From d688b66554cfc4411db3907dab0d237f966586c1 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Mon, 18 May 2026 14:58:11 +0200 Subject: [PATCH 01/20] bind circuits of proof agg to vk hashes --- .../verifiers/bfv/BfvDecryptionVerifier.sol | 6 + .../contracts/verifiers/bfv/BfvPkVerifier.sol | 37 +++- .../scripts/benchmarkGasFromRaw.ts | 66 ++++++- packages/enclave-contracts/scripts/utils.ts | 14 ++ .../test/BfvDecryptionVerifier.spec.ts | 60 +++++++ .../test/BfvPkVerifier.spec.ts | 170 +++++++++++++----- 6 files changed, 306 insertions(+), 47 deletions(-) diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol index c572c0949..ec5afc3f3 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol @@ -61,6 +61,12 @@ contract BfvDecryptionVerifier is IDecryptionVerifier { if (publicInputs[1] != expectedC7KeyHash) { return false; } + if (publicInputs[0] != expectedC6FoldKeyHash) { + return false; + } + if (publicInputs[1] != expectedC7KeyHash) { + return false; + } if (!_verifyPlaintextHash(publicInputs, plaintextOutputHash)) { return false; } diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol index de5cb6a70..d9872d0a9 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol @@ -7,17 +7,35 @@ pragma solidity 0.8.28; import { IPkVerifier } from "../../interfaces/IPkVerifier.sol"; import { ICircuitVerifier } from "../../interfaces/ICircuitVerifier.sol"; +import { CommitteeHashLib } from "../../lib/CommitteeHashLib.sol"; /** * @title BfvPkVerifier * @notice Verifies the DkgAggregator (EVM) proof produced by the recursive * aggregation pipeline (node folds + C5/pk_aggregation verified - * internally). Binds the proof to a caller-supplied `pkCommitment`. + * internally). Binds the proof to a caller-supplied `pkCommitment` + * and on-chain committee hash. * @dev Used when the Enclave is configured with encryptionSchemeId * keccak256("fhe.rs:BFV"). The aggregator circuit's last public input is * the hash-based aggregated PK commitment. */ contract BfvPkVerifier is IPkVerifier { + /// @dev Must match `lib::configs::default::H` (micro committee size). + uint256 internal constant H = 3; + + /// @dev `publicInputs` index for `committee_hash_hi` (after `party_ids`). + uint256 internal constant COMMITTEE_HASH_HI_IDX = 2 + H; + + /// @dev `publicInputs` index for `committee_hash_lo` (after `party_ids`). + uint256 internal constant COMMITTEE_HASH_LO_IDX = 3 + H; + + /// @dev `7` pub params + `8` return fields for micro `H = 3` (`dkg_aggregator`). + uint256 internal constant EXPECTED_PUBLIC_INPUTS_LEN = 15; + + /// @dev Index of `pkCommitment` (last return field). + uint256 internal constant PK_COMMITMENT_IDX = + EXPECTED_PUBLIC_INPUTS_LEN - 1; + /// @notice Underlying Honk verifier for the DkgAggregator circuit. ICircuitVerifier public immutable circuitVerifier; @@ -40,6 +58,7 @@ contract BfvPkVerifier is IPkVerifier { /// @inheritdoc IPkVerifier function verify( bytes32 pkCommitment, + bytes32 committeeHash, bytes calldata proof ) external view override returns (bool) { (bytes memory rawProof, bytes32[] memory publicInputs) = abi.decode( @@ -47,7 +66,7 @@ contract BfvPkVerifier is IPkVerifier { (bytes, bytes32[]) ); - if (publicInputs.length < 3) { + if (publicInputs.length != EXPECTED_PUBLIC_INPUTS_LEN) { return false; } if (publicInputs[0] != expectedNodesFoldKeyHash) { @@ -56,7 +75,19 @@ contract BfvPkVerifier is IPkVerifier { if (publicInputs[1] != expectedC5KeyHash) { return false; } - if (publicInputs[publicInputs.length - 1] != pkCommitment) { + if ( + publicInputs[COMMITTEE_HASH_HI_IDX] != + CommitteeHashLib.hi(committeeHash) + ) { + return false; + } + if ( + publicInputs[COMMITTEE_HASH_LO_IDX] != + CommitteeHashLib.lo(committeeHash) + ) { + return false; + } + if (publicInputs[PK_COMMITMENT_IDX] != pkCommitment) { return false; } return circuitVerifier.verify(rawProof, publicInputs); diff --git a/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts b/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts index 610a807d5..77b32f516 100644 --- a/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts +++ b/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts @@ -10,6 +10,7 @@ import path from "node:path"; import { BFV_DECRYPTION_SUB_CIRCUIT_VK_HASH_PATHS, BFV_PK_SUB_CIRCUIT_VK_HASH_PATHS, + committeeHashFromLimbs, readVkRecursiveHash, } from "./utils"; @@ -23,6 +24,24 @@ function findRawJson(rawDir: string, fragment: string): any { throw new Error(`Missing raw benchmark JSON for fragment: ${fragment}`); } +const MIN_VK_HASH_PUBLIC_INPUTS = 2; +const DKG_COMMITTEE_HASH_IDX_HI = 5; +const DKG_COMMITTEE_HASH_IDX_LO = 6; +const DEC_COMMITTEE_HASH_IDX_HI = 2; +const DEC_COMMITTEE_HASH_IDX_LO = 3; + +function requirePublicInputLen( + label: string, + publicInputs: string[], + minLen: number, +): void { + if (publicInputs.length < minLen) { + throw new Error( + `${label}: public_inputs length ${publicInputs.length} < ${minLen} (truncated or stale artifact?)`, + ); + } +} + function hexToBytes32Array(hex: string): string[] { const clean = hex.startsWith("0x") ? hex.slice(2) : hex; if (clean.length === 0) return []; @@ -98,6 +117,16 @@ async function main() { const dkgPublicInputs = hexToBytes32Array(dkgPublicHex); const decPublicInputs = hexToBytes32Array(decPublicHex); + requirePublicInputLen( + "dkg_aggregator", + dkgPublicInputs, + MIN_VK_HASH_PUBLIC_INPUTS, + ); + requirePublicInputLen( + "decryption_aggregator", + decPublicInputs, + MIN_VK_HASH_PUBLIC_INPUTS, + ); const expectedNodesFoldKeyHash = readVkRecursiveHash( BFV_PK_SUB_CIRCUIT_VK_HASH_PATHS.nodesFold, @@ -173,14 +202,31 @@ async function main() { ["bytes", "bytes32[]"], [dkgProofHex, dkgPublicInputs], ); + requirePublicInputLen( + "dkg_aggregator committee_hash", + dkgPublicInputs, + DKG_COMMITTEE_HASH_IDX_LO + 1, + ); const pkCommitment = dkgPublicInputs[dkgPublicInputs.length - 1]; - const dkgOk = await bfvPk.verify.staticCall(pkCommitment, dkgEncodedProof); + const dkgCommitteeHash = committeeHashFromLimbs( + dkgPublicInputs[DKG_COMMITTEE_HASH_IDX_HI], + dkgPublicInputs[DKG_COMMITTEE_HASH_IDX_LO], + ); + const dkgOk = await bfvPk.verify.staticCall( + pkCommitment, + dkgCommitteeHash, + dkgEncodedProof, + ); if (!dkgOk) { throw new Error( "BfvPkVerifier.verify returned false for folded DKG proof (Honk VK / proof mismatch?)", ); } - const dkgGas = await bfvPk.verify.estimateGas(pkCommitment, dkgEncodedProof); + const dkgGas = await bfvPk.verify.estimateGas( + pkCommitment, + dkgCommitteeHash, + dkgEncodedProof, + ); const bfvDec = await ( await ethers.getContractFactory("BfvDecryptionVerifier") @@ -192,7 +238,20 @@ async function main() { [decProofHex, decPublicInputs], ); const plaintextHash = plaintextHashFromPublicInputs(decPublicInputs, ethers); - const decOk = await bfvDec.verify.staticCall(plaintextHash, decEncodedProof); + requirePublicInputLen( + "decryption_aggregator committee_hash", + decPublicInputs, + DEC_COMMITTEE_HASH_IDX_LO + 1, + ); + const decCommitteeHash = committeeHashFromLimbs( + decPublicInputs[DEC_COMMITTEE_HASH_IDX_HI], + decPublicInputs[DEC_COMMITTEE_HASH_IDX_LO], + ); + const decOk = await bfvDec.verify.staticCall( + plaintextHash, + decCommitteeHash, + decEncodedProof, + ); if (!decOk) { throw new Error( "BfvDecryptionVerifier.verify returned false for folded decryption proof (Honk VK / proof mismatch?)", @@ -200,6 +259,7 @@ async function main() { } const decGas = await bfvDec.verify.estimateGas( plaintextHash, + decCommitteeHash, decEncodedProof, ); diff --git a/packages/enclave-contracts/scripts/utils.ts b/packages/enclave-contracts/scripts/utils.ts index bd672365e..b60a4af72 100644 --- a/packages/enclave-contracts/scripts/utils.ts +++ b/packages/enclave-contracts/scripts/utils.ts @@ -3,10 +3,24 @@ // This file is provided WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +import { getBytes, hexlify, zeroPadValue } from "ethers"; import fs from "fs"; import { fileURLToPath } from "node:url"; import path from "path"; +/** + * Reconstruct `keccak256(abi.encodePacked(topNodes))` from aggregator public-input + * limbs. Each limb is a bytes32 with 128 bits right-aligned (`CommitteeHashLib`). + */ +export function committeeHashFromLimbs(hi: string, lo: string): string { + const hiBytes = getBytes(zeroPadValue(hi, 32)); + const loBytes = getBytes(zeroPadValue(lo, 32)); + const hash = new Uint8Array(32); + hash.set(hiBytes.subarray(16, 32), 0); + hash.set(loBytes.subarray(16, 32), 16); + return hexlify(hash); +} + export const deploymentsFile = path.join("deployed_contracts.json"); /** Monorepo root (`enclave/`), resolved from `packages/enclave-contracts/scripts/`. */ diff --git a/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts b/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts index d2bc3e29a..faf6fbfe2 100644 --- a/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts +++ b/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts @@ -182,6 +182,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, diff --git a/packages/enclave-contracts/test/BfvPkVerifier.spec.ts b/packages/enclave-contracts/test/BfvPkVerifier.spec.ts index c2d24dbf5..17cbafe2c 100644 --- a/packages/enclave-contracts/test/BfvPkVerifier.spec.ts +++ b/packages/enclave-contracts/test/BfvPkVerifier.spec.ts @@ -17,6 +17,32 @@ const { loadFixture } = networkHelpers; const EXPECTED_NODES_FOLD_KEY_HASH = ethers.id("nodes_fold"); const EXPECTED_C5_KEY_HASH = ethers.id("c5"); +/** Must match `BfvPkVerifier` / default circuit `H`. */ +const H = 3; +const DKG_RETURN_FIELD_COUNT = 8; + +function committeeHashLimbs(committeeHash: string): [string, string] { + const bn = BigInt(committeeHash); + const hi = ethers.toBeHex(bn >> 128n, 32); + const lo = ethers.toBeHex(bn & ((1n << 128n) - 1n), 32); + return [hi, lo]; +} + +function minimalDkgPublicInputs( + pkCommitment: string, + committeeHash: string = ethers.ZeroHash, +): string[] { + const [hi, lo] = committeeHashLimbs(committeeHash); + return [ + EXPECTED_NODES_FOLD_KEY_HASH, + EXPECTED_C5_KEY_HASH, + ...Array(H).fill(ethers.ZeroHash), + hi, + lo, + ...Array(DKG_RETURN_FIELD_COUNT - 1).fill(ethers.ZeroHash), + pkCommitment, + ]; +} function encodeProof(rawProof: string, publicInputs: string[]): string { const abiCoder = ethers.AbiCoder.defaultAbiCoder(); @@ -51,7 +77,11 @@ describe("BfvPkVerifier", function () { const invalidProof = "0xdeadbeef"; await expect( - bfvPkVerifier.verify.staticCall(pkCommitment, invalidProof), + bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + invalidProof, + ), ).to.be.revert(ethers); }); @@ -60,7 +90,11 @@ describe("BfvPkVerifier", function () { const pkCommitment = ethers.keccak256("0x1234"); const proof = encodeProof("0x01", []); - const result = await bfvPkVerifier.verify.staticCall(pkCommitment, proof); + const result = await bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + proof, + ); expect(result).to.equal(false); }); @@ -69,7 +103,48 @@ describe("BfvPkVerifier", function () { const pkCommitment = ethers.keccak256("0xabcd"); const proof = encodeProof("0x01", [pkCommitment]); - const result = await bfvPkVerifier.verify.staticCall(pkCommitment, proof); + const result = await bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + proof, + ); + expect(result).to.equal(false); + }); + + it("returns false when publicInputs has trailing elements past expected length", async function () { + const { bfvPkVerifier } = await loadFixture(deployWithMockCircuit); + const pkCommitment = ethers.keccak256("0xabcd"); + const proof = encodeProof("0x01", [ + ...minimalDkgPublicInputs(pkCommitment), + ethers.ZeroHash, + ]); + + const result = await bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + proof, + ); + expect(result).to.equal(false); + }); + + it("returns false when publicInputs has only pub params (length 7, no return fields)", async function () { + const { bfvPkVerifier } = await loadFixture(deployWithMockCircuit); + const pkCommitment = ethers.keccak256("0xabcd"); + const [hi, lo] = committeeHashLimbs(ethers.ZeroHash); + const proof = encodeProof("0x01", [ + EXPECTED_NODES_FOLD_KEY_HASH, + EXPECTED_C5_KEY_HASH, + ...Array(H).fill(ethers.ZeroHash), + hi, + lo, + pkCommitment, + ]); + + const result = await bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + proof, + ); expect(result).to.equal(false); }); @@ -81,7 +156,11 @@ describe("BfvPkVerifier", function () { EXPECTED_C5_KEY_HASH, ]); - const result = await bfvPkVerifier.verify.staticCall(pkCommitment, proof); + const result = await bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + proof, + ); expect(result).to.equal(false); }); @@ -101,13 +180,18 @@ describe("BfvPkVerifier", function () { await bfvPkVerifier.waitForDeployment(); const pkCommitment = ethers.keccak256("0xabcd"); - const proof = encodeProof("0x01", [ - ethers.id("wrong-nodes-fold"), - EXPECTED_C5_KEY_HASH, - pkCommitment, - ]); + const proof = encodeProof( + "0x01", + minimalDkgPublicInputs(pkCommitment).map((v, i) => + i === 0 ? ethers.id("wrong-nodes-fold") : v, + ), + ); - const result = await bfvPkVerifier.verify.staticCall(pkCommitment, proof); + const result = await bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + proof, + ); expect(result).to.equal(false); }); @@ -127,13 +211,18 @@ describe("BfvPkVerifier", function () { await bfvPkVerifier.waitForDeployment(); const pkCommitment = ethers.keccak256("0xabcd"); - const proof = encodeProof("0x01", [ - EXPECTED_NODES_FOLD_KEY_HASH, - ethers.id("wrong-c5"), - pkCommitment, - ]); + const proof = encodeProof( + "0x01", + minimalDkgPublicInputs(pkCommitment).map((v, i) => + i === 1 ? ethers.id("wrong-c5") : v, + ), + ); - const result = await bfvPkVerifier.verify.staticCall(pkCommitment, proof); + const result = await bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + proof, + ); expect(result).to.equal(false); }); @@ -145,13 +234,13 @@ describe("BfvPkVerifier", function () { const pkCommitment = ethers.keccak256("0xabcd"); const wrong = ethers.keccak256("0x1234"); - const proof = encodeProof("0x01", [ - EXPECTED_NODES_FOLD_KEY_HASH, - EXPECTED_C5_KEY_HASH, - wrong, - ]); + const proof = encodeProof("0x01", minimalDkgPublicInputs(wrong)); - const result = await bfvPkVerifier.verify.staticCall(pkCommitment, proof); + const result = await bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + proof, + ); expect(result).to.equal(false); }); @@ -162,13 +251,13 @@ describe("BfvPkVerifier", function () { await mockCircuit.setReturnValue(false); const pkCommitment = ethers.keccak256("0xabcd"); - const proof = encodeProof("0x01", [ - EXPECTED_NODES_FOLD_KEY_HASH, - EXPECTED_C5_KEY_HASH, - pkCommitment, - ]); + const proof = encodeProof("0x01", minimalDkgPublicInputs(pkCommitment)); - const result = await bfvPkVerifier.verify.staticCall(pkCommitment, proof); + const result = await bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + proof, + ); expect(result).to.equal(false); }); @@ -183,13 +272,13 @@ describe("BfvPkVerifier", function () { await bfvPkVerifier.waitForDeployment(); const pkCommitment = ethers.keccak256("0xabcd"); - const proof = encodeProof("0x0102", [ - EXPECTED_NODES_FOLD_KEY_HASH, - EXPECTED_C5_KEY_HASH, - pkCommitment, - ]); + const proof = encodeProof("0x0102", minimalDkgPublicInputs(pkCommitment)); - const result = await bfvPkVerifier.verify.staticCall(pkCommitment, proof); + const result = await bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + proof, + ); expect(result).to.equal(false); }); }); @@ -202,14 +291,13 @@ describe("BfvPkVerifier", function () { await mockCircuit.setReturnValue(true); const pkCommitment = ethers.keccak256("0xabcd"); - const proof = encodeProof("0x0102", [ - EXPECTED_NODES_FOLD_KEY_HASH, - EXPECTED_C5_KEY_HASH, - "0x" + "00".repeat(32), - pkCommitment, - ]); + const proof = encodeProof("0x0102", minimalDkgPublicInputs(pkCommitment)); - const result = await bfvPkVerifier.verify.staticCall(pkCommitment, proof); + const result = await bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + proof, + ); expect(result).to.equal(true); }); }); From 5bec88d23e588c6788b06049d93479bf0e3fc2c1 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Mon, 18 May 2026 15:59:10 +0200 Subject: [PATCH 02/20] address coderabbit comments --- .../enclave-contracts/test/BfvPkVerifier.spec.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/enclave-contracts/test/BfvPkVerifier.spec.ts b/packages/enclave-contracts/test/BfvPkVerifier.spec.ts index 17cbafe2c..89e8e8827 100644 --- a/packages/enclave-contracts/test/BfvPkVerifier.spec.ts +++ b/packages/enclave-contracts/test/BfvPkVerifier.spec.ts @@ -164,6 +164,18 @@ describe("BfvPkVerifier", function () { 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, proof); + expect(result).to.equal(false); + }); + it("returns false when nodes_fold key hash does not match", async function () { const revertingVerifier = await ( await ethers.getContractFactory("RevertOnVerifyCircuitVerifier") From 56f580c9af76853013574975d82c639e359c4356 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Tue, 19 May 2026 13:36:22 +0200 Subject: [PATCH 03/20] bind to on-chain data --- circuits/benchmarks/README.md | 12 + .../scripts/check_circuit_preset_artifacts.sh | 61 + .../scripts/ensure_circuit_preset_built.sh | 68 + .../scripts/extract_crisp_verify_gas.sh | 67 +- .../scripts/replay_folded_verify_gas.sh | 24 +- circuits/benchmarks/scripts/run_benchmarks.sh | 13 +- .../decryption_aggregator/src/main.nr | 3 + .../dkg_aggregator/src/main.nr | 4 + crates/aggregator/src/committee_hash.rs | 7 + crates/aggregator/src/lib.rs | 1 + crates/aggregator/src/publickey_aggregator.rs | 3 + .../src/threshold_plaintext_aggregator.rs | 92 +- .../src/enclave_event/compute_request/zk.rs | 4 + crates/evm/src/ciphernode_registry_sol.rs | 17 +- crates/multithread/src/multithread.rs | 2 + crates/sortition/src/sortition.rs | 31 + crates/utils/src/committee_hash.rs | 77 + crates/utils/src/lib.rs | 1 + .../src/circuits/aggregation/node_dkg_fold.rs | 19 + .../contracts/Enclave.sol/Enclave.json | 6 +- .../IBondingRegistry.json | 2 +- .../ICiphernodeRegistry.json | 26 +- .../interfaces/IEnclave.sol/IEnclave.json | 2 +- .../ISlashingManager.json | 2 +- .../CiphernodeRegistryOwnable.json | 50 +- .../enclave-contracts/contracts/Enclave.sol | 2 + .../interfaces/ICiphernodeRegistry.sol | 11 +- .../interfaces/IDecryptionVerifier.sol | 6 +- .../contracts/interfaces/IPkVerifier.sol | 6 +- .../contracts/lib/CommitteeHashLib.sol | 36 + .../registry/CiphernodeRegistryOwnable.sol | 25 +- .../contracts/test/MockCiphernodeRegistry.sol | 12 +- .../contracts/test/MockDecryptionVerifier.sol | 1 + .../contracts/test/MockPkVerifier.sol | 1 + .../verifiers/bfv/BfvDecryptionVerifier.sol | 15 +- .../bfv/honk/DecryptionAggregatorVerifier.sol | 1641 +++++------------ .../bfv/honk/DkgAggregatorVerifier.sol | 1641 +++++------------ packages/enclave-contracts/tasks/enclave.ts | 1 - .../test/BfvDecryptionVerifier.spec.ts | 28 +- .../test/BfvPkVerifier.spec.ts | 6 +- .../test/BfvVkBindingIntegration.spec.ts | 65 +- .../test/E3Lifecycle/E3Integration.spec.ts | 18 +- .../enclave-contracts/test/Enclave.spec.ts | 183 +- .../test/Pricing/Pricing.spec.ts | 2 +- .../CiphernodeRegistryOwnable.spec.ts | 39 +- .../test/Slashing/CommitteeExpulsion.spec.ts | 8 +- scripts/build-circuits.ts | 76 +- scripts/generate-verifiers.ts | 15 +- 48 files changed, 1780 insertions(+), 2652 deletions(-) create mode 100755 circuits/benchmarks/scripts/check_circuit_preset_artifacts.sh create mode 100755 circuits/benchmarks/scripts/ensure_circuit_preset_built.sh create mode 100644 crates/aggregator/src/committee_hash.rs create mode 100644 crates/utils/src/committee_hash.rs create mode 100644 packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol diff --git a/circuits/benchmarks/README.md b/circuits/benchmarks/README.md index 63c2f3562..f0fff873b 100644 --- a/circuits/benchmarks/README.md +++ b/circuits/benchmarks/README.md @@ -134,5 +134,17 @@ For `Π_DKG` and `Π_dec`, verifier gas is sourced from folded recursive-aggrega by `cargo test -p e3-tests test_trbfv_actor` (via `BENCHMARK_FOLDED_OUTPUT`) and then replayed into EVM verifier `estimateGas` in `packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts`. +`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 --no-compile` refreshes Honk contracts before integration export and +Hardhat replay. + +- **`--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 + integration + gas replay). Fails fast unless `dist/circuits//` and `circuits/bin` targets + are present for that preset (`check_circuit_preset_artifacts.sh`). + `Calldata gas` is computed from benchmark proof/public-input bytes with EVM calldata costs (`0x00 -> 4`, non-zero byte -> 16) and stored in raw benchmark JSON. diff --git a/circuits/benchmarks/scripts/check_circuit_preset_artifacts.sh b/circuits/benchmarks/scripts/check_circuit_preset_artifacts.sh new file mode 100755 index 000000000..20ecf8ad7 --- /dev/null +++ b/circuits/benchmarks/scripts/check_circuit_preset_artifacts.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# Verify dist + circuits/bin artifacts exist for a benchmark preset. +# Exit 0 if ready, 1 if not (prints missing paths on stderr). +# +# Usage: ./check_circuit_preset_artifacts.sh + +set -e + +PRESET="${1:-}" +if [ -z "$PRESET" ]; then + echo "Usage: $0 " >&2 + exit 1 +fi +if [ "$PRESET" != "insecure-512" ] && [ "$PRESET" != "secure-8192" ]; then + echo "Error: preset must be insecure-512 or secure-8192" >&2 + exit 1 +fi + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)" +DIST="${REPO_ROOT}/dist/circuits/${PRESET}" +BIN="${REPO_ROOT}/circuits/bin" +STAMP="${DIST}/.build-stamp.json" + +MARKERS=( + "${DIST}/default/recursive_aggregation/dkg_aggregator/dkg_aggregator.json" + "${DIST}/default/recursive_aggregation/decryption_aggregator/decryption_aggregator.json" + "${BIN}/recursive_aggregation/dkg_aggregator/target/dkg_aggregator.json" + "${BIN}/recursive_aggregation/dkg_aggregator/target/dkg_aggregator.vk_recursive" + "${BIN}/recursive_aggregation/decryption_aggregator/target/decryption_aggregator.json" + "${BIN}/recursive_aggregation/decryption_aggregator/target/decryption_aggregator.vk_recursive" + "${BIN}/dkg/target/pk.json" + "${BIN}/threshold/target/pk_aggregation.json" +) + +missing=() +for path in "${MARKERS[@]}"; do + if [ ! -f "$path" ]; then + missing+=("$path") + fi +done + +if [ ! -f "$STAMP" ]; then + missing+=("$STAMP") +elif ! jq -e --arg p "$PRESET" '.preset == $p' "$STAMP" >/dev/null 2>&1; then + echo "Error: ${STAMP} is for a different preset (expected ${PRESET})." >&2 + echo " Run: pnpm build:circuits --preset ${PRESET}" >&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 + echo " Fix: pnpm build:circuits --preset ${PRESET}" >&2 + echo " Or run this script without --skip-build." >&2 + echo "Missing:" >&2 + printf ' %s\n' "${missing[@]}" >&2 + exit 1 +fi + +exit 0 diff --git a/circuits/benchmarks/scripts/ensure_circuit_preset_built.sh b/circuits/benchmarks/scripts/ensure_circuit_preset_built.sh new file mode 100755 index 000000000..8def1bf78 --- /dev/null +++ b/circuits/benchmarks/scripts/ensure_circuit_preset_built.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# Ensure Noir circuit artifacts exist for a benchmark preset (insecure-512 | secure-8192). +# +# Usage (from repo root): +# ./circuits/benchmarks/scripts/ensure_circuit_preset_built.sh [--force-build] [--verbose] +# +# Default: pnpm build:circuits --skip-if-built --no-clean --no-clean-targets (fast re-runs). +# --force-build: full rebuild (wipes dist/circuits and circuits/bin targets via build:circuits). + +set -e + +PRESET="" +FORCE_BUILD=false +VERBOSE=false + +while [[ $# -gt 0 ]]; do + case $1 in + --force-build) + FORCE_BUILD=true + shift + ;; + --verbose|-v) + VERBOSE=true + shift + ;; + -*) + echo "Unknown option: $1" + echo "Usage: $0 [--force-build] [--verbose]" + exit 1 + ;; + *) + if [ -z "$PRESET" ]; then + PRESET="$1" + else + echo "Unexpected argument: $1" + exit 1 + fi + shift + ;; + esac +done + +if [ -z "$PRESET" ]; then + echo "Usage: $0 [--force-build] [--verbose]" + exit 1 +fi +if [ "$PRESET" != "insecure-512" ] && [ "$PRESET" != "secure-8192" ]; then + echo "Error: preset must be insecure-512 or secure-8192" + exit 1 +fi + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)" + +BUILD_ARGS=(--preset "$PRESET") +if [ "$FORCE_BUILD" = true ]; then + echo " [circuits] Full rebuild: pnpm build:circuits --preset ${PRESET}" +else + BUILD_ARGS+=(--skip-if-built --no-clean --no-clean-targets) + echo " [circuits] Ensuring preset ${PRESET} (skip-if-built; use --force-build to recompile)..." +fi + +if [ "$VERBOSE" = true ]; then + echo " [circuits] Running: pnpm build:circuits ${BUILD_ARGS[*]}" + (cd "$REPO_ROOT" && pnpm build:circuits "${BUILD_ARGS[@]}") +else + (cd "$REPO_ROOT" && pnpm build:circuits "${BUILD_ARGS[@]}" >/dev/null) +fi diff --git a/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh b/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh index 1ebf45847..d4a6df7cf 100755 --- a/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh +++ b/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh @@ -2,12 +2,15 @@ # 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] set -e OUTPUT_JSON="" MODE="insecure" VERBOSE=false +SKIP_BUILD=false +FORCE_BUILD=false while [[ $# -gt 0 ]]; do case $1 in @@ -23,16 +26,28 @@ while [[ $# -gt 0 ]]; do VERBOSE=true shift ;; + --skip-build|--no-build) + SKIP_BUILD=true + shift + ;; + --force-build) + FORCE_BUILD=true + shift + ;; *) echo "Unknown option: $1" - echo "Usage: $0 --output [--mode insecure|secure] [--verbose]" + echo "Usage: $0 --output [--mode insecure|secure] [--verbose] [--skip-build] [--force-build]" exit 1 ;; esac done if [ -z "$OUTPUT_JSON" ]; then - echo "Usage: $0 --output [--mode insecure|secure] [--verbose]" + echo "Usage: $0 --output [--mode insecure|secure] [--verbose] [--skip-build] [--force-build]" + exit 1 +fi +if [ "$SKIP_BUILD" = true ] && [ "$FORCE_BUILD" = true ]; then + echo "Error: --skip-build and --force-build are mutually exclusive" exit 1 fi if [ "$MODE" != "insecure" ] && [ "$MODE" != "secure" ]; then @@ -77,20 +92,43 @@ if [ "$MODE" = "secure" ]; then else PRESET_NAME="insecure-512" fi -echo " [gas] Preparing recursive verifier artifacts (build:circuits ${PRESET_NAME})..." -if [ "$VERBOSE" = true ]; then - echo " [gas] [verbose] Running: pnpm build:circuits --preset ${PRESET_NAME}" - ( - cd "$REPO_ROOT" && \ - pnpm build:circuits --preset "$PRESET_NAME" - ) + +require_preset_artifacts() { + if ! "${SCRIPT_DIR}/check_circuit_preset_artifacts.sh" "$PRESET_NAME"; then + exit 1 + fi +} + +if [ "$SKIP_BUILD" = true ]; then + echo " [gas] Skipping circuit build and Honk verifier generation (--skip-build)." + require_preset_artifacts else - ( - cd "$REPO_ROOT" && \ - pnpm build:circuits --preset "$PRESET_NAME" >/dev/null - ) + ENSURE_ARGS=("$PRESET_NAME") + if [ "$FORCE_BUILD" = true ]; then + ENSURE_ARGS+=(--force-build) + fi + if [ "$VERBOSE" = true ]; then + ENSURE_ARGS+=(--verbose) + fi + "${SCRIPT_DIR}/ensure_circuit_preset_built.sh" "${ENSURE_ARGS[@]}" + echo " [gas] Build artifacts ready." + + echo " [gas] Regenerating Honk Solidity verifiers (dkg_aggregator, decryption_aggregator)..." + if [ "$VERBOSE" = true ]; then + echo " [gas] [verbose] Running: pnpm generate:verifiers --no-compile" + ( + cd "$REPO_ROOT" && \ + pnpm generate:verifiers --no-compile + ) + else + ( + cd "$REPO_ROOT" && \ + pnpm generate:verifiers --no-compile >/dev/null + ) + fi + echo " [gas] Honk verifiers ready." + require_preset_artifacts fi -echo " [gas] Build artifacts ready." set +e echo " [gas] Running CRISP verifier test for Pi_user gas..." @@ -100,6 +138,7 @@ echo " [gas] Running CRISP verifier test for Pi_user gas..." ) 2>&1 | tee "$TMP_LOG_CRISP" 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..." ( cd "$REPO_ROOT" && \ diff --git a/circuits/benchmarks/scripts/replay_folded_verify_gas.sh b/circuits/benchmarks/scripts/replay_folded_verify_gas.sh index 4ff10956a..76e0b9594 100755 --- a/circuits/benchmarks/scripts/replay_folded_verify_gas.sh +++ b/circuits/benchmarks/scripts/replay_folded_verify_gas.sh @@ -17,6 +17,8 @@ set -e SUMMARY_JSON="" GAS_JSON="" BUILD_PRESET="" +FORCE_BUILD=false +SKIP_BUILD=false SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)" ENCLAVE_CONTRACTS="${REPO_ROOT}/packages/enclave-contracts" @@ -29,6 +31,14 @@ while [[ $# -gt 0 ]]; do BUILD_PRESET="$2" shift 2 ;; + --force-build) + FORCE_BUILD=true + shift + ;; + --skip-build|--no-build) + SKIP_BUILD=true + shift + ;; *) echo "Unknown option: $1" echo "Usage: $0 --summary --gas-json [--build ]" @@ -68,8 +78,18 @@ trap 'rm -f "$TMP_FOLDED" "$TMP_GAS_PARTIAL"' EXIT jq -c '.folded_artifacts' "$SUMMARY_JSON" >"$TMP_FOLDED" if [ -n "$BUILD_PRESET" ]; then - echo " [replay-gas] Building verifier artifacts: pnpm build:circuits --preset ${BUILD_PRESET}" - (cd "$REPO_ROOT" && pnpm build:circuits --preset "$BUILD_PRESET") + if [ "$SKIP_BUILD" = true ]; then + echo " [replay-gas] Skipping circuit build (--skip-build)." + "${SCRIPT_DIR}/check_circuit_preset_artifacts.sh" "$BUILD_PRESET" + else + ENSURE_ARGS=("$BUILD_PRESET") + if [ "$FORCE_BUILD" = true ]; 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) + fi fi echo " [replay-gas] Running Hardhat benchmarkGasFromRaw.ts (folded proofs)..." diff --git a/circuits/benchmarks/scripts/run_benchmarks.sh b/circuits/benchmarks/scripts/run_benchmarks.sh index 84d1b219d..71e5f5f08 100755 --- a/circuits/benchmarks/scripts/run_benchmarks.sh +++ b/circuits/benchmarks/scripts/run_benchmarks.sh @@ -145,18 +145,11 @@ if [ "$SKIP_COMPILE" = false ]; then else PRESET_NAME="insecure-512" fi - echo "Preflight: pnpm build:circuits --preset ${PRESET_NAME}" + ENSURE_ARGS=("$PRESET_NAME") if [ "$VERBOSE" = true ]; then - ( - cd "$REPO_ROOT" && \ - pnpm build:circuits --preset "$PRESET_NAME" - ) - else - ( - cd "$REPO_ROOT" && \ - pnpm build:circuits --preset "$PRESET_NAME" >/dev/null - ) + ENSURE_ARGS+=(--verbose) fi + "${SCRIPT_DIR}/ensure_circuit_preset_built.sh" "${ENSURE_ARGS[@]}" echo "Preflight build complete." echo "" fi diff --git a/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr b/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr index 7bd53501f..1748cc627 100644 --- a/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr +++ b/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr @@ -7,6 +7,7 @@ //! Decryption aggregator: non-ZK `c6_fold` (folded C6 column) + non-ZK `decrypted_shares_aggregation` //! (C7, both `noir-recursive-no-zk`). The final `bb prove -t evm` proof of this circuit is what the //! contract verifies on-chain. +//! `committee_hash_hi` / `committee_hash_lo` bind the proof to the full on-chain committee ordering. use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; use lib::configs::default::MAX_MSG_NON_ZERO_COEFFS; @@ -42,6 +43,8 @@ fn main( c7_public: [Field; C7_PUBLIC_LEN], c6_fold_key_hash: pub Field, c7_key_hash: pub Field, + committee_hash_hi: pub Field, + committee_hash_lo: pub Field, ) -> pub (Field, [Field; T + 1], [Field; T + 1], [Field; T + 1], [Field; MAX_MSG_NON_ZERO_COEFFS]) { verify_honk_proof_non_zk(c6_fold_vk, c6_fold_proof, c6_fold_public, c6_fold_key_hash); verify_honk_proof_non_zk(c7_vk, c7_proof, c7_public, c7_key_hash); diff --git a/circuits/bin/recursive_aggregation/dkg_aggregator/src/main.nr b/circuits/bin/recursive_aggregation/dkg_aggregator/src/main.nr index f82707dbe..ab7d14000 100644 --- a/circuits/bin/recursive_aggregation/dkg_aggregator/src/main.nr +++ b/circuits/bin/recursive_aggregation/dkg_aggregator/src/main.nr @@ -6,6 +6,8 @@ //! DKG aggregator: non-ZK `nodes_fold` and non-ZK `pk_aggregation` (both `noir-recursive-no-zk`). //! The final `bb prove -t evm` proof of this circuit is what the contract verifies on-chain. +//! `committee_hash_hi` / `committee_hash_lo` bind the proof to `keccak256(abi.encodePacked(topNodes))` +//! (128-bit limbs); the contract recomputes the hash from on-chain `topNodes`. use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; use lib::configs::default::dkg::L_THRESHOLD; @@ -50,6 +52,8 @@ fn main( nodes_fold_key_hash: pub Field, c5_key_hash: pub Field, party_ids: pub [Field; H], + committee_hash_hi: pub Field, + committee_hash_lo: pub Field, ) -> pub (Field, [Field; H], [Field; H], Field) { verify_honk_proof_non_zk( nodes_fold_vk, diff --git a/crates/aggregator/src/committee_hash.rs b/crates/aggregator/src/committee_hash.rs new file mode 100644 index 000000000..9fa41132b --- /dev/null +++ b/crates/aggregator/src/committee_hash.rs @@ -0,0 +1,7 @@ +// 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. + +pub use e3_utils::committee_hash::*; diff --git a/crates/aggregator/src/lib.rs b/crates/aggregator/src/lib.rs index 1738a79a6..d39991fa8 100644 --- a/crates/aggregator/src/lib.rs +++ b/crates/aggregator/src/lib.rs @@ -4,6 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +pub mod committee_hash; mod committee_finalizer; mod decryptionshare_created_buffer; pub mod ext; diff --git a/crates/aggregator/src/publickey_aggregator.rs b/crates/aggregator/src/publickey_aggregator.rs index 3f720cbe8..e698f2f47 100644 --- a/crates/aggregator/src/publickey_aggregator.rs +++ b/crates/aggregator/src/publickey_aggregator.rs @@ -610,6 +610,7 @@ impl PublicKeyAggregator { fn try_dispatch_dkg_aggregation(&mut self, ec: &EventContext) -> Result<()> { let state = self.state.get(); let Some(PublicKeyAggregatorState::GeneratingC5Proof { + nodes, dkg_node_proofs, honest_party_ids, c5_proof_pending, @@ -732,6 +733,7 @@ impl PublicKeyAggregator { node_fold_proofs, c5_proof: c5_proof.clone(), party_ids, + committee_addresses: nodes.iter().cloned().collect::>(), params_preset: self.params_preset, }), corr, @@ -1348,6 +1350,7 @@ mod tests { node_fold_proofs: vec![dummy_proof(CircuitName::PkAggregation)], c5_proof: dummy_proof(CircuitName::PkAggregation), party_ids: vec![0], + committee_addresses: vec!["0x0000000000000000000000000000000000000001".to_string()], params_preset: BfvPreset::InsecureThreshold512, }), correlation_id, diff --git a/crates/aggregator/src/threshold_plaintext_aggregator.rs b/crates/aggregator/src/threshold_plaintext_aggregator.rs index 73cc1ec3b..9f68bf68c 100644 --- a/crates/aggregator/src/threshold_plaintext_aggregator.rs +++ b/crates/aggregator/src/threshold_plaintext_aggregator.rs @@ -20,7 +20,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, @@ -186,6 +189,8 @@ pub struct ThresholdPlaintextAggregator { decryption_aggregator_proofs: Option>, /// Last event context, reused for ZK and final publish. last_ec: Option>, + /// Ordered committee (`topNodes`) for decryption-aggregator `committee_hash_*` inputs. + committee_members: Option>, } pub struct ThresholdPlaintextAggregatorParams { @@ -214,6 +219,7 @@ impl ThresholdPlaintextAggregator { c7_proofs_pending: None, decryption_aggregator_proofs: None, last_ec: None, + committee_members: None, } } @@ -483,7 +489,6 @@ impl ThresholdPlaintextAggregator { })?; self.last_ec = Some(ec.clone()); - self.try_publish_complete()?; Ok(()) } @@ -620,6 +625,7 @@ impl ThresholdPlaintextAggregator { pub fn handle_aggregation_proof_signed( &mut self, msg: TypedEvent, + ctx: &mut Context, ) -> Result<()> { let (msg, ec) = msg.into_components(); @@ -652,10 +658,42 @@ impl ThresholdPlaintextAggregator { info!("C7 proof signed — awaiting DecryptionAggregation..."); self.c7_proofs_pending = Some(proofs); self.last_ec = Some(ec.clone()); + self.maybe_start_decryption_aggregation(&ec, ctx.address().recipient())?; self.try_publish_complete() } - fn dispatch_decryption_aggregation(&mut self, ec: &EventContext) -> Result<()> { + fn maybe_start_decryption_aggregation( + &mut self, + ec: &EventContext, + reply: Recipient, + ) -> Result<()> { + if self.c7_proofs_pending.is_none() { + return Ok(()); + } + if self.decryption_aggregator_proofs.is_some() + || self.decryption_aggregation_correlation.is_some() + { + return Ok(()); + } + if !self.proof_aggregation_enabled { + return Ok(()); + } + self.dispatch_decryption_aggregation(ec, Some(reply)) + } + + fn dispatch_decryption_aggregation( + &mut self, + ec: &EventContext, + committee_reply: Option>, + ) -> Result<()> { + if self.committee_members.is_none() { + let reply = committee_reply + .ok_or_else(|| anyhow!("committee reply required to fetch members"))?; + let e3_id = self.e3_id.clone(); + self.sortition.do_send(GetCommitteeMembersRequest { e3_id, reply }); + return Ok(()); + } + let Some(c7_proofs) = self.c7_proofs_pending.as_ref() else { return Ok(()); }; @@ -722,6 +760,7 @@ impl ThresholdPlaintextAggregator { ZkRequest::DecryptionAggregation(DecryptionAggregationRequest { c6_total_slots, jobs, + committee_addresses: self.committee_members.clone().unwrap_or_default(), params_preset: self.params_preset, }), corr, @@ -733,7 +772,11 @@ impl ThresholdPlaintextAggregator { Ok(()) } - pub fn handle_compute_response(&mut self, msg: TypedEvent) -> Result<()> { + pub fn handle_compute_response( + &mut self, + msg: TypedEvent, + ctx: &mut Context, + ) -> Result<()> { let (msg, ec) = msg.into_components(); ensure!( msg.e3_id == self.e3_id, @@ -808,7 +851,7 @@ impl ThresholdPlaintextAggregator { // Not a response we handle — ignore } } - + let _ = ctx; Ok(()) } @@ -854,9 +897,6 @@ impl ThresholdPlaintextAggregator { let Some(c7_proofs) = self.c7_proofs_pending.clone() else { return Ok(()); }; - if let Some(ec) = self.last_ec.clone() { - self.dispatch_decryption_aggregation(&ec)?; - } let dec_ready = self.decryption_aggregator_proofs.is_some() && self.decryption_aggregation_correlation.is_none(); if !dec_ready { @@ -1029,13 +1069,33 @@ 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(&mut self, msg: TypedEvent, _: &mut Self::Context) -> Self::Result { + fn handle( + &mut self, + msg: TypedEvent, + ctx: &mut Self::Context, + ) -> Self::Result { trap( EType::PlaintextAggregation, &self.bus.with_ec(msg.get_ctx()), - || self.handle_compute_response(msg), + || self.handle_compute_response(msg, ctx), ) } } @@ -1107,12 +1167,12 @@ impl Handler> for ThresholdPlaintextAggregato fn handle( &mut self, msg: TypedEvent, - _ctx: &mut Self::Context, + ctx: &mut Self::Context, ) -> Self::Result { trap( EType::PlaintextAggregation, &self.bus.with_ec(msg.get_ctx()), - || self.handle_aggregation_proof_signed(msg), + || self.handle_aggregation_proof_signed(msg, ctx), ) } } @@ -1339,6 +1399,7 @@ mod tests { ZkRequest::DecryptionAggregation(DecryptionAggregationRequest { c6_total_slots: 1, jobs: Vec::new(), + committee_addresses: vec!["0x0000000000000000000000000000000000000001".to_string()], params_preset: BfvPreset::InsecureThreshold512, }), correlation_id, @@ -1381,11 +1442,14 @@ mod tests { (1, vec![dummy_proof(CircuitName::ThresholdShareDecryption)]), ]); - aggregator.dispatch_decryption_aggregation(&test_ctx(E3Failed { + aggregator.committee_members = + Some(vec!["0x0000000000000000000000000000000000000001".to_string()]); + let ec = test_ctx(E3Failed { e3_id: e3_id.clone(), failed_at_stage: E3Stage::None, reason: FailureReason::None, - }))?; + }); + aggregator.dispatch_decryption_aggregation(&ec, None)?; let event = next_event(&history).await?; assert!(matches!( diff --git a/crates/events/src/enclave_event/compute_request/zk.rs b/crates/events/src/enclave_event/compute_request/zk.rs index dce15a8da..411fe9a66 100644 --- a/crates/events/src/enclave_event/compute_request/zk.rs +++ b/crates/events/src/enclave_event/compute_request/zk.rs @@ -76,6 +76,8 @@ pub struct DkgAggregationRequest { pub node_fold_proofs: Vec, pub c5_proof: Proof, pub party_ids: Vec, + /// Ordered committee addresses (`topNodes`) for `committee_hash_*` public inputs. + pub committee_addresses: Vec, pub params_preset: BfvPreset, } @@ -84,6 +86,8 @@ pub struct DkgAggregationRequest { pub struct DecryptionAggregationRequest { pub c6_total_slots: usize, pub jobs: Vec, + /// Ordered committee addresses (`topNodes`) for `committee_hash_*` public inputs. + pub committee_addresses: Vec, pub params_preset: BfvPreset, } diff --git a/crates/evm/src/ciphernode_registry_sol.rs b/crates/evm/src/ciphernode_registry_sol.rs index fc5d92f23..65f24ef6f 100644 --- a/crates/evm/src/ciphernode_registry_sol.rs +++ b/crates/evm/src/ciphernode_registry_sol.rs @@ -489,7 +489,6 @@ impl Handler Handler, contract_address: Address, e3_id: E3id, - nodes: OrderedSet, public_key: ArcBytes, pk_commitment: [u8; 32], dkg_aggregator_proof: Option<&Proof>, @@ -686,15 +683,9 @@ pub async fn publish_committee_to_registry Bytes::new(), }; - let nodes_vec: Vec
= nodes - .into_iter() - .filter_map(|node| node.parse().ok()) - .collect(); - // RPC may not have synced finalization yet send_tx_with_retry("publishCommittee", &["CommitteeNotFinalized"], || { let provider = provider.clone(); - let nodes_vec = nodes_vec.clone(); let public_key_bytes = public_key_bytes.clone(); let proof = proof.clone(); async move { @@ -707,13 +698,7 @@ pub async fn publish_committee_to_registry Deref for E3CommitteeContainsResponse { } } +/// Request the ordered finalized committee member list for an E3. +#[derive(Message, Clone, Debug)] +#[rtype(result = "()")] +pub struct GetCommitteeMembersRequest { + pub e3_id: E3id, + pub reply: Recipient, +} + +/// Response with committee members in party-id order (index == party_id). +#[derive(Message, Clone, Debug)] +#[rtype(result = "()")] +pub struct CommitteeMembersResponse { + pub members: Vec, +} + /// Sortition actor that manages the sortition algorithm and the node state. pub struct Sortition { /// Persistent map of `chain_id -> SortitionBackend`. @@ -716,6 +731,22 @@ impl Handler> for Sortition { } } +impl Handler for Sortition { + type Result = (); + + fn handle( + &mut self, + msg: GetCommitteeMembersRequest, + _: &mut Self::Context, + ) -> Self::Result { + let members = self + .get_committee(&msg.e3_id) + .map(|c| c.members().to_vec()) + .unwrap_or_default(); + let _ = msg.reply.do_send(CommitteeMembersResponse { members }); + } +} + impl Handler> for Sortition where T: Clone + Send + Sync + 'static, diff --git a/crates/utils/src/committee_hash.rs b/crates/utils/src/committee_hash.rs new file mode 100644 index 000000000..051e460e1 --- /dev/null +++ b/crates/utils/src/committee_hash.rs @@ -0,0 +1,77 @@ +// 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. + +//! Canonical committee hash for DKG / decryption aggregator proofs. +//! Must match `CommitteeHashLib.sol` (`keccak256(abi.encodePacked(addresses))`). + +use alloy::primitives::{keccak256, Address, B256, U256}; + +/// Hi/lo limbs of `keccak256(abi.encodePacked(addresses))` for Noir public inputs. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct CommitteeHashLimbs { + pub hi: B256, + pub lo: B256, +} + +/// `keccak256(abi.encodePacked(addresses))` for the ordered on-chain committee. +pub fn hash_committee_addresses(addresses: &[Address]) -> B256 { + let packed: Vec = addresses + .iter() + .flat_map(|addr| addr.into_array()) + .collect(); + keccak256(packed) +} + +/// Split a committee hash into 128-bit limbs for BN254 public inputs. +pub fn split_committee_hash(hash: B256) -> CommitteeHashLimbs { + let value = U256::from_be_bytes(hash.0); + let hi = B256::from(value >> 128); + let lo_mask = (U256::from(1) << 128) - U256::from(1); + let lo = B256::from(value & lo_mask); + CommitteeHashLimbs { hi, lo } +} + +/// Hash and split in one step. +pub fn committee_hash_limbs_from_addresses(addresses: &[Address]) -> CommitteeHashLimbs { + split_committee_hash(hash_committee_addresses(addresses)) +} + +/// Parse checksummed or lowercase hex node addresses (as used in events). +pub fn hash_committee_node_strings(nodes: &[String]) -> anyhow::Result { + let addresses: Vec
= nodes + .iter() + .map(|s| s.parse()) + .collect::>()?; + Ok(hash_committee_addresses(&addresses)) +} + +/// Field hex strings (`0x…`, 32 bytes) for Noir witness `committee_hash_hi` / `committee_hash_lo`. +pub fn committee_hash_field_hex(nodes: &[String]) -> anyhow::Result<(String, String)> { + let limbs = split_committee_hash(hash_committee_node_strings(nodes)?); + Ok((field_hex_from_b256(limbs.hi), field_hex_from_b256(limbs.lo))) +} + +fn field_hex_from_b256(value: B256) -> String { + format!("0x{}", hex::encode(value)) +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy::primitives::address; + + #[test] + fn encode_packed_matches_solidity_layout() { + let nodes = vec![ + address!("0x0000000000000000000000000000000000000001"), + address!("0x0000000000000000000000000000000000000002"), + ]; + let hash = hash_committee_addresses(&nodes); + let limbs = split_committee_hash(hash); + assert_ne!(limbs.hi, B256::ZERO); + assert_ne!(limbs.lo, B256::ZERO); + } +} diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index d76f7b6b5..b00bbbe2c 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -7,6 +7,7 @@ extern crate self as e3_utils; // need this for e3_utils_derive to reference this crate pub mod actix; pub mod alloy; +pub mod committee_hash; pub mod constants; pub mod error; pub mod formatters; 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 0c352bbb0..5ef123668 100644 --- a/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs +++ b/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs @@ -295,6 +295,8 @@ pub struct DkgAggregationInput<'a> { pub c5_proof: &'a Proof, /// Honest party ids in the same order as `node_fold_proofs` (e.g. sorted ascending). pub party_ids: &'a [u64], + /// Ordered committee addresses (`topNodes` / sortition order) for `committee_hash_*` public inputs. + pub committee_addresses: &'a [String], } #[derive(Serialize)] @@ -308,6 +310,8 @@ struct DkgAggregatorWitness { nodes_fold_key_hash: String, c5_key_hash: String, party_ids: Vec, + committee_hash_hi: String, + committee_hash_lo: String, } /// [`CircuitName::DkgAggregator`] over sequential [`CircuitName::NodesFold`] + C5, proved with @@ -355,6 +359,10 @@ pub fn prove_dkg_aggregation( .map(u64_to_field_hex) .collect(); + let (committee_hash_hi, committee_hash_lo) = + e3_utils::committee_hash::committee_hash_field_hex(input.committee_addresses) + .map_err(|e| ZkError::InvalidInput(e.to_string()))?; + let witness = DkgAggregatorWitness { nodes_fold_vk: nodes_fold_vk.verification_key.clone(), nodes_fold_proof: proof_field_strings(&nodes_fold_proof)?, @@ -365,6 +373,8 @@ pub fn prove_dkg_aggregation( nodes_fold_key_hash: nodes_fold_vk.key_hash.clone(), c5_key_hash: c5_vk.key_hash.clone(), party_ids: party_id_fields, + committee_hash_hi, + committee_hash_lo, }; let json = @@ -403,6 +413,8 @@ struct DecryptionAggregatorWitness { c7_public: Vec, c6_fold_key_hash: String, c7_key_hash: String, + committee_hash_hi: String, + committee_hash_lo: String, } /// Prove [`CircuitName::DecryptionAggregator`] for each job (C6 fold + C7), with @@ -411,6 +423,7 @@ pub fn prove_decryption_aggregation_jobs( prover: &ZkProver, c6_total_slots: usize, jobs: &[DecryptionAggregationJob], + committee_addresses: &[String], e3_id: &str, artifacts_dir: &str, ) -> Result, ZkError> { @@ -430,6 +443,10 @@ pub fn prove_decryption_aggregation_jobs( artifacts_dir, )?; + let (committee_hash_hi, committee_hash_lo) = + e3_utils::committee_hash::committee_hash_field_hex(committee_addresses) + .map_err(|e| ZkError::InvalidInput(e.to_string()))?; + let mut out = Vec::with_capacity(jobs.len()); for (i, job) in jobs.iter().enumerate() { let c6_fold = generate_sequential_c6_fold( @@ -450,6 +467,8 @@ pub fn prove_decryption_aggregation_jobs( c7_public: proof_public_field_strings(job.c7_proof)?, c6_fold_key_hash: c6_fold_vk.key_hash.clone(), c7_key_hash: c7_vk.key_hash.clone(), + committee_hash_hi: committee_hash_hi.clone(), + committee_hash_lo: committee_hash_lo.clone(), }; let json = serde_json::to_value(&witness) diff --git a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json index 767f7b9b7..67a36b98f 100644 --- a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json +++ b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json @@ -2558,11 +2558,11 @@ "type": "function" } ], - "bytecode": "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b615af7806100d65f395ff3fe608060405234801561000f575f5ffd5b50600436106102ce575f3560e01c806390173a4111610182578063cb649617116100e0578063f0691cba1161008f578063f0691cba14610886578063f2fde38b14610899578063f3ceba3a146108ac578063f81b8ef6146108cd578063fad8e111146108e0578063fbdb3237146108f3578063fd2f3d011461091b575f5ffd5b8063cb649617146107ef578063cbd16872146107f8578063cf0f34c41461080b578063cfbdc98d1461081e578063d8afed3e1461084d578063e59e469514610860578063ea71aa5714610873575f5ffd5b80639e57b9341161013c5780639e57b93414610607578063a87f4ab91461061a578063ac3d2f421461076c578063bb2d1b8214610794578063bff232c1146107a7578063c1ab0f1f146107ba578063c4ccafa2146107cd575f5ffd5b806390173a41146105705780639117173c146105855780639231238614610598578063929a8faf146105ab57806399c6679d146105cc5780639c8570c8146105f4575f5ffd5b80635d1684181161022f5780637edcd7ab116101e95780637edcd7ab146104e757806381476ec21461050a578063830d71811461051d57806385814243146105305780638da5cb5b146105435780638dcdd86b1461054b5780638e5ce3ad1461055d575f5ffd5b80635d1684181461047d578063647846a51461049d5780636db5c8fd146104b0578063715018a6146104b95780637c8c3b4d146104c15780637cfa9d74146104d4575f5ffd5b806336c5d38a1161028b57806336c5d38a1461039b5780634017daf0146103ca578063406ed35c146103f75780634147a360146104175780634d600e5d146104445780634e92ec63146104575780634fc772641461046a575f5ffd5b806302a3a9c9146102d25780630ef81b2f146102e757806310bc62811461032557806311bd61d91461034d57806315cce224146103755780631ba7294514610388575b5f5ffd5b6102e56102e036600461478a565b61092e565b005b61030f6102f53660046147ac565b5f908152600960205260409020546001600160a01b031690565b60405161031c91906147d0565b60405180910390f35b61030f6103333660046147ac565b60096020525f90815260409020546001600160a01b031681565b61036061035b3660046147f7565b6109da565b60405163ffffffff909116815260200161031c565b6102e561038336600461478a565b610a16565b6102e561039636600461482f565b610abb565b6103bd6103a93660046147ac565b5f908152600f602052604090205460ff1690565b60405161031c9190614871565b6103dd6103d83660046147ac565b610acf565b60405161031c9e9d9c9b9a999897969594939291906148bd565b61040a6104053660046147ac565b610c7a565b60405161031c9190614a97565b6104366104253660046147ac565b600c6020525f908152604090205481565b60405190815260200161031c565b6102e5610452366004614ab4565b610ef7565b6102e56104653660046147ac565b611135565b6102e561047836600461478a565b6111c4565b61049061048b366004614b4a565b611257565b60405161031c9190614b63565b60045461030f906001600160a01b031681565b61043660055481565b6102e56112ee565b6102e56104cf366004614b75565b611301565b6102e56104e23660046147ac565b6113c1565b6104fa6104f5366004614be0565b6114b4565b604051901515815260200161031c565b6102e5610518366004614c57565b61170a565b6102e561052b366004614c77565b6117fe565b60015461030f906001600160a01b031681565b61030f61190a565b5f5461030f906001600160a01b031681565b60035461030f906001600160a01b031681565b610578611938565b60405161031c9190614cc5565b6102e56105933660046147ac565b61197e565b6105786105a63660046147ac565b611aec565b6105be6105b93660046147ac565b611b45565b60405161031c929190614ce6565b61030f6105da3660046147ac565b5f908152601060205260409020546001600160a01b031690565b6104fa610602366004614be0565b611b6c565b610436610615366004614cfc565b611e04565b61075f604080516101e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081019190915250604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a0840152640100000000909104166101c082015290565b60405161031c9190614d33565b61030f61077a3660046147ac565b5f908152600a60205260409020546001600160a01b031690565b6102e56107a2366004614e40565b6123ce565b6102e56107b536600461478a565b61248a565b6102e56107c8366004614c57565b612531565b6104fa6107db36600461478a565b60076020525f908152604090205460ff1681565b61043660065481565b6102e5610806366004614b75565b6125ee565b6102e56108193660046147ac565b6126a8565b61084061082c3660046147ac565b5f908152600d602052604090205460ff1690565b60405161031c9190614e7a565b6102e561085b366004614e88565b6126e5565b6102e561086e36600461478a565b612972565b6102e5610881366004614ea2565b612a0c565b60025461030f906001600160a01b031681565b6102e56108a736600461478a565b612cb9565b6108bf6108ba366004614cfc565b612cf3565b60405161031c929190614ed9565b6103bd6108db3660046147ac565b613747565b6102e56108ee36600461478a565b6138e1565b61030f6109013660046147ac565b600a6020525f90815260409020546001600160a01b031681565b6102e561092936600461478a565b613979565b610936613a08565b6001600160a01b0381166109915760405162461bcd60e51b815260206004820152601f60248201527f496e76616c6964204533526566756e644d616e6167657220616464726573730060448201526064015b60405180910390fd5b600280546001600160a01b0319166001600160a01b0383169081179091556040517f9557d04c1c0b16f93f13b69aed23b3b6ab935bff3c53ac81d17896d3583542ed905f90a250565b6012602052815f5260405f2081600281106109f3575f80fd5b60089182820401919006600402915091509054906101000a900463ffffffff1681565b610a1e613a08565b6001600160a01b03811615801590610a4457506004546001600160a01b03828116911614155b8190610a645760405163eddf07f560e01b815260040161098891906147d0565b50600480546001600160a01b0319166001600160a01b0383161790556040517f722ff84c1234b2482061def5c82c6b5080c117b3cbb69d686844a051e4b8e7f390610ab09083906147d0565b60405180910390a150565b610ac3613a08565b610acc81613a3a565b50565b60086020525f9081526040902080546001820154600283015460058401546006850154600786018054959660ff95861696949593946001600160a01b03841694600160a01b90940490931692909190610b2790614ef9565b80601f0160208091040260200160405190810160405280929190818152602001828054610b5390614ef9565b8015610b9e5780601f10610b7557610100808354040283529160200191610b9e565b820191905f5260205f20905b815481529060010190602001808311610b8157829003601f168201915b50505060088401546009850154600a860154600b870154600c8801805497986001600160a01b03958616989490951696509194509291610bdd90614ef9565b80601f0160208091040260200160405190810160405280929190818152602001828054610c0990614ef9565b8015610c545780601f10610c2b57610100808354040283529160200191610c54565b820191905f5260205f20905b815481529060010190602001808311610c3757829003601f168201915b505050600d90930154919250506001600160a01b0381169060ff600160a01b909104168e565b610c826145f8565b5f8281526008602090815260409182902082516101e08101909352805483526001810154909183019060ff166003811115610cbf57610cbf614849565b6003811115610cd057610cd0614849565b8152600282810154602083015260408051808201808352919093019291600385019182845b815481526020019060010190808311610cf55750505091835250506005820154602082015260068201546001600160a01b0381166040830152600160a01b900460ff166060820152600782018054608090920191610d5290614ef9565b80601f0160208091040260200160405190810160405280929190818152602001828054610d7e90614ef9565b8015610dc95780601f10610da057610100808354040283529160200191610dc9565b820191905f5260205f20905b815481529060010190602001808311610dac57829003601f168201915b505050918352505060088201546001600160a01b0390811660208301526009830154166040820152600a8201546060820152600b8201546080820152600c8201805460a090920191610e1a90614ef9565b80601f0160208091040260200160405190810160405280929190818152602001828054610e4690614ef9565b8015610e915780601f10610e6857610100808354040283529160200191610e91565b820191905f5260205f20905b815481529060010190602001808311610e7457829003601f168201915b5050509183525050600d91909101546001600160a01b038082166020840152600160a01b90910460ff16151560409092019190915260a0820151919250839116610ef15760405163cd6f4a4f60e01b815260040161098891815260200190565b50919050565b5f610f00613af7565b805490915060ff600160401b82041615906001600160401b03165f81158015610f265750825b90505f826001600160401b03166001148015610f415750303b155b905081158015610f4f575080155b15610f6d5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610f9757845460ff60401b1916600160401b1785555b610fa033613b21565b610fa9876126a8565b610fb28b6138e1565b610fbb8a612972565b610fc48961092e565b610fcd88610a16565b610fd686613a3a565b604080516101e081018252620186a080825261c3506020808401829052612710948401859052603260608501819052620493e060808601819052620f424060a0870181905261138860c088018190525f60e089018190526105dc6101008a015261012089018190526109c46101408a018190526101608a018390526101808a01526101a089018190526101c090980197909752601895909555601993909355601a95909555601b94909455601c55601d55601e55601f80546001600160f01b03191669027104e202710000017760a21b179055805467ffffffffffffffff191690556110c061190a565b6001600160a01b03168c6001600160a01b0316146110e1576110e18c612cb9565b831561112757845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050505050565b61113d613a08565b5f8181526009602052604090205481906001600160a01b0316611176576040516381c4951960e01b815260040161098891815260200190565b505f818152600960205260409081902080546001600160a01b0319169055517f104eb329a192aef26eddea07c2af5ad2587792e62b37ed4045b6ba59bc5540fc90610ab09083815260200190565b6111cc613a08565b6001600160a01b0381165f90815260076020526040902054819060ff16611207576040516321ac7c5f60e01b815260040161098891906147d0565b506001600160a01b0381165f9081526007602052604090819020805460ff19169055517f56070b80bd617fcd2f7a284861edb488830a38f9dedcd77b2cb2f4eac17743e790610ab09083906147d0565b600b6020525f90815260409020805461126f90614ef9565b80601f016020809104026020016040519081016040528092919081815260200182805461129b90614ef9565b80156112e65780601f106112bd576101008083540402835291602001916112e6565b820191905f5260205f20905b8154815290600101906020018083116112c957829003601f168201915b505050505081565b6112f6613a08565b6112ff5f613b32565b565b611309613a08565b6001600160a01b0381161580159061133a57505f828152600a60205260409020546001600160a01b03828116911614155b829061135c576040516381c4951960e01b815260040161098891815260200190565b505f828152600a60205260409081902080546001600160a01b0319166001600160a01b0384161790555182907f53661e3e12f23eea1e322a5352171ad3e4407d1394f869f53bb148c27e00908a906113b59084906147d0565b60405180910390a25050565b5f546001600160a01b031633146113eb5760405163b56831db60e01b815260040160405180910390fd5b5f818152600d602052604090205460ff16600181600681111561141057611410614849565b1461143557816001826040516337e1404160e01b815260040161098893929190614f2b565b5f828152600d60205260409020805460ff1916600217905560155461145a9042614f60565b5f838152600e602052604080822092909255905183917fc44405af9078047712501f519e1fb900c2896c62b488336f84529c72ae16e6f191a2815f516020615acb5f395f51905f52600160026040516113b5929190614f73565b5f5f6114bf87610c7a565b5f888152600d602052604090205490915060ff1660048160068111156114e7576114e7614849565b148860048390919261150f576040516337e1404160e01b815260040161098893929190614f2b565b5050505f888152600e60209081526040918290208251606081018452815481526001820154928101929092526002015491810182905290899042811015611572576040516308f3034360e31b815260048101929092526024820152604401610988565b50505f898152600860205260409020600c0161158f888a83615001565b505f898152600d60205260409020805460ff191660051790556101c08301511561168957846115d157604051631eae1a4d60e31b815260040160405180910390fd5b8261010001516001600160a01b031663258ae58289896040516115f59291906150b5565b6040519081900381206001600160e01b031960e084901b16825261161f918a908a906004016150ec565b602060405180830381865afa15801561163a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061165e919061511b565b935087878561168257604051632f9f8ab960e01b8152600401610988929190615136565b505061168e565b600193505b61169789613ba2565b887f3a140076c461ebc41d74833ae0ee8bbc8079a135a63392098cd381e84350b69b898989896040516116cd9493929190615149565b60405180910390a2885f516020615acb5f395f51905f52600460056040516116f6929190614f73565b60405180910390a250505095945050505050565b5f546001600160a01b031633146117345760405163b56831db60e01b815260040160405180910390fd5b5f828152600860209081526040808320600d9092529091205460ff16600281600681111561176457611764614849565b1461178957836002826040516337e1404160e01b815260040161098893929190614f2b565b5f848152600d6020526040808220805460ff19166003179055600a84018590555185917f11df18edb9bc9cd90a79068e0e208b630202148643d797d6150e7bacb733e63c91a2835f516020615acb5f395f51905f52600260036040516117f0929190614f73565b60405180910390a250505050565b611806613a08565b806118425760405162461bcd60e51b815260206004820152600c60248201526b456d70747920706172616d7360a01b6044820152606401610988565b60ff83165f908152600b60205260409020805461185e90614ef9565b1590506118ad5760405162461bcd60e51b815260206004820152601b60248201527f506172616d53657420616c7265616479207265676973746572656400000000006044820152606401610988565b60ff83165f908152600b602052604090206118c9828483615001565b507f6e4a4ea7f38fc775e616080b155744337e6216848e886a69c918b4ab84da21958383836040516118fd9392919061517a565b60405180910390a1505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b61195960405180606001604052805f81526020015f81526020015f81525090565b5060408051606081018252601554815260165460208201526017549181019190915290565b5f818152600d602052604090205460ff1660068160068111156119a3576119a3614849565b1482906119c657604051637cb2d48360e11b815260040161098891815260200190565b505f828152600c602052604090205482816119f7576040516345ba89d560e11b815260040161098891815260200190565b505f838152600c60205260408120819055611a118461400e565b5f858152601160205260409020546002549192506001600160a01b0390811691611a3e91839116856140fb565b60025460405163da19b69760e01b81526001600160a01b039091169063da19b69790611a749088908790879087906004016151d9565b5f604051808303815f87803b158015611a8b575f5ffd5b505af1158015611a9d573d5f5f3e3d5ffd5b50505050847f5297818f48a66292b8b3e2caab83eec531b669bb20807fd38cf006adb2a07317848451604051611add929190918252602082015260400190565b60405180910390a25050505050565b611b0d60405180606001604052805f81526020015f81526020015f81525090565b505f908152600e6020908152604091829020825160608101845281548152600182015492810192909252600201549181019190915290565b5f818152600d6020526040812054819060ff16611b628482614158565b9250925050915091565b5f5f611b7787610c7a565b5f888152600d602052604090205490915060ff166003816006811115611b9f57611b9f614849565b1488600383909192611bc7576040516337e1404160e01b815260040161098893929190614f2b565b5050505f888152600e6020908152604091829020825160608101845281548152600182015492810183905260029091015492810192909252899042811015611c2b576040516308f3034360e31b815260048101929092526024820152604401610988565b5050606083015160200151899042811115611c625760405163017e35e560e71b815260048101929092526024820152604401610988565b5050610160830151899015611c8d57604051637eb9cea960e11b815260040161098891815260200190565b505f8888604051611c9f9291906150b5565b60408051918290039091205f8c815260086020908152838220600b01839055600d905291909120805460ff19166004179055601754909150611ce19042614f60565b5f8b8152600e6020526040908190206002019190915560a08501519051632f0e1bbf60e01b81526001600160a01b0390911690632f0e1bbf90611d2e908d9085908c908c90600401615224565b6020604051808303815f875af1158015611d4a573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d6e919061511b565b9450888886611d9257604051632f9f8ab960e01b8152600401610988929190615136565b5050897f7cc27e4a5626cbc4f8ba1a927b0448de55e6a114bc87660331270c5109ade0718a8a604051611dc6929190615136565b60405180910390a2895f516020615acb5f395f51905f5260036004604051611def929190614f73565b60405180910390a25050505095945050505050565b5f80600b81611e1960a0860160808701614b4a565b60ff1660ff1681526020019081526020015f208054611e3790614ef9565b905011611e565760405162461bcd60e51b815260040161098890615243565b5f601281611e67602086018661527a565b6003811115611e7857611e78614849565b6003811115611e8957611e89614849565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411611eb0579050505050505090505f81600160028110611f0757611f07615210565b602002015163ffffffff1611835f016020810190611f25919061527a565b90611f445760405163286c068d60e11b81526004016109889190615293565b506020808201518251604080516101e081018252601854815260195481860152601a5491810191909152601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e0830152600160a01b810461ffff908116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152925463ffffffff8181166101a0860181905264010000000090920481166101c08601529283169391909216911561206f576101a081015163ffffffff16846001602002015163ffffffff161015865f01602081019061204e919061527a565b9061206d5760405163010b971d60e31b81526004016109889190615293565b505b6101c081015163ffffffff16156120be576101c081015184519063ffffffff90811690821610156120bc57604051630a4b6b6360e11b815263ffffffff9091166004820152602401610988565b505b604086013560208701358110156120eb5760405163174b5a0760e21b815260040161098891815260200190565b506101808101516017545f91612710916121099161ffff16906152a1565b61211391906152b8565b61271061ffff1683610160015161ffff1660156001015461213491906152a1565b61213e91906152b8565b61271061ffff1684610140015161ffff1660155f015461215e91906152a1565b61216891906152b8565b5f5460408051634f87c3a560e11b8152815160208e81013594938f0135936001600160a01b031692639f0f874a92600480830193928290030181865afa1580156121b4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121d891906152d7565b6121e29190614f60565b6121ec91906152ee565b6121f69190614f60565b6122009190614f60565b61220a9190614f60565b90505f6122186001866152ee565b6122239060046152a1565b61222e90600e614f60565b90505f85845f015161224091906152a1565b90508186856020015161225391906152a1565b61225d91906152a1565b6122679082614f60565b905060018611156122af57600261227f6001886152ee565b61228990886152a1565b856040015161229891906152a1565b6122a291906152b8565b6122ac9082614f60565b90505b81868560c001516122c091906152a1565b6122ca91906152a1565b6122d49082614f60565b9050828685606001516122e791906152a1565b6122f191906152a1565b6122fb9082614f60565b905084846080015161230d91906152a1565b6123179082614f60565b9050600185111561235f57600261232f6001876152ee565b61233990876152a1565b856040015161234891906152a1565b61235291906152b8565b61235c9082614f60565b90505b60a084015161236e9082614f60565b610100850151909150612710906123899061ffff1682614f60565b61239390836152a1565b61239d91906152b8565b975087806123c157604051638c4fcd9360e01b815260040161098891815260200190565b5050505050505050919050565b5f546001600160a01b03163314806123f057506003546001600160a01b031633145b61240d57604051639e75a8b560e01b815260040160405180910390fd5b5f8160ff161180156124235750600d60ff821611155b6124685760405162461bcd60e51b815260206004820152601660248201527524b73b30b634b2103330b4b63ab932903932b0b9b7b760511b6044820152606401610988565b612486828260ff16600d81111561248157612481614849565b6142e0565b5050565b612492613a08565b6001600160a01b0381166124e85760405162461bcd60e51b815260206004820152601f60248201527f496e76616c696420536c617368696e674d616e616765722061646472657373006044820152606401610988565b600380546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b6003546001600160a01b0316331461255c576040516357d6948d60e11b815260040160405180910390fd5b60025460405163c1ab0f1f60e01b815260048101849052602481018390526001600160a01b039091169063c1ab0f1f906044015f604051808303815f87803b1580156125a6575f5ffd5b505af11580156125b8573d5f5f3e3d5ffd5b50505050817f4f41a3b0a032ebcae925f2ace77d507435840ca4b2dbaffdd7723fa8d72ee542826040516113b591815260200190565b6125f6613a08565b6001600160a01b0381161580159061262757505f828152600960205260409020546001600160a01b03828116911614155b8290612649576040516381c4951960e01b815260040161098891815260200190565b505f8281526009602090815260409182902080546001600160a01b0319166001600160a01b03851617905590518381527ff4041a3f914dac3bc9bf5f003ba41f28dbb84abe42f4e07c76266f5c8ceecb69910160405180910390a15050565b6126b0613a08565b60058190556040518181527fba0716ba1ee2ea8ecc4c64119b4537cdb42a99d82acf92af5b87607b8b52355290602001610ab0565b6126ed613a08565b6127106127026101208301610100840161531b565b61ffff16111561271a6101208301610100840161531b565b9061273f576040516301027fc160e21b815261ffff9091166004820152602401610988565b506127106127556101408301610120840161531b565b61ffff16111561276d6101408301610120840161531b565b90612792576040516301027fc160e21b815261ffff9091166004820152602401610988565b506127106127a86101608301610140840161531b565b61ffff1611156127c06101608301610140840161531b565b906127e557604051633239953960e01b815261ffff9091166004820152602401610988565b506127106127fb6101808301610160840161531b565b61ffff1611156128136101808301610160840161531b565b9061283857604051633239953960e01b815261ffff9091166004820152602401610988565b5061271061284e6101a08301610180840161531b565b61ffff1611156128666101a08301610180840161531b565b9061288b57604051633239953960e01b815261ffff9091166004820152602401610988565b5061289e6101408201610120830161531b565b61ffff1615806128c757505f6128bb610100830160e0840161478a565b6001600160a01b031614155b6128e45760405163015f92ff60e51b815260040160405180910390fd5b6128f66101e082016101c08301615352565b63ffffffff1661290e6101c083016101a08401615352565b63ffffffff161015612933576040516392f55c6560e01b815260040160405180910390fd5b8060186129408282615391565b9050507fbf3951313e980027eb48ce363fdb707286195ec6a0f802ac153927cf929c3fc681604051610ab0919061554f565b61297a613a08565b6001600160a01b038116158015906129a057506001546001600160a01b03828116911614155b81906129c0576040516320252f0b60e01b815260040161098891906147d0565b50600180546001600160a01b0319166001600160a01b0383161790556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790610ab09083906147d0565b612a14613a08565b612a216020820182615352565b63ffffffff16612a376040830160208401615352565b63ffffffff1610158015612a5c57505f612a546020830183615352565b63ffffffff16115b612a7957604051634564ab9b60e01b815260040160405180910390fd5b604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a08401819052640100000000909204166101c083015215612bb2576101a081015163ffffffff16612b626040840160208501615352565b63ffffffff161015612b7a6040840160208501615352565b826101a001519091612baf57604051633ccc4c2160e21b815263ffffffff928316600482015291166024820152604401610988565b50505b6101c081015163ffffffff1615612c29576101c081015163ffffffff16612bdc6020840184615352565b63ffffffff161015612bf16020840184615352565b826101c001519091612c265760405163156c4e5b60e11b815263ffffffff928316600482015291166024820152604401610988565b50505b8160125f856003811115612c3f57612c3f614849565b6003811115612c5057612c50614849565b815260208101919091526040015f20612c6a916002614675565b50826003811115612c7d57612c7d614849565b7f8b56fae526eee054f0849759a99fc7d4ff3823824ebf097a56f7d78adb6b34fa83604051612cac9190615659565b60405180910390a2505050565b612cc1613a08565b6001600160a01b038116612cea575f604051631e4fbdf760e01b815260040161098891906147d0565b610acc81613b32565b5f612cfc6145f8565b5f601281612d0d602087018761527a565b6003811115612d1e57612d1e614849565b6003811115612d2f57612d2f614849565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411612d56579050505050505090505f81600160028110612dad57612dad615210565b602002015163ffffffff1611845f016020810190612dcb919061527a565b90612dea5760405163286c068d60e11b81526004016109889190615293565b50602084013542811015612e1457604051630b99e87960e01b815260040161098891815260200190565b5060408401356020850135811015612e425760405163174b5a0760e21b815260040161098891815260200190565b506017546016545f9190612e5a4260408901356152ee565b612e649190614f60565b612e6e9190614f60565b905060055481108190612e97576040516313b783af60e21b815260040161098891815260200190565b5060075f612eab608088016060890161478a565b6001600160a01b0316815260208101919091526040015f205460ff16612ed7608087016060880161478a565b90612ef65760405163295a6a6f60e11b815260040161098891906147d0565b505f612f0186611e04565b60068054965090915085905f612f1683615699565b9091555050604080514460208201529081018690525f9060600160408051601f1981840301815291815281516020928301205f898152600c84528281208690556004546011855283822080546001600160a01b03199081166001600160a01b0393841617909155601f805460138852868520805461ffff191661ffff600160b01b909304929092169190911790555460148752858420805483169190931617909155600d8552838220805460ff191660011790556010909452829020805433941693909317909255601654919250612ff19190890135614f60565b5f878152600e60209081526040909120600101919091558186526130179088018861527a565b8560200190600381111561302d5761302d614849565b9081600381111561304057613040614849565b905250436040808701919091528051808201825290602089019060029083908390808284375f920191909152505050606080870191909152613088906080890190890161478a565b6001600160a01b031660a0808701919091526130a990880160808901614b4a565b60ff1660c0808701919091526130c1908801886156b1565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050505060e08087019190915261310e9061010089019089016156f3565b15156101c08601525f610140860181905261016086018190526040805160208101909152818152610180870152336101a0870152600b8161315560a08b0160808c01614b4a565b60ff1660ff1681526020019081526020015f20805461317390614ef9565b80601f016020809104026020016040519081016040528092919081815260200182805461319f90614ef9565b80156131ea5780601f106131c1576101008083540402835291602001916131ea565b820191905f5260205f20905b8154815290600101906020018083116131cd57829003601f168201915b505050505090505f8151116132115760405162461bcd60e51b815260040161098890615243565b5f61322260808a0160608b0161478a565b6001600160a01b031663fefd9a8b89858561324060a08f018f6156b1565b8f8060c0019061325091906156b1565b6040518863ffffffff1660e01b8152600401613272979695949392919061570e565b6020604051808303815f875af115801561328e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132b291906152d7565b5f818152600960205260409020549091506001600160a01b031681816132ee576040516381c4951960e01b815260040161098891815260200190565b505f828152600a60205260409020546001600160a01b03168281613328576040516381c4951960e01b815260040161098891815260200190565b50608089018390526001600160a01b038083166101008b015281166101208a01525f8a81526008602090815260409091208a518155908a0151600180830180548d94939260ff19919091169083600381111561338657613386614849565b0217905550604082015181600201556060820151816003019060026133ac929190614716565b506080820151600582015560a082015160068201805460c085015160ff16600160a01b026001600160a81b03199091166001600160a01b039093169290921791909117905560e082015160078201906134059082615762565b506101008201516008820180546001600160a01b039283166001600160a01b031991821617909155610120840151600984018054919093169116179055610140820151600a820155610160820151600b820155610180820151600c82019061346d9082615762565b506101a0820151600d90910180546101c0909301511515600160a01b026001600160a81b03199093166001600160a01b0392831617929092179091556004546134b9911633308961443b565b608089018390526001600160a01b038083166101008b015281166101208a01525f8a81526008602090815260409091208a518155908a0151600180830180548d94939260ff19919091169083600381111561351657613516614849565b02179055506040820151816002015560608201518160030190600261353c929190614716565b506080820151600582015560a082015160068201805460c085015160ff16600160a01b026001600160a81b03199091166001600160a01b039093169290921791909117905560e082015160078201906135959082615762565b506101008201516008820180546001600160a01b039283166001600160a01b031991821617909155610120840151600984018054919093169116179055610140820151600a820155610160820151600b820155610180820151600c8201906135fd9082615762565b506101a0820151600d90910180546101c0909301511515600160a01b026001600160a81b03199093166001600160a01b0392831617929092179091555f5460405163291a691b60e01b815291169063291a691b90613663908d9089908d90600401615817565b6020604051808303815f875af115801561367f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906136a3919061511b565b6136c057604051630d8dbe2560e01b815260040160405180910390fd5b6136d060808c0160608d0161478a565b6001600160a01b03167f5090c9764b5cd13df7afc0013f733dfbe6eaf1b6ddc22a5e291fa387efd4c15e8b8b60405161370a929190614ed9565b60405180910390a2895f516020615acb5f395f51905f525f6001604051613732929190614f73565b60405180910390a25050505050505050915091565b5f818152600d602052604081205460ff168181600681111561376b5761376b614849565b0361379057826001826040516337e1404160e01b815260040161098893929190614f2b565b60058160068111156137a4576137a4614849565b036137c55760405163462c7bed60e01b815260048101849052602401610988565b60068160068111156137d9576137d9614849565b036137fa57604051633de16e3560e11b815260048101849052602401610988565b5f6138058483614158565b935090508061382a57604051639f65d93560e01b815260048101859052602401610988565b5f848152600d6020526040902080546006919060ff191660018302179055505f848152600f60205260409020805484919060ff1916600183600d81111561387357613873614849565b0217905550835f516020615acb5f395f51905f52836006604051613898929190614f73565b60405180910390a2837fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb83856040516138d292919061585c565b60405180910390a25050919050565b6138e9613a08565b6001600160a01b0381161580159061390e57505f546001600160a01b03828116911614155b819061392e576040516375ac4eb760e11b815260040161098891906147d0565b505f80546001600160a01b0319166001600160a01b0383161790556040517f80052b810d39120cf6c976cca504a21703f585521dc7a41c6d241090e6c579b690610ab09083906147d0565b6001600160a01b0381165f90815260076020526040902054819060ff16156139b55760405163b29d459560e01b815260040161098891906147d0565b506001600160a01b0381165f9081526007602052604090819020805460ff19166001179055517fb8d368517268f297fff00825d67d098763117d061360d31027be5b2e1a59d46790610ab09083906147d0565b33613a1161190a565b6001600160a01b0316146112ff573360405163118cdaa760e01b815260040161098891906147d0565b8035613a595760405163055f269d60e01b815260040160405180910390fd5b5f816020013511613a7d5760405163055f269d60e01b815260040160405180910390fd5b5f816040013511613aa15760405163055f269d60e01b815260040160405180910390fd5b80356015819055602080830135601681905560408085013560178190558151948552928401919091528201527f7e86ba16b805e2835af5c5b7aa5a942ced8bcc1fb95a05fbe42dae3862350a1690606001610ab0565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005b92915050565b613b2961447a565b610acc8161449f565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b5f805460405162beb08960e51b8152600481018490526001600160a01b03909116906317d61120906024015f60405180830381865afa158015613be7573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613c0e9190810190615933565b5080515f848152600c60209081526040808320805490849055601190925282205493945091926001600160a01b031690829003613cad576002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613c79908890889086906004016159f8565b5f604051808303815f87803b158015613c90575f5ffd5b505af1158015613ca2573d5f5f3e3d5ffd5b505050505050505050565b825f03613d4e575f858152601060205260409020546001600160a01b03168015613ce557613ce56001600160a01b03831682856140fb565b6002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613d19908990899087906004016159f8565b5f604051808303815f87803b158015613d30575f5ffd5b505af1158015613d42573d5f5f3e3d5ffd5b50505050505050505050565b5f85815260136020908152604080832054601490925282205461ffff909116906001600160a01b03168115801590613d8e57506001600160a01b03811615155b15613dca57612710613da461ffff8416876152a1565b613dae91906152b8565b92508215613dca57613dca6001600160a01b03851682856140fb565b5f613dd584876152ee565b90505f876001600160401b03811115613df057613df0614f8e565b604051908082528060200260200182016040528015613e19578160200160208202803683370190505b5090505f613e2789846152b8565b90505f805b8a811015613e665782848281518110613e4757613e47615210565b6020908102919091010152613e5c8383614f60565b9150600101613e2c565b505f613e7282866152ee565b90508015613eaf578084613e8760018e6152ee565b81518110613e9757613e97615210565b60200260200101818151613eab9190614f60565b9052505b600154613ec9906001600160a01b038b81169116876144a7565b60015f9054906101000a90046001600160a01b03166001600160a01b031663dd8c818e8a8e876040518463ffffffff1660e01b8152600401613f0d93929190615a58565b5f604051808303815f87803b158015613f24575f5ffd5b505af1158015613f36573d5f5f3e3d5ffd5b5050600154613f5492506001600160a01b038c81169250165f6144a7565b8c7fac9fe8ad7f55eac03284399116ecafc104f10459773f4cdf47063c46e5be335a8d86604051613f86929190615a8d565b60405180910390a260025f9054906101000a90046001600160a01b03166001600160a01b03166341489f158e8e8c6040518463ffffffff1660e01b8152600401613fd2939291906159f8565b5f604051808303815f87803b158015613fe9575f5ffd5b505af1158015613ffb573d5f5f3e3d5ffd5b5050505050505050505050505050505050565b5f818152600f602052604090205460609060ff16600181600d81111561403657614036614849565b14806140535750600281600d81111561405157614051614849565b145b1561408b575f5b604051908082528060200260200182016040528015614083578160200160208202803683370190505b509392505050565b5f5460405162beb08960e51b8152600481018590526001600160a01b03909116906317d61120906024015f60405180830381865afa9250505080156140f157506040513d5f823e601f3d908101601f191682016040526140ee9190810190615933565b60015b614083575f61405a565b61415383846001600160a01b031663a9059cbb8585604051602401614121929190615ab1565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050614533565b505050565b5f828152600e60209081526040808320815160608101835281548152600182015493810193909352600201548282015282549051632800d82960e01b81526004810186905283929183916001600160a01b0390911690632800d82990602401602060405180830381865afa1580156141d2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906141f691906152d7565b9050600185600681111561420c5761420c614849565b14801561421857508042115b1561422b576001809350935050506142d9565b600285600681111561423f5761423f614849565b14801561424c5750815142115b1561426057600160039350935050506142d9565b600385600681111561427457614274614849565b1480156142845750816020015142115b1561429857600160069350935050506142d9565b60048560068111156142ac576142ac614849565b1480156142bc5750816040015142115b156142d0576001600a9350935050506142d9565b5f5f9350935050505b9250929050565b5f828152600d602052604081205460ff169081600681111561430457614304614849565b0361432957826001826040516337e1404160e01b815260040161098893929190614f2b565b600581600681111561433d5761433d614849565b0361435e5760405163462c7bed60e01b815260048101849052602401610988565b600681600681111561437257614372614849565b0361439357604051633de16e3560e11b815260048101849052602401610988565b5f838152600d6020526040902080546006919060ff191660018302179055505f838152600f60205260409020805483919060ff1916600183600d8111156143dc576143dc614849565b0217905550825f516020615acb5f395f51905f52826006604051614401929190614f73565b60405180910390a2827fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb8284604051612cac92919061585c565b6040516001600160a01b0384811660248301528381166044830152606482018390526144749186918216906323b872dd90608401614121565b50505050565b614482614596565b6112ff57604051631afcd79f60e31b815260040160405180910390fd5b612cc161447a565b5f836001600160a01b031663095ea7b384846040516024016144ca929190615ab1565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050905061450384826145af565b6144745761452d84856001600160a01b031663095ea7b3865f604051602401614121929190615ab1565b61447484825b5f5f60205f8451602086015f885af180614552576040513d5f823e3d81fd5b50505f513d91508115614569578060011415614576565b6001600160a01b0384163b155b156144745783604051635274afe760e01b815260040161098891906147d0565b5f61459f613af7565b54600160401b900460ff16919050565b5f5f5f5f60205f8651602088015f8a5af192503d91505f5190508280156145ee575081156145e057806001146145ee565b5f866001600160a01b03163b115b9695505050505050565b604080516101e081019091525f808252602082019081526020015f8152602001614620614744565b81525f602082018190526040820181905260608083018290526080830181905260a0830182905260c0830182905260e08301829052610100830182905261012083015261014082018190526101609091015290565b600183019183908215614706579160200282015f5b838211156146d457833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff160217905550926020019260040160208160030104928301926001030261468a565b80156147045782816101000a81549063ffffffff02191690556004016020816003010492830192600103026146d4565b505b50614712929150614762565b5090565b8260028101928215614706579160200282015b82811115614706578251825591602001919060010190614729565b60405180604001604052806002906020820280368337509192915050565b5b80821115614712575f8155600101614763565b6001600160a01b0381168114610acc575f5ffd5b5f6020828403121561479a575f5ffd5b81356147a581614776565b9392505050565b5f602082840312156147bc575f5ffd5b5035919050565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b8035600481106147f2575f5ffd5b919050565b5f5f60408385031215614808575f5ffd5b614811836147e4565b946020939093013593505050565b5f60608284031215610ef1575f5ffd5b5f6060828403121561483f575f5ffd5b6147a5838361481f565b634e487b7160e01b5f52602160045260245ffd5b600e811061486d5761486d614849565b9052565b60208101613b1b828461485d565b6004811061486d5761486d614849565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b8e81526148cd602082018f61487f565b8c60408201528b60608201526148e6608082018c6147c3565b60ff8a1660a08201526101c060c08201525f6149066101c083018b61488f565b61491360e084018b6147c3565b61492161010084018a6147c3565b8761012084015286610140840152828103610160840152614942818761488f565b9150506149536101808301856147c3565b8215156101a08301529f9e505050505050505050505050505050565b805f5b6002811015614474578151845260209384019390910190600101614972565b805182525f60208201516149a8602085018261487f565b506040820151604084015260608201516149c5606085018261496f565b50608082015160a084015260a08201516149e260c08501826147c3565b5060c082015160ff811660e08501525060e0820151610200610100850152614a0e61020085018261488f565b9050610100830151614a246101208601826147c3565b50610120830151614a396101408601826147c3565b506101408301516101608501526101608301516101808501526101808301518482036101a0860152614a6b828261488f565b9150506101a0830151614a826101c08601826147c3565b506101c08301518015156101e0860152614083565b602081525f6147a56020830184614991565b80356147f281614776565b5f5f5f5f5f5f5f610120888a031215614acb575f5ffd5b8735614ad681614776565b96506020880135614ae681614776565b95506040880135614af681614776565b94506060880135614b0681614776565b93506080880135614b1681614776565b925060a08801359150614b2c8960c08a0161481f565b905092959891949750929550565b803560ff811681146147f2575f5ffd5b5f60208284031215614b5a575f5ffd5b6147a582614b3a565b602081525f6147a5602083018461488f565b5f5f60408385031215614b86575f5ffd5b823591506020830135614b9881614776565b809150509250929050565b5f5f83601f840112614bb3575f5ffd5b5081356001600160401b03811115614bc9575f5ffd5b6020830191508360208285010111156142d9575f5ffd5b5f5f5f5f5f60608688031215614bf4575f5ffd5b8535945060208601356001600160401b03811115614c10575f5ffd5b614c1c88828901614ba3565b90955093505060408601356001600160401b03811115614c3a575f5ffd5b614c4688828901614ba3565b969995985093965092949392505050565b5f5f60408385031215614c68575f5ffd5b50508035926020909101359150565b5f5f5f60408486031215614c89575f5ffd5b614c9284614b3a565b925060208401356001600160401b03811115614cac575f5ffd5b614cb886828701614ba3565b9497909650939450505050565b81518152602080830151908201526040808301519082015260608101613b1b565b8215158152604081016147a5602083018461485d565b5f60208284031215614d0c575f5ffd5b81356001600160401b03811115614d21575f5ffd5b820161010081850312156147a5575f5ffd5b5f6101e082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e0830151614d8d60e08401826147c3565b50610100830151614da561010084018261ffff169052565b50610120830151614dbd61012084018261ffff169052565b50610140830151614dd561014084018261ffff169052565b50610160830151614ded61016084018261ffff169052565b50610180830151614e0561018084018261ffff169052565b506101a0830151614e1f6101a084018263ffffffff169052565b506101c0830151614e396101c084018263ffffffff169052565b5092915050565b5f5f60408385031215614e51575f5ffd5b82359150614e6160208401614b3a565b90509250929050565b6007811061486d5761486d614849565b60208101613b1b8284614e6a565b5f6101e0828403128015614e9a575f5ffd5b509092915050565b5f5f60608385031215614eb3575f5ffd5b614ebc836147e4565b915083606084011115614ecd575f5ffd5b50926020919091019150565b828152604060208201525f614ef16040830184614991565b949350505050565b600181811c90821680614f0d57607f821691505b602082108103610ef157634e487b7160e01b5f52602260045260245ffd5b83815260608101614f3f6020830185614e6a565b614ef16040830184614e6a565b634e487b7160e01b5f52601160045260245ffd5b80820180821115613b1b57613b1b614f4c565b60408101614f818285614e6a565b6147a56020830184614e6a565b634e487b7160e01b5f52604160045260245ffd5b601f82111561415357805f5260205f20601f840160051c81016020851015614fc75750805b601f840160051c820191505b81811015614fe6575f8155600101614fd3565b5050505050565b5f19600383901b1c191660019190911b1790565b6001600160401b0383111561501857615018614f8e565b61502c836150268354614ef9565b83614fa2565b5f601f841160018114615058575f85156150465750838201355b6150508682614fed565b845550614fe6565b5f83815260208120601f198716915b828110156150875786850135825560209485019460019092019101615067565b50868210156150a3575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b818382375f9101908152919050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b838152604060208201525f6151056040830184866150c4565b95945050505050565b8015158114610acc575f5ffd5b5f6020828403121561512b575f5ffd5b81516147a58161510e565b602081525f614ef16020830184866150c4565b604081525f61515c6040830186886150c4565b828103602084015261516f8185876150c4565b979650505050505050565b60ff84168152604060208201525f6151056040830184866150c4565b5f8151808452602084019350602083015f5b828110156151cf5781516001600160a01b03168652602095860195909101906001016151a8565b5093949350505050565b848152836020820152608060408201525f6151f76080830185615196565b905060018060a01b038316606083015295945050505050565b634e487b7160e01b5f52603260045260245ffd5b848152836020820152606060408201525f6145ee6060830184866150c4565b6020808252601c908201527f42465620706172616d20736574206e6f74207265676973746572656400000000604082015260600190565b5f6020828403121561528a575f5ffd5b6147a5826147e4565b60208101613b1b828461487f565b8082028115828204841417613b1b57613b1b614f4c565b5f826152d257634e487b7160e01b5f52601260045260245ffd5b500490565b5f602082840312156152e7575f5ffd5b5051919050565b81810381811115613b1b57613b1b614f4c565b61ffff81168114610acc575f5ffd5b80356147f281615301565b5f6020828403121561532b575f5ffd5b81356147a581615301565b63ffffffff81168114610acc575f5ffd5b80356147f281615336565b5f60208284031215615362575f5ffd5b81356147a581615336565b5f8135613b1b81614776565b5f8135613b1b81615301565b5f8135613b1b81615336565b813581556020820135600182015560408201356002820155606082013560038201556080820135600482015560a0820135600582015560c08201356006820155600781016154016153e460e0850161536d565b82546001600160a01b0319166001600160a01b0391909116178255565b6154316154116101008501615379565b82805461ffff60a01b191660a09290921b61ffff60a01b16919091179055565b6154616154416101208501615379565b82805461ffff60b01b191660b09290921b61ffff60b01b16919091179055565b6154916154716101408501615379565b82805461ffff60c01b191660c09290921b61ffff60c01b16919091179055565b6154c16154a16101608501615379565b82805461ffff60d01b191660d09290921b61ffff60d01b16919091179055565b6154f16154d16101808501615379565b82805461ffff60e01b191660e09290921b61ffff60e01b16919091179055565b506008810161551d6155066101a08501615385565b825463ffffffff191663ffffffff91909116178255565b61415361552d6101c08501615385565b825467ffffffff00000000191660209190911b67ffffffff0000000016178255565b813581526020808301359082015260408083013590820152606080830135908201526080808301359082015260a0808301359082015260c080830135908201526101e081016155a060e08401614aa9565b6155ad60e08401826147c3565b506155bb6101008401615310565b61ffff166101008301526155d26101208401615310565b61ffff166101208301526155e96101408401615310565b61ffff166101408301526156006101608401615310565b61ffff166101608301526156176101808401615310565b61ffff1661018083015261562e6101a08401615347565b63ffffffff166101a08301526156476101c08401615347565b63ffffffff81166101c0840152614e39565b6040810181835f5b600281101561569057813561567581615336565b63ffffffff1683526020928301929190910190600101615661565b50505092915050565b5f600182016156aa576156aa614f4c565b5060010190565b5f5f8335601e198436030181126156c6575f5ffd5b8301803591506001600160401b038211156156df575f5ffd5b6020019150368190038213156142d9575f5ffd5b5f60208284031215615703575f5ffd5b81356147a58161510e565b87815286602082015260a060408201525f61572c60a083018861488f565b828103606084015261573f8187896150c4565b905082810360808401526157548185876150c4565b9a9950505050505050505050565b81516001600160401b0381111561577b5761577b614f8e565b61578f816157898454614ef9565b84614fa2565b6020601f8211600181146157bc575f83156157aa5750848201515b6157b48482614fed565b855550614fe6565b5f84815260208120601f198516915b828110156157eb57878501518255602094850194600190920191016157cb565b508482101561580857868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b838152602081018390526080810160408201835f5b600281101561585157815163ffffffff1683526020928301929091019060010161582c565b505050949350505050565b6040810161586a8285614e6a565b6147a5602083018461485d565b604051601f8201601f191681016001600160401b038111828210171561589f5761589f614f8e565b604052919050565b5f6001600160401b038211156158bf576158bf614f8e565b5060051b60200190565b5f82601f8301126158d8575f5ffd5b81516158eb6158e6826158a7565b615877565b8082825260208201915060208360051b86010192508583111561590c575f5ffd5b602085015b83811015615929578051835260209283019201615911565b5095945050505050565b5f5f60408385031215615944575f5ffd5b82516001600160401b03811115615959575f5ffd5b8301601f81018513615969575f5ffd5b80516159776158e6826158a7565b8082825260208201915060208360051b850101925087831115615998575f5ffd5b6020840193505b828410156159c35783516159b281614776565b82526020938401939091019061599f565b8095505050505060208301516001600160401b038111156159e2575f5ffd5b6159ee858286016158c9565b9150509250929050565b838152606060208201525f615a106060830185615196565b905060018060a01b0383166040830152949350505050565b5f8151808452602084019350602083015f5b828110156151cf578151865260209586019590910190600101615a3a565b6001600160a01b03841681526060602082018190525f90615a7b90830185615196565b82810360408401526145ee8185615a28565b604081525f615a9f6040830185615196565b82810360208401526151058185615a28565b6001600160a01b0392909216825260208201526040019056fe1b418a230a21d37a078bf8f16decbde8ccceacd77159371f62f0d4ea00d19967a164736f6c634300081c000a", - "deployedBytecode": "0x608060405234801561000f575f5ffd5b50600436106102ce575f3560e01c806390173a4111610182578063cb649617116100e0578063f0691cba1161008f578063f0691cba14610886578063f2fde38b14610899578063f3ceba3a146108ac578063f81b8ef6146108cd578063fad8e111146108e0578063fbdb3237146108f3578063fd2f3d011461091b575f5ffd5b8063cb649617146107ef578063cbd16872146107f8578063cf0f34c41461080b578063cfbdc98d1461081e578063d8afed3e1461084d578063e59e469514610860578063ea71aa5714610873575f5ffd5b80639e57b9341161013c5780639e57b93414610607578063a87f4ab91461061a578063ac3d2f421461076c578063bb2d1b8214610794578063bff232c1146107a7578063c1ab0f1f146107ba578063c4ccafa2146107cd575f5ffd5b806390173a41146105705780639117173c146105855780639231238614610598578063929a8faf146105ab57806399c6679d146105cc5780639c8570c8146105f4575f5ffd5b80635d1684181161022f5780637edcd7ab116101e95780637edcd7ab146104e757806381476ec21461050a578063830d71811461051d57806385814243146105305780638da5cb5b146105435780638dcdd86b1461054b5780638e5ce3ad1461055d575f5ffd5b80635d1684181461047d578063647846a51461049d5780636db5c8fd146104b0578063715018a6146104b95780637c8c3b4d146104c15780637cfa9d74146104d4575f5ffd5b806336c5d38a1161028b57806336c5d38a1461039b5780634017daf0146103ca578063406ed35c146103f75780634147a360146104175780634d600e5d146104445780634e92ec63146104575780634fc772641461046a575f5ffd5b806302a3a9c9146102d25780630ef81b2f146102e757806310bc62811461032557806311bd61d91461034d57806315cce224146103755780631ba7294514610388575b5f5ffd5b6102e56102e036600461478a565b61092e565b005b61030f6102f53660046147ac565b5f908152600960205260409020546001600160a01b031690565b60405161031c91906147d0565b60405180910390f35b61030f6103333660046147ac565b60096020525f90815260409020546001600160a01b031681565b61036061035b3660046147f7565b6109da565b60405163ffffffff909116815260200161031c565b6102e561038336600461478a565b610a16565b6102e561039636600461482f565b610abb565b6103bd6103a93660046147ac565b5f908152600f602052604090205460ff1690565b60405161031c9190614871565b6103dd6103d83660046147ac565b610acf565b60405161031c9e9d9c9b9a999897969594939291906148bd565b61040a6104053660046147ac565b610c7a565b60405161031c9190614a97565b6104366104253660046147ac565b600c6020525f908152604090205481565b60405190815260200161031c565b6102e5610452366004614ab4565b610ef7565b6102e56104653660046147ac565b611135565b6102e561047836600461478a565b6111c4565b61049061048b366004614b4a565b611257565b60405161031c9190614b63565b60045461030f906001600160a01b031681565b61043660055481565b6102e56112ee565b6102e56104cf366004614b75565b611301565b6102e56104e23660046147ac565b6113c1565b6104fa6104f5366004614be0565b6114b4565b604051901515815260200161031c565b6102e5610518366004614c57565b61170a565b6102e561052b366004614c77565b6117fe565b60015461030f906001600160a01b031681565b61030f61190a565b5f5461030f906001600160a01b031681565b60035461030f906001600160a01b031681565b610578611938565b60405161031c9190614cc5565b6102e56105933660046147ac565b61197e565b6105786105a63660046147ac565b611aec565b6105be6105b93660046147ac565b611b45565b60405161031c929190614ce6565b61030f6105da3660046147ac565b5f908152601060205260409020546001600160a01b031690565b6104fa610602366004614be0565b611b6c565b610436610615366004614cfc565b611e04565b61075f604080516101e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081019190915250604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a0840152640100000000909104166101c082015290565b60405161031c9190614d33565b61030f61077a3660046147ac565b5f908152600a60205260409020546001600160a01b031690565b6102e56107a2366004614e40565b6123ce565b6102e56107b536600461478a565b61248a565b6102e56107c8366004614c57565b612531565b6104fa6107db36600461478a565b60076020525f908152604090205460ff1681565b61043660065481565b6102e5610806366004614b75565b6125ee565b6102e56108193660046147ac565b6126a8565b61084061082c3660046147ac565b5f908152600d602052604090205460ff1690565b60405161031c9190614e7a565b6102e561085b366004614e88565b6126e5565b6102e561086e36600461478a565b612972565b6102e5610881366004614ea2565b612a0c565b60025461030f906001600160a01b031681565b6102e56108a736600461478a565b612cb9565b6108bf6108ba366004614cfc565b612cf3565b60405161031c929190614ed9565b6103bd6108db3660046147ac565b613747565b6102e56108ee36600461478a565b6138e1565b61030f6109013660046147ac565b600a6020525f90815260409020546001600160a01b031681565b6102e561092936600461478a565b613979565b610936613a08565b6001600160a01b0381166109915760405162461bcd60e51b815260206004820152601f60248201527f496e76616c6964204533526566756e644d616e6167657220616464726573730060448201526064015b60405180910390fd5b600280546001600160a01b0319166001600160a01b0383169081179091556040517f9557d04c1c0b16f93f13b69aed23b3b6ab935bff3c53ac81d17896d3583542ed905f90a250565b6012602052815f5260405f2081600281106109f3575f80fd5b60089182820401919006600402915091509054906101000a900463ffffffff1681565b610a1e613a08565b6001600160a01b03811615801590610a4457506004546001600160a01b03828116911614155b8190610a645760405163eddf07f560e01b815260040161098891906147d0565b50600480546001600160a01b0319166001600160a01b0383161790556040517f722ff84c1234b2482061def5c82c6b5080c117b3cbb69d686844a051e4b8e7f390610ab09083906147d0565b60405180910390a150565b610ac3613a08565b610acc81613a3a565b50565b60086020525f9081526040902080546001820154600283015460058401546006850154600786018054959660ff95861696949593946001600160a01b03841694600160a01b90940490931692909190610b2790614ef9565b80601f0160208091040260200160405190810160405280929190818152602001828054610b5390614ef9565b8015610b9e5780601f10610b7557610100808354040283529160200191610b9e565b820191905f5260205f20905b815481529060010190602001808311610b8157829003601f168201915b50505060088401546009850154600a860154600b870154600c8801805497986001600160a01b03958616989490951696509194509291610bdd90614ef9565b80601f0160208091040260200160405190810160405280929190818152602001828054610c0990614ef9565b8015610c545780601f10610c2b57610100808354040283529160200191610c54565b820191905f5260205f20905b815481529060010190602001808311610c3757829003601f168201915b505050600d90930154919250506001600160a01b0381169060ff600160a01b909104168e565b610c826145f8565b5f8281526008602090815260409182902082516101e08101909352805483526001810154909183019060ff166003811115610cbf57610cbf614849565b6003811115610cd057610cd0614849565b8152600282810154602083015260408051808201808352919093019291600385019182845b815481526020019060010190808311610cf55750505091835250506005820154602082015260068201546001600160a01b0381166040830152600160a01b900460ff166060820152600782018054608090920191610d5290614ef9565b80601f0160208091040260200160405190810160405280929190818152602001828054610d7e90614ef9565b8015610dc95780601f10610da057610100808354040283529160200191610dc9565b820191905f5260205f20905b815481529060010190602001808311610dac57829003601f168201915b505050918352505060088201546001600160a01b0390811660208301526009830154166040820152600a8201546060820152600b8201546080820152600c8201805460a090920191610e1a90614ef9565b80601f0160208091040260200160405190810160405280929190818152602001828054610e4690614ef9565b8015610e915780601f10610e6857610100808354040283529160200191610e91565b820191905f5260205f20905b815481529060010190602001808311610e7457829003601f168201915b5050509183525050600d91909101546001600160a01b038082166020840152600160a01b90910460ff16151560409092019190915260a0820151919250839116610ef15760405163cd6f4a4f60e01b815260040161098891815260200190565b50919050565b5f610f00613af7565b805490915060ff600160401b82041615906001600160401b03165f81158015610f265750825b90505f826001600160401b03166001148015610f415750303b155b905081158015610f4f575080155b15610f6d5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610f9757845460ff60401b1916600160401b1785555b610fa033613b21565b610fa9876126a8565b610fb28b6138e1565b610fbb8a612972565b610fc48961092e565b610fcd88610a16565b610fd686613a3a565b604080516101e081018252620186a080825261c3506020808401829052612710948401859052603260608501819052620493e060808601819052620f424060a0870181905261138860c088018190525f60e089018190526105dc6101008a015261012089018190526109c46101408a018190526101608a018390526101808a01526101a089018190526101c090980197909752601895909555601993909355601a95909555601b94909455601c55601d55601e55601f80546001600160f01b03191669027104e202710000017760a21b179055805467ffffffffffffffff191690556110c061190a565b6001600160a01b03168c6001600160a01b0316146110e1576110e18c612cb9565b831561112757845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050505050565b61113d613a08565b5f8181526009602052604090205481906001600160a01b0316611176576040516381c4951960e01b815260040161098891815260200190565b505f818152600960205260409081902080546001600160a01b0319169055517f104eb329a192aef26eddea07c2af5ad2587792e62b37ed4045b6ba59bc5540fc90610ab09083815260200190565b6111cc613a08565b6001600160a01b0381165f90815260076020526040902054819060ff16611207576040516321ac7c5f60e01b815260040161098891906147d0565b506001600160a01b0381165f9081526007602052604090819020805460ff19169055517f56070b80bd617fcd2f7a284861edb488830a38f9dedcd77b2cb2f4eac17743e790610ab09083906147d0565b600b6020525f90815260409020805461126f90614ef9565b80601f016020809104026020016040519081016040528092919081815260200182805461129b90614ef9565b80156112e65780601f106112bd576101008083540402835291602001916112e6565b820191905f5260205f20905b8154815290600101906020018083116112c957829003601f168201915b505050505081565b6112f6613a08565b6112ff5f613b32565b565b611309613a08565b6001600160a01b0381161580159061133a57505f828152600a60205260409020546001600160a01b03828116911614155b829061135c576040516381c4951960e01b815260040161098891815260200190565b505f828152600a60205260409081902080546001600160a01b0319166001600160a01b0384161790555182907f53661e3e12f23eea1e322a5352171ad3e4407d1394f869f53bb148c27e00908a906113b59084906147d0565b60405180910390a25050565b5f546001600160a01b031633146113eb5760405163b56831db60e01b815260040160405180910390fd5b5f818152600d602052604090205460ff16600181600681111561141057611410614849565b1461143557816001826040516337e1404160e01b815260040161098893929190614f2b565b5f828152600d60205260409020805460ff1916600217905560155461145a9042614f60565b5f838152600e602052604080822092909255905183917fc44405af9078047712501f519e1fb900c2896c62b488336f84529c72ae16e6f191a2815f516020615acb5f395f51905f52600160026040516113b5929190614f73565b5f5f6114bf87610c7a565b5f888152600d602052604090205490915060ff1660048160068111156114e7576114e7614849565b148860048390919261150f576040516337e1404160e01b815260040161098893929190614f2b565b5050505f888152600e60209081526040918290208251606081018452815481526001820154928101929092526002015491810182905290899042811015611572576040516308f3034360e31b815260048101929092526024820152604401610988565b50505f898152600860205260409020600c0161158f888a83615001565b505f898152600d60205260409020805460ff191660051790556101c08301511561168957846115d157604051631eae1a4d60e31b815260040160405180910390fd5b8261010001516001600160a01b031663258ae58289896040516115f59291906150b5565b6040519081900381206001600160e01b031960e084901b16825261161f918a908a906004016150ec565b602060405180830381865afa15801561163a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061165e919061511b565b935087878561168257604051632f9f8ab960e01b8152600401610988929190615136565b505061168e565b600193505b61169789613ba2565b887f3a140076c461ebc41d74833ae0ee8bbc8079a135a63392098cd381e84350b69b898989896040516116cd9493929190615149565b60405180910390a2885f516020615acb5f395f51905f52600460056040516116f6929190614f73565b60405180910390a250505095945050505050565b5f546001600160a01b031633146117345760405163b56831db60e01b815260040160405180910390fd5b5f828152600860209081526040808320600d9092529091205460ff16600281600681111561176457611764614849565b1461178957836002826040516337e1404160e01b815260040161098893929190614f2b565b5f848152600d6020526040808220805460ff19166003179055600a84018590555185917f11df18edb9bc9cd90a79068e0e208b630202148643d797d6150e7bacb733e63c91a2835f516020615acb5f395f51905f52600260036040516117f0929190614f73565b60405180910390a250505050565b611806613a08565b806118425760405162461bcd60e51b815260206004820152600c60248201526b456d70747920706172616d7360a01b6044820152606401610988565b60ff83165f908152600b60205260409020805461185e90614ef9565b1590506118ad5760405162461bcd60e51b815260206004820152601b60248201527f506172616d53657420616c7265616479207265676973746572656400000000006044820152606401610988565b60ff83165f908152600b602052604090206118c9828483615001565b507f6e4a4ea7f38fc775e616080b155744337e6216848e886a69c918b4ab84da21958383836040516118fd9392919061517a565b60405180910390a1505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b61195960405180606001604052805f81526020015f81526020015f81525090565b5060408051606081018252601554815260165460208201526017549181019190915290565b5f818152600d602052604090205460ff1660068160068111156119a3576119a3614849565b1482906119c657604051637cb2d48360e11b815260040161098891815260200190565b505f828152600c602052604090205482816119f7576040516345ba89d560e11b815260040161098891815260200190565b505f838152600c60205260408120819055611a118461400e565b5f858152601160205260409020546002549192506001600160a01b0390811691611a3e91839116856140fb565b60025460405163da19b69760e01b81526001600160a01b039091169063da19b69790611a749088908790879087906004016151d9565b5f604051808303815f87803b158015611a8b575f5ffd5b505af1158015611a9d573d5f5f3e3d5ffd5b50505050847f5297818f48a66292b8b3e2caab83eec531b669bb20807fd38cf006adb2a07317848451604051611add929190918252602082015260400190565b60405180910390a25050505050565b611b0d60405180606001604052805f81526020015f81526020015f81525090565b505f908152600e6020908152604091829020825160608101845281548152600182015492810192909252600201549181019190915290565b5f818152600d6020526040812054819060ff16611b628482614158565b9250925050915091565b5f5f611b7787610c7a565b5f888152600d602052604090205490915060ff166003816006811115611b9f57611b9f614849565b1488600383909192611bc7576040516337e1404160e01b815260040161098893929190614f2b565b5050505f888152600e6020908152604091829020825160608101845281548152600182015492810183905260029091015492810192909252899042811015611c2b576040516308f3034360e31b815260048101929092526024820152604401610988565b5050606083015160200151899042811115611c625760405163017e35e560e71b815260048101929092526024820152604401610988565b5050610160830151899015611c8d57604051637eb9cea960e11b815260040161098891815260200190565b505f8888604051611c9f9291906150b5565b60408051918290039091205f8c815260086020908152838220600b01839055600d905291909120805460ff19166004179055601754909150611ce19042614f60565b5f8b8152600e6020526040908190206002019190915560a08501519051632f0e1bbf60e01b81526001600160a01b0390911690632f0e1bbf90611d2e908d9085908c908c90600401615224565b6020604051808303815f875af1158015611d4a573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d6e919061511b565b9450888886611d9257604051632f9f8ab960e01b8152600401610988929190615136565b5050897f7cc27e4a5626cbc4f8ba1a927b0448de55e6a114bc87660331270c5109ade0718a8a604051611dc6929190615136565b60405180910390a2895f516020615acb5f395f51905f5260036004604051611def929190614f73565b60405180910390a25050505095945050505050565b5f80600b81611e1960a0860160808701614b4a565b60ff1660ff1681526020019081526020015f208054611e3790614ef9565b905011611e565760405162461bcd60e51b815260040161098890615243565b5f601281611e67602086018661527a565b6003811115611e7857611e78614849565b6003811115611e8957611e89614849565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411611eb0579050505050505090505f81600160028110611f0757611f07615210565b602002015163ffffffff1611835f016020810190611f25919061527a565b90611f445760405163286c068d60e11b81526004016109889190615293565b506020808201518251604080516101e081018252601854815260195481860152601a5491810191909152601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e0830152600160a01b810461ffff908116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152925463ffffffff8181166101a0860181905264010000000090920481166101c08601529283169391909216911561206f576101a081015163ffffffff16846001602002015163ffffffff161015865f01602081019061204e919061527a565b9061206d5760405163010b971d60e31b81526004016109889190615293565b505b6101c081015163ffffffff16156120be576101c081015184519063ffffffff90811690821610156120bc57604051630a4b6b6360e11b815263ffffffff9091166004820152602401610988565b505b604086013560208701358110156120eb5760405163174b5a0760e21b815260040161098891815260200190565b506101808101516017545f91612710916121099161ffff16906152a1565b61211391906152b8565b61271061ffff1683610160015161ffff1660156001015461213491906152a1565b61213e91906152b8565b61271061ffff1684610140015161ffff1660155f015461215e91906152a1565b61216891906152b8565b5f5460408051634f87c3a560e11b8152815160208e81013594938f0135936001600160a01b031692639f0f874a92600480830193928290030181865afa1580156121b4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121d891906152d7565b6121e29190614f60565b6121ec91906152ee565b6121f69190614f60565b6122009190614f60565b61220a9190614f60565b90505f6122186001866152ee565b6122239060046152a1565b61222e90600e614f60565b90505f85845f015161224091906152a1565b90508186856020015161225391906152a1565b61225d91906152a1565b6122679082614f60565b905060018611156122af57600261227f6001886152ee565b61228990886152a1565b856040015161229891906152a1565b6122a291906152b8565b6122ac9082614f60565b90505b81868560c001516122c091906152a1565b6122ca91906152a1565b6122d49082614f60565b9050828685606001516122e791906152a1565b6122f191906152a1565b6122fb9082614f60565b905084846080015161230d91906152a1565b6123179082614f60565b9050600185111561235f57600261232f6001876152ee565b61233990876152a1565b856040015161234891906152a1565b61235291906152b8565b61235c9082614f60565b90505b60a084015161236e9082614f60565b610100850151909150612710906123899061ffff1682614f60565b61239390836152a1565b61239d91906152b8565b975087806123c157604051638c4fcd9360e01b815260040161098891815260200190565b5050505050505050919050565b5f546001600160a01b03163314806123f057506003546001600160a01b031633145b61240d57604051639e75a8b560e01b815260040160405180910390fd5b5f8160ff161180156124235750600d60ff821611155b6124685760405162461bcd60e51b815260206004820152601660248201527524b73b30b634b2103330b4b63ab932903932b0b9b7b760511b6044820152606401610988565b612486828260ff16600d81111561248157612481614849565b6142e0565b5050565b612492613a08565b6001600160a01b0381166124e85760405162461bcd60e51b815260206004820152601f60248201527f496e76616c696420536c617368696e674d616e616765722061646472657373006044820152606401610988565b600380546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b6003546001600160a01b0316331461255c576040516357d6948d60e11b815260040160405180910390fd5b60025460405163c1ab0f1f60e01b815260048101849052602481018390526001600160a01b039091169063c1ab0f1f906044015f604051808303815f87803b1580156125a6575f5ffd5b505af11580156125b8573d5f5f3e3d5ffd5b50505050817f4f41a3b0a032ebcae925f2ace77d507435840ca4b2dbaffdd7723fa8d72ee542826040516113b591815260200190565b6125f6613a08565b6001600160a01b0381161580159061262757505f828152600960205260409020546001600160a01b03828116911614155b8290612649576040516381c4951960e01b815260040161098891815260200190565b505f8281526009602090815260409182902080546001600160a01b0319166001600160a01b03851617905590518381527ff4041a3f914dac3bc9bf5f003ba41f28dbb84abe42f4e07c76266f5c8ceecb69910160405180910390a15050565b6126b0613a08565b60058190556040518181527fba0716ba1ee2ea8ecc4c64119b4537cdb42a99d82acf92af5b87607b8b52355290602001610ab0565b6126ed613a08565b6127106127026101208301610100840161531b565b61ffff16111561271a6101208301610100840161531b565b9061273f576040516301027fc160e21b815261ffff9091166004820152602401610988565b506127106127556101408301610120840161531b565b61ffff16111561276d6101408301610120840161531b565b90612792576040516301027fc160e21b815261ffff9091166004820152602401610988565b506127106127a86101608301610140840161531b565b61ffff1611156127c06101608301610140840161531b565b906127e557604051633239953960e01b815261ffff9091166004820152602401610988565b506127106127fb6101808301610160840161531b565b61ffff1611156128136101808301610160840161531b565b9061283857604051633239953960e01b815261ffff9091166004820152602401610988565b5061271061284e6101a08301610180840161531b565b61ffff1611156128666101a08301610180840161531b565b9061288b57604051633239953960e01b815261ffff9091166004820152602401610988565b5061289e6101408201610120830161531b565b61ffff1615806128c757505f6128bb610100830160e0840161478a565b6001600160a01b031614155b6128e45760405163015f92ff60e51b815260040160405180910390fd5b6128f66101e082016101c08301615352565b63ffffffff1661290e6101c083016101a08401615352565b63ffffffff161015612933576040516392f55c6560e01b815260040160405180910390fd5b8060186129408282615391565b9050507fbf3951313e980027eb48ce363fdb707286195ec6a0f802ac153927cf929c3fc681604051610ab0919061554f565b61297a613a08565b6001600160a01b038116158015906129a057506001546001600160a01b03828116911614155b81906129c0576040516320252f0b60e01b815260040161098891906147d0565b50600180546001600160a01b0319166001600160a01b0383161790556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790610ab09083906147d0565b612a14613a08565b612a216020820182615352565b63ffffffff16612a376040830160208401615352565b63ffffffff1610158015612a5c57505f612a546020830183615352565b63ffffffff16115b612a7957604051634564ab9b60e01b815260040160405180910390fd5b604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a08401819052640100000000909204166101c083015215612bb2576101a081015163ffffffff16612b626040840160208501615352565b63ffffffff161015612b7a6040840160208501615352565b826101a001519091612baf57604051633ccc4c2160e21b815263ffffffff928316600482015291166024820152604401610988565b50505b6101c081015163ffffffff1615612c29576101c081015163ffffffff16612bdc6020840184615352565b63ffffffff161015612bf16020840184615352565b826101c001519091612c265760405163156c4e5b60e11b815263ffffffff928316600482015291166024820152604401610988565b50505b8160125f856003811115612c3f57612c3f614849565b6003811115612c5057612c50614849565b815260208101919091526040015f20612c6a916002614675565b50826003811115612c7d57612c7d614849565b7f8b56fae526eee054f0849759a99fc7d4ff3823824ebf097a56f7d78adb6b34fa83604051612cac9190615659565b60405180910390a2505050565b612cc1613a08565b6001600160a01b038116612cea575f604051631e4fbdf760e01b815260040161098891906147d0565b610acc81613b32565b5f612cfc6145f8565b5f601281612d0d602087018761527a565b6003811115612d1e57612d1e614849565b6003811115612d2f57612d2f614849565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411612d56579050505050505090505f81600160028110612dad57612dad615210565b602002015163ffffffff1611845f016020810190612dcb919061527a565b90612dea5760405163286c068d60e11b81526004016109889190615293565b50602084013542811015612e1457604051630b99e87960e01b815260040161098891815260200190565b5060408401356020850135811015612e425760405163174b5a0760e21b815260040161098891815260200190565b506017546016545f9190612e5a4260408901356152ee565b612e649190614f60565b612e6e9190614f60565b905060055481108190612e97576040516313b783af60e21b815260040161098891815260200190565b5060075f612eab608088016060890161478a565b6001600160a01b0316815260208101919091526040015f205460ff16612ed7608087016060880161478a565b90612ef65760405163295a6a6f60e11b815260040161098891906147d0565b505f612f0186611e04565b60068054965090915085905f612f1683615699565b9091555050604080514460208201529081018690525f9060600160408051601f1981840301815291815281516020928301205f898152600c84528281208690556004546011855283822080546001600160a01b03199081166001600160a01b0393841617909155601f805460138852868520805461ffff191661ffff600160b01b909304929092169190911790555460148752858420805483169190931617909155600d8552838220805460ff191660011790556010909452829020805433941693909317909255601654919250612ff19190890135614f60565b5f878152600e60209081526040909120600101919091558186526130179088018861527a565b8560200190600381111561302d5761302d614849565b9081600381111561304057613040614849565b905250436040808701919091528051808201825290602089019060029083908390808284375f920191909152505050606080870191909152613088906080890190890161478a565b6001600160a01b031660a0808701919091526130a990880160808901614b4a565b60ff1660c0808701919091526130c1908801886156b1565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050505060e08087019190915261310e9061010089019089016156f3565b15156101c08601525f610140860181905261016086018190526040805160208101909152818152610180870152336101a0870152600b8161315560a08b0160808c01614b4a565b60ff1660ff1681526020019081526020015f20805461317390614ef9565b80601f016020809104026020016040519081016040528092919081815260200182805461319f90614ef9565b80156131ea5780601f106131c1576101008083540402835291602001916131ea565b820191905f5260205f20905b8154815290600101906020018083116131cd57829003601f168201915b505050505090505f8151116132115760405162461bcd60e51b815260040161098890615243565b5f61322260808a0160608b0161478a565b6001600160a01b031663fefd9a8b89858561324060a08f018f6156b1565b8f8060c0019061325091906156b1565b6040518863ffffffff1660e01b8152600401613272979695949392919061570e565b6020604051808303815f875af115801561328e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132b291906152d7565b5f818152600960205260409020549091506001600160a01b031681816132ee576040516381c4951960e01b815260040161098891815260200190565b505f828152600a60205260409020546001600160a01b03168281613328576040516381c4951960e01b815260040161098891815260200190565b50608089018390526001600160a01b038083166101008b015281166101208a01525f8a81526008602090815260409091208a518155908a0151600180830180548d94939260ff19919091169083600381111561338657613386614849565b0217905550604082015181600201556060820151816003019060026133ac929190614716565b506080820151600582015560a082015160068201805460c085015160ff16600160a01b026001600160a81b03199091166001600160a01b039093169290921791909117905560e082015160078201906134059082615762565b506101008201516008820180546001600160a01b039283166001600160a01b031991821617909155610120840151600984018054919093169116179055610140820151600a820155610160820151600b820155610180820151600c82019061346d9082615762565b506101a0820151600d90910180546101c0909301511515600160a01b026001600160a81b03199093166001600160a01b0392831617929092179091556004546134b9911633308961443b565b608089018390526001600160a01b038083166101008b015281166101208a01525f8a81526008602090815260409091208a518155908a0151600180830180548d94939260ff19919091169083600381111561351657613516614849565b02179055506040820151816002015560608201518160030190600261353c929190614716565b506080820151600582015560a082015160068201805460c085015160ff16600160a01b026001600160a81b03199091166001600160a01b039093169290921791909117905560e082015160078201906135959082615762565b506101008201516008820180546001600160a01b039283166001600160a01b031991821617909155610120840151600984018054919093169116179055610140820151600a820155610160820151600b820155610180820151600c8201906135fd9082615762565b506101a0820151600d90910180546101c0909301511515600160a01b026001600160a81b03199093166001600160a01b0392831617929092179091555f5460405163291a691b60e01b815291169063291a691b90613663908d9089908d90600401615817565b6020604051808303815f875af115801561367f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906136a3919061511b565b6136c057604051630d8dbe2560e01b815260040160405180910390fd5b6136d060808c0160608d0161478a565b6001600160a01b03167f5090c9764b5cd13df7afc0013f733dfbe6eaf1b6ddc22a5e291fa387efd4c15e8b8b60405161370a929190614ed9565b60405180910390a2895f516020615acb5f395f51905f525f6001604051613732929190614f73565b60405180910390a25050505050505050915091565b5f818152600d602052604081205460ff168181600681111561376b5761376b614849565b0361379057826001826040516337e1404160e01b815260040161098893929190614f2b565b60058160068111156137a4576137a4614849565b036137c55760405163462c7bed60e01b815260048101849052602401610988565b60068160068111156137d9576137d9614849565b036137fa57604051633de16e3560e11b815260048101849052602401610988565b5f6138058483614158565b935090508061382a57604051639f65d93560e01b815260048101859052602401610988565b5f848152600d6020526040902080546006919060ff191660018302179055505f848152600f60205260409020805484919060ff1916600183600d81111561387357613873614849565b0217905550835f516020615acb5f395f51905f52836006604051613898929190614f73565b60405180910390a2837fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb83856040516138d292919061585c565b60405180910390a25050919050565b6138e9613a08565b6001600160a01b0381161580159061390e57505f546001600160a01b03828116911614155b819061392e576040516375ac4eb760e11b815260040161098891906147d0565b505f80546001600160a01b0319166001600160a01b0383161790556040517f80052b810d39120cf6c976cca504a21703f585521dc7a41c6d241090e6c579b690610ab09083906147d0565b6001600160a01b0381165f90815260076020526040902054819060ff16156139b55760405163b29d459560e01b815260040161098891906147d0565b506001600160a01b0381165f9081526007602052604090819020805460ff19166001179055517fb8d368517268f297fff00825d67d098763117d061360d31027be5b2e1a59d46790610ab09083906147d0565b33613a1161190a565b6001600160a01b0316146112ff573360405163118cdaa760e01b815260040161098891906147d0565b8035613a595760405163055f269d60e01b815260040160405180910390fd5b5f816020013511613a7d5760405163055f269d60e01b815260040160405180910390fd5b5f816040013511613aa15760405163055f269d60e01b815260040160405180910390fd5b80356015819055602080830135601681905560408085013560178190558151948552928401919091528201527f7e86ba16b805e2835af5c5b7aa5a942ced8bcc1fb95a05fbe42dae3862350a1690606001610ab0565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005b92915050565b613b2961447a565b610acc8161449f565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b5f805460405162beb08960e51b8152600481018490526001600160a01b03909116906317d61120906024015f60405180830381865afa158015613be7573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613c0e9190810190615933565b5080515f848152600c60209081526040808320805490849055601190925282205493945091926001600160a01b031690829003613cad576002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613c79908890889086906004016159f8565b5f604051808303815f87803b158015613c90575f5ffd5b505af1158015613ca2573d5f5f3e3d5ffd5b505050505050505050565b825f03613d4e575f858152601060205260409020546001600160a01b03168015613ce557613ce56001600160a01b03831682856140fb565b6002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613d19908990899087906004016159f8565b5f604051808303815f87803b158015613d30575f5ffd5b505af1158015613d42573d5f5f3e3d5ffd5b50505050505050505050565b5f85815260136020908152604080832054601490925282205461ffff909116906001600160a01b03168115801590613d8e57506001600160a01b03811615155b15613dca57612710613da461ffff8416876152a1565b613dae91906152b8565b92508215613dca57613dca6001600160a01b03851682856140fb565b5f613dd584876152ee565b90505f876001600160401b03811115613df057613df0614f8e565b604051908082528060200260200182016040528015613e19578160200160208202803683370190505b5090505f613e2789846152b8565b90505f805b8a811015613e665782848281518110613e4757613e47615210565b6020908102919091010152613e5c8383614f60565b9150600101613e2c565b505f613e7282866152ee565b90508015613eaf578084613e8760018e6152ee565b81518110613e9757613e97615210565b60200260200101818151613eab9190614f60565b9052505b600154613ec9906001600160a01b038b81169116876144a7565b60015f9054906101000a90046001600160a01b03166001600160a01b031663dd8c818e8a8e876040518463ffffffff1660e01b8152600401613f0d93929190615a58565b5f604051808303815f87803b158015613f24575f5ffd5b505af1158015613f36573d5f5f3e3d5ffd5b5050600154613f5492506001600160a01b038c81169250165f6144a7565b8c7fac9fe8ad7f55eac03284399116ecafc104f10459773f4cdf47063c46e5be335a8d86604051613f86929190615a8d565b60405180910390a260025f9054906101000a90046001600160a01b03166001600160a01b03166341489f158e8e8c6040518463ffffffff1660e01b8152600401613fd2939291906159f8565b5f604051808303815f87803b158015613fe9575f5ffd5b505af1158015613ffb573d5f5f3e3d5ffd5b5050505050505050505050505050505050565b5f818152600f602052604090205460609060ff16600181600d81111561403657614036614849565b14806140535750600281600d81111561405157614051614849565b145b1561408b575f5b604051908082528060200260200182016040528015614083578160200160208202803683370190505b509392505050565b5f5460405162beb08960e51b8152600481018590526001600160a01b03909116906317d61120906024015f60405180830381865afa9250505080156140f157506040513d5f823e601f3d908101601f191682016040526140ee9190810190615933565b60015b614083575f61405a565b61415383846001600160a01b031663a9059cbb8585604051602401614121929190615ab1565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050614533565b505050565b5f828152600e60209081526040808320815160608101835281548152600182015493810193909352600201548282015282549051632800d82960e01b81526004810186905283929183916001600160a01b0390911690632800d82990602401602060405180830381865afa1580156141d2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906141f691906152d7565b9050600185600681111561420c5761420c614849565b14801561421857508042115b1561422b576001809350935050506142d9565b600285600681111561423f5761423f614849565b14801561424c5750815142115b1561426057600160039350935050506142d9565b600385600681111561427457614274614849565b1480156142845750816020015142115b1561429857600160069350935050506142d9565b60048560068111156142ac576142ac614849565b1480156142bc5750816040015142115b156142d0576001600a9350935050506142d9565b5f5f9350935050505b9250929050565b5f828152600d602052604081205460ff169081600681111561430457614304614849565b0361432957826001826040516337e1404160e01b815260040161098893929190614f2b565b600581600681111561433d5761433d614849565b0361435e5760405163462c7bed60e01b815260048101849052602401610988565b600681600681111561437257614372614849565b0361439357604051633de16e3560e11b815260048101849052602401610988565b5f838152600d6020526040902080546006919060ff191660018302179055505f838152600f60205260409020805483919060ff1916600183600d8111156143dc576143dc614849565b0217905550825f516020615acb5f395f51905f52826006604051614401929190614f73565b60405180910390a2827fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb8284604051612cac92919061585c565b6040516001600160a01b0384811660248301528381166044830152606482018390526144749186918216906323b872dd90608401614121565b50505050565b614482614596565b6112ff57604051631afcd79f60e31b815260040160405180910390fd5b612cc161447a565b5f836001600160a01b031663095ea7b384846040516024016144ca929190615ab1565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050905061450384826145af565b6144745761452d84856001600160a01b031663095ea7b3865f604051602401614121929190615ab1565b61447484825b5f5f60205f8451602086015f885af180614552576040513d5f823e3d81fd5b50505f513d91508115614569578060011415614576565b6001600160a01b0384163b155b156144745783604051635274afe760e01b815260040161098891906147d0565b5f61459f613af7565b54600160401b900460ff16919050565b5f5f5f5f60205f8651602088015f8a5af192503d91505f5190508280156145ee575081156145e057806001146145ee565b5f866001600160a01b03163b115b9695505050505050565b604080516101e081019091525f808252602082019081526020015f8152602001614620614744565b81525f602082018190526040820181905260608083018290526080830181905260a0830182905260c0830182905260e08301829052610100830182905261012083015261014082018190526101609091015290565b600183019183908215614706579160200282015f5b838211156146d457833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff160217905550926020019260040160208160030104928301926001030261468a565b80156147045782816101000a81549063ffffffff02191690556004016020816003010492830192600103026146d4565b505b50614712929150614762565b5090565b8260028101928215614706579160200282015b82811115614706578251825591602001919060010190614729565b60405180604001604052806002906020820280368337509192915050565b5b80821115614712575f8155600101614763565b6001600160a01b0381168114610acc575f5ffd5b5f6020828403121561479a575f5ffd5b81356147a581614776565b9392505050565b5f602082840312156147bc575f5ffd5b5035919050565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b8035600481106147f2575f5ffd5b919050565b5f5f60408385031215614808575f5ffd5b614811836147e4565b946020939093013593505050565b5f60608284031215610ef1575f5ffd5b5f6060828403121561483f575f5ffd5b6147a5838361481f565b634e487b7160e01b5f52602160045260245ffd5b600e811061486d5761486d614849565b9052565b60208101613b1b828461485d565b6004811061486d5761486d614849565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b8e81526148cd602082018f61487f565b8c60408201528b60608201526148e6608082018c6147c3565b60ff8a1660a08201526101c060c08201525f6149066101c083018b61488f565b61491360e084018b6147c3565b61492161010084018a6147c3565b8761012084015286610140840152828103610160840152614942818761488f565b9150506149536101808301856147c3565b8215156101a08301529f9e505050505050505050505050505050565b805f5b6002811015614474578151845260209384019390910190600101614972565b805182525f60208201516149a8602085018261487f565b506040820151604084015260608201516149c5606085018261496f565b50608082015160a084015260a08201516149e260c08501826147c3565b5060c082015160ff811660e08501525060e0820151610200610100850152614a0e61020085018261488f565b9050610100830151614a246101208601826147c3565b50610120830151614a396101408601826147c3565b506101408301516101608501526101608301516101808501526101808301518482036101a0860152614a6b828261488f565b9150506101a0830151614a826101c08601826147c3565b506101c08301518015156101e0860152614083565b602081525f6147a56020830184614991565b80356147f281614776565b5f5f5f5f5f5f5f610120888a031215614acb575f5ffd5b8735614ad681614776565b96506020880135614ae681614776565b95506040880135614af681614776565b94506060880135614b0681614776565b93506080880135614b1681614776565b925060a08801359150614b2c8960c08a0161481f565b905092959891949750929550565b803560ff811681146147f2575f5ffd5b5f60208284031215614b5a575f5ffd5b6147a582614b3a565b602081525f6147a5602083018461488f565b5f5f60408385031215614b86575f5ffd5b823591506020830135614b9881614776565b809150509250929050565b5f5f83601f840112614bb3575f5ffd5b5081356001600160401b03811115614bc9575f5ffd5b6020830191508360208285010111156142d9575f5ffd5b5f5f5f5f5f60608688031215614bf4575f5ffd5b8535945060208601356001600160401b03811115614c10575f5ffd5b614c1c88828901614ba3565b90955093505060408601356001600160401b03811115614c3a575f5ffd5b614c4688828901614ba3565b969995985093965092949392505050565b5f5f60408385031215614c68575f5ffd5b50508035926020909101359150565b5f5f5f60408486031215614c89575f5ffd5b614c9284614b3a565b925060208401356001600160401b03811115614cac575f5ffd5b614cb886828701614ba3565b9497909650939450505050565b81518152602080830151908201526040808301519082015260608101613b1b565b8215158152604081016147a5602083018461485d565b5f60208284031215614d0c575f5ffd5b81356001600160401b03811115614d21575f5ffd5b820161010081850312156147a5575f5ffd5b5f6101e082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e0830151614d8d60e08401826147c3565b50610100830151614da561010084018261ffff169052565b50610120830151614dbd61012084018261ffff169052565b50610140830151614dd561014084018261ffff169052565b50610160830151614ded61016084018261ffff169052565b50610180830151614e0561018084018261ffff169052565b506101a0830151614e1f6101a084018263ffffffff169052565b506101c0830151614e396101c084018263ffffffff169052565b5092915050565b5f5f60408385031215614e51575f5ffd5b82359150614e6160208401614b3a565b90509250929050565b6007811061486d5761486d614849565b60208101613b1b8284614e6a565b5f6101e0828403128015614e9a575f5ffd5b509092915050565b5f5f60608385031215614eb3575f5ffd5b614ebc836147e4565b915083606084011115614ecd575f5ffd5b50926020919091019150565b828152604060208201525f614ef16040830184614991565b949350505050565b600181811c90821680614f0d57607f821691505b602082108103610ef157634e487b7160e01b5f52602260045260245ffd5b83815260608101614f3f6020830185614e6a565b614ef16040830184614e6a565b634e487b7160e01b5f52601160045260245ffd5b80820180821115613b1b57613b1b614f4c565b60408101614f818285614e6a565b6147a56020830184614e6a565b634e487b7160e01b5f52604160045260245ffd5b601f82111561415357805f5260205f20601f840160051c81016020851015614fc75750805b601f840160051c820191505b81811015614fe6575f8155600101614fd3565b5050505050565b5f19600383901b1c191660019190911b1790565b6001600160401b0383111561501857615018614f8e565b61502c836150268354614ef9565b83614fa2565b5f601f841160018114615058575f85156150465750838201355b6150508682614fed565b845550614fe6565b5f83815260208120601f198716915b828110156150875786850135825560209485019460019092019101615067565b50868210156150a3575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b818382375f9101908152919050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b838152604060208201525f6151056040830184866150c4565b95945050505050565b8015158114610acc575f5ffd5b5f6020828403121561512b575f5ffd5b81516147a58161510e565b602081525f614ef16020830184866150c4565b604081525f61515c6040830186886150c4565b828103602084015261516f8185876150c4565b979650505050505050565b60ff84168152604060208201525f6151056040830184866150c4565b5f8151808452602084019350602083015f5b828110156151cf5781516001600160a01b03168652602095860195909101906001016151a8565b5093949350505050565b848152836020820152608060408201525f6151f76080830185615196565b905060018060a01b038316606083015295945050505050565b634e487b7160e01b5f52603260045260245ffd5b848152836020820152606060408201525f6145ee6060830184866150c4565b6020808252601c908201527f42465620706172616d20736574206e6f74207265676973746572656400000000604082015260600190565b5f6020828403121561528a575f5ffd5b6147a5826147e4565b60208101613b1b828461487f565b8082028115828204841417613b1b57613b1b614f4c565b5f826152d257634e487b7160e01b5f52601260045260245ffd5b500490565b5f602082840312156152e7575f5ffd5b5051919050565b81810381811115613b1b57613b1b614f4c565b61ffff81168114610acc575f5ffd5b80356147f281615301565b5f6020828403121561532b575f5ffd5b81356147a581615301565b63ffffffff81168114610acc575f5ffd5b80356147f281615336565b5f60208284031215615362575f5ffd5b81356147a581615336565b5f8135613b1b81614776565b5f8135613b1b81615301565b5f8135613b1b81615336565b813581556020820135600182015560408201356002820155606082013560038201556080820135600482015560a0820135600582015560c08201356006820155600781016154016153e460e0850161536d565b82546001600160a01b0319166001600160a01b0391909116178255565b6154316154116101008501615379565b82805461ffff60a01b191660a09290921b61ffff60a01b16919091179055565b6154616154416101208501615379565b82805461ffff60b01b191660b09290921b61ffff60b01b16919091179055565b6154916154716101408501615379565b82805461ffff60c01b191660c09290921b61ffff60c01b16919091179055565b6154c16154a16101608501615379565b82805461ffff60d01b191660d09290921b61ffff60d01b16919091179055565b6154f16154d16101808501615379565b82805461ffff60e01b191660e09290921b61ffff60e01b16919091179055565b506008810161551d6155066101a08501615385565b825463ffffffff191663ffffffff91909116178255565b61415361552d6101c08501615385565b825467ffffffff00000000191660209190911b67ffffffff0000000016178255565b813581526020808301359082015260408083013590820152606080830135908201526080808301359082015260a0808301359082015260c080830135908201526101e081016155a060e08401614aa9565b6155ad60e08401826147c3565b506155bb6101008401615310565b61ffff166101008301526155d26101208401615310565b61ffff166101208301526155e96101408401615310565b61ffff166101408301526156006101608401615310565b61ffff166101608301526156176101808401615310565b61ffff1661018083015261562e6101a08401615347565b63ffffffff166101a08301526156476101c08401615347565b63ffffffff81166101c0840152614e39565b6040810181835f5b600281101561569057813561567581615336565b63ffffffff1683526020928301929190910190600101615661565b50505092915050565b5f600182016156aa576156aa614f4c565b5060010190565b5f5f8335601e198436030181126156c6575f5ffd5b8301803591506001600160401b038211156156df575f5ffd5b6020019150368190038213156142d9575f5ffd5b5f60208284031215615703575f5ffd5b81356147a58161510e565b87815286602082015260a060408201525f61572c60a083018861488f565b828103606084015261573f8187896150c4565b905082810360808401526157548185876150c4565b9a9950505050505050505050565b81516001600160401b0381111561577b5761577b614f8e565b61578f816157898454614ef9565b84614fa2565b6020601f8211600181146157bc575f83156157aa5750848201515b6157b48482614fed565b855550614fe6565b5f84815260208120601f198516915b828110156157eb57878501518255602094850194600190920191016157cb565b508482101561580857868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b838152602081018390526080810160408201835f5b600281101561585157815163ffffffff1683526020928301929091019060010161582c565b505050949350505050565b6040810161586a8285614e6a565b6147a5602083018461485d565b604051601f8201601f191681016001600160401b038111828210171561589f5761589f614f8e565b604052919050565b5f6001600160401b038211156158bf576158bf614f8e565b5060051b60200190565b5f82601f8301126158d8575f5ffd5b81516158eb6158e6826158a7565b615877565b8082825260208201915060208360051b86010192508583111561590c575f5ffd5b602085015b83811015615929578051835260209283019201615911565b5095945050505050565b5f5f60408385031215615944575f5ffd5b82516001600160401b03811115615959575f5ffd5b8301601f81018513615969575f5ffd5b80516159776158e6826158a7565b8082825260208201915060208360051b850101925087831115615998575f5ffd5b6020840193505b828410156159c35783516159b281614776565b82526020938401939091019061599f565b8095505050505060208301516001600160401b038111156159e2575f5ffd5b6159ee858286016158c9565b9150509250929050565b838152606060208201525f615a106060830185615196565b905060018060a01b0383166040830152949350505050565b5f8151808452602084019350602083015f5b828110156151cf578151865260209586019590910190600101615a3a565b6001600160a01b03841681526060602082018190525f90615a7b90830185615196565b82810360408401526145ee8185615a28565b604081525f615a9f6040830185615196565b82810360208401526151058185615a28565b6001600160a01b0392909216825260208201526040019056fe1b418a230a21d37a078bf8f16decbde8ccceacd77159371f62f0d4ea00d19967a164736f6c634300081c000a", + "bytecode": "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b6159d7806100d65f395ff3fe608060405234801561000f575f5ffd5b50600436106102ce575f3560e01c806390173a4111610182578063cb649617116100e0578063f0691cba1161008f578063f0691cba14610886578063f2fde38b14610899578063f3ceba3a146108ac578063f81b8ef6146108cd578063fad8e111146108e0578063fbdb3237146108f3578063fd2f3d011461091b575f5ffd5b8063cb649617146107ef578063cbd16872146107f8578063cf0f34c41461080b578063cfbdc98d1461081e578063d8afed3e1461084d578063e59e469514610860578063ea71aa5714610873575f5ffd5b80639e57b9341161013c5780639e57b93414610607578063a87f4ab91461061a578063ac3d2f421461076c578063bb2d1b8214610794578063bff232c1146107a7578063c1ab0f1f146107ba578063c4ccafa2146107cd575f5ffd5b806390173a41146105705780639117173c146105855780639231238614610598578063929a8faf146105ab57806399c6679d146105cc5780639c8570c8146105f4575f5ffd5b80635d1684181161022f5780637edcd7ab116101e95780637edcd7ab146104e757806381476ec21461050a578063830d71811461051d57806385814243146105305780638da5cb5b146105435780638dcdd86b1461054b5780638e5ce3ad1461055d575f5ffd5b80635d1684181461047d578063647846a51461049d5780636db5c8fd146104b0578063715018a6146104b95780637c8c3b4d146104c15780637cfa9d74146104d4575f5ffd5b806336c5d38a1161028b57806336c5d38a1461039b5780634017daf0146103ca578063406ed35c146103f75780634147a360146104175780634d600e5d146104445780634e92ec63146104575780634fc772641461046a575f5ffd5b806302a3a9c9146102d25780630ef81b2f146102e757806310bc62811461032557806311bd61d91461034d57806315cce224146103755780631ba7294514610388575b5f5ffd5b6102e56102e0366004614683565b61092e565b005b61030f6102f53660046146a5565b5f908152600960205260409020546001600160a01b031690565b60405161031c91906146c9565b60405180910390f35b61030f6103333660046146a5565b60096020525f90815260409020546001600160a01b031681565b61036061035b3660046146f0565b6109da565b60405163ffffffff909116815260200161031c565b6102e5610383366004614683565b610a16565b6102e5610396366004614728565b610abb565b6103bd6103a93660046146a5565b5f908152600f602052604090205460ff1690565b60405161031c919061476a565b6103dd6103d83660046146a5565b610acf565b60405161031c9e9d9c9b9a999897969594939291906147b6565b61040a6104053660046146a5565b610c7a565b60405161031c9190614990565b6104366104253660046146a5565b600c6020525f908152604090205481565b60405190815260200161031c565b6102e56104523660046149ad565b610ef7565b6102e56104653660046146a5565b611135565b6102e5610478366004614683565b6111c4565b61049061048b366004614a43565b611257565b60405161031c9190614a5c565b60045461030f906001600160a01b031681565b61043660055481565b6102e56112ee565b6102e56104cf366004614a6e565b611301565b6102e56104e23660046146a5565b6113c1565b6104fa6104f5366004614ad9565b6114b4565b604051901515815260200161031c565b6102e5610518366004614b50565b61177a565b6102e561052b366004614b70565b61186e565b60015461030f906001600160a01b031681565b61030f61197a565b5f5461030f906001600160a01b031681565b60035461030f906001600160a01b031681565b6105786119a8565b60405161031c9190614bbe565b6102e56105933660046146a5565b6119ee565b6105786105a63660046146a5565b611b5c565b6105be6105b93660046146a5565b611bb5565b60405161031c929190614bdf565b61030f6105da3660046146a5565b5f908152601060205260409020546001600160a01b031690565b6104fa610602366004614ad9565b611bdc565b610436610615366004614bf5565b611e74565b61075f604080516101e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081019190915250604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a0840152640100000000909104166101c082015290565b60405161031c9190614c2c565b61030f61077a3660046146a5565b5f908152600a60205260409020546001600160a01b031690565b6102e56107a2366004614d39565b61243e565b6102e56107b5366004614683565b6124fa565b6102e56107c8366004614b50565b6125a1565b6104fa6107db366004614683565b60076020525f908152604090205460ff1681565b61043660065481565b6102e5610806366004614a6e565b61265e565b6102e56108193660046146a5565b612718565b61084061082c3660046146a5565b5f908152600d602052604090205460ff1690565b60405161031c9190614d73565b6102e561085b366004614d81565b612755565b6102e561086e366004614683565b6129e2565b6102e5610881366004614d9b565b612a7c565b60025461030f906001600160a01b031681565b6102e56108a7366004614683565b612d29565b6108bf6108ba366004614bf5565b612d63565b60405161031c929190614dd2565b6103bd6108db3660046146a5565b613640565b6102e56108ee366004614683565b6137da565b61030f6109013660046146a5565b600a6020525f90815260409020546001600160a01b031681565b6102e5610929366004614683565b613872565b610936613901565b6001600160a01b0381166109915760405162461bcd60e51b815260206004820152601f60248201527f496e76616c6964204533526566756e644d616e6167657220616464726573730060448201526064015b60405180910390fd5b600280546001600160a01b0319166001600160a01b0383169081179091556040517f9557d04c1c0b16f93f13b69aed23b3b6ab935bff3c53ac81d17896d3583542ed905f90a250565b6012602052815f5260405f2081600281106109f3575f80fd5b60089182820401919006600402915091509054906101000a900463ffffffff1681565b610a1e613901565b6001600160a01b03811615801590610a4457506004546001600160a01b03828116911614155b8190610a645760405163eddf07f560e01b815260040161098891906146c9565b50600480546001600160a01b0319166001600160a01b0383161790556040517f722ff84c1234b2482061def5c82c6b5080c117b3cbb69d686844a051e4b8e7f390610ab09083906146c9565b60405180910390a150565b610ac3613901565b610acc81613933565b50565b60086020525f9081526040902080546001820154600283015460058401546006850154600786018054959660ff95861696949593946001600160a01b03841694600160a01b90940490931692909190610b2790614df2565b80601f0160208091040260200160405190810160405280929190818152602001828054610b5390614df2565b8015610b9e5780601f10610b7557610100808354040283529160200191610b9e565b820191905f5260205f20905b815481529060010190602001808311610b8157829003601f168201915b50505060088401546009850154600a860154600b870154600c8801805497986001600160a01b03958616989490951696509194509291610bdd90614df2565b80601f0160208091040260200160405190810160405280929190818152602001828054610c0990614df2565b8015610c545780601f10610c2b57610100808354040283529160200191610c54565b820191905f5260205f20905b815481529060010190602001808311610c3757829003601f168201915b505050600d90930154919250506001600160a01b0381169060ff600160a01b909104168e565b610c826144f1565b5f8281526008602090815260409182902082516101e08101909352805483526001810154909183019060ff166003811115610cbf57610cbf614742565b6003811115610cd057610cd0614742565b8152600282810154602083015260408051808201808352919093019291600385019182845b815481526020019060010190808311610cf55750505091835250506005820154602082015260068201546001600160a01b0381166040830152600160a01b900460ff166060820152600782018054608090920191610d5290614df2565b80601f0160208091040260200160405190810160405280929190818152602001828054610d7e90614df2565b8015610dc95780601f10610da057610100808354040283529160200191610dc9565b820191905f5260205f20905b815481529060010190602001808311610dac57829003601f168201915b505050918352505060088201546001600160a01b0390811660208301526009830154166040820152600a8201546060820152600b8201546080820152600c8201805460a090920191610e1a90614df2565b80601f0160208091040260200160405190810160405280929190818152602001828054610e4690614df2565b8015610e915780601f10610e6857610100808354040283529160200191610e91565b820191905f5260205f20905b815481529060010190602001808311610e7457829003601f168201915b5050509183525050600d91909101546001600160a01b038082166020840152600160a01b90910460ff16151560409092019190915260a0820151919250839116610ef15760405163cd6f4a4f60e01b815260040161098891815260200190565b50919050565b5f610f006139f0565b805490915060ff600160401b82041615906001600160401b03165f81158015610f265750825b90505f826001600160401b03166001148015610f415750303b155b905081158015610f4f575080155b15610f6d5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610f9757845460ff60401b1916600160401b1785555b610fa033613a1a565b610fa987612718565b610fb28b6137da565b610fbb8a6129e2565b610fc48961092e565b610fcd88610a16565b610fd686613933565b604080516101e081018252620186a080825261c3506020808401829052612710948401859052603260608501819052620493e060808601819052620f424060a0870181905261138860c088018190525f60e089018190526105dc6101008a015261012089018190526109c46101408a018190526101608a018390526101808a01526101a089018190526101c090980197909752601895909555601993909355601a95909555601b94909455601c55601d55601e55601f80546001600160f01b03191669027104e202710000017760a21b179055805467ffffffffffffffff191690556110c061197a565b6001600160a01b03168c6001600160a01b0316146110e1576110e18c612d29565b831561112757845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050505050565b61113d613901565b5f8181526009602052604090205481906001600160a01b0316611176576040516381c4951960e01b815260040161098891815260200190565b505f818152600960205260409081902080546001600160a01b0319169055517f104eb329a192aef26eddea07c2af5ad2587792e62b37ed4045b6ba59bc5540fc90610ab09083815260200190565b6111cc613901565b6001600160a01b0381165f90815260076020526040902054819060ff16611207576040516321ac7c5f60e01b815260040161098891906146c9565b506001600160a01b0381165f9081526007602052604090819020805460ff19169055517f56070b80bd617fcd2f7a284861edb488830a38f9dedcd77b2cb2f4eac17743e790610ab09083906146c9565b600b6020525f90815260409020805461126f90614df2565b80601f016020809104026020016040519081016040528092919081815260200182805461129b90614df2565b80156112e65780601f106112bd576101008083540402835291602001916112e6565b820191905f5260205f20905b8154815290600101906020018083116112c957829003601f168201915b505050505081565b6112f6613901565b6112ff5f613a2b565b565b611309613901565b6001600160a01b0381161580159061133a57505f828152600a60205260409020546001600160a01b03828116911614155b829061135c576040516381c4951960e01b815260040161098891815260200190565b505f828152600a60205260409081902080546001600160a01b0319166001600160a01b0384161790555182907f53661e3e12f23eea1e322a5352171ad3e4407d1394f869f53bb148c27e00908a906113b59084906146c9565b60405180910390a25050565b5f546001600160a01b031633146113eb5760405163b56831db60e01b815260040160405180910390fd5b5f818152600d602052604090205460ff16600181600681111561141057611410614742565b1461143557816001826040516337e1404160e01b815260040161098893929190614e24565b5f828152600d60205260409020805460ff1916600217905560155461145a9042614e59565b5f838152600e602052604080822092909255905183917fc44405af9078047712501f519e1fb900c2896c62b488336f84529c72ae16e6f191a2815f5160206159ab5f395f51905f52600160026040516113b5929190614e6c565b5f5f6114bf87610c7a565b5f888152600d602052604090205490915060ff1660048160068111156114e7576114e7614742565b148860048390919261150f576040516337e1404160e01b815260040161098893929190614e24565b5050505f888152600e60209081526040918290208251606081018452815481526001820154928101929092526002015491810182905290899042811015611572576040516308f3034360e31b815260048101929092526024820152604401610988565b50505f898152600860205260409020600c0161158f888a83614efa565b505f898152600d60205260409020805460ff191660051790556101c0830151156116f957846115d157604051631eae1a4d60e31b815260040160405180910390fd5b5f80546040516304cd0b0d60e11b8152600481018c90526001600160a01b039091169063099a161a90602401602060405180830381865afa158015611618573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061163c9190614fae565b90508361010001516001600160a01b031663de12c6408a8a604051611662929190614fc5565b6040519081900381206001600160e01b031960e084901b16825261168e9185908c908c90600401614ffc565b602060405180830381865afa1580156116a9573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116cd9190615028565b94508888866116f157604051632f9f8ab960e01b8152600401610988929190615043565b5050506116fe565b600193505b61170789613a9b565b887f3a140076c461ebc41d74833ae0ee8bbc8079a135a63392098cd381e84350b69b8989898960405161173d9493929190615056565b60405180910390a2885f5160206159ab5f395f51905f5260046005604051611766929190614e6c565b60405180910390a250505095945050505050565b5f546001600160a01b031633146117a45760405163b56831db60e01b815260040160405180910390fd5b5f828152600860209081526040808320600d9092529091205460ff1660028160068111156117d4576117d4614742565b146117f957836002826040516337e1404160e01b815260040161098893929190614e24565b5f848152600d6020526040808220805460ff19166003179055600a84018590555185917f11df18edb9bc9cd90a79068e0e208b630202148643d797d6150e7bacb733e63c91a2835f5160206159ab5f395f51905f5260026003604051611860929190614e6c565b60405180910390a250505050565b611876613901565b806118b25760405162461bcd60e51b815260206004820152600c60248201526b456d70747920706172616d7360a01b6044820152606401610988565b60ff83165f908152600b6020526040902080546118ce90614df2565b15905061191d5760405162461bcd60e51b815260206004820152601b60248201527f506172616d53657420616c7265616479207265676973746572656400000000006044820152606401610988565b60ff83165f908152600b60205260409020611939828483614efa565b507f6e4a4ea7f38fc775e616080b155744337e6216848e886a69c918b4ab84da219583838360405161196d93929190615087565b60405180910390a1505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6119c960405180606001604052805f81526020015f81526020015f81525090565b5060408051606081018252601554815260165460208201526017549181019190915290565b5f818152600d602052604090205460ff166006816006811115611a1357611a13614742565b148290611a3657604051637cb2d48360e11b815260040161098891815260200190565b505f828152600c60205260409020548281611a67576040516345ba89d560e11b815260040161098891815260200190565b505f838152600c60205260408120819055611a8184613f07565b5f858152601160205260409020546002549192506001600160a01b0390811691611aae9183911685613ff4565b60025460405163da19b69760e01b81526001600160a01b039091169063da19b69790611ae49088908790879087906004016150ef565b5f604051808303815f87803b158015611afb575f5ffd5b505af1158015611b0d573d5f5f3e3d5ffd5b50505050847f5297818f48a66292b8b3e2caab83eec531b669bb20807fd38cf006adb2a07317848451604051611b4d929190918252602082015260400190565b60405180910390a25050505050565b611b7d60405180606001604052805f81526020015f81526020015f81525090565b505f908152600e6020908152604091829020825160608101845281548152600182015492810192909252600201549181019190915290565b5f818152600d6020526040812054819060ff16611bd28482614051565b9250925050915091565b5f5f611be787610c7a565b5f888152600d602052604090205490915060ff166003816006811115611c0f57611c0f614742565b1488600383909192611c37576040516337e1404160e01b815260040161098893929190614e24565b5050505f888152600e6020908152604091829020825160608101845281548152600182015492810183905260029091015492810192909252899042811015611c9b576040516308f3034360e31b815260048101929092526024820152604401610988565b5050606083015160200151899042811115611cd25760405163017e35e560e71b815260048101929092526024820152604401610988565b5050610160830151899015611cfd57604051637eb9cea960e11b815260040161098891815260200190565b505f8888604051611d0f929190614fc5565b60408051918290039091205f8c815260086020908152838220600b01839055600d905291909120805460ff19166004179055601754909150611d519042614e59565b5f8b8152600e6020526040908190206002019190915560a08501519051632f0e1bbf60e01b81526001600160a01b0390911690632f0e1bbf90611d9e908d9085908c908c90600401614ffc565b6020604051808303815f875af1158015611dba573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dde9190615028565b9450888886611e0257604051632f9f8ab960e01b8152600401610988929190615043565b5050897f7cc27e4a5626cbc4f8ba1a927b0448de55e6a114bc87660331270c5109ade0718a8a604051611e36929190615043565b60405180910390a2895f5160206159ab5f395f51905f5260036004604051611e5f929190614e6c565b60405180910390a25050505095945050505050565b5f80600b81611e8960a0860160808701614a43565b60ff1660ff1681526020019081526020015f208054611ea790614df2565b905011611ec65760405162461bcd60e51b81526004016109889061513a565b5f601281611ed76020860186615171565b6003811115611ee857611ee8614742565b6003811115611ef957611ef9614742565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411611f20579050505050505090505f81600160028110611f7757611f77615126565b602002015163ffffffff1611835f016020810190611f959190615171565b90611fb45760405163286c068d60e11b8152600401610988919061518a565b506020808201518251604080516101e081018252601854815260195481860152601a5491810191909152601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e0830152600160a01b810461ffff908116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152925463ffffffff8181166101a0860181905264010000000090920481166101c0860152928316939190921691156120df576101a081015163ffffffff16846001602002015163ffffffff161015865f0160208101906120be9190615171565b906120dd5760405163010b971d60e31b8152600401610988919061518a565b505b6101c081015163ffffffff161561212e576101c081015184519063ffffffff908116908216101561212c57604051630a4b6b6360e11b815263ffffffff9091166004820152602401610988565b505b6040860135602087013581101561215b5760405163174b5a0760e21b815260040161098891815260200190565b506101808101516017545f91612710916121799161ffff1690615198565b61218391906151af565b61271061ffff1683610160015161ffff166015600101546121a49190615198565b6121ae91906151af565b61271061ffff1684610140015161ffff1660155f01546121ce9190615198565b6121d891906151af565b5f5460408051634f87c3a560e11b8152815160208e81013594938f0135936001600160a01b031692639f0f874a92600480830193928290030181865afa158015612224573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122489190614fae565b6122529190614e59565b61225c91906151ce565b6122669190614e59565b6122709190614e59565b61227a9190614e59565b90505f6122886001866151ce565b612293906004615198565b61229e90600e614e59565b90505f85845f01516122b09190615198565b9050818685602001516122c39190615198565b6122cd9190615198565b6122d79082614e59565b9050600186111561231f5760026122ef6001886151ce565b6122f99088615198565b85604001516123089190615198565b61231291906151af565b61231c9082614e59565b90505b81868560c001516123309190615198565b61233a9190615198565b6123449082614e59565b9050828685606001516123579190615198565b6123619190615198565b61236b9082614e59565b905084846080015161237d9190615198565b6123879082614e59565b905060018511156123cf57600261239f6001876151ce565b6123a99087615198565b85604001516123b89190615198565b6123c291906151af565b6123cc9082614e59565b90505b60a08401516123de9082614e59565b610100850151909150612710906123f99061ffff1682614e59565b6124039083615198565b61240d91906151af565b9750878061243157604051638c4fcd9360e01b815260040161098891815260200190565b5050505050505050919050565b5f546001600160a01b031633148061246057506003546001600160a01b031633145b61247d57604051639e75a8b560e01b815260040160405180910390fd5b5f8160ff161180156124935750600d60ff821611155b6124d85760405162461bcd60e51b815260206004820152601660248201527524b73b30b634b2103330b4b63ab932903932b0b9b7b760511b6044820152606401610988565b6124f6828260ff16600d8111156124f1576124f1614742565b6141d9565b5050565b612502613901565b6001600160a01b0381166125585760405162461bcd60e51b815260206004820152601f60248201527f496e76616c696420536c617368696e674d616e616765722061646472657373006044820152606401610988565b600380546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b6003546001600160a01b031633146125cc576040516357d6948d60e11b815260040160405180910390fd5b60025460405163c1ab0f1f60e01b815260048101849052602481018390526001600160a01b039091169063c1ab0f1f906044015f604051808303815f87803b158015612616575f5ffd5b505af1158015612628573d5f5f3e3d5ffd5b50505050817f4f41a3b0a032ebcae925f2ace77d507435840ca4b2dbaffdd7723fa8d72ee542826040516113b591815260200190565b612666613901565b6001600160a01b0381161580159061269757505f828152600960205260409020546001600160a01b03828116911614155b82906126b9576040516381c4951960e01b815260040161098891815260200190565b505f8281526009602090815260409182902080546001600160a01b0319166001600160a01b03851617905590518381527ff4041a3f914dac3bc9bf5f003ba41f28dbb84abe42f4e07c76266f5c8ceecb69910160405180910390a15050565b612720613901565b60058190556040518181527fba0716ba1ee2ea8ecc4c64119b4537cdb42a99d82acf92af5b87607b8b52355290602001610ab0565b61275d613901565b612710612772610120830161010084016151fb565b61ffff16111561278a610120830161010084016151fb565b906127af576040516301027fc160e21b815261ffff9091166004820152602401610988565b506127106127c5610140830161012084016151fb565b61ffff1611156127dd610140830161012084016151fb565b90612802576040516301027fc160e21b815261ffff9091166004820152602401610988565b50612710612818610160830161014084016151fb565b61ffff161115612830610160830161014084016151fb565b9061285557604051633239953960e01b815261ffff9091166004820152602401610988565b5061271061286b610180830161016084016151fb565b61ffff161115612883610180830161016084016151fb565b906128a857604051633239953960e01b815261ffff9091166004820152602401610988565b506127106128be6101a0830161018084016151fb565b61ffff1611156128d66101a0830161018084016151fb565b906128fb57604051633239953960e01b815261ffff9091166004820152602401610988565b5061290e610140820161012083016151fb565b61ffff16158061293757505f61292b610100830160e08401614683565b6001600160a01b031614155b6129545760405163015f92ff60e51b815260040160405180910390fd5b6129666101e082016101c08301615232565b63ffffffff1661297e6101c083016101a08401615232565b63ffffffff1610156129a3576040516392f55c6560e01b815260040160405180910390fd5b8060186129b08282615271565b9050507fbf3951313e980027eb48ce363fdb707286195ec6a0f802ac153927cf929c3fc681604051610ab0919061542f565b6129ea613901565b6001600160a01b03811615801590612a1057506001546001600160a01b03828116911614155b8190612a30576040516320252f0b60e01b815260040161098891906146c9565b50600180546001600160a01b0319166001600160a01b0383161790556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790610ab09083906146c9565b612a84613901565b612a916020820182615232565b63ffffffff16612aa76040830160208401615232565b63ffffffff1610158015612acc57505f612ac46020830183615232565b63ffffffff16115b612ae957604051634564ab9b60e01b815260040160405180910390fd5b604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a08401819052640100000000909204166101c083015215612c22576101a081015163ffffffff16612bd26040840160208501615232565b63ffffffff161015612bea6040840160208501615232565b826101a001519091612c1f57604051633ccc4c2160e21b815263ffffffff928316600482015291166024820152604401610988565b50505b6101c081015163ffffffff1615612c99576101c081015163ffffffff16612c4c6020840184615232565b63ffffffff161015612c616020840184615232565b826101c001519091612c965760405163156c4e5b60e11b815263ffffffff928316600482015291166024820152604401610988565b50505b8160125f856003811115612caf57612caf614742565b6003811115612cc057612cc0614742565b815260208101919091526040015f20612cda91600261456e565b50826003811115612ced57612ced614742565b7f8b56fae526eee054f0849759a99fc7d4ff3823824ebf097a56f7d78adb6b34fa83604051612d1c9190615539565b60405180910390a2505050565b612d31613901565b6001600160a01b038116612d5a575f604051631e4fbdf760e01b815260040161098891906146c9565b610acc81613a2b565b5f612d6c6144f1565b5f601281612d7d6020870187615171565b6003811115612d8e57612d8e614742565b6003811115612d9f57612d9f614742565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411612dc6579050505050505090505f81600160028110612e1d57612e1d615126565b602002015163ffffffff1611845f016020810190612e3b9190615171565b90612e5a5760405163286c068d60e11b8152600401610988919061518a565b50602084013542811015612e8457604051630b99e87960e01b815260040161098891815260200190565b5060408401356020850135811015612eb25760405163174b5a0760e21b815260040161098891815260200190565b506017546016545f9190612eca4260408901356151ce565b612ed49190614e59565b612ede9190614e59565b905060055481108190612f07576040516313b783af60e21b815260040161098891815260200190565b5060075f612f1b6080880160608901614683565b6001600160a01b0316815260208101919091526040015f205460ff16612f476080870160608801614683565b90612f665760405163295a6a6f60e11b815260040161098891906146c9565b505f612f7186611e74565b60068054965090915085905f612f8683615579565b9091555050604080514460208201529081018690525f9060600160408051601f1981840301815291815281516020928301205f898152600c84528281208690556004546011855283822080546001600160a01b03199081166001600160a01b0393841617909155601f805460138852868520805461ffff191661ffff600160b01b909304929092169190911790555460148752858420805483169190931617909155600d8552838220805460ff1916600117905560109094528290208054339416939093179092556016549192506130619190890135614e59565b5f878152600e602090815260409091206001019190915581865261308790880188615171565b8560200190600381111561309d5761309d614742565b908160038111156130b0576130b0614742565b905250436040808701919091528051808201825290602089019060029083908390808284375f9201919091525050506060808701919091526130f89060808901908901614683565b6001600160a01b031660a08087019190915261311990880160808901614a43565b60ff1660c08087019190915261313190880188615591565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050505060e08087019190915261317e9061010089019089016155d3565b15156101c08601525f610140860181905261016086018190526040805160208101909152818152610180870152336101a0870152600b816131c560a08b0160808c01614a43565b60ff1660ff1681526020019081526020015f2080546131e390614df2565b80601f016020809104026020016040519081016040528092919081815260200182805461320f90614df2565b801561325a5780601f106132315761010080835404028352916020019161325a565b820191905f5260205f20905b81548152906001019060200180831161323d57829003601f168201915b505050505090505f8151116132815760405162461bcd60e51b81526004016109889061513a565b5f61329260808a0160608b01614683565b6001600160a01b031663fefd9a8b8985856132b060a08f018f615591565b8f8060c001906132c09190615591565b6040518863ffffffff1660e01b81526004016132e297969594939291906155ee565b6020604051808303815f875af11580156132fe573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906133229190614fae565b5f818152600960205260409020549091506001600160a01b0316818161335e576040516381c4951960e01b815260040161098891815260200190565b505f828152600a60205260409020546001600160a01b03168281613398576040516381c4951960e01b815260040161098891815260200190565b50608089018390526001600160a01b038083166101008b015281166101208a01525f8a81526008602090815260409091208a518155908a0151600180830180548d94939260ff1991909116908360038111156133f6576133f6614742565b02179055506040820151816002015560608201518160030190600261341c92919061460f565b506080820151600582015560a082015160068201805460c085015160ff16600160a01b026001600160a81b03199091166001600160a01b039093169290921791909117905560e082015160078201906134759082615642565b506101008201516008820180546001600160a01b039283166001600160a01b031991821617909155610120840151600984018054919093169116179055610140820151600a820155610160820151600b820155610180820151600c8201906134dd9082615642565b506101a0820151600d90910180546101c0909301511515600160a01b026001600160a81b03199093166001600160a01b0392831617929092179091556004546135299116333089614334565b5f5460405163291a691b60e01b81526001600160a01b039091169063291a691b9061355c908d9089908d906004016156f7565b6020604051808303815f875af1158015613578573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061359c9190615028565b6135b957604051630d8dbe2560e01b815260040160405180910390fd5b6135c960808c0160608d01614683565b6001600160a01b03167f5090c9764b5cd13df7afc0013f733dfbe6eaf1b6ddc22a5e291fa387efd4c15e8b8b604051613603929190614dd2565b60405180910390a2895f5160206159ab5f395f51905f525f600160405161362b929190614e6c565b60405180910390a25050505050505050915091565b5f818152600d602052604081205460ff168181600681111561366457613664614742565b0361368957826001826040516337e1404160e01b815260040161098893929190614e24565b600581600681111561369d5761369d614742565b036136be5760405163462c7bed60e01b815260048101849052602401610988565b60068160068111156136d2576136d2614742565b036136f357604051633de16e3560e11b815260048101849052602401610988565b5f6136fe8483614051565b935090508061372357604051639f65d93560e01b815260048101859052602401610988565b5f848152600d6020526040902080546006919060ff191660018302179055505f848152600f60205260409020805484919060ff1916600183600d81111561376c5761376c614742565b0217905550835f5160206159ab5f395f51905f52836006604051613791929190614e6c565b60405180910390a2837fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb83856040516137cb92919061573c565b60405180910390a25050919050565b6137e2613901565b6001600160a01b0381161580159061380757505f546001600160a01b03828116911614155b8190613827576040516375ac4eb760e11b815260040161098891906146c9565b505f80546001600160a01b0319166001600160a01b0383161790556040517f80052b810d39120cf6c976cca504a21703f585521dc7a41c6d241090e6c579b690610ab09083906146c9565b6001600160a01b0381165f90815260076020526040902054819060ff16156138ae5760405163b29d459560e01b815260040161098891906146c9565b506001600160a01b0381165f9081526007602052604090819020805460ff19166001179055517fb8d368517268f297fff00825d67d098763117d061360d31027be5b2e1a59d46790610ab09083906146c9565b3361390a61197a565b6001600160a01b0316146112ff573360405163118cdaa760e01b815260040161098891906146c9565b80356139525760405163055f269d60e01b815260040160405180910390fd5b5f8160200135116139765760405163055f269d60e01b815260040160405180910390fd5b5f81604001351161399a5760405163055f269d60e01b815260040160405180910390fd5b80356015819055602080830135601681905560408085013560178190558151948552928401919091528201527f7e86ba16b805e2835af5c5b7aa5a942ced8bcc1fb95a05fbe42dae3862350a1690606001610ab0565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005b92915050565b613a22614373565b610acc81614398565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b5f805460405162beb08960e51b8152600481018490526001600160a01b03909116906317d61120906024015f60405180830381865afa158015613ae0573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613b079190810190615813565b5080515f848152600c60209081526040808320805490849055601190925282205493945091926001600160a01b031690829003613ba6576002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613b72908890889086906004016158d8565b5f604051808303815f87803b158015613b89575f5ffd5b505af1158015613b9b573d5f5f3e3d5ffd5b505050505050505050565b825f03613c47575f858152601060205260409020546001600160a01b03168015613bde57613bde6001600160a01b0383168285613ff4565b6002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613c12908990899087906004016158d8565b5f604051808303815f87803b158015613c29575f5ffd5b505af1158015613c3b573d5f5f3e3d5ffd5b50505050505050505050565b5f85815260136020908152604080832054601490925282205461ffff909116906001600160a01b03168115801590613c8757506001600160a01b03811615155b15613cc357612710613c9d61ffff841687615198565b613ca791906151af565b92508215613cc357613cc36001600160a01b0385168285613ff4565b5f613cce84876151ce565b90505f876001600160401b03811115613ce957613ce9614e87565b604051908082528060200260200182016040528015613d12578160200160208202803683370190505b5090505f613d2089846151af565b90505f805b8a811015613d5f5782848281518110613d4057613d40615126565b6020908102919091010152613d558383614e59565b9150600101613d25565b505f613d6b82866151ce565b90508015613da8578084613d8060018e6151ce565b81518110613d9057613d90615126565b60200260200101818151613da49190614e59565b9052505b600154613dc2906001600160a01b038b81169116876143a0565b60015f9054906101000a90046001600160a01b03166001600160a01b031663dd8c818e8a8e876040518463ffffffff1660e01b8152600401613e0693929190615938565b5f604051808303815f87803b158015613e1d575f5ffd5b505af1158015613e2f573d5f5f3e3d5ffd5b5050600154613e4d92506001600160a01b038c81169250165f6143a0565b8c7fac9fe8ad7f55eac03284399116ecafc104f10459773f4cdf47063c46e5be335a8d86604051613e7f92919061596d565b60405180910390a260025f9054906101000a90046001600160a01b03166001600160a01b03166341489f158e8e8c6040518463ffffffff1660e01b8152600401613ecb939291906158d8565b5f604051808303815f87803b158015613ee2575f5ffd5b505af1158015613ef4573d5f5f3e3d5ffd5b5050505050505050505050505050505050565b5f818152600f602052604090205460609060ff16600181600d811115613f2f57613f2f614742565b1480613f4c5750600281600d811115613f4a57613f4a614742565b145b15613f84575f5b604051908082528060200260200182016040528015613f7c578160200160208202803683370190505b509392505050565b5f5460405162beb08960e51b8152600481018590526001600160a01b03909116906317d61120906024015f60405180830381865afa925050508015613fea57506040513d5f823e601f3d908101601f19168201604052613fe79190810190615813565b60015b613f7c575f613f53565b61404c83846001600160a01b031663a9059cbb858560405160240161401a929190615991565b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505061442c565b505050565b5f828152600e60209081526040808320815160608101835281548152600182015493810193909352600201548282015282549051632800d82960e01b81526004810186905283929183916001600160a01b0390911690632800d82990602401602060405180830381865afa1580156140cb573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906140ef9190614fae565b9050600185600681111561410557614105614742565b14801561411157508042115b15614124576001809350935050506141d2565b600285600681111561413857614138614742565b1480156141455750815142115b1561415957600160039350935050506141d2565b600385600681111561416d5761416d614742565b14801561417d5750816020015142115b1561419157600160069350935050506141d2565b60048560068111156141a5576141a5614742565b1480156141b55750816040015142115b156141c9576001600a9350935050506141d2565b5f5f9350935050505b9250929050565b5f828152600d602052604081205460ff16908160068111156141fd576141fd614742565b0361422257826001826040516337e1404160e01b815260040161098893929190614e24565b600581600681111561423657614236614742565b036142575760405163462c7bed60e01b815260048101849052602401610988565b600681600681111561426b5761426b614742565b0361428c57604051633de16e3560e11b815260048101849052602401610988565b5f838152600d6020526040902080546006919060ff191660018302179055505f838152600f60205260409020805483919060ff1916600183600d8111156142d5576142d5614742565b0217905550825f5160206159ab5f395f51905f528260066040516142fa929190614e6c565b60405180910390a2827fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb8284604051612d1c92919061573c565b6040516001600160a01b03848116602483015283811660448301526064820183905261436d9186918216906323b872dd9060840161401a565b50505050565b61437b61448f565b6112ff57604051631afcd79f60e31b815260040160405180910390fd5b612d31614373565b5f836001600160a01b031663095ea7b384846040516024016143c3929190615991565b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505090506143fc84826144a8565b61436d5761442684856001600160a01b031663095ea7b3865f60405160240161401a929190615991565b61436d84825b5f5f60205f8451602086015f885af18061444b576040513d5f823e3d81fd5b50505f513d9150811561446257806001141561446f565b6001600160a01b0384163b155b1561436d5783604051635274afe760e01b815260040161098891906146c9565b5f6144986139f0565b54600160401b900460ff16919050565b5f5f5f5f60205f8651602088015f8a5af192503d91505f5190508280156144e7575081156144d957806001146144e7565b5f866001600160a01b03163b115b9695505050505050565b604080516101e081019091525f808252602082019081526020015f815260200161451961463d565b81525f602082018190526040820181905260608083018290526080830181905260a0830182905260c0830182905260e08301829052610100830182905261012083015261014082018190526101609091015290565b6001830191839082156145ff579160200282015f5b838211156145cd57833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302614583565b80156145fd5782816101000a81549063ffffffff02191690556004016020816003010492830192600103026145cd565b505b5061460b92915061465b565b5090565b82600281019282156145ff579160200282015b828111156145ff578251825591602001919060010190614622565b60405180604001604052806002906020820280368337509192915050565b5b8082111561460b575f815560010161465c565b6001600160a01b0381168114610acc575f5ffd5b5f60208284031215614693575f5ffd5b813561469e8161466f565b9392505050565b5f602082840312156146b5575f5ffd5b5035919050565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b8035600481106146eb575f5ffd5b919050565b5f5f60408385031215614701575f5ffd5b61470a836146dd565b946020939093013593505050565b5f60608284031215610ef1575f5ffd5b5f60608284031215614738575f5ffd5b61469e8383614718565b634e487b7160e01b5f52602160045260245ffd5b600e811061476657614766614742565b9052565b60208101613a148284614756565b6004811061476657614766614742565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b8e81526147c6602082018f614778565b8c60408201528b60608201526147df608082018c6146bc565b60ff8a1660a08201526101c060c08201525f6147ff6101c083018b614788565b61480c60e084018b6146bc565b61481a61010084018a6146bc565b876101208401528661014084015282810361016084015261483b8187614788565b91505061484c6101808301856146bc565b8215156101a08301529f9e505050505050505050505050505050565b805f5b600281101561436d57815184526020938401939091019060010161486b565b805182525f60208201516148a16020850182614778565b506040820151604084015260608201516148be6060850182614868565b50608082015160a084015260a08201516148db60c08501826146bc565b5060c082015160ff811660e08501525060e0820151610200610100850152614907610200850182614788565b905061010083015161491d6101208601826146bc565b506101208301516149326101408601826146bc565b506101408301516101608501526101608301516101808501526101808301518482036101a08601526149648282614788565b9150506101a083015161497b6101c08601826146bc565b506101c08301518015156101e0860152613f7c565b602081525f61469e602083018461488a565b80356146eb8161466f565b5f5f5f5f5f5f5f610120888a0312156149c4575f5ffd5b87356149cf8161466f565b965060208801356149df8161466f565b955060408801356149ef8161466f565b945060608801356149ff8161466f565b93506080880135614a0f8161466f565b925060a08801359150614a258960c08a01614718565b905092959891949750929550565b803560ff811681146146eb575f5ffd5b5f60208284031215614a53575f5ffd5b61469e82614a33565b602081525f61469e6020830184614788565b5f5f60408385031215614a7f575f5ffd5b823591506020830135614a918161466f565b809150509250929050565b5f5f83601f840112614aac575f5ffd5b5081356001600160401b03811115614ac2575f5ffd5b6020830191508360208285010111156141d2575f5ffd5b5f5f5f5f5f60608688031215614aed575f5ffd5b8535945060208601356001600160401b03811115614b09575f5ffd5b614b1588828901614a9c565b90955093505060408601356001600160401b03811115614b33575f5ffd5b614b3f88828901614a9c565b969995985093965092949392505050565b5f5f60408385031215614b61575f5ffd5b50508035926020909101359150565b5f5f5f60408486031215614b82575f5ffd5b614b8b84614a33565b925060208401356001600160401b03811115614ba5575f5ffd5b614bb186828701614a9c565b9497909650939450505050565b81518152602080830151908201526040808301519082015260608101613a14565b82151581526040810161469e6020830184614756565b5f60208284031215614c05575f5ffd5b81356001600160401b03811115614c1a575f5ffd5b8201610100818503121561469e575f5ffd5b5f6101e082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e0830151614c8660e08401826146bc565b50610100830151614c9e61010084018261ffff169052565b50610120830151614cb661012084018261ffff169052565b50610140830151614cce61014084018261ffff169052565b50610160830151614ce661016084018261ffff169052565b50610180830151614cfe61018084018261ffff169052565b506101a0830151614d186101a084018263ffffffff169052565b506101c0830151614d326101c084018263ffffffff169052565b5092915050565b5f5f60408385031215614d4a575f5ffd5b82359150614d5a60208401614a33565b90509250929050565b6007811061476657614766614742565b60208101613a148284614d63565b5f6101e0828403128015614d93575f5ffd5b509092915050565b5f5f60608385031215614dac575f5ffd5b614db5836146dd565b915083606084011115614dc6575f5ffd5b50926020919091019150565b828152604060208201525f614dea604083018461488a565b949350505050565b600181811c90821680614e0657607f821691505b602082108103610ef157634e487b7160e01b5f52602260045260245ffd5b83815260608101614e386020830185614d63565b614dea6040830184614d63565b634e487b7160e01b5f52601160045260245ffd5b80820180821115613a1457613a14614e45565b60408101614e7a8285614d63565b61469e6020830184614d63565b634e487b7160e01b5f52604160045260245ffd5b601f82111561404c57805f5260205f20601f840160051c81016020851015614ec05750805b601f840160051c820191505b81811015614edf575f8155600101614ecc565b5050505050565b5f19600383901b1c191660019190911b1790565b6001600160401b03831115614f1157614f11614e87565b614f2583614f1f8354614df2565b83614e9b565b5f601f841160018114614f51575f8515614f3f5750838201355b614f498682614ee6565b845550614edf565b5f83815260208120601f198716915b82811015614f805786850135825560209485019460019092019101614f60565b5086821015614f9c575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b5f60208284031215614fbe575f5ffd5b5051919050565b818382375f9101908152919050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b848152836020820152606060408201525f6144e7606083018486614fd4565b8015158114610acc575f5ffd5b5f60208284031215615038575f5ffd5b815161469e8161501b565b602081525f614dea602083018486614fd4565b604081525f615069604083018688614fd4565b828103602084015261507c818587614fd4565b979650505050505050565b60ff84168152604060208201525f6150a3604083018486614fd4565b95945050505050565b5f8151808452602084019350602083015f5b828110156150e55781516001600160a01b03168652602095860195909101906001016150be565b5093949350505050565b848152836020820152608060408201525f61510d60808301856150ac565b905060018060a01b038316606083015295945050505050565b634e487b7160e01b5f52603260045260245ffd5b6020808252601c908201527f42465620706172616d20736574206e6f74207265676973746572656400000000604082015260600190565b5f60208284031215615181575f5ffd5b61469e826146dd565b60208101613a148284614778565b8082028115828204841417613a1457613a14614e45565b5f826151c957634e487b7160e01b5f52601260045260245ffd5b500490565b81810381811115613a1457613a14614e45565b61ffff81168114610acc575f5ffd5b80356146eb816151e1565b5f6020828403121561520b575f5ffd5b813561469e816151e1565b63ffffffff81168114610acc575f5ffd5b80356146eb81615216565b5f60208284031215615242575f5ffd5b813561469e81615216565b5f8135613a148161466f565b5f8135613a14816151e1565b5f8135613a1481615216565b813581556020820135600182015560408201356002820155606082013560038201556080820135600482015560a0820135600582015560c08201356006820155600781016152e16152c460e0850161524d565b82546001600160a01b0319166001600160a01b0391909116178255565b6153116152f16101008501615259565b82805461ffff60a01b191660a09290921b61ffff60a01b16919091179055565b6153416153216101208501615259565b82805461ffff60b01b191660b09290921b61ffff60b01b16919091179055565b6153716153516101408501615259565b82805461ffff60c01b191660c09290921b61ffff60c01b16919091179055565b6153a16153816101608501615259565b82805461ffff60d01b191660d09290921b61ffff60d01b16919091179055565b6153d16153b16101808501615259565b82805461ffff60e01b191660e09290921b61ffff60e01b16919091179055565b50600881016153fd6153e66101a08501615265565b825463ffffffff191663ffffffff91909116178255565b61404c61540d6101c08501615265565b825467ffffffff00000000191660209190911b67ffffffff0000000016178255565b813581526020808301359082015260408083013590820152606080830135908201526080808301359082015260a0808301359082015260c080830135908201526101e0810161548060e084016149a2565b61548d60e08401826146bc565b5061549b61010084016151f0565b61ffff166101008301526154b261012084016151f0565b61ffff166101208301526154c961014084016151f0565b61ffff166101408301526154e061016084016151f0565b61ffff166101608301526154f761018084016151f0565b61ffff1661018083015261550e6101a08401615227565b63ffffffff166101a08301526155276101c08401615227565b63ffffffff81166101c0840152614d32565b6040810181835f5b600281101561557057813561555581615216565b63ffffffff1683526020928301929190910190600101615541565b50505092915050565b5f6001820161558a5761558a614e45565b5060010190565b5f5f8335601e198436030181126155a6575f5ffd5b8301803591506001600160401b038211156155bf575f5ffd5b6020019150368190038213156141d2575f5ffd5b5f602082840312156155e3575f5ffd5b813561469e8161501b565b87815286602082015260a060408201525f61560c60a0830188614788565b828103606084015261561f818789614fd4565b90508281036080840152615634818587614fd4565b9a9950505050505050505050565b81516001600160401b0381111561565b5761565b614e87565b61566f816156698454614df2565b84614e9b565b6020601f82116001811461569c575f831561568a5750848201515b6156948482614ee6565b855550614edf565b5f84815260208120601f198516915b828110156156cb57878501518255602094850194600190920191016156ab565b50848210156156e857868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b838152602081018390526080810160408201835f5b600281101561573157815163ffffffff1683526020928301929091019060010161570c565b505050949350505050565b6040810161574a8285614d63565b61469e6020830184614756565b604051601f8201601f191681016001600160401b038111828210171561577f5761577f614e87565b604052919050565b5f6001600160401b0382111561579f5761579f614e87565b5060051b60200190565b5f82601f8301126157b8575f5ffd5b81516157cb6157c682615787565b615757565b8082825260208201915060208360051b8601019250858311156157ec575f5ffd5b602085015b838110156158095780518352602092830192016157f1565b5095945050505050565b5f5f60408385031215615824575f5ffd5b82516001600160401b03811115615839575f5ffd5b8301601f81018513615849575f5ffd5b80516158576157c682615787565b8082825260208201915060208360051b850101925087831115615878575f5ffd5b6020840193505b828410156158a35783516158928161466f565b82526020938401939091019061587f565b8095505050505060208301516001600160401b038111156158c2575f5ffd5b6158ce858286016157a9565b9150509250929050565b838152606060208201525f6158f060608301856150ac565b905060018060a01b0383166040830152949350505050565b5f8151808452602084019350602083015f5b828110156150e557815186526020958601959091019060010161591a565b6001600160a01b03841681526060602082018190525f9061595b908301856150ac565b82810360408401526144e78185615908565b604081525f61597f60408301856150ac565b82810360208401526150a38185615908565b6001600160a01b0392909216825260208201526040019056fe1b418a230a21d37a078bf8f16decbde8ccceacd77159371f62f0d4ea00d19967a164736f6c634300081c000a", + "deployedBytecode": "0x608060405234801561000f575f5ffd5b50600436106102ce575f3560e01c806390173a4111610182578063cb649617116100e0578063f0691cba1161008f578063f0691cba14610886578063f2fde38b14610899578063f3ceba3a146108ac578063f81b8ef6146108cd578063fad8e111146108e0578063fbdb3237146108f3578063fd2f3d011461091b575f5ffd5b8063cb649617146107ef578063cbd16872146107f8578063cf0f34c41461080b578063cfbdc98d1461081e578063d8afed3e1461084d578063e59e469514610860578063ea71aa5714610873575f5ffd5b80639e57b9341161013c5780639e57b93414610607578063a87f4ab91461061a578063ac3d2f421461076c578063bb2d1b8214610794578063bff232c1146107a7578063c1ab0f1f146107ba578063c4ccafa2146107cd575f5ffd5b806390173a41146105705780639117173c146105855780639231238614610598578063929a8faf146105ab57806399c6679d146105cc5780639c8570c8146105f4575f5ffd5b80635d1684181161022f5780637edcd7ab116101e95780637edcd7ab146104e757806381476ec21461050a578063830d71811461051d57806385814243146105305780638da5cb5b146105435780638dcdd86b1461054b5780638e5ce3ad1461055d575f5ffd5b80635d1684181461047d578063647846a51461049d5780636db5c8fd146104b0578063715018a6146104b95780637c8c3b4d146104c15780637cfa9d74146104d4575f5ffd5b806336c5d38a1161028b57806336c5d38a1461039b5780634017daf0146103ca578063406ed35c146103f75780634147a360146104175780634d600e5d146104445780634e92ec63146104575780634fc772641461046a575f5ffd5b806302a3a9c9146102d25780630ef81b2f146102e757806310bc62811461032557806311bd61d91461034d57806315cce224146103755780631ba7294514610388575b5f5ffd5b6102e56102e0366004614683565b61092e565b005b61030f6102f53660046146a5565b5f908152600960205260409020546001600160a01b031690565b60405161031c91906146c9565b60405180910390f35b61030f6103333660046146a5565b60096020525f90815260409020546001600160a01b031681565b61036061035b3660046146f0565b6109da565b60405163ffffffff909116815260200161031c565b6102e5610383366004614683565b610a16565b6102e5610396366004614728565b610abb565b6103bd6103a93660046146a5565b5f908152600f602052604090205460ff1690565b60405161031c919061476a565b6103dd6103d83660046146a5565b610acf565b60405161031c9e9d9c9b9a999897969594939291906147b6565b61040a6104053660046146a5565b610c7a565b60405161031c9190614990565b6104366104253660046146a5565b600c6020525f908152604090205481565b60405190815260200161031c565b6102e56104523660046149ad565b610ef7565b6102e56104653660046146a5565b611135565b6102e5610478366004614683565b6111c4565b61049061048b366004614a43565b611257565b60405161031c9190614a5c565b60045461030f906001600160a01b031681565b61043660055481565b6102e56112ee565b6102e56104cf366004614a6e565b611301565b6102e56104e23660046146a5565b6113c1565b6104fa6104f5366004614ad9565b6114b4565b604051901515815260200161031c565b6102e5610518366004614b50565b61177a565b6102e561052b366004614b70565b61186e565b60015461030f906001600160a01b031681565b61030f61197a565b5f5461030f906001600160a01b031681565b60035461030f906001600160a01b031681565b6105786119a8565b60405161031c9190614bbe565b6102e56105933660046146a5565b6119ee565b6105786105a63660046146a5565b611b5c565b6105be6105b93660046146a5565b611bb5565b60405161031c929190614bdf565b61030f6105da3660046146a5565b5f908152601060205260409020546001600160a01b031690565b6104fa610602366004614ad9565b611bdc565b610436610615366004614bf5565b611e74565b61075f604080516101e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081019190915250604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a0840152640100000000909104166101c082015290565b60405161031c9190614c2c565b61030f61077a3660046146a5565b5f908152600a60205260409020546001600160a01b031690565b6102e56107a2366004614d39565b61243e565b6102e56107b5366004614683565b6124fa565b6102e56107c8366004614b50565b6125a1565b6104fa6107db366004614683565b60076020525f908152604090205460ff1681565b61043660065481565b6102e5610806366004614a6e565b61265e565b6102e56108193660046146a5565b612718565b61084061082c3660046146a5565b5f908152600d602052604090205460ff1690565b60405161031c9190614d73565b6102e561085b366004614d81565b612755565b6102e561086e366004614683565b6129e2565b6102e5610881366004614d9b565b612a7c565b60025461030f906001600160a01b031681565b6102e56108a7366004614683565b612d29565b6108bf6108ba366004614bf5565b612d63565b60405161031c929190614dd2565b6103bd6108db3660046146a5565b613640565b6102e56108ee366004614683565b6137da565b61030f6109013660046146a5565b600a6020525f90815260409020546001600160a01b031681565b6102e5610929366004614683565b613872565b610936613901565b6001600160a01b0381166109915760405162461bcd60e51b815260206004820152601f60248201527f496e76616c6964204533526566756e644d616e6167657220616464726573730060448201526064015b60405180910390fd5b600280546001600160a01b0319166001600160a01b0383169081179091556040517f9557d04c1c0b16f93f13b69aed23b3b6ab935bff3c53ac81d17896d3583542ed905f90a250565b6012602052815f5260405f2081600281106109f3575f80fd5b60089182820401919006600402915091509054906101000a900463ffffffff1681565b610a1e613901565b6001600160a01b03811615801590610a4457506004546001600160a01b03828116911614155b8190610a645760405163eddf07f560e01b815260040161098891906146c9565b50600480546001600160a01b0319166001600160a01b0383161790556040517f722ff84c1234b2482061def5c82c6b5080c117b3cbb69d686844a051e4b8e7f390610ab09083906146c9565b60405180910390a150565b610ac3613901565b610acc81613933565b50565b60086020525f9081526040902080546001820154600283015460058401546006850154600786018054959660ff95861696949593946001600160a01b03841694600160a01b90940490931692909190610b2790614df2565b80601f0160208091040260200160405190810160405280929190818152602001828054610b5390614df2565b8015610b9e5780601f10610b7557610100808354040283529160200191610b9e565b820191905f5260205f20905b815481529060010190602001808311610b8157829003601f168201915b50505060088401546009850154600a860154600b870154600c8801805497986001600160a01b03958616989490951696509194509291610bdd90614df2565b80601f0160208091040260200160405190810160405280929190818152602001828054610c0990614df2565b8015610c545780601f10610c2b57610100808354040283529160200191610c54565b820191905f5260205f20905b815481529060010190602001808311610c3757829003601f168201915b505050600d90930154919250506001600160a01b0381169060ff600160a01b909104168e565b610c826144f1565b5f8281526008602090815260409182902082516101e08101909352805483526001810154909183019060ff166003811115610cbf57610cbf614742565b6003811115610cd057610cd0614742565b8152600282810154602083015260408051808201808352919093019291600385019182845b815481526020019060010190808311610cf55750505091835250506005820154602082015260068201546001600160a01b0381166040830152600160a01b900460ff166060820152600782018054608090920191610d5290614df2565b80601f0160208091040260200160405190810160405280929190818152602001828054610d7e90614df2565b8015610dc95780601f10610da057610100808354040283529160200191610dc9565b820191905f5260205f20905b815481529060010190602001808311610dac57829003601f168201915b505050918352505060088201546001600160a01b0390811660208301526009830154166040820152600a8201546060820152600b8201546080820152600c8201805460a090920191610e1a90614df2565b80601f0160208091040260200160405190810160405280929190818152602001828054610e4690614df2565b8015610e915780601f10610e6857610100808354040283529160200191610e91565b820191905f5260205f20905b815481529060010190602001808311610e7457829003601f168201915b5050509183525050600d91909101546001600160a01b038082166020840152600160a01b90910460ff16151560409092019190915260a0820151919250839116610ef15760405163cd6f4a4f60e01b815260040161098891815260200190565b50919050565b5f610f006139f0565b805490915060ff600160401b82041615906001600160401b03165f81158015610f265750825b90505f826001600160401b03166001148015610f415750303b155b905081158015610f4f575080155b15610f6d5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610f9757845460ff60401b1916600160401b1785555b610fa033613a1a565b610fa987612718565b610fb28b6137da565b610fbb8a6129e2565b610fc48961092e565b610fcd88610a16565b610fd686613933565b604080516101e081018252620186a080825261c3506020808401829052612710948401859052603260608501819052620493e060808601819052620f424060a0870181905261138860c088018190525f60e089018190526105dc6101008a015261012089018190526109c46101408a018190526101608a018390526101808a01526101a089018190526101c090980197909752601895909555601993909355601a95909555601b94909455601c55601d55601e55601f80546001600160f01b03191669027104e202710000017760a21b179055805467ffffffffffffffff191690556110c061197a565b6001600160a01b03168c6001600160a01b0316146110e1576110e18c612d29565b831561112757845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050505050565b61113d613901565b5f8181526009602052604090205481906001600160a01b0316611176576040516381c4951960e01b815260040161098891815260200190565b505f818152600960205260409081902080546001600160a01b0319169055517f104eb329a192aef26eddea07c2af5ad2587792e62b37ed4045b6ba59bc5540fc90610ab09083815260200190565b6111cc613901565b6001600160a01b0381165f90815260076020526040902054819060ff16611207576040516321ac7c5f60e01b815260040161098891906146c9565b506001600160a01b0381165f9081526007602052604090819020805460ff19169055517f56070b80bd617fcd2f7a284861edb488830a38f9dedcd77b2cb2f4eac17743e790610ab09083906146c9565b600b6020525f90815260409020805461126f90614df2565b80601f016020809104026020016040519081016040528092919081815260200182805461129b90614df2565b80156112e65780601f106112bd576101008083540402835291602001916112e6565b820191905f5260205f20905b8154815290600101906020018083116112c957829003601f168201915b505050505081565b6112f6613901565b6112ff5f613a2b565b565b611309613901565b6001600160a01b0381161580159061133a57505f828152600a60205260409020546001600160a01b03828116911614155b829061135c576040516381c4951960e01b815260040161098891815260200190565b505f828152600a60205260409081902080546001600160a01b0319166001600160a01b0384161790555182907f53661e3e12f23eea1e322a5352171ad3e4407d1394f869f53bb148c27e00908a906113b59084906146c9565b60405180910390a25050565b5f546001600160a01b031633146113eb5760405163b56831db60e01b815260040160405180910390fd5b5f818152600d602052604090205460ff16600181600681111561141057611410614742565b1461143557816001826040516337e1404160e01b815260040161098893929190614e24565b5f828152600d60205260409020805460ff1916600217905560155461145a9042614e59565b5f838152600e602052604080822092909255905183917fc44405af9078047712501f519e1fb900c2896c62b488336f84529c72ae16e6f191a2815f5160206159ab5f395f51905f52600160026040516113b5929190614e6c565b5f5f6114bf87610c7a565b5f888152600d602052604090205490915060ff1660048160068111156114e7576114e7614742565b148860048390919261150f576040516337e1404160e01b815260040161098893929190614e24565b5050505f888152600e60209081526040918290208251606081018452815481526001820154928101929092526002015491810182905290899042811015611572576040516308f3034360e31b815260048101929092526024820152604401610988565b50505f898152600860205260409020600c0161158f888a83614efa565b505f898152600d60205260409020805460ff191660051790556101c0830151156116f957846115d157604051631eae1a4d60e31b815260040160405180910390fd5b5f80546040516304cd0b0d60e11b8152600481018c90526001600160a01b039091169063099a161a90602401602060405180830381865afa158015611618573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061163c9190614fae565b90508361010001516001600160a01b031663de12c6408a8a604051611662929190614fc5565b6040519081900381206001600160e01b031960e084901b16825261168e9185908c908c90600401614ffc565b602060405180830381865afa1580156116a9573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116cd9190615028565b94508888866116f157604051632f9f8ab960e01b8152600401610988929190615043565b5050506116fe565b600193505b61170789613a9b565b887f3a140076c461ebc41d74833ae0ee8bbc8079a135a63392098cd381e84350b69b8989898960405161173d9493929190615056565b60405180910390a2885f5160206159ab5f395f51905f5260046005604051611766929190614e6c565b60405180910390a250505095945050505050565b5f546001600160a01b031633146117a45760405163b56831db60e01b815260040160405180910390fd5b5f828152600860209081526040808320600d9092529091205460ff1660028160068111156117d4576117d4614742565b146117f957836002826040516337e1404160e01b815260040161098893929190614e24565b5f848152600d6020526040808220805460ff19166003179055600a84018590555185917f11df18edb9bc9cd90a79068e0e208b630202148643d797d6150e7bacb733e63c91a2835f5160206159ab5f395f51905f5260026003604051611860929190614e6c565b60405180910390a250505050565b611876613901565b806118b25760405162461bcd60e51b815260206004820152600c60248201526b456d70747920706172616d7360a01b6044820152606401610988565b60ff83165f908152600b6020526040902080546118ce90614df2565b15905061191d5760405162461bcd60e51b815260206004820152601b60248201527f506172616d53657420616c7265616479207265676973746572656400000000006044820152606401610988565b60ff83165f908152600b60205260409020611939828483614efa565b507f6e4a4ea7f38fc775e616080b155744337e6216848e886a69c918b4ab84da219583838360405161196d93929190615087565b60405180910390a1505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6119c960405180606001604052805f81526020015f81526020015f81525090565b5060408051606081018252601554815260165460208201526017549181019190915290565b5f818152600d602052604090205460ff166006816006811115611a1357611a13614742565b148290611a3657604051637cb2d48360e11b815260040161098891815260200190565b505f828152600c60205260409020548281611a67576040516345ba89d560e11b815260040161098891815260200190565b505f838152600c60205260408120819055611a8184613f07565b5f858152601160205260409020546002549192506001600160a01b0390811691611aae9183911685613ff4565b60025460405163da19b69760e01b81526001600160a01b039091169063da19b69790611ae49088908790879087906004016150ef565b5f604051808303815f87803b158015611afb575f5ffd5b505af1158015611b0d573d5f5f3e3d5ffd5b50505050847f5297818f48a66292b8b3e2caab83eec531b669bb20807fd38cf006adb2a07317848451604051611b4d929190918252602082015260400190565b60405180910390a25050505050565b611b7d60405180606001604052805f81526020015f81526020015f81525090565b505f908152600e6020908152604091829020825160608101845281548152600182015492810192909252600201549181019190915290565b5f818152600d6020526040812054819060ff16611bd28482614051565b9250925050915091565b5f5f611be787610c7a565b5f888152600d602052604090205490915060ff166003816006811115611c0f57611c0f614742565b1488600383909192611c37576040516337e1404160e01b815260040161098893929190614e24565b5050505f888152600e6020908152604091829020825160608101845281548152600182015492810183905260029091015492810192909252899042811015611c9b576040516308f3034360e31b815260048101929092526024820152604401610988565b5050606083015160200151899042811115611cd25760405163017e35e560e71b815260048101929092526024820152604401610988565b5050610160830151899015611cfd57604051637eb9cea960e11b815260040161098891815260200190565b505f8888604051611d0f929190614fc5565b60408051918290039091205f8c815260086020908152838220600b01839055600d905291909120805460ff19166004179055601754909150611d519042614e59565b5f8b8152600e6020526040908190206002019190915560a08501519051632f0e1bbf60e01b81526001600160a01b0390911690632f0e1bbf90611d9e908d9085908c908c90600401614ffc565b6020604051808303815f875af1158015611dba573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dde9190615028565b9450888886611e0257604051632f9f8ab960e01b8152600401610988929190615043565b5050897f7cc27e4a5626cbc4f8ba1a927b0448de55e6a114bc87660331270c5109ade0718a8a604051611e36929190615043565b60405180910390a2895f5160206159ab5f395f51905f5260036004604051611e5f929190614e6c565b60405180910390a25050505095945050505050565b5f80600b81611e8960a0860160808701614a43565b60ff1660ff1681526020019081526020015f208054611ea790614df2565b905011611ec65760405162461bcd60e51b81526004016109889061513a565b5f601281611ed76020860186615171565b6003811115611ee857611ee8614742565b6003811115611ef957611ef9614742565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411611f20579050505050505090505f81600160028110611f7757611f77615126565b602002015163ffffffff1611835f016020810190611f959190615171565b90611fb45760405163286c068d60e11b8152600401610988919061518a565b506020808201518251604080516101e081018252601854815260195481860152601a5491810191909152601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e0830152600160a01b810461ffff908116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152925463ffffffff8181166101a0860181905264010000000090920481166101c0860152928316939190921691156120df576101a081015163ffffffff16846001602002015163ffffffff161015865f0160208101906120be9190615171565b906120dd5760405163010b971d60e31b8152600401610988919061518a565b505b6101c081015163ffffffff161561212e576101c081015184519063ffffffff908116908216101561212c57604051630a4b6b6360e11b815263ffffffff9091166004820152602401610988565b505b6040860135602087013581101561215b5760405163174b5a0760e21b815260040161098891815260200190565b506101808101516017545f91612710916121799161ffff1690615198565b61218391906151af565b61271061ffff1683610160015161ffff166015600101546121a49190615198565b6121ae91906151af565b61271061ffff1684610140015161ffff1660155f01546121ce9190615198565b6121d891906151af565b5f5460408051634f87c3a560e11b8152815160208e81013594938f0135936001600160a01b031692639f0f874a92600480830193928290030181865afa158015612224573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122489190614fae565b6122529190614e59565b61225c91906151ce565b6122669190614e59565b6122709190614e59565b61227a9190614e59565b90505f6122886001866151ce565b612293906004615198565b61229e90600e614e59565b90505f85845f01516122b09190615198565b9050818685602001516122c39190615198565b6122cd9190615198565b6122d79082614e59565b9050600186111561231f5760026122ef6001886151ce565b6122f99088615198565b85604001516123089190615198565b61231291906151af565b61231c9082614e59565b90505b81868560c001516123309190615198565b61233a9190615198565b6123449082614e59565b9050828685606001516123579190615198565b6123619190615198565b61236b9082614e59565b905084846080015161237d9190615198565b6123879082614e59565b905060018511156123cf57600261239f6001876151ce565b6123a99087615198565b85604001516123b89190615198565b6123c291906151af565b6123cc9082614e59565b90505b60a08401516123de9082614e59565b610100850151909150612710906123f99061ffff1682614e59565b6124039083615198565b61240d91906151af565b9750878061243157604051638c4fcd9360e01b815260040161098891815260200190565b5050505050505050919050565b5f546001600160a01b031633148061246057506003546001600160a01b031633145b61247d57604051639e75a8b560e01b815260040160405180910390fd5b5f8160ff161180156124935750600d60ff821611155b6124d85760405162461bcd60e51b815260206004820152601660248201527524b73b30b634b2103330b4b63ab932903932b0b9b7b760511b6044820152606401610988565b6124f6828260ff16600d8111156124f1576124f1614742565b6141d9565b5050565b612502613901565b6001600160a01b0381166125585760405162461bcd60e51b815260206004820152601f60248201527f496e76616c696420536c617368696e674d616e616765722061646472657373006044820152606401610988565b600380546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b6003546001600160a01b031633146125cc576040516357d6948d60e11b815260040160405180910390fd5b60025460405163c1ab0f1f60e01b815260048101849052602481018390526001600160a01b039091169063c1ab0f1f906044015f604051808303815f87803b158015612616575f5ffd5b505af1158015612628573d5f5f3e3d5ffd5b50505050817f4f41a3b0a032ebcae925f2ace77d507435840ca4b2dbaffdd7723fa8d72ee542826040516113b591815260200190565b612666613901565b6001600160a01b0381161580159061269757505f828152600960205260409020546001600160a01b03828116911614155b82906126b9576040516381c4951960e01b815260040161098891815260200190565b505f8281526009602090815260409182902080546001600160a01b0319166001600160a01b03851617905590518381527ff4041a3f914dac3bc9bf5f003ba41f28dbb84abe42f4e07c76266f5c8ceecb69910160405180910390a15050565b612720613901565b60058190556040518181527fba0716ba1ee2ea8ecc4c64119b4537cdb42a99d82acf92af5b87607b8b52355290602001610ab0565b61275d613901565b612710612772610120830161010084016151fb565b61ffff16111561278a610120830161010084016151fb565b906127af576040516301027fc160e21b815261ffff9091166004820152602401610988565b506127106127c5610140830161012084016151fb565b61ffff1611156127dd610140830161012084016151fb565b90612802576040516301027fc160e21b815261ffff9091166004820152602401610988565b50612710612818610160830161014084016151fb565b61ffff161115612830610160830161014084016151fb565b9061285557604051633239953960e01b815261ffff9091166004820152602401610988565b5061271061286b610180830161016084016151fb565b61ffff161115612883610180830161016084016151fb565b906128a857604051633239953960e01b815261ffff9091166004820152602401610988565b506127106128be6101a0830161018084016151fb565b61ffff1611156128d66101a0830161018084016151fb565b906128fb57604051633239953960e01b815261ffff9091166004820152602401610988565b5061290e610140820161012083016151fb565b61ffff16158061293757505f61292b610100830160e08401614683565b6001600160a01b031614155b6129545760405163015f92ff60e51b815260040160405180910390fd5b6129666101e082016101c08301615232565b63ffffffff1661297e6101c083016101a08401615232565b63ffffffff1610156129a3576040516392f55c6560e01b815260040160405180910390fd5b8060186129b08282615271565b9050507fbf3951313e980027eb48ce363fdb707286195ec6a0f802ac153927cf929c3fc681604051610ab0919061542f565b6129ea613901565b6001600160a01b03811615801590612a1057506001546001600160a01b03828116911614155b8190612a30576040516320252f0b60e01b815260040161098891906146c9565b50600180546001600160a01b0319166001600160a01b0383161790556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790610ab09083906146c9565b612a84613901565b612a916020820182615232565b63ffffffff16612aa76040830160208401615232565b63ffffffff1610158015612acc57505f612ac46020830183615232565b63ffffffff16115b612ae957604051634564ab9b60e01b815260040160405180910390fd5b604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a08401819052640100000000909204166101c083015215612c22576101a081015163ffffffff16612bd26040840160208501615232565b63ffffffff161015612bea6040840160208501615232565b826101a001519091612c1f57604051633ccc4c2160e21b815263ffffffff928316600482015291166024820152604401610988565b50505b6101c081015163ffffffff1615612c99576101c081015163ffffffff16612c4c6020840184615232565b63ffffffff161015612c616020840184615232565b826101c001519091612c965760405163156c4e5b60e11b815263ffffffff928316600482015291166024820152604401610988565b50505b8160125f856003811115612caf57612caf614742565b6003811115612cc057612cc0614742565b815260208101919091526040015f20612cda91600261456e565b50826003811115612ced57612ced614742565b7f8b56fae526eee054f0849759a99fc7d4ff3823824ebf097a56f7d78adb6b34fa83604051612d1c9190615539565b60405180910390a2505050565b612d31613901565b6001600160a01b038116612d5a575f604051631e4fbdf760e01b815260040161098891906146c9565b610acc81613a2b565b5f612d6c6144f1565b5f601281612d7d6020870187615171565b6003811115612d8e57612d8e614742565b6003811115612d9f57612d9f614742565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411612dc6579050505050505090505f81600160028110612e1d57612e1d615126565b602002015163ffffffff1611845f016020810190612e3b9190615171565b90612e5a5760405163286c068d60e11b8152600401610988919061518a565b50602084013542811015612e8457604051630b99e87960e01b815260040161098891815260200190565b5060408401356020850135811015612eb25760405163174b5a0760e21b815260040161098891815260200190565b506017546016545f9190612eca4260408901356151ce565b612ed49190614e59565b612ede9190614e59565b905060055481108190612f07576040516313b783af60e21b815260040161098891815260200190565b5060075f612f1b6080880160608901614683565b6001600160a01b0316815260208101919091526040015f205460ff16612f476080870160608801614683565b90612f665760405163295a6a6f60e11b815260040161098891906146c9565b505f612f7186611e74565b60068054965090915085905f612f8683615579565b9091555050604080514460208201529081018690525f9060600160408051601f1981840301815291815281516020928301205f898152600c84528281208690556004546011855283822080546001600160a01b03199081166001600160a01b0393841617909155601f805460138852868520805461ffff191661ffff600160b01b909304929092169190911790555460148752858420805483169190931617909155600d8552838220805460ff1916600117905560109094528290208054339416939093179092556016549192506130619190890135614e59565b5f878152600e602090815260409091206001019190915581865261308790880188615171565b8560200190600381111561309d5761309d614742565b908160038111156130b0576130b0614742565b905250436040808701919091528051808201825290602089019060029083908390808284375f9201919091525050506060808701919091526130f89060808901908901614683565b6001600160a01b031660a08087019190915261311990880160808901614a43565b60ff1660c08087019190915261313190880188615591565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050505060e08087019190915261317e9061010089019089016155d3565b15156101c08601525f610140860181905261016086018190526040805160208101909152818152610180870152336101a0870152600b816131c560a08b0160808c01614a43565b60ff1660ff1681526020019081526020015f2080546131e390614df2565b80601f016020809104026020016040519081016040528092919081815260200182805461320f90614df2565b801561325a5780601f106132315761010080835404028352916020019161325a565b820191905f5260205f20905b81548152906001019060200180831161323d57829003601f168201915b505050505090505f8151116132815760405162461bcd60e51b81526004016109889061513a565b5f61329260808a0160608b01614683565b6001600160a01b031663fefd9a8b8985856132b060a08f018f615591565b8f8060c001906132c09190615591565b6040518863ffffffff1660e01b81526004016132e297969594939291906155ee565b6020604051808303815f875af11580156132fe573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906133229190614fae565b5f818152600960205260409020549091506001600160a01b0316818161335e576040516381c4951960e01b815260040161098891815260200190565b505f828152600a60205260409020546001600160a01b03168281613398576040516381c4951960e01b815260040161098891815260200190565b50608089018390526001600160a01b038083166101008b015281166101208a01525f8a81526008602090815260409091208a518155908a0151600180830180548d94939260ff1991909116908360038111156133f6576133f6614742565b02179055506040820151816002015560608201518160030190600261341c92919061460f565b506080820151600582015560a082015160068201805460c085015160ff16600160a01b026001600160a81b03199091166001600160a01b039093169290921791909117905560e082015160078201906134759082615642565b506101008201516008820180546001600160a01b039283166001600160a01b031991821617909155610120840151600984018054919093169116179055610140820151600a820155610160820151600b820155610180820151600c8201906134dd9082615642565b506101a0820151600d90910180546101c0909301511515600160a01b026001600160a81b03199093166001600160a01b0392831617929092179091556004546135299116333089614334565b5f5460405163291a691b60e01b81526001600160a01b039091169063291a691b9061355c908d9089908d906004016156f7565b6020604051808303815f875af1158015613578573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061359c9190615028565b6135b957604051630d8dbe2560e01b815260040160405180910390fd5b6135c960808c0160608d01614683565b6001600160a01b03167f5090c9764b5cd13df7afc0013f733dfbe6eaf1b6ddc22a5e291fa387efd4c15e8b8b604051613603929190614dd2565b60405180910390a2895f5160206159ab5f395f51905f525f600160405161362b929190614e6c565b60405180910390a25050505050505050915091565b5f818152600d602052604081205460ff168181600681111561366457613664614742565b0361368957826001826040516337e1404160e01b815260040161098893929190614e24565b600581600681111561369d5761369d614742565b036136be5760405163462c7bed60e01b815260048101849052602401610988565b60068160068111156136d2576136d2614742565b036136f357604051633de16e3560e11b815260048101849052602401610988565b5f6136fe8483614051565b935090508061372357604051639f65d93560e01b815260048101859052602401610988565b5f848152600d6020526040902080546006919060ff191660018302179055505f848152600f60205260409020805484919060ff1916600183600d81111561376c5761376c614742565b0217905550835f5160206159ab5f395f51905f52836006604051613791929190614e6c565b60405180910390a2837fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb83856040516137cb92919061573c565b60405180910390a25050919050565b6137e2613901565b6001600160a01b0381161580159061380757505f546001600160a01b03828116911614155b8190613827576040516375ac4eb760e11b815260040161098891906146c9565b505f80546001600160a01b0319166001600160a01b0383161790556040517f80052b810d39120cf6c976cca504a21703f585521dc7a41c6d241090e6c579b690610ab09083906146c9565b6001600160a01b0381165f90815260076020526040902054819060ff16156138ae5760405163b29d459560e01b815260040161098891906146c9565b506001600160a01b0381165f9081526007602052604090819020805460ff19166001179055517fb8d368517268f297fff00825d67d098763117d061360d31027be5b2e1a59d46790610ab09083906146c9565b3361390a61197a565b6001600160a01b0316146112ff573360405163118cdaa760e01b815260040161098891906146c9565b80356139525760405163055f269d60e01b815260040160405180910390fd5b5f8160200135116139765760405163055f269d60e01b815260040160405180910390fd5b5f81604001351161399a5760405163055f269d60e01b815260040160405180910390fd5b80356015819055602080830135601681905560408085013560178190558151948552928401919091528201527f7e86ba16b805e2835af5c5b7aa5a942ced8bcc1fb95a05fbe42dae3862350a1690606001610ab0565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005b92915050565b613a22614373565b610acc81614398565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b5f805460405162beb08960e51b8152600481018490526001600160a01b03909116906317d61120906024015f60405180830381865afa158015613ae0573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613b079190810190615813565b5080515f848152600c60209081526040808320805490849055601190925282205493945091926001600160a01b031690829003613ba6576002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613b72908890889086906004016158d8565b5f604051808303815f87803b158015613b89575f5ffd5b505af1158015613b9b573d5f5f3e3d5ffd5b505050505050505050565b825f03613c47575f858152601060205260409020546001600160a01b03168015613bde57613bde6001600160a01b0383168285613ff4565b6002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613c12908990899087906004016158d8565b5f604051808303815f87803b158015613c29575f5ffd5b505af1158015613c3b573d5f5f3e3d5ffd5b50505050505050505050565b5f85815260136020908152604080832054601490925282205461ffff909116906001600160a01b03168115801590613c8757506001600160a01b03811615155b15613cc357612710613c9d61ffff841687615198565b613ca791906151af565b92508215613cc357613cc36001600160a01b0385168285613ff4565b5f613cce84876151ce565b90505f876001600160401b03811115613ce957613ce9614e87565b604051908082528060200260200182016040528015613d12578160200160208202803683370190505b5090505f613d2089846151af565b90505f805b8a811015613d5f5782848281518110613d4057613d40615126565b6020908102919091010152613d558383614e59565b9150600101613d25565b505f613d6b82866151ce565b90508015613da8578084613d8060018e6151ce565b81518110613d9057613d90615126565b60200260200101818151613da49190614e59565b9052505b600154613dc2906001600160a01b038b81169116876143a0565b60015f9054906101000a90046001600160a01b03166001600160a01b031663dd8c818e8a8e876040518463ffffffff1660e01b8152600401613e0693929190615938565b5f604051808303815f87803b158015613e1d575f5ffd5b505af1158015613e2f573d5f5f3e3d5ffd5b5050600154613e4d92506001600160a01b038c81169250165f6143a0565b8c7fac9fe8ad7f55eac03284399116ecafc104f10459773f4cdf47063c46e5be335a8d86604051613e7f92919061596d565b60405180910390a260025f9054906101000a90046001600160a01b03166001600160a01b03166341489f158e8e8c6040518463ffffffff1660e01b8152600401613ecb939291906158d8565b5f604051808303815f87803b158015613ee2575f5ffd5b505af1158015613ef4573d5f5f3e3d5ffd5b5050505050505050505050505050505050565b5f818152600f602052604090205460609060ff16600181600d811115613f2f57613f2f614742565b1480613f4c5750600281600d811115613f4a57613f4a614742565b145b15613f84575f5b604051908082528060200260200182016040528015613f7c578160200160208202803683370190505b509392505050565b5f5460405162beb08960e51b8152600481018590526001600160a01b03909116906317d61120906024015f60405180830381865afa925050508015613fea57506040513d5f823e601f3d908101601f19168201604052613fe79190810190615813565b60015b613f7c575f613f53565b61404c83846001600160a01b031663a9059cbb858560405160240161401a929190615991565b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505061442c565b505050565b5f828152600e60209081526040808320815160608101835281548152600182015493810193909352600201548282015282549051632800d82960e01b81526004810186905283929183916001600160a01b0390911690632800d82990602401602060405180830381865afa1580156140cb573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906140ef9190614fae565b9050600185600681111561410557614105614742565b14801561411157508042115b15614124576001809350935050506141d2565b600285600681111561413857614138614742565b1480156141455750815142115b1561415957600160039350935050506141d2565b600385600681111561416d5761416d614742565b14801561417d5750816020015142115b1561419157600160069350935050506141d2565b60048560068111156141a5576141a5614742565b1480156141b55750816040015142115b156141c9576001600a9350935050506141d2565b5f5f9350935050505b9250929050565b5f828152600d602052604081205460ff16908160068111156141fd576141fd614742565b0361422257826001826040516337e1404160e01b815260040161098893929190614e24565b600581600681111561423657614236614742565b036142575760405163462c7bed60e01b815260048101849052602401610988565b600681600681111561426b5761426b614742565b0361428c57604051633de16e3560e11b815260048101849052602401610988565b5f838152600d6020526040902080546006919060ff191660018302179055505f838152600f60205260409020805483919060ff1916600183600d8111156142d5576142d5614742565b0217905550825f5160206159ab5f395f51905f528260066040516142fa929190614e6c565b60405180910390a2827fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb8284604051612d1c92919061573c565b6040516001600160a01b03848116602483015283811660448301526064820183905261436d9186918216906323b872dd9060840161401a565b50505050565b61437b61448f565b6112ff57604051631afcd79f60e31b815260040160405180910390fd5b612d31614373565b5f836001600160a01b031663095ea7b384846040516024016143c3929190615991565b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505090506143fc84826144a8565b61436d5761442684856001600160a01b031663095ea7b3865f60405160240161401a929190615991565b61436d84825b5f5f60205f8451602086015f885af18061444b576040513d5f823e3d81fd5b50505f513d9150811561446257806001141561446f565b6001600160a01b0384163b155b1561436d5783604051635274afe760e01b815260040161098891906146c9565b5f6144986139f0565b54600160401b900460ff16919050565b5f5f5f5f60205f8651602088015f8a5af192503d91505f5190508280156144e7575081156144d957806001146144e7565b5f866001600160a01b03163b115b9695505050505050565b604080516101e081019091525f808252602082019081526020015f815260200161451961463d565b81525f602082018190526040820181905260608083018290526080830181905260a0830182905260c0830182905260e08301829052610100830182905261012083015261014082018190526101609091015290565b6001830191839082156145ff579160200282015f5b838211156145cd57833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302614583565b80156145fd5782816101000a81549063ffffffff02191690556004016020816003010492830192600103026145cd565b505b5061460b92915061465b565b5090565b82600281019282156145ff579160200282015b828111156145ff578251825591602001919060010190614622565b60405180604001604052806002906020820280368337509192915050565b5b8082111561460b575f815560010161465c565b6001600160a01b0381168114610acc575f5ffd5b5f60208284031215614693575f5ffd5b813561469e8161466f565b9392505050565b5f602082840312156146b5575f5ffd5b5035919050565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b8035600481106146eb575f5ffd5b919050565b5f5f60408385031215614701575f5ffd5b61470a836146dd565b946020939093013593505050565b5f60608284031215610ef1575f5ffd5b5f60608284031215614738575f5ffd5b61469e8383614718565b634e487b7160e01b5f52602160045260245ffd5b600e811061476657614766614742565b9052565b60208101613a148284614756565b6004811061476657614766614742565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b8e81526147c6602082018f614778565b8c60408201528b60608201526147df608082018c6146bc565b60ff8a1660a08201526101c060c08201525f6147ff6101c083018b614788565b61480c60e084018b6146bc565b61481a61010084018a6146bc565b876101208401528661014084015282810361016084015261483b8187614788565b91505061484c6101808301856146bc565b8215156101a08301529f9e505050505050505050505050505050565b805f5b600281101561436d57815184526020938401939091019060010161486b565b805182525f60208201516148a16020850182614778565b506040820151604084015260608201516148be6060850182614868565b50608082015160a084015260a08201516148db60c08501826146bc565b5060c082015160ff811660e08501525060e0820151610200610100850152614907610200850182614788565b905061010083015161491d6101208601826146bc565b506101208301516149326101408601826146bc565b506101408301516101608501526101608301516101808501526101808301518482036101a08601526149648282614788565b9150506101a083015161497b6101c08601826146bc565b506101c08301518015156101e0860152613f7c565b602081525f61469e602083018461488a565b80356146eb8161466f565b5f5f5f5f5f5f5f610120888a0312156149c4575f5ffd5b87356149cf8161466f565b965060208801356149df8161466f565b955060408801356149ef8161466f565b945060608801356149ff8161466f565b93506080880135614a0f8161466f565b925060a08801359150614a258960c08a01614718565b905092959891949750929550565b803560ff811681146146eb575f5ffd5b5f60208284031215614a53575f5ffd5b61469e82614a33565b602081525f61469e6020830184614788565b5f5f60408385031215614a7f575f5ffd5b823591506020830135614a918161466f565b809150509250929050565b5f5f83601f840112614aac575f5ffd5b5081356001600160401b03811115614ac2575f5ffd5b6020830191508360208285010111156141d2575f5ffd5b5f5f5f5f5f60608688031215614aed575f5ffd5b8535945060208601356001600160401b03811115614b09575f5ffd5b614b1588828901614a9c565b90955093505060408601356001600160401b03811115614b33575f5ffd5b614b3f88828901614a9c565b969995985093965092949392505050565b5f5f60408385031215614b61575f5ffd5b50508035926020909101359150565b5f5f5f60408486031215614b82575f5ffd5b614b8b84614a33565b925060208401356001600160401b03811115614ba5575f5ffd5b614bb186828701614a9c565b9497909650939450505050565b81518152602080830151908201526040808301519082015260608101613a14565b82151581526040810161469e6020830184614756565b5f60208284031215614c05575f5ffd5b81356001600160401b03811115614c1a575f5ffd5b8201610100818503121561469e575f5ffd5b5f6101e082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e0830151614c8660e08401826146bc565b50610100830151614c9e61010084018261ffff169052565b50610120830151614cb661012084018261ffff169052565b50610140830151614cce61014084018261ffff169052565b50610160830151614ce661016084018261ffff169052565b50610180830151614cfe61018084018261ffff169052565b506101a0830151614d186101a084018263ffffffff169052565b506101c0830151614d326101c084018263ffffffff169052565b5092915050565b5f5f60408385031215614d4a575f5ffd5b82359150614d5a60208401614a33565b90509250929050565b6007811061476657614766614742565b60208101613a148284614d63565b5f6101e0828403128015614d93575f5ffd5b509092915050565b5f5f60608385031215614dac575f5ffd5b614db5836146dd565b915083606084011115614dc6575f5ffd5b50926020919091019150565b828152604060208201525f614dea604083018461488a565b949350505050565b600181811c90821680614e0657607f821691505b602082108103610ef157634e487b7160e01b5f52602260045260245ffd5b83815260608101614e386020830185614d63565b614dea6040830184614d63565b634e487b7160e01b5f52601160045260245ffd5b80820180821115613a1457613a14614e45565b60408101614e7a8285614d63565b61469e6020830184614d63565b634e487b7160e01b5f52604160045260245ffd5b601f82111561404c57805f5260205f20601f840160051c81016020851015614ec05750805b601f840160051c820191505b81811015614edf575f8155600101614ecc565b5050505050565b5f19600383901b1c191660019190911b1790565b6001600160401b03831115614f1157614f11614e87565b614f2583614f1f8354614df2565b83614e9b565b5f601f841160018114614f51575f8515614f3f5750838201355b614f498682614ee6565b845550614edf565b5f83815260208120601f198716915b82811015614f805786850135825560209485019460019092019101614f60565b5086821015614f9c575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b5f60208284031215614fbe575f5ffd5b5051919050565b818382375f9101908152919050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b848152836020820152606060408201525f6144e7606083018486614fd4565b8015158114610acc575f5ffd5b5f60208284031215615038575f5ffd5b815161469e8161501b565b602081525f614dea602083018486614fd4565b604081525f615069604083018688614fd4565b828103602084015261507c818587614fd4565b979650505050505050565b60ff84168152604060208201525f6150a3604083018486614fd4565b95945050505050565b5f8151808452602084019350602083015f5b828110156150e55781516001600160a01b03168652602095860195909101906001016150be565b5093949350505050565b848152836020820152608060408201525f61510d60808301856150ac565b905060018060a01b038316606083015295945050505050565b634e487b7160e01b5f52603260045260245ffd5b6020808252601c908201527f42465620706172616d20736574206e6f74207265676973746572656400000000604082015260600190565b5f60208284031215615181575f5ffd5b61469e826146dd565b60208101613a148284614778565b8082028115828204841417613a1457613a14614e45565b5f826151c957634e487b7160e01b5f52601260045260245ffd5b500490565b81810381811115613a1457613a14614e45565b61ffff81168114610acc575f5ffd5b80356146eb816151e1565b5f6020828403121561520b575f5ffd5b813561469e816151e1565b63ffffffff81168114610acc575f5ffd5b80356146eb81615216565b5f60208284031215615242575f5ffd5b813561469e81615216565b5f8135613a148161466f565b5f8135613a14816151e1565b5f8135613a1481615216565b813581556020820135600182015560408201356002820155606082013560038201556080820135600482015560a0820135600582015560c08201356006820155600781016152e16152c460e0850161524d565b82546001600160a01b0319166001600160a01b0391909116178255565b6153116152f16101008501615259565b82805461ffff60a01b191660a09290921b61ffff60a01b16919091179055565b6153416153216101208501615259565b82805461ffff60b01b191660b09290921b61ffff60b01b16919091179055565b6153716153516101408501615259565b82805461ffff60c01b191660c09290921b61ffff60c01b16919091179055565b6153a16153816101608501615259565b82805461ffff60d01b191660d09290921b61ffff60d01b16919091179055565b6153d16153b16101808501615259565b82805461ffff60e01b191660e09290921b61ffff60e01b16919091179055565b50600881016153fd6153e66101a08501615265565b825463ffffffff191663ffffffff91909116178255565b61404c61540d6101c08501615265565b825467ffffffff00000000191660209190911b67ffffffff0000000016178255565b813581526020808301359082015260408083013590820152606080830135908201526080808301359082015260a0808301359082015260c080830135908201526101e0810161548060e084016149a2565b61548d60e08401826146bc565b5061549b61010084016151f0565b61ffff166101008301526154b261012084016151f0565b61ffff166101208301526154c961014084016151f0565b61ffff166101408301526154e061016084016151f0565b61ffff166101608301526154f761018084016151f0565b61ffff1661018083015261550e6101a08401615227565b63ffffffff166101a08301526155276101c08401615227565b63ffffffff81166101c0840152614d32565b6040810181835f5b600281101561557057813561555581615216565b63ffffffff1683526020928301929190910190600101615541565b50505092915050565b5f6001820161558a5761558a614e45565b5060010190565b5f5f8335601e198436030181126155a6575f5ffd5b8301803591506001600160401b038211156155bf575f5ffd5b6020019150368190038213156141d2575f5ffd5b5f602082840312156155e3575f5ffd5b813561469e8161501b565b87815286602082015260a060408201525f61560c60a0830188614788565b828103606084015261561f818789614fd4565b90508281036080840152615634818587614fd4565b9a9950505050505050505050565b81516001600160401b0381111561565b5761565b614e87565b61566f816156698454614df2565b84614e9b565b6020601f82116001811461569c575f831561568a5750848201515b6156948482614ee6565b855550614edf565b5f84815260208120601f198516915b828110156156cb57878501518255602094850194600190920191016156ab565b50848210156156e857868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b838152602081018390526080810160408201835f5b600281101561573157815163ffffffff1683526020928301929091019060010161570c565b505050949350505050565b6040810161574a8285614d63565b61469e6020830184614756565b604051601f8201601f191681016001600160401b038111828210171561577f5761577f614e87565b604052919050565b5f6001600160401b0382111561579f5761579f614e87565b5060051b60200190565b5f82601f8301126157b8575f5ffd5b81516157cb6157c682615787565b615757565b8082825260208201915060208360051b8601019250858311156157ec575f5ffd5b602085015b838110156158095780518352602092830192016157f1565b5095945050505050565b5f5f60408385031215615824575f5ffd5b82516001600160401b03811115615839575f5ffd5b8301601f81018513615849575f5ffd5b80516158576157c682615787565b8082825260208201915060208360051b850101925087831115615878575f5ffd5b6020840193505b828410156158a35783516158928161466f565b82526020938401939091019061587f565b8095505050505060208301516001600160401b038111156158c2575f5ffd5b6158ce858286016157a9565b9150509250929050565b838152606060208201525f6158f060608301856150ac565b905060018060a01b0383166040830152949350505050565b5f8151808452602084019350602083015f5b828110156150e557815186526020958601959091019060010161591a565b6001600160a01b03841681526060602082018190525f9061595b908301856150ac565b82810360408401526144e78185615908565b604081525f61597f60408301856150ac565b82810360208401526150a38185615908565b6001600160a01b0392909216825260208201526040019056fe1b418a230a21d37a078bf8f16decbde8ccceacd77159371f62f0d4ea00d19967a164736f6c634300081c000a", "linkReferences": {}, "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/Enclave.sol", - "buildInfoId": "solc-0_8_28-2705a75bc2d2d1f8b1e08ebca4cc37d76480abc8" + "buildInfoId": "solc-0_8_28-e33d1cfedc69d8ad74eec5a84c8ef358a020ec1a" } \ 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 694687054..8b843cf8d 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-2705a75bc2d2d1f8b1e08ebca4cc37d76480abc8" + "buildInfoId": "solc-0_8_28-e33d1cfedc69d8ad74eec5a84c8ef358a020ec1a" } \ 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 c2def89b1..59d3b845d 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json @@ -616,6 +616,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + } + ], + "name": "getCommitteeHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "committeeHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -781,11 +800,6 @@ "name": "e3Id", "type": "uint256" }, - { - "internalType": "address[]", - "name": "nodes", - "type": "address[]" - }, { "internalType": "bytes", "name": "publicKey", @@ -971,5 +985,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ICiphernodeRegistry.sol", - "buildInfoId": "solc-0_8_28-2705a75bc2d2d1f8b1e08ebca4cc37d76480abc8" + "buildInfoId": "solc-0_8_28-e33d1cfedc69d8ad74eec5a84c8ef358a020ec1a" } \ 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 83e49e9f6..28718e065 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-2705a75bc2d2d1f8b1e08ebca4cc37d76480abc8" + "buildInfoId": "solc-0_8_28-e33d1cfedc69d8ad74eec5a84c8ef358a020ec1a" } \ 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 9e5c214eb..bfc1587e1 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-2705a75bc2d2d1f8b1e08ebca4cc37d76480abc8" + "buildInfoId": "solc-0_8_28-e33d1cfedc69d8ad74eec5a84c8ef358a020ec1a" } \ 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 208465867..5122e33e1 100644 --- a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json +++ b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json @@ -806,6 +806,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + } + ], + "name": "getCommitteeHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "committeeHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -1034,11 +1053,6 @@ "name": "e3Id", "type": "uint256" }, - { - "internalType": "address[]", - "name": "nodes", - "type": "address[]" - }, { "internalType": "bytes", "name": "publicKey", @@ -1283,30 +1297,30 @@ "type": "function" } ], - "bytecode": "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b613db5806100d65f395ff3fe608060405234801561000f575f5ffd5b506004361061023f575f3560e01c80639a7a2ffc11610135578063dbb06c93116100b4578063f165053611610079578063f165053614610578578063f2fde38b14610592578063f379b0df146105a5578063f52fd803146105df578063f6fc05d514610651575f5ffd5b8063dbb06c9314610525578063e59e469514610537578063e6745e131461054a578063e82f3b701461055d578063ebf0c71714610570575f5ffd5b8063c2b40ae4116100fa578063c2b40ae4146104b0578063c3a0ec30146104cf578063ca2869a0146104e0578063cd6dc687146104ff578063da881e5a14610512575f5ffd5b80639a7a2ffc146104255780639f0f874a14610461578063a01649301461046a578063a8a4d69b1461048a578063bff232c11461049d575f5ffd5b806370e36bbe116101c15780638cb89ecb116101865780638cb89ecb146103c25780638d1ddfb1146103e15780638da5cb5b146103f75780638e5ce3ad146103ff5780639015d37114610412575f5ffd5b806370e36bbe14610347578063715018a61461035a5780637c92f52414610362578063858142431461038f5780638a78bb15146103af575f5ffd5b8063291a691b11610207578063291a691b146102c95780632e7b716d146102ec5780634d6861a6146102ff57806350e6d94c146103125780635d50477614610334575f5ffd5b8063096b810a146102435780630f3e34121461025857806317d611201461026b5780631cc321b5146102955780632800d829146102a8575b5f5ffd5b610256610251366004613303565b61065a565b005b61025661026636600461331e565b6107a6565b61027e61027936600461331e565b6107e9565b60405161028c9291906133a8565b60405180910390f35b6102566102a3366004613419565b610993565b6102bb6102b636600461331e565b610cb7565b60405190815260200161028c565b6102dc6102d73660046134f8565b610d03565b604051901515815260200161028c565b6102dc6102fa366004613303565b610edd565b6102dc61030d36600461331e565b610f90565b6102dc610320366004613303565b60066020525f908152604090205460ff1681565b6102dc610342366004613531565b610fcf565b610256610355366004613303565b611013565b610256611089565b61037561037036600461355f565b61109c565b6040805192835263ffffffff90911660208301520161028c565b6001546103a2906001600160a01b031681565b60405161028c9190613594565b6102566103bd366004613303565b611243565b6102bb6103d036600461331e565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102bb565b6103a2611381565b600b546103a2906001600160a01b031681565b6102dc610420366004613303565b6113af565b61044b610433366004613303565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff909116815260200161028c565b6102bb60035481565b61047d61047836600461331e565b6113cc565b60405161028c91906135a8565b6102dc610498366004613531565b611462565b6102566104ab366004613303565b6114a6565b6102bb6104be36600461331e565b60086020525f908152604090205481565b6001546001600160a01b03166103a2565b6102bb6104ee36600461331e565b5f9081526008602052604090205490565b61025661050d3660046135ba565b61151e565b6102dc61052036600461331e565b61167b565b5f546103a2906001600160a01b031681565b610256610545366004613303565b611955565b6102566105583660046135e4565b6119cd565b6102bb61056b36600461331e565b611b90565b6102bb611bc1565b610580601481565b60405160ff909116815260200161028c565b6102566105a0366004613303565b611bd3565b6004546105c19064ffffffffff80821691600160281b90041682565b6040805164ffffffffff93841681529290911660208301520161028c565b6106226105ed36600461331e565b5f908152600a6020819052604090912090810154600590910154909163ffffffff80831692600160201b900416908284101590565b60405161028c949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102bb60025481565b610662611381565b6001600160a01b0316336001600160a01b0316148061068b57506001546001600160a01b031633145b6106a857604051632864c4e160e01b815260040160405180910390fd5b6106b1816113af565b81906106da576040516381e5828960e01b81526004016106d19190613594565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906107089060049083611c0d565b6001600160a01b0382165f908152600660205260408120805460ff19169055600280549161073583613618565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b6107ae611eaf565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a60208190526040909120600681015491810154606092839291806001600160401b038111156108205761082061362d565b604051908082528060200260200182016040528015610849578160200160208202803683370190505b509450806001600160401b038111156108645761086461362d565b60405190808252806020026020018201604052801561088d578160200160208202803683370190505b5093505f805b83811015610989575f8560060182815481106108b1576108b1613641565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f90815260098801602052604090205460ff1660028111156108f7576108f7613655565b03610980578088848151811061090f5761090f613641565b60200260200101906001600160a01b031690816001600160a01b031681525050856008015f826001600160a01b03166001600160a01b031681526020019081526020015f205487848151811061096757610967613641565b60209081029190910101528261097c81613669565b9350505b50600101610893565b5050505050915091565b5f888152600a602052604090206002815460ff1660038111156109b8576109b8613655565b146109d657604051634f4b461f60e11b815260040160405180910390fd5b6004810154156109f95760405163632a22bb60e01b815260040160405180910390fd5b60068101548714610a425760405162461bcd60e51b815260206004820152601360248201527209cdec8ca40c6deeadce840dad2e6dac2e8c6d606b1b60448201526064016106d1565b83610a875760405162461bcd60e51b81526020600482015260156024820152741c1ad0dbdb5b5a5d1b595b9d081c995c5d5a5c9959605a1b60448201526064016106d1565b5f805460405163101bb4d760e21b8152600481018c90526001600160a01b039091169063406ed35c906024015f60405180830381865afa158015610acd573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610af491908101906137d7565b9050806101c0015115610bf35782610b3f5760405162461bcd60e51b815260206004820152600e60248201526d1c1c9bdbd9881c995c5d5a5c995960921b60448201526064016106d1565b8061012001516001600160a01b031663258ae5828686866040518463ffffffff1660e01b8152600401610b749392919061395d565b602060405180830381865afa158015610b8f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bb39190613976565b610bf35760405162461bcd60e51b815260206004820152601160248201527024b73b30b634b2102225a390383937b7b360791b60448201526064016106d1565b60048281018690555f8b815260096020526040808220889055905490516340a3b76160e11b81529182018c9052602482018790526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015610c51575f5ffd5b505af1158015610c63573d5f5f3e3d5ffd5b50505050897fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f8a8a8a8a8a8a8a604051610ca3979695949392919061398f565b60405180910390a250505050505050505050565b5f818152600a6020526040812081815460ff166003811115610cdb57610cdb613655565b03610cf957604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610d2e5760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610d5257610d52613655565b14610d70576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610db7573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ddb9190613a0f565b905080610dee6040860160208701613a39565b63ffffffff161115610e066040860160208701613a39565b829091610e34576040516344ec930f60e01b815263ffffffff909216600483015260248201526044016106d1565b5050815460ff1916600190811783558201859055436002830155600354610e5b9042613a52565b6003830155610e6f6005830185600261323a565b50610e78611bc1565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610ec9928a928a9291613a65565b60405180910390a250600195945050505050565b5f610ee7826113af565b610ef257505f919050565b6001546001600160a01b0316610f1b576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610f4b908590600401613594565b602060405180830381865afa158015610f66573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f8a9190613976565b92915050565b5f818152600a602052604081206001815460ff166003811115610fb557610fb5613655565b14610fc257505f92915050565b6003015442111592915050565b5f60015f848152600a602090815260408083206001600160a01b038716845260090190915290205460ff16600281111561100b5761100b613655565b149392505050565b61101b611eaf565b6001600160a01b0381166110425760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b611091611eaf565b61109a5f611ee1565b565b600b545f9081906001600160a01b031633146110cb5760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff1660038111156110f0576110f0613655565b1461110e57604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f90815260098301602052604090205463ffffffff909116925060019060ff16600281111561114e5761114e613655565b1461115e57600a0154915061123b565b6001600160a01b0385165f9081526009820160205260408120805460ff19166002179055600a820180549161119283613618565b919050555080600a01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b84986866040516111e3929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b61124b611381565b6001600160a01b0316336001600160a01b0316148061127457506001546001600160a01b031633145b61129157604051632864c4e160e01b815260040160405180910390fd5b61129a816113af565b61137e5760048054600160281b900464ffffffffff16906112c4906001600160a01b038416611f51565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff19909116179055600280549161131583613669565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539060600161079a565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a602052604090206004810154606091906113ff576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561145557602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611437575b5050505050915050919050565b5f805f848152600a602090815260408083206001600160a01b038716845260090190915290205460ff16600281111561149d5761149d613655565b14159392505050565b6114ae611eaf565b6001600160a01b0381166114d55760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b5f611527612127565b805490915060ff600160401b82041615906001600160401b03165f8115801561154d5750825b90505f826001600160401b031660011480156115685750303b155b905081158015611576575080155b156115945760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156115be57845460ff60401b1916600160401b1785555b6001600160a01b0387166115e55760405163d92e233d60e01b815260040160405180910390fd5b6115ee3361214f565b6115fa60046014612160565b611603866107a6565b61160b611381565b6001600160a01b0316876001600160a01b03161461162c5761162c87611bd3565b831561167257845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff16600381111561169f5761169f613655565b036116bd57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156116d5576116d5613655565b146116f357604051631860f69960e31b815260040160405180910390fd5b8060030154421161171757604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff161115806117fc578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b1580156117dd575f5ffd5b505af11580156117ef573d5f5f3e3d5ffd5b505f979650505050505050565b815460ff191660021782556006820154600a83018190555f816001600160401b0381111561182c5761182c61362d565b604051908082528060200260200182016040528015611855578160200160208202803683370190505b5090505f5b828110156118c757846008015f86600601838154811061187c5761187c613641565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205482518390839081106118b4576118b4613641565b602090810291909101015260010161185a565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b15801561190a575f5ffd5b505af115801561191c573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610ec9929190613ab5565b61195d611eaf565b6001600160a01b0381166119845760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff1660038111156119f1576119f1613655565b03611a0f57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611a2757611a27613655565b14611a4557604051631860f69960e31b815260040160405180910390fd5b8060030154421115611a6a57604051639a19114d60e01b815260040160405180910390fd5b335f90815260078201602052604090205460ff1615611a9c5760405163257309f160e11b815260040160405180910390fd5b611aa533610edd565b611ac25760405163149fbcfd60e11b815260040160405180910390fd5b611acd3383856121df565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526007850160205260409020805460ff19166001179055909150611b4c908390836123b0565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480611bbc576040516322e679e360e11b815260040160405180910390fd5b919050565b5f611bce600460146125b1565b905090565b611bdb611eaf565b6001600160a01b038116611c04575f604051631e4fbdf760e01b81526004016106d19190613594565b61137e81611ee1565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611c4c5760405162461bcd60e51b81526004016106d190613b18565b825464ffffffffff600160281b90910481169082168111611caa5760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b60448201526064016106d1565b825f5b81866001015f611cbd84886126aa565b64ffffffffff1681526020019081526020015f20819055505f816001611ce39190613b62565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611d185750611ea7565b600185165f03611ddf575f611d3783611d32886001613b7b565b6126aa565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611d9891600401613b98565b602060405180830381865af4158015611db3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dd79190613a0f565b935050611e93565b5f611def83611d32600189613bc8565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611e5091600401613b98565b602060405180830381865af4158015611e6b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e8f9190613a0f565b9350505b50647fffffffff600194851c169301611cad565b505050505050565b33611eb8611381565b6001600160a01b03161461109a573360405163118cdaa760e01b81526004016106d19190613594565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611fa05760405162461bcd60e51b81526004016106d190613b18565b825464ffffffffff90811690821610611ff35760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b60448201526064016106d1565b611ffe816001613b7b565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f61203584876126aa565b64ffffffffff16815260208101919091526040015f20556001831615612120575f61206582611d32600187613bc8565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916120c691600401613b98565b602060405180830381865af41580156120e1573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121059190613a0f565b647fffffffff600195861c1694909350919091019050612025565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610f8a565b6121576126c7565b61137e816126ec565b602060ff821611156121ae5760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b60448201526064016106d1565b6121bf600160ff831681901b613be5565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b5f82116121ff5760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612228576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd7191889161225e91613be5565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa1580156122a5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122c99190613a0f565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa15801561231c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123409190613a0f565b90505f81116123625760405163aeaddff160e01b815260040160405180910390fd5b5f61236d8284613bf8565b90505f811161238f5760405163149fbcfd60e11b815260040160405180910390fd5b808611156116725760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff169081111561242e57508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526008870182526040808420869055600988019092529120805460ff19168217905590506125aa565b5f5f90505f876008015f855f8154811061244a5761244a613641565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b84548110156124d2575f896008015f87848154811061249457612494613641565b5f9182526020808320909101546001600160a01b031683528201929092526040019020549050828111156124c9578092508193505b50600101612473565b508086106124e6575f9450505050506125aa565b5f886009015f8685815481106124fe576124fe613641565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff1916600183600281111561253b5761253b613655565b02179055508684838154811061255357612553613641565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260088a018252604080822089905560098b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff16116126045760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e203000000000000060448201526064016106d1565b602060ff831611156126285760405162461bcd60e51b81526004016106d190613c17565b8254600160281b900464ffffffffff168061264760ff85166002613d68565b64ffffffffff1610156126975760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b60448201526064016106d1565b6126a28482856126f4565b949350505050565b5f816126bd60ff851663ffffffff613d81565b6125aa9190613b7b565b6126cf6127bc565b61109a57604051631afcd79f60e31b815260040160405180910390fd5b611bdb6126c7565b5f602060ff831611156127195760405162461bcd60e51b81526004016106d190613c17565b8264ffffffffff165f0361273757612730826127d5565b90506125aa565b5f612743836001613b62565b60ff166001600160401b0381111561275d5761275d61362d565b604051908082528060200260200182016040528015612786578160200160208202803683370190505b50905061279585858584612e6f565b808360ff16815181106127aa576127aa613641565b60200260200101519150509392505050565b5f6127c5612127565b54600160401b900460ff16919050565b5f8160ff165f036127e757505f919050565b8160ff1660010361281957507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361284b57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361287d57507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff166004036128af57507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff166005036128e157507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff1660060361291357507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361294557507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff1660080361297757507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff166009036129a957507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a036129db57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b03612a0d57507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c03612a3f57507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d03612a7157507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e03612aa357507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f03612ad557507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff16601003612b0757507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff16601103612b3957507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff16601203612b6b57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff16601303612b9d57507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff16601403612bcf57507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff16601503612c0157507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff16601603612c3357507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff16601703612c6557507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff16601803612c9757507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff16601903612cc957507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a03612cfb57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03612d2d57507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03612d5f57507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03612d9157507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03612dc357507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03612df557507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff16602003612e2757507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e646578000060448201526064016106d1565b602060ff83161115612e935760405162461bcd60e51b81526004016106d190613c17565b5f8364ffffffffff1611612ef75760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b60648201526084016106d1565b5f612f03600185613bc8565b9050600181165f03612f5657846001015f612f1e5f846126aa565b64ffffffffff1681526020019081526020015f2054825f81518110612f4557612f45613641565b602002602001018181525050612f7e565b612f5f5f6127d5565b825f81518110612f7157612f71613641565b6020026020010181815250505b5f5b8360ff168160ff161015611ea757600182165f036130765773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110612fd257612fd2613641565b60200260200101518152602001612fe8856127d5565b8152506040518263ffffffff1660e01b81526004016130079190613b98565b602060405180830381865af4158015613022573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130469190613a0f565b83613052836001613b62565b60ff168151811061306557613065613641565b602002602001018181525050613227565b5f613082826001613b62565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613124575f876001015f6130d98560016130c89190613b62565b60018864ffffffffff16901c6126aa565b64ffffffffff1681526020019081526020015f2054905080858460016130ff9190613b62565b60ff168151811061311257613112613641565b60200260200101818152505050613225565b5f876001015f61313b85600188611d329190613bc8565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff168151811061319257613192613641565b60200260200101518152506040518263ffffffff1660e01b81526004016131b99190613b98565b602060405180830381865af41580156131d4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906131f89190613a0f565b85613204856001613b62565b60ff168151811061321757613217613641565b602002602001018181525050505b505b647fffffffff600192831c169101612f80565b6001830191839082156132cb579160200282015f5b8382111561329957833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff160217905550926020019260040160208160030104928301926001030261324f565b80156132c95782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613299565b505b506132d79291506132db565b5090565b5b808211156132d7575f81556001016132dc565b6001600160a01b038116811461137e575f5ffd5b5f60208284031215613313575f5ffd5b81356125aa816132ef565b5f6020828403121561332e575f5ffd5b5035919050565b5f8151808452602084019350602083015f5b8281101561336e5781516001600160a01b0316865260209586019590910190600101613347565b5093949350505050565b5f8151808452602084019350602083015f5b8281101561336e57815186526020958601959091019060010161338a565b604081525f6133ba6040830185613335565b82810360208401526133cc8185613378565b95945050505050565b5f5f83601f8401126133e5575f5ffd5b5081356001600160401b038111156133fb575f5ffd5b602083019150836020828501011115613412575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f60a0898b031215613430575f5ffd5b8835975060208901356001600160401b0381111561344c575f5ffd5b8901601f81018b1361345c575f5ffd5b80356001600160401b03811115613471575f5ffd5b8b60208260051b8401011115613485575f5ffd5b6020919091019750955060408901356001600160401b038111156134a7575f5ffd5b6134b38b828c016133d5565b9096509450506060890135925060808901356001600160401b038111156134d8575f5ffd5b6134e48b828c016133d5565b999c989b5096995094979396929594505050565b5f5f5f6080848603121561350a575f5ffd5b833592506020840135915060808401851015613524575f5ffd5b6040840190509250925092565b5f5f60408385031215613542575f5ffd5b823591506020830135613554816132ef565b809150509250929050565b5f5f5f60608486031215613571575f5ffd5b833592506020840135613583816132ef565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b602081525f6125aa6020830184613335565b5f5f604083850312156135cb575f5ffd5b82356135d6816132ef565b946020939093013593505050565b5f5f604083850312156135f5575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f8161362657613626613604565b505f190190565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b5f6001820161367a5761367a613604565b5060010190565b6040516101e081016001600160401b03811182821017156136a4576136a461362d565b60405290565b805160048110611bbc575f5ffd5b5f82601f8301126136c7575f5ffd5b604080519081016001600160401b03811182821017156136e9576136e961362d565b80604052508060408401858111156136ff575f5ffd5b845b81811015613719578051835260209283019201613701565b509195945050505050565b8051611bbc816132ef565b805160ff81168114611bbc575f5ffd5b5f82601f83011261374e575f5ffd5b81516001600160401b038111156137675761376761362d565b604051601f8201601f19908116603f011681016001600160401b03811182821017156137955761379561362d565b6040528181528382016020018510156137ac575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b80518015158114611bbc575f5ffd5b5f602082840312156137e7575f5ffd5b81516001600160401b038111156137fc575f5ffd5b8201610200818503121561380e575f5ffd5b613816613681565b81518152613826602083016136aa565b60208201526040828101519082015261384285606084016136b8565b606082015260a0820151608082015261385d60c08301613724565b60a082015261386e60e0830161372f565b60c08201526101008201516001600160401b0381111561388c575f5ffd5b6138988682850161373f565b60e0830152506138ab6101208301613724565b6101008201526138be6101408301613724565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b038111156138f4575f5ffd5b6139008682850161373f565b610180830152506139146101c08301613724565b6101a08201526139276101e083016137c8565b6101c0820152949350505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b838152604060208201525f6133cc604083018486613935565b5f60208284031215613986575f5ffd5b6125aa826137c8565b608080825281018790525f8860a08301825b8a8110156139d15782356139b4816132ef565b6001600160a01b03168252602092830192909101906001016139a1565b5083810360208501526139e581898b613935565b9150508560408401528281036060840152613a01818587613935565b9a9950505050505050505050565b5f60208284031215613a1f575f5ffd5b5051919050565b803563ffffffff81168114611bbc575f5ffd5b5f60208284031215613a49575f5ffd5b6125aa82613a26565b80820180821115610f8a57610f8a613604565b84815260a0810160208201855f5b6002811015613aa05763ffffffff613a8a83613a26565b1683526020928301929190910190600101613a73565b50505060608201939093526080015292915050565b604080825283549082018190525f8481526020812090916060840190835b81811015613afa5783546001600160a01b0316835260019384019360209093019201613ad3565b50508381036020850152613b0e8186613378565b9695505050505050565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610f8a57610f8a613604565b64ffffffffff8181168382160190811115610f8a57610f8a613604565b6040810181835f5b6002811015613bbf578151835260209283019290910190600101613ba0565b50505092915050565b64ffffffffff8281168282160390811115610f8a57610f8a613604565b81810381811115610f8a57610f8a613604565b5f82613c1257634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b600184111561123b57808504811115613c7957613c79613604565b6001841615613c8757908102905b60019390931c928002613c5e565b5f82613ca357506001610f8a565b81613caf57505f610f8a565b8160018114613cc55760028114613ccf57613d01565b6001915050610f8a565b60ff841115613ce057613ce0613604565b6001841b915064ffffffffff821115613cfb57613cfb613604565b50610f8a565b5060208310610133831016604e8410600b8410161715613d39575081810a64ffffffffff811115613d3457613d34613604565b610f8a565b613d4964ffffffffff8484613c5a565b8064ffffffffff04821115613d6057613d60613604565b029392505050565b5f6125aa64ffffffffff841664ffffffffff8416613c95565b64ffffffffff8181168382160290811690818114613da157613da1613604565b509291505056fea164736f6c634300081c000a", - "deployedBytecode": "0x608060405234801561000f575f5ffd5b506004361061023f575f3560e01c80639a7a2ffc11610135578063dbb06c93116100b4578063f165053611610079578063f165053614610578578063f2fde38b14610592578063f379b0df146105a5578063f52fd803146105df578063f6fc05d514610651575f5ffd5b8063dbb06c9314610525578063e59e469514610537578063e6745e131461054a578063e82f3b701461055d578063ebf0c71714610570575f5ffd5b8063c2b40ae4116100fa578063c2b40ae4146104b0578063c3a0ec30146104cf578063ca2869a0146104e0578063cd6dc687146104ff578063da881e5a14610512575f5ffd5b80639a7a2ffc146104255780639f0f874a14610461578063a01649301461046a578063a8a4d69b1461048a578063bff232c11461049d575f5ffd5b806370e36bbe116101c15780638cb89ecb116101865780638cb89ecb146103c25780638d1ddfb1146103e15780638da5cb5b146103f75780638e5ce3ad146103ff5780639015d37114610412575f5ffd5b806370e36bbe14610347578063715018a61461035a5780637c92f52414610362578063858142431461038f5780638a78bb15146103af575f5ffd5b8063291a691b11610207578063291a691b146102c95780632e7b716d146102ec5780634d6861a6146102ff57806350e6d94c146103125780635d50477614610334575f5ffd5b8063096b810a146102435780630f3e34121461025857806317d611201461026b5780631cc321b5146102955780632800d829146102a8575b5f5ffd5b610256610251366004613303565b61065a565b005b61025661026636600461331e565b6107a6565b61027e61027936600461331e565b6107e9565b60405161028c9291906133a8565b60405180910390f35b6102566102a3366004613419565b610993565b6102bb6102b636600461331e565b610cb7565b60405190815260200161028c565b6102dc6102d73660046134f8565b610d03565b604051901515815260200161028c565b6102dc6102fa366004613303565b610edd565b6102dc61030d36600461331e565b610f90565b6102dc610320366004613303565b60066020525f908152604090205460ff1681565b6102dc610342366004613531565b610fcf565b610256610355366004613303565b611013565b610256611089565b61037561037036600461355f565b61109c565b6040805192835263ffffffff90911660208301520161028c565b6001546103a2906001600160a01b031681565b60405161028c9190613594565b6102566103bd366004613303565b611243565b6102bb6103d036600461331e565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102bb565b6103a2611381565b600b546103a2906001600160a01b031681565b6102dc610420366004613303565b6113af565b61044b610433366004613303565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff909116815260200161028c565b6102bb60035481565b61047d61047836600461331e565b6113cc565b60405161028c91906135a8565b6102dc610498366004613531565b611462565b6102566104ab366004613303565b6114a6565b6102bb6104be36600461331e565b60086020525f908152604090205481565b6001546001600160a01b03166103a2565b6102bb6104ee36600461331e565b5f9081526008602052604090205490565b61025661050d3660046135ba565b61151e565b6102dc61052036600461331e565b61167b565b5f546103a2906001600160a01b031681565b610256610545366004613303565b611955565b6102566105583660046135e4565b6119cd565b6102bb61056b36600461331e565b611b90565b6102bb611bc1565b610580601481565b60405160ff909116815260200161028c565b6102566105a0366004613303565b611bd3565b6004546105c19064ffffffffff80821691600160281b90041682565b6040805164ffffffffff93841681529290911660208301520161028c565b6106226105ed36600461331e565b5f908152600a6020819052604090912090810154600590910154909163ffffffff80831692600160201b900416908284101590565b60405161028c949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102bb60025481565b610662611381565b6001600160a01b0316336001600160a01b0316148061068b57506001546001600160a01b031633145b6106a857604051632864c4e160e01b815260040160405180910390fd5b6106b1816113af565b81906106da576040516381e5828960e01b81526004016106d19190613594565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906107089060049083611c0d565b6001600160a01b0382165f908152600660205260408120805460ff19169055600280549161073583613618565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b6107ae611eaf565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a60208190526040909120600681015491810154606092839291806001600160401b038111156108205761082061362d565b604051908082528060200260200182016040528015610849578160200160208202803683370190505b509450806001600160401b038111156108645761086461362d565b60405190808252806020026020018201604052801561088d578160200160208202803683370190505b5093505f805b83811015610989575f8560060182815481106108b1576108b1613641565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f90815260098801602052604090205460ff1660028111156108f7576108f7613655565b03610980578088848151811061090f5761090f613641565b60200260200101906001600160a01b031690816001600160a01b031681525050856008015f826001600160a01b03166001600160a01b031681526020019081526020015f205487848151811061096757610967613641565b60209081029190910101528261097c81613669565b9350505b50600101610893565b5050505050915091565b5f888152600a602052604090206002815460ff1660038111156109b8576109b8613655565b146109d657604051634f4b461f60e11b815260040160405180910390fd5b6004810154156109f95760405163632a22bb60e01b815260040160405180910390fd5b60068101548714610a425760405162461bcd60e51b815260206004820152601360248201527209cdec8ca40c6deeadce840dad2e6dac2e8c6d606b1b60448201526064016106d1565b83610a875760405162461bcd60e51b81526020600482015260156024820152741c1ad0dbdb5b5a5d1b595b9d081c995c5d5a5c9959605a1b60448201526064016106d1565b5f805460405163101bb4d760e21b8152600481018c90526001600160a01b039091169063406ed35c906024015f60405180830381865afa158015610acd573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610af491908101906137d7565b9050806101c0015115610bf35782610b3f5760405162461bcd60e51b815260206004820152600e60248201526d1c1c9bdbd9881c995c5d5a5c995960921b60448201526064016106d1565b8061012001516001600160a01b031663258ae5828686866040518463ffffffff1660e01b8152600401610b749392919061395d565b602060405180830381865afa158015610b8f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bb39190613976565b610bf35760405162461bcd60e51b815260206004820152601160248201527024b73b30b634b2102225a390383937b7b360791b60448201526064016106d1565b60048281018690555f8b815260096020526040808220889055905490516340a3b76160e11b81529182018c9052602482018790526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015610c51575f5ffd5b505af1158015610c63573d5f5f3e3d5ffd5b50505050897fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f8a8a8a8a8a8a8a604051610ca3979695949392919061398f565b60405180910390a250505050505050505050565b5f818152600a6020526040812081815460ff166003811115610cdb57610cdb613655565b03610cf957604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610d2e5760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610d5257610d52613655565b14610d70576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610db7573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ddb9190613a0f565b905080610dee6040860160208701613a39565b63ffffffff161115610e066040860160208701613a39565b829091610e34576040516344ec930f60e01b815263ffffffff909216600483015260248201526044016106d1565b5050815460ff1916600190811783558201859055436002830155600354610e5b9042613a52565b6003830155610e6f6005830185600261323a565b50610e78611bc1565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610ec9928a928a9291613a65565b60405180910390a250600195945050505050565b5f610ee7826113af565b610ef257505f919050565b6001546001600160a01b0316610f1b576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610f4b908590600401613594565b602060405180830381865afa158015610f66573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f8a9190613976565b92915050565b5f818152600a602052604081206001815460ff166003811115610fb557610fb5613655565b14610fc257505f92915050565b6003015442111592915050565b5f60015f848152600a602090815260408083206001600160a01b038716845260090190915290205460ff16600281111561100b5761100b613655565b149392505050565b61101b611eaf565b6001600160a01b0381166110425760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b611091611eaf565b61109a5f611ee1565b565b600b545f9081906001600160a01b031633146110cb5760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff1660038111156110f0576110f0613655565b1461110e57604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f90815260098301602052604090205463ffffffff909116925060019060ff16600281111561114e5761114e613655565b1461115e57600a0154915061123b565b6001600160a01b0385165f9081526009820160205260408120805460ff19166002179055600a820180549161119283613618565b919050555080600a01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b84986866040516111e3929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b61124b611381565b6001600160a01b0316336001600160a01b0316148061127457506001546001600160a01b031633145b61129157604051632864c4e160e01b815260040160405180910390fd5b61129a816113af565b61137e5760048054600160281b900464ffffffffff16906112c4906001600160a01b038416611f51565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff19909116179055600280549161131583613669565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539060600161079a565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a602052604090206004810154606091906113ff576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561145557602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611437575b5050505050915050919050565b5f805f848152600a602090815260408083206001600160a01b038716845260090190915290205460ff16600281111561149d5761149d613655565b14159392505050565b6114ae611eaf565b6001600160a01b0381166114d55760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b5f611527612127565b805490915060ff600160401b82041615906001600160401b03165f8115801561154d5750825b90505f826001600160401b031660011480156115685750303b155b905081158015611576575080155b156115945760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156115be57845460ff60401b1916600160401b1785555b6001600160a01b0387166115e55760405163d92e233d60e01b815260040160405180910390fd5b6115ee3361214f565b6115fa60046014612160565b611603866107a6565b61160b611381565b6001600160a01b0316876001600160a01b03161461162c5761162c87611bd3565b831561167257845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff16600381111561169f5761169f613655565b036116bd57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156116d5576116d5613655565b146116f357604051631860f69960e31b815260040160405180910390fd5b8060030154421161171757604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff161115806117fc578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b1580156117dd575f5ffd5b505af11580156117ef573d5f5f3e3d5ffd5b505f979650505050505050565b815460ff191660021782556006820154600a83018190555f816001600160401b0381111561182c5761182c61362d565b604051908082528060200260200182016040528015611855578160200160208202803683370190505b5090505f5b828110156118c757846008015f86600601838154811061187c5761187c613641565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205482518390839081106118b4576118b4613641565b602090810291909101015260010161185a565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b15801561190a575f5ffd5b505af115801561191c573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610ec9929190613ab5565b61195d611eaf565b6001600160a01b0381166119845760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff1660038111156119f1576119f1613655565b03611a0f57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611a2757611a27613655565b14611a4557604051631860f69960e31b815260040160405180910390fd5b8060030154421115611a6a57604051639a19114d60e01b815260040160405180910390fd5b335f90815260078201602052604090205460ff1615611a9c5760405163257309f160e11b815260040160405180910390fd5b611aa533610edd565b611ac25760405163149fbcfd60e11b815260040160405180910390fd5b611acd3383856121df565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526007850160205260409020805460ff19166001179055909150611b4c908390836123b0565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480611bbc576040516322e679e360e11b815260040160405180910390fd5b919050565b5f611bce600460146125b1565b905090565b611bdb611eaf565b6001600160a01b038116611c04575f604051631e4fbdf760e01b81526004016106d19190613594565b61137e81611ee1565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611c4c5760405162461bcd60e51b81526004016106d190613b18565b825464ffffffffff600160281b90910481169082168111611caa5760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b60448201526064016106d1565b825f5b81866001015f611cbd84886126aa565b64ffffffffff1681526020019081526020015f20819055505f816001611ce39190613b62565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611d185750611ea7565b600185165f03611ddf575f611d3783611d32886001613b7b565b6126aa565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611d9891600401613b98565b602060405180830381865af4158015611db3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dd79190613a0f565b935050611e93565b5f611def83611d32600189613bc8565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611e5091600401613b98565b602060405180830381865af4158015611e6b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e8f9190613a0f565b9350505b50647fffffffff600194851c169301611cad565b505050505050565b33611eb8611381565b6001600160a01b03161461109a573360405163118cdaa760e01b81526004016106d19190613594565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611fa05760405162461bcd60e51b81526004016106d190613b18565b825464ffffffffff90811690821610611ff35760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b60448201526064016106d1565b611ffe816001613b7b565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f61203584876126aa565b64ffffffffff16815260208101919091526040015f20556001831615612120575f61206582611d32600187613bc8565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916120c691600401613b98565b602060405180830381865af41580156120e1573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121059190613a0f565b647fffffffff600195861c1694909350919091019050612025565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610f8a565b6121576126c7565b61137e816126ec565b602060ff821611156121ae5760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b60448201526064016106d1565b6121bf600160ff831681901b613be5565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b5f82116121ff5760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612228576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd7191889161225e91613be5565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa1580156122a5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122c99190613a0f565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa15801561231c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123409190613a0f565b90505f81116123625760405163aeaddff160e01b815260040160405180910390fd5b5f61236d8284613bf8565b90505f811161238f5760405163149fbcfd60e11b815260040160405180910390fd5b808611156116725760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff169081111561242e57508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526008870182526040808420869055600988019092529120805460ff19168217905590506125aa565b5f5f90505f876008015f855f8154811061244a5761244a613641565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b84548110156124d2575f896008015f87848154811061249457612494613641565b5f9182526020808320909101546001600160a01b031683528201929092526040019020549050828111156124c9578092508193505b50600101612473565b508086106124e6575f9450505050506125aa565b5f886009015f8685815481106124fe576124fe613641565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff1916600183600281111561253b5761253b613655565b02179055508684838154811061255357612553613641565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260088a018252604080822089905560098b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff16116126045760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e203000000000000060448201526064016106d1565b602060ff831611156126285760405162461bcd60e51b81526004016106d190613c17565b8254600160281b900464ffffffffff168061264760ff85166002613d68565b64ffffffffff1610156126975760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b60448201526064016106d1565b6126a28482856126f4565b949350505050565b5f816126bd60ff851663ffffffff613d81565b6125aa9190613b7b565b6126cf6127bc565b61109a57604051631afcd79f60e31b815260040160405180910390fd5b611bdb6126c7565b5f602060ff831611156127195760405162461bcd60e51b81526004016106d190613c17565b8264ffffffffff165f0361273757612730826127d5565b90506125aa565b5f612743836001613b62565b60ff166001600160401b0381111561275d5761275d61362d565b604051908082528060200260200182016040528015612786578160200160208202803683370190505b50905061279585858584612e6f565b808360ff16815181106127aa576127aa613641565b60200260200101519150509392505050565b5f6127c5612127565b54600160401b900460ff16919050565b5f8160ff165f036127e757505f919050565b8160ff1660010361281957507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361284b57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361287d57507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff166004036128af57507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff166005036128e157507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff1660060361291357507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361294557507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff1660080361297757507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff166009036129a957507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a036129db57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b03612a0d57507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c03612a3f57507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d03612a7157507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e03612aa357507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f03612ad557507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff16601003612b0757507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff16601103612b3957507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff16601203612b6b57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff16601303612b9d57507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff16601403612bcf57507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff16601503612c0157507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff16601603612c3357507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff16601703612c6557507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff16601803612c9757507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff16601903612cc957507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a03612cfb57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03612d2d57507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03612d5f57507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03612d9157507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03612dc357507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03612df557507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff16602003612e2757507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e646578000060448201526064016106d1565b602060ff83161115612e935760405162461bcd60e51b81526004016106d190613c17565b5f8364ffffffffff1611612ef75760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b60648201526084016106d1565b5f612f03600185613bc8565b9050600181165f03612f5657846001015f612f1e5f846126aa565b64ffffffffff1681526020019081526020015f2054825f81518110612f4557612f45613641565b602002602001018181525050612f7e565b612f5f5f6127d5565b825f81518110612f7157612f71613641565b6020026020010181815250505b5f5b8360ff168160ff161015611ea757600182165f036130765773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110612fd257612fd2613641565b60200260200101518152602001612fe8856127d5565b8152506040518263ffffffff1660e01b81526004016130079190613b98565b602060405180830381865af4158015613022573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130469190613a0f565b83613052836001613b62565b60ff168151811061306557613065613641565b602002602001018181525050613227565b5f613082826001613b62565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613124575f876001015f6130d98560016130c89190613b62565b60018864ffffffffff16901c6126aa565b64ffffffffff1681526020019081526020015f2054905080858460016130ff9190613b62565b60ff168151811061311257613112613641565b60200260200101818152505050613225565b5f876001015f61313b85600188611d329190613bc8565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff168151811061319257613192613641565b60200260200101518152506040518263ffffffff1660e01b81526004016131b99190613b98565b602060405180830381865af41580156131d4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906131f89190613a0f565b85613204856001613b62565b60ff168151811061321757613217613641565b602002602001018181525050505b505b647fffffffff600192831c169101612f80565b6001830191839082156132cb579160200282015f5b8382111561329957833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff160217905550926020019260040160208160030104928301926001030261324f565b80156132c95782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613299565b505b506132d79291506132db565b5090565b5b808211156132d7575f81556001016132dc565b6001600160a01b038116811461137e575f5ffd5b5f60208284031215613313575f5ffd5b81356125aa816132ef565b5f6020828403121561332e575f5ffd5b5035919050565b5f8151808452602084019350602083015f5b8281101561336e5781516001600160a01b0316865260209586019590910190600101613347565b5093949350505050565b5f8151808452602084019350602083015f5b8281101561336e57815186526020958601959091019060010161338a565b604081525f6133ba6040830185613335565b82810360208401526133cc8185613378565b95945050505050565b5f5f83601f8401126133e5575f5ffd5b5081356001600160401b038111156133fb575f5ffd5b602083019150836020828501011115613412575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f60a0898b031215613430575f5ffd5b8835975060208901356001600160401b0381111561344c575f5ffd5b8901601f81018b1361345c575f5ffd5b80356001600160401b03811115613471575f5ffd5b8b60208260051b8401011115613485575f5ffd5b6020919091019750955060408901356001600160401b038111156134a7575f5ffd5b6134b38b828c016133d5565b9096509450506060890135925060808901356001600160401b038111156134d8575f5ffd5b6134e48b828c016133d5565b999c989b5096995094979396929594505050565b5f5f5f6080848603121561350a575f5ffd5b833592506020840135915060808401851015613524575f5ffd5b6040840190509250925092565b5f5f60408385031215613542575f5ffd5b823591506020830135613554816132ef565b809150509250929050565b5f5f5f60608486031215613571575f5ffd5b833592506020840135613583816132ef565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b602081525f6125aa6020830184613335565b5f5f604083850312156135cb575f5ffd5b82356135d6816132ef565b946020939093013593505050565b5f5f604083850312156135f5575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f8161362657613626613604565b505f190190565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b5f6001820161367a5761367a613604565b5060010190565b6040516101e081016001600160401b03811182821017156136a4576136a461362d565b60405290565b805160048110611bbc575f5ffd5b5f82601f8301126136c7575f5ffd5b604080519081016001600160401b03811182821017156136e9576136e961362d565b80604052508060408401858111156136ff575f5ffd5b845b81811015613719578051835260209283019201613701565b509195945050505050565b8051611bbc816132ef565b805160ff81168114611bbc575f5ffd5b5f82601f83011261374e575f5ffd5b81516001600160401b038111156137675761376761362d565b604051601f8201601f19908116603f011681016001600160401b03811182821017156137955761379561362d565b6040528181528382016020018510156137ac575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b80518015158114611bbc575f5ffd5b5f602082840312156137e7575f5ffd5b81516001600160401b038111156137fc575f5ffd5b8201610200818503121561380e575f5ffd5b613816613681565b81518152613826602083016136aa565b60208201526040828101519082015261384285606084016136b8565b606082015260a0820151608082015261385d60c08301613724565b60a082015261386e60e0830161372f565b60c08201526101008201516001600160401b0381111561388c575f5ffd5b6138988682850161373f565b60e0830152506138ab6101208301613724565b6101008201526138be6101408301613724565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b038111156138f4575f5ffd5b6139008682850161373f565b610180830152506139146101c08301613724565b6101a08201526139276101e083016137c8565b6101c0820152949350505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b838152604060208201525f6133cc604083018486613935565b5f60208284031215613986575f5ffd5b6125aa826137c8565b608080825281018790525f8860a08301825b8a8110156139d15782356139b4816132ef565b6001600160a01b03168252602092830192909101906001016139a1565b5083810360208501526139e581898b613935565b9150508560408401528281036060840152613a01818587613935565b9a9950505050505050505050565b5f60208284031215613a1f575f5ffd5b5051919050565b803563ffffffff81168114611bbc575f5ffd5b5f60208284031215613a49575f5ffd5b6125aa82613a26565b80820180821115610f8a57610f8a613604565b84815260a0810160208201855f5b6002811015613aa05763ffffffff613a8a83613a26565b1683526020928301929190910190600101613a73565b50505060608201939093526080015292915050565b604080825283549082018190525f8481526020812090916060840190835b81811015613afa5783546001600160a01b0316835260019384019360209093019201613ad3565b50508381036020850152613b0e8186613378565b9695505050505050565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610f8a57610f8a613604565b64ffffffffff8181168382160190811115610f8a57610f8a613604565b6040810181835f5b6002811015613bbf578151835260209283019290910190600101613ba0565b50505092915050565b64ffffffffff8281168282160390811115610f8a57610f8a613604565b81810381811115610f8a57610f8a613604565b5f82613c1257634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b600184111561123b57808504811115613c7957613c79613604565b6001841615613c8757908102905b60019390931c928002613c5e565b5f82613ca357506001610f8a565b81613caf57505f610f8a565b8160018114613cc55760028114613ccf57613d01565b6001915050610f8a565b60ff841115613ce057613ce0613604565b6001841b915064ffffffffff821115613cfb57613cfb613604565b50610f8a565b5060208310610133831016604e8410600b8410161715613d39575081810a64ffffffffff811115613d3457613d34613604565b610f8a565b613d4964ffffffffff8484613c5a565b8064ffffffffff04821115613d6057613d60613604565b029392505050565b5f6125aa64ffffffffff841664ffffffffff8416613c95565b64ffffffffff8181168382160290811690818114613da157613da1613604565b509291505056fea164736f6c634300081c000a", + "bytecode": "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b613e01806100d65f395ff3fe608060405234801561000f575f5ffd5b506004361061024a575f3560e01c80639a7a2ffc11610140578063da881e5a116100bf578063ebf0c71711610084578063ebf0c7171461058a578063f165053614610592578063f2fde38b146105ac578063f379b0df146105bf578063f52fd803146105f9578063f6fc05d514610669575f5ffd5b8063da881e5a1461052c578063dbb06c931461053f578063e59e469514610551578063e6745e1314610564578063e82f3b7014610577575f5ffd5b8063c2b40ae411610105578063c2b40ae4146104b7578063c3a0ec30146104d6578063c6b2a438146104e7578063ca2869a0146104fa578063cd6dc68714610519575f5ffd5b80639a7a2ffc1461042c5780639f0f874a14610468578063a016493014610471578063a8a4d69b14610491578063bff232c1146104a4575f5ffd5b806370e36bbe116101cc5780638cb89ecb116101915780638cb89ecb146103c95780638d1ddfb1146103e85780638da5cb5b146103fe5780638e5ce3ad146104065780639015d37114610419575f5ffd5b806370e36bbe1461034e578063715018a6146103615780637c92f5241461036957806385814243146103965780638a78bb15146103b6575f5ffd5b8063291a691b11610212578063291a691b146102d05780632e7b716d146102f35780634d6861a61461030657806350e6d94c146103195780635d5047761461033b575f5ffd5b8063096b810a1461024e578063099a161a146102635780630f3e34121461028957806317d611201461029c5780632800d829146102bd575b5f5ffd5b61026161025c3660046133b3565b610672565b005b6102766102713660046133ce565b6107be565b6040519081526020015b60405180910390f35b6102616102973660046133ce565b6107f7565b6102af6102aa3660046133ce565b61083a565b604051610280929190613458565b6102766102cb3660046133ce565b6109e3565b6102e36102de366004613485565b610a2f565b6040519015158152602001610280565b6102e36103013660046133b3565b610c09565b6102e36103143660046133ce565b610cbc565b6102e36103273660046133b3565b60066020525f908152604090205460ff1681565b6102e36103493660046134be565b610cfb565b61026161035c3660046133b3565b610d3e565b610261610db4565b61037c6103773660046134ec565b610dc7565b6040805192835263ffffffff909116602083015201610280565b6001546103a9906001600160a01b031681565b6040516102809190613521565b6102616103c43660046133b3565b610f6e565b6102766103d73660046133ce565b60096020525f908152604090205481565b600454600160281b900464ffffffffff16610276565b6103a96110ac565b600b546103a9906001600160a01b031681565b6102e36104273660046133b3565b6110da565b61045261043a3660046133b3565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff9091168152602001610280565b61027660035481565b61048461047f3660046133ce565b6110f7565b6040516102809190613535565b6102e361049f3660046134be565b61118d565b6102616104b23660046133b3565b6111d0565b6102766104c53660046133ce565b60086020525f908152604090205481565b6001546001600160a01b03166103a9565b6102616104f536600461358b565b611248565b6102766105083660046133ce565b5f9081526008602052604090205490565b61026161052736600461360b565b61153e565b6102e361053a3660046133ce565b61169b565b5f546103a9906001600160a01b031681565b61026161055f3660046133b3565b611975565b610261610572366004613635565b6119ed565b6102766105853660046133ce565b611bb0565b610276611be1565b61059a601481565b60405160ff9091168152602001610280565b6102616105ba3660046133b3565b611bf3565b6004546105db9064ffffffffff80821691600160281b90041682565b6040805164ffffffffff938416815292909116602083015201610280565b61063a6106073660046133ce565b5f908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b604051610280949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b61027660025481565b61067a6110ac565b6001600160a01b0316336001600160a01b031614806106a357506001546001600160a01b031633145b6106c057604051632864c4e160e01b815260040160405180910390fd5b6106c9816110da565b81906106f2576040516381e5828960e01b81526004016106e99190613521565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906107209060049083611c2d565b6001600160a01b0382165f908152600660205260408120805460ff19169055600280549161074d83613669565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b5f818152600a6020526040812060048101546107ed576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b6107ff611ecf565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602052604090206006810154600b82015460609283929091806001600160401b038111156108705761087061367e565b604051908082528060200260200182016040528015610899578160200160208202803683370190505b509450806001600160401b038111156108b4576108b461367e565b6040519080825280602002602001820160405280156108dd578160200160208202803683370190505b5093505f805b838110156109d9575f85600601828154811061090157610901613692565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f908152600a8801602052604090205460ff166002811115610947576109476136a6565b036109d0578088848151811061095f5761095f613692565b60200260200101906001600160a01b031690816001600160a01b031681525050856009015f826001600160a01b03166001600160a01b031681526020019081526020015f20548784815181106109b7576109b7613692565b6020908102919091010152826109cc816136ba565b9350505b506001016108e3565b5050505050915091565b5f818152600a6020526040812081815460ff166003811115610a0757610a076136a6565b03610a2557604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610a5a5760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610a7e57610a7e6136a6565b14610a9c576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610ae3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b0791906136d2565b905080610b1a60408601602087016136fc565b63ffffffff161115610b3260408601602087016136fc565b829091610b60576040516344ec930f60e01b815263ffffffff909216600483015260248201526044016106e9565b5050815460ff1916600190811783558201859055436002830155600354610b879042613715565b6003830155610b9b600583018560026132ea565b50610ba4611be1565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610bf5928a928a9291613728565b60405180910390a250600195945050505050565b5f610c13826110da565b610c1e57505f919050565b6001546001600160a01b0316610c47576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610c77908590600401613521565b602060405180830381865afa158015610c92573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cb69190613787565b92915050565b5f818152600a602052604081206001815460ff166003811115610ce157610ce16136a6565b14610cee57505f92915050565b6003015442111592915050565b5f60015f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff166002811115610d3657610d366136a6565b149392505050565b610d46611ecf565b6001600160a01b038116610d6d5760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b610dbc611ecf565b610dc55f611f01565b565b600b545f9081906001600160a01b03163314610df65760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff166003811115610e1b57610e1b6136a6565b14610e3957604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f908152600a8301602052604090205463ffffffff909116925060019060ff166002811115610e7957610e796136a6565b14610e8957600b01549150610f66565b6001600160a01b0385165f908152600a820160205260408120805460ff19166002179055600b8201805491610ebd83613669565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051610f0e929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b610f766110ac565b6001600160a01b0316336001600160a01b03161480610f9f57506001546001600160a01b031633145b610fbc57604051632864c4e160e01b815260040160405180910390fd5b610fc5816110da565b6110a95760048054600160281b900464ffffffffff1690610fef906001600160a01b038416611f71565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff199091161790556002805491611040836136ba565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db53906060016107b2565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a6020526040902060048101546060919061112a576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561118057602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611162575b5050505050915050919050565b5f805f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156111c7576111c76136a6565b14159392505050565b6111d8611ecf565b6001600160a01b0381166111ff5760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b5f868152600a602052604090206002815460ff16600381111561126d5761126d6136a6565b1461128b57604051634f4b461f60e11b815260040160405180910390fd5b6004810154156112ae5760405163632a22bb60e01b815260040160405180910390fd5b836112f35760405162461bcd60e51b81526020600482015260156024820152741c1ad0dbdb5b5a5d1b595b9d081c995c5d5a5c9959605a1b60448201526064016106e9565b5f61130082600601612147565b600783018190555f805460405163101bb4d760e21b8152600481018c905292935090916001600160a01b039091169063406ed35c906024015f60405180830381865afa158015611352573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261137991908101906138e7565b9050806101c001511561147a57836113c45760405162461bcd60e51b815260206004820152600e60248201526d1c1c9bdbd9881c995c5d5a5c995960921b60448201526064016106e9565b8061012001516001600160a01b031663de12c640878488886040518563ffffffff1660e01b81526004016113fb9493929190613a6d565b602060405180830381865afa158015611416573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061143a9190613787565b61147a5760405162461bcd60e51b815260206004820152601160248201527024b73b30b634b2102225a390383937b7b360791b60448201526064016106e9565b60048381018790555f8a815260096020526040808220899055905490516340a3b76160e11b81529182018b9052602482018890526001600160a01b0316906381476ec2906044015f604051808303815f87803b1580156114d8575f5ffd5b505af11580156114ea573d5f5f3e3d5ffd5b50505050887fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018a8a8a8a8a60405161152b96959493929190613ad2565b60405180910390a2505050505050505050565b5f6115476121a8565b805490915060ff600160401b82041615906001600160401b03165f8115801561156d5750825b90505f826001600160401b031660011480156115885750303b155b905081158015611596575080155b156115b45760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156115de57845460ff60401b1916600160401b1785555b6001600160a01b0387166116055760405163d92e233d60e01b815260040160405180910390fd5b61160e336121d0565b61161a600460146121e1565b611623866107f7565b61162b6110ac565b6001600160a01b0316876001600160a01b03161461164c5761164c87611bf3565b831561169257845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff1660038111156116bf576116bf6136a6565b036116dd57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156116f5576116f56136a6565b1461171357604051631860f69960e31b815260040160405180910390fd5b8060030154421161173757604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff1611158061181c578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b1580156117fd575f5ffd5b505af115801561180f573d5f5f3e3d5ffd5b505f979650505050505050565b815460ff191660021782556006820154600b83018190555f816001600160401b0381111561184c5761184c61367e565b604051908082528060200260200182016040528015611875578160200160208202803683370190505b5090505f5b828110156118e757846009015f86600601838154811061189c5761189c613692565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205482518390839081106118d4576118d4613692565b602090810291909101015260010161187a565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b15801561192a575f5ffd5b505af115801561193c573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610bf5929190613b1f565b61197d611ecf565b6001600160a01b0381166119a45760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff166003811115611a1157611a116136a6565b03611a2f57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611a4757611a476136a6565b14611a6557604051631860f69960e31b815260040160405180910390fd5b8060030154421115611a8a57604051639a19114d60e01b815260040160405180910390fd5b335f90815260088201602052604090205460ff1615611abc5760405163257309f160e11b815260040160405180910390fd5b611ac533610c09565b611ae25760405163149fbcfd60e11b815260040160405180910390fd5b611aed338385612260565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526008850160205260409020805460ff19166001179055909150611b6c90839083612431565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480611bdc576040516322e679e360e11b815260040160405180910390fd5b919050565b5f611bee60046014612632565b905090565b611bfb611ecf565b6001600160a01b038116611c24575f604051631e4fbdf760e01b81526004016106e99190613521565b6110a981611f01565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611c6c5760405162461bcd60e51b81526004016106e990613b31565b825464ffffffffff600160281b90910481169082168111611cca5760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b60448201526064016106e9565b825f5b81866001015f611cdd848861272b565b64ffffffffff1681526020019081526020015f20819055505f816001611d039190613b7b565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611d385750611ec7565b600185165f03611dff575f611d5783611d52886001613b94565b61272b565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611db891600401613bb1565b602060405180830381865af4158015611dd3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611df791906136d2565b935050611eb3565b5f611e0f83611d52600189613be1565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611e7091600401613bb1565b602060405180830381865af4158015611e8b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611eaf91906136d2565b9350505b50647fffffffff600194851c169301611ccd565b505050505050565b33611ed86110ac565b6001600160a01b031614610dc5573360405163118cdaa760e01b81526004016106e99190613521565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611fc05760405162461bcd60e51b81526004016106e990613b31565b825464ffffffffff908116908216106120135760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b60448201526064016106e9565b61201e816001613b94565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f612055848761272b565b64ffffffffff16815260208101919091526040015f20556001831615612140575f61208582611d52600187613be1565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916120e691600401613bb1565b602060405180830381865af4158015612101573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061212591906136d2565b647fffffffff600195861c1694909350919091019050612045565b5050505050565b5f610cb68280548060200260200160405190810160405280929190818152602001828054801561219e57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311612180575b5050505050612748565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610cb6565b6121d8612777565b6110a98161279c565b602060ff8216111561222f5760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b60448201526064016106e9565b612240600160ff831681901b613bfe565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b5f82116122805760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b03166122a9576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd719188916122df91613bfe565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612326573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061234a91906136d2565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa15801561239d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123c191906136d2565b90505f81116123e35760405163aeaddff160e01b815260040160405180910390fd5b5f6123ee8284613c11565b90505f81116124105760405163149fbcfd60e11b815260040160405180910390fd5b808611156116925760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff16908111156124af57508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff191682179055905061262b565b5f5f90505f876009015f855f815481106124cb576124cb613692565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612553575f896009015f87848154811061251557612515613692565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205490508281111561254a578092508193505b506001016124f4565b50808610612567575f94505050505061262b565b5f88600a015f86858154811061257f5761257f613692565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff191660018360028111156125bc576125bc6136a6565b0217905550868483815481106125d4576125d4613692565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff16116126855760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e203000000000000060448201526064016106e9565b602060ff831611156126a95760405162461bcd60e51b81526004016106e990613c30565b8254600160281b900464ffffffffff16806126c860ff85166002613d81565b64ffffffffff1610156127185760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b60448201526064016106e9565b6127238482856127a4565b949350505050565b5f8161273e60ff851663ffffffff613d9a565b61262b9190613b94565b5f8160405160200161275a9190613dc1565b604051602081830303815290604052805190602001209050919050565b61277f61286c565b610dc557604051631afcd79f60e31b815260040160405180910390fd5b611bfb612777565b5f602060ff831611156127c95760405162461bcd60e51b81526004016106e990613c30565b8264ffffffffff165f036127e7576127e082612885565b905061262b565b5f6127f3836001613b7b565b60ff166001600160401b0381111561280d5761280d61367e565b604051908082528060200260200182016040528015612836578160200160208202803683370190505b50905061284585858584612f1f565b808360ff168151811061285a5761285a613692565b60200260200101519150509392505050565b5f6128756121a8565b54600160401b900460ff16919050565b5f8160ff165f0361289757505f919050565b8160ff166001036128c957507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff166002036128fb57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361292d57507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361295f57507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361299157507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036129c357507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff166007036129f557507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff16600803612a2757507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff16600903612a5957507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a03612a8b57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b03612abd57507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c03612aef57507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d03612b2157507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e03612b5357507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f03612b8557507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff16601003612bb757507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff16601103612be957507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff16601203612c1b57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff16601303612c4d57507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff16601403612c7f57507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff16601503612cb157507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff16601603612ce357507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff16601703612d1557507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff16601803612d4757507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff16601903612d7957507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a03612dab57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03612ddd57507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03612e0f57507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03612e4157507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03612e7357507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03612ea557507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff16602003612ed757507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e646578000060448201526064016106e9565b602060ff83161115612f435760405162461bcd60e51b81526004016106e990613c30565b5f8364ffffffffff1611612fa75760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b60648201526084016106e9565b5f612fb3600185613be1565b9050600181165f0361300657846001015f612fce5f8461272b565b64ffffffffff1681526020019081526020015f2054825f81518110612ff557612ff5613692565b60200260200101818152505061302e565b61300f5f612885565b825f8151811061302157613021613692565b6020026020010181815250505b5f5b8360ff168160ff161015611ec757600182165f036131265773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff168151811061308257613082613692565b6020026020010151815260200161309885612885565b8152506040518263ffffffff1660e01b81526004016130b79190613bb1565b602060405180830381865af41580156130d2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130f691906136d2565b83613102836001613b7b565b60ff168151811061311557613115613692565b6020026020010181815250506132d7565b5f613132826001613b7b565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff168111156131d4575f876001015f6131898560016131789190613b7b565b60018864ffffffffff16901c61272b565b64ffffffffff1681526020019081526020015f2054905080858460016131af9190613b7b565b60ff16815181106131c2576131c2613692565b602002602001018181525050506132d5565b5f876001015f6131eb85600188611d529190613be1565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff168151811061324257613242613692565b60200260200101518152506040518263ffffffff1660e01b81526004016132699190613bb1565b602060405180830381865af4158015613284573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132a891906136d2565b856132b4856001613b7b565b60ff16815181106132c7576132c7613692565b602002602001018181525050505b505b647fffffffff600192831c169101613030565b60018301918390821561337b579160200282015f5b8382111561334957833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff16021790555092602001926004016020816003010492830192600103026132ff565b80156133795782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613349565b505b5061338792915061338b565b5090565b5b80821115613387575f815560010161338c565b6001600160a01b03811681146110a9575f5ffd5b5f602082840312156133c3575f5ffd5b813561262b8161339f565b5f602082840312156133de575f5ffd5b5035919050565b5f8151808452602084019350602083015f5b8281101561341e5781516001600160a01b03168652602095860195909101906001016133f7565b5093949350505050565b5f8151808452602084019350602083015f5b8281101561341e57815186526020958601959091019060010161343a565b604081525f61346a60408301856133e5565b828103602084015261347c8185613428565b95945050505050565b5f5f5f60808486031215613497575f5ffd5b8335925060208401359150608084018510156134b1575f5ffd5b6040840190509250925092565b5f5f604083850312156134cf575f5ffd5b8235915060208301356134e18161339f565b809150509250929050565b5f5f5f606084860312156134fe575f5ffd5b8335925060208401356135108161339f565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b602081525f61262b60208301846133e5565b5f5f83601f840112613557575f5ffd5b5081356001600160401b0381111561356d575f5ffd5b602083019150836020828501011115613584575f5ffd5b9250929050565b5f5f5f5f5f5f608087890312156135a0575f5ffd5b8635955060208701356001600160401b038111156135bc575f5ffd5b6135c889828a01613547565b9096509450506040870135925060608701356001600160401b038111156135ed575f5ffd5b6135f989828a01613547565b979a9699509497509295939492505050565b5f5f6040838503121561361c575f5ffd5b82356136278161339f565b946020939093013593505050565b5f5f60408385031215613646575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f8161367757613677613655565b505f190190565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b5f600182016136cb576136cb613655565b5060010190565b5f602082840312156136e2575f5ffd5b5051919050565b803563ffffffff81168114611bdc575f5ffd5b5f6020828403121561370c575f5ffd5b61262b826136e9565b80820180821115610cb657610cb6613655565b84815260a0810160208201855f5b60028110156137635763ffffffff61374d836136e9565b1683526020928301929190910190600101613736565b50505060608201939093526080015292915050565b80518015158114611bdc575f5ffd5b5f60208284031215613797575f5ffd5b61262b82613778565b6040516101e081016001600160401b03811182821017156137c3576137c361367e565b60405290565b805160048110611bdc575f5ffd5b5f82601f8301126137e6575f5ffd5b604080519081016001600160401b03811182821017156138085761380861367e565b806040525080604084018581111561381e575f5ffd5b845b81811015613838578051835260209283019201613820565b509195945050505050565b8051611bdc8161339f565b805160ff81168114611bdc575f5ffd5b5f82601f83011261386d575f5ffd5b81516001600160401b038111156138865761388661367e565b604051601f8201601f19908116603f011681016001600160401b03811182821017156138b4576138b461367e565b6040528181528382016020018510156138cb575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f602082840312156138f7575f5ffd5b81516001600160401b0381111561390c575f5ffd5b8201610200818503121561391e575f5ffd5b6139266137a0565b81518152613936602083016137c9565b60208201526040828101519082015261395285606084016137d7565b606082015260a0820151608082015261396d60c08301613843565b60a082015261397e60e0830161384e565b60c08201526101008201516001600160401b0381111561399c575f5ffd5b6139a88682850161385e565b60e0830152506139bb6101208301613843565b6101008201526139ce6101408301613843565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613a04575f5ffd5b613a108682850161385e565b61018083015250613a246101c08301613843565b6101a0820152613a376101e08301613778565b6101c0820152949350505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b848152836020820152606060408201525f613a8c606083018486613a45565b9695505050505050565b5f8154808452602084019350825f5260205f205f5b8281101561341e5781546001600160a01b0316865260209095019460019182019101613aab565b608081525f613ae46080830189613a96565b8281036020840152613af781888a613a45565b90508560408401528281036060840152613b12818587613a45565b9998505050505050505050565b604081525f61346a6040830185613a96565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610cb657610cb6613655565b64ffffffffff8181168382160190811115610cb657610cb6613655565b6040810181835f5b6002811015613bd8578151835260209283019290910190600101613bb9565b50505092915050565b64ffffffffff8281168282160390811115610cb657610cb6613655565b81810381811115610cb657610cb6613655565b5f82613c2b57634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b6001841115610f6657808504811115613c9257613c92613655565b6001841615613ca057908102905b60019390931c928002613c77565b5f82613cbc57506001610cb6565b81613cc857505f610cb6565b8160018114613cde5760028114613ce857613d1a565b6001915050610cb6565b60ff841115613cf957613cf9613655565b6001841b915064ffffffffff821115613d1457613d14613655565b50610cb6565b5060208310610133831016604e8410600b8410161715613d52575081810a64ffffffffff811115613d4d57613d4d613655565b610cb6565b613d6264ffffffffff8484613c73565b8064ffffffffff04821115613d7957613d79613655565b029392505050565b5f61262b64ffffffffff841664ffffffffff8416613cae565b64ffffffffff8181168382160290811690818114613dba57613dba613655565b5092915050565b81515f90829060208501835b828110156138385781516001600160a01b0316845260209384019390910190600101613dcd56fea164736f6c634300081c000a", + "deployedBytecode": "0x608060405234801561000f575f5ffd5b506004361061024a575f3560e01c80639a7a2ffc11610140578063da881e5a116100bf578063ebf0c71711610084578063ebf0c7171461058a578063f165053614610592578063f2fde38b146105ac578063f379b0df146105bf578063f52fd803146105f9578063f6fc05d514610669575f5ffd5b8063da881e5a1461052c578063dbb06c931461053f578063e59e469514610551578063e6745e1314610564578063e82f3b7014610577575f5ffd5b8063c2b40ae411610105578063c2b40ae4146104b7578063c3a0ec30146104d6578063c6b2a438146104e7578063ca2869a0146104fa578063cd6dc68714610519575f5ffd5b80639a7a2ffc1461042c5780639f0f874a14610468578063a016493014610471578063a8a4d69b14610491578063bff232c1146104a4575f5ffd5b806370e36bbe116101cc5780638cb89ecb116101915780638cb89ecb146103c95780638d1ddfb1146103e85780638da5cb5b146103fe5780638e5ce3ad146104065780639015d37114610419575f5ffd5b806370e36bbe1461034e578063715018a6146103615780637c92f5241461036957806385814243146103965780638a78bb15146103b6575f5ffd5b8063291a691b11610212578063291a691b146102d05780632e7b716d146102f35780634d6861a61461030657806350e6d94c146103195780635d5047761461033b575f5ffd5b8063096b810a1461024e578063099a161a146102635780630f3e34121461028957806317d611201461029c5780632800d829146102bd575b5f5ffd5b61026161025c3660046133b3565b610672565b005b6102766102713660046133ce565b6107be565b6040519081526020015b60405180910390f35b6102616102973660046133ce565b6107f7565b6102af6102aa3660046133ce565b61083a565b604051610280929190613458565b6102766102cb3660046133ce565b6109e3565b6102e36102de366004613485565b610a2f565b6040519015158152602001610280565b6102e36103013660046133b3565b610c09565b6102e36103143660046133ce565b610cbc565b6102e36103273660046133b3565b60066020525f908152604090205460ff1681565b6102e36103493660046134be565b610cfb565b61026161035c3660046133b3565b610d3e565b610261610db4565b61037c6103773660046134ec565b610dc7565b6040805192835263ffffffff909116602083015201610280565b6001546103a9906001600160a01b031681565b6040516102809190613521565b6102616103c43660046133b3565b610f6e565b6102766103d73660046133ce565b60096020525f908152604090205481565b600454600160281b900464ffffffffff16610276565b6103a96110ac565b600b546103a9906001600160a01b031681565b6102e36104273660046133b3565b6110da565b61045261043a3660046133b3565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff9091168152602001610280565b61027660035481565b61048461047f3660046133ce565b6110f7565b6040516102809190613535565b6102e361049f3660046134be565b61118d565b6102616104b23660046133b3565b6111d0565b6102766104c53660046133ce565b60086020525f908152604090205481565b6001546001600160a01b03166103a9565b6102616104f536600461358b565b611248565b6102766105083660046133ce565b5f9081526008602052604090205490565b61026161052736600461360b565b61153e565b6102e361053a3660046133ce565b61169b565b5f546103a9906001600160a01b031681565b61026161055f3660046133b3565b611975565b610261610572366004613635565b6119ed565b6102766105853660046133ce565b611bb0565b610276611be1565b61059a601481565b60405160ff9091168152602001610280565b6102616105ba3660046133b3565b611bf3565b6004546105db9064ffffffffff80821691600160281b90041682565b6040805164ffffffffff938416815292909116602083015201610280565b61063a6106073660046133ce565b5f908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b604051610280949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b61027660025481565b61067a6110ac565b6001600160a01b0316336001600160a01b031614806106a357506001546001600160a01b031633145b6106c057604051632864c4e160e01b815260040160405180910390fd5b6106c9816110da565b81906106f2576040516381e5828960e01b81526004016106e99190613521565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906107209060049083611c2d565b6001600160a01b0382165f908152600660205260408120805460ff19169055600280549161074d83613669565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b5f818152600a6020526040812060048101546107ed576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b6107ff611ecf565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602052604090206006810154600b82015460609283929091806001600160401b038111156108705761087061367e565b604051908082528060200260200182016040528015610899578160200160208202803683370190505b509450806001600160401b038111156108b4576108b461367e565b6040519080825280602002602001820160405280156108dd578160200160208202803683370190505b5093505f805b838110156109d9575f85600601828154811061090157610901613692565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f908152600a8801602052604090205460ff166002811115610947576109476136a6565b036109d0578088848151811061095f5761095f613692565b60200260200101906001600160a01b031690816001600160a01b031681525050856009015f826001600160a01b03166001600160a01b031681526020019081526020015f20548784815181106109b7576109b7613692565b6020908102919091010152826109cc816136ba565b9350505b506001016108e3565b5050505050915091565b5f818152600a6020526040812081815460ff166003811115610a0757610a076136a6565b03610a2557604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610a5a5760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610a7e57610a7e6136a6565b14610a9c576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610ae3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b0791906136d2565b905080610b1a60408601602087016136fc565b63ffffffff161115610b3260408601602087016136fc565b829091610b60576040516344ec930f60e01b815263ffffffff909216600483015260248201526044016106e9565b5050815460ff1916600190811783558201859055436002830155600354610b879042613715565b6003830155610b9b600583018560026132ea565b50610ba4611be1565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610bf5928a928a9291613728565b60405180910390a250600195945050505050565b5f610c13826110da565b610c1e57505f919050565b6001546001600160a01b0316610c47576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610c77908590600401613521565b602060405180830381865afa158015610c92573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cb69190613787565b92915050565b5f818152600a602052604081206001815460ff166003811115610ce157610ce16136a6565b14610cee57505f92915050565b6003015442111592915050565b5f60015f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff166002811115610d3657610d366136a6565b149392505050565b610d46611ecf565b6001600160a01b038116610d6d5760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b610dbc611ecf565b610dc55f611f01565b565b600b545f9081906001600160a01b03163314610df65760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff166003811115610e1b57610e1b6136a6565b14610e3957604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f908152600a8301602052604090205463ffffffff909116925060019060ff166002811115610e7957610e796136a6565b14610e8957600b01549150610f66565b6001600160a01b0385165f908152600a820160205260408120805460ff19166002179055600b8201805491610ebd83613669565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051610f0e929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b610f766110ac565b6001600160a01b0316336001600160a01b03161480610f9f57506001546001600160a01b031633145b610fbc57604051632864c4e160e01b815260040160405180910390fd5b610fc5816110da565b6110a95760048054600160281b900464ffffffffff1690610fef906001600160a01b038416611f71565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff199091161790556002805491611040836136ba565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db53906060016107b2565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a6020526040902060048101546060919061112a576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561118057602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611162575b5050505050915050919050565b5f805f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156111c7576111c76136a6565b14159392505050565b6111d8611ecf565b6001600160a01b0381166111ff5760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b5f868152600a602052604090206002815460ff16600381111561126d5761126d6136a6565b1461128b57604051634f4b461f60e11b815260040160405180910390fd5b6004810154156112ae5760405163632a22bb60e01b815260040160405180910390fd5b836112f35760405162461bcd60e51b81526020600482015260156024820152741c1ad0dbdb5b5a5d1b595b9d081c995c5d5a5c9959605a1b60448201526064016106e9565b5f61130082600601612147565b600783018190555f805460405163101bb4d760e21b8152600481018c905292935090916001600160a01b039091169063406ed35c906024015f60405180830381865afa158015611352573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261137991908101906138e7565b9050806101c001511561147a57836113c45760405162461bcd60e51b815260206004820152600e60248201526d1c1c9bdbd9881c995c5d5a5c995960921b60448201526064016106e9565b8061012001516001600160a01b031663de12c640878488886040518563ffffffff1660e01b81526004016113fb9493929190613a6d565b602060405180830381865afa158015611416573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061143a9190613787565b61147a5760405162461bcd60e51b815260206004820152601160248201527024b73b30b634b2102225a390383937b7b360791b60448201526064016106e9565b60048381018790555f8a815260096020526040808220899055905490516340a3b76160e11b81529182018b9052602482018890526001600160a01b0316906381476ec2906044015f604051808303815f87803b1580156114d8575f5ffd5b505af11580156114ea573d5f5f3e3d5ffd5b50505050887fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018a8a8a8a8a60405161152b96959493929190613ad2565b60405180910390a2505050505050505050565b5f6115476121a8565b805490915060ff600160401b82041615906001600160401b03165f8115801561156d5750825b90505f826001600160401b031660011480156115885750303b155b905081158015611596575080155b156115b45760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156115de57845460ff60401b1916600160401b1785555b6001600160a01b0387166116055760405163d92e233d60e01b815260040160405180910390fd5b61160e336121d0565b61161a600460146121e1565b611623866107f7565b61162b6110ac565b6001600160a01b0316876001600160a01b03161461164c5761164c87611bf3565b831561169257845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff1660038111156116bf576116bf6136a6565b036116dd57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156116f5576116f56136a6565b1461171357604051631860f69960e31b815260040160405180910390fd5b8060030154421161173757604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff1611158061181c578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b1580156117fd575f5ffd5b505af115801561180f573d5f5f3e3d5ffd5b505f979650505050505050565b815460ff191660021782556006820154600b83018190555f816001600160401b0381111561184c5761184c61367e565b604051908082528060200260200182016040528015611875578160200160208202803683370190505b5090505f5b828110156118e757846009015f86600601838154811061189c5761189c613692565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205482518390839081106118d4576118d4613692565b602090810291909101015260010161187a565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b15801561192a575f5ffd5b505af115801561193c573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610bf5929190613b1f565b61197d611ecf565b6001600160a01b0381166119a45760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff166003811115611a1157611a116136a6565b03611a2f57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611a4757611a476136a6565b14611a6557604051631860f69960e31b815260040160405180910390fd5b8060030154421115611a8a57604051639a19114d60e01b815260040160405180910390fd5b335f90815260088201602052604090205460ff1615611abc5760405163257309f160e11b815260040160405180910390fd5b611ac533610c09565b611ae25760405163149fbcfd60e11b815260040160405180910390fd5b611aed338385612260565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526008850160205260409020805460ff19166001179055909150611b6c90839083612431565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480611bdc576040516322e679e360e11b815260040160405180910390fd5b919050565b5f611bee60046014612632565b905090565b611bfb611ecf565b6001600160a01b038116611c24575f604051631e4fbdf760e01b81526004016106e99190613521565b6110a981611f01565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611c6c5760405162461bcd60e51b81526004016106e990613b31565b825464ffffffffff600160281b90910481169082168111611cca5760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b60448201526064016106e9565b825f5b81866001015f611cdd848861272b565b64ffffffffff1681526020019081526020015f20819055505f816001611d039190613b7b565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611d385750611ec7565b600185165f03611dff575f611d5783611d52886001613b94565b61272b565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611db891600401613bb1565b602060405180830381865af4158015611dd3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611df791906136d2565b935050611eb3565b5f611e0f83611d52600189613be1565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611e7091600401613bb1565b602060405180830381865af4158015611e8b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611eaf91906136d2565b9350505b50647fffffffff600194851c169301611ccd565b505050505050565b33611ed86110ac565b6001600160a01b031614610dc5573360405163118cdaa760e01b81526004016106e99190613521565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611fc05760405162461bcd60e51b81526004016106e990613b31565b825464ffffffffff908116908216106120135760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b60448201526064016106e9565b61201e816001613b94565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f612055848761272b565b64ffffffffff16815260208101919091526040015f20556001831615612140575f61208582611d52600187613be1565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916120e691600401613bb1565b602060405180830381865af4158015612101573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061212591906136d2565b647fffffffff600195861c1694909350919091019050612045565b5050505050565b5f610cb68280548060200260200160405190810160405280929190818152602001828054801561219e57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311612180575b5050505050612748565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610cb6565b6121d8612777565b6110a98161279c565b602060ff8216111561222f5760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b60448201526064016106e9565b612240600160ff831681901b613bfe565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b5f82116122805760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b03166122a9576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd719188916122df91613bfe565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612326573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061234a91906136d2565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa15801561239d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123c191906136d2565b90505f81116123e35760405163aeaddff160e01b815260040160405180910390fd5b5f6123ee8284613c11565b90505f81116124105760405163149fbcfd60e11b815260040160405180910390fd5b808611156116925760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff16908111156124af57508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff191682179055905061262b565b5f5f90505f876009015f855f815481106124cb576124cb613692565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612553575f896009015f87848154811061251557612515613692565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205490508281111561254a578092508193505b506001016124f4565b50808610612567575f94505050505061262b565b5f88600a015f86858154811061257f5761257f613692565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff191660018360028111156125bc576125bc6136a6565b0217905550868483815481106125d4576125d4613692565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff16116126855760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e203000000000000060448201526064016106e9565b602060ff831611156126a95760405162461bcd60e51b81526004016106e990613c30565b8254600160281b900464ffffffffff16806126c860ff85166002613d81565b64ffffffffff1610156127185760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b60448201526064016106e9565b6127238482856127a4565b949350505050565b5f8161273e60ff851663ffffffff613d9a565b61262b9190613b94565b5f8160405160200161275a9190613dc1565b604051602081830303815290604052805190602001209050919050565b61277f61286c565b610dc557604051631afcd79f60e31b815260040160405180910390fd5b611bfb612777565b5f602060ff831611156127c95760405162461bcd60e51b81526004016106e990613c30565b8264ffffffffff165f036127e7576127e082612885565b905061262b565b5f6127f3836001613b7b565b60ff166001600160401b0381111561280d5761280d61367e565b604051908082528060200260200182016040528015612836578160200160208202803683370190505b50905061284585858584612f1f565b808360ff168151811061285a5761285a613692565b60200260200101519150509392505050565b5f6128756121a8565b54600160401b900460ff16919050565b5f8160ff165f0361289757505f919050565b8160ff166001036128c957507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff166002036128fb57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361292d57507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361295f57507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361299157507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036129c357507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff166007036129f557507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff16600803612a2757507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff16600903612a5957507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a03612a8b57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b03612abd57507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c03612aef57507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d03612b2157507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e03612b5357507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f03612b8557507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff16601003612bb757507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff16601103612be957507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff16601203612c1b57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff16601303612c4d57507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff16601403612c7f57507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff16601503612cb157507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff16601603612ce357507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff16601703612d1557507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff16601803612d4757507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff16601903612d7957507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a03612dab57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03612ddd57507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03612e0f57507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03612e4157507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03612e7357507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03612ea557507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff16602003612ed757507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e646578000060448201526064016106e9565b602060ff83161115612f435760405162461bcd60e51b81526004016106e990613c30565b5f8364ffffffffff1611612fa75760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b60648201526084016106e9565b5f612fb3600185613be1565b9050600181165f0361300657846001015f612fce5f8461272b565b64ffffffffff1681526020019081526020015f2054825f81518110612ff557612ff5613692565b60200260200101818152505061302e565b61300f5f612885565b825f8151811061302157613021613692565b6020026020010181815250505b5f5b8360ff168160ff161015611ec757600182165f036131265773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff168151811061308257613082613692565b6020026020010151815260200161309885612885565b8152506040518263ffffffff1660e01b81526004016130b79190613bb1565b602060405180830381865af41580156130d2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130f691906136d2565b83613102836001613b7b565b60ff168151811061311557613115613692565b6020026020010181815250506132d7565b5f613132826001613b7b565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff168111156131d4575f876001015f6131898560016131789190613b7b565b60018864ffffffffff16901c61272b565b64ffffffffff1681526020019081526020015f2054905080858460016131af9190613b7b565b60ff16815181106131c2576131c2613692565b602002602001018181525050506132d5565b5f876001015f6131eb85600188611d529190613be1565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff168151811061324257613242613692565b60200260200101518152506040518263ffffffff1660e01b81526004016132699190613bb1565b602060405180830381865af4158015613284573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132a891906136d2565b856132b4856001613b7b565b60ff16815181106132c7576132c7613692565b602002602001018181525050505b505b647fffffffff600192831c169101613030565b60018301918390821561337b579160200282015f5b8382111561334957833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff16021790555092602001926004016020816003010492830192600103026132ff565b80156133795782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613349565b505b5061338792915061338b565b5090565b5b80821115613387575f815560010161338c565b6001600160a01b03811681146110a9575f5ffd5b5f602082840312156133c3575f5ffd5b813561262b8161339f565b5f602082840312156133de575f5ffd5b5035919050565b5f8151808452602084019350602083015f5b8281101561341e5781516001600160a01b03168652602095860195909101906001016133f7565b5093949350505050565b5f8151808452602084019350602083015f5b8281101561341e57815186526020958601959091019060010161343a565b604081525f61346a60408301856133e5565b828103602084015261347c8185613428565b95945050505050565b5f5f5f60808486031215613497575f5ffd5b8335925060208401359150608084018510156134b1575f5ffd5b6040840190509250925092565b5f5f604083850312156134cf575f5ffd5b8235915060208301356134e18161339f565b809150509250929050565b5f5f5f606084860312156134fe575f5ffd5b8335925060208401356135108161339f565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b602081525f61262b60208301846133e5565b5f5f83601f840112613557575f5ffd5b5081356001600160401b0381111561356d575f5ffd5b602083019150836020828501011115613584575f5ffd5b9250929050565b5f5f5f5f5f5f608087890312156135a0575f5ffd5b8635955060208701356001600160401b038111156135bc575f5ffd5b6135c889828a01613547565b9096509450506040870135925060608701356001600160401b038111156135ed575f5ffd5b6135f989828a01613547565b979a9699509497509295939492505050565b5f5f6040838503121561361c575f5ffd5b82356136278161339f565b946020939093013593505050565b5f5f60408385031215613646575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f8161367757613677613655565b505f190190565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b5f600182016136cb576136cb613655565b5060010190565b5f602082840312156136e2575f5ffd5b5051919050565b803563ffffffff81168114611bdc575f5ffd5b5f6020828403121561370c575f5ffd5b61262b826136e9565b80820180821115610cb657610cb6613655565b84815260a0810160208201855f5b60028110156137635763ffffffff61374d836136e9565b1683526020928301929190910190600101613736565b50505060608201939093526080015292915050565b80518015158114611bdc575f5ffd5b5f60208284031215613797575f5ffd5b61262b82613778565b6040516101e081016001600160401b03811182821017156137c3576137c361367e565b60405290565b805160048110611bdc575f5ffd5b5f82601f8301126137e6575f5ffd5b604080519081016001600160401b03811182821017156138085761380861367e565b806040525080604084018581111561381e575f5ffd5b845b81811015613838578051835260209283019201613820565b509195945050505050565b8051611bdc8161339f565b805160ff81168114611bdc575f5ffd5b5f82601f83011261386d575f5ffd5b81516001600160401b038111156138865761388661367e565b604051601f8201601f19908116603f011681016001600160401b03811182821017156138b4576138b461367e565b6040528181528382016020018510156138cb575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f602082840312156138f7575f5ffd5b81516001600160401b0381111561390c575f5ffd5b8201610200818503121561391e575f5ffd5b6139266137a0565b81518152613936602083016137c9565b60208201526040828101519082015261395285606084016137d7565b606082015260a0820151608082015261396d60c08301613843565b60a082015261397e60e0830161384e565b60c08201526101008201516001600160401b0381111561399c575f5ffd5b6139a88682850161385e565b60e0830152506139bb6101208301613843565b6101008201526139ce6101408301613843565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613a04575f5ffd5b613a108682850161385e565b61018083015250613a246101c08301613843565b6101a0820152613a376101e08301613778565b6101c0820152949350505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b848152836020820152606060408201525f613a8c606083018486613a45565b9695505050505050565b5f8154808452602084019350825f5260205f205f5b8281101561341e5781546001600160a01b0316865260209095019460019182019101613aab565b608081525f613ae46080830189613a96565b8281036020840152613af781888a613a45565b90508560408401528281036060840152613b12818587613a45565b9998505050505050505050565b604081525f61346a6040830185613a96565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610cb657610cb6613655565b64ffffffffff8181168382160190811115610cb657610cb6613655565b6040810181835f5b6002811015613bd8578151835260209283019290910190600101613bb9565b50505092915050565b64ffffffffff8281168282160390811115610cb657610cb6613655565b81810381811115610cb657610cb6613655565b5f82613c2b57634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b6001841115610f6657808504811115613c9257613c92613655565b6001841615613ca057908102905b60019390931c928002613c77565b5f82613cbc57506001610cb6565b81613cc857505f610cb6565b8160018114613cde5760028114613ce857613d1a565b6001915050610cb6565b60ff841115613cf957613cf9613655565b6001841b915064ffffffffff821115613d1457613d14613655565b50610cb6565b5060208310610133831016604e8410600b8410161715613d52575081810a64ffffffffff811115613d4d57613d4d613655565b610cb6565b613d6264ffffffffff8484613c73565b8064ffffffffff04821115613d7957613d79613655565b029392505050565b5f61262b64ffffffffff841664ffffffffff8416613cae565b64ffffffffff8181168382160290811690818114613dba57613dba613655565b5092915050565b81515f90829060208501835b828110156138385781516001600160a01b0316845260209384019390910190600101613dcd56fea164736f6c634300081c000a", "linkReferences": { "npm/poseidon-solidity@0.0.5/PoseidonT3.sol": { "PoseidonT3": [ { "length": 20, - "start": 7752 + "start": 7784 }, { "length": 20, - "start": 7936 + "start": 7968 }, { "length": 20, - "start": 8566 + "start": 8598 }, { "length": 20, - "start": 12400 + "start": 12576 }, { "length": 20, - "start": 12842 + "start": 13018 } ] } @@ -1316,28 +1330,28 @@ "PoseidonT3": [ { "length": 20, - "start": 7538 + "start": 7570 }, { "length": 20, - "start": 7722 + "start": 7754 }, { "length": 20, - "start": 8352 + "start": 8384 }, { "length": 20, - "start": 12186 + "start": 12362 }, { "length": 20, - "start": 12628 + "start": 12804 } ] } }, "immutableReferences": {}, "inputSourceName": "project/contracts/registry/CiphernodeRegistryOwnable.sol", - "buildInfoId": "solc-0_8_28-2705a75bc2d2d1f8b1e08ebca4cc37d76480abc8" + "buildInfoId": "solc-0_8_28-e33d1cfedc69d8ad74eec5a84c8ef358a020ec1a" } \ No newline at end of file diff --git a/packages/enclave-contracts/contracts/Enclave.sol b/packages/enclave-contracts/contracts/Enclave.sol index b469cb097..29cf0103c 100644 --- a/packages/enclave-contracts/contracts/Enclave.sol +++ b/packages/enclave-contracts/contracts/Enclave.sol @@ -419,8 +419,10 @@ contract Enclave is IEnclave, OwnableUpgradeable { if (e3.proofAggregationEnabled) { require(proof.length > 0, ProofRequired()); + bytes32 committeeHash = ciphernodeRegistry.getCommitteeHash(e3Id); success = e3.decryptionVerifier.verify( keccak256(plaintextOutput), + committeeHash, proof ); require(success, InvalidOutput(plaintextOutput)); diff --git a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol index aa574cf1f..9172ab4c7 100644 --- a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol @@ -38,6 +38,7 @@ interface ICiphernodeRegistry { /// @param publicKey Hash of the committee's public key. /// @param seed The seed for the round. /// @param topNodes Sorted top-N nodes selected during sortition. + /// @param committeeHash `keccak256(abi.encodePacked(topNodes))`, set at publication. /// @param submitted Mapping of nodes to their submission status. /// @param scoreOf Mapping of nodes to their scores. /// @param memberStatus Tri-state membership tracking (None / Active / Expelled). @@ -49,6 +50,7 @@ interface ICiphernodeRegistry { bytes32 publicKey; uint32[2] threshold; address[] topNodes; + bytes32 committeeHash; mapping(address node => bool submitted) submitted; mapping(address node => uint256 score) scoreOf; mapping(address node => MemberStatus) memberStatus; @@ -304,14 +306,12 @@ interface ICiphernodeRegistry { /// `pkCommitment` via the E3's pk verifier. When disabled, `proof` may be empty /// and `pkCommitment` is trusted from the (signed) aggregator. /// @param e3Id ID of the E3 for which to select the committee. - /// @param nodes Array of ciphernode addresses selected for the committee. /// @param publicKey The public key generated by the given committee. /// @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. function publishCommittee( uint256 e3Id, - address[] calldata nodes, bytes calldata publicKey, bytes32 pkCommitment, bytes calldata proof @@ -334,6 +334,13 @@ interface ICiphernodeRegistry { uint256 e3Id ) external view returns (address[] memory committeeNodes); + /// @notice Returns the committee hash cached at `publishCommittee` time. + /// @param e3Id ID of the E3 + /// @return committeeHash `keccak256(abi.encodePacked(topNodes))` for the published committee + function getCommitteeHash( + uint256 e3Id + ) external view returns (bytes32 committeeHash); + /// @notice Returns the current root of the ciphernode IMT /// @return Current IMT root function root() external view returns (uint256); diff --git a/packages/enclave-contracts/contracts/interfaces/IDecryptionVerifier.sol b/packages/enclave-contracts/contracts/interfaces/IDecryptionVerifier.sol index 1519032e3..b52efb6b4 100644 --- a/packages/enclave-contracts/contracts/interfaces/IDecryptionVerifier.sol +++ b/packages/enclave-contracts/contracts/interfaces/IDecryptionVerifier.sol @@ -13,12 +13,14 @@ pragma solidity 0.8.28; * needs to verify the final EVM proof and bind it to the claimed plaintext. */ interface IDecryptionVerifier { - /// @notice Verify a DecryptionAggregator EVM proof and bind it to `plaintextOutputHash`. + /// @notice Verify a DecryptionAggregator EVM proof and bind it to `plaintextOutputHash` and `committeeHash`. /// @param plaintextOutputHash `keccak256(plaintextOutput)` expected by the Enclave. + /// @param committeeHash `keccak256(abi.encodePacked(topNodes))` for the on-chain committee. /// @param proof ABI-encoded `(bytes rawProof, bytes32[] publicInputs)`. - /// @return success True if the proof is valid and its embedded plaintext matches `plaintextOutputHash`. + /// @return success True if the proof is valid and bound to `plaintextOutputHash` and `committeeHash`. function verify( bytes32 plaintextOutputHash, + bytes32 committeeHash, bytes calldata proof ) external view returns (bool success); } diff --git a/packages/enclave-contracts/contracts/interfaces/IPkVerifier.sol b/packages/enclave-contracts/contracts/interfaces/IPkVerifier.sol index 98c3c7667..7984851c6 100644 --- a/packages/enclave-contracts/contracts/interfaces/IPkVerifier.sol +++ b/packages/enclave-contracts/contracts/interfaces/IPkVerifier.sol @@ -14,13 +14,15 @@ pragma solidity 0.8.28; * matches the committee's aggregated public-key commitment. */ interface IPkVerifier { - /// @notice Verify a DkgAggregator EVM proof and bind it to `pkCommitment`. + /// @notice Verify a DkgAggregator EVM proof and bind it to `pkCommitment` and `committeeHash`. /// @param pkCommitment Hash-based aggregated PK commitment the proof must attest to /// (equals `publicInputs[publicInputs.length - 1]`). + /// @param committeeHash `keccak256(abi.encodePacked(topNodes))` for the on-chain committee. /// @param proof ABI-encoded `(bytes rawProof, bytes32[] publicInputs)`. - /// @return success True if the proof is valid and its last public input equals `pkCommitment`. + /// @return success True if the proof is valid and bound to `pkCommitment` and `committeeHash`. function verify( bytes32 pkCommitment, + bytes32 committeeHash, bytes calldata proof ) external view returns (bool success); } diff --git a/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol b/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol new file mode 100644 index 000000000..6f3fc3a9c --- /dev/null +++ b/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol @@ -0,0 +1,36 @@ +// 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 CommitteeHashLib + * @notice Canonical `keccak256(abi.encodePacked(topNodes))` binding for aggregator proofs. + * @dev Must match `e3_utils::committee_hash` (hi/lo split into two 128-bit limbs). + */ +library CommitteeHashLib { + uint256 private constant _LO_MASK = (uint256(1) << 128) - 1; + + /// @notice `keccak256(abi.encodePacked(nodes))` for the ordered on-chain committee. + function hash(address[] storage nodes) internal view returns (bytes32) { + return hashMemory(nodes); + } + + /// @notice Same as {hash} for calldata/memory address lists. + function hashMemory(address[] memory nodes) internal pure returns (bytes32) { + return keccak256(abi.encodePacked(nodes)); + } + + /// @notice High 128 bits of a committee hash (Noir public input `committee_hash_hi`). + function hi(bytes32 committeeHash) internal pure returns (bytes32) { + return bytes32(uint256(committeeHash) >> 128); + } + + /// @notice Low 128 bits of a committee hash (Noir public input `committee_hash_lo`). + function lo(bytes32 committeeHash) internal pure returns (bytes32) { + return bytes32(uint256(committeeHash) & _LO_MASK); + } +} diff --git a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol index 15fb028bb..aaff9c383 100644 --- a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol +++ b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol @@ -17,6 +17,7 @@ import { InternalLazyIMT, LazyIMTData } from "@zk-kit/lazy-imt.sol/InternalLazyIMT.sol"; +import { CommitteeHashLib } from "../lib/CommitteeHashLib.sol"; /** * @title CiphernodeRegistryOwnable @@ -195,7 +196,6 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { /// @inheritdoc ICiphernodeRegistry function publishCommittee( uint256 e3Id, - address[] calldata nodes, bytes calldata publicKey, bytes32 pkCommitment, bytes calldata proof @@ -207,14 +207,16 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { CommitteeNotFinalized() ); require(c.publicKey == bytes32(0), CommitteeAlreadyPublished()); - require(nodes.length == c.topNodes.length, "Node count mismatch"); require(pkCommitment != bytes32(0), "pkCommitment required"); + bytes32 committeeHash = CommitteeHashLib.hash(c.topNodes); + c.committeeHash = committeeHash; + E3 memory e3 = enclave.getE3(e3Id); if (e3.proofAggregationEnabled) { require(proof.length > 0, "proof required"); require( - e3.pkVerifier.verify(pkCommitment, proof), + e3.pkVerifier.verify(pkCommitment, committeeHash, proof), "Invalid DKG proof" ); } @@ -224,7 +226,13 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { enclave.onCommitteePublished(e3Id, pkCommitment); - emit CommitteePublished(e3Id, nodes, publicKey, pkCommitment, proof); + emit CommitteePublished( + e3Id, + c.topNodes, + publicKey, + pkCommitment, + proof + ); } /// @inheritdoc ICiphernodeRegistry @@ -464,6 +472,15 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { nodes = c.topNodes; } + /// @inheritdoc ICiphernodeRegistry + function getCommitteeHash( + uint256 e3Id + ) public view returns (bytes32 committeeHash) { + Committee storage c = committees[e3Id]; + require(c.publicKey != bytes32(0), CommitteeNotPublished()); + committeeHash = c.committeeHash; + } + /// @notice Returns the current size of the ciphernode IMT /// @return Size of the IMT function treeSize() public view returns (uint256) { diff --git a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol index 558927152..ab6a8eaa4 100644 --- a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol @@ -68,7 +68,6 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry { function publishCommittee( uint256, - address[] calldata, bytes calldata, bytes32, bytes calldata @@ -80,6 +79,12 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry { return _committeeNodes[e3Id]; } + function getCommitteeHash( + uint256 e3Id + ) external view returns (bytes32) { + return keccak256(abi.encodePacked(_committeeNodes[e3Id])); + } + function root() external pure returns (uint256) { return 0; } @@ -209,7 +214,6 @@ contract MockCiphernodeRegistryEmptyKey is ICiphernodeRegistry { function publishCommittee( uint256, - address[] calldata, bytes calldata, bytes32, bytes calldata @@ -222,6 +226,10 @@ contract MockCiphernodeRegistryEmptyKey is ICiphernodeRegistry { return nodes; } + function getCommitteeHash(uint256) external pure returns (bytes32) { + return bytes32(0); + } + function root() external pure returns (uint256) { return 0; } diff --git a/packages/enclave-contracts/contracts/test/MockDecryptionVerifier.sol b/packages/enclave-contracts/contracts/test/MockDecryptionVerifier.sol index 6b8ed8b99..b55542f71 100644 --- a/packages/enclave-contracts/contracts/test/MockDecryptionVerifier.sol +++ b/packages/enclave-contracts/contracts/test/MockDecryptionVerifier.sol @@ -13,6 +13,7 @@ contract MockDecryptionVerifier is IDecryptionVerifier { bytes4 private constant _FAIL_MAGIC = 0xdeadbeef; function verify( + bytes32, bytes32, bytes calldata proof ) external pure returns (bool success) { diff --git a/packages/enclave-contracts/contracts/test/MockPkVerifier.sol b/packages/enclave-contracts/contracts/test/MockPkVerifier.sol index bd54d16ab..d96359c95 100644 --- a/packages/enclave-contracts/contracts/test/MockPkVerifier.sol +++ b/packages/enclave-contracts/contracts/test/MockPkVerifier.sol @@ -10,6 +10,7 @@ import { IPkVerifier } from "../interfaces/IPkVerifier.sol"; contract MockPkVerifier is IPkVerifier { function verify( bytes32 pkCommitment, + bytes32, bytes calldata proof ) external pure returns (bool) { (, bytes32[] memory publicInputs) = abi.decode( diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol index ec5afc3f3..097a3545e 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol @@ -7,13 +7,14 @@ pragma solidity 0.8.28; import { IDecryptionVerifier } from "../../interfaces/IDecryptionVerifier.sol"; import { ICircuitVerifier } from "../../interfaces/ICircuitVerifier.sol"; +import { CommitteeHashLib } from "../../lib/CommitteeHashLib.sol"; /** * @title BfvDecryptionVerifier * @notice Verifies the DecryptionAggregator (EVM) proof produced by the * recursive aggregation pipeline (C6 folds + C7/decrypted_shares * verified internally). Binds the proof to the claimed - * `plaintextOutputHash`. + * `plaintextOutputHash` and on-chain committee hash. * @dev Used when the Enclave is configured with encryptionSchemeId * keccak256("fhe.rs:BFV"). The plaintext is exposed as the last * `MESSAGE_COEFFS_COUNT` public inputs, matching @@ -23,6 +24,12 @@ contract BfvDecryptionVerifier is IDecryptionVerifier { /// @dev Message is always the last 100 public inputs (100 uint64 coeffs = 800 bytes plaintext). uint256 constant MESSAGE_COEFFS_COUNT = 100; + /// @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; @@ -45,6 +52,7 @@ contract BfvDecryptionVerifier is IDecryptionVerifier { /// @inheritdoc IDecryptionVerifier function verify( bytes32 plaintextOutputHash, + bytes32 committeeHash, bytes calldata proof ) external view override returns (bool) { (bytes memory rawProof, bytes32[] memory publicInputs) = abi.decode( @@ -52,7 +60,10 @@ contract BfvDecryptionVerifier is IDecryptionVerifier { (bytes, bytes32[]) ); - if (publicInputs.length < MESSAGE_COEFFS_COUNT + 2) { + if ( + publicInputs.length < + MESSAGE_COEFFS_COUNT + COMMITTEE_HASH_LO_IDX + 1 + ) { return false; } if (publicInputs[0] != expectedC6FoldKeyHash) { diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol index 8f2600096..2fe91884c 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol @@ -7,241 +7,125 @@ pragma solidity >=0.8.21; uint256 constant N = 2097152; uint256 constant LOG_N = 21; -uint256 constant NUMBER_OF_PUBLIC_INPUTS = 125; -uint256 constant VK_HASH = 0x06bd2790bd0ebcdac35c8a64701d3ac99c897bfb988d6a9fa2b1f07a6630a73c; +uint256 constant NUMBER_OF_PUBLIC_INPUTS = 127; +uint256 constant VK_HASH = 0x02734bf64581e3f98e47575b06750fc45ec81a34079564e4a7326a3d547d2b90; 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(125), - ql: Honk.G1Point({ - x: uint256( - 0x117c944457d4c445ea9296e8369c57035aaa6aa1a8d8961945b3265da72a31bd - ), - y: uint256( - 0x1085aa2ad3f5c33ba5b8281d03fc61950c0efb18edf3ca4bbacab6dac95eecc9 - ) + publicInputsSize: uint256(127), + ql: Honk.G1Point({ + x: uint256(0x22f1675e1cbb2fcd4d4b892e591f16c3201a876c34d711e86159aa593f8957df), + y: uint256(0x06b38fe5bca0f4814e532d26345b64ddd5fdbc72c4171889f9c4cad61139787d) }), - qr: Honk.G1Point({ - x: uint256( - 0x2167efafb6f18a256ec49ac455de2b906ff483dd58e50c27a3eb01c965527b58 - ), - y: uint256( - 0x0e3673e8a48e9d2cdb54886bbd28ad0dd56f51ef8cb5b5b8d0c0586df815f0a0 - ) + qr: Honk.G1Point({ + x: uint256(0x0beaa5627f856b98b6c9300fd93f16d9de0a364e8319e818171fd4e0c0a5c91e), + y: uint256(0x2d884ebc1127945f836920004f1499ed01afd2fc89691f0f96f7a928a9998fc6) }), - qo: Honk.G1Point({ - x: uint256( - 0x0a0501c4ac048110cf75f2b20f9ffdcd9a68876fd2e5d222b64c7bcac4915773 - ), - y: uint256( - 0x06d4c7296e366ae470e2744f7c35c10a36205e875d76ff49b031643a34ca4b92 - ) + qo: Honk.G1Point({ + x: uint256(0x02e50bcff38f3c94d748e3ce7b80bb26019c24fd8c64c55e3469cc3ddf138b8e), + y: uint256(0x248ca1123436cf2e53bdd9ed5e3e594213a59785ae39c6218eb5e28aedf552dc) }), - q4: Honk.G1Point({ - x: uint256( - 0x1cb87c34cda7588d707dfd9e5edc22aebcc4082a9a53bcd5cde3bee60468e8b0 - ), - y: uint256( - 0x05ff1a3b7cee6850d9d747a8da9eded3048f5d04bc0e3bc9430691c03e1e27c2 - ) + q4: Honk.G1Point({ + x: uint256(0x2be0a336b2241e8cc572c843dfe5de1bb1d26f2ffb244461939573c3910ae7e4), + y: uint256(0x18195c8e933be2a9c081ff5fcad1f3d362ed2408ca5bd683ecb76f831937bf8c) }), - qm: Honk.G1Point({ - x: uint256( - 0x202127ce81fb7649a03ec97775ca9fe2914220f389a449ddb636ba3d1176f3c3 - ), - y: uint256( - 0x267b8e078201ba78546404fa94714a5dcf759d079c3979f0a5ee5c132be90b45 - ) + qm: Honk.G1Point({ + x: uint256(0x2aaee69977fc70b11f35a9417bf0efc1b8fc3ed5ed1c6a14cb1e2f587b01618d), + y: uint256(0x167831b0a4fb0627b623ab3b229eac297d78d9ffb020801e31e4ba9ed288f00f) }), - qc: Honk.G1Point({ - x: uint256( - 0x24252d386e1956c3f2eec5c2523dd7f1f00acab8486d728cda31a43a4c67ee03 - ), - y: uint256( - 0x0bfc7d6c95dd2d204a8535c6c39293700dc0a10b7c436927ea67d06c5e092b47 - ) + qc: Honk.G1Point({ + x: uint256(0x0c8357f6962ce8370d3f5965652b63f972d8eed7766a7fefba8c443b69edab68), + y: uint256(0x238822fb432ce7bed5edcee885a9a75beec94f64602b27518e5bb66da54b4157) }), - qLookup: Honk.G1Point({ - x: uint256( - 0x2f67e65f1cc76c010e87bd861eff857a592e08ada281645410d67488ce521499 - ), - y: uint256( - 0x056e0fd959cdd56f2cb9d6d01440f7a5c2610ec3cb0efd64f856a119066366e9 - ) + qLookup: Honk.G1Point({ + x: uint256(0x29e47a139596dcf5450c2de10d5d121d883389ab7c0e930c1ad2f20283098fc1), + y: uint256(0x2548e6b14779657467f37fc862ec803097b1e328abf936d9b93438db371b5aff) }), - qArith: Honk.G1Point({ - x: uint256( - 0x0061573aad5b769a27e0a025f41dc6916969da4cb5e543584ab86b72c93e3014 - ), - y: uint256( - 0x18285aff8bc7ed9f8cd1ea037443a178c0e77d7bae05c91613529a85717a5473 - ) + qArith: Honk.G1Point({ + x: uint256(0x0c6962ed640803bfd254fe60f15bfbd3619695b639eaa50f8ad9bca8f26c1c67), + y: uint256(0x2a81c4ea9b5f20a9b7966ef5a0f095f1bf0fc03400c4328f06da00547e5ede6b) }), - qDeltaRange: Honk.G1Point({ - x: uint256( - 0x11fe5aa4683da4b17c27c36019fd04b6c4daaf06d1138c136a82676d532e6897 - ), - y: uint256( - 0x244c605f6af78086d243fb2fffd815776932922d3cf081fa8d2ff7145b65dc0b - ) + qDeltaRange: Honk.G1Point({ + x: uint256(0x12740b73a60d59b99352868b5ff15474d8d40676659ede6a613c2869926496e9), + y: uint256(0x2e92d9a28c1a2b6f6d5802abcfc28eb69fbf0f0fce55a7e60046d8dfc4fdf260) }), - qElliptic: Honk.G1Point({ - x: uint256( - 0x0e6398a099538b9c1f74c3a86c63d474fd2b934772171c785230674a79962144 - ), - y: uint256( - 0x1152647b290d29791357e57cf4252e23aa8bb69266de667fcb6d49b50833c0fa - ) + qElliptic: Honk.G1Point({ + x: uint256(0x300990501116cc8c9f5950d44ce76927d8443b777155a61e3cf6bb2e5060ca9d), + y: uint256(0x269ae09b2eee7365a0aed387b13e4ca9916e3ce2c75967672180345d33cb70b7) }), - qMemory: Honk.G1Point({ - x: uint256( - 0x2f77d58e1935021197cf807adc9d6ed89c208e7397f837811935ad8e04a25646 - ), - y: uint256( - 0x28ca0aea22ed60e0fda6a3087590deac2e3c6dbb39f672db617a2c8f7576a3a3 - ) + qMemory: Honk.G1Point({ + x: uint256(0x1c42c01418da4f6b73611ae563df3215901ab635ad3c850db84e1a9b13ab9235), + y: uint256(0x2d23fcba8d43a2ecc03a6af88af95e13ea8b3cca01165ee491dbb23a4ea4ffa5) }), - qNnf: Honk.G1Point({ - x: uint256( - 0x1ba222b35b1b79a59d990df8d802bbffd526ed44aa573cb7de03b4926e33cfda - ), - y: uint256( - 0x1e359c171885a972cb085a213975238e93b78b9b5067f6d470da0e47997cb560 - ) + qNnf: Honk.G1Point({ + x: uint256(0x1dfa93f3351f60495e8df8d87903805817c44f87a73b945c81a438be0facc481), + y: uint256(0x0e0341343e527ffedee24e4028bc14b8501adc68d0fce00c17ac11120d2df513) }), - qPoseidon2External: Honk.G1Point({ - x: uint256( - 0x1a8754cb4b93dbc97099cedc11c48286b8fb9ab103537f8872f0e44d05b6f677 - ), - y: uint256( - 0x25b8a35780ef8e6b2544f4b3c9b96d25df0f4f3f2eb652c0d3ba10ee3066dc06 - ) + qPoseidon2External: Honk.G1Point({ + x: uint256(0x048462f577ba1216245079d12c9d55487cf3b330eed20a66fe7db67d447fd72f), + y: uint256(0x1f06371d9dedf997f6dc1b5508b5fa620a05c3261e833bb43b88e9b6b6f5a979) }), - qPoseidon2Internal: Honk.G1Point({ - x: uint256( - 0x15dcd14e33a5c64ef3eef7938fccf400d798ec44f2a02fdf2e196dfa65f10c32 - ), - y: uint256( - 0x1d52fe9202be704fa45de182df518c960eca7b5c8c8f8f3ff92d6522b9efaa13 - ) + qPoseidon2Internal: Honk.G1Point({ + x: uint256(0x1189e97ee885b8a608dca70a417cfd3d339455452c9798752febedd0582f4977), + y: uint256(0x13860b3c5483cf0abe4d5ec8f382e455b20aa391dc315862abb5b49228bf7083) }), - s1: Honk.G1Point({ - x: uint256( - 0x168d4cfd6436be78fc713ef78e188f60e11008a4740fce4f64342bd82ced648b - ), - y: uint256( - 0x011b219a479e28c150896d9aed6042b687a10b2e3d725f5d08d7fea94a122f45 - ) + s1: Honk.G1Point({ + x: uint256(0x11b5d23e9c3c361c011754470be1828b314b3bc41dd92723edd773e0dc721f87), + y: uint256(0x12d77c6c17a5aa9ab610abb810fecdd5d3a20370c04f9a04969e1c7b51157f90) }), - s2: Honk.G1Point({ - x: uint256( - 0x2e416ff405b6c53bc74973defa708ccc60c6fe491349100cec7b1481979da38e - ), - y: uint256( - 0x14324c310091b0f31ddfcbbac474565ff58d178a307ffc8178cec436ccd7163c - ) + s2: Honk.G1Point({ + x: uint256(0x0ccfd83fc13c1e477918617d55ec0e302167fc9e35ac5e7933267dbf6b04225d), + y: uint256(0x0345818c0b610d75f563262999377e05bed55ebaff83f0eb7a9525394a67436d) }), - s3: Honk.G1Point({ - x: uint256( - 0x25133c6ea741cb3281d03c49f306b4f01fb6d338d27906e5409715ced74033f7 - ), - y: uint256( - 0x2a6c7c2db40a4d31607bd6f7d823207f1f5ed217b0784a085920722fd3ad36f4 - ) + s3: Honk.G1Point({ + x: uint256(0x18c9ccbc44c932d6d698e781e285674b7c169c4435df7bf6a41b56aebc8104f5), + y: uint256(0x145974be15fb78b2d78c93a97669c4501670649ecaf20a58a183b4199301cba7) }), - s4: Honk.G1Point({ - x: uint256( - 0x1609e2a7fef984bfb951cdaa3901af7d335fdde29d47d4cae8c3b9e7363134b4 - ), - y: uint256( - 0x1a6b3f2acc00e7396c9348d7dde2587bc1d0c8ee1b9c35465b02d16d5e23d3b7 - ) + s4: Honk.G1Point({ + x: uint256(0x1985589ee73cb2f44b4dbb33e470a1c996c5d06ed7baf10ddbfd507acb8684ae), + y: uint256(0x2d07e474573859fda8dc083c17728f59cddb11cd0e29be5651705f5dbddb669a) }), - t1: Honk.G1Point({ - x: uint256( - 0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26 - ), - y: uint256( - 0x0015b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f - ) + t1: Honk.G1Point({ + x: uint256(0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26), + y: uint256(0x0015b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f) }), - t2: Honk.G1Point({ - x: uint256( - 0x1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e - ), - y: uint256( - 0x305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d19 - ) + t2: Honk.G1Point({ + x: uint256(0x1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e), + y: uint256(0x305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d19) }), - t3: Honk.G1Point({ - x: uint256( - 0x061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d - ), - y: uint256( - 0x1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e - ) + t3: Honk.G1Point({ + x: uint256(0x061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d), + y: uint256(0x1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e) }), - t4: Honk.G1Point({ - x: uint256( - 0x043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce - ), - y: uint256( - 0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854 - ) + t4: Honk.G1Point({ + x: uint256(0x043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce), + y: uint256(0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854) }), - id1: Honk.G1Point({ - x: uint256( - 0x032c782b90a26fe68b4503eff33053fb0491f972c7589f678cecb1823567cfbb - ), - y: uint256( - 0x13cea8eda4216f5eec776c455ebe175fc799c21bfcc40bf06c386d575042624d - ) + id1: Honk.G1Point({ + x: uint256(0x0ce4b985e3d494ea5e27349d6bfd5a93f2da5df7b83a4e95247e187be4b04234), + y: uint256(0x2b14f7ab190415ec456c7d10382e6e5a573d65a180bcc698638d6c38f0c58198) }), - id2: Honk.G1Point({ - x: uint256( - 0x1927315353225025dbf69ce7184ee16deb1f77c740b23f61bb7c7dc0ed459aae - ), - y: uint256( - 0x1163822d778b1f374f971a3bbbac861c3b3b7eacdb0e506cf216c40921f12174 - ) + id2: Honk.G1Point({ + x: uint256(0x2cedaca0e3610529837cd83d76d7a67e6f037068b22b7f0e21c5c0fd2bb16587), + y: uint256(0x16f7b9a3f2b5c36530840e8b7046b3faf0273017b736d7d4e9c86a56e22c997f) }), - id3: Honk.G1Point({ - x: uint256( - 0x211deee9dfac40c974eb8567e5f98f33c095c3b473646744ff9249c0141557f1 - ), - y: uint256( - 0x012032061151f693f8b9db5136efa15439a59fcef2ce2a994474cc0e38deea65 - ) + id3: Honk.G1Point({ + x: uint256(0x195328bc8631c53b83e65739b932c063ab3f2861c3f25e186e693b96ec32ec6b), + y: uint256(0x1c2e18885411c408222af35cf31ebc1e7e3c82428cd8b4111b418a15b47bf778) }), - id4: Honk.G1Point({ - x: uint256( - 0x19feb056cf9e0242cd8b03b6382a7f4cc56ad7d791279cf4902bceaf76480661 - ), - y: uint256( - 0x0ce22a0fb2bcfc2acceb26380d901ed53b28665d3202b66c178efe54ae0601e5 - ) + id4: Honk.G1Point({ + x: uint256(0x0d629ffdaf3c341c70dc25efc19ea32364f2718a9f2e7d3001f2582bc7770eba), + y: uint256(0x218e10d582f130e792dac3a6cd0ca6d339566ea722f23aa8c7e505c444a8b426) }), - lagrangeFirst: Honk.G1Point({ - x: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000001 - ), - y: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000002 - ) + lagrangeFirst: Honk.G1Point({ + x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), + y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) }), - lagrangeLast: Honk.G1Point({ - x: uint256( - 0x07a3911591931e1a78fe8e7034f651e03fa7512effe4eca4e5669efffccae5b3 - ), - y: uint256( - 0x1e175c0cba144505ad7bf362c1fa2edd1cec27db3efc62e7aaec8f974ebd5d3d - ) + lagrangeLast: Honk.G1Point({ + x: uint256(0x194a661bffd8b96eb251f3e4fd4c4ca2099d006d6047d379e36ac4f48075ca84), + y: uint256(0x05cdeca24a13e225c1cca9954e1e3d0826037e148f4c4cabe583813ea42f3318) }) }); 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 c913812e7..3116a3b52 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol @@ -7,241 +7,125 @@ pragma solidity >=0.8.21; uint256 constant N = 2097152; uint256 constant LOG_N = 21; -uint256 constant NUMBER_OF_PUBLIC_INPUTS = 29; -uint256 constant VK_HASH = 0x07706ac4275caeba3a6e83631b7cc30dc8322839a88f5a941700619161f38391; +uint256 constant NUMBER_OF_PUBLIC_INPUTS = 31; +uint256 constant VK_HASH = 0x1e8f6b05e7a356352348aac33d63b40dd9e397018959e1b07bb32bce03a31ca3; 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(29), - ql: Honk.G1Point({ - x: uint256( - 0x111a728f6d97feb2d07c8650c58297f43fa1cb737536686ba611035833266766 - ), - y: uint256( - 0x0fd591041aa17dcbf78787ba1f6eb644df43fb92c7b60870b3c29dd033933cd1 - ) + publicInputsSize: uint256(31), + ql: Honk.G1Point({ + x: uint256(0x16d8337237d4ec2403b09c245b20ae52736b3c3b0c18c643a26a00569a27cadc), + y: uint256(0x1b84afa97d44876f12c9857b371684953b15dd5a102621470fe428ce3440a74e) }), - qr: Honk.G1Point({ - x: uint256( - 0x062bd2526fa803f36afac0562659df1a9f4a0c6589aa65c6c6af7ed41d973839 - ), - y: uint256( - 0x0ea545fff69b5528031196319619d3bc836a0b18c4b0d8323c2c3f235f225c50 - ) + qr: Honk.G1Point({ + x: uint256(0x05cd8cefcf9533c15dd3a87996f365a1d87d537019531bfa1443523a2e8d9567), + y: uint256(0x18ca3ec3ab25ee727789389c4e3a450dd49835e3ff89641b7499751c5eddffb4) }), - qo: Honk.G1Point({ - x: uint256( - 0x2f0724b72d5ed5a5fbe800439d3e9a5f0b202dbfacd761a0b4e4686fed34c6a1 - ), - y: uint256( - 0x28bdb4ee0c725dfd07c5c7409c846bcd4ecaedfaa5806962add7336452afa15d - ) + qo: Honk.G1Point({ + x: uint256(0x0ee7a4cb268e178430f1ab4b692ca1e998f61f59f640709628f3ab061e1ac31e), + y: uint256(0x0f1051efd6dd1ce3d5f3b009b795b22a45b489f682600ebef98ad03b235d2e4e) }), - q4: Honk.G1Point({ - x: uint256( - 0x0c39b0d165d345799d17d07f9950f4002d6e60382e9b56eb6c14010d467e6aba - ), - y: uint256( - 0x2e49a73ee7e9bb5a90edc5e724beb2fd71ccf5fcaa5f30d7b86c290de9be1e90 - ) + q4: Honk.G1Point({ + x: uint256(0x133b09a5e4a51e7c7596de7bd299628b2c61acc5d9d076619a1b01510b7a0b0c), + y: uint256(0x05e0e81bb4a9ce485e85d9dc1d9c91340f65ac58b1586a0c759f22036aa025ef) }), - qm: Honk.G1Point({ - x: uint256( - 0x1536bfcfa305be4288c24dedcd842d1700eea427c52b732d404e83a426babb68 - ), - y: uint256( - 0x21a70fbc5c35eb021d4e24055c2fcdd215d7711ce8d99c001c7327be43500162 - ) + qm: Honk.G1Point({ + x: uint256(0x25305186adb6ab510e45dc5912cad43e52ec3d21d3bb479a9c31dc5638d7d9fe), + y: uint256(0x1701052b060e45fdc21dbea0a000bf67d3ce7855da5a2495e5134efffbbc4f59) }), - qc: Honk.G1Point({ - x: uint256( - 0x0bd8b8c90ec9f0acab4b254d831db478a05a95e8ab4bb5d0cb9c7fd38ff4e837 - ), - y: uint256( - 0x152422e5e5f4c10b9d337cf5284a22c017afd562787f81cce3e573e948cbebde - ) + qc: Honk.G1Point({ + x: uint256(0x257bffaddd8fa52641b20e502375928e7c43840f1026a5d8009ee915bdb6ac6d), + y: uint256(0x1eda4d3bd95201aab871d32aefc8ced2e696789b0933b6c85f2ae1de282847db) }), - qLookup: Honk.G1Point({ - x: uint256( - 0x2ee537ee80c33fc3003e53f2fa67b14d63887059b541c3d5dbe38bfd517aa579 - ), - y: uint256( - 0x0ecddf7b61838886fcf1a7fec5c69d8e02933d1e153e33e5ea7d03b0c21b314a - ) + qLookup: Honk.G1Point({ + x: uint256(0x116151016b4fea676c06d9d297159098031c1b2d5c0ae18d5e5d874105b26eae), + y: uint256(0x0610cfeee3e9dbc93565c2b5ca7cbfb4a301b32b4903f16cc95bdd4a8d30275d) }), - qArith: Honk.G1Point({ - x: uint256( - 0x2ae5c90804b7a2eee8676464c0afd8105452bc0b5160c5af08e07332f642509b - ), - y: uint256( - 0x1faa3c5b0d043233968e58ab03d1e3988d53933fd5716f73dfceaa283321fedc - ) + qArith: Honk.G1Point({ + x: uint256(0x07d4cc138af86d1461d286af965b1dedad0b9a44e5b3764000c4fc25db44e2c7), + y: uint256(0x283149ff7ff97882b22d98fd89937bc119029f1683fcd2dc4aef822c226eed6b) }), - qDeltaRange: Honk.G1Point({ - x: uint256( - 0x12fabb263760a8bd891d6549cbbe45b5db26449b9095e982986ba5f0f8b71bcf - ), - y: uint256( - 0x206f2d10317a3ba8c5147afb8c356eaa6af0912f3dd5f6af3acf4caac3075e6f - ) + qDeltaRange: Honk.G1Point({ + x: uint256(0x1f0332c373c4a8fce8a50b55bfc656f76a5ccfb3cbedbfca049704694dfac777), + y: uint256(0x1bec984d96e9150d2dbedd8d19564ab492d8db9aa52f02edadd06fb85b156bee) }), - qElliptic: Honk.G1Point({ - x: uint256( - 0x0791fed23dfada00ede6c9c02c9d8c5203db1c0aefb755b6bfd8858fd6ecbf20 - ), - y: uint256( - 0x21b24edba4ae135a28e9d5537974b00b80b0e1eb42850f3f659af9e923887ec0 - ) + qElliptic: Honk.G1Point({ + x: uint256(0x2b524a596ca01057f413e2d264278850dd071ef8d2178bb6fe4c5848359f9307), + y: uint256(0x260fa657bf3c7f5db8dba3c6b4c9d701db6cebbdb338acce0bd49a325faf1fbe) }), - qMemory: Honk.G1Point({ - x: uint256( - 0x09e78ace9cfdcf5ad7cc6802eecf242bc9e3e198894af77aaf3debcc2bd32fa5 - ), - y: uint256( - 0x14fd3c6a7580ed1a7de952571906b5ce0185a46514fa57d0e04ab32c0ca0dd34 - ) + qMemory: Honk.G1Point({ + x: uint256(0x088712e929e91683038e64aa93060b67b4c4f8bfbae75648cc2257c6733acf12), + y: uint256(0x04b38e231192be299a84c5495056bf59029b1b7e4a5bf591096a8945265232df) }), - qNnf: Honk.G1Point({ - x: uint256( - 0x0fd61103ff82eb1bae7052f5497eab0a2faf991c16f7e69d15f07cae89cce2ba - ), - y: uint256( - 0x1e1dd49698f822a28bb1395b3f1f13f15e59d374ad9a55c90f32dd77ce17c858 - ) + qNnf: Honk.G1Point({ + x: uint256(0x1c24660db3144b45bf6427e3c1bf6a6c86a2c828217df4d064a1e96797e30154), + y: uint256(0x22039202701dac37b8a59b1cb37ec9c0950c16b651560f20b7cca068ebd47056) }), - qPoseidon2External: Honk.G1Point({ - x: uint256( - 0x2aec5550a01a45e0bc78f9c2c92eeab9dbb308125921dda7789fcff1d6d0db5d - ), - y: uint256( - 0x117277f243642f35b0de763270a2f67f23c1a577fb9b645068f9bf3a8142f1bf - ) + qPoseidon2External: Honk.G1Point({ + x: uint256(0x1be6b465dcde65e3a5b9f2a90d2d1be72254cd5b4e9f4f8d5123655475e6d3fb), + y: uint256(0x2c6716fb2a299303d79589483f34d09251a84eab9ba72d2d455f9b0a450d762b) }), - qPoseidon2Internal: Honk.G1Point({ - x: uint256( - 0x231e7ffadf1a4fe4c731aadba30f4e688c57036f2f0a292f6f4ed598bf5c45be - ), - y: uint256( - 0x04ad4cd62afd2071e675fb9f806fd010bdde31728b54162c5d388ff8407311a9 - ) + qPoseidon2Internal: Honk.G1Point({ + x: uint256(0x0485bdf6b5d9bad6c67b7eebb14e462422b17f8f5c7bb35f655cf706882d34fb), + y: uint256(0x06b5e3947f649e125bcede91f9319b269f3db52ad8c56e6d91bd6615732467fd) }), - s1: Honk.G1Point({ - x: uint256( - 0x12e76ee7f67d824af0f9eaf001d62ee30e3f77812e99060eb6f89c2cc0e2b825 - ), - y: uint256( - 0x116e5a8a4011461fb9c38f402e6b1c6cc46bb827e701c4d8dc94f6e6e9b05085 - ) + s1: Honk.G1Point({ + x: uint256(0x13b5177fc08968ad64a53d03cd78fded2405901bcb6a0adf91500111e0e91816), + y: uint256(0x1ac68a536966301bf3cdfe70d55442d41ab697984821c8db5605018ea78505fc) }), - s2: Honk.G1Point({ - x: uint256( - 0x27474a8477926570b376c9484b946a50407ad175301d24f0823aa48bd4225990 - ), - y: uint256( - 0x0ac3513a35e7af6ee46189ca6e0a2d701f7568427a6f6cab64490652985904f1 - ) + s2: Honk.G1Point({ + x: uint256(0x22f103cca6c92c267a93626743edea371bfe65360e8d425bc6dfa7f37fea5316), + y: uint256(0x297cd73d12d894253268fdb948ea7f85d1b41e942f15eee43381db4707795c79) }), - s3: Honk.G1Point({ - x: uint256( - 0x25446ef56d42b22185a7b772992264f8614dd81c830197979efa2ce70b81c94c - ), - y: uint256( - 0x1fc621751d8cc710f997f7665d71c5969433e4148f625915610d437f25771a9a - ) + s3: Honk.G1Point({ + x: uint256(0x1f59d8f212864a8d1eb077d44972fc944a31d99d4bd8932cefefac230c8fbdcd), + y: uint256(0x186140203c30f7ca375ab9daa1610b84eb1567bc49bbfd68907c9314e79aa8d6) }), - s4: Honk.G1Point({ - x: uint256( - 0x101488084a381a1a19fc537fff3cfdbc971c14810faf379184d40a10f8a2b111 - ), - y: uint256( - 0x222d5570c3352d841d289672c2150d62852bb77cae4798e22890d5772e678c9b - ) + s4: Honk.G1Point({ + x: uint256(0x0b54d2e585ff9ad6a009b877cd9a695fd6e0228bbcb918a69402b398fb179090), + y: uint256(0x2c8c78f62d51be262cb36e5d500ec56d5aa9aca88b5c59f5dae069f65356302d) }), - t1: Honk.G1Point({ - x: uint256( - 0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26 - ), - y: uint256( - 0x0015b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f - ) + t1: Honk.G1Point({ + x: uint256(0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26), + y: uint256(0x0015b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f) }), - t2: Honk.G1Point({ - x: uint256( - 0x1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e - ), - y: uint256( - 0x305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d19 - ) + t2: Honk.G1Point({ + x: uint256(0x1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e), + y: uint256(0x305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d19) }), - t3: Honk.G1Point({ - x: uint256( - 0x061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d - ), - y: uint256( - 0x1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e - ) + t3: Honk.G1Point({ + x: uint256(0x061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d), + y: uint256(0x1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e) }), - t4: Honk.G1Point({ - x: uint256( - 0x043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce - ), - y: uint256( - 0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854 - ) + t4: Honk.G1Point({ + x: uint256(0x043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce), + y: uint256(0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854) }), - id1: Honk.G1Point({ - x: uint256( - 0x2c9d9734c6b4280d3b45cd2f229b94ef9d6ea0cc4b12d3115772c8a44da778cd - ), - y: uint256( - 0x1bb5ebb400bf039d6bf710f9f260cbb912ee939afeef8d9d7038419872b51800 - ) + id1: Honk.G1Point({ + x: uint256(0x2ca4d4cf001b0127cd0565fc4b314c2474265b2622649219983cab5c3a264711), + y: uint256(0x0d3ea6f0e79667587a7b6282ef29030ab179083b2bf591eab27083c9dfefc030) }), - id2: Honk.G1Point({ - x: uint256( - 0x03d6c8a21ed149f265d420e561a26db16aa036f6255b1d7cb1dbd7fef424e679 - ), - y: uint256( - 0x0bec696b07abc01e6d9be8ac176d7e3f22c1ba9745dfef9b8ec43544a6401b72 - ) + id2: Honk.G1Point({ + x: uint256(0x2e385c38f2da1b46e0d42a4e2a23d0daf6bb72f51825e4eb5138a63ea595e94c), + y: uint256(0x043ebbefa92caf5877c70c19abad06f960d0ffc4cda2c872eeb8e684e23011ff) }), - id3: Honk.G1Point({ - x: uint256( - 0x08079b32015b38fd3892014f149d0ae2f96b1251d2614dd1b749d18996e9efd8 - ), - y: uint256( - 0x154c73548738dbb8b06e4a787dd140c0ff343b94223a1ed3af02851ae55ed685 - ) + id3: Honk.G1Point({ + x: uint256(0x0b329f411f2cbeffd924314b090f38bc2e545cce875002c8f0bc316d3b1d2306), + y: uint256(0x1cfe8ce6e2b63866d787025ce891808674a03b1019feba01619009ed49699b52) }), - id4: Honk.G1Point({ - x: uint256( - 0x036e02a212e0ea99da1fc3b9c88f80132bfce41225a39da38588bb8fb646dcc7 - ), - y: uint256( - 0x212442209a29191258fc995d08e625ec8c4cd47e930363a0398644cbb11c30c4 - ) + id4: Honk.G1Point({ + x: uint256(0x276c422860c1379865016a49ae306de911d13c93ed6ed41ab4cbd32ae399c0d1), + y: uint256(0x0e00679146a6dca134568a276976a693a2210ad675464dfab6359df22424b681) }), - lagrangeFirst: Honk.G1Point({ - x: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000001 - ), - y: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000002 - ) + lagrangeFirst: Honk.G1Point({ + x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), + y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) }), - lagrangeLast: Honk.G1Point({ - x: uint256( - 0x2dd29b2f19810390ba06ca70af2a664ae7bc0928bc4352eaf47ca52aecc163e8 - ), - y: uint256( - 0x2facd07b0816618d20a2dbeeb6a7f0045e981d6f0020135fc1131c8278a34730 - ) + lagrangeLast: Honk.G1Point({ + x: uint256(0x16e42cf8cb1c82a0961f9eba8413bce1974df0c9ff87b539fb06f3568375a5ca), + y: uint256(0x05cbe0b6a237e4926c1ba462a2bc921e4d40a0cf5c6b828f0c5d2fade965942f) }) }); 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/tasks/enclave.ts b/packages/enclave-contracts/tasks/enclave.ts index 2914450fa..e639b1ddc 100644 --- a/packages/enclave-contracts/tasks/enclave.ts +++ b/packages/enclave-contracts/tasks/enclave.ts @@ -382,7 +382,6 @@ export const publishCommittee = task( const tx = await ciphernodeRegistry.publishCommittee( e3Id, - nodesToSend, publicKey, pkCommitment, proof, diff --git a/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts b/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts index faf6fbfe2..f31013e1f 100644 --- a/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts +++ b/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts @@ -21,6 +21,12 @@ const MESSAGE_COEFFS_COUNT = 100; const EXPECTED_C6_FOLD_KEY_HASH = ethers.id("c6_fold"); const EXPECTED_C7_KEY_HASH = ethers.id("c7"); +/** Must match `BfvDecryptionVerifier.COMMITTEE_HASH_LO_IDX` (3). */ +const COMMITTEE_HASH_LO_IDX = 3; + +/** Minimum `publicInputs.length` for verify (message tail + committee limbs). */ +const MIN_PUBLIC_INPUTS_LEN = MESSAGE_COEFFS_COUNT + COMMITTEE_HASH_LO_IDX + 1; + function buildPublicInputsWithMessage( messageCoeffs: bigint[], totalInputs = 402, @@ -97,11 +103,15 @@ describe("BfvDecryptionVerifier", function () { const invalidProof = "0xdeadbeef"; await expect( - bfvDecryptionVerifier.verify.staticCall(plaintextHash, invalidProof), + bfvDecryptionVerifier.verify.staticCall( + plaintextHash, + ethers.ZeroHash, + invalidProof, + ), ).to.be.revert(ethers); }); - it("returns false when publicInputs.length < MESSAGE_COEFFS_COUNT + 2", async function () { + it("returns false when publicInputs.length < MIN_PUBLIC_INPUTS_LEN", async function () { const { bfvDecryptionVerifier, mockCircuit } = await loadFixture( deployWithMockCircuit, ); @@ -110,13 +120,14 @@ describe("BfvDecryptionVerifier", function () { const messageCoeffs = [1n, 2n, 3n]; const publicInputs = buildPublicInputsWithMessage( messageCoeffs, - MESSAGE_COEFFS_COUNT + 2, - ).slice(0, MESSAGE_COEFFS_COUNT + 1); + MIN_PUBLIC_INPUTS_LEN, + ).slice(0, MIN_PUBLIC_INPUTS_LEN - 1); const plaintextHash = plaintextToHash(messageCoeffs); const proof = encodeProof("0x01", publicInputs); const result = await bfvDecryptionVerifier.verify.staticCall( plaintextHash, + ethers.ZeroHash, proof, ); expect(result).to.equal(false); @@ -147,6 +158,7 @@ describe("BfvDecryptionVerifier", function () { const result = await bfvDecryptionVerifier.verify.staticCall( plaintextHash, + ethers.ZeroHash, proof, ); expect(result).to.equal(false); @@ -177,6 +189,7 @@ describe("BfvDecryptionVerifier", function () { const result = await bfvDecryptionVerifier.verify.staticCall( plaintextHash, + ethers.ZeroHash, proof, ); expect(result).to.equal(false); @@ -255,6 +268,7 @@ describe("BfvDecryptionVerifier", function () { const result = await bfvDecryptionVerifier.verify.staticCall( wrongHash, + ethers.ZeroHash, proof, ); expect(result).to.equal(false); @@ -273,6 +287,7 @@ describe("BfvDecryptionVerifier", function () { const result = await bfvDecryptionVerifier.verify.staticCall( plaintextHash, + ethers.ZeroHash, proof, ); expect(result).to.equal(false); @@ -295,6 +310,7 @@ describe("BfvDecryptionVerifier", function () { const result = await bfvDecryptionVerifier.verify.staticCall( plaintextHash, + ethers.ZeroHash, proof, ); expect(result).to.equal(false); @@ -315,6 +331,7 @@ describe("BfvDecryptionVerifier", function () { const result = await bfvDecryptionVerifier.verify.staticCall( plaintextHash, + ethers.ZeroHash, proof, ); expect(result).to.equal(true); @@ -329,13 +346,14 @@ describe("BfvDecryptionVerifier", function () { const messageCoeffs = [1n, 2n, 3n]; const publicInputs = buildPublicInputsWithMessage( messageCoeffs, - MESSAGE_COEFFS_COUNT + 2, + MIN_PUBLIC_INPUTS_LEN, ); const plaintextHash = plaintextToHash(messageCoeffs); const proof = encodeProof("0x01", publicInputs); const result = await bfvDecryptionVerifier.verify.staticCall( plaintextHash, + ethers.ZeroHash, proof, ); expect(result).to.equal(true); diff --git a/packages/enclave-contracts/test/BfvPkVerifier.spec.ts b/packages/enclave-contracts/test/BfvPkVerifier.spec.ts index 89e8e8827..36447a324 100644 --- a/packages/enclave-contracts/test/BfvPkVerifier.spec.ts +++ b/packages/enclave-contracts/test/BfvPkVerifier.spec.ts @@ -172,7 +172,11 @@ describe("BfvPkVerifier", function () { EXPECTED_C5_KEY_HASH, ]); - const result = await bfvPkVerifier.verify.staticCall(pkCommitment, proof); + const result = await bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + proof, + ); expect(result).to.equal(false); }); diff --git a/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts b/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts index a437d063f..ed56f1a7e 100644 --- a/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts +++ b/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts @@ -47,6 +47,24 @@ function hexToBytes32Array(hex: string): string[] { return out; } +/** Micro `H` — must match `BfvPkVerifier` / default `dkg_aggregator`. */ +const DKG_H = 3; +const DKG_COMMITTEE_HASH_HI_IDX = 2 + DKG_H; +const DKG_COMMITTEE_HASH_LO_IDX = 3 + DKG_H; +/** `7` pub params + `8` return fields for `H = 3`. */ +const DKG_EXPECTED_PUBLIC_INPUT_LEN = 15; +const DEC_COMMITTEE_HASH_HI_IDX = 2; +const DEC_COMMITTEE_HASH_LO_IDX = 3; +/** `4` pub params + `107` return fields (`T = 1`). */ +const DEC_EXPECTED_PUBLIC_INPUT_LEN = 111; + +function committeeHashFromLimbs(hi: string, lo: string): string { + const hiBn = BigInt(hi); + const loBn = BigInt(lo); + return ("0x" + + ((hiBn << 128n) | loBn).toString(16).padStart(64, "0")) as `0x${string}`; +} + function plaintextHashFromPublicInputs(publicInputs: string[]): string { const messageCoeffsCount = 100; const offset = publicInputs.length - messageCoeffsCount; @@ -224,6 +242,27 @@ describe("BfvVkBindingIntegration", function () { expect(decPublicInputs[0]).to.equal(expectedC6FoldKeyHash); expect(decPublicInputs[1]).to.equal(expectedC7KeyHash); + if ( + dkgPublicInputs.length !== DKG_EXPECTED_PUBLIC_INPUT_LEN || + decPublicInputs.length !== DEC_EXPECTED_PUBLIC_INPUT_LEN + ) { + console.warn( + "Skipping folded proof verify: integration_summary.json was built before " + + "committee_hash public inputs. Regenerate via integration test with " + + "BENCHMARK_SUMMARY_OUTPUT or update circuits/benchmarks/results_insecure/integration_summary.json.", + ); + this.skip(); + } + + const dkgCommitteeHash = committeeHashFromLimbs( + dkgPublicInputs[DKG_COMMITTEE_HASH_HI_IDX], + dkgPublicInputs[DKG_COMMITTEE_HASH_LO_IDX], + ); + const decCommitteeHash = committeeHashFromLimbs( + decPublicInputs[DEC_COMMITTEE_HASH_HI_IDX], + decPublicInputs[DEC_COMMITTEE_HASH_LO_IDX], + ); + const { bfvPk, bfvDec } = await deployHonkAndBfv(); const abiCoder = ethers.AbiCoder.defaultAbiCoder(); @@ -232,9 +271,13 @@ describe("BfvVkBindingIntegration", function () { [summary.folded_artifacts.dkg_aggregator.proof_hex, dkgPublicInputs], ); const pkCommitment = dkgPublicInputs[dkgPublicInputs.length - 1]; - expect(await bfvPk.verify.staticCall(pkCommitment, dkgEncoded)).to.equal( - true, - ); + expect( + await bfvPk.verify.staticCall( + pkCommitment, + dkgCommitteeHash, + dkgEncoded, + ), + ).to.equal(true); const decEncoded = abiCoder.encode( ["bytes", "bytes32[]"], @@ -245,7 +288,11 @@ describe("BfvVkBindingIntegration", function () { ); const plaintextHash = plaintextHashFromPublicInputs(decPublicInputs); expect( - await bfvDec.verify.staticCall(plaintextHash, decEncoded), + await bfvDec.verify.staticCall( + plaintextHash, + decCommitteeHash, + decEncoded, + ), ).to.equal(true); }, ); @@ -302,9 +349,13 @@ describe("BfvVkBindingIntegration", function () { ); const pkCommitment = dkgPublicInputs[dkgPublicInputs.length - 1]; - expect(await bfvPk.verify.staticCall(pkCommitment, dkgEncoded)).to.equal( - false, - ); + expect( + await bfvPk.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + dkgEncoded, + ), + ).to.equal(false); }, ); }); diff --git a/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts b/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts index 0ac741413..2902570cb 100644 --- a/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts +++ b/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts @@ -446,7 +446,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, nodes, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); // Verify stage transitioned to KeyPublished (after publishCommittee which calls onKeyPublished) stage = await enclave.getE3Stage(0); @@ -492,7 +492,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const pkCommitment = ethers.keccak256(publicKey); await expect( - registry.publishCommittee(0, nodes, publicKey, pkCommitment, "0x"), + registry.publishCommittee(0, publicKey, pkCommitment, "0x"), ) .to.emit(enclave, "CommitteeFormed") .withArgs(0); @@ -756,7 +756,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, nodes, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); // 2. Wait past compute deadline → mark as failed const e3 = await enclave.getE3(0); @@ -860,7 +860,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, nodes, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); // 2. Fail via compute timeout const e3 = await enclave.getE3(0); @@ -1112,7 +1112,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, nodes, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); stage = await enclave.getE3Stage(0); expect(stage).to.equal(3); // KeyPublished @@ -1191,7 +1191,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, nodes, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); stage = await enclave.getE3Stage(0); expect(stage).to.equal(3); // KeyPublished @@ -1434,7 +1434,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, nodes, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); expect(await enclave.getE3Stage(0)).to.equal(3); // KeyPublished @@ -1565,7 +1565,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, nodes, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); expect(await enclave.getE3Stage(0)).to.equal(3); // KeyPublished @@ -1624,7 +1624,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, nodes, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "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 853521534..01790ce68 100644 --- a/packages/enclave-contracts/test/Enclave.spec.ts +++ b/packages/enclave-contracts/test/Enclave.spec.ts @@ -79,7 +79,6 @@ describe("Enclave", function () { const setupAndPublishCommittee = async ( registry: any, e3Id: number, - nodes: string[], publicKey: string, operators: Signer[], committeeProof: string = "0x", @@ -92,7 +91,6 @@ describe("Enclave", function () { const pkCommitment = ethers.keccak256(publicKey); await registry.publishCommittee( e3Id, - nodes, publicKey, pkCommitment, committeeProof, @@ -825,17 +823,11 @@ describe("Enclave", function () { proofAggregationEnabled: false, }); - await setupAndPublishCommittee( - ciphernodeRegistryContract, - e3Id, - [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ], - data, - [operator1, operator2, operator3], - ); + await setupAndPublishCommittee(ciphernodeRegistryContract, e3Id, data, [ + operator1, + operator2, + operator3, + ]); await mine(2, { interval: inputWindowDuration }); await enclave.publishCiphertextOutput(e3Id, data, proof); @@ -860,17 +852,11 @@ describe("Enclave", function () { inputWindow: [(await time.latest()) + 20, (await time.latest()) + 100], }); - await setupAndPublishCommittee( - ciphernodeRegistryContract, - e3Id, - [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ], - data, - [operator1, operator2, operator3], - ); + await setupAndPublishCommittee(ciphernodeRegistryContract, e3Id, data, [ + operator1, + operator2, + operator3, + ]); await mine(2, { interval: inputWindowDuration + timeoutConfig.computeWindow, }); @@ -900,17 +886,11 @@ describe("Enclave", function () { proofAggregationEnabled: false, }); - await setupAndPublishCommittee( - ciphernodeRegistryContract, - e3Id, - [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ], - data, - [operator1, operator2, operator3], - ); + await setupAndPublishCommittee(ciphernodeRegistryContract, e3Id, data, [ + operator1, + operator2, + operator3, + ]); await mine(2, { interval: inputWindowDuration }); await expect( enclave.publishCiphertextOutput(e3Id, "0x", "0x"), @@ -933,17 +913,11 @@ describe("Enclave", function () { inputWindow: [(await time.latest()) + 20, (await time.latest()) + 100], }); - await setupAndPublishCommittee( - ciphernodeRegistryContract, - e3Id, - [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ], - data, - [operator1, operator2, operator3], - ); + await setupAndPublishCommittee(ciphernodeRegistryContract, e3Id, data, [ + operator1, + operator2, + operator3, + ]); await mine(2, { interval: inputWindowDuration }); expect(await enclave.publishCiphertextOutput(e3Id, data, proof)); const e3 = await enclave.getE3(e3Id); @@ -966,17 +940,11 @@ describe("Enclave", function () { inputWindow: [(await time.latest()) + 20, (await time.latest()) + 100], }); - await setupAndPublishCommittee( - ciphernodeRegistryContract, - e3Id, - [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ], - data, - [operator1, operator2, operator3], - ); + await setupAndPublishCommittee(ciphernodeRegistryContract, e3Id, data, [ + operator1, + operator2, + operator3, + ]); await mine(2, { interval: inputWindowDuration }); expect( await enclave.publishCiphertextOutput.staticCall(e3Id, data, proof), @@ -999,17 +967,11 @@ describe("Enclave", function () { inputWindow: [(await time.latest()) + 20, (await time.latest()) + 100], }); - await setupAndPublishCommittee( - ciphernodeRegistryContract, - e3Id, - [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ], - data, - [operator1, operator2, operator3], - ); + await setupAndPublishCommittee(ciphernodeRegistryContract, e3Id, data, [ + operator1, + operator2, + operator3, + ]); await mine(2, { interval: inputWindowDuration }); await expect(enclave.publishCiphertextOutput(e3Id, data, proof)) .to.emit(enclave, "CiphertextOutputPublished") @@ -1044,17 +1006,11 @@ describe("Enclave", function () { inputWindow: [(await time.latest()) + 20, (await time.latest()) + 100], }); - await setupAndPublishCommittee( - ciphernodeRegistryContract, - e3Id, - [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ], - data, - [operator1, operator2, operator3], - ); + await setupAndPublishCommittee(ciphernodeRegistryContract, e3Id, data, [ + operator1, + operator2, + operator3, + ]); await expect( enclave.publishPlaintextOutput(e3Id, data, "0x"), ).to.be.revertedWithCustomError(enclave, "InvalidStage"); @@ -1076,17 +1032,11 @@ describe("Enclave", function () { inputWindow: [(await time.latest()) + 20, (await time.latest()) + 100], }); - await setupAndPublishCommittee( - ciphernodeRegistryContract, - e3Id, - [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ], - data, - [operator1, operator2, operator3], - ); + await setupAndPublishCommittee(ciphernodeRegistryContract, e3Id, data, [ + operator1, + operator2, + operator3, + ]); await mine(2, { interval: inputWindowDuration }); await enclave.publishCiphertextOutput(e3Id, data, proof); await enclave.publishPlaintextOutput(e3Id, data, proof); @@ -1116,11 +1066,6 @@ describe("Enclave", function () { await setupAndPublishCommittee( ciphernodeRegistryContract, e3Id, - [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ], data, [operator1, operator2, operator3], encodeMockDkgProof(pkCommitment), @@ -1148,17 +1093,11 @@ describe("Enclave", function () { inputWindow: [(await time.latest()) + 20, (await time.latest()) + 100], }); - await setupAndPublishCommittee( - ciphernodeRegistryContract, - e3Id, - [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ], - data, - [operator1, operator2, operator3], - ); + await setupAndPublishCommittee(ciphernodeRegistryContract, e3Id, data, [ + operator1, + operator2, + operator3, + ]); await mine(2, { interval: inputWindowDuration }); await enclave.publishCiphertextOutput(e3Id, data, proof); expect(await enclave.publishPlaintextOutput(e3Id, data, proof)); @@ -1183,17 +1122,11 @@ describe("Enclave", function () { inputWindow: [(await time.latest()) + 20, (await time.latest()) + 100], }); - await setupAndPublishCommittee( - ciphernodeRegistryContract, - e3Id, - [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ], - data, - [operator1, operator2, operator3], - ); + await setupAndPublishCommittee(ciphernodeRegistryContract, e3Id, data, [ + operator1, + operator2, + operator3, + ]); await mine(2, { interval: inputWindowDuration }); await enclave.publishCiphertextOutput(e3Id, data, proof); expect( @@ -1217,17 +1150,11 @@ describe("Enclave", function () { inputWindow: [(await time.latest()) + 20, (await time.latest()) + 100], }); - await setupAndPublishCommittee( - ciphernodeRegistryContract, - e3Id, - [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ], - data, - [operator1, operator2, operator3], - ); + await setupAndPublishCommittee(ciphernodeRegistryContract, e3Id, data, [ + operator1, + operator2, + operator3, + ]); await mine(2, { interval: inputWindowDuration }); await enclave.publishCiphertextOutput(e3Id, data, proof); await expect(await enclave.publishPlaintextOutput(e3Id, data, proof)) diff --git a/packages/enclave-contracts/test/Pricing/Pricing.spec.ts b/packages/enclave-contracts/test/Pricing/Pricing.spec.ts index c1f303ec2..58eb88821 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, nodes, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(e3Id, publicKey, pkCommitment, "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 ea5384614..da957b89c 100644 --- a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts +++ b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts @@ -426,17 +426,7 @@ describe("CiphernodeRegistryOwnable", function () { await expect( registry .connect(notTheOwner) - .publishCommittee( - 0, - [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ], - data, - dataHash, - "0x", - ), + .publishCommittee(0, data, dataHash, "0x"), ) .to.emit(registry, "CommitteePublished") .withArgs( @@ -474,14 +464,7 @@ describe("CiphernodeRegistryOwnable", function () { await registry.connect(operator3).submitTicket(0, 1); await finalizeCommitteeAfterWindow(registry, 0); - await registry.publishCommittee( - 0, - [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ], - data, + await registry.publishCommittee(0, data, dataHash, "0x", ); @@ -512,14 +495,7 @@ describe("CiphernodeRegistryOwnable", function () { await finalizeCommitteeAfterWindow(registry, 0); await expect( - await registry.publishCommittee( - 0, - [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ], - data, + await registry.publishCommittee(0, data, dataHash, "0x", ), @@ -694,14 +670,7 @@ describe("CiphernodeRegistryOwnable", function () { await registry.connect(operator3).submitTicket(e3Id, 1); await finalizeCommitteeAfterWindow(registry, e3Id); - await registry.publishCommittee( - e3Id, - [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ], - data, + await registry.publishCommittee(e3Id, data, dataHash, "0x", ); diff --git a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts index bc4bf24e9..c1f675673 100644 --- a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts +++ b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts @@ -345,13 +345,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { const nodes = await Promise.all(operators.map((op) => op.getAddress())); const publicKey = ethers.toUtf8Bytes("fake-public-key"); const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee( - e3Id, - nodes, - publicKey, - pkCommitment, - "0x", - ); + await registry.publishCommittee(e3Id, publicKey, pkCommitment, "0x"); } // ── Return ───────────────────────────────────────────────────────────────── diff --git a/scripts/build-circuits.ts b/scripts/build-circuits.ts index cc1ef1cb2..eb92b7cca 100644 --- a/scripts/build-circuits.ts +++ b/scripts/build-circuits.ts @@ -55,9 +55,17 @@ interface BuildOptions { skipVk?: boolean outputDir?: string clean?: boolean + noCleanTargets?: boolean + skipIfBuilt?: boolean dryRun?: boolean preset?: CircuitPreset | 'all' } + +interface PresetBuildStamp { + preset: string + sourceHash: string + builtAt: string +} interface BuildResult { success: boolean compiled: CompiledCircuit[] @@ -146,6 +154,52 @@ class NoirCircuitBuilder { console.log(` 📋 Set Noir config to: ${configModule} (preset: ${preset})`) } + private presetStampPath(preset: string): string { + return join(this.options.outputDir!, preset, '.build-stamp.json') + } + + private readPresetStamp(preset: string): PresetBuildStamp | null { + const stampPath = this.presetStampPath(preset) + if (!existsSync(stampPath)) return null + try { + return JSON.parse(readFileSync(stampPath, 'utf-8')) as PresetBuildStamp + } catch { + return null + } + } + + private writePresetStamp(preset: string, sourceHash: string): void { + const stamp: PresetBuildStamp = { + preset, + sourceHash, + builtAt: new Date().toISOString(), + } + mkdirSync(join(this.options.outputDir!, preset), { recursive: true }) + 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[] { + 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'), + 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'), + join(bin, CIRCUIT_GROUPS.AGGREGATION, 'decryption_aggregator', 'target', 'decryption_aggregator.vk_recursive'), + join(bin, CIRCUIT_GROUPS.DKG, 'target', 'pk.json'), + join(bin, CIRCUIT_GROUPS.THRESHOLD, 'target', 'pk_aggregation.json'), + ] + } + + private isPresetUpToDate(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)) + } + private async buildForPreset(preset: CircuitPreset): Promise { const result: BuildResult = { success: true, compiled: [], errors: [] } const presetOutputDir = join(this.options.outputDir!, preset) @@ -169,10 +223,21 @@ class NoirCircuitBuilder { return result } - this.cleanTargetDirs(circuits) - mkdirSync(presetOutputDir, { recursive: true }) + const sourceHash = this.computeSourceHash() + result.sourceHash = sourceHash + + if (this.options.skipIfBuilt && this.isPresetUpToDate(preset, sourceHash)) { + console.log( + ` ⏭️ Skipping preset ${preset} (artifacts up to date; source_hash=${sourceHash}). ` + + `Use a full rebuild without --skip-if-built to refresh.`, + ) + return result + } - result.sourceHash = this.computeSourceHash() + if (!this.options.noCleanTargets) { + this.cleanTargetDirs(circuits) + } + mkdirSync(presetOutputDir, { recursive: true }) for (const circuit of circuits) { try { @@ -184,6 +249,7 @@ class NoirCircuitBuilder { } this.copyArtifacts(result.compiled, presetOutputDir, preset) + this.writePresetStamp(preset, sourceHash) console.log(`\n✅ Built ${result.compiled.length} circuits for preset: ${preset}`) if (result.errors.length > 0) { console.error('\n❌ Failed circuits:') @@ -651,6 +717,8 @@ async function main() { else if (arg === '--skip-checksums') options.skipChecksums = true else if (arg === '--skip-vk') options.skipVk = true 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 === '--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]) @@ -692,6 +760,8 @@ Options: -o, --output Output directory (default: dist/circuits) --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 -h, --help Show help `) } diff --git a/scripts/generate-verifiers.ts b/scripts/generate-verifiers.ts index bed14998a..88a556869 100644 --- a/scripts/generate-verifiers.ts +++ b/scripts/generate-verifiers.ts @@ -18,6 +18,7 @@ * 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) */ import { execSync } from 'child_process' @@ -49,6 +50,7 @@ interface GenerateOptions { clean?: boolean dryRun?: boolean compile?: boolean // compile circuits before generating verifiers + noCleanTargets?: boolean // skip deleting nargo target dirs before generation } // --------------------------------------------------------------------------- @@ -93,8 +95,10 @@ class VerifierGenerator { return } - // Clean stale nargo build caches to prevent using outdated artifacts - this.cleanTargetDirs(circuits) + // Clean stale nargo build caches unless caller just ran build:circuits (--no-compile / --no-clean-targets). + if (!this.options.noCleanTargets && this.options.compile !== false) { + this.cleanTargetDirs(circuits) + } // Prepare output directory if (this.options.clean && existsSync(this.verifierDir)) { @@ -375,6 +379,9 @@ async function main() { options.clean = true } else if (arg === '--no-compile') { options.compile = false + options.noCleanTargets = true + } else if (arg === '--no-clean-targets') { + options.noCleanTargets = true } else if (arg === '--group') { const value = args[++i] if (!value || value.startsWith('--')) { @@ -407,7 +414,9 @@ Options: --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 - --no-compile Don't compile circuits automatically (fail if not already compiled) + --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 From 2bef312dabb2ddae29bbdbb88d605ff8cfd308c3 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Tue, 19 May 2026 13:38:44 +0200 Subject: [PATCH 04/20] fmt --- agent/flow-trace/00_INDEX.md | 22 +- agent/flow-trace/04_DKG_AND_COMPUTATION.md | 11 +- .../results_insecure/crisp_verify_gas.json | 48 +- .../results_insecure/integration_summary.json | 20 +- crates/aggregator/src/lib.rs | 2 +- .../src/threshold_plaintext_aggregator.rs | 14 +- crates/sortition/src/sortition.rs | 6 +- crates/utils/src/committee_hash.rs | 5 +- .../contracts/lib/CommitteeHashLib.sol | 4 +- .../contracts/test/MockCiphernodeRegistry.sol | 4 +- .../bfv/honk/DecryptionAggregatorVerifier.sol | 1635 ++++++++++++----- .../bfv/honk/DkgAggregatorVerifier.sol | 1635 ++++++++++++----- .../test/E3Lifecycle/E3Integration.spec.ts | 49 +- .../CiphernodeRegistryOwnable.spec.ts | 21 +- .../test/Slashing/CommitteeExpulsion.spec.ts | 1 - 15 files changed, 2438 insertions(+), 1039 deletions(-) diff --git a/agent/flow-trace/00_INDEX.md b/agent/flow-trace/00_INDEX.md index 8776fcaf2..a6d5e3b4c 100644 --- a/agent/flow-trace/00_INDEX.md +++ b/agent/flow-trace/00_INDEX.md @@ -176,19 +176,19 @@ _Found during source-code cross-referencing of these trace documents._ | 4 | `activate()` calls `register()` → `registerOperator()` which has `require(!registered, AlreadyRegistered())`. So activate **reverts** for already-registered operators. It only works for re-registration after deregistration. | BondingRegistry.sol:308 | 01_REGISTRATION | | 5 | `E3Requested` event is `(uint256 e3Id, E3 e3, IE3Program indexed e3Program)` — seed and params are inside the E3 struct, not separate parameters. | IEnclave.sol:82 | 03_E3_REQUEST | | 6 | `finalizeCommittee()` checks `>=` deadline, not `>`. | CiphernodeRegistryOwnable.sol | 03_E3_REQUEST | -| 7 | `publishCommittee()` is now permissionless. The effective access control is DKG proof verification plus the single-publish guard `publicKeyHashes[e3Id] == 0`; the old `onlyOwner` note is obsolete. | CiphernodeRegistryOwnable.sol | 04_DKG | -| 8 | `CommitteePublished` event emits `(e3Id, nodes, publicKey, pkCommitment, proof)` — full PK bytes, pkCommitment, and proof bytes (DkgAggregator when proof aggregation is enabled), not just pkHash. | CiphernodeRegistryOwnable.sol | 04_DKG | +| 7 | `publishCommittee()` is now permissionless. The effective access control is DKG proof verification plus the single-publish guard `publicKeyHashes[e3Id] == 0`; the old `onlyOwner` note is obsolete. | CiphernodeRegistryOwnable.sol | 04_DKG | +| 8 | `CommitteePublished` event emits `(e3Id, nodes, publicKey, pkCommitment, proof)` — full PK bytes, pkCommitment, and proof bytes (DkgAggregator when proof aggregation is enabled), not just pkHash. | CiphernodeRegistryOwnable.sol | 04_DKG | | 9 | `_validateNodeEligibility` calls `bondingRegistry.getTicketBalanceAtBlock()` (not `ticketToken.getPastVotes()` directly). | CiphernodeRegistryOwnable.sol:668 | 03_E3_REQUEST | | 10 | Lane A slashing uses **attestation-based** verification (committee quorum votes), not direct ZK proof re-verification on-chain. `proposeSlash()` decodes voter addresses, agrees, data hashes, and ECDSA signatures — not ZK proofs. | SlashingManager.sol | 05_FAILURE | ### Protocol Design Concerns -| # | Concern | Severity | Detail | -| --- | ---------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 1 | **Deregister-before-slash race** | Accepted | SlashingManager Lane B (evidence+appeal) has a window during which the operator can deregister and claim their exit. If they do, the slash executes against 0 funds. The contract comments acknowledge this as an accepted tradeoff for the appeal window design. | -| 2 | **Committee publication decentralized** | Resolved | `publishCommittee()` is permissionless. Off-chain role selection chooses the active aggregator, while on-chain C5 proof verification and the single-publish guard prevent invalid or duplicate committee publication. | -| 3 | **`gracePeriod` is dead code** | Medium | `gracePeriod` is stored and validated during config updates but never actually used in any timeout check. Either the deadlines already bake in sufficient buffer, or this is a missing feature. | -| 4 | **`activate` CLI command is misleading** | Low | Named "activate" but actually calls "register" — will fail for already-registered operators. There's no standalone way to trigger re-evaluation of active status; instead, `_updateOperatorStatus()` runs automatically inside `addTicketBalance()`, `bondLicense()`, etc. | -| 5 | **Active-job load balancing bug fixed** | Info | The Rust `NodeStateStore.available_tickets()` subtracts `active_jobs` from total tickets, reducing the chance of busy nodes being selected for new E3s. Previously, the `Sortition` actor's `Handler` was missing match arms for `E3Failed` and `E3StageChanged`, causing these events to fall to the default `_ => ()` — the typed handlers for decrementing jobs were dead code. This has been fixed: E3Failed and E3StageChanged are now routed to their handlers, and `finalized_committees` is cleaned up in `decrement_jobs_for_e3` to prevent unbounded memory growth. | -| 6 | **Committee member expulsion** | Info | `SlashingManager` can call `expelCommitteeMember()` mid-DKG. The `Sortition` actor enriches the raw `CommitteeMemberExpelled` event with the expelled member's `party_id` (resolved from its stored `Committee` list) and re-publishes it. `ThresholdKeyshare` then uses the enriched `party_id` to update its collectors, potentially completing DKG with fewer parties. `ThresholdKeyshare` itself does not hold committee state. | -| 7 | **ProofRequestActor failure bridge fixed** | Info | `ProofRequestActor` no longer leaves proof publication suppressed under log-only "will not be published" exits. `ComputeRequestError` and local proof-signing failures for DKG-path proofs (`C0` through `C5`) now emit `E3Failed { failed_at_stage: CommitteeFinalized, reason: DKGInvalidShares }`, while decryption-path proofs (`C6` and `C7`) emit `E3Failed { failed_at_stage: CiphertextReady, reason: DecryptionInvalidShares }`. | +| # | Concern | Severity | Detail | +| --- | ------------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | **Deregister-before-slash race** | Accepted | SlashingManager Lane B (evidence+appeal) has a window during which the operator can deregister and claim their exit. If they do, the slash executes against 0 funds. The contract comments acknowledge this as an accepted tradeoff for the appeal window design. | +| 2 | **Committee publication decentralized** | Resolved | `publishCommittee()` is permissionless. Off-chain role selection chooses the active aggregator, while on-chain C5 proof verification and the single-publish guard prevent invalid or duplicate committee publication. | +| 3 | **`gracePeriod` is dead code** | Medium | `gracePeriod` is stored and validated during config updates but never actually used in any timeout check. Either the deadlines already bake in sufficient buffer, or this is a missing feature. | +| 4 | **`activate` CLI command is misleading** | Low | Named "activate" but actually calls "register" — will fail for already-registered operators. There's no standalone way to trigger re-evaluation of active status; instead, `_updateOperatorStatus()` runs automatically inside `addTicketBalance()`, `bondLicense()`, etc. | +| 5 | **Active-job load balancing bug fixed** | Info | The Rust `NodeStateStore.available_tickets()` subtracts `active_jobs` from total tickets, reducing the chance of busy nodes being selected for new E3s. Previously, the `Sortition` actor's `Handler` was missing match arms for `E3Failed` and `E3StageChanged`, causing these events to fall to the default `_ => ()` — the typed handlers for decrementing jobs were dead code. This has been fixed: E3Failed and E3StageChanged are now routed to their handlers, and `finalized_committees` is cleaned up in `decrement_jobs_for_e3` to prevent unbounded memory growth. | +| 6 | **Committee member expulsion** | Info | `SlashingManager` can call `expelCommitteeMember()` mid-DKG. The `Sortition` actor enriches the raw `CommitteeMemberExpelled` event with the expelled member's `party_id` (resolved from its stored `Committee` list) and re-publishes it. `ThresholdKeyshare` then uses the enriched `party_id` to update its collectors, potentially completing DKG with fewer parties. `ThresholdKeyshare` itself does not hold committee state. | +| 7 | **ProofRequestActor failure bridge fixed** | Info | `ProofRequestActor` no longer leaves proof publication suppressed under log-only "will not be published" exits. `ComputeRequestError` and local proof-signing failures for DKG-path proofs (`C0` through `C5`) now emit `E3Failed { failed_at_stage: CommitteeFinalized, reason: DKGInvalidShares }`, while decryption-path proofs (`C6` and `C7`) emit `E3Failed { failed_at_stage: CiphertextReady, reason: DecryptionInvalidShares }`. | diff --git a/agent/flow-trace/04_DKG_AND_COMPUTATION.md b/agent/flow-trace/04_DKG_AND_COMPUTATION.md index 520a7690f..b3d5ea385 100644 --- a/agent/flow-trace/04_DKG_AND_COMPUTATION.md +++ b/agent/flow-trace/04_DKG_AND_COMPUTATION.md @@ -171,6 +171,7 @@ ThresholdKeyshare receives AllEncryptionKeysCollected │ │ Output: esi_sss[num_ciphertexts][N] │ │ └─────────────────────────────────────────────────────────┘ ``` + │ ├─ ThresholdKeyshare tracks the correlation id for both TrBFV requests: │ ├─ `GenPkShareAndSkSss` @@ -314,11 +315,11 @@ aggregation path can terminate deterministically instead of stalling on missing `PublicKeyAggregator` and `ThresholdPlaintextAggregator` dispatch the aggregator requests instead of pairwise folding. -**Failure bridge:** `ProofRequestActor` now converts proof-generation worker failures -and local proof-signing failures into terminal round failures instead of only -logging that the proof-bearing artifact will not be published. DKG-path proofs -(`C0` through `C5`) emit `E3Failed { failed_at_stage: CommitteeFinalized, -reason: DKGInvalidShares }`; decryption-path proofs (`C6` and `C7`) emit +**Failure bridge:** `ProofRequestActor` now converts proof-generation worker failures and local +proof-signing failures into terminal round failures instead of only logging that the proof-bearing +artifact will not be published. DKG-path proofs (`C0` through `C5`) emit +`E3Failed { failed_at_stage: CommitteeFinalized, reason: DKGInvalidShares }`; decryption-path proofs +(`C6` and `C7`) emit `E3Failed { failed_at_stage: CiphertextReady, reason: DecryptionInvalidShares }`. ### Step 6: Collect All Threshold Shares (with C2/C3 Verification) diff --git a/circuits/benchmarks/results_insecure/crisp_verify_gas.json b/circuits/benchmarks/results_insecure/crisp_verify_gas.json index 42985be40..9f0b06b12 100644 --- a/circuits/benchmarks/results_insecure/crisp_verify_gas.json +++ b/circuits/benchmarks/results_insecure/crisp_verify_gas.json @@ -27,7 +27,53 @@ "total": 186764 } }, - "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.115728250,"runs":3,"total_seconds":0.347184751},{"name":"CalculateDecryptionShare","avg_seconds":0.608609847,"runs":3,"total_seconds":1.825829541},{"name":"CalculateThresholdDecryption","avg_seconds":0.578625417,"runs":1,"total_seconds":0.578625417},{"name":"GenEsiSss","avg_seconds":0.124242194,"runs":3,"total_seconds":0.372726584},{"name":"GenPkShareAndSkSss","avg_seconds":0.223503888,"runs":3,"total_seconds":0.670511665},{"name":"ZkDecryptedSharesAggregation","avg_seconds":8.568398333,"runs":1,"total_seconds":8.568398333},{"name":"ZkDecryptionAggregation","avg_seconds":49.047040500,"runs":1,"total_seconds":49.047040500},{"name":"ZkDkgAggregation","avg_seconds":20.151443750,"runs":1,"total_seconds":20.151443750},{"name":"ZkDkgShareDecryption","avg_seconds":1.504357312,"runs":6,"total_seconds":9.026143874},{"name":"ZkNodeDkgFold","avg_seconds":62.889247500,"runs":3,"total_seconds":188.667742500},{"name":"ZkPkAggregation","avg_seconds":2.156797250,"runs":1,"total_seconds":2.156797250},{"name":"ZkPkBfv","avg_seconds":0.328596097,"runs":3,"total_seconds":0.985788291},{"name":"ZkPkGeneration","avg_seconds":1.329930791,"runs":3,"total_seconds":3.989792375},{"name":"ZkShareComputation","avg_seconds":2.693458527,"runs":6,"total_seconds":16.160751167},{"name":"ZkShareEncryption","avg_seconds":2.489975329,"runs":24,"total_seconds":59.759407916},{"name":"ZkThresholdShareDecryption","avg_seconds":6.047533777,"runs":3,"total_seconds":18.142601333},{"name":"ZkVerifyShareDecryptionProofs","avg_seconds":0.097465083,"runs":3,"total_seconds":0.292395251},{"name":"ZkVerifyShareProofs","avg_seconds":0.226985783,"runs":5,"total_seconds":1.134928918}],"operation_timings_total_seconds":381.878109416,"timings_seconds":[{"label":"Starting trbfv actor test","seconds":0E-9},{"label":"Setup completed","seconds":3.041689125},{"label":"Committee Setup Completed","seconds":20.243337708},{"label":"Committee Finalization Complete","seconds":0.007484375},{"label":"ThresholdShares -> PublicKeyAggregated","seconds":304.504600792},{"label":"E3Request -> PublicKeyAggregated","seconds":307.024598667},{"label":"Application CT Gen","seconds":0.318660917},{"label":"Running FHE Application","seconds":0.003694084},{"label":"Ciphertext published -> PlaintextAggregated","seconds":79.271051417},{"label":"Entire Test","seconds":409.917920083}],"folded_artifacts":{"dkg_aggregator":{"proof_hex":"0x00000000000000000000000000000000000000000000000469753df342aec0e0000000000000000000000000000000000000000000000006e4d829d66aff749d000000000000000000000000000000000000000000000005f6499be2b18300b20000000000000000000000000000000000000000000000000002be6db4c5558c00000000000000000000000000000000000000000000000eedb4b6056c881e3400000000000000000000000000000000000000000000000867b55169f2600f58000000000000000000000000000000000000000000000001bc9f519f74446bfa0000000000000000000000000000000000000000000000000002b6d6df53885c000000000000000000000000000000000000000000000007269f9ee62308a02500000000000000000000000000000000000000000000000fc8ef0ceed9608e82000000000000000000000000000000000000000000000002d32a3b72f08734b600000000000000000000000000000000000000000000000000018a5af26897d800000000000000000000000000000000000000000000000665ee11e35db35bab00000000000000000000000000000000000000000000000c29f716629ef9a6b300000000000000000000000000000000000000000000000ef868c0047d6dd22700000000000000000000000000000000000000000000000000026ab8350fbcd619e3c65ffa1c0d29b2cd62700cf8d71ebcb032227e21f28af6c3d9993353159827a80a592b60afb7bd0a5547279d7b7b6d745bd21e61cb30dd2fe6fa64ed7ee81bddec82e6608821134bc64e48afc548d54643761d84f4518e91e1bb576a9b3a1efdee0270f6a11edde9c2fd71e9de5a134a7ffcfa996506f17b7ded320b946905e9267cb38c0568773f0534e90cb7b63d7211d220cf75c33fef50d1b5a5100e055a47bec0d9798656da3a784b526caa90e53303c14eb63b919c2949535645bf0d9547c8dce32e13dd504bc6be4948f9635a4189cac34427ead69640255ad33c0d14a348d9edcbdb6ef4d4b0a379e7fdeb9801b76a0da963bdb56d89ba8f3a0210b7c42426c99152b8bbcdf2b36f1c1855f0ceb7482c347de29259ad7e05498a03d6dc7925daaca12e8661b8410ba878a1497660cac39150a0d2d1ca7b556cd5217867ffbd422a5ec0f3eda032d0da1dae248fdb76aa76f2aef8583db964d1491f5aff0a8af8585259f308839565b5bfa984c574d4cf02f2552749a67f4ee7f91fd21add31916d3b40f4006b7a83ebf00c14999182cacfc3b853fa33eb52c4bc0d7a9f47486d5c9f5dc5f1fba6a4c5edebfa27f2b9d47ef6a7de0f966356dfac080b3dab741554edf23aad2a27840334a132e4ccc09bc438f503346aad442b0214216a2005c8c7a405b68cdc0032c8a6f8ff1c663bfcfd165d7de791193c31c30dd9f2f2f2a0431be2e4e4f1fe5b4be54f4df8b761ca81b8634e42a243789b7219142015b3808efb3a739c019f139df3488006f7e463f6d19fcf228bce9fa6d613cc0c01c36911240ee0501a0cc007655ea1fd15b8a1ad2f8d1ddf49931bc0bf177827b2cceb2956abbee92ecef40924cb1e618efef90f0194b8d8df4745ac94211fbc877e3f1c1843dd9c04c81659fd1071b38a33797f6a6c395143aec09c8901b9d02917af3362e348cfb2e9aabd53ebbe6b3565508163ea1901f214764dda2cc1ea3a3e121b0655786495091b1b6eb05be9d77b5149795f94e4bec6e2b183149dc7fb2a6c49197a09199db3d00a13ed9d3fe11a18f5fac973b7349533df4922301951689c04b3f866c7f8425c047237cf8d409e8ba27c49e924268817c9e9055066deefa06a0b869217f5fed30835f92bd9662aca74e8b06d29e525ade59a1fb24ae716ec7d331fe77e298f28024bb1fdcc10c4390920b5592dc36749909d0c0f625429773d2393a4f713600cabfca65ec0617333ae5764f69790a9f97bab2f003fff339c5965274e74d713ade8d48df30bf48503c109d9e6c124b70b6277299d62ad7b2b7138796b217dbcc3c4acf6017e1bd6fdbc366957610a0f79eb4f17ed4c180fa64e56f7ce6c9c3aab320f1552db2274fcd7a4576de6cfb0d3db3c1ea4b1ce1f3afd3ab96c2e78be599e3d94f43d7f89851fc6ed825d6658f005e815a7cd1bd26641e038a56293b8095be2f287d24612af3ed6ba44a1f3f3a911e3302a5476eff60236e0580cf8b25f9efc8244b30ed8786f66e39dd4456aa4f024029eba613ba4fb9a7994e9ce5afb6923109490c62e0c988df549f2e9dcc3a56829bb3f7054a4b8cab4aa1769fa658620e87d592cbc03f9a71bca647f275288ab02dab6867241f0d9d7a67a4b33933a1709d75274bf5bbdca414d3ec79ed120fb064ea0f18c25252dd3a82b941e656196dae6336b385dbc60cb74e8cd668c6f631fa549c5ccb417790c6e015e9adb9d640012eb6c177e18eb7a319e4fd7a266671c31bccb4604b5acb7226d74e075d4276b4e9410a595e91dc5f864d35c6e95b72d4308c3be58dc29da40e25d1f63a36940f572b901c39f31e355b075dfc95a2d2092ece23729f1bf832fac0ec1ccd994f016562ca1efebf54428b688f1603df1076714c5d7d7ccd14a87225c15c5178ecb331bf323ab74f8240715fd0166c33605affcaa1b4b75d30f034659860df6af13b6ee9e6e5c1cbc5e131c2709ea226c11cf137940d661f8f254ebfe274cbd889768cc23d728cd2c5082012f427228cd21cf49509a55121e5756aa33e207a548d5a8f15e9f4869564fb2fcef1c4577e81f237f271365e73cbe12dfdee90ca49799cfef0cc0af26d57ac820007cf3cfc9073dcb3915ed77c7e50f58441879c2c5bb450ada48fb95182dd02943e980031a257c5bb6c116e0e17a1efd1877dcc3a988d9b5680f05f5dd7927bbd0336c143c0aa03fbd503e48787f9296c4cdb00160adecdd0ac45174988fbc535e86d6e4250b3b669d61dbca0afec215f54337b74b36820a3794c4824c9c812aecbdb860be238bcc0ab026c45a1329e6ccbb1f0e6abd83cb9910e5468c9e22ce6c4c3c5d1b2892318212462091cc6f082e3946445e67456576206a0777bdafae6420bc108d0d545b60b47a8647c9acec31abd3eb4a940ce24304658f9ce2ff50a5f5a656f80dcec5d9ed6e857d317c33e96e97e19dea73112a08b301cee1c5a26264b2033918a03534a00b9812d0dd992842efea7d0323402034bef78dbc11232402264318246fac64ff0d90acbd0b1930b4377ba08f35e91ebf526c3802640b74f6b786ec118f486e979a8f8d9bcc19307bee149a5a4d459a145723fa863d8230753eb1dd08f4187ced1f037867e3308affbc4fd535746aa6760d018f75bca7bf3396a59902320058d6cd5935e44ea6943d47fb9d45ccdbf8ef6de9a5231e2e4e2a1558662b10e223298ce2cc72aa45a021f1437be501b2743c08bfbc9708db8b2cd7a9ce17256be9fee29f8d3dbeffea7001aa6648ed7fdd53c0e9117d88379f75466d7c2cac187ce9ea645f137c8531c417fa019100cbdf78be2763d38414b97a6b00871a954bdc2d8e1df7970a636eb0e9d8e5acaaf827e955adfe4a8a2a43598a4cba094833c3e3dbd1f261a5d75b5fd313a5f240ff30d989823dc45a3c3ec90082c626ae27a87be9dadb61eff083583ee3568378e95a1ba75f4bfda023c820466f822adc5410bfa974d63142c051bcafb9f1b00ea44385db8cf1d0c36be3e71899c00cc356ea4a7f52ace0bf72913493c437e3a0cda118619333e1b503b29a7e008a0159746030e90700e49f97c957078dfe35497ae3c4fab77cc9c6f080f84637291d9bd340a31415e39c83c58e3f3b901ccf2e2e2356feb40dc496008982bd189118de77f08ce5a1515447c4a04a8c35e9c89f86d164f10d3df3c3701d8148e5681c027f18f613d5f5d6b22de99a7e7c5d1154db617adb0f4aa4b140a4cc4946ba2c6a68f07b9c6b738d4039291b0e7bce61dd330aed09aadba81f6658e15c46251c44e7e03576d9a2d902f93d2cc226b10a6e44adb7ac928f8275333365101c821d0ce2c8b77aebdb5aabffa4c641b602cfb62c3c98c9f966b2d950a193a4c6e4196464c9eab169d15ac12cf5c4ba880c3bb829afc21ada4364d6a4f81299de8b21679e06e1e557e7c831c03cdb1b8629cb925d4fdc7d44fce7f4ec1517897ec72a2ea327bc07437e1a70ce03392e1d739c2f913698ee590e86c3a7c5e01e70292b73f7dd483bd896583d2bd30e736926227cf981f05cd4d718c6f8025127da4b205d377c16c2d412e76aa1486b04538dee538376fe1522413e41df227cdd52e72c8c867f4a3ea87ee1328f964365e79338fccb36c83a0518445e3c09aa2005ef15405379466c46a41cabae490c736d70476264f3b78287804bca97b68bef27c409883a5ce11bb36fe046d33a2447f7cfc35506203a2e3a01d389c060aa3604cc237fbc043276f8bcbfb7d2d1adadfaba9cbb70a9220efebd6ffc62ed96bd9bf02387189dc44aa75b967986a74ece75106f97c17256baec95737d6de103c6d7a2118ed1f68bbf4fcb6647e625e0a210b31dd9399f49665aa4c27d88a639767a521dafb03ac0ee4f48394d53fe6d209740009a1c11886f6c721cf91e29e268e8cb2a7dba0ab3bcfaf639f230f5cc32a443d1f9afca79a77113657e12b3a4023f130838602c3c9bcda88afbf7ce8fddd16060ec4e0e955bffe48ce7795a750121a7304f58bf35b291bff1e28314c5e1aec8a7301a178b3e2e76b81f27951a2550b817406d894712963faeeed76b7069a0c164e685255aa0ec245c5d223b36b05f760307a8d077c47184df6356e68a3df1b6e645a7a1f68c3a4ec30a79497dbeee8821f4cc5aaee04d9c0c28cbe66e5cbcc48994a4efa36572343f005e385aaa301e157faaaf53694d880169b06e0699f6aa01cd14868786ca9be54a5571ea0791df156e53a49573829a7493415d75ea54104d64b800d4688fe718eb4069b59f93162277d97ac74c5836b5078e713d2181bba46a58bd1be7cfb2d359473be276081f1b6ccb434bbb6f226e9188bc9bcc75826becfb2b589244ff57e97a2994682a3e007aad5202beb5bc1a5ee1fd908cf63b2d36f76e04978c164ce4d36a3646d7581474432c9e22f05b48726087539ee97f746f73cd9d414d242ab5bdfd7609d0b32155a9da08e2067a889517aaf8cb024a8168f94dafce6f93b2f12dfc00641d5218c35fd80cc79d0b4bbb1f766f20ad4ecfc79ca94562895c78057587e9edcf1211fca08a404520fb20794782ed562767d84c1e6bd5d1121a2b58b63d633cb3d528392519b02c9a33d6200c2b94bfeae8eb83460ba6ad4dff419969ce80ad48540cd03c06c82c17abae5a66cd8db314d6cbcfbed1472a446afe81b08ee8e710121bbc002b678d3e50d223f02e4edb538e65fe35f26fa0d21cd77169d208d1cea6188ad9b99abd82c6f89525f6f1034be005f761a9503d049c955ce43bf0997e192ee5118d2c94ab12ba2df5075f42f1572f28660109c53a4051c8aeefb2015915127447e3992602c10f17fa0248d4a63d3fb51d02d7c87575ddd2a228838d54e015405e3aeeb0eda949ed50111d0881e0d2eface34b66941e9ea7246365202f1d207ad9eaf4c7ea11527fa2ceb4c32496270b04edd79eca3542d97606c3ad946601d47aae61d27411aabf3fb0d5c57301ee1ffb7c01ceb4d04ff39180475d097f2b07fc67bf89790286b97b1d814ee45b760d49ddecf8dc3907cfae048afc82550a7f416d7de4c9a6e3a847c5700b4642b637ff9872096679d2d5921eee91c5e11110a2da4abbceb28aa0f9b4bbc132493a2ec0f2712ab0d2cb8d2c589168f9af2f14df7d9de5d4463ff031988639bc2bc42c844a737ca569dc14045284ac011e02992542a5ae3e06562f08bd9eb6d695652b0c0207c3514b292402a5745a60cb22cbfa325db3d30866e1739fe38aca41517e9076700489344ddf68c38c28ae9619b91304c3e55c8f4ffbb089b13b2f04ce5d61caad3d2cb54341b5d1a930d35511de0fd7e69802c35962bd45cd5733a23093208051359e4257f5c9cf68082b362f3744ac22c015a9ac28540106e7d03ddc305882758a50c372a580e5d70c09a720755d74fa777039850e88a289095bc3fda6e3de76a661307c2e77237916761e1dc2e2fb9e5e3e31b522be2e0d8f2e3e7e9cc1dfecb73a1a79a0e26c7c9083080a1e1842116ee304103ebdccf52062bf9578e864afe05f3983c17b84f7bf4d1209460b438ba34839d978ec1c5bf6aab43d09bcada67751ba23a5d39910102bec0c09b6ed88010416941c845ff0402963a715b14ae1a371e9f1913698b402ca86250046ab1d84ba0a6bb12686cac9ba831fa28fa3d0ba72589da190425b42548e220351f64ece113e51fdad160958af52e544b5d08fde40fe37bb84cb659bf593033e291841fa82a53a8f2c141b596c4ed461f562e632b2ca893c12a53bf0de5d2f084d18fd57562a366766d549e205cb36846670c81b92ceed909400a673e53223e9c3b1001c31f367bc9125089c292d5c65dbe914f59cfb0c1b702c7847f2151732806c02b5aa5e97871f60839b79d3e6feef3a3f696115c06e05b0429e1d5321042be272a689d6010378e100c44a1ea4c1841d88a61ded92b9c04347056b3e098fb4e93729884cf98a415354961cb468d38c8c5dab2466952e1863952de5d22bce831cc9e2f11fc39a4e1911e4e391aef8fd7e2f2868e7e8593519a4dbbafd163660310bbb68814e644e234d2a6214d3c2d84d5e78091ce6a3167f992d6c411b70840ca25c4251db8cb551e4a37170bcc4e0aad6a73ca6eae85ae85437a8c12fdf8773e4d6896da6b5510c65e8e5ebe2214ce0aeee893aac3b5169ace00fd20cd9084610949486651f579981d04ce71f246593a621b94b11bf2d4a53a9b7272a75ec707e5d6e9b94cba7277335b4dbe3a721bcc5d8d33f2d6521047d8b2e33035d2aa05920b16961a8e9f5091d1e8dcb0ef43ed663dd29d95e24307a3cbb14248a1fde5949b811c8cb64d561a5814c4e42db4c2a82ed3f85428951892885a92727050ec02883b4fae76a8ff11f88b279cafc825d492c8d88e6a21b8069bdbe3020b22284e21dbc649e9fea5576619b0d5a6c20662adc00a8cae9dc666fe84d2a33893fe7fd23a4b3602b1872968e09b248a1e5f009b8221019113733a639e314bce17972dd81107ac90a257fb403b80809e8a41f298cfeb0be9d87341e9e8026b58446c8f6efe18ba6b664917d764b67f883a1573207364f3a05ea5ac4124a247da1fbf361caf35db0991d02d7cbeec4e6867b454e1c728cd64859a18ff1d72ade435bb8dbed3cf274c0830a6adc7e9aa296f01e451882b007fd972875c54a2a76150e904347222648306798c03ad2eabf742dee661f1ff2ee8b7aad5061f408e8c42347c0400fa8fd99c1e5d560ffd9c45e274bba4f8e3de5f6899a9eafbc05c680f29f0e759926432803bd257f347279ad57e0cfae3ac1820766f2bfa89a2a054c1318608489a6149c8ce98f751f4f90897fa87921b04885242913fd053c29d2250a857eadfa84a16b5b4ded1c2f89d817a2e005da0b94a425b9890920c829d60b0fc66656f0a79faa3e5496317705e56c49f69b3732213b652469ede8b62007b1e8209e5e0eb0842a86321e49413e7d1c48539b8c8689d932b368452d8f00a415eadc12aeec854463da5ec3e848343d7e035b69fc369e03b45d426c80cc21af387870e76a693d616c271e15dbaf75d32b19d1e6ccbc63ac71e155d002ad2992a221a39a21245e9ce4d06a5d450398bf7fdad0fcfc4edc1f12c3a109d98d1b24c341201ab7ad61528931ed868d4ae748b0935ab27304779ac23257a0b4e40fcd390fe49200b9b9a086a7756db0a14b232ad01be5356c40d31c1aea918a7206b55b1f8d4af81e1cedb29c4368641a2a5d2440c70d6908fde350ac8a26c7f22d3b2f21b2c3632af0f2e9f3c9e52e99d9afb03fddd83f09cb28d206f67086000dad45940c134287e81c13e4bb5bc22ef82e4c5e061e69084a781a21997c063010b394acfd38162430147456b9de74aad70a060df546ca38407bdcbe464b01aa140d5bfd0a61d5158d157b2f9eb42ee971192939f25f02448cb5782b36d5ba5413dde40e8944b03e710c31402a37c420dacb73603c0e62af12c136f833aa3437064f488a6685c8f0b0c291eb33aa33b81a9e4c513e6ac55c7175c3073335d9740cfe4ba38839d89625ce94c63e04178c0c76553844241b8ffbf82c53237a02390282e7cff22065a0568345157ed5e428b107946d5003cc6ac9718b020a21e589158a93d1249c81b6f5edc19b1a461d4886f40991c89be5c89ac77ec5486d77b12852972575eea908a0d0ad066ab7ff71ff02720e41b559dfaf5cc99992a1e82b267146784c9faf18f0a30b1d2c8926de25b4a2b5d4efcf00aee059031129823405e5c2b04465b50e6a6300c519152d3b175595d3ee4a7c103c90a46683bf33900973a27e9d6caefb89a1d148934865286ceceb2eb2a6dcffbf93f40d6b8141f32df15c9b7528bf722ac0cfcd16b5d3b5992965d3f49c62a5516dcafc36be9bd117d498ae3e827c74807a0f6120713c018891bc502fcb2f3a3ef5df92fd36e09123278dd884d45abba015519edebefbbe07b19dfb6eee736e611a001af8182f7d28d2df78f84999ed714f241de56318517b31798f9bedfae1771e43d28c74ec01236fa605728da81f251b890f3ae9375925ce406c5808328b62d0c1f61d507fee0d603e2271f6256b38c56b4d51698152dd71d572f18aef64bb8956cfdbbac8291420757829b48a402faa763299af4f927add42dfe05421d6f381015fd09950cb1d49db81efd5ff650a753c43ef4d6c56742c82663cd9ccde44c04a9d16efb3f226f10221eb3fb7fb9cd29e0ca0a9bef0f9b7ce84f43ea5bf4c7497642e2ee7ea1cfe335df4a4cac533cacec297ac86541cfdbb6baa5d5ac993ea49936cd81b391fd9942ed996e79fa851a07adcefd1c2a8b8f57810e54d04a23aacb01abdaf8d2e2bb8819d1ac0ecd13c2e7af62f8407d63039b06b61192c4d7a82f65bcab66e05e769b154a29cc138b5119a732f22dc15ccdd3beed12318162f437d9da4532508b3af66a3e7a10172a476ec9de4a05d3cef956d6ad5bfbb0ec8923723117f5a046ef296f2b5d88055a90b443c25e5b0b877e3474f28f2cdc79f49c618f44aaa02d3d1638363ea5018f4710ed06eeba5776f89dc9dc4afc3fe942f92f90326771de423f8057cd1463a148594606857c7410f3de1767f89e692f64aeb9c80dbd6063943261cef4c11bf08aa50589f51ac7125d9fd81d005b2ddf1d8b1c5e7f97512b4bc9df1655304994d4c05d0881eab395a5925b935d565194e3db86ea73f9e0cb618907cb0aa0d73720855ad56a543d380344559d846d0c87d2f66c010826d2f0a5280fafcc55461e94a95c98123087fe7c5afd72020c01b5bb159507bac36081c5efff00bc7e17f6289e9d2c2c5e9beb2bf4dc4465c3e954f2393e70ceac216ae4898e4821f8dee3c88d8c1085a01d6abd2a23f0ff1f6b92df037992c73a602cdad1370bd59f65f93ed6344a19ffcfae76acee1f8e2444d8fa27b346719541a4dd20cb3c9f3dc615bac655270d1362c9edad97c23f13b6897d65ef2e3d67b2e948af7af733a7252895958bf669fb507eae810fcadebad364fbb3b5d1a68a1146f82fe31c5e8c26c46d4fd4d134123c591bb36cfce94158faadd437ae2ef3920ee3821c8ef268c38cdcdbb380ec23ad56abf2c5cbc9fcdc291c02269af47df0e6fbd50a5e974c1e50024975c390f43b6da32df921c90f11d18d6577fdc99c120126a58a070d74d076cd1b872e0cbba7354f30dd38057fae7023c6dcd63527a1c00ad87e6e3ba05bda510ec142acd5a3c662373ac3f7a7cc8a10ed678466446176938d996ea81ab6002cc8f4c41e5269d007637a81878e6bf0bd562e0d1b60e10da7445419432b35618cd61bf90079a08b5a0fdab94cf8f36d066351118484606ed4fbf014e02b795dfd62b14b16ba950be77d5c317dd70fb058c0d896e18a1073024ed35b213beac45d0eed9cdc684c954bdf3c18f365dc541b68618b8c06e1d8a83550339c96e752a82d077cfb8ce0211447e4bbdbc3b127712004fb7caea05d2641dd30ce23b7d8b9c5530671324cc9aa1bd271ac6c9be835fec30741fa103c877e6c320cd6c0bab7d62d3bcb698b6605be618784663158b717f37ab03000f60911e4d3e33184fd26170e83152da85d24a8f095334e59b7b62cf4c2ed8400688807ae631070c134aadb8290c356a9521af9333a69da38904671ef89c4d97004d118138a5d8f7d945a78c0abc64881ac3ab0c86060db570be08d3dc2445032afc433cd6ea04a649a3b523471a5d2ad1431f3584f5142b2b5046b5f39bc69d16565ecc4219043483b3906d8e2623ca40a26cbf9f18f51ac5259545a592d5f827e0ffe8b7ff77b8f63b0ef131ffa4c5667134828fe82cdc754d15c7cd33cb6c0077f04d332e7ddf9956d603ec3c9c0a55e1db71f19642c7f876452248a70f3b1cdfb9da087a8ca3055014df3aac51deddcbc64dd8d5a8a221d8e91e749b308113fbabb99460f5785f829d0b922389a673cb6dd7cff8c3d288a77f07823028da1292b1f04a858b4630c8306b8fc5226f918328babcef14b9dc10c1880582877e1d450f991999bb052fa475bef0846a915a9d643aff92ed06420fcba96e673339288c3fc6586c13eb5f581e6e7200163a5e011881ebecf259bf2b84139ede52f60e389ecb2a3b2d75a3cc041b4fca6ef2cb232527ef829a8476563013a4d336e204e8ec63f06e0917808f60401e320069d022f661d3ba23965bc66155ac3623f30b29bfc132f9b01495f4c869c57e8cc42cd2242e72fdca3ae4727c02d9ac881c0a145fa3f17edb13347bbd19819e570e3b016cb952295952683f30761f4f03cc105cb9a489ad41d21c68151afe3def6129200c5de1b932ab352119aeae31df5121e519547cd28f05371a2c65f716c239ab69d20fce2af689bc05f99ce4247cfa29cc49b35b4358a3b42ae5c91d95fb99655002e2b0d6d535f21a6c58b9f82a2119649e434ee728c3248ed73b956bb9667ca6b77443651eb331f0b6520d62c5852e3370acf4ada80391db77ba155e3aba9e8d821c2ce1d31ec09f9898ac21b1112dddc3ef15cb12590a98531763b8d043596632648e08f8b80de1767e74855ad7259558e9228544d393351f17db1a37f55314b6d2a39ff65ff22247ce74073deb24cc708bd37b6ff151588a7c0d107521ae217f13afecd55265888f3263cb2bc924a1faf27e8345e2cab32ebc6aae03b62d115631527b68761882befcaea90c0102a7abe55e1764b18199cefbe32acd68f76650b5a83204dd677195396700b93e268fd11b30f6aeae2288c6b31926a50007a0e85eaaf2f7a6c3c040369df5111e1f8e3e91e40e7095ea9820a37d8ed5408382dea8fa3be255dcd2432ac79c3b2a1072aee7d8c69cfea210a58c99dd9c158744bc20b17e8dba95c49091b70ba4f4038acdad5293ad3f47569799a1203e607107575ad494b4857aa06a2be9d0d4ce2aa07260c845899f56d89fbfb7e18c24130097b1e41bc0ec6d7243558f2191bc1ee0b5ece2d0d55c02d3abb80cfd8d10d6bbf360068dcf6cc17debe3a4e8ffa3287fbddc25ef9071872116ea57fb9055a978d85768f3dc162876957749af16512290c1d094e99bde4c38c864d31bbbf85ce2ee488f943be32e2727633089385f2b461d8ca9e0855827cda5fb219fb1717ff0a838ed1b27d82947288e5e8c14590e039cc9b1ab4a33501362144ea57353d0ce3ae7f845bdaecd9e1304b5472ff61d5f0fe5673507d3b79974ae6d6294e16ef1918a4fa4a8d023e89823124327d21b74fe4ae3c1ad52801ad8210a3cefb6ddc0297038ce08f9e4f49ae8f1f99e5e293e702e81f1c7861c1c445fcb48d92ba77eea9b250ba2967d506925efef7f261ada0b3d9f2742502c26d1ac9f750649cf0155a7e327a3556b2bd9f20e009b9601165f5c53fc4b398b8f3f7413d065aa3fae26a12917fdf716f1360181fd1dc81444a57a6b94fd298742cfadfacd30076bfc6ab47be4f09b66cc76b022a47ca5191e725f35288a70698d51c5cf43b77c2f80b2b3bd4d35c2ea033008cedc2f042eab11def2bcd80ba95c3ee8c4fb917435c62c6aeaf2f4a7131b429b5cb2a4ff23081ade5ac7c55b647aee9aab520bd3869f9c93c36aa7326ca6bff7af3771a201959164e8a0978053f0c13e7a87916c47c7d7ba0d1f1d1c826b8873eb31f7d22a07070f5d5fcad09bc1de1960c10ec0ee3285aa2dc35bc42d8b206ddfe119b621ff709b9861e00dff5f5caf92d33c79082c90e3e9a811cfb225ccf802c6c51e0dd1ffc5d73e22eecdb9be76ac089ac3cbe39613b2e6f4948ae91a42c6ccbe9f278c01e611cb037785788834d4b7306a5f94e0f3bc26e9731c53db35e41fd2b41ea68bb1a63e21995c245b20761bb235c4bdb5248afed40f7531fff2a4e300c80ad0019f2ee27ac9ab8181f077b7a0bebcaf59459cac8ff3758a91b051e89dee24ec65c2bd9503a6ec590079fb13870f775c5a56325c5317a88e5c439303808c0846b44c4526e81fd78c7cc680ed2993cbf600b108e88aeae1e772b0b0f8ae5806f883cebfb726b4263916b6969cac42ca7a9e5910be070fb64d007d0a7315d91daaf0e62bb35b5e24c1e2a5095a294afe753fd690a0f4bf298d5f1d0e3fb5311399d876cf50e48fb817b3e8c7146c1d061f07f7d0710e182991462d793227590788cd56236e23963e9a41d9b6fee0eca464fe0b5ecc58b78151f57622a97ca700863f02eb1bc9d78d1082feb4f383d1e2eb5bdcfb4fd72020922208eb19f7e809e9e7b184a15bfa5a0a43863b5de3e191059cb78bf74c53bff6104e6c5637ad0121186bf2df31b19bf8e87fc74d1c71d8d520f181c275958a409dcb7bac15021cc57260697c1b3bf9c2db1a1e87f6db39677caf5445f6e85853823a0d358472092cd19d6850a62402d1e425a2d7c0fdb61d2e02f86928c3fbc56d2ff048147b07b2e6dd21224c6bfed79c76e49a5d79a1da6722776e2c4cacf6a71f4fdf8c3421001e4747173ea80422461affd27e3af8ce9fe9eafe6bd76e47ca4bfb9f54e01787798b5ebc4cf20038ab846044605785202d2ebedc82441c938599daa982200676dab42c70099280328e8899465ad1cf74bbc27cefdbabdabe85be02dcc6be2780dce9f7f6b97c77bf7284dc71ce4fdf48f13b36553b372bde71f04c1010f719c9e06ad95bf42e16fb4c5e9f5779cecc8a99830b62e6452cd80f8843dae91517bca2479d53cf305a9d43105eef240fcf12a391e92c49596770405de304801d27b4f39ddf78d44201072df5a66c3753d6cc740f42fca640ec8a9c2aa509941c2d8a8d9dda693b87dc33c5b641332770cdeff855b01b7d48304376e48925c963036bce8d831d7efd5f879447cb403c7e4d7b01c62e0ded5dbd39ccdbb545c5f30bdff5b42ced0c62cd22918e3598f7a6cb7c9cbdb7cc8cce49be5fb57fedbf702e06430500eb0bc6b794b6172145c3d7e0b18e36f98f98861f8dcd0511b707b91dd03caac365db1699acc849db995026ffed7dedce3505260acbf7c4b09aca9d0b09ef40ff6a704425e1195ffcccffca50ddb4c9cd440fe5d8f918774b2852b7177d38c0a40a9325dae46f5c11c30c0b4043067451cd714925e0799f22524d0d0c2997d8d76af181579491ae7893f4d087b1daaf0a5e38723690b34d70a1d0d32b0eff8e2c99c5e155e65f42c60d0fbcc7604b757896e0910f97a1f49f6283ee2e09e4434f0300af0e0abf8b98762f5c6295427fe8e0fb6748b7e7aafa26b02b1d819b307c1f70a1a3ab3812f41571c1b85dbe9603288f4afce9591e8449d99a0c07aa3eb97c33a646725cb03d52fc53ba19064ce10ae7544921f23c2eddae110359572c3d7b81433fcb47a8ed65977667fdbe70a029dd1f9ce9dfa4db220f8a2bb209257dd8d49b864ae9621cd32e3772d453207bdc88ea8be26869afcb5a3c114c5ed52c900da7ed5489e6e199c7ff224a18275897d832bfe121afc7d663750613d5ad6f4ca266eaf1f69c6e0f1617c647e6a5830ceeb3f4d5a2e70730fffd259aed34a0a6d955f29fbbbb96a7051a9b8aeb3de2c077dd632f0f91ae4d2fcc13ad02527dcaac974cd68ce11d09ee138b586a61ea77db1f4d8877146da47b710d5e19796318dca7b99638e5450448ed4b0776331585b6846d0b3f504854665421ba13580166db9ba74f35923efc566aca7d76e32ed30283f7c5b96a12a4cba7130476d1a3b19df10ba213cf604761861d3b4264b342a68811bcaf6c26c4ded5189b15e6f0deafa36e7396871fc0054270765d9cd75681bda91ef8f1f156f51528c7e724218c156417cabe8378dc773cd875b748baaa0360dcfec3f8b5fd79fa15758af830260018537aabe640e55fe9e8d3ba07fd4e849035112962edc1ff6d26a4bfa0e267a938b358a6d95d86a3208e3a1ac448dd043cbae10f847b76a8a20ce0a559bd0f3cba36be6fb50512452bf06915094f7a7c7b1a7a517c7fe3a666249c3368fc161a3932f2d10cd88276932d3d2315ec9bbd8a1511216be52be76d1cc48820c98e773afc3e24181c2666fe05cf22c763103498316ba0759750d575156016a55d457f3a538a4822d1f4dc598d9c9e25fbb0381f2aee56bad06d5c6a1430f3a6fd2b33477e72b8f91a066301dec27e6c9bb69acb1fe54f692ff9b20e14a2a71e577f1f5f7064bef074b0654239599257dc86b12bdec3376250f97f5f005ae534014c6a69ff11c135d18b4dd33c13e4cc11018b7445104b16e6d94b580049efe999a49bc660ce9f8757ea3456c42319d1f91e21277ede209bcb9bf2da1aa875a2c36bff51aeff4e2c01151a0243d5551e016b5e1ef76d8a98443d304604391d823e2248de66f8a0348170e55f5b635d380ccfac02b8fc88df515c0ddf26c4c9d9b7980a35b819aced7c720ebe1fa773e33e84c79dad6f46d3f9bc35e92d2c337a767350d906769ef475cb72fe2e03ecc358f1ee1074f5d620b36480f21255cf596afae8506f5a7b164314cfd890f45fadb279bcb742345ddd9007b4492bde23b40195702ec4cf12305ef29d751e1a31c1fbd3ffd9d29fff53a3dfb371227c0f79f3487ce0e76d7188070768a8ca64021d32ce22f13989dee1c46be72b0d2faacc65a02532940e3759cfd742171bf642d8ad3321c96fb7a6da32a70300226f637fe241477c36bbcf1c01a059b520194003c1f6bb6983d74395fa5b48f914801b01405c4d89b666b6918ca546afcce05aa80dcc4da69af0bfddf2a5460e","public_inputs_hex":"0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000211521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a"},"decryption_aggregator":{"proof_hex":"0x00000000000000000000000000000000000000000000000ab6871f5e0c604e230000000000000000000000000000000000000000000000063e0c5faa3697e97800000000000000000000000000000000000000000000000c4edda900b7425ccc0000000000000000000000000000000000000000000000000002befd5183896c000000000000000000000000000000000000000000000004d8f5da040d18947000000000000000000000000000000000000000000000000e372e41542c47b15600000000000000000000000000000000000000000000000eb6fd9729b1572bba00000000000000000000000000000000000000000000000000020aebcd8e4cd90000000000000000000000000000000000000000000000027d1ef6bdc1bdc6fb000000000000000000000000000000000000000000000003b63834a1a171bf2700000000000000000000000000000000000000000000000007d98d752da17665000000000000000000000000000000000000000000000000000218b2af27d6fd000000000000000000000000000000000000000000000005fc5b00c064fff6aa000000000000000000000000000000000000000000000005475acb7f772fc6a300000000000000000000000000000000000000000000000d5eaea21fb7f5474c00000000000000000000000000000000000000000000000000025c5ad7f49b8206f35cbb5b62e5b765b61ee0b49a1f8770bea9d70025c2700523a6cdeffad74e2d17c339a3922f7ef70421fafba18febc1023e82b90cd74104a98902574f6d1c226c706d93043c153de5bf841a134de7656959e544c04017eceb0069aa692b1b09d09bd7a13e5ed5ec3350da8de0eb3d4bbbb261473acd3a03e1f56acf424efe0c6de5bfd3bcd37d09cc02b16d1a369712519dc1e780047008bccad88b1517ab06359968bb78d1f165250eefedd6b57eab508d2953f5240a27b3c8f21fbfafb207bef508a58b51480594683a51f7ea0ab2210a377696def48a79ff7efbaa8e0823f41ad197d6b1ee7ee556998280123ec1b9aaae6dd280edf895997e038d1c1c25a9dc3e528367cd6ddf78257f8f4a4c8c13a20283316c3eb5d11cb539b99e5806618d8b39dba1be4c13bd33f165d43ef169b3b372eea8224a06ae43caa0d921207c4f8a36cdc97347efe815d7f3321ce22eb52b6258a38b98f20205e737b6f61ec9e8a86e5cb149f99b66f85d8a15b805fb61123bb0b5dec773cb3b297fe0ee07c1cdbd49dbcb2961b3969e1607c7518a382930487888e32d9bed12a4b7298a26da1f07cba3368953cf9f4a5c127d3fb11a2477adfeebc6be03f27dde2c51742d449697cacf031c884d94c35016ade6bbffa6a1ded069664af9617665c4ed7f09333f51d61f47a9365621869531df3ff6fdde13d7571858c89dad97081a5d032db062ed586adde96838ac6e2c39cb5d50c316062bf90f0b49fa7acd55e6ba1620d7097883c0c1432d691f8ff8fcd8c8075c631e9d6268bed9ca37f2e49bdcf215c9022b55733861a9e7bb732ad6df58cfd3446e7ec2067b2746836af4f7f89c06f25e697f3401aa322174f68a210066a5b63035baed84e1406dc44ec30e74d91129f27facb96b2eb96dbda62db646bbfaa784d1a5fba7dba608196df258f5020c01e07ebef101e89b91d5dec206ff84318ba1638547fe25aec6b87d674308840cae64a836af4ef9ea8cd2ab455927f23633e086b6f6b741485e76bef2b6cc032bb4e67d1a535f5a810fd3cc9899368676a27a90f311557be62e196f20ff52ef13f86ea9ba08fa7eaabb18c6b1afbb4f7771d605e4b1f2be6fe991394d0e2f0626490f444e9ea9fbd75efdb2a25d56667fae74512e2fc0acbd49afee2b2c23e72373c15067632bf4180a0cdc54e9c9394e7154b900a48eba42ba1f44b1d9fc72128f4b3bedaecb9e70e27ed92a46717fb202dac7e349a24736229c5c0836ea091cf2028b1a7eaa6a876513fa071249ea34cd4e1e27e33cd996b948907ebc60990d5930a69598cdf690b2c2c4a1ae184affe235d854ec81da06447e2738270b582081a0574605536f0f836c368de05a08b39fd2d65450c5c5d6d6cf1d87a7cd5a0a079aead0c9239382526011399682aca59d1ac68ee5c18aa4c3f61d13d2dc9f1940bbfceb517ddbe3d02b950661d24e7ed55d0550eb3689f421b3a2a9056451192059863b2f5bbf2fe06feab1df4d0b2165f90a3dbafeca86e3619716ccf0980bf1fe0721c73d5cfcefd66b41e50aa7eadabdd8c455f2779c3b3398132087261797ac8754b7044232941f4d74818e58ed96ec7f2caaf282f7a531758636d2020f7ad743013efcd97a076ead680b17dd244cb55039f07344e15a1a8e2f38870a29edba2439b0837a103e1a8c00e450006e6b589a654b8ccbefd5de0045c8e21c1545fa845e862ab714a464c0b6e00265beabcbaef383504fcf2c5f80cd7360b10d5c24f04fa84e22c6a8e81696a7d4debd55b2058c64ee4339f7e1acb1c8674f2ba9e03d761ec3bc086bf76ce20afa4cbf8328033c71e096a1b21e3e1bbb980c260b3e581b7f7afe02b1ea39b3b73031d0b96080204bffcbf3a04b43f5365bdb249ae301120746252ac7b763df1d20111dc3827f858fc8b12665c07364f9e6042b8adb4df253af298a3b669077f4aeac1aa8f6cc0b44af53c3c1fa4cc56a075d2857a42d3b05cc24d8e929608bb93e738fd680f4cd8af7e6506808ecef185b2b2ebdc44cb7a9ac2efa142c54b09cd054bda803a047c314060dc259796a51bea1238b6879fdf41ffc4269cbc9859c6220dd57373fadcfcda2548125a04d9447280a0696340db71c34a8cabeaf6636088c0331952ccce04a506c57ad047ad7bb9222907f98f62436ed0491d278b5ed7b273a27601258a9ff54c7ac8d3dbb68cf3f1749e0a4c950d439213e3bf0524945627736e7e8fe7f07029b26979c722849f62eedf78929c852c7bd06c10d5a69015ff5983519c3e0937bc3c825472e519547273409dd5643ecec7d1630979937792e5ed4b7cfaa110c7d87ddb2e600ae1cba2ba7b5464a14183ed799350e4d18c512d14b66266eedafd5d32e1141c543d6db248547031da4097158b87ecb7845a8f659c5c02b9ea2ac6f636f3e3d4015739223049f86005714294ac037d14a7ccdac80e89882751314baa8dda7bcb024504e2e27c6a101a3035ff6ef42b99d56c20c824d839f3d98e3131932867267d947c02bb2270046b020798f033e718352799c0cf9d26c86961efbf43562dc263b9d250e36aaa19d199f1d2789110fa14b42d0cd40a491ba99682f72fa9b301d08528b0cd55651c7fecb9fc18e4a725043f251aa9b69be5d31aaee787dc8771fbf31252a90ad86466fbc0eacd6a4cd0d6b94c53ab9fb4c6398b68c5077d5fda03d7e7a1d60824bd6a0859b7fd17c5e3ea922c212b33b1b36a7c5c060eecdec697ba38c0f968500d89f9543e48accf3511e6d0fbb18f089137a7db279c6abe326dd15532f7ce125605cbd676ab45449b2ac04d190420e48f1c0a029eda9944c2b651f681194348f35c5946265da9ae92238bb49a90f7a05e723b185bf8083ee7448ec9517320c5f10fd917b0375e3c88136b03d916220b42d3ef345c9617873212e1e6d1af9a175a5da49ca79114ab1bd3fa654bb9dd1cb9a287c2278f367cc7ef0e35d064982209f67c25194f6d480bd1af6da9c21e590bf77f8e556bc82b3f36629e51ba0bd9e8e8f2a59d7bbbc20be61ddd3d78efba879719d28c116762ad44f7a1c17212136d31493bc280107ecfc3e7f56a3155e6a8dc95ac459573523c5db8e9726948905e785fae860f81331108d539f7f1b8b4c914936d1a06976787a461aed10fcc5ee13a48ab03e06c2eb6d80964b9faad63932663a5a02cbd28b40f159d3191fed983fd4a451dfc47cf6bc11201e2d2faf3049e8ebfb3c7422ca072136f3294c96e6a0fd7348adc0080aa0cffa5ae817a64bf74e78406e6f12e2ce53b20f24795f8d35edf71c5de2ab175685fab6f9c73dc06620bda26f67208c9774b7e52526e6da846f50a867b53b0c964535f5e52c14958463d58d2417c74b2a25d8012d22f20b89f69d9cafc3744aa9a3435674cc2d23c1e68cce4216211907defcc128f00687ab857061bb9fc3fb5f4d2bc70d8652f2f9cf0808c69135eab8fe2b05192d7577511195d93abab820c9f30795df0b10c7f55de58c03d19139b28f5d90128f7041b76c81e6f1444d47642da90ad4e2903cac9481dd31dec5947d5feb56114e0d1865a7bf262de8132d41340ad3aa5e1938c8692b7d9e6af1633d74a3a21d0b65ee6e74bc543b648cfeb52beb7fc0e500afbeab9bdb8a425e43efc6e0ba03f2678dbb80015852281938260095069eaeb25fcbd581d8ebd64babae79e41d11efceb26339550bbba1e36ecc3c91f71ea9f1cd23c0ca5036758e74edf7858e14ad89c7f9c8f71c3cf32fe11c37f583c932bcef67203e999e45700d3a5816870afe8a87a72d9be4d639fa57feccb778e06dbba738460fd0f511c0ad977dd06206c54e18c4a57066e49e55e207d36107037ccc095ec6fa2feae81aaaa44b88370906d542c020f0d3b0e2d80843567501f13066782c96646ccfbe031283544962216425d07ad1796b75e2e9d402062083da2d671d32e1c34e3647bad0322067911656aa8439e56e08c30a1384877c1a32e69c3440d5a25f687c175239ee6cc75c04d11f5e379d21a7e8b32943f9772db2e168f4f1b7b087ee8b7119fd1f18aec2269e83608b1d071924f9f2dcbbfcd070bab6d7f5a447e3a3da752065677b64921849ccadc866212b4a74dfdfe4b8430bdb74f4a9f17a6a99495f0886b442625f2547a6f4500d0dd6f5416d1f40310b3fb8ca4a72861597ffa93f0f9c8e3955fa2bf139bd084522a6726933ffd9e47077365bcf03a3ee29f99edad41cce14458f0684554a86d665240423b236917bcdb313a210c1946d234dd1fe542e742668fd0d64b6ee71070db72111e430e1f27beaba2482951428015889eaad1482b4c2b3157c8eb633ce89bdb8cc75ec48b40e2c50fc5c46e897efa49a7e4b535b16dba729a619462465eef8f964af5497553815acb5545f74d5d677d2c0c1e339416b9c219e50a898ee88e1fb2f1db566e9696f253d79f68772a0aecc07896d2e49332c0f18fd89635440b49b7a80d33da8557759c2fb146fb125ae93087fc2b6749e6a0726f493c7ea11b9d67a1be2dbf04a50105d2b489c75a2efcad8658fb92521580df1b992af777cb0e3ba2c825243eb0e6bdf3d0a6978d1411104b59a46f0bf720a79728ba37648d5d60c06f92608a4dbf1c99fc3e449de7549a7e50762d0232c2b116994593bf466bb0dee85010662193d8ec2fd8a25ecdb2a7b34f7478aed831b3025e984f0ada44ea0333d2d72380d11ea5c1b5db3d182548eb261b77b13181f48514e3f03784963faaaafe860688661a8bbdce565ead9184b182a9802311d19069af44ad2e08496a94d229f869f5a2830c420d4e1bde78c230d8da4384b220c88dc1324a1b82f54e16647c431dd5011a1421c785e130013cfd882192d72f01374b6f2b7f6011bb2194cb772e337033fd01038f1d77810506f0f93aa4eae822649b30fff4c315bcd99b6cb7bc282d60e8aa24fa71bc754482ab58e0d721c3c2ec839f0f8b1c83585fe7ebc00368d3baa4a381440565216c8d2bd04fb96d32311649b8a7a3ca661a0cf53114a8fb32d33cad95318a6154178abc8c06440e21d22e5c70296141521743a8a37855d70b311927b84716de2f8fc092c91eb3a7a1f221f26aea2f88548f3af4b19fc475bcf49786adfc8d7e7349e1693548f512a221f381f9587f1fd3cf3cd069024a1170feafba8229cebb8923a7ab8fbd856196217b589aa128af06c775f25ed9b3aa9bc23803fa5965ee8b7794206585ab63ebc119c919779bcd9b1e024982c6fb199b1679c9af2da8f200348f68ec3b2a00c0f17a15318a649ae59bada674e3a149bb1d6431461411e2df3e2aa1958b722048b16a07dd414de7958c407c3eb8c760779358ec1e2439b58b8d00378bdd2e9e9450de9e777da0eebf79d558d029019a16d1f8c076fa5294863dda329ececee07020721d000c002b15cc3fd3cbf0f0fbc80d75c1bb53ae1b7a244fa507318c57e4c2d6fafb6038ee34355adf14f0363881911843ba3c655934688976f48ac38ca4e162f5b94463b38687726d27320a2d8c97ff9d8a79e81261babee9852ad0958c210d8ae1fe24253e9229b410ba1f11ab4cad40642dd7c818aff33430752c5f36205ef43d3378c9968b0b16a881404aa643c3d555f84b78446cb9bec133cbc10ad00b9207a883ec2d82bad1051a9c39453d1b63188f2c5c052643e597db86bdd1621806a9ef2c04e8cbd9fdaba392d0fc997cf5a49811bb4c8b68f104ccac280541ac123db803c502457797ba6ef0da3b43641d8406b78f1007c09560d45062a65259bc7636f785729b40176ebd0ca1c6f2ffdb9ef2915602c4a6275ac8fa7cbe71adcd397d6043297be672053abf8339f90c137a367ec7d6a782a51b8285f63721916d859797c32299e9413788fc27175800859f9bfc7d3939d4f6b5cc9777b2f198710723aaa5fb5f57450a5ceb88a674796d19ce70fab58e730240326a016922b06a453d8c9f93c4f6a13bb87c227a297ba159e626f5823114a76cd7b0f39410cc25f0afd48083c62151fbaa1853b27d7a17fa1b9e2fa28a284200bf12a6f5a2f550ca3ccff9b6327e4f7d9983a448cae3e7f9ca4e628df133a58dfe58d741b284a4e0b987dcb8dccd1f21874cd434280feb0e942f8f0e5ad8fed96b73bd26f024f4f697877b9a2164985542d2969f52a5b57c433eea4f07a445aa9e833a37f15dc67773cbb58f86cc70a1b5779b019ad98af907b00c0a1fec4492d26e38cd72085f1cba3e5e9c54abcd00c2108dd54aef936879809260609e0787ae4bea1f618a9df278efdd7f15dfe9247dc384fa4f971588fa356b4e67377e8fb6251408f2b82baac18f833a2875d3bc642a5838432ce511b38005fb7024bc01688ef3f670da2fd6a5794ae972da048893816c19549b73421e0d585751496ed222a7cfbca1d415320f214e7cee462d0689ffc31bd05f4c9a5ea3b1791bce9fcbf374d2d14004ca233a4b174ab409e968d6399606527b624923f381eb8c17b2805dfab7cca280308a2b5af4f1aab2d05e78198ee4536f2deebeafec0eebb33df157fdbbace2e747e434240c5cfe49275180859832b2d69923bf08c0d597d9112eeb73568c91fd8ea19778ae26aa6a872c78c89c957d2518154b689bab59b2ffe5bc04361ae131dd276df1a28b2e06a9df883220bcf76424cf289b94d5bd90056ca2a6828f210f1ff74decbe9a8961ee9c5963876aa8face86ca89e9b038fbc5b1ba5bc962c128107f963cd0d439c4fdb465eb7834c943f21c1f3d46a7f057b75b42aac8c472a5c5521abd8d5913f59e4798f9edd0bbe643ab68d2d5f2d4d7b7dba52dd5c8c01fc9c7bdbc80873c54c494f37b6f6fa4536f37b101a16a7da7ac943fe0f666304c7a6659b382bfb94977ea2f22e244f5224369d6025dabac1e08614146ee2501ddaefc72b74b56f7f72ee8d908bc52a354105655e85edb6dc768a83f3dfc0a317c66db9a34532d5f7356579f0741dacf256f0af84798ee17836c4a40fe4b5cc04c199f7039d38a4bc5bc7f2d99fba60a0fdca779b92f02ad5aaf76f1fdefb091381e6eb635e8634516faf93dc50ee8b484c07b917f28d75a657b00a68ba0b71015191755b4d819437281f7565299d9d5ab193750340c3436c4e1ec8af65c201101a107fb877fdcdbd711cc0e888f6a3918bbab8780e9651d6c8bc29d473a25a263d53cc7805cea42498373014e822341492cb0b5846f3651c4de21d6a02b449249f136615c3fa59efaab74793ee99dea1346e28a861afe2f931757642bd3ea028fac43df603f0eef3eb0387cd88048d715e733dc835545af0a970b030f6bcd40739b526796af9c7636fc1e0023beae25886eb1ec9fb69465f36cb8db0143282114a980ff547ceae0ae0328b0a2ff83894cc7efb1d8d3268e623e20257143ddf1690c3f89ba0cea9077e7448601c66a68816c5d7adb596e9df73a9e7d4f3b5151e8e6dba9f96d00681e644aa6cc33f2c88c76c3f204f9e0f4abe5667921cc5dd0a77e105a39ddd0f53525089e44c33468ab32cf7761380760d7c24966908f7291f3cb7b08e1addf167d9a5eb038f1f433e711c6e67adc478606f09c33400928b09658bcc54c224cdca2dae3dba6278e91f8a416474312cd9325be5c38d7f5d7e07f54faf13c87c1b95c0f7f21f9f879644988df52091918a8fb17defcd05035b2c01c5516e3c3a8cb16dbfc84ebf730cc91c289ae1d9a2247ef46867c4c3f8b214c7e231c7382212bcfd3bdfba84e5befd2d04ead43d97823562f066e832f1f31e830d7e9747675bde52ae766e1b6f1fd1868b78f7acd5e595aea09c9dfadbd42088b2218775b734708929cd68d07da0d0544236991c2b7cf3b1c8a535fc2a741e04a56884e7fec5c8b37e770b9ce037d6d9e5963f682452e8d786a57ea1ca4c11dafb71c383f1751378b133a730dff48da6d638942abb532cbcc712975bb260159681bc8d1480de29c8d1791ececbebef1dd47e7055855bd32ab876af784fad1175e04f583e93f827e3336d431ddf5f2a9347290732acb2c1e70b32aa39a1b61535d18522b8e335ee209aef0c81c3ba6c2902fb07d3d35c978a48c6242a5c0a0e5a2a7ab8189202cad4eef209e352db7c4548e813f6479e5ae2cdb3eaf32b601bf6758cf1a0678a0daf9120dc0fa7c4a30f5602f2ae2cd64d49f6cce7de341603a1e9661d5b7553dd96174da8b0763cb968592477ac917adc4dafb94d89522c29bcb39fadde0390e904c2638779567407ab30dabeb7f8beb0b49947dc9e8fe7291197bd5ebbf948c0326263c7cb67a772e3ffefa7d93deb6fe34c83db575bd913e74272a40222380713812899c64497ddd2f0ffa40bd9435ccd8dcf07812318062d0ce0a9887f24c623352f28434512a3f02497d08f70e2acd92c6f9c7db9c70ce84edc6d0b2e6a85061ecbbb6d524d77900e545869d585f02e66c11c5683262a89acb3a1d01fbc647be2e4ff6850475f86dbbf3a5533cddb04f9f759ed3f8c1a58e6111f8ab44f6eb753ce9b76b08ec2c1ca1e8e4a0b12f2468685e97514f80084f9ec72296c13c390174cb7058a1cb652524e152af41bdd9db80f4b26159d019c5915c863e55429c22b5df53f3c2c708d16dcddf9ca6144b3b12c102f6e4726abcb1e662e072858dd31ebcb6fec808ae3818a0e1529bd7f967ae9e4965f942953dd62008a7880ab0f25d1aba340e932e7ca5cfc654d05efae33677e86e73c1e16c255cadb04bfeb1f46d2a57eda2e9e0125c6a25e611177db2f81b52e4ac6270c29e8222702c6a2590dc3853e75a6dc26aad0dc20e13b71d273946564d9d7258d3a80b86cb1a2b655be37f8b0f78265f6bb866571b384a0792cd7ccd6f9f61514364c60b0d46d272ddf70cc26a62f1132e1edd6f617e815dadc8febd9a74e2e645c1d5c5e21b5c8747ef39578966822fb78829299d4ffe3e411f3d3bd5f092e06ad539341a69ee6302e31511830369f8bf22dd7204099269cf871cc0997191f1a2d248d13fcc11672953d9f5ace68497ee899bfb32b4b7bbeb46047e4ef821402516b83cb78caadfadd9fc6c12a398d99f1b878ec66b515e7483a9a0cbccb083041b2aac57d88f01d6d47b3ae84186a84792b52151c25f6587e21f8e6b07b1c0fc000b43896466542644603e14a650e16686d853e62db267e023569d5469c0809a8d114f1007b7827216e65fafee4b603e959641d11636efd348dbd46ed560624ef25c04f501716ef93cee02aad775bd9ee6919852491f3d29b942372e1882085117390b7aeab2e50367937357f59b9f3a24d1c14cc6497dbc0818fbed5041553c7bc5740d0e9aef2bf62243c1ca12f8567c9d3f39a40110770253767fa7c26a871d60e27b75d9e142b1162a4130343e788148124d712988045ef96d48f8920e88ff5c9f60d09d2f77ba9b403bf6c6d0fc0799048710792a96035c73a3e450c283036736b9628210c2497b080c5abc5372666768ca864d18a6f203fed35cf272622ddb9a3970878f471c80de989f90ebd8cea6120f8bd4c593763bdbc5a98058b8395cb3225c7060bb36acc2d22c0338edaac7d75befe39ac4206479c86fa237ab01ca34b0328eb6699049f29188e92e7354e69dd727f960802fefe99c3d80c097dd8728bf5d5825041d67e5ac9858d748bf30271341377176ee8bca796f60e5c92647f6871a1579155741c7b634c268068d9941eb9acd84569018e4de82811c568bf95b24c04cb3cda2a83b14307e668bb54db1a2569360d0caae00498f91e0494092814900d31c78e68f63f06cb09a782d204a10067a78a04801888f8b707e647e4d114ea7c1e7faba4176bd2977b7c1d0e97d6e3ed976c66930ea8d1ea278f3899dc95fad95147a22de703ff82865da597000525a1bd0656d82a84f5d2255bca0b9bef6038b61f0e92a5ba1395707daa095674120eec99fc8ac392eb5a178588131cc52f9e6d45d766712bb3eaf094693a5af7a4f746175bf7aecd09831d846931480245a862576fd3a006b07b1fd9c2db51db74543e275d9e7cc26f2d27452fd95e45041aaedc81d99067f7567db1fe9759e84b4fb2d1bd93bfefdce805a6cabc48654d9d38ebf014ecc33eab6dfe7fbe12b4007cb490b235982a55a611fdeb14cbd17ae02f6dbddc8b4ab0e8209312dfc80292b4c072401ff7336ce8055b3fba6b7058057342f5e55266f9bf87220d9c9ac9fe0853f3d4f4d019d829195d64b5e10165b80751e8ed7945c9a19a7960580b7f3a9d7a6065ce6a26fd3128a6e7e2d8d168db7ce213fc75b4315145a583fdcab1de9a71a41fda973ff1490721ae0f3ee28441b6a46876cebe7b6d692d551bd2746bef1fd28623c1af91c020c7c80ce66224b03e7b9106196f94b8e0c9b5ed67af49449aa45213b412de6e24c842e747291b84fa0553bd0fae951fdf6e70d9412ef0ba752166a5dcf09ecb079b9de21279373de98b9ca059e22817390f90680d8d338d7e35d0dc81751a500d4bd7104c7cc8e52af19d74caa3d6928ce9b0bca085889fbf59a86959c2dcdb13df2f80658bacae156b7f1f4865db24b07fbb6b27942099f9d61e57ae43c3c723bfd52acc8ffaf4fd9cb1147322dc31f7ec5671c5f0ca5c5155ed6d9f2e2413248241722317c262c5bc54875d886f0d90780674e073c5f7f921a6666e3048cd16d5814372e0236fe69a63a4f7118d7eb75bc4465dd01181c9a197ea72f8307c2841f6397a9baab5a97fb04e109006d8127d604f0b7d6736162c40bd8b1c09221e0922d2139987796499a7fb097c9d0a4671b898532936cccadae32376c93dfb059ad2915af4a690d40a516c72d74ce5d4ab9e752f58873ea88227546e021f701702aac2d40191a410dea52f40a4bb996be8002e1c57413e0dea9914098f8a880a8c387b2e3309533a4c07aa51444956ed8fd2a314017814af55da77e40ff7051641fde9c392981a5e7d779529be3e0be0f6890bf19b24f53ebdbde0faf9d69f10e9fe5c469cd99083a9c572eb19b940f9b0e8094a75fc98a7131f2c923408620f3d931493d5a7f759cc6a21edbe9dbe770a109672574d8bf6aacdd48623e833296a76845bc8dc66f9e0bf4d05b889bee67c4c2852750c30d06903783da6ce172c02a8f8653d8211beecc345dc19b6f7cbcc803d66ae2128ba1ba055f761ca3a1aad5843b15c067428741c224242e5c518ffddc45193b3a5ecc922cb13c0d0a72d4130a3f278c1f463f66423e4710027d0b319048abd0a6fbb66c195b9d1452e2e0c7c0e8374b22294a2129630e2cc3dea0b98c2c13758345954fa86eab6ab710a512893425e0268e10b34ee3e2e6a4fb03040687698c97982e30bc2a8a2b2fd2248da56fa8f2105609a497670832678fbaf89a7935cf26768c2c03f443fcc9012eb0427bcaf487189c20e24956f34d23792ad3f62596d3767ac07ae38f3fd4b11324800ac6e2d023729dd5df77dc4e0cd81d0ba8e10e90a266711e69d577bd406b1e218645fa2552bf747d0d1a87fd66c7644155f6f5cba9115d114d7631cec08770d53cead9762e9eb24399c4f97d98fb3c365d158799be00cccd3d20c3b090b431b0c2f7b7c2ef333ec898e469fa101f1adf6f73815f3b09bbdad8349034f1f90f27a8046dca9070e149c1c23150321e612756a0c2c7942d7e30059be4908050ad08455affab49f27dd02ec5169cd976f4567515baecfbadbe97c382d3f1826774b8c096b6862b740081c523d47bd0b9639994b4ec95ab12845086dd5fd482538707df831ef734170cf55d329490aa2e0e49643922241739793a8c75e7386283a026efc405fe0598677d246226a3ece5958e0d48de439e944a05fc643d0641fff61f83de97b677bf4840a977ef5625bc385b7591755fe89ee851ea5d15ebf2655014e914e878ae7c1f8282c6189ecdcfd5110178786f7faa3b05de46b43cf17e113d1492df5303ceba990b322420dc3220d8d4415f89381df54d5ead445821656c02d97585ac6f793d80d33115e53d6415f05f0c5d90c3d77c3d140226be02a3ee5564c70975456bf79d426c8c33efdf706cd178ac3d687e28e4d2204547d0fd5af73db10f93e0b6c523b4ef081426af896697aad46e292ae0c5fab03ec3d0d8dbfff9127ba2e3c4eb3e7a55d92890674bd6132382df800faf6b8b407cb5e2f34f902cc48285bc99e308f2acf715a3b8f884aa21e144ea9fe2a36eadfba592775d95bfd62c6d71c3089734d67d08cb00dd85d23185796cd641089a93e19b12beac08125be0a38ea27cef99c4cdbeddb4b278caa2fb8d47e2745d5c09e83222c0efa0932bba4cb4546e28667c714e5b2b974eabe47fb29b3f78cd5b43742f101e475b51ad373772e93166104a79b4efb175dac3b442b52b9504c88304419102b169b8bacf4d47e97ab2c69e9f5654f5a0cb6f55d8fea831cb665e93bda050b2ad2bee6223c4f9377be451cb1bb4d7d5445b1eb9b58549ec07694f34940216118b0e8add0f6d3ff6199052ea7668d13954c7caa2fe6af1663263c9a6210ae0b01981a9f0fc8d9d2eeb8d92572ad287146f3bf0c20d23de8d08c40c78dad3d061c8102981be8ed68bd6f3b7bf768c40b86a06b6da762153c82d045133d3976a50aa6637a1eec72f4f886a043ee57a4e6ecf52067f7f1dd512d0829b36989687320fec9da648b374ad4e0247d1fa97700c286b7e1103f696941674e6f3a6f9ad4223aab447b6b691951dcceffdcb30f047653433c4f8a1abeab791b5ac76b18502575e60cd921ee2346e0ede35620fb30907dfbfa218913647ad415c9a48ff09305f729f2bf156c6478011766cb094981c086bab98c384f67a7dc579b811ba5c11f607412b5d8cce667aade35ed4efee23bb261c4f278e09b257d502bc61ae48712daae64de5ae2bb6d679b43fd9669b02023aa3eb4d01d10c4603bed9416d06f16bc522446aa35b063e490767fcc90a7bf197281c5c295c1e0415dc27d1e29df06d659418c6c2d19a198517016a3c76ce82fab8d540e656ec7170093c80446c42bb2ab499e9eeed746759a7814701b945e602522926c49aef0d217ba7f3ec0ac1495c4281a7b4a9e1464ce6c97788d617101c912e7918be268957e80dd37fa1c1daef5d845aae5ae93f3d73e13e95b2f974ec218a863270fb89a30f0a81f70f815e9e11d54842d2997758695e9e919c49ee9568970dd985f95ad792c4de69c602ca2a090660e70cff6f75b308d6b58fd3d41bf6c2e1ab5210914a167f161efb208999866e918d0ac040afd7f314e279aea6a9503c0fe69b94c0596ec7204937c282c824f63fa7641a2732762083c6fd099f8538f33fabde31df0ff298e4d691403845eb37769b4a7e325f7b4b90dc138807c7bf8c94b3308c8131021dbc702ec1b0d2773d0d393b5ee72fb1b8c46bf5a2d96e008f0a27bd052c6ce663df466bc10b34b779436e65c9da5839de588d9356cf64f721915fb81cf1107e5861a4fe51ceb29793e3bfd641066d690ab8a27bcd93604040376f886a2bddf62b99aa21012f03a25b1808210494fbfb7c5a6e98c9b0183db9d9366514708d725bd15d6471c37dd3530ee3ab502f0bfaf4639574de4088216596534660c852c04f73e77270a40fd0514c085057ceb30638d46641a2429e8aeb06b89a01c7a19bb9287955c17d5b7abe02aecf3f5e497b8ce13ff4402e42e044c3d270c04db487f5d00f04a0697b0af7a5e27a2a7c04d5bbd73c3e91e2ba020b861cf43676ed66ec9c18f4129399d25127fd3a40f696bb6dbccf3829d8babd3a13e6aec6f0e6bfa632bb90b26c35be6ad3e9fea820c0327735cc17a5244802c9466dd98739a06bb34e742dd044554adfcdbbe8d300cbc39c983ac1a9222513ebf33bd79f429c47d58beece61dd0536dfb22b4ee0b661069eeadf37d8c1456b05f3e19ee3e058c732c8d1ba62a6a39994da45c80490e4a53575e6a739a01d8817ced669ef6dee1dedb34984b2688cd26b1fd6e4ed748d8dba93b6ee4a21493066c1a10f7fa44f50a02206b342a5e15c483ce678c3f87fa37ae6701c4105ea18957e330b13106f6d62f55dd9e05f880741b3c98407fa02b8b956a0b42279f01abf85630fbbe613129d4a710020602b5ce04f6a3eb5c329dc34aa10af6d222c111845bb6016b2cd69f48e4edde14106fe41edd0cc43ad8800ca68c5aa644bdc46f64654b3fa561e10dc84c4612059e79b06da8e2bfb580c268e0c94f02625b1feac968586930f5fa9daa1d52e2166bc02b21f5b5a51eac5219ca1c42b6f563ed338189e34fa6c3e8d5922ca880062f1a75d8d73e3cce28ccd91097acfac61ce220ced87115497eda778f752709011be2b3aec8c33879c1b7d9f041cfea2e9f10ec121a0887e278396eb143953a261cc5d74697fb31f3b525f2129e9dce7542e906372204e07e6d68f59f2bfb9d194e059fc2816cc7b5faab72efde88c1daafb9dce629acf1bdcf7516b7551a8f0d427ae711881bb9dd56f6ddf46d96644fd2e7bd577830779ac1e706b6dc298e0b89cccbea03d22a857589fe4895f822cd5c5fd1634ffe6ef44b7dca09e57cf30126afd120fbdc1cde39aa695bdce2ca770926968c345e801bac05c163e4bbc4279d02daac4bb1a306be1941d646c5e93050adca4277432645be6a4ff50ddf5805d1070e4775d2d6e006453d6b67e718f8289e49ce80b841212e613e505452a827677548ec16fc965456db4e86d45b4d59be76f01e4ba348015b12e75937bf20","public_inputs_hex":"0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0101cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db70000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}}}, + "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.11572825, "runs": 3, "total_seconds": 0.347184751 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 0.608609847, "runs": 3, "total_seconds": 1.825829541 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 0.578625417, "runs": 1, "total_seconds": 0.578625417 }, + { "name": "GenEsiSss", "avg_seconds": 0.124242194, "runs": 3, "total_seconds": 0.372726584 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 0.223503888, "runs": 3, "total_seconds": 0.670511665 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.568398333, "runs": 1, "total_seconds": 8.568398333 }, + { "name": "ZkDecryptionAggregation", "avg_seconds": 49.0470405, "runs": 1, "total_seconds": 49.0470405 }, + { "name": "ZkDkgAggregation", "avg_seconds": 20.15144375, "runs": 1, "total_seconds": 20.15144375 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 1.504357312, "runs": 6, "total_seconds": 9.026143874 }, + { "name": "ZkNodeDkgFold", "avg_seconds": 62.8892475, "runs": 3, "total_seconds": 188.6677425 }, + { "name": "ZkPkAggregation", "avg_seconds": 2.15679725, "runs": 1, "total_seconds": 2.15679725 }, + { "name": "ZkPkBfv", "avg_seconds": 0.328596097, "runs": 3, "total_seconds": 0.985788291 }, + { "name": "ZkPkGeneration", "avg_seconds": 1.329930791, "runs": 3, "total_seconds": 3.989792375 }, + { "name": "ZkShareComputation", "avg_seconds": 2.693458527, "runs": 6, "total_seconds": 16.160751167 }, + { "name": "ZkShareEncryption", "avg_seconds": 2.489975329, "runs": 24, "total_seconds": 59.759407916 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 6.047533777, "runs": 3, "total_seconds": 18.142601333 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.097465083, "runs": 3, "total_seconds": 0.292395251 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.226985783, "runs": 5, "total_seconds": 1.134928918 } + ], + "operation_timings_total_seconds": 381.878109416, + "timings_seconds": [ + { "label": "Starting trbfv actor test", "seconds": 0e-9 }, + { "label": "Setup completed", "seconds": 3.041689125 }, + { "label": "Committee Setup Completed", "seconds": 20.243337708 }, + { "label": "Committee Finalization Complete", "seconds": 0.007484375 }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 304.504600792 }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 307.024598667 }, + { "label": "Application CT Gen", "seconds": 0.318660917 }, + { "label": "Running FHE Application", "seconds": 0.003694084 }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 79.271051417 }, + { "label": "Entire Test", "seconds": 409.917920083 } + ], + "folded_artifacts": { + "dkg_aggregator": { + "proof_hex": "0x00000000000000000000000000000000000000000000000469753df342aec0e0000000000000000000000000000000000000000000000006e4d829d66aff749d000000000000000000000000000000000000000000000005f6499be2b18300b20000000000000000000000000000000000000000000000000002be6db4c5558c00000000000000000000000000000000000000000000000eedb4b6056c881e3400000000000000000000000000000000000000000000000867b55169f2600f58000000000000000000000000000000000000000000000001bc9f519f74446bfa0000000000000000000000000000000000000000000000000002b6d6df53885c000000000000000000000000000000000000000000000007269f9ee62308a02500000000000000000000000000000000000000000000000fc8ef0ceed9608e82000000000000000000000000000000000000000000000002d32a3b72f08734b600000000000000000000000000000000000000000000000000018a5af26897d800000000000000000000000000000000000000000000000665ee11e35db35bab00000000000000000000000000000000000000000000000c29f716629ef9a6b300000000000000000000000000000000000000000000000ef868c0047d6dd22700000000000000000000000000000000000000000000000000026ab8350fbcd619e3c65ffa1c0d29b2cd62700cf8d71ebcb032227e21f28af6c3d9993353159827a80a592b60afb7bd0a5547279d7b7b6d745bd21e61cb30dd2fe6fa64ed7ee81bddec82e6608821134bc64e48afc548d54643761d84f4518e91e1bb576a9b3a1efdee0270f6a11edde9c2fd71e9de5a134a7ffcfa996506f17b7ded320b946905e9267cb38c0568773f0534e90cb7b63d7211d220cf75c33fef50d1b5a5100e055a47bec0d9798656da3a784b526caa90e53303c14eb63b919c2949535645bf0d9547c8dce32e13dd504bc6be4948f9635a4189cac34427ead69640255ad33c0d14a348d9edcbdb6ef4d4b0a379e7fdeb9801b76a0da963bdb56d89ba8f3a0210b7c42426c99152b8bbcdf2b36f1c1855f0ceb7482c347de29259ad7e05498a03d6dc7925daaca12e8661b8410ba878a1497660cac39150a0d2d1ca7b556cd5217867ffbd422a5ec0f3eda032d0da1dae248fdb76aa76f2aef8583db964d1491f5aff0a8af8585259f308839565b5bfa984c574d4cf02f2552749a67f4ee7f91fd21add31916d3b40f4006b7a83ebf00c14999182cacfc3b853fa33eb52c4bc0d7a9f47486d5c9f5dc5f1fba6a4c5edebfa27f2b9d47ef6a7de0f966356dfac080b3dab741554edf23aad2a27840334a132e4ccc09bc438f503346aad442b0214216a2005c8c7a405b68cdc0032c8a6f8ff1c663bfcfd165d7de791193c31c30dd9f2f2f2a0431be2e4e4f1fe5b4be54f4df8b761ca81b8634e42a243789b7219142015b3808efb3a739c019f139df3488006f7e463f6d19fcf228bce9fa6d613cc0c01c36911240ee0501a0cc007655ea1fd15b8a1ad2f8d1ddf49931bc0bf177827b2cceb2956abbee92ecef40924cb1e618efef90f0194b8d8df4745ac94211fbc877e3f1c1843dd9c04c81659fd1071b38a33797f6a6c395143aec09c8901b9d02917af3362e348cfb2e9aabd53ebbe6b3565508163ea1901f214764dda2cc1ea3a3e121b0655786495091b1b6eb05be9d77b5149795f94e4bec6e2b183149dc7fb2a6c49197a09199db3d00a13ed9d3fe11a18f5fac973b7349533df4922301951689c04b3f866c7f8425c047237cf8d409e8ba27c49e924268817c9e9055066deefa06a0b869217f5fed30835f92bd9662aca74e8b06d29e525ade59a1fb24ae716ec7d331fe77e298f28024bb1fdcc10c4390920b5592dc36749909d0c0f625429773d2393a4f713600cabfca65ec0617333ae5764f69790a9f97bab2f003fff339c5965274e74d713ade8d48df30bf48503c109d9e6c124b70b6277299d62ad7b2b7138796b217dbcc3c4acf6017e1bd6fdbc366957610a0f79eb4f17ed4c180fa64e56f7ce6c9c3aab320f1552db2274fcd7a4576de6cfb0d3db3c1ea4b1ce1f3afd3ab96c2e78be599e3d94f43d7f89851fc6ed825d6658f005e815a7cd1bd26641e038a56293b8095be2f287d24612af3ed6ba44a1f3f3a911e3302a5476eff60236e0580cf8b25f9efc8244b30ed8786f66e39dd4456aa4f024029eba613ba4fb9a7994e9ce5afb6923109490c62e0c988df549f2e9dcc3a56829bb3f7054a4b8cab4aa1769fa658620e87d592cbc03f9a71bca647f275288ab02dab6867241f0d9d7a67a4b33933a1709d75274bf5bbdca414d3ec79ed120fb064ea0f18c25252dd3a82b941e656196dae6336b385dbc60cb74e8cd668c6f631fa549c5ccb417790c6e015e9adb9d640012eb6c177e18eb7a319e4fd7a266671c31bccb4604b5acb7226d74e075d4276b4e9410a595e91dc5f864d35c6e95b72d4308c3be58dc29da40e25d1f63a36940f572b901c39f31e355b075dfc95a2d2092ece23729f1bf832fac0ec1ccd994f016562ca1efebf54428b688f1603df1076714c5d7d7ccd14a87225c15c5178ecb331bf323ab74f8240715fd0166c33605affcaa1b4b75d30f034659860df6af13b6ee9e6e5c1cbc5e131c2709ea226c11cf137940d661f8f254ebfe274cbd889768cc23d728cd2c5082012f427228cd21cf49509a55121e5756aa33e207a548d5a8f15e9f4869564fb2fcef1c4577e81f237f271365e73cbe12dfdee90ca49799cfef0cc0af26d57ac820007cf3cfc9073dcb3915ed77c7e50f58441879c2c5bb450ada48fb95182dd02943e980031a257c5bb6c116e0e17a1efd1877dcc3a988d9b5680f05f5dd7927bbd0336c143c0aa03fbd503e48787f9296c4cdb00160adecdd0ac45174988fbc535e86d6e4250b3b669d61dbca0afec215f54337b74b36820a3794c4824c9c812aecbdb860be238bcc0ab026c45a1329e6ccbb1f0e6abd83cb9910e5468c9e22ce6c4c3c5d1b2892318212462091cc6f082e3946445e67456576206a0777bdafae6420bc108d0d545b60b47a8647c9acec31abd3eb4a940ce24304658f9ce2ff50a5f5a656f80dcec5d9ed6e857d317c33e96e97e19dea73112a08b301cee1c5a26264b2033918a03534a00b9812d0dd992842efea7d0323402034bef78dbc11232402264318246fac64ff0d90acbd0b1930b4377ba08f35e91ebf526c3802640b74f6b786ec118f486e979a8f8d9bcc19307bee149a5a4d459a145723fa863d8230753eb1dd08f4187ced1f037867e3308affbc4fd535746aa6760d018f75bca7bf3396a59902320058d6cd5935e44ea6943d47fb9d45ccdbf8ef6de9a5231e2e4e2a1558662b10e223298ce2cc72aa45a021f1437be501b2743c08bfbc9708db8b2cd7a9ce17256be9fee29f8d3dbeffea7001aa6648ed7fdd53c0e9117d88379f75466d7c2cac187ce9ea645f137c8531c417fa019100cbdf78be2763d38414b97a6b00871a954bdc2d8e1df7970a636eb0e9d8e5acaaf827e955adfe4a8a2a43598a4cba094833c3e3dbd1f261a5d75b5fd313a5f240ff30d989823dc45a3c3ec90082c626ae27a87be9dadb61eff083583ee3568378e95a1ba75f4bfda023c820466f822adc5410bfa974d63142c051bcafb9f1b00ea44385db8cf1d0c36be3e71899c00cc356ea4a7f52ace0bf72913493c437e3a0cda118619333e1b503b29a7e008a0159746030e90700e49f97c957078dfe35497ae3c4fab77cc9c6f080f84637291d9bd340a31415e39c83c58e3f3b901ccf2e2e2356feb40dc496008982bd189118de77f08ce5a1515447c4a04a8c35e9c89f86d164f10d3df3c3701d8148e5681c027f18f613d5f5d6b22de99a7e7c5d1154db617adb0f4aa4b140a4cc4946ba2c6a68f07b9c6b738d4039291b0e7bce61dd330aed09aadba81f6658e15c46251c44e7e03576d9a2d902f93d2cc226b10a6e44adb7ac928f8275333365101c821d0ce2c8b77aebdb5aabffa4c641b602cfb62c3c98c9f966b2d950a193a4c6e4196464c9eab169d15ac12cf5c4ba880c3bb829afc21ada4364d6a4f81299de8b21679e06e1e557e7c831c03cdb1b8629cb925d4fdc7d44fce7f4ec1517897ec72a2ea327bc07437e1a70ce03392e1d739c2f913698ee590e86c3a7c5e01e70292b73f7dd483bd896583d2bd30e736926227cf981f05cd4d718c6f8025127da4b205d377c16c2d412e76aa1486b04538dee538376fe1522413e41df227cdd52e72c8c867f4a3ea87ee1328f964365e79338fccb36c83a0518445e3c09aa2005ef15405379466c46a41cabae490c736d70476264f3b78287804bca97b68bef27c409883a5ce11bb36fe046d33a2447f7cfc35506203a2e3a01d389c060aa3604cc237fbc043276f8bcbfb7d2d1adadfaba9cbb70a9220efebd6ffc62ed96bd9bf02387189dc44aa75b967986a74ece75106f97c17256baec95737d6de103c6d7a2118ed1f68bbf4fcb6647e625e0a210b31dd9399f49665aa4c27d88a639767a521dafb03ac0ee4f48394d53fe6d209740009a1c11886f6c721cf91e29e268e8cb2a7dba0ab3bcfaf639f230f5cc32a443d1f9afca79a77113657e12b3a4023f130838602c3c9bcda88afbf7ce8fddd16060ec4e0e955bffe48ce7795a750121a7304f58bf35b291bff1e28314c5e1aec8a7301a178b3e2e76b81f27951a2550b817406d894712963faeeed76b7069a0c164e685255aa0ec245c5d223b36b05f760307a8d077c47184df6356e68a3df1b6e645a7a1f68c3a4ec30a79497dbeee8821f4cc5aaee04d9c0c28cbe66e5cbcc48994a4efa36572343f005e385aaa301e157faaaf53694d880169b06e0699f6aa01cd14868786ca9be54a5571ea0791df156e53a49573829a7493415d75ea54104d64b800d4688fe718eb4069b59f93162277d97ac74c5836b5078e713d2181bba46a58bd1be7cfb2d359473be276081f1b6ccb434bbb6f226e9188bc9bcc75826becfb2b589244ff57e97a2994682a3e007aad5202beb5bc1a5ee1fd908cf63b2d36f76e04978c164ce4d36a3646d7581474432c9e22f05b48726087539ee97f746f73cd9d414d242ab5bdfd7609d0b32155a9da08e2067a889517aaf8cb024a8168f94dafce6f93b2f12dfc00641d5218c35fd80cc79d0b4bbb1f766f20ad4ecfc79ca94562895c78057587e9edcf1211fca08a404520fb20794782ed562767d84c1e6bd5d1121a2b58b63d633cb3d528392519b02c9a33d6200c2b94bfeae8eb83460ba6ad4dff419969ce80ad48540cd03c06c82c17abae5a66cd8db314d6cbcfbed1472a446afe81b08ee8e710121bbc002b678d3e50d223f02e4edb538e65fe35f26fa0d21cd77169d208d1cea6188ad9b99abd82c6f89525f6f1034be005f761a9503d049c955ce43bf0997e192ee5118d2c94ab12ba2df5075f42f1572f28660109c53a4051c8aeefb2015915127447e3992602c10f17fa0248d4a63d3fb51d02d7c87575ddd2a228838d54e015405e3aeeb0eda949ed50111d0881e0d2eface34b66941e9ea7246365202f1d207ad9eaf4c7ea11527fa2ceb4c32496270b04edd79eca3542d97606c3ad946601d47aae61d27411aabf3fb0d5c57301ee1ffb7c01ceb4d04ff39180475d097f2b07fc67bf89790286b97b1d814ee45b760d49ddecf8dc3907cfae048afc82550a7f416d7de4c9a6e3a847c5700b4642b637ff9872096679d2d5921eee91c5e11110a2da4abbceb28aa0f9b4bbc132493a2ec0f2712ab0d2cb8d2c589168f9af2f14df7d9de5d4463ff031988639bc2bc42c844a737ca569dc14045284ac011e02992542a5ae3e06562f08bd9eb6d695652b0c0207c3514b292402a5745a60cb22cbfa325db3d30866e1739fe38aca41517e9076700489344ddf68c38c28ae9619b91304c3e55c8f4ffbb089b13b2f04ce5d61caad3d2cb54341b5d1a930d35511de0fd7e69802c35962bd45cd5733a23093208051359e4257f5c9cf68082b362f3744ac22c015a9ac28540106e7d03ddc305882758a50c372a580e5d70c09a720755d74fa777039850e88a289095bc3fda6e3de76a661307c2e77237916761e1dc2e2fb9e5e3e31b522be2e0d8f2e3e7e9cc1dfecb73a1a79a0e26c7c9083080a1e1842116ee304103ebdccf52062bf9578e864afe05f3983c17b84f7bf4d1209460b438ba34839d978ec1c5bf6aab43d09bcada67751ba23a5d39910102bec0c09b6ed88010416941c845ff0402963a715b14ae1a371e9f1913698b402ca86250046ab1d84ba0a6bb12686cac9ba831fa28fa3d0ba72589da190425b42548e220351f64ece113e51fdad160958af52e544b5d08fde40fe37bb84cb659bf593033e291841fa82a53a8f2c141b596c4ed461f562e632b2ca893c12a53bf0de5d2f084d18fd57562a366766d549e205cb36846670c81b92ceed909400a673e53223e9c3b1001c31f367bc9125089c292d5c65dbe914f59cfb0c1b702c7847f2151732806c02b5aa5e97871f60839b79d3e6feef3a3f696115c06e05b0429e1d5321042be272a689d6010378e100c44a1ea4c1841d88a61ded92b9c04347056b3e098fb4e93729884cf98a415354961cb468d38c8c5dab2466952e1863952de5d22bce831cc9e2f11fc39a4e1911e4e391aef8fd7e2f2868e7e8593519a4dbbafd163660310bbb68814e644e234d2a6214d3c2d84d5e78091ce6a3167f992d6c411b70840ca25c4251db8cb551e4a37170bcc4e0aad6a73ca6eae85ae85437a8c12fdf8773e4d6896da6b5510c65e8e5ebe2214ce0aeee893aac3b5169ace00fd20cd9084610949486651f579981d04ce71f246593a621b94b11bf2d4a53a9b7272a75ec707e5d6e9b94cba7277335b4dbe3a721bcc5d8d33f2d6521047d8b2e33035d2aa05920b16961a8e9f5091d1e8dcb0ef43ed663dd29d95e24307a3cbb14248a1fde5949b811c8cb64d561a5814c4e42db4c2a82ed3f85428951892885a92727050ec02883b4fae76a8ff11f88b279cafc825d492c8d88e6a21b8069bdbe3020b22284e21dbc649e9fea5576619b0d5a6c20662adc00a8cae9dc666fe84d2a33893fe7fd23a4b3602b1872968e09b248a1e5f009b8221019113733a639e314bce17972dd81107ac90a257fb403b80809e8a41f298cfeb0be9d87341e9e8026b58446c8f6efe18ba6b664917d764b67f883a1573207364f3a05ea5ac4124a247da1fbf361caf35db0991d02d7cbeec4e6867b454e1c728cd64859a18ff1d72ade435bb8dbed3cf274c0830a6adc7e9aa296f01e451882b007fd972875c54a2a76150e904347222648306798c03ad2eabf742dee661f1ff2ee8b7aad5061f408e8c42347c0400fa8fd99c1e5d560ffd9c45e274bba4f8e3de5f6899a9eafbc05c680f29f0e759926432803bd257f347279ad57e0cfae3ac1820766f2bfa89a2a054c1318608489a6149c8ce98f751f4f90897fa87921b04885242913fd053c29d2250a857eadfa84a16b5b4ded1c2f89d817a2e005da0b94a425b9890920c829d60b0fc66656f0a79faa3e5496317705e56c49f69b3732213b652469ede8b62007b1e8209e5e0eb0842a86321e49413e7d1c48539b8c8689d932b368452d8f00a415eadc12aeec854463da5ec3e848343d7e035b69fc369e03b45d426c80cc21af387870e76a693d616c271e15dbaf75d32b19d1e6ccbc63ac71e155d002ad2992a221a39a21245e9ce4d06a5d450398bf7fdad0fcfc4edc1f12c3a109d98d1b24c341201ab7ad61528931ed868d4ae748b0935ab27304779ac23257a0b4e40fcd390fe49200b9b9a086a7756db0a14b232ad01be5356c40d31c1aea918a7206b55b1f8d4af81e1cedb29c4368641a2a5d2440c70d6908fde350ac8a26c7f22d3b2f21b2c3632af0f2e9f3c9e52e99d9afb03fddd83f09cb28d206f67086000dad45940c134287e81c13e4bb5bc22ef82e4c5e061e69084a781a21997c063010b394acfd38162430147456b9de74aad70a060df546ca38407bdcbe464b01aa140d5bfd0a61d5158d157b2f9eb42ee971192939f25f02448cb5782b36d5ba5413dde40e8944b03e710c31402a37c420dacb73603c0e62af12c136f833aa3437064f488a6685c8f0b0c291eb33aa33b81a9e4c513e6ac55c7175c3073335d9740cfe4ba38839d89625ce94c63e04178c0c76553844241b8ffbf82c53237a02390282e7cff22065a0568345157ed5e428b107946d5003cc6ac9718b020a21e589158a93d1249c81b6f5edc19b1a461d4886f40991c89be5c89ac77ec5486d77b12852972575eea908a0d0ad066ab7ff71ff02720e41b559dfaf5cc99992a1e82b267146784c9faf18f0a30b1d2c8926de25b4a2b5d4efcf00aee059031129823405e5c2b04465b50e6a6300c519152d3b175595d3ee4a7c103c90a46683bf33900973a27e9d6caefb89a1d148934865286ceceb2eb2a6dcffbf93f40d6b8141f32df15c9b7528bf722ac0cfcd16b5d3b5992965d3f49c62a5516dcafc36be9bd117d498ae3e827c74807a0f6120713c018891bc502fcb2f3a3ef5df92fd36e09123278dd884d45abba015519edebefbbe07b19dfb6eee736e611a001af8182f7d28d2df78f84999ed714f241de56318517b31798f9bedfae1771e43d28c74ec01236fa605728da81f251b890f3ae9375925ce406c5808328b62d0c1f61d507fee0d603e2271f6256b38c56b4d51698152dd71d572f18aef64bb8956cfdbbac8291420757829b48a402faa763299af4f927add42dfe05421d6f381015fd09950cb1d49db81efd5ff650a753c43ef4d6c56742c82663cd9ccde44c04a9d16efb3f226f10221eb3fb7fb9cd29e0ca0a9bef0f9b7ce84f43ea5bf4c7497642e2ee7ea1cfe335df4a4cac533cacec297ac86541cfdbb6baa5d5ac993ea49936cd81b391fd9942ed996e79fa851a07adcefd1c2a8b8f57810e54d04a23aacb01abdaf8d2e2bb8819d1ac0ecd13c2e7af62f8407d63039b06b61192c4d7a82f65bcab66e05e769b154a29cc138b5119a732f22dc15ccdd3beed12318162f437d9da4532508b3af66a3e7a10172a476ec9de4a05d3cef956d6ad5bfbb0ec8923723117f5a046ef296f2b5d88055a90b443c25e5b0b877e3474f28f2cdc79f49c618f44aaa02d3d1638363ea5018f4710ed06eeba5776f89dc9dc4afc3fe942f92f90326771de423f8057cd1463a148594606857c7410f3de1767f89e692f64aeb9c80dbd6063943261cef4c11bf08aa50589f51ac7125d9fd81d005b2ddf1d8b1c5e7f97512b4bc9df1655304994d4c05d0881eab395a5925b935d565194e3db86ea73f9e0cb618907cb0aa0d73720855ad56a543d380344559d846d0c87d2f66c010826d2f0a5280fafcc55461e94a95c98123087fe7c5afd72020c01b5bb159507bac36081c5efff00bc7e17f6289e9d2c2c5e9beb2bf4dc4465c3e954f2393e70ceac216ae4898e4821f8dee3c88d8c1085a01d6abd2a23f0ff1f6b92df037992c73a602cdad1370bd59f65f93ed6344a19ffcfae76acee1f8e2444d8fa27b346719541a4dd20cb3c9f3dc615bac655270d1362c9edad97c23f13b6897d65ef2e3d67b2e948af7af733a7252895958bf669fb507eae810fcadebad364fbb3b5d1a68a1146f82fe31c5e8c26c46d4fd4d134123c591bb36cfce94158faadd437ae2ef3920ee3821c8ef268c38cdcdbb380ec23ad56abf2c5cbc9fcdc291c02269af47df0e6fbd50a5e974c1e50024975c390f43b6da32df921c90f11d18d6577fdc99c120126a58a070d74d076cd1b872e0cbba7354f30dd38057fae7023c6dcd63527a1c00ad87e6e3ba05bda510ec142acd5a3c662373ac3f7a7cc8a10ed678466446176938d996ea81ab6002cc8f4c41e5269d007637a81878e6bf0bd562e0d1b60e10da7445419432b35618cd61bf90079a08b5a0fdab94cf8f36d066351118484606ed4fbf014e02b795dfd62b14b16ba950be77d5c317dd70fb058c0d896e18a1073024ed35b213beac45d0eed9cdc684c954bdf3c18f365dc541b68618b8c06e1d8a83550339c96e752a82d077cfb8ce0211447e4bbdbc3b127712004fb7caea05d2641dd30ce23b7d8b9c5530671324cc9aa1bd271ac6c9be835fec30741fa103c877e6c320cd6c0bab7d62d3bcb698b6605be618784663158b717f37ab03000f60911e4d3e33184fd26170e83152da85d24a8f095334e59b7b62cf4c2ed8400688807ae631070c134aadb8290c356a9521af9333a69da38904671ef89c4d97004d118138a5d8f7d945a78c0abc64881ac3ab0c86060db570be08d3dc2445032afc433cd6ea04a649a3b523471a5d2ad1431f3584f5142b2b5046b5f39bc69d16565ecc4219043483b3906d8e2623ca40a26cbf9f18f51ac5259545a592d5f827e0ffe8b7ff77b8f63b0ef131ffa4c5667134828fe82cdc754d15c7cd33cb6c0077f04d332e7ddf9956d603ec3c9c0a55e1db71f19642c7f876452248a70f3b1cdfb9da087a8ca3055014df3aac51deddcbc64dd8d5a8a221d8e91e749b308113fbabb99460f5785f829d0b922389a673cb6dd7cff8c3d288a77f07823028da1292b1f04a858b4630c8306b8fc5226f918328babcef14b9dc10c1880582877e1d450f991999bb052fa475bef0846a915a9d643aff92ed06420fcba96e673339288c3fc6586c13eb5f581e6e7200163a5e011881ebecf259bf2b84139ede52f60e389ecb2a3b2d75a3cc041b4fca6ef2cb232527ef829a8476563013a4d336e204e8ec63f06e0917808f60401e320069d022f661d3ba23965bc66155ac3623f30b29bfc132f9b01495f4c869c57e8cc42cd2242e72fdca3ae4727c02d9ac881c0a145fa3f17edb13347bbd19819e570e3b016cb952295952683f30761f4f03cc105cb9a489ad41d21c68151afe3def6129200c5de1b932ab352119aeae31df5121e519547cd28f05371a2c65f716c239ab69d20fce2af689bc05f99ce4247cfa29cc49b35b4358a3b42ae5c91d95fb99655002e2b0d6d535f21a6c58b9f82a2119649e434ee728c3248ed73b956bb9667ca6b77443651eb331f0b6520d62c5852e3370acf4ada80391db77ba155e3aba9e8d821c2ce1d31ec09f9898ac21b1112dddc3ef15cb12590a98531763b8d043596632648e08f8b80de1767e74855ad7259558e9228544d393351f17db1a37f55314b6d2a39ff65ff22247ce74073deb24cc708bd37b6ff151588a7c0d107521ae217f13afecd55265888f3263cb2bc924a1faf27e8345e2cab32ebc6aae03b62d115631527b68761882befcaea90c0102a7abe55e1764b18199cefbe32acd68f76650b5a83204dd677195396700b93e268fd11b30f6aeae2288c6b31926a50007a0e85eaaf2f7a6c3c040369df5111e1f8e3e91e40e7095ea9820a37d8ed5408382dea8fa3be255dcd2432ac79c3b2a1072aee7d8c69cfea210a58c99dd9c158744bc20b17e8dba95c49091b70ba4f4038acdad5293ad3f47569799a1203e607107575ad494b4857aa06a2be9d0d4ce2aa07260c845899f56d89fbfb7e18c24130097b1e41bc0ec6d7243558f2191bc1ee0b5ece2d0d55c02d3abb80cfd8d10d6bbf360068dcf6cc17debe3a4e8ffa3287fbddc25ef9071872116ea57fb9055a978d85768f3dc162876957749af16512290c1d094e99bde4c38c864d31bbbf85ce2ee488f943be32e2727633089385f2b461d8ca9e0855827cda5fb219fb1717ff0a838ed1b27d82947288e5e8c14590e039cc9b1ab4a33501362144ea57353d0ce3ae7f845bdaecd9e1304b5472ff61d5f0fe5673507d3b79974ae6d6294e16ef1918a4fa4a8d023e89823124327d21b74fe4ae3c1ad52801ad8210a3cefb6ddc0297038ce08f9e4f49ae8f1f99e5e293e702e81f1c7861c1c445fcb48d92ba77eea9b250ba2967d506925efef7f261ada0b3d9f2742502c26d1ac9f750649cf0155a7e327a3556b2bd9f20e009b9601165f5c53fc4b398b8f3f7413d065aa3fae26a12917fdf716f1360181fd1dc81444a57a6b94fd298742cfadfacd30076bfc6ab47be4f09b66cc76b022a47ca5191e725f35288a70698d51c5cf43b77c2f80b2b3bd4d35c2ea033008cedc2f042eab11def2bcd80ba95c3ee8c4fb917435c62c6aeaf2f4a7131b429b5cb2a4ff23081ade5ac7c55b647aee9aab520bd3869f9c93c36aa7326ca6bff7af3771a201959164e8a0978053f0c13e7a87916c47c7d7ba0d1f1d1c826b8873eb31f7d22a07070f5d5fcad09bc1de1960c10ec0ee3285aa2dc35bc42d8b206ddfe119b621ff709b9861e00dff5f5caf92d33c79082c90e3e9a811cfb225ccf802c6c51e0dd1ffc5d73e22eecdb9be76ac089ac3cbe39613b2e6f4948ae91a42c6ccbe9f278c01e611cb037785788834d4b7306a5f94e0f3bc26e9731c53db35e41fd2b41ea68bb1a63e21995c245b20761bb235c4bdb5248afed40f7531fff2a4e300c80ad0019f2ee27ac9ab8181f077b7a0bebcaf59459cac8ff3758a91b051e89dee24ec65c2bd9503a6ec590079fb13870f775c5a56325c5317a88e5c439303808c0846b44c4526e81fd78c7cc680ed2993cbf600b108e88aeae1e772b0b0f8ae5806f883cebfb726b4263916b6969cac42ca7a9e5910be070fb64d007d0a7315d91daaf0e62bb35b5e24c1e2a5095a294afe753fd690a0f4bf298d5f1d0e3fb5311399d876cf50e48fb817b3e8c7146c1d061f07f7d0710e182991462d793227590788cd56236e23963e9a41d9b6fee0eca464fe0b5ecc58b78151f57622a97ca700863f02eb1bc9d78d1082feb4f383d1e2eb5bdcfb4fd72020922208eb19f7e809e9e7b184a15bfa5a0a43863b5de3e191059cb78bf74c53bff6104e6c5637ad0121186bf2df31b19bf8e87fc74d1c71d8d520f181c275958a409dcb7bac15021cc57260697c1b3bf9c2db1a1e87f6db39677caf5445f6e85853823a0d358472092cd19d6850a62402d1e425a2d7c0fdb61d2e02f86928c3fbc56d2ff048147b07b2e6dd21224c6bfed79c76e49a5d79a1da6722776e2c4cacf6a71f4fdf8c3421001e4747173ea80422461affd27e3af8ce9fe9eafe6bd76e47ca4bfb9f54e01787798b5ebc4cf20038ab846044605785202d2ebedc82441c938599daa982200676dab42c70099280328e8899465ad1cf74bbc27cefdbabdabe85be02dcc6be2780dce9f7f6b97c77bf7284dc71ce4fdf48f13b36553b372bde71f04c1010f719c9e06ad95bf42e16fb4c5e9f5779cecc8a99830b62e6452cd80f8843dae91517bca2479d53cf305a9d43105eef240fcf12a391e92c49596770405de304801d27b4f39ddf78d44201072df5a66c3753d6cc740f42fca640ec8a9c2aa509941c2d8a8d9dda693b87dc33c5b641332770cdeff855b01b7d48304376e48925c963036bce8d831d7efd5f879447cb403c7e4d7b01c62e0ded5dbd39ccdbb545c5f30bdff5b42ced0c62cd22918e3598f7a6cb7c9cbdb7cc8cce49be5fb57fedbf702e06430500eb0bc6b794b6172145c3d7e0b18e36f98f98861f8dcd0511b707b91dd03caac365db1699acc849db995026ffed7dedce3505260acbf7c4b09aca9d0b09ef40ff6a704425e1195ffcccffca50ddb4c9cd440fe5d8f918774b2852b7177d38c0a40a9325dae46f5c11c30c0b4043067451cd714925e0799f22524d0d0c2997d8d76af181579491ae7893f4d087b1daaf0a5e38723690b34d70a1d0d32b0eff8e2c99c5e155e65f42c60d0fbcc7604b757896e0910f97a1f49f6283ee2e09e4434f0300af0e0abf8b98762f5c6295427fe8e0fb6748b7e7aafa26b02b1d819b307c1f70a1a3ab3812f41571c1b85dbe9603288f4afce9591e8449d99a0c07aa3eb97c33a646725cb03d52fc53ba19064ce10ae7544921f23c2eddae110359572c3d7b81433fcb47a8ed65977667fdbe70a029dd1f9ce9dfa4db220f8a2bb209257dd8d49b864ae9621cd32e3772d453207bdc88ea8be26869afcb5a3c114c5ed52c900da7ed5489e6e199c7ff224a18275897d832bfe121afc7d663750613d5ad6f4ca266eaf1f69c6e0f1617c647e6a5830ceeb3f4d5a2e70730fffd259aed34a0a6d955f29fbbbb96a7051a9b8aeb3de2c077dd632f0f91ae4d2fcc13ad02527dcaac974cd68ce11d09ee138b586a61ea77db1f4d8877146da47b710d5e19796318dca7b99638e5450448ed4b0776331585b6846d0b3f504854665421ba13580166db9ba74f35923efc566aca7d76e32ed30283f7c5b96a12a4cba7130476d1a3b19df10ba213cf604761861d3b4264b342a68811bcaf6c26c4ded5189b15e6f0deafa36e7396871fc0054270765d9cd75681bda91ef8f1f156f51528c7e724218c156417cabe8378dc773cd875b748baaa0360dcfec3f8b5fd79fa15758af830260018537aabe640e55fe9e8d3ba07fd4e849035112962edc1ff6d26a4bfa0e267a938b358a6d95d86a3208e3a1ac448dd043cbae10f847b76a8a20ce0a559bd0f3cba36be6fb50512452bf06915094f7a7c7b1a7a517c7fe3a666249c3368fc161a3932f2d10cd88276932d3d2315ec9bbd8a1511216be52be76d1cc48820c98e773afc3e24181c2666fe05cf22c763103498316ba0759750d575156016a55d457f3a538a4822d1f4dc598d9c9e25fbb0381f2aee56bad06d5c6a1430f3a6fd2b33477e72b8f91a066301dec27e6c9bb69acb1fe54f692ff9b20e14a2a71e577f1f5f7064bef074b0654239599257dc86b12bdec3376250f97f5f005ae534014c6a69ff11c135d18b4dd33c13e4cc11018b7445104b16e6d94b580049efe999a49bc660ce9f8757ea3456c42319d1f91e21277ede209bcb9bf2da1aa875a2c36bff51aeff4e2c01151a0243d5551e016b5e1ef76d8a98443d304604391d823e2248de66f8a0348170e55f5b635d380ccfac02b8fc88df515c0ddf26c4c9d9b7980a35b819aced7c720ebe1fa773e33e84c79dad6f46d3f9bc35e92d2c337a767350d906769ef475cb72fe2e03ecc358f1ee1074f5d620b36480f21255cf596afae8506f5a7b164314cfd890f45fadb279bcb742345ddd9007b4492bde23b40195702ec4cf12305ef29d751e1a31c1fbd3ffd9d29fff53a3dfb371227c0f79f3487ce0e76d7188070768a8ca64021d32ce22f13989dee1c46be72b0d2faacc65a02532940e3759cfd742171bf642d8ad3321c96fb7a6da32a70300226f637fe241477c36bbcf1c01a059b520194003c1f6bb6983d74395fa5b48f914801b01405c4d89b666b6918ca546afcce05aa80dcc4da69af0bfddf2a5460e", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000211521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + }, + "decryption_aggregator": { + "proof_hex": "0x00000000000000000000000000000000000000000000000ab6871f5e0c604e230000000000000000000000000000000000000000000000063e0c5faa3697e97800000000000000000000000000000000000000000000000c4edda900b7425ccc0000000000000000000000000000000000000000000000000002befd5183896c000000000000000000000000000000000000000000000004d8f5da040d18947000000000000000000000000000000000000000000000000e372e41542c47b15600000000000000000000000000000000000000000000000eb6fd9729b1572bba00000000000000000000000000000000000000000000000000020aebcd8e4cd90000000000000000000000000000000000000000000000027d1ef6bdc1bdc6fb000000000000000000000000000000000000000000000003b63834a1a171bf2700000000000000000000000000000000000000000000000007d98d752da17665000000000000000000000000000000000000000000000000000218b2af27d6fd000000000000000000000000000000000000000000000005fc5b00c064fff6aa000000000000000000000000000000000000000000000005475acb7f772fc6a300000000000000000000000000000000000000000000000d5eaea21fb7f5474c00000000000000000000000000000000000000000000000000025c5ad7f49b8206f35cbb5b62e5b765b61ee0b49a1f8770bea9d70025c2700523a6cdeffad74e2d17c339a3922f7ef70421fafba18febc1023e82b90cd74104a98902574f6d1c226c706d93043c153de5bf841a134de7656959e544c04017eceb0069aa692b1b09d09bd7a13e5ed5ec3350da8de0eb3d4bbbb261473acd3a03e1f56acf424efe0c6de5bfd3bcd37d09cc02b16d1a369712519dc1e780047008bccad88b1517ab06359968bb78d1f165250eefedd6b57eab508d2953f5240a27b3c8f21fbfafb207bef508a58b51480594683a51f7ea0ab2210a377696def48a79ff7efbaa8e0823f41ad197d6b1ee7ee556998280123ec1b9aaae6dd280edf895997e038d1c1c25a9dc3e528367cd6ddf78257f8f4a4c8c13a20283316c3eb5d11cb539b99e5806618d8b39dba1be4c13bd33f165d43ef169b3b372eea8224a06ae43caa0d921207c4f8a36cdc97347efe815d7f3321ce22eb52b6258a38b98f20205e737b6f61ec9e8a86e5cb149f99b66f85d8a15b805fb61123bb0b5dec773cb3b297fe0ee07c1cdbd49dbcb2961b3969e1607c7518a382930487888e32d9bed12a4b7298a26da1f07cba3368953cf9f4a5c127d3fb11a2477adfeebc6be03f27dde2c51742d449697cacf031c884d94c35016ade6bbffa6a1ded069664af9617665c4ed7f09333f51d61f47a9365621869531df3ff6fdde13d7571858c89dad97081a5d032db062ed586adde96838ac6e2c39cb5d50c316062bf90f0b49fa7acd55e6ba1620d7097883c0c1432d691f8ff8fcd8c8075c631e9d6268bed9ca37f2e49bdcf215c9022b55733861a9e7bb732ad6df58cfd3446e7ec2067b2746836af4f7f89c06f25e697f3401aa322174f68a210066a5b63035baed84e1406dc44ec30e74d91129f27facb96b2eb96dbda62db646bbfaa784d1a5fba7dba608196df258f5020c01e07ebef101e89b91d5dec206ff84318ba1638547fe25aec6b87d674308840cae64a836af4ef9ea8cd2ab455927f23633e086b6f6b741485e76bef2b6cc032bb4e67d1a535f5a810fd3cc9899368676a27a90f311557be62e196f20ff52ef13f86ea9ba08fa7eaabb18c6b1afbb4f7771d605e4b1f2be6fe991394d0e2f0626490f444e9ea9fbd75efdb2a25d56667fae74512e2fc0acbd49afee2b2c23e72373c15067632bf4180a0cdc54e9c9394e7154b900a48eba42ba1f44b1d9fc72128f4b3bedaecb9e70e27ed92a46717fb202dac7e349a24736229c5c0836ea091cf2028b1a7eaa6a876513fa071249ea34cd4e1e27e33cd996b948907ebc60990d5930a69598cdf690b2c2c4a1ae184affe235d854ec81da06447e2738270b582081a0574605536f0f836c368de05a08b39fd2d65450c5c5d6d6cf1d87a7cd5a0a079aead0c9239382526011399682aca59d1ac68ee5c18aa4c3f61d13d2dc9f1940bbfceb517ddbe3d02b950661d24e7ed55d0550eb3689f421b3a2a9056451192059863b2f5bbf2fe06feab1df4d0b2165f90a3dbafeca86e3619716ccf0980bf1fe0721c73d5cfcefd66b41e50aa7eadabdd8c455f2779c3b3398132087261797ac8754b7044232941f4d74818e58ed96ec7f2caaf282f7a531758636d2020f7ad743013efcd97a076ead680b17dd244cb55039f07344e15a1a8e2f38870a29edba2439b0837a103e1a8c00e450006e6b589a654b8ccbefd5de0045c8e21c1545fa845e862ab714a464c0b6e00265beabcbaef383504fcf2c5f80cd7360b10d5c24f04fa84e22c6a8e81696a7d4debd55b2058c64ee4339f7e1acb1c8674f2ba9e03d761ec3bc086bf76ce20afa4cbf8328033c71e096a1b21e3e1bbb980c260b3e581b7f7afe02b1ea39b3b73031d0b96080204bffcbf3a04b43f5365bdb249ae301120746252ac7b763df1d20111dc3827f858fc8b12665c07364f9e6042b8adb4df253af298a3b669077f4aeac1aa8f6cc0b44af53c3c1fa4cc56a075d2857a42d3b05cc24d8e929608bb93e738fd680f4cd8af7e6506808ecef185b2b2ebdc44cb7a9ac2efa142c54b09cd054bda803a047c314060dc259796a51bea1238b6879fdf41ffc4269cbc9859c6220dd57373fadcfcda2548125a04d9447280a0696340db71c34a8cabeaf6636088c0331952ccce04a506c57ad047ad7bb9222907f98f62436ed0491d278b5ed7b273a27601258a9ff54c7ac8d3dbb68cf3f1749e0a4c950d439213e3bf0524945627736e7e8fe7f07029b26979c722849f62eedf78929c852c7bd06c10d5a69015ff5983519c3e0937bc3c825472e519547273409dd5643ecec7d1630979937792e5ed4b7cfaa110c7d87ddb2e600ae1cba2ba7b5464a14183ed799350e4d18c512d14b66266eedafd5d32e1141c543d6db248547031da4097158b87ecb7845a8f659c5c02b9ea2ac6f636f3e3d4015739223049f86005714294ac037d14a7ccdac80e89882751314baa8dda7bcb024504e2e27c6a101a3035ff6ef42b99d56c20c824d839f3d98e3131932867267d947c02bb2270046b020798f033e718352799c0cf9d26c86961efbf43562dc263b9d250e36aaa19d199f1d2789110fa14b42d0cd40a491ba99682f72fa9b301d08528b0cd55651c7fecb9fc18e4a725043f251aa9b69be5d31aaee787dc8771fbf31252a90ad86466fbc0eacd6a4cd0d6b94c53ab9fb4c6398b68c5077d5fda03d7e7a1d60824bd6a0859b7fd17c5e3ea922c212b33b1b36a7c5c060eecdec697ba38c0f968500d89f9543e48accf3511e6d0fbb18f089137a7db279c6abe326dd15532f7ce125605cbd676ab45449b2ac04d190420e48f1c0a029eda9944c2b651f681194348f35c5946265da9ae92238bb49a90f7a05e723b185bf8083ee7448ec9517320c5f10fd917b0375e3c88136b03d916220b42d3ef345c9617873212e1e6d1af9a175a5da49ca79114ab1bd3fa654bb9dd1cb9a287c2278f367cc7ef0e35d064982209f67c25194f6d480bd1af6da9c21e590bf77f8e556bc82b3f36629e51ba0bd9e8e8f2a59d7bbbc20be61ddd3d78efba879719d28c116762ad44f7a1c17212136d31493bc280107ecfc3e7f56a3155e6a8dc95ac459573523c5db8e9726948905e785fae860f81331108d539f7f1b8b4c914936d1a06976787a461aed10fcc5ee13a48ab03e06c2eb6d80964b9faad63932663a5a02cbd28b40f159d3191fed983fd4a451dfc47cf6bc11201e2d2faf3049e8ebfb3c7422ca072136f3294c96e6a0fd7348adc0080aa0cffa5ae817a64bf74e78406e6f12e2ce53b20f24795f8d35edf71c5de2ab175685fab6f9c73dc06620bda26f67208c9774b7e52526e6da846f50a867b53b0c964535f5e52c14958463d58d2417c74b2a25d8012d22f20b89f69d9cafc3744aa9a3435674cc2d23c1e68cce4216211907defcc128f00687ab857061bb9fc3fb5f4d2bc70d8652f2f9cf0808c69135eab8fe2b05192d7577511195d93abab820c9f30795df0b10c7f55de58c03d19139b28f5d90128f7041b76c81e6f1444d47642da90ad4e2903cac9481dd31dec5947d5feb56114e0d1865a7bf262de8132d41340ad3aa5e1938c8692b7d9e6af1633d74a3a21d0b65ee6e74bc543b648cfeb52beb7fc0e500afbeab9bdb8a425e43efc6e0ba03f2678dbb80015852281938260095069eaeb25fcbd581d8ebd64babae79e41d11efceb26339550bbba1e36ecc3c91f71ea9f1cd23c0ca5036758e74edf7858e14ad89c7f9c8f71c3cf32fe11c37f583c932bcef67203e999e45700d3a5816870afe8a87a72d9be4d639fa57feccb778e06dbba738460fd0f511c0ad977dd06206c54e18c4a57066e49e55e207d36107037ccc095ec6fa2feae81aaaa44b88370906d542c020f0d3b0e2d80843567501f13066782c96646ccfbe031283544962216425d07ad1796b75e2e9d402062083da2d671d32e1c34e3647bad0322067911656aa8439e56e08c30a1384877c1a32e69c3440d5a25f687c175239ee6cc75c04d11f5e379d21a7e8b32943f9772db2e168f4f1b7b087ee8b7119fd1f18aec2269e83608b1d071924f9f2dcbbfcd070bab6d7f5a447e3a3da752065677b64921849ccadc866212b4a74dfdfe4b8430bdb74f4a9f17a6a99495f0886b442625f2547a6f4500d0dd6f5416d1f40310b3fb8ca4a72861597ffa93f0f9c8e3955fa2bf139bd084522a6726933ffd9e47077365bcf03a3ee29f99edad41cce14458f0684554a86d665240423b236917bcdb313a210c1946d234dd1fe542e742668fd0d64b6ee71070db72111e430e1f27beaba2482951428015889eaad1482b4c2b3157c8eb633ce89bdb8cc75ec48b40e2c50fc5c46e897efa49a7e4b535b16dba729a619462465eef8f964af5497553815acb5545f74d5d677d2c0c1e339416b9c219e50a898ee88e1fb2f1db566e9696f253d79f68772a0aecc07896d2e49332c0f18fd89635440b49b7a80d33da8557759c2fb146fb125ae93087fc2b6749e6a0726f493c7ea11b9d67a1be2dbf04a50105d2b489c75a2efcad8658fb92521580df1b992af777cb0e3ba2c825243eb0e6bdf3d0a6978d1411104b59a46f0bf720a79728ba37648d5d60c06f92608a4dbf1c99fc3e449de7549a7e50762d0232c2b116994593bf466bb0dee85010662193d8ec2fd8a25ecdb2a7b34f7478aed831b3025e984f0ada44ea0333d2d72380d11ea5c1b5db3d182548eb261b77b13181f48514e3f03784963faaaafe860688661a8bbdce565ead9184b182a9802311d19069af44ad2e08496a94d229f869f5a2830c420d4e1bde78c230d8da4384b220c88dc1324a1b82f54e16647c431dd5011a1421c785e130013cfd882192d72f01374b6f2b7f6011bb2194cb772e337033fd01038f1d77810506f0f93aa4eae822649b30fff4c315bcd99b6cb7bc282d60e8aa24fa71bc754482ab58e0d721c3c2ec839f0f8b1c83585fe7ebc00368d3baa4a381440565216c8d2bd04fb96d32311649b8a7a3ca661a0cf53114a8fb32d33cad95318a6154178abc8c06440e21d22e5c70296141521743a8a37855d70b311927b84716de2f8fc092c91eb3a7a1f221f26aea2f88548f3af4b19fc475bcf49786adfc8d7e7349e1693548f512a221f381f9587f1fd3cf3cd069024a1170feafba8229cebb8923a7ab8fbd856196217b589aa128af06c775f25ed9b3aa9bc23803fa5965ee8b7794206585ab63ebc119c919779bcd9b1e024982c6fb199b1679c9af2da8f200348f68ec3b2a00c0f17a15318a649ae59bada674e3a149bb1d6431461411e2df3e2aa1958b722048b16a07dd414de7958c407c3eb8c760779358ec1e2439b58b8d00378bdd2e9e9450de9e777da0eebf79d558d029019a16d1f8c076fa5294863dda329ececee07020721d000c002b15cc3fd3cbf0f0fbc80d75c1bb53ae1b7a244fa507318c57e4c2d6fafb6038ee34355adf14f0363881911843ba3c655934688976f48ac38ca4e162f5b94463b38687726d27320a2d8c97ff9d8a79e81261babee9852ad0958c210d8ae1fe24253e9229b410ba1f11ab4cad40642dd7c818aff33430752c5f36205ef43d3378c9968b0b16a881404aa643c3d555f84b78446cb9bec133cbc10ad00b9207a883ec2d82bad1051a9c39453d1b63188f2c5c052643e597db86bdd1621806a9ef2c04e8cbd9fdaba392d0fc997cf5a49811bb4c8b68f104ccac280541ac123db803c502457797ba6ef0da3b43641d8406b78f1007c09560d45062a65259bc7636f785729b40176ebd0ca1c6f2ffdb9ef2915602c4a6275ac8fa7cbe71adcd397d6043297be672053abf8339f90c137a367ec7d6a782a51b8285f63721916d859797c32299e9413788fc27175800859f9bfc7d3939d4f6b5cc9777b2f198710723aaa5fb5f57450a5ceb88a674796d19ce70fab58e730240326a016922b06a453d8c9f93c4f6a13bb87c227a297ba159e626f5823114a76cd7b0f39410cc25f0afd48083c62151fbaa1853b27d7a17fa1b9e2fa28a284200bf12a6f5a2f550ca3ccff9b6327e4f7d9983a448cae3e7f9ca4e628df133a58dfe58d741b284a4e0b987dcb8dccd1f21874cd434280feb0e942f8f0e5ad8fed96b73bd26f024f4f697877b9a2164985542d2969f52a5b57c433eea4f07a445aa9e833a37f15dc67773cbb58f86cc70a1b5779b019ad98af907b00c0a1fec4492d26e38cd72085f1cba3e5e9c54abcd00c2108dd54aef936879809260609e0787ae4bea1f618a9df278efdd7f15dfe9247dc384fa4f971588fa356b4e67377e8fb6251408f2b82baac18f833a2875d3bc642a5838432ce511b38005fb7024bc01688ef3f670da2fd6a5794ae972da048893816c19549b73421e0d585751496ed222a7cfbca1d415320f214e7cee462d0689ffc31bd05f4c9a5ea3b1791bce9fcbf374d2d14004ca233a4b174ab409e968d6399606527b624923f381eb8c17b2805dfab7cca280308a2b5af4f1aab2d05e78198ee4536f2deebeafec0eebb33df157fdbbace2e747e434240c5cfe49275180859832b2d69923bf08c0d597d9112eeb73568c91fd8ea19778ae26aa6a872c78c89c957d2518154b689bab59b2ffe5bc04361ae131dd276df1a28b2e06a9df883220bcf76424cf289b94d5bd90056ca2a6828f210f1ff74decbe9a8961ee9c5963876aa8face86ca89e9b038fbc5b1ba5bc962c128107f963cd0d439c4fdb465eb7834c943f21c1f3d46a7f057b75b42aac8c472a5c5521abd8d5913f59e4798f9edd0bbe643ab68d2d5f2d4d7b7dba52dd5c8c01fc9c7bdbc80873c54c494f37b6f6fa4536f37b101a16a7da7ac943fe0f666304c7a6659b382bfb94977ea2f22e244f5224369d6025dabac1e08614146ee2501ddaefc72b74b56f7f72ee8d908bc52a354105655e85edb6dc768a83f3dfc0a317c66db9a34532d5f7356579f0741dacf256f0af84798ee17836c4a40fe4b5cc04c199f7039d38a4bc5bc7f2d99fba60a0fdca779b92f02ad5aaf76f1fdefb091381e6eb635e8634516faf93dc50ee8b484c07b917f28d75a657b00a68ba0b71015191755b4d819437281f7565299d9d5ab193750340c3436c4e1ec8af65c201101a107fb877fdcdbd711cc0e888f6a3918bbab8780e9651d6c8bc29d473a25a263d53cc7805cea42498373014e822341492cb0b5846f3651c4de21d6a02b449249f136615c3fa59efaab74793ee99dea1346e28a861afe2f931757642bd3ea028fac43df603f0eef3eb0387cd88048d715e733dc835545af0a970b030f6bcd40739b526796af9c7636fc1e0023beae25886eb1ec9fb69465f36cb8db0143282114a980ff547ceae0ae0328b0a2ff83894cc7efb1d8d3268e623e20257143ddf1690c3f89ba0cea9077e7448601c66a68816c5d7adb596e9df73a9e7d4f3b5151e8e6dba9f96d00681e644aa6cc33f2c88c76c3f204f9e0f4abe5667921cc5dd0a77e105a39ddd0f53525089e44c33468ab32cf7761380760d7c24966908f7291f3cb7b08e1addf167d9a5eb038f1f433e711c6e67adc478606f09c33400928b09658bcc54c224cdca2dae3dba6278e91f8a416474312cd9325be5c38d7f5d7e07f54faf13c87c1b95c0f7f21f9f879644988df52091918a8fb17defcd05035b2c01c5516e3c3a8cb16dbfc84ebf730cc91c289ae1d9a2247ef46867c4c3f8b214c7e231c7382212bcfd3bdfba84e5befd2d04ead43d97823562f066e832f1f31e830d7e9747675bde52ae766e1b6f1fd1868b78f7acd5e595aea09c9dfadbd42088b2218775b734708929cd68d07da0d0544236991c2b7cf3b1c8a535fc2a741e04a56884e7fec5c8b37e770b9ce037d6d9e5963f682452e8d786a57ea1ca4c11dafb71c383f1751378b133a730dff48da6d638942abb532cbcc712975bb260159681bc8d1480de29c8d1791ececbebef1dd47e7055855bd32ab876af784fad1175e04f583e93f827e3336d431ddf5f2a9347290732acb2c1e70b32aa39a1b61535d18522b8e335ee209aef0c81c3ba6c2902fb07d3d35c978a48c6242a5c0a0e5a2a7ab8189202cad4eef209e352db7c4548e813f6479e5ae2cdb3eaf32b601bf6758cf1a0678a0daf9120dc0fa7c4a30f5602f2ae2cd64d49f6cce7de341603a1e9661d5b7553dd96174da8b0763cb968592477ac917adc4dafb94d89522c29bcb39fadde0390e904c2638779567407ab30dabeb7f8beb0b49947dc9e8fe7291197bd5ebbf948c0326263c7cb67a772e3ffefa7d93deb6fe34c83db575bd913e74272a40222380713812899c64497ddd2f0ffa40bd9435ccd8dcf07812318062d0ce0a9887f24c623352f28434512a3f02497d08f70e2acd92c6f9c7db9c70ce84edc6d0b2e6a85061ecbbb6d524d77900e545869d585f02e66c11c5683262a89acb3a1d01fbc647be2e4ff6850475f86dbbf3a5533cddb04f9f759ed3f8c1a58e6111f8ab44f6eb753ce9b76b08ec2c1ca1e8e4a0b12f2468685e97514f80084f9ec72296c13c390174cb7058a1cb652524e152af41bdd9db80f4b26159d019c5915c863e55429c22b5df53f3c2c708d16dcddf9ca6144b3b12c102f6e4726abcb1e662e072858dd31ebcb6fec808ae3818a0e1529bd7f967ae9e4965f942953dd62008a7880ab0f25d1aba340e932e7ca5cfc654d05efae33677e86e73c1e16c255cadb04bfeb1f46d2a57eda2e9e0125c6a25e611177db2f81b52e4ac6270c29e8222702c6a2590dc3853e75a6dc26aad0dc20e13b71d273946564d9d7258d3a80b86cb1a2b655be37f8b0f78265f6bb866571b384a0792cd7ccd6f9f61514364c60b0d46d272ddf70cc26a62f1132e1edd6f617e815dadc8febd9a74e2e645c1d5c5e21b5c8747ef39578966822fb78829299d4ffe3e411f3d3bd5f092e06ad539341a69ee6302e31511830369f8bf22dd7204099269cf871cc0997191f1a2d248d13fcc11672953d9f5ace68497ee899bfb32b4b7bbeb46047e4ef821402516b83cb78caadfadd9fc6c12a398d99f1b878ec66b515e7483a9a0cbccb083041b2aac57d88f01d6d47b3ae84186a84792b52151c25f6587e21f8e6b07b1c0fc000b43896466542644603e14a650e16686d853e62db267e023569d5469c0809a8d114f1007b7827216e65fafee4b603e959641d11636efd348dbd46ed560624ef25c04f501716ef93cee02aad775bd9ee6919852491f3d29b942372e1882085117390b7aeab2e50367937357f59b9f3a24d1c14cc6497dbc0818fbed5041553c7bc5740d0e9aef2bf62243c1ca12f8567c9d3f39a40110770253767fa7c26a871d60e27b75d9e142b1162a4130343e788148124d712988045ef96d48f8920e88ff5c9f60d09d2f77ba9b403bf6c6d0fc0799048710792a96035c73a3e450c283036736b9628210c2497b080c5abc5372666768ca864d18a6f203fed35cf272622ddb9a3970878f471c80de989f90ebd8cea6120f8bd4c593763bdbc5a98058b8395cb3225c7060bb36acc2d22c0338edaac7d75befe39ac4206479c86fa237ab01ca34b0328eb6699049f29188e92e7354e69dd727f960802fefe99c3d80c097dd8728bf5d5825041d67e5ac9858d748bf30271341377176ee8bca796f60e5c92647f6871a1579155741c7b634c268068d9941eb9acd84569018e4de82811c568bf95b24c04cb3cda2a83b14307e668bb54db1a2569360d0caae00498f91e0494092814900d31c78e68f63f06cb09a782d204a10067a78a04801888f8b707e647e4d114ea7c1e7faba4176bd2977b7c1d0e97d6e3ed976c66930ea8d1ea278f3899dc95fad95147a22de703ff82865da597000525a1bd0656d82a84f5d2255bca0b9bef6038b61f0e92a5ba1395707daa095674120eec99fc8ac392eb5a178588131cc52f9e6d45d766712bb3eaf094693a5af7a4f746175bf7aecd09831d846931480245a862576fd3a006b07b1fd9c2db51db74543e275d9e7cc26f2d27452fd95e45041aaedc81d99067f7567db1fe9759e84b4fb2d1bd93bfefdce805a6cabc48654d9d38ebf014ecc33eab6dfe7fbe12b4007cb490b235982a55a611fdeb14cbd17ae02f6dbddc8b4ab0e8209312dfc80292b4c072401ff7336ce8055b3fba6b7058057342f5e55266f9bf87220d9c9ac9fe0853f3d4f4d019d829195d64b5e10165b80751e8ed7945c9a19a7960580b7f3a9d7a6065ce6a26fd3128a6e7e2d8d168db7ce213fc75b4315145a583fdcab1de9a71a41fda973ff1490721ae0f3ee28441b6a46876cebe7b6d692d551bd2746bef1fd28623c1af91c020c7c80ce66224b03e7b9106196f94b8e0c9b5ed67af49449aa45213b412de6e24c842e747291b84fa0553bd0fae951fdf6e70d9412ef0ba752166a5dcf09ecb079b9de21279373de98b9ca059e22817390f90680d8d338d7e35d0dc81751a500d4bd7104c7cc8e52af19d74caa3d6928ce9b0bca085889fbf59a86959c2dcdb13df2f80658bacae156b7f1f4865db24b07fbb6b27942099f9d61e57ae43c3c723bfd52acc8ffaf4fd9cb1147322dc31f7ec5671c5f0ca5c5155ed6d9f2e2413248241722317c262c5bc54875d886f0d90780674e073c5f7f921a6666e3048cd16d5814372e0236fe69a63a4f7118d7eb75bc4465dd01181c9a197ea72f8307c2841f6397a9baab5a97fb04e109006d8127d604f0b7d6736162c40bd8b1c09221e0922d2139987796499a7fb097c9d0a4671b898532936cccadae32376c93dfb059ad2915af4a690d40a516c72d74ce5d4ab9e752f58873ea88227546e021f701702aac2d40191a410dea52f40a4bb996be8002e1c57413e0dea9914098f8a880a8c387b2e3309533a4c07aa51444956ed8fd2a314017814af55da77e40ff7051641fde9c392981a5e7d779529be3e0be0f6890bf19b24f53ebdbde0faf9d69f10e9fe5c469cd99083a9c572eb19b940f9b0e8094a75fc98a7131f2c923408620f3d931493d5a7f759cc6a21edbe9dbe770a109672574d8bf6aacdd48623e833296a76845bc8dc66f9e0bf4d05b889bee67c4c2852750c30d06903783da6ce172c02a8f8653d8211beecc345dc19b6f7cbcc803d66ae2128ba1ba055f761ca3a1aad5843b15c067428741c224242e5c518ffddc45193b3a5ecc922cb13c0d0a72d4130a3f278c1f463f66423e4710027d0b319048abd0a6fbb66c195b9d1452e2e0c7c0e8374b22294a2129630e2cc3dea0b98c2c13758345954fa86eab6ab710a512893425e0268e10b34ee3e2e6a4fb03040687698c97982e30bc2a8a2b2fd2248da56fa8f2105609a497670832678fbaf89a7935cf26768c2c03f443fcc9012eb0427bcaf487189c20e24956f34d23792ad3f62596d3767ac07ae38f3fd4b11324800ac6e2d023729dd5df77dc4e0cd81d0ba8e10e90a266711e69d577bd406b1e218645fa2552bf747d0d1a87fd66c7644155f6f5cba9115d114d7631cec08770d53cead9762e9eb24399c4f97d98fb3c365d158799be00cccd3d20c3b090b431b0c2f7b7c2ef333ec898e469fa101f1adf6f73815f3b09bbdad8349034f1f90f27a8046dca9070e149c1c23150321e612756a0c2c7942d7e30059be4908050ad08455affab49f27dd02ec5169cd976f4567515baecfbadbe97c382d3f1826774b8c096b6862b740081c523d47bd0b9639994b4ec95ab12845086dd5fd482538707df831ef734170cf55d329490aa2e0e49643922241739793a8c75e7386283a026efc405fe0598677d246226a3ece5958e0d48de439e944a05fc643d0641fff61f83de97b677bf4840a977ef5625bc385b7591755fe89ee851ea5d15ebf2655014e914e878ae7c1f8282c6189ecdcfd5110178786f7faa3b05de46b43cf17e113d1492df5303ceba990b322420dc3220d8d4415f89381df54d5ead445821656c02d97585ac6f793d80d33115e53d6415f05f0c5d90c3d77c3d140226be02a3ee5564c70975456bf79d426c8c33efdf706cd178ac3d687e28e4d2204547d0fd5af73db10f93e0b6c523b4ef081426af896697aad46e292ae0c5fab03ec3d0d8dbfff9127ba2e3c4eb3e7a55d92890674bd6132382df800faf6b8b407cb5e2f34f902cc48285bc99e308f2acf715a3b8f884aa21e144ea9fe2a36eadfba592775d95bfd62c6d71c3089734d67d08cb00dd85d23185796cd641089a93e19b12beac08125be0a38ea27cef99c4cdbeddb4b278caa2fb8d47e2745d5c09e83222c0efa0932bba4cb4546e28667c714e5b2b974eabe47fb29b3f78cd5b43742f101e475b51ad373772e93166104a79b4efb175dac3b442b52b9504c88304419102b169b8bacf4d47e97ab2c69e9f5654f5a0cb6f55d8fea831cb665e93bda050b2ad2bee6223c4f9377be451cb1bb4d7d5445b1eb9b58549ec07694f34940216118b0e8add0f6d3ff6199052ea7668d13954c7caa2fe6af1663263c9a6210ae0b01981a9f0fc8d9d2eeb8d92572ad287146f3bf0c20d23de8d08c40c78dad3d061c8102981be8ed68bd6f3b7bf768c40b86a06b6da762153c82d045133d3976a50aa6637a1eec72f4f886a043ee57a4e6ecf52067f7f1dd512d0829b36989687320fec9da648b374ad4e0247d1fa97700c286b7e1103f696941674e6f3a6f9ad4223aab447b6b691951dcceffdcb30f047653433c4f8a1abeab791b5ac76b18502575e60cd921ee2346e0ede35620fb30907dfbfa218913647ad415c9a48ff09305f729f2bf156c6478011766cb094981c086bab98c384f67a7dc579b811ba5c11f607412b5d8cce667aade35ed4efee23bb261c4f278e09b257d502bc61ae48712daae64de5ae2bb6d679b43fd9669b02023aa3eb4d01d10c4603bed9416d06f16bc522446aa35b063e490767fcc90a7bf197281c5c295c1e0415dc27d1e29df06d659418c6c2d19a198517016a3c76ce82fab8d540e656ec7170093c80446c42bb2ab499e9eeed746759a7814701b945e602522926c49aef0d217ba7f3ec0ac1495c4281a7b4a9e1464ce6c97788d617101c912e7918be268957e80dd37fa1c1daef5d845aae5ae93f3d73e13e95b2f974ec218a863270fb89a30f0a81f70f815e9e11d54842d2997758695e9e919c49ee9568970dd985f95ad792c4de69c602ca2a090660e70cff6f75b308d6b58fd3d41bf6c2e1ab5210914a167f161efb208999866e918d0ac040afd7f314e279aea6a9503c0fe69b94c0596ec7204937c282c824f63fa7641a2732762083c6fd099f8538f33fabde31df0ff298e4d691403845eb37769b4a7e325f7b4b90dc138807c7bf8c94b3308c8131021dbc702ec1b0d2773d0d393b5ee72fb1b8c46bf5a2d96e008f0a27bd052c6ce663df466bc10b34b779436e65c9da5839de588d9356cf64f721915fb81cf1107e5861a4fe51ceb29793e3bfd641066d690ab8a27bcd93604040376f886a2bddf62b99aa21012f03a25b1808210494fbfb7c5a6e98c9b0183db9d9366514708d725bd15d6471c37dd3530ee3ab502f0bfaf4639574de4088216596534660c852c04f73e77270a40fd0514c085057ceb30638d46641a2429e8aeb06b89a01c7a19bb9287955c17d5b7abe02aecf3f5e497b8ce13ff4402e42e044c3d270c04db487f5d00f04a0697b0af7a5e27a2a7c04d5bbd73c3e91e2ba020b861cf43676ed66ec9c18f4129399d25127fd3a40f696bb6dbccf3829d8babd3a13e6aec6f0e6bfa632bb90b26c35be6ad3e9fea820c0327735cc17a5244802c9466dd98739a06bb34e742dd044554adfcdbbe8d300cbc39c983ac1a9222513ebf33bd79f429c47d58beece61dd0536dfb22b4ee0b661069eeadf37d8c1456b05f3e19ee3e058c732c8d1ba62a6a39994da45c80490e4a53575e6a739a01d8817ced669ef6dee1dedb34984b2688cd26b1fd6e4ed748d8dba93b6ee4a21493066c1a10f7fa44f50a02206b342a5e15c483ce678c3f87fa37ae6701c4105ea18957e330b13106f6d62f55dd9e05f880741b3c98407fa02b8b956a0b42279f01abf85630fbbe613129d4a710020602b5ce04f6a3eb5c329dc34aa10af6d222c111845bb6016b2cd69f48e4edde14106fe41edd0cc43ad8800ca68c5aa644bdc46f64654b3fa561e10dc84c4612059e79b06da8e2bfb580c268e0c94f02625b1feac968586930f5fa9daa1d52e2166bc02b21f5b5a51eac5219ca1c42b6f563ed338189e34fa6c3e8d5922ca880062f1a75d8d73e3cce28ccd91097acfac61ce220ced87115497eda778f752709011be2b3aec8c33879c1b7d9f041cfea2e9f10ec121a0887e278396eb143953a261cc5d74697fb31f3b525f2129e9dce7542e906372204e07e6d68f59f2bfb9d194e059fc2816cc7b5faab72efde88c1daafb9dce629acf1bdcf7516b7551a8f0d427ae711881bb9dd56f6ddf46d96644fd2e7bd577830779ac1e706b6dc298e0b89cccbea03d22a857589fe4895f822cd5c5fd1634ffe6ef44b7dca09e57cf30126afd120fbdc1cde39aa695bdce2ca770926968c345e801bac05c163e4bbc4279d02daac4bb1a306be1941d646c5e93050adca4277432645be6a4ff50ddf5805d1070e4775d2d6e006453d6b67e718f8289e49ce80b841212e613e505452a827677548ec16fc965456db4e86d45b4d59be76f01e4ba348015b12e75937bf20", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0101cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db70000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + } + }, "test_exit_code": { "crisp": 0, "folded_export": 0, diff --git a/circuits/benchmarks/results_insecure/integration_summary.json b/circuits/benchmarks/results_insecure/integration_summary.json index d189e8d12..193dd1263 100644 --- a/circuits/benchmarks/results_insecure/integration_summary.json +++ b/circuits/benchmarks/results_insecure/integration_summary.json @@ -8,7 +8,7 @@ "operation_timings": [ { "name": "CalculateDecryptionKey", - "avg_seconds": 0.115728250, + "avg_seconds": 0.11572825, "runs": 3, "total_seconds": 0.347184751 }, @@ -44,15 +44,15 @@ }, { "name": "ZkDecryptionAggregation", - "avg_seconds": 49.047040500, + "avg_seconds": 49.0470405, "runs": 1, - "total_seconds": 49.047040500 + "total_seconds": 49.0470405 }, { "name": "ZkDkgAggregation", - "avg_seconds": 20.151443750, + "avg_seconds": 20.15144375, "runs": 1, - "total_seconds": 20.151443750 + "total_seconds": 20.15144375 }, { "name": "ZkDkgShareDecryption", @@ -62,15 +62,15 @@ }, { "name": "ZkNodeDkgFold", - "avg_seconds": 62.889247500, + "avg_seconds": 62.8892475, "runs": 3, - "total_seconds": 188.667742500 + "total_seconds": 188.6677425 }, { "name": "ZkPkAggregation", - "avg_seconds": 2.156797250, + "avg_seconds": 2.15679725, "runs": 1, - "total_seconds": 2.156797250 + "total_seconds": 2.15679725 }, { "name": "ZkPkBfv", @@ -119,7 +119,7 @@ "timings_seconds": [ { "label": "Starting trbfv actor test", - "seconds": 0E-9 + "seconds": 0e-9 }, { "label": "Setup completed", diff --git a/crates/aggregator/src/lib.rs b/crates/aggregator/src/lib.rs index d39991fa8..f4bb7c7bf 100644 --- a/crates/aggregator/src/lib.rs +++ b/crates/aggregator/src/lib.rs @@ -4,8 +4,8 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -pub mod committee_hash; mod committee_finalizer; +pub mod committee_hash; mod decryptionshare_created_buffer; pub mod ext; mod keyshare_created_filter_buffer; diff --git a/crates/aggregator/src/threshold_plaintext_aggregator.rs b/crates/aggregator/src/threshold_plaintext_aggregator.rs index 9f68bf68c..3155b5680 100644 --- a/crates/aggregator/src/threshold_plaintext_aggregator.rs +++ b/crates/aggregator/src/threshold_plaintext_aggregator.rs @@ -690,7 +690,8 @@ impl ThresholdPlaintextAggregator { let reply = committee_reply .ok_or_else(|| anyhow!("committee reply required to fetch members"))?; let e3_id = self.e3_id.clone(); - self.sortition.do_send(GetCommitteeMembersRequest { e3_id, reply }); + self.sortition + .do_send(GetCommitteeMembersRequest { e3_id, reply }); return Ok(()); } @@ -1072,11 +1073,7 @@ impl Handler>> impl Handler for ThresholdPlaintextAggregator { type Result = (); - fn handle( - &mut self, - msg: CommitteeMembersResponse, - ctx: &mut Self::Context, - ) -> Self::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()); @@ -1442,8 +1439,9 @@ mod tests { (1, vec![dummy_proof(CircuitName::ThresholdShareDecryption)]), ]); - aggregator.committee_members = - Some(vec!["0x0000000000000000000000000000000000000001".to_string()]); + aggregator.committee_members = Some(vec![ + "0x0000000000000000000000000000000000000001".to_string() + ]); let ec = test_ctx(E3Failed { e3_id: e3_id.clone(), failed_at_stage: E3Stage::None, diff --git a/crates/sortition/src/sortition.rs b/crates/sortition/src/sortition.rs index fae86eab3..f526b5df0 100644 --- a/crates/sortition/src/sortition.rs +++ b/crates/sortition/src/sortition.rs @@ -734,11 +734,7 @@ impl Handler> for Sortition { impl Handler for Sortition { type Result = (); - fn handle( - &mut self, - msg: GetCommitteeMembersRequest, - _: &mut Self::Context, - ) -> Self::Result { + fn handle(&mut self, msg: GetCommitteeMembersRequest, _: &mut Self::Context) -> Self::Result { let members = self .get_committee(&msg.e3_id) .map(|c| c.members().to_vec()) diff --git a/crates/utils/src/committee_hash.rs b/crates/utils/src/committee_hash.rs index 051e460e1..e8d750ac2 100644 --- a/crates/utils/src/committee_hash.rs +++ b/crates/utils/src/committee_hash.rs @@ -41,10 +41,7 @@ pub fn committee_hash_limbs_from_addresses(addresses: &[Address]) -> CommitteeHa /// Parse checksummed or lowercase hex node addresses (as used in events). pub fn hash_committee_node_strings(nodes: &[String]) -> anyhow::Result { - let addresses: Vec
= nodes - .iter() - .map(|s| s.parse()) - .collect::>()?; + let addresses: Vec
= nodes.iter().map(|s| s.parse()).collect::>()?; Ok(hash_committee_addresses(&addresses)) } diff --git a/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol b/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol index 6f3fc3a9c..a2e902f20 100644 --- a/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol +++ b/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol @@ -20,7 +20,9 @@ library CommitteeHashLib { } /// @notice Same as {hash} for calldata/memory address lists. - function hashMemory(address[] memory nodes) internal pure returns (bytes32) { + function hashMemory( + address[] memory nodes + ) internal pure returns (bytes32) { return keccak256(abi.encodePacked(nodes)); } diff --git a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol index ab6a8eaa4..d6c8bb6f8 100644 --- a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol @@ -79,9 +79,7 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry { return _committeeNodes[e3Id]; } - function getCommitteeHash( - uint256 e3Id - ) external view returns (bytes32) { + function getCommitteeHash(uint256 e3Id) external view returns (bytes32) { return keccak256(abi.encodePacked(_committeeNodes[e3Id])); } diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol index 2fe91884c..7b2803ffd 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 = 0x02734bf64581e3f98e47575b06750fc45ec81a34079564e4a7326a3d547d2b90; 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(0x22f1675e1cbb2fcd4d4b892e591f16c3201a876c34d711e86159aa593f8957df), - y: uint256(0x06b38fe5bca0f4814e532d26345b64ddd5fdbc72c4171889f9c4cad61139787d) + ql: Honk.G1Point({ + x: uint256( + 0x22f1675e1cbb2fcd4d4b892e591f16c3201a876c34d711e86159aa593f8957df + ), + y: uint256( + 0x06b38fe5bca0f4814e532d26345b64ddd5fdbc72c4171889f9c4cad61139787d + ) }), - qr: Honk.G1Point({ - x: uint256(0x0beaa5627f856b98b6c9300fd93f16d9de0a364e8319e818171fd4e0c0a5c91e), - y: uint256(0x2d884ebc1127945f836920004f1499ed01afd2fc89691f0f96f7a928a9998fc6) + qr: Honk.G1Point({ + x: uint256( + 0x0beaa5627f856b98b6c9300fd93f16d9de0a364e8319e818171fd4e0c0a5c91e + ), + y: uint256( + 0x2d884ebc1127945f836920004f1499ed01afd2fc89691f0f96f7a928a9998fc6 + ) }), - qo: Honk.G1Point({ - x: uint256(0x02e50bcff38f3c94d748e3ce7b80bb26019c24fd8c64c55e3469cc3ddf138b8e), - y: uint256(0x248ca1123436cf2e53bdd9ed5e3e594213a59785ae39c6218eb5e28aedf552dc) + qo: Honk.G1Point({ + x: uint256( + 0x02e50bcff38f3c94d748e3ce7b80bb26019c24fd8c64c55e3469cc3ddf138b8e + ), + y: uint256( + 0x248ca1123436cf2e53bdd9ed5e3e594213a59785ae39c6218eb5e28aedf552dc + ) }), - q4: Honk.G1Point({ - x: uint256(0x2be0a336b2241e8cc572c843dfe5de1bb1d26f2ffb244461939573c3910ae7e4), - y: uint256(0x18195c8e933be2a9c081ff5fcad1f3d362ed2408ca5bd683ecb76f831937bf8c) + q4: Honk.G1Point({ + x: uint256( + 0x2be0a336b2241e8cc572c843dfe5de1bb1d26f2ffb244461939573c3910ae7e4 + ), + y: uint256( + 0x18195c8e933be2a9c081ff5fcad1f3d362ed2408ca5bd683ecb76f831937bf8c + ) }), - qm: Honk.G1Point({ - x: uint256(0x2aaee69977fc70b11f35a9417bf0efc1b8fc3ed5ed1c6a14cb1e2f587b01618d), - y: uint256(0x167831b0a4fb0627b623ab3b229eac297d78d9ffb020801e31e4ba9ed288f00f) + qm: Honk.G1Point({ + x: uint256( + 0x2aaee69977fc70b11f35a9417bf0efc1b8fc3ed5ed1c6a14cb1e2f587b01618d + ), + y: uint256( + 0x167831b0a4fb0627b623ab3b229eac297d78d9ffb020801e31e4ba9ed288f00f + ) }), - qc: Honk.G1Point({ - x: uint256(0x0c8357f6962ce8370d3f5965652b63f972d8eed7766a7fefba8c443b69edab68), - y: uint256(0x238822fb432ce7bed5edcee885a9a75beec94f64602b27518e5bb66da54b4157) + qc: Honk.G1Point({ + x: uint256( + 0x0c8357f6962ce8370d3f5965652b63f972d8eed7766a7fefba8c443b69edab68 + ), + y: uint256( + 0x238822fb432ce7bed5edcee885a9a75beec94f64602b27518e5bb66da54b4157 + ) }), - qLookup: Honk.G1Point({ - x: uint256(0x29e47a139596dcf5450c2de10d5d121d883389ab7c0e930c1ad2f20283098fc1), - y: uint256(0x2548e6b14779657467f37fc862ec803097b1e328abf936d9b93438db371b5aff) + qLookup: Honk.G1Point({ + x: uint256( + 0x29e47a139596dcf5450c2de10d5d121d883389ab7c0e930c1ad2f20283098fc1 + ), + y: uint256( + 0x2548e6b14779657467f37fc862ec803097b1e328abf936d9b93438db371b5aff + ) }), - qArith: Honk.G1Point({ - x: uint256(0x0c6962ed640803bfd254fe60f15bfbd3619695b639eaa50f8ad9bca8f26c1c67), - y: uint256(0x2a81c4ea9b5f20a9b7966ef5a0f095f1bf0fc03400c4328f06da00547e5ede6b) + qArith: Honk.G1Point({ + x: uint256( + 0x0c6962ed640803bfd254fe60f15bfbd3619695b639eaa50f8ad9bca8f26c1c67 + ), + y: uint256( + 0x2a81c4ea9b5f20a9b7966ef5a0f095f1bf0fc03400c4328f06da00547e5ede6b + ) }), - qDeltaRange: Honk.G1Point({ - x: uint256(0x12740b73a60d59b99352868b5ff15474d8d40676659ede6a613c2869926496e9), - y: uint256(0x2e92d9a28c1a2b6f6d5802abcfc28eb69fbf0f0fce55a7e60046d8dfc4fdf260) + qDeltaRange: Honk.G1Point({ + x: uint256( + 0x12740b73a60d59b99352868b5ff15474d8d40676659ede6a613c2869926496e9 + ), + y: uint256( + 0x2e92d9a28c1a2b6f6d5802abcfc28eb69fbf0f0fce55a7e60046d8dfc4fdf260 + ) }), - qElliptic: Honk.G1Point({ - x: uint256(0x300990501116cc8c9f5950d44ce76927d8443b777155a61e3cf6bb2e5060ca9d), - y: uint256(0x269ae09b2eee7365a0aed387b13e4ca9916e3ce2c75967672180345d33cb70b7) + qElliptic: Honk.G1Point({ + x: uint256( + 0x300990501116cc8c9f5950d44ce76927d8443b777155a61e3cf6bb2e5060ca9d + ), + y: uint256( + 0x269ae09b2eee7365a0aed387b13e4ca9916e3ce2c75967672180345d33cb70b7 + ) }), - qMemory: Honk.G1Point({ - x: uint256(0x1c42c01418da4f6b73611ae563df3215901ab635ad3c850db84e1a9b13ab9235), - y: uint256(0x2d23fcba8d43a2ecc03a6af88af95e13ea8b3cca01165ee491dbb23a4ea4ffa5) + qMemory: Honk.G1Point({ + x: uint256( + 0x1c42c01418da4f6b73611ae563df3215901ab635ad3c850db84e1a9b13ab9235 + ), + y: uint256( + 0x2d23fcba8d43a2ecc03a6af88af95e13ea8b3cca01165ee491dbb23a4ea4ffa5 + ) }), - qNnf: Honk.G1Point({ - x: uint256(0x1dfa93f3351f60495e8df8d87903805817c44f87a73b945c81a438be0facc481), - y: uint256(0x0e0341343e527ffedee24e4028bc14b8501adc68d0fce00c17ac11120d2df513) + qNnf: Honk.G1Point({ + x: uint256( + 0x1dfa93f3351f60495e8df8d87903805817c44f87a73b945c81a438be0facc481 + ), + y: uint256( + 0x0e0341343e527ffedee24e4028bc14b8501adc68d0fce00c17ac11120d2df513 + ) }), - qPoseidon2External: Honk.G1Point({ - x: uint256(0x048462f577ba1216245079d12c9d55487cf3b330eed20a66fe7db67d447fd72f), - y: uint256(0x1f06371d9dedf997f6dc1b5508b5fa620a05c3261e833bb43b88e9b6b6f5a979) + qPoseidon2External: Honk.G1Point({ + x: uint256( + 0x048462f577ba1216245079d12c9d55487cf3b330eed20a66fe7db67d447fd72f + ), + y: uint256( + 0x1f06371d9dedf997f6dc1b5508b5fa620a05c3261e833bb43b88e9b6b6f5a979 + ) }), - qPoseidon2Internal: Honk.G1Point({ - x: uint256(0x1189e97ee885b8a608dca70a417cfd3d339455452c9798752febedd0582f4977), - y: uint256(0x13860b3c5483cf0abe4d5ec8f382e455b20aa391dc315862abb5b49228bf7083) + qPoseidon2Internal: Honk.G1Point({ + x: uint256( + 0x1189e97ee885b8a608dca70a417cfd3d339455452c9798752febedd0582f4977 + ), + y: uint256( + 0x13860b3c5483cf0abe4d5ec8f382e455b20aa391dc315862abb5b49228bf7083 + ) }), - s1: Honk.G1Point({ - x: uint256(0x11b5d23e9c3c361c011754470be1828b314b3bc41dd92723edd773e0dc721f87), - y: uint256(0x12d77c6c17a5aa9ab610abb810fecdd5d3a20370c04f9a04969e1c7b51157f90) + s1: Honk.G1Point({ + x: uint256( + 0x11b5d23e9c3c361c011754470be1828b314b3bc41dd92723edd773e0dc721f87 + ), + y: uint256( + 0x12d77c6c17a5aa9ab610abb810fecdd5d3a20370c04f9a04969e1c7b51157f90 + ) }), - s2: Honk.G1Point({ - x: uint256(0x0ccfd83fc13c1e477918617d55ec0e302167fc9e35ac5e7933267dbf6b04225d), - y: uint256(0x0345818c0b610d75f563262999377e05bed55ebaff83f0eb7a9525394a67436d) + s2: Honk.G1Point({ + x: uint256( + 0x0ccfd83fc13c1e477918617d55ec0e302167fc9e35ac5e7933267dbf6b04225d + ), + y: uint256( + 0x0345818c0b610d75f563262999377e05bed55ebaff83f0eb7a9525394a67436d + ) }), - s3: Honk.G1Point({ - x: uint256(0x18c9ccbc44c932d6d698e781e285674b7c169c4435df7bf6a41b56aebc8104f5), - y: uint256(0x145974be15fb78b2d78c93a97669c4501670649ecaf20a58a183b4199301cba7) + s3: Honk.G1Point({ + x: uint256( + 0x18c9ccbc44c932d6d698e781e285674b7c169c4435df7bf6a41b56aebc8104f5 + ), + y: uint256( + 0x145974be15fb78b2d78c93a97669c4501670649ecaf20a58a183b4199301cba7 + ) }), - s4: Honk.G1Point({ - x: uint256(0x1985589ee73cb2f44b4dbb33e470a1c996c5d06ed7baf10ddbfd507acb8684ae), - y: uint256(0x2d07e474573859fda8dc083c17728f59cddb11cd0e29be5651705f5dbddb669a) + s4: Honk.G1Point({ + x: uint256( + 0x1985589ee73cb2f44b4dbb33e470a1c996c5d06ed7baf10ddbfd507acb8684ae + ), + y: uint256( + 0x2d07e474573859fda8dc083c17728f59cddb11cd0e29be5651705f5dbddb669a + ) }), - t1: Honk.G1Point({ - x: uint256(0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26), - y: uint256(0x0015b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f) + t1: Honk.G1Point({ + x: uint256( + 0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26 + ), + y: uint256( + 0x0015b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f + ) }), - t2: Honk.G1Point({ - x: uint256(0x1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e), - y: uint256(0x305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d19) + t2: Honk.G1Point({ + x: uint256( + 0x1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e + ), + y: uint256( + 0x305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d19 + ) }), - t3: Honk.G1Point({ - x: uint256(0x061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d), - y: uint256(0x1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e) + t3: Honk.G1Point({ + x: uint256( + 0x061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d + ), + y: uint256( + 0x1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e + ) }), - t4: Honk.G1Point({ - x: uint256(0x043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce), - y: uint256(0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854) + t4: Honk.G1Point({ + x: uint256( + 0x043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce + ), + y: uint256( + 0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854 + ) }), - id1: Honk.G1Point({ - x: uint256(0x0ce4b985e3d494ea5e27349d6bfd5a93f2da5df7b83a4e95247e187be4b04234), - y: uint256(0x2b14f7ab190415ec456c7d10382e6e5a573d65a180bcc698638d6c38f0c58198) + id1: Honk.G1Point({ + x: uint256( + 0x0ce4b985e3d494ea5e27349d6bfd5a93f2da5df7b83a4e95247e187be4b04234 + ), + y: uint256( + 0x2b14f7ab190415ec456c7d10382e6e5a573d65a180bcc698638d6c38f0c58198 + ) }), - id2: Honk.G1Point({ - x: uint256(0x2cedaca0e3610529837cd83d76d7a67e6f037068b22b7f0e21c5c0fd2bb16587), - y: uint256(0x16f7b9a3f2b5c36530840e8b7046b3faf0273017b736d7d4e9c86a56e22c997f) + id2: Honk.G1Point({ + x: uint256( + 0x2cedaca0e3610529837cd83d76d7a67e6f037068b22b7f0e21c5c0fd2bb16587 + ), + y: uint256( + 0x16f7b9a3f2b5c36530840e8b7046b3faf0273017b736d7d4e9c86a56e22c997f + ) }), - id3: Honk.G1Point({ - x: uint256(0x195328bc8631c53b83e65739b932c063ab3f2861c3f25e186e693b96ec32ec6b), - y: uint256(0x1c2e18885411c408222af35cf31ebc1e7e3c82428cd8b4111b418a15b47bf778) + id3: Honk.G1Point({ + x: uint256( + 0x195328bc8631c53b83e65739b932c063ab3f2861c3f25e186e693b96ec32ec6b + ), + y: uint256( + 0x1c2e18885411c408222af35cf31ebc1e7e3c82428cd8b4111b418a15b47bf778 + ) }), - id4: Honk.G1Point({ - x: uint256(0x0d629ffdaf3c341c70dc25efc19ea32364f2718a9f2e7d3001f2582bc7770eba), - y: uint256(0x218e10d582f130e792dac3a6cd0ca6d339566ea722f23aa8c7e505c444a8b426) + id4: Honk.G1Point({ + x: uint256( + 0x0d629ffdaf3c341c70dc25efc19ea32364f2718a9f2e7d3001f2582bc7770eba + ), + y: uint256( + 0x218e10d582f130e792dac3a6cd0ca6d339566ea722f23aa8c7e505c444a8b426 + ) }), - lagrangeFirst: Honk.G1Point({ - x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), - y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) + lagrangeFirst: Honk.G1Point({ + x: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000001 + ), + y: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000002 + ) }), - lagrangeLast: Honk.G1Point({ - x: uint256(0x194a661bffd8b96eb251f3e4fd4c4ca2099d006d6047d379e36ac4f48075ca84), - y: uint256(0x05cdeca24a13e225c1cca9954e1e3d0826037e148f4c4cabe583813ea42f3318) + lagrangeLast: Honk.G1Point({ + x: uint256( + 0x194a661bffd8b96eb251f3e4fd4c4ca2099d006d6047d379e36ac4f48075ca84 + ), + y: uint256( + 0x05cdeca24a13e225c1cca9954e1e3d0826037e148f4c4cabe583813ea42f3318 + ) }) }); 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 3116a3b52..a731bfb12 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 = 0x1e8f6b05e7a356352348aac33d63b40dd9e397018959e1b07bb32bce03a31ca3; 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(0x16d8337237d4ec2403b09c245b20ae52736b3c3b0c18c643a26a00569a27cadc), - y: uint256(0x1b84afa97d44876f12c9857b371684953b15dd5a102621470fe428ce3440a74e) + ql: Honk.G1Point({ + x: uint256( + 0x16d8337237d4ec2403b09c245b20ae52736b3c3b0c18c643a26a00569a27cadc + ), + y: uint256( + 0x1b84afa97d44876f12c9857b371684953b15dd5a102621470fe428ce3440a74e + ) }), - qr: Honk.G1Point({ - x: uint256(0x05cd8cefcf9533c15dd3a87996f365a1d87d537019531bfa1443523a2e8d9567), - y: uint256(0x18ca3ec3ab25ee727789389c4e3a450dd49835e3ff89641b7499751c5eddffb4) + qr: Honk.G1Point({ + x: uint256( + 0x05cd8cefcf9533c15dd3a87996f365a1d87d537019531bfa1443523a2e8d9567 + ), + y: uint256( + 0x18ca3ec3ab25ee727789389c4e3a450dd49835e3ff89641b7499751c5eddffb4 + ) }), - qo: Honk.G1Point({ - x: uint256(0x0ee7a4cb268e178430f1ab4b692ca1e998f61f59f640709628f3ab061e1ac31e), - y: uint256(0x0f1051efd6dd1ce3d5f3b009b795b22a45b489f682600ebef98ad03b235d2e4e) + qo: Honk.G1Point({ + x: uint256( + 0x0ee7a4cb268e178430f1ab4b692ca1e998f61f59f640709628f3ab061e1ac31e + ), + y: uint256( + 0x0f1051efd6dd1ce3d5f3b009b795b22a45b489f682600ebef98ad03b235d2e4e + ) }), - q4: Honk.G1Point({ - x: uint256(0x133b09a5e4a51e7c7596de7bd299628b2c61acc5d9d076619a1b01510b7a0b0c), - y: uint256(0x05e0e81bb4a9ce485e85d9dc1d9c91340f65ac58b1586a0c759f22036aa025ef) + q4: Honk.G1Point({ + x: uint256( + 0x133b09a5e4a51e7c7596de7bd299628b2c61acc5d9d076619a1b01510b7a0b0c + ), + y: uint256( + 0x05e0e81bb4a9ce485e85d9dc1d9c91340f65ac58b1586a0c759f22036aa025ef + ) }), - qm: Honk.G1Point({ - x: uint256(0x25305186adb6ab510e45dc5912cad43e52ec3d21d3bb479a9c31dc5638d7d9fe), - y: uint256(0x1701052b060e45fdc21dbea0a000bf67d3ce7855da5a2495e5134efffbbc4f59) + qm: Honk.G1Point({ + x: uint256( + 0x25305186adb6ab510e45dc5912cad43e52ec3d21d3bb479a9c31dc5638d7d9fe + ), + y: uint256( + 0x1701052b060e45fdc21dbea0a000bf67d3ce7855da5a2495e5134efffbbc4f59 + ) }), - qc: Honk.G1Point({ - x: uint256(0x257bffaddd8fa52641b20e502375928e7c43840f1026a5d8009ee915bdb6ac6d), - y: uint256(0x1eda4d3bd95201aab871d32aefc8ced2e696789b0933b6c85f2ae1de282847db) + qc: Honk.G1Point({ + x: uint256( + 0x257bffaddd8fa52641b20e502375928e7c43840f1026a5d8009ee915bdb6ac6d + ), + y: uint256( + 0x1eda4d3bd95201aab871d32aefc8ced2e696789b0933b6c85f2ae1de282847db + ) }), - qLookup: Honk.G1Point({ - x: uint256(0x116151016b4fea676c06d9d297159098031c1b2d5c0ae18d5e5d874105b26eae), - y: uint256(0x0610cfeee3e9dbc93565c2b5ca7cbfb4a301b32b4903f16cc95bdd4a8d30275d) + qLookup: Honk.G1Point({ + x: uint256( + 0x116151016b4fea676c06d9d297159098031c1b2d5c0ae18d5e5d874105b26eae + ), + y: uint256( + 0x0610cfeee3e9dbc93565c2b5ca7cbfb4a301b32b4903f16cc95bdd4a8d30275d + ) }), - qArith: Honk.G1Point({ - x: uint256(0x07d4cc138af86d1461d286af965b1dedad0b9a44e5b3764000c4fc25db44e2c7), - y: uint256(0x283149ff7ff97882b22d98fd89937bc119029f1683fcd2dc4aef822c226eed6b) + qArith: Honk.G1Point({ + x: uint256( + 0x07d4cc138af86d1461d286af965b1dedad0b9a44e5b3764000c4fc25db44e2c7 + ), + y: uint256( + 0x283149ff7ff97882b22d98fd89937bc119029f1683fcd2dc4aef822c226eed6b + ) }), - qDeltaRange: Honk.G1Point({ - x: uint256(0x1f0332c373c4a8fce8a50b55bfc656f76a5ccfb3cbedbfca049704694dfac777), - y: uint256(0x1bec984d96e9150d2dbedd8d19564ab492d8db9aa52f02edadd06fb85b156bee) + qDeltaRange: Honk.G1Point({ + x: uint256( + 0x1f0332c373c4a8fce8a50b55bfc656f76a5ccfb3cbedbfca049704694dfac777 + ), + y: uint256( + 0x1bec984d96e9150d2dbedd8d19564ab492d8db9aa52f02edadd06fb85b156bee + ) }), - qElliptic: Honk.G1Point({ - x: uint256(0x2b524a596ca01057f413e2d264278850dd071ef8d2178bb6fe4c5848359f9307), - y: uint256(0x260fa657bf3c7f5db8dba3c6b4c9d701db6cebbdb338acce0bd49a325faf1fbe) + qElliptic: Honk.G1Point({ + x: uint256( + 0x2b524a596ca01057f413e2d264278850dd071ef8d2178bb6fe4c5848359f9307 + ), + y: uint256( + 0x260fa657bf3c7f5db8dba3c6b4c9d701db6cebbdb338acce0bd49a325faf1fbe + ) }), - qMemory: Honk.G1Point({ - x: uint256(0x088712e929e91683038e64aa93060b67b4c4f8bfbae75648cc2257c6733acf12), - y: uint256(0x04b38e231192be299a84c5495056bf59029b1b7e4a5bf591096a8945265232df) + qMemory: Honk.G1Point({ + x: uint256( + 0x088712e929e91683038e64aa93060b67b4c4f8bfbae75648cc2257c6733acf12 + ), + y: uint256( + 0x04b38e231192be299a84c5495056bf59029b1b7e4a5bf591096a8945265232df + ) }), - qNnf: Honk.G1Point({ - x: uint256(0x1c24660db3144b45bf6427e3c1bf6a6c86a2c828217df4d064a1e96797e30154), - y: uint256(0x22039202701dac37b8a59b1cb37ec9c0950c16b651560f20b7cca068ebd47056) + qNnf: Honk.G1Point({ + x: uint256( + 0x1c24660db3144b45bf6427e3c1bf6a6c86a2c828217df4d064a1e96797e30154 + ), + y: uint256( + 0x22039202701dac37b8a59b1cb37ec9c0950c16b651560f20b7cca068ebd47056 + ) }), - qPoseidon2External: Honk.G1Point({ - x: uint256(0x1be6b465dcde65e3a5b9f2a90d2d1be72254cd5b4e9f4f8d5123655475e6d3fb), - y: uint256(0x2c6716fb2a299303d79589483f34d09251a84eab9ba72d2d455f9b0a450d762b) + qPoseidon2External: Honk.G1Point({ + x: uint256( + 0x1be6b465dcde65e3a5b9f2a90d2d1be72254cd5b4e9f4f8d5123655475e6d3fb + ), + y: uint256( + 0x2c6716fb2a299303d79589483f34d09251a84eab9ba72d2d455f9b0a450d762b + ) }), - qPoseidon2Internal: Honk.G1Point({ - x: uint256(0x0485bdf6b5d9bad6c67b7eebb14e462422b17f8f5c7bb35f655cf706882d34fb), - y: uint256(0x06b5e3947f649e125bcede91f9319b269f3db52ad8c56e6d91bd6615732467fd) + qPoseidon2Internal: Honk.G1Point({ + x: uint256( + 0x0485bdf6b5d9bad6c67b7eebb14e462422b17f8f5c7bb35f655cf706882d34fb + ), + y: uint256( + 0x06b5e3947f649e125bcede91f9319b269f3db52ad8c56e6d91bd6615732467fd + ) }), - s1: Honk.G1Point({ - x: uint256(0x13b5177fc08968ad64a53d03cd78fded2405901bcb6a0adf91500111e0e91816), - y: uint256(0x1ac68a536966301bf3cdfe70d55442d41ab697984821c8db5605018ea78505fc) + s1: Honk.G1Point({ + x: uint256( + 0x13b5177fc08968ad64a53d03cd78fded2405901bcb6a0adf91500111e0e91816 + ), + y: uint256( + 0x1ac68a536966301bf3cdfe70d55442d41ab697984821c8db5605018ea78505fc + ) }), - s2: Honk.G1Point({ - x: uint256(0x22f103cca6c92c267a93626743edea371bfe65360e8d425bc6dfa7f37fea5316), - y: uint256(0x297cd73d12d894253268fdb948ea7f85d1b41e942f15eee43381db4707795c79) + s2: Honk.G1Point({ + x: uint256( + 0x22f103cca6c92c267a93626743edea371bfe65360e8d425bc6dfa7f37fea5316 + ), + y: uint256( + 0x297cd73d12d894253268fdb948ea7f85d1b41e942f15eee43381db4707795c79 + ) }), - s3: Honk.G1Point({ - x: uint256(0x1f59d8f212864a8d1eb077d44972fc944a31d99d4bd8932cefefac230c8fbdcd), - y: uint256(0x186140203c30f7ca375ab9daa1610b84eb1567bc49bbfd68907c9314e79aa8d6) + s3: Honk.G1Point({ + x: uint256( + 0x1f59d8f212864a8d1eb077d44972fc944a31d99d4bd8932cefefac230c8fbdcd + ), + y: uint256( + 0x186140203c30f7ca375ab9daa1610b84eb1567bc49bbfd68907c9314e79aa8d6 + ) }), - s4: Honk.G1Point({ - x: uint256(0x0b54d2e585ff9ad6a009b877cd9a695fd6e0228bbcb918a69402b398fb179090), - y: uint256(0x2c8c78f62d51be262cb36e5d500ec56d5aa9aca88b5c59f5dae069f65356302d) + s4: Honk.G1Point({ + x: uint256( + 0x0b54d2e585ff9ad6a009b877cd9a695fd6e0228bbcb918a69402b398fb179090 + ), + y: uint256( + 0x2c8c78f62d51be262cb36e5d500ec56d5aa9aca88b5c59f5dae069f65356302d + ) }), - t1: Honk.G1Point({ - x: uint256(0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26), - y: uint256(0x0015b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f) + t1: Honk.G1Point({ + x: uint256( + 0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26 + ), + y: uint256( + 0x0015b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f + ) }), - t2: Honk.G1Point({ - x: uint256(0x1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e), - y: uint256(0x305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d19) + t2: Honk.G1Point({ + x: uint256( + 0x1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e + ), + y: uint256( + 0x305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d19 + ) }), - t3: Honk.G1Point({ - x: uint256(0x061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d), - y: uint256(0x1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e) + t3: Honk.G1Point({ + x: uint256( + 0x061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d + ), + y: uint256( + 0x1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e + ) }), - t4: Honk.G1Point({ - x: uint256(0x043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce), - y: uint256(0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854) + t4: Honk.G1Point({ + x: uint256( + 0x043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce + ), + y: uint256( + 0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854 + ) }), - id1: Honk.G1Point({ - x: uint256(0x2ca4d4cf001b0127cd0565fc4b314c2474265b2622649219983cab5c3a264711), - y: uint256(0x0d3ea6f0e79667587a7b6282ef29030ab179083b2bf591eab27083c9dfefc030) + id1: Honk.G1Point({ + x: uint256( + 0x2ca4d4cf001b0127cd0565fc4b314c2474265b2622649219983cab5c3a264711 + ), + y: uint256( + 0x0d3ea6f0e79667587a7b6282ef29030ab179083b2bf591eab27083c9dfefc030 + ) }), - id2: Honk.G1Point({ - x: uint256(0x2e385c38f2da1b46e0d42a4e2a23d0daf6bb72f51825e4eb5138a63ea595e94c), - y: uint256(0x043ebbefa92caf5877c70c19abad06f960d0ffc4cda2c872eeb8e684e23011ff) + id2: Honk.G1Point({ + x: uint256( + 0x2e385c38f2da1b46e0d42a4e2a23d0daf6bb72f51825e4eb5138a63ea595e94c + ), + y: uint256( + 0x043ebbefa92caf5877c70c19abad06f960d0ffc4cda2c872eeb8e684e23011ff + ) }), - id3: Honk.G1Point({ - x: uint256(0x0b329f411f2cbeffd924314b090f38bc2e545cce875002c8f0bc316d3b1d2306), - y: uint256(0x1cfe8ce6e2b63866d787025ce891808674a03b1019feba01619009ed49699b52) + id3: Honk.G1Point({ + x: uint256( + 0x0b329f411f2cbeffd924314b090f38bc2e545cce875002c8f0bc316d3b1d2306 + ), + y: uint256( + 0x1cfe8ce6e2b63866d787025ce891808674a03b1019feba01619009ed49699b52 + ) }), - id4: Honk.G1Point({ - x: uint256(0x276c422860c1379865016a49ae306de911d13c93ed6ed41ab4cbd32ae399c0d1), - y: uint256(0x0e00679146a6dca134568a276976a693a2210ad675464dfab6359df22424b681) + id4: Honk.G1Point({ + x: uint256( + 0x276c422860c1379865016a49ae306de911d13c93ed6ed41ab4cbd32ae399c0d1 + ), + y: uint256( + 0x0e00679146a6dca134568a276976a693a2210ad675464dfab6359df22424b681 + ) }), - lagrangeFirst: Honk.G1Point({ - x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), - y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) + lagrangeFirst: Honk.G1Point({ + x: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000001 + ), + y: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000002 + ) }), - lagrangeLast: Honk.G1Point({ - x: uint256(0x16e42cf8cb1c82a0961f9eba8413bce1974df0c9ff87b539fb06f3568375a5ca), - y: uint256(0x05cbe0b6a237e4926c1ba462a2bc921e4d40a0cf5c6b828f0c5d2fade965942f) + lagrangeLast: Honk.G1Point({ + x: uint256( + 0x16e42cf8cb1c82a0961f9eba8413bce1974df0c9ff87b539fb06f3568375a5ca + ), + y: uint256( + 0x05cbe0b6a237e4926c1ba462a2bc921e4d40a0cf5c6b828f0c5d2fade965942f + ) }) }); 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/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts b/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts index 2902570cb..8b39e0345 100644 --- a/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts +++ b/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts @@ -438,11 +438,6 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await registry.finalizeCommittee(0); // Publish committee (this triggers onCommitteePublished -> onCommitteeFormed) - const nodes = [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); @@ -483,17 +478,10 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await registry.finalizeCommittee(0); // Publish committee and expect CommitteeFormed event - const nodes = [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await expect( - registry.publishCommittee(0, publicKey, pkCommitment, "0x"), - ) + await expect(registry.publishCommittee(0, publicKey, pkCommitment, "0x")) .to.emit(enclave, "CommitteeFormed") .withArgs(0); }); @@ -749,11 +737,6 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(0); - const nodes = [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); @@ -853,11 +836,6 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(0); - const nodes = [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); @@ -1105,11 +1083,6 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(0); - const nodes = [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); @@ -1184,11 +1157,6 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(0); - const nodes = [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); @@ -1427,11 +1395,6 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(0); - const nodes = [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); @@ -1558,11 +1521,6 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { expect(await enclave.getE3Stage(0)).to.equal(2); // CommitteeFinalized - const nodes = [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); @@ -1617,11 +1575,6 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(0); - const nodes = [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ]; const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); diff --git a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts index da957b89c..7ff760319 100644 --- a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts +++ b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts @@ -424,9 +424,7 @@ describe("CiphernodeRegistryOwnable", function () { await finalizeCommitteeAfterWindow(registry, 0); await expect( - registry - .connect(notTheOwner) - .publishCommittee(0, data, dataHash, "0x"), + registry.connect(notTheOwner).publishCommittee(0, data, dataHash, "0x"), ) .to.emit(registry, "CommitteePublished") .withArgs( @@ -464,10 +462,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"); expect(await registry.committeePublicKey(0)).to.equal(dataHash); }); it("emits a CommitteePublished event", async function () { @@ -494,12 +489,7 @@ describe("CiphernodeRegistryOwnable", function () { await registry.connect(operator3).submitTicket(0, 1); await finalizeCommitteeAfterWindow(registry, 0); - await expect( - await registry.publishCommittee(0, data, - dataHash, - "0x", - ), - ) + await expect(await registry.publishCommittee(0, data, dataHash, "0x")) .to.emit(registry, "CommitteePublished") .withArgs( 0, @@ -670,10 +660,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"); 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 c1f675673..032c50a07 100644 --- a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts +++ b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts @@ -342,7 +342,6 @@ describe("Committee Expulsion & Fault Tolerance", function () { await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(e3Id); - const nodes = await Promise.all(operators.map((op) => op.getAddress())); const publicKey = ethers.toUtf8Bytes("fake-public-key"); const pkCommitment = ethers.keccak256(publicKey); await registry.publishCommittee(e3Id, publicKey, pkCommitment, "0x"); From f049c890074cbec0cf33ce8562da558590386662 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Tue, 19 May 2026 13:55:12 +0200 Subject: [PATCH 05/20] update CRISP --- .../crisp-contracts/deployed_contracts.json | 137 ++++++++++++++++++ .../CiphernodeRegistryOwnable.json | 2 +- 2 files changed, 138 insertions(+), 1 deletion(-) diff --git a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json index 4c1f11b02..bfda4258e 100644 --- a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json +++ b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json @@ -145,5 +145,142 @@ "address": "0xA2Ced5053E66BA1E42583ffC996038A24110fEc7", "blockNumber": 10697384 } + }, + "localhost": { + "PoseidonT3": { + "blockNumber": 3, + "address": "0x3333333C0A88F9BE4fd23ed0536F9B6c427e3B93" + }, + "MockUSDC": { + "constructorArgs": { + "initialSupply": "1000000" + }, + "blockNumber": 4, + "address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" + }, + "EnclaveToken": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + }, + "blockNumber": 5, + "address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" + }, + "EnclaveTicketToken": { + "constructorArgs": { + "baseToken": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "registry": "0x0000000000000000000000000000000000000001", + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + }, + "blockNumber": 7, + "address": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" + }, + "SlashingManager": { + "constructorArgs": { + "admin": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + }, + "blockNumber": 8, + "address": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" + }, + "CiphernodeRegistryOwnable": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "submissionWindow": "10" + }, + "proxyRecords": { + "initData": "0xcd6dc687000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000000000000000000000000000000000000000000a", + "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "proxyAddress": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853", + "proxyAdminAddress": "0x9bd03768a7DCc129555dE410FF8E85528A4F88b5", + "implementationAddress": "0x0165878A594ca255338adfa4d48449f69242Eb8F" + }, + "blockNumber": 9, + "address": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" + }, + "BondingRegistry": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "ticketToken": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9", + "licenseToken": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0", + "registry": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853", + "slashedFundsTreasury": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "ticketPrice": "10000000", + "licenseRequiredBond": "100000000000000000000", + "minTicketBalance": "1", + "exitDelay": "604800" + }, + "proxyRecords": { + "initData": "0x7333fa82000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000dc64a140aa3e981100a9beca4e685f962f0cf6c90000000000000000000000009fe46736679d2d9a65f0992f2272de9f3c7fa6e0000000000000000000000000a513e6e4b8f2a923d98304ec87f64353c4d5c853000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000000000000000000000000000000000000009896800000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000093a80", + "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "proxyAddress": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318", + "proxyAdminAddress": "0x8aCd85898458400f7Db866d53FCFF6f0D49741FF", + "implementationAddress": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" + }, + "blockNumber": 10, + "address": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" + }, + "Enclave": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "registry": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853", + "bondingRegistry": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318", + "e3RefundManager": "0x0000000000000000000000000000000000000001", + "feeToken": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "maxDuration": "2592000", + "timeoutConfig": "{\"committeeFormationWindow\":3600,\"dkgWindow\":7200,\"computeWindow\":86400,\"decryptionWindow\":3600}" + }, + "proxyRecords": { + "initData": "0x4d600e5d000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000a513e6e4b8f2a923d98304ec87f64353c4d5c8530000000000000000000000008a791620dd6260079bf849dc5567adc3f2fdc3180000000000000000000000000000000000000000000000000000000000000001000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f05120000000000000000000000000000000000000000000000000000000000278d000000000000000000000000000000000000000000000000000000000000001c2000000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000000000000000e10", + "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "proxyAddress": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e", + "proxyAdminAddress": "0x8dAF17A20c9DBA35f005b6324F493785D239719d", + "implementationAddress": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788" + }, + "blockNumber": 13, + "address": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" + }, + "E3RefundManager": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "enclave": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e", + "treasury": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + }, + "proxyRecords": { + "initData": "0xc0c53b8b000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000b7f8bc63bbcad18155201308c8f3540b07f84f5e000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "proxyAddress": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82", + "proxyAdminAddress": "0x32467b43BFa67273FC7dDda0999Ee9A12F2AaA08", + "implementationAddress": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" + }, + "blockNumber": 15, + "address": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82" + }, + "MockComputeProvider": { + "blockNumber": 17, + "address": "0x9E545E3C0baAB3E08CdfD552C960A1050f373042" + }, + "MockDecryptionVerifier": { + "blockNumber": 18, + "address": "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9" + }, + "MockPkVerifier": { + "blockNumber": 19, + "address": "0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8" + }, + "MockE3Program": { + "blockNumber": 20, + "address": "0x851356ae760d987E095750cCeb3bC6014560891C" + }, + "ZKTranscriptLib": { + "blockNumber": 22, + "address": "0x95401dc811bb5740090279Ba06cfA8fcF6113778" + }, + "DecryptionAggregatorVerifier": { + "blockNumber": 23, + "address": "0x998abeb3E57409262aE5b751f60747921B33613E" + }, + "DkgAggregatorVerifier": { + "blockNumber": 24, + "address": "0x70e0bA845a1A0F2DA3359C97E0285013525FFC49" + } } } \ 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 5122e33e1..50424a311 100644 --- a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json +++ b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json @@ -1353,5 +1353,5 @@ }, "immutableReferences": {}, "inputSourceName": "project/contracts/registry/CiphernodeRegistryOwnable.sol", - "buildInfoId": "solc-0_8_28-e33d1cfedc69d8ad74eec5a84c8ef358a020ec1a" + "buildInfoId": "solc-0_8_28-23df357f03d1ab6ff0a7509a32ecf84190997859" } \ No newline at end of file From ea9e4fa3b1ea98082c1647538482e9389cfb2e9a Mon Sep 17 00:00:00 2001 From: 0xjei Date: Tue, 19 May 2026 14:10:41 +0200 Subject: [PATCH 06/20] update insecure benches --- .../results_insecure/crisp_verify_gas.json | 86 +- .../results_insecure/integration_summary.json | 100 +- .../benchmarks/results_insecure/report.md | 94 +- .../bfv/honk/DecryptionAggregatorVerifier.sol | 1635 +++++----------- .../bfv/honk/DkgAggregatorVerifier.sol | 1637 +++++------------ 5 files changed, 1065 insertions(+), 2487 deletions(-) diff --git a/circuits/benchmarks/results_insecure/crisp_verify_gas.json b/circuits/benchmarks/results_insecure/crisp_verify_gas.json index 9f0b06b12..f72f01071 100644 --- a/circuits/benchmarks/results_insecure/crisp_verify_gas.json +++ b/circuits/benchmarks/results_insecure/crisp_verify_gas.json @@ -1,76 +1,76 @@ { "verify_gas": { - "dkg": 3037910, - "user": 2972965, - "dec": 3549222 + "dkg": 3042950, + "user": 2973097, + "dec": 3553770 }, "source": "folded_proof_export_plus_crisp_verify_test", "artifact_sizes_bytes": { "dkg": { "proof": 10944, - "public_inputs": 416 + "public_inputs": 480 }, "dec": { "proof": 10944, - "public_inputs": 3488 + "public_inputs": 3552 } }, "calldata_gas": { "dkg": { - "proof": 169920, - "public_inputs": 5504, - "total": 175424 + "proof": 170124, + "public_inputs": 6144, + "total": 176268 }, "dec": { - "proof": 170100, - "public_inputs": 16664, - "total": 186764 + "proof": 169932, + "public_inputs": 17304, + "total": 187236 } }, "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.11572825, "runs": 3, "total_seconds": 0.347184751 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 0.608609847, "runs": 3, "total_seconds": 1.825829541 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 0.578625417, "runs": 1, "total_seconds": 0.578625417 }, - { "name": "GenEsiSss", "avg_seconds": 0.124242194, "runs": 3, "total_seconds": 0.372726584 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 0.223503888, "runs": 3, "total_seconds": 0.670511665 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.568398333, "runs": 1, "total_seconds": 8.568398333 }, - { "name": "ZkDecryptionAggregation", "avg_seconds": 49.0470405, "runs": 1, "total_seconds": 49.0470405 }, - { "name": "ZkDkgAggregation", "avg_seconds": 20.15144375, "runs": 1, "total_seconds": 20.15144375 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 1.504357312, "runs": 6, "total_seconds": 9.026143874 }, - { "name": "ZkNodeDkgFold", "avg_seconds": 62.8892475, "runs": 3, "total_seconds": 188.6677425 }, - { "name": "ZkPkAggregation", "avg_seconds": 2.15679725, "runs": 1, "total_seconds": 2.15679725 }, - { "name": "ZkPkBfv", "avg_seconds": 0.328596097, "runs": 3, "total_seconds": 0.985788291 }, - { "name": "ZkPkGeneration", "avg_seconds": 1.329930791, "runs": 3, "total_seconds": 3.989792375 }, - { "name": "ZkShareComputation", "avg_seconds": 2.693458527, "runs": 6, "total_seconds": 16.160751167 }, - { "name": "ZkShareEncryption", "avg_seconds": 2.489975329, "runs": 24, "total_seconds": 59.759407916 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 6.047533777, "runs": 3, "total_seconds": 18.142601333 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.097465083, "runs": 3, "total_seconds": 0.292395251 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.226985783, "runs": 5, "total_seconds": 1.134928918 } + { "name": "CalculateDecryptionKey", "avg_seconds": 0.10908618, "runs": 3, "total_seconds": 0.327258541 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 0.608034097, "runs": 3, "total_seconds": 1.824102292 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 0.556764833, "runs": 1, "total_seconds": 0.556764833 }, + { "name": "GenEsiSss", "avg_seconds": 0.124542333, "runs": 3, "total_seconds": 0.373627 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 0.222099139, "runs": 3, "total_seconds": 0.666297417 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.418797042, "runs": 1, "total_seconds": 8.418797042 }, + { "name": "ZkDecryptionAggregation", "avg_seconds": 48.00139975, "runs": 1, "total_seconds": 48.00139975 }, + { "name": "ZkDkgAggregation", "avg_seconds": 19.870362291, "runs": 1, "total_seconds": 19.870362291 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 1.439776368, "runs": 6, "total_seconds": 8.638658209 }, + { "name": "ZkNodeDkgFold", "avg_seconds": 60.322392749, "runs": 3, "total_seconds": 180.967178249 }, + { "name": "ZkPkAggregation", "avg_seconds": 2.121552416, "runs": 1, "total_seconds": 2.121552416 }, + { "name": "ZkPkBfv", "avg_seconds": 0.329691472, "runs": 3, "total_seconds": 0.989074416 }, + { "name": "ZkPkGeneration", "avg_seconds": 1.332915194, "runs": 3, "total_seconds": 3.998745584 }, + { "name": "ZkShareComputation", "avg_seconds": 2.649475611, "runs": 6, "total_seconds": 15.896853666 }, + { "name": "ZkShareEncryption", "avg_seconds": 2.470565315, "runs": 24, "total_seconds": 59.293567582 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 6.067798569, "runs": 3, "total_seconds": 18.203395708 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.094844083, "runs": 3, "total_seconds": 0.284532251 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.207447841, "runs": 5, "total_seconds": 1.037239207 } ], - "operation_timings_total_seconds": 381.878109416, + "operation_timings_total_seconds": 371.469406454, "timings_seconds": [ { "label": "Starting trbfv actor test", "seconds": 0e-9 }, - { "label": "Setup completed", "seconds": 3.041689125 }, - { "label": "Committee Setup Completed", "seconds": 20.243337708 }, - { "label": "Committee Finalization Complete", "seconds": 0.007484375 }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 304.504600792 }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 307.024598667 }, - { "label": "Application CT Gen", "seconds": 0.318660917 }, - { "label": "Running FHE Application", "seconds": 0.003694084 }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 79.271051417 }, - { "label": "Entire Test", "seconds": 409.917920083 } + { "label": "Setup completed", "seconds": 3.030690125 }, + { "label": "Committee Setup Completed", "seconds": 20.213050291 }, + { "label": "Committee Finalization Complete", "seconds": 0.006164875 }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 295.255807583 }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 297.75637375 }, + { "label": "Application CT Gen", "seconds": 0.311138833 }, + { "label": "Running FHE Application", "seconds": 0.003876709 }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 78.078044792 }, + { "label": "Entire Test", "seconds": 399.405836208 } ], "folded_artifacts": { "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000469753df342aec0e0000000000000000000000000000000000000000000000006e4d829d66aff749d000000000000000000000000000000000000000000000005f6499be2b18300b20000000000000000000000000000000000000000000000000002be6db4c5558c00000000000000000000000000000000000000000000000eedb4b6056c881e3400000000000000000000000000000000000000000000000867b55169f2600f58000000000000000000000000000000000000000000000001bc9f519f74446bfa0000000000000000000000000000000000000000000000000002b6d6df53885c000000000000000000000000000000000000000000000007269f9ee62308a02500000000000000000000000000000000000000000000000fc8ef0ceed9608e82000000000000000000000000000000000000000000000002d32a3b72f08734b600000000000000000000000000000000000000000000000000018a5af26897d800000000000000000000000000000000000000000000000665ee11e35db35bab00000000000000000000000000000000000000000000000c29f716629ef9a6b300000000000000000000000000000000000000000000000ef868c0047d6dd22700000000000000000000000000000000000000000000000000026ab8350fbcd619e3c65ffa1c0d29b2cd62700cf8d71ebcb032227e21f28af6c3d9993353159827a80a592b60afb7bd0a5547279d7b7b6d745bd21e61cb30dd2fe6fa64ed7ee81bddec82e6608821134bc64e48afc548d54643761d84f4518e91e1bb576a9b3a1efdee0270f6a11edde9c2fd71e9de5a134a7ffcfa996506f17b7ded320b946905e9267cb38c0568773f0534e90cb7b63d7211d220cf75c33fef50d1b5a5100e055a47bec0d9798656da3a784b526caa90e53303c14eb63b919c2949535645bf0d9547c8dce32e13dd504bc6be4948f9635a4189cac34427ead69640255ad33c0d14a348d9edcbdb6ef4d4b0a379e7fdeb9801b76a0da963bdb56d89ba8f3a0210b7c42426c99152b8bbcdf2b36f1c1855f0ceb7482c347de29259ad7e05498a03d6dc7925daaca12e8661b8410ba878a1497660cac39150a0d2d1ca7b556cd5217867ffbd422a5ec0f3eda032d0da1dae248fdb76aa76f2aef8583db964d1491f5aff0a8af8585259f308839565b5bfa984c574d4cf02f2552749a67f4ee7f91fd21add31916d3b40f4006b7a83ebf00c14999182cacfc3b853fa33eb52c4bc0d7a9f47486d5c9f5dc5f1fba6a4c5edebfa27f2b9d47ef6a7de0f966356dfac080b3dab741554edf23aad2a27840334a132e4ccc09bc438f503346aad442b0214216a2005c8c7a405b68cdc0032c8a6f8ff1c663bfcfd165d7de791193c31c30dd9f2f2f2a0431be2e4e4f1fe5b4be54f4df8b761ca81b8634e42a243789b7219142015b3808efb3a739c019f139df3488006f7e463f6d19fcf228bce9fa6d613cc0c01c36911240ee0501a0cc007655ea1fd15b8a1ad2f8d1ddf49931bc0bf177827b2cceb2956abbee92ecef40924cb1e618efef90f0194b8d8df4745ac94211fbc877e3f1c1843dd9c04c81659fd1071b38a33797f6a6c395143aec09c8901b9d02917af3362e348cfb2e9aabd53ebbe6b3565508163ea1901f214764dda2cc1ea3a3e121b0655786495091b1b6eb05be9d77b5149795f94e4bec6e2b183149dc7fb2a6c49197a09199db3d00a13ed9d3fe11a18f5fac973b7349533df4922301951689c04b3f866c7f8425c047237cf8d409e8ba27c49e924268817c9e9055066deefa06a0b869217f5fed30835f92bd9662aca74e8b06d29e525ade59a1fb24ae716ec7d331fe77e298f28024bb1fdcc10c4390920b5592dc36749909d0c0f625429773d2393a4f713600cabfca65ec0617333ae5764f69790a9f97bab2f003fff339c5965274e74d713ade8d48df30bf48503c109d9e6c124b70b6277299d62ad7b2b7138796b217dbcc3c4acf6017e1bd6fdbc366957610a0f79eb4f17ed4c180fa64e56f7ce6c9c3aab320f1552db2274fcd7a4576de6cfb0d3db3c1ea4b1ce1f3afd3ab96c2e78be599e3d94f43d7f89851fc6ed825d6658f005e815a7cd1bd26641e038a56293b8095be2f287d24612af3ed6ba44a1f3f3a911e3302a5476eff60236e0580cf8b25f9efc8244b30ed8786f66e39dd4456aa4f024029eba613ba4fb9a7994e9ce5afb6923109490c62e0c988df549f2e9dcc3a56829bb3f7054a4b8cab4aa1769fa658620e87d592cbc03f9a71bca647f275288ab02dab6867241f0d9d7a67a4b33933a1709d75274bf5bbdca414d3ec79ed120fb064ea0f18c25252dd3a82b941e656196dae6336b385dbc60cb74e8cd668c6f631fa549c5ccb417790c6e015e9adb9d640012eb6c177e18eb7a319e4fd7a266671c31bccb4604b5acb7226d74e075d4276b4e9410a595e91dc5f864d35c6e95b72d4308c3be58dc29da40e25d1f63a36940f572b901c39f31e355b075dfc95a2d2092ece23729f1bf832fac0ec1ccd994f016562ca1efebf54428b688f1603df1076714c5d7d7ccd14a87225c15c5178ecb331bf323ab74f8240715fd0166c33605affcaa1b4b75d30f034659860df6af13b6ee9e6e5c1cbc5e131c2709ea226c11cf137940d661f8f254ebfe274cbd889768cc23d728cd2c5082012f427228cd21cf49509a55121e5756aa33e207a548d5a8f15e9f4869564fb2fcef1c4577e81f237f271365e73cbe12dfdee90ca49799cfef0cc0af26d57ac820007cf3cfc9073dcb3915ed77c7e50f58441879c2c5bb450ada48fb95182dd02943e980031a257c5bb6c116e0e17a1efd1877dcc3a988d9b5680f05f5dd7927bbd0336c143c0aa03fbd503e48787f9296c4cdb00160adecdd0ac45174988fbc535e86d6e4250b3b669d61dbca0afec215f54337b74b36820a3794c4824c9c812aecbdb860be238bcc0ab026c45a1329e6ccbb1f0e6abd83cb9910e5468c9e22ce6c4c3c5d1b2892318212462091cc6f082e3946445e67456576206a0777bdafae6420bc108d0d545b60b47a8647c9acec31abd3eb4a940ce24304658f9ce2ff50a5f5a656f80dcec5d9ed6e857d317c33e96e97e19dea73112a08b301cee1c5a26264b2033918a03534a00b9812d0dd992842efea7d0323402034bef78dbc11232402264318246fac64ff0d90acbd0b1930b4377ba08f35e91ebf526c3802640b74f6b786ec118f486e979a8f8d9bcc19307bee149a5a4d459a145723fa863d8230753eb1dd08f4187ced1f037867e3308affbc4fd535746aa6760d018f75bca7bf3396a59902320058d6cd5935e44ea6943d47fb9d45ccdbf8ef6de9a5231e2e4e2a1558662b10e223298ce2cc72aa45a021f1437be501b2743c08bfbc9708db8b2cd7a9ce17256be9fee29f8d3dbeffea7001aa6648ed7fdd53c0e9117d88379f75466d7c2cac187ce9ea645f137c8531c417fa019100cbdf78be2763d38414b97a6b00871a954bdc2d8e1df7970a636eb0e9d8e5acaaf827e955adfe4a8a2a43598a4cba094833c3e3dbd1f261a5d75b5fd313a5f240ff30d989823dc45a3c3ec90082c626ae27a87be9dadb61eff083583ee3568378e95a1ba75f4bfda023c820466f822adc5410bfa974d63142c051bcafb9f1b00ea44385db8cf1d0c36be3e71899c00cc356ea4a7f52ace0bf72913493c437e3a0cda118619333e1b503b29a7e008a0159746030e90700e49f97c957078dfe35497ae3c4fab77cc9c6f080f84637291d9bd340a31415e39c83c58e3f3b901ccf2e2e2356feb40dc496008982bd189118de77f08ce5a1515447c4a04a8c35e9c89f86d164f10d3df3c3701d8148e5681c027f18f613d5f5d6b22de99a7e7c5d1154db617adb0f4aa4b140a4cc4946ba2c6a68f07b9c6b738d4039291b0e7bce61dd330aed09aadba81f6658e15c46251c44e7e03576d9a2d902f93d2cc226b10a6e44adb7ac928f8275333365101c821d0ce2c8b77aebdb5aabffa4c641b602cfb62c3c98c9f966b2d950a193a4c6e4196464c9eab169d15ac12cf5c4ba880c3bb829afc21ada4364d6a4f81299de8b21679e06e1e557e7c831c03cdb1b8629cb925d4fdc7d44fce7f4ec1517897ec72a2ea327bc07437e1a70ce03392e1d739c2f913698ee590e86c3a7c5e01e70292b73f7dd483bd896583d2bd30e736926227cf981f05cd4d718c6f8025127da4b205d377c16c2d412e76aa1486b04538dee538376fe1522413e41df227cdd52e72c8c867f4a3ea87ee1328f964365e79338fccb36c83a0518445e3c09aa2005ef15405379466c46a41cabae490c736d70476264f3b78287804bca97b68bef27c409883a5ce11bb36fe046d33a2447f7cfc35506203a2e3a01d389c060aa3604cc237fbc043276f8bcbfb7d2d1adadfaba9cbb70a9220efebd6ffc62ed96bd9bf02387189dc44aa75b967986a74ece75106f97c17256baec95737d6de103c6d7a2118ed1f68bbf4fcb6647e625e0a210b31dd9399f49665aa4c27d88a639767a521dafb03ac0ee4f48394d53fe6d209740009a1c11886f6c721cf91e29e268e8cb2a7dba0ab3bcfaf639f230f5cc32a443d1f9afca79a77113657e12b3a4023f130838602c3c9bcda88afbf7ce8fddd16060ec4e0e955bffe48ce7795a750121a7304f58bf35b291bff1e28314c5e1aec8a7301a178b3e2e76b81f27951a2550b817406d894712963faeeed76b7069a0c164e685255aa0ec245c5d223b36b05f760307a8d077c47184df6356e68a3df1b6e645a7a1f68c3a4ec30a79497dbeee8821f4cc5aaee04d9c0c28cbe66e5cbcc48994a4efa36572343f005e385aaa301e157faaaf53694d880169b06e0699f6aa01cd14868786ca9be54a5571ea0791df156e53a49573829a7493415d75ea54104d64b800d4688fe718eb4069b59f93162277d97ac74c5836b5078e713d2181bba46a58bd1be7cfb2d359473be276081f1b6ccb434bbb6f226e9188bc9bcc75826becfb2b589244ff57e97a2994682a3e007aad5202beb5bc1a5ee1fd908cf63b2d36f76e04978c164ce4d36a3646d7581474432c9e22f05b48726087539ee97f746f73cd9d414d242ab5bdfd7609d0b32155a9da08e2067a889517aaf8cb024a8168f94dafce6f93b2f12dfc00641d5218c35fd80cc79d0b4bbb1f766f20ad4ecfc79ca94562895c78057587e9edcf1211fca08a404520fb20794782ed562767d84c1e6bd5d1121a2b58b63d633cb3d528392519b02c9a33d6200c2b94bfeae8eb83460ba6ad4dff419969ce80ad48540cd03c06c82c17abae5a66cd8db314d6cbcfbed1472a446afe81b08ee8e710121bbc002b678d3e50d223f02e4edb538e65fe35f26fa0d21cd77169d208d1cea6188ad9b99abd82c6f89525f6f1034be005f761a9503d049c955ce43bf0997e192ee5118d2c94ab12ba2df5075f42f1572f28660109c53a4051c8aeefb2015915127447e3992602c10f17fa0248d4a63d3fb51d02d7c87575ddd2a228838d54e015405e3aeeb0eda949ed50111d0881e0d2eface34b66941e9ea7246365202f1d207ad9eaf4c7ea11527fa2ceb4c32496270b04edd79eca3542d97606c3ad946601d47aae61d27411aabf3fb0d5c57301ee1ffb7c01ceb4d04ff39180475d097f2b07fc67bf89790286b97b1d814ee45b760d49ddecf8dc3907cfae048afc82550a7f416d7de4c9a6e3a847c5700b4642b637ff9872096679d2d5921eee91c5e11110a2da4abbceb28aa0f9b4bbc132493a2ec0f2712ab0d2cb8d2c589168f9af2f14df7d9de5d4463ff031988639bc2bc42c844a737ca569dc14045284ac011e02992542a5ae3e06562f08bd9eb6d695652b0c0207c3514b292402a5745a60cb22cbfa325db3d30866e1739fe38aca41517e9076700489344ddf68c38c28ae9619b91304c3e55c8f4ffbb089b13b2f04ce5d61caad3d2cb54341b5d1a930d35511de0fd7e69802c35962bd45cd5733a23093208051359e4257f5c9cf68082b362f3744ac22c015a9ac28540106e7d03ddc305882758a50c372a580e5d70c09a720755d74fa777039850e88a289095bc3fda6e3de76a661307c2e77237916761e1dc2e2fb9e5e3e31b522be2e0d8f2e3e7e9cc1dfecb73a1a79a0e26c7c9083080a1e1842116ee304103ebdccf52062bf9578e864afe05f3983c17b84f7bf4d1209460b438ba34839d978ec1c5bf6aab43d09bcada67751ba23a5d39910102bec0c09b6ed88010416941c845ff0402963a715b14ae1a371e9f1913698b402ca86250046ab1d84ba0a6bb12686cac9ba831fa28fa3d0ba72589da190425b42548e220351f64ece113e51fdad160958af52e544b5d08fde40fe37bb84cb659bf593033e291841fa82a53a8f2c141b596c4ed461f562e632b2ca893c12a53bf0de5d2f084d18fd57562a366766d549e205cb36846670c81b92ceed909400a673e53223e9c3b1001c31f367bc9125089c292d5c65dbe914f59cfb0c1b702c7847f2151732806c02b5aa5e97871f60839b79d3e6feef3a3f696115c06e05b0429e1d5321042be272a689d6010378e100c44a1ea4c1841d88a61ded92b9c04347056b3e098fb4e93729884cf98a415354961cb468d38c8c5dab2466952e1863952de5d22bce831cc9e2f11fc39a4e1911e4e391aef8fd7e2f2868e7e8593519a4dbbafd163660310bbb68814e644e234d2a6214d3c2d84d5e78091ce6a3167f992d6c411b70840ca25c4251db8cb551e4a37170bcc4e0aad6a73ca6eae85ae85437a8c12fdf8773e4d6896da6b5510c65e8e5ebe2214ce0aeee893aac3b5169ace00fd20cd9084610949486651f579981d04ce71f246593a621b94b11bf2d4a53a9b7272a75ec707e5d6e9b94cba7277335b4dbe3a721bcc5d8d33f2d6521047d8b2e33035d2aa05920b16961a8e9f5091d1e8dcb0ef43ed663dd29d95e24307a3cbb14248a1fde5949b811c8cb64d561a5814c4e42db4c2a82ed3f85428951892885a92727050ec02883b4fae76a8ff11f88b279cafc825d492c8d88e6a21b8069bdbe3020b22284e21dbc649e9fea5576619b0d5a6c20662adc00a8cae9dc666fe84d2a33893fe7fd23a4b3602b1872968e09b248a1e5f009b8221019113733a639e314bce17972dd81107ac90a257fb403b80809e8a41f298cfeb0be9d87341e9e8026b58446c8f6efe18ba6b664917d764b67f883a1573207364f3a05ea5ac4124a247da1fbf361caf35db0991d02d7cbeec4e6867b454e1c728cd64859a18ff1d72ade435bb8dbed3cf274c0830a6adc7e9aa296f01e451882b007fd972875c54a2a76150e904347222648306798c03ad2eabf742dee661f1ff2ee8b7aad5061f408e8c42347c0400fa8fd99c1e5d560ffd9c45e274bba4f8e3de5f6899a9eafbc05c680f29f0e759926432803bd257f347279ad57e0cfae3ac1820766f2bfa89a2a054c1318608489a6149c8ce98f751f4f90897fa87921b04885242913fd053c29d2250a857eadfa84a16b5b4ded1c2f89d817a2e005da0b94a425b9890920c829d60b0fc66656f0a79faa3e5496317705e56c49f69b3732213b652469ede8b62007b1e8209e5e0eb0842a86321e49413e7d1c48539b8c8689d932b368452d8f00a415eadc12aeec854463da5ec3e848343d7e035b69fc369e03b45d426c80cc21af387870e76a693d616c271e15dbaf75d32b19d1e6ccbc63ac71e155d002ad2992a221a39a21245e9ce4d06a5d450398bf7fdad0fcfc4edc1f12c3a109d98d1b24c341201ab7ad61528931ed868d4ae748b0935ab27304779ac23257a0b4e40fcd390fe49200b9b9a086a7756db0a14b232ad01be5356c40d31c1aea918a7206b55b1f8d4af81e1cedb29c4368641a2a5d2440c70d6908fde350ac8a26c7f22d3b2f21b2c3632af0f2e9f3c9e52e99d9afb03fddd83f09cb28d206f67086000dad45940c134287e81c13e4bb5bc22ef82e4c5e061e69084a781a21997c063010b394acfd38162430147456b9de74aad70a060df546ca38407bdcbe464b01aa140d5bfd0a61d5158d157b2f9eb42ee971192939f25f02448cb5782b36d5ba5413dde40e8944b03e710c31402a37c420dacb73603c0e62af12c136f833aa3437064f488a6685c8f0b0c291eb33aa33b81a9e4c513e6ac55c7175c3073335d9740cfe4ba38839d89625ce94c63e04178c0c76553844241b8ffbf82c53237a02390282e7cff22065a0568345157ed5e428b107946d5003cc6ac9718b020a21e589158a93d1249c81b6f5edc19b1a461d4886f40991c89be5c89ac77ec5486d77b12852972575eea908a0d0ad066ab7ff71ff02720e41b559dfaf5cc99992a1e82b267146784c9faf18f0a30b1d2c8926de25b4a2b5d4efcf00aee059031129823405e5c2b04465b50e6a6300c519152d3b175595d3ee4a7c103c90a46683bf33900973a27e9d6caefb89a1d148934865286ceceb2eb2a6dcffbf93f40d6b8141f32df15c9b7528bf722ac0cfcd16b5d3b5992965d3f49c62a5516dcafc36be9bd117d498ae3e827c74807a0f6120713c018891bc502fcb2f3a3ef5df92fd36e09123278dd884d45abba015519edebefbbe07b19dfb6eee736e611a001af8182f7d28d2df78f84999ed714f241de56318517b31798f9bedfae1771e43d28c74ec01236fa605728da81f251b890f3ae9375925ce406c5808328b62d0c1f61d507fee0d603e2271f6256b38c56b4d51698152dd71d572f18aef64bb8956cfdbbac8291420757829b48a402faa763299af4f927add42dfe05421d6f381015fd09950cb1d49db81efd5ff650a753c43ef4d6c56742c82663cd9ccde44c04a9d16efb3f226f10221eb3fb7fb9cd29e0ca0a9bef0f9b7ce84f43ea5bf4c7497642e2ee7ea1cfe335df4a4cac533cacec297ac86541cfdbb6baa5d5ac993ea49936cd81b391fd9942ed996e79fa851a07adcefd1c2a8b8f57810e54d04a23aacb01abdaf8d2e2bb8819d1ac0ecd13c2e7af62f8407d63039b06b61192c4d7a82f65bcab66e05e769b154a29cc138b5119a732f22dc15ccdd3beed12318162f437d9da4532508b3af66a3e7a10172a476ec9de4a05d3cef956d6ad5bfbb0ec8923723117f5a046ef296f2b5d88055a90b443c25e5b0b877e3474f28f2cdc79f49c618f44aaa02d3d1638363ea5018f4710ed06eeba5776f89dc9dc4afc3fe942f92f90326771de423f8057cd1463a148594606857c7410f3de1767f89e692f64aeb9c80dbd6063943261cef4c11bf08aa50589f51ac7125d9fd81d005b2ddf1d8b1c5e7f97512b4bc9df1655304994d4c05d0881eab395a5925b935d565194e3db86ea73f9e0cb618907cb0aa0d73720855ad56a543d380344559d846d0c87d2f66c010826d2f0a5280fafcc55461e94a95c98123087fe7c5afd72020c01b5bb159507bac36081c5efff00bc7e17f6289e9d2c2c5e9beb2bf4dc4465c3e954f2393e70ceac216ae4898e4821f8dee3c88d8c1085a01d6abd2a23f0ff1f6b92df037992c73a602cdad1370bd59f65f93ed6344a19ffcfae76acee1f8e2444d8fa27b346719541a4dd20cb3c9f3dc615bac655270d1362c9edad97c23f13b6897d65ef2e3d67b2e948af7af733a7252895958bf669fb507eae810fcadebad364fbb3b5d1a68a1146f82fe31c5e8c26c46d4fd4d134123c591bb36cfce94158faadd437ae2ef3920ee3821c8ef268c38cdcdbb380ec23ad56abf2c5cbc9fcdc291c02269af47df0e6fbd50a5e974c1e50024975c390f43b6da32df921c90f11d18d6577fdc99c120126a58a070d74d076cd1b872e0cbba7354f30dd38057fae7023c6dcd63527a1c00ad87e6e3ba05bda510ec142acd5a3c662373ac3f7a7cc8a10ed678466446176938d996ea81ab6002cc8f4c41e5269d007637a81878e6bf0bd562e0d1b60e10da7445419432b35618cd61bf90079a08b5a0fdab94cf8f36d066351118484606ed4fbf014e02b795dfd62b14b16ba950be77d5c317dd70fb058c0d896e18a1073024ed35b213beac45d0eed9cdc684c954bdf3c18f365dc541b68618b8c06e1d8a83550339c96e752a82d077cfb8ce0211447e4bbdbc3b127712004fb7caea05d2641dd30ce23b7d8b9c5530671324cc9aa1bd271ac6c9be835fec30741fa103c877e6c320cd6c0bab7d62d3bcb698b6605be618784663158b717f37ab03000f60911e4d3e33184fd26170e83152da85d24a8f095334e59b7b62cf4c2ed8400688807ae631070c134aadb8290c356a9521af9333a69da38904671ef89c4d97004d118138a5d8f7d945a78c0abc64881ac3ab0c86060db570be08d3dc2445032afc433cd6ea04a649a3b523471a5d2ad1431f3584f5142b2b5046b5f39bc69d16565ecc4219043483b3906d8e2623ca40a26cbf9f18f51ac5259545a592d5f827e0ffe8b7ff77b8f63b0ef131ffa4c5667134828fe82cdc754d15c7cd33cb6c0077f04d332e7ddf9956d603ec3c9c0a55e1db71f19642c7f876452248a70f3b1cdfb9da087a8ca3055014df3aac51deddcbc64dd8d5a8a221d8e91e749b308113fbabb99460f5785f829d0b922389a673cb6dd7cff8c3d288a77f07823028da1292b1f04a858b4630c8306b8fc5226f918328babcef14b9dc10c1880582877e1d450f991999bb052fa475bef0846a915a9d643aff92ed06420fcba96e673339288c3fc6586c13eb5f581e6e7200163a5e011881ebecf259bf2b84139ede52f60e389ecb2a3b2d75a3cc041b4fca6ef2cb232527ef829a8476563013a4d336e204e8ec63f06e0917808f60401e320069d022f661d3ba23965bc66155ac3623f30b29bfc132f9b01495f4c869c57e8cc42cd2242e72fdca3ae4727c02d9ac881c0a145fa3f17edb13347bbd19819e570e3b016cb952295952683f30761f4f03cc105cb9a489ad41d21c68151afe3def6129200c5de1b932ab352119aeae31df5121e519547cd28f05371a2c65f716c239ab69d20fce2af689bc05f99ce4247cfa29cc49b35b4358a3b42ae5c91d95fb99655002e2b0d6d535f21a6c58b9f82a2119649e434ee728c3248ed73b956bb9667ca6b77443651eb331f0b6520d62c5852e3370acf4ada80391db77ba155e3aba9e8d821c2ce1d31ec09f9898ac21b1112dddc3ef15cb12590a98531763b8d043596632648e08f8b80de1767e74855ad7259558e9228544d393351f17db1a37f55314b6d2a39ff65ff22247ce74073deb24cc708bd37b6ff151588a7c0d107521ae217f13afecd55265888f3263cb2bc924a1faf27e8345e2cab32ebc6aae03b62d115631527b68761882befcaea90c0102a7abe55e1764b18199cefbe32acd68f76650b5a83204dd677195396700b93e268fd11b30f6aeae2288c6b31926a50007a0e85eaaf2f7a6c3c040369df5111e1f8e3e91e40e7095ea9820a37d8ed5408382dea8fa3be255dcd2432ac79c3b2a1072aee7d8c69cfea210a58c99dd9c158744bc20b17e8dba95c49091b70ba4f4038acdad5293ad3f47569799a1203e607107575ad494b4857aa06a2be9d0d4ce2aa07260c845899f56d89fbfb7e18c24130097b1e41bc0ec6d7243558f2191bc1ee0b5ece2d0d55c02d3abb80cfd8d10d6bbf360068dcf6cc17debe3a4e8ffa3287fbddc25ef9071872116ea57fb9055a978d85768f3dc162876957749af16512290c1d094e99bde4c38c864d31bbbf85ce2ee488f943be32e2727633089385f2b461d8ca9e0855827cda5fb219fb1717ff0a838ed1b27d82947288e5e8c14590e039cc9b1ab4a33501362144ea57353d0ce3ae7f845bdaecd9e1304b5472ff61d5f0fe5673507d3b79974ae6d6294e16ef1918a4fa4a8d023e89823124327d21b74fe4ae3c1ad52801ad8210a3cefb6ddc0297038ce08f9e4f49ae8f1f99e5e293e702e81f1c7861c1c445fcb48d92ba77eea9b250ba2967d506925efef7f261ada0b3d9f2742502c26d1ac9f750649cf0155a7e327a3556b2bd9f20e009b9601165f5c53fc4b398b8f3f7413d065aa3fae26a12917fdf716f1360181fd1dc81444a57a6b94fd298742cfadfacd30076bfc6ab47be4f09b66cc76b022a47ca5191e725f35288a70698d51c5cf43b77c2f80b2b3bd4d35c2ea033008cedc2f042eab11def2bcd80ba95c3ee8c4fb917435c62c6aeaf2f4a7131b429b5cb2a4ff23081ade5ac7c55b647aee9aab520bd3869f9c93c36aa7326ca6bff7af3771a201959164e8a0978053f0c13e7a87916c47c7d7ba0d1f1d1c826b8873eb31f7d22a07070f5d5fcad09bc1de1960c10ec0ee3285aa2dc35bc42d8b206ddfe119b621ff709b9861e00dff5f5caf92d33c79082c90e3e9a811cfb225ccf802c6c51e0dd1ffc5d73e22eecdb9be76ac089ac3cbe39613b2e6f4948ae91a42c6ccbe9f278c01e611cb037785788834d4b7306a5f94e0f3bc26e9731c53db35e41fd2b41ea68bb1a63e21995c245b20761bb235c4bdb5248afed40f7531fff2a4e300c80ad0019f2ee27ac9ab8181f077b7a0bebcaf59459cac8ff3758a91b051e89dee24ec65c2bd9503a6ec590079fb13870f775c5a56325c5317a88e5c439303808c0846b44c4526e81fd78c7cc680ed2993cbf600b108e88aeae1e772b0b0f8ae5806f883cebfb726b4263916b6969cac42ca7a9e5910be070fb64d007d0a7315d91daaf0e62bb35b5e24c1e2a5095a294afe753fd690a0f4bf298d5f1d0e3fb5311399d876cf50e48fb817b3e8c7146c1d061f07f7d0710e182991462d793227590788cd56236e23963e9a41d9b6fee0eca464fe0b5ecc58b78151f57622a97ca700863f02eb1bc9d78d1082feb4f383d1e2eb5bdcfb4fd72020922208eb19f7e809e9e7b184a15bfa5a0a43863b5de3e191059cb78bf74c53bff6104e6c5637ad0121186bf2df31b19bf8e87fc74d1c71d8d520f181c275958a409dcb7bac15021cc57260697c1b3bf9c2db1a1e87f6db39677caf5445f6e85853823a0d358472092cd19d6850a62402d1e425a2d7c0fdb61d2e02f86928c3fbc56d2ff048147b07b2e6dd21224c6bfed79c76e49a5d79a1da6722776e2c4cacf6a71f4fdf8c3421001e4747173ea80422461affd27e3af8ce9fe9eafe6bd76e47ca4bfb9f54e01787798b5ebc4cf20038ab846044605785202d2ebedc82441c938599daa982200676dab42c70099280328e8899465ad1cf74bbc27cefdbabdabe85be02dcc6be2780dce9f7f6b97c77bf7284dc71ce4fdf48f13b36553b372bde71f04c1010f719c9e06ad95bf42e16fb4c5e9f5779cecc8a99830b62e6452cd80f8843dae91517bca2479d53cf305a9d43105eef240fcf12a391e92c49596770405de304801d27b4f39ddf78d44201072df5a66c3753d6cc740f42fca640ec8a9c2aa509941c2d8a8d9dda693b87dc33c5b641332770cdeff855b01b7d48304376e48925c963036bce8d831d7efd5f879447cb403c7e4d7b01c62e0ded5dbd39ccdbb545c5f30bdff5b42ced0c62cd22918e3598f7a6cb7c9cbdb7cc8cce49be5fb57fedbf702e06430500eb0bc6b794b6172145c3d7e0b18e36f98f98861f8dcd0511b707b91dd03caac365db1699acc849db995026ffed7dedce3505260acbf7c4b09aca9d0b09ef40ff6a704425e1195ffcccffca50ddb4c9cd440fe5d8f918774b2852b7177d38c0a40a9325dae46f5c11c30c0b4043067451cd714925e0799f22524d0d0c2997d8d76af181579491ae7893f4d087b1daaf0a5e38723690b34d70a1d0d32b0eff8e2c99c5e155e65f42c60d0fbcc7604b757896e0910f97a1f49f6283ee2e09e4434f0300af0e0abf8b98762f5c6295427fe8e0fb6748b7e7aafa26b02b1d819b307c1f70a1a3ab3812f41571c1b85dbe9603288f4afce9591e8449d99a0c07aa3eb97c33a646725cb03d52fc53ba19064ce10ae7544921f23c2eddae110359572c3d7b81433fcb47a8ed65977667fdbe70a029dd1f9ce9dfa4db220f8a2bb209257dd8d49b864ae9621cd32e3772d453207bdc88ea8be26869afcb5a3c114c5ed52c900da7ed5489e6e199c7ff224a18275897d832bfe121afc7d663750613d5ad6f4ca266eaf1f69c6e0f1617c647e6a5830ceeb3f4d5a2e70730fffd259aed34a0a6d955f29fbbbb96a7051a9b8aeb3de2c077dd632f0f91ae4d2fcc13ad02527dcaac974cd68ce11d09ee138b586a61ea77db1f4d8877146da47b710d5e19796318dca7b99638e5450448ed4b0776331585b6846d0b3f504854665421ba13580166db9ba74f35923efc566aca7d76e32ed30283f7c5b96a12a4cba7130476d1a3b19df10ba213cf604761861d3b4264b342a68811bcaf6c26c4ded5189b15e6f0deafa36e7396871fc0054270765d9cd75681bda91ef8f1f156f51528c7e724218c156417cabe8378dc773cd875b748baaa0360dcfec3f8b5fd79fa15758af830260018537aabe640e55fe9e8d3ba07fd4e849035112962edc1ff6d26a4bfa0e267a938b358a6d95d86a3208e3a1ac448dd043cbae10f847b76a8a20ce0a559bd0f3cba36be6fb50512452bf06915094f7a7c7b1a7a517c7fe3a666249c3368fc161a3932f2d10cd88276932d3d2315ec9bbd8a1511216be52be76d1cc48820c98e773afc3e24181c2666fe05cf22c763103498316ba0759750d575156016a55d457f3a538a4822d1f4dc598d9c9e25fbb0381f2aee56bad06d5c6a1430f3a6fd2b33477e72b8f91a066301dec27e6c9bb69acb1fe54f692ff9b20e14a2a71e577f1f5f7064bef074b0654239599257dc86b12bdec3376250f97f5f005ae534014c6a69ff11c135d18b4dd33c13e4cc11018b7445104b16e6d94b580049efe999a49bc660ce9f8757ea3456c42319d1f91e21277ede209bcb9bf2da1aa875a2c36bff51aeff4e2c01151a0243d5551e016b5e1ef76d8a98443d304604391d823e2248de66f8a0348170e55f5b635d380ccfac02b8fc88df515c0ddf26c4c9d9b7980a35b819aced7c720ebe1fa773e33e84c79dad6f46d3f9bc35e92d2c337a767350d906769ef475cb72fe2e03ecc358f1ee1074f5d620b36480f21255cf596afae8506f5a7b164314cfd890f45fadb279bcb742345ddd9007b4492bde23b40195702ec4cf12305ef29d751e1a31c1fbd3ffd9d29fff53a3dfb371227c0f79f3487ce0e76d7188070768a8ca64021d32ce22f13989dee1c46be72b0d2faacc65a02532940e3759cfd742171bf642d8ad3321c96fb7a6da32a70300226f637fe241477c36bbcf1c01a059b520194003c1f6bb6983d74395fa5b48f914801b01405c4d89b666b6918ca546afcce05aa80dcc4da69af0bfddf2a5460e", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000211521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + "proof_hex": "0x0000000000000000000000000000000000000000000000063115aef9dd42310a0000000000000000000000000000000000000000000000047698484c13252b5300000000000000000000000000000000000000000000000793a399b1d4073f7f000000000000000000000000000000000000000000000000000188a919b07d57000000000000000000000000000000000000000000000009d59f2c2a28ddd9ac00000000000000000000000000000000000000000000000ed82c9dbec6f22f5100000000000000000000000000000000000000000000000074f0d5d0b895fa0100000000000000000000000000000000000000000000000000018f934b25e9460000000000000000000000000000000000000000000000050b758b170434dc43000000000000000000000000000000000000000000000005e705b87c810fcf1200000000000000000000000000000000000000000000000ac947aa7ac1d9ea30000000000000000000000000000000000000000000000000000214ef97e1cb8a00000000000000000000000000000000000000000000000c365fa58673f1280100000000000000000000000000000000000000000000000c0306cbbd1646e85800000000000000000000000000000000000000000000000b5f507047ada95ff6000000000000000000000000000000000000000000000000000275a4179079091d0fd7adeb7812fd07faadde449e629089b4cdc0c410fcac699af6d2c4e3fea92a5b53ce90b5e0ff0cfe567d996cb6aca1a1246606ca25b86c909f630bebca00265a64231072629f487aeee5ce8d7e010733367bf831e7f63d1b7101497d90491070c09fd0b855118c842cd15ae0320fd756e7ca1dbd3b7010f8e5ae5b268fc00b7dbd954930cd55f698bc9bc8d5f16cc60d4df84f28f2a6290b46501147f98f039fae43b805d73e009418bc836a43788c01be8dc8e9a92b3cb5717edada8b1e233a591b22a8a5ece11574df80c26d70963dbfe6fc235884d78eb51e724f03962378b21b29354376429108bb6667ea87c52a589cf5014aca06c883fe1fd6254f02f8d24d0251b16b997167504500d2a698f1883396eb8bfaa0061bd0fd50473e121dccb4babfdbf5e403dc53a7f4edb47ba6f30b888e190798f67b8fa7ea8b9617fbfbadec069413712b80ad7ef070d6c7967299514921dbb2b933ddd4ffe6710fe639c41c1ae67b0b2f557986d1b5c889c5081a42d8723c2febf8a2298726cc242d340effd764bbf2ee85a4a2ca104d4a3a60cd2321f21bcdb6cf6fe0b2219a030b2c4ab077ed77a64c633ad1994c5be4ae7c3fc40f569e5e913f2193318d9408b0acd9b422395ff688ed0684b8668a67424d2ba723555c9bab7c8bff816ddb1b2d5453d71b602b9f82dbca9466a34c4c7fe94b8b9261086540664b893483260b9b0f50511620158ee8ba0c4430b02da78047a1b0c5b489e9693db6c17f9af330134d427e392d98ddc08373e170b14fa5e12686a51b1b4fc43c8e32f92054181a7de76b8d346ea02fcff8e3ef3cf773cf0171ae0ec5dafb45fd1d20a98ecd0626a9f499e039070050431240f77f24ae171d4756989fc8564cfe1152bc4a478124642a1ff2e2cca217e047052d415ed8113a2ef10448e389e11b55d505ed4568291be4d6e5ad8ddfef6ab61935c38c48101a7a6e7d995206da76cf3d9ce6008110adaec17d2ea8c31ca166ed8d74323023ce5f832083c83cf383a42bb4d522b116a8d89a2e753064230698277590b0102e75ae48c79bb35abbe6e5fc43c9050517546178ed4c05e57f9fb4cc0a5ef089754d543b659ddc1b7121b757f3f9322b0ce7b3447d36496bd9afed562bdac1a231efac0b51999ffd720fa1ca9d07d5271b5b28b8100030f24305dc42a262b83eb2253d8fe199e23ff0b7c3bec3d9a2912eedd6a98e8f675ac664338943124fd5b44b419eac45acff1323ecd493f686ee27631ecf5496ba04fa946b95bfb10fb1d2d43e86a13c109c5f59c95726f3bafa1e5cc25e35a370ab5d0dd951bdd459a6d27a41c5b103f2206b50779174d3b0bc1c952e9214d27a1dbddb5de018cc5937f9db710a17a28e984a8d91e886f4fa640cf57674741fba4d28e2d35151eb618a4504b2c048b2604539abf0876d65716509e484b67957d3fa601a61ed1d37a999cd5d2663ef8602949cd3a1a2b71ae7071fd6c07cef8f0451e5f43a26f702287797d6d74b55998c4e20e6eec8530cf87a09e0149dba7a7411b2ddc83d9fb216f11b3d1ee08fb6f903831fcfa9fbedb3a12e4011636680096e69ee044ed59db4ddad9977b87364026dbe1507e11ea79e5b264750abd4ba0b668bfa90dac3a450101352696bdf69d5332cda48b386e1fddb23114df1be460f352ffdbfe29e2e5ac8ac7f6470f9efa905a115e1e9adc64a1f2023e890f0dc2d9d68391ae052a1cf110a0cf5c3215e7de2f984f1c1048b8cd30ed40af36a815ea0eca7201590ff2b20e5b104cb449df088a2a8c5269526749408494e2977f7c1a0212942ee09a2ef6a6ae21170921e58981887c809e5b2d82326630621510eb59c904ce15fc0497d2ba78b1bf8eca3800318961703a5a40a0820dbd19315824c1af9ca4b94dccee9c7e15597acaecf2633159ac23022d6c1120fb8369440d8e1ea9cdf387e58e1909c3d4ed280686b046b6bde738eeb6d5d4509c03f9ec131655669fe27cf126d44aadd6aaf253dd5e53f15603cef4640db5b0590563558b05af450b3d54dc74f51a807c713c595af076c43fa2432993b05e6300c2864be15e52bba2b6fb00a7bbeb534bb77d858c23cf4e2aa98cae797c8071f9c038cac8946f77b603e91f89cd186a752ca515376178fad9d87e4da92f11e2e150a97faa9dd1070255507a1602af276f1d2f28c7a6a537644aa27f552ddad12243b315fdece7cf05b814bbc25715c24a098389030b910b73ccc31cb446a312792f8b831c8d9e3473d7bb32719326c088390cf3200cf1b2712a1e8109033df0a2826c610569f292da390590e7bfe1bf580c318b3ee0024d49bd8fccdb997a42a429d2c1e59fb115f9b21337237263cfbbb392164610c3587cabae55d6e53211f9f29bd4f1a0af73e75c094f89c2212c783cb7800b8ae6de0b2f793e50f753e1d28439ddaf4750075879b4cb7df5facdc2856519a422ca77992a9edbf573e180f521f7963f1795e1b453f0ef9cc04de2a32c9869794fefa9269f6c84d9e9d1321c9585efd32ae9d1c8fc4e83d3e44d64c77204c232ed31b493b2677e6c5b06a0ff2062506f6785feb3782197ca25e13e111851a229f6587b322bb993d2917e017a199a263c00d46d9f5ba1dfd8f6c6a1043881b27e2bda1ccd544375b95197e283bcaaec150d651f13d40395231c76f7ed439db3bb179332261c09a51ebc77c0e94c0ae2f92699f663765ee759ec63ef6768baf1792ca7fba45f6f7ef302dc125f6af22946ac84161f57f35aafe2c8bd2607e12900196573f1f6d39c66aef592a16db837b55bf3a42587aec8a50311e5081f0f97c37b5d6b17e885ddaebe6190429a13327377e64a15fef6108a5143959a93ce97e79df26f960117c3e2e83ee1dba0ecb0c98f726af182125d09bb22cce9a5c9d15ff9f05d9bb8d1d5fad601716e0abc590a5dc8f77ea98b616cd569a199c51787e08d49359fe745f08e1d81821005529d844e14dd8b81d7a1b3598abbc2ca3e90bb45cd3080a26a647cb127b2417e439737c55268709568bb5f8eb247e3f73778f2218a1411733d4aa93af9e1a1cbe7075cd0999c6fedcb5a837293dcd4cde1e6b575449108e9499c0b13071013c1d0525727925970d98b30fa0861f406ca081cc9c78cbe22559d0575d59800f59920ee80f0b8afe9e0c83c94609a0fb4d745ada61f42351b502f57f513bc10ab13a9b4fc2b37e09f1cebaaad70a7b9f876a43c8b5b958408aca2379fb438d03f330b0d8cbce2a8a32d7d92bf0c77e8e536ba59d59987575092ef8e2e9de69062503b6c29960942dda327d464bad797ed244f15a692affc2cddc366b369def01d69d77012bf3004e87c85c2f3328caa8e5ae13a58e432c550c1a4cd0d0152e13133ff2ba51355534e3ce561f40083db91e50383a13b6d348aa54d0459f90cb2c8135f2e3a001cf86544e86891e3ab854442639d2c374c766f7c2b0d6087e9e0bfa4bf315b552b24bdb08dc93a4fbb824bd5e64baf22df785b6aa1b65ab53942ec18b91fc6850f278620d99c86e408d8f8b14870eb18fc149b1474099cb54742307de566ea77dea59d3d249c3b3c61146f0f4bd8df5732dddb647a24064d20d2b344a36e6e6a2dcf83b9ebee35055d95d9b00952ccbba72f2754ebf2169e48104809bbbaa0b40e98129e5c90b617185b0367ebb15eb2543704bbe51e1532c6d15699e1ecb86b4f0fd61e77fc4c41db426e0fa74789b358249449a9b749e8199300b5862ccea21353d4b1538e962d2732e28be130a2dd86f42759855e5d8a47116dd260c4f67f5b6dd33721621465f5290dfcbe165eabb75f1dfe17cb84434df262d9c0944750e38c8f972bd21a50d4f636c92f575a07e7f4d908c1ad083312f1ad21d33a8c15c79cb71fd13951938692ec8d9b1c7dfcbf401a256e92fc451882c3b2fe36055cbfa4e664473d1234b9a87ab7201f17936d5544451fb6b1935d014c15fb2b3a19d546e6ce00c7a7b65e7821fe72bd563ea29ab5b938e63a0319106ee90f27582cdf489dc203ade9154113d6e66f95b3548735b09412b77a5442311797d3d71921706923e374923e1aafb6eacb86a89dbdd75476c07a5581893801fc301aec79ed35523bb8ceb3be54b19b21cd5400d4eadc1e772b862bbfdfd600b97076b53b6e0afcab84cdf55a25278487011aa0d71df6a5169d9ecbee182d322659f7f61da58e919fb602d1ff24c62cc53e8ab6dcf01d4dc4d64aaabd987540b433948bbdaf7b1865e5caa3fc76eceea5939ed78d8b057603d443c8ca8dcff082f962ea623218da6ced991e7b85b6b8e519c0129baa723bb32259ce1ad5d090a27ac17e6d530fcb85fa3406c9559058fbb7795c9b8d550c37090e28f31e322051bdcd88d74a20ad4c29446ca5aec2c2b8bcfb1d7f07aaf3e9ec1a656b58a341aefd630c847a011e2ea23b2d6e27bf8162ace64423818eb032688e36f61505f0a61789f606696853840245b606b39204e9b3e60e4ca45b0c538e3d7422993e9125434e09b8680961c6d47b2d4372ef35e67b1c91242275eba69be43fd745bbb0f528453b1ec2dd9f76d53ef90dad732494d1b936b640de37aa6ac1c044007f50391b72db135340374236daf96f528d6cf450429a9ad166e6e8df8416914a1860b5fc6b77c5dae07dda1262959b47fe408ff401afa0827a7e16b4f6f2ae0887a0ceb347824fe27dc767dfdf136e17b32301a98e7e9cc49a5c2f8b68bc47ac922300c897a819c28298513dc1936ce5f4ae394a0a762edf0f07fae0f23b49cdc44054a9728a49cc488cfaed54a5a4b012ddb0630f0400e7293ba4330ec46fd95bd03495f1fec982b8aeb87a09ffbbb50478d9109365c28a4cc507098263a9b96b30e49c15a21ccd730578b9e32e3a9c0c6ca548d7340cefbd4986f669f01ee12bd16d3bacfd3b57aa4f54949959c501de8326e4793cb12c5685de0e8ed4221ed482a68b70286635d45456f3fa195aa4ee1338aeb6b5a4a4a4168502ea35e4434c32822b4170506a783bca6a33beddd3fde8d1b78dee1620a8d133527872648b85e2737c225c1dbf2a82058447abc64bb30a2e9814143cb10d1217072307396306914d061b219fcc52d9f0c7e1f7a409d06c0785fc7c744dfc24c8160b4a6d00dd016b5ec086d9506146e29f3a87e2a91295a7fcffb75ba616ef58507bb20d013d2036ca1bf9e9dbe8234cb57016a40c1df29c5819bc1d64151284eed3069386fa523640db88af8cc1a66f793289c1f1a80ea559c2c3653e0c9542f986a2d3c3afd234e6e3c7273e5b491f7d5d4947d5951e0ec0ff911dda8023d2972ce8bb1d9151168034f4d9e87189491c3c4e5beb598edbb47ec1912fee9944d13aa02636d0409665e57410359763dc466580968d4039318fbf7986c903a07eb5a5c5b4476870b00bc68bfa32f6ee9b90d76ba121cf8b63314495e99c12fa1a43322bafc866216bfe3f131fc2de0f53109a042c70e24af1abffb41e578c665568f3d16cbe54c0cea817d6b5341059039a7ee117d14845ddba9e95f479045914e101efa4150a52a4cdf205120977337e9a26ed8493bb0cc0e75c16d8986313b0238380aad69db15b9e63e0c8b417780486eea2b945cdc0844f662df774afc4d623f3d19ff3e2e239542ce815b91e98b1b500fa18ce98dfa476bb19ac2dde5aff99a10df77015510075848f6dba5ce9044ae7559385ffc30be6afbf64d6b026f4bc3dd497195501a83d86f323dc812989fb27c76b6dcb3782b6914ba43ad5e9c72bce07cb0dcda06160ef64ac69e572a3a1b6931fbab48d4cb7024f9d88e3ef23400c208e1c7c42378f38973c9a20a7dd9ec8eebc989ea43cadb72b068148ffd7f5f7fed57964a0f8701c6142aec15d3e72c0a87ea0cfc3d8fc076b672f4262daf68cebb5751490871b47e35edbfd82ab9c5088a7139dd1a0ca9ac33a9b121406df3410c09355b2aa6b18960d208424312d66c1b2057b353f60447562e3dd40755582b0196b927268bee3a57d5f462d99606ec9b7866d8acdbc222df175af39b3f98d0ebee02d2050cf5430da514c4fc23cd2103e9ce6cddc4a456578138112079f3eb0109857f0daeb1e13ae070a0f3a6234cebcbf63855cbe86479e74ec01e253b27a87915b31df385046553bf6b8f350388a1cfe158bae1a23d48bed8f5ef527dbd36811a0c106e01b566fbc9c45c1d235f3b8b123b696fc63637858b4a18b0282de69c5b691cef2bbbed27e61076b756bff5a3a12003f7c487296881293d9f389ff8435c012b84a3cb4da52b32c7e3eb9b23d3c582474fd36ab20cbe32ae48f045f12ddb9605e2d54d8057718545a4418c22cf3b202147399008abf98c1c08dcb044b0d4a514081805ecf8895e86ed22d9540e61650d7b38531a755d7bfdbb47035a3ae527178430b9d7ed50783e9f2d6a139432c30fbe4b250e711445010f0965871e77c50dc3557d1a543c4c17808e4c8ce62b53e260056f1310900190ccc1376d6c8f692a4407b1f730269f888eccea84dff995cf49084c395f67c257a2a9a7e088ff8d1ceeaa3bed5062f7f27c9ededd5c88bef3da6ddb8cdbe4df04bb370f2911ebba2a18ef1d8f6fc99d73b98bc700bea98865cea2d8c871d5c5703f813962c21de20fd323d1109946beff8c485c333f521b010b7c24a68de5d26e5ca759817585b608d1cf1cd0ffb1359256ada7d6c6ad9e020de1559d564e0075c91c7405bf378a2063a8a21243e0c9ce443654e6b0c1c6d7fde159ed74faaf82233c4e704daa312e8c7534e5e2f0e26a8dd523251eef25da2eecae56cac4114b4bc22fd9433d8c09dd96b43d76d86e2014f826cd1ae89251b15bdb6a1bf743973ee53556969f191c4338d4d5e8a99e29255f7bf9ce859f6f1419d11e8740e627906954513ee2c9110e0c3b20f5c1fe7c8ba9ba7cc4fb01cfa1d1f34b6eb6d1a033c6d4678f6be91cf8b5755fa90a9e5d4450656e97913a16561765e43c575fe879994c652c8c290eacfb807c85b7abe6ddadfc9416ae1d625a0aef2728540864f4e0c94ee5696610b74d78e61dffde7ec105b3401322a88a0dba9bf64bf901e744f12cf547af5c1f58bf24b443b3c7a632e05c4e9ababb0d3bb30b5eed6e6c9816c6ea85d9756d031888757e911c3bb04303053c7739fc7f3f287806abf025b790bb8a5e4b1625083dc324f6214093d3d089e40e7e6f6482d35ce1530e35da4db6d9c510fe2af40758ffb55a24a8b62e9e1362e71be4e013cd70869ada510f88a862d22ac11e67066ab76704dbe14bead1cb8f6a2c4b53daf714bbf9a7b216ca8607923da71a7713b3d7dcf3d674cbfc9ba7b651b67974db0398bfa70f4091bf2c1ab82c9a1e222434d6ee173e1e5ff781174ef50d74d2bccc364c09f2de5baf1064d7c30e050c2a8f067004f269807fe984b7924e6214c49baf8cafc5454344982495a3ed37ac1dbf1b21def8be857ff70ed4e848c85d0723d7c3b39ac31dd8335adbcac3a3b711fd7ce514a178d92cc0d70ed94ee72b319d9e037fcc1795e5a7c273f8524e5c031ad20e77e5f262a9abe5597ffb7ea806fbd651946c6aa20dac3e20f0d832f306e6be4dd91caba66a3aedcc07f162d90b36179c22fae082c5abd57c37e0c9ec13705437877f256e081d2f3adb8b0df2f54befa6b5da02b01dc5507058776b4729916ec4f5a9b52608da6ddd60cd63db2d790abd08fb4850e03b62eff4e1867a124ff4a3439e68ea0066bcbe1313252ff7376b557ce3aff6347d82a4a9ee142d05cc677617bfbf697f5deaca64a4c5812ce492864c29652648f74dc8352f7f4723de0d19c35090f0b490cb1f41af0f7d27eba21a80a38de88f1602db9910d6042cb14301059f8b431ce698a8d9fe156c35581ee8c1d2b019b9ba582eda43c46e1d4e178cf073679aaab6a59a964e18a6599211e6fe9c17cb82c3d2b6ad670bcd17e546ad386d3c95b08633c4d037d49094b5e28b5a144abd0e4b49f1747ac7bf20a2e7ea6b4e6469ba61530f54e4de36467ecc72bf1766d834d6ab0a9107b8a13051b30f7bcbafeab3ffc23c63151d4f481589b1d026cb7d6ea6269dfac11a732aa3d8256a2b71b7afe8e1db35516ad40f5c8432978ecce8f0b2741111a49e10177f485e4587cf6dcaef64de2e50f14a0047e8d5e4b09b2f88486ab089c2921d05c5ffdabbb2da3d2983b762ef2491722ad28037ace7be05cffd2e2aee196af226156fffb41319fd49d29c7786b3d3c8974f642540a279a37d1c738a7542f9ef12ebb9190b726fa1431d5a917f0da84651e1ed1e49296e85902a0d3a48aeeaa722746eef3458a9b6da57850bbd9f0827f2b809970dafbfae0035e317ab2e1f2c27ff6a8f554c2aa972093c0187e21e12c118ce6cd6276e885cb8fb472b1679d81f7ee9c2d05d810037cde9708be12b76618dbde8710837b24ef2a17493b4c6e60fae48662c314450f19c75c3aa8d23cdb8d8039cfc012d3c9db0f7cdf362ce23129178790d8811c11494bae78db60019b7f4037fa4d268fce6bce25b03b3176905d6785f1837f16c73ebb45488261b148e5525f77e994d3f8847166e3cafb93927d32074303f97f384281b7657dc1dbbce6739986ecb88b958c4b0f82dd76fc102db85272b2adc661566acf8cecd5a37be7c86201377da3fb27b59a6ddbfd1b12ecc8f86fe618ca7ddfb5e89e3e6dcd0ab9d05f97b2971f1dea6decdb8e5dc6a09ee9a50dbf98df76dc2f0b0cb83d70857080e0bff039494b05ebbe4dadc52b626f218baa4f93d2d80880c93321d6cc880dac2641bbf860e965aefabc5091caf2b4d3249d106e5a32a6d9dcc3f5b6d5605b5f29e0fe6019249b8be61e73ef1c207abef8f0de5777c2d788829f317aecedb26ec090ace43ca75260c9696058e5115747517324e8d91a20d609b9123839821c8b5b327ab9abc69c5c665c4bd8de930353c63e2a57fa5ce86c58d015f84667622e695045f59d44109e2695f50df010151590072226a725592f5cc4e71be8f2287da5866e27dbfc0ea2ff686d5244e0df62294b7551da83aa32dfb1e844f654d932e84e1ba78305b2e3d62c9bfa3cb06881c419cc67f0a13e09d7b7493347dcbdfd3af84f00054503ef8d2bc29e266292e33ab2450e2a9e5d02655ecac7ff7a91d143f7a362971e3fce4ea8f2392d6164a0d17ca0ae35ff61122933f4599d725024949473bfc179fe7f9aeb10fecb71c2d2a40ecb4ae1cb074e18438018f6f1525726fab5ade73c4026751f0a37afc2c9e3e0d5a54bddb38ba7c9ab5d61e9508446af4306b421442c0a574f8f81bc70718e7aea8efa3e48f1b0b99e1067a0d4bf638118e5ddbb5cba4036f1ee669ce2dd37de7bc1d8ee5ba3b84019adcb7d270f1f116c426d55021c6adc1866e024a2c9424366d5259d6b0a7cbab5333e4fea95803b9318812d9177c62d5d26d19e303f126ce51038605d112a5f136c56a67485b6dc24749e62db0dd1a4e2958906805d74d5a9399b450d6d01932d9e2e2418ab576080eedce812011abbe2cb9f67819efb4bde108ecc8ebf1b3d10e2e4561fd8adcef381b682e754ae4aa7594891d258bb47d048359aa4ca9758f34244e6e4c58a278adff0300319446a7d984fbb21f9121bc247fbefd6063c309d958c0eaa2b5dee1c2a6503bb12afd44ea70740e0c8462110f87dcf4b43b6d487148d05ef241751439cb441b357db7ae2219dcb025c0ea3f13cf50a286c83dad8ea629701d3d59059641d2b3a129a621dcddb9f821a0e84021e68e164a5d6878498b0da4e304e3d8a26a35fd1b541d85e3642c49081a5c7638338f388f14f8c1816c27487eefc278779ead704fd4472958086cd51c210c6768e7b756ef6ca978e9262f942c9fcf0507abeca8dba8913fa103a1862b851deb3ad4980fe48a27a32b7d084b085cc28c7b47b33818c31f215c0082a8131a45bc66154022fca07f9fbec3a2f953ff778f17dbb76570f075b0a37930c507ca568de8f5b775d2884d7db842656fbb0fb2daf57aa2d000ef01c3130deb432b6cba93514b6653f4560948d2bdb9683d221eeff2e3376ef65cd332109eaf03214e76a94bec589f6dc340d328c03cbc6140c6c9393c39c15ea57ae0c299ed761f68d50c8f1f5583477c0b22fe6eddb2690eca016f8c140cd730da5ac78a9f2223b886af8aa74f2ffaa3ee96b2a5a3fa9f8ce3675086043c7de7dafca01129a82f68358148efc49e0b268fce3bdf8d6796e33b2164ce9be3e8506b8777042830082c389c2e9ffabde64b99f1ed327c83d6084487ef75003ffbffa8f384d511630b10c944134a7f27b0039c03b18e354f2fa946c17edae236a6a4b60ddb193a4018f99765d7e83e228e2a639461bf41e0b0b47a7efc5cf0aeae9e5f6ced307b2809fb29576bc643436bf81082cc91562e46ca03586004bd9e61492ef62a8a438d1ed2aaaa984ea9046378e6e16272f7c5082ac5a61f42e11f884694d8bb61ec02250e6aa207950c0e9dba918aeabb959447e732ec672c88f75d47bcbfc189cc611e2e916364ecf79d3f1eaeed7f0df433bc50c3c02a63b72f1b45793f8c8cfdb21d3b9a28454bd97948613b877d3dc9cfd69130f070b2d2376300c4a1303dc5ee24076c0adfbd73e45a4a270f0484f56f7ad39023a4c57aec70a503646b89f5ef191b824963ec1501edcac3edf6c0729dc9dba7a62a3ff3aef21fbab70572b7210d9cf6afe09334329f236204c9830a65b03fb534f38d8aded437b008238ebd7302e805c41371f1266bdaaf95ce284b8e5aa532c4a23b09d8c485fa3d04a66d660995c8459ae231cefa318c0445ada5c1a752d805fc796326537de8007951b7da01f91daa9f50b14938d4ee1bc94e95bc7c98db2299592ecda1f26df375bbe40a0033959d2b1bae4fe99fc1192a0e3067e3b1ec2ac024e50a51bdcff735710eca2714ec3c964a9edb8166e8521d52eb269c2649c520c9dbce9e0f1c7d0e2f2e0b17b5ecce333dc6f6229f065b2f8dff845857f58a1193cb7edeeeb7c090bfd30e232fe66df34fcb61aadf233a346bda9061d128ae2cd06c25eb161382189eb1e92848d2ef16fa62ad8f01adf4cd437e15452228367e63feffd2832bda6cbdb42d078382caa6e469db47e4f2de3250d0ce4d829c8fcef92eac8e49989586ad05ab1ec676ff277032e77c03df654c77684b5ff8d99cac8adf95cf52ed545f70d30f02d58fb9b72db10135e570785e3c444c31a86e6683c13e25de1669337eb60a831724fdfea0c242a7684e881a9445434a8b004e0737a1849857cd5055ebd9e53f2b9f77ac6c6d6ade940c7d0da692e31526be610c47e51239017d62f5858d81330eb9977b3bc8aecab272b4e5368ea8cefd1dbdca495268aa37e9f555dee0fbd6208d2f217f2d8ea47b33f88a5bedbcf0c92ec2d6782ce92e04927cfc2fff29791bad3b44b984ec2b20b6e5e4177c62afcee50b8a12094dffff7e24ee4ea49a7a017f6253df2dffbddebf6a52681a5fcce61dff932691abcad4809168e8bfbd700eab738a0be934ff6a746e65f393d35c4b91c9408127488aaf565076bd3417851fb2986b570222452655e1df8d36961b61d83a859c721d3e2d5494ce359a382c002ce294655a9a110d75c21095665b76d39b52cc7139167906987bf7eb2bae18285ede734693690ba714608ea2c05cf51c3519834741850f9ec9612eaf3a77e01a8cab89a628c64866621ed720698ba89dc67142d5131608bbc32b8dce8f52040f36fb0f24cee64cac63225c59f0e3c3d48b1b4f57c60c5f20205df70e81b3f6011546ef9e9a29041bfc7e96dc9f60bc9c2da6610b504e35e9fe8a2986f7eb4f09f18e3d9491ace8d9b4e3b53bf3ceb2fc5fdf2d225bff6ad4b1a635216395381c4cab4abf95fa872160d83527e2e9b8a9253e72ce50b46e8e236b165ed1327402982f80a9a1b641fec34d718d8227d5df442e2495f51b68ee657741eaa259590193cb15f5c742ca5d537f0b445107d320f63a2e07ed336b1c27f8fef2d7463c0b5e6a2225e3739ea7370540024c609fe4da0333ac173faacb7ea8b0d0c0b7fa1d3003aba571bf41e49546d024f6e1e3d2f25760eb99a37f7ea3d29dba812d2f045a6d395f1fd7680b9db19df70781eb44393479495c21525c8fb1c1d8344cb81c6de68f7bb5cf8add4fa8edd9d5b0ef2973b07ee8bdc97247feb0ad0aecb7f411b369fd8ccc0786e2d2b735117609cc3af441f74c20ef7c86a123763068c62a302c094d1b2775451287216ecd90020817f65c5a8e10299b006799169c625c2017616d95125434c7856221a5e2777d768edd3f8664203b380f15cf21545e13281a798171667d10568c6386676f4d8fb1484f2d7926f45c19e72d55b45aeb547305e9755394435105494028fdb87d00d0b4829c77c613d851381962d4f2ea023e1b12a1e9400fb4a4f318543795720ed91ac450a9eacf24e2c29489f33527df26246bec9a0a7c6a3b1c76d22f6029036b38ca39905f87f337779f199862c631110ffd7ee869f5cf0baeeea9e9b8a074b36434be6c211f48bee747b010444c9b5c2a28ce1edcbe2604ac3d4f30e5926581b67a0ed6f59eeaf40ac63a08338ec77c08ce1bd3b0037f614ebcb225c3043a913ec81444cee3de7ce6bc6de74808394a1a86e8f7f19e38591fd3e2d521923bab5bd648643b8253ede3146199fa4b91a514e28879b329240228fe9cf686163c1807ac9cc2d9bc083ef90f537bd4dbad6d2d1e2db659e88cd97c26620372321f344178d8a9f96b622dca3b7ff0b34f34d627190e8946c19c0e65f4d8abb707b5490bf3a4370aa1bd6d152be87a5d3e25382da8e2e6846682513d1eeb08f4474f3612d13664d0ff9e8d9b471a5df965d25324beef0e6582144da0f05c310bad6b3d6c98169df5fd3c4d49981b2d159aaaa01de771a8f2cbbfb50737867c711f5f0389d8c3779af6262d1dd92dbd5a4b3f2f1516bbd4a9881fb493d858a41c4ed588cab675fdf798543d489edee64de8c7441710732fb0ed9d2f2cff60cae3e0062615ea0f8729c5e6d331c07a8d9bdd6c3e278019eb5d63bc62470e520afbc73b32afa2be723c863ae3f32816f09484e5b21646b52266186d8402abf75bc28ea054fceb94e743982f5cb4ac10cd845299891b2dff0ad2f845b232c84f3fde7adb516bd263ef65bcdcf53d6beef08af0dd3a2ed740520158fc2821502123151ebcea24e779052171652a8bf5fe3822a4550727443a2aa0cf0bdad8de81e43d9681d7750d14934bc7e131cc99bd353859e236167278b4237a38139c1ba767bc77cf5556351e62d5616d1ba71c964fb9b56f930f83775fca58ee1ac09b5d8b2d33404be402ecbfafde582245ce76150269783f0802d6e5760c0b8c93dfc7efd70de95cf5497e95fa482f0ccdf887cd80854468266e86005705eda6540d8e677c151c441dcd7d0d828eb321b34223d3f117e94d29e0da0b1c74cbfacd4d7f53714a3274b154589c857501a860ce75b0651f56c61a4ec3f6a44a7fcd41c185dcb33bbfcd260b95e51d3592de4441a7a7c453904a25c0c2836d43dba07199e20d90beb3ba294b46c8e7d601c64da1a7818986f4b8099edd63d23fe357a61bae8f15bbdbcf5c997d3cb54eb6df8e7e266886c9a35f29e72bfa386f072608fbe0bb8382dc26dd3cbb6d567e294e196152f9bd070abc18049bd19c40f614b7c048389d2ab809d2aea10ae1f458e5e99d203d5d59ac9b014c402845ad77a6d5092ecaeded657473f501bcbe07e16a1c76dd195163ca0d0589a021adc5e795b71792761e3adbdbde2cbb59100011e957bf199b7b38228c25439db36b3a9ee126d6a392a6ebed6619d7ef1c5479ee12448a1cf02185c4430c4568e57deeed2d85cd1f255964991cb8152f2d63680e0a2b9c222f4db49bb3304d8c6fdd7076681223108dfd23553b3151f14e9e82488d9f61a9c2ed5edbf309bd156d9a73afff41990625db984c47d5f2e630029c2e90e4a7479f850b5af32912ef32b12f5e3e97b511467f4900cf6844c6f60cdd17a79a5cf1b96e2e482f102ba5bd44ba62cf5c157d5a8a3ed7258a4caa6ccb2cbf645647c04d97d38aa11732bad3ff547139760bc7b2450f9dc7931ffb24d0a94bd5545581c73014e15c16638fc0215c8f010612a893137bd631d9eef4db227827cec0688d555b8279471eff23b1fd16a495dbf1d0d7998160f4c5068d823eb2a0ddb58fcb09ca8e89400fb5432d5c826f63768de570f6db99f03c0c47dfccfd09509ce1edac0250a7321c9910d7e5845654cb43e9562c31a0667d7d3c560bfe0d28d6a7260ea5962fed1aa7f67d9fd1c032fefdee10d0e0c34bd825fbccab8f5f3e9e11a588f0f5e6472629972fb7d358d0793aeab24736c17fdc1330fd2a45ca85c5708e2d0d172377131d8872ee66f249ed8b03e4b3af11ea922aacfefb7a62a647fbbe0ac233d7301bc39180c2df57b9d8d08bcbb47e971d9dc6eb3a8d38094bd4e60ec96c1677c207fe087d78d0b71e0372c25322ae2a6daf3d935dd06cbc450f405d50ac38f27a177ab52d31a5e4261c18a8155ad4adb82fdf02c0053e7360c17995fd55dd992b24283e340b5288528d2190cdea5acdf8b46fa0df576166cf78f5740985999601171261b8105bfa215d536c60d5cbd7e0c393ed23842004f0861c88f88056798a28942de5d2b23355ad7c0f10b9b16e3e26c5e82ffb2b60746ed798861f1e0e3c", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000f475a2798782a4befbde35b045f9fca800000000000000000000000000000000b5d68e70b88567b1352944061f30726611521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" }, "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000ab6871f5e0c604e230000000000000000000000000000000000000000000000063e0c5faa3697e97800000000000000000000000000000000000000000000000c4edda900b7425ccc0000000000000000000000000000000000000000000000000002befd5183896c000000000000000000000000000000000000000000000004d8f5da040d18947000000000000000000000000000000000000000000000000e372e41542c47b15600000000000000000000000000000000000000000000000eb6fd9729b1572bba00000000000000000000000000000000000000000000000000020aebcd8e4cd90000000000000000000000000000000000000000000000027d1ef6bdc1bdc6fb000000000000000000000000000000000000000000000003b63834a1a171bf2700000000000000000000000000000000000000000000000007d98d752da17665000000000000000000000000000000000000000000000000000218b2af27d6fd000000000000000000000000000000000000000000000005fc5b00c064fff6aa000000000000000000000000000000000000000000000005475acb7f772fc6a300000000000000000000000000000000000000000000000d5eaea21fb7f5474c00000000000000000000000000000000000000000000000000025c5ad7f49b8206f35cbb5b62e5b765b61ee0b49a1f8770bea9d70025c2700523a6cdeffad74e2d17c339a3922f7ef70421fafba18febc1023e82b90cd74104a98902574f6d1c226c706d93043c153de5bf841a134de7656959e544c04017eceb0069aa692b1b09d09bd7a13e5ed5ec3350da8de0eb3d4bbbb261473acd3a03e1f56acf424efe0c6de5bfd3bcd37d09cc02b16d1a369712519dc1e780047008bccad88b1517ab06359968bb78d1f165250eefedd6b57eab508d2953f5240a27b3c8f21fbfafb207bef508a58b51480594683a51f7ea0ab2210a377696def48a79ff7efbaa8e0823f41ad197d6b1ee7ee556998280123ec1b9aaae6dd280edf895997e038d1c1c25a9dc3e528367cd6ddf78257f8f4a4c8c13a20283316c3eb5d11cb539b99e5806618d8b39dba1be4c13bd33f165d43ef169b3b372eea8224a06ae43caa0d921207c4f8a36cdc97347efe815d7f3321ce22eb52b6258a38b98f20205e737b6f61ec9e8a86e5cb149f99b66f85d8a15b805fb61123bb0b5dec773cb3b297fe0ee07c1cdbd49dbcb2961b3969e1607c7518a382930487888e32d9bed12a4b7298a26da1f07cba3368953cf9f4a5c127d3fb11a2477adfeebc6be03f27dde2c51742d449697cacf031c884d94c35016ade6bbffa6a1ded069664af9617665c4ed7f09333f51d61f47a9365621869531df3ff6fdde13d7571858c89dad97081a5d032db062ed586adde96838ac6e2c39cb5d50c316062bf90f0b49fa7acd55e6ba1620d7097883c0c1432d691f8ff8fcd8c8075c631e9d6268bed9ca37f2e49bdcf215c9022b55733861a9e7bb732ad6df58cfd3446e7ec2067b2746836af4f7f89c06f25e697f3401aa322174f68a210066a5b63035baed84e1406dc44ec30e74d91129f27facb96b2eb96dbda62db646bbfaa784d1a5fba7dba608196df258f5020c01e07ebef101e89b91d5dec206ff84318ba1638547fe25aec6b87d674308840cae64a836af4ef9ea8cd2ab455927f23633e086b6f6b741485e76bef2b6cc032bb4e67d1a535f5a810fd3cc9899368676a27a90f311557be62e196f20ff52ef13f86ea9ba08fa7eaabb18c6b1afbb4f7771d605e4b1f2be6fe991394d0e2f0626490f444e9ea9fbd75efdb2a25d56667fae74512e2fc0acbd49afee2b2c23e72373c15067632bf4180a0cdc54e9c9394e7154b900a48eba42ba1f44b1d9fc72128f4b3bedaecb9e70e27ed92a46717fb202dac7e349a24736229c5c0836ea091cf2028b1a7eaa6a876513fa071249ea34cd4e1e27e33cd996b948907ebc60990d5930a69598cdf690b2c2c4a1ae184affe235d854ec81da06447e2738270b582081a0574605536f0f836c368de05a08b39fd2d65450c5c5d6d6cf1d87a7cd5a0a079aead0c9239382526011399682aca59d1ac68ee5c18aa4c3f61d13d2dc9f1940bbfceb517ddbe3d02b950661d24e7ed55d0550eb3689f421b3a2a9056451192059863b2f5bbf2fe06feab1df4d0b2165f90a3dbafeca86e3619716ccf0980bf1fe0721c73d5cfcefd66b41e50aa7eadabdd8c455f2779c3b3398132087261797ac8754b7044232941f4d74818e58ed96ec7f2caaf282f7a531758636d2020f7ad743013efcd97a076ead680b17dd244cb55039f07344e15a1a8e2f38870a29edba2439b0837a103e1a8c00e450006e6b589a654b8ccbefd5de0045c8e21c1545fa845e862ab714a464c0b6e00265beabcbaef383504fcf2c5f80cd7360b10d5c24f04fa84e22c6a8e81696a7d4debd55b2058c64ee4339f7e1acb1c8674f2ba9e03d761ec3bc086bf76ce20afa4cbf8328033c71e096a1b21e3e1bbb980c260b3e581b7f7afe02b1ea39b3b73031d0b96080204bffcbf3a04b43f5365bdb249ae301120746252ac7b763df1d20111dc3827f858fc8b12665c07364f9e6042b8adb4df253af298a3b669077f4aeac1aa8f6cc0b44af53c3c1fa4cc56a075d2857a42d3b05cc24d8e929608bb93e738fd680f4cd8af7e6506808ecef185b2b2ebdc44cb7a9ac2efa142c54b09cd054bda803a047c314060dc259796a51bea1238b6879fdf41ffc4269cbc9859c6220dd57373fadcfcda2548125a04d9447280a0696340db71c34a8cabeaf6636088c0331952ccce04a506c57ad047ad7bb9222907f98f62436ed0491d278b5ed7b273a27601258a9ff54c7ac8d3dbb68cf3f1749e0a4c950d439213e3bf0524945627736e7e8fe7f07029b26979c722849f62eedf78929c852c7bd06c10d5a69015ff5983519c3e0937bc3c825472e519547273409dd5643ecec7d1630979937792e5ed4b7cfaa110c7d87ddb2e600ae1cba2ba7b5464a14183ed799350e4d18c512d14b66266eedafd5d32e1141c543d6db248547031da4097158b87ecb7845a8f659c5c02b9ea2ac6f636f3e3d4015739223049f86005714294ac037d14a7ccdac80e89882751314baa8dda7bcb024504e2e27c6a101a3035ff6ef42b99d56c20c824d839f3d98e3131932867267d947c02bb2270046b020798f033e718352799c0cf9d26c86961efbf43562dc263b9d250e36aaa19d199f1d2789110fa14b42d0cd40a491ba99682f72fa9b301d08528b0cd55651c7fecb9fc18e4a725043f251aa9b69be5d31aaee787dc8771fbf31252a90ad86466fbc0eacd6a4cd0d6b94c53ab9fb4c6398b68c5077d5fda03d7e7a1d60824bd6a0859b7fd17c5e3ea922c212b33b1b36a7c5c060eecdec697ba38c0f968500d89f9543e48accf3511e6d0fbb18f089137a7db279c6abe326dd15532f7ce125605cbd676ab45449b2ac04d190420e48f1c0a029eda9944c2b651f681194348f35c5946265da9ae92238bb49a90f7a05e723b185bf8083ee7448ec9517320c5f10fd917b0375e3c88136b03d916220b42d3ef345c9617873212e1e6d1af9a175a5da49ca79114ab1bd3fa654bb9dd1cb9a287c2278f367cc7ef0e35d064982209f67c25194f6d480bd1af6da9c21e590bf77f8e556bc82b3f36629e51ba0bd9e8e8f2a59d7bbbc20be61ddd3d78efba879719d28c116762ad44f7a1c17212136d31493bc280107ecfc3e7f56a3155e6a8dc95ac459573523c5db8e9726948905e785fae860f81331108d539f7f1b8b4c914936d1a06976787a461aed10fcc5ee13a48ab03e06c2eb6d80964b9faad63932663a5a02cbd28b40f159d3191fed983fd4a451dfc47cf6bc11201e2d2faf3049e8ebfb3c7422ca072136f3294c96e6a0fd7348adc0080aa0cffa5ae817a64bf74e78406e6f12e2ce53b20f24795f8d35edf71c5de2ab175685fab6f9c73dc06620bda26f67208c9774b7e52526e6da846f50a867b53b0c964535f5e52c14958463d58d2417c74b2a25d8012d22f20b89f69d9cafc3744aa9a3435674cc2d23c1e68cce4216211907defcc128f00687ab857061bb9fc3fb5f4d2bc70d8652f2f9cf0808c69135eab8fe2b05192d7577511195d93abab820c9f30795df0b10c7f55de58c03d19139b28f5d90128f7041b76c81e6f1444d47642da90ad4e2903cac9481dd31dec5947d5feb56114e0d1865a7bf262de8132d41340ad3aa5e1938c8692b7d9e6af1633d74a3a21d0b65ee6e74bc543b648cfeb52beb7fc0e500afbeab9bdb8a425e43efc6e0ba03f2678dbb80015852281938260095069eaeb25fcbd581d8ebd64babae79e41d11efceb26339550bbba1e36ecc3c91f71ea9f1cd23c0ca5036758e74edf7858e14ad89c7f9c8f71c3cf32fe11c37f583c932bcef67203e999e45700d3a5816870afe8a87a72d9be4d639fa57feccb778e06dbba738460fd0f511c0ad977dd06206c54e18c4a57066e49e55e207d36107037ccc095ec6fa2feae81aaaa44b88370906d542c020f0d3b0e2d80843567501f13066782c96646ccfbe031283544962216425d07ad1796b75e2e9d402062083da2d671d32e1c34e3647bad0322067911656aa8439e56e08c30a1384877c1a32e69c3440d5a25f687c175239ee6cc75c04d11f5e379d21a7e8b32943f9772db2e168f4f1b7b087ee8b7119fd1f18aec2269e83608b1d071924f9f2dcbbfcd070bab6d7f5a447e3a3da752065677b64921849ccadc866212b4a74dfdfe4b8430bdb74f4a9f17a6a99495f0886b442625f2547a6f4500d0dd6f5416d1f40310b3fb8ca4a72861597ffa93f0f9c8e3955fa2bf139bd084522a6726933ffd9e47077365bcf03a3ee29f99edad41cce14458f0684554a86d665240423b236917bcdb313a210c1946d234dd1fe542e742668fd0d64b6ee71070db72111e430e1f27beaba2482951428015889eaad1482b4c2b3157c8eb633ce89bdb8cc75ec48b40e2c50fc5c46e897efa49a7e4b535b16dba729a619462465eef8f964af5497553815acb5545f74d5d677d2c0c1e339416b9c219e50a898ee88e1fb2f1db566e9696f253d79f68772a0aecc07896d2e49332c0f18fd89635440b49b7a80d33da8557759c2fb146fb125ae93087fc2b6749e6a0726f493c7ea11b9d67a1be2dbf04a50105d2b489c75a2efcad8658fb92521580df1b992af777cb0e3ba2c825243eb0e6bdf3d0a6978d1411104b59a46f0bf720a79728ba37648d5d60c06f92608a4dbf1c99fc3e449de7549a7e50762d0232c2b116994593bf466bb0dee85010662193d8ec2fd8a25ecdb2a7b34f7478aed831b3025e984f0ada44ea0333d2d72380d11ea5c1b5db3d182548eb261b77b13181f48514e3f03784963faaaafe860688661a8bbdce565ead9184b182a9802311d19069af44ad2e08496a94d229f869f5a2830c420d4e1bde78c230d8da4384b220c88dc1324a1b82f54e16647c431dd5011a1421c785e130013cfd882192d72f01374b6f2b7f6011bb2194cb772e337033fd01038f1d77810506f0f93aa4eae822649b30fff4c315bcd99b6cb7bc282d60e8aa24fa71bc754482ab58e0d721c3c2ec839f0f8b1c83585fe7ebc00368d3baa4a381440565216c8d2bd04fb96d32311649b8a7a3ca661a0cf53114a8fb32d33cad95318a6154178abc8c06440e21d22e5c70296141521743a8a37855d70b311927b84716de2f8fc092c91eb3a7a1f221f26aea2f88548f3af4b19fc475bcf49786adfc8d7e7349e1693548f512a221f381f9587f1fd3cf3cd069024a1170feafba8229cebb8923a7ab8fbd856196217b589aa128af06c775f25ed9b3aa9bc23803fa5965ee8b7794206585ab63ebc119c919779bcd9b1e024982c6fb199b1679c9af2da8f200348f68ec3b2a00c0f17a15318a649ae59bada674e3a149bb1d6431461411e2df3e2aa1958b722048b16a07dd414de7958c407c3eb8c760779358ec1e2439b58b8d00378bdd2e9e9450de9e777da0eebf79d558d029019a16d1f8c076fa5294863dda329ececee07020721d000c002b15cc3fd3cbf0f0fbc80d75c1bb53ae1b7a244fa507318c57e4c2d6fafb6038ee34355adf14f0363881911843ba3c655934688976f48ac38ca4e162f5b94463b38687726d27320a2d8c97ff9d8a79e81261babee9852ad0958c210d8ae1fe24253e9229b410ba1f11ab4cad40642dd7c818aff33430752c5f36205ef43d3378c9968b0b16a881404aa643c3d555f84b78446cb9bec133cbc10ad00b9207a883ec2d82bad1051a9c39453d1b63188f2c5c052643e597db86bdd1621806a9ef2c04e8cbd9fdaba392d0fc997cf5a49811bb4c8b68f104ccac280541ac123db803c502457797ba6ef0da3b43641d8406b78f1007c09560d45062a65259bc7636f785729b40176ebd0ca1c6f2ffdb9ef2915602c4a6275ac8fa7cbe71adcd397d6043297be672053abf8339f90c137a367ec7d6a782a51b8285f63721916d859797c32299e9413788fc27175800859f9bfc7d3939d4f6b5cc9777b2f198710723aaa5fb5f57450a5ceb88a674796d19ce70fab58e730240326a016922b06a453d8c9f93c4f6a13bb87c227a297ba159e626f5823114a76cd7b0f39410cc25f0afd48083c62151fbaa1853b27d7a17fa1b9e2fa28a284200bf12a6f5a2f550ca3ccff9b6327e4f7d9983a448cae3e7f9ca4e628df133a58dfe58d741b284a4e0b987dcb8dccd1f21874cd434280feb0e942f8f0e5ad8fed96b73bd26f024f4f697877b9a2164985542d2969f52a5b57c433eea4f07a445aa9e833a37f15dc67773cbb58f86cc70a1b5779b019ad98af907b00c0a1fec4492d26e38cd72085f1cba3e5e9c54abcd00c2108dd54aef936879809260609e0787ae4bea1f618a9df278efdd7f15dfe9247dc384fa4f971588fa356b4e67377e8fb6251408f2b82baac18f833a2875d3bc642a5838432ce511b38005fb7024bc01688ef3f670da2fd6a5794ae972da048893816c19549b73421e0d585751496ed222a7cfbca1d415320f214e7cee462d0689ffc31bd05f4c9a5ea3b1791bce9fcbf374d2d14004ca233a4b174ab409e968d6399606527b624923f381eb8c17b2805dfab7cca280308a2b5af4f1aab2d05e78198ee4536f2deebeafec0eebb33df157fdbbace2e747e434240c5cfe49275180859832b2d69923bf08c0d597d9112eeb73568c91fd8ea19778ae26aa6a872c78c89c957d2518154b689bab59b2ffe5bc04361ae131dd276df1a28b2e06a9df883220bcf76424cf289b94d5bd90056ca2a6828f210f1ff74decbe9a8961ee9c5963876aa8face86ca89e9b038fbc5b1ba5bc962c128107f963cd0d439c4fdb465eb7834c943f21c1f3d46a7f057b75b42aac8c472a5c5521abd8d5913f59e4798f9edd0bbe643ab68d2d5f2d4d7b7dba52dd5c8c01fc9c7bdbc80873c54c494f37b6f6fa4536f37b101a16a7da7ac943fe0f666304c7a6659b382bfb94977ea2f22e244f5224369d6025dabac1e08614146ee2501ddaefc72b74b56f7f72ee8d908bc52a354105655e85edb6dc768a83f3dfc0a317c66db9a34532d5f7356579f0741dacf256f0af84798ee17836c4a40fe4b5cc04c199f7039d38a4bc5bc7f2d99fba60a0fdca779b92f02ad5aaf76f1fdefb091381e6eb635e8634516faf93dc50ee8b484c07b917f28d75a657b00a68ba0b71015191755b4d819437281f7565299d9d5ab193750340c3436c4e1ec8af65c201101a107fb877fdcdbd711cc0e888f6a3918bbab8780e9651d6c8bc29d473a25a263d53cc7805cea42498373014e822341492cb0b5846f3651c4de21d6a02b449249f136615c3fa59efaab74793ee99dea1346e28a861afe2f931757642bd3ea028fac43df603f0eef3eb0387cd88048d715e733dc835545af0a970b030f6bcd40739b526796af9c7636fc1e0023beae25886eb1ec9fb69465f36cb8db0143282114a980ff547ceae0ae0328b0a2ff83894cc7efb1d8d3268e623e20257143ddf1690c3f89ba0cea9077e7448601c66a68816c5d7adb596e9df73a9e7d4f3b5151e8e6dba9f96d00681e644aa6cc33f2c88c76c3f204f9e0f4abe5667921cc5dd0a77e105a39ddd0f53525089e44c33468ab32cf7761380760d7c24966908f7291f3cb7b08e1addf167d9a5eb038f1f433e711c6e67adc478606f09c33400928b09658bcc54c224cdca2dae3dba6278e91f8a416474312cd9325be5c38d7f5d7e07f54faf13c87c1b95c0f7f21f9f879644988df52091918a8fb17defcd05035b2c01c5516e3c3a8cb16dbfc84ebf730cc91c289ae1d9a2247ef46867c4c3f8b214c7e231c7382212bcfd3bdfba84e5befd2d04ead43d97823562f066e832f1f31e830d7e9747675bde52ae766e1b6f1fd1868b78f7acd5e595aea09c9dfadbd42088b2218775b734708929cd68d07da0d0544236991c2b7cf3b1c8a535fc2a741e04a56884e7fec5c8b37e770b9ce037d6d9e5963f682452e8d786a57ea1ca4c11dafb71c383f1751378b133a730dff48da6d638942abb532cbcc712975bb260159681bc8d1480de29c8d1791ececbebef1dd47e7055855bd32ab876af784fad1175e04f583e93f827e3336d431ddf5f2a9347290732acb2c1e70b32aa39a1b61535d18522b8e335ee209aef0c81c3ba6c2902fb07d3d35c978a48c6242a5c0a0e5a2a7ab8189202cad4eef209e352db7c4548e813f6479e5ae2cdb3eaf32b601bf6758cf1a0678a0daf9120dc0fa7c4a30f5602f2ae2cd64d49f6cce7de341603a1e9661d5b7553dd96174da8b0763cb968592477ac917adc4dafb94d89522c29bcb39fadde0390e904c2638779567407ab30dabeb7f8beb0b49947dc9e8fe7291197bd5ebbf948c0326263c7cb67a772e3ffefa7d93deb6fe34c83db575bd913e74272a40222380713812899c64497ddd2f0ffa40bd9435ccd8dcf07812318062d0ce0a9887f24c623352f28434512a3f02497d08f70e2acd92c6f9c7db9c70ce84edc6d0b2e6a85061ecbbb6d524d77900e545869d585f02e66c11c5683262a89acb3a1d01fbc647be2e4ff6850475f86dbbf3a5533cddb04f9f759ed3f8c1a58e6111f8ab44f6eb753ce9b76b08ec2c1ca1e8e4a0b12f2468685e97514f80084f9ec72296c13c390174cb7058a1cb652524e152af41bdd9db80f4b26159d019c5915c863e55429c22b5df53f3c2c708d16dcddf9ca6144b3b12c102f6e4726abcb1e662e072858dd31ebcb6fec808ae3818a0e1529bd7f967ae9e4965f942953dd62008a7880ab0f25d1aba340e932e7ca5cfc654d05efae33677e86e73c1e16c255cadb04bfeb1f46d2a57eda2e9e0125c6a25e611177db2f81b52e4ac6270c29e8222702c6a2590dc3853e75a6dc26aad0dc20e13b71d273946564d9d7258d3a80b86cb1a2b655be37f8b0f78265f6bb866571b384a0792cd7ccd6f9f61514364c60b0d46d272ddf70cc26a62f1132e1edd6f617e815dadc8febd9a74e2e645c1d5c5e21b5c8747ef39578966822fb78829299d4ffe3e411f3d3bd5f092e06ad539341a69ee6302e31511830369f8bf22dd7204099269cf871cc0997191f1a2d248d13fcc11672953d9f5ace68497ee899bfb32b4b7bbeb46047e4ef821402516b83cb78caadfadd9fc6c12a398d99f1b878ec66b515e7483a9a0cbccb083041b2aac57d88f01d6d47b3ae84186a84792b52151c25f6587e21f8e6b07b1c0fc000b43896466542644603e14a650e16686d853e62db267e023569d5469c0809a8d114f1007b7827216e65fafee4b603e959641d11636efd348dbd46ed560624ef25c04f501716ef93cee02aad775bd9ee6919852491f3d29b942372e1882085117390b7aeab2e50367937357f59b9f3a24d1c14cc6497dbc0818fbed5041553c7bc5740d0e9aef2bf62243c1ca12f8567c9d3f39a40110770253767fa7c26a871d60e27b75d9e142b1162a4130343e788148124d712988045ef96d48f8920e88ff5c9f60d09d2f77ba9b403bf6c6d0fc0799048710792a96035c73a3e450c283036736b9628210c2497b080c5abc5372666768ca864d18a6f203fed35cf272622ddb9a3970878f471c80de989f90ebd8cea6120f8bd4c593763bdbc5a98058b8395cb3225c7060bb36acc2d22c0338edaac7d75befe39ac4206479c86fa237ab01ca34b0328eb6699049f29188e92e7354e69dd727f960802fefe99c3d80c097dd8728bf5d5825041d67e5ac9858d748bf30271341377176ee8bca796f60e5c92647f6871a1579155741c7b634c268068d9941eb9acd84569018e4de82811c568bf95b24c04cb3cda2a83b14307e668bb54db1a2569360d0caae00498f91e0494092814900d31c78e68f63f06cb09a782d204a10067a78a04801888f8b707e647e4d114ea7c1e7faba4176bd2977b7c1d0e97d6e3ed976c66930ea8d1ea278f3899dc95fad95147a22de703ff82865da597000525a1bd0656d82a84f5d2255bca0b9bef6038b61f0e92a5ba1395707daa095674120eec99fc8ac392eb5a178588131cc52f9e6d45d766712bb3eaf094693a5af7a4f746175bf7aecd09831d846931480245a862576fd3a006b07b1fd9c2db51db74543e275d9e7cc26f2d27452fd95e45041aaedc81d99067f7567db1fe9759e84b4fb2d1bd93bfefdce805a6cabc48654d9d38ebf014ecc33eab6dfe7fbe12b4007cb490b235982a55a611fdeb14cbd17ae02f6dbddc8b4ab0e8209312dfc80292b4c072401ff7336ce8055b3fba6b7058057342f5e55266f9bf87220d9c9ac9fe0853f3d4f4d019d829195d64b5e10165b80751e8ed7945c9a19a7960580b7f3a9d7a6065ce6a26fd3128a6e7e2d8d168db7ce213fc75b4315145a583fdcab1de9a71a41fda973ff1490721ae0f3ee28441b6a46876cebe7b6d692d551bd2746bef1fd28623c1af91c020c7c80ce66224b03e7b9106196f94b8e0c9b5ed67af49449aa45213b412de6e24c842e747291b84fa0553bd0fae951fdf6e70d9412ef0ba752166a5dcf09ecb079b9de21279373de98b9ca059e22817390f90680d8d338d7e35d0dc81751a500d4bd7104c7cc8e52af19d74caa3d6928ce9b0bca085889fbf59a86959c2dcdb13df2f80658bacae156b7f1f4865db24b07fbb6b27942099f9d61e57ae43c3c723bfd52acc8ffaf4fd9cb1147322dc31f7ec5671c5f0ca5c5155ed6d9f2e2413248241722317c262c5bc54875d886f0d90780674e073c5f7f921a6666e3048cd16d5814372e0236fe69a63a4f7118d7eb75bc4465dd01181c9a197ea72f8307c2841f6397a9baab5a97fb04e109006d8127d604f0b7d6736162c40bd8b1c09221e0922d2139987796499a7fb097c9d0a4671b898532936cccadae32376c93dfb059ad2915af4a690d40a516c72d74ce5d4ab9e752f58873ea88227546e021f701702aac2d40191a410dea52f40a4bb996be8002e1c57413e0dea9914098f8a880a8c387b2e3309533a4c07aa51444956ed8fd2a314017814af55da77e40ff7051641fde9c392981a5e7d779529be3e0be0f6890bf19b24f53ebdbde0faf9d69f10e9fe5c469cd99083a9c572eb19b940f9b0e8094a75fc98a7131f2c923408620f3d931493d5a7f759cc6a21edbe9dbe770a109672574d8bf6aacdd48623e833296a76845bc8dc66f9e0bf4d05b889bee67c4c2852750c30d06903783da6ce172c02a8f8653d8211beecc345dc19b6f7cbcc803d66ae2128ba1ba055f761ca3a1aad5843b15c067428741c224242e5c518ffddc45193b3a5ecc922cb13c0d0a72d4130a3f278c1f463f66423e4710027d0b319048abd0a6fbb66c195b9d1452e2e0c7c0e8374b22294a2129630e2cc3dea0b98c2c13758345954fa86eab6ab710a512893425e0268e10b34ee3e2e6a4fb03040687698c97982e30bc2a8a2b2fd2248da56fa8f2105609a497670832678fbaf89a7935cf26768c2c03f443fcc9012eb0427bcaf487189c20e24956f34d23792ad3f62596d3767ac07ae38f3fd4b11324800ac6e2d023729dd5df77dc4e0cd81d0ba8e10e90a266711e69d577bd406b1e218645fa2552bf747d0d1a87fd66c7644155f6f5cba9115d114d7631cec08770d53cead9762e9eb24399c4f97d98fb3c365d158799be00cccd3d20c3b090b431b0c2f7b7c2ef333ec898e469fa101f1adf6f73815f3b09bbdad8349034f1f90f27a8046dca9070e149c1c23150321e612756a0c2c7942d7e30059be4908050ad08455affab49f27dd02ec5169cd976f4567515baecfbadbe97c382d3f1826774b8c096b6862b740081c523d47bd0b9639994b4ec95ab12845086dd5fd482538707df831ef734170cf55d329490aa2e0e49643922241739793a8c75e7386283a026efc405fe0598677d246226a3ece5958e0d48de439e944a05fc643d0641fff61f83de97b677bf4840a977ef5625bc385b7591755fe89ee851ea5d15ebf2655014e914e878ae7c1f8282c6189ecdcfd5110178786f7faa3b05de46b43cf17e113d1492df5303ceba990b322420dc3220d8d4415f89381df54d5ead445821656c02d97585ac6f793d80d33115e53d6415f05f0c5d90c3d77c3d140226be02a3ee5564c70975456bf79d426c8c33efdf706cd178ac3d687e28e4d2204547d0fd5af73db10f93e0b6c523b4ef081426af896697aad46e292ae0c5fab03ec3d0d8dbfff9127ba2e3c4eb3e7a55d92890674bd6132382df800faf6b8b407cb5e2f34f902cc48285bc99e308f2acf715a3b8f884aa21e144ea9fe2a36eadfba592775d95bfd62c6d71c3089734d67d08cb00dd85d23185796cd641089a93e19b12beac08125be0a38ea27cef99c4cdbeddb4b278caa2fb8d47e2745d5c09e83222c0efa0932bba4cb4546e28667c714e5b2b974eabe47fb29b3f78cd5b43742f101e475b51ad373772e93166104a79b4efb175dac3b442b52b9504c88304419102b169b8bacf4d47e97ab2c69e9f5654f5a0cb6f55d8fea831cb665e93bda050b2ad2bee6223c4f9377be451cb1bb4d7d5445b1eb9b58549ec07694f34940216118b0e8add0f6d3ff6199052ea7668d13954c7caa2fe6af1663263c9a6210ae0b01981a9f0fc8d9d2eeb8d92572ad287146f3bf0c20d23de8d08c40c78dad3d061c8102981be8ed68bd6f3b7bf768c40b86a06b6da762153c82d045133d3976a50aa6637a1eec72f4f886a043ee57a4e6ecf52067f7f1dd512d0829b36989687320fec9da648b374ad4e0247d1fa97700c286b7e1103f696941674e6f3a6f9ad4223aab447b6b691951dcceffdcb30f047653433c4f8a1abeab791b5ac76b18502575e60cd921ee2346e0ede35620fb30907dfbfa218913647ad415c9a48ff09305f729f2bf156c6478011766cb094981c086bab98c384f67a7dc579b811ba5c11f607412b5d8cce667aade35ed4efee23bb261c4f278e09b257d502bc61ae48712daae64de5ae2bb6d679b43fd9669b02023aa3eb4d01d10c4603bed9416d06f16bc522446aa35b063e490767fcc90a7bf197281c5c295c1e0415dc27d1e29df06d659418c6c2d19a198517016a3c76ce82fab8d540e656ec7170093c80446c42bb2ab499e9eeed746759a7814701b945e602522926c49aef0d217ba7f3ec0ac1495c4281a7b4a9e1464ce6c97788d617101c912e7918be268957e80dd37fa1c1daef5d845aae5ae93f3d73e13e95b2f974ec218a863270fb89a30f0a81f70f815e9e11d54842d2997758695e9e919c49ee9568970dd985f95ad792c4de69c602ca2a090660e70cff6f75b308d6b58fd3d41bf6c2e1ab5210914a167f161efb208999866e918d0ac040afd7f314e279aea6a9503c0fe69b94c0596ec7204937c282c824f63fa7641a2732762083c6fd099f8538f33fabde31df0ff298e4d691403845eb37769b4a7e325f7b4b90dc138807c7bf8c94b3308c8131021dbc702ec1b0d2773d0d393b5ee72fb1b8c46bf5a2d96e008f0a27bd052c6ce663df466bc10b34b779436e65c9da5839de588d9356cf64f721915fb81cf1107e5861a4fe51ceb29793e3bfd641066d690ab8a27bcd93604040376f886a2bddf62b99aa21012f03a25b1808210494fbfb7c5a6e98c9b0183db9d9366514708d725bd15d6471c37dd3530ee3ab502f0bfaf4639574de4088216596534660c852c04f73e77270a40fd0514c085057ceb30638d46641a2429e8aeb06b89a01c7a19bb9287955c17d5b7abe02aecf3f5e497b8ce13ff4402e42e044c3d270c04db487f5d00f04a0697b0af7a5e27a2a7c04d5bbd73c3e91e2ba020b861cf43676ed66ec9c18f4129399d25127fd3a40f696bb6dbccf3829d8babd3a13e6aec6f0e6bfa632bb90b26c35be6ad3e9fea820c0327735cc17a5244802c9466dd98739a06bb34e742dd044554adfcdbbe8d300cbc39c983ac1a9222513ebf33bd79f429c47d58beece61dd0536dfb22b4ee0b661069eeadf37d8c1456b05f3e19ee3e058c732c8d1ba62a6a39994da45c80490e4a53575e6a739a01d8817ced669ef6dee1dedb34984b2688cd26b1fd6e4ed748d8dba93b6ee4a21493066c1a10f7fa44f50a02206b342a5e15c483ce678c3f87fa37ae6701c4105ea18957e330b13106f6d62f55dd9e05f880741b3c98407fa02b8b956a0b42279f01abf85630fbbe613129d4a710020602b5ce04f6a3eb5c329dc34aa10af6d222c111845bb6016b2cd69f48e4edde14106fe41edd0cc43ad8800ca68c5aa644bdc46f64654b3fa561e10dc84c4612059e79b06da8e2bfb580c268e0c94f02625b1feac968586930f5fa9daa1d52e2166bc02b21f5b5a51eac5219ca1c42b6f563ed338189e34fa6c3e8d5922ca880062f1a75d8d73e3cce28ccd91097acfac61ce220ced87115497eda778f752709011be2b3aec8c33879c1b7d9f041cfea2e9f10ec121a0887e278396eb143953a261cc5d74697fb31f3b525f2129e9dce7542e906372204e07e6d68f59f2bfb9d194e059fc2816cc7b5faab72efde88c1daafb9dce629acf1bdcf7516b7551a8f0d427ae711881bb9dd56f6ddf46d96644fd2e7bd577830779ac1e706b6dc298e0b89cccbea03d22a857589fe4895f822cd5c5fd1634ffe6ef44b7dca09e57cf30126afd120fbdc1cde39aa695bdce2ca770926968c345e801bac05c163e4bbc4279d02daac4bb1a306be1941d646c5e93050adca4277432645be6a4ff50ddf5805d1070e4775d2d6e006453d6b67e718f8289e49ce80b841212e613e505452a827677548ec16fc965456db4e86d45b4d59be76f01e4ba348015b12e75937bf20", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0101cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db70000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x000000000000000000000000000000000000000000000005313d9a6e542b9b7e0000000000000000000000000000000000000000000000013903483c243ff982000000000000000000000000000000000000000000000000f652d9273b7756cb00000000000000000000000000000000000000000000000000027f630846016e0000000000000000000000000000000000000000000000065d1e5b1c24ed4fe80000000000000000000000000000000000000000000000043ee31d5f86ab9338000000000000000000000000000000000000000000000007905d0c87aec5be530000000000000000000000000000000000000000000000000002a10ac0b741cc000000000000000000000000000000000000000000000000c7b51a204d90654400000000000000000000000000000000000000000000000e008b3a4546cb0029000000000000000000000000000000000000000000000000cdbddfffc47364a700000000000000000000000000000000000000000000000000017e0e4d1ea8f200000000000000000000000000000000000000000000000f22ab1a2f5805860e00000000000000000000000000000000000000000000000590f6e2873666cf4200000000000000000000000000000000000000000000000eac6afb5d8d6b5f680000000000000000000000000000000000000000000000000002cec631c36c46032851a9dfc62b5a3386657554763bc9a25609f33ef55cb756f31505ce36e3db0a05b1409e30e6882fde08711fddd33f3a44f63da1deadbf7927d51d3365bc6f093d95d7862c4a0af8261b8bb460c50a13d0f1912f0f74eba2e0ba2aacb5acfd093e3ec1ad390f2a5ffc1edca1cba82de741b80f242560d646492a9dcd4010a402a77a5ab41d7eafe3d77f45358dc9f9606ad20a1eacc2a61cd1dd938d7193aa13930f3869b94dac758e78c545419a33fbb9a40a82673408ee7740e3a520e4cc01d958176942b6e13bc5a3f5f43bc9d14d60de8ecb191de20d42101a085305361c09bef2e2a18b396466c452ac37cd484de8bb99477f3c4498fd618ab8650a1511810f77ae32802a556df4e238f85dcb488a8949c92f1f17c9da333a39b46f7e0490d4a2682047aed8aaa555c1442aff40e17d150662232cb1f5497e9d9f75631b0570752d219754db0e7b38a8b0d900dd5eca242d745248ed72a8a78eeab691073f1f16d0f47fcfd81fa73b5b025f56a13d05da716c38c47450adc7bc6bc3e60956e58c6e5ac4e21c191f15788a58c3b9d3a4b2504028b9677abf113bb9713c0f5e556ebc4df8cc405f439d15f2e52f81ba346d5179ed44b3d5f96197f77fa021ea6893e7d50abcf176a3fe4566c19a86f25d3ae0ec886334991b36112a8b011fa0c797ebbc32a6f723304b35ef468f9aae7150ea4b8e9e77ddb1088c30e95722ccf074798477ac0073d3489aa144112f2a486b964a5ce4990487446b9980f4261268be768521cdb8db5c9d1266187af6b07048cfa2716a074a184b7fdcecdd15eaf1989dc2a067241e737e37bf4bbfe3b197f356aa5d7ec836563a147a5f031836aa1407efd201a899b782e88d1ee01439927c4d975bc0dad5905e5a943dc32631ddfd067e1391444c1aac4b17f7b0c412202687a5791c633dcd9ce1a5959f1b22e74e7e0ee123b765c3f8f4dcc838ca0668c2b760387bd583b0ee27bc72ab2e2fc3e85e45577cf5efe5fdf3dd75f23773bc720bfff9a22b4bf328c69e804509d2a5c5658290efed9e2132189f24d644c64ac69b5b6de0f8e2200918df08aa1c47feb63f57e3c01a577f13d1cf5074100a7333794ef2c30690f329837b80460e018af6c534ef2a32a489affdfc92ef782701dad0d8e4ee12094fca23e0613713d5a51ea31f6607fda990251b11427ef851117f277108eb658d2e439a47c19f204083a535a2a10bd01423a13b1dfc9e3d3ac5411ffcc863bb6ff8dc4abe59891a9a20872821232ae50dcc46d0d0669de09f69844b506bbfb8b706ed1eecf0fb10a3f79784ec36f7f79ef7240f448a97acf8ffb80ce4af683c5a5dd95607d454015f64b5a5940339083d5e4ae358b2d24d0af748b51c65cfd06e95043e74d669214c2f698b2fef5147420e7e3c0d21dc3fe5f3f96ffff1c022962c5b51eecc9a0420cb4fcf1da089596e06175e9708dc70e66ec6b61342054186e3d681b6ee2a1c3b72a5ef49f5f5631ff307ec9a01f5113096219edfa78ea5e5c3c1d0c7398519654c0f0e33d4b3ccc5a30138a672087584a448997817fa6b682125c197c7a00f69b8f94f6a1766f19d2f618bdaa3a6d393e211fbf085115184a6b29290ec4529624bd29e01730ad78c71881d157eb407d191310d3eab86732b0d7bee43e9801d020395d413c7eb94df2f0a228538de50b701674cec415a6f17e45ead136dc310df9abf4c32e790dd45dd999fb10ccbfc083c17ca40b7d15d404b78b1c9cfab1624ab6b69aa63f47bc816de70b4b841e1db7c1334d56171143ce06ee3f8ac6806831e6f8dc9687d7e02eea8bf02d5879fab57db7e7db839b60212fa840e92f52fb8788ec87b94f6f42059e8ebab894618e3dd8c7898642e1ae539bb97ff3dc810139b3596daf0513ab41747423f4dcbaf8bb067e52183192c2f9707a42955e41f675dd80d236e500ea507e0cf020e8dd63871560b83017e49b39ec085c8fe470b16b25135ad3b01518546ee240eb86d777015a51e54a3bef499cab6282c48f72e0173e779011b3d66a2efc61a882677f8271f700594d418eb06d3c56b2223ba289afc3822406eeb62a4b4dc070d3071582c1a803dcd834f5e0f3529d9d79a422d0d6edd2cd11d806f194b4e71ce3066e02a142a3a85edf93ddb1aca6ffd27a718ccb4bc77a275f4538e77c479e448c1624c14d19ff707a448f659ce39acea572f3cab520fe49079887131808623c163e90906e7ed8821e62f3c0a53679c8a37199fc5028c4c0c4fa687ab1a7714b2c5a2211a9a7417a0ec1affc4c951ff38c309cc8b3d407ec398f587e05963c19315a9839b8cf4e1f4c15c4242dd4636e574041306832a9f5c8312eb039a5bf47667edcab0a5bdf459fb3c662bac7f16c71607a5f433bc684caab8b460e92b48410e62fb2f37ceb48642c45e6b515b7a17d01608601d90544628cadc00a4ca65a85b92b82bdd05fef7edfbb605c2fcb8c02f0ccc1669e3dad8b3a9d9473961c1b3441b7b4976e33f94c3f0ad47c1479937bb1be501ba3d7ceee0db11ec5333a589692f7effdfcd7611294e455f22c5ad0f9d13b2f2141100b017ef1474b21edaafea1fc1fb4e88fcdc7c28f6d82069e8d7c41e0e977dd5fd266ab3537b6cb92f621605dfad56f51bbb60cae4550e1a888f9c1493ab4be927b692643d83c0e7550fa85aebdfac97bfc90f965a14e8cc9528062ff0a443c9f5fd1746f19bdd5b6b64457f61b06534689bf551f16a85a14436a6010a702c41bb1e0a6c2be5a38b75afc82006930287135c11e26f547bf7c58f131190097012682ba2a52106334d8968736bb338941b866c16a8753f7a533e215e22fbe7147d0612282fbfba91903fe00b4bcbb22730c795f325162d6bda74757e2cb44471b703e1883cbdd8697418b7eb070d497bee39b0dbd302796f71b560ff19cccf4c7374f196851f26786251c874dd3c9b37c8980c518e1220d59112132c30076fe67cc1467250d011b3fe659ff4b936b5c776b21d5e8d59018c31bca8c62feb6f10c683649cbdf979dd0d114604b627e830b84573f61b5b540d65c17d802b6f539a24fc894fad4752908d7bba4d35e357c0ead16a73328557b8675a2a3214cf4a332021c9a3eef6619a5107c13fdac4a2abdaf7d73d90b3694c3ef8d28528fdf338401b8379d044031ca7c841130f8885b60e557cb3ab99d3d7d1e868601dea1979b30754602912ff0f2ca0fd1ebcc4ca3889011ed814fc9fc6adde78fe1c11dc2d135fc5d12c7db4930eb2344f23181392fae51025c8f852b57d0dcbf9162c748ed56ff9ed17e51fba6ae658627ac81434801bf4aaf9b9c30d2cb4745408c68c9cee327fa2c3a914a76ff530d35b5455a2dd54428924defdb4db6a2f6b1e3e7ce480ad10b16fcde57c3655e4d793cd1f668cdfa752389cfa140aa0085e1d147e9a1bce8a9463e5c07c5f45e96e2b9d9b07c5bdff11ac093342dc1ebc2204edfc258fb3bb960b667a51031c776b1778608b80487798ef2d84e0db0f6e4e141c6fb0a8bc3b7135ba6d0d6d8836fb252d9c2f134e3e463bfbe9c63df0cad905422868774c9da5b37cc9aa725bf0519e8bd4ab8512d84b8b1819de157218e4108538080b0073b4976aa65c7d1591099a55943a8f695cae7fbf48ffad0128f902b18286fc8c2a8a3d4f3ff4ab57885c422adac648d5caa7a206a7947b1405d6023501dc48bea28fc7d71a40ed12eac227fe6b1d2911dcfdadc7e0ceaeacdcf1115c04bd06ae76d4be2cb6fba63ba55fce10015d98d91c08ed62a8b2baafa6e80bdcd637ffb9b0a591ed4fae1fef90350abcf183a52797af4c9f68990c1baa02175980c426b36aec67c01b40c434c907f828e162b48d54a90be5591dadd12676028c2aa67d120845380f6e34a3dc3089a809bdb9952681b720cd8f4c9b433e5c0ed724dfb2b1d51ac931e23b9b175bc2d86c931ed0404efc902a524b2a9e8de22cd36a8f2d393067359dc3668c940cfa463fa26d2d1282a5e478fc785a387fd20e3c90b4a0ea0524b8c98766e4a91752cb0ac4fc26d4ddc0103f927e5dac3a4a124a134b5046aa36a21046068e677e4647f1f606cba1275c6a33dbdb58c8f78500ea6f64b70219437f56e73ebcf8b9869534cfad042cbced12e29af5a184b5b310d7619382aed0adf87a9a77c3f6d0b684bb89e527d48fd0b30f6181c167e32b1233fc74536b1abe9e2ec4b8211bed9770dda095df93c96bf9f69c101611740e1bc969c001bc9ea5485d64b29894c16f9210eb37190dedaea922209cb15302bc26ffd078aff907be66328d6033ce24be8b932db43b2dcebee85ed4d35e32f2d900c2b086f3c6afcb9d6157c937be026532fa062bb9d8a324ad1c767bb48e93df27ae8a3c5713328a26df36868c72ec6b7343fae644779e861c99612ae1cacee60601bc807dd21674f54282e27f923c5dd3f9f032c60128ed051208210973e3ac059a6cc964e2e21bc07a7c8dcd5648c99fa16134625f12afc450febd3157bff300ff3cfe1390b922e2224bd6c5a072e7cf1baeb3ea670dfb4b7ed87bd935fe2d0ab01c5c02b437a8c1a325821fb1397d28184d085c217218ce9772a15504ebe62845bbd18102f444b7f42056656ac9414a9f0beab0bf7576a832699189b3f52a129b7b1093c80ed82ae46ed1654970154fdbf10afd1b20395ee2982a25f65a8a0355c507e9bf351a1d9ab88140cfb63ef0e31bc4ff85100d2d64b8fea8153f8f03ce756874b7dee3e33db736d3014a52eeb4ba84f035ccabc22fdd930d015fc101b9a24e5e9db4b182b82096d17f7b39c4419a2e30003732ee53a2a961c4d1ee1e7df59284a41368ad005b8be0f5720f914bbc82f5e814a2e6d79e730e96653020b653d5eb54c93bc2dec97c0a29b62f3483b8c54112c31b9a17e6f9bed56f361d1296b42bea1371c13fe380d4bf7b7cebf3de5994f900f778e8c3a57e8aa6fb2ff731444d8b174dfea3f6fd8c8ca8105eca56b8d23027b15f04baab984ef49e2f13acad5025d8d1b15e2e64fc0a424bc589b2b5e1db37855bff6eb56a73db33031708cdbaf9c669cd7b650db18d2975f2fb55aa50b455f946f573d64b068d68228275d24daf2fc2634b9963f4888a89222199b1617d337ceaca57592d7253461c5bcaedcc413b455aa91785bc842c533d705632454d571583e7526a8cddca2316c55131317730a98ebd450ccc06d7280fe9448bbf865c73d79b20a40d0fa6d312f28950279d79ae13503f1c87c4eb4081300aa2ec70b6aebd36f3a911cf96b32dfd80e60b3e1e434347bbba00b797102eb56116bba3d7beac36a6d75b3670eb271c53c9c0806db8d51122a55f7bd660546a9bdce2babe09e62387bbf362020d1fbca41ba60c7a6ea9e31f5888293eba6610c9d03d86eb3322b37e5a9c1a17722acb06be54c8ddcd5d4b5ee8c950a19aac715fc02454f8508cb9ab4629f5647e0e9278290b3a0331ecf4a6a4dc28ff9af971e3477c1f141e587e6ae88e14e088235aa1590c55581df682e15f236d54bd804b50d91dcb0c4979d29a32642044d0080bb9e42d2e20180083e4d80c200358074dc749e03c06fdb3f16e8afb29843f1ac767a8436915faa3cfb72a4e215c1507254cbc8d3cd1e85faf93eacc407af20e4aa4dba393c436361a04397a3e92dbd609442471acc7ba6ec5ca3d8c70985125c5256898ac7a525f33019072748ef8ec24a4f75c9d4e8c3c20f70e863071d72ebc98bdae976998c290b16ea2b86187019f6c367bcaeedcfac53b54ce49fa96082e6e6f27fa07097659f90b79e38bdeeb24ec718cbfc5567937e15867902ce81576b44b9766484315c6ca56b6a5e1776f791c37473719e0fffd72149d9c2b3819cac4c62627a3cef8c719b0115b37a5d0ce55e0a0cbd1df5cd05d9af4afa26c119a52bdb434284430f43cdee3c95ffa79aa15caa494c7c72c730d72084c17221eba17e30227c699f53c0e64c439cfc61f397450ce5c1adb973f1f96a59a677427647beafd478e15081391afae06d5f95634062600695209751465d716d4ba052c2aa975f1dcbfb41eba65d44a7e07ce96c7934d9c461cf343b9958f40be55621badc077e730d30e40f59297f148455bde7c36300eb70a2ed7283e525d6a50812e5dabea9ac4c981731fbc314fa9c54c8a133131d2b4ab4cfa96c5efc31aa5ab0fa2e1d87bf6eea08d17099bcf1831e49c99902957cd14811a58f1281a5edb560308ddda5de1767163db2ac5c77e02a910a34d3159542f63c0d7fe1bb0005bff2b55c7797fa2d51e5e0a42a3dbad55be2690827c9bf126ab3015fc01b953a85b086967b1bb9d73af5a8abbe120c01bf8106f57ef1ff75d175c4f265ba7db8ec30e603bb046abf3d48b1436f3854db422c9cb232b92427bf121304d7e4bd9cf9618cace3a2785ee002320b8bb0c2d0d3a1d7b44e9636e9638207e3068411aa5a4226eda54e1e2e2cd72de7b3af6abd4a874c04f834f8a6b7563a1457b74eed13f089978d3bfc7e75282edbe84a65cc4d3ba4b2a40068f45da4fde51c250a61a842e3ecd39064275b66223645a0721701baf9b71d101dbebc2f605881cc9a327040f5557d835211325c034a4b41ea58e10902dab630ef06fcb777c601a28e7a1e91f782f2b7f6774104aaa7e77e720c605504d2a623cce19419e01b2749c2fa88722c5fc5faaa7897e91b598a9979577506dcf54e1aedc9ea33fdcc85d76d07c950f467c13320baa0a4c8ab1b2d0523d45ec9167617ae703ce6148dda0602977420a66fda256a321a75cb4a85ae296bf5899f00ef3146e1ca9e2d47d780faa5d822d6c4e1f29cf4380a0f80594032967cb90f300607c63a4e8e78b3075f61d4eb20a6055d11426fc4d8c01fb31b9fc47e6acbf25b4353eef352f642ab1a7f4ed8e0d24e0006d6019dd6d6e35d53c2c08d73bcfe6a138549d6a35fabb911a247d860d18c75fb954e1ff05d39603748b2b2eb67a70440a2043380ee66d028686267c0a982fd7b8e1080e23b0049c40f49e6b4a2574a3260c7fe9b03d3a486e27905b06a2bdfb9db75c5c005df6cdc95a353c88d99e9993e0fd7bd3652ebaca1da2e121abe5d833ad18457216aa39d94d26dd0ee370e8f9d74ebfdfde6b077f317bf607e748f8cd930a9e23acc1dd4dfd353e3cf1ad59efb65b969933a6f2214d347f003fd18c7b54d22f392a9ab1e228600843f5beb630736308f0922473bd712a2f021bd0980f9c15841804d33f7e1611fd3e50bdbd0535632222610af3a10c93c8040cae78f0faa973ffcc633f2420ac5af157288c59c9cae2815627017ae2cb290e625a6fecdf1747aa56ffb74a28b618654cbfa9b34fd5da3dc75a20ccdb20ca2d2c490dd0e19a68e7d7246fee9aeb239f0e983762acb98b89a0a206d5c8db3e26e2f1eeec9290be1ebf3078709f2f1c6573c07eee4f67eaf8aed4fab0c9bc1f252a496548157fbd3073fac78985de108ecea7c014b70101aa0a820ef520c4d9274c5708e35def891f8fdd8d911a78400db021e4d1d991a2b6e89d580200b92510f7c1bcfbdabb44081e35aae69936f1386e278220603c34385c0b2eee0ff6d709c751d6678181eecf010c35ac5d976a5bae5a45c6281489db801061d13c521e14d9a5ae5dfa502cd4f469e996e22a67e9b9d26fd05a2590e551fe0967c5a36d2dc181947d6bd4ceb14344b08ddd82876f6c9a1c81458767892e10d9befc8f5203aecec36a058a460c92333f96050a459d9dc67e297fa884b500f18b25d7e2d7001c2ff2088f5a0e009cbcb5799e9f76bb26030294cfa941069b908a746007d112dea7b8822219d16260e8bd94bcd6caeeafbc577e6e91d5e3cd214335b12364286261f1ef830b57805b05336e64e4309891fd910901236abffda2a40b261d6e2d5dd598aaf8a403272074f58006aa6063e826e80710c8bdb33e3ace794460b624e5d92f16711c0d476b9fd47a44937762d349782ee891245ef7c1467e761e972bdd93616cda73439cd15fac16c91b7159eaf77ed61999696b1c51a3c95b830b26c3b63ec1d5ca06932fd8c4c6d315ca4dbb7a5dc33b7d0077739f8cf0b4300526c11ae401cff33cfdf300b319d25f6268fd1c646acc15f24c600971aeb4b0b71d7fd0db6a5b55a80640d08fd687756bd0861f0582a91ba67c4b68136b93ad410bf9d6fddbce8d1ed7620df43a06b0c924da7fc0c81f340518c0bdf29dae73c600c5e616935810b62ea41fecfe3e9b384d6854c5c3d16248cf10441d22354ef211d35afb0eef9009ec93cc0ecfe4dd122488342d5e5bda730e75fec2338826f507a7cade523fd6fd0f2550b32e090e147ccd32dbb2776fbda83fdb13399f60ea183e73fba3d143ea64137fdef5d92ef69990ceedd9e8eb7415479bbda31e29c5083115cbb8166306e7540b94a97bdd8bd801a14cb439175018d7b0e18f50a0441266ed58f2d80f443c804b9aca5c3718c712ea5dbfd695e9ce3b342bcf3855140c897ba837ff675271d7b54cc0463eae14577aa26b4722267b0e579a5398692207414bd26d362d504eb65ce8b5ca0f1d606236744e2b7446b1ce7f93f6a8ae6f1f41fed8b6a2be3fb85d0f1044cbaa3a093d5e1bfb965ea3c0d1d5b5ef28e2542b0e376b551b02c55594f61aa992c1b25bcbe21cd7b9628670ff840540afeb861dcb876ffd83eb2dd63c3446b95c2ef69691cbbffd832800e05219195269c5c82846c130f1c1c59760fc39af0c93dab5bc7b24b325078d528c1c5622f72157fb2c9725b1f6719b6337dd0bc0bdc101573369b02311da230861af6c434cc5cc4030406c36fab4cf096e344d9427e5cf2c445afc9bfea608858bd33a381788ab860fe8f5aaf219ab054c25a84c3d2bd338730f030cb94cf0d0deaaf1015251ce1425b44b04217d53c38cd810af54d704cbb2a3a945f2beab57d69fa34042c0c2c20e2a38e9a33f4933c1c529924fa284e5aa9cdf3e036cfb3bb3ac3099b5a16b8c03f73381a885c105224ef28b5cc63e0f121f77bba6f1b2c9c58c7e0eec024eb315652bb2d3c4224be7c7d63fdb8ed6518812865f1137a8b31cc671489a6e5a172fd108241658326df986cda04d9ff5e123f9da24a93bf06f9f6f2172e8bf9a9304db7631c3efb2853ecf7ac0776f718aa2427af6bc0ed6ef22f0b5aa593d0cb210d9842d2cf9d0913161634e2cc3b236af9a318aab4bdda28a9c9126199f5a431554e47333df95dbe73c7013b1191590c38eb17dfcecb0b85854eadc685bfe871f26f230217491079c7ee3f2a3b96a589a0dfc7098c54cfd56d8fe6a34165da8144fb2bc30b8ba2eda5319037fefbb4c0dddbd1f5996cd639acb46fb15eb39d8278563e3ccebec5f13759febde6298927cd0f63d16c2fe5ddeaa40e8e7322db50260785eb9cc878dde1c7346817aba0f622563cc539d7972a4c96bbe58a245ef293d5d90c84a5c365de8bd7cf045008c3dd8356651862dac9d75525dc2a1e3701cda907ae916cbe037a314be9cbc5defbebcce1e5744ad127b6cced88589803d1de409e56c463196ea3236a3f637dcf551498b95707e068bac509b20e76e0152126b01b6102fed588efdc7f91711c7262a2ac765d34f3008ebe9713718623fc20948d75e171cdfb5138a140352edd8e47269aa4e3bc05f9aca30ac8d613831d60e26070f3df87d86b38f3be5cca387dadd78c8d0ddcf05be84fe1af29029264922240d7a23a586ec56b9cd97d562d07fc8a3860c099184a9a291f84a2ad1d19d0a66f175e19a9ce1b43e8efdbab958e247156eac6423c73766e19636b4db7f312de57a1221a8abd07972f675083d29f92d50c561cbe33bab17bae1c1b1f81102158140029848568b9d25a35336520e3bfb573d86b4c0beaece815e4ee28d399b1559b911e71c708f9e6ec016af4ac90bb534a11ea0c598c4fd5fe16388fb0abf25097a7926ebbc8bf7f3c837208832b116e6e63d08af247814d579af24822b6d2272d7f4f0d51d96328a860524975f30b94948efd8f4b8917ee858921f1088a61fbf3a3ffc3384166d2b70cd945106a56daab84f2ff0ea2a6b4d7e2fc65357cd247e80ccb304acbfe8ee2e9aca5a6c207838ecbad0f8dab50e545e431fab5ed309fc511b32859e158a30e7e6522b27ecae399df9206fc9366fdb32a7254d40951501c21c1346a0a38807f10776773483620a3b982d4832938c6d705997926d802a445a86845ef0eddee41991f6a899795bc6c2ebf92bb41d02f46f256136a13826ef04e5c6d9e6f79d0b0541aef2fdccd86a0bab50538c5c566707022fbb51750bc88cf7a3bab2cc4431ea83864ee426761b58260a1a1f5235c9f7d8157550d71542ed5b0e06d044c9555a551038ab65f71149a1aeb3c320624506c34372b45d0b87fe668ab956acca3c4b56b93a48c32334802c8f6a73f4251ab5e4639df887045794a805d46294efa164f4f9e6fc7116411c8c7002effe88f42a6e588118c91e691b26f4aa6970a96eb35dec9cac140fa44ccce5059d7f6ffb5c91bcc108dd00fe153fa19eb56b0f91f69b6a8498a55d49c5a65138da658cde1ecd16279d602e96c2e34c466c9c46e3928189b9bbfafd5c1c4c064c45b708f3e34d0de85d4d0bbdd60d36745c0958f80618697832738d206f84bc783ba0e0724eb7af874f3e1f5199b84b5732d22896397148257436bf3d83ad5781316fb116a7df22d84db12cff83bfded1da718b11d426e7569f8144c6013ec2216c18655787741975844713fad5cdd79b8dff7ae1e15e9ed5a2c15e41ad93cee1678c50ce6e1eb3f004200b6982e5f3684e5f6b484ee0469921dd7323ad1ca20040fa9ad007d5570545d123d36bf5713bbdd089cb36ee1f09fd813172be997d9699b6618bae2d1f6780a1075417d68adbbeea23d7012e125414922ee74fa3ace6fb9b73b516dc41c7d0e10ba240ae5a6e5708f3ee1ee7ad49d9893c3f422280834c9409133268afb4c0b90e66b92c5aba698443d52d875a3debec111ed4afcb4424e52e13a4d7841c989213806ea55d38d081142ced1aeb43a5a3a323be7b4a0ec29260d510264d1a70a800c3bcbaf1580e2f23ec9dc5dca3b203246ae9d9dcb2fb3f83228034a35628a6133edaf9c20ca5b1ed1c9effb6a13d2a518939f04f04036ab7d045daa2d942132761d5716bc80b806ec3419028af146ed60f6de371764fceb41bd46e4ca33e6d0fbe82f07a0e82485e2935652a269b01b39e286b2b1ccc56290da670e35a19ff0f3aaca706e07ed34f0de8168b703176bbedd3864c0050f39ae1a942b488f2d22acdff067a89c02171160bfbad07006cbb234fe478a0d0f8fe6b9d89fd51ea770b85eca4d4273baa1b6b5add4dd68d0c482079695ffc1be2ccfaa71a0c23fd632001411616fdba2a7e9f742a85e52ba0d7f447d0becf1e2cea6b012aebc2771f16065280e86bdcb0263d00671bd7a06a0145f81a0d2dbecd2a21890cdfb0bd461dcde9a20c8e73c6c262f671c9feceedecd07585d2c49a40f9cc3b990e034d7319ff3aa3481d699a1d27f46aee3c31354bdc0763919c8728d271061f87e68be5150658481ae553fe963708380604728f0491374cf0f6e8e8f9c9a2f16ee15c0815b9ee486b51c403039c8e48db2e8d1d3c411907d303e2941d80e1dd942a1cbd085e0dc6f928f14884ab953b1bde9455862eccbc4fc5bab638419d488c0f7ead1f62be8f4ec67777be9d3df38801b1ef12d10b0f39266923550b29234665837807df7ccc989c96c24da104165fb8b755c33085a914b3cb83d8d7ad991b948028033deeb8faaa41e455b4a2ce0370ec3a617cc4f3dff1936f0ce25a84c6e8312b210e4a354b3550816a2fd4009a92f7aa5247df29519b6f1c9f71a4a39bf3c7e12eb72a97819f6f7900232f19d18e9b198bca53207c6975a74e570edb01467c1026461ac9f08c19cd9e01895f038387152f0ed8fe5f590e4f0867668efe6646492d1a756de56aadf7574c331a60e8b24204685c511d8dc6693a2c0cfd05d4c9ea26a51bf68fe84253fa50f1fa76c08b8f637c13a19bcd8ce5e77e9e52b1573070131940693e028c20c367a2eafdd66998b3ea483d2991d6e2f4b2e8eddbe90cd006d4dbd0964a7092d827b557cc843dba26521c8e95aa720da9c53db25bd4305502c0d9fab86cfbc57a1cb7a42cc1805aab43b7c022600af615e4ca4aee3372830c3953c0df24e125fe9b5403d7c253c3319977a95bbb44a6ec9a0994d2b2704d1c59d481bcebb19ec56ad9002e52c19e6f577201840088d57026e729c04e724925e338bec9cbfd1053ec2553cd934cba2b63920c8fd21c052d1d0947cad484bb20d3bba8720d3ca830d80046ff101c56ae7ed9e11665e82554d5039629f464610ad56c2f4874e6ddc7b2b09da8e38a605a2361d3840e48167e77a090bcd0a92410e5e71918f2bc580c62343630a6e7409144fada9bb90b4569d4fa5e7f7f09743030a45bfff7197f3f6abb2b3016c02b3c2f55024052cacf956cd3b089dba1c721f64d9c7054603b633941a6b09d3f98ae729d0960126e023caae5a8fa158fc41c4fac61a56f17b2bf78df6aa950ffa7c7180d0a1e62f9fe8bc6427417d0841d06a2284f5577e015d6748d5317d236af84b0ef7a9b5fa848e4f120e0225c7d141d00dc404b3bd42b4736512af0d3d89232293a7e7e21941efdbdff32be5b3a6e19f0573f126154ad84dcdd53d087711459be592d2b8e9fe517b547b4bae1fda706212a786b4419e9483bc1607045d0e1a27613b531032a50cfc644c97bd2b05a25940c11035a125aa817e3f76d372e788a891f1b4500c2bbe4d6d298e432317816be4f81c7d63522f0242104dbac5a441f3bdb55c00c1caf2deca849a007e38a26ebbfea5a96d96a391e7d790d79a8de78af8873b113b42028786f513e8886582cc56f39d4edb211773b9682dbb1faff8f9b1e6bf51f00b9515d83aaaa7acdd61d0b50bd743a75edfbdcd99e171015ad606ed6c491a009660d207f37fb538dcb04e6c69a4a9fb8405d6064cee99cd0c1285efed5ac261146755e3ff537945dad0e8e53e91783bcabbc7722f35d5c2c94c90032c32403a413a1e169eefc9ae7ad2c48f1189a6966fa08813b0097ffbcd9285eaec55700c3c0eef1d11314e4c35a2b0333f7a3bcf62de93107b2be86d92b58701b591f7fff2919373463c84179c51b95e0d9ec2e0ee282073c180d7b025282990702a0278377f532000d92fd29dc1d68eaea99bb46b4da09f3b5e75d4837af081723bf9fb87bee9298a5a8a63aee0ec44885cde79eb1aff94330d4f8360821e0cee4c6796b279de4c0dd76b65e290d9ae821b40e1a011385e49cf18f8f801b659b1b9e756629cd9116f6f22f3b9f16d0cb02f2ec7335871f2eafddb046c6e8cbfa0f3cef0ca48227493c37e5f1f8105d3867081e64ad2b9636b8d4aa7e90a51cbdaebd10e21d3fa13ee87411014012f1c7b91ab37d4f17fc1fdf02e415685a3a17d7fb93d460c61f810067c989c11088a0034f6f4fbafdda43cd031ab2623198e8c736170a51bbb1a4697028015b0ed38814617e45f45491297fa7bfd19cf1c19f7f8dd079fc5092db967d55a1b22750db73de037a28b59561e56df70dbc35fbcd26a65601a178dbfb147e0443cc20c20844326e38f94aef3a77ce705dbd65959a7313a8a4f9d4c22b0e9d8fddf41b4ddb92842e6d3bcd82b6cde893b04af43658b5a21b6b4f0f8d3e107885de0405ecb0f9414be1abbcfa928f82934efc07cf99ba63bbe9079c9bbc78524794422f791b779b98b7740ae3cbe41a92c2404a5cafc7dba261e55bdc8972bb32966316ffe0171058bcecdf2ff1d363370bac34e209a4afae3fb02b99a4360ecc004c14e9d48c82a0ac4777b71cb73f6524b9a4df1aaf41b8e46a3a93d84fbb2592fe2262d6fcb8d0750b1c2cd0c96209c9e34b9575f8243afbc7d19a3631f8f33f431e573f86927203bbe874a3da4e74eefe24d8058b6547e5a862fecae5c3c6349c1fdd6b3ddeaa836b94ce72bf53380aecafb5f0dfa862eeda3859858e6b254f4711940450eaad6a1262d37760fc8f58287375c06fcde63d136850a953eca5d45928f8ef8c796cc13d436dec9148116ecd0be13d978290daeeaf22cead7a82bd312bd9809bae76712604886ec3b9a050dc87b6044ae220c0a5659ad1b7f6246c401e84f03dcddc76b7891116f5706dcbc5794708606b66727caf30f2592483322a155f818a6a839ad20cf0ecca58a2079004067af822f520d078981f954efa860f0972eccbac054346ac8fd9c795271a4991b8439a75fe4655484e3ba4b49991691eb75ddaca63b04f2b65fb618de570cfb472e644f1c90f245559f8e1f109eaf90696e3061ef14e5ab4a01d182729f0143a7154cc5800b51884b0a40bc77d0eff03542542fdba9c694056fb472c25661eae1ce78f8f04ccf89828d4c341dc2f732ead6887fd4e7a83bd2314bcf106c0e9ac4eba025b9913c2ab4834453546ad1715667c854995fb5c071470521f2c99852cfb69e382599058cabceee8bbf686382a1d69181fa8c617dd09c3e938b5b24cac69a0fe54cb0613d41ec1ffee5e7b732bd3aa99525d5c83c712b1229a817b2d0b431bf081323d650290ebeff132dc58019aec382fcc8f8bc1e2aa581188af5a5ea672818b1516ebb4eada883a3740e9100a6591a842e053956aba182352f74395f5433f26603c2eddd71b10510a16eb", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000f475a2798782a4befbde35b045f9fca800000000000000000000000000000000b5d68e70b88567b1352944061f30726601cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } }, diff --git a/circuits/benchmarks/results_insecure/integration_summary.json b/circuits/benchmarks/results_insecure/integration_summary.json index 193dd1263..7b2f72220 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.11572825, + "avg_seconds": 0.10908618, "runs": 3, - "total_seconds": 0.347184751 + "total_seconds": 0.327258541 }, { "name": "CalculateDecryptionShare", - "avg_seconds": 0.608609847, + "avg_seconds": 0.608034097, "runs": 3, - "total_seconds": 1.825829541 + "total_seconds": 1.824102292 }, { "name": "CalculateThresholdDecryption", - "avg_seconds": 0.578625417, + "avg_seconds": 0.556764833, "runs": 1, - "total_seconds": 0.578625417 + "total_seconds": 0.556764833 }, { "name": "GenEsiSss", - "avg_seconds": 0.124242194, + "avg_seconds": 0.124542333, "runs": 3, - "total_seconds": 0.372726584 + "total_seconds": 0.373627 }, { "name": "GenPkShareAndSkSss", - "avg_seconds": 0.223503888, + "avg_seconds": 0.222099139, "runs": 3, - "total_seconds": 0.670511665 + "total_seconds": 0.666297417 }, { "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 8.568398333, + "avg_seconds": 8.418797042, "runs": 1, - "total_seconds": 8.568398333 + "total_seconds": 8.418797042 }, { "name": "ZkDecryptionAggregation", - "avg_seconds": 49.0470405, + "avg_seconds": 48.00139975, "runs": 1, - "total_seconds": 49.0470405 + "total_seconds": 48.00139975 }, { "name": "ZkDkgAggregation", - "avg_seconds": 20.15144375, + "avg_seconds": 19.870362291, "runs": 1, - "total_seconds": 20.15144375 + "total_seconds": 19.870362291 }, { "name": "ZkDkgShareDecryption", - "avg_seconds": 1.504357312, + "avg_seconds": 1.439776368, "runs": 6, - "total_seconds": 9.026143874 + "total_seconds": 8.638658209 }, { "name": "ZkNodeDkgFold", - "avg_seconds": 62.8892475, + "avg_seconds": 60.322392749, "runs": 3, - "total_seconds": 188.6677425 + "total_seconds": 180.967178249 }, { "name": "ZkPkAggregation", - "avg_seconds": 2.15679725, + "avg_seconds": 2.121552416, "runs": 1, - "total_seconds": 2.15679725 + "total_seconds": 2.121552416 }, { "name": "ZkPkBfv", - "avg_seconds": 0.328596097, + "avg_seconds": 0.329691472, "runs": 3, - "total_seconds": 0.985788291 + "total_seconds": 0.989074416 }, { "name": "ZkPkGeneration", - "avg_seconds": 1.329930791, + "avg_seconds": 1.332915194, "runs": 3, - "total_seconds": 3.989792375 + "total_seconds": 3.998745584 }, { "name": "ZkShareComputation", - "avg_seconds": 2.693458527, + "avg_seconds": 2.649475611, "runs": 6, - "total_seconds": 16.160751167 + "total_seconds": 15.896853666 }, { "name": "ZkShareEncryption", - "avg_seconds": 2.489975329, + "avg_seconds": 2.470565315, "runs": 24, - "total_seconds": 59.759407916 + "total_seconds": 59.293567582 }, { "name": "ZkThresholdShareDecryption", - "avg_seconds": 6.047533777, + "avg_seconds": 6.067798569, "runs": 3, - "total_seconds": 18.142601333 + "total_seconds": 18.203395708 }, { "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.097465083, + "avg_seconds": 0.094844083, "runs": 3, - "total_seconds": 0.292395251 + "total_seconds": 0.284532251 }, { "name": "ZkVerifyShareProofs", - "avg_seconds": 0.226985783, + "avg_seconds": 0.207447841, "runs": 5, - "total_seconds": 1.134928918 + "total_seconds": 1.037239207 } ], - "operation_timings_total_seconds": 381.878109416, + "operation_timings_total_seconds": 371.469406454, "timings_seconds": [ { "label": "Starting trbfv actor test", @@ -123,49 +123,49 @@ }, { "label": "Setup completed", - "seconds": 3.041689125 + "seconds": 3.030690125 }, { "label": "Committee Setup Completed", - "seconds": 20.243337708 + "seconds": 20.213050291 }, { "label": "Committee Finalization Complete", - "seconds": 0.007484375 + "seconds": 0.006164875 }, { "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 304.504600792 + "seconds": 295.255807583 }, { "label": "E3Request -> PublicKeyAggregated", - "seconds": 307.024598667 + "seconds": 297.75637375 }, { "label": "Application CT Gen", - "seconds": 0.318660917 + "seconds": 0.311138833 }, { "label": "Running FHE Application", - "seconds": 0.003694084 + "seconds": 0.003876709 }, { "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 79.271051417 + "seconds": 78.078044792 }, { "label": "Entire Test", - "seconds": 409.917920083 + "seconds": 399.405836208 } ], "folded_artifacts": { "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000469753df342aec0e0000000000000000000000000000000000000000000000006e4d829d66aff749d000000000000000000000000000000000000000000000005f6499be2b18300b20000000000000000000000000000000000000000000000000002be6db4c5558c00000000000000000000000000000000000000000000000eedb4b6056c881e3400000000000000000000000000000000000000000000000867b55169f2600f58000000000000000000000000000000000000000000000001bc9f519f74446bfa0000000000000000000000000000000000000000000000000002b6d6df53885c000000000000000000000000000000000000000000000007269f9ee62308a02500000000000000000000000000000000000000000000000fc8ef0ceed9608e82000000000000000000000000000000000000000000000002d32a3b72f08734b600000000000000000000000000000000000000000000000000018a5af26897d800000000000000000000000000000000000000000000000665ee11e35db35bab00000000000000000000000000000000000000000000000c29f716629ef9a6b300000000000000000000000000000000000000000000000ef868c0047d6dd22700000000000000000000000000000000000000000000000000026ab8350fbcd619e3c65ffa1c0d29b2cd62700cf8d71ebcb032227e21f28af6c3d9993353159827a80a592b60afb7bd0a5547279d7b7b6d745bd21e61cb30dd2fe6fa64ed7ee81bddec82e6608821134bc64e48afc548d54643761d84f4518e91e1bb576a9b3a1efdee0270f6a11edde9c2fd71e9de5a134a7ffcfa996506f17b7ded320b946905e9267cb38c0568773f0534e90cb7b63d7211d220cf75c33fef50d1b5a5100e055a47bec0d9798656da3a784b526caa90e53303c14eb63b919c2949535645bf0d9547c8dce32e13dd504bc6be4948f9635a4189cac34427ead69640255ad33c0d14a348d9edcbdb6ef4d4b0a379e7fdeb9801b76a0da963bdb56d89ba8f3a0210b7c42426c99152b8bbcdf2b36f1c1855f0ceb7482c347de29259ad7e05498a03d6dc7925daaca12e8661b8410ba878a1497660cac39150a0d2d1ca7b556cd5217867ffbd422a5ec0f3eda032d0da1dae248fdb76aa76f2aef8583db964d1491f5aff0a8af8585259f308839565b5bfa984c574d4cf02f2552749a67f4ee7f91fd21add31916d3b40f4006b7a83ebf00c14999182cacfc3b853fa33eb52c4bc0d7a9f47486d5c9f5dc5f1fba6a4c5edebfa27f2b9d47ef6a7de0f966356dfac080b3dab741554edf23aad2a27840334a132e4ccc09bc438f503346aad442b0214216a2005c8c7a405b68cdc0032c8a6f8ff1c663bfcfd165d7de791193c31c30dd9f2f2f2a0431be2e4e4f1fe5b4be54f4df8b761ca81b8634e42a243789b7219142015b3808efb3a739c019f139df3488006f7e463f6d19fcf228bce9fa6d613cc0c01c36911240ee0501a0cc007655ea1fd15b8a1ad2f8d1ddf49931bc0bf177827b2cceb2956abbee92ecef40924cb1e618efef90f0194b8d8df4745ac94211fbc877e3f1c1843dd9c04c81659fd1071b38a33797f6a6c395143aec09c8901b9d02917af3362e348cfb2e9aabd53ebbe6b3565508163ea1901f214764dda2cc1ea3a3e121b0655786495091b1b6eb05be9d77b5149795f94e4bec6e2b183149dc7fb2a6c49197a09199db3d00a13ed9d3fe11a18f5fac973b7349533df4922301951689c04b3f866c7f8425c047237cf8d409e8ba27c49e924268817c9e9055066deefa06a0b869217f5fed30835f92bd9662aca74e8b06d29e525ade59a1fb24ae716ec7d331fe77e298f28024bb1fdcc10c4390920b5592dc36749909d0c0f625429773d2393a4f713600cabfca65ec0617333ae5764f69790a9f97bab2f003fff339c5965274e74d713ade8d48df30bf48503c109d9e6c124b70b6277299d62ad7b2b7138796b217dbcc3c4acf6017e1bd6fdbc366957610a0f79eb4f17ed4c180fa64e56f7ce6c9c3aab320f1552db2274fcd7a4576de6cfb0d3db3c1ea4b1ce1f3afd3ab96c2e78be599e3d94f43d7f89851fc6ed825d6658f005e815a7cd1bd26641e038a56293b8095be2f287d24612af3ed6ba44a1f3f3a911e3302a5476eff60236e0580cf8b25f9efc8244b30ed8786f66e39dd4456aa4f024029eba613ba4fb9a7994e9ce5afb6923109490c62e0c988df549f2e9dcc3a56829bb3f7054a4b8cab4aa1769fa658620e87d592cbc03f9a71bca647f275288ab02dab6867241f0d9d7a67a4b33933a1709d75274bf5bbdca414d3ec79ed120fb064ea0f18c25252dd3a82b941e656196dae6336b385dbc60cb74e8cd668c6f631fa549c5ccb417790c6e015e9adb9d640012eb6c177e18eb7a319e4fd7a266671c31bccb4604b5acb7226d74e075d4276b4e9410a595e91dc5f864d35c6e95b72d4308c3be58dc29da40e25d1f63a36940f572b901c39f31e355b075dfc95a2d2092ece23729f1bf832fac0ec1ccd994f016562ca1efebf54428b688f1603df1076714c5d7d7ccd14a87225c15c5178ecb331bf323ab74f8240715fd0166c33605affcaa1b4b75d30f034659860df6af13b6ee9e6e5c1cbc5e131c2709ea226c11cf137940d661f8f254ebfe274cbd889768cc23d728cd2c5082012f427228cd21cf49509a55121e5756aa33e207a548d5a8f15e9f4869564fb2fcef1c4577e81f237f271365e73cbe12dfdee90ca49799cfef0cc0af26d57ac820007cf3cfc9073dcb3915ed77c7e50f58441879c2c5bb450ada48fb95182dd02943e980031a257c5bb6c116e0e17a1efd1877dcc3a988d9b5680f05f5dd7927bbd0336c143c0aa03fbd503e48787f9296c4cdb00160adecdd0ac45174988fbc535e86d6e4250b3b669d61dbca0afec215f54337b74b36820a3794c4824c9c812aecbdb860be238bcc0ab026c45a1329e6ccbb1f0e6abd83cb9910e5468c9e22ce6c4c3c5d1b2892318212462091cc6f082e3946445e67456576206a0777bdafae6420bc108d0d545b60b47a8647c9acec31abd3eb4a940ce24304658f9ce2ff50a5f5a656f80dcec5d9ed6e857d317c33e96e97e19dea73112a08b301cee1c5a26264b2033918a03534a00b9812d0dd992842efea7d0323402034bef78dbc11232402264318246fac64ff0d90acbd0b1930b4377ba08f35e91ebf526c3802640b74f6b786ec118f486e979a8f8d9bcc19307bee149a5a4d459a145723fa863d8230753eb1dd08f4187ced1f037867e3308affbc4fd535746aa6760d018f75bca7bf3396a59902320058d6cd5935e44ea6943d47fb9d45ccdbf8ef6de9a5231e2e4e2a1558662b10e223298ce2cc72aa45a021f1437be501b2743c08bfbc9708db8b2cd7a9ce17256be9fee29f8d3dbeffea7001aa6648ed7fdd53c0e9117d88379f75466d7c2cac187ce9ea645f137c8531c417fa019100cbdf78be2763d38414b97a6b00871a954bdc2d8e1df7970a636eb0e9d8e5acaaf827e955adfe4a8a2a43598a4cba094833c3e3dbd1f261a5d75b5fd313a5f240ff30d989823dc45a3c3ec90082c626ae27a87be9dadb61eff083583ee3568378e95a1ba75f4bfda023c820466f822adc5410bfa974d63142c051bcafb9f1b00ea44385db8cf1d0c36be3e71899c00cc356ea4a7f52ace0bf72913493c437e3a0cda118619333e1b503b29a7e008a0159746030e90700e49f97c957078dfe35497ae3c4fab77cc9c6f080f84637291d9bd340a31415e39c83c58e3f3b901ccf2e2e2356feb40dc496008982bd189118de77f08ce5a1515447c4a04a8c35e9c89f86d164f10d3df3c3701d8148e5681c027f18f613d5f5d6b22de99a7e7c5d1154db617adb0f4aa4b140a4cc4946ba2c6a68f07b9c6b738d4039291b0e7bce61dd330aed09aadba81f6658e15c46251c44e7e03576d9a2d902f93d2cc226b10a6e44adb7ac928f8275333365101c821d0ce2c8b77aebdb5aabffa4c641b602cfb62c3c98c9f966b2d950a193a4c6e4196464c9eab169d15ac12cf5c4ba880c3bb829afc21ada4364d6a4f81299de8b21679e06e1e557e7c831c03cdb1b8629cb925d4fdc7d44fce7f4ec1517897ec72a2ea327bc07437e1a70ce03392e1d739c2f913698ee590e86c3a7c5e01e70292b73f7dd483bd896583d2bd30e736926227cf981f05cd4d718c6f8025127da4b205d377c16c2d412e76aa1486b04538dee538376fe1522413e41df227cdd52e72c8c867f4a3ea87ee1328f964365e79338fccb36c83a0518445e3c09aa2005ef15405379466c46a41cabae490c736d70476264f3b78287804bca97b68bef27c409883a5ce11bb36fe046d33a2447f7cfc35506203a2e3a01d389c060aa3604cc237fbc043276f8bcbfb7d2d1adadfaba9cbb70a9220efebd6ffc62ed96bd9bf02387189dc44aa75b967986a74ece75106f97c17256baec95737d6de103c6d7a2118ed1f68bbf4fcb6647e625e0a210b31dd9399f49665aa4c27d88a639767a521dafb03ac0ee4f48394d53fe6d209740009a1c11886f6c721cf91e29e268e8cb2a7dba0ab3bcfaf639f230f5cc32a443d1f9afca79a77113657e12b3a4023f130838602c3c9bcda88afbf7ce8fddd16060ec4e0e955bffe48ce7795a750121a7304f58bf35b291bff1e28314c5e1aec8a7301a178b3e2e76b81f27951a2550b817406d894712963faeeed76b7069a0c164e685255aa0ec245c5d223b36b05f760307a8d077c47184df6356e68a3df1b6e645a7a1f68c3a4ec30a79497dbeee8821f4cc5aaee04d9c0c28cbe66e5cbcc48994a4efa36572343f005e385aaa301e157faaaf53694d880169b06e0699f6aa01cd14868786ca9be54a5571ea0791df156e53a49573829a7493415d75ea54104d64b800d4688fe718eb4069b59f93162277d97ac74c5836b5078e713d2181bba46a58bd1be7cfb2d359473be276081f1b6ccb434bbb6f226e9188bc9bcc75826becfb2b589244ff57e97a2994682a3e007aad5202beb5bc1a5ee1fd908cf63b2d36f76e04978c164ce4d36a3646d7581474432c9e22f05b48726087539ee97f746f73cd9d414d242ab5bdfd7609d0b32155a9da08e2067a889517aaf8cb024a8168f94dafce6f93b2f12dfc00641d5218c35fd80cc79d0b4bbb1f766f20ad4ecfc79ca94562895c78057587e9edcf1211fca08a404520fb20794782ed562767d84c1e6bd5d1121a2b58b63d633cb3d528392519b02c9a33d6200c2b94bfeae8eb83460ba6ad4dff419969ce80ad48540cd03c06c82c17abae5a66cd8db314d6cbcfbed1472a446afe81b08ee8e710121bbc002b678d3e50d223f02e4edb538e65fe35f26fa0d21cd77169d208d1cea6188ad9b99abd82c6f89525f6f1034be005f761a9503d049c955ce43bf0997e192ee5118d2c94ab12ba2df5075f42f1572f28660109c53a4051c8aeefb2015915127447e3992602c10f17fa0248d4a63d3fb51d02d7c87575ddd2a228838d54e015405e3aeeb0eda949ed50111d0881e0d2eface34b66941e9ea7246365202f1d207ad9eaf4c7ea11527fa2ceb4c32496270b04edd79eca3542d97606c3ad946601d47aae61d27411aabf3fb0d5c57301ee1ffb7c01ceb4d04ff39180475d097f2b07fc67bf89790286b97b1d814ee45b760d49ddecf8dc3907cfae048afc82550a7f416d7de4c9a6e3a847c5700b4642b637ff9872096679d2d5921eee91c5e11110a2da4abbceb28aa0f9b4bbc132493a2ec0f2712ab0d2cb8d2c589168f9af2f14df7d9de5d4463ff031988639bc2bc42c844a737ca569dc14045284ac011e02992542a5ae3e06562f08bd9eb6d695652b0c0207c3514b292402a5745a60cb22cbfa325db3d30866e1739fe38aca41517e9076700489344ddf68c38c28ae9619b91304c3e55c8f4ffbb089b13b2f04ce5d61caad3d2cb54341b5d1a930d35511de0fd7e69802c35962bd45cd5733a23093208051359e4257f5c9cf68082b362f3744ac22c015a9ac28540106e7d03ddc305882758a50c372a580e5d70c09a720755d74fa777039850e88a289095bc3fda6e3de76a661307c2e77237916761e1dc2e2fb9e5e3e31b522be2e0d8f2e3e7e9cc1dfecb73a1a79a0e26c7c9083080a1e1842116ee304103ebdccf52062bf9578e864afe05f3983c17b84f7bf4d1209460b438ba34839d978ec1c5bf6aab43d09bcada67751ba23a5d39910102bec0c09b6ed88010416941c845ff0402963a715b14ae1a371e9f1913698b402ca86250046ab1d84ba0a6bb12686cac9ba831fa28fa3d0ba72589da190425b42548e220351f64ece113e51fdad160958af52e544b5d08fde40fe37bb84cb659bf593033e291841fa82a53a8f2c141b596c4ed461f562e632b2ca893c12a53bf0de5d2f084d18fd57562a366766d549e205cb36846670c81b92ceed909400a673e53223e9c3b1001c31f367bc9125089c292d5c65dbe914f59cfb0c1b702c7847f2151732806c02b5aa5e97871f60839b79d3e6feef3a3f696115c06e05b0429e1d5321042be272a689d6010378e100c44a1ea4c1841d88a61ded92b9c04347056b3e098fb4e93729884cf98a415354961cb468d38c8c5dab2466952e1863952de5d22bce831cc9e2f11fc39a4e1911e4e391aef8fd7e2f2868e7e8593519a4dbbafd163660310bbb68814e644e234d2a6214d3c2d84d5e78091ce6a3167f992d6c411b70840ca25c4251db8cb551e4a37170bcc4e0aad6a73ca6eae85ae85437a8c12fdf8773e4d6896da6b5510c65e8e5ebe2214ce0aeee893aac3b5169ace00fd20cd9084610949486651f579981d04ce71f246593a621b94b11bf2d4a53a9b7272a75ec707e5d6e9b94cba7277335b4dbe3a721bcc5d8d33f2d6521047d8b2e33035d2aa05920b16961a8e9f5091d1e8dcb0ef43ed663dd29d95e24307a3cbb14248a1fde5949b811c8cb64d561a5814c4e42db4c2a82ed3f85428951892885a92727050ec02883b4fae76a8ff11f88b279cafc825d492c8d88e6a21b8069bdbe3020b22284e21dbc649e9fea5576619b0d5a6c20662adc00a8cae9dc666fe84d2a33893fe7fd23a4b3602b1872968e09b248a1e5f009b8221019113733a639e314bce17972dd81107ac90a257fb403b80809e8a41f298cfeb0be9d87341e9e8026b58446c8f6efe18ba6b664917d764b67f883a1573207364f3a05ea5ac4124a247da1fbf361caf35db0991d02d7cbeec4e6867b454e1c728cd64859a18ff1d72ade435bb8dbed3cf274c0830a6adc7e9aa296f01e451882b007fd972875c54a2a76150e904347222648306798c03ad2eabf742dee661f1ff2ee8b7aad5061f408e8c42347c0400fa8fd99c1e5d560ffd9c45e274bba4f8e3de5f6899a9eafbc05c680f29f0e759926432803bd257f347279ad57e0cfae3ac1820766f2bfa89a2a054c1318608489a6149c8ce98f751f4f90897fa87921b04885242913fd053c29d2250a857eadfa84a16b5b4ded1c2f89d817a2e005da0b94a425b9890920c829d60b0fc66656f0a79faa3e5496317705e56c49f69b3732213b652469ede8b62007b1e8209e5e0eb0842a86321e49413e7d1c48539b8c8689d932b368452d8f00a415eadc12aeec854463da5ec3e848343d7e035b69fc369e03b45d426c80cc21af387870e76a693d616c271e15dbaf75d32b19d1e6ccbc63ac71e155d002ad2992a221a39a21245e9ce4d06a5d450398bf7fdad0fcfc4edc1f12c3a109d98d1b24c341201ab7ad61528931ed868d4ae748b0935ab27304779ac23257a0b4e40fcd390fe49200b9b9a086a7756db0a14b232ad01be5356c40d31c1aea918a7206b55b1f8d4af81e1cedb29c4368641a2a5d2440c70d6908fde350ac8a26c7f22d3b2f21b2c3632af0f2e9f3c9e52e99d9afb03fddd83f09cb28d206f67086000dad45940c134287e81c13e4bb5bc22ef82e4c5e061e69084a781a21997c063010b394acfd38162430147456b9de74aad70a060df546ca38407bdcbe464b01aa140d5bfd0a61d5158d157b2f9eb42ee971192939f25f02448cb5782b36d5ba5413dde40e8944b03e710c31402a37c420dacb73603c0e62af12c136f833aa3437064f488a6685c8f0b0c291eb33aa33b81a9e4c513e6ac55c7175c3073335d9740cfe4ba38839d89625ce94c63e04178c0c76553844241b8ffbf82c53237a02390282e7cff22065a0568345157ed5e428b107946d5003cc6ac9718b020a21e589158a93d1249c81b6f5edc19b1a461d4886f40991c89be5c89ac77ec5486d77b12852972575eea908a0d0ad066ab7ff71ff02720e41b559dfaf5cc99992a1e82b267146784c9faf18f0a30b1d2c8926de25b4a2b5d4efcf00aee059031129823405e5c2b04465b50e6a6300c519152d3b175595d3ee4a7c103c90a46683bf33900973a27e9d6caefb89a1d148934865286ceceb2eb2a6dcffbf93f40d6b8141f32df15c9b7528bf722ac0cfcd16b5d3b5992965d3f49c62a5516dcafc36be9bd117d498ae3e827c74807a0f6120713c018891bc502fcb2f3a3ef5df92fd36e09123278dd884d45abba015519edebefbbe07b19dfb6eee736e611a001af8182f7d28d2df78f84999ed714f241de56318517b31798f9bedfae1771e43d28c74ec01236fa605728da81f251b890f3ae9375925ce406c5808328b62d0c1f61d507fee0d603e2271f6256b38c56b4d51698152dd71d572f18aef64bb8956cfdbbac8291420757829b48a402faa763299af4f927add42dfe05421d6f381015fd09950cb1d49db81efd5ff650a753c43ef4d6c56742c82663cd9ccde44c04a9d16efb3f226f10221eb3fb7fb9cd29e0ca0a9bef0f9b7ce84f43ea5bf4c7497642e2ee7ea1cfe335df4a4cac533cacec297ac86541cfdbb6baa5d5ac993ea49936cd81b391fd9942ed996e79fa851a07adcefd1c2a8b8f57810e54d04a23aacb01abdaf8d2e2bb8819d1ac0ecd13c2e7af62f8407d63039b06b61192c4d7a82f65bcab66e05e769b154a29cc138b5119a732f22dc15ccdd3beed12318162f437d9da4532508b3af66a3e7a10172a476ec9de4a05d3cef956d6ad5bfbb0ec8923723117f5a046ef296f2b5d88055a90b443c25e5b0b877e3474f28f2cdc79f49c618f44aaa02d3d1638363ea5018f4710ed06eeba5776f89dc9dc4afc3fe942f92f90326771de423f8057cd1463a148594606857c7410f3de1767f89e692f64aeb9c80dbd6063943261cef4c11bf08aa50589f51ac7125d9fd81d005b2ddf1d8b1c5e7f97512b4bc9df1655304994d4c05d0881eab395a5925b935d565194e3db86ea73f9e0cb618907cb0aa0d73720855ad56a543d380344559d846d0c87d2f66c010826d2f0a5280fafcc55461e94a95c98123087fe7c5afd72020c01b5bb159507bac36081c5efff00bc7e17f6289e9d2c2c5e9beb2bf4dc4465c3e954f2393e70ceac216ae4898e4821f8dee3c88d8c1085a01d6abd2a23f0ff1f6b92df037992c73a602cdad1370bd59f65f93ed6344a19ffcfae76acee1f8e2444d8fa27b346719541a4dd20cb3c9f3dc615bac655270d1362c9edad97c23f13b6897d65ef2e3d67b2e948af7af733a7252895958bf669fb507eae810fcadebad364fbb3b5d1a68a1146f82fe31c5e8c26c46d4fd4d134123c591bb36cfce94158faadd437ae2ef3920ee3821c8ef268c38cdcdbb380ec23ad56abf2c5cbc9fcdc291c02269af47df0e6fbd50a5e974c1e50024975c390f43b6da32df921c90f11d18d6577fdc99c120126a58a070d74d076cd1b872e0cbba7354f30dd38057fae7023c6dcd63527a1c00ad87e6e3ba05bda510ec142acd5a3c662373ac3f7a7cc8a10ed678466446176938d996ea81ab6002cc8f4c41e5269d007637a81878e6bf0bd562e0d1b60e10da7445419432b35618cd61bf90079a08b5a0fdab94cf8f36d066351118484606ed4fbf014e02b795dfd62b14b16ba950be77d5c317dd70fb058c0d896e18a1073024ed35b213beac45d0eed9cdc684c954bdf3c18f365dc541b68618b8c06e1d8a83550339c96e752a82d077cfb8ce0211447e4bbdbc3b127712004fb7caea05d2641dd30ce23b7d8b9c5530671324cc9aa1bd271ac6c9be835fec30741fa103c877e6c320cd6c0bab7d62d3bcb698b6605be618784663158b717f37ab03000f60911e4d3e33184fd26170e83152da85d24a8f095334e59b7b62cf4c2ed8400688807ae631070c134aadb8290c356a9521af9333a69da38904671ef89c4d97004d118138a5d8f7d945a78c0abc64881ac3ab0c86060db570be08d3dc2445032afc433cd6ea04a649a3b523471a5d2ad1431f3584f5142b2b5046b5f39bc69d16565ecc4219043483b3906d8e2623ca40a26cbf9f18f51ac5259545a592d5f827e0ffe8b7ff77b8f63b0ef131ffa4c5667134828fe82cdc754d15c7cd33cb6c0077f04d332e7ddf9956d603ec3c9c0a55e1db71f19642c7f876452248a70f3b1cdfb9da087a8ca3055014df3aac51deddcbc64dd8d5a8a221d8e91e749b308113fbabb99460f5785f829d0b922389a673cb6dd7cff8c3d288a77f07823028da1292b1f04a858b4630c8306b8fc5226f918328babcef14b9dc10c1880582877e1d450f991999bb052fa475bef0846a915a9d643aff92ed06420fcba96e673339288c3fc6586c13eb5f581e6e7200163a5e011881ebecf259bf2b84139ede52f60e389ecb2a3b2d75a3cc041b4fca6ef2cb232527ef829a8476563013a4d336e204e8ec63f06e0917808f60401e320069d022f661d3ba23965bc66155ac3623f30b29bfc132f9b01495f4c869c57e8cc42cd2242e72fdca3ae4727c02d9ac881c0a145fa3f17edb13347bbd19819e570e3b016cb952295952683f30761f4f03cc105cb9a489ad41d21c68151afe3def6129200c5de1b932ab352119aeae31df5121e519547cd28f05371a2c65f716c239ab69d20fce2af689bc05f99ce4247cfa29cc49b35b4358a3b42ae5c91d95fb99655002e2b0d6d535f21a6c58b9f82a2119649e434ee728c3248ed73b956bb9667ca6b77443651eb331f0b6520d62c5852e3370acf4ada80391db77ba155e3aba9e8d821c2ce1d31ec09f9898ac21b1112dddc3ef15cb12590a98531763b8d043596632648e08f8b80de1767e74855ad7259558e9228544d393351f17db1a37f55314b6d2a39ff65ff22247ce74073deb24cc708bd37b6ff151588a7c0d107521ae217f13afecd55265888f3263cb2bc924a1faf27e8345e2cab32ebc6aae03b62d115631527b68761882befcaea90c0102a7abe55e1764b18199cefbe32acd68f76650b5a83204dd677195396700b93e268fd11b30f6aeae2288c6b31926a50007a0e85eaaf2f7a6c3c040369df5111e1f8e3e91e40e7095ea9820a37d8ed5408382dea8fa3be255dcd2432ac79c3b2a1072aee7d8c69cfea210a58c99dd9c158744bc20b17e8dba95c49091b70ba4f4038acdad5293ad3f47569799a1203e607107575ad494b4857aa06a2be9d0d4ce2aa07260c845899f56d89fbfb7e18c24130097b1e41bc0ec6d7243558f2191bc1ee0b5ece2d0d55c02d3abb80cfd8d10d6bbf360068dcf6cc17debe3a4e8ffa3287fbddc25ef9071872116ea57fb9055a978d85768f3dc162876957749af16512290c1d094e99bde4c38c864d31bbbf85ce2ee488f943be32e2727633089385f2b461d8ca9e0855827cda5fb219fb1717ff0a838ed1b27d82947288e5e8c14590e039cc9b1ab4a33501362144ea57353d0ce3ae7f845bdaecd9e1304b5472ff61d5f0fe5673507d3b79974ae6d6294e16ef1918a4fa4a8d023e89823124327d21b74fe4ae3c1ad52801ad8210a3cefb6ddc0297038ce08f9e4f49ae8f1f99e5e293e702e81f1c7861c1c445fcb48d92ba77eea9b250ba2967d506925efef7f261ada0b3d9f2742502c26d1ac9f750649cf0155a7e327a3556b2bd9f20e009b9601165f5c53fc4b398b8f3f7413d065aa3fae26a12917fdf716f1360181fd1dc81444a57a6b94fd298742cfadfacd30076bfc6ab47be4f09b66cc76b022a47ca5191e725f35288a70698d51c5cf43b77c2f80b2b3bd4d35c2ea033008cedc2f042eab11def2bcd80ba95c3ee8c4fb917435c62c6aeaf2f4a7131b429b5cb2a4ff23081ade5ac7c55b647aee9aab520bd3869f9c93c36aa7326ca6bff7af3771a201959164e8a0978053f0c13e7a87916c47c7d7ba0d1f1d1c826b8873eb31f7d22a07070f5d5fcad09bc1de1960c10ec0ee3285aa2dc35bc42d8b206ddfe119b621ff709b9861e00dff5f5caf92d33c79082c90e3e9a811cfb225ccf802c6c51e0dd1ffc5d73e22eecdb9be76ac089ac3cbe39613b2e6f4948ae91a42c6ccbe9f278c01e611cb037785788834d4b7306a5f94e0f3bc26e9731c53db35e41fd2b41ea68bb1a63e21995c245b20761bb235c4bdb5248afed40f7531fff2a4e300c80ad0019f2ee27ac9ab8181f077b7a0bebcaf59459cac8ff3758a91b051e89dee24ec65c2bd9503a6ec590079fb13870f775c5a56325c5317a88e5c439303808c0846b44c4526e81fd78c7cc680ed2993cbf600b108e88aeae1e772b0b0f8ae5806f883cebfb726b4263916b6969cac42ca7a9e5910be070fb64d007d0a7315d91daaf0e62bb35b5e24c1e2a5095a294afe753fd690a0f4bf298d5f1d0e3fb5311399d876cf50e48fb817b3e8c7146c1d061f07f7d0710e182991462d793227590788cd56236e23963e9a41d9b6fee0eca464fe0b5ecc58b78151f57622a97ca700863f02eb1bc9d78d1082feb4f383d1e2eb5bdcfb4fd72020922208eb19f7e809e9e7b184a15bfa5a0a43863b5de3e191059cb78bf74c53bff6104e6c5637ad0121186bf2df31b19bf8e87fc74d1c71d8d520f181c275958a409dcb7bac15021cc57260697c1b3bf9c2db1a1e87f6db39677caf5445f6e85853823a0d358472092cd19d6850a62402d1e425a2d7c0fdb61d2e02f86928c3fbc56d2ff048147b07b2e6dd21224c6bfed79c76e49a5d79a1da6722776e2c4cacf6a71f4fdf8c3421001e4747173ea80422461affd27e3af8ce9fe9eafe6bd76e47ca4bfb9f54e01787798b5ebc4cf20038ab846044605785202d2ebedc82441c938599daa982200676dab42c70099280328e8899465ad1cf74bbc27cefdbabdabe85be02dcc6be2780dce9f7f6b97c77bf7284dc71ce4fdf48f13b36553b372bde71f04c1010f719c9e06ad95bf42e16fb4c5e9f5779cecc8a99830b62e6452cd80f8843dae91517bca2479d53cf305a9d43105eef240fcf12a391e92c49596770405de304801d27b4f39ddf78d44201072df5a66c3753d6cc740f42fca640ec8a9c2aa509941c2d8a8d9dda693b87dc33c5b641332770cdeff855b01b7d48304376e48925c963036bce8d831d7efd5f879447cb403c7e4d7b01c62e0ded5dbd39ccdbb545c5f30bdff5b42ced0c62cd22918e3598f7a6cb7c9cbdb7cc8cce49be5fb57fedbf702e06430500eb0bc6b794b6172145c3d7e0b18e36f98f98861f8dcd0511b707b91dd03caac365db1699acc849db995026ffed7dedce3505260acbf7c4b09aca9d0b09ef40ff6a704425e1195ffcccffca50ddb4c9cd440fe5d8f918774b2852b7177d38c0a40a9325dae46f5c11c30c0b4043067451cd714925e0799f22524d0d0c2997d8d76af181579491ae7893f4d087b1daaf0a5e38723690b34d70a1d0d32b0eff8e2c99c5e155e65f42c60d0fbcc7604b757896e0910f97a1f49f6283ee2e09e4434f0300af0e0abf8b98762f5c6295427fe8e0fb6748b7e7aafa26b02b1d819b307c1f70a1a3ab3812f41571c1b85dbe9603288f4afce9591e8449d99a0c07aa3eb97c33a646725cb03d52fc53ba19064ce10ae7544921f23c2eddae110359572c3d7b81433fcb47a8ed65977667fdbe70a029dd1f9ce9dfa4db220f8a2bb209257dd8d49b864ae9621cd32e3772d453207bdc88ea8be26869afcb5a3c114c5ed52c900da7ed5489e6e199c7ff224a18275897d832bfe121afc7d663750613d5ad6f4ca266eaf1f69c6e0f1617c647e6a5830ceeb3f4d5a2e70730fffd259aed34a0a6d955f29fbbbb96a7051a9b8aeb3de2c077dd632f0f91ae4d2fcc13ad02527dcaac974cd68ce11d09ee138b586a61ea77db1f4d8877146da47b710d5e19796318dca7b99638e5450448ed4b0776331585b6846d0b3f504854665421ba13580166db9ba74f35923efc566aca7d76e32ed30283f7c5b96a12a4cba7130476d1a3b19df10ba213cf604761861d3b4264b342a68811bcaf6c26c4ded5189b15e6f0deafa36e7396871fc0054270765d9cd75681bda91ef8f1f156f51528c7e724218c156417cabe8378dc773cd875b748baaa0360dcfec3f8b5fd79fa15758af830260018537aabe640e55fe9e8d3ba07fd4e849035112962edc1ff6d26a4bfa0e267a938b358a6d95d86a3208e3a1ac448dd043cbae10f847b76a8a20ce0a559bd0f3cba36be6fb50512452bf06915094f7a7c7b1a7a517c7fe3a666249c3368fc161a3932f2d10cd88276932d3d2315ec9bbd8a1511216be52be76d1cc48820c98e773afc3e24181c2666fe05cf22c763103498316ba0759750d575156016a55d457f3a538a4822d1f4dc598d9c9e25fbb0381f2aee56bad06d5c6a1430f3a6fd2b33477e72b8f91a066301dec27e6c9bb69acb1fe54f692ff9b20e14a2a71e577f1f5f7064bef074b0654239599257dc86b12bdec3376250f97f5f005ae534014c6a69ff11c135d18b4dd33c13e4cc11018b7445104b16e6d94b580049efe999a49bc660ce9f8757ea3456c42319d1f91e21277ede209bcb9bf2da1aa875a2c36bff51aeff4e2c01151a0243d5551e016b5e1ef76d8a98443d304604391d823e2248de66f8a0348170e55f5b635d380ccfac02b8fc88df515c0ddf26c4c9d9b7980a35b819aced7c720ebe1fa773e33e84c79dad6f46d3f9bc35e92d2c337a767350d906769ef475cb72fe2e03ecc358f1ee1074f5d620b36480f21255cf596afae8506f5a7b164314cfd890f45fadb279bcb742345ddd9007b4492bde23b40195702ec4cf12305ef29d751e1a31c1fbd3ffd9d29fff53a3dfb371227c0f79f3487ce0e76d7188070768a8ca64021d32ce22f13989dee1c46be72b0d2faacc65a02532940e3759cfd742171bf642d8ad3321c96fb7a6da32a70300226f637fe241477c36bbcf1c01a059b520194003c1f6bb6983d74395fa5b48f914801b01405c4d89b666b6918ca546afcce05aa80dcc4da69af0bfddf2a5460e", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000211521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + "proof_hex": "0x0000000000000000000000000000000000000000000000063115aef9dd42310a0000000000000000000000000000000000000000000000047698484c13252b5300000000000000000000000000000000000000000000000793a399b1d4073f7f000000000000000000000000000000000000000000000000000188a919b07d57000000000000000000000000000000000000000000000009d59f2c2a28ddd9ac00000000000000000000000000000000000000000000000ed82c9dbec6f22f5100000000000000000000000000000000000000000000000074f0d5d0b895fa0100000000000000000000000000000000000000000000000000018f934b25e9460000000000000000000000000000000000000000000000050b758b170434dc43000000000000000000000000000000000000000000000005e705b87c810fcf1200000000000000000000000000000000000000000000000ac947aa7ac1d9ea30000000000000000000000000000000000000000000000000000214ef97e1cb8a00000000000000000000000000000000000000000000000c365fa58673f1280100000000000000000000000000000000000000000000000c0306cbbd1646e85800000000000000000000000000000000000000000000000b5f507047ada95ff6000000000000000000000000000000000000000000000000000275a4179079091d0fd7adeb7812fd07faadde449e629089b4cdc0c410fcac699af6d2c4e3fea92a5b53ce90b5e0ff0cfe567d996cb6aca1a1246606ca25b86c909f630bebca00265a64231072629f487aeee5ce8d7e010733367bf831e7f63d1b7101497d90491070c09fd0b855118c842cd15ae0320fd756e7ca1dbd3b7010f8e5ae5b268fc00b7dbd954930cd55f698bc9bc8d5f16cc60d4df84f28f2a6290b46501147f98f039fae43b805d73e009418bc836a43788c01be8dc8e9a92b3cb5717edada8b1e233a591b22a8a5ece11574df80c26d70963dbfe6fc235884d78eb51e724f03962378b21b29354376429108bb6667ea87c52a589cf5014aca06c883fe1fd6254f02f8d24d0251b16b997167504500d2a698f1883396eb8bfaa0061bd0fd50473e121dccb4babfdbf5e403dc53a7f4edb47ba6f30b888e190798f67b8fa7ea8b9617fbfbadec069413712b80ad7ef070d6c7967299514921dbb2b933ddd4ffe6710fe639c41c1ae67b0b2f557986d1b5c889c5081a42d8723c2febf8a2298726cc242d340effd764bbf2ee85a4a2ca104d4a3a60cd2321f21bcdb6cf6fe0b2219a030b2c4ab077ed77a64c633ad1994c5be4ae7c3fc40f569e5e913f2193318d9408b0acd9b422395ff688ed0684b8668a67424d2ba723555c9bab7c8bff816ddb1b2d5453d71b602b9f82dbca9466a34c4c7fe94b8b9261086540664b893483260b9b0f50511620158ee8ba0c4430b02da78047a1b0c5b489e9693db6c17f9af330134d427e392d98ddc08373e170b14fa5e12686a51b1b4fc43c8e32f92054181a7de76b8d346ea02fcff8e3ef3cf773cf0171ae0ec5dafb45fd1d20a98ecd0626a9f499e039070050431240f77f24ae171d4756989fc8564cfe1152bc4a478124642a1ff2e2cca217e047052d415ed8113a2ef10448e389e11b55d505ed4568291be4d6e5ad8ddfef6ab61935c38c48101a7a6e7d995206da76cf3d9ce6008110adaec17d2ea8c31ca166ed8d74323023ce5f832083c83cf383a42bb4d522b116a8d89a2e753064230698277590b0102e75ae48c79bb35abbe6e5fc43c9050517546178ed4c05e57f9fb4cc0a5ef089754d543b659ddc1b7121b757f3f9322b0ce7b3447d36496bd9afed562bdac1a231efac0b51999ffd720fa1ca9d07d5271b5b28b8100030f24305dc42a262b83eb2253d8fe199e23ff0b7c3bec3d9a2912eedd6a98e8f675ac664338943124fd5b44b419eac45acff1323ecd493f686ee27631ecf5496ba04fa946b95bfb10fb1d2d43e86a13c109c5f59c95726f3bafa1e5cc25e35a370ab5d0dd951bdd459a6d27a41c5b103f2206b50779174d3b0bc1c952e9214d27a1dbddb5de018cc5937f9db710a17a28e984a8d91e886f4fa640cf57674741fba4d28e2d35151eb618a4504b2c048b2604539abf0876d65716509e484b67957d3fa601a61ed1d37a999cd5d2663ef8602949cd3a1a2b71ae7071fd6c07cef8f0451e5f43a26f702287797d6d74b55998c4e20e6eec8530cf87a09e0149dba7a7411b2ddc83d9fb216f11b3d1ee08fb6f903831fcfa9fbedb3a12e4011636680096e69ee044ed59db4ddad9977b87364026dbe1507e11ea79e5b264750abd4ba0b668bfa90dac3a450101352696bdf69d5332cda48b386e1fddb23114df1be460f352ffdbfe29e2e5ac8ac7f6470f9efa905a115e1e9adc64a1f2023e890f0dc2d9d68391ae052a1cf110a0cf5c3215e7de2f984f1c1048b8cd30ed40af36a815ea0eca7201590ff2b20e5b104cb449df088a2a8c5269526749408494e2977f7c1a0212942ee09a2ef6a6ae21170921e58981887c809e5b2d82326630621510eb59c904ce15fc0497d2ba78b1bf8eca3800318961703a5a40a0820dbd19315824c1af9ca4b94dccee9c7e15597acaecf2633159ac23022d6c1120fb8369440d8e1ea9cdf387e58e1909c3d4ed280686b046b6bde738eeb6d5d4509c03f9ec131655669fe27cf126d44aadd6aaf253dd5e53f15603cef4640db5b0590563558b05af450b3d54dc74f51a807c713c595af076c43fa2432993b05e6300c2864be15e52bba2b6fb00a7bbeb534bb77d858c23cf4e2aa98cae797c8071f9c038cac8946f77b603e91f89cd186a752ca515376178fad9d87e4da92f11e2e150a97faa9dd1070255507a1602af276f1d2f28c7a6a537644aa27f552ddad12243b315fdece7cf05b814bbc25715c24a098389030b910b73ccc31cb446a312792f8b831c8d9e3473d7bb32719326c088390cf3200cf1b2712a1e8109033df0a2826c610569f292da390590e7bfe1bf580c318b3ee0024d49bd8fccdb997a42a429d2c1e59fb115f9b21337237263cfbbb392164610c3587cabae55d6e53211f9f29bd4f1a0af73e75c094f89c2212c783cb7800b8ae6de0b2f793e50f753e1d28439ddaf4750075879b4cb7df5facdc2856519a422ca77992a9edbf573e180f521f7963f1795e1b453f0ef9cc04de2a32c9869794fefa9269f6c84d9e9d1321c9585efd32ae9d1c8fc4e83d3e44d64c77204c232ed31b493b2677e6c5b06a0ff2062506f6785feb3782197ca25e13e111851a229f6587b322bb993d2917e017a199a263c00d46d9f5ba1dfd8f6c6a1043881b27e2bda1ccd544375b95197e283bcaaec150d651f13d40395231c76f7ed439db3bb179332261c09a51ebc77c0e94c0ae2f92699f663765ee759ec63ef6768baf1792ca7fba45f6f7ef302dc125f6af22946ac84161f57f35aafe2c8bd2607e12900196573f1f6d39c66aef592a16db837b55bf3a42587aec8a50311e5081f0f97c37b5d6b17e885ddaebe6190429a13327377e64a15fef6108a5143959a93ce97e79df26f960117c3e2e83ee1dba0ecb0c98f726af182125d09bb22cce9a5c9d15ff9f05d9bb8d1d5fad601716e0abc590a5dc8f77ea98b616cd569a199c51787e08d49359fe745f08e1d81821005529d844e14dd8b81d7a1b3598abbc2ca3e90bb45cd3080a26a647cb127b2417e439737c55268709568bb5f8eb247e3f73778f2218a1411733d4aa93af9e1a1cbe7075cd0999c6fedcb5a837293dcd4cde1e6b575449108e9499c0b13071013c1d0525727925970d98b30fa0861f406ca081cc9c78cbe22559d0575d59800f59920ee80f0b8afe9e0c83c94609a0fb4d745ada61f42351b502f57f513bc10ab13a9b4fc2b37e09f1cebaaad70a7b9f876a43c8b5b958408aca2379fb438d03f330b0d8cbce2a8a32d7d92bf0c77e8e536ba59d59987575092ef8e2e9de69062503b6c29960942dda327d464bad797ed244f15a692affc2cddc366b369def01d69d77012bf3004e87c85c2f3328caa8e5ae13a58e432c550c1a4cd0d0152e13133ff2ba51355534e3ce561f40083db91e50383a13b6d348aa54d0459f90cb2c8135f2e3a001cf86544e86891e3ab854442639d2c374c766f7c2b0d6087e9e0bfa4bf315b552b24bdb08dc93a4fbb824bd5e64baf22df785b6aa1b65ab53942ec18b91fc6850f278620d99c86e408d8f8b14870eb18fc149b1474099cb54742307de566ea77dea59d3d249c3b3c61146f0f4bd8df5732dddb647a24064d20d2b344a36e6e6a2dcf83b9ebee35055d95d9b00952ccbba72f2754ebf2169e48104809bbbaa0b40e98129e5c90b617185b0367ebb15eb2543704bbe51e1532c6d15699e1ecb86b4f0fd61e77fc4c41db426e0fa74789b358249449a9b749e8199300b5862ccea21353d4b1538e962d2732e28be130a2dd86f42759855e5d8a47116dd260c4f67f5b6dd33721621465f5290dfcbe165eabb75f1dfe17cb84434df262d9c0944750e38c8f972bd21a50d4f636c92f575a07e7f4d908c1ad083312f1ad21d33a8c15c79cb71fd13951938692ec8d9b1c7dfcbf401a256e92fc451882c3b2fe36055cbfa4e664473d1234b9a87ab7201f17936d5544451fb6b1935d014c15fb2b3a19d546e6ce00c7a7b65e7821fe72bd563ea29ab5b938e63a0319106ee90f27582cdf489dc203ade9154113d6e66f95b3548735b09412b77a5442311797d3d71921706923e374923e1aafb6eacb86a89dbdd75476c07a5581893801fc301aec79ed35523bb8ceb3be54b19b21cd5400d4eadc1e772b862bbfdfd600b97076b53b6e0afcab84cdf55a25278487011aa0d71df6a5169d9ecbee182d322659f7f61da58e919fb602d1ff24c62cc53e8ab6dcf01d4dc4d64aaabd987540b433948bbdaf7b1865e5caa3fc76eceea5939ed78d8b057603d443c8ca8dcff082f962ea623218da6ced991e7b85b6b8e519c0129baa723bb32259ce1ad5d090a27ac17e6d530fcb85fa3406c9559058fbb7795c9b8d550c37090e28f31e322051bdcd88d74a20ad4c29446ca5aec2c2b8bcfb1d7f07aaf3e9ec1a656b58a341aefd630c847a011e2ea23b2d6e27bf8162ace64423818eb032688e36f61505f0a61789f606696853840245b606b39204e9b3e60e4ca45b0c538e3d7422993e9125434e09b8680961c6d47b2d4372ef35e67b1c91242275eba69be43fd745bbb0f528453b1ec2dd9f76d53ef90dad732494d1b936b640de37aa6ac1c044007f50391b72db135340374236daf96f528d6cf450429a9ad166e6e8df8416914a1860b5fc6b77c5dae07dda1262959b47fe408ff401afa0827a7e16b4f6f2ae0887a0ceb347824fe27dc767dfdf136e17b32301a98e7e9cc49a5c2f8b68bc47ac922300c897a819c28298513dc1936ce5f4ae394a0a762edf0f07fae0f23b49cdc44054a9728a49cc488cfaed54a5a4b012ddb0630f0400e7293ba4330ec46fd95bd03495f1fec982b8aeb87a09ffbbb50478d9109365c28a4cc507098263a9b96b30e49c15a21ccd730578b9e32e3a9c0c6ca548d7340cefbd4986f669f01ee12bd16d3bacfd3b57aa4f54949959c501de8326e4793cb12c5685de0e8ed4221ed482a68b70286635d45456f3fa195aa4ee1338aeb6b5a4a4a4168502ea35e4434c32822b4170506a783bca6a33beddd3fde8d1b78dee1620a8d133527872648b85e2737c225c1dbf2a82058447abc64bb30a2e9814143cb10d1217072307396306914d061b219fcc52d9f0c7e1f7a409d06c0785fc7c744dfc24c8160b4a6d00dd016b5ec086d9506146e29f3a87e2a91295a7fcffb75ba616ef58507bb20d013d2036ca1bf9e9dbe8234cb57016a40c1df29c5819bc1d64151284eed3069386fa523640db88af8cc1a66f793289c1f1a80ea559c2c3653e0c9542f986a2d3c3afd234e6e3c7273e5b491f7d5d4947d5951e0ec0ff911dda8023d2972ce8bb1d9151168034f4d9e87189491c3c4e5beb598edbb47ec1912fee9944d13aa02636d0409665e57410359763dc466580968d4039318fbf7986c903a07eb5a5c5b4476870b00bc68bfa32f6ee9b90d76ba121cf8b63314495e99c12fa1a43322bafc866216bfe3f131fc2de0f53109a042c70e24af1abffb41e578c665568f3d16cbe54c0cea817d6b5341059039a7ee117d14845ddba9e95f479045914e101efa4150a52a4cdf205120977337e9a26ed8493bb0cc0e75c16d8986313b0238380aad69db15b9e63e0c8b417780486eea2b945cdc0844f662df774afc4d623f3d19ff3e2e239542ce815b91e98b1b500fa18ce98dfa476bb19ac2dde5aff99a10df77015510075848f6dba5ce9044ae7559385ffc30be6afbf64d6b026f4bc3dd497195501a83d86f323dc812989fb27c76b6dcb3782b6914ba43ad5e9c72bce07cb0dcda06160ef64ac69e572a3a1b6931fbab48d4cb7024f9d88e3ef23400c208e1c7c42378f38973c9a20a7dd9ec8eebc989ea43cadb72b068148ffd7f5f7fed57964a0f8701c6142aec15d3e72c0a87ea0cfc3d8fc076b672f4262daf68cebb5751490871b47e35edbfd82ab9c5088a7139dd1a0ca9ac33a9b121406df3410c09355b2aa6b18960d208424312d66c1b2057b353f60447562e3dd40755582b0196b927268bee3a57d5f462d99606ec9b7866d8acdbc222df175af39b3f98d0ebee02d2050cf5430da514c4fc23cd2103e9ce6cddc4a456578138112079f3eb0109857f0daeb1e13ae070a0f3a6234cebcbf63855cbe86479e74ec01e253b27a87915b31df385046553bf6b8f350388a1cfe158bae1a23d48bed8f5ef527dbd36811a0c106e01b566fbc9c45c1d235f3b8b123b696fc63637858b4a18b0282de69c5b691cef2bbbed27e61076b756bff5a3a12003f7c487296881293d9f389ff8435c012b84a3cb4da52b32c7e3eb9b23d3c582474fd36ab20cbe32ae48f045f12ddb9605e2d54d8057718545a4418c22cf3b202147399008abf98c1c08dcb044b0d4a514081805ecf8895e86ed22d9540e61650d7b38531a755d7bfdbb47035a3ae527178430b9d7ed50783e9f2d6a139432c30fbe4b250e711445010f0965871e77c50dc3557d1a543c4c17808e4c8ce62b53e260056f1310900190ccc1376d6c8f692a4407b1f730269f888eccea84dff995cf49084c395f67c257a2a9a7e088ff8d1ceeaa3bed5062f7f27c9ededd5c88bef3da6ddb8cdbe4df04bb370f2911ebba2a18ef1d8f6fc99d73b98bc700bea98865cea2d8c871d5c5703f813962c21de20fd323d1109946beff8c485c333f521b010b7c24a68de5d26e5ca759817585b608d1cf1cd0ffb1359256ada7d6c6ad9e020de1559d564e0075c91c7405bf378a2063a8a21243e0c9ce443654e6b0c1c6d7fde159ed74faaf82233c4e704daa312e8c7534e5e2f0e26a8dd523251eef25da2eecae56cac4114b4bc22fd9433d8c09dd96b43d76d86e2014f826cd1ae89251b15bdb6a1bf743973ee53556969f191c4338d4d5e8a99e29255f7bf9ce859f6f1419d11e8740e627906954513ee2c9110e0c3b20f5c1fe7c8ba9ba7cc4fb01cfa1d1f34b6eb6d1a033c6d4678f6be91cf8b5755fa90a9e5d4450656e97913a16561765e43c575fe879994c652c8c290eacfb807c85b7abe6ddadfc9416ae1d625a0aef2728540864f4e0c94ee5696610b74d78e61dffde7ec105b3401322a88a0dba9bf64bf901e744f12cf547af5c1f58bf24b443b3c7a632e05c4e9ababb0d3bb30b5eed6e6c9816c6ea85d9756d031888757e911c3bb04303053c7739fc7f3f287806abf025b790bb8a5e4b1625083dc324f6214093d3d089e40e7e6f6482d35ce1530e35da4db6d9c510fe2af40758ffb55a24a8b62e9e1362e71be4e013cd70869ada510f88a862d22ac11e67066ab76704dbe14bead1cb8f6a2c4b53daf714bbf9a7b216ca8607923da71a7713b3d7dcf3d674cbfc9ba7b651b67974db0398bfa70f4091bf2c1ab82c9a1e222434d6ee173e1e5ff781174ef50d74d2bccc364c09f2de5baf1064d7c30e050c2a8f067004f269807fe984b7924e6214c49baf8cafc5454344982495a3ed37ac1dbf1b21def8be857ff70ed4e848c85d0723d7c3b39ac31dd8335adbcac3a3b711fd7ce514a178d92cc0d70ed94ee72b319d9e037fcc1795e5a7c273f8524e5c031ad20e77e5f262a9abe5597ffb7ea806fbd651946c6aa20dac3e20f0d832f306e6be4dd91caba66a3aedcc07f162d90b36179c22fae082c5abd57c37e0c9ec13705437877f256e081d2f3adb8b0df2f54befa6b5da02b01dc5507058776b4729916ec4f5a9b52608da6ddd60cd63db2d790abd08fb4850e03b62eff4e1867a124ff4a3439e68ea0066bcbe1313252ff7376b557ce3aff6347d82a4a9ee142d05cc677617bfbf697f5deaca64a4c5812ce492864c29652648f74dc8352f7f4723de0d19c35090f0b490cb1f41af0f7d27eba21a80a38de88f1602db9910d6042cb14301059f8b431ce698a8d9fe156c35581ee8c1d2b019b9ba582eda43c46e1d4e178cf073679aaab6a59a964e18a6599211e6fe9c17cb82c3d2b6ad670bcd17e546ad386d3c95b08633c4d037d49094b5e28b5a144abd0e4b49f1747ac7bf20a2e7ea6b4e6469ba61530f54e4de36467ecc72bf1766d834d6ab0a9107b8a13051b30f7bcbafeab3ffc23c63151d4f481589b1d026cb7d6ea6269dfac11a732aa3d8256a2b71b7afe8e1db35516ad40f5c8432978ecce8f0b2741111a49e10177f485e4587cf6dcaef64de2e50f14a0047e8d5e4b09b2f88486ab089c2921d05c5ffdabbb2da3d2983b762ef2491722ad28037ace7be05cffd2e2aee196af226156fffb41319fd49d29c7786b3d3c8974f642540a279a37d1c738a7542f9ef12ebb9190b726fa1431d5a917f0da84651e1ed1e49296e85902a0d3a48aeeaa722746eef3458a9b6da57850bbd9f0827f2b809970dafbfae0035e317ab2e1f2c27ff6a8f554c2aa972093c0187e21e12c118ce6cd6276e885cb8fb472b1679d81f7ee9c2d05d810037cde9708be12b76618dbde8710837b24ef2a17493b4c6e60fae48662c314450f19c75c3aa8d23cdb8d8039cfc012d3c9db0f7cdf362ce23129178790d8811c11494bae78db60019b7f4037fa4d268fce6bce25b03b3176905d6785f1837f16c73ebb45488261b148e5525f77e994d3f8847166e3cafb93927d32074303f97f384281b7657dc1dbbce6739986ecb88b958c4b0f82dd76fc102db85272b2adc661566acf8cecd5a37be7c86201377da3fb27b59a6ddbfd1b12ecc8f86fe618ca7ddfb5e89e3e6dcd0ab9d05f97b2971f1dea6decdb8e5dc6a09ee9a50dbf98df76dc2f0b0cb83d70857080e0bff039494b05ebbe4dadc52b626f218baa4f93d2d80880c93321d6cc880dac2641bbf860e965aefabc5091caf2b4d3249d106e5a32a6d9dcc3f5b6d5605b5f29e0fe6019249b8be61e73ef1c207abef8f0de5777c2d788829f317aecedb26ec090ace43ca75260c9696058e5115747517324e8d91a20d609b9123839821c8b5b327ab9abc69c5c665c4bd8de930353c63e2a57fa5ce86c58d015f84667622e695045f59d44109e2695f50df010151590072226a725592f5cc4e71be8f2287da5866e27dbfc0ea2ff686d5244e0df62294b7551da83aa32dfb1e844f654d932e84e1ba78305b2e3d62c9bfa3cb06881c419cc67f0a13e09d7b7493347dcbdfd3af84f00054503ef8d2bc29e266292e33ab2450e2a9e5d02655ecac7ff7a91d143f7a362971e3fce4ea8f2392d6164a0d17ca0ae35ff61122933f4599d725024949473bfc179fe7f9aeb10fecb71c2d2a40ecb4ae1cb074e18438018f6f1525726fab5ade73c4026751f0a37afc2c9e3e0d5a54bddb38ba7c9ab5d61e9508446af4306b421442c0a574f8f81bc70718e7aea8efa3e48f1b0b99e1067a0d4bf638118e5ddbb5cba4036f1ee669ce2dd37de7bc1d8ee5ba3b84019adcb7d270f1f116c426d55021c6adc1866e024a2c9424366d5259d6b0a7cbab5333e4fea95803b9318812d9177c62d5d26d19e303f126ce51038605d112a5f136c56a67485b6dc24749e62db0dd1a4e2958906805d74d5a9399b450d6d01932d9e2e2418ab576080eedce812011abbe2cb9f67819efb4bde108ecc8ebf1b3d10e2e4561fd8adcef381b682e754ae4aa7594891d258bb47d048359aa4ca9758f34244e6e4c58a278adff0300319446a7d984fbb21f9121bc247fbefd6063c309d958c0eaa2b5dee1c2a6503bb12afd44ea70740e0c8462110f87dcf4b43b6d487148d05ef241751439cb441b357db7ae2219dcb025c0ea3f13cf50a286c83dad8ea629701d3d59059641d2b3a129a621dcddb9f821a0e84021e68e164a5d6878498b0da4e304e3d8a26a35fd1b541d85e3642c49081a5c7638338f388f14f8c1816c27487eefc278779ead704fd4472958086cd51c210c6768e7b756ef6ca978e9262f942c9fcf0507abeca8dba8913fa103a1862b851deb3ad4980fe48a27a32b7d084b085cc28c7b47b33818c31f215c0082a8131a45bc66154022fca07f9fbec3a2f953ff778f17dbb76570f075b0a37930c507ca568de8f5b775d2884d7db842656fbb0fb2daf57aa2d000ef01c3130deb432b6cba93514b6653f4560948d2bdb9683d221eeff2e3376ef65cd332109eaf03214e76a94bec589f6dc340d328c03cbc6140c6c9393c39c15ea57ae0c299ed761f68d50c8f1f5583477c0b22fe6eddb2690eca016f8c140cd730da5ac78a9f2223b886af8aa74f2ffaa3ee96b2a5a3fa9f8ce3675086043c7de7dafca01129a82f68358148efc49e0b268fce3bdf8d6796e33b2164ce9be3e8506b8777042830082c389c2e9ffabde64b99f1ed327c83d6084487ef75003ffbffa8f384d511630b10c944134a7f27b0039c03b18e354f2fa946c17edae236a6a4b60ddb193a4018f99765d7e83e228e2a639461bf41e0b0b47a7efc5cf0aeae9e5f6ced307b2809fb29576bc643436bf81082cc91562e46ca03586004bd9e61492ef62a8a438d1ed2aaaa984ea9046378e6e16272f7c5082ac5a61f42e11f884694d8bb61ec02250e6aa207950c0e9dba918aeabb959447e732ec672c88f75d47bcbfc189cc611e2e916364ecf79d3f1eaeed7f0df433bc50c3c02a63b72f1b45793f8c8cfdb21d3b9a28454bd97948613b877d3dc9cfd69130f070b2d2376300c4a1303dc5ee24076c0adfbd73e45a4a270f0484f56f7ad39023a4c57aec70a503646b89f5ef191b824963ec1501edcac3edf6c0729dc9dba7a62a3ff3aef21fbab70572b7210d9cf6afe09334329f236204c9830a65b03fb534f38d8aded437b008238ebd7302e805c41371f1266bdaaf95ce284b8e5aa532c4a23b09d8c485fa3d04a66d660995c8459ae231cefa318c0445ada5c1a752d805fc796326537de8007951b7da01f91daa9f50b14938d4ee1bc94e95bc7c98db2299592ecda1f26df375bbe40a0033959d2b1bae4fe99fc1192a0e3067e3b1ec2ac024e50a51bdcff735710eca2714ec3c964a9edb8166e8521d52eb269c2649c520c9dbce9e0f1c7d0e2f2e0b17b5ecce333dc6f6229f065b2f8dff845857f58a1193cb7edeeeb7c090bfd30e232fe66df34fcb61aadf233a346bda9061d128ae2cd06c25eb161382189eb1e92848d2ef16fa62ad8f01adf4cd437e15452228367e63feffd2832bda6cbdb42d078382caa6e469db47e4f2de3250d0ce4d829c8fcef92eac8e49989586ad05ab1ec676ff277032e77c03df654c77684b5ff8d99cac8adf95cf52ed545f70d30f02d58fb9b72db10135e570785e3c444c31a86e6683c13e25de1669337eb60a831724fdfea0c242a7684e881a9445434a8b004e0737a1849857cd5055ebd9e53f2b9f77ac6c6d6ade940c7d0da692e31526be610c47e51239017d62f5858d81330eb9977b3bc8aecab272b4e5368ea8cefd1dbdca495268aa37e9f555dee0fbd6208d2f217f2d8ea47b33f88a5bedbcf0c92ec2d6782ce92e04927cfc2fff29791bad3b44b984ec2b20b6e5e4177c62afcee50b8a12094dffff7e24ee4ea49a7a017f6253df2dffbddebf6a52681a5fcce61dff932691abcad4809168e8bfbd700eab738a0be934ff6a746e65f393d35c4b91c9408127488aaf565076bd3417851fb2986b570222452655e1df8d36961b61d83a859c721d3e2d5494ce359a382c002ce294655a9a110d75c21095665b76d39b52cc7139167906987bf7eb2bae18285ede734693690ba714608ea2c05cf51c3519834741850f9ec9612eaf3a77e01a8cab89a628c64866621ed720698ba89dc67142d5131608bbc32b8dce8f52040f36fb0f24cee64cac63225c59f0e3c3d48b1b4f57c60c5f20205df70e81b3f6011546ef9e9a29041bfc7e96dc9f60bc9c2da6610b504e35e9fe8a2986f7eb4f09f18e3d9491ace8d9b4e3b53bf3ceb2fc5fdf2d225bff6ad4b1a635216395381c4cab4abf95fa872160d83527e2e9b8a9253e72ce50b46e8e236b165ed1327402982f80a9a1b641fec34d718d8227d5df442e2495f51b68ee657741eaa259590193cb15f5c742ca5d537f0b445107d320f63a2e07ed336b1c27f8fef2d7463c0b5e6a2225e3739ea7370540024c609fe4da0333ac173faacb7ea8b0d0c0b7fa1d3003aba571bf41e49546d024f6e1e3d2f25760eb99a37f7ea3d29dba812d2f045a6d395f1fd7680b9db19df70781eb44393479495c21525c8fb1c1d8344cb81c6de68f7bb5cf8add4fa8edd9d5b0ef2973b07ee8bdc97247feb0ad0aecb7f411b369fd8ccc0786e2d2b735117609cc3af441f74c20ef7c86a123763068c62a302c094d1b2775451287216ecd90020817f65c5a8e10299b006799169c625c2017616d95125434c7856221a5e2777d768edd3f8664203b380f15cf21545e13281a798171667d10568c6386676f4d8fb1484f2d7926f45c19e72d55b45aeb547305e9755394435105494028fdb87d00d0b4829c77c613d851381962d4f2ea023e1b12a1e9400fb4a4f318543795720ed91ac450a9eacf24e2c29489f33527df26246bec9a0a7c6a3b1c76d22f6029036b38ca39905f87f337779f199862c631110ffd7ee869f5cf0baeeea9e9b8a074b36434be6c211f48bee747b010444c9b5c2a28ce1edcbe2604ac3d4f30e5926581b67a0ed6f59eeaf40ac63a08338ec77c08ce1bd3b0037f614ebcb225c3043a913ec81444cee3de7ce6bc6de74808394a1a86e8f7f19e38591fd3e2d521923bab5bd648643b8253ede3146199fa4b91a514e28879b329240228fe9cf686163c1807ac9cc2d9bc083ef90f537bd4dbad6d2d1e2db659e88cd97c26620372321f344178d8a9f96b622dca3b7ff0b34f34d627190e8946c19c0e65f4d8abb707b5490bf3a4370aa1bd6d152be87a5d3e25382da8e2e6846682513d1eeb08f4474f3612d13664d0ff9e8d9b471a5df965d25324beef0e6582144da0f05c310bad6b3d6c98169df5fd3c4d49981b2d159aaaa01de771a8f2cbbfb50737867c711f5f0389d8c3779af6262d1dd92dbd5a4b3f2f1516bbd4a9881fb493d858a41c4ed588cab675fdf798543d489edee64de8c7441710732fb0ed9d2f2cff60cae3e0062615ea0f8729c5e6d331c07a8d9bdd6c3e278019eb5d63bc62470e520afbc73b32afa2be723c863ae3f32816f09484e5b21646b52266186d8402abf75bc28ea054fceb94e743982f5cb4ac10cd845299891b2dff0ad2f845b232c84f3fde7adb516bd263ef65bcdcf53d6beef08af0dd3a2ed740520158fc2821502123151ebcea24e779052171652a8bf5fe3822a4550727443a2aa0cf0bdad8de81e43d9681d7750d14934bc7e131cc99bd353859e236167278b4237a38139c1ba767bc77cf5556351e62d5616d1ba71c964fb9b56f930f83775fca58ee1ac09b5d8b2d33404be402ecbfafde582245ce76150269783f0802d6e5760c0b8c93dfc7efd70de95cf5497e95fa482f0ccdf887cd80854468266e86005705eda6540d8e677c151c441dcd7d0d828eb321b34223d3f117e94d29e0da0b1c74cbfacd4d7f53714a3274b154589c857501a860ce75b0651f56c61a4ec3f6a44a7fcd41c185dcb33bbfcd260b95e51d3592de4441a7a7c453904a25c0c2836d43dba07199e20d90beb3ba294b46c8e7d601c64da1a7818986f4b8099edd63d23fe357a61bae8f15bbdbcf5c997d3cb54eb6df8e7e266886c9a35f29e72bfa386f072608fbe0bb8382dc26dd3cbb6d567e294e196152f9bd070abc18049bd19c40f614b7c048389d2ab809d2aea10ae1f458e5e99d203d5d59ac9b014c402845ad77a6d5092ecaeded657473f501bcbe07e16a1c76dd195163ca0d0589a021adc5e795b71792761e3adbdbde2cbb59100011e957bf199b7b38228c25439db36b3a9ee126d6a392a6ebed6619d7ef1c5479ee12448a1cf02185c4430c4568e57deeed2d85cd1f255964991cb8152f2d63680e0a2b9c222f4db49bb3304d8c6fdd7076681223108dfd23553b3151f14e9e82488d9f61a9c2ed5edbf309bd156d9a73afff41990625db984c47d5f2e630029c2e90e4a7479f850b5af32912ef32b12f5e3e97b511467f4900cf6844c6f60cdd17a79a5cf1b96e2e482f102ba5bd44ba62cf5c157d5a8a3ed7258a4caa6ccb2cbf645647c04d97d38aa11732bad3ff547139760bc7b2450f9dc7931ffb24d0a94bd5545581c73014e15c16638fc0215c8f010612a893137bd631d9eef4db227827cec0688d555b8279471eff23b1fd16a495dbf1d0d7998160f4c5068d823eb2a0ddb58fcb09ca8e89400fb5432d5c826f63768de570f6db99f03c0c47dfccfd09509ce1edac0250a7321c9910d7e5845654cb43e9562c31a0667d7d3c560bfe0d28d6a7260ea5962fed1aa7f67d9fd1c032fefdee10d0e0c34bd825fbccab8f5f3e9e11a588f0f5e6472629972fb7d358d0793aeab24736c17fdc1330fd2a45ca85c5708e2d0d172377131d8872ee66f249ed8b03e4b3af11ea922aacfefb7a62a647fbbe0ac233d7301bc39180c2df57b9d8d08bcbb47e971d9dc6eb3a8d38094bd4e60ec96c1677c207fe087d78d0b71e0372c25322ae2a6daf3d935dd06cbc450f405d50ac38f27a177ab52d31a5e4261c18a8155ad4adb82fdf02c0053e7360c17995fd55dd992b24283e340b5288528d2190cdea5acdf8b46fa0df576166cf78f5740985999601171261b8105bfa215d536c60d5cbd7e0c393ed23842004f0861c88f88056798a28942de5d2b23355ad7c0f10b9b16e3e26c5e82ffb2b60746ed798861f1e0e3c", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000f475a2798782a4befbde35b045f9fca800000000000000000000000000000000b5d68e70b88567b1352944061f30726611521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" }, "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000ab6871f5e0c604e230000000000000000000000000000000000000000000000063e0c5faa3697e97800000000000000000000000000000000000000000000000c4edda900b7425ccc0000000000000000000000000000000000000000000000000002befd5183896c000000000000000000000000000000000000000000000004d8f5da040d18947000000000000000000000000000000000000000000000000e372e41542c47b15600000000000000000000000000000000000000000000000eb6fd9729b1572bba00000000000000000000000000000000000000000000000000020aebcd8e4cd90000000000000000000000000000000000000000000000027d1ef6bdc1bdc6fb000000000000000000000000000000000000000000000003b63834a1a171bf2700000000000000000000000000000000000000000000000007d98d752da17665000000000000000000000000000000000000000000000000000218b2af27d6fd000000000000000000000000000000000000000000000005fc5b00c064fff6aa000000000000000000000000000000000000000000000005475acb7f772fc6a300000000000000000000000000000000000000000000000d5eaea21fb7f5474c00000000000000000000000000000000000000000000000000025c5ad7f49b8206f35cbb5b62e5b765b61ee0b49a1f8770bea9d70025c2700523a6cdeffad74e2d17c339a3922f7ef70421fafba18febc1023e82b90cd74104a98902574f6d1c226c706d93043c153de5bf841a134de7656959e544c04017eceb0069aa692b1b09d09bd7a13e5ed5ec3350da8de0eb3d4bbbb261473acd3a03e1f56acf424efe0c6de5bfd3bcd37d09cc02b16d1a369712519dc1e780047008bccad88b1517ab06359968bb78d1f165250eefedd6b57eab508d2953f5240a27b3c8f21fbfafb207bef508a58b51480594683a51f7ea0ab2210a377696def48a79ff7efbaa8e0823f41ad197d6b1ee7ee556998280123ec1b9aaae6dd280edf895997e038d1c1c25a9dc3e528367cd6ddf78257f8f4a4c8c13a20283316c3eb5d11cb539b99e5806618d8b39dba1be4c13bd33f165d43ef169b3b372eea8224a06ae43caa0d921207c4f8a36cdc97347efe815d7f3321ce22eb52b6258a38b98f20205e737b6f61ec9e8a86e5cb149f99b66f85d8a15b805fb61123bb0b5dec773cb3b297fe0ee07c1cdbd49dbcb2961b3969e1607c7518a382930487888e32d9bed12a4b7298a26da1f07cba3368953cf9f4a5c127d3fb11a2477adfeebc6be03f27dde2c51742d449697cacf031c884d94c35016ade6bbffa6a1ded069664af9617665c4ed7f09333f51d61f47a9365621869531df3ff6fdde13d7571858c89dad97081a5d032db062ed586adde96838ac6e2c39cb5d50c316062bf90f0b49fa7acd55e6ba1620d7097883c0c1432d691f8ff8fcd8c8075c631e9d6268bed9ca37f2e49bdcf215c9022b55733861a9e7bb732ad6df58cfd3446e7ec2067b2746836af4f7f89c06f25e697f3401aa322174f68a210066a5b63035baed84e1406dc44ec30e74d91129f27facb96b2eb96dbda62db646bbfaa784d1a5fba7dba608196df258f5020c01e07ebef101e89b91d5dec206ff84318ba1638547fe25aec6b87d674308840cae64a836af4ef9ea8cd2ab455927f23633e086b6f6b741485e76bef2b6cc032bb4e67d1a535f5a810fd3cc9899368676a27a90f311557be62e196f20ff52ef13f86ea9ba08fa7eaabb18c6b1afbb4f7771d605e4b1f2be6fe991394d0e2f0626490f444e9ea9fbd75efdb2a25d56667fae74512e2fc0acbd49afee2b2c23e72373c15067632bf4180a0cdc54e9c9394e7154b900a48eba42ba1f44b1d9fc72128f4b3bedaecb9e70e27ed92a46717fb202dac7e349a24736229c5c0836ea091cf2028b1a7eaa6a876513fa071249ea34cd4e1e27e33cd996b948907ebc60990d5930a69598cdf690b2c2c4a1ae184affe235d854ec81da06447e2738270b582081a0574605536f0f836c368de05a08b39fd2d65450c5c5d6d6cf1d87a7cd5a0a079aead0c9239382526011399682aca59d1ac68ee5c18aa4c3f61d13d2dc9f1940bbfceb517ddbe3d02b950661d24e7ed55d0550eb3689f421b3a2a9056451192059863b2f5bbf2fe06feab1df4d0b2165f90a3dbafeca86e3619716ccf0980bf1fe0721c73d5cfcefd66b41e50aa7eadabdd8c455f2779c3b3398132087261797ac8754b7044232941f4d74818e58ed96ec7f2caaf282f7a531758636d2020f7ad743013efcd97a076ead680b17dd244cb55039f07344e15a1a8e2f38870a29edba2439b0837a103e1a8c00e450006e6b589a654b8ccbefd5de0045c8e21c1545fa845e862ab714a464c0b6e00265beabcbaef383504fcf2c5f80cd7360b10d5c24f04fa84e22c6a8e81696a7d4debd55b2058c64ee4339f7e1acb1c8674f2ba9e03d761ec3bc086bf76ce20afa4cbf8328033c71e096a1b21e3e1bbb980c260b3e581b7f7afe02b1ea39b3b73031d0b96080204bffcbf3a04b43f5365bdb249ae301120746252ac7b763df1d20111dc3827f858fc8b12665c07364f9e6042b8adb4df253af298a3b669077f4aeac1aa8f6cc0b44af53c3c1fa4cc56a075d2857a42d3b05cc24d8e929608bb93e738fd680f4cd8af7e6506808ecef185b2b2ebdc44cb7a9ac2efa142c54b09cd054bda803a047c314060dc259796a51bea1238b6879fdf41ffc4269cbc9859c6220dd57373fadcfcda2548125a04d9447280a0696340db71c34a8cabeaf6636088c0331952ccce04a506c57ad047ad7bb9222907f98f62436ed0491d278b5ed7b273a27601258a9ff54c7ac8d3dbb68cf3f1749e0a4c950d439213e3bf0524945627736e7e8fe7f07029b26979c722849f62eedf78929c852c7bd06c10d5a69015ff5983519c3e0937bc3c825472e519547273409dd5643ecec7d1630979937792e5ed4b7cfaa110c7d87ddb2e600ae1cba2ba7b5464a14183ed799350e4d18c512d14b66266eedafd5d32e1141c543d6db248547031da4097158b87ecb7845a8f659c5c02b9ea2ac6f636f3e3d4015739223049f86005714294ac037d14a7ccdac80e89882751314baa8dda7bcb024504e2e27c6a101a3035ff6ef42b99d56c20c824d839f3d98e3131932867267d947c02bb2270046b020798f033e718352799c0cf9d26c86961efbf43562dc263b9d250e36aaa19d199f1d2789110fa14b42d0cd40a491ba99682f72fa9b301d08528b0cd55651c7fecb9fc18e4a725043f251aa9b69be5d31aaee787dc8771fbf31252a90ad86466fbc0eacd6a4cd0d6b94c53ab9fb4c6398b68c5077d5fda03d7e7a1d60824bd6a0859b7fd17c5e3ea922c212b33b1b36a7c5c060eecdec697ba38c0f968500d89f9543e48accf3511e6d0fbb18f089137a7db279c6abe326dd15532f7ce125605cbd676ab45449b2ac04d190420e48f1c0a029eda9944c2b651f681194348f35c5946265da9ae92238bb49a90f7a05e723b185bf8083ee7448ec9517320c5f10fd917b0375e3c88136b03d916220b42d3ef345c9617873212e1e6d1af9a175a5da49ca79114ab1bd3fa654bb9dd1cb9a287c2278f367cc7ef0e35d064982209f67c25194f6d480bd1af6da9c21e590bf77f8e556bc82b3f36629e51ba0bd9e8e8f2a59d7bbbc20be61ddd3d78efba879719d28c116762ad44f7a1c17212136d31493bc280107ecfc3e7f56a3155e6a8dc95ac459573523c5db8e9726948905e785fae860f81331108d539f7f1b8b4c914936d1a06976787a461aed10fcc5ee13a48ab03e06c2eb6d80964b9faad63932663a5a02cbd28b40f159d3191fed983fd4a451dfc47cf6bc11201e2d2faf3049e8ebfb3c7422ca072136f3294c96e6a0fd7348adc0080aa0cffa5ae817a64bf74e78406e6f12e2ce53b20f24795f8d35edf71c5de2ab175685fab6f9c73dc06620bda26f67208c9774b7e52526e6da846f50a867b53b0c964535f5e52c14958463d58d2417c74b2a25d8012d22f20b89f69d9cafc3744aa9a3435674cc2d23c1e68cce4216211907defcc128f00687ab857061bb9fc3fb5f4d2bc70d8652f2f9cf0808c69135eab8fe2b05192d7577511195d93abab820c9f30795df0b10c7f55de58c03d19139b28f5d90128f7041b76c81e6f1444d47642da90ad4e2903cac9481dd31dec5947d5feb56114e0d1865a7bf262de8132d41340ad3aa5e1938c8692b7d9e6af1633d74a3a21d0b65ee6e74bc543b648cfeb52beb7fc0e500afbeab9bdb8a425e43efc6e0ba03f2678dbb80015852281938260095069eaeb25fcbd581d8ebd64babae79e41d11efceb26339550bbba1e36ecc3c91f71ea9f1cd23c0ca5036758e74edf7858e14ad89c7f9c8f71c3cf32fe11c37f583c932bcef67203e999e45700d3a5816870afe8a87a72d9be4d639fa57feccb778e06dbba738460fd0f511c0ad977dd06206c54e18c4a57066e49e55e207d36107037ccc095ec6fa2feae81aaaa44b88370906d542c020f0d3b0e2d80843567501f13066782c96646ccfbe031283544962216425d07ad1796b75e2e9d402062083da2d671d32e1c34e3647bad0322067911656aa8439e56e08c30a1384877c1a32e69c3440d5a25f687c175239ee6cc75c04d11f5e379d21a7e8b32943f9772db2e168f4f1b7b087ee8b7119fd1f18aec2269e83608b1d071924f9f2dcbbfcd070bab6d7f5a447e3a3da752065677b64921849ccadc866212b4a74dfdfe4b8430bdb74f4a9f17a6a99495f0886b442625f2547a6f4500d0dd6f5416d1f40310b3fb8ca4a72861597ffa93f0f9c8e3955fa2bf139bd084522a6726933ffd9e47077365bcf03a3ee29f99edad41cce14458f0684554a86d665240423b236917bcdb313a210c1946d234dd1fe542e742668fd0d64b6ee71070db72111e430e1f27beaba2482951428015889eaad1482b4c2b3157c8eb633ce89bdb8cc75ec48b40e2c50fc5c46e897efa49a7e4b535b16dba729a619462465eef8f964af5497553815acb5545f74d5d677d2c0c1e339416b9c219e50a898ee88e1fb2f1db566e9696f253d79f68772a0aecc07896d2e49332c0f18fd89635440b49b7a80d33da8557759c2fb146fb125ae93087fc2b6749e6a0726f493c7ea11b9d67a1be2dbf04a50105d2b489c75a2efcad8658fb92521580df1b992af777cb0e3ba2c825243eb0e6bdf3d0a6978d1411104b59a46f0bf720a79728ba37648d5d60c06f92608a4dbf1c99fc3e449de7549a7e50762d0232c2b116994593bf466bb0dee85010662193d8ec2fd8a25ecdb2a7b34f7478aed831b3025e984f0ada44ea0333d2d72380d11ea5c1b5db3d182548eb261b77b13181f48514e3f03784963faaaafe860688661a8bbdce565ead9184b182a9802311d19069af44ad2e08496a94d229f869f5a2830c420d4e1bde78c230d8da4384b220c88dc1324a1b82f54e16647c431dd5011a1421c785e130013cfd882192d72f01374b6f2b7f6011bb2194cb772e337033fd01038f1d77810506f0f93aa4eae822649b30fff4c315bcd99b6cb7bc282d60e8aa24fa71bc754482ab58e0d721c3c2ec839f0f8b1c83585fe7ebc00368d3baa4a381440565216c8d2bd04fb96d32311649b8a7a3ca661a0cf53114a8fb32d33cad95318a6154178abc8c06440e21d22e5c70296141521743a8a37855d70b311927b84716de2f8fc092c91eb3a7a1f221f26aea2f88548f3af4b19fc475bcf49786adfc8d7e7349e1693548f512a221f381f9587f1fd3cf3cd069024a1170feafba8229cebb8923a7ab8fbd856196217b589aa128af06c775f25ed9b3aa9bc23803fa5965ee8b7794206585ab63ebc119c919779bcd9b1e024982c6fb199b1679c9af2da8f200348f68ec3b2a00c0f17a15318a649ae59bada674e3a149bb1d6431461411e2df3e2aa1958b722048b16a07dd414de7958c407c3eb8c760779358ec1e2439b58b8d00378bdd2e9e9450de9e777da0eebf79d558d029019a16d1f8c076fa5294863dda329ececee07020721d000c002b15cc3fd3cbf0f0fbc80d75c1bb53ae1b7a244fa507318c57e4c2d6fafb6038ee34355adf14f0363881911843ba3c655934688976f48ac38ca4e162f5b94463b38687726d27320a2d8c97ff9d8a79e81261babee9852ad0958c210d8ae1fe24253e9229b410ba1f11ab4cad40642dd7c818aff33430752c5f36205ef43d3378c9968b0b16a881404aa643c3d555f84b78446cb9bec133cbc10ad00b9207a883ec2d82bad1051a9c39453d1b63188f2c5c052643e597db86bdd1621806a9ef2c04e8cbd9fdaba392d0fc997cf5a49811bb4c8b68f104ccac280541ac123db803c502457797ba6ef0da3b43641d8406b78f1007c09560d45062a65259bc7636f785729b40176ebd0ca1c6f2ffdb9ef2915602c4a6275ac8fa7cbe71adcd397d6043297be672053abf8339f90c137a367ec7d6a782a51b8285f63721916d859797c32299e9413788fc27175800859f9bfc7d3939d4f6b5cc9777b2f198710723aaa5fb5f57450a5ceb88a674796d19ce70fab58e730240326a016922b06a453d8c9f93c4f6a13bb87c227a297ba159e626f5823114a76cd7b0f39410cc25f0afd48083c62151fbaa1853b27d7a17fa1b9e2fa28a284200bf12a6f5a2f550ca3ccff9b6327e4f7d9983a448cae3e7f9ca4e628df133a58dfe58d741b284a4e0b987dcb8dccd1f21874cd434280feb0e942f8f0e5ad8fed96b73bd26f024f4f697877b9a2164985542d2969f52a5b57c433eea4f07a445aa9e833a37f15dc67773cbb58f86cc70a1b5779b019ad98af907b00c0a1fec4492d26e38cd72085f1cba3e5e9c54abcd00c2108dd54aef936879809260609e0787ae4bea1f618a9df278efdd7f15dfe9247dc384fa4f971588fa356b4e67377e8fb6251408f2b82baac18f833a2875d3bc642a5838432ce511b38005fb7024bc01688ef3f670da2fd6a5794ae972da048893816c19549b73421e0d585751496ed222a7cfbca1d415320f214e7cee462d0689ffc31bd05f4c9a5ea3b1791bce9fcbf374d2d14004ca233a4b174ab409e968d6399606527b624923f381eb8c17b2805dfab7cca280308a2b5af4f1aab2d05e78198ee4536f2deebeafec0eebb33df157fdbbace2e747e434240c5cfe49275180859832b2d69923bf08c0d597d9112eeb73568c91fd8ea19778ae26aa6a872c78c89c957d2518154b689bab59b2ffe5bc04361ae131dd276df1a28b2e06a9df883220bcf76424cf289b94d5bd90056ca2a6828f210f1ff74decbe9a8961ee9c5963876aa8face86ca89e9b038fbc5b1ba5bc962c128107f963cd0d439c4fdb465eb7834c943f21c1f3d46a7f057b75b42aac8c472a5c5521abd8d5913f59e4798f9edd0bbe643ab68d2d5f2d4d7b7dba52dd5c8c01fc9c7bdbc80873c54c494f37b6f6fa4536f37b101a16a7da7ac943fe0f666304c7a6659b382bfb94977ea2f22e244f5224369d6025dabac1e08614146ee2501ddaefc72b74b56f7f72ee8d908bc52a354105655e85edb6dc768a83f3dfc0a317c66db9a34532d5f7356579f0741dacf256f0af84798ee17836c4a40fe4b5cc04c199f7039d38a4bc5bc7f2d99fba60a0fdca779b92f02ad5aaf76f1fdefb091381e6eb635e8634516faf93dc50ee8b484c07b917f28d75a657b00a68ba0b71015191755b4d819437281f7565299d9d5ab193750340c3436c4e1ec8af65c201101a107fb877fdcdbd711cc0e888f6a3918bbab8780e9651d6c8bc29d473a25a263d53cc7805cea42498373014e822341492cb0b5846f3651c4de21d6a02b449249f136615c3fa59efaab74793ee99dea1346e28a861afe2f931757642bd3ea028fac43df603f0eef3eb0387cd88048d715e733dc835545af0a970b030f6bcd40739b526796af9c7636fc1e0023beae25886eb1ec9fb69465f36cb8db0143282114a980ff547ceae0ae0328b0a2ff83894cc7efb1d8d3268e623e20257143ddf1690c3f89ba0cea9077e7448601c66a68816c5d7adb596e9df73a9e7d4f3b5151e8e6dba9f96d00681e644aa6cc33f2c88c76c3f204f9e0f4abe5667921cc5dd0a77e105a39ddd0f53525089e44c33468ab32cf7761380760d7c24966908f7291f3cb7b08e1addf167d9a5eb038f1f433e711c6e67adc478606f09c33400928b09658bcc54c224cdca2dae3dba6278e91f8a416474312cd9325be5c38d7f5d7e07f54faf13c87c1b95c0f7f21f9f879644988df52091918a8fb17defcd05035b2c01c5516e3c3a8cb16dbfc84ebf730cc91c289ae1d9a2247ef46867c4c3f8b214c7e231c7382212bcfd3bdfba84e5befd2d04ead43d97823562f066e832f1f31e830d7e9747675bde52ae766e1b6f1fd1868b78f7acd5e595aea09c9dfadbd42088b2218775b734708929cd68d07da0d0544236991c2b7cf3b1c8a535fc2a741e04a56884e7fec5c8b37e770b9ce037d6d9e5963f682452e8d786a57ea1ca4c11dafb71c383f1751378b133a730dff48da6d638942abb532cbcc712975bb260159681bc8d1480de29c8d1791ececbebef1dd47e7055855bd32ab876af784fad1175e04f583e93f827e3336d431ddf5f2a9347290732acb2c1e70b32aa39a1b61535d18522b8e335ee209aef0c81c3ba6c2902fb07d3d35c978a48c6242a5c0a0e5a2a7ab8189202cad4eef209e352db7c4548e813f6479e5ae2cdb3eaf32b601bf6758cf1a0678a0daf9120dc0fa7c4a30f5602f2ae2cd64d49f6cce7de341603a1e9661d5b7553dd96174da8b0763cb968592477ac917adc4dafb94d89522c29bcb39fadde0390e904c2638779567407ab30dabeb7f8beb0b49947dc9e8fe7291197bd5ebbf948c0326263c7cb67a772e3ffefa7d93deb6fe34c83db575bd913e74272a40222380713812899c64497ddd2f0ffa40bd9435ccd8dcf07812318062d0ce0a9887f24c623352f28434512a3f02497d08f70e2acd92c6f9c7db9c70ce84edc6d0b2e6a85061ecbbb6d524d77900e545869d585f02e66c11c5683262a89acb3a1d01fbc647be2e4ff6850475f86dbbf3a5533cddb04f9f759ed3f8c1a58e6111f8ab44f6eb753ce9b76b08ec2c1ca1e8e4a0b12f2468685e97514f80084f9ec72296c13c390174cb7058a1cb652524e152af41bdd9db80f4b26159d019c5915c863e55429c22b5df53f3c2c708d16dcddf9ca6144b3b12c102f6e4726abcb1e662e072858dd31ebcb6fec808ae3818a0e1529bd7f967ae9e4965f942953dd62008a7880ab0f25d1aba340e932e7ca5cfc654d05efae33677e86e73c1e16c255cadb04bfeb1f46d2a57eda2e9e0125c6a25e611177db2f81b52e4ac6270c29e8222702c6a2590dc3853e75a6dc26aad0dc20e13b71d273946564d9d7258d3a80b86cb1a2b655be37f8b0f78265f6bb866571b384a0792cd7ccd6f9f61514364c60b0d46d272ddf70cc26a62f1132e1edd6f617e815dadc8febd9a74e2e645c1d5c5e21b5c8747ef39578966822fb78829299d4ffe3e411f3d3bd5f092e06ad539341a69ee6302e31511830369f8bf22dd7204099269cf871cc0997191f1a2d248d13fcc11672953d9f5ace68497ee899bfb32b4b7bbeb46047e4ef821402516b83cb78caadfadd9fc6c12a398d99f1b878ec66b515e7483a9a0cbccb083041b2aac57d88f01d6d47b3ae84186a84792b52151c25f6587e21f8e6b07b1c0fc000b43896466542644603e14a650e16686d853e62db267e023569d5469c0809a8d114f1007b7827216e65fafee4b603e959641d11636efd348dbd46ed560624ef25c04f501716ef93cee02aad775bd9ee6919852491f3d29b942372e1882085117390b7aeab2e50367937357f59b9f3a24d1c14cc6497dbc0818fbed5041553c7bc5740d0e9aef2bf62243c1ca12f8567c9d3f39a40110770253767fa7c26a871d60e27b75d9e142b1162a4130343e788148124d712988045ef96d48f8920e88ff5c9f60d09d2f77ba9b403bf6c6d0fc0799048710792a96035c73a3e450c283036736b9628210c2497b080c5abc5372666768ca864d18a6f203fed35cf272622ddb9a3970878f471c80de989f90ebd8cea6120f8bd4c593763bdbc5a98058b8395cb3225c7060bb36acc2d22c0338edaac7d75befe39ac4206479c86fa237ab01ca34b0328eb6699049f29188e92e7354e69dd727f960802fefe99c3d80c097dd8728bf5d5825041d67e5ac9858d748bf30271341377176ee8bca796f60e5c92647f6871a1579155741c7b634c268068d9941eb9acd84569018e4de82811c568bf95b24c04cb3cda2a83b14307e668bb54db1a2569360d0caae00498f91e0494092814900d31c78e68f63f06cb09a782d204a10067a78a04801888f8b707e647e4d114ea7c1e7faba4176bd2977b7c1d0e97d6e3ed976c66930ea8d1ea278f3899dc95fad95147a22de703ff82865da597000525a1bd0656d82a84f5d2255bca0b9bef6038b61f0e92a5ba1395707daa095674120eec99fc8ac392eb5a178588131cc52f9e6d45d766712bb3eaf094693a5af7a4f746175bf7aecd09831d846931480245a862576fd3a006b07b1fd9c2db51db74543e275d9e7cc26f2d27452fd95e45041aaedc81d99067f7567db1fe9759e84b4fb2d1bd93bfefdce805a6cabc48654d9d38ebf014ecc33eab6dfe7fbe12b4007cb490b235982a55a611fdeb14cbd17ae02f6dbddc8b4ab0e8209312dfc80292b4c072401ff7336ce8055b3fba6b7058057342f5e55266f9bf87220d9c9ac9fe0853f3d4f4d019d829195d64b5e10165b80751e8ed7945c9a19a7960580b7f3a9d7a6065ce6a26fd3128a6e7e2d8d168db7ce213fc75b4315145a583fdcab1de9a71a41fda973ff1490721ae0f3ee28441b6a46876cebe7b6d692d551bd2746bef1fd28623c1af91c020c7c80ce66224b03e7b9106196f94b8e0c9b5ed67af49449aa45213b412de6e24c842e747291b84fa0553bd0fae951fdf6e70d9412ef0ba752166a5dcf09ecb079b9de21279373de98b9ca059e22817390f90680d8d338d7e35d0dc81751a500d4bd7104c7cc8e52af19d74caa3d6928ce9b0bca085889fbf59a86959c2dcdb13df2f80658bacae156b7f1f4865db24b07fbb6b27942099f9d61e57ae43c3c723bfd52acc8ffaf4fd9cb1147322dc31f7ec5671c5f0ca5c5155ed6d9f2e2413248241722317c262c5bc54875d886f0d90780674e073c5f7f921a6666e3048cd16d5814372e0236fe69a63a4f7118d7eb75bc4465dd01181c9a197ea72f8307c2841f6397a9baab5a97fb04e109006d8127d604f0b7d6736162c40bd8b1c09221e0922d2139987796499a7fb097c9d0a4671b898532936cccadae32376c93dfb059ad2915af4a690d40a516c72d74ce5d4ab9e752f58873ea88227546e021f701702aac2d40191a410dea52f40a4bb996be8002e1c57413e0dea9914098f8a880a8c387b2e3309533a4c07aa51444956ed8fd2a314017814af55da77e40ff7051641fde9c392981a5e7d779529be3e0be0f6890bf19b24f53ebdbde0faf9d69f10e9fe5c469cd99083a9c572eb19b940f9b0e8094a75fc98a7131f2c923408620f3d931493d5a7f759cc6a21edbe9dbe770a109672574d8bf6aacdd48623e833296a76845bc8dc66f9e0bf4d05b889bee67c4c2852750c30d06903783da6ce172c02a8f8653d8211beecc345dc19b6f7cbcc803d66ae2128ba1ba055f761ca3a1aad5843b15c067428741c224242e5c518ffddc45193b3a5ecc922cb13c0d0a72d4130a3f278c1f463f66423e4710027d0b319048abd0a6fbb66c195b9d1452e2e0c7c0e8374b22294a2129630e2cc3dea0b98c2c13758345954fa86eab6ab710a512893425e0268e10b34ee3e2e6a4fb03040687698c97982e30bc2a8a2b2fd2248da56fa8f2105609a497670832678fbaf89a7935cf26768c2c03f443fcc9012eb0427bcaf487189c20e24956f34d23792ad3f62596d3767ac07ae38f3fd4b11324800ac6e2d023729dd5df77dc4e0cd81d0ba8e10e90a266711e69d577bd406b1e218645fa2552bf747d0d1a87fd66c7644155f6f5cba9115d114d7631cec08770d53cead9762e9eb24399c4f97d98fb3c365d158799be00cccd3d20c3b090b431b0c2f7b7c2ef333ec898e469fa101f1adf6f73815f3b09bbdad8349034f1f90f27a8046dca9070e149c1c23150321e612756a0c2c7942d7e30059be4908050ad08455affab49f27dd02ec5169cd976f4567515baecfbadbe97c382d3f1826774b8c096b6862b740081c523d47bd0b9639994b4ec95ab12845086dd5fd482538707df831ef734170cf55d329490aa2e0e49643922241739793a8c75e7386283a026efc405fe0598677d246226a3ece5958e0d48de439e944a05fc643d0641fff61f83de97b677bf4840a977ef5625bc385b7591755fe89ee851ea5d15ebf2655014e914e878ae7c1f8282c6189ecdcfd5110178786f7faa3b05de46b43cf17e113d1492df5303ceba990b322420dc3220d8d4415f89381df54d5ead445821656c02d97585ac6f793d80d33115e53d6415f05f0c5d90c3d77c3d140226be02a3ee5564c70975456bf79d426c8c33efdf706cd178ac3d687e28e4d2204547d0fd5af73db10f93e0b6c523b4ef081426af896697aad46e292ae0c5fab03ec3d0d8dbfff9127ba2e3c4eb3e7a55d92890674bd6132382df800faf6b8b407cb5e2f34f902cc48285bc99e308f2acf715a3b8f884aa21e144ea9fe2a36eadfba592775d95bfd62c6d71c3089734d67d08cb00dd85d23185796cd641089a93e19b12beac08125be0a38ea27cef99c4cdbeddb4b278caa2fb8d47e2745d5c09e83222c0efa0932bba4cb4546e28667c714e5b2b974eabe47fb29b3f78cd5b43742f101e475b51ad373772e93166104a79b4efb175dac3b442b52b9504c88304419102b169b8bacf4d47e97ab2c69e9f5654f5a0cb6f55d8fea831cb665e93bda050b2ad2bee6223c4f9377be451cb1bb4d7d5445b1eb9b58549ec07694f34940216118b0e8add0f6d3ff6199052ea7668d13954c7caa2fe6af1663263c9a6210ae0b01981a9f0fc8d9d2eeb8d92572ad287146f3bf0c20d23de8d08c40c78dad3d061c8102981be8ed68bd6f3b7bf768c40b86a06b6da762153c82d045133d3976a50aa6637a1eec72f4f886a043ee57a4e6ecf52067f7f1dd512d0829b36989687320fec9da648b374ad4e0247d1fa97700c286b7e1103f696941674e6f3a6f9ad4223aab447b6b691951dcceffdcb30f047653433c4f8a1abeab791b5ac76b18502575e60cd921ee2346e0ede35620fb30907dfbfa218913647ad415c9a48ff09305f729f2bf156c6478011766cb094981c086bab98c384f67a7dc579b811ba5c11f607412b5d8cce667aade35ed4efee23bb261c4f278e09b257d502bc61ae48712daae64de5ae2bb6d679b43fd9669b02023aa3eb4d01d10c4603bed9416d06f16bc522446aa35b063e490767fcc90a7bf197281c5c295c1e0415dc27d1e29df06d659418c6c2d19a198517016a3c76ce82fab8d540e656ec7170093c80446c42bb2ab499e9eeed746759a7814701b945e602522926c49aef0d217ba7f3ec0ac1495c4281a7b4a9e1464ce6c97788d617101c912e7918be268957e80dd37fa1c1daef5d845aae5ae93f3d73e13e95b2f974ec218a863270fb89a30f0a81f70f815e9e11d54842d2997758695e9e919c49ee9568970dd985f95ad792c4de69c602ca2a090660e70cff6f75b308d6b58fd3d41bf6c2e1ab5210914a167f161efb208999866e918d0ac040afd7f314e279aea6a9503c0fe69b94c0596ec7204937c282c824f63fa7641a2732762083c6fd099f8538f33fabde31df0ff298e4d691403845eb37769b4a7e325f7b4b90dc138807c7bf8c94b3308c8131021dbc702ec1b0d2773d0d393b5ee72fb1b8c46bf5a2d96e008f0a27bd052c6ce663df466bc10b34b779436e65c9da5839de588d9356cf64f721915fb81cf1107e5861a4fe51ceb29793e3bfd641066d690ab8a27bcd93604040376f886a2bddf62b99aa21012f03a25b1808210494fbfb7c5a6e98c9b0183db9d9366514708d725bd15d6471c37dd3530ee3ab502f0bfaf4639574de4088216596534660c852c04f73e77270a40fd0514c085057ceb30638d46641a2429e8aeb06b89a01c7a19bb9287955c17d5b7abe02aecf3f5e497b8ce13ff4402e42e044c3d270c04db487f5d00f04a0697b0af7a5e27a2a7c04d5bbd73c3e91e2ba020b861cf43676ed66ec9c18f4129399d25127fd3a40f696bb6dbccf3829d8babd3a13e6aec6f0e6bfa632bb90b26c35be6ad3e9fea820c0327735cc17a5244802c9466dd98739a06bb34e742dd044554adfcdbbe8d300cbc39c983ac1a9222513ebf33bd79f429c47d58beece61dd0536dfb22b4ee0b661069eeadf37d8c1456b05f3e19ee3e058c732c8d1ba62a6a39994da45c80490e4a53575e6a739a01d8817ced669ef6dee1dedb34984b2688cd26b1fd6e4ed748d8dba93b6ee4a21493066c1a10f7fa44f50a02206b342a5e15c483ce678c3f87fa37ae6701c4105ea18957e330b13106f6d62f55dd9e05f880741b3c98407fa02b8b956a0b42279f01abf85630fbbe613129d4a710020602b5ce04f6a3eb5c329dc34aa10af6d222c111845bb6016b2cd69f48e4edde14106fe41edd0cc43ad8800ca68c5aa644bdc46f64654b3fa561e10dc84c4612059e79b06da8e2bfb580c268e0c94f02625b1feac968586930f5fa9daa1d52e2166bc02b21f5b5a51eac5219ca1c42b6f563ed338189e34fa6c3e8d5922ca880062f1a75d8d73e3cce28ccd91097acfac61ce220ced87115497eda778f752709011be2b3aec8c33879c1b7d9f041cfea2e9f10ec121a0887e278396eb143953a261cc5d74697fb31f3b525f2129e9dce7542e906372204e07e6d68f59f2bfb9d194e059fc2816cc7b5faab72efde88c1daafb9dce629acf1bdcf7516b7551a8f0d427ae711881bb9dd56f6ddf46d96644fd2e7bd577830779ac1e706b6dc298e0b89cccbea03d22a857589fe4895f822cd5c5fd1634ffe6ef44b7dca09e57cf30126afd120fbdc1cde39aa695bdce2ca770926968c345e801bac05c163e4bbc4279d02daac4bb1a306be1941d646c5e93050adca4277432645be6a4ff50ddf5805d1070e4775d2d6e006453d6b67e718f8289e49ce80b841212e613e505452a827677548ec16fc965456db4e86d45b4d59be76f01e4ba348015b12e75937bf20", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0101cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db70000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x000000000000000000000000000000000000000000000005313d9a6e542b9b7e0000000000000000000000000000000000000000000000013903483c243ff982000000000000000000000000000000000000000000000000f652d9273b7756cb00000000000000000000000000000000000000000000000000027f630846016e0000000000000000000000000000000000000000000000065d1e5b1c24ed4fe80000000000000000000000000000000000000000000000043ee31d5f86ab9338000000000000000000000000000000000000000000000007905d0c87aec5be530000000000000000000000000000000000000000000000000002a10ac0b741cc000000000000000000000000000000000000000000000000c7b51a204d90654400000000000000000000000000000000000000000000000e008b3a4546cb0029000000000000000000000000000000000000000000000000cdbddfffc47364a700000000000000000000000000000000000000000000000000017e0e4d1ea8f200000000000000000000000000000000000000000000000f22ab1a2f5805860e00000000000000000000000000000000000000000000000590f6e2873666cf4200000000000000000000000000000000000000000000000eac6afb5d8d6b5f680000000000000000000000000000000000000000000000000002cec631c36c46032851a9dfc62b5a3386657554763bc9a25609f33ef55cb756f31505ce36e3db0a05b1409e30e6882fde08711fddd33f3a44f63da1deadbf7927d51d3365bc6f093d95d7862c4a0af8261b8bb460c50a13d0f1912f0f74eba2e0ba2aacb5acfd093e3ec1ad390f2a5ffc1edca1cba82de741b80f242560d646492a9dcd4010a402a77a5ab41d7eafe3d77f45358dc9f9606ad20a1eacc2a61cd1dd938d7193aa13930f3869b94dac758e78c545419a33fbb9a40a82673408ee7740e3a520e4cc01d958176942b6e13bc5a3f5f43bc9d14d60de8ecb191de20d42101a085305361c09bef2e2a18b396466c452ac37cd484de8bb99477f3c4498fd618ab8650a1511810f77ae32802a556df4e238f85dcb488a8949c92f1f17c9da333a39b46f7e0490d4a2682047aed8aaa555c1442aff40e17d150662232cb1f5497e9d9f75631b0570752d219754db0e7b38a8b0d900dd5eca242d745248ed72a8a78eeab691073f1f16d0f47fcfd81fa73b5b025f56a13d05da716c38c47450adc7bc6bc3e60956e58c6e5ac4e21c191f15788a58c3b9d3a4b2504028b9677abf113bb9713c0f5e556ebc4df8cc405f439d15f2e52f81ba346d5179ed44b3d5f96197f77fa021ea6893e7d50abcf176a3fe4566c19a86f25d3ae0ec886334991b36112a8b011fa0c797ebbc32a6f723304b35ef468f9aae7150ea4b8e9e77ddb1088c30e95722ccf074798477ac0073d3489aa144112f2a486b964a5ce4990487446b9980f4261268be768521cdb8db5c9d1266187af6b07048cfa2716a074a184b7fdcecdd15eaf1989dc2a067241e737e37bf4bbfe3b197f356aa5d7ec836563a147a5f031836aa1407efd201a899b782e88d1ee01439927c4d975bc0dad5905e5a943dc32631ddfd067e1391444c1aac4b17f7b0c412202687a5791c633dcd9ce1a5959f1b22e74e7e0ee123b765c3f8f4dcc838ca0668c2b760387bd583b0ee27bc72ab2e2fc3e85e45577cf5efe5fdf3dd75f23773bc720bfff9a22b4bf328c69e804509d2a5c5658290efed9e2132189f24d644c64ac69b5b6de0f8e2200918df08aa1c47feb63f57e3c01a577f13d1cf5074100a7333794ef2c30690f329837b80460e018af6c534ef2a32a489affdfc92ef782701dad0d8e4ee12094fca23e0613713d5a51ea31f6607fda990251b11427ef851117f277108eb658d2e439a47c19f204083a535a2a10bd01423a13b1dfc9e3d3ac5411ffcc863bb6ff8dc4abe59891a9a20872821232ae50dcc46d0d0669de09f69844b506bbfb8b706ed1eecf0fb10a3f79784ec36f7f79ef7240f448a97acf8ffb80ce4af683c5a5dd95607d454015f64b5a5940339083d5e4ae358b2d24d0af748b51c65cfd06e95043e74d669214c2f698b2fef5147420e7e3c0d21dc3fe5f3f96ffff1c022962c5b51eecc9a0420cb4fcf1da089596e06175e9708dc70e66ec6b61342054186e3d681b6ee2a1c3b72a5ef49f5f5631ff307ec9a01f5113096219edfa78ea5e5c3c1d0c7398519654c0f0e33d4b3ccc5a30138a672087584a448997817fa6b682125c197c7a00f69b8f94f6a1766f19d2f618bdaa3a6d393e211fbf085115184a6b29290ec4529624bd29e01730ad78c71881d157eb407d191310d3eab86732b0d7bee43e9801d020395d413c7eb94df2f0a228538de50b701674cec415a6f17e45ead136dc310df9abf4c32e790dd45dd999fb10ccbfc083c17ca40b7d15d404b78b1c9cfab1624ab6b69aa63f47bc816de70b4b841e1db7c1334d56171143ce06ee3f8ac6806831e6f8dc9687d7e02eea8bf02d5879fab57db7e7db839b60212fa840e92f52fb8788ec87b94f6f42059e8ebab894618e3dd8c7898642e1ae539bb97ff3dc810139b3596daf0513ab41747423f4dcbaf8bb067e52183192c2f9707a42955e41f675dd80d236e500ea507e0cf020e8dd63871560b83017e49b39ec085c8fe470b16b25135ad3b01518546ee240eb86d777015a51e54a3bef499cab6282c48f72e0173e779011b3d66a2efc61a882677f8271f700594d418eb06d3c56b2223ba289afc3822406eeb62a4b4dc070d3071582c1a803dcd834f5e0f3529d9d79a422d0d6edd2cd11d806f194b4e71ce3066e02a142a3a85edf93ddb1aca6ffd27a718ccb4bc77a275f4538e77c479e448c1624c14d19ff707a448f659ce39acea572f3cab520fe49079887131808623c163e90906e7ed8821e62f3c0a53679c8a37199fc5028c4c0c4fa687ab1a7714b2c5a2211a9a7417a0ec1affc4c951ff38c309cc8b3d407ec398f587e05963c19315a9839b8cf4e1f4c15c4242dd4636e574041306832a9f5c8312eb039a5bf47667edcab0a5bdf459fb3c662bac7f16c71607a5f433bc684caab8b460e92b48410e62fb2f37ceb48642c45e6b515b7a17d01608601d90544628cadc00a4ca65a85b92b82bdd05fef7edfbb605c2fcb8c02f0ccc1669e3dad8b3a9d9473961c1b3441b7b4976e33f94c3f0ad47c1479937bb1be501ba3d7ceee0db11ec5333a589692f7effdfcd7611294e455f22c5ad0f9d13b2f2141100b017ef1474b21edaafea1fc1fb4e88fcdc7c28f6d82069e8d7c41e0e977dd5fd266ab3537b6cb92f621605dfad56f51bbb60cae4550e1a888f9c1493ab4be927b692643d83c0e7550fa85aebdfac97bfc90f965a14e8cc9528062ff0a443c9f5fd1746f19bdd5b6b64457f61b06534689bf551f16a85a14436a6010a702c41bb1e0a6c2be5a38b75afc82006930287135c11e26f547bf7c58f131190097012682ba2a52106334d8968736bb338941b866c16a8753f7a533e215e22fbe7147d0612282fbfba91903fe00b4bcbb22730c795f325162d6bda74757e2cb44471b703e1883cbdd8697418b7eb070d497bee39b0dbd302796f71b560ff19cccf4c7374f196851f26786251c874dd3c9b37c8980c518e1220d59112132c30076fe67cc1467250d011b3fe659ff4b936b5c776b21d5e8d59018c31bca8c62feb6f10c683649cbdf979dd0d114604b627e830b84573f61b5b540d65c17d802b6f539a24fc894fad4752908d7bba4d35e357c0ead16a73328557b8675a2a3214cf4a332021c9a3eef6619a5107c13fdac4a2abdaf7d73d90b3694c3ef8d28528fdf338401b8379d044031ca7c841130f8885b60e557cb3ab99d3d7d1e868601dea1979b30754602912ff0f2ca0fd1ebcc4ca3889011ed814fc9fc6adde78fe1c11dc2d135fc5d12c7db4930eb2344f23181392fae51025c8f852b57d0dcbf9162c748ed56ff9ed17e51fba6ae658627ac81434801bf4aaf9b9c30d2cb4745408c68c9cee327fa2c3a914a76ff530d35b5455a2dd54428924defdb4db6a2f6b1e3e7ce480ad10b16fcde57c3655e4d793cd1f668cdfa752389cfa140aa0085e1d147e9a1bce8a9463e5c07c5f45e96e2b9d9b07c5bdff11ac093342dc1ebc2204edfc258fb3bb960b667a51031c776b1778608b80487798ef2d84e0db0f6e4e141c6fb0a8bc3b7135ba6d0d6d8836fb252d9c2f134e3e463bfbe9c63df0cad905422868774c9da5b37cc9aa725bf0519e8bd4ab8512d84b8b1819de157218e4108538080b0073b4976aa65c7d1591099a55943a8f695cae7fbf48ffad0128f902b18286fc8c2a8a3d4f3ff4ab57885c422adac648d5caa7a206a7947b1405d6023501dc48bea28fc7d71a40ed12eac227fe6b1d2911dcfdadc7e0ceaeacdcf1115c04bd06ae76d4be2cb6fba63ba55fce10015d98d91c08ed62a8b2baafa6e80bdcd637ffb9b0a591ed4fae1fef90350abcf183a52797af4c9f68990c1baa02175980c426b36aec67c01b40c434c907f828e162b48d54a90be5591dadd12676028c2aa67d120845380f6e34a3dc3089a809bdb9952681b720cd8f4c9b433e5c0ed724dfb2b1d51ac931e23b9b175bc2d86c931ed0404efc902a524b2a9e8de22cd36a8f2d393067359dc3668c940cfa463fa26d2d1282a5e478fc785a387fd20e3c90b4a0ea0524b8c98766e4a91752cb0ac4fc26d4ddc0103f927e5dac3a4a124a134b5046aa36a21046068e677e4647f1f606cba1275c6a33dbdb58c8f78500ea6f64b70219437f56e73ebcf8b9869534cfad042cbced12e29af5a184b5b310d7619382aed0adf87a9a77c3f6d0b684bb89e527d48fd0b30f6181c167e32b1233fc74536b1abe9e2ec4b8211bed9770dda095df93c96bf9f69c101611740e1bc969c001bc9ea5485d64b29894c16f9210eb37190dedaea922209cb15302bc26ffd078aff907be66328d6033ce24be8b932db43b2dcebee85ed4d35e32f2d900c2b086f3c6afcb9d6157c937be026532fa062bb9d8a324ad1c767bb48e93df27ae8a3c5713328a26df36868c72ec6b7343fae644779e861c99612ae1cacee60601bc807dd21674f54282e27f923c5dd3f9f032c60128ed051208210973e3ac059a6cc964e2e21bc07a7c8dcd5648c99fa16134625f12afc450febd3157bff300ff3cfe1390b922e2224bd6c5a072e7cf1baeb3ea670dfb4b7ed87bd935fe2d0ab01c5c02b437a8c1a325821fb1397d28184d085c217218ce9772a15504ebe62845bbd18102f444b7f42056656ac9414a9f0beab0bf7576a832699189b3f52a129b7b1093c80ed82ae46ed1654970154fdbf10afd1b20395ee2982a25f65a8a0355c507e9bf351a1d9ab88140cfb63ef0e31bc4ff85100d2d64b8fea8153f8f03ce756874b7dee3e33db736d3014a52eeb4ba84f035ccabc22fdd930d015fc101b9a24e5e9db4b182b82096d17f7b39c4419a2e30003732ee53a2a961c4d1ee1e7df59284a41368ad005b8be0f5720f914bbc82f5e814a2e6d79e730e96653020b653d5eb54c93bc2dec97c0a29b62f3483b8c54112c31b9a17e6f9bed56f361d1296b42bea1371c13fe380d4bf7b7cebf3de5994f900f778e8c3a57e8aa6fb2ff731444d8b174dfea3f6fd8c8ca8105eca56b8d23027b15f04baab984ef49e2f13acad5025d8d1b15e2e64fc0a424bc589b2b5e1db37855bff6eb56a73db33031708cdbaf9c669cd7b650db18d2975f2fb55aa50b455f946f573d64b068d68228275d24daf2fc2634b9963f4888a89222199b1617d337ceaca57592d7253461c5bcaedcc413b455aa91785bc842c533d705632454d571583e7526a8cddca2316c55131317730a98ebd450ccc06d7280fe9448bbf865c73d79b20a40d0fa6d312f28950279d79ae13503f1c87c4eb4081300aa2ec70b6aebd36f3a911cf96b32dfd80e60b3e1e434347bbba00b797102eb56116bba3d7beac36a6d75b3670eb271c53c9c0806db8d51122a55f7bd660546a9bdce2babe09e62387bbf362020d1fbca41ba60c7a6ea9e31f5888293eba6610c9d03d86eb3322b37e5a9c1a17722acb06be54c8ddcd5d4b5ee8c950a19aac715fc02454f8508cb9ab4629f5647e0e9278290b3a0331ecf4a6a4dc28ff9af971e3477c1f141e587e6ae88e14e088235aa1590c55581df682e15f236d54bd804b50d91dcb0c4979d29a32642044d0080bb9e42d2e20180083e4d80c200358074dc749e03c06fdb3f16e8afb29843f1ac767a8436915faa3cfb72a4e215c1507254cbc8d3cd1e85faf93eacc407af20e4aa4dba393c436361a04397a3e92dbd609442471acc7ba6ec5ca3d8c70985125c5256898ac7a525f33019072748ef8ec24a4f75c9d4e8c3c20f70e863071d72ebc98bdae976998c290b16ea2b86187019f6c367bcaeedcfac53b54ce49fa96082e6e6f27fa07097659f90b79e38bdeeb24ec718cbfc5567937e15867902ce81576b44b9766484315c6ca56b6a5e1776f791c37473719e0fffd72149d9c2b3819cac4c62627a3cef8c719b0115b37a5d0ce55e0a0cbd1df5cd05d9af4afa26c119a52bdb434284430f43cdee3c95ffa79aa15caa494c7c72c730d72084c17221eba17e30227c699f53c0e64c439cfc61f397450ce5c1adb973f1f96a59a677427647beafd478e15081391afae06d5f95634062600695209751465d716d4ba052c2aa975f1dcbfb41eba65d44a7e07ce96c7934d9c461cf343b9958f40be55621badc077e730d30e40f59297f148455bde7c36300eb70a2ed7283e525d6a50812e5dabea9ac4c981731fbc314fa9c54c8a133131d2b4ab4cfa96c5efc31aa5ab0fa2e1d87bf6eea08d17099bcf1831e49c99902957cd14811a58f1281a5edb560308ddda5de1767163db2ac5c77e02a910a34d3159542f63c0d7fe1bb0005bff2b55c7797fa2d51e5e0a42a3dbad55be2690827c9bf126ab3015fc01b953a85b086967b1bb9d73af5a8abbe120c01bf8106f57ef1ff75d175c4f265ba7db8ec30e603bb046abf3d48b1436f3854db422c9cb232b92427bf121304d7e4bd9cf9618cace3a2785ee002320b8bb0c2d0d3a1d7b44e9636e9638207e3068411aa5a4226eda54e1e2e2cd72de7b3af6abd4a874c04f834f8a6b7563a1457b74eed13f089978d3bfc7e75282edbe84a65cc4d3ba4b2a40068f45da4fde51c250a61a842e3ecd39064275b66223645a0721701baf9b71d101dbebc2f605881cc9a327040f5557d835211325c034a4b41ea58e10902dab630ef06fcb777c601a28e7a1e91f782f2b7f6774104aaa7e77e720c605504d2a623cce19419e01b2749c2fa88722c5fc5faaa7897e91b598a9979577506dcf54e1aedc9ea33fdcc85d76d07c950f467c13320baa0a4c8ab1b2d0523d45ec9167617ae703ce6148dda0602977420a66fda256a321a75cb4a85ae296bf5899f00ef3146e1ca9e2d47d780faa5d822d6c4e1f29cf4380a0f80594032967cb90f300607c63a4e8e78b3075f61d4eb20a6055d11426fc4d8c01fb31b9fc47e6acbf25b4353eef352f642ab1a7f4ed8e0d24e0006d6019dd6d6e35d53c2c08d73bcfe6a138549d6a35fabb911a247d860d18c75fb954e1ff05d39603748b2b2eb67a70440a2043380ee66d028686267c0a982fd7b8e1080e23b0049c40f49e6b4a2574a3260c7fe9b03d3a486e27905b06a2bdfb9db75c5c005df6cdc95a353c88d99e9993e0fd7bd3652ebaca1da2e121abe5d833ad18457216aa39d94d26dd0ee370e8f9d74ebfdfde6b077f317bf607e748f8cd930a9e23acc1dd4dfd353e3cf1ad59efb65b969933a6f2214d347f003fd18c7b54d22f392a9ab1e228600843f5beb630736308f0922473bd712a2f021bd0980f9c15841804d33f7e1611fd3e50bdbd0535632222610af3a10c93c8040cae78f0faa973ffcc633f2420ac5af157288c59c9cae2815627017ae2cb290e625a6fecdf1747aa56ffb74a28b618654cbfa9b34fd5da3dc75a20ccdb20ca2d2c490dd0e19a68e7d7246fee9aeb239f0e983762acb98b89a0a206d5c8db3e26e2f1eeec9290be1ebf3078709f2f1c6573c07eee4f67eaf8aed4fab0c9bc1f252a496548157fbd3073fac78985de108ecea7c014b70101aa0a820ef520c4d9274c5708e35def891f8fdd8d911a78400db021e4d1d991a2b6e89d580200b92510f7c1bcfbdabb44081e35aae69936f1386e278220603c34385c0b2eee0ff6d709c751d6678181eecf010c35ac5d976a5bae5a45c6281489db801061d13c521e14d9a5ae5dfa502cd4f469e996e22a67e9b9d26fd05a2590e551fe0967c5a36d2dc181947d6bd4ceb14344b08ddd82876f6c9a1c81458767892e10d9befc8f5203aecec36a058a460c92333f96050a459d9dc67e297fa884b500f18b25d7e2d7001c2ff2088f5a0e009cbcb5799e9f76bb26030294cfa941069b908a746007d112dea7b8822219d16260e8bd94bcd6caeeafbc577e6e91d5e3cd214335b12364286261f1ef830b57805b05336e64e4309891fd910901236abffda2a40b261d6e2d5dd598aaf8a403272074f58006aa6063e826e80710c8bdb33e3ace794460b624e5d92f16711c0d476b9fd47a44937762d349782ee891245ef7c1467e761e972bdd93616cda73439cd15fac16c91b7159eaf77ed61999696b1c51a3c95b830b26c3b63ec1d5ca06932fd8c4c6d315ca4dbb7a5dc33b7d0077739f8cf0b4300526c11ae401cff33cfdf300b319d25f6268fd1c646acc15f24c600971aeb4b0b71d7fd0db6a5b55a80640d08fd687756bd0861f0582a91ba67c4b68136b93ad410bf9d6fddbce8d1ed7620df43a06b0c924da7fc0c81f340518c0bdf29dae73c600c5e616935810b62ea41fecfe3e9b384d6854c5c3d16248cf10441d22354ef211d35afb0eef9009ec93cc0ecfe4dd122488342d5e5bda730e75fec2338826f507a7cade523fd6fd0f2550b32e090e147ccd32dbb2776fbda83fdb13399f60ea183e73fba3d143ea64137fdef5d92ef69990ceedd9e8eb7415479bbda31e29c5083115cbb8166306e7540b94a97bdd8bd801a14cb439175018d7b0e18f50a0441266ed58f2d80f443c804b9aca5c3718c712ea5dbfd695e9ce3b342bcf3855140c897ba837ff675271d7b54cc0463eae14577aa26b4722267b0e579a5398692207414bd26d362d504eb65ce8b5ca0f1d606236744e2b7446b1ce7f93f6a8ae6f1f41fed8b6a2be3fb85d0f1044cbaa3a093d5e1bfb965ea3c0d1d5b5ef28e2542b0e376b551b02c55594f61aa992c1b25bcbe21cd7b9628670ff840540afeb861dcb876ffd83eb2dd63c3446b95c2ef69691cbbffd832800e05219195269c5c82846c130f1c1c59760fc39af0c93dab5bc7b24b325078d528c1c5622f72157fb2c9725b1f6719b6337dd0bc0bdc101573369b02311da230861af6c434cc5cc4030406c36fab4cf096e344d9427e5cf2c445afc9bfea608858bd33a381788ab860fe8f5aaf219ab054c25a84c3d2bd338730f030cb94cf0d0deaaf1015251ce1425b44b04217d53c38cd810af54d704cbb2a3a945f2beab57d69fa34042c0c2c20e2a38e9a33f4933c1c529924fa284e5aa9cdf3e036cfb3bb3ac3099b5a16b8c03f73381a885c105224ef28b5cc63e0f121f77bba6f1b2c9c58c7e0eec024eb315652bb2d3c4224be7c7d63fdb8ed6518812865f1137a8b31cc671489a6e5a172fd108241658326df986cda04d9ff5e123f9da24a93bf06f9f6f2172e8bf9a9304db7631c3efb2853ecf7ac0776f718aa2427af6bc0ed6ef22f0b5aa593d0cb210d9842d2cf9d0913161634e2cc3b236af9a318aab4bdda28a9c9126199f5a431554e47333df95dbe73c7013b1191590c38eb17dfcecb0b85854eadc685bfe871f26f230217491079c7ee3f2a3b96a589a0dfc7098c54cfd56d8fe6a34165da8144fb2bc30b8ba2eda5319037fefbb4c0dddbd1f5996cd639acb46fb15eb39d8278563e3ccebec5f13759febde6298927cd0f63d16c2fe5ddeaa40e8e7322db50260785eb9cc878dde1c7346817aba0f622563cc539d7972a4c96bbe58a245ef293d5d90c84a5c365de8bd7cf045008c3dd8356651862dac9d75525dc2a1e3701cda907ae916cbe037a314be9cbc5defbebcce1e5744ad127b6cced88589803d1de409e56c463196ea3236a3f637dcf551498b95707e068bac509b20e76e0152126b01b6102fed588efdc7f91711c7262a2ac765d34f3008ebe9713718623fc20948d75e171cdfb5138a140352edd8e47269aa4e3bc05f9aca30ac8d613831d60e26070f3df87d86b38f3be5cca387dadd78c8d0ddcf05be84fe1af29029264922240d7a23a586ec56b9cd97d562d07fc8a3860c099184a9a291f84a2ad1d19d0a66f175e19a9ce1b43e8efdbab958e247156eac6423c73766e19636b4db7f312de57a1221a8abd07972f675083d29f92d50c561cbe33bab17bae1c1b1f81102158140029848568b9d25a35336520e3bfb573d86b4c0beaece815e4ee28d399b1559b911e71c708f9e6ec016af4ac90bb534a11ea0c598c4fd5fe16388fb0abf25097a7926ebbc8bf7f3c837208832b116e6e63d08af247814d579af24822b6d2272d7f4f0d51d96328a860524975f30b94948efd8f4b8917ee858921f1088a61fbf3a3ffc3384166d2b70cd945106a56daab84f2ff0ea2a6b4d7e2fc65357cd247e80ccb304acbfe8ee2e9aca5a6c207838ecbad0f8dab50e545e431fab5ed309fc511b32859e158a30e7e6522b27ecae399df9206fc9366fdb32a7254d40951501c21c1346a0a38807f10776773483620a3b982d4832938c6d705997926d802a445a86845ef0eddee41991f6a899795bc6c2ebf92bb41d02f46f256136a13826ef04e5c6d9e6f79d0b0541aef2fdccd86a0bab50538c5c566707022fbb51750bc88cf7a3bab2cc4431ea83864ee426761b58260a1a1f5235c9f7d8157550d71542ed5b0e06d044c9555a551038ab65f71149a1aeb3c320624506c34372b45d0b87fe668ab956acca3c4b56b93a48c32334802c8f6a73f4251ab5e4639df887045794a805d46294efa164f4f9e6fc7116411c8c7002effe88f42a6e588118c91e691b26f4aa6970a96eb35dec9cac140fa44ccce5059d7f6ffb5c91bcc108dd00fe153fa19eb56b0f91f69b6a8498a55d49c5a65138da658cde1ecd16279d602e96c2e34c466c9c46e3928189b9bbfafd5c1c4c064c45b708f3e34d0de85d4d0bbdd60d36745c0958f80618697832738d206f84bc783ba0e0724eb7af874f3e1f5199b84b5732d22896397148257436bf3d83ad5781316fb116a7df22d84db12cff83bfded1da718b11d426e7569f8144c6013ec2216c18655787741975844713fad5cdd79b8dff7ae1e15e9ed5a2c15e41ad93cee1678c50ce6e1eb3f004200b6982e5f3684e5f6b484ee0469921dd7323ad1ca20040fa9ad007d5570545d123d36bf5713bbdd089cb36ee1f09fd813172be997d9699b6618bae2d1f6780a1075417d68adbbeea23d7012e125414922ee74fa3ace6fb9b73b516dc41c7d0e10ba240ae5a6e5708f3ee1ee7ad49d9893c3f422280834c9409133268afb4c0b90e66b92c5aba698443d52d875a3debec111ed4afcb4424e52e13a4d7841c989213806ea55d38d081142ced1aeb43a5a3a323be7b4a0ec29260d510264d1a70a800c3bcbaf1580e2f23ec9dc5dca3b203246ae9d9dcb2fb3f83228034a35628a6133edaf9c20ca5b1ed1c9effb6a13d2a518939f04f04036ab7d045daa2d942132761d5716bc80b806ec3419028af146ed60f6de371764fceb41bd46e4ca33e6d0fbe82f07a0e82485e2935652a269b01b39e286b2b1ccc56290da670e35a19ff0f3aaca706e07ed34f0de8168b703176bbedd3864c0050f39ae1a942b488f2d22acdff067a89c02171160bfbad07006cbb234fe478a0d0f8fe6b9d89fd51ea770b85eca4d4273baa1b6b5add4dd68d0c482079695ffc1be2ccfaa71a0c23fd632001411616fdba2a7e9f742a85e52ba0d7f447d0becf1e2cea6b012aebc2771f16065280e86bdcb0263d00671bd7a06a0145f81a0d2dbecd2a21890cdfb0bd461dcde9a20c8e73c6c262f671c9feceedecd07585d2c49a40f9cc3b990e034d7319ff3aa3481d699a1d27f46aee3c31354bdc0763919c8728d271061f87e68be5150658481ae553fe963708380604728f0491374cf0f6e8e8f9c9a2f16ee15c0815b9ee486b51c403039c8e48db2e8d1d3c411907d303e2941d80e1dd942a1cbd085e0dc6f928f14884ab953b1bde9455862eccbc4fc5bab638419d488c0f7ead1f62be8f4ec67777be9d3df38801b1ef12d10b0f39266923550b29234665837807df7ccc989c96c24da104165fb8b755c33085a914b3cb83d8d7ad991b948028033deeb8faaa41e455b4a2ce0370ec3a617cc4f3dff1936f0ce25a84c6e8312b210e4a354b3550816a2fd4009a92f7aa5247df29519b6f1c9f71a4a39bf3c7e12eb72a97819f6f7900232f19d18e9b198bca53207c6975a74e570edb01467c1026461ac9f08c19cd9e01895f038387152f0ed8fe5f590e4f0867668efe6646492d1a756de56aadf7574c331a60e8b24204685c511d8dc6693a2c0cfd05d4c9ea26a51bf68fe84253fa50f1fa76c08b8f637c13a19bcd8ce5e77e9e52b1573070131940693e028c20c367a2eafdd66998b3ea483d2991d6e2f4b2e8eddbe90cd006d4dbd0964a7092d827b557cc843dba26521c8e95aa720da9c53db25bd4305502c0d9fab86cfbc57a1cb7a42cc1805aab43b7c022600af615e4ca4aee3372830c3953c0df24e125fe9b5403d7c253c3319977a95bbb44a6ec9a0994d2b2704d1c59d481bcebb19ec56ad9002e52c19e6f577201840088d57026e729c04e724925e338bec9cbfd1053ec2553cd934cba2b63920c8fd21c052d1d0947cad484bb20d3bba8720d3ca830d80046ff101c56ae7ed9e11665e82554d5039629f464610ad56c2f4874e6ddc7b2b09da8e38a605a2361d3840e48167e77a090bcd0a92410e5e71918f2bc580c62343630a6e7409144fada9bb90b4569d4fa5e7f7f09743030a45bfff7197f3f6abb2b3016c02b3c2f55024052cacf956cd3b089dba1c721f64d9c7054603b633941a6b09d3f98ae729d0960126e023caae5a8fa158fc41c4fac61a56f17b2bf78df6aa950ffa7c7180d0a1e62f9fe8bc6427417d0841d06a2284f5577e015d6748d5317d236af84b0ef7a9b5fa848e4f120e0225c7d141d00dc404b3bd42b4736512af0d3d89232293a7e7e21941efdbdff32be5b3a6e19f0573f126154ad84dcdd53d087711459be592d2b8e9fe517b547b4bae1fda706212a786b4419e9483bc1607045d0e1a27613b531032a50cfc644c97bd2b05a25940c11035a125aa817e3f76d372e788a891f1b4500c2bbe4d6d298e432317816be4f81c7d63522f0242104dbac5a441f3bdb55c00c1caf2deca849a007e38a26ebbfea5a96d96a391e7d790d79a8de78af8873b113b42028786f513e8886582cc56f39d4edb211773b9682dbb1faff8f9b1e6bf51f00b9515d83aaaa7acdd61d0b50bd743a75edfbdcd99e171015ad606ed6c491a009660d207f37fb538dcb04e6c69a4a9fb8405d6064cee99cd0c1285efed5ac261146755e3ff537945dad0e8e53e91783bcabbc7722f35d5c2c94c90032c32403a413a1e169eefc9ae7ad2c48f1189a6966fa08813b0097ffbcd9285eaec55700c3c0eef1d11314e4c35a2b0333f7a3bcf62de93107b2be86d92b58701b591f7fff2919373463c84179c51b95e0d9ec2e0ee282073c180d7b025282990702a0278377f532000d92fd29dc1d68eaea99bb46b4da09f3b5e75d4837af081723bf9fb87bee9298a5a8a63aee0ec44885cde79eb1aff94330d4f8360821e0cee4c6796b279de4c0dd76b65e290d9ae821b40e1a011385e49cf18f8f801b659b1b9e756629cd9116f6f22f3b9f16d0cb02f2ec7335871f2eafddb046c6e8cbfa0f3cef0ca48227493c37e5f1f8105d3867081e64ad2b9636b8d4aa7e90a51cbdaebd10e21d3fa13ee87411014012f1c7b91ab37d4f17fc1fdf02e415685a3a17d7fb93d460c61f810067c989c11088a0034f6f4fbafdda43cd031ab2623198e8c736170a51bbb1a4697028015b0ed38814617e45f45491297fa7bfd19cf1c19f7f8dd079fc5092db967d55a1b22750db73de037a28b59561e56df70dbc35fbcd26a65601a178dbfb147e0443cc20c20844326e38f94aef3a77ce705dbd65959a7313a8a4f9d4c22b0e9d8fddf41b4ddb92842e6d3bcd82b6cde893b04af43658b5a21b6b4f0f8d3e107885de0405ecb0f9414be1abbcfa928f82934efc07cf99ba63bbe9079c9bbc78524794422f791b779b98b7740ae3cbe41a92c2404a5cafc7dba261e55bdc8972bb32966316ffe0171058bcecdf2ff1d363370bac34e209a4afae3fb02b99a4360ecc004c14e9d48c82a0ac4777b71cb73f6524b9a4df1aaf41b8e46a3a93d84fbb2592fe2262d6fcb8d0750b1c2cd0c96209c9e34b9575f8243afbc7d19a3631f8f33f431e573f86927203bbe874a3da4e74eefe24d8058b6547e5a862fecae5c3c6349c1fdd6b3ddeaa836b94ce72bf53380aecafb5f0dfa862eeda3859858e6b254f4711940450eaad6a1262d37760fc8f58287375c06fcde63d136850a953eca5d45928f8ef8c796cc13d436dec9148116ecd0be13d978290daeeaf22cead7a82bd312bd9809bae76712604886ec3b9a050dc87b6044ae220c0a5659ad1b7f6246c401e84f03dcddc76b7891116f5706dcbc5794708606b66727caf30f2592483322a155f818a6a839ad20cf0ecca58a2079004067af822f520d078981f954efa860f0972eccbac054346ac8fd9c795271a4991b8439a75fe4655484e3ba4b49991691eb75ddaca63b04f2b65fb618de570cfb472e644f1c90f245559f8e1f109eaf90696e3061ef14e5ab4a01d182729f0143a7154cc5800b51884b0a40bc77d0eff03542542fdba9c694056fb472c25661eae1ce78f8f04ccf89828d4c341dc2f732ead6887fd4e7a83bd2314bcf106c0e9ac4eba025b9913c2ab4834453546ad1715667c854995fb5c071470521f2c99852cfb69e382599058cabceee8bbf686382a1d69181fa8c617dd09c3e938b5b24cac69a0fe54cb0613d41ec1ffee5e7b732bd3aa99525d5c83c712b1229a817b2d0b431bf081323d650290ebeff132dc58019aec382fcc8f8bc1e2aa581188af5a5ea672818b1516ebb4eada883a3740e9100a6591a842e053956aba182352f74395f5433f26603c2eddd71b10510a16eb", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000f475a2798782a4befbde35b045f9fca800000000000000000000000000000000b5d68e70b88567b1352944061f30726601cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } } diff --git a/circuits/benchmarks/results_insecure/report.md b/circuits/benchmarks/results_insecure/report.md index d32a04903..6a6a9552f 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-18 13:44:31 UTC +**Generated:** 2026-05-19 12:09:10 UTC -**Git Branch:** `feat/1524` -**Git Commit:** `7df3cad298ea4d0194af1dcea8afc397a7c0540e` +**Git Branch:** `feat/1525` +**Git Commit:** `dafa8c81c69457b7d90df0d222642b342e1a902d` **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.12 | 26.98 | 15.88 | -| C1 | 57818 | 0.33 | 25.28 | 15.88 | -| C2a | 142625 | 0.77 | 25.29 | 15.88 | -| C2b | 198355 | 0.83 | 25.44 | 15.88 | -| C3a | 132633 | 0.79 | 26.15 | 15.88 | -| C3b | 132633 | 0.79 | 26.15 | 15.88 | -| C4a | 92515 | 0.49 | 25.59 | 15.88 | -| C4b | 92515 | 0.49 | 25.59 | 15.88 | -| C5 | 151717 | 0.79 | 25.38 | 15.88 | -| user_data_encryption | 53732 | 0.32 | 24.95 | 15.88 | -| C6 | 86927 | 0.50 | 24.76 | 15.88 | -| C7 | 104273 | 0.48 | 26.30 | 15.88 | +| C0 | 6847 | 0.12 | 25.15 | 15.88 | +| C1 | 57818 | 0.36 | 25.98 | 15.88 | +| C2a | 142625 | 0.75 | 24.68 | 15.88 | +| C2b | 198355 | 0.85 | 25.40 | 15.88 | +| C3a | 132633 | 0.80 | 24.97 | 15.88 | +| C3b | 132633 | 0.80 | 24.97 | 15.88 | +| C4a | 92515 | 0.56 | 26.81 | 15.88 | +| C4b | 92515 | 0.56 | 26.81 | 15.88 | +| C5 | 151717 | 0.83 | 25.21 | 15.88 | +| user_data_encryption | 53732 | 0.32 | 25.80 | 15.88 | +| C6 | 86927 | 0.52 | 25.12 | 15.88 | +| C7 | 104273 | 0.50 | 25.30 | 15.88 | ### Artifacts | Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | | -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 10.69 KB | 0.41 KB | 3037910 | 175424 | 3213334 | -| Π_user | 15.88 KB | 0.12 KB | 2972965 | 170200 | 3143165 | -| Π_dec | 10.69 KB | 3.41 KB | 3549222 | 186764 | 3735986 | +| Π_DKG | 10.69 KB | 0.47 KB | 3042950 | 176268 | 3219218 | +| Π_user | 15.88 KB | 0.12 KB | 2973097 | 170320 | 3143417 | +| Π_dec | 10.69 KB | 3.47 KB | 3553770 | 187236 | 3741006 | ### Role / Phase / Activity | Role | Phase | Activity | Prove time | Proof size | Bandwidth | | --------------- | ----- | -------------------------------- | ---------- | ---------- | --------- | -| Each ciphernode | P1 | one-time DKG participation | 304.50 s | 127.00 KB | 128.19 KB | -| Aggregator | P2 | combine folds + C5 | 0.79 s | 10.69 KB | 11.09 KB | -| User | P3 | per user input | 0.64 s | 15.88 KB | 16.00 KB | -| Each ciphernode | P4 | per computation output (C6) | 0.50 s | 15.88 KB | 16.00 KB | -| Aggregator | P4 | per computation output (C7+fold) | 79.27 s | 10.69 KB | 14.09 KB | +| Each ciphernode | P1 | one-time DKG participation | 295.26 s | 127.00 KB | 128.19 KB | +| Aggregator | P2 | combine folds + C5 | 0.83 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) | 78.08 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.04 | -| Committee Setup Completed | 20.24 | +| Setup completed | 3.03 | +| Committee Setup Completed | 20.21 | | Committee Finalization Complete | 0.01 | -| ThresholdShares -> PublicKeyAggregated | 304.50 | -| E3Request -> PublicKeyAggregated | 307.02 | -| Application CT Gen | 0.32 | +| ThresholdShares -> PublicKeyAggregated | 295.26 | +| E3Request -> PublicKeyAggregated | 297.76 | +| Application CT Gen | 0.31 | | Running FHE Application | 0.00 | -| Ciphertext published -> PlaintextAggregated | 79.27 | -| Entire Test | 409.92 | +| Ciphertext published -> PlaintextAggregated | 78.08 | +| Entire Test | 399.41 | ### Thread pool (same process as integration test) @@ -75,26 +75,26 @@ | Name | Avg (s) | Runs | Total (s) | | ----------------------------- | ------- | ---- | --------- | -| CalculateDecryptionKey | 0.12 | 3 | 0.35 | -| CalculateDecryptionShare | 0.61 | 3 | 1.83 | -| CalculateThresholdDecryption | 0.58 | 1 | 0.58 | +| CalculateDecryptionKey | 0.11 | 3 | 0.33 | +| CalculateDecryptionShare | 0.61 | 3 | 1.82 | +| CalculateThresholdDecryption | 0.56 | 1 | 0.56 | | GenEsiSss | 0.12 | 3 | 0.37 | | GenPkShareAndSkSss | 0.22 | 3 | 0.67 | -| ZkDecryptedSharesAggregation | 8.57 | 1 | 8.57 | -| ZkDecryptionAggregation | 49.05 | 1 | 49.05 | -| ZkDkgAggregation | 20.15 | 1 | 20.15 | -| ZkDkgShareDecryption | 1.50 | 6 | 9.03 | -| ZkNodeDkgFold | 62.89 | 3 | 188.67 | -| ZkPkAggregation | 2.16 | 1 | 2.16 | +| ZkDecryptedSharesAggregation | 8.42 | 1 | 8.42 | +| ZkDecryptionAggregation | 48.00 | 1 | 48.00 | +| ZkDkgAggregation | 19.87 | 1 | 19.87 | +| ZkDkgShareDecryption | 1.44 | 6 | 8.64 | +| ZkNodeDkgFold | 60.32 | 3 | 180.97 | +| ZkPkAggregation | 2.12 | 1 | 2.12 | | ZkPkBfv | 0.33 | 3 | 0.99 | -| ZkPkGeneration | 1.33 | 3 | 3.99 | -| ZkShareComputation | 2.69 | 6 | 16.16 | -| ZkShareEncryption | 2.49 | 24 | 59.76 | -| ZkThresholdShareDecryption | 6.05 | 3 | 18.14 | -| ZkVerifyShareDecryptionProofs | 0.10 | 3 | 0.29 | -| ZkVerifyShareProofs | 0.23 | 5 | 1.13 | - -Sum of tracked operation wall time: **381.88 s** (often much larger than end-to-end wall clock +| ZkPkGeneration | 1.33 | 3 | 4.00 | +| ZkShareComputation | 2.65 | 6 | 15.90 | +| ZkShareEncryption | 2.47 | 24 | 59.29 | +| ZkThresholdShareDecryption | 6.07 | 3 | 18.20 | +| ZkVerifyShareDecryptionProofs | 0.09 | 3 | 0.28 | +| ZkVerifyShareProofs | 0.21 | 5 | 1.04 | + +Sum of tracked operation wall time: **371.47 s** (often much larger than end-to-end wall clock because work runs in parallel). ## Raw circuit benchmark JSON (Nargo) diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol index 7b2803ffd..2fe91884c 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 = 0x02734bf64581e3f98e47575b06750fc45ec81a34079564e4a7326a3d547d2b90; 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( - 0x22f1675e1cbb2fcd4d4b892e591f16c3201a876c34d711e86159aa593f8957df - ), - y: uint256( - 0x06b38fe5bca0f4814e532d26345b64ddd5fdbc72c4171889f9c4cad61139787d - ) + ql: Honk.G1Point({ + x: uint256(0x22f1675e1cbb2fcd4d4b892e591f16c3201a876c34d711e86159aa593f8957df), + y: uint256(0x06b38fe5bca0f4814e532d26345b64ddd5fdbc72c4171889f9c4cad61139787d) }), - qr: Honk.G1Point({ - x: uint256( - 0x0beaa5627f856b98b6c9300fd93f16d9de0a364e8319e818171fd4e0c0a5c91e - ), - y: uint256( - 0x2d884ebc1127945f836920004f1499ed01afd2fc89691f0f96f7a928a9998fc6 - ) + qr: Honk.G1Point({ + x: uint256(0x0beaa5627f856b98b6c9300fd93f16d9de0a364e8319e818171fd4e0c0a5c91e), + y: uint256(0x2d884ebc1127945f836920004f1499ed01afd2fc89691f0f96f7a928a9998fc6) }), - qo: Honk.G1Point({ - x: uint256( - 0x02e50bcff38f3c94d748e3ce7b80bb26019c24fd8c64c55e3469cc3ddf138b8e - ), - y: uint256( - 0x248ca1123436cf2e53bdd9ed5e3e594213a59785ae39c6218eb5e28aedf552dc - ) + qo: Honk.G1Point({ + x: uint256(0x02e50bcff38f3c94d748e3ce7b80bb26019c24fd8c64c55e3469cc3ddf138b8e), + y: uint256(0x248ca1123436cf2e53bdd9ed5e3e594213a59785ae39c6218eb5e28aedf552dc) }), - q4: Honk.G1Point({ - x: uint256( - 0x2be0a336b2241e8cc572c843dfe5de1bb1d26f2ffb244461939573c3910ae7e4 - ), - y: uint256( - 0x18195c8e933be2a9c081ff5fcad1f3d362ed2408ca5bd683ecb76f831937bf8c - ) + q4: Honk.G1Point({ + x: uint256(0x2be0a336b2241e8cc572c843dfe5de1bb1d26f2ffb244461939573c3910ae7e4), + y: uint256(0x18195c8e933be2a9c081ff5fcad1f3d362ed2408ca5bd683ecb76f831937bf8c) }), - qm: Honk.G1Point({ - x: uint256( - 0x2aaee69977fc70b11f35a9417bf0efc1b8fc3ed5ed1c6a14cb1e2f587b01618d - ), - y: uint256( - 0x167831b0a4fb0627b623ab3b229eac297d78d9ffb020801e31e4ba9ed288f00f - ) + qm: Honk.G1Point({ + x: uint256(0x2aaee69977fc70b11f35a9417bf0efc1b8fc3ed5ed1c6a14cb1e2f587b01618d), + y: uint256(0x167831b0a4fb0627b623ab3b229eac297d78d9ffb020801e31e4ba9ed288f00f) }), - qc: Honk.G1Point({ - x: uint256( - 0x0c8357f6962ce8370d3f5965652b63f972d8eed7766a7fefba8c443b69edab68 - ), - y: uint256( - 0x238822fb432ce7bed5edcee885a9a75beec94f64602b27518e5bb66da54b4157 - ) + qc: Honk.G1Point({ + x: uint256(0x0c8357f6962ce8370d3f5965652b63f972d8eed7766a7fefba8c443b69edab68), + y: uint256(0x238822fb432ce7bed5edcee885a9a75beec94f64602b27518e5bb66da54b4157) }), - qLookup: Honk.G1Point({ - x: uint256( - 0x29e47a139596dcf5450c2de10d5d121d883389ab7c0e930c1ad2f20283098fc1 - ), - y: uint256( - 0x2548e6b14779657467f37fc862ec803097b1e328abf936d9b93438db371b5aff - ) + qLookup: Honk.G1Point({ + x: uint256(0x29e47a139596dcf5450c2de10d5d121d883389ab7c0e930c1ad2f20283098fc1), + y: uint256(0x2548e6b14779657467f37fc862ec803097b1e328abf936d9b93438db371b5aff) }), - qArith: Honk.G1Point({ - x: uint256( - 0x0c6962ed640803bfd254fe60f15bfbd3619695b639eaa50f8ad9bca8f26c1c67 - ), - y: uint256( - 0x2a81c4ea9b5f20a9b7966ef5a0f095f1bf0fc03400c4328f06da00547e5ede6b - ) + qArith: Honk.G1Point({ + x: uint256(0x0c6962ed640803bfd254fe60f15bfbd3619695b639eaa50f8ad9bca8f26c1c67), + y: uint256(0x2a81c4ea9b5f20a9b7966ef5a0f095f1bf0fc03400c4328f06da00547e5ede6b) }), - qDeltaRange: Honk.G1Point({ - x: uint256( - 0x12740b73a60d59b99352868b5ff15474d8d40676659ede6a613c2869926496e9 - ), - y: uint256( - 0x2e92d9a28c1a2b6f6d5802abcfc28eb69fbf0f0fce55a7e60046d8dfc4fdf260 - ) + qDeltaRange: Honk.G1Point({ + x: uint256(0x12740b73a60d59b99352868b5ff15474d8d40676659ede6a613c2869926496e9), + y: uint256(0x2e92d9a28c1a2b6f6d5802abcfc28eb69fbf0f0fce55a7e60046d8dfc4fdf260) }), - qElliptic: Honk.G1Point({ - x: uint256( - 0x300990501116cc8c9f5950d44ce76927d8443b777155a61e3cf6bb2e5060ca9d - ), - y: uint256( - 0x269ae09b2eee7365a0aed387b13e4ca9916e3ce2c75967672180345d33cb70b7 - ) + qElliptic: Honk.G1Point({ + x: uint256(0x300990501116cc8c9f5950d44ce76927d8443b777155a61e3cf6bb2e5060ca9d), + y: uint256(0x269ae09b2eee7365a0aed387b13e4ca9916e3ce2c75967672180345d33cb70b7) }), - qMemory: Honk.G1Point({ - x: uint256( - 0x1c42c01418da4f6b73611ae563df3215901ab635ad3c850db84e1a9b13ab9235 - ), - y: uint256( - 0x2d23fcba8d43a2ecc03a6af88af95e13ea8b3cca01165ee491dbb23a4ea4ffa5 - ) + qMemory: Honk.G1Point({ + x: uint256(0x1c42c01418da4f6b73611ae563df3215901ab635ad3c850db84e1a9b13ab9235), + y: uint256(0x2d23fcba8d43a2ecc03a6af88af95e13ea8b3cca01165ee491dbb23a4ea4ffa5) }), - qNnf: Honk.G1Point({ - x: uint256( - 0x1dfa93f3351f60495e8df8d87903805817c44f87a73b945c81a438be0facc481 - ), - y: uint256( - 0x0e0341343e527ffedee24e4028bc14b8501adc68d0fce00c17ac11120d2df513 - ) + qNnf: Honk.G1Point({ + x: uint256(0x1dfa93f3351f60495e8df8d87903805817c44f87a73b945c81a438be0facc481), + y: uint256(0x0e0341343e527ffedee24e4028bc14b8501adc68d0fce00c17ac11120d2df513) }), - qPoseidon2External: Honk.G1Point({ - x: uint256( - 0x048462f577ba1216245079d12c9d55487cf3b330eed20a66fe7db67d447fd72f - ), - y: uint256( - 0x1f06371d9dedf997f6dc1b5508b5fa620a05c3261e833bb43b88e9b6b6f5a979 - ) + qPoseidon2External: Honk.G1Point({ + x: uint256(0x048462f577ba1216245079d12c9d55487cf3b330eed20a66fe7db67d447fd72f), + y: uint256(0x1f06371d9dedf997f6dc1b5508b5fa620a05c3261e833bb43b88e9b6b6f5a979) }), - qPoseidon2Internal: Honk.G1Point({ - x: uint256( - 0x1189e97ee885b8a608dca70a417cfd3d339455452c9798752febedd0582f4977 - ), - y: uint256( - 0x13860b3c5483cf0abe4d5ec8f382e455b20aa391dc315862abb5b49228bf7083 - ) + qPoseidon2Internal: Honk.G1Point({ + x: uint256(0x1189e97ee885b8a608dca70a417cfd3d339455452c9798752febedd0582f4977), + y: uint256(0x13860b3c5483cf0abe4d5ec8f382e455b20aa391dc315862abb5b49228bf7083) }), - s1: Honk.G1Point({ - x: uint256( - 0x11b5d23e9c3c361c011754470be1828b314b3bc41dd92723edd773e0dc721f87 - ), - y: uint256( - 0x12d77c6c17a5aa9ab610abb810fecdd5d3a20370c04f9a04969e1c7b51157f90 - ) + s1: Honk.G1Point({ + x: uint256(0x11b5d23e9c3c361c011754470be1828b314b3bc41dd92723edd773e0dc721f87), + y: uint256(0x12d77c6c17a5aa9ab610abb810fecdd5d3a20370c04f9a04969e1c7b51157f90) }), - s2: Honk.G1Point({ - x: uint256( - 0x0ccfd83fc13c1e477918617d55ec0e302167fc9e35ac5e7933267dbf6b04225d - ), - y: uint256( - 0x0345818c0b610d75f563262999377e05bed55ebaff83f0eb7a9525394a67436d - ) + s2: Honk.G1Point({ + x: uint256(0x0ccfd83fc13c1e477918617d55ec0e302167fc9e35ac5e7933267dbf6b04225d), + y: uint256(0x0345818c0b610d75f563262999377e05bed55ebaff83f0eb7a9525394a67436d) }), - s3: Honk.G1Point({ - x: uint256( - 0x18c9ccbc44c932d6d698e781e285674b7c169c4435df7bf6a41b56aebc8104f5 - ), - y: uint256( - 0x145974be15fb78b2d78c93a97669c4501670649ecaf20a58a183b4199301cba7 - ) + s3: Honk.G1Point({ + x: uint256(0x18c9ccbc44c932d6d698e781e285674b7c169c4435df7bf6a41b56aebc8104f5), + y: uint256(0x145974be15fb78b2d78c93a97669c4501670649ecaf20a58a183b4199301cba7) }), - s4: Honk.G1Point({ - x: uint256( - 0x1985589ee73cb2f44b4dbb33e470a1c996c5d06ed7baf10ddbfd507acb8684ae - ), - y: uint256( - 0x2d07e474573859fda8dc083c17728f59cddb11cd0e29be5651705f5dbddb669a - ) + s4: Honk.G1Point({ + x: uint256(0x1985589ee73cb2f44b4dbb33e470a1c996c5d06ed7baf10ddbfd507acb8684ae), + y: uint256(0x2d07e474573859fda8dc083c17728f59cddb11cd0e29be5651705f5dbddb669a) }), - t1: Honk.G1Point({ - x: uint256( - 0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26 - ), - y: uint256( - 0x0015b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f - ) + t1: Honk.G1Point({ + x: uint256(0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26), + y: uint256(0x0015b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f) }), - t2: Honk.G1Point({ - x: uint256( - 0x1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e - ), - y: uint256( - 0x305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d19 - ) + t2: Honk.G1Point({ + x: uint256(0x1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e), + y: uint256(0x305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d19) }), - t3: Honk.G1Point({ - x: uint256( - 0x061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d - ), - y: uint256( - 0x1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e - ) + t3: Honk.G1Point({ + x: uint256(0x061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d), + y: uint256(0x1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e) }), - t4: Honk.G1Point({ - x: uint256( - 0x043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce - ), - y: uint256( - 0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854 - ) + t4: Honk.G1Point({ + x: uint256(0x043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce), + y: uint256(0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854) }), - id1: Honk.G1Point({ - x: uint256( - 0x0ce4b985e3d494ea5e27349d6bfd5a93f2da5df7b83a4e95247e187be4b04234 - ), - y: uint256( - 0x2b14f7ab190415ec456c7d10382e6e5a573d65a180bcc698638d6c38f0c58198 - ) + id1: Honk.G1Point({ + x: uint256(0x0ce4b985e3d494ea5e27349d6bfd5a93f2da5df7b83a4e95247e187be4b04234), + y: uint256(0x2b14f7ab190415ec456c7d10382e6e5a573d65a180bcc698638d6c38f0c58198) }), - id2: Honk.G1Point({ - x: uint256( - 0x2cedaca0e3610529837cd83d76d7a67e6f037068b22b7f0e21c5c0fd2bb16587 - ), - y: uint256( - 0x16f7b9a3f2b5c36530840e8b7046b3faf0273017b736d7d4e9c86a56e22c997f - ) + id2: Honk.G1Point({ + x: uint256(0x2cedaca0e3610529837cd83d76d7a67e6f037068b22b7f0e21c5c0fd2bb16587), + y: uint256(0x16f7b9a3f2b5c36530840e8b7046b3faf0273017b736d7d4e9c86a56e22c997f) }), - id3: Honk.G1Point({ - x: uint256( - 0x195328bc8631c53b83e65739b932c063ab3f2861c3f25e186e693b96ec32ec6b - ), - y: uint256( - 0x1c2e18885411c408222af35cf31ebc1e7e3c82428cd8b4111b418a15b47bf778 - ) + id3: Honk.G1Point({ + x: uint256(0x195328bc8631c53b83e65739b932c063ab3f2861c3f25e186e693b96ec32ec6b), + y: uint256(0x1c2e18885411c408222af35cf31ebc1e7e3c82428cd8b4111b418a15b47bf778) }), - id4: Honk.G1Point({ - x: uint256( - 0x0d629ffdaf3c341c70dc25efc19ea32364f2718a9f2e7d3001f2582bc7770eba - ), - y: uint256( - 0x218e10d582f130e792dac3a6cd0ca6d339566ea722f23aa8c7e505c444a8b426 - ) + id4: Honk.G1Point({ + x: uint256(0x0d629ffdaf3c341c70dc25efc19ea32364f2718a9f2e7d3001f2582bc7770eba), + y: uint256(0x218e10d582f130e792dac3a6cd0ca6d339566ea722f23aa8c7e505c444a8b426) }), - lagrangeFirst: Honk.G1Point({ - x: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000001 - ), - y: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000002 - ) + lagrangeFirst: Honk.G1Point({ + x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), + y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) }), - lagrangeLast: Honk.G1Point({ - x: uint256( - 0x194a661bffd8b96eb251f3e4fd4c4ca2099d006d6047d379e36ac4f48075ca84 - ), - y: uint256( - 0x05cdeca24a13e225c1cca9954e1e3d0826037e148f4c4cabe583813ea42f3318 - ) + lagrangeLast: Honk.G1Point({ + x: uint256(0x194a661bffd8b96eb251f3e4fd4c4ca2099d006d6047d379e36ac4f48075ca84), + y: uint256(0x05cdeca24a13e225c1cca9954e1e3d0826037e148f4c4cabe583813ea42f3318) }) }); 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 a731bfb12..ebd900e7a 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol @@ -8,240 +8,124 @@ 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 = 0x1e8f6b05e7a356352348aac33d63b40dd9e397018959e1b07bb32bce03a31ca3; +uint256 constant VK_HASH = 0x0eae6bb9df6e16c4fee3caaeb291d0ff369a68d391139167cc1b388f6e46eead; 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( - 0x16d8337237d4ec2403b09c245b20ae52736b3c3b0c18c643a26a00569a27cadc - ), - y: uint256( - 0x1b84afa97d44876f12c9857b371684953b15dd5a102621470fe428ce3440a74e - ) + ql: Honk.G1Point({ + x: uint256(0x004818a3ec8aefdad2bd7a9cf7a167e7218c4a8d550f11509d56127827364528), + y: uint256(0x226a9aefe34f960dcabc1f74380c4e5ab96715f867a46af851a72937f9257c6d) }), - qr: Honk.G1Point({ - x: uint256( - 0x05cd8cefcf9533c15dd3a87996f365a1d87d537019531bfa1443523a2e8d9567 - ), - y: uint256( - 0x18ca3ec3ab25ee727789389c4e3a450dd49835e3ff89641b7499751c5eddffb4 - ) + qr: Honk.G1Point({ + x: uint256(0x2c1f1ee92a0a396190f7c7109f90c8117b7fb200c2dcfd6e063f64a3995dc17e), + y: uint256(0x266b0f31b457ded74a809dd78b8ac3999f829549fbd8bdcbe627625a8a0e38cb) }), - qo: Honk.G1Point({ - x: uint256( - 0x0ee7a4cb268e178430f1ab4b692ca1e998f61f59f640709628f3ab061e1ac31e - ), - y: uint256( - 0x0f1051efd6dd1ce3d5f3b009b795b22a45b489f682600ebef98ad03b235d2e4e - ) + qo: Honk.G1Point({ + x: uint256(0x3059f7ff6c9d3f4706fdf095ef295401efb164e188240166de6210d840e16b63), + y: uint256(0x1887de51df5e8e867580cc80d4b1bbc958d00a66dfdfa83ab792775603241462) }), - q4: Honk.G1Point({ - x: uint256( - 0x133b09a5e4a51e7c7596de7bd299628b2c61acc5d9d076619a1b01510b7a0b0c - ), - y: uint256( - 0x05e0e81bb4a9ce485e85d9dc1d9c91340f65ac58b1586a0c759f22036aa025ef - ) + q4: Honk.G1Point({ + x: uint256(0x22fff8145b11f585103c50b3aba419d37129a9a542541da525d945f04e8e73ef), + y: uint256(0x0167b0ed7e0fc999068b12769ad5e6da87501e8813b9aa23e230af631ffe0b97) }), - qm: Honk.G1Point({ - x: uint256( - 0x25305186adb6ab510e45dc5912cad43e52ec3d21d3bb479a9c31dc5638d7d9fe - ), - y: uint256( - 0x1701052b060e45fdc21dbea0a000bf67d3ce7855da5a2495e5134efffbbc4f59 - ) + qm: Honk.G1Point({ + x: uint256(0x11655f3723457a2232e8fb19ee22264a6e684b935889cbbb9d6fd4337e2f7ccc), + y: uint256(0x255b49a883f5f1e1797b4f4a3d421e2d9fe84f25f4f32081692cc348ac681984) }), - qc: Honk.G1Point({ - x: uint256( - 0x257bffaddd8fa52641b20e502375928e7c43840f1026a5d8009ee915bdb6ac6d - ), - y: uint256( - 0x1eda4d3bd95201aab871d32aefc8ced2e696789b0933b6c85f2ae1de282847db - ) + qc: Honk.G1Point({ + x: uint256(0x2a99ac21cb8147739d3aae3d2dc5f3350845d91936f96a5a8581eb2267dfb9df), + y: uint256(0x2995cafda58fefc6d696193a378b27e0ca6efc771efbbabf13c147efa2c8d724) }), - qLookup: Honk.G1Point({ - x: uint256( - 0x116151016b4fea676c06d9d297159098031c1b2d5c0ae18d5e5d874105b26eae - ), - y: uint256( - 0x0610cfeee3e9dbc93565c2b5ca7cbfb4a301b32b4903f16cc95bdd4a8d30275d - ) + qLookup: Honk.G1Point({ + x: uint256(0x116151016b4fea676c06d9d297159098031c1b2d5c0ae18d5e5d874105b26eae), + y: uint256(0x0610cfeee3e9dbc93565c2b5ca7cbfb4a301b32b4903f16cc95bdd4a8d30275d) }), - qArith: Honk.G1Point({ - x: uint256( - 0x07d4cc138af86d1461d286af965b1dedad0b9a44e5b3764000c4fc25db44e2c7 - ), - y: uint256( - 0x283149ff7ff97882b22d98fd89937bc119029f1683fcd2dc4aef822c226eed6b - ) + qArith: Honk.G1Point({ + x: uint256(0x18da344749ae9ab3a9c9599a6868ae7a85f9b0dc440a7531866fbdaa89ff5adb), + y: uint256(0x1b7d987ca28c4b07ab06db9e914e4fd2084e435f5cd8f2e4aca2efa90364d471) }), - qDeltaRange: Honk.G1Point({ - x: uint256( - 0x1f0332c373c4a8fce8a50b55bfc656f76a5ccfb3cbedbfca049704694dfac777 - ), - y: uint256( - 0x1bec984d96e9150d2dbedd8d19564ab492d8db9aa52f02edadd06fb85b156bee - ) + qDeltaRange: Honk.G1Point({ + x: uint256(0x1a2698210b8f31e5028eff667dcdb0a4d9459a40dd77fc93056716f062091a2c), + y: uint256(0x011c9674c7b0572beff21164795ae872433f13640e9374ae17637dd96abd9473) }), - qElliptic: Honk.G1Point({ - x: uint256( - 0x2b524a596ca01057f413e2d264278850dd071ef8d2178bb6fe4c5848359f9307 - ), - y: uint256( - 0x260fa657bf3c7f5db8dba3c6b4c9d701db6cebbdb338acce0bd49a325faf1fbe - ) + qElliptic: Honk.G1Point({ + x: uint256(0x165c75e2af27a39cbce9b53443b3e4d90126754104bcdbb319628fdd4c7a5996), + y: uint256(0x0bdf3b93eb3c0e3f949eb68c030f6c6be52d0700c512b3eb6f53908a6594b1c7) }), - qMemory: Honk.G1Point({ - x: uint256( - 0x088712e929e91683038e64aa93060b67b4c4f8bfbae75648cc2257c6733acf12 - ), - y: uint256( - 0x04b38e231192be299a84c5495056bf59029b1b7e4a5bf591096a8945265232df - ) + qMemory: Honk.G1Point({ + x: uint256(0x0112ecf0acc382b4c304965d1934f5d193b073e29ae6447ce3d89909d4c031fb), + y: uint256(0x051a6fefd0adefb4141e57a764384de39c6bc52aa07d781d9f1fe4cb1af6fabb) }), - qNnf: Honk.G1Point({ - x: uint256( - 0x1c24660db3144b45bf6427e3c1bf6a6c86a2c828217df4d064a1e96797e30154 - ), - y: uint256( - 0x22039202701dac37b8a59b1cb37ec9c0950c16b651560f20b7cca068ebd47056 - ) + qNnf: Honk.G1Point({ + x: uint256(0x1ab0c425506a1d5f665a1c662c25b93bc032de5a608791f5ed59e71594c72888), + y: uint256(0x18330c3505b1a78233682574717de5f260b27125774dd2ab8a3a16efcefecbd9) }), - qPoseidon2External: Honk.G1Point({ - x: uint256( - 0x1be6b465dcde65e3a5b9f2a90d2d1be72254cd5b4e9f4f8d5123655475e6d3fb - ), - y: uint256( - 0x2c6716fb2a299303d79589483f34d09251a84eab9ba72d2d455f9b0a450d762b - ) + qPoseidon2External: Honk.G1Point({ + x: uint256(0x258797782e8223ae6fc8b349f55deac0b3b7026c169021f517c3370560e856af), + y: uint256(0x2b88cb88b7382eff4bb31a164dd13bd4c9a39ede79d96ee07fa226f4e575896e) }), - qPoseidon2Internal: Honk.G1Point({ - x: uint256( - 0x0485bdf6b5d9bad6c67b7eebb14e462422b17f8f5c7bb35f655cf706882d34fb - ), - y: uint256( - 0x06b5e3947f649e125bcede91f9319b269f3db52ad8c56e6d91bd6615732467fd - ) + qPoseidon2Internal: Honk.G1Point({ + x: uint256(0x18440eeacaaa55526950f7290505f8a1ff41effe471ff489d402452afe39e291), + y: uint256(0x14a9e90338b2227b53959cdc9602fb225b9a92f1574d6f002c5558af6090bc9e) }), - s1: Honk.G1Point({ - x: uint256( - 0x13b5177fc08968ad64a53d03cd78fded2405901bcb6a0adf91500111e0e91816 - ), - y: uint256( - 0x1ac68a536966301bf3cdfe70d55442d41ab697984821c8db5605018ea78505fc - ) + s1: Honk.G1Point({ + x: uint256(0x0fae8ce29a99bf0d69a8b4c1d671f628fee07a6cf898ef8cec6164072d3bb76d), + y: uint256(0x038120c21ec79a771366e53e851e8d8a451057808f774168b8d35530b92e9926) }), - s2: Honk.G1Point({ - x: uint256( - 0x22f103cca6c92c267a93626743edea371bfe65360e8d425bc6dfa7f37fea5316 - ), - y: uint256( - 0x297cd73d12d894253268fdb948ea7f85d1b41e942f15eee43381db4707795c79 - ) + s2: Honk.G1Point({ + x: uint256(0x294c1a2fcf929a7c00883e44ec8ce8689214777ba05b16b7e7709dc36dcf7b91), + y: uint256(0x2b5fe1b0b84f8cb46c6a6b6db3d17ffc1fc9c0d4c147b153ea0a93571b5a7aa1) }), - s3: Honk.G1Point({ - x: uint256( - 0x1f59d8f212864a8d1eb077d44972fc944a31d99d4bd8932cefefac230c8fbdcd - ), - y: uint256( - 0x186140203c30f7ca375ab9daa1610b84eb1567bc49bbfd68907c9314e79aa8d6 - ) + s3: Honk.G1Point({ + x: uint256(0x2fc1413ef17fe3f486e5854b33fbdd1c4a9422807adee945820ad32e061ba030), + y: uint256(0x25b2b2a575527f222127d271093adb3f3a8e09bf75a3fbf379566b7cbfa6271c) }), - s4: Honk.G1Point({ - x: uint256( - 0x0b54d2e585ff9ad6a009b877cd9a695fd6e0228bbcb918a69402b398fb179090 - ), - y: uint256( - 0x2c8c78f62d51be262cb36e5d500ec56d5aa9aca88b5c59f5dae069f65356302d - ) + s4: Honk.G1Point({ + x: uint256(0x25ad7067fd41410ff3e57cc9b59a4f2f9bdaa7a180d756a97b33aa2887993d3b), + y: uint256(0x0d5d250decc2b796928740ddf673dd8371a79d695a910d9a83b30f4909cc73bf) }), - t1: Honk.G1Point({ - x: uint256( - 0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26 - ), - y: uint256( - 0x0015b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f - ) + t1: Honk.G1Point({ + x: uint256(0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26), + y: uint256(0x0015b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f) }), - t2: Honk.G1Point({ - x: uint256( - 0x1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e - ), - y: uint256( - 0x305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d19 - ) + t2: Honk.G1Point({ + x: uint256(0x1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e), + y: uint256(0x305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d19) }), - t3: Honk.G1Point({ - x: uint256( - 0x061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d - ), - y: uint256( - 0x1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e - ) + t3: Honk.G1Point({ + x: uint256(0x061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d), + y: uint256(0x1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e) }), - t4: Honk.G1Point({ - x: uint256( - 0x043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce - ), - y: uint256( - 0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854 - ) + t4: Honk.G1Point({ + x: uint256(0x043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce), + y: uint256(0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854) }), - id1: Honk.G1Point({ - x: uint256( - 0x2ca4d4cf001b0127cd0565fc4b314c2474265b2622649219983cab5c3a264711 - ), - y: uint256( - 0x0d3ea6f0e79667587a7b6282ef29030ab179083b2bf591eab27083c9dfefc030 - ) + id1: Honk.G1Point({ + x: uint256(0x110eb94d62692dc870fde8230d5fe9ce0830c1029e8892b09b754cbdbd86d9ef), + y: uint256(0x1811be5b1b9970e033e306ff7fd1de103bb8dca9524be14c57401d2aa631d861) }), - id2: Honk.G1Point({ - x: uint256( - 0x2e385c38f2da1b46e0d42a4e2a23d0daf6bb72f51825e4eb5138a63ea595e94c - ), - y: uint256( - 0x043ebbefa92caf5877c70c19abad06f960d0ffc4cda2c872eeb8e684e23011ff - ) + id2: Honk.G1Point({ + x: uint256(0x1aaf61c2817d932c47dd3aeefc6d231e39c018f4d85306814942da2a16438bd5), + y: uint256(0x0256007a2d30623b14b7b21592af23eedbdaaebbe0770bac755a0991a1d0b9e6) }), - id3: Honk.G1Point({ - x: uint256( - 0x0b329f411f2cbeffd924314b090f38bc2e545cce875002c8f0bc316d3b1d2306 - ), - y: uint256( - 0x1cfe8ce6e2b63866d787025ce891808674a03b1019feba01619009ed49699b52 - ) + id3: Honk.G1Point({ + x: uint256(0x1600d6653d962a6e1d7eb1e852ca98cfdad2af447d57277e97316f1c658f319a), + y: uint256(0x0885d0a87171c93f343fcd71e5dc119f2a90d5f2d4aca5afe4bb0e8ef9705b70) }), - id4: Honk.G1Point({ - x: uint256( - 0x276c422860c1379865016a49ae306de911d13c93ed6ed41ab4cbd32ae399c0d1 - ), - y: uint256( - 0x0e00679146a6dca134568a276976a693a2210ad675464dfab6359df22424b681 - ) + id4: Honk.G1Point({ + x: uint256(0x21b281792bb8fecaeed970f77521f0d4183d3ce32ca186a35dd061aa7ffb1412), + y: uint256(0x0ddc612b779c179c38ea085b8d31dc888fa877b8e240587067dfb4cb8bc50ae1) }), - lagrangeFirst: Honk.G1Point({ - x: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000001 - ), - y: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000002 - ) + lagrangeFirst: Honk.G1Point({ + x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), + y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) }), - lagrangeLast: Honk.G1Point({ - x: uint256( - 0x16e42cf8cb1c82a0961f9eba8413bce1974df0c9ff87b539fb06f3568375a5ca - ), - y: uint256( - 0x05cbe0b6a237e4926c1ba462a2bc921e4d40a0cf5c6b828f0c5d2fade965942f - ) + lagrangeLast: Honk.G1Point({ + x: uint256(0x138a77bcca55fa5af243382db42438e2d0a3d09bc54d88b142fb49cb92a2b6d5), + y: uint256(0x071faf9aed6a777a75cc2c77b494705fc47630e814cf6320a233d283414264a2) }) }); 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 757bd2661ea941a735a04e0237d1c01a6e7a426b Mon Sep 17 00:00:00 2001 From: 0xjei Date: Tue, 19 May 2026 14:46:57 +0200 Subject: [PATCH 07/20] update contract addresses --- examples/CRISP/enclave.config.yaml | 8 ++++---- .../crisp-contracts/deployed_contracts.json | 14 +++++++++----- examples/CRISP/server/.env.example | 8 +++++++- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/examples/CRISP/enclave.config.yaml b/examples/CRISP/enclave.config.yaml index d5c3c9a26..ccc6c0d30 100644 --- a/examples/CRISP/enclave.config.yaml +++ b/examples/CRISP/enclave.config.yaml @@ -7,16 +7,16 @@ chains: deploy_block: 37 enclave: address: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" - deploy_block: 14 + deploy_block: 13 ciphernode_registry: address: "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" - deploy_block: 10 + deploy_block: 9 bonding_registry: address: "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" - deploy_block: 11 + deploy_block: 10 slashing_manager: address: "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" - deploy_block: 9 + deploy_block: 8 fee_token: address: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" deploy_block: 4 diff --git a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json index bfda4258e..aa9b9976f 100644 --- a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json +++ b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json @@ -274,13 +274,17 @@ "blockNumber": 22, "address": "0x95401dc811bb5740090279Ba06cfA8fcF6113778" }, - "DecryptionAggregatorVerifier": { + "HonkVerifier": { "blockNumber": 23, - "address": "0x998abeb3E57409262aE5b751f60747921B33613E" + "address": "0x99bbA657f2BbC93c02D617f8bA121cB8Fc104Acf" + }, + "CRISPProgram": { + "blockNumber": 25, + "address": "0x0E801D84Fa97b50751Dbf25036d067dCf18858bF" }, - "DkgAggregatorVerifier": { - "blockNumber": 24, - "address": "0x70e0bA845a1A0F2DA3359C97E0285013525FFC49" + "MockVotingToken": { + "blockNumber": 26, + "address": "0x9d4454B023096f34B160D6B654540c56A1F81688" } } } \ No newline at end of file diff --git a/examples/CRISP/server/.env.example b/examples/CRISP/server/.env.example index 5696348a1..ce927ce22 100644 --- a/examples/CRISP/server/.env.example +++ b/examples/CRISP/server/.env.example @@ -12,12 +12,18 @@ ETHERSCAN_API_KEY="" # Cron-job API key to trigger new rounds CRON_API_KEY=1234567890 -# Based on Default Anvil Deployments (Only for testing) +# Based on default Anvil deployments (mock Enclave stack + CRISPProgram) ENCLAVE_ADDRESS=0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e FEE_TOKEN_ADDRESS=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 E3_PROGRAM_ADDRESS=0x0E801D84Fa97b50751Dbf25036d067dCf18858bF CIPHERNODE_REGISTRY_ADDRESS=0xa513E6E4b8f2a923D98304ec87F64353C4D5C853 +# Mock contracts registered on Enclave during deploy (reference only) +MOCK_COMPUTE_PROVIDER_ADDRESS=0x9E545E3C0baAB3E08CdfD552C960A1050f373042 +MOCK_DECRYPTION_VERIFIER_ADDRESS=0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9 +MOCK_PK_VERIFIER_ADDRESS=0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8 +MOCK_E3_PROGRAM_ADDRESS=0x851356ae760d987E095750cCeb3bC6014560891C + # E3 Config # Defines the time interval during which users can submit their inputs # After this interval, the computation phase starts automatically From 24fb8a4d65acf3b308ac36cac723cda1627b390d Mon Sep 17 00:00:00 2001 From: 0xjei Date: Tue, 19 May 2026 15:49:13 +0200 Subject: [PATCH 08/20] address hash commit issue --- agent/flow-trace/04_DKG_AND_COMPUTATION.md | 34 +++--- .../results_insecure/crisp_verify_gas.json | 62 ++-------- .../results_insecure/integration_summary.json | 102 ++++++++-------- .../benchmarks/results_insecure/report.md | 98 ++++++++-------- .../decryption_aggregator/src/main.nr | 7 ++ .../dkg_aggregator/src/main.nr | 6 + circuits/lib/src/configs/default/mod.nr | 3 +- circuits/lib/src/math/committee_hash.nr | 55 +++++++++ circuits/lib/src/math/mod.nr | 1 + crates/utils/src/committee_hash.rs | 36 +++++- .../src/circuits/aggregation/helpers.rs | 10 ++ .../src/circuits/aggregation/node_dkg_fold.rs | 17 ++- .../bfv/honk/DecryptionAggregatorVerifier.sol | 110 +++++++++--------- .../bfv/honk/DkgAggregatorVerifier.sol | 110 +++++++++--------- .../test/BfvVkBindingIntegration.spec.ts | 83 +++++++------ .../test/fixtures/bfv_vk_binding/README.md | 15 +++ .../bfv_vk_binding/folded_artifacts.json | 10 ++ 17 files changed, 427 insertions(+), 332 deletions(-) create mode 100644 circuits/lib/src/math/committee_hash.nr create mode 100644 packages/enclave-contracts/test/fixtures/bfv_vk_binding/README.md create mode 100644 packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json diff --git a/agent/flow-trace/04_DKG_AND_COMPUTATION.md b/agent/flow-trace/04_DKG_AND_COMPUTATION.md index b3d5ea385..30d54512e 100644 --- a/agent/flow-trace/04_DKG_AND_COMPUTATION.md +++ b/agent/flow-trace/04_DKG_AND_COMPUTATION.md @@ -600,36 +600,38 @@ ThresholdKeyshare receives AllThresholdSharesCollected ├─ Requires EffectsEnabled ├─ Requires active_aggregators[e3_id] == true ├─ Reads chain state to confirm committee public key is still unset - └─ Calls contract.publishCommittee(e3_id, nodes, publicKey, pkHash) + └─ Calls contract.publishCommittee(e3_id, publicKey, pkCommitment, proof) │ │ ┌─── ON-CHAIN (CiphernodeRegistryOwnable) ──────────┐ │ │ │ - │ │ publishCommittee(e3Id, nodes, pk, pkHash) { │ - │ │ 1. require(initialized && finalized) │ - │ │ 2. require(publicKeyHashes[e3Id] == 0) │ - │ │ → Can only publish once │ - │ │ 3. require(nodes.length == committee.length) │ + │ │ publishCommittee(e3Id, publicKey, pkCommitment, proof) { │ + │ │ 1. require(stage == Finalized) │ + │ │ 2. require(c.publicKey == 0) — publish once │ + │ │ 3. committeeHash = keccak256(abi.encodePacked(c.topNodes)) │ + │ │ c.committeeHash = committeeHash │ │ │ 4. When proofAggregationEnabled: │ - │ │ e3.pkVerifier.verify(pkCommitment, proof) │ + │ │ e3.pkVerifier.verify(pkCommitment, committeeHash, proof) │ │ │ → BFV: `BfvPkVerifier` (DkgAggregator Honk) │ │ │ • pins `publicInputs[0]` = nodes_fold VK hash │ │ │ • pins `publicInputs[1]` = C5 VK hash │ - │ │ • checks last PI = pkCommitment │ - │ │ Redeploy verifier when sub-circuit VKs change. │ - │ │ 5. publicKeyHashes[e3Id] = pkHash │ - │ │ 6. enclave.onCommitteePublished(e3Id, pkHash) │ + │ │ • checks committee_hash_hi/lo vs committeeHash │ + │ │ • checks last PI == pkCommitment │ + │ │ Redeploy `BfvPkVerifier` / `BfvDecryptionVerifier` │ + │ │ when sub-circuit VK immutables change. │ + │ │ 5. c.publicKey = pkCommitment │ + │ │ publicKeyHashes[e3Id] = pkCommitment │ + │ │ 6. enclave.onCommitteePublished(e3Id, pkCommitment) │ │ │ │ │ │ │ │ ┌─ Enclave.sol ────────────────────────┐ │ - │ │ │ │ onCommitteePublished(e3Id, pkHash) {│ │ + │ │ │ │ onCommitteePublished(e3Id, pk) { │ │ │ │ │ │ require(stage==CommitteeFinalized) │ │ - │ │ │ │ e3.committeePublicKey = pkHash │ │ - │ │ │ │ → stored as bytes32 (a hash) │ │ + │ │ │ │ e3.committeePublicKey = pk │ │ │ │ │ │ stage = KeyPublished │ │ │ │ │ │ Emit E3StageChanged(KeyPublished) │ │ │ │ │ │ } │ │ │ │ │ └──────────────────────────────────────┘ │ - │ │ 7. Emit CommitteePublished(e3Id, nodes, pk, C5 proof) │ - │ │ → Note: emits full pk bytes, NOT just pkHash │ + │ │ 7. Emit CommitteePublished( │ + │ │ e3Id, c.topNodes, publicKey, pkCommitment, proof) │ │ │ } │ │ └─────────────────────────────────────────────────────┘ ``` diff --git a/circuits/benchmarks/results_insecure/crisp_verify_gas.json b/circuits/benchmarks/results_insecure/crisp_verify_gas.json index f72f01071..26f51823d 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": 3042950, - "user": 2973097, - "dec": 3553770 + "dkg": 3042828, + "user": 2972965, + "dec": 3553758 }, "source": "folded_proof_export_plus_crisp_verify_test", "artifact_sizes_bytes": { @@ -17,63 +17,17 @@ }, "calldata_gas": { "dkg": { - "proof": 170124, + "proof": 170004, "public_inputs": 6144, - "total": 176268 + "total": 176148 }, "dec": { - "proof": 169932, - "public_inputs": 17304, + "proof": 169944, + "public_inputs": 17292, "total": 187236 } }, - "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.10908618, "runs": 3, "total_seconds": 0.327258541 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 0.608034097, "runs": 3, "total_seconds": 1.824102292 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 0.556764833, "runs": 1, "total_seconds": 0.556764833 }, - { "name": "GenEsiSss", "avg_seconds": 0.124542333, "runs": 3, "total_seconds": 0.373627 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 0.222099139, "runs": 3, "total_seconds": 0.666297417 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.418797042, "runs": 1, "total_seconds": 8.418797042 }, - { "name": "ZkDecryptionAggregation", "avg_seconds": 48.00139975, "runs": 1, "total_seconds": 48.00139975 }, - { "name": "ZkDkgAggregation", "avg_seconds": 19.870362291, "runs": 1, "total_seconds": 19.870362291 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 1.439776368, "runs": 6, "total_seconds": 8.638658209 }, - { "name": "ZkNodeDkgFold", "avg_seconds": 60.322392749, "runs": 3, "total_seconds": 180.967178249 }, - { "name": "ZkPkAggregation", "avg_seconds": 2.121552416, "runs": 1, "total_seconds": 2.121552416 }, - { "name": "ZkPkBfv", "avg_seconds": 0.329691472, "runs": 3, "total_seconds": 0.989074416 }, - { "name": "ZkPkGeneration", "avg_seconds": 1.332915194, "runs": 3, "total_seconds": 3.998745584 }, - { "name": "ZkShareComputation", "avg_seconds": 2.649475611, "runs": 6, "total_seconds": 15.896853666 }, - { "name": "ZkShareEncryption", "avg_seconds": 2.470565315, "runs": 24, "total_seconds": 59.293567582 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 6.067798569, "runs": 3, "total_seconds": 18.203395708 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.094844083, "runs": 3, "total_seconds": 0.284532251 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.207447841, "runs": 5, "total_seconds": 1.037239207 } - ], - "operation_timings_total_seconds": 371.469406454, - "timings_seconds": [ - { "label": "Starting trbfv actor test", "seconds": 0e-9 }, - { "label": "Setup completed", "seconds": 3.030690125 }, - { "label": "Committee Setup Completed", "seconds": 20.213050291 }, - { "label": "Committee Finalization Complete", "seconds": 0.006164875 }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 295.255807583 }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 297.75637375 }, - { "label": "Application CT Gen", "seconds": 0.311138833 }, - { "label": "Running FHE Application", "seconds": 0.003876709 }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 78.078044792 }, - { "label": "Entire Test", "seconds": 399.405836208 } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x0000000000000000000000000000000000000000000000063115aef9dd42310a0000000000000000000000000000000000000000000000047698484c13252b5300000000000000000000000000000000000000000000000793a399b1d4073f7f000000000000000000000000000000000000000000000000000188a919b07d57000000000000000000000000000000000000000000000009d59f2c2a28ddd9ac00000000000000000000000000000000000000000000000ed82c9dbec6f22f5100000000000000000000000000000000000000000000000074f0d5d0b895fa0100000000000000000000000000000000000000000000000000018f934b25e9460000000000000000000000000000000000000000000000050b758b170434dc43000000000000000000000000000000000000000000000005e705b87c810fcf1200000000000000000000000000000000000000000000000ac947aa7ac1d9ea30000000000000000000000000000000000000000000000000000214ef97e1cb8a00000000000000000000000000000000000000000000000c365fa58673f1280100000000000000000000000000000000000000000000000c0306cbbd1646e85800000000000000000000000000000000000000000000000b5f507047ada95ff6000000000000000000000000000000000000000000000000000275a4179079091d0fd7adeb7812fd07faadde449e629089b4cdc0c410fcac699af6d2c4e3fea92a5b53ce90b5e0ff0cfe567d996cb6aca1a1246606ca25b86c909f630bebca00265a64231072629f487aeee5ce8d7e010733367bf831e7f63d1b7101497d90491070c09fd0b855118c842cd15ae0320fd756e7ca1dbd3b7010f8e5ae5b268fc00b7dbd954930cd55f698bc9bc8d5f16cc60d4df84f28f2a6290b46501147f98f039fae43b805d73e009418bc836a43788c01be8dc8e9a92b3cb5717edada8b1e233a591b22a8a5ece11574df80c26d70963dbfe6fc235884d78eb51e724f03962378b21b29354376429108bb6667ea87c52a589cf5014aca06c883fe1fd6254f02f8d24d0251b16b997167504500d2a698f1883396eb8bfaa0061bd0fd50473e121dccb4babfdbf5e403dc53a7f4edb47ba6f30b888e190798f67b8fa7ea8b9617fbfbadec069413712b80ad7ef070d6c7967299514921dbb2b933ddd4ffe6710fe639c41c1ae67b0b2f557986d1b5c889c5081a42d8723c2febf8a2298726cc242d340effd764bbf2ee85a4a2ca104d4a3a60cd2321f21bcdb6cf6fe0b2219a030b2c4ab077ed77a64c633ad1994c5be4ae7c3fc40f569e5e913f2193318d9408b0acd9b422395ff688ed0684b8668a67424d2ba723555c9bab7c8bff816ddb1b2d5453d71b602b9f82dbca9466a34c4c7fe94b8b9261086540664b893483260b9b0f50511620158ee8ba0c4430b02da78047a1b0c5b489e9693db6c17f9af330134d427e392d98ddc08373e170b14fa5e12686a51b1b4fc43c8e32f92054181a7de76b8d346ea02fcff8e3ef3cf773cf0171ae0ec5dafb45fd1d20a98ecd0626a9f499e039070050431240f77f24ae171d4756989fc8564cfe1152bc4a478124642a1ff2e2cca217e047052d415ed8113a2ef10448e389e11b55d505ed4568291be4d6e5ad8ddfef6ab61935c38c48101a7a6e7d995206da76cf3d9ce6008110adaec17d2ea8c31ca166ed8d74323023ce5f832083c83cf383a42bb4d522b116a8d89a2e753064230698277590b0102e75ae48c79bb35abbe6e5fc43c9050517546178ed4c05e57f9fb4cc0a5ef089754d543b659ddc1b7121b757f3f9322b0ce7b3447d36496bd9afed562bdac1a231efac0b51999ffd720fa1ca9d07d5271b5b28b8100030f24305dc42a262b83eb2253d8fe199e23ff0b7c3bec3d9a2912eedd6a98e8f675ac664338943124fd5b44b419eac45acff1323ecd493f686ee27631ecf5496ba04fa946b95bfb10fb1d2d43e86a13c109c5f59c95726f3bafa1e5cc25e35a370ab5d0dd951bdd459a6d27a41c5b103f2206b50779174d3b0bc1c952e9214d27a1dbddb5de018cc5937f9db710a17a28e984a8d91e886f4fa640cf57674741fba4d28e2d35151eb618a4504b2c048b2604539abf0876d65716509e484b67957d3fa601a61ed1d37a999cd5d2663ef8602949cd3a1a2b71ae7071fd6c07cef8f0451e5f43a26f702287797d6d74b55998c4e20e6eec8530cf87a09e0149dba7a7411b2ddc83d9fb216f11b3d1ee08fb6f903831fcfa9fbedb3a12e4011636680096e69ee044ed59db4ddad9977b87364026dbe1507e11ea79e5b264750abd4ba0b668bfa90dac3a450101352696bdf69d5332cda48b386e1fddb23114df1be460f352ffdbfe29e2e5ac8ac7f6470f9efa905a115e1e9adc64a1f2023e890f0dc2d9d68391ae052a1cf110a0cf5c3215e7de2f984f1c1048b8cd30ed40af36a815ea0eca7201590ff2b20e5b104cb449df088a2a8c5269526749408494e2977f7c1a0212942ee09a2ef6a6ae21170921e58981887c809e5b2d82326630621510eb59c904ce15fc0497d2ba78b1bf8eca3800318961703a5a40a0820dbd19315824c1af9ca4b94dccee9c7e15597acaecf2633159ac23022d6c1120fb8369440d8e1ea9cdf387e58e1909c3d4ed280686b046b6bde738eeb6d5d4509c03f9ec131655669fe27cf126d44aadd6aaf253dd5e53f15603cef4640db5b0590563558b05af450b3d54dc74f51a807c713c595af076c43fa2432993b05e6300c2864be15e52bba2b6fb00a7bbeb534bb77d858c23cf4e2aa98cae797c8071f9c038cac8946f77b603e91f89cd186a752ca515376178fad9d87e4da92f11e2e150a97faa9dd1070255507a1602af276f1d2f28c7a6a537644aa27f552ddad12243b315fdece7cf05b814bbc25715c24a098389030b910b73ccc31cb446a312792f8b831c8d9e3473d7bb32719326c088390cf3200cf1b2712a1e8109033df0a2826c610569f292da390590e7bfe1bf580c318b3ee0024d49bd8fccdb997a42a429d2c1e59fb115f9b21337237263cfbbb392164610c3587cabae55d6e53211f9f29bd4f1a0af73e75c094f89c2212c783cb7800b8ae6de0b2f793e50f753e1d28439ddaf4750075879b4cb7df5facdc2856519a422ca77992a9edbf573e180f521f7963f1795e1b453f0ef9cc04de2a32c9869794fefa9269f6c84d9e9d1321c9585efd32ae9d1c8fc4e83d3e44d64c77204c232ed31b493b2677e6c5b06a0ff2062506f6785feb3782197ca25e13e111851a229f6587b322bb993d2917e017a199a263c00d46d9f5ba1dfd8f6c6a1043881b27e2bda1ccd544375b95197e283bcaaec150d651f13d40395231c76f7ed439db3bb179332261c09a51ebc77c0e94c0ae2f92699f663765ee759ec63ef6768baf1792ca7fba45f6f7ef302dc125f6af22946ac84161f57f35aafe2c8bd2607e12900196573f1f6d39c66aef592a16db837b55bf3a42587aec8a50311e5081f0f97c37b5d6b17e885ddaebe6190429a13327377e64a15fef6108a5143959a93ce97e79df26f960117c3e2e83ee1dba0ecb0c98f726af182125d09bb22cce9a5c9d15ff9f05d9bb8d1d5fad601716e0abc590a5dc8f77ea98b616cd569a199c51787e08d49359fe745f08e1d81821005529d844e14dd8b81d7a1b3598abbc2ca3e90bb45cd3080a26a647cb127b2417e439737c55268709568bb5f8eb247e3f73778f2218a1411733d4aa93af9e1a1cbe7075cd0999c6fedcb5a837293dcd4cde1e6b575449108e9499c0b13071013c1d0525727925970d98b30fa0861f406ca081cc9c78cbe22559d0575d59800f59920ee80f0b8afe9e0c83c94609a0fb4d745ada61f42351b502f57f513bc10ab13a9b4fc2b37e09f1cebaaad70a7b9f876a43c8b5b958408aca2379fb438d03f330b0d8cbce2a8a32d7d92bf0c77e8e536ba59d59987575092ef8e2e9de69062503b6c29960942dda327d464bad797ed244f15a692affc2cddc366b369def01d69d77012bf3004e87c85c2f3328caa8e5ae13a58e432c550c1a4cd0d0152e13133ff2ba51355534e3ce561f40083db91e50383a13b6d348aa54d0459f90cb2c8135f2e3a001cf86544e86891e3ab854442639d2c374c766f7c2b0d6087e9e0bfa4bf315b552b24bdb08dc93a4fbb824bd5e64baf22df785b6aa1b65ab53942ec18b91fc6850f278620d99c86e408d8f8b14870eb18fc149b1474099cb54742307de566ea77dea59d3d249c3b3c61146f0f4bd8df5732dddb647a24064d20d2b344a36e6e6a2dcf83b9ebee35055d95d9b00952ccbba72f2754ebf2169e48104809bbbaa0b40e98129e5c90b617185b0367ebb15eb2543704bbe51e1532c6d15699e1ecb86b4f0fd61e77fc4c41db426e0fa74789b358249449a9b749e8199300b5862ccea21353d4b1538e962d2732e28be130a2dd86f42759855e5d8a47116dd260c4f67f5b6dd33721621465f5290dfcbe165eabb75f1dfe17cb84434df262d9c0944750e38c8f972bd21a50d4f636c92f575a07e7f4d908c1ad083312f1ad21d33a8c15c79cb71fd13951938692ec8d9b1c7dfcbf401a256e92fc451882c3b2fe36055cbfa4e664473d1234b9a87ab7201f17936d5544451fb6b1935d014c15fb2b3a19d546e6ce00c7a7b65e7821fe72bd563ea29ab5b938e63a0319106ee90f27582cdf489dc203ade9154113d6e66f95b3548735b09412b77a5442311797d3d71921706923e374923e1aafb6eacb86a89dbdd75476c07a5581893801fc301aec79ed35523bb8ceb3be54b19b21cd5400d4eadc1e772b862bbfdfd600b97076b53b6e0afcab84cdf55a25278487011aa0d71df6a5169d9ecbee182d322659f7f61da58e919fb602d1ff24c62cc53e8ab6dcf01d4dc4d64aaabd987540b433948bbdaf7b1865e5caa3fc76eceea5939ed78d8b057603d443c8ca8dcff082f962ea623218da6ced991e7b85b6b8e519c0129baa723bb32259ce1ad5d090a27ac17e6d530fcb85fa3406c9559058fbb7795c9b8d550c37090e28f31e322051bdcd88d74a20ad4c29446ca5aec2c2b8bcfb1d7f07aaf3e9ec1a656b58a341aefd630c847a011e2ea23b2d6e27bf8162ace64423818eb032688e36f61505f0a61789f606696853840245b606b39204e9b3e60e4ca45b0c538e3d7422993e9125434e09b8680961c6d47b2d4372ef35e67b1c91242275eba69be43fd745bbb0f528453b1ec2dd9f76d53ef90dad732494d1b936b640de37aa6ac1c044007f50391b72db135340374236daf96f528d6cf450429a9ad166e6e8df8416914a1860b5fc6b77c5dae07dda1262959b47fe408ff401afa0827a7e16b4f6f2ae0887a0ceb347824fe27dc767dfdf136e17b32301a98e7e9cc49a5c2f8b68bc47ac922300c897a819c28298513dc1936ce5f4ae394a0a762edf0f07fae0f23b49cdc44054a9728a49cc488cfaed54a5a4b012ddb0630f0400e7293ba4330ec46fd95bd03495f1fec982b8aeb87a09ffbbb50478d9109365c28a4cc507098263a9b96b30e49c15a21ccd730578b9e32e3a9c0c6ca548d7340cefbd4986f669f01ee12bd16d3bacfd3b57aa4f54949959c501de8326e4793cb12c5685de0e8ed4221ed482a68b70286635d45456f3fa195aa4ee1338aeb6b5a4a4a4168502ea35e4434c32822b4170506a783bca6a33beddd3fde8d1b78dee1620a8d133527872648b85e2737c225c1dbf2a82058447abc64bb30a2e9814143cb10d1217072307396306914d061b219fcc52d9f0c7e1f7a409d06c0785fc7c744dfc24c8160b4a6d00dd016b5ec086d9506146e29f3a87e2a91295a7fcffb75ba616ef58507bb20d013d2036ca1bf9e9dbe8234cb57016a40c1df29c5819bc1d64151284eed3069386fa523640db88af8cc1a66f793289c1f1a80ea559c2c3653e0c9542f986a2d3c3afd234e6e3c7273e5b491f7d5d4947d5951e0ec0ff911dda8023d2972ce8bb1d9151168034f4d9e87189491c3c4e5beb598edbb47ec1912fee9944d13aa02636d0409665e57410359763dc466580968d4039318fbf7986c903a07eb5a5c5b4476870b00bc68bfa32f6ee9b90d76ba121cf8b63314495e99c12fa1a43322bafc866216bfe3f131fc2de0f53109a042c70e24af1abffb41e578c665568f3d16cbe54c0cea817d6b5341059039a7ee117d14845ddba9e95f479045914e101efa4150a52a4cdf205120977337e9a26ed8493bb0cc0e75c16d8986313b0238380aad69db15b9e63e0c8b417780486eea2b945cdc0844f662df774afc4d623f3d19ff3e2e239542ce815b91e98b1b500fa18ce98dfa476bb19ac2dde5aff99a10df77015510075848f6dba5ce9044ae7559385ffc30be6afbf64d6b026f4bc3dd497195501a83d86f323dc812989fb27c76b6dcb3782b6914ba43ad5e9c72bce07cb0dcda06160ef64ac69e572a3a1b6931fbab48d4cb7024f9d88e3ef23400c208e1c7c42378f38973c9a20a7dd9ec8eebc989ea43cadb72b068148ffd7f5f7fed57964a0f8701c6142aec15d3e72c0a87ea0cfc3d8fc076b672f4262daf68cebb5751490871b47e35edbfd82ab9c5088a7139dd1a0ca9ac33a9b121406df3410c09355b2aa6b18960d208424312d66c1b2057b353f60447562e3dd40755582b0196b927268bee3a57d5f462d99606ec9b7866d8acdbc222df175af39b3f98d0ebee02d2050cf5430da514c4fc23cd2103e9ce6cddc4a456578138112079f3eb0109857f0daeb1e13ae070a0f3a6234cebcbf63855cbe86479e74ec01e253b27a87915b31df385046553bf6b8f350388a1cfe158bae1a23d48bed8f5ef527dbd36811a0c106e01b566fbc9c45c1d235f3b8b123b696fc63637858b4a18b0282de69c5b691cef2bbbed27e61076b756bff5a3a12003f7c487296881293d9f389ff8435c012b84a3cb4da52b32c7e3eb9b23d3c582474fd36ab20cbe32ae48f045f12ddb9605e2d54d8057718545a4418c22cf3b202147399008abf98c1c08dcb044b0d4a514081805ecf8895e86ed22d9540e61650d7b38531a755d7bfdbb47035a3ae527178430b9d7ed50783e9f2d6a139432c30fbe4b250e711445010f0965871e77c50dc3557d1a543c4c17808e4c8ce62b53e260056f1310900190ccc1376d6c8f692a4407b1f730269f888eccea84dff995cf49084c395f67c257a2a9a7e088ff8d1ceeaa3bed5062f7f27c9ededd5c88bef3da6ddb8cdbe4df04bb370f2911ebba2a18ef1d8f6fc99d73b98bc700bea98865cea2d8c871d5c5703f813962c21de20fd323d1109946beff8c485c333f521b010b7c24a68de5d26e5ca759817585b608d1cf1cd0ffb1359256ada7d6c6ad9e020de1559d564e0075c91c7405bf378a2063a8a21243e0c9ce443654e6b0c1c6d7fde159ed74faaf82233c4e704daa312e8c7534e5e2f0e26a8dd523251eef25da2eecae56cac4114b4bc22fd9433d8c09dd96b43d76d86e2014f826cd1ae89251b15bdb6a1bf743973ee53556969f191c4338d4d5e8a99e29255f7bf9ce859f6f1419d11e8740e627906954513ee2c9110e0c3b20f5c1fe7c8ba9ba7cc4fb01cfa1d1f34b6eb6d1a033c6d4678f6be91cf8b5755fa90a9e5d4450656e97913a16561765e43c575fe879994c652c8c290eacfb807c85b7abe6ddadfc9416ae1d625a0aef2728540864f4e0c94ee5696610b74d78e61dffde7ec105b3401322a88a0dba9bf64bf901e744f12cf547af5c1f58bf24b443b3c7a632e05c4e9ababb0d3bb30b5eed6e6c9816c6ea85d9756d031888757e911c3bb04303053c7739fc7f3f287806abf025b790bb8a5e4b1625083dc324f6214093d3d089e40e7e6f6482d35ce1530e35da4db6d9c510fe2af40758ffb55a24a8b62e9e1362e71be4e013cd70869ada510f88a862d22ac11e67066ab76704dbe14bead1cb8f6a2c4b53daf714bbf9a7b216ca8607923da71a7713b3d7dcf3d674cbfc9ba7b651b67974db0398bfa70f4091bf2c1ab82c9a1e222434d6ee173e1e5ff781174ef50d74d2bccc364c09f2de5baf1064d7c30e050c2a8f067004f269807fe984b7924e6214c49baf8cafc5454344982495a3ed37ac1dbf1b21def8be857ff70ed4e848c85d0723d7c3b39ac31dd8335adbcac3a3b711fd7ce514a178d92cc0d70ed94ee72b319d9e037fcc1795e5a7c273f8524e5c031ad20e77e5f262a9abe5597ffb7ea806fbd651946c6aa20dac3e20f0d832f306e6be4dd91caba66a3aedcc07f162d90b36179c22fae082c5abd57c37e0c9ec13705437877f256e081d2f3adb8b0df2f54befa6b5da02b01dc5507058776b4729916ec4f5a9b52608da6ddd60cd63db2d790abd08fb4850e03b62eff4e1867a124ff4a3439e68ea0066bcbe1313252ff7376b557ce3aff6347d82a4a9ee142d05cc677617bfbf697f5deaca64a4c5812ce492864c29652648f74dc8352f7f4723de0d19c35090f0b490cb1f41af0f7d27eba21a80a38de88f1602db9910d6042cb14301059f8b431ce698a8d9fe156c35581ee8c1d2b019b9ba582eda43c46e1d4e178cf073679aaab6a59a964e18a6599211e6fe9c17cb82c3d2b6ad670bcd17e546ad386d3c95b08633c4d037d49094b5e28b5a144abd0e4b49f1747ac7bf20a2e7ea6b4e6469ba61530f54e4de36467ecc72bf1766d834d6ab0a9107b8a13051b30f7bcbafeab3ffc23c63151d4f481589b1d026cb7d6ea6269dfac11a732aa3d8256a2b71b7afe8e1db35516ad40f5c8432978ecce8f0b2741111a49e10177f485e4587cf6dcaef64de2e50f14a0047e8d5e4b09b2f88486ab089c2921d05c5ffdabbb2da3d2983b762ef2491722ad28037ace7be05cffd2e2aee196af226156fffb41319fd49d29c7786b3d3c8974f642540a279a37d1c738a7542f9ef12ebb9190b726fa1431d5a917f0da84651e1ed1e49296e85902a0d3a48aeeaa722746eef3458a9b6da57850bbd9f0827f2b809970dafbfae0035e317ab2e1f2c27ff6a8f554c2aa972093c0187e21e12c118ce6cd6276e885cb8fb472b1679d81f7ee9c2d05d810037cde9708be12b76618dbde8710837b24ef2a17493b4c6e60fae48662c314450f19c75c3aa8d23cdb8d8039cfc012d3c9db0f7cdf362ce23129178790d8811c11494bae78db60019b7f4037fa4d268fce6bce25b03b3176905d6785f1837f16c73ebb45488261b148e5525f77e994d3f8847166e3cafb93927d32074303f97f384281b7657dc1dbbce6739986ecb88b958c4b0f82dd76fc102db85272b2adc661566acf8cecd5a37be7c86201377da3fb27b59a6ddbfd1b12ecc8f86fe618ca7ddfb5e89e3e6dcd0ab9d05f97b2971f1dea6decdb8e5dc6a09ee9a50dbf98df76dc2f0b0cb83d70857080e0bff039494b05ebbe4dadc52b626f218baa4f93d2d80880c93321d6cc880dac2641bbf860e965aefabc5091caf2b4d3249d106e5a32a6d9dcc3f5b6d5605b5f29e0fe6019249b8be61e73ef1c207abef8f0de5777c2d788829f317aecedb26ec090ace43ca75260c9696058e5115747517324e8d91a20d609b9123839821c8b5b327ab9abc69c5c665c4bd8de930353c63e2a57fa5ce86c58d015f84667622e695045f59d44109e2695f50df010151590072226a725592f5cc4e71be8f2287da5866e27dbfc0ea2ff686d5244e0df62294b7551da83aa32dfb1e844f654d932e84e1ba78305b2e3d62c9bfa3cb06881c419cc67f0a13e09d7b7493347dcbdfd3af84f00054503ef8d2bc29e266292e33ab2450e2a9e5d02655ecac7ff7a91d143f7a362971e3fce4ea8f2392d6164a0d17ca0ae35ff61122933f4599d725024949473bfc179fe7f9aeb10fecb71c2d2a40ecb4ae1cb074e18438018f6f1525726fab5ade73c4026751f0a37afc2c9e3e0d5a54bddb38ba7c9ab5d61e9508446af4306b421442c0a574f8f81bc70718e7aea8efa3e48f1b0b99e1067a0d4bf638118e5ddbb5cba4036f1ee669ce2dd37de7bc1d8ee5ba3b84019adcb7d270f1f116c426d55021c6adc1866e024a2c9424366d5259d6b0a7cbab5333e4fea95803b9318812d9177c62d5d26d19e303f126ce51038605d112a5f136c56a67485b6dc24749e62db0dd1a4e2958906805d74d5a9399b450d6d01932d9e2e2418ab576080eedce812011abbe2cb9f67819efb4bde108ecc8ebf1b3d10e2e4561fd8adcef381b682e754ae4aa7594891d258bb47d048359aa4ca9758f34244e6e4c58a278adff0300319446a7d984fbb21f9121bc247fbefd6063c309d958c0eaa2b5dee1c2a6503bb12afd44ea70740e0c8462110f87dcf4b43b6d487148d05ef241751439cb441b357db7ae2219dcb025c0ea3f13cf50a286c83dad8ea629701d3d59059641d2b3a129a621dcddb9f821a0e84021e68e164a5d6878498b0da4e304e3d8a26a35fd1b541d85e3642c49081a5c7638338f388f14f8c1816c27487eefc278779ead704fd4472958086cd51c210c6768e7b756ef6ca978e9262f942c9fcf0507abeca8dba8913fa103a1862b851deb3ad4980fe48a27a32b7d084b085cc28c7b47b33818c31f215c0082a8131a45bc66154022fca07f9fbec3a2f953ff778f17dbb76570f075b0a37930c507ca568de8f5b775d2884d7db842656fbb0fb2daf57aa2d000ef01c3130deb432b6cba93514b6653f4560948d2bdb9683d221eeff2e3376ef65cd332109eaf03214e76a94bec589f6dc340d328c03cbc6140c6c9393c39c15ea57ae0c299ed761f68d50c8f1f5583477c0b22fe6eddb2690eca016f8c140cd730da5ac78a9f2223b886af8aa74f2ffaa3ee96b2a5a3fa9f8ce3675086043c7de7dafca01129a82f68358148efc49e0b268fce3bdf8d6796e33b2164ce9be3e8506b8777042830082c389c2e9ffabde64b99f1ed327c83d6084487ef75003ffbffa8f384d511630b10c944134a7f27b0039c03b18e354f2fa946c17edae236a6a4b60ddb193a4018f99765d7e83e228e2a639461bf41e0b0b47a7efc5cf0aeae9e5f6ced307b2809fb29576bc643436bf81082cc91562e46ca03586004bd9e61492ef62a8a438d1ed2aaaa984ea9046378e6e16272f7c5082ac5a61f42e11f884694d8bb61ec02250e6aa207950c0e9dba918aeabb959447e732ec672c88f75d47bcbfc189cc611e2e916364ecf79d3f1eaeed7f0df433bc50c3c02a63b72f1b45793f8c8cfdb21d3b9a28454bd97948613b877d3dc9cfd69130f070b2d2376300c4a1303dc5ee24076c0adfbd73e45a4a270f0484f56f7ad39023a4c57aec70a503646b89f5ef191b824963ec1501edcac3edf6c0729dc9dba7a62a3ff3aef21fbab70572b7210d9cf6afe09334329f236204c9830a65b03fb534f38d8aded437b008238ebd7302e805c41371f1266bdaaf95ce284b8e5aa532c4a23b09d8c485fa3d04a66d660995c8459ae231cefa318c0445ada5c1a752d805fc796326537de8007951b7da01f91daa9f50b14938d4ee1bc94e95bc7c98db2299592ecda1f26df375bbe40a0033959d2b1bae4fe99fc1192a0e3067e3b1ec2ac024e50a51bdcff735710eca2714ec3c964a9edb8166e8521d52eb269c2649c520c9dbce9e0f1c7d0e2f2e0b17b5ecce333dc6f6229f065b2f8dff845857f58a1193cb7edeeeb7c090bfd30e232fe66df34fcb61aadf233a346bda9061d128ae2cd06c25eb161382189eb1e92848d2ef16fa62ad8f01adf4cd437e15452228367e63feffd2832bda6cbdb42d078382caa6e469db47e4f2de3250d0ce4d829c8fcef92eac8e49989586ad05ab1ec676ff277032e77c03df654c77684b5ff8d99cac8adf95cf52ed545f70d30f02d58fb9b72db10135e570785e3c444c31a86e6683c13e25de1669337eb60a831724fdfea0c242a7684e881a9445434a8b004e0737a1849857cd5055ebd9e53f2b9f77ac6c6d6ade940c7d0da692e31526be610c47e51239017d62f5858d81330eb9977b3bc8aecab272b4e5368ea8cefd1dbdca495268aa37e9f555dee0fbd6208d2f217f2d8ea47b33f88a5bedbcf0c92ec2d6782ce92e04927cfc2fff29791bad3b44b984ec2b20b6e5e4177c62afcee50b8a12094dffff7e24ee4ea49a7a017f6253df2dffbddebf6a52681a5fcce61dff932691abcad4809168e8bfbd700eab738a0be934ff6a746e65f393d35c4b91c9408127488aaf565076bd3417851fb2986b570222452655e1df8d36961b61d83a859c721d3e2d5494ce359a382c002ce294655a9a110d75c21095665b76d39b52cc7139167906987bf7eb2bae18285ede734693690ba714608ea2c05cf51c3519834741850f9ec9612eaf3a77e01a8cab89a628c64866621ed720698ba89dc67142d5131608bbc32b8dce8f52040f36fb0f24cee64cac63225c59f0e3c3d48b1b4f57c60c5f20205df70e81b3f6011546ef9e9a29041bfc7e96dc9f60bc9c2da6610b504e35e9fe8a2986f7eb4f09f18e3d9491ace8d9b4e3b53bf3ceb2fc5fdf2d225bff6ad4b1a635216395381c4cab4abf95fa872160d83527e2e9b8a9253e72ce50b46e8e236b165ed1327402982f80a9a1b641fec34d718d8227d5df442e2495f51b68ee657741eaa259590193cb15f5c742ca5d537f0b445107d320f63a2e07ed336b1c27f8fef2d7463c0b5e6a2225e3739ea7370540024c609fe4da0333ac173faacb7ea8b0d0c0b7fa1d3003aba571bf41e49546d024f6e1e3d2f25760eb99a37f7ea3d29dba812d2f045a6d395f1fd7680b9db19df70781eb44393479495c21525c8fb1c1d8344cb81c6de68f7bb5cf8add4fa8edd9d5b0ef2973b07ee8bdc97247feb0ad0aecb7f411b369fd8ccc0786e2d2b735117609cc3af441f74c20ef7c86a123763068c62a302c094d1b2775451287216ecd90020817f65c5a8e10299b006799169c625c2017616d95125434c7856221a5e2777d768edd3f8664203b380f15cf21545e13281a798171667d10568c6386676f4d8fb1484f2d7926f45c19e72d55b45aeb547305e9755394435105494028fdb87d00d0b4829c77c613d851381962d4f2ea023e1b12a1e9400fb4a4f318543795720ed91ac450a9eacf24e2c29489f33527df26246bec9a0a7c6a3b1c76d22f6029036b38ca39905f87f337779f199862c631110ffd7ee869f5cf0baeeea9e9b8a074b36434be6c211f48bee747b010444c9b5c2a28ce1edcbe2604ac3d4f30e5926581b67a0ed6f59eeaf40ac63a08338ec77c08ce1bd3b0037f614ebcb225c3043a913ec81444cee3de7ce6bc6de74808394a1a86e8f7f19e38591fd3e2d521923bab5bd648643b8253ede3146199fa4b91a514e28879b329240228fe9cf686163c1807ac9cc2d9bc083ef90f537bd4dbad6d2d1e2db659e88cd97c26620372321f344178d8a9f96b622dca3b7ff0b34f34d627190e8946c19c0e65f4d8abb707b5490bf3a4370aa1bd6d152be87a5d3e25382da8e2e6846682513d1eeb08f4474f3612d13664d0ff9e8d9b471a5df965d25324beef0e6582144da0f05c310bad6b3d6c98169df5fd3c4d49981b2d159aaaa01de771a8f2cbbfb50737867c711f5f0389d8c3779af6262d1dd92dbd5a4b3f2f1516bbd4a9881fb493d858a41c4ed588cab675fdf798543d489edee64de8c7441710732fb0ed9d2f2cff60cae3e0062615ea0f8729c5e6d331c07a8d9bdd6c3e278019eb5d63bc62470e520afbc73b32afa2be723c863ae3f32816f09484e5b21646b52266186d8402abf75bc28ea054fceb94e743982f5cb4ac10cd845299891b2dff0ad2f845b232c84f3fde7adb516bd263ef65bcdcf53d6beef08af0dd3a2ed740520158fc2821502123151ebcea24e779052171652a8bf5fe3822a4550727443a2aa0cf0bdad8de81e43d9681d7750d14934bc7e131cc99bd353859e236167278b4237a38139c1ba767bc77cf5556351e62d5616d1ba71c964fb9b56f930f83775fca58ee1ac09b5d8b2d33404be402ecbfafde582245ce76150269783f0802d6e5760c0b8c93dfc7efd70de95cf5497e95fa482f0ccdf887cd80854468266e86005705eda6540d8e677c151c441dcd7d0d828eb321b34223d3f117e94d29e0da0b1c74cbfacd4d7f53714a3274b154589c857501a860ce75b0651f56c61a4ec3f6a44a7fcd41c185dcb33bbfcd260b95e51d3592de4441a7a7c453904a25c0c2836d43dba07199e20d90beb3ba294b46c8e7d601c64da1a7818986f4b8099edd63d23fe357a61bae8f15bbdbcf5c997d3cb54eb6df8e7e266886c9a35f29e72bfa386f072608fbe0bb8382dc26dd3cbb6d567e294e196152f9bd070abc18049bd19c40f614b7c048389d2ab809d2aea10ae1f458e5e99d203d5d59ac9b014c402845ad77a6d5092ecaeded657473f501bcbe07e16a1c76dd195163ca0d0589a021adc5e795b71792761e3adbdbde2cbb59100011e957bf199b7b38228c25439db36b3a9ee126d6a392a6ebed6619d7ef1c5479ee12448a1cf02185c4430c4568e57deeed2d85cd1f255964991cb8152f2d63680e0a2b9c222f4db49bb3304d8c6fdd7076681223108dfd23553b3151f14e9e82488d9f61a9c2ed5edbf309bd156d9a73afff41990625db984c47d5f2e630029c2e90e4a7479f850b5af32912ef32b12f5e3e97b511467f4900cf6844c6f60cdd17a79a5cf1b96e2e482f102ba5bd44ba62cf5c157d5a8a3ed7258a4caa6ccb2cbf645647c04d97d38aa11732bad3ff547139760bc7b2450f9dc7931ffb24d0a94bd5545581c73014e15c16638fc0215c8f010612a893137bd631d9eef4db227827cec0688d555b8279471eff23b1fd16a495dbf1d0d7998160f4c5068d823eb2a0ddb58fcb09ca8e89400fb5432d5c826f63768de570f6db99f03c0c47dfccfd09509ce1edac0250a7321c9910d7e5845654cb43e9562c31a0667d7d3c560bfe0d28d6a7260ea5962fed1aa7f67d9fd1c032fefdee10d0e0c34bd825fbccab8f5f3e9e11a588f0f5e6472629972fb7d358d0793aeab24736c17fdc1330fd2a45ca85c5708e2d0d172377131d8872ee66f249ed8b03e4b3af11ea922aacfefb7a62a647fbbe0ac233d7301bc39180c2df57b9d8d08bcbb47e971d9dc6eb3a8d38094bd4e60ec96c1677c207fe087d78d0b71e0372c25322ae2a6daf3d935dd06cbc450f405d50ac38f27a177ab52d31a5e4261c18a8155ad4adb82fdf02c0053e7360c17995fd55dd992b24283e340b5288528d2190cdea5acdf8b46fa0df576166cf78f5740985999601171261b8105bfa215d536c60d5cbd7e0c393ed23842004f0861c88f88056798a28942de5d2b23355ad7c0f10b9b16e3e26c5e82ffb2b60746ed798861f1e0e3c", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000f475a2798782a4befbde35b045f9fca800000000000000000000000000000000b5d68e70b88567b1352944061f30726611521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" - }, - "decryption_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000005313d9a6e542b9b7e0000000000000000000000000000000000000000000000013903483c243ff982000000000000000000000000000000000000000000000000f652d9273b7756cb00000000000000000000000000000000000000000000000000027f630846016e0000000000000000000000000000000000000000000000065d1e5b1c24ed4fe80000000000000000000000000000000000000000000000043ee31d5f86ab9338000000000000000000000000000000000000000000000007905d0c87aec5be530000000000000000000000000000000000000000000000000002a10ac0b741cc000000000000000000000000000000000000000000000000c7b51a204d90654400000000000000000000000000000000000000000000000e008b3a4546cb0029000000000000000000000000000000000000000000000000cdbddfffc47364a700000000000000000000000000000000000000000000000000017e0e4d1ea8f200000000000000000000000000000000000000000000000f22ab1a2f5805860e00000000000000000000000000000000000000000000000590f6e2873666cf4200000000000000000000000000000000000000000000000eac6afb5d8d6b5f680000000000000000000000000000000000000000000000000002cec631c36c46032851a9dfc62b5a3386657554763bc9a25609f33ef55cb756f31505ce36e3db0a05b1409e30e6882fde08711fddd33f3a44f63da1deadbf7927d51d3365bc6f093d95d7862c4a0af8261b8bb460c50a13d0f1912f0f74eba2e0ba2aacb5acfd093e3ec1ad390f2a5ffc1edca1cba82de741b80f242560d646492a9dcd4010a402a77a5ab41d7eafe3d77f45358dc9f9606ad20a1eacc2a61cd1dd938d7193aa13930f3869b94dac758e78c545419a33fbb9a40a82673408ee7740e3a520e4cc01d958176942b6e13bc5a3f5f43bc9d14d60de8ecb191de20d42101a085305361c09bef2e2a18b396466c452ac37cd484de8bb99477f3c4498fd618ab8650a1511810f77ae32802a556df4e238f85dcb488a8949c92f1f17c9da333a39b46f7e0490d4a2682047aed8aaa555c1442aff40e17d150662232cb1f5497e9d9f75631b0570752d219754db0e7b38a8b0d900dd5eca242d745248ed72a8a78eeab691073f1f16d0f47fcfd81fa73b5b025f56a13d05da716c38c47450adc7bc6bc3e60956e58c6e5ac4e21c191f15788a58c3b9d3a4b2504028b9677abf113bb9713c0f5e556ebc4df8cc405f439d15f2e52f81ba346d5179ed44b3d5f96197f77fa021ea6893e7d50abcf176a3fe4566c19a86f25d3ae0ec886334991b36112a8b011fa0c797ebbc32a6f723304b35ef468f9aae7150ea4b8e9e77ddb1088c30e95722ccf074798477ac0073d3489aa144112f2a486b964a5ce4990487446b9980f4261268be768521cdb8db5c9d1266187af6b07048cfa2716a074a184b7fdcecdd15eaf1989dc2a067241e737e37bf4bbfe3b197f356aa5d7ec836563a147a5f031836aa1407efd201a899b782e88d1ee01439927c4d975bc0dad5905e5a943dc32631ddfd067e1391444c1aac4b17f7b0c412202687a5791c633dcd9ce1a5959f1b22e74e7e0ee123b765c3f8f4dcc838ca0668c2b760387bd583b0ee27bc72ab2e2fc3e85e45577cf5efe5fdf3dd75f23773bc720bfff9a22b4bf328c69e804509d2a5c5658290efed9e2132189f24d644c64ac69b5b6de0f8e2200918df08aa1c47feb63f57e3c01a577f13d1cf5074100a7333794ef2c30690f329837b80460e018af6c534ef2a32a489affdfc92ef782701dad0d8e4ee12094fca23e0613713d5a51ea31f6607fda990251b11427ef851117f277108eb658d2e439a47c19f204083a535a2a10bd01423a13b1dfc9e3d3ac5411ffcc863bb6ff8dc4abe59891a9a20872821232ae50dcc46d0d0669de09f69844b506bbfb8b706ed1eecf0fb10a3f79784ec36f7f79ef7240f448a97acf8ffb80ce4af683c5a5dd95607d454015f64b5a5940339083d5e4ae358b2d24d0af748b51c65cfd06e95043e74d669214c2f698b2fef5147420e7e3c0d21dc3fe5f3f96ffff1c022962c5b51eecc9a0420cb4fcf1da089596e06175e9708dc70e66ec6b61342054186e3d681b6ee2a1c3b72a5ef49f5f5631ff307ec9a01f5113096219edfa78ea5e5c3c1d0c7398519654c0f0e33d4b3ccc5a30138a672087584a448997817fa6b682125c197c7a00f69b8f94f6a1766f19d2f618bdaa3a6d393e211fbf085115184a6b29290ec4529624bd29e01730ad78c71881d157eb407d191310d3eab86732b0d7bee43e9801d020395d413c7eb94df2f0a228538de50b701674cec415a6f17e45ead136dc310df9abf4c32e790dd45dd999fb10ccbfc083c17ca40b7d15d404b78b1c9cfab1624ab6b69aa63f47bc816de70b4b841e1db7c1334d56171143ce06ee3f8ac6806831e6f8dc9687d7e02eea8bf02d5879fab57db7e7db839b60212fa840e92f52fb8788ec87b94f6f42059e8ebab894618e3dd8c7898642e1ae539bb97ff3dc810139b3596daf0513ab41747423f4dcbaf8bb067e52183192c2f9707a42955e41f675dd80d236e500ea507e0cf020e8dd63871560b83017e49b39ec085c8fe470b16b25135ad3b01518546ee240eb86d777015a51e54a3bef499cab6282c48f72e0173e779011b3d66a2efc61a882677f8271f700594d418eb06d3c56b2223ba289afc3822406eeb62a4b4dc070d3071582c1a803dcd834f5e0f3529d9d79a422d0d6edd2cd11d806f194b4e71ce3066e02a142a3a85edf93ddb1aca6ffd27a718ccb4bc77a275f4538e77c479e448c1624c14d19ff707a448f659ce39acea572f3cab520fe49079887131808623c163e90906e7ed8821e62f3c0a53679c8a37199fc5028c4c0c4fa687ab1a7714b2c5a2211a9a7417a0ec1affc4c951ff38c309cc8b3d407ec398f587e05963c19315a9839b8cf4e1f4c15c4242dd4636e574041306832a9f5c8312eb039a5bf47667edcab0a5bdf459fb3c662bac7f16c71607a5f433bc684caab8b460e92b48410e62fb2f37ceb48642c45e6b515b7a17d01608601d90544628cadc00a4ca65a85b92b82bdd05fef7edfbb605c2fcb8c02f0ccc1669e3dad8b3a9d9473961c1b3441b7b4976e33f94c3f0ad47c1479937bb1be501ba3d7ceee0db11ec5333a589692f7effdfcd7611294e455f22c5ad0f9d13b2f2141100b017ef1474b21edaafea1fc1fb4e88fcdc7c28f6d82069e8d7c41e0e977dd5fd266ab3537b6cb92f621605dfad56f51bbb60cae4550e1a888f9c1493ab4be927b692643d83c0e7550fa85aebdfac97bfc90f965a14e8cc9528062ff0a443c9f5fd1746f19bdd5b6b64457f61b06534689bf551f16a85a14436a6010a702c41bb1e0a6c2be5a38b75afc82006930287135c11e26f547bf7c58f131190097012682ba2a52106334d8968736bb338941b866c16a8753f7a533e215e22fbe7147d0612282fbfba91903fe00b4bcbb22730c795f325162d6bda74757e2cb44471b703e1883cbdd8697418b7eb070d497bee39b0dbd302796f71b560ff19cccf4c7374f196851f26786251c874dd3c9b37c8980c518e1220d59112132c30076fe67cc1467250d011b3fe659ff4b936b5c776b21d5e8d59018c31bca8c62feb6f10c683649cbdf979dd0d114604b627e830b84573f61b5b540d65c17d802b6f539a24fc894fad4752908d7bba4d35e357c0ead16a73328557b8675a2a3214cf4a332021c9a3eef6619a5107c13fdac4a2abdaf7d73d90b3694c3ef8d28528fdf338401b8379d044031ca7c841130f8885b60e557cb3ab99d3d7d1e868601dea1979b30754602912ff0f2ca0fd1ebcc4ca3889011ed814fc9fc6adde78fe1c11dc2d135fc5d12c7db4930eb2344f23181392fae51025c8f852b57d0dcbf9162c748ed56ff9ed17e51fba6ae658627ac81434801bf4aaf9b9c30d2cb4745408c68c9cee327fa2c3a914a76ff530d35b5455a2dd54428924defdb4db6a2f6b1e3e7ce480ad10b16fcde57c3655e4d793cd1f668cdfa752389cfa140aa0085e1d147e9a1bce8a9463e5c07c5f45e96e2b9d9b07c5bdff11ac093342dc1ebc2204edfc258fb3bb960b667a51031c776b1778608b80487798ef2d84e0db0f6e4e141c6fb0a8bc3b7135ba6d0d6d8836fb252d9c2f134e3e463bfbe9c63df0cad905422868774c9da5b37cc9aa725bf0519e8bd4ab8512d84b8b1819de157218e4108538080b0073b4976aa65c7d1591099a55943a8f695cae7fbf48ffad0128f902b18286fc8c2a8a3d4f3ff4ab57885c422adac648d5caa7a206a7947b1405d6023501dc48bea28fc7d71a40ed12eac227fe6b1d2911dcfdadc7e0ceaeacdcf1115c04bd06ae76d4be2cb6fba63ba55fce10015d98d91c08ed62a8b2baafa6e80bdcd637ffb9b0a591ed4fae1fef90350abcf183a52797af4c9f68990c1baa02175980c426b36aec67c01b40c434c907f828e162b48d54a90be5591dadd12676028c2aa67d120845380f6e34a3dc3089a809bdb9952681b720cd8f4c9b433e5c0ed724dfb2b1d51ac931e23b9b175bc2d86c931ed0404efc902a524b2a9e8de22cd36a8f2d393067359dc3668c940cfa463fa26d2d1282a5e478fc785a387fd20e3c90b4a0ea0524b8c98766e4a91752cb0ac4fc26d4ddc0103f927e5dac3a4a124a134b5046aa36a21046068e677e4647f1f606cba1275c6a33dbdb58c8f78500ea6f64b70219437f56e73ebcf8b9869534cfad042cbced12e29af5a184b5b310d7619382aed0adf87a9a77c3f6d0b684bb89e527d48fd0b30f6181c167e32b1233fc74536b1abe9e2ec4b8211bed9770dda095df93c96bf9f69c101611740e1bc969c001bc9ea5485d64b29894c16f9210eb37190dedaea922209cb15302bc26ffd078aff907be66328d6033ce24be8b932db43b2dcebee85ed4d35e32f2d900c2b086f3c6afcb9d6157c937be026532fa062bb9d8a324ad1c767bb48e93df27ae8a3c5713328a26df36868c72ec6b7343fae644779e861c99612ae1cacee60601bc807dd21674f54282e27f923c5dd3f9f032c60128ed051208210973e3ac059a6cc964e2e21bc07a7c8dcd5648c99fa16134625f12afc450febd3157bff300ff3cfe1390b922e2224bd6c5a072e7cf1baeb3ea670dfb4b7ed87bd935fe2d0ab01c5c02b437a8c1a325821fb1397d28184d085c217218ce9772a15504ebe62845bbd18102f444b7f42056656ac9414a9f0beab0bf7576a832699189b3f52a129b7b1093c80ed82ae46ed1654970154fdbf10afd1b20395ee2982a25f65a8a0355c507e9bf351a1d9ab88140cfb63ef0e31bc4ff85100d2d64b8fea8153f8f03ce756874b7dee3e33db736d3014a52eeb4ba84f035ccabc22fdd930d015fc101b9a24e5e9db4b182b82096d17f7b39c4419a2e30003732ee53a2a961c4d1ee1e7df59284a41368ad005b8be0f5720f914bbc82f5e814a2e6d79e730e96653020b653d5eb54c93bc2dec97c0a29b62f3483b8c54112c31b9a17e6f9bed56f361d1296b42bea1371c13fe380d4bf7b7cebf3de5994f900f778e8c3a57e8aa6fb2ff731444d8b174dfea3f6fd8c8ca8105eca56b8d23027b15f04baab984ef49e2f13acad5025d8d1b15e2e64fc0a424bc589b2b5e1db37855bff6eb56a73db33031708cdbaf9c669cd7b650db18d2975f2fb55aa50b455f946f573d64b068d68228275d24daf2fc2634b9963f4888a89222199b1617d337ceaca57592d7253461c5bcaedcc413b455aa91785bc842c533d705632454d571583e7526a8cddca2316c55131317730a98ebd450ccc06d7280fe9448bbf865c73d79b20a40d0fa6d312f28950279d79ae13503f1c87c4eb4081300aa2ec70b6aebd36f3a911cf96b32dfd80e60b3e1e434347bbba00b797102eb56116bba3d7beac36a6d75b3670eb271c53c9c0806db8d51122a55f7bd660546a9bdce2babe09e62387bbf362020d1fbca41ba60c7a6ea9e31f5888293eba6610c9d03d86eb3322b37e5a9c1a17722acb06be54c8ddcd5d4b5ee8c950a19aac715fc02454f8508cb9ab4629f5647e0e9278290b3a0331ecf4a6a4dc28ff9af971e3477c1f141e587e6ae88e14e088235aa1590c55581df682e15f236d54bd804b50d91dcb0c4979d29a32642044d0080bb9e42d2e20180083e4d80c200358074dc749e03c06fdb3f16e8afb29843f1ac767a8436915faa3cfb72a4e215c1507254cbc8d3cd1e85faf93eacc407af20e4aa4dba393c436361a04397a3e92dbd609442471acc7ba6ec5ca3d8c70985125c5256898ac7a525f33019072748ef8ec24a4f75c9d4e8c3c20f70e863071d72ebc98bdae976998c290b16ea2b86187019f6c367bcaeedcfac53b54ce49fa96082e6e6f27fa07097659f90b79e38bdeeb24ec718cbfc5567937e15867902ce81576b44b9766484315c6ca56b6a5e1776f791c37473719e0fffd72149d9c2b3819cac4c62627a3cef8c719b0115b37a5d0ce55e0a0cbd1df5cd05d9af4afa26c119a52bdb434284430f43cdee3c95ffa79aa15caa494c7c72c730d72084c17221eba17e30227c699f53c0e64c439cfc61f397450ce5c1adb973f1f96a59a677427647beafd478e15081391afae06d5f95634062600695209751465d716d4ba052c2aa975f1dcbfb41eba65d44a7e07ce96c7934d9c461cf343b9958f40be55621badc077e730d30e40f59297f148455bde7c36300eb70a2ed7283e525d6a50812e5dabea9ac4c981731fbc314fa9c54c8a133131d2b4ab4cfa96c5efc31aa5ab0fa2e1d87bf6eea08d17099bcf1831e49c99902957cd14811a58f1281a5edb560308ddda5de1767163db2ac5c77e02a910a34d3159542f63c0d7fe1bb0005bff2b55c7797fa2d51e5e0a42a3dbad55be2690827c9bf126ab3015fc01b953a85b086967b1bb9d73af5a8abbe120c01bf8106f57ef1ff75d175c4f265ba7db8ec30e603bb046abf3d48b1436f3854db422c9cb232b92427bf121304d7e4bd9cf9618cace3a2785ee002320b8bb0c2d0d3a1d7b44e9636e9638207e3068411aa5a4226eda54e1e2e2cd72de7b3af6abd4a874c04f834f8a6b7563a1457b74eed13f089978d3bfc7e75282edbe84a65cc4d3ba4b2a40068f45da4fde51c250a61a842e3ecd39064275b66223645a0721701baf9b71d101dbebc2f605881cc9a327040f5557d835211325c034a4b41ea58e10902dab630ef06fcb777c601a28e7a1e91f782f2b7f6774104aaa7e77e720c605504d2a623cce19419e01b2749c2fa88722c5fc5faaa7897e91b598a9979577506dcf54e1aedc9ea33fdcc85d76d07c950f467c13320baa0a4c8ab1b2d0523d45ec9167617ae703ce6148dda0602977420a66fda256a321a75cb4a85ae296bf5899f00ef3146e1ca9e2d47d780faa5d822d6c4e1f29cf4380a0f80594032967cb90f300607c63a4e8e78b3075f61d4eb20a6055d11426fc4d8c01fb31b9fc47e6acbf25b4353eef352f642ab1a7f4ed8e0d24e0006d6019dd6d6e35d53c2c08d73bcfe6a138549d6a35fabb911a247d860d18c75fb954e1ff05d39603748b2b2eb67a70440a2043380ee66d028686267c0a982fd7b8e1080e23b0049c40f49e6b4a2574a3260c7fe9b03d3a486e27905b06a2bdfb9db75c5c005df6cdc95a353c88d99e9993e0fd7bd3652ebaca1da2e121abe5d833ad18457216aa39d94d26dd0ee370e8f9d74ebfdfde6b077f317bf607e748f8cd930a9e23acc1dd4dfd353e3cf1ad59efb65b969933a6f2214d347f003fd18c7b54d22f392a9ab1e228600843f5beb630736308f0922473bd712a2f021bd0980f9c15841804d33f7e1611fd3e50bdbd0535632222610af3a10c93c8040cae78f0faa973ffcc633f2420ac5af157288c59c9cae2815627017ae2cb290e625a6fecdf1747aa56ffb74a28b618654cbfa9b34fd5da3dc75a20ccdb20ca2d2c490dd0e19a68e7d7246fee9aeb239f0e983762acb98b89a0a206d5c8db3e26e2f1eeec9290be1ebf3078709f2f1c6573c07eee4f67eaf8aed4fab0c9bc1f252a496548157fbd3073fac78985de108ecea7c014b70101aa0a820ef520c4d9274c5708e35def891f8fdd8d911a78400db021e4d1d991a2b6e89d580200b92510f7c1bcfbdabb44081e35aae69936f1386e278220603c34385c0b2eee0ff6d709c751d6678181eecf010c35ac5d976a5bae5a45c6281489db801061d13c521e14d9a5ae5dfa502cd4f469e996e22a67e9b9d26fd05a2590e551fe0967c5a36d2dc181947d6bd4ceb14344b08ddd82876f6c9a1c81458767892e10d9befc8f5203aecec36a058a460c92333f96050a459d9dc67e297fa884b500f18b25d7e2d7001c2ff2088f5a0e009cbcb5799e9f76bb26030294cfa941069b908a746007d112dea7b8822219d16260e8bd94bcd6caeeafbc577e6e91d5e3cd214335b12364286261f1ef830b57805b05336e64e4309891fd910901236abffda2a40b261d6e2d5dd598aaf8a403272074f58006aa6063e826e80710c8bdb33e3ace794460b624e5d92f16711c0d476b9fd47a44937762d349782ee891245ef7c1467e761e972bdd93616cda73439cd15fac16c91b7159eaf77ed61999696b1c51a3c95b830b26c3b63ec1d5ca06932fd8c4c6d315ca4dbb7a5dc33b7d0077739f8cf0b4300526c11ae401cff33cfdf300b319d25f6268fd1c646acc15f24c600971aeb4b0b71d7fd0db6a5b55a80640d08fd687756bd0861f0582a91ba67c4b68136b93ad410bf9d6fddbce8d1ed7620df43a06b0c924da7fc0c81f340518c0bdf29dae73c600c5e616935810b62ea41fecfe3e9b384d6854c5c3d16248cf10441d22354ef211d35afb0eef9009ec93cc0ecfe4dd122488342d5e5bda730e75fec2338826f507a7cade523fd6fd0f2550b32e090e147ccd32dbb2776fbda83fdb13399f60ea183e73fba3d143ea64137fdef5d92ef69990ceedd9e8eb7415479bbda31e29c5083115cbb8166306e7540b94a97bdd8bd801a14cb439175018d7b0e18f50a0441266ed58f2d80f443c804b9aca5c3718c712ea5dbfd695e9ce3b342bcf3855140c897ba837ff675271d7b54cc0463eae14577aa26b4722267b0e579a5398692207414bd26d362d504eb65ce8b5ca0f1d606236744e2b7446b1ce7f93f6a8ae6f1f41fed8b6a2be3fb85d0f1044cbaa3a093d5e1bfb965ea3c0d1d5b5ef28e2542b0e376b551b02c55594f61aa992c1b25bcbe21cd7b9628670ff840540afeb861dcb876ffd83eb2dd63c3446b95c2ef69691cbbffd832800e05219195269c5c82846c130f1c1c59760fc39af0c93dab5bc7b24b325078d528c1c5622f72157fb2c9725b1f6719b6337dd0bc0bdc101573369b02311da230861af6c434cc5cc4030406c36fab4cf096e344d9427e5cf2c445afc9bfea608858bd33a381788ab860fe8f5aaf219ab054c25a84c3d2bd338730f030cb94cf0d0deaaf1015251ce1425b44b04217d53c38cd810af54d704cbb2a3a945f2beab57d69fa34042c0c2c20e2a38e9a33f4933c1c529924fa284e5aa9cdf3e036cfb3bb3ac3099b5a16b8c03f73381a885c105224ef28b5cc63e0f121f77bba6f1b2c9c58c7e0eec024eb315652bb2d3c4224be7c7d63fdb8ed6518812865f1137a8b31cc671489a6e5a172fd108241658326df986cda04d9ff5e123f9da24a93bf06f9f6f2172e8bf9a9304db7631c3efb2853ecf7ac0776f718aa2427af6bc0ed6ef22f0b5aa593d0cb210d9842d2cf9d0913161634e2cc3b236af9a318aab4bdda28a9c9126199f5a431554e47333df95dbe73c7013b1191590c38eb17dfcecb0b85854eadc685bfe871f26f230217491079c7ee3f2a3b96a589a0dfc7098c54cfd56d8fe6a34165da8144fb2bc30b8ba2eda5319037fefbb4c0dddbd1f5996cd639acb46fb15eb39d8278563e3ccebec5f13759febde6298927cd0f63d16c2fe5ddeaa40e8e7322db50260785eb9cc878dde1c7346817aba0f622563cc539d7972a4c96bbe58a245ef293d5d90c84a5c365de8bd7cf045008c3dd8356651862dac9d75525dc2a1e3701cda907ae916cbe037a314be9cbc5defbebcce1e5744ad127b6cced88589803d1de409e56c463196ea3236a3f637dcf551498b95707e068bac509b20e76e0152126b01b6102fed588efdc7f91711c7262a2ac765d34f3008ebe9713718623fc20948d75e171cdfb5138a140352edd8e47269aa4e3bc05f9aca30ac8d613831d60e26070f3df87d86b38f3be5cca387dadd78c8d0ddcf05be84fe1af29029264922240d7a23a586ec56b9cd97d562d07fc8a3860c099184a9a291f84a2ad1d19d0a66f175e19a9ce1b43e8efdbab958e247156eac6423c73766e19636b4db7f312de57a1221a8abd07972f675083d29f92d50c561cbe33bab17bae1c1b1f81102158140029848568b9d25a35336520e3bfb573d86b4c0beaece815e4ee28d399b1559b911e71c708f9e6ec016af4ac90bb534a11ea0c598c4fd5fe16388fb0abf25097a7926ebbc8bf7f3c837208832b116e6e63d08af247814d579af24822b6d2272d7f4f0d51d96328a860524975f30b94948efd8f4b8917ee858921f1088a61fbf3a3ffc3384166d2b70cd945106a56daab84f2ff0ea2a6b4d7e2fc65357cd247e80ccb304acbfe8ee2e9aca5a6c207838ecbad0f8dab50e545e431fab5ed309fc511b32859e158a30e7e6522b27ecae399df9206fc9366fdb32a7254d40951501c21c1346a0a38807f10776773483620a3b982d4832938c6d705997926d802a445a86845ef0eddee41991f6a899795bc6c2ebf92bb41d02f46f256136a13826ef04e5c6d9e6f79d0b0541aef2fdccd86a0bab50538c5c566707022fbb51750bc88cf7a3bab2cc4431ea83864ee426761b58260a1a1f5235c9f7d8157550d71542ed5b0e06d044c9555a551038ab65f71149a1aeb3c320624506c34372b45d0b87fe668ab956acca3c4b56b93a48c32334802c8f6a73f4251ab5e4639df887045794a805d46294efa164f4f9e6fc7116411c8c7002effe88f42a6e588118c91e691b26f4aa6970a96eb35dec9cac140fa44ccce5059d7f6ffb5c91bcc108dd00fe153fa19eb56b0f91f69b6a8498a55d49c5a65138da658cde1ecd16279d602e96c2e34c466c9c46e3928189b9bbfafd5c1c4c064c45b708f3e34d0de85d4d0bbdd60d36745c0958f80618697832738d206f84bc783ba0e0724eb7af874f3e1f5199b84b5732d22896397148257436bf3d83ad5781316fb116a7df22d84db12cff83bfded1da718b11d426e7569f8144c6013ec2216c18655787741975844713fad5cdd79b8dff7ae1e15e9ed5a2c15e41ad93cee1678c50ce6e1eb3f004200b6982e5f3684e5f6b484ee0469921dd7323ad1ca20040fa9ad007d5570545d123d36bf5713bbdd089cb36ee1f09fd813172be997d9699b6618bae2d1f6780a1075417d68adbbeea23d7012e125414922ee74fa3ace6fb9b73b516dc41c7d0e10ba240ae5a6e5708f3ee1ee7ad49d9893c3f422280834c9409133268afb4c0b90e66b92c5aba698443d52d875a3debec111ed4afcb4424e52e13a4d7841c989213806ea55d38d081142ced1aeb43a5a3a323be7b4a0ec29260d510264d1a70a800c3bcbaf1580e2f23ec9dc5dca3b203246ae9d9dcb2fb3f83228034a35628a6133edaf9c20ca5b1ed1c9effb6a13d2a518939f04f04036ab7d045daa2d942132761d5716bc80b806ec3419028af146ed60f6de371764fceb41bd46e4ca33e6d0fbe82f07a0e82485e2935652a269b01b39e286b2b1ccc56290da670e35a19ff0f3aaca706e07ed34f0de8168b703176bbedd3864c0050f39ae1a942b488f2d22acdff067a89c02171160bfbad07006cbb234fe478a0d0f8fe6b9d89fd51ea770b85eca4d4273baa1b6b5add4dd68d0c482079695ffc1be2ccfaa71a0c23fd632001411616fdba2a7e9f742a85e52ba0d7f447d0becf1e2cea6b012aebc2771f16065280e86bdcb0263d00671bd7a06a0145f81a0d2dbecd2a21890cdfb0bd461dcde9a20c8e73c6c262f671c9feceedecd07585d2c49a40f9cc3b990e034d7319ff3aa3481d699a1d27f46aee3c31354bdc0763919c8728d271061f87e68be5150658481ae553fe963708380604728f0491374cf0f6e8e8f9c9a2f16ee15c0815b9ee486b51c403039c8e48db2e8d1d3c411907d303e2941d80e1dd942a1cbd085e0dc6f928f14884ab953b1bde9455862eccbc4fc5bab638419d488c0f7ead1f62be8f4ec67777be9d3df38801b1ef12d10b0f39266923550b29234665837807df7ccc989c96c24da104165fb8b755c33085a914b3cb83d8d7ad991b948028033deeb8faaa41e455b4a2ce0370ec3a617cc4f3dff1936f0ce25a84c6e8312b210e4a354b3550816a2fd4009a92f7aa5247df29519b6f1c9f71a4a39bf3c7e12eb72a97819f6f7900232f19d18e9b198bca53207c6975a74e570edb01467c1026461ac9f08c19cd9e01895f038387152f0ed8fe5f590e4f0867668efe6646492d1a756de56aadf7574c331a60e8b24204685c511d8dc6693a2c0cfd05d4c9ea26a51bf68fe84253fa50f1fa76c08b8f637c13a19bcd8ce5e77e9e52b1573070131940693e028c20c367a2eafdd66998b3ea483d2991d6e2f4b2e8eddbe90cd006d4dbd0964a7092d827b557cc843dba26521c8e95aa720da9c53db25bd4305502c0d9fab86cfbc57a1cb7a42cc1805aab43b7c022600af615e4ca4aee3372830c3953c0df24e125fe9b5403d7c253c3319977a95bbb44a6ec9a0994d2b2704d1c59d481bcebb19ec56ad9002e52c19e6f577201840088d57026e729c04e724925e338bec9cbfd1053ec2553cd934cba2b63920c8fd21c052d1d0947cad484bb20d3bba8720d3ca830d80046ff101c56ae7ed9e11665e82554d5039629f464610ad56c2f4874e6ddc7b2b09da8e38a605a2361d3840e48167e77a090bcd0a92410e5e71918f2bc580c62343630a6e7409144fada9bb90b4569d4fa5e7f7f09743030a45bfff7197f3f6abb2b3016c02b3c2f55024052cacf956cd3b089dba1c721f64d9c7054603b633941a6b09d3f98ae729d0960126e023caae5a8fa158fc41c4fac61a56f17b2bf78df6aa950ffa7c7180d0a1e62f9fe8bc6427417d0841d06a2284f5577e015d6748d5317d236af84b0ef7a9b5fa848e4f120e0225c7d141d00dc404b3bd42b4736512af0d3d89232293a7e7e21941efdbdff32be5b3a6e19f0573f126154ad84dcdd53d087711459be592d2b8e9fe517b547b4bae1fda706212a786b4419e9483bc1607045d0e1a27613b531032a50cfc644c97bd2b05a25940c11035a125aa817e3f76d372e788a891f1b4500c2bbe4d6d298e432317816be4f81c7d63522f0242104dbac5a441f3bdb55c00c1caf2deca849a007e38a26ebbfea5a96d96a391e7d790d79a8de78af8873b113b42028786f513e8886582cc56f39d4edb211773b9682dbb1faff8f9b1e6bf51f00b9515d83aaaa7acdd61d0b50bd743a75edfbdcd99e171015ad606ed6c491a009660d207f37fb538dcb04e6c69a4a9fb8405d6064cee99cd0c1285efed5ac261146755e3ff537945dad0e8e53e91783bcabbc7722f35d5c2c94c90032c32403a413a1e169eefc9ae7ad2c48f1189a6966fa08813b0097ffbcd9285eaec55700c3c0eef1d11314e4c35a2b0333f7a3bcf62de93107b2be86d92b58701b591f7fff2919373463c84179c51b95e0d9ec2e0ee282073c180d7b025282990702a0278377f532000d92fd29dc1d68eaea99bb46b4da09f3b5e75d4837af081723bf9fb87bee9298a5a8a63aee0ec44885cde79eb1aff94330d4f8360821e0cee4c6796b279de4c0dd76b65e290d9ae821b40e1a011385e49cf18f8f801b659b1b9e756629cd9116f6f22f3b9f16d0cb02f2ec7335871f2eafddb046c6e8cbfa0f3cef0ca48227493c37e5f1f8105d3867081e64ad2b9636b8d4aa7e90a51cbdaebd10e21d3fa13ee87411014012f1c7b91ab37d4f17fc1fdf02e415685a3a17d7fb93d460c61f810067c989c11088a0034f6f4fbafdda43cd031ab2623198e8c736170a51bbb1a4697028015b0ed38814617e45f45491297fa7bfd19cf1c19f7f8dd079fc5092db967d55a1b22750db73de037a28b59561e56df70dbc35fbcd26a65601a178dbfb147e0443cc20c20844326e38f94aef3a77ce705dbd65959a7313a8a4f9d4c22b0e9d8fddf41b4ddb92842e6d3bcd82b6cde893b04af43658b5a21b6b4f0f8d3e107885de0405ecb0f9414be1abbcfa928f82934efc07cf99ba63bbe9079c9bbc78524794422f791b779b98b7740ae3cbe41a92c2404a5cafc7dba261e55bdc8972bb32966316ffe0171058bcecdf2ff1d363370bac34e209a4afae3fb02b99a4360ecc004c14e9d48c82a0ac4777b71cb73f6524b9a4df1aaf41b8e46a3a93d84fbb2592fe2262d6fcb8d0750b1c2cd0c96209c9e34b9575f8243afbc7d19a3631f8f33f431e573f86927203bbe874a3da4e74eefe24d8058b6547e5a862fecae5c3c6349c1fdd6b3ddeaa836b94ce72bf53380aecafb5f0dfa862eeda3859858e6b254f4711940450eaad6a1262d37760fc8f58287375c06fcde63d136850a953eca5d45928f8ef8c796cc13d436dec9148116ecd0be13d978290daeeaf22cead7a82bd312bd9809bae76712604886ec3b9a050dc87b6044ae220c0a5659ad1b7f6246c401e84f03dcddc76b7891116f5706dcbc5794708606b66727caf30f2592483322a155f818a6a839ad20cf0ecca58a2079004067af822f520d078981f954efa860f0972eccbac054346ac8fd9c795271a4991b8439a75fe4655484e3ba4b49991691eb75ddaca63b04f2b65fb618de570cfb472e644f1c90f245559f8e1f109eaf90696e3061ef14e5ab4a01d182729f0143a7154cc5800b51884b0a40bc77d0eff03542542fdba9c694056fb472c25661eae1ce78f8f04ccf89828d4c341dc2f732ead6887fd4e7a83bd2314bcf106c0e9ac4eba025b9913c2ab4834453546ad1715667c854995fb5c071470521f2c99852cfb69e382599058cabceee8bbf686382a1d69181fa8c617dd09c3e938b5b24cac69a0fe54cb0613d41ec1ffee5e7b732bd3aa99525d5c83c712b1229a817b2d0b431bf081323d650290ebeff132dc58019aec382fcc8f8bc1e2aa581188af5a5ea672818b1516ebb4eada883a3740e9100a6591a842e053956aba182352f74395f5433f26603c2eddd71b10510a16eb", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000f475a2798782a4befbde35b045f9fca800000000000000000000000000000000b5d68e70b88567b1352944061f30726601cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - } - }, + "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.112547319,"runs":3,"total_seconds":0.337641958},{"name":"CalculateDecryptionShare","avg_seconds":0.623812430,"runs":3,"total_seconds":1.871437292},{"name":"CalculateThresholdDecryption","avg_seconds":0.565414459,"runs":1,"total_seconds":0.565414459},{"name":"GenEsiSss","avg_seconds":0.125009639,"runs":3,"total_seconds":0.375028917},{"name":"GenPkShareAndSkSss","avg_seconds":0.230471250,"runs":3,"total_seconds":0.691413750},{"name":"ZkDecryptedSharesAggregation","avg_seconds":8.597811291,"runs":1,"total_seconds":8.597811291},{"name":"ZkDecryptionAggregation","avg_seconds":51.185872000,"runs":1,"total_seconds":51.185872000},{"name":"ZkDkgAggregation","avg_seconds":21.084386667,"runs":1,"total_seconds":21.084386667},{"name":"ZkDkgShareDecryption","avg_seconds":1.499727389,"runs":6,"total_seconds":8.998364335},{"name":"ZkNodeDkgFold","avg_seconds":63.746265722,"runs":3,"total_seconds":191.238797167},{"name":"ZkPkAggregation","avg_seconds":2.189493875,"runs":1,"total_seconds":2.189493875},{"name":"ZkPkBfv","avg_seconds":0.344567750,"runs":3,"total_seconds":1.033703250},{"name":"ZkPkGeneration","avg_seconds":1.381717805,"runs":3,"total_seconds":4.145153416},{"name":"ZkShareComputation","avg_seconds":2.753499194,"runs":6,"total_seconds":16.520995168},{"name":"ZkShareEncryption","avg_seconds":2.574498532,"runs":24,"total_seconds":61.787964790},{"name":"ZkThresholdShareDecryption","avg_seconds":6.255039736,"runs":3,"total_seconds":18.765119210},{"name":"ZkVerifyShareDecryptionProofs","avg_seconds":0.099047375,"runs":3,"total_seconds":0.297142125},{"name":"ZkVerifyShareProofs","avg_seconds":0.227246316,"runs":5,"total_seconds":1.136231584}],"operation_timings_total_seconds":390.821971254,"timings_seconds":[{"label":"Starting trbfv actor test","seconds":0E-9},{"label":"Setup completed","seconds":3.056267000},{"label":"Committee Setup Completed","seconds":20.242110208},{"label":"Committee Finalization Complete","seconds":0.006024833},{"label":"ThresholdShares -> PublicKeyAggregated","seconds":310.649556208},{"label":"E3Request -> PublicKeyAggregated","seconds":313.306656917},{"label":"Application CT Gen","seconds":0.321336625},{"label":"Running FHE Application","seconds":0.003349958},{"label":"Ciphertext published -> PlaintextAggregated","seconds":82.112244417},{"label":"Entire Test","seconds":419.055032166}],"folded_artifacts":{"dkg_aggregator":{"proof_hex":"0x00000000000000000000000000000000000000000000000ab6528b828dc60dbb00000000000000000000000000000000000000000000000a1abbbfd7f6dfd38c00000000000000000000000000000000000000000000000830618157af29e0dd000000000000000000000000000000000000000000000000000030b0b3de35ea00000000000000000000000000000000000000000000000188735db8f53dc39800000000000000000000000000000000000000000000000a18fc5b2c715b3bdc00000000000000000000000000000000000000000000000bda61492423a3ec510000000000000000000000000000000000000000000000000000dd0290b3c7a9000000000000000000000000000000000000000000000004983364b7fd2661c10000000000000000000000000000000000000000000000039b4556d8607bdb7f0000000000000000000000000000000000000000000000099a73ba6ee66e29af0000000000000000000000000000000000000000000000000002f88cb3525c2400000000000000000000000000000000000000000000000dee1e0248c53aec7a000000000000000000000000000000000000000000000002d7030903bd0292ad00000000000000000000000000000000000000000000000d3bc4e4978a08e90a00000000000000000000000000000000000000000000000000023bca494aea202d963ffe2e1c6090bd0417565279b28b01889be9222dcfe21d89b8bc37ea633a118425af1d443927e153024e09a6f511d70b18941e6691c58bd0fcb01eb5580d1bf539f221f8b59e7182eebb1d4f8fe20f6d071ae56a9209b65dba1db587938116ca9f42700f6637513c56ba8c9b0515960d790c8b4dbe376efe344d5f935cea06b4b861140217a2a61f3c7e65b5f762ac0a03f3a240dbfa9e18f437a7cb30b5199c8aae11cf8c30a4ef2de6b9622cd84bd98d344b563e0ad901317254e5f708029cd7ccd824db93db38c3ac3983443731fea6679ede17b63365e86e934fa6bc0806ae63c2a4d1a99d5f8be2e7d9a42bfd10502673ae3e598c2c0c3362def4a927aeb4cd7a438200bdeecfe20a3b86d93a666bcc3eff4dc3eb82ff02e3a2b2a4218ee3aa235f850585d5e928808022969398b5b4c2ad44264feea427187abe2a2e359653c433f3965bcbf214db7711f8bd8bd7fe71c7feb983cb463ce891add81689ee23cb1e15053533135d6a5b3354bddec9afb60428d5db053cd7e6f16b7f01a8b5545ffba4cfb3fd23b26cf203722f86cb76878b459259f3a08d9fdd32f22153b36759cc3a604f7812d907a333a091afe1821dbf642c8b8e522071c729c924b0e2aff5d72d70c59180b7f69061f709c6a2508e68a5e86ce5914fc7dc55aa2ec18dbc526b61e9cf05734d48c3955e23058f894a535ac974e102236e60bef51d63943cc3435eeff39b9905d4ba6d221c1392add2485af108330ddfe0c76da1192a7156f072b520802de68204d5b130afebd9f7de607b326d6265b1e64ff2182bd0e3458273fbbd7f7f5f6fc7da4acc89468c6a82fd21a84bcf8f5a169b502e14cab9389991a4d1aae476597ca83bf5e0a3d2654caa22b02c7ede94e1f4392015227284b88fdd39e7406e3efc1896e8668d4576465a3fb0bc0203261adb539e04aa1a23894473f04e76904d56e7a5861eb2f5aaaf44aa8ef22934998ca79d482884f74605be1e455bf594fbb689c1fb78f53d217184a674c725df26407f7e712dc0ab88ca15039410497ce8310927ad651c80cdde05da3e9ca5498ffbe89e01149e820d0a10d51851e40fd7645040132b3c1a4813466249d6037559300da0e5143232ffe0c122c73393a689d26e91faacc5b6194d8843088e11503b50ba986713933bac02d1b0a39e55efe0c4b8bdba00b2104956c6755aaab1e33961c91c792546146690dc9bfe19b77238cb26d4cfe0b9746ff20199ec3579c7d3e54cfa7624c17e311a082443911f00cc9be18fe6126814303a40656a52db542563901a33200a084c1dabaf2302042c0e7387813c36a24d46cfe883888653580f32706af013bf242fa9b03a2f7e40a45f1d23c9048effe8eb8de9f41046e35a8d753851e408fcb71165eb5d6f6c197c7a0fd6991b155db680a06cdd0116615e653c85ba7c1294aa0a07ef05bfceb690467dcd7ee88dcf31ffcb5453db3f71a2ccd2e75df523584ab053db076c6fb57ceb1356781e4eda6a4b1603da76bafd0bbbb6d61f40260ea34d7aeec03ffc1a93c42370f74c0a4ed8043d950136de56617a7759274611b14e0b002ca984eb4ae6dadce0aeccabd358192021a15bd33b76dc01c877b72bc09a425821140f55a66529a1cfab1abfa742c9e78c858182aa2db6b9483e892e467f3a4a9a130cc8d5b4373149254a438a33a1c72975cdb0425271599c4e9f02e35e2aad8080ce76aad49fcdbb528dff3f2404d0920be45890ba1b29cb4fca098bc976b98680a06e873daec3d1179855d59cc2f420e1a16e5b7da81a1b4eb907576f97540f9df83d2c5ea725c1adfb2b1e789cb0889b2dfed9329a9345b70d2da362dabfb7e832f1c4cc319924756eca3240f9faff90eb37224a7f33ce899e2e5130c2cfa148e522504889f859d71a412b10b374d84a6c139084239080067016429a61503f916f0ce4db10b7d0584b6dc3bac6df045686fbdaec850ff10b901466522e6931ae2771820cb1919ab5fec1e9a4fbdf686b1c66df96b7e319f243122cd4186f0f615e3a6b6e7d7f0e4968b1380f3751d7b3bd220f8c27a46208d60a2487ce2b1ca1802aa8364092ded7ff00976bcb3a1620c79ae30146031a010a2c0a2a00c6e4bff75a514d26be107d1b126c95cdb6e677736d9e665678bd12f70f209adde22fc74e1ee6b059bf66f54b3d240c2fc4b6f50ddd82d617ea2d5cba1bde17c4fb34105992103768d2b0378abd71926522ba98afed09931f50b3bd9527f78f8c6fb753b05db1aa4eb4d283344acdbd8fc4e4e473e470dc9807ca6d252048dcdb39999d4575a00011ab76c1b9cbebd74d6e61de94ea700464f14ed7b622d1c86a8dafc5edd967ffa4d14c8783edbe39442888f1878b9358d0a8aa78480f4ca426772b8d77e03813f6746c729583e1b0fb0013b5dcdf2643ec202799b52ac74b84233b3e19972530e0eb9e270c31decb023ca2216a641da5b326dfd8da24198fa386e1e60ed5257039de5f1abe0cceff64cec32c2e001b5ba1cc1899261f97f33e03b7b2b70d3f9a7124c4ce410f818051c3d38b385386b8a2d29cea72273660298664bbb39f126e0a51b6e026dc983640af54b7c6c6c9b19d3849c2f8242fdcbd80c208af1b934549fa4e256cc9c5c87e44d3f34429af107e081d8251296ad5ad4027c55437aefa751b0084f58e19227dfffebb39fe09200304927d8d04e3dbe7bdf246e8a9034765e69e7b4debdb242daf2a654116f3f6ec3b527eb817127f4dee47fdbe34f8efc4114aef0b3c73558735da92b92d0c54acb864b55b1c1b879c3b64e5efef70fb093f10c7b26f286ff8a95cd1fc78cbfd205dda55b12c079a072d723e63051bedcb0e9ec0bf522e4f219d88f7b794beb58b79be8ae303e542dfa8998fe8e2e40f0c2df8960c6fe54f1252372d030134f16fa2594a412a70af625481ac842b428aa11075415bf98856d4fc23375f09b6769f988976570f54169c1ecd9808627f0a9a85e1468abbf435a00fb3d1668484efd3724c7682008a87ea2bdfe7d46eb25ff8378ee96849409a8fb37eb7011ad04fb384c4dd2c1f4ef9f1b37b1371558b8e2f93582cb2646f2c09724656efb97d3337dcab06c90f0e0a99dbd83d4a4040273cf915e6042171711250cf964bd163d23a72972b3f0b9d4b10137e2807a7f31b2f9004fdb8b520e002a13526ac923eee0f81e6c1640d58e1c704ace55a8fe098468cdcac47a514ce7c13d1bff170761bb70805646c1ce220f83362153b1e348491bfb71d71e1d7efc77b6d24c3ca974a94d52c496905ab852fe706d48bbdbf5ae25ce05dee3e8b7b6ae617c3cb19530fa069d0471f21fd12a73708d3bffe920539440d67b4d3f416ea872b8201a1fde3ec5cbaf9b102e000c471ca6b9a956924a7090f27fe186fed06b74101b9320e5c21353be11a02275397a74cdc6c9c22b70a9888537e3672d2af698c54b749648261a50409f2047b34e162adab623e609aa4807edd86ab29f032d7d7def5528e3f459d1c83ff184be5373b224676aef2e0746332fdfbaa300f276d444f7843a2a98705aa467e2de40fa7cf1cf09ac1c95460fc6add42a149d1830c1b3a1ba13fe1ea634d731306cabf66feb50a478f667de4ba425e8a0d3deaf6848a0ab8c7db3f5574340a7d1214de510d4c573dfbf81e2ced85574f085b25585d25656c4c44b6de923a840a167bd567f720f2035b2a456790a5c36363ce092704be976aab4b7d59302d178f0edea2005a2dfde72ce639358d5b71b0d71cef895d6e7416864a2b7f83d1162a0d570982dba35651835593d240d35fdbf3c39b92d70cb1eaa52722b06dc5c92226b4a401b7cd3ac29a59254af9e4d233300734b4fb2c968ec5e2893566f7ce921847b7ba1aab42f4e4f5f51c638c03a13ad23805b9de2cc0a7d92549c97bcf6c10a78f15aaf95bd7419f2b73befe6f639828985bd1c8b28ef032a4a56adbfcfd1df4856798eed823feb886065eb10586cb72ab2737640bfa4b7c5e5fff3b2a652f48aad393feab573c94a109a7801ee0da56281d67b8691a8ae036e9060c4fc22964f9dfd7d8d3f81c1e68e0392e1c8f672d3cc7daf941f5eea32c8c194f85531d2643255822a749fba1e156e0c96de8ffef2b002b753ea47633d0a35078298b1ffc0eeb733fcd2e39f1ef634cbb169a64529f0d8c09d7e4739795946e0bf9451f995d5977e1a6c154229e20a048e7803169e2aed6f306b8e7e503853ff5e70403ebe23597b0a87ab2d5e0882414db412e051e15e0eb2ff95069bf0ac8c145250e84bbe57101fa3560210f08a76f92c25c8313f9330ce8ab268f6a37d3f955450ac7dd3e876fd857582c12d23c67aec92d75a9cc0e8888a9b984c31a9b1a290f2d8a3296f661b42d2bc8d60093f7293e6d5152112ebcc19bfb2b431ce691187f1622524074146516bdf0695d725a9b2b8b2ab8b171321c21f6bc9e966e93afae0f21beb5db56c01e0764cdc9cc1fafb0e77ed45120a2859d7ace771c262f609f23337085ad7ac0bd1518b19d7b02f22d36007e4a63a9655147b2549b74911bb02309ca97c90114bdc21fbc2e8354aedf45453389690da39edac5fdda7d03430411465676719090813825915d4ae9ec91c3afe24ccec744d07bb4de99add1605409b0c014deee8122ecfec860077f4e94dc76c97d6ed198b85e40174939c8b6aa15b8c1050960dd58e39fd5071db6b77591dc9529a230ca6af9fcc9f005b5caca043c24781515871b203e1de9f33fae96804acf9dd66a37851e916e09d0eb92f42232ad796741d77958f87d6be73a92fe19723019c9c9191346e428e0d06611952a980c1b7ddebc02477fd7417b32342780bb56756ef02acb202e3a68725a381f2b92ceedad2961000dce17ecfb2cc250be2c22e51fc82287d365e7ac359789ad2d6ff81fccc535275f3cc21c3e3bf4a04791627dd6d29d721f27082dcaa91f6e07f16049e9bbfa056dfd61d3b7b4b669787d139ea5119d4d9935cab7396c0e5d1214293575a65ee565f4922d3bc625d71f6a09afb3289208b9d3483c05975430031d01ce4ba374f41d28ac18c62bac813ed4c81aa4092905795358f08d116aa20409f44609813fe3641045f1632fbbfbf1277c4a783f5f412fc58b5d902d9fef218370a7b167710d3e2b896fede982fead14198b2bb122176a2cf6a41e596fed0f77a6ec2b5cc237a288ec5558415477ee54a5823a4c25b9a935a086381ae87400e36213fe3c54fe588df40ad8f9910d1289375d78aea52c585e6b0d8b000ca70a2814832c8ba9d7102d18c2c6432d2b437276444d455cb827d2cee930edc7e325b7423f5022749fcafff9a8e0f17ccdae9d2495be960cb92e0522412d116d8c2f1235fc89fe520e96cba827e08dbcc18942ea99d34396d07a510b7e4f2da3dc2448ef4ad4743479abd38c5ad3321ee0024af73947e13ac6e8e05ab58d0c1ce70d39e806ed89cd8f4be46b3890b8e73f6eccb674e53898b7d20c3858642f051c2e1d4894abeee0ac8b42e5098272d0c70d20fcfb4210b2b050c241e49e197cb11f53f6ae5413b466feae19c03d4818c347e400fa24a88187cebcb8e2a03b7f6700d14e54f9fd3815afea6aa78f1eab9ea0e63aba4e6051e188a1e3e11242f1c219d4bcd07f03a22b20ce8dabe5c7e276b88230a1bcb9968fca6fa9ce70779f350e073860dd63b61332e41cf6b742f050a43c9ada81ede0273e2f1d7ac851cdac0e6b76c48a46bb1506326939d912cb207b0452cb0657f810168ea8f5bb5336af12946dc6a82457d21b99ee430376f2943713cc5c0ea1e305d3f339985477c86d1461abaa8a5bb1949440d17fddd237e7aa73ac7b53f8eaddda829d7ffc6860de18f796e1c8e4ba59c7c8268e0c2c1ee35edf60c62230d3abc9bf57818e7f32a40a0f716c567d18e4dc7658bf84af3d5acb0c49f8db7a6c594f2e4b6eca678d460cb93e47b1344d2e4703f14f1d8d2a0555588ca28e19c7262cd4e5cb8f32bd1207a5d911fcba9704a8407c74e8cb344a4bfc107d6ca66320908508105c49253d04f3d0fbdf6801000f6c79ab6e24279275e5966c2eb653daf4f55be52c661aad0600f4c6c80d9d9fc0e43126c9fcae4f8593cd15c655f6096617f7c219bfb81817655fcbf9f285a52b89b1472f10724b7fb50540b377da2b45384b198d1772f229828e95ce47662a8f3e49e0606ee53e2d860c44af493f0d3b0d572070e8e5f20cab47637347d09170a19ae0a99ae74354c855b4611655aed2de1fa258cc243600dd47ad410fca16aac2a116fcce260a895c81a3a89ce81a19afb5868b3a64de0dfa78ffd98eba18398883fe8955fdf45716dd3bc94011f7062506ca98744fba2fea32b10cf3b2394da53d9988e193d4c5d06789938195241605941bc9d5d66c04302095ac006752fc3cbb0d7512fabb3a4b93bf67264df85bad30f28d468745212d98ac8e9fb00dbc19b1a1f92e24cc8573592579ce3af143ed04a9029bafdf1a0f4fa7afce28cbbefae3194c23921d61d988b4c18d29138ea3e95e3786f75600de9e1339cb0792aa919d41b3bc0e35a96053d13228d0d0dc37732da80dcd05027ca25b34cb25393e1b9d30065a5f8cf325982c4406199a0b4ec569928809ea1765a05be230f3b04c5e6feb4244c1c94caaf2123fa942a5d05ec8ebd4b446d52f44119a9447ed0b7dd6cf5e9abd2842fac8c1fd2dc88a968fcd238dba2b100a27c7851b2997c86cfba255e10b7ac0d6c9496291cf63ccde6285d45fd46da38810bf3ad0fc190493c633a698581fce39efcca70a352620f72803f7587656d23b2753b69783f660b76c896d865dab1fc38c104563f253241ac37c8bcbc68d88e309e109d15f9e10c2c39a1fbee52879576826e06802a9c990ddeb73299543b1871b903b1927c1f9e25478e97faaf3bf8cdbf206994210050ec9dac8bf965f3e6e08ed6d0746709cd2aaa86c5e32d1bc480d3c8c53e9a4c0fc785cbee882e360cb2d6016adbe6d57df4352707b43440ce847343bc82190047dca88d2a925a10c882dbd843bee3d2f61f817dc396bb607b3069ce2007f8f38664f16fae5814c4f15037e191d5f2d381491bcfad0ad7714c0f62326f534bd932a85621328d3faafac292949dd9eb0c36f2c1f14868951a8ccf391d6a963e2f46dcb3b34de509254b223ce8c532602bfd600fe080235761b6f24e35f6a869247389e27fafaf261a3302ebb431b8ef908c86b147fe26568f2beec945500d20a16461946220dc41556fc09aa6ae0f757562c86b4d4539f1e7eccce199fd06a9221d358cc0dbbbfd66db5193515ace6da5cf705b604ef665eb3e048a0091d2b6c82bcf4e3f9f3ebfee98c17dd7b4f796c867536767b7cd01e648b24260af29d0fd53203c08b4eacc24c3223bdb95bc20fc8422f635e0e5d8188e393b1e7aabf1fd025d977bbf4498537dd1809c8d4f3736424abefaefbe35918025ee7c3571bee54aa24a8b812e6c4d5730dbc7f04df7009edf8a6c80f2eb13eeea47f76177ec317bf7e97f452a20565992b4be9d856d012b26309d4a9acbaf736c5b917c42bb663468a2cb60c53e6e81225db1c970f1145e4fba55a75981d1ee8a0d93c3f221194b18b5d80cf453dc31a28b52c8720c7778419ef6f03bec2acd6b32c9b1ca1f9e575ba939756158420b10fd8ba42b0a8a67881c5a02d339a71d2a712361d8c6633086fc4e7676e27b82b06d429375baf1f7211242630f00c9e2662b30ddbe78ecba02aa976ad1e9036230408908cd28b1b62b594ccfb9117f9e450ef605467e290f4eba7cc659eccd225262f9ffa5f1313713f793743decc21e2e3cd0960194b545cdd53d36782747922300978a244e319a6c6d89727be7f6119bcca83ddbf8188658495ea0d1e090db51e8821a456f9c1e93c05c94ff5f2943c71cb687bb2a613ae0524cf2ed3478b5e16e825a4df6bc173e72ebd4dee10f62973780746d87d667b46db12dd7becd1aa1c625d08739eec7fe5ac1aa259bad07272df6327f08ef2fa141751f13e538d722192456d40acc4116717531af7ddedb324a90dd8ce98756bbcf3489445ec89dc2a56ff76e9acd6839977f0052dbc9f28feafc3cbc4ee225aae2369aec5adf1881a4bcddf349e614e33616aea81c97e0823d12b5d471624a709b21f17b406aa532981df1c06a00f315c817b93ca03e6b51f0f87738873c4717095e95cc4390b651671ae7f2cc09330f65231d1bcb0396062a2c990390507cf4e4d916bf62281ea04705d0990539c3be4863e38e4548a6b11b462fcffe572710b9839fa7053790d0fdcbc54ac8897b836768706bc00cec2396a68dd4b6536194d5bcb608215cde515db3ba42c4907b9e7516c51c00a2d6d98b91c40e4540249dc687254bf6f0e261a75ef9066f9ea43f9854010451680fe61ff14cdd2d837e478f3f25097f4cbcd1905831494cf90b01c3565af7e3d295861b05bfd2c4cf50a9615cb2228cfd93a05c5ed43c593260b00d2747a6b63a9e2ba2c61130070f54972aefc1bf1fca6a214a664368ea883c4153b2080c0da033b3e2cf09cf70b79b2ca3604d2768dbc6c25c54d918a2e32ae2b15a733b4f0ecc57ab6b53131ec55effbc7beade097ea9e17748761b4f968e77ba7319feab82d5b5ef23646e40de26a20665c3f9d85370a14bffc2db9ef2fe52f4835e51a7f869f540079e51bbd0b65898cce6ed666cfa12d11799b1b8a26b300be1b1149f009afbbd3690e9cdbd958462b08b25527e5c91e2ced05551874cec301f57b0460c7682ad99d95f975188981188383caad550e17de8f35ec3412101e2368d797eec0fecd967428430dfda277f9beaa5d363ff5122a46168b8de0b4f951d9b065f1cbba637dd52d2192b594355084fa3a9b9a090fe79e7dc890b555ca849b7c88ac83af6d1f73b87e69c141e8d86195424c441a1a5c0158e48abb771bcede8936a1b9113f55898928ca73dc75e5b6c4de406f4114e8708379c7e5d1433d4845b35737dc65e84327b340c01dbf65cacc706ca58117da8029464130c99b5892187898e58abaabb6944eeccccf67dcaf9a8adc6ba52f351fc70df1a9023ffe1b3ac2357f5661dc9e3e2da37226434eb80f8c7cdf0113bda125efbc22740e9593a0b09928efb71520453485365391c7790d931f68712f1e3914c9bf5018a3df1979f3cc1d9bc8ce6c59ac9f0b4d3eeab6392e4bbccf01e75106c9caaf1123582416ccdd87ad50ba24944c8af7d2e663ac666e2f45951d48e3d982908aa887a32d5f2812c2bc7d0819d57f474758bee18c53c5b275fb282c23fc095cf698950df8016f2948992fbf6142aa4e0e30a5e736c515b1f2c02b195790411f09de4a0cc204bdf0245a9a574e4f79a91ecc1bbb69a48b311da91eefe7c39b57535035eed99d825e0d1ab768608aa47f30306158ef34edd4bbbd0a0a9d6cb21f1ca1da8a519fd734a1c7184bce140551f50816a085dcf4bdb4c92ed9c14821e951be0b6ba610ace2bd159bc83184e9e675bea003a97278fc57182a65fa1cc3f8f5fe06c3955051fbe3c1b4c02ddedd0e8db7a17e9ec190dde15709681ecf7b55113616b516a24fdd90a53286c1514d865e7767ea9ba09547fa471ca8c329de319b9454634395fede811145c72226d89cdfc98851bf5925f6e1682b03cb157a9dfe93962463c49e37e794fb4f7bcc8ef15f4b95a08ee504b424bc2b9b0ceb50d95c08549b29ac4433d6b5c0e112e25496bb7526998764d265ed5723569ffdd6ccff3f7b662fa9b12c449562c4f646654842a558c8b1c9f244ecd0083f3d836874363dff34932fa01abab49421c8e40cbdfa9ff52d5cd9887a9395148a55b111d46832f98328dedc0cae8db24360ef8e97fc6d4e5685e8e40ac31a018afd561b36f06b595090ee4caa5595baa0d1525fd0a9a465799925d7749e60163c569702c9ce5c359a1d07b70d603d86cd18217349a14acd53f0bc030edf5511ff984a8cb7855e63d9c51f7ac955363e610eb9758994f4b10b66064c5a456314644d1ddfac620436c9d8700d2c0fe22e6304a1675686a2875b236afed295fd05bb3ac47584e57fee2044db8b3f62dd73c55a56bbef55e04abc70f00cd6141222e712bccd22dfb053db450c6f9b67a0f0e9158ff01f4a955005f899bb7eb8d921c2cf4e03814886d1c239f5a451d9e6a1026540307703dd10a05a24714d7ccd0a4a0c13790c564d83c3d3a2f06a0bc05a1c61445d8a1a40aebac9fce29c3a241ea1d19f175757fa05361f97de1490537c9e0c0fe9bea0ae2b4d014760d083bf1ac8dc300c7b476fe73e89782c02646bb380554fc729de6d7e84b4a0f373b6972129ba59b38b642558fe44e48ec2b07c3def9b782483d8a45dc5ee71ce5f2ce907a836cea49c19e51cfeb0f1b546a60ae3efc45458ade65d02447c74c6ca74061c5324a909fd2b00503aec9ec3ddd84146744b9d29dd9ecd507cdfc617dc91fd1a28648209d385173df6d92f02072f3577ab4e023049b5a0f79506ac6ac893f322888d051f9643dda5e82fdb3a9dea6c69b37008312ff610a0bfdea80026e645207340443fed37069a0fe383b75ed6494fd3d630334568b5209a570fdad7193e1076088607464721c381c121e50a8277fde1fa45252b8eaa0093c45e8714ada412fa188436a26f46c5ab225791057fc6ec5b9223467da3fe1663b6b1a451018306dd342d2124c81e5cd1c74c727bc60407909c9e65297271669c7a921a2cb40321eee41107d80636df7868c993350247387951661232392a971ab5ddef7a26310c617af0981a260c9665c71129c442cf034f627f494a2ca59885a5ce8f1c854b1da0afb25ae9248b1df9a14ff11608dc4a6f8ddcd2d7b39dacc3a35c9f89b08d16ebb6ff7a15520ebbecec07fd074db247d9df3b0af7586012ed8346e8901d0c258cd55cb05ce90f89f0b55cd8593da3d72a350af0e1a9c5d7ac18818e8c2ed516815a67775370220d07270f32a2bab24e5597711b7ece5ade6d223e8c9bd433195c85cfb22324dfc23d98db74abe107938e348182357e25d635b13397c10a5d1a5646bd39f10e13977bd19648d86c980b224741e3950f923c189fa7de5f071c2b675e233a8d975e1ae439254275baf0177cb2fc7d2b56854946a10039a91dc217fa761bb0d32fbdd6454952081adafd2c34b8c868aec6d98f0ce42b92a473d41de0b8e19fe52f03cf4d73e6c2ae367f476aa9db8d11139e4b3bb44588a4c7e72db5ef6579f8014f40bd2d00556a2c42d14c767dee9ae851a918a2ffcc449f4a209afa2d34b900dfc8b5101e846c67d3df2a9094abbdc3ef9f1a9f5da79a06012e37756e68a16cd6da82d916c17a6d00049d7bc40ec8583047a2ff323fc833771588497c7fd1458b07c8b0abc1b68ed2ebd9639d52df43c565be65be3377720525b270e3b0295caf51e1985bcbcc88d0505cb0b7b55b050f2ae1e8db0162582716f9db7a4484d9c768536b3974165a121c546071189caf4eb69b67cfe0cd3b3b1d59107c902e2eb2cdda7b1c7694c673ca206a79f86c1ffca04ba6f33b91ddd00ee174600ee6994a3d302ddbbd15c7828ffe05cf7ccfd91a873b56a46b2501152f803d7e02d9396338acc57809d06ef1d137d82c2557ff5d24764340eff9fd2307f62425a6967429b64d92f334ac221b833ba98697428f2c3e85d309a279bd9e02816cb49946a30fd532a1a0fb89d39ab14a9a894795358324bdeceb41a55dd723daba1de94e2a573870b7b6a8f934adff43734d22b5760b8958863f9b67f1472e94e714fee447cabc676bf034fea6f76208174c2535b4afe9d80204dcaac64909baed85190b56be2dabbe24268ffe8736522454b17ccf33167390d6514980f103d6d6b2a28408e7a1e460f70e572835abcb3e5f421087307f79822ab1db970423c688bc5c514aa0c845d7a880875ac269a7d1a07c190dec94d0adaeebd9b98a1f0388b58ef832e3243a0bf2d16550c3c71c996e75bea5f8213c66b63c2099b40a5e6be7c31ea4943dd66be1b0291b07e00b0863468128b1162f795c5fcce187039276ba1e7a672e4652f683ced72f5ddf271f3b94718bb96e0fe9ab74bb27882521d9cffd656320df03d1f2de945c6b4d1aa98af9c601d1586646fd843228b40ebaabafc00b73f2396c55b101a518a1e9afc8a8ea68a9af963e79ce9941e5d20ee983990f78ffaf203dac89c95e8abae36cdda5de6355f92eff43775335bca410a40d8e2a65586fb089ed470b635cfce7a12ff2f8b3d30799f707f4c755f4762ae8449dc1c2ea29bb8b730ae3ba7bc1f0a4b921d02ddbafe7193af11e58b5dc0ce5b0aba68ef34813d98e93e5e583031b09c03ba7af58d13ad32dc8d01c2745273b080c6255e7ae2d988312fc53fdd72e45a684a444beffdab1ddd37fe6a44d2d86ed6f10435b658fc474e5edc21bb4431d910505eb635e93c7674263c2ec6b1fb34e149f747638a1dcdc050f11dad7b7e8d065aca7506c507402e3377068d604e34eaaf674bfd9fe78ca3abcec0ddcf8f6bdddd63edfb5ac26d3dc71bb55a302eb54b023b184308479aa31779ab38bfe4c079bf64c261927a9ab29024684ef282b9ee273058e423e040347318becef2eb3be691dc31d88a5d4ca81db21351314a73eda94f6c93a9efecf96e5b3b6ff4dc188a0903be1b982d18c64d0cc00a10584ca47a49fbf12c4b47cb84113c978554b15ce45bdaee4248bd39c6604064813a5506d7cc1616735f0cbab341ccb85183f35d85299f22376ec3e462f27ff2922207204f8a3b0d84905b621d1b23396dce3f221802cab32d6f9eab465a923f114c89aeb7e0920c1c6a2d977d1d47925e60fc5d9dda0ae95d65a55b85f79117d0f5157194cf4cf4153465a2159496b100e590e95c49d32f51e269d6deea9211110cf1f23d59f979e136fe641878c5a12ca107ba89cc33a14349bd240b386cd4d0b678476c9b29bd37c1dba6c6f3c0d6c75ebe15d2a0824d717afd4300027b4ac2577ca36f078da8ae3e5a84fc2ef24db140aa937d8ebd806d73ae1fd23085428223722496760f2e56e2fce9c872f3f4d0f01b94a2370b264bddad5635afd70181bbed96e63dd35cffb340dfdeaa33bca7d2d315a7ee4448ed7c4b503f1282d5c038a7eaf34a2615bfc690fe5115879b0e58580c68ad170e7d8bbd686c6796438125cb3371bedb5c2a25ed7f6267303b70f7fc9f61256b40affcd13dcbae955f7207f8f01625ea1f57d1a826ceb00266f81b2e20b858cb83b8d4f60fb8c08154f14f074ecb9e5c37dfdcebebe1512dfb0fb8bdb524eda27dd5c852763ae846fea26575543210da57aeabe876f760921c932a68fe762bbd03d1af766894a1ed86a28da92abde2f9d21ad9471d4a4284ee9fdf80990583ef128320745707bb783d319112c0e40b98c05d59864c555f81ffc838808a57127ec66acbcfd7a2c731f2e0dd9690a47e11ddc33904030cd07b0e3352939885b6262a867de8f9821f0e5f721e58457e22c50272bc506b71b21d3ac2e2f37d8c54b265c26d01ba437a1bc2a07f8401e73dba5feed49c616d8741d05ae99d5a1cd60942089b64ac10a16c108245855b5ae308b2959dce5637cb82ef4c9e79adb804721297abab577dc19b1760206337651b3eb4384cd69f9f3bf18b2d4fa030b030cc6e3080255efb74345542ad4b20297c048fa3a2f70bd883e133a4e872e61519141479b9ffdf24080460519db4fb299366e2208d381dd1259499368682198369a5bde6d6652b8d71a02b310af0409c98816450a84aabaf3ff77b2ef99fc5249a0dff332457c31b66a3fa60370064d536106a22d8204d019b89b1d8b6fef01c337a0f21a62dafef82705da0c6d144be8c7742be9b07f1c3f62b8ec0ace292f09462b8a04dd3b5d070f83752cdfa5a3421ec582635806dbe9f7b4823be70252b199459b582bd30354b7f7d21e2c028f596814988d8192e31d97c6bd00085094c19b1b6102ad40f5e3dc162b139e03a3594640c003fe31235eb52638208faee9019ec21d2eaf1e0806fe0a9b0844255999c8ecbc879487961f7675eb445c026de579a2ab8c64e11af9bdb02b0d649300414155f6bbe2157846e682765ac896378142bf7cc897ad47c6bf922f1cef8aec47001f925029f48ca18e0a67d9564272ebd5c7333265e30d0a59cb940ab65a79efa6fa683f181ac7a041e0281751ed9dbf057fa2e7096b321d9e7f04239c1619ba20a42fcf86ff7bfcb6d5a95d2f2c9fe292eeb82ca7b6aa5b066c902b2f3a182a03e3cd0911d8da65c9ab51f3e3ffd0e3e5420fd33fbd355d82cafa1018f921624106f8f7d730fb60b5c2c376c8aac7cd3be4abdc9bb9abc81e56212b809c432f5e8853cc06d9070e2a711a385d1db13c42aa124db365427d0ae26a2c23bdfdb957e7b43fc759f17cd9b1702b4f6bce659fc70dde9c63978aac49902ab8c18dc483d6da31ace9c12636f2d173c202c50e4d488ee4b8b8af90f8e9a826895f5e63dd0d85cc708cf80940667491fb9dbb106423c5ae2ece55c871d2e42b37c2f0da7018fc93dc69714cdb24c3ea6677f55332868f2fcdc7408d51d949164b1bcf1f55f71aefc1dfffc6420a064081823a25adb3f7b3e6edbf384e21f00931613f859f0e6860156f6d19c40bdfcc8e05ce960555a9491244c19f36c94e02f5f84b0e5d301e5a765aa8376aa85ccfb6da13e611b40335d88fcd2d567aa1","public_inputs_hex":"0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e940000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000041390e9098d147b0784030810969efcf00000000000000000000000000000000c017075b91d1c9c5aadc2f4d3b29027511521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a"},"decryption_aggregator":{"proof_hex":"0x00000000000000000000000000000000000000000000000edce069b20cdd3b3c0000000000000000000000000000000000000000000000035587edaec19bc9d0000000000000000000000000000000000000000000000004e8b61a1848c5d71d0000000000000000000000000000000000000000000000000000ec07f134570a00000000000000000000000000000000000000000000000285b9d961e7514ba30000000000000000000000000000000000000000000000037ced53ee4c6f829d000000000000000000000000000000000000000000000003b4542de3f9235c48000000000000000000000000000000000000000000000000000295d724d1e6c40000000000000000000000000000000000000000000000024984d76621f77c0e000000000000000000000000000000000000000000000008d9170675a02d3baf00000000000000000000000000000000000000000000000a55ee4aa4908eca680000000000000000000000000000000000000000000000000000178c72a0427f00000000000000000000000000000000000000000000000c780a0719338d9b0c00000000000000000000000000000000000000000000000287dc8993e1ea3e03000000000000000000000000000000000000000000000009b918c594689408490000000000000000000000000000000000000000000000000000e6a0f127f38b1c0664c1e064d80eb94bbb91785f1d24ed1d7be9507613a3cf0d4f6bf2e9d7231d1ec3f000b4ae52b58619010189eef29373ab9262aa91aa7143502034d8863011d3224f2e2edd4d00c173d14a0563b3055120bd423e5ba160b9b85989830c122c3d4e983a824d1a6d29966c7111df5c9199915567cd4aba98c44fd0ae834f9a277a09cbb2913101f449f75e3676615906ce01379d63002576ee7da737262b32236564d38f5e7a803c1f8d9ba4618283f2321568857b28f7c1f0a958192e02761c28d6c7d667f72a60c1b47882ac5bd519c2f272339121ba07a2660621dfe03922117962f26bcb850f7c4d6c290c1394ef955cb7d8158b965a8f73ebe55715b60a5ad26fc88ba140f26b746000167b877d731647f2bad22f5e22aa8662107b3903ba9d871d08782e78385c33c1e9322705d793ce23c873db9d99711f08a2fd4419bbd315d1e4fcdf4ecbaa1f63ced3d1a9e6d8224596b8db1f5bb494a2016bde28d00c42c42b88f68c6ad1f771011f95bc6cde5ad55f886bf7594359a1faeec71a2546cb3ae645c2ceba21aaac656c9691eff73811ab3f7599e839f35ecacf0d13122d59cddefd6b314e97ec4518cd154f343b7a3e4a803e21aadfa71512f8500e046fc1a29c86ac30ba80b92d6c88a8ff016611d89caf17b20af9ea60288bc707548bd820b583f244b94937314f51020158fbce2e9118933f203594ad959eed07b4b35a6ec429eaf7613eafa54fb3dcbec9ee7bedf0b142fa863ff3ab979f532a51f208c9b1df2dbc7b483ed886bfa98e9725ea544775c7bb9d667028f4b19627f5ca4f842d8e717dd35f718dc7aaf4ff57825a57ce340181d5bd8e6d2147ff2b1b1daccdc7efe44863337dafc46a94c0c29b71c850f8af09d96d588b92035d04470ad5f8011691979683753d0aba181757afe9aadd77cd8044e703fa50efdc03a037ec4c811132711ffa1d32f74ab4c4d105fce937a1e674aeed5311039f6c1b82ac81638d61248c4974ac1a4204cbefe6558773523e38183701596fc70db51e4ef18838b97f26696532758504247d6204e1bb16d1d4a6f7690e22b92ec22b1e7216c99504649f548ea5e76058166e6ab62d8b597da944b3f251e0a7f5dd550cd43a77afcf6fdc66df86ae1f2a0bcbf217be0e001d5db52d8b348ee751b0191305dc8b39e1a34a8f2b255f13e90c5f4175cb2e3863169b1e6b74eddd072b9f21a8bc5ba5c3f55267fc4c73f55b05016a0db089eda8e476039e4b47da5f9dfe2b79a3acb1133d7b29f45ff225d0c4272bdb5904c6558a7731288c110a549d491d428dfbb0b13b94fe735c89379880bbee36f28f8069de31e430ff99288c66fe0c006bb22bf7b4a9592cd03dffe27a9ed159b0659b109004e5178e4bdef83b282a5e323d882ab264f227bd6470279199a135bd78b51de8c6f36652f1ace3c0990bab78767cbb925396a35508bdbb408df3194860a19d252babb16e91402e2ed12eba5dff99e35eba65c89111f2f8ecb8ab001a0c4d0025d5d2c275efa03b181202fc7f806b44988c8fe287a384ea1afd59ac50a70e7d0d47d94be1897ec5e9e203ac5d9e3f5f1f7cf379e828e01f76efdcccb0614b23beddfa5f6f8cbe09e9e41c6bd67b5e867148d005eb0eb83a20c69edae66cb10f0399f049cc4dfc577fe92f3160e22735ba35b84465fffccdec15f911306df24c11ee96201e4b04d2e1490d1ea90f45ed5669b39f607652462fd9f10ff45449ba0a3d69318d075e365393000429a8ffe54041f667d458a24a950cd629a1f25c88e500b915ef8b8191ae931e7bf88306e191275053149520e06fc4c5ec65452d0f231b2a840a1c2e47c4d6274c2fb4eb7d2ce13cdd3eef4bbaaf730576192979cf71566a194c11723e7cf52a8f8c03407906136dfb54c7618450df427bfe58533534b6f607df93d4e525510fb61f91c34085310910d69d63416b1144cad84e2db53f079311f0c7d2cc880222cd9094f071155155d56e58f28055a5403eada3ea64bd00a15222216f54a58a11d88c720803f206e5ae51935e5afd64b155c4fdb32ad6acf0efc7a2fff6c6dc29aec8db1d185dec27709075fd7366dddb28a9909f20b637e709d5ac48b066d2056728aa6ea0b695a094e8b077280be353b709c208c531be2174262ee74a6ddd16cd375703f431f50f42d497ebfaafb2204fbe31c1db00f847c205a9e5bc54db1f42362fb4a9b97d732b7b2a543c772b785fb139c372ebd00127ff15437a653f0397d45a567e5c6000a4b9ac2812b07511de3a692e5b94e5118dc68dd31ee21507c2f4c9c52f9c54b45ebd3c2918942e1498162908e85bd8cea61ee40e823bc801a3a24b6721333c061e10ebcfd63353a8651b95377d721138d170246bf515650ded72aa95d8673e5c8e1440ad005d31d9f75f78a8402b38522bd14a172eb6171201a44177bd3a939cece9958e1b278c01d40beb9d5b46360c4f07a43b49852420a12fde2f03060a123ad784f488d0b4872e94d10feba6c2fd3959e9a9b1b56210cf7b3b52aa00a90692846a05675ac063d0e933c1bf8f8bb8a99cf90967576308d1f7402a634a8031346f58838220918ebf2a36611413ba04bbee53f23595c2011fec50e061c919f78a127db735b3c640ae6c8f1ea70d4b8216595f35da58041939c77c2d3b9526ddb4971f0b4442675f2e645daaa4cbc3441c424463efa5d42a6350ca221811927c5c08451ea4a05499a29d69b219f2c993d451a437f43d8516e679a796ae6143e42a26668b048b4ce5c0debd5fc4eb4e934d2e6ba7b7b5b31084748288df95597c2f838cd23851420c57562eece196a9cf56f605cd2cc5802ebfbc1e5f4d970648ca521f5dc09194cb6b31cd11e5dc65a808aae5a72b0b8d06f3ed69825af86f15664eaf3d8cf9b9f44edbc0cffde603a1649a04381d008c1586681d08744417cf896484566b7c9f40d9197cc46472eea063bd1763135fc824fd8ff1e9cd4bd25ac764edfa4127ae79f3b4b3236c7e149b0409e4244575bb064648188294df57511c5e320a52988c7a98e71de98924c63cc4df72c10f76770770552f6688be1849aa0a91cca874e842ea9a0d080d3d6eadad68206fbe2ada0d07de35b059ad5e3f3944dcb6a650ee64e230a53f4f04a4e9ec9e3330f9312e182d90545db4f65973f636cbe00905c00d1cf4c173052b4f1deaad0f09239cc80e6477cb573bb32c25ddce44a1b5997eef3b1489a520c3e714f2bd84b33964b020a23f0a9ca584020b90dc27764852b6b9fa3551aab838b649b78767cbf40a1b1b928595c0ff6d5b9a2c8dfbd94eeab852563fe2554b29e55c8966aab39ba2cf11c5e2a750468bd5b34956ea7e85d226cf5f99f6c7d0fec89924bedc43b020120e17513393289e3cf9a0f0a86447b80b25275c4e543758bdfe998290522ae8421a90eba5171c4ce624b6466846d2ae316de1ff93039df1cbfaea396d150477dd040cb85d01066dd0015f3151bf7e2ef022c1be954af6bf1032312afcac83ccab302363d2e381c358a1bddf79f73bbe462b5df71897535eb55e73bfd38ca44f510357a687f0b64fc814ab4f3a84c2a71894b89bd2089f27aa805757662a9c5006200d757d3341fdae52baa65b943829ecf02888b4401bf065989bdd8a2cb71ad31796f5df7bb522beccab8a547cfe2cdbdce2fc7413233b72ced05ce662f88af9123ac93aae51fd8c1e8025da7bc2345b3b110b5e59d2ae2ffc729d0550db5fc60329666ce8fb0d6356568f1ef2618ac7f7e4bf2a6e3ae35b7588780b29e198d52b298c4ed6a93551745f03889c5752475272e91f87e836ea54caa93cddbcfc8613cd6384d28dd49ec99b2232309b798fa694072d9772542aab79584eb091633d202572c6c995c7268e871d4f8840d366099e7c124c6f0f37a91b629079c7835500d621755dbf91a3c126649d7ccaa9c979b09aad02b315869a542dfb4b7366831c2e3955c00d7fbe2c15682d53ac21ce310fea224a7c8d972cad6c4b1415d0a91278e6b67b2a051654d60eacacfe4f32e698bcf8bd7ac192bb696d9e13994133116a66f3ab150806fb12287e271bd614aeb9048dac568c1cff2c1fbe79ade43f133b89887f6f2c8ec436d5b37eea62acd2eaf5508a77662fa8bc2bbf5c39dcb225f3bd14c21670a9294633249d98d602eae71803a11c9bc9277e1f3ae18b2f3919f778be7ca5b0d88337df43757f52b0a273d554fab9f5649c3c9e3deb5f0ed82e597504378f93584af394922da247f0631ec17835c50217ae6f476c04220a57054cdd47a98bacfc724f2ab5d7c7825ce80f5978e502f847663637b90af2d961188d67c326e5be62c40cad44d5d128f5b18fec898bb36bc426c93f982f6d470d17bdb5f79943b681d033607bdbe16be4da1a1dd8fe1e61ac11a1f2b4cea2a3fe26d2e66df0f886c561d05372a059845d520c1f729b37c5ca4093b912fd806d9e15d647dee4fa61a39a465c97ce7cb5a3793b99986163121f8545744c3ad753f82ffe37a90ced4f23815bda2c9278462546ca5ae118e198c4097d3e7dc28e03be11f8ba469496e5bd7d9b6bf4a8b5d56b480e205e03e82cce8b21f464a4dbb6d52374e84c1acdfb69fa9e531159ff2f6f261fab2e661214c69bb7d93f5f0101ec12e85651e59e0436e3af84b244b29464d5237e0de9f96b68da0e0e756b013792218cae5ba282e49b29b371bb74fb66dd8019240639ca2424c3e739ca210a056506c0631528a20cb89f1c2a8ecc7dea173b6ded9a7ccfc9abe627af2d7ea6591a151c1673a09d84f1132062ddbee11993003c72dd6f4de585c418319fd64fb87e18f994b3c90618cd566283a36193487235f5e20b3eab9b15e3e842246b3df0720ff304ddad5e0d9b443938d38b7c6c8ce9341d9ae8071016a21fc694f170002d163f7f12d068f82cc36e51ff8eac359d86fea4f76ce490557e9c8d628cd2ec7311d270d3b58ff7ba7d52f5d7d3c8903dba14149a80ab7c9ed09988a5ea0b82620fc56bd2241ec60e84caa696384f1094b28877aa06e1061600b4e581ab5bfa85176412243451efbb1dae13d6e78968d3476151211492d8d6bea0a32a28eb70c814fcec1e1a77683784680cccec1bc494e96aae146a4e663a8dee882caf2895d80be4fde7baa3a49d24c53c0bc5f54b6367c26837215974a6cfa2ee738edf84b21115cf41b00618506749df9221f329a01dea694776bf3abaa9fbb352f9454ba01fe2a5c04286be6419fdd0afb3d051ad92a46178d89410fd158b32d3625afacd0232156648d27127995b308f527364b3162e0eac7db4e1c9d7222b263d2093071643a985a9493381f9224fe080f7c49e0c1f178b1cf486bdf027b5677f67194f17990a030cd95227782c3ce2d6184ad12c5988823233924a13ec460a43db62a518a59ccd5f1069fc98131ca373c6b618fa7792591edb22dd0c242b5447019f57229b7191aabb525535024a14782f1ff968b3a1e8be97503ae7cefb6aa8fd3a352f3db9f3b9a9e6ce148345848e3988651f5472abfe980dbb8743ac95f00720131054d873d7ef9d60eb7bdd6af577d252f486808d1e8449abc1d68b69f4eb56811a8ff281cdaac4ef304f4582285d5e403c5367a43eb7027b36be9351bfec94cb0b607e652ba04957a4c0fc0482cbaffaf8932b90d94796e989278b0d100b92f826b213ac5b4b3b8f3028b80f591d0274f9a65fb57e385ecf47e33ecafcc031ae2e8c65f2a0175b4ec3b0ebdd832f955e11178b2591b7cb64f3cef1263b56b4bb2a77690f4fada83c47188bbb06ad5c506a2bbe442b763efc4fe3ea2fbc21e7e12fd3c530be1d11cd7e2272464c70d36f6a87717d45c02039d089cec0e1e812bc0f0f466212d8f19f9582712cc3d7f4de6c659abd9ec8682cf25bd9c827349d1f2f9b83f6082eb0d1f5f99f966f8411cce43872f5299bdab07880b3cfa693c9da160c1460f44d22314988c3a6dbe5127978d96a9dc6cdb3e523ff3a7dc68192cc133676d1aeecfb4209c6cf678cf1dade494eb15819b2171f776abba11bb5ec951a46ef47e4bfa4cec4e833fcac0a9fcbf9ce8fa3dbba049ee45162512a1d8eff00670e517adc74f7cd0627f765323e0115bf5d66b6302c28b66300112c7809a720ea9b409f976ebf64fc48d0654945028900fed3563e17258bb0ed64d410660a2819a435f89469d02c009c5d5c810725651ad230e22f758768ebad58dd9559b527cbeb59f0d6c6bcfe965596dea8e090fdbd70e9f18d5d6199cad88165f329d612b8518edc1bc88f0431bb6999172a8802c4ed1c417dd26aca97ec6358938353292e3ede19a0fd0e699fc08f072829424760b71457eadb10eb25e52987144f92080e4c20439d7bf3fc26bd9f95d7c87dc30e6f6f5ec033a751f2006d050c75961da6b26abbed3ba81398b3a374371dfa96a1b6ed1af5b9794c5789d0b9a912181e090050f3ef76dbd74eb1b18101e748671915502be94c168d34a44382caaf3816c14e6456dd88c14d8e68e2a0d3624e29cc00d6d0086d6fcac00011208602d21276b44c40d08f7edb5f2dedbd3e7a955ee1f92f0806854cad1ff3bf05cf91a3103b631f8c0563591c5d1e70e97e16b1c7d05b505ab1643e49146500cb77eee01d79a14852e3fa0beafda3aad1e41941c3e2c9dc4c4630f1218656d719506dad0a2a35a616a505d4ce99fe5e476f59413c41b02fc17c1c6dd0e2a06fd16c203e0d43613f60be6b6ef8389331dd4fedfc337d72277d459c4f6a5f6f5923716f2a1c23488142dc51a76fe1cadb7a3a1989ad7e79d1e63b75e8952f85ececd4851f22098299a53a0608b5eefe88e783328a37526cf50b972c25f782a2e4b8c0832c302ce119c943a5659209e9395d43a1d411fda6b050c14e7e1ddcfc24df068e781c194e17b295ba95cfd03df3c17d0ee6a0e36cc8f432b6c339d1d81bd80b621804194513ee0d0de54e0c0d2eb0bc3900cdbaaa7f49e6dd212d386a15507ef3f8200f7c2a499aaba3d59ebee961ebb27ef96f5c1d8b11b46d6fab6162a3f22f8315578c760b7d247b7c7aca1925eb17442fbd0010842c7faebee5e0d3df7cbd6901dd2c01f4ef6e0234a6b5ee31e14e71a44f652f51db365417e717efd7cf0ba920e728f72b1246ba4a66757bc3d6986178a01c5818dd0a1a4fc7ecb03465903b103629d6df9333ca804018e7d7422aad2a87673bd59d3b53e429b9abb82559921620f508d103296b6915ce66e75611f67f8500111ed1ad5139744aa1f4828c3d07f2bfe7999bd6a3eb38147a887de506421fedac4bdc00ec8138f985fb7d034a01300c4b88c4adc6eff96c0c86ae338b902caf73936434123b14e06525d0ccd12ab7cf13b2e53f7590b841b8fe4aae1252fd75c40e2069390e5ffbb7ab3674a028d85230eff2bf8759207cc777ebb790986beb140f46d20e9bc7f4ba078dd1cc05d91b86bb252b812317d54453271888c960dbea5ca6d90195f0dfbe8e9c5d9203fb3c294d6b473b0a16ff79ffdb3ec24daf6d59dbef2fca6013dc937fd103572c818726e3561594626eaa1f45db3f64a8964104a9272ab81af3a1c6bb478adc1020f11aa1bd01ee31f58f4ebb848520510dfbc05408cc2787e367ef0c31e41328e0accdb8de5dc22799b8805f173931694094f7c0e5de52dcd41d815d5b6c7c2daa0496def1b376e3a51d49025d9f4041715a2b593d7423d0248819a54246e5275cffd211d8ef63b85f2b0958c61c32d58abbd65ef70ec68ce84be78310d6ef264bb4a81ad1622c4fb0d54040f90d90786174ed1e353935ee3f169a98aa2d58029207afc41769c02bd3deca0c5403a93efac655b6a56ac4d4838dd6a08b1e1a1410888fe7e934796fbcc11d77c3b0c2edb80437a769787966939c9cd55a364f3061778bcbe570cdf79ee300718671ba9baad1c30888faafd754749d691e71801b9821a796991690747083227e61d17ef7a39b511cd5b007568a1758a9395fd016df8a16e4e56ec6379bf190011628638fc021a696e3bd9603d9f3fd7e1e2c571a4b5945b7cf7dd86b8981ef6b97b7bafc2b22a55bd6368091f03a645ef0df97279acee5fa55dee3ab0191d1633027c14609a74332b0eadd3d6f3ed85a7956680f2f1c5a7c9445d8390821c05fcc9aadabee41ade1fa32e9421313381aefc7b3170feae96e81634e042eedc9d30d9e1d523a550d682760ddc428c98fef51b1e221048fc6822f1c3e89a26903106cb0cafaa69eeefdc7ef3305925ffeceac96e121206290ce9d91c8ff16641033910e84b4bd4bc1ee56db43b62ba86dfded8ca113d00114389bbca8cae5cef3c14f725ad380e7d712f7d93a1e588647cfdbdc5402c7d4a698eb18c2bdfede827d90ffacdea8cc298a5629b28514bf731b7998f82e19b16c332526bc1b3713c5b41b16489bb6228f176ee6f96cc286665fc5db930036af42600c67bbf26009805dcbf054487b9b0e1c2cd7173f38be7459892ec90652b26f72abad8dfd40edde3511e0898abf30e957b7e70b725b2653cec4234d180fead76b8ae6be8b7e18ed0f6a6922f146b264ed870aec50f2bbf7e4d178b81fc952955a05a27f760b0adfb14fd86c9e21c4b6ab0074e6df091e08653b89d324e1e066a0f2825afb9be08e90f11ab66a57602c43d45ab5f7d97ef60a8670a61fdb46b341552a18fad736bed3ae77890a408470e7c308696f37ad26289f8a7f00cb96b31fcc1d4b0cf0f058943e8249b5723f031c2a02d659c2a761fa0d05d002781fac13ceb35217f82bc90dcc84665530ef2ef7e326470ca0fae8f6771a0b181d7f987290f7946bad4dd103156d149d2f4d7fd9350246dd54b274f5a9c2ef256421db001f48a5a3b95deaa6967db143533599dda5154770528c1c4a01f2bc246ec9f2caa1539470292b344cf2ad33e67a96fdd2e7d07450df1ea9e5d36bd510b5a2b1d115be0f0700e1a5652fe4b304d99ed587e6ff6955cd02469a5fecf829b17341a8f46cb83222faacbd1f539b89a186ded5664e49c7deace0b5233b10275cdc7135f9be5e3c62a640ca27b22b92456b9e6f146bd6e805cb071d32b2dd00449a613510837c67588fa39fe0e49fbf31b99b5144c9476150973ac9f0a4591d17e76456b59e88170401c1d4a92b94f17ec904a2374dee427b7664a6b2c22e240e1ca86974fd9e5e172d8647abea27a002645a90db6bcbc5e88dff24f1743323be66dd4df719696f2bd75926dcda82d3accd3c865cf42853941ef41de95af32c261b646deadb7d82ddb53574514e261a14aa12cf773f483d2545dc4a45105e23de104c8ca2eaa8d94c42f4ec7c0e11279a62ebba987e6c329eb49f2037b8092352ce3c21318796a17ce472d8ffde018425fc0b4c11f3e492475d2afe0c9ae00b0cdb4f51df51cc833ddcaa3aa78b74df3d936893f0a5714b7810fabdb17c0810d19fa485bd97f1507a77fb95b2a0c5b1ce5830dea0de18ef5f6c20a00e15d12331f7d203d3a5c9d261cdc4db79b82c59a02ec3746ce141a29564fc67a7e6ad0035707ce5a02d5035190ce39d1ade935bce8d2db6bf855b748412c8f6b441480b1da708c7587d576017bfee5fd7339097c4e205a4f0deb9385a3a0f7806fbc92f5490de7ba18539c7d8c659406341579581b124dce903c88eb72a4f4a86eb22093addd77a4a8b8f2fe48a564d820eae33127cfb442fedb91ed8eae5381ba9f1046e54e4e5c6887713366918f0ef69869a1e1c1254bd5e205917d5d5f7571a2e1c9b575c9df2ea2c0f6738f4a4206330ac1320b7f16f1906c4b067a64294082e2d754c8fa48c6470cf77edb7e03bb86f4a396b7ae2dbc56fbef172ef0cc08caa219968432847a0b5b933f0498d92e427b6e0d75969eeea525ce41eac022e468c0514420c561aff6c8edf72164ace360ef5ffd7cc2ed742cb8cfbaf855a20491a228ee0455b1013c4b2788e4afe5f5c3cb56aeea9c0aba0c2a3a99939fddc306c2e0d4dac3598daf10471cc14ebc2233d710393ea6ef90252aa70e609081040e814d26daaac5656530fc3586cef2d6c4743200ad764b5e4849b3afb18e28c13512271c93f14df40b13bb0e4ba277e2d20080be06d4b74dab1c3b51ad544e763f125ebcaf349f694dd1f6b8c60c2794ee456f96176bff72351704e7620228cf9352b65dc2e4ce420d8ba84e849a41e2986f0bbafc592af6a2eac9a74d6646379bf22dda3078b0408cacf02cb1a4fb0d4effcacdef4e5aff14cad3d90b0b79cc149129a4bdb67b7ba6c4333b1167dc29e2c02ae1130f373e25a4e602dbe1f9b88532001917a73ed406cb1019c2fe78c0278c373405583d6b76103ce83ac88def2ae0a8be4e4d15523c70c2baae6d7a2d10818b8894c593f7dac6103a75be2c4f6531ed679400c7fa71c82c9b8856c0d03dff14d810a3863018124c30e248a61eb93227f470138981ff90eb35b5ccfb27b22f9c3e99c4b8470896d7618b570015aa826ec67f903e741cd58a559e1c5451579265ebc761ea6a358754df10ebd689c5218df613d6ac78aa2720071cac7fb34ad6ad194e3e654b8a344971ae9a0d9fd530f3fc109ed0f01a69f6ef0208f0edeba13cfca9d6fa2999f44beac2fdbfcdd312c3132400d2921f726b2eac70133a0dd787448687c557fc1e69083e8169747421a39b826c54c494f1ac98dec8be7e330044daf7a0bf4eef11c79644bcff8298e0dbb24bb3a782d13457901c8e2ee0609dc1247478b2001d0d6c9db518ac9068208fd83bfe7d3508abdfa2503267f7a45de285633535685b6da31d63d4e2ff0720154ea6904ca630ce1857e3e8a1464cb6b5b16766638074546226ab14fde7afe2c0e86924792862a3f8832e46311b61bf635a27a61b3cfdffe662e7b8341fb9514e51b6f5688113dc4f5f812945d7a8ccfad91ede7fe0a607dfae5741848c88f01827ab0ecad4343b879f24bf632dcf9ce5db1568b3dcb0e92a3ea5d32bea5bc06fafea5a53fcf97c3dbe274575ef305fbde8c7eb4bfbd8e48bb3b062bce895e258826ec523ced0e17c77f73323f26abfeac8c7d8e401107f7240757fd4490841615f11738971be20699fc6c8afe8c9cdc2a78df1dd876c7265f3824b959bb5212c9b389a43203fd0b16ca64615dd125232f77f77153f9983d1a3c54b847aff7022cb4a694c0f04531367c7cd278a7f7a5aad796dca8eb486791d297a593f69912bd5a038e02b40783d1345f2cb58f787712a1ab0316cc7bc64d21d6035363f51a529233a8e89a1dea82a455dd42bc3795828066ab8dcf048ee7351d55a9a7870a0dee695599b4a9bd9a6a5cf01e1dbf2d89ce252c15cbe5271c2c2815edaf9f05db450c973db3930f9a551e2af7210dd55bcdda11077913f597781cd3a16457088bd5d22510baf958ef5e5d75a44055207b030f5bea7a15e7d6263aa056e9db1369a3521bb7832d51831151d9344364cc9337c3560d387864b7382aad762ff81f0a1adb64a26323cbf1d2f2be86ee22c9ce9dbfa51860184340226563b1b8af04deb0a186abb9d3d399c0a8548582177aa8378309d08aadd8aafa187508e9bd146d89b17af7b31920d5e2f7ea2b5881a86e81376d927a6a75ef1ad8dac6140219e02d5d2df2b473fd67a74b14fde0df6c9e9bb9253edb6d885b748527f90b30265690d0032e77862cbe2c5157330f90e10db865899ba2004fb7aac420bc40540b027d6a2389de4a85895dae7e6438772f59708f6ab2c0a0e8949d890d15d5fb14cb09a333e1d04ba6263268e7f6bda2915967987f0d2e45df0eb9e601aae96312e39e1add5778ec0e13ea0feb65580f7d4b73942aa5fb43cef4260dfd71cd6c25ca74631d5f182895f93bd853d8fe8c13db6e6a3064f4984cc00f502049db6b2f3fc84e7f072beba9e1e0da592e03103c6884b2241da120a6ce8f0dec4a023e0bb68670913ad55bd31adca44ef3afdb24a74965e6a5a4f6423c682b3f2505f119b8b942abb373d83f159674fbd223b4082c6a943ed5f6a3c0c30b4cba04c56d296e61d11f994ec45b3eb7c0fadce0892b09588510deeb84ad0fb43af65800ce1cf2b42a1f77afa03429b0e724ee01aeed3c0f0da7c26e9e0841f6f3e6995a6c26394ee8c1cf6f966c69582a31ecde78b71a2cb6980da3b820348502c53b6e762025993aab0dda298ac2c722b379d1769978b065166b59c953e243260294dc7f0811a8e2eead9e087b065819882752fca41693873712be6b9592ae57f30124af16a149d2e3e18080acb56723895e4b6b960a2e565c13bdbd7f4a56947457ace609911477284348841790e22ffb6407be7a6a3cc3cfa687abb560e83b8f95b12f11ee57180949ab894c6f63194f9795bb301e12258bda6bd19eef208895d3646407fc4b759cd026c4d8859b45b2db5951a4567b74ac30cb25763fbe11ab5cefab02121a2d8d2f4e68fa8da95a1d857eb9c915a24e6d65cacfdfea17b4f7c20b3a2a449be2dad4aa3acae6e681796e2e1c80d8e2be9ba6f33c83eb5efd04de8fca143d6424e42e247559e0d90173a80aa319d3698c58d33eeb2bd7224c03fe44de1bc549874bc66035e4ee132138100bb60a755a543c6598a5f06e0ef5d37d66891ab1b930794b5c2f560004d2356bb0b6d1e1bee7760b5f7d6e676b61896e91e72ca9789023470f3e234285e254c88fe67302f0c605e59670a402401135af417519c6454310e55f64af70feea1e255cee278895ad44da1a078bbf0f676dd705f605ff24647ad14d866c1b6d0670d72dbff0f44d02b049695345c6105d280c45791a7d544c5ef1225e1fb2bc196bb9aee9c3d7f0ae59c4346f6ef9e2042ce4906712a0284ceacf4e137c3e49b7305912c45069a5009e94d0de009b8412b1c101bd13792df3d482832d5622d9765b0829e4ba254614a09747aa80f7f11cd11f63c20711b449ae52188e09085afc42d7d3217f74f29686c3a5e4936e31ef89fb578f2655924c25b7b1a31ad2f2019eefc28890279d30b8fc665ee19dd8356f9eef3f0f754a9b70b8dbec4e7285dfc7f0ff474334c6bbdd8cd3c28f964fe49a17f0da160d18b7defd79fae2b4a2a0d713034f58e89606b1c905c31d316d878cda2f331f4f17fd9ace21980261985cdf4b609d527278aa16ab00b84f910d4193f280190563d7ed91b44e80458bad71e43f4338205ded6dc8fe68809fcad4460e3d1ddc2d2a4417c4dd117adb272f1fd4b5077d3f1b108fdb6648403ed5af2132302a2b2825988faee937f12c17e377767a51a562014dc54f7043c27df06d554e61611821961b9bec98d5c2570c9f6fe8487dbecc40137e68fa352623d570be4791c99f0800ba8d3b538ff0c41b13d298ba9924a3f180d85bcf37e477edf58d311f11af03fca8f7e3bbce16b2162db1ba146b4e895f914de18f120676a9471bc0cdcddf06d65a86b6b351545c1b659a08df6f9e19f66c3e0ac434fa776037a4e5ee4b3811b508a56849c1eae0857281bf4632fb10c571f2dc118a695db6f242ce9fd044295545f486838cb226f250ebf115a28f9f3e6be7b7c89876e31069d9ea516f0815289ee7e0afd2f36c1b2ab344cb6bef65eb037d2e4d2f16cf5504daf24290c00bd40ee9122929b83e4254115a940b02985fef66afdf26bfefdccddfbe2f25650fc238758bac9ed132a290886daac1dfc82dc410475fb48d8cf51ab1b19d73d829b0cfc486ebfb3a89eb25393460833342fcd2092edffaf42d364b7d80fd766a1270ac3e8f5c706c97f8d90728a2839a8f2bfa3bc81939a540515ee8b6f4275c1be4ec2588f003e9474418d88830187c2ce192fd7c69c1907d0e73826c3f86390c0af6fbc19e6a0db23b87a09ecd4de039db42fcb862c3315cf4580d5345d32606bb0085509bca0d4a5eaa1f3b0a34432db80b51b372b3cc68a16d16edd004a70796d5b65c46f72f16383ac710b0105a0a9f4b1efec577d5c4af544e9cddbf942062e58be832605b62a9c89c818605ec5eb258ebe45ce66d99e1df8ea696d1ea07f1787d1346652c451651f25e1134e6cc24bd4557df3930d2db2550f98653001ed3a64ac328546bf26e19e7dfd77c354788d256fbd110b9c266dc66aea7e2ae0b7cbf43d883fb25c91d1b778e277812b660b5fbcff53511027ba6d54afbd88d1adb25a5c52d05da1438ce5b88f7f61917547932ebc310994832fc7588df225b1c393e22f16abb8017e1118e20736e7844a3fad04dea0fce59cb6d2f477904af2cd57babe35dc13c3964fb7b49be5b34f676fbcc1123ca7063f42fcbd9d2f9f31dd9dd09efc55e5dd647b395c73e84834380708e332436b329f726a0a7ed75830f110176b0e1d9d3f313e2d89a5d1e0e053fbf010e3e20fdf030799fc73c51cd2772ad06cfd94569f5b8a998cba9dde2b6b4e50ac35f7970e38b814e063c368407349b0d618635aca08298e56598879e02db373cd6ed499931c2c617346fd15a23d8a1a020f6b71cefd7744a8b42921d02253d8d84db053065ba311bf7f5af1401196a38b0999ec52fbe6ced3f7464ef2472f4bac6cfee49af2581c31e5034980eab6404e8aa8cadfa913a7492149b7bbb8dc6fe77616870c2090e32d7ddc7692d0fb3a73ab606111690b73c8959bc3af3755223e56d07e34c8e4d0b492b3bcb1b96798b13cc92b1638a3f885d83e9f30b14cfdae34a307c233c578190d65aad0f421ecd053dea61996f8adf5906cbb7dd5e7a87169580a1ce5328d3d0937b28","public_inputs_hex":"0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000335fc711e25eec4bc7002806d9b00c9600000000000000000000000000000000cdab8c25e7ffe0274955ed025ec557bd01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db70000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}}}, "test_exit_code": { "crisp": 0, "folded_export": 0, diff --git a/circuits/benchmarks/results_insecure/integration_summary.json b/circuits/benchmarks/results_insecure/integration_summary.json index 7b2f72220..4ea1ad0cb 100644 --- a/circuits/benchmarks/results_insecure/integration_summary.json +++ b/circuits/benchmarks/results_insecure/integration_summary.json @@ -8,164 +8,164 @@ "operation_timings": [ { "name": "CalculateDecryptionKey", - "avg_seconds": 0.10908618, + "avg_seconds": 0.112547319, "runs": 3, - "total_seconds": 0.327258541 + "total_seconds": 0.337641958 }, { "name": "CalculateDecryptionShare", - "avg_seconds": 0.608034097, + "avg_seconds": 0.623812430, "runs": 3, - "total_seconds": 1.824102292 + "total_seconds": 1.871437292 }, { "name": "CalculateThresholdDecryption", - "avg_seconds": 0.556764833, + "avg_seconds": 0.565414459, "runs": 1, - "total_seconds": 0.556764833 + "total_seconds": 0.565414459 }, { "name": "GenEsiSss", - "avg_seconds": 0.124542333, + "avg_seconds": 0.125009639, "runs": 3, - "total_seconds": 0.373627 + "total_seconds": 0.375028917 }, { "name": "GenPkShareAndSkSss", - "avg_seconds": 0.222099139, + "avg_seconds": 0.230471250, "runs": 3, - "total_seconds": 0.666297417 + "total_seconds": 0.691413750 }, { "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 8.418797042, + "avg_seconds": 8.597811291, "runs": 1, - "total_seconds": 8.418797042 + "total_seconds": 8.597811291 }, { "name": "ZkDecryptionAggregation", - "avg_seconds": 48.00139975, + "avg_seconds": 51.185872000, "runs": 1, - "total_seconds": 48.00139975 + "total_seconds": 51.185872000 }, { "name": "ZkDkgAggregation", - "avg_seconds": 19.870362291, + "avg_seconds": 21.084386667, "runs": 1, - "total_seconds": 19.870362291 + "total_seconds": 21.084386667 }, { "name": "ZkDkgShareDecryption", - "avg_seconds": 1.439776368, + "avg_seconds": 1.499727389, "runs": 6, - "total_seconds": 8.638658209 + "total_seconds": 8.998364335 }, { "name": "ZkNodeDkgFold", - "avg_seconds": 60.322392749, + "avg_seconds": 63.746265722, "runs": 3, - "total_seconds": 180.967178249 + "total_seconds": 191.238797167 }, { "name": "ZkPkAggregation", - "avg_seconds": 2.121552416, + "avg_seconds": 2.189493875, "runs": 1, - "total_seconds": 2.121552416 + "total_seconds": 2.189493875 }, { "name": "ZkPkBfv", - "avg_seconds": 0.329691472, + "avg_seconds": 0.344567750, "runs": 3, - "total_seconds": 0.989074416 + "total_seconds": 1.033703250 }, { "name": "ZkPkGeneration", - "avg_seconds": 1.332915194, + "avg_seconds": 1.381717805, "runs": 3, - "total_seconds": 3.998745584 + "total_seconds": 4.145153416 }, { "name": "ZkShareComputation", - "avg_seconds": 2.649475611, + "avg_seconds": 2.753499194, "runs": 6, - "total_seconds": 15.896853666 + "total_seconds": 16.520995168 }, { "name": "ZkShareEncryption", - "avg_seconds": 2.470565315, + "avg_seconds": 2.574498532, "runs": 24, - "total_seconds": 59.293567582 + "total_seconds": 61.787964790 }, { "name": "ZkThresholdShareDecryption", - "avg_seconds": 6.067798569, + "avg_seconds": 6.255039736, "runs": 3, - "total_seconds": 18.203395708 + "total_seconds": 18.765119210 }, { "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.094844083, + "avg_seconds": 0.099047375, "runs": 3, - "total_seconds": 0.284532251 + "total_seconds": 0.297142125 }, { "name": "ZkVerifyShareProofs", - "avg_seconds": 0.207447841, + "avg_seconds": 0.227246316, "runs": 5, - "total_seconds": 1.037239207 + "total_seconds": 1.136231584 } ], - "operation_timings_total_seconds": 371.469406454, + "operation_timings_total_seconds": 390.821971254, "timings_seconds": [ { "label": "Starting trbfv actor test", - "seconds": 0e-9 + "seconds": 0E-9 }, { "label": "Setup completed", - "seconds": 3.030690125 + "seconds": 3.056267000 }, { "label": "Committee Setup Completed", - "seconds": 20.213050291 + "seconds": 20.242110208 }, { "label": "Committee Finalization Complete", - "seconds": 0.006164875 + "seconds": 0.006024833 }, { "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 295.255807583 + "seconds": 310.649556208 }, { "label": "E3Request -> PublicKeyAggregated", - "seconds": 297.75637375 + "seconds": 313.306656917 }, { "label": "Application CT Gen", - "seconds": 0.311138833 + "seconds": 0.321336625 }, { "label": "Running FHE Application", - "seconds": 0.003876709 + "seconds": 0.003349958 }, { "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 78.078044792 + "seconds": 82.112244417 }, { "label": "Entire Test", - "seconds": 399.405836208 + "seconds": 419.055032166 } ], "folded_artifacts": { "dkg_aggregator": { - "proof_hex": "0x0000000000000000000000000000000000000000000000063115aef9dd42310a0000000000000000000000000000000000000000000000047698484c13252b5300000000000000000000000000000000000000000000000793a399b1d4073f7f000000000000000000000000000000000000000000000000000188a919b07d57000000000000000000000000000000000000000000000009d59f2c2a28ddd9ac00000000000000000000000000000000000000000000000ed82c9dbec6f22f5100000000000000000000000000000000000000000000000074f0d5d0b895fa0100000000000000000000000000000000000000000000000000018f934b25e9460000000000000000000000000000000000000000000000050b758b170434dc43000000000000000000000000000000000000000000000005e705b87c810fcf1200000000000000000000000000000000000000000000000ac947aa7ac1d9ea30000000000000000000000000000000000000000000000000000214ef97e1cb8a00000000000000000000000000000000000000000000000c365fa58673f1280100000000000000000000000000000000000000000000000c0306cbbd1646e85800000000000000000000000000000000000000000000000b5f507047ada95ff6000000000000000000000000000000000000000000000000000275a4179079091d0fd7adeb7812fd07faadde449e629089b4cdc0c410fcac699af6d2c4e3fea92a5b53ce90b5e0ff0cfe567d996cb6aca1a1246606ca25b86c909f630bebca00265a64231072629f487aeee5ce8d7e010733367bf831e7f63d1b7101497d90491070c09fd0b855118c842cd15ae0320fd756e7ca1dbd3b7010f8e5ae5b268fc00b7dbd954930cd55f698bc9bc8d5f16cc60d4df84f28f2a6290b46501147f98f039fae43b805d73e009418bc836a43788c01be8dc8e9a92b3cb5717edada8b1e233a591b22a8a5ece11574df80c26d70963dbfe6fc235884d78eb51e724f03962378b21b29354376429108bb6667ea87c52a589cf5014aca06c883fe1fd6254f02f8d24d0251b16b997167504500d2a698f1883396eb8bfaa0061bd0fd50473e121dccb4babfdbf5e403dc53a7f4edb47ba6f30b888e190798f67b8fa7ea8b9617fbfbadec069413712b80ad7ef070d6c7967299514921dbb2b933ddd4ffe6710fe639c41c1ae67b0b2f557986d1b5c889c5081a42d8723c2febf8a2298726cc242d340effd764bbf2ee85a4a2ca104d4a3a60cd2321f21bcdb6cf6fe0b2219a030b2c4ab077ed77a64c633ad1994c5be4ae7c3fc40f569e5e913f2193318d9408b0acd9b422395ff688ed0684b8668a67424d2ba723555c9bab7c8bff816ddb1b2d5453d71b602b9f82dbca9466a34c4c7fe94b8b9261086540664b893483260b9b0f50511620158ee8ba0c4430b02da78047a1b0c5b489e9693db6c17f9af330134d427e392d98ddc08373e170b14fa5e12686a51b1b4fc43c8e32f92054181a7de76b8d346ea02fcff8e3ef3cf773cf0171ae0ec5dafb45fd1d20a98ecd0626a9f499e039070050431240f77f24ae171d4756989fc8564cfe1152bc4a478124642a1ff2e2cca217e047052d415ed8113a2ef10448e389e11b55d505ed4568291be4d6e5ad8ddfef6ab61935c38c48101a7a6e7d995206da76cf3d9ce6008110adaec17d2ea8c31ca166ed8d74323023ce5f832083c83cf383a42bb4d522b116a8d89a2e753064230698277590b0102e75ae48c79bb35abbe6e5fc43c9050517546178ed4c05e57f9fb4cc0a5ef089754d543b659ddc1b7121b757f3f9322b0ce7b3447d36496bd9afed562bdac1a231efac0b51999ffd720fa1ca9d07d5271b5b28b8100030f24305dc42a262b83eb2253d8fe199e23ff0b7c3bec3d9a2912eedd6a98e8f675ac664338943124fd5b44b419eac45acff1323ecd493f686ee27631ecf5496ba04fa946b95bfb10fb1d2d43e86a13c109c5f59c95726f3bafa1e5cc25e35a370ab5d0dd951bdd459a6d27a41c5b103f2206b50779174d3b0bc1c952e9214d27a1dbddb5de018cc5937f9db710a17a28e984a8d91e886f4fa640cf57674741fba4d28e2d35151eb618a4504b2c048b2604539abf0876d65716509e484b67957d3fa601a61ed1d37a999cd5d2663ef8602949cd3a1a2b71ae7071fd6c07cef8f0451e5f43a26f702287797d6d74b55998c4e20e6eec8530cf87a09e0149dba7a7411b2ddc83d9fb216f11b3d1ee08fb6f903831fcfa9fbedb3a12e4011636680096e69ee044ed59db4ddad9977b87364026dbe1507e11ea79e5b264750abd4ba0b668bfa90dac3a450101352696bdf69d5332cda48b386e1fddb23114df1be460f352ffdbfe29e2e5ac8ac7f6470f9efa905a115e1e9adc64a1f2023e890f0dc2d9d68391ae052a1cf110a0cf5c3215e7de2f984f1c1048b8cd30ed40af36a815ea0eca7201590ff2b20e5b104cb449df088a2a8c5269526749408494e2977f7c1a0212942ee09a2ef6a6ae21170921e58981887c809e5b2d82326630621510eb59c904ce15fc0497d2ba78b1bf8eca3800318961703a5a40a0820dbd19315824c1af9ca4b94dccee9c7e15597acaecf2633159ac23022d6c1120fb8369440d8e1ea9cdf387e58e1909c3d4ed280686b046b6bde738eeb6d5d4509c03f9ec131655669fe27cf126d44aadd6aaf253dd5e53f15603cef4640db5b0590563558b05af450b3d54dc74f51a807c713c595af076c43fa2432993b05e6300c2864be15e52bba2b6fb00a7bbeb534bb77d858c23cf4e2aa98cae797c8071f9c038cac8946f77b603e91f89cd186a752ca515376178fad9d87e4da92f11e2e150a97faa9dd1070255507a1602af276f1d2f28c7a6a537644aa27f552ddad12243b315fdece7cf05b814bbc25715c24a098389030b910b73ccc31cb446a312792f8b831c8d9e3473d7bb32719326c088390cf3200cf1b2712a1e8109033df0a2826c610569f292da390590e7bfe1bf580c318b3ee0024d49bd8fccdb997a42a429d2c1e59fb115f9b21337237263cfbbb392164610c3587cabae55d6e53211f9f29bd4f1a0af73e75c094f89c2212c783cb7800b8ae6de0b2f793e50f753e1d28439ddaf4750075879b4cb7df5facdc2856519a422ca77992a9edbf573e180f521f7963f1795e1b453f0ef9cc04de2a32c9869794fefa9269f6c84d9e9d1321c9585efd32ae9d1c8fc4e83d3e44d64c77204c232ed31b493b2677e6c5b06a0ff2062506f6785feb3782197ca25e13e111851a229f6587b322bb993d2917e017a199a263c00d46d9f5ba1dfd8f6c6a1043881b27e2bda1ccd544375b95197e283bcaaec150d651f13d40395231c76f7ed439db3bb179332261c09a51ebc77c0e94c0ae2f92699f663765ee759ec63ef6768baf1792ca7fba45f6f7ef302dc125f6af22946ac84161f57f35aafe2c8bd2607e12900196573f1f6d39c66aef592a16db837b55bf3a42587aec8a50311e5081f0f97c37b5d6b17e885ddaebe6190429a13327377e64a15fef6108a5143959a93ce97e79df26f960117c3e2e83ee1dba0ecb0c98f726af182125d09bb22cce9a5c9d15ff9f05d9bb8d1d5fad601716e0abc590a5dc8f77ea98b616cd569a199c51787e08d49359fe745f08e1d81821005529d844e14dd8b81d7a1b3598abbc2ca3e90bb45cd3080a26a647cb127b2417e439737c55268709568bb5f8eb247e3f73778f2218a1411733d4aa93af9e1a1cbe7075cd0999c6fedcb5a837293dcd4cde1e6b575449108e9499c0b13071013c1d0525727925970d98b30fa0861f406ca081cc9c78cbe22559d0575d59800f59920ee80f0b8afe9e0c83c94609a0fb4d745ada61f42351b502f57f513bc10ab13a9b4fc2b37e09f1cebaaad70a7b9f876a43c8b5b958408aca2379fb438d03f330b0d8cbce2a8a32d7d92bf0c77e8e536ba59d59987575092ef8e2e9de69062503b6c29960942dda327d464bad797ed244f15a692affc2cddc366b369def01d69d77012bf3004e87c85c2f3328caa8e5ae13a58e432c550c1a4cd0d0152e13133ff2ba51355534e3ce561f40083db91e50383a13b6d348aa54d0459f90cb2c8135f2e3a001cf86544e86891e3ab854442639d2c374c766f7c2b0d6087e9e0bfa4bf315b552b24bdb08dc93a4fbb824bd5e64baf22df785b6aa1b65ab53942ec18b91fc6850f278620d99c86e408d8f8b14870eb18fc149b1474099cb54742307de566ea77dea59d3d249c3b3c61146f0f4bd8df5732dddb647a24064d20d2b344a36e6e6a2dcf83b9ebee35055d95d9b00952ccbba72f2754ebf2169e48104809bbbaa0b40e98129e5c90b617185b0367ebb15eb2543704bbe51e1532c6d15699e1ecb86b4f0fd61e77fc4c41db426e0fa74789b358249449a9b749e8199300b5862ccea21353d4b1538e962d2732e28be130a2dd86f42759855e5d8a47116dd260c4f67f5b6dd33721621465f5290dfcbe165eabb75f1dfe17cb84434df262d9c0944750e38c8f972bd21a50d4f636c92f575a07e7f4d908c1ad083312f1ad21d33a8c15c79cb71fd13951938692ec8d9b1c7dfcbf401a256e92fc451882c3b2fe36055cbfa4e664473d1234b9a87ab7201f17936d5544451fb6b1935d014c15fb2b3a19d546e6ce00c7a7b65e7821fe72bd563ea29ab5b938e63a0319106ee90f27582cdf489dc203ade9154113d6e66f95b3548735b09412b77a5442311797d3d71921706923e374923e1aafb6eacb86a89dbdd75476c07a5581893801fc301aec79ed35523bb8ceb3be54b19b21cd5400d4eadc1e772b862bbfdfd600b97076b53b6e0afcab84cdf55a25278487011aa0d71df6a5169d9ecbee182d322659f7f61da58e919fb602d1ff24c62cc53e8ab6dcf01d4dc4d64aaabd987540b433948bbdaf7b1865e5caa3fc76eceea5939ed78d8b057603d443c8ca8dcff082f962ea623218da6ced991e7b85b6b8e519c0129baa723bb32259ce1ad5d090a27ac17e6d530fcb85fa3406c9559058fbb7795c9b8d550c37090e28f31e322051bdcd88d74a20ad4c29446ca5aec2c2b8bcfb1d7f07aaf3e9ec1a656b58a341aefd630c847a011e2ea23b2d6e27bf8162ace64423818eb032688e36f61505f0a61789f606696853840245b606b39204e9b3e60e4ca45b0c538e3d7422993e9125434e09b8680961c6d47b2d4372ef35e67b1c91242275eba69be43fd745bbb0f528453b1ec2dd9f76d53ef90dad732494d1b936b640de37aa6ac1c044007f50391b72db135340374236daf96f528d6cf450429a9ad166e6e8df8416914a1860b5fc6b77c5dae07dda1262959b47fe408ff401afa0827a7e16b4f6f2ae0887a0ceb347824fe27dc767dfdf136e17b32301a98e7e9cc49a5c2f8b68bc47ac922300c897a819c28298513dc1936ce5f4ae394a0a762edf0f07fae0f23b49cdc44054a9728a49cc488cfaed54a5a4b012ddb0630f0400e7293ba4330ec46fd95bd03495f1fec982b8aeb87a09ffbbb50478d9109365c28a4cc507098263a9b96b30e49c15a21ccd730578b9e32e3a9c0c6ca548d7340cefbd4986f669f01ee12bd16d3bacfd3b57aa4f54949959c501de8326e4793cb12c5685de0e8ed4221ed482a68b70286635d45456f3fa195aa4ee1338aeb6b5a4a4a4168502ea35e4434c32822b4170506a783bca6a33beddd3fde8d1b78dee1620a8d133527872648b85e2737c225c1dbf2a82058447abc64bb30a2e9814143cb10d1217072307396306914d061b219fcc52d9f0c7e1f7a409d06c0785fc7c744dfc24c8160b4a6d00dd016b5ec086d9506146e29f3a87e2a91295a7fcffb75ba616ef58507bb20d013d2036ca1bf9e9dbe8234cb57016a40c1df29c5819bc1d64151284eed3069386fa523640db88af8cc1a66f793289c1f1a80ea559c2c3653e0c9542f986a2d3c3afd234e6e3c7273e5b491f7d5d4947d5951e0ec0ff911dda8023d2972ce8bb1d9151168034f4d9e87189491c3c4e5beb598edbb47ec1912fee9944d13aa02636d0409665e57410359763dc466580968d4039318fbf7986c903a07eb5a5c5b4476870b00bc68bfa32f6ee9b90d76ba121cf8b63314495e99c12fa1a43322bafc866216bfe3f131fc2de0f53109a042c70e24af1abffb41e578c665568f3d16cbe54c0cea817d6b5341059039a7ee117d14845ddba9e95f479045914e101efa4150a52a4cdf205120977337e9a26ed8493bb0cc0e75c16d8986313b0238380aad69db15b9e63e0c8b417780486eea2b945cdc0844f662df774afc4d623f3d19ff3e2e239542ce815b91e98b1b500fa18ce98dfa476bb19ac2dde5aff99a10df77015510075848f6dba5ce9044ae7559385ffc30be6afbf64d6b026f4bc3dd497195501a83d86f323dc812989fb27c76b6dcb3782b6914ba43ad5e9c72bce07cb0dcda06160ef64ac69e572a3a1b6931fbab48d4cb7024f9d88e3ef23400c208e1c7c42378f38973c9a20a7dd9ec8eebc989ea43cadb72b068148ffd7f5f7fed57964a0f8701c6142aec15d3e72c0a87ea0cfc3d8fc076b672f4262daf68cebb5751490871b47e35edbfd82ab9c5088a7139dd1a0ca9ac33a9b121406df3410c09355b2aa6b18960d208424312d66c1b2057b353f60447562e3dd40755582b0196b927268bee3a57d5f462d99606ec9b7866d8acdbc222df175af39b3f98d0ebee02d2050cf5430da514c4fc23cd2103e9ce6cddc4a456578138112079f3eb0109857f0daeb1e13ae070a0f3a6234cebcbf63855cbe86479e74ec01e253b27a87915b31df385046553bf6b8f350388a1cfe158bae1a23d48bed8f5ef527dbd36811a0c106e01b566fbc9c45c1d235f3b8b123b696fc63637858b4a18b0282de69c5b691cef2bbbed27e61076b756bff5a3a12003f7c487296881293d9f389ff8435c012b84a3cb4da52b32c7e3eb9b23d3c582474fd36ab20cbe32ae48f045f12ddb9605e2d54d8057718545a4418c22cf3b202147399008abf98c1c08dcb044b0d4a514081805ecf8895e86ed22d9540e61650d7b38531a755d7bfdbb47035a3ae527178430b9d7ed50783e9f2d6a139432c30fbe4b250e711445010f0965871e77c50dc3557d1a543c4c17808e4c8ce62b53e260056f1310900190ccc1376d6c8f692a4407b1f730269f888eccea84dff995cf49084c395f67c257a2a9a7e088ff8d1ceeaa3bed5062f7f27c9ededd5c88bef3da6ddb8cdbe4df04bb370f2911ebba2a18ef1d8f6fc99d73b98bc700bea98865cea2d8c871d5c5703f813962c21de20fd323d1109946beff8c485c333f521b010b7c24a68de5d26e5ca759817585b608d1cf1cd0ffb1359256ada7d6c6ad9e020de1559d564e0075c91c7405bf378a2063a8a21243e0c9ce443654e6b0c1c6d7fde159ed74faaf82233c4e704daa312e8c7534e5e2f0e26a8dd523251eef25da2eecae56cac4114b4bc22fd9433d8c09dd96b43d76d86e2014f826cd1ae89251b15bdb6a1bf743973ee53556969f191c4338d4d5e8a99e29255f7bf9ce859f6f1419d11e8740e627906954513ee2c9110e0c3b20f5c1fe7c8ba9ba7cc4fb01cfa1d1f34b6eb6d1a033c6d4678f6be91cf8b5755fa90a9e5d4450656e97913a16561765e43c575fe879994c652c8c290eacfb807c85b7abe6ddadfc9416ae1d625a0aef2728540864f4e0c94ee5696610b74d78e61dffde7ec105b3401322a88a0dba9bf64bf901e744f12cf547af5c1f58bf24b443b3c7a632e05c4e9ababb0d3bb30b5eed6e6c9816c6ea85d9756d031888757e911c3bb04303053c7739fc7f3f287806abf025b790bb8a5e4b1625083dc324f6214093d3d089e40e7e6f6482d35ce1530e35da4db6d9c510fe2af40758ffb55a24a8b62e9e1362e71be4e013cd70869ada510f88a862d22ac11e67066ab76704dbe14bead1cb8f6a2c4b53daf714bbf9a7b216ca8607923da71a7713b3d7dcf3d674cbfc9ba7b651b67974db0398bfa70f4091bf2c1ab82c9a1e222434d6ee173e1e5ff781174ef50d74d2bccc364c09f2de5baf1064d7c30e050c2a8f067004f269807fe984b7924e6214c49baf8cafc5454344982495a3ed37ac1dbf1b21def8be857ff70ed4e848c85d0723d7c3b39ac31dd8335adbcac3a3b711fd7ce514a178d92cc0d70ed94ee72b319d9e037fcc1795e5a7c273f8524e5c031ad20e77e5f262a9abe5597ffb7ea806fbd651946c6aa20dac3e20f0d832f306e6be4dd91caba66a3aedcc07f162d90b36179c22fae082c5abd57c37e0c9ec13705437877f256e081d2f3adb8b0df2f54befa6b5da02b01dc5507058776b4729916ec4f5a9b52608da6ddd60cd63db2d790abd08fb4850e03b62eff4e1867a124ff4a3439e68ea0066bcbe1313252ff7376b557ce3aff6347d82a4a9ee142d05cc677617bfbf697f5deaca64a4c5812ce492864c29652648f74dc8352f7f4723de0d19c35090f0b490cb1f41af0f7d27eba21a80a38de88f1602db9910d6042cb14301059f8b431ce698a8d9fe156c35581ee8c1d2b019b9ba582eda43c46e1d4e178cf073679aaab6a59a964e18a6599211e6fe9c17cb82c3d2b6ad670bcd17e546ad386d3c95b08633c4d037d49094b5e28b5a144abd0e4b49f1747ac7bf20a2e7ea6b4e6469ba61530f54e4de36467ecc72bf1766d834d6ab0a9107b8a13051b30f7bcbafeab3ffc23c63151d4f481589b1d026cb7d6ea6269dfac11a732aa3d8256a2b71b7afe8e1db35516ad40f5c8432978ecce8f0b2741111a49e10177f485e4587cf6dcaef64de2e50f14a0047e8d5e4b09b2f88486ab089c2921d05c5ffdabbb2da3d2983b762ef2491722ad28037ace7be05cffd2e2aee196af226156fffb41319fd49d29c7786b3d3c8974f642540a279a37d1c738a7542f9ef12ebb9190b726fa1431d5a917f0da84651e1ed1e49296e85902a0d3a48aeeaa722746eef3458a9b6da57850bbd9f0827f2b809970dafbfae0035e317ab2e1f2c27ff6a8f554c2aa972093c0187e21e12c118ce6cd6276e885cb8fb472b1679d81f7ee9c2d05d810037cde9708be12b76618dbde8710837b24ef2a17493b4c6e60fae48662c314450f19c75c3aa8d23cdb8d8039cfc012d3c9db0f7cdf362ce23129178790d8811c11494bae78db60019b7f4037fa4d268fce6bce25b03b3176905d6785f1837f16c73ebb45488261b148e5525f77e994d3f8847166e3cafb93927d32074303f97f384281b7657dc1dbbce6739986ecb88b958c4b0f82dd76fc102db85272b2adc661566acf8cecd5a37be7c86201377da3fb27b59a6ddbfd1b12ecc8f86fe618ca7ddfb5e89e3e6dcd0ab9d05f97b2971f1dea6decdb8e5dc6a09ee9a50dbf98df76dc2f0b0cb83d70857080e0bff039494b05ebbe4dadc52b626f218baa4f93d2d80880c93321d6cc880dac2641bbf860e965aefabc5091caf2b4d3249d106e5a32a6d9dcc3f5b6d5605b5f29e0fe6019249b8be61e73ef1c207abef8f0de5777c2d788829f317aecedb26ec090ace43ca75260c9696058e5115747517324e8d91a20d609b9123839821c8b5b327ab9abc69c5c665c4bd8de930353c63e2a57fa5ce86c58d015f84667622e695045f59d44109e2695f50df010151590072226a725592f5cc4e71be8f2287da5866e27dbfc0ea2ff686d5244e0df62294b7551da83aa32dfb1e844f654d932e84e1ba78305b2e3d62c9bfa3cb06881c419cc67f0a13e09d7b7493347dcbdfd3af84f00054503ef8d2bc29e266292e33ab2450e2a9e5d02655ecac7ff7a91d143f7a362971e3fce4ea8f2392d6164a0d17ca0ae35ff61122933f4599d725024949473bfc179fe7f9aeb10fecb71c2d2a40ecb4ae1cb074e18438018f6f1525726fab5ade73c4026751f0a37afc2c9e3e0d5a54bddb38ba7c9ab5d61e9508446af4306b421442c0a574f8f81bc70718e7aea8efa3e48f1b0b99e1067a0d4bf638118e5ddbb5cba4036f1ee669ce2dd37de7bc1d8ee5ba3b84019adcb7d270f1f116c426d55021c6adc1866e024a2c9424366d5259d6b0a7cbab5333e4fea95803b9318812d9177c62d5d26d19e303f126ce51038605d112a5f136c56a67485b6dc24749e62db0dd1a4e2958906805d74d5a9399b450d6d01932d9e2e2418ab576080eedce812011abbe2cb9f67819efb4bde108ecc8ebf1b3d10e2e4561fd8adcef381b682e754ae4aa7594891d258bb47d048359aa4ca9758f34244e6e4c58a278adff0300319446a7d984fbb21f9121bc247fbefd6063c309d958c0eaa2b5dee1c2a6503bb12afd44ea70740e0c8462110f87dcf4b43b6d487148d05ef241751439cb441b357db7ae2219dcb025c0ea3f13cf50a286c83dad8ea629701d3d59059641d2b3a129a621dcddb9f821a0e84021e68e164a5d6878498b0da4e304e3d8a26a35fd1b541d85e3642c49081a5c7638338f388f14f8c1816c27487eefc278779ead704fd4472958086cd51c210c6768e7b756ef6ca978e9262f942c9fcf0507abeca8dba8913fa103a1862b851deb3ad4980fe48a27a32b7d084b085cc28c7b47b33818c31f215c0082a8131a45bc66154022fca07f9fbec3a2f953ff778f17dbb76570f075b0a37930c507ca568de8f5b775d2884d7db842656fbb0fb2daf57aa2d000ef01c3130deb432b6cba93514b6653f4560948d2bdb9683d221eeff2e3376ef65cd332109eaf03214e76a94bec589f6dc340d328c03cbc6140c6c9393c39c15ea57ae0c299ed761f68d50c8f1f5583477c0b22fe6eddb2690eca016f8c140cd730da5ac78a9f2223b886af8aa74f2ffaa3ee96b2a5a3fa9f8ce3675086043c7de7dafca01129a82f68358148efc49e0b268fce3bdf8d6796e33b2164ce9be3e8506b8777042830082c389c2e9ffabde64b99f1ed327c83d6084487ef75003ffbffa8f384d511630b10c944134a7f27b0039c03b18e354f2fa946c17edae236a6a4b60ddb193a4018f99765d7e83e228e2a639461bf41e0b0b47a7efc5cf0aeae9e5f6ced307b2809fb29576bc643436bf81082cc91562e46ca03586004bd9e61492ef62a8a438d1ed2aaaa984ea9046378e6e16272f7c5082ac5a61f42e11f884694d8bb61ec02250e6aa207950c0e9dba918aeabb959447e732ec672c88f75d47bcbfc189cc611e2e916364ecf79d3f1eaeed7f0df433bc50c3c02a63b72f1b45793f8c8cfdb21d3b9a28454bd97948613b877d3dc9cfd69130f070b2d2376300c4a1303dc5ee24076c0adfbd73e45a4a270f0484f56f7ad39023a4c57aec70a503646b89f5ef191b824963ec1501edcac3edf6c0729dc9dba7a62a3ff3aef21fbab70572b7210d9cf6afe09334329f236204c9830a65b03fb534f38d8aded437b008238ebd7302e805c41371f1266bdaaf95ce284b8e5aa532c4a23b09d8c485fa3d04a66d660995c8459ae231cefa318c0445ada5c1a752d805fc796326537de8007951b7da01f91daa9f50b14938d4ee1bc94e95bc7c98db2299592ecda1f26df375bbe40a0033959d2b1bae4fe99fc1192a0e3067e3b1ec2ac024e50a51bdcff735710eca2714ec3c964a9edb8166e8521d52eb269c2649c520c9dbce9e0f1c7d0e2f2e0b17b5ecce333dc6f6229f065b2f8dff845857f58a1193cb7edeeeb7c090bfd30e232fe66df34fcb61aadf233a346bda9061d128ae2cd06c25eb161382189eb1e92848d2ef16fa62ad8f01adf4cd437e15452228367e63feffd2832bda6cbdb42d078382caa6e469db47e4f2de3250d0ce4d829c8fcef92eac8e49989586ad05ab1ec676ff277032e77c03df654c77684b5ff8d99cac8adf95cf52ed545f70d30f02d58fb9b72db10135e570785e3c444c31a86e6683c13e25de1669337eb60a831724fdfea0c242a7684e881a9445434a8b004e0737a1849857cd5055ebd9e53f2b9f77ac6c6d6ade940c7d0da692e31526be610c47e51239017d62f5858d81330eb9977b3bc8aecab272b4e5368ea8cefd1dbdca495268aa37e9f555dee0fbd6208d2f217f2d8ea47b33f88a5bedbcf0c92ec2d6782ce92e04927cfc2fff29791bad3b44b984ec2b20b6e5e4177c62afcee50b8a12094dffff7e24ee4ea49a7a017f6253df2dffbddebf6a52681a5fcce61dff932691abcad4809168e8bfbd700eab738a0be934ff6a746e65f393d35c4b91c9408127488aaf565076bd3417851fb2986b570222452655e1df8d36961b61d83a859c721d3e2d5494ce359a382c002ce294655a9a110d75c21095665b76d39b52cc7139167906987bf7eb2bae18285ede734693690ba714608ea2c05cf51c3519834741850f9ec9612eaf3a77e01a8cab89a628c64866621ed720698ba89dc67142d5131608bbc32b8dce8f52040f36fb0f24cee64cac63225c59f0e3c3d48b1b4f57c60c5f20205df70e81b3f6011546ef9e9a29041bfc7e96dc9f60bc9c2da6610b504e35e9fe8a2986f7eb4f09f18e3d9491ace8d9b4e3b53bf3ceb2fc5fdf2d225bff6ad4b1a635216395381c4cab4abf95fa872160d83527e2e9b8a9253e72ce50b46e8e236b165ed1327402982f80a9a1b641fec34d718d8227d5df442e2495f51b68ee657741eaa259590193cb15f5c742ca5d537f0b445107d320f63a2e07ed336b1c27f8fef2d7463c0b5e6a2225e3739ea7370540024c609fe4da0333ac173faacb7ea8b0d0c0b7fa1d3003aba571bf41e49546d024f6e1e3d2f25760eb99a37f7ea3d29dba812d2f045a6d395f1fd7680b9db19df70781eb44393479495c21525c8fb1c1d8344cb81c6de68f7bb5cf8add4fa8edd9d5b0ef2973b07ee8bdc97247feb0ad0aecb7f411b369fd8ccc0786e2d2b735117609cc3af441f74c20ef7c86a123763068c62a302c094d1b2775451287216ecd90020817f65c5a8e10299b006799169c625c2017616d95125434c7856221a5e2777d768edd3f8664203b380f15cf21545e13281a798171667d10568c6386676f4d8fb1484f2d7926f45c19e72d55b45aeb547305e9755394435105494028fdb87d00d0b4829c77c613d851381962d4f2ea023e1b12a1e9400fb4a4f318543795720ed91ac450a9eacf24e2c29489f33527df26246bec9a0a7c6a3b1c76d22f6029036b38ca39905f87f337779f199862c631110ffd7ee869f5cf0baeeea9e9b8a074b36434be6c211f48bee747b010444c9b5c2a28ce1edcbe2604ac3d4f30e5926581b67a0ed6f59eeaf40ac63a08338ec77c08ce1bd3b0037f614ebcb225c3043a913ec81444cee3de7ce6bc6de74808394a1a86e8f7f19e38591fd3e2d521923bab5bd648643b8253ede3146199fa4b91a514e28879b329240228fe9cf686163c1807ac9cc2d9bc083ef90f537bd4dbad6d2d1e2db659e88cd97c26620372321f344178d8a9f96b622dca3b7ff0b34f34d627190e8946c19c0e65f4d8abb707b5490bf3a4370aa1bd6d152be87a5d3e25382da8e2e6846682513d1eeb08f4474f3612d13664d0ff9e8d9b471a5df965d25324beef0e6582144da0f05c310bad6b3d6c98169df5fd3c4d49981b2d159aaaa01de771a8f2cbbfb50737867c711f5f0389d8c3779af6262d1dd92dbd5a4b3f2f1516bbd4a9881fb493d858a41c4ed588cab675fdf798543d489edee64de8c7441710732fb0ed9d2f2cff60cae3e0062615ea0f8729c5e6d331c07a8d9bdd6c3e278019eb5d63bc62470e520afbc73b32afa2be723c863ae3f32816f09484e5b21646b52266186d8402abf75bc28ea054fceb94e743982f5cb4ac10cd845299891b2dff0ad2f845b232c84f3fde7adb516bd263ef65bcdcf53d6beef08af0dd3a2ed740520158fc2821502123151ebcea24e779052171652a8bf5fe3822a4550727443a2aa0cf0bdad8de81e43d9681d7750d14934bc7e131cc99bd353859e236167278b4237a38139c1ba767bc77cf5556351e62d5616d1ba71c964fb9b56f930f83775fca58ee1ac09b5d8b2d33404be402ecbfafde582245ce76150269783f0802d6e5760c0b8c93dfc7efd70de95cf5497e95fa482f0ccdf887cd80854468266e86005705eda6540d8e677c151c441dcd7d0d828eb321b34223d3f117e94d29e0da0b1c74cbfacd4d7f53714a3274b154589c857501a860ce75b0651f56c61a4ec3f6a44a7fcd41c185dcb33bbfcd260b95e51d3592de4441a7a7c453904a25c0c2836d43dba07199e20d90beb3ba294b46c8e7d601c64da1a7818986f4b8099edd63d23fe357a61bae8f15bbdbcf5c997d3cb54eb6df8e7e266886c9a35f29e72bfa386f072608fbe0bb8382dc26dd3cbb6d567e294e196152f9bd070abc18049bd19c40f614b7c048389d2ab809d2aea10ae1f458e5e99d203d5d59ac9b014c402845ad77a6d5092ecaeded657473f501bcbe07e16a1c76dd195163ca0d0589a021adc5e795b71792761e3adbdbde2cbb59100011e957bf199b7b38228c25439db36b3a9ee126d6a392a6ebed6619d7ef1c5479ee12448a1cf02185c4430c4568e57deeed2d85cd1f255964991cb8152f2d63680e0a2b9c222f4db49bb3304d8c6fdd7076681223108dfd23553b3151f14e9e82488d9f61a9c2ed5edbf309bd156d9a73afff41990625db984c47d5f2e630029c2e90e4a7479f850b5af32912ef32b12f5e3e97b511467f4900cf6844c6f60cdd17a79a5cf1b96e2e482f102ba5bd44ba62cf5c157d5a8a3ed7258a4caa6ccb2cbf645647c04d97d38aa11732bad3ff547139760bc7b2450f9dc7931ffb24d0a94bd5545581c73014e15c16638fc0215c8f010612a893137bd631d9eef4db227827cec0688d555b8279471eff23b1fd16a495dbf1d0d7998160f4c5068d823eb2a0ddb58fcb09ca8e89400fb5432d5c826f63768de570f6db99f03c0c47dfccfd09509ce1edac0250a7321c9910d7e5845654cb43e9562c31a0667d7d3c560bfe0d28d6a7260ea5962fed1aa7f67d9fd1c032fefdee10d0e0c34bd825fbccab8f5f3e9e11a588f0f5e6472629972fb7d358d0793aeab24736c17fdc1330fd2a45ca85c5708e2d0d172377131d8872ee66f249ed8b03e4b3af11ea922aacfefb7a62a647fbbe0ac233d7301bc39180c2df57b9d8d08bcbb47e971d9dc6eb3a8d38094bd4e60ec96c1677c207fe087d78d0b71e0372c25322ae2a6daf3d935dd06cbc450f405d50ac38f27a177ab52d31a5e4261c18a8155ad4adb82fdf02c0053e7360c17995fd55dd992b24283e340b5288528d2190cdea5acdf8b46fa0df576166cf78f5740985999601171261b8105bfa215d536c60d5cbd7e0c393ed23842004f0861c88f88056798a28942de5d2b23355ad7c0f10b9b16e3e26c5e82ffb2b60746ed798861f1e0e3c", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000f475a2798782a4befbde35b045f9fca800000000000000000000000000000000b5d68e70b88567b1352944061f30726611521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + "proof_hex": "0x00000000000000000000000000000000000000000000000ab6528b828dc60dbb00000000000000000000000000000000000000000000000a1abbbfd7f6dfd38c00000000000000000000000000000000000000000000000830618157af29e0dd000000000000000000000000000000000000000000000000000030b0b3de35ea00000000000000000000000000000000000000000000000188735db8f53dc39800000000000000000000000000000000000000000000000a18fc5b2c715b3bdc00000000000000000000000000000000000000000000000bda61492423a3ec510000000000000000000000000000000000000000000000000000dd0290b3c7a9000000000000000000000000000000000000000000000004983364b7fd2661c10000000000000000000000000000000000000000000000039b4556d8607bdb7f0000000000000000000000000000000000000000000000099a73ba6ee66e29af0000000000000000000000000000000000000000000000000002f88cb3525c2400000000000000000000000000000000000000000000000dee1e0248c53aec7a000000000000000000000000000000000000000000000002d7030903bd0292ad00000000000000000000000000000000000000000000000d3bc4e4978a08e90a00000000000000000000000000000000000000000000000000023bca494aea202d963ffe2e1c6090bd0417565279b28b01889be9222dcfe21d89b8bc37ea633a118425af1d443927e153024e09a6f511d70b18941e6691c58bd0fcb01eb5580d1bf539f221f8b59e7182eebb1d4f8fe20f6d071ae56a9209b65dba1db587938116ca9f42700f6637513c56ba8c9b0515960d790c8b4dbe376efe344d5f935cea06b4b861140217a2a61f3c7e65b5f762ac0a03f3a240dbfa9e18f437a7cb30b5199c8aae11cf8c30a4ef2de6b9622cd84bd98d344b563e0ad901317254e5f708029cd7ccd824db93db38c3ac3983443731fea6679ede17b63365e86e934fa6bc0806ae63c2a4d1a99d5f8be2e7d9a42bfd10502673ae3e598c2c0c3362def4a927aeb4cd7a438200bdeecfe20a3b86d93a666bcc3eff4dc3eb82ff02e3a2b2a4218ee3aa235f850585d5e928808022969398b5b4c2ad44264feea427187abe2a2e359653c433f3965bcbf214db7711f8bd8bd7fe71c7feb983cb463ce891add81689ee23cb1e15053533135d6a5b3354bddec9afb60428d5db053cd7e6f16b7f01a8b5545ffba4cfb3fd23b26cf203722f86cb76878b459259f3a08d9fdd32f22153b36759cc3a604f7812d907a333a091afe1821dbf642c8b8e522071c729c924b0e2aff5d72d70c59180b7f69061f709c6a2508e68a5e86ce5914fc7dc55aa2ec18dbc526b61e9cf05734d48c3955e23058f894a535ac974e102236e60bef51d63943cc3435eeff39b9905d4ba6d221c1392add2485af108330ddfe0c76da1192a7156f072b520802de68204d5b130afebd9f7de607b326d6265b1e64ff2182bd0e3458273fbbd7f7f5f6fc7da4acc89468c6a82fd21a84bcf8f5a169b502e14cab9389991a4d1aae476597ca83bf5e0a3d2654caa22b02c7ede94e1f4392015227284b88fdd39e7406e3efc1896e8668d4576465a3fb0bc0203261adb539e04aa1a23894473f04e76904d56e7a5861eb2f5aaaf44aa8ef22934998ca79d482884f74605be1e455bf594fbb689c1fb78f53d217184a674c725df26407f7e712dc0ab88ca15039410497ce8310927ad651c80cdde05da3e9ca5498ffbe89e01149e820d0a10d51851e40fd7645040132b3c1a4813466249d6037559300da0e5143232ffe0c122c73393a689d26e91faacc5b6194d8843088e11503b50ba986713933bac02d1b0a39e55efe0c4b8bdba00b2104956c6755aaab1e33961c91c792546146690dc9bfe19b77238cb26d4cfe0b9746ff20199ec3579c7d3e54cfa7624c17e311a082443911f00cc9be18fe6126814303a40656a52db542563901a33200a084c1dabaf2302042c0e7387813c36a24d46cfe883888653580f32706af013bf242fa9b03a2f7e40a45f1d23c9048effe8eb8de9f41046e35a8d753851e408fcb71165eb5d6f6c197c7a0fd6991b155db680a06cdd0116615e653c85ba7c1294aa0a07ef05bfceb690467dcd7ee88dcf31ffcb5453db3f71a2ccd2e75df523584ab053db076c6fb57ceb1356781e4eda6a4b1603da76bafd0bbbb6d61f40260ea34d7aeec03ffc1a93c42370f74c0a4ed8043d950136de56617a7759274611b14e0b002ca984eb4ae6dadce0aeccabd358192021a15bd33b76dc01c877b72bc09a425821140f55a66529a1cfab1abfa742c9e78c858182aa2db6b9483e892e467f3a4a9a130cc8d5b4373149254a438a33a1c72975cdb0425271599c4e9f02e35e2aad8080ce76aad49fcdbb528dff3f2404d0920be45890ba1b29cb4fca098bc976b98680a06e873daec3d1179855d59cc2f420e1a16e5b7da81a1b4eb907576f97540f9df83d2c5ea725c1adfb2b1e789cb0889b2dfed9329a9345b70d2da362dabfb7e832f1c4cc319924756eca3240f9faff90eb37224a7f33ce899e2e5130c2cfa148e522504889f859d71a412b10b374d84a6c139084239080067016429a61503f916f0ce4db10b7d0584b6dc3bac6df045686fbdaec850ff10b901466522e6931ae2771820cb1919ab5fec1e9a4fbdf686b1c66df96b7e319f243122cd4186f0f615e3a6b6e7d7f0e4968b1380f3751d7b3bd220f8c27a46208d60a2487ce2b1ca1802aa8364092ded7ff00976bcb3a1620c79ae30146031a010a2c0a2a00c6e4bff75a514d26be107d1b126c95cdb6e677736d9e665678bd12f70f209adde22fc74e1ee6b059bf66f54b3d240c2fc4b6f50ddd82d617ea2d5cba1bde17c4fb34105992103768d2b0378abd71926522ba98afed09931f50b3bd9527f78f8c6fb753b05db1aa4eb4d283344acdbd8fc4e4e473e470dc9807ca6d252048dcdb39999d4575a00011ab76c1b9cbebd74d6e61de94ea700464f14ed7b622d1c86a8dafc5edd967ffa4d14c8783edbe39442888f1878b9358d0a8aa78480f4ca426772b8d77e03813f6746c729583e1b0fb0013b5dcdf2643ec202799b52ac74b84233b3e19972530e0eb9e270c31decb023ca2216a641da5b326dfd8da24198fa386e1e60ed5257039de5f1abe0cceff64cec32c2e001b5ba1cc1899261f97f33e03b7b2b70d3f9a7124c4ce410f818051c3d38b385386b8a2d29cea72273660298664bbb39f126e0a51b6e026dc983640af54b7c6c6c9b19d3849c2f8242fdcbd80c208af1b934549fa4e256cc9c5c87e44d3f34429af107e081d8251296ad5ad4027c55437aefa751b0084f58e19227dfffebb39fe09200304927d8d04e3dbe7bdf246e8a9034765e69e7b4debdb242daf2a654116f3f6ec3b527eb817127f4dee47fdbe34f8efc4114aef0b3c73558735da92b92d0c54acb864b55b1c1b879c3b64e5efef70fb093f10c7b26f286ff8a95cd1fc78cbfd205dda55b12c079a072d723e63051bedcb0e9ec0bf522e4f219d88f7b794beb58b79be8ae303e542dfa8998fe8e2e40f0c2df8960c6fe54f1252372d030134f16fa2594a412a70af625481ac842b428aa11075415bf98856d4fc23375f09b6769f988976570f54169c1ecd9808627f0a9a85e1468abbf435a00fb3d1668484efd3724c7682008a87ea2bdfe7d46eb25ff8378ee96849409a8fb37eb7011ad04fb384c4dd2c1f4ef9f1b37b1371558b8e2f93582cb2646f2c09724656efb97d3337dcab06c90f0e0a99dbd83d4a4040273cf915e6042171711250cf964bd163d23a72972b3f0b9d4b10137e2807a7f31b2f9004fdb8b520e002a13526ac923eee0f81e6c1640d58e1c704ace55a8fe098468cdcac47a514ce7c13d1bff170761bb70805646c1ce220f83362153b1e348491bfb71d71e1d7efc77b6d24c3ca974a94d52c496905ab852fe706d48bbdbf5ae25ce05dee3e8b7b6ae617c3cb19530fa069d0471f21fd12a73708d3bffe920539440d67b4d3f416ea872b8201a1fde3ec5cbaf9b102e000c471ca6b9a956924a7090f27fe186fed06b74101b9320e5c21353be11a02275397a74cdc6c9c22b70a9888537e3672d2af698c54b749648261a50409f2047b34e162adab623e609aa4807edd86ab29f032d7d7def5528e3f459d1c83ff184be5373b224676aef2e0746332fdfbaa300f276d444f7843a2a98705aa467e2de40fa7cf1cf09ac1c95460fc6add42a149d1830c1b3a1ba13fe1ea634d731306cabf66feb50a478f667de4ba425e8a0d3deaf6848a0ab8c7db3f5574340a7d1214de510d4c573dfbf81e2ced85574f085b25585d25656c4c44b6de923a840a167bd567f720f2035b2a456790a5c36363ce092704be976aab4b7d59302d178f0edea2005a2dfde72ce639358d5b71b0d71cef895d6e7416864a2b7f83d1162a0d570982dba35651835593d240d35fdbf3c39b92d70cb1eaa52722b06dc5c92226b4a401b7cd3ac29a59254af9e4d233300734b4fb2c968ec5e2893566f7ce921847b7ba1aab42f4e4f5f51c638c03a13ad23805b9de2cc0a7d92549c97bcf6c10a78f15aaf95bd7419f2b73befe6f639828985bd1c8b28ef032a4a56adbfcfd1df4856798eed823feb886065eb10586cb72ab2737640bfa4b7c5e5fff3b2a652f48aad393feab573c94a109a7801ee0da56281d67b8691a8ae036e9060c4fc22964f9dfd7d8d3f81c1e68e0392e1c8f672d3cc7daf941f5eea32c8c194f85531d2643255822a749fba1e156e0c96de8ffef2b002b753ea47633d0a35078298b1ffc0eeb733fcd2e39f1ef634cbb169a64529f0d8c09d7e4739795946e0bf9451f995d5977e1a6c154229e20a048e7803169e2aed6f306b8e7e503853ff5e70403ebe23597b0a87ab2d5e0882414db412e051e15e0eb2ff95069bf0ac8c145250e84bbe57101fa3560210f08a76f92c25c8313f9330ce8ab268f6a37d3f955450ac7dd3e876fd857582c12d23c67aec92d75a9cc0e8888a9b984c31a9b1a290f2d8a3296f661b42d2bc8d60093f7293e6d5152112ebcc19bfb2b431ce691187f1622524074146516bdf0695d725a9b2b8b2ab8b171321c21f6bc9e966e93afae0f21beb5db56c01e0764cdc9cc1fafb0e77ed45120a2859d7ace771c262f609f23337085ad7ac0bd1518b19d7b02f22d36007e4a63a9655147b2549b74911bb02309ca97c90114bdc21fbc2e8354aedf45453389690da39edac5fdda7d03430411465676719090813825915d4ae9ec91c3afe24ccec744d07bb4de99add1605409b0c014deee8122ecfec860077f4e94dc76c97d6ed198b85e40174939c8b6aa15b8c1050960dd58e39fd5071db6b77591dc9529a230ca6af9fcc9f005b5caca043c24781515871b203e1de9f33fae96804acf9dd66a37851e916e09d0eb92f42232ad796741d77958f87d6be73a92fe19723019c9c9191346e428e0d06611952a980c1b7ddebc02477fd7417b32342780bb56756ef02acb202e3a68725a381f2b92ceedad2961000dce17ecfb2cc250be2c22e51fc82287d365e7ac359789ad2d6ff81fccc535275f3cc21c3e3bf4a04791627dd6d29d721f27082dcaa91f6e07f16049e9bbfa056dfd61d3b7b4b669787d139ea5119d4d9935cab7396c0e5d1214293575a65ee565f4922d3bc625d71f6a09afb3289208b9d3483c05975430031d01ce4ba374f41d28ac18c62bac813ed4c81aa4092905795358f08d116aa20409f44609813fe3641045f1632fbbfbf1277c4a783f5f412fc58b5d902d9fef218370a7b167710d3e2b896fede982fead14198b2bb122176a2cf6a41e596fed0f77a6ec2b5cc237a288ec5558415477ee54a5823a4c25b9a935a086381ae87400e36213fe3c54fe588df40ad8f9910d1289375d78aea52c585e6b0d8b000ca70a2814832c8ba9d7102d18c2c6432d2b437276444d455cb827d2cee930edc7e325b7423f5022749fcafff9a8e0f17ccdae9d2495be960cb92e0522412d116d8c2f1235fc89fe520e96cba827e08dbcc18942ea99d34396d07a510b7e4f2da3dc2448ef4ad4743479abd38c5ad3321ee0024af73947e13ac6e8e05ab58d0c1ce70d39e806ed89cd8f4be46b3890b8e73f6eccb674e53898b7d20c3858642f051c2e1d4894abeee0ac8b42e5098272d0c70d20fcfb4210b2b050c241e49e197cb11f53f6ae5413b466feae19c03d4818c347e400fa24a88187cebcb8e2a03b7f6700d14e54f9fd3815afea6aa78f1eab9ea0e63aba4e6051e188a1e3e11242f1c219d4bcd07f03a22b20ce8dabe5c7e276b88230a1bcb9968fca6fa9ce70779f350e073860dd63b61332e41cf6b742f050a43c9ada81ede0273e2f1d7ac851cdac0e6b76c48a46bb1506326939d912cb207b0452cb0657f810168ea8f5bb5336af12946dc6a82457d21b99ee430376f2943713cc5c0ea1e305d3f339985477c86d1461abaa8a5bb1949440d17fddd237e7aa73ac7b53f8eaddda829d7ffc6860de18f796e1c8e4ba59c7c8268e0c2c1ee35edf60c62230d3abc9bf57818e7f32a40a0f716c567d18e4dc7658bf84af3d5acb0c49f8db7a6c594f2e4b6eca678d460cb93e47b1344d2e4703f14f1d8d2a0555588ca28e19c7262cd4e5cb8f32bd1207a5d911fcba9704a8407c74e8cb344a4bfc107d6ca66320908508105c49253d04f3d0fbdf6801000f6c79ab6e24279275e5966c2eb653daf4f55be52c661aad0600f4c6c80d9d9fc0e43126c9fcae4f8593cd15c655f6096617f7c219bfb81817655fcbf9f285a52b89b1472f10724b7fb50540b377da2b45384b198d1772f229828e95ce47662a8f3e49e0606ee53e2d860c44af493f0d3b0d572070e8e5f20cab47637347d09170a19ae0a99ae74354c855b4611655aed2de1fa258cc243600dd47ad410fca16aac2a116fcce260a895c81a3a89ce81a19afb5868b3a64de0dfa78ffd98eba18398883fe8955fdf45716dd3bc94011f7062506ca98744fba2fea32b10cf3b2394da53d9988e193d4c5d06789938195241605941bc9d5d66c04302095ac006752fc3cbb0d7512fabb3a4b93bf67264df85bad30f28d468745212d98ac8e9fb00dbc19b1a1f92e24cc8573592579ce3af143ed04a9029bafdf1a0f4fa7afce28cbbefae3194c23921d61d988b4c18d29138ea3e95e3786f75600de9e1339cb0792aa919d41b3bc0e35a96053d13228d0d0dc37732da80dcd05027ca25b34cb25393e1b9d30065a5f8cf325982c4406199a0b4ec569928809ea1765a05be230f3b04c5e6feb4244c1c94caaf2123fa942a5d05ec8ebd4b446d52f44119a9447ed0b7dd6cf5e9abd2842fac8c1fd2dc88a968fcd238dba2b100a27c7851b2997c86cfba255e10b7ac0d6c9496291cf63ccde6285d45fd46da38810bf3ad0fc190493c633a698581fce39efcca70a352620f72803f7587656d23b2753b69783f660b76c896d865dab1fc38c104563f253241ac37c8bcbc68d88e309e109d15f9e10c2c39a1fbee52879576826e06802a9c990ddeb73299543b1871b903b1927c1f9e25478e97faaf3bf8cdbf206994210050ec9dac8bf965f3e6e08ed6d0746709cd2aaa86c5e32d1bc480d3c8c53e9a4c0fc785cbee882e360cb2d6016adbe6d57df4352707b43440ce847343bc82190047dca88d2a925a10c882dbd843bee3d2f61f817dc396bb607b3069ce2007f8f38664f16fae5814c4f15037e191d5f2d381491bcfad0ad7714c0f62326f534bd932a85621328d3faafac292949dd9eb0c36f2c1f14868951a8ccf391d6a963e2f46dcb3b34de509254b223ce8c532602bfd600fe080235761b6f24e35f6a869247389e27fafaf261a3302ebb431b8ef908c86b147fe26568f2beec945500d20a16461946220dc41556fc09aa6ae0f757562c86b4d4539f1e7eccce199fd06a9221d358cc0dbbbfd66db5193515ace6da5cf705b604ef665eb3e048a0091d2b6c82bcf4e3f9f3ebfee98c17dd7b4f796c867536767b7cd01e648b24260af29d0fd53203c08b4eacc24c3223bdb95bc20fc8422f635e0e5d8188e393b1e7aabf1fd025d977bbf4498537dd1809c8d4f3736424abefaefbe35918025ee7c3571bee54aa24a8b812e6c4d5730dbc7f04df7009edf8a6c80f2eb13eeea47f76177ec317bf7e97f452a20565992b4be9d856d012b26309d4a9acbaf736c5b917c42bb663468a2cb60c53e6e81225db1c970f1145e4fba55a75981d1ee8a0d93c3f221194b18b5d80cf453dc31a28b52c8720c7778419ef6f03bec2acd6b32c9b1ca1f9e575ba939756158420b10fd8ba42b0a8a67881c5a02d339a71d2a712361d8c6633086fc4e7676e27b82b06d429375baf1f7211242630f00c9e2662b30ddbe78ecba02aa976ad1e9036230408908cd28b1b62b594ccfb9117f9e450ef605467e290f4eba7cc659eccd225262f9ffa5f1313713f793743decc21e2e3cd0960194b545cdd53d36782747922300978a244e319a6c6d89727be7f6119bcca83ddbf8188658495ea0d1e090db51e8821a456f9c1e93c05c94ff5f2943c71cb687bb2a613ae0524cf2ed3478b5e16e825a4df6bc173e72ebd4dee10f62973780746d87d667b46db12dd7becd1aa1c625d08739eec7fe5ac1aa259bad07272df6327f08ef2fa141751f13e538d722192456d40acc4116717531af7ddedb324a90dd8ce98756bbcf3489445ec89dc2a56ff76e9acd6839977f0052dbc9f28feafc3cbc4ee225aae2369aec5adf1881a4bcddf349e614e33616aea81c97e0823d12b5d471624a709b21f17b406aa532981df1c06a00f315c817b93ca03e6b51f0f87738873c4717095e95cc4390b651671ae7f2cc09330f65231d1bcb0396062a2c990390507cf4e4d916bf62281ea04705d0990539c3be4863e38e4548a6b11b462fcffe572710b9839fa7053790d0fdcbc54ac8897b836768706bc00cec2396a68dd4b6536194d5bcb608215cde515db3ba42c4907b9e7516c51c00a2d6d98b91c40e4540249dc687254bf6f0e261a75ef9066f9ea43f9854010451680fe61ff14cdd2d837e478f3f25097f4cbcd1905831494cf90b01c3565af7e3d295861b05bfd2c4cf50a9615cb2228cfd93a05c5ed43c593260b00d2747a6b63a9e2ba2c61130070f54972aefc1bf1fca6a214a664368ea883c4153b2080c0da033b3e2cf09cf70b79b2ca3604d2768dbc6c25c54d918a2e32ae2b15a733b4f0ecc57ab6b53131ec55effbc7beade097ea9e17748761b4f968e77ba7319feab82d5b5ef23646e40de26a20665c3f9d85370a14bffc2db9ef2fe52f4835e51a7f869f540079e51bbd0b65898cce6ed666cfa12d11799b1b8a26b300be1b1149f009afbbd3690e9cdbd958462b08b25527e5c91e2ced05551874cec301f57b0460c7682ad99d95f975188981188383caad550e17de8f35ec3412101e2368d797eec0fecd967428430dfda277f9beaa5d363ff5122a46168b8de0b4f951d9b065f1cbba637dd52d2192b594355084fa3a9b9a090fe79e7dc890b555ca849b7c88ac83af6d1f73b87e69c141e8d86195424c441a1a5c0158e48abb771bcede8936a1b9113f55898928ca73dc75e5b6c4de406f4114e8708379c7e5d1433d4845b35737dc65e84327b340c01dbf65cacc706ca58117da8029464130c99b5892187898e58abaabb6944eeccccf67dcaf9a8adc6ba52f351fc70df1a9023ffe1b3ac2357f5661dc9e3e2da37226434eb80f8c7cdf0113bda125efbc22740e9593a0b09928efb71520453485365391c7790d931f68712f1e3914c9bf5018a3df1979f3cc1d9bc8ce6c59ac9f0b4d3eeab6392e4bbccf01e75106c9caaf1123582416ccdd87ad50ba24944c8af7d2e663ac666e2f45951d48e3d982908aa887a32d5f2812c2bc7d0819d57f474758bee18c53c5b275fb282c23fc095cf698950df8016f2948992fbf6142aa4e0e30a5e736c515b1f2c02b195790411f09de4a0cc204bdf0245a9a574e4f79a91ecc1bbb69a48b311da91eefe7c39b57535035eed99d825e0d1ab768608aa47f30306158ef34edd4bbbd0a0a9d6cb21f1ca1da8a519fd734a1c7184bce140551f50816a085dcf4bdb4c92ed9c14821e951be0b6ba610ace2bd159bc83184e9e675bea003a97278fc57182a65fa1cc3f8f5fe06c3955051fbe3c1b4c02ddedd0e8db7a17e9ec190dde15709681ecf7b55113616b516a24fdd90a53286c1514d865e7767ea9ba09547fa471ca8c329de319b9454634395fede811145c72226d89cdfc98851bf5925f6e1682b03cb157a9dfe93962463c49e37e794fb4f7bcc8ef15f4b95a08ee504b424bc2b9b0ceb50d95c08549b29ac4433d6b5c0e112e25496bb7526998764d265ed5723569ffdd6ccff3f7b662fa9b12c449562c4f646654842a558c8b1c9f244ecd0083f3d836874363dff34932fa01abab49421c8e40cbdfa9ff52d5cd9887a9395148a55b111d46832f98328dedc0cae8db24360ef8e97fc6d4e5685e8e40ac31a018afd561b36f06b595090ee4caa5595baa0d1525fd0a9a465799925d7749e60163c569702c9ce5c359a1d07b70d603d86cd18217349a14acd53f0bc030edf5511ff984a8cb7855e63d9c51f7ac955363e610eb9758994f4b10b66064c5a456314644d1ddfac620436c9d8700d2c0fe22e6304a1675686a2875b236afed295fd05bb3ac47584e57fee2044db8b3f62dd73c55a56bbef55e04abc70f00cd6141222e712bccd22dfb053db450c6f9b67a0f0e9158ff01f4a955005f899bb7eb8d921c2cf4e03814886d1c239f5a451d9e6a1026540307703dd10a05a24714d7ccd0a4a0c13790c564d83c3d3a2f06a0bc05a1c61445d8a1a40aebac9fce29c3a241ea1d19f175757fa05361f97de1490537c9e0c0fe9bea0ae2b4d014760d083bf1ac8dc300c7b476fe73e89782c02646bb380554fc729de6d7e84b4a0f373b6972129ba59b38b642558fe44e48ec2b07c3def9b782483d8a45dc5ee71ce5f2ce907a836cea49c19e51cfeb0f1b546a60ae3efc45458ade65d02447c74c6ca74061c5324a909fd2b00503aec9ec3ddd84146744b9d29dd9ecd507cdfc617dc91fd1a28648209d385173df6d92f02072f3577ab4e023049b5a0f79506ac6ac893f322888d051f9643dda5e82fdb3a9dea6c69b37008312ff610a0bfdea80026e645207340443fed37069a0fe383b75ed6494fd3d630334568b5209a570fdad7193e1076088607464721c381c121e50a8277fde1fa45252b8eaa0093c45e8714ada412fa188436a26f46c5ab225791057fc6ec5b9223467da3fe1663b6b1a451018306dd342d2124c81e5cd1c74c727bc60407909c9e65297271669c7a921a2cb40321eee41107d80636df7868c993350247387951661232392a971ab5ddef7a26310c617af0981a260c9665c71129c442cf034f627f494a2ca59885a5ce8f1c854b1da0afb25ae9248b1df9a14ff11608dc4a6f8ddcd2d7b39dacc3a35c9f89b08d16ebb6ff7a15520ebbecec07fd074db247d9df3b0af7586012ed8346e8901d0c258cd55cb05ce90f89f0b55cd8593da3d72a350af0e1a9c5d7ac18818e8c2ed516815a67775370220d07270f32a2bab24e5597711b7ece5ade6d223e8c9bd433195c85cfb22324dfc23d98db74abe107938e348182357e25d635b13397c10a5d1a5646bd39f10e13977bd19648d86c980b224741e3950f923c189fa7de5f071c2b675e233a8d975e1ae439254275baf0177cb2fc7d2b56854946a10039a91dc217fa761bb0d32fbdd6454952081adafd2c34b8c868aec6d98f0ce42b92a473d41de0b8e19fe52f03cf4d73e6c2ae367f476aa9db8d11139e4b3bb44588a4c7e72db5ef6579f8014f40bd2d00556a2c42d14c767dee9ae851a918a2ffcc449f4a209afa2d34b900dfc8b5101e846c67d3df2a9094abbdc3ef9f1a9f5da79a06012e37756e68a16cd6da82d916c17a6d00049d7bc40ec8583047a2ff323fc833771588497c7fd1458b07c8b0abc1b68ed2ebd9639d52df43c565be65be3377720525b270e3b0295caf51e1985bcbcc88d0505cb0b7b55b050f2ae1e8db0162582716f9db7a4484d9c768536b3974165a121c546071189caf4eb69b67cfe0cd3b3b1d59107c902e2eb2cdda7b1c7694c673ca206a79f86c1ffca04ba6f33b91ddd00ee174600ee6994a3d302ddbbd15c7828ffe05cf7ccfd91a873b56a46b2501152f803d7e02d9396338acc57809d06ef1d137d82c2557ff5d24764340eff9fd2307f62425a6967429b64d92f334ac221b833ba98697428f2c3e85d309a279bd9e02816cb49946a30fd532a1a0fb89d39ab14a9a894795358324bdeceb41a55dd723daba1de94e2a573870b7b6a8f934adff43734d22b5760b8958863f9b67f1472e94e714fee447cabc676bf034fea6f76208174c2535b4afe9d80204dcaac64909baed85190b56be2dabbe24268ffe8736522454b17ccf33167390d6514980f103d6d6b2a28408e7a1e460f70e572835abcb3e5f421087307f79822ab1db970423c688bc5c514aa0c845d7a880875ac269a7d1a07c190dec94d0adaeebd9b98a1f0388b58ef832e3243a0bf2d16550c3c71c996e75bea5f8213c66b63c2099b40a5e6be7c31ea4943dd66be1b0291b07e00b0863468128b1162f795c5fcce187039276ba1e7a672e4652f683ced72f5ddf271f3b94718bb96e0fe9ab74bb27882521d9cffd656320df03d1f2de945c6b4d1aa98af9c601d1586646fd843228b40ebaabafc00b73f2396c55b101a518a1e9afc8a8ea68a9af963e79ce9941e5d20ee983990f78ffaf203dac89c95e8abae36cdda5de6355f92eff43775335bca410a40d8e2a65586fb089ed470b635cfce7a12ff2f8b3d30799f707f4c755f4762ae8449dc1c2ea29bb8b730ae3ba7bc1f0a4b921d02ddbafe7193af11e58b5dc0ce5b0aba68ef34813d98e93e5e583031b09c03ba7af58d13ad32dc8d01c2745273b080c6255e7ae2d988312fc53fdd72e45a684a444beffdab1ddd37fe6a44d2d86ed6f10435b658fc474e5edc21bb4431d910505eb635e93c7674263c2ec6b1fb34e149f747638a1dcdc050f11dad7b7e8d065aca7506c507402e3377068d604e34eaaf674bfd9fe78ca3abcec0ddcf8f6bdddd63edfb5ac26d3dc71bb55a302eb54b023b184308479aa31779ab38bfe4c079bf64c261927a9ab29024684ef282b9ee273058e423e040347318becef2eb3be691dc31d88a5d4ca81db21351314a73eda94f6c93a9efecf96e5b3b6ff4dc188a0903be1b982d18c64d0cc00a10584ca47a49fbf12c4b47cb84113c978554b15ce45bdaee4248bd39c6604064813a5506d7cc1616735f0cbab341ccb85183f35d85299f22376ec3e462f27ff2922207204f8a3b0d84905b621d1b23396dce3f221802cab32d6f9eab465a923f114c89aeb7e0920c1c6a2d977d1d47925e60fc5d9dda0ae95d65a55b85f79117d0f5157194cf4cf4153465a2159496b100e590e95c49d32f51e269d6deea9211110cf1f23d59f979e136fe641878c5a12ca107ba89cc33a14349bd240b386cd4d0b678476c9b29bd37c1dba6c6f3c0d6c75ebe15d2a0824d717afd4300027b4ac2577ca36f078da8ae3e5a84fc2ef24db140aa937d8ebd806d73ae1fd23085428223722496760f2e56e2fce9c872f3f4d0f01b94a2370b264bddad5635afd70181bbed96e63dd35cffb340dfdeaa33bca7d2d315a7ee4448ed7c4b503f1282d5c038a7eaf34a2615bfc690fe5115879b0e58580c68ad170e7d8bbd686c6796438125cb3371bedb5c2a25ed7f6267303b70f7fc9f61256b40affcd13dcbae955f7207f8f01625ea1f57d1a826ceb00266f81b2e20b858cb83b8d4f60fb8c08154f14f074ecb9e5c37dfdcebebe1512dfb0fb8bdb524eda27dd5c852763ae846fea26575543210da57aeabe876f760921c932a68fe762bbd03d1af766894a1ed86a28da92abde2f9d21ad9471d4a4284ee9fdf80990583ef128320745707bb783d319112c0e40b98c05d59864c555f81ffc838808a57127ec66acbcfd7a2c731f2e0dd9690a47e11ddc33904030cd07b0e3352939885b6262a867de8f9821f0e5f721e58457e22c50272bc506b71b21d3ac2e2f37d8c54b265c26d01ba437a1bc2a07f8401e73dba5feed49c616d8741d05ae99d5a1cd60942089b64ac10a16c108245855b5ae308b2959dce5637cb82ef4c9e79adb804721297abab577dc19b1760206337651b3eb4384cd69f9f3bf18b2d4fa030b030cc6e3080255efb74345542ad4b20297c048fa3a2f70bd883e133a4e872e61519141479b9ffdf24080460519db4fb299366e2208d381dd1259499368682198369a5bde6d6652b8d71a02b310af0409c98816450a84aabaf3ff77b2ef99fc5249a0dff332457c31b66a3fa60370064d536106a22d8204d019b89b1d8b6fef01c337a0f21a62dafef82705da0c6d144be8c7742be9b07f1c3f62b8ec0ace292f09462b8a04dd3b5d070f83752cdfa5a3421ec582635806dbe9f7b4823be70252b199459b582bd30354b7f7d21e2c028f596814988d8192e31d97c6bd00085094c19b1b6102ad40f5e3dc162b139e03a3594640c003fe31235eb52638208faee9019ec21d2eaf1e0806fe0a9b0844255999c8ecbc879487961f7675eb445c026de579a2ab8c64e11af9bdb02b0d649300414155f6bbe2157846e682765ac896378142bf7cc897ad47c6bf922f1cef8aec47001f925029f48ca18e0a67d9564272ebd5c7333265e30d0a59cb940ab65a79efa6fa683f181ac7a041e0281751ed9dbf057fa2e7096b321d9e7f04239c1619ba20a42fcf86ff7bfcb6d5a95d2f2c9fe292eeb82ca7b6aa5b066c902b2f3a182a03e3cd0911d8da65c9ab51f3e3ffd0e3e5420fd33fbd355d82cafa1018f921624106f8f7d730fb60b5c2c376c8aac7cd3be4abdc9bb9abc81e56212b809c432f5e8853cc06d9070e2a711a385d1db13c42aa124db365427d0ae26a2c23bdfdb957e7b43fc759f17cd9b1702b4f6bce659fc70dde9c63978aac49902ab8c18dc483d6da31ace9c12636f2d173c202c50e4d488ee4b8b8af90f8e9a826895f5e63dd0d85cc708cf80940667491fb9dbb106423c5ae2ece55c871d2e42b37c2f0da7018fc93dc69714cdb24c3ea6677f55332868f2fcdc7408d51d949164b1bcf1f55f71aefc1dfffc6420a064081823a25adb3f7b3e6edbf384e21f00931613f859f0e6860156f6d19c40bdfcc8e05ce960555a9491244c19f36c94e02f5f84b0e5d301e5a765aa8376aa85ccfb6da13e611b40335d88fcd2d567aa1", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e940000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000041390e9098d147b0784030810969efcf00000000000000000000000000000000c017075b91d1c9c5aadc2f4d3b29027511521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" }, "decryption_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000005313d9a6e542b9b7e0000000000000000000000000000000000000000000000013903483c243ff982000000000000000000000000000000000000000000000000f652d9273b7756cb00000000000000000000000000000000000000000000000000027f630846016e0000000000000000000000000000000000000000000000065d1e5b1c24ed4fe80000000000000000000000000000000000000000000000043ee31d5f86ab9338000000000000000000000000000000000000000000000007905d0c87aec5be530000000000000000000000000000000000000000000000000002a10ac0b741cc000000000000000000000000000000000000000000000000c7b51a204d90654400000000000000000000000000000000000000000000000e008b3a4546cb0029000000000000000000000000000000000000000000000000cdbddfffc47364a700000000000000000000000000000000000000000000000000017e0e4d1ea8f200000000000000000000000000000000000000000000000f22ab1a2f5805860e00000000000000000000000000000000000000000000000590f6e2873666cf4200000000000000000000000000000000000000000000000eac6afb5d8d6b5f680000000000000000000000000000000000000000000000000002cec631c36c46032851a9dfc62b5a3386657554763bc9a25609f33ef55cb756f31505ce36e3db0a05b1409e30e6882fde08711fddd33f3a44f63da1deadbf7927d51d3365bc6f093d95d7862c4a0af8261b8bb460c50a13d0f1912f0f74eba2e0ba2aacb5acfd093e3ec1ad390f2a5ffc1edca1cba82de741b80f242560d646492a9dcd4010a402a77a5ab41d7eafe3d77f45358dc9f9606ad20a1eacc2a61cd1dd938d7193aa13930f3869b94dac758e78c545419a33fbb9a40a82673408ee7740e3a520e4cc01d958176942b6e13bc5a3f5f43bc9d14d60de8ecb191de20d42101a085305361c09bef2e2a18b396466c452ac37cd484de8bb99477f3c4498fd618ab8650a1511810f77ae32802a556df4e238f85dcb488a8949c92f1f17c9da333a39b46f7e0490d4a2682047aed8aaa555c1442aff40e17d150662232cb1f5497e9d9f75631b0570752d219754db0e7b38a8b0d900dd5eca242d745248ed72a8a78eeab691073f1f16d0f47fcfd81fa73b5b025f56a13d05da716c38c47450adc7bc6bc3e60956e58c6e5ac4e21c191f15788a58c3b9d3a4b2504028b9677abf113bb9713c0f5e556ebc4df8cc405f439d15f2e52f81ba346d5179ed44b3d5f96197f77fa021ea6893e7d50abcf176a3fe4566c19a86f25d3ae0ec886334991b36112a8b011fa0c797ebbc32a6f723304b35ef468f9aae7150ea4b8e9e77ddb1088c30e95722ccf074798477ac0073d3489aa144112f2a486b964a5ce4990487446b9980f4261268be768521cdb8db5c9d1266187af6b07048cfa2716a074a184b7fdcecdd15eaf1989dc2a067241e737e37bf4bbfe3b197f356aa5d7ec836563a147a5f031836aa1407efd201a899b782e88d1ee01439927c4d975bc0dad5905e5a943dc32631ddfd067e1391444c1aac4b17f7b0c412202687a5791c633dcd9ce1a5959f1b22e74e7e0ee123b765c3f8f4dcc838ca0668c2b760387bd583b0ee27bc72ab2e2fc3e85e45577cf5efe5fdf3dd75f23773bc720bfff9a22b4bf328c69e804509d2a5c5658290efed9e2132189f24d644c64ac69b5b6de0f8e2200918df08aa1c47feb63f57e3c01a577f13d1cf5074100a7333794ef2c30690f329837b80460e018af6c534ef2a32a489affdfc92ef782701dad0d8e4ee12094fca23e0613713d5a51ea31f6607fda990251b11427ef851117f277108eb658d2e439a47c19f204083a535a2a10bd01423a13b1dfc9e3d3ac5411ffcc863bb6ff8dc4abe59891a9a20872821232ae50dcc46d0d0669de09f69844b506bbfb8b706ed1eecf0fb10a3f79784ec36f7f79ef7240f448a97acf8ffb80ce4af683c5a5dd95607d454015f64b5a5940339083d5e4ae358b2d24d0af748b51c65cfd06e95043e74d669214c2f698b2fef5147420e7e3c0d21dc3fe5f3f96ffff1c022962c5b51eecc9a0420cb4fcf1da089596e06175e9708dc70e66ec6b61342054186e3d681b6ee2a1c3b72a5ef49f5f5631ff307ec9a01f5113096219edfa78ea5e5c3c1d0c7398519654c0f0e33d4b3ccc5a30138a672087584a448997817fa6b682125c197c7a00f69b8f94f6a1766f19d2f618bdaa3a6d393e211fbf085115184a6b29290ec4529624bd29e01730ad78c71881d157eb407d191310d3eab86732b0d7bee43e9801d020395d413c7eb94df2f0a228538de50b701674cec415a6f17e45ead136dc310df9abf4c32e790dd45dd999fb10ccbfc083c17ca40b7d15d404b78b1c9cfab1624ab6b69aa63f47bc816de70b4b841e1db7c1334d56171143ce06ee3f8ac6806831e6f8dc9687d7e02eea8bf02d5879fab57db7e7db839b60212fa840e92f52fb8788ec87b94f6f42059e8ebab894618e3dd8c7898642e1ae539bb97ff3dc810139b3596daf0513ab41747423f4dcbaf8bb067e52183192c2f9707a42955e41f675dd80d236e500ea507e0cf020e8dd63871560b83017e49b39ec085c8fe470b16b25135ad3b01518546ee240eb86d777015a51e54a3bef499cab6282c48f72e0173e779011b3d66a2efc61a882677f8271f700594d418eb06d3c56b2223ba289afc3822406eeb62a4b4dc070d3071582c1a803dcd834f5e0f3529d9d79a422d0d6edd2cd11d806f194b4e71ce3066e02a142a3a85edf93ddb1aca6ffd27a718ccb4bc77a275f4538e77c479e448c1624c14d19ff707a448f659ce39acea572f3cab520fe49079887131808623c163e90906e7ed8821e62f3c0a53679c8a37199fc5028c4c0c4fa687ab1a7714b2c5a2211a9a7417a0ec1affc4c951ff38c309cc8b3d407ec398f587e05963c19315a9839b8cf4e1f4c15c4242dd4636e574041306832a9f5c8312eb039a5bf47667edcab0a5bdf459fb3c662bac7f16c71607a5f433bc684caab8b460e92b48410e62fb2f37ceb48642c45e6b515b7a17d01608601d90544628cadc00a4ca65a85b92b82bdd05fef7edfbb605c2fcb8c02f0ccc1669e3dad8b3a9d9473961c1b3441b7b4976e33f94c3f0ad47c1479937bb1be501ba3d7ceee0db11ec5333a589692f7effdfcd7611294e455f22c5ad0f9d13b2f2141100b017ef1474b21edaafea1fc1fb4e88fcdc7c28f6d82069e8d7c41e0e977dd5fd266ab3537b6cb92f621605dfad56f51bbb60cae4550e1a888f9c1493ab4be927b692643d83c0e7550fa85aebdfac97bfc90f965a14e8cc9528062ff0a443c9f5fd1746f19bdd5b6b64457f61b06534689bf551f16a85a14436a6010a702c41bb1e0a6c2be5a38b75afc82006930287135c11e26f547bf7c58f131190097012682ba2a52106334d8968736bb338941b866c16a8753f7a533e215e22fbe7147d0612282fbfba91903fe00b4bcbb22730c795f325162d6bda74757e2cb44471b703e1883cbdd8697418b7eb070d497bee39b0dbd302796f71b560ff19cccf4c7374f196851f26786251c874dd3c9b37c8980c518e1220d59112132c30076fe67cc1467250d011b3fe659ff4b936b5c776b21d5e8d59018c31bca8c62feb6f10c683649cbdf979dd0d114604b627e830b84573f61b5b540d65c17d802b6f539a24fc894fad4752908d7bba4d35e357c0ead16a73328557b8675a2a3214cf4a332021c9a3eef6619a5107c13fdac4a2abdaf7d73d90b3694c3ef8d28528fdf338401b8379d044031ca7c841130f8885b60e557cb3ab99d3d7d1e868601dea1979b30754602912ff0f2ca0fd1ebcc4ca3889011ed814fc9fc6adde78fe1c11dc2d135fc5d12c7db4930eb2344f23181392fae51025c8f852b57d0dcbf9162c748ed56ff9ed17e51fba6ae658627ac81434801bf4aaf9b9c30d2cb4745408c68c9cee327fa2c3a914a76ff530d35b5455a2dd54428924defdb4db6a2f6b1e3e7ce480ad10b16fcde57c3655e4d793cd1f668cdfa752389cfa140aa0085e1d147e9a1bce8a9463e5c07c5f45e96e2b9d9b07c5bdff11ac093342dc1ebc2204edfc258fb3bb960b667a51031c776b1778608b80487798ef2d84e0db0f6e4e141c6fb0a8bc3b7135ba6d0d6d8836fb252d9c2f134e3e463bfbe9c63df0cad905422868774c9da5b37cc9aa725bf0519e8bd4ab8512d84b8b1819de157218e4108538080b0073b4976aa65c7d1591099a55943a8f695cae7fbf48ffad0128f902b18286fc8c2a8a3d4f3ff4ab57885c422adac648d5caa7a206a7947b1405d6023501dc48bea28fc7d71a40ed12eac227fe6b1d2911dcfdadc7e0ceaeacdcf1115c04bd06ae76d4be2cb6fba63ba55fce10015d98d91c08ed62a8b2baafa6e80bdcd637ffb9b0a591ed4fae1fef90350abcf183a52797af4c9f68990c1baa02175980c426b36aec67c01b40c434c907f828e162b48d54a90be5591dadd12676028c2aa67d120845380f6e34a3dc3089a809bdb9952681b720cd8f4c9b433e5c0ed724dfb2b1d51ac931e23b9b175bc2d86c931ed0404efc902a524b2a9e8de22cd36a8f2d393067359dc3668c940cfa463fa26d2d1282a5e478fc785a387fd20e3c90b4a0ea0524b8c98766e4a91752cb0ac4fc26d4ddc0103f927e5dac3a4a124a134b5046aa36a21046068e677e4647f1f606cba1275c6a33dbdb58c8f78500ea6f64b70219437f56e73ebcf8b9869534cfad042cbced12e29af5a184b5b310d7619382aed0adf87a9a77c3f6d0b684bb89e527d48fd0b30f6181c167e32b1233fc74536b1abe9e2ec4b8211bed9770dda095df93c96bf9f69c101611740e1bc969c001bc9ea5485d64b29894c16f9210eb37190dedaea922209cb15302bc26ffd078aff907be66328d6033ce24be8b932db43b2dcebee85ed4d35e32f2d900c2b086f3c6afcb9d6157c937be026532fa062bb9d8a324ad1c767bb48e93df27ae8a3c5713328a26df36868c72ec6b7343fae644779e861c99612ae1cacee60601bc807dd21674f54282e27f923c5dd3f9f032c60128ed051208210973e3ac059a6cc964e2e21bc07a7c8dcd5648c99fa16134625f12afc450febd3157bff300ff3cfe1390b922e2224bd6c5a072e7cf1baeb3ea670dfb4b7ed87bd935fe2d0ab01c5c02b437a8c1a325821fb1397d28184d085c217218ce9772a15504ebe62845bbd18102f444b7f42056656ac9414a9f0beab0bf7576a832699189b3f52a129b7b1093c80ed82ae46ed1654970154fdbf10afd1b20395ee2982a25f65a8a0355c507e9bf351a1d9ab88140cfb63ef0e31bc4ff85100d2d64b8fea8153f8f03ce756874b7dee3e33db736d3014a52eeb4ba84f035ccabc22fdd930d015fc101b9a24e5e9db4b182b82096d17f7b39c4419a2e30003732ee53a2a961c4d1ee1e7df59284a41368ad005b8be0f5720f914bbc82f5e814a2e6d79e730e96653020b653d5eb54c93bc2dec97c0a29b62f3483b8c54112c31b9a17e6f9bed56f361d1296b42bea1371c13fe380d4bf7b7cebf3de5994f900f778e8c3a57e8aa6fb2ff731444d8b174dfea3f6fd8c8ca8105eca56b8d23027b15f04baab984ef49e2f13acad5025d8d1b15e2e64fc0a424bc589b2b5e1db37855bff6eb56a73db33031708cdbaf9c669cd7b650db18d2975f2fb55aa50b455f946f573d64b068d68228275d24daf2fc2634b9963f4888a89222199b1617d337ceaca57592d7253461c5bcaedcc413b455aa91785bc842c533d705632454d571583e7526a8cddca2316c55131317730a98ebd450ccc06d7280fe9448bbf865c73d79b20a40d0fa6d312f28950279d79ae13503f1c87c4eb4081300aa2ec70b6aebd36f3a911cf96b32dfd80e60b3e1e434347bbba00b797102eb56116bba3d7beac36a6d75b3670eb271c53c9c0806db8d51122a55f7bd660546a9bdce2babe09e62387bbf362020d1fbca41ba60c7a6ea9e31f5888293eba6610c9d03d86eb3322b37e5a9c1a17722acb06be54c8ddcd5d4b5ee8c950a19aac715fc02454f8508cb9ab4629f5647e0e9278290b3a0331ecf4a6a4dc28ff9af971e3477c1f141e587e6ae88e14e088235aa1590c55581df682e15f236d54bd804b50d91dcb0c4979d29a32642044d0080bb9e42d2e20180083e4d80c200358074dc749e03c06fdb3f16e8afb29843f1ac767a8436915faa3cfb72a4e215c1507254cbc8d3cd1e85faf93eacc407af20e4aa4dba393c436361a04397a3e92dbd609442471acc7ba6ec5ca3d8c70985125c5256898ac7a525f33019072748ef8ec24a4f75c9d4e8c3c20f70e863071d72ebc98bdae976998c290b16ea2b86187019f6c367bcaeedcfac53b54ce49fa96082e6e6f27fa07097659f90b79e38bdeeb24ec718cbfc5567937e15867902ce81576b44b9766484315c6ca56b6a5e1776f791c37473719e0fffd72149d9c2b3819cac4c62627a3cef8c719b0115b37a5d0ce55e0a0cbd1df5cd05d9af4afa26c119a52bdb434284430f43cdee3c95ffa79aa15caa494c7c72c730d72084c17221eba17e30227c699f53c0e64c439cfc61f397450ce5c1adb973f1f96a59a677427647beafd478e15081391afae06d5f95634062600695209751465d716d4ba052c2aa975f1dcbfb41eba65d44a7e07ce96c7934d9c461cf343b9958f40be55621badc077e730d30e40f59297f148455bde7c36300eb70a2ed7283e525d6a50812e5dabea9ac4c981731fbc314fa9c54c8a133131d2b4ab4cfa96c5efc31aa5ab0fa2e1d87bf6eea08d17099bcf1831e49c99902957cd14811a58f1281a5edb560308ddda5de1767163db2ac5c77e02a910a34d3159542f63c0d7fe1bb0005bff2b55c7797fa2d51e5e0a42a3dbad55be2690827c9bf126ab3015fc01b953a85b086967b1bb9d73af5a8abbe120c01bf8106f57ef1ff75d175c4f265ba7db8ec30e603bb046abf3d48b1436f3854db422c9cb232b92427bf121304d7e4bd9cf9618cace3a2785ee002320b8bb0c2d0d3a1d7b44e9636e9638207e3068411aa5a4226eda54e1e2e2cd72de7b3af6abd4a874c04f834f8a6b7563a1457b74eed13f089978d3bfc7e75282edbe84a65cc4d3ba4b2a40068f45da4fde51c250a61a842e3ecd39064275b66223645a0721701baf9b71d101dbebc2f605881cc9a327040f5557d835211325c034a4b41ea58e10902dab630ef06fcb777c601a28e7a1e91f782f2b7f6774104aaa7e77e720c605504d2a623cce19419e01b2749c2fa88722c5fc5faaa7897e91b598a9979577506dcf54e1aedc9ea33fdcc85d76d07c950f467c13320baa0a4c8ab1b2d0523d45ec9167617ae703ce6148dda0602977420a66fda256a321a75cb4a85ae296bf5899f00ef3146e1ca9e2d47d780faa5d822d6c4e1f29cf4380a0f80594032967cb90f300607c63a4e8e78b3075f61d4eb20a6055d11426fc4d8c01fb31b9fc47e6acbf25b4353eef352f642ab1a7f4ed8e0d24e0006d6019dd6d6e35d53c2c08d73bcfe6a138549d6a35fabb911a247d860d18c75fb954e1ff05d39603748b2b2eb67a70440a2043380ee66d028686267c0a982fd7b8e1080e23b0049c40f49e6b4a2574a3260c7fe9b03d3a486e27905b06a2bdfb9db75c5c005df6cdc95a353c88d99e9993e0fd7bd3652ebaca1da2e121abe5d833ad18457216aa39d94d26dd0ee370e8f9d74ebfdfde6b077f317bf607e748f8cd930a9e23acc1dd4dfd353e3cf1ad59efb65b969933a6f2214d347f003fd18c7b54d22f392a9ab1e228600843f5beb630736308f0922473bd712a2f021bd0980f9c15841804d33f7e1611fd3e50bdbd0535632222610af3a10c93c8040cae78f0faa973ffcc633f2420ac5af157288c59c9cae2815627017ae2cb290e625a6fecdf1747aa56ffb74a28b618654cbfa9b34fd5da3dc75a20ccdb20ca2d2c490dd0e19a68e7d7246fee9aeb239f0e983762acb98b89a0a206d5c8db3e26e2f1eeec9290be1ebf3078709f2f1c6573c07eee4f67eaf8aed4fab0c9bc1f252a496548157fbd3073fac78985de108ecea7c014b70101aa0a820ef520c4d9274c5708e35def891f8fdd8d911a78400db021e4d1d991a2b6e89d580200b92510f7c1bcfbdabb44081e35aae69936f1386e278220603c34385c0b2eee0ff6d709c751d6678181eecf010c35ac5d976a5bae5a45c6281489db801061d13c521e14d9a5ae5dfa502cd4f469e996e22a67e9b9d26fd05a2590e551fe0967c5a36d2dc181947d6bd4ceb14344b08ddd82876f6c9a1c81458767892e10d9befc8f5203aecec36a058a460c92333f96050a459d9dc67e297fa884b500f18b25d7e2d7001c2ff2088f5a0e009cbcb5799e9f76bb26030294cfa941069b908a746007d112dea7b8822219d16260e8bd94bcd6caeeafbc577e6e91d5e3cd214335b12364286261f1ef830b57805b05336e64e4309891fd910901236abffda2a40b261d6e2d5dd598aaf8a403272074f58006aa6063e826e80710c8bdb33e3ace794460b624e5d92f16711c0d476b9fd47a44937762d349782ee891245ef7c1467e761e972bdd93616cda73439cd15fac16c91b7159eaf77ed61999696b1c51a3c95b830b26c3b63ec1d5ca06932fd8c4c6d315ca4dbb7a5dc33b7d0077739f8cf0b4300526c11ae401cff33cfdf300b319d25f6268fd1c646acc15f24c600971aeb4b0b71d7fd0db6a5b55a80640d08fd687756bd0861f0582a91ba67c4b68136b93ad410bf9d6fddbce8d1ed7620df43a06b0c924da7fc0c81f340518c0bdf29dae73c600c5e616935810b62ea41fecfe3e9b384d6854c5c3d16248cf10441d22354ef211d35afb0eef9009ec93cc0ecfe4dd122488342d5e5bda730e75fec2338826f507a7cade523fd6fd0f2550b32e090e147ccd32dbb2776fbda83fdb13399f60ea183e73fba3d143ea64137fdef5d92ef69990ceedd9e8eb7415479bbda31e29c5083115cbb8166306e7540b94a97bdd8bd801a14cb439175018d7b0e18f50a0441266ed58f2d80f443c804b9aca5c3718c712ea5dbfd695e9ce3b342bcf3855140c897ba837ff675271d7b54cc0463eae14577aa26b4722267b0e579a5398692207414bd26d362d504eb65ce8b5ca0f1d606236744e2b7446b1ce7f93f6a8ae6f1f41fed8b6a2be3fb85d0f1044cbaa3a093d5e1bfb965ea3c0d1d5b5ef28e2542b0e376b551b02c55594f61aa992c1b25bcbe21cd7b9628670ff840540afeb861dcb876ffd83eb2dd63c3446b95c2ef69691cbbffd832800e05219195269c5c82846c130f1c1c59760fc39af0c93dab5bc7b24b325078d528c1c5622f72157fb2c9725b1f6719b6337dd0bc0bdc101573369b02311da230861af6c434cc5cc4030406c36fab4cf096e344d9427e5cf2c445afc9bfea608858bd33a381788ab860fe8f5aaf219ab054c25a84c3d2bd338730f030cb94cf0d0deaaf1015251ce1425b44b04217d53c38cd810af54d704cbb2a3a945f2beab57d69fa34042c0c2c20e2a38e9a33f4933c1c529924fa284e5aa9cdf3e036cfb3bb3ac3099b5a16b8c03f73381a885c105224ef28b5cc63e0f121f77bba6f1b2c9c58c7e0eec024eb315652bb2d3c4224be7c7d63fdb8ed6518812865f1137a8b31cc671489a6e5a172fd108241658326df986cda04d9ff5e123f9da24a93bf06f9f6f2172e8bf9a9304db7631c3efb2853ecf7ac0776f718aa2427af6bc0ed6ef22f0b5aa593d0cb210d9842d2cf9d0913161634e2cc3b236af9a318aab4bdda28a9c9126199f5a431554e47333df95dbe73c7013b1191590c38eb17dfcecb0b85854eadc685bfe871f26f230217491079c7ee3f2a3b96a589a0dfc7098c54cfd56d8fe6a34165da8144fb2bc30b8ba2eda5319037fefbb4c0dddbd1f5996cd639acb46fb15eb39d8278563e3ccebec5f13759febde6298927cd0f63d16c2fe5ddeaa40e8e7322db50260785eb9cc878dde1c7346817aba0f622563cc539d7972a4c96bbe58a245ef293d5d90c84a5c365de8bd7cf045008c3dd8356651862dac9d75525dc2a1e3701cda907ae916cbe037a314be9cbc5defbebcce1e5744ad127b6cced88589803d1de409e56c463196ea3236a3f637dcf551498b95707e068bac509b20e76e0152126b01b6102fed588efdc7f91711c7262a2ac765d34f3008ebe9713718623fc20948d75e171cdfb5138a140352edd8e47269aa4e3bc05f9aca30ac8d613831d60e26070f3df87d86b38f3be5cca387dadd78c8d0ddcf05be84fe1af29029264922240d7a23a586ec56b9cd97d562d07fc8a3860c099184a9a291f84a2ad1d19d0a66f175e19a9ce1b43e8efdbab958e247156eac6423c73766e19636b4db7f312de57a1221a8abd07972f675083d29f92d50c561cbe33bab17bae1c1b1f81102158140029848568b9d25a35336520e3bfb573d86b4c0beaece815e4ee28d399b1559b911e71c708f9e6ec016af4ac90bb534a11ea0c598c4fd5fe16388fb0abf25097a7926ebbc8bf7f3c837208832b116e6e63d08af247814d579af24822b6d2272d7f4f0d51d96328a860524975f30b94948efd8f4b8917ee858921f1088a61fbf3a3ffc3384166d2b70cd945106a56daab84f2ff0ea2a6b4d7e2fc65357cd247e80ccb304acbfe8ee2e9aca5a6c207838ecbad0f8dab50e545e431fab5ed309fc511b32859e158a30e7e6522b27ecae399df9206fc9366fdb32a7254d40951501c21c1346a0a38807f10776773483620a3b982d4832938c6d705997926d802a445a86845ef0eddee41991f6a899795bc6c2ebf92bb41d02f46f256136a13826ef04e5c6d9e6f79d0b0541aef2fdccd86a0bab50538c5c566707022fbb51750bc88cf7a3bab2cc4431ea83864ee426761b58260a1a1f5235c9f7d8157550d71542ed5b0e06d044c9555a551038ab65f71149a1aeb3c320624506c34372b45d0b87fe668ab956acca3c4b56b93a48c32334802c8f6a73f4251ab5e4639df887045794a805d46294efa164f4f9e6fc7116411c8c7002effe88f42a6e588118c91e691b26f4aa6970a96eb35dec9cac140fa44ccce5059d7f6ffb5c91bcc108dd00fe153fa19eb56b0f91f69b6a8498a55d49c5a65138da658cde1ecd16279d602e96c2e34c466c9c46e3928189b9bbfafd5c1c4c064c45b708f3e34d0de85d4d0bbdd60d36745c0958f80618697832738d206f84bc783ba0e0724eb7af874f3e1f5199b84b5732d22896397148257436bf3d83ad5781316fb116a7df22d84db12cff83bfded1da718b11d426e7569f8144c6013ec2216c18655787741975844713fad5cdd79b8dff7ae1e15e9ed5a2c15e41ad93cee1678c50ce6e1eb3f004200b6982e5f3684e5f6b484ee0469921dd7323ad1ca20040fa9ad007d5570545d123d36bf5713bbdd089cb36ee1f09fd813172be997d9699b6618bae2d1f6780a1075417d68adbbeea23d7012e125414922ee74fa3ace6fb9b73b516dc41c7d0e10ba240ae5a6e5708f3ee1ee7ad49d9893c3f422280834c9409133268afb4c0b90e66b92c5aba698443d52d875a3debec111ed4afcb4424e52e13a4d7841c989213806ea55d38d081142ced1aeb43a5a3a323be7b4a0ec29260d510264d1a70a800c3bcbaf1580e2f23ec9dc5dca3b203246ae9d9dcb2fb3f83228034a35628a6133edaf9c20ca5b1ed1c9effb6a13d2a518939f04f04036ab7d045daa2d942132761d5716bc80b806ec3419028af146ed60f6de371764fceb41bd46e4ca33e6d0fbe82f07a0e82485e2935652a269b01b39e286b2b1ccc56290da670e35a19ff0f3aaca706e07ed34f0de8168b703176bbedd3864c0050f39ae1a942b488f2d22acdff067a89c02171160bfbad07006cbb234fe478a0d0f8fe6b9d89fd51ea770b85eca4d4273baa1b6b5add4dd68d0c482079695ffc1be2ccfaa71a0c23fd632001411616fdba2a7e9f742a85e52ba0d7f447d0becf1e2cea6b012aebc2771f16065280e86bdcb0263d00671bd7a06a0145f81a0d2dbecd2a21890cdfb0bd461dcde9a20c8e73c6c262f671c9feceedecd07585d2c49a40f9cc3b990e034d7319ff3aa3481d699a1d27f46aee3c31354bdc0763919c8728d271061f87e68be5150658481ae553fe963708380604728f0491374cf0f6e8e8f9c9a2f16ee15c0815b9ee486b51c403039c8e48db2e8d1d3c411907d303e2941d80e1dd942a1cbd085e0dc6f928f14884ab953b1bde9455862eccbc4fc5bab638419d488c0f7ead1f62be8f4ec67777be9d3df38801b1ef12d10b0f39266923550b29234665837807df7ccc989c96c24da104165fb8b755c33085a914b3cb83d8d7ad991b948028033deeb8faaa41e455b4a2ce0370ec3a617cc4f3dff1936f0ce25a84c6e8312b210e4a354b3550816a2fd4009a92f7aa5247df29519b6f1c9f71a4a39bf3c7e12eb72a97819f6f7900232f19d18e9b198bca53207c6975a74e570edb01467c1026461ac9f08c19cd9e01895f038387152f0ed8fe5f590e4f0867668efe6646492d1a756de56aadf7574c331a60e8b24204685c511d8dc6693a2c0cfd05d4c9ea26a51bf68fe84253fa50f1fa76c08b8f637c13a19bcd8ce5e77e9e52b1573070131940693e028c20c367a2eafdd66998b3ea483d2991d6e2f4b2e8eddbe90cd006d4dbd0964a7092d827b557cc843dba26521c8e95aa720da9c53db25bd4305502c0d9fab86cfbc57a1cb7a42cc1805aab43b7c022600af615e4ca4aee3372830c3953c0df24e125fe9b5403d7c253c3319977a95bbb44a6ec9a0994d2b2704d1c59d481bcebb19ec56ad9002e52c19e6f577201840088d57026e729c04e724925e338bec9cbfd1053ec2553cd934cba2b63920c8fd21c052d1d0947cad484bb20d3bba8720d3ca830d80046ff101c56ae7ed9e11665e82554d5039629f464610ad56c2f4874e6ddc7b2b09da8e38a605a2361d3840e48167e77a090bcd0a92410e5e71918f2bc580c62343630a6e7409144fada9bb90b4569d4fa5e7f7f09743030a45bfff7197f3f6abb2b3016c02b3c2f55024052cacf956cd3b089dba1c721f64d9c7054603b633941a6b09d3f98ae729d0960126e023caae5a8fa158fc41c4fac61a56f17b2bf78df6aa950ffa7c7180d0a1e62f9fe8bc6427417d0841d06a2284f5577e015d6748d5317d236af84b0ef7a9b5fa848e4f120e0225c7d141d00dc404b3bd42b4736512af0d3d89232293a7e7e21941efdbdff32be5b3a6e19f0573f126154ad84dcdd53d087711459be592d2b8e9fe517b547b4bae1fda706212a786b4419e9483bc1607045d0e1a27613b531032a50cfc644c97bd2b05a25940c11035a125aa817e3f76d372e788a891f1b4500c2bbe4d6d298e432317816be4f81c7d63522f0242104dbac5a441f3bdb55c00c1caf2deca849a007e38a26ebbfea5a96d96a391e7d790d79a8de78af8873b113b42028786f513e8886582cc56f39d4edb211773b9682dbb1faff8f9b1e6bf51f00b9515d83aaaa7acdd61d0b50bd743a75edfbdcd99e171015ad606ed6c491a009660d207f37fb538dcb04e6c69a4a9fb8405d6064cee99cd0c1285efed5ac261146755e3ff537945dad0e8e53e91783bcabbc7722f35d5c2c94c90032c32403a413a1e169eefc9ae7ad2c48f1189a6966fa08813b0097ffbcd9285eaec55700c3c0eef1d11314e4c35a2b0333f7a3bcf62de93107b2be86d92b58701b591f7fff2919373463c84179c51b95e0d9ec2e0ee282073c180d7b025282990702a0278377f532000d92fd29dc1d68eaea99bb46b4da09f3b5e75d4837af081723bf9fb87bee9298a5a8a63aee0ec44885cde79eb1aff94330d4f8360821e0cee4c6796b279de4c0dd76b65e290d9ae821b40e1a011385e49cf18f8f801b659b1b9e756629cd9116f6f22f3b9f16d0cb02f2ec7335871f2eafddb046c6e8cbfa0f3cef0ca48227493c37e5f1f8105d3867081e64ad2b9636b8d4aa7e90a51cbdaebd10e21d3fa13ee87411014012f1c7b91ab37d4f17fc1fdf02e415685a3a17d7fb93d460c61f810067c989c11088a0034f6f4fbafdda43cd031ab2623198e8c736170a51bbb1a4697028015b0ed38814617e45f45491297fa7bfd19cf1c19f7f8dd079fc5092db967d55a1b22750db73de037a28b59561e56df70dbc35fbcd26a65601a178dbfb147e0443cc20c20844326e38f94aef3a77ce705dbd65959a7313a8a4f9d4c22b0e9d8fddf41b4ddb92842e6d3bcd82b6cde893b04af43658b5a21b6b4f0f8d3e107885de0405ecb0f9414be1abbcfa928f82934efc07cf99ba63bbe9079c9bbc78524794422f791b779b98b7740ae3cbe41a92c2404a5cafc7dba261e55bdc8972bb32966316ffe0171058bcecdf2ff1d363370bac34e209a4afae3fb02b99a4360ecc004c14e9d48c82a0ac4777b71cb73f6524b9a4df1aaf41b8e46a3a93d84fbb2592fe2262d6fcb8d0750b1c2cd0c96209c9e34b9575f8243afbc7d19a3631f8f33f431e573f86927203bbe874a3da4e74eefe24d8058b6547e5a862fecae5c3c6349c1fdd6b3ddeaa836b94ce72bf53380aecafb5f0dfa862eeda3859858e6b254f4711940450eaad6a1262d37760fc8f58287375c06fcde63d136850a953eca5d45928f8ef8c796cc13d436dec9148116ecd0be13d978290daeeaf22cead7a82bd312bd9809bae76712604886ec3b9a050dc87b6044ae220c0a5659ad1b7f6246c401e84f03dcddc76b7891116f5706dcbc5794708606b66727caf30f2592483322a155f818a6a839ad20cf0ecca58a2079004067af822f520d078981f954efa860f0972eccbac054346ac8fd9c795271a4991b8439a75fe4655484e3ba4b49991691eb75ddaca63b04f2b65fb618de570cfb472e644f1c90f245559f8e1f109eaf90696e3061ef14e5ab4a01d182729f0143a7154cc5800b51884b0a40bc77d0eff03542542fdba9c694056fb472c25661eae1ce78f8f04ccf89828d4c341dc2f732ead6887fd4e7a83bd2314bcf106c0e9ac4eba025b9913c2ab4834453546ad1715667c854995fb5c071470521f2c99852cfb69e382599058cabceee8bbf686382a1d69181fa8c617dd09c3e938b5b24cac69a0fe54cb0613d41ec1ffee5e7b732bd3aa99525d5c83c712b1229a817b2d0b431bf081323d650290ebeff132dc58019aec382fcc8f8bc1e2aa581188af5a5ea672818b1516ebb4eada883a3740e9100a6591a842e053956aba182352f74395f5433f26603c2eddd71b10510a16eb", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000f475a2798782a4befbde35b045f9fca800000000000000000000000000000000b5d68e70b88567b1352944061f30726601cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x00000000000000000000000000000000000000000000000edce069b20cdd3b3c0000000000000000000000000000000000000000000000035587edaec19bc9d0000000000000000000000000000000000000000000000004e8b61a1848c5d71d0000000000000000000000000000000000000000000000000000ec07f134570a00000000000000000000000000000000000000000000000285b9d961e7514ba30000000000000000000000000000000000000000000000037ced53ee4c6f829d000000000000000000000000000000000000000000000003b4542de3f9235c48000000000000000000000000000000000000000000000000000295d724d1e6c40000000000000000000000000000000000000000000000024984d76621f77c0e000000000000000000000000000000000000000000000008d9170675a02d3baf00000000000000000000000000000000000000000000000a55ee4aa4908eca680000000000000000000000000000000000000000000000000000178c72a0427f00000000000000000000000000000000000000000000000c780a0719338d9b0c00000000000000000000000000000000000000000000000287dc8993e1ea3e03000000000000000000000000000000000000000000000009b918c594689408490000000000000000000000000000000000000000000000000000e6a0f127f38b1c0664c1e064d80eb94bbb91785f1d24ed1d7be9507613a3cf0d4f6bf2e9d7231d1ec3f000b4ae52b58619010189eef29373ab9262aa91aa7143502034d8863011d3224f2e2edd4d00c173d14a0563b3055120bd423e5ba160b9b85989830c122c3d4e983a824d1a6d29966c7111df5c9199915567cd4aba98c44fd0ae834f9a277a09cbb2913101f449f75e3676615906ce01379d63002576ee7da737262b32236564d38f5e7a803c1f8d9ba4618283f2321568857b28f7c1f0a958192e02761c28d6c7d667f72a60c1b47882ac5bd519c2f272339121ba07a2660621dfe03922117962f26bcb850f7c4d6c290c1394ef955cb7d8158b965a8f73ebe55715b60a5ad26fc88ba140f26b746000167b877d731647f2bad22f5e22aa8662107b3903ba9d871d08782e78385c33c1e9322705d793ce23c873db9d99711f08a2fd4419bbd315d1e4fcdf4ecbaa1f63ced3d1a9e6d8224596b8db1f5bb494a2016bde28d00c42c42b88f68c6ad1f771011f95bc6cde5ad55f886bf7594359a1faeec71a2546cb3ae645c2ceba21aaac656c9691eff73811ab3f7599e839f35ecacf0d13122d59cddefd6b314e97ec4518cd154f343b7a3e4a803e21aadfa71512f8500e046fc1a29c86ac30ba80b92d6c88a8ff016611d89caf17b20af9ea60288bc707548bd820b583f244b94937314f51020158fbce2e9118933f203594ad959eed07b4b35a6ec429eaf7613eafa54fb3dcbec9ee7bedf0b142fa863ff3ab979f532a51f208c9b1df2dbc7b483ed886bfa98e9725ea544775c7bb9d667028f4b19627f5ca4f842d8e717dd35f718dc7aaf4ff57825a57ce340181d5bd8e6d2147ff2b1b1daccdc7efe44863337dafc46a94c0c29b71c850f8af09d96d588b92035d04470ad5f8011691979683753d0aba181757afe9aadd77cd8044e703fa50efdc03a037ec4c811132711ffa1d32f74ab4c4d105fce937a1e674aeed5311039f6c1b82ac81638d61248c4974ac1a4204cbefe6558773523e38183701596fc70db51e4ef18838b97f26696532758504247d6204e1bb16d1d4a6f7690e22b92ec22b1e7216c99504649f548ea5e76058166e6ab62d8b597da944b3f251e0a7f5dd550cd43a77afcf6fdc66df86ae1f2a0bcbf217be0e001d5db52d8b348ee751b0191305dc8b39e1a34a8f2b255f13e90c5f4175cb2e3863169b1e6b74eddd072b9f21a8bc5ba5c3f55267fc4c73f55b05016a0db089eda8e476039e4b47da5f9dfe2b79a3acb1133d7b29f45ff225d0c4272bdb5904c6558a7731288c110a549d491d428dfbb0b13b94fe735c89379880bbee36f28f8069de31e430ff99288c66fe0c006bb22bf7b4a9592cd03dffe27a9ed159b0659b109004e5178e4bdef83b282a5e323d882ab264f227bd6470279199a135bd78b51de8c6f36652f1ace3c0990bab78767cbb925396a35508bdbb408df3194860a19d252babb16e91402e2ed12eba5dff99e35eba65c89111f2f8ecb8ab001a0c4d0025d5d2c275efa03b181202fc7f806b44988c8fe287a384ea1afd59ac50a70e7d0d47d94be1897ec5e9e203ac5d9e3f5f1f7cf379e828e01f76efdcccb0614b23beddfa5f6f8cbe09e9e41c6bd67b5e867148d005eb0eb83a20c69edae66cb10f0399f049cc4dfc577fe92f3160e22735ba35b84465fffccdec15f911306df24c11ee96201e4b04d2e1490d1ea90f45ed5669b39f607652462fd9f10ff45449ba0a3d69318d075e365393000429a8ffe54041f667d458a24a950cd629a1f25c88e500b915ef8b8191ae931e7bf88306e191275053149520e06fc4c5ec65452d0f231b2a840a1c2e47c4d6274c2fb4eb7d2ce13cdd3eef4bbaaf730576192979cf71566a194c11723e7cf52a8f8c03407906136dfb54c7618450df427bfe58533534b6f607df93d4e525510fb61f91c34085310910d69d63416b1144cad84e2db53f079311f0c7d2cc880222cd9094f071155155d56e58f28055a5403eada3ea64bd00a15222216f54a58a11d88c720803f206e5ae51935e5afd64b155c4fdb32ad6acf0efc7a2fff6c6dc29aec8db1d185dec27709075fd7366dddb28a9909f20b637e709d5ac48b066d2056728aa6ea0b695a094e8b077280be353b709c208c531be2174262ee74a6ddd16cd375703f431f50f42d497ebfaafb2204fbe31c1db00f847c205a9e5bc54db1f42362fb4a9b97d732b7b2a543c772b785fb139c372ebd00127ff15437a653f0397d45a567e5c6000a4b9ac2812b07511de3a692e5b94e5118dc68dd31ee21507c2f4c9c52f9c54b45ebd3c2918942e1498162908e85bd8cea61ee40e823bc801a3a24b6721333c061e10ebcfd63353a8651b95377d721138d170246bf515650ded72aa95d8673e5c8e1440ad005d31d9f75f78a8402b38522bd14a172eb6171201a44177bd3a939cece9958e1b278c01d40beb9d5b46360c4f07a43b49852420a12fde2f03060a123ad784f488d0b4872e94d10feba6c2fd3959e9a9b1b56210cf7b3b52aa00a90692846a05675ac063d0e933c1bf8f8bb8a99cf90967576308d1f7402a634a8031346f58838220918ebf2a36611413ba04bbee53f23595c2011fec50e061c919f78a127db735b3c640ae6c8f1ea70d4b8216595f35da58041939c77c2d3b9526ddb4971f0b4442675f2e645daaa4cbc3441c424463efa5d42a6350ca221811927c5c08451ea4a05499a29d69b219f2c993d451a437f43d8516e679a796ae6143e42a26668b048b4ce5c0debd5fc4eb4e934d2e6ba7b7b5b31084748288df95597c2f838cd23851420c57562eece196a9cf56f605cd2cc5802ebfbc1e5f4d970648ca521f5dc09194cb6b31cd11e5dc65a808aae5a72b0b8d06f3ed69825af86f15664eaf3d8cf9b9f44edbc0cffde603a1649a04381d008c1586681d08744417cf896484566b7c9f40d9197cc46472eea063bd1763135fc824fd8ff1e9cd4bd25ac764edfa4127ae79f3b4b3236c7e149b0409e4244575bb064648188294df57511c5e320a52988c7a98e71de98924c63cc4df72c10f76770770552f6688be1849aa0a91cca874e842ea9a0d080d3d6eadad68206fbe2ada0d07de35b059ad5e3f3944dcb6a650ee64e230a53f4f04a4e9ec9e3330f9312e182d90545db4f65973f636cbe00905c00d1cf4c173052b4f1deaad0f09239cc80e6477cb573bb32c25ddce44a1b5997eef3b1489a520c3e714f2bd84b33964b020a23f0a9ca584020b90dc27764852b6b9fa3551aab838b649b78767cbf40a1b1b928595c0ff6d5b9a2c8dfbd94eeab852563fe2554b29e55c8966aab39ba2cf11c5e2a750468bd5b34956ea7e85d226cf5f99f6c7d0fec89924bedc43b020120e17513393289e3cf9a0f0a86447b80b25275c4e543758bdfe998290522ae8421a90eba5171c4ce624b6466846d2ae316de1ff93039df1cbfaea396d150477dd040cb85d01066dd0015f3151bf7e2ef022c1be954af6bf1032312afcac83ccab302363d2e381c358a1bddf79f73bbe462b5df71897535eb55e73bfd38ca44f510357a687f0b64fc814ab4f3a84c2a71894b89bd2089f27aa805757662a9c5006200d757d3341fdae52baa65b943829ecf02888b4401bf065989bdd8a2cb71ad31796f5df7bb522beccab8a547cfe2cdbdce2fc7413233b72ced05ce662f88af9123ac93aae51fd8c1e8025da7bc2345b3b110b5e59d2ae2ffc729d0550db5fc60329666ce8fb0d6356568f1ef2618ac7f7e4bf2a6e3ae35b7588780b29e198d52b298c4ed6a93551745f03889c5752475272e91f87e836ea54caa93cddbcfc8613cd6384d28dd49ec99b2232309b798fa694072d9772542aab79584eb091633d202572c6c995c7268e871d4f8840d366099e7c124c6f0f37a91b629079c7835500d621755dbf91a3c126649d7ccaa9c979b09aad02b315869a542dfb4b7366831c2e3955c00d7fbe2c15682d53ac21ce310fea224a7c8d972cad6c4b1415d0a91278e6b67b2a051654d60eacacfe4f32e698bcf8bd7ac192bb696d9e13994133116a66f3ab150806fb12287e271bd614aeb9048dac568c1cff2c1fbe79ade43f133b89887f6f2c8ec436d5b37eea62acd2eaf5508a77662fa8bc2bbf5c39dcb225f3bd14c21670a9294633249d98d602eae71803a11c9bc9277e1f3ae18b2f3919f778be7ca5b0d88337df43757f52b0a273d554fab9f5649c3c9e3deb5f0ed82e597504378f93584af394922da247f0631ec17835c50217ae6f476c04220a57054cdd47a98bacfc724f2ab5d7c7825ce80f5978e502f847663637b90af2d961188d67c326e5be62c40cad44d5d128f5b18fec898bb36bc426c93f982f6d470d17bdb5f79943b681d033607bdbe16be4da1a1dd8fe1e61ac11a1f2b4cea2a3fe26d2e66df0f886c561d05372a059845d520c1f729b37c5ca4093b912fd806d9e15d647dee4fa61a39a465c97ce7cb5a3793b99986163121f8545744c3ad753f82ffe37a90ced4f23815bda2c9278462546ca5ae118e198c4097d3e7dc28e03be11f8ba469496e5bd7d9b6bf4a8b5d56b480e205e03e82cce8b21f464a4dbb6d52374e84c1acdfb69fa9e531159ff2f6f261fab2e661214c69bb7d93f5f0101ec12e85651e59e0436e3af84b244b29464d5237e0de9f96b68da0e0e756b013792218cae5ba282e49b29b371bb74fb66dd8019240639ca2424c3e739ca210a056506c0631528a20cb89f1c2a8ecc7dea173b6ded9a7ccfc9abe627af2d7ea6591a151c1673a09d84f1132062ddbee11993003c72dd6f4de585c418319fd64fb87e18f994b3c90618cd566283a36193487235f5e20b3eab9b15e3e842246b3df0720ff304ddad5e0d9b443938d38b7c6c8ce9341d9ae8071016a21fc694f170002d163f7f12d068f82cc36e51ff8eac359d86fea4f76ce490557e9c8d628cd2ec7311d270d3b58ff7ba7d52f5d7d3c8903dba14149a80ab7c9ed09988a5ea0b82620fc56bd2241ec60e84caa696384f1094b28877aa06e1061600b4e581ab5bfa85176412243451efbb1dae13d6e78968d3476151211492d8d6bea0a32a28eb70c814fcec1e1a77683784680cccec1bc494e96aae146a4e663a8dee882caf2895d80be4fde7baa3a49d24c53c0bc5f54b6367c26837215974a6cfa2ee738edf84b21115cf41b00618506749df9221f329a01dea694776bf3abaa9fbb352f9454ba01fe2a5c04286be6419fdd0afb3d051ad92a46178d89410fd158b32d3625afacd0232156648d27127995b308f527364b3162e0eac7db4e1c9d7222b263d2093071643a985a9493381f9224fe080f7c49e0c1f178b1cf486bdf027b5677f67194f17990a030cd95227782c3ce2d6184ad12c5988823233924a13ec460a43db62a518a59ccd5f1069fc98131ca373c6b618fa7792591edb22dd0c242b5447019f57229b7191aabb525535024a14782f1ff968b3a1e8be97503ae7cefb6aa8fd3a352f3db9f3b9a9e6ce148345848e3988651f5472abfe980dbb8743ac95f00720131054d873d7ef9d60eb7bdd6af577d252f486808d1e8449abc1d68b69f4eb56811a8ff281cdaac4ef304f4582285d5e403c5367a43eb7027b36be9351bfec94cb0b607e652ba04957a4c0fc0482cbaffaf8932b90d94796e989278b0d100b92f826b213ac5b4b3b8f3028b80f591d0274f9a65fb57e385ecf47e33ecafcc031ae2e8c65f2a0175b4ec3b0ebdd832f955e11178b2591b7cb64f3cef1263b56b4bb2a77690f4fada83c47188bbb06ad5c506a2bbe442b763efc4fe3ea2fbc21e7e12fd3c530be1d11cd7e2272464c70d36f6a87717d45c02039d089cec0e1e812bc0f0f466212d8f19f9582712cc3d7f4de6c659abd9ec8682cf25bd9c827349d1f2f9b83f6082eb0d1f5f99f966f8411cce43872f5299bdab07880b3cfa693c9da160c1460f44d22314988c3a6dbe5127978d96a9dc6cdb3e523ff3a7dc68192cc133676d1aeecfb4209c6cf678cf1dade494eb15819b2171f776abba11bb5ec951a46ef47e4bfa4cec4e833fcac0a9fcbf9ce8fa3dbba049ee45162512a1d8eff00670e517adc74f7cd0627f765323e0115bf5d66b6302c28b66300112c7809a720ea9b409f976ebf64fc48d0654945028900fed3563e17258bb0ed64d410660a2819a435f89469d02c009c5d5c810725651ad230e22f758768ebad58dd9559b527cbeb59f0d6c6bcfe965596dea8e090fdbd70e9f18d5d6199cad88165f329d612b8518edc1bc88f0431bb6999172a8802c4ed1c417dd26aca97ec6358938353292e3ede19a0fd0e699fc08f072829424760b71457eadb10eb25e52987144f92080e4c20439d7bf3fc26bd9f95d7c87dc30e6f6f5ec033a751f2006d050c75961da6b26abbed3ba81398b3a374371dfa96a1b6ed1af5b9794c5789d0b9a912181e090050f3ef76dbd74eb1b18101e748671915502be94c168d34a44382caaf3816c14e6456dd88c14d8e68e2a0d3624e29cc00d6d0086d6fcac00011208602d21276b44c40d08f7edb5f2dedbd3e7a955ee1f92f0806854cad1ff3bf05cf91a3103b631f8c0563591c5d1e70e97e16b1c7d05b505ab1643e49146500cb77eee01d79a14852e3fa0beafda3aad1e41941c3e2c9dc4c4630f1218656d719506dad0a2a35a616a505d4ce99fe5e476f59413c41b02fc17c1c6dd0e2a06fd16c203e0d43613f60be6b6ef8389331dd4fedfc337d72277d459c4f6a5f6f5923716f2a1c23488142dc51a76fe1cadb7a3a1989ad7e79d1e63b75e8952f85ececd4851f22098299a53a0608b5eefe88e783328a37526cf50b972c25f782a2e4b8c0832c302ce119c943a5659209e9395d43a1d411fda6b050c14e7e1ddcfc24df068e781c194e17b295ba95cfd03df3c17d0ee6a0e36cc8f432b6c339d1d81bd80b621804194513ee0d0de54e0c0d2eb0bc3900cdbaaa7f49e6dd212d386a15507ef3f8200f7c2a499aaba3d59ebee961ebb27ef96f5c1d8b11b46d6fab6162a3f22f8315578c760b7d247b7c7aca1925eb17442fbd0010842c7faebee5e0d3df7cbd6901dd2c01f4ef6e0234a6b5ee31e14e71a44f652f51db365417e717efd7cf0ba920e728f72b1246ba4a66757bc3d6986178a01c5818dd0a1a4fc7ecb03465903b103629d6df9333ca804018e7d7422aad2a87673bd59d3b53e429b9abb82559921620f508d103296b6915ce66e75611f67f8500111ed1ad5139744aa1f4828c3d07f2bfe7999bd6a3eb38147a887de506421fedac4bdc00ec8138f985fb7d034a01300c4b88c4adc6eff96c0c86ae338b902caf73936434123b14e06525d0ccd12ab7cf13b2e53f7590b841b8fe4aae1252fd75c40e2069390e5ffbb7ab3674a028d85230eff2bf8759207cc777ebb790986beb140f46d20e9bc7f4ba078dd1cc05d91b86bb252b812317d54453271888c960dbea5ca6d90195f0dfbe8e9c5d9203fb3c294d6b473b0a16ff79ffdb3ec24daf6d59dbef2fca6013dc937fd103572c818726e3561594626eaa1f45db3f64a8964104a9272ab81af3a1c6bb478adc1020f11aa1bd01ee31f58f4ebb848520510dfbc05408cc2787e367ef0c31e41328e0accdb8de5dc22799b8805f173931694094f7c0e5de52dcd41d815d5b6c7c2daa0496def1b376e3a51d49025d9f4041715a2b593d7423d0248819a54246e5275cffd211d8ef63b85f2b0958c61c32d58abbd65ef70ec68ce84be78310d6ef264bb4a81ad1622c4fb0d54040f90d90786174ed1e353935ee3f169a98aa2d58029207afc41769c02bd3deca0c5403a93efac655b6a56ac4d4838dd6a08b1e1a1410888fe7e934796fbcc11d77c3b0c2edb80437a769787966939c9cd55a364f3061778bcbe570cdf79ee300718671ba9baad1c30888faafd754749d691e71801b9821a796991690747083227e61d17ef7a39b511cd5b007568a1758a9395fd016df8a16e4e56ec6379bf190011628638fc021a696e3bd9603d9f3fd7e1e2c571a4b5945b7cf7dd86b8981ef6b97b7bafc2b22a55bd6368091f03a645ef0df97279acee5fa55dee3ab0191d1633027c14609a74332b0eadd3d6f3ed85a7956680f2f1c5a7c9445d8390821c05fcc9aadabee41ade1fa32e9421313381aefc7b3170feae96e81634e042eedc9d30d9e1d523a550d682760ddc428c98fef51b1e221048fc6822f1c3e89a26903106cb0cafaa69eeefdc7ef3305925ffeceac96e121206290ce9d91c8ff16641033910e84b4bd4bc1ee56db43b62ba86dfded8ca113d00114389bbca8cae5cef3c14f725ad380e7d712f7d93a1e588647cfdbdc5402c7d4a698eb18c2bdfede827d90ffacdea8cc298a5629b28514bf731b7998f82e19b16c332526bc1b3713c5b41b16489bb6228f176ee6f96cc286665fc5db930036af42600c67bbf26009805dcbf054487b9b0e1c2cd7173f38be7459892ec90652b26f72abad8dfd40edde3511e0898abf30e957b7e70b725b2653cec4234d180fead76b8ae6be8b7e18ed0f6a6922f146b264ed870aec50f2bbf7e4d178b81fc952955a05a27f760b0adfb14fd86c9e21c4b6ab0074e6df091e08653b89d324e1e066a0f2825afb9be08e90f11ab66a57602c43d45ab5f7d97ef60a8670a61fdb46b341552a18fad736bed3ae77890a408470e7c308696f37ad26289f8a7f00cb96b31fcc1d4b0cf0f058943e8249b5723f031c2a02d659c2a761fa0d05d002781fac13ceb35217f82bc90dcc84665530ef2ef7e326470ca0fae8f6771a0b181d7f987290f7946bad4dd103156d149d2f4d7fd9350246dd54b274f5a9c2ef256421db001f48a5a3b95deaa6967db143533599dda5154770528c1c4a01f2bc246ec9f2caa1539470292b344cf2ad33e67a96fdd2e7d07450df1ea9e5d36bd510b5a2b1d115be0f0700e1a5652fe4b304d99ed587e6ff6955cd02469a5fecf829b17341a8f46cb83222faacbd1f539b89a186ded5664e49c7deace0b5233b10275cdc7135f9be5e3c62a640ca27b22b92456b9e6f146bd6e805cb071d32b2dd00449a613510837c67588fa39fe0e49fbf31b99b5144c9476150973ac9f0a4591d17e76456b59e88170401c1d4a92b94f17ec904a2374dee427b7664a6b2c22e240e1ca86974fd9e5e172d8647abea27a002645a90db6bcbc5e88dff24f1743323be66dd4df719696f2bd75926dcda82d3accd3c865cf42853941ef41de95af32c261b646deadb7d82ddb53574514e261a14aa12cf773f483d2545dc4a45105e23de104c8ca2eaa8d94c42f4ec7c0e11279a62ebba987e6c329eb49f2037b8092352ce3c21318796a17ce472d8ffde018425fc0b4c11f3e492475d2afe0c9ae00b0cdb4f51df51cc833ddcaa3aa78b74df3d936893f0a5714b7810fabdb17c0810d19fa485bd97f1507a77fb95b2a0c5b1ce5830dea0de18ef5f6c20a00e15d12331f7d203d3a5c9d261cdc4db79b82c59a02ec3746ce141a29564fc67a7e6ad0035707ce5a02d5035190ce39d1ade935bce8d2db6bf855b748412c8f6b441480b1da708c7587d576017bfee5fd7339097c4e205a4f0deb9385a3a0f7806fbc92f5490de7ba18539c7d8c659406341579581b124dce903c88eb72a4f4a86eb22093addd77a4a8b8f2fe48a564d820eae33127cfb442fedb91ed8eae5381ba9f1046e54e4e5c6887713366918f0ef69869a1e1c1254bd5e205917d5d5f7571a2e1c9b575c9df2ea2c0f6738f4a4206330ac1320b7f16f1906c4b067a64294082e2d754c8fa48c6470cf77edb7e03bb86f4a396b7ae2dbc56fbef172ef0cc08caa219968432847a0b5b933f0498d92e427b6e0d75969eeea525ce41eac022e468c0514420c561aff6c8edf72164ace360ef5ffd7cc2ed742cb8cfbaf855a20491a228ee0455b1013c4b2788e4afe5f5c3cb56aeea9c0aba0c2a3a99939fddc306c2e0d4dac3598daf10471cc14ebc2233d710393ea6ef90252aa70e609081040e814d26daaac5656530fc3586cef2d6c4743200ad764b5e4849b3afb18e28c13512271c93f14df40b13bb0e4ba277e2d20080be06d4b74dab1c3b51ad544e763f125ebcaf349f694dd1f6b8c60c2794ee456f96176bff72351704e7620228cf9352b65dc2e4ce420d8ba84e849a41e2986f0bbafc592af6a2eac9a74d6646379bf22dda3078b0408cacf02cb1a4fb0d4effcacdef4e5aff14cad3d90b0b79cc149129a4bdb67b7ba6c4333b1167dc29e2c02ae1130f373e25a4e602dbe1f9b88532001917a73ed406cb1019c2fe78c0278c373405583d6b76103ce83ac88def2ae0a8be4e4d15523c70c2baae6d7a2d10818b8894c593f7dac6103a75be2c4f6531ed679400c7fa71c82c9b8856c0d03dff14d810a3863018124c30e248a61eb93227f470138981ff90eb35b5ccfb27b22f9c3e99c4b8470896d7618b570015aa826ec67f903e741cd58a559e1c5451579265ebc761ea6a358754df10ebd689c5218df613d6ac78aa2720071cac7fb34ad6ad194e3e654b8a344971ae9a0d9fd530f3fc109ed0f01a69f6ef0208f0edeba13cfca9d6fa2999f44beac2fdbfcdd312c3132400d2921f726b2eac70133a0dd787448687c557fc1e69083e8169747421a39b826c54c494f1ac98dec8be7e330044daf7a0bf4eef11c79644bcff8298e0dbb24bb3a782d13457901c8e2ee0609dc1247478b2001d0d6c9db518ac9068208fd83bfe7d3508abdfa2503267f7a45de285633535685b6da31d63d4e2ff0720154ea6904ca630ce1857e3e8a1464cb6b5b16766638074546226ab14fde7afe2c0e86924792862a3f8832e46311b61bf635a27a61b3cfdffe662e7b8341fb9514e51b6f5688113dc4f5f812945d7a8ccfad91ede7fe0a607dfae5741848c88f01827ab0ecad4343b879f24bf632dcf9ce5db1568b3dcb0e92a3ea5d32bea5bc06fafea5a53fcf97c3dbe274575ef305fbde8c7eb4bfbd8e48bb3b062bce895e258826ec523ced0e17c77f73323f26abfeac8c7d8e401107f7240757fd4490841615f11738971be20699fc6c8afe8c9cdc2a78df1dd876c7265f3824b959bb5212c9b389a43203fd0b16ca64615dd125232f77f77153f9983d1a3c54b847aff7022cb4a694c0f04531367c7cd278a7f7a5aad796dca8eb486791d297a593f69912bd5a038e02b40783d1345f2cb58f787712a1ab0316cc7bc64d21d6035363f51a529233a8e89a1dea82a455dd42bc3795828066ab8dcf048ee7351d55a9a7870a0dee695599b4a9bd9a6a5cf01e1dbf2d89ce252c15cbe5271c2c2815edaf9f05db450c973db3930f9a551e2af7210dd55bcdda11077913f597781cd3a16457088bd5d22510baf958ef5e5d75a44055207b030f5bea7a15e7d6263aa056e9db1369a3521bb7832d51831151d9344364cc9337c3560d387864b7382aad762ff81f0a1adb64a26323cbf1d2f2be86ee22c9ce9dbfa51860184340226563b1b8af04deb0a186abb9d3d399c0a8548582177aa8378309d08aadd8aafa187508e9bd146d89b17af7b31920d5e2f7ea2b5881a86e81376d927a6a75ef1ad8dac6140219e02d5d2df2b473fd67a74b14fde0df6c9e9bb9253edb6d885b748527f90b30265690d0032e77862cbe2c5157330f90e10db865899ba2004fb7aac420bc40540b027d6a2389de4a85895dae7e6438772f59708f6ab2c0a0e8949d890d15d5fb14cb09a333e1d04ba6263268e7f6bda2915967987f0d2e45df0eb9e601aae96312e39e1add5778ec0e13ea0feb65580f7d4b73942aa5fb43cef4260dfd71cd6c25ca74631d5f182895f93bd853d8fe8c13db6e6a3064f4984cc00f502049db6b2f3fc84e7f072beba9e1e0da592e03103c6884b2241da120a6ce8f0dec4a023e0bb68670913ad55bd31adca44ef3afdb24a74965e6a5a4f6423c682b3f2505f119b8b942abb373d83f159674fbd223b4082c6a943ed5f6a3c0c30b4cba04c56d296e61d11f994ec45b3eb7c0fadce0892b09588510deeb84ad0fb43af65800ce1cf2b42a1f77afa03429b0e724ee01aeed3c0f0da7c26e9e0841f6f3e6995a6c26394ee8c1cf6f966c69582a31ecde78b71a2cb6980da3b820348502c53b6e762025993aab0dda298ac2c722b379d1769978b065166b59c953e243260294dc7f0811a8e2eead9e087b065819882752fca41693873712be6b9592ae57f30124af16a149d2e3e18080acb56723895e4b6b960a2e565c13bdbd7f4a56947457ace609911477284348841790e22ffb6407be7a6a3cc3cfa687abb560e83b8f95b12f11ee57180949ab894c6f63194f9795bb301e12258bda6bd19eef208895d3646407fc4b759cd026c4d8859b45b2db5951a4567b74ac30cb25763fbe11ab5cefab02121a2d8d2f4e68fa8da95a1d857eb9c915a24e6d65cacfdfea17b4f7c20b3a2a449be2dad4aa3acae6e681796e2e1c80d8e2be9ba6f33c83eb5efd04de8fca143d6424e42e247559e0d90173a80aa319d3698c58d33eeb2bd7224c03fe44de1bc549874bc66035e4ee132138100bb60a755a543c6598a5f06e0ef5d37d66891ab1b930794b5c2f560004d2356bb0b6d1e1bee7760b5f7d6e676b61896e91e72ca9789023470f3e234285e254c88fe67302f0c605e59670a402401135af417519c6454310e55f64af70feea1e255cee278895ad44da1a078bbf0f676dd705f605ff24647ad14d866c1b6d0670d72dbff0f44d02b049695345c6105d280c45791a7d544c5ef1225e1fb2bc196bb9aee9c3d7f0ae59c4346f6ef9e2042ce4906712a0284ceacf4e137c3e49b7305912c45069a5009e94d0de009b8412b1c101bd13792df3d482832d5622d9765b0829e4ba254614a09747aa80f7f11cd11f63c20711b449ae52188e09085afc42d7d3217f74f29686c3a5e4936e31ef89fb578f2655924c25b7b1a31ad2f2019eefc28890279d30b8fc665ee19dd8356f9eef3f0f754a9b70b8dbec4e7285dfc7f0ff474334c6bbdd8cd3c28f964fe49a17f0da160d18b7defd79fae2b4a2a0d713034f58e89606b1c905c31d316d878cda2f331f4f17fd9ace21980261985cdf4b609d527278aa16ab00b84f910d4193f280190563d7ed91b44e80458bad71e43f4338205ded6dc8fe68809fcad4460e3d1ddc2d2a4417c4dd117adb272f1fd4b5077d3f1b108fdb6648403ed5af2132302a2b2825988faee937f12c17e377767a51a562014dc54f7043c27df06d554e61611821961b9bec98d5c2570c9f6fe8487dbecc40137e68fa352623d570be4791c99f0800ba8d3b538ff0c41b13d298ba9924a3f180d85bcf37e477edf58d311f11af03fca8f7e3bbce16b2162db1ba146b4e895f914de18f120676a9471bc0cdcddf06d65a86b6b351545c1b659a08df6f9e19f66c3e0ac434fa776037a4e5ee4b3811b508a56849c1eae0857281bf4632fb10c571f2dc118a695db6f242ce9fd044295545f486838cb226f250ebf115a28f9f3e6be7b7c89876e31069d9ea516f0815289ee7e0afd2f36c1b2ab344cb6bef65eb037d2e4d2f16cf5504daf24290c00bd40ee9122929b83e4254115a940b02985fef66afdf26bfefdccddfbe2f25650fc238758bac9ed132a290886daac1dfc82dc410475fb48d8cf51ab1b19d73d829b0cfc486ebfb3a89eb25393460833342fcd2092edffaf42d364b7d80fd766a1270ac3e8f5c706c97f8d90728a2839a8f2bfa3bc81939a540515ee8b6f4275c1be4ec2588f003e9474418d88830187c2ce192fd7c69c1907d0e73826c3f86390c0af6fbc19e6a0db23b87a09ecd4de039db42fcb862c3315cf4580d5345d32606bb0085509bca0d4a5eaa1f3b0a34432db80b51b372b3cc68a16d16edd004a70796d5b65c46f72f16383ac710b0105a0a9f4b1efec577d5c4af544e9cddbf942062e58be832605b62a9c89c818605ec5eb258ebe45ce66d99e1df8ea696d1ea07f1787d1346652c451651f25e1134e6cc24bd4557df3930d2db2550f98653001ed3a64ac328546bf26e19e7dfd77c354788d256fbd110b9c266dc66aea7e2ae0b7cbf43d883fb25c91d1b778e277812b660b5fbcff53511027ba6d54afbd88d1adb25a5c52d05da1438ce5b88f7f61917547932ebc310994832fc7588df225b1c393e22f16abb8017e1118e20736e7844a3fad04dea0fce59cb6d2f477904af2cd57babe35dc13c3964fb7b49be5b34f676fbcc1123ca7063f42fcbd9d2f9f31dd9dd09efc55e5dd647b395c73e84834380708e332436b329f726a0a7ed75830f110176b0e1d9d3f313e2d89a5d1e0e053fbf010e3e20fdf030799fc73c51cd2772ad06cfd94569f5b8a998cba9dde2b6b4e50ac35f7970e38b814e063c368407349b0d618635aca08298e56598879e02db373cd6ed499931c2c617346fd15a23d8a1a020f6b71cefd7744a8b42921d02253d8d84db053065ba311bf7f5af1401196a38b0999ec52fbe6ced3f7464ef2472f4bac6cfee49af2581c31e5034980eab6404e8aa8cadfa913a7492149b7bbb8dc6fe77616870c2090e32d7ddc7692d0fb3a73ab606111690b73c8959bc3af3755223e56d07e34c8e4d0b492b3bcb1b96798b13cc92b1638a3f885d83e9f30b14cfdae34a307c233c578190d65aad0f421ecd053dea61996f8adf5906cbb7dd5e7a87169580a1ce5328d3d0937b28", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000335fc711e25eec4bc7002806d9b00c9600000000000000000000000000000000cdab8c25e7ffe0274955ed025ec557bd01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db70000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } } diff --git a/circuits/benchmarks/results_insecure/report.md b/circuits/benchmarks/results_insecure/report.md index 6a6a9552f..83847f3b2 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-19 12:09:10 UTC +**Generated:** 2026-05-19 13:45:13 UTC **Git Branch:** `feat/1525` -**Git Commit:** `dafa8c81c69457b7d90df0d222642b342e1a902d` +**Git Commit:** `ab26bae432a591aa0345b8b7b64e069f28b26bc1` **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.12 | 25.15 | 15.88 | -| C1 | 57818 | 0.36 | 25.98 | 15.88 | -| C2a | 142625 | 0.75 | 24.68 | 15.88 | -| C2b | 198355 | 0.85 | 25.40 | 15.88 | -| C3a | 132633 | 0.80 | 24.97 | 15.88 | -| C3b | 132633 | 0.80 | 24.97 | 15.88 | -| C4a | 92515 | 0.56 | 26.81 | 15.88 | -| C4b | 92515 | 0.56 | 26.81 | 15.88 | -| C5 | 151717 | 0.83 | 25.21 | 15.88 | -| user_data_encryption | 53732 | 0.32 | 25.80 | 15.88 | -| C6 | 86927 | 0.52 | 25.12 | 15.88 | -| C7 | 104273 | 0.50 | 25.30 | 15.88 | +| C0 | 6847 | 0.12 | 25.86 | 15.88 | +| C1 | 57818 | 0.36 | 27.07 | 15.88 | +| C2a | 142625 | 0.82 | 27.33 | 15.88 | +| C2b | 198355 | 0.91 | 27.67 | 15.88 | +| C3a | 132633 | 0.85 | 27.21 | 15.88 | +| C3b | 132633 | 0.85 | 27.21 | 15.88 | +| C4a | 92515 | 0.53 | 27.01 | 15.88 | +| C4b | 92515 | 0.53 | 27.01 | 15.88 | +| C5 | 151717 | 0.85 | 27.10 | 15.88 | +| user_data_encryption | 53732 | 0.34 | 26.27 | 15.88 | +| C6 | 86927 | 0.54 | 28.16 | 15.88 | +| C7 | 104273 | 0.50 | 27.64 | 15.88 | ### Artifacts | Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | | -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 10.69 KB | 0.47 KB | 3042950 | 176268 | 3219218 | -| Π_user | 15.88 KB | 0.12 KB | 2973097 | 170320 | 3143417 | -| Π_dec | 10.69 KB | 3.47 KB | 3553770 | 187236 | 3741006 | +| Π_DKG | 10.69 KB | 0.47 KB | 3042828 | 176148 | 3218976 | +| Π_user | 15.88 KB | 0.12 KB | 2972965 | 170272 | 3143237 | +| Π_dec | 10.69 KB | 3.47 KB | 3553758 | 187236 | 3740994 | ### Role / Phase / Activity | Role | Phase | Activity | Prove time | Proof size | Bandwidth | | --------------- | ----- | -------------------------------- | ---------- | ---------- | --------- | -| Each ciphernode | P1 | one-time DKG participation | 295.26 s | 127.00 KB | 128.19 KB | -| Aggregator | P2 | combine folds + C5 | 0.83 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) | 78.08 s | 10.69 KB | 14.16 KB | +| Each ciphernode | P1 | one-time DKG participation | 310.65 s | 127.00 KB | 128.19 KB | +| Aggregator | P2 | combine folds + C5 | 0.85 s | 10.69 KB | 11.16 KB | +| User | P3 | per user input | 0.67 s | 15.88 KB | 16.00 KB | +| Each ciphernode | P4 | per computation output (C6) | 0.54 s | 15.88 KB | 16.00 KB | +| Aggregator | P4 | per computation output (C7+fold) | 82.11 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.03 | -| Committee Setup Completed | 20.21 | +| Setup completed | 3.06 | +| Committee Setup Completed | 20.24 | | Committee Finalization Complete | 0.01 | -| ThresholdShares -> PublicKeyAggregated | 295.26 | -| E3Request -> PublicKeyAggregated | 297.76 | -| Application CT Gen | 0.31 | +| ThresholdShares -> PublicKeyAggregated | 310.65 | +| E3Request -> PublicKeyAggregated | 313.31 | +| Application CT Gen | 0.32 | | Running FHE Application | 0.00 | -| Ciphertext published -> PlaintextAggregated | 78.08 | -| Entire Test | 399.41 | +| Ciphertext published -> PlaintextAggregated | 82.11 | +| Entire Test | 419.06 | ### 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.82 | -| CalculateThresholdDecryption | 0.56 | 1 | 0.56 | -| GenEsiSss | 0.12 | 3 | 0.37 | -| GenPkShareAndSkSss | 0.22 | 3 | 0.67 | -| ZkDecryptedSharesAggregation | 8.42 | 1 | 8.42 | -| ZkDecryptionAggregation | 48.00 | 1 | 48.00 | -| ZkDkgAggregation | 19.87 | 1 | 19.87 | -| ZkDkgShareDecryption | 1.44 | 6 | 8.64 | -| ZkNodeDkgFold | 60.32 | 3 | 180.97 | -| ZkPkAggregation | 2.12 | 1 | 2.12 | -| ZkPkBfv | 0.33 | 3 | 0.99 | -| ZkPkGeneration | 1.33 | 3 | 4.00 | -| ZkShareComputation | 2.65 | 6 | 15.90 | -| ZkShareEncryption | 2.47 | 24 | 59.29 | -| ZkThresholdShareDecryption | 6.07 | 3 | 18.20 | -| ZkVerifyShareDecryptionProofs | 0.09 | 3 | 0.28 | -| ZkVerifyShareProofs | 0.21 | 5 | 1.04 | - -Sum of tracked operation wall time: **371.47 s** (often much larger than end-to-end wall clock +| CalculateDecryptionKey | 0.11 | 3 | 0.34 | +| CalculateDecryptionShare | 0.62 | 3 | 1.87 | +| CalculateThresholdDecryption | 0.57 | 1 | 0.57 | +| GenEsiSss | 0.13 | 3 | 0.38 | +| GenPkShareAndSkSss | 0.23 | 3 | 0.69 | +| ZkDecryptedSharesAggregation | 8.60 | 1 | 8.60 | +| ZkDecryptionAggregation | 51.19 | 1 | 51.19 | +| ZkDkgAggregation | 21.08 | 1 | 21.08 | +| ZkDkgShareDecryption | 1.50 | 6 | 9.00 | +| ZkNodeDkgFold | 63.75 | 3 | 191.24 | +| ZkPkAggregation | 2.19 | 1 | 2.19 | +| ZkPkBfv | 0.34 | 3 | 1.03 | +| ZkPkGeneration | 1.38 | 3 | 4.15 | +| ZkShareComputation | 2.75 | 6 | 16.52 | +| ZkShareEncryption | 2.57 | 24 | 61.79 | +| ZkThresholdShareDecryption | 6.26 | 3 | 18.77 | +| ZkVerifyShareDecryptionProofs | 0.10 | 3 | 0.30 | +| ZkVerifyShareProofs | 0.23 | 5 | 1.14 | + +Sum of tracked operation wall time: **390.82 s** (often much larger than end-to-end wall clock because work runs in parallel). ## Raw circuit benchmark JSON (Nargo) diff --git a/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr b/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr index 1748cc627..dc6304f41 100644 --- a/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr +++ b/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr @@ -10,9 +10,11 @@ //! `committee_hash_hi` / `committee_hash_lo` bind the proof to the full on-chain committee ordering. 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::T; use lib::math::commitments::compute_vk_hash; +use lib::math::committee_hash::committee_hash_limbs; /// Must match `c6_fold::C6_FOLD_PUBLIC_LEN`: pub params + flattened `(out_sk, out_esm, out_ct, out_d)`. pub global C6_FOLD_PUBLIC_LEN: u32 = 4 + (4 * (T + 1)); @@ -43,9 +45,14 @@ fn main( c7_public: [Field; C7_PUBLIC_LEN], c6_fold_key_hash: pub Field, c7_key_hash: pub Field, + committee_members: [Field; H], committee_hash_hi: pub Field, committee_hash_lo: pub Field, ) -> pub (Field, [Field; T + 1], [Field; T + 1], [Field; T + 1], [Field; MAX_MSG_NON_ZERO_COEFFS]) { + let (expected_hi, expected_lo) = committee_hash_limbs(committee_members); + assert(expected_hi == committee_hash_hi); + assert(expected_lo == committee_hash_lo); + verify_honk_proof_non_zk(c6_fold_vk, c6_fold_proof, c6_fold_public, c6_fold_key_hash); verify_honk_proof_non_zk(c7_vk, c7_proof, c7_public, c7_key_hash); diff --git a/circuits/bin/recursive_aggregation/dkg_aggregator/src/main.nr b/circuits/bin/recursive_aggregation/dkg_aggregator/src/main.nr index ab7d14000..4bbd03ac6 100644 --- a/circuits/bin/recursive_aggregation/dkg_aggregator/src/main.nr +++ b/circuits/bin/recursive_aggregation/dkg_aggregator/src/main.nr @@ -13,6 +13,7 @@ use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_hon use lib::configs::default::dkg::L_THRESHOLD; use lib::configs::default::{H, N_PARTIES}; use lib::math::commitments::compute_vk_hash; +use lib::math::committee_hash::committee_hash_limbs; /// Must match `node_fold::NODE_FOLD_PUBLIC_LEN`. pub global NODE_FOLD_PUBLIC_LEN: u32 = 11 + N_PARTIES + (2 * (N_PARTIES + H) * L_THRESHOLD); @@ -52,9 +53,14 @@ fn main( nodes_fold_key_hash: pub Field, c5_key_hash: pub Field, party_ids: pub [Field; H], + committee_members: [Field; H], committee_hash_hi: pub Field, committee_hash_lo: pub Field, ) -> pub (Field, [Field; H], [Field; H], Field) { + let (expected_hi, expected_lo) = committee_hash_limbs(committee_members); + assert(expected_hi == committee_hash_hi); + assert(expected_lo == committee_hash_lo); + verify_honk_proof_non_zk( nodes_fold_vk, nodes_fold_proof, diff --git a/circuits/lib/src/configs/default/mod.nr b/circuits/lib/src/configs/default/mod.nr index 70e6219e5..f29df7cd8 100644 --- a/circuits/lib/src/configs/default/mod.nr +++ b/circuits/lib/src/configs/default/mod.nr @@ -4,8 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. // -// Only place where we change param-set: re-export insecure or production. -// (in future any other param-set). All circuits use lib::configs::default::*. +// Auto-generated by build-circuits.ts for preset: insecure-512 pub use super::committee::micro::{H, N_PARTIES, T}; pub use super::insecure::dkg; diff --git a/circuits/lib/src/math/committee_hash.nr b/circuits/lib/src/math/committee_hash.nr new file mode 100644 index 000000000..87d35e799 --- /dev/null +++ b/circuits/lib/src/math/committee_hash.nr @@ -0,0 +1,55 @@ +// 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. + +//! `keccak256(abi.encodePacked(addresses))` split into hi/lo 128-bit limbs. +//! Must match `CommitteeHashLib.sol` and `e3_utils::committee_hash`. + +use crate::configs::committee::micro::H; +use keccak256::keccak256; + +global ADDRESS_BYTES: u32 = 20; +global COMMITTEE_PACKED_BYTES: u32 = H * ADDRESS_BYTES; + +fn field_to_address_bytes(member: Field) -> [u8; 20] { + let be: [u8; 32] = member.to_be_bytes(); + let mut out = [0u8; 20]; + for i in 0..20 { + out[i] = be[12 + i]; + } + out +} + +fn bytes32_to_field(bytes: [u8; 32]) -> Field { + let mut value: Field = 0; + for i in 0..32 { + value = value * 256 + bytes[i] as Field; + } + value +} + +/// Hash ordered committee members and return `(committee_hash_hi, committee_hash_lo)` public limbs. +pub fn committee_hash_limbs(members: [Field; H]) -> (Field, Field) { + let mut packed = [0u8; COMMITTEE_PACKED_BYTES]; + for i in 0..H { + let addr = field_to_address_bytes(members[i]); + for j in 0..20 { + packed[i * 20 + j] = addr[j]; + } + } + + let hash_bytes = keccak256(packed, COMMITTEE_PACKED_BYTES); + + // Match `CommitteeHashLib.hi` / `lo`: each limb is a bytes32 with its 128 bits + // right-aligned (same as `bytes32(uint256(hash) >> 128)` and `& _LO_MASK`). + let mut hi_bytes = [0u8; 32]; + let mut lo_bytes = [0u8; 32]; + for i in 0..16 { + hi_bytes[16 + i] = hash_bytes[i]; + lo_bytes[16 + i] = hash_bytes[16 + i]; + } + + (bytes32_to_field(hi_bytes), bytes32_to_field(lo_bytes)) +} diff --git a/circuits/lib/src/math/mod.nr b/circuits/lib/src/math/mod.nr index b03c54f7f..c0eb2cc3a 100644 --- a/circuits/lib/src/math/mod.nr +++ b/circuits/lib/src/math/mod.nr @@ -9,3 +9,4 @@ pub mod safe; pub mod helpers; pub mod modulo; pub mod commitments; +pub mod committee_hash; diff --git a/crates/utils/src/committee_hash.rs b/crates/utils/src/committee_hash.rs index e8d750ac2..148e3414b 100644 --- a/crates/utils/src/committee_hash.rs +++ b/crates/utils/src/committee_hash.rs @@ -7,7 +7,7 @@ //! Canonical committee hash for DKG / decryption aggregator proofs. //! Must match `CommitteeHashLib.sol` (`keccak256(abi.encodePacked(addresses))`). -use alloy::primitives::{keccak256, Address, B256, U256}; +use alloy::primitives::{keccak256, Address, B256}; /// Hi/lo limbs of `keccak256(abi.encodePacked(addresses))` for Noir public inputs. #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -26,12 +26,16 @@ pub fn hash_committee_addresses(addresses: &[Address]) -> B256 { } /// Split a committee hash into 128-bit limbs for BN254 public inputs. +/// Each limb is a bytes32 with its 128 bits right-aligned, matching `CommitteeHashLib`. pub fn split_committee_hash(hash: B256) -> CommitteeHashLimbs { - let value = U256::from_be_bytes(hash.0); - let hi = B256::from(value >> 128); - let lo_mask = (U256::from(1) << 128) - U256::from(1); - let lo = B256::from(value & lo_mask); - CommitteeHashLimbs { hi, lo } + let mut hi = [0u8; 32]; + hi[16..].copy_from_slice(&hash.0[..16]); + let mut lo = [0u8; 32]; + lo[16..].copy_from_slice(&hash.0[16..]); + CommitteeHashLimbs { + hi: B256::from(hi), + lo: B256::from(lo), + } } /// Hash and split in one step. @@ -71,4 +75,24 @@ mod tests { assert_ne!(limbs.hi, B256::ZERO); assert_ne!(limbs.lo, B256::ZERO); } + + /// Limb bytes32 layout must match `CommitteeHashLib.hi` / `lo`. + #[test] + fn split_limbs_match_solidity_bytes32_layout() { + let nodes = vec![ + address!("0x0000000000000000000000000000000000000001"), + address!("0x0000000000000000000000000000000000000002"), + address!("0x0000000000000000000000000000000000000003"), + ]; + let hash = hash_committee_addresses(&nodes); + let limbs = split_committee_hash(hash); + + let mut expected_hi = [0u8; 32]; + expected_hi[16..].copy_from_slice(&hash.0[..16]); + assert_eq!(limbs.hi.0, expected_hi); + + let mut expected_lo = [0u8; 32]; + expected_lo[16..].copy_from_slice(&hash.0[16..]); + assert_eq!(limbs.lo.0, expected_lo); + } } diff --git a/crates/zk-prover/src/circuits/aggregation/helpers.rs b/crates/zk-prover/src/circuits/aggregation/helpers.rs index e8e4be89c..1bba328e8 100644 --- a/crates/zk-prover/src/circuits/aggregation/helpers.rs +++ b/crates/zk-prover/src/circuits/aggregation/helpers.rs @@ -42,6 +42,16 @@ pub fn u64_to_field_hex(value: u64) -> String { format!("0x{}", hex::encode(bytes)) } +/// Encode an EVM address as a 32-byte field hex string (20-byte address right-aligned). +pub fn address_to_field_hex(address: &str) -> Result { + let parsed: alloy::primitives::Address = address + .parse() + .map_err(|e| ZkError::InvalidInput(format!("invalid address {address}: {e}")))?; + let mut bytes = [0u8; 32]; + bytes[12..].copy_from_slice(parsed.as_slice()); + Ok(format!("0x{}", hex::encode(bytes))) +} + /// Extract a single 32-byte public input/output by name. `kind` must be `"input"` or `"output"`. pub fn extract_single_field( proof: &Proof, 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 5ef123668..cc10eb960 100644 --- a/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs +++ b/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs @@ -9,7 +9,7 @@ use crate::circuits::aggregation::c3_accumulator::generate_sequential_c3_fold; use crate::circuits::aggregation::c6_accumulator::generate_sequential_c6_fold; -use crate::circuits::aggregation::helpers::u64_to_field_hex; +use crate::circuits::aggregation::helpers::{address_to_field_hex, u64_to_field_hex}; use crate::circuits::aggregation::nodes_fold_accumulator::generate_sequential_nodes_fold; use crate::circuits::utils::{bytes_to_field_strings, inputs_json_to_input_map}; use crate::circuits::vk; @@ -310,6 +310,7 @@ struct DkgAggregatorWitness { nodes_fold_key_hash: String, c5_key_hash: String, party_ids: Vec, + committee_members: Vec, committee_hash_hi: String, committee_hash_lo: String, } @@ -363,6 +364,12 @@ pub fn prove_dkg_aggregation( e3_utils::committee_hash::committee_hash_field_hex(input.committee_addresses) .map_err(|e| ZkError::InvalidInput(e.to_string()))?; + 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)?, @@ -373,6 +380,7 @@ pub fn prove_dkg_aggregation( nodes_fold_key_hash: nodes_fold_vk.key_hash.clone(), c5_key_hash: c5_vk.key_hash.clone(), party_ids: party_id_fields, + committee_members, committee_hash_hi, committee_hash_lo, }; @@ -413,6 +421,7 @@ struct DecryptionAggregatorWitness { c7_public: Vec, c6_fold_key_hash: String, c7_key_hash: String, + committee_members: Vec, committee_hash_hi: String, committee_hash_lo: String, } @@ -447,6 +456,11 @@ pub fn prove_decryption_aggregation_jobs( e3_utils::committee_hash::committee_hash_field_hex(committee_addresses) .map_err(|e| ZkError::InvalidInput(e.to_string()))?; + 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( @@ -467,6 +481,7 @@ pub fn prove_decryption_aggregation_jobs( c7_public: proof_public_field_strings(job.c7_proof)?, c6_fold_key_hash: c6_fold_vk.key_hash.clone(), c7_key_hash: c7_vk.key_hash.clone(), + committee_members: committee_members.clone(), committee_hash_hi: committee_hash_hi.clone(), committee_hash_lo: committee_hash_lo.clone(), }; diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol index 2fe91884c..247725dbe 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.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 = 127; -uint256 constant VK_HASH = 0x02734bf64581e3f98e47575b06750fc45ec81a34079564e4a7326a3d547d2b90; +uint256 constant VK_HASH = 0x23be1dd720adbe93a20641346295fa6b91a060ce9532c5274e91a438434d4fa0; library HonkVerificationKey { function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { Honk.VerificationKey memory vk = Honk.VerificationKey({ @@ -16,116 +16,116 @@ library HonkVerificationKey { logCircuitSize: uint256(21), publicInputsSize: uint256(127), ql: Honk.G1Point({ - x: uint256(0x22f1675e1cbb2fcd4d4b892e591f16c3201a876c34d711e86159aa593f8957df), - y: uint256(0x06b38fe5bca0f4814e532d26345b64ddd5fdbc72c4171889f9c4cad61139787d) + x: uint256(0x05d32cc409554430668b52564a0dd3e7c5e26b2e970ab80e13285a2e02df18dc), + y: uint256(0x185e84c9e227fa12f2f951b68b41c73ccda11518663b45d46a8d8ce054f7d363) }), qr: Honk.G1Point({ - x: uint256(0x0beaa5627f856b98b6c9300fd93f16d9de0a364e8319e818171fd4e0c0a5c91e), - y: uint256(0x2d884ebc1127945f836920004f1499ed01afd2fc89691f0f96f7a928a9998fc6) + x: uint256(0x2b453c47255c6d5225dc081251a4a781157691c23d193ff46de4e0f41fe1b3e9), + y: uint256(0x18deab5160dcd08f2a7bc2c767fed54c056a62565af8291196916797400802c7) }), qo: Honk.G1Point({ - x: uint256(0x02e50bcff38f3c94d748e3ce7b80bb26019c24fd8c64c55e3469cc3ddf138b8e), - y: uint256(0x248ca1123436cf2e53bdd9ed5e3e594213a59785ae39c6218eb5e28aedf552dc) + x: uint256(0x2767043cf43b790a3a17ba0ac68a8316c631a2220f95e6ae9f5fdb6c7dccb11b), + y: uint256(0x26617bb06961db2a4c4a87f51155ef06e3e820f644af379137703f484ac8d0c5) }), q4: Honk.G1Point({ - x: uint256(0x2be0a336b2241e8cc572c843dfe5de1bb1d26f2ffb244461939573c3910ae7e4), - y: uint256(0x18195c8e933be2a9c081ff5fcad1f3d362ed2408ca5bd683ecb76f831937bf8c) + x: uint256(0x2128584d8d874764f2d7926d78d7f8f5b7dc9eb8b5121f3fa3170235fd8fe618), + y: uint256(0x1d564fa1501c0c8a198c5b4597bf87f1a0c6d6331359f99b05b67ce46d278ea0) }), qm: Honk.G1Point({ - x: uint256(0x2aaee69977fc70b11f35a9417bf0efc1b8fc3ed5ed1c6a14cb1e2f587b01618d), - y: uint256(0x167831b0a4fb0627b623ab3b229eac297d78d9ffb020801e31e4ba9ed288f00f) + x: uint256(0x17fcefb84130f269e76bd0f308fb32344474986c718e43ead5ea21dd3cb10334), + y: uint256(0x22b522a965b4d856e21f8eb12658b17da267d65fe1e0772ce97f26fca9879e37) }), qc: Honk.G1Point({ - x: uint256(0x0c8357f6962ce8370d3f5965652b63f972d8eed7766a7fefba8c443b69edab68), - y: uint256(0x238822fb432ce7bed5edcee885a9a75beec94f64602b27518e5bb66da54b4157) + x: uint256(0x2517978cb349ef864c272efe890ffdce75ed7790902c37e7087cf9790e7f1cdc), + y: uint256(0x042f1330f591b1345d8675358b31393c21b7416567bbb4db887a0f5c877d2a68) }), qLookup: Honk.G1Point({ - x: uint256(0x29e47a139596dcf5450c2de10d5d121d883389ab7c0e930c1ad2f20283098fc1), - y: uint256(0x2548e6b14779657467f37fc862ec803097b1e328abf936d9b93438db371b5aff) + x: uint256(0x13143d24a192079453fc93ca72b6be61609f4d042621b3d3973bfa341bb8a424), + y: uint256(0x2dee6429bc80fc94550da46393279fc6e08def2542d28bbc312a082f31e56409) }), qArith: Honk.G1Point({ - x: uint256(0x0c6962ed640803bfd254fe60f15bfbd3619695b639eaa50f8ad9bca8f26c1c67), - y: uint256(0x2a81c4ea9b5f20a9b7966ef5a0f095f1bf0fc03400c4328f06da00547e5ede6b) + x: uint256(0x23de7f17e2a2a904521bcb9ae1173eae1ac10fc5d4d1a2389cc51758fd89d4b3), + y: uint256(0x0363de80dbae03838b654e9ee7ddfe17e891983d01042db26b0afd145d77e692) }), qDeltaRange: Honk.G1Point({ - x: uint256(0x12740b73a60d59b99352868b5ff15474d8d40676659ede6a613c2869926496e9), - y: uint256(0x2e92d9a28c1a2b6f6d5802abcfc28eb69fbf0f0fce55a7e60046d8dfc4fdf260) + x: uint256(0x0bcde00ecf5d0958bdd6224d6a54719e72fd904eb163dcf5ea324daafacf2b7f), + y: uint256(0x2e7adff5b94dd989e0d9dca6eb419889621ec14f5d570543168e4e42409c83c6) }), qElliptic: Honk.G1Point({ - x: uint256(0x300990501116cc8c9f5950d44ce76927d8443b777155a61e3cf6bb2e5060ca9d), - y: uint256(0x269ae09b2eee7365a0aed387b13e4ca9916e3ce2c75967672180345d33cb70b7) + x: uint256(0x1b0f171ae66227192763b958126f202bbe0bf894455dc70542a65b6ce49672ab), + y: uint256(0x080e380b2fbc0db5614dcd7903bb1837b5814f1ea6d315d547dc63145f12fd9f) }), qMemory: Honk.G1Point({ - x: uint256(0x1c42c01418da4f6b73611ae563df3215901ab635ad3c850db84e1a9b13ab9235), - y: uint256(0x2d23fcba8d43a2ecc03a6af88af95e13ea8b3cca01165ee491dbb23a4ea4ffa5) + x: uint256(0x2508e83664595b6817e4b2dc994c7f5fcd07b6e0d739f06df3aaef2aabb4a354), + y: uint256(0x1ceac93c1a2560a969a76c9203eac99036b3f50471941f7cb9f4be3381de5ab7) }), qNnf: Honk.G1Point({ - x: uint256(0x1dfa93f3351f60495e8df8d87903805817c44f87a73b945c81a438be0facc481), - y: uint256(0x0e0341343e527ffedee24e4028bc14b8501adc68d0fce00c17ac11120d2df513) + x: uint256(0x0e154fcc6ac38a216d26f2a5eb005c7a17d7158ccc496355c736b7ee7085296a), + y: uint256(0x11a0c91986352495d1b391c415d947577348f36697aa570238d2b19b50353902) }), qPoseidon2External: Honk.G1Point({ - x: uint256(0x048462f577ba1216245079d12c9d55487cf3b330eed20a66fe7db67d447fd72f), - y: uint256(0x1f06371d9dedf997f6dc1b5508b5fa620a05c3261e833bb43b88e9b6b6f5a979) + x: uint256(0x1e1f6dce6c7b8406de819fe1d10524ec3161ec0885eb3695a86a5107e1d83589), + y: uint256(0x1fe55e0e37c825072934535d7f33de9196a1b4c8b71a72a958761e4a1f55531e) }), qPoseidon2Internal: Honk.G1Point({ - x: uint256(0x1189e97ee885b8a608dca70a417cfd3d339455452c9798752febedd0582f4977), - y: uint256(0x13860b3c5483cf0abe4d5ec8f382e455b20aa391dc315862abb5b49228bf7083) + x: uint256(0x0e5d05348574583cfb629d78f4ca745d482d5c7399082b33cfdb7edf59fc6ab5), + y: uint256(0x1f1c05396894b1b80d8162391a7a1c24b2a5dbd1294cb683434d8f6345f379b9) }), s1: Honk.G1Point({ - x: uint256(0x11b5d23e9c3c361c011754470be1828b314b3bc41dd92723edd773e0dc721f87), - y: uint256(0x12d77c6c17a5aa9ab610abb810fecdd5d3a20370c04f9a04969e1c7b51157f90) + x: uint256(0x220924e2d368b51ce6265fb3077f544eec065123aa48ead8cc9ea8d8138a1d80), + y: uint256(0x0a77f2cecf7b6c95615d4cb84f44a68196df673bcdfa9045fa4430991de7459d) }), s2: Honk.G1Point({ - x: uint256(0x0ccfd83fc13c1e477918617d55ec0e302167fc9e35ac5e7933267dbf6b04225d), - y: uint256(0x0345818c0b610d75f563262999377e05bed55ebaff83f0eb7a9525394a67436d) + x: uint256(0x01301eda90b3c69ca965b47d874af1a63a6a4db818b21435579d10bd7866c253), + y: uint256(0x2ebd7190a5695c302a27c81884302282dbd34a86d9f3c1b6eeb28dfcfba69fd1) }), s3: Honk.G1Point({ - x: uint256(0x18c9ccbc44c932d6d698e781e285674b7c169c4435df7bf6a41b56aebc8104f5), - y: uint256(0x145974be15fb78b2d78c93a97669c4501670649ecaf20a58a183b4199301cba7) + x: uint256(0x11c240beac486e3d294d585995394c3e895f8cabc94049de498aac199237770b), + y: uint256(0x269695fc9c8c1caa5ae553b9c0ccfb5aadb257f8bf5bfad76bb4d304acc7cac9) }), s4: Honk.G1Point({ - x: uint256(0x1985589ee73cb2f44b4dbb33e470a1c996c5d06ed7baf10ddbfd507acb8684ae), - y: uint256(0x2d07e474573859fda8dc083c17728f59cddb11cd0e29be5651705f5dbddb669a) + x: uint256(0x245003058192b706ca21dd546a96ee58afd8679394d9518da3344ca4b21be2ad), + y: uint256(0x29a048078639061c011b8b6e3978dc2f03ed0f2bc24208c8e36fc8434ed201b6) }), t1: Honk.G1Point({ - x: uint256(0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26), - y: uint256(0x0015b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f) + x: uint256(0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c), + y: uint256(0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579) }), t2: Honk.G1Point({ - x: uint256(0x1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e), - y: uint256(0x305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d19) + x: uint256(0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887), + y: uint256(0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8) }), t3: Honk.G1Point({ - x: uint256(0x061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d), - y: uint256(0x1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e) + x: uint256(0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f), + y: uint256(0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7) }), t4: Honk.G1Point({ - x: uint256(0x043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce), - y: uint256(0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854) + x: uint256(0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6), + y: uint256(0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea) }), id1: Honk.G1Point({ - x: uint256(0x0ce4b985e3d494ea5e27349d6bfd5a93f2da5df7b83a4e95247e187be4b04234), - y: uint256(0x2b14f7ab190415ec456c7d10382e6e5a573d65a180bcc698638d6c38f0c58198) + x: uint256(0x01f63ed4a2629fd4c6cc58c73374226a59212d60bf9f9ee7d71fb3b1040f4442), + y: uint256(0x1376d6bcc1ce592b45c346209c9fd2ddbf5f3470fc261c1f48568655638bf747) }), id2: Honk.G1Point({ - x: uint256(0x2cedaca0e3610529837cd83d76d7a67e6f037068b22b7f0e21c5c0fd2bb16587), - y: uint256(0x16f7b9a3f2b5c36530840e8b7046b3faf0273017b736d7d4e9c86a56e22c997f) + x: uint256(0x1830b0efd90ca2e4fea4529f59a1ddb537d8b564810ad2af7837e8e8a5b9e927), + y: uint256(0x232234bd7890b992269353e2ebb7872fe31dc86b650e0f560a06da9dbc8c3d90) }), id3: Honk.G1Point({ - x: uint256(0x195328bc8631c53b83e65739b932c063ab3f2861c3f25e186e693b96ec32ec6b), - y: uint256(0x1c2e18885411c408222af35cf31ebc1e7e3c82428cd8b4111b418a15b47bf778) + x: uint256(0x0c74b589240ea2ade23d38fff23b2658110d1cc3f4d201c7d9c65808e36771d9), + y: uint256(0x162c019fc641e6d049308d7014c1acd4a0cd922871ad697fbdc394b7129e4f45) }), id4: Honk.G1Point({ - x: uint256(0x0d629ffdaf3c341c70dc25efc19ea32364f2718a9f2e7d3001f2582bc7770eba), - y: uint256(0x218e10d582f130e792dac3a6cd0ca6d339566ea722f23aa8c7e505c444a8b426) + x: uint256(0x21e71e95b4ee9bb8a3122acd478de72a2261de0c897d040edc077aab05f3fdb7), + y: uint256(0x275e27d4a4d9c0c15d998eed6b60a44836146cd9816b1691969ecc8172e7b3a4) }), lagrangeFirst: Honk.G1Point({ x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) }), lagrangeLast: Honk.G1Point({ - x: uint256(0x194a661bffd8b96eb251f3e4fd4c4ca2099d006d6047d379e36ac4f48075ca84), - y: uint256(0x05cdeca24a13e225c1cca9954e1e3d0826037e148f4c4cabe583813ea42f3318) + x: uint256(0x10be2fc7531b16bcf869314c6e9ad92e822f3ca9cd1f2fc4badd9b68e3df3c60), + y: uint256(0x0d9bbf7771aa706695d42688be4788f4d722e5ab38d27acf721626fa295bfd6d) }) }); return vk; diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol index ebd900e7a..c1c931a73 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 = 0x0eae6bb9df6e16c4fee3caaeb291d0ff369a68d391139167cc1b388f6e46eead; +uint256 constant VK_HASH = 0x2937fa911eeb39d6d5a928094331339f60b0e50a6b7d0dd2ae546d8209fc933f; library HonkVerificationKey { function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { Honk.VerificationKey memory vk = Honk.VerificationKey({ @@ -16,116 +16,116 @@ library HonkVerificationKey { logCircuitSize: uint256(21), publicInputsSize: uint256(31), ql: Honk.G1Point({ - x: uint256(0x004818a3ec8aefdad2bd7a9cf7a167e7218c4a8d550f11509d56127827364528), - y: uint256(0x226a9aefe34f960dcabc1f74380c4e5ab96715f867a46af851a72937f9257c6d) + x: uint256(0x26ad9ec73647e6cbac68fb3eecb522376fdd482a519c7e6a14271e668bc1ddaf), + y: uint256(0x2360a0b5ca8b242fe62ba4b68f647f4dcb4441f2c46c184364514a78d273acd9) }), qr: Honk.G1Point({ - x: uint256(0x2c1f1ee92a0a396190f7c7109f90c8117b7fb200c2dcfd6e063f64a3995dc17e), - y: uint256(0x266b0f31b457ded74a809dd78b8ac3999f829549fbd8bdcbe627625a8a0e38cb) + x: uint256(0x226a163c49844e7a6cc7287edcf1b26c3597e26de9c17bf2961e210bcfadf895), + y: uint256(0x08c9455e7c33f67894eb5102d6b0e8dbb70fac2337edb46cf442e89896a1d02d) }), qo: Honk.G1Point({ - x: uint256(0x3059f7ff6c9d3f4706fdf095ef295401efb164e188240166de6210d840e16b63), - y: uint256(0x1887de51df5e8e867580cc80d4b1bbc958d00a66dfdfa83ab792775603241462) + x: uint256(0x2b753cc3a52c28ac2e053efc2e6e95e83cbd8353eb2f4a784cf783681f701400), + y: uint256(0x24e380eed26b1da529be4a2666ad42e6668f4be2caec72213b757834da93502c) }), q4: Honk.G1Point({ - x: uint256(0x22fff8145b11f585103c50b3aba419d37129a9a542541da525d945f04e8e73ef), - y: uint256(0x0167b0ed7e0fc999068b12769ad5e6da87501e8813b9aa23e230af631ffe0b97) + x: uint256(0x1a2224b4986b1ff6f1c1949345d84863f0bf504727d65eb04e58073c5a890047), + y: uint256(0x2715c720aeaf5b213505cf7902e376f0f1d4d22319e8368fc7303ac4e9a12a94) }), qm: Honk.G1Point({ - x: uint256(0x11655f3723457a2232e8fb19ee22264a6e684b935889cbbb9d6fd4337e2f7ccc), - y: uint256(0x255b49a883f5f1e1797b4f4a3d421e2d9fe84f25f4f32081692cc348ac681984) + x: uint256(0x25b47f2902609f550548d0a2c27c5552d9b51dc112e8e9b263ec80688e33635c), + y: uint256(0x1ccda8f90b56da93afacb2fe218966e07654a5b0d136a37ff11a9188b4d8bfb5) }), qc: Honk.G1Point({ - x: uint256(0x2a99ac21cb8147739d3aae3d2dc5f3350845d91936f96a5a8581eb2267dfb9df), - y: uint256(0x2995cafda58fefc6d696193a378b27e0ca6efc771efbbabf13c147efa2c8d724) + x: uint256(0x23ea11b593f242c992a3de9b9c07d9954cd851d32fec4469c5aa71a814eda310), + y: uint256(0x0b6827cfc9288cc0d66de88351195cdab6fdcd2ae3abd20590eefe3d608ca1e2) }), qLookup: Honk.G1Point({ - x: uint256(0x116151016b4fea676c06d9d297159098031c1b2d5c0ae18d5e5d874105b26eae), - y: uint256(0x0610cfeee3e9dbc93565c2b5ca7cbfb4a301b32b4903f16cc95bdd4a8d30275d) + x: uint256(0x182070aa2b03de9e8b4cada6a0760a0c6c72852783e7db97b4cd91281c03d3ea), + y: uint256(0x08c55ecc2b52f5393505462ae16474727d81f865b293adee900644436146a8e1) }), qArith: Honk.G1Point({ - x: uint256(0x18da344749ae9ab3a9c9599a6868ae7a85f9b0dc440a7531866fbdaa89ff5adb), - y: uint256(0x1b7d987ca28c4b07ab06db9e914e4fd2084e435f5cd8f2e4aca2efa90364d471) + x: uint256(0x117dc34d0c135ba99861d8eb4b0ba9e2454b11353971f8f754147edeee218f25), + y: uint256(0x1a52d059eea878acde805797fed889677b7504c05e08674d9ce8b7ed57893699) }), qDeltaRange: Honk.G1Point({ - x: uint256(0x1a2698210b8f31e5028eff667dcdb0a4d9459a40dd77fc93056716f062091a2c), - y: uint256(0x011c9674c7b0572beff21164795ae872433f13640e9374ae17637dd96abd9473) + x: uint256(0x199f74af36a62f790433377dfa9953d9d79fe5f3f843a257ebdfbec208aac0eb), + y: uint256(0x1729371270b5dc935d448c628bf3d9a84162def09ea5c3c991b18a84270ea98f) }), qElliptic: Honk.G1Point({ - x: uint256(0x165c75e2af27a39cbce9b53443b3e4d90126754104bcdbb319628fdd4c7a5996), - y: uint256(0x0bdf3b93eb3c0e3f949eb68c030f6c6be52d0700c512b3eb6f53908a6594b1c7) + x: uint256(0x0ff5ebe74c64d32e3e6a38e021b5aa0c7ccd683325165b0c7ba3fd40c06f3a8d), + y: uint256(0x2792de8ab62b7a8ac9433df3b9aa53506a6aa0d8bd30faa62b73ecde7d3e1ac7) }), qMemory: Honk.G1Point({ - x: uint256(0x0112ecf0acc382b4c304965d1934f5d193b073e29ae6447ce3d89909d4c031fb), - y: uint256(0x051a6fefd0adefb4141e57a764384de39c6bc52aa07d781d9f1fe4cb1af6fabb) + x: uint256(0x27112e3ba014810ee6ad32c37b61f5b89f7e25aac7d1ff50ef65004f2404c6e4), + y: uint256(0x2a994019050ce0aace7a030a29216e89a0dc832c2ec855d2044e996f74b065e8) }), qNnf: Honk.G1Point({ - x: uint256(0x1ab0c425506a1d5f665a1c662c25b93bc032de5a608791f5ed59e71594c72888), - y: uint256(0x18330c3505b1a78233682574717de5f260b27125774dd2ab8a3a16efcefecbd9) + x: uint256(0x192328adab0d1f56b4e1028bf449af9cc0d77f53b9c1fca4a5f2f6148ebece7f), + y: uint256(0x0808887e1f287af5bf7c7d8602d014e3419155a04b3070a1ffc1cc0cbcd50e2d) }), qPoseidon2External: Honk.G1Point({ - x: uint256(0x258797782e8223ae6fc8b349f55deac0b3b7026c169021f517c3370560e856af), - y: uint256(0x2b88cb88b7382eff4bb31a164dd13bd4c9a39ede79d96ee07fa226f4e575896e) + x: uint256(0x00e3022b8e21c0f5f462a5c13224d7257b6a3ed0b9d7bd734b6c1125abf633b0), + y: uint256(0x094c5338f79be9ea69797ff7b4a15d9aa918f4e50e6a86fb86a95ed8e22da852) }), qPoseidon2Internal: Honk.G1Point({ - x: uint256(0x18440eeacaaa55526950f7290505f8a1ff41effe471ff489d402452afe39e291), - y: uint256(0x14a9e90338b2227b53959cdc9602fb225b9a92f1574d6f002c5558af6090bc9e) + x: uint256(0x0798a34a77c41d91638fbca51b0057fb480898be2b7e5ae82bdf54a3cfb2f811), + y: uint256(0x24e471b3f0ddc31e775dffa48334e7c6e77f50bfce60d27005b1d280c99ba779) }), s1: Honk.G1Point({ - x: uint256(0x0fae8ce29a99bf0d69a8b4c1d671f628fee07a6cf898ef8cec6164072d3bb76d), - y: uint256(0x038120c21ec79a771366e53e851e8d8a451057808f774168b8d35530b92e9926) + x: uint256(0x155ac8307b2528efe8cf165ef29382bea17a6d4cfb7b21890315651cf8718b30), + y: uint256(0x2bb8ca70fbf5a1948582af7371e4d4143238ebfd32f1159c23193dbd2b22a03c) }), s2: Honk.G1Point({ - x: uint256(0x294c1a2fcf929a7c00883e44ec8ce8689214777ba05b16b7e7709dc36dcf7b91), - y: uint256(0x2b5fe1b0b84f8cb46c6a6b6db3d17ffc1fc9c0d4c147b153ea0a93571b5a7aa1) + x: uint256(0x19cbadda70765a860e9f7c73ecab703a2c2ddf2d2407d33a8da8173701b64d9d), + y: uint256(0x0d7029ec77a89543facb74408b4536a5e1ea600dc8d0f31aee86541e81d7b930) }), s3: Honk.G1Point({ - x: uint256(0x2fc1413ef17fe3f486e5854b33fbdd1c4a9422807adee945820ad32e061ba030), - y: uint256(0x25b2b2a575527f222127d271093adb3f3a8e09bf75a3fbf379566b7cbfa6271c) + x: uint256(0x275603bf2ba273a074f0791d33ee3f069449dfa2a89a7c69734d2f8fc40149ea), + y: uint256(0x1db1f55c697e147f29ec0c1aa54400ba4c44277d09d19ca495efa22c11fd4873) }), s4: Honk.G1Point({ - x: uint256(0x25ad7067fd41410ff3e57cc9b59a4f2f9bdaa7a180d756a97b33aa2887993d3b), - y: uint256(0x0d5d250decc2b796928740ddf673dd8371a79d695a910d9a83b30f4909cc73bf) + x: uint256(0x00e93184e1e95febe1cf63bc56e1d5fb1c7ec4dee64066e43ccecfa24e9aedab), + y: uint256(0x13e68e8e14a84d3477825b856ba254d0af40cc58fe21f276aa95511f4906563f) }), t1: Honk.G1Point({ - x: uint256(0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26), - y: uint256(0x0015b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f) + x: uint256(0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c), + y: uint256(0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579) }), t2: Honk.G1Point({ - x: uint256(0x1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e), - y: uint256(0x305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d19) + x: uint256(0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887), + y: uint256(0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8) }), t3: Honk.G1Point({ - x: uint256(0x061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d), - y: uint256(0x1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e) + x: uint256(0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f), + y: uint256(0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7) }), t4: Honk.G1Point({ - x: uint256(0x043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce), - y: uint256(0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854) + x: uint256(0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6), + y: uint256(0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea) }), id1: Honk.G1Point({ - x: uint256(0x110eb94d62692dc870fde8230d5fe9ce0830c1029e8892b09b754cbdbd86d9ef), - y: uint256(0x1811be5b1b9970e033e306ff7fd1de103bb8dca9524be14c57401d2aa631d861) + x: uint256(0x12280b9608c56c0fc84d5d14ea31e2ed4e2e9100c22b3f87216bee7bcb78fcb7), + y: uint256(0x145a43cd663f4f88784979dbedc733da43611d13a3ce638923fb5bb4f5f7e89b) }), id2: Honk.G1Point({ - x: uint256(0x1aaf61c2817d932c47dd3aeefc6d231e39c018f4d85306814942da2a16438bd5), - y: uint256(0x0256007a2d30623b14b7b21592af23eedbdaaebbe0770bac755a0991a1d0b9e6) + x: uint256(0x1ff8c6c495764da3398f1eef1688a9a8adb26177c7682642e3955ae3d89478ed), + y: uint256(0x028b82e61296a2f12eb839e90846a4ab38ea638d4cb97968436a8e27902e1a92) }), id3: Honk.G1Point({ - x: uint256(0x1600d6653d962a6e1d7eb1e852ca98cfdad2af447d57277e97316f1c658f319a), - y: uint256(0x0885d0a87171c93f343fcd71e5dc119f2a90d5f2d4aca5afe4bb0e8ef9705b70) + x: uint256(0x1860447ca16e85291f8e0aa72314153f7a15572de7ca5bef409a8ef31ee16fb1), + y: uint256(0x27fd203c2fede3b848d91415fe7ae6f2abcb8d9d09e732996250f0871d57224c) }), id4: Honk.G1Point({ - x: uint256(0x21b281792bb8fecaeed970f77521f0d4183d3ce32ca186a35dd061aa7ffb1412), - y: uint256(0x0ddc612b779c179c38ea085b8d31dc888fa877b8e240587067dfb4cb8bc50ae1) + x: uint256(0x1fd4389b7f9fddd10c6a909708fa0dadd7813a27c5aa904c74fe07f2ee366416), + y: uint256(0x2292fe69c9250db970af41a14b5d469d8a87d92128f54b18d5642689c35cd02b) }), lagrangeFirst: Honk.G1Point({ x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) }), lagrangeLast: Honk.G1Point({ - x: uint256(0x138a77bcca55fa5af243382db42438e2d0a3d09bc54d88b142fb49cb92a2b6d5), - y: uint256(0x071faf9aed6a777a75cc2c77b494705fc47630e814cf6320a233d283414264a2) + x: uint256(0x21bec12b46400ddfce58bfe6d2f403895eda4bd1718990489594da1c3f0fea92), + y: uint256(0x060b340ca69b6e38e8426ce9b8f6a613d862edb65bea3c8a14b1fb4680caac73) }) }); return vk; diff --git a/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts b/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts index ed56f1a7e..33bc5e2b5 100644 --- a/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts +++ b/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts @@ -7,13 +7,14 @@ import { expect } from "chai"; import { network } from "hardhat"; import fs from "node:fs"; import path from "node:path"; +import { fileURLToPath } from "node:url"; import { BFV_DECRYPTION_SUB_CIRCUIT_VK_HASH_PATHS, BFV_PK_SUB_CIRCUIT_VK_HASH_PATHS, - REPO_ROOT, assertBfvDecryptionVerifierSubCircuitVkHashes, assertBfvPkVerifierSubCircuitVkHashes, + committeeHashFromLimbs, readVkRecursiveHash, } from "../scripts/utils"; import type { BfvDecryptionVerifier, BfvPkVerifier } from "../types"; @@ -21,10 +22,26 @@ import type { BfvDecryptionVerifier, BfvPkVerifier } from "../types"; const { ethers, networkHelpers } = await network.connect(); const { loadFixture } = networkHelpers; -const INTEGRATION_SUMMARY = path.join( - REPO_ROOT, - "circuits/benchmarks/results_insecure/integration_summary.json", -); +const testDir = path.dirname(fileURLToPath(import.meta.url)); + +/** Committed golden folded proofs for on-chain Honk verify (insecure micro preset). */ +const FOLDED_ARTIFACTS_FIXTURE = + process.env.BFV_VK_BINDING_FOLDED_ARTIFACTS ?? + path.join(testDir, "fixtures/bfv_vk_binding/folded_artifacts.json"); + +type FoldedArtifacts = { + dkg_aggregator: { proof_hex: string; public_inputs_hex: string }; + decryption_aggregator: { proof_hex: string; public_inputs_hex: string }; +}; + +const loadFoldedArtifacts = (): FoldedArtifacts | null => { + if (!fs.existsSync(FOLDED_ARTIFACTS_FIXTURE)) { + return null; + } + return JSON.parse( + fs.readFileSync(FOLDED_ARTIFACTS_FIXTURE, "utf8"), + ) as FoldedArtifacts; +}; const hasCompiledVkArtifacts = (): boolean => Object.values(BFV_PK_SUB_CIRCUIT_VK_HASH_PATHS).every((p) => @@ -58,13 +75,6 @@ const DEC_COMMITTEE_HASH_LO_IDX = 3; /** `4` pub params + `107` return fields (`T = 1`). */ const DEC_EXPECTED_PUBLIC_INPUT_LEN = 111; -function committeeHashFromLimbs(hi: string, lo: string): string { - const hiBn = BigInt(hi); - const loBn = BigInt(lo); - return ("0x" + - ((hiBn << 128n) | loBn).toString(16).padStart(64, "0")) as `0x${string}`; -} - function plaintextHashFromPublicInputs(publicInputs: string[]): string { const messageCoeffsCount = 100; const offset = publicInputs.length - messageCoeffsCount; @@ -198,30 +208,23 @@ describe("BfvVkBindingIntegration", function () { }); const runFoldedProofIntegration = - fs.existsSync(INTEGRATION_SUMMARY) && hasCompiledVkArtifacts(); + loadFoldedArtifacts() !== null && hasCompiledVkArtifacts(); (runFoldedProofIntegration ? it : it.skip)( "folded aggregator proofs: artifact VK hashes match publicInputs[0..1] and verify passes", async function () { this.timeout(120_000); - const summary = JSON.parse( - fs.readFileSync(INTEGRATION_SUMMARY, "utf8"), - ) as { - folded_artifacts: { - dkg_aggregator: { proof_hex: string; public_inputs_hex: string }; - decryption_aggregator: { - proof_hex: string; - public_inputs_hex: string; - }; - }; - }; + const folded = loadFoldedArtifacts(); + if (folded === null) { + this.skip(); + } const dkgPublicInputs = hexToBytes32Array( - summary.folded_artifacts.dkg_aggregator.public_inputs_hex, + folded.dkg_aggregator.public_inputs_hex, ); const decPublicInputs = hexToBytes32Array( - summary.folded_artifacts.decryption_aggregator.public_inputs_hex, + folded.decryption_aggregator.public_inputs_hex, ); const expectedNodesFoldKeyHash = readVkRecursiveHash( @@ -247,9 +250,9 @@ describe("BfvVkBindingIntegration", function () { decPublicInputs.length !== DEC_EXPECTED_PUBLIC_INPUT_LEN ) { console.warn( - "Skipping folded proof verify: integration_summary.json was built before " + - "committee_hash public inputs. Regenerate via integration test with " + - "BENCHMARK_SUMMARY_OUTPUT or update circuits/benchmarks/results_insecure/integration_summary.json.", + "Skipping folded proof verify: fixture public-input layout is stale. " + + "Re-run test_trbfv_actor, then refresh test/fixtures/bfv_vk_binding/folded_artifacts.json " + + "(see jq one-liner in that directory README).", ); this.skip(); } @@ -268,7 +271,7 @@ describe("BfvVkBindingIntegration", function () { const dkgEncoded = abiCoder.encode( ["bytes", "bytes32[]"], - [summary.folded_artifacts.dkg_aggregator.proof_hex, dkgPublicInputs], + [folded.dkg_aggregator.proof_hex, dkgPublicInputs], ); const pkCommitment = dkgPublicInputs[dkgPublicInputs.length - 1]; expect( @@ -281,10 +284,7 @@ describe("BfvVkBindingIntegration", function () { const decEncoded = abiCoder.encode( ["bytes", "bytes32[]"], - [ - summary.folded_artifacts.decryption_aggregator.proof_hex, - decPublicInputs, - ], + [folded.decryption_aggregator.proof_hex, decPublicInputs], ); const plaintextHash = plaintextHashFromPublicInputs(decPublicInputs); expect( @@ -302,16 +302,13 @@ describe("BfvVkBindingIntegration", function () { async function () { this.timeout(120_000); - const summary = JSON.parse( - fs.readFileSync(INTEGRATION_SUMMARY, "utf8"), - ) as { - folded_artifacts: { - dkg_aggregator: { proof_hex: string; public_inputs_hex: string }; - }; - }; + const folded = loadFoldedArtifacts(); + if (folded === null) { + this.skip(); + } const dkgPublicInputs = hexToBytes32Array( - summary.folded_artifacts.dkg_aggregator.public_inputs_hex, + folded.dkg_aggregator.public_inputs_hex, ); const expectedC5KeyHash = readVkRecursiveHash( BFV_PK_SUB_CIRCUIT_VK_HASH_PATHS.c5, @@ -345,7 +342,7 @@ describe("BfvVkBindingIntegration", function () { const abiCoder = ethers.AbiCoder.defaultAbiCoder(); const dkgEncoded = abiCoder.encode( ["bytes", "bytes32[]"], - [summary.folded_artifacts.dkg_aggregator.proof_hex, dkgPublicInputs], + [folded.dkg_aggregator.proof_hex, dkgPublicInputs], ); const pkCommitment = dkgPublicInputs[dkgPublicInputs.length - 1]; diff --git a/packages/enclave-contracts/test/fixtures/bfv_vk_binding/README.md b/packages/enclave-contracts/test/fixtures/bfv_vk_binding/README.md new file mode 100644 index 000000000..da9d7e672 --- /dev/null +++ b/packages/enclave-contracts/test/fixtures/bfv_vk_binding/README.md @@ -0,0 +1,15 @@ +# BFV VK binding fixtures + +Golden `dkg_aggregator` / `decryption_aggregator` EVM proofs for +`test/BfvVkBindingIntegration.spec.ts`. Independent of `circuits/benchmarks/`. + +Refresh after circuit or aggregator public-input layout changes: + +```bash +# From repo root, after insecure integration run exports integration_summary: +jq '.folded_artifacts' circuits/benchmarks/results_insecure/integration_summary.json \ + > packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json +``` + +Or set `BFV_VK_BINDING_FOLDED_ARTIFACTS` to another JSON file with the same +shape. 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 new file mode 100644 index 000000000..e7250103d --- /dev/null +++ b/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json @@ -0,0 +1,10 @@ +{ + "dkg_aggregator": { + "proof_hex": "0x00000000000000000000000000000000000000000000000ab6528b828dc60dbb00000000000000000000000000000000000000000000000a1abbbfd7f6dfd38c00000000000000000000000000000000000000000000000830618157af29e0dd000000000000000000000000000000000000000000000000000030b0b3de35ea00000000000000000000000000000000000000000000000188735db8f53dc39800000000000000000000000000000000000000000000000a18fc5b2c715b3bdc00000000000000000000000000000000000000000000000bda61492423a3ec510000000000000000000000000000000000000000000000000000dd0290b3c7a9000000000000000000000000000000000000000000000004983364b7fd2661c10000000000000000000000000000000000000000000000039b4556d8607bdb7f0000000000000000000000000000000000000000000000099a73ba6ee66e29af0000000000000000000000000000000000000000000000000002f88cb3525c2400000000000000000000000000000000000000000000000dee1e0248c53aec7a000000000000000000000000000000000000000000000002d7030903bd0292ad00000000000000000000000000000000000000000000000d3bc4e4978a08e90a00000000000000000000000000000000000000000000000000023bca494aea202d963ffe2e1c6090bd0417565279b28b01889be9222dcfe21d89b8bc37ea633a118425af1d443927e153024e09a6f511d70b18941e6691c58bd0fcb01eb5580d1bf539f221f8b59e7182eebb1d4f8fe20f6d071ae56a9209b65dba1db587938116ca9f42700f6637513c56ba8c9b0515960d790c8b4dbe376efe344d5f935cea06b4b861140217a2a61f3c7e65b5f762ac0a03f3a240dbfa9e18f437a7cb30b5199c8aae11cf8c30a4ef2de6b9622cd84bd98d344b563e0ad901317254e5f708029cd7ccd824db93db38c3ac3983443731fea6679ede17b63365e86e934fa6bc0806ae63c2a4d1a99d5f8be2e7d9a42bfd10502673ae3e598c2c0c3362def4a927aeb4cd7a438200bdeecfe20a3b86d93a666bcc3eff4dc3eb82ff02e3a2b2a4218ee3aa235f850585d5e928808022969398b5b4c2ad44264feea427187abe2a2e359653c433f3965bcbf214db7711f8bd8bd7fe71c7feb983cb463ce891add81689ee23cb1e15053533135d6a5b3354bddec9afb60428d5db053cd7e6f16b7f01a8b5545ffba4cfb3fd23b26cf203722f86cb76878b459259f3a08d9fdd32f22153b36759cc3a604f7812d907a333a091afe1821dbf642c8b8e522071c729c924b0e2aff5d72d70c59180b7f69061f709c6a2508e68a5e86ce5914fc7dc55aa2ec18dbc526b61e9cf05734d48c3955e23058f894a535ac974e102236e60bef51d63943cc3435eeff39b9905d4ba6d221c1392add2485af108330ddfe0c76da1192a7156f072b520802de68204d5b130afebd9f7de607b326d6265b1e64ff2182bd0e3458273fbbd7f7f5f6fc7da4acc89468c6a82fd21a84bcf8f5a169b502e14cab9389991a4d1aae476597ca83bf5e0a3d2654caa22b02c7ede94e1f4392015227284b88fdd39e7406e3efc1896e8668d4576465a3fb0bc0203261adb539e04aa1a23894473f04e76904d56e7a5861eb2f5aaaf44aa8ef22934998ca79d482884f74605be1e455bf594fbb689c1fb78f53d217184a674c725df26407f7e712dc0ab88ca15039410497ce8310927ad651c80cdde05da3e9ca5498ffbe89e01149e820d0a10d51851e40fd7645040132b3c1a4813466249d6037559300da0e5143232ffe0c122c73393a689d26e91faacc5b6194d8843088e11503b50ba986713933bac02d1b0a39e55efe0c4b8bdba00b2104956c6755aaab1e33961c91c792546146690dc9bfe19b77238cb26d4cfe0b9746ff20199ec3579c7d3e54cfa7624c17e311a082443911f00cc9be18fe6126814303a40656a52db542563901a33200a084c1dabaf2302042c0e7387813c36a24d46cfe883888653580f32706af013bf242fa9b03a2f7e40a45f1d23c9048effe8eb8de9f41046e35a8d753851e408fcb71165eb5d6f6c197c7a0fd6991b155db680a06cdd0116615e653c85ba7c1294aa0a07ef05bfceb690467dcd7ee88dcf31ffcb5453db3f71a2ccd2e75df523584ab053db076c6fb57ceb1356781e4eda6a4b1603da76bafd0bbbb6d61f40260ea34d7aeec03ffc1a93c42370f74c0a4ed8043d950136de56617a7759274611b14e0b002ca984eb4ae6dadce0aeccabd358192021a15bd33b76dc01c877b72bc09a425821140f55a66529a1cfab1abfa742c9e78c858182aa2db6b9483e892e467f3a4a9a130cc8d5b4373149254a438a33a1c72975cdb0425271599c4e9f02e35e2aad8080ce76aad49fcdbb528dff3f2404d0920be45890ba1b29cb4fca098bc976b98680a06e873daec3d1179855d59cc2f420e1a16e5b7da81a1b4eb907576f97540f9df83d2c5ea725c1adfb2b1e789cb0889b2dfed9329a9345b70d2da362dabfb7e832f1c4cc319924756eca3240f9faff90eb37224a7f33ce899e2e5130c2cfa148e522504889f859d71a412b10b374d84a6c139084239080067016429a61503f916f0ce4db10b7d0584b6dc3bac6df045686fbdaec850ff10b901466522e6931ae2771820cb1919ab5fec1e9a4fbdf686b1c66df96b7e319f243122cd4186f0f615e3a6b6e7d7f0e4968b1380f3751d7b3bd220f8c27a46208d60a2487ce2b1ca1802aa8364092ded7ff00976bcb3a1620c79ae30146031a010a2c0a2a00c6e4bff75a514d26be107d1b126c95cdb6e677736d9e665678bd12f70f209adde22fc74e1ee6b059bf66f54b3d240c2fc4b6f50ddd82d617ea2d5cba1bde17c4fb34105992103768d2b0378abd71926522ba98afed09931f50b3bd9527f78f8c6fb753b05db1aa4eb4d283344acdbd8fc4e4e473e470dc9807ca6d252048dcdb39999d4575a00011ab76c1b9cbebd74d6e61de94ea700464f14ed7b622d1c86a8dafc5edd967ffa4d14c8783edbe39442888f1878b9358d0a8aa78480f4ca426772b8d77e03813f6746c729583e1b0fb0013b5dcdf2643ec202799b52ac74b84233b3e19972530e0eb9e270c31decb023ca2216a641da5b326dfd8da24198fa386e1e60ed5257039de5f1abe0cceff64cec32c2e001b5ba1cc1899261f97f33e03b7b2b70d3f9a7124c4ce410f818051c3d38b385386b8a2d29cea72273660298664bbb39f126e0a51b6e026dc983640af54b7c6c6c9b19d3849c2f8242fdcbd80c208af1b934549fa4e256cc9c5c87e44d3f34429af107e081d8251296ad5ad4027c55437aefa751b0084f58e19227dfffebb39fe09200304927d8d04e3dbe7bdf246e8a9034765e69e7b4debdb242daf2a654116f3f6ec3b527eb817127f4dee47fdbe34f8efc4114aef0b3c73558735da92b92d0c54acb864b55b1c1b879c3b64e5efef70fb093f10c7b26f286ff8a95cd1fc78cbfd205dda55b12c079a072d723e63051bedcb0e9ec0bf522e4f219d88f7b794beb58b79be8ae303e542dfa8998fe8e2e40f0c2df8960c6fe54f1252372d030134f16fa2594a412a70af625481ac842b428aa11075415bf98856d4fc23375f09b6769f988976570f54169c1ecd9808627f0a9a85e1468abbf435a00fb3d1668484efd3724c7682008a87ea2bdfe7d46eb25ff8378ee96849409a8fb37eb7011ad04fb384c4dd2c1f4ef9f1b37b1371558b8e2f93582cb2646f2c09724656efb97d3337dcab06c90f0e0a99dbd83d4a4040273cf915e6042171711250cf964bd163d23a72972b3f0b9d4b10137e2807a7f31b2f9004fdb8b520e002a13526ac923eee0f81e6c1640d58e1c704ace55a8fe098468cdcac47a514ce7c13d1bff170761bb70805646c1ce220f83362153b1e348491bfb71d71e1d7efc77b6d24c3ca974a94d52c496905ab852fe706d48bbdbf5ae25ce05dee3e8b7b6ae617c3cb19530fa069d0471f21fd12a73708d3bffe920539440d67b4d3f416ea872b8201a1fde3ec5cbaf9b102e000c471ca6b9a956924a7090f27fe186fed06b74101b9320e5c21353be11a02275397a74cdc6c9c22b70a9888537e3672d2af698c54b749648261a50409f2047b34e162adab623e609aa4807edd86ab29f032d7d7def5528e3f459d1c83ff184be5373b224676aef2e0746332fdfbaa300f276d444f7843a2a98705aa467e2de40fa7cf1cf09ac1c95460fc6add42a149d1830c1b3a1ba13fe1ea634d731306cabf66feb50a478f667de4ba425e8a0d3deaf6848a0ab8c7db3f5574340a7d1214de510d4c573dfbf81e2ced85574f085b25585d25656c4c44b6de923a840a167bd567f720f2035b2a456790a5c36363ce092704be976aab4b7d59302d178f0edea2005a2dfde72ce639358d5b71b0d71cef895d6e7416864a2b7f83d1162a0d570982dba35651835593d240d35fdbf3c39b92d70cb1eaa52722b06dc5c92226b4a401b7cd3ac29a59254af9e4d233300734b4fb2c968ec5e2893566f7ce921847b7ba1aab42f4e4f5f51c638c03a13ad23805b9de2cc0a7d92549c97bcf6c10a78f15aaf95bd7419f2b73befe6f639828985bd1c8b28ef032a4a56adbfcfd1df4856798eed823feb886065eb10586cb72ab2737640bfa4b7c5e5fff3b2a652f48aad393feab573c94a109a7801ee0da56281d67b8691a8ae036e9060c4fc22964f9dfd7d8d3f81c1e68e0392e1c8f672d3cc7daf941f5eea32c8c194f85531d2643255822a749fba1e156e0c96de8ffef2b002b753ea47633d0a35078298b1ffc0eeb733fcd2e39f1ef634cbb169a64529f0d8c09d7e4739795946e0bf9451f995d5977e1a6c154229e20a048e7803169e2aed6f306b8e7e503853ff5e70403ebe23597b0a87ab2d5e0882414db412e051e15e0eb2ff95069bf0ac8c145250e84bbe57101fa3560210f08a76f92c25c8313f9330ce8ab268f6a37d3f955450ac7dd3e876fd857582c12d23c67aec92d75a9cc0e8888a9b984c31a9b1a290f2d8a3296f661b42d2bc8d60093f7293e6d5152112ebcc19bfb2b431ce691187f1622524074146516bdf0695d725a9b2b8b2ab8b171321c21f6bc9e966e93afae0f21beb5db56c01e0764cdc9cc1fafb0e77ed45120a2859d7ace771c262f609f23337085ad7ac0bd1518b19d7b02f22d36007e4a63a9655147b2549b74911bb02309ca97c90114bdc21fbc2e8354aedf45453389690da39edac5fdda7d03430411465676719090813825915d4ae9ec91c3afe24ccec744d07bb4de99add1605409b0c014deee8122ecfec860077f4e94dc76c97d6ed198b85e40174939c8b6aa15b8c1050960dd58e39fd5071db6b77591dc9529a230ca6af9fcc9f005b5caca043c24781515871b203e1de9f33fae96804acf9dd66a37851e916e09d0eb92f42232ad796741d77958f87d6be73a92fe19723019c9c9191346e428e0d06611952a980c1b7ddebc02477fd7417b32342780bb56756ef02acb202e3a68725a381f2b92ceedad2961000dce17ecfb2cc250be2c22e51fc82287d365e7ac359789ad2d6ff81fccc535275f3cc21c3e3bf4a04791627dd6d29d721f27082dcaa91f6e07f16049e9bbfa056dfd61d3b7b4b669787d139ea5119d4d9935cab7396c0e5d1214293575a65ee565f4922d3bc625d71f6a09afb3289208b9d3483c05975430031d01ce4ba374f41d28ac18c62bac813ed4c81aa4092905795358f08d116aa20409f44609813fe3641045f1632fbbfbf1277c4a783f5f412fc58b5d902d9fef218370a7b167710d3e2b896fede982fead14198b2bb122176a2cf6a41e596fed0f77a6ec2b5cc237a288ec5558415477ee54a5823a4c25b9a935a086381ae87400e36213fe3c54fe588df40ad8f9910d1289375d78aea52c585e6b0d8b000ca70a2814832c8ba9d7102d18c2c6432d2b437276444d455cb827d2cee930edc7e325b7423f5022749fcafff9a8e0f17ccdae9d2495be960cb92e0522412d116d8c2f1235fc89fe520e96cba827e08dbcc18942ea99d34396d07a510b7e4f2da3dc2448ef4ad4743479abd38c5ad3321ee0024af73947e13ac6e8e05ab58d0c1ce70d39e806ed89cd8f4be46b3890b8e73f6eccb674e53898b7d20c3858642f051c2e1d4894abeee0ac8b42e5098272d0c70d20fcfb4210b2b050c241e49e197cb11f53f6ae5413b466feae19c03d4818c347e400fa24a88187cebcb8e2a03b7f6700d14e54f9fd3815afea6aa78f1eab9ea0e63aba4e6051e188a1e3e11242f1c219d4bcd07f03a22b20ce8dabe5c7e276b88230a1bcb9968fca6fa9ce70779f350e073860dd63b61332e41cf6b742f050a43c9ada81ede0273e2f1d7ac851cdac0e6b76c48a46bb1506326939d912cb207b0452cb0657f810168ea8f5bb5336af12946dc6a82457d21b99ee430376f2943713cc5c0ea1e305d3f339985477c86d1461abaa8a5bb1949440d17fddd237e7aa73ac7b53f8eaddda829d7ffc6860de18f796e1c8e4ba59c7c8268e0c2c1ee35edf60c62230d3abc9bf57818e7f32a40a0f716c567d18e4dc7658bf84af3d5acb0c49f8db7a6c594f2e4b6eca678d460cb93e47b1344d2e4703f14f1d8d2a0555588ca28e19c7262cd4e5cb8f32bd1207a5d911fcba9704a8407c74e8cb344a4bfc107d6ca66320908508105c49253d04f3d0fbdf6801000f6c79ab6e24279275e5966c2eb653daf4f55be52c661aad0600f4c6c80d9d9fc0e43126c9fcae4f8593cd15c655f6096617f7c219bfb81817655fcbf9f285a52b89b1472f10724b7fb50540b377da2b45384b198d1772f229828e95ce47662a8f3e49e0606ee53e2d860c44af493f0d3b0d572070e8e5f20cab47637347d09170a19ae0a99ae74354c855b4611655aed2de1fa258cc243600dd47ad410fca16aac2a116fcce260a895c81a3a89ce81a19afb5868b3a64de0dfa78ffd98eba18398883fe8955fdf45716dd3bc94011f7062506ca98744fba2fea32b10cf3b2394da53d9988e193d4c5d06789938195241605941bc9d5d66c04302095ac006752fc3cbb0d7512fabb3a4b93bf67264df85bad30f28d468745212d98ac8e9fb00dbc19b1a1f92e24cc8573592579ce3af143ed04a9029bafdf1a0f4fa7afce28cbbefae3194c23921d61d988b4c18d29138ea3e95e3786f75600de9e1339cb0792aa919d41b3bc0e35a96053d13228d0d0dc37732da80dcd05027ca25b34cb25393e1b9d30065a5f8cf325982c4406199a0b4ec569928809ea1765a05be230f3b04c5e6feb4244c1c94caaf2123fa942a5d05ec8ebd4b446d52f44119a9447ed0b7dd6cf5e9abd2842fac8c1fd2dc88a968fcd238dba2b100a27c7851b2997c86cfba255e10b7ac0d6c9496291cf63ccde6285d45fd46da38810bf3ad0fc190493c633a698581fce39efcca70a352620f72803f7587656d23b2753b69783f660b76c896d865dab1fc38c104563f253241ac37c8bcbc68d88e309e109d15f9e10c2c39a1fbee52879576826e06802a9c990ddeb73299543b1871b903b1927c1f9e25478e97faaf3bf8cdbf206994210050ec9dac8bf965f3e6e08ed6d0746709cd2aaa86c5e32d1bc480d3c8c53e9a4c0fc785cbee882e360cb2d6016adbe6d57df4352707b43440ce847343bc82190047dca88d2a925a10c882dbd843bee3d2f61f817dc396bb607b3069ce2007f8f38664f16fae5814c4f15037e191d5f2d381491bcfad0ad7714c0f62326f534bd932a85621328d3faafac292949dd9eb0c36f2c1f14868951a8ccf391d6a963e2f46dcb3b34de509254b223ce8c532602bfd600fe080235761b6f24e35f6a869247389e27fafaf261a3302ebb431b8ef908c86b147fe26568f2beec945500d20a16461946220dc41556fc09aa6ae0f757562c86b4d4539f1e7eccce199fd06a9221d358cc0dbbbfd66db5193515ace6da5cf705b604ef665eb3e048a0091d2b6c82bcf4e3f9f3ebfee98c17dd7b4f796c867536767b7cd01e648b24260af29d0fd53203c08b4eacc24c3223bdb95bc20fc8422f635e0e5d8188e393b1e7aabf1fd025d977bbf4498537dd1809c8d4f3736424abefaefbe35918025ee7c3571bee54aa24a8b812e6c4d5730dbc7f04df7009edf8a6c80f2eb13eeea47f76177ec317bf7e97f452a20565992b4be9d856d012b26309d4a9acbaf736c5b917c42bb663468a2cb60c53e6e81225db1c970f1145e4fba55a75981d1ee8a0d93c3f221194b18b5d80cf453dc31a28b52c8720c7778419ef6f03bec2acd6b32c9b1ca1f9e575ba939756158420b10fd8ba42b0a8a67881c5a02d339a71d2a712361d8c6633086fc4e7676e27b82b06d429375baf1f7211242630f00c9e2662b30ddbe78ecba02aa976ad1e9036230408908cd28b1b62b594ccfb9117f9e450ef605467e290f4eba7cc659eccd225262f9ffa5f1313713f793743decc21e2e3cd0960194b545cdd53d36782747922300978a244e319a6c6d89727be7f6119bcca83ddbf8188658495ea0d1e090db51e8821a456f9c1e93c05c94ff5f2943c71cb687bb2a613ae0524cf2ed3478b5e16e825a4df6bc173e72ebd4dee10f62973780746d87d667b46db12dd7becd1aa1c625d08739eec7fe5ac1aa259bad07272df6327f08ef2fa141751f13e538d722192456d40acc4116717531af7ddedb324a90dd8ce98756bbcf3489445ec89dc2a56ff76e9acd6839977f0052dbc9f28feafc3cbc4ee225aae2369aec5adf1881a4bcddf349e614e33616aea81c97e0823d12b5d471624a709b21f17b406aa532981df1c06a00f315c817b93ca03e6b51f0f87738873c4717095e95cc4390b651671ae7f2cc09330f65231d1bcb0396062a2c990390507cf4e4d916bf62281ea04705d0990539c3be4863e38e4548a6b11b462fcffe572710b9839fa7053790d0fdcbc54ac8897b836768706bc00cec2396a68dd4b6536194d5bcb608215cde515db3ba42c4907b9e7516c51c00a2d6d98b91c40e4540249dc687254bf6f0e261a75ef9066f9ea43f9854010451680fe61ff14cdd2d837e478f3f25097f4cbcd1905831494cf90b01c3565af7e3d295861b05bfd2c4cf50a9615cb2228cfd93a05c5ed43c593260b00d2747a6b63a9e2ba2c61130070f54972aefc1bf1fca6a214a664368ea883c4153b2080c0da033b3e2cf09cf70b79b2ca3604d2768dbc6c25c54d918a2e32ae2b15a733b4f0ecc57ab6b53131ec55effbc7beade097ea9e17748761b4f968e77ba7319feab82d5b5ef23646e40de26a20665c3f9d85370a14bffc2db9ef2fe52f4835e51a7f869f540079e51bbd0b65898cce6ed666cfa12d11799b1b8a26b300be1b1149f009afbbd3690e9cdbd958462b08b25527e5c91e2ced05551874cec301f57b0460c7682ad99d95f975188981188383caad550e17de8f35ec3412101e2368d797eec0fecd967428430dfda277f9beaa5d363ff5122a46168b8de0b4f951d9b065f1cbba637dd52d2192b594355084fa3a9b9a090fe79e7dc890b555ca849b7c88ac83af6d1f73b87e69c141e8d86195424c441a1a5c0158e48abb771bcede8936a1b9113f55898928ca73dc75e5b6c4de406f4114e8708379c7e5d1433d4845b35737dc65e84327b340c01dbf65cacc706ca58117da8029464130c99b5892187898e58abaabb6944eeccccf67dcaf9a8adc6ba52f351fc70df1a9023ffe1b3ac2357f5661dc9e3e2da37226434eb80f8c7cdf0113bda125efbc22740e9593a0b09928efb71520453485365391c7790d931f68712f1e3914c9bf5018a3df1979f3cc1d9bc8ce6c59ac9f0b4d3eeab6392e4bbccf01e75106c9caaf1123582416ccdd87ad50ba24944c8af7d2e663ac666e2f45951d48e3d982908aa887a32d5f2812c2bc7d0819d57f474758bee18c53c5b275fb282c23fc095cf698950df8016f2948992fbf6142aa4e0e30a5e736c515b1f2c02b195790411f09de4a0cc204bdf0245a9a574e4f79a91ecc1bbb69a48b311da91eefe7c39b57535035eed99d825e0d1ab768608aa47f30306158ef34edd4bbbd0a0a9d6cb21f1ca1da8a519fd734a1c7184bce140551f50816a085dcf4bdb4c92ed9c14821e951be0b6ba610ace2bd159bc83184e9e675bea003a97278fc57182a65fa1cc3f8f5fe06c3955051fbe3c1b4c02ddedd0e8db7a17e9ec190dde15709681ecf7b55113616b516a24fdd90a53286c1514d865e7767ea9ba09547fa471ca8c329de319b9454634395fede811145c72226d89cdfc98851bf5925f6e1682b03cb157a9dfe93962463c49e37e794fb4f7bcc8ef15f4b95a08ee504b424bc2b9b0ceb50d95c08549b29ac4433d6b5c0e112e25496bb7526998764d265ed5723569ffdd6ccff3f7b662fa9b12c449562c4f646654842a558c8b1c9f244ecd0083f3d836874363dff34932fa01abab49421c8e40cbdfa9ff52d5cd9887a9395148a55b111d46832f98328dedc0cae8db24360ef8e97fc6d4e5685e8e40ac31a018afd561b36f06b595090ee4caa5595baa0d1525fd0a9a465799925d7749e60163c569702c9ce5c359a1d07b70d603d86cd18217349a14acd53f0bc030edf5511ff984a8cb7855e63d9c51f7ac955363e610eb9758994f4b10b66064c5a456314644d1ddfac620436c9d8700d2c0fe22e6304a1675686a2875b236afed295fd05bb3ac47584e57fee2044db8b3f62dd73c55a56bbef55e04abc70f00cd6141222e712bccd22dfb053db450c6f9b67a0f0e9158ff01f4a955005f899bb7eb8d921c2cf4e03814886d1c239f5a451d9e6a1026540307703dd10a05a24714d7ccd0a4a0c13790c564d83c3d3a2f06a0bc05a1c61445d8a1a40aebac9fce29c3a241ea1d19f175757fa05361f97de1490537c9e0c0fe9bea0ae2b4d014760d083bf1ac8dc300c7b476fe73e89782c02646bb380554fc729de6d7e84b4a0f373b6972129ba59b38b642558fe44e48ec2b07c3def9b782483d8a45dc5ee71ce5f2ce907a836cea49c19e51cfeb0f1b546a60ae3efc45458ade65d02447c74c6ca74061c5324a909fd2b00503aec9ec3ddd84146744b9d29dd9ecd507cdfc617dc91fd1a28648209d385173df6d92f02072f3577ab4e023049b5a0f79506ac6ac893f322888d051f9643dda5e82fdb3a9dea6c69b37008312ff610a0bfdea80026e645207340443fed37069a0fe383b75ed6494fd3d630334568b5209a570fdad7193e1076088607464721c381c121e50a8277fde1fa45252b8eaa0093c45e8714ada412fa188436a26f46c5ab225791057fc6ec5b9223467da3fe1663b6b1a451018306dd342d2124c81e5cd1c74c727bc60407909c9e65297271669c7a921a2cb40321eee41107d80636df7868c993350247387951661232392a971ab5ddef7a26310c617af0981a260c9665c71129c442cf034f627f494a2ca59885a5ce8f1c854b1da0afb25ae9248b1df9a14ff11608dc4a6f8ddcd2d7b39dacc3a35c9f89b08d16ebb6ff7a15520ebbecec07fd074db247d9df3b0af7586012ed8346e8901d0c258cd55cb05ce90f89f0b55cd8593da3d72a350af0e1a9c5d7ac18818e8c2ed516815a67775370220d07270f32a2bab24e5597711b7ece5ade6d223e8c9bd433195c85cfb22324dfc23d98db74abe107938e348182357e25d635b13397c10a5d1a5646bd39f10e13977bd19648d86c980b224741e3950f923c189fa7de5f071c2b675e233a8d975e1ae439254275baf0177cb2fc7d2b56854946a10039a91dc217fa761bb0d32fbdd6454952081adafd2c34b8c868aec6d98f0ce42b92a473d41de0b8e19fe52f03cf4d73e6c2ae367f476aa9db8d11139e4b3bb44588a4c7e72db5ef6579f8014f40bd2d00556a2c42d14c767dee9ae851a918a2ffcc449f4a209afa2d34b900dfc8b5101e846c67d3df2a9094abbdc3ef9f1a9f5da79a06012e37756e68a16cd6da82d916c17a6d00049d7bc40ec8583047a2ff323fc833771588497c7fd1458b07c8b0abc1b68ed2ebd9639d52df43c565be65be3377720525b270e3b0295caf51e1985bcbcc88d0505cb0b7b55b050f2ae1e8db0162582716f9db7a4484d9c768536b3974165a121c546071189caf4eb69b67cfe0cd3b3b1d59107c902e2eb2cdda7b1c7694c673ca206a79f86c1ffca04ba6f33b91ddd00ee174600ee6994a3d302ddbbd15c7828ffe05cf7ccfd91a873b56a46b2501152f803d7e02d9396338acc57809d06ef1d137d82c2557ff5d24764340eff9fd2307f62425a6967429b64d92f334ac221b833ba98697428f2c3e85d309a279bd9e02816cb49946a30fd532a1a0fb89d39ab14a9a894795358324bdeceb41a55dd723daba1de94e2a573870b7b6a8f934adff43734d22b5760b8958863f9b67f1472e94e714fee447cabc676bf034fea6f76208174c2535b4afe9d80204dcaac64909baed85190b56be2dabbe24268ffe8736522454b17ccf33167390d6514980f103d6d6b2a28408e7a1e460f70e572835abcb3e5f421087307f79822ab1db970423c688bc5c514aa0c845d7a880875ac269a7d1a07c190dec94d0adaeebd9b98a1f0388b58ef832e3243a0bf2d16550c3c71c996e75bea5f8213c66b63c2099b40a5e6be7c31ea4943dd66be1b0291b07e00b0863468128b1162f795c5fcce187039276ba1e7a672e4652f683ced72f5ddf271f3b94718bb96e0fe9ab74bb27882521d9cffd656320df03d1f2de945c6b4d1aa98af9c601d1586646fd843228b40ebaabafc00b73f2396c55b101a518a1e9afc8a8ea68a9af963e79ce9941e5d20ee983990f78ffaf203dac89c95e8abae36cdda5de6355f92eff43775335bca410a40d8e2a65586fb089ed470b635cfce7a12ff2f8b3d30799f707f4c755f4762ae8449dc1c2ea29bb8b730ae3ba7bc1f0a4b921d02ddbafe7193af11e58b5dc0ce5b0aba68ef34813d98e93e5e583031b09c03ba7af58d13ad32dc8d01c2745273b080c6255e7ae2d988312fc53fdd72e45a684a444beffdab1ddd37fe6a44d2d86ed6f10435b658fc474e5edc21bb4431d910505eb635e93c7674263c2ec6b1fb34e149f747638a1dcdc050f11dad7b7e8d065aca7506c507402e3377068d604e34eaaf674bfd9fe78ca3abcec0ddcf8f6bdddd63edfb5ac26d3dc71bb55a302eb54b023b184308479aa31779ab38bfe4c079bf64c261927a9ab29024684ef282b9ee273058e423e040347318becef2eb3be691dc31d88a5d4ca81db21351314a73eda94f6c93a9efecf96e5b3b6ff4dc188a0903be1b982d18c64d0cc00a10584ca47a49fbf12c4b47cb84113c978554b15ce45bdaee4248bd39c6604064813a5506d7cc1616735f0cbab341ccb85183f35d85299f22376ec3e462f27ff2922207204f8a3b0d84905b621d1b23396dce3f221802cab32d6f9eab465a923f114c89aeb7e0920c1c6a2d977d1d47925e60fc5d9dda0ae95d65a55b85f79117d0f5157194cf4cf4153465a2159496b100e590e95c49d32f51e269d6deea9211110cf1f23d59f979e136fe641878c5a12ca107ba89cc33a14349bd240b386cd4d0b678476c9b29bd37c1dba6c6f3c0d6c75ebe15d2a0824d717afd4300027b4ac2577ca36f078da8ae3e5a84fc2ef24db140aa937d8ebd806d73ae1fd23085428223722496760f2e56e2fce9c872f3f4d0f01b94a2370b264bddad5635afd70181bbed96e63dd35cffb340dfdeaa33bca7d2d315a7ee4448ed7c4b503f1282d5c038a7eaf34a2615bfc690fe5115879b0e58580c68ad170e7d8bbd686c6796438125cb3371bedb5c2a25ed7f6267303b70f7fc9f61256b40affcd13dcbae955f7207f8f01625ea1f57d1a826ceb00266f81b2e20b858cb83b8d4f60fb8c08154f14f074ecb9e5c37dfdcebebe1512dfb0fb8bdb524eda27dd5c852763ae846fea26575543210da57aeabe876f760921c932a68fe762bbd03d1af766894a1ed86a28da92abde2f9d21ad9471d4a4284ee9fdf80990583ef128320745707bb783d319112c0e40b98c05d59864c555f81ffc838808a57127ec66acbcfd7a2c731f2e0dd9690a47e11ddc33904030cd07b0e3352939885b6262a867de8f9821f0e5f721e58457e22c50272bc506b71b21d3ac2e2f37d8c54b265c26d01ba437a1bc2a07f8401e73dba5feed49c616d8741d05ae99d5a1cd60942089b64ac10a16c108245855b5ae308b2959dce5637cb82ef4c9e79adb804721297abab577dc19b1760206337651b3eb4384cd69f9f3bf18b2d4fa030b030cc6e3080255efb74345542ad4b20297c048fa3a2f70bd883e133a4e872e61519141479b9ffdf24080460519db4fb299366e2208d381dd1259499368682198369a5bde6d6652b8d71a02b310af0409c98816450a84aabaf3ff77b2ef99fc5249a0dff332457c31b66a3fa60370064d536106a22d8204d019b89b1d8b6fef01c337a0f21a62dafef82705da0c6d144be8c7742be9b07f1c3f62b8ec0ace292f09462b8a04dd3b5d070f83752cdfa5a3421ec582635806dbe9f7b4823be70252b199459b582bd30354b7f7d21e2c028f596814988d8192e31d97c6bd00085094c19b1b6102ad40f5e3dc162b139e03a3594640c003fe31235eb52638208faee9019ec21d2eaf1e0806fe0a9b0844255999c8ecbc879487961f7675eb445c026de579a2ab8c64e11af9bdb02b0d649300414155f6bbe2157846e682765ac896378142bf7cc897ad47c6bf922f1cef8aec47001f925029f48ca18e0a67d9564272ebd5c7333265e30d0a59cb940ab65a79efa6fa683f181ac7a041e0281751ed9dbf057fa2e7096b321d9e7f04239c1619ba20a42fcf86ff7bfcb6d5a95d2f2c9fe292eeb82ca7b6aa5b066c902b2f3a182a03e3cd0911d8da65c9ab51f3e3ffd0e3e5420fd33fbd355d82cafa1018f921624106f8f7d730fb60b5c2c376c8aac7cd3be4abdc9bb9abc81e56212b809c432f5e8853cc06d9070e2a711a385d1db13c42aa124db365427d0ae26a2c23bdfdb957e7b43fc759f17cd9b1702b4f6bce659fc70dde9c63978aac49902ab8c18dc483d6da31ace9c12636f2d173c202c50e4d488ee4b8b8af90f8e9a826895f5e63dd0d85cc708cf80940667491fb9dbb106423c5ae2ece55c871d2e42b37c2f0da7018fc93dc69714cdb24c3ea6677f55332868f2fcdc7408d51d949164b1bcf1f55f71aefc1dfffc6420a064081823a25adb3f7b3e6edbf384e21f00931613f859f0e6860156f6d19c40bdfcc8e05ce960555a9491244c19f36c94e02f5f84b0e5d301e5a765aa8376aa85ccfb6da13e611b40335d88fcd2d567aa1", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e940000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000041390e9098d147b0784030810969efcf00000000000000000000000000000000c017075b91d1c9c5aadc2f4d3b29027511521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + }, + "decryption_aggregator": { + "proof_hex": "0x00000000000000000000000000000000000000000000000edce069b20cdd3b3c0000000000000000000000000000000000000000000000035587edaec19bc9d0000000000000000000000000000000000000000000000004e8b61a1848c5d71d0000000000000000000000000000000000000000000000000000ec07f134570a00000000000000000000000000000000000000000000000285b9d961e7514ba30000000000000000000000000000000000000000000000037ced53ee4c6f829d000000000000000000000000000000000000000000000003b4542de3f9235c48000000000000000000000000000000000000000000000000000295d724d1e6c40000000000000000000000000000000000000000000000024984d76621f77c0e000000000000000000000000000000000000000000000008d9170675a02d3baf00000000000000000000000000000000000000000000000a55ee4aa4908eca680000000000000000000000000000000000000000000000000000178c72a0427f00000000000000000000000000000000000000000000000c780a0719338d9b0c00000000000000000000000000000000000000000000000287dc8993e1ea3e03000000000000000000000000000000000000000000000009b918c594689408490000000000000000000000000000000000000000000000000000e6a0f127f38b1c0664c1e064d80eb94bbb91785f1d24ed1d7be9507613a3cf0d4f6bf2e9d7231d1ec3f000b4ae52b58619010189eef29373ab9262aa91aa7143502034d8863011d3224f2e2edd4d00c173d14a0563b3055120bd423e5ba160b9b85989830c122c3d4e983a824d1a6d29966c7111df5c9199915567cd4aba98c44fd0ae834f9a277a09cbb2913101f449f75e3676615906ce01379d63002576ee7da737262b32236564d38f5e7a803c1f8d9ba4618283f2321568857b28f7c1f0a958192e02761c28d6c7d667f72a60c1b47882ac5bd519c2f272339121ba07a2660621dfe03922117962f26bcb850f7c4d6c290c1394ef955cb7d8158b965a8f73ebe55715b60a5ad26fc88ba140f26b746000167b877d731647f2bad22f5e22aa8662107b3903ba9d871d08782e78385c33c1e9322705d793ce23c873db9d99711f08a2fd4419bbd315d1e4fcdf4ecbaa1f63ced3d1a9e6d8224596b8db1f5bb494a2016bde28d00c42c42b88f68c6ad1f771011f95bc6cde5ad55f886bf7594359a1faeec71a2546cb3ae645c2ceba21aaac656c9691eff73811ab3f7599e839f35ecacf0d13122d59cddefd6b314e97ec4518cd154f343b7a3e4a803e21aadfa71512f8500e046fc1a29c86ac30ba80b92d6c88a8ff016611d89caf17b20af9ea60288bc707548bd820b583f244b94937314f51020158fbce2e9118933f203594ad959eed07b4b35a6ec429eaf7613eafa54fb3dcbec9ee7bedf0b142fa863ff3ab979f532a51f208c9b1df2dbc7b483ed886bfa98e9725ea544775c7bb9d667028f4b19627f5ca4f842d8e717dd35f718dc7aaf4ff57825a57ce340181d5bd8e6d2147ff2b1b1daccdc7efe44863337dafc46a94c0c29b71c850f8af09d96d588b92035d04470ad5f8011691979683753d0aba181757afe9aadd77cd8044e703fa50efdc03a037ec4c811132711ffa1d32f74ab4c4d105fce937a1e674aeed5311039f6c1b82ac81638d61248c4974ac1a4204cbefe6558773523e38183701596fc70db51e4ef18838b97f26696532758504247d6204e1bb16d1d4a6f7690e22b92ec22b1e7216c99504649f548ea5e76058166e6ab62d8b597da944b3f251e0a7f5dd550cd43a77afcf6fdc66df86ae1f2a0bcbf217be0e001d5db52d8b348ee751b0191305dc8b39e1a34a8f2b255f13e90c5f4175cb2e3863169b1e6b74eddd072b9f21a8bc5ba5c3f55267fc4c73f55b05016a0db089eda8e476039e4b47da5f9dfe2b79a3acb1133d7b29f45ff225d0c4272bdb5904c6558a7731288c110a549d491d428dfbb0b13b94fe735c89379880bbee36f28f8069de31e430ff99288c66fe0c006bb22bf7b4a9592cd03dffe27a9ed159b0659b109004e5178e4bdef83b282a5e323d882ab264f227bd6470279199a135bd78b51de8c6f36652f1ace3c0990bab78767cbb925396a35508bdbb408df3194860a19d252babb16e91402e2ed12eba5dff99e35eba65c89111f2f8ecb8ab001a0c4d0025d5d2c275efa03b181202fc7f806b44988c8fe287a384ea1afd59ac50a70e7d0d47d94be1897ec5e9e203ac5d9e3f5f1f7cf379e828e01f76efdcccb0614b23beddfa5f6f8cbe09e9e41c6bd67b5e867148d005eb0eb83a20c69edae66cb10f0399f049cc4dfc577fe92f3160e22735ba35b84465fffccdec15f911306df24c11ee96201e4b04d2e1490d1ea90f45ed5669b39f607652462fd9f10ff45449ba0a3d69318d075e365393000429a8ffe54041f667d458a24a950cd629a1f25c88e500b915ef8b8191ae931e7bf88306e191275053149520e06fc4c5ec65452d0f231b2a840a1c2e47c4d6274c2fb4eb7d2ce13cdd3eef4bbaaf730576192979cf71566a194c11723e7cf52a8f8c03407906136dfb54c7618450df427bfe58533534b6f607df93d4e525510fb61f91c34085310910d69d63416b1144cad84e2db53f079311f0c7d2cc880222cd9094f071155155d56e58f28055a5403eada3ea64bd00a15222216f54a58a11d88c720803f206e5ae51935e5afd64b155c4fdb32ad6acf0efc7a2fff6c6dc29aec8db1d185dec27709075fd7366dddb28a9909f20b637e709d5ac48b066d2056728aa6ea0b695a094e8b077280be353b709c208c531be2174262ee74a6ddd16cd375703f431f50f42d497ebfaafb2204fbe31c1db00f847c205a9e5bc54db1f42362fb4a9b97d732b7b2a543c772b785fb139c372ebd00127ff15437a653f0397d45a567e5c6000a4b9ac2812b07511de3a692e5b94e5118dc68dd31ee21507c2f4c9c52f9c54b45ebd3c2918942e1498162908e85bd8cea61ee40e823bc801a3a24b6721333c061e10ebcfd63353a8651b95377d721138d170246bf515650ded72aa95d8673e5c8e1440ad005d31d9f75f78a8402b38522bd14a172eb6171201a44177bd3a939cece9958e1b278c01d40beb9d5b46360c4f07a43b49852420a12fde2f03060a123ad784f488d0b4872e94d10feba6c2fd3959e9a9b1b56210cf7b3b52aa00a90692846a05675ac063d0e933c1bf8f8bb8a99cf90967576308d1f7402a634a8031346f58838220918ebf2a36611413ba04bbee53f23595c2011fec50e061c919f78a127db735b3c640ae6c8f1ea70d4b8216595f35da58041939c77c2d3b9526ddb4971f0b4442675f2e645daaa4cbc3441c424463efa5d42a6350ca221811927c5c08451ea4a05499a29d69b219f2c993d451a437f43d8516e679a796ae6143e42a26668b048b4ce5c0debd5fc4eb4e934d2e6ba7b7b5b31084748288df95597c2f838cd23851420c57562eece196a9cf56f605cd2cc5802ebfbc1e5f4d970648ca521f5dc09194cb6b31cd11e5dc65a808aae5a72b0b8d06f3ed69825af86f15664eaf3d8cf9b9f44edbc0cffde603a1649a04381d008c1586681d08744417cf896484566b7c9f40d9197cc46472eea063bd1763135fc824fd8ff1e9cd4bd25ac764edfa4127ae79f3b4b3236c7e149b0409e4244575bb064648188294df57511c5e320a52988c7a98e71de98924c63cc4df72c10f76770770552f6688be1849aa0a91cca874e842ea9a0d080d3d6eadad68206fbe2ada0d07de35b059ad5e3f3944dcb6a650ee64e230a53f4f04a4e9ec9e3330f9312e182d90545db4f65973f636cbe00905c00d1cf4c173052b4f1deaad0f09239cc80e6477cb573bb32c25ddce44a1b5997eef3b1489a520c3e714f2bd84b33964b020a23f0a9ca584020b90dc27764852b6b9fa3551aab838b649b78767cbf40a1b1b928595c0ff6d5b9a2c8dfbd94eeab852563fe2554b29e55c8966aab39ba2cf11c5e2a750468bd5b34956ea7e85d226cf5f99f6c7d0fec89924bedc43b020120e17513393289e3cf9a0f0a86447b80b25275c4e543758bdfe998290522ae8421a90eba5171c4ce624b6466846d2ae316de1ff93039df1cbfaea396d150477dd040cb85d01066dd0015f3151bf7e2ef022c1be954af6bf1032312afcac83ccab302363d2e381c358a1bddf79f73bbe462b5df71897535eb55e73bfd38ca44f510357a687f0b64fc814ab4f3a84c2a71894b89bd2089f27aa805757662a9c5006200d757d3341fdae52baa65b943829ecf02888b4401bf065989bdd8a2cb71ad31796f5df7bb522beccab8a547cfe2cdbdce2fc7413233b72ced05ce662f88af9123ac93aae51fd8c1e8025da7bc2345b3b110b5e59d2ae2ffc729d0550db5fc60329666ce8fb0d6356568f1ef2618ac7f7e4bf2a6e3ae35b7588780b29e198d52b298c4ed6a93551745f03889c5752475272e91f87e836ea54caa93cddbcfc8613cd6384d28dd49ec99b2232309b798fa694072d9772542aab79584eb091633d202572c6c995c7268e871d4f8840d366099e7c124c6f0f37a91b629079c7835500d621755dbf91a3c126649d7ccaa9c979b09aad02b315869a542dfb4b7366831c2e3955c00d7fbe2c15682d53ac21ce310fea224a7c8d972cad6c4b1415d0a91278e6b67b2a051654d60eacacfe4f32e698bcf8bd7ac192bb696d9e13994133116a66f3ab150806fb12287e271bd614aeb9048dac568c1cff2c1fbe79ade43f133b89887f6f2c8ec436d5b37eea62acd2eaf5508a77662fa8bc2bbf5c39dcb225f3bd14c21670a9294633249d98d602eae71803a11c9bc9277e1f3ae18b2f3919f778be7ca5b0d88337df43757f52b0a273d554fab9f5649c3c9e3deb5f0ed82e597504378f93584af394922da247f0631ec17835c50217ae6f476c04220a57054cdd47a98bacfc724f2ab5d7c7825ce80f5978e502f847663637b90af2d961188d67c326e5be62c40cad44d5d128f5b18fec898bb36bc426c93f982f6d470d17bdb5f79943b681d033607bdbe16be4da1a1dd8fe1e61ac11a1f2b4cea2a3fe26d2e66df0f886c561d05372a059845d520c1f729b37c5ca4093b912fd806d9e15d647dee4fa61a39a465c97ce7cb5a3793b99986163121f8545744c3ad753f82ffe37a90ced4f23815bda2c9278462546ca5ae118e198c4097d3e7dc28e03be11f8ba469496e5bd7d9b6bf4a8b5d56b480e205e03e82cce8b21f464a4dbb6d52374e84c1acdfb69fa9e531159ff2f6f261fab2e661214c69bb7d93f5f0101ec12e85651e59e0436e3af84b244b29464d5237e0de9f96b68da0e0e756b013792218cae5ba282e49b29b371bb74fb66dd8019240639ca2424c3e739ca210a056506c0631528a20cb89f1c2a8ecc7dea173b6ded9a7ccfc9abe627af2d7ea6591a151c1673a09d84f1132062ddbee11993003c72dd6f4de585c418319fd64fb87e18f994b3c90618cd566283a36193487235f5e20b3eab9b15e3e842246b3df0720ff304ddad5e0d9b443938d38b7c6c8ce9341d9ae8071016a21fc694f170002d163f7f12d068f82cc36e51ff8eac359d86fea4f76ce490557e9c8d628cd2ec7311d270d3b58ff7ba7d52f5d7d3c8903dba14149a80ab7c9ed09988a5ea0b82620fc56bd2241ec60e84caa696384f1094b28877aa06e1061600b4e581ab5bfa85176412243451efbb1dae13d6e78968d3476151211492d8d6bea0a32a28eb70c814fcec1e1a77683784680cccec1bc494e96aae146a4e663a8dee882caf2895d80be4fde7baa3a49d24c53c0bc5f54b6367c26837215974a6cfa2ee738edf84b21115cf41b00618506749df9221f329a01dea694776bf3abaa9fbb352f9454ba01fe2a5c04286be6419fdd0afb3d051ad92a46178d89410fd158b32d3625afacd0232156648d27127995b308f527364b3162e0eac7db4e1c9d7222b263d2093071643a985a9493381f9224fe080f7c49e0c1f178b1cf486bdf027b5677f67194f17990a030cd95227782c3ce2d6184ad12c5988823233924a13ec460a43db62a518a59ccd5f1069fc98131ca373c6b618fa7792591edb22dd0c242b5447019f57229b7191aabb525535024a14782f1ff968b3a1e8be97503ae7cefb6aa8fd3a352f3db9f3b9a9e6ce148345848e3988651f5472abfe980dbb8743ac95f00720131054d873d7ef9d60eb7bdd6af577d252f486808d1e8449abc1d68b69f4eb56811a8ff281cdaac4ef304f4582285d5e403c5367a43eb7027b36be9351bfec94cb0b607e652ba04957a4c0fc0482cbaffaf8932b90d94796e989278b0d100b92f826b213ac5b4b3b8f3028b80f591d0274f9a65fb57e385ecf47e33ecafcc031ae2e8c65f2a0175b4ec3b0ebdd832f955e11178b2591b7cb64f3cef1263b56b4bb2a77690f4fada83c47188bbb06ad5c506a2bbe442b763efc4fe3ea2fbc21e7e12fd3c530be1d11cd7e2272464c70d36f6a87717d45c02039d089cec0e1e812bc0f0f466212d8f19f9582712cc3d7f4de6c659abd9ec8682cf25bd9c827349d1f2f9b83f6082eb0d1f5f99f966f8411cce43872f5299bdab07880b3cfa693c9da160c1460f44d22314988c3a6dbe5127978d96a9dc6cdb3e523ff3a7dc68192cc133676d1aeecfb4209c6cf678cf1dade494eb15819b2171f776abba11bb5ec951a46ef47e4bfa4cec4e833fcac0a9fcbf9ce8fa3dbba049ee45162512a1d8eff00670e517adc74f7cd0627f765323e0115bf5d66b6302c28b66300112c7809a720ea9b409f976ebf64fc48d0654945028900fed3563e17258bb0ed64d410660a2819a435f89469d02c009c5d5c810725651ad230e22f758768ebad58dd9559b527cbeb59f0d6c6bcfe965596dea8e090fdbd70e9f18d5d6199cad88165f329d612b8518edc1bc88f0431bb6999172a8802c4ed1c417dd26aca97ec6358938353292e3ede19a0fd0e699fc08f072829424760b71457eadb10eb25e52987144f92080e4c20439d7bf3fc26bd9f95d7c87dc30e6f6f5ec033a751f2006d050c75961da6b26abbed3ba81398b3a374371dfa96a1b6ed1af5b9794c5789d0b9a912181e090050f3ef76dbd74eb1b18101e748671915502be94c168d34a44382caaf3816c14e6456dd88c14d8e68e2a0d3624e29cc00d6d0086d6fcac00011208602d21276b44c40d08f7edb5f2dedbd3e7a955ee1f92f0806854cad1ff3bf05cf91a3103b631f8c0563591c5d1e70e97e16b1c7d05b505ab1643e49146500cb77eee01d79a14852e3fa0beafda3aad1e41941c3e2c9dc4c4630f1218656d719506dad0a2a35a616a505d4ce99fe5e476f59413c41b02fc17c1c6dd0e2a06fd16c203e0d43613f60be6b6ef8389331dd4fedfc337d72277d459c4f6a5f6f5923716f2a1c23488142dc51a76fe1cadb7a3a1989ad7e79d1e63b75e8952f85ececd4851f22098299a53a0608b5eefe88e783328a37526cf50b972c25f782a2e4b8c0832c302ce119c943a5659209e9395d43a1d411fda6b050c14e7e1ddcfc24df068e781c194e17b295ba95cfd03df3c17d0ee6a0e36cc8f432b6c339d1d81bd80b621804194513ee0d0de54e0c0d2eb0bc3900cdbaaa7f49e6dd212d386a15507ef3f8200f7c2a499aaba3d59ebee961ebb27ef96f5c1d8b11b46d6fab6162a3f22f8315578c760b7d247b7c7aca1925eb17442fbd0010842c7faebee5e0d3df7cbd6901dd2c01f4ef6e0234a6b5ee31e14e71a44f652f51db365417e717efd7cf0ba920e728f72b1246ba4a66757bc3d6986178a01c5818dd0a1a4fc7ecb03465903b103629d6df9333ca804018e7d7422aad2a87673bd59d3b53e429b9abb82559921620f508d103296b6915ce66e75611f67f8500111ed1ad5139744aa1f4828c3d07f2bfe7999bd6a3eb38147a887de506421fedac4bdc00ec8138f985fb7d034a01300c4b88c4adc6eff96c0c86ae338b902caf73936434123b14e06525d0ccd12ab7cf13b2e53f7590b841b8fe4aae1252fd75c40e2069390e5ffbb7ab3674a028d85230eff2bf8759207cc777ebb790986beb140f46d20e9bc7f4ba078dd1cc05d91b86bb252b812317d54453271888c960dbea5ca6d90195f0dfbe8e9c5d9203fb3c294d6b473b0a16ff79ffdb3ec24daf6d59dbef2fca6013dc937fd103572c818726e3561594626eaa1f45db3f64a8964104a9272ab81af3a1c6bb478adc1020f11aa1bd01ee31f58f4ebb848520510dfbc05408cc2787e367ef0c31e41328e0accdb8de5dc22799b8805f173931694094f7c0e5de52dcd41d815d5b6c7c2daa0496def1b376e3a51d49025d9f4041715a2b593d7423d0248819a54246e5275cffd211d8ef63b85f2b0958c61c32d58abbd65ef70ec68ce84be78310d6ef264bb4a81ad1622c4fb0d54040f90d90786174ed1e353935ee3f169a98aa2d58029207afc41769c02bd3deca0c5403a93efac655b6a56ac4d4838dd6a08b1e1a1410888fe7e934796fbcc11d77c3b0c2edb80437a769787966939c9cd55a364f3061778bcbe570cdf79ee300718671ba9baad1c30888faafd754749d691e71801b9821a796991690747083227e61d17ef7a39b511cd5b007568a1758a9395fd016df8a16e4e56ec6379bf190011628638fc021a696e3bd9603d9f3fd7e1e2c571a4b5945b7cf7dd86b8981ef6b97b7bafc2b22a55bd6368091f03a645ef0df97279acee5fa55dee3ab0191d1633027c14609a74332b0eadd3d6f3ed85a7956680f2f1c5a7c9445d8390821c05fcc9aadabee41ade1fa32e9421313381aefc7b3170feae96e81634e042eedc9d30d9e1d523a550d682760ddc428c98fef51b1e221048fc6822f1c3e89a26903106cb0cafaa69eeefdc7ef3305925ffeceac96e121206290ce9d91c8ff16641033910e84b4bd4bc1ee56db43b62ba86dfded8ca113d00114389bbca8cae5cef3c14f725ad380e7d712f7d93a1e588647cfdbdc5402c7d4a698eb18c2bdfede827d90ffacdea8cc298a5629b28514bf731b7998f82e19b16c332526bc1b3713c5b41b16489bb6228f176ee6f96cc286665fc5db930036af42600c67bbf26009805dcbf054487b9b0e1c2cd7173f38be7459892ec90652b26f72abad8dfd40edde3511e0898abf30e957b7e70b725b2653cec4234d180fead76b8ae6be8b7e18ed0f6a6922f146b264ed870aec50f2bbf7e4d178b81fc952955a05a27f760b0adfb14fd86c9e21c4b6ab0074e6df091e08653b89d324e1e066a0f2825afb9be08e90f11ab66a57602c43d45ab5f7d97ef60a8670a61fdb46b341552a18fad736bed3ae77890a408470e7c308696f37ad26289f8a7f00cb96b31fcc1d4b0cf0f058943e8249b5723f031c2a02d659c2a761fa0d05d002781fac13ceb35217f82bc90dcc84665530ef2ef7e326470ca0fae8f6771a0b181d7f987290f7946bad4dd103156d149d2f4d7fd9350246dd54b274f5a9c2ef256421db001f48a5a3b95deaa6967db143533599dda5154770528c1c4a01f2bc246ec9f2caa1539470292b344cf2ad33e67a96fdd2e7d07450df1ea9e5d36bd510b5a2b1d115be0f0700e1a5652fe4b304d99ed587e6ff6955cd02469a5fecf829b17341a8f46cb83222faacbd1f539b89a186ded5664e49c7deace0b5233b10275cdc7135f9be5e3c62a640ca27b22b92456b9e6f146bd6e805cb071d32b2dd00449a613510837c67588fa39fe0e49fbf31b99b5144c9476150973ac9f0a4591d17e76456b59e88170401c1d4a92b94f17ec904a2374dee427b7664a6b2c22e240e1ca86974fd9e5e172d8647abea27a002645a90db6bcbc5e88dff24f1743323be66dd4df719696f2bd75926dcda82d3accd3c865cf42853941ef41de95af32c261b646deadb7d82ddb53574514e261a14aa12cf773f483d2545dc4a45105e23de104c8ca2eaa8d94c42f4ec7c0e11279a62ebba987e6c329eb49f2037b8092352ce3c21318796a17ce472d8ffde018425fc0b4c11f3e492475d2afe0c9ae00b0cdb4f51df51cc833ddcaa3aa78b74df3d936893f0a5714b7810fabdb17c0810d19fa485bd97f1507a77fb95b2a0c5b1ce5830dea0de18ef5f6c20a00e15d12331f7d203d3a5c9d261cdc4db79b82c59a02ec3746ce141a29564fc67a7e6ad0035707ce5a02d5035190ce39d1ade935bce8d2db6bf855b748412c8f6b441480b1da708c7587d576017bfee5fd7339097c4e205a4f0deb9385a3a0f7806fbc92f5490de7ba18539c7d8c659406341579581b124dce903c88eb72a4f4a86eb22093addd77a4a8b8f2fe48a564d820eae33127cfb442fedb91ed8eae5381ba9f1046e54e4e5c6887713366918f0ef69869a1e1c1254bd5e205917d5d5f7571a2e1c9b575c9df2ea2c0f6738f4a4206330ac1320b7f16f1906c4b067a64294082e2d754c8fa48c6470cf77edb7e03bb86f4a396b7ae2dbc56fbef172ef0cc08caa219968432847a0b5b933f0498d92e427b6e0d75969eeea525ce41eac022e468c0514420c561aff6c8edf72164ace360ef5ffd7cc2ed742cb8cfbaf855a20491a228ee0455b1013c4b2788e4afe5f5c3cb56aeea9c0aba0c2a3a99939fddc306c2e0d4dac3598daf10471cc14ebc2233d710393ea6ef90252aa70e609081040e814d26daaac5656530fc3586cef2d6c4743200ad764b5e4849b3afb18e28c13512271c93f14df40b13bb0e4ba277e2d20080be06d4b74dab1c3b51ad544e763f125ebcaf349f694dd1f6b8c60c2794ee456f96176bff72351704e7620228cf9352b65dc2e4ce420d8ba84e849a41e2986f0bbafc592af6a2eac9a74d6646379bf22dda3078b0408cacf02cb1a4fb0d4effcacdef4e5aff14cad3d90b0b79cc149129a4bdb67b7ba6c4333b1167dc29e2c02ae1130f373e25a4e602dbe1f9b88532001917a73ed406cb1019c2fe78c0278c373405583d6b76103ce83ac88def2ae0a8be4e4d15523c70c2baae6d7a2d10818b8894c593f7dac6103a75be2c4f6531ed679400c7fa71c82c9b8856c0d03dff14d810a3863018124c30e248a61eb93227f470138981ff90eb35b5ccfb27b22f9c3e99c4b8470896d7618b570015aa826ec67f903e741cd58a559e1c5451579265ebc761ea6a358754df10ebd689c5218df613d6ac78aa2720071cac7fb34ad6ad194e3e654b8a344971ae9a0d9fd530f3fc109ed0f01a69f6ef0208f0edeba13cfca9d6fa2999f44beac2fdbfcdd312c3132400d2921f726b2eac70133a0dd787448687c557fc1e69083e8169747421a39b826c54c494f1ac98dec8be7e330044daf7a0bf4eef11c79644bcff8298e0dbb24bb3a782d13457901c8e2ee0609dc1247478b2001d0d6c9db518ac9068208fd83bfe7d3508abdfa2503267f7a45de285633535685b6da31d63d4e2ff0720154ea6904ca630ce1857e3e8a1464cb6b5b16766638074546226ab14fde7afe2c0e86924792862a3f8832e46311b61bf635a27a61b3cfdffe662e7b8341fb9514e51b6f5688113dc4f5f812945d7a8ccfad91ede7fe0a607dfae5741848c88f01827ab0ecad4343b879f24bf632dcf9ce5db1568b3dcb0e92a3ea5d32bea5bc06fafea5a53fcf97c3dbe274575ef305fbde8c7eb4bfbd8e48bb3b062bce895e258826ec523ced0e17c77f73323f26abfeac8c7d8e401107f7240757fd4490841615f11738971be20699fc6c8afe8c9cdc2a78df1dd876c7265f3824b959bb5212c9b389a43203fd0b16ca64615dd125232f77f77153f9983d1a3c54b847aff7022cb4a694c0f04531367c7cd278a7f7a5aad796dca8eb486791d297a593f69912bd5a038e02b40783d1345f2cb58f787712a1ab0316cc7bc64d21d6035363f51a529233a8e89a1dea82a455dd42bc3795828066ab8dcf048ee7351d55a9a7870a0dee695599b4a9bd9a6a5cf01e1dbf2d89ce252c15cbe5271c2c2815edaf9f05db450c973db3930f9a551e2af7210dd55bcdda11077913f597781cd3a16457088bd5d22510baf958ef5e5d75a44055207b030f5bea7a15e7d6263aa056e9db1369a3521bb7832d51831151d9344364cc9337c3560d387864b7382aad762ff81f0a1adb64a26323cbf1d2f2be86ee22c9ce9dbfa51860184340226563b1b8af04deb0a186abb9d3d399c0a8548582177aa8378309d08aadd8aafa187508e9bd146d89b17af7b31920d5e2f7ea2b5881a86e81376d927a6a75ef1ad8dac6140219e02d5d2df2b473fd67a74b14fde0df6c9e9bb9253edb6d885b748527f90b30265690d0032e77862cbe2c5157330f90e10db865899ba2004fb7aac420bc40540b027d6a2389de4a85895dae7e6438772f59708f6ab2c0a0e8949d890d15d5fb14cb09a333e1d04ba6263268e7f6bda2915967987f0d2e45df0eb9e601aae96312e39e1add5778ec0e13ea0feb65580f7d4b73942aa5fb43cef4260dfd71cd6c25ca74631d5f182895f93bd853d8fe8c13db6e6a3064f4984cc00f502049db6b2f3fc84e7f072beba9e1e0da592e03103c6884b2241da120a6ce8f0dec4a023e0bb68670913ad55bd31adca44ef3afdb24a74965e6a5a4f6423c682b3f2505f119b8b942abb373d83f159674fbd223b4082c6a943ed5f6a3c0c30b4cba04c56d296e61d11f994ec45b3eb7c0fadce0892b09588510deeb84ad0fb43af65800ce1cf2b42a1f77afa03429b0e724ee01aeed3c0f0da7c26e9e0841f6f3e6995a6c26394ee8c1cf6f966c69582a31ecde78b71a2cb6980da3b820348502c53b6e762025993aab0dda298ac2c722b379d1769978b065166b59c953e243260294dc7f0811a8e2eead9e087b065819882752fca41693873712be6b9592ae57f30124af16a149d2e3e18080acb56723895e4b6b960a2e565c13bdbd7f4a56947457ace609911477284348841790e22ffb6407be7a6a3cc3cfa687abb560e83b8f95b12f11ee57180949ab894c6f63194f9795bb301e12258bda6bd19eef208895d3646407fc4b759cd026c4d8859b45b2db5951a4567b74ac30cb25763fbe11ab5cefab02121a2d8d2f4e68fa8da95a1d857eb9c915a24e6d65cacfdfea17b4f7c20b3a2a449be2dad4aa3acae6e681796e2e1c80d8e2be9ba6f33c83eb5efd04de8fca143d6424e42e247559e0d90173a80aa319d3698c58d33eeb2bd7224c03fe44de1bc549874bc66035e4ee132138100bb60a755a543c6598a5f06e0ef5d37d66891ab1b930794b5c2f560004d2356bb0b6d1e1bee7760b5f7d6e676b61896e91e72ca9789023470f3e234285e254c88fe67302f0c605e59670a402401135af417519c6454310e55f64af70feea1e255cee278895ad44da1a078bbf0f676dd705f605ff24647ad14d866c1b6d0670d72dbff0f44d02b049695345c6105d280c45791a7d544c5ef1225e1fb2bc196bb9aee9c3d7f0ae59c4346f6ef9e2042ce4906712a0284ceacf4e137c3e49b7305912c45069a5009e94d0de009b8412b1c101bd13792df3d482832d5622d9765b0829e4ba254614a09747aa80f7f11cd11f63c20711b449ae52188e09085afc42d7d3217f74f29686c3a5e4936e31ef89fb578f2655924c25b7b1a31ad2f2019eefc28890279d30b8fc665ee19dd8356f9eef3f0f754a9b70b8dbec4e7285dfc7f0ff474334c6bbdd8cd3c28f964fe49a17f0da160d18b7defd79fae2b4a2a0d713034f58e89606b1c905c31d316d878cda2f331f4f17fd9ace21980261985cdf4b609d527278aa16ab00b84f910d4193f280190563d7ed91b44e80458bad71e43f4338205ded6dc8fe68809fcad4460e3d1ddc2d2a4417c4dd117adb272f1fd4b5077d3f1b108fdb6648403ed5af2132302a2b2825988faee937f12c17e377767a51a562014dc54f7043c27df06d554e61611821961b9bec98d5c2570c9f6fe8487dbecc40137e68fa352623d570be4791c99f0800ba8d3b538ff0c41b13d298ba9924a3f180d85bcf37e477edf58d311f11af03fca8f7e3bbce16b2162db1ba146b4e895f914de18f120676a9471bc0cdcddf06d65a86b6b351545c1b659a08df6f9e19f66c3e0ac434fa776037a4e5ee4b3811b508a56849c1eae0857281bf4632fb10c571f2dc118a695db6f242ce9fd044295545f486838cb226f250ebf115a28f9f3e6be7b7c89876e31069d9ea516f0815289ee7e0afd2f36c1b2ab344cb6bef65eb037d2e4d2f16cf5504daf24290c00bd40ee9122929b83e4254115a940b02985fef66afdf26bfefdccddfbe2f25650fc238758bac9ed132a290886daac1dfc82dc410475fb48d8cf51ab1b19d73d829b0cfc486ebfb3a89eb25393460833342fcd2092edffaf42d364b7d80fd766a1270ac3e8f5c706c97f8d90728a2839a8f2bfa3bc81939a540515ee8b6f4275c1be4ec2588f003e9474418d88830187c2ce192fd7c69c1907d0e73826c3f86390c0af6fbc19e6a0db23b87a09ecd4de039db42fcb862c3315cf4580d5345d32606bb0085509bca0d4a5eaa1f3b0a34432db80b51b372b3cc68a16d16edd004a70796d5b65c46f72f16383ac710b0105a0a9f4b1efec577d5c4af544e9cddbf942062e58be832605b62a9c89c818605ec5eb258ebe45ce66d99e1df8ea696d1ea07f1787d1346652c451651f25e1134e6cc24bd4557df3930d2db2550f98653001ed3a64ac328546bf26e19e7dfd77c354788d256fbd110b9c266dc66aea7e2ae0b7cbf43d883fb25c91d1b778e277812b660b5fbcff53511027ba6d54afbd88d1adb25a5c52d05da1438ce5b88f7f61917547932ebc310994832fc7588df225b1c393e22f16abb8017e1118e20736e7844a3fad04dea0fce59cb6d2f477904af2cd57babe35dc13c3964fb7b49be5b34f676fbcc1123ca7063f42fcbd9d2f9f31dd9dd09efc55e5dd647b395c73e84834380708e332436b329f726a0a7ed75830f110176b0e1d9d3f313e2d89a5d1e0e053fbf010e3e20fdf030799fc73c51cd2772ad06cfd94569f5b8a998cba9dde2b6b4e50ac35f7970e38b814e063c368407349b0d618635aca08298e56598879e02db373cd6ed499931c2c617346fd15a23d8a1a020f6b71cefd7744a8b42921d02253d8d84db053065ba311bf7f5af1401196a38b0999ec52fbe6ced3f7464ef2472f4bac6cfee49af2581c31e5034980eab6404e8aa8cadfa913a7492149b7bbb8dc6fe77616870c2090e32d7ddc7692d0fb3a73ab606111690b73c8959bc3af3755223e56d07e34c8e4d0b492b3bcb1b96798b13cc92b1638a3f885d83e9f30b14cfdae34a307c233c578190d65aad0f421ecd053dea61996f8adf5906cbb7dd5e7a87169580a1ce5328d3d0937b28", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000335fc711e25eec4bc7002806d9b00c9600000000000000000000000000000000cdab8c25e7ffe0274955ed025ec557bd01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db70000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } +} From 6faf97d1b0dfb620be38733a708f158cb65acc66 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Tue, 19 May 2026 16:15:33 +0200 Subject: [PATCH 09/20] address coderabbit issues --- .../results_insecure/crisp_verify_gas.json | 60 +- .../results_insecure/integration_summary.json | 102 +- .../benchmarks/results_insecure/report.md | 84 +- .../src/threshold_plaintext_aggregator.rs | 67 +- crates/sortition/src/sortition.rs | 8 +- .../bfv/honk/DecryptionAggregatorVerifier.sol | 1635 ++++++++++++----- .../bfv/honk/DkgAggregatorVerifier.sol | 1635 ++++++++++++----- .../test/BfvVkBindingIntegration.spec.ts | 6 +- scripts/build-circuits.ts | 12 +- 9 files changed, 2568 insertions(+), 1041 deletions(-) diff --git a/circuits/benchmarks/results_insecure/crisp_verify_gas.json b/circuits/benchmarks/results_insecure/crisp_verify_gas.json index 26f51823d..ef433d9ba 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": 3042828, + "dkg": 3042773, "user": 2972965, - "dec": 3553758 + "dec": 3553807 }, "source": "folded_proof_export_plus_crisp_verify_test", "artifact_sizes_bytes": { @@ -17,17 +17,63 @@ }, "calldata_gas": { "dkg": { - "proof": 170004, + "proof": 170100, "public_inputs": 6144, - "total": 176148 + "total": 176244 }, "dec": { - "proof": 169944, + "proof": 169992, "public_inputs": 17292, - "total": 187236 + "total": 187284 + } + }, + "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.112618042, "runs": 3, "total_seconds": 0.337854126 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 0.613634805, "runs": 3, "total_seconds": 1.840904417 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 0.566882416, "runs": 1, "total_seconds": 0.566882416 }, + { "name": "GenEsiSss", "avg_seconds": 0.12575218, "runs": 3, "total_seconds": 0.377256542 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 0.229232416, "runs": 3, "total_seconds": 0.68769725 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.659371958, "runs": 1, "total_seconds": 8.659371958 }, + { "name": "ZkDecryptionAggregation", "avg_seconds": 51.718500333, "runs": 1, "total_seconds": 51.718500333 }, + { "name": "ZkDkgAggregation", "avg_seconds": 20.93329175, "runs": 1, "total_seconds": 20.93329175 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 1.496832486, "runs": 6, "total_seconds": 8.980994917 }, + { "name": "ZkNodeDkgFold", "avg_seconds": 63.680252444, "runs": 3, "total_seconds": 191.040757332 }, + { "name": "ZkPkAggregation", "avg_seconds": 2.149436333, "runs": 1, "total_seconds": 2.149436333 }, + { "name": "ZkPkBfv", "avg_seconds": 0.345419874, "runs": 3, "total_seconds": 1.036259624 }, + { "name": "ZkPkGeneration", "avg_seconds": 1.385717208, "runs": 3, "total_seconds": 4.157151625 }, + { "name": "ZkShareComputation", "avg_seconds": 2.745120014, "runs": 6, "total_seconds": 16.470720084 }, + { "name": "ZkShareEncryption", "avg_seconds": 2.550104026, "runs": 24, "total_seconds": 61.202496626 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 6.228828042, "runs": 3, "total_seconds": 18.686484126 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.10385268, "runs": 3, "total_seconds": 0.311558042 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.240555883, "runs": 5, "total_seconds": 1.202779416 } + ], + "operation_timings_total_seconds": 390.360396917, + "timings_seconds": [ + { "label": "Starting trbfv actor test", "seconds": 0e-9 }, + { "label": "Setup completed", "seconds": 3.083043708 }, + { "label": "Committee Setup Completed", "seconds": 20.233524667 }, + { "label": "Committee Finalization Complete", "seconds": 0.006022792 }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 309.654077459 }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 312.282717083 }, + { "label": "Application CT Gen", "seconds": 0.316875208 }, + { "label": "Running FHE Application", "seconds": 0.003470791 }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 82.613328416 }, + { "label": "Entire Test", "seconds": 418.546447792 } + ], + "folded_artifacts": { + "dkg_aggregator": { + "proof_hex": "0x000000000000000000000000000000000000000000000005427a2ed994ffc252000000000000000000000000000000000000000000000006d241db063da6026f0000000000000000000000000000000000000000000000043626bb84a690ed04000000000000000000000000000000000000000000000000000193b944fc656600000000000000000000000000000000000000000000000afab2a128f843ba2800000000000000000000000000000000000000000000000ffdd135f92e6eeb130000000000000000000000000000000000000000000000033876e4ff9051c64800000000000000000000000000000000000000000000000000022e934d408c6d00000000000000000000000000000000000000000000000d2aacac256fe3719700000000000000000000000000000000000000000000000a2b7205fe21b014c600000000000000000000000000000000000000000000000b16c840b7ebe5c0ec000000000000000000000000000000000000000000000000000160d7a729e6eb000000000000000000000000000000000000000000000004f7bd1f8c59cfcd0a00000000000000000000000000000000000000000000000c6910e92c5cf1346400000000000000000000000000000000000000000000000ce9d40d2c6e2c24a5000000000000000000000000000000000000000000000000000194c4bf6f99841c44750b98e8b12c09eb7105c405d362756f8b9f4429e44ea7c476067abed031301689e3b57f19c1daba697f74bbedf39016ca9ce34cc521368d45dfb22109a11b835b6e94ac453a3ce5898868a4cab42d8d35a417e9e3e86d00eae06dc3661d2fb487229e2fed8db71130092935e55d256a75107fa96e6a3a23ea8cc2e8336429ee252bcac0ad7229c8f25829d8c533b22df0c5e5b036013f05f795502acd8c08ebcb8f199adfff8d624b85f06f4babeac2e9c483addcd9c1d37470eab5079615d833c8c924c55f6dc49bab1af3749cb6d50a775c7a902a0fa98373fc9a3b232601603ba65283002be13c5c3f80a74675d2f98988b112a4d232f0fadc06654800c72ddd92d4f108f0a548bba472c75610544d4d0d22a3ccd05e914663e93ec72c348e6b39f9ef1df2f0b05814f5a86a80a796bc3980268972986c0db0ec76ca028774356b570c119159a3b6f4bec272a8ac2b65e3661483419b1ec82bee728f1e83b88ffdb8c2bb668a4963de3f92604a0beaf90d2940eac097c6c5b1ac8a0e187679b30d947880bc050d437d70d6a9e52caeff087dac0a627fa343b48702de2323ea003f71de4b143244e858beb7032422ec4364222fbf540117b69ed27baf0210980aa90048a07e991238d24501107ff46264444e1c1a8e3fb6cf85dd12d70d25e7fed1ea3cc18e8987d7df2ee1378d55b79aefcfecc16d9a3bdbca3d4c2503ae270dc55614c28019386899d2b1473c7ae4eb1fa98cf8646d224f5ea13cb22527c61737f9958b027b06fc66f27a797ea8c10d3002dbd12864e769d8051555195d4c402d26641b7c5d862396c5a6618d1e54462b620f45f116a524a5d130331b49e391d5adbf8ddfabecf18dafd472c03caca3e7489467b8f826ef9bc7b16a2b7de443763d311542d0f7ba75c4ac61c381aa883a56153dac0233b24c2b1b542ea08ab907ab674a9440074d582141a10963f2c3fbb8407e1b8218f1f6123df80838bbc699ed896d4b98b7ce92e6ace001189eef52436a0a7ed249e60142278827e4642ef254526283a7d17777ea4a89a5ecf102c3a81957fd56a4864dbf836a1fb504bd8e7e3916867f161e9b397905e7877f6d58f9e1a48a103b7096d5d79f03f301693b397e9169cf2e06bf9daebfb630ab2e8cbe7461797dd834306b712d1c4f5696801f0196f91ed965829065f4591b95902c7a034e211d26e41b1703550b2693c21a8b711573b4ff021892366ad8968a2104c9485c11e17f8cab541abb01b6fea8d27f6e153c10765b27ca4120b6eeac0045bb8d9d4e8a3cb105601c442305eec51c2887e768d4d51ffc323225deeb1b540e4d0e6b12c82424bc7644a9179487f9375e9ef9a421f7a709413ad73725381ff8676636116843dfe97c01a71e4c2af2c1fcc6a3835296d729383f9d1b5dd21656f44256d4a8355187bb79e824f8c064c34d9c653b30673b3f5a2ad6365ede55baa021eaead52c7c417f46ea1420f848dec5ee8e2e4496dcb32445beda9d72b0e9dc505ac5471cba4826b7212ffd8668e33524493f12cad4c336f9425e27e07af458f680aa7556d693d2429b23516e5a2e20f6ab4f59fed1e3033f0b712a76bf4d235c54ede97fe9a387caba10a43e1509f0e6c163f67dd09015a90ff48ee7543424f51229b33de99d428a3004ec53f1ff45a533b356cd647d2a7f6339da8f8a4f6e0c097ee7fe6ee7fa62a606eed939e91067552413b42233ac95f4ad56ca26d7247199d2b53abbbecd602f2df89683d1b224587ea81d8ca66defea2c003aa77c467cb0d51543a04e401bf410126537a3e3feef528a4f94de279b833418f7215d57c1cb2c47fdc166d9ee24170d246422ba15c5d96f5a7c34c81d23f7a555acf5f67229eb9e94c4f1694db2134a751fa6280f3cd09e75a041662a8cc0b266315e25a354a58b8541ac7189421b4a57c8f1daee946f60d115ae4430dac1bc818c4f9ec244b60cc6e659835e6a0b99b702cefacc3b601622faff354765deee0c9e0f62aab394ac7310238e7096298cd07fa6af7ba58089902eb54381c6da51b2b225fba65ae99a1c1d724e9e9001a6809bab48ee95c1baee7ae05f02204d143994e977100e0450fb9eb073dafc26d1adf32f6f143dce3846a965acf99f8921d68c5553c893ace6d30b7f42e34a07fa85456da9930a7ebb651e2e8720b7a1e452dac68c32f9729bbe8ed10d66b20494a03bdc16679631aa96a19a495f0445b5e2341c843207e24b29453364742f0f4c48913f038624283aad35cfea5961543f74258d9547ccf86df266330284f6211282a6d25eb8a2edbd5bb21a1fc8dc5f807f78d8c0cd831b1e8ef8afcf80fd1bf9d7779d73958e3cbf0164cb6eb9512ced3961dbd8e5600ad7db18adc5b2a206c1902f40e0c388e7b08ea147a9028094e402b8fb5bc2c6d030609ffa3cf97c208fb4414d508f49e22ae7baf57479677627d470e20807302ead225aec2f3e24014327ed1cb3ef9662b9dfe2cf4fa39ea77b63d10cf93b6de3cb38925c8f027f171595a5b16a1300ef2a4848dec9867e0d36f27a6797d046eba20b60fdd214622084b25b40c55414595d095b4d3d8be36242911213c161e33b2c24d8d9482e8f227c7d47327840dd14a790d202f5b1b4d7876ebffbd5c8b59bc1c5e72d1562ef24befc8f005177e891a2e17a029ac9415338824b678ab58e44eeb753713f8b251611cf41d1bb18471d26aeeb7701ea36aa93d2a102224cc1052588f394c88d290e929cba7d61959b04d5b033d54766aee7185d338b31678b3fedc741a666b23d27c74ff7b2593eac40a9945a60e73c7cf7975d440d239b00c7cad347d824513f1c8fbddb5f754c292178eca5548bee9d306b6fee243801bdbcafa9d8453d3a48109d15ca04cac8ab7b3c93562b158b4bab0cc051eca421ab5c8fc6d4a60a58501a38feb0a4732c38abd876e0b5baf947dfb0f3ca987b736ca8330e4d42c1e05426d3b08ea5091826e4a32c5c4ef1e2b6a9e80a5b4762e887959e79cca1e94cc00c0336da0f387a4fe57fe353552ba8ede9e31f5b85c3658e5c43ce251e2ddbf50e166c29985ee8f3613c648146814b09dde9e9cd78f2a81ea3f73408280a5cbb160197a8b62af1814b64d002058947d07591550cfd4bad5169df7b8779d5a2a5242bc8ec5e8a35ece688fc7c6ad09509eb4350eff0e98aecc9ed266aefcaf6ef0075f5b3bc59150d2992162620938acb03376fb7aa0ce3e2283beedcc0db26942c77d59950f3e76c86a79a13c07e45161cf812d1d393b734d62dc4c215a39edd00c16adfce0a0ca7efb9dff0002d60aadc0c6e4b86ed6814853857f4f7bde5a50a37e4862b3de19096cd6b48b6d609dfc6583362d3fcaad9a79a8238c75d2a6402c0ba6956325bbf412d787000b9bfd8b0db6e6ff78e9ecb7f38845ba6818a1904f9b2405aa88bb415a0b51e300fb260e55bec6f1d916acc63148eddf10666a41fee9f325e36e4d3b1789ad36d266056cc53b36576ba294a1b4b6f904a3b80cf2e6fd2b7d3e95625ef16d9d8eee6dd9661c916bb2277baccbbfc182c9c74c70e0c68efba43cd58dbdad21bc9ed58ef09ed0b18022da877828574c9221779f70e04c9f0fcf05d526b9bf79729b673f32796a0f1eecc0a508e91b925086f337ef115fe2d0c1fa9e9691d622b086685986060ed2b79d0c0a6c54a299cb32c17c7c3165055ebdb2d8271325bfef3add0482b3497130d398804d69e66f889b4bac75c0e14534a4b5b12ac8dc61ce4165cf84241c203655292bc6a7c6c54f1641e765c2b27f416297deb2474a50b7d28a1ea3d805fc577fce06e286160663d75e63d8125ea040c1382b541d5d52777c5beb5fa5b366ebecfafad3e36f5b2963644c17113be9ee83fb92303a34b5163ac1709d698c952a0113c99259706eb0b0cb57aca238fda315a015caa19b67a67b2e9df8ce8694f7796511e8392a2df820bac8b2c2193e4ad15a489fdd141eebfd24e917835fd8fd958aac6eae3299acf418ee39a1cc2183980e4c09b31b4f975e587e473c09c065f5051fe7f31f1b361973bd87719f484814aed4ad2e448b3bc99671ccc1af77900e747dc0e90b6e93594de697a2cfcaac6c33ecf598072c0ac193cad8dfb027fcce0fa7c3ea275ede47e683dde0db3c4ae111bcc1ab458db20bff8c117d02239a6c1c6ec4d21a0af673ae1a42a0044a0159c1e23e463bd6b60704fff68443fee73d6214c714aaa793ccc339e7b06ca9127af4c41ac7312a1d2fd7b30099fc072d91b7456936b4e974cc99c5bcd143fe8395aa5dd14b8be8948265e711bd9ad6cae938273f44cd9a0b840650b462d5503a9e2070daab4e933e1de683a011201dde145987e277dcae8114d49056700e602421111c15fbed4728c3180666697e330351fbbf2395331ad4f72ce09dc21548ecdd678ceb9f2ba17bfc607eaa32f4838ac14c2ef52a0b18da26db7d52b1c5f3ec56fe48e45a0ddd8c12bd719e8ea4031d577a75b2064500d819de9f1af2d361f1382269adb6e42c5a0d07dcd2128e04acca36489d23e55e414067c606107e59f635e1d99a543c25faea760060cf93fb1558aea51ec8a1c4aa1ca86b34f08d7d71a093f5675c68c9d3d1ca68aba3975a315ad7d67914bdfb3e26eea736205a89305449ac7c3bdae9e169e7c50f962aaeea83d1c1aced16c84933cbafe5615ba2d0ff96a87f4a2d97f3778a1c19e12e0ee350f19bf37ac514c76c40a08da23161861f90aab73b9979b4d0aaf65a3c4bf61c066f67b5fddcfaaabb8293514193ab141d32a18e5ea09cb0864bc3e59ff46e85c9ec33684b54ca11b0711e7482505e86f2ed799ecd477ea8dc74c6dbb6349959cfa6133aa298bf97a16aabd2a1f1e93d03538ec2adf140f50ca5384ac1f51e9f0b91eb651af8566dacf83af7226be48e368ef21d810e272f1fd4abbea3ea7c0429032aec9d08cf31ef0445e2111480087e31fc4eee991df405ff8ef658acad605b93399f19d1d5251539e3c2a21682d0d46c9c290bf4025885c8b829a2ea48895c9de693b3d2f6215a164a33d2f91db9f73cf3343b53df247340a1dc2069eadea19cda684e22757864add16770049442b7451bc67fbf4d12ba4ef3b7fbf1034264151012cd735ec2135ca3aa8288bfbfb1fdd9dfe27d71d70335b9adb8a6211c9f6d954513f8581779ea50fcb271bf6e3d84927583aa787c969b8d4b7ea0e231de07b45b5625d0bce77b6fad327ced1eee216fb07103e815fa058dad6c9d633e1f828b83e38326c7535cba62b138eda58a22bf1ddf2447c536ffba078bae7208ed320e6f135e5c5cc8c69627d15bc7c974ed7bbd33ea0b38bc55dd005091f2e898aa0c9b355296c30dadf99972c6bc70999b9c727c1fc77ab5309d28ad835bac6ac74be8f8344c7309b1e9c31175f922b6a8c45b7ca7b23408d302925d94684700cefd959bc20d0bed14482bd0acebaed6ce3274d682f8189b88856c62616a84742619f43ac61e497d81ad02021a1116915fd972410f0f607cdf9b76ab6077239c7ec8d88127b02c1f99204df063d6fdce454305b7b6636918275793027def492ebced6e0f8c948a81cca82822efb3bfd155fc8b11c87ef4155977f9908b28913cba20e3ee547cfdbac774be30caa4409aba47a47898bfd62d126668b276cd7130e95bff2cfaf9b1d5f67566f18dd55e21a22ddd14363c0498ad2e29f9aaad8da7aaf2364f01455a8c66d51e21523a86aa8bb228ce37b3ab8ed758979805dea717810e201a83ea886658d2c192f062751fa9ad00f0c3abcf402901185f98b50b5830b479c5b03d2a7769d86dc29d7bbf1f14e3fdd96aea564df38a58a9d7aa7b4b950d4ff9b2baeb7fd12063b006f43d735123875aaaf324dee5942e7708afeeb616323a7824d01104ba950b01d2ed69d9ad865b23ac688494c461747ed3838abe69809d68c00d871a022e621121c45c397d5979a64469373ba2043c21d986ec9ff69d9a86e32e38005da9edb2c5536a42b6bea1f4a3104ad6d6f55926d7334d6111f664e66c1e1334dc0a3be19a0778e139674813524c28e93384f0a0307dd2e2d2906a34d513898c715b4900e816426eefb96196c5a24242463fa9eef879a84ddffda7408a10e2902c4049605086f7544cc27fafb47d23935c796b5a1c46a06665550d819875c4aed25f0e50ef7341e1fd6eec58cb9237d2a62bf3b686f4cf2210abe367284c7a91516fe6d29b0e24f81cf79efa422e642037734f33388ac21292cd636b3246b96f6c751a720bc206b63f51a1195d32b64932d61305bde740cad4a138649cc9f7e2b2f8edf2841d3b2a2a2469e0b59b584167915eef7da32455deb4f92268cc496a07be60f18bfed970eecba7e9d43415155e844ed87864ae0682050fff6343741b463be402367528d2a6631371c5f22995dd9878859be81b9dbb2cdff59b776f5ea6c6ba22bca5677b5ae9a4aa25c1c9fc3fbab0c9a40317bf22fb8627fb4fc7e858a3e2c11a231622b389f74934afb33b26403aa579863f0977e230c7df65088dc5ff97a1cf772260cc16803416ba1d76edda186979cf5a9725842585d7c77b2cd4fb9f61ce9c6ff056e8f8762fc2f355d6136ffcd49627ebb4e58cd711c56843b5cb4ed1e072d092a7931a57ebcad4e57e14df027ed837b8b3b0903b1996fc6a475faa11621dc49c44e03e9a9f0bba70b42a96d88258eaba4e175ff50c6903e9f30203c283a925a4ec8c62f69aa72d80be89e515fa5e2adf17db2deee86dc00622e1ef113db1b3c33a9f435db2e3081de6bf777aab27c72d645f4c8e73802dd112b9f0e0e7b95ed1a03cb6e0e20f121127f574b123b9dbd2f4744d8644deff40a3dfc132740bc41fd107465ebe01cf00e3786dfc16c78e10ac7f8bd29c0334b570f88b61ee6fa5a292a8b801a8a05d813b85a4eeb3361c6cb386bc50400af38bf92123028adf840f8368e8bf6ab9cc310541dd627c914094e2089037a5bda512edd26a50b4f6dfbe4d6e19290da4a183f24197658c8d3fc02f402119a98e09dd42f0ddf1b72fb646b22deddd2aecc3c747d1d7c96569a1df62f750dc0d5bb8d0fc62c481468e9310d37822c963f1b9fe1f6b8b2efa4610b304e75e4601e7dbafdacb5321f5479c51b931401e10016d89c54724fdaf6c9988d459af9aef1d0fb0d04debd03e3ec9228fc16a48055b55f810a72bf30bb0ee1ebb6abed769a5445cee071be226716851281d8c0e03bd5edd44b7cf510d58b29afd4e77fe5518816821697d929fb08d77798c06a1f0c77b9af14abbf79ee2c307fdad2d0530f71c98fb626710adb5f48de96d1e4f458d5f38607d927bd6c0d5e1cd68dea421d66160fd89c7f1adbaa32b1144f80bc89de0523293f2fd10208b780e7a7a33ef5ddb5f2a328a310ce90537ba9fd0a1c20278e249843ad0185cd639081f8cf9777e73ba2ff4d752ad5c9b89624cf8a16e00127527f0dc88e8a2f30416b0a6f1c68c3ee1aaab4510718c1fec2a1ec3d3ed44704b8eab63afa6b7578d2b23cc024f7e786de234e75299bbabd2698ccfe09dc0dad1a8da8bcf1ff56d70486014281749a253cb2fa7c137c1fb8940817085d81cfe10712b6f1078841a069d11bb8c9ae8e1b7c96203f2de4b8f460aaa29ce8f214867c80cf5ee01dcf3fffbb0a4aba5ea357b43f778714a0e6e5a88402cdd15661caa96c1eff838c9d110fa41adaba2f16909424660f0be037c920a9e0896b96e4741378d9ada7f661dda4cc59dcbe0baf905f9aac1419fae7dbaa67c5b716cbc798e91633cd644ab51f6e392f0bbb9bd6dfc9d821820c275da0cef71d86108839a62dc17ce62fd013148037bb8f9eaff1f0df7cf7530c8347b646518a1a7e26ee77d072bb8164efd58fc67089fd81ef778fa25fe7a51668183d73affec12f2693f517416737be621f39d2a984f9868edf2e95cb9352221569c1a4cd8f00c168304962564b834276d5947a965bf3e3d778e9e998166e0027d26e139a8227737bf2dce3d4e184aa193e1debfa354dfa66de175e154938151827ed49d4a988020c96575b60bea8ac4542e3bb50c0c57a0eee83fd16f7d32e672c90fef162349138a32a8bcae4a2669819e95b99bda0004035c4c334b51904ac5a9ffe9c5ab955c17488597794288943a09aedc771cb2f740dfa925f3e38053bd23ebb807e754a9ab811da53f0c68d20ecb5d3d331ea411368f8ffe2ab381aee0421ef6029ac9ac06635b28f168e3d1cd9bc601d267df1668540eb03c2dc0398867cb6806e214e25ff07128cd65f75b2323f3cc3f6eb8a7e94a9cb1108a017379a285b003cb790b2d4dff7a83e3d28eaf53dce39914ac03fbb07901048351dcfa0a14f4913c280050aef6c8e25c029d4fc33831cc24ac8e8ae8c493ea1b315d9775d74f63bceb9115c181bd86adb585afcafa2d58dc63f1888e9136d386c18fedee4e37578c6abf32b93ccc064c3d0c55b887f9ece1a5f61cc1baa4828250139ddbdb992639a3905c04a310c10cd849f4ac8c3e08f8e512020d2bf23e6121f13db7c7a5f65c7a649082c61cd76f89f5b1ba5b1e572292211f7158f6ecf0c0b236fd68f1aeb4edc78171301f5e357294017927d40b67718633ab3f811829226d0de241de2be29895744767634378213c4862b2fdda53169beb443e8ef4fe703a649cfbf629bf554bc9d08b385743a33b028490276378715d982178988ec3f2694c5511979e058d6c86375a7b0d21e83d7042f8db4f17944a69ebe1c6ed16119f897fa5c9dc86ef23d663dd1be79c6fbdf4b46cea563fc13dca98646e3188520574ec994ddb3cd019cbd75e32400bc771893ad2500aed3757ede87fc38f67322c1e5bf85d8d9ad22da0eb8848d24379062dcfe171eec7106efbcbcf443b469115dc9485e989c5c0b6a9d702223c31c6b21a4ff831956237f5acd75453b75de185fa18fe9e5d87c0fe2539a78471e4bc7c9b78f4e41c860034271bdc511e9340e6d4571ba8ac6c2dfa6955ece1cb4a2dd96936d0fc8edfef05b5de1c3fef37826d0deb099f334f4719c62afcf06014047bb6e10b2b9336de07e0e066af47d231a2d56d393ec4903d2fc4c825c8bcbd3a522f9bb481bc98da6b3e96579fe3ea418273226404c9d86da57c891ea33bb0e82de738c4448469c991208eaa8e6356a2ffe1e79cf6f65af2f4222c67d87c6b443042c68d96c5ce069d61f4adfcc64ff08a70b8796c0ee517cab57ce2e162f9cba588799be9da90cf7fd1bb45aeb8fd309d4a162c74ecff399cfcbcd49d9afdbd724f1bc54329bbb990d83d389517d3d0c75ee82c1c9c75f7a17741ab6b188b4f38dfa6588b420bbdf98126ddd1ecd3425ce7a7553791aea59e971433bb351687e9185aedeb781652bab5438a9a8cc4202fbd9974c864aab5e69e064b01f44e1bb6673df3625825dbace20d18c3ee78f2d86bb769cd098c86e06cced60e53ecb10ca0e13ab5167753ab91a914084f7661e6a65ea09e5453e01ab423d42df91f603a6e47a1494566e96673eca55c5084909f7bcc007abcb20f422158ffca594e2cc602627cc88358af3311e11d23cc01c0e11ad4f3c25d939bbcec101c4dd2abbb48a37d928f365470f236a8d04c8cca82cac8332f3bfe006ded5bcefc66d761ffee35d3dd45ca488bf9a79a50eac2cde238e7d32bf059ae8e207b92dee1f3070615903aaa66afbab7d23f29de47c98c92ea084e8b097cfa9e12f356c94f4659126c1cc30a0cd6f52d470bccb9409d0800b46a37632a11516d15c56d7ea870bd9315cb81f6f902fdd0f7455a18af14830300ae1171718a4908d16538cb5c929c10b5a727b6ab0217d1b46221a76caac7f270aa5cf851cd7525d2049b214996a569486eb2ad01e890577c09a053fa67ee005e680498e30fd0e439f746a1d29bf91acad7ab03924042c527c19f68bd2ccd4053aa5773a58afcc7bb36109f17dc2e6fb1f4294896e7a9b402c4f466c667c382b2bb4df3fcab103f8df94a63cb710528c76c9e1379df5f296d75c3786c260c22b20846ebe0c3619f895f3ba0a1b7bc477d9b4bf450d9a7b00b26bb6e70d5da72f0457710db196a06ddd3b8b62e303c08f8a585ef70359b86d465f1f8bb73c3a1453b2daeb92287fd42102f7b5ca5a560ecd106e6b408fc797ebe163f467deca04044bee8ffb8ea417ad3a7b8bc359c67d841f6692db1ed79c5cc25b02fbe5c811049dd1ed0b5c352924cc3e8b2664222db798b7c6ef923e3adecf989ac62da42e473e269d9a7e2ddda23885573aa476d1afda16c29257d76fff097954052a85224cb8ab3616616ea1d52f321bad55930577e6ab58121149bb36063657f971ce0b83fbd09fb46d34aab513832fb7d81595136826e0de9599cc98657f1d6359e40c81d682d1e20b22d514470247fec7077b1fd7b3ca5911460955f64cafcd8d4a216d2b05bb88f67e6f156ebeed80b25bf9f125270b1365fefff4115098c5f65b0d829f8cebb57a7c7805002abdb074d207969974ef7812823e5ddb938674ff801c4c9bd4b72d9ecedc4b702eca90b62c905312783803e17ec510729d5d38567c0bcfd30a7b2bd45188bece4463dee5444ed24fe4adb2867371e565e1e3aab628107ac1e12d020be22c848a8977118ccb2d92977372dc6a0f8978106acdaea6e0292ccd626a2b1eb06d169e505a6341c6422e52996eea6de39408db47c67984951182df115ccbb65be8b0301291ac4636d6b98d7c3035798b2ff513b53eb82d921f51cd3e97d6505b90ce990ba4a8d916514621c24ce6eb09a26e98074dfbcf0d101b640d3bcd25aa75d8ada4e20e0c0f2d17ee3a33357cfde8a0b8b8379c182809af3ab78bbe6a8b39ac620a3d4ead2f251e7b9333a63e76a21c7e9066b5276d2e721ddb3c69086779960a78b08cc0519bec15b1f8d7ef2a926725d64b9506c7103a2eab75c13fda0a345e32ca698fb8102614e8ebf680c9f9960855fc0429782504ba67ece853767898b85700b7d1189a27147c6587b2b86eae6ec6da78f133245dc01d446f00bda83401f3d195feb523784eeb6bf0027fea97c94cad1ebf602e70b69ccb05ef3f394b022da4c0c4ed5028d7a7f343f9182e15a987e0ec1c50040cf0d57aad81a2157b45cd0ec5d2dd8051f456b9115e8530f3a9bfaef8870f1bfb114cc5a6f7d7806ce6e2e3786860cb10c75af4a4a0aad6393f34a3827cab13e3015f656c040f86d0e3ba53d855399e6ed9ef007638844fa76e303f6d2f6a0d5b89bf97f7451df6ce4f9e4fe6d349dccca1c4e2ca2f48a77c26a6f592bb48032ba8271657f1a3ad8b826c428125c38d1fe7d141a63e3db8ebc52732eeac300f12d7c6ea95217873f3ac2b99e61278ef379fd78337854d81f8bc895536173a14d4e18eb945dfa4f676efffab03565d899a7b1ba4851d92fd47736ef9418cf20ca268683fa15eb94f399f72b5636db0e5d3b7d4b6a59df4be6d0d75e76914ee13a60b249096345edc3ad5177de2efb8995e918e139ef99e29f23ae4b7bce10802c1cdf46d78c354977a380d0fa181edd7d51563ecb010db5df96263d1ddf3bf268b3eccb7f8d9eb3083c8e1bb21e7cbfbb06346fe2290d97ddd852e37ab4bb001eff9c43143c8288f88cad33bf291abe41167d311972febf2c7ad57590a758e157de6987ba42a59f96c5266fedd56085d6272920d0ffe46e64341dc47b19dc223e96bc01786d7753ab35afecc5c719f29b08c93f1f98438801f0b296a8b362628ca14b9f24007d51941ec228ece5b04f88ee6955f3554c1e0d936826f51534c150a500f9966f975fa8197fd781e0f93b3a5e792c0847f89cc963aafdbb0c16524db2b339cb153fb23db491993979f23ddeaed0c826c75f07221fccf46d6dda51ed9cd3d43e52cf4a59b96751b05e17fe19548485ae19e3fe23cf2f82d5b983d021b37cd76bdbd878bc8cd49b41cfd4e6c2ebb9070693373b64489dad9f9e6162b9928fce70ea5670ee265aab85fa86ee46897a09dcf76e6709c5ef1603274cc2ab37d949d26e56a0adfa75b6d2aad41c04e42aeeaaedeb2986f1a6cc878f447142967bb077b3ea9964792b1039a815c65db489e3225d4755f038bbd411c7dde1c4e6d270d649f842f7d8c78b3d003adda353cf06f25349d5222a6d0bcaedc5707bf54b2b666cf489a7638216e665e71734a16b023a4858320b0d96908285a5217c636140c22faacd97964a4dfc0c4fa41a54a2bb0d2425c10d7481ab27aaabf2e4cd612c34e777a6b2f5ebabf1f5d818d863a60a49733d24cd8c6b72aa805111f4206a3fb56955b441691771e4d29b7928079b01f07627d34c1beb5d23ff47b03498b3bbc2c94cdb18e4f2104f17f22bf2df403d2d347641a00c521017f9a091107ce97ab1939826d8edc70b104242ca2e2dbf5be43d8250026af929be9a3a92ddf88e2978dbac1b91090215454a1930379686ce96094b198e60c8eb8503ae71bb9c966874f964f97ae919c73254d318538874ef4702705de9428f981f139e904d374daff608b58e8a5f1026ab3e9a35379f7d6cd25281b0d9abe41e9f282240538ffe266a4dcb85002fd4d40e90d28b876d48f1baf813451d59ab4eedc91161ca555aa3f878db7814e35ef7a7e4706d21e0320564f6143a86aa7106041724a2aa71fb51c91ae4959815b2cbe3dc7c71cd0bb03433c6823137e30db9a620a800f1f78f876e37e85ec1c4fdf63c9c4a4a40ab94a607cc6c14a7d170b02e604af0d4cb08d305c09d6bb29e33cb97702eea06b87c89ae44ac7598448b274c52f3d049ec959106eec78613838a837e97d7f494869b6e7c086402619f747726ebba2148d92d292b8e20d765c381a83e4eb794a9af15c7e9d4ede347e284cbdb8178d190f3948b3c9b05b4702ea8c5ef529ca26d7b840baa7dd780567bcdb7a7c61dd2658d4f94bd234a1e09f7995c40a0e593c60f0851c991e502e6de2ff483a8e1f2ab0eaff54012fc4eb1f4524d2033ead54f5a315a2d9020acdf6731ede50305d04f8ca370fc0ed91324ba43e8aaa1d2e098ef20c67bf68ac69cae3de5de993631f578b274bb8a9daffc9a181500548f4c8e16547801f672dd3a6ed780ec370eb27c17e9676566917b0b24f93ec005391c4f338bdf8758576cc3b3c6b241447a0039ba76ac1c9eb52effe3d4e70d1617d1875c07a44b25b2d80da267b3740ad970c47e1fec9b5fccdac40cace2beefce18826a23eb4d07b1a009db25b2dea6ffb2cc470e2419f2a4aff07a198c843f52cc57bee5df7e0e8464fa9a29991bd12a82f97f8c2e8f0f5873298313e53f17cee2ce90f3a2550048950457ffe145ac6881f13a6ff6a1e3b5a1dcb59ff1d47ec6ced8ddc6ab5162a7dc676f84ef5c941612c2f67b984fd81ca3ef6b93a27d756c0f4ff1940fb0d7c7b826986dc522b03f92f8b25340ce0daceb2d9c4983373cf53cd3b043ecf5bf73b077493baea47271019cdb0e7c28dd135685ef319d75dfa25836928b6a71957bc9ba46f556bf0c4bc2cac526ec3fed7ba2b11d0836923284d9cf1a609015ffbe4ad1c89a6c7b8dfcd1ba78d832d3022fada7ffbbe6ef7da3f392be43ba377eb0c3feab9ad7db607ea0001034bcdd15b5c1a8adaca877dc27cbfe0d8ca99ebf58b7b89eccd991e29a51cf85dcafdd2007bf477a35fc6490b78283111cf8bf3e151fbc322564aee8bca195f622ed44463ad949c61f8a93a84a929def6441975065cf2eb887b4836273320a4962d42109b44362597d44dd3b82ef0af798471c1ffff7477395c90ab26ce189563c13de09b25cb3f81479a9c2e1002556a5bad221159f45ac01a9b84e288299799819099c129b9a742fabeaddbc2189b11ba3b82fbb8938f91a6172961ae08496b69a2bc73af9e02c15ebd3b7ab7db1454538d62fa63441868e20534a8c71009f1122d822c5db1c104df011122519afe8d55dd35d6c252223c0a40f07ae9194f5f635230c06a259eee27b05b23f82dbbced798e7e3f0e6a8470428ea3bd216e8e2969fb632e3a2a0ee5bfbff6c2b82bce702db04a5f0a4df8de7f1707a772fbddc0d35dbab0332e92ae3d6349d637173d90a226e2b145eb355ee8360d1e52223cba36140d531408c6fbd49bd4154572eba6b5b4cd18177807eea3c5970c01121f6d014c65d2a73098dccfd5d920876e0e99b19b273aefa48044caf127e75163e939f3e4eaafc3bf8c2524061ed5ea365e24a2250816f27ae08755eb307da0bbcc3c0983d953f919b522041fefee59cd9d96b853cf6b8767a5573d0c4d6ad305cba385b6d235e0d8fdea59ec6a6948338ac431535414aa2919f75dd70d0f21064ada32aabacec88cb1a92a99fc1c6c7f2ff808757bdb56a95ae11b44456aa055273083e3fa43d816390c962185dfc50c16faf010b74e39f011533cea6b3ce0a0b15992fcff180e4909661d502b9e7d4a735e9101b708510b01f834cc08245275b28a79dc54f19189b8756decbb519dd4d2b7dec71868dea469705f8d6be061c72ff0fb451e6d860f8d694b46ba87d209128e1c4f7e32722af1679e7baa8370f999db4c627b535f5b2d953573eae7b97814e87e3ebfb0bcec82c18e28b2f5d18bfe49ea065a686dd34e63cbc364235da3676ce4226cf9cb05ad91d477f309a26f4477dd2e778c4b50f1f725590e7f75fe9e46cfd1a139f0a9f130eb07809362b8ae3c1fabbf3f657831fa0a0cfac6f3405b9380712d4ad885344a9c69b9b02", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000e0b1a7da3e047845b1d8ff775f052add00000000000000000000000000000000ba6cc6067d9aebd22aa647499868d26111521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + }, + "decryption_aggregator": { + "proof_hex": "0x00000000000000000000000000000000000000000000000312a8f853a46ad8950000000000000000000000000000000000000000000000053fdd2acddf75b42f00000000000000000000000000000000000000000000000a88f17af3c3c0039d00000000000000000000000000000000000000000000000000026b4e21c49c4800000000000000000000000000000000000000000000000571c6bbb50a7e605c000000000000000000000000000000000000000000000003b06b8a62b6ff439f00000000000000000000000000000000000000000000000a406e0f7ace42494b00000000000000000000000000000000000000000000000000015bb9eb3fb55e0000000000000000000000000000000000000000000000035c33191f6a014d4e00000000000000000000000000000000000000000000000a5917facff53230a5000000000000000000000000000000000000000000000002d448fff5591c30080000000000000000000000000000000000000000000000000001ffd7a66840c000000000000000000000000000000000000000000000000c66bd6a7b1539fd3c00000000000000000000000000000000000000000000000dd4af2f894c7e385300000000000000000000000000000000000000000000000e26358010edd2eaf000000000000000000000000000000000000000000000000000022a7fac5859ca1e90cd04e5ffcbf6c71ec61cc2ae9aab902a0fb4fa1773218ef831a0189c6da40a2c1e5e6ce201cae653ddc10d41aafa0588a10422bfafb8b8066a06b93407a61b227938b91228349304ad087e3d68c3d7a714d940dd4997a26526f1c918c6b815ea5438f1eede873183871d8b8c952ab7f9f00796500b1755102cceb1e6bd382fc67f3e3aa8b0ec912d44ac31892b842bec6fbcf674e23ca2b9307969fbe4fa07cf015cf4dbca9e7846328f330620ad4ae74e79fc87a32c4131c9e7d26c8b7e28dbb5c90ed466e09507a61716a6e6bd59181b7f763247bea444384584c7e8ac16ed88d759c0a39196fbdc6aec044721b849e6d056c91763a0b314269a21900720e74a900444e7c42e1ce70f42152840178f5559f63332a1a1746b525d722ff708f0a1f1d0036607cd983ffac29224e1bfa05de2d02d104e326c69c2ab424513114e4656a4766d85168d36a5b5e79d4d90ef46bf807a0eb197624f9d8651c81f1b0b59a52f0be0ba78094fb9582fcb805272039845feb7e26cc9e9c087bd703b1ed8bea6a0bd7ca560425e6591091564fff151c04b13378c39e838dc213143312d1d9f68eae024b52f8a4f6d0dc5d76ed64cf4a729b167ce62e890e958d627d2040d277ac803e4a7f08e5c0d3701eea9aee8914f7f04ba961b821989101de7a1293b8a47c69fc120f3e3ab1cf75315573176f1d5b32bd0a09465750e907c44f52577abc81703d64918eb22fca332c2d603ca98e6a5d65a1642f1fca0daba682b17cf76d30f2edf93d920ddc87d762cf310860f5e9608ec4d95af5aa9baa1a21c05e6a870151085fee2176ac709101912ba9d278862321caf91711a3941718a0c158b14e0e8c865fa45e95d81853d4c83af3f0b4b78186bd7490d80a324458717004a2806da584dec525c3508a608d94756ec875a0e15f211442f66779e051eb42c18ad0290edf383ff8e26ed6a9b96c88392dd147ba40d10f2d0bb266afe142714d493ee3fe57858083b8f9577bfcef33e4ea30be6d15fdfbdfc7c2d8523e4281ab3ddbb039eedd28b26bafe5c301504bf8f88da4d31e16001015a1e8a936c522c1c16662644707a9281992245cc203feb75341d1ea27c107520bf897172392a2f3fdd1afc8380086e6e595e446ca7553bc180728e55e34883d6760a5c231be21275fe254ba19f443b55b8e3adc094b7621b3900ed90cd61e7d133962d376c5b1628ba6aa5871fc7516cc3d741a9e0049f5cc184d437698b9a927fd743c645040936659eb8c72d35c7f1cf495075859fad3d4f6759cf1140ed45b857c8cfb41d2f415d1a4d4f6889e087d2ab17210241ecf2cc412ae9df24b203671991221c3e2a789b7d451a6777017f127393022d91e84711de1de9238eca4563ff08b8cb0f102eb0a3fc969da0fa75118372fe3015d37562f8c81b78d6091eeb9e32dd12a3263867b6644f83e00fa935478d2c647af646e163dfb2625077015e26fcdbc6d92aee4c39efffe577684b7d4818bcf57b27b258181ff5d2848b4077596f08c1840c93ae83cdefe78b6d582c3e70f7e59ca0cf331cae4ca0affe6184faaf82175810f7d1c63432177c4663167eb7536ce9ab826ab2ead6603e14474b5ecf3272e2158e12d17e51a1dbb03a79ce50bf70217135909bcf3ab0b0afbaaf7000b28f220cc085c8d33dbc859feda8bc2b6c4d03e81da737cc4a2a9ebd1157fd0c1ac1441d59a883ac5c259fc58ead531c2aaa234f10705b143284571a1514758b320f320662c97914023d0a21937713b7647ee807f86f5485301664da7fbe46cec1c88508893afdc844fb0c17fca13df00d9b2a3fa45cf69f2a2998ec8ef69de37148d31f8f9897c860d4e8409a355cb2a3ac6be0f55425f63d36d06600131a725e2132251c31489f93761226e30128cbb603a5d2f17e79ed0526712d5d2374cb4e5c1829d39c2feb8523e91e59088be6659ab5fe024e3a0b306c71ba0b5703c679ffb60e1973ace159a6bd73e363677a031a72457a911e7658ce7e517e296c65447009085b6e26addfc88d54519d2a5ba252e9cd6acfa8a295f87b83852661e5f37df0010b99091191d06a3bc905e4a920844d8fd6a986c0597a0b5d5a0af207e5584202feac48d7bf02d573aea8e2b65ee62a6968306cf18c52debd8e0e593f524de409ebdd0bf4c65668ab0659ea1284e7de44bc8e90431f312ff585fd1b2268d75f1511db68a4ced686f244e270b39093fb6580d618f3480b39883eb06925b303131f818c6bbfe7e0451552b1cc718fe6bf25b8f65722c329edb45b0fc8f86005711ff86bc611a91891204f1c1352c04e467816eb3d637693bc6fa479e765cd489407f8870aac11f80c69313f7c704ca99a1b24fae889f75b295af8d6f6c0b25a51218d84f23a1cb256a464e3b3e5ac64efcb47336060f675f73ea28d5eb13516e61148dfce44e232e91a6b2618e1d4305148d4dd456948f69a3f1e843ce27e0ae52cdf83515af53b8acf3dbe95a8edf66280f9794c5249c97fbb40db6a4fa4d61a2d4e021844b6151ba105874ed159d40c0acfdd6976ccab55750aa969557f426a09c240388e16bbb4d639fde88676a158260e2b454120fff932f6b671803dd81f033f680bd5f9e1947fa409c9e1326e92baa11f275fff4bbff430cfc61c3bbd0e02d150af4dd072cf96f577bd2b682656b8ac75411275b2f8f34aa9d2cee70fd700ad5deec2a22d0b8ce8982f4243a2b4de625d84cad7efae743dc2bd761fcdba0acc6750c675c049239eee472efbf67e866e67062c7f3f5580a7bab50f57bb822dc5b8c730dbf443e0615d8fb7406ab1a86d2293963bfa6768a516a532e1f6b315d9bab1937267265bed04cd54aa50c02347a8c7b1b3eb58ad24d334c6aa1a0e03ab474b2f328742012f847fec43377d8f80c2f6a21413f06d34213e54bf889402c7310861009735660804b2b42281e40025b2582e39989b218e1fba5dee27d90017221db795dc096eb273c1d9e3fa208808a2b9dd8d7a15d187ee74ea0ad84124c8bb7652ad81d2c6cd7dc156626a083c41f88d34131d6a857afa04108b771620bcb7e37ed965da55b701b3e734dcbc8609d781cc677d677579bf243f2779e111a66140060c233ca997b9e9940179e4f87bdc39986551df89f94643416c537e005b1ce0bed6524a3a13ca4ca4c613b924bc9d3b505c9d34d424573ba935946e0a6307744a875f5e3162fccf9e28cc4c530a960a9c2007c22a2e7227311c60d92531346043323ff3d849c7500eb14b948a97889b1af6201c444a8e156cecc85423ad0d9afcb2b8f6e7eb4209138863d04bcfd8f97c60b19f6e4f5f53e0fd78d20723d4f93a179a27a59edc289413b041eff2b0e1f02a1597e82f9c40565c0e1b092ab9b0f829fd2325ddb0607bafa31efe7e9ca42cdadd1d8deeeaeb1d4ff56205eb74af7da2f65b19b911bb861444e7eab710725de0bcca573b2cce82aae44a239139af46302611b55922b21484e6f4cc06a502dba4d7f7c3e31bcc1fe5840007360c6afffc356c717bb47ac891fc0f9a730558fe8aed33c473a45bc06a02540c77e7faa173b495c4c5dc83c29244dbf2ef576be3c060a96060836e2e2d8e28149ea7e78744d764f9ecb044be417312f207ced8be362adfecbf9d849b08a6d229df377bbfc41bc2ce927fc040cdc6948598867008ea416bdad0b77ef4f377e324a903de05c1a8559ff3749b7ac5bab0286b1b433fda0a7139927dc230c42ea100bd4cf9297fda04df9edb9d3061bf5fc91a79f2dd1849e529b1a98537906e7514870096b08a38c5112b07a59ad46e7e4c6d8322871fdff22381a881f0d7d8702bbdf95515139f66da6c5a4863d7e7b53129dbb223cf15c5f4fe31231444287f0bf272ae60a20604a21c49d2a51fb25b17c2ff3a68873947da5155283d6c0a371882a3a4601c58cf931ad159a69478cb3f92c2368e58627d8fc63a09deae329c2179287dcafde7ed77f0f356292d0a335e7d9d68b3cdb3cea583450a59cfd5ec092e9ef8a77db63692373fb037ef8632770ed4200fedeaa6e0f558e67bea9c9f11c65d5ff25b5f9d4251d84235df2ce0bfcbe88ce980caa2a159ef77cc9573610d2844fd0ab00c04b007cff838ebfec214761d6fe8c636253b17766444d9558404192a2a8e0d7df5aabe258688b3ca8b68f732e4c88fdf5337085e6ea37f59ce0584bbd09f492cc20966aee2f2ce929fe5874b12be10a7403580a2015640c9261b626a3e4ba46a2a27cb5a7649060d912c78fa0fbf4c6f57c35bd5fe9c3ca035259794300b5905b33884dd53d58503c4060c5917031232ab44348dd27f95f62f137e10cb5deb44901fc562ca6f29a690be6f39aefce02c22b2476a8e82fcc77c0890a186a3ebf3e0ff3503d3c2e8efac08bb52a3273e7400cc5400e5f7385d9a1f0e83b573d1586614e188c35a870a0d244719bf366b2176a4584758d38f92660796338f177fedb649406f596d071d30752a4f7751bc697b7aaa20648fad04a2176ea776b6a600b025f9d9830fdf3044c7b6e180bc168e741e3a8c9a6447be061309a6b8ae0fea0ad4ed51050aadd9df16e66489b1c1a45af6ff26ec8f5a554d263c0ef7436f2855fa20a13aee5e6e9abcfd0f6103858242597a723af5ec6bdc26afb848244f0b6b88974a7649f577aef3c18042f7f0c0240ed06ae9ddd9689400b528d259f16326f18e4d9968140ceefe8e35f7df0f5c9e31eb9ef9afaa1e1b107619b8d745a7413674ab38b12563750428cd751869af8a284897ede1844181018e110afc61dd3ce0d4a418b6ecbc3cf6ce7d1d5814352c6c86fe76dfa9a9742753b0286a22ea0f19cd84c549c8551fc04222d5f5a43b0c8e8c37c97907626901577728de1e2337811177d672de4c15c066a16728012d8f6f08788889bbf65406813a0842c1a7ad0d137ee1f888a2bba9ae6425666e119b4ff8e433b53fd3e01739b691622cd0035ee5b9141be685421e2342c17ca231ab31a6a20ae900122b17fe0ea53cd3389bcc94cbfdf6a1a49f72878656bbba02601ac8f76dfa9dcb1612d7fd5615eddd4fe81a450f8cfc4931d3d310281e7728a29e52ad49e927e5c22da3a74794fbea01a961cdbcbb6659a7fce75e6807292ca007780e3e017db4b61029674552ca688b946813ad5b631b27bc4309c366afb2f2390cb15e75c8e16b0c63e908ac26ed4888ce8d886d9eab0696f6d410fc47935d928ae40ea63d22e62cafa727b163910e394ff602b00c243fd60ede4b995cd503d1571772c69a20680065933aa51f23d25a33a56a695816722a4bd53def5c282b4f4070b1e9ab62592204f9e6ad2fd26ddc59c40b6a96489d1d89123403760026fd8dc573f7cec14525e802219c74b509b723ac5bab87c59db3f667a74c26c384305717d09acddb52301d3a2ad4fb2511489387657f71397d01ce8766cbd176f00ba4c37dfc8b5b5d22e79b5e6e244ee65c74bf3b8e5d9aeff46754ffeecd2ef962e6676ad87fd2480e6b1034669635ee85baa10dddba443584fcfd2e9b1e3ce38f50efbdadbe4b402c43b42464b599c40447dfa0802e1938dc84be0a3a33d0199e6c05a170aa5ce11069db429bb24df34476dd3799df06892f1b6fcbb2032f277eb50c9a3353e87c08f7250b22c6902ce746e9984a02dafb3d18bfb8a1babf0b6ea4874c3eeb9cf101e7e74c66b25bb2416160aede9e01575b45cc0b6f14015da95c18b67e7166bc16050a2d65bc3ff5ddca6becb59c187e686d7753224bc49a47957073bfb6a673233a02790938b26caebcbfba480e68a67365a97442a5b91f68195f7c246a59ef0ef8827eaad232e92878a8b3bec861efb9d32ed06c1eb994e0f0235fbfd785711233bc36b20751654b0b8f95448b32cdec919a80c0902a7bca212473e9e60f120ebb89709ac4953d6f2a205e5fbf09ce8647bb80c87bf9dc7fd304a02e68c6962dd3bb9a1a8c7338f45f27c372a6b781ed4822c9ae4eaa8ef9d56606353a6f3e1250727f272ccc609ee9e39f892c33686d88dd15a3fa6c78ec0958c92b82fb90008dbb496864b3d1867086edb1095fdc75ece3a2e166f054385c4229c3b944e1223e3857a7356f8b7b9d06a424dcbc2e8f62be1adb52b563074ebb73cd5be13815dc4cbf1262ac6c6c20e6ed3f812bd26086771ce1a1d89e934f859f367c4e9f2d56b404295d9ea940c0e1f5d8f9eeb2796201c9e0ef8483e19cec66c6c2408809a577a9541da1aa89c5c3b4b993c75da82572772d9dca0a9371d59badc520020096b1191b962550cd065781ce770e42fba08605a22283ac6ab14860768724ee224b2c8b3ab53437401a68e9637da41f7f3c12b118e31a59b72f7d2927eac3a42c1dc5e65203d2bc6c994ea11781fa7430f33cf52719724f62e48aed905087630aaa2258bd137ffa9a3e87d31c94becaffae0aaf39ea4a1517e998a53c9a06f21b7925efc2dc5781a2cb2a77f7c11c95a97a53f86d67747b1cd77d4d5dc2cb5c27747242d4d4cf92f7b575cf3cc0268260f6e6f66680185def9753c8ea2195392b3a2622408a88ff2a0de00b1fe81781c78d2d056a9351eb83bccdfd96bf4b9407dbb0632093e34772464e5d32ff9b7dc7ef15d75648cf69db97f9a55f8dd55f0dc42d474b936c99a77cef8e7a9253c6ddb9289672e57067ff679946c072c49809edf255cc8f5549f47625c3174e5e372c4ba56ccd879380f021d961b2026c1e2c432a0685305ddd1777ef5dd550b5ac84458a3ab13ed8554036476ba780f4a6128064204b952b23440a199dc4a4eb1f357c095ac203887d7e009652884a299203ef023a8b5ef5f3f2f6567baa0f1a3fbc8b36606aa998312ae7fcde8524e6b22a8d3ee11e5f46e018f9f7137b53456c93836685ca81193df4e36106e9e67d722c9f44d2e95535b1ea7144fb65f3a49ccbde27127446f8b48a93ed9ee10946e40cab66821d8e80da372e8c3a05eea60394732d5967470a99e0cc46d8c8ef54b40ccabd27c19d503e94dd2a701d89a3c8351d27a529333bf4820b6644e3da820d19f3d2ea3e86d1f6dd5c3e593992d4e3f7cbf7047fcfccec97d934096cabdf84263e966f77925a48ebb22dad0814115128746ae8efb2ac7b2997a8ffe24738722ba6876b888d8afe65f2a1b0e3631d98302ebb057bd7074c29aabe65c2fa4fde0da2bbbffb2f8a4143ad1b86b5b286c3a280e2ee2a87f358701bdc55e39d33970db14da12d809bae278310f329050afc3ff0c052aeb73b834d49fcda26f3abb60bf906a239af734543e14b0be8d6a1c05ff5d39559c803f55110828e949d4fd92e2af021bafc61f5c33b89342540061200013bec78beac8236eb6bc99a74b5f50b3964ee581c75eef7a6f2cafd80746c4220d6b5691364f5e29f36ba4c0297f810b6f2cfaf7221fe473634d58840520c3273e3e28a530e86bb70f31c99d434a7169992fff226260357e8f8094e771ad760d21b8fcf9d6fad432d3878ecd1b94f2b988d692084652b6047d19c06ec2aecca3bcba90654bf075517904cda2a97c614cd87d68efe7eb87ca71fc1e36a848442e49b4788b22637621097fae2260a2b0644fcb47375d3809cd6acdb146c64294f11dc888d95621df31922a9cb9479e4046bb1aab8dd2624adce7d48693dbb8229468e0bd5ba459d06227d7a4b2765532aa3c4befdde90ba711e7273608b9fb123ad2185079b52803faf3b31d920d83e2f6dcbeb9a3d52064f5d9b98587be049736e1868a0ad6fa8772bc96e0817dce913bbde6b832b75ca1c56d3d250434bed8cd086358ee781be306a60a6c3dcf8521390e3e1018421da946837f4381f7dddbafd5e6e481e0b64d4d395fce061ee522c22d18cb73bd0a1a69fa20f3720ec5d997dec42a246a8e574f5fe4555b0c84c229d1ba4b5f1c0c872ae872d327b4976485639fd8f69d1a02486499927ef6b481dfec6c501da7b56f800a69811474e0b84b5c0ea56cdf03f39f7bd8171accfe707555406e942ea4c7e8b87324bdf56f4830663a2ff0a3f4c45f4dde1136f951511955651e1c01f7db16bb8db027e1a40835e229e979502af70258d4dcab5241c1469b0200a398d47d450b9c64fe233b104c6abdc71a560fde406114444a6d29305daaed26ef541e69304684372b43ffcfbceb08129f0196acc0a20243deaf97717d85e87f057c02fa3cc57961487a0a221294b8f2821c9345e41e28733821562009c8b05a14e56fd8016d3d47605f66fddaacea202cebbc96b913121a0a31ce62972d1c68a30cc1aa09ab64fad5a2f78d8182217bf437545cd14937d7f35cca122e0443de8f1ad8f249596e209dbd878d60973da2f881614f185d840f57abdf52f1d691a8f9abcc79d7f76de8dae8983819ce58d511822a45dea994b76e5c5510a53f53916a145d9a86be0df11b97befa4fa7b647a5b1b9db42d96b445aaf7460eecc2da6bf185d2d517bfaefef677638a9c490ffa6651d99296c483da911fa00e3b8f3dc658f30f9778f958c37d3aeb7dc10ad1d10eb52527192ace1b1eab9c1d0587c174e1515d3b7fdae56072482efea8c7dc1575305cb8cc550858425605305e77df03c5eefe5bf36b5abb550be19bbf77d58b2bc5d0e4db303b43cc46e41d41857fa0ce2efd647ab3ba3b4d199d193cf89162be86fa665741634ed9a443136cf002003fe661f3e9f03b3933567ee3ac7b505616c7e8390a38a841511e3b065a4ed68e2bb2cc5991c50e574c9abdfb85aadd7ef23d6e3441f8ad49daeb6b1a9d059a533e1540de4b0163834c6e3abdcab75285c80778a83899271cfda8e5265a47bdd549051e5d7dc730ef1f3d7ee9a7d1535302e46f507faf60feb333240985cb59a7a95e18ade5aa512e4893178d8bdf2296911a28f18e40ea610f163f2d7ad4d27e7ad1d2ac45fa97185e0433726c735c7066aeb2f00fa5d2b5a7bcc209b158cd748557487d5c2e84685772fc040f8549817551ebbfd4461e454176792f54ee2ff6bf4652b6ad757f97fdea70ad789a89d3b4fe9d04aa3efbbcbe352c00fbb22f8987a6f3cb11d1311cafa31eae092abba3d39bcca2d797ba4e8cfc512dd357d4a7b3b0f15d0a594e60d2ea5dfd345dfd14972f755fbe7f807ed258c40095bcfe709c045c9703f589bff8ed17a3312edd544da0bc056f1e2288bf48e4118732d39a335f4c2a1c0e928d022b2dda9847748721a3effd753439b1a30e022b79ab16626a9f64df29f8644357b88878bcf79d8d83f83eb91846de3bbaed1d1ca94b287cbca8715b778ee42eb328341bd00cfd6e2cbd6bc7783a0bf5ff7c30244dfd747be053160f6fea13b2dd74a9918c0a27496457881cb1e971465f389e0d8499501840f1b7af05db487119767fb4966c39dad2b6372fe30deed2b99743269ce2833c4f208d55c6be3e47ac5131b8e541d5c666291c42e23db62ad9d1cd00cffcd6f36073fc193b2e0bd9534d1e6f16704da01ecfbece0de21a169fb3ac290664474020e634ae8b181924be1c4d6ca0ed50cb84bbd7ffbd17219c1a5a7621ffa701891c3434376173a43e42262faa0d0cb2cb3fc28da95b1533466d8d2409e8273991e4882a8fee7523935ca297faf2b3af6cd199ec4e206f37636c02b41a3857782d2c895729ad03c17c64de1d22bdd94650caf95652179d78fe58ac3b20d6b4cefa8256d1efac992267a07797feb4b6c2f642379edb7a4874d8db030a2195cadb6296ad13bf87b7ef8e0767b18992a8b98e31eff1feced2b12e06180010cfe8b1ffbc81474b7fae7122ea2ebc3718c3964a000a1420e634ae48803fc825594e123f569aa1ad0a068c89fad8b68f09ec8927bb78315a559f8cf20e9af20674477edc1388991548393f944e2f8a1c2bbea9966b09bfa30ee99a7b60a0ff0901fd0144ddd92e7388590ea6263f002e6357e16122e778dd3b8c509807c7c82f595f4ee88c0e65bd878d2929cb4ce2f3393d539e9f56af72ad1bfb1638ade92195e493e338d401a6471a6003fdcc55d510b5d26f71d04a6ae0576cc149c8682643592d4f3255995648e5fff273cf5e45997a2e44f74dd2f8bde2558b15577828830c23c518702afe557268ed3f7b0c18299f30cc4c88b2dd0d2aef5da75a5b14ae5900a249b62081fb9948614650054c68362cfb36f9f0934e1ec98c891dbd28c8bc103356091a7a474d2bf9e9594d01cce31d7ed420458dc9625117695d6a2606183b5e46069c734491740a054ca0dcb4934b96d3070fc363837db1ff81e210d9c471b7191a8a98a4891d2aed19bdfda19c717ceb64b75fb6df837a948ed723eecdf33d23fe91b6fc3df5f78763ab1bcb718fe339098b101f2a54ebc146110f8d3a65e7909c9da41323266e19a9c13ce43d0e3be679d49daf7ae09f4861da266fee65b132d563c46376e6647c0a90668349769ee6a3b44c5afab705f9d2491598dcfb52d52820b3df3e58962e47f8ab928d4a72f0aa351874d682452af7181b679a81504ea8ae1fe71503c8a05614e90aff50a31c2e6511faf8b9af0002762557ada0aede367f43bb86033dde827b8a4e5c7fea436ed1fece280691b26d7006bc12cd8b4fde77aae8cad9c66ae6d465e3d9cfad484cc969b38d6d61766c8f14f4d66c51006eab750e753bb86fe74889086e64ad24d1bcf4402fc17f5ae46b2013d73c8323e73cf698baa650a8ae47bf5ad18125b6a7919155ebc9b9b34369246031c231bd06ce79610be9eed895e590873090a539cbe4be9a91afad701e5f24b587c8f7b1f06056e97ecb69efaa7f87a4fd53da40e3b6439eedb4306e75320bdfee5c1d4ac101d47a39b81517459820240d4a51e2de4f1727bf9a0eb6242322e390bb87e48a4d518190d833b620f27cc16a330a2baaf84cf2d7a489995a5603c29a2e61d5bc8a928a567d133c7290466082c27306ea65fe7b9feb7283de1404c8e3228ace8eb4448f1c7ef9f48b0d4bfef0d7ba036cdebaa6c771448dc7221fba16ddf9c822e9b171fbf29178c2e429b1b26634404083d54e92e6ee9d064e1883c9e25523d4ee8450ca12b431cd30e9f6fb87490ec3e57d138fd8084287ea2a83c1dd0f94e1bebd9a38a46e7b7e58e9e79f72591cda6b79df6755b331d45c11ad28aa7306ddadd12c035f488a2004be39afdf94dbf885b87ee64cd9dcbd511039b08d1dd14bd5bab8357f18afc9d04f90c28253f96626a8f93422c1fec165069f9588974db7ac5ad7cdc22c454ae09724a0fa1aedccf9d4291ff835ae1f6010bc0bcb34e53b22e175cc0059ed2872537084d5463bb2284aff2873a23c82662fe123885f1a767818e968dca8ea08f351c9525336c36eb4a2915698e5aa7978047ee2f13ec6b7e88499d6bedab91cc826c49448ace3e32dfe68620ec807476524745679f039703d8386396d3cd22acd7ffbd565355db2cf03cbd765181332c013f9e6b2776c9c5ae0a5036bf4e5367842fd78d47dcfcbc46711cf82a85ba7d713b96f3e63ec8d8833911702b24b7cad910deb6a94b916924a2ffca10e0e60c909b02130faef1c4537e2754ea02c57757a79469e757e82368dd8b48224ef94ed24bd8cb3c279a2fd8287e948e68a883566fe24e37a3ede38b577ddd7dc3fc77d00b7e03c04f7da8110b02aedbe796e29042942a2dc8204730919c8c704ee339e05279d7043a5ca60fce10c903ee2659a8cb333ed2bb78efce80d3e058d9a0b191fe5abd7ca56fd0b06888adfbdc297781cdd296674df014f260189d39431b9652b08978b7abfb19a3e1a2e3925eafcfd183a47ee5704f1262ca2b2bf1e8e30b514cc2a3a5dc1391ec6a9bf99dbe9b5a048f66ad5f00a9e1ca43dd8a470c386ba255624aec4376bbb7cd322d0aee7804ed9245e7cd2e6864e284b94b72dde2c942b10914f7b86ec2ce839d329b7fc2cccdb0f24c18574be20c4535b0b0c4a1fb823a43d15d97b3d008a7201e4d49ec6c06c7dd722a69347f3ed389798bb21941f17ddd2a1758d4555edb9bcef81bf6a325f318f657f91e8bebc9f29ccf8c9f1ad0020b779789996bb3f9558903fb3ae05ef542b9f32439024b773ebaa1efc97c91441d11c241c4e93c725c663462e7f2a942fdc50a3c29c371bdd1b13e1fb64870aa2e3cd29dea8116eb2a423729c8ace6a5d9c5e72ac419c065bd1feb927e08b15868cc4b06c7b62a97a2203de907b009231461e3bbae4ee4bb08af82f15afbb13475a97b53fa89d7006d69d317d0c39bfdabf9e4257d63e4b53e40ceb13a6f621a1ac55dc06d177b6fcedc6e7e32005711c498992dcd4bc1bdbf1a6d423db7526d069e2325fd1b868f61754dbb71af2e7697b48a0b38453c778fe6ec30529ff03799247e8ff813e8a34563861cfb6e6c054c30fd8fea3d10ee38414c99568820823039ce58ac0487b57359d83825ca1dbebc7b64396881b741a8d815934796e2ed5416e37b207542726b2de7e7bffee33ddb33f95a5e312f23eafae94f6349006551c8206f287153aa4bee37501378463eeeedf05fe60b2dee20b828275b4ac244c896ce38f93183bd127a6f43b90402dde1e105e34060a5aee5e3e7d97e34e2106a0ec6dd1ebf7052081db6955990d6f1706720f956367c173a17a436d480a2a9b62175fcb45071b23051147762c58c3f57cb2f14133a0da0fa66b77fb4e2e1c9cdc085d18e7d5b0476361be69fea7572f03ab25cd56201ec95afffa9d63b709c6d4d2fbc435aa7df2efd79be5fe8cbbb4db4605024dcbffd2b22d193c5e57000568d246af4467050a7c8de7868a492ceaecababc6f9b0a7f2b0fc91ea5efb2881c095e6cce89188d2e3f3f61bea3ab99ad360ccc285baf30d99b2699053c30c1546aef7e49935f10f4ff2d96ccf1be26e7f1333c4e7f6c16296f35f090a11200ee333cb88f924d0e6d4314b0062db0062db746150be5d7b6f5c6b74d3ed6c2adb09c5cab66285133042c579f40cccf4313262054a0d91df0f143fd5c7f93023ef40e6d4c4cdcfd90e1e5927a840402374c318efdf47725de161f0ac04314b23a1964d6150e0db2eec017ad72d50aedb800f9a1353cd20343754d5a65c6ee61e92943a713ba5ee7340bce0944ed8f309507ffa0a21b79a86d97b174f82c889031481b4bf7fda6f352f05a77345ade76cbbdb1ef4b42bc47ff31b50d289f38a0d1751b13c8bc3766622e84ead59d75d4ae41d2abd3bc706228f6b1667a02b740d165d80a7d3799ed375c71d9c4063416add7c7211808bcec4f289e804072e4426b83ff7dd6ea6d86155c3707f1feab0fb1e61397a2e90ec53bee9e149be796711d9ec0096e422b70034ebd92932886abca48fd91dd7db7f2a8de5f081195f6604daed2bad88c0715960c4d030c05f9ee7214891942160e00af4c8c56117f717191a9aecb55b2e82d0f47e0de9e55d4dc8b8830f0e132acc3ec7425db5e4098c1385772afbb58bf86edf9511bac855dd8b1ac4ec4539336e5447fce99fd2a63f10ed9cd9f42267145f2ac01f65f3c0123bc2ff183bfc15fe0046aede9e8232ca24bef7ca3086d92d4ed47137e2e80879ad112fbf6696fcd7896a93775d15a7b2155988a8493bc0e94608b651979d3f1f15d22c2933750dd4d287eba002532c802b4c422b6ce2ea10207f448a40cfd32d724a27d98549d581c63d1be712cf97fd253723fd234f97161dd22e7ec000045e7e23a8408a4e97a6712de49dbb3bae7514a1b3bbfd8b9fdbe6d5fa95d6e126f4e8e17b04e73567b661c3b199d68815b025de2247d6c0de94aa05a9f2552c0e31c38e3cb87744a4834a2ff85818368f39095360eae9ee80496d25e6551ec1b7e616b9c8b44e76b74f31eab8d7210893f10d4f65e29a53c31a2e84b8ded0d75424aabb32b3a961c239195aa0680cce94af13e4037c199abd6470d20f891b1be6faebdbb79f57b304210e70a1f8da590d4b0df85d59c2bce5ca25817f6703960b482e7303a84a56362f4c7d3b08778230451f25e5942eb0e642f5dc96c04850e4034e6a543a61b1d150b77fd532471071de27480b4d66b9ab75f0a282e72f057e4154c505b811aa895bafde9990f322f3ea1c13c5c27a09aef8391bdd8c472dc7d2f49062b457039fc10fe5b4364df3c86605a7bdea2824362bea87b03343bdd661277633b55d7a17dbb1e17ee1f1c5f26405e417713b4425fbea37370ae2756c652e802c1e3444d3ca4e2deec0ef2003ba0aea889b723251304669ee70762568c94bba42d99427408f7527443d093fc4a82f00a310b49ccbdf20696c28e52483a522cdb83119e8f2d5c0b5295ecc9f42020d12b0da710fceb50030401f256e57f8c4f8edd7c6cdf72fad2232fb519c96752dba8dc6164dbeaf0281f7844af403aec85ad9df8dde7d7ef031f8de8c1668400db7a02fa8dbd25cb9828d584392f633111c7cb01efa564da95b6fe7147241e116281f16e15bd1ae4c3d06fa33112ad72fd432d5d3afaf25b77722d45d03b14311985fbfb9a3ec5e5e5fbc886770edda9ad928a02fa0636300485adf41f1649a0875bb957041786adfb7834d4613ebd217fc010e1eebe5423e121ebd97a120be1795892d862910c3274cdec364c5daafad955d71047a6f056d12789885edc09e1bfd7b459f80ae1842acf4e0617b97fe79ce82ab0b2b8cb47183239f144dcd6d25c6ae2b068759c130b1d659478374a7f1442d8c1bdf196cc3fb505f695d328f0cdf23f37002486726ada49fd4c40a207801bb0c0b0ecd8dc842398d89d7c054", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000d160d4006c61f6385f7902a17d4b19cc00000000000000000000000000000000523d6eb247a935a8de4a66706e303c9a01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db70000000000000000000000000000000000000000000000000000000000000013000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } } }, - "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.112547319,"runs":3,"total_seconds":0.337641958},{"name":"CalculateDecryptionShare","avg_seconds":0.623812430,"runs":3,"total_seconds":1.871437292},{"name":"CalculateThresholdDecryption","avg_seconds":0.565414459,"runs":1,"total_seconds":0.565414459},{"name":"GenEsiSss","avg_seconds":0.125009639,"runs":3,"total_seconds":0.375028917},{"name":"GenPkShareAndSkSss","avg_seconds":0.230471250,"runs":3,"total_seconds":0.691413750},{"name":"ZkDecryptedSharesAggregation","avg_seconds":8.597811291,"runs":1,"total_seconds":8.597811291},{"name":"ZkDecryptionAggregation","avg_seconds":51.185872000,"runs":1,"total_seconds":51.185872000},{"name":"ZkDkgAggregation","avg_seconds":21.084386667,"runs":1,"total_seconds":21.084386667},{"name":"ZkDkgShareDecryption","avg_seconds":1.499727389,"runs":6,"total_seconds":8.998364335},{"name":"ZkNodeDkgFold","avg_seconds":63.746265722,"runs":3,"total_seconds":191.238797167},{"name":"ZkPkAggregation","avg_seconds":2.189493875,"runs":1,"total_seconds":2.189493875},{"name":"ZkPkBfv","avg_seconds":0.344567750,"runs":3,"total_seconds":1.033703250},{"name":"ZkPkGeneration","avg_seconds":1.381717805,"runs":3,"total_seconds":4.145153416},{"name":"ZkShareComputation","avg_seconds":2.753499194,"runs":6,"total_seconds":16.520995168},{"name":"ZkShareEncryption","avg_seconds":2.574498532,"runs":24,"total_seconds":61.787964790},{"name":"ZkThresholdShareDecryption","avg_seconds":6.255039736,"runs":3,"total_seconds":18.765119210},{"name":"ZkVerifyShareDecryptionProofs","avg_seconds":0.099047375,"runs":3,"total_seconds":0.297142125},{"name":"ZkVerifyShareProofs","avg_seconds":0.227246316,"runs":5,"total_seconds":1.136231584}],"operation_timings_total_seconds":390.821971254,"timings_seconds":[{"label":"Starting trbfv actor test","seconds":0E-9},{"label":"Setup completed","seconds":3.056267000},{"label":"Committee Setup Completed","seconds":20.242110208},{"label":"Committee Finalization Complete","seconds":0.006024833},{"label":"ThresholdShares -> PublicKeyAggregated","seconds":310.649556208},{"label":"E3Request -> PublicKeyAggregated","seconds":313.306656917},{"label":"Application CT Gen","seconds":0.321336625},{"label":"Running FHE Application","seconds":0.003349958},{"label":"Ciphertext published -> PlaintextAggregated","seconds":82.112244417},{"label":"Entire Test","seconds":419.055032166}],"folded_artifacts":{"dkg_aggregator":{"proof_hex":"0x00000000000000000000000000000000000000000000000ab6528b828dc60dbb00000000000000000000000000000000000000000000000a1abbbfd7f6dfd38c00000000000000000000000000000000000000000000000830618157af29e0dd000000000000000000000000000000000000000000000000000030b0b3de35ea00000000000000000000000000000000000000000000000188735db8f53dc39800000000000000000000000000000000000000000000000a18fc5b2c715b3bdc00000000000000000000000000000000000000000000000bda61492423a3ec510000000000000000000000000000000000000000000000000000dd0290b3c7a9000000000000000000000000000000000000000000000004983364b7fd2661c10000000000000000000000000000000000000000000000039b4556d8607bdb7f0000000000000000000000000000000000000000000000099a73ba6ee66e29af0000000000000000000000000000000000000000000000000002f88cb3525c2400000000000000000000000000000000000000000000000dee1e0248c53aec7a000000000000000000000000000000000000000000000002d7030903bd0292ad00000000000000000000000000000000000000000000000d3bc4e4978a08e90a00000000000000000000000000000000000000000000000000023bca494aea202d963ffe2e1c6090bd0417565279b28b01889be9222dcfe21d89b8bc37ea633a118425af1d443927e153024e09a6f511d70b18941e6691c58bd0fcb01eb5580d1bf539f221f8b59e7182eebb1d4f8fe20f6d071ae56a9209b65dba1db587938116ca9f42700f6637513c56ba8c9b0515960d790c8b4dbe376efe344d5f935cea06b4b861140217a2a61f3c7e65b5f762ac0a03f3a240dbfa9e18f437a7cb30b5199c8aae11cf8c30a4ef2de6b9622cd84bd98d344b563e0ad901317254e5f708029cd7ccd824db93db38c3ac3983443731fea6679ede17b63365e86e934fa6bc0806ae63c2a4d1a99d5f8be2e7d9a42bfd10502673ae3e598c2c0c3362def4a927aeb4cd7a438200bdeecfe20a3b86d93a666bcc3eff4dc3eb82ff02e3a2b2a4218ee3aa235f850585d5e928808022969398b5b4c2ad44264feea427187abe2a2e359653c433f3965bcbf214db7711f8bd8bd7fe71c7feb983cb463ce891add81689ee23cb1e15053533135d6a5b3354bddec9afb60428d5db053cd7e6f16b7f01a8b5545ffba4cfb3fd23b26cf203722f86cb76878b459259f3a08d9fdd32f22153b36759cc3a604f7812d907a333a091afe1821dbf642c8b8e522071c729c924b0e2aff5d72d70c59180b7f69061f709c6a2508e68a5e86ce5914fc7dc55aa2ec18dbc526b61e9cf05734d48c3955e23058f894a535ac974e102236e60bef51d63943cc3435eeff39b9905d4ba6d221c1392add2485af108330ddfe0c76da1192a7156f072b520802de68204d5b130afebd9f7de607b326d6265b1e64ff2182bd0e3458273fbbd7f7f5f6fc7da4acc89468c6a82fd21a84bcf8f5a169b502e14cab9389991a4d1aae476597ca83bf5e0a3d2654caa22b02c7ede94e1f4392015227284b88fdd39e7406e3efc1896e8668d4576465a3fb0bc0203261adb539e04aa1a23894473f04e76904d56e7a5861eb2f5aaaf44aa8ef22934998ca79d482884f74605be1e455bf594fbb689c1fb78f53d217184a674c725df26407f7e712dc0ab88ca15039410497ce8310927ad651c80cdde05da3e9ca5498ffbe89e01149e820d0a10d51851e40fd7645040132b3c1a4813466249d6037559300da0e5143232ffe0c122c73393a689d26e91faacc5b6194d8843088e11503b50ba986713933bac02d1b0a39e55efe0c4b8bdba00b2104956c6755aaab1e33961c91c792546146690dc9bfe19b77238cb26d4cfe0b9746ff20199ec3579c7d3e54cfa7624c17e311a082443911f00cc9be18fe6126814303a40656a52db542563901a33200a084c1dabaf2302042c0e7387813c36a24d46cfe883888653580f32706af013bf242fa9b03a2f7e40a45f1d23c9048effe8eb8de9f41046e35a8d753851e408fcb71165eb5d6f6c197c7a0fd6991b155db680a06cdd0116615e653c85ba7c1294aa0a07ef05bfceb690467dcd7ee88dcf31ffcb5453db3f71a2ccd2e75df523584ab053db076c6fb57ceb1356781e4eda6a4b1603da76bafd0bbbb6d61f40260ea34d7aeec03ffc1a93c42370f74c0a4ed8043d950136de56617a7759274611b14e0b002ca984eb4ae6dadce0aeccabd358192021a15bd33b76dc01c877b72bc09a425821140f55a66529a1cfab1abfa742c9e78c858182aa2db6b9483e892e467f3a4a9a130cc8d5b4373149254a438a33a1c72975cdb0425271599c4e9f02e35e2aad8080ce76aad49fcdbb528dff3f2404d0920be45890ba1b29cb4fca098bc976b98680a06e873daec3d1179855d59cc2f420e1a16e5b7da81a1b4eb907576f97540f9df83d2c5ea725c1adfb2b1e789cb0889b2dfed9329a9345b70d2da362dabfb7e832f1c4cc319924756eca3240f9faff90eb37224a7f33ce899e2e5130c2cfa148e522504889f859d71a412b10b374d84a6c139084239080067016429a61503f916f0ce4db10b7d0584b6dc3bac6df045686fbdaec850ff10b901466522e6931ae2771820cb1919ab5fec1e9a4fbdf686b1c66df96b7e319f243122cd4186f0f615e3a6b6e7d7f0e4968b1380f3751d7b3bd220f8c27a46208d60a2487ce2b1ca1802aa8364092ded7ff00976bcb3a1620c79ae30146031a010a2c0a2a00c6e4bff75a514d26be107d1b126c95cdb6e677736d9e665678bd12f70f209adde22fc74e1ee6b059bf66f54b3d240c2fc4b6f50ddd82d617ea2d5cba1bde17c4fb34105992103768d2b0378abd71926522ba98afed09931f50b3bd9527f78f8c6fb753b05db1aa4eb4d283344acdbd8fc4e4e473e470dc9807ca6d252048dcdb39999d4575a00011ab76c1b9cbebd74d6e61de94ea700464f14ed7b622d1c86a8dafc5edd967ffa4d14c8783edbe39442888f1878b9358d0a8aa78480f4ca426772b8d77e03813f6746c729583e1b0fb0013b5dcdf2643ec202799b52ac74b84233b3e19972530e0eb9e270c31decb023ca2216a641da5b326dfd8da24198fa386e1e60ed5257039de5f1abe0cceff64cec32c2e001b5ba1cc1899261f97f33e03b7b2b70d3f9a7124c4ce410f818051c3d38b385386b8a2d29cea72273660298664bbb39f126e0a51b6e026dc983640af54b7c6c6c9b19d3849c2f8242fdcbd80c208af1b934549fa4e256cc9c5c87e44d3f34429af107e081d8251296ad5ad4027c55437aefa751b0084f58e19227dfffebb39fe09200304927d8d04e3dbe7bdf246e8a9034765e69e7b4debdb242daf2a654116f3f6ec3b527eb817127f4dee47fdbe34f8efc4114aef0b3c73558735da92b92d0c54acb864b55b1c1b879c3b64e5efef70fb093f10c7b26f286ff8a95cd1fc78cbfd205dda55b12c079a072d723e63051bedcb0e9ec0bf522e4f219d88f7b794beb58b79be8ae303e542dfa8998fe8e2e40f0c2df8960c6fe54f1252372d030134f16fa2594a412a70af625481ac842b428aa11075415bf98856d4fc23375f09b6769f988976570f54169c1ecd9808627f0a9a85e1468abbf435a00fb3d1668484efd3724c7682008a87ea2bdfe7d46eb25ff8378ee96849409a8fb37eb7011ad04fb384c4dd2c1f4ef9f1b37b1371558b8e2f93582cb2646f2c09724656efb97d3337dcab06c90f0e0a99dbd83d4a4040273cf915e6042171711250cf964bd163d23a72972b3f0b9d4b10137e2807a7f31b2f9004fdb8b520e002a13526ac923eee0f81e6c1640d58e1c704ace55a8fe098468cdcac47a514ce7c13d1bff170761bb70805646c1ce220f83362153b1e348491bfb71d71e1d7efc77b6d24c3ca974a94d52c496905ab852fe706d48bbdbf5ae25ce05dee3e8b7b6ae617c3cb19530fa069d0471f21fd12a73708d3bffe920539440d67b4d3f416ea872b8201a1fde3ec5cbaf9b102e000c471ca6b9a956924a7090f27fe186fed06b74101b9320e5c21353be11a02275397a74cdc6c9c22b70a9888537e3672d2af698c54b749648261a50409f2047b34e162adab623e609aa4807edd86ab29f032d7d7def5528e3f459d1c83ff184be5373b224676aef2e0746332fdfbaa300f276d444f7843a2a98705aa467e2de40fa7cf1cf09ac1c95460fc6add42a149d1830c1b3a1ba13fe1ea634d731306cabf66feb50a478f667de4ba425e8a0d3deaf6848a0ab8c7db3f5574340a7d1214de510d4c573dfbf81e2ced85574f085b25585d25656c4c44b6de923a840a167bd567f720f2035b2a456790a5c36363ce092704be976aab4b7d59302d178f0edea2005a2dfde72ce639358d5b71b0d71cef895d6e7416864a2b7f83d1162a0d570982dba35651835593d240d35fdbf3c39b92d70cb1eaa52722b06dc5c92226b4a401b7cd3ac29a59254af9e4d233300734b4fb2c968ec5e2893566f7ce921847b7ba1aab42f4e4f5f51c638c03a13ad23805b9de2cc0a7d92549c97bcf6c10a78f15aaf95bd7419f2b73befe6f639828985bd1c8b28ef032a4a56adbfcfd1df4856798eed823feb886065eb10586cb72ab2737640bfa4b7c5e5fff3b2a652f48aad393feab573c94a109a7801ee0da56281d67b8691a8ae036e9060c4fc22964f9dfd7d8d3f81c1e68e0392e1c8f672d3cc7daf941f5eea32c8c194f85531d2643255822a749fba1e156e0c96de8ffef2b002b753ea47633d0a35078298b1ffc0eeb733fcd2e39f1ef634cbb169a64529f0d8c09d7e4739795946e0bf9451f995d5977e1a6c154229e20a048e7803169e2aed6f306b8e7e503853ff5e70403ebe23597b0a87ab2d5e0882414db412e051e15e0eb2ff95069bf0ac8c145250e84bbe57101fa3560210f08a76f92c25c8313f9330ce8ab268f6a37d3f955450ac7dd3e876fd857582c12d23c67aec92d75a9cc0e8888a9b984c31a9b1a290f2d8a3296f661b42d2bc8d60093f7293e6d5152112ebcc19bfb2b431ce691187f1622524074146516bdf0695d725a9b2b8b2ab8b171321c21f6bc9e966e93afae0f21beb5db56c01e0764cdc9cc1fafb0e77ed45120a2859d7ace771c262f609f23337085ad7ac0bd1518b19d7b02f22d36007e4a63a9655147b2549b74911bb02309ca97c90114bdc21fbc2e8354aedf45453389690da39edac5fdda7d03430411465676719090813825915d4ae9ec91c3afe24ccec744d07bb4de99add1605409b0c014deee8122ecfec860077f4e94dc76c97d6ed198b85e40174939c8b6aa15b8c1050960dd58e39fd5071db6b77591dc9529a230ca6af9fcc9f005b5caca043c24781515871b203e1de9f33fae96804acf9dd66a37851e916e09d0eb92f42232ad796741d77958f87d6be73a92fe19723019c9c9191346e428e0d06611952a980c1b7ddebc02477fd7417b32342780bb56756ef02acb202e3a68725a381f2b92ceedad2961000dce17ecfb2cc250be2c22e51fc82287d365e7ac359789ad2d6ff81fccc535275f3cc21c3e3bf4a04791627dd6d29d721f27082dcaa91f6e07f16049e9bbfa056dfd61d3b7b4b669787d139ea5119d4d9935cab7396c0e5d1214293575a65ee565f4922d3bc625d71f6a09afb3289208b9d3483c05975430031d01ce4ba374f41d28ac18c62bac813ed4c81aa4092905795358f08d116aa20409f44609813fe3641045f1632fbbfbf1277c4a783f5f412fc58b5d902d9fef218370a7b167710d3e2b896fede982fead14198b2bb122176a2cf6a41e596fed0f77a6ec2b5cc237a288ec5558415477ee54a5823a4c25b9a935a086381ae87400e36213fe3c54fe588df40ad8f9910d1289375d78aea52c585e6b0d8b000ca70a2814832c8ba9d7102d18c2c6432d2b437276444d455cb827d2cee930edc7e325b7423f5022749fcafff9a8e0f17ccdae9d2495be960cb92e0522412d116d8c2f1235fc89fe520e96cba827e08dbcc18942ea99d34396d07a510b7e4f2da3dc2448ef4ad4743479abd38c5ad3321ee0024af73947e13ac6e8e05ab58d0c1ce70d39e806ed89cd8f4be46b3890b8e73f6eccb674e53898b7d20c3858642f051c2e1d4894abeee0ac8b42e5098272d0c70d20fcfb4210b2b050c241e49e197cb11f53f6ae5413b466feae19c03d4818c347e400fa24a88187cebcb8e2a03b7f6700d14e54f9fd3815afea6aa78f1eab9ea0e63aba4e6051e188a1e3e11242f1c219d4bcd07f03a22b20ce8dabe5c7e276b88230a1bcb9968fca6fa9ce70779f350e073860dd63b61332e41cf6b742f050a43c9ada81ede0273e2f1d7ac851cdac0e6b76c48a46bb1506326939d912cb207b0452cb0657f810168ea8f5bb5336af12946dc6a82457d21b99ee430376f2943713cc5c0ea1e305d3f339985477c86d1461abaa8a5bb1949440d17fddd237e7aa73ac7b53f8eaddda829d7ffc6860de18f796e1c8e4ba59c7c8268e0c2c1ee35edf60c62230d3abc9bf57818e7f32a40a0f716c567d18e4dc7658bf84af3d5acb0c49f8db7a6c594f2e4b6eca678d460cb93e47b1344d2e4703f14f1d8d2a0555588ca28e19c7262cd4e5cb8f32bd1207a5d911fcba9704a8407c74e8cb344a4bfc107d6ca66320908508105c49253d04f3d0fbdf6801000f6c79ab6e24279275e5966c2eb653daf4f55be52c661aad0600f4c6c80d9d9fc0e43126c9fcae4f8593cd15c655f6096617f7c219bfb81817655fcbf9f285a52b89b1472f10724b7fb50540b377da2b45384b198d1772f229828e95ce47662a8f3e49e0606ee53e2d860c44af493f0d3b0d572070e8e5f20cab47637347d09170a19ae0a99ae74354c855b4611655aed2de1fa258cc243600dd47ad410fca16aac2a116fcce260a895c81a3a89ce81a19afb5868b3a64de0dfa78ffd98eba18398883fe8955fdf45716dd3bc94011f7062506ca98744fba2fea32b10cf3b2394da53d9988e193d4c5d06789938195241605941bc9d5d66c04302095ac006752fc3cbb0d7512fabb3a4b93bf67264df85bad30f28d468745212d98ac8e9fb00dbc19b1a1f92e24cc8573592579ce3af143ed04a9029bafdf1a0f4fa7afce28cbbefae3194c23921d61d988b4c18d29138ea3e95e3786f75600de9e1339cb0792aa919d41b3bc0e35a96053d13228d0d0dc37732da80dcd05027ca25b34cb25393e1b9d30065a5f8cf325982c4406199a0b4ec569928809ea1765a05be230f3b04c5e6feb4244c1c94caaf2123fa942a5d05ec8ebd4b446d52f44119a9447ed0b7dd6cf5e9abd2842fac8c1fd2dc88a968fcd238dba2b100a27c7851b2997c86cfba255e10b7ac0d6c9496291cf63ccde6285d45fd46da38810bf3ad0fc190493c633a698581fce39efcca70a352620f72803f7587656d23b2753b69783f660b76c896d865dab1fc38c104563f253241ac37c8bcbc68d88e309e109d15f9e10c2c39a1fbee52879576826e06802a9c990ddeb73299543b1871b903b1927c1f9e25478e97faaf3bf8cdbf206994210050ec9dac8bf965f3e6e08ed6d0746709cd2aaa86c5e32d1bc480d3c8c53e9a4c0fc785cbee882e360cb2d6016adbe6d57df4352707b43440ce847343bc82190047dca88d2a925a10c882dbd843bee3d2f61f817dc396bb607b3069ce2007f8f38664f16fae5814c4f15037e191d5f2d381491bcfad0ad7714c0f62326f534bd932a85621328d3faafac292949dd9eb0c36f2c1f14868951a8ccf391d6a963e2f46dcb3b34de509254b223ce8c532602bfd600fe080235761b6f24e35f6a869247389e27fafaf261a3302ebb431b8ef908c86b147fe26568f2beec945500d20a16461946220dc41556fc09aa6ae0f757562c86b4d4539f1e7eccce199fd06a9221d358cc0dbbbfd66db5193515ace6da5cf705b604ef665eb3e048a0091d2b6c82bcf4e3f9f3ebfee98c17dd7b4f796c867536767b7cd01e648b24260af29d0fd53203c08b4eacc24c3223bdb95bc20fc8422f635e0e5d8188e393b1e7aabf1fd025d977bbf4498537dd1809c8d4f3736424abefaefbe35918025ee7c3571bee54aa24a8b812e6c4d5730dbc7f04df7009edf8a6c80f2eb13eeea47f76177ec317bf7e97f452a20565992b4be9d856d012b26309d4a9acbaf736c5b917c42bb663468a2cb60c53e6e81225db1c970f1145e4fba55a75981d1ee8a0d93c3f221194b18b5d80cf453dc31a28b52c8720c7778419ef6f03bec2acd6b32c9b1ca1f9e575ba939756158420b10fd8ba42b0a8a67881c5a02d339a71d2a712361d8c6633086fc4e7676e27b82b06d429375baf1f7211242630f00c9e2662b30ddbe78ecba02aa976ad1e9036230408908cd28b1b62b594ccfb9117f9e450ef605467e290f4eba7cc659eccd225262f9ffa5f1313713f793743decc21e2e3cd0960194b545cdd53d36782747922300978a244e319a6c6d89727be7f6119bcca83ddbf8188658495ea0d1e090db51e8821a456f9c1e93c05c94ff5f2943c71cb687bb2a613ae0524cf2ed3478b5e16e825a4df6bc173e72ebd4dee10f62973780746d87d667b46db12dd7becd1aa1c625d08739eec7fe5ac1aa259bad07272df6327f08ef2fa141751f13e538d722192456d40acc4116717531af7ddedb324a90dd8ce98756bbcf3489445ec89dc2a56ff76e9acd6839977f0052dbc9f28feafc3cbc4ee225aae2369aec5adf1881a4bcddf349e614e33616aea81c97e0823d12b5d471624a709b21f17b406aa532981df1c06a00f315c817b93ca03e6b51f0f87738873c4717095e95cc4390b651671ae7f2cc09330f65231d1bcb0396062a2c990390507cf4e4d916bf62281ea04705d0990539c3be4863e38e4548a6b11b462fcffe572710b9839fa7053790d0fdcbc54ac8897b836768706bc00cec2396a68dd4b6536194d5bcb608215cde515db3ba42c4907b9e7516c51c00a2d6d98b91c40e4540249dc687254bf6f0e261a75ef9066f9ea43f9854010451680fe61ff14cdd2d837e478f3f25097f4cbcd1905831494cf90b01c3565af7e3d295861b05bfd2c4cf50a9615cb2228cfd93a05c5ed43c593260b00d2747a6b63a9e2ba2c61130070f54972aefc1bf1fca6a214a664368ea883c4153b2080c0da033b3e2cf09cf70b79b2ca3604d2768dbc6c25c54d918a2e32ae2b15a733b4f0ecc57ab6b53131ec55effbc7beade097ea9e17748761b4f968e77ba7319feab82d5b5ef23646e40de26a20665c3f9d85370a14bffc2db9ef2fe52f4835e51a7f869f540079e51bbd0b65898cce6ed666cfa12d11799b1b8a26b300be1b1149f009afbbd3690e9cdbd958462b08b25527e5c91e2ced05551874cec301f57b0460c7682ad99d95f975188981188383caad550e17de8f35ec3412101e2368d797eec0fecd967428430dfda277f9beaa5d363ff5122a46168b8de0b4f951d9b065f1cbba637dd52d2192b594355084fa3a9b9a090fe79e7dc890b555ca849b7c88ac83af6d1f73b87e69c141e8d86195424c441a1a5c0158e48abb771bcede8936a1b9113f55898928ca73dc75e5b6c4de406f4114e8708379c7e5d1433d4845b35737dc65e84327b340c01dbf65cacc706ca58117da8029464130c99b5892187898e58abaabb6944eeccccf67dcaf9a8adc6ba52f351fc70df1a9023ffe1b3ac2357f5661dc9e3e2da37226434eb80f8c7cdf0113bda125efbc22740e9593a0b09928efb71520453485365391c7790d931f68712f1e3914c9bf5018a3df1979f3cc1d9bc8ce6c59ac9f0b4d3eeab6392e4bbccf01e75106c9caaf1123582416ccdd87ad50ba24944c8af7d2e663ac666e2f45951d48e3d982908aa887a32d5f2812c2bc7d0819d57f474758bee18c53c5b275fb282c23fc095cf698950df8016f2948992fbf6142aa4e0e30a5e736c515b1f2c02b195790411f09de4a0cc204bdf0245a9a574e4f79a91ecc1bbb69a48b311da91eefe7c39b57535035eed99d825e0d1ab768608aa47f30306158ef34edd4bbbd0a0a9d6cb21f1ca1da8a519fd734a1c7184bce140551f50816a085dcf4bdb4c92ed9c14821e951be0b6ba610ace2bd159bc83184e9e675bea003a97278fc57182a65fa1cc3f8f5fe06c3955051fbe3c1b4c02ddedd0e8db7a17e9ec190dde15709681ecf7b55113616b516a24fdd90a53286c1514d865e7767ea9ba09547fa471ca8c329de319b9454634395fede811145c72226d89cdfc98851bf5925f6e1682b03cb157a9dfe93962463c49e37e794fb4f7bcc8ef15f4b95a08ee504b424bc2b9b0ceb50d95c08549b29ac4433d6b5c0e112e25496bb7526998764d265ed5723569ffdd6ccff3f7b662fa9b12c449562c4f646654842a558c8b1c9f244ecd0083f3d836874363dff34932fa01abab49421c8e40cbdfa9ff52d5cd9887a9395148a55b111d46832f98328dedc0cae8db24360ef8e97fc6d4e5685e8e40ac31a018afd561b36f06b595090ee4caa5595baa0d1525fd0a9a465799925d7749e60163c569702c9ce5c359a1d07b70d603d86cd18217349a14acd53f0bc030edf5511ff984a8cb7855e63d9c51f7ac955363e610eb9758994f4b10b66064c5a456314644d1ddfac620436c9d8700d2c0fe22e6304a1675686a2875b236afed295fd05bb3ac47584e57fee2044db8b3f62dd73c55a56bbef55e04abc70f00cd6141222e712bccd22dfb053db450c6f9b67a0f0e9158ff01f4a955005f899bb7eb8d921c2cf4e03814886d1c239f5a451d9e6a1026540307703dd10a05a24714d7ccd0a4a0c13790c564d83c3d3a2f06a0bc05a1c61445d8a1a40aebac9fce29c3a241ea1d19f175757fa05361f97de1490537c9e0c0fe9bea0ae2b4d014760d083bf1ac8dc300c7b476fe73e89782c02646bb380554fc729de6d7e84b4a0f373b6972129ba59b38b642558fe44e48ec2b07c3def9b782483d8a45dc5ee71ce5f2ce907a836cea49c19e51cfeb0f1b546a60ae3efc45458ade65d02447c74c6ca74061c5324a909fd2b00503aec9ec3ddd84146744b9d29dd9ecd507cdfc617dc91fd1a28648209d385173df6d92f02072f3577ab4e023049b5a0f79506ac6ac893f322888d051f9643dda5e82fdb3a9dea6c69b37008312ff610a0bfdea80026e645207340443fed37069a0fe383b75ed6494fd3d630334568b5209a570fdad7193e1076088607464721c381c121e50a8277fde1fa45252b8eaa0093c45e8714ada412fa188436a26f46c5ab225791057fc6ec5b9223467da3fe1663b6b1a451018306dd342d2124c81e5cd1c74c727bc60407909c9e65297271669c7a921a2cb40321eee41107d80636df7868c993350247387951661232392a971ab5ddef7a26310c617af0981a260c9665c71129c442cf034f627f494a2ca59885a5ce8f1c854b1da0afb25ae9248b1df9a14ff11608dc4a6f8ddcd2d7b39dacc3a35c9f89b08d16ebb6ff7a15520ebbecec07fd074db247d9df3b0af7586012ed8346e8901d0c258cd55cb05ce90f89f0b55cd8593da3d72a350af0e1a9c5d7ac18818e8c2ed516815a67775370220d07270f32a2bab24e5597711b7ece5ade6d223e8c9bd433195c85cfb22324dfc23d98db74abe107938e348182357e25d635b13397c10a5d1a5646bd39f10e13977bd19648d86c980b224741e3950f923c189fa7de5f071c2b675e233a8d975e1ae439254275baf0177cb2fc7d2b56854946a10039a91dc217fa761bb0d32fbdd6454952081adafd2c34b8c868aec6d98f0ce42b92a473d41de0b8e19fe52f03cf4d73e6c2ae367f476aa9db8d11139e4b3bb44588a4c7e72db5ef6579f8014f40bd2d00556a2c42d14c767dee9ae851a918a2ffcc449f4a209afa2d34b900dfc8b5101e846c67d3df2a9094abbdc3ef9f1a9f5da79a06012e37756e68a16cd6da82d916c17a6d00049d7bc40ec8583047a2ff323fc833771588497c7fd1458b07c8b0abc1b68ed2ebd9639d52df43c565be65be3377720525b270e3b0295caf51e1985bcbcc88d0505cb0b7b55b050f2ae1e8db0162582716f9db7a4484d9c768536b3974165a121c546071189caf4eb69b67cfe0cd3b3b1d59107c902e2eb2cdda7b1c7694c673ca206a79f86c1ffca04ba6f33b91ddd00ee174600ee6994a3d302ddbbd15c7828ffe05cf7ccfd91a873b56a46b2501152f803d7e02d9396338acc57809d06ef1d137d82c2557ff5d24764340eff9fd2307f62425a6967429b64d92f334ac221b833ba98697428f2c3e85d309a279bd9e02816cb49946a30fd532a1a0fb89d39ab14a9a894795358324bdeceb41a55dd723daba1de94e2a573870b7b6a8f934adff43734d22b5760b8958863f9b67f1472e94e714fee447cabc676bf034fea6f76208174c2535b4afe9d80204dcaac64909baed85190b56be2dabbe24268ffe8736522454b17ccf33167390d6514980f103d6d6b2a28408e7a1e460f70e572835abcb3e5f421087307f79822ab1db970423c688bc5c514aa0c845d7a880875ac269a7d1a07c190dec94d0adaeebd9b98a1f0388b58ef832e3243a0bf2d16550c3c71c996e75bea5f8213c66b63c2099b40a5e6be7c31ea4943dd66be1b0291b07e00b0863468128b1162f795c5fcce187039276ba1e7a672e4652f683ced72f5ddf271f3b94718bb96e0fe9ab74bb27882521d9cffd656320df03d1f2de945c6b4d1aa98af9c601d1586646fd843228b40ebaabafc00b73f2396c55b101a518a1e9afc8a8ea68a9af963e79ce9941e5d20ee983990f78ffaf203dac89c95e8abae36cdda5de6355f92eff43775335bca410a40d8e2a65586fb089ed470b635cfce7a12ff2f8b3d30799f707f4c755f4762ae8449dc1c2ea29bb8b730ae3ba7bc1f0a4b921d02ddbafe7193af11e58b5dc0ce5b0aba68ef34813d98e93e5e583031b09c03ba7af58d13ad32dc8d01c2745273b080c6255e7ae2d988312fc53fdd72e45a684a444beffdab1ddd37fe6a44d2d86ed6f10435b658fc474e5edc21bb4431d910505eb635e93c7674263c2ec6b1fb34e149f747638a1dcdc050f11dad7b7e8d065aca7506c507402e3377068d604e34eaaf674bfd9fe78ca3abcec0ddcf8f6bdddd63edfb5ac26d3dc71bb55a302eb54b023b184308479aa31779ab38bfe4c079bf64c261927a9ab29024684ef282b9ee273058e423e040347318becef2eb3be691dc31d88a5d4ca81db21351314a73eda94f6c93a9efecf96e5b3b6ff4dc188a0903be1b982d18c64d0cc00a10584ca47a49fbf12c4b47cb84113c978554b15ce45bdaee4248bd39c6604064813a5506d7cc1616735f0cbab341ccb85183f35d85299f22376ec3e462f27ff2922207204f8a3b0d84905b621d1b23396dce3f221802cab32d6f9eab465a923f114c89aeb7e0920c1c6a2d977d1d47925e60fc5d9dda0ae95d65a55b85f79117d0f5157194cf4cf4153465a2159496b100e590e95c49d32f51e269d6deea9211110cf1f23d59f979e136fe641878c5a12ca107ba89cc33a14349bd240b386cd4d0b678476c9b29bd37c1dba6c6f3c0d6c75ebe15d2a0824d717afd4300027b4ac2577ca36f078da8ae3e5a84fc2ef24db140aa937d8ebd806d73ae1fd23085428223722496760f2e56e2fce9c872f3f4d0f01b94a2370b264bddad5635afd70181bbed96e63dd35cffb340dfdeaa33bca7d2d315a7ee4448ed7c4b503f1282d5c038a7eaf34a2615bfc690fe5115879b0e58580c68ad170e7d8bbd686c6796438125cb3371bedb5c2a25ed7f6267303b70f7fc9f61256b40affcd13dcbae955f7207f8f01625ea1f57d1a826ceb00266f81b2e20b858cb83b8d4f60fb8c08154f14f074ecb9e5c37dfdcebebe1512dfb0fb8bdb524eda27dd5c852763ae846fea26575543210da57aeabe876f760921c932a68fe762bbd03d1af766894a1ed86a28da92abde2f9d21ad9471d4a4284ee9fdf80990583ef128320745707bb783d319112c0e40b98c05d59864c555f81ffc838808a57127ec66acbcfd7a2c731f2e0dd9690a47e11ddc33904030cd07b0e3352939885b6262a867de8f9821f0e5f721e58457e22c50272bc506b71b21d3ac2e2f37d8c54b265c26d01ba437a1bc2a07f8401e73dba5feed49c616d8741d05ae99d5a1cd60942089b64ac10a16c108245855b5ae308b2959dce5637cb82ef4c9e79adb804721297abab577dc19b1760206337651b3eb4384cd69f9f3bf18b2d4fa030b030cc6e3080255efb74345542ad4b20297c048fa3a2f70bd883e133a4e872e61519141479b9ffdf24080460519db4fb299366e2208d381dd1259499368682198369a5bde6d6652b8d71a02b310af0409c98816450a84aabaf3ff77b2ef99fc5249a0dff332457c31b66a3fa60370064d536106a22d8204d019b89b1d8b6fef01c337a0f21a62dafef82705da0c6d144be8c7742be9b07f1c3f62b8ec0ace292f09462b8a04dd3b5d070f83752cdfa5a3421ec582635806dbe9f7b4823be70252b199459b582bd30354b7f7d21e2c028f596814988d8192e31d97c6bd00085094c19b1b6102ad40f5e3dc162b139e03a3594640c003fe31235eb52638208faee9019ec21d2eaf1e0806fe0a9b0844255999c8ecbc879487961f7675eb445c026de579a2ab8c64e11af9bdb02b0d649300414155f6bbe2157846e682765ac896378142bf7cc897ad47c6bf922f1cef8aec47001f925029f48ca18e0a67d9564272ebd5c7333265e30d0a59cb940ab65a79efa6fa683f181ac7a041e0281751ed9dbf057fa2e7096b321d9e7f04239c1619ba20a42fcf86ff7bfcb6d5a95d2f2c9fe292eeb82ca7b6aa5b066c902b2f3a182a03e3cd0911d8da65c9ab51f3e3ffd0e3e5420fd33fbd355d82cafa1018f921624106f8f7d730fb60b5c2c376c8aac7cd3be4abdc9bb9abc81e56212b809c432f5e8853cc06d9070e2a711a385d1db13c42aa124db365427d0ae26a2c23bdfdb957e7b43fc759f17cd9b1702b4f6bce659fc70dde9c63978aac49902ab8c18dc483d6da31ace9c12636f2d173c202c50e4d488ee4b8b8af90f8e9a826895f5e63dd0d85cc708cf80940667491fb9dbb106423c5ae2ece55c871d2e42b37c2f0da7018fc93dc69714cdb24c3ea6677f55332868f2fcdc7408d51d949164b1bcf1f55f71aefc1dfffc6420a064081823a25adb3f7b3e6edbf384e21f00931613f859f0e6860156f6d19c40bdfcc8e05ce960555a9491244c19f36c94e02f5f84b0e5d301e5a765aa8376aa85ccfb6da13e611b40335d88fcd2d567aa1","public_inputs_hex":"0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e940000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000041390e9098d147b0784030810969efcf00000000000000000000000000000000c017075b91d1c9c5aadc2f4d3b29027511521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a"},"decryption_aggregator":{"proof_hex":"0x00000000000000000000000000000000000000000000000edce069b20cdd3b3c0000000000000000000000000000000000000000000000035587edaec19bc9d0000000000000000000000000000000000000000000000004e8b61a1848c5d71d0000000000000000000000000000000000000000000000000000ec07f134570a00000000000000000000000000000000000000000000000285b9d961e7514ba30000000000000000000000000000000000000000000000037ced53ee4c6f829d000000000000000000000000000000000000000000000003b4542de3f9235c48000000000000000000000000000000000000000000000000000295d724d1e6c40000000000000000000000000000000000000000000000024984d76621f77c0e000000000000000000000000000000000000000000000008d9170675a02d3baf00000000000000000000000000000000000000000000000a55ee4aa4908eca680000000000000000000000000000000000000000000000000000178c72a0427f00000000000000000000000000000000000000000000000c780a0719338d9b0c00000000000000000000000000000000000000000000000287dc8993e1ea3e03000000000000000000000000000000000000000000000009b918c594689408490000000000000000000000000000000000000000000000000000e6a0f127f38b1c0664c1e064d80eb94bbb91785f1d24ed1d7be9507613a3cf0d4f6bf2e9d7231d1ec3f000b4ae52b58619010189eef29373ab9262aa91aa7143502034d8863011d3224f2e2edd4d00c173d14a0563b3055120bd423e5ba160b9b85989830c122c3d4e983a824d1a6d29966c7111df5c9199915567cd4aba98c44fd0ae834f9a277a09cbb2913101f449f75e3676615906ce01379d63002576ee7da737262b32236564d38f5e7a803c1f8d9ba4618283f2321568857b28f7c1f0a958192e02761c28d6c7d667f72a60c1b47882ac5bd519c2f272339121ba07a2660621dfe03922117962f26bcb850f7c4d6c290c1394ef955cb7d8158b965a8f73ebe55715b60a5ad26fc88ba140f26b746000167b877d731647f2bad22f5e22aa8662107b3903ba9d871d08782e78385c33c1e9322705d793ce23c873db9d99711f08a2fd4419bbd315d1e4fcdf4ecbaa1f63ced3d1a9e6d8224596b8db1f5bb494a2016bde28d00c42c42b88f68c6ad1f771011f95bc6cde5ad55f886bf7594359a1faeec71a2546cb3ae645c2ceba21aaac656c9691eff73811ab3f7599e839f35ecacf0d13122d59cddefd6b314e97ec4518cd154f343b7a3e4a803e21aadfa71512f8500e046fc1a29c86ac30ba80b92d6c88a8ff016611d89caf17b20af9ea60288bc707548bd820b583f244b94937314f51020158fbce2e9118933f203594ad959eed07b4b35a6ec429eaf7613eafa54fb3dcbec9ee7bedf0b142fa863ff3ab979f532a51f208c9b1df2dbc7b483ed886bfa98e9725ea544775c7bb9d667028f4b19627f5ca4f842d8e717dd35f718dc7aaf4ff57825a57ce340181d5bd8e6d2147ff2b1b1daccdc7efe44863337dafc46a94c0c29b71c850f8af09d96d588b92035d04470ad5f8011691979683753d0aba181757afe9aadd77cd8044e703fa50efdc03a037ec4c811132711ffa1d32f74ab4c4d105fce937a1e674aeed5311039f6c1b82ac81638d61248c4974ac1a4204cbefe6558773523e38183701596fc70db51e4ef18838b97f26696532758504247d6204e1bb16d1d4a6f7690e22b92ec22b1e7216c99504649f548ea5e76058166e6ab62d8b597da944b3f251e0a7f5dd550cd43a77afcf6fdc66df86ae1f2a0bcbf217be0e001d5db52d8b348ee751b0191305dc8b39e1a34a8f2b255f13e90c5f4175cb2e3863169b1e6b74eddd072b9f21a8bc5ba5c3f55267fc4c73f55b05016a0db089eda8e476039e4b47da5f9dfe2b79a3acb1133d7b29f45ff225d0c4272bdb5904c6558a7731288c110a549d491d428dfbb0b13b94fe735c89379880bbee36f28f8069de31e430ff99288c66fe0c006bb22bf7b4a9592cd03dffe27a9ed159b0659b109004e5178e4bdef83b282a5e323d882ab264f227bd6470279199a135bd78b51de8c6f36652f1ace3c0990bab78767cbb925396a35508bdbb408df3194860a19d252babb16e91402e2ed12eba5dff99e35eba65c89111f2f8ecb8ab001a0c4d0025d5d2c275efa03b181202fc7f806b44988c8fe287a384ea1afd59ac50a70e7d0d47d94be1897ec5e9e203ac5d9e3f5f1f7cf379e828e01f76efdcccb0614b23beddfa5f6f8cbe09e9e41c6bd67b5e867148d005eb0eb83a20c69edae66cb10f0399f049cc4dfc577fe92f3160e22735ba35b84465fffccdec15f911306df24c11ee96201e4b04d2e1490d1ea90f45ed5669b39f607652462fd9f10ff45449ba0a3d69318d075e365393000429a8ffe54041f667d458a24a950cd629a1f25c88e500b915ef8b8191ae931e7bf88306e191275053149520e06fc4c5ec65452d0f231b2a840a1c2e47c4d6274c2fb4eb7d2ce13cdd3eef4bbaaf730576192979cf71566a194c11723e7cf52a8f8c03407906136dfb54c7618450df427bfe58533534b6f607df93d4e525510fb61f91c34085310910d69d63416b1144cad84e2db53f079311f0c7d2cc880222cd9094f071155155d56e58f28055a5403eada3ea64bd00a15222216f54a58a11d88c720803f206e5ae51935e5afd64b155c4fdb32ad6acf0efc7a2fff6c6dc29aec8db1d185dec27709075fd7366dddb28a9909f20b637e709d5ac48b066d2056728aa6ea0b695a094e8b077280be353b709c208c531be2174262ee74a6ddd16cd375703f431f50f42d497ebfaafb2204fbe31c1db00f847c205a9e5bc54db1f42362fb4a9b97d732b7b2a543c772b785fb139c372ebd00127ff15437a653f0397d45a567e5c6000a4b9ac2812b07511de3a692e5b94e5118dc68dd31ee21507c2f4c9c52f9c54b45ebd3c2918942e1498162908e85bd8cea61ee40e823bc801a3a24b6721333c061e10ebcfd63353a8651b95377d721138d170246bf515650ded72aa95d8673e5c8e1440ad005d31d9f75f78a8402b38522bd14a172eb6171201a44177bd3a939cece9958e1b278c01d40beb9d5b46360c4f07a43b49852420a12fde2f03060a123ad784f488d0b4872e94d10feba6c2fd3959e9a9b1b56210cf7b3b52aa00a90692846a05675ac063d0e933c1bf8f8bb8a99cf90967576308d1f7402a634a8031346f58838220918ebf2a36611413ba04bbee53f23595c2011fec50e061c919f78a127db735b3c640ae6c8f1ea70d4b8216595f35da58041939c77c2d3b9526ddb4971f0b4442675f2e645daaa4cbc3441c424463efa5d42a6350ca221811927c5c08451ea4a05499a29d69b219f2c993d451a437f43d8516e679a796ae6143e42a26668b048b4ce5c0debd5fc4eb4e934d2e6ba7b7b5b31084748288df95597c2f838cd23851420c57562eece196a9cf56f605cd2cc5802ebfbc1e5f4d970648ca521f5dc09194cb6b31cd11e5dc65a808aae5a72b0b8d06f3ed69825af86f15664eaf3d8cf9b9f44edbc0cffde603a1649a04381d008c1586681d08744417cf896484566b7c9f40d9197cc46472eea063bd1763135fc824fd8ff1e9cd4bd25ac764edfa4127ae79f3b4b3236c7e149b0409e4244575bb064648188294df57511c5e320a52988c7a98e71de98924c63cc4df72c10f76770770552f6688be1849aa0a91cca874e842ea9a0d080d3d6eadad68206fbe2ada0d07de35b059ad5e3f3944dcb6a650ee64e230a53f4f04a4e9ec9e3330f9312e182d90545db4f65973f636cbe00905c00d1cf4c173052b4f1deaad0f09239cc80e6477cb573bb32c25ddce44a1b5997eef3b1489a520c3e714f2bd84b33964b020a23f0a9ca584020b90dc27764852b6b9fa3551aab838b649b78767cbf40a1b1b928595c0ff6d5b9a2c8dfbd94eeab852563fe2554b29e55c8966aab39ba2cf11c5e2a750468bd5b34956ea7e85d226cf5f99f6c7d0fec89924bedc43b020120e17513393289e3cf9a0f0a86447b80b25275c4e543758bdfe998290522ae8421a90eba5171c4ce624b6466846d2ae316de1ff93039df1cbfaea396d150477dd040cb85d01066dd0015f3151bf7e2ef022c1be954af6bf1032312afcac83ccab302363d2e381c358a1bddf79f73bbe462b5df71897535eb55e73bfd38ca44f510357a687f0b64fc814ab4f3a84c2a71894b89bd2089f27aa805757662a9c5006200d757d3341fdae52baa65b943829ecf02888b4401bf065989bdd8a2cb71ad31796f5df7bb522beccab8a547cfe2cdbdce2fc7413233b72ced05ce662f88af9123ac93aae51fd8c1e8025da7bc2345b3b110b5e59d2ae2ffc729d0550db5fc60329666ce8fb0d6356568f1ef2618ac7f7e4bf2a6e3ae35b7588780b29e198d52b298c4ed6a93551745f03889c5752475272e91f87e836ea54caa93cddbcfc8613cd6384d28dd49ec99b2232309b798fa694072d9772542aab79584eb091633d202572c6c995c7268e871d4f8840d366099e7c124c6f0f37a91b629079c7835500d621755dbf91a3c126649d7ccaa9c979b09aad02b315869a542dfb4b7366831c2e3955c00d7fbe2c15682d53ac21ce310fea224a7c8d972cad6c4b1415d0a91278e6b67b2a051654d60eacacfe4f32e698bcf8bd7ac192bb696d9e13994133116a66f3ab150806fb12287e271bd614aeb9048dac568c1cff2c1fbe79ade43f133b89887f6f2c8ec436d5b37eea62acd2eaf5508a77662fa8bc2bbf5c39dcb225f3bd14c21670a9294633249d98d602eae71803a11c9bc9277e1f3ae18b2f3919f778be7ca5b0d88337df43757f52b0a273d554fab9f5649c3c9e3deb5f0ed82e597504378f93584af394922da247f0631ec17835c50217ae6f476c04220a57054cdd47a98bacfc724f2ab5d7c7825ce80f5978e502f847663637b90af2d961188d67c326e5be62c40cad44d5d128f5b18fec898bb36bc426c93f982f6d470d17bdb5f79943b681d033607bdbe16be4da1a1dd8fe1e61ac11a1f2b4cea2a3fe26d2e66df0f886c561d05372a059845d520c1f729b37c5ca4093b912fd806d9e15d647dee4fa61a39a465c97ce7cb5a3793b99986163121f8545744c3ad753f82ffe37a90ced4f23815bda2c9278462546ca5ae118e198c4097d3e7dc28e03be11f8ba469496e5bd7d9b6bf4a8b5d56b480e205e03e82cce8b21f464a4dbb6d52374e84c1acdfb69fa9e531159ff2f6f261fab2e661214c69bb7d93f5f0101ec12e85651e59e0436e3af84b244b29464d5237e0de9f96b68da0e0e756b013792218cae5ba282e49b29b371bb74fb66dd8019240639ca2424c3e739ca210a056506c0631528a20cb89f1c2a8ecc7dea173b6ded9a7ccfc9abe627af2d7ea6591a151c1673a09d84f1132062ddbee11993003c72dd6f4de585c418319fd64fb87e18f994b3c90618cd566283a36193487235f5e20b3eab9b15e3e842246b3df0720ff304ddad5e0d9b443938d38b7c6c8ce9341d9ae8071016a21fc694f170002d163f7f12d068f82cc36e51ff8eac359d86fea4f76ce490557e9c8d628cd2ec7311d270d3b58ff7ba7d52f5d7d3c8903dba14149a80ab7c9ed09988a5ea0b82620fc56bd2241ec60e84caa696384f1094b28877aa06e1061600b4e581ab5bfa85176412243451efbb1dae13d6e78968d3476151211492d8d6bea0a32a28eb70c814fcec1e1a77683784680cccec1bc494e96aae146a4e663a8dee882caf2895d80be4fde7baa3a49d24c53c0bc5f54b6367c26837215974a6cfa2ee738edf84b21115cf41b00618506749df9221f329a01dea694776bf3abaa9fbb352f9454ba01fe2a5c04286be6419fdd0afb3d051ad92a46178d89410fd158b32d3625afacd0232156648d27127995b308f527364b3162e0eac7db4e1c9d7222b263d2093071643a985a9493381f9224fe080f7c49e0c1f178b1cf486bdf027b5677f67194f17990a030cd95227782c3ce2d6184ad12c5988823233924a13ec460a43db62a518a59ccd5f1069fc98131ca373c6b618fa7792591edb22dd0c242b5447019f57229b7191aabb525535024a14782f1ff968b3a1e8be97503ae7cefb6aa8fd3a352f3db9f3b9a9e6ce148345848e3988651f5472abfe980dbb8743ac95f00720131054d873d7ef9d60eb7bdd6af577d252f486808d1e8449abc1d68b69f4eb56811a8ff281cdaac4ef304f4582285d5e403c5367a43eb7027b36be9351bfec94cb0b607e652ba04957a4c0fc0482cbaffaf8932b90d94796e989278b0d100b92f826b213ac5b4b3b8f3028b80f591d0274f9a65fb57e385ecf47e33ecafcc031ae2e8c65f2a0175b4ec3b0ebdd832f955e11178b2591b7cb64f3cef1263b56b4bb2a77690f4fada83c47188bbb06ad5c506a2bbe442b763efc4fe3ea2fbc21e7e12fd3c530be1d11cd7e2272464c70d36f6a87717d45c02039d089cec0e1e812bc0f0f466212d8f19f9582712cc3d7f4de6c659abd9ec8682cf25bd9c827349d1f2f9b83f6082eb0d1f5f99f966f8411cce43872f5299bdab07880b3cfa693c9da160c1460f44d22314988c3a6dbe5127978d96a9dc6cdb3e523ff3a7dc68192cc133676d1aeecfb4209c6cf678cf1dade494eb15819b2171f776abba11bb5ec951a46ef47e4bfa4cec4e833fcac0a9fcbf9ce8fa3dbba049ee45162512a1d8eff00670e517adc74f7cd0627f765323e0115bf5d66b6302c28b66300112c7809a720ea9b409f976ebf64fc48d0654945028900fed3563e17258bb0ed64d410660a2819a435f89469d02c009c5d5c810725651ad230e22f758768ebad58dd9559b527cbeb59f0d6c6bcfe965596dea8e090fdbd70e9f18d5d6199cad88165f329d612b8518edc1bc88f0431bb6999172a8802c4ed1c417dd26aca97ec6358938353292e3ede19a0fd0e699fc08f072829424760b71457eadb10eb25e52987144f92080e4c20439d7bf3fc26bd9f95d7c87dc30e6f6f5ec033a751f2006d050c75961da6b26abbed3ba81398b3a374371dfa96a1b6ed1af5b9794c5789d0b9a912181e090050f3ef76dbd74eb1b18101e748671915502be94c168d34a44382caaf3816c14e6456dd88c14d8e68e2a0d3624e29cc00d6d0086d6fcac00011208602d21276b44c40d08f7edb5f2dedbd3e7a955ee1f92f0806854cad1ff3bf05cf91a3103b631f8c0563591c5d1e70e97e16b1c7d05b505ab1643e49146500cb77eee01d79a14852e3fa0beafda3aad1e41941c3e2c9dc4c4630f1218656d719506dad0a2a35a616a505d4ce99fe5e476f59413c41b02fc17c1c6dd0e2a06fd16c203e0d43613f60be6b6ef8389331dd4fedfc337d72277d459c4f6a5f6f5923716f2a1c23488142dc51a76fe1cadb7a3a1989ad7e79d1e63b75e8952f85ececd4851f22098299a53a0608b5eefe88e783328a37526cf50b972c25f782a2e4b8c0832c302ce119c943a5659209e9395d43a1d411fda6b050c14e7e1ddcfc24df068e781c194e17b295ba95cfd03df3c17d0ee6a0e36cc8f432b6c339d1d81bd80b621804194513ee0d0de54e0c0d2eb0bc3900cdbaaa7f49e6dd212d386a15507ef3f8200f7c2a499aaba3d59ebee961ebb27ef96f5c1d8b11b46d6fab6162a3f22f8315578c760b7d247b7c7aca1925eb17442fbd0010842c7faebee5e0d3df7cbd6901dd2c01f4ef6e0234a6b5ee31e14e71a44f652f51db365417e717efd7cf0ba920e728f72b1246ba4a66757bc3d6986178a01c5818dd0a1a4fc7ecb03465903b103629d6df9333ca804018e7d7422aad2a87673bd59d3b53e429b9abb82559921620f508d103296b6915ce66e75611f67f8500111ed1ad5139744aa1f4828c3d07f2bfe7999bd6a3eb38147a887de506421fedac4bdc00ec8138f985fb7d034a01300c4b88c4adc6eff96c0c86ae338b902caf73936434123b14e06525d0ccd12ab7cf13b2e53f7590b841b8fe4aae1252fd75c40e2069390e5ffbb7ab3674a028d85230eff2bf8759207cc777ebb790986beb140f46d20e9bc7f4ba078dd1cc05d91b86bb252b812317d54453271888c960dbea5ca6d90195f0dfbe8e9c5d9203fb3c294d6b473b0a16ff79ffdb3ec24daf6d59dbef2fca6013dc937fd103572c818726e3561594626eaa1f45db3f64a8964104a9272ab81af3a1c6bb478adc1020f11aa1bd01ee31f58f4ebb848520510dfbc05408cc2787e367ef0c31e41328e0accdb8de5dc22799b8805f173931694094f7c0e5de52dcd41d815d5b6c7c2daa0496def1b376e3a51d49025d9f4041715a2b593d7423d0248819a54246e5275cffd211d8ef63b85f2b0958c61c32d58abbd65ef70ec68ce84be78310d6ef264bb4a81ad1622c4fb0d54040f90d90786174ed1e353935ee3f169a98aa2d58029207afc41769c02bd3deca0c5403a93efac655b6a56ac4d4838dd6a08b1e1a1410888fe7e934796fbcc11d77c3b0c2edb80437a769787966939c9cd55a364f3061778bcbe570cdf79ee300718671ba9baad1c30888faafd754749d691e71801b9821a796991690747083227e61d17ef7a39b511cd5b007568a1758a9395fd016df8a16e4e56ec6379bf190011628638fc021a696e3bd9603d9f3fd7e1e2c571a4b5945b7cf7dd86b8981ef6b97b7bafc2b22a55bd6368091f03a645ef0df97279acee5fa55dee3ab0191d1633027c14609a74332b0eadd3d6f3ed85a7956680f2f1c5a7c9445d8390821c05fcc9aadabee41ade1fa32e9421313381aefc7b3170feae96e81634e042eedc9d30d9e1d523a550d682760ddc428c98fef51b1e221048fc6822f1c3e89a26903106cb0cafaa69eeefdc7ef3305925ffeceac96e121206290ce9d91c8ff16641033910e84b4bd4bc1ee56db43b62ba86dfded8ca113d00114389bbca8cae5cef3c14f725ad380e7d712f7d93a1e588647cfdbdc5402c7d4a698eb18c2bdfede827d90ffacdea8cc298a5629b28514bf731b7998f82e19b16c332526bc1b3713c5b41b16489bb6228f176ee6f96cc286665fc5db930036af42600c67bbf26009805dcbf054487b9b0e1c2cd7173f38be7459892ec90652b26f72abad8dfd40edde3511e0898abf30e957b7e70b725b2653cec4234d180fead76b8ae6be8b7e18ed0f6a6922f146b264ed870aec50f2bbf7e4d178b81fc952955a05a27f760b0adfb14fd86c9e21c4b6ab0074e6df091e08653b89d324e1e066a0f2825afb9be08e90f11ab66a57602c43d45ab5f7d97ef60a8670a61fdb46b341552a18fad736bed3ae77890a408470e7c308696f37ad26289f8a7f00cb96b31fcc1d4b0cf0f058943e8249b5723f031c2a02d659c2a761fa0d05d002781fac13ceb35217f82bc90dcc84665530ef2ef7e326470ca0fae8f6771a0b181d7f987290f7946bad4dd103156d149d2f4d7fd9350246dd54b274f5a9c2ef256421db001f48a5a3b95deaa6967db143533599dda5154770528c1c4a01f2bc246ec9f2caa1539470292b344cf2ad33e67a96fdd2e7d07450df1ea9e5d36bd510b5a2b1d115be0f0700e1a5652fe4b304d99ed587e6ff6955cd02469a5fecf829b17341a8f46cb83222faacbd1f539b89a186ded5664e49c7deace0b5233b10275cdc7135f9be5e3c62a640ca27b22b92456b9e6f146bd6e805cb071d32b2dd00449a613510837c67588fa39fe0e49fbf31b99b5144c9476150973ac9f0a4591d17e76456b59e88170401c1d4a92b94f17ec904a2374dee427b7664a6b2c22e240e1ca86974fd9e5e172d8647abea27a002645a90db6bcbc5e88dff24f1743323be66dd4df719696f2bd75926dcda82d3accd3c865cf42853941ef41de95af32c261b646deadb7d82ddb53574514e261a14aa12cf773f483d2545dc4a45105e23de104c8ca2eaa8d94c42f4ec7c0e11279a62ebba987e6c329eb49f2037b8092352ce3c21318796a17ce472d8ffde018425fc0b4c11f3e492475d2afe0c9ae00b0cdb4f51df51cc833ddcaa3aa78b74df3d936893f0a5714b7810fabdb17c0810d19fa485bd97f1507a77fb95b2a0c5b1ce5830dea0de18ef5f6c20a00e15d12331f7d203d3a5c9d261cdc4db79b82c59a02ec3746ce141a29564fc67a7e6ad0035707ce5a02d5035190ce39d1ade935bce8d2db6bf855b748412c8f6b441480b1da708c7587d576017bfee5fd7339097c4e205a4f0deb9385a3a0f7806fbc92f5490de7ba18539c7d8c659406341579581b124dce903c88eb72a4f4a86eb22093addd77a4a8b8f2fe48a564d820eae33127cfb442fedb91ed8eae5381ba9f1046e54e4e5c6887713366918f0ef69869a1e1c1254bd5e205917d5d5f7571a2e1c9b575c9df2ea2c0f6738f4a4206330ac1320b7f16f1906c4b067a64294082e2d754c8fa48c6470cf77edb7e03bb86f4a396b7ae2dbc56fbef172ef0cc08caa219968432847a0b5b933f0498d92e427b6e0d75969eeea525ce41eac022e468c0514420c561aff6c8edf72164ace360ef5ffd7cc2ed742cb8cfbaf855a20491a228ee0455b1013c4b2788e4afe5f5c3cb56aeea9c0aba0c2a3a99939fddc306c2e0d4dac3598daf10471cc14ebc2233d710393ea6ef90252aa70e609081040e814d26daaac5656530fc3586cef2d6c4743200ad764b5e4849b3afb18e28c13512271c93f14df40b13bb0e4ba277e2d20080be06d4b74dab1c3b51ad544e763f125ebcaf349f694dd1f6b8c60c2794ee456f96176bff72351704e7620228cf9352b65dc2e4ce420d8ba84e849a41e2986f0bbafc592af6a2eac9a74d6646379bf22dda3078b0408cacf02cb1a4fb0d4effcacdef4e5aff14cad3d90b0b79cc149129a4bdb67b7ba6c4333b1167dc29e2c02ae1130f373e25a4e602dbe1f9b88532001917a73ed406cb1019c2fe78c0278c373405583d6b76103ce83ac88def2ae0a8be4e4d15523c70c2baae6d7a2d10818b8894c593f7dac6103a75be2c4f6531ed679400c7fa71c82c9b8856c0d03dff14d810a3863018124c30e248a61eb93227f470138981ff90eb35b5ccfb27b22f9c3e99c4b8470896d7618b570015aa826ec67f903e741cd58a559e1c5451579265ebc761ea6a358754df10ebd689c5218df613d6ac78aa2720071cac7fb34ad6ad194e3e654b8a344971ae9a0d9fd530f3fc109ed0f01a69f6ef0208f0edeba13cfca9d6fa2999f44beac2fdbfcdd312c3132400d2921f726b2eac70133a0dd787448687c557fc1e69083e8169747421a39b826c54c494f1ac98dec8be7e330044daf7a0bf4eef11c79644bcff8298e0dbb24bb3a782d13457901c8e2ee0609dc1247478b2001d0d6c9db518ac9068208fd83bfe7d3508abdfa2503267f7a45de285633535685b6da31d63d4e2ff0720154ea6904ca630ce1857e3e8a1464cb6b5b16766638074546226ab14fde7afe2c0e86924792862a3f8832e46311b61bf635a27a61b3cfdffe662e7b8341fb9514e51b6f5688113dc4f5f812945d7a8ccfad91ede7fe0a607dfae5741848c88f01827ab0ecad4343b879f24bf632dcf9ce5db1568b3dcb0e92a3ea5d32bea5bc06fafea5a53fcf97c3dbe274575ef305fbde8c7eb4bfbd8e48bb3b062bce895e258826ec523ced0e17c77f73323f26abfeac8c7d8e401107f7240757fd4490841615f11738971be20699fc6c8afe8c9cdc2a78df1dd876c7265f3824b959bb5212c9b389a43203fd0b16ca64615dd125232f77f77153f9983d1a3c54b847aff7022cb4a694c0f04531367c7cd278a7f7a5aad796dca8eb486791d297a593f69912bd5a038e02b40783d1345f2cb58f787712a1ab0316cc7bc64d21d6035363f51a529233a8e89a1dea82a455dd42bc3795828066ab8dcf048ee7351d55a9a7870a0dee695599b4a9bd9a6a5cf01e1dbf2d89ce252c15cbe5271c2c2815edaf9f05db450c973db3930f9a551e2af7210dd55bcdda11077913f597781cd3a16457088bd5d22510baf958ef5e5d75a44055207b030f5bea7a15e7d6263aa056e9db1369a3521bb7832d51831151d9344364cc9337c3560d387864b7382aad762ff81f0a1adb64a26323cbf1d2f2be86ee22c9ce9dbfa51860184340226563b1b8af04deb0a186abb9d3d399c0a8548582177aa8378309d08aadd8aafa187508e9bd146d89b17af7b31920d5e2f7ea2b5881a86e81376d927a6a75ef1ad8dac6140219e02d5d2df2b473fd67a74b14fde0df6c9e9bb9253edb6d885b748527f90b30265690d0032e77862cbe2c5157330f90e10db865899ba2004fb7aac420bc40540b027d6a2389de4a85895dae7e6438772f59708f6ab2c0a0e8949d890d15d5fb14cb09a333e1d04ba6263268e7f6bda2915967987f0d2e45df0eb9e601aae96312e39e1add5778ec0e13ea0feb65580f7d4b73942aa5fb43cef4260dfd71cd6c25ca74631d5f182895f93bd853d8fe8c13db6e6a3064f4984cc00f502049db6b2f3fc84e7f072beba9e1e0da592e03103c6884b2241da120a6ce8f0dec4a023e0bb68670913ad55bd31adca44ef3afdb24a74965e6a5a4f6423c682b3f2505f119b8b942abb373d83f159674fbd223b4082c6a943ed5f6a3c0c30b4cba04c56d296e61d11f994ec45b3eb7c0fadce0892b09588510deeb84ad0fb43af65800ce1cf2b42a1f77afa03429b0e724ee01aeed3c0f0da7c26e9e0841f6f3e6995a6c26394ee8c1cf6f966c69582a31ecde78b71a2cb6980da3b820348502c53b6e762025993aab0dda298ac2c722b379d1769978b065166b59c953e243260294dc7f0811a8e2eead9e087b065819882752fca41693873712be6b9592ae57f30124af16a149d2e3e18080acb56723895e4b6b960a2e565c13bdbd7f4a56947457ace609911477284348841790e22ffb6407be7a6a3cc3cfa687abb560e83b8f95b12f11ee57180949ab894c6f63194f9795bb301e12258bda6bd19eef208895d3646407fc4b759cd026c4d8859b45b2db5951a4567b74ac30cb25763fbe11ab5cefab02121a2d8d2f4e68fa8da95a1d857eb9c915a24e6d65cacfdfea17b4f7c20b3a2a449be2dad4aa3acae6e681796e2e1c80d8e2be9ba6f33c83eb5efd04de8fca143d6424e42e247559e0d90173a80aa319d3698c58d33eeb2bd7224c03fe44de1bc549874bc66035e4ee132138100bb60a755a543c6598a5f06e0ef5d37d66891ab1b930794b5c2f560004d2356bb0b6d1e1bee7760b5f7d6e676b61896e91e72ca9789023470f3e234285e254c88fe67302f0c605e59670a402401135af417519c6454310e55f64af70feea1e255cee278895ad44da1a078bbf0f676dd705f605ff24647ad14d866c1b6d0670d72dbff0f44d02b049695345c6105d280c45791a7d544c5ef1225e1fb2bc196bb9aee9c3d7f0ae59c4346f6ef9e2042ce4906712a0284ceacf4e137c3e49b7305912c45069a5009e94d0de009b8412b1c101bd13792df3d482832d5622d9765b0829e4ba254614a09747aa80f7f11cd11f63c20711b449ae52188e09085afc42d7d3217f74f29686c3a5e4936e31ef89fb578f2655924c25b7b1a31ad2f2019eefc28890279d30b8fc665ee19dd8356f9eef3f0f754a9b70b8dbec4e7285dfc7f0ff474334c6bbdd8cd3c28f964fe49a17f0da160d18b7defd79fae2b4a2a0d713034f58e89606b1c905c31d316d878cda2f331f4f17fd9ace21980261985cdf4b609d527278aa16ab00b84f910d4193f280190563d7ed91b44e80458bad71e43f4338205ded6dc8fe68809fcad4460e3d1ddc2d2a4417c4dd117adb272f1fd4b5077d3f1b108fdb6648403ed5af2132302a2b2825988faee937f12c17e377767a51a562014dc54f7043c27df06d554e61611821961b9bec98d5c2570c9f6fe8487dbecc40137e68fa352623d570be4791c99f0800ba8d3b538ff0c41b13d298ba9924a3f180d85bcf37e477edf58d311f11af03fca8f7e3bbce16b2162db1ba146b4e895f914de18f120676a9471bc0cdcddf06d65a86b6b351545c1b659a08df6f9e19f66c3e0ac434fa776037a4e5ee4b3811b508a56849c1eae0857281bf4632fb10c571f2dc118a695db6f242ce9fd044295545f486838cb226f250ebf115a28f9f3e6be7b7c89876e31069d9ea516f0815289ee7e0afd2f36c1b2ab344cb6bef65eb037d2e4d2f16cf5504daf24290c00bd40ee9122929b83e4254115a940b02985fef66afdf26bfefdccddfbe2f25650fc238758bac9ed132a290886daac1dfc82dc410475fb48d8cf51ab1b19d73d829b0cfc486ebfb3a89eb25393460833342fcd2092edffaf42d364b7d80fd766a1270ac3e8f5c706c97f8d90728a2839a8f2bfa3bc81939a540515ee8b6f4275c1be4ec2588f003e9474418d88830187c2ce192fd7c69c1907d0e73826c3f86390c0af6fbc19e6a0db23b87a09ecd4de039db42fcb862c3315cf4580d5345d32606bb0085509bca0d4a5eaa1f3b0a34432db80b51b372b3cc68a16d16edd004a70796d5b65c46f72f16383ac710b0105a0a9f4b1efec577d5c4af544e9cddbf942062e58be832605b62a9c89c818605ec5eb258ebe45ce66d99e1df8ea696d1ea07f1787d1346652c451651f25e1134e6cc24bd4557df3930d2db2550f98653001ed3a64ac328546bf26e19e7dfd77c354788d256fbd110b9c266dc66aea7e2ae0b7cbf43d883fb25c91d1b778e277812b660b5fbcff53511027ba6d54afbd88d1adb25a5c52d05da1438ce5b88f7f61917547932ebc310994832fc7588df225b1c393e22f16abb8017e1118e20736e7844a3fad04dea0fce59cb6d2f477904af2cd57babe35dc13c3964fb7b49be5b34f676fbcc1123ca7063f42fcbd9d2f9f31dd9dd09efc55e5dd647b395c73e84834380708e332436b329f726a0a7ed75830f110176b0e1d9d3f313e2d89a5d1e0e053fbf010e3e20fdf030799fc73c51cd2772ad06cfd94569f5b8a998cba9dde2b6b4e50ac35f7970e38b814e063c368407349b0d618635aca08298e56598879e02db373cd6ed499931c2c617346fd15a23d8a1a020f6b71cefd7744a8b42921d02253d8d84db053065ba311bf7f5af1401196a38b0999ec52fbe6ced3f7464ef2472f4bac6cfee49af2581c31e5034980eab6404e8aa8cadfa913a7492149b7bbb8dc6fe77616870c2090e32d7ddc7692d0fb3a73ab606111690b73c8959bc3af3755223e56d07e34c8e4d0b492b3bcb1b96798b13cc92b1638a3f885d83e9f30b14cfdae34a307c233c578190d65aad0f421ecd053dea61996f8adf5906cbb7dd5e7a87169580a1ce5328d3d0937b28","public_inputs_hex":"0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000335fc711e25eec4bc7002806d9b00c9600000000000000000000000000000000cdab8c25e7ffe0274955ed025ec557bd01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db70000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}}}, "test_exit_code": { "crisp": 0, "folded_export": 0, diff --git a/circuits/benchmarks/results_insecure/integration_summary.json b/circuits/benchmarks/results_insecure/integration_summary.json index 4ea1ad0cb..b8820b36b 100644 --- a/circuits/benchmarks/results_insecure/integration_summary.json +++ b/circuits/benchmarks/results_insecure/integration_summary.json @@ -8,164 +8,164 @@ "operation_timings": [ { "name": "CalculateDecryptionKey", - "avg_seconds": 0.112547319, + "avg_seconds": 0.112618042, "runs": 3, - "total_seconds": 0.337641958 + "total_seconds": 0.337854126 }, { "name": "CalculateDecryptionShare", - "avg_seconds": 0.623812430, + "avg_seconds": 0.613634805, "runs": 3, - "total_seconds": 1.871437292 + "total_seconds": 1.840904417 }, { "name": "CalculateThresholdDecryption", - "avg_seconds": 0.565414459, + "avg_seconds": 0.566882416, "runs": 1, - "total_seconds": 0.565414459 + "total_seconds": 0.566882416 }, { "name": "GenEsiSss", - "avg_seconds": 0.125009639, + "avg_seconds": 0.12575218, "runs": 3, - "total_seconds": 0.375028917 + "total_seconds": 0.377256542 }, { "name": "GenPkShareAndSkSss", - "avg_seconds": 0.230471250, + "avg_seconds": 0.229232416, "runs": 3, - "total_seconds": 0.691413750 + "total_seconds": 0.68769725 }, { "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 8.597811291, + "avg_seconds": 8.659371958, "runs": 1, - "total_seconds": 8.597811291 + "total_seconds": 8.659371958 }, { "name": "ZkDecryptionAggregation", - "avg_seconds": 51.185872000, + "avg_seconds": 51.718500333, "runs": 1, - "total_seconds": 51.185872000 + "total_seconds": 51.718500333 }, { "name": "ZkDkgAggregation", - "avg_seconds": 21.084386667, + "avg_seconds": 20.93329175, "runs": 1, - "total_seconds": 21.084386667 + "total_seconds": 20.93329175 }, { "name": "ZkDkgShareDecryption", - "avg_seconds": 1.499727389, + "avg_seconds": 1.496832486, "runs": 6, - "total_seconds": 8.998364335 + "total_seconds": 8.980994917 }, { "name": "ZkNodeDkgFold", - "avg_seconds": 63.746265722, + "avg_seconds": 63.680252444, "runs": 3, - "total_seconds": 191.238797167 + "total_seconds": 191.040757332 }, { "name": "ZkPkAggregation", - "avg_seconds": 2.189493875, + "avg_seconds": 2.149436333, "runs": 1, - "total_seconds": 2.189493875 + "total_seconds": 2.149436333 }, { "name": "ZkPkBfv", - "avg_seconds": 0.344567750, + "avg_seconds": 0.345419874, "runs": 3, - "total_seconds": 1.033703250 + "total_seconds": 1.036259624 }, { "name": "ZkPkGeneration", - "avg_seconds": 1.381717805, + "avg_seconds": 1.385717208, "runs": 3, - "total_seconds": 4.145153416 + "total_seconds": 4.157151625 }, { "name": "ZkShareComputation", - "avg_seconds": 2.753499194, + "avg_seconds": 2.745120014, "runs": 6, - "total_seconds": 16.520995168 + "total_seconds": 16.470720084 }, { "name": "ZkShareEncryption", - "avg_seconds": 2.574498532, + "avg_seconds": 2.550104026, "runs": 24, - "total_seconds": 61.787964790 + "total_seconds": 61.202496626 }, { "name": "ZkThresholdShareDecryption", - "avg_seconds": 6.255039736, + "avg_seconds": 6.228828042, "runs": 3, - "total_seconds": 18.765119210 + "total_seconds": 18.686484126 }, { "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.099047375, + "avg_seconds": 0.10385268, "runs": 3, - "total_seconds": 0.297142125 + "total_seconds": 0.311558042 }, { "name": "ZkVerifyShareProofs", - "avg_seconds": 0.227246316, + "avg_seconds": 0.240555883, "runs": 5, - "total_seconds": 1.136231584 + "total_seconds": 1.202779416 } ], - "operation_timings_total_seconds": 390.821971254, + "operation_timings_total_seconds": 390.360396917, "timings_seconds": [ { "label": "Starting trbfv actor test", - "seconds": 0E-9 + "seconds": 0e-9 }, { "label": "Setup completed", - "seconds": 3.056267000 + "seconds": 3.083043708 }, { "label": "Committee Setup Completed", - "seconds": 20.242110208 + "seconds": 20.233524667 }, { "label": "Committee Finalization Complete", - "seconds": 0.006024833 + "seconds": 0.006022792 }, { "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 310.649556208 + "seconds": 309.654077459 }, { "label": "E3Request -> PublicKeyAggregated", - "seconds": 313.306656917 + "seconds": 312.282717083 }, { "label": "Application CT Gen", - "seconds": 0.321336625 + "seconds": 0.316875208 }, { "label": "Running FHE Application", - "seconds": 0.003349958 + "seconds": 0.003470791 }, { "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 82.112244417 + "seconds": 82.613328416 }, { "label": "Entire Test", - "seconds": 419.055032166 + "seconds": 418.546447792 } ], "folded_artifacts": { "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000ab6528b828dc60dbb00000000000000000000000000000000000000000000000a1abbbfd7f6dfd38c00000000000000000000000000000000000000000000000830618157af29e0dd000000000000000000000000000000000000000000000000000030b0b3de35ea00000000000000000000000000000000000000000000000188735db8f53dc39800000000000000000000000000000000000000000000000a18fc5b2c715b3bdc00000000000000000000000000000000000000000000000bda61492423a3ec510000000000000000000000000000000000000000000000000000dd0290b3c7a9000000000000000000000000000000000000000000000004983364b7fd2661c10000000000000000000000000000000000000000000000039b4556d8607bdb7f0000000000000000000000000000000000000000000000099a73ba6ee66e29af0000000000000000000000000000000000000000000000000002f88cb3525c2400000000000000000000000000000000000000000000000dee1e0248c53aec7a000000000000000000000000000000000000000000000002d7030903bd0292ad00000000000000000000000000000000000000000000000d3bc4e4978a08e90a00000000000000000000000000000000000000000000000000023bca494aea202d963ffe2e1c6090bd0417565279b28b01889be9222dcfe21d89b8bc37ea633a118425af1d443927e153024e09a6f511d70b18941e6691c58bd0fcb01eb5580d1bf539f221f8b59e7182eebb1d4f8fe20f6d071ae56a9209b65dba1db587938116ca9f42700f6637513c56ba8c9b0515960d790c8b4dbe376efe344d5f935cea06b4b861140217a2a61f3c7e65b5f762ac0a03f3a240dbfa9e18f437a7cb30b5199c8aae11cf8c30a4ef2de6b9622cd84bd98d344b563e0ad901317254e5f708029cd7ccd824db93db38c3ac3983443731fea6679ede17b63365e86e934fa6bc0806ae63c2a4d1a99d5f8be2e7d9a42bfd10502673ae3e598c2c0c3362def4a927aeb4cd7a438200bdeecfe20a3b86d93a666bcc3eff4dc3eb82ff02e3a2b2a4218ee3aa235f850585d5e928808022969398b5b4c2ad44264feea427187abe2a2e359653c433f3965bcbf214db7711f8bd8bd7fe71c7feb983cb463ce891add81689ee23cb1e15053533135d6a5b3354bddec9afb60428d5db053cd7e6f16b7f01a8b5545ffba4cfb3fd23b26cf203722f86cb76878b459259f3a08d9fdd32f22153b36759cc3a604f7812d907a333a091afe1821dbf642c8b8e522071c729c924b0e2aff5d72d70c59180b7f69061f709c6a2508e68a5e86ce5914fc7dc55aa2ec18dbc526b61e9cf05734d48c3955e23058f894a535ac974e102236e60bef51d63943cc3435eeff39b9905d4ba6d221c1392add2485af108330ddfe0c76da1192a7156f072b520802de68204d5b130afebd9f7de607b326d6265b1e64ff2182bd0e3458273fbbd7f7f5f6fc7da4acc89468c6a82fd21a84bcf8f5a169b502e14cab9389991a4d1aae476597ca83bf5e0a3d2654caa22b02c7ede94e1f4392015227284b88fdd39e7406e3efc1896e8668d4576465a3fb0bc0203261adb539e04aa1a23894473f04e76904d56e7a5861eb2f5aaaf44aa8ef22934998ca79d482884f74605be1e455bf594fbb689c1fb78f53d217184a674c725df26407f7e712dc0ab88ca15039410497ce8310927ad651c80cdde05da3e9ca5498ffbe89e01149e820d0a10d51851e40fd7645040132b3c1a4813466249d6037559300da0e5143232ffe0c122c73393a689d26e91faacc5b6194d8843088e11503b50ba986713933bac02d1b0a39e55efe0c4b8bdba00b2104956c6755aaab1e33961c91c792546146690dc9bfe19b77238cb26d4cfe0b9746ff20199ec3579c7d3e54cfa7624c17e311a082443911f00cc9be18fe6126814303a40656a52db542563901a33200a084c1dabaf2302042c0e7387813c36a24d46cfe883888653580f32706af013bf242fa9b03a2f7e40a45f1d23c9048effe8eb8de9f41046e35a8d753851e408fcb71165eb5d6f6c197c7a0fd6991b155db680a06cdd0116615e653c85ba7c1294aa0a07ef05bfceb690467dcd7ee88dcf31ffcb5453db3f71a2ccd2e75df523584ab053db076c6fb57ceb1356781e4eda6a4b1603da76bafd0bbbb6d61f40260ea34d7aeec03ffc1a93c42370f74c0a4ed8043d950136de56617a7759274611b14e0b002ca984eb4ae6dadce0aeccabd358192021a15bd33b76dc01c877b72bc09a425821140f55a66529a1cfab1abfa742c9e78c858182aa2db6b9483e892e467f3a4a9a130cc8d5b4373149254a438a33a1c72975cdb0425271599c4e9f02e35e2aad8080ce76aad49fcdbb528dff3f2404d0920be45890ba1b29cb4fca098bc976b98680a06e873daec3d1179855d59cc2f420e1a16e5b7da81a1b4eb907576f97540f9df83d2c5ea725c1adfb2b1e789cb0889b2dfed9329a9345b70d2da362dabfb7e832f1c4cc319924756eca3240f9faff90eb37224a7f33ce899e2e5130c2cfa148e522504889f859d71a412b10b374d84a6c139084239080067016429a61503f916f0ce4db10b7d0584b6dc3bac6df045686fbdaec850ff10b901466522e6931ae2771820cb1919ab5fec1e9a4fbdf686b1c66df96b7e319f243122cd4186f0f615e3a6b6e7d7f0e4968b1380f3751d7b3bd220f8c27a46208d60a2487ce2b1ca1802aa8364092ded7ff00976bcb3a1620c79ae30146031a010a2c0a2a00c6e4bff75a514d26be107d1b126c95cdb6e677736d9e665678bd12f70f209adde22fc74e1ee6b059bf66f54b3d240c2fc4b6f50ddd82d617ea2d5cba1bde17c4fb34105992103768d2b0378abd71926522ba98afed09931f50b3bd9527f78f8c6fb753b05db1aa4eb4d283344acdbd8fc4e4e473e470dc9807ca6d252048dcdb39999d4575a00011ab76c1b9cbebd74d6e61de94ea700464f14ed7b622d1c86a8dafc5edd967ffa4d14c8783edbe39442888f1878b9358d0a8aa78480f4ca426772b8d77e03813f6746c729583e1b0fb0013b5dcdf2643ec202799b52ac74b84233b3e19972530e0eb9e270c31decb023ca2216a641da5b326dfd8da24198fa386e1e60ed5257039de5f1abe0cceff64cec32c2e001b5ba1cc1899261f97f33e03b7b2b70d3f9a7124c4ce410f818051c3d38b385386b8a2d29cea72273660298664bbb39f126e0a51b6e026dc983640af54b7c6c6c9b19d3849c2f8242fdcbd80c208af1b934549fa4e256cc9c5c87e44d3f34429af107e081d8251296ad5ad4027c55437aefa751b0084f58e19227dfffebb39fe09200304927d8d04e3dbe7bdf246e8a9034765e69e7b4debdb242daf2a654116f3f6ec3b527eb817127f4dee47fdbe34f8efc4114aef0b3c73558735da92b92d0c54acb864b55b1c1b879c3b64e5efef70fb093f10c7b26f286ff8a95cd1fc78cbfd205dda55b12c079a072d723e63051bedcb0e9ec0bf522e4f219d88f7b794beb58b79be8ae303e542dfa8998fe8e2e40f0c2df8960c6fe54f1252372d030134f16fa2594a412a70af625481ac842b428aa11075415bf98856d4fc23375f09b6769f988976570f54169c1ecd9808627f0a9a85e1468abbf435a00fb3d1668484efd3724c7682008a87ea2bdfe7d46eb25ff8378ee96849409a8fb37eb7011ad04fb384c4dd2c1f4ef9f1b37b1371558b8e2f93582cb2646f2c09724656efb97d3337dcab06c90f0e0a99dbd83d4a4040273cf915e6042171711250cf964bd163d23a72972b3f0b9d4b10137e2807a7f31b2f9004fdb8b520e002a13526ac923eee0f81e6c1640d58e1c704ace55a8fe098468cdcac47a514ce7c13d1bff170761bb70805646c1ce220f83362153b1e348491bfb71d71e1d7efc77b6d24c3ca974a94d52c496905ab852fe706d48bbdbf5ae25ce05dee3e8b7b6ae617c3cb19530fa069d0471f21fd12a73708d3bffe920539440d67b4d3f416ea872b8201a1fde3ec5cbaf9b102e000c471ca6b9a956924a7090f27fe186fed06b74101b9320e5c21353be11a02275397a74cdc6c9c22b70a9888537e3672d2af698c54b749648261a50409f2047b34e162adab623e609aa4807edd86ab29f032d7d7def5528e3f459d1c83ff184be5373b224676aef2e0746332fdfbaa300f276d444f7843a2a98705aa467e2de40fa7cf1cf09ac1c95460fc6add42a149d1830c1b3a1ba13fe1ea634d731306cabf66feb50a478f667de4ba425e8a0d3deaf6848a0ab8c7db3f5574340a7d1214de510d4c573dfbf81e2ced85574f085b25585d25656c4c44b6de923a840a167bd567f720f2035b2a456790a5c36363ce092704be976aab4b7d59302d178f0edea2005a2dfde72ce639358d5b71b0d71cef895d6e7416864a2b7f83d1162a0d570982dba35651835593d240d35fdbf3c39b92d70cb1eaa52722b06dc5c92226b4a401b7cd3ac29a59254af9e4d233300734b4fb2c968ec5e2893566f7ce921847b7ba1aab42f4e4f5f51c638c03a13ad23805b9de2cc0a7d92549c97bcf6c10a78f15aaf95bd7419f2b73befe6f639828985bd1c8b28ef032a4a56adbfcfd1df4856798eed823feb886065eb10586cb72ab2737640bfa4b7c5e5fff3b2a652f48aad393feab573c94a109a7801ee0da56281d67b8691a8ae036e9060c4fc22964f9dfd7d8d3f81c1e68e0392e1c8f672d3cc7daf941f5eea32c8c194f85531d2643255822a749fba1e156e0c96de8ffef2b002b753ea47633d0a35078298b1ffc0eeb733fcd2e39f1ef634cbb169a64529f0d8c09d7e4739795946e0bf9451f995d5977e1a6c154229e20a048e7803169e2aed6f306b8e7e503853ff5e70403ebe23597b0a87ab2d5e0882414db412e051e15e0eb2ff95069bf0ac8c145250e84bbe57101fa3560210f08a76f92c25c8313f9330ce8ab268f6a37d3f955450ac7dd3e876fd857582c12d23c67aec92d75a9cc0e8888a9b984c31a9b1a290f2d8a3296f661b42d2bc8d60093f7293e6d5152112ebcc19bfb2b431ce691187f1622524074146516bdf0695d725a9b2b8b2ab8b171321c21f6bc9e966e93afae0f21beb5db56c01e0764cdc9cc1fafb0e77ed45120a2859d7ace771c262f609f23337085ad7ac0bd1518b19d7b02f22d36007e4a63a9655147b2549b74911bb02309ca97c90114bdc21fbc2e8354aedf45453389690da39edac5fdda7d03430411465676719090813825915d4ae9ec91c3afe24ccec744d07bb4de99add1605409b0c014deee8122ecfec860077f4e94dc76c97d6ed198b85e40174939c8b6aa15b8c1050960dd58e39fd5071db6b77591dc9529a230ca6af9fcc9f005b5caca043c24781515871b203e1de9f33fae96804acf9dd66a37851e916e09d0eb92f42232ad796741d77958f87d6be73a92fe19723019c9c9191346e428e0d06611952a980c1b7ddebc02477fd7417b32342780bb56756ef02acb202e3a68725a381f2b92ceedad2961000dce17ecfb2cc250be2c22e51fc82287d365e7ac359789ad2d6ff81fccc535275f3cc21c3e3bf4a04791627dd6d29d721f27082dcaa91f6e07f16049e9bbfa056dfd61d3b7b4b669787d139ea5119d4d9935cab7396c0e5d1214293575a65ee565f4922d3bc625d71f6a09afb3289208b9d3483c05975430031d01ce4ba374f41d28ac18c62bac813ed4c81aa4092905795358f08d116aa20409f44609813fe3641045f1632fbbfbf1277c4a783f5f412fc58b5d902d9fef218370a7b167710d3e2b896fede982fead14198b2bb122176a2cf6a41e596fed0f77a6ec2b5cc237a288ec5558415477ee54a5823a4c25b9a935a086381ae87400e36213fe3c54fe588df40ad8f9910d1289375d78aea52c585e6b0d8b000ca70a2814832c8ba9d7102d18c2c6432d2b437276444d455cb827d2cee930edc7e325b7423f5022749fcafff9a8e0f17ccdae9d2495be960cb92e0522412d116d8c2f1235fc89fe520e96cba827e08dbcc18942ea99d34396d07a510b7e4f2da3dc2448ef4ad4743479abd38c5ad3321ee0024af73947e13ac6e8e05ab58d0c1ce70d39e806ed89cd8f4be46b3890b8e73f6eccb674e53898b7d20c3858642f051c2e1d4894abeee0ac8b42e5098272d0c70d20fcfb4210b2b050c241e49e197cb11f53f6ae5413b466feae19c03d4818c347e400fa24a88187cebcb8e2a03b7f6700d14e54f9fd3815afea6aa78f1eab9ea0e63aba4e6051e188a1e3e11242f1c219d4bcd07f03a22b20ce8dabe5c7e276b88230a1bcb9968fca6fa9ce70779f350e073860dd63b61332e41cf6b742f050a43c9ada81ede0273e2f1d7ac851cdac0e6b76c48a46bb1506326939d912cb207b0452cb0657f810168ea8f5bb5336af12946dc6a82457d21b99ee430376f2943713cc5c0ea1e305d3f339985477c86d1461abaa8a5bb1949440d17fddd237e7aa73ac7b53f8eaddda829d7ffc6860de18f796e1c8e4ba59c7c8268e0c2c1ee35edf60c62230d3abc9bf57818e7f32a40a0f716c567d18e4dc7658bf84af3d5acb0c49f8db7a6c594f2e4b6eca678d460cb93e47b1344d2e4703f14f1d8d2a0555588ca28e19c7262cd4e5cb8f32bd1207a5d911fcba9704a8407c74e8cb344a4bfc107d6ca66320908508105c49253d04f3d0fbdf6801000f6c79ab6e24279275e5966c2eb653daf4f55be52c661aad0600f4c6c80d9d9fc0e43126c9fcae4f8593cd15c655f6096617f7c219bfb81817655fcbf9f285a52b89b1472f10724b7fb50540b377da2b45384b198d1772f229828e95ce47662a8f3e49e0606ee53e2d860c44af493f0d3b0d572070e8e5f20cab47637347d09170a19ae0a99ae74354c855b4611655aed2de1fa258cc243600dd47ad410fca16aac2a116fcce260a895c81a3a89ce81a19afb5868b3a64de0dfa78ffd98eba18398883fe8955fdf45716dd3bc94011f7062506ca98744fba2fea32b10cf3b2394da53d9988e193d4c5d06789938195241605941bc9d5d66c04302095ac006752fc3cbb0d7512fabb3a4b93bf67264df85bad30f28d468745212d98ac8e9fb00dbc19b1a1f92e24cc8573592579ce3af143ed04a9029bafdf1a0f4fa7afce28cbbefae3194c23921d61d988b4c18d29138ea3e95e3786f75600de9e1339cb0792aa919d41b3bc0e35a96053d13228d0d0dc37732da80dcd05027ca25b34cb25393e1b9d30065a5f8cf325982c4406199a0b4ec569928809ea1765a05be230f3b04c5e6feb4244c1c94caaf2123fa942a5d05ec8ebd4b446d52f44119a9447ed0b7dd6cf5e9abd2842fac8c1fd2dc88a968fcd238dba2b100a27c7851b2997c86cfba255e10b7ac0d6c9496291cf63ccde6285d45fd46da38810bf3ad0fc190493c633a698581fce39efcca70a352620f72803f7587656d23b2753b69783f660b76c896d865dab1fc38c104563f253241ac37c8bcbc68d88e309e109d15f9e10c2c39a1fbee52879576826e06802a9c990ddeb73299543b1871b903b1927c1f9e25478e97faaf3bf8cdbf206994210050ec9dac8bf965f3e6e08ed6d0746709cd2aaa86c5e32d1bc480d3c8c53e9a4c0fc785cbee882e360cb2d6016adbe6d57df4352707b43440ce847343bc82190047dca88d2a925a10c882dbd843bee3d2f61f817dc396bb607b3069ce2007f8f38664f16fae5814c4f15037e191d5f2d381491bcfad0ad7714c0f62326f534bd932a85621328d3faafac292949dd9eb0c36f2c1f14868951a8ccf391d6a963e2f46dcb3b34de509254b223ce8c532602bfd600fe080235761b6f24e35f6a869247389e27fafaf261a3302ebb431b8ef908c86b147fe26568f2beec945500d20a16461946220dc41556fc09aa6ae0f757562c86b4d4539f1e7eccce199fd06a9221d358cc0dbbbfd66db5193515ace6da5cf705b604ef665eb3e048a0091d2b6c82bcf4e3f9f3ebfee98c17dd7b4f796c867536767b7cd01e648b24260af29d0fd53203c08b4eacc24c3223bdb95bc20fc8422f635e0e5d8188e393b1e7aabf1fd025d977bbf4498537dd1809c8d4f3736424abefaefbe35918025ee7c3571bee54aa24a8b812e6c4d5730dbc7f04df7009edf8a6c80f2eb13eeea47f76177ec317bf7e97f452a20565992b4be9d856d012b26309d4a9acbaf736c5b917c42bb663468a2cb60c53e6e81225db1c970f1145e4fba55a75981d1ee8a0d93c3f221194b18b5d80cf453dc31a28b52c8720c7778419ef6f03bec2acd6b32c9b1ca1f9e575ba939756158420b10fd8ba42b0a8a67881c5a02d339a71d2a712361d8c6633086fc4e7676e27b82b06d429375baf1f7211242630f00c9e2662b30ddbe78ecba02aa976ad1e9036230408908cd28b1b62b594ccfb9117f9e450ef605467e290f4eba7cc659eccd225262f9ffa5f1313713f793743decc21e2e3cd0960194b545cdd53d36782747922300978a244e319a6c6d89727be7f6119bcca83ddbf8188658495ea0d1e090db51e8821a456f9c1e93c05c94ff5f2943c71cb687bb2a613ae0524cf2ed3478b5e16e825a4df6bc173e72ebd4dee10f62973780746d87d667b46db12dd7becd1aa1c625d08739eec7fe5ac1aa259bad07272df6327f08ef2fa141751f13e538d722192456d40acc4116717531af7ddedb324a90dd8ce98756bbcf3489445ec89dc2a56ff76e9acd6839977f0052dbc9f28feafc3cbc4ee225aae2369aec5adf1881a4bcddf349e614e33616aea81c97e0823d12b5d471624a709b21f17b406aa532981df1c06a00f315c817b93ca03e6b51f0f87738873c4717095e95cc4390b651671ae7f2cc09330f65231d1bcb0396062a2c990390507cf4e4d916bf62281ea04705d0990539c3be4863e38e4548a6b11b462fcffe572710b9839fa7053790d0fdcbc54ac8897b836768706bc00cec2396a68dd4b6536194d5bcb608215cde515db3ba42c4907b9e7516c51c00a2d6d98b91c40e4540249dc687254bf6f0e261a75ef9066f9ea43f9854010451680fe61ff14cdd2d837e478f3f25097f4cbcd1905831494cf90b01c3565af7e3d295861b05bfd2c4cf50a9615cb2228cfd93a05c5ed43c593260b00d2747a6b63a9e2ba2c61130070f54972aefc1bf1fca6a214a664368ea883c4153b2080c0da033b3e2cf09cf70b79b2ca3604d2768dbc6c25c54d918a2e32ae2b15a733b4f0ecc57ab6b53131ec55effbc7beade097ea9e17748761b4f968e77ba7319feab82d5b5ef23646e40de26a20665c3f9d85370a14bffc2db9ef2fe52f4835e51a7f869f540079e51bbd0b65898cce6ed666cfa12d11799b1b8a26b300be1b1149f009afbbd3690e9cdbd958462b08b25527e5c91e2ced05551874cec301f57b0460c7682ad99d95f975188981188383caad550e17de8f35ec3412101e2368d797eec0fecd967428430dfda277f9beaa5d363ff5122a46168b8de0b4f951d9b065f1cbba637dd52d2192b594355084fa3a9b9a090fe79e7dc890b555ca849b7c88ac83af6d1f73b87e69c141e8d86195424c441a1a5c0158e48abb771bcede8936a1b9113f55898928ca73dc75e5b6c4de406f4114e8708379c7e5d1433d4845b35737dc65e84327b340c01dbf65cacc706ca58117da8029464130c99b5892187898e58abaabb6944eeccccf67dcaf9a8adc6ba52f351fc70df1a9023ffe1b3ac2357f5661dc9e3e2da37226434eb80f8c7cdf0113bda125efbc22740e9593a0b09928efb71520453485365391c7790d931f68712f1e3914c9bf5018a3df1979f3cc1d9bc8ce6c59ac9f0b4d3eeab6392e4bbccf01e75106c9caaf1123582416ccdd87ad50ba24944c8af7d2e663ac666e2f45951d48e3d982908aa887a32d5f2812c2bc7d0819d57f474758bee18c53c5b275fb282c23fc095cf698950df8016f2948992fbf6142aa4e0e30a5e736c515b1f2c02b195790411f09de4a0cc204bdf0245a9a574e4f79a91ecc1bbb69a48b311da91eefe7c39b57535035eed99d825e0d1ab768608aa47f30306158ef34edd4bbbd0a0a9d6cb21f1ca1da8a519fd734a1c7184bce140551f50816a085dcf4bdb4c92ed9c14821e951be0b6ba610ace2bd159bc83184e9e675bea003a97278fc57182a65fa1cc3f8f5fe06c3955051fbe3c1b4c02ddedd0e8db7a17e9ec190dde15709681ecf7b55113616b516a24fdd90a53286c1514d865e7767ea9ba09547fa471ca8c329de319b9454634395fede811145c72226d89cdfc98851bf5925f6e1682b03cb157a9dfe93962463c49e37e794fb4f7bcc8ef15f4b95a08ee504b424bc2b9b0ceb50d95c08549b29ac4433d6b5c0e112e25496bb7526998764d265ed5723569ffdd6ccff3f7b662fa9b12c449562c4f646654842a558c8b1c9f244ecd0083f3d836874363dff34932fa01abab49421c8e40cbdfa9ff52d5cd9887a9395148a55b111d46832f98328dedc0cae8db24360ef8e97fc6d4e5685e8e40ac31a018afd561b36f06b595090ee4caa5595baa0d1525fd0a9a465799925d7749e60163c569702c9ce5c359a1d07b70d603d86cd18217349a14acd53f0bc030edf5511ff984a8cb7855e63d9c51f7ac955363e610eb9758994f4b10b66064c5a456314644d1ddfac620436c9d8700d2c0fe22e6304a1675686a2875b236afed295fd05bb3ac47584e57fee2044db8b3f62dd73c55a56bbef55e04abc70f00cd6141222e712bccd22dfb053db450c6f9b67a0f0e9158ff01f4a955005f899bb7eb8d921c2cf4e03814886d1c239f5a451d9e6a1026540307703dd10a05a24714d7ccd0a4a0c13790c564d83c3d3a2f06a0bc05a1c61445d8a1a40aebac9fce29c3a241ea1d19f175757fa05361f97de1490537c9e0c0fe9bea0ae2b4d014760d083bf1ac8dc300c7b476fe73e89782c02646bb380554fc729de6d7e84b4a0f373b6972129ba59b38b642558fe44e48ec2b07c3def9b782483d8a45dc5ee71ce5f2ce907a836cea49c19e51cfeb0f1b546a60ae3efc45458ade65d02447c74c6ca74061c5324a909fd2b00503aec9ec3ddd84146744b9d29dd9ecd507cdfc617dc91fd1a28648209d385173df6d92f02072f3577ab4e023049b5a0f79506ac6ac893f322888d051f9643dda5e82fdb3a9dea6c69b37008312ff610a0bfdea80026e645207340443fed37069a0fe383b75ed6494fd3d630334568b5209a570fdad7193e1076088607464721c381c121e50a8277fde1fa45252b8eaa0093c45e8714ada412fa188436a26f46c5ab225791057fc6ec5b9223467da3fe1663b6b1a451018306dd342d2124c81e5cd1c74c727bc60407909c9e65297271669c7a921a2cb40321eee41107d80636df7868c993350247387951661232392a971ab5ddef7a26310c617af0981a260c9665c71129c442cf034f627f494a2ca59885a5ce8f1c854b1da0afb25ae9248b1df9a14ff11608dc4a6f8ddcd2d7b39dacc3a35c9f89b08d16ebb6ff7a15520ebbecec07fd074db247d9df3b0af7586012ed8346e8901d0c258cd55cb05ce90f89f0b55cd8593da3d72a350af0e1a9c5d7ac18818e8c2ed516815a67775370220d07270f32a2bab24e5597711b7ece5ade6d223e8c9bd433195c85cfb22324dfc23d98db74abe107938e348182357e25d635b13397c10a5d1a5646bd39f10e13977bd19648d86c980b224741e3950f923c189fa7de5f071c2b675e233a8d975e1ae439254275baf0177cb2fc7d2b56854946a10039a91dc217fa761bb0d32fbdd6454952081adafd2c34b8c868aec6d98f0ce42b92a473d41de0b8e19fe52f03cf4d73e6c2ae367f476aa9db8d11139e4b3bb44588a4c7e72db5ef6579f8014f40bd2d00556a2c42d14c767dee9ae851a918a2ffcc449f4a209afa2d34b900dfc8b5101e846c67d3df2a9094abbdc3ef9f1a9f5da79a06012e37756e68a16cd6da82d916c17a6d00049d7bc40ec8583047a2ff323fc833771588497c7fd1458b07c8b0abc1b68ed2ebd9639d52df43c565be65be3377720525b270e3b0295caf51e1985bcbcc88d0505cb0b7b55b050f2ae1e8db0162582716f9db7a4484d9c768536b3974165a121c546071189caf4eb69b67cfe0cd3b3b1d59107c902e2eb2cdda7b1c7694c673ca206a79f86c1ffca04ba6f33b91ddd00ee174600ee6994a3d302ddbbd15c7828ffe05cf7ccfd91a873b56a46b2501152f803d7e02d9396338acc57809d06ef1d137d82c2557ff5d24764340eff9fd2307f62425a6967429b64d92f334ac221b833ba98697428f2c3e85d309a279bd9e02816cb49946a30fd532a1a0fb89d39ab14a9a894795358324bdeceb41a55dd723daba1de94e2a573870b7b6a8f934adff43734d22b5760b8958863f9b67f1472e94e714fee447cabc676bf034fea6f76208174c2535b4afe9d80204dcaac64909baed85190b56be2dabbe24268ffe8736522454b17ccf33167390d6514980f103d6d6b2a28408e7a1e460f70e572835abcb3e5f421087307f79822ab1db970423c688bc5c514aa0c845d7a880875ac269a7d1a07c190dec94d0adaeebd9b98a1f0388b58ef832e3243a0bf2d16550c3c71c996e75bea5f8213c66b63c2099b40a5e6be7c31ea4943dd66be1b0291b07e00b0863468128b1162f795c5fcce187039276ba1e7a672e4652f683ced72f5ddf271f3b94718bb96e0fe9ab74bb27882521d9cffd656320df03d1f2de945c6b4d1aa98af9c601d1586646fd843228b40ebaabafc00b73f2396c55b101a518a1e9afc8a8ea68a9af963e79ce9941e5d20ee983990f78ffaf203dac89c95e8abae36cdda5de6355f92eff43775335bca410a40d8e2a65586fb089ed470b635cfce7a12ff2f8b3d30799f707f4c755f4762ae8449dc1c2ea29bb8b730ae3ba7bc1f0a4b921d02ddbafe7193af11e58b5dc0ce5b0aba68ef34813d98e93e5e583031b09c03ba7af58d13ad32dc8d01c2745273b080c6255e7ae2d988312fc53fdd72e45a684a444beffdab1ddd37fe6a44d2d86ed6f10435b658fc474e5edc21bb4431d910505eb635e93c7674263c2ec6b1fb34e149f747638a1dcdc050f11dad7b7e8d065aca7506c507402e3377068d604e34eaaf674bfd9fe78ca3abcec0ddcf8f6bdddd63edfb5ac26d3dc71bb55a302eb54b023b184308479aa31779ab38bfe4c079bf64c261927a9ab29024684ef282b9ee273058e423e040347318becef2eb3be691dc31d88a5d4ca81db21351314a73eda94f6c93a9efecf96e5b3b6ff4dc188a0903be1b982d18c64d0cc00a10584ca47a49fbf12c4b47cb84113c978554b15ce45bdaee4248bd39c6604064813a5506d7cc1616735f0cbab341ccb85183f35d85299f22376ec3e462f27ff2922207204f8a3b0d84905b621d1b23396dce3f221802cab32d6f9eab465a923f114c89aeb7e0920c1c6a2d977d1d47925e60fc5d9dda0ae95d65a55b85f79117d0f5157194cf4cf4153465a2159496b100e590e95c49d32f51e269d6deea9211110cf1f23d59f979e136fe641878c5a12ca107ba89cc33a14349bd240b386cd4d0b678476c9b29bd37c1dba6c6f3c0d6c75ebe15d2a0824d717afd4300027b4ac2577ca36f078da8ae3e5a84fc2ef24db140aa937d8ebd806d73ae1fd23085428223722496760f2e56e2fce9c872f3f4d0f01b94a2370b264bddad5635afd70181bbed96e63dd35cffb340dfdeaa33bca7d2d315a7ee4448ed7c4b503f1282d5c038a7eaf34a2615bfc690fe5115879b0e58580c68ad170e7d8bbd686c6796438125cb3371bedb5c2a25ed7f6267303b70f7fc9f61256b40affcd13dcbae955f7207f8f01625ea1f57d1a826ceb00266f81b2e20b858cb83b8d4f60fb8c08154f14f074ecb9e5c37dfdcebebe1512dfb0fb8bdb524eda27dd5c852763ae846fea26575543210da57aeabe876f760921c932a68fe762bbd03d1af766894a1ed86a28da92abde2f9d21ad9471d4a4284ee9fdf80990583ef128320745707bb783d319112c0e40b98c05d59864c555f81ffc838808a57127ec66acbcfd7a2c731f2e0dd9690a47e11ddc33904030cd07b0e3352939885b6262a867de8f9821f0e5f721e58457e22c50272bc506b71b21d3ac2e2f37d8c54b265c26d01ba437a1bc2a07f8401e73dba5feed49c616d8741d05ae99d5a1cd60942089b64ac10a16c108245855b5ae308b2959dce5637cb82ef4c9e79adb804721297abab577dc19b1760206337651b3eb4384cd69f9f3bf18b2d4fa030b030cc6e3080255efb74345542ad4b20297c048fa3a2f70bd883e133a4e872e61519141479b9ffdf24080460519db4fb299366e2208d381dd1259499368682198369a5bde6d6652b8d71a02b310af0409c98816450a84aabaf3ff77b2ef99fc5249a0dff332457c31b66a3fa60370064d536106a22d8204d019b89b1d8b6fef01c337a0f21a62dafef82705da0c6d144be8c7742be9b07f1c3f62b8ec0ace292f09462b8a04dd3b5d070f83752cdfa5a3421ec582635806dbe9f7b4823be70252b199459b582bd30354b7f7d21e2c028f596814988d8192e31d97c6bd00085094c19b1b6102ad40f5e3dc162b139e03a3594640c003fe31235eb52638208faee9019ec21d2eaf1e0806fe0a9b0844255999c8ecbc879487961f7675eb445c026de579a2ab8c64e11af9bdb02b0d649300414155f6bbe2157846e682765ac896378142bf7cc897ad47c6bf922f1cef8aec47001f925029f48ca18e0a67d9564272ebd5c7333265e30d0a59cb940ab65a79efa6fa683f181ac7a041e0281751ed9dbf057fa2e7096b321d9e7f04239c1619ba20a42fcf86ff7bfcb6d5a95d2f2c9fe292eeb82ca7b6aa5b066c902b2f3a182a03e3cd0911d8da65c9ab51f3e3ffd0e3e5420fd33fbd355d82cafa1018f921624106f8f7d730fb60b5c2c376c8aac7cd3be4abdc9bb9abc81e56212b809c432f5e8853cc06d9070e2a711a385d1db13c42aa124db365427d0ae26a2c23bdfdb957e7b43fc759f17cd9b1702b4f6bce659fc70dde9c63978aac49902ab8c18dc483d6da31ace9c12636f2d173c202c50e4d488ee4b8b8af90f8e9a826895f5e63dd0d85cc708cf80940667491fb9dbb106423c5ae2ece55c871d2e42b37c2f0da7018fc93dc69714cdb24c3ea6677f55332868f2fcdc7408d51d949164b1bcf1f55f71aefc1dfffc6420a064081823a25adb3f7b3e6edbf384e21f00931613f859f0e6860156f6d19c40bdfcc8e05ce960555a9491244c19f36c94e02f5f84b0e5d301e5a765aa8376aa85ccfb6da13e611b40335d88fcd2d567aa1", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e940000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000041390e9098d147b0784030810969efcf00000000000000000000000000000000c017075b91d1c9c5aadc2f4d3b29027511521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + "proof_hex": "0x000000000000000000000000000000000000000000000005427a2ed994ffc252000000000000000000000000000000000000000000000006d241db063da6026f0000000000000000000000000000000000000000000000043626bb84a690ed04000000000000000000000000000000000000000000000000000193b944fc656600000000000000000000000000000000000000000000000afab2a128f843ba2800000000000000000000000000000000000000000000000ffdd135f92e6eeb130000000000000000000000000000000000000000000000033876e4ff9051c64800000000000000000000000000000000000000000000000000022e934d408c6d00000000000000000000000000000000000000000000000d2aacac256fe3719700000000000000000000000000000000000000000000000a2b7205fe21b014c600000000000000000000000000000000000000000000000b16c840b7ebe5c0ec000000000000000000000000000000000000000000000000000160d7a729e6eb000000000000000000000000000000000000000000000004f7bd1f8c59cfcd0a00000000000000000000000000000000000000000000000c6910e92c5cf1346400000000000000000000000000000000000000000000000ce9d40d2c6e2c24a5000000000000000000000000000000000000000000000000000194c4bf6f99841c44750b98e8b12c09eb7105c405d362756f8b9f4429e44ea7c476067abed031301689e3b57f19c1daba697f74bbedf39016ca9ce34cc521368d45dfb22109a11b835b6e94ac453a3ce5898868a4cab42d8d35a417e9e3e86d00eae06dc3661d2fb487229e2fed8db71130092935e55d256a75107fa96e6a3a23ea8cc2e8336429ee252bcac0ad7229c8f25829d8c533b22df0c5e5b036013f05f795502acd8c08ebcb8f199adfff8d624b85f06f4babeac2e9c483addcd9c1d37470eab5079615d833c8c924c55f6dc49bab1af3749cb6d50a775c7a902a0fa98373fc9a3b232601603ba65283002be13c5c3f80a74675d2f98988b112a4d232f0fadc06654800c72ddd92d4f108f0a548bba472c75610544d4d0d22a3ccd05e914663e93ec72c348e6b39f9ef1df2f0b05814f5a86a80a796bc3980268972986c0db0ec76ca028774356b570c119159a3b6f4bec272a8ac2b65e3661483419b1ec82bee728f1e83b88ffdb8c2bb668a4963de3f92604a0beaf90d2940eac097c6c5b1ac8a0e187679b30d947880bc050d437d70d6a9e52caeff087dac0a627fa343b48702de2323ea003f71de4b143244e858beb7032422ec4364222fbf540117b69ed27baf0210980aa90048a07e991238d24501107ff46264444e1c1a8e3fb6cf85dd12d70d25e7fed1ea3cc18e8987d7df2ee1378d55b79aefcfecc16d9a3bdbca3d4c2503ae270dc55614c28019386899d2b1473c7ae4eb1fa98cf8646d224f5ea13cb22527c61737f9958b027b06fc66f27a797ea8c10d3002dbd12864e769d8051555195d4c402d26641b7c5d862396c5a6618d1e54462b620f45f116a524a5d130331b49e391d5adbf8ddfabecf18dafd472c03caca3e7489467b8f826ef9bc7b16a2b7de443763d311542d0f7ba75c4ac61c381aa883a56153dac0233b24c2b1b542ea08ab907ab674a9440074d582141a10963f2c3fbb8407e1b8218f1f6123df80838bbc699ed896d4b98b7ce92e6ace001189eef52436a0a7ed249e60142278827e4642ef254526283a7d17777ea4a89a5ecf102c3a81957fd56a4864dbf836a1fb504bd8e7e3916867f161e9b397905e7877f6d58f9e1a48a103b7096d5d79f03f301693b397e9169cf2e06bf9daebfb630ab2e8cbe7461797dd834306b712d1c4f5696801f0196f91ed965829065f4591b95902c7a034e211d26e41b1703550b2693c21a8b711573b4ff021892366ad8968a2104c9485c11e17f8cab541abb01b6fea8d27f6e153c10765b27ca4120b6eeac0045bb8d9d4e8a3cb105601c442305eec51c2887e768d4d51ffc323225deeb1b540e4d0e6b12c82424bc7644a9179487f9375e9ef9a421f7a709413ad73725381ff8676636116843dfe97c01a71e4c2af2c1fcc6a3835296d729383f9d1b5dd21656f44256d4a8355187bb79e824f8c064c34d9c653b30673b3f5a2ad6365ede55baa021eaead52c7c417f46ea1420f848dec5ee8e2e4496dcb32445beda9d72b0e9dc505ac5471cba4826b7212ffd8668e33524493f12cad4c336f9425e27e07af458f680aa7556d693d2429b23516e5a2e20f6ab4f59fed1e3033f0b712a76bf4d235c54ede97fe9a387caba10a43e1509f0e6c163f67dd09015a90ff48ee7543424f51229b33de99d428a3004ec53f1ff45a533b356cd647d2a7f6339da8f8a4f6e0c097ee7fe6ee7fa62a606eed939e91067552413b42233ac95f4ad56ca26d7247199d2b53abbbecd602f2df89683d1b224587ea81d8ca66defea2c003aa77c467cb0d51543a04e401bf410126537a3e3feef528a4f94de279b833418f7215d57c1cb2c47fdc166d9ee24170d246422ba15c5d96f5a7c34c81d23f7a555acf5f67229eb9e94c4f1694db2134a751fa6280f3cd09e75a041662a8cc0b266315e25a354a58b8541ac7189421b4a57c8f1daee946f60d115ae4430dac1bc818c4f9ec244b60cc6e659835e6a0b99b702cefacc3b601622faff354765deee0c9e0f62aab394ac7310238e7096298cd07fa6af7ba58089902eb54381c6da51b2b225fba65ae99a1c1d724e9e9001a6809bab48ee95c1baee7ae05f02204d143994e977100e0450fb9eb073dafc26d1adf32f6f143dce3846a965acf99f8921d68c5553c893ace6d30b7f42e34a07fa85456da9930a7ebb651e2e8720b7a1e452dac68c32f9729bbe8ed10d66b20494a03bdc16679631aa96a19a495f0445b5e2341c843207e24b29453364742f0f4c48913f038624283aad35cfea5961543f74258d9547ccf86df266330284f6211282a6d25eb8a2edbd5bb21a1fc8dc5f807f78d8c0cd831b1e8ef8afcf80fd1bf9d7779d73958e3cbf0164cb6eb9512ced3961dbd8e5600ad7db18adc5b2a206c1902f40e0c388e7b08ea147a9028094e402b8fb5bc2c6d030609ffa3cf97c208fb4414d508f49e22ae7baf57479677627d470e20807302ead225aec2f3e24014327ed1cb3ef9662b9dfe2cf4fa39ea77b63d10cf93b6de3cb38925c8f027f171595a5b16a1300ef2a4848dec9867e0d36f27a6797d046eba20b60fdd214622084b25b40c55414595d095b4d3d8be36242911213c161e33b2c24d8d9482e8f227c7d47327840dd14a790d202f5b1b4d7876ebffbd5c8b59bc1c5e72d1562ef24befc8f005177e891a2e17a029ac9415338824b678ab58e44eeb753713f8b251611cf41d1bb18471d26aeeb7701ea36aa93d2a102224cc1052588f394c88d290e929cba7d61959b04d5b033d54766aee7185d338b31678b3fedc741a666b23d27c74ff7b2593eac40a9945a60e73c7cf7975d440d239b00c7cad347d824513f1c8fbddb5f754c292178eca5548bee9d306b6fee243801bdbcafa9d8453d3a48109d15ca04cac8ab7b3c93562b158b4bab0cc051eca421ab5c8fc6d4a60a58501a38feb0a4732c38abd876e0b5baf947dfb0f3ca987b736ca8330e4d42c1e05426d3b08ea5091826e4a32c5c4ef1e2b6a9e80a5b4762e887959e79cca1e94cc00c0336da0f387a4fe57fe353552ba8ede9e31f5b85c3658e5c43ce251e2ddbf50e166c29985ee8f3613c648146814b09dde9e9cd78f2a81ea3f73408280a5cbb160197a8b62af1814b64d002058947d07591550cfd4bad5169df7b8779d5a2a5242bc8ec5e8a35ece688fc7c6ad09509eb4350eff0e98aecc9ed266aefcaf6ef0075f5b3bc59150d2992162620938acb03376fb7aa0ce3e2283beedcc0db26942c77d59950f3e76c86a79a13c07e45161cf812d1d393b734d62dc4c215a39edd00c16adfce0a0ca7efb9dff0002d60aadc0c6e4b86ed6814853857f4f7bde5a50a37e4862b3de19096cd6b48b6d609dfc6583362d3fcaad9a79a8238c75d2a6402c0ba6956325bbf412d787000b9bfd8b0db6e6ff78e9ecb7f38845ba6818a1904f9b2405aa88bb415a0b51e300fb260e55bec6f1d916acc63148eddf10666a41fee9f325e36e4d3b1789ad36d266056cc53b36576ba294a1b4b6f904a3b80cf2e6fd2b7d3e95625ef16d9d8eee6dd9661c916bb2277baccbbfc182c9c74c70e0c68efba43cd58dbdad21bc9ed58ef09ed0b18022da877828574c9221779f70e04c9f0fcf05d526b9bf79729b673f32796a0f1eecc0a508e91b925086f337ef115fe2d0c1fa9e9691d622b086685986060ed2b79d0c0a6c54a299cb32c17c7c3165055ebdb2d8271325bfef3add0482b3497130d398804d69e66f889b4bac75c0e14534a4b5b12ac8dc61ce4165cf84241c203655292bc6a7c6c54f1641e765c2b27f416297deb2474a50b7d28a1ea3d805fc577fce06e286160663d75e63d8125ea040c1382b541d5d52777c5beb5fa5b366ebecfafad3e36f5b2963644c17113be9ee83fb92303a34b5163ac1709d698c952a0113c99259706eb0b0cb57aca238fda315a015caa19b67a67b2e9df8ce8694f7796511e8392a2df820bac8b2c2193e4ad15a489fdd141eebfd24e917835fd8fd958aac6eae3299acf418ee39a1cc2183980e4c09b31b4f975e587e473c09c065f5051fe7f31f1b361973bd87719f484814aed4ad2e448b3bc99671ccc1af77900e747dc0e90b6e93594de697a2cfcaac6c33ecf598072c0ac193cad8dfb027fcce0fa7c3ea275ede47e683dde0db3c4ae111bcc1ab458db20bff8c117d02239a6c1c6ec4d21a0af673ae1a42a0044a0159c1e23e463bd6b60704fff68443fee73d6214c714aaa793ccc339e7b06ca9127af4c41ac7312a1d2fd7b30099fc072d91b7456936b4e974cc99c5bcd143fe8395aa5dd14b8be8948265e711bd9ad6cae938273f44cd9a0b840650b462d5503a9e2070daab4e933e1de683a011201dde145987e277dcae8114d49056700e602421111c15fbed4728c3180666697e330351fbbf2395331ad4f72ce09dc21548ecdd678ceb9f2ba17bfc607eaa32f4838ac14c2ef52a0b18da26db7d52b1c5f3ec56fe48e45a0ddd8c12bd719e8ea4031d577a75b2064500d819de9f1af2d361f1382269adb6e42c5a0d07dcd2128e04acca36489d23e55e414067c606107e59f635e1d99a543c25faea760060cf93fb1558aea51ec8a1c4aa1ca86b34f08d7d71a093f5675c68c9d3d1ca68aba3975a315ad7d67914bdfb3e26eea736205a89305449ac7c3bdae9e169e7c50f962aaeea83d1c1aced16c84933cbafe5615ba2d0ff96a87f4a2d97f3778a1c19e12e0ee350f19bf37ac514c76c40a08da23161861f90aab73b9979b4d0aaf65a3c4bf61c066f67b5fddcfaaabb8293514193ab141d32a18e5ea09cb0864bc3e59ff46e85c9ec33684b54ca11b0711e7482505e86f2ed799ecd477ea8dc74c6dbb6349959cfa6133aa298bf97a16aabd2a1f1e93d03538ec2adf140f50ca5384ac1f51e9f0b91eb651af8566dacf83af7226be48e368ef21d810e272f1fd4abbea3ea7c0429032aec9d08cf31ef0445e2111480087e31fc4eee991df405ff8ef658acad605b93399f19d1d5251539e3c2a21682d0d46c9c290bf4025885c8b829a2ea48895c9de693b3d2f6215a164a33d2f91db9f73cf3343b53df247340a1dc2069eadea19cda684e22757864add16770049442b7451bc67fbf4d12ba4ef3b7fbf1034264151012cd735ec2135ca3aa8288bfbfb1fdd9dfe27d71d70335b9adb8a6211c9f6d954513f8581779ea50fcb271bf6e3d84927583aa787c969b8d4b7ea0e231de07b45b5625d0bce77b6fad327ced1eee216fb07103e815fa058dad6c9d633e1f828b83e38326c7535cba62b138eda58a22bf1ddf2447c536ffba078bae7208ed320e6f135e5c5cc8c69627d15bc7c974ed7bbd33ea0b38bc55dd005091f2e898aa0c9b355296c30dadf99972c6bc70999b9c727c1fc77ab5309d28ad835bac6ac74be8f8344c7309b1e9c31175f922b6a8c45b7ca7b23408d302925d94684700cefd959bc20d0bed14482bd0acebaed6ce3274d682f8189b88856c62616a84742619f43ac61e497d81ad02021a1116915fd972410f0f607cdf9b76ab6077239c7ec8d88127b02c1f99204df063d6fdce454305b7b6636918275793027def492ebced6e0f8c948a81cca82822efb3bfd155fc8b11c87ef4155977f9908b28913cba20e3ee547cfdbac774be30caa4409aba47a47898bfd62d126668b276cd7130e95bff2cfaf9b1d5f67566f18dd55e21a22ddd14363c0498ad2e29f9aaad8da7aaf2364f01455a8c66d51e21523a86aa8bb228ce37b3ab8ed758979805dea717810e201a83ea886658d2c192f062751fa9ad00f0c3abcf402901185f98b50b5830b479c5b03d2a7769d86dc29d7bbf1f14e3fdd96aea564df38a58a9d7aa7b4b950d4ff9b2baeb7fd12063b006f43d735123875aaaf324dee5942e7708afeeb616323a7824d01104ba950b01d2ed69d9ad865b23ac688494c461747ed3838abe69809d68c00d871a022e621121c45c397d5979a64469373ba2043c21d986ec9ff69d9a86e32e38005da9edb2c5536a42b6bea1f4a3104ad6d6f55926d7334d6111f664e66c1e1334dc0a3be19a0778e139674813524c28e93384f0a0307dd2e2d2906a34d513898c715b4900e816426eefb96196c5a24242463fa9eef879a84ddffda7408a10e2902c4049605086f7544cc27fafb47d23935c796b5a1c46a06665550d819875c4aed25f0e50ef7341e1fd6eec58cb9237d2a62bf3b686f4cf2210abe367284c7a91516fe6d29b0e24f81cf79efa422e642037734f33388ac21292cd636b3246b96f6c751a720bc206b63f51a1195d32b64932d61305bde740cad4a138649cc9f7e2b2f8edf2841d3b2a2a2469e0b59b584167915eef7da32455deb4f92268cc496a07be60f18bfed970eecba7e9d43415155e844ed87864ae0682050fff6343741b463be402367528d2a6631371c5f22995dd9878859be81b9dbb2cdff59b776f5ea6c6ba22bca5677b5ae9a4aa25c1c9fc3fbab0c9a40317bf22fb8627fb4fc7e858a3e2c11a231622b389f74934afb33b26403aa579863f0977e230c7df65088dc5ff97a1cf772260cc16803416ba1d76edda186979cf5a9725842585d7c77b2cd4fb9f61ce9c6ff056e8f8762fc2f355d6136ffcd49627ebb4e58cd711c56843b5cb4ed1e072d092a7931a57ebcad4e57e14df027ed837b8b3b0903b1996fc6a475faa11621dc49c44e03e9a9f0bba70b42a96d88258eaba4e175ff50c6903e9f30203c283a925a4ec8c62f69aa72d80be89e515fa5e2adf17db2deee86dc00622e1ef113db1b3c33a9f435db2e3081de6bf777aab27c72d645f4c8e73802dd112b9f0e0e7b95ed1a03cb6e0e20f121127f574b123b9dbd2f4744d8644deff40a3dfc132740bc41fd107465ebe01cf00e3786dfc16c78e10ac7f8bd29c0334b570f88b61ee6fa5a292a8b801a8a05d813b85a4eeb3361c6cb386bc50400af38bf92123028adf840f8368e8bf6ab9cc310541dd627c914094e2089037a5bda512edd26a50b4f6dfbe4d6e19290da4a183f24197658c8d3fc02f402119a98e09dd42f0ddf1b72fb646b22deddd2aecc3c747d1d7c96569a1df62f750dc0d5bb8d0fc62c481468e9310d37822c963f1b9fe1f6b8b2efa4610b304e75e4601e7dbafdacb5321f5479c51b931401e10016d89c54724fdaf6c9988d459af9aef1d0fb0d04debd03e3ec9228fc16a48055b55f810a72bf30bb0ee1ebb6abed769a5445cee071be226716851281d8c0e03bd5edd44b7cf510d58b29afd4e77fe5518816821697d929fb08d77798c06a1f0c77b9af14abbf79ee2c307fdad2d0530f71c98fb626710adb5f48de96d1e4f458d5f38607d927bd6c0d5e1cd68dea421d66160fd89c7f1adbaa32b1144f80bc89de0523293f2fd10208b780e7a7a33ef5ddb5f2a328a310ce90537ba9fd0a1c20278e249843ad0185cd639081f8cf9777e73ba2ff4d752ad5c9b89624cf8a16e00127527f0dc88e8a2f30416b0a6f1c68c3ee1aaab4510718c1fec2a1ec3d3ed44704b8eab63afa6b7578d2b23cc024f7e786de234e75299bbabd2698ccfe09dc0dad1a8da8bcf1ff56d70486014281749a253cb2fa7c137c1fb8940817085d81cfe10712b6f1078841a069d11bb8c9ae8e1b7c96203f2de4b8f460aaa29ce8f214867c80cf5ee01dcf3fffbb0a4aba5ea357b43f778714a0e6e5a88402cdd15661caa96c1eff838c9d110fa41adaba2f16909424660f0be037c920a9e0896b96e4741378d9ada7f661dda4cc59dcbe0baf905f9aac1419fae7dbaa67c5b716cbc798e91633cd644ab51f6e392f0bbb9bd6dfc9d821820c275da0cef71d86108839a62dc17ce62fd013148037bb8f9eaff1f0df7cf7530c8347b646518a1a7e26ee77d072bb8164efd58fc67089fd81ef778fa25fe7a51668183d73affec12f2693f517416737be621f39d2a984f9868edf2e95cb9352221569c1a4cd8f00c168304962564b834276d5947a965bf3e3d778e9e998166e0027d26e139a8227737bf2dce3d4e184aa193e1debfa354dfa66de175e154938151827ed49d4a988020c96575b60bea8ac4542e3bb50c0c57a0eee83fd16f7d32e672c90fef162349138a32a8bcae4a2669819e95b99bda0004035c4c334b51904ac5a9ffe9c5ab955c17488597794288943a09aedc771cb2f740dfa925f3e38053bd23ebb807e754a9ab811da53f0c68d20ecb5d3d331ea411368f8ffe2ab381aee0421ef6029ac9ac06635b28f168e3d1cd9bc601d267df1668540eb03c2dc0398867cb6806e214e25ff07128cd65f75b2323f3cc3f6eb8a7e94a9cb1108a017379a285b003cb790b2d4dff7a83e3d28eaf53dce39914ac03fbb07901048351dcfa0a14f4913c280050aef6c8e25c029d4fc33831cc24ac8e8ae8c493ea1b315d9775d74f63bceb9115c181bd86adb585afcafa2d58dc63f1888e9136d386c18fedee4e37578c6abf32b93ccc064c3d0c55b887f9ece1a5f61cc1baa4828250139ddbdb992639a3905c04a310c10cd849f4ac8c3e08f8e512020d2bf23e6121f13db7c7a5f65c7a649082c61cd76f89f5b1ba5b1e572292211f7158f6ecf0c0b236fd68f1aeb4edc78171301f5e357294017927d40b67718633ab3f811829226d0de241de2be29895744767634378213c4862b2fdda53169beb443e8ef4fe703a649cfbf629bf554bc9d08b385743a33b028490276378715d982178988ec3f2694c5511979e058d6c86375a7b0d21e83d7042f8db4f17944a69ebe1c6ed16119f897fa5c9dc86ef23d663dd1be79c6fbdf4b46cea563fc13dca98646e3188520574ec994ddb3cd019cbd75e32400bc771893ad2500aed3757ede87fc38f67322c1e5bf85d8d9ad22da0eb8848d24379062dcfe171eec7106efbcbcf443b469115dc9485e989c5c0b6a9d702223c31c6b21a4ff831956237f5acd75453b75de185fa18fe9e5d87c0fe2539a78471e4bc7c9b78f4e41c860034271bdc511e9340e6d4571ba8ac6c2dfa6955ece1cb4a2dd96936d0fc8edfef05b5de1c3fef37826d0deb099f334f4719c62afcf06014047bb6e10b2b9336de07e0e066af47d231a2d56d393ec4903d2fc4c825c8bcbd3a522f9bb481bc98da6b3e96579fe3ea418273226404c9d86da57c891ea33bb0e82de738c4448469c991208eaa8e6356a2ffe1e79cf6f65af2f4222c67d87c6b443042c68d96c5ce069d61f4adfcc64ff08a70b8796c0ee517cab57ce2e162f9cba588799be9da90cf7fd1bb45aeb8fd309d4a162c74ecff399cfcbcd49d9afdbd724f1bc54329bbb990d83d389517d3d0c75ee82c1c9c75f7a17741ab6b188b4f38dfa6588b420bbdf98126ddd1ecd3425ce7a7553791aea59e971433bb351687e9185aedeb781652bab5438a9a8cc4202fbd9974c864aab5e69e064b01f44e1bb6673df3625825dbace20d18c3ee78f2d86bb769cd098c86e06cced60e53ecb10ca0e13ab5167753ab91a914084f7661e6a65ea09e5453e01ab423d42df91f603a6e47a1494566e96673eca55c5084909f7bcc007abcb20f422158ffca594e2cc602627cc88358af3311e11d23cc01c0e11ad4f3c25d939bbcec101c4dd2abbb48a37d928f365470f236a8d04c8cca82cac8332f3bfe006ded5bcefc66d761ffee35d3dd45ca488bf9a79a50eac2cde238e7d32bf059ae8e207b92dee1f3070615903aaa66afbab7d23f29de47c98c92ea084e8b097cfa9e12f356c94f4659126c1cc30a0cd6f52d470bccb9409d0800b46a37632a11516d15c56d7ea870bd9315cb81f6f902fdd0f7455a18af14830300ae1171718a4908d16538cb5c929c10b5a727b6ab0217d1b46221a76caac7f270aa5cf851cd7525d2049b214996a569486eb2ad01e890577c09a053fa67ee005e680498e30fd0e439f746a1d29bf91acad7ab03924042c527c19f68bd2ccd4053aa5773a58afcc7bb36109f17dc2e6fb1f4294896e7a9b402c4f466c667c382b2bb4df3fcab103f8df94a63cb710528c76c9e1379df5f296d75c3786c260c22b20846ebe0c3619f895f3ba0a1b7bc477d9b4bf450d9a7b00b26bb6e70d5da72f0457710db196a06ddd3b8b62e303c08f8a585ef70359b86d465f1f8bb73c3a1453b2daeb92287fd42102f7b5ca5a560ecd106e6b408fc797ebe163f467deca04044bee8ffb8ea417ad3a7b8bc359c67d841f6692db1ed79c5cc25b02fbe5c811049dd1ed0b5c352924cc3e8b2664222db798b7c6ef923e3adecf989ac62da42e473e269d9a7e2ddda23885573aa476d1afda16c29257d76fff097954052a85224cb8ab3616616ea1d52f321bad55930577e6ab58121149bb36063657f971ce0b83fbd09fb46d34aab513832fb7d81595136826e0de9599cc98657f1d6359e40c81d682d1e20b22d514470247fec7077b1fd7b3ca5911460955f64cafcd8d4a216d2b05bb88f67e6f156ebeed80b25bf9f125270b1365fefff4115098c5f65b0d829f8cebb57a7c7805002abdb074d207969974ef7812823e5ddb938674ff801c4c9bd4b72d9ecedc4b702eca90b62c905312783803e17ec510729d5d38567c0bcfd30a7b2bd45188bece4463dee5444ed24fe4adb2867371e565e1e3aab628107ac1e12d020be22c848a8977118ccb2d92977372dc6a0f8978106acdaea6e0292ccd626a2b1eb06d169e505a6341c6422e52996eea6de39408db47c67984951182df115ccbb65be8b0301291ac4636d6b98d7c3035798b2ff513b53eb82d921f51cd3e97d6505b90ce990ba4a8d916514621c24ce6eb09a26e98074dfbcf0d101b640d3bcd25aa75d8ada4e20e0c0f2d17ee3a33357cfde8a0b8b8379c182809af3ab78bbe6a8b39ac620a3d4ead2f251e7b9333a63e76a21c7e9066b5276d2e721ddb3c69086779960a78b08cc0519bec15b1f8d7ef2a926725d64b9506c7103a2eab75c13fda0a345e32ca698fb8102614e8ebf680c9f9960855fc0429782504ba67ece853767898b85700b7d1189a27147c6587b2b86eae6ec6da78f133245dc01d446f00bda83401f3d195feb523784eeb6bf0027fea97c94cad1ebf602e70b69ccb05ef3f394b022da4c0c4ed5028d7a7f343f9182e15a987e0ec1c50040cf0d57aad81a2157b45cd0ec5d2dd8051f456b9115e8530f3a9bfaef8870f1bfb114cc5a6f7d7806ce6e2e3786860cb10c75af4a4a0aad6393f34a3827cab13e3015f656c040f86d0e3ba53d855399e6ed9ef007638844fa76e303f6d2f6a0d5b89bf97f7451df6ce4f9e4fe6d349dccca1c4e2ca2f48a77c26a6f592bb48032ba8271657f1a3ad8b826c428125c38d1fe7d141a63e3db8ebc52732eeac300f12d7c6ea95217873f3ac2b99e61278ef379fd78337854d81f8bc895536173a14d4e18eb945dfa4f676efffab03565d899a7b1ba4851d92fd47736ef9418cf20ca268683fa15eb94f399f72b5636db0e5d3b7d4b6a59df4be6d0d75e76914ee13a60b249096345edc3ad5177de2efb8995e918e139ef99e29f23ae4b7bce10802c1cdf46d78c354977a380d0fa181edd7d51563ecb010db5df96263d1ddf3bf268b3eccb7f8d9eb3083c8e1bb21e7cbfbb06346fe2290d97ddd852e37ab4bb001eff9c43143c8288f88cad33bf291abe41167d311972febf2c7ad57590a758e157de6987ba42a59f96c5266fedd56085d6272920d0ffe46e64341dc47b19dc223e96bc01786d7753ab35afecc5c719f29b08c93f1f98438801f0b296a8b362628ca14b9f24007d51941ec228ece5b04f88ee6955f3554c1e0d936826f51534c150a500f9966f975fa8197fd781e0f93b3a5e792c0847f89cc963aafdbb0c16524db2b339cb153fb23db491993979f23ddeaed0c826c75f07221fccf46d6dda51ed9cd3d43e52cf4a59b96751b05e17fe19548485ae19e3fe23cf2f82d5b983d021b37cd76bdbd878bc8cd49b41cfd4e6c2ebb9070693373b64489dad9f9e6162b9928fce70ea5670ee265aab85fa86ee46897a09dcf76e6709c5ef1603274cc2ab37d949d26e56a0adfa75b6d2aad41c04e42aeeaaedeb2986f1a6cc878f447142967bb077b3ea9964792b1039a815c65db489e3225d4755f038bbd411c7dde1c4e6d270d649f842f7d8c78b3d003adda353cf06f25349d5222a6d0bcaedc5707bf54b2b666cf489a7638216e665e71734a16b023a4858320b0d96908285a5217c636140c22faacd97964a4dfc0c4fa41a54a2bb0d2425c10d7481ab27aaabf2e4cd612c34e777a6b2f5ebabf1f5d818d863a60a49733d24cd8c6b72aa805111f4206a3fb56955b441691771e4d29b7928079b01f07627d34c1beb5d23ff47b03498b3bbc2c94cdb18e4f2104f17f22bf2df403d2d347641a00c521017f9a091107ce97ab1939826d8edc70b104242ca2e2dbf5be43d8250026af929be9a3a92ddf88e2978dbac1b91090215454a1930379686ce96094b198e60c8eb8503ae71bb9c966874f964f97ae919c73254d318538874ef4702705de9428f981f139e904d374daff608b58e8a5f1026ab3e9a35379f7d6cd25281b0d9abe41e9f282240538ffe266a4dcb85002fd4d40e90d28b876d48f1baf813451d59ab4eedc91161ca555aa3f878db7814e35ef7a7e4706d21e0320564f6143a86aa7106041724a2aa71fb51c91ae4959815b2cbe3dc7c71cd0bb03433c6823137e30db9a620a800f1f78f876e37e85ec1c4fdf63c9c4a4a40ab94a607cc6c14a7d170b02e604af0d4cb08d305c09d6bb29e33cb97702eea06b87c89ae44ac7598448b274c52f3d049ec959106eec78613838a837e97d7f494869b6e7c086402619f747726ebba2148d92d292b8e20d765c381a83e4eb794a9af15c7e9d4ede347e284cbdb8178d190f3948b3c9b05b4702ea8c5ef529ca26d7b840baa7dd780567bcdb7a7c61dd2658d4f94bd234a1e09f7995c40a0e593c60f0851c991e502e6de2ff483a8e1f2ab0eaff54012fc4eb1f4524d2033ead54f5a315a2d9020acdf6731ede50305d04f8ca370fc0ed91324ba43e8aaa1d2e098ef20c67bf68ac69cae3de5de993631f578b274bb8a9daffc9a181500548f4c8e16547801f672dd3a6ed780ec370eb27c17e9676566917b0b24f93ec005391c4f338bdf8758576cc3b3c6b241447a0039ba76ac1c9eb52effe3d4e70d1617d1875c07a44b25b2d80da267b3740ad970c47e1fec9b5fccdac40cace2beefce18826a23eb4d07b1a009db25b2dea6ffb2cc470e2419f2a4aff07a198c843f52cc57bee5df7e0e8464fa9a29991bd12a82f97f8c2e8f0f5873298313e53f17cee2ce90f3a2550048950457ffe145ac6881f13a6ff6a1e3b5a1dcb59ff1d47ec6ced8ddc6ab5162a7dc676f84ef5c941612c2f67b984fd81ca3ef6b93a27d756c0f4ff1940fb0d7c7b826986dc522b03f92f8b25340ce0daceb2d9c4983373cf53cd3b043ecf5bf73b077493baea47271019cdb0e7c28dd135685ef319d75dfa25836928b6a71957bc9ba46f556bf0c4bc2cac526ec3fed7ba2b11d0836923284d9cf1a609015ffbe4ad1c89a6c7b8dfcd1ba78d832d3022fada7ffbbe6ef7da3f392be43ba377eb0c3feab9ad7db607ea0001034bcdd15b5c1a8adaca877dc27cbfe0d8ca99ebf58b7b89eccd991e29a51cf85dcafdd2007bf477a35fc6490b78283111cf8bf3e151fbc322564aee8bca195f622ed44463ad949c61f8a93a84a929def6441975065cf2eb887b4836273320a4962d42109b44362597d44dd3b82ef0af798471c1ffff7477395c90ab26ce189563c13de09b25cb3f81479a9c2e1002556a5bad221159f45ac01a9b84e288299799819099c129b9a742fabeaddbc2189b11ba3b82fbb8938f91a6172961ae08496b69a2bc73af9e02c15ebd3b7ab7db1454538d62fa63441868e20534a8c71009f1122d822c5db1c104df011122519afe8d55dd35d6c252223c0a40f07ae9194f5f635230c06a259eee27b05b23f82dbbced798e7e3f0e6a8470428ea3bd216e8e2969fb632e3a2a0ee5bfbff6c2b82bce702db04a5f0a4df8de7f1707a772fbddc0d35dbab0332e92ae3d6349d637173d90a226e2b145eb355ee8360d1e52223cba36140d531408c6fbd49bd4154572eba6b5b4cd18177807eea3c5970c01121f6d014c65d2a73098dccfd5d920876e0e99b19b273aefa48044caf127e75163e939f3e4eaafc3bf8c2524061ed5ea365e24a2250816f27ae08755eb307da0bbcc3c0983d953f919b522041fefee59cd9d96b853cf6b8767a5573d0c4d6ad305cba385b6d235e0d8fdea59ec6a6948338ac431535414aa2919f75dd70d0f21064ada32aabacec88cb1a92a99fc1c6c7f2ff808757bdb56a95ae11b44456aa055273083e3fa43d816390c962185dfc50c16faf010b74e39f011533cea6b3ce0a0b15992fcff180e4909661d502b9e7d4a735e9101b708510b01f834cc08245275b28a79dc54f19189b8756decbb519dd4d2b7dec71868dea469705f8d6be061c72ff0fb451e6d860f8d694b46ba87d209128e1c4f7e32722af1679e7baa8370f999db4c627b535f5b2d953573eae7b97814e87e3ebfb0bcec82c18e28b2f5d18bfe49ea065a686dd34e63cbc364235da3676ce4226cf9cb05ad91d477f309a26f4477dd2e778c4b50f1f725590e7f75fe9e46cfd1a139f0a9f130eb07809362b8ae3c1fabbf3f657831fa0a0cfac6f3405b9380712d4ad885344a9c69b9b02", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000e0b1a7da3e047845b1d8ff775f052add00000000000000000000000000000000ba6cc6067d9aebd22aa647499868d26111521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" }, "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000edce069b20cdd3b3c0000000000000000000000000000000000000000000000035587edaec19bc9d0000000000000000000000000000000000000000000000004e8b61a1848c5d71d0000000000000000000000000000000000000000000000000000ec07f134570a00000000000000000000000000000000000000000000000285b9d961e7514ba30000000000000000000000000000000000000000000000037ced53ee4c6f829d000000000000000000000000000000000000000000000003b4542de3f9235c48000000000000000000000000000000000000000000000000000295d724d1e6c40000000000000000000000000000000000000000000000024984d76621f77c0e000000000000000000000000000000000000000000000008d9170675a02d3baf00000000000000000000000000000000000000000000000a55ee4aa4908eca680000000000000000000000000000000000000000000000000000178c72a0427f00000000000000000000000000000000000000000000000c780a0719338d9b0c00000000000000000000000000000000000000000000000287dc8993e1ea3e03000000000000000000000000000000000000000000000009b918c594689408490000000000000000000000000000000000000000000000000000e6a0f127f38b1c0664c1e064d80eb94bbb91785f1d24ed1d7be9507613a3cf0d4f6bf2e9d7231d1ec3f000b4ae52b58619010189eef29373ab9262aa91aa7143502034d8863011d3224f2e2edd4d00c173d14a0563b3055120bd423e5ba160b9b85989830c122c3d4e983a824d1a6d29966c7111df5c9199915567cd4aba98c44fd0ae834f9a277a09cbb2913101f449f75e3676615906ce01379d63002576ee7da737262b32236564d38f5e7a803c1f8d9ba4618283f2321568857b28f7c1f0a958192e02761c28d6c7d667f72a60c1b47882ac5bd519c2f272339121ba07a2660621dfe03922117962f26bcb850f7c4d6c290c1394ef955cb7d8158b965a8f73ebe55715b60a5ad26fc88ba140f26b746000167b877d731647f2bad22f5e22aa8662107b3903ba9d871d08782e78385c33c1e9322705d793ce23c873db9d99711f08a2fd4419bbd315d1e4fcdf4ecbaa1f63ced3d1a9e6d8224596b8db1f5bb494a2016bde28d00c42c42b88f68c6ad1f771011f95bc6cde5ad55f886bf7594359a1faeec71a2546cb3ae645c2ceba21aaac656c9691eff73811ab3f7599e839f35ecacf0d13122d59cddefd6b314e97ec4518cd154f343b7a3e4a803e21aadfa71512f8500e046fc1a29c86ac30ba80b92d6c88a8ff016611d89caf17b20af9ea60288bc707548bd820b583f244b94937314f51020158fbce2e9118933f203594ad959eed07b4b35a6ec429eaf7613eafa54fb3dcbec9ee7bedf0b142fa863ff3ab979f532a51f208c9b1df2dbc7b483ed886bfa98e9725ea544775c7bb9d667028f4b19627f5ca4f842d8e717dd35f718dc7aaf4ff57825a57ce340181d5bd8e6d2147ff2b1b1daccdc7efe44863337dafc46a94c0c29b71c850f8af09d96d588b92035d04470ad5f8011691979683753d0aba181757afe9aadd77cd8044e703fa50efdc03a037ec4c811132711ffa1d32f74ab4c4d105fce937a1e674aeed5311039f6c1b82ac81638d61248c4974ac1a4204cbefe6558773523e38183701596fc70db51e4ef18838b97f26696532758504247d6204e1bb16d1d4a6f7690e22b92ec22b1e7216c99504649f548ea5e76058166e6ab62d8b597da944b3f251e0a7f5dd550cd43a77afcf6fdc66df86ae1f2a0bcbf217be0e001d5db52d8b348ee751b0191305dc8b39e1a34a8f2b255f13e90c5f4175cb2e3863169b1e6b74eddd072b9f21a8bc5ba5c3f55267fc4c73f55b05016a0db089eda8e476039e4b47da5f9dfe2b79a3acb1133d7b29f45ff225d0c4272bdb5904c6558a7731288c110a549d491d428dfbb0b13b94fe735c89379880bbee36f28f8069de31e430ff99288c66fe0c006bb22bf7b4a9592cd03dffe27a9ed159b0659b109004e5178e4bdef83b282a5e323d882ab264f227bd6470279199a135bd78b51de8c6f36652f1ace3c0990bab78767cbb925396a35508bdbb408df3194860a19d252babb16e91402e2ed12eba5dff99e35eba65c89111f2f8ecb8ab001a0c4d0025d5d2c275efa03b181202fc7f806b44988c8fe287a384ea1afd59ac50a70e7d0d47d94be1897ec5e9e203ac5d9e3f5f1f7cf379e828e01f76efdcccb0614b23beddfa5f6f8cbe09e9e41c6bd67b5e867148d005eb0eb83a20c69edae66cb10f0399f049cc4dfc577fe92f3160e22735ba35b84465fffccdec15f911306df24c11ee96201e4b04d2e1490d1ea90f45ed5669b39f607652462fd9f10ff45449ba0a3d69318d075e365393000429a8ffe54041f667d458a24a950cd629a1f25c88e500b915ef8b8191ae931e7bf88306e191275053149520e06fc4c5ec65452d0f231b2a840a1c2e47c4d6274c2fb4eb7d2ce13cdd3eef4bbaaf730576192979cf71566a194c11723e7cf52a8f8c03407906136dfb54c7618450df427bfe58533534b6f607df93d4e525510fb61f91c34085310910d69d63416b1144cad84e2db53f079311f0c7d2cc880222cd9094f071155155d56e58f28055a5403eada3ea64bd00a15222216f54a58a11d88c720803f206e5ae51935e5afd64b155c4fdb32ad6acf0efc7a2fff6c6dc29aec8db1d185dec27709075fd7366dddb28a9909f20b637e709d5ac48b066d2056728aa6ea0b695a094e8b077280be353b709c208c531be2174262ee74a6ddd16cd375703f431f50f42d497ebfaafb2204fbe31c1db00f847c205a9e5bc54db1f42362fb4a9b97d732b7b2a543c772b785fb139c372ebd00127ff15437a653f0397d45a567e5c6000a4b9ac2812b07511de3a692e5b94e5118dc68dd31ee21507c2f4c9c52f9c54b45ebd3c2918942e1498162908e85bd8cea61ee40e823bc801a3a24b6721333c061e10ebcfd63353a8651b95377d721138d170246bf515650ded72aa95d8673e5c8e1440ad005d31d9f75f78a8402b38522bd14a172eb6171201a44177bd3a939cece9958e1b278c01d40beb9d5b46360c4f07a43b49852420a12fde2f03060a123ad784f488d0b4872e94d10feba6c2fd3959e9a9b1b56210cf7b3b52aa00a90692846a05675ac063d0e933c1bf8f8bb8a99cf90967576308d1f7402a634a8031346f58838220918ebf2a36611413ba04bbee53f23595c2011fec50e061c919f78a127db735b3c640ae6c8f1ea70d4b8216595f35da58041939c77c2d3b9526ddb4971f0b4442675f2e645daaa4cbc3441c424463efa5d42a6350ca221811927c5c08451ea4a05499a29d69b219f2c993d451a437f43d8516e679a796ae6143e42a26668b048b4ce5c0debd5fc4eb4e934d2e6ba7b7b5b31084748288df95597c2f838cd23851420c57562eece196a9cf56f605cd2cc5802ebfbc1e5f4d970648ca521f5dc09194cb6b31cd11e5dc65a808aae5a72b0b8d06f3ed69825af86f15664eaf3d8cf9b9f44edbc0cffde603a1649a04381d008c1586681d08744417cf896484566b7c9f40d9197cc46472eea063bd1763135fc824fd8ff1e9cd4bd25ac764edfa4127ae79f3b4b3236c7e149b0409e4244575bb064648188294df57511c5e320a52988c7a98e71de98924c63cc4df72c10f76770770552f6688be1849aa0a91cca874e842ea9a0d080d3d6eadad68206fbe2ada0d07de35b059ad5e3f3944dcb6a650ee64e230a53f4f04a4e9ec9e3330f9312e182d90545db4f65973f636cbe00905c00d1cf4c173052b4f1deaad0f09239cc80e6477cb573bb32c25ddce44a1b5997eef3b1489a520c3e714f2bd84b33964b020a23f0a9ca584020b90dc27764852b6b9fa3551aab838b649b78767cbf40a1b1b928595c0ff6d5b9a2c8dfbd94eeab852563fe2554b29e55c8966aab39ba2cf11c5e2a750468bd5b34956ea7e85d226cf5f99f6c7d0fec89924bedc43b020120e17513393289e3cf9a0f0a86447b80b25275c4e543758bdfe998290522ae8421a90eba5171c4ce624b6466846d2ae316de1ff93039df1cbfaea396d150477dd040cb85d01066dd0015f3151bf7e2ef022c1be954af6bf1032312afcac83ccab302363d2e381c358a1bddf79f73bbe462b5df71897535eb55e73bfd38ca44f510357a687f0b64fc814ab4f3a84c2a71894b89bd2089f27aa805757662a9c5006200d757d3341fdae52baa65b943829ecf02888b4401bf065989bdd8a2cb71ad31796f5df7bb522beccab8a547cfe2cdbdce2fc7413233b72ced05ce662f88af9123ac93aae51fd8c1e8025da7bc2345b3b110b5e59d2ae2ffc729d0550db5fc60329666ce8fb0d6356568f1ef2618ac7f7e4bf2a6e3ae35b7588780b29e198d52b298c4ed6a93551745f03889c5752475272e91f87e836ea54caa93cddbcfc8613cd6384d28dd49ec99b2232309b798fa694072d9772542aab79584eb091633d202572c6c995c7268e871d4f8840d366099e7c124c6f0f37a91b629079c7835500d621755dbf91a3c126649d7ccaa9c979b09aad02b315869a542dfb4b7366831c2e3955c00d7fbe2c15682d53ac21ce310fea224a7c8d972cad6c4b1415d0a91278e6b67b2a051654d60eacacfe4f32e698bcf8bd7ac192bb696d9e13994133116a66f3ab150806fb12287e271bd614aeb9048dac568c1cff2c1fbe79ade43f133b89887f6f2c8ec436d5b37eea62acd2eaf5508a77662fa8bc2bbf5c39dcb225f3bd14c21670a9294633249d98d602eae71803a11c9bc9277e1f3ae18b2f3919f778be7ca5b0d88337df43757f52b0a273d554fab9f5649c3c9e3deb5f0ed82e597504378f93584af394922da247f0631ec17835c50217ae6f476c04220a57054cdd47a98bacfc724f2ab5d7c7825ce80f5978e502f847663637b90af2d961188d67c326e5be62c40cad44d5d128f5b18fec898bb36bc426c93f982f6d470d17bdb5f79943b681d033607bdbe16be4da1a1dd8fe1e61ac11a1f2b4cea2a3fe26d2e66df0f886c561d05372a059845d520c1f729b37c5ca4093b912fd806d9e15d647dee4fa61a39a465c97ce7cb5a3793b99986163121f8545744c3ad753f82ffe37a90ced4f23815bda2c9278462546ca5ae118e198c4097d3e7dc28e03be11f8ba469496e5bd7d9b6bf4a8b5d56b480e205e03e82cce8b21f464a4dbb6d52374e84c1acdfb69fa9e531159ff2f6f261fab2e661214c69bb7d93f5f0101ec12e85651e59e0436e3af84b244b29464d5237e0de9f96b68da0e0e756b013792218cae5ba282e49b29b371bb74fb66dd8019240639ca2424c3e739ca210a056506c0631528a20cb89f1c2a8ecc7dea173b6ded9a7ccfc9abe627af2d7ea6591a151c1673a09d84f1132062ddbee11993003c72dd6f4de585c418319fd64fb87e18f994b3c90618cd566283a36193487235f5e20b3eab9b15e3e842246b3df0720ff304ddad5e0d9b443938d38b7c6c8ce9341d9ae8071016a21fc694f170002d163f7f12d068f82cc36e51ff8eac359d86fea4f76ce490557e9c8d628cd2ec7311d270d3b58ff7ba7d52f5d7d3c8903dba14149a80ab7c9ed09988a5ea0b82620fc56bd2241ec60e84caa696384f1094b28877aa06e1061600b4e581ab5bfa85176412243451efbb1dae13d6e78968d3476151211492d8d6bea0a32a28eb70c814fcec1e1a77683784680cccec1bc494e96aae146a4e663a8dee882caf2895d80be4fde7baa3a49d24c53c0bc5f54b6367c26837215974a6cfa2ee738edf84b21115cf41b00618506749df9221f329a01dea694776bf3abaa9fbb352f9454ba01fe2a5c04286be6419fdd0afb3d051ad92a46178d89410fd158b32d3625afacd0232156648d27127995b308f527364b3162e0eac7db4e1c9d7222b263d2093071643a985a9493381f9224fe080f7c49e0c1f178b1cf486bdf027b5677f67194f17990a030cd95227782c3ce2d6184ad12c5988823233924a13ec460a43db62a518a59ccd5f1069fc98131ca373c6b618fa7792591edb22dd0c242b5447019f57229b7191aabb525535024a14782f1ff968b3a1e8be97503ae7cefb6aa8fd3a352f3db9f3b9a9e6ce148345848e3988651f5472abfe980dbb8743ac95f00720131054d873d7ef9d60eb7bdd6af577d252f486808d1e8449abc1d68b69f4eb56811a8ff281cdaac4ef304f4582285d5e403c5367a43eb7027b36be9351bfec94cb0b607e652ba04957a4c0fc0482cbaffaf8932b90d94796e989278b0d100b92f826b213ac5b4b3b8f3028b80f591d0274f9a65fb57e385ecf47e33ecafcc031ae2e8c65f2a0175b4ec3b0ebdd832f955e11178b2591b7cb64f3cef1263b56b4bb2a77690f4fada83c47188bbb06ad5c506a2bbe442b763efc4fe3ea2fbc21e7e12fd3c530be1d11cd7e2272464c70d36f6a87717d45c02039d089cec0e1e812bc0f0f466212d8f19f9582712cc3d7f4de6c659abd9ec8682cf25bd9c827349d1f2f9b83f6082eb0d1f5f99f966f8411cce43872f5299bdab07880b3cfa693c9da160c1460f44d22314988c3a6dbe5127978d96a9dc6cdb3e523ff3a7dc68192cc133676d1aeecfb4209c6cf678cf1dade494eb15819b2171f776abba11bb5ec951a46ef47e4bfa4cec4e833fcac0a9fcbf9ce8fa3dbba049ee45162512a1d8eff00670e517adc74f7cd0627f765323e0115bf5d66b6302c28b66300112c7809a720ea9b409f976ebf64fc48d0654945028900fed3563e17258bb0ed64d410660a2819a435f89469d02c009c5d5c810725651ad230e22f758768ebad58dd9559b527cbeb59f0d6c6bcfe965596dea8e090fdbd70e9f18d5d6199cad88165f329d612b8518edc1bc88f0431bb6999172a8802c4ed1c417dd26aca97ec6358938353292e3ede19a0fd0e699fc08f072829424760b71457eadb10eb25e52987144f92080e4c20439d7bf3fc26bd9f95d7c87dc30e6f6f5ec033a751f2006d050c75961da6b26abbed3ba81398b3a374371dfa96a1b6ed1af5b9794c5789d0b9a912181e090050f3ef76dbd74eb1b18101e748671915502be94c168d34a44382caaf3816c14e6456dd88c14d8e68e2a0d3624e29cc00d6d0086d6fcac00011208602d21276b44c40d08f7edb5f2dedbd3e7a955ee1f92f0806854cad1ff3bf05cf91a3103b631f8c0563591c5d1e70e97e16b1c7d05b505ab1643e49146500cb77eee01d79a14852e3fa0beafda3aad1e41941c3e2c9dc4c4630f1218656d719506dad0a2a35a616a505d4ce99fe5e476f59413c41b02fc17c1c6dd0e2a06fd16c203e0d43613f60be6b6ef8389331dd4fedfc337d72277d459c4f6a5f6f5923716f2a1c23488142dc51a76fe1cadb7a3a1989ad7e79d1e63b75e8952f85ececd4851f22098299a53a0608b5eefe88e783328a37526cf50b972c25f782a2e4b8c0832c302ce119c943a5659209e9395d43a1d411fda6b050c14e7e1ddcfc24df068e781c194e17b295ba95cfd03df3c17d0ee6a0e36cc8f432b6c339d1d81bd80b621804194513ee0d0de54e0c0d2eb0bc3900cdbaaa7f49e6dd212d386a15507ef3f8200f7c2a499aaba3d59ebee961ebb27ef96f5c1d8b11b46d6fab6162a3f22f8315578c760b7d247b7c7aca1925eb17442fbd0010842c7faebee5e0d3df7cbd6901dd2c01f4ef6e0234a6b5ee31e14e71a44f652f51db365417e717efd7cf0ba920e728f72b1246ba4a66757bc3d6986178a01c5818dd0a1a4fc7ecb03465903b103629d6df9333ca804018e7d7422aad2a87673bd59d3b53e429b9abb82559921620f508d103296b6915ce66e75611f67f8500111ed1ad5139744aa1f4828c3d07f2bfe7999bd6a3eb38147a887de506421fedac4bdc00ec8138f985fb7d034a01300c4b88c4adc6eff96c0c86ae338b902caf73936434123b14e06525d0ccd12ab7cf13b2e53f7590b841b8fe4aae1252fd75c40e2069390e5ffbb7ab3674a028d85230eff2bf8759207cc777ebb790986beb140f46d20e9bc7f4ba078dd1cc05d91b86bb252b812317d54453271888c960dbea5ca6d90195f0dfbe8e9c5d9203fb3c294d6b473b0a16ff79ffdb3ec24daf6d59dbef2fca6013dc937fd103572c818726e3561594626eaa1f45db3f64a8964104a9272ab81af3a1c6bb478adc1020f11aa1bd01ee31f58f4ebb848520510dfbc05408cc2787e367ef0c31e41328e0accdb8de5dc22799b8805f173931694094f7c0e5de52dcd41d815d5b6c7c2daa0496def1b376e3a51d49025d9f4041715a2b593d7423d0248819a54246e5275cffd211d8ef63b85f2b0958c61c32d58abbd65ef70ec68ce84be78310d6ef264bb4a81ad1622c4fb0d54040f90d90786174ed1e353935ee3f169a98aa2d58029207afc41769c02bd3deca0c5403a93efac655b6a56ac4d4838dd6a08b1e1a1410888fe7e934796fbcc11d77c3b0c2edb80437a769787966939c9cd55a364f3061778bcbe570cdf79ee300718671ba9baad1c30888faafd754749d691e71801b9821a796991690747083227e61d17ef7a39b511cd5b007568a1758a9395fd016df8a16e4e56ec6379bf190011628638fc021a696e3bd9603d9f3fd7e1e2c571a4b5945b7cf7dd86b8981ef6b97b7bafc2b22a55bd6368091f03a645ef0df97279acee5fa55dee3ab0191d1633027c14609a74332b0eadd3d6f3ed85a7956680f2f1c5a7c9445d8390821c05fcc9aadabee41ade1fa32e9421313381aefc7b3170feae96e81634e042eedc9d30d9e1d523a550d682760ddc428c98fef51b1e221048fc6822f1c3e89a26903106cb0cafaa69eeefdc7ef3305925ffeceac96e121206290ce9d91c8ff16641033910e84b4bd4bc1ee56db43b62ba86dfded8ca113d00114389bbca8cae5cef3c14f725ad380e7d712f7d93a1e588647cfdbdc5402c7d4a698eb18c2bdfede827d90ffacdea8cc298a5629b28514bf731b7998f82e19b16c332526bc1b3713c5b41b16489bb6228f176ee6f96cc286665fc5db930036af42600c67bbf26009805dcbf054487b9b0e1c2cd7173f38be7459892ec90652b26f72abad8dfd40edde3511e0898abf30e957b7e70b725b2653cec4234d180fead76b8ae6be8b7e18ed0f6a6922f146b264ed870aec50f2bbf7e4d178b81fc952955a05a27f760b0adfb14fd86c9e21c4b6ab0074e6df091e08653b89d324e1e066a0f2825afb9be08e90f11ab66a57602c43d45ab5f7d97ef60a8670a61fdb46b341552a18fad736bed3ae77890a408470e7c308696f37ad26289f8a7f00cb96b31fcc1d4b0cf0f058943e8249b5723f031c2a02d659c2a761fa0d05d002781fac13ceb35217f82bc90dcc84665530ef2ef7e326470ca0fae8f6771a0b181d7f987290f7946bad4dd103156d149d2f4d7fd9350246dd54b274f5a9c2ef256421db001f48a5a3b95deaa6967db143533599dda5154770528c1c4a01f2bc246ec9f2caa1539470292b344cf2ad33e67a96fdd2e7d07450df1ea9e5d36bd510b5a2b1d115be0f0700e1a5652fe4b304d99ed587e6ff6955cd02469a5fecf829b17341a8f46cb83222faacbd1f539b89a186ded5664e49c7deace0b5233b10275cdc7135f9be5e3c62a640ca27b22b92456b9e6f146bd6e805cb071d32b2dd00449a613510837c67588fa39fe0e49fbf31b99b5144c9476150973ac9f0a4591d17e76456b59e88170401c1d4a92b94f17ec904a2374dee427b7664a6b2c22e240e1ca86974fd9e5e172d8647abea27a002645a90db6bcbc5e88dff24f1743323be66dd4df719696f2bd75926dcda82d3accd3c865cf42853941ef41de95af32c261b646deadb7d82ddb53574514e261a14aa12cf773f483d2545dc4a45105e23de104c8ca2eaa8d94c42f4ec7c0e11279a62ebba987e6c329eb49f2037b8092352ce3c21318796a17ce472d8ffde018425fc0b4c11f3e492475d2afe0c9ae00b0cdb4f51df51cc833ddcaa3aa78b74df3d936893f0a5714b7810fabdb17c0810d19fa485bd97f1507a77fb95b2a0c5b1ce5830dea0de18ef5f6c20a00e15d12331f7d203d3a5c9d261cdc4db79b82c59a02ec3746ce141a29564fc67a7e6ad0035707ce5a02d5035190ce39d1ade935bce8d2db6bf855b748412c8f6b441480b1da708c7587d576017bfee5fd7339097c4e205a4f0deb9385a3a0f7806fbc92f5490de7ba18539c7d8c659406341579581b124dce903c88eb72a4f4a86eb22093addd77a4a8b8f2fe48a564d820eae33127cfb442fedb91ed8eae5381ba9f1046e54e4e5c6887713366918f0ef69869a1e1c1254bd5e205917d5d5f7571a2e1c9b575c9df2ea2c0f6738f4a4206330ac1320b7f16f1906c4b067a64294082e2d754c8fa48c6470cf77edb7e03bb86f4a396b7ae2dbc56fbef172ef0cc08caa219968432847a0b5b933f0498d92e427b6e0d75969eeea525ce41eac022e468c0514420c561aff6c8edf72164ace360ef5ffd7cc2ed742cb8cfbaf855a20491a228ee0455b1013c4b2788e4afe5f5c3cb56aeea9c0aba0c2a3a99939fddc306c2e0d4dac3598daf10471cc14ebc2233d710393ea6ef90252aa70e609081040e814d26daaac5656530fc3586cef2d6c4743200ad764b5e4849b3afb18e28c13512271c93f14df40b13bb0e4ba277e2d20080be06d4b74dab1c3b51ad544e763f125ebcaf349f694dd1f6b8c60c2794ee456f96176bff72351704e7620228cf9352b65dc2e4ce420d8ba84e849a41e2986f0bbafc592af6a2eac9a74d6646379bf22dda3078b0408cacf02cb1a4fb0d4effcacdef4e5aff14cad3d90b0b79cc149129a4bdb67b7ba6c4333b1167dc29e2c02ae1130f373e25a4e602dbe1f9b88532001917a73ed406cb1019c2fe78c0278c373405583d6b76103ce83ac88def2ae0a8be4e4d15523c70c2baae6d7a2d10818b8894c593f7dac6103a75be2c4f6531ed679400c7fa71c82c9b8856c0d03dff14d810a3863018124c30e248a61eb93227f470138981ff90eb35b5ccfb27b22f9c3e99c4b8470896d7618b570015aa826ec67f903e741cd58a559e1c5451579265ebc761ea6a358754df10ebd689c5218df613d6ac78aa2720071cac7fb34ad6ad194e3e654b8a344971ae9a0d9fd530f3fc109ed0f01a69f6ef0208f0edeba13cfca9d6fa2999f44beac2fdbfcdd312c3132400d2921f726b2eac70133a0dd787448687c557fc1e69083e8169747421a39b826c54c494f1ac98dec8be7e330044daf7a0bf4eef11c79644bcff8298e0dbb24bb3a782d13457901c8e2ee0609dc1247478b2001d0d6c9db518ac9068208fd83bfe7d3508abdfa2503267f7a45de285633535685b6da31d63d4e2ff0720154ea6904ca630ce1857e3e8a1464cb6b5b16766638074546226ab14fde7afe2c0e86924792862a3f8832e46311b61bf635a27a61b3cfdffe662e7b8341fb9514e51b6f5688113dc4f5f812945d7a8ccfad91ede7fe0a607dfae5741848c88f01827ab0ecad4343b879f24bf632dcf9ce5db1568b3dcb0e92a3ea5d32bea5bc06fafea5a53fcf97c3dbe274575ef305fbde8c7eb4bfbd8e48bb3b062bce895e258826ec523ced0e17c77f73323f26abfeac8c7d8e401107f7240757fd4490841615f11738971be20699fc6c8afe8c9cdc2a78df1dd876c7265f3824b959bb5212c9b389a43203fd0b16ca64615dd125232f77f77153f9983d1a3c54b847aff7022cb4a694c0f04531367c7cd278a7f7a5aad796dca8eb486791d297a593f69912bd5a038e02b40783d1345f2cb58f787712a1ab0316cc7bc64d21d6035363f51a529233a8e89a1dea82a455dd42bc3795828066ab8dcf048ee7351d55a9a7870a0dee695599b4a9bd9a6a5cf01e1dbf2d89ce252c15cbe5271c2c2815edaf9f05db450c973db3930f9a551e2af7210dd55bcdda11077913f597781cd3a16457088bd5d22510baf958ef5e5d75a44055207b030f5bea7a15e7d6263aa056e9db1369a3521bb7832d51831151d9344364cc9337c3560d387864b7382aad762ff81f0a1adb64a26323cbf1d2f2be86ee22c9ce9dbfa51860184340226563b1b8af04deb0a186abb9d3d399c0a8548582177aa8378309d08aadd8aafa187508e9bd146d89b17af7b31920d5e2f7ea2b5881a86e81376d927a6a75ef1ad8dac6140219e02d5d2df2b473fd67a74b14fde0df6c9e9bb9253edb6d885b748527f90b30265690d0032e77862cbe2c5157330f90e10db865899ba2004fb7aac420bc40540b027d6a2389de4a85895dae7e6438772f59708f6ab2c0a0e8949d890d15d5fb14cb09a333e1d04ba6263268e7f6bda2915967987f0d2e45df0eb9e601aae96312e39e1add5778ec0e13ea0feb65580f7d4b73942aa5fb43cef4260dfd71cd6c25ca74631d5f182895f93bd853d8fe8c13db6e6a3064f4984cc00f502049db6b2f3fc84e7f072beba9e1e0da592e03103c6884b2241da120a6ce8f0dec4a023e0bb68670913ad55bd31adca44ef3afdb24a74965e6a5a4f6423c682b3f2505f119b8b942abb373d83f159674fbd223b4082c6a943ed5f6a3c0c30b4cba04c56d296e61d11f994ec45b3eb7c0fadce0892b09588510deeb84ad0fb43af65800ce1cf2b42a1f77afa03429b0e724ee01aeed3c0f0da7c26e9e0841f6f3e6995a6c26394ee8c1cf6f966c69582a31ecde78b71a2cb6980da3b820348502c53b6e762025993aab0dda298ac2c722b379d1769978b065166b59c953e243260294dc7f0811a8e2eead9e087b065819882752fca41693873712be6b9592ae57f30124af16a149d2e3e18080acb56723895e4b6b960a2e565c13bdbd7f4a56947457ace609911477284348841790e22ffb6407be7a6a3cc3cfa687abb560e83b8f95b12f11ee57180949ab894c6f63194f9795bb301e12258bda6bd19eef208895d3646407fc4b759cd026c4d8859b45b2db5951a4567b74ac30cb25763fbe11ab5cefab02121a2d8d2f4e68fa8da95a1d857eb9c915a24e6d65cacfdfea17b4f7c20b3a2a449be2dad4aa3acae6e681796e2e1c80d8e2be9ba6f33c83eb5efd04de8fca143d6424e42e247559e0d90173a80aa319d3698c58d33eeb2bd7224c03fe44de1bc549874bc66035e4ee132138100bb60a755a543c6598a5f06e0ef5d37d66891ab1b930794b5c2f560004d2356bb0b6d1e1bee7760b5f7d6e676b61896e91e72ca9789023470f3e234285e254c88fe67302f0c605e59670a402401135af417519c6454310e55f64af70feea1e255cee278895ad44da1a078bbf0f676dd705f605ff24647ad14d866c1b6d0670d72dbff0f44d02b049695345c6105d280c45791a7d544c5ef1225e1fb2bc196bb9aee9c3d7f0ae59c4346f6ef9e2042ce4906712a0284ceacf4e137c3e49b7305912c45069a5009e94d0de009b8412b1c101bd13792df3d482832d5622d9765b0829e4ba254614a09747aa80f7f11cd11f63c20711b449ae52188e09085afc42d7d3217f74f29686c3a5e4936e31ef89fb578f2655924c25b7b1a31ad2f2019eefc28890279d30b8fc665ee19dd8356f9eef3f0f754a9b70b8dbec4e7285dfc7f0ff474334c6bbdd8cd3c28f964fe49a17f0da160d18b7defd79fae2b4a2a0d713034f58e89606b1c905c31d316d878cda2f331f4f17fd9ace21980261985cdf4b609d527278aa16ab00b84f910d4193f280190563d7ed91b44e80458bad71e43f4338205ded6dc8fe68809fcad4460e3d1ddc2d2a4417c4dd117adb272f1fd4b5077d3f1b108fdb6648403ed5af2132302a2b2825988faee937f12c17e377767a51a562014dc54f7043c27df06d554e61611821961b9bec98d5c2570c9f6fe8487dbecc40137e68fa352623d570be4791c99f0800ba8d3b538ff0c41b13d298ba9924a3f180d85bcf37e477edf58d311f11af03fca8f7e3bbce16b2162db1ba146b4e895f914de18f120676a9471bc0cdcddf06d65a86b6b351545c1b659a08df6f9e19f66c3e0ac434fa776037a4e5ee4b3811b508a56849c1eae0857281bf4632fb10c571f2dc118a695db6f242ce9fd044295545f486838cb226f250ebf115a28f9f3e6be7b7c89876e31069d9ea516f0815289ee7e0afd2f36c1b2ab344cb6bef65eb037d2e4d2f16cf5504daf24290c00bd40ee9122929b83e4254115a940b02985fef66afdf26bfefdccddfbe2f25650fc238758bac9ed132a290886daac1dfc82dc410475fb48d8cf51ab1b19d73d829b0cfc486ebfb3a89eb25393460833342fcd2092edffaf42d364b7d80fd766a1270ac3e8f5c706c97f8d90728a2839a8f2bfa3bc81939a540515ee8b6f4275c1be4ec2588f003e9474418d88830187c2ce192fd7c69c1907d0e73826c3f86390c0af6fbc19e6a0db23b87a09ecd4de039db42fcb862c3315cf4580d5345d32606bb0085509bca0d4a5eaa1f3b0a34432db80b51b372b3cc68a16d16edd004a70796d5b65c46f72f16383ac710b0105a0a9f4b1efec577d5c4af544e9cddbf942062e58be832605b62a9c89c818605ec5eb258ebe45ce66d99e1df8ea696d1ea07f1787d1346652c451651f25e1134e6cc24bd4557df3930d2db2550f98653001ed3a64ac328546bf26e19e7dfd77c354788d256fbd110b9c266dc66aea7e2ae0b7cbf43d883fb25c91d1b778e277812b660b5fbcff53511027ba6d54afbd88d1adb25a5c52d05da1438ce5b88f7f61917547932ebc310994832fc7588df225b1c393e22f16abb8017e1118e20736e7844a3fad04dea0fce59cb6d2f477904af2cd57babe35dc13c3964fb7b49be5b34f676fbcc1123ca7063f42fcbd9d2f9f31dd9dd09efc55e5dd647b395c73e84834380708e332436b329f726a0a7ed75830f110176b0e1d9d3f313e2d89a5d1e0e053fbf010e3e20fdf030799fc73c51cd2772ad06cfd94569f5b8a998cba9dde2b6b4e50ac35f7970e38b814e063c368407349b0d618635aca08298e56598879e02db373cd6ed499931c2c617346fd15a23d8a1a020f6b71cefd7744a8b42921d02253d8d84db053065ba311bf7f5af1401196a38b0999ec52fbe6ced3f7464ef2472f4bac6cfee49af2581c31e5034980eab6404e8aa8cadfa913a7492149b7bbb8dc6fe77616870c2090e32d7ddc7692d0fb3a73ab606111690b73c8959bc3af3755223e56d07e34c8e4d0b492b3bcb1b96798b13cc92b1638a3f885d83e9f30b14cfdae34a307c233c578190d65aad0f421ecd053dea61996f8adf5906cbb7dd5e7a87169580a1ce5328d3d0937b28", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000335fc711e25eec4bc7002806d9b00c9600000000000000000000000000000000cdab8c25e7ffe0274955ed025ec557bd01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db70000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x00000000000000000000000000000000000000000000000312a8f853a46ad8950000000000000000000000000000000000000000000000053fdd2acddf75b42f00000000000000000000000000000000000000000000000a88f17af3c3c0039d00000000000000000000000000000000000000000000000000026b4e21c49c4800000000000000000000000000000000000000000000000571c6bbb50a7e605c000000000000000000000000000000000000000000000003b06b8a62b6ff439f00000000000000000000000000000000000000000000000a406e0f7ace42494b00000000000000000000000000000000000000000000000000015bb9eb3fb55e0000000000000000000000000000000000000000000000035c33191f6a014d4e00000000000000000000000000000000000000000000000a5917facff53230a5000000000000000000000000000000000000000000000002d448fff5591c30080000000000000000000000000000000000000000000000000001ffd7a66840c000000000000000000000000000000000000000000000000c66bd6a7b1539fd3c00000000000000000000000000000000000000000000000dd4af2f894c7e385300000000000000000000000000000000000000000000000e26358010edd2eaf000000000000000000000000000000000000000000000000000022a7fac5859ca1e90cd04e5ffcbf6c71ec61cc2ae9aab902a0fb4fa1773218ef831a0189c6da40a2c1e5e6ce201cae653ddc10d41aafa0588a10422bfafb8b8066a06b93407a61b227938b91228349304ad087e3d68c3d7a714d940dd4997a26526f1c918c6b815ea5438f1eede873183871d8b8c952ab7f9f00796500b1755102cceb1e6bd382fc67f3e3aa8b0ec912d44ac31892b842bec6fbcf674e23ca2b9307969fbe4fa07cf015cf4dbca9e7846328f330620ad4ae74e79fc87a32c4131c9e7d26c8b7e28dbb5c90ed466e09507a61716a6e6bd59181b7f763247bea444384584c7e8ac16ed88d759c0a39196fbdc6aec044721b849e6d056c91763a0b314269a21900720e74a900444e7c42e1ce70f42152840178f5559f63332a1a1746b525d722ff708f0a1f1d0036607cd983ffac29224e1bfa05de2d02d104e326c69c2ab424513114e4656a4766d85168d36a5b5e79d4d90ef46bf807a0eb197624f9d8651c81f1b0b59a52f0be0ba78094fb9582fcb805272039845feb7e26cc9e9c087bd703b1ed8bea6a0bd7ca560425e6591091564fff151c04b13378c39e838dc213143312d1d9f68eae024b52f8a4f6d0dc5d76ed64cf4a729b167ce62e890e958d627d2040d277ac803e4a7f08e5c0d3701eea9aee8914f7f04ba961b821989101de7a1293b8a47c69fc120f3e3ab1cf75315573176f1d5b32bd0a09465750e907c44f52577abc81703d64918eb22fca332c2d603ca98e6a5d65a1642f1fca0daba682b17cf76d30f2edf93d920ddc87d762cf310860f5e9608ec4d95af5aa9baa1a21c05e6a870151085fee2176ac709101912ba9d278862321caf91711a3941718a0c158b14e0e8c865fa45e95d81853d4c83af3f0b4b78186bd7490d80a324458717004a2806da584dec525c3508a608d94756ec875a0e15f211442f66779e051eb42c18ad0290edf383ff8e26ed6a9b96c88392dd147ba40d10f2d0bb266afe142714d493ee3fe57858083b8f9577bfcef33e4ea30be6d15fdfbdfc7c2d8523e4281ab3ddbb039eedd28b26bafe5c301504bf8f88da4d31e16001015a1e8a936c522c1c16662644707a9281992245cc203feb75341d1ea27c107520bf897172392a2f3fdd1afc8380086e6e595e446ca7553bc180728e55e34883d6760a5c231be21275fe254ba19f443b55b8e3adc094b7621b3900ed90cd61e7d133962d376c5b1628ba6aa5871fc7516cc3d741a9e0049f5cc184d437698b9a927fd743c645040936659eb8c72d35c7f1cf495075859fad3d4f6759cf1140ed45b857c8cfb41d2f415d1a4d4f6889e087d2ab17210241ecf2cc412ae9df24b203671991221c3e2a789b7d451a6777017f127393022d91e84711de1de9238eca4563ff08b8cb0f102eb0a3fc969da0fa75118372fe3015d37562f8c81b78d6091eeb9e32dd12a3263867b6644f83e00fa935478d2c647af646e163dfb2625077015e26fcdbc6d92aee4c39efffe577684b7d4818bcf57b27b258181ff5d2848b4077596f08c1840c93ae83cdefe78b6d582c3e70f7e59ca0cf331cae4ca0affe6184faaf82175810f7d1c63432177c4663167eb7536ce9ab826ab2ead6603e14474b5ecf3272e2158e12d17e51a1dbb03a79ce50bf70217135909bcf3ab0b0afbaaf7000b28f220cc085c8d33dbc859feda8bc2b6c4d03e81da737cc4a2a9ebd1157fd0c1ac1441d59a883ac5c259fc58ead531c2aaa234f10705b143284571a1514758b320f320662c97914023d0a21937713b7647ee807f86f5485301664da7fbe46cec1c88508893afdc844fb0c17fca13df00d9b2a3fa45cf69f2a2998ec8ef69de37148d31f8f9897c860d4e8409a355cb2a3ac6be0f55425f63d36d06600131a725e2132251c31489f93761226e30128cbb603a5d2f17e79ed0526712d5d2374cb4e5c1829d39c2feb8523e91e59088be6659ab5fe024e3a0b306c71ba0b5703c679ffb60e1973ace159a6bd73e363677a031a72457a911e7658ce7e517e296c65447009085b6e26addfc88d54519d2a5ba252e9cd6acfa8a295f87b83852661e5f37df0010b99091191d06a3bc905e4a920844d8fd6a986c0597a0b5d5a0af207e5584202feac48d7bf02d573aea8e2b65ee62a6968306cf18c52debd8e0e593f524de409ebdd0bf4c65668ab0659ea1284e7de44bc8e90431f312ff585fd1b2268d75f1511db68a4ced686f244e270b39093fb6580d618f3480b39883eb06925b303131f818c6bbfe7e0451552b1cc718fe6bf25b8f65722c329edb45b0fc8f86005711ff86bc611a91891204f1c1352c04e467816eb3d637693bc6fa479e765cd489407f8870aac11f80c69313f7c704ca99a1b24fae889f75b295af8d6f6c0b25a51218d84f23a1cb256a464e3b3e5ac64efcb47336060f675f73ea28d5eb13516e61148dfce44e232e91a6b2618e1d4305148d4dd456948f69a3f1e843ce27e0ae52cdf83515af53b8acf3dbe95a8edf66280f9794c5249c97fbb40db6a4fa4d61a2d4e021844b6151ba105874ed159d40c0acfdd6976ccab55750aa969557f426a09c240388e16bbb4d639fde88676a158260e2b454120fff932f6b671803dd81f033f680bd5f9e1947fa409c9e1326e92baa11f275fff4bbff430cfc61c3bbd0e02d150af4dd072cf96f577bd2b682656b8ac75411275b2f8f34aa9d2cee70fd700ad5deec2a22d0b8ce8982f4243a2b4de625d84cad7efae743dc2bd761fcdba0acc6750c675c049239eee472efbf67e866e67062c7f3f5580a7bab50f57bb822dc5b8c730dbf443e0615d8fb7406ab1a86d2293963bfa6768a516a532e1f6b315d9bab1937267265bed04cd54aa50c02347a8c7b1b3eb58ad24d334c6aa1a0e03ab474b2f328742012f847fec43377d8f80c2f6a21413f06d34213e54bf889402c7310861009735660804b2b42281e40025b2582e39989b218e1fba5dee27d90017221db795dc096eb273c1d9e3fa208808a2b9dd8d7a15d187ee74ea0ad84124c8bb7652ad81d2c6cd7dc156626a083c41f88d34131d6a857afa04108b771620bcb7e37ed965da55b701b3e734dcbc8609d781cc677d677579bf243f2779e111a66140060c233ca997b9e9940179e4f87bdc39986551df89f94643416c537e005b1ce0bed6524a3a13ca4ca4c613b924bc9d3b505c9d34d424573ba935946e0a6307744a875f5e3162fccf9e28cc4c530a960a9c2007c22a2e7227311c60d92531346043323ff3d849c7500eb14b948a97889b1af6201c444a8e156cecc85423ad0d9afcb2b8f6e7eb4209138863d04bcfd8f97c60b19f6e4f5f53e0fd78d20723d4f93a179a27a59edc289413b041eff2b0e1f02a1597e82f9c40565c0e1b092ab9b0f829fd2325ddb0607bafa31efe7e9ca42cdadd1d8deeeaeb1d4ff56205eb74af7da2f65b19b911bb861444e7eab710725de0bcca573b2cce82aae44a239139af46302611b55922b21484e6f4cc06a502dba4d7f7c3e31bcc1fe5840007360c6afffc356c717bb47ac891fc0f9a730558fe8aed33c473a45bc06a02540c77e7faa173b495c4c5dc83c29244dbf2ef576be3c060a96060836e2e2d8e28149ea7e78744d764f9ecb044be417312f207ced8be362adfecbf9d849b08a6d229df377bbfc41bc2ce927fc040cdc6948598867008ea416bdad0b77ef4f377e324a903de05c1a8559ff3749b7ac5bab0286b1b433fda0a7139927dc230c42ea100bd4cf9297fda04df9edb9d3061bf5fc91a79f2dd1849e529b1a98537906e7514870096b08a38c5112b07a59ad46e7e4c6d8322871fdff22381a881f0d7d8702bbdf95515139f66da6c5a4863d7e7b53129dbb223cf15c5f4fe31231444287f0bf272ae60a20604a21c49d2a51fb25b17c2ff3a68873947da5155283d6c0a371882a3a4601c58cf931ad159a69478cb3f92c2368e58627d8fc63a09deae329c2179287dcafde7ed77f0f356292d0a335e7d9d68b3cdb3cea583450a59cfd5ec092e9ef8a77db63692373fb037ef8632770ed4200fedeaa6e0f558e67bea9c9f11c65d5ff25b5f9d4251d84235df2ce0bfcbe88ce980caa2a159ef77cc9573610d2844fd0ab00c04b007cff838ebfec214761d6fe8c636253b17766444d9558404192a2a8e0d7df5aabe258688b3ca8b68f732e4c88fdf5337085e6ea37f59ce0584bbd09f492cc20966aee2f2ce929fe5874b12be10a7403580a2015640c9261b626a3e4ba46a2a27cb5a7649060d912c78fa0fbf4c6f57c35bd5fe9c3ca035259794300b5905b33884dd53d58503c4060c5917031232ab44348dd27f95f62f137e10cb5deb44901fc562ca6f29a690be6f39aefce02c22b2476a8e82fcc77c0890a186a3ebf3e0ff3503d3c2e8efac08bb52a3273e7400cc5400e5f7385d9a1f0e83b573d1586614e188c35a870a0d244719bf366b2176a4584758d38f92660796338f177fedb649406f596d071d30752a4f7751bc697b7aaa20648fad04a2176ea776b6a600b025f9d9830fdf3044c7b6e180bc168e741e3a8c9a6447be061309a6b8ae0fea0ad4ed51050aadd9df16e66489b1c1a45af6ff26ec8f5a554d263c0ef7436f2855fa20a13aee5e6e9abcfd0f6103858242597a723af5ec6bdc26afb848244f0b6b88974a7649f577aef3c18042f7f0c0240ed06ae9ddd9689400b528d259f16326f18e4d9968140ceefe8e35f7df0f5c9e31eb9ef9afaa1e1b107619b8d745a7413674ab38b12563750428cd751869af8a284897ede1844181018e110afc61dd3ce0d4a418b6ecbc3cf6ce7d1d5814352c6c86fe76dfa9a9742753b0286a22ea0f19cd84c549c8551fc04222d5f5a43b0c8e8c37c97907626901577728de1e2337811177d672de4c15c066a16728012d8f6f08788889bbf65406813a0842c1a7ad0d137ee1f888a2bba9ae6425666e119b4ff8e433b53fd3e01739b691622cd0035ee5b9141be685421e2342c17ca231ab31a6a20ae900122b17fe0ea53cd3389bcc94cbfdf6a1a49f72878656bbba02601ac8f76dfa9dcb1612d7fd5615eddd4fe81a450f8cfc4931d3d310281e7728a29e52ad49e927e5c22da3a74794fbea01a961cdbcbb6659a7fce75e6807292ca007780e3e017db4b61029674552ca688b946813ad5b631b27bc4309c366afb2f2390cb15e75c8e16b0c63e908ac26ed4888ce8d886d9eab0696f6d410fc47935d928ae40ea63d22e62cafa727b163910e394ff602b00c243fd60ede4b995cd503d1571772c69a20680065933aa51f23d25a33a56a695816722a4bd53def5c282b4f4070b1e9ab62592204f9e6ad2fd26ddc59c40b6a96489d1d89123403760026fd8dc573f7cec14525e802219c74b509b723ac5bab87c59db3f667a74c26c384305717d09acddb52301d3a2ad4fb2511489387657f71397d01ce8766cbd176f00ba4c37dfc8b5b5d22e79b5e6e244ee65c74bf3b8e5d9aeff46754ffeecd2ef962e6676ad87fd2480e6b1034669635ee85baa10dddba443584fcfd2e9b1e3ce38f50efbdadbe4b402c43b42464b599c40447dfa0802e1938dc84be0a3a33d0199e6c05a170aa5ce11069db429bb24df34476dd3799df06892f1b6fcbb2032f277eb50c9a3353e87c08f7250b22c6902ce746e9984a02dafb3d18bfb8a1babf0b6ea4874c3eeb9cf101e7e74c66b25bb2416160aede9e01575b45cc0b6f14015da95c18b67e7166bc16050a2d65bc3ff5ddca6becb59c187e686d7753224bc49a47957073bfb6a673233a02790938b26caebcbfba480e68a67365a97442a5b91f68195f7c246a59ef0ef8827eaad232e92878a8b3bec861efb9d32ed06c1eb994e0f0235fbfd785711233bc36b20751654b0b8f95448b32cdec919a80c0902a7bca212473e9e60f120ebb89709ac4953d6f2a205e5fbf09ce8647bb80c87bf9dc7fd304a02e68c6962dd3bb9a1a8c7338f45f27c372a6b781ed4822c9ae4eaa8ef9d56606353a6f3e1250727f272ccc609ee9e39f892c33686d88dd15a3fa6c78ec0958c92b82fb90008dbb496864b3d1867086edb1095fdc75ece3a2e166f054385c4229c3b944e1223e3857a7356f8b7b9d06a424dcbc2e8f62be1adb52b563074ebb73cd5be13815dc4cbf1262ac6c6c20e6ed3f812bd26086771ce1a1d89e934f859f367c4e9f2d56b404295d9ea940c0e1f5d8f9eeb2796201c9e0ef8483e19cec66c6c2408809a577a9541da1aa89c5c3b4b993c75da82572772d9dca0a9371d59badc520020096b1191b962550cd065781ce770e42fba08605a22283ac6ab14860768724ee224b2c8b3ab53437401a68e9637da41f7f3c12b118e31a59b72f7d2927eac3a42c1dc5e65203d2bc6c994ea11781fa7430f33cf52719724f62e48aed905087630aaa2258bd137ffa9a3e87d31c94becaffae0aaf39ea4a1517e998a53c9a06f21b7925efc2dc5781a2cb2a77f7c11c95a97a53f86d67747b1cd77d4d5dc2cb5c27747242d4d4cf92f7b575cf3cc0268260f6e6f66680185def9753c8ea2195392b3a2622408a88ff2a0de00b1fe81781c78d2d056a9351eb83bccdfd96bf4b9407dbb0632093e34772464e5d32ff9b7dc7ef15d75648cf69db97f9a55f8dd55f0dc42d474b936c99a77cef8e7a9253c6ddb9289672e57067ff679946c072c49809edf255cc8f5549f47625c3174e5e372c4ba56ccd879380f021d961b2026c1e2c432a0685305ddd1777ef5dd550b5ac84458a3ab13ed8554036476ba780f4a6128064204b952b23440a199dc4a4eb1f357c095ac203887d7e009652884a299203ef023a8b5ef5f3f2f6567baa0f1a3fbc8b36606aa998312ae7fcde8524e6b22a8d3ee11e5f46e018f9f7137b53456c93836685ca81193df4e36106e9e67d722c9f44d2e95535b1ea7144fb65f3a49ccbde27127446f8b48a93ed9ee10946e40cab66821d8e80da372e8c3a05eea60394732d5967470a99e0cc46d8c8ef54b40ccabd27c19d503e94dd2a701d89a3c8351d27a529333bf4820b6644e3da820d19f3d2ea3e86d1f6dd5c3e593992d4e3f7cbf7047fcfccec97d934096cabdf84263e966f77925a48ebb22dad0814115128746ae8efb2ac7b2997a8ffe24738722ba6876b888d8afe65f2a1b0e3631d98302ebb057bd7074c29aabe65c2fa4fde0da2bbbffb2f8a4143ad1b86b5b286c3a280e2ee2a87f358701bdc55e39d33970db14da12d809bae278310f329050afc3ff0c052aeb73b834d49fcda26f3abb60bf906a239af734543e14b0be8d6a1c05ff5d39559c803f55110828e949d4fd92e2af021bafc61f5c33b89342540061200013bec78beac8236eb6bc99a74b5f50b3964ee581c75eef7a6f2cafd80746c4220d6b5691364f5e29f36ba4c0297f810b6f2cfaf7221fe473634d58840520c3273e3e28a530e86bb70f31c99d434a7169992fff226260357e8f8094e771ad760d21b8fcf9d6fad432d3878ecd1b94f2b988d692084652b6047d19c06ec2aecca3bcba90654bf075517904cda2a97c614cd87d68efe7eb87ca71fc1e36a848442e49b4788b22637621097fae2260a2b0644fcb47375d3809cd6acdb146c64294f11dc888d95621df31922a9cb9479e4046bb1aab8dd2624adce7d48693dbb8229468e0bd5ba459d06227d7a4b2765532aa3c4befdde90ba711e7273608b9fb123ad2185079b52803faf3b31d920d83e2f6dcbeb9a3d52064f5d9b98587be049736e1868a0ad6fa8772bc96e0817dce913bbde6b832b75ca1c56d3d250434bed8cd086358ee781be306a60a6c3dcf8521390e3e1018421da946837f4381f7dddbafd5e6e481e0b64d4d395fce061ee522c22d18cb73bd0a1a69fa20f3720ec5d997dec42a246a8e574f5fe4555b0c84c229d1ba4b5f1c0c872ae872d327b4976485639fd8f69d1a02486499927ef6b481dfec6c501da7b56f800a69811474e0b84b5c0ea56cdf03f39f7bd8171accfe707555406e942ea4c7e8b87324bdf56f4830663a2ff0a3f4c45f4dde1136f951511955651e1c01f7db16bb8db027e1a40835e229e979502af70258d4dcab5241c1469b0200a398d47d450b9c64fe233b104c6abdc71a560fde406114444a6d29305daaed26ef541e69304684372b43ffcfbceb08129f0196acc0a20243deaf97717d85e87f057c02fa3cc57961487a0a221294b8f2821c9345e41e28733821562009c8b05a14e56fd8016d3d47605f66fddaacea202cebbc96b913121a0a31ce62972d1c68a30cc1aa09ab64fad5a2f78d8182217bf437545cd14937d7f35cca122e0443de8f1ad8f249596e209dbd878d60973da2f881614f185d840f57abdf52f1d691a8f9abcc79d7f76de8dae8983819ce58d511822a45dea994b76e5c5510a53f53916a145d9a86be0df11b97befa4fa7b647a5b1b9db42d96b445aaf7460eecc2da6bf185d2d517bfaefef677638a9c490ffa6651d99296c483da911fa00e3b8f3dc658f30f9778f958c37d3aeb7dc10ad1d10eb52527192ace1b1eab9c1d0587c174e1515d3b7fdae56072482efea8c7dc1575305cb8cc550858425605305e77df03c5eefe5bf36b5abb550be19bbf77d58b2bc5d0e4db303b43cc46e41d41857fa0ce2efd647ab3ba3b4d199d193cf89162be86fa665741634ed9a443136cf002003fe661f3e9f03b3933567ee3ac7b505616c7e8390a38a841511e3b065a4ed68e2bb2cc5991c50e574c9abdfb85aadd7ef23d6e3441f8ad49daeb6b1a9d059a533e1540de4b0163834c6e3abdcab75285c80778a83899271cfda8e5265a47bdd549051e5d7dc730ef1f3d7ee9a7d1535302e46f507faf60feb333240985cb59a7a95e18ade5aa512e4893178d8bdf2296911a28f18e40ea610f163f2d7ad4d27e7ad1d2ac45fa97185e0433726c735c7066aeb2f00fa5d2b5a7bcc209b158cd748557487d5c2e84685772fc040f8549817551ebbfd4461e454176792f54ee2ff6bf4652b6ad757f97fdea70ad789a89d3b4fe9d04aa3efbbcbe352c00fbb22f8987a6f3cb11d1311cafa31eae092abba3d39bcca2d797ba4e8cfc512dd357d4a7b3b0f15d0a594e60d2ea5dfd345dfd14972f755fbe7f807ed258c40095bcfe709c045c9703f589bff8ed17a3312edd544da0bc056f1e2288bf48e4118732d39a335f4c2a1c0e928d022b2dda9847748721a3effd753439b1a30e022b79ab16626a9f64df29f8644357b88878bcf79d8d83f83eb91846de3bbaed1d1ca94b287cbca8715b778ee42eb328341bd00cfd6e2cbd6bc7783a0bf5ff7c30244dfd747be053160f6fea13b2dd74a9918c0a27496457881cb1e971465f389e0d8499501840f1b7af05db487119767fb4966c39dad2b6372fe30deed2b99743269ce2833c4f208d55c6be3e47ac5131b8e541d5c666291c42e23db62ad9d1cd00cffcd6f36073fc193b2e0bd9534d1e6f16704da01ecfbece0de21a169fb3ac290664474020e634ae8b181924be1c4d6ca0ed50cb84bbd7ffbd17219c1a5a7621ffa701891c3434376173a43e42262faa0d0cb2cb3fc28da95b1533466d8d2409e8273991e4882a8fee7523935ca297faf2b3af6cd199ec4e206f37636c02b41a3857782d2c895729ad03c17c64de1d22bdd94650caf95652179d78fe58ac3b20d6b4cefa8256d1efac992267a07797feb4b6c2f642379edb7a4874d8db030a2195cadb6296ad13bf87b7ef8e0767b18992a8b98e31eff1feced2b12e06180010cfe8b1ffbc81474b7fae7122ea2ebc3718c3964a000a1420e634ae48803fc825594e123f569aa1ad0a068c89fad8b68f09ec8927bb78315a559f8cf20e9af20674477edc1388991548393f944e2f8a1c2bbea9966b09bfa30ee99a7b60a0ff0901fd0144ddd92e7388590ea6263f002e6357e16122e778dd3b8c509807c7c82f595f4ee88c0e65bd878d2929cb4ce2f3393d539e9f56af72ad1bfb1638ade92195e493e338d401a6471a6003fdcc55d510b5d26f71d04a6ae0576cc149c8682643592d4f3255995648e5fff273cf5e45997a2e44f74dd2f8bde2558b15577828830c23c518702afe557268ed3f7b0c18299f30cc4c88b2dd0d2aef5da75a5b14ae5900a249b62081fb9948614650054c68362cfb36f9f0934e1ec98c891dbd28c8bc103356091a7a474d2bf9e9594d01cce31d7ed420458dc9625117695d6a2606183b5e46069c734491740a054ca0dcb4934b96d3070fc363837db1ff81e210d9c471b7191a8a98a4891d2aed19bdfda19c717ceb64b75fb6df837a948ed723eecdf33d23fe91b6fc3df5f78763ab1bcb718fe339098b101f2a54ebc146110f8d3a65e7909c9da41323266e19a9c13ce43d0e3be679d49daf7ae09f4861da266fee65b132d563c46376e6647c0a90668349769ee6a3b44c5afab705f9d2491598dcfb52d52820b3df3e58962e47f8ab928d4a72f0aa351874d682452af7181b679a81504ea8ae1fe71503c8a05614e90aff50a31c2e6511faf8b9af0002762557ada0aede367f43bb86033dde827b8a4e5c7fea436ed1fece280691b26d7006bc12cd8b4fde77aae8cad9c66ae6d465e3d9cfad484cc969b38d6d61766c8f14f4d66c51006eab750e753bb86fe74889086e64ad24d1bcf4402fc17f5ae46b2013d73c8323e73cf698baa650a8ae47bf5ad18125b6a7919155ebc9b9b34369246031c231bd06ce79610be9eed895e590873090a539cbe4be9a91afad701e5f24b587c8f7b1f06056e97ecb69efaa7f87a4fd53da40e3b6439eedb4306e75320bdfee5c1d4ac101d47a39b81517459820240d4a51e2de4f1727bf9a0eb6242322e390bb87e48a4d518190d833b620f27cc16a330a2baaf84cf2d7a489995a5603c29a2e61d5bc8a928a567d133c7290466082c27306ea65fe7b9feb7283de1404c8e3228ace8eb4448f1c7ef9f48b0d4bfef0d7ba036cdebaa6c771448dc7221fba16ddf9c822e9b171fbf29178c2e429b1b26634404083d54e92e6ee9d064e1883c9e25523d4ee8450ca12b431cd30e9f6fb87490ec3e57d138fd8084287ea2a83c1dd0f94e1bebd9a38a46e7b7e58e9e79f72591cda6b79df6755b331d45c11ad28aa7306ddadd12c035f488a2004be39afdf94dbf885b87ee64cd9dcbd511039b08d1dd14bd5bab8357f18afc9d04f90c28253f96626a8f93422c1fec165069f9588974db7ac5ad7cdc22c454ae09724a0fa1aedccf9d4291ff835ae1f6010bc0bcb34e53b22e175cc0059ed2872537084d5463bb2284aff2873a23c82662fe123885f1a767818e968dca8ea08f351c9525336c36eb4a2915698e5aa7978047ee2f13ec6b7e88499d6bedab91cc826c49448ace3e32dfe68620ec807476524745679f039703d8386396d3cd22acd7ffbd565355db2cf03cbd765181332c013f9e6b2776c9c5ae0a5036bf4e5367842fd78d47dcfcbc46711cf82a85ba7d713b96f3e63ec8d8833911702b24b7cad910deb6a94b916924a2ffca10e0e60c909b02130faef1c4537e2754ea02c57757a79469e757e82368dd8b48224ef94ed24bd8cb3c279a2fd8287e948e68a883566fe24e37a3ede38b577ddd7dc3fc77d00b7e03c04f7da8110b02aedbe796e29042942a2dc8204730919c8c704ee339e05279d7043a5ca60fce10c903ee2659a8cb333ed2bb78efce80d3e058d9a0b191fe5abd7ca56fd0b06888adfbdc297781cdd296674df014f260189d39431b9652b08978b7abfb19a3e1a2e3925eafcfd183a47ee5704f1262ca2b2bf1e8e30b514cc2a3a5dc1391ec6a9bf99dbe9b5a048f66ad5f00a9e1ca43dd8a470c386ba255624aec4376bbb7cd322d0aee7804ed9245e7cd2e6864e284b94b72dde2c942b10914f7b86ec2ce839d329b7fc2cccdb0f24c18574be20c4535b0b0c4a1fb823a43d15d97b3d008a7201e4d49ec6c06c7dd722a69347f3ed389798bb21941f17ddd2a1758d4555edb9bcef81bf6a325f318f657f91e8bebc9f29ccf8c9f1ad0020b779789996bb3f9558903fb3ae05ef542b9f32439024b773ebaa1efc97c91441d11c241c4e93c725c663462e7f2a942fdc50a3c29c371bdd1b13e1fb64870aa2e3cd29dea8116eb2a423729c8ace6a5d9c5e72ac419c065bd1feb927e08b15868cc4b06c7b62a97a2203de907b009231461e3bbae4ee4bb08af82f15afbb13475a97b53fa89d7006d69d317d0c39bfdabf9e4257d63e4b53e40ceb13a6f621a1ac55dc06d177b6fcedc6e7e32005711c498992dcd4bc1bdbf1a6d423db7526d069e2325fd1b868f61754dbb71af2e7697b48a0b38453c778fe6ec30529ff03799247e8ff813e8a34563861cfb6e6c054c30fd8fea3d10ee38414c99568820823039ce58ac0487b57359d83825ca1dbebc7b64396881b741a8d815934796e2ed5416e37b207542726b2de7e7bffee33ddb33f95a5e312f23eafae94f6349006551c8206f287153aa4bee37501378463eeeedf05fe60b2dee20b828275b4ac244c896ce38f93183bd127a6f43b90402dde1e105e34060a5aee5e3e7d97e34e2106a0ec6dd1ebf7052081db6955990d6f1706720f956367c173a17a436d480a2a9b62175fcb45071b23051147762c58c3f57cb2f14133a0da0fa66b77fb4e2e1c9cdc085d18e7d5b0476361be69fea7572f03ab25cd56201ec95afffa9d63b709c6d4d2fbc435aa7df2efd79be5fe8cbbb4db4605024dcbffd2b22d193c5e57000568d246af4467050a7c8de7868a492ceaecababc6f9b0a7f2b0fc91ea5efb2881c095e6cce89188d2e3f3f61bea3ab99ad360ccc285baf30d99b2699053c30c1546aef7e49935f10f4ff2d96ccf1be26e7f1333c4e7f6c16296f35f090a11200ee333cb88f924d0e6d4314b0062db0062db746150be5d7b6f5c6b74d3ed6c2adb09c5cab66285133042c579f40cccf4313262054a0d91df0f143fd5c7f93023ef40e6d4c4cdcfd90e1e5927a840402374c318efdf47725de161f0ac04314b23a1964d6150e0db2eec017ad72d50aedb800f9a1353cd20343754d5a65c6ee61e92943a713ba5ee7340bce0944ed8f309507ffa0a21b79a86d97b174f82c889031481b4bf7fda6f352f05a77345ade76cbbdb1ef4b42bc47ff31b50d289f38a0d1751b13c8bc3766622e84ead59d75d4ae41d2abd3bc706228f6b1667a02b740d165d80a7d3799ed375c71d9c4063416add7c7211808bcec4f289e804072e4426b83ff7dd6ea6d86155c3707f1feab0fb1e61397a2e90ec53bee9e149be796711d9ec0096e422b70034ebd92932886abca48fd91dd7db7f2a8de5f081195f6604daed2bad88c0715960c4d030c05f9ee7214891942160e00af4c8c56117f717191a9aecb55b2e82d0f47e0de9e55d4dc8b8830f0e132acc3ec7425db5e4098c1385772afbb58bf86edf9511bac855dd8b1ac4ec4539336e5447fce99fd2a63f10ed9cd9f42267145f2ac01f65f3c0123bc2ff183bfc15fe0046aede9e8232ca24bef7ca3086d92d4ed47137e2e80879ad112fbf6696fcd7896a93775d15a7b2155988a8493bc0e94608b651979d3f1f15d22c2933750dd4d287eba002532c802b4c422b6ce2ea10207f448a40cfd32d724a27d98549d581c63d1be712cf97fd253723fd234f97161dd22e7ec000045e7e23a8408a4e97a6712de49dbb3bae7514a1b3bbfd8b9fdbe6d5fa95d6e126f4e8e17b04e73567b661c3b199d68815b025de2247d6c0de94aa05a9f2552c0e31c38e3cb87744a4834a2ff85818368f39095360eae9ee80496d25e6551ec1b7e616b9c8b44e76b74f31eab8d7210893f10d4f65e29a53c31a2e84b8ded0d75424aabb32b3a961c239195aa0680cce94af13e4037c199abd6470d20f891b1be6faebdbb79f57b304210e70a1f8da590d4b0df85d59c2bce5ca25817f6703960b482e7303a84a56362f4c7d3b08778230451f25e5942eb0e642f5dc96c04850e4034e6a543a61b1d150b77fd532471071de27480b4d66b9ab75f0a282e72f057e4154c505b811aa895bafde9990f322f3ea1c13c5c27a09aef8391bdd8c472dc7d2f49062b457039fc10fe5b4364df3c86605a7bdea2824362bea87b03343bdd661277633b55d7a17dbb1e17ee1f1c5f26405e417713b4425fbea37370ae2756c652e802c1e3444d3ca4e2deec0ef2003ba0aea889b723251304669ee70762568c94bba42d99427408f7527443d093fc4a82f00a310b49ccbdf20696c28e52483a522cdb83119e8f2d5c0b5295ecc9f42020d12b0da710fceb50030401f256e57f8c4f8edd7c6cdf72fad2232fb519c96752dba8dc6164dbeaf0281f7844af403aec85ad9df8dde7d7ef031f8de8c1668400db7a02fa8dbd25cb9828d584392f633111c7cb01efa564da95b6fe7147241e116281f16e15bd1ae4c3d06fa33112ad72fd432d5d3afaf25b77722d45d03b14311985fbfb9a3ec5e5e5fbc886770edda9ad928a02fa0636300485adf41f1649a0875bb957041786adfb7834d4613ebd217fc010e1eebe5423e121ebd97a120be1795892d862910c3274cdec364c5daafad955d71047a6f056d12789885edc09e1bfd7b459f80ae1842acf4e0617b97fe79ce82ab0b2b8cb47183239f144dcd6d25c6ae2b068759c130b1d659478374a7f1442d8c1bdf196cc3fb505f695d328f0cdf23f37002486726ada49fd4c40a207801bb0c0b0ecd8dc842398d89d7c054", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000d160d4006c61f6385f7902a17d4b19cc00000000000000000000000000000000523d6eb247a935a8de4a66706e303c9a01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db70000000000000000000000000000000000000000000000000000000000000013000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } } diff --git a/circuits/benchmarks/results_insecure/report.md b/circuits/benchmarks/results_insecure/report.md index 83847f3b2..4a8917ac4 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-19 13:45:13 UTC +**Generated:** 2026-05-19 14:14:09 UTC **Git Branch:** `feat/1525` -**Git Commit:** `ab26bae432a591aa0345b8b7b64e069f28b26bc1` +**Git Commit:** `cfe60a5290ceb27522bdee8a5e7d508e0e556d08` **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.12 | 25.86 | 15.88 | -| C1 | 57818 | 0.36 | 27.07 | 15.88 | -| C2a | 142625 | 0.82 | 27.33 | 15.88 | -| C2b | 198355 | 0.91 | 27.67 | 15.88 | -| C3a | 132633 | 0.85 | 27.21 | 15.88 | -| C3b | 132633 | 0.85 | 27.21 | 15.88 | -| C4a | 92515 | 0.53 | 27.01 | 15.88 | -| C4b | 92515 | 0.53 | 27.01 | 15.88 | -| C5 | 151717 | 0.85 | 27.10 | 15.88 | -| user_data_encryption | 53732 | 0.34 | 26.27 | 15.88 | -| C6 | 86927 | 0.54 | 28.16 | 15.88 | -| C7 | 104273 | 0.50 | 27.64 | 15.88 | +| C0 | 6847 | 0.13 | 28.33 | 15.88 | +| C1 | 57818 | 0.35 | 27.90 | 15.88 | +| C2a | 142625 | 0.83 | 29.66 | 15.88 | +| C2b | 198355 | 0.89 | 28.73 | 15.88 | +| C3a | 132633 | 0.83 | 28.25 | 15.88 | +| C3b | 132633 | 0.83 | 28.25 | 15.88 | +| C4a | 92515 | 0.52 | 29.91 | 15.88 | +| C4b | 92515 | 0.52 | 29.91 | 15.88 | +| C5 | 151717 | 0.85 | 28.47 | 15.88 | +| user_data_encryption | 53732 | 0.35 | 27.79 | 15.88 | +| C6 | 86927 | 0.54 | 28.57 | 15.88 | +| C7 | 104273 | 0.52 | 28.75 | 15.88 | ### Artifacts | Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | | -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 10.69 KB | 0.47 KB | 3042828 | 176148 | 3218976 | -| Π_user | 15.88 KB | 0.12 KB | 2972965 | 170272 | 3143237 | -| Π_dec | 10.69 KB | 3.47 KB | 3553758 | 187236 | 3740994 | +| Π_DKG | 10.69 KB | 0.47 KB | 3042773 | 176244 | 3219017 | +| Π_user | 15.88 KB | 0.12 KB | 2972965 | 170452 | 3143417 | +| Π_dec | 10.69 KB | 3.47 KB | 3553807 | 187284 | 3741091 | ### Role / Phase / Activity | Role | Phase | Activity | Prove time | Proof size | Bandwidth | | --------------- | ----- | -------------------------------- | ---------- | ---------- | --------- | -| Each ciphernode | P1 | one-time DKG participation | 310.65 s | 127.00 KB | 128.19 KB | +| Each ciphernode | P1 | one-time DKG participation | 309.65 s | 127.00 KB | 128.19 KB | | Aggregator | P2 | combine folds + C5 | 0.85 s | 10.69 KB | 11.16 KB | -| User | P3 | per user input | 0.67 s | 15.88 KB | 16.00 KB | +| User | P3 | per user input | 0.68 s | 15.88 KB | 16.00 KB | | Each ciphernode | P4 | per computation output (C6) | 0.54 s | 15.88 KB | 16.00 KB | -| Aggregator | P4 | per computation output (C7+fold) | 82.11 s | 10.69 KB | 14.16 KB | +| Aggregator | P4 | per computation output (C7+fold) | 82.61 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.06 | -| Committee Setup Completed | 20.24 | +| Setup completed | 3.08 | +| Committee Setup Completed | 20.23 | | Committee Finalization Complete | 0.01 | -| ThresholdShares -> PublicKeyAggregated | 310.65 | -| E3Request -> PublicKeyAggregated | 313.31 | +| ThresholdShares -> PublicKeyAggregated | 309.65 | +| E3Request -> PublicKeyAggregated | 312.28 | | Application CT Gen | 0.32 | | Running FHE Application | 0.00 | -| Ciphertext published -> PlaintextAggregated | 82.11 | -| Entire Test | 419.06 | +| Ciphertext published -> PlaintextAggregated | 82.61 | +| Entire Test | 418.55 | ### Thread pool (same process as integration test) @@ -76,25 +76,25 @@ | Name | Avg (s) | Runs | Total (s) | | ----------------------------- | ------- | ---- | --------- | | CalculateDecryptionKey | 0.11 | 3 | 0.34 | -| CalculateDecryptionShare | 0.62 | 3 | 1.87 | +| CalculateDecryptionShare | 0.61 | 3 | 1.84 | | CalculateThresholdDecryption | 0.57 | 1 | 0.57 | | GenEsiSss | 0.13 | 3 | 0.38 | | GenPkShareAndSkSss | 0.23 | 3 | 0.69 | -| ZkDecryptedSharesAggregation | 8.60 | 1 | 8.60 | -| ZkDecryptionAggregation | 51.19 | 1 | 51.19 | -| ZkDkgAggregation | 21.08 | 1 | 21.08 | -| ZkDkgShareDecryption | 1.50 | 6 | 9.00 | -| ZkNodeDkgFold | 63.75 | 3 | 191.24 | -| ZkPkAggregation | 2.19 | 1 | 2.19 | -| ZkPkBfv | 0.34 | 3 | 1.03 | -| ZkPkGeneration | 1.38 | 3 | 4.15 | -| ZkShareComputation | 2.75 | 6 | 16.52 | -| ZkShareEncryption | 2.57 | 24 | 61.79 | -| ZkThresholdShareDecryption | 6.26 | 3 | 18.77 | -| ZkVerifyShareDecryptionProofs | 0.10 | 3 | 0.30 | -| ZkVerifyShareProofs | 0.23 | 5 | 1.14 | - -Sum of tracked operation wall time: **390.82 s** (often much larger than end-to-end wall clock +| ZkDecryptedSharesAggregation | 8.66 | 1 | 8.66 | +| ZkDecryptionAggregation | 51.72 | 1 | 51.72 | +| ZkDkgAggregation | 20.93 | 1 | 20.93 | +| ZkDkgShareDecryption | 1.50 | 6 | 8.98 | +| ZkNodeDkgFold | 63.68 | 3 | 191.04 | +| ZkPkAggregation | 2.15 | 1 | 2.15 | +| ZkPkBfv | 0.35 | 3 | 1.04 | +| ZkPkGeneration | 1.39 | 3 | 4.16 | +| ZkShareComputation | 2.75 | 6 | 16.47 | +| ZkShareEncryption | 2.55 | 24 | 61.20 | +| ZkThresholdShareDecryption | 6.23 | 3 | 18.69 | +| ZkVerifyShareDecryptionProofs | 0.10 | 3 | 0.31 | +| ZkVerifyShareProofs | 0.24 | 5 | 1.20 | + +Sum of tracked operation wall time: **390.36 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/src/threshold_plaintext_aggregator.rs b/crates/aggregator/src/threshold_plaintext_aggregator.rs index 3155b5680..521111d08 100644 --- a/crates/aggregator/src/threshold_plaintext_aggregator.rs +++ b/crates/aggregator/src/threshold_plaintext_aggregator.rs @@ -676,6 +676,9 @@ impl ThresholdPlaintextAggregator { return Ok(()); } if !self.proof_aggregation_enabled { + if self.decryption_aggregator_proofs.is_none() { + self.decryption_aggregator_proofs = Some(Vec::new()); + } return Ok(()); } self.dispatch_decryption_aggregation(ec, Some(reply)) @@ -1074,10 +1077,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" + ); + } } } } @@ -1273,6 +1299,7 @@ mod tests { async fn build_plaintext_aggregator( initial_state: ThresholdPlaintextAggregatorState, + proof_aggregation_enabled: bool, ) -> Result<( ThresholdPlaintextAggregator, Addr>, @@ -1287,7 +1314,7 @@ mod tests { sortition: start_sortition(&bus), e3_id: e3_id.clone(), params_preset: BfvPreset::InsecureThreshold512, - proof_aggregation_enabled: true, + proof_aggregation_enabled, }, test_persistable(initial_state), ); @@ -1305,7 +1332,7 @@ mod tests { async fn threshold_decryption_compute_error_emits_e3_failed() -> Result<()> { let correlation_id = CorrelationId::new(); let (mut aggregator, history, e3_id) = - build_plaintext_aggregator(computing_state()).await?; + build_plaintext_aggregator(computing_state(), true).await?; aggregator.threshold_decryption_correlation = Some(correlation_id.clone()); let request = ComputeRequest::trbfv( @@ -1348,7 +1375,7 @@ mod tests { #[actix::test] async fn insufficient_honest_c6_shares_emit_e3_failed() -> Result<()> { let (mut aggregator, history, e3_id) = - build_plaintext_aggregator(verifying_c6_state()).await?; + build_plaintext_aggregator(verifying_c6_state(), true).await?; aggregator.handle_c6_verification_complete(TypedEvent::new( ShareVerificationComplete { @@ -1379,7 +1406,7 @@ mod tests { async fn decryption_aggregation_compute_error_emits_e3_failed() -> Result<()> { let correlation_id = CorrelationId::new(); let (mut aggregator, history, e3_id) = - build_plaintext_aggregator(generating_c7_state()).await?; + build_plaintext_aggregator(generating_c7_state(), true).await?; aggregator.c7_proofs_pending = Some(vec![dummy_proof(CircuitName::PkAggregation)]); aggregator.honest_c6_proofs_for_agg = Some(vec![( 0, @@ -1432,7 +1459,7 @@ mod tests { #[actix::test] async fn missing_c6_inner_proofs_emit_e3_failed() -> Result<()> { let (mut aggregator, history, e3_id) = - build_plaintext_aggregator(generating_c7_state()).await?; + build_plaintext_aggregator(generating_c7_state(), true).await?; aggregator.c7_proofs_pending = Some(vec![dummy_proof(CircuitName::PkAggregation)]); aggregator.honest_c6_proofs_for_agg = Some(vec![ (0, vec![]), @@ -1464,4 +1491,28 @@ mod tests { Ok(()) } + + #[actix::test] + async fn proof_aggregation_disabled_marks_decryption_aggregator_ready() -> Result<()> { + let (mut aggregator, _history, _e3_id) = + build_plaintext_aggregator(generating_c7_state(), false).await?; + aggregator.c7_proofs_pending = Some(vec![dummy_proof(CircuitName::PkAggregation)]); + aggregator.committee_members = Some(vec![ + "0x0000000000000000000000000000000000000001".to_string() + ]); + let ec = test_ctx(E3Failed { + e3_id: aggregator.e3_id.clone(), + failed_at_stage: E3Stage::None, + reason: FailureReason::None, + }); + + aggregator.dispatch_decryption_aggregation(&ec, None)?; + assert!(aggregator + .decryption_aggregator_proofs + .as_ref() + .is_some_and(|p| p.is_empty())); + assert!(aggregator.decryption_aggregation_correlation.is_none()); + + Ok(()) + } } diff --git a/crates/sortition/src/sortition.rs b/crates/sortition/src/sortition.rs index f526b5df0..6f20c04ce 100644 --- a/crates/sortition/src/sortition.rs +++ b/crates/sortition/src/sortition.rs @@ -216,7 +216,8 @@ pub struct GetCommitteeMembersRequest { #[derive(Message, Clone, Debug)] #[rtype(result = "()")] pub struct CommitteeMembersResponse { - pub members: Vec, + /// `None` when the E3 committee is not finalized in sortition yet. + pub members: Option>, } /// Sortition actor that manages the sortition algorithm and the node state. @@ -735,10 +736,7 @@ impl Handler for Sortition { type Result = (); fn handle(&mut self, msg: GetCommitteeMembersRequest, _: &mut Self::Context) -> Self::Result { - let members = self - .get_committee(&msg.e3_id) - .map(|c| c.members().to_vec()) - .unwrap_or_default(); + let members = self.get_committee(&msg.e3_id).map(|c| c.members().to_vec()); let _ = msg.reply.do_send(CommitteeMembersResponse { members }); } } diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol index 247725dbe..08e4336b1 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 = 0x23be1dd720adbe93a20641346295fa6b91a060ce9532c5274e91a438434d4fa0; 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(0x05d32cc409554430668b52564a0dd3e7c5e26b2e970ab80e13285a2e02df18dc), - y: uint256(0x185e84c9e227fa12f2f951b68b41c73ccda11518663b45d46a8d8ce054f7d363) + ql: Honk.G1Point({ + x: uint256( + 0x05d32cc409554430668b52564a0dd3e7c5e26b2e970ab80e13285a2e02df18dc + ), + y: uint256( + 0x185e84c9e227fa12f2f951b68b41c73ccda11518663b45d46a8d8ce054f7d363 + ) }), - qr: Honk.G1Point({ - x: uint256(0x2b453c47255c6d5225dc081251a4a781157691c23d193ff46de4e0f41fe1b3e9), - y: uint256(0x18deab5160dcd08f2a7bc2c767fed54c056a62565af8291196916797400802c7) + qr: Honk.G1Point({ + x: uint256( + 0x2b453c47255c6d5225dc081251a4a781157691c23d193ff46de4e0f41fe1b3e9 + ), + y: uint256( + 0x18deab5160dcd08f2a7bc2c767fed54c056a62565af8291196916797400802c7 + ) }), - qo: Honk.G1Point({ - x: uint256(0x2767043cf43b790a3a17ba0ac68a8316c631a2220f95e6ae9f5fdb6c7dccb11b), - y: uint256(0x26617bb06961db2a4c4a87f51155ef06e3e820f644af379137703f484ac8d0c5) + qo: Honk.G1Point({ + x: uint256( + 0x2767043cf43b790a3a17ba0ac68a8316c631a2220f95e6ae9f5fdb6c7dccb11b + ), + y: uint256( + 0x26617bb06961db2a4c4a87f51155ef06e3e820f644af379137703f484ac8d0c5 + ) }), - q4: Honk.G1Point({ - x: uint256(0x2128584d8d874764f2d7926d78d7f8f5b7dc9eb8b5121f3fa3170235fd8fe618), - y: uint256(0x1d564fa1501c0c8a198c5b4597bf87f1a0c6d6331359f99b05b67ce46d278ea0) + q4: Honk.G1Point({ + x: uint256( + 0x2128584d8d874764f2d7926d78d7f8f5b7dc9eb8b5121f3fa3170235fd8fe618 + ), + y: uint256( + 0x1d564fa1501c0c8a198c5b4597bf87f1a0c6d6331359f99b05b67ce46d278ea0 + ) }), - qm: Honk.G1Point({ - x: uint256(0x17fcefb84130f269e76bd0f308fb32344474986c718e43ead5ea21dd3cb10334), - y: uint256(0x22b522a965b4d856e21f8eb12658b17da267d65fe1e0772ce97f26fca9879e37) + qm: Honk.G1Point({ + x: uint256( + 0x17fcefb84130f269e76bd0f308fb32344474986c718e43ead5ea21dd3cb10334 + ), + y: uint256( + 0x22b522a965b4d856e21f8eb12658b17da267d65fe1e0772ce97f26fca9879e37 + ) }), - qc: Honk.G1Point({ - x: uint256(0x2517978cb349ef864c272efe890ffdce75ed7790902c37e7087cf9790e7f1cdc), - y: uint256(0x042f1330f591b1345d8675358b31393c21b7416567bbb4db887a0f5c877d2a68) + qc: Honk.G1Point({ + x: uint256( + 0x2517978cb349ef864c272efe890ffdce75ed7790902c37e7087cf9790e7f1cdc + ), + y: uint256( + 0x042f1330f591b1345d8675358b31393c21b7416567bbb4db887a0f5c877d2a68 + ) }), - qLookup: Honk.G1Point({ - x: uint256(0x13143d24a192079453fc93ca72b6be61609f4d042621b3d3973bfa341bb8a424), - y: uint256(0x2dee6429bc80fc94550da46393279fc6e08def2542d28bbc312a082f31e56409) + qLookup: Honk.G1Point({ + x: uint256( + 0x13143d24a192079453fc93ca72b6be61609f4d042621b3d3973bfa341bb8a424 + ), + y: uint256( + 0x2dee6429bc80fc94550da46393279fc6e08def2542d28bbc312a082f31e56409 + ) }), - qArith: Honk.G1Point({ - x: uint256(0x23de7f17e2a2a904521bcb9ae1173eae1ac10fc5d4d1a2389cc51758fd89d4b3), - y: uint256(0x0363de80dbae03838b654e9ee7ddfe17e891983d01042db26b0afd145d77e692) + qArith: Honk.G1Point({ + x: uint256( + 0x23de7f17e2a2a904521bcb9ae1173eae1ac10fc5d4d1a2389cc51758fd89d4b3 + ), + y: uint256( + 0x0363de80dbae03838b654e9ee7ddfe17e891983d01042db26b0afd145d77e692 + ) }), - qDeltaRange: Honk.G1Point({ - x: uint256(0x0bcde00ecf5d0958bdd6224d6a54719e72fd904eb163dcf5ea324daafacf2b7f), - y: uint256(0x2e7adff5b94dd989e0d9dca6eb419889621ec14f5d570543168e4e42409c83c6) + qDeltaRange: Honk.G1Point({ + x: uint256( + 0x0bcde00ecf5d0958bdd6224d6a54719e72fd904eb163dcf5ea324daafacf2b7f + ), + y: uint256( + 0x2e7adff5b94dd989e0d9dca6eb419889621ec14f5d570543168e4e42409c83c6 + ) }), - qElliptic: Honk.G1Point({ - x: uint256(0x1b0f171ae66227192763b958126f202bbe0bf894455dc70542a65b6ce49672ab), - y: uint256(0x080e380b2fbc0db5614dcd7903bb1837b5814f1ea6d315d547dc63145f12fd9f) + qElliptic: Honk.G1Point({ + x: uint256( + 0x1b0f171ae66227192763b958126f202bbe0bf894455dc70542a65b6ce49672ab + ), + y: uint256( + 0x080e380b2fbc0db5614dcd7903bb1837b5814f1ea6d315d547dc63145f12fd9f + ) }), - qMemory: Honk.G1Point({ - x: uint256(0x2508e83664595b6817e4b2dc994c7f5fcd07b6e0d739f06df3aaef2aabb4a354), - y: uint256(0x1ceac93c1a2560a969a76c9203eac99036b3f50471941f7cb9f4be3381de5ab7) + qMemory: Honk.G1Point({ + x: uint256( + 0x2508e83664595b6817e4b2dc994c7f5fcd07b6e0d739f06df3aaef2aabb4a354 + ), + y: uint256( + 0x1ceac93c1a2560a969a76c9203eac99036b3f50471941f7cb9f4be3381de5ab7 + ) }), - qNnf: Honk.G1Point({ - x: uint256(0x0e154fcc6ac38a216d26f2a5eb005c7a17d7158ccc496355c736b7ee7085296a), - y: uint256(0x11a0c91986352495d1b391c415d947577348f36697aa570238d2b19b50353902) + qNnf: Honk.G1Point({ + x: uint256( + 0x0e154fcc6ac38a216d26f2a5eb005c7a17d7158ccc496355c736b7ee7085296a + ), + y: uint256( + 0x11a0c91986352495d1b391c415d947577348f36697aa570238d2b19b50353902 + ) }), - qPoseidon2External: Honk.G1Point({ - x: uint256(0x1e1f6dce6c7b8406de819fe1d10524ec3161ec0885eb3695a86a5107e1d83589), - y: uint256(0x1fe55e0e37c825072934535d7f33de9196a1b4c8b71a72a958761e4a1f55531e) + qPoseidon2External: Honk.G1Point({ + x: uint256( + 0x1e1f6dce6c7b8406de819fe1d10524ec3161ec0885eb3695a86a5107e1d83589 + ), + y: uint256( + 0x1fe55e0e37c825072934535d7f33de9196a1b4c8b71a72a958761e4a1f55531e + ) }), - qPoseidon2Internal: Honk.G1Point({ - x: uint256(0x0e5d05348574583cfb629d78f4ca745d482d5c7399082b33cfdb7edf59fc6ab5), - y: uint256(0x1f1c05396894b1b80d8162391a7a1c24b2a5dbd1294cb683434d8f6345f379b9) + qPoseidon2Internal: Honk.G1Point({ + x: uint256( + 0x0e5d05348574583cfb629d78f4ca745d482d5c7399082b33cfdb7edf59fc6ab5 + ), + y: uint256( + 0x1f1c05396894b1b80d8162391a7a1c24b2a5dbd1294cb683434d8f6345f379b9 + ) }), - s1: Honk.G1Point({ - x: uint256(0x220924e2d368b51ce6265fb3077f544eec065123aa48ead8cc9ea8d8138a1d80), - y: uint256(0x0a77f2cecf7b6c95615d4cb84f44a68196df673bcdfa9045fa4430991de7459d) + s1: Honk.G1Point({ + x: uint256( + 0x220924e2d368b51ce6265fb3077f544eec065123aa48ead8cc9ea8d8138a1d80 + ), + y: uint256( + 0x0a77f2cecf7b6c95615d4cb84f44a68196df673bcdfa9045fa4430991de7459d + ) }), - s2: Honk.G1Point({ - x: uint256(0x01301eda90b3c69ca965b47d874af1a63a6a4db818b21435579d10bd7866c253), - y: uint256(0x2ebd7190a5695c302a27c81884302282dbd34a86d9f3c1b6eeb28dfcfba69fd1) + s2: Honk.G1Point({ + x: uint256( + 0x01301eda90b3c69ca965b47d874af1a63a6a4db818b21435579d10bd7866c253 + ), + y: uint256( + 0x2ebd7190a5695c302a27c81884302282dbd34a86d9f3c1b6eeb28dfcfba69fd1 + ) }), - s3: Honk.G1Point({ - x: uint256(0x11c240beac486e3d294d585995394c3e895f8cabc94049de498aac199237770b), - y: uint256(0x269695fc9c8c1caa5ae553b9c0ccfb5aadb257f8bf5bfad76bb4d304acc7cac9) + s3: Honk.G1Point({ + x: uint256( + 0x11c240beac486e3d294d585995394c3e895f8cabc94049de498aac199237770b + ), + y: uint256( + 0x269695fc9c8c1caa5ae553b9c0ccfb5aadb257f8bf5bfad76bb4d304acc7cac9 + ) }), - s4: Honk.G1Point({ - x: uint256(0x245003058192b706ca21dd546a96ee58afd8679394d9518da3344ca4b21be2ad), - y: uint256(0x29a048078639061c011b8b6e3978dc2f03ed0f2bc24208c8e36fc8434ed201b6) + s4: Honk.G1Point({ + x: uint256( + 0x245003058192b706ca21dd546a96ee58afd8679394d9518da3344ca4b21be2ad + ), + y: uint256( + 0x29a048078639061c011b8b6e3978dc2f03ed0f2bc24208c8e36fc8434ed201b6 + ) }), - 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(0x01f63ed4a2629fd4c6cc58c73374226a59212d60bf9f9ee7d71fb3b1040f4442), - y: uint256(0x1376d6bcc1ce592b45c346209c9fd2ddbf5f3470fc261c1f48568655638bf747) + id1: Honk.G1Point({ + x: uint256( + 0x01f63ed4a2629fd4c6cc58c73374226a59212d60bf9f9ee7d71fb3b1040f4442 + ), + y: uint256( + 0x1376d6bcc1ce592b45c346209c9fd2ddbf5f3470fc261c1f48568655638bf747 + ) }), - id2: Honk.G1Point({ - x: uint256(0x1830b0efd90ca2e4fea4529f59a1ddb537d8b564810ad2af7837e8e8a5b9e927), - y: uint256(0x232234bd7890b992269353e2ebb7872fe31dc86b650e0f560a06da9dbc8c3d90) + id2: Honk.G1Point({ + x: uint256( + 0x1830b0efd90ca2e4fea4529f59a1ddb537d8b564810ad2af7837e8e8a5b9e927 + ), + y: uint256( + 0x232234bd7890b992269353e2ebb7872fe31dc86b650e0f560a06da9dbc8c3d90 + ) }), - id3: Honk.G1Point({ - x: uint256(0x0c74b589240ea2ade23d38fff23b2658110d1cc3f4d201c7d9c65808e36771d9), - y: uint256(0x162c019fc641e6d049308d7014c1acd4a0cd922871ad697fbdc394b7129e4f45) + id3: Honk.G1Point({ + x: uint256( + 0x0c74b589240ea2ade23d38fff23b2658110d1cc3f4d201c7d9c65808e36771d9 + ), + y: uint256( + 0x162c019fc641e6d049308d7014c1acd4a0cd922871ad697fbdc394b7129e4f45 + ) }), - id4: Honk.G1Point({ - x: uint256(0x21e71e95b4ee9bb8a3122acd478de72a2261de0c897d040edc077aab05f3fdb7), - y: uint256(0x275e27d4a4d9c0c15d998eed6b60a44836146cd9816b1691969ecc8172e7b3a4) + id4: Honk.G1Point({ + x: uint256( + 0x21e71e95b4ee9bb8a3122acd478de72a2261de0c897d040edc077aab05f3fdb7 + ), + y: uint256( + 0x275e27d4a4d9c0c15d998eed6b60a44836146cd9816b1691969ecc8172e7b3a4 + ) }), - lagrangeFirst: Honk.G1Point({ - x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), - y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) + lagrangeFirst: Honk.G1Point({ + x: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000001 + ), + y: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000002 + ) }), - lagrangeLast: Honk.G1Point({ - x: uint256(0x10be2fc7531b16bcf869314c6e9ad92e822f3ca9cd1f2fc4badd9b68e3df3c60), - y: uint256(0x0d9bbf7771aa706695d42688be4788f4d722e5ab38d27acf721626fa295bfd6d) + lagrangeLast: Honk.G1Point({ + x: uint256( + 0x10be2fc7531b16bcf869314c6e9ad92e822f3ca9cd1f2fc4badd9b68e3df3c60 + ), + y: uint256( + 0x0d9bbf7771aa706695d42688be4788f4d722e5ab38d27acf721626fa295bfd6d + ) }) }); 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 c1c931a73..03ed6716d 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 = 0x2937fa911eeb39d6d5a928094331339f60b0e50a6b7d0dd2ae546d8209fc933f; 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(0x26ad9ec73647e6cbac68fb3eecb522376fdd482a519c7e6a14271e668bc1ddaf), - y: uint256(0x2360a0b5ca8b242fe62ba4b68f647f4dcb4441f2c46c184364514a78d273acd9) + ql: Honk.G1Point({ + x: uint256( + 0x26ad9ec73647e6cbac68fb3eecb522376fdd482a519c7e6a14271e668bc1ddaf + ), + y: uint256( + 0x2360a0b5ca8b242fe62ba4b68f647f4dcb4441f2c46c184364514a78d273acd9 + ) }), - qr: Honk.G1Point({ - x: uint256(0x226a163c49844e7a6cc7287edcf1b26c3597e26de9c17bf2961e210bcfadf895), - y: uint256(0x08c9455e7c33f67894eb5102d6b0e8dbb70fac2337edb46cf442e89896a1d02d) + qr: Honk.G1Point({ + x: uint256( + 0x226a163c49844e7a6cc7287edcf1b26c3597e26de9c17bf2961e210bcfadf895 + ), + y: uint256( + 0x08c9455e7c33f67894eb5102d6b0e8dbb70fac2337edb46cf442e89896a1d02d + ) }), - qo: Honk.G1Point({ - x: uint256(0x2b753cc3a52c28ac2e053efc2e6e95e83cbd8353eb2f4a784cf783681f701400), - y: uint256(0x24e380eed26b1da529be4a2666ad42e6668f4be2caec72213b757834da93502c) + qo: Honk.G1Point({ + x: uint256( + 0x2b753cc3a52c28ac2e053efc2e6e95e83cbd8353eb2f4a784cf783681f701400 + ), + y: uint256( + 0x24e380eed26b1da529be4a2666ad42e6668f4be2caec72213b757834da93502c + ) }), - q4: Honk.G1Point({ - x: uint256(0x1a2224b4986b1ff6f1c1949345d84863f0bf504727d65eb04e58073c5a890047), - y: uint256(0x2715c720aeaf5b213505cf7902e376f0f1d4d22319e8368fc7303ac4e9a12a94) + q4: Honk.G1Point({ + x: uint256( + 0x1a2224b4986b1ff6f1c1949345d84863f0bf504727d65eb04e58073c5a890047 + ), + y: uint256( + 0x2715c720aeaf5b213505cf7902e376f0f1d4d22319e8368fc7303ac4e9a12a94 + ) }), - qm: Honk.G1Point({ - x: uint256(0x25b47f2902609f550548d0a2c27c5552d9b51dc112e8e9b263ec80688e33635c), - y: uint256(0x1ccda8f90b56da93afacb2fe218966e07654a5b0d136a37ff11a9188b4d8bfb5) + qm: Honk.G1Point({ + x: uint256( + 0x25b47f2902609f550548d0a2c27c5552d9b51dc112e8e9b263ec80688e33635c + ), + y: uint256( + 0x1ccda8f90b56da93afacb2fe218966e07654a5b0d136a37ff11a9188b4d8bfb5 + ) }), - qc: Honk.G1Point({ - x: uint256(0x23ea11b593f242c992a3de9b9c07d9954cd851d32fec4469c5aa71a814eda310), - y: uint256(0x0b6827cfc9288cc0d66de88351195cdab6fdcd2ae3abd20590eefe3d608ca1e2) + qc: Honk.G1Point({ + x: uint256( + 0x23ea11b593f242c992a3de9b9c07d9954cd851d32fec4469c5aa71a814eda310 + ), + y: uint256( + 0x0b6827cfc9288cc0d66de88351195cdab6fdcd2ae3abd20590eefe3d608ca1e2 + ) }), - qLookup: Honk.G1Point({ - x: uint256(0x182070aa2b03de9e8b4cada6a0760a0c6c72852783e7db97b4cd91281c03d3ea), - y: uint256(0x08c55ecc2b52f5393505462ae16474727d81f865b293adee900644436146a8e1) + qLookup: Honk.G1Point({ + x: uint256( + 0x182070aa2b03de9e8b4cada6a0760a0c6c72852783e7db97b4cd91281c03d3ea + ), + y: uint256( + 0x08c55ecc2b52f5393505462ae16474727d81f865b293adee900644436146a8e1 + ) }), - qArith: Honk.G1Point({ - x: uint256(0x117dc34d0c135ba99861d8eb4b0ba9e2454b11353971f8f754147edeee218f25), - y: uint256(0x1a52d059eea878acde805797fed889677b7504c05e08674d9ce8b7ed57893699) + qArith: Honk.G1Point({ + x: uint256( + 0x117dc34d0c135ba99861d8eb4b0ba9e2454b11353971f8f754147edeee218f25 + ), + y: uint256( + 0x1a52d059eea878acde805797fed889677b7504c05e08674d9ce8b7ed57893699 + ) }), - qDeltaRange: Honk.G1Point({ - x: uint256(0x199f74af36a62f790433377dfa9953d9d79fe5f3f843a257ebdfbec208aac0eb), - y: uint256(0x1729371270b5dc935d448c628bf3d9a84162def09ea5c3c991b18a84270ea98f) + qDeltaRange: Honk.G1Point({ + x: uint256( + 0x199f74af36a62f790433377dfa9953d9d79fe5f3f843a257ebdfbec208aac0eb + ), + y: uint256( + 0x1729371270b5dc935d448c628bf3d9a84162def09ea5c3c991b18a84270ea98f + ) }), - qElliptic: Honk.G1Point({ - x: uint256(0x0ff5ebe74c64d32e3e6a38e021b5aa0c7ccd683325165b0c7ba3fd40c06f3a8d), - y: uint256(0x2792de8ab62b7a8ac9433df3b9aa53506a6aa0d8bd30faa62b73ecde7d3e1ac7) + qElliptic: Honk.G1Point({ + x: uint256( + 0x0ff5ebe74c64d32e3e6a38e021b5aa0c7ccd683325165b0c7ba3fd40c06f3a8d + ), + y: uint256( + 0x2792de8ab62b7a8ac9433df3b9aa53506a6aa0d8bd30faa62b73ecde7d3e1ac7 + ) }), - qMemory: Honk.G1Point({ - x: uint256(0x27112e3ba014810ee6ad32c37b61f5b89f7e25aac7d1ff50ef65004f2404c6e4), - y: uint256(0x2a994019050ce0aace7a030a29216e89a0dc832c2ec855d2044e996f74b065e8) + qMemory: Honk.G1Point({ + x: uint256( + 0x27112e3ba014810ee6ad32c37b61f5b89f7e25aac7d1ff50ef65004f2404c6e4 + ), + y: uint256( + 0x2a994019050ce0aace7a030a29216e89a0dc832c2ec855d2044e996f74b065e8 + ) }), - qNnf: Honk.G1Point({ - x: uint256(0x192328adab0d1f56b4e1028bf449af9cc0d77f53b9c1fca4a5f2f6148ebece7f), - y: uint256(0x0808887e1f287af5bf7c7d8602d014e3419155a04b3070a1ffc1cc0cbcd50e2d) + qNnf: Honk.G1Point({ + x: uint256( + 0x192328adab0d1f56b4e1028bf449af9cc0d77f53b9c1fca4a5f2f6148ebece7f + ), + y: uint256( + 0x0808887e1f287af5bf7c7d8602d014e3419155a04b3070a1ffc1cc0cbcd50e2d + ) }), - qPoseidon2External: Honk.G1Point({ - x: uint256(0x00e3022b8e21c0f5f462a5c13224d7257b6a3ed0b9d7bd734b6c1125abf633b0), - y: uint256(0x094c5338f79be9ea69797ff7b4a15d9aa918f4e50e6a86fb86a95ed8e22da852) + qPoseidon2External: Honk.G1Point({ + x: uint256( + 0x00e3022b8e21c0f5f462a5c13224d7257b6a3ed0b9d7bd734b6c1125abf633b0 + ), + y: uint256( + 0x094c5338f79be9ea69797ff7b4a15d9aa918f4e50e6a86fb86a95ed8e22da852 + ) }), - qPoseidon2Internal: Honk.G1Point({ - x: uint256(0x0798a34a77c41d91638fbca51b0057fb480898be2b7e5ae82bdf54a3cfb2f811), - y: uint256(0x24e471b3f0ddc31e775dffa48334e7c6e77f50bfce60d27005b1d280c99ba779) + qPoseidon2Internal: Honk.G1Point({ + x: uint256( + 0x0798a34a77c41d91638fbca51b0057fb480898be2b7e5ae82bdf54a3cfb2f811 + ), + y: uint256( + 0x24e471b3f0ddc31e775dffa48334e7c6e77f50bfce60d27005b1d280c99ba779 + ) }), - s1: Honk.G1Point({ - x: uint256(0x155ac8307b2528efe8cf165ef29382bea17a6d4cfb7b21890315651cf8718b30), - y: uint256(0x2bb8ca70fbf5a1948582af7371e4d4143238ebfd32f1159c23193dbd2b22a03c) + s1: Honk.G1Point({ + x: uint256( + 0x155ac8307b2528efe8cf165ef29382bea17a6d4cfb7b21890315651cf8718b30 + ), + y: uint256( + 0x2bb8ca70fbf5a1948582af7371e4d4143238ebfd32f1159c23193dbd2b22a03c + ) }), - s2: Honk.G1Point({ - x: uint256(0x19cbadda70765a860e9f7c73ecab703a2c2ddf2d2407d33a8da8173701b64d9d), - y: uint256(0x0d7029ec77a89543facb74408b4536a5e1ea600dc8d0f31aee86541e81d7b930) + s2: Honk.G1Point({ + x: uint256( + 0x19cbadda70765a860e9f7c73ecab703a2c2ddf2d2407d33a8da8173701b64d9d + ), + y: uint256( + 0x0d7029ec77a89543facb74408b4536a5e1ea600dc8d0f31aee86541e81d7b930 + ) }), - s3: Honk.G1Point({ - x: uint256(0x275603bf2ba273a074f0791d33ee3f069449dfa2a89a7c69734d2f8fc40149ea), - y: uint256(0x1db1f55c697e147f29ec0c1aa54400ba4c44277d09d19ca495efa22c11fd4873) + s3: Honk.G1Point({ + x: uint256( + 0x275603bf2ba273a074f0791d33ee3f069449dfa2a89a7c69734d2f8fc40149ea + ), + y: uint256( + 0x1db1f55c697e147f29ec0c1aa54400ba4c44277d09d19ca495efa22c11fd4873 + ) }), - s4: Honk.G1Point({ - x: uint256(0x00e93184e1e95febe1cf63bc56e1d5fb1c7ec4dee64066e43ccecfa24e9aedab), - y: uint256(0x13e68e8e14a84d3477825b856ba254d0af40cc58fe21f276aa95511f4906563f) + s4: Honk.G1Point({ + x: uint256( + 0x00e93184e1e95febe1cf63bc56e1d5fb1c7ec4dee64066e43ccecfa24e9aedab + ), + y: uint256( + 0x13e68e8e14a84d3477825b856ba254d0af40cc58fe21f276aa95511f4906563f + ) }), - 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(0x12280b9608c56c0fc84d5d14ea31e2ed4e2e9100c22b3f87216bee7bcb78fcb7), - y: uint256(0x145a43cd663f4f88784979dbedc733da43611d13a3ce638923fb5bb4f5f7e89b) + id1: Honk.G1Point({ + x: uint256( + 0x12280b9608c56c0fc84d5d14ea31e2ed4e2e9100c22b3f87216bee7bcb78fcb7 + ), + y: uint256( + 0x145a43cd663f4f88784979dbedc733da43611d13a3ce638923fb5bb4f5f7e89b + ) }), - id2: Honk.G1Point({ - x: uint256(0x1ff8c6c495764da3398f1eef1688a9a8adb26177c7682642e3955ae3d89478ed), - y: uint256(0x028b82e61296a2f12eb839e90846a4ab38ea638d4cb97968436a8e27902e1a92) + id2: Honk.G1Point({ + x: uint256( + 0x1ff8c6c495764da3398f1eef1688a9a8adb26177c7682642e3955ae3d89478ed + ), + y: uint256( + 0x028b82e61296a2f12eb839e90846a4ab38ea638d4cb97968436a8e27902e1a92 + ) }), - id3: Honk.G1Point({ - x: uint256(0x1860447ca16e85291f8e0aa72314153f7a15572de7ca5bef409a8ef31ee16fb1), - y: uint256(0x27fd203c2fede3b848d91415fe7ae6f2abcb8d9d09e732996250f0871d57224c) + id3: Honk.G1Point({ + x: uint256( + 0x1860447ca16e85291f8e0aa72314153f7a15572de7ca5bef409a8ef31ee16fb1 + ), + y: uint256( + 0x27fd203c2fede3b848d91415fe7ae6f2abcb8d9d09e732996250f0871d57224c + ) }), - id4: Honk.G1Point({ - x: uint256(0x1fd4389b7f9fddd10c6a909708fa0dadd7813a27c5aa904c74fe07f2ee366416), - y: uint256(0x2292fe69c9250db970af41a14b5d469d8a87d92128f54b18d5642689c35cd02b) + id4: Honk.G1Point({ + x: uint256( + 0x1fd4389b7f9fddd10c6a909708fa0dadd7813a27c5aa904c74fe07f2ee366416 + ), + y: uint256( + 0x2292fe69c9250db970af41a14b5d469d8a87d92128f54b18d5642689c35cd02b + ) }), - lagrangeFirst: Honk.G1Point({ - x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), - y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) + lagrangeFirst: Honk.G1Point({ + x: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000001 + ), + y: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000002 + ) }), - lagrangeLast: Honk.G1Point({ - x: uint256(0x21bec12b46400ddfce58bfe6d2f403895eda4bd1718990489594da1c3f0fea92), - y: uint256(0x060b340ca69b6e38e8426ce9b8f6a613d862edb65bea3c8a14b1fb4680caac73) + lagrangeLast: Honk.G1Point({ + x: uint256( + 0x21bec12b46400ddfce58bfe6d2f403895eda4bd1718990489594da1c3f0fea92 + ), + y: uint256( + 0x060b340ca69b6e38e8426ce9b8f6a613d862edb65bea3c8a14b1fb4680caac73 + ) }) }); 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/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts b/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts index 33bc5e2b5..6ab527d6b 100644 --- a/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts +++ b/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts @@ -345,11 +345,15 @@ describe("BfvVkBindingIntegration", function () { [folded.dkg_aggregator.proof_hex, dkgPublicInputs], ); const pkCommitment = dkgPublicInputs[dkgPublicInputs.length - 1]; + const dkgCommitteeHash = committeeHashFromLimbs( + dkgPublicInputs[DKG_COMMITTEE_HASH_HI_IDX], + dkgPublicInputs[DKG_COMMITTEE_HASH_LO_IDX], + ); expect( await bfvPk.verify.staticCall( pkCommitment, - ethers.ZeroHash, + dkgCommitteeHash, dkgEncoded, ), ).to.equal(false); diff --git a/scripts/build-circuits.ts b/scripts/build-circuits.ts index eb92b7cca..14b42ff38 100644 --- a/scripts/build-circuits.ts +++ b/scripts/build-circuits.ts @@ -223,7 +223,7 @@ class NoirCircuitBuilder { return result } - const sourceHash = this.computeSourceHash() + const sourceHash = this.computeSourceHash(preset) result.sourceHash = sourceHash if (this.options.skipIfBuilt && this.isPresetUpToDate(preset, sourceHash)) { @@ -249,7 +249,9 @@ class NoirCircuitBuilder { } this.copyArtifacts(result.compiled, presetOutputDir, preset) - this.writePresetStamp(preset, sourceHash) + if (result.errors.length === 0) { + this.writePresetStamp(preset, sourceHash) + } console.log(`\n✅ Built ${result.compiled.length} circuits for preset: ${preset}`) if (result.errors.length > 0) { console.error('\n❌ Failed circuits:') @@ -665,8 +667,12 @@ class NoirCircuitBuilder { return outputDir } - computeSourceHash(): string { + computeSourceHash(preset?: CircuitPreset): string { const hash = createHash('sha256') + if (preset !== undefined) { + hash.update(`preset:${preset}\n`) + hash.update(`noir_config:${PRESET_NOIR_CONFIG[preset]}\n`) + } const circuits = this.discoverCircuits().sort((a, b) => `${a.group}/${a.name}`.localeCompare(`${b.group}/${b.name}`)) for (const c of circuits) this.hashDir(c.path, hash) return hash.digest('hex').substring(0, 16) From c8ed260ea643c772b59111c1d3814d2db661074a Mon Sep 17 00:00:00 2001 From: 0xjei Date: Tue, 19 May 2026 16:42:52 +0200 Subject: [PATCH 10/20] fix changes --- .../results_insecure/crisp_verify_gas.json | 64 +- .../results_insecure/integration_summary.json | 102 +- .../benchmarks/results_insecure/report.md | 82 +- .../decryption_aggregator/src/main.nr | 15 +- .../dkg_aggregator/src/main.nr | 17 +- circuits/lib/src/math/committee_hash.nr | 15 +- .../src/circuits/aggregation/node_dkg_fold.rs | 22 + .../enclave-contracts/contracts/Enclave.sol | 3 + .../bfv/honk/DecryptionAggregatorVerifier.sol | 1637 +++++------------ .../bfv/honk/DkgAggregatorVerifier.sol | 1637 +++++------------ 10 files changed, 1091 insertions(+), 2503 deletions(-) diff --git a/circuits/benchmarks/results_insecure/crisp_verify_gas.json b/circuits/benchmarks/results_insecure/crisp_verify_gas.json index ef433d9ba..574f0d0e9 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": 3042773, - "user": 2972965, - "dec": 3553807 + "dkg": 3042639, + "user": 2972941, + "dec": 3553795 }, "source": "folded_proof_export_plus_crisp_verify_test", "artifact_sizes_bytes": { @@ -17,63 +17,17 @@ }, "calldata_gas": { "dkg": { - "proof": 170100, + "proof": 169968, "public_inputs": 6144, - "total": 176244 + "total": 176112 }, "dec": { - "proof": 169992, - "public_inputs": 17292, - "total": 187284 - } - }, - "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.112618042, "runs": 3, "total_seconds": 0.337854126 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 0.613634805, "runs": 3, "total_seconds": 1.840904417 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 0.566882416, "runs": 1, "total_seconds": 0.566882416 }, - { "name": "GenEsiSss", "avg_seconds": 0.12575218, "runs": 3, "total_seconds": 0.377256542 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 0.229232416, "runs": 3, "total_seconds": 0.68769725 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.659371958, "runs": 1, "total_seconds": 8.659371958 }, - { "name": "ZkDecryptionAggregation", "avg_seconds": 51.718500333, "runs": 1, "total_seconds": 51.718500333 }, - { "name": "ZkDkgAggregation", "avg_seconds": 20.93329175, "runs": 1, "total_seconds": 20.93329175 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 1.496832486, "runs": 6, "total_seconds": 8.980994917 }, - { "name": "ZkNodeDkgFold", "avg_seconds": 63.680252444, "runs": 3, "total_seconds": 191.040757332 }, - { "name": "ZkPkAggregation", "avg_seconds": 2.149436333, "runs": 1, "total_seconds": 2.149436333 }, - { "name": "ZkPkBfv", "avg_seconds": 0.345419874, "runs": 3, "total_seconds": 1.036259624 }, - { "name": "ZkPkGeneration", "avg_seconds": 1.385717208, "runs": 3, "total_seconds": 4.157151625 }, - { "name": "ZkShareComputation", "avg_seconds": 2.745120014, "runs": 6, "total_seconds": 16.470720084 }, - { "name": "ZkShareEncryption", "avg_seconds": 2.550104026, "runs": 24, "total_seconds": 61.202496626 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 6.228828042, "runs": 3, "total_seconds": 18.686484126 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.10385268, "runs": 3, "total_seconds": 0.311558042 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.240555883, "runs": 5, "total_seconds": 1.202779416 } - ], - "operation_timings_total_seconds": 390.360396917, - "timings_seconds": [ - { "label": "Starting trbfv actor test", "seconds": 0e-9 }, - { "label": "Setup completed", "seconds": 3.083043708 }, - { "label": "Committee Setup Completed", "seconds": 20.233524667 }, - { "label": "Committee Finalization Complete", "seconds": 0.006022792 }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 309.654077459 }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 312.282717083 }, - { "label": "Application CT Gen", "seconds": 0.316875208 }, - { "label": "Running FHE Application", "seconds": 0.003470791 }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 82.613328416 }, - { "label": "Entire Test", "seconds": 418.546447792 } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000005427a2ed994ffc252000000000000000000000000000000000000000000000006d241db063da6026f0000000000000000000000000000000000000000000000043626bb84a690ed04000000000000000000000000000000000000000000000000000193b944fc656600000000000000000000000000000000000000000000000afab2a128f843ba2800000000000000000000000000000000000000000000000ffdd135f92e6eeb130000000000000000000000000000000000000000000000033876e4ff9051c64800000000000000000000000000000000000000000000000000022e934d408c6d00000000000000000000000000000000000000000000000d2aacac256fe3719700000000000000000000000000000000000000000000000a2b7205fe21b014c600000000000000000000000000000000000000000000000b16c840b7ebe5c0ec000000000000000000000000000000000000000000000000000160d7a729e6eb000000000000000000000000000000000000000000000004f7bd1f8c59cfcd0a00000000000000000000000000000000000000000000000c6910e92c5cf1346400000000000000000000000000000000000000000000000ce9d40d2c6e2c24a5000000000000000000000000000000000000000000000000000194c4bf6f99841c44750b98e8b12c09eb7105c405d362756f8b9f4429e44ea7c476067abed031301689e3b57f19c1daba697f74bbedf39016ca9ce34cc521368d45dfb22109a11b835b6e94ac453a3ce5898868a4cab42d8d35a417e9e3e86d00eae06dc3661d2fb487229e2fed8db71130092935e55d256a75107fa96e6a3a23ea8cc2e8336429ee252bcac0ad7229c8f25829d8c533b22df0c5e5b036013f05f795502acd8c08ebcb8f199adfff8d624b85f06f4babeac2e9c483addcd9c1d37470eab5079615d833c8c924c55f6dc49bab1af3749cb6d50a775c7a902a0fa98373fc9a3b232601603ba65283002be13c5c3f80a74675d2f98988b112a4d232f0fadc06654800c72ddd92d4f108f0a548bba472c75610544d4d0d22a3ccd05e914663e93ec72c348e6b39f9ef1df2f0b05814f5a86a80a796bc3980268972986c0db0ec76ca028774356b570c119159a3b6f4bec272a8ac2b65e3661483419b1ec82bee728f1e83b88ffdb8c2bb668a4963de3f92604a0beaf90d2940eac097c6c5b1ac8a0e187679b30d947880bc050d437d70d6a9e52caeff087dac0a627fa343b48702de2323ea003f71de4b143244e858beb7032422ec4364222fbf540117b69ed27baf0210980aa90048a07e991238d24501107ff46264444e1c1a8e3fb6cf85dd12d70d25e7fed1ea3cc18e8987d7df2ee1378d55b79aefcfecc16d9a3bdbca3d4c2503ae270dc55614c28019386899d2b1473c7ae4eb1fa98cf8646d224f5ea13cb22527c61737f9958b027b06fc66f27a797ea8c10d3002dbd12864e769d8051555195d4c402d26641b7c5d862396c5a6618d1e54462b620f45f116a524a5d130331b49e391d5adbf8ddfabecf18dafd472c03caca3e7489467b8f826ef9bc7b16a2b7de443763d311542d0f7ba75c4ac61c381aa883a56153dac0233b24c2b1b542ea08ab907ab674a9440074d582141a10963f2c3fbb8407e1b8218f1f6123df80838bbc699ed896d4b98b7ce92e6ace001189eef52436a0a7ed249e60142278827e4642ef254526283a7d17777ea4a89a5ecf102c3a81957fd56a4864dbf836a1fb504bd8e7e3916867f161e9b397905e7877f6d58f9e1a48a103b7096d5d79f03f301693b397e9169cf2e06bf9daebfb630ab2e8cbe7461797dd834306b712d1c4f5696801f0196f91ed965829065f4591b95902c7a034e211d26e41b1703550b2693c21a8b711573b4ff021892366ad8968a2104c9485c11e17f8cab541abb01b6fea8d27f6e153c10765b27ca4120b6eeac0045bb8d9d4e8a3cb105601c442305eec51c2887e768d4d51ffc323225deeb1b540e4d0e6b12c82424bc7644a9179487f9375e9ef9a421f7a709413ad73725381ff8676636116843dfe97c01a71e4c2af2c1fcc6a3835296d729383f9d1b5dd21656f44256d4a8355187bb79e824f8c064c34d9c653b30673b3f5a2ad6365ede55baa021eaead52c7c417f46ea1420f848dec5ee8e2e4496dcb32445beda9d72b0e9dc505ac5471cba4826b7212ffd8668e33524493f12cad4c336f9425e27e07af458f680aa7556d693d2429b23516e5a2e20f6ab4f59fed1e3033f0b712a76bf4d235c54ede97fe9a387caba10a43e1509f0e6c163f67dd09015a90ff48ee7543424f51229b33de99d428a3004ec53f1ff45a533b356cd647d2a7f6339da8f8a4f6e0c097ee7fe6ee7fa62a606eed939e91067552413b42233ac95f4ad56ca26d7247199d2b53abbbecd602f2df89683d1b224587ea81d8ca66defea2c003aa77c467cb0d51543a04e401bf410126537a3e3feef528a4f94de279b833418f7215d57c1cb2c47fdc166d9ee24170d246422ba15c5d96f5a7c34c81d23f7a555acf5f67229eb9e94c4f1694db2134a751fa6280f3cd09e75a041662a8cc0b266315e25a354a58b8541ac7189421b4a57c8f1daee946f60d115ae4430dac1bc818c4f9ec244b60cc6e659835e6a0b99b702cefacc3b601622faff354765deee0c9e0f62aab394ac7310238e7096298cd07fa6af7ba58089902eb54381c6da51b2b225fba65ae99a1c1d724e9e9001a6809bab48ee95c1baee7ae05f02204d143994e977100e0450fb9eb073dafc26d1adf32f6f143dce3846a965acf99f8921d68c5553c893ace6d30b7f42e34a07fa85456da9930a7ebb651e2e8720b7a1e452dac68c32f9729bbe8ed10d66b20494a03bdc16679631aa96a19a495f0445b5e2341c843207e24b29453364742f0f4c48913f038624283aad35cfea5961543f74258d9547ccf86df266330284f6211282a6d25eb8a2edbd5bb21a1fc8dc5f807f78d8c0cd831b1e8ef8afcf80fd1bf9d7779d73958e3cbf0164cb6eb9512ced3961dbd8e5600ad7db18adc5b2a206c1902f40e0c388e7b08ea147a9028094e402b8fb5bc2c6d030609ffa3cf97c208fb4414d508f49e22ae7baf57479677627d470e20807302ead225aec2f3e24014327ed1cb3ef9662b9dfe2cf4fa39ea77b63d10cf93b6de3cb38925c8f027f171595a5b16a1300ef2a4848dec9867e0d36f27a6797d046eba20b60fdd214622084b25b40c55414595d095b4d3d8be36242911213c161e33b2c24d8d9482e8f227c7d47327840dd14a790d202f5b1b4d7876ebffbd5c8b59bc1c5e72d1562ef24befc8f005177e891a2e17a029ac9415338824b678ab58e44eeb753713f8b251611cf41d1bb18471d26aeeb7701ea36aa93d2a102224cc1052588f394c88d290e929cba7d61959b04d5b033d54766aee7185d338b31678b3fedc741a666b23d27c74ff7b2593eac40a9945a60e73c7cf7975d440d239b00c7cad347d824513f1c8fbddb5f754c292178eca5548bee9d306b6fee243801bdbcafa9d8453d3a48109d15ca04cac8ab7b3c93562b158b4bab0cc051eca421ab5c8fc6d4a60a58501a38feb0a4732c38abd876e0b5baf947dfb0f3ca987b736ca8330e4d42c1e05426d3b08ea5091826e4a32c5c4ef1e2b6a9e80a5b4762e887959e79cca1e94cc00c0336da0f387a4fe57fe353552ba8ede9e31f5b85c3658e5c43ce251e2ddbf50e166c29985ee8f3613c648146814b09dde9e9cd78f2a81ea3f73408280a5cbb160197a8b62af1814b64d002058947d07591550cfd4bad5169df7b8779d5a2a5242bc8ec5e8a35ece688fc7c6ad09509eb4350eff0e98aecc9ed266aefcaf6ef0075f5b3bc59150d2992162620938acb03376fb7aa0ce3e2283beedcc0db26942c77d59950f3e76c86a79a13c07e45161cf812d1d393b734d62dc4c215a39edd00c16adfce0a0ca7efb9dff0002d60aadc0c6e4b86ed6814853857f4f7bde5a50a37e4862b3de19096cd6b48b6d609dfc6583362d3fcaad9a79a8238c75d2a6402c0ba6956325bbf412d787000b9bfd8b0db6e6ff78e9ecb7f38845ba6818a1904f9b2405aa88bb415a0b51e300fb260e55bec6f1d916acc63148eddf10666a41fee9f325e36e4d3b1789ad36d266056cc53b36576ba294a1b4b6f904a3b80cf2e6fd2b7d3e95625ef16d9d8eee6dd9661c916bb2277baccbbfc182c9c74c70e0c68efba43cd58dbdad21bc9ed58ef09ed0b18022da877828574c9221779f70e04c9f0fcf05d526b9bf79729b673f32796a0f1eecc0a508e91b925086f337ef115fe2d0c1fa9e9691d622b086685986060ed2b79d0c0a6c54a299cb32c17c7c3165055ebdb2d8271325bfef3add0482b3497130d398804d69e66f889b4bac75c0e14534a4b5b12ac8dc61ce4165cf84241c203655292bc6a7c6c54f1641e765c2b27f416297deb2474a50b7d28a1ea3d805fc577fce06e286160663d75e63d8125ea040c1382b541d5d52777c5beb5fa5b366ebecfafad3e36f5b2963644c17113be9ee83fb92303a34b5163ac1709d698c952a0113c99259706eb0b0cb57aca238fda315a015caa19b67a67b2e9df8ce8694f7796511e8392a2df820bac8b2c2193e4ad15a489fdd141eebfd24e917835fd8fd958aac6eae3299acf418ee39a1cc2183980e4c09b31b4f975e587e473c09c065f5051fe7f31f1b361973bd87719f484814aed4ad2e448b3bc99671ccc1af77900e747dc0e90b6e93594de697a2cfcaac6c33ecf598072c0ac193cad8dfb027fcce0fa7c3ea275ede47e683dde0db3c4ae111bcc1ab458db20bff8c117d02239a6c1c6ec4d21a0af673ae1a42a0044a0159c1e23e463bd6b60704fff68443fee73d6214c714aaa793ccc339e7b06ca9127af4c41ac7312a1d2fd7b30099fc072d91b7456936b4e974cc99c5bcd143fe8395aa5dd14b8be8948265e711bd9ad6cae938273f44cd9a0b840650b462d5503a9e2070daab4e933e1de683a011201dde145987e277dcae8114d49056700e602421111c15fbed4728c3180666697e330351fbbf2395331ad4f72ce09dc21548ecdd678ceb9f2ba17bfc607eaa32f4838ac14c2ef52a0b18da26db7d52b1c5f3ec56fe48e45a0ddd8c12bd719e8ea4031d577a75b2064500d819de9f1af2d361f1382269adb6e42c5a0d07dcd2128e04acca36489d23e55e414067c606107e59f635e1d99a543c25faea760060cf93fb1558aea51ec8a1c4aa1ca86b34f08d7d71a093f5675c68c9d3d1ca68aba3975a315ad7d67914bdfb3e26eea736205a89305449ac7c3bdae9e169e7c50f962aaeea83d1c1aced16c84933cbafe5615ba2d0ff96a87f4a2d97f3778a1c19e12e0ee350f19bf37ac514c76c40a08da23161861f90aab73b9979b4d0aaf65a3c4bf61c066f67b5fddcfaaabb8293514193ab141d32a18e5ea09cb0864bc3e59ff46e85c9ec33684b54ca11b0711e7482505e86f2ed799ecd477ea8dc74c6dbb6349959cfa6133aa298bf97a16aabd2a1f1e93d03538ec2adf140f50ca5384ac1f51e9f0b91eb651af8566dacf83af7226be48e368ef21d810e272f1fd4abbea3ea7c0429032aec9d08cf31ef0445e2111480087e31fc4eee991df405ff8ef658acad605b93399f19d1d5251539e3c2a21682d0d46c9c290bf4025885c8b829a2ea48895c9de693b3d2f6215a164a33d2f91db9f73cf3343b53df247340a1dc2069eadea19cda684e22757864add16770049442b7451bc67fbf4d12ba4ef3b7fbf1034264151012cd735ec2135ca3aa8288bfbfb1fdd9dfe27d71d70335b9adb8a6211c9f6d954513f8581779ea50fcb271bf6e3d84927583aa787c969b8d4b7ea0e231de07b45b5625d0bce77b6fad327ced1eee216fb07103e815fa058dad6c9d633e1f828b83e38326c7535cba62b138eda58a22bf1ddf2447c536ffba078bae7208ed320e6f135e5c5cc8c69627d15bc7c974ed7bbd33ea0b38bc55dd005091f2e898aa0c9b355296c30dadf99972c6bc70999b9c727c1fc77ab5309d28ad835bac6ac74be8f8344c7309b1e9c31175f922b6a8c45b7ca7b23408d302925d94684700cefd959bc20d0bed14482bd0acebaed6ce3274d682f8189b88856c62616a84742619f43ac61e497d81ad02021a1116915fd972410f0f607cdf9b76ab6077239c7ec8d88127b02c1f99204df063d6fdce454305b7b6636918275793027def492ebced6e0f8c948a81cca82822efb3bfd155fc8b11c87ef4155977f9908b28913cba20e3ee547cfdbac774be30caa4409aba47a47898bfd62d126668b276cd7130e95bff2cfaf9b1d5f67566f18dd55e21a22ddd14363c0498ad2e29f9aaad8da7aaf2364f01455a8c66d51e21523a86aa8bb228ce37b3ab8ed758979805dea717810e201a83ea886658d2c192f062751fa9ad00f0c3abcf402901185f98b50b5830b479c5b03d2a7769d86dc29d7bbf1f14e3fdd96aea564df38a58a9d7aa7b4b950d4ff9b2baeb7fd12063b006f43d735123875aaaf324dee5942e7708afeeb616323a7824d01104ba950b01d2ed69d9ad865b23ac688494c461747ed3838abe69809d68c00d871a022e621121c45c397d5979a64469373ba2043c21d986ec9ff69d9a86e32e38005da9edb2c5536a42b6bea1f4a3104ad6d6f55926d7334d6111f664e66c1e1334dc0a3be19a0778e139674813524c28e93384f0a0307dd2e2d2906a34d513898c715b4900e816426eefb96196c5a24242463fa9eef879a84ddffda7408a10e2902c4049605086f7544cc27fafb47d23935c796b5a1c46a06665550d819875c4aed25f0e50ef7341e1fd6eec58cb9237d2a62bf3b686f4cf2210abe367284c7a91516fe6d29b0e24f81cf79efa422e642037734f33388ac21292cd636b3246b96f6c751a720bc206b63f51a1195d32b64932d61305bde740cad4a138649cc9f7e2b2f8edf2841d3b2a2a2469e0b59b584167915eef7da32455deb4f92268cc496a07be60f18bfed970eecba7e9d43415155e844ed87864ae0682050fff6343741b463be402367528d2a6631371c5f22995dd9878859be81b9dbb2cdff59b776f5ea6c6ba22bca5677b5ae9a4aa25c1c9fc3fbab0c9a40317bf22fb8627fb4fc7e858a3e2c11a231622b389f74934afb33b26403aa579863f0977e230c7df65088dc5ff97a1cf772260cc16803416ba1d76edda186979cf5a9725842585d7c77b2cd4fb9f61ce9c6ff056e8f8762fc2f355d6136ffcd49627ebb4e58cd711c56843b5cb4ed1e072d092a7931a57ebcad4e57e14df027ed837b8b3b0903b1996fc6a475faa11621dc49c44e03e9a9f0bba70b42a96d88258eaba4e175ff50c6903e9f30203c283a925a4ec8c62f69aa72d80be89e515fa5e2adf17db2deee86dc00622e1ef113db1b3c33a9f435db2e3081de6bf777aab27c72d645f4c8e73802dd112b9f0e0e7b95ed1a03cb6e0e20f121127f574b123b9dbd2f4744d8644deff40a3dfc132740bc41fd107465ebe01cf00e3786dfc16c78e10ac7f8bd29c0334b570f88b61ee6fa5a292a8b801a8a05d813b85a4eeb3361c6cb386bc50400af38bf92123028adf840f8368e8bf6ab9cc310541dd627c914094e2089037a5bda512edd26a50b4f6dfbe4d6e19290da4a183f24197658c8d3fc02f402119a98e09dd42f0ddf1b72fb646b22deddd2aecc3c747d1d7c96569a1df62f750dc0d5bb8d0fc62c481468e9310d37822c963f1b9fe1f6b8b2efa4610b304e75e4601e7dbafdacb5321f5479c51b931401e10016d89c54724fdaf6c9988d459af9aef1d0fb0d04debd03e3ec9228fc16a48055b55f810a72bf30bb0ee1ebb6abed769a5445cee071be226716851281d8c0e03bd5edd44b7cf510d58b29afd4e77fe5518816821697d929fb08d77798c06a1f0c77b9af14abbf79ee2c307fdad2d0530f71c98fb626710adb5f48de96d1e4f458d5f38607d927bd6c0d5e1cd68dea421d66160fd89c7f1adbaa32b1144f80bc89de0523293f2fd10208b780e7a7a33ef5ddb5f2a328a310ce90537ba9fd0a1c20278e249843ad0185cd639081f8cf9777e73ba2ff4d752ad5c9b89624cf8a16e00127527f0dc88e8a2f30416b0a6f1c68c3ee1aaab4510718c1fec2a1ec3d3ed44704b8eab63afa6b7578d2b23cc024f7e786de234e75299bbabd2698ccfe09dc0dad1a8da8bcf1ff56d70486014281749a253cb2fa7c137c1fb8940817085d81cfe10712b6f1078841a069d11bb8c9ae8e1b7c96203f2de4b8f460aaa29ce8f214867c80cf5ee01dcf3fffbb0a4aba5ea357b43f778714a0e6e5a88402cdd15661caa96c1eff838c9d110fa41adaba2f16909424660f0be037c920a9e0896b96e4741378d9ada7f661dda4cc59dcbe0baf905f9aac1419fae7dbaa67c5b716cbc798e91633cd644ab51f6e392f0bbb9bd6dfc9d821820c275da0cef71d86108839a62dc17ce62fd013148037bb8f9eaff1f0df7cf7530c8347b646518a1a7e26ee77d072bb8164efd58fc67089fd81ef778fa25fe7a51668183d73affec12f2693f517416737be621f39d2a984f9868edf2e95cb9352221569c1a4cd8f00c168304962564b834276d5947a965bf3e3d778e9e998166e0027d26e139a8227737bf2dce3d4e184aa193e1debfa354dfa66de175e154938151827ed49d4a988020c96575b60bea8ac4542e3bb50c0c57a0eee83fd16f7d32e672c90fef162349138a32a8bcae4a2669819e95b99bda0004035c4c334b51904ac5a9ffe9c5ab955c17488597794288943a09aedc771cb2f740dfa925f3e38053bd23ebb807e754a9ab811da53f0c68d20ecb5d3d331ea411368f8ffe2ab381aee0421ef6029ac9ac06635b28f168e3d1cd9bc601d267df1668540eb03c2dc0398867cb6806e214e25ff07128cd65f75b2323f3cc3f6eb8a7e94a9cb1108a017379a285b003cb790b2d4dff7a83e3d28eaf53dce39914ac03fbb07901048351dcfa0a14f4913c280050aef6c8e25c029d4fc33831cc24ac8e8ae8c493ea1b315d9775d74f63bceb9115c181bd86adb585afcafa2d58dc63f1888e9136d386c18fedee4e37578c6abf32b93ccc064c3d0c55b887f9ece1a5f61cc1baa4828250139ddbdb992639a3905c04a310c10cd849f4ac8c3e08f8e512020d2bf23e6121f13db7c7a5f65c7a649082c61cd76f89f5b1ba5b1e572292211f7158f6ecf0c0b236fd68f1aeb4edc78171301f5e357294017927d40b67718633ab3f811829226d0de241de2be29895744767634378213c4862b2fdda53169beb443e8ef4fe703a649cfbf629bf554bc9d08b385743a33b028490276378715d982178988ec3f2694c5511979e058d6c86375a7b0d21e83d7042f8db4f17944a69ebe1c6ed16119f897fa5c9dc86ef23d663dd1be79c6fbdf4b46cea563fc13dca98646e3188520574ec994ddb3cd019cbd75e32400bc771893ad2500aed3757ede87fc38f67322c1e5bf85d8d9ad22da0eb8848d24379062dcfe171eec7106efbcbcf443b469115dc9485e989c5c0b6a9d702223c31c6b21a4ff831956237f5acd75453b75de185fa18fe9e5d87c0fe2539a78471e4bc7c9b78f4e41c860034271bdc511e9340e6d4571ba8ac6c2dfa6955ece1cb4a2dd96936d0fc8edfef05b5de1c3fef37826d0deb099f334f4719c62afcf06014047bb6e10b2b9336de07e0e066af47d231a2d56d393ec4903d2fc4c825c8bcbd3a522f9bb481bc98da6b3e96579fe3ea418273226404c9d86da57c891ea33bb0e82de738c4448469c991208eaa8e6356a2ffe1e79cf6f65af2f4222c67d87c6b443042c68d96c5ce069d61f4adfcc64ff08a70b8796c0ee517cab57ce2e162f9cba588799be9da90cf7fd1bb45aeb8fd309d4a162c74ecff399cfcbcd49d9afdbd724f1bc54329bbb990d83d389517d3d0c75ee82c1c9c75f7a17741ab6b188b4f38dfa6588b420bbdf98126ddd1ecd3425ce7a7553791aea59e971433bb351687e9185aedeb781652bab5438a9a8cc4202fbd9974c864aab5e69e064b01f44e1bb6673df3625825dbace20d18c3ee78f2d86bb769cd098c86e06cced60e53ecb10ca0e13ab5167753ab91a914084f7661e6a65ea09e5453e01ab423d42df91f603a6e47a1494566e96673eca55c5084909f7bcc007abcb20f422158ffca594e2cc602627cc88358af3311e11d23cc01c0e11ad4f3c25d939bbcec101c4dd2abbb48a37d928f365470f236a8d04c8cca82cac8332f3bfe006ded5bcefc66d761ffee35d3dd45ca488bf9a79a50eac2cde238e7d32bf059ae8e207b92dee1f3070615903aaa66afbab7d23f29de47c98c92ea084e8b097cfa9e12f356c94f4659126c1cc30a0cd6f52d470bccb9409d0800b46a37632a11516d15c56d7ea870bd9315cb81f6f902fdd0f7455a18af14830300ae1171718a4908d16538cb5c929c10b5a727b6ab0217d1b46221a76caac7f270aa5cf851cd7525d2049b214996a569486eb2ad01e890577c09a053fa67ee005e680498e30fd0e439f746a1d29bf91acad7ab03924042c527c19f68bd2ccd4053aa5773a58afcc7bb36109f17dc2e6fb1f4294896e7a9b402c4f466c667c382b2bb4df3fcab103f8df94a63cb710528c76c9e1379df5f296d75c3786c260c22b20846ebe0c3619f895f3ba0a1b7bc477d9b4bf450d9a7b00b26bb6e70d5da72f0457710db196a06ddd3b8b62e303c08f8a585ef70359b86d465f1f8bb73c3a1453b2daeb92287fd42102f7b5ca5a560ecd106e6b408fc797ebe163f467deca04044bee8ffb8ea417ad3a7b8bc359c67d841f6692db1ed79c5cc25b02fbe5c811049dd1ed0b5c352924cc3e8b2664222db798b7c6ef923e3adecf989ac62da42e473e269d9a7e2ddda23885573aa476d1afda16c29257d76fff097954052a85224cb8ab3616616ea1d52f321bad55930577e6ab58121149bb36063657f971ce0b83fbd09fb46d34aab513832fb7d81595136826e0de9599cc98657f1d6359e40c81d682d1e20b22d514470247fec7077b1fd7b3ca5911460955f64cafcd8d4a216d2b05bb88f67e6f156ebeed80b25bf9f125270b1365fefff4115098c5f65b0d829f8cebb57a7c7805002abdb074d207969974ef7812823e5ddb938674ff801c4c9bd4b72d9ecedc4b702eca90b62c905312783803e17ec510729d5d38567c0bcfd30a7b2bd45188bece4463dee5444ed24fe4adb2867371e565e1e3aab628107ac1e12d020be22c848a8977118ccb2d92977372dc6a0f8978106acdaea6e0292ccd626a2b1eb06d169e505a6341c6422e52996eea6de39408db47c67984951182df115ccbb65be8b0301291ac4636d6b98d7c3035798b2ff513b53eb82d921f51cd3e97d6505b90ce990ba4a8d916514621c24ce6eb09a26e98074dfbcf0d101b640d3bcd25aa75d8ada4e20e0c0f2d17ee3a33357cfde8a0b8b8379c182809af3ab78bbe6a8b39ac620a3d4ead2f251e7b9333a63e76a21c7e9066b5276d2e721ddb3c69086779960a78b08cc0519bec15b1f8d7ef2a926725d64b9506c7103a2eab75c13fda0a345e32ca698fb8102614e8ebf680c9f9960855fc0429782504ba67ece853767898b85700b7d1189a27147c6587b2b86eae6ec6da78f133245dc01d446f00bda83401f3d195feb523784eeb6bf0027fea97c94cad1ebf602e70b69ccb05ef3f394b022da4c0c4ed5028d7a7f343f9182e15a987e0ec1c50040cf0d57aad81a2157b45cd0ec5d2dd8051f456b9115e8530f3a9bfaef8870f1bfb114cc5a6f7d7806ce6e2e3786860cb10c75af4a4a0aad6393f34a3827cab13e3015f656c040f86d0e3ba53d855399e6ed9ef007638844fa76e303f6d2f6a0d5b89bf97f7451df6ce4f9e4fe6d349dccca1c4e2ca2f48a77c26a6f592bb48032ba8271657f1a3ad8b826c428125c38d1fe7d141a63e3db8ebc52732eeac300f12d7c6ea95217873f3ac2b99e61278ef379fd78337854d81f8bc895536173a14d4e18eb945dfa4f676efffab03565d899a7b1ba4851d92fd47736ef9418cf20ca268683fa15eb94f399f72b5636db0e5d3b7d4b6a59df4be6d0d75e76914ee13a60b249096345edc3ad5177de2efb8995e918e139ef99e29f23ae4b7bce10802c1cdf46d78c354977a380d0fa181edd7d51563ecb010db5df96263d1ddf3bf268b3eccb7f8d9eb3083c8e1bb21e7cbfbb06346fe2290d97ddd852e37ab4bb001eff9c43143c8288f88cad33bf291abe41167d311972febf2c7ad57590a758e157de6987ba42a59f96c5266fedd56085d6272920d0ffe46e64341dc47b19dc223e96bc01786d7753ab35afecc5c719f29b08c93f1f98438801f0b296a8b362628ca14b9f24007d51941ec228ece5b04f88ee6955f3554c1e0d936826f51534c150a500f9966f975fa8197fd781e0f93b3a5e792c0847f89cc963aafdbb0c16524db2b339cb153fb23db491993979f23ddeaed0c826c75f07221fccf46d6dda51ed9cd3d43e52cf4a59b96751b05e17fe19548485ae19e3fe23cf2f82d5b983d021b37cd76bdbd878bc8cd49b41cfd4e6c2ebb9070693373b64489dad9f9e6162b9928fce70ea5670ee265aab85fa86ee46897a09dcf76e6709c5ef1603274cc2ab37d949d26e56a0adfa75b6d2aad41c04e42aeeaaedeb2986f1a6cc878f447142967bb077b3ea9964792b1039a815c65db489e3225d4755f038bbd411c7dde1c4e6d270d649f842f7d8c78b3d003adda353cf06f25349d5222a6d0bcaedc5707bf54b2b666cf489a7638216e665e71734a16b023a4858320b0d96908285a5217c636140c22faacd97964a4dfc0c4fa41a54a2bb0d2425c10d7481ab27aaabf2e4cd612c34e777a6b2f5ebabf1f5d818d863a60a49733d24cd8c6b72aa805111f4206a3fb56955b441691771e4d29b7928079b01f07627d34c1beb5d23ff47b03498b3bbc2c94cdb18e4f2104f17f22bf2df403d2d347641a00c521017f9a091107ce97ab1939826d8edc70b104242ca2e2dbf5be43d8250026af929be9a3a92ddf88e2978dbac1b91090215454a1930379686ce96094b198e60c8eb8503ae71bb9c966874f964f97ae919c73254d318538874ef4702705de9428f981f139e904d374daff608b58e8a5f1026ab3e9a35379f7d6cd25281b0d9abe41e9f282240538ffe266a4dcb85002fd4d40e90d28b876d48f1baf813451d59ab4eedc91161ca555aa3f878db7814e35ef7a7e4706d21e0320564f6143a86aa7106041724a2aa71fb51c91ae4959815b2cbe3dc7c71cd0bb03433c6823137e30db9a620a800f1f78f876e37e85ec1c4fdf63c9c4a4a40ab94a607cc6c14a7d170b02e604af0d4cb08d305c09d6bb29e33cb97702eea06b87c89ae44ac7598448b274c52f3d049ec959106eec78613838a837e97d7f494869b6e7c086402619f747726ebba2148d92d292b8e20d765c381a83e4eb794a9af15c7e9d4ede347e284cbdb8178d190f3948b3c9b05b4702ea8c5ef529ca26d7b840baa7dd780567bcdb7a7c61dd2658d4f94bd234a1e09f7995c40a0e593c60f0851c991e502e6de2ff483a8e1f2ab0eaff54012fc4eb1f4524d2033ead54f5a315a2d9020acdf6731ede50305d04f8ca370fc0ed91324ba43e8aaa1d2e098ef20c67bf68ac69cae3de5de993631f578b274bb8a9daffc9a181500548f4c8e16547801f672dd3a6ed780ec370eb27c17e9676566917b0b24f93ec005391c4f338bdf8758576cc3b3c6b241447a0039ba76ac1c9eb52effe3d4e70d1617d1875c07a44b25b2d80da267b3740ad970c47e1fec9b5fccdac40cace2beefce18826a23eb4d07b1a009db25b2dea6ffb2cc470e2419f2a4aff07a198c843f52cc57bee5df7e0e8464fa9a29991bd12a82f97f8c2e8f0f5873298313e53f17cee2ce90f3a2550048950457ffe145ac6881f13a6ff6a1e3b5a1dcb59ff1d47ec6ced8ddc6ab5162a7dc676f84ef5c941612c2f67b984fd81ca3ef6b93a27d756c0f4ff1940fb0d7c7b826986dc522b03f92f8b25340ce0daceb2d9c4983373cf53cd3b043ecf5bf73b077493baea47271019cdb0e7c28dd135685ef319d75dfa25836928b6a71957bc9ba46f556bf0c4bc2cac526ec3fed7ba2b11d0836923284d9cf1a609015ffbe4ad1c89a6c7b8dfcd1ba78d832d3022fada7ffbbe6ef7da3f392be43ba377eb0c3feab9ad7db607ea0001034bcdd15b5c1a8adaca877dc27cbfe0d8ca99ebf58b7b89eccd991e29a51cf85dcafdd2007bf477a35fc6490b78283111cf8bf3e151fbc322564aee8bca195f622ed44463ad949c61f8a93a84a929def6441975065cf2eb887b4836273320a4962d42109b44362597d44dd3b82ef0af798471c1ffff7477395c90ab26ce189563c13de09b25cb3f81479a9c2e1002556a5bad221159f45ac01a9b84e288299799819099c129b9a742fabeaddbc2189b11ba3b82fbb8938f91a6172961ae08496b69a2bc73af9e02c15ebd3b7ab7db1454538d62fa63441868e20534a8c71009f1122d822c5db1c104df011122519afe8d55dd35d6c252223c0a40f07ae9194f5f635230c06a259eee27b05b23f82dbbced798e7e3f0e6a8470428ea3bd216e8e2969fb632e3a2a0ee5bfbff6c2b82bce702db04a5f0a4df8de7f1707a772fbddc0d35dbab0332e92ae3d6349d637173d90a226e2b145eb355ee8360d1e52223cba36140d531408c6fbd49bd4154572eba6b5b4cd18177807eea3c5970c01121f6d014c65d2a73098dccfd5d920876e0e99b19b273aefa48044caf127e75163e939f3e4eaafc3bf8c2524061ed5ea365e24a2250816f27ae08755eb307da0bbcc3c0983d953f919b522041fefee59cd9d96b853cf6b8767a5573d0c4d6ad305cba385b6d235e0d8fdea59ec6a6948338ac431535414aa2919f75dd70d0f21064ada32aabacec88cb1a92a99fc1c6c7f2ff808757bdb56a95ae11b44456aa055273083e3fa43d816390c962185dfc50c16faf010b74e39f011533cea6b3ce0a0b15992fcff180e4909661d502b9e7d4a735e9101b708510b01f834cc08245275b28a79dc54f19189b8756decbb519dd4d2b7dec71868dea469705f8d6be061c72ff0fb451e6d860f8d694b46ba87d209128e1c4f7e32722af1679e7baa8370f999db4c627b535f5b2d953573eae7b97814e87e3ebfb0bcec82c18e28b2f5d18bfe49ea065a686dd34e63cbc364235da3676ce4226cf9cb05ad91d477f309a26f4477dd2e778c4b50f1f725590e7f75fe9e46cfd1a139f0a9f130eb07809362b8ae3c1fabbf3f657831fa0a0cfac6f3405b9380712d4ad885344a9c69b9b02", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000e0b1a7da3e047845b1d8ff775f052add00000000000000000000000000000000ba6cc6067d9aebd22aa647499868d26111521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" - }, - "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000312a8f853a46ad8950000000000000000000000000000000000000000000000053fdd2acddf75b42f00000000000000000000000000000000000000000000000a88f17af3c3c0039d00000000000000000000000000000000000000000000000000026b4e21c49c4800000000000000000000000000000000000000000000000571c6bbb50a7e605c000000000000000000000000000000000000000000000003b06b8a62b6ff439f00000000000000000000000000000000000000000000000a406e0f7ace42494b00000000000000000000000000000000000000000000000000015bb9eb3fb55e0000000000000000000000000000000000000000000000035c33191f6a014d4e00000000000000000000000000000000000000000000000a5917facff53230a5000000000000000000000000000000000000000000000002d448fff5591c30080000000000000000000000000000000000000000000000000001ffd7a66840c000000000000000000000000000000000000000000000000c66bd6a7b1539fd3c00000000000000000000000000000000000000000000000dd4af2f894c7e385300000000000000000000000000000000000000000000000e26358010edd2eaf000000000000000000000000000000000000000000000000000022a7fac5859ca1e90cd04e5ffcbf6c71ec61cc2ae9aab902a0fb4fa1773218ef831a0189c6da40a2c1e5e6ce201cae653ddc10d41aafa0588a10422bfafb8b8066a06b93407a61b227938b91228349304ad087e3d68c3d7a714d940dd4997a26526f1c918c6b815ea5438f1eede873183871d8b8c952ab7f9f00796500b1755102cceb1e6bd382fc67f3e3aa8b0ec912d44ac31892b842bec6fbcf674e23ca2b9307969fbe4fa07cf015cf4dbca9e7846328f330620ad4ae74e79fc87a32c4131c9e7d26c8b7e28dbb5c90ed466e09507a61716a6e6bd59181b7f763247bea444384584c7e8ac16ed88d759c0a39196fbdc6aec044721b849e6d056c91763a0b314269a21900720e74a900444e7c42e1ce70f42152840178f5559f63332a1a1746b525d722ff708f0a1f1d0036607cd983ffac29224e1bfa05de2d02d104e326c69c2ab424513114e4656a4766d85168d36a5b5e79d4d90ef46bf807a0eb197624f9d8651c81f1b0b59a52f0be0ba78094fb9582fcb805272039845feb7e26cc9e9c087bd703b1ed8bea6a0bd7ca560425e6591091564fff151c04b13378c39e838dc213143312d1d9f68eae024b52f8a4f6d0dc5d76ed64cf4a729b167ce62e890e958d627d2040d277ac803e4a7f08e5c0d3701eea9aee8914f7f04ba961b821989101de7a1293b8a47c69fc120f3e3ab1cf75315573176f1d5b32bd0a09465750e907c44f52577abc81703d64918eb22fca332c2d603ca98e6a5d65a1642f1fca0daba682b17cf76d30f2edf93d920ddc87d762cf310860f5e9608ec4d95af5aa9baa1a21c05e6a870151085fee2176ac709101912ba9d278862321caf91711a3941718a0c158b14e0e8c865fa45e95d81853d4c83af3f0b4b78186bd7490d80a324458717004a2806da584dec525c3508a608d94756ec875a0e15f211442f66779e051eb42c18ad0290edf383ff8e26ed6a9b96c88392dd147ba40d10f2d0bb266afe142714d493ee3fe57858083b8f9577bfcef33e4ea30be6d15fdfbdfc7c2d8523e4281ab3ddbb039eedd28b26bafe5c301504bf8f88da4d31e16001015a1e8a936c522c1c16662644707a9281992245cc203feb75341d1ea27c107520bf897172392a2f3fdd1afc8380086e6e595e446ca7553bc180728e55e34883d6760a5c231be21275fe254ba19f443b55b8e3adc094b7621b3900ed90cd61e7d133962d376c5b1628ba6aa5871fc7516cc3d741a9e0049f5cc184d437698b9a927fd743c645040936659eb8c72d35c7f1cf495075859fad3d4f6759cf1140ed45b857c8cfb41d2f415d1a4d4f6889e087d2ab17210241ecf2cc412ae9df24b203671991221c3e2a789b7d451a6777017f127393022d91e84711de1de9238eca4563ff08b8cb0f102eb0a3fc969da0fa75118372fe3015d37562f8c81b78d6091eeb9e32dd12a3263867b6644f83e00fa935478d2c647af646e163dfb2625077015e26fcdbc6d92aee4c39efffe577684b7d4818bcf57b27b258181ff5d2848b4077596f08c1840c93ae83cdefe78b6d582c3e70f7e59ca0cf331cae4ca0affe6184faaf82175810f7d1c63432177c4663167eb7536ce9ab826ab2ead6603e14474b5ecf3272e2158e12d17e51a1dbb03a79ce50bf70217135909bcf3ab0b0afbaaf7000b28f220cc085c8d33dbc859feda8bc2b6c4d03e81da737cc4a2a9ebd1157fd0c1ac1441d59a883ac5c259fc58ead531c2aaa234f10705b143284571a1514758b320f320662c97914023d0a21937713b7647ee807f86f5485301664da7fbe46cec1c88508893afdc844fb0c17fca13df00d9b2a3fa45cf69f2a2998ec8ef69de37148d31f8f9897c860d4e8409a355cb2a3ac6be0f55425f63d36d06600131a725e2132251c31489f93761226e30128cbb603a5d2f17e79ed0526712d5d2374cb4e5c1829d39c2feb8523e91e59088be6659ab5fe024e3a0b306c71ba0b5703c679ffb60e1973ace159a6bd73e363677a031a72457a911e7658ce7e517e296c65447009085b6e26addfc88d54519d2a5ba252e9cd6acfa8a295f87b83852661e5f37df0010b99091191d06a3bc905e4a920844d8fd6a986c0597a0b5d5a0af207e5584202feac48d7bf02d573aea8e2b65ee62a6968306cf18c52debd8e0e593f524de409ebdd0bf4c65668ab0659ea1284e7de44bc8e90431f312ff585fd1b2268d75f1511db68a4ced686f244e270b39093fb6580d618f3480b39883eb06925b303131f818c6bbfe7e0451552b1cc718fe6bf25b8f65722c329edb45b0fc8f86005711ff86bc611a91891204f1c1352c04e467816eb3d637693bc6fa479e765cd489407f8870aac11f80c69313f7c704ca99a1b24fae889f75b295af8d6f6c0b25a51218d84f23a1cb256a464e3b3e5ac64efcb47336060f675f73ea28d5eb13516e61148dfce44e232e91a6b2618e1d4305148d4dd456948f69a3f1e843ce27e0ae52cdf83515af53b8acf3dbe95a8edf66280f9794c5249c97fbb40db6a4fa4d61a2d4e021844b6151ba105874ed159d40c0acfdd6976ccab55750aa969557f426a09c240388e16bbb4d639fde88676a158260e2b454120fff932f6b671803dd81f033f680bd5f9e1947fa409c9e1326e92baa11f275fff4bbff430cfc61c3bbd0e02d150af4dd072cf96f577bd2b682656b8ac75411275b2f8f34aa9d2cee70fd700ad5deec2a22d0b8ce8982f4243a2b4de625d84cad7efae743dc2bd761fcdba0acc6750c675c049239eee472efbf67e866e67062c7f3f5580a7bab50f57bb822dc5b8c730dbf443e0615d8fb7406ab1a86d2293963bfa6768a516a532e1f6b315d9bab1937267265bed04cd54aa50c02347a8c7b1b3eb58ad24d334c6aa1a0e03ab474b2f328742012f847fec43377d8f80c2f6a21413f06d34213e54bf889402c7310861009735660804b2b42281e40025b2582e39989b218e1fba5dee27d90017221db795dc096eb273c1d9e3fa208808a2b9dd8d7a15d187ee74ea0ad84124c8bb7652ad81d2c6cd7dc156626a083c41f88d34131d6a857afa04108b771620bcb7e37ed965da55b701b3e734dcbc8609d781cc677d677579bf243f2779e111a66140060c233ca997b9e9940179e4f87bdc39986551df89f94643416c537e005b1ce0bed6524a3a13ca4ca4c613b924bc9d3b505c9d34d424573ba935946e0a6307744a875f5e3162fccf9e28cc4c530a960a9c2007c22a2e7227311c60d92531346043323ff3d849c7500eb14b948a97889b1af6201c444a8e156cecc85423ad0d9afcb2b8f6e7eb4209138863d04bcfd8f97c60b19f6e4f5f53e0fd78d20723d4f93a179a27a59edc289413b041eff2b0e1f02a1597e82f9c40565c0e1b092ab9b0f829fd2325ddb0607bafa31efe7e9ca42cdadd1d8deeeaeb1d4ff56205eb74af7da2f65b19b911bb861444e7eab710725de0bcca573b2cce82aae44a239139af46302611b55922b21484e6f4cc06a502dba4d7f7c3e31bcc1fe5840007360c6afffc356c717bb47ac891fc0f9a730558fe8aed33c473a45bc06a02540c77e7faa173b495c4c5dc83c29244dbf2ef576be3c060a96060836e2e2d8e28149ea7e78744d764f9ecb044be417312f207ced8be362adfecbf9d849b08a6d229df377bbfc41bc2ce927fc040cdc6948598867008ea416bdad0b77ef4f377e324a903de05c1a8559ff3749b7ac5bab0286b1b433fda0a7139927dc230c42ea100bd4cf9297fda04df9edb9d3061bf5fc91a79f2dd1849e529b1a98537906e7514870096b08a38c5112b07a59ad46e7e4c6d8322871fdff22381a881f0d7d8702bbdf95515139f66da6c5a4863d7e7b53129dbb223cf15c5f4fe31231444287f0bf272ae60a20604a21c49d2a51fb25b17c2ff3a68873947da5155283d6c0a371882a3a4601c58cf931ad159a69478cb3f92c2368e58627d8fc63a09deae329c2179287dcafde7ed77f0f356292d0a335e7d9d68b3cdb3cea583450a59cfd5ec092e9ef8a77db63692373fb037ef8632770ed4200fedeaa6e0f558e67bea9c9f11c65d5ff25b5f9d4251d84235df2ce0bfcbe88ce980caa2a159ef77cc9573610d2844fd0ab00c04b007cff838ebfec214761d6fe8c636253b17766444d9558404192a2a8e0d7df5aabe258688b3ca8b68f732e4c88fdf5337085e6ea37f59ce0584bbd09f492cc20966aee2f2ce929fe5874b12be10a7403580a2015640c9261b626a3e4ba46a2a27cb5a7649060d912c78fa0fbf4c6f57c35bd5fe9c3ca035259794300b5905b33884dd53d58503c4060c5917031232ab44348dd27f95f62f137e10cb5deb44901fc562ca6f29a690be6f39aefce02c22b2476a8e82fcc77c0890a186a3ebf3e0ff3503d3c2e8efac08bb52a3273e7400cc5400e5f7385d9a1f0e83b573d1586614e188c35a870a0d244719bf366b2176a4584758d38f92660796338f177fedb649406f596d071d30752a4f7751bc697b7aaa20648fad04a2176ea776b6a600b025f9d9830fdf3044c7b6e180bc168e741e3a8c9a6447be061309a6b8ae0fea0ad4ed51050aadd9df16e66489b1c1a45af6ff26ec8f5a554d263c0ef7436f2855fa20a13aee5e6e9abcfd0f6103858242597a723af5ec6bdc26afb848244f0b6b88974a7649f577aef3c18042f7f0c0240ed06ae9ddd9689400b528d259f16326f18e4d9968140ceefe8e35f7df0f5c9e31eb9ef9afaa1e1b107619b8d745a7413674ab38b12563750428cd751869af8a284897ede1844181018e110afc61dd3ce0d4a418b6ecbc3cf6ce7d1d5814352c6c86fe76dfa9a9742753b0286a22ea0f19cd84c549c8551fc04222d5f5a43b0c8e8c37c97907626901577728de1e2337811177d672de4c15c066a16728012d8f6f08788889bbf65406813a0842c1a7ad0d137ee1f888a2bba9ae6425666e119b4ff8e433b53fd3e01739b691622cd0035ee5b9141be685421e2342c17ca231ab31a6a20ae900122b17fe0ea53cd3389bcc94cbfdf6a1a49f72878656bbba02601ac8f76dfa9dcb1612d7fd5615eddd4fe81a450f8cfc4931d3d310281e7728a29e52ad49e927e5c22da3a74794fbea01a961cdbcbb6659a7fce75e6807292ca007780e3e017db4b61029674552ca688b946813ad5b631b27bc4309c366afb2f2390cb15e75c8e16b0c63e908ac26ed4888ce8d886d9eab0696f6d410fc47935d928ae40ea63d22e62cafa727b163910e394ff602b00c243fd60ede4b995cd503d1571772c69a20680065933aa51f23d25a33a56a695816722a4bd53def5c282b4f4070b1e9ab62592204f9e6ad2fd26ddc59c40b6a96489d1d89123403760026fd8dc573f7cec14525e802219c74b509b723ac5bab87c59db3f667a74c26c384305717d09acddb52301d3a2ad4fb2511489387657f71397d01ce8766cbd176f00ba4c37dfc8b5b5d22e79b5e6e244ee65c74bf3b8e5d9aeff46754ffeecd2ef962e6676ad87fd2480e6b1034669635ee85baa10dddba443584fcfd2e9b1e3ce38f50efbdadbe4b402c43b42464b599c40447dfa0802e1938dc84be0a3a33d0199e6c05a170aa5ce11069db429bb24df34476dd3799df06892f1b6fcbb2032f277eb50c9a3353e87c08f7250b22c6902ce746e9984a02dafb3d18bfb8a1babf0b6ea4874c3eeb9cf101e7e74c66b25bb2416160aede9e01575b45cc0b6f14015da95c18b67e7166bc16050a2d65bc3ff5ddca6becb59c187e686d7753224bc49a47957073bfb6a673233a02790938b26caebcbfba480e68a67365a97442a5b91f68195f7c246a59ef0ef8827eaad232e92878a8b3bec861efb9d32ed06c1eb994e0f0235fbfd785711233bc36b20751654b0b8f95448b32cdec919a80c0902a7bca212473e9e60f120ebb89709ac4953d6f2a205e5fbf09ce8647bb80c87bf9dc7fd304a02e68c6962dd3bb9a1a8c7338f45f27c372a6b781ed4822c9ae4eaa8ef9d56606353a6f3e1250727f272ccc609ee9e39f892c33686d88dd15a3fa6c78ec0958c92b82fb90008dbb496864b3d1867086edb1095fdc75ece3a2e166f054385c4229c3b944e1223e3857a7356f8b7b9d06a424dcbc2e8f62be1adb52b563074ebb73cd5be13815dc4cbf1262ac6c6c20e6ed3f812bd26086771ce1a1d89e934f859f367c4e9f2d56b404295d9ea940c0e1f5d8f9eeb2796201c9e0ef8483e19cec66c6c2408809a577a9541da1aa89c5c3b4b993c75da82572772d9dca0a9371d59badc520020096b1191b962550cd065781ce770e42fba08605a22283ac6ab14860768724ee224b2c8b3ab53437401a68e9637da41f7f3c12b118e31a59b72f7d2927eac3a42c1dc5e65203d2bc6c994ea11781fa7430f33cf52719724f62e48aed905087630aaa2258bd137ffa9a3e87d31c94becaffae0aaf39ea4a1517e998a53c9a06f21b7925efc2dc5781a2cb2a77f7c11c95a97a53f86d67747b1cd77d4d5dc2cb5c27747242d4d4cf92f7b575cf3cc0268260f6e6f66680185def9753c8ea2195392b3a2622408a88ff2a0de00b1fe81781c78d2d056a9351eb83bccdfd96bf4b9407dbb0632093e34772464e5d32ff9b7dc7ef15d75648cf69db97f9a55f8dd55f0dc42d474b936c99a77cef8e7a9253c6ddb9289672e57067ff679946c072c49809edf255cc8f5549f47625c3174e5e372c4ba56ccd879380f021d961b2026c1e2c432a0685305ddd1777ef5dd550b5ac84458a3ab13ed8554036476ba780f4a6128064204b952b23440a199dc4a4eb1f357c095ac203887d7e009652884a299203ef023a8b5ef5f3f2f6567baa0f1a3fbc8b36606aa998312ae7fcde8524e6b22a8d3ee11e5f46e018f9f7137b53456c93836685ca81193df4e36106e9e67d722c9f44d2e95535b1ea7144fb65f3a49ccbde27127446f8b48a93ed9ee10946e40cab66821d8e80da372e8c3a05eea60394732d5967470a99e0cc46d8c8ef54b40ccabd27c19d503e94dd2a701d89a3c8351d27a529333bf4820b6644e3da820d19f3d2ea3e86d1f6dd5c3e593992d4e3f7cbf7047fcfccec97d934096cabdf84263e966f77925a48ebb22dad0814115128746ae8efb2ac7b2997a8ffe24738722ba6876b888d8afe65f2a1b0e3631d98302ebb057bd7074c29aabe65c2fa4fde0da2bbbffb2f8a4143ad1b86b5b286c3a280e2ee2a87f358701bdc55e39d33970db14da12d809bae278310f329050afc3ff0c052aeb73b834d49fcda26f3abb60bf906a239af734543e14b0be8d6a1c05ff5d39559c803f55110828e949d4fd92e2af021bafc61f5c33b89342540061200013bec78beac8236eb6bc99a74b5f50b3964ee581c75eef7a6f2cafd80746c4220d6b5691364f5e29f36ba4c0297f810b6f2cfaf7221fe473634d58840520c3273e3e28a530e86bb70f31c99d434a7169992fff226260357e8f8094e771ad760d21b8fcf9d6fad432d3878ecd1b94f2b988d692084652b6047d19c06ec2aecca3bcba90654bf075517904cda2a97c614cd87d68efe7eb87ca71fc1e36a848442e49b4788b22637621097fae2260a2b0644fcb47375d3809cd6acdb146c64294f11dc888d95621df31922a9cb9479e4046bb1aab8dd2624adce7d48693dbb8229468e0bd5ba459d06227d7a4b2765532aa3c4befdde90ba711e7273608b9fb123ad2185079b52803faf3b31d920d83e2f6dcbeb9a3d52064f5d9b98587be049736e1868a0ad6fa8772bc96e0817dce913bbde6b832b75ca1c56d3d250434bed8cd086358ee781be306a60a6c3dcf8521390e3e1018421da946837f4381f7dddbafd5e6e481e0b64d4d395fce061ee522c22d18cb73bd0a1a69fa20f3720ec5d997dec42a246a8e574f5fe4555b0c84c229d1ba4b5f1c0c872ae872d327b4976485639fd8f69d1a02486499927ef6b481dfec6c501da7b56f800a69811474e0b84b5c0ea56cdf03f39f7bd8171accfe707555406e942ea4c7e8b87324bdf56f4830663a2ff0a3f4c45f4dde1136f951511955651e1c01f7db16bb8db027e1a40835e229e979502af70258d4dcab5241c1469b0200a398d47d450b9c64fe233b104c6abdc71a560fde406114444a6d29305daaed26ef541e69304684372b43ffcfbceb08129f0196acc0a20243deaf97717d85e87f057c02fa3cc57961487a0a221294b8f2821c9345e41e28733821562009c8b05a14e56fd8016d3d47605f66fddaacea202cebbc96b913121a0a31ce62972d1c68a30cc1aa09ab64fad5a2f78d8182217bf437545cd14937d7f35cca122e0443de8f1ad8f249596e209dbd878d60973da2f881614f185d840f57abdf52f1d691a8f9abcc79d7f76de8dae8983819ce58d511822a45dea994b76e5c5510a53f53916a145d9a86be0df11b97befa4fa7b647a5b1b9db42d96b445aaf7460eecc2da6bf185d2d517bfaefef677638a9c490ffa6651d99296c483da911fa00e3b8f3dc658f30f9778f958c37d3aeb7dc10ad1d10eb52527192ace1b1eab9c1d0587c174e1515d3b7fdae56072482efea8c7dc1575305cb8cc550858425605305e77df03c5eefe5bf36b5abb550be19bbf77d58b2bc5d0e4db303b43cc46e41d41857fa0ce2efd647ab3ba3b4d199d193cf89162be86fa665741634ed9a443136cf002003fe661f3e9f03b3933567ee3ac7b505616c7e8390a38a841511e3b065a4ed68e2bb2cc5991c50e574c9abdfb85aadd7ef23d6e3441f8ad49daeb6b1a9d059a533e1540de4b0163834c6e3abdcab75285c80778a83899271cfda8e5265a47bdd549051e5d7dc730ef1f3d7ee9a7d1535302e46f507faf60feb333240985cb59a7a95e18ade5aa512e4893178d8bdf2296911a28f18e40ea610f163f2d7ad4d27e7ad1d2ac45fa97185e0433726c735c7066aeb2f00fa5d2b5a7bcc209b158cd748557487d5c2e84685772fc040f8549817551ebbfd4461e454176792f54ee2ff6bf4652b6ad757f97fdea70ad789a89d3b4fe9d04aa3efbbcbe352c00fbb22f8987a6f3cb11d1311cafa31eae092abba3d39bcca2d797ba4e8cfc512dd357d4a7b3b0f15d0a594e60d2ea5dfd345dfd14972f755fbe7f807ed258c40095bcfe709c045c9703f589bff8ed17a3312edd544da0bc056f1e2288bf48e4118732d39a335f4c2a1c0e928d022b2dda9847748721a3effd753439b1a30e022b79ab16626a9f64df29f8644357b88878bcf79d8d83f83eb91846de3bbaed1d1ca94b287cbca8715b778ee42eb328341bd00cfd6e2cbd6bc7783a0bf5ff7c30244dfd747be053160f6fea13b2dd74a9918c0a27496457881cb1e971465f389e0d8499501840f1b7af05db487119767fb4966c39dad2b6372fe30deed2b99743269ce2833c4f208d55c6be3e47ac5131b8e541d5c666291c42e23db62ad9d1cd00cffcd6f36073fc193b2e0bd9534d1e6f16704da01ecfbece0de21a169fb3ac290664474020e634ae8b181924be1c4d6ca0ed50cb84bbd7ffbd17219c1a5a7621ffa701891c3434376173a43e42262faa0d0cb2cb3fc28da95b1533466d8d2409e8273991e4882a8fee7523935ca297faf2b3af6cd199ec4e206f37636c02b41a3857782d2c895729ad03c17c64de1d22bdd94650caf95652179d78fe58ac3b20d6b4cefa8256d1efac992267a07797feb4b6c2f642379edb7a4874d8db030a2195cadb6296ad13bf87b7ef8e0767b18992a8b98e31eff1feced2b12e06180010cfe8b1ffbc81474b7fae7122ea2ebc3718c3964a000a1420e634ae48803fc825594e123f569aa1ad0a068c89fad8b68f09ec8927bb78315a559f8cf20e9af20674477edc1388991548393f944e2f8a1c2bbea9966b09bfa30ee99a7b60a0ff0901fd0144ddd92e7388590ea6263f002e6357e16122e778dd3b8c509807c7c82f595f4ee88c0e65bd878d2929cb4ce2f3393d539e9f56af72ad1bfb1638ade92195e493e338d401a6471a6003fdcc55d510b5d26f71d04a6ae0576cc149c8682643592d4f3255995648e5fff273cf5e45997a2e44f74dd2f8bde2558b15577828830c23c518702afe557268ed3f7b0c18299f30cc4c88b2dd0d2aef5da75a5b14ae5900a249b62081fb9948614650054c68362cfb36f9f0934e1ec98c891dbd28c8bc103356091a7a474d2bf9e9594d01cce31d7ed420458dc9625117695d6a2606183b5e46069c734491740a054ca0dcb4934b96d3070fc363837db1ff81e210d9c471b7191a8a98a4891d2aed19bdfda19c717ceb64b75fb6df837a948ed723eecdf33d23fe91b6fc3df5f78763ab1bcb718fe339098b101f2a54ebc146110f8d3a65e7909c9da41323266e19a9c13ce43d0e3be679d49daf7ae09f4861da266fee65b132d563c46376e6647c0a90668349769ee6a3b44c5afab705f9d2491598dcfb52d52820b3df3e58962e47f8ab928d4a72f0aa351874d682452af7181b679a81504ea8ae1fe71503c8a05614e90aff50a31c2e6511faf8b9af0002762557ada0aede367f43bb86033dde827b8a4e5c7fea436ed1fece280691b26d7006bc12cd8b4fde77aae8cad9c66ae6d465e3d9cfad484cc969b38d6d61766c8f14f4d66c51006eab750e753bb86fe74889086e64ad24d1bcf4402fc17f5ae46b2013d73c8323e73cf698baa650a8ae47bf5ad18125b6a7919155ebc9b9b34369246031c231bd06ce79610be9eed895e590873090a539cbe4be9a91afad701e5f24b587c8f7b1f06056e97ecb69efaa7f87a4fd53da40e3b6439eedb4306e75320bdfee5c1d4ac101d47a39b81517459820240d4a51e2de4f1727bf9a0eb6242322e390bb87e48a4d518190d833b620f27cc16a330a2baaf84cf2d7a489995a5603c29a2e61d5bc8a928a567d133c7290466082c27306ea65fe7b9feb7283de1404c8e3228ace8eb4448f1c7ef9f48b0d4bfef0d7ba036cdebaa6c771448dc7221fba16ddf9c822e9b171fbf29178c2e429b1b26634404083d54e92e6ee9d064e1883c9e25523d4ee8450ca12b431cd30e9f6fb87490ec3e57d138fd8084287ea2a83c1dd0f94e1bebd9a38a46e7b7e58e9e79f72591cda6b79df6755b331d45c11ad28aa7306ddadd12c035f488a2004be39afdf94dbf885b87ee64cd9dcbd511039b08d1dd14bd5bab8357f18afc9d04f90c28253f96626a8f93422c1fec165069f9588974db7ac5ad7cdc22c454ae09724a0fa1aedccf9d4291ff835ae1f6010bc0bcb34e53b22e175cc0059ed2872537084d5463bb2284aff2873a23c82662fe123885f1a767818e968dca8ea08f351c9525336c36eb4a2915698e5aa7978047ee2f13ec6b7e88499d6bedab91cc826c49448ace3e32dfe68620ec807476524745679f039703d8386396d3cd22acd7ffbd565355db2cf03cbd765181332c013f9e6b2776c9c5ae0a5036bf4e5367842fd78d47dcfcbc46711cf82a85ba7d713b96f3e63ec8d8833911702b24b7cad910deb6a94b916924a2ffca10e0e60c909b02130faef1c4537e2754ea02c57757a79469e757e82368dd8b48224ef94ed24bd8cb3c279a2fd8287e948e68a883566fe24e37a3ede38b577ddd7dc3fc77d00b7e03c04f7da8110b02aedbe796e29042942a2dc8204730919c8c704ee339e05279d7043a5ca60fce10c903ee2659a8cb333ed2bb78efce80d3e058d9a0b191fe5abd7ca56fd0b06888adfbdc297781cdd296674df014f260189d39431b9652b08978b7abfb19a3e1a2e3925eafcfd183a47ee5704f1262ca2b2bf1e8e30b514cc2a3a5dc1391ec6a9bf99dbe9b5a048f66ad5f00a9e1ca43dd8a470c386ba255624aec4376bbb7cd322d0aee7804ed9245e7cd2e6864e284b94b72dde2c942b10914f7b86ec2ce839d329b7fc2cccdb0f24c18574be20c4535b0b0c4a1fb823a43d15d97b3d008a7201e4d49ec6c06c7dd722a69347f3ed389798bb21941f17ddd2a1758d4555edb9bcef81bf6a325f318f657f91e8bebc9f29ccf8c9f1ad0020b779789996bb3f9558903fb3ae05ef542b9f32439024b773ebaa1efc97c91441d11c241c4e93c725c663462e7f2a942fdc50a3c29c371bdd1b13e1fb64870aa2e3cd29dea8116eb2a423729c8ace6a5d9c5e72ac419c065bd1feb927e08b15868cc4b06c7b62a97a2203de907b009231461e3bbae4ee4bb08af82f15afbb13475a97b53fa89d7006d69d317d0c39bfdabf9e4257d63e4b53e40ceb13a6f621a1ac55dc06d177b6fcedc6e7e32005711c498992dcd4bc1bdbf1a6d423db7526d069e2325fd1b868f61754dbb71af2e7697b48a0b38453c778fe6ec30529ff03799247e8ff813e8a34563861cfb6e6c054c30fd8fea3d10ee38414c99568820823039ce58ac0487b57359d83825ca1dbebc7b64396881b741a8d815934796e2ed5416e37b207542726b2de7e7bffee33ddb33f95a5e312f23eafae94f6349006551c8206f287153aa4bee37501378463eeeedf05fe60b2dee20b828275b4ac244c896ce38f93183bd127a6f43b90402dde1e105e34060a5aee5e3e7d97e34e2106a0ec6dd1ebf7052081db6955990d6f1706720f956367c173a17a436d480a2a9b62175fcb45071b23051147762c58c3f57cb2f14133a0da0fa66b77fb4e2e1c9cdc085d18e7d5b0476361be69fea7572f03ab25cd56201ec95afffa9d63b709c6d4d2fbc435aa7df2efd79be5fe8cbbb4db4605024dcbffd2b22d193c5e57000568d246af4467050a7c8de7868a492ceaecababc6f9b0a7f2b0fc91ea5efb2881c095e6cce89188d2e3f3f61bea3ab99ad360ccc285baf30d99b2699053c30c1546aef7e49935f10f4ff2d96ccf1be26e7f1333c4e7f6c16296f35f090a11200ee333cb88f924d0e6d4314b0062db0062db746150be5d7b6f5c6b74d3ed6c2adb09c5cab66285133042c579f40cccf4313262054a0d91df0f143fd5c7f93023ef40e6d4c4cdcfd90e1e5927a840402374c318efdf47725de161f0ac04314b23a1964d6150e0db2eec017ad72d50aedb800f9a1353cd20343754d5a65c6ee61e92943a713ba5ee7340bce0944ed8f309507ffa0a21b79a86d97b174f82c889031481b4bf7fda6f352f05a77345ade76cbbdb1ef4b42bc47ff31b50d289f38a0d1751b13c8bc3766622e84ead59d75d4ae41d2abd3bc706228f6b1667a02b740d165d80a7d3799ed375c71d9c4063416add7c7211808bcec4f289e804072e4426b83ff7dd6ea6d86155c3707f1feab0fb1e61397a2e90ec53bee9e149be796711d9ec0096e422b70034ebd92932886abca48fd91dd7db7f2a8de5f081195f6604daed2bad88c0715960c4d030c05f9ee7214891942160e00af4c8c56117f717191a9aecb55b2e82d0f47e0de9e55d4dc8b8830f0e132acc3ec7425db5e4098c1385772afbb58bf86edf9511bac855dd8b1ac4ec4539336e5447fce99fd2a63f10ed9cd9f42267145f2ac01f65f3c0123bc2ff183bfc15fe0046aede9e8232ca24bef7ca3086d92d4ed47137e2e80879ad112fbf6696fcd7896a93775d15a7b2155988a8493bc0e94608b651979d3f1f15d22c2933750dd4d287eba002532c802b4c422b6ce2ea10207f448a40cfd32d724a27d98549d581c63d1be712cf97fd253723fd234f97161dd22e7ec000045e7e23a8408a4e97a6712de49dbb3bae7514a1b3bbfd8b9fdbe6d5fa95d6e126f4e8e17b04e73567b661c3b199d68815b025de2247d6c0de94aa05a9f2552c0e31c38e3cb87744a4834a2ff85818368f39095360eae9ee80496d25e6551ec1b7e616b9c8b44e76b74f31eab8d7210893f10d4f65e29a53c31a2e84b8ded0d75424aabb32b3a961c239195aa0680cce94af13e4037c199abd6470d20f891b1be6faebdbb79f57b304210e70a1f8da590d4b0df85d59c2bce5ca25817f6703960b482e7303a84a56362f4c7d3b08778230451f25e5942eb0e642f5dc96c04850e4034e6a543a61b1d150b77fd532471071de27480b4d66b9ab75f0a282e72f057e4154c505b811aa895bafde9990f322f3ea1c13c5c27a09aef8391bdd8c472dc7d2f49062b457039fc10fe5b4364df3c86605a7bdea2824362bea87b03343bdd661277633b55d7a17dbb1e17ee1f1c5f26405e417713b4425fbea37370ae2756c652e802c1e3444d3ca4e2deec0ef2003ba0aea889b723251304669ee70762568c94bba42d99427408f7527443d093fc4a82f00a310b49ccbdf20696c28e52483a522cdb83119e8f2d5c0b5295ecc9f42020d12b0da710fceb50030401f256e57f8c4f8edd7c6cdf72fad2232fb519c96752dba8dc6164dbeaf0281f7844af403aec85ad9df8dde7d7ef031f8de8c1668400db7a02fa8dbd25cb9828d584392f633111c7cb01efa564da95b6fe7147241e116281f16e15bd1ae4c3d06fa33112ad72fd432d5d3afaf25b77722d45d03b14311985fbfb9a3ec5e5e5fbc886770edda9ad928a02fa0636300485adf41f1649a0875bb957041786adfb7834d4613ebd217fc010e1eebe5423e121ebd97a120be1795892d862910c3274cdec364c5daafad955d71047a6f056d12789885edc09e1bfd7b459f80ae1842acf4e0617b97fe79ce82ab0b2b8cb47183239f144dcd6d25c6ae2b068759c130b1d659478374a7f1442d8c1bdf196cc3fb505f695d328f0cdf23f37002486726ada49fd4c40a207801bb0c0b0ecd8dc842398d89d7c054", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000d160d4006c61f6385f7902a17d4b19cc00000000000000000000000000000000523d6eb247a935a8de4a66706e303c9a01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db70000000000000000000000000000000000000000000000000000000000000013000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } + "proof": 169956, + "public_inputs": 17304, + "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.113354416,"runs":3,"total_seconds":0.340063250},{"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.513430000,"runs":1,"total_seconds":8.513430000},{"name":"ZkDecryptionAggregation","avg_seconds":51.141794500,"runs":1,"total_seconds":51.141794500},{"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.193263750,"runs":1,"total_seconds":2.193263750},{"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.289490750},{"name":"ZkVerifyShareProofs","avg_seconds":0.234150691,"runs":5,"total_seconds":1.170753457}],"operation_timings_total_seconds":394.711996540,"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/benchmarks/results_insecure/integration_summary.json b/circuits/benchmarks/results_insecure/integration_summary.json index b8820b36b..06209317f 100644 --- a/circuits/benchmarks/results_insecure/integration_summary.json +++ b/circuits/benchmarks/results_insecure/integration_summary.json @@ -8,164 +8,164 @@ "operation_timings": [ { "name": "CalculateDecryptionKey", - "avg_seconds": 0.112618042, + "avg_seconds": 0.113354416, "runs": 3, - "total_seconds": 0.337854126 + "total_seconds": 0.340063250 }, { "name": "CalculateDecryptionShare", - "avg_seconds": 0.613634805, + "avg_seconds": 0.612437277, "runs": 3, - "total_seconds": 1.840904417 + "total_seconds": 1.837311833 }, { "name": "CalculateThresholdDecryption", - "avg_seconds": 0.566882416, + "avg_seconds": 0.565879209, "runs": 1, - "total_seconds": 0.566882416 + "total_seconds": 0.565879209 }, { "name": "GenEsiSss", - "avg_seconds": 0.12575218, + "avg_seconds": 0.126440847, "runs": 3, - "total_seconds": 0.377256542 + "total_seconds": 0.379322542 }, { "name": "GenPkShareAndSkSss", - "avg_seconds": 0.229232416, + "avg_seconds": 0.230296195, "runs": 3, - "total_seconds": 0.68769725 + "total_seconds": 0.690888585 }, { "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 8.659371958, + "avg_seconds": 8.513430000, "runs": 1, - "total_seconds": 8.659371958 + "total_seconds": 8.513430000 }, { "name": "ZkDecryptionAggregation", - "avg_seconds": 51.718500333, + "avg_seconds": 51.141794500, "runs": 1, - "total_seconds": 51.718500333 + "total_seconds": 51.141794500 }, { "name": "ZkDkgAggregation", - "avg_seconds": 20.93329175, + "avg_seconds": 21.243403209, "runs": 1, - "total_seconds": 20.93329175 + "total_seconds": 21.243403209 }, { "name": "ZkDkgShareDecryption", - "avg_seconds": 1.496832486, + "avg_seconds": 1.512020374, "runs": 6, - "total_seconds": 8.980994917 + "total_seconds": 9.072122249 }, { "name": "ZkNodeDkgFold", - "avg_seconds": 63.680252444, + "avg_seconds": 64.910513486, "runs": 3, - "total_seconds": 191.040757332 + "total_seconds": 194.731540458 }, { "name": "ZkPkAggregation", - "avg_seconds": 2.149436333, + "avg_seconds": 2.193263750, "runs": 1, - "total_seconds": 2.149436333 + "total_seconds": 2.193263750 }, { "name": "ZkPkBfv", - "avg_seconds": 0.345419874, + "avg_seconds": 0.347720638, "runs": 3, - "total_seconds": 1.036259624 + "total_seconds": 1.043161916 }, { "name": "ZkPkGeneration", - "avg_seconds": 1.385717208, + "avg_seconds": 1.388650764, "runs": 3, - "total_seconds": 4.157151625 + "total_seconds": 4.165952292 }, { "name": "ZkShareComputation", - "avg_seconds": 2.745120014, + "avg_seconds": 2.774372228, "runs": 6, - "total_seconds": 16.470720084 + "total_seconds": 16.646233373 }, { "name": "ZkShareEncryption", - "avg_seconds": 2.550104026, + "avg_seconds": 2.591194588, "runs": 24, - "total_seconds": 61.202496626 + "total_seconds": 62.188670125 }, { "name": "ZkThresholdShareDecryption", - "avg_seconds": 6.228828042, + "avg_seconds": 6.166238347, "runs": 3, - "total_seconds": 18.686484126 + "total_seconds": 18.498715042 }, { "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.10385268, + "avg_seconds": 0.096496916, "runs": 3, - "total_seconds": 0.311558042 + "total_seconds": 0.289490750 }, { "name": "ZkVerifyShareProofs", - "avg_seconds": 0.240555883, + "avg_seconds": 0.234150691, "runs": 5, - "total_seconds": 1.202779416 + "total_seconds": 1.170753457 } ], - "operation_timings_total_seconds": 390.360396917, + "operation_timings_total_seconds": 394.711996540, "timings_seconds": [ { "label": "Starting trbfv actor test", - "seconds": 0e-9 + "seconds": 0E-9 }, { "label": "Setup completed", - "seconds": 3.083043708 + "seconds": 3.078788792 }, { "label": "Committee Setup Completed", - "seconds": 20.233524667 + "seconds": 20.221637667 }, { "label": "Committee Finalization Complete", - "seconds": 0.006022792 + "seconds": 0.006574583 }, { "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 309.654077459 + "seconds": 314.973630875 }, { "label": "E3Request -> PublicKeyAggregated", - "seconds": 312.282717083 + "seconds": 317.623802666 }, { "label": "Application CT Gen", - "seconds": 0.316875208 + "seconds": 0.315554541 }, { "label": "Running FHE Application", - "seconds": 0.003470791 + "seconds": 0.003503458 }, { "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 82.613328416 + "seconds": 81.660311958 }, { "label": "Entire Test", - "seconds": 418.546447792 + "seconds": 422.916649625 } ], "folded_artifacts": { "dkg_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000005427a2ed994ffc252000000000000000000000000000000000000000000000006d241db063da6026f0000000000000000000000000000000000000000000000043626bb84a690ed04000000000000000000000000000000000000000000000000000193b944fc656600000000000000000000000000000000000000000000000afab2a128f843ba2800000000000000000000000000000000000000000000000ffdd135f92e6eeb130000000000000000000000000000000000000000000000033876e4ff9051c64800000000000000000000000000000000000000000000000000022e934d408c6d00000000000000000000000000000000000000000000000d2aacac256fe3719700000000000000000000000000000000000000000000000a2b7205fe21b014c600000000000000000000000000000000000000000000000b16c840b7ebe5c0ec000000000000000000000000000000000000000000000000000160d7a729e6eb000000000000000000000000000000000000000000000004f7bd1f8c59cfcd0a00000000000000000000000000000000000000000000000c6910e92c5cf1346400000000000000000000000000000000000000000000000ce9d40d2c6e2c24a5000000000000000000000000000000000000000000000000000194c4bf6f99841c44750b98e8b12c09eb7105c405d362756f8b9f4429e44ea7c476067abed031301689e3b57f19c1daba697f74bbedf39016ca9ce34cc521368d45dfb22109a11b835b6e94ac453a3ce5898868a4cab42d8d35a417e9e3e86d00eae06dc3661d2fb487229e2fed8db71130092935e55d256a75107fa96e6a3a23ea8cc2e8336429ee252bcac0ad7229c8f25829d8c533b22df0c5e5b036013f05f795502acd8c08ebcb8f199adfff8d624b85f06f4babeac2e9c483addcd9c1d37470eab5079615d833c8c924c55f6dc49bab1af3749cb6d50a775c7a902a0fa98373fc9a3b232601603ba65283002be13c5c3f80a74675d2f98988b112a4d232f0fadc06654800c72ddd92d4f108f0a548bba472c75610544d4d0d22a3ccd05e914663e93ec72c348e6b39f9ef1df2f0b05814f5a86a80a796bc3980268972986c0db0ec76ca028774356b570c119159a3b6f4bec272a8ac2b65e3661483419b1ec82bee728f1e83b88ffdb8c2bb668a4963de3f92604a0beaf90d2940eac097c6c5b1ac8a0e187679b30d947880bc050d437d70d6a9e52caeff087dac0a627fa343b48702de2323ea003f71de4b143244e858beb7032422ec4364222fbf540117b69ed27baf0210980aa90048a07e991238d24501107ff46264444e1c1a8e3fb6cf85dd12d70d25e7fed1ea3cc18e8987d7df2ee1378d55b79aefcfecc16d9a3bdbca3d4c2503ae270dc55614c28019386899d2b1473c7ae4eb1fa98cf8646d224f5ea13cb22527c61737f9958b027b06fc66f27a797ea8c10d3002dbd12864e769d8051555195d4c402d26641b7c5d862396c5a6618d1e54462b620f45f116a524a5d130331b49e391d5adbf8ddfabecf18dafd472c03caca3e7489467b8f826ef9bc7b16a2b7de443763d311542d0f7ba75c4ac61c381aa883a56153dac0233b24c2b1b542ea08ab907ab674a9440074d582141a10963f2c3fbb8407e1b8218f1f6123df80838bbc699ed896d4b98b7ce92e6ace001189eef52436a0a7ed249e60142278827e4642ef254526283a7d17777ea4a89a5ecf102c3a81957fd56a4864dbf836a1fb504bd8e7e3916867f161e9b397905e7877f6d58f9e1a48a103b7096d5d79f03f301693b397e9169cf2e06bf9daebfb630ab2e8cbe7461797dd834306b712d1c4f5696801f0196f91ed965829065f4591b95902c7a034e211d26e41b1703550b2693c21a8b711573b4ff021892366ad8968a2104c9485c11e17f8cab541abb01b6fea8d27f6e153c10765b27ca4120b6eeac0045bb8d9d4e8a3cb105601c442305eec51c2887e768d4d51ffc323225deeb1b540e4d0e6b12c82424bc7644a9179487f9375e9ef9a421f7a709413ad73725381ff8676636116843dfe97c01a71e4c2af2c1fcc6a3835296d729383f9d1b5dd21656f44256d4a8355187bb79e824f8c064c34d9c653b30673b3f5a2ad6365ede55baa021eaead52c7c417f46ea1420f848dec5ee8e2e4496dcb32445beda9d72b0e9dc505ac5471cba4826b7212ffd8668e33524493f12cad4c336f9425e27e07af458f680aa7556d693d2429b23516e5a2e20f6ab4f59fed1e3033f0b712a76bf4d235c54ede97fe9a387caba10a43e1509f0e6c163f67dd09015a90ff48ee7543424f51229b33de99d428a3004ec53f1ff45a533b356cd647d2a7f6339da8f8a4f6e0c097ee7fe6ee7fa62a606eed939e91067552413b42233ac95f4ad56ca26d7247199d2b53abbbecd602f2df89683d1b224587ea81d8ca66defea2c003aa77c467cb0d51543a04e401bf410126537a3e3feef528a4f94de279b833418f7215d57c1cb2c47fdc166d9ee24170d246422ba15c5d96f5a7c34c81d23f7a555acf5f67229eb9e94c4f1694db2134a751fa6280f3cd09e75a041662a8cc0b266315e25a354a58b8541ac7189421b4a57c8f1daee946f60d115ae4430dac1bc818c4f9ec244b60cc6e659835e6a0b99b702cefacc3b601622faff354765deee0c9e0f62aab394ac7310238e7096298cd07fa6af7ba58089902eb54381c6da51b2b225fba65ae99a1c1d724e9e9001a6809bab48ee95c1baee7ae05f02204d143994e977100e0450fb9eb073dafc26d1adf32f6f143dce3846a965acf99f8921d68c5553c893ace6d30b7f42e34a07fa85456da9930a7ebb651e2e8720b7a1e452dac68c32f9729bbe8ed10d66b20494a03bdc16679631aa96a19a495f0445b5e2341c843207e24b29453364742f0f4c48913f038624283aad35cfea5961543f74258d9547ccf86df266330284f6211282a6d25eb8a2edbd5bb21a1fc8dc5f807f78d8c0cd831b1e8ef8afcf80fd1bf9d7779d73958e3cbf0164cb6eb9512ced3961dbd8e5600ad7db18adc5b2a206c1902f40e0c388e7b08ea147a9028094e402b8fb5bc2c6d030609ffa3cf97c208fb4414d508f49e22ae7baf57479677627d470e20807302ead225aec2f3e24014327ed1cb3ef9662b9dfe2cf4fa39ea77b63d10cf93b6de3cb38925c8f027f171595a5b16a1300ef2a4848dec9867e0d36f27a6797d046eba20b60fdd214622084b25b40c55414595d095b4d3d8be36242911213c161e33b2c24d8d9482e8f227c7d47327840dd14a790d202f5b1b4d7876ebffbd5c8b59bc1c5e72d1562ef24befc8f005177e891a2e17a029ac9415338824b678ab58e44eeb753713f8b251611cf41d1bb18471d26aeeb7701ea36aa93d2a102224cc1052588f394c88d290e929cba7d61959b04d5b033d54766aee7185d338b31678b3fedc741a666b23d27c74ff7b2593eac40a9945a60e73c7cf7975d440d239b00c7cad347d824513f1c8fbddb5f754c292178eca5548bee9d306b6fee243801bdbcafa9d8453d3a48109d15ca04cac8ab7b3c93562b158b4bab0cc051eca421ab5c8fc6d4a60a58501a38feb0a4732c38abd876e0b5baf947dfb0f3ca987b736ca8330e4d42c1e05426d3b08ea5091826e4a32c5c4ef1e2b6a9e80a5b4762e887959e79cca1e94cc00c0336da0f387a4fe57fe353552ba8ede9e31f5b85c3658e5c43ce251e2ddbf50e166c29985ee8f3613c648146814b09dde9e9cd78f2a81ea3f73408280a5cbb160197a8b62af1814b64d002058947d07591550cfd4bad5169df7b8779d5a2a5242bc8ec5e8a35ece688fc7c6ad09509eb4350eff0e98aecc9ed266aefcaf6ef0075f5b3bc59150d2992162620938acb03376fb7aa0ce3e2283beedcc0db26942c77d59950f3e76c86a79a13c07e45161cf812d1d393b734d62dc4c215a39edd00c16adfce0a0ca7efb9dff0002d60aadc0c6e4b86ed6814853857f4f7bde5a50a37e4862b3de19096cd6b48b6d609dfc6583362d3fcaad9a79a8238c75d2a6402c0ba6956325bbf412d787000b9bfd8b0db6e6ff78e9ecb7f38845ba6818a1904f9b2405aa88bb415a0b51e300fb260e55bec6f1d916acc63148eddf10666a41fee9f325e36e4d3b1789ad36d266056cc53b36576ba294a1b4b6f904a3b80cf2e6fd2b7d3e95625ef16d9d8eee6dd9661c916bb2277baccbbfc182c9c74c70e0c68efba43cd58dbdad21bc9ed58ef09ed0b18022da877828574c9221779f70e04c9f0fcf05d526b9bf79729b673f32796a0f1eecc0a508e91b925086f337ef115fe2d0c1fa9e9691d622b086685986060ed2b79d0c0a6c54a299cb32c17c7c3165055ebdb2d8271325bfef3add0482b3497130d398804d69e66f889b4bac75c0e14534a4b5b12ac8dc61ce4165cf84241c203655292bc6a7c6c54f1641e765c2b27f416297deb2474a50b7d28a1ea3d805fc577fce06e286160663d75e63d8125ea040c1382b541d5d52777c5beb5fa5b366ebecfafad3e36f5b2963644c17113be9ee83fb92303a34b5163ac1709d698c952a0113c99259706eb0b0cb57aca238fda315a015caa19b67a67b2e9df8ce8694f7796511e8392a2df820bac8b2c2193e4ad15a489fdd141eebfd24e917835fd8fd958aac6eae3299acf418ee39a1cc2183980e4c09b31b4f975e587e473c09c065f5051fe7f31f1b361973bd87719f484814aed4ad2e448b3bc99671ccc1af77900e747dc0e90b6e93594de697a2cfcaac6c33ecf598072c0ac193cad8dfb027fcce0fa7c3ea275ede47e683dde0db3c4ae111bcc1ab458db20bff8c117d02239a6c1c6ec4d21a0af673ae1a42a0044a0159c1e23e463bd6b60704fff68443fee73d6214c714aaa793ccc339e7b06ca9127af4c41ac7312a1d2fd7b30099fc072d91b7456936b4e974cc99c5bcd143fe8395aa5dd14b8be8948265e711bd9ad6cae938273f44cd9a0b840650b462d5503a9e2070daab4e933e1de683a011201dde145987e277dcae8114d49056700e602421111c15fbed4728c3180666697e330351fbbf2395331ad4f72ce09dc21548ecdd678ceb9f2ba17bfc607eaa32f4838ac14c2ef52a0b18da26db7d52b1c5f3ec56fe48e45a0ddd8c12bd719e8ea4031d577a75b2064500d819de9f1af2d361f1382269adb6e42c5a0d07dcd2128e04acca36489d23e55e414067c606107e59f635e1d99a543c25faea760060cf93fb1558aea51ec8a1c4aa1ca86b34f08d7d71a093f5675c68c9d3d1ca68aba3975a315ad7d67914bdfb3e26eea736205a89305449ac7c3bdae9e169e7c50f962aaeea83d1c1aced16c84933cbafe5615ba2d0ff96a87f4a2d97f3778a1c19e12e0ee350f19bf37ac514c76c40a08da23161861f90aab73b9979b4d0aaf65a3c4bf61c066f67b5fddcfaaabb8293514193ab141d32a18e5ea09cb0864bc3e59ff46e85c9ec33684b54ca11b0711e7482505e86f2ed799ecd477ea8dc74c6dbb6349959cfa6133aa298bf97a16aabd2a1f1e93d03538ec2adf140f50ca5384ac1f51e9f0b91eb651af8566dacf83af7226be48e368ef21d810e272f1fd4abbea3ea7c0429032aec9d08cf31ef0445e2111480087e31fc4eee991df405ff8ef658acad605b93399f19d1d5251539e3c2a21682d0d46c9c290bf4025885c8b829a2ea48895c9de693b3d2f6215a164a33d2f91db9f73cf3343b53df247340a1dc2069eadea19cda684e22757864add16770049442b7451bc67fbf4d12ba4ef3b7fbf1034264151012cd735ec2135ca3aa8288bfbfb1fdd9dfe27d71d70335b9adb8a6211c9f6d954513f8581779ea50fcb271bf6e3d84927583aa787c969b8d4b7ea0e231de07b45b5625d0bce77b6fad327ced1eee216fb07103e815fa058dad6c9d633e1f828b83e38326c7535cba62b138eda58a22bf1ddf2447c536ffba078bae7208ed320e6f135e5c5cc8c69627d15bc7c974ed7bbd33ea0b38bc55dd005091f2e898aa0c9b355296c30dadf99972c6bc70999b9c727c1fc77ab5309d28ad835bac6ac74be8f8344c7309b1e9c31175f922b6a8c45b7ca7b23408d302925d94684700cefd959bc20d0bed14482bd0acebaed6ce3274d682f8189b88856c62616a84742619f43ac61e497d81ad02021a1116915fd972410f0f607cdf9b76ab6077239c7ec8d88127b02c1f99204df063d6fdce454305b7b6636918275793027def492ebced6e0f8c948a81cca82822efb3bfd155fc8b11c87ef4155977f9908b28913cba20e3ee547cfdbac774be30caa4409aba47a47898bfd62d126668b276cd7130e95bff2cfaf9b1d5f67566f18dd55e21a22ddd14363c0498ad2e29f9aaad8da7aaf2364f01455a8c66d51e21523a86aa8bb228ce37b3ab8ed758979805dea717810e201a83ea886658d2c192f062751fa9ad00f0c3abcf402901185f98b50b5830b479c5b03d2a7769d86dc29d7bbf1f14e3fdd96aea564df38a58a9d7aa7b4b950d4ff9b2baeb7fd12063b006f43d735123875aaaf324dee5942e7708afeeb616323a7824d01104ba950b01d2ed69d9ad865b23ac688494c461747ed3838abe69809d68c00d871a022e621121c45c397d5979a64469373ba2043c21d986ec9ff69d9a86e32e38005da9edb2c5536a42b6bea1f4a3104ad6d6f55926d7334d6111f664e66c1e1334dc0a3be19a0778e139674813524c28e93384f0a0307dd2e2d2906a34d513898c715b4900e816426eefb96196c5a24242463fa9eef879a84ddffda7408a10e2902c4049605086f7544cc27fafb47d23935c796b5a1c46a06665550d819875c4aed25f0e50ef7341e1fd6eec58cb9237d2a62bf3b686f4cf2210abe367284c7a91516fe6d29b0e24f81cf79efa422e642037734f33388ac21292cd636b3246b96f6c751a720bc206b63f51a1195d32b64932d61305bde740cad4a138649cc9f7e2b2f8edf2841d3b2a2a2469e0b59b584167915eef7da32455deb4f92268cc496a07be60f18bfed970eecba7e9d43415155e844ed87864ae0682050fff6343741b463be402367528d2a6631371c5f22995dd9878859be81b9dbb2cdff59b776f5ea6c6ba22bca5677b5ae9a4aa25c1c9fc3fbab0c9a40317bf22fb8627fb4fc7e858a3e2c11a231622b389f74934afb33b26403aa579863f0977e230c7df65088dc5ff97a1cf772260cc16803416ba1d76edda186979cf5a9725842585d7c77b2cd4fb9f61ce9c6ff056e8f8762fc2f355d6136ffcd49627ebb4e58cd711c56843b5cb4ed1e072d092a7931a57ebcad4e57e14df027ed837b8b3b0903b1996fc6a475faa11621dc49c44e03e9a9f0bba70b42a96d88258eaba4e175ff50c6903e9f30203c283a925a4ec8c62f69aa72d80be89e515fa5e2adf17db2deee86dc00622e1ef113db1b3c33a9f435db2e3081de6bf777aab27c72d645f4c8e73802dd112b9f0e0e7b95ed1a03cb6e0e20f121127f574b123b9dbd2f4744d8644deff40a3dfc132740bc41fd107465ebe01cf00e3786dfc16c78e10ac7f8bd29c0334b570f88b61ee6fa5a292a8b801a8a05d813b85a4eeb3361c6cb386bc50400af38bf92123028adf840f8368e8bf6ab9cc310541dd627c914094e2089037a5bda512edd26a50b4f6dfbe4d6e19290da4a183f24197658c8d3fc02f402119a98e09dd42f0ddf1b72fb646b22deddd2aecc3c747d1d7c96569a1df62f750dc0d5bb8d0fc62c481468e9310d37822c963f1b9fe1f6b8b2efa4610b304e75e4601e7dbafdacb5321f5479c51b931401e10016d89c54724fdaf6c9988d459af9aef1d0fb0d04debd03e3ec9228fc16a48055b55f810a72bf30bb0ee1ebb6abed769a5445cee071be226716851281d8c0e03bd5edd44b7cf510d58b29afd4e77fe5518816821697d929fb08d77798c06a1f0c77b9af14abbf79ee2c307fdad2d0530f71c98fb626710adb5f48de96d1e4f458d5f38607d927bd6c0d5e1cd68dea421d66160fd89c7f1adbaa32b1144f80bc89de0523293f2fd10208b780e7a7a33ef5ddb5f2a328a310ce90537ba9fd0a1c20278e249843ad0185cd639081f8cf9777e73ba2ff4d752ad5c9b89624cf8a16e00127527f0dc88e8a2f30416b0a6f1c68c3ee1aaab4510718c1fec2a1ec3d3ed44704b8eab63afa6b7578d2b23cc024f7e786de234e75299bbabd2698ccfe09dc0dad1a8da8bcf1ff56d70486014281749a253cb2fa7c137c1fb8940817085d81cfe10712b6f1078841a069d11bb8c9ae8e1b7c96203f2de4b8f460aaa29ce8f214867c80cf5ee01dcf3fffbb0a4aba5ea357b43f778714a0e6e5a88402cdd15661caa96c1eff838c9d110fa41adaba2f16909424660f0be037c920a9e0896b96e4741378d9ada7f661dda4cc59dcbe0baf905f9aac1419fae7dbaa67c5b716cbc798e91633cd644ab51f6e392f0bbb9bd6dfc9d821820c275da0cef71d86108839a62dc17ce62fd013148037bb8f9eaff1f0df7cf7530c8347b646518a1a7e26ee77d072bb8164efd58fc67089fd81ef778fa25fe7a51668183d73affec12f2693f517416737be621f39d2a984f9868edf2e95cb9352221569c1a4cd8f00c168304962564b834276d5947a965bf3e3d778e9e998166e0027d26e139a8227737bf2dce3d4e184aa193e1debfa354dfa66de175e154938151827ed49d4a988020c96575b60bea8ac4542e3bb50c0c57a0eee83fd16f7d32e672c90fef162349138a32a8bcae4a2669819e95b99bda0004035c4c334b51904ac5a9ffe9c5ab955c17488597794288943a09aedc771cb2f740dfa925f3e38053bd23ebb807e754a9ab811da53f0c68d20ecb5d3d331ea411368f8ffe2ab381aee0421ef6029ac9ac06635b28f168e3d1cd9bc601d267df1668540eb03c2dc0398867cb6806e214e25ff07128cd65f75b2323f3cc3f6eb8a7e94a9cb1108a017379a285b003cb790b2d4dff7a83e3d28eaf53dce39914ac03fbb07901048351dcfa0a14f4913c280050aef6c8e25c029d4fc33831cc24ac8e8ae8c493ea1b315d9775d74f63bceb9115c181bd86adb585afcafa2d58dc63f1888e9136d386c18fedee4e37578c6abf32b93ccc064c3d0c55b887f9ece1a5f61cc1baa4828250139ddbdb992639a3905c04a310c10cd849f4ac8c3e08f8e512020d2bf23e6121f13db7c7a5f65c7a649082c61cd76f89f5b1ba5b1e572292211f7158f6ecf0c0b236fd68f1aeb4edc78171301f5e357294017927d40b67718633ab3f811829226d0de241de2be29895744767634378213c4862b2fdda53169beb443e8ef4fe703a649cfbf629bf554bc9d08b385743a33b028490276378715d982178988ec3f2694c5511979e058d6c86375a7b0d21e83d7042f8db4f17944a69ebe1c6ed16119f897fa5c9dc86ef23d663dd1be79c6fbdf4b46cea563fc13dca98646e3188520574ec994ddb3cd019cbd75e32400bc771893ad2500aed3757ede87fc38f67322c1e5bf85d8d9ad22da0eb8848d24379062dcfe171eec7106efbcbcf443b469115dc9485e989c5c0b6a9d702223c31c6b21a4ff831956237f5acd75453b75de185fa18fe9e5d87c0fe2539a78471e4bc7c9b78f4e41c860034271bdc511e9340e6d4571ba8ac6c2dfa6955ece1cb4a2dd96936d0fc8edfef05b5de1c3fef37826d0deb099f334f4719c62afcf06014047bb6e10b2b9336de07e0e066af47d231a2d56d393ec4903d2fc4c825c8bcbd3a522f9bb481bc98da6b3e96579fe3ea418273226404c9d86da57c891ea33bb0e82de738c4448469c991208eaa8e6356a2ffe1e79cf6f65af2f4222c67d87c6b443042c68d96c5ce069d61f4adfcc64ff08a70b8796c0ee517cab57ce2e162f9cba588799be9da90cf7fd1bb45aeb8fd309d4a162c74ecff399cfcbcd49d9afdbd724f1bc54329bbb990d83d389517d3d0c75ee82c1c9c75f7a17741ab6b188b4f38dfa6588b420bbdf98126ddd1ecd3425ce7a7553791aea59e971433bb351687e9185aedeb781652bab5438a9a8cc4202fbd9974c864aab5e69e064b01f44e1bb6673df3625825dbace20d18c3ee78f2d86bb769cd098c86e06cced60e53ecb10ca0e13ab5167753ab91a914084f7661e6a65ea09e5453e01ab423d42df91f603a6e47a1494566e96673eca55c5084909f7bcc007abcb20f422158ffca594e2cc602627cc88358af3311e11d23cc01c0e11ad4f3c25d939bbcec101c4dd2abbb48a37d928f365470f236a8d04c8cca82cac8332f3bfe006ded5bcefc66d761ffee35d3dd45ca488bf9a79a50eac2cde238e7d32bf059ae8e207b92dee1f3070615903aaa66afbab7d23f29de47c98c92ea084e8b097cfa9e12f356c94f4659126c1cc30a0cd6f52d470bccb9409d0800b46a37632a11516d15c56d7ea870bd9315cb81f6f902fdd0f7455a18af14830300ae1171718a4908d16538cb5c929c10b5a727b6ab0217d1b46221a76caac7f270aa5cf851cd7525d2049b214996a569486eb2ad01e890577c09a053fa67ee005e680498e30fd0e439f746a1d29bf91acad7ab03924042c527c19f68bd2ccd4053aa5773a58afcc7bb36109f17dc2e6fb1f4294896e7a9b402c4f466c667c382b2bb4df3fcab103f8df94a63cb710528c76c9e1379df5f296d75c3786c260c22b20846ebe0c3619f895f3ba0a1b7bc477d9b4bf450d9a7b00b26bb6e70d5da72f0457710db196a06ddd3b8b62e303c08f8a585ef70359b86d465f1f8bb73c3a1453b2daeb92287fd42102f7b5ca5a560ecd106e6b408fc797ebe163f467deca04044bee8ffb8ea417ad3a7b8bc359c67d841f6692db1ed79c5cc25b02fbe5c811049dd1ed0b5c352924cc3e8b2664222db798b7c6ef923e3adecf989ac62da42e473e269d9a7e2ddda23885573aa476d1afda16c29257d76fff097954052a85224cb8ab3616616ea1d52f321bad55930577e6ab58121149bb36063657f971ce0b83fbd09fb46d34aab513832fb7d81595136826e0de9599cc98657f1d6359e40c81d682d1e20b22d514470247fec7077b1fd7b3ca5911460955f64cafcd8d4a216d2b05bb88f67e6f156ebeed80b25bf9f125270b1365fefff4115098c5f65b0d829f8cebb57a7c7805002abdb074d207969974ef7812823e5ddb938674ff801c4c9bd4b72d9ecedc4b702eca90b62c905312783803e17ec510729d5d38567c0bcfd30a7b2bd45188bece4463dee5444ed24fe4adb2867371e565e1e3aab628107ac1e12d020be22c848a8977118ccb2d92977372dc6a0f8978106acdaea6e0292ccd626a2b1eb06d169e505a6341c6422e52996eea6de39408db47c67984951182df115ccbb65be8b0301291ac4636d6b98d7c3035798b2ff513b53eb82d921f51cd3e97d6505b90ce990ba4a8d916514621c24ce6eb09a26e98074dfbcf0d101b640d3bcd25aa75d8ada4e20e0c0f2d17ee3a33357cfde8a0b8b8379c182809af3ab78bbe6a8b39ac620a3d4ead2f251e7b9333a63e76a21c7e9066b5276d2e721ddb3c69086779960a78b08cc0519bec15b1f8d7ef2a926725d64b9506c7103a2eab75c13fda0a345e32ca698fb8102614e8ebf680c9f9960855fc0429782504ba67ece853767898b85700b7d1189a27147c6587b2b86eae6ec6da78f133245dc01d446f00bda83401f3d195feb523784eeb6bf0027fea97c94cad1ebf602e70b69ccb05ef3f394b022da4c0c4ed5028d7a7f343f9182e15a987e0ec1c50040cf0d57aad81a2157b45cd0ec5d2dd8051f456b9115e8530f3a9bfaef8870f1bfb114cc5a6f7d7806ce6e2e3786860cb10c75af4a4a0aad6393f34a3827cab13e3015f656c040f86d0e3ba53d855399e6ed9ef007638844fa76e303f6d2f6a0d5b89bf97f7451df6ce4f9e4fe6d349dccca1c4e2ca2f48a77c26a6f592bb48032ba8271657f1a3ad8b826c428125c38d1fe7d141a63e3db8ebc52732eeac300f12d7c6ea95217873f3ac2b99e61278ef379fd78337854d81f8bc895536173a14d4e18eb945dfa4f676efffab03565d899a7b1ba4851d92fd47736ef9418cf20ca268683fa15eb94f399f72b5636db0e5d3b7d4b6a59df4be6d0d75e76914ee13a60b249096345edc3ad5177de2efb8995e918e139ef99e29f23ae4b7bce10802c1cdf46d78c354977a380d0fa181edd7d51563ecb010db5df96263d1ddf3bf268b3eccb7f8d9eb3083c8e1bb21e7cbfbb06346fe2290d97ddd852e37ab4bb001eff9c43143c8288f88cad33bf291abe41167d311972febf2c7ad57590a758e157de6987ba42a59f96c5266fedd56085d6272920d0ffe46e64341dc47b19dc223e96bc01786d7753ab35afecc5c719f29b08c93f1f98438801f0b296a8b362628ca14b9f24007d51941ec228ece5b04f88ee6955f3554c1e0d936826f51534c150a500f9966f975fa8197fd781e0f93b3a5e792c0847f89cc963aafdbb0c16524db2b339cb153fb23db491993979f23ddeaed0c826c75f07221fccf46d6dda51ed9cd3d43e52cf4a59b96751b05e17fe19548485ae19e3fe23cf2f82d5b983d021b37cd76bdbd878bc8cd49b41cfd4e6c2ebb9070693373b64489dad9f9e6162b9928fce70ea5670ee265aab85fa86ee46897a09dcf76e6709c5ef1603274cc2ab37d949d26e56a0adfa75b6d2aad41c04e42aeeaaedeb2986f1a6cc878f447142967bb077b3ea9964792b1039a815c65db489e3225d4755f038bbd411c7dde1c4e6d270d649f842f7d8c78b3d003adda353cf06f25349d5222a6d0bcaedc5707bf54b2b666cf489a7638216e665e71734a16b023a4858320b0d96908285a5217c636140c22faacd97964a4dfc0c4fa41a54a2bb0d2425c10d7481ab27aaabf2e4cd612c34e777a6b2f5ebabf1f5d818d863a60a49733d24cd8c6b72aa805111f4206a3fb56955b441691771e4d29b7928079b01f07627d34c1beb5d23ff47b03498b3bbc2c94cdb18e4f2104f17f22bf2df403d2d347641a00c521017f9a091107ce97ab1939826d8edc70b104242ca2e2dbf5be43d8250026af929be9a3a92ddf88e2978dbac1b91090215454a1930379686ce96094b198e60c8eb8503ae71bb9c966874f964f97ae919c73254d318538874ef4702705de9428f981f139e904d374daff608b58e8a5f1026ab3e9a35379f7d6cd25281b0d9abe41e9f282240538ffe266a4dcb85002fd4d40e90d28b876d48f1baf813451d59ab4eedc91161ca555aa3f878db7814e35ef7a7e4706d21e0320564f6143a86aa7106041724a2aa71fb51c91ae4959815b2cbe3dc7c71cd0bb03433c6823137e30db9a620a800f1f78f876e37e85ec1c4fdf63c9c4a4a40ab94a607cc6c14a7d170b02e604af0d4cb08d305c09d6bb29e33cb97702eea06b87c89ae44ac7598448b274c52f3d049ec959106eec78613838a837e97d7f494869b6e7c086402619f747726ebba2148d92d292b8e20d765c381a83e4eb794a9af15c7e9d4ede347e284cbdb8178d190f3948b3c9b05b4702ea8c5ef529ca26d7b840baa7dd780567bcdb7a7c61dd2658d4f94bd234a1e09f7995c40a0e593c60f0851c991e502e6de2ff483a8e1f2ab0eaff54012fc4eb1f4524d2033ead54f5a315a2d9020acdf6731ede50305d04f8ca370fc0ed91324ba43e8aaa1d2e098ef20c67bf68ac69cae3de5de993631f578b274bb8a9daffc9a181500548f4c8e16547801f672dd3a6ed780ec370eb27c17e9676566917b0b24f93ec005391c4f338bdf8758576cc3b3c6b241447a0039ba76ac1c9eb52effe3d4e70d1617d1875c07a44b25b2d80da267b3740ad970c47e1fec9b5fccdac40cace2beefce18826a23eb4d07b1a009db25b2dea6ffb2cc470e2419f2a4aff07a198c843f52cc57bee5df7e0e8464fa9a29991bd12a82f97f8c2e8f0f5873298313e53f17cee2ce90f3a2550048950457ffe145ac6881f13a6ff6a1e3b5a1dcb59ff1d47ec6ced8ddc6ab5162a7dc676f84ef5c941612c2f67b984fd81ca3ef6b93a27d756c0f4ff1940fb0d7c7b826986dc522b03f92f8b25340ce0daceb2d9c4983373cf53cd3b043ecf5bf73b077493baea47271019cdb0e7c28dd135685ef319d75dfa25836928b6a71957bc9ba46f556bf0c4bc2cac526ec3fed7ba2b11d0836923284d9cf1a609015ffbe4ad1c89a6c7b8dfcd1ba78d832d3022fada7ffbbe6ef7da3f392be43ba377eb0c3feab9ad7db607ea0001034bcdd15b5c1a8adaca877dc27cbfe0d8ca99ebf58b7b89eccd991e29a51cf85dcafdd2007bf477a35fc6490b78283111cf8bf3e151fbc322564aee8bca195f622ed44463ad949c61f8a93a84a929def6441975065cf2eb887b4836273320a4962d42109b44362597d44dd3b82ef0af798471c1ffff7477395c90ab26ce189563c13de09b25cb3f81479a9c2e1002556a5bad221159f45ac01a9b84e288299799819099c129b9a742fabeaddbc2189b11ba3b82fbb8938f91a6172961ae08496b69a2bc73af9e02c15ebd3b7ab7db1454538d62fa63441868e20534a8c71009f1122d822c5db1c104df011122519afe8d55dd35d6c252223c0a40f07ae9194f5f635230c06a259eee27b05b23f82dbbced798e7e3f0e6a8470428ea3bd216e8e2969fb632e3a2a0ee5bfbff6c2b82bce702db04a5f0a4df8de7f1707a772fbddc0d35dbab0332e92ae3d6349d637173d90a226e2b145eb355ee8360d1e52223cba36140d531408c6fbd49bd4154572eba6b5b4cd18177807eea3c5970c01121f6d014c65d2a73098dccfd5d920876e0e99b19b273aefa48044caf127e75163e939f3e4eaafc3bf8c2524061ed5ea365e24a2250816f27ae08755eb307da0bbcc3c0983d953f919b522041fefee59cd9d96b853cf6b8767a5573d0c4d6ad305cba385b6d235e0d8fdea59ec6a6948338ac431535414aa2919f75dd70d0f21064ada32aabacec88cb1a92a99fc1c6c7f2ff808757bdb56a95ae11b44456aa055273083e3fa43d816390c962185dfc50c16faf010b74e39f011533cea6b3ce0a0b15992fcff180e4909661d502b9e7d4a735e9101b708510b01f834cc08245275b28a79dc54f19189b8756decbb519dd4d2b7dec71868dea469705f8d6be061c72ff0fb451e6d860f8d694b46ba87d209128e1c4f7e32722af1679e7baa8370f999db4c627b535f5b2d953573eae7b97814e87e3ebfb0bcec82c18e28b2f5d18bfe49ea065a686dd34e63cbc364235da3676ce4226cf9cb05ad91d477f309a26f4477dd2e778c4b50f1f725590e7f75fe9e46cfd1a139f0a9f130eb07809362b8ae3c1fabbf3f657831fa0a0cfac6f3405b9380712d4ad885344a9c69b9b02", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000e0b1a7da3e047845b1d8ff775f052add00000000000000000000000000000000ba6cc6067d9aebd22aa647499868d26111521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + "proof_hex": "0x00000000000000000000000000000000000000000000000ec9ef698121a5c39000000000000000000000000000000000000000000000000e41f942fce4d490eb00000000000000000000000000000000000000000000000195fb4f10129d1d78000000000000000000000000000000000000000000000000000096d82d22f57a000000000000000000000000000000000000000000000009954ce731edb6dd3c00000000000000000000000000000000000000000000000c3bd0ec9dd55b67f0000000000000000000000000000000000000000000000003728adde765d0e1be00000000000000000000000000000000000000000000000000027b59548a2ec1000000000000000000000000000000000000000000000005347bf3da34ea673100000000000000000000000000000000000000000000000d629407ed2ca5889600000000000000000000000000000000000000000000000d9316749580d8335d0000000000000000000000000000000000000000000000000000451a30b1de5100000000000000000000000000000000000000000000000c16172d7eecd0619500000000000000000000000000000000000000000000000adf01248f5b8c94e200000000000000000000000000000000000000000000000859ad32a233d1424d0000000000000000000000000000000000000000000000000001e8827d8f4dd91d0bf0d7bc3141a4dd134caf21d77bafd25e2801bae69edef7950fe1c8cf0e1c05fd9ce40ef8073d0e9167a762a736f4facc1815f0d8163bfd93cc017eca42fc0fc496da80095322fc75820967dda64c09ac96275567f11e014fe874e35f4c1b1c6dbbd70d14d031ccf593c50dddda968df0f26bcd235d9bfd78d7fadcf5dc4c082c8650c77afa5be438815c3d42ea08aee11084cdfa6acd4d46d109ea7bc42a2042981382bef951d645feb6dc93d46518f03db584013c33209e0cfd8165016716707faef9290e1f2d5456366066051d4a5b1ed85c9b56b2f3a767c8898ae08819fa1d753808dda784dc6ae81116c5984b2e3f7cd8de51a83adb0afa9b3ca6432a1fae1883236c06bc4f084263a4df184700231ae641d20277f6e7626fc026c52dc7e925492285c0754912271f4b382112f5a0cf23229fa89f6b1a66c5caacd114260f3e6d1f10af300e1843b16413df24e32bbad69247f23fbd482469a12cc72b45ef9117ab888d998d42e1d4cdcdd8fadf0f4c0615a748d25a80b35c7b14a408ca6b5c9be88eb9f015340260f7ac3d4b702fa29ea583c12aedde88a29295d507a99c79637f9e6e508b6e2cee68bcf5a4d55b996096e9958ed14b64160422fc272fac98173a2b2fe6c548b7cfec9b9936a29adbdd9d16250226e77c797b945a1d98a9dcf2a7dba90f9326de6dbc89a442570e30f26606652826872b79016e3c26d7da27f6178484f07514cda21d42c6bcf42d2a77bc8b4f36e5f2c6d29d063b120b810cc1c1f2e69ab126edf280494d9a9c94507eec1f37d5b66b71bc5a23e6128dbd86ba4a243a9a0cacf72100f9d43b1e8a3dd25349e062e3b29e4ca4db2f23d46ec0a0321161ea953620e0d0a400bd522610d2bf3a1d1f4e03408280a4eb01f2c96de382a1d4032e1a201afc51eae9795b8f229e48abd893c57eb94f7a512a65956c2aa748f1afb6f0946d8ccb60fc874da7ff480e945a506337c8e046320e2aa705ddc585323194799593e9e5d359fd88a17a9f65187cf0210940e1659a29563411bd539cb6be00d056442c9e842afbbe75f44280307f3666287e0f21662f49976c867a62e4c2c3a24b11ffcc6b043a772418bcff0b68f9946b9f9c73b42fef2ffd192d0fb4e9601d72ebb81253217d473ab13967c8cf0673928c6187201c9b3f5d4aa22cee646950cdcda3da589539df5f1aacb1d2725dc60344ee5d9728abef864a21f93aeb0016e4b9a9b0968fbdab4e601b8f88bfb18de531d9f78019d8b1ab781050c8fcc4062bcf4bce74df6ce2861013af9fca034ca763399e3010091a5bf71607501a0d61e999341708104b83ca21703421c2e400192a1f21bd0c1c673628d372ec09c56d244a04560e265c971cc505e31b30fdce03472507dd0610bf3ab5bbb3bb5d6bdc5ca8cbd82afb058dd7baf634f759379524cf80e7162ab4036dda4b58ad3781247af235afafd6e919f90dbb72ac2226f2f86bc2df1c008f6ff95a49c89d880cb6af606a4ddf7e9ccf097b1c4b946a187f90555d393a11a1d909a57fdfb30a8410180cdb113ccf6fdb0a7fae7281c76e49c0580bc3160d77aa58dbde5ed1d51f402a0fce68badbd10cbe75c128fbcf22cf53e2709eab0834a0e5526c91fbc6b15c1b5bfead10b8e6d0ab3171b2d8a106c3e4fd026a7a01e840edb4e03449356fbcccdffca3a45a59afd472fc980d83fc1504e952a2790013006ba1e5427ac816f25adae5fff4194e25659862be62b0d43b8961bc224f11ccf3bd16f35538162c261e65048d9e4e4cc70286af0eac700570096141331b0be19c23a4117d1bfadafc509612d2d774c13810ecc791068dcda29d75f889001ab891ba8fe14dd2d11f05b422d7484fae0de35e39b6b97451cb4dc12cf5694d0f8de2e3ee7e5aaae8cfe3633efb2d01b1cee79b870603ddf78d339a31238439000a22a5dde7d7abf2e0f72fc8806d086d73f98f3202b454db461aae23c7a8a80b51da3d101f0721ac69e21cf9cade88a606ddc8f8b4f9c0d80f12e5fe750ae6200cded97041e21930cbdad1c0903302950346e6a3861935a0208dcb71ad1c5a0d627447005be70389df3998f482d674f40ce14592c3407f2eb19213c436835406590d2867f21c316ad81b9fcaa64fb2356cc4023c282b8fcfca989770b2ad551a20ce681a2dbcbefa6c08c847e87bc3a307e36cdb61cddeed0f464b27e0fc86290793fbb62b364038dd10ae3b7f13b856e9b0b427cfedd8211248b16e392f4f04b4d3c00630fe829bd6e54d4e1eacce1c37cdd9c6ba7bde51e8ae7591df978729a97138329eee41221d97413dfb35c1178d301098d2972ef49c9d3d37ce8ba92142609dce818d3dc272faef7a9d6739c9a2329b3ae8b741d1cebe2d482868630fced55ac41271ea90925f69eaf5f506bf915a7b60c7b1cea5a8814f3dfb2daa2712c16feeed6b5bd73bb8437f0b33a2b2a09557f97649370eb9c15edffb6cfc0d451a3b62f6a1582ef86137cf115d72346fedcfdd09cf2057ba0ff3d50cb3c5045c72890b5ebd82af87b0a950c802d4f8372fb67d9cba3103e4c3c21b59e887164a23970149af17c8074496fee140c989307fef339b5846f4fff60ea4771b6f1b627b1c3c5e6534abf8e1ceca32fb8d2f7256135e3a2a905a65c7c4900a5eba009da53c08a8264c2d95a8e9e0e66129704ed450139855b738a57f2f8e4838501d88faf34c798100dcceaa6ba31a844742bb57d646b8403f5d9bb8f55f0c3626150d5018b6695de63c4c6346253f531674c1f6083cfdba94c3e7281a0de65c7220681300cc6b94580371624a9a778201db61e5d292c9984c040d8a2bdaf01f6528196094e94b726e83f8527b0860b11ba1cf78a2c1c5c72f85a57958a2689f361e2e867bdd4a28e7ba5be4fae95e5e66d44caabb18509df95c5e8d04060619da0302378d130924320c43337c2b9f71339261401d608ea2acbcd25fcf47f7959001676330b04e5e7df983712fd2ca5159de2b2d2e9dc0a3b1e6dc022fd21daa9b26f45730dd7b23fa1299408d88269521da1225e7e8fc4aac0b0fa30375a218de0d97ca096e738e1c890cb57b3c4fe078a1251f33d24e52695ca0fe31b93c04060eeead6540e060ac7adb14f6719ced88e631fc70ba50f8ca8e5be17c0d8716f924e7bdb0daab87e3b6901720eae49e7ed230828c54c597dbc024797cd024429604a1ce139df61c37b81d6326f880a4077129e81a08a92be8bbb7375805a0b71409c44c049dfb0093e06392154a6742f353159620f2e94f83e3dd9a069a8416fe1376e6fd487a4766af44cef2eab670e550c8fe51df3424de3c3e215f9a7e8a3106073d4c626b7a496930e0e59a8898fe992a41604af683ccb5a29e6e7840a4260c5ffdfb5a3699688ba4808fe5eb9bc44bca0ff5979693272d9a1c362b9843ae28c6fc1f51cd98c5c2da4242c9f650d5c267e513d8208a40df4b7dddd14e789009a8274b38ce0c3c928b4927b9548b283ad304295c0598dfdb0ba0ca3c96b5d8094f587b973d1425a3746abc0daae7480fda4a2bbb2ddabaf50c5770f7291a950edcd9863a06cf513bd75c3616ada207dff99bc316f83d7d1156c2ef24c9e96c11c9337457ea5b6c6baddc66818eee1decd61ffd15b5cb5cb7b5a9eed72a4c5406b832c1d539589d402db21f7b1ccbcd9c5272379ea7438bea5f9d8f2a5e5739057112baf67d6070c3f347e349e0fb06cf579b9a8f289ce45cfe74635d5253980a126858ab7d12980496c06a7b7c6a95e7f6e4ecfa2957c77da9748cefc08d8618875439e9e2168e7e05a1cb42af517def29aab9753a568f465bc9e1378028090e0e94f05fa827a7590b317f23227190c6b01454f6acc256311dcb76b3033fa7064fed92f22742923d2974e504ce93711614bf5c7edc00c0a3b8cc38c014317704e2d1dce3b2a9fafa881c14edc39a36bc545db0887c9475f53a7ef5eb32f90b1e87d8f50fa529f18eaabb7b84ec1868f9eacd9927431091cde1044ffd78afca2563fa2c2917e76b9aeec95bbf2c2da274934c839ff2466e449ef0aabb5a444c296c050c8c44966025cdb1b8fc46bbb208c3a372758c440f4e46069ac0ff2e5c1e9e0bfae61ed536008d9fbfe946f189eb66f0395dd51247f73f0c18167f594b1437ee2fd4a565041f523b89e595c81ae1f54d72f42f538b8dde9b8989aa532b0942d496965da36cb461dae9ddf7110e2440295e1cc589240f54c0007585df172c1e3279e05a2d616620ce42c5e923b8b41fae97c732cf92c1efe28d039d673d1acf1670740612698be6eec0d7794d28c2215f2539f181a4547e7a027cda29682b0271548cda6b0d33b33defcea7fbe8208e11f22fd00409920c98e408dc84911f3be215ee39f6d6cf11eb97f9a1552dea6e333a850eda7a5865e446d39451a91ebe76a8e4991a190efb68b81ed74f6b9fbe5a9440f278e006bf4a050caf74ed29ce83fa8130bba02eb6d8502546641804143dffadaae81a48455c942abaa3f92ea5dd70c89b99cfaf9039646212b32dbc450fc6c155ae7e54e69feafa93bd480278aff364713a492223b70d38acbe5b15791a0391e5a90259d10af8d93ce96a0f07d35efd224a4afea83eacb73c5cb3fc61a66b75551deb76c9db8f207f0e9f1377dd7b8cd97d7411b11fc2152d21d6e04591ca623d4ab2e5f1164d6a95896f2212ecb096f1e21d1492b2469bbe96ffcfdf77502b7338122562cb390c30752f0d1459977331a5b65fa24818be9c9a0b01e5d3652ce97ab8eda935aa40935ddb0d7e334f37a1e1e9a24428e5773c01db1495d15f23b41f95113f3e7cbb2b64b30998b4e26a623cced0c6c246c2421d7a354fe1cf10e574ecdfe20887bdeec0ff2e9f7d479d603603479d950bc5b65cdec41f175c50e12fc2f10d513693da8cba09be3be16b55a0b63a2f6c83cdde050b3e979a19aa88efca5544d356591fed7122e8585a390e81386ec788812fc378251cbcca61186f818d764c5e3ac2ff651e2b1b84df02745bcd24956724035b2fcbb13b09e6641ed6980ac44f5a780fb875286cdec284ea9959449fd7a72aea4d800927d9c1bcaf02e28181b58536990db6030360c87597c8ab02eedae23a1cbff1dd4d17dfd9b3ff44f5bf139b63ce10f51705255ba9ad0425f329897bd4cff19afe6ba0a77107f212b1b00a40861af4df1cdcc6665a294a4d1f020d3ce73fe5094fe5384dcfa10677dbd29fd8669af5e223ebe866f0809bcc298055e9f9dc847dbd9722f94c8e7eede324ec82cda66491026afe9a61d8567e3b887aeebb05593a672d6835b574a4b3ca4d4f3cf5e99f282b55d6f90dd66f2417dcf5b93b85d5f63d7a88bf7e11851e7f811c920eb9833a149fb762c1330b97e0a9dcfa44f1fead5d80dd39c321635bc63534971f52453825ed4c0a060d6a421b5601c8ab40eb8aaddb993a06c759e51841e94547742aa923eaf9bb18f29a544fae4f5349e1df1dfb84399b8b21a746775de962d5d2f0461273e8c8a1b50db92053f02695053619ab0ee4b7d36c46d489b9f6e3c5a4d15f256a827b4c38c9be76860729c4627c31ec725e74ec35b29b6fd3f225793903ff04932416ab27e43368a89ab6f380a76196c1a282e65a5496624a3f8451d1d5392390fa9bb3aa8dc264a0cf6d92f276ec585abd770d5e5a515c20c2e8127dba660403190dbb1cf5a965b95d9cb997676fe763ebc5504b4f33a9e67aab1b3017800b399001da114a5958d9c0997270fa455ca54c44328abb04ad1d38eb1ee5d92e1d848d673315acc09e92178b36c42336cba10b15e42ab30533aee5d6207a23fa06cca99cbe2e1e5ba0a4c48a20df0ba9f7bdc6e4865d24e657de9f8e74772ec323f94d7faadb7609d70896ad03b3873e7312de54ce84a0d25977a186e5d36fdd04826858efc95f0e725c1a0170f90126e28218127098fccc6f8e93bbc5dc94a42d93b194a264619494f32d24c66f7d74266ea517c85acadcf79d983a00c58f9d3021f4345b0d3bb028c12bb97fd98ccd2085891e183adc9b7bcf30d7f7a16b8f128c35858ad8953b3e8c1112282650e531e94f151f3bc9e8bd392184b82919d518584dfd8ec5ccb00e67b92d22da2594b81e7338ea8e17fbc5a366a20b28d6b2157ffc8ec16bb4a79738cc95b88255eaba014f04d4f8ef8a1b3f837c6cc2f26f2deda5477f7f3681eefea8bda62548d5d8aa1ee1283ec16f53e4f3df239a12f403dc3aa82aaaaf4fce71f168a3c9bd12138fef7f2a75bb250a84e5b3dd2b69771a3ec4561abdd4dbcbbf63daeb7ee1fbfaf8a2e14c8e63fdb33e8ca97fbd8dde2c3a64777200326b95cfaca821f536be5e7103037b42e6db4b8468a2518439cf17b05607a124eb2ddf7ea1ef3d0926610263cec35870f64d736cb576bec97ff02e4f1b460d3218f62b5fed7ef4d5734311f0d96370538c1e0d2a3456203a4da1149b20f6f624c1a5eb14f3b6e23dc5703e6a0639c4f4295421fafaf44b43f3a3295139e08f2740ee9928e679c015b16e378e0459daecc0e1289990dc3c43d57d2ce7adf15e252b64728e4eb4474e069291886becc5e3a7f3f7a23971914acab50999b3d1b1247a99b4d476acf564ec63978549733d80e5c89f29ed55df452acd2572934bd1fed33bf7a38dc15eec7a58aa24266f3269d64789f092e7f2226084058d43881ee24895a4ab41d6df42c1fc1adcbd8dd364a4685fa6f785590c98ec2e859091087d624d7953bc8286b9e75152cd225c2ded34a72de786d003f53ed305d0e276b613bf274ddf18d47d8671e1cc36053ab1043b7bedc8c8f73a51eaac1a92867b56d997ada05774fea9de9098df706bd127ae6ead366acae1e70df2100403c7eb2d5b35a5025f1cb28d1d658ce39dca67b38430c42a1f6476135514420ad054a7fb7658417a95ca357ee3371d5243796fcd6ef5d3e756fb3fd388f4561fca53dfc96f682133bb48a7b345c1111cc9af0516b67195b851222a50bd0f9e010c08f4218d579c9e5bf4d34984f3e67b76f59a09f36c0845b5fb7c7de8f516034ed58397993debe05f425882b5356afcd858bd95f36d21d30a8cbe81b5a433006813e0a75c02e31ce51ee8e4bf5e9db6b9bb4da9e8ff7cd7035a2fe9b29c1603574f3c14bf0969e84c55940ce0507d8ff40a14d3a3807c98aa06d20addb2961a35842b5d31175d70914bbac65f30541fd7939d8476d332481bc935db7686b40d10706636fe09879dd9c78b5d966f85346262cec45b9ca953bfce5754a973482191b13b36cdb489638d09c696f9789ca8ce03bd3154563073d0152b5f7938652e7e872589bf6af3ba89d6a643a1d628c88b615b3f9ee38a0415dbdd0fc7f4ae07c69d33bc6e4ff89d9c048f3040bfb2818a0327b6241aa91277ec984a54b52c2c4134c0f6765334c4b9550c24e031cb1a0b08ecca7e8acc43d3da56e06dd3222fbd07388306b44e8ee96acc822500c3bea60f0ec5ddc469b085f040ad7435af19c3b6c8c584c648f81a3c1532af5bfaa4d9f7800b1ac53726a4209ac340d8f00db7bf471cc277d7bdaba3b289cedb5d29e8b1a0de6933389bd97b838b49b3a02d99764a4165f2cfa8a42dfc3eb407ea37372abc442d49513d1a248ca15837d7200c1eeba96c6dcf63aff0c43912249be0de697e8216ec83c7014b3890379a361c60c5cab97ebcd3a91c63be61161d5776fe04a0d4b7794547d58434f22b1d2e01df5c3496479fb742fdb383182a0f82806f034a73d11ddb26c29a113f41bcca286191a6537e9e2465081026bd75f3d8004fd720005839243a73636179bd8eaa16f74b44f5598ebad7ad0ad6edf8518371752effbbb0d9158cde2adfef8aff80028a14d27017e83282e4a58d3aa87aa413c0d1b455efaf476696989d446625c4104a26b0d94c9660c31cc3fc65e3c4df1ece6207b1411b125cb84e3557d226731a7a20b3f7a5882a6ab25d92c8599da8a7d0f6d4e6639b834d5a1ad0369d2f45127cb1c3222a85abae4598e65930dd8d0470cbf40d2041afed6884134142f1d0249afceffd4a23865d3bd073b67004cd5390222d061931e6053e354a0ff761cd258c36a285bc13906e3a4fed7c07298421178922838ec126cf41c69ae2c9b92619ab4f4d85454d3a1194383446a73388cca8df303e88c55dd395632e9fd30d18216f670538dba3684ccfec7bb4635b2a8d22834b0bfa34b5b20bf49939ec1ef717f399c44a938dd4db05855c2468e3575d93d4ef586d759e572930b84e7ee1dc1fb18a2977fca5896ac9a9b11ed5a95856de6b617a0144867fed36d3e7e8ff73043dcc0f89c5417055cc4c742a0077d552bbe0621f5d8149a525eb2950d8c1d720c6f766e5e8939ca092bee411d46b2a87156db048d1466f97ec9418860b4f810ecf93c938c1bb700075d96d0ee33df8c0f1ed946d36339440a75ceeaf5e4877057a7f59931eb2dfed2c25cf8c8c1304104f31fecd29ae0bcd927db54898e4a111ffb3116d03b9acd3afab6184dde59ca077660c0371dfda948a8002dfbd058706933c4b4d9908ad759dc904e51946b76a5867436f005a2d19ce71996a520d6a195103257550171b731843e416bef558014c1695d5088df4724f7066e138d47711c8063c1316481f8d0bb12717fb97bac786a86b1f9a5671c563afe2f130da2d02b8a37417251824e2daa6c59778cec9ebf043455e64c8b0a90b89404b069721049b53b68565413c38334d8317b7e3ca57e03a76bc32da196655c744b65ce208094564dbdf75b7dd1ceed6e687cb6c33e991132225d05961f3b1286294dae60f2b5e916bcb4c5109e9cd706422eb5ae2ae1593dd462be1d3254de039b624537e24d695f9479cc9509dc1abcfd35053bbb1469148ab8b69535b6a24a7d32406ae264e670d49bea15796d45e6cbd3328d629ae50a7cd9e039e3aec8370985a2b1a0398016bf94ed111f901d936a82c85399301e266d506529a4e8cb8a156b512552524763d17dadf27538ba2a18779c5641f2ceef4e00f4acf96b96c3d223dbcf11a67e18fc6ee529f1d0c5ce12bca9dd5f1f8e4c3f9b05f097338fc517e55a08f0a29ef8caf7b0b3d6f8abc1f930a7367a40c58f1af0c9452f8e866c1be94cb1e10b8f25d5b84e601da02555eb3834514a9707ec8d17ce1b9f1fafa25fe8721152e6b294d391e83a358bfd180718df442d2341f7924da88c1f18773266dc326451aa57efabefbd9a5961c1b4bf59fc240a3b2fb88d10c8006e46433dbb521eb6b2af8e7808c4683783411ba2f259273a45714d3e431138dc1565c9a50d639956a080b639dec74c42cbf5a38ca79e6cdb2757e5bcd9fe18a421c48ee9088f87eeb22168ee57508a1a494ae8ce0b902f3d37dc799eaf69da83b70ff3b07aae4ba412502ee2bafa3e54e281a448e024b7a0cedf8b25ce751d0638d046671ffaba8f910955ba929a4aae6df051c7b89a70aefdb331f87191304d438517708a89ee55c276dd93baabb317b3c30e8406fc70f26ea09a18f9c2dc4b527a3171011dda53822ffe71aa39d9c61bb0d01668c7cb0791a26cf5a84a4e764cda8eb47af8ef0641e0407eb0363e9de0ddd095f007b7cfb3641afd06c355972e0c9d2b491a903410cdc1ce070fd0bc607a3e4db6c41bb13f8b2d2328013c608975b32ededbd44ed29b5ed7618cd1be2ce97d9c43e76b88e226cbf4cfb2c51fb6b84218336b8fd4c0d5bb3240dc9d465fe6463f8e0262479813fb30c001ff332babe68b455fb72710009d1841d10bbe3ddbcaaec39838218778cf0c5674d469f8a4efd30adad662f0fe5ae0ddb452d32153500cc27bd4412b66ec45e6fb646f17dd1fc81f7c73b842b4857e3052aee438d2c763e1ebbcbba7b7069bb6d823394f03cfa195a39f18d1658a78504eae6f75cc451f170758d7113541ccb415867d0b58beb96be221048077e7d29008740cb1b48a6def944e0459b21b3e1004004c9e179001389ccad591c603f2ddc61c9656d59c818d56b711e30bd2297566cb37b89a11022e3cdd6f71c229e7447a4c86292624b5b740fcd8dbc73bc1e46a015a4b3f98cdae09ae00b177767121241cb9ac8c79fc93278c12b96bb8e083aa11cb0498a73b1f5d37afe06705224f43e0f7d3b35a77d70b56660856e0d3cc857bfadad79fb9197b3d3cc242055656db132f6f02fda406a393c7dee9114efdce2457f5184cdb24b2005e200a08b50a61efe929580ec871a4c6e67ba64688becde11c210bdf24a631795da19735508bd0e3ca0ee697304516416446a044f7f727064d56488e407f86c1a490eed9a8e618a11a9409d570d687d160070f22b4f932fc86dd65fd5ccb3881d2f01bc09e3a463bf73d783f5213559fb74d00def06547a0012f787e490ef71ccd713ca081f1f774eac7ca1f375d5b8c3770ecb120d3206976c6e5e1d8cf53721fa152d8da9139720432fa6c991d33df3494cb3e28618cb99dd6b393307a243d59205d725f4a775f611ab67eaaf9eceb852924330b7a99b4a858938940ed5caf7be09bf3379a4987cad24280f7ee03cf166da0198732d8471be63261149d23f3c5f1d71e74558ee93e3bd8be503154ff8804309ee04aa82f19edee7d2d5abb503192ac2ae60b489fc6eab39860fbe9fea9b2b9dcd75c580cdda69e19d3a9f06f95115949d534059557fcc3647457dd0a51b99b5830c29713cc3fa6bec10a1cfcfae1da83004079637147a159c63f632eab4893aacde0642cc2d5a082f00c7f51c37210cb6c1e85b9bb7c30b9b6859ad823a2824244bb938d5700a34a82d02dd87300027febe85e66c00ad1f8ae3f4cb8705de7a307935a9b09c5f83b9cd6830182a2d5684ed85666bc9f259d55854b97bc7af5e5b0bab4fcbb50845cdf6120109591e0bb326e7bb1528a02dceafc2b2a35d747e1461c2790654cf1c28bca13446991f833ad980f9d49fe652de455e78b898729071dc77f08cc04e76d289fa594e8711e19a4c38ad676a582ee9143e91a782f326c22f2d1eeb069b7a97718275c67a1fdfd4d009c75184369d892b1c6d514d985221cb5206c7f68d3688562c543c782245aa3c0a6b70079f244538bb89270199b59ddde6820a5cb1af06b39adeaa131a84734c2ebbbfc7ec2fa961f6be227fa23f238bfc7a21b3d80b3a3886b492800cf524c843bb2e4015995a1d2fa9ec624c9d4df6754fbc0a05f474ccd63dc4a0012dd1099334568939b2be801baede610cffa142af1b033817c60d1976f6c15412d70ecc05267712f7c38d569db6ce29c2fa140e469353dfa14874aa9dfc60650e8e7bae67d9f7ba8c84d65bd13a40b609f899edc8feb8b8b1ac1579330a75bf2fe6869bcf2328f5e4e33b94bc6f2beaa92831115b6d467cefc8a4dfcefa31ac12d7f722e74af1cc316f18c08809f55c997be302ec77ed498890fff92f7a94581621fafa36ee7a1d850b6bb85afe296408dcc86c13ab680b9e29deabc49621fc25906893f898794b11682c468b198d9d5bc6ba3337d3b9aeb6c6a5e6745d3a31144d3023681f47ea2d17736cf5cbf1114af0039e9f8c5ab1a643a8b6a109189816a8492490080135728a91d1645c79e51ab15a41f354e0652fec1176cb03430f08623df28499bb77f02e5b1123e82b6756e026a4059fd0d3cf55fd8bfc9deb3210b0423c3a9db9283f97f4d76bfcb8eafb2e108a2e66cdf599b0c8e3c2bf6c6922b0a2b81f915c9d5f0434e366e54d6a4106ea972f53abe8ff8ffe0ad1eba1430cde400af6ebdc11f2e4e2c3d206ecbc19adf11e2adeff7fa31d917b55ea2bf00cb57c0a65f17ebb620679c389f99ade39a20505d0c6808a6cb7a0fd593766df227df8dbfa84c24339f15354eca15dd767e20c29554da57c66b67707105e845b09a7217d628f1340c453cd388b1da17efa05fa276f6e097eafa5a3b74f99cd2e114f6355af8024d3b8ddd9b99d73f8c889ca9c64b8e0ed9d77a69f99fea56a660d686198b2405606c4bcd45e6e727b19beeb31c8b47b0022f7e47c9d8ece2b6f2ec0a6dcb76b443a39b13338920905390616eace86842070b5c9426e93e324e7272ea108f073748438c0af8d393b38edf9d52d21fa46766722c9289692be58062a5ef5ff8e5849bb343ba9a6266e53b7153b1bce64d139cebc408ab095f5757025b74f6c065b749ccf62bc64a21cca2a22d79cbfc59c513e29976922d085470a01274a09f67c7da32d829b46f8a042a53d3eb56350003bb9c5a13400fbd579bb112e3a0b477808a9d8cd63e3e34a09a6c2af7d443f0fbe7e1a3d28602877c5761867c1c0abc4931171e3bac1608f5cbdc9381d0963de0bd82571511d3ddd658e2a9728021432e7cb66eeda6d4fe87b4675e176d187b219dd77ade78b86318f461d20abce28731e07ddcba90a7701997eca0991f165923a36456ef637fd79bbeb05adb1bc23f502c3bfeee6605b6d3138409c87bbc364179ec25da46d7ba11fc92caf8b9a23397d2e46e9a1b86a90c7c99d613f1e3c085c6b83809ace5f1d6d532fd1c522723decda9bec99793be2dc9684a48fb3567cf2605419f85d8f009dd728169b519f260b527b25a970daeeec072cc98fdc23d4d7730578cc6174d7f8c40619800139210dc43a7639768d87a358423cd79442ad129bdb6adf00d0b219b20f9baa3f2d6ea3fe67d1adefef449ee22a10f904f3bc7abacd99005e61c338f80406fe2c1933d5ef2bfe1ee8ed9b1427951d5ce603bee9834b4ccb297e2acb20259a5cf41a686e3587f4f895b59eb0d527a1f95735f7bb29bb9d59c94a48ee403026ee06550afc53fd9fb85a8410a74a7fc03ff4df74982ac8cd2a8d5d1e7c0315f9438d3e31c0e36cb921fafe6e806f6728654181e51db4cfe13f9d36342bc806e818863c7e1e5efc1c5168be37f22fc77ee04a6589a85175f77bbc9c283f332393aa9c6b3dbdef56323f063b8fd6b835f921f5dc403f4722f0829474431ac012c288f568c10edfd731dd5597e1538b90cc7b7ff1fe00b720d0cb5a021ddc470bb7842a2fa779ed68d676dba9ea7a0736f469b4b0e6d232fda6ac2e910168b5293b84640a7cbf59198c750df56fd995b3eaded30053ed3c0154f1785e4cacde1af52eb9d77775b1d329ede55e6ac3c444790ed3cef6b0716efe2114906cc0622618ad8668e123de4801b43702661449265d930b426e51a24f18135a1e5258d429b2a9858a26bb3983fcec97e10769ff4b430d6cdcd35b12d5802199d5ec720727dd5bad0298f5a7ea86723e3a66074c24fb52baa95a92322d9b664f99464c1c104e0de8fe760ddc5b596a42802c6d711e028d4bf57fe361effee928e831f964157caa124877aa387111135ff422bf789f61ae66a3fbe10bb39d5d0d1383320205795fc08bc6f17224289bedb10920ecceabd9f94f3eada029e345836ec25f0d01603105878447d2d67597e5863401eed65d49a187f4aa2fcb510bea0c4cd5bb2e8cc9181a570eec275b55e8b03f856d06395953c5c0bdd93c918738f629e3b90372621f2309f9d913fb456f0c8d20b0d7906f0bf59a8d8e2edd27b7d08be9fd26ef9327e0607d7f1d49c2ae3c4ed6bbf50c656577fd1fc7078821ab2a5324b40de3d2b4e84e33e7978b9ee0aa7495eea94da74589fe1148d56c03f20ea3f2c71329a770903472464d7014f35b3107d1a7d0c8c712265fed9e04431af9683ed5033ffbb3cb1af11674d42845284118d87134424e057506f42cbdd50c9f8ea0fb2936d4297ba69dc7a4353625866a89e1288a75b11af9957d992b2a4e8e11846a25b4e1685f8c89b6e18e05192aad92ff0f8b7959650b7ef7c88435213bfac0c41b3610ca52adfba126d2993368ee3ba12771f382a8db73989bb89b8f0a2afd4e190eb1950b78049808b2cf3bbe30a7a577027e638b64c4a24c610220774cb83924db815cb811bc9d986c4e8635112d135517580b0b3a489184f1d6317697c1f02d5097e64657cf18e301454bf94c1f9fabba3bf2dcc67ca5e1cd0d84c7ce935e1a7bf1fd835d3f46c115d3500b3a71f60bdd42d8aa66e1c43ac536a5f3095e942cfa2e99494fdeb5b586811c476ed6e5b2cd4205c149725592f00c043a7eab8924ec7ef41f85d65c9c07c46f4e95ffa683d2ad865fb6a1da4be35e301d0aef812ca0e15d566bd5366fcd105dd32b7c8abcafa8cb6ad02c158664c34c8cdea2cb2ca0d2b718149bacc1cc8ea8a14a6dacaf52f5e8797659d8f2f4e7028910071f10c993da8089e7ab97984622bb7386c1d4bc50b386e32a10616402dc89ad01dc1cb758476fcdd35cd2fb578af3ca05604cbd47db4d07750d818b4dcbd2505d6d11cae381f1b23ec2f40da6277b5d0b6b7f4c31c839f0b9fae4e29fdcd958170528b072d20601928376357f3b43851eabfe95a972d6176ed7f96f5e721f23ea7a07ed33dbc77fa93a56eaaac2753e601ec8c951b1562f1c31998301b03a387cec2eca7ff9ba994eb3a57d8a589ce7e237bfffc7681ca1fa3dcfc559c80152bb1c0b7d8ee21748ca807f20c6fbdf72c3a66ea6a830a5928eb049852c2460e5f7502dad794cbb2b673ebe853c482df5aa18c14e9e674451cd881f7e83c219fc8612073d1cd815ba18d7aec4d84a3ff2bcd2522b3c799b336602c4bbbe22cc9d489213a88661e6638d7d3cb1cc7202b66a7ff096ca0384610e757215062f5a8463480090ddf6487c809e131b04d314f631f7b624021e3affea8775d156bd7ed296fa", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e94000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000005dcc024becbeff142d393c7e24ebb4140000000000000000000000000000000022e67462ecbabe72cbabd2ff92be537a11521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" }, "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000312a8f853a46ad8950000000000000000000000000000000000000000000000053fdd2acddf75b42f00000000000000000000000000000000000000000000000a88f17af3c3c0039d00000000000000000000000000000000000000000000000000026b4e21c49c4800000000000000000000000000000000000000000000000571c6bbb50a7e605c000000000000000000000000000000000000000000000003b06b8a62b6ff439f00000000000000000000000000000000000000000000000a406e0f7ace42494b00000000000000000000000000000000000000000000000000015bb9eb3fb55e0000000000000000000000000000000000000000000000035c33191f6a014d4e00000000000000000000000000000000000000000000000a5917facff53230a5000000000000000000000000000000000000000000000002d448fff5591c30080000000000000000000000000000000000000000000000000001ffd7a66840c000000000000000000000000000000000000000000000000c66bd6a7b1539fd3c00000000000000000000000000000000000000000000000dd4af2f894c7e385300000000000000000000000000000000000000000000000e26358010edd2eaf000000000000000000000000000000000000000000000000000022a7fac5859ca1e90cd04e5ffcbf6c71ec61cc2ae9aab902a0fb4fa1773218ef831a0189c6da40a2c1e5e6ce201cae653ddc10d41aafa0588a10422bfafb8b8066a06b93407a61b227938b91228349304ad087e3d68c3d7a714d940dd4997a26526f1c918c6b815ea5438f1eede873183871d8b8c952ab7f9f00796500b1755102cceb1e6bd382fc67f3e3aa8b0ec912d44ac31892b842bec6fbcf674e23ca2b9307969fbe4fa07cf015cf4dbca9e7846328f330620ad4ae74e79fc87a32c4131c9e7d26c8b7e28dbb5c90ed466e09507a61716a6e6bd59181b7f763247bea444384584c7e8ac16ed88d759c0a39196fbdc6aec044721b849e6d056c91763a0b314269a21900720e74a900444e7c42e1ce70f42152840178f5559f63332a1a1746b525d722ff708f0a1f1d0036607cd983ffac29224e1bfa05de2d02d104e326c69c2ab424513114e4656a4766d85168d36a5b5e79d4d90ef46bf807a0eb197624f9d8651c81f1b0b59a52f0be0ba78094fb9582fcb805272039845feb7e26cc9e9c087bd703b1ed8bea6a0bd7ca560425e6591091564fff151c04b13378c39e838dc213143312d1d9f68eae024b52f8a4f6d0dc5d76ed64cf4a729b167ce62e890e958d627d2040d277ac803e4a7f08e5c0d3701eea9aee8914f7f04ba961b821989101de7a1293b8a47c69fc120f3e3ab1cf75315573176f1d5b32bd0a09465750e907c44f52577abc81703d64918eb22fca332c2d603ca98e6a5d65a1642f1fca0daba682b17cf76d30f2edf93d920ddc87d762cf310860f5e9608ec4d95af5aa9baa1a21c05e6a870151085fee2176ac709101912ba9d278862321caf91711a3941718a0c158b14e0e8c865fa45e95d81853d4c83af3f0b4b78186bd7490d80a324458717004a2806da584dec525c3508a608d94756ec875a0e15f211442f66779e051eb42c18ad0290edf383ff8e26ed6a9b96c88392dd147ba40d10f2d0bb266afe142714d493ee3fe57858083b8f9577bfcef33e4ea30be6d15fdfbdfc7c2d8523e4281ab3ddbb039eedd28b26bafe5c301504bf8f88da4d31e16001015a1e8a936c522c1c16662644707a9281992245cc203feb75341d1ea27c107520bf897172392a2f3fdd1afc8380086e6e595e446ca7553bc180728e55e34883d6760a5c231be21275fe254ba19f443b55b8e3adc094b7621b3900ed90cd61e7d133962d376c5b1628ba6aa5871fc7516cc3d741a9e0049f5cc184d437698b9a927fd743c645040936659eb8c72d35c7f1cf495075859fad3d4f6759cf1140ed45b857c8cfb41d2f415d1a4d4f6889e087d2ab17210241ecf2cc412ae9df24b203671991221c3e2a789b7d451a6777017f127393022d91e84711de1de9238eca4563ff08b8cb0f102eb0a3fc969da0fa75118372fe3015d37562f8c81b78d6091eeb9e32dd12a3263867b6644f83e00fa935478d2c647af646e163dfb2625077015e26fcdbc6d92aee4c39efffe577684b7d4818bcf57b27b258181ff5d2848b4077596f08c1840c93ae83cdefe78b6d582c3e70f7e59ca0cf331cae4ca0affe6184faaf82175810f7d1c63432177c4663167eb7536ce9ab826ab2ead6603e14474b5ecf3272e2158e12d17e51a1dbb03a79ce50bf70217135909bcf3ab0b0afbaaf7000b28f220cc085c8d33dbc859feda8bc2b6c4d03e81da737cc4a2a9ebd1157fd0c1ac1441d59a883ac5c259fc58ead531c2aaa234f10705b143284571a1514758b320f320662c97914023d0a21937713b7647ee807f86f5485301664da7fbe46cec1c88508893afdc844fb0c17fca13df00d9b2a3fa45cf69f2a2998ec8ef69de37148d31f8f9897c860d4e8409a355cb2a3ac6be0f55425f63d36d06600131a725e2132251c31489f93761226e30128cbb603a5d2f17e79ed0526712d5d2374cb4e5c1829d39c2feb8523e91e59088be6659ab5fe024e3a0b306c71ba0b5703c679ffb60e1973ace159a6bd73e363677a031a72457a911e7658ce7e517e296c65447009085b6e26addfc88d54519d2a5ba252e9cd6acfa8a295f87b83852661e5f37df0010b99091191d06a3bc905e4a920844d8fd6a986c0597a0b5d5a0af207e5584202feac48d7bf02d573aea8e2b65ee62a6968306cf18c52debd8e0e593f524de409ebdd0bf4c65668ab0659ea1284e7de44bc8e90431f312ff585fd1b2268d75f1511db68a4ced686f244e270b39093fb6580d618f3480b39883eb06925b303131f818c6bbfe7e0451552b1cc718fe6bf25b8f65722c329edb45b0fc8f86005711ff86bc611a91891204f1c1352c04e467816eb3d637693bc6fa479e765cd489407f8870aac11f80c69313f7c704ca99a1b24fae889f75b295af8d6f6c0b25a51218d84f23a1cb256a464e3b3e5ac64efcb47336060f675f73ea28d5eb13516e61148dfce44e232e91a6b2618e1d4305148d4dd456948f69a3f1e843ce27e0ae52cdf83515af53b8acf3dbe95a8edf66280f9794c5249c97fbb40db6a4fa4d61a2d4e021844b6151ba105874ed159d40c0acfdd6976ccab55750aa969557f426a09c240388e16bbb4d639fde88676a158260e2b454120fff932f6b671803dd81f033f680bd5f9e1947fa409c9e1326e92baa11f275fff4bbff430cfc61c3bbd0e02d150af4dd072cf96f577bd2b682656b8ac75411275b2f8f34aa9d2cee70fd700ad5deec2a22d0b8ce8982f4243a2b4de625d84cad7efae743dc2bd761fcdba0acc6750c675c049239eee472efbf67e866e67062c7f3f5580a7bab50f57bb822dc5b8c730dbf443e0615d8fb7406ab1a86d2293963bfa6768a516a532e1f6b315d9bab1937267265bed04cd54aa50c02347a8c7b1b3eb58ad24d334c6aa1a0e03ab474b2f328742012f847fec43377d8f80c2f6a21413f06d34213e54bf889402c7310861009735660804b2b42281e40025b2582e39989b218e1fba5dee27d90017221db795dc096eb273c1d9e3fa208808a2b9dd8d7a15d187ee74ea0ad84124c8bb7652ad81d2c6cd7dc156626a083c41f88d34131d6a857afa04108b771620bcb7e37ed965da55b701b3e734dcbc8609d781cc677d677579bf243f2779e111a66140060c233ca997b9e9940179e4f87bdc39986551df89f94643416c537e005b1ce0bed6524a3a13ca4ca4c613b924bc9d3b505c9d34d424573ba935946e0a6307744a875f5e3162fccf9e28cc4c530a960a9c2007c22a2e7227311c60d92531346043323ff3d849c7500eb14b948a97889b1af6201c444a8e156cecc85423ad0d9afcb2b8f6e7eb4209138863d04bcfd8f97c60b19f6e4f5f53e0fd78d20723d4f93a179a27a59edc289413b041eff2b0e1f02a1597e82f9c40565c0e1b092ab9b0f829fd2325ddb0607bafa31efe7e9ca42cdadd1d8deeeaeb1d4ff56205eb74af7da2f65b19b911bb861444e7eab710725de0bcca573b2cce82aae44a239139af46302611b55922b21484e6f4cc06a502dba4d7f7c3e31bcc1fe5840007360c6afffc356c717bb47ac891fc0f9a730558fe8aed33c473a45bc06a02540c77e7faa173b495c4c5dc83c29244dbf2ef576be3c060a96060836e2e2d8e28149ea7e78744d764f9ecb044be417312f207ced8be362adfecbf9d849b08a6d229df377bbfc41bc2ce927fc040cdc6948598867008ea416bdad0b77ef4f377e324a903de05c1a8559ff3749b7ac5bab0286b1b433fda0a7139927dc230c42ea100bd4cf9297fda04df9edb9d3061bf5fc91a79f2dd1849e529b1a98537906e7514870096b08a38c5112b07a59ad46e7e4c6d8322871fdff22381a881f0d7d8702bbdf95515139f66da6c5a4863d7e7b53129dbb223cf15c5f4fe31231444287f0bf272ae60a20604a21c49d2a51fb25b17c2ff3a68873947da5155283d6c0a371882a3a4601c58cf931ad159a69478cb3f92c2368e58627d8fc63a09deae329c2179287dcafde7ed77f0f356292d0a335e7d9d68b3cdb3cea583450a59cfd5ec092e9ef8a77db63692373fb037ef8632770ed4200fedeaa6e0f558e67bea9c9f11c65d5ff25b5f9d4251d84235df2ce0bfcbe88ce980caa2a159ef77cc9573610d2844fd0ab00c04b007cff838ebfec214761d6fe8c636253b17766444d9558404192a2a8e0d7df5aabe258688b3ca8b68f732e4c88fdf5337085e6ea37f59ce0584bbd09f492cc20966aee2f2ce929fe5874b12be10a7403580a2015640c9261b626a3e4ba46a2a27cb5a7649060d912c78fa0fbf4c6f57c35bd5fe9c3ca035259794300b5905b33884dd53d58503c4060c5917031232ab44348dd27f95f62f137e10cb5deb44901fc562ca6f29a690be6f39aefce02c22b2476a8e82fcc77c0890a186a3ebf3e0ff3503d3c2e8efac08bb52a3273e7400cc5400e5f7385d9a1f0e83b573d1586614e188c35a870a0d244719bf366b2176a4584758d38f92660796338f177fedb649406f596d071d30752a4f7751bc697b7aaa20648fad04a2176ea776b6a600b025f9d9830fdf3044c7b6e180bc168e741e3a8c9a6447be061309a6b8ae0fea0ad4ed51050aadd9df16e66489b1c1a45af6ff26ec8f5a554d263c0ef7436f2855fa20a13aee5e6e9abcfd0f6103858242597a723af5ec6bdc26afb848244f0b6b88974a7649f577aef3c18042f7f0c0240ed06ae9ddd9689400b528d259f16326f18e4d9968140ceefe8e35f7df0f5c9e31eb9ef9afaa1e1b107619b8d745a7413674ab38b12563750428cd751869af8a284897ede1844181018e110afc61dd3ce0d4a418b6ecbc3cf6ce7d1d5814352c6c86fe76dfa9a9742753b0286a22ea0f19cd84c549c8551fc04222d5f5a43b0c8e8c37c97907626901577728de1e2337811177d672de4c15c066a16728012d8f6f08788889bbf65406813a0842c1a7ad0d137ee1f888a2bba9ae6425666e119b4ff8e433b53fd3e01739b691622cd0035ee5b9141be685421e2342c17ca231ab31a6a20ae900122b17fe0ea53cd3389bcc94cbfdf6a1a49f72878656bbba02601ac8f76dfa9dcb1612d7fd5615eddd4fe81a450f8cfc4931d3d310281e7728a29e52ad49e927e5c22da3a74794fbea01a961cdbcbb6659a7fce75e6807292ca007780e3e017db4b61029674552ca688b946813ad5b631b27bc4309c366afb2f2390cb15e75c8e16b0c63e908ac26ed4888ce8d886d9eab0696f6d410fc47935d928ae40ea63d22e62cafa727b163910e394ff602b00c243fd60ede4b995cd503d1571772c69a20680065933aa51f23d25a33a56a695816722a4bd53def5c282b4f4070b1e9ab62592204f9e6ad2fd26ddc59c40b6a96489d1d89123403760026fd8dc573f7cec14525e802219c74b509b723ac5bab87c59db3f667a74c26c384305717d09acddb52301d3a2ad4fb2511489387657f71397d01ce8766cbd176f00ba4c37dfc8b5b5d22e79b5e6e244ee65c74bf3b8e5d9aeff46754ffeecd2ef962e6676ad87fd2480e6b1034669635ee85baa10dddba443584fcfd2e9b1e3ce38f50efbdadbe4b402c43b42464b599c40447dfa0802e1938dc84be0a3a33d0199e6c05a170aa5ce11069db429bb24df34476dd3799df06892f1b6fcbb2032f277eb50c9a3353e87c08f7250b22c6902ce746e9984a02dafb3d18bfb8a1babf0b6ea4874c3eeb9cf101e7e74c66b25bb2416160aede9e01575b45cc0b6f14015da95c18b67e7166bc16050a2d65bc3ff5ddca6becb59c187e686d7753224bc49a47957073bfb6a673233a02790938b26caebcbfba480e68a67365a97442a5b91f68195f7c246a59ef0ef8827eaad232e92878a8b3bec861efb9d32ed06c1eb994e0f0235fbfd785711233bc36b20751654b0b8f95448b32cdec919a80c0902a7bca212473e9e60f120ebb89709ac4953d6f2a205e5fbf09ce8647bb80c87bf9dc7fd304a02e68c6962dd3bb9a1a8c7338f45f27c372a6b781ed4822c9ae4eaa8ef9d56606353a6f3e1250727f272ccc609ee9e39f892c33686d88dd15a3fa6c78ec0958c92b82fb90008dbb496864b3d1867086edb1095fdc75ece3a2e166f054385c4229c3b944e1223e3857a7356f8b7b9d06a424dcbc2e8f62be1adb52b563074ebb73cd5be13815dc4cbf1262ac6c6c20e6ed3f812bd26086771ce1a1d89e934f859f367c4e9f2d56b404295d9ea940c0e1f5d8f9eeb2796201c9e0ef8483e19cec66c6c2408809a577a9541da1aa89c5c3b4b993c75da82572772d9dca0a9371d59badc520020096b1191b962550cd065781ce770e42fba08605a22283ac6ab14860768724ee224b2c8b3ab53437401a68e9637da41f7f3c12b118e31a59b72f7d2927eac3a42c1dc5e65203d2bc6c994ea11781fa7430f33cf52719724f62e48aed905087630aaa2258bd137ffa9a3e87d31c94becaffae0aaf39ea4a1517e998a53c9a06f21b7925efc2dc5781a2cb2a77f7c11c95a97a53f86d67747b1cd77d4d5dc2cb5c27747242d4d4cf92f7b575cf3cc0268260f6e6f66680185def9753c8ea2195392b3a2622408a88ff2a0de00b1fe81781c78d2d056a9351eb83bccdfd96bf4b9407dbb0632093e34772464e5d32ff9b7dc7ef15d75648cf69db97f9a55f8dd55f0dc42d474b936c99a77cef8e7a9253c6ddb9289672e57067ff679946c072c49809edf255cc8f5549f47625c3174e5e372c4ba56ccd879380f021d961b2026c1e2c432a0685305ddd1777ef5dd550b5ac84458a3ab13ed8554036476ba780f4a6128064204b952b23440a199dc4a4eb1f357c095ac203887d7e009652884a299203ef023a8b5ef5f3f2f6567baa0f1a3fbc8b36606aa998312ae7fcde8524e6b22a8d3ee11e5f46e018f9f7137b53456c93836685ca81193df4e36106e9e67d722c9f44d2e95535b1ea7144fb65f3a49ccbde27127446f8b48a93ed9ee10946e40cab66821d8e80da372e8c3a05eea60394732d5967470a99e0cc46d8c8ef54b40ccabd27c19d503e94dd2a701d89a3c8351d27a529333bf4820b6644e3da820d19f3d2ea3e86d1f6dd5c3e593992d4e3f7cbf7047fcfccec97d934096cabdf84263e966f77925a48ebb22dad0814115128746ae8efb2ac7b2997a8ffe24738722ba6876b888d8afe65f2a1b0e3631d98302ebb057bd7074c29aabe65c2fa4fde0da2bbbffb2f8a4143ad1b86b5b286c3a280e2ee2a87f358701bdc55e39d33970db14da12d809bae278310f329050afc3ff0c052aeb73b834d49fcda26f3abb60bf906a239af734543e14b0be8d6a1c05ff5d39559c803f55110828e949d4fd92e2af021bafc61f5c33b89342540061200013bec78beac8236eb6bc99a74b5f50b3964ee581c75eef7a6f2cafd80746c4220d6b5691364f5e29f36ba4c0297f810b6f2cfaf7221fe473634d58840520c3273e3e28a530e86bb70f31c99d434a7169992fff226260357e8f8094e771ad760d21b8fcf9d6fad432d3878ecd1b94f2b988d692084652b6047d19c06ec2aecca3bcba90654bf075517904cda2a97c614cd87d68efe7eb87ca71fc1e36a848442e49b4788b22637621097fae2260a2b0644fcb47375d3809cd6acdb146c64294f11dc888d95621df31922a9cb9479e4046bb1aab8dd2624adce7d48693dbb8229468e0bd5ba459d06227d7a4b2765532aa3c4befdde90ba711e7273608b9fb123ad2185079b52803faf3b31d920d83e2f6dcbeb9a3d52064f5d9b98587be049736e1868a0ad6fa8772bc96e0817dce913bbde6b832b75ca1c56d3d250434bed8cd086358ee781be306a60a6c3dcf8521390e3e1018421da946837f4381f7dddbafd5e6e481e0b64d4d395fce061ee522c22d18cb73bd0a1a69fa20f3720ec5d997dec42a246a8e574f5fe4555b0c84c229d1ba4b5f1c0c872ae872d327b4976485639fd8f69d1a02486499927ef6b481dfec6c501da7b56f800a69811474e0b84b5c0ea56cdf03f39f7bd8171accfe707555406e942ea4c7e8b87324bdf56f4830663a2ff0a3f4c45f4dde1136f951511955651e1c01f7db16bb8db027e1a40835e229e979502af70258d4dcab5241c1469b0200a398d47d450b9c64fe233b104c6abdc71a560fde406114444a6d29305daaed26ef541e69304684372b43ffcfbceb08129f0196acc0a20243deaf97717d85e87f057c02fa3cc57961487a0a221294b8f2821c9345e41e28733821562009c8b05a14e56fd8016d3d47605f66fddaacea202cebbc96b913121a0a31ce62972d1c68a30cc1aa09ab64fad5a2f78d8182217bf437545cd14937d7f35cca122e0443de8f1ad8f249596e209dbd878d60973da2f881614f185d840f57abdf52f1d691a8f9abcc79d7f76de8dae8983819ce58d511822a45dea994b76e5c5510a53f53916a145d9a86be0df11b97befa4fa7b647a5b1b9db42d96b445aaf7460eecc2da6bf185d2d517bfaefef677638a9c490ffa6651d99296c483da911fa00e3b8f3dc658f30f9778f958c37d3aeb7dc10ad1d10eb52527192ace1b1eab9c1d0587c174e1515d3b7fdae56072482efea8c7dc1575305cb8cc550858425605305e77df03c5eefe5bf36b5abb550be19bbf77d58b2bc5d0e4db303b43cc46e41d41857fa0ce2efd647ab3ba3b4d199d193cf89162be86fa665741634ed9a443136cf002003fe661f3e9f03b3933567ee3ac7b505616c7e8390a38a841511e3b065a4ed68e2bb2cc5991c50e574c9abdfb85aadd7ef23d6e3441f8ad49daeb6b1a9d059a533e1540de4b0163834c6e3abdcab75285c80778a83899271cfda8e5265a47bdd549051e5d7dc730ef1f3d7ee9a7d1535302e46f507faf60feb333240985cb59a7a95e18ade5aa512e4893178d8bdf2296911a28f18e40ea610f163f2d7ad4d27e7ad1d2ac45fa97185e0433726c735c7066aeb2f00fa5d2b5a7bcc209b158cd748557487d5c2e84685772fc040f8549817551ebbfd4461e454176792f54ee2ff6bf4652b6ad757f97fdea70ad789a89d3b4fe9d04aa3efbbcbe352c00fbb22f8987a6f3cb11d1311cafa31eae092abba3d39bcca2d797ba4e8cfc512dd357d4a7b3b0f15d0a594e60d2ea5dfd345dfd14972f755fbe7f807ed258c40095bcfe709c045c9703f589bff8ed17a3312edd544da0bc056f1e2288bf48e4118732d39a335f4c2a1c0e928d022b2dda9847748721a3effd753439b1a30e022b79ab16626a9f64df29f8644357b88878bcf79d8d83f83eb91846de3bbaed1d1ca94b287cbca8715b778ee42eb328341bd00cfd6e2cbd6bc7783a0bf5ff7c30244dfd747be053160f6fea13b2dd74a9918c0a27496457881cb1e971465f389e0d8499501840f1b7af05db487119767fb4966c39dad2b6372fe30deed2b99743269ce2833c4f208d55c6be3e47ac5131b8e541d5c666291c42e23db62ad9d1cd00cffcd6f36073fc193b2e0bd9534d1e6f16704da01ecfbece0de21a169fb3ac290664474020e634ae8b181924be1c4d6ca0ed50cb84bbd7ffbd17219c1a5a7621ffa701891c3434376173a43e42262faa0d0cb2cb3fc28da95b1533466d8d2409e8273991e4882a8fee7523935ca297faf2b3af6cd199ec4e206f37636c02b41a3857782d2c895729ad03c17c64de1d22bdd94650caf95652179d78fe58ac3b20d6b4cefa8256d1efac992267a07797feb4b6c2f642379edb7a4874d8db030a2195cadb6296ad13bf87b7ef8e0767b18992a8b98e31eff1feced2b12e06180010cfe8b1ffbc81474b7fae7122ea2ebc3718c3964a000a1420e634ae48803fc825594e123f569aa1ad0a068c89fad8b68f09ec8927bb78315a559f8cf20e9af20674477edc1388991548393f944e2f8a1c2bbea9966b09bfa30ee99a7b60a0ff0901fd0144ddd92e7388590ea6263f002e6357e16122e778dd3b8c509807c7c82f595f4ee88c0e65bd878d2929cb4ce2f3393d539e9f56af72ad1bfb1638ade92195e493e338d401a6471a6003fdcc55d510b5d26f71d04a6ae0576cc149c8682643592d4f3255995648e5fff273cf5e45997a2e44f74dd2f8bde2558b15577828830c23c518702afe557268ed3f7b0c18299f30cc4c88b2dd0d2aef5da75a5b14ae5900a249b62081fb9948614650054c68362cfb36f9f0934e1ec98c891dbd28c8bc103356091a7a474d2bf9e9594d01cce31d7ed420458dc9625117695d6a2606183b5e46069c734491740a054ca0dcb4934b96d3070fc363837db1ff81e210d9c471b7191a8a98a4891d2aed19bdfda19c717ceb64b75fb6df837a948ed723eecdf33d23fe91b6fc3df5f78763ab1bcb718fe339098b101f2a54ebc146110f8d3a65e7909c9da41323266e19a9c13ce43d0e3be679d49daf7ae09f4861da266fee65b132d563c46376e6647c0a90668349769ee6a3b44c5afab705f9d2491598dcfb52d52820b3df3e58962e47f8ab928d4a72f0aa351874d682452af7181b679a81504ea8ae1fe71503c8a05614e90aff50a31c2e6511faf8b9af0002762557ada0aede367f43bb86033dde827b8a4e5c7fea436ed1fece280691b26d7006bc12cd8b4fde77aae8cad9c66ae6d465e3d9cfad484cc969b38d6d61766c8f14f4d66c51006eab750e753bb86fe74889086e64ad24d1bcf4402fc17f5ae46b2013d73c8323e73cf698baa650a8ae47bf5ad18125b6a7919155ebc9b9b34369246031c231bd06ce79610be9eed895e590873090a539cbe4be9a91afad701e5f24b587c8f7b1f06056e97ecb69efaa7f87a4fd53da40e3b6439eedb4306e75320bdfee5c1d4ac101d47a39b81517459820240d4a51e2de4f1727bf9a0eb6242322e390bb87e48a4d518190d833b620f27cc16a330a2baaf84cf2d7a489995a5603c29a2e61d5bc8a928a567d133c7290466082c27306ea65fe7b9feb7283de1404c8e3228ace8eb4448f1c7ef9f48b0d4bfef0d7ba036cdebaa6c771448dc7221fba16ddf9c822e9b171fbf29178c2e429b1b26634404083d54e92e6ee9d064e1883c9e25523d4ee8450ca12b431cd30e9f6fb87490ec3e57d138fd8084287ea2a83c1dd0f94e1bebd9a38a46e7b7e58e9e79f72591cda6b79df6755b331d45c11ad28aa7306ddadd12c035f488a2004be39afdf94dbf885b87ee64cd9dcbd511039b08d1dd14bd5bab8357f18afc9d04f90c28253f96626a8f93422c1fec165069f9588974db7ac5ad7cdc22c454ae09724a0fa1aedccf9d4291ff835ae1f6010bc0bcb34e53b22e175cc0059ed2872537084d5463bb2284aff2873a23c82662fe123885f1a767818e968dca8ea08f351c9525336c36eb4a2915698e5aa7978047ee2f13ec6b7e88499d6bedab91cc826c49448ace3e32dfe68620ec807476524745679f039703d8386396d3cd22acd7ffbd565355db2cf03cbd765181332c013f9e6b2776c9c5ae0a5036bf4e5367842fd78d47dcfcbc46711cf82a85ba7d713b96f3e63ec8d8833911702b24b7cad910deb6a94b916924a2ffca10e0e60c909b02130faef1c4537e2754ea02c57757a79469e757e82368dd8b48224ef94ed24bd8cb3c279a2fd8287e948e68a883566fe24e37a3ede38b577ddd7dc3fc77d00b7e03c04f7da8110b02aedbe796e29042942a2dc8204730919c8c704ee339e05279d7043a5ca60fce10c903ee2659a8cb333ed2bb78efce80d3e058d9a0b191fe5abd7ca56fd0b06888adfbdc297781cdd296674df014f260189d39431b9652b08978b7abfb19a3e1a2e3925eafcfd183a47ee5704f1262ca2b2bf1e8e30b514cc2a3a5dc1391ec6a9bf99dbe9b5a048f66ad5f00a9e1ca43dd8a470c386ba255624aec4376bbb7cd322d0aee7804ed9245e7cd2e6864e284b94b72dde2c942b10914f7b86ec2ce839d329b7fc2cccdb0f24c18574be20c4535b0b0c4a1fb823a43d15d97b3d008a7201e4d49ec6c06c7dd722a69347f3ed389798bb21941f17ddd2a1758d4555edb9bcef81bf6a325f318f657f91e8bebc9f29ccf8c9f1ad0020b779789996bb3f9558903fb3ae05ef542b9f32439024b773ebaa1efc97c91441d11c241c4e93c725c663462e7f2a942fdc50a3c29c371bdd1b13e1fb64870aa2e3cd29dea8116eb2a423729c8ace6a5d9c5e72ac419c065bd1feb927e08b15868cc4b06c7b62a97a2203de907b009231461e3bbae4ee4bb08af82f15afbb13475a97b53fa89d7006d69d317d0c39bfdabf9e4257d63e4b53e40ceb13a6f621a1ac55dc06d177b6fcedc6e7e32005711c498992dcd4bc1bdbf1a6d423db7526d069e2325fd1b868f61754dbb71af2e7697b48a0b38453c778fe6ec30529ff03799247e8ff813e8a34563861cfb6e6c054c30fd8fea3d10ee38414c99568820823039ce58ac0487b57359d83825ca1dbebc7b64396881b741a8d815934796e2ed5416e37b207542726b2de7e7bffee33ddb33f95a5e312f23eafae94f6349006551c8206f287153aa4bee37501378463eeeedf05fe60b2dee20b828275b4ac244c896ce38f93183bd127a6f43b90402dde1e105e34060a5aee5e3e7d97e34e2106a0ec6dd1ebf7052081db6955990d6f1706720f956367c173a17a436d480a2a9b62175fcb45071b23051147762c58c3f57cb2f14133a0da0fa66b77fb4e2e1c9cdc085d18e7d5b0476361be69fea7572f03ab25cd56201ec95afffa9d63b709c6d4d2fbc435aa7df2efd79be5fe8cbbb4db4605024dcbffd2b22d193c5e57000568d246af4467050a7c8de7868a492ceaecababc6f9b0a7f2b0fc91ea5efb2881c095e6cce89188d2e3f3f61bea3ab99ad360ccc285baf30d99b2699053c30c1546aef7e49935f10f4ff2d96ccf1be26e7f1333c4e7f6c16296f35f090a11200ee333cb88f924d0e6d4314b0062db0062db746150be5d7b6f5c6b74d3ed6c2adb09c5cab66285133042c579f40cccf4313262054a0d91df0f143fd5c7f93023ef40e6d4c4cdcfd90e1e5927a840402374c318efdf47725de161f0ac04314b23a1964d6150e0db2eec017ad72d50aedb800f9a1353cd20343754d5a65c6ee61e92943a713ba5ee7340bce0944ed8f309507ffa0a21b79a86d97b174f82c889031481b4bf7fda6f352f05a77345ade76cbbdb1ef4b42bc47ff31b50d289f38a0d1751b13c8bc3766622e84ead59d75d4ae41d2abd3bc706228f6b1667a02b740d165d80a7d3799ed375c71d9c4063416add7c7211808bcec4f289e804072e4426b83ff7dd6ea6d86155c3707f1feab0fb1e61397a2e90ec53bee9e149be796711d9ec0096e422b70034ebd92932886abca48fd91dd7db7f2a8de5f081195f6604daed2bad88c0715960c4d030c05f9ee7214891942160e00af4c8c56117f717191a9aecb55b2e82d0f47e0de9e55d4dc8b8830f0e132acc3ec7425db5e4098c1385772afbb58bf86edf9511bac855dd8b1ac4ec4539336e5447fce99fd2a63f10ed9cd9f42267145f2ac01f65f3c0123bc2ff183bfc15fe0046aede9e8232ca24bef7ca3086d92d4ed47137e2e80879ad112fbf6696fcd7896a93775d15a7b2155988a8493bc0e94608b651979d3f1f15d22c2933750dd4d287eba002532c802b4c422b6ce2ea10207f448a40cfd32d724a27d98549d581c63d1be712cf97fd253723fd234f97161dd22e7ec000045e7e23a8408a4e97a6712de49dbb3bae7514a1b3bbfd8b9fdbe6d5fa95d6e126f4e8e17b04e73567b661c3b199d68815b025de2247d6c0de94aa05a9f2552c0e31c38e3cb87744a4834a2ff85818368f39095360eae9ee80496d25e6551ec1b7e616b9c8b44e76b74f31eab8d7210893f10d4f65e29a53c31a2e84b8ded0d75424aabb32b3a961c239195aa0680cce94af13e4037c199abd6470d20f891b1be6faebdbb79f57b304210e70a1f8da590d4b0df85d59c2bce5ca25817f6703960b482e7303a84a56362f4c7d3b08778230451f25e5942eb0e642f5dc96c04850e4034e6a543a61b1d150b77fd532471071de27480b4d66b9ab75f0a282e72f057e4154c505b811aa895bafde9990f322f3ea1c13c5c27a09aef8391bdd8c472dc7d2f49062b457039fc10fe5b4364df3c86605a7bdea2824362bea87b03343bdd661277633b55d7a17dbb1e17ee1f1c5f26405e417713b4425fbea37370ae2756c652e802c1e3444d3ca4e2deec0ef2003ba0aea889b723251304669ee70762568c94bba42d99427408f7527443d093fc4a82f00a310b49ccbdf20696c28e52483a522cdb83119e8f2d5c0b5295ecc9f42020d12b0da710fceb50030401f256e57f8c4f8edd7c6cdf72fad2232fb519c96752dba8dc6164dbeaf0281f7844af403aec85ad9df8dde7d7ef031f8de8c1668400db7a02fa8dbd25cb9828d584392f633111c7cb01efa564da95b6fe7147241e116281f16e15bd1ae4c3d06fa33112ad72fd432d5d3afaf25b77722d45d03b14311985fbfb9a3ec5e5e5fbc886770edda9ad928a02fa0636300485adf41f1649a0875bb957041786adfb7834d4613ebd217fc010e1eebe5423e121ebd97a120be1795892d862910c3274cdec364c5daafad955d71047a6f056d12789885edc09e1bfd7b459f80ae1842acf4e0617b97fe79ce82ab0b2b8cb47183239f144dcd6d25c6ae2b068759c130b1d659478374a7f1442d8c1bdf196cc3fb505f695d328f0cdf23f37002486726ada49fd4c40a207801bb0c0b0ecd8dc842398d89d7c054", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000d160d4006c61f6385f7902a17d4b19cc00000000000000000000000000000000523d6eb247a935a8de4a66706e303c9a01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db70000000000000000000000000000000000000000000000000000000000000013000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x00000000000000000000000000000000000000000000000cb72a26f4bc2b655f000000000000000000000000000000000000000000000009d27836abb5c49cea00000000000000000000000000000000000000000000000ce96c2d18739d82800000000000000000000000000000000000000000000000000000d5f41f8cd725000000000000000000000000000000000000000000000008883f6e266d0f541b000000000000000000000000000000000000000000000008c334df8c530f7d2e000000000000000000000000000000000000000000000006f020d9ffd1efe48b000000000000000000000000000000000000000000000000000085100738e49500000000000000000000000000000000000000000000000191dde7354124a1a4000000000000000000000000000000000000000000000000f51afb5d050a083700000000000000000000000000000000000000000000000cbf6b9bd0271d19050000000000000000000000000000000000000000000000000001f87fba51408f00000000000000000000000000000000000000000000000507d6c41d8c3244190000000000000000000000000000000000000000000000025dd8654e5a64657900000000000000000000000000000000000000000000000bb69b4488a6661578000000000000000000000000000000000000000000000000000000586bb4925a2313c38a4107f7a1bc1cf105ff5dbe5cce29c167c6d1454b2ec0371ba20730be1215aabf311de0fd5a7e01718e75cac3a5f3d2e9345bee5f0aff320d0cf441b22b940278a9e081c76995173a5668ad1b3c21d52e6515fc1c6111bc8570a97d2513cff4f84ee3292426574cbbd61b9fae99e95fa54d61edd498a00035ccbc19f911aad47df27a1f8d80c091ee4f4722c1831b60a2ed67b54f0d892a3abe9cf9d6000df18f0126f35f6af728bb8c9b5d5b06f5f0facd0ad007c578aed54dd6350d108ccd74a2a74a956663b2f5295ec9f3b56907a436c363da245fe137e4a27ade2d50ba826d38743ebfb8fb237263a20e1cf000ef5a59ebcab9aa9607b8c53dab08008b33f585fe76174121a00c186674a178454a2f1161fe1ce167675ebfb1ff20a7bdb97e3b74ae55d582dcb0becf8e0252d35b442e1ecd9ee727d3c73b1890189f5984477376e807d8fb5294234a8b233a26f9b6c718cf12664dbd17a94f2430404927f8413c9c4d3ba95ebefc4df717868f29e2ea8eed471daa1b632670401475056ba0bd6c349b55e561f7255f437cc48c7a9f7778ecced8c8d58f146f9e0b05ea0d166787c39d8d0bc4db81911371839929baab4dae64cc4d8d574fd30c1d6ba7d0933eaeeaab5dff6cb1af30ad4ba340edabb6dfe01e6f3ade14e206fa12392c605ab00e35de0f3bf35a27774dcd013d5d4038643b21508bb87a59519c1cf7aa122725cf720db8e870728206d50dc85ea0ef6231c890d5c943ef06ebd707e8c9e2a413e944d576a78cc3886020b1bffc604797f597712e1c8d81e3ec6f2542d46bab3e1415b01a176f2cdeb38564ef8a903260938cccedc9f22d3984d92b6585980a340a32614b1ca661901e3801bcfd86a4d1e12191b45fd9d216cb851c7a4a7a32501751a1a4c066f9ed267238c015616330809488b9028ac1041afc06eaaa51442c82a333c6690d7176e596a4f84454367ac1eef73fc39a250e9aaf281aaf24528af51d7061ce7f33dc128198c8d69c5512195e6ff5c7d16e9d25c00bf088a1174233241186c1c93e4544ed8feb5c12ea7eb189f8057720e47524ca15bb6834930409a27b99b6260b9d5df8198ff26dee77f148e06d93bb26b5da1110a5b032d732ac9dd68ac656db90510eb6784c09b5a36d3b653e4ee61f4f4fda273034a8a8a9d894099e482217ed8b5866a4a1ff4beb500a8121f2e0e97c28bb191fc3b85b16dd65970de19f9dc158860f2fdedef692a5801889143ecdd701db172d0437ff30d393a70585b5296b85bbeca15d9a670eab6a0dfb4811882d1983158c4ac2c1bfa3ca560948e794018e4f76b1b2bd25478552837685762e3192112658e9a1e375972488108f6e4ea30062534dc4eba65fd538a3913a9b502f37f411adc3f57187ae6922cf19790a0abe806939d842a4faeedabeabf8ae695fa38b0414ff61f933081d600479b086d53a04eb9332d8640b27783be4808412de388a16becbc94f095ae4f213c20af9322cd1b0038368a1e96bd4adec2338f3fef17f14637b8511223255c5ff15bd5cccc22bdfc3f2d434d6d5037a97869cf0dc02b523019b5dd4f9cd45f07f725dd1d8b0fe2fda8358066a603dd80599f3436763910386264fb6a42c239e760975d435ae996764e3a2c59d7d59c69c87b70c2a295107cea1f16f6c9488b10bc111d9ba5ce00dd862e42d4e06c6f7157fc9f267a337154ae8956a5a22d5ab4ed3d1a57de54132903b2d5499117f60ae26e7a82c1117099713785a7196ff1cccf53bcaddcb8df2b758b68b70bf9aaec914925fd1efde0287acbba337ec0e101459bb740b78cba08a7cb9467a52d94ae55e68b4d1eae304215101d9400a36c7d0caeb436d7eb4b929fee6394c10ed15f589466d2a9f682f6a1978b75fcd7440fa4b75a9ece24dc94877ded7b76f596687d10a4af92f301c3391a9d9ac3bad220771549d0b32dcb0c21f4e9816437fa46339dbc0c667ce03a1456de29a3e69252e7609eebb4743deb6fbc3ac6a7226b5eb7ab4f35f5e8a253e7fccb26dc26f35d4d0897f9f34a98a833956a5d8a34f552cc52b8eedfdbe2c8bed7dcc889be5ab58f16a066ff0738e677b964dee8f77d71ca6fbdf7f6a5f1efec00e95fa2cdf9cb13fe409c243c73bbfda0da9389698bf926bf678cf08bc1a154a4e2740d06e239be0ab6037453f90080c7638fb3a4b47be173fa5fb5dd927386e66d7c301a0b7b84a433b7abbc0c17ee52a4610ac27caac41f9a57fd6572f94f3f0feeb716798e345e74c026fe1eab060a9753bcc6b494adae78b8c48831c4b3068e874e8ae97f098c279efce9fe97431a88ed6383e376b697a8dd7b29a06cd19dbbb19bc26ef7f8e24dcc533e1e6df210f3d9201d0818a2acfed7cba4609dd1f04bba079b62438571dfa4bcd5f9cb3ed792201d81ea8b469de00d554952a10164c6602ba7ee3c93eec521d5b37fe71f00f135dce445d919d1456416e0008f9e1ea2c5f0edf3d8d0e705fd77daed72d4060376d93aff9343976c9f9a9ec1c6fc299607e32dfce088440c1b27910c04f99a8c19bc048504ab3ea1c3149cc189d32066676f85fd4e4b58360c14dce07bcfefb8786990fa3d2d02f55df9d291e358eacc2f8cb961241f3f1aea3f16c859f9a9427cacf73be9fd75855899aa52effdd8445043da2bd17ae2097c5193a37aabf27215edd430732a1968ba5c5cd163fd4787fb223c6425fe79a53a468f901eca21952d954b4f1acfd51fafdf82727e491c3bb70e64d50280395515573b29cda31e01e06f7b2e44caf0b74733a091dc63384e63175842a450d156987bfc733bb50df42f4588a1d8aba142ca728b80e2c5cc3248d531aaac9ab22134aadc1d4b8235879649e448af0d13256e8c70d104cd193eb25bc3b69749015b1977e911bd0360598ead3e73053fcdb62119086244fc8f8198fa0a02425d387d9e842b0f98a41710ab2cf859d59f93acf99b00e0ec230d1ab4cd5571f19f9a9d89f04a9fbe3eb93643915d3083afd68d6ac67e51a886c2e7401e737f324852eb1058647c1ad920bcd4ce15f4a8f1e05536e690c1cdd151b9c5c3bc9eee54d4f92e069b280834727aa57425fd74dd464e11d6e47133a5dd5da94b805eb2de7bb269a9ca87124d0ed304ee1865a1d7788fd4e0a0111fac6499e357b1c303671d1bcb274e49850271998e5c3fe2505a32320745c9f0788d50f5a3f0f7af6a8a11623e0d90e96261a23a0e0be4406fb1df38bba0cf1126c091c192f49185fc1b85af39cc747a605a758a43ed4de6bc969a8e4b94b4e04103b6c7e771115bc25ffb588df0e378b20828b626f60d91609ba557aa16c7a2a86d96745a4af60cef3377e54f1f0a918418915c5b0843a9e8e62defa23fcc7236cbdf03146f9d5cff3c2738cc19c712bfc1e97d04c592abd2a503977876305156635927f604f6d49cab3351b6bdec01b73285797925b9d56231a26b151cda9151bf01dc3bf7843552f07c3c599b32a74b9a1924bae7e2462538fb3d11f872b301e2e979c50766eeb68e2fef80423c0aa0be3237b280ad3b287a0dcc6ec1307118d89e95c499e5375b430f6834447ebc4c975181d5c801730bf232099ebe89d2361ba955cbe174d15ac109b458838c87e0057fee651ab45699245dffe0e0947125172de2d5a309139f81385a6de0665004aaa2a099b3ba491262ff5446cfe2c002136e84f99687d4c2dd3401a0f272c80729f6a49124e5256dce015cdad858b0fe213385b7f84bb2b36ce9195f8698fcb1e5eca0df20b38066799ebac0db9151540a4735f7dbd647255af0ba418bbedf05cf04ce6ffea4263693b6f9929cfaf1ca0558cdfe52634a0dc615b3937be0d6929c9203f15d3b16ae394109cb12b450c26cd6fd4594d042c25bac5212fa45710eb1ae7c12035f2756cc5b79cf1275d21fd427ab4df6fb02fd7e07a7ce5912a7ff309c34b6e8214e214933e375a867e17c84b3a9f7402af2621a1737a0b6b35265bf0e27109ace625439cdda98fe2cf0b8cea574da812b5e8675b450ecc2cb603ba8cd9d23856c0157175a52310c26e16ade24aeb764c5c1c8524595eb4f3add96c7b8dc400a42135dc18da82da97540fd1231c528e70b362440faaedd8d9fc6984f9130a767dba76e3c7a6bf758fa40995ca9d7a5af60270bec0e91cc535b007a685c8c839e9c527c5ab8eb7ee448f0086c298961b8c479e1d2fe0bd61eeb6efdc10ec5db741331f4742b28630fc8f2cd7ad5ed08569bddf91f47e40f0ebc70c2e8d7a5344ae40968a7e06e22fdd9c0a269f5c1d73a857aeb0879fb7fa857a73df6fe8c5699a8fc37cd4d9704391e52af11ffd63ca8cce301c4d2e7046f3e352f3a1a84127940296b9bd7677c48e2a09721bf6df1939a2931b67c40dacbd49c2b0b7b73e173d0257867e03c509a3d11311889591d077ac2e47fa0f0934712deea03834c1f20e1d52b360ae53ef69cc2d098bf4d516ddbbf219dda0bde7756c7a969bdb7258490da74441edca6545b61c59a5c01c7bcd3f276f4511de30c3c9c60a98c5fc07844de934975c350267df1782ecdfed3b15fa815b1eb0844b9a36c33d3f4b6eac1d5e829af190f37cab9509ffe42d384ec7e245b6140a75b7c17af5d44d09b4073dfccac35a60ea12ecdf055f473428571221c87429f479c89e9dbcb99f63dfb86298d9b0357cec7da6532c0bcce9bf9e5a5fe86f44ac9f07f04b76dff78614438b38939c01718301fc3d2b5de0c98b93441550e8d759eb21d914ca217a29d6be440cc2d2368863129b37033998b03ab2db1ff7f43c41ecb606284c4ed2b966ed1b3d7545dd1110808c25237b91337b27c2baf9f82732cca4fd15674245bb6c13c418463c31fa7718d281060f50d783fc5c32a04b819c7e843d15ccb658e7ed3601c3e11460874102652d2746e2fc3a238955923c713d04ae01e1588718ce72710ee2d4744b2b7c7f066e23f97b1d534f10efc221a8adaf64dc52200370e7b67d2a1b56dd5c2fc3cd806315ded78e4cadb5b5b4b51a2ed1a09deccd8f6d7ab212cce6e667d359a56e1d921d85c36ffdcc4aad0db718e2822ba268be4cd25f748cce52d28094aa304d8ca2003e17a0062957689eb6c3c8d22e491100c9049f0f406122b94a8496256ddab51f0e3e1ed32f21fea7688dfc76e43e7a0a0f569fa50e55ad20ebc7281700826926d2b55248000e0a120f542210ce4c3c666232bc6e2862a3c5baff4e739870c010921e85d0d413b13bb5fd7d074c5b85a3e77d5fcb87c2319f6d059759bae5cf2a913ee4800b575b2d10c30b1e960accfbfa92bd0270ea55fa3076b4b583143c20a8ee6d30c78d807de60607cd2469b66b5df59484f0f751cf0bdb2eeadab0ac0bebb994ee223175e760f62893d7c241aa1e5dbb177114b55fbd5f81d009447621e6b9e212ea7f25810a7ea5cc34eabfd6e35a0d3daf29c62fa09ff67c782a0b0563a6cd009e2ec14f1677dfecfbebeb6e18dd9fe2759bec8c2259c64a96c6e62cf64277d23440e9df48b88e25a23507a0b537621cf6d65a7cfed83b9d5e1bbc030bab18c76b60a07de1cb3fc5d9af66747bb0f4a901a930ad67e3ab13ae17d017615cab0f232437159824ce1b76e9065e9457dcfdfb4ce9b364ed06c959cd531913286b79453bb50e0dc4198993b58d03f61d8f44e5b195fd6a3ac0635484d31e96f84fa9217fa2c8cc6745a13cb027fd85c3dc5c12e21291ea1d4143f377a816c048dbf593d4ee59c1115477cc5fe0921b0751d00b36467f73f31d87809faf1f5ff7387d960fa1e9e7bceeac38a4b3951c7887e57c73f639b60c03c9e2f10e27a87b83e774dd7ab1476413dd75c179f4777e339a431559295099e4a521a0490343c381f3704ac29c32f659803addfaafbb556da2f4e2e4e20c9cf1a42d639b15c324488caff0a612c5d6e23afa5c97f6c08140c2b68b5e26529b7abd3c422c033b8ad5cc4aa0849611f1336a1132860abf0496caff22ab0d6d6a8571362cd21cf10df66a695c3a3fae7f5c4cf0b3cd05312075ef875d210ed6618b54cc70852cc8a2ce2934aa228623c7d3e28c8b95a3b639909a68786e5e2cdbe9d83d670602f57f03140395962b50608e083702fd9d54fb26cba708aa32303ec6db94f08929ba7e70015e2dec89d790e53adc8832dad12bea71a76b062d9af4914a0712191517d6c1f295db9ef064765e3bbf16fbe762134852bf0d388ce24bd179f721c4159bbef83fe9a1a2faad7860284fea07825e7744c103fdd244164abc6ebc3d8a1a81dde0301fb25d53907ebb44fea80073371748b3cba4a5146b7e26bb7df99419b6c729bab92e54c5b85b9109c89bb8117d10473fde6e238cf3be539441fd7f265f5c1b07c59a0af8eba52e61614478d50b01fa5ae7b04aaa8593c2b4e3f8f50c2e2864be975e2a0aad0c687128530a4e7f729012edba9b8d5db3ae708aecea069440ee37908c90d60d45203cddecc3b1c760c4f8694a2b31be3b69f6fb5bc42447dccc556c4f4f3dbfb61d9d00203ce2ee1b2d525a12ac890edaedd7eb5156055b878d91521a378052cc88eaf6ff10a20578c029bbfea6e1d7d4b8ea0b70bb3049cdf4083e12f88d74eb75c55488b3fadd884d9fbe62ae22a81fd8dbe4722a27da0e76c31d588779327ce844c20e507f2f516133ec25e6a3c2bfebce4d77f718f53a59118d337ad80ac883b2ff1e0879503dd15e78735fa17e15782e3f05791e53855a12f699b26092b96a0a30eb5157dbf3b5707a16f397dafbcff4e8ee3102d0cbe81199bcfaaf6b3ab751aa32045a578a9ce9c2c312b1847a7871304d4710e5eab9d7ccb6d83f7754cb4e7ac2efb44d45d4165df31c86d03a17a375492a2078373808c9babfe4210762dff443c65be414a814311ac3734b60be87a6d5f50ad1dee8f7d7ee2945cec9db4abe572ad1376925a930daa6798d27f7d35f5f77133dee4a69e0096c7e746499f3f3fd1077c29af316af120acbe1513d97992fb42a9c4464ca87b2d8f9c104a69f28b8415268fc2e828e2824f026f03af66fd1e11f44b76a5c49ae396cc5d3203ca6abad8f756fb8ea36c8f35ea527f382f35eda217e8f122cc3061ab2a2fbe180883059747fa8e1784c0b515eba37bcd6d4a0b51ff843b8d53948501be8a3ac98c02bd9fc903320b49d12e725916c5283f9401b27aca219a88cd6af41323a73b10eabad4274edb5e649ef93d3feb4cbcc5efb28166b07b8e7dcff384040f8c59e62c37e59a90dc87ff0f7610418db2e75e7d7a82b84a028ff05076eec82ea0f4e7fad6aeee05c0638923ee6352c22bb426db70b075b23591b07cf49fe34eb90b8a4b806d9cc9af3fcf24ff60a2d4ce065960bc12d55ca8b11e60998621555ff79251140cc84f39f339c102f774696e94349e63e07bb5d0e9d8a10f5f983deddbbf018cef93fcac66850d1bb2d378a859993e43d135dc83a4f66ffea12b85fa5b8df46beecc582a2e406b09252da9fbd01d6208501523ae368c7179d70c238ed89f951dd6d7a10253da90e68b225eb35fac658de0b7b96f652ff291cbe7d64daa91d40c9dcd19ec109d592624caa4358483c174027b78b2f933df2f6b098d71d2ac1d6aebc2fb29361448d44ebd93e3468bf22110e8aa311aa8db85e773c3067585caa497802475fbf34b1f148388b265276abb5207492d086f609659abb12d4d48f3a2c84d9c80b069025e36e593093fb5ebeaa1725c6a5df89f634e9ce26ff556cf6f88d7b67c7e193055ccacc20c9eb78ab721af3ea516e38ae26ffe5a7b5c1954f4590a4d52578cf9153a9acf33af550c04926911c6dd114937ab8ea8147d08c7db5716275245fb951c767f82430ccec444501d3c16986d95134976e7a7203583bcde83af9a99aa003fb4b55fa03f94c09e82329669086fa0b5fc56daa0ad6df98574c612c0788fba15f4e3fa1d3c00ea3ad1a32b7c183d6a097f0413f606f106a6cdc0e93a7ee18830496f8e1731626428f01bbfae67ee1909bc7d84ffe61070be9ccfc109b5e7fbbbd27452a431187c53308ca03e55d4845dbbb575edf975784b8242889b19cf9001075a3a0d084bc2daf1be59a8c0addb7c6fcf3d922c7d19ff3981f5c9645b031839de80b56da2982f426ce8869036679da3970739f24bdfdea24526fdabaedc36040f2902b1b1119e72c14c74f69aeadd07892831a5da635c6a312cc1139f8ff1531db999dc979200100719683749f6cf50a21868958e37ff4786e7b5ee1703d43e9261f52d12d59582986a895846ad4d5c7ef17aeedc053e72783f575d909aa2f782938ce8246a11e1372c04b95f0c9a1fd6ee50f1121f93cb391c50d0c958a37a68add49cd5e556b25a982c3a0b02603edc7a09e993aa34d7d780e71e955a6c56d5b8a16209a386e2e989ee588189f093e06ef28f0cb18aac37a1b3c369a80c40b64fdcde957ce4f2ac250b30125a6bb044d6cbeec900598bbab4ca89612b3d2c264a28f034873672e18d7f11e234506bf1f2309b7444f8db2c7d5f5fe70c612fb85ea9dbdff796807ca6ec9c93676363ab70041ea6a529224ce93f1a0abc53a17701c0c3d2f4f6d2ede4b9fb18914a4113f65e3c0c6111267c4f6be1fb8046ef25bf35c44d19b842ae2fe2b889e477c54c98d82da4c7b0612603d9f6e561a05948ab0c30e494e0b2635f26b324ee6ac274f83f257657109d07720342b7b7b5fa7637f477d41af6c08a23725165ed2a19148c9ea0ba8ab684b496108cf65e5f4e7f0ddf795c404120b518f3e1511ea4f7546ebb2b1caab95ca3f24ea068450b7a0099859ae7c1ac908b13c49f09e88fc97a883216872bd6858ebc1fdfe5f52dbd0356e05257d80320fa187705c51f66366f2f05466b1ca3c892961f9a589a6cb44bb273dd4fb84551bff7f3406bfa4f63c668260021425f9b7163670447b8235423b3710b5e484bf3039a7737aceb27e9be16933623f1cc9c9082c83599759847de30c04678606c6003d492575ac04ffba536c55e0b25a03951405779fcb7d3cfd98fbf3df1e61742d76e67149ddca631eb12e528d988b718e63cc9619433b95482a23ef3db9d01a2005fe40cb5a62f8be9698ce69cd4b9db4778bf5febbdde68eb90f19868969d802ae7a7d22b8d6032085d6040f6c03b6d7492886d6301c9afabe287c6c45b0292b310e8d9d7449443006fa8131a2f3f5656802d209ae64072361a13cd7b0f2281fab02ef911d09729456a937561f28b22445560977a07b0e00888b90f5f476c92db9d5073658a3be4ce0ac5d1db0fe45be0b1c3bd53829f62ed81d5ff6d51ad31621458d9822ae610b033350f9c993233d79ae279ea4264e67f31efb46dc276410248f27d94a51f4c7a665bb24cc03872c8cfad6c883481b414686d970691abe0fe37c44c605610848a3076689513895acaa05834a3b9e0e024b345cd8e1b87003d5d7475d881900fc5586054fa9234cfb46bea65c71eca250a63844c749279f2b5b49120d771d71fc3500f1d125662bc80e13db85e6695c40b5e7fd94ba3f2122663e719d61c8829a3f55141ca58c5ed1e28beeb46bae86567d9b7808fcc839235222a95ecbe861aab8e7d0c4e600144af761a5a540fbd9def7aa41cfee261e021e3286ab9b59f7a311d01c19931e346a5bb1e1c4d7c9bb3faec5f1ce5d2643047b3bfc0e3659c2b8b8f7fc07dd843d506d60a8e65056c2aeb3cfbcc25149661e2c5eb1eda809e387a860c1e9a7f37e1ea8a18b48cfb239aceee1641fd9ecc023c4925fa502947194eabc25b3a8948861d26414e21f19e7d57d23ba4d935a281313be2a19396e46e733a2d08840076c2f9d38c7891201640355969f72e9492e01f41e5ce954612273f29ffe616557a9c01bc2ed58536ed6e0171a7a9631df70079f38ef26f2bd6a9d0fe7e253fdd1fe098e9e59c0fdf14400cb83091242267f205c26520404b3df973224a3564faef86238ab4e08c67bb4cf64f57e17b0ea4b1cff7bfc129663c0c261d026fd7aeeb7fb358b296c7202c8b2eaa839b4f9175002835b9ab3ec3ce576da4ad7ad83dea97c3d9ed7bd366878cfe8a5e5aa28c0df0139e89362425d85b45523b95a262b5a68718df8f13c6fbd052ef97bdaea84ac158db8e784574a71e7211a48e7ef64c569161cd19539ea3d1597e05b0a83356d0e4055519520120d0ffe93ec75da6b630acfed959e69e25d46501659373bceed1306c3e92851ec6cb8dc53f1aa72aa8bdb0d85a815d3968a9b034ccb292a66b310935d44098cf4b92a29ac0617d8dc6322e4ffd1afeb5813197719186dc6e0d600dad3781e520792127fb8fd72c9702411c0a1f947aa32f86510936893607fbf0de068f52c56a8691c5d4c02741954d82563a90dbdbde6a4e4c81fb7d620ca76265fd84201581590686edc35cd730bdecd186812b216770d2b3b15dc8a398be425649dc6e4f3635a2b9f8552be41509240dba79b4f056366063fa0f35bafe42b110ca07b7f2c8034386bb0dc771415aa8cd885bc06160e77bf85f5c3655d641624fcb342117f94e4a5afcee5b98a648baac82063d897ce4a82a3feca3bd6ef6b2b32f06f9f38eaa15fbd3f49e6f411ded6558ae35c4911ab1c316db2b01ddcdd206ef277976083b82e474813b23b4b8d9780e0bb6db3860a269b5215d33878c207c796c7a764bd2bf1bf255249cead62b9d7b90051ac8375a1701a271a5a504b025d4982dc7781c4a68b774c5e14d7c7a6982229b6a5db2a69012f9dba9a444e178223d54934f655c362925278725824ab9bfc428d2026d7725f63f6b210d08103b372038eb05ad0deb2cddf7b9ca8871fdfdaeecaa6e78480148cd53d772e002fbf0df391012015bf65477a51e31a284c0dd71fa342e2922818119bc930a61500f7c8b0f04920ba24b379b6cc2952fb3119a8b363893def2a9ac3fa5bb5a36e19f5e706f9197907da882af06c4935b07323af3293c75d8ddcace5e0e5a5aebb0717fc5308f336c38ecd96c89c9f7422b335b0a4e18f88656f255e9459d477e515a56275db5ba929662aed1b09c814b5a3ed4920039bd65300cf52c70fb7a42a1c81454eac1c3d276f1c272f81a95250fadba17c9aca2c0583aa2bf5b0d2873418a9688b46dc7ab1f36aeb09ea1b00873a8ff04e4bc608d9e35b53e7735fd5a01dbd1eb40cc29b13bb21e85f00fca65fd0983459d458f25a3bdbb09b4094ba692896c82fea2e9353d9bda7feedf2416f1507b3342c0d3d2a2a50809022c001bd2e6412debc02a94d461ece20fdd765c74e78f6e9e43fd14d935b7884b716a0692004490d547b1c97f3b60288eaa7daec61698fc7e0d302ef97ad1cf8cee533b816f6f722c3056c097c802609d9c7b074c50e75ff09c190d2cf732c974af4097623dcb10ab54bfbe197b4e971c40d664e273b6dd3e93a40a8ae5187ef842159940dad07983ae2f8e04736680fa307ab50921708d716bca4297a7ff0c816b6446601987090bf0b69c6415de518d73a56474ebd658f8d1e9aa500e0020a6c3151201b11a3cce79a98ad901722371fb8fe162f465e6319fcf30209128c969701bcb70001e8aa66cd3c38449bb2e520f07b9e1fc2fe710ffbd456d39711efa0832ace2aa3c12f7793c3b97e69d5e28f84d9dde4cfc2990b64e13022e5472b64c29c782e4cbe5186a9cd9f5c372f2a414838dcebfa655361de8f64b3155ecca983aad81b51c65aefafea18e7d0a0430bd86f301462df63650497152a83d5518eb590ad03c3b822cbde2466c3571d685b8141f972d64a770a52f2912ccfa1af908e84570a58db6f335e36a93b8b46c8fee6a65d1dd1f84aa7d0b022392329cd47f1acd8149af8bbb40ba250d374000d409fb81cc9aaf8c8124303485900e653f153c5d108e2fbe580e582df090d041a64c9631d114bd77943a103f09961f818567c3c711c3a5b6daf0e32e910da373b362b9641706892cb96419e5a8c76f29f34c05c4013123379388bdb0e5883b299d8469542bdb58f62773c942c5707f5c903f88c5e056779d741068745343dcb01bbede6be417932bb0cc0fd1c86270af2cb63496e175cd36612fb28f4a8affc5dfec8a844daf2e67e83ad7db40aab6dcfc23413a702336cbaf76a18fef3c7bfe65a85cf8e99d59537b8afdab62cd6882d17bd1cab00c6fec1e87fb717719c8f1f73751ce868eb93e57f813648d813dd68f03f30521c2976a50f6721e68754e5fc7369621b74dc6c2b3a407065b35b5dabef052d1a01ad1cd893425a4d56671a57219b26d215fc4bdd5b53680c43453081304b374507a566e2c9d5827d3fc804809d7a8aefaafec10fccd87763c298c59dfcc7a69318dfd77590a160094d3a359e73ca0c1279cd5fd655a128f580b7fc2758ec480317deb9f9addff0b92367143d9cd8f30c84b15b25eb5f4a47ec56caaef58a6ff1139792c89b163437743a4d60fbd172e18b270d653927725b28ec3f69a8eaece000f3d850d877a132fd182989ac44e2aa29459f7b3914f4f6030076ea47ae401b203e48b45fa72c752b9947086a35a67b68e8bc183f3e9e37397cfd45312ad31908ede3e9e4d8c78e9e224e1a42c56f5bd1980e06e9b1d557ba4d57493cebaf89100ac163e836ee4f1816600283dba01f6b1f1699f0c1ae32630a712b828fc8fc16442afe40c2f51e6e9ab05fc7190e8bb2415ed1e5a42ce7e4c858005c353d910efc9eb2cd154c9d5b03da687d58cad45ddcd6ccbb36062282ee94870bfee5031854ba4f8c71350dbb06dfb2fe67ea4bc8853f2951f36f89ca530c4ecbb2a3b30869e7f23b5b62505613dbefde6e5a8670649048987fd3f588e459fcb44f65c91324c04be47f7bf00f94bdbd730a95c86122be5086d212e10042e980a2571cb7164875082c4e04eabce1d7df6b6b74149cf0d6f5214893750882693d6837e6cb2c846f42cf78fc0e634cd304dd118bff8386c3616be40b1453caee5b2e833a4817911ac39961f385178c49973efbb97a5ac5fdb03cb61084c3e58ca4284bd78510e102da33ffc70684ad4f19bf2bf727b1e19f421915eb4574442db4a356cdb31b5a6ca64ba0b4bf26344a21d31b62575d12809c18f9afce06a7e61ee6adf8e205a6d7e18315546fde9232a5ba7f09a05f98a64e90480e069a3dc1e788465d0211443339feb7ae07069444094267bbaeb0886a7395a2c858b74bc77e9c12082916cf2bac5255804cb043cae050335971a55b97ded520eba50eed6389524dc9cf1cf873a57c6bb3c9eb90393a184f19700736c2a1bbee9d61bcc848e5c743387b058d210bff2531aecae2e31175afea3ecd122a5f7a43ab8c7ef95fc562d5133604e5a81e8dd44d7dde63dca73a34cdce179691cd99dba061ba1e52664d6e8b531317f9cc29b4e6737ff28a14c91ae16a9509df249d4caa5d7a9accf203ce43ab0d4a819495cabbaaadbb284b01061f61add5ec9c0a9d39f792581cc1201ff41918dc8f1749902f8d4eaef9bb1385f4d84cd68f9e86b0adb4c1b961f3a4fae3342fe7b21f2b28fa20671100694f661fef4ca029f391f9ca4caf3cc19e39a4981f131d4f538c71f968fc0d9f03cf323fb1cd48dd9bb900876c300a022f3e817a62245004d95c4a1b15d78fc5024996506557dd9ac3bcdaab64e61559e8ccfaf4480c2bc8a49d41504a25a33ec9f6e81b37e9550f8df92d1d49f3df4724c28ba71c1ae21a17e393aedc796558a02c5d671d715aa45e42efba7af0bd351bd712f9cb21000f926d7c0f172c62aba013fb20716bc976182953d9378a505980b02d55470a9faaf9bcc8290bd4b1d1f3bfc1e7a3db16f44bb060e693037eb8c2630853782811e636bb7b14aec5a9dd3e96b4f781cb0827aab48e5b6c3e3c8fb903a57f291e35a11477edd96f428ef8334e311a837592ef308cdf95e6b25bcd99eb82b7470af7f98f8f070045cc5baad5dac76c953ac04bd81c2f3556964e3779a6d49bd125b669ca6ff09c379f03bb30cc3850e652ccc14af965f1e6f7304ec6c17f0b8f032f2223cf5ab06bf1fbb19b635064e577862391c64ba9c3ac9869bf77b04ecb2f304149ad0dfcf55e5150dd5e739c170e146e0b68e778880c556fab86fdf98c0b4a8ba767b05fc1b31ddc271538d4d74269697a278d281e032a953cfb62016213166fb3698dc2527a7c37dd6350366036a9bbe0aa7e36349e83e56e21438dfb204239e67fbeac731d256fe906462573ef805387cd62d66593cb274dc1329edd1be34024d2c8c7a529346672c40c60cf1014b3b41389d8401788bbd8cb2105472d79f63e18e169babf3faf554f155eef0895ff746fb7bc96e4e06b0c283d291f024e9d2a47dd1fc370e2262a15b81c5c5a8744919f50bafdb4e8c51df6a934a120eda77dce60c20ecf47300852ba0eae62c5bdd58c292dae1dbe15bb8b7336160806a01ee0096f1183ddfa1847f17e3efa20786a469ffe58a6084946b2881efe1041ffe6e5a395efb68fa39ce73758041c29dd0f2a8c50501c97e761164a1de90626d0a72ca8083f1f970d8944cd3f269c919f52f510287b5faa74350d382f252031db8526e96fdfd680e5a32fb0737a7dc5326b6a1f3c69583113719b4d8a5021ad6adc313fe5fdfd4b0a3d38d789ae51ce92b83939f8386978617f43ad22380a1c75f0fafb5b46d62c89adb165184ce88b27f9e572b9c763fbc2cebd5124e702b2047e10a8735aceb69559dcedaf4a248d938728d651a01bf76c3e8ff7572f", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000002996b0c00e4dc6ae412f0b46d68dffcf000000000000000000000000000000007758cb4d71d9cf4b6349037b19fd145101cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } } diff --git a/circuits/benchmarks/results_insecure/report.md b/circuits/benchmarks/results_insecure/report.md index 4a8917ac4..1d166abc6 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-19 14:14:09 UTC +**Generated:** 2026-05-19 14:41:50 UTC **Git Branch:** `feat/1525` -**Git Commit:** `cfe60a5290ceb27522bdee8a5e7d508e0e556d08` +**Git Commit:** `d49dd7569dc6968edf92a66bd3ff48b6c5ee0503` **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 | 28.33 | 15.88 | -| C1 | 57818 | 0.35 | 27.90 | 15.88 | -| C2a | 142625 | 0.83 | 29.66 | 15.88 | -| C2b | 198355 | 0.89 | 28.73 | 15.88 | -| C3a | 132633 | 0.83 | 28.25 | 15.88 | -| C3b | 132633 | 0.83 | 28.25 | 15.88 | -| C4a | 92515 | 0.52 | 29.91 | 15.88 | -| C4b | 92515 | 0.52 | 29.91 | 15.88 | -| C5 | 151717 | 0.85 | 28.47 | 15.88 | -| user_data_encryption | 53732 | 0.35 | 27.79 | 15.88 | -| C6 | 86927 | 0.54 | 28.57 | 15.88 | -| C7 | 104273 | 0.52 | 28.75 | 15.88 | +| C0 | 6847 | 0.13 | 27.23 | 15.88 | +| C1 | 57818 | 0.35 | 26.50 | 15.88 | +| C2a | 142625 | 0.83 | 26.50 | 15.88 | +| C2b | 198355 | 0.90 | 27.09 | 15.88 | +| C3a | 132633 | 0.84 | 26.75 | 15.88 | +| C3b | 132633 | 0.84 | 26.75 | 15.88 | +| C4a | 92515 | 0.52 | 26.62 | 15.88 | +| C4b | 92515 | 0.52 | 26.62 | 15.88 | +| C5 | 151717 | 0.83 | 27.39 | 15.88 | +| user_data_encryption | 53732 | 0.34 | 26.43 | 15.88 | +| C6 | 86927 | 0.55 | 27.35 | 15.88 | +| C7 | 104273 | 0.52 | 26.86 | 15.88 | ### Artifacts | Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | | -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 10.69 KB | 0.47 KB | 3042773 | 176244 | 3219017 | -| Π_user | 15.88 KB | 0.12 KB | 2972965 | 170452 | 3143417 | -| Π_dec | 10.69 KB | 3.47 KB | 3553807 | 187284 | 3741091 | +| Π_DKG | 10.69 KB | 0.47 KB | 3042639 | 176112 | 3218751 | +| Π_user | 15.88 KB | 0.12 KB | 2972941 | 170404 | 3143345 | +| Π_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 | 309.65 s | 127.00 KB | 128.19 KB | -| Aggregator | P2 | combine folds + C5 | 0.85 s | 10.69 KB | 11.16 KB | -| User | P3 | per user input | 0.68 s | 15.88 KB | 16.00 KB | -| Each ciphernode | P4 | per computation output (C6) | 0.54 s | 15.88 KB | 16.00 KB | -| Aggregator | P4 | per computation output (C7+fold) | 82.61 s | 10.69 KB | 14.16 KB | +| Each ciphernode | P1 | one-time DKG participation | 314.97 s | 127.00 KB | 128.19 KB | +| Aggregator | P2 | combine folds + C5 | 0.83 s | 10.69 KB | 11.16 KB | +| User | P3 | per user input | 0.67 s | 15.88 KB | 16.00 KB | +| Each ciphernode | P4 | per computation output (C6) | 0.55 s | 15.88 KB | 16.00 KB | +| Aggregator | P4 | per computation output (C7+fold) | 81.66 s | 10.69 KB | 14.16 KB | ## Integration test (`test_trbfv_actor`) @@ -54,14 +54,14 @@ | ------------------------------------------- | ------------ | | Starting trbfv actor test | 0.00 | | Setup completed | 3.08 | -| Committee Setup Completed | 20.23 | +| Committee Setup Completed | 20.22 | | Committee Finalization Complete | 0.01 | -| ThresholdShares -> PublicKeyAggregated | 309.65 | -| E3Request -> PublicKeyAggregated | 312.28 | +| ThresholdShares -> PublicKeyAggregated | 314.97 | +| E3Request -> PublicKeyAggregated | 317.62 | | Application CT Gen | 0.32 | | Running FHE Application | 0.00 | -| Ciphertext published -> PlaintextAggregated | 82.61 | -| Entire Test | 418.55 | +| Ciphertext published -> PlaintextAggregated | 81.66 | +| Entire Test | 422.92 | ### Thread pool (same process as integration test) @@ -80,21 +80,21 @@ | CalculateThresholdDecryption | 0.57 | 1 | 0.57 | | GenEsiSss | 0.13 | 3 | 0.38 | | GenPkShareAndSkSss | 0.23 | 3 | 0.69 | -| ZkDecryptedSharesAggregation | 8.66 | 1 | 8.66 | -| ZkDecryptionAggregation | 51.72 | 1 | 51.72 | -| ZkDkgAggregation | 20.93 | 1 | 20.93 | -| ZkDkgShareDecryption | 1.50 | 6 | 8.98 | -| ZkNodeDkgFold | 63.68 | 3 | 191.04 | -| ZkPkAggregation | 2.15 | 1 | 2.15 | +| ZkDecryptedSharesAggregation | 8.51 | 1 | 8.51 | +| ZkDecryptionAggregation | 51.14 | 1 | 51.14 | +| ZkDkgAggregation | 21.24 | 1 | 21.24 | +| ZkDkgShareDecryption | 1.51 | 6 | 9.07 | +| ZkNodeDkgFold | 64.91 | 3 | 194.73 | +| ZkPkAggregation | 2.19 | 1 | 2.19 | | ZkPkBfv | 0.35 | 3 | 1.04 | -| ZkPkGeneration | 1.39 | 3 | 4.16 | -| ZkShareComputation | 2.75 | 6 | 16.47 | -| ZkShareEncryption | 2.55 | 24 | 61.20 | -| ZkThresholdShareDecryption | 6.23 | 3 | 18.69 | -| ZkVerifyShareDecryptionProofs | 0.10 | 3 | 0.31 | -| ZkVerifyShareProofs | 0.24 | 5 | 1.20 | - -Sum of tracked operation wall time: **390.36 s** (often much larger than end-to-end wall clock +| ZkPkGeneration | 1.39 | 3 | 4.17 | +| ZkShareComputation | 2.77 | 6 | 16.65 | +| ZkShareEncryption | 2.59 | 24 | 62.19 | +| ZkThresholdShareDecryption | 6.17 | 3 | 18.50 | +| ZkVerifyShareDecryptionProofs | 0.10 | 3 | 0.29 | +| ZkVerifyShareProofs | 0.23 | 5 | 1.17 | + +Sum of tracked operation wall time: **394.71 s** (often much larger than end-to-end wall clock because work runs in parallel). ## Raw circuit benchmark JSON (Nargo) diff --git a/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr b/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr index dc6304f41..67f6a513b 100644 --- a/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr +++ b/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr @@ -8,10 +8,15 @@ //! (C7, both `noir-recursive-no-zk`). The final `bb prove -t evm` proof of this circuit is what the //! contract verifies on-chain. //! `committee_hash_hi` / `committee_hash_lo` bind the proof to the full on-chain committee ordering. +//! +//! Committee sizing: `committee_members` covers the **full registered committee** of size +//! `N_PARTIES`, matching `topNodes` on-chain. The `T + 1` participating `party_ids` (extracted +//! from the inner C7 proof) are indices into that full committee. `committee_members` must be +//! 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; use lib::math::commitments::compute_vk_hash; use lib::math::committee_hash::committee_hash_limbs; @@ -45,7 +50,7 @@ fn main( c7_public: [Field; C7_PUBLIC_LEN], c6_fold_key_hash: pub Field, c7_key_hash: pub Field, - committee_members: [Field; H], + committee_members: [Field; N_PARTIES], committee_hash_hi: pub Field, committee_hash_lo: pub Field, ) -> pub (Field, [Field; T + 1], [Field; T + 1], [Field; T + 1], [Field; MAX_MSG_NON_ZERO_COEFFS]) { @@ -77,6 +82,12 @@ fn main( party_ids[i] = c7_public[C7_PARTY_ID_OFFSET + i]; expected_sk[i] = c6_fold_public[C6_FOLD_PREFIX_LEN + C6_FOLD_SK_COL_OFFSET + i]; expected_esm[i] = c6_fold_public[C6_FOLD_PREFIX_LEN + C6_FOLD_ESM_COL_OFFSET + i]; + + // Range-check each participating `party_ids[i]` against the full committee size + // `N_PARTIES`, so `committee_members[party_ids[i]]` is a well-defined committee slot. + // Structural pre-condition for future per-party signer/identity constraints. + let id = party_ids[i] as u32; + assert(id < N_PARTIES); } let mut message: [Field; MAX_MSG_NON_ZERO_COEFFS] = [0; MAX_MSG_NON_ZERO_COEFFS]; diff --git a/circuits/bin/recursive_aggregation/dkg_aggregator/src/main.nr b/circuits/bin/recursive_aggregation/dkg_aggregator/src/main.nr index 4bbd03ac6..c7b395c1c 100644 --- a/circuits/bin/recursive_aggregation/dkg_aggregator/src/main.nr +++ b/circuits/bin/recursive_aggregation/dkg_aggregator/src/main.nr @@ -8,6 +8,12 @@ //! The final `bb prove -t evm` proof of this circuit is what the contract verifies on-chain. //! `committee_hash_hi` / `committee_hash_lo` bind the proof to `keccak256(abi.encodePacked(topNodes))` //! (128-bit limbs); the contract recomputes the hash from on-chain `topNodes`. +//! +//! Committee sizing: `committee_members` covers the **full registered committee** of size +//! `N_PARTIES`, matching `topNodes` on-chain. The H folded `party_ids` are indices into that +//! full committee (today `H == N_PARTIES`; after future honest-set selection `H` may be smaller +//! than `N_PARTIES`). `committee_members` must be supplied in the same order as on-chain +//! `topNodes`; the hash equality is what pins that order. use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; use lib::configs::default::dkg::L_THRESHOLD; @@ -53,7 +59,7 @@ fn main( nodes_fold_key_hash: pub Field, c5_key_hash: pub Field, party_ids: pub [Field; H], - committee_members: [Field; H], + committee_members: [Field; N_PARTIES], committee_hash_hi: pub Field, committee_hash_lo: pub Field, ) -> pub (Field, [Field; H], [Field; H], Field) { @@ -61,6 +67,15 @@ fn main( assert(expected_hi == committee_hash_hi); assert(expected_lo == committee_hash_lo); + // Range-check each folded `party_ids[i]` against the full committee size `N_PARTIES`. + // This makes `committee_members[party_ids[i]]` a well-defined committee slot (today + // `H == N_PARTIES`; preserves soundness once `H < N_PARTIES`) and is the structural + // pre-condition for future per-party signer/identity constraints. + for i in 0..H { + let id = party_ids[i] as u32; + assert(id < N_PARTIES); + } + verify_honk_proof_non_zk( nodes_fold_vk, nodes_fold_proof, diff --git a/circuits/lib/src/math/committee_hash.nr b/circuits/lib/src/math/committee_hash.nr index 87d35e799..da3edbbb2 100644 --- a/circuits/lib/src/math/committee_hash.nr +++ b/circuits/lib/src/math/committee_hash.nr @@ -6,12 +6,16 @@ //! `keccak256(abi.encodePacked(addresses))` split into hi/lo 128-bit limbs. //! Must match `CommitteeHashLib.sol` and `e3_utils::committee_hash`. +//! +//! The hash is taken over the **full registered committee** of size `N_PARTIES` +//! (matches `topNodes` on-chain). It is not parameterised by `H` (the +//! folded honest-set size) because `topNodes` always carries the full committee. -use crate::configs::committee::micro::H; +use crate::configs::committee::micro::N_PARTIES; use keccak256::keccak256; global ADDRESS_BYTES: u32 = 20; -global COMMITTEE_PACKED_BYTES: u32 = H * ADDRESS_BYTES; +global COMMITTEE_PACKED_BYTES: u32 = N_PARTIES * ADDRESS_BYTES; fn field_to_address_bytes(member: Field) -> [u8; 20] { let be: [u8; 32] = member.to_be_bytes(); @@ -30,10 +34,11 @@ fn bytes32_to_field(bytes: [u8; 32]) -> Field { value } -/// Hash ordered committee members and return `(committee_hash_hi, committee_hash_lo)` public limbs. -pub fn committee_hash_limbs(members: [Field; H]) -> (Field, Field) { +/// Hash the ordered full committee (`N_PARTIES` members, matching on-chain `topNodes`) and +/// return `(committee_hash_hi, committee_hash_lo)` public limbs. +pub fn committee_hash_limbs(members: [Field; N_PARTIES]) -> (Field, Field) { let mut packed = [0u8; COMMITTEE_PACKED_BYTES]; - for i in 0..H { + for i in 0..N_PARTIES { let addr = field_to_address_bytes(members[i]); for j in 0..20 { packed[i * 20 + j] = addr[j]; 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 cc10eb960..f348226be 100644 --- a/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs +++ b/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs @@ -334,6 +334,18 @@ pub fn prove_dkg_aggregation( )); } let h = input.node_fold_proofs.len(); + // `committee_addresses` must cover the full registered committee of size `N_PARTIES` + // (matches on-chain `topNodes`), so `committee_hash_limbs` hashes the right list and + // `party_ids[i]` is a valid index into `committee_members`. Today `H == N_PARTIES`; once + // honest-set selection makes `H < N_PARTIES`, this check should compare against the + // preset's `N_PARTIES` directly rather than `h`. + if input.committee_addresses.len() != h { + return Err(ZkError::InvalidInput(format!( + "committee_addresses length {} does not match expected N_PARTIES ({})", + input.committee_addresses.len(), + h, + ))); + } let slot_indices: Vec = (0u32..h as u32).collect(); let nodes_fold_proof = generate_sequential_nodes_fold( prover, @@ -452,6 +464,16 @@ pub fn prove_decryption_aggregation_jobs( artifacts_dir, )?; + // `committee_addresses` must cover the full registered committee of size `N_PARTIES` + // (matches on-chain `topNodes`); the Noir circuit's `committee_members: [Field; N_PARTIES]` + // shape enforces this — supplying the wrong length surfaces as `WitnessGenerationFailed`. + // The non-empty guard catches the common misuse early with a clearer error. + if committee_addresses.is_empty() { + return Err(ZkError::InvalidInput( + "prove_decryption_aggregation_jobs: committee_addresses must equal on-chain topNodes (N_PARTIES)".into(), + )); + } + let (committee_hash_hi, committee_hash_lo) = e3_utils::committee_hash::committee_hash_field_hex(committee_addresses) .map_err(|e| ZkError::InvalidInput(e.to_string()))?; diff --git a/packages/enclave-contracts/contracts/Enclave.sol b/packages/enclave-contracts/contracts/Enclave.sol index 29cf0103c..ac806dfce 100644 --- a/packages/enclave-contracts/contracts/Enclave.sol +++ b/packages/enclave-contracts/contracts/Enclave.sol @@ -419,6 +419,9 @@ contract Enclave is IEnclave, OwnableUpgradeable { if (e3.proofAggregationEnabled) { require(proof.length > 0, ProofRequired()); + // Reaching `CiphertextReady` implies the committee was published, so + // `getCommitteeHash` is guaranteed non-zero here; the registry still + // reverts with `CommitteeNotPublished` if that invariant ever breaks. bytes32 committeeHash = ciphernodeRegistry.getCommitteeHash(e3Id); success = e3.decryptionVerifier.verify( keccak256(plaintextOutput), diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol index 08e4336b1..d30ad9e01 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol @@ -8,240 +8,124 @@ pragma solidity >=0.8.21; uint256 constant N = 2097152; uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 127; -uint256 constant VK_HASH = 0x23be1dd720adbe93a20641346295fa6b91a060ce9532c5274e91a438434d4fa0; +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( - 0x05d32cc409554430668b52564a0dd3e7c5e26b2e970ab80e13285a2e02df18dc - ), - y: uint256( - 0x185e84c9e227fa12f2f951b68b41c73ccda11518663b45d46a8d8ce054f7d363 - ) + ql: Honk.G1Point({ + x: uint256(0x21b909972bfe373b93f74e8d1b23d4f9da2d359033f9bea3bfd0dff4b14583b7), + y: uint256(0x00cca3f61b1c83f29f138790d10246931d7ac90c67f0868928a8f87f115bae3f) }), - qr: Honk.G1Point({ - x: uint256( - 0x2b453c47255c6d5225dc081251a4a781157691c23d193ff46de4e0f41fe1b3e9 - ), - y: uint256( - 0x18deab5160dcd08f2a7bc2c767fed54c056a62565af8291196916797400802c7 - ) + qr: Honk.G1Point({ + x: uint256(0x282acc4814ca5bff02f14c4a58b751ecc70c1c01494610385bff82d9072bf548), + y: uint256(0x1d4bd882f21883ba61543964458224dbd4a53595d002e18c0db812390c45416c) }), - qo: Honk.G1Point({ - x: uint256( - 0x2767043cf43b790a3a17ba0ac68a8316c631a2220f95e6ae9f5fdb6c7dccb11b - ), - y: uint256( - 0x26617bb06961db2a4c4a87f51155ef06e3e820f644af379137703f484ac8d0c5 - ) + qo: Honk.G1Point({ + x: uint256(0x1dd7761ff2c72c1af984fb53d1b3b42460c3323214a2d7dde42c62973d34198e), + y: uint256(0x13040272a4c0bedcfdec2b313f9efe3ee9a0bbd5ca58d9432dc8d832127e99b0) }), - q4: Honk.G1Point({ - x: uint256( - 0x2128584d8d874764f2d7926d78d7f8f5b7dc9eb8b5121f3fa3170235fd8fe618 - ), - y: uint256( - 0x1d564fa1501c0c8a198c5b4597bf87f1a0c6d6331359f99b05b67ce46d278ea0 - ) + q4: Honk.G1Point({ + x: uint256(0x174f2955dfa54bf68658a287d4658af6994ecd888993452c4c14f1c60871ce56), + y: uint256(0x0cda4ce133f1492a2f13a0e29c6df44b8e697f84e481f1b2dbf2d8d741be5e04) }), - qm: Honk.G1Point({ - x: uint256( - 0x17fcefb84130f269e76bd0f308fb32344474986c718e43ead5ea21dd3cb10334 - ), - y: uint256( - 0x22b522a965b4d856e21f8eb12658b17da267d65fe1e0772ce97f26fca9879e37 - ) + qm: Honk.G1Point({ + x: uint256(0x22ac07bc4c7d102054ba8dc18954f43d66ed6c57ede3a78e5fe44e80ab26daff), + y: uint256(0x01a5cce0a2e3607ae4fc406e7379aed53d7cd2cdb0d3a14e759531cee30cb9ea) }), - qc: Honk.G1Point({ - x: uint256( - 0x2517978cb349ef864c272efe890ffdce75ed7790902c37e7087cf9790e7f1cdc - ), - y: uint256( - 0x042f1330f591b1345d8675358b31393c21b7416567bbb4db887a0f5c877d2a68 - ) + 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( - 0x23de7f17e2a2a904521bcb9ae1173eae1ac10fc5d4d1a2389cc51758fd89d4b3 - ), - y: uint256( - 0x0363de80dbae03838b654e9ee7ddfe17e891983d01042db26b0afd145d77e692 - ) + qArith: Honk.G1Point({ + x: uint256(0x2624d4d9d7eac2515cb4b322afc263ddc87c535791e2206eef64bc24024968e1), + y: uint256(0x105a6a9c813245babf469aebeafca60e878d41b05f79125dedf362bee561b5ec) }), - qDeltaRange: Honk.G1Point({ - x: uint256( - 0x0bcde00ecf5d0958bdd6224d6a54719e72fd904eb163dcf5ea324daafacf2b7f - ), - y: uint256( - 0x2e7adff5b94dd989e0d9dca6eb419889621ec14f5d570543168e4e42409c83c6 - ) + qDeltaRange: Honk.G1Point({ + x: uint256(0x1cec49a84cd964f7dccf24f37f746eb4660ffa446ba4e79d04582d86fc5fb2be), + y: uint256(0x16acc276874333a56f75e2c79d9e723e9ac1bb18d1ab5bd579a3ab1702464ed0) }), - qElliptic: Honk.G1Point({ - x: uint256( - 0x1b0f171ae66227192763b958126f202bbe0bf894455dc70542a65b6ce49672ab - ), - y: uint256( - 0x080e380b2fbc0db5614dcd7903bb1837b5814f1ea6d315d547dc63145f12fd9f - ) + qElliptic: Honk.G1Point({ + x: uint256(0x006554df9837516dfb90ce208134e4b81d29ebf81032b08330501733f5f20d6a), + y: uint256(0x0ff31f52484554b3123ffc5c911d928e91ee373db03b305bd1350ae27aba4169) }), - qMemory: Honk.G1Point({ - x: uint256( - 0x2508e83664595b6817e4b2dc994c7f5fcd07b6e0d739f06df3aaef2aabb4a354 - ), - y: uint256( - 0x1ceac93c1a2560a969a76c9203eac99036b3f50471941f7cb9f4be3381de5ab7 - ) + qMemory: Honk.G1Point({ + x: uint256(0x28fad415a8ba66a6c2d15321977f696a033b56580937a63c0be78be9ccdbf00a), + y: uint256(0x229fa12d35300e25b3095908acfed5751d51e93cd6ecf4af6757ba5a4c540c18) }), - qNnf: Honk.G1Point({ - x: uint256( - 0x0e154fcc6ac38a216d26f2a5eb005c7a17d7158ccc496355c736b7ee7085296a - ), - y: uint256( - 0x11a0c91986352495d1b391c415d947577348f36697aa570238d2b19b50353902 - ) + qNnf: Honk.G1Point({ + x: uint256(0x27769c90ab027f74a7f86fbe3a1832e41518cd4975e8ba110311664df43f0ce3), + y: uint256(0x24a8f977133bcb034382e4cbcd3e335373ae5aac0e67824a2647554a52536b87) }), - qPoseidon2External: Honk.G1Point({ - x: uint256( - 0x1e1f6dce6c7b8406de819fe1d10524ec3161ec0885eb3695a86a5107e1d83589 - ), - y: uint256( - 0x1fe55e0e37c825072934535d7f33de9196a1b4c8b71a72a958761e4a1f55531e - ) + qPoseidon2External: Honk.G1Point({ + x: uint256(0x2eb3443efed96b06718b28d1bfbbc35a407b6af60f720ba5a9d0ac78501f0ed2), + y: uint256(0x17022aa4435561f83bdddcaa9174723a1e31c11d128a3455edc0b21bf22d334b) }), - qPoseidon2Internal: Honk.G1Point({ - x: uint256( - 0x0e5d05348574583cfb629d78f4ca745d482d5c7399082b33cfdb7edf59fc6ab5 - ), - y: uint256( - 0x1f1c05396894b1b80d8162391a7a1c24b2a5dbd1294cb683434d8f6345f379b9 - ) + qPoseidon2Internal: Honk.G1Point({ + x: uint256(0x2ef9e66a814fe6821f53a2a2e1e93ac8630a347d7c9fee2afd2edcdc13bc0548), + y: uint256(0x0ebdcee17969483e898170e905ff58418ad7e99173fa87c028966bd8c040c923) }), - s1: Honk.G1Point({ - x: uint256( - 0x220924e2d368b51ce6265fb3077f544eec065123aa48ead8cc9ea8d8138a1d80 - ), - y: uint256( - 0x0a77f2cecf7b6c95615d4cb84f44a68196df673bcdfa9045fa4430991de7459d - ) + s1: Honk.G1Point({ + x: uint256(0x235a96328f656f5e8e3935de342e7ffb06d3400d2e11e03ff3f5e9729dec07de), + y: uint256(0x23b38403e02d9a93b48b7e21c10d7360fd7a39299f6aae2174d47e2df318775f) }), - s2: Honk.G1Point({ - x: uint256( - 0x01301eda90b3c69ca965b47d874af1a63a6a4db818b21435579d10bd7866c253 - ), - y: uint256( - 0x2ebd7190a5695c302a27c81884302282dbd34a86d9f3c1b6eeb28dfcfba69fd1 - ) + s2: Honk.G1Point({ + x: uint256(0x2c15698c01375d97f94676f1bbdb3ba5b157bacaadec11b12cf074cf212f6e12), + y: uint256(0x141ec5c8ba7190c9cb1fab4f19e817d9d8f9cb2c0cedd614d4f7820a2f7c4abb) }), - s3: Honk.G1Point({ - x: uint256( - 0x11c240beac486e3d294d585995394c3e895f8cabc94049de498aac199237770b - ), - y: uint256( - 0x269695fc9c8c1caa5ae553b9c0ccfb5aadb257f8bf5bfad76bb4d304acc7cac9 - ) + s3: Honk.G1Point({ + x: uint256(0x22e7871e851cfc6514318d6f16d1c34305dd0e3c0dbe39df3527feda3b0d1eb0), + y: uint256(0x078c546da57b7d1340a5a5b11922ab15592a2c3d32553532c318f0a238768a01) }), - s4: Honk.G1Point({ - x: uint256( - 0x245003058192b706ca21dd546a96ee58afd8679394d9518da3344ca4b21be2ad - ), - y: uint256( - 0x29a048078639061c011b8b6e3978dc2f03ed0f2bc24208c8e36fc8434ed201b6 - ) + 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( - 0x01f63ed4a2629fd4c6cc58c73374226a59212d60bf9f9ee7d71fb3b1040f4442 - ), - y: uint256( - 0x1376d6bcc1ce592b45c346209c9fd2ddbf5f3470fc261c1f48568655638bf747 - ) + id1: Honk.G1Point({ + x: uint256(0x162e6ffc2acbbe037aa8301684ed9e2d850a2c83a3c1a3164453b5c2187c8c75), + y: uint256(0x22f0647302fbfc4d83670140b7ec0cd606fd991bd3e7cebeee96ee3b6169e538) }), - id2: Honk.G1Point({ - x: uint256( - 0x1830b0efd90ca2e4fea4529f59a1ddb537d8b564810ad2af7837e8e8a5b9e927 - ), - y: uint256( - 0x232234bd7890b992269353e2ebb7872fe31dc86b650e0f560a06da9dbc8c3d90 - ) + id2: Honk.G1Point({ + x: uint256(0x1fc1d8dce21a638cd9695d5ed2d796b7b1423fe391391cbf0076dabcd5b1229c), + y: uint256(0x2e4d338298032c5426ca47e6970b8ef0b055728771a8ad6b455f4d3001abd402) }), - id3: Honk.G1Point({ - x: uint256( - 0x0c74b589240ea2ade23d38fff23b2658110d1cc3f4d201c7d9c65808e36771d9 - ), - y: uint256( - 0x162c019fc641e6d049308d7014c1acd4a0cd922871ad697fbdc394b7129e4f45 - ) + id3: Honk.G1Point({ + x: uint256(0x011c7ccc37d9abaf9dd6ffb88f045f8f6adb02dde453b8645b7a5461356255f8), + y: uint256(0x0186d6fa335ec0a6179c9edeb2cfca478103eb4989218cd11ddeb6a4762ff294) }), - id4: Honk.G1Point({ - x: uint256( - 0x21e71e95b4ee9bb8a3122acd478de72a2261de0c897d040edc077aab05f3fdb7 - ), - y: uint256( - 0x275e27d4a4d9c0c15d998eed6b60a44836146cd9816b1691969ecc8172e7b3a4 - ) + 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( - 0x10be2fc7531b16bcf869314c6e9ad92e822f3ca9cd1f2fc4badd9b68e3df3c60 - ), - y: uint256( - 0x0d9bbf7771aa706695d42688be4788f4d722e5ab38d27acf721626fa295bfd6d - ) + 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 03ed6716d..7dcc0571c 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol @@ -8,240 +8,124 @@ 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 = 0x2937fa911eeb39d6d5a928094331339f60b0e50a6b7d0dd2ae546d8209fc933f; +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( - 0x26ad9ec73647e6cbac68fb3eecb522376fdd482a519c7e6a14271e668bc1ddaf - ), - y: uint256( - 0x2360a0b5ca8b242fe62ba4b68f647f4dcb4441f2c46c184364514a78d273acd9 - ) + ql: Honk.G1Point({ + x: uint256(0x1d906d083872617485399605e6b8d7e0eea0a10f71271b8c59adf24cc8339be7), + y: uint256(0x26cad07a2734aa9aee4ce7b358d3043b357b9c91a96529d54f52520050342e9a) }), - qr: Honk.G1Point({ - x: uint256( - 0x226a163c49844e7a6cc7287edcf1b26c3597e26de9c17bf2961e210bcfadf895 - ), - y: uint256( - 0x08c9455e7c33f67894eb5102d6b0e8dbb70fac2337edb46cf442e89896a1d02d - ) + qr: Honk.G1Point({ + x: uint256(0x246cfefd937a61df24c91a5f3fe31de8e7ce3f17837637c0e1f55e5889f58c77), + y: uint256(0x11bed90de8b7c6acb3b363d70850528c89b62b73d9f9b83a2ca30bce346bad94) }), - qo: Honk.G1Point({ - x: uint256( - 0x2b753cc3a52c28ac2e053efc2e6e95e83cbd8353eb2f4a784cf783681f701400 - ), - y: uint256( - 0x24e380eed26b1da529be4a2666ad42e6668f4be2caec72213b757834da93502c - ) + qo: Honk.G1Point({ + x: uint256(0x0e932b810103be7429befc3ade0b3c06617473d69778daad01d4b62e6ea89507), + y: uint256(0x1b808239e3a1eca0011029525cc8fbfa471341d8d94df76b1c17718216f5eeda) }), - q4: Honk.G1Point({ - x: uint256( - 0x1a2224b4986b1ff6f1c1949345d84863f0bf504727d65eb04e58073c5a890047 - ), - y: uint256( - 0x2715c720aeaf5b213505cf7902e376f0f1d4d22319e8368fc7303ac4e9a12a94 - ) + q4: Honk.G1Point({ + x: uint256(0x0c18563d13b6db7effc22340fc74a2e7b2f9dbb0b42d3f0acc47331f3558dae9), + y: uint256(0x2ff93536486524c1c58a883b55284b278c7efdafc5b41808d88fce3243b11d69) }), - qm: Honk.G1Point({ - x: uint256( - 0x25b47f2902609f550548d0a2c27c5552d9b51dc112e8e9b263ec80688e33635c - ), - y: uint256( - 0x1ccda8f90b56da93afacb2fe218966e07654a5b0d136a37ff11a9188b4d8bfb5 - ) + qm: Honk.G1Point({ + x: uint256(0x104aa9b4476927b9c3df51c532b1cae7b87f5b7ab9373da43777be83108dbca7), + y: uint256(0x1cd279d089fb417574bdfff9320b8ef459196f7841d15f10ef03e78999520fe6) }), - qc: Honk.G1Point({ - x: uint256( - 0x23ea11b593f242c992a3de9b9c07d9954cd851d32fec4469c5aa71a814eda310 - ), - y: uint256( - 0x0b6827cfc9288cc0d66de88351195cdab6fdcd2ae3abd20590eefe3d608ca1e2 - ) + 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( - 0x117dc34d0c135ba99861d8eb4b0ba9e2454b11353971f8f754147edeee218f25 - ), - y: uint256( - 0x1a52d059eea878acde805797fed889677b7504c05e08674d9ce8b7ed57893699 - ) + qArith: Honk.G1Point({ + x: uint256(0x2efb699f4c4dbbd9ec2552bfd41f42f8a5e958cc50ff9dc953be33140272458a), + y: uint256(0x06c6d7d7d0a685224f444ac30d3fa678760361d759abc9e7a229ea8979703e61) }), - qDeltaRange: Honk.G1Point({ - x: uint256( - 0x199f74af36a62f790433377dfa9953d9d79fe5f3f843a257ebdfbec208aac0eb - ), - y: uint256( - 0x1729371270b5dc935d448c628bf3d9a84162def09ea5c3c991b18a84270ea98f - ) + qDeltaRange: Honk.G1Point({ + x: uint256(0x019002e97cd41dd882e373b2bc79c3cad6a400244ff1e80aae06c0b1186b1e20), + y: uint256(0x1fef3eed64626bfd4a793a5330d1eebc6af338a1814c237fae0dc532abe6203a) }), - qElliptic: Honk.G1Point({ - x: uint256( - 0x0ff5ebe74c64d32e3e6a38e021b5aa0c7ccd683325165b0c7ba3fd40c06f3a8d - ), - y: uint256( - 0x2792de8ab62b7a8ac9433df3b9aa53506a6aa0d8bd30faa62b73ecde7d3e1ac7 - ) + qElliptic: Honk.G1Point({ + x: uint256(0x2d867487b60acfaa537feeae0185cfbcb84315e1f9d8eb13e33fcee51e35cc4d), + y: uint256(0x231c0fe3f3de4990752e61ac8897498223d1cc527808376a7ab250c61b48e170) }), - qMemory: Honk.G1Point({ - x: uint256( - 0x27112e3ba014810ee6ad32c37b61f5b89f7e25aac7d1ff50ef65004f2404c6e4 - ), - y: uint256( - 0x2a994019050ce0aace7a030a29216e89a0dc832c2ec855d2044e996f74b065e8 - ) + qMemory: Honk.G1Point({ + x: uint256(0x1954e635e980037fb7bc3c25c450d12a9a19837e01e3f1d479d51728bb70af6b), + y: uint256(0x2ce76f6c6fe16633bf7c675281759908490fed6ff2829e8997513eb4fcf56f53) }), - qNnf: Honk.G1Point({ - x: uint256( - 0x192328adab0d1f56b4e1028bf449af9cc0d77f53b9c1fca4a5f2f6148ebece7f - ), - y: uint256( - 0x0808887e1f287af5bf7c7d8602d014e3419155a04b3070a1ffc1cc0cbcd50e2d - ) + qNnf: Honk.G1Point({ + x: uint256(0x077d3f2336940aec76438f6c30edddd2e751c56a72ffaf64e9ef476f524daa2c), + y: uint256(0x04b61eb1f9b0837bcc0714bb3c00c190349dacb7ccaab86a6027b910dd2309e0) }), - qPoseidon2External: Honk.G1Point({ - x: uint256( - 0x00e3022b8e21c0f5f462a5c13224d7257b6a3ed0b9d7bd734b6c1125abf633b0 - ), - y: uint256( - 0x094c5338f79be9ea69797ff7b4a15d9aa918f4e50e6a86fb86a95ed8e22da852 - ) + qPoseidon2External: Honk.G1Point({ + x: uint256(0x24a3cef3397bef0d207ce03d64d77ea0a7dabde143471d98cd7360b7685a90b7), + y: uint256(0x165894aaaf725b36f72e71880ff23c78ffa1b757304b00b94c56f7e9886936ce) }), - qPoseidon2Internal: Honk.G1Point({ - x: uint256( - 0x0798a34a77c41d91638fbca51b0057fb480898be2b7e5ae82bdf54a3cfb2f811 - ), - y: uint256( - 0x24e471b3f0ddc31e775dffa48334e7c6e77f50bfce60d27005b1d280c99ba779 - ) + qPoseidon2Internal: Honk.G1Point({ + x: uint256(0x24bf2806f2e4fdb9a4728a47a888038a8602f1a564cce5439d8a0c89eb04b351), + y: uint256(0x21002a27f9d4cb5b284ad2b3c83e2956e1ecdef6ab8ad07b085a429f82374a77) }), - s1: Honk.G1Point({ - x: uint256( - 0x155ac8307b2528efe8cf165ef29382bea17a6d4cfb7b21890315651cf8718b30 - ), - y: uint256( - 0x2bb8ca70fbf5a1948582af7371e4d4143238ebfd32f1159c23193dbd2b22a03c - ) + s1: Honk.G1Point({ + x: uint256(0x1b1656cadd0b2ab6f8a6e5e22d0dec012d95f87e22432d4acfa44d0272596189), + y: uint256(0x283c907c7278fa3fce6eeadeaaa9c48f7ab8211c94882f851c9fdcabdbac1bdc) }), - s2: Honk.G1Point({ - x: uint256( - 0x19cbadda70765a860e9f7c73ecab703a2c2ddf2d2407d33a8da8173701b64d9d - ), - y: uint256( - 0x0d7029ec77a89543facb74408b4536a5e1ea600dc8d0f31aee86541e81d7b930 - ) + s2: Honk.G1Point({ + x: uint256(0x23f558a747590ffc98f1cec61982a939c17078fb8597d196c5f71436f6135393), + y: uint256(0x0bb3abcbf063633ba4de4a51dec092e6f79c64cf48340c2bb149bd340cc95e9c) }), - s3: Honk.G1Point({ - x: uint256( - 0x275603bf2ba273a074f0791d33ee3f069449dfa2a89a7c69734d2f8fc40149ea - ), - y: uint256( - 0x1db1f55c697e147f29ec0c1aa54400ba4c44277d09d19ca495efa22c11fd4873 - ) + s3: Honk.G1Point({ + x: uint256(0x120874be1571c176bdb51bf3357b34f678084ba91f8d644019ce9cf69f819537), + y: uint256(0x0b28eef919110795820b3818e697556033489a739c2056801c36b3e15b7a16ea) }), - s4: Honk.G1Point({ - x: uint256( - 0x00e93184e1e95febe1cf63bc56e1d5fb1c7ec4dee64066e43ccecfa24e9aedab - ), - y: uint256( - 0x13e68e8e14a84d3477825b856ba254d0af40cc58fe21f276aa95511f4906563f - ) + 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( - 0x12280b9608c56c0fc84d5d14ea31e2ed4e2e9100c22b3f87216bee7bcb78fcb7 - ), - y: uint256( - 0x145a43cd663f4f88784979dbedc733da43611d13a3ce638923fb5bb4f5f7e89b - ) + id1: Honk.G1Point({ + x: uint256(0x00efe1593a923558e7779f10e4753794a59ad0dcc02df6790b72346175b1b7b9), + y: uint256(0x28b0744e7ea51d27a2624e7ffe4ccae8f3c9c6350131555ceedb42078a229a3c) }), - id2: Honk.G1Point({ - x: uint256( - 0x1ff8c6c495764da3398f1eef1688a9a8adb26177c7682642e3955ae3d89478ed - ), - y: uint256( - 0x028b82e61296a2f12eb839e90846a4ab38ea638d4cb97968436a8e27902e1a92 - ) + id2: Honk.G1Point({ + x: uint256(0x0f28ce7d149491697d292002785ff24f6089b458ea01234e4af8eda65b9dd266), + y: uint256(0x24519d3dab7ba27bcf50279756292c7052edff4183446b2ae85617e6dff01883) }), - id3: Honk.G1Point({ - x: uint256( - 0x1860447ca16e85291f8e0aa72314153f7a15572de7ca5bef409a8ef31ee16fb1 - ), - y: uint256( - 0x27fd203c2fede3b848d91415fe7ae6f2abcb8d9d09e732996250f0871d57224c - ) + id3: Honk.G1Point({ + x: uint256(0x27ae1958d5100a4fbe5ae016fdac9ea54c5c4172cc798a38d3f8730c5f481a5d), + y: uint256(0x2e28100a9463b43a586fb70dfe15c841ddbd30703149bd5d9fb7273d520554c5) }), - id4: Honk.G1Point({ - x: uint256( - 0x1fd4389b7f9fddd10c6a909708fa0dadd7813a27c5aa904c74fe07f2ee366416 - ), - y: uint256( - 0x2292fe69c9250db970af41a14b5d469d8a87d92128f54b18d5642689c35cd02b - ) + 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( - 0x21bec12b46400ddfce58bfe6d2f403895eda4bd1718990489594da1c3f0fea92 - ), - y: uint256( - 0x060b340ca69b6e38e8426ce9b8f6a613d862edb65bea3c8a14b1fb4680caac73 - ) + 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 e38dd6fc0da1b347d285f77634be267c6ddc4078 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Tue, 19 May 2026 16:44:43 +0200 Subject: [PATCH 11/20] address small fixes --- crates/sortition/src/sortition.rs | 9 +++++++-- .../artifacts/contracts/Enclave.sol/Enclave.json | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/sortition/src/sortition.rs b/crates/sortition/src/sortition.rs index 6f20c04ce..4a9250c66 100644 --- a/crates/sortition/src/sortition.rs +++ b/crates/sortition/src/sortition.rs @@ -736,8 +736,13 @@ impl Handler for Sortition { type Result = (); fn handle(&mut self, msg: GetCommitteeMembersRequest, _: &mut Self::Context) -> Self::Result { - let members = self.get_committee(&msg.e3_id).map(|c| c.members().to_vec()); - let _ = msg.reply.do_send(CommitteeMembersResponse { members }); + trap(EType::Sortition, &self.bus.clone(), || { + let members = self.get_committee(&msg.e3_id).map(|c| c.members().to_vec()); + msg.reply + .try_send(CommitteeMembersResponse { members }) + .map_err(|e| anyhow!("committee members reply send failed: {e}"))?; + Ok(()) + }) } } diff --git a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json index 67a36b98f..4eec94d7b 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-e33d1cfedc69d8ad74eec5a84c8ef358a020ec1a" + "buildInfoId": "solc-0_8_28-0431dc09dff33382cec136e860cfd8d32d55db8a" } \ No newline at end of file From ee08919970761007c4093a7b3be5d16b7a1d3cff Mon Sep 17 00:00:00 2001 From: 0xjei Date: Tue, 19 May 2026 17:07:58 +0200 Subject: [PATCH 12/20] update bfv vk binding values --- .../results_insecure/crisp_verify_gas.json | 62 +- .../results_insecure/integration_summary.json | 102 +- .../benchmarks/results_insecure/report.md | 90 +- circuits/benchmarks/scripts/run_benchmarks.sh | 4 + .../scripts/sync_bfv_vk_binding_fixture.sh | 27 + .../bfv/honk/DecryptionAggregatorVerifier.sol | 1635 ++++++++++++----- .../bfv/honk/DkgAggregatorVerifier.sol | 1635 ++++++++++++----- .../test/BfvVkBindingIntegration.spec.ts | 68 +- .../test/fixtures/bfv_vk_binding/README.md | 24 +- .../bfv_vk_binding/folded_artifacts.json | 8 +- 10 files changed, 2603 insertions(+), 1052 deletions(-) create mode 100755 circuits/benchmarks/scripts/sync_bfv_vk_binding_fixture.sh diff --git a/circuits/benchmarks/results_insecure/crisp_verify_gas.json b/circuits/benchmarks/results_insecure/crisp_verify_gas.json index 574f0d0e9..cad618487 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": 3042639, - "user": 2972941, - "dec": 3553795 + "dkg": 3042712, + "user": 2972857, + "dec": 3553819 }, "source": "folded_proof_export_plus_crisp_verify_test", "artifact_sizes_bytes": { @@ -17,17 +17,63 @@ }, "calldata_gas": { "dkg": { - "proof": 169968, + "proof": 170052, "public_inputs": 6144, - "total": 176112 + "total": 176196 }, "dec": { - "proof": 169956, + "proof": 169980, "public_inputs": 17304, - "total": 187260 + "total": 187284 + } + }, + "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.109944514, "runs": 3, "total_seconds": 0.329833543 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 0.607762083, "runs": 3, "total_seconds": 1.823286249 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 0.557054791, "runs": 1, "total_seconds": 0.557054791 }, + { "name": "GenEsiSss", "avg_seconds": 0.125857458, "runs": 3, "total_seconds": 0.377572374 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 0.226792222, "runs": 3, "total_seconds": 0.680376666 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.377944667, "runs": 1, "total_seconds": 8.377944667 }, + { "name": "ZkDecryptionAggregation", "avg_seconds": 48.469535625, "runs": 1, "total_seconds": 48.469535625 }, + { "name": "ZkDkgAggregation", "avg_seconds": 20.0494395, "runs": 1, "total_seconds": 20.0494395 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 1.483909729, "runs": 6, "total_seconds": 8.903458377 }, + { "name": "ZkNodeDkgFold", "avg_seconds": 61.021601069, "runs": 3, "total_seconds": 183.064803208 }, + { "name": "ZkPkAggregation", "avg_seconds": 2.118477708, "runs": 1, "total_seconds": 2.118477708 }, + { "name": "ZkPkBfv", "avg_seconds": 0.337032069, "runs": 3, "total_seconds": 1.011096209 }, + { "name": "ZkPkGeneration", "avg_seconds": 1.3624095, "runs": 3, "total_seconds": 4.087228501 }, + { "name": "ZkShareComputation", "avg_seconds": 2.714979847, "runs": 6, "total_seconds": 16.289879085 }, + { "name": "ZkShareEncryption", "avg_seconds": 2.516941928, "runs": 24, "total_seconds": 60.406606294 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 6.083372944, "runs": 3, "total_seconds": 18.250118833 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.096223944, "runs": 3, "total_seconds": 0.288671834 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.214040633, "runs": 5, "total_seconds": 1.070203166 } + ], + "operation_timings_total_seconds": 376.15558663, + "timings_seconds": [ + { "label": "Starting trbfv actor test", "seconds": 0e-9 }, + { "label": "Setup completed", "seconds": 3.066766584 }, + { "label": "Committee Setup Completed", "seconds": 20.21209275 }, + { "label": "Committee Finalization Complete", "seconds": 0.006218875 }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 299.47482725 }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 302.025307167 }, + { "label": "Application CT Gen", "seconds": 0.311998458 }, + { "label": "Running FHE Application", "seconds": 0.003517959 }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 78.562423209 }, + { "label": "Entire Test", "seconds": 404.196322167 } + ], + "folded_artifacts": { + "dkg_aggregator": { + "proof_hex": "0x0000000000000000000000000000000000000000000000001cb3e9df822c791200000000000000000000000000000000000000000000000e6e2eb24f99c6e4fe00000000000000000000000000000000000000000000000f0341c256bc7f8f2c0000000000000000000000000000000000000000000000000002945e9bb27e6800000000000000000000000000000000000000000000000ca22d28f7e6b41a5600000000000000000000000000000000000000000000000fa4ce39d3e2c6c64c000000000000000000000000000000000000000000000000c382057b093f299f0000000000000000000000000000000000000000000000000000d2ab1e847eef00000000000000000000000000000000000000000000000732eae124bf33406c00000000000000000000000000000000000000000000000825bba25f971304c000000000000000000000000000000000000000000000000f31aa002ce9a71b020000000000000000000000000000000000000000000000000001c69dee5a348a0000000000000000000000000000000000000000000000082047502ee34e43830000000000000000000000000000000000000000000000098bc3dc090da06bd200000000000000000000000000000000000000000000000a395a0475f5823e140000000000000000000000000000000000000000000000000001d21f325773d10b2b91c7a8b3d057b7bc490148ca03e7c07ae5285593e8de30cc1ac03ac33ea123a54330466872a485fe60682f01940aecf0a16a29400c85c1ff026756e3feb104e48aa44e3c4435c6a81422971f410e652c2a8dbb6dce04531118b73de26b410850267190a569b1e5c0d99d36551817631476dce47dab6ce7ddfa42abee9cd50d8774b5dbe2992589f46d6b0d3c14df64e1f145ecf602d7f07d6bb8b077ad801635d81fb371fc342b18cf95c1280232e65ec5fdf4f7edc0694727528363aded2adf3a83dc8a210463d985cdb56d2f0d39aaeef44f7e2567bec394a51280aaa41d9718c798bf7003a861694b93dc6114eb66daa0cc69e0dbfc33065a442bff1f24da7df7f66206346b9c7f067dc75502807746037df9556453c8cfe619c13bf2002ffe7ada7eaa20f50572c400c4ffc4942970b82e2e42a5569a83f6f7cb7e11032c18d50c12e995d4aec40ac9bef1f9e92449cc1287a6ae05407339c176d21818bac164c691fda117bc7877f2e7bccc8e7d7257e4bb51996272017b55b3dda603b572716b3071143fad60717290ac7522b0944c91980411ff8755372680bdd7192dd2340cf54de8aed55abc38be8e4d864a57bd60f620f2a66f008218a6b5f41d926dc02c0c633628367cb8e460116ca71dbdb7054035eca657119596c3de48187112be8a3281245ce6e500f797cc1432a4d3c09f50d55d3cf2a76c072123062816948f10550ad9cc66240c3475cc3f7e1be8a364b22c852289e341fa94ef710e359bc943797bfdd06360aab35078dcb0f5f34feae065749d4499ce323c0c26276f732dc63c13091a70d853fd6f9d06c67a9219b36ab6cfc590fc247994b9cc26f3325182becff2efcc85ba9a3c8a5d260edc035d0bfc3e0845aabd9d1b893210c0d54eefaab1eaee3b1703468de3bf3b65973d385114a22b661f1b24e99a9a1df7f33c0a098f95b18ac88841857d37fd0d01befbff8eda91f1ee54b9d9bf4d0972e399e72488027be9753aa1dbcf846b5d614dc2e83df5ed880a51ac5672e90569ae383a1ca0dbc467c684df3bc238b9c05156103aa2aef2eb1fdf18fc875926636c6e183c34d39a42e830a4f8894c70c00a993805d0013500ff4cdd8d014620ce10ca69f17a638c92367872e4f56e8aacb16fe54754cf3f56c74e9296ff6823e9d239478326b2dfda0f711567d671b7c617f59f0369ba10265ba61a2debb21dc7411bc598d5fa875f66505485e1674af48502bfb1c69679777cbe6b7a901314fc90eaf3185389206b54c33a72f351e81beb8f7b0d26511d530a8eab1b28e11b330ff20265875c8ac3b81da94e8665e4a60044918614ed85954f5ee5ab759e1512c2e32b793a0d0acbdf5e4e38d9cacbe258b66de1622bb1fb345e98b6173d2a8df716393a447531eaba0be23f1fbded411e71808271a3a82b4f9975c174051569589970a0511743bf47bd75b9f04e2f7a5d4b7deaab3ca11d0dc741998c2a030c6c5458d176c04428bec5a3e0d8e0e5c56a3eee999b10f4e41008d0ee6c500c4d4285ef91799d520036adce78bb4c4d435e0379a267084b998c065e2b5d2c05da4a111ab012aa81a0bf2854c7c58136cb6e2c629474ecd90a5041606bc645022463e9a9e43918859426adcea475191e505ee6a37d3afb0cda9aa1c7c788bb0a88deee1aefc855d078af3172244c5d3f21ddbfc514c0836a3c498dea40a5511c6fb874ae86b70059e90abb007bcef2ef2c247cbc5c6327d1d53a26e8c39123107878df9ad00525610d70a76cb32b6db209f420c58f9fb3b3053cdd884ab03303467453d17b995bb31d2bb667f55e5a6f98af83e14c7d2f71f661cbcb3e29e42855c0566eec6cd80417f89b09a4dad9bbf28e9aeacde3580fe7fbe92cbc8d6e23394556cab576138d117b28865defcf49b06e5b82ccc8137f314a7b3f7c2bec071311be89cf5d785e50c106985d91efc1623bb02487a838a29ba70228de3cd618e92a7a82355306a10a99302db1b6f705e6e6ae73cd5d6e3e7d5787023be31124704e1185bf63c8a0578e04b8fde9c99c2f4f5b96431334d6b4065ff89d8cf905c47b96cd804bb285ce1d70a99679ce64f47a71354031d01399cdcbd0ad1e0e06ce9eca7c849eba54a12dbe233f25e8e7a041c192512e99f964dbe30ae60c9b0d2152494c0e45fe1ee814a0931add16233f30a51e9865d9f39e6c5e956c872b14b51eaa8a1c3607325a9b7daf83937fb73caa1fe79e4791869cdfd75a2a4d662cbb0dd627932ea4a38efd1bbc223e92e269674601bb0042b816acd8f55aadc0186ec668949e62b5cc325bed81f906800ee00b52c5f0c3dc0fc6aada1fd6b5a823e1d846890c9bf6f66d991dd8547b31d71058b9de07eab2192c8afe637c651f161176928f1ada22ee5450992b6410cd20f9c0cb72ed0aa36e7f92d47592c53b0b5e2f62a81ee85e4454543894d2e45ca07656b56cec384333eb8620438e1dbb07d8b15737f5a26091192263237fb3545f2a7c2cb52e27d82fc63dadc2ef6ec41cbf43034f22b18a487f1571b7f49ad6cb43662c1d88884ddfc4c6c636ae714d0ca4003603acb3a8a4ba957d6cfc7cfda372f89a2f389757dbe339dd2171f5351baa09fa66e772ff12885bbf3be23fcce81cb59e6021505417367c2f49821f7823ba2358ab38328713a2efb8139298fbd22899d5669e32ff033d4e760e7d8f9d1b3399208db97688cd9c02c839ca9843236e13c214a2d8598e601cb5697ebc782d31df4ba699e8ee8ae919ab36f57dc5ca0cb0e5ae37e10a6706eb18514783df1e89de03b764cde27719b9536b5e4b9f15842d91513fcff1171e709145b5e5f215721d3f2ec845743d4e522e9f27aa662317ca94c561e19a62a3aab50e1d093f097730e2abd72721923c2513efd225ebd9e4b87073bc3c5b2bde83296d57572211f90af331fdef28dc12ed2ba3e3457e2d30e25d1cd26a66b9948b4682c7ea34143d8dc9ad72dc0c04b2a9828c7fd365f728364267489a5defaad4506e3528fc2313b419a3513eebddc60db948fd08df616379ac05f981d69e9e6ae19d5216b22b9c725c9f56c6c0542f7d4070281c3d16adfa7280d2f7846be2a75c73b99e5e230445761d0b8c4cbea2c0b6f7bad692ced4087f1b72de9ee1385140d3bf59390dfef89575ea75a6c0006f14bc476665d96fbdd305c046b7785bfa3063e49fe20879f97e36487df6604faaac54f65709de50110e7f64b62567aca3d6d1478b222ec9289fc1f61f423f08706ebb039d8a224625cdb422ade922c3c0d15036b3e80328f349c0cef97e0a1260b8eae02768c399d5b0f0f29946fb8a18437fda491114ddfa6945933cdc3521ebc1f70b5a3cffe275b5fa04d6d087e1142dee32f46919f1de02a266f6698e3e1f7326ea2c0e5a0e45dd29d22f412e7d39f0cb8f0fdb140d8a7588a9664f65018b4119f5a9d841f67618f140b5c0b43d85b4765a1b292e1bfa2fe17a6947d95f1abe578060534da85fa9478affe42e5eac1c93ebdf8e050d285507fa80e7e8720eb1212f85fad61f1bfd5781c2f60717013af98afd0b1018f1d3953038b40c827b29ec08ad490c80187c112ad8cb0516affcf68d114715b47322c59b0e1d0f0d7d05d85f30e3c115d84a815a7ca750f54e65380a208d1797c705b503bdb86142e1b3581dffc3bab4936c6bc2ceddb9ba58168ccf34492e26801b14cfa1d6cbc813c6ca494101f98faf1cc503d28cb425effd439e81aa2a35364d8803d7a3099f3ec344b02fed23158c79ae5cc2ad43741d6cfa830fe21448fc263e12436347d0d59429d15f024abb4c992b911607b0e4f3a31d9496830f80f0049b464f3f1d4cb46954e071c61399cc66260135a1c79012993d59aa1b218b5f0f80f334e9274dcca5fad1e7a857adf7674bb638a9d0e1392968058512045719e05e2b0b9924a33e2769d059a9e92001bf1d8ff4a3c467d10c0226515106794a473cdf41066d07fd466d2d26e02adabc066365f55b758e5a7f7de4f11c1c63e88a4dea4ad07b671448ef5648964f5bf16094882acf6039d3d7eb07fd350a575b3c32e6f01c55e761b811e7a2c76e2ea0c2478f11844f709f68a8ba186d0692161774697b1f7fcd284ec73cec608c4d4970b58619e605e6c720bdc0f4a50ba4d96cbb1641bc70af97f43ee092264f76633be11a88b4c693c07633537e850d91d688b0abdfa27ac8224656f773bde1aa49655d53de94684fd4469d5a98251e3a5b80127d39d1cbcd95ac6b9942655b77c697de2a43fa9791a27c1a6b50dd1aeb9197a93cfdee8d8c51cc1603f26f631a7327ad19003afc88c47469cb195b2f2955b16d08495c1b318cb0b58965dfc7f8c37faf153429001e8c9526753fbd07480dd9cf394444fbf38ae59ab4ea32b0f1d98d4f5615abd67e895e03f9fe072ac6572e3170094d4b8ba9279275f46b832a091d5345ead1b14b425157fdaf93112579386dcf605702a30bceb04ccb587b8c9fd67db5a084ebe2c133e1f0de76153de5b3e8e2f0089b682f578cc0c83be4bcb066e2b8d23e41bf850e18ebf504056a78c10aaf829a5613e43adc2d74df5317560b0f92d1bf9c8a7e028dedd9c914ad2fd6e06e8b56e1043312a8ab2562b8a1811e012f39db2a42d53b53e8862002816727324c27a9099190fa3b9d4ebf24614ae352a18aa9853e9e51e8e9fce807c16f4d1ed3fe76046138217835250863c7372ee35d0e44df6ae7f3354a030614efd55b8fc55f11a1e630f4d62a90e541de4057b60f1e340fe299aaec97842419aa5aab5d02cbb119f8667ebd6596282dfaced456b243a1017e636257c00725145c8c1a7d0ced612168ed7785fbf218546cc3773b80d8fe9a2cd899cc67ab2117ff739c3882ac173dcd0997adaa4836f1d565c5fb896fe92d36be4ea354959626ccc45e32ebe2da8450b4c324e9c84e859826d026dc527092ad112e52ca923617a5a64a750f9973cc25777d4e10bf1bc54f366f5820efd2a4d084b14fde269812ed4006701b1da3b123cfea6825b7408aa2fa247b2d4e48e068400a90ef01fe21067a7e5de43254e905a3a430247e8e945fdcf8301f08064fa14fafb084f0b60ff3820f3a15db26cf3b3d472b6c8e568171b75e01a0687857ee3d697359e81d1e7c35fb460bce208eaf60da69cd3970cc07431a6c7b414c3f14335b6ea545082978787372cac6946fa0a5181a8edff08ae4951f0aabe83c4327f9b16fe126542a07056e734d522aa8e3beda35baccfbaf8f8b07d5861f0a93d872676e20d3c1066991d1944305e34c4dfef3dc1880e5df027313a00f04b9ed2f5c98950ce16a0b54648a6fff62bad46d6201674e4f6c21b626f6c16806c354c68dc9b4aa6c9706851f4626499889d5cb3c148f4cbdbbbfeb07e408def8f627609f9fb4b796ea2f6f8f2aa0c153f339c07e47b7f2a875ab7f7ac5cfce654ec42c87600f21d31616b1c342cbffa7ed1b1d4d288a521634153403519363147c9971f04133d89971045398001304fd850beea80c393312d40c8555cb03bc562af7ac3b349b1bf2cd2b92ae6a6792c1d2f64fed85ef4c1a5c6e8dddf8f9a579a54c916c60e82047dd1600e5431125d76b78b8c2a098f18e46f415869bd5dac6f83169c2c8d29f6b4328a0912196820e9d995762575d0f0debeaf9b5b0e2d270c5ce3b76083e36e08401f94afce6ace19ea8fc1fbb1af3f43e109914b7abc6a65fea2ab48a5000af7223958132cb267dbb555c4bc2399e5426b6b3a27105f22c707becfbacb18ad58306fb6fe953b77d8e16475a79f0a1e4722528ed68fea7f1f534c99c8d2db904cc0cda8066d588098e2046d1ea0630ffbfe2011ec234d0c48e8dc6113a19671866253d11e40aefb19efcd13c463084ac484b02f2dedce09c436be72443f4213baa070dd14cf108dff6cb696f02cd05fa5f3e95bae74dd28547273cc1abd77cdbcb00714b116dd81e1e08a4936266aed104058d594954937b52d87ca8ba959a577108d031a69f7febd6c4168783d44fe7287829c8a07371159878bd11fe51cb232f0921f082d176de4cf1ed7bc8b76c7b56de66ec2f752931bb6b6194ea26e0c6f72a98a7e7145a8975a74091cf36fb93f433c969dd565b1bc5cdfcb205cc15c4642f4084b54cee37e360892808ecc8dff740f475bc40d04195bac4a03e9ec76cad1bebe7afd1732cf16794ef6462ab017a80d75ec20e745783bb35803f4b4b617e11526b6e094e5fdbd4f2ac0531a43d1106a93e8052366edba0ecd93ae8e04f282d1333ca58b422ab5234dbc503685b3b1fc93606c1aa5b712b2379e365bf55a51e63f83c7934394efef92012d8cb6800663808ca41d82a5e76c71495a7eebc7629d59d3741b52d96dec1f9741dd750baa5e3e411bdcf0532fd4a0b394a9d911a13b695df6d5ecb4503c9dad0b958664def47dd332b4058bc0e9b1933ac7503c92aca8608d310e70c33a422f0f3f5417380984574f512371b28f66cddfa883c4006b04a9b3c7a3ba0f6804dd6c4df7feee728a5632d04660c6d954179eaeb44542d11b58d31f9ed821464cd0373912dfe4fa22bd19b7065abbc6ceb26a19981842a3965530707b4befb527c0fda0a0cf69e63eda44b13ef3d14863b69dd6978131b11cabd86e10ffed5d750876db45ed410584991599cf4aca734bd4bf51c781127c6ea4ab85b240cb1bd4facec08e873d35dde95121dbdd9d47a8793cb4870df0a1b9a78b9b685580c9dfb20a5e2a15fa3752130f183edcb97c5be401beb6cf107c0adc97caab3584251942f7286cc06fa9f098e3d246e7dbc07d9608b6f5f991a6cac58ef9eb3e90c9f8fe72538df09b2b7b4a577e8b5ad0a2dccc52226746f09b1124b9ecf6a30f133399dccfa765b5d4700dd4b3b9bb71bc2bfead45447c30a868eb5de7f9664fabc6fd95a16fbc657ed53652cb805c3a819bad87ee0eeea07613f74b099d871c110ad32d1351a8aeadf23e6d0e204def4439207b50b2d4202383e3c044f9459cdbe3b7bd460b412b228e7b2f765cc1c5ef19eaf6e417be81389227ca8073c6176027f2d7e0e8421339891238ac8b089c5d1ea4396b43b650d8c183e6314a3c894dd7e6e201332e5c474a08a6047bad68d93909b92aaede71a80a43546806bad9165fd9c426fcbf14a233da388ddbbc7a4b9e6ea3518db232d05a4a41555db710f110c8f3dec06f2efee927471e7c2956b89410d14ceebc5066d005f93104f18c112b2dfec079cb4063f6af28f97c50ff62e6e6c1e5b5d970ab0cd7969c070c5ace23eafb24c3504cd673d1e5dd2fcafe0fb5c5a8116ea8414c1441534aa5ce52008ee910e68da93e170405862dea3ad15760000906447372ffe6c06baccfc669d8c0abbbee6fbc66a530df1c8a15ddecfece85c3e59c99b2f408d6225a32f1c8a5bbcb092d81020c5c943918ca370ee1066b9cda7ac237f25e442ce78c832078a1cca4ed502d30f1d61f4cb9b31fa1c3339535e317b7ad5291c5ab8955c5fa35886e5ef299dee976d839d1659aa0b7798f3a04053fbb3e00a9540ae0e1e203b830e5076fec300443e13d97564007c833faa0afda19fe75d05b04d2cd5243a1c81ea8ad4d236a1cd7a49e82d0fe8260ef7db074dc985bc53273cabfa4034402fd5f0be8f50f61d64063bed010ddf545a2a0819da87aabd2522b9c66a1023ccdd30183e1c297ccf73678f5342d0f6c3fecdb4ea6549f6f9890570d83521bdafb330e97cda239c6c72c27822f493589f2c3a9a7bc45dab9f230dfbb6680e8f3f8d2d51c3d0ac847f7a3110440318e046edefb4d3a5c854747a2d517240483a68a859c5a2517f9d257642c2a95603c14929a57100056111310713bbee85e2eb28d2f56212ec3df83e9bc2d95901789082be5eb67ccfa5d9fb1802b54fb79467cc5b7d9105117cb0be5c3774ca2b71c5c6ebff3be8981994cfad0e12ccee44ba1686ddb45ca9e05c5e827cba306084515b046578f11a9e25b72c089771b551d6cda59fe84c62744869636eaadc43ec56d5050f7fba28c6bafbe32eee5865d3d6d2f7ae5bce8ceeeed068cb1f022728177e68ac18ffac673bad8d2e5ab2e2f406cbc4592907bf333bd5fc49dd9e01370f3c7a9b006cf71557d51827da6014a008f75d47fb0c6eb5f92f4967de88232ad37605c6100984941f681513d9f213a93bed129c2e3e04ade7b03081573e8dee5d8efe325fed0a720c9810144f53c1a953c7076ee5f1762cd845b22f20de501647c107b71a88d034d1383413369152bb657df8a1586b7fcd8ba48363c9305ab33f7f5b244b97287b02a2d924fac5ad4344918be6b863a65249135b38df4166c6d7dae73a36aca560c91ceb203fdfcbbd96b37cdd2a655d2db5fab215991015eb7aa3a6dfef01849794be8f1159c8fb2e0ac69da8979d7646f5aadd53063ec5ab68bd06439c2b9ce469ccbc19acb857ab262170b62d959f7abe12547d52b38a70b048448428f3d56e7c52e029413e913cfdc8db69f7e7d37ebbe49e29c75ce22209ce58b4ff9f1d9fc9fe802af5b6551d3a1756dfe71637f61bcaac595f3aa1989ccde9acd830305f80fbbd2d16fbd3956488af15e5d4fdeae1b5261646dd5fc86d84bb6b45793e77695de20fb8c84900d6f5e8f54a963dcd6d42e073ce04fbe219e2a4af3a8fcf31c952051db25526c091693d249eb293bd4503d1103aecbc69bbe5bb5631483f1b9594442e7fdac9e909c715dcf47095e393e79f8d5b4ab497648032f1629733d97ef3ee1d8878696954e5fa815d5d1ff3ec0ab8ab9723d6837924e59169682902d3db110d7cd26eecc981d01bcb139557c486c744018ca6bd19b5c388a7e077c6e556f706f4ddc0bf4429db4217165962bb635099628fdd49630587a94143c0c89ad8cc0243fa20eb7f688550b80136aba7bb80ef53272fe7d205c34de7b1300c99023a07a06f52b53dbfd06e521156b855efb8221e0f69072c5635a904f63b7d2d011126e6720cf2927e27280d4518b1cacd709c5918b022b40996e1768dd9d3cb128d1c9fd2a5697a15b330812eb172ce016e47f4bbb5635a1a22c618e23281b471510c77e93d57d882bdea72c28849a4c0516c9ee76b492065afcd0c4fd86d90de9c135ec9a551e0387d1f5937023543e78c0f1e915cf6488565eb451ac3b3e20a6602c1673f7387f668a16c81eb509a15ebe643363b15be3a5b325e0221ec1cf0bc3061a2eaefe0bccd271a1291add175632b619454843e7e1bc46e34441587b61e00a40a910d9fb377770ebd08cc804592ea16e64684237cda52ce30381696281719ca3144b11fe63310d8c20cfe793ff404ae5d1bb37990c661091a17c90f9dc81a12f6e7b6ca63ae4bddd5923718d2b57fe6d4f11c443e06d49767c13e458bca27169799b2d6e904950b62d0ab568bd90296e39f8c03b2d90222508dbcaa76112df2ff50a75f281063fc81340a1ed747062fd26481f85b13c2de3a476b3a73fb1b39392a1f122f0c51a5c476d7895586763837029da273e25e111ba071d6dce602a13dd662cd862fc55adf37d20d67d523839c127dfbd1845c858051e193399c2736016c9c2ff55d2c1eec1456fc6d05a71007b69fa06ffaeff35cbec075bc102dd0e482b0b3e99e947f8c7f8531842fa58ce3f6f7111c6e52d0d54a16365fe52e15426bf9adf5031e27dac309386109c90e1e4780b9d03e42a2f450e2018f902c6fac8c3a202c1a526fe1c0c02ee28473be3fa4bb274cdb80f39cb0f8c958f5128a6d2cbcaf93586738ddf271c23bec5621fb147d08c561a89ae924383c048b086408035394acbc323bda1909c591844a75b4ac76486651352f0fc28cbb946a0d675cdb218ea5d78641584ec8447db6ec0d870e92199d0711e092c7d5fb7a1b0d5b8659b2f4f09d665914906900169696e026127ec615dfc7f72172d4e9c9950e40b402f05affb26f8e17d638becc4998d96780e0dc21262cd79bb5dcac57f00f9fe8c5541078e8a05994bfae65586c513d8e93142fd0f38e02dd1ac04378db089cd4f5d445f6c772cc79813e2803f220cbfa3429929f5498ce561493460b3613ffee4ec8aabaefd4e078e34431c3623277623cae710755721a6889d28dcb682fe59bd2a912c3a0ea267afa0bcbc29decc7598452e8427ec96bd4e4ff7dad93279bf4d91fec451d7e038f87a15f6a8a4b39f7836384879e5a558ac7f12a5fab2c732e618b33458c70a287b9c392bf96e15d064acdb6c85b4ffb27a8d6c08f9403bd525b914f19ccc4e0752c1ba7303f3ba9a414e6e620de995481fd369295cc085ee0d4086cc322511612aeeba8435785e0d747f6ef41113e99f02daeb6b9b016e04769c7707b166fc721ba312ac44869e72db339d8d94f12280ef730872d27180307091a47a51ff40387adfff6cfe5ff23ca72a089110af0f3877e2c35c0111199a2422a0bb1b19e7ceefe12c2683fb846c5be7a1c6f60aaf4ecd576c6b2c62cb5a0bff53fd46873a6782deb555edc23bbc11bdbba75dd1be6a1ad6cf829f70072d47f9170994d097f05cf07012dc2b0b4ec8dd4d9c779b858504a67c6833c22e7d6ac42fd50951f66bf81ae3a02f2f806dc1b77d076b5890bc4f44044ee2f2eafb480f71d11792aa60ad86e548a74684fd1aba10723a92b11ce1a7dd6a5561fe49c7c07442f6276a9508e3f805c833fc367856062c57f21f63a55942ee8fc2e9471647d1417f060bd8d0c66a27d1c1f3901339d4697ff39e6480609fd878b176f95e7a6596bc761cfc02a612cb5807b33e7803fc5b1d79bc27eccc7d3f035177b4134650ab8951b1539c16d7ff813346f0ba46042ab279344cbe1bfb4ac912b800fca5c41a1e01e2e48367606d18a6ac9ac259eab77260b92273b43a0faad0559d05bf0d90c3d1f7b74a54cdc688f619dd581aa440f8417492d917847623a18ef5410b04c88ec70cd12b19d1b4d506a86d8b42ef83bba4b3e5af3c49f3e8026e2eb7d9fec7687354ac5b0887116157de802696db73422719c1ea40e944c3229d5159ce10079adcf9ceb5ff67124799050f8ec9e742b8bb1ab15d470e72d890af040425dc1bf62303ae3a3e7a96d2a43c893e80ffb8a28f4111b111aeb361103ee2e7d8024fd4e508efe2e2c0bea5a56827b8726cacc7dc08fa96011013de61510a2b1b058e3f643c735507536f749772c7baf6cdf30eacb40473ca90c745323467fd21411c1ff380b74345d7e144ad0bd7fa7d7c1fc7251fd916348bd38e0169732cd4a5c892c1ea29cff802ca57db4593be104a408399a46f970c0d6f84325a21b7cf69ee061ccc2a3a50316c840af78877ba416a90b831f59bced3bc2f42a89d5a9b0aa377090344502b3507058d31582b403b5817becf3abf3063e40611dbca2846cc99bc087b5f2324908a49e5e08d09fded93fc0fc1a98e7dc5279ae235dfb49b929674403a5e7215847f2c4263f4a937f45683395abd3fda138a1e8041e083ec5724df8f5f59ee81ab3d8f91243b4748ecb153908f8ceddc7b2a6132bc19e5dbc68d1159619cab24990dde0069d6923ec90219fbc634f0ffdaebd6e2dec866563e71fb0b99b341a3225d9941ed6f84bfdaf764bcf58829b65008f8f1ec19284d1e0d08e4fe2bb65238804b564e62228659033401f5137bffa7748ef26ee4d3cb886995198691f9bd7f6821dafc091f004ead76deff2afa175022fad1d70f5f312bb20b8c34c9b38335493d62414b52515b4c70bb5fd41efeb8bfe54027b2630d780f4d4b4c5f6ec6e38d9ac6e64b0771cf910a02666224d7c9930a400d381db895e295584fd6dd968103a6d4c18e438f5d3cedc0b55b1cccb54a12313c43815f9ee10284257ad86a0ecaa993455d03fe2c4d54daf8197032b9baa401614ced4399d79bb7beef9ce5bcd8932ae1cf525f565402887c83aa92456761d0abd91781abfb5469a2640f41acbf4ce293417771fb35f5ffd261422ea27deb808b8f1e7f676bcb61968d1cf3fc00f94fce26a573eacfc8c1638da80d4eddde126a10c89af520a37d898f88198aafd4d70804ebe05448ad486113edb2b2aebaa18e964f683cbcc9090e294d7a7437be1ca1ea74dcb36d84a2321bf301db8193e2b94705f8aebc28168b8a51c4ca04dcfbac794fab48303ff71240442ef421e250fa2c370d0ace9c55ea508ca6166436d03d3df8315970bbd8bcd844f4bc68f5b06299dab59818f88f7e301b5c4316aeb59aad5d5f7097eb27b86c0cd57fa81c703af3bf34b8c520cc3d2c5a52747788eb8199250fe53a397e6974413b277b9c70a27b0ac6f69139f75109d096a4f17c88c4e93bb3f9ecbe10c1dabf88f8fa6981df485dcd33951e7e060d99193bb414647953005e6a41a281ae92fc0b106ab1d0c649df7df67a84a19fbe57e78f9cd919f876ceb2600cae60afb4d422a6daeff0f3e8b0010a88b0bbbc353e403115f4fd89be0290c657993caed524b65b9e71e1354e3d251eafbfdfad286e16e963cc476f575b88f39d75d4aba1c508b0202150a22b7c9062f93aabc32cd5ee70b08a94da92dd9c39e7d49fef5343e8730231d1cf3ebfeb1b25dbcfb218b5dde3966565d22eac32cc996110a53c0dc12a9721325df2ce3db03f36341907cd84122d2a897bbd9e35d2c5e2827a496bcc38d982c2bcb25cf055472bf29acc26c66698133800299b78ce48a634e6323bae6db88f7211ad69ebad87c01ba4d11ef6b763ba53395eeaf888db5c71163b7d73cd536771ec8d7e9f205f0bdeac8aecfc6ab04715f93f780868392380ff39e7999c6270e0d3997a8e3c12650b84b34333bb27e91818848e7a01a41dc8f603c55fec0e05524fbd9701f9695c6f1a89a4fcdd01babf46de6fa4f6a1abe626ae1e3fb09ad630ba5ddcea5c34b3d7db938ce41fe53f504783b69bacf58a3153bf960af8e7b92248d686e4891e170074d3b614db88a20b1a58e021253a639e04e495a18eeb9502446d5a60190f1b37f202ec7e975678e6a91bdf5f4bb15cca09c259f6d3ad63214fb72a1b7f6880ae472304e65f140fbfca829ca4ebdea50884189a0e34ec4f80d70eedb8edd7539e615f29015d0a2a3533bddccb3ac3319709615a7747ddf91059fa99e04f1fd1d6961ca462b4ae617c861a104f13758d93a106de1f3ef546d2a731e0019756656b099fc3d53d47386b271558ce7f557490f78816a160b9642303f16a6c56c5783ffc1f2bccf9b1c2dd9095fe89596add52b8cd0f27e17e0870deef8f8bb27bb30aeee2710f187ce6177eff64cfb0a0830d2b2c4424784eafc0f2277e643aa11411f9562c2484149e3ad02c0550338fbbd690aeb6eb7f458981086439d92601f693280eb07e9b8d53730775f628b1eeb9ec29e40e1e83b45a015ec2c7f6a2bea0d0a76c6cd0d4eaa26f5b9d8b15f7ae79a36f878ec6899ac75163090de4856d27388b729d8cb911f2dbaac4a61755208607e4d59a2191a1e8a0f92b395ef84f5cca72650a5ec96711783064e0b3038131aedf028cdd0de22210c7e41132f4dc554c726402bd9867726f671be49f37317ee6760d69c5617b6240f2a0cd8114083dc0a60525f9b94e16a3c61893e7b4cd4f91f030662ac3f32b00e35a3e8598f3df274610f9957ea5be083acd8f25279cda1f89f8d5c05a0a1b202eda0b2b2846b1899e37ea945922422c83846190fed65f9e24351f0569ff47d29f0fa3f89529e14f881940aa1a4841ad144c7c7696ea6ccfc4ea4bb7212653c04f248c459caee04c2e37d3e7dc683dab19dcf65ea2b26c5d7cca848625d7efd0dabbb2426915687ef3abc3b069ec8307c050d038c74ab190645aa0dccb1421c04b258ff3ed12c8db75b58a14309d2e26d73d52eedbfb78a0776ece8ff981a7b04b066d7fd4c3379ef0de749d0f0d4cb2fa9ef181a6337578033e287cd292a492a7d9fee18d21a08545b65da4aa812835303c5d899687963bd441be43ccc703e23483ab9796681a8df92a34ab2b18d397028867323f31f7049deb704611ac9cb12fee16283124c9b2428cf4c007af6be15d7b5031a38fca9de3b791871cc113911378a4ab56d049babdbfe707e94528f5a3767e39df784cef4dd84c2f1fbf6ad21654e556af2b701f4a90b0250c325e831293f5b184cc33dd9a4ad1eb0593269089f225f0ebedf94b11fb6f7be7d859e0cfc3eb14313bfea9686703262927b4b146bd2aac0450d1a61d6759af2ebbd40d8eb989f86399f60a5667de00a28fded06200c29f6eebc1eb9e47664d46dafc3afc47d3a4ea9508c16cc7c0a80dde88a02e592616956b9a090e0d519584e21e33221560cedb330523ef2b7c0ff50acfd146fd4d838c633a57c87c264b49a494c28e364f8ef5c6f3fd011cd6b32a0c2c02d60abc970afe83e55897b2581afb4dc367930ac9fffa440dab2ec982dd410b9287a000b0612d4b154560cd9b564e94c8e2aae0bbccc7de765a416650df2174519caefb7804b23bf526429bb01c70f3480caa10501782733990703005f297ee2051ebb04e3c1df02fa1209f95314f1236c8aa37a21641a3bda5f949d8292a718024bad13583e6c37df502842df6e1ce87aaaaf5e5fd5ea2bbbe56be9111b95f20882e244c3d5cbe120a620326c3cf2852b2233aef66bbc6d20a3bed7394c4f49207a510fd873763e5f933d6ec9552bc5956c7c16e579968778d059dc5d3b95f6009e8c3ed9c8afeb8ee33bc36a209435fca7353b43d7ce49d0b7da67d8c3384a", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000adf8670373d6f0424af5f29cb946f164000000000000000000000000000000008f927aa40772310f80a858405907bb7011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + }, + "decryption_aggregator": { + "proof_hex": "0x000000000000000000000000000000000000000000000001f3e77d68ed5db4ee000000000000000000000000000000000000000000000002824efb1dd50dc53d000000000000000000000000000000000000000000000001e287c5b545301ce400000000000000000000000000000000000000000000000000003ac30f94551900000000000000000000000000000000000000000000000425ae327975b900880000000000000000000000000000000000000000000000030450cf304ee5f67d00000000000000000000000000000000000000000000000ddf49ff2fcefc54550000000000000000000000000000000000000000000000000001e6a4e87b9a2000000000000000000000000000000000000000000000000373d5ff3b6917377d00000000000000000000000000000000000000000000000ae9b0b68e3252781a0000000000000000000000000000000000000000000000078bae91658ec6a2b20000000000000000000000000000000000000000000000000001edde5cf5eeec0000000000000000000000000000000000000000000000024db0e0b9187cf4460000000000000000000000000000000000000000000000061da17c01b0ec38f8000000000000000000000000000000000000000000000007228437c29b55d1c600000000000000000000000000000000000000000000000000013d3baa2598e82a7f1abbbf32e6984fcb0010b83c6c7844ea6ca3fd0182ed6cfe12534a9a1ab32d95760c1b06880b626933c5156327a93ae0c04616b6f2cfd6457c4dfeb0b88f115d8837813343413efdc6428b79367f2cea1f6fa8b8d3ec221fa34eb49abfe209fe2bb0aeacf24470fe359956c7a9d3196c4d59810478f5e6c219a0a1ce604b02dc0bd84807a39b811417bd98322a228ff199aab7bf5543ac55c50a2b6509a607be7abd06b0d150fb024b43f0a0264f8f2a2258386a1e23c891c2cb0f8bc84212bdf036a748a87c8949b4fbf035079fe8b10ebd8c3b4e7f3ff7033fab3de191217123f39e02abc543c57376b6a015041fc623668c649686560fe0fdc1def2e6251b41df3fe84d67e48c15e23ddb2a83225f0edceb2c59758b5bd5faa1af92831ebb797fa8d6559e379f0bd0cc77e360b78c17a1e131017d4ecabdc2ee16ad021a31399184a8a78a5ff3667d4ba9ff5ca43d76c39055e64fe36d15d8be71fbf62733a4eed75dd54386c0915aa949a3ed5710627443b8ede3bffcee932e416b52251aae03e2369a82a51fe16dad9fef2b0e96efdcabd3322c1483aac4f532a1171401989403a42325d2afad1973d1a723680a168d3ae569995f8a97a7aecd777e19cde1b4ff4744fb82c63a6d990d2185ff7b003862b25776bd65a5ed0e414fdb227c255f71b15ae0c6933ed5702b3e729e7f826a590387398b121e06df511d5514fac1e05b458c10dfe141aa13ebf0647e24c0765eca7a633a6d82c8cdbf1e281308f82f1605ab9e282cf3731ea329850e8463f81d274c6f12c703ee4027535a287eccb7bc38c8523ec425377fb9e706a8c53c138c98c517ceabc0497f3845672c3b9942b4ec1692c479a8dfa18e55c4ad68c6519198b612a79177ef62c1ddc02d7842bb16583370aebcfa9a055bb2811bb27866c4f05557866b524f27f1e17503d9ab172ac8130eeb870cc0a647bdd39634c84b59cdf975ac0a773c6cbb151c1d126e82ef43f98e082640c22339e906c342e788aaafac7bf0f644097be2628e0232986c6d79f5ce13059c444f38ac33d416a25009f2dc0f86de79f54885b0510a86856cfb4965ef34d9bcf19bd9ab16fbea080afde613668d967b25fb3bc82707f9ec76cf626377bef45c4a0d2568adba49e805e59649f0695412b947af0a520131e9eaacec50553d90ccbca23fa3df01c8d236b1c156382732390af2085e4c0d7b8c85c65f3aab8fab763cec04cdaed0cf90145a204081c1ed3d483732a4ed13d9ffb221838c5f0d8ebc8c9aa98357bcf7fe1c7702f89b83bf5dbea988767c2ef522f59abf597777d99e620b501d519bc981fafb76042b453d1a7167e91a2e1c2b5edd2429647eb7b408c757e9a3c1c19c49bc6401f10816c3ca20a8d2768e19838e2be7eee84a64b232d47264296afebd614484dc8d735a5d28ddcc2a0c840edc31f87870ac41ada9eae239427acad4010ade9baa676779c222f23ebf5245235875eeb04b748a32a4aa8d9ca4cdfe03450077b68715308644eeb2cb1a68f1006d6d41a8e984f8e1114350487c1587996fbfaa19d2e60cb1175558e6a0292c0ed5ff0a5b855c452584fa8af47335737158637c2efd534da21b3853bf89f9242f9c279f94e8e47a25cb8df25ad1787142eea7c1e64d248709147b370f9fb54e25281994695e79395c7c6a8f9506b17f11c9e84beb4ca71f547cc5a24b737c1d139df5ef48dd868001c3ee1213fc3d145b8a0f1a4df3ba5f3b84041faad9c38729836a3cf695c826bada884a9b2765cd2dc3c519a142fe6dce7c61ff3c35b14e1d0f88c8c1042161b3fa888a7f10bd499c9fc4cec04fda3ec346eecfe4b8e64117ba207695a64301d974e223e8613159d26d2466c0bf49ee276f880a715f536b1252d850fc51c0b123ea14e1ee08e305ff2af518b23394984fced28b0d25ed1d053723691e73f00810db57dc33e7668cfa7ee8ea82dfb08ae1157a7ffab67bdc1e65810fab2b117de99f771fe66e33edd6842ab29ee28449a3a711513839b6c425cda1c407664580d3e7c4a68849d1c986366e3ac00ade2de89dc36065bdca0b272f4c4f8230570158f2d1d5f1131d7d7dc9abbd6be93171ab7bc99ad78497702925a81cae48f544226832ca5cf1cd5f97732b9537d9237258c8b8511f10723f057859047d53d2b70ffd0049c6ec0514ec267512b9c08a80a91d5d597b51c57e069c3a0203febcb8ece93238af70b491e7738dcb4fa26835c6dc843869122dfe09dc4f1773676441f3237c9b4417010f6a26a914ea3db681688f37d2c1f749b8111f6705f50a9b212a1f112a0be2c92ef9fbe0890df133252d4b59510aac501522c73c857179241fb523b6cbd447f6e20ffb3cd52797ea66d4c6fdcaa4d73a770bd4db1d004838ee5b6ff558a35fe31ebbe17211aade2d97750277a00aba976002bcacc9932d5aeae711c5c0e2b80ba242938e0d52e1c91ddd667c6f2b74abbf1da41a0b6c35c00e17a6ebc7901f7808ef157a01418d01949389052ac91b2d711c40a79aec70f73e9fdffe0ca944d1cac8d9e4b5ad2601316b5bd7e540f511662ab6ff3f08581ffbf961ee342499b951c63e998e60f4ab34881c9b8dd4e2dbe310cfdda4611947f2faaa558060914e3dad366bd4332aafe4bfc4805f6e84205304a03965d812f279734680b13e6cacac7b23c2228b1acbb93f4af41aa025bbf91ab9a9a42630a755b364b3ef3790337d1da0e315388521831502a260b09813081dcad647b64ede244918a587ff629d2b160c0ed328617eb4cd3df037fa8c30b62fdfc6a29ff02714877f0a4a4f09c29a9af2e85e427e502427651a8e3b304c361d2c9136de21197c24ce33da03e917c925be1923321e11e36bd043e1f730e88b2f174635edae1d9553e512b035b4e0b933d12716ae0346a30d6bc1c111c58df925b6e006c118da59d5e91142cb123ed18216a9a34235d2a452ac4206f0e6550b07c6ecfacd2f7fc86480c80dd178172a575c26c299c307ffa1b8b0a93f3f01be15b861d10213dd8c560e919c5fe5a46c8ca6c639fdde608dd918de75d6567a851b7680b297a2ff4259a48a36d482d1a698a6b16c9df14176812371e52cde3c222f843387102556dae72046097464c095df1ee46c69826480752cf48d60672cdd0299635611bb07626a8273bea0fcea5c90d886657e8c4ed47b62c3aef9191bc612aa8d7a3a8bbff9fb80690060ff6b36c102475b9136c49857634c927dcc9c481cd72807184e90c8e852a0801c946d6411cc2a195f398be1dfc37a2ef718a3091ddfaa1f317f91b550be51b86e43d0ac570a6c47ce50cc98a9905f886e44b69a02447cd1ee0c5ecda92b2d39872b82503583790fff4a195ef071c41ba778253a069b853bfc3cb73bf9c3d93c2ced9f9d1d9a9b44ff81145c0b9a6bdea82eb5fc0631a206074d902529dd3b03bfd38837b9295763ce9a173990da54473658fd092ff440bf4cbe8c837d5c828c80ae4d66f944545c0e27fc240ab1cef2f95b043929d25f3ed4e9ec08892ec6721c85052a6c78521f0feab2acdfcd4c91a7437e5d202aef919da4ecaa22b001155f637b156ba351e5a052c60bebf6953f295b369a27d042baaa94a56aff86ccf43bcd34f6e6080dae752c359e47a030ee0ce2921e0ce36acd288f10068d9904c877c9d62a7d937318f9f7c5f1df900a0e32eae9932c8f06a0b83e30b0b225cdebebd3b42a7131f4893cb5933ca34d249e7c78a9dc022560ea911eb98c0c473c4feccbaf919ba5e1ab1e9a139672bb0c81f0de2d6d236afef4ff6f79135129f6c4d375d5b53d96bf96bc04b2e36f1dce1ed5dfa982103fbd4e1941a5f51b4b114d45ca0397d8cc0b25e7cc9e4efec098fc9619fb31301f54c3195c1283407e2b520120c77daf2b6428d78e5ca5ddc13021c6505efe15f88f53c947bfc4408bfc93856fbdaf8a9c72df53fa60f0a8c91609f67457c703b6fb5b5acbf95186219605ee9245f80b948754af2fac1161b89589ebff5fa9168039fb3c5e72aa8a2adfcd6aa59ad3cf183103a5c1313dba6f6d978aeaa96226ee3f12f03eae59e60fb73db15bff1e51ae67cf88da23bdff5ec9e0b49566fa1683a4f335e2ad10bb52a7445b05a5765175abf26e2d8a05620767255f88f9070f0f0fb28cea2cf72e0745391bdad338e1b728132537f3495f2343514bcb1b3b0d8d39c1b7b17423a59059f96366f5a80c6016296951122774a1bf3f4176b2251fbf1b33040f6c6437af3d6fb09858b7972dea5c1baee1986cbb548c465e502708bd2c3149c3e86d6ad2452b70102fa4a3c748d65ae3a5bb2a90c6de79f60f6f090b044848ebc4579d7cd68132a3fc3a16989090cc37ce21079a152cd3fe47fa1fd48c0313cffed38c172e64da1a3f89942231c2113c5115e2bc85c3debef2ea054c043d9ad9cb32cef65a18f28f1e15b7c9985733c69a52eb6af4287922b6222f5205113943ff5d8e1ed099f5299cf8240527200186239863e36d4a58a2a5fb065d78c66b7136a52ef53de8e5d0319f5d2ac5d1c4b5eb6c6c31eb57e8e00f3b05879a174a1da0394f7602dba05a7cbcd64b297c0650eb8980cdca29736045d724b1959380c73fb0d631a680244a454dd3a4e0bba490eeaaf956f8296d39cb9a00ac3a4c762a8d11fd8645a19ecf3d149dc3de9a91d6fc22fcf41a3f41cf5d2c2cbc5eb7a019d0f1abe491758a5a35f9941edf83ae783c1009d9b32efbcd5773252cf6fc0899360b17905f9665f760f0d05a383645404d7ebe4acbd3cfc0bebe1a3e6067abe34217bd9d63a445ab7d62814ce52e0c7947b155bea4336495c7521729b8332e879ba575cb2b5b17984efcfaf4f2f0016a92233578f09cc64c1c5226e733c382eece007d0a5b2792b13ae338a8edd9034811a0d5a18e1ba453107c2fcd495401b784847149562b319c2ddf5434b54f6f98a55010e2d151b34ac1c61f0f08bd81faed2b6ace599d4a68f5e12eef8b2fda21ba86dbc6307001f9554a29d40fa86deb11a05a25c8dc66ec9e525526aed32573f30014da08c2d9382254292bcd80c5fb3c4aff19006441bf4090d3f5e72244d8967b068ab8a720e2f8f8242a3331efa61b12eb0f568148b190fe0aef1655aee8129c611ff7ab4d2b7e7f0456931fa078ca2b6cd155f04f7a8cf31a2540ad143669812e18153379b610f11a1ebc35a1e580b55c355adff570ccf222d05ba579793ebf482994816ecc57aa1ceb6fe0e137c9ad7d23215013c3d02755009ec47dd527d366d54f7bda9c80501b163775e5f22abeefc0e4d0ab58b3bfbc5c9a6971f5cd29ec40cb312fe43e9b09d53ff3dea50a09c42faa82a943c4cf5b06cb0c46d0e78564929a69cb6ece0c2c5c935dfe7dedf4020a9d2344467a8776559fb824100d798d988c0dda97c42622dc97061e54e3c9c7220c5326b65d25af7785919937794c407761f4273d7d5502c53ff11a6b83114105cb2f09891858a4920eecb5b8b73a5c803f4f8c68e16f2540d7a584736cd89561c2c22b3639d06dc502dde723bfd7d10fc023d7650b780aa52ffc396e1ba4eaee1e79d4ca4e42ac6fac5815ad08ada300c50431875f970511405be5f78f927ba165aeeb6e8349977f8bc883e90fa7946fcf97c403eb8b2906a69f84bd75e4bd3bddae210d7100d285c94009c31cfc067e7f6fdf538a3c085c166dc8c565e5a7fcdda24325b2c61dca091a4e4cb7f42dfd5c26ece367f02788948403bb25fdd624b084a539ba98fb6e00dd50b0021e2ce918b0dec5c6220414cb321509524b921d65134fc959ca8112dd6e7b6645cc9dffd9b0be97512e201d78105bc66c1d6b44fb0dd0d14d1575362cf2d5c4644bf4afbaa963fad75d219e7a9dea5e6a44092c906bed7582cef5b50b36b7120f7e56749f2c23b0667e0b3e97bd8b681cc3e3cf0564c8c508cad783d871a755580ef9cfc3f68511e6011814740377b0ef4c3e85e74f282803f7bf7a60a69ed76269beaf2e541f2f01461f1d3a7b61dccce66f421b1702dae8e1095183bd485317ee1121acc0eed3d9841499f6b9a0adc4a459414f50da7ca00070a63623de5e075e32778ea939f2b5fc2c7681f628866d4e70be2636b34ff2c719b9c49d94241c8993e398a07652126f0eb20da7dfc1543ea992b023d65d006cb4ba0270c324ed1ab10295eba02f28e72a0591f43791564c8edc198a23254b9fe3711abb8ebad8b2a28d76b063829d5b1856a4b156fddb3e21c5fbabcf1a2fcb1aa0aef309dbdd7b30fa9c24a475e1c506bae9cdc0be1efa29becf66ed43a000da45c20f24a5c8dfc08bf490000c17f023acc1af65a33f8ec051411061d7015c07dfd31c7ebfc09a9715008d0523364b2ddfd345073e5059d754260e7578661403ce987d3048e9480c61862d8899b4f7289e8c9f289e87a9f906dd963933d3626632989d2eb26250af5b76cea59070a4012afdb97165ca9da72545f49e15633ea2a26c7735c944e990dc961357e776441813f18afa14f8327bf2db98a0e3e7019f2751879c267bcb6c7863aec69caede24500dfa808bc3ccb710fd545fc116218319df0bab38e404c07ae223d27c9d4f29916205a86f00600a61aa0c2e0dbee33cf3613b31ef280895a78d292170e5911a3cde214de3e8d23939407279f7cd2ef57240e96eeb86081eefd70e5b57a3ad21b8f1063cc1e3b6f6f11d52bf76ebfc3ad91339479a7a3d2c2b8e6f69fd78d010f2dce00d6d8b680c06ecc20f6d67ec19a28f9c20ee9b624b784e05418ff4552d1ceeed6741f7ed9c3d00a3fd38447dad774a539bc99a5052a95f628286a5e62a4ccf79bab6da12d1576c537101a31fced2f45211f914c764e5ed965d5d9419156f62a957a277eee361d36f5d7c60222d27c3376828803d28c6633a8f2110601f35785bd5788bc8a8c47929d7d820cd2a4dab83e704ec786ea5e5fc3886dc9615b5d4a5355cd97beb11573a909572900531439f2a63f421eb5494cbed5ca15503e22f5cfb43b27639d7af28165b4591305706d1f2a9b0d1fdaf068e685508150c3d3d23a9c0a31fc682dff0e845c6317a414395c9e6c4a33ccab5c6395006971edfa8f68e55940b83b22ee4dc105cf2e39f0af7ea4a44a7a2992b76c4c570a30b677986924aa592738f56f95726a1df409d84e7f7dd45727c6c723d7d44300f256dabb802ab4c9a88121fae65192fc5aebfe70ffee877bda75be243ef3c1836206382f14f942875cee4ac22f4207aef59da5dcd208064b367dd78777384b80503be38dd876624f57cc4198a025b37ab52d7229aa2c68239ff2dd43d8994795828a5cddcc20bb663b36de0bcf88564e95318f7d59f159b263745ddfe380abfd71f5c2a6d01a4dfd231fa8c8adaa500d5df747ec3f5fad7f41f4d3307d99d67c019d7a23b048f7d63eb11c1133b5d573c502ecc2f5623f246c183a7d384044883190100bbc33c4aac39a021366c17e24a8e8d897bea7b9aa2b0b0ff17a79a91db2e36a411fe0c982dabbfc76adb74e710dc0cbb2101b4e04c96683f49b0d9e8321f4f529c3d73b380455084bad0671f759bba657fa3b04a41dac8acb156085556126a7b131eb954f4c98ad9a97666023f78a49a1906c542ddc59dc60bbca6c45b121406d8eec540912b0d0b03638fac7205b2556bfb4b096924ee52e816d79310095af3cb21300eb0416e87a6db4efa734e6f54a61d56e45c21f8501a2c18c19311f75013282383feec1caf488b8cf5e1d6bf898e420a0ceba1f47a001c4273d01357402e04c95adb4a05284a08504bd20e45a7ff3b37a127bb457edff631001c2134448d544b87c7cec84445bdedcf77b43978a27227650cf6653e85e62680d91a9a336e6013866aea57a78226ba26021773e81386c7e09f3942eff5c524b8ce0dd795c2959de2311e2b05f5e66cd6a92cffe26a580241663b6b4792cd8b8b872628c340bba16177e26150c31659ccdb694c9871407b0f2ae7c2020bd2d213e726f43ae8d6415189434d3f6053dd0f14fa9ab5e26e384aca8260901a01b46f83222053230900b4e765a5013a7931b2d689ae848d66815bd07bf7bcacd37f7f9e28c5e3f11c8c868570ada9d40a96ef71f80ab55a99eb2bf8e78db8182d518a6c1f8ac5c99cb8be988eed079d489e85c336fcb5f8057bb20d6322fa86e62bd8be01be1982e48210e025aef091643c5cf1d0219e877cf4718183baa29fd1e998b2233110abcdf05adb27255a66180b98862d820fb8220f99ec23d305d9f2be45400c8a6b6b564726412d6cd628efded4bd4d8d3faca4636f44edca4fe191db5bc302d917613326ba93f366cb36705e2ea7aeb69da71610e0546364dd7b8b962d62284be2ca6d0542cf4c387809d6cb0ac49ecf5e41166f72429c711e70927fe10f1357793171f937cba3af6a6d5140e6812dfc3f2377ba7e11204c0f27626e61ca00c3dcc0694e6fd6ab948627668d4eb432f6ce32c38726a5a35211facd2ad25e20b903fb971f880376def335853ccd89ae2b2485dd06ef4fbd72af678f3684d2175ac14729f842e00cfd4f48f8080e536f428c32e16954519e481da94b30b3e00dbc1e2d668931c3cb33526098b89da49bf023a3405526b77df1015c6aa8b88624f585b6022615ec24682a2e594e74c3cc251b082eb238167e9a5bcc7d8f97d20c0439e4fd8e706820a24114509132c61fa129989b7c5624f7dc11ebe3a6a1001bb52e32134ccc60f5e3111bc490086ed2b20eb735312371d60f9a2e69477f820bb45596866f4f846e312b6376de071f1354e67f33abadbfd869ff728c920bf21390824d579f86d9a78736e37eb6a1702cbb0535f3df93abf216055257c126c907b88a19fbede7e3eaf9972c99395a91a86259cf8b6bb7fc3e74365c983df0d3102c4173d44687042228e3dd26d15d4350bc9c99247ea8c96456f864edaa1ffe18a291daf39eb370763f0e0e71727b57dc895c7a0ca61d736fd361d0af7397ce060a77335485fe34cd581d4e971f77bd186535149d78654617f2d6f41d6ce2c825c039324ffcc10a27e50c6e47a5b2a84ad8faae7fd6932a91f3ee2d3009f4942569b4394e802c9207533e7c87ef88ecf09e20249d2e9158466d4e38a4cce7e309a3d2d82cd6c979458b7a60bb3372e544d4650c34c31410d78ce03b80bbdeb81cdc482661fce86890b76c3ce45a72c36b70d441e203c6f385584449176e1fa81827627eb4f2650e88b29856eba0822a44a63cb2c71695be176035a50dbf92b32487ed2ec3637821549f8013c816eb3ccd0d194634a82bf32b0957c6172ecd771769549a3138b690aec5c96013608f624ce42a753b61dbeaded643d8946cd9562b19aac5fd513923122ab8e5b28be59b6228825d403400207e70fca0ebbdf7c11656e662f005a9297c1cf3a5c68a23d88b5065a434aeca985d3947bdd7bf1caf2a904e27a9375bc4e58d025812903d2a8d9c1eefdf2b76e1a8f3bb20809a5b951f963e310b9b296516448e4847e8bf10487cd6704badf264e38036fd74b7c39c01f37047e62239081cd9cd5e3059d1070c9c1f89488626733f2b11bc14eb89481a2176a0bbd96c66575f009518c91670075a6d9488dccc597d27ad2085d5e559188d32c5168cc71152887d8ea71d15bf34ef9cd15cb041c40a6aefe67a530658033b23542baf13eb49ed5f4c0b7b111d7734ae2f88e7b05f9b0cdd7d573db49a19429bf46545942bb14edc07963d2800b5b66414a552a19759e3bee9c57d4e680be3d6503174807ebb9845825a0c70ab68b7d11d4af2cc3f09f078aecab7e88010e194843746e2c4c27893ed916d12d7575bc66eb1a290eccf0513a3e09579072ce06d8a7f38211ea1da1d8bfa710062124f853e1c9261705447defc47a1adf80c6a0ca3ea3d92ceb4b19219cdfbb15c1ffa71cb7fb0a35739a497e640efefdb2ff43e55a01f649aa81a5ffbd71e6dce6d3fd23d2011701aea425fdc6b4b89b62e946be2642b9247e35c6a16e8bbc8dc310107f6b7ce4fa1b22e96fb9d8562f02eda1a5ca567421a816fcd54396eb32759b565c9ea9cb3661a4c8e42986e8d272a33a58ab5bc23b11d2ee27a94d1fc5faa2a0ac039fd46f4f3540e952035f2a01762f1ea6c159137e220650a4de7c37550dfdcf0fb2852288a2dc0d75a13b721252e4413513bdb146f23f6c453c8212964a0ad3cc7e1706d547dd9a6e920c7be07bfc9ccef26adbbb81482041600e5be4608b25b7ade0bd39707d1141a9c38e91d6d6a8710e20c146bd6a167599b91eeba7d279906fc5a99ca5b5316472a92aa0e8b256cfa9df92304e935d1022ba8ee8691311ac01f5acdfe586ee52cd2be6025ba4653066c824575b4d79e762c2da88526218e5d1684647a0b97b5fcc8f97a24e99a4f6e8cab10ccb9181685a81710d9f486c8a9108c547f2d7d9796c1228b0709a12e3fd8326bf7834a1dacd53ba4e2dda1464eb76bf8e10837c5e0c8f11c2ea8f00356fbf4601bfddc1b11e5b391eec85ecdfbb3c9174bb085f3029ef74712def3d1bc2eb8b062e1cd968b374e2ddebb72d61c52ecb61f1deef94b7912361d7cf658040c7c687d5f2c471ae5a16628d632ae7811680f078efea45fd12ebb24dd2fd3480c5d399b9595fc698bd6d0c435ec72ab51a7d09edf66764065d9cb142b7e6d555a11b06605fdc3479afe95b5fad22b5e9b4604d2ca7ba3eb51578f23dead1a272168f78d251f6019c0ef83eeaf64bdee46c83ab0119debf5fc865717d8032f1d166e1de0446dec8737fa858d523c1c8155d4aa8d253b4f46d2973f178b0492d8680a005dca61f448687547ab04454c5c8e0ce84401c2203ae4a65d2a4c297f9b989bebc0d717a7b7b1c26a72ae0c8e838cd6eacbd41c6c66badc10014e4d4de5b875ace180f4562b87c63328a89149e4fe0d1f76c6f468a1f36fa60c7bc7e5fe59eafa01cbcb0000cb0f1498544708be7a7b8419d0abc91fa29728021d27ffcc472552a602c5c809451567cef6341e2c070dbbfdeea42a20968ed6254aa989dc871e3c91a70144102aa5696b7f4f669e1e0b3db2a8c101e1899f4803e8724d5e9407f485851ab9e27f0e7940a436faa458d51734e72c88c953a1cd19bfed29f9a6e503696a0b6823493b253c7878515b54225fa2cc2c43cc938fd1018c70f38a4649a2760f6cc067909d2812897dc8516e28343a51d9a0d987d0e7117eaad11d73c1e43dbd43da5e94f29ee359b1639549c847831283677d2dc6fa0b4e9bdf4affaeac14d82d816a069f303c63ff8bc301ad2cae0e77130b8771412f326f2dadc7c0d69543f99fc3b5b07139ada8c8443a7239af214df474b7515218b6e60c22294e4752120ff00af6b0d8a6f574fde3d9d31ac52559f9c1364efa10aca4626bddf9117a6cde7324503141532c8abd79c4694e62d4cb86596466160840dbfaf9000ffb80391b20e672a5f3f84f69c2d97d498f7449d1bb13f01818002923644f08ea3b3e94a83909315385d2720f9f98bffc8373ec9615900ed48016d62d4b5bbd9db07be2097ec1142cb4ef5789c9761ea7131d74168531c966e226d6dc9ace4464dd4aa2419f4fd2560ae90ee5021d85f58d208de486b6d008262451667f7fbf78bd41f6640410e52c87d7f000983c3282f1a6d7f4bc6c3753842dadd4bd8444fb54dfbbddfb4a627f6cc274263ae1060a69bdf3e0861847cc8810dc714756a05b8ca2ad1e2802a1d702133aec5901fba30e45dc302c7cbc1968132a3146c7832f67796a4c541788345733da795f14cd6049ddf75a49015e970c167f44226bf08bcc0721b0e43312cd33edba128ec39367db196b3f9e22817bc11d3bc0fd02aca7dc97a67b6896ddcef0fb349e9c3b36214fe2efd4cb55709fdd0511c1f18652d148e1959d2e6798bc956b6141176e0ff385b00f6178ae44f8bc2738aa8b71330cd645514f97c7db99e5722f935c22998887b7051d0ece56a1630af166104b50c80dd514eca36ad8feea1457acfa6d77b7991d1cecd96e7bada5161d34c5df4bfa8e7084390684652ddf60aa86f07a3269144e915bcc6e02f3de203dd990d931368a32f6075b3f49f95246e2686285651db5d4a08cfd102858902bfe01c03f1ad9cbf1b7ef45f67e6312ebb0c6b532731a4d26eab88ea7339f2e17582ba8136f2991301a7c24a3a3b5fb5fec1f3fef8b11e29ff40e9790f783af2f1da1cf1c4820e552ca692c2142fb81189dd32ee5b4cd67dc8920701023ef7b04edd473d4dc28377445acb892620abb94d69cbfcdd14f32227020a45a1ac5ba140ac9814b939870f27931e506f0a2676c9f65d88cac80d75b67a4ce57a07f8f2c0dd48cca8b68c2402cac0f8aed4314857f3473f904c7cf90286339fc11daa42b68686f74087bee2d18a1ceb6573d5faaf230e23dee53502595939e6220245606fbef3f3647483c3b7faea0b02e4e1ba5945258c9d51fa75512da79ebc9cc930184dd776227a2ac970d98c26cc7bbcbb699908974f2772729d069f7b8d890ab157aaf2faaf3282fecfe158631803de33736808af53a4febc35e372ff2452905007d266868ef9e28c1c631de830a7a3c38bb3b0664292487848815b002e4d0ed2474460c04b736484a520eaefb1ea931b86147abf7762ab20f703dcc3d607f8b0089caaa2eb682bb184dc81f43d39bc24daded115540412e4ea3ffb5adef53530a886a7b84b585d604f5b132e4f314fb20e59db3d005199a0703a149156a65e32aa0e38754aa43ef903a00c49b9c4a88a05d9f3bf958e07266055dd65088d9332b6c7d29ff611772b29263c0eccc81514b73cebbcf284247c809bd344aa416941c6bd68cc4e5c26c659f096345c1b8d5edc76a9d61ba130345fd5ad74b27a5e92781a44d259141e045f548c11b9679e7161823a61977ffb659471b0271d1000e2b89ad259d73b24aeba57cd6b941067906339e0ca696d048d302751cc0a39acc0b3a7ffbab9093266b59eedc8a12ee36530b7765feb1d69d64940461947246d619a14b4a57cb4bb9d60feeb98d8dedbefa88e3fb63be9ea99f9d65f7694c55462847e8212c9332f8c8e330500161e7eb4d113e3ec89356ea8c57eb739f5f01cf1e1e6a1040be7d194c75cec9fc63d377dd590df604615d5c1391ec17b535d7680dee50b2f96af256227ad345cc93f80822614d3fdb7f50966ef5d5771f6a239c24a3670723bec8bd40f09dd547cee615b07b54866e67f6323f4a6183a87e22b21690068f21e34f58f1854f39f902c2121cf7621a5ab326138284d3f4c54ea61a2cb1f8fc94b90c64a62f15927c4279fe1fd41551c26d4799ef9326809090aebb294270c2a541da191e151e04bca72bb50ea7154e249e8bf58d3248c323ab9efe2c75cc88deba848e0477c2d4f7842823efc16251b55dea0ddf7c06ff6372598913b16600c12af1f085fc8b0d695e469397a0ba7a18a1570930aff5be1b450c1d00b59bff7887dfcf7f2f0bd0a92934561b2e093c3295cd3969b8fd8836b37145271f5d5905e9e81ba87591638170928a90b8e9045055e740fb43cc3276a66c9129e35a0a0ef6fe53bbe1e2745db53e13f623bbdbaaa0bb4203ecf23e85f3838c1a74837fc00299a85ef5897d070b4faf2a7b56998b2e05dc2aeb645f18090d5d232d40f6bc20623c01fb890301fe074c7d09bc63ebe1bb260ac78cec14373add0f6dd4686087eae0e89f6a854adcc3437d01268dd3984957437fc292ff847d0704480c3a484db21e9f0d77969fd778259d723f1b887943f50ea98f75371864780eeecfcf5a77a5498f3379bf8d52e461765acd5ef59a7578df882e599c82b441291216f68f6ca3e602b47ed99aa0c6dfbfe69f1a644d22b66faa012fe35e84031c6cd6640f8cf548e61d4a0205b33dec975e6d8f3f7632b02c952948780ebc89169212ee0c8cbba3869490e0f659f8bbae0bcfed4ba2c81396a26fd929eb167e077a82cd634a5a203ca87729e6057ca8961428828cb53066492efe6ebc2d264b0f0b99506a07addda2986179905ae1744a342d6abfb3c82510551c7d46f176ec19b3fc7a6c4e0d0f0fbe8d495007c2d575b4f6edd0187aa08936a90101deafdf0b4a5912abba298b2d3864ddc9282c756bf0b241a35873151fa04296301519c00fe2804df9545cc47117b30af8cba291e17af28d40c30d05568a0f2c6febf4341ff71b80a9ed4944339aa12387542572592e2456eba9e4af6ea17722834a13602f714effd3c4eaa41db256790c488b7f4cd0c00a7e7be1c0740942fc931c3514016ddfcfcda716b312953b009d3fa126c50e2ea5515e4c0b5a3a66f53f27f261098a8adcbe46d98d5afc16e08ba6c716f18a36c56ed66529113d6b6b707071822cb4593a890ac726c30a87f2a356904b8acb3395fbaa5fa7f7e5c4b1f0c385261c40a7973e6c2847b215e7245b9f9ff0f381c098493bf8934241901d3b0238c82927ac418a390cb7d18d15626062a992593358c3eea37934ccd84161bbd272d021cfe3105284aa92738660327df5994eaa506a85ddc3bb5b12dd2f73c31511b1097640233620fce20826a296910b7a30f774fb572b7a1b60959ee49247fa74412b4c395963a505008da29ef947a6f676686340b98a7f7f8030c44c8bf90d1fb2167e266e7bcc2a6c7dde2496aaedc51f58bf1227b79ff40a60ea3581c19597eb1198139824e4cffed72280b7fcc1bc10a25541c2cde5143424deba327200c291", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000007a6dedcfcddb992da4890c8a97aebf9f00000000000000000000000000000000b4d83f75777e7e4919824dcb4ff934df01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } } }, - "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.340063250},{"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.513430000,"runs":1,"total_seconds":8.513430000},{"name":"ZkDecryptionAggregation","avg_seconds":51.141794500,"runs":1,"total_seconds":51.141794500},{"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.193263750,"runs":1,"total_seconds":2.193263750},{"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.289490750},{"name":"ZkVerifyShareProofs","avg_seconds":0.234150691,"runs":5,"total_seconds":1.170753457}],"operation_timings_total_seconds":394.711996540,"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/benchmarks/results_insecure/integration_summary.json b/circuits/benchmarks/results_insecure/integration_summary.json index 06209317f..213444f7b 100644 --- a/circuits/benchmarks/results_insecure/integration_summary.json +++ b/circuits/benchmarks/results_insecure/integration_summary.json @@ -8,164 +8,164 @@ "operation_timings": [ { "name": "CalculateDecryptionKey", - "avg_seconds": 0.113354416, + "avg_seconds": 0.109944514, "runs": 3, - "total_seconds": 0.340063250 + "total_seconds": 0.329833543 }, { "name": "CalculateDecryptionShare", - "avg_seconds": 0.612437277, + "avg_seconds": 0.607762083, "runs": 3, - "total_seconds": 1.837311833 + "total_seconds": 1.823286249 }, { "name": "CalculateThresholdDecryption", - "avg_seconds": 0.565879209, + "avg_seconds": 0.557054791, "runs": 1, - "total_seconds": 0.565879209 + "total_seconds": 0.557054791 }, { "name": "GenEsiSss", - "avg_seconds": 0.126440847, + "avg_seconds": 0.125857458, "runs": 3, - "total_seconds": 0.379322542 + "total_seconds": 0.377572374 }, { "name": "GenPkShareAndSkSss", - "avg_seconds": 0.230296195, + "avg_seconds": 0.226792222, "runs": 3, - "total_seconds": 0.690888585 + "total_seconds": 0.680376666 }, { "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 8.513430000, + "avg_seconds": 8.377944667, "runs": 1, - "total_seconds": 8.513430000 + "total_seconds": 8.377944667 }, { "name": "ZkDecryptionAggregation", - "avg_seconds": 51.141794500, + "avg_seconds": 48.469535625, "runs": 1, - "total_seconds": 51.141794500 + "total_seconds": 48.469535625 }, { "name": "ZkDkgAggregation", - "avg_seconds": 21.243403209, + "avg_seconds": 20.0494395, "runs": 1, - "total_seconds": 21.243403209 + "total_seconds": 20.0494395 }, { "name": "ZkDkgShareDecryption", - "avg_seconds": 1.512020374, + "avg_seconds": 1.483909729, "runs": 6, - "total_seconds": 9.072122249 + "total_seconds": 8.903458377 }, { "name": "ZkNodeDkgFold", - "avg_seconds": 64.910513486, + "avg_seconds": 61.021601069, "runs": 3, - "total_seconds": 194.731540458 + "total_seconds": 183.064803208 }, { "name": "ZkPkAggregation", - "avg_seconds": 2.193263750, + "avg_seconds": 2.118477708, "runs": 1, - "total_seconds": 2.193263750 + "total_seconds": 2.118477708 }, { "name": "ZkPkBfv", - "avg_seconds": 0.347720638, + "avg_seconds": 0.337032069, "runs": 3, - "total_seconds": 1.043161916 + "total_seconds": 1.011096209 }, { "name": "ZkPkGeneration", - "avg_seconds": 1.388650764, + "avg_seconds": 1.3624095, "runs": 3, - "total_seconds": 4.165952292 + "total_seconds": 4.087228501 }, { "name": "ZkShareComputation", - "avg_seconds": 2.774372228, + "avg_seconds": 2.714979847, "runs": 6, - "total_seconds": 16.646233373 + "total_seconds": 16.289879085 }, { "name": "ZkShareEncryption", - "avg_seconds": 2.591194588, + "avg_seconds": 2.516941928, "runs": 24, - "total_seconds": 62.188670125 + "total_seconds": 60.406606294 }, { "name": "ZkThresholdShareDecryption", - "avg_seconds": 6.166238347, + "avg_seconds": 6.083372944, "runs": 3, - "total_seconds": 18.498715042 + "total_seconds": 18.250118833 }, { "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.096496916, + "avg_seconds": 0.096223944, "runs": 3, - "total_seconds": 0.289490750 + "total_seconds": 0.288671834 }, { "name": "ZkVerifyShareProofs", - "avg_seconds": 0.234150691, + "avg_seconds": 0.214040633, "runs": 5, - "total_seconds": 1.170753457 + "total_seconds": 1.070203166 } ], - "operation_timings_total_seconds": 394.711996540, + "operation_timings_total_seconds": 376.15558663, "timings_seconds": [ { "label": "Starting trbfv actor test", - "seconds": 0E-9 + "seconds": 0e-9 }, { "label": "Setup completed", - "seconds": 3.078788792 + "seconds": 3.066766584 }, { "label": "Committee Setup Completed", - "seconds": 20.221637667 + "seconds": 20.21209275 }, { "label": "Committee Finalization Complete", - "seconds": 0.006574583 + "seconds": 0.006218875 }, { "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 314.973630875 + "seconds": 299.47482725 }, { "label": "E3Request -> PublicKeyAggregated", - "seconds": 317.623802666 + "seconds": 302.025307167 }, { "label": "Application CT Gen", - "seconds": 0.315554541 + "seconds": 0.311998458 }, { "label": "Running FHE Application", - "seconds": 0.003503458 + "seconds": 0.003517959 }, { "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 81.660311958 + "seconds": 78.562423209 }, { "label": "Entire Test", - "seconds": 422.916649625 + "seconds": 404.196322167 } ], "folded_artifacts": { "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000ec9ef698121a5c39000000000000000000000000000000000000000000000000e41f942fce4d490eb00000000000000000000000000000000000000000000000195fb4f10129d1d78000000000000000000000000000000000000000000000000000096d82d22f57a000000000000000000000000000000000000000000000009954ce731edb6dd3c00000000000000000000000000000000000000000000000c3bd0ec9dd55b67f0000000000000000000000000000000000000000000000003728adde765d0e1be00000000000000000000000000000000000000000000000000027b59548a2ec1000000000000000000000000000000000000000000000005347bf3da34ea673100000000000000000000000000000000000000000000000d629407ed2ca5889600000000000000000000000000000000000000000000000d9316749580d8335d0000000000000000000000000000000000000000000000000000451a30b1de5100000000000000000000000000000000000000000000000c16172d7eecd0619500000000000000000000000000000000000000000000000adf01248f5b8c94e200000000000000000000000000000000000000000000000859ad32a233d1424d0000000000000000000000000000000000000000000000000001e8827d8f4dd91d0bf0d7bc3141a4dd134caf21d77bafd25e2801bae69edef7950fe1c8cf0e1c05fd9ce40ef8073d0e9167a762a736f4facc1815f0d8163bfd93cc017eca42fc0fc496da80095322fc75820967dda64c09ac96275567f11e014fe874e35f4c1b1c6dbbd70d14d031ccf593c50dddda968df0f26bcd235d9bfd78d7fadcf5dc4c082c8650c77afa5be438815c3d42ea08aee11084cdfa6acd4d46d109ea7bc42a2042981382bef951d645feb6dc93d46518f03db584013c33209e0cfd8165016716707faef9290e1f2d5456366066051d4a5b1ed85c9b56b2f3a767c8898ae08819fa1d753808dda784dc6ae81116c5984b2e3f7cd8de51a83adb0afa9b3ca6432a1fae1883236c06bc4f084263a4df184700231ae641d20277f6e7626fc026c52dc7e925492285c0754912271f4b382112f5a0cf23229fa89f6b1a66c5caacd114260f3e6d1f10af300e1843b16413df24e32bbad69247f23fbd482469a12cc72b45ef9117ab888d998d42e1d4cdcdd8fadf0f4c0615a748d25a80b35c7b14a408ca6b5c9be88eb9f015340260f7ac3d4b702fa29ea583c12aedde88a29295d507a99c79637f9e6e508b6e2cee68bcf5a4d55b996096e9958ed14b64160422fc272fac98173a2b2fe6c548b7cfec9b9936a29adbdd9d16250226e77c797b945a1d98a9dcf2a7dba90f9326de6dbc89a442570e30f26606652826872b79016e3c26d7da27f6178484f07514cda21d42c6bcf42d2a77bc8b4f36e5f2c6d29d063b120b810cc1c1f2e69ab126edf280494d9a9c94507eec1f37d5b66b71bc5a23e6128dbd86ba4a243a9a0cacf72100f9d43b1e8a3dd25349e062e3b29e4ca4db2f23d46ec0a0321161ea953620e0d0a400bd522610d2bf3a1d1f4e03408280a4eb01f2c96de382a1d4032e1a201afc51eae9795b8f229e48abd893c57eb94f7a512a65956c2aa748f1afb6f0946d8ccb60fc874da7ff480e945a506337c8e046320e2aa705ddc585323194799593e9e5d359fd88a17a9f65187cf0210940e1659a29563411bd539cb6be00d056442c9e842afbbe75f44280307f3666287e0f21662f49976c867a62e4c2c3a24b11ffcc6b043a772418bcff0b68f9946b9f9c73b42fef2ffd192d0fb4e9601d72ebb81253217d473ab13967c8cf0673928c6187201c9b3f5d4aa22cee646950cdcda3da589539df5f1aacb1d2725dc60344ee5d9728abef864a21f93aeb0016e4b9a9b0968fbdab4e601b8f88bfb18de531d9f78019d8b1ab781050c8fcc4062bcf4bce74df6ce2861013af9fca034ca763399e3010091a5bf71607501a0d61e999341708104b83ca21703421c2e400192a1f21bd0c1c673628d372ec09c56d244a04560e265c971cc505e31b30fdce03472507dd0610bf3ab5bbb3bb5d6bdc5ca8cbd82afb058dd7baf634f759379524cf80e7162ab4036dda4b58ad3781247af235afafd6e919f90dbb72ac2226f2f86bc2df1c008f6ff95a49c89d880cb6af606a4ddf7e9ccf097b1c4b946a187f90555d393a11a1d909a57fdfb30a8410180cdb113ccf6fdb0a7fae7281c76e49c0580bc3160d77aa58dbde5ed1d51f402a0fce68badbd10cbe75c128fbcf22cf53e2709eab0834a0e5526c91fbc6b15c1b5bfead10b8e6d0ab3171b2d8a106c3e4fd026a7a01e840edb4e03449356fbcccdffca3a45a59afd472fc980d83fc1504e952a2790013006ba1e5427ac816f25adae5fff4194e25659862be62b0d43b8961bc224f11ccf3bd16f35538162c261e65048d9e4e4cc70286af0eac700570096141331b0be19c23a4117d1bfadafc509612d2d774c13810ecc791068dcda29d75f889001ab891ba8fe14dd2d11f05b422d7484fae0de35e39b6b97451cb4dc12cf5694d0f8de2e3ee7e5aaae8cfe3633efb2d01b1cee79b870603ddf78d339a31238439000a22a5dde7d7abf2e0f72fc8806d086d73f98f3202b454db461aae23c7a8a80b51da3d101f0721ac69e21cf9cade88a606ddc8f8b4f9c0d80f12e5fe750ae6200cded97041e21930cbdad1c0903302950346e6a3861935a0208dcb71ad1c5a0d627447005be70389df3998f482d674f40ce14592c3407f2eb19213c436835406590d2867f21c316ad81b9fcaa64fb2356cc4023c282b8fcfca989770b2ad551a20ce681a2dbcbefa6c08c847e87bc3a307e36cdb61cddeed0f464b27e0fc86290793fbb62b364038dd10ae3b7f13b856e9b0b427cfedd8211248b16e392f4f04b4d3c00630fe829bd6e54d4e1eacce1c37cdd9c6ba7bde51e8ae7591df978729a97138329eee41221d97413dfb35c1178d301098d2972ef49c9d3d37ce8ba92142609dce818d3dc272faef7a9d6739c9a2329b3ae8b741d1cebe2d482868630fced55ac41271ea90925f69eaf5f506bf915a7b60c7b1cea5a8814f3dfb2daa2712c16feeed6b5bd73bb8437f0b33a2b2a09557f97649370eb9c15edffb6cfc0d451a3b62f6a1582ef86137cf115d72346fedcfdd09cf2057ba0ff3d50cb3c5045c72890b5ebd82af87b0a950c802d4f8372fb67d9cba3103e4c3c21b59e887164a23970149af17c8074496fee140c989307fef339b5846f4fff60ea4771b6f1b627b1c3c5e6534abf8e1ceca32fb8d2f7256135e3a2a905a65c7c4900a5eba009da53c08a8264c2d95a8e9e0e66129704ed450139855b738a57f2f8e4838501d88faf34c798100dcceaa6ba31a844742bb57d646b8403f5d9bb8f55f0c3626150d5018b6695de63c4c6346253f531674c1f6083cfdba94c3e7281a0de65c7220681300cc6b94580371624a9a778201db61e5d292c9984c040d8a2bdaf01f6528196094e94b726e83f8527b0860b11ba1cf78a2c1c5c72f85a57958a2689f361e2e867bdd4a28e7ba5be4fae95e5e66d44caabb18509df95c5e8d04060619da0302378d130924320c43337c2b9f71339261401d608ea2acbcd25fcf47f7959001676330b04e5e7df983712fd2ca5159de2b2d2e9dc0a3b1e6dc022fd21daa9b26f45730dd7b23fa1299408d88269521da1225e7e8fc4aac0b0fa30375a218de0d97ca096e738e1c890cb57b3c4fe078a1251f33d24e52695ca0fe31b93c04060eeead6540e060ac7adb14f6719ced88e631fc70ba50f8ca8e5be17c0d8716f924e7bdb0daab87e3b6901720eae49e7ed230828c54c597dbc024797cd024429604a1ce139df61c37b81d6326f880a4077129e81a08a92be8bbb7375805a0b71409c44c049dfb0093e06392154a6742f353159620f2e94f83e3dd9a069a8416fe1376e6fd487a4766af44cef2eab670e550c8fe51df3424de3c3e215f9a7e8a3106073d4c626b7a496930e0e59a8898fe992a41604af683ccb5a29e6e7840a4260c5ffdfb5a3699688ba4808fe5eb9bc44bca0ff5979693272d9a1c362b9843ae28c6fc1f51cd98c5c2da4242c9f650d5c267e513d8208a40df4b7dddd14e789009a8274b38ce0c3c928b4927b9548b283ad304295c0598dfdb0ba0ca3c96b5d8094f587b973d1425a3746abc0daae7480fda4a2bbb2ddabaf50c5770f7291a950edcd9863a06cf513bd75c3616ada207dff99bc316f83d7d1156c2ef24c9e96c11c9337457ea5b6c6baddc66818eee1decd61ffd15b5cb5cb7b5a9eed72a4c5406b832c1d539589d402db21f7b1ccbcd9c5272379ea7438bea5f9d8f2a5e5739057112baf67d6070c3f347e349e0fb06cf579b9a8f289ce45cfe74635d5253980a126858ab7d12980496c06a7b7c6a95e7f6e4ecfa2957c77da9748cefc08d8618875439e9e2168e7e05a1cb42af517def29aab9753a568f465bc9e1378028090e0e94f05fa827a7590b317f23227190c6b01454f6acc256311dcb76b3033fa7064fed92f22742923d2974e504ce93711614bf5c7edc00c0a3b8cc38c014317704e2d1dce3b2a9fafa881c14edc39a36bc545db0887c9475f53a7ef5eb32f90b1e87d8f50fa529f18eaabb7b84ec1868f9eacd9927431091cde1044ffd78afca2563fa2c2917e76b9aeec95bbf2c2da274934c839ff2466e449ef0aabb5a444c296c050c8c44966025cdb1b8fc46bbb208c3a372758c440f4e46069ac0ff2e5c1e9e0bfae61ed536008d9fbfe946f189eb66f0395dd51247f73f0c18167f594b1437ee2fd4a565041f523b89e595c81ae1f54d72f42f538b8dde9b8989aa532b0942d496965da36cb461dae9ddf7110e2440295e1cc589240f54c0007585df172c1e3279e05a2d616620ce42c5e923b8b41fae97c732cf92c1efe28d039d673d1acf1670740612698be6eec0d7794d28c2215f2539f181a4547e7a027cda29682b0271548cda6b0d33b33defcea7fbe8208e11f22fd00409920c98e408dc84911f3be215ee39f6d6cf11eb97f9a1552dea6e333a850eda7a5865e446d39451a91ebe76a8e4991a190efb68b81ed74f6b9fbe5a9440f278e006bf4a050caf74ed29ce83fa8130bba02eb6d8502546641804143dffadaae81a48455c942abaa3f92ea5dd70c89b99cfaf9039646212b32dbc450fc6c155ae7e54e69feafa93bd480278aff364713a492223b70d38acbe5b15791a0391e5a90259d10af8d93ce96a0f07d35efd224a4afea83eacb73c5cb3fc61a66b75551deb76c9db8f207f0e9f1377dd7b8cd97d7411b11fc2152d21d6e04591ca623d4ab2e5f1164d6a95896f2212ecb096f1e21d1492b2469bbe96ffcfdf77502b7338122562cb390c30752f0d1459977331a5b65fa24818be9c9a0b01e5d3652ce97ab8eda935aa40935ddb0d7e334f37a1e1e9a24428e5773c01db1495d15f23b41f95113f3e7cbb2b64b30998b4e26a623cced0c6c246c2421d7a354fe1cf10e574ecdfe20887bdeec0ff2e9f7d479d603603479d950bc5b65cdec41f175c50e12fc2f10d513693da8cba09be3be16b55a0b63a2f6c83cdde050b3e979a19aa88efca5544d356591fed7122e8585a390e81386ec788812fc378251cbcca61186f818d764c5e3ac2ff651e2b1b84df02745bcd24956724035b2fcbb13b09e6641ed6980ac44f5a780fb875286cdec284ea9959449fd7a72aea4d800927d9c1bcaf02e28181b58536990db6030360c87597c8ab02eedae23a1cbff1dd4d17dfd9b3ff44f5bf139b63ce10f51705255ba9ad0425f329897bd4cff19afe6ba0a77107f212b1b00a40861af4df1cdcc6665a294a4d1f020d3ce73fe5094fe5384dcfa10677dbd29fd8669af5e223ebe866f0809bcc298055e9f9dc847dbd9722f94c8e7eede324ec82cda66491026afe9a61d8567e3b887aeebb05593a672d6835b574a4b3ca4d4f3cf5e99f282b55d6f90dd66f2417dcf5b93b85d5f63d7a88bf7e11851e7f811c920eb9833a149fb762c1330b97e0a9dcfa44f1fead5d80dd39c321635bc63534971f52453825ed4c0a060d6a421b5601c8ab40eb8aaddb993a06c759e51841e94547742aa923eaf9bb18f29a544fae4f5349e1df1dfb84399b8b21a746775de962d5d2f0461273e8c8a1b50db92053f02695053619ab0ee4b7d36c46d489b9f6e3c5a4d15f256a827b4c38c9be76860729c4627c31ec725e74ec35b29b6fd3f225793903ff04932416ab27e43368a89ab6f380a76196c1a282e65a5496624a3f8451d1d5392390fa9bb3aa8dc264a0cf6d92f276ec585abd770d5e5a515c20c2e8127dba660403190dbb1cf5a965b95d9cb997676fe763ebc5504b4f33a9e67aab1b3017800b399001da114a5958d9c0997270fa455ca54c44328abb04ad1d38eb1ee5d92e1d848d673315acc09e92178b36c42336cba10b15e42ab30533aee5d6207a23fa06cca99cbe2e1e5ba0a4c48a20df0ba9f7bdc6e4865d24e657de9f8e74772ec323f94d7faadb7609d70896ad03b3873e7312de54ce84a0d25977a186e5d36fdd04826858efc95f0e725c1a0170f90126e28218127098fccc6f8e93bbc5dc94a42d93b194a264619494f32d24c66f7d74266ea517c85acadcf79d983a00c58f9d3021f4345b0d3bb028c12bb97fd98ccd2085891e183adc9b7bcf30d7f7a16b8f128c35858ad8953b3e8c1112282650e531e94f151f3bc9e8bd392184b82919d518584dfd8ec5ccb00e67b92d22da2594b81e7338ea8e17fbc5a366a20b28d6b2157ffc8ec16bb4a79738cc95b88255eaba014f04d4f8ef8a1b3f837c6cc2f26f2deda5477f7f3681eefea8bda62548d5d8aa1ee1283ec16f53e4f3df239a12f403dc3aa82aaaaf4fce71f168a3c9bd12138fef7f2a75bb250a84e5b3dd2b69771a3ec4561abdd4dbcbbf63daeb7ee1fbfaf8a2e14c8e63fdb33e8ca97fbd8dde2c3a64777200326b95cfaca821f536be5e7103037b42e6db4b8468a2518439cf17b05607a124eb2ddf7ea1ef3d0926610263cec35870f64d736cb576bec97ff02e4f1b460d3218f62b5fed7ef4d5734311f0d96370538c1e0d2a3456203a4da1149b20f6f624c1a5eb14f3b6e23dc5703e6a0639c4f4295421fafaf44b43f3a3295139e08f2740ee9928e679c015b16e378e0459daecc0e1289990dc3c43d57d2ce7adf15e252b64728e4eb4474e069291886becc5e3a7f3f7a23971914acab50999b3d1b1247a99b4d476acf564ec63978549733d80e5c89f29ed55df452acd2572934bd1fed33bf7a38dc15eec7a58aa24266f3269d64789f092e7f2226084058d43881ee24895a4ab41d6df42c1fc1adcbd8dd364a4685fa6f785590c98ec2e859091087d624d7953bc8286b9e75152cd225c2ded34a72de786d003f53ed305d0e276b613bf274ddf18d47d8671e1cc36053ab1043b7bedc8c8f73a51eaac1a92867b56d997ada05774fea9de9098df706bd127ae6ead366acae1e70df2100403c7eb2d5b35a5025f1cb28d1d658ce39dca67b38430c42a1f6476135514420ad054a7fb7658417a95ca357ee3371d5243796fcd6ef5d3e756fb3fd388f4561fca53dfc96f682133bb48a7b345c1111cc9af0516b67195b851222a50bd0f9e010c08f4218d579c9e5bf4d34984f3e67b76f59a09f36c0845b5fb7c7de8f516034ed58397993debe05f425882b5356afcd858bd95f36d21d30a8cbe81b5a433006813e0a75c02e31ce51ee8e4bf5e9db6b9bb4da9e8ff7cd7035a2fe9b29c1603574f3c14bf0969e84c55940ce0507d8ff40a14d3a3807c98aa06d20addb2961a35842b5d31175d70914bbac65f30541fd7939d8476d332481bc935db7686b40d10706636fe09879dd9c78b5d966f85346262cec45b9ca953bfce5754a973482191b13b36cdb489638d09c696f9789ca8ce03bd3154563073d0152b5f7938652e7e872589bf6af3ba89d6a643a1d628c88b615b3f9ee38a0415dbdd0fc7f4ae07c69d33bc6e4ff89d9c048f3040bfb2818a0327b6241aa91277ec984a54b52c2c4134c0f6765334c4b9550c24e031cb1a0b08ecca7e8acc43d3da56e06dd3222fbd07388306b44e8ee96acc822500c3bea60f0ec5ddc469b085f040ad7435af19c3b6c8c584c648f81a3c1532af5bfaa4d9f7800b1ac53726a4209ac340d8f00db7bf471cc277d7bdaba3b289cedb5d29e8b1a0de6933389bd97b838b49b3a02d99764a4165f2cfa8a42dfc3eb407ea37372abc442d49513d1a248ca15837d7200c1eeba96c6dcf63aff0c43912249be0de697e8216ec83c7014b3890379a361c60c5cab97ebcd3a91c63be61161d5776fe04a0d4b7794547d58434f22b1d2e01df5c3496479fb742fdb383182a0f82806f034a73d11ddb26c29a113f41bcca286191a6537e9e2465081026bd75f3d8004fd720005839243a73636179bd8eaa16f74b44f5598ebad7ad0ad6edf8518371752effbbb0d9158cde2adfef8aff80028a14d27017e83282e4a58d3aa87aa413c0d1b455efaf476696989d446625c4104a26b0d94c9660c31cc3fc65e3c4df1ece6207b1411b125cb84e3557d226731a7a20b3f7a5882a6ab25d92c8599da8a7d0f6d4e6639b834d5a1ad0369d2f45127cb1c3222a85abae4598e65930dd8d0470cbf40d2041afed6884134142f1d0249afceffd4a23865d3bd073b67004cd5390222d061931e6053e354a0ff761cd258c36a285bc13906e3a4fed7c07298421178922838ec126cf41c69ae2c9b92619ab4f4d85454d3a1194383446a73388cca8df303e88c55dd395632e9fd30d18216f670538dba3684ccfec7bb4635b2a8d22834b0bfa34b5b20bf49939ec1ef717f399c44a938dd4db05855c2468e3575d93d4ef586d759e572930b84e7ee1dc1fb18a2977fca5896ac9a9b11ed5a95856de6b617a0144867fed36d3e7e8ff73043dcc0f89c5417055cc4c742a0077d552bbe0621f5d8149a525eb2950d8c1d720c6f766e5e8939ca092bee411d46b2a87156db048d1466f97ec9418860b4f810ecf93c938c1bb700075d96d0ee33df8c0f1ed946d36339440a75ceeaf5e4877057a7f59931eb2dfed2c25cf8c8c1304104f31fecd29ae0bcd927db54898e4a111ffb3116d03b9acd3afab6184dde59ca077660c0371dfda948a8002dfbd058706933c4b4d9908ad759dc904e51946b76a5867436f005a2d19ce71996a520d6a195103257550171b731843e416bef558014c1695d5088df4724f7066e138d47711c8063c1316481f8d0bb12717fb97bac786a86b1f9a5671c563afe2f130da2d02b8a37417251824e2daa6c59778cec9ebf043455e64c8b0a90b89404b069721049b53b68565413c38334d8317b7e3ca57e03a76bc32da196655c744b65ce208094564dbdf75b7dd1ceed6e687cb6c33e991132225d05961f3b1286294dae60f2b5e916bcb4c5109e9cd706422eb5ae2ae1593dd462be1d3254de039b624537e24d695f9479cc9509dc1abcfd35053bbb1469148ab8b69535b6a24a7d32406ae264e670d49bea15796d45e6cbd3328d629ae50a7cd9e039e3aec8370985a2b1a0398016bf94ed111f901d936a82c85399301e266d506529a4e8cb8a156b512552524763d17dadf27538ba2a18779c5641f2ceef4e00f4acf96b96c3d223dbcf11a67e18fc6ee529f1d0c5ce12bca9dd5f1f8e4c3f9b05f097338fc517e55a08f0a29ef8caf7b0b3d6f8abc1f930a7367a40c58f1af0c9452f8e866c1be94cb1e10b8f25d5b84e601da02555eb3834514a9707ec8d17ce1b9f1fafa25fe8721152e6b294d391e83a358bfd180718df442d2341f7924da88c1f18773266dc326451aa57efabefbd9a5961c1b4bf59fc240a3b2fb88d10c8006e46433dbb521eb6b2af8e7808c4683783411ba2f259273a45714d3e431138dc1565c9a50d639956a080b639dec74c42cbf5a38ca79e6cdb2757e5bcd9fe18a421c48ee9088f87eeb22168ee57508a1a494ae8ce0b902f3d37dc799eaf69da83b70ff3b07aae4ba412502ee2bafa3e54e281a448e024b7a0cedf8b25ce751d0638d046671ffaba8f910955ba929a4aae6df051c7b89a70aefdb331f87191304d438517708a89ee55c276dd93baabb317b3c30e8406fc70f26ea09a18f9c2dc4b527a3171011dda53822ffe71aa39d9c61bb0d01668c7cb0791a26cf5a84a4e764cda8eb47af8ef0641e0407eb0363e9de0ddd095f007b7cfb3641afd06c355972e0c9d2b491a903410cdc1ce070fd0bc607a3e4db6c41bb13f8b2d2328013c608975b32ededbd44ed29b5ed7618cd1be2ce97d9c43e76b88e226cbf4cfb2c51fb6b84218336b8fd4c0d5bb3240dc9d465fe6463f8e0262479813fb30c001ff332babe68b455fb72710009d1841d10bbe3ddbcaaec39838218778cf0c5674d469f8a4efd30adad662f0fe5ae0ddb452d32153500cc27bd4412b66ec45e6fb646f17dd1fc81f7c73b842b4857e3052aee438d2c763e1ebbcbba7b7069bb6d823394f03cfa195a39f18d1658a78504eae6f75cc451f170758d7113541ccb415867d0b58beb96be221048077e7d29008740cb1b48a6def944e0459b21b3e1004004c9e179001389ccad591c603f2ddc61c9656d59c818d56b711e30bd2297566cb37b89a11022e3cdd6f71c229e7447a4c86292624b5b740fcd8dbc73bc1e46a015a4b3f98cdae09ae00b177767121241cb9ac8c79fc93278c12b96bb8e083aa11cb0498a73b1f5d37afe06705224f43e0f7d3b35a77d70b56660856e0d3cc857bfadad79fb9197b3d3cc242055656db132f6f02fda406a393c7dee9114efdce2457f5184cdb24b2005e200a08b50a61efe929580ec871a4c6e67ba64688becde11c210bdf24a631795da19735508bd0e3ca0ee697304516416446a044f7f727064d56488e407f86c1a490eed9a8e618a11a9409d570d687d160070f22b4f932fc86dd65fd5ccb3881d2f01bc09e3a463bf73d783f5213559fb74d00def06547a0012f787e490ef71ccd713ca081f1f774eac7ca1f375d5b8c3770ecb120d3206976c6e5e1d8cf53721fa152d8da9139720432fa6c991d33df3494cb3e28618cb99dd6b393307a243d59205d725f4a775f611ab67eaaf9eceb852924330b7a99b4a858938940ed5caf7be09bf3379a4987cad24280f7ee03cf166da0198732d8471be63261149d23f3c5f1d71e74558ee93e3bd8be503154ff8804309ee04aa82f19edee7d2d5abb503192ac2ae60b489fc6eab39860fbe9fea9b2b9dcd75c580cdda69e19d3a9f06f95115949d534059557fcc3647457dd0a51b99b5830c29713cc3fa6bec10a1cfcfae1da83004079637147a159c63f632eab4893aacde0642cc2d5a082f00c7f51c37210cb6c1e85b9bb7c30b9b6859ad823a2824244bb938d5700a34a82d02dd87300027febe85e66c00ad1f8ae3f4cb8705de7a307935a9b09c5f83b9cd6830182a2d5684ed85666bc9f259d55854b97bc7af5e5b0bab4fcbb50845cdf6120109591e0bb326e7bb1528a02dceafc2b2a35d747e1461c2790654cf1c28bca13446991f833ad980f9d49fe652de455e78b898729071dc77f08cc04e76d289fa594e8711e19a4c38ad676a582ee9143e91a782f326c22f2d1eeb069b7a97718275c67a1fdfd4d009c75184369d892b1c6d514d985221cb5206c7f68d3688562c543c782245aa3c0a6b70079f244538bb89270199b59ddde6820a5cb1af06b39adeaa131a84734c2ebbbfc7ec2fa961f6be227fa23f238bfc7a21b3d80b3a3886b492800cf524c843bb2e4015995a1d2fa9ec624c9d4df6754fbc0a05f474ccd63dc4a0012dd1099334568939b2be801baede610cffa142af1b033817c60d1976f6c15412d70ecc05267712f7c38d569db6ce29c2fa140e469353dfa14874aa9dfc60650e8e7bae67d9f7ba8c84d65bd13a40b609f899edc8feb8b8b1ac1579330a75bf2fe6869bcf2328f5e4e33b94bc6f2beaa92831115b6d467cefc8a4dfcefa31ac12d7f722e74af1cc316f18c08809f55c997be302ec77ed498890fff92f7a94581621fafa36ee7a1d850b6bb85afe296408dcc86c13ab680b9e29deabc49621fc25906893f898794b11682c468b198d9d5bc6ba3337d3b9aeb6c6a5e6745d3a31144d3023681f47ea2d17736cf5cbf1114af0039e9f8c5ab1a643a8b6a109189816a8492490080135728a91d1645c79e51ab15a41f354e0652fec1176cb03430f08623df28499bb77f02e5b1123e82b6756e026a4059fd0d3cf55fd8bfc9deb3210b0423c3a9db9283f97f4d76bfcb8eafb2e108a2e66cdf599b0c8e3c2bf6c6922b0a2b81f915c9d5f0434e366e54d6a4106ea972f53abe8ff8ffe0ad1eba1430cde400af6ebdc11f2e4e2c3d206ecbc19adf11e2adeff7fa31d917b55ea2bf00cb57c0a65f17ebb620679c389f99ade39a20505d0c6808a6cb7a0fd593766df227df8dbfa84c24339f15354eca15dd767e20c29554da57c66b67707105e845b09a7217d628f1340c453cd388b1da17efa05fa276f6e097eafa5a3b74f99cd2e114f6355af8024d3b8ddd9b99d73f8c889ca9c64b8e0ed9d77a69f99fea56a660d686198b2405606c4bcd45e6e727b19beeb31c8b47b0022f7e47c9d8ece2b6f2ec0a6dcb76b443a39b13338920905390616eace86842070b5c9426e93e324e7272ea108f073748438c0af8d393b38edf9d52d21fa46766722c9289692be58062a5ef5ff8e5849bb343ba9a6266e53b7153b1bce64d139cebc408ab095f5757025b74f6c065b749ccf62bc64a21cca2a22d79cbfc59c513e29976922d085470a01274a09f67c7da32d829b46f8a042a53d3eb56350003bb9c5a13400fbd579bb112e3a0b477808a9d8cd63e3e34a09a6c2af7d443f0fbe7e1a3d28602877c5761867c1c0abc4931171e3bac1608f5cbdc9381d0963de0bd82571511d3ddd658e2a9728021432e7cb66eeda6d4fe87b4675e176d187b219dd77ade78b86318f461d20abce28731e07ddcba90a7701997eca0991f165923a36456ef637fd79bbeb05adb1bc23f502c3bfeee6605b6d3138409c87bbc364179ec25da46d7ba11fc92caf8b9a23397d2e46e9a1b86a90c7c99d613f1e3c085c6b83809ace5f1d6d532fd1c522723decda9bec99793be2dc9684a48fb3567cf2605419f85d8f009dd728169b519f260b527b25a970daeeec072cc98fdc23d4d7730578cc6174d7f8c40619800139210dc43a7639768d87a358423cd79442ad129bdb6adf00d0b219b20f9baa3f2d6ea3fe67d1adefef449ee22a10f904f3bc7abacd99005e61c338f80406fe2c1933d5ef2bfe1ee8ed9b1427951d5ce603bee9834b4ccb297e2acb20259a5cf41a686e3587f4f895b59eb0d527a1f95735f7bb29bb9d59c94a48ee403026ee06550afc53fd9fb85a8410a74a7fc03ff4df74982ac8cd2a8d5d1e7c0315f9438d3e31c0e36cb921fafe6e806f6728654181e51db4cfe13f9d36342bc806e818863c7e1e5efc1c5168be37f22fc77ee04a6589a85175f77bbc9c283f332393aa9c6b3dbdef56323f063b8fd6b835f921f5dc403f4722f0829474431ac012c288f568c10edfd731dd5597e1538b90cc7b7ff1fe00b720d0cb5a021ddc470bb7842a2fa779ed68d676dba9ea7a0736f469b4b0e6d232fda6ac2e910168b5293b84640a7cbf59198c750df56fd995b3eaded30053ed3c0154f1785e4cacde1af52eb9d77775b1d329ede55e6ac3c444790ed3cef6b0716efe2114906cc0622618ad8668e123de4801b43702661449265d930b426e51a24f18135a1e5258d429b2a9858a26bb3983fcec97e10769ff4b430d6cdcd35b12d5802199d5ec720727dd5bad0298f5a7ea86723e3a66074c24fb52baa95a92322d9b664f99464c1c104e0de8fe760ddc5b596a42802c6d711e028d4bf57fe361effee928e831f964157caa124877aa387111135ff422bf789f61ae66a3fbe10bb39d5d0d1383320205795fc08bc6f17224289bedb10920ecceabd9f94f3eada029e345836ec25f0d01603105878447d2d67597e5863401eed65d49a187f4aa2fcb510bea0c4cd5bb2e8cc9181a570eec275b55e8b03f856d06395953c5c0bdd93c918738f629e3b90372621f2309f9d913fb456f0c8d20b0d7906f0bf59a8d8e2edd27b7d08be9fd26ef9327e0607d7f1d49c2ae3c4ed6bbf50c656577fd1fc7078821ab2a5324b40de3d2b4e84e33e7978b9ee0aa7495eea94da74589fe1148d56c03f20ea3f2c71329a770903472464d7014f35b3107d1a7d0c8c712265fed9e04431af9683ed5033ffbb3cb1af11674d42845284118d87134424e057506f42cbdd50c9f8ea0fb2936d4297ba69dc7a4353625866a89e1288a75b11af9957d992b2a4e8e11846a25b4e1685f8c89b6e18e05192aad92ff0f8b7959650b7ef7c88435213bfac0c41b3610ca52adfba126d2993368ee3ba12771f382a8db73989bb89b8f0a2afd4e190eb1950b78049808b2cf3bbe30a7a577027e638b64c4a24c610220774cb83924db815cb811bc9d986c4e8635112d135517580b0b3a489184f1d6317697c1f02d5097e64657cf18e301454bf94c1f9fabba3bf2dcc67ca5e1cd0d84c7ce935e1a7bf1fd835d3f46c115d3500b3a71f60bdd42d8aa66e1c43ac536a5f3095e942cfa2e99494fdeb5b586811c476ed6e5b2cd4205c149725592f00c043a7eab8924ec7ef41f85d65c9c07c46f4e95ffa683d2ad865fb6a1da4be35e301d0aef812ca0e15d566bd5366fcd105dd32b7c8abcafa8cb6ad02c158664c34c8cdea2cb2ca0d2b718149bacc1cc8ea8a14a6dacaf52f5e8797659d8f2f4e7028910071f10c993da8089e7ab97984622bb7386c1d4bc50b386e32a10616402dc89ad01dc1cb758476fcdd35cd2fb578af3ca05604cbd47db4d07750d818b4dcbd2505d6d11cae381f1b23ec2f40da6277b5d0b6b7f4c31c839f0b9fae4e29fdcd958170528b072d20601928376357f3b43851eabfe95a972d6176ed7f96f5e721f23ea7a07ed33dbc77fa93a56eaaac2753e601ec8c951b1562f1c31998301b03a387cec2eca7ff9ba994eb3a57d8a589ce7e237bfffc7681ca1fa3dcfc559c80152bb1c0b7d8ee21748ca807f20c6fbdf72c3a66ea6a830a5928eb049852c2460e5f7502dad794cbb2b673ebe853c482df5aa18c14e9e674451cd881f7e83c219fc8612073d1cd815ba18d7aec4d84a3ff2bcd2522b3c799b336602c4bbbe22cc9d489213a88661e6638d7d3cb1cc7202b66a7ff096ca0384610e757215062f5a8463480090ddf6487c809e131b04d314f631f7b624021e3affea8775d156bd7ed296fa", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e94000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000005dcc024becbeff142d393c7e24ebb4140000000000000000000000000000000022e67462ecbabe72cbabd2ff92be537a11521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + "proof_hex": "0x0000000000000000000000000000000000000000000000001cb3e9df822c791200000000000000000000000000000000000000000000000e6e2eb24f99c6e4fe00000000000000000000000000000000000000000000000f0341c256bc7f8f2c0000000000000000000000000000000000000000000000000002945e9bb27e6800000000000000000000000000000000000000000000000ca22d28f7e6b41a5600000000000000000000000000000000000000000000000fa4ce39d3e2c6c64c000000000000000000000000000000000000000000000000c382057b093f299f0000000000000000000000000000000000000000000000000000d2ab1e847eef00000000000000000000000000000000000000000000000732eae124bf33406c00000000000000000000000000000000000000000000000825bba25f971304c000000000000000000000000000000000000000000000000f31aa002ce9a71b020000000000000000000000000000000000000000000000000001c69dee5a348a0000000000000000000000000000000000000000000000082047502ee34e43830000000000000000000000000000000000000000000000098bc3dc090da06bd200000000000000000000000000000000000000000000000a395a0475f5823e140000000000000000000000000000000000000000000000000001d21f325773d10b2b91c7a8b3d057b7bc490148ca03e7c07ae5285593e8de30cc1ac03ac33ea123a54330466872a485fe60682f01940aecf0a16a29400c85c1ff026756e3feb104e48aa44e3c4435c6a81422971f410e652c2a8dbb6dce04531118b73de26b410850267190a569b1e5c0d99d36551817631476dce47dab6ce7ddfa42abee9cd50d8774b5dbe2992589f46d6b0d3c14df64e1f145ecf602d7f07d6bb8b077ad801635d81fb371fc342b18cf95c1280232e65ec5fdf4f7edc0694727528363aded2adf3a83dc8a210463d985cdb56d2f0d39aaeef44f7e2567bec394a51280aaa41d9718c798bf7003a861694b93dc6114eb66daa0cc69e0dbfc33065a442bff1f24da7df7f66206346b9c7f067dc75502807746037df9556453c8cfe619c13bf2002ffe7ada7eaa20f50572c400c4ffc4942970b82e2e42a5569a83f6f7cb7e11032c18d50c12e995d4aec40ac9bef1f9e92449cc1287a6ae05407339c176d21818bac164c691fda117bc7877f2e7bccc8e7d7257e4bb51996272017b55b3dda603b572716b3071143fad60717290ac7522b0944c91980411ff8755372680bdd7192dd2340cf54de8aed55abc38be8e4d864a57bd60f620f2a66f008218a6b5f41d926dc02c0c633628367cb8e460116ca71dbdb7054035eca657119596c3de48187112be8a3281245ce6e500f797cc1432a4d3c09f50d55d3cf2a76c072123062816948f10550ad9cc66240c3475cc3f7e1be8a364b22c852289e341fa94ef710e359bc943797bfdd06360aab35078dcb0f5f34feae065749d4499ce323c0c26276f732dc63c13091a70d853fd6f9d06c67a9219b36ab6cfc590fc247994b9cc26f3325182becff2efcc85ba9a3c8a5d260edc035d0bfc3e0845aabd9d1b893210c0d54eefaab1eaee3b1703468de3bf3b65973d385114a22b661f1b24e99a9a1df7f33c0a098f95b18ac88841857d37fd0d01befbff8eda91f1ee54b9d9bf4d0972e399e72488027be9753aa1dbcf846b5d614dc2e83df5ed880a51ac5672e90569ae383a1ca0dbc467c684df3bc238b9c05156103aa2aef2eb1fdf18fc875926636c6e183c34d39a42e830a4f8894c70c00a993805d0013500ff4cdd8d014620ce10ca69f17a638c92367872e4f56e8aacb16fe54754cf3f56c74e9296ff6823e9d239478326b2dfda0f711567d671b7c617f59f0369ba10265ba61a2debb21dc7411bc598d5fa875f66505485e1674af48502bfb1c69679777cbe6b7a901314fc90eaf3185389206b54c33a72f351e81beb8f7b0d26511d530a8eab1b28e11b330ff20265875c8ac3b81da94e8665e4a60044918614ed85954f5ee5ab759e1512c2e32b793a0d0acbdf5e4e38d9cacbe258b66de1622bb1fb345e98b6173d2a8df716393a447531eaba0be23f1fbded411e71808271a3a82b4f9975c174051569589970a0511743bf47bd75b9f04e2f7a5d4b7deaab3ca11d0dc741998c2a030c6c5458d176c04428bec5a3e0d8e0e5c56a3eee999b10f4e41008d0ee6c500c4d4285ef91799d520036adce78bb4c4d435e0379a267084b998c065e2b5d2c05da4a111ab012aa81a0bf2854c7c58136cb6e2c629474ecd90a5041606bc645022463e9a9e43918859426adcea475191e505ee6a37d3afb0cda9aa1c7c788bb0a88deee1aefc855d078af3172244c5d3f21ddbfc514c0836a3c498dea40a5511c6fb874ae86b70059e90abb007bcef2ef2c247cbc5c6327d1d53a26e8c39123107878df9ad00525610d70a76cb32b6db209f420c58f9fb3b3053cdd884ab03303467453d17b995bb31d2bb667f55e5a6f98af83e14c7d2f71f661cbcb3e29e42855c0566eec6cd80417f89b09a4dad9bbf28e9aeacde3580fe7fbe92cbc8d6e23394556cab576138d117b28865defcf49b06e5b82ccc8137f314a7b3f7c2bec071311be89cf5d785e50c106985d91efc1623bb02487a838a29ba70228de3cd618e92a7a82355306a10a99302db1b6f705e6e6ae73cd5d6e3e7d5787023be31124704e1185bf63c8a0578e04b8fde9c99c2f4f5b96431334d6b4065ff89d8cf905c47b96cd804bb285ce1d70a99679ce64f47a71354031d01399cdcbd0ad1e0e06ce9eca7c849eba54a12dbe233f25e8e7a041c192512e99f964dbe30ae60c9b0d2152494c0e45fe1ee814a0931add16233f30a51e9865d9f39e6c5e956c872b14b51eaa8a1c3607325a9b7daf83937fb73caa1fe79e4791869cdfd75a2a4d662cbb0dd627932ea4a38efd1bbc223e92e269674601bb0042b816acd8f55aadc0186ec668949e62b5cc325bed81f906800ee00b52c5f0c3dc0fc6aada1fd6b5a823e1d846890c9bf6f66d991dd8547b31d71058b9de07eab2192c8afe637c651f161176928f1ada22ee5450992b6410cd20f9c0cb72ed0aa36e7f92d47592c53b0b5e2f62a81ee85e4454543894d2e45ca07656b56cec384333eb8620438e1dbb07d8b15737f5a26091192263237fb3545f2a7c2cb52e27d82fc63dadc2ef6ec41cbf43034f22b18a487f1571b7f49ad6cb43662c1d88884ddfc4c6c636ae714d0ca4003603acb3a8a4ba957d6cfc7cfda372f89a2f389757dbe339dd2171f5351baa09fa66e772ff12885bbf3be23fcce81cb59e6021505417367c2f49821f7823ba2358ab38328713a2efb8139298fbd22899d5669e32ff033d4e760e7d8f9d1b3399208db97688cd9c02c839ca9843236e13c214a2d8598e601cb5697ebc782d31df4ba699e8ee8ae919ab36f57dc5ca0cb0e5ae37e10a6706eb18514783df1e89de03b764cde27719b9536b5e4b9f15842d91513fcff1171e709145b5e5f215721d3f2ec845743d4e522e9f27aa662317ca94c561e19a62a3aab50e1d093f097730e2abd72721923c2513efd225ebd9e4b87073bc3c5b2bde83296d57572211f90af331fdef28dc12ed2ba3e3457e2d30e25d1cd26a66b9948b4682c7ea34143d8dc9ad72dc0c04b2a9828c7fd365f728364267489a5defaad4506e3528fc2313b419a3513eebddc60db948fd08df616379ac05f981d69e9e6ae19d5216b22b9c725c9f56c6c0542f7d4070281c3d16adfa7280d2f7846be2a75c73b99e5e230445761d0b8c4cbea2c0b6f7bad692ced4087f1b72de9ee1385140d3bf59390dfef89575ea75a6c0006f14bc476665d96fbdd305c046b7785bfa3063e49fe20879f97e36487df6604faaac54f65709de50110e7f64b62567aca3d6d1478b222ec9289fc1f61f423f08706ebb039d8a224625cdb422ade922c3c0d15036b3e80328f349c0cef97e0a1260b8eae02768c399d5b0f0f29946fb8a18437fda491114ddfa6945933cdc3521ebc1f70b5a3cffe275b5fa04d6d087e1142dee32f46919f1de02a266f6698e3e1f7326ea2c0e5a0e45dd29d22f412e7d39f0cb8f0fdb140d8a7588a9664f65018b4119f5a9d841f67618f140b5c0b43d85b4765a1b292e1bfa2fe17a6947d95f1abe578060534da85fa9478affe42e5eac1c93ebdf8e050d285507fa80e7e8720eb1212f85fad61f1bfd5781c2f60717013af98afd0b1018f1d3953038b40c827b29ec08ad490c80187c112ad8cb0516affcf68d114715b47322c59b0e1d0f0d7d05d85f30e3c115d84a815a7ca750f54e65380a208d1797c705b503bdb86142e1b3581dffc3bab4936c6bc2ceddb9ba58168ccf34492e26801b14cfa1d6cbc813c6ca494101f98faf1cc503d28cb425effd439e81aa2a35364d8803d7a3099f3ec344b02fed23158c79ae5cc2ad43741d6cfa830fe21448fc263e12436347d0d59429d15f024abb4c992b911607b0e4f3a31d9496830f80f0049b464f3f1d4cb46954e071c61399cc66260135a1c79012993d59aa1b218b5f0f80f334e9274dcca5fad1e7a857adf7674bb638a9d0e1392968058512045719e05e2b0b9924a33e2769d059a9e92001bf1d8ff4a3c467d10c0226515106794a473cdf41066d07fd466d2d26e02adabc066365f55b758e5a7f7de4f11c1c63e88a4dea4ad07b671448ef5648964f5bf16094882acf6039d3d7eb07fd350a575b3c32e6f01c55e761b811e7a2c76e2ea0c2478f11844f709f68a8ba186d0692161774697b1f7fcd284ec73cec608c4d4970b58619e605e6c720bdc0f4a50ba4d96cbb1641bc70af97f43ee092264f76633be11a88b4c693c07633537e850d91d688b0abdfa27ac8224656f773bde1aa49655d53de94684fd4469d5a98251e3a5b80127d39d1cbcd95ac6b9942655b77c697de2a43fa9791a27c1a6b50dd1aeb9197a93cfdee8d8c51cc1603f26f631a7327ad19003afc88c47469cb195b2f2955b16d08495c1b318cb0b58965dfc7f8c37faf153429001e8c9526753fbd07480dd9cf394444fbf38ae59ab4ea32b0f1d98d4f5615abd67e895e03f9fe072ac6572e3170094d4b8ba9279275f46b832a091d5345ead1b14b425157fdaf93112579386dcf605702a30bceb04ccb587b8c9fd67db5a084ebe2c133e1f0de76153de5b3e8e2f0089b682f578cc0c83be4bcb066e2b8d23e41bf850e18ebf504056a78c10aaf829a5613e43adc2d74df5317560b0f92d1bf9c8a7e028dedd9c914ad2fd6e06e8b56e1043312a8ab2562b8a1811e012f39db2a42d53b53e8862002816727324c27a9099190fa3b9d4ebf24614ae352a18aa9853e9e51e8e9fce807c16f4d1ed3fe76046138217835250863c7372ee35d0e44df6ae7f3354a030614efd55b8fc55f11a1e630f4d62a90e541de4057b60f1e340fe299aaec97842419aa5aab5d02cbb119f8667ebd6596282dfaced456b243a1017e636257c00725145c8c1a7d0ced612168ed7785fbf218546cc3773b80d8fe9a2cd899cc67ab2117ff739c3882ac173dcd0997adaa4836f1d565c5fb896fe92d36be4ea354959626ccc45e32ebe2da8450b4c324e9c84e859826d026dc527092ad112e52ca923617a5a64a750f9973cc25777d4e10bf1bc54f366f5820efd2a4d084b14fde269812ed4006701b1da3b123cfea6825b7408aa2fa247b2d4e48e068400a90ef01fe21067a7e5de43254e905a3a430247e8e945fdcf8301f08064fa14fafb084f0b60ff3820f3a15db26cf3b3d472b6c8e568171b75e01a0687857ee3d697359e81d1e7c35fb460bce208eaf60da69cd3970cc07431a6c7b414c3f14335b6ea545082978787372cac6946fa0a5181a8edff08ae4951f0aabe83c4327f9b16fe126542a07056e734d522aa8e3beda35baccfbaf8f8b07d5861f0a93d872676e20d3c1066991d1944305e34c4dfef3dc1880e5df027313a00f04b9ed2f5c98950ce16a0b54648a6fff62bad46d6201674e4f6c21b626f6c16806c354c68dc9b4aa6c9706851f4626499889d5cb3c148f4cbdbbbfeb07e408def8f627609f9fb4b796ea2f6f8f2aa0c153f339c07e47b7f2a875ab7f7ac5cfce654ec42c87600f21d31616b1c342cbffa7ed1b1d4d288a521634153403519363147c9971f04133d89971045398001304fd850beea80c393312d40c8555cb03bc562af7ac3b349b1bf2cd2b92ae6a6792c1d2f64fed85ef4c1a5c6e8dddf8f9a579a54c916c60e82047dd1600e5431125d76b78b8c2a098f18e46f415869bd5dac6f83169c2c8d29f6b4328a0912196820e9d995762575d0f0debeaf9b5b0e2d270c5ce3b76083e36e08401f94afce6ace19ea8fc1fbb1af3f43e109914b7abc6a65fea2ab48a5000af7223958132cb267dbb555c4bc2399e5426b6b3a27105f22c707becfbacb18ad58306fb6fe953b77d8e16475a79f0a1e4722528ed68fea7f1f534c99c8d2db904cc0cda8066d588098e2046d1ea0630ffbfe2011ec234d0c48e8dc6113a19671866253d11e40aefb19efcd13c463084ac484b02f2dedce09c436be72443f4213baa070dd14cf108dff6cb696f02cd05fa5f3e95bae74dd28547273cc1abd77cdbcb00714b116dd81e1e08a4936266aed104058d594954937b52d87ca8ba959a577108d031a69f7febd6c4168783d44fe7287829c8a07371159878bd11fe51cb232f0921f082d176de4cf1ed7bc8b76c7b56de66ec2f752931bb6b6194ea26e0c6f72a98a7e7145a8975a74091cf36fb93f433c969dd565b1bc5cdfcb205cc15c4642f4084b54cee37e360892808ecc8dff740f475bc40d04195bac4a03e9ec76cad1bebe7afd1732cf16794ef6462ab017a80d75ec20e745783bb35803f4b4b617e11526b6e094e5fdbd4f2ac0531a43d1106a93e8052366edba0ecd93ae8e04f282d1333ca58b422ab5234dbc503685b3b1fc93606c1aa5b712b2379e365bf55a51e63f83c7934394efef92012d8cb6800663808ca41d82a5e76c71495a7eebc7629d59d3741b52d96dec1f9741dd750baa5e3e411bdcf0532fd4a0b394a9d911a13b695df6d5ecb4503c9dad0b958664def47dd332b4058bc0e9b1933ac7503c92aca8608d310e70c33a422f0f3f5417380984574f512371b28f66cddfa883c4006b04a9b3c7a3ba0f6804dd6c4df7feee728a5632d04660c6d954179eaeb44542d11b58d31f9ed821464cd0373912dfe4fa22bd19b7065abbc6ceb26a19981842a3965530707b4befb527c0fda0a0cf69e63eda44b13ef3d14863b69dd6978131b11cabd86e10ffed5d750876db45ed410584991599cf4aca734bd4bf51c781127c6ea4ab85b240cb1bd4facec08e873d35dde95121dbdd9d47a8793cb4870df0a1b9a78b9b685580c9dfb20a5e2a15fa3752130f183edcb97c5be401beb6cf107c0adc97caab3584251942f7286cc06fa9f098e3d246e7dbc07d9608b6f5f991a6cac58ef9eb3e90c9f8fe72538df09b2b7b4a577e8b5ad0a2dccc52226746f09b1124b9ecf6a30f133399dccfa765b5d4700dd4b3b9bb71bc2bfead45447c30a868eb5de7f9664fabc6fd95a16fbc657ed53652cb805c3a819bad87ee0eeea07613f74b099d871c110ad32d1351a8aeadf23e6d0e204def4439207b50b2d4202383e3c044f9459cdbe3b7bd460b412b228e7b2f765cc1c5ef19eaf6e417be81389227ca8073c6176027f2d7e0e8421339891238ac8b089c5d1ea4396b43b650d8c183e6314a3c894dd7e6e201332e5c474a08a6047bad68d93909b92aaede71a80a43546806bad9165fd9c426fcbf14a233da388ddbbc7a4b9e6ea3518db232d05a4a41555db710f110c8f3dec06f2efee927471e7c2956b89410d14ceebc5066d005f93104f18c112b2dfec079cb4063f6af28f97c50ff62e6e6c1e5b5d970ab0cd7969c070c5ace23eafb24c3504cd673d1e5dd2fcafe0fb5c5a8116ea8414c1441534aa5ce52008ee910e68da93e170405862dea3ad15760000906447372ffe6c06baccfc669d8c0abbbee6fbc66a530df1c8a15ddecfece85c3e59c99b2f408d6225a32f1c8a5bbcb092d81020c5c943918ca370ee1066b9cda7ac237f25e442ce78c832078a1cca4ed502d30f1d61f4cb9b31fa1c3339535e317b7ad5291c5ab8955c5fa35886e5ef299dee976d839d1659aa0b7798f3a04053fbb3e00a9540ae0e1e203b830e5076fec300443e13d97564007c833faa0afda19fe75d05b04d2cd5243a1c81ea8ad4d236a1cd7a49e82d0fe8260ef7db074dc985bc53273cabfa4034402fd5f0be8f50f61d64063bed010ddf545a2a0819da87aabd2522b9c66a1023ccdd30183e1c297ccf73678f5342d0f6c3fecdb4ea6549f6f9890570d83521bdafb330e97cda239c6c72c27822f493589f2c3a9a7bc45dab9f230dfbb6680e8f3f8d2d51c3d0ac847f7a3110440318e046edefb4d3a5c854747a2d517240483a68a859c5a2517f9d257642c2a95603c14929a57100056111310713bbee85e2eb28d2f56212ec3df83e9bc2d95901789082be5eb67ccfa5d9fb1802b54fb79467cc5b7d9105117cb0be5c3774ca2b71c5c6ebff3be8981994cfad0e12ccee44ba1686ddb45ca9e05c5e827cba306084515b046578f11a9e25b72c089771b551d6cda59fe84c62744869636eaadc43ec56d5050f7fba28c6bafbe32eee5865d3d6d2f7ae5bce8ceeeed068cb1f022728177e68ac18ffac673bad8d2e5ab2e2f406cbc4592907bf333bd5fc49dd9e01370f3c7a9b006cf71557d51827da6014a008f75d47fb0c6eb5f92f4967de88232ad37605c6100984941f681513d9f213a93bed129c2e3e04ade7b03081573e8dee5d8efe325fed0a720c9810144f53c1a953c7076ee5f1762cd845b22f20de501647c107b71a88d034d1383413369152bb657df8a1586b7fcd8ba48363c9305ab33f7f5b244b97287b02a2d924fac5ad4344918be6b863a65249135b38df4166c6d7dae73a36aca560c91ceb203fdfcbbd96b37cdd2a655d2db5fab215991015eb7aa3a6dfef01849794be8f1159c8fb2e0ac69da8979d7646f5aadd53063ec5ab68bd06439c2b9ce469ccbc19acb857ab262170b62d959f7abe12547d52b38a70b048448428f3d56e7c52e029413e913cfdc8db69f7e7d37ebbe49e29c75ce22209ce58b4ff9f1d9fc9fe802af5b6551d3a1756dfe71637f61bcaac595f3aa1989ccde9acd830305f80fbbd2d16fbd3956488af15e5d4fdeae1b5261646dd5fc86d84bb6b45793e77695de20fb8c84900d6f5e8f54a963dcd6d42e073ce04fbe219e2a4af3a8fcf31c952051db25526c091693d249eb293bd4503d1103aecbc69bbe5bb5631483f1b9594442e7fdac9e909c715dcf47095e393e79f8d5b4ab497648032f1629733d97ef3ee1d8878696954e5fa815d5d1ff3ec0ab8ab9723d6837924e59169682902d3db110d7cd26eecc981d01bcb139557c486c744018ca6bd19b5c388a7e077c6e556f706f4ddc0bf4429db4217165962bb635099628fdd49630587a94143c0c89ad8cc0243fa20eb7f688550b80136aba7bb80ef53272fe7d205c34de7b1300c99023a07a06f52b53dbfd06e521156b855efb8221e0f69072c5635a904f63b7d2d011126e6720cf2927e27280d4518b1cacd709c5918b022b40996e1768dd9d3cb128d1c9fd2a5697a15b330812eb172ce016e47f4bbb5635a1a22c618e23281b471510c77e93d57d882bdea72c28849a4c0516c9ee76b492065afcd0c4fd86d90de9c135ec9a551e0387d1f5937023543e78c0f1e915cf6488565eb451ac3b3e20a6602c1673f7387f668a16c81eb509a15ebe643363b15be3a5b325e0221ec1cf0bc3061a2eaefe0bccd271a1291add175632b619454843e7e1bc46e34441587b61e00a40a910d9fb377770ebd08cc804592ea16e64684237cda52ce30381696281719ca3144b11fe63310d8c20cfe793ff404ae5d1bb37990c661091a17c90f9dc81a12f6e7b6ca63ae4bddd5923718d2b57fe6d4f11c443e06d49767c13e458bca27169799b2d6e904950b62d0ab568bd90296e39f8c03b2d90222508dbcaa76112df2ff50a75f281063fc81340a1ed747062fd26481f85b13c2de3a476b3a73fb1b39392a1f122f0c51a5c476d7895586763837029da273e25e111ba071d6dce602a13dd662cd862fc55adf37d20d67d523839c127dfbd1845c858051e193399c2736016c9c2ff55d2c1eec1456fc6d05a71007b69fa06ffaeff35cbec075bc102dd0e482b0b3e99e947f8c7f8531842fa58ce3f6f7111c6e52d0d54a16365fe52e15426bf9adf5031e27dac309386109c90e1e4780b9d03e42a2f450e2018f902c6fac8c3a202c1a526fe1c0c02ee28473be3fa4bb274cdb80f39cb0f8c958f5128a6d2cbcaf93586738ddf271c23bec5621fb147d08c561a89ae924383c048b086408035394acbc323bda1909c591844a75b4ac76486651352f0fc28cbb946a0d675cdb218ea5d78641584ec8447db6ec0d870e92199d0711e092c7d5fb7a1b0d5b8659b2f4f09d665914906900169696e026127ec615dfc7f72172d4e9c9950e40b402f05affb26f8e17d638becc4998d96780e0dc21262cd79bb5dcac57f00f9fe8c5541078e8a05994bfae65586c513d8e93142fd0f38e02dd1ac04378db089cd4f5d445f6c772cc79813e2803f220cbfa3429929f5498ce561493460b3613ffee4ec8aabaefd4e078e34431c3623277623cae710755721a6889d28dcb682fe59bd2a912c3a0ea267afa0bcbc29decc7598452e8427ec96bd4e4ff7dad93279bf4d91fec451d7e038f87a15f6a8a4b39f7836384879e5a558ac7f12a5fab2c732e618b33458c70a287b9c392bf96e15d064acdb6c85b4ffb27a8d6c08f9403bd525b914f19ccc4e0752c1ba7303f3ba9a414e6e620de995481fd369295cc085ee0d4086cc322511612aeeba8435785e0d747f6ef41113e99f02daeb6b9b016e04769c7707b166fc721ba312ac44869e72db339d8d94f12280ef730872d27180307091a47a51ff40387adfff6cfe5ff23ca72a089110af0f3877e2c35c0111199a2422a0bb1b19e7ceefe12c2683fb846c5be7a1c6f60aaf4ecd576c6b2c62cb5a0bff53fd46873a6782deb555edc23bbc11bdbba75dd1be6a1ad6cf829f70072d47f9170994d097f05cf07012dc2b0b4ec8dd4d9c779b858504a67c6833c22e7d6ac42fd50951f66bf81ae3a02f2f806dc1b77d076b5890bc4f44044ee2f2eafb480f71d11792aa60ad86e548a74684fd1aba10723a92b11ce1a7dd6a5561fe49c7c07442f6276a9508e3f805c833fc367856062c57f21f63a55942ee8fc2e9471647d1417f060bd8d0c66a27d1c1f3901339d4697ff39e6480609fd878b176f95e7a6596bc761cfc02a612cb5807b33e7803fc5b1d79bc27eccc7d3f035177b4134650ab8951b1539c16d7ff813346f0ba46042ab279344cbe1bfb4ac912b800fca5c41a1e01e2e48367606d18a6ac9ac259eab77260b92273b43a0faad0559d05bf0d90c3d1f7b74a54cdc688f619dd581aa440f8417492d917847623a18ef5410b04c88ec70cd12b19d1b4d506a86d8b42ef83bba4b3e5af3c49f3e8026e2eb7d9fec7687354ac5b0887116157de802696db73422719c1ea40e944c3229d5159ce10079adcf9ceb5ff67124799050f8ec9e742b8bb1ab15d470e72d890af040425dc1bf62303ae3a3e7a96d2a43c893e80ffb8a28f4111b111aeb361103ee2e7d8024fd4e508efe2e2c0bea5a56827b8726cacc7dc08fa96011013de61510a2b1b058e3f643c735507536f749772c7baf6cdf30eacb40473ca90c745323467fd21411c1ff380b74345d7e144ad0bd7fa7d7c1fc7251fd916348bd38e0169732cd4a5c892c1ea29cff802ca57db4593be104a408399a46f970c0d6f84325a21b7cf69ee061ccc2a3a50316c840af78877ba416a90b831f59bced3bc2f42a89d5a9b0aa377090344502b3507058d31582b403b5817becf3abf3063e40611dbca2846cc99bc087b5f2324908a49e5e08d09fded93fc0fc1a98e7dc5279ae235dfb49b929674403a5e7215847f2c4263f4a937f45683395abd3fda138a1e8041e083ec5724df8f5f59ee81ab3d8f91243b4748ecb153908f8ceddc7b2a6132bc19e5dbc68d1159619cab24990dde0069d6923ec90219fbc634f0ffdaebd6e2dec866563e71fb0b99b341a3225d9941ed6f84bfdaf764bcf58829b65008f8f1ec19284d1e0d08e4fe2bb65238804b564e62228659033401f5137bffa7748ef26ee4d3cb886995198691f9bd7f6821dafc091f004ead76deff2afa175022fad1d70f5f312bb20b8c34c9b38335493d62414b52515b4c70bb5fd41efeb8bfe54027b2630d780f4d4b4c5f6ec6e38d9ac6e64b0771cf910a02666224d7c9930a400d381db895e295584fd6dd968103a6d4c18e438f5d3cedc0b55b1cccb54a12313c43815f9ee10284257ad86a0ecaa993455d03fe2c4d54daf8197032b9baa401614ced4399d79bb7beef9ce5bcd8932ae1cf525f565402887c83aa92456761d0abd91781abfb5469a2640f41acbf4ce293417771fb35f5ffd261422ea27deb808b8f1e7f676bcb61968d1cf3fc00f94fce26a573eacfc8c1638da80d4eddde126a10c89af520a37d898f88198aafd4d70804ebe05448ad486113edb2b2aebaa18e964f683cbcc9090e294d7a7437be1ca1ea74dcb36d84a2321bf301db8193e2b94705f8aebc28168b8a51c4ca04dcfbac794fab48303ff71240442ef421e250fa2c370d0ace9c55ea508ca6166436d03d3df8315970bbd8bcd844f4bc68f5b06299dab59818f88f7e301b5c4316aeb59aad5d5f7097eb27b86c0cd57fa81c703af3bf34b8c520cc3d2c5a52747788eb8199250fe53a397e6974413b277b9c70a27b0ac6f69139f75109d096a4f17c88c4e93bb3f9ecbe10c1dabf88f8fa6981df485dcd33951e7e060d99193bb414647953005e6a41a281ae92fc0b106ab1d0c649df7df67a84a19fbe57e78f9cd919f876ceb2600cae60afb4d422a6daeff0f3e8b0010a88b0bbbc353e403115f4fd89be0290c657993caed524b65b9e71e1354e3d251eafbfdfad286e16e963cc476f575b88f39d75d4aba1c508b0202150a22b7c9062f93aabc32cd5ee70b08a94da92dd9c39e7d49fef5343e8730231d1cf3ebfeb1b25dbcfb218b5dde3966565d22eac32cc996110a53c0dc12a9721325df2ce3db03f36341907cd84122d2a897bbd9e35d2c5e2827a496bcc38d982c2bcb25cf055472bf29acc26c66698133800299b78ce48a634e6323bae6db88f7211ad69ebad87c01ba4d11ef6b763ba53395eeaf888db5c71163b7d73cd536771ec8d7e9f205f0bdeac8aecfc6ab04715f93f780868392380ff39e7999c6270e0d3997a8e3c12650b84b34333bb27e91818848e7a01a41dc8f603c55fec0e05524fbd9701f9695c6f1a89a4fcdd01babf46de6fa4f6a1abe626ae1e3fb09ad630ba5ddcea5c34b3d7db938ce41fe53f504783b69bacf58a3153bf960af8e7b92248d686e4891e170074d3b614db88a20b1a58e021253a639e04e495a18eeb9502446d5a60190f1b37f202ec7e975678e6a91bdf5f4bb15cca09c259f6d3ad63214fb72a1b7f6880ae472304e65f140fbfca829ca4ebdea50884189a0e34ec4f80d70eedb8edd7539e615f29015d0a2a3533bddccb3ac3319709615a7747ddf91059fa99e04f1fd1d6961ca462b4ae617c861a104f13758d93a106de1f3ef546d2a731e0019756656b099fc3d53d47386b271558ce7f557490f78816a160b9642303f16a6c56c5783ffc1f2bccf9b1c2dd9095fe89596add52b8cd0f27e17e0870deef8f8bb27bb30aeee2710f187ce6177eff64cfb0a0830d2b2c4424784eafc0f2277e643aa11411f9562c2484149e3ad02c0550338fbbd690aeb6eb7f458981086439d92601f693280eb07e9b8d53730775f628b1eeb9ec29e40e1e83b45a015ec2c7f6a2bea0d0a76c6cd0d4eaa26f5b9d8b15f7ae79a36f878ec6899ac75163090de4856d27388b729d8cb911f2dbaac4a61755208607e4d59a2191a1e8a0f92b395ef84f5cca72650a5ec96711783064e0b3038131aedf028cdd0de22210c7e41132f4dc554c726402bd9867726f671be49f37317ee6760d69c5617b6240f2a0cd8114083dc0a60525f9b94e16a3c61893e7b4cd4f91f030662ac3f32b00e35a3e8598f3df274610f9957ea5be083acd8f25279cda1f89f8d5c05a0a1b202eda0b2b2846b1899e37ea945922422c83846190fed65f9e24351f0569ff47d29f0fa3f89529e14f881940aa1a4841ad144c7c7696ea6ccfc4ea4bb7212653c04f248c459caee04c2e37d3e7dc683dab19dcf65ea2b26c5d7cca848625d7efd0dabbb2426915687ef3abc3b069ec8307c050d038c74ab190645aa0dccb1421c04b258ff3ed12c8db75b58a14309d2e26d73d52eedbfb78a0776ece8ff981a7b04b066d7fd4c3379ef0de749d0f0d4cb2fa9ef181a6337578033e287cd292a492a7d9fee18d21a08545b65da4aa812835303c5d899687963bd441be43ccc703e23483ab9796681a8df92a34ab2b18d397028867323f31f7049deb704611ac9cb12fee16283124c9b2428cf4c007af6be15d7b5031a38fca9de3b791871cc113911378a4ab56d049babdbfe707e94528f5a3767e39df784cef4dd84c2f1fbf6ad21654e556af2b701f4a90b0250c325e831293f5b184cc33dd9a4ad1eb0593269089f225f0ebedf94b11fb6f7be7d859e0cfc3eb14313bfea9686703262927b4b146bd2aac0450d1a61d6759af2ebbd40d8eb989f86399f60a5667de00a28fded06200c29f6eebc1eb9e47664d46dafc3afc47d3a4ea9508c16cc7c0a80dde88a02e592616956b9a090e0d519584e21e33221560cedb330523ef2b7c0ff50acfd146fd4d838c633a57c87c264b49a494c28e364f8ef5c6f3fd011cd6b32a0c2c02d60abc970afe83e55897b2581afb4dc367930ac9fffa440dab2ec982dd410b9287a000b0612d4b154560cd9b564e94c8e2aae0bbccc7de765a416650df2174519caefb7804b23bf526429bb01c70f3480caa10501782733990703005f297ee2051ebb04e3c1df02fa1209f95314f1236c8aa37a21641a3bda5f949d8292a718024bad13583e6c37df502842df6e1ce87aaaaf5e5fd5ea2bbbe56be9111b95f20882e244c3d5cbe120a620326c3cf2852b2233aef66bbc6d20a3bed7394c4f49207a510fd873763e5f933d6ec9552bc5956c7c16e579968778d059dc5d3b95f6009e8c3ed9c8afeb8ee33bc36a209435fca7353b43d7ce49d0b7da67d8c3384a", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000adf8670373d6f0424af5f29cb946f164000000000000000000000000000000008f927aa40772310f80a858405907bb7011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" }, "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000cb72a26f4bc2b655f000000000000000000000000000000000000000000000009d27836abb5c49cea00000000000000000000000000000000000000000000000ce96c2d18739d82800000000000000000000000000000000000000000000000000000d5f41f8cd725000000000000000000000000000000000000000000000008883f6e266d0f541b000000000000000000000000000000000000000000000008c334df8c530f7d2e000000000000000000000000000000000000000000000006f020d9ffd1efe48b000000000000000000000000000000000000000000000000000085100738e49500000000000000000000000000000000000000000000000191dde7354124a1a4000000000000000000000000000000000000000000000000f51afb5d050a083700000000000000000000000000000000000000000000000cbf6b9bd0271d19050000000000000000000000000000000000000000000000000001f87fba51408f00000000000000000000000000000000000000000000000507d6c41d8c3244190000000000000000000000000000000000000000000000025dd8654e5a64657900000000000000000000000000000000000000000000000bb69b4488a6661578000000000000000000000000000000000000000000000000000000586bb4925a2313c38a4107f7a1bc1cf105ff5dbe5cce29c167c6d1454b2ec0371ba20730be1215aabf311de0fd5a7e01718e75cac3a5f3d2e9345bee5f0aff320d0cf441b22b940278a9e081c76995173a5668ad1b3c21d52e6515fc1c6111bc8570a97d2513cff4f84ee3292426574cbbd61b9fae99e95fa54d61edd498a00035ccbc19f911aad47df27a1f8d80c091ee4f4722c1831b60a2ed67b54f0d892a3abe9cf9d6000df18f0126f35f6af728bb8c9b5d5b06f5f0facd0ad007c578aed54dd6350d108ccd74a2a74a956663b2f5295ec9f3b56907a436c363da245fe137e4a27ade2d50ba826d38743ebfb8fb237263a20e1cf000ef5a59ebcab9aa9607b8c53dab08008b33f585fe76174121a00c186674a178454a2f1161fe1ce167675ebfb1ff20a7bdb97e3b74ae55d582dcb0becf8e0252d35b442e1ecd9ee727d3c73b1890189f5984477376e807d8fb5294234a8b233a26f9b6c718cf12664dbd17a94f2430404927f8413c9c4d3ba95ebefc4df717868f29e2ea8eed471daa1b632670401475056ba0bd6c349b55e561f7255f437cc48c7a9f7778ecced8c8d58f146f9e0b05ea0d166787c39d8d0bc4db81911371839929baab4dae64cc4d8d574fd30c1d6ba7d0933eaeeaab5dff6cb1af30ad4ba340edabb6dfe01e6f3ade14e206fa12392c605ab00e35de0f3bf35a27774dcd013d5d4038643b21508bb87a59519c1cf7aa122725cf720db8e870728206d50dc85ea0ef6231c890d5c943ef06ebd707e8c9e2a413e944d576a78cc3886020b1bffc604797f597712e1c8d81e3ec6f2542d46bab3e1415b01a176f2cdeb38564ef8a903260938cccedc9f22d3984d92b6585980a340a32614b1ca661901e3801bcfd86a4d1e12191b45fd9d216cb851c7a4a7a32501751a1a4c066f9ed267238c015616330809488b9028ac1041afc06eaaa51442c82a333c6690d7176e596a4f84454367ac1eef73fc39a250e9aaf281aaf24528af51d7061ce7f33dc128198c8d69c5512195e6ff5c7d16e9d25c00bf088a1174233241186c1c93e4544ed8feb5c12ea7eb189f8057720e47524ca15bb6834930409a27b99b6260b9d5df8198ff26dee77f148e06d93bb26b5da1110a5b032d732ac9dd68ac656db90510eb6784c09b5a36d3b653e4ee61f4f4fda273034a8a8a9d894099e482217ed8b5866a4a1ff4beb500a8121f2e0e97c28bb191fc3b85b16dd65970de19f9dc158860f2fdedef692a5801889143ecdd701db172d0437ff30d393a70585b5296b85bbeca15d9a670eab6a0dfb4811882d1983158c4ac2c1bfa3ca560948e794018e4f76b1b2bd25478552837685762e3192112658e9a1e375972488108f6e4ea30062534dc4eba65fd538a3913a9b502f37f411adc3f57187ae6922cf19790a0abe806939d842a4faeedabeabf8ae695fa38b0414ff61f933081d600479b086d53a04eb9332d8640b27783be4808412de388a16becbc94f095ae4f213c20af9322cd1b0038368a1e96bd4adec2338f3fef17f14637b8511223255c5ff15bd5cccc22bdfc3f2d434d6d5037a97869cf0dc02b523019b5dd4f9cd45f07f725dd1d8b0fe2fda8358066a603dd80599f3436763910386264fb6a42c239e760975d435ae996764e3a2c59d7d59c69c87b70c2a295107cea1f16f6c9488b10bc111d9ba5ce00dd862e42d4e06c6f7157fc9f267a337154ae8956a5a22d5ab4ed3d1a57de54132903b2d5499117f60ae26e7a82c1117099713785a7196ff1cccf53bcaddcb8df2b758b68b70bf9aaec914925fd1efde0287acbba337ec0e101459bb740b78cba08a7cb9467a52d94ae55e68b4d1eae304215101d9400a36c7d0caeb436d7eb4b929fee6394c10ed15f589466d2a9f682f6a1978b75fcd7440fa4b75a9ece24dc94877ded7b76f596687d10a4af92f301c3391a9d9ac3bad220771549d0b32dcb0c21f4e9816437fa46339dbc0c667ce03a1456de29a3e69252e7609eebb4743deb6fbc3ac6a7226b5eb7ab4f35f5e8a253e7fccb26dc26f35d4d0897f9f34a98a833956a5d8a34f552cc52b8eedfdbe2c8bed7dcc889be5ab58f16a066ff0738e677b964dee8f77d71ca6fbdf7f6a5f1efec00e95fa2cdf9cb13fe409c243c73bbfda0da9389698bf926bf678cf08bc1a154a4e2740d06e239be0ab6037453f90080c7638fb3a4b47be173fa5fb5dd927386e66d7c301a0b7b84a433b7abbc0c17ee52a4610ac27caac41f9a57fd6572f94f3f0feeb716798e345e74c026fe1eab060a9753bcc6b494adae78b8c48831c4b3068e874e8ae97f098c279efce9fe97431a88ed6383e376b697a8dd7b29a06cd19dbbb19bc26ef7f8e24dcc533e1e6df210f3d9201d0818a2acfed7cba4609dd1f04bba079b62438571dfa4bcd5f9cb3ed792201d81ea8b469de00d554952a10164c6602ba7ee3c93eec521d5b37fe71f00f135dce445d919d1456416e0008f9e1ea2c5f0edf3d8d0e705fd77daed72d4060376d93aff9343976c9f9a9ec1c6fc299607e32dfce088440c1b27910c04f99a8c19bc048504ab3ea1c3149cc189d32066676f85fd4e4b58360c14dce07bcfefb8786990fa3d2d02f55df9d291e358eacc2f8cb961241f3f1aea3f16c859f9a9427cacf73be9fd75855899aa52effdd8445043da2bd17ae2097c5193a37aabf27215edd430732a1968ba5c5cd163fd4787fb223c6425fe79a53a468f901eca21952d954b4f1acfd51fafdf82727e491c3bb70e64d50280395515573b29cda31e01e06f7b2e44caf0b74733a091dc63384e63175842a450d156987bfc733bb50df42f4588a1d8aba142ca728b80e2c5cc3248d531aaac9ab22134aadc1d4b8235879649e448af0d13256e8c70d104cd193eb25bc3b69749015b1977e911bd0360598ead3e73053fcdb62119086244fc8f8198fa0a02425d387d9e842b0f98a41710ab2cf859d59f93acf99b00e0ec230d1ab4cd5571f19f9a9d89f04a9fbe3eb93643915d3083afd68d6ac67e51a886c2e7401e737f324852eb1058647c1ad920bcd4ce15f4a8f1e05536e690c1cdd151b9c5c3bc9eee54d4f92e069b280834727aa57425fd74dd464e11d6e47133a5dd5da94b805eb2de7bb269a9ca87124d0ed304ee1865a1d7788fd4e0a0111fac6499e357b1c303671d1bcb274e49850271998e5c3fe2505a32320745c9f0788d50f5a3f0f7af6a8a11623e0d90e96261a23a0e0be4406fb1df38bba0cf1126c091c192f49185fc1b85af39cc747a605a758a43ed4de6bc969a8e4b94b4e04103b6c7e771115bc25ffb588df0e378b20828b626f60d91609ba557aa16c7a2a86d96745a4af60cef3377e54f1f0a918418915c5b0843a9e8e62defa23fcc7236cbdf03146f9d5cff3c2738cc19c712bfc1e97d04c592abd2a503977876305156635927f604f6d49cab3351b6bdec01b73285797925b9d56231a26b151cda9151bf01dc3bf7843552f07c3c599b32a74b9a1924bae7e2462538fb3d11f872b301e2e979c50766eeb68e2fef80423c0aa0be3237b280ad3b287a0dcc6ec1307118d89e95c499e5375b430f6834447ebc4c975181d5c801730bf232099ebe89d2361ba955cbe174d15ac109b458838c87e0057fee651ab45699245dffe0e0947125172de2d5a309139f81385a6de0665004aaa2a099b3ba491262ff5446cfe2c002136e84f99687d4c2dd3401a0f272c80729f6a49124e5256dce015cdad858b0fe213385b7f84bb2b36ce9195f8698fcb1e5eca0df20b38066799ebac0db9151540a4735f7dbd647255af0ba418bbedf05cf04ce6ffea4263693b6f9929cfaf1ca0558cdfe52634a0dc615b3937be0d6929c9203f15d3b16ae394109cb12b450c26cd6fd4594d042c25bac5212fa45710eb1ae7c12035f2756cc5b79cf1275d21fd427ab4df6fb02fd7e07a7ce5912a7ff309c34b6e8214e214933e375a867e17c84b3a9f7402af2621a1737a0b6b35265bf0e27109ace625439cdda98fe2cf0b8cea574da812b5e8675b450ecc2cb603ba8cd9d23856c0157175a52310c26e16ade24aeb764c5c1c8524595eb4f3add96c7b8dc400a42135dc18da82da97540fd1231c528e70b362440faaedd8d9fc6984f9130a767dba76e3c7a6bf758fa40995ca9d7a5af60270bec0e91cc535b007a685c8c839e9c527c5ab8eb7ee448f0086c298961b8c479e1d2fe0bd61eeb6efdc10ec5db741331f4742b28630fc8f2cd7ad5ed08569bddf91f47e40f0ebc70c2e8d7a5344ae40968a7e06e22fdd9c0a269f5c1d73a857aeb0879fb7fa857a73df6fe8c5699a8fc37cd4d9704391e52af11ffd63ca8cce301c4d2e7046f3e352f3a1a84127940296b9bd7677c48e2a09721bf6df1939a2931b67c40dacbd49c2b0b7b73e173d0257867e03c509a3d11311889591d077ac2e47fa0f0934712deea03834c1f20e1d52b360ae53ef69cc2d098bf4d516ddbbf219dda0bde7756c7a969bdb7258490da74441edca6545b61c59a5c01c7bcd3f276f4511de30c3c9c60a98c5fc07844de934975c350267df1782ecdfed3b15fa815b1eb0844b9a36c33d3f4b6eac1d5e829af190f37cab9509ffe42d384ec7e245b6140a75b7c17af5d44d09b4073dfccac35a60ea12ecdf055f473428571221c87429f479c89e9dbcb99f63dfb86298d9b0357cec7da6532c0bcce9bf9e5a5fe86f44ac9f07f04b76dff78614438b38939c01718301fc3d2b5de0c98b93441550e8d759eb21d914ca217a29d6be440cc2d2368863129b37033998b03ab2db1ff7f43c41ecb606284c4ed2b966ed1b3d7545dd1110808c25237b91337b27c2baf9f82732cca4fd15674245bb6c13c418463c31fa7718d281060f50d783fc5c32a04b819c7e843d15ccb658e7ed3601c3e11460874102652d2746e2fc3a238955923c713d04ae01e1588718ce72710ee2d4744b2b7c7f066e23f97b1d534f10efc221a8adaf64dc52200370e7b67d2a1b56dd5c2fc3cd806315ded78e4cadb5b5b4b51a2ed1a09deccd8f6d7ab212cce6e667d359a56e1d921d85c36ffdcc4aad0db718e2822ba268be4cd25f748cce52d28094aa304d8ca2003e17a0062957689eb6c3c8d22e491100c9049f0f406122b94a8496256ddab51f0e3e1ed32f21fea7688dfc76e43e7a0a0f569fa50e55ad20ebc7281700826926d2b55248000e0a120f542210ce4c3c666232bc6e2862a3c5baff4e739870c010921e85d0d413b13bb5fd7d074c5b85a3e77d5fcb87c2319f6d059759bae5cf2a913ee4800b575b2d10c30b1e960accfbfa92bd0270ea55fa3076b4b583143c20a8ee6d30c78d807de60607cd2469b66b5df59484f0f751cf0bdb2eeadab0ac0bebb994ee223175e760f62893d7c241aa1e5dbb177114b55fbd5f81d009447621e6b9e212ea7f25810a7ea5cc34eabfd6e35a0d3daf29c62fa09ff67c782a0b0563a6cd009e2ec14f1677dfecfbebeb6e18dd9fe2759bec8c2259c64a96c6e62cf64277d23440e9df48b88e25a23507a0b537621cf6d65a7cfed83b9d5e1bbc030bab18c76b60a07de1cb3fc5d9af66747bb0f4a901a930ad67e3ab13ae17d017615cab0f232437159824ce1b76e9065e9457dcfdfb4ce9b364ed06c959cd531913286b79453bb50e0dc4198993b58d03f61d8f44e5b195fd6a3ac0635484d31e96f84fa9217fa2c8cc6745a13cb027fd85c3dc5c12e21291ea1d4143f377a816c048dbf593d4ee59c1115477cc5fe0921b0751d00b36467f73f31d87809faf1f5ff7387d960fa1e9e7bceeac38a4b3951c7887e57c73f639b60c03c9e2f10e27a87b83e774dd7ab1476413dd75c179f4777e339a431559295099e4a521a0490343c381f3704ac29c32f659803addfaafbb556da2f4e2e4e20c9cf1a42d639b15c324488caff0a612c5d6e23afa5c97f6c08140c2b68b5e26529b7abd3c422c033b8ad5cc4aa0849611f1336a1132860abf0496caff22ab0d6d6a8571362cd21cf10df66a695c3a3fae7f5c4cf0b3cd05312075ef875d210ed6618b54cc70852cc8a2ce2934aa228623c7d3e28c8b95a3b639909a68786e5e2cdbe9d83d670602f57f03140395962b50608e083702fd9d54fb26cba708aa32303ec6db94f08929ba7e70015e2dec89d790e53adc8832dad12bea71a76b062d9af4914a0712191517d6c1f295db9ef064765e3bbf16fbe762134852bf0d388ce24bd179f721c4159bbef83fe9a1a2faad7860284fea07825e7744c103fdd244164abc6ebc3d8a1a81dde0301fb25d53907ebb44fea80073371748b3cba4a5146b7e26bb7df99419b6c729bab92e54c5b85b9109c89bb8117d10473fde6e238cf3be539441fd7f265f5c1b07c59a0af8eba52e61614478d50b01fa5ae7b04aaa8593c2b4e3f8f50c2e2864be975e2a0aad0c687128530a4e7f729012edba9b8d5db3ae708aecea069440ee37908c90d60d45203cddecc3b1c760c4f8694a2b31be3b69f6fb5bc42447dccc556c4f4f3dbfb61d9d00203ce2ee1b2d525a12ac890edaedd7eb5156055b878d91521a378052cc88eaf6ff10a20578c029bbfea6e1d7d4b8ea0b70bb3049cdf4083e12f88d74eb75c55488b3fadd884d9fbe62ae22a81fd8dbe4722a27da0e76c31d588779327ce844c20e507f2f516133ec25e6a3c2bfebce4d77f718f53a59118d337ad80ac883b2ff1e0879503dd15e78735fa17e15782e3f05791e53855a12f699b26092b96a0a30eb5157dbf3b5707a16f397dafbcff4e8ee3102d0cbe81199bcfaaf6b3ab751aa32045a578a9ce9c2c312b1847a7871304d4710e5eab9d7ccb6d83f7754cb4e7ac2efb44d45d4165df31c86d03a17a375492a2078373808c9babfe4210762dff443c65be414a814311ac3734b60be87a6d5f50ad1dee8f7d7ee2945cec9db4abe572ad1376925a930daa6798d27f7d35f5f77133dee4a69e0096c7e746499f3f3fd1077c29af316af120acbe1513d97992fb42a9c4464ca87b2d8f9c104a69f28b8415268fc2e828e2824f026f03af66fd1e11f44b76a5c49ae396cc5d3203ca6abad8f756fb8ea36c8f35ea527f382f35eda217e8f122cc3061ab2a2fbe180883059747fa8e1784c0b515eba37bcd6d4a0b51ff843b8d53948501be8a3ac98c02bd9fc903320b49d12e725916c5283f9401b27aca219a88cd6af41323a73b10eabad4274edb5e649ef93d3feb4cbcc5efb28166b07b8e7dcff384040f8c59e62c37e59a90dc87ff0f7610418db2e75e7d7a82b84a028ff05076eec82ea0f4e7fad6aeee05c0638923ee6352c22bb426db70b075b23591b07cf49fe34eb90b8a4b806d9cc9af3fcf24ff60a2d4ce065960bc12d55ca8b11e60998621555ff79251140cc84f39f339c102f774696e94349e63e07bb5d0e9d8a10f5f983deddbbf018cef93fcac66850d1bb2d378a859993e43d135dc83a4f66ffea12b85fa5b8df46beecc582a2e406b09252da9fbd01d6208501523ae368c7179d70c238ed89f951dd6d7a10253da90e68b225eb35fac658de0b7b96f652ff291cbe7d64daa91d40c9dcd19ec109d592624caa4358483c174027b78b2f933df2f6b098d71d2ac1d6aebc2fb29361448d44ebd93e3468bf22110e8aa311aa8db85e773c3067585caa497802475fbf34b1f148388b265276abb5207492d086f609659abb12d4d48f3a2c84d9c80b069025e36e593093fb5ebeaa1725c6a5df89f634e9ce26ff556cf6f88d7b67c7e193055ccacc20c9eb78ab721af3ea516e38ae26ffe5a7b5c1954f4590a4d52578cf9153a9acf33af550c04926911c6dd114937ab8ea8147d08c7db5716275245fb951c767f82430ccec444501d3c16986d95134976e7a7203583bcde83af9a99aa003fb4b55fa03f94c09e82329669086fa0b5fc56daa0ad6df98574c612c0788fba15f4e3fa1d3c00ea3ad1a32b7c183d6a097f0413f606f106a6cdc0e93a7ee18830496f8e1731626428f01bbfae67ee1909bc7d84ffe61070be9ccfc109b5e7fbbbd27452a431187c53308ca03e55d4845dbbb575edf975784b8242889b19cf9001075a3a0d084bc2daf1be59a8c0addb7c6fcf3d922c7d19ff3981f5c9645b031839de80b56da2982f426ce8869036679da3970739f24bdfdea24526fdabaedc36040f2902b1b1119e72c14c74f69aeadd07892831a5da635c6a312cc1139f8ff1531db999dc979200100719683749f6cf50a21868958e37ff4786e7b5ee1703d43e9261f52d12d59582986a895846ad4d5c7ef17aeedc053e72783f575d909aa2f782938ce8246a11e1372c04b95f0c9a1fd6ee50f1121f93cb391c50d0c958a37a68add49cd5e556b25a982c3a0b02603edc7a09e993aa34d7d780e71e955a6c56d5b8a16209a386e2e989ee588189f093e06ef28f0cb18aac37a1b3c369a80c40b64fdcde957ce4f2ac250b30125a6bb044d6cbeec900598bbab4ca89612b3d2c264a28f034873672e18d7f11e234506bf1f2309b7444f8db2c7d5f5fe70c612fb85ea9dbdff796807ca6ec9c93676363ab70041ea6a529224ce93f1a0abc53a17701c0c3d2f4f6d2ede4b9fb18914a4113f65e3c0c6111267c4f6be1fb8046ef25bf35c44d19b842ae2fe2b889e477c54c98d82da4c7b0612603d9f6e561a05948ab0c30e494e0b2635f26b324ee6ac274f83f257657109d07720342b7b7b5fa7637f477d41af6c08a23725165ed2a19148c9ea0ba8ab684b496108cf65e5f4e7f0ddf795c404120b518f3e1511ea4f7546ebb2b1caab95ca3f24ea068450b7a0099859ae7c1ac908b13c49f09e88fc97a883216872bd6858ebc1fdfe5f52dbd0356e05257d80320fa187705c51f66366f2f05466b1ca3c892961f9a589a6cb44bb273dd4fb84551bff7f3406bfa4f63c668260021425f9b7163670447b8235423b3710b5e484bf3039a7737aceb27e9be16933623f1cc9c9082c83599759847de30c04678606c6003d492575ac04ffba536c55e0b25a03951405779fcb7d3cfd98fbf3df1e61742d76e67149ddca631eb12e528d988b718e63cc9619433b95482a23ef3db9d01a2005fe40cb5a62f8be9698ce69cd4b9db4778bf5febbdde68eb90f19868969d802ae7a7d22b8d6032085d6040f6c03b6d7492886d6301c9afabe287c6c45b0292b310e8d9d7449443006fa8131a2f3f5656802d209ae64072361a13cd7b0f2281fab02ef911d09729456a937561f28b22445560977a07b0e00888b90f5f476c92db9d5073658a3be4ce0ac5d1db0fe45be0b1c3bd53829f62ed81d5ff6d51ad31621458d9822ae610b033350f9c993233d79ae279ea4264e67f31efb46dc276410248f27d94a51f4c7a665bb24cc03872c8cfad6c883481b414686d970691abe0fe37c44c605610848a3076689513895acaa05834a3b9e0e024b345cd8e1b87003d5d7475d881900fc5586054fa9234cfb46bea65c71eca250a63844c749279f2b5b49120d771d71fc3500f1d125662bc80e13db85e6695c40b5e7fd94ba3f2122663e719d61c8829a3f55141ca58c5ed1e28beeb46bae86567d9b7808fcc839235222a95ecbe861aab8e7d0c4e600144af761a5a540fbd9def7aa41cfee261e021e3286ab9b59f7a311d01c19931e346a5bb1e1c4d7c9bb3faec5f1ce5d2643047b3bfc0e3659c2b8b8f7fc07dd843d506d60a8e65056c2aeb3cfbcc25149661e2c5eb1eda809e387a860c1e9a7f37e1ea8a18b48cfb239aceee1641fd9ecc023c4925fa502947194eabc25b3a8948861d26414e21f19e7d57d23ba4d935a281313be2a19396e46e733a2d08840076c2f9d38c7891201640355969f72e9492e01f41e5ce954612273f29ffe616557a9c01bc2ed58536ed6e0171a7a9631df70079f38ef26f2bd6a9d0fe7e253fdd1fe098e9e59c0fdf14400cb83091242267f205c26520404b3df973224a3564faef86238ab4e08c67bb4cf64f57e17b0ea4b1cff7bfc129663c0c261d026fd7aeeb7fb358b296c7202c8b2eaa839b4f9175002835b9ab3ec3ce576da4ad7ad83dea97c3d9ed7bd366878cfe8a5e5aa28c0df0139e89362425d85b45523b95a262b5a68718df8f13c6fbd052ef97bdaea84ac158db8e784574a71e7211a48e7ef64c569161cd19539ea3d1597e05b0a83356d0e4055519520120d0ffe93ec75da6b630acfed959e69e25d46501659373bceed1306c3e92851ec6cb8dc53f1aa72aa8bdb0d85a815d3968a9b034ccb292a66b310935d44098cf4b92a29ac0617d8dc6322e4ffd1afeb5813197719186dc6e0d600dad3781e520792127fb8fd72c9702411c0a1f947aa32f86510936893607fbf0de068f52c56a8691c5d4c02741954d82563a90dbdbde6a4e4c81fb7d620ca76265fd84201581590686edc35cd730bdecd186812b216770d2b3b15dc8a398be425649dc6e4f3635a2b9f8552be41509240dba79b4f056366063fa0f35bafe42b110ca07b7f2c8034386bb0dc771415aa8cd885bc06160e77bf85f5c3655d641624fcb342117f94e4a5afcee5b98a648baac82063d897ce4a82a3feca3bd6ef6b2b32f06f9f38eaa15fbd3f49e6f411ded6558ae35c4911ab1c316db2b01ddcdd206ef277976083b82e474813b23b4b8d9780e0bb6db3860a269b5215d33878c207c796c7a764bd2bf1bf255249cead62b9d7b90051ac8375a1701a271a5a504b025d4982dc7781c4a68b774c5e14d7c7a6982229b6a5db2a69012f9dba9a444e178223d54934f655c362925278725824ab9bfc428d2026d7725f63f6b210d08103b372038eb05ad0deb2cddf7b9ca8871fdfdaeecaa6e78480148cd53d772e002fbf0df391012015bf65477a51e31a284c0dd71fa342e2922818119bc930a61500f7c8b0f04920ba24b379b6cc2952fb3119a8b363893def2a9ac3fa5bb5a36e19f5e706f9197907da882af06c4935b07323af3293c75d8ddcace5e0e5a5aebb0717fc5308f336c38ecd96c89c9f7422b335b0a4e18f88656f255e9459d477e515a56275db5ba929662aed1b09c814b5a3ed4920039bd65300cf52c70fb7a42a1c81454eac1c3d276f1c272f81a95250fadba17c9aca2c0583aa2bf5b0d2873418a9688b46dc7ab1f36aeb09ea1b00873a8ff04e4bc608d9e35b53e7735fd5a01dbd1eb40cc29b13bb21e85f00fca65fd0983459d458f25a3bdbb09b4094ba692896c82fea2e9353d9bda7feedf2416f1507b3342c0d3d2a2a50809022c001bd2e6412debc02a94d461ece20fdd765c74e78f6e9e43fd14d935b7884b716a0692004490d547b1c97f3b60288eaa7daec61698fc7e0d302ef97ad1cf8cee533b816f6f722c3056c097c802609d9c7b074c50e75ff09c190d2cf732c974af4097623dcb10ab54bfbe197b4e971c40d664e273b6dd3e93a40a8ae5187ef842159940dad07983ae2f8e04736680fa307ab50921708d716bca4297a7ff0c816b6446601987090bf0b69c6415de518d73a56474ebd658f8d1e9aa500e0020a6c3151201b11a3cce79a98ad901722371fb8fe162f465e6319fcf30209128c969701bcb70001e8aa66cd3c38449bb2e520f07b9e1fc2fe710ffbd456d39711efa0832ace2aa3c12f7793c3b97e69d5e28f84d9dde4cfc2990b64e13022e5472b64c29c782e4cbe5186a9cd9f5c372f2a414838dcebfa655361de8f64b3155ecca983aad81b51c65aefafea18e7d0a0430bd86f301462df63650497152a83d5518eb590ad03c3b822cbde2466c3571d685b8141f972d64a770a52f2912ccfa1af908e84570a58db6f335e36a93b8b46c8fee6a65d1dd1f84aa7d0b022392329cd47f1acd8149af8bbb40ba250d374000d409fb81cc9aaf8c8124303485900e653f153c5d108e2fbe580e582df090d041a64c9631d114bd77943a103f09961f818567c3c711c3a5b6daf0e32e910da373b362b9641706892cb96419e5a8c76f29f34c05c4013123379388bdb0e5883b299d8469542bdb58f62773c942c5707f5c903f88c5e056779d741068745343dcb01bbede6be417932bb0cc0fd1c86270af2cb63496e175cd36612fb28f4a8affc5dfec8a844daf2e67e83ad7db40aab6dcfc23413a702336cbaf76a18fef3c7bfe65a85cf8e99d59537b8afdab62cd6882d17bd1cab00c6fec1e87fb717719c8f1f73751ce868eb93e57f813648d813dd68f03f30521c2976a50f6721e68754e5fc7369621b74dc6c2b3a407065b35b5dabef052d1a01ad1cd893425a4d56671a57219b26d215fc4bdd5b53680c43453081304b374507a566e2c9d5827d3fc804809d7a8aefaafec10fccd87763c298c59dfcc7a69318dfd77590a160094d3a359e73ca0c1279cd5fd655a128f580b7fc2758ec480317deb9f9addff0b92367143d9cd8f30c84b15b25eb5f4a47ec56caaef58a6ff1139792c89b163437743a4d60fbd172e18b270d653927725b28ec3f69a8eaece000f3d850d877a132fd182989ac44e2aa29459f7b3914f4f6030076ea47ae401b203e48b45fa72c752b9947086a35a67b68e8bc183f3e9e37397cfd45312ad31908ede3e9e4d8c78e9e224e1a42c56f5bd1980e06e9b1d557ba4d57493cebaf89100ac163e836ee4f1816600283dba01f6b1f1699f0c1ae32630a712b828fc8fc16442afe40c2f51e6e9ab05fc7190e8bb2415ed1e5a42ce7e4c858005c353d910efc9eb2cd154c9d5b03da687d58cad45ddcd6ccbb36062282ee94870bfee5031854ba4f8c71350dbb06dfb2fe67ea4bc8853f2951f36f89ca530c4ecbb2a3b30869e7f23b5b62505613dbefde6e5a8670649048987fd3f588e459fcb44f65c91324c04be47f7bf00f94bdbd730a95c86122be5086d212e10042e980a2571cb7164875082c4e04eabce1d7df6b6b74149cf0d6f5214893750882693d6837e6cb2c846f42cf78fc0e634cd304dd118bff8386c3616be40b1453caee5b2e833a4817911ac39961f385178c49973efbb97a5ac5fdb03cb61084c3e58ca4284bd78510e102da33ffc70684ad4f19bf2bf727b1e19f421915eb4574442db4a356cdb31b5a6ca64ba0b4bf26344a21d31b62575d12809c18f9afce06a7e61ee6adf8e205a6d7e18315546fde9232a5ba7f09a05f98a64e90480e069a3dc1e788465d0211443339feb7ae07069444094267bbaeb0886a7395a2c858b74bc77e9c12082916cf2bac5255804cb043cae050335971a55b97ded520eba50eed6389524dc9cf1cf873a57c6bb3c9eb90393a184f19700736c2a1bbee9d61bcc848e5c743387b058d210bff2531aecae2e31175afea3ecd122a5f7a43ab8c7ef95fc562d5133604e5a81e8dd44d7dde63dca73a34cdce179691cd99dba061ba1e52664d6e8b531317f9cc29b4e6737ff28a14c91ae16a9509df249d4caa5d7a9accf203ce43ab0d4a819495cabbaaadbb284b01061f61add5ec9c0a9d39f792581cc1201ff41918dc8f1749902f8d4eaef9bb1385f4d84cd68f9e86b0adb4c1b961f3a4fae3342fe7b21f2b28fa20671100694f661fef4ca029f391f9ca4caf3cc19e39a4981f131d4f538c71f968fc0d9f03cf323fb1cd48dd9bb900876c300a022f3e817a62245004d95c4a1b15d78fc5024996506557dd9ac3bcdaab64e61559e8ccfaf4480c2bc8a49d41504a25a33ec9f6e81b37e9550f8df92d1d49f3df4724c28ba71c1ae21a17e393aedc796558a02c5d671d715aa45e42efba7af0bd351bd712f9cb21000f926d7c0f172c62aba013fb20716bc976182953d9378a505980b02d55470a9faaf9bcc8290bd4b1d1f3bfc1e7a3db16f44bb060e693037eb8c2630853782811e636bb7b14aec5a9dd3e96b4f781cb0827aab48e5b6c3e3c8fb903a57f291e35a11477edd96f428ef8334e311a837592ef308cdf95e6b25bcd99eb82b7470af7f98f8f070045cc5baad5dac76c953ac04bd81c2f3556964e3779a6d49bd125b669ca6ff09c379f03bb30cc3850e652ccc14af965f1e6f7304ec6c17f0b8f032f2223cf5ab06bf1fbb19b635064e577862391c64ba9c3ac9869bf77b04ecb2f304149ad0dfcf55e5150dd5e739c170e146e0b68e778880c556fab86fdf98c0b4a8ba767b05fc1b31ddc271538d4d74269697a278d281e032a953cfb62016213166fb3698dc2527a7c37dd6350366036a9bbe0aa7e36349e83e56e21438dfb204239e67fbeac731d256fe906462573ef805387cd62d66593cb274dc1329edd1be34024d2c8c7a529346672c40c60cf1014b3b41389d8401788bbd8cb2105472d79f63e18e169babf3faf554f155eef0895ff746fb7bc96e4e06b0c283d291f024e9d2a47dd1fc370e2262a15b81c5c5a8744919f50bafdb4e8c51df6a934a120eda77dce60c20ecf47300852ba0eae62c5bdd58c292dae1dbe15bb8b7336160806a01ee0096f1183ddfa1847f17e3efa20786a469ffe58a6084946b2881efe1041ffe6e5a395efb68fa39ce73758041c29dd0f2a8c50501c97e761164a1de90626d0a72ca8083f1f970d8944cd3f269c919f52f510287b5faa74350d382f252031db8526e96fdfd680e5a32fb0737a7dc5326b6a1f3c69583113719b4d8a5021ad6adc313fe5fdfd4b0a3d38d789ae51ce92b83939f8386978617f43ad22380a1c75f0fafb5b46d62c89adb165184ce88b27f9e572b9c763fbc2cebd5124e702b2047e10a8735aceb69559dcedaf4a248d938728d651a01bf76c3e8ff7572f", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000002996b0c00e4dc6ae412f0b46d68dffcf000000000000000000000000000000007758cb4d71d9cf4b6349037b19fd145101cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x000000000000000000000000000000000000000000000001f3e77d68ed5db4ee000000000000000000000000000000000000000000000002824efb1dd50dc53d000000000000000000000000000000000000000000000001e287c5b545301ce400000000000000000000000000000000000000000000000000003ac30f94551900000000000000000000000000000000000000000000000425ae327975b900880000000000000000000000000000000000000000000000030450cf304ee5f67d00000000000000000000000000000000000000000000000ddf49ff2fcefc54550000000000000000000000000000000000000000000000000001e6a4e87b9a2000000000000000000000000000000000000000000000000373d5ff3b6917377d00000000000000000000000000000000000000000000000ae9b0b68e3252781a0000000000000000000000000000000000000000000000078bae91658ec6a2b20000000000000000000000000000000000000000000000000001edde5cf5eeec0000000000000000000000000000000000000000000000024db0e0b9187cf4460000000000000000000000000000000000000000000000061da17c01b0ec38f8000000000000000000000000000000000000000000000007228437c29b55d1c600000000000000000000000000000000000000000000000000013d3baa2598e82a7f1abbbf32e6984fcb0010b83c6c7844ea6ca3fd0182ed6cfe12534a9a1ab32d95760c1b06880b626933c5156327a93ae0c04616b6f2cfd6457c4dfeb0b88f115d8837813343413efdc6428b79367f2cea1f6fa8b8d3ec221fa34eb49abfe209fe2bb0aeacf24470fe359956c7a9d3196c4d59810478f5e6c219a0a1ce604b02dc0bd84807a39b811417bd98322a228ff199aab7bf5543ac55c50a2b6509a607be7abd06b0d150fb024b43f0a0264f8f2a2258386a1e23c891c2cb0f8bc84212bdf036a748a87c8949b4fbf035079fe8b10ebd8c3b4e7f3ff7033fab3de191217123f39e02abc543c57376b6a015041fc623668c649686560fe0fdc1def2e6251b41df3fe84d67e48c15e23ddb2a83225f0edceb2c59758b5bd5faa1af92831ebb797fa8d6559e379f0bd0cc77e360b78c17a1e131017d4ecabdc2ee16ad021a31399184a8a78a5ff3667d4ba9ff5ca43d76c39055e64fe36d15d8be71fbf62733a4eed75dd54386c0915aa949a3ed5710627443b8ede3bffcee932e416b52251aae03e2369a82a51fe16dad9fef2b0e96efdcabd3322c1483aac4f532a1171401989403a42325d2afad1973d1a723680a168d3ae569995f8a97a7aecd777e19cde1b4ff4744fb82c63a6d990d2185ff7b003862b25776bd65a5ed0e414fdb227c255f71b15ae0c6933ed5702b3e729e7f826a590387398b121e06df511d5514fac1e05b458c10dfe141aa13ebf0647e24c0765eca7a633a6d82c8cdbf1e281308f82f1605ab9e282cf3731ea329850e8463f81d274c6f12c703ee4027535a287eccb7bc38c8523ec425377fb9e706a8c53c138c98c517ceabc0497f3845672c3b9942b4ec1692c479a8dfa18e55c4ad68c6519198b612a79177ef62c1ddc02d7842bb16583370aebcfa9a055bb2811bb27866c4f05557866b524f27f1e17503d9ab172ac8130eeb870cc0a647bdd39634c84b59cdf975ac0a773c6cbb151c1d126e82ef43f98e082640c22339e906c342e788aaafac7bf0f644097be2628e0232986c6d79f5ce13059c444f38ac33d416a25009f2dc0f86de79f54885b0510a86856cfb4965ef34d9bcf19bd9ab16fbea080afde613668d967b25fb3bc82707f9ec76cf626377bef45c4a0d2568adba49e805e59649f0695412b947af0a520131e9eaacec50553d90ccbca23fa3df01c8d236b1c156382732390af2085e4c0d7b8c85c65f3aab8fab763cec04cdaed0cf90145a204081c1ed3d483732a4ed13d9ffb221838c5f0d8ebc8c9aa98357bcf7fe1c7702f89b83bf5dbea988767c2ef522f59abf597777d99e620b501d519bc981fafb76042b453d1a7167e91a2e1c2b5edd2429647eb7b408c757e9a3c1c19c49bc6401f10816c3ca20a8d2768e19838e2be7eee84a64b232d47264296afebd614484dc8d735a5d28ddcc2a0c840edc31f87870ac41ada9eae239427acad4010ade9baa676779c222f23ebf5245235875eeb04b748a32a4aa8d9ca4cdfe03450077b68715308644eeb2cb1a68f1006d6d41a8e984f8e1114350487c1587996fbfaa19d2e60cb1175558e6a0292c0ed5ff0a5b855c452584fa8af47335737158637c2efd534da21b3853bf89f9242f9c279f94e8e47a25cb8df25ad1787142eea7c1e64d248709147b370f9fb54e25281994695e79395c7c6a8f9506b17f11c9e84beb4ca71f547cc5a24b737c1d139df5ef48dd868001c3ee1213fc3d145b8a0f1a4df3ba5f3b84041faad9c38729836a3cf695c826bada884a9b2765cd2dc3c519a142fe6dce7c61ff3c35b14e1d0f88c8c1042161b3fa888a7f10bd499c9fc4cec04fda3ec346eecfe4b8e64117ba207695a64301d974e223e8613159d26d2466c0bf49ee276f880a715f536b1252d850fc51c0b123ea14e1ee08e305ff2af518b23394984fced28b0d25ed1d053723691e73f00810db57dc33e7668cfa7ee8ea82dfb08ae1157a7ffab67bdc1e65810fab2b117de99f771fe66e33edd6842ab29ee28449a3a711513839b6c425cda1c407664580d3e7c4a68849d1c986366e3ac00ade2de89dc36065bdca0b272f4c4f8230570158f2d1d5f1131d7d7dc9abbd6be93171ab7bc99ad78497702925a81cae48f544226832ca5cf1cd5f97732b9537d9237258c8b8511f10723f057859047d53d2b70ffd0049c6ec0514ec267512b9c08a80a91d5d597b51c57e069c3a0203febcb8ece93238af70b491e7738dcb4fa26835c6dc843869122dfe09dc4f1773676441f3237c9b4417010f6a26a914ea3db681688f37d2c1f749b8111f6705f50a9b212a1f112a0be2c92ef9fbe0890df133252d4b59510aac501522c73c857179241fb523b6cbd447f6e20ffb3cd52797ea66d4c6fdcaa4d73a770bd4db1d004838ee5b6ff558a35fe31ebbe17211aade2d97750277a00aba976002bcacc9932d5aeae711c5c0e2b80ba242938e0d52e1c91ddd667c6f2b74abbf1da41a0b6c35c00e17a6ebc7901f7808ef157a01418d01949389052ac91b2d711c40a79aec70f73e9fdffe0ca944d1cac8d9e4b5ad2601316b5bd7e540f511662ab6ff3f08581ffbf961ee342499b951c63e998e60f4ab34881c9b8dd4e2dbe310cfdda4611947f2faaa558060914e3dad366bd4332aafe4bfc4805f6e84205304a03965d812f279734680b13e6cacac7b23c2228b1acbb93f4af41aa025bbf91ab9a9a42630a755b364b3ef3790337d1da0e315388521831502a260b09813081dcad647b64ede244918a587ff629d2b160c0ed328617eb4cd3df037fa8c30b62fdfc6a29ff02714877f0a4a4f09c29a9af2e85e427e502427651a8e3b304c361d2c9136de21197c24ce33da03e917c925be1923321e11e36bd043e1f730e88b2f174635edae1d9553e512b035b4e0b933d12716ae0346a30d6bc1c111c58df925b6e006c118da59d5e91142cb123ed18216a9a34235d2a452ac4206f0e6550b07c6ecfacd2f7fc86480c80dd178172a575c26c299c307ffa1b8b0a93f3f01be15b861d10213dd8c560e919c5fe5a46c8ca6c639fdde608dd918de75d6567a851b7680b297a2ff4259a48a36d482d1a698a6b16c9df14176812371e52cde3c222f843387102556dae72046097464c095df1ee46c69826480752cf48d60672cdd0299635611bb07626a8273bea0fcea5c90d886657e8c4ed47b62c3aef9191bc612aa8d7a3a8bbff9fb80690060ff6b36c102475b9136c49857634c927dcc9c481cd72807184e90c8e852a0801c946d6411cc2a195f398be1dfc37a2ef718a3091ddfaa1f317f91b550be51b86e43d0ac570a6c47ce50cc98a9905f886e44b69a02447cd1ee0c5ecda92b2d39872b82503583790fff4a195ef071c41ba778253a069b853bfc3cb73bf9c3d93c2ced9f9d1d9a9b44ff81145c0b9a6bdea82eb5fc0631a206074d902529dd3b03bfd38837b9295763ce9a173990da54473658fd092ff440bf4cbe8c837d5c828c80ae4d66f944545c0e27fc240ab1cef2f95b043929d25f3ed4e9ec08892ec6721c85052a6c78521f0feab2acdfcd4c91a7437e5d202aef919da4ecaa22b001155f637b156ba351e5a052c60bebf6953f295b369a27d042baaa94a56aff86ccf43bcd34f6e6080dae752c359e47a030ee0ce2921e0ce36acd288f10068d9904c877c9d62a7d937318f9f7c5f1df900a0e32eae9932c8f06a0b83e30b0b225cdebebd3b42a7131f4893cb5933ca34d249e7c78a9dc022560ea911eb98c0c473c4feccbaf919ba5e1ab1e9a139672bb0c81f0de2d6d236afef4ff6f79135129f6c4d375d5b53d96bf96bc04b2e36f1dce1ed5dfa982103fbd4e1941a5f51b4b114d45ca0397d8cc0b25e7cc9e4efec098fc9619fb31301f54c3195c1283407e2b520120c77daf2b6428d78e5ca5ddc13021c6505efe15f88f53c947bfc4408bfc93856fbdaf8a9c72df53fa60f0a8c91609f67457c703b6fb5b5acbf95186219605ee9245f80b948754af2fac1161b89589ebff5fa9168039fb3c5e72aa8a2adfcd6aa59ad3cf183103a5c1313dba6f6d978aeaa96226ee3f12f03eae59e60fb73db15bff1e51ae67cf88da23bdff5ec9e0b49566fa1683a4f335e2ad10bb52a7445b05a5765175abf26e2d8a05620767255f88f9070f0f0fb28cea2cf72e0745391bdad338e1b728132537f3495f2343514bcb1b3b0d8d39c1b7b17423a59059f96366f5a80c6016296951122774a1bf3f4176b2251fbf1b33040f6c6437af3d6fb09858b7972dea5c1baee1986cbb548c465e502708bd2c3149c3e86d6ad2452b70102fa4a3c748d65ae3a5bb2a90c6de79f60f6f090b044848ebc4579d7cd68132a3fc3a16989090cc37ce21079a152cd3fe47fa1fd48c0313cffed38c172e64da1a3f89942231c2113c5115e2bc85c3debef2ea054c043d9ad9cb32cef65a18f28f1e15b7c9985733c69a52eb6af4287922b6222f5205113943ff5d8e1ed099f5299cf8240527200186239863e36d4a58a2a5fb065d78c66b7136a52ef53de8e5d0319f5d2ac5d1c4b5eb6c6c31eb57e8e00f3b05879a174a1da0394f7602dba05a7cbcd64b297c0650eb8980cdca29736045d724b1959380c73fb0d631a680244a454dd3a4e0bba490eeaaf956f8296d39cb9a00ac3a4c762a8d11fd8645a19ecf3d149dc3de9a91d6fc22fcf41a3f41cf5d2c2cbc5eb7a019d0f1abe491758a5a35f9941edf83ae783c1009d9b32efbcd5773252cf6fc0899360b17905f9665f760f0d05a383645404d7ebe4acbd3cfc0bebe1a3e6067abe34217bd9d63a445ab7d62814ce52e0c7947b155bea4336495c7521729b8332e879ba575cb2b5b17984efcfaf4f2f0016a92233578f09cc64c1c5226e733c382eece007d0a5b2792b13ae338a8edd9034811a0d5a18e1ba453107c2fcd495401b784847149562b319c2ddf5434b54f6f98a55010e2d151b34ac1c61f0f08bd81faed2b6ace599d4a68f5e12eef8b2fda21ba86dbc6307001f9554a29d40fa86deb11a05a25c8dc66ec9e525526aed32573f30014da08c2d9382254292bcd80c5fb3c4aff19006441bf4090d3f5e72244d8967b068ab8a720e2f8f8242a3331efa61b12eb0f568148b190fe0aef1655aee8129c611ff7ab4d2b7e7f0456931fa078ca2b6cd155f04f7a8cf31a2540ad143669812e18153379b610f11a1ebc35a1e580b55c355adff570ccf222d05ba579793ebf482994816ecc57aa1ceb6fe0e137c9ad7d23215013c3d02755009ec47dd527d366d54f7bda9c80501b163775e5f22abeefc0e4d0ab58b3bfbc5c9a6971f5cd29ec40cb312fe43e9b09d53ff3dea50a09c42faa82a943c4cf5b06cb0c46d0e78564929a69cb6ece0c2c5c935dfe7dedf4020a9d2344467a8776559fb824100d798d988c0dda97c42622dc97061e54e3c9c7220c5326b65d25af7785919937794c407761f4273d7d5502c53ff11a6b83114105cb2f09891858a4920eecb5b8b73a5c803f4f8c68e16f2540d7a584736cd89561c2c22b3639d06dc502dde723bfd7d10fc023d7650b780aa52ffc396e1ba4eaee1e79d4ca4e42ac6fac5815ad08ada300c50431875f970511405be5f78f927ba165aeeb6e8349977f8bc883e90fa7946fcf97c403eb8b2906a69f84bd75e4bd3bddae210d7100d285c94009c31cfc067e7f6fdf538a3c085c166dc8c565e5a7fcdda24325b2c61dca091a4e4cb7f42dfd5c26ece367f02788948403bb25fdd624b084a539ba98fb6e00dd50b0021e2ce918b0dec5c6220414cb321509524b921d65134fc959ca8112dd6e7b6645cc9dffd9b0be97512e201d78105bc66c1d6b44fb0dd0d14d1575362cf2d5c4644bf4afbaa963fad75d219e7a9dea5e6a44092c906bed7582cef5b50b36b7120f7e56749f2c23b0667e0b3e97bd8b681cc3e3cf0564c8c508cad783d871a755580ef9cfc3f68511e6011814740377b0ef4c3e85e74f282803f7bf7a60a69ed76269beaf2e541f2f01461f1d3a7b61dccce66f421b1702dae8e1095183bd485317ee1121acc0eed3d9841499f6b9a0adc4a459414f50da7ca00070a63623de5e075e32778ea939f2b5fc2c7681f628866d4e70be2636b34ff2c719b9c49d94241c8993e398a07652126f0eb20da7dfc1543ea992b023d65d006cb4ba0270c324ed1ab10295eba02f28e72a0591f43791564c8edc198a23254b9fe3711abb8ebad8b2a28d76b063829d5b1856a4b156fddb3e21c5fbabcf1a2fcb1aa0aef309dbdd7b30fa9c24a475e1c506bae9cdc0be1efa29becf66ed43a000da45c20f24a5c8dfc08bf490000c17f023acc1af65a33f8ec051411061d7015c07dfd31c7ebfc09a9715008d0523364b2ddfd345073e5059d754260e7578661403ce987d3048e9480c61862d8899b4f7289e8c9f289e87a9f906dd963933d3626632989d2eb26250af5b76cea59070a4012afdb97165ca9da72545f49e15633ea2a26c7735c944e990dc961357e776441813f18afa14f8327bf2db98a0e3e7019f2751879c267bcb6c7863aec69caede24500dfa808bc3ccb710fd545fc116218319df0bab38e404c07ae223d27c9d4f29916205a86f00600a61aa0c2e0dbee33cf3613b31ef280895a78d292170e5911a3cde214de3e8d23939407279f7cd2ef57240e96eeb86081eefd70e5b57a3ad21b8f1063cc1e3b6f6f11d52bf76ebfc3ad91339479a7a3d2c2b8e6f69fd78d010f2dce00d6d8b680c06ecc20f6d67ec19a28f9c20ee9b624b784e05418ff4552d1ceeed6741f7ed9c3d00a3fd38447dad774a539bc99a5052a95f628286a5e62a4ccf79bab6da12d1576c537101a31fced2f45211f914c764e5ed965d5d9419156f62a957a277eee361d36f5d7c60222d27c3376828803d28c6633a8f2110601f35785bd5788bc8a8c47929d7d820cd2a4dab83e704ec786ea5e5fc3886dc9615b5d4a5355cd97beb11573a909572900531439f2a63f421eb5494cbed5ca15503e22f5cfb43b27639d7af28165b4591305706d1f2a9b0d1fdaf068e685508150c3d3d23a9c0a31fc682dff0e845c6317a414395c9e6c4a33ccab5c6395006971edfa8f68e55940b83b22ee4dc105cf2e39f0af7ea4a44a7a2992b76c4c570a30b677986924aa592738f56f95726a1df409d84e7f7dd45727c6c723d7d44300f256dabb802ab4c9a88121fae65192fc5aebfe70ffee877bda75be243ef3c1836206382f14f942875cee4ac22f4207aef59da5dcd208064b367dd78777384b80503be38dd876624f57cc4198a025b37ab52d7229aa2c68239ff2dd43d8994795828a5cddcc20bb663b36de0bcf88564e95318f7d59f159b263745ddfe380abfd71f5c2a6d01a4dfd231fa8c8adaa500d5df747ec3f5fad7f41f4d3307d99d67c019d7a23b048f7d63eb11c1133b5d573c502ecc2f5623f246c183a7d384044883190100bbc33c4aac39a021366c17e24a8e8d897bea7b9aa2b0b0ff17a79a91db2e36a411fe0c982dabbfc76adb74e710dc0cbb2101b4e04c96683f49b0d9e8321f4f529c3d73b380455084bad0671f759bba657fa3b04a41dac8acb156085556126a7b131eb954f4c98ad9a97666023f78a49a1906c542ddc59dc60bbca6c45b121406d8eec540912b0d0b03638fac7205b2556bfb4b096924ee52e816d79310095af3cb21300eb0416e87a6db4efa734e6f54a61d56e45c21f8501a2c18c19311f75013282383feec1caf488b8cf5e1d6bf898e420a0ceba1f47a001c4273d01357402e04c95adb4a05284a08504bd20e45a7ff3b37a127bb457edff631001c2134448d544b87c7cec84445bdedcf77b43978a27227650cf6653e85e62680d91a9a336e6013866aea57a78226ba26021773e81386c7e09f3942eff5c524b8ce0dd795c2959de2311e2b05f5e66cd6a92cffe26a580241663b6b4792cd8b8b872628c340bba16177e26150c31659ccdb694c9871407b0f2ae7c2020bd2d213e726f43ae8d6415189434d3f6053dd0f14fa9ab5e26e384aca8260901a01b46f83222053230900b4e765a5013a7931b2d689ae848d66815bd07bf7bcacd37f7f9e28c5e3f11c8c868570ada9d40a96ef71f80ab55a99eb2bf8e78db8182d518a6c1f8ac5c99cb8be988eed079d489e85c336fcb5f8057bb20d6322fa86e62bd8be01be1982e48210e025aef091643c5cf1d0219e877cf4718183baa29fd1e998b2233110abcdf05adb27255a66180b98862d820fb8220f99ec23d305d9f2be45400c8a6b6b564726412d6cd628efded4bd4d8d3faca4636f44edca4fe191db5bc302d917613326ba93f366cb36705e2ea7aeb69da71610e0546364dd7b8b962d62284be2ca6d0542cf4c387809d6cb0ac49ecf5e41166f72429c711e70927fe10f1357793171f937cba3af6a6d5140e6812dfc3f2377ba7e11204c0f27626e61ca00c3dcc0694e6fd6ab948627668d4eb432f6ce32c38726a5a35211facd2ad25e20b903fb971f880376def335853ccd89ae2b2485dd06ef4fbd72af678f3684d2175ac14729f842e00cfd4f48f8080e536f428c32e16954519e481da94b30b3e00dbc1e2d668931c3cb33526098b89da49bf023a3405526b77df1015c6aa8b88624f585b6022615ec24682a2e594e74c3cc251b082eb238167e9a5bcc7d8f97d20c0439e4fd8e706820a24114509132c61fa129989b7c5624f7dc11ebe3a6a1001bb52e32134ccc60f5e3111bc490086ed2b20eb735312371d60f9a2e69477f820bb45596866f4f846e312b6376de071f1354e67f33abadbfd869ff728c920bf21390824d579f86d9a78736e37eb6a1702cbb0535f3df93abf216055257c126c907b88a19fbede7e3eaf9972c99395a91a86259cf8b6bb7fc3e74365c983df0d3102c4173d44687042228e3dd26d15d4350bc9c99247ea8c96456f864edaa1ffe18a291daf39eb370763f0e0e71727b57dc895c7a0ca61d736fd361d0af7397ce060a77335485fe34cd581d4e971f77bd186535149d78654617f2d6f41d6ce2c825c039324ffcc10a27e50c6e47a5b2a84ad8faae7fd6932a91f3ee2d3009f4942569b4394e802c9207533e7c87ef88ecf09e20249d2e9158466d4e38a4cce7e309a3d2d82cd6c979458b7a60bb3372e544d4650c34c31410d78ce03b80bbdeb81cdc482661fce86890b76c3ce45a72c36b70d441e203c6f385584449176e1fa81827627eb4f2650e88b29856eba0822a44a63cb2c71695be176035a50dbf92b32487ed2ec3637821549f8013c816eb3ccd0d194634a82bf32b0957c6172ecd771769549a3138b690aec5c96013608f624ce42a753b61dbeaded643d8946cd9562b19aac5fd513923122ab8e5b28be59b6228825d403400207e70fca0ebbdf7c11656e662f005a9297c1cf3a5c68a23d88b5065a434aeca985d3947bdd7bf1caf2a904e27a9375bc4e58d025812903d2a8d9c1eefdf2b76e1a8f3bb20809a5b951f963e310b9b296516448e4847e8bf10487cd6704badf264e38036fd74b7c39c01f37047e62239081cd9cd5e3059d1070c9c1f89488626733f2b11bc14eb89481a2176a0bbd96c66575f009518c91670075a6d9488dccc597d27ad2085d5e559188d32c5168cc71152887d8ea71d15bf34ef9cd15cb041c40a6aefe67a530658033b23542baf13eb49ed5f4c0b7b111d7734ae2f88e7b05f9b0cdd7d573db49a19429bf46545942bb14edc07963d2800b5b66414a552a19759e3bee9c57d4e680be3d6503174807ebb9845825a0c70ab68b7d11d4af2cc3f09f078aecab7e88010e194843746e2c4c27893ed916d12d7575bc66eb1a290eccf0513a3e09579072ce06d8a7f38211ea1da1d8bfa710062124f853e1c9261705447defc47a1adf80c6a0ca3ea3d92ceb4b19219cdfbb15c1ffa71cb7fb0a35739a497e640efefdb2ff43e55a01f649aa81a5ffbd71e6dce6d3fd23d2011701aea425fdc6b4b89b62e946be2642b9247e35c6a16e8bbc8dc310107f6b7ce4fa1b22e96fb9d8562f02eda1a5ca567421a816fcd54396eb32759b565c9ea9cb3661a4c8e42986e8d272a33a58ab5bc23b11d2ee27a94d1fc5faa2a0ac039fd46f4f3540e952035f2a01762f1ea6c159137e220650a4de7c37550dfdcf0fb2852288a2dc0d75a13b721252e4413513bdb146f23f6c453c8212964a0ad3cc7e1706d547dd9a6e920c7be07bfc9ccef26adbbb81482041600e5be4608b25b7ade0bd39707d1141a9c38e91d6d6a8710e20c146bd6a167599b91eeba7d279906fc5a99ca5b5316472a92aa0e8b256cfa9df92304e935d1022ba8ee8691311ac01f5acdfe586ee52cd2be6025ba4653066c824575b4d79e762c2da88526218e5d1684647a0b97b5fcc8f97a24e99a4f6e8cab10ccb9181685a81710d9f486c8a9108c547f2d7d9796c1228b0709a12e3fd8326bf7834a1dacd53ba4e2dda1464eb76bf8e10837c5e0c8f11c2ea8f00356fbf4601bfddc1b11e5b391eec85ecdfbb3c9174bb085f3029ef74712def3d1bc2eb8b062e1cd968b374e2ddebb72d61c52ecb61f1deef94b7912361d7cf658040c7c687d5f2c471ae5a16628d632ae7811680f078efea45fd12ebb24dd2fd3480c5d399b9595fc698bd6d0c435ec72ab51a7d09edf66764065d9cb142b7e6d555a11b06605fdc3479afe95b5fad22b5e9b4604d2ca7ba3eb51578f23dead1a272168f78d251f6019c0ef83eeaf64bdee46c83ab0119debf5fc865717d8032f1d166e1de0446dec8737fa858d523c1c8155d4aa8d253b4f46d2973f178b0492d8680a005dca61f448687547ab04454c5c8e0ce84401c2203ae4a65d2a4c297f9b989bebc0d717a7b7b1c26a72ae0c8e838cd6eacbd41c6c66badc10014e4d4de5b875ace180f4562b87c63328a89149e4fe0d1f76c6f468a1f36fa60c7bc7e5fe59eafa01cbcb0000cb0f1498544708be7a7b8419d0abc91fa29728021d27ffcc472552a602c5c809451567cef6341e2c070dbbfdeea42a20968ed6254aa989dc871e3c91a70144102aa5696b7f4f669e1e0b3db2a8c101e1899f4803e8724d5e9407f485851ab9e27f0e7940a436faa458d51734e72c88c953a1cd19bfed29f9a6e503696a0b6823493b253c7878515b54225fa2cc2c43cc938fd1018c70f38a4649a2760f6cc067909d2812897dc8516e28343a51d9a0d987d0e7117eaad11d73c1e43dbd43da5e94f29ee359b1639549c847831283677d2dc6fa0b4e9bdf4affaeac14d82d816a069f303c63ff8bc301ad2cae0e77130b8771412f326f2dadc7c0d69543f99fc3b5b07139ada8c8443a7239af214df474b7515218b6e60c22294e4752120ff00af6b0d8a6f574fde3d9d31ac52559f9c1364efa10aca4626bddf9117a6cde7324503141532c8abd79c4694e62d4cb86596466160840dbfaf9000ffb80391b20e672a5f3f84f69c2d97d498f7449d1bb13f01818002923644f08ea3b3e94a83909315385d2720f9f98bffc8373ec9615900ed48016d62d4b5bbd9db07be2097ec1142cb4ef5789c9761ea7131d74168531c966e226d6dc9ace4464dd4aa2419f4fd2560ae90ee5021d85f58d208de486b6d008262451667f7fbf78bd41f6640410e52c87d7f000983c3282f1a6d7f4bc6c3753842dadd4bd8444fb54dfbbddfb4a627f6cc274263ae1060a69bdf3e0861847cc8810dc714756a05b8ca2ad1e2802a1d702133aec5901fba30e45dc302c7cbc1968132a3146c7832f67796a4c541788345733da795f14cd6049ddf75a49015e970c167f44226bf08bcc0721b0e43312cd33edba128ec39367db196b3f9e22817bc11d3bc0fd02aca7dc97a67b6896ddcef0fb349e9c3b36214fe2efd4cb55709fdd0511c1f18652d148e1959d2e6798bc956b6141176e0ff385b00f6178ae44f8bc2738aa8b71330cd645514f97c7db99e5722f935c22998887b7051d0ece56a1630af166104b50c80dd514eca36ad8feea1457acfa6d77b7991d1cecd96e7bada5161d34c5df4bfa8e7084390684652ddf60aa86f07a3269144e915bcc6e02f3de203dd990d931368a32f6075b3f49f95246e2686285651db5d4a08cfd102858902bfe01c03f1ad9cbf1b7ef45f67e6312ebb0c6b532731a4d26eab88ea7339f2e17582ba8136f2991301a7c24a3a3b5fb5fec1f3fef8b11e29ff40e9790f783af2f1da1cf1c4820e552ca692c2142fb81189dd32ee5b4cd67dc8920701023ef7b04edd473d4dc28377445acb892620abb94d69cbfcdd14f32227020a45a1ac5ba140ac9814b939870f27931e506f0a2676c9f65d88cac80d75b67a4ce57a07f8f2c0dd48cca8b68c2402cac0f8aed4314857f3473f904c7cf90286339fc11daa42b68686f74087bee2d18a1ceb6573d5faaf230e23dee53502595939e6220245606fbef3f3647483c3b7faea0b02e4e1ba5945258c9d51fa75512da79ebc9cc930184dd776227a2ac970d98c26cc7bbcbb699908974f2772729d069f7b8d890ab157aaf2faaf3282fecfe158631803de33736808af53a4febc35e372ff2452905007d266868ef9e28c1c631de830a7a3c38bb3b0664292487848815b002e4d0ed2474460c04b736484a520eaefb1ea931b86147abf7762ab20f703dcc3d607f8b0089caaa2eb682bb184dc81f43d39bc24daded115540412e4ea3ffb5adef53530a886a7b84b585d604f5b132e4f314fb20e59db3d005199a0703a149156a65e32aa0e38754aa43ef903a00c49b9c4a88a05d9f3bf958e07266055dd65088d9332b6c7d29ff611772b29263c0eccc81514b73cebbcf284247c809bd344aa416941c6bd68cc4e5c26c659f096345c1b8d5edc76a9d61ba130345fd5ad74b27a5e92781a44d259141e045f548c11b9679e7161823a61977ffb659471b0271d1000e2b89ad259d73b24aeba57cd6b941067906339e0ca696d048d302751cc0a39acc0b3a7ffbab9093266b59eedc8a12ee36530b7765feb1d69d64940461947246d619a14b4a57cb4bb9d60feeb98d8dedbefa88e3fb63be9ea99f9d65f7694c55462847e8212c9332f8c8e330500161e7eb4d113e3ec89356ea8c57eb739f5f01cf1e1e6a1040be7d194c75cec9fc63d377dd590df604615d5c1391ec17b535d7680dee50b2f96af256227ad345cc93f80822614d3fdb7f50966ef5d5771f6a239c24a3670723bec8bd40f09dd547cee615b07b54866e67f6323f4a6183a87e22b21690068f21e34f58f1854f39f902c2121cf7621a5ab326138284d3f4c54ea61a2cb1f8fc94b90c64a62f15927c4279fe1fd41551c26d4799ef9326809090aebb294270c2a541da191e151e04bca72bb50ea7154e249e8bf58d3248c323ab9efe2c75cc88deba848e0477c2d4f7842823efc16251b55dea0ddf7c06ff6372598913b16600c12af1f085fc8b0d695e469397a0ba7a18a1570930aff5be1b450c1d00b59bff7887dfcf7f2f0bd0a92934561b2e093c3295cd3969b8fd8836b37145271f5d5905e9e81ba87591638170928a90b8e9045055e740fb43cc3276a66c9129e35a0a0ef6fe53bbe1e2745db53e13f623bbdbaaa0bb4203ecf23e85f3838c1a74837fc00299a85ef5897d070b4faf2a7b56998b2e05dc2aeb645f18090d5d232d40f6bc20623c01fb890301fe074c7d09bc63ebe1bb260ac78cec14373add0f6dd4686087eae0e89f6a854adcc3437d01268dd3984957437fc292ff847d0704480c3a484db21e9f0d77969fd778259d723f1b887943f50ea98f75371864780eeecfcf5a77a5498f3379bf8d52e461765acd5ef59a7578df882e599c82b441291216f68f6ca3e602b47ed99aa0c6dfbfe69f1a644d22b66faa012fe35e84031c6cd6640f8cf548e61d4a0205b33dec975e6d8f3f7632b02c952948780ebc89169212ee0c8cbba3869490e0f659f8bbae0bcfed4ba2c81396a26fd929eb167e077a82cd634a5a203ca87729e6057ca8961428828cb53066492efe6ebc2d264b0f0b99506a07addda2986179905ae1744a342d6abfb3c82510551c7d46f176ec19b3fc7a6c4e0d0f0fbe8d495007c2d575b4f6edd0187aa08936a90101deafdf0b4a5912abba298b2d3864ddc9282c756bf0b241a35873151fa04296301519c00fe2804df9545cc47117b30af8cba291e17af28d40c30d05568a0f2c6febf4341ff71b80a9ed4944339aa12387542572592e2456eba9e4af6ea17722834a13602f714effd3c4eaa41db256790c488b7f4cd0c00a7e7be1c0740942fc931c3514016ddfcfcda716b312953b009d3fa126c50e2ea5515e4c0b5a3a66f53f27f261098a8adcbe46d98d5afc16e08ba6c716f18a36c56ed66529113d6b6b707071822cb4593a890ac726c30a87f2a356904b8acb3395fbaa5fa7f7e5c4b1f0c385261c40a7973e6c2847b215e7245b9f9ff0f381c098493bf8934241901d3b0238c82927ac418a390cb7d18d15626062a992593358c3eea37934ccd84161bbd272d021cfe3105284aa92738660327df5994eaa506a85ddc3bb5b12dd2f73c31511b1097640233620fce20826a296910b7a30f774fb572b7a1b60959ee49247fa74412b4c395963a505008da29ef947a6f676686340b98a7f7f8030c44c8bf90d1fb2167e266e7bcc2a6c7dde2496aaedc51f58bf1227b79ff40a60ea3581c19597eb1198139824e4cffed72280b7fcc1bc10a25541c2cde5143424deba327200c291", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000007a6dedcfcddb992da4890c8a97aebf9f00000000000000000000000000000000b4d83f75777e7e4919824dcb4ff934df01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } } diff --git a/circuits/benchmarks/results_insecure/report.md b/circuits/benchmarks/results_insecure/report.md index 1d166abc6..4127b3532 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-19 14:41:50 UTC +**Generated:** 2026-05-19 15:02:16 UTC **Git Branch:** `feat/1525` -**Git Commit:** `d49dd7569dc6968edf92a66bd3ff48b6c5ee0503` +**Git Commit:** `a9da36063d769bd6a7fb70c313315091e702f273` **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 | 27.23 | 15.88 | -| C1 | 57818 | 0.35 | 26.50 | 15.88 | -| C2a | 142625 | 0.83 | 26.50 | 15.88 | -| C2b | 198355 | 0.90 | 27.09 | 15.88 | -| C3a | 132633 | 0.84 | 26.75 | 15.88 | -| C3b | 132633 | 0.84 | 26.75 | 15.88 | -| C4a | 92515 | 0.52 | 26.62 | 15.88 | -| C4b | 92515 | 0.52 | 26.62 | 15.88 | -| C5 | 151717 | 0.83 | 27.39 | 15.88 | -| user_data_encryption | 53732 | 0.34 | 26.43 | 15.88 | -| C6 | 86927 | 0.55 | 27.35 | 15.88 | -| C7 | 104273 | 0.52 | 26.86 | 15.88 | +| C0 | 6847 | 0.12 | 25.42 | 15.88 | +| C1 | 57818 | 0.35 | 26.35 | 15.88 | +| C2a | 142625 | 0.78 | 26.20 | 15.88 | +| C2b | 198355 | 0.92 | 26.92 | 15.88 | +| C3a | 132633 | 0.79 | 27.00 | 15.88 | +| C3b | 132633 | 0.79 | 27.00 | 15.88 | +| C4a | 92515 | 0.50 | 26.01 | 15.88 | +| C4b | 92515 | 0.50 | 26.01 | 15.88 | +| C5 | 151717 | 0.83 | 27.40 | 15.88 | +| user_data_encryption | 53732 | 0.37 | 28.29 | 15.88 | +| C6 | 86927 | 0.52 | 27.28 | 15.88 | +| C7 | 104273 | 0.50 | 26.38 | 15.88 | ### Artifacts | Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | | -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 10.69 KB | 0.47 KB | 3042639 | 176112 | 3218751 | -| Π_user | 15.88 KB | 0.12 KB | 2972941 | 170404 | 3143345 | -| Π_dec | 10.69 KB | 3.47 KB | 3553795 | 187260 | 3741055 | +| Π_DKG | 10.69 KB | 0.47 KB | 3042712 | 176196 | 3218908 | +| Π_user | 15.88 KB | 0.12 KB | 2972857 | 170200 | 3143057 | +| Π_dec | 10.69 KB | 3.47 KB | 3553819 | 187284 | 3741103 | ### Role / Phase / Activity | Role | Phase | Activity | Prove time | Proof size | Bandwidth | | --------------- | ----- | -------------------------------- | ---------- | ---------- | --------- | -| Each ciphernode | P1 | one-time DKG participation | 314.97 s | 127.00 KB | 128.19 KB | +| Each ciphernode | P1 | one-time DKG participation | 299.47 s | 127.00 KB | 128.19 KB | | Aggregator | P2 | combine folds + C5 | 0.83 s | 10.69 KB | 11.16 KB | -| User | P3 | per user input | 0.67 s | 15.88 KB | 16.00 KB | -| Each ciphernode | P4 | per computation output (C6) | 0.55 s | 15.88 KB | 16.00 KB | -| Aggregator | P4 | per computation output (C7+fold) | 81.66 s | 10.69 KB | 14.16 KB | +| User | P3 | per user input | 0.70 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) | 78.56 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.08 | -| Committee Setup Completed | 20.22 | +| Setup completed | 3.07 | +| Committee Setup Completed | 20.21 | | Committee Finalization Complete | 0.01 | -| ThresholdShares -> PublicKeyAggregated | 314.97 | -| E3Request -> PublicKeyAggregated | 317.62 | -| Application CT Gen | 0.32 | +| ThresholdShares -> PublicKeyAggregated | 299.47 | +| E3Request -> PublicKeyAggregated | 302.03 | +| Application CT Gen | 0.31 | | Running FHE Application | 0.00 | -| Ciphertext published -> PlaintextAggregated | 81.66 | -| Entire Test | 422.92 | +| Ciphertext published -> PlaintextAggregated | 78.56 | +| Entire Test | 404.20 | ### Thread pool (same process as integration test) @@ -75,26 +75,26 @@ | Name | Avg (s) | Runs | Total (s) | | ----------------------------- | ------- | ---- | --------- | -| CalculateDecryptionKey | 0.11 | 3 | 0.34 | -| CalculateDecryptionShare | 0.61 | 3 | 1.84 | -| CalculateThresholdDecryption | 0.57 | 1 | 0.57 | +| CalculateDecryptionKey | 0.11 | 3 | 0.33 | +| CalculateDecryptionShare | 0.61 | 3 | 1.82 | +| CalculateThresholdDecryption | 0.56 | 1 | 0.56 | | GenEsiSss | 0.13 | 3 | 0.38 | -| GenPkShareAndSkSss | 0.23 | 3 | 0.69 | -| ZkDecryptedSharesAggregation | 8.51 | 1 | 8.51 | -| ZkDecryptionAggregation | 51.14 | 1 | 51.14 | -| ZkDkgAggregation | 21.24 | 1 | 21.24 | -| ZkDkgShareDecryption | 1.51 | 6 | 9.07 | -| ZkNodeDkgFold | 64.91 | 3 | 194.73 | -| ZkPkAggregation | 2.19 | 1 | 2.19 | -| ZkPkBfv | 0.35 | 3 | 1.04 | -| ZkPkGeneration | 1.39 | 3 | 4.17 | -| ZkShareComputation | 2.77 | 6 | 16.65 | -| ZkShareEncryption | 2.59 | 24 | 62.19 | -| ZkThresholdShareDecryption | 6.17 | 3 | 18.50 | +| GenPkShareAndSkSss | 0.23 | 3 | 0.68 | +| ZkDecryptedSharesAggregation | 8.38 | 1 | 8.38 | +| ZkDecryptionAggregation | 48.47 | 1 | 48.47 | +| ZkDkgAggregation | 20.05 | 1 | 20.05 | +| ZkDkgShareDecryption | 1.48 | 6 | 8.90 | +| ZkNodeDkgFold | 61.02 | 3 | 183.06 | +| ZkPkAggregation | 2.12 | 1 | 2.12 | +| ZkPkBfv | 0.34 | 3 | 1.01 | +| ZkPkGeneration | 1.36 | 3 | 4.09 | +| ZkShareComputation | 2.71 | 6 | 16.29 | +| ZkShareEncryption | 2.52 | 24 | 60.41 | +| ZkThresholdShareDecryption | 6.08 | 3 | 18.25 | | ZkVerifyShareDecryptionProofs | 0.10 | 3 | 0.29 | -| ZkVerifyShareProofs | 0.23 | 5 | 1.17 | +| ZkVerifyShareProofs | 0.21 | 5 | 1.07 | -Sum of tracked operation wall time: **394.71 s** (often much larger than end-to-end wall clock +Sum of tracked operation wall time: **376.16 s** (often much larger than end-to-end wall clock because work runs in parallel). ## Raw circuit benchmark JSON (Nargo) diff --git a/circuits/benchmarks/scripts/run_benchmarks.sh b/circuits/benchmarks/scripts/run_benchmarks.sh index 71e5f5f08..30f9626fe 100755 --- a/circuits/benchmarks/scripts/run_benchmarks.sh +++ b/circuits/benchmarks/scripts/run_benchmarks.sh @@ -269,6 +269,10 @@ 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 + "${SCRIPT_DIR}/sync_bfv_vk_binding_fixture.sh" "${INTEGRATION_SNAPSHOT}" +fi + echo "Stage 3/3: Finalizing outputs..." echo "✓ Report generated: ${REPORT_FILE}" echo "" diff --git a/circuits/benchmarks/scripts/sync_bfv_vk_binding_fixture.sh b/circuits/benchmarks/scripts/sync_bfv_vk_binding_fixture.sh new file mode 100755 index 000000000..0df8397a8 --- /dev/null +++ b/circuits/benchmarks/scripts/sync_bfv_vk_binding_fixture.sh @@ -0,0 +1,27 @@ +#!/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). + +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}" +FIXTURE="${REPO_ROOT}/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json" + +if [ ! -f "${INTEGRATION_JSON}" ]; then + echo "Skipping BFV VK binding fixture sync: ${INTEGRATION_JSON} not found" + exit 0 +fi + +if ! jq -e '.folded_artifacts.dkg_aggregator.proof_hex and .folded_artifacts.decryption_aggregator.proof_hex' \ + "${INTEGRATION_JSON}" >/dev/null 2>&1; then + echo "Skipping BFV VK binding fixture sync: no valid .folded_artifacts in ${INTEGRATION_JSON}" + exit 0 +fi + +mkdir -p "$(dirname "${FIXTURE}")" +jq '.folded_artifacts' "${INTEGRATION_JSON}" >"${FIXTURE}.tmp" +mv "${FIXTURE}.tmp" "${FIXTURE}" +echo "✓ Synced BFV VK binding fixture: ${FIXTURE}" 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/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts b/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts index 6ab527d6b..239ce3cd7 100644 --- a/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts +++ b/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts @@ -23,26 +23,68 @@ const { ethers, networkHelpers } = await network.connect(); const { loadFixture } = networkHelpers; const testDir = path.dirname(fileURLToPath(import.meta.url)); - -/** Committed golden folded proofs for on-chain Honk verify (insecure micro preset). */ -const FOLDED_ARTIFACTS_FIXTURE = - process.env.BFV_VK_BINDING_FOLDED_ARTIFACTS ?? - path.join(testDir, "fixtures/bfv_vk_binding/folded_artifacts.json"); +const repoRoot = path.join(testDir, "../../.."); +const COMMITTED_FOLDED_ARTIFACTS_FIXTURE = path.join( + testDir, + "fixtures/bfv_vk_binding/folded_artifacts.json", +); +const INSECURE_INTEGRATION_SUMMARY = path.join( + repoRoot, + "circuits/benchmarks/results_insecure/integration_summary.json", +); type FoldedArtifacts = { dkg_aggregator: { proof_hex: string; public_inputs_hex: string }; decryption_aggregator: { proof_hex: string; public_inputs_hex: string }; }; -const loadFoldedArtifacts = (): FoldedArtifacts | null => { - if (!fs.existsSync(FOLDED_ARTIFACTS_FIXTURE)) { +const isValidFoldedArtifacts = (value: unknown): value is FoldedArtifacts => { + if (value === null || typeof value !== "object") { + return false; + } + const folded = value as FoldedArtifacts; + return ( + typeof folded.dkg_aggregator?.proof_hex === "string" && + typeof folded.dkg_aggregator?.public_inputs_hex === "string" && + typeof folded.decryption_aggregator?.proof_hex === "string" && + typeof folded.decryption_aggregator?.public_inputs_hex === "string" + ); +}; + +const readFoldedArtifactsFromFile = ( + filePath: string, +): FoldedArtifacts | null => { + if (!fs.existsSync(filePath)) { return null; } - return JSON.parse( - fs.readFileSync(FOLDED_ARTIFACTS_FIXTURE, "utf8"), - ) as FoldedArtifacts; + const parsed: unknown = JSON.parse(fs.readFileSync(filePath, "utf8")); + if (filePath.endsWith("integration_summary.json")) { + const summary = parsed as { folded_artifacts?: unknown }; + return isValidFoldedArtifacts(summary.folded_artifacts) + ? summary.folded_artifacts + : null; + } + return isValidFoldedArtifacts(parsed) ? parsed : null; }; +/** Prefer env override, then fresh insecure benchmark output, then committed fixture. */ +const resolveFoldedArtifacts = (): FoldedArtifacts | null => { + const envPath = process.env.BFV_VK_BINDING_FOLDED_ARTIFACTS; + if (envPath) { + return readFoldedArtifactsFromFile(envPath); + } + const fromBenchmark = readFoldedArtifactsFromFile( + INSECURE_INTEGRATION_SUMMARY, + ); + if (fromBenchmark !== null) { + return fromBenchmark; + } + return readFoldedArtifactsFromFile(COMMITTED_FOLDED_ARTIFACTS_FIXTURE); +}; + +const loadFoldedArtifacts = (): FoldedArtifacts | null => + resolveFoldedArtifacts(); + const hasCompiledVkArtifacts = (): boolean => Object.values(BFV_PK_SUB_CIRCUIT_VK_HASH_PATHS).every((p) => fs.existsSync(p), @@ -250,9 +292,9 @@ describe("BfvVkBindingIntegration", function () { decPublicInputs.length !== DEC_EXPECTED_PUBLIC_INPUT_LEN ) { console.warn( - "Skipping folded proof verify: fixture public-input layout is stale. " + - "Re-run test_trbfv_actor, then refresh test/fixtures/bfv_vk_binding/folded_artifacts.json " + - "(see jq one-liner in that directory README).", + "Skipping folded proof verify: folded artifact public-input layout is stale. " + + "Re-run insecure benchmarks (syncs test/fixtures/bfv_vk_binding/folded_artifacts.json) " + + "or set BFV_VK_BINDING_FOLDED_ARTIFACTS.", ); this.skip(); } 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 da9d7e672..c289ecab3 100644 --- a/packages/enclave-contracts/test/fixtures/bfv_vk_binding/README.md +++ b/packages/enclave-contracts/test/fixtures/bfv_vk_binding/README.md @@ -1,15 +1,25 @@ # BFV VK binding fixtures Golden `dkg_aggregator` / `decryption_aggregator` EVM proofs for -`test/BfvVkBindingIntegration.spec.ts`. Independent of `circuits/benchmarks/`. +`test/BfvVkBindingIntegration.spec.ts` (insecure micro preset). -Refresh after circuit or aggregator public-input layout changes: +## Automatic refresh + +After an insecure benchmark run that writes +`circuits/benchmarks/results_insecure/integration_summary.json`, +`circuits/benchmarks/scripts/run_benchmarks.sh` calls +`sync_bfv_vk_binding_fixture.sh` and updates this directory’s +`folded_artifacts.json`. + +`BfvVkBindingIntegration` also reads `integration_summary.json` directly when +present, so local `pnpm evm:test` stays aligned even before you commit the +synced fixture. + +## Manual override ```bash -# From repo root, after insecure integration run exports integration_summary: -jq '.folded_artifacts' circuits/benchmarks/results_insecure/integration_summary.json \ - > packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json +./circuits/benchmarks/scripts/sync_bfv_vk_binding_fixture.sh ``` -Or set `BFV_VK_BINDING_FOLDED_ARTIFACTS` to another JSON file with the same -shape. +Or set `BFV_VK_BINDING_FOLDED_ARTIFACTS` to a folded-artifacts JSON file (or an +`integration_summary.json` containing `.folded_artifacts`). 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 e7250103d..6336eba31 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": "0x00000000000000000000000000000000000000000000000ab6528b828dc60dbb00000000000000000000000000000000000000000000000a1abbbfd7f6dfd38c00000000000000000000000000000000000000000000000830618157af29e0dd000000000000000000000000000000000000000000000000000030b0b3de35ea00000000000000000000000000000000000000000000000188735db8f53dc39800000000000000000000000000000000000000000000000a18fc5b2c715b3bdc00000000000000000000000000000000000000000000000bda61492423a3ec510000000000000000000000000000000000000000000000000000dd0290b3c7a9000000000000000000000000000000000000000000000004983364b7fd2661c10000000000000000000000000000000000000000000000039b4556d8607bdb7f0000000000000000000000000000000000000000000000099a73ba6ee66e29af0000000000000000000000000000000000000000000000000002f88cb3525c2400000000000000000000000000000000000000000000000dee1e0248c53aec7a000000000000000000000000000000000000000000000002d7030903bd0292ad00000000000000000000000000000000000000000000000d3bc4e4978a08e90a00000000000000000000000000000000000000000000000000023bca494aea202d963ffe2e1c6090bd0417565279b28b01889be9222dcfe21d89b8bc37ea633a118425af1d443927e153024e09a6f511d70b18941e6691c58bd0fcb01eb5580d1bf539f221f8b59e7182eebb1d4f8fe20f6d071ae56a9209b65dba1db587938116ca9f42700f6637513c56ba8c9b0515960d790c8b4dbe376efe344d5f935cea06b4b861140217a2a61f3c7e65b5f762ac0a03f3a240dbfa9e18f437a7cb30b5199c8aae11cf8c30a4ef2de6b9622cd84bd98d344b563e0ad901317254e5f708029cd7ccd824db93db38c3ac3983443731fea6679ede17b63365e86e934fa6bc0806ae63c2a4d1a99d5f8be2e7d9a42bfd10502673ae3e598c2c0c3362def4a927aeb4cd7a438200bdeecfe20a3b86d93a666bcc3eff4dc3eb82ff02e3a2b2a4218ee3aa235f850585d5e928808022969398b5b4c2ad44264feea427187abe2a2e359653c433f3965bcbf214db7711f8bd8bd7fe71c7feb983cb463ce891add81689ee23cb1e15053533135d6a5b3354bddec9afb60428d5db053cd7e6f16b7f01a8b5545ffba4cfb3fd23b26cf203722f86cb76878b459259f3a08d9fdd32f22153b36759cc3a604f7812d907a333a091afe1821dbf642c8b8e522071c729c924b0e2aff5d72d70c59180b7f69061f709c6a2508e68a5e86ce5914fc7dc55aa2ec18dbc526b61e9cf05734d48c3955e23058f894a535ac974e102236e60bef51d63943cc3435eeff39b9905d4ba6d221c1392add2485af108330ddfe0c76da1192a7156f072b520802de68204d5b130afebd9f7de607b326d6265b1e64ff2182bd0e3458273fbbd7f7f5f6fc7da4acc89468c6a82fd21a84bcf8f5a169b502e14cab9389991a4d1aae476597ca83bf5e0a3d2654caa22b02c7ede94e1f4392015227284b88fdd39e7406e3efc1896e8668d4576465a3fb0bc0203261adb539e04aa1a23894473f04e76904d56e7a5861eb2f5aaaf44aa8ef22934998ca79d482884f74605be1e455bf594fbb689c1fb78f53d217184a674c725df26407f7e712dc0ab88ca15039410497ce8310927ad651c80cdde05da3e9ca5498ffbe89e01149e820d0a10d51851e40fd7645040132b3c1a4813466249d6037559300da0e5143232ffe0c122c73393a689d26e91faacc5b6194d8843088e11503b50ba986713933bac02d1b0a39e55efe0c4b8bdba00b2104956c6755aaab1e33961c91c792546146690dc9bfe19b77238cb26d4cfe0b9746ff20199ec3579c7d3e54cfa7624c17e311a082443911f00cc9be18fe6126814303a40656a52db542563901a33200a084c1dabaf2302042c0e7387813c36a24d46cfe883888653580f32706af013bf242fa9b03a2f7e40a45f1d23c9048effe8eb8de9f41046e35a8d753851e408fcb71165eb5d6f6c197c7a0fd6991b155db680a06cdd0116615e653c85ba7c1294aa0a07ef05bfceb690467dcd7ee88dcf31ffcb5453db3f71a2ccd2e75df523584ab053db076c6fb57ceb1356781e4eda6a4b1603da76bafd0bbbb6d61f40260ea34d7aeec03ffc1a93c42370f74c0a4ed8043d950136de56617a7759274611b14e0b002ca984eb4ae6dadce0aeccabd358192021a15bd33b76dc01c877b72bc09a425821140f55a66529a1cfab1abfa742c9e78c858182aa2db6b9483e892e467f3a4a9a130cc8d5b4373149254a438a33a1c72975cdb0425271599c4e9f02e35e2aad8080ce76aad49fcdbb528dff3f2404d0920be45890ba1b29cb4fca098bc976b98680a06e873daec3d1179855d59cc2f420e1a16e5b7da81a1b4eb907576f97540f9df83d2c5ea725c1adfb2b1e789cb0889b2dfed9329a9345b70d2da362dabfb7e832f1c4cc319924756eca3240f9faff90eb37224a7f33ce899e2e5130c2cfa148e522504889f859d71a412b10b374d84a6c139084239080067016429a61503f916f0ce4db10b7d0584b6dc3bac6df045686fbdaec850ff10b901466522e6931ae2771820cb1919ab5fec1e9a4fbdf686b1c66df96b7e319f243122cd4186f0f615e3a6b6e7d7f0e4968b1380f3751d7b3bd220f8c27a46208d60a2487ce2b1ca1802aa8364092ded7ff00976bcb3a1620c79ae30146031a010a2c0a2a00c6e4bff75a514d26be107d1b126c95cdb6e677736d9e665678bd12f70f209adde22fc74e1ee6b059bf66f54b3d240c2fc4b6f50ddd82d617ea2d5cba1bde17c4fb34105992103768d2b0378abd71926522ba98afed09931f50b3bd9527f78f8c6fb753b05db1aa4eb4d283344acdbd8fc4e4e473e470dc9807ca6d252048dcdb39999d4575a00011ab76c1b9cbebd74d6e61de94ea700464f14ed7b622d1c86a8dafc5edd967ffa4d14c8783edbe39442888f1878b9358d0a8aa78480f4ca426772b8d77e03813f6746c729583e1b0fb0013b5dcdf2643ec202799b52ac74b84233b3e19972530e0eb9e270c31decb023ca2216a641da5b326dfd8da24198fa386e1e60ed5257039de5f1abe0cceff64cec32c2e001b5ba1cc1899261f97f33e03b7b2b70d3f9a7124c4ce410f818051c3d38b385386b8a2d29cea72273660298664bbb39f126e0a51b6e026dc983640af54b7c6c6c9b19d3849c2f8242fdcbd80c208af1b934549fa4e256cc9c5c87e44d3f34429af107e081d8251296ad5ad4027c55437aefa751b0084f58e19227dfffebb39fe09200304927d8d04e3dbe7bdf246e8a9034765e69e7b4debdb242daf2a654116f3f6ec3b527eb817127f4dee47fdbe34f8efc4114aef0b3c73558735da92b92d0c54acb864b55b1c1b879c3b64e5efef70fb093f10c7b26f286ff8a95cd1fc78cbfd205dda55b12c079a072d723e63051bedcb0e9ec0bf522e4f219d88f7b794beb58b79be8ae303e542dfa8998fe8e2e40f0c2df8960c6fe54f1252372d030134f16fa2594a412a70af625481ac842b428aa11075415bf98856d4fc23375f09b6769f988976570f54169c1ecd9808627f0a9a85e1468abbf435a00fb3d1668484efd3724c7682008a87ea2bdfe7d46eb25ff8378ee96849409a8fb37eb7011ad04fb384c4dd2c1f4ef9f1b37b1371558b8e2f93582cb2646f2c09724656efb97d3337dcab06c90f0e0a99dbd83d4a4040273cf915e6042171711250cf964bd163d23a72972b3f0b9d4b10137e2807a7f31b2f9004fdb8b520e002a13526ac923eee0f81e6c1640d58e1c704ace55a8fe098468cdcac47a514ce7c13d1bff170761bb70805646c1ce220f83362153b1e348491bfb71d71e1d7efc77b6d24c3ca974a94d52c496905ab852fe706d48bbdbf5ae25ce05dee3e8b7b6ae617c3cb19530fa069d0471f21fd12a73708d3bffe920539440d67b4d3f416ea872b8201a1fde3ec5cbaf9b102e000c471ca6b9a956924a7090f27fe186fed06b74101b9320e5c21353be11a02275397a74cdc6c9c22b70a9888537e3672d2af698c54b749648261a50409f2047b34e162adab623e609aa4807edd86ab29f032d7d7def5528e3f459d1c83ff184be5373b224676aef2e0746332fdfbaa300f276d444f7843a2a98705aa467e2de40fa7cf1cf09ac1c95460fc6add42a149d1830c1b3a1ba13fe1ea634d731306cabf66feb50a478f667de4ba425e8a0d3deaf6848a0ab8c7db3f5574340a7d1214de510d4c573dfbf81e2ced85574f085b25585d25656c4c44b6de923a840a167bd567f720f2035b2a456790a5c36363ce092704be976aab4b7d59302d178f0edea2005a2dfde72ce639358d5b71b0d71cef895d6e7416864a2b7f83d1162a0d570982dba35651835593d240d35fdbf3c39b92d70cb1eaa52722b06dc5c92226b4a401b7cd3ac29a59254af9e4d233300734b4fb2c968ec5e2893566f7ce921847b7ba1aab42f4e4f5f51c638c03a13ad23805b9de2cc0a7d92549c97bcf6c10a78f15aaf95bd7419f2b73befe6f639828985bd1c8b28ef032a4a56adbfcfd1df4856798eed823feb886065eb10586cb72ab2737640bfa4b7c5e5fff3b2a652f48aad393feab573c94a109a7801ee0da56281d67b8691a8ae036e9060c4fc22964f9dfd7d8d3f81c1e68e0392e1c8f672d3cc7daf941f5eea32c8c194f85531d2643255822a749fba1e156e0c96de8ffef2b002b753ea47633d0a35078298b1ffc0eeb733fcd2e39f1ef634cbb169a64529f0d8c09d7e4739795946e0bf9451f995d5977e1a6c154229e20a048e7803169e2aed6f306b8e7e503853ff5e70403ebe23597b0a87ab2d5e0882414db412e051e15e0eb2ff95069bf0ac8c145250e84bbe57101fa3560210f08a76f92c25c8313f9330ce8ab268f6a37d3f955450ac7dd3e876fd857582c12d23c67aec92d75a9cc0e8888a9b984c31a9b1a290f2d8a3296f661b42d2bc8d60093f7293e6d5152112ebcc19bfb2b431ce691187f1622524074146516bdf0695d725a9b2b8b2ab8b171321c21f6bc9e966e93afae0f21beb5db56c01e0764cdc9cc1fafb0e77ed45120a2859d7ace771c262f609f23337085ad7ac0bd1518b19d7b02f22d36007e4a63a9655147b2549b74911bb02309ca97c90114bdc21fbc2e8354aedf45453389690da39edac5fdda7d03430411465676719090813825915d4ae9ec91c3afe24ccec744d07bb4de99add1605409b0c014deee8122ecfec860077f4e94dc76c97d6ed198b85e40174939c8b6aa15b8c1050960dd58e39fd5071db6b77591dc9529a230ca6af9fcc9f005b5caca043c24781515871b203e1de9f33fae96804acf9dd66a37851e916e09d0eb92f42232ad796741d77958f87d6be73a92fe19723019c9c9191346e428e0d06611952a980c1b7ddebc02477fd7417b32342780bb56756ef02acb202e3a68725a381f2b92ceedad2961000dce17ecfb2cc250be2c22e51fc82287d365e7ac359789ad2d6ff81fccc535275f3cc21c3e3bf4a04791627dd6d29d721f27082dcaa91f6e07f16049e9bbfa056dfd61d3b7b4b669787d139ea5119d4d9935cab7396c0e5d1214293575a65ee565f4922d3bc625d71f6a09afb3289208b9d3483c05975430031d01ce4ba374f41d28ac18c62bac813ed4c81aa4092905795358f08d116aa20409f44609813fe3641045f1632fbbfbf1277c4a783f5f412fc58b5d902d9fef218370a7b167710d3e2b896fede982fead14198b2bb122176a2cf6a41e596fed0f77a6ec2b5cc237a288ec5558415477ee54a5823a4c25b9a935a086381ae87400e36213fe3c54fe588df40ad8f9910d1289375d78aea52c585e6b0d8b000ca70a2814832c8ba9d7102d18c2c6432d2b437276444d455cb827d2cee930edc7e325b7423f5022749fcafff9a8e0f17ccdae9d2495be960cb92e0522412d116d8c2f1235fc89fe520e96cba827e08dbcc18942ea99d34396d07a510b7e4f2da3dc2448ef4ad4743479abd38c5ad3321ee0024af73947e13ac6e8e05ab58d0c1ce70d39e806ed89cd8f4be46b3890b8e73f6eccb674e53898b7d20c3858642f051c2e1d4894abeee0ac8b42e5098272d0c70d20fcfb4210b2b050c241e49e197cb11f53f6ae5413b466feae19c03d4818c347e400fa24a88187cebcb8e2a03b7f6700d14e54f9fd3815afea6aa78f1eab9ea0e63aba4e6051e188a1e3e11242f1c219d4bcd07f03a22b20ce8dabe5c7e276b88230a1bcb9968fca6fa9ce70779f350e073860dd63b61332e41cf6b742f050a43c9ada81ede0273e2f1d7ac851cdac0e6b76c48a46bb1506326939d912cb207b0452cb0657f810168ea8f5bb5336af12946dc6a82457d21b99ee430376f2943713cc5c0ea1e305d3f339985477c86d1461abaa8a5bb1949440d17fddd237e7aa73ac7b53f8eaddda829d7ffc6860de18f796e1c8e4ba59c7c8268e0c2c1ee35edf60c62230d3abc9bf57818e7f32a40a0f716c567d18e4dc7658bf84af3d5acb0c49f8db7a6c594f2e4b6eca678d460cb93e47b1344d2e4703f14f1d8d2a0555588ca28e19c7262cd4e5cb8f32bd1207a5d911fcba9704a8407c74e8cb344a4bfc107d6ca66320908508105c49253d04f3d0fbdf6801000f6c79ab6e24279275e5966c2eb653daf4f55be52c661aad0600f4c6c80d9d9fc0e43126c9fcae4f8593cd15c655f6096617f7c219bfb81817655fcbf9f285a52b89b1472f10724b7fb50540b377da2b45384b198d1772f229828e95ce47662a8f3e49e0606ee53e2d860c44af493f0d3b0d572070e8e5f20cab47637347d09170a19ae0a99ae74354c855b4611655aed2de1fa258cc243600dd47ad410fca16aac2a116fcce260a895c81a3a89ce81a19afb5868b3a64de0dfa78ffd98eba18398883fe8955fdf45716dd3bc94011f7062506ca98744fba2fea32b10cf3b2394da53d9988e193d4c5d06789938195241605941bc9d5d66c04302095ac006752fc3cbb0d7512fabb3a4b93bf67264df85bad30f28d468745212d98ac8e9fb00dbc19b1a1f92e24cc8573592579ce3af143ed04a9029bafdf1a0f4fa7afce28cbbefae3194c23921d61d988b4c18d29138ea3e95e3786f75600de9e1339cb0792aa919d41b3bc0e35a96053d13228d0d0dc37732da80dcd05027ca25b34cb25393e1b9d30065a5f8cf325982c4406199a0b4ec569928809ea1765a05be230f3b04c5e6feb4244c1c94caaf2123fa942a5d05ec8ebd4b446d52f44119a9447ed0b7dd6cf5e9abd2842fac8c1fd2dc88a968fcd238dba2b100a27c7851b2997c86cfba255e10b7ac0d6c9496291cf63ccde6285d45fd46da38810bf3ad0fc190493c633a698581fce39efcca70a352620f72803f7587656d23b2753b69783f660b76c896d865dab1fc38c104563f253241ac37c8bcbc68d88e309e109d15f9e10c2c39a1fbee52879576826e06802a9c990ddeb73299543b1871b903b1927c1f9e25478e97faaf3bf8cdbf206994210050ec9dac8bf965f3e6e08ed6d0746709cd2aaa86c5e32d1bc480d3c8c53e9a4c0fc785cbee882e360cb2d6016adbe6d57df4352707b43440ce847343bc82190047dca88d2a925a10c882dbd843bee3d2f61f817dc396bb607b3069ce2007f8f38664f16fae5814c4f15037e191d5f2d381491bcfad0ad7714c0f62326f534bd932a85621328d3faafac292949dd9eb0c36f2c1f14868951a8ccf391d6a963e2f46dcb3b34de509254b223ce8c532602bfd600fe080235761b6f24e35f6a869247389e27fafaf261a3302ebb431b8ef908c86b147fe26568f2beec945500d20a16461946220dc41556fc09aa6ae0f757562c86b4d4539f1e7eccce199fd06a9221d358cc0dbbbfd66db5193515ace6da5cf705b604ef665eb3e048a0091d2b6c82bcf4e3f9f3ebfee98c17dd7b4f796c867536767b7cd01e648b24260af29d0fd53203c08b4eacc24c3223bdb95bc20fc8422f635e0e5d8188e393b1e7aabf1fd025d977bbf4498537dd1809c8d4f3736424abefaefbe35918025ee7c3571bee54aa24a8b812e6c4d5730dbc7f04df7009edf8a6c80f2eb13eeea47f76177ec317bf7e97f452a20565992b4be9d856d012b26309d4a9acbaf736c5b917c42bb663468a2cb60c53e6e81225db1c970f1145e4fba55a75981d1ee8a0d93c3f221194b18b5d80cf453dc31a28b52c8720c7778419ef6f03bec2acd6b32c9b1ca1f9e575ba939756158420b10fd8ba42b0a8a67881c5a02d339a71d2a712361d8c6633086fc4e7676e27b82b06d429375baf1f7211242630f00c9e2662b30ddbe78ecba02aa976ad1e9036230408908cd28b1b62b594ccfb9117f9e450ef605467e290f4eba7cc659eccd225262f9ffa5f1313713f793743decc21e2e3cd0960194b545cdd53d36782747922300978a244e319a6c6d89727be7f6119bcca83ddbf8188658495ea0d1e090db51e8821a456f9c1e93c05c94ff5f2943c71cb687bb2a613ae0524cf2ed3478b5e16e825a4df6bc173e72ebd4dee10f62973780746d87d667b46db12dd7becd1aa1c625d08739eec7fe5ac1aa259bad07272df6327f08ef2fa141751f13e538d722192456d40acc4116717531af7ddedb324a90dd8ce98756bbcf3489445ec89dc2a56ff76e9acd6839977f0052dbc9f28feafc3cbc4ee225aae2369aec5adf1881a4bcddf349e614e33616aea81c97e0823d12b5d471624a709b21f17b406aa532981df1c06a00f315c817b93ca03e6b51f0f87738873c4717095e95cc4390b651671ae7f2cc09330f65231d1bcb0396062a2c990390507cf4e4d916bf62281ea04705d0990539c3be4863e38e4548a6b11b462fcffe572710b9839fa7053790d0fdcbc54ac8897b836768706bc00cec2396a68dd4b6536194d5bcb608215cde515db3ba42c4907b9e7516c51c00a2d6d98b91c40e4540249dc687254bf6f0e261a75ef9066f9ea43f9854010451680fe61ff14cdd2d837e478f3f25097f4cbcd1905831494cf90b01c3565af7e3d295861b05bfd2c4cf50a9615cb2228cfd93a05c5ed43c593260b00d2747a6b63a9e2ba2c61130070f54972aefc1bf1fca6a214a664368ea883c4153b2080c0da033b3e2cf09cf70b79b2ca3604d2768dbc6c25c54d918a2e32ae2b15a733b4f0ecc57ab6b53131ec55effbc7beade097ea9e17748761b4f968e77ba7319feab82d5b5ef23646e40de26a20665c3f9d85370a14bffc2db9ef2fe52f4835e51a7f869f540079e51bbd0b65898cce6ed666cfa12d11799b1b8a26b300be1b1149f009afbbd3690e9cdbd958462b08b25527e5c91e2ced05551874cec301f57b0460c7682ad99d95f975188981188383caad550e17de8f35ec3412101e2368d797eec0fecd967428430dfda277f9beaa5d363ff5122a46168b8de0b4f951d9b065f1cbba637dd52d2192b594355084fa3a9b9a090fe79e7dc890b555ca849b7c88ac83af6d1f73b87e69c141e8d86195424c441a1a5c0158e48abb771bcede8936a1b9113f55898928ca73dc75e5b6c4de406f4114e8708379c7e5d1433d4845b35737dc65e84327b340c01dbf65cacc706ca58117da8029464130c99b5892187898e58abaabb6944eeccccf67dcaf9a8adc6ba52f351fc70df1a9023ffe1b3ac2357f5661dc9e3e2da37226434eb80f8c7cdf0113bda125efbc22740e9593a0b09928efb71520453485365391c7790d931f68712f1e3914c9bf5018a3df1979f3cc1d9bc8ce6c59ac9f0b4d3eeab6392e4bbccf01e75106c9caaf1123582416ccdd87ad50ba24944c8af7d2e663ac666e2f45951d48e3d982908aa887a32d5f2812c2bc7d0819d57f474758bee18c53c5b275fb282c23fc095cf698950df8016f2948992fbf6142aa4e0e30a5e736c515b1f2c02b195790411f09de4a0cc204bdf0245a9a574e4f79a91ecc1bbb69a48b311da91eefe7c39b57535035eed99d825e0d1ab768608aa47f30306158ef34edd4bbbd0a0a9d6cb21f1ca1da8a519fd734a1c7184bce140551f50816a085dcf4bdb4c92ed9c14821e951be0b6ba610ace2bd159bc83184e9e675bea003a97278fc57182a65fa1cc3f8f5fe06c3955051fbe3c1b4c02ddedd0e8db7a17e9ec190dde15709681ecf7b55113616b516a24fdd90a53286c1514d865e7767ea9ba09547fa471ca8c329de319b9454634395fede811145c72226d89cdfc98851bf5925f6e1682b03cb157a9dfe93962463c49e37e794fb4f7bcc8ef15f4b95a08ee504b424bc2b9b0ceb50d95c08549b29ac4433d6b5c0e112e25496bb7526998764d265ed5723569ffdd6ccff3f7b662fa9b12c449562c4f646654842a558c8b1c9f244ecd0083f3d836874363dff34932fa01abab49421c8e40cbdfa9ff52d5cd9887a9395148a55b111d46832f98328dedc0cae8db24360ef8e97fc6d4e5685e8e40ac31a018afd561b36f06b595090ee4caa5595baa0d1525fd0a9a465799925d7749e60163c569702c9ce5c359a1d07b70d603d86cd18217349a14acd53f0bc030edf5511ff984a8cb7855e63d9c51f7ac955363e610eb9758994f4b10b66064c5a456314644d1ddfac620436c9d8700d2c0fe22e6304a1675686a2875b236afed295fd05bb3ac47584e57fee2044db8b3f62dd73c55a56bbef55e04abc70f00cd6141222e712bccd22dfb053db450c6f9b67a0f0e9158ff01f4a955005f899bb7eb8d921c2cf4e03814886d1c239f5a451d9e6a1026540307703dd10a05a24714d7ccd0a4a0c13790c564d83c3d3a2f06a0bc05a1c61445d8a1a40aebac9fce29c3a241ea1d19f175757fa05361f97de1490537c9e0c0fe9bea0ae2b4d014760d083bf1ac8dc300c7b476fe73e89782c02646bb380554fc729de6d7e84b4a0f373b6972129ba59b38b642558fe44e48ec2b07c3def9b782483d8a45dc5ee71ce5f2ce907a836cea49c19e51cfeb0f1b546a60ae3efc45458ade65d02447c74c6ca74061c5324a909fd2b00503aec9ec3ddd84146744b9d29dd9ecd507cdfc617dc91fd1a28648209d385173df6d92f02072f3577ab4e023049b5a0f79506ac6ac893f322888d051f9643dda5e82fdb3a9dea6c69b37008312ff610a0bfdea80026e645207340443fed37069a0fe383b75ed6494fd3d630334568b5209a570fdad7193e1076088607464721c381c121e50a8277fde1fa45252b8eaa0093c45e8714ada412fa188436a26f46c5ab225791057fc6ec5b9223467da3fe1663b6b1a451018306dd342d2124c81e5cd1c74c727bc60407909c9e65297271669c7a921a2cb40321eee41107d80636df7868c993350247387951661232392a971ab5ddef7a26310c617af0981a260c9665c71129c442cf034f627f494a2ca59885a5ce8f1c854b1da0afb25ae9248b1df9a14ff11608dc4a6f8ddcd2d7b39dacc3a35c9f89b08d16ebb6ff7a15520ebbecec07fd074db247d9df3b0af7586012ed8346e8901d0c258cd55cb05ce90f89f0b55cd8593da3d72a350af0e1a9c5d7ac18818e8c2ed516815a67775370220d07270f32a2bab24e5597711b7ece5ade6d223e8c9bd433195c85cfb22324dfc23d98db74abe107938e348182357e25d635b13397c10a5d1a5646bd39f10e13977bd19648d86c980b224741e3950f923c189fa7de5f071c2b675e233a8d975e1ae439254275baf0177cb2fc7d2b56854946a10039a91dc217fa761bb0d32fbdd6454952081adafd2c34b8c868aec6d98f0ce42b92a473d41de0b8e19fe52f03cf4d73e6c2ae367f476aa9db8d11139e4b3bb44588a4c7e72db5ef6579f8014f40bd2d00556a2c42d14c767dee9ae851a918a2ffcc449f4a209afa2d34b900dfc8b5101e846c67d3df2a9094abbdc3ef9f1a9f5da79a06012e37756e68a16cd6da82d916c17a6d00049d7bc40ec8583047a2ff323fc833771588497c7fd1458b07c8b0abc1b68ed2ebd9639d52df43c565be65be3377720525b270e3b0295caf51e1985bcbcc88d0505cb0b7b55b050f2ae1e8db0162582716f9db7a4484d9c768536b3974165a121c546071189caf4eb69b67cfe0cd3b3b1d59107c902e2eb2cdda7b1c7694c673ca206a79f86c1ffca04ba6f33b91ddd00ee174600ee6994a3d302ddbbd15c7828ffe05cf7ccfd91a873b56a46b2501152f803d7e02d9396338acc57809d06ef1d137d82c2557ff5d24764340eff9fd2307f62425a6967429b64d92f334ac221b833ba98697428f2c3e85d309a279bd9e02816cb49946a30fd532a1a0fb89d39ab14a9a894795358324bdeceb41a55dd723daba1de94e2a573870b7b6a8f934adff43734d22b5760b8958863f9b67f1472e94e714fee447cabc676bf034fea6f76208174c2535b4afe9d80204dcaac64909baed85190b56be2dabbe24268ffe8736522454b17ccf33167390d6514980f103d6d6b2a28408e7a1e460f70e572835abcb3e5f421087307f79822ab1db970423c688bc5c514aa0c845d7a880875ac269a7d1a07c190dec94d0adaeebd9b98a1f0388b58ef832e3243a0bf2d16550c3c71c996e75bea5f8213c66b63c2099b40a5e6be7c31ea4943dd66be1b0291b07e00b0863468128b1162f795c5fcce187039276ba1e7a672e4652f683ced72f5ddf271f3b94718bb96e0fe9ab74bb27882521d9cffd656320df03d1f2de945c6b4d1aa98af9c601d1586646fd843228b40ebaabafc00b73f2396c55b101a518a1e9afc8a8ea68a9af963e79ce9941e5d20ee983990f78ffaf203dac89c95e8abae36cdda5de6355f92eff43775335bca410a40d8e2a65586fb089ed470b635cfce7a12ff2f8b3d30799f707f4c755f4762ae8449dc1c2ea29bb8b730ae3ba7bc1f0a4b921d02ddbafe7193af11e58b5dc0ce5b0aba68ef34813d98e93e5e583031b09c03ba7af58d13ad32dc8d01c2745273b080c6255e7ae2d988312fc53fdd72e45a684a444beffdab1ddd37fe6a44d2d86ed6f10435b658fc474e5edc21bb4431d910505eb635e93c7674263c2ec6b1fb34e149f747638a1dcdc050f11dad7b7e8d065aca7506c507402e3377068d604e34eaaf674bfd9fe78ca3abcec0ddcf8f6bdddd63edfb5ac26d3dc71bb55a302eb54b023b184308479aa31779ab38bfe4c079bf64c261927a9ab29024684ef282b9ee273058e423e040347318becef2eb3be691dc31d88a5d4ca81db21351314a73eda94f6c93a9efecf96e5b3b6ff4dc188a0903be1b982d18c64d0cc00a10584ca47a49fbf12c4b47cb84113c978554b15ce45bdaee4248bd39c6604064813a5506d7cc1616735f0cbab341ccb85183f35d85299f22376ec3e462f27ff2922207204f8a3b0d84905b621d1b23396dce3f221802cab32d6f9eab465a923f114c89aeb7e0920c1c6a2d977d1d47925e60fc5d9dda0ae95d65a55b85f79117d0f5157194cf4cf4153465a2159496b100e590e95c49d32f51e269d6deea9211110cf1f23d59f979e136fe641878c5a12ca107ba89cc33a14349bd240b386cd4d0b678476c9b29bd37c1dba6c6f3c0d6c75ebe15d2a0824d717afd4300027b4ac2577ca36f078da8ae3e5a84fc2ef24db140aa937d8ebd806d73ae1fd23085428223722496760f2e56e2fce9c872f3f4d0f01b94a2370b264bddad5635afd70181bbed96e63dd35cffb340dfdeaa33bca7d2d315a7ee4448ed7c4b503f1282d5c038a7eaf34a2615bfc690fe5115879b0e58580c68ad170e7d8bbd686c6796438125cb3371bedb5c2a25ed7f6267303b70f7fc9f61256b40affcd13dcbae955f7207f8f01625ea1f57d1a826ceb00266f81b2e20b858cb83b8d4f60fb8c08154f14f074ecb9e5c37dfdcebebe1512dfb0fb8bdb524eda27dd5c852763ae846fea26575543210da57aeabe876f760921c932a68fe762bbd03d1af766894a1ed86a28da92abde2f9d21ad9471d4a4284ee9fdf80990583ef128320745707bb783d319112c0e40b98c05d59864c555f81ffc838808a57127ec66acbcfd7a2c731f2e0dd9690a47e11ddc33904030cd07b0e3352939885b6262a867de8f9821f0e5f721e58457e22c50272bc506b71b21d3ac2e2f37d8c54b265c26d01ba437a1bc2a07f8401e73dba5feed49c616d8741d05ae99d5a1cd60942089b64ac10a16c108245855b5ae308b2959dce5637cb82ef4c9e79adb804721297abab577dc19b1760206337651b3eb4384cd69f9f3bf18b2d4fa030b030cc6e3080255efb74345542ad4b20297c048fa3a2f70bd883e133a4e872e61519141479b9ffdf24080460519db4fb299366e2208d381dd1259499368682198369a5bde6d6652b8d71a02b310af0409c98816450a84aabaf3ff77b2ef99fc5249a0dff332457c31b66a3fa60370064d536106a22d8204d019b89b1d8b6fef01c337a0f21a62dafef82705da0c6d144be8c7742be9b07f1c3f62b8ec0ace292f09462b8a04dd3b5d070f83752cdfa5a3421ec582635806dbe9f7b4823be70252b199459b582bd30354b7f7d21e2c028f596814988d8192e31d97c6bd00085094c19b1b6102ad40f5e3dc162b139e03a3594640c003fe31235eb52638208faee9019ec21d2eaf1e0806fe0a9b0844255999c8ecbc879487961f7675eb445c026de579a2ab8c64e11af9bdb02b0d649300414155f6bbe2157846e682765ac896378142bf7cc897ad47c6bf922f1cef8aec47001f925029f48ca18e0a67d9564272ebd5c7333265e30d0a59cb940ab65a79efa6fa683f181ac7a041e0281751ed9dbf057fa2e7096b321d9e7f04239c1619ba20a42fcf86ff7bfcb6d5a95d2f2c9fe292eeb82ca7b6aa5b066c902b2f3a182a03e3cd0911d8da65c9ab51f3e3ffd0e3e5420fd33fbd355d82cafa1018f921624106f8f7d730fb60b5c2c376c8aac7cd3be4abdc9bb9abc81e56212b809c432f5e8853cc06d9070e2a711a385d1db13c42aa124db365427d0ae26a2c23bdfdb957e7b43fc759f17cd9b1702b4f6bce659fc70dde9c63978aac49902ab8c18dc483d6da31ace9c12636f2d173c202c50e4d488ee4b8b8af90f8e9a826895f5e63dd0d85cc708cf80940667491fb9dbb106423c5ae2ece55c871d2e42b37c2f0da7018fc93dc69714cdb24c3ea6677f55332868f2fcdc7408d51d949164b1bcf1f55f71aefc1dfffc6420a064081823a25adb3f7b3e6edbf384e21f00931613f859f0e6860156f6d19c40bdfcc8e05ce960555a9491244c19f36c94e02f5f84b0e5d301e5a765aa8376aa85ccfb6da13e611b40335d88fcd2d567aa1", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e940000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000041390e9098d147b0784030810969efcf00000000000000000000000000000000c017075b91d1c9c5aadc2f4d3b29027511521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + "proof_hex": "0x0000000000000000000000000000000000000000000000001cb3e9df822c791200000000000000000000000000000000000000000000000e6e2eb24f99c6e4fe00000000000000000000000000000000000000000000000f0341c256bc7f8f2c0000000000000000000000000000000000000000000000000002945e9bb27e6800000000000000000000000000000000000000000000000ca22d28f7e6b41a5600000000000000000000000000000000000000000000000fa4ce39d3e2c6c64c000000000000000000000000000000000000000000000000c382057b093f299f0000000000000000000000000000000000000000000000000000d2ab1e847eef00000000000000000000000000000000000000000000000732eae124bf33406c00000000000000000000000000000000000000000000000825bba25f971304c000000000000000000000000000000000000000000000000f31aa002ce9a71b020000000000000000000000000000000000000000000000000001c69dee5a348a0000000000000000000000000000000000000000000000082047502ee34e43830000000000000000000000000000000000000000000000098bc3dc090da06bd200000000000000000000000000000000000000000000000a395a0475f5823e140000000000000000000000000000000000000000000000000001d21f325773d10b2b91c7a8b3d057b7bc490148ca03e7c07ae5285593e8de30cc1ac03ac33ea123a54330466872a485fe60682f01940aecf0a16a29400c85c1ff026756e3feb104e48aa44e3c4435c6a81422971f410e652c2a8dbb6dce04531118b73de26b410850267190a569b1e5c0d99d36551817631476dce47dab6ce7ddfa42abee9cd50d8774b5dbe2992589f46d6b0d3c14df64e1f145ecf602d7f07d6bb8b077ad801635d81fb371fc342b18cf95c1280232e65ec5fdf4f7edc0694727528363aded2adf3a83dc8a210463d985cdb56d2f0d39aaeef44f7e2567bec394a51280aaa41d9718c798bf7003a861694b93dc6114eb66daa0cc69e0dbfc33065a442bff1f24da7df7f66206346b9c7f067dc75502807746037df9556453c8cfe619c13bf2002ffe7ada7eaa20f50572c400c4ffc4942970b82e2e42a5569a83f6f7cb7e11032c18d50c12e995d4aec40ac9bef1f9e92449cc1287a6ae05407339c176d21818bac164c691fda117bc7877f2e7bccc8e7d7257e4bb51996272017b55b3dda603b572716b3071143fad60717290ac7522b0944c91980411ff8755372680bdd7192dd2340cf54de8aed55abc38be8e4d864a57bd60f620f2a66f008218a6b5f41d926dc02c0c633628367cb8e460116ca71dbdb7054035eca657119596c3de48187112be8a3281245ce6e500f797cc1432a4d3c09f50d55d3cf2a76c072123062816948f10550ad9cc66240c3475cc3f7e1be8a364b22c852289e341fa94ef710e359bc943797bfdd06360aab35078dcb0f5f34feae065749d4499ce323c0c26276f732dc63c13091a70d853fd6f9d06c67a9219b36ab6cfc590fc247994b9cc26f3325182becff2efcc85ba9a3c8a5d260edc035d0bfc3e0845aabd9d1b893210c0d54eefaab1eaee3b1703468de3bf3b65973d385114a22b661f1b24e99a9a1df7f33c0a098f95b18ac88841857d37fd0d01befbff8eda91f1ee54b9d9bf4d0972e399e72488027be9753aa1dbcf846b5d614dc2e83df5ed880a51ac5672e90569ae383a1ca0dbc467c684df3bc238b9c05156103aa2aef2eb1fdf18fc875926636c6e183c34d39a42e830a4f8894c70c00a993805d0013500ff4cdd8d014620ce10ca69f17a638c92367872e4f56e8aacb16fe54754cf3f56c74e9296ff6823e9d239478326b2dfda0f711567d671b7c617f59f0369ba10265ba61a2debb21dc7411bc598d5fa875f66505485e1674af48502bfb1c69679777cbe6b7a901314fc90eaf3185389206b54c33a72f351e81beb8f7b0d26511d530a8eab1b28e11b330ff20265875c8ac3b81da94e8665e4a60044918614ed85954f5ee5ab759e1512c2e32b793a0d0acbdf5e4e38d9cacbe258b66de1622bb1fb345e98b6173d2a8df716393a447531eaba0be23f1fbded411e71808271a3a82b4f9975c174051569589970a0511743bf47bd75b9f04e2f7a5d4b7deaab3ca11d0dc741998c2a030c6c5458d176c04428bec5a3e0d8e0e5c56a3eee999b10f4e41008d0ee6c500c4d4285ef91799d520036adce78bb4c4d435e0379a267084b998c065e2b5d2c05da4a111ab012aa81a0bf2854c7c58136cb6e2c629474ecd90a5041606bc645022463e9a9e43918859426adcea475191e505ee6a37d3afb0cda9aa1c7c788bb0a88deee1aefc855d078af3172244c5d3f21ddbfc514c0836a3c498dea40a5511c6fb874ae86b70059e90abb007bcef2ef2c247cbc5c6327d1d53a26e8c39123107878df9ad00525610d70a76cb32b6db209f420c58f9fb3b3053cdd884ab03303467453d17b995bb31d2bb667f55e5a6f98af83e14c7d2f71f661cbcb3e29e42855c0566eec6cd80417f89b09a4dad9bbf28e9aeacde3580fe7fbe92cbc8d6e23394556cab576138d117b28865defcf49b06e5b82ccc8137f314a7b3f7c2bec071311be89cf5d785e50c106985d91efc1623bb02487a838a29ba70228de3cd618e92a7a82355306a10a99302db1b6f705e6e6ae73cd5d6e3e7d5787023be31124704e1185bf63c8a0578e04b8fde9c99c2f4f5b96431334d6b4065ff89d8cf905c47b96cd804bb285ce1d70a99679ce64f47a71354031d01399cdcbd0ad1e0e06ce9eca7c849eba54a12dbe233f25e8e7a041c192512e99f964dbe30ae60c9b0d2152494c0e45fe1ee814a0931add16233f30a51e9865d9f39e6c5e956c872b14b51eaa8a1c3607325a9b7daf83937fb73caa1fe79e4791869cdfd75a2a4d662cbb0dd627932ea4a38efd1bbc223e92e269674601bb0042b816acd8f55aadc0186ec668949e62b5cc325bed81f906800ee00b52c5f0c3dc0fc6aada1fd6b5a823e1d846890c9bf6f66d991dd8547b31d71058b9de07eab2192c8afe637c651f161176928f1ada22ee5450992b6410cd20f9c0cb72ed0aa36e7f92d47592c53b0b5e2f62a81ee85e4454543894d2e45ca07656b56cec384333eb8620438e1dbb07d8b15737f5a26091192263237fb3545f2a7c2cb52e27d82fc63dadc2ef6ec41cbf43034f22b18a487f1571b7f49ad6cb43662c1d88884ddfc4c6c636ae714d0ca4003603acb3a8a4ba957d6cfc7cfda372f89a2f389757dbe339dd2171f5351baa09fa66e772ff12885bbf3be23fcce81cb59e6021505417367c2f49821f7823ba2358ab38328713a2efb8139298fbd22899d5669e32ff033d4e760e7d8f9d1b3399208db97688cd9c02c839ca9843236e13c214a2d8598e601cb5697ebc782d31df4ba699e8ee8ae919ab36f57dc5ca0cb0e5ae37e10a6706eb18514783df1e89de03b764cde27719b9536b5e4b9f15842d91513fcff1171e709145b5e5f215721d3f2ec845743d4e522e9f27aa662317ca94c561e19a62a3aab50e1d093f097730e2abd72721923c2513efd225ebd9e4b87073bc3c5b2bde83296d57572211f90af331fdef28dc12ed2ba3e3457e2d30e25d1cd26a66b9948b4682c7ea34143d8dc9ad72dc0c04b2a9828c7fd365f728364267489a5defaad4506e3528fc2313b419a3513eebddc60db948fd08df616379ac05f981d69e9e6ae19d5216b22b9c725c9f56c6c0542f7d4070281c3d16adfa7280d2f7846be2a75c73b99e5e230445761d0b8c4cbea2c0b6f7bad692ced4087f1b72de9ee1385140d3bf59390dfef89575ea75a6c0006f14bc476665d96fbdd305c046b7785bfa3063e49fe20879f97e36487df6604faaac54f65709de50110e7f64b62567aca3d6d1478b222ec9289fc1f61f423f08706ebb039d8a224625cdb422ade922c3c0d15036b3e80328f349c0cef97e0a1260b8eae02768c399d5b0f0f29946fb8a18437fda491114ddfa6945933cdc3521ebc1f70b5a3cffe275b5fa04d6d087e1142dee32f46919f1de02a266f6698e3e1f7326ea2c0e5a0e45dd29d22f412e7d39f0cb8f0fdb140d8a7588a9664f65018b4119f5a9d841f67618f140b5c0b43d85b4765a1b292e1bfa2fe17a6947d95f1abe578060534da85fa9478affe42e5eac1c93ebdf8e050d285507fa80e7e8720eb1212f85fad61f1bfd5781c2f60717013af98afd0b1018f1d3953038b40c827b29ec08ad490c80187c112ad8cb0516affcf68d114715b47322c59b0e1d0f0d7d05d85f30e3c115d84a815a7ca750f54e65380a208d1797c705b503bdb86142e1b3581dffc3bab4936c6bc2ceddb9ba58168ccf34492e26801b14cfa1d6cbc813c6ca494101f98faf1cc503d28cb425effd439e81aa2a35364d8803d7a3099f3ec344b02fed23158c79ae5cc2ad43741d6cfa830fe21448fc263e12436347d0d59429d15f024abb4c992b911607b0e4f3a31d9496830f80f0049b464f3f1d4cb46954e071c61399cc66260135a1c79012993d59aa1b218b5f0f80f334e9274dcca5fad1e7a857adf7674bb638a9d0e1392968058512045719e05e2b0b9924a33e2769d059a9e92001bf1d8ff4a3c467d10c0226515106794a473cdf41066d07fd466d2d26e02adabc066365f55b758e5a7f7de4f11c1c63e88a4dea4ad07b671448ef5648964f5bf16094882acf6039d3d7eb07fd350a575b3c32e6f01c55e761b811e7a2c76e2ea0c2478f11844f709f68a8ba186d0692161774697b1f7fcd284ec73cec608c4d4970b58619e605e6c720bdc0f4a50ba4d96cbb1641bc70af97f43ee092264f76633be11a88b4c693c07633537e850d91d688b0abdfa27ac8224656f773bde1aa49655d53de94684fd4469d5a98251e3a5b80127d39d1cbcd95ac6b9942655b77c697de2a43fa9791a27c1a6b50dd1aeb9197a93cfdee8d8c51cc1603f26f631a7327ad19003afc88c47469cb195b2f2955b16d08495c1b318cb0b58965dfc7f8c37faf153429001e8c9526753fbd07480dd9cf394444fbf38ae59ab4ea32b0f1d98d4f5615abd67e895e03f9fe072ac6572e3170094d4b8ba9279275f46b832a091d5345ead1b14b425157fdaf93112579386dcf605702a30bceb04ccb587b8c9fd67db5a084ebe2c133e1f0de76153de5b3e8e2f0089b682f578cc0c83be4bcb066e2b8d23e41bf850e18ebf504056a78c10aaf829a5613e43adc2d74df5317560b0f92d1bf9c8a7e028dedd9c914ad2fd6e06e8b56e1043312a8ab2562b8a1811e012f39db2a42d53b53e8862002816727324c27a9099190fa3b9d4ebf24614ae352a18aa9853e9e51e8e9fce807c16f4d1ed3fe76046138217835250863c7372ee35d0e44df6ae7f3354a030614efd55b8fc55f11a1e630f4d62a90e541de4057b60f1e340fe299aaec97842419aa5aab5d02cbb119f8667ebd6596282dfaced456b243a1017e636257c00725145c8c1a7d0ced612168ed7785fbf218546cc3773b80d8fe9a2cd899cc67ab2117ff739c3882ac173dcd0997adaa4836f1d565c5fb896fe92d36be4ea354959626ccc45e32ebe2da8450b4c324e9c84e859826d026dc527092ad112e52ca923617a5a64a750f9973cc25777d4e10bf1bc54f366f5820efd2a4d084b14fde269812ed4006701b1da3b123cfea6825b7408aa2fa247b2d4e48e068400a90ef01fe21067a7e5de43254e905a3a430247e8e945fdcf8301f08064fa14fafb084f0b60ff3820f3a15db26cf3b3d472b6c8e568171b75e01a0687857ee3d697359e81d1e7c35fb460bce208eaf60da69cd3970cc07431a6c7b414c3f14335b6ea545082978787372cac6946fa0a5181a8edff08ae4951f0aabe83c4327f9b16fe126542a07056e734d522aa8e3beda35baccfbaf8f8b07d5861f0a93d872676e20d3c1066991d1944305e34c4dfef3dc1880e5df027313a00f04b9ed2f5c98950ce16a0b54648a6fff62bad46d6201674e4f6c21b626f6c16806c354c68dc9b4aa6c9706851f4626499889d5cb3c148f4cbdbbbfeb07e408def8f627609f9fb4b796ea2f6f8f2aa0c153f339c07e47b7f2a875ab7f7ac5cfce654ec42c87600f21d31616b1c342cbffa7ed1b1d4d288a521634153403519363147c9971f04133d89971045398001304fd850beea80c393312d40c8555cb03bc562af7ac3b349b1bf2cd2b92ae6a6792c1d2f64fed85ef4c1a5c6e8dddf8f9a579a54c916c60e82047dd1600e5431125d76b78b8c2a098f18e46f415869bd5dac6f83169c2c8d29f6b4328a0912196820e9d995762575d0f0debeaf9b5b0e2d270c5ce3b76083e36e08401f94afce6ace19ea8fc1fbb1af3f43e109914b7abc6a65fea2ab48a5000af7223958132cb267dbb555c4bc2399e5426b6b3a27105f22c707becfbacb18ad58306fb6fe953b77d8e16475a79f0a1e4722528ed68fea7f1f534c99c8d2db904cc0cda8066d588098e2046d1ea0630ffbfe2011ec234d0c48e8dc6113a19671866253d11e40aefb19efcd13c463084ac484b02f2dedce09c436be72443f4213baa070dd14cf108dff6cb696f02cd05fa5f3e95bae74dd28547273cc1abd77cdbcb00714b116dd81e1e08a4936266aed104058d594954937b52d87ca8ba959a577108d031a69f7febd6c4168783d44fe7287829c8a07371159878bd11fe51cb232f0921f082d176de4cf1ed7bc8b76c7b56de66ec2f752931bb6b6194ea26e0c6f72a98a7e7145a8975a74091cf36fb93f433c969dd565b1bc5cdfcb205cc15c4642f4084b54cee37e360892808ecc8dff740f475bc40d04195bac4a03e9ec76cad1bebe7afd1732cf16794ef6462ab017a80d75ec20e745783bb35803f4b4b617e11526b6e094e5fdbd4f2ac0531a43d1106a93e8052366edba0ecd93ae8e04f282d1333ca58b422ab5234dbc503685b3b1fc93606c1aa5b712b2379e365bf55a51e63f83c7934394efef92012d8cb6800663808ca41d82a5e76c71495a7eebc7629d59d3741b52d96dec1f9741dd750baa5e3e411bdcf0532fd4a0b394a9d911a13b695df6d5ecb4503c9dad0b958664def47dd332b4058bc0e9b1933ac7503c92aca8608d310e70c33a422f0f3f5417380984574f512371b28f66cddfa883c4006b04a9b3c7a3ba0f6804dd6c4df7feee728a5632d04660c6d954179eaeb44542d11b58d31f9ed821464cd0373912dfe4fa22bd19b7065abbc6ceb26a19981842a3965530707b4befb527c0fda0a0cf69e63eda44b13ef3d14863b69dd6978131b11cabd86e10ffed5d750876db45ed410584991599cf4aca734bd4bf51c781127c6ea4ab85b240cb1bd4facec08e873d35dde95121dbdd9d47a8793cb4870df0a1b9a78b9b685580c9dfb20a5e2a15fa3752130f183edcb97c5be401beb6cf107c0adc97caab3584251942f7286cc06fa9f098e3d246e7dbc07d9608b6f5f991a6cac58ef9eb3e90c9f8fe72538df09b2b7b4a577e8b5ad0a2dccc52226746f09b1124b9ecf6a30f133399dccfa765b5d4700dd4b3b9bb71bc2bfead45447c30a868eb5de7f9664fabc6fd95a16fbc657ed53652cb805c3a819bad87ee0eeea07613f74b099d871c110ad32d1351a8aeadf23e6d0e204def4439207b50b2d4202383e3c044f9459cdbe3b7bd460b412b228e7b2f765cc1c5ef19eaf6e417be81389227ca8073c6176027f2d7e0e8421339891238ac8b089c5d1ea4396b43b650d8c183e6314a3c894dd7e6e201332e5c474a08a6047bad68d93909b92aaede71a80a43546806bad9165fd9c426fcbf14a233da388ddbbc7a4b9e6ea3518db232d05a4a41555db710f110c8f3dec06f2efee927471e7c2956b89410d14ceebc5066d005f93104f18c112b2dfec079cb4063f6af28f97c50ff62e6e6c1e5b5d970ab0cd7969c070c5ace23eafb24c3504cd673d1e5dd2fcafe0fb5c5a8116ea8414c1441534aa5ce52008ee910e68da93e170405862dea3ad15760000906447372ffe6c06baccfc669d8c0abbbee6fbc66a530df1c8a15ddecfece85c3e59c99b2f408d6225a32f1c8a5bbcb092d81020c5c943918ca370ee1066b9cda7ac237f25e442ce78c832078a1cca4ed502d30f1d61f4cb9b31fa1c3339535e317b7ad5291c5ab8955c5fa35886e5ef299dee976d839d1659aa0b7798f3a04053fbb3e00a9540ae0e1e203b830e5076fec300443e13d97564007c833faa0afda19fe75d05b04d2cd5243a1c81ea8ad4d236a1cd7a49e82d0fe8260ef7db074dc985bc53273cabfa4034402fd5f0be8f50f61d64063bed010ddf545a2a0819da87aabd2522b9c66a1023ccdd30183e1c297ccf73678f5342d0f6c3fecdb4ea6549f6f9890570d83521bdafb330e97cda239c6c72c27822f493589f2c3a9a7bc45dab9f230dfbb6680e8f3f8d2d51c3d0ac847f7a3110440318e046edefb4d3a5c854747a2d517240483a68a859c5a2517f9d257642c2a95603c14929a57100056111310713bbee85e2eb28d2f56212ec3df83e9bc2d95901789082be5eb67ccfa5d9fb1802b54fb79467cc5b7d9105117cb0be5c3774ca2b71c5c6ebff3be8981994cfad0e12ccee44ba1686ddb45ca9e05c5e827cba306084515b046578f11a9e25b72c089771b551d6cda59fe84c62744869636eaadc43ec56d5050f7fba28c6bafbe32eee5865d3d6d2f7ae5bce8ceeeed068cb1f022728177e68ac18ffac673bad8d2e5ab2e2f406cbc4592907bf333bd5fc49dd9e01370f3c7a9b006cf71557d51827da6014a008f75d47fb0c6eb5f92f4967de88232ad37605c6100984941f681513d9f213a93bed129c2e3e04ade7b03081573e8dee5d8efe325fed0a720c9810144f53c1a953c7076ee5f1762cd845b22f20de501647c107b71a88d034d1383413369152bb657df8a1586b7fcd8ba48363c9305ab33f7f5b244b97287b02a2d924fac5ad4344918be6b863a65249135b38df4166c6d7dae73a36aca560c91ceb203fdfcbbd96b37cdd2a655d2db5fab215991015eb7aa3a6dfef01849794be8f1159c8fb2e0ac69da8979d7646f5aadd53063ec5ab68bd06439c2b9ce469ccbc19acb857ab262170b62d959f7abe12547d52b38a70b048448428f3d56e7c52e029413e913cfdc8db69f7e7d37ebbe49e29c75ce22209ce58b4ff9f1d9fc9fe802af5b6551d3a1756dfe71637f61bcaac595f3aa1989ccde9acd830305f80fbbd2d16fbd3956488af15e5d4fdeae1b5261646dd5fc86d84bb6b45793e77695de20fb8c84900d6f5e8f54a963dcd6d42e073ce04fbe219e2a4af3a8fcf31c952051db25526c091693d249eb293bd4503d1103aecbc69bbe5bb5631483f1b9594442e7fdac9e909c715dcf47095e393e79f8d5b4ab497648032f1629733d97ef3ee1d8878696954e5fa815d5d1ff3ec0ab8ab9723d6837924e59169682902d3db110d7cd26eecc981d01bcb139557c486c744018ca6bd19b5c388a7e077c6e556f706f4ddc0bf4429db4217165962bb635099628fdd49630587a94143c0c89ad8cc0243fa20eb7f688550b80136aba7bb80ef53272fe7d205c34de7b1300c99023a07a06f52b53dbfd06e521156b855efb8221e0f69072c5635a904f63b7d2d011126e6720cf2927e27280d4518b1cacd709c5918b022b40996e1768dd9d3cb128d1c9fd2a5697a15b330812eb172ce016e47f4bbb5635a1a22c618e23281b471510c77e93d57d882bdea72c28849a4c0516c9ee76b492065afcd0c4fd86d90de9c135ec9a551e0387d1f5937023543e78c0f1e915cf6488565eb451ac3b3e20a6602c1673f7387f668a16c81eb509a15ebe643363b15be3a5b325e0221ec1cf0bc3061a2eaefe0bccd271a1291add175632b619454843e7e1bc46e34441587b61e00a40a910d9fb377770ebd08cc804592ea16e64684237cda52ce30381696281719ca3144b11fe63310d8c20cfe793ff404ae5d1bb37990c661091a17c90f9dc81a12f6e7b6ca63ae4bddd5923718d2b57fe6d4f11c443e06d49767c13e458bca27169799b2d6e904950b62d0ab568bd90296e39f8c03b2d90222508dbcaa76112df2ff50a75f281063fc81340a1ed747062fd26481f85b13c2de3a476b3a73fb1b39392a1f122f0c51a5c476d7895586763837029da273e25e111ba071d6dce602a13dd662cd862fc55adf37d20d67d523839c127dfbd1845c858051e193399c2736016c9c2ff55d2c1eec1456fc6d05a71007b69fa06ffaeff35cbec075bc102dd0e482b0b3e99e947f8c7f8531842fa58ce3f6f7111c6e52d0d54a16365fe52e15426bf9adf5031e27dac309386109c90e1e4780b9d03e42a2f450e2018f902c6fac8c3a202c1a526fe1c0c02ee28473be3fa4bb274cdb80f39cb0f8c958f5128a6d2cbcaf93586738ddf271c23bec5621fb147d08c561a89ae924383c048b086408035394acbc323bda1909c591844a75b4ac76486651352f0fc28cbb946a0d675cdb218ea5d78641584ec8447db6ec0d870e92199d0711e092c7d5fb7a1b0d5b8659b2f4f09d665914906900169696e026127ec615dfc7f72172d4e9c9950e40b402f05affb26f8e17d638becc4998d96780e0dc21262cd79bb5dcac57f00f9fe8c5541078e8a05994bfae65586c513d8e93142fd0f38e02dd1ac04378db089cd4f5d445f6c772cc79813e2803f220cbfa3429929f5498ce561493460b3613ffee4ec8aabaefd4e078e34431c3623277623cae710755721a6889d28dcb682fe59bd2a912c3a0ea267afa0bcbc29decc7598452e8427ec96bd4e4ff7dad93279bf4d91fec451d7e038f87a15f6a8a4b39f7836384879e5a558ac7f12a5fab2c732e618b33458c70a287b9c392bf96e15d064acdb6c85b4ffb27a8d6c08f9403bd525b914f19ccc4e0752c1ba7303f3ba9a414e6e620de995481fd369295cc085ee0d4086cc322511612aeeba8435785e0d747f6ef41113e99f02daeb6b9b016e04769c7707b166fc721ba312ac44869e72db339d8d94f12280ef730872d27180307091a47a51ff40387adfff6cfe5ff23ca72a089110af0f3877e2c35c0111199a2422a0bb1b19e7ceefe12c2683fb846c5be7a1c6f60aaf4ecd576c6b2c62cb5a0bff53fd46873a6782deb555edc23bbc11bdbba75dd1be6a1ad6cf829f70072d47f9170994d097f05cf07012dc2b0b4ec8dd4d9c779b858504a67c6833c22e7d6ac42fd50951f66bf81ae3a02f2f806dc1b77d076b5890bc4f44044ee2f2eafb480f71d11792aa60ad86e548a74684fd1aba10723a92b11ce1a7dd6a5561fe49c7c07442f6276a9508e3f805c833fc367856062c57f21f63a55942ee8fc2e9471647d1417f060bd8d0c66a27d1c1f3901339d4697ff39e6480609fd878b176f95e7a6596bc761cfc02a612cb5807b33e7803fc5b1d79bc27eccc7d3f035177b4134650ab8951b1539c16d7ff813346f0ba46042ab279344cbe1bfb4ac912b800fca5c41a1e01e2e48367606d18a6ac9ac259eab77260b92273b43a0faad0559d05bf0d90c3d1f7b74a54cdc688f619dd581aa440f8417492d917847623a18ef5410b04c88ec70cd12b19d1b4d506a86d8b42ef83bba4b3e5af3c49f3e8026e2eb7d9fec7687354ac5b0887116157de802696db73422719c1ea40e944c3229d5159ce10079adcf9ceb5ff67124799050f8ec9e742b8bb1ab15d470e72d890af040425dc1bf62303ae3a3e7a96d2a43c893e80ffb8a28f4111b111aeb361103ee2e7d8024fd4e508efe2e2c0bea5a56827b8726cacc7dc08fa96011013de61510a2b1b058e3f643c735507536f749772c7baf6cdf30eacb40473ca90c745323467fd21411c1ff380b74345d7e144ad0bd7fa7d7c1fc7251fd916348bd38e0169732cd4a5c892c1ea29cff802ca57db4593be104a408399a46f970c0d6f84325a21b7cf69ee061ccc2a3a50316c840af78877ba416a90b831f59bced3bc2f42a89d5a9b0aa377090344502b3507058d31582b403b5817becf3abf3063e40611dbca2846cc99bc087b5f2324908a49e5e08d09fded93fc0fc1a98e7dc5279ae235dfb49b929674403a5e7215847f2c4263f4a937f45683395abd3fda138a1e8041e083ec5724df8f5f59ee81ab3d8f91243b4748ecb153908f8ceddc7b2a6132bc19e5dbc68d1159619cab24990dde0069d6923ec90219fbc634f0ffdaebd6e2dec866563e71fb0b99b341a3225d9941ed6f84bfdaf764bcf58829b65008f8f1ec19284d1e0d08e4fe2bb65238804b564e62228659033401f5137bffa7748ef26ee4d3cb886995198691f9bd7f6821dafc091f004ead76deff2afa175022fad1d70f5f312bb20b8c34c9b38335493d62414b52515b4c70bb5fd41efeb8bfe54027b2630d780f4d4b4c5f6ec6e38d9ac6e64b0771cf910a02666224d7c9930a400d381db895e295584fd6dd968103a6d4c18e438f5d3cedc0b55b1cccb54a12313c43815f9ee10284257ad86a0ecaa993455d03fe2c4d54daf8197032b9baa401614ced4399d79bb7beef9ce5bcd8932ae1cf525f565402887c83aa92456761d0abd91781abfb5469a2640f41acbf4ce293417771fb35f5ffd261422ea27deb808b8f1e7f676bcb61968d1cf3fc00f94fce26a573eacfc8c1638da80d4eddde126a10c89af520a37d898f88198aafd4d70804ebe05448ad486113edb2b2aebaa18e964f683cbcc9090e294d7a7437be1ca1ea74dcb36d84a2321bf301db8193e2b94705f8aebc28168b8a51c4ca04dcfbac794fab48303ff71240442ef421e250fa2c370d0ace9c55ea508ca6166436d03d3df8315970bbd8bcd844f4bc68f5b06299dab59818f88f7e301b5c4316aeb59aad5d5f7097eb27b86c0cd57fa81c703af3bf34b8c520cc3d2c5a52747788eb8199250fe53a397e6974413b277b9c70a27b0ac6f69139f75109d096a4f17c88c4e93bb3f9ecbe10c1dabf88f8fa6981df485dcd33951e7e060d99193bb414647953005e6a41a281ae92fc0b106ab1d0c649df7df67a84a19fbe57e78f9cd919f876ceb2600cae60afb4d422a6daeff0f3e8b0010a88b0bbbc353e403115f4fd89be0290c657993caed524b65b9e71e1354e3d251eafbfdfad286e16e963cc476f575b88f39d75d4aba1c508b0202150a22b7c9062f93aabc32cd5ee70b08a94da92dd9c39e7d49fef5343e8730231d1cf3ebfeb1b25dbcfb218b5dde3966565d22eac32cc996110a53c0dc12a9721325df2ce3db03f36341907cd84122d2a897bbd9e35d2c5e2827a496bcc38d982c2bcb25cf055472bf29acc26c66698133800299b78ce48a634e6323bae6db88f7211ad69ebad87c01ba4d11ef6b763ba53395eeaf888db5c71163b7d73cd536771ec8d7e9f205f0bdeac8aecfc6ab04715f93f780868392380ff39e7999c6270e0d3997a8e3c12650b84b34333bb27e91818848e7a01a41dc8f603c55fec0e05524fbd9701f9695c6f1a89a4fcdd01babf46de6fa4f6a1abe626ae1e3fb09ad630ba5ddcea5c34b3d7db938ce41fe53f504783b69bacf58a3153bf960af8e7b92248d686e4891e170074d3b614db88a20b1a58e021253a639e04e495a18eeb9502446d5a60190f1b37f202ec7e975678e6a91bdf5f4bb15cca09c259f6d3ad63214fb72a1b7f6880ae472304e65f140fbfca829ca4ebdea50884189a0e34ec4f80d70eedb8edd7539e615f29015d0a2a3533bddccb3ac3319709615a7747ddf91059fa99e04f1fd1d6961ca462b4ae617c861a104f13758d93a106de1f3ef546d2a731e0019756656b099fc3d53d47386b271558ce7f557490f78816a160b9642303f16a6c56c5783ffc1f2bccf9b1c2dd9095fe89596add52b8cd0f27e17e0870deef8f8bb27bb30aeee2710f187ce6177eff64cfb0a0830d2b2c4424784eafc0f2277e643aa11411f9562c2484149e3ad02c0550338fbbd690aeb6eb7f458981086439d92601f693280eb07e9b8d53730775f628b1eeb9ec29e40e1e83b45a015ec2c7f6a2bea0d0a76c6cd0d4eaa26f5b9d8b15f7ae79a36f878ec6899ac75163090de4856d27388b729d8cb911f2dbaac4a61755208607e4d59a2191a1e8a0f92b395ef84f5cca72650a5ec96711783064e0b3038131aedf028cdd0de22210c7e41132f4dc554c726402bd9867726f671be49f37317ee6760d69c5617b6240f2a0cd8114083dc0a60525f9b94e16a3c61893e7b4cd4f91f030662ac3f32b00e35a3e8598f3df274610f9957ea5be083acd8f25279cda1f89f8d5c05a0a1b202eda0b2b2846b1899e37ea945922422c83846190fed65f9e24351f0569ff47d29f0fa3f89529e14f881940aa1a4841ad144c7c7696ea6ccfc4ea4bb7212653c04f248c459caee04c2e37d3e7dc683dab19dcf65ea2b26c5d7cca848625d7efd0dabbb2426915687ef3abc3b069ec8307c050d038c74ab190645aa0dccb1421c04b258ff3ed12c8db75b58a14309d2e26d73d52eedbfb78a0776ece8ff981a7b04b066d7fd4c3379ef0de749d0f0d4cb2fa9ef181a6337578033e287cd292a492a7d9fee18d21a08545b65da4aa812835303c5d899687963bd441be43ccc703e23483ab9796681a8df92a34ab2b18d397028867323f31f7049deb704611ac9cb12fee16283124c9b2428cf4c007af6be15d7b5031a38fca9de3b791871cc113911378a4ab56d049babdbfe707e94528f5a3767e39df784cef4dd84c2f1fbf6ad21654e556af2b701f4a90b0250c325e831293f5b184cc33dd9a4ad1eb0593269089f225f0ebedf94b11fb6f7be7d859e0cfc3eb14313bfea9686703262927b4b146bd2aac0450d1a61d6759af2ebbd40d8eb989f86399f60a5667de00a28fded06200c29f6eebc1eb9e47664d46dafc3afc47d3a4ea9508c16cc7c0a80dde88a02e592616956b9a090e0d519584e21e33221560cedb330523ef2b7c0ff50acfd146fd4d838c633a57c87c264b49a494c28e364f8ef5c6f3fd011cd6b32a0c2c02d60abc970afe83e55897b2581afb4dc367930ac9fffa440dab2ec982dd410b9287a000b0612d4b154560cd9b564e94c8e2aae0bbccc7de765a416650df2174519caefb7804b23bf526429bb01c70f3480caa10501782733990703005f297ee2051ebb04e3c1df02fa1209f95314f1236c8aa37a21641a3bda5f949d8292a718024bad13583e6c37df502842df6e1ce87aaaaf5e5fd5ea2bbbe56be9111b95f20882e244c3d5cbe120a620326c3cf2852b2233aef66bbc6d20a3bed7394c4f49207a510fd873763e5f933d6ec9552bc5956c7c16e579968778d059dc5d3b95f6009e8c3ed9c8afeb8ee33bc36a209435fca7353b43d7ce49d0b7da67d8c3384a", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000adf8670373d6f0424af5f29cb946f164000000000000000000000000000000008f927aa40772310f80a858405907bb7011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" }, "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000edce069b20cdd3b3c0000000000000000000000000000000000000000000000035587edaec19bc9d0000000000000000000000000000000000000000000000004e8b61a1848c5d71d0000000000000000000000000000000000000000000000000000ec07f134570a00000000000000000000000000000000000000000000000285b9d961e7514ba30000000000000000000000000000000000000000000000037ced53ee4c6f829d000000000000000000000000000000000000000000000003b4542de3f9235c48000000000000000000000000000000000000000000000000000295d724d1e6c40000000000000000000000000000000000000000000000024984d76621f77c0e000000000000000000000000000000000000000000000008d9170675a02d3baf00000000000000000000000000000000000000000000000a55ee4aa4908eca680000000000000000000000000000000000000000000000000000178c72a0427f00000000000000000000000000000000000000000000000c780a0719338d9b0c00000000000000000000000000000000000000000000000287dc8993e1ea3e03000000000000000000000000000000000000000000000009b918c594689408490000000000000000000000000000000000000000000000000000e6a0f127f38b1c0664c1e064d80eb94bbb91785f1d24ed1d7be9507613a3cf0d4f6bf2e9d7231d1ec3f000b4ae52b58619010189eef29373ab9262aa91aa7143502034d8863011d3224f2e2edd4d00c173d14a0563b3055120bd423e5ba160b9b85989830c122c3d4e983a824d1a6d29966c7111df5c9199915567cd4aba98c44fd0ae834f9a277a09cbb2913101f449f75e3676615906ce01379d63002576ee7da737262b32236564d38f5e7a803c1f8d9ba4618283f2321568857b28f7c1f0a958192e02761c28d6c7d667f72a60c1b47882ac5bd519c2f272339121ba07a2660621dfe03922117962f26bcb850f7c4d6c290c1394ef955cb7d8158b965a8f73ebe55715b60a5ad26fc88ba140f26b746000167b877d731647f2bad22f5e22aa8662107b3903ba9d871d08782e78385c33c1e9322705d793ce23c873db9d99711f08a2fd4419bbd315d1e4fcdf4ecbaa1f63ced3d1a9e6d8224596b8db1f5bb494a2016bde28d00c42c42b88f68c6ad1f771011f95bc6cde5ad55f886bf7594359a1faeec71a2546cb3ae645c2ceba21aaac656c9691eff73811ab3f7599e839f35ecacf0d13122d59cddefd6b314e97ec4518cd154f343b7a3e4a803e21aadfa71512f8500e046fc1a29c86ac30ba80b92d6c88a8ff016611d89caf17b20af9ea60288bc707548bd820b583f244b94937314f51020158fbce2e9118933f203594ad959eed07b4b35a6ec429eaf7613eafa54fb3dcbec9ee7bedf0b142fa863ff3ab979f532a51f208c9b1df2dbc7b483ed886bfa98e9725ea544775c7bb9d667028f4b19627f5ca4f842d8e717dd35f718dc7aaf4ff57825a57ce340181d5bd8e6d2147ff2b1b1daccdc7efe44863337dafc46a94c0c29b71c850f8af09d96d588b92035d04470ad5f8011691979683753d0aba181757afe9aadd77cd8044e703fa50efdc03a037ec4c811132711ffa1d32f74ab4c4d105fce937a1e674aeed5311039f6c1b82ac81638d61248c4974ac1a4204cbefe6558773523e38183701596fc70db51e4ef18838b97f26696532758504247d6204e1bb16d1d4a6f7690e22b92ec22b1e7216c99504649f548ea5e76058166e6ab62d8b597da944b3f251e0a7f5dd550cd43a77afcf6fdc66df86ae1f2a0bcbf217be0e001d5db52d8b348ee751b0191305dc8b39e1a34a8f2b255f13e90c5f4175cb2e3863169b1e6b74eddd072b9f21a8bc5ba5c3f55267fc4c73f55b05016a0db089eda8e476039e4b47da5f9dfe2b79a3acb1133d7b29f45ff225d0c4272bdb5904c6558a7731288c110a549d491d428dfbb0b13b94fe735c89379880bbee36f28f8069de31e430ff99288c66fe0c006bb22bf7b4a9592cd03dffe27a9ed159b0659b109004e5178e4bdef83b282a5e323d882ab264f227bd6470279199a135bd78b51de8c6f36652f1ace3c0990bab78767cbb925396a35508bdbb408df3194860a19d252babb16e91402e2ed12eba5dff99e35eba65c89111f2f8ecb8ab001a0c4d0025d5d2c275efa03b181202fc7f806b44988c8fe287a384ea1afd59ac50a70e7d0d47d94be1897ec5e9e203ac5d9e3f5f1f7cf379e828e01f76efdcccb0614b23beddfa5f6f8cbe09e9e41c6bd67b5e867148d005eb0eb83a20c69edae66cb10f0399f049cc4dfc577fe92f3160e22735ba35b84465fffccdec15f911306df24c11ee96201e4b04d2e1490d1ea90f45ed5669b39f607652462fd9f10ff45449ba0a3d69318d075e365393000429a8ffe54041f667d458a24a950cd629a1f25c88e500b915ef8b8191ae931e7bf88306e191275053149520e06fc4c5ec65452d0f231b2a840a1c2e47c4d6274c2fb4eb7d2ce13cdd3eef4bbaaf730576192979cf71566a194c11723e7cf52a8f8c03407906136dfb54c7618450df427bfe58533534b6f607df93d4e525510fb61f91c34085310910d69d63416b1144cad84e2db53f079311f0c7d2cc880222cd9094f071155155d56e58f28055a5403eada3ea64bd00a15222216f54a58a11d88c720803f206e5ae51935e5afd64b155c4fdb32ad6acf0efc7a2fff6c6dc29aec8db1d185dec27709075fd7366dddb28a9909f20b637e709d5ac48b066d2056728aa6ea0b695a094e8b077280be353b709c208c531be2174262ee74a6ddd16cd375703f431f50f42d497ebfaafb2204fbe31c1db00f847c205a9e5bc54db1f42362fb4a9b97d732b7b2a543c772b785fb139c372ebd00127ff15437a653f0397d45a567e5c6000a4b9ac2812b07511de3a692e5b94e5118dc68dd31ee21507c2f4c9c52f9c54b45ebd3c2918942e1498162908e85bd8cea61ee40e823bc801a3a24b6721333c061e10ebcfd63353a8651b95377d721138d170246bf515650ded72aa95d8673e5c8e1440ad005d31d9f75f78a8402b38522bd14a172eb6171201a44177bd3a939cece9958e1b278c01d40beb9d5b46360c4f07a43b49852420a12fde2f03060a123ad784f488d0b4872e94d10feba6c2fd3959e9a9b1b56210cf7b3b52aa00a90692846a05675ac063d0e933c1bf8f8bb8a99cf90967576308d1f7402a634a8031346f58838220918ebf2a36611413ba04bbee53f23595c2011fec50e061c919f78a127db735b3c640ae6c8f1ea70d4b8216595f35da58041939c77c2d3b9526ddb4971f0b4442675f2e645daaa4cbc3441c424463efa5d42a6350ca221811927c5c08451ea4a05499a29d69b219f2c993d451a437f43d8516e679a796ae6143e42a26668b048b4ce5c0debd5fc4eb4e934d2e6ba7b7b5b31084748288df95597c2f838cd23851420c57562eece196a9cf56f605cd2cc5802ebfbc1e5f4d970648ca521f5dc09194cb6b31cd11e5dc65a808aae5a72b0b8d06f3ed69825af86f15664eaf3d8cf9b9f44edbc0cffde603a1649a04381d008c1586681d08744417cf896484566b7c9f40d9197cc46472eea063bd1763135fc824fd8ff1e9cd4bd25ac764edfa4127ae79f3b4b3236c7e149b0409e4244575bb064648188294df57511c5e320a52988c7a98e71de98924c63cc4df72c10f76770770552f6688be1849aa0a91cca874e842ea9a0d080d3d6eadad68206fbe2ada0d07de35b059ad5e3f3944dcb6a650ee64e230a53f4f04a4e9ec9e3330f9312e182d90545db4f65973f636cbe00905c00d1cf4c173052b4f1deaad0f09239cc80e6477cb573bb32c25ddce44a1b5997eef3b1489a520c3e714f2bd84b33964b020a23f0a9ca584020b90dc27764852b6b9fa3551aab838b649b78767cbf40a1b1b928595c0ff6d5b9a2c8dfbd94eeab852563fe2554b29e55c8966aab39ba2cf11c5e2a750468bd5b34956ea7e85d226cf5f99f6c7d0fec89924bedc43b020120e17513393289e3cf9a0f0a86447b80b25275c4e543758bdfe998290522ae8421a90eba5171c4ce624b6466846d2ae316de1ff93039df1cbfaea396d150477dd040cb85d01066dd0015f3151bf7e2ef022c1be954af6bf1032312afcac83ccab302363d2e381c358a1bddf79f73bbe462b5df71897535eb55e73bfd38ca44f510357a687f0b64fc814ab4f3a84c2a71894b89bd2089f27aa805757662a9c5006200d757d3341fdae52baa65b943829ecf02888b4401bf065989bdd8a2cb71ad31796f5df7bb522beccab8a547cfe2cdbdce2fc7413233b72ced05ce662f88af9123ac93aae51fd8c1e8025da7bc2345b3b110b5e59d2ae2ffc729d0550db5fc60329666ce8fb0d6356568f1ef2618ac7f7e4bf2a6e3ae35b7588780b29e198d52b298c4ed6a93551745f03889c5752475272e91f87e836ea54caa93cddbcfc8613cd6384d28dd49ec99b2232309b798fa694072d9772542aab79584eb091633d202572c6c995c7268e871d4f8840d366099e7c124c6f0f37a91b629079c7835500d621755dbf91a3c126649d7ccaa9c979b09aad02b315869a542dfb4b7366831c2e3955c00d7fbe2c15682d53ac21ce310fea224a7c8d972cad6c4b1415d0a91278e6b67b2a051654d60eacacfe4f32e698bcf8bd7ac192bb696d9e13994133116a66f3ab150806fb12287e271bd614aeb9048dac568c1cff2c1fbe79ade43f133b89887f6f2c8ec436d5b37eea62acd2eaf5508a77662fa8bc2bbf5c39dcb225f3bd14c21670a9294633249d98d602eae71803a11c9bc9277e1f3ae18b2f3919f778be7ca5b0d88337df43757f52b0a273d554fab9f5649c3c9e3deb5f0ed82e597504378f93584af394922da247f0631ec17835c50217ae6f476c04220a57054cdd47a98bacfc724f2ab5d7c7825ce80f5978e502f847663637b90af2d961188d67c326e5be62c40cad44d5d128f5b18fec898bb36bc426c93f982f6d470d17bdb5f79943b681d033607bdbe16be4da1a1dd8fe1e61ac11a1f2b4cea2a3fe26d2e66df0f886c561d05372a059845d520c1f729b37c5ca4093b912fd806d9e15d647dee4fa61a39a465c97ce7cb5a3793b99986163121f8545744c3ad753f82ffe37a90ced4f23815bda2c9278462546ca5ae118e198c4097d3e7dc28e03be11f8ba469496e5bd7d9b6bf4a8b5d56b480e205e03e82cce8b21f464a4dbb6d52374e84c1acdfb69fa9e531159ff2f6f261fab2e661214c69bb7d93f5f0101ec12e85651e59e0436e3af84b244b29464d5237e0de9f96b68da0e0e756b013792218cae5ba282e49b29b371bb74fb66dd8019240639ca2424c3e739ca210a056506c0631528a20cb89f1c2a8ecc7dea173b6ded9a7ccfc9abe627af2d7ea6591a151c1673a09d84f1132062ddbee11993003c72dd6f4de585c418319fd64fb87e18f994b3c90618cd566283a36193487235f5e20b3eab9b15e3e842246b3df0720ff304ddad5e0d9b443938d38b7c6c8ce9341d9ae8071016a21fc694f170002d163f7f12d068f82cc36e51ff8eac359d86fea4f76ce490557e9c8d628cd2ec7311d270d3b58ff7ba7d52f5d7d3c8903dba14149a80ab7c9ed09988a5ea0b82620fc56bd2241ec60e84caa696384f1094b28877aa06e1061600b4e581ab5bfa85176412243451efbb1dae13d6e78968d3476151211492d8d6bea0a32a28eb70c814fcec1e1a77683784680cccec1bc494e96aae146a4e663a8dee882caf2895d80be4fde7baa3a49d24c53c0bc5f54b6367c26837215974a6cfa2ee738edf84b21115cf41b00618506749df9221f329a01dea694776bf3abaa9fbb352f9454ba01fe2a5c04286be6419fdd0afb3d051ad92a46178d89410fd158b32d3625afacd0232156648d27127995b308f527364b3162e0eac7db4e1c9d7222b263d2093071643a985a9493381f9224fe080f7c49e0c1f178b1cf486bdf027b5677f67194f17990a030cd95227782c3ce2d6184ad12c5988823233924a13ec460a43db62a518a59ccd5f1069fc98131ca373c6b618fa7792591edb22dd0c242b5447019f57229b7191aabb525535024a14782f1ff968b3a1e8be97503ae7cefb6aa8fd3a352f3db9f3b9a9e6ce148345848e3988651f5472abfe980dbb8743ac95f00720131054d873d7ef9d60eb7bdd6af577d252f486808d1e8449abc1d68b69f4eb56811a8ff281cdaac4ef304f4582285d5e403c5367a43eb7027b36be9351bfec94cb0b607e652ba04957a4c0fc0482cbaffaf8932b90d94796e989278b0d100b92f826b213ac5b4b3b8f3028b80f591d0274f9a65fb57e385ecf47e33ecafcc031ae2e8c65f2a0175b4ec3b0ebdd832f955e11178b2591b7cb64f3cef1263b56b4bb2a77690f4fada83c47188bbb06ad5c506a2bbe442b763efc4fe3ea2fbc21e7e12fd3c530be1d11cd7e2272464c70d36f6a87717d45c02039d089cec0e1e812bc0f0f466212d8f19f9582712cc3d7f4de6c659abd9ec8682cf25bd9c827349d1f2f9b83f6082eb0d1f5f99f966f8411cce43872f5299bdab07880b3cfa693c9da160c1460f44d22314988c3a6dbe5127978d96a9dc6cdb3e523ff3a7dc68192cc133676d1aeecfb4209c6cf678cf1dade494eb15819b2171f776abba11bb5ec951a46ef47e4bfa4cec4e833fcac0a9fcbf9ce8fa3dbba049ee45162512a1d8eff00670e517adc74f7cd0627f765323e0115bf5d66b6302c28b66300112c7809a720ea9b409f976ebf64fc48d0654945028900fed3563e17258bb0ed64d410660a2819a435f89469d02c009c5d5c810725651ad230e22f758768ebad58dd9559b527cbeb59f0d6c6bcfe965596dea8e090fdbd70e9f18d5d6199cad88165f329d612b8518edc1bc88f0431bb6999172a8802c4ed1c417dd26aca97ec6358938353292e3ede19a0fd0e699fc08f072829424760b71457eadb10eb25e52987144f92080e4c20439d7bf3fc26bd9f95d7c87dc30e6f6f5ec033a751f2006d050c75961da6b26abbed3ba81398b3a374371dfa96a1b6ed1af5b9794c5789d0b9a912181e090050f3ef76dbd74eb1b18101e748671915502be94c168d34a44382caaf3816c14e6456dd88c14d8e68e2a0d3624e29cc00d6d0086d6fcac00011208602d21276b44c40d08f7edb5f2dedbd3e7a955ee1f92f0806854cad1ff3bf05cf91a3103b631f8c0563591c5d1e70e97e16b1c7d05b505ab1643e49146500cb77eee01d79a14852e3fa0beafda3aad1e41941c3e2c9dc4c4630f1218656d719506dad0a2a35a616a505d4ce99fe5e476f59413c41b02fc17c1c6dd0e2a06fd16c203e0d43613f60be6b6ef8389331dd4fedfc337d72277d459c4f6a5f6f5923716f2a1c23488142dc51a76fe1cadb7a3a1989ad7e79d1e63b75e8952f85ececd4851f22098299a53a0608b5eefe88e783328a37526cf50b972c25f782a2e4b8c0832c302ce119c943a5659209e9395d43a1d411fda6b050c14e7e1ddcfc24df068e781c194e17b295ba95cfd03df3c17d0ee6a0e36cc8f432b6c339d1d81bd80b621804194513ee0d0de54e0c0d2eb0bc3900cdbaaa7f49e6dd212d386a15507ef3f8200f7c2a499aaba3d59ebee961ebb27ef96f5c1d8b11b46d6fab6162a3f22f8315578c760b7d247b7c7aca1925eb17442fbd0010842c7faebee5e0d3df7cbd6901dd2c01f4ef6e0234a6b5ee31e14e71a44f652f51db365417e717efd7cf0ba920e728f72b1246ba4a66757bc3d6986178a01c5818dd0a1a4fc7ecb03465903b103629d6df9333ca804018e7d7422aad2a87673bd59d3b53e429b9abb82559921620f508d103296b6915ce66e75611f67f8500111ed1ad5139744aa1f4828c3d07f2bfe7999bd6a3eb38147a887de506421fedac4bdc00ec8138f985fb7d034a01300c4b88c4adc6eff96c0c86ae338b902caf73936434123b14e06525d0ccd12ab7cf13b2e53f7590b841b8fe4aae1252fd75c40e2069390e5ffbb7ab3674a028d85230eff2bf8759207cc777ebb790986beb140f46d20e9bc7f4ba078dd1cc05d91b86bb252b812317d54453271888c960dbea5ca6d90195f0dfbe8e9c5d9203fb3c294d6b473b0a16ff79ffdb3ec24daf6d59dbef2fca6013dc937fd103572c818726e3561594626eaa1f45db3f64a8964104a9272ab81af3a1c6bb478adc1020f11aa1bd01ee31f58f4ebb848520510dfbc05408cc2787e367ef0c31e41328e0accdb8de5dc22799b8805f173931694094f7c0e5de52dcd41d815d5b6c7c2daa0496def1b376e3a51d49025d9f4041715a2b593d7423d0248819a54246e5275cffd211d8ef63b85f2b0958c61c32d58abbd65ef70ec68ce84be78310d6ef264bb4a81ad1622c4fb0d54040f90d90786174ed1e353935ee3f169a98aa2d58029207afc41769c02bd3deca0c5403a93efac655b6a56ac4d4838dd6a08b1e1a1410888fe7e934796fbcc11d77c3b0c2edb80437a769787966939c9cd55a364f3061778bcbe570cdf79ee300718671ba9baad1c30888faafd754749d691e71801b9821a796991690747083227e61d17ef7a39b511cd5b007568a1758a9395fd016df8a16e4e56ec6379bf190011628638fc021a696e3bd9603d9f3fd7e1e2c571a4b5945b7cf7dd86b8981ef6b97b7bafc2b22a55bd6368091f03a645ef0df97279acee5fa55dee3ab0191d1633027c14609a74332b0eadd3d6f3ed85a7956680f2f1c5a7c9445d8390821c05fcc9aadabee41ade1fa32e9421313381aefc7b3170feae96e81634e042eedc9d30d9e1d523a550d682760ddc428c98fef51b1e221048fc6822f1c3e89a26903106cb0cafaa69eeefdc7ef3305925ffeceac96e121206290ce9d91c8ff16641033910e84b4bd4bc1ee56db43b62ba86dfded8ca113d00114389bbca8cae5cef3c14f725ad380e7d712f7d93a1e588647cfdbdc5402c7d4a698eb18c2bdfede827d90ffacdea8cc298a5629b28514bf731b7998f82e19b16c332526bc1b3713c5b41b16489bb6228f176ee6f96cc286665fc5db930036af42600c67bbf26009805dcbf054487b9b0e1c2cd7173f38be7459892ec90652b26f72abad8dfd40edde3511e0898abf30e957b7e70b725b2653cec4234d180fead76b8ae6be8b7e18ed0f6a6922f146b264ed870aec50f2bbf7e4d178b81fc952955a05a27f760b0adfb14fd86c9e21c4b6ab0074e6df091e08653b89d324e1e066a0f2825afb9be08e90f11ab66a57602c43d45ab5f7d97ef60a8670a61fdb46b341552a18fad736bed3ae77890a408470e7c308696f37ad26289f8a7f00cb96b31fcc1d4b0cf0f058943e8249b5723f031c2a02d659c2a761fa0d05d002781fac13ceb35217f82bc90dcc84665530ef2ef7e326470ca0fae8f6771a0b181d7f987290f7946bad4dd103156d149d2f4d7fd9350246dd54b274f5a9c2ef256421db001f48a5a3b95deaa6967db143533599dda5154770528c1c4a01f2bc246ec9f2caa1539470292b344cf2ad33e67a96fdd2e7d07450df1ea9e5d36bd510b5a2b1d115be0f0700e1a5652fe4b304d99ed587e6ff6955cd02469a5fecf829b17341a8f46cb83222faacbd1f539b89a186ded5664e49c7deace0b5233b10275cdc7135f9be5e3c62a640ca27b22b92456b9e6f146bd6e805cb071d32b2dd00449a613510837c67588fa39fe0e49fbf31b99b5144c9476150973ac9f0a4591d17e76456b59e88170401c1d4a92b94f17ec904a2374dee427b7664a6b2c22e240e1ca86974fd9e5e172d8647abea27a002645a90db6bcbc5e88dff24f1743323be66dd4df719696f2bd75926dcda82d3accd3c865cf42853941ef41de95af32c261b646deadb7d82ddb53574514e261a14aa12cf773f483d2545dc4a45105e23de104c8ca2eaa8d94c42f4ec7c0e11279a62ebba987e6c329eb49f2037b8092352ce3c21318796a17ce472d8ffde018425fc0b4c11f3e492475d2afe0c9ae00b0cdb4f51df51cc833ddcaa3aa78b74df3d936893f0a5714b7810fabdb17c0810d19fa485bd97f1507a77fb95b2a0c5b1ce5830dea0de18ef5f6c20a00e15d12331f7d203d3a5c9d261cdc4db79b82c59a02ec3746ce141a29564fc67a7e6ad0035707ce5a02d5035190ce39d1ade935bce8d2db6bf855b748412c8f6b441480b1da708c7587d576017bfee5fd7339097c4e205a4f0deb9385a3a0f7806fbc92f5490de7ba18539c7d8c659406341579581b124dce903c88eb72a4f4a86eb22093addd77a4a8b8f2fe48a564d820eae33127cfb442fedb91ed8eae5381ba9f1046e54e4e5c6887713366918f0ef69869a1e1c1254bd5e205917d5d5f7571a2e1c9b575c9df2ea2c0f6738f4a4206330ac1320b7f16f1906c4b067a64294082e2d754c8fa48c6470cf77edb7e03bb86f4a396b7ae2dbc56fbef172ef0cc08caa219968432847a0b5b933f0498d92e427b6e0d75969eeea525ce41eac022e468c0514420c561aff6c8edf72164ace360ef5ffd7cc2ed742cb8cfbaf855a20491a228ee0455b1013c4b2788e4afe5f5c3cb56aeea9c0aba0c2a3a99939fddc306c2e0d4dac3598daf10471cc14ebc2233d710393ea6ef90252aa70e609081040e814d26daaac5656530fc3586cef2d6c4743200ad764b5e4849b3afb18e28c13512271c93f14df40b13bb0e4ba277e2d20080be06d4b74dab1c3b51ad544e763f125ebcaf349f694dd1f6b8c60c2794ee456f96176bff72351704e7620228cf9352b65dc2e4ce420d8ba84e849a41e2986f0bbafc592af6a2eac9a74d6646379bf22dda3078b0408cacf02cb1a4fb0d4effcacdef4e5aff14cad3d90b0b79cc149129a4bdb67b7ba6c4333b1167dc29e2c02ae1130f373e25a4e602dbe1f9b88532001917a73ed406cb1019c2fe78c0278c373405583d6b76103ce83ac88def2ae0a8be4e4d15523c70c2baae6d7a2d10818b8894c593f7dac6103a75be2c4f6531ed679400c7fa71c82c9b8856c0d03dff14d810a3863018124c30e248a61eb93227f470138981ff90eb35b5ccfb27b22f9c3e99c4b8470896d7618b570015aa826ec67f903e741cd58a559e1c5451579265ebc761ea6a358754df10ebd689c5218df613d6ac78aa2720071cac7fb34ad6ad194e3e654b8a344971ae9a0d9fd530f3fc109ed0f01a69f6ef0208f0edeba13cfca9d6fa2999f44beac2fdbfcdd312c3132400d2921f726b2eac70133a0dd787448687c557fc1e69083e8169747421a39b826c54c494f1ac98dec8be7e330044daf7a0bf4eef11c79644bcff8298e0dbb24bb3a782d13457901c8e2ee0609dc1247478b2001d0d6c9db518ac9068208fd83bfe7d3508abdfa2503267f7a45de285633535685b6da31d63d4e2ff0720154ea6904ca630ce1857e3e8a1464cb6b5b16766638074546226ab14fde7afe2c0e86924792862a3f8832e46311b61bf635a27a61b3cfdffe662e7b8341fb9514e51b6f5688113dc4f5f812945d7a8ccfad91ede7fe0a607dfae5741848c88f01827ab0ecad4343b879f24bf632dcf9ce5db1568b3dcb0e92a3ea5d32bea5bc06fafea5a53fcf97c3dbe274575ef305fbde8c7eb4bfbd8e48bb3b062bce895e258826ec523ced0e17c77f73323f26abfeac8c7d8e401107f7240757fd4490841615f11738971be20699fc6c8afe8c9cdc2a78df1dd876c7265f3824b959bb5212c9b389a43203fd0b16ca64615dd125232f77f77153f9983d1a3c54b847aff7022cb4a694c0f04531367c7cd278a7f7a5aad796dca8eb486791d297a593f69912bd5a038e02b40783d1345f2cb58f787712a1ab0316cc7bc64d21d6035363f51a529233a8e89a1dea82a455dd42bc3795828066ab8dcf048ee7351d55a9a7870a0dee695599b4a9bd9a6a5cf01e1dbf2d89ce252c15cbe5271c2c2815edaf9f05db450c973db3930f9a551e2af7210dd55bcdda11077913f597781cd3a16457088bd5d22510baf958ef5e5d75a44055207b030f5bea7a15e7d6263aa056e9db1369a3521bb7832d51831151d9344364cc9337c3560d387864b7382aad762ff81f0a1adb64a26323cbf1d2f2be86ee22c9ce9dbfa51860184340226563b1b8af04deb0a186abb9d3d399c0a8548582177aa8378309d08aadd8aafa187508e9bd146d89b17af7b31920d5e2f7ea2b5881a86e81376d927a6a75ef1ad8dac6140219e02d5d2df2b473fd67a74b14fde0df6c9e9bb9253edb6d885b748527f90b30265690d0032e77862cbe2c5157330f90e10db865899ba2004fb7aac420bc40540b027d6a2389de4a85895dae7e6438772f59708f6ab2c0a0e8949d890d15d5fb14cb09a333e1d04ba6263268e7f6bda2915967987f0d2e45df0eb9e601aae96312e39e1add5778ec0e13ea0feb65580f7d4b73942aa5fb43cef4260dfd71cd6c25ca74631d5f182895f93bd853d8fe8c13db6e6a3064f4984cc00f502049db6b2f3fc84e7f072beba9e1e0da592e03103c6884b2241da120a6ce8f0dec4a023e0bb68670913ad55bd31adca44ef3afdb24a74965e6a5a4f6423c682b3f2505f119b8b942abb373d83f159674fbd223b4082c6a943ed5f6a3c0c30b4cba04c56d296e61d11f994ec45b3eb7c0fadce0892b09588510deeb84ad0fb43af65800ce1cf2b42a1f77afa03429b0e724ee01aeed3c0f0da7c26e9e0841f6f3e6995a6c26394ee8c1cf6f966c69582a31ecde78b71a2cb6980da3b820348502c53b6e762025993aab0dda298ac2c722b379d1769978b065166b59c953e243260294dc7f0811a8e2eead9e087b065819882752fca41693873712be6b9592ae57f30124af16a149d2e3e18080acb56723895e4b6b960a2e565c13bdbd7f4a56947457ace609911477284348841790e22ffb6407be7a6a3cc3cfa687abb560e83b8f95b12f11ee57180949ab894c6f63194f9795bb301e12258bda6bd19eef208895d3646407fc4b759cd026c4d8859b45b2db5951a4567b74ac30cb25763fbe11ab5cefab02121a2d8d2f4e68fa8da95a1d857eb9c915a24e6d65cacfdfea17b4f7c20b3a2a449be2dad4aa3acae6e681796e2e1c80d8e2be9ba6f33c83eb5efd04de8fca143d6424e42e247559e0d90173a80aa319d3698c58d33eeb2bd7224c03fe44de1bc549874bc66035e4ee132138100bb60a755a543c6598a5f06e0ef5d37d66891ab1b930794b5c2f560004d2356bb0b6d1e1bee7760b5f7d6e676b61896e91e72ca9789023470f3e234285e254c88fe67302f0c605e59670a402401135af417519c6454310e55f64af70feea1e255cee278895ad44da1a078bbf0f676dd705f605ff24647ad14d866c1b6d0670d72dbff0f44d02b049695345c6105d280c45791a7d544c5ef1225e1fb2bc196bb9aee9c3d7f0ae59c4346f6ef9e2042ce4906712a0284ceacf4e137c3e49b7305912c45069a5009e94d0de009b8412b1c101bd13792df3d482832d5622d9765b0829e4ba254614a09747aa80f7f11cd11f63c20711b449ae52188e09085afc42d7d3217f74f29686c3a5e4936e31ef89fb578f2655924c25b7b1a31ad2f2019eefc28890279d30b8fc665ee19dd8356f9eef3f0f754a9b70b8dbec4e7285dfc7f0ff474334c6bbdd8cd3c28f964fe49a17f0da160d18b7defd79fae2b4a2a0d713034f58e89606b1c905c31d316d878cda2f331f4f17fd9ace21980261985cdf4b609d527278aa16ab00b84f910d4193f280190563d7ed91b44e80458bad71e43f4338205ded6dc8fe68809fcad4460e3d1ddc2d2a4417c4dd117adb272f1fd4b5077d3f1b108fdb6648403ed5af2132302a2b2825988faee937f12c17e377767a51a562014dc54f7043c27df06d554e61611821961b9bec98d5c2570c9f6fe8487dbecc40137e68fa352623d570be4791c99f0800ba8d3b538ff0c41b13d298ba9924a3f180d85bcf37e477edf58d311f11af03fca8f7e3bbce16b2162db1ba146b4e895f914de18f120676a9471bc0cdcddf06d65a86b6b351545c1b659a08df6f9e19f66c3e0ac434fa776037a4e5ee4b3811b508a56849c1eae0857281bf4632fb10c571f2dc118a695db6f242ce9fd044295545f486838cb226f250ebf115a28f9f3e6be7b7c89876e31069d9ea516f0815289ee7e0afd2f36c1b2ab344cb6bef65eb037d2e4d2f16cf5504daf24290c00bd40ee9122929b83e4254115a940b02985fef66afdf26bfefdccddfbe2f25650fc238758bac9ed132a290886daac1dfc82dc410475fb48d8cf51ab1b19d73d829b0cfc486ebfb3a89eb25393460833342fcd2092edffaf42d364b7d80fd766a1270ac3e8f5c706c97f8d90728a2839a8f2bfa3bc81939a540515ee8b6f4275c1be4ec2588f003e9474418d88830187c2ce192fd7c69c1907d0e73826c3f86390c0af6fbc19e6a0db23b87a09ecd4de039db42fcb862c3315cf4580d5345d32606bb0085509bca0d4a5eaa1f3b0a34432db80b51b372b3cc68a16d16edd004a70796d5b65c46f72f16383ac710b0105a0a9f4b1efec577d5c4af544e9cddbf942062e58be832605b62a9c89c818605ec5eb258ebe45ce66d99e1df8ea696d1ea07f1787d1346652c451651f25e1134e6cc24bd4557df3930d2db2550f98653001ed3a64ac328546bf26e19e7dfd77c354788d256fbd110b9c266dc66aea7e2ae0b7cbf43d883fb25c91d1b778e277812b660b5fbcff53511027ba6d54afbd88d1adb25a5c52d05da1438ce5b88f7f61917547932ebc310994832fc7588df225b1c393e22f16abb8017e1118e20736e7844a3fad04dea0fce59cb6d2f477904af2cd57babe35dc13c3964fb7b49be5b34f676fbcc1123ca7063f42fcbd9d2f9f31dd9dd09efc55e5dd647b395c73e84834380708e332436b329f726a0a7ed75830f110176b0e1d9d3f313e2d89a5d1e0e053fbf010e3e20fdf030799fc73c51cd2772ad06cfd94569f5b8a998cba9dde2b6b4e50ac35f7970e38b814e063c368407349b0d618635aca08298e56598879e02db373cd6ed499931c2c617346fd15a23d8a1a020f6b71cefd7744a8b42921d02253d8d84db053065ba311bf7f5af1401196a38b0999ec52fbe6ced3f7464ef2472f4bac6cfee49af2581c31e5034980eab6404e8aa8cadfa913a7492149b7bbb8dc6fe77616870c2090e32d7ddc7692d0fb3a73ab606111690b73c8959bc3af3755223e56d07e34c8e4d0b492b3bcb1b96798b13cc92b1638a3f885d83e9f30b14cfdae34a307c233c578190d65aad0f421ecd053dea61996f8adf5906cbb7dd5e7a87169580a1ce5328d3d0937b28", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000335fc711e25eec4bc7002806d9b00c9600000000000000000000000000000000cdab8c25e7ffe0274955ed025ec557bd01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db70000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x000000000000000000000000000000000000000000000001f3e77d68ed5db4ee000000000000000000000000000000000000000000000002824efb1dd50dc53d000000000000000000000000000000000000000000000001e287c5b545301ce400000000000000000000000000000000000000000000000000003ac30f94551900000000000000000000000000000000000000000000000425ae327975b900880000000000000000000000000000000000000000000000030450cf304ee5f67d00000000000000000000000000000000000000000000000ddf49ff2fcefc54550000000000000000000000000000000000000000000000000001e6a4e87b9a2000000000000000000000000000000000000000000000000373d5ff3b6917377d00000000000000000000000000000000000000000000000ae9b0b68e3252781a0000000000000000000000000000000000000000000000078bae91658ec6a2b20000000000000000000000000000000000000000000000000001edde5cf5eeec0000000000000000000000000000000000000000000000024db0e0b9187cf4460000000000000000000000000000000000000000000000061da17c01b0ec38f8000000000000000000000000000000000000000000000007228437c29b55d1c600000000000000000000000000000000000000000000000000013d3baa2598e82a7f1abbbf32e6984fcb0010b83c6c7844ea6ca3fd0182ed6cfe12534a9a1ab32d95760c1b06880b626933c5156327a93ae0c04616b6f2cfd6457c4dfeb0b88f115d8837813343413efdc6428b79367f2cea1f6fa8b8d3ec221fa34eb49abfe209fe2bb0aeacf24470fe359956c7a9d3196c4d59810478f5e6c219a0a1ce604b02dc0bd84807a39b811417bd98322a228ff199aab7bf5543ac55c50a2b6509a607be7abd06b0d150fb024b43f0a0264f8f2a2258386a1e23c891c2cb0f8bc84212bdf036a748a87c8949b4fbf035079fe8b10ebd8c3b4e7f3ff7033fab3de191217123f39e02abc543c57376b6a015041fc623668c649686560fe0fdc1def2e6251b41df3fe84d67e48c15e23ddb2a83225f0edceb2c59758b5bd5faa1af92831ebb797fa8d6559e379f0bd0cc77e360b78c17a1e131017d4ecabdc2ee16ad021a31399184a8a78a5ff3667d4ba9ff5ca43d76c39055e64fe36d15d8be71fbf62733a4eed75dd54386c0915aa949a3ed5710627443b8ede3bffcee932e416b52251aae03e2369a82a51fe16dad9fef2b0e96efdcabd3322c1483aac4f532a1171401989403a42325d2afad1973d1a723680a168d3ae569995f8a97a7aecd777e19cde1b4ff4744fb82c63a6d990d2185ff7b003862b25776bd65a5ed0e414fdb227c255f71b15ae0c6933ed5702b3e729e7f826a590387398b121e06df511d5514fac1e05b458c10dfe141aa13ebf0647e24c0765eca7a633a6d82c8cdbf1e281308f82f1605ab9e282cf3731ea329850e8463f81d274c6f12c703ee4027535a287eccb7bc38c8523ec425377fb9e706a8c53c138c98c517ceabc0497f3845672c3b9942b4ec1692c479a8dfa18e55c4ad68c6519198b612a79177ef62c1ddc02d7842bb16583370aebcfa9a055bb2811bb27866c4f05557866b524f27f1e17503d9ab172ac8130eeb870cc0a647bdd39634c84b59cdf975ac0a773c6cbb151c1d126e82ef43f98e082640c22339e906c342e788aaafac7bf0f644097be2628e0232986c6d79f5ce13059c444f38ac33d416a25009f2dc0f86de79f54885b0510a86856cfb4965ef34d9bcf19bd9ab16fbea080afde613668d967b25fb3bc82707f9ec76cf626377bef45c4a0d2568adba49e805e59649f0695412b947af0a520131e9eaacec50553d90ccbca23fa3df01c8d236b1c156382732390af2085e4c0d7b8c85c65f3aab8fab763cec04cdaed0cf90145a204081c1ed3d483732a4ed13d9ffb221838c5f0d8ebc8c9aa98357bcf7fe1c7702f89b83bf5dbea988767c2ef522f59abf597777d99e620b501d519bc981fafb76042b453d1a7167e91a2e1c2b5edd2429647eb7b408c757e9a3c1c19c49bc6401f10816c3ca20a8d2768e19838e2be7eee84a64b232d47264296afebd614484dc8d735a5d28ddcc2a0c840edc31f87870ac41ada9eae239427acad4010ade9baa676779c222f23ebf5245235875eeb04b748a32a4aa8d9ca4cdfe03450077b68715308644eeb2cb1a68f1006d6d41a8e984f8e1114350487c1587996fbfaa19d2e60cb1175558e6a0292c0ed5ff0a5b855c452584fa8af47335737158637c2efd534da21b3853bf89f9242f9c279f94e8e47a25cb8df25ad1787142eea7c1e64d248709147b370f9fb54e25281994695e79395c7c6a8f9506b17f11c9e84beb4ca71f547cc5a24b737c1d139df5ef48dd868001c3ee1213fc3d145b8a0f1a4df3ba5f3b84041faad9c38729836a3cf695c826bada884a9b2765cd2dc3c519a142fe6dce7c61ff3c35b14e1d0f88c8c1042161b3fa888a7f10bd499c9fc4cec04fda3ec346eecfe4b8e64117ba207695a64301d974e223e8613159d26d2466c0bf49ee276f880a715f536b1252d850fc51c0b123ea14e1ee08e305ff2af518b23394984fced28b0d25ed1d053723691e73f00810db57dc33e7668cfa7ee8ea82dfb08ae1157a7ffab67bdc1e65810fab2b117de99f771fe66e33edd6842ab29ee28449a3a711513839b6c425cda1c407664580d3e7c4a68849d1c986366e3ac00ade2de89dc36065bdca0b272f4c4f8230570158f2d1d5f1131d7d7dc9abbd6be93171ab7bc99ad78497702925a81cae48f544226832ca5cf1cd5f97732b9537d9237258c8b8511f10723f057859047d53d2b70ffd0049c6ec0514ec267512b9c08a80a91d5d597b51c57e069c3a0203febcb8ece93238af70b491e7738dcb4fa26835c6dc843869122dfe09dc4f1773676441f3237c9b4417010f6a26a914ea3db681688f37d2c1f749b8111f6705f50a9b212a1f112a0be2c92ef9fbe0890df133252d4b59510aac501522c73c857179241fb523b6cbd447f6e20ffb3cd52797ea66d4c6fdcaa4d73a770bd4db1d004838ee5b6ff558a35fe31ebbe17211aade2d97750277a00aba976002bcacc9932d5aeae711c5c0e2b80ba242938e0d52e1c91ddd667c6f2b74abbf1da41a0b6c35c00e17a6ebc7901f7808ef157a01418d01949389052ac91b2d711c40a79aec70f73e9fdffe0ca944d1cac8d9e4b5ad2601316b5bd7e540f511662ab6ff3f08581ffbf961ee342499b951c63e998e60f4ab34881c9b8dd4e2dbe310cfdda4611947f2faaa558060914e3dad366bd4332aafe4bfc4805f6e84205304a03965d812f279734680b13e6cacac7b23c2228b1acbb93f4af41aa025bbf91ab9a9a42630a755b364b3ef3790337d1da0e315388521831502a260b09813081dcad647b64ede244918a587ff629d2b160c0ed328617eb4cd3df037fa8c30b62fdfc6a29ff02714877f0a4a4f09c29a9af2e85e427e502427651a8e3b304c361d2c9136de21197c24ce33da03e917c925be1923321e11e36bd043e1f730e88b2f174635edae1d9553e512b035b4e0b933d12716ae0346a30d6bc1c111c58df925b6e006c118da59d5e91142cb123ed18216a9a34235d2a452ac4206f0e6550b07c6ecfacd2f7fc86480c80dd178172a575c26c299c307ffa1b8b0a93f3f01be15b861d10213dd8c560e919c5fe5a46c8ca6c639fdde608dd918de75d6567a851b7680b297a2ff4259a48a36d482d1a698a6b16c9df14176812371e52cde3c222f843387102556dae72046097464c095df1ee46c69826480752cf48d60672cdd0299635611bb07626a8273bea0fcea5c90d886657e8c4ed47b62c3aef9191bc612aa8d7a3a8bbff9fb80690060ff6b36c102475b9136c49857634c927dcc9c481cd72807184e90c8e852a0801c946d6411cc2a195f398be1dfc37a2ef718a3091ddfaa1f317f91b550be51b86e43d0ac570a6c47ce50cc98a9905f886e44b69a02447cd1ee0c5ecda92b2d39872b82503583790fff4a195ef071c41ba778253a069b853bfc3cb73bf9c3d93c2ced9f9d1d9a9b44ff81145c0b9a6bdea82eb5fc0631a206074d902529dd3b03bfd38837b9295763ce9a173990da54473658fd092ff440bf4cbe8c837d5c828c80ae4d66f944545c0e27fc240ab1cef2f95b043929d25f3ed4e9ec08892ec6721c85052a6c78521f0feab2acdfcd4c91a7437e5d202aef919da4ecaa22b001155f637b156ba351e5a052c60bebf6953f295b369a27d042baaa94a56aff86ccf43bcd34f6e6080dae752c359e47a030ee0ce2921e0ce36acd288f10068d9904c877c9d62a7d937318f9f7c5f1df900a0e32eae9932c8f06a0b83e30b0b225cdebebd3b42a7131f4893cb5933ca34d249e7c78a9dc022560ea911eb98c0c473c4feccbaf919ba5e1ab1e9a139672bb0c81f0de2d6d236afef4ff6f79135129f6c4d375d5b53d96bf96bc04b2e36f1dce1ed5dfa982103fbd4e1941a5f51b4b114d45ca0397d8cc0b25e7cc9e4efec098fc9619fb31301f54c3195c1283407e2b520120c77daf2b6428d78e5ca5ddc13021c6505efe15f88f53c947bfc4408bfc93856fbdaf8a9c72df53fa60f0a8c91609f67457c703b6fb5b5acbf95186219605ee9245f80b948754af2fac1161b89589ebff5fa9168039fb3c5e72aa8a2adfcd6aa59ad3cf183103a5c1313dba6f6d978aeaa96226ee3f12f03eae59e60fb73db15bff1e51ae67cf88da23bdff5ec9e0b49566fa1683a4f335e2ad10bb52a7445b05a5765175abf26e2d8a05620767255f88f9070f0f0fb28cea2cf72e0745391bdad338e1b728132537f3495f2343514bcb1b3b0d8d39c1b7b17423a59059f96366f5a80c6016296951122774a1bf3f4176b2251fbf1b33040f6c6437af3d6fb09858b7972dea5c1baee1986cbb548c465e502708bd2c3149c3e86d6ad2452b70102fa4a3c748d65ae3a5bb2a90c6de79f60f6f090b044848ebc4579d7cd68132a3fc3a16989090cc37ce21079a152cd3fe47fa1fd48c0313cffed38c172e64da1a3f89942231c2113c5115e2bc85c3debef2ea054c043d9ad9cb32cef65a18f28f1e15b7c9985733c69a52eb6af4287922b6222f5205113943ff5d8e1ed099f5299cf8240527200186239863e36d4a58a2a5fb065d78c66b7136a52ef53de8e5d0319f5d2ac5d1c4b5eb6c6c31eb57e8e00f3b05879a174a1da0394f7602dba05a7cbcd64b297c0650eb8980cdca29736045d724b1959380c73fb0d631a680244a454dd3a4e0bba490eeaaf956f8296d39cb9a00ac3a4c762a8d11fd8645a19ecf3d149dc3de9a91d6fc22fcf41a3f41cf5d2c2cbc5eb7a019d0f1abe491758a5a35f9941edf83ae783c1009d9b32efbcd5773252cf6fc0899360b17905f9665f760f0d05a383645404d7ebe4acbd3cfc0bebe1a3e6067abe34217bd9d63a445ab7d62814ce52e0c7947b155bea4336495c7521729b8332e879ba575cb2b5b17984efcfaf4f2f0016a92233578f09cc64c1c5226e733c382eece007d0a5b2792b13ae338a8edd9034811a0d5a18e1ba453107c2fcd495401b784847149562b319c2ddf5434b54f6f98a55010e2d151b34ac1c61f0f08bd81faed2b6ace599d4a68f5e12eef8b2fda21ba86dbc6307001f9554a29d40fa86deb11a05a25c8dc66ec9e525526aed32573f30014da08c2d9382254292bcd80c5fb3c4aff19006441bf4090d3f5e72244d8967b068ab8a720e2f8f8242a3331efa61b12eb0f568148b190fe0aef1655aee8129c611ff7ab4d2b7e7f0456931fa078ca2b6cd155f04f7a8cf31a2540ad143669812e18153379b610f11a1ebc35a1e580b55c355adff570ccf222d05ba579793ebf482994816ecc57aa1ceb6fe0e137c9ad7d23215013c3d02755009ec47dd527d366d54f7bda9c80501b163775e5f22abeefc0e4d0ab58b3bfbc5c9a6971f5cd29ec40cb312fe43e9b09d53ff3dea50a09c42faa82a943c4cf5b06cb0c46d0e78564929a69cb6ece0c2c5c935dfe7dedf4020a9d2344467a8776559fb824100d798d988c0dda97c42622dc97061e54e3c9c7220c5326b65d25af7785919937794c407761f4273d7d5502c53ff11a6b83114105cb2f09891858a4920eecb5b8b73a5c803f4f8c68e16f2540d7a584736cd89561c2c22b3639d06dc502dde723bfd7d10fc023d7650b780aa52ffc396e1ba4eaee1e79d4ca4e42ac6fac5815ad08ada300c50431875f970511405be5f78f927ba165aeeb6e8349977f8bc883e90fa7946fcf97c403eb8b2906a69f84bd75e4bd3bddae210d7100d285c94009c31cfc067e7f6fdf538a3c085c166dc8c565e5a7fcdda24325b2c61dca091a4e4cb7f42dfd5c26ece367f02788948403bb25fdd624b084a539ba98fb6e00dd50b0021e2ce918b0dec5c6220414cb321509524b921d65134fc959ca8112dd6e7b6645cc9dffd9b0be97512e201d78105bc66c1d6b44fb0dd0d14d1575362cf2d5c4644bf4afbaa963fad75d219e7a9dea5e6a44092c906bed7582cef5b50b36b7120f7e56749f2c23b0667e0b3e97bd8b681cc3e3cf0564c8c508cad783d871a755580ef9cfc3f68511e6011814740377b0ef4c3e85e74f282803f7bf7a60a69ed76269beaf2e541f2f01461f1d3a7b61dccce66f421b1702dae8e1095183bd485317ee1121acc0eed3d9841499f6b9a0adc4a459414f50da7ca00070a63623de5e075e32778ea939f2b5fc2c7681f628866d4e70be2636b34ff2c719b9c49d94241c8993e398a07652126f0eb20da7dfc1543ea992b023d65d006cb4ba0270c324ed1ab10295eba02f28e72a0591f43791564c8edc198a23254b9fe3711abb8ebad8b2a28d76b063829d5b1856a4b156fddb3e21c5fbabcf1a2fcb1aa0aef309dbdd7b30fa9c24a475e1c506bae9cdc0be1efa29becf66ed43a000da45c20f24a5c8dfc08bf490000c17f023acc1af65a33f8ec051411061d7015c07dfd31c7ebfc09a9715008d0523364b2ddfd345073e5059d754260e7578661403ce987d3048e9480c61862d8899b4f7289e8c9f289e87a9f906dd963933d3626632989d2eb26250af5b76cea59070a4012afdb97165ca9da72545f49e15633ea2a26c7735c944e990dc961357e776441813f18afa14f8327bf2db98a0e3e7019f2751879c267bcb6c7863aec69caede24500dfa808bc3ccb710fd545fc116218319df0bab38e404c07ae223d27c9d4f29916205a86f00600a61aa0c2e0dbee33cf3613b31ef280895a78d292170e5911a3cde214de3e8d23939407279f7cd2ef57240e96eeb86081eefd70e5b57a3ad21b8f1063cc1e3b6f6f11d52bf76ebfc3ad91339479a7a3d2c2b8e6f69fd78d010f2dce00d6d8b680c06ecc20f6d67ec19a28f9c20ee9b624b784e05418ff4552d1ceeed6741f7ed9c3d00a3fd38447dad774a539bc99a5052a95f628286a5e62a4ccf79bab6da12d1576c537101a31fced2f45211f914c764e5ed965d5d9419156f62a957a277eee361d36f5d7c60222d27c3376828803d28c6633a8f2110601f35785bd5788bc8a8c47929d7d820cd2a4dab83e704ec786ea5e5fc3886dc9615b5d4a5355cd97beb11573a909572900531439f2a63f421eb5494cbed5ca15503e22f5cfb43b27639d7af28165b4591305706d1f2a9b0d1fdaf068e685508150c3d3d23a9c0a31fc682dff0e845c6317a414395c9e6c4a33ccab5c6395006971edfa8f68e55940b83b22ee4dc105cf2e39f0af7ea4a44a7a2992b76c4c570a30b677986924aa592738f56f95726a1df409d84e7f7dd45727c6c723d7d44300f256dabb802ab4c9a88121fae65192fc5aebfe70ffee877bda75be243ef3c1836206382f14f942875cee4ac22f4207aef59da5dcd208064b367dd78777384b80503be38dd876624f57cc4198a025b37ab52d7229aa2c68239ff2dd43d8994795828a5cddcc20bb663b36de0bcf88564e95318f7d59f159b263745ddfe380abfd71f5c2a6d01a4dfd231fa8c8adaa500d5df747ec3f5fad7f41f4d3307d99d67c019d7a23b048f7d63eb11c1133b5d573c502ecc2f5623f246c183a7d384044883190100bbc33c4aac39a021366c17e24a8e8d897bea7b9aa2b0b0ff17a79a91db2e36a411fe0c982dabbfc76adb74e710dc0cbb2101b4e04c96683f49b0d9e8321f4f529c3d73b380455084bad0671f759bba657fa3b04a41dac8acb156085556126a7b131eb954f4c98ad9a97666023f78a49a1906c542ddc59dc60bbca6c45b121406d8eec540912b0d0b03638fac7205b2556bfb4b096924ee52e816d79310095af3cb21300eb0416e87a6db4efa734e6f54a61d56e45c21f8501a2c18c19311f75013282383feec1caf488b8cf5e1d6bf898e420a0ceba1f47a001c4273d01357402e04c95adb4a05284a08504bd20e45a7ff3b37a127bb457edff631001c2134448d544b87c7cec84445bdedcf77b43978a27227650cf6653e85e62680d91a9a336e6013866aea57a78226ba26021773e81386c7e09f3942eff5c524b8ce0dd795c2959de2311e2b05f5e66cd6a92cffe26a580241663b6b4792cd8b8b872628c340bba16177e26150c31659ccdb694c9871407b0f2ae7c2020bd2d213e726f43ae8d6415189434d3f6053dd0f14fa9ab5e26e384aca8260901a01b46f83222053230900b4e765a5013a7931b2d689ae848d66815bd07bf7bcacd37f7f9e28c5e3f11c8c868570ada9d40a96ef71f80ab55a99eb2bf8e78db8182d518a6c1f8ac5c99cb8be988eed079d489e85c336fcb5f8057bb20d6322fa86e62bd8be01be1982e48210e025aef091643c5cf1d0219e877cf4718183baa29fd1e998b2233110abcdf05adb27255a66180b98862d820fb8220f99ec23d305d9f2be45400c8a6b6b564726412d6cd628efded4bd4d8d3faca4636f44edca4fe191db5bc302d917613326ba93f366cb36705e2ea7aeb69da71610e0546364dd7b8b962d62284be2ca6d0542cf4c387809d6cb0ac49ecf5e41166f72429c711e70927fe10f1357793171f937cba3af6a6d5140e6812dfc3f2377ba7e11204c0f27626e61ca00c3dcc0694e6fd6ab948627668d4eb432f6ce32c38726a5a35211facd2ad25e20b903fb971f880376def335853ccd89ae2b2485dd06ef4fbd72af678f3684d2175ac14729f842e00cfd4f48f8080e536f428c32e16954519e481da94b30b3e00dbc1e2d668931c3cb33526098b89da49bf023a3405526b77df1015c6aa8b88624f585b6022615ec24682a2e594e74c3cc251b082eb238167e9a5bcc7d8f97d20c0439e4fd8e706820a24114509132c61fa129989b7c5624f7dc11ebe3a6a1001bb52e32134ccc60f5e3111bc490086ed2b20eb735312371d60f9a2e69477f820bb45596866f4f846e312b6376de071f1354e67f33abadbfd869ff728c920bf21390824d579f86d9a78736e37eb6a1702cbb0535f3df93abf216055257c126c907b88a19fbede7e3eaf9972c99395a91a86259cf8b6bb7fc3e74365c983df0d3102c4173d44687042228e3dd26d15d4350bc9c99247ea8c96456f864edaa1ffe18a291daf39eb370763f0e0e71727b57dc895c7a0ca61d736fd361d0af7397ce060a77335485fe34cd581d4e971f77bd186535149d78654617f2d6f41d6ce2c825c039324ffcc10a27e50c6e47a5b2a84ad8faae7fd6932a91f3ee2d3009f4942569b4394e802c9207533e7c87ef88ecf09e20249d2e9158466d4e38a4cce7e309a3d2d82cd6c979458b7a60bb3372e544d4650c34c31410d78ce03b80bbdeb81cdc482661fce86890b76c3ce45a72c36b70d441e203c6f385584449176e1fa81827627eb4f2650e88b29856eba0822a44a63cb2c71695be176035a50dbf92b32487ed2ec3637821549f8013c816eb3ccd0d194634a82bf32b0957c6172ecd771769549a3138b690aec5c96013608f624ce42a753b61dbeaded643d8946cd9562b19aac5fd513923122ab8e5b28be59b6228825d403400207e70fca0ebbdf7c11656e662f005a9297c1cf3a5c68a23d88b5065a434aeca985d3947bdd7bf1caf2a904e27a9375bc4e58d025812903d2a8d9c1eefdf2b76e1a8f3bb20809a5b951f963e310b9b296516448e4847e8bf10487cd6704badf264e38036fd74b7c39c01f37047e62239081cd9cd5e3059d1070c9c1f89488626733f2b11bc14eb89481a2176a0bbd96c66575f009518c91670075a6d9488dccc597d27ad2085d5e559188d32c5168cc71152887d8ea71d15bf34ef9cd15cb041c40a6aefe67a530658033b23542baf13eb49ed5f4c0b7b111d7734ae2f88e7b05f9b0cdd7d573db49a19429bf46545942bb14edc07963d2800b5b66414a552a19759e3bee9c57d4e680be3d6503174807ebb9845825a0c70ab68b7d11d4af2cc3f09f078aecab7e88010e194843746e2c4c27893ed916d12d7575bc66eb1a290eccf0513a3e09579072ce06d8a7f38211ea1da1d8bfa710062124f853e1c9261705447defc47a1adf80c6a0ca3ea3d92ceb4b19219cdfbb15c1ffa71cb7fb0a35739a497e640efefdb2ff43e55a01f649aa81a5ffbd71e6dce6d3fd23d2011701aea425fdc6b4b89b62e946be2642b9247e35c6a16e8bbc8dc310107f6b7ce4fa1b22e96fb9d8562f02eda1a5ca567421a816fcd54396eb32759b565c9ea9cb3661a4c8e42986e8d272a33a58ab5bc23b11d2ee27a94d1fc5faa2a0ac039fd46f4f3540e952035f2a01762f1ea6c159137e220650a4de7c37550dfdcf0fb2852288a2dc0d75a13b721252e4413513bdb146f23f6c453c8212964a0ad3cc7e1706d547dd9a6e920c7be07bfc9ccef26adbbb81482041600e5be4608b25b7ade0bd39707d1141a9c38e91d6d6a8710e20c146bd6a167599b91eeba7d279906fc5a99ca5b5316472a92aa0e8b256cfa9df92304e935d1022ba8ee8691311ac01f5acdfe586ee52cd2be6025ba4653066c824575b4d79e762c2da88526218e5d1684647a0b97b5fcc8f97a24e99a4f6e8cab10ccb9181685a81710d9f486c8a9108c547f2d7d9796c1228b0709a12e3fd8326bf7834a1dacd53ba4e2dda1464eb76bf8e10837c5e0c8f11c2ea8f00356fbf4601bfddc1b11e5b391eec85ecdfbb3c9174bb085f3029ef74712def3d1bc2eb8b062e1cd968b374e2ddebb72d61c52ecb61f1deef94b7912361d7cf658040c7c687d5f2c471ae5a16628d632ae7811680f078efea45fd12ebb24dd2fd3480c5d399b9595fc698bd6d0c435ec72ab51a7d09edf66764065d9cb142b7e6d555a11b06605fdc3479afe95b5fad22b5e9b4604d2ca7ba3eb51578f23dead1a272168f78d251f6019c0ef83eeaf64bdee46c83ab0119debf5fc865717d8032f1d166e1de0446dec8737fa858d523c1c8155d4aa8d253b4f46d2973f178b0492d8680a005dca61f448687547ab04454c5c8e0ce84401c2203ae4a65d2a4c297f9b989bebc0d717a7b7b1c26a72ae0c8e838cd6eacbd41c6c66badc10014e4d4de5b875ace180f4562b87c63328a89149e4fe0d1f76c6f468a1f36fa60c7bc7e5fe59eafa01cbcb0000cb0f1498544708be7a7b8419d0abc91fa29728021d27ffcc472552a602c5c809451567cef6341e2c070dbbfdeea42a20968ed6254aa989dc871e3c91a70144102aa5696b7f4f669e1e0b3db2a8c101e1899f4803e8724d5e9407f485851ab9e27f0e7940a436faa458d51734e72c88c953a1cd19bfed29f9a6e503696a0b6823493b253c7878515b54225fa2cc2c43cc938fd1018c70f38a4649a2760f6cc067909d2812897dc8516e28343a51d9a0d987d0e7117eaad11d73c1e43dbd43da5e94f29ee359b1639549c847831283677d2dc6fa0b4e9bdf4affaeac14d82d816a069f303c63ff8bc301ad2cae0e77130b8771412f326f2dadc7c0d69543f99fc3b5b07139ada8c8443a7239af214df474b7515218b6e60c22294e4752120ff00af6b0d8a6f574fde3d9d31ac52559f9c1364efa10aca4626bddf9117a6cde7324503141532c8abd79c4694e62d4cb86596466160840dbfaf9000ffb80391b20e672a5f3f84f69c2d97d498f7449d1bb13f01818002923644f08ea3b3e94a83909315385d2720f9f98bffc8373ec9615900ed48016d62d4b5bbd9db07be2097ec1142cb4ef5789c9761ea7131d74168531c966e226d6dc9ace4464dd4aa2419f4fd2560ae90ee5021d85f58d208de486b6d008262451667f7fbf78bd41f6640410e52c87d7f000983c3282f1a6d7f4bc6c3753842dadd4bd8444fb54dfbbddfb4a627f6cc274263ae1060a69bdf3e0861847cc8810dc714756a05b8ca2ad1e2802a1d702133aec5901fba30e45dc302c7cbc1968132a3146c7832f67796a4c541788345733da795f14cd6049ddf75a49015e970c167f44226bf08bcc0721b0e43312cd33edba128ec39367db196b3f9e22817bc11d3bc0fd02aca7dc97a67b6896ddcef0fb349e9c3b36214fe2efd4cb55709fdd0511c1f18652d148e1959d2e6798bc956b6141176e0ff385b00f6178ae44f8bc2738aa8b71330cd645514f97c7db99e5722f935c22998887b7051d0ece56a1630af166104b50c80dd514eca36ad8feea1457acfa6d77b7991d1cecd96e7bada5161d34c5df4bfa8e7084390684652ddf60aa86f07a3269144e915bcc6e02f3de203dd990d931368a32f6075b3f49f95246e2686285651db5d4a08cfd102858902bfe01c03f1ad9cbf1b7ef45f67e6312ebb0c6b532731a4d26eab88ea7339f2e17582ba8136f2991301a7c24a3a3b5fb5fec1f3fef8b11e29ff40e9790f783af2f1da1cf1c4820e552ca692c2142fb81189dd32ee5b4cd67dc8920701023ef7b04edd473d4dc28377445acb892620abb94d69cbfcdd14f32227020a45a1ac5ba140ac9814b939870f27931e506f0a2676c9f65d88cac80d75b67a4ce57a07f8f2c0dd48cca8b68c2402cac0f8aed4314857f3473f904c7cf90286339fc11daa42b68686f74087bee2d18a1ceb6573d5faaf230e23dee53502595939e6220245606fbef3f3647483c3b7faea0b02e4e1ba5945258c9d51fa75512da79ebc9cc930184dd776227a2ac970d98c26cc7bbcbb699908974f2772729d069f7b8d890ab157aaf2faaf3282fecfe158631803de33736808af53a4febc35e372ff2452905007d266868ef9e28c1c631de830a7a3c38bb3b0664292487848815b002e4d0ed2474460c04b736484a520eaefb1ea931b86147abf7762ab20f703dcc3d607f8b0089caaa2eb682bb184dc81f43d39bc24daded115540412e4ea3ffb5adef53530a886a7b84b585d604f5b132e4f314fb20e59db3d005199a0703a149156a65e32aa0e38754aa43ef903a00c49b9c4a88a05d9f3bf958e07266055dd65088d9332b6c7d29ff611772b29263c0eccc81514b73cebbcf284247c809bd344aa416941c6bd68cc4e5c26c659f096345c1b8d5edc76a9d61ba130345fd5ad74b27a5e92781a44d259141e045f548c11b9679e7161823a61977ffb659471b0271d1000e2b89ad259d73b24aeba57cd6b941067906339e0ca696d048d302751cc0a39acc0b3a7ffbab9093266b59eedc8a12ee36530b7765feb1d69d64940461947246d619a14b4a57cb4bb9d60feeb98d8dedbefa88e3fb63be9ea99f9d65f7694c55462847e8212c9332f8c8e330500161e7eb4d113e3ec89356ea8c57eb739f5f01cf1e1e6a1040be7d194c75cec9fc63d377dd590df604615d5c1391ec17b535d7680dee50b2f96af256227ad345cc93f80822614d3fdb7f50966ef5d5771f6a239c24a3670723bec8bd40f09dd547cee615b07b54866e67f6323f4a6183a87e22b21690068f21e34f58f1854f39f902c2121cf7621a5ab326138284d3f4c54ea61a2cb1f8fc94b90c64a62f15927c4279fe1fd41551c26d4799ef9326809090aebb294270c2a541da191e151e04bca72bb50ea7154e249e8bf58d3248c323ab9efe2c75cc88deba848e0477c2d4f7842823efc16251b55dea0ddf7c06ff6372598913b16600c12af1f085fc8b0d695e469397a0ba7a18a1570930aff5be1b450c1d00b59bff7887dfcf7f2f0bd0a92934561b2e093c3295cd3969b8fd8836b37145271f5d5905e9e81ba87591638170928a90b8e9045055e740fb43cc3276a66c9129e35a0a0ef6fe53bbe1e2745db53e13f623bbdbaaa0bb4203ecf23e85f3838c1a74837fc00299a85ef5897d070b4faf2a7b56998b2e05dc2aeb645f18090d5d232d40f6bc20623c01fb890301fe074c7d09bc63ebe1bb260ac78cec14373add0f6dd4686087eae0e89f6a854adcc3437d01268dd3984957437fc292ff847d0704480c3a484db21e9f0d77969fd778259d723f1b887943f50ea98f75371864780eeecfcf5a77a5498f3379bf8d52e461765acd5ef59a7578df882e599c82b441291216f68f6ca3e602b47ed99aa0c6dfbfe69f1a644d22b66faa012fe35e84031c6cd6640f8cf548e61d4a0205b33dec975e6d8f3f7632b02c952948780ebc89169212ee0c8cbba3869490e0f659f8bbae0bcfed4ba2c81396a26fd929eb167e077a82cd634a5a203ca87729e6057ca8961428828cb53066492efe6ebc2d264b0f0b99506a07addda2986179905ae1744a342d6abfb3c82510551c7d46f176ec19b3fc7a6c4e0d0f0fbe8d495007c2d575b4f6edd0187aa08936a90101deafdf0b4a5912abba298b2d3864ddc9282c756bf0b241a35873151fa04296301519c00fe2804df9545cc47117b30af8cba291e17af28d40c30d05568a0f2c6febf4341ff71b80a9ed4944339aa12387542572592e2456eba9e4af6ea17722834a13602f714effd3c4eaa41db256790c488b7f4cd0c00a7e7be1c0740942fc931c3514016ddfcfcda716b312953b009d3fa126c50e2ea5515e4c0b5a3a66f53f27f261098a8adcbe46d98d5afc16e08ba6c716f18a36c56ed66529113d6b6b707071822cb4593a890ac726c30a87f2a356904b8acb3395fbaa5fa7f7e5c4b1f0c385261c40a7973e6c2847b215e7245b9f9ff0f381c098493bf8934241901d3b0238c82927ac418a390cb7d18d15626062a992593358c3eea37934ccd84161bbd272d021cfe3105284aa92738660327df5994eaa506a85ddc3bb5b12dd2f73c31511b1097640233620fce20826a296910b7a30f774fb572b7a1b60959ee49247fa74412b4c395963a505008da29ef947a6f676686340b98a7f7f8030c44c8bf90d1fb2167e266e7bcc2a6c7dde2496aaedc51f58bf1227b79ff40a60ea3581c19597eb1198139824e4cffed72280b7fcc1bc10a25541c2cde5143424deba327200c291", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000007a6dedcfcddb992da4890c8a97aebf9f00000000000000000000000000000000b4d83f75777e7e4919824dcb4ff934df01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } From 367615a2370651d761dd3a78ba4e1f5ffd35e74f Mon Sep 17 00:00:00 2001 From: 0xjei Date: Tue, 19 May 2026 22:30:18 +0200 Subject: [PATCH 13/20] fix stale after rebase --- .../results_insecure/crisp_verify_gas.json | 58 +- .../results_insecure/integration_summary.json | 102 +- .../benchmarks/results_insecure/report.md | 86 +- .../src/threshold_plaintext_aggregator.rs | 14 + crates/sortition/src/sortition.rs | 15 +- .../verifiers/bfv/BfvDecryptionVerifier.sol | 10 +- .../bfv/honk/DecryptionAggregatorVerifier.sol | 1635 +++++------------ .../bfv/honk/DkgAggregatorVerifier.sol | 1635 +++++------------ .../enclave-contracts/deployed_contracts.json | 125 ++ packages/enclave-contracts/tasks/enclave.ts | 2 +- .../test/BfvDecryptionVerifier.spec.ts | 60 - .../bfv_vk_binding/folded_artifacts.json | 8 +- tests/integration/base.sh | 3 +- 13 files changed, 1190 insertions(+), 2563 deletions(-) diff --git a/circuits/benchmarks/results_insecure/crisp_verify_gas.json b/circuits/benchmarks/results_insecure/crisp_verify_gas.json index cad618487..44e97a919 100644 --- a/circuits/benchmarks/results_insecure/crisp_verify_gas.json +++ b/circuits/benchmarks/results_insecure/crisp_verify_gas.json @@ -1,7 +1,7 @@ { "verify_gas": { - "dkg": 3042712, - "user": 2972857, + "dkg": 3042761, + "user": 2973025, "dec": 3553819 }, "source": "folded_proof_export_plus_crisp_verify_test", @@ -17,9 +17,9 @@ }, "calldata_gas": { "dkg": { - "proof": 170052, - "public_inputs": 6144, - "total": 176196 + "proof": 170124, + "public_inputs": 6132, + "total": 176256 }, "dec": { "proof": 169980, @@ -27,53 +27,7 @@ "total": 187284 } }, - "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.109944514, "runs": 3, "total_seconds": 0.329833543 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 0.607762083, "runs": 3, "total_seconds": 1.823286249 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 0.557054791, "runs": 1, "total_seconds": 0.557054791 }, - { "name": "GenEsiSss", "avg_seconds": 0.125857458, "runs": 3, "total_seconds": 0.377572374 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 0.226792222, "runs": 3, "total_seconds": 0.680376666 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.377944667, "runs": 1, "total_seconds": 8.377944667 }, - { "name": "ZkDecryptionAggregation", "avg_seconds": 48.469535625, "runs": 1, "total_seconds": 48.469535625 }, - { "name": "ZkDkgAggregation", "avg_seconds": 20.0494395, "runs": 1, "total_seconds": 20.0494395 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 1.483909729, "runs": 6, "total_seconds": 8.903458377 }, - { "name": "ZkNodeDkgFold", "avg_seconds": 61.021601069, "runs": 3, "total_seconds": 183.064803208 }, - { "name": "ZkPkAggregation", "avg_seconds": 2.118477708, "runs": 1, "total_seconds": 2.118477708 }, - { "name": "ZkPkBfv", "avg_seconds": 0.337032069, "runs": 3, "total_seconds": 1.011096209 }, - { "name": "ZkPkGeneration", "avg_seconds": 1.3624095, "runs": 3, "total_seconds": 4.087228501 }, - { "name": "ZkShareComputation", "avg_seconds": 2.714979847, "runs": 6, "total_seconds": 16.289879085 }, - { "name": "ZkShareEncryption", "avg_seconds": 2.516941928, "runs": 24, "total_seconds": 60.406606294 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 6.083372944, "runs": 3, "total_seconds": 18.250118833 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.096223944, "runs": 3, "total_seconds": 0.288671834 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.214040633, "runs": 5, "total_seconds": 1.070203166 } - ], - "operation_timings_total_seconds": 376.15558663, - "timings_seconds": [ - { "label": "Starting trbfv actor test", "seconds": 0e-9 }, - { "label": "Setup completed", "seconds": 3.066766584 }, - { "label": "Committee Setup Completed", "seconds": 20.21209275 }, - { "label": "Committee Finalization Complete", "seconds": 0.006218875 }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 299.47482725 }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 302.025307167 }, - { "label": "Application CT Gen", "seconds": 0.311998458 }, - { "label": "Running FHE Application", "seconds": 0.003517959 }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 78.562423209 }, - { "label": "Entire Test", "seconds": 404.196322167 } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x0000000000000000000000000000000000000000000000001cb3e9df822c791200000000000000000000000000000000000000000000000e6e2eb24f99c6e4fe00000000000000000000000000000000000000000000000f0341c256bc7f8f2c0000000000000000000000000000000000000000000000000002945e9bb27e6800000000000000000000000000000000000000000000000ca22d28f7e6b41a5600000000000000000000000000000000000000000000000fa4ce39d3e2c6c64c000000000000000000000000000000000000000000000000c382057b093f299f0000000000000000000000000000000000000000000000000000d2ab1e847eef00000000000000000000000000000000000000000000000732eae124bf33406c00000000000000000000000000000000000000000000000825bba25f971304c000000000000000000000000000000000000000000000000f31aa002ce9a71b020000000000000000000000000000000000000000000000000001c69dee5a348a0000000000000000000000000000000000000000000000082047502ee34e43830000000000000000000000000000000000000000000000098bc3dc090da06bd200000000000000000000000000000000000000000000000a395a0475f5823e140000000000000000000000000000000000000000000000000001d21f325773d10b2b91c7a8b3d057b7bc490148ca03e7c07ae5285593e8de30cc1ac03ac33ea123a54330466872a485fe60682f01940aecf0a16a29400c85c1ff026756e3feb104e48aa44e3c4435c6a81422971f410e652c2a8dbb6dce04531118b73de26b410850267190a569b1e5c0d99d36551817631476dce47dab6ce7ddfa42abee9cd50d8774b5dbe2992589f46d6b0d3c14df64e1f145ecf602d7f07d6bb8b077ad801635d81fb371fc342b18cf95c1280232e65ec5fdf4f7edc0694727528363aded2adf3a83dc8a210463d985cdb56d2f0d39aaeef44f7e2567bec394a51280aaa41d9718c798bf7003a861694b93dc6114eb66daa0cc69e0dbfc33065a442bff1f24da7df7f66206346b9c7f067dc75502807746037df9556453c8cfe619c13bf2002ffe7ada7eaa20f50572c400c4ffc4942970b82e2e42a5569a83f6f7cb7e11032c18d50c12e995d4aec40ac9bef1f9e92449cc1287a6ae05407339c176d21818bac164c691fda117bc7877f2e7bccc8e7d7257e4bb51996272017b55b3dda603b572716b3071143fad60717290ac7522b0944c91980411ff8755372680bdd7192dd2340cf54de8aed55abc38be8e4d864a57bd60f620f2a66f008218a6b5f41d926dc02c0c633628367cb8e460116ca71dbdb7054035eca657119596c3de48187112be8a3281245ce6e500f797cc1432a4d3c09f50d55d3cf2a76c072123062816948f10550ad9cc66240c3475cc3f7e1be8a364b22c852289e341fa94ef710e359bc943797bfdd06360aab35078dcb0f5f34feae065749d4499ce323c0c26276f732dc63c13091a70d853fd6f9d06c67a9219b36ab6cfc590fc247994b9cc26f3325182becff2efcc85ba9a3c8a5d260edc035d0bfc3e0845aabd9d1b893210c0d54eefaab1eaee3b1703468de3bf3b65973d385114a22b661f1b24e99a9a1df7f33c0a098f95b18ac88841857d37fd0d01befbff8eda91f1ee54b9d9bf4d0972e399e72488027be9753aa1dbcf846b5d614dc2e83df5ed880a51ac5672e90569ae383a1ca0dbc467c684df3bc238b9c05156103aa2aef2eb1fdf18fc875926636c6e183c34d39a42e830a4f8894c70c00a993805d0013500ff4cdd8d014620ce10ca69f17a638c92367872e4f56e8aacb16fe54754cf3f56c74e9296ff6823e9d239478326b2dfda0f711567d671b7c617f59f0369ba10265ba61a2debb21dc7411bc598d5fa875f66505485e1674af48502bfb1c69679777cbe6b7a901314fc90eaf3185389206b54c33a72f351e81beb8f7b0d26511d530a8eab1b28e11b330ff20265875c8ac3b81da94e8665e4a60044918614ed85954f5ee5ab759e1512c2e32b793a0d0acbdf5e4e38d9cacbe258b66de1622bb1fb345e98b6173d2a8df716393a447531eaba0be23f1fbded411e71808271a3a82b4f9975c174051569589970a0511743bf47bd75b9f04e2f7a5d4b7deaab3ca11d0dc741998c2a030c6c5458d176c04428bec5a3e0d8e0e5c56a3eee999b10f4e41008d0ee6c500c4d4285ef91799d520036adce78bb4c4d435e0379a267084b998c065e2b5d2c05da4a111ab012aa81a0bf2854c7c58136cb6e2c629474ecd90a5041606bc645022463e9a9e43918859426adcea475191e505ee6a37d3afb0cda9aa1c7c788bb0a88deee1aefc855d078af3172244c5d3f21ddbfc514c0836a3c498dea40a5511c6fb874ae86b70059e90abb007bcef2ef2c247cbc5c6327d1d53a26e8c39123107878df9ad00525610d70a76cb32b6db209f420c58f9fb3b3053cdd884ab03303467453d17b995bb31d2bb667f55e5a6f98af83e14c7d2f71f661cbcb3e29e42855c0566eec6cd80417f89b09a4dad9bbf28e9aeacde3580fe7fbe92cbc8d6e23394556cab576138d117b28865defcf49b06e5b82ccc8137f314a7b3f7c2bec071311be89cf5d785e50c106985d91efc1623bb02487a838a29ba70228de3cd618e92a7a82355306a10a99302db1b6f705e6e6ae73cd5d6e3e7d5787023be31124704e1185bf63c8a0578e04b8fde9c99c2f4f5b96431334d6b4065ff89d8cf905c47b96cd804bb285ce1d70a99679ce64f47a71354031d01399cdcbd0ad1e0e06ce9eca7c849eba54a12dbe233f25e8e7a041c192512e99f964dbe30ae60c9b0d2152494c0e45fe1ee814a0931add16233f30a51e9865d9f39e6c5e956c872b14b51eaa8a1c3607325a9b7daf83937fb73caa1fe79e4791869cdfd75a2a4d662cbb0dd627932ea4a38efd1bbc223e92e269674601bb0042b816acd8f55aadc0186ec668949e62b5cc325bed81f906800ee00b52c5f0c3dc0fc6aada1fd6b5a823e1d846890c9bf6f66d991dd8547b31d71058b9de07eab2192c8afe637c651f161176928f1ada22ee5450992b6410cd20f9c0cb72ed0aa36e7f92d47592c53b0b5e2f62a81ee85e4454543894d2e45ca07656b56cec384333eb8620438e1dbb07d8b15737f5a26091192263237fb3545f2a7c2cb52e27d82fc63dadc2ef6ec41cbf43034f22b18a487f1571b7f49ad6cb43662c1d88884ddfc4c6c636ae714d0ca4003603acb3a8a4ba957d6cfc7cfda372f89a2f389757dbe339dd2171f5351baa09fa66e772ff12885bbf3be23fcce81cb59e6021505417367c2f49821f7823ba2358ab38328713a2efb8139298fbd22899d5669e32ff033d4e760e7d8f9d1b3399208db97688cd9c02c839ca9843236e13c214a2d8598e601cb5697ebc782d31df4ba699e8ee8ae919ab36f57dc5ca0cb0e5ae37e10a6706eb18514783df1e89de03b764cde27719b9536b5e4b9f15842d91513fcff1171e709145b5e5f215721d3f2ec845743d4e522e9f27aa662317ca94c561e19a62a3aab50e1d093f097730e2abd72721923c2513efd225ebd9e4b87073bc3c5b2bde83296d57572211f90af331fdef28dc12ed2ba3e3457e2d30e25d1cd26a66b9948b4682c7ea34143d8dc9ad72dc0c04b2a9828c7fd365f728364267489a5defaad4506e3528fc2313b419a3513eebddc60db948fd08df616379ac05f981d69e9e6ae19d5216b22b9c725c9f56c6c0542f7d4070281c3d16adfa7280d2f7846be2a75c73b99e5e230445761d0b8c4cbea2c0b6f7bad692ced4087f1b72de9ee1385140d3bf59390dfef89575ea75a6c0006f14bc476665d96fbdd305c046b7785bfa3063e49fe20879f97e36487df6604faaac54f65709de50110e7f64b62567aca3d6d1478b222ec9289fc1f61f423f08706ebb039d8a224625cdb422ade922c3c0d15036b3e80328f349c0cef97e0a1260b8eae02768c399d5b0f0f29946fb8a18437fda491114ddfa6945933cdc3521ebc1f70b5a3cffe275b5fa04d6d087e1142dee32f46919f1de02a266f6698e3e1f7326ea2c0e5a0e45dd29d22f412e7d39f0cb8f0fdb140d8a7588a9664f65018b4119f5a9d841f67618f140b5c0b43d85b4765a1b292e1bfa2fe17a6947d95f1abe578060534da85fa9478affe42e5eac1c93ebdf8e050d285507fa80e7e8720eb1212f85fad61f1bfd5781c2f60717013af98afd0b1018f1d3953038b40c827b29ec08ad490c80187c112ad8cb0516affcf68d114715b47322c59b0e1d0f0d7d05d85f30e3c115d84a815a7ca750f54e65380a208d1797c705b503bdb86142e1b3581dffc3bab4936c6bc2ceddb9ba58168ccf34492e26801b14cfa1d6cbc813c6ca494101f98faf1cc503d28cb425effd439e81aa2a35364d8803d7a3099f3ec344b02fed23158c79ae5cc2ad43741d6cfa830fe21448fc263e12436347d0d59429d15f024abb4c992b911607b0e4f3a31d9496830f80f0049b464f3f1d4cb46954e071c61399cc66260135a1c79012993d59aa1b218b5f0f80f334e9274dcca5fad1e7a857adf7674bb638a9d0e1392968058512045719e05e2b0b9924a33e2769d059a9e92001bf1d8ff4a3c467d10c0226515106794a473cdf41066d07fd466d2d26e02adabc066365f55b758e5a7f7de4f11c1c63e88a4dea4ad07b671448ef5648964f5bf16094882acf6039d3d7eb07fd350a575b3c32e6f01c55e761b811e7a2c76e2ea0c2478f11844f709f68a8ba186d0692161774697b1f7fcd284ec73cec608c4d4970b58619e605e6c720bdc0f4a50ba4d96cbb1641bc70af97f43ee092264f76633be11a88b4c693c07633537e850d91d688b0abdfa27ac8224656f773bde1aa49655d53de94684fd4469d5a98251e3a5b80127d39d1cbcd95ac6b9942655b77c697de2a43fa9791a27c1a6b50dd1aeb9197a93cfdee8d8c51cc1603f26f631a7327ad19003afc88c47469cb195b2f2955b16d08495c1b318cb0b58965dfc7f8c37faf153429001e8c9526753fbd07480dd9cf394444fbf38ae59ab4ea32b0f1d98d4f5615abd67e895e03f9fe072ac6572e3170094d4b8ba9279275f46b832a091d5345ead1b14b425157fdaf93112579386dcf605702a30bceb04ccb587b8c9fd67db5a084ebe2c133e1f0de76153de5b3e8e2f0089b682f578cc0c83be4bcb066e2b8d23e41bf850e18ebf504056a78c10aaf829a5613e43adc2d74df5317560b0f92d1bf9c8a7e028dedd9c914ad2fd6e06e8b56e1043312a8ab2562b8a1811e012f39db2a42d53b53e8862002816727324c27a9099190fa3b9d4ebf24614ae352a18aa9853e9e51e8e9fce807c16f4d1ed3fe76046138217835250863c7372ee35d0e44df6ae7f3354a030614efd55b8fc55f11a1e630f4d62a90e541de4057b60f1e340fe299aaec97842419aa5aab5d02cbb119f8667ebd6596282dfaced456b243a1017e636257c00725145c8c1a7d0ced612168ed7785fbf218546cc3773b80d8fe9a2cd899cc67ab2117ff739c3882ac173dcd0997adaa4836f1d565c5fb896fe92d36be4ea354959626ccc45e32ebe2da8450b4c324e9c84e859826d026dc527092ad112e52ca923617a5a64a750f9973cc25777d4e10bf1bc54f366f5820efd2a4d084b14fde269812ed4006701b1da3b123cfea6825b7408aa2fa247b2d4e48e068400a90ef01fe21067a7e5de43254e905a3a430247e8e945fdcf8301f08064fa14fafb084f0b60ff3820f3a15db26cf3b3d472b6c8e568171b75e01a0687857ee3d697359e81d1e7c35fb460bce208eaf60da69cd3970cc07431a6c7b414c3f14335b6ea545082978787372cac6946fa0a5181a8edff08ae4951f0aabe83c4327f9b16fe126542a07056e734d522aa8e3beda35baccfbaf8f8b07d5861f0a93d872676e20d3c1066991d1944305e34c4dfef3dc1880e5df027313a00f04b9ed2f5c98950ce16a0b54648a6fff62bad46d6201674e4f6c21b626f6c16806c354c68dc9b4aa6c9706851f4626499889d5cb3c148f4cbdbbbfeb07e408def8f627609f9fb4b796ea2f6f8f2aa0c153f339c07e47b7f2a875ab7f7ac5cfce654ec42c87600f21d31616b1c342cbffa7ed1b1d4d288a521634153403519363147c9971f04133d89971045398001304fd850beea80c393312d40c8555cb03bc562af7ac3b349b1bf2cd2b92ae6a6792c1d2f64fed85ef4c1a5c6e8dddf8f9a579a54c916c60e82047dd1600e5431125d76b78b8c2a098f18e46f415869bd5dac6f83169c2c8d29f6b4328a0912196820e9d995762575d0f0debeaf9b5b0e2d270c5ce3b76083e36e08401f94afce6ace19ea8fc1fbb1af3f43e109914b7abc6a65fea2ab48a5000af7223958132cb267dbb555c4bc2399e5426b6b3a27105f22c707becfbacb18ad58306fb6fe953b77d8e16475a79f0a1e4722528ed68fea7f1f534c99c8d2db904cc0cda8066d588098e2046d1ea0630ffbfe2011ec234d0c48e8dc6113a19671866253d11e40aefb19efcd13c463084ac484b02f2dedce09c436be72443f4213baa070dd14cf108dff6cb696f02cd05fa5f3e95bae74dd28547273cc1abd77cdbcb00714b116dd81e1e08a4936266aed104058d594954937b52d87ca8ba959a577108d031a69f7febd6c4168783d44fe7287829c8a07371159878bd11fe51cb232f0921f082d176de4cf1ed7bc8b76c7b56de66ec2f752931bb6b6194ea26e0c6f72a98a7e7145a8975a74091cf36fb93f433c969dd565b1bc5cdfcb205cc15c4642f4084b54cee37e360892808ecc8dff740f475bc40d04195bac4a03e9ec76cad1bebe7afd1732cf16794ef6462ab017a80d75ec20e745783bb35803f4b4b617e11526b6e094e5fdbd4f2ac0531a43d1106a93e8052366edba0ecd93ae8e04f282d1333ca58b422ab5234dbc503685b3b1fc93606c1aa5b712b2379e365bf55a51e63f83c7934394efef92012d8cb6800663808ca41d82a5e76c71495a7eebc7629d59d3741b52d96dec1f9741dd750baa5e3e411bdcf0532fd4a0b394a9d911a13b695df6d5ecb4503c9dad0b958664def47dd332b4058bc0e9b1933ac7503c92aca8608d310e70c33a422f0f3f5417380984574f512371b28f66cddfa883c4006b04a9b3c7a3ba0f6804dd6c4df7feee728a5632d04660c6d954179eaeb44542d11b58d31f9ed821464cd0373912dfe4fa22bd19b7065abbc6ceb26a19981842a3965530707b4befb527c0fda0a0cf69e63eda44b13ef3d14863b69dd6978131b11cabd86e10ffed5d750876db45ed410584991599cf4aca734bd4bf51c781127c6ea4ab85b240cb1bd4facec08e873d35dde95121dbdd9d47a8793cb4870df0a1b9a78b9b685580c9dfb20a5e2a15fa3752130f183edcb97c5be401beb6cf107c0adc97caab3584251942f7286cc06fa9f098e3d246e7dbc07d9608b6f5f991a6cac58ef9eb3e90c9f8fe72538df09b2b7b4a577e8b5ad0a2dccc52226746f09b1124b9ecf6a30f133399dccfa765b5d4700dd4b3b9bb71bc2bfead45447c30a868eb5de7f9664fabc6fd95a16fbc657ed53652cb805c3a819bad87ee0eeea07613f74b099d871c110ad32d1351a8aeadf23e6d0e204def4439207b50b2d4202383e3c044f9459cdbe3b7bd460b412b228e7b2f765cc1c5ef19eaf6e417be81389227ca8073c6176027f2d7e0e8421339891238ac8b089c5d1ea4396b43b650d8c183e6314a3c894dd7e6e201332e5c474a08a6047bad68d93909b92aaede71a80a43546806bad9165fd9c426fcbf14a233da388ddbbc7a4b9e6ea3518db232d05a4a41555db710f110c8f3dec06f2efee927471e7c2956b89410d14ceebc5066d005f93104f18c112b2dfec079cb4063f6af28f97c50ff62e6e6c1e5b5d970ab0cd7969c070c5ace23eafb24c3504cd673d1e5dd2fcafe0fb5c5a8116ea8414c1441534aa5ce52008ee910e68da93e170405862dea3ad15760000906447372ffe6c06baccfc669d8c0abbbee6fbc66a530df1c8a15ddecfece85c3e59c99b2f408d6225a32f1c8a5bbcb092d81020c5c943918ca370ee1066b9cda7ac237f25e442ce78c832078a1cca4ed502d30f1d61f4cb9b31fa1c3339535e317b7ad5291c5ab8955c5fa35886e5ef299dee976d839d1659aa0b7798f3a04053fbb3e00a9540ae0e1e203b830e5076fec300443e13d97564007c833faa0afda19fe75d05b04d2cd5243a1c81ea8ad4d236a1cd7a49e82d0fe8260ef7db074dc985bc53273cabfa4034402fd5f0be8f50f61d64063bed010ddf545a2a0819da87aabd2522b9c66a1023ccdd30183e1c297ccf73678f5342d0f6c3fecdb4ea6549f6f9890570d83521bdafb330e97cda239c6c72c27822f493589f2c3a9a7bc45dab9f230dfbb6680e8f3f8d2d51c3d0ac847f7a3110440318e046edefb4d3a5c854747a2d517240483a68a859c5a2517f9d257642c2a95603c14929a57100056111310713bbee85e2eb28d2f56212ec3df83e9bc2d95901789082be5eb67ccfa5d9fb1802b54fb79467cc5b7d9105117cb0be5c3774ca2b71c5c6ebff3be8981994cfad0e12ccee44ba1686ddb45ca9e05c5e827cba306084515b046578f11a9e25b72c089771b551d6cda59fe84c62744869636eaadc43ec56d5050f7fba28c6bafbe32eee5865d3d6d2f7ae5bce8ceeeed068cb1f022728177e68ac18ffac673bad8d2e5ab2e2f406cbc4592907bf333bd5fc49dd9e01370f3c7a9b006cf71557d51827da6014a008f75d47fb0c6eb5f92f4967de88232ad37605c6100984941f681513d9f213a93bed129c2e3e04ade7b03081573e8dee5d8efe325fed0a720c9810144f53c1a953c7076ee5f1762cd845b22f20de501647c107b71a88d034d1383413369152bb657df8a1586b7fcd8ba48363c9305ab33f7f5b244b97287b02a2d924fac5ad4344918be6b863a65249135b38df4166c6d7dae73a36aca560c91ceb203fdfcbbd96b37cdd2a655d2db5fab215991015eb7aa3a6dfef01849794be8f1159c8fb2e0ac69da8979d7646f5aadd53063ec5ab68bd06439c2b9ce469ccbc19acb857ab262170b62d959f7abe12547d52b38a70b048448428f3d56e7c52e029413e913cfdc8db69f7e7d37ebbe49e29c75ce22209ce58b4ff9f1d9fc9fe802af5b6551d3a1756dfe71637f61bcaac595f3aa1989ccde9acd830305f80fbbd2d16fbd3956488af15e5d4fdeae1b5261646dd5fc86d84bb6b45793e77695de20fb8c84900d6f5e8f54a963dcd6d42e073ce04fbe219e2a4af3a8fcf31c952051db25526c091693d249eb293bd4503d1103aecbc69bbe5bb5631483f1b9594442e7fdac9e909c715dcf47095e393e79f8d5b4ab497648032f1629733d97ef3ee1d8878696954e5fa815d5d1ff3ec0ab8ab9723d6837924e59169682902d3db110d7cd26eecc981d01bcb139557c486c744018ca6bd19b5c388a7e077c6e556f706f4ddc0bf4429db4217165962bb635099628fdd49630587a94143c0c89ad8cc0243fa20eb7f688550b80136aba7bb80ef53272fe7d205c34de7b1300c99023a07a06f52b53dbfd06e521156b855efb8221e0f69072c5635a904f63b7d2d011126e6720cf2927e27280d4518b1cacd709c5918b022b40996e1768dd9d3cb128d1c9fd2a5697a15b330812eb172ce016e47f4bbb5635a1a22c618e23281b471510c77e93d57d882bdea72c28849a4c0516c9ee76b492065afcd0c4fd86d90de9c135ec9a551e0387d1f5937023543e78c0f1e915cf6488565eb451ac3b3e20a6602c1673f7387f668a16c81eb509a15ebe643363b15be3a5b325e0221ec1cf0bc3061a2eaefe0bccd271a1291add175632b619454843e7e1bc46e34441587b61e00a40a910d9fb377770ebd08cc804592ea16e64684237cda52ce30381696281719ca3144b11fe63310d8c20cfe793ff404ae5d1bb37990c661091a17c90f9dc81a12f6e7b6ca63ae4bddd5923718d2b57fe6d4f11c443e06d49767c13e458bca27169799b2d6e904950b62d0ab568bd90296e39f8c03b2d90222508dbcaa76112df2ff50a75f281063fc81340a1ed747062fd26481f85b13c2de3a476b3a73fb1b39392a1f122f0c51a5c476d7895586763837029da273e25e111ba071d6dce602a13dd662cd862fc55adf37d20d67d523839c127dfbd1845c858051e193399c2736016c9c2ff55d2c1eec1456fc6d05a71007b69fa06ffaeff35cbec075bc102dd0e482b0b3e99e947f8c7f8531842fa58ce3f6f7111c6e52d0d54a16365fe52e15426bf9adf5031e27dac309386109c90e1e4780b9d03e42a2f450e2018f902c6fac8c3a202c1a526fe1c0c02ee28473be3fa4bb274cdb80f39cb0f8c958f5128a6d2cbcaf93586738ddf271c23bec5621fb147d08c561a89ae924383c048b086408035394acbc323bda1909c591844a75b4ac76486651352f0fc28cbb946a0d675cdb218ea5d78641584ec8447db6ec0d870e92199d0711e092c7d5fb7a1b0d5b8659b2f4f09d665914906900169696e026127ec615dfc7f72172d4e9c9950e40b402f05affb26f8e17d638becc4998d96780e0dc21262cd79bb5dcac57f00f9fe8c5541078e8a05994bfae65586c513d8e93142fd0f38e02dd1ac04378db089cd4f5d445f6c772cc79813e2803f220cbfa3429929f5498ce561493460b3613ffee4ec8aabaefd4e078e34431c3623277623cae710755721a6889d28dcb682fe59bd2a912c3a0ea267afa0bcbc29decc7598452e8427ec96bd4e4ff7dad93279bf4d91fec451d7e038f87a15f6a8a4b39f7836384879e5a558ac7f12a5fab2c732e618b33458c70a287b9c392bf96e15d064acdb6c85b4ffb27a8d6c08f9403bd525b914f19ccc4e0752c1ba7303f3ba9a414e6e620de995481fd369295cc085ee0d4086cc322511612aeeba8435785e0d747f6ef41113e99f02daeb6b9b016e04769c7707b166fc721ba312ac44869e72db339d8d94f12280ef730872d27180307091a47a51ff40387adfff6cfe5ff23ca72a089110af0f3877e2c35c0111199a2422a0bb1b19e7ceefe12c2683fb846c5be7a1c6f60aaf4ecd576c6b2c62cb5a0bff53fd46873a6782deb555edc23bbc11bdbba75dd1be6a1ad6cf829f70072d47f9170994d097f05cf07012dc2b0b4ec8dd4d9c779b858504a67c6833c22e7d6ac42fd50951f66bf81ae3a02f2f806dc1b77d076b5890bc4f44044ee2f2eafb480f71d11792aa60ad86e548a74684fd1aba10723a92b11ce1a7dd6a5561fe49c7c07442f6276a9508e3f805c833fc367856062c57f21f63a55942ee8fc2e9471647d1417f060bd8d0c66a27d1c1f3901339d4697ff39e6480609fd878b176f95e7a6596bc761cfc02a612cb5807b33e7803fc5b1d79bc27eccc7d3f035177b4134650ab8951b1539c16d7ff813346f0ba46042ab279344cbe1bfb4ac912b800fca5c41a1e01e2e48367606d18a6ac9ac259eab77260b92273b43a0faad0559d05bf0d90c3d1f7b74a54cdc688f619dd581aa440f8417492d917847623a18ef5410b04c88ec70cd12b19d1b4d506a86d8b42ef83bba4b3e5af3c49f3e8026e2eb7d9fec7687354ac5b0887116157de802696db73422719c1ea40e944c3229d5159ce10079adcf9ceb5ff67124799050f8ec9e742b8bb1ab15d470e72d890af040425dc1bf62303ae3a3e7a96d2a43c893e80ffb8a28f4111b111aeb361103ee2e7d8024fd4e508efe2e2c0bea5a56827b8726cacc7dc08fa96011013de61510a2b1b058e3f643c735507536f749772c7baf6cdf30eacb40473ca90c745323467fd21411c1ff380b74345d7e144ad0bd7fa7d7c1fc7251fd916348bd38e0169732cd4a5c892c1ea29cff802ca57db4593be104a408399a46f970c0d6f84325a21b7cf69ee061ccc2a3a50316c840af78877ba416a90b831f59bced3bc2f42a89d5a9b0aa377090344502b3507058d31582b403b5817becf3abf3063e40611dbca2846cc99bc087b5f2324908a49e5e08d09fded93fc0fc1a98e7dc5279ae235dfb49b929674403a5e7215847f2c4263f4a937f45683395abd3fda138a1e8041e083ec5724df8f5f59ee81ab3d8f91243b4748ecb153908f8ceddc7b2a6132bc19e5dbc68d1159619cab24990dde0069d6923ec90219fbc634f0ffdaebd6e2dec866563e71fb0b99b341a3225d9941ed6f84bfdaf764bcf58829b65008f8f1ec19284d1e0d08e4fe2bb65238804b564e62228659033401f5137bffa7748ef26ee4d3cb886995198691f9bd7f6821dafc091f004ead76deff2afa175022fad1d70f5f312bb20b8c34c9b38335493d62414b52515b4c70bb5fd41efeb8bfe54027b2630d780f4d4b4c5f6ec6e38d9ac6e64b0771cf910a02666224d7c9930a400d381db895e295584fd6dd968103a6d4c18e438f5d3cedc0b55b1cccb54a12313c43815f9ee10284257ad86a0ecaa993455d03fe2c4d54daf8197032b9baa401614ced4399d79bb7beef9ce5bcd8932ae1cf525f565402887c83aa92456761d0abd91781abfb5469a2640f41acbf4ce293417771fb35f5ffd261422ea27deb808b8f1e7f676bcb61968d1cf3fc00f94fce26a573eacfc8c1638da80d4eddde126a10c89af520a37d898f88198aafd4d70804ebe05448ad486113edb2b2aebaa18e964f683cbcc9090e294d7a7437be1ca1ea74dcb36d84a2321bf301db8193e2b94705f8aebc28168b8a51c4ca04dcfbac794fab48303ff71240442ef421e250fa2c370d0ace9c55ea508ca6166436d03d3df8315970bbd8bcd844f4bc68f5b06299dab59818f88f7e301b5c4316aeb59aad5d5f7097eb27b86c0cd57fa81c703af3bf34b8c520cc3d2c5a52747788eb8199250fe53a397e6974413b277b9c70a27b0ac6f69139f75109d096a4f17c88c4e93bb3f9ecbe10c1dabf88f8fa6981df485dcd33951e7e060d99193bb414647953005e6a41a281ae92fc0b106ab1d0c649df7df67a84a19fbe57e78f9cd919f876ceb2600cae60afb4d422a6daeff0f3e8b0010a88b0bbbc353e403115f4fd89be0290c657993caed524b65b9e71e1354e3d251eafbfdfad286e16e963cc476f575b88f39d75d4aba1c508b0202150a22b7c9062f93aabc32cd5ee70b08a94da92dd9c39e7d49fef5343e8730231d1cf3ebfeb1b25dbcfb218b5dde3966565d22eac32cc996110a53c0dc12a9721325df2ce3db03f36341907cd84122d2a897bbd9e35d2c5e2827a496bcc38d982c2bcb25cf055472bf29acc26c66698133800299b78ce48a634e6323bae6db88f7211ad69ebad87c01ba4d11ef6b763ba53395eeaf888db5c71163b7d73cd536771ec8d7e9f205f0bdeac8aecfc6ab04715f93f780868392380ff39e7999c6270e0d3997a8e3c12650b84b34333bb27e91818848e7a01a41dc8f603c55fec0e05524fbd9701f9695c6f1a89a4fcdd01babf46de6fa4f6a1abe626ae1e3fb09ad630ba5ddcea5c34b3d7db938ce41fe53f504783b69bacf58a3153bf960af8e7b92248d686e4891e170074d3b614db88a20b1a58e021253a639e04e495a18eeb9502446d5a60190f1b37f202ec7e975678e6a91bdf5f4bb15cca09c259f6d3ad63214fb72a1b7f6880ae472304e65f140fbfca829ca4ebdea50884189a0e34ec4f80d70eedb8edd7539e615f29015d0a2a3533bddccb3ac3319709615a7747ddf91059fa99e04f1fd1d6961ca462b4ae617c861a104f13758d93a106de1f3ef546d2a731e0019756656b099fc3d53d47386b271558ce7f557490f78816a160b9642303f16a6c56c5783ffc1f2bccf9b1c2dd9095fe89596add52b8cd0f27e17e0870deef8f8bb27bb30aeee2710f187ce6177eff64cfb0a0830d2b2c4424784eafc0f2277e643aa11411f9562c2484149e3ad02c0550338fbbd690aeb6eb7f458981086439d92601f693280eb07e9b8d53730775f628b1eeb9ec29e40e1e83b45a015ec2c7f6a2bea0d0a76c6cd0d4eaa26f5b9d8b15f7ae79a36f878ec6899ac75163090de4856d27388b729d8cb911f2dbaac4a61755208607e4d59a2191a1e8a0f92b395ef84f5cca72650a5ec96711783064e0b3038131aedf028cdd0de22210c7e41132f4dc554c726402bd9867726f671be49f37317ee6760d69c5617b6240f2a0cd8114083dc0a60525f9b94e16a3c61893e7b4cd4f91f030662ac3f32b00e35a3e8598f3df274610f9957ea5be083acd8f25279cda1f89f8d5c05a0a1b202eda0b2b2846b1899e37ea945922422c83846190fed65f9e24351f0569ff47d29f0fa3f89529e14f881940aa1a4841ad144c7c7696ea6ccfc4ea4bb7212653c04f248c459caee04c2e37d3e7dc683dab19dcf65ea2b26c5d7cca848625d7efd0dabbb2426915687ef3abc3b069ec8307c050d038c74ab190645aa0dccb1421c04b258ff3ed12c8db75b58a14309d2e26d73d52eedbfb78a0776ece8ff981a7b04b066d7fd4c3379ef0de749d0f0d4cb2fa9ef181a6337578033e287cd292a492a7d9fee18d21a08545b65da4aa812835303c5d899687963bd441be43ccc703e23483ab9796681a8df92a34ab2b18d397028867323f31f7049deb704611ac9cb12fee16283124c9b2428cf4c007af6be15d7b5031a38fca9de3b791871cc113911378a4ab56d049babdbfe707e94528f5a3767e39df784cef4dd84c2f1fbf6ad21654e556af2b701f4a90b0250c325e831293f5b184cc33dd9a4ad1eb0593269089f225f0ebedf94b11fb6f7be7d859e0cfc3eb14313bfea9686703262927b4b146bd2aac0450d1a61d6759af2ebbd40d8eb989f86399f60a5667de00a28fded06200c29f6eebc1eb9e47664d46dafc3afc47d3a4ea9508c16cc7c0a80dde88a02e592616956b9a090e0d519584e21e33221560cedb330523ef2b7c0ff50acfd146fd4d838c633a57c87c264b49a494c28e364f8ef5c6f3fd011cd6b32a0c2c02d60abc970afe83e55897b2581afb4dc367930ac9fffa440dab2ec982dd410b9287a000b0612d4b154560cd9b564e94c8e2aae0bbccc7de765a416650df2174519caefb7804b23bf526429bb01c70f3480caa10501782733990703005f297ee2051ebb04e3c1df02fa1209f95314f1236c8aa37a21641a3bda5f949d8292a718024bad13583e6c37df502842df6e1ce87aaaaf5e5fd5ea2bbbe56be9111b95f20882e244c3d5cbe120a620326c3cf2852b2233aef66bbc6d20a3bed7394c4f49207a510fd873763e5f933d6ec9552bc5956c7c16e579968778d059dc5d3b95f6009e8c3ed9c8afeb8ee33bc36a209435fca7353b43d7ce49d0b7da67d8c3384a", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000adf8670373d6f0424af5f29cb946f164000000000000000000000000000000008f927aa40772310f80a858405907bb7011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" - }, - "decryption_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000001f3e77d68ed5db4ee000000000000000000000000000000000000000000000002824efb1dd50dc53d000000000000000000000000000000000000000000000001e287c5b545301ce400000000000000000000000000000000000000000000000000003ac30f94551900000000000000000000000000000000000000000000000425ae327975b900880000000000000000000000000000000000000000000000030450cf304ee5f67d00000000000000000000000000000000000000000000000ddf49ff2fcefc54550000000000000000000000000000000000000000000000000001e6a4e87b9a2000000000000000000000000000000000000000000000000373d5ff3b6917377d00000000000000000000000000000000000000000000000ae9b0b68e3252781a0000000000000000000000000000000000000000000000078bae91658ec6a2b20000000000000000000000000000000000000000000000000001edde5cf5eeec0000000000000000000000000000000000000000000000024db0e0b9187cf4460000000000000000000000000000000000000000000000061da17c01b0ec38f8000000000000000000000000000000000000000000000007228437c29b55d1c600000000000000000000000000000000000000000000000000013d3baa2598e82a7f1abbbf32e6984fcb0010b83c6c7844ea6ca3fd0182ed6cfe12534a9a1ab32d95760c1b06880b626933c5156327a93ae0c04616b6f2cfd6457c4dfeb0b88f115d8837813343413efdc6428b79367f2cea1f6fa8b8d3ec221fa34eb49abfe209fe2bb0aeacf24470fe359956c7a9d3196c4d59810478f5e6c219a0a1ce604b02dc0bd84807a39b811417bd98322a228ff199aab7bf5543ac55c50a2b6509a607be7abd06b0d150fb024b43f0a0264f8f2a2258386a1e23c891c2cb0f8bc84212bdf036a748a87c8949b4fbf035079fe8b10ebd8c3b4e7f3ff7033fab3de191217123f39e02abc543c57376b6a015041fc623668c649686560fe0fdc1def2e6251b41df3fe84d67e48c15e23ddb2a83225f0edceb2c59758b5bd5faa1af92831ebb797fa8d6559e379f0bd0cc77e360b78c17a1e131017d4ecabdc2ee16ad021a31399184a8a78a5ff3667d4ba9ff5ca43d76c39055e64fe36d15d8be71fbf62733a4eed75dd54386c0915aa949a3ed5710627443b8ede3bffcee932e416b52251aae03e2369a82a51fe16dad9fef2b0e96efdcabd3322c1483aac4f532a1171401989403a42325d2afad1973d1a723680a168d3ae569995f8a97a7aecd777e19cde1b4ff4744fb82c63a6d990d2185ff7b003862b25776bd65a5ed0e414fdb227c255f71b15ae0c6933ed5702b3e729e7f826a590387398b121e06df511d5514fac1e05b458c10dfe141aa13ebf0647e24c0765eca7a633a6d82c8cdbf1e281308f82f1605ab9e282cf3731ea329850e8463f81d274c6f12c703ee4027535a287eccb7bc38c8523ec425377fb9e706a8c53c138c98c517ceabc0497f3845672c3b9942b4ec1692c479a8dfa18e55c4ad68c6519198b612a79177ef62c1ddc02d7842bb16583370aebcfa9a055bb2811bb27866c4f05557866b524f27f1e17503d9ab172ac8130eeb870cc0a647bdd39634c84b59cdf975ac0a773c6cbb151c1d126e82ef43f98e082640c22339e906c342e788aaafac7bf0f644097be2628e0232986c6d79f5ce13059c444f38ac33d416a25009f2dc0f86de79f54885b0510a86856cfb4965ef34d9bcf19bd9ab16fbea080afde613668d967b25fb3bc82707f9ec76cf626377bef45c4a0d2568adba49e805e59649f0695412b947af0a520131e9eaacec50553d90ccbca23fa3df01c8d236b1c156382732390af2085e4c0d7b8c85c65f3aab8fab763cec04cdaed0cf90145a204081c1ed3d483732a4ed13d9ffb221838c5f0d8ebc8c9aa98357bcf7fe1c7702f89b83bf5dbea988767c2ef522f59abf597777d99e620b501d519bc981fafb76042b453d1a7167e91a2e1c2b5edd2429647eb7b408c757e9a3c1c19c49bc6401f10816c3ca20a8d2768e19838e2be7eee84a64b232d47264296afebd614484dc8d735a5d28ddcc2a0c840edc31f87870ac41ada9eae239427acad4010ade9baa676779c222f23ebf5245235875eeb04b748a32a4aa8d9ca4cdfe03450077b68715308644eeb2cb1a68f1006d6d41a8e984f8e1114350487c1587996fbfaa19d2e60cb1175558e6a0292c0ed5ff0a5b855c452584fa8af47335737158637c2efd534da21b3853bf89f9242f9c279f94e8e47a25cb8df25ad1787142eea7c1e64d248709147b370f9fb54e25281994695e79395c7c6a8f9506b17f11c9e84beb4ca71f547cc5a24b737c1d139df5ef48dd868001c3ee1213fc3d145b8a0f1a4df3ba5f3b84041faad9c38729836a3cf695c826bada884a9b2765cd2dc3c519a142fe6dce7c61ff3c35b14e1d0f88c8c1042161b3fa888a7f10bd499c9fc4cec04fda3ec346eecfe4b8e64117ba207695a64301d974e223e8613159d26d2466c0bf49ee276f880a715f536b1252d850fc51c0b123ea14e1ee08e305ff2af518b23394984fced28b0d25ed1d053723691e73f00810db57dc33e7668cfa7ee8ea82dfb08ae1157a7ffab67bdc1e65810fab2b117de99f771fe66e33edd6842ab29ee28449a3a711513839b6c425cda1c407664580d3e7c4a68849d1c986366e3ac00ade2de89dc36065bdca0b272f4c4f8230570158f2d1d5f1131d7d7dc9abbd6be93171ab7bc99ad78497702925a81cae48f544226832ca5cf1cd5f97732b9537d9237258c8b8511f10723f057859047d53d2b70ffd0049c6ec0514ec267512b9c08a80a91d5d597b51c57e069c3a0203febcb8ece93238af70b491e7738dcb4fa26835c6dc843869122dfe09dc4f1773676441f3237c9b4417010f6a26a914ea3db681688f37d2c1f749b8111f6705f50a9b212a1f112a0be2c92ef9fbe0890df133252d4b59510aac501522c73c857179241fb523b6cbd447f6e20ffb3cd52797ea66d4c6fdcaa4d73a770bd4db1d004838ee5b6ff558a35fe31ebbe17211aade2d97750277a00aba976002bcacc9932d5aeae711c5c0e2b80ba242938e0d52e1c91ddd667c6f2b74abbf1da41a0b6c35c00e17a6ebc7901f7808ef157a01418d01949389052ac91b2d711c40a79aec70f73e9fdffe0ca944d1cac8d9e4b5ad2601316b5bd7e540f511662ab6ff3f08581ffbf961ee342499b951c63e998e60f4ab34881c9b8dd4e2dbe310cfdda4611947f2faaa558060914e3dad366bd4332aafe4bfc4805f6e84205304a03965d812f279734680b13e6cacac7b23c2228b1acbb93f4af41aa025bbf91ab9a9a42630a755b364b3ef3790337d1da0e315388521831502a260b09813081dcad647b64ede244918a587ff629d2b160c0ed328617eb4cd3df037fa8c30b62fdfc6a29ff02714877f0a4a4f09c29a9af2e85e427e502427651a8e3b304c361d2c9136de21197c24ce33da03e917c925be1923321e11e36bd043e1f730e88b2f174635edae1d9553e512b035b4e0b933d12716ae0346a30d6bc1c111c58df925b6e006c118da59d5e91142cb123ed18216a9a34235d2a452ac4206f0e6550b07c6ecfacd2f7fc86480c80dd178172a575c26c299c307ffa1b8b0a93f3f01be15b861d10213dd8c560e919c5fe5a46c8ca6c639fdde608dd918de75d6567a851b7680b297a2ff4259a48a36d482d1a698a6b16c9df14176812371e52cde3c222f843387102556dae72046097464c095df1ee46c69826480752cf48d60672cdd0299635611bb07626a8273bea0fcea5c90d886657e8c4ed47b62c3aef9191bc612aa8d7a3a8bbff9fb80690060ff6b36c102475b9136c49857634c927dcc9c481cd72807184e90c8e852a0801c946d6411cc2a195f398be1dfc37a2ef718a3091ddfaa1f317f91b550be51b86e43d0ac570a6c47ce50cc98a9905f886e44b69a02447cd1ee0c5ecda92b2d39872b82503583790fff4a195ef071c41ba778253a069b853bfc3cb73bf9c3d93c2ced9f9d1d9a9b44ff81145c0b9a6bdea82eb5fc0631a206074d902529dd3b03bfd38837b9295763ce9a173990da54473658fd092ff440bf4cbe8c837d5c828c80ae4d66f944545c0e27fc240ab1cef2f95b043929d25f3ed4e9ec08892ec6721c85052a6c78521f0feab2acdfcd4c91a7437e5d202aef919da4ecaa22b001155f637b156ba351e5a052c60bebf6953f295b369a27d042baaa94a56aff86ccf43bcd34f6e6080dae752c359e47a030ee0ce2921e0ce36acd288f10068d9904c877c9d62a7d937318f9f7c5f1df900a0e32eae9932c8f06a0b83e30b0b225cdebebd3b42a7131f4893cb5933ca34d249e7c78a9dc022560ea911eb98c0c473c4feccbaf919ba5e1ab1e9a139672bb0c81f0de2d6d236afef4ff6f79135129f6c4d375d5b53d96bf96bc04b2e36f1dce1ed5dfa982103fbd4e1941a5f51b4b114d45ca0397d8cc0b25e7cc9e4efec098fc9619fb31301f54c3195c1283407e2b520120c77daf2b6428d78e5ca5ddc13021c6505efe15f88f53c947bfc4408bfc93856fbdaf8a9c72df53fa60f0a8c91609f67457c703b6fb5b5acbf95186219605ee9245f80b948754af2fac1161b89589ebff5fa9168039fb3c5e72aa8a2adfcd6aa59ad3cf183103a5c1313dba6f6d978aeaa96226ee3f12f03eae59e60fb73db15bff1e51ae67cf88da23bdff5ec9e0b49566fa1683a4f335e2ad10bb52a7445b05a5765175abf26e2d8a05620767255f88f9070f0f0fb28cea2cf72e0745391bdad338e1b728132537f3495f2343514bcb1b3b0d8d39c1b7b17423a59059f96366f5a80c6016296951122774a1bf3f4176b2251fbf1b33040f6c6437af3d6fb09858b7972dea5c1baee1986cbb548c465e502708bd2c3149c3e86d6ad2452b70102fa4a3c748d65ae3a5bb2a90c6de79f60f6f090b044848ebc4579d7cd68132a3fc3a16989090cc37ce21079a152cd3fe47fa1fd48c0313cffed38c172e64da1a3f89942231c2113c5115e2bc85c3debef2ea054c043d9ad9cb32cef65a18f28f1e15b7c9985733c69a52eb6af4287922b6222f5205113943ff5d8e1ed099f5299cf8240527200186239863e36d4a58a2a5fb065d78c66b7136a52ef53de8e5d0319f5d2ac5d1c4b5eb6c6c31eb57e8e00f3b05879a174a1da0394f7602dba05a7cbcd64b297c0650eb8980cdca29736045d724b1959380c73fb0d631a680244a454dd3a4e0bba490eeaaf956f8296d39cb9a00ac3a4c762a8d11fd8645a19ecf3d149dc3de9a91d6fc22fcf41a3f41cf5d2c2cbc5eb7a019d0f1abe491758a5a35f9941edf83ae783c1009d9b32efbcd5773252cf6fc0899360b17905f9665f760f0d05a383645404d7ebe4acbd3cfc0bebe1a3e6067abe34217bd9d63a445ab7d62814ce52e0c7947b155bea4336495c7521729b8332e879ba575cb2b5b17984efcfaf4f2f0016a92233578f09cc64c1c5226e733c382eece007d0a5b2792b13ae338a8edd9034811a0d5a18e1ba453107c2fcd495401b784847149562b319c2ddf5434b54f6f98a55010e2d151b34ac1c61f0f08bd81faed2b6ace599d4a68f5e12eef8b2fda21ba86dbc6307001f9554a29d40fa86deb11a05a25c8dc66ec9e525526aed32573f30014da08c2d9382254292bcd80c5fb3c4aff19006441bf4090d3f5e72244d8967b068ab8a720e2f8f8242a3331efa61b12eb0f568148b190fe0aef1655aee8129c611ff7ab4d2b7e7f0456931fa078ca2b6cd155f04f7a8cf31a2540ad143669812e18153379b610f11a1ebc35a1e580b55c355adff570ccf222d05ba579793ebf482994816ecc57aa1ceb6fe0e137c9ad7d23215013c3d02755009ec47dd527d366d54f7bda9c80501b163775e5f22abeefc0e4d0ab58b3bfbc5c9a6971f5cd29ec40cb312fe43e9b09d53ff3dea50a09c42faa82a943c4cf5b06cb0c46d0e78564929a69cb6ece0c2c5c935dfe7dedf4020a9d2344467a8776559fb824100d798d988c0dda97c42622dc97061e54e3c9c7220c5326b65d25af7785919937794c407761f4273d7d5502c53ff11a6b83114105cb2f09891858a4920eecb5b8b73a5c803f4f8c68e16f2540d7a584736cd89561c2c22b3639d06dc502dde723bfd7d10fc023d7650b780aa52ffc396e1ba4eaee1e79d4ca4e42ac6fac5815ad08ada300c50431875f970511405be5f78f927ba165aeeb6e8349977f8bc883e90fa7946fcf97c403eb8b2906a69f84bd75e4bd3bddae210d7100d285c94009c31cfc067e7f6fdf538a3c085c166dc8c565e5a7fcdda24325b2c61dca091a4e4cb7f42dfd5c26ece367f02788948403bb25fdd624b084a539ba98fb6e00dd50b0021e2ce918b0dec5c6220414cb321509524b921d65134fc959ca8112dd6e7b6645cc9dffd9b0be97512e201d78105bc66c1d6b44fb0dd0d14d1575362cf2d5c4644bf4afbaa963fad75d219e7a9dea5e6a44092c906bed7582cef5b50b36b7120f7e56749f2c23b0667e0b3e97bd8b681cc3e3cf0564c8c508cad783d871a755580ef9cfc3f68511e6011814740377b0ef4c3e85e74f282803f7bf7a60a69ed76269beaf2e541f2f01461f1d3a7b61dccce66f421b1702dae8e1095183bd485317ee1121acc0eed3d9841499f6b9a0adc4a459414f50da7ca00070a63623de5e075e32778ea939f2b5fc2c7681f628866d4e70be2636b34ff2c719b9c49d94241c8993e398a07652126f0eb20da7dfc1543ea992b023d65d006cb4ba0270c324ed1ab10295eba02f28e72a0591f43791564c8edc198a23254b9fe3711abb8ebad8b2a28d76b063829d5b1856a4b156fddb3e21c5fbabcf1a2fcb1aa0aef309dbdd7b30fa9c24a475e1c506bae9cdc0be1efa29becf66ed43a000da45c20f24a5c8dfc08bf490000c17f023acc1af65a33f8ec051411061d7015c07dfd31c7ebfc09a9715008d0523364b2ddfd345073e5059d754260e7578661403ce987d3048e9480c61862d8899b4f7289e8c9f289e87a9f906dd963933d3626632989d2eb26250af5b76cea59070a4012afdb97165ca9da72545f49e15633ea2a26c7735c944e990dc961357e776441813f18afa14f8327bf2db98a0e3e7019f2751879c267bcb6c7863aec69caede24500dfa808bc3ccb710fd545fc116218319df0bab38e404c07ae223d27c9d4f29916205a86f00600a61aa0c2e0dbee33cf3613b31ef280895a78d292170e5911a3cde214de3e8d23939407279f7cd2ef57240e96eeb86081eefd70e5b57a3ad21b8f1063cc1e3b6f6f11d52bf76ebfc3ad91339479a7a3d2c2b8e6f69fd78d010f2dce00d6d8b680c06ecc20f6d67ec19a28f9c20ee9b624b784e05418ff4552d1ceeed6741f7ed9c3d00a3fd38447dad774a539bc99a5052a95f628286a5e62a4ccf79bab6da12d1576c537101a31fced2f45211f914c764e5ed965d5d9419156f62a957a277eee361d36f5d7c60222d27c3376828803d28c6633a8f2110601f35785bd5788bc8a8c47929d7d820cd2a4dab83e704ec786ea5e5fc3886dc9615b5d4a5355cd97beb11573a909572900531439f2a63f421eb5494cbed5ca15503e22f5cfb43b27639d7af28165b4591305706d1f2a9b0d1fdaf068e685508150c3d3d23a9c0a31fc682dff0e845c6317a414395c9e6c4a33ccab5c6395006971edfa8f68e55940b83b22ee4dc105cf2e39f0af7ea4a44a7a2992b76c4c570a30b677986924aa592738f56f95726a1df409d84e7f7dd45727c6c723d7d44300f256dabb802ab4c9a88121fae65192fc5aebfe70ffee877bda75be243ef3c1836206382f14f942875cee4ac22f4207aef59da5dcd208064b367dd78777384b80503be38dd876624f57cc4198a025b37ab52d7229aa2c68239ff2dd43d8994795828a5cddcc20bb663b36de0bcf88564e95318f7d59f159b263745ddfe380abfd71f5c2a6d01a4dfd231fa8c8adaa500d5df747ec3f5fad7f41f4d3307d99d67c019d7a23b048f7d63eb11c1133b5d573c502ecc2f5623f246c183a7d384044883190100bbc33c4aac39a021366c17e24a8e8d897bea7b9aa2b0b0ff17a79a91db2e36a411fe0c982dabbfc76adb74e710dc0cbb2101b4e04c96683f49b0d9e8321f4f529c3d73b380455084bad0671f759bba657fa3b04a41dac8acb156085556126a7b131eb954f4c98ad9a97666023f78a49a1906c542ddc59dc60bbca6c45b121406d8eec540912b0d0b03638fac7205b2556bfb4b096924ee52e816d79310095af3cb21300eb0416e87a6db4efa734e6f54a61d56e45c21f8501a2c18c19311f75013282383feec1caf488b8cf5e1d6bf898e420a0ceba1f47a001c4273d01357402e04c95adb4a05284a08504bd20e45a7ff3b37a127bb457edff631001c2134448d544b87c7cec84445bdedcf77b43978a27227650cf6653e85e62680d91a9a336e6013866aea57a78226ba26021773e81386c7e09f3942eff5c524b8ce0dd795c2959de2311e2b05f5e66cd6a92cffe26a580241663b6b4792cd8b8b872628c340bba16177e26150c31659ccdb694c9871407b0f2ae7c2020bd2d213e726f43ae8d6415189434d3f6053dd0f14fa9ab5e26e384aca8260901a01b46f83222053230900b4e765a5013a7931b2d689ae848d66815bd07bf7bcacd37f7f9e28c5e3f11c8c868570ada9d40a96ef71f80ab55a99eb2bf8e78db8182d518a6c1f8ac5c99cb8be988eed079d489e85c336fcb5f8057bb20d6322fa86e62bd8be01be1982e48210e025aef091643c5cf1d0219e877cf4718183baa29fd1e998b2233110abcdf05adb27255a66180b98862d820fb8220f99ec23d305d9f2be45400c8a6b6b564726412d6cd628efded4bd4d8d3faca4636f44edca4fe191db5bc302d917613326ba93f366cb36705e2ea7aeb69da71610e0546364dd7b8b962d62284be2ca6d0542cf4c387809d6cb0ac49ecf5e41166f72429c711e70927fe10f1357793171f937cba3af6a6d5140e6812dfc3f2377ba7e11204c0f27626e61ca00c3dcc0694e6fd6ab948627668d4eb432f6ce32c38726a5a35211facd2ad25e20b903fb971f880376def335853ccd89ae2b2485dd06ef4fbd72af678f3684d2175ac14729f842e00cfd4f48f8080e536f428c32e16954519e481da94b30b3e00dbc1e2d668931c3cb33526098b89da49bf023a3405526b77df1015c6aa8b88624f585b6022615ec24682a2e594e74c3cc251b082eb238167e9a5bcc7d8f97d20c0439e4fd8e706820a24114509132c61fa129989b7c5624f7dc11ebe3a6a1001bb52e32134ccc60f5e3111bc490086ed2b20eb735312371d60f9a2e69477f820bb45596866f4f846e312b6376de071f1354e67f33abadbfd869ff728c920bf21390824d579f86d9a78736e37eb6a1702cbb0535f3df93abf216055257c126c907b88a19fbede7e3eaf9972c99395a91a86259cf8b6bb7fc3e74365c983df0d3102c4173d44687042228e3dd26d15d4350bc9c99247ea8c96456f864edaa1ffe18a291daf39eb370763f0e0e71727b57dc895c7a0ca61d736fd361d0af7397ce060a77335485fe34cd581d4e971f77bd186535149d78654617f2d6f41d6ce2c825c039324ffcc10a27e50c6e47a5b2a84ad8faae7fd6932a91f3ee2d3009f4942569b4394e802c9207533e7c87ef88ecf09e20249d2e9158466d4e38a4cce7e309a3d2d82cd6c979458b7a60bb3372e544d4650c34c31410d78ce03b80bbdeb81cdc482661fce86890b76c3ce45a72c36b70d441e203c6f385584449176e1fa81827627eb4f2650e88b29856eba0822a44a63cb2c71695be176035a50dbf92b32487ed2ec3637821549f8013c816eb3ccd0d194634a82bf32b0957c6172ecd771769549a3138b690aec5c96013608f624ce42a753b61dbeaded643d8946cd9562b19aac5fd513923122ab8e5b28be59b6228825d403400207e70fca0ebbdf7c11656e662f005a9297c1cf3a5c68a23d88b5065a434aeca985d3947bdd7bf1caf2a904e27a9375bc4e58d025812903d2a8d9c1eefdf2b76e1a8f3bb20809a5b951f963e310b9b296516448e4847e8bf10487cd6704badf264e38036fd74b7c39c01f37047e62239081cd9cd5e3059d1070c9c1f89488626733f2b11bc14eb89481a2176a0bbd96c66575f009518c91670075a6d9488dccc597d27ad2085d5e559188d32c5168cc71152887d8ea71d15bf34ef9cd15cb041c40a6aefe67a530658033b23542baf13eb49ed5f4c0b7b111d7734ae2f88e7b05f9b0cdd7d573db49a19429bf46545942bb14edc07963d2800b5b66414a552a19759e3bee9c57d4e680be3d6503174807ebb9845825a0c70ab68b7d11d4af2cc3f09f078aecab7e88010e194843746e2c4c27893ed916d12d7575bc66eb1a290eccf0513a3e09579072ce06d8a7f38211ea1da1d8bfa710062124f853e1c9261705447defc47a1adf80c6a0ca3ea3d92ceb4b19219cdfbb15c1ffa71cb7fb0a35739a497e640efefdb2ff43e55a01f649aa81a5ffbd71e6dce6d3fd23d2011701aea425fdc6b4b89b62e946be2642b9247e35c6a16e8bbc8dc310107f6b7ce4fa1b22e96fb9d8562f02eda1a5ca567421a816fcd54396eb32759b565c9ea9cb3661a4c8e42986e8d272a33a58ab5bc23b11d2ee27a94d1fc5faa2a0ac039fd46f4f3540e952035f2a01762f1ea6c159137e220650a4de7c37550dfdcf0fb2852288a2dc0d75a13b721252e4413513bdb146f23f6c453c8212964a0ad3cc7e1706d547dd9a6e920c7be07bfc9ccef26adbbb81482041600e5be4608b25b7ade0bd39707d1141a9c38e91d6d6a8710e20c146bd6a167599b91eeba7d279906fc5a99ca5b5316472a92aa0e8b256cfa9df92304e935d1022ba8ee8691311ac01f5acdfe586ee52cd2be6025ba4653066c824575b4d79e762c2da88526218e5d1684647a0b97b5fcc8f97a24e99a4f6e8cab10ccb9181685a81710d9f486c8a9108c547f2d7d9796c1228b0709a12e3fd8326bf7834a1dacd53ba4e2dda1464eb76bf8e10837c5e0c8f11c2ea8f00356fbf4601bfddc1b11e5b391eec85ecdfbb3c9174bb085f3029ef74712def3d1bc2eb8b062e1cd968b374e2ddebb72d61c52ecb61f1deef94b7912361d7cf658040c7c687d5f2c471ae5a16628d632ae7811680f078efea45fd12ebb24dd2fd3480c5d399b9595fc698bd6d0c435ec72ab51a7d09edf66764065d9cb142b7e6d555a11b06605fdc3479afe95b5fad22b5e9b4604d2ca7ba3eb51578f23dead1a272168f78d251f6019c0ef83eeaf64bdee46c83ab0119debf5fc865717d8032f1d166e1de0446dec8737fa858d523c1c8155d4aa8d253b4f46d2973f178b0492d8680a005dca61f448687547ab04454c5c8e0ce84401c2203ae4a65d2a4c297f9b989bebc0d717a7b7b1c26a72ae0c8e838cd6eacbd41c6c66badc10014e4d4de5b875ace180f4562b87c63328a89149e4fe0d1f76c6f468a1f36fa60c7bc7e5fe59eafa01cbcb0000cb0f1498544708be7a7b8419d0abc91fa29728021d27ffcc472552a602c5c809451567cef6341e2c070dbbfdeea42a20968ed6254aa989dc871e3c91a70144102aa5696b7f4f669e1e0b3db2a8c101e1899f4803e8724d5e9407f485851ab9e27f0e7940a436faa458d51734e72c88c953a1cd19bfed29f9a6e503696a0b6823493b253c7878515b54225fa2cc2c43cc938fd1018c70f38a4649a2760f6cc067909d2812897dc8516e28343a51d9a0d987d0e7117eaad11d73c1e43dbd43da5e94f29ee359b1639549c847831283677d2dc6fa0b4e9bdf4affaeac14d82d816a069f303c63ff8bc301ad2cae0e77130b8771412f326f2dadc7c0d69543f99fc3b5b07139ada8c8443a7239af214df474b7515218b6e60c22294e4752120ff00af6b0d8a6f574fde3d9d31ac52559f9c1364efa10aca4626bddf9117a6cde7324503141532c8abd79c4694e62d4cb86596466160840dbfaf9000ffb80391b20e672a5f3f84f69c2d97d498f7449d1bb13f01818002923644f08ea3b3e94a83909315385d2720f9f98bffc8373ec9615900ed48016d62d4b5bbd9db07be2097ec1142cb4ef5789c9761ea7131d74168531c966e226d6dc9ace4464dd4aa2419f4fd2560ae90ee5021d85f58d208de486b6d008262451667f7fbf78bd41f6640410e52c87d7f000983c3282f1a6d7f4bc6c3753842dadd4bd8444fb54dfbbddfb4a627f6cc274263ae1060a69bdf3e0861847cc8810dc714756a05b8ca2ad1e2802a1d702133aec5901fba30e45dc302c7cbc1968132a3146c7832f67796a4c541788345733da795f14cd6049ddf75a49015e970c167f44226bf08bcc0721b0e43312cd33edba128ec39367db196b3f9e22817bc11d3bc0fd02aca7dc97a67b6896ddcef0fb349e9c3b36214fe2efd4cb55709fdd0511c1f18652d148e1959d2e6798bc956b6141176e0ff385b00f6178ae44f8bc2738aa8b71330cd645514f97c7db99e5722f935c22998887b7051d0ece56a1630af166104b50c80dd514eca36ad8feea1457acfa6d77b7991d1cecd96e7bada5161d34c5df4bfa8e7084390684652ddf60aa86f07a3269144e915bcc6e02f3de203dd990d931368a32f6075b3f49f95246e2686285651db5d4a08cfd102858902bfe01c03f1ad9cbf1b7ef45f67e6312ebb0c6b532731a4d26eab88ea7339f2e17582ba8136f2991301a7c24a3a3b5fb5fec1f3fef8b11e29ff40e9790f783af2f1da1cf1c4820e552ca692c2142fb81189dd32ee5b4cd67dc8920701023ef7b04edd473d4dc28377445acb892620abb94d69cbfcdd14f32227020a45a1ac5ba140ac9814b939870f27931e506f0a2676c9f65d88cac80d75b67a4ce57a07f8f2c0dd48cca8b68c2402cac0f8aed4314857f3473f904c7cf90286339fc11daa42b68686f74087bee2d18a1ceb6573d5faaf230e23dee53502595939e6220245606fbef3f3647483c3b7faea0b02e4e1ba5945258c9d51fa75512da79ebc9cc930184dd776227a2ac970d98c26cc7bbcbb699908974f2772729d069f7b8d890ab157aaf2faaf3282fecfe158631803de33736808af53a4febc35e372ff2452905007d266868ef9e28c1c631de830a7a3c38bb3b0664292487848815b002e4d0ed2474460c04b736484a520eaefb1ea931b86147abf7762ab20f703dcc3d607f8b0089caaa2eb682bb184dc81f43d39bc24daded115540412e4ea3ffb5adef53530a886a7b84b585d604f5b132e4f314fb20e59db3d005199a0703a149156a65e32aa0e38754aa43ef903a00c49b9c4a88a05d9f3bf958e07266055dd65088d9332b6c7d29ff611772b29263c0eccc81514b73cebbcf284247c809bd344aa416941c6bd68cc4e5c26c659f096345c1b8d5edc76a9d61ba130345fd5ad74b27a5e92781a44d259141e045f548c11b9679e7161823a61977ffb659471b0271d1000e2b89ad259d73b24aeba57cd6b941067906339e0ca696d048d302751cc0a39acc0b3a7ffbab9093266b59eedc8a12ee36530b7765feb1d69d64940461947246d619a14b4a57cb4bb9d60feeb98d8dedbefa88e3fb63be9ea99f9d65f7694c55462847e8212c9332f8c8e330500161e7eb4d113e3ec89356ea8c57eb739f5f01cf1e1e6a1040be7d194c75cec9fc63d377dd590df604615d5c1391ec17b535d7680dee50b2f96af256227ad345cc93f80822614d3fdb7f50966ef5d5771f6a239c24a3670723bec8bd40f09dd547cee615b07b54866e67f6323f4a6183a87e22b21690068f21e34f58f1854f39f902c2121cf7621a5ab326138284d3f4c54ea61a2cb1f8fc94b90c64a62f15927c4279fe1fd41551c26d4799ef9326809090aebb294270c2a541da191e151e04bca72bb50ea7154e249e8bf58d3248c323ab9efe2c75cc88deba848e0477c2d4f7842823efc16251b55dea0ddf7c06ff6372598913b16600c12af1f085fc8b0d695e469397a0ba7a18a1570930aff5be1b450c1d00b59bff7887dfcf7f2f0bd0a92934561b2e093c3295cd3969b8fd8836b37145271f5d5905e9e81ba87591638170928a90b8e9045055e740fb43cc3276a66c9129e35a0a0ef6fe53bbe1e2745db53e13f623bbdbaaa0bb4203ecf23e85f3838c1a74837fc00299a85ef5897d070b4faf2a7b56998b2e05dc2aeb645f18090d5d232d40f6bc20623c01fb890301fe074c7d09bc63ebe1bb260ac78cec14373add0f6dd4686087eae0e89f6a854adcc3437d01268dd3984957437fc292ff847d0704480c3a484db21e9f0d77969fd778259d723f1b887943f50ea98f75371864780eeecfcf5a77a5498f3379bf8d52e461765acd5ef59a7578df882e599c82b441291216f68f6ca3e602b47ed99aa0c6dfbfe69f1a644d22b66faa012fe35e84031c6cd6640f8cf548e61d4a0205b33dec975e6d8f3f7632b02c952948780ebc89169212ee0c8cbba3869490e0f659f8bbae0bcfed4ba2c81396a26fd929eb167e077a82cd634a5a203ca87729e6057ca8961428828cb53066492efe6ebc2d264b0f0b99506a07addda2986179905ae1744a342d6abfb3c82510551c7d46f176ec19b3fc7a6c4e0d0f0fbe8d495007c2d575b4f6edd0187aa08936a90101deafdf0b4a5912abba298b2d3864ddc9282c756bf0b241a35873151fa04296301519c00fe2804df9545cc47117b30af8cba291e17af28d40c30d05568a0f2c6febf4341ff71b80a9ed4944339aa12387542572592e2456eba9e4af6ea17722834a13602f714effd3c4eaa41db256790c488b7f4cd0c00a7e7be1c0740942fc931c3514016ddfcfcda716b312953b009d3fa126c50e2ea5515e4c0b5a3a66f53f27f261098a8adcbe46d98d5afc16e08ba6c716f18a36c56ed66529113d6b6b707071822cb4593a890ac726c30a87f2a356904b8acb3395fbaa5fa7f7e5c4b1f0c385261c40a7973e6c2847b215e7245b9f9ff0f381c098493bf8934241901d3b0238c82927ac418a390cb7d18d15626062a992593358c3eea37934ccd84161bbd272d021cfe3105284aa92738660327df5994eaa506a85ddc3bb5b12dd2f73c31511b1097640233620fce20826a296910b7a30f774fb572b7a1b60959ee49247fa74412b4c395963a505008da29ef947a6f676686340b98a7f7f8030c44c8bf90d1fb2167e266e7bcc2a6c7dde2496aaedc51f58bf1227b79ff40a60ea3581c19597eb1198139824e4cffed72280b7fcc1bc10a25541c2cde5143424deba327200c291", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000007a6dedcfcddb992da4890c8a97aebf9f00000000000000000000000000000000b4d83f75777e7e4919824dcb4ff934df01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - } - }, + "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.109601222,"runs":3,"total_seconds":0.328803667},{"name":"CalculateDecryptionShare","avg_seconds":0.606677194,"runs":3,"total_seconds":1.820031584},{"name":"CalculateThresholdDecryption","avg_seconds":0.556015083,"runs":1,"total_seconds":0.556015083},{"name":"GenEsiSss","avg_seconds":0.124351986,"runs":3,"total_seconds":0.373055958},{"name":"GenPkShareAndSkSss","avg_seconds":0.223574569,"runs":3,"total_seconds":0.670723708},{"name":"ZkDecryptedSharesAggregation","avg_seconds":8.412974209,"runs":1,"total_seconds":8.412974209},{"name":"ZkDecryptionAggregation","avg_seconds":49.375812292,"runs":1,"total_seconds":49.375812292},{"name":"ZkDkgAggregation","avg_seconds":20.649942958,"runs":1,"total_seconds":20.649942958},{"name":"ZkDkgShareDecryption","avg_seconds":1.465726409,"runs":6,"total_seconds":8.794358459},{"name":"ZkNodeDkgFold","avg_seconds":62.099165625,"runs":3,"total_seconds":186.297496875},{"name":"ZkPkAggregation","avg_seconds":2.150821000,"runs":1,"total_seconds":2.150821000},{"name":"ZkPkBfv","avg_seconds":0.330616986,"runs":3,"total_seconds":0.991850959},{"name":"ZkPkGeneration","avg_seconds":1.356784125,"runs":3,"total_seconds":4.070352376},{"name":"ZkShareComputation","avg_seconds":2.679413639,"runs":6,"total_seconds":16.076481834},{"name":"ZkShareEncryption","avg_seconds":2.500371689,"runs":24,"total_seconds":60.008920539},{"name":"ZkThresholdShareDecryption","avg_seconds":6.122451375,"runs":3,"total_seconds":18.367354125},{"name":"ZkVerifyShareDecryptionProofs","avg_seconds":0.101859736,"runs":3,"total_seconds":0.305579209},{"name":"ZkVerifyShareProofs","avg_seconds":0.215221433,"runs":5,"total_seconds":1.076107167}],"operation_timings_total_seconds":380.326682002,"timings_seconds":[{"label":"Starting trbfv actor test","seconds":0E-9},{"label":"Setup completed","seconds":3.048507083},{"label":"Committee Setup Completed","seconds":20.239957500},{"label":"Committee Finalization Complete","seconds":0.006028542},{"label":"ThresholdShares -> PublicKeyAggregated","seconds":302.757272083},{"label":"E3Request -> PublicKeyAggregated","seconds":305.289772375},{"label":"Application CT Gen","seconds":0.309189375},{"label":"Running FHE Application","seconds":0.004053542},{"label":"Ciphertext published -> PlaintextAggregated","seconds":79.623283542},{"label":"Entire Test","seconds":408.526410584}],"folded_artifacts":{"dkg_aggregator":{"proof_hex":"0x00000000000000000000000000000000000000000000000c231c04d197a8e708000000000000000000000000000000000000000000000004dff372f7e0128d790000000000000000000000000000000000000000000000075b49d509002a176d00000000000000000000000000000000000000000000000000013555c49a6d66000000000000000000000000000000000000000000000000e6842811517da5170000000000000000000000000000000000000000000000003669759c5c8027dd000000000000000000000000000000000000000000000005a31742b4a58157240000000000000000000000000000000000000000000000000000e79318c631dd000000000000000000000000000000000000000000000003f855d900f178f79700000000000000000000000000000000000000000000000660c1fafd86f70c7900000000000000000000000000000000000000000000000992e486dc1bb3e6960000000000000000000000000000000000000000000000000000e7f1f964c980000000000000000000000000000000000000000000000004ffb738420181a4ac00000000000000000000000000000000000000000000000f05f675e4f309b2960000000000000000000000000000000000000000000000079ba8c5829d9868b700000000000000000000000000000000000000000000000000011dee0a4299770832f879c803fd7b787b6c5ec4e1e0714f5aae691b0542def35cff3251fdc5b30f8d12e5ee82c4f7d3164b41bb4cdaa80237d61a4cb9e779f1462a241f80f1ac1cd23d6f8490793bee73dc357175a2b9ebeac1e5975a21e290d819a4cf1d9ff01eb1cbb093ecc02906356b0797a42cb304014e505f70f1190991810dc812e176297a3c0bad21939a9b1cb32dd6b82db4643d1eb8aec6fbc56dd93fde0996960c1cbdda9ab25913b46b163d4a332e799d201d1cdba04297f438ea4a4efd17ba1a289f4af62ac367ef7ad3197f26868c463bc7f7be75ebcd1f4cb0d8af9ff494a9183660f437d662a4eb7505bdf9d193fcf0c08a95e9fa826b850ae8419aab4e8a25f55283723fcc95ac9eacfe4409b460a0d1d57a6d77f7b524c405002c208fe520bbe73a3e2f784dc967fc117d7f6d480ac9960bad15f20b7ef9103bac9795b123787fc7493bb5e8dd91740730cc2c2acff5fc0b376938de79e664adfa8b9c1f2964d0f06e4c92e958f5fd77a5a49f0d4158de25b55f4a022794d66fcf0d66b61c71274729412aa51cfd6caa29c854f02b0f6381c8969e2927833619fbaea251280a6dfb0c2098a8eeb87c9606c13dd2fdbadee2822d0d474c2c5b2d29a7db431a3377b08f5f1ca652302d0ec96d751aa03a143ec73e27fc7287749e48007f121d76c44c0f508cd42146d4c85da1ba7f0079d44f7ec8d86385facddd56ec7d3b2c2310d8d4896a745ebf3284b67d7dfe6a70bf912feda40d6a7d735e2c6917ed0c4d75fcecb16b258dbfa38b206c4390536979a9fcee7ecbad671b527245fa30194e070f0536b206f4728be22149c5043f9162a2676b1cbbf5970efdc3f09adf15c02013d398517db992673bca32c394646855203b7e50f25af4e859fae1186d10b2529864fb2290e037275f80785700c29a8df6aedbd6742ba5ea46d6b7452909274fcca2b7ab41d0684c1cadebaf11593d6765db6e88be19eaca96735db527250a9d92a04f51e65149dd9b772f2e4c77ef2504a92b0ae5ef7216de2dcff50419e13a3bffdd680ec1c60b461d72df65afac17828feeda81b9a2441f16480b821b5d909d1c8242c008e6b4ad1af1d798bb41a68c767c9b3a45a60871364310b517d0323cd8922d5db1fd5bc98f56ae03e83eadbd0a64b1a960057fecf780d189106993800da22c6d624fcbb23dc2031f3bcf55b2350b901fa3b6c9598cea064913c140a7440455db00bed4312b796f57a058c03446d58c84672705a1af9a15980262ddcce5e03fa8737cbb8d9b59edfb4bb0376593116b6acf935b6ed928d1b7039574bb7faa48a2e45b9abd1b95ce656a2c3fcad52e50644ab4f430b092950713a9be10309a139e4114960f59fd48470ac69c1d3eb77b9ac02e1be09eb1eb571345da560337d679e98eb65ec13e231b25ace4b4f4327c012879a4eff8752252073bc94743f68c6488c2659ed13bc4777e9b31d819d3528d12989616b713b2a21f4561994e2b294dd8451f4d34e7f17f37b31c7659e8ccda79528830ffb2d8600f7028bdc8cc1f247db1c996a7c683e4ba8a621111c4bf18c193aa299e54a818290a2fa13fc299ed1c9655ffaa435d5581e27e321594d33c84721e01e4c976fa2f83abf8d7ad3101c5575de5ed03233a502c87ceeef5c8ac9c4ae7ce55e5a12702c4a8dc4290c18ed54297509ae69fc6e10116bab06916be62c1b66d4c31452e28780558518a9a0677de43d2381aa22d4cb1e05b371a0abb3474990c81e0045a1010f281874e4dc7bf8f4c266bb74d4de791f9444d6a979df2930d4ce8989d5f134d2b517b71ff043acc1e77ec06863b770df40820833ee29309e6d5940c98e20a46b2e7ab26ed6e58880c0c757d00d9e298b2ff637f68e3e0ae78d362684ac71f4ef526adc5a9362aa12348d54dad58410e69e9998c043ef9cbb9ca457f367e2b540b26156b565d77217132ab2cda06e373b5c8e42fb1c549218db94a975eaa04925195341bf71bb44848f66b754f8844df69caafefacf5a80fb7c48d3541b52a54d7f1ff24a7da4cc29a6ca8c63a6bf1f142faa1da9ffb6cbf576a74f1665a2f6cb8998e325cffedcfb1ff8ae10ddbf30553384be2327852ef6cbddbb80c031612944a6367ecc5ddec0bbdb492409ea8ee0b749023820855af207f2771efe415357d2e65146970c4e134ae290cfa6c78765c9f93f822f7442f38be924feed3075d0f4c505e8f412cd52430157769957d983cf833076ac64ff6cb3089a5c23e2a4b7311a60385b29b440ff083a03357b30a7ce34c506b724f7491d6397a5b220bfbb4078dde8510b9516907fc0e26a5249af99436db17ddf3e08b63330a4e1f2fdc4166cb2192df0fd7da156ff33c342789ebbaa8412c362670a8e1d1e3660d04c8ee09dba54e2480700559aaf372ac10b3fa798957ca140d1293c4ab7fde740b5485945480745e0cf536eb74a1b75abf1f942da1cc613621de9fe86c60ab972f724566c306515796c892b36d522b92c2c481fc29f1e711953987999e3898ae038f4fe31d52d3d91f94e01fbbb0d0101568ef1b2d02bc0ef78877e5a9e51cee134025275cd9aa251def979540cc5bc9963016fd04a56f42ea0c384d20bcb51b2815de4721915d31b83934288813e0914eb9388cd661dab0fedae3594e84ea0519ce06222d0c01325eadb2239c4281880b62af0913eb476a3e57fee4ef2564fa14495d5aae740b0a554e1b0155f6451c2bc68e136ef1b76a6d538cfdbbd17c5c2d8ea0571306bfc84903b7de1030e1596d801b8faa51635ffcf1a4c40b9b070f2fa3fa1053239ff9320d966969c616127cd2fc20a65986161a03cd6c3941aef31341535b0568b2a713c86a642f4da67f3141252ab971636722ed12adb2a793c72adddf418e8586058ee51dc6f148ac40c0c26dfb144bbcf5d307c72968ac60f7257b9964070a64942d647ee644678caca09ce457f2e613fbc10526152798a5f624546f7fc22bd2801f4aa585d2483b8472a4b3f5af24b3dd476e671d3cc2a52f05bdd9fb07430c3cc97d6bfb0c577b2f9e8e50d6e1fe74089ebe128aa657e68926183d6cd87dd0ebd530920013a0a301d505c47262a9edebe462a0ac28c6cd2610b59a1787db42188287610d1cedb9df8ccb0c33e846116c2d571ab84660a728198a7806104f6e505519e52bff03fb9eb0a00256556355c4cd7dec17af7147dc071beee6a5686d1ba5a185809baf446d80e20cac33cb693da5da0b962d2bd19e017661aa136cf73ed71baf62505bd5afa4d39bdd0684b582ee6e6f4b7c6137a610b8a5f142ac1cbce5c02b9d078a10b567ecdeea8eac300fbcbff1d8f92d75440396973f60f9c313f2fc46ac28340d60ce96317c2b1fa08c2604b0d939d0d18b1152f44f71949e022b665a3915226bd97337b1873de651fe90f3e70a3cd806f21f82d88ec05123bfe13d32b3fad2e2f874ce2a4ca42ce4276d95b216d0d11aff23e9053c45f28d1ca75a522049373683eb011b3fe4eb644b68274fd6361bea130bf786cc75187bfbff53c6c8809b4ee3962dd87ec04aadd8440848869d77361924a910d6f25dce1bb4afcd04c6d823db6e94f29baba17e10f598331f4db5492a2622397ea1c9c156db10797fd4570223efd35c0fd70bda70794e5a28ff326d03194c82df5490a622bd475ec3b6423cb73dba4eea28b6cb978b7efe60b0da77f31c2adae631cc4fe8ecd22f605c974bb849132a4b3339ce8e68c7a6578a3acde71a7a2c7fe23193fafacc18024a9313cf0b126c6d7c58f503803adbb09809b6090818993e81b3dbc750cbe9ccd56a01cc9266bff7289e2a08a14124af6bff64d9173b277e654acd6db771e75b5cc586e8d8153aa7ab685032632cce7af6e17e7d1f8f30910d5e7a54c8b1972efed7d2f5e1f6aa76bdcba76057cb941950523f532d02800f31fe4bd26167857004a22b83585847bb2b97b487d08a0a164c301c610ed2a41c62ade87f7ea3e1376bfe73a07e8a98e7a68a0e59fadc1beaf37084b302ffc3df53fdfa8cfe80b5a0427f8024d1450f11fa7f2747efb3d30c0b140fd31bc3a87fa0baa6219996c7ba23057aed724685d8806b849f935bb32eef4a605605a81dcf2249ee73ea621b56f520e32aa0b6a3d93d13f29aa0b933361b7b4bc415b2342befe6057b29f81986d412861cdf37c13ce9e02f395067e17eb426edba05ee1ad79588ad90e65a4da63c3a0589d6d7c393c35e1f9c6026cd599c3df53c2f63ca034855057d89de7932ed12821db83c79ff9010773cae04fec7b850ae3f04951f33d5cbe2f4a05d9b5e3369785b1180c4b645c13a811156e17872ab15420d6095cbc95077e025a6ae3cee3fbcf97a3b552e6f10b2903ed11f88f60b4f3d1923b42010f679c50ea946e9ea8a8332e59ddcfa2f9d68b260fbc8b81452adf41022900746861cf9b59b1719e2eef8e882e75922a6be8df309d8694d5ee46d6a10e370ba867c3128ca6dc7883414ad7f3a6b3b01479f965eaa0247cf84938dab303d0942d69328e413e0c5818e8b0d4f7ce1bcc11f65e539de2ed2e3fbb46e4e25a36903fd8576aba8efc706cf8722171a2fecffd79a415a3d3645924c755f8817e4cf0a30e0203e5c32c990022b3ff687cef3c789d018875fe44ad11240af3a1ad14160b802d382e8f576b579de16820563c4e212cf36752246772bd379a5110ba9f0b8e02469515c2c9676c859356e0941689efcdb6a2241d0ee46b580810410afd610f1ee5c4d2965d64153350490ca842b210984ed286463eb79bcddeff41cde9fa4367f246608dfec52ffa6ee6bf08df305c33e96e22593896ffc1e36230ca2aca0629659dc3571f0be7e809e9a510ec9a7f0c1723d69c985600f3833bc02fe173deaa04c41f9b5b1f83ea37d42474dbc81b94c81b05a64581aa57213cc13165676762c6ad6b51ffb3a73f1f10060358b19de35c6d4babbf829b224e95303d69362694e0ac29cc0fba636fb26473b14fc238ac027dd77f8b677693a5a571af4105d307cf0d0696d37f321995ab288e77ce09f4ddc73a252c300d4a00c981b554dacf0415b8da77fa1c4dc28ed4dde3a3573c6c3e7df3f2390d98087ba1d2a97806f0e9fc3c1a16c64c35704079f146cffaea0ef7314b90d21667bf209570875505253493deac384d5044c00e94d50a15a130f8362084459e05f3bf69c982a364cd3bec37ee44649738d102dce02eae15c9133963dc6f00af8027b41704b1e65d7c2c3c445743491863faacee7a449739fa7c5faef8f6fa8091c326f1fd12a2320f689d0be3d099247858f481ff84a1c3a760f54fad6eaaa7461972bb5b1241c2e95617f5eb5ec64c536fd9a884a1d21e74b17d1b6fedfd6524f370ba6b023f770d9877a3968948bb8622d649578f5c39217b8ddbd83c42a9f826848f6ec2c4a78791b282fc5551bad0e1b3f95f63b2ea587b46c4e4b4602fdc1f24824910a59a9a3b3f2ad2b6058ec542fc2b40bf67fe3ea4c4ceec0f171a1736b0c63d901bc7465c249d7b191878584f5254636ad94c076ecdc7402eaa26be71dde4051302af10ab5291187ef10822091e574ead9269789d6132639c25627efd71702202d180e2caa6d96a03e8f02ee3139ba30e989586cab693a0a79e12fd162c12d432407c683bf99ae6a2cda59cf6c90ad591ceb18a99e3c1148215b36b2fb3e39f00486f2ed2caf1477061cb1684be5c51bcfb19901b9c02ec447beb24078171aaa2f64680388b8bb597288b662a0382edf3c14ac070737e86726b6d12c7c500ce72d3480a05120092fdbd1229001fb4fb81231535b9653878afdc3addff421963903786aec552940e39d9ff33460f74a33387f9c70dd4066697c5f2bbfd4c2efb11b86d2cb5f3fc4af8ceea1a93a1103922e43dd160bbdb8f161bafcc7fd31f8191505db967d01b7bf5e5c929ef3469c08f817fd7c7ec78261a5cf10d0c06f43810c2ef26ef7f2e9336ff587f6f8900096de08d6bf739f6313594db9443ec342c30fac34260f3c79034b64928e3081c1ee74b0fff1cd01f9d148601c02d6e482551800e080c9c229d59589ed97ca4af09a2608a599ccb54c19aabf16486ff4db460e7e1472fc208c2ca3ed2a948a06ce66ef010f7172df7bd2831fd060cc233dce03955bb32d144cebd97bca622b5ee2f2e6e8399a2adb6f538bd41181201b029c2f796924efa294745ad0c575cf542fb7a763c43db2cc32ba8db6f775b99b295020596ab671b0038e64f5606a8fa8eafbd10d29ab148e907904f73da636d63ad40a4c6396efab8297934902c7cb657d39c8dcf44eb4cb3a86c795841b0f93be581de8532c7577ec52f4a12537a9956b0ac212c03cf336c0bcf99dbd3b892e45ed2d2a71dc9d826fb9cdd91d43408791e5ec56fa2fba832774e589084086303a0c16ff05c6f5780fa20a52820469bf3b8978a7ed7937b0217479d86ceb9cbc90ac1117c686fe10d0a97288182e9ca7ac5e47384be383b7cc53854d7a5505c6dc000ba57d3623d8d3f995c14f756832ff184559ec8d627221a65026e964e2eb36aa09c11f60eee4a48a5a59465eb6449bde6db4fa670458d925591318b9082c5f0f0257ae6d8b494bc66a2443dead3e49bb7bed337edf061f17c49b264641fb59dd26984407e9accc23b4d11adcb0c10651e75e05ed573edc971df97827ef712b551513e963428dcb136b795ffc909f9c259347fc67ca0cf5d367fdb41d830d91ab02366b303321eb6971795d674b16d05de722e238f9f8dcbbbdf82cdf46e2121222aaa6f521143bda9fabc2237e2fef019f237d6fa666acad9a65f44943d549b515e85e088d8e3ec42b92d49a3994bdbc03ff1b0073d63c0ec74b9bd5008c4fa424585a1fc5b5b5ec1f6ff4d7ba9e1d0708efece885a50bfac097d50b0a8a450527f3d7df5039a8c1b83d0a7c217819cd3fd158d79a679145885730d2da273e422401b4c932c33726c20c3a350f4141a5665d3508cca37ead4b34b4ba27cbc6b9132dcaaf7c522f40b88edb47f434de442846a765c2fa47788196fc9df7c83f5b29ee831a1f7284e6f4e3c133df7d9e54e3ec5e0a7cb3127a1374c12ff4d616de163ab2749853ee6c337caaaf97084d29d86d7fa4743b52d7e37a4689f4a754c42ab7dd83f960a3e90114e53473f486132ececf39a29995d2a8174ae2b547852f2f30d4bacce67216597d1124fbd87ecca5dde59c0f22cfc7c6322f740a909aeb0a973c5b14f33f2054698e7e03e338769b7919224c638db7924a8245e308a43b1f85d7e406c8446e58fc361467f1c6cc21d378faab8ee790ace260454e153e6210807cd049974e1ef0c1cb56c32ba83314f282a98d1153cf2af5665eaeeb6a16139a127f61b33fd3faabb69d6cfa40f9329a58331ab431d59240d8bc2e966fda22d7e1eb4bceb911e8df376d4c050cf65dc124dc5faa21af8758a079d7c0b85b02b6c0ba4355df267525d12e8205f3c8eecd10356464704c7cace51d0a81e9640bc6d63255d3e7e5ec844e84a5230a07bb33c79585fd8267b76abe7150a786a21fd35591ff3238fca651f2d59cc90eeed3ba2253efc0f567ba0f8aba737194af1d11a2a63c20be531387940968da873cd43bd7dc03e3c1d3bc12aa2fbe99ada21e20e2b14daa35699764599763ce95a5506581bc494b271f865932bebec143ca0e6358610510cf53ad2fe1d5f61ad1d24df406b44656c61ed6e7205445faf3a918d391f98dd546d7803485b78a225a4ef29ff7599a16d288af013a297784c0b02a6fbd947ad57219cc3df3cdbf0f7d59e36ce25c95edeaf6245753f62932bd9502cac9c21e636fbcac7c2987640ecc38ada9a81278c7de458bfb76216033d8d313c337b1a163a73ddc3140db57161a4a3fd173ed6b24ffd66be49ff72c6c40dd0fb35f02e279f9f385ab009f147da9aef912956e37c57262166bef70d27b677f103e54368d3cbf57152e241f21a7a3ce73647df2544af9ccd49dd58ce80b31e8192a528fd130ff6b38f3bc4f495bda106ed59c6f3f0fc39c0395b4497dbd5eac0c6b1abff83546d63b037b05b68ef47b5f6b5d8b0436d38f372d731d6fd0ec472f39308f03dd5783ae424871ffc058ac46f5c126810a51504fea799030db97c6077deacd4c8da49de813a7dd9ebd434f32490cff962a047a55647a095a8279760d4eef1aa7a2eea2c7b8d80f82019b08b4e788b538de74f2ba002d414dd6abfe206033b26f0a38b1b88a80fdcbc1e8ab9f37d0de3ee6375c07915af54a986b712dd8a4a75a710c535434b496e4bdcecb0ea8f8ff48287705b3f579b2aff304930528e7d18eb79f7043c9a1aaa1e46dbd5fb9dc284e2b867bfa2c371bcfa0cff8246c4fe2f51460bde5647596fa9e4a8c6e38d838c8e8467b5a684cd45963b5cb04c2fc702e846d4eaeb30abf70328cc7145e757144d261a89ae51b5fc2ed58f01d1ac594165c1ddbed15007d392ab654909ba10443116328aba7c8af8b09265b27d7c53374a87bf6f98d28f04592ec3f7d613a50ef183784e7ad894d47ae991c05ac8a35b6d4307f7931256358077480d2eb99142c278440fd800d9fb8578bbc1af8eab305ee037541e604da2ed341479186d828627024d4f1363067737ee3bd0fc6ae2f1b556ff69d05c598a92d68f6d7953788aa43e8aeb4971ed7bab3fa7b079e231f452192c2b92e2cb0e2305eea4e2f2641a55f50be480beaaf829daa8a2c89801f65112458cd3ae535785e6b84f4430210b454a30372b14fda4e3393772d720d07044a0a99f8f8e247d152f8ec470c6eadf946e52499a5083db06d3bf62b632db82ab142bcc95accecaafa757e9b56640b1f106678cf8109554c8d80be21895139752ea939af20954934cacf705c99630dffe4254f701420309bfae9111a6f243f7fd657036f373dc43c281335fb1a17b843e61da43093df3be32aa0121b61059d9259d23088afb4baf936b92eee94156781c4e381c76c62791f879c0d266b33cff5acabb6b7dbc81d67a22cc97a976aa798d5eb86beaa21da26ef726617683a10d48af74af941aee5b015c31e7c4fb0af21f474a190944e145bea17c727ec1f4de4e389fed639b14cf173b7419caae9c3c33deb11806963024e14107520e3a6f816911548b98f74c0fc5eb6f384807561e18dd0e417c96a062e12453e0515c4ad0a7155e54724b197e6e200e3b61df77b12f853c8f0192e0b38e8bf8b1fc9f87ecfb8749f4651d29c764148128cd0172f37c559032cb1eaf080664e3f167a19ffde30c1c79b7e3e44f715b4b20d5e143ca7715f0300d4e7ee2435c23705f6c29d34d4d9bee5da3e6ac50d25ed57adfb31c71777520c147b0d110f8ba0183e296f88dc0d7dd30cc8c1a552e20d3484781fa7bffe789cfbfbc38252f357273d99464757ad98c4bd83924a89fcb690c3548c35bf6cc93722206b8aa7b998059dbfaf9b597f883df0a4e79ea7a87586c53949d42296315f262e19eb4443541e591182121a4d53c9b009bac47744cd2d655db9645f3b31b46fd1cf213cbe7416a3520270f458d7ec5604157876a5b81be998f854666815e1ed6792252bb2da0ab85a4b49874fbdf69d9ae78e895a82d02855d0025bf8ea19d5a7f67b63a52e0c627bad904e362e90ef868308717e4d00895153f9ba4f20568c3344e505d88b0efaf03ab4c593750c40c438314e010f8aea4aa22933f64f38aae2ac56c5b00c222ea879ad9a723dab3e3324fe8ce6e68b54f9b838a2e265d2a81d091f43a97b1218727895e7980462b651160b79beac0207bfa5c127701088140fdc4741ae4f270a6d0861d29d825910ec8efef266852ad117e8bfb72c4827be08ecdab3ab21214b427bc61b5bd42ff1f6e84198732bab6b043d8eaca51e94815db13c22a5f2178c97d3e67bb633138012c8f19e6edca36f28207be6fd25feb47eacd3f7300a12ffd16717ae1f87b30572f78fe8e9f6c0285864a19d52ef1416c6bafd296ee5274e16e37cb37acb075b842be01f7f577d430667f65154b11070b70437937270300c53983a317d20ef5892fa22fa5ff721fb9be17da55e2f82a64f5a3febf7192ca47d3122f9be516f7a7b8c2c02d0bfd706b6ca47de944ae10e732351daea2a031e416a38ddb62e1af22d03e222ca180a8b69288c999f3b851f5ed999b2a0a61fa27837d952cd8421cbc240d987a37155a8c1e5e1f4eb7c49aff109ac16af6d0784f3728889808e1391e69b25e095af7a018c8a81f1cefb60aee7375879697e14388cbf84fd159c7a4e1882c56bfb3bb5b5b9ac8db66038a0257e777fe0c435090aa91cb05b93a1fe40b21f5a9b40143eb498fb7e030e98a4b9d491ef20ae3a148193295c2c9dfd0c148b0d6e84fd8eb8edcd5ce1337a1998e78ab217561406166e870a1ff737d0401591c5a5b2f4da40bc19a0f246793f62b04bc5df66d6020970535a0d0e97b797d44e58f47891b11c30b5ab101d43e6413f3e5c048781910a1f8ef5c1f90a0c2f60909f5b5109b80e03613cd84103eaec0ad5cad363665e2234e51f0eacdc42f1dc6c74fb844fbf0d7e2ad23e9574946fdb9d7a3afd10910881c68d7025845e58e84554285ac330b2317fe4ea446d6917f98d15e362e1481d3d6a5c2067ff2ba2cd17fd68cd5ad79e440ec065e883123cb3950f2ac4b6480db142b1f16d08c353e2f557be1dc2ff5359e80e8efaf31772170cbdc1deb54d1ea5312f9e915c83584235ed85109d4a59bd9e028c5f44eaae735386f684a6f607ed78682cab8beb171780c9a54e3f57a392378b6051bd47cc4104dc4678c74f0300f5276b1f61679e9c481b80823bfe4fbae5082026c4996e61a319bc1274b00ea1dcf79295bd3c34f1b1fdb77357dea3955457b33db3393905ea35deb49616071ec289dae2a241d2d7715964b2db700909956a819439b1e04a7d1f922043f204570c529d1220e1c5c3a83350978b08bb3d65d6844cc1e615d634f5ae768f6e1ae5775ac84325b9a3c2976dea264f8bf3d7a1e14c31b9cf8d6c4855fc51c750042533b8db0f0ccb4a24085cc029702b9432137bcc39c18a492ce1e62f3c2393070a3d2b973aae4f5dafb491b7ae5e36230fe2c376a48a66f5d516352c8ae01c0cc43121e8b8010835398c313fce1f959c0911058646e807b1640cadac36369f203610cd91f6f96552cc2d9b85d3ed3dfee006928fc67518977a68ba6cb35135164cfec4ff35ae655e66f1586f933e88c2874350c6091f90a682e32c02690ad219264c0c4e73ad397e314da27dd9fc97d941bc619e13826ccb19f58d0651b693270214a4270e230bed23fd16705e90ffb3c8b9b7d9cfe03ee2a094a9e9b2909f2678bcdf76a943c56ca03fa8189eb05a15b10840e4f83e7ceaffbbf07fa822851245572d390a62945618a0a04cf1b830df30e13a4bafc8414b13ffacf3488a9725353f07f6bddca341c57e2a1d437a0fbba07c4e5bb31fa6c41d9d71fe038e6f25ad9aaca4bf67db38dc232dca80ac6cc577f2fe237614c0987331d8569772871276ebbcd824e2d6fdd1adbcedd9e14db8c44abec004b0ac587962949f41b1800c781b7221b31e4e3a02d40e9b2c1b7ea3b73f180ba5b1b27ef069b6f05961800d662205d6e426c30f893bd0cff9b606a3704ddc4fda64f149a583b5e2b1ec5012b58b7e12f4ea84f4f9ab698e625d09d85e8ab36100d477be566bd3aea6264500cb6669de3a9bbcfb18d17dd15a9d6827277fa1a6c57b83cc7aeb2965222df40929e9d749b07a43e36393a92da64323ff33bd3a3cabe072a929c29847450a431c403bf371e626651430d76e2f0f1142ce5ca04d5211a40a3eb5b522492293041a356d17586f9c2a30628c4bdaf8f93868e51faa16f6da0e030dca9a4952eb1c14219210c228064f8846d40e9368ffc5b17c70dceb3c4f13b9e42dca06c872ce0f6cfe8b8d14533b480f908e52f254f91ed84793aaf219683c7d271ab2778dfd30099fcfc1e4ae79674ebce5a1315298c221404b418707036cc69bab53c5313800008570b8cf57caf6020c9829afa07666b66fa1ebda11ced87b18a109dcb1b41aba076719214db7fc4b5ff92778ca481e89f912fe88dcd39ff7794fef20cd9520d52821d92a633a17d70485634a0541c0272f020df6f58af977b0e91d6a4bcf03ae248c091081ccc4e8fffc6a050e05357240b08c0ff78548a041239002452a0ca3dfe850ce5c5ba424344467ed464b57e098456fe57a2ed16b1317685f26ff0f5ed7b176e08e4d9de99f5b344a7d3dd3b0bcb4effca2df187a117c576443e01dd6eb7912779095f7972c7414cf7b72ca7904ef8364f53db792e6cea4abd3422d840981612e99fdd97f2f4389914f0f78cffa1d95e815ebabfac5dce2ecf8412a99be58b4cf34ac6f91f5b4a1dffd405bb782f6189d26f468585fd50761a8a52df5ed597fde9811dfb1d21346499c99146b00cdbb6354a8118f558295fb716b2a9723c61eb8b1427d6bca3db42ac998223f93b5760ce1e263fd58603f18e55a0d532e7cdf42645bdb8cad58eb88dd52f2349c626f08d2426a383deacbde9e251aa2b56f08f371c6d6917381ea4241973ccf1afafadf9e78745d163f0de52db4141c05e1a20ffa115820d0c5d3a1ee1825490c446493fd2ebc52cc34783c5b0304d04162c1609e7340f3c6559cdaef78739a3ecc0e1fdebfc1bd4aa2cdd31d3808275192c8e3cc7921c1fa351a66d0d8f43bb8bce2999756a00a2276d0af4f141934be7f31bde374351e50ccacc048284f6f6ef77f9ffc3c3a7d9f65b4d4b7db1e6e30581acdb9fbf6832fe4e12149b397f857fe633b733e77628b0bda39db6e16572f317cb53137f8dc0c054841faa1c0f6752757873ad0b2c78d29d619ff9d0832d2e39f7cc552cd4203cbb6ba9455ed305398629287b88486b0b914d0be2d1440fddccbdfadec77be5340a0f75035752c4ad485a9ce28f18d0896d358c1560f1faebaba5440acaa383fe25a1a1849a29a56a42d8fba183191983b101a5ac221dca03aedb73584a7be6415ff937bc6bcff8fe8595760af12db068187f52e571d88580aacd45b8723a5c0fb5609f14c5eba1afd3f40f5a164ee4b97a98a72c82150e3ac2ee4314561df798eb3c3a573dcc6aa766f26a84eb7b6e5584c3243911c09193d51120216d2e246fa2edff374a80c1932c39acf41c3e3e040b38373df1e28db8d4179c2cd8efd93091b611d7f70648e832a9301d931c14765a87bb97009b8844ca5d4bfb2ad0934927e11da2fd9832690b28b45793cdb1b80d9b6fe68273abded42460ce3d60f075af10d465818244599cede90585ac63b43c08d7080219e49ddf2bd21ef6119cb9d8aa31cc1f8141373ea1f9d490f15be7ab965c5ad290845de60a58fc9ca397be4eb86efaa128990d4f4441c3445a59e608a666bd10c0c0ce65aeca5e959fd32eafc6212421fcecbd3f10d6ea744d0f65cb21f162b23d804a8d20d2873309f646f8128fb9c9fcd02cca58845c5ec1ae55ed669bc891792260fd1d7b5ea3c3a790494b73f071fa0cd35624889e52c965e136c215e142cd89743e54a6b3988ef9852ca79e456ca5cc1afd24eade12ecb3b27ba89cf1725bdd58395c8594e6a8a01e4b388f76d54d35096af2a521220e268b0dd633f28205bfa81ec110c18221ca77e9bd5bf3f6d090e7b3c925c89ad60d4bd9abb694402a7ebea4ee8be2ab21b00146f7033fb36ee7c6dbcfa20b80d70e2291b46bc8d2761d2dc0d29d23d0cc7a58873bca8114808ac811fc3f45b25cf41787065af9101d6d2ce542984b742c097b92e85ff17d61ae6fd69c597b7dde659a2ea1c201f18aeea00abc08ad5a9d9acafb5316d1054655ec5a02bb2434946b584af7271ca1629090a0cbdf2fe152faf22486825550d29bd8b08899aa562b391cd47deedb726ba0d95a1020b5be53a375ca0c98024b3a961af37170f6b40cb58186c8ad79724ef8fc6d60b44f0de34aa1146f6933f964afc553538f3b2a2b7dc17a83c58701e902854b59f2a7ebe132fb19ae003a24a693386b7fa4d0f7f589542cc6c1c702f9f592898c4de2f94cd9a689da1f0855dd1d29def7e48a900fb989eb7df340e20a9ad75ccc9711d53d4142f02b635db1ad3458617e50c0f91ff6e64858b669909e40555073b0af9e9766e94492a6c509d936e6e678b19ce25fd5aad8a9f9dcb301dca4dd5e47a124900d4ba230002188ffd37f98c29cb0e2f7bd055376b4df01380a76e5ca5017b8b97d887490f494780633d00defdac3346cbce89045a83660e56d2e8a6d8b47211642faca1883a83c23045124d490d982c1218dea8f05e340f95ca48916da15d07fa5f923c3da0c890e36b26c55ffa2f8693485e5b4c67fc29b3be5768259f9af7c17eca51c8ca09d65fa1fb21db1f7c5a8c545b1cf8f78e1c14a5648cd566753b73c05c7b55c1af4005899b442e53f08669ac88dfc0eefc2e5b96c76ae4f5459241fd870544832c2a4be2954852b47df941e647bf7f914e1ddd6f241b5123b5f87aa912ff5c35d8aa7d645b100f1d409338cd9280a0c65515e673654f8fb5de1da42fb7ea5fc5cf1f5afdc4591f7f22f759fd8a9ef709230bddb28064cb8fe3b9a8806b987b14d8e58aaa13c86396832da0d2c440ff95450d3be24a3d6c7c976bc5450ef350a98f129882f8b372c714efacf7a9382285d6046fd45168becff5d64fbf5bdc12073731dabf923bdcab04f43de20c8e95fb66","public_inputs_hex":"0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000fc4a275557be3acc7e37f1dd383752c0000000000000000000000000000000009800ccaf777e6cc629fb0e8d8ad1652511521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a"},"decryption_aggregator":{"proof_hex":"0x00000000000000000000000000000000000000000000000416c9296e38cf3689000000000000000000000000000000000000000000000007abf85d3a9f30c50400000000000000000000000000000000000000000000000237b411162b313eba000000000000000000000000000000000000000000000000000156b66ae45ebb00000000000000000000000000000000000000000000000fb163c45b929b1c0800000000000000000000000000000000000000000000000302879a33ee70e101000000000000000000000000000000000000000000000007d3fb89f63848af0800000000000000000000000000000000000000000000000000001191ba15fbd500000000000000000000000000000000000000000000000266d108d2bd6f116300000000000000000000000000000000000000000000000044a3d48dd17dc3d200000000000000000000000000000000000000000000000bc7f72ab82ba9582d00000000000000000000000000000000000000000000000000010d1bf42c416a0000000000000000000000000000000000000000000000021bab3c5cfd31c3d500000000000000000000000000000000000000000000000d01d612334779aeab0000000000000000000000000000000000000000000000090f4390fb5773215b0000000000000000000000000000000000000000000000000002561ec9fb68bf1e61c54fb309fe860b2a599e2c1339c9a0c1d4d4d7c19eb9e1925ecc353851c21f29faa0b2c9e2dce7b0ff6430e42bcab396059f4a009b643e80bb07dfd986a22c753cdc1297ee81add827710dcdce1b3905d8d137d45de8c4b58bb725680e4316c6900efe1d29c325ba03cc43ec30b8df8f7c8cef5da35935b202319ea75c872438635f5b3eebc558a54a7faacc657de20dda2064d0177e5ed3ee3ecd9f7ce014be0008a5b22bf423fb02ded702d843cb50b96102f67478344d3736244b75a9042116c4e0c0968f6fedabaad0ccf9ea1be6d1b6a2b42b566cf78603c24d65f315da1957f66178602e15a9baa053810b896567ba5fbc53b50b48a44400965116157c7f19bd181c7b184650af1f6df0687c64c2b54927fa663a224c6979cbb5a7274367923451bec6afa2d84276037f1dca6505e3ea5f37b5218d7e270ff9cddc001cc131923fe1c5c0cb360571bbde6d7670a3d91fad79d20d130044d4bab7e51ac2a0199f2bb567f4aa9fdae3b8634f70a6d47d4a8ec6829176f075caf523470eef2d4270dd12ee163794ae40acc907a4033b8427230e0a3080bbf4208bedc2169c5e1cf9f47ce448b3c89989c8ad99bd8726360fc259b1c35041205dd88b56028a4398df5cf93caadad64d1962b80ff243973bc4bf4a89700552f80d1e293c25c339bdd767fe4d0b7ee2d9b6c24f5bf65ea3a6e265f9c682b4f1b1a393f98c2d4017693a6eefa1e776ff4db63e9054253ba54e0f6c92613a8e3b55849035c606f4433589d1e140795dfd40198ef021ad53951f2dd41624bf7cc92e59783f3a29d9f6eca1bf91f470e595ece8a85c451e45a50cbacf2aedd775be0fdd413d44301d5db28a25fc8412e2a28e4e33bd176b54eee796bc300cd3d536f889712e1a0aad307d819cedd13a8abbed236376ad97d5acde39e0c8b0dc4d764ee788a0fd027b38b4bc46b09a7da5ad7f8af689326fe9c126a33a8123e47aaed4d5e877aa00d9708ee83d5b9fa03b2e24aa4245fc0d7e9250fae7191b292ede39b6180ad423b152d3cc3db2aed13f19256c56af480663ce8296ca90c2d85c2d694b51e6e20a4c03571d895ac7cf59e35392dc3908895026785cb42194d0559b3242912ea62adf77af10cd491f81184ad570951e233a8648246f3ffbcbdeb4e0f9307df9f824bc8a5dd0cfea8a335721305684c25bed635cfbac10e99964369e01ba3da0fa0df8138a289bef477027be8cb063844e59ff5567c05369b3d7fbc2d13656fee81b95251566f5aecb02dc9365cb51d522c3e368e3753313a9773206e10493eda00a40cebdc64ab5f42e68383442090734101028d4a3b3cc57c973529f6044eb1b1f09ed3188252c949bbccf14141f50b9c0d25f47019e1c0bfcdc01c9291ab7702315d4e4585011faf49227fabdfec64cc580697ea62d342a6d27227dd5a91a5b2a29035ec3e66629a2c99358f95d94ec98b50ef1b5bb59ee704b5470280668351ceeed6dfc8ba9c4627663f1a4b363425195d7bade1870885f1c07a8a2f8e82029d24fc02584248d25570148345a07fad19c756a1bddc07a8e0c3e0f72286441114933efd521c51bea3f3cc320adb2deac91b89e6fd016363d0b7fb6e2d10d4810e91cb4e2713ac83a62d28cb182779fd18be9f3766c7303ecff585f1c0bfd9503eeff54d606f92e1f978c61adb70cf59a3c0790d6c8184b688b47d10f28c5db24fc5858dde57e4fd4316c29873ff0f683c46c50af520b86b6bafc2505fabcaa0292ec8178651acd6b90460e6d65060be11d41ca74111a3bfbdc9b1d99cf7b3b221aa8a5f4789dbe9b007c91120d20e499db36f7abd83f02a1482d752f995987147cdb505bcf5b9968e82a0705a22f689f00bcb5a30ccc8fe0b25a99c0a22a6c27791efd79fe58fffcf59fa1aa0a7d8ca1a8e84fba4698840c6df2e048272a3528637ef304377cadacc8a86df22dcca01c4a63889492f030c519472e1f869ca329e91c181f32cf54a5bca551356b9c4d0c60e538977356b9622d597db2e97fb313fcc82f881eae7053f7aa4bd0edc0686b34acb24bbb90ba6757c84c3b6730ef27bf0f198b9488a471da7334a60d80558b08ced4e9c1b01f79f2358160268c9c1670d5334f325833ba1db22411592c14af3e83369d03ae3da83f8c0e4c9fd3620c897530e597293444e28daa1055f7d6f89f6ece42b1d9973c3f0c897dbfcf96160fa66e6c9c8bed3143148d883ff4fa31c3d6fe1ae6ec6e8b5ac3b840455595109d6b068a85ded28955858559d700697ffcc401df847df334026c21cb932da1072ad661a6469e211b76e5e7eb160581e3b296b2b5d771babaaf6508a29c034817feb2af6bf325448de4c7251b9e24a02dc474c923986346e30980758eb0cf2c19b53befbed9bdbdd43bbbd73b2f04cdbad27b9ebd4df23ed8955052b9a8f6fd1c32bff4a07aff3e1e56df730f2e2fc216b4f9fffdb5f6d53318b4987c68def42c021095a60702d3997ce3eeffeca5543841ae0b4a55349381d1222a2d6632b4244ff2a5766dd407d0cdb88beaf70bdef8c46ca2dccb6cfee6f5b90a61dd965b0186fcf84374a039cabcb975e7d50ebd8982c5a2474c5db21a6fa262cdb79a672220b8fa06bdf4aa029113eef5e332003ce5f03ef7618230c3774ed61c0482860128f589977a08a12e68b2743a80badf484f63bbeb5fb9141c25aeba24bc878a2741422ff3fed8720c2eb136d8d540db48d4428fd9b59150168bc623b40f22bf213fc3d76986485ba4f955af7475afdaff9f1a12c63f2baa6aa7ddd40cced56201d8a974e433e042073171389edb7a7b5e946148cba308dd80a02e78a2ad3e23019ef96b61d3dd0123805d495b486d76d0b9f3d8a48f1310ee4c733bcff3191500cfff383f27955e4ed578f043245bbccde47fd234312e389039a1b5bcf90b0d194297260a62a1ac783a496200362453eacf59e3be3dba243c1e33541a51aab521df84868549940a5b7e545b12a4c1fa0ce838bfc5244ac69e8b2834c7ba509f2801c3e1c0d77b6ab4c4f13a5e459ae4720f3aaa4e5bc98de8236f2a61188ec12326b66e96fbb825b539b41db33e51e53a6aca36b68bac613b91ab1b657d99f2187e9e99857159b991e1878086e6b931d24a8b5c391f1194461942c0ce522d8428dd1095799c7732b2af1df9ada0adecf15a46454aac9374abae6fc565e7aed60db2b57e5a24b226a0484bcf343aea6affba8c8997f19131a083d7da09a5bfaf165988bad45dfcb328512a710823a8a1743dfbec870e83e2352929f624372b97064e9d0dd64209c66dfd55ba091268d173e98baf277f969456077d749ba215a11417fddfbb5e3f7b410bc1350064abc3c0cc74470c845869eb2d57705cb5f11c21b11c833089928354ac0773b5bc6fafa12d4e9ba089acd82035f8a57b5e7d262829fce2cfa4e37be9fc640b5205689609d6ad185c87cf437727cc12b204fb310f676fcfee6c78ee2f3637333626d227af0d92d7912ff4dbe346b094b32bf36108066c5a3b416577bf7d266bbec4812447cabdadfc2e1660ee003e241449709f0196177629bd7a488502404a03c9f48199bc0d241b8f6ce8cbd8da78fce4eac72d68606740bd2a582d2f784d3cb38c610ff9c13bfe676485e7d02062273aad9a0d3aac2f2f3017e6d248adb5ac397957e3bbd8d1ce038afe1ea4b5368f73ea7907fb6239d7054a16a6139c7c9e9a44e66ce18c15d31ba66b18b5b7ab4d399f4706c5d0479decf2b5da336e6d2945c5c8317d9060ba8df6fa55a78def98656728083b6fea725a1e47cc8e7be08a999f1ab21d528816758209157ab26af22d787f196820105c4640ee968e4d67efdc9a46831d6b9623783013ea8361076998a3902fa23d94b1eaf9fd2f4676328df7a1b2081a69ee6b0d2ec1cd8437cfcfe5dcd411102263ba18952e56d9c59d047986456fb27170726bf7d605dc9c784eeed06d0d59a79ec52c11a1425f1aba6149c77a9309e8838992a803b339c6bf84db99de2e9e4148c3c43b740f6b456920ab24f7c0f9f820802051f900e30d3fd65e2ac91b1a57199f8c82c5ed3c86433a63b8cfbbb9745a5335cbae916502221a2789522a06ce883be2fdcac598cd70549e8c9f4927e327b836a98993f5c0352784ab4721bb59a473265104f1443983ba0c1f39583416ffdd1d897cc07d525218bd4200248199c942d674c166ddcc723dec117f0189752a57e4c763a2521f849285d49c0ca1d57d0bab91a530614e9a27da6aeacb64e5afc8878ba1c514d8579b1b2e762a859538f5ec6ea88523485a6e209cf838b4bf1225e681b8618809bec3778e832414d4c62f30678651e1d582a448252a16039e7ef6432892c0736a508c25ec9521de8dbb5d21b94f9d3f7b8520bdcbf31608a972f58ccb140a0f08bf0f3ef76b2f615185f8187636d59208375e12a170ded06c8f4d95db7e5a384c26312af709116fbc8fa440f4770f9131ba7882138ce3395fee9afb912050f9735442c7873311d08205fb16fc22d5fee5654380ddb1d88156a4f652f7aad1b5e23f9bef20b8223b417fed4192d84787d35e607239d822e6d16da4a2bac6f06d21267fde3bec0dafc9f0651e8abbfae8f450051d56071f197afae930935305da4d9dbf63ea1c0f62368f70f8a15866c9150ca5de551b11148581b7dbb3ac3a91104d89e4f4bc125e5a505925cadd64a4d7c104c6fe4792895f26b040c1cc46273327d06eda7c2d3de1f00f8dfcf6f911397b3b2c20c96d905efe0204cbba45adc56fd4ada7f8169ecd4306444313c846aa1d6e7881acc2bb3c32a9dd04bac0cb7043d97e45380502b10e7ee88623edb4ea6ac4aec8de66cff047b3a0b8de7e7030100434f5c11cbb97f6700728797787cad7111709a20288c667d7d4e08e095a40ad9b05be6e0552b75ed7fbd61c8dbe1165f2bd9a0e119b596999e49d83be09dbefb7e529a909c76b5258e8aab2f9b0e9e060a0e5b502f31647fd562b198e7a4aa3fce1b6332e35a4bc64f6d28fb7be4acd22efd02880227c2bb7de981afe547306c8a69f3f1cb20eb9fb9669531084d45b0149539882d12da0394612b2283947a289e615f113772c23cf3f00e8dd3b33e5fa8f024c53b3267b463981f72ce19e96a80dd6e61ada2e13c3f530742cfd36835e8a97c9a506cb699e0f86df3af34c52f4d3b1091213e38fb599a06c87eebc1ba26ba8d888df60d65a4a3554f4a061a9c63d369926e680d16f1db522fc33a0fbbf03153c6e6cb4058a11c4615ee010e70c379b71012acb47a537212ddc7fb695b37befbef71eab971ac03ae4cf7939a7c754e80208c03b9ae2f8efa57d6b4bf62e2ab53bff941b81ab2004d4214c8d1c3e41acd406c7f3f77d48a84e3c60228d1a839558a6c5b040155a3afcad38b03a89e03510128eb4e9892ab0b92c03ba8d4b43b964caba607a7d376bebc5bbe59cfcc543611f71dfdb56ad567e3ab4868bdc28f681e1284d267fee5eacc7938a2fb15539342bf1d9de71f15ceaeee10e13f488ab8e3a45d54c7abd84723bc141f90f1576e805498646c59515ae476fba76baf7609653089dd1b80dea0cc074c25794f48124100a10a5a3953da1e19403d981dccd265f4bdd93dfeb0a3b65f7dbf3a8b06a0a04e1ebe52ece119625511b8d01969c973f33de7b4b3fb6e2022e30de838c07dd12002f5b2ecc6538fb4c520a8f2e7baea4fefb8c5cf1ef2fafb00f0d19c6580c1b5a9c9f9deee13a3e889e50facb2f4538c1c43d325810643c44b1ec7f5526bd029e6c6b1d9a45bc6684109670fc6e0c9f8b1f3feb00fee3a61169e9a83c9dd42be2c6001cf3592b5c2932f494ed9a83bad49f60473d7612ed60a635d3e953d0028e993169bec778afb691c637b0cae6aba585d2cb8905481bd3eb5bf6de1da71a308aa629517e68450eaa8e7ea91580bbbeb30b7998ee27568ebd6145b6e70c20161bdf2491f545c1dcfa7303f7705b6628a1094faf98a5f1a36c6456e074a10401c234e52c9961b76f238172189d91befd0f7ebcd9d79039e29626f293d009100affd6d2254e10219e9b9566f8a923167933d37e586ee3f614a5012d69a00c00cd99154a20de6f0f04d9f32be106015e53d2c23d60fd9da49730d5ce3837402a375894d8f12d1b34e3995b154874c873f9a3037d09783b1b27ca9306bf2c3308dc4c73e1b95df94f830d9b250fc45d5e2370dfa351cbb79791e1c2d94dffbf13d328b9f475e53fda0f9b44c28ea21108032367e8edfef64f4c4e977b1c47a205869eaafa9c978e967a3cac32e4aae0aa895fe2cc67a6ed720d1169b06264e92dfbc6a99923c8e6fed8f6a951c771e259892ecc638a57264a7351b8e94e26d4185960e8ef13c4be85419936c73a289be253b2bda37d06148e481b495152e8ec1e0a7d1fdbd3a0d7de9891fcbd88f215cbcde85b4563f8993450bb2fb117443627b76e42ca03ed9c2718c877c640e216665f316867b5d1517328f980fdab9c0b1cad6ffe013a43c2fda4ced6d18d4a900d42a6397ff6cf7aa04665b6430ef14b04cf5f0b8e827810f4b9e628df101f9cc07b7f9d21aa602466ef25cac111f75b09c0ed411b1e0a0f442764955adc807d2292c032052d814ffb11fe2c4f25f54d26089052c2abc21152d0c63457259245e5dc695b2c3633eabdb6bb64ee7d1f652e5651678b410b12f0e7060b8e744416318503d6545ba957f5592047ca89f52b0ff2a856af7f6ec8051056bf31941000957a5d7e8a1b90f6758abb559a8582e726e70d762a07770a22949a32efad63c88fce8ab69e7ecfc221aa52fd689afca502f80afe8af67e02f63ea1dae245ae6df1691fc2922e963dffb4b40969a2afc401e1bc978955a901660fbc7d320019a8e81c76fc5e7e0719a37eb099913441151a6250590a660df462c08f119d70e9089e5acdb163526fd9d16f3d51d7ad6c612229709e6d3b097463069ade52da3f6e91a6c09d975847a351261525b3ed8b352e7c11480d1b743bf34c1c314e8a2143c012200bc4ccf66adc4cb8ecd828971e1065589ecdcf966626c728a71a42830941c96a4490ad3a3f081750168fead13327718d11cf276a90ba8e97d38e258e68e543300d01be6f2b06c51cc2b37489910233c2b17cc751da4d859d9d4bffd7a3cb89d6e8ecef481ee10e3ea70c3a50340bcc3646db58bd841b4439c17bd7116250020f3e9d0bcae0d122f013145b049309d0b89a89b0b5542a6c071dd247117f7a9c11d92a5813ab2234bd046e5f35682dbbcc16fe1f0d9b1d0fc7947ee0fd7d0fa3f41aaa13e7feb5ac3758b09b43a71d39360b515dd22150b18dcd0f4161ffd6ce130fdf34a1f21af7dadc0b3477492c58230829031e24bbd5947512c07ecbac8974dda0968a88ec196ea2a32988e41960210a0fcbe12fe59cfc251aee2a958577820683d2474c03530b5c3af41daf11471a0ba1ecf08b931a8b592a4341124146f7eea929b5bcdf97f4309388e4832e8acf205614a839dd4c803065ada539c038efa3793fd5d817a5d37150ac380c2688e1818bd1cd9402e785e1ec0bf8f70c0205df96db354c21c841dc5798019f150636540bc651792c2074eb47e8f6b8c8f4a1c18d43da452614900f830f4f6b1dd3cba0b6cdffdf86d4465254183f3b3037e25e5341956ae5bbb3e5b69764722fbccb0f85e82ddf9ac3e1b6c0e3765db51e570310681c3500d64f65fc81879c0755923b6c99e531545dcc3bd316065c7da8959f0adef1052b4371510dce386b17a9bad3a8a4e29ff2ba56067a8b4fe001c8a17667bd3776764b957eb097479e0c0b11340e650a8c5ca4a2fdaaac9f751fdc22b5ad7665a23856456a267cb44f0e0019ad5b49a2017bdf0771f62e195722f18270c350dbac52a3a0d303d9309821aca0436a16f4a3c4ca41dc150249bdd0364f2c89ab3c895b0004c6d626b566124a30f423f8c8b635d0919fdce2053e0414360f57280cd37737b540e65e2d99041a698a36f3ab376d2fde74928c547cc481f89a588176a7509bae404405fe041e6089219e12b9f770c516245400171796d9380cd2985e7eb7cdaa8cb473a3f4074552579d80c0699366babab60854d2c33ac00b5160891b6372947f93e910ea1348213ea777f50a05a88fdbd4b2494a7d16a2d5f6ea737e75ecbc7204b9864f192fe5523992d60c8d84d1b6eba8aeee8014743fba88fcee19d349c13d3d37242e3ddad8d0f0eece179bcd5382678b95d153b45a6172059f81d391c8bbbb815c1d82c2b71be144549c02e0f170ec5a8b1b86d844996b7a89320b61611d12e3cb00775bad99ee32f45f565cfe39d8c90333241b8aeb8c33596316a9b9d652b1ec2d5e8e9748cfe75a2049627dab27a6734e98f15902aa1aa3cd82d3a762fdcb02200ed6da0e18cf3f8afcca5bed31a93dfb79795183d3cb7d1032fd7da336aee52867f3c4d1fbe72a72ab839b3864acdb59b20ac38a1a267f62e4dcf71924a2cf035af3ac007525f75d53985c1ebef4201300f1052dbc5d090342d2ef29b839251024bae62c29cb468ead61bfdad488c919537f50e85fe271c25d7d7d7b550aea0a018522ca71bda2137e14a43499e6da8e8fc21aca461520818a0ed89bec8d8a280eb898c57d587192dbdf7c155004fabce811991a165fadbc3175a4ea707b2829bde1a0ee2ea0a10fc1fd8b041bd6a10ec6ed6549761499d74b7d9fd1e43e9b1642c7004a01c8dad4c27982cb87a76a7cdd92324a1c4a03338e0fb63bc6aee71e0f7ef06ba08da79fc1af81885746f981a1b1ccb86849bd93dc4946da8194560ee663dc8abfb3b4f401e97d4868baeb3cc9e02380dd317eba44b77585d03ad818eaaacb65f3696055f484c63bac79187a5569731e961f428906f70a9df8da601171826be0373379ee40fb7946317f2add3ba941641bc08e1c9e76500ed274c72fd2346a922c9ed939bed51203e6e8434334c8430851bcc79269e800eb376f9d05571558d8ebd306a3f43867406dfec2834d7e60492d786f022ac77e5fe38a4e1f3bcc526e694028e64972abf52a540e5fa9c3657b255b16443b0f94cf1f88450627b6abc191058fa16cee748550de84eabb8051a54cbde1478d527b01fec7d722847c86788c1e29add90ff796f2a4b430c50ac6724b1f50fbe0678f7f63ea8d08ba24f780459ee2118ffe09e17617a9a875f5fa866244a1e0a17929f2d4b67502b26decc17b0a6833b9e1fc9d6aa1014410dacf458da78fdd1cd87aeff21e0001a33b055d9887c58a323281ca2d6f08372098564035043daaadfe13ed795c100fbcfb85ee33450f020afe5c6701108419af3ab19dc36b0136a4e09638cbad8618cf10d5da4bb473b20e2d4f869f6abd9fdbb3125899efcf8145c6dbc8eaa5782dd735114427a7368894596336986dbfc6f83dcf2abfd02dc49e5096425bacaa0f4fefe353c5aed743e4f7de346ded390cdcf6cfa6866b2e26e307463f5e54142266af3ecbae5dfc0f6dc6ae9c95fe0b53369b7fe4d333ac323866d5577a41ea2c6d2a0983c01fa88c910a18d05f4b2ccc64617912fb052f68ec8f732e1c4ff81494d7b3ada40bac069be1534e46036e4fa15b67d82e35b6c4c537cc8d69babc03aae75362394a6489321b24e0767b05b683c261858ceb4252792911c06b625b0a1733c96181dcea552b33390c474d94fba7f60c1e79dd8630594b0e85eeff922521e5e26916a8d161d83d01faa227f2a9d6444fef04c87f4dcaa6039e1777692a97adbeb62a33eb4089559d7cb7dccb4f04431881d57d6d895aae0495f4327e2e4d202b13b3e055f00130e17d7312f1057000096cb0873f6f4e75cc094a879117c3b3c7a747f460fbb0783f83b3b0854477e023042ca514e68f4de8ce774d1626e6ad5c9f8602fe6c6b5fd8b31137dd1233ef55f086f28918764c2e9d456f212fb0444788ce44fd4352e3a05ac3fd55d1da6c5d5242c206256f663ede364e6e2373732b6b18f7611b1e4b63ad4c976c1deadf391b5da979c5b02b25d5ece40228affd372dd8a5500634ea7f14730689049209d5b5d28ed1af43b6a02339af83197d38d1f6964f7ebbeb83383a01410d59e08e3753b29980e6b536ae638590652a7b9a117822013f4bcdaea47c121aba0f791728da022a30d4aada1d57f8c623068f6b97acdf1888e3c9222e48ff811cb06afe6817952a5f84e27bd6ae7e6eb4063954f2271e0d8ed25f91193aa8f92e4a1a8c16fecad358545445169a129aef0d65d2d7d185b145537bc3b2785e0d7bc7ea6aa9309a2d0a00385348c250b3b614b1fbb7aae00ee336a9c40c652b647742a3607a56be6b253cd9152e8afeb67613bb31e1b89554b30c9ddb90be8fd9f795054412260ba7f17a74233ba263c9311c487638106e346d6570371469fd0bfd06b4db971cab7d4ef0990f3af631d7a41b9dffe4983c4c2e984d47ee3327e25e0e1bb4fd786160c4b34c846a56b76bf91c8277ea0a4cf36eb760488a05cc2aa70bcee215bd2d93510b35fc4eed1f1b4915e88f5976f5793046aba0a00dbccd7f3a316beab9510be08c9f05e784540a9e232fe8b33d7ec5ed4a277e7b0c17e27239a133d82cfe4dd3adad6816773967d50b2b89728d1190f312e5f10e8fc7d3ee8218ead95eecc465d5083cb6849c4e6c2c2d359afba7a0809dced78cd7163d88caf325866d1ebf80dba614817c3c47ca0594cbf43b6961415ad022c67b6e16820cca393368b51f821687ace8ab744039174d28be0ce2c8d9b07bbdd2972d52c9bfe6f26f55ddc67f015df08a2bdafd7512a69698ececec58664bbdb7c7389052eddaed595852df40e2666659e661a4972176250fff62ca3380e7f0e2fe8b22b2d710d76e6fcd4ffb0f7e8c9ac6c1127a2a83a5c61ab4c3749440a8f664d7d31bfd430dd80ed0cd48b0a70d592add19760701d3a991136bcb410559e27783a49625e8988733857cf2629c9197d174a94007003e3884b2dcbf2eadcafa5dd6f4c0795d41f78535c9e78424fd46ddb62e50251cec8360443fe70e56dda0d20550006d6fb8144d28755b55e83dda5a22dd8522df0d8beee822666e8eb9e52c43c3fbd074c41ca47c3bacd979ccfb528b2f2b0882f525851801b114af0f703235ca9cf8f9c9e50b2b5224430225b371379e7a1c7239a50c48885d57d708f217488080bd1ecb6d58f5205665fd593c9e0bcddf23b2f4eb1ef9301694a34e8738179e8e1369a60fc3fa11ed46407606363f52cd24ee26f4d3910611f69283f272f9ccc2844c3411baa34e8da7364dae8a75684000f0b2f9f04ff420e60d1d353ac9d13e7faafa17000ae8014df2fbf7d12c3d7817223f92516a4a8bb4f0e93ae2be4207956f7fe6c126daea596e4821329737b12f0e82c3f01cdb5915dcb711359d725ca71d8db88a20194032ee174e0296031b2f696b4545925563e1d1517e2c04b20f117d21ab1f518da2275af79d25e8d41607b2f5e9c795c05bd09e8e258b198a9b8d59b9abc7b6f80225c58cfeede76fb3278d3c307fac2e86cfb0dacebffb44e0c6dd9a82c44b6a09e480a03db77457542526566aaf260a14c32b6116575b481d6207aca05141d8d2a9d491cb9a1a99162696ec670c96835be0dbb7acd8345d7f58bde2465c9411c61efa95cf126e0c660a6cd0352efca0376e792a21ffb8bc5c5c4efda9db0450bb0ace4d4c9fa128b4297c7114a11be0b0e120b51308938e35bda2a3883517ae4eaa1d40c1b06f159e07d21576c33046501b0e5fcdcabfce665b833125b78d505ca673246352dd27fd1485ef6fb7528e432f977ed5ec10b0b786a819607c7750295556208206f2578925accd4fc26cab678389452aab1b5c737641fd584576f8b5eec4855e12d9ca56215db2dad99e19a7bfb18537fac87fd6e2afd076a160426081c063d883cf07f20a0721161fb081ff18cf9984812240c76a5ff5978697bd7ca060816322c90ad80126347b928a7ee34d2fa4331c9964d45b20e4dd03bfccef79a30ad07bc86b641d7ecde5d7184ccad6aaa66d296fefc304910def84095cb11e85d41fa5aa6b6e164350ec9827f1da24730efcd821b084047abd9dd6c6926526d5ae954086b0c510324f8d63c5d5cf2c7a0ec72078e8935b864d4174b1d65c9e56916404d56e472ada136e932d27cd277edc82d035efc8f117cff743cfd0988b3dd87e8c4af85425b0f856933d7f16194b39760fec8c7ceda5bfc1954cb224bb267af6ca4e2a1e0aee11946cb19fb8bb526017499058df0bfa6e932419e9114c7fa3504bae3aa52a34fba2cd6e0d3e3b57aef491f62d5cbfa2ca81503d2432eb3718df6baddad1042cc2b16fa503487386d885f97a0165000acbcfe60914f7f378bd732ac1632e2d3c231c0dc6f4f73190481327e93a6ea070953061c590c61cac490e057ed68d1041c85c671f90f92b4adf967b23d4b3bd487d88f2d20ddf0892d88fe69e7a732440efc7fcaf1bfe1c4447b2903475b6924ea834909b586dd15210e517abde7302648b539b2d3205bcca3c5e04320030fbed6bd23a791adbae7b600d61cfe58d0ecf9b32496dd8b49a19bc92e151bf569e3cdb51f522c92ad5fbcfb3825b2b4f088764f0347e072898b819fa4b7c650c6bf5bd0e3fa673ce756a382a7d7dd254265f60ed152b172b49ef4a87ec2cbeb0359785bfb85c5cf704640a8d891caef112dde0c9526f702286bf5ca74291dc3d23150254ea8fa8d7153ac3e5992750591938e09df761c13c4b4aa1921e072bfe257472220140d5ebdaa8cae7f560e9f714c95c52b4894db5df6c247c94baf38f12096da6808999fd18aa6670c88065e80226628c983330269161ac7017ffc0a4ffd32a3327d9a7cfe8db6a38ffe148801c6fde9870699e806e07e05aed0ad6e4ae588914c12cc74e63b4f4d30552d5fe051a876daef9218fc92516085c9864b930e810f0b61bfbc9a395b47da39e565817a2fca920d257e9ff9bf3eee1912d6c229cb578f5b0a15337fd914f134119c5153ae19ad490edcc124686d9fc1788c029d6cbb231dab0867395a3ced52cf5992b7aaa6d12151bb3ae8c3473238e72dc9fe1c255632cbcd1fc9481c38e4b3f9421b2aa75e8be1e3298246d5dfb689746af68ca58b6286b688406bb0bafc94c66286f9f12af684c6ebd555ffff7db08ecf3bd2fa20500b05ab8968351747ca14a0c9d64e6beedca294c73ce5617f6978c9b24e519125c94ba09c7ed874a80b7620feab3b353605c010f4b857897d7e39e3a6fbc840c848b4cb68aad5f3479611f2004a749cfe35a02ac5dc89b5393fe30f7c2afdc837b159c7a2ff06e596519c7116760cb3b73de1361f18d384c55c9e3540321a68c4e2e44e5c941b9f665059a1e008a16dce409193ed8cd54ec0b06a24ab4ae94fd5897ed4c4cdc5d1d3dc0b826c0b6ca7c81d0d98b62d261143a762da94b02604b811e3e7c53f79396d23a5b19f8794ad7850f1da3c9462a0716a77fb87fbe85d34c610c65e5a93921bce8160c2049a123f1e6f9357851a2900dec44340ab20ced52a80cd6e5ed17b9dc19471e2ae29c6cf98c7bec7d33c1cc38f39128f019367465a12455f773fd0ad443e0230482f9c17e3187b3ee26ac397edcddeee325f4dc38f7003776bd3f29645ce016d755ce4eaf4730ba5bea43cace5fc4f491b75bf4f40a1ef5d6880f57006f2e0ff39ff9153f842dbbe9239d0c5f12fa491325697746afbeb24c3fe8248ce5170bfa24f3eea9fc6bd7569cf327cc1dabca0c6d9334e669a0bc6fd382665a7bc60d5921f4c5b37845e303ad5f87cc337055eead375da43220395f7f258e2d617a028900a614996a6cc5e37c72feb25276f20428853d95e38dacb125fe86cd90d52c69c68c578991bf0a52e63bb34af47081cf8a6bac755d8a48593965adc95aea177dfc2a3e04caaec905d3c695a91784eca3b126da5e9c866d91c683b6cb3912124fc0d5d6688f89d4ed73b54d5f3e34face890f3958f7f8cb41dece502429942f9901794c73a7c09cf36212a51fd0782cc06fe30ee79f82d5484f23733f6c13233f93f7016dad4e73fff5bca533ac101209ee573f1cce3ff1fee1c719056c5e2ae2322639c3928eed49f97f1f1b48e503958593f036366eb21e23ce36b7214a1f59eae5e142031921a7723ce84d0724174f2ff35cadc02308f5fd66a0d59fab2fc15cd83713002d3fd0a2476d38b7851818a1b01b49ea13b0759ba2508e928807cb3903616113ef1e11f6ca8ab55043807f94c991add5aab254af2f42fc3ddd0cec41674123652d4628b6d3fcb5b051dd82eda1c68d133c33ba1bbd22a595c714a3ec3875595951d46b252c14e88841e72a69079ea1e1a0ac9cdb4d41e799c61a6d52ca385bcb12df3bc6bd2573d385559eb994f25c61406fa247a311066b9b2d6da3eaf9a50dc2d39ca4ea36890d8716745651c84cdece99eb27167760ee320fa5003bb3d07f7bacdd3251bb18c32693721a6558f007fb7831c0a7f1d8f0a7230909ae06b2c389fe18276a448db3414e3bbcaffa39a00de317b42e1d1be8fb0b3426ed4de416d966414a95a64f0006ccc9b753425580ec9c2c84f45dc83ad90471d2c0fd4fe6d06a3a279e72b673320bba822138c7b151d727b6abd414a88111fe52b8b0917acb88c22681ba264f140ab7b2cf227252babd5e83e3fee517361317646fad64650f2d6fc29e552093d4791db40cb5752ea44f8f1f42cb550c90","public_inputs_hex":"0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000154d2d8b9b5e779fd4a14022ac64137900000000000000000000000000000000fcc98eb31a0ea5ce49a4df5434c3e52d01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}}}, "test_exit_code": { "crisp": 0, "folded_export": 0, diff --git a/circuits/benchmarks/results_insecure/integration_summary.json b/circuits/benchmarks/results_insecure/integration_summary.json index 213444f7b..fd27b1262 100644 --- a/circuits/benchmarks/results_insecure/integration_summary.json +++ b/circuits/benchmarks/results_insecure/integration_summary.json @@ -8,164 +8,164 @@ "operation_timings": [ { "name": "CalculateDecryptionKey", - "avg_seconds": 0.109944514, + "avg_seconds": 0.109601222, "runs": 3, - "total_seconds": 0.329833543 + "total_seconds": 0.328803667 }, { "name": "CalculateDecryptionShare", - "avg_seconds": 0.607762083, + "avg_seconds": 0.606677194, "runs": 3, - "total_seconds": 1.823286249 + "total_seconds": 1.820031584 }, { "name": "CalculateThresholdDecryption", - "avg_seconds": 0.557054791, + "avg_seconds": 0.556015083, "runs": 1, - "total_seconds": 0.557054791 + "total_seconds": 0.556015083 }, { "name": "GenEsiSss", - "avg_seconds": 0.125857458, + "avg_seconds": 0.124351986, "runs": 3, - "total_seconds": 0.377572374 + "total_seconds": 0.373055958 }, { "name": "GenPkShareAndSkSss", - "avg_seconds": 0.226792222, + "avg_seconds": 0.223574569, "runs": 3, - "total_seconds": 0.680376666 + "total_seconds": 0.670723708 }, { "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 8.377944667, + "avg_seconds": 8.412974209, "runs": 1, - "total_seconds": 8.377944667 + "total_seconds": 8.412974209 }, { "name": "ZkDecryptionAggregation", - "avg_seconds": 48.469535625, + "avg_seconds": 49.375812292, "runs": 1, - "total_seconds": 48.469535625 + "total_seconds": 49.375812292 }, { "name": "ZkDkgAggregation", - "avg_seconds": 20.0494395, + "avg_seconds": 20.649942958, "runs": 1, - "total_seconds": 20.0494395 + "total_seconds": 20.649942958 }, { "name": "ZkDkgShareDecryption", - "avg_seconds": 1.483909729, + "avg_seconds": 1.465726409, "runs": 6, - "total_seconds": 8.903458377 + "total_seconds": 8.794358459 }, { "name": "ZkNodeDkgFold", - "avg_seconds": 61.021601069, + "avg_seconds": 62.099165625, "runs": 3, - "total_seconds": 183.064803208 + "total_seconds": 186.297496875 }, { "name": "ZkPkAggregation", - "avg_seconds": 2.118477708, + "avg_seconds": 2.150821000, "runs": 1, - "total_seconds": 2.118477708 + "total_seconds": 2.150821000 }, { "name": "ZkPkBfv", - "avg_seconds": 0.337032069, + "avg_seconds": 0.330616986, "runs": 3, - "total_seconds": 1.011096209 + "total_seconds": 0.991850959 }, { "name": "ZkPkGeneration", - "avg_seconds": 1.3624095, + "avg_seconds": 1.356784125, "runs": 3, - "total_seconds": 4.087228501 + "total_seconds": 4.070352376 }, { "name": "ZkShareComputation", - "avg_seconds": 2.714979847, + "avg_seconds": 2.679413639, "runs": 6, - "total_seconds": 16.289879085 + "total_seconds": 16.076481834 }, { "name": "ZkShareEncryption", - "avg_seconds": 2.516941928, + "avg_seconds": 2.500371689, "runs": 24, - "total_seconds": 60.406606294 + "total_seconds": 60.008920539 }, { "name": "ZkThresholdShareDecryption", - "avg_seconds": 6.083372944, + "avg_seconds": 6.122451375, "runs": 3, - "total_seconds": 18.250118833 + "total_seconds": 18.367354125 }, { "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.096223944, + "avg_seconds": 0.101859736, "runs": 3, - "total_seconds": 0.288671834 + "total_seconds": 0.305579209 }, { "name": "ZkVerifyShareProofs", - "avg_seconds": 0.214040633, + "avg_seconds": 0.215221433, "runs": 5, - "total_seconds": 1.070203166 + "total_seconds": 1.076107167 } ], - "operation_timings_total_seconds": 376.15558663, + "operation_timings_total_seconds": 380.326682002, "timings_seconds": [ { "label": "Starting trbfv actor test", - "seconds": 0e-9 + "seconds": 0E-9 }, { "label": "Setup completed", - "seconds": 3.066766584 + "seconds": 3.048507083 }, { "label": "Committee Setup Completed", - "seconds": 20.21209275 + "seconds": 20.239957500 }, { "label": "Committee Finalization Complete", - "seconds": 0.006218875 + "seconds": 0.006028542 }, { "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 299.47482725 + "seconds": 302.757272083 }, { "label": "E3Request -> PublicKeyAggregated", - "seconds": 302.025307167 + "seconds": 305.289772375 }, { "label": "Application CT Gen", - "seconds": 0.311998458 + "seconds": 0.309189375 }, { "label": "Running FHE Application", - "seconds": 0.003517959 + "seconds": 0.004053542 }, { "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 78.562423209 + "seconds": 79.623283542 }, { "label": "Entire Test", - "seconds": 404.196322167 + "seconds": 408.526410584 } ], "folded_artifacts": { "dkg_aggregator": { - "proof_hex": "0x0000000000000000000000000000000000000000000000001cb3e9df822c791200000000000000000000000000000000000000000000000e6e2eb24f99c6e4fe00000000000000000000000000000000000000000000000f0341c256bc7f8f2c0000000000000000000000000000000000000000000000000002945e9bb27e6800000000000000000000000000000000000000000000000ca22d28f7e6b41a5600000000000000000000000000000000000000000000000fa4ce39d3e2c6c64c000000000000000000000000000000000000000000000000c382057b093f299f0000000000000000000000000000000000000000000000000000d2ab1e847eef00000000000000000000000000000000000000000000000732eae124bf33406c00000000000000000000000000000000000000000000000825bba25f971304c000000000000000000000000000000000000000000000000f31aa002ce9a71b020000000000000000000000000000000000000000000000000001c69dee5a348a0000000000000000000000000000000000000000000000082047502ee34e43830000000000000000000000000000000000000000000000098bc3dc090da06bd200000000000000000000000000000000000000000000000a395a0475f5823e140000000000000000000000000000000000000000000000000001d21f325773d10b2b91c7a8b3d057b7bc490148ca03e7c07ae5285593e8de30cc1ac03ac33ea123a54330466872a485fe60682f01940aecf0a16a29400c85c1ff026756e3feb104e48aa44e3c4435c6a81422971f410e652c2a8dbb6dce04531118b73de26b410850267190a569b1e5c0d99d36551817631476dce47dab6ce7ddfa42abee9cd50d8774b5dbe2992589f46d6b0d3c14df64e1f145ecf602d7f07d6bb8b077ad801635d81fb371fc342b18cf95c1280232e65ec5fdf4f7edc0694727528363aded2adf3a83dc8a210463d985cdb56d2f0d39aaeef44f7e2567bec394a51280aaa41d9718c798bf7003a861694b93dc6114eb66daa0cc69e0dbfc33065a442bff1f24da7df7f66206346b9c7f067dc75502807746037df9556453c8cfe619c13bf2002ffe7ada7eaa20f50572c400c4ffc4942970b82e2e42a5569a83f6f7cb7e11032c18d50c12e995d4aec40ac9bef1f9e92449cc1287a6ae05407339c176d21818bac164c691fda117bc7877f2e7bccc8e7d7257e4bb51996272017b55b3dda603b572716b3071143fad60717290ac7522b0944c91980411ff8755372680bdd7192dd2340cf54de8aed55abc38be8e4d864a57bd60f620f2a66f008218a6b5f41d926dc02c0c633628367cb8e460116ca71dbdb7054035eca657119596c3de48187112be8a3281245ce6e500f797cc1432a4d3c09f50d55d3cf2a76c072123062816948f10550ad9cc66240c3475cc3f7e1be8a364b22c852289e341fa94ef710e359bc943797bfdd06360aab35078dcb0f5f34feae065749d4499ce323c0c26276f732dc63c13091a70d853fd6f9d06c67a9219b36ab6cfc590fc247994b9cc26f3325182becff2efcc85ba9a3c8a5d260edc035d0bfc3e0845aabd9d1b893210c0d54eefaab1eaee3b1703468de3bf3b65973d385114a22b661f1b24e99a9a1df7f33c0a098f95b18ac88841857d37fd0d01befbff8eda91f1ee54b9d9bf4d0972e399e72488027be9753aa1dbcf846b5d614dc2e83df5ed880a51ac5672e90569ae383a1ca0dbc467c684df3bc238b9c05156103aa2aef2eb1fdf18fc875926636c6e183c34d39a42e830a4f8894c70c00a993805d0013500ff4cdd8d014620ce10ca69f17a638c92367872e4f56e8aacb16fe54754cf3f56c74e9296ff6823e9d239478326b2dfda0f711567d671b7c617f59f0369ba10265ba61a2debb21dc7411bc598d5fa875f66505485e1674af48502bfb1c69679777cbe6b7a901314fc90eaf3185389206b54c33a72f351e81beb8f7b0d26511d530a8eab1b28e11b330ff20265875c8ac3b81da94e8665e4a60044918614ed85954f5ee5ab759e1512c2e32b793a0d0acbdf5e4e38d9cacbe258b66de1622bb1fb345e98b6173d2a8df716393a447531eaba0be23f1fbded411e71808271a3a82b4f9975c174051569589970a0511743bf47bd75b9f04e2f7a5d4b7deaab3ca11d0dc741998c2a030c6c5458d176c04428bec5a3e0d8e0e5c56a3eee999b10f4e41008d0ee6c500c4d4285ef91799d520036adce78bb4c4d435e0379a267084b998c065e2b5d2c05da4a111ab012aa81a0bf2854c7c58136cb6e2c629474ecd90a5041606bc645022463e9a9e43918859426adcea475191e505ee6a37d3afb0cda9aa1c7c788bb0a88deee1aefc855d078af3172244c5d3f21ddbfc514c0836a3c498dea40a5511c6fb874ae86b70059e90abb007bcef2ef2c247cbc5c6327d1d53a26e8c39123107878df9ad00525610d70a76cb32b6db209f420c58f9fb3b3053cdd884ab03303467453d17b995bb31d2bb667f55e5a6f98af83e14c7d2f71f661cbcb3e29e42855c0566eec6cd80417f89b09a4dad9bbf28e9aeacde3580fe7fbe92cbc8d6e23394556cab576138d117b28865defcf49b06e5b82ccc8137f314a7b3f7c2bec071311be89cf5d785e50c106985d91efc1623bb02487a838a29ba70228de3cd618e92a7a82355306a10a99302db1b6f705e6e6ae73cd5d6e3e7d5787023be31124704e1185bf63c8a0578e04b8fde9c99c2f4f5b96431334d6b4065ff89d8cf905c47b96cd804bb285ce1d70a99679ce64f47a71354031d01399cdcbd0ad1e0e06ce9eca7c849eba54a12dbe233f25e8e7a041c192512e99f964dbe30ae60c9b0d2152494c0e45fe1ee814a0931add16233f30a51e9865d9f39e6c5e956c872b14b51eaa8a1c3607325a9b7daf83937fb73caa1fe79e4791869cdfd75a2a4d662cbb0dd627932ea4a38efd1bbc223e92e269674601bb0042b816acd8f55aadc0186ec668949e62b5cc325bed81f906800ee00b52c5f0c3dc0fc6aada1fd6b5a823e1d846890c9bf6f66d991dd8547b31d71058b9de07eab2192c8afe637c651f161176928f1ada22ee5450992b6410cd20f9c0cb72ed0aa36e7f92d47592c53b0b5e2f62a81ee85e4454543894d2e45ca07656b56cec384333eb8620438e1dbb07d8b15737f5a26091192263237fb3545f2a7c2cb52e27d82fc63dadc2ef6ec41cbf43034f22b18a487f1571b7f49ad6cb43662c1d88884ddfc4c6c636ae714d0ca4003603acb3a8a4ba957d6cfc7cfda372f89a2f389757dbe339dd2171f5351baa09fa66e772ff12885bbf3be23fcce81cb59e6021505417367c2f49821f7823ba2358ab38328713a2efb8139298fbd22899d5669e32ff033d4e760e7d8f9d1b3399208db97688cd9c02c839ca9843236e13c214a2d8598e601cb5697ebc782d31df4ba699e8ee8ae919ab36f57dc5ca0cb0e5ae37e10a6706eb18514783df1e89de03b764cde27719b9536b5e4b9f15842d91513fcff1171e709145b5e5f215721d3f2ec845743d4e522e9f27aa662317ca94c561e19a62a3aab50e1d093f097730e2abd72721923c2513efd225ebd9e4b87073bc3c5b2bde83296d57572211f90af331fdef28dc12ed2ba3e3457e2d30e25d1cd26a66b9948b4682c7ea34143d8dc9ad72dc0c04b2a9828c7fd365f728364267489a5defaad4506e3528fc2313b419a3513eebddc60db948fd08df616379ac05f981d69e9e6ae19d5216b22b9c725c9f56c6c0542f7d4070281c3d16adfa7280d2f7846be2a75c73b99e5e230445761d0b8c4cbea2c0b6f7bad692ced4087f1b72de9ee1385140d3bf59390dfef89575ea75a6c0006f14bc476665d96fbdd305c046b7785bfa3063e49fe20879f97e36487df6604faaac54f65709de50110e7f64b62567aca3d6d1478b222ec9289fc1f61f423f08706ebb039d8a224625cdb422ade922c3c0d15036b3e80328f349c0cef97e0a1260b8eae02768c399d5b0f0f29946fb8a18437fda491114ddfa6945933cdc3521ebc1f70b5a3cffe275b5fa04d6d087e1142dee32f46919f1de02a266f6698e3e1f7326ea2c0e5a0e45dd29d22f412e7d39f0cb8f0fdb140d8a7588a9664f65018b4119f5a9d841f67618f140b5c0b43d85b4765a1b292e1bfa2fe17a6947d95f1abe578060534da85fa9478affe42e5eac1c93ebdf8e050d285507fa80e7e8720eb1212f85fad61f1bfd5781c2f60717013af98afd0b1018f1d3953038b40c827b29ec08ad490c80187c112ad8cb0516affcf68d114715b47322c59b0e1d0f0d7d05d85f30e3c115d84a815a7ca750f54e65380a208d1797c705b503bdb86142e1b3581dffc3bab4936c6bc2ceddb9ba58168ccf34492e26801b14cfa1d6cbc813c6ca494101f98faf1cc503d28cb425effd439e81aa2a35364d8803d7a3099f3ec344b02fed23158c79ae5cc2ad43741d6cfa830fe21448fc263e12436347d0d59429d15f024abb4c992b911607b0e4f3a31d9496830f80f0049b464f3f1d4cb46954e071c61399cc66260135a1c79012993d59aa1b218b5f0f80f334e9274dcca5fad1e7a857adf7674bb638a9d0e1392968058512045719e05e2b0b9924a33e2769d059a9e92001bf1d8ff4a3c467d10c0226515106794a473cdf41066d07fd466d2d26e02adabc066365f55b758e5a7f7de4f11c1c63e88a4dea4ad07b671448ef5648964f5bf16094882acf6039d3d7eb07fd350a575b3c32e6f01c55e761b811e7a2c76e2ea0c2478f11844f709f68a8ba186d0692161774697b1f7fcd284ec73cec608c4d4970b58619e605e6c720bdc0f4a50ba4d96cbb1641bc70af97f43ee092264f76633be11a88b4c693c07633537e850d91d688b0abdfa27ac8224656f773bde1aa49655d53de94684fd4469d5a98251e3a5b80127d39d1cbcd95ac6b9942655b77c697de2a43fa9791a27c1a6b50dd1aeb9197a93cfdee8d8c51cc1603f26f631a7327ad19003afc88c47469cb195b2f2955b16d08495c1b318cb0b58965dfc7f8c37faf153429001e8c9526753fbd07480dd9cf394444fbf38ae59ab4ea32b0f1d98d4f5615abd67e895e03f9fe072ac6572e3170094d4b8ba9279275f46b832a091d5345ead1b14b425157fdaf93112579386dcf605702a30bceb04ccb587b8c9fd67db5a084ebe2c133e1f0de76153de5b3e8e2f0089b682f578cc0c83be4bcb066e2b8d23e41bf850e18ebf504056a78c10aaf829a5613e43adc2d74df5317560b0f92d1bf9c8a7e028dedd9c914ad2fd6e06e8b56e1043312a8ab2562b8a1811e012f39db2a42d53b53e8862002816727324c27a9099190fa3b9d4ebf24614ae352a18aa9853e9e51e8e9fce807c16f4d1ed3fe76046138217835250863c7372ee35d0e44df6ae7f3354a030614efd55b8fc55f11a1e630f4d62a90e541de4057b60f1e340fe299aaec97842419aa5aab5d02cbb119f8667ebd6596282dfaced456b243a1017e636257c00725145c8c1a7d0ced612168ed7785fbf218546cc3773b80d8fe9a2cd899cc67ab2117ff739c3882ac173dcd0997adaa4836f1d565c5fb896fe92d36be4ea354959626ccc45e32ebe2da8450b4c324e9c84e859826d026dc527092ad112e52ca923617a5a64a750f9973cc25777d4e10bf1bc54f366f5820efd2a4d084b14fde269812ed4006701b1da3b123cfea6825b7408aa2fa247b2d4e48e068400a90ef01fe21067a7e5de43254e905a3a430247e8e945fdcf8301f08064fa14fafb084f0b60ff3820f3a15db26cf3b3d472b6c8e568171b75e01a0687857ee3d697359e81d1e7c35fb460bce208eaf60da69cd3970cc07431a6c7b414c3f14335b6ea545082978787372cac6946fa0a5181a8edff08ae4951f0aabe83c4327f9b16fe126542a07056e734d522aa8e3beda35baccfbaf8f8b07d5861f0a93d872676e20d3c1066991d1944305e34c4dfef3dc1880e5df027313a00f04b9ed2f5c98950ce16a0b54648a6fff62bad46d6201674e4f6c21b626f6c16806c354c68dc9b4aa6c9706851f4626499889d5cb3c148f4cbdbbbfeb07e408def8f627609f9fb4b796ea2f6f8f2aa0c153f339c07e47b7f2a875ab7f7ac5cfce654ec42c87600f21d31616b1c342cbffa7ed1b1d4d288a521634153403519363147c9971f04133d89971045398001304fd850beea80c393312d40c8555cb03bc562af7ac3b349b1bf2cd2b92ae6a6792c1d2f64fed85ef4c1a5c6e8dddf8f9a579a54c916c60e82047dd1600e5431125d76b78b8c2a098f18e46f415869bd5dac6f83169c2c8d29f6b4328a0912196820e9d995762575d0f0debeaf9b5b0e2d270c5ce3b76083e36e08401f94afce6ace19ea8fc1fbb1af3f43e109914b7abc6a65fea2ab48a5000af7223958132cb267dbb555c4bc2399e5426b6b3a27105f22c707becfbacb18ad58306fb6fe953b77d8e16475a79f0a1e4722528ed68fea7f1f534c99c8d2db904cc0cda8066d588098e2046d1ea0630ffbfe2011ec234d0c48e8dc6113a19671866253d11e40aefb19efcd13c463084ac484b02f2dedce09c436be72443f4213baa070dd14cf108dff6cb696f02cd05fa5f3e95bae74dd28547273cc1abd77cdbcb00714b116dd81e1e08a4936266aed104058d594954937b52d87ca8ba959a577108d031a69f7febd6c4168783d44fe7287829c8a07371159878bd11fe51cb232f0921f082d176de4cf1ed7bc8b76c7b56de66ec2f752931bb6b6194ea26e0c6f72a98a7e7145a8975a74091cf36fb93f433c969dd565b1bc5cdfcb205cc15c4642f4084b54cee37e360892808ecc8dff740f475bc40d04195bac4a03e9ec76cad1bebe7afd1732cf16794ef6462ab017a80d75ec20e745783bb35803f4b4b617e11526b6e094e5fdbd4f2ac0531a43d1106a93e8052366edba0ecd93ae8e04f282d1333ca58b422ab5234dbc503685b3b1fc93606c1aa5b712b2379e365bf55a51e63f83c7934394efef92012d8cb6800663808ca41d82a5e76c71495a7eebc7629d59d3741b52d96dec1f9741dd750baa5e3e411bdcf0532fd4a0b394a9d911a13b695df6d5ecb4503c9dad0b958664def47dd332b4058bc0e9b1933ac7503c92aca8608d310e70c33a422f0f3f5417380984574f512371b28f66cddfa883c4006b04a9b3c7a3ba0f6804dd6c4df7feee728a5632d04660c6d954179eaeb44542d11b58d31f9ed821464cd0373912dfe4fa22bd19b7065abbc6ceb26a19981842a3965530707b4befb527c0fda0a0cf69e63eda44b13ef3d14863b69dd6978131b11cabd86e10ffed5d750876db45ed410584991599cf4aca734bd4bf51c781127c6ea4ab85b240cb1bd4facec08e873d35dde95121dbdd9d47a8793cb4870df0a1b9a78b9b685580c9dfb20a5e2a15fa3752130f183edcb97c5be401beb6cf107c0adc97caab3584251942f7286cc06fa9f098e3d246e7dbc07d9608b6f5f991a6cac58ef9eb3e90c9f8fe72538df09b2b7b4a577e8b5ad0a2dccc52226746f09b1124b9ecf6a30f133399dccfa765b5d4700dd4b3b9bb71bc2bfead45447c30a868eb5de7f9664fabc6fd95a16fbc657ed53652cb805c3a819bad87ee0eeea07613f74b099d871c110ad32d1351a8aeadf23e6d0e204def4439207b50b2d4202383e3c044f9459cdbe3b7bd460b412b228e7b2f765cc1c5ef19eaf6e417be81389227ca8073c6176027f2d7e0e8421339891238ac8b089c5d1ea4396b43b650d8c183e6314a3c894dd7e6e201332e5c474a08a6047bad68d93909b92aaede71a80a43546806bad9165fd9c426fcbf14a233da388ddbbc7a4b9e6ea3518db232d05a4a41555db710f110c8f3dec06f2efee927471e7c2956b89410d14ceebc5066d005f93104f18c112b2dfec079cb4063f6af28f97c50ff62e6e6c1e5b5d970ab0cd7969c070c5ace23eafb24c3504cd673d1e5dd2fcafe0fb5c5a8116ea8414c1441534aa5ce52008ee910e68da93e170405862dea3ad15760000906447372ffe6c06baccfc669d8c0abbbee6fbc66a530df1c8a15ddecfece85c3e59c99b2f408d6225a32f1c8a5bbcb092d81020c5c943918ca370ee1066b9cda7ac237f25e442ce78c832078a1cca4ed502d30f1d61f4cb9b31fa1c3339535e317b7ad5291c5ab8955c5fa35886e5ef299dee976d839d1659aa0b7798f3a04053fbb3e00a9540ae0e1e203b830e5076fec300443e13d97564007c833faa0afda19fe75d05b04d2cd5243a1c81ea8ad4d236a1cd7a49e82d0fe8260ef7db074dc985bc53273cabfa4034402fd5f0be8f50f61d64063bed010ddf545a2a0819da87aabd2522b9c66a1023ccdd30183e1c297ccf73678f5342d0f6c3fecdb4ea6549f6f9890570d83521bdafb330e97cda239c6c72c27822f493589f2c3a9a7bc45dab9f230dfbb6680e8f3f8d2d51c3d0ac847f7a3110440318e046edefb4d3a5c854747a2d517240483a68a859c5a2517f9d257642c2a95603c14929a57100056111310713bbee85e2eb28d2f56212ec3df83e9bc2d95901789082be5eb67ccfa5d9fb1802b54fb79467cc5b7d9105117cb0be5c3774ca2b71c5c6ebff3be8981994cfad0e12ccee44ba1686ddb45ca9e05c5e827cba306084515b046578f11a9e25b72c089771b551d6cda59fe84c62744869636eaadc43ec56d5050f7fba28c6bafbe32eee5865d3d6d2f7ae5bce8ceeeed068cb1f022728177e68ac18ffac673bad8d2e5ab2e2f406cbc4592907bf333bd5fc49dd9e01370f3c7a9b006cf71557d51827da6014a008f75d47fb0c6eb5f92f4967de88232ad37605c6100984941f681513d9f213a93bed129c2e3e04ade7b03081573e8dee5d8efe325fed0a720c9810144f53c1a953c7076ee5f1762cd845b22f20de501647c107b71a88d034d1383413369152bb657df8a1586b7fcd8ba48363c9305ab33f7f5b244b97287b02a2d924fac5ad4344918be6b863a65249135b38df4166c6d7dae73a36aca560c91ceb203fdfcbbd96b37cdd2a655d2db5fab215991015eb7aa3a6dfef01849794be8f1159c8fb2e0ac69da8979d7646f5aadd53063ec5ab68bd06439c2b9ce469ccbc19acb857ab262170b62d959f7abe12547d52b38a70b048448428f3d56e7c52e029413e913cfdc8db69f7e7d37ebbe49e29c75ce22209ce58b4ff9f1d9fc9fe802af5b6551d3a1756dfe71637f61bcaac595f3aa1989ccde9acd830305f80fbbd2d16fbd3956488af15e5d4fdeae1b5261646dd5fc86d84bb6b45793e77695de20fb8c84900d6f5e8f54a963dcd6d42e073ce04fbe219e2a4af3a8fcf31c952051db25526c091693d249eb293bd4503d1103aecbc69bbe5bb5631483f1b9594442e7fdac9e909c715dcf47095e393e79f8d5b4ab497648032f1629733d97ef3ee1d8878696954e5fa815d5d1ff3ec0ab8ab9723d6837924e59169682902d3db110d7cd26eecc981d01bcb139557c486c744018ca6bd19b5c388a7e077c6e556f706f4ddc0bf4429db4217165962bb635099628fdd49630587a94143c0c89ad8cc0243fa20eb7f688550b80136aba7bb80ef53272fe7d205c34de7b1300c99023a07a06f52b53dbfd06e521156b855efb8221e0f69072c5635a904f63b7d2d011126e6720cf2927e27280d4518b1cacd709c5918b022b40996e1768dd9d3cb128d1c9fd2a5697a15b330812eb172ce016e47f4bbb5635a1a22c618e23281b471510c77e93d57d882bdea72c28849a4c0516c9ee76b492065afcd0c4fd86d90de9c135ec9a551e0387d1f5937023543e78c0f1e915cf6488565eb451ac3b3e20a6602c1673f7387f668a16c81eb509a15ebe643363b15be3a5b325e0221ec1cf0bc3061a2eaefe0bccd271a1291add175632b619454843e7e1bc46e34441587b61e00a40a910d9fb377770ebd08cc804592ea16e64684237cda52ce30381696281719ca3144b11fe63310d8c20cfe793ff404ae5d1bb37990c661091a17c90f9dc81a12f6e7b6ca63ae4bddd5923718d2b57fe6d4f11c443e06d49767c13e458bca27169799b2d6e904950b62d0ab568bd90296e39f8c03b2d90222508dbcaa76112df2ff50a75f281063fc81340a1ed747062fd26481f85b13c2de3a476b3a73fb1b39392a1f122f0c51a5c476d7895586763837029da273e25e111ba071d6dce602a13dd662cd862fc55adf37d20d67d523839c127dfbd1845c858051e193399c2736016c9c2ff55d2c1eec1456fc6d05a71007b69fa06ffaeff35cbec075bc102dd0e482b0b3e99e947f8c7f8531842fa58ce3f6f7111c6e52d0d54a16365fe52e15426bf9adf5031e27dac309386109c90e1e4780b9d03e42a2f450e2018f902c6fac8c3a202c1a526fe1c0c02ee28473be3fa4bb274cdb80f39cb0f8c958f5128a6d2cbcaf93586738ddf271c23bec5621fb147d08c561a89ae924383c048b086408035394acbc323bda1909c591844a75b4ac76486651352f0fc28cbb946a0d675cdb218ea5d78641584ec8447db6ec0d870e92199d0711e092c7d5fb7a1b0d5b8659b2f4f09d665914906900169696e026127ec615dfc7f72172d4e9c9950e40b402f05affb26f8e17d638becc4998d96780e0dc21262cd79bb5dcac57f00f9fe8c5541078e8a05994bfae65586c513d8e93142fd0f38e02dd1ac04378db089cd4f5d445f6c772cc79813e2803f220cbfa3429929f5498ce561493460b3613ffee4ec8aabaefd4e078e34431c3623277623cae710755721a6889d28dcb682fe59bd2a912c3a0ea267afa0bcbc29decc7598452e8427ec96bd4e4ff7dad93279bf4d91fec451d7e038f87a15f6a8a4b39f7836384879e5a558ac7f12a5fab2c732e618b33458c70a287b9c392bf96e15d064acdb6c85b4ffb27a8d6c08f9403bd525b914f19ccc4e0752c1ba7303f3ba9a414e6e620de995481fd369295cc085ee0d4086cc322511612aeeba8435785e0d747f6ef41113e99f02daeb6b9b016e04769c7707b166fc721ba312ac44869e72db339d8d94f12280ef730872d27180307091a47a51ff40387adfff6cfe5ff23ca72a089110af0f3877e2c35c0111199a2422a0bb1b19e7ceefe12c2683fb846c5be7a1c6f60aaf4ecd576c6b2c62cb5a0bff53fd46873a6782deb555edc23bbc11bdbba75dd1be6a1ad6cf829f70072d47f9170994d097f05cf07012dc2b0b4ec8dd4d9c779b858504a67c6833c22e7d6ac42fd50951f66bf81ae3a02f2f806dc1b77d076b5890bc4f44044ee2f2eafb480f71d11792aa60ad86e548a74684fd1aba10723a92b11ce1a7dd6a5561fe49c7c07442f6276a9508e3f805c833fc367856062c57f21f63a55942ee8fc2e9471647d1417f060bd8d0c66a27d1c1f3901339d4697ff39e6480609fd878b176f95e7a6596bc761cfc02a612cb5807b33e7803fc5b1d79bc27eccc7d3f035177b4134650ab8951b1539c16d7ff813346f0ba46042ab279344cbe1bfb4ac912b800fca5c41a1e01e2e48367606d18a6ac9ac259eab77260b92273b43a0faad0559d05bf0d90c3d1f7b74a54cdc688f619dd581aa440f8417492d917847623a18ef5410b04c88ec70cd12b19d1b4d506a86d8b42ef83bba4b3e5af3c49f3e8026e2eb7d9fec7687354ac5b0887116157de802696db73422719c1ea40e944c3229d5159ce10079adcf9ceb5ff67124799050f8ec9e742b8bb1ab15d470e72d890af040425dc1bf62303ae3a3e7a96d2a43c893e80ffb8a28f4111b111aeb361103ee2e7d8024fd4e508efe2e2c0bea5a56827b8726cacc7dc08fa96011013de61510a2b1b058e3f643c735507536f749772c7baf6cdf30eacb40473ca90c745323467fd21411c1ff380b74345d7e144ad0bd7fa7d7c1fc7251fd916348bd38e0169732cd4a5c892c1ea29cff802ca57db4593be104a408399a46f970c0d6f84325a21b7cf69ee061ccc2a3a50316c840af78877ba416a90b831f59bced3bc2f42a89d5a9b0aa377090344502b3507058d31582b403b5817becf3abf3063e40611dbca2846cc99bc087b5f2324908a49e5e08d09fded93fc0fc1a98e7dc5279ae235dfb49b929674403a5e7215847f2c4263f4a937f45683395abd3fda138a1e8041e083ec5724df8f5f59ee81ab3d8f91243b4748ecb153908f8ceddc7b2a6132bc19e5dbc68d1159619cab24990dde0069d6923ec90219fbc634f0ffdaebd6e2dec866563e71fb0b99b341a3225d9941ed6f84bfdaf764bcf58829b65008f8f1ec19284d1e0d08e4fe2bb65238804b564e62228659033401f5137bffa7748ef26ee4d3cb886995198691f9bd7f6821dafc091f004ead76deff2afa175022fad1d70f5f312bb20b8c34c9b38335493d62414b52515b4c70bb5fd41efeb8bfe54027b2630d780f4d4b4c5f6ec6e38d9ac6e64b0771cf910a02666224d7c9930a400d381db895e295584fd6dd968103a6d4c18e438f5d3cedc0b55b1cccb54a12313c43815f9ee10284257ad86a0ecaa993455d03fe2c4d54daf8197032b9baa401614ced4399d79bb7beef9ce5bcd8932ae1cf525f565402887c83aa92456761d0abd91781abfb5469a2640f41acbf4ce293417771fb35f5ffd261422ea27deb808b8f1e7f676bcb61968d1cf3fc00f94fce26a573eacfc8c1638da80d4eddde126a10c89af520a37d898f88198aafd4d70804ebe05448ad486113edb2b2aebaa18e964f683cbcc9090e294d7a7437be1ca1ea74dcb36d84a2321bf301db8193e2b94705f8aebc28168b8a51c4ca04dcfbac794fab48303ff71240442ef421e250fa2c370d0ace9c55ea508ca6166436d03d3df8315970bbd8bcd844f4bc68f5b06299dab59818f88f7e301b5c4316aeb59aad5d5f7097eb27b86c0cd57fa81c703af3bf34b8c520cc3d2c5a52747788eb8199250fe53a397e6974413b277b9c70a27b0ac6f69139f75109d096a4f17c88c4e93bb3f9ecbe10c1dabf88f8fa6981df485dcd33951e7e060d99193bb414647953005e6a41a281ae92fc0b106ab1d0c649df7df67a84a19fbe57e78f9cd919f876ceb2600cae60afb4d422a6daeff0f3e8b0010a88b0bbbc353e403115f4fd89be0290c657993caed524b65b9e71e1354e3d251eafbfdfad286e16e963cc476f575b88f39d75d4aba1c508b0202150a22b7c9062f93aabc32cd5ee70b08a94da92dd9c39e7d49fef5343e8730231d1cf3ebfeb1b25dbcfb218b5dde3966565d22eac32cc996110a53c0dc12a9721325df2ce3db03f36341907cd84122d2a897bbd9e35d2c5e2827a496bcc38d982c2bcb25cf055472bf29acc26c66698133800299b78ce48a634e6323bae6db88f7211ad69ebad87c01ba4d11ef6b763ba53395eeaf888db5c71163b7d73cd536771ec8d7e9f205f0bdeac8aecfc6ab04715f93f780868392380ff39e7999c6270e0d3997a8e3c12650b84b34333bb27e91818848e7a01a41dc8f603c55fec0e05524fbd9701f9695c6f1a89a4fcdd01babf46de6fa4f6a1abe626ae1e3fb09ad630ba5ddcea5c34b3d7db938ce41fe53f504783b69bacf58a3153bf960af8e7b92248d686e4891e170074d3b614db88a20b1a58e021253a639e04e495a18eeb9502446d5a60190f1b37f202ec7e975678e6a91bdf5f4bb15cca09c259f6d3ad63214fb72a1b7f6880ae472304e65f140fbfca829ca4ebdea50884189a0e34ec4f80d70eedb8edd7539e615f29015d0a2a3533bddccb3ac3319709615a7747ddf91059fa99e04f1fd1d6961ca462b4ae617c861a104f13758d93a106de1f3ef546d2a731e0019756656b099fc3d53d47386b271558ce7f557490f78816a160b9642303f16a6c56c5783ffc1f2bccf9b1c2dd9095fe89596add52b8cd0f27e17e0870deef8f8bb27bb30aeee2710f187ce6177eff64cfb0a0830d2b2c4424784eafc0f2277e643aa11411f9562c2484149e3ad02c0550338fbbd690aeb6eb7f458981086439d92601f693280eb07e9b8d53730775f628b1eeb9ec29e40e1e83b45a015ec2c7f6a2bea0d0a76c6cd0d4eaa26f5b9d8b15f7ae79a36f878ec6899ac75163090de4856d27388b729d8cb911f2dbaac4a61755208607e4d59a2191a1e8a0f92b395ef84f5cca72650a5ec96711783064e0b3038131aedf028cdd0de22210c7e41132f4dc554c726402bd9867726f671be49f37317ee6760d69c5617b6240f2a0cd8114083dc0a60525f9b94e16a3c61893e7b4cd4f91f030662ac3f32b00e35a3e8598f3df274610f9957ea5be083acd8f25279cda1f89f8d5c05a0a1b202eda0b2b2846b1899e37ea945922422c83846190fed65f9e24351f0569ff47d29f0fa3f89529e14f881940aa1a4841ad144c7c7696ea6ccfc4ea4bb7212653c04f248c459caee04c2e37d3e7dc683dab19dcf65ea2b26c5d7cca848625d7efd0dabbb2426915687ef3abc3b069ec8307c050d038c74ab190645aa0dccb1421c04b258ff3ed12c8db75b58a14309d2e26d73d52eedbfb78a0776ece8ff981a7b04b066d7fd4c3379ef0de749d0f0d4cb2fa9ef181a6337578033e287cd292a492a7d9fee18d21a08545b65da4aa812835303c5d899687963bd441be43ccc703e23483ab9796681a8df92a34ab2b18d397028867323f31f7049deb704611ac9cb12fee16283124c9b2428cf4c007af6be15d7b5031a38fca9de3b791871cc113911378a4ab56d049babdbfe707e94528f5a3767e39df784cef4dd84c2f1fbf6ad21654e556af2b701f4a90b0250c325e831293f5b184cc33dd9a4ad1eb0593269089f225f0ebedf94b11fb6f7be7d859e0cfc3eb14313bfea9686703262927b4b146bd2aac0450d1a61d6759af2ebbd40d8eb989f86399f60a5667de00a28fded06200c29f6eebc1eb9e47664d46dafc3afc47d3a4ea9508c16cc7c0a80dde88a02e592616956b9a090e0d519584e21e33221560cedb330523ef2b7c0ff50acfd146fd4d838c633a57c87c264b49a494c28e364f8ef5c6f3fd011cd6b32a0c2c02d60abc970afe83e55897b2581afb4dc367930ac9fffa440dab2ec982dd410b9287a000b0612d4b154560cd9b564e94c8e2aae0bbccc7de765a416650df2174519caefb7804b23bf526429bb01c70f3480caa10501782733990703005f297ee2051ebb04e3c1df02fa1209f95314f1236c8aa37a21641a3bda5f949d8292a718024bad13583e6c37df502842df6e1ce87aaaaf5e5fd5ea2bbbe56be9111b95f20882e244c3d5cbe120a620326c3cf2852b2233aef66bbc6d20a3bed7394c4f49207a510fd873763e5f933d6ec9552bc5956c7c16e579968778d059dc5d3b95f6009e8c3ed9c8afeb8ee33bc36a209435fca7353b43d7ce49d0b7da67d8c3384a", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000adf8670373d6f0424af5f29cb946f164000000000000000000000000000000008f927aa40772310f80a858405907bb7011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + "proof_hex": "0x00000000000000000000000000000000000000000000000c231c04d197a8e708000000000000000000000000000000000000000000000004dff372f7e0128d790000000000000000000000000000000000000000000000075b49d509002a176d00000000000000000000000000000000000000000000000000013555c49a6d66000000000000000000000000000000000000000000000000e6842811517da5170000000000000000000000000000000000000000000000003669759c5c8027dd000000000000000000000000000000000000000000000005a31742b4a58157240000000000000000000000000000000000000000000000000000e79318c631dd000000000000000000000000000000000000000000000003f855d900f178f79700000000000000000000000000000000000000000000000660c1fafd86f70c7900000000000000000000000000000000000000000000000992e486dc1bb3e6960000000000000000000000000000000000000000000000000000e7f1f964c980000000000000000000000000000000000000000000000004ffb738420181a4ac00000000000000000000000000000000000000000000000f05f675e4f309b2960000000000000000000000000000000000000000000000079ba8c5829d9868b700000000000000000000000000000000000000000000000000011dee0a4299770832f879c803fd7b787b6c5ec4e1e0714f5aae691b0542def35cff3251fdc5b30f8d12e5ee82c4f7d3164b41bb4cdaa80237d61a4cb9e779f1462a241f80f1ac1cd23d6f8490793bee73dc357175a2b9ebeac1e5975a21e290d819a4cf1d9ff01eb1cbb093ecc02906356b0797a42cb304014e505f70f1190991810dc812e176297a3c0bad21939a9b1cb32dd6b82db4643d1eb8aec6fbc56dd93fde0996960c1cbdda9ab25913b46b163d4a332e799d201d1cdba04297f438ea4a4efd17ba1a289f4af62ac367ef7ad3197f26868c463bc7f7be75ebcd1f4cb0d8af9ff494a9183660f437d662a4eb7505bdf9d193fcf0c08a95e9fa826b850ae8419aab4e8a25f55283723fcc95ac9eacfe4409b460a0d1d57a6d77f7b524c405002c208fe520bbe73a3e2f784dc967fc117d7f6d480ac9960bad15f20b7ef9103bac9795b123787fc7493bb5e8dd91740730cc2c2acff5fc0b376938de79e664adfa8b9c1f2964d0f06e4c92e958f5fd77a5a49f0d4158de25b55f4a022794d66fcf0d66b61c71274729412aa51cfd6caa29c854f02b0f6381c8969e2927833619fbaea251280a6dfb0c2098a8eeb87c9606c13dd2fdbadee2822d0d474c2c5b2d29a7db431a3377b08f5f1ca652302d0ec96d751aa03a143ec73e27fc7287749e48007f121d76c44c0f508cd42146d4c85da1ba7f0079d44f7ec8d86385facddd56ec7d3b2c2310d8d4896a745ebf3284b67d7dfe6a70bf912feda40d6a7d735e2c6917ed0c4d75fcecb16b258dbfa38b206c4390536979a9fcee7ecbad671b527245fa30194e070f0536b206f4728be22149c5043f9162a2676b1cbbf5970efdc3f09adf15c02013d398517db992673bca32c394646855203b7e50f25af4e859fae1186d10b2529864fb2290e037275f80785700c29a8df6aedbd6742ba5ea46d6b7452909274fcca2b7ab41d0684c1cadebaf11593d6765db6e88be19eaca96735db527250a9d92a04f51e65149dd9b772f2e4c77ef2504a92b0ae5ef7216de2dcff50419e13a3bffdd680ec1c60b461d72df65afac17828feeda81b9a2441f16480b821b5d909d1c8242c008e6b4ad1af1d798bb41a68c767c9b3a45a60871364310b517d0323cd8922d5db1fd5bc98f56ae03e83eadbd0a64b1a960057fecf780d189106993800da22c6d624fcbb23dc2031f3bcf55b2350b901fa3b6c9598cea064913c140a7440455db00bed4312b796f57a058c03446d58c84672705a1af9a15980262ddcce5e03fa8737cbb8d9b59edfb4bb0376593116b6acf935b6ed928d1b7039574bb7faa48a2e45b9abd1b95ce656a2c3fcad52e50644ab4f430b092950713a9be10309a139e4114960f59fd48470ac69c1d3eb77b9ac02e1be09eb1eb571345da560337d679e98eb65ec13e231b25ace4b4f4327c012879a4eff8752252073bc94743f68c6488c2659ed13bc4777e9b31d819d3528d12989616b713b2a21f4561994e2b294dd8451f4d34e7f17f37b31c7659e8ccda79528830ffb2d8600f7028bdc8cc1f247db1c996a7c683e4ba8a621111c4bf18c193aa299e54a818290a2fa13fc299ed1c9655ffaa435d5581e27e321594d33c84721e01e4c976fa2f83abf8d7ad3101c5575de5ed03233a502c87ceeef5c8ac9c4ae7ce55e5a12702c4a8dc4290c18ed54297509ae69fc6e10116bab06916be62c1b66d4c31452e28780558518a9a0677de43d2381aa22d4cb1e05b371a0abb3474990c81e0045a1010f281874e4dc7bf8f4c266bb74d4de791f9444d6a979df2930d4ce8989d5f134d2b517b71ff043acc1e77ec06863b770df40820833ee29309e6d5940c98e20a46b2e7ab26ed6e58880c0c757d00d9e298b2ff637f68e3e0ae78d362684ac71f4ef526adc5a9362aa12348d54dad58410e69e9998c043ef9cbb9ca457f367e2b540b26156b565d77217132ab2cda06e373b5c8e42fb1c549218db94a975eaa04925195341bf71bb44848f66b754f8844df69caafefacf5a80fb7c48d3541b52a54d7f1ff24a7da4cc29a6ca8c63a6bf1f142faa1da9ffb6cbf576a74f1665a2f6cb8998e325cffedcfb1ff8ae10ddbf30553384be2327852ef6cbddbb80c031612944a6367ecc5ddec0bbdb492409ea8ee0b749023820855af207f2771efe415357d2e65146970c4e134ae290cfa6c78765c9f93f822f7442f38be924feed3075d0f4c505e8f412cd52430157769957d983cf833076ac64ff6cb3089a5c23e2a4b7311a60385b29b440ff083a03357b30a7ce34c506b724f7491d6397a5b220bfbb4078dde8510b9516907fc0e26a5249af99436db17ddf3e08b63330a4e1f2fdc4166cb2192df0fd7da156ff33c342789ebbaa8412c362670a8e1d1e3660d04c8ee09dba54e2480700559aaf372ac10b3fa798957ca140d1293c4ab7fde740b5485945480745e0cf536eb74a1b75abf1f942da1cc613621de9fe86c60ab972f724566c306515796c892b36d522b92c2c481fc29f1e711953987999e3898ae038f4fe31d52d3d91f94e01fbbb0d0101568ef1b2d02bc0ef78877e5a9e51cee134025275cd9aa251def979540cc5bc9963016fd04a56f42ea0c384d20bcb51b2815de4721915d31b83934288813e0914eb9388cd661dab0fedae3594e84ea0519ce06222d0c01325eadb2239c4281880b62af0913eb476a3e57fee4ef2564fa14495d5aae740b0a554e1b0155f6451c2bc68e136ef1b76a6d538cfdbbd17c5c2d8ea0571306bfc84903b7de1030e1596d801b8faa51635ffcf1a4c40b9b070f2fa3fa1053239ff9320d966969c616127cd2fc20a65986161a03cd6c3941aef31341535b0568b2a713c86a642f4da67f3141252ab971636722ed12adb2a793c72adddf418e8586058ee51dc6f148ac40c0c26dfb144bbcf5d307c72968ac60f7257b9964070a64942d647ee644678caca09ce457f2e613fbc10526152798a5f624546f7fc22bd2801f4aa585d2483b8472a4b3f5af24b3dd476e671d3cc2a52f05bdd9fb07430c3cc97d6bfb0c577b2f9e8e50d6e1fe74089ebe128aa657e68926183d6cd87dd0ebd530920013a0a301d505c47262a9edebe462a0ac28c6cd2610b59a1787db42188287610d1cedb9df8ccb0c33e846116c2d571ab84660a728198a7806104f6e505519e52bff03fb9eb0a00256556355c4cd7dec17af7147dc071beee6a5686d1ba5a185809baf446d80e20cac33cb693da5da0b962d2bd19e017661aa136cf73ed71baf62505bd5afa4d39bdd0684b582ee6e6f4b7c6137a610b8a5f142ac1cbce5c02b9d078a10b567ecdeea8eac300fbcbff1d8f92d75440396973f60f9c313f2fc46ac28340d60ce96317c2b1fa08c2604b0d939d0d18b1152f44f71949e022b665a3915226bd97337b1873de651fe90f3e70a3cd806f21f82d88ec05123bfe13d32b3fad2e2f874ce2a4ca42ce4276d95b216d0d11aff23e9053c45f28d1ca75a522049373683eb011b3fe4eb644b68274fd6361bea130bf786cc75187bfbff53c6c8809b4ee3962dd87ec04aadd8440848869d77361924a910d6f25dce1bb4afcd04c6d823db6e94f29baba17e10f598331f4db5492a2622397ea1c9c156db10797fd4570223efd35c0fd70bda70794e5a28ff326d03194c82df5490a622bd475ec3b6423cb73dba4eea28b6cb978b7efe60b0da77f31c2adae631cc4fe8ecd22f605c974bb849132a4b3339ce8e68c7a6578a3acde71a7a2c7fe23193fafacc18024a9313cf0b126c6d7c58f503803adbb09809b6090818993e81b3dbc750cbe9ccd56a01cc9266bff7289e2a08a14124af6bff64d9173b277e654acd6db771e75b5cc586e8d8153aa7ab685032632cce7af6e17e7d1f8f30910d5e7a54c8b1972efed7d2f5e1f6aa76bdcba76057cb941950523f532d02800f31fe4bd26167857004a22b83585847bb2b97b487d08a0a164c301c610ed2a41c62ade87f7ea3e1376bfe73a07e8a98e7a68a0e59fadc1beaf37084b302ffc3df53fdfa8cfe80b5a0427f8024d1450f11fa7f2747efb3d30c0b140fd31bc3a87fa0baa6219996c7ba23057aed724685d8806b849f935bb32eef4a605605a81dcf2249ee73ea621b56f520e32aa0b6a3d93d13f29aa0b933361b7b4bc415b2342befe6057b29f81986d412861cdf37c13ce9e02f395067e17eb426edba05ee1ad79588ad90e65a4da63c3a0589d6d7c393c35e1f9c6026cd599c3df53c2f63ca034855057d89de7932ed12821db83c79ff9010773cae04fec7b850ae3f04951f33d5cbe2f4a05d9b5e3369785b1180c4b645c13a811156e17872ab15420d6095cbc95077e025a6ae3cee3fbcf97a3b552e6f10b2903ed11f88f60b4f3d1923b42010f679c50ea946e9ea8a8332e59ddcfa2f9d68b260fbc8b81452adf41022900746861cf9b59b1719e2eef8e882e75922a6be8df309d8694d5ee46d6a10e370ba867c3128ca6dc7883414ad7f3a6b3b01479f965eaa0247cf84938dab303d0942d69328e413e0c5818e8b0d4f7ce1bcc11f65e539de2ed2e3fbb46e4e25a36903fd8576aba8efc706cf8722171a2fecffd79a415a3d3645924c755f8817e4cf0a30e0203e5c32c990022b3ff687cef3c789d018875fe44ad11240af3a1ad14160b802d382e8f576b579de16820563c4e212cf36752246772bd379a5110ba9f0b8e02469515c2c9676c859356e0941689efcdb6a2241d0ee46b580810410afd610f1ee5c4d2965d64153350490ca842b210984ed286463eb79bcddeff41cde9fa4367f246608dfec52ffa6ee6bf08df305c33e96e22593896ffc1e36230ca2aca0629659dc3571f0be7e809e9a510ec9a7f0c1723d69c985600f3833bc02fe173deaa04c41f9b5b1f83ea37d42474dbc81b94c81b05a64581aa57213cc13165676762c6ad6b51ffb3a73f1f10060358b19de35c6d4babbf829b224e95303d69362694e0ac29cc0fba636fb26473b14fc238ac027dd77f8b677693a5a571af4105d307cf0d0696d37f321995ab288e77ce09f4ddc73a252c300d4a00c981b554dacf0415b8da77fa1c4dc28ed4dde3a3573c6c3e7df3f2390d98087ba1d2a97806f0e9fc3c1a16c64c35704079f146cffaea0ef7314b90d21667bf209570875505253493deac384d5044c00e94d50a15a130f8362084459e05f3bf69c982a364cd3bec37ee44649738d102dce02eae15c9133963dc6f00af8027b41704b1e65d7c2c3c445743491863faacee7a449739fa7c5faef8f6fa8091c326f1fd12a2320f689d0be3d099247858f481ff84a1c3a760f54fad6eaaa7461972bb5b1241c2e95617f5eb5ec64c536fd9a884a1d21e74b17d1b6fedfd6524f370ba6b023f770d9877a3968948bb8622d649578f5c39217b8ddbd83c42a9f826848f6ec2c4a78791b282fc5551bad0e1b3f95f63b2ea587b46c4e4b4602fdc1f24824910a59a9a3b3f2ad2b6058ec542fc2b40bf67fe3ea4c4ceec0f171a1736b0c63d901bc7465c249d7b191878584f5254636ad94c076ecdc7402eaa26be71dde4051302af10ab5291187ef10822091e574ead9269789d6132639c25627efd71702202d180e2caa6d96a03e8f02ee3139ba30e989586cab693a0a79e12fd162c12d432407c683bf99ae6a2cda59cf6c90ad591ceb18a99e3c1148215b36b2fb3e39f00486f2ed2caf1477061cb1684be5c51bcfb19901b9c02ec447beb24078171aaa2f64680388b8bb597288b662a0382edf3c14ac070737e86726b6d12c7c500ce72d3480a05120092fdbd1229001fb4fb81231535b9653878afdc3addff421963903786aec552940e39d9ff33460f74a33387f9c70dd4066697c5f2bbfd4c2efb11b86d2cb5f3fc4af8ceea1a93a1103922e43dd160bbdb8f161bafcc7fd31f8191505db967d01b7bf5e5c929ef3469c08f817fd7c7ec78261a5cf10d0c06f43810c2ef26ef7f2e9336ff587f6f8900096de08d6bf739f6313594db9443ec342c30fac34260f3c79034b64928e3081c1ee74b0fff1cd01f9d148601c02d6e482551800e080c9c229d59589ed97ca4af09a2608a599ccb54c19aabf16486ff4db460e7e1472fc208c2ca3ed2a948a06ce66ef010f7172df7bd2831fd060cc233dce03955bb32d144cebd97bca622b5ee2f2e6e8399a2adb6f538bd41181201b029c2f796924efa294745ad0c575cf542fb7a763c43db2cc32ba8db6f775b99b295020596ab671b0038e64f5606a8fa8eafbd10d29ab148e907904f73da636d63ad40a4c6396efab8297934902c7cb657d39c8dcf44eb4cb3a86c795841b0f93be581de8532c7577ec52f4a12537a9956b0ac212c03cf336c0bcf99dbd3b892e45ed2d2a71dc9d826fb9cdd91d43408791e5ec56fa2fba832774e589084086303a0c16ff05c6f5780fa20a52820469bf3b8978a7ed7937b0217479d86ceb9cbc90ac1117c686fe10d0a97288182e9ca7ac5e47384be383b7cc53854d7a5505c6dc000ba57d3623d8d3f995c14f756832ff184559ec8d627221a65026e964e2eb36aa09c11f60eee4a48a5a59465eb6449bde6db4fa670458d925591318b9082c5f0f0257ae6d8b494bc66a2443dead3e49bb7bed337edf061f17c49b264641fb59dd26984407e9accc23b4d11adcb0c10651e75e05ed573edc971df97827ef712b551513e963428dcb136b795ffc909f9c259347fc67ca0cf5d367fdb41d830d91ab02366b303321eb6971795d674b16d05de722e238f9f8dcbbbdf82cdf46e2121222aaa6f521143bda9fabc2237e2fef019f237d6fa666acad9a65f44943d549b515e85e088d8e3ec42b92d49a3994bdbc03ff1b0073d63c0ec74b9bd5008c4fa424585a1fc5b5b5ec1f6ff4d7ba9e1d0708efece885a50bfac097d50b0a8a450527f3d7df5039a8c1b83d0a7c217819cd3fd158d79a679145885730d2da273e422401b4c932c33726c20c3a350f4141a5665d3508cca37ead4b34b4ba27cbc6b9132dcaaf7c522f40b88edb47f434de442846a765c2fa47788196fc9df7c83f5b29ee831a1f7284e6f4e3c133df7d9e54e3ec5e0a7cb3127a1374c12ff4d616de163ab2749853ee6c337caaaf97084d29d86d7fa4743b52d7e37a4689f4a754c42ab7dd83f960a3e90114e53473f486132ececf39a29995d2a8174ae2b547852f2f30d4bacce67216597d1124fbd87ecca5dde59c0f22cfc7c6322f740a909aeb0a973c5b14f33f2054698e7e03e338769b7919224c638db7924a8245e308a43b1f85d7e406c8446e58fc361467f1c6cc21d378faab8ee790ace260454e153e6210807cd049974e1ef0c1cb56c32ba83314f282a98d1153cf2af5665eaeeb6a16139a127f61b33fd3faabb69d6cfa40f9329a58331ab431d59240d8bc2e966fda22d7e1eb4bceb911e8df376d4c050cf65dc124dc5faa21af8758a079d7c0b85b02b6c0ba4355df267525d12e8205f3c8eecd10356464704c7cace51d0a81e9640bc6d63255d3e7e5ec844e84a5230a07bb33c79585fd8267b76abe7150a786a21fd35591ff3238fca651f2d59cc90eeed3ba2253efc0f567ba0f8aba737194af1d11a2a63c20be531387940968da873cd43bd7dc03e3c1d3bc12aa2fbe99ada21e20e2b14daa35699764599763ce95a5506581bc494b271f865932bebec143ca0e6358610510cf53ad2fe1d5f61ad1d24df406b44656c61ed6e7205445faf3a918d391f98dd546d7803485b78a225a4ef29ff7599a16d288af013a297784c0b02a6fbd947ad57219cc3df3cdbf0f7d59e36ce25c95edeaf6245753f62932bd9502cac9c21e636fbcac7c2987640ecc38ada9a81278c7de458bfb76216033d8d313c337b1a163a73ddc3140db57161a4a3fd173ed6b24ffd66be49ff72c6c40dd0fb35f02e279f9f385ab009f147da9aef912956e37c57262166bef70d27b677f103e54368d3cbf57152e241f21a7a3ce73647df2544af9ccd49dd58ce80b31e8192a528fd130ff6b38f3bc4f495bda106ed59c6f3f0fc39c0395b4497dbd5eac0c6b1abff83546d63b037b05b68ef47b5f6b5d8b0436d38f372d731d6fd0ec472f39308f03dd5783ae424871ffc058ac46f5c126810a51504fea799030db97c6077deacd4c8da49de813a7dd9ebd434f32490cff962a047a55647a095a8279760d4eef1aa7a2eea2c7b8d80f82019b08b4e788b538de74f2ba002d414dd6abfe206033b26f0a38b1b88a80fdcbc1e8ab9f37d0de3ee6375c07915af54a986b712dd8a4a75a710c535434b496e4bdcecb0ea8f8ff48287705b3f579b2aff304930528e7d18eb79f7043c9a1aaa1e46dbd5fb9dc284e2b867bfa2c371bcfa0cff8246c4fe2f51460bde5647596fa9e4a8c6e38d838c8e8467b5a684cd45963b5cb04c2fc702e846d4eaeb30abf70328cc7145e757144d261a89ae51b5fc2ed58f01d1ac594165c1ddbed15007d392ab654909ba10443116328aba7c8af8b09265b27d7c53374a87bf6f98d28f04592ec3f7d613a50ef183784e7ad894d47ae991c05ac8a35b6d4307f7931256358077480d2eb99142c278440fd800d9fb8578bbc1af8eab305ee037541e604da2ed341479186d828627024d4f1363067737ee3bd0fc6ae2f1b556ff69d05c598a92d68f6d7953788aa43e8aeb4971ed7bab3fa7b079e231f452192c2b92e2cb0e2305eea4e2f2641a55f50be480beaaf829daa8a2c89801f65112458cd3ae535785e6b84f4430210b454a30372b14fda4e3393772d720d07044a0a99f8f8e247d152f8ec470c6eadf946e52499a5083db06d3bf62b632db82ab142bcc95accecaafa757e9b56640b1f106678cf8109554c8d80be21895139752ea939af20954934cacf705c99630dffe4254f701420309bfae9111a6f243f7fd657036f373dc43c281335fb1a17b843e61da43093df3be32aa0121b61059d9259d23088afb4baf936b92eee94156781c4e381c76c62791f879c0d266b33cff5acabb6b7dbc81d67a22cc97a976aa798d5eb86beaa21da26ef726617683a10d48af74af941aee5b015c31e7c4fb0af21f474a190944e145bea17c727ec1f4de4e389fed639b14cf173b7419caae9c3c33deb11806963024e14107520e3a6f816911548b98f74c0fc5eb6f384807561e18dd0e417c96a062e12453e0515c4ad0a7155e54724b197e6e200e3b61df77b12f853c8f0192e0b38e8bf8b1fc9f87ecfb8749f4651d29c764148128cd0172f37c559032cb1eaf080664e3f167a19ffde30c1c79b7e3e44f715b4b20d5e143ca7715f0300d4e7ee2435c23705f6c29d34d4d9bee5da3e6ac50d25ed57adfb31c71777520c147b0d110f8ba0183e296f88dc0d7dd30cc8c1a552e20d3484781fa7bffe789cfbfbc38252f357273d99464757ad98c4bd83924a89fcb690c3548c35bf6cc93722206b8aa7b998059dbfaf9b597f883df0a4e79ea7a87586c53949d42296315f262e19eb4443541e591182121a4d53c9b009bac47744cd2d655db9645f3b31b46fd1cf213cbe7416a3520270f458d7ec5604157876a5b81be998f854666815e1ed6792252bb2da0ab85a4b49874fbdf69d9ae78e895a82d02855d0025bf8ea19d5a7f67b63a52e0c627bad904e362e90ef868308717e4d00895153f9ba4f20568c3344e505d88b0efaf03ab4c593750c40c438314e010f8aea4aa22933f64f38aae2ac56c5b00c222ea879ad9a723dab3e3324fe8ce6e68b54f9b838a2e265d2a81d091f43a97b1218727895e7980462b651160b79beac0207bfa5c127701088140fdc4741ae4f270a6d0861d29d825910ec8efef266852ad117e8bfb72c4827be08ecdab3ab21214b427bc61b5bd42ff1f6e84198732bab6b043d8eaca51e94815db13c22a5f2178c97d3e67bb633138012c8f19e6edca36f28207be6fd25feb47eacd3f7300a12ffd16717ae1f87b30572f78fe8e9f6c0285864a19d52ef1416c6bafd296ee5274e16e37cb37acb075b842be01f7f577d430667f65154b11070b70437937270300c53983a317d20ef5892fa22fa5ff721fb9be17da55e2f82a64f5a3febf7192ca47d3122f9be516f7a7b8c2c02d0bfd706b6ca47de944ae10e732351daea2a031e416a38ddb62e1af22d03e222ca180a8b69288c999f3b851f5ed999b2a0a61fa27837d952cd8421cbc240d987a37155a8c1e5e1f4eb7c49aff109ac16af6d0784f3728889808e1391e69b25e095af7a018c8a81f1cefb60aee7375879697e14388cbf84fd159c7a4e1882c56bfb3bb5b5b9ac8db66038a0257e777fe0c435090aa91cb05b93a1fe40b21f5a9b40143eb498fb7e030e98a4b9d491ef20ae3a148193295c2c9dfd0c148b0d6e84fd8eb8edcd5ce1337a1998e78ab217561406166e870a1ff737d0401591c5a5b2f4da40bc19a0f246793f62b04bc5df66d6020970535a0d0e97b797d44e58f47891b11c30b5ab101d43e6413f3e5c048781910a1f8ef5c1f90a0c2f60909f5b5109b80e03613cd84103eaec0ad5cad363665e2234e51f0eacdc42f1dc6c74fb844fbf0d7e2ad23e9574946fdb9d7a3afd10910881c68d7025845e58e84554285ac330b2317fe4ea446d6917f98d15e362e1481d3d6a5c2067ff2ba2cd17fd68cd5ad79e440ec065e883123cb3950f2ac4b6480db142b1f16d08c353e2f557be1dc2ff5359e80e8efaf31772170cbdc1deb54d1ea5312f9e915c83584235ed85109d4a59bd9e028c5f44eaae735386f684a6f607ed78682cab8beb171780c9a54e3f57a392378b6051bd47cc4104dc4678c74f0300f5276b1f61679e9c481b80823bfe4fbae5082026c4996e61a319bc1274b00ea1dcf79295bd3c34f1b1fdb77357dea3955457b33db3393905ea35deb49616071ec289dae2a241d2d7715964b2db700909956a819439b1e04a7d1f922043f204570c529d1220e1c5c3a83350978b08bb3d65d6844cc1e615d634f5ae768f6e1ae5775ac84325b9a3c2976dea264f8bf3d7a1e14c31b9cf8d6c4855fc51c750042533b8db0f0ccb4a24085cc029702b9432137bcc39c18a492ce1e62f3c2393070a3d2b973aae4f5dafb491b7ae5e36230fe2c376a48a66f5d516352c8ae01c0cc43121e8b8010835398c313fce1f959c0911058646e807b1640cadac36369f203610cd91f6f96552cc2d9b85d3ed3dfee006928fc67518977a68ba6cb35135164cfec4ff35ae655e66f1586f933e88c2874350c6091f90a682e32c02690ad219264c0c4e73ad397e314da27dd9fc97d941bc619e13826ccb19f58d0651b693270214a4270e230bed23fd16705e90ffb3c8b9b7d9cfe03ee2a094a9e9b2909f2678bcdf76a943c56ca03fa8189eb05a15b10840e4f83e7ceaffbbf07fa822851245572d390a62945618a0a04cf1b830df30e13a4bafc8414b13ffacf3488a9725353f07f6bddca341c57e2a1d437a0fbba07c4e5bb31fa6c41d9d71fe038e6f25ad9aaca4bf67db38dc232dca80ac6cc577f2fe237614c0987331d8569772871276ebbcd824e2d6fdd1adbcedd9e14db8c44abec004b0ac587962949f41b1800c781b7221b31e4e3a02d40e9b2c1b7ea3b73f180ba5b1b27ef069b6f05961800d662205d6e426c30f893bd0cff9b606a3704ddc4fda64f149a583b5e2b1ec5012b58b7e12f4ea84f4f9ab698e625d09d85e8ab36100d477be566bd3aea6264500cb6669de3a9bbcfb18d17dd15a9d6827277fa1a6c57b83cc7aeb2965222df40929e9d749b07a43e36393a92da64323ff33bd3a3cabe072a929c29847450a431c403bf371e626651430d76e2f0f1142ce5ca04d5211a40a3eb5b522492293041a356d17586f9c2a30628c4bdaf8f93868e51faa16f6da0e030dca9a4952eb1c14219210c228064f8846d40e9368ffc5b17c70dceb3c4f13b9e42dca06c872ce0f6cfe8b8d14533b480f908e52f254f91ed84793aaf219683c7d271ab2778dfd30099fcfc1e4ae79674ebce5a1315298c221404b418707036cc69bab53c5313800008570b8cf57caf6020c9829afa07666b66fa1ebda11ced87b18a109dcb1b41aba076719214db7fc4b5ff92778ca481e89f912fe88dcd39ff7794fef20cd9520d52821d92a633a17d70485634a0541c0272f020df6f58af977b0e91d6a4bcf03ae248c091081ccc4e8fffc6a050e05357240b08c0ff78548a041239002452a0ca3dfe850ce5c5ba424344467ed464b57e098456fe57a2ed16b1317685f26ff0f5ed7b176e08e4d9de99f5b344a7d3dd3b0bcb4effca2df187a117c576443e01dd6eb7912779095f7972c7414cf7b72ca7904ef8364f53db792e6cea4abd3422d840981612e99fdd97f2f4389914f0f78cffa1d95e815ebabfac5dce2ecf8412a99be58b4cf34ac6f91f5b4a1dffd405bb782f6189d26f468585fd50761a8a52df5ed597fde9811dfb1d21346499c99146b00cdbb6354a8118f558295fb716b2a9723c61eb8b1427d6bca3db42ac998223f93b5760ce1e263fd58603f18e55a0d532e7cdf42645bdb8cad58eb88dd52f2349c626f08d2426a383deacbde9e251aa2b56f08f371c6d6917381ea4241973ccf1afafadf9e78745d163f0de52db4141c05e1a20ffa115820d0c5d3a1ee1825490c446493fd2ebc52cc34783c5b0304d04162c1609e7340f3c6559cdaef78739a3ecc0e1fdebfc1bd4aa2cdd31d3808275192c8e3cc7921c1fa351a66d0d8f43bb8bce2999756a00a2276d0af4f141934be7f31bde374351e50ccacc048284f6f6ef77f9ffc3c3a7d9f65b4d4b7db1e6e30581acdb9fbf6832fe4e12149b397f857fe633b733e77628b0bda39db6e16572f317cb53137f8dc0c054841faa1c0f6752757873ad0b2c78d29d619ff9d0832d2e39f7cc552cd4203cbb6ba9455ed305398629287b88486b0b914d0be2d1440fddccbdfadec77be5340a0f75035752c4ad485a9ce28f18d0896d358c1560f1faebaba5440acaa383fe25a1a1849a29a56a42d8fba183191983b101a5ac221dca03aedb73584a7be6415ff937bc6bcff8fe8595760af12db068187f52e571d88580aacd45b8723a5c0fb5609f14c5eba1afd3f40f5a164ee4b97a98a72c82150e3ac2ee4314561df798eb3c3a573dcc6aa766f26a84eb7b6e5584c3243911c09193d51120216d2e246fa2edff374a80c1932c39acf41c3e3e040b38373df1e28db8d4179c2cd8efd93091b611d7f70648e832a9301d931c14765a87bb97009b8844ca5d4bfb2ad0934927e11da2fd9832690b28b45793cdb1b80d9b6fe68273abded42460ce3d60f075af10d465818244599cede90585ac63b43c08d7080219e49ddf2bd21ef6119cb9d8aa31cc1f8141373ea1f9d490f15be7ab965c5ad290845de60a58fc9ca397be4eb86efaa128990d4f4441c3445a59e608a666bd10c0c0ce65aeca5e959fd32eafc6212421fcecbd3f10d6ea744d0f65cb21f162b23d804a8d20d2873309f646f8128fb9c9fcd02cca58845c5ec1ae55ed669bc891792260fd1d7b5ea3c3a790494b73f071fa0cd35624889e52c965e136c215e142cd89743e54a6b3988ef9852ca79e456ca5cc1afd24eade12ecb3b27ba89cf1725bdd58395c8594e6a8a01e4b388f76d54d35096af2a521220e268b0dd633f28205bfa81ec110c18221ca77e9bd5bf3f6d090e7b3c925c89ad60d4bd9abb694402a7ebea4ee8be2ab21b00146f7033fb36ee7c6dbcfa20b80d70e2291b46bc8d2761d2dc0d29d23d0cc7a58873bca8114808ac811fc3f45b25cf41787065af9101d6d2ce542984b742c097b92e85ff17d61ae6fd69c597b7dde659a2ea1c201f18aeea00abc08ad5a9d9acafb5316d1054655ec5a02bb2434946b584af7271ca1629090a0cbdf2fe152faf22486825550d29bd8b08899aa562b391cd47deedb726ba0d95a1020b5be53a375ca0c98024b3a961af37170f6b40cb58186c8ad79724ef8fc6d60b44f0de34aa1146f6933f964afc553538f3b2a2b7dc17a83c58701e902854b59f2a7ebe132fb19ae003a24a693386b7fa4d0f7f589542cc6c1c702f9f592898c4de2f94cd9a689da1f0855dd1d29def7e48a900fb989eb7df340e20a9ad75ccc9711d53d4142f02b635db1ad3458617e50c0f91ff6e64858b669909e40555073b0af9e9766e94492a6c509d936e6e678b19ce25fd5aad8a9f9dcb301dca4dd5e47a124900d4ba230002188ffd37f98c29cb0e2f7bd055376b4df01380a76e5ca5017b8b97d887490f494780633d00defdac3346cbce89045a83660e56d2e8a6d8b47211642faca1883a83c23045124d490d982c1218dea8f05e340f95ca48916da15d07fa5f923c3da0c890e36b26c55ffa2f8693485e5b4c67fc29b3be5768259f9af7c17eca51c8ca09d65fa1fb21db1f7c5a8c545b1cf8f78e1c14a5648cd566753b73c05c7b55c1af4005899b442e53f08669ac88dfc0eefc2e5b96c76ae4f5459241fd870544832c2a4be2954852b47df941e647bf7f914e1ddd6f241b5123b5f87aa912ff5c35d8aa7d645b100f1d409338cd9280a0c65515e673654f8fb5de1da42fb7ea5fc5cf1f5afdc4591f7f22f759fd8a9ef709230bddb28064cb8fe3b9a8806b987b14d8e58aaa13c86396832da0d2c440ff95450d3be24a3d6c7c976bc5450ef350a98f129882f8b372c714efacf7a9382285d6046fd45168becff5d64fbf5bdc12073731dabf923bdcab04f43de20c8e95fb66", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000fc4a275557be3acc7e37f1dd383752c0000000000000000000000000000000009800ccaf777e6cc629fb0e8d8ad1652511521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" }, "decryption_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000001f3e77d68ed5db4ee000000000000000000000000000000000000000000000002824efb1dd50dc53d000000000000000000000000000000000000000000000001e287c5b545301ce400000000000000000000000000000000000000000000000000003ac30f94551900000000000000000000000000000000000000000000000425ae327975b900880000000000000000000000000000000000000000000000030450cf304ee5f67d00000000000000000000000000000000000000000000000ddf49ff2fcefc54550000000000000000000000000000000000000000000000000001e6a4e87b9a2000000000000000000000000000000000000000000000000373d5ff3b6917377d00000000000000000000000000000000000000000000000ae9b0b68e3252781a0000000000000000000000000000000000000000000000078bae91658ec6a2b20000000000000000000000000000000000000000000000000001edde5cf5eeec0000000000000000000000000000000000000000000000024db0e0b9187cf4460000000000000000000000000000000000000000000000061da17c01b0ec38f8000000000000000000000000000000000000000000000007228437c29b55d1c600000000000000000000000000000000000000000000000000013d3baa2598e82a7f1abbbf32e6984fcb0010b83c6c7844ea6ca3fd0182ed6cfe12534a9a1ab32d95760c1b06880b626933c5156327a93ae0c04616b6f2cfd6457c4dfeb0b88f115d8837813343413efdc6428b79367f2cea1f6fa8b8d3ec221fa34eb49abfe209fe2bb0aeacf24470fe359956c7a9d3196c4d59810478f5e6c219a0a1ce604b02dc0bd84807a39b811417bd98322a228ff199aab7bf5543ac55c50a2b6509a607be7abd06b0d150fb024b43f0a0264f8f2a2258386a1e23c891c2cb0f8bc84212bdf036a748a87c8949b4fbf035079fe8b10ebd8c3b4e7f3ff7033fab3de191217123f39e02abc543c57376b6a015041fc623668c649686560fe0fdc1def2e6251b41df3fe84d67e48c15e23ddb2a83225f0edceb2c59758b5bd5faa1af92831ebb797fa8d6559e379f0bd0cc77e360b78c17a1e131017d4ecabdc2ee16ad021a31399184a8a78a5ff3667d4ba9ff5ca43d76c39055e64fe36d15d8be71fbf62733a4eed75dd54386c0915aa949a3ed5710627443b8ede3bffcee932e416b52251aae03e2369a82a51fe16dad9fef2b0e96efdcabd3322c1483aac4f532a1171401989403a42325d2afad1973d1a723680a168d3ae569995f8a97a7aecd777e19cde1b4ff4744fb82c63a6d990d2185ff7b003862b25776bd65a5ed0e414fdb227c255f71b15ae0c6933ed5702b3e729e7f826a590387398b121e06df511d5514fac1e05b458c10dfe141aa13ebf0647e24c0765eca7a633a6d82c8cdbf1e281308f82f1605ab9e282cf3731ea329850e8463f81d274c6f12c703ee4027535a287eccb7bc38c8523ec425377fb9e706a8c53c138c98c517ceabc0497f3845672c3b9942b4ec1692c479a8dfa18e55c4ad68c6519198b612a79177ef62c1ddc02d7842bb16583370aebcfa9a055bb2811bb27866c4f05557866b524f27f1e17503d9ab172ac8130eeb870cc0a647bdd39634c84b59cdf975ac0a773c6cbb151c1d126e82ef43f98e082640c22339e906c342e788aaafac7bf0f644097be2628e0232986c6d79f5ce13059c444f38ac33d416a25009f2dc0f86de79f54885b0510a86856cfb4965ef34d9bcf19bd9ab16fbea080afde613668d967b25fb3bc82707f9ec76cf626377bef45c4a0d2568adba49e805e59649f0695412b947af0a520131e9eaacec50553d90ccbca23fa3df01c8d236b1c156382732390af2085e4c0d7b8c85c65f3aab8fab763cec04cdaed0cf90145a204081c1ed3d483732a4ed13d9ffb221838c5f0d8ebc8c9aa98357bcf7fe1c7702f89b83bf5dbea988767c2ef522f59abf597777d99e620b501d519bc981fafb76042b453d1a7167e91a2e1c2b5edd2429647eb7b408c757e9a3c1c19c49bc6401f10816c3ca20a8d2768e19838e2be7eee84a64b232d47264296afebd614484dc8d735a5d28ddcc2a0c840edc31f87870ac41ada9eae239427acad4010ade9baa676779c222f23ebf5245235875eeb04b748a32a4aa8d9ca4cdfe03450077b68715308644eeb2cb1a68f1006d6d41a8e984f8e1114350487c1587996fbfaa19d2e60cb1175558e6a0292c0ed5ff0a5b855c452584fa8af47335737158637c2efd534da21b3853bf89f9242f9c279f94e8e47a25cb8df25ad1787142eea7c1e64d248709147b370f9fb54e25281994695e79395c7c6a8f9506b17f11c9e84beb4ca71f547cc5a24b737c1d139df5ef48dd868001c3ee1213fc3d145b8a0f1a4df3ba5f3b84041faad9c38729836a3cf695c826bada884a9b2765cd2dc3c519a142fe6dce7c61ff3c35b14e1d0f88c8c1042161b3fa888a7f10bd499c9fc4cec04fda3ec346eecfe4b8e64117ba207695a64301d974e223e8613159d26d2466c0bf49ee276f880a715f536b1252d850fc51c0b123ea14e1ee08e305ff2af518b23394984fced28b0d25ed1d053723691e73f00810db57dc33e7668cfa7ee8ea82dfb08ae1157a7ffab67bdc1e65810fab2b117de99f771fe66e33edd6842ab29ee28449a3a711513839b6c425cda1c407664580d3e7c4a68849d1c986366e3ac00ade2de89dc36065bdca0b272f4c4f8230570158f2d1d5f1131d7d7dc9abbd6be93171ab7bc99ad78497702925a81cae48f544226832ca5cf1cd5f97732b9537d9237258c8b8511f10723f057859047d53d2b70ffd0049c6ec0514ec267512b9c08a80a91d5d597b51c57e069c3a0203febcb8ece93238af70b491e7738dcb4fa26835c6dc843869122dfe09dc4f1773676441f3237c9b4417010f6a26a914ea3db681688f37d2c1f749b8111f6705f50a9b212a1f112a0be2c92ef9fbe0890df133252d4b59510aac501522c73c857179241fb523b6cbd447f6e20ffb3cd52797ea66d4c6fdcaa4d73a770bd4db1d004838ee5b6ff558a35fe31ebbe17211aade2d97750277a00aba976002bcacc9932d5aeae711c5c0e2b80ba242938e0d52e1c91ddd667c6f2b74abbf1da41a0b6c35c00e17a6ebc7901f7808ef157a01418d01949389052ac91b2d711c40a79aec70f73e9fdffe0ca944d1cac8d9e4b5ad2601316b5bd7e540f511662ab6ff3f08581ffbf961ee342499b951c63e998e60f4ab34881c9b8dd4e2dbe310cfdda4611947f2faaa558060914e3dad366bd4332aafe4bfc4805f6e84205304a03965d812f279734680b13e6cacac7b23c2228b1acbb93f4af41aa025bbf91ab9a9a42630a755b364b3ef3790337d1da0e315388521831502a260b09813081dcad647b64ede244918a587ff629d2b160c0ed328617eb4cd3df037fa8c30b62fdfc6a29ff02714877f0a4a4f09c29a9af2e85e427e502427651a8e3b304c361d2c9136de21197c24ce33da03e917c925be1923321e11e36bd043e1f730e88b2f174635edae1d9553e512b035b4e0b933d12716ae0346a30d6bc1c111c58df925b6e006c118da59d5e91142cb123ed18216a9a34235d2a452ac4206f0e6550b07c6ecfacd2f7fc86480c80dd178172a575c26c299c307ffa1b8b0a93f3f01be15b861d10213dd8c560e919c5fe5a46c8ca6c639fdde608dd918de75d6567a851b7680b297a2ff4259a48a36d482d1a698a6b16c9df14176812371e52cde3c222f843387102556dae72046097464c095df1ee46c69826480752cf48d60672cdd0299635611bb07626a8273bea0fcea5c90d886657e8c4ed47b62c3aef9191bc612aa8d7a3a8bbff9fb80690060ff6b36c102475b9136c49857634c927dcc9c481cd72807184e90c8e852a0801c946d6411cc2a195f398be1dfc37a2ef718a3091ddfaa1f317f91b550be51b86e43d0ac570a6c47ce50cc98a9905f886e44b69a02447cd1ee0c5ecda92b2d39872b82503583790fff4a195ef071c41ba778253a069b853bfc3cb73bf9c3d93c2ced9f9d1d9a9b44ff81145c0b9a6bdea82eb5fc0631a206074d902529dd3b03bfd38837b9295763ce9a173990da54473658fd092ff440bf4cbe8c837d5c828c80ae4d66f944545c0e27fc240ab1cef2f95b043929d25f3ed4e9ec08892ec6721c85052a6c78521f0feab2acdfcd4c91a7437e5d202aef919da4ecaa22b001155f637b156ba351e5a052c60bebf6953f295b369a27d042baaa94a56aff86ccf43bcd34f6e6080dae752c359e47a030ee0ce2921e0ce36acd288f10068d9904c877c9d62a7d937318f9f7c5f1df900a0e32eae9932c8f06a0b83e30b0b225cdebebd3b42a7131f4893cb5933ca34d249e7c78a9dc022560ea911eb98c0c473c4feccbaf919ba5e1ab1e9a139672bb0c81f0de2d6d236afef4ff6f79135129f6c4d375d5b53d96bf96bc04b2e36f1dce1ed5dfa982103fbd4e1941a5f51b4b114d45ca0397d8cc0b25e7cc9e4efec098fc9619fb31301f54c3195c1283407e2b520120c77daf2b6428d78e5ca5ddc13021c6505efe15f88f53c947bfc4408bfc93856fbdaf8a9c72df53fa60f0a8c91609f67457c703b6fb5b5acbf95186219605ee9245f80b948754af2fac1161b89589ebff5fa9168039fb3c5e72aa8a2adfcd6aa59ad3cf183103a5c1313dba6f6d978aeaa96226ee3f12f03eae59e60fb73db15bff1e51ae67cf88da23bdff5ec9e0b49566fa1683a4f335e2ad10bb52a7445b05a5765175abf26e2d8a05620767255f88f9070f0f0fb28cea2cf72e0745391bdad338e1b728132537f3495f2343514bcb1b3b0d8d39c1b7b17423a59059f96366f5a80c6016296951122774a1bf3f4176b2251fbf1b33040f6c6437af3d6fb09858b7972dea5c1baee1986cbb548c465e502708bd2c3149c3e86d6ad2452b70102fa4a3c748d65ae3a5bb2a90c6de79f60f6f090b044848ebc4579d7cd68132a3fc3a16989090cc37ce21079a152cd3fe47fa1fd48c0313cffed38c172e64da1a3f89942231c2113c5115e2bc85c3debef2ea054c043d9ad9cb32cef65a18f28f1e15b7c9985733c69a52eb6af4287922b6222f5205113943ff5d8e1ed099f5299cf8240527200186239863e36d4a58a2a5fb065d78c66b7136a52ef53de8e5d0319f5d2ac5d1c4b5eb6c6c31eb57e8e00f3b05879a174a1da0394f7602dba05a7cbcd64b297c0650eb8980cdca29736045d724b1959380c73fb0d631a680244a454dd3a4e0bba490eeaaf956f8296d39cb9a00ac3a4c762a8d11fd8645a19ecf3d149dc3de9a91d6fc22fcf41a3f41cf5d2c2cbc5eb7a019d0f1abe491758a5a35f9941edf83ae783c1009d9b32efbcd5773252cf6fc0899360b17905f9665f760f0d05a383645404d7ebe4acbd3cfc0bebe1a3e6067abe34217bd9d63a445ab7d62814ce52e0c7947b155bea4336495c7521729b8332e879ba575cb2b5b17984efcfaf4f2f0016a92233578f09cc64c1c5226e733c382eece007d0a5b2792b13ae338a8edd9034811a0d5a18e1ba453107c2fcd495401b784847149562b319c2ddf5434b54f6f98a55010e2d151b34ac1c61f0f08bd81faed2b6ace599d4a68f5e12eef8b2fda21ba86dbc6307001f9554a29d40fa86deb11a05a25c8dc66ec9e525526aed32573f30014da08c2d9382254292bcd80c5fb3c4aff19006441bf4090d3f5e72244d8967b068ab8a720e2f8f8242a3331efa61b12eb0f568148b190fe0aef1655aee8129c611ff7ab4d2b7e7f0456931fa078ca2b6cd155f04f7a8cf31a2540ad143669812e18153379b610f11a1ebc35a1e580b55c355adff570ccf222d05ba579793ebf482994816ecc57aa1ceb6fe0e137c9ad7d23215013c3d02755009ec47dd527d366d54f7bda9c80501b163775e5f22abeefc0e4d0ab58b3bfbc5c9a6971f5cd29ec40cb312fe43e9b09d53ff3dea50a09c42faa82a943c4cf5b06cb0c46d0e78564929a69cb6ece0c2c5c935dfe7dedf4020a9d2344467a8776559fb824100d798d988c0dda97c42622dc97061e54e3c9c7220c5326b65d25af7785919937794c407761f4273d7d5502c53ff11a6b83114105cb2f09891858a4920eecb5b8b73a5c803f4f8c68e16f2540d7a584736cd89561c2c22b3639d06dc502dde723bfd7d10fc023d7650b780aa52ffc396e1ba4eaee1e79d4ca4e42ac6fac5815ad08ada300c50431875f970511405be5f78f927ba165aeeb6e8349977f8bc883e90fa7946fcf97c403eb8b2906a69f84bd75e4bd3bddae210d7100d285c94009c31cfc067e7f6fdf538a3c085c166dc8c565e5a7fcdda24325b2c61dca091a4e4cb7f42dfd5c26ece367f02788948403bb25fdd624b084a539ba98fb6e00dd50b0021e2ce918b0dec5c6220414cb321509524b921d65134fc959ca8112dd6e7b6645cc9dffd9b0be97512e201d78105bc66c1d6b44fb0dd0d14d1575362cf2d5c4644bf4afbaa963fad75d219e7a9dea5e6a44092c906bed7582cef5b50b36b7120f7e56749f2c23b0667e0b3e97bd8b681cc3e3cf0564c8c508cad783d871a755580ef9cfc3f68511e6011814740377b0ef4c3e85e74f282803f7bf7a60a69ed76269beaf2e541f2f01461f1d3a7b61dccce66f421b1702dae8e1095183bd485317ee1121acc0eed3d9841499f6b9a0adc4a459414f50da7ca00070a63623de5e075e32778ea939f2b5fc2c7681f628866d4e70be2636b34ff2c719b9c49d94241c8993e398a07652126f0eb20da7dfc1543ea992b023d65d006cb4ba0270c324ed1ab10295eba02f28e72a0591f43791564c8edc198a23254b9fe3711abb8ebad8b2a28d76b063829d5b1856a4b156fddb3e21c5fbabcf1a2fcb1aa0aef309dbdd7b30fa9c24a475e1c506bae9cdc0be1efa29becf66ed43a000da45c20f24a5c8dfc08bf490000c17f023acc1af65a33f8ec051411061d7015c07dfd31c7ebfc09a9715008d0523364b2ddfd345073e5059d754260e7578661403ce987d3048e9480c61862d8899b4f7289e8c9f289e87a9f906dd963933d3626632989d2eb26250af5b76cea59070a4012afdb97165ca9da72545f49e15633ea2a26c7735c944e990dc961357e776441813f18afa14f8327bf2db98a0e3e7019f2751879c267bcb6c7863aec69caede24500dfa808bc3ccb710fd545fc116218319df0bab38e404c07ae223d27c9d4f29916205a86f00600a61aa0c2e0dbee33cf3613b31ef280895a78d292170e5911a3cde214de3e8d23939407279f7cd2ef57240e96eeb86081eefd70e5b57a3ad21b8f1063cc1e3b6f6f11d52bf76ebfc3ad91339479a7a3d2c2b8e6f69fd78d010f2dce00d6d8b680c06ecc20f6d67ec19a28f9c20ee9b624b784e05418ff4552d1ceeed6741f7ed9c3d00a3fd38447dad774a539bc99a5052a95f628286a5e62a4ccf79bab6da12d1576c537101a31fced2f45211f914c764e5ed965d5d9419156f62a957a277eee361d36f5d7c60222d27c3376828803d28c6633a8f2110601f35785bd5788bc8a8c47929d7d820cd2a4dab83e704ec786ea5e5fc3886dc9615b5d4a5355cd97beb11573a909572900531439f2a63f421eb5494cbed5ca15503e22f5cfb43b27639d7af28165b4591305706d1f2a9b0d1fdaf068e685508150c3d3d23a9c0a31fc682dff0e845c6317a414395c9e6c4a33ccab5c6395006971edfa8f68e55940b83b22ee4dc105cf2e39f0af7ea4a44a7a2992b76c4c570a30b677986924aa592738f56f95726a1df409d84e7f7dd45727c6c723d7d44300f256dabb802ab4c9a88121fae65192fc5aebfe70ffee877bda75be243ef3c1836206382f14f942875cee4ac22f4207aef59da5dcd208064b367dd78777384b80503be38dd876624f57cc4198a025b37ab52d7229aa2c68239ff2dd43d8994795828a5cddcc20bb663b36de0bcf88564e95318f7d59f159b263745ddfe380abfd71f5c2a6d01a4dfd231fa8c8adaa500d5df747ec3f5fad7f41f4d3307d99d67c019d7a23b048f7d63eb11c1133b5d573c502ecc2f5623f246c183a7d384044883190100bbc33c4aac39a021366c17e24a8e8d897bea7b9aa2b0b0ff17a79a91db2e36a411fe0c982dabbfc76adb74e710dc0cbb2101b4e04c96683f49b0d9e8321f4f529c3d73b380455084bad0671f759bba657fa3b04a41dac8acb156085556126a7b131eb954f4c98ad9a97666023f78a49a1906c542ddc59dc60bbca6c45b121406d8eec540912b0d0b03638fac7205b2556bfb4b096924ee52e816d79310095af3cb21300eb0416e87a6db4efa734e6f54a61d56e45c21f8501a2c18c19311f75013282383feec1caf488b8cf5e1d6bf898e420a0ceba1f47a001c4273d01357402e04c95adb4a05284a08504bd20e45a7ff3b37a127bb457edff631001c2134448d544b87c7cec84445bdedcf77b43978a27227650cf6653e85e62680d91a9a336e6013866aea57a78226ba26021773e81386c7e09f3942eff5c524b8ce0dd795c2959de2311e2b05f5e66cd6a92cffe26a580241663b6b4792cd8b8b872628c340bba16177e26150c31659ccdb694c9871407b0f2ae7c2020bd2d213e726f43ae8d6415189434d3f6053dd0f14fa9ab5e26e384aca8260901a01b46f83222053230900b4e765a5013a7931b2d689ae848d66815bd07bf7bcacd37f7f9e28c5e3f11c8c868570ada9d40a96ef71f80ab55a99eb2bf8e78db8182d518a6c1f8ac5c99cb8be988eed079d489e85c336fcb5f8057bb20d6322fa86e62bd8be01be1982e48210e025aef091643c5cf1d0219e877cf4718183baa29fd1e998b2233110abcdf05adb27255a66180b98862d820fb8220f99ec23d305d9f2be45400c8a6b6b564726412d6cd628efded4bd4d8d3faca4636f44edca4fe191db5bc302d917613326ba93f366cb36705e2ea7aeb69da71610e0546364dd7b8b962d62284be2ca6d0542cf4c387809d6cb0ac49ecf5e41166f72429c711e70927fe10f1357793171f937cba3af6a6d5140e6812dfc3f2377ba7e11204c0f27626e61ca00c3dcc0694e6fd6ab948627668d4eb432f6ce32c38726a5a35211facd2ad25e20b903fb971f880376def335853ccd89ae2b2485dd06ef4fbd72af678f3684d2175ac14729f842e00cfd4f48f8080e536f428c32e16954519e481da94b30b3e00dbc1e2d668931c3cb33526098b89da49bf023a3405526b77df1015c6aa8b88624f585b6022615ec24682a2e594e74c3cc251b082eb238167e9a5bcc7d8f97d20c0439e4fd8e706820a24114509132c61fa129989b7c5624f7dc11ebe3a6a1001bb52e32134ccc60f5e3111bc490086ed2b20eb735312371d60f9a2e69477f820bb45596866f4f846e312b6376de071f1354e67f33abadbfd869ff728c920bf21390824d579f86d9a78736e37eb6a1702cbb0535f3df93abf216055257c126c907b88a19fbede7e3eaf9972c99395a91a86259cf8b6bb7fc3e74365c983df0d3102c4173d44687042228e3dd26d15d4350bc9c99247ea8c96456f864edaa1ffe18a291daf39eb370763f0e0e71727b57dc895c7a0ca61d736fd361d0af7397ce060a77335485fe34cd581d4e971f77bd186535149d78654617f2d6f41d6ce2c825c039324ffcc10a27e50c6e47a5b2a84ad8faae7fd6932a91f3ee2d3009f4942569b4394e802c9207533e7c87ef88ecf09e20249d2e9158466d4e38a4cce7e309a3d2d82cd6c979458b7a60bb3372e544d4650c34c31410d78ce03b80bbdeb81cdc482661fce86890b76c3ce45a72c36b70d441e203c6f385584449176e1fa81827627eb4f2650e88b29856eba0822a44a63cb2c71695be176035a50dbf92b32487ed2ec3637821549f8013c816eb3ccd0d194634a82bf32b0957c6172ecd771769549a3138b690aec5c96013608f624ce42a753b61dbeaded643d8946cd9562b19aac5fd513923122ab8e5b28be59b6228825d403400207e70fca0ebbdf7c11656e662f005a9297c1cf3a5c68a23d88b5065a434aeca985d3947bdd7bf1caf2a904e27a9375bc4e58d025812903d2a8d9c1eefdf2b76e1a8f3bb20809a5b951f963e310b9b296516448e4847e8bf10487cd6704badf264e38036fd74b7c39c01f37047e62239081cd9cd5e3059d1070c9c1f89488626733f2b11bc14eb89481a2176a0bbd96c66575f009518c91670075a6d9488dccc597d27ad2085d5e559188d32c5168cc71152887d8ea71d15bf34ef9cd15cb041c40a6aefe67a530658033b23542baf13eb49ed5f4c0b7b111d7734ae2f88e7b05f9b0cdd7d573db49a19429bf46545942bb14edc07963d2800b5b66414a552a19759e3bee9c57d4e680be3d6503174807ebb9845825a0c70ab68b7d11d4af2cc3f09f078aecab7e88010e194843746e2c4c27893ed916d12d7575bc66eb1a290eccf0513a3e09579072ce06d8a7f38211ea1da1d8bfa710062124f853e1c9261705447defc47a1adf80c6a0ca3ea3d92ceb4b19219cdfbb15c1ffa71cb7fb0a35739a497e640efefdb2ff43e55a01f649aa81a5ffbd71e6dce6d3fd23d2011701aea425fdc6b4b89b62e946be2642b9247e35c6a16e8bbc8dc310107f6b7ce4fa1b22e96fb9d8562f02eda1a5ca567421a816fcd54396eb32759b565c9ea9cb3661a4c8e42986e8d272a33a58ab5bc23b11d2ee27a94d1fc5faa2a0ac039fd46f4f3540e952035f2a01762f1ea6c159137e220650a4de7c37550dfdcf0fb2852288a2dc0d75a13b721252e4413513bdb146f23f6c453c8212964a0ad3cc7e1706d547dd9a6e920c7be07bfc9ccef26adbbb81482041600e5be4608b25b7ade0bd39707d1141a9c38e91d6d6a8710e20c146bd6a167599b91eeba7d279906fc5a99ca5b5316472a92aa0e8b256cfa9df92304e935d1022ba8ee8691311ac01f5acdfe586ee52cd2be6025ba4653066c824575b4d79e762c2da88526218e5d1684647a0b97b5fcc8f97a24e99a4f6e8cab10ccb9181685a81710d9f486c8a9108c547f2d7d9796c1228b0709a12e3fd8326bf7834a1dacd53ba4e2dda1464eb76bf8e10837c5e0c8f11c2ea8f00356fbf4601bfddc1b11e5b391eec85ecdfbb3c9174bb085f3029ef74712def3d1bc2eb8b062e1cd968b374e2ddebb72d61c52ecb61f1deef94b7912361d7cf658040c7c687d5f2c471ae5a16628d632ae7811680f078efea45fd12ebb24dd2fd3480c5d399b9595fc698bd6d0c435ec72ab51a7d09edf66764065d9cb142b7e6d555a11b06605fdc3479afe95b5fad22b5e9b4604d2ca7ba3eb51578f23dead1a272168f78d251f6019c0ef83eeaf64bdee46c83ab0119debf5fc865717d8032f1d166e1de0446dec8737fa858d523c1c8155d4aa8d253b4f46d2973f178b0492d8680a005dca61f448687547ab04454c5c8e0ce84401c2203ae4a65d2a4c297f9b989bebc0d717a7b7b1c26a72ae0c8e838cd6eacbd41c6c66badc10014e4d4de5b875ace180f4562b87c63328a89149e4fe0d1f76c6f468a1f36fa60c7bc7e5fe59eafa01cbcb0000cb0f1498544708be7a7b8419d0abc91fa29728021d27ffcc472552a602c5c809451567cef6341e2c070dbbfdeea42a20968ed6254aa989dc871e3c91a70144102aa5696b7f4f669e1e0b3db2a8c101e1899f4803e8724d5e9407f485851ab9e27f0e7940a436faa458d51734e72c88c953a1cd19bfed29f9a6e503696a0b6823493b253c7878515b54225fa2cc2c43cc938fd1018c70f38a4649a2760f6cc067909d2812897dc8516e28343a51d9a0d987d0e7117eaad11d73c1e43dbd43da5e94f29ee359b1639549c847831283677d2dc6fa0b4e9bdf4affaeac14d82d816a069f303c63ff8bc301ad2cae0e77130b8771412f326f2dadc7c0d69543f99fc3b5b07139ada8c8443a7239af214df474b7515218b6e60c22294e4752120ff00af6b0d8a6f574fde3d9d31ac52559f9c1364efa10aca4626bddf9117a6cde7324503141532c8abd79c4694e62d4cb86596466160840dbfaf9000ffb80391b20e672a5f3f84f69c2d97d498f7449d1bb13f01818002923644f08ea3b3e94a83909315385d2720f9f98bffc8373ec9615900ed48016d62d4b5bbd9db07be2097ec1142cb4ef5789c9761ea7131d74168531c966e226d6dc9ace4464dd4aa2419f4fd2560ae90ee5021d85f58d208de486b6d008262451667f7fbf78bd41f6640410e52c87d7f000983c3282f1a6d7f4bc6c3753842dadd4bd8444fb54dfbbddfb4a627f6cc274263ae1060a69bdf3e0861847cc8810dc714756a05b8ca2ad1e2802a1d702133aec5901fba30e45dc302c7cbc1968132a3146c7832f67796a4c541788345733da795f14cd6049ddf75a49015e970c167f44226bf08bcc0721b0e43312cd33edba128ec39367db196b3f9e22817bc11d3bc0fd02aca7dc97a67b6896ddcef0fb349e9c3b36214fe2efd4cb55709fdd0511c1f18652d148e1959d2e6798bc956b6141176e0ff385b00f6178ae44f8bc2738aa8b71330cd645514f97c7db99e5722f935c22998887b7051d0ece56a1630af166104b50c80dd514eca36ad8feea1457acfa6d77b7991d1cecd96e7bada5161d34c5df4bfa8e7084390684652ddf60aa86f07a3269144e915bcc6e02f3de203dd990d931368a32f6075b3f49f95246e2686285651db5d4a08cfd102858902bfe01c03f1ad9cbf1b7ef45f67e6312ebb0c6b532731a4d26eab88ea7339f2e17582ba8136f2991301a7c24a3a3b5fb5fec1f3fef8b11e29ff40e9790f783af2f1da1cf1c4820e552ca692c2142fb81189dd32ee5b4cd67dc8920701023ef7b04edd473d4dc28377445acb892620abb94d69cbfcdd14f32227020a45a1ac5ba140ac9814b939870f27931e506f0a2676c9f65d88cac80d75b67a4ce57a07f8f2c0dd48cca8b68c2402cac0f8aed4314857f3473f904c7cf90286339fc11daa42b68686f74087bee2d18a1ceb6573d5faaf230e23dee53502595939e6220245606fbef3f3647483c3b7faea0b02e4e1ba5945258c9d51fa75512da79ebc9cc930184dd776227a2ac970d98c26cc7bbcbb699908974f2772729d069f7b8d890ab157aaf2faaf3282fecfe158631803de33736808af53a4febc35e372ff2452905007d266868ef9e28c1c631de830a7a3c38bb3b0664292487848815b002e4d0ed2474460c04b736484a520eaefb1ea931b86147abf7762ab20f703dcc3d607f8b0089caaa2eb682bb184dc81f43d39bc24daded115540412e4ea3ffb5adef53530a886a7b84b585d604f5b132e4f314fb20e59db3d005199a0703a149156a65e32aa0e38754aa43ef903a00c49b9c4a88a05d9f3bf958e07266055dd65088d9332b6c7d29ff611772b29263c0eccc81514b73cebbcf284247c809bd344aa416941c6bd68cc4e5c26c659f096345c1b8d5edc76a9d61ba130345fd5ad74b27a5e92781a44d259141e045f548c11b9679e7161823a61977ffb659471b0271d1000e2b89ad259d73b24aeba57cd6b941067906339e0ca696d048d302751cc0a39acc0b3a7ffbab9093266b59eedc8a12ee36530b7765feb1d69d64940461947246d619a14b4a57cb4bb9d60feeb98d8dedbefa88e3fb63be9ea99f9d65f7694c55462847e8212c9332f8c8e330500161e7eb4d113e3ec89356ea8c57eb739f5f01cf1e1e6a1040be7d194c75cec9fc63d377dd590df604615d5c1391ec17b535d7680dee50b2f96af256227ad345cc93f80822614d3fdb7f50966ef5d5771f6a239c24a3670723bec8bd40f09dd547cee615b07b54866e67f6323f4a6183a87e22b21690068f21e34f58f1854f39f902c2121cf7621a5ab326138284d3f4c54ea61a2cb1f8fc94b90c64a62f15927c4279fe1fd41551c26d4799ef9326809090aebb294270c2a541da191e151e04bca72bb50ea7154e249e8bf58d3248c323ab9efe2c75cc88deba848e0477c2d4f7842823efc16251b55dea0ddf7c06ff6372598913b16600c12af1f085fc8b0d695e469397a0ba7a18a1570930aff5be1b450c1d00b59bff7887dfcf7f2f0bd0a92934561b2e093c3295cd3969b8fd8836b37145271f5d5905e9e81ba87591638170928a90b8e9045055e740fb43cc3276a66c9129e35a0a0ef6fe53bbe1e2745db53e13f623bbdbaaa0bb4203ecf23e85f3838c1a74837fc00299a85ef5897d070b4faf2a7b56998b2e05dc2aeb645f18090d5d232d40f6bc20623c01fb890301fe074c7d09bc63ebe1bb260ac78cec14373add0f6dd4686087eae0e89f6a854adcc3437d01268dd3984957437fc292ff847d0704480c3a484db21e9f0d77969fd778259d723f1b887943f50ea98f75371864780eeecfcf5a77a5498f3379bf8d52e461765acd5ef59a7578df882e599c82b441291216f68f6ca3e602b47ed99aa0c6dfbfe69f1a644d22b66faa012fe35e84031c6cd6640f8cf548e61d4a0205b33dec975e6d8f3f7632b02c952948780ebc89169212ee0c8cbba3869490e0f659f8bbae0bcfed4ba2c81396a26fd929eb167e077a82cd634a5a203ca87729e6057ca8961428828cb53066492efe6ebc2d264b0f0b99506a07addda2986179905ae1744a342d6abfb3c82510551c7d46f176ec19b3fc7a6c4e0d0f0fbe8d495007c2d575b4f6edd0187aa08936a90101deafdf0b4a5912abba298b2d3864ddc9282c756bf0b241a35873151fa04296301519c00fe2804df9545cc47117b30af8cba291e17af28d40c30d05568a0f2c6febf4341ff71b80a9ed4944339aa12387542572592e2456eba9e4af6ea17722834a13602f714effd3c4eaa41db256790c488b7f4cd0c00a7e7be1c0740942fc931c3514016ddfcfcda716b312953b009d3fa126c50e2ea5515e4c0b5a3a66f53f27f261098a8adcbe46d98d5afc16e08ba6c716f18a36c56ed66529113d6b6b707071822cb4593a890ac726c30a87f2a356904b8acb3395fbaa5fa7f7e5c4b1f0c385261c40a7973e6c2847b215e7245b9f9ff0f381c098493bf8934241901d3b0238c82927ac418a390cb7d18d15626062a992593358c3eea37934ccd84161bbd272d021cfe3105284aa92738660327df5994eaa506a85ddc3bb5b12dd2f73c31511b1097640233620fce20826a296910b7a30f774fb572b7a1b60959ee49247fa74412b4c395963a505008da29ef947a6f676686340b98a7f7f8030c44c8bf90d1fb2167e266e7bcc2a6c7dde2496aaedc51f58bf1227b79ff40a60ea3581c19597eb1198139824e4cffed72280b7fcc1bc10a25541c2cde5143424deba327200c291", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000007a6dedcfcddb992da4890c8a97aebf9f00000000000000000000000000000000b4d83f75777e7e4919824dcb4ff934df01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x00000000000000000000000000000000000000000000000416c9296e38cf3689000000000000000000000000000000000000000000000007abf85d3a9f30c50400000000000000000000000000000000000000000000000237b411162b313eba000000000000000000000000000000000000000000000000000156b66ae45ebb00000000000000000000000000000000000000000000000fb163c45b929b1c0800000000000000000000000000000000000000000000000302879a33ee70e101000000000000000000000000000000000000000000000007d3fb89f63848af0800000000000000000000000000000000000000000000000000001191ba15fbd500000000000000000000000000000000000000000000000266d108d2bd6f116300000000000000000000000000000000000000000000000044a3d48dd17dc3d200000000000000000000000000000000000000000000000bc7f72ab82ba9582d00000000000000000000000000000000000000000000000000010d1bf42c416a0000000000000000000000000000000000000000000000021bab3c5cfd31c3d500000000000000000000000000000000000000000000000d01d612334779aeab0000000000000000000000000000000000000000000000090f4390fb5773215b0000000000000000000000000000000000000000000000000002561ec9fb68bf1e61c54fb309fe860b2a599e2c1339c9a0c1d4d4d7c19eb9e1925ecc353851c21f29faa0b2c9e2dce7b0ff6430e42bcab396059f4a009b643e80bb07dfd986a22c753cdc1297ee81add827710dcdce1b3905d8d137d45de8c4b58bb725680e4316c6900efe1d29c325ba03cc43ec30b8df8f7c8cef5da35935b202319ea75c872438635f5b3eebc558a54a7faacc657de20dda2064d0177e5ed3ee3ecd9f7ce014be0008a5b22bf423fb02ded702d843cb50b96102f67478344d3736244b75a9042116c4e0c0968f6fedabaad0ccf9ea1be6d1b6a2b42b566cf78603c24d65f315da1957f66178602e15a9baa053810b896567ba5fbc53b50b48a44400965116157c7f19bd181c7b184650af1f6df0687c64c2b54927fa663a224c6979cbb5a7274367923451bec6afa2d84276037f1dca6505e3ea5f37b5218d7e270ff9cddc001cc131923fe1c5c0cb360571bbde6d7670a3d91fad79d20d130044d4bab7e51ac2a0199f2bb567f4aa9fdae3b8634f70a6d47d4a8ec6829176f075caf523470eef2d4270dd12ee163794ae40acc907a4033b8427230e0a3080bbf4208bedc2169c5e1cf9f47ce448b3c89989c8ad99bd8726360fc259b1c35041205dd88b56028a4398df5cf93caadad64d1962b80ff243973bc4bf4a89700552f80d1e293c25c339bdd767fe4d0b7ee2d9b6c24f5bf65ea3a6e265f9c682b4f1b1a393f98c2d4017693a6eefa1e776ff4db63e9054253ba54e0f6c92613a8e3b55849035c606f4433589d1e140795dfd40198ef021ad53951f2dd41624bf7cc92e59783f3a29d9f6eca1bf91f470e595ece8a85c451e45a50cbacf2aedd775be0fdd413d44301d5db28a25fc8412e2a28e4e33bd176b54eee796bc300cd3d536f889712e1a0aad307d819cedd13a8abbed236376ad97d5acde39e0c8b0dc4d764ee788a0fd027b38b4bc46b09a7da5ad7f8af689326fe9c126a33a8123e47aaed4d5e877aa00d9708ee83d5b9fa03b2e24aa4245fc0d7e9250fae7191b292ede39b6180ad423b152d3cc3db2aed13f19256c56af480663ce8296ca90c2d85c2d694b51e6e20a4c03571d895ac7cf59e35392dc3908895026785cb42194d0559b3242912ea62adf77af10cd491f81184ad570951e233a8648246f3ffbcbdeb4e0f9307df9f824bc8a5dd0cfea8a335721305684c25bed635cfbac10e99964369e01ba3da0fa0df8138a289bef477027be8cb063844e59ff5567c05369b3d7fbc2d13656fee81b95251566f5aecb02dc9365cb51d522c3e368e3753313a9773206e10493eda00a40cebdc64ab5f42e68383442090734101028d4a3b3cc57c973529f6044eb1b1f09ed3188252c949bbccf14141f50b9c0d25f47019e1c0bfcdc01c9291ab7702315d4e4585011faf49227fabdfec64cc580697ea62d342a6d27227dd5a91a5b2a29035ec3e66629a2c99358f95d94ec98b50ef1b5bb59ee704b5470280668351ceeed6dfc8ba9c4627663f1a4b363425195d7bade1870885f1c07a8a2f8e82029d24fc02584248d25570148345a07fad19c756a1bddc07a8e0c3e0f72286441114933efd521c51bea3f3cc320adb2deac91b89e6fd016363d0b7fb6e2d10d4810e91cb4e2713ac83a62d28cb182779fd18be9f3766c7303ecff585f1c0bfd9503eeff54d606f92e1f978c61adb70cf59a3c0790d6c8184b688b47d10f28c5db24fc5858dde57e4fd4316c29873ff0f683c46c50af520b86b6bafc2505fabcaa0292ec8178651acd6b90460e6d65060be11d41ca74111a3bfbdc9b1d99cf7b3b221aa8a5f4789dbe9b007c91120d20e499db36f7abd83f02a1482d752f995987147cdb505bcf5b9968e82a0705a22f689f00bcb5a30ccc8fe0b25a99c0a22a6c27791efd79fe58fffcf59fa1aa0a7d8ca1a8e84fba4698840c6df2e048272a3528637ef304377cadacc8a86df22dcca01c4a63889492f030c519472e1f869ca329e91c181f32cf54a5bca551356b9c4d0c60e538977356b9622d597db2e97fb313fcc82f881eae7053f7aa4bd0edc0686b34acb24bbb90ba6757c84c3b6730ef27bf0f198b9488a471da7334a60d80558b08ced4e9c1b01f79f2358160268c9c1670d5334f325833ba1db22411592c14af3e83369d03ae3da83f8c0e4c9fd3620c897530e597293444e28daa1055f7d6f89f6ece42b1d9973c3f0c897dbfcf96160fa66e6c9c8bed3143148d883ff4fa31c3d6fe1ae6ec6e8b5ac3b840455595109d6b068a85ded28955858559d700697ffcc401df847df334026c21cb932da1072ad661a6469e211b76e5e7eb160581e3b296b2b5d771babaaf6508a29c034817feb2af6bf325448de4c7251b9e24a02dc474c923986346e30980758eb0cf2c19b53befbed9bdbdd43bbbd73b2f04cdbad27b9ebd4df23ed8955052b9a8f6fd1c32bff4a07aff3e1e56df730f2e2fc216b4f9fffdb5f6d53318b4987c68def42c021095a60702d3997ce3eeffeca5543841ae0b4a55349381d1222a2d6632b4244ff2a5766dd407d0cdb88beaf70bdef8c46ca2dccb6cfee6f5b90a61dd965b0186fcf84374a039cabcb975e7d50ebd8982c5a2474c5db21a6fa262cdb79a672220b8fa06bdf4aa029113eef5e332003ce5f03ef7618230c3774ed61c0482860128f589977a08a12e68b2743a80badf484f63bbeb5fb9141c25aeba24bc878a2741422ff3fed8720c2eb136d8d540db48d4428fd9b59150168bc623b40f22bf213fc3d76986485ba4f955af7475afdaff9f1a12c63f2baa6aa7ddd40cced56201d8a974e433e042073171389edb7a7b5e946148cba308dd80a02e78a2ad3e23019ef96b61d3dd0123805d495b486d76d0b9f3d8a48f1310ee4c733bcff3191500cfff383f27955e4ed578f043245bbccde47fd234312e389039a1b5bcf90b0d194297260a62a1ac783a496200362453eacf59e3be3dba243c1e33541a51aab521df84868549940a5b7e545b12a4c1fa0ce838bfc5244ac69e8b2834c7ba509f2801c3e1c0d77b6ab4c4f13a5e459ae4720f3aaa4e5bc98de8236f2a61188ec12326b66e96fbb825b539b41db33e51e53a6aca36b68bac613b91ab1b657d99f2187e9e99857159b991e1878086e6b931d24a8b5c391f1194461942c0ce522d8428dd1095799c7732b2af1df9ada0adecf15a46454aac9374abae6fc565e7aed60db2b57e5a24b226a0484bcf343aea6affba8c8997f19131a083d7da09a5bfaf165988bad45dfcb328512a710823a8a1743dfbec870e83e2352929f624372b97064e9d0dd64209c66dfd55ba091268d173e98baf277f969456077d749ba215a11417fddfbb5e3f7b410bc1350064abc3c0cc74470c845869eb2d57705cb5f11c21b11c833089928354ac0773b5bc6fafa12d4e9ba089acd82035f8a57b5e7d262829fce2cfa4e37be9fc640b5205689609d6ad185c87cf437727cc12b204fb310f676fcfee6c78ee2f3637333626d227af0d92d7912ff4dbe346b094b32bf36108066c5a3b416577bf7d266bbec4812447cabdadfc2e1660ee003e241449709f0196177629bd7a488502404a03c9f48199bc0d241b8f6ce8cbd8da78fce4eac72d68606740bd2a582d2f784d3cb38c610ff9c13bfe676485e7d02062273aad9a0d3aac2f2f3017e6d248adb5ac397957e3bbd8d1ce038afe1ea4b5368f73ea7907fb6239d7054a16a6139c7c9e9a44e66ce18c15d31ba66b18b5b7ab4d399f4706c5d0479decf2b5da336e6d2945c5c8317d9060ba8df6fa55a78def98656728083b6fea725a1e47cc8e7be08a999f1ab21d528816758209157ab26af22d787f196820105c4640ee968e4d67efdc9a46831d6b9623783013ea8361076998a3902fa23d94b1eaf9fd2f4676328df7a1b2081a69ee6b0d2ec1cd8437cfcfe5dcd411102263ba18952e56d9c59d047986456fb27170726bf7d605dc9c784eeed06d0d59a79ec52c11a1425f1aba6149c77a9309e8838992a803b339c6bf84db99de2e9e4148c3c43b740f6b456920ab24f7c0f9f820802051f900e30d3fd65e2ac91b1a57199f8c82c5ed3c86433a63b8cfbbb9745a5335cbae916502221a2789522a06ce883be2fdcac598cd70549e8c9f4927e327b836a98993f5c0352784ab4721bb59a473265104f1443983ba0c1f39583416ffdd1d897cc07d525218bd4200248199c942d674c166ddcc723dec117f0189752a57e4c763a2521f849285d49c0ca1d57d0bab91a530614e9a27da6aeacb64e5afc8878ba1c514d8579b1b2e762a859538f5ec6ea88523485a6e209cf838b4bf1225e681b8618809bec3778e832414d4c62f30678651e1d582a448252a16039e7ef6432892c0736a508c25ec9521de8dbb5d21b94f9d3f7b8520bdcbf31608a972f58ccb140a0f08bf0f3ef76b2f615185f8187636d59208375e12a170ded06c8f4d95db7e5a384c26312af709116fbc8fa440f4770f9131ba7882138ce3395fee9afb912050f9735442c7873311d08205fb16fc22d5fee5654380ddb1d88156a4f652f7aad1b5e23f9bef20b8223b417fed4192d84787d35e607239d822e6d16da4a2bac6f06d21267fde3bec0dafc9f0651e8abbfae8f450051d56071f197afae930935305da4d9dbf63ea1c0f62368f70f8a15866c9150ca5de551b11148581b7dbb3ac3a91104d89e4f4bc125e5a505925cadd64a4d7c104c6fe4792895f26b040c1cc46273327d06eda7c2d3de1f00f8dfcf6f911397b3b2c20c96d905efe0204cbba45adc56fd4ada7f8169ecd4306444313c846aa1d6e7881acc2bb3c32a9dd04bac0cb7043d97e45380502b10e7ee88623edb4ea6ac4aec8de66cff047b3a0b8de7e7030100434f5c11cbb97f6700728797787cad7111709a20288c667d7d4e08e095a40ad9b05be6e0552b75ed7fbd61c8dbe1165f2bd9a0e119b596999e49d83be09dbefb7e529a909c76b5258e8aab2f9b0e9e060a0e5b502f31647fd562b198e7a4aa3fce1b6332e35a4bc64f6d28fb7be4acd22efd02880227c2bb7de981afe547306c8a69f3f1cb20eb9fb9669531084d45b0149539882d12da0394612b2283947a289e615f113772c23cf3f00e8dd3b33e5fa8f024c53b3267b463981f72ce19e96a80dd6e61ada2e13c3f530742cfd36835e8a97c9a506cb699e0f86df3af34c52f4d3b1091213e38fb599a06c87eebc1ba26ba8d888df60d65a4a3554f4a061a9c63d369926e680d16f1db522fc33a0fbbf03153c6e6cb4058a11c4615ee010e70c379b71012acb47a537212ddc7fb695b37befbef71eab971ac03ae4cf7939a7c754e80208c03b9ae2f8efa57d6b4bf62e2ab53bff941b81ab2004d4214c8d1c3e41acd406c7f3f77d48a84e3c60228d1a839558a6c5b040155a3afcad38b03a89e03510128eb4e9892ab0b92c03ba8d4b43b964caba607a7d376bebc5bbe59cfcc543611f71dfdb56ad567e3ab4868bdc28f681e1284d267fee5eacc7938a2fb15539342bf1d9de71f15ceaeee10e13f488ab8e3a45d54c7abd84723bc141f90f1576e805498646c59515ae476fba76baf7609653089dd1b80dea0cc074c25794f48124100a10a5a3953da1e19403d981dccd265f4bdd93dfeb0a3b65f7dbf3a8b06a0a04e1ebe52ece119625511b8d01969c973f33de7b4b3fb6e2022e30de838c07dd12002f5b2ecc6538fb4c520a8f2e7baea4fefb8c5cf1ef2fafb00f0d19c6580c1b5a9c9f9deee13a3e889e50facb2f4538c1c43d325810643c44b1ec7f5526bd029e6c6b1d9a45bc6684109670fc6e0c9f8b1f3feb00fee3a61169e9a83c9dd42be2c6001cf3592b5c2932f494ed9a83bad49f60473d7612ed60a635d3e953d0028e993169bec778afb691c637b0cae6aba585d2cb8905481bd3eb5bf6de1da71a308aa629517e68450eaa8e7ea91580bbbeb30b7998ee27568ebd6145b6e70c20161bdf2491f545c1dcfa7303f7705b6628a1094faf98a5f1a36c6456e074a10401c234e52c9961b76f238172189d91befd0f7ebcd9d79039e29626f293d009100affd6d2254e10219e9b9566f8a923167933d37e586ee3f614a5012d69a00c00cd99154a20de6f0f04d9f32be106015e53d2c23d60fd9da49730d5ce3837402a375894d8f12d1b34e3995b154874c873f9a3037d09783b1b27ca9306bf2c3308dc4c73e1b95df94f830d9b250fc45d5e2370dfa351cbb79791e1c2d94dffbf13d328b9f475e53fda0f9b44c28ea21108032367e8edfef64f4c4e977b1c47a205869eaafa9c978e967a3cac32e4aae0aa895fe2cc67a6ed720d1169b06264e92dfbc6a99923c8e6fed8f6a951c771e259892ecc638a57264a7351b8e94e26d4185960e8ef13c4be85419936c73a289be253b2bda37d06148e481b495152e8ec1e0a7d1fdbd3a0d7de9891fcbd88f215cbcde85b4563f8993450bb2fb117443627b76e42ca03ed9c2718c877c640e216665f316867b5d1517328f980fdab9c0b1cad6ffe013a43c2fda4ced6d18d4a900d42a6397ff6cf7aa04665b6430ef14b04cf5f0b8e827810f4b9e628df101f9cc07b7f9d21aa602466ef25cac111f75b09c0ed411b1e0a0f442764955adc807d2292c032052d814ffb11fe2c4f25f54d26089052c2abc21152d0c63457259245e5dc695b2c3633eabdb6bb64ee7d1f652e5651678b410b12f0e7060b8e744416318503d6545ba957f5592047ca89f52b0ff2a856af7f6ec8051056bf31941000957a5d7e8a1b90f6758abb559a8582e726e70d762a07770a22949a32efad63c88fce8ab69e7ecfc221aa52fd689afca502f80afe8af67e02f63ea1dae245ae6df1691fc2922e963dffb4b40969a2afc401e1bc978955a901660fbc7d320019a8e81c76fc5e7e0719a37eb099913441151a6250590a660df462c08f119d70e9089e5acdb163526fd9d16f3d51d7ad6c612229709e6d3b097463069ade52da3f6e91a6c09d975847a351261525b3ed8b352e7c11480d1b743bf34c1c314e8a2143c012200bc4ccf66adc4cb8ecd828971e1065589ecdcf966626c728a71a42830941c96a4490ad3a3f081750168fead13327718d11cf276a90ba8e97d38e258e68e543300d01be6f2b06c51cc2b37489910233c2b17cc751da4d859d9d4bffd7a3cb89d6e8ecef481ee10e3ea70c3a50340bcc3646db58bd841b4439c17bd7116250020f3e9d0bcae0d122f013145b049309d0b89a89b0b5542a6c071dd247117f7a9c11d92a5813ab2234bd046e5f35682dbbcc16fe1f0d9b1d0fc7947ee0fd7d0fa3f41aaa13e7feb5ac3758b09b43a71d39360b515dd22150b18dcd0f4161ffd6ce130fdf34a1f21af7dadc0b3477492c58230829031e24bbd5947512c07ecbac8974dda0968a88ec196ea2a32988e41960210a0fcbe12fe59cfc251aee2a958577820683d2474c03530b5c3af41daf11471a0ba1ecf08b931a8b592a4341124146f7eea929b5bcdf97f4309388e4832e8acf205614a839dd4c803065ada539c038efa3793fd5d817a5d37150ac380c2688e1818bd1cd9402e785e1ec0bf8f70c0205df96db354c21c841dc5798019f150636540bc651792c2074eb47e8f6b8c8f4a1c18d43da452614900f830f4f6b1dd3cba0b6cdffdf86d4465254183f3b3037e25e5341956ae5bbb3e5b69764722fbccb0f85e82ddf9ac3e1b6c0e3765db51e570310681c3500d64f65fc81879c0755923b6c99e531545dcc3bd316065c7da8959f0adef1052b4371510dce386b17a9bad3a8a4e29ff2ba56067a8b4fe001c8a17667bd3776764b957eb097479e0c0b11340e650a8c5ca4a2fdaaac9f751fdc22b5ad7665a23856456a267cb44f0e0019ad5b49a2017bdf0771f62e195722f18270c350dbac52a3a0d303d9309821aca0436a16f4a3c4ca41dc150249bdd0364f2c89ab3c895b0004c6d626b566124a30f423f8c8b635d0919fdce2053e0414360f57280cd37737b540e65e2d99041a698a36f3ab376d2fde74928c547cc481f89a588176a7509bae404405fe041e6089219e12b9f770c516245400171796d9380cd2985e7eb7cdaa8cb473a3f4074552579d80c0699366babab60854d2c33ac00b5160891b6372947f93e910ea1348213ea777f50a05a88fdbd4b2494a7d16a2d5f6ea737e75ecbc7204b9864f192fe5523992d60c8d84d1b6eba8aeee8014743fba88fcee19d349c13d3d37242e3ddad8d0f0eece179bcd5382678b95d153b45a6172059f81d391c8bbbb815c1d82c2b71be144549c02e0f170ec5a8b1b86d844996b7a89320b61611d12e3cb00775bad99ee32f45f565cfe39d8c90333241b8aeb8c33596316a9b9d652b1ec2d5e8e9748cfe75a2049627dab27a6734e98f15902aa1aa3cd82d3a762fdcb02200ed6da0e18cf3f8afcca5bed31a93dfb79795183d3cb7d1032fd7da336aee52867f3c4d1fbe72a72ab839b3864acdb59b20ac38a1a267f62e4dcf71924a2cf035af3ac007525f75d53985c1ebef4201300f1052dbc5d090342d2ef29b839251024bae62c29cb468ead61bfdad488c919537f50e85fe271c25d7d7d7b550aea0a018522ca71bda2137e14a43499e6da8e8fc21aca461520818a0ed89bec8d8a280eb898c57d587192dbdf7c155004fabce811991a165fadbc3175a4ea707b2829bde1a0ee2ea0a10fc1fd8b041bd6a10ec6ed6549761499d74b7d9fd1e43e9b1642c7004a01c8dad4c27982cb87a76a7cdd92324a1c4a03338e0fb63bc6aee71e0f7ef06ba08da79fc1af81885746f981a1b1ccb86849bd93dc4946da8194560ee663dc8abfb3b4f401e97d4868baeb3cc9e02380dd317eba44b77585d03ad818eaaacb65f3696055f484c63bac79187a5569731e961f428906f70a9df8da601171826be0373379ee40fb7946317f2add3ba941641bc08e1c9e76500ed274c72fd2346a922c9ed939bed51203e6e8434334c8430851bcc79269e800eb376f9d05571558d8ebd306a3f43867406dfec2834d7e60492d786f022ac77e5fe38a4e1f3bcc526e694028e64972abf52a540e5fa9c3657b255b16443b0f94cf1f88450627b6abc191058fa16cee748550de84eabb8051a54cbde1478d527b01fec7d722847c86788c1e29add90ff796f2a4b430c50ac6724b1f50fbe0678f7f63ea8d08ba24f780459ee2118ffe09e17617a9a875f5fa866244a1e0a17929f2d4b67502b26decc17b0a6833b9e1fc9d6aa1014410dacf458da78fdd1cd87aeff21e0001a33b055d9887c58a323281ca2d6f08372098564035043daaadfe13ed795c100fbcfb85ee33450f020afe5c6701108419af3ab19dc36b0136a4e09638cbad8618cf10d5da4bb473b20e2d4f869f6abd9fdbb3125899efcf8145c6dbc8eaa5782dd735114427a7368894596336986dbfc6f83dcf2abfd02dc49e5096425bacaa0f4fefe353c5aed743e4f7de346ded390cdcf6cfa6866b2e26e307463f5e54142266af3ecbae5dfc0f6dc6ae9c95fe0b53369b7fe4d333ac323866d5577a41ea2c6d2a0983c01fa88c910a18d05f4b2ccc64617912fb052f68ec8f732e1c4ff81494d7b3ada40bac069be1534e46036e4fa15b67d82e35b6c4c537cc8d69babc03aae75362394a6489321b24e0767b05b683c261858ceb4252792911c06b625b0a1733c96181dcea552b33390c474d94fba7f60c1e79dd8630594b0e85eeff922521e5e26916a8d161d83d01faa227f2a9d6444fef04c87f4dcaa6039e1777692a97adbeb62a33eb4089559d7cb7dccb4f04431881d57d6d895aae0495f4327e2e4d202b13b3e055f00130e17d7312f1057000096cb0873f6f4e75cc094a879117c3b3c7a747f460fbb0783f83b3b0854477e023042ca514e68f4de8ce774d1626e6ad5c9f8602fe6c6b5fd8b31137dd1233ef55f086f28918764c2e9d456f212fb0444788ce44fd4352e3a05ac3fd55d1da6c5d5242c206256f663ede364e6e2373732b6b18f7611b1e4b63ad4c976c1deadf391b5da979c5b02b25d5ece40228affd372dd8a5500634ea7f14730689049209d5b5d28ed1af43b6a02339af83197d38d1f6964f7ebbeb83383a01410d59e08e3753b29980e6b536ae638590652a7b9a117822013f4bcdaea47c121aba0f791728da022a30d4aada1d57f8c623068f6b97acdf1888e3c9222e48ff811cb06afe6817952a5f84e27bd6ae7e6eb4063954f2271e0d8ed25f91193aa8f92e4a1a8c16fecad358545445169a129aef0d65d2d7d185b145537bc3b2785e0d7bc7ea6aa9309a2d0a00385348c250b3b614b1fbb7aae00ee336a9c40c652b647742a3607a56be6b253cd9152e8afeb67613bb31e1b89554b30c9ddb90be8fd9f795054412260ba7f17a74233ba263c9311c487638106e346d6570371469fd0bfd06b4db971cab7d4ef0990f3af631d7a41b9dffe4983c4c2e984d47ee3327e25e0e1bb4fd786160c4b34c846a56b76bf91c8277ea0a4cf36eb760488a05cc2aa70bcee215bd2d93510b35fc4eed1f1b4915e88f5976f5793046aba0a00dbccd7f3a316beab9510be08c9f05e784540a9e232fe8b33d7ec5ed4a277e7b0c17e27239a133d82cfe4dd3adad6816773967d50b2b89728d1190f312e5f10e8fc7d3ee8218ead95eecc465d5083cb6849c4e6c2c2d359afba7a0809dced78cd7163d88caf325866d1ebf80dba614817c3c47ca0594cbf43b6961415ad022c67b6e16820cca393368b51f821687ace8ab744039174d28be0ce2c8d9b07bbdd2972d52c9bfe6f26f55ddc67f015df08a2bdafd7512a69698ececec58664bbdb7c7389052eddaed595852df40e2666659e661a4972176250fff62ca3380e7f0e2fe8b22b2d710d76e6fcd4ffb0f7e8c9ac6c1127a2a83a5c61ab4c3749440a8f664d7d31bfd430dd80ed0cd48b0a70d592add19760701d3a991136bcb410559e27783a49625e8988733857cf2629c9197d174a94007003e3884b2dcbf2eadcafa5dd6f4c0795d41f78535c9e78424fd46ddb62e50251cec8360443fe70e56dda0d20550006d6fb8144d28755b55e83dda5a22dd8522df0d8beee822666e8eb9e52c43c3fbd074c41ca47c3bacd979ccfb528b2f2b0882f525851801b114af0f703235ca9cf8f9c9e50b2b5224430225b371379e7a1c7239a50c48885d57d708f217488080bd1ecb6d58f5205665fd593c9e0bcddf23b2f4eb1ef9301694a34e8738179e8e1369a60fc3fa11ed46407606363f52cd24ee26f4d3910611f69283f272f9ccc2844c3411baa34e8da7364dae8a75684000f0b2f9f04ff420e60d1d353ac9d13e7faafa17000ae8014df2fbf7d12c3d7817223f92516a4a8bb4f0e93ae2be4207956f7fe6c126daea596e4821329737b12f0e82c3f01cdb5915dcb711359d725ca71d8db88a20194032ee174e0296031b2f696b4545925563e1d1517e2c04b20f117d21ab1f518da2275af79d25e8d41607b2f5e9c795c05bd09e8e258b198a9b8d59b9abc7b6f80225c58cfeede76fb3278d3c307fac2e86cfb0dacebffb44e0c6dd9a82c44b6a09e480a03db77457542526566aaf260a14c32b6116575b481d6207aca05141d8d2a9d491cb9a1a99162696ec670c96835be0dbb7acd8345d7f58bde2465c9411c61efa95cf126e0c660a6cd0352efca0376e792a21ffb8bc5c5c4efda9db0450bb0ace4d4c9fa128b4297c7114a11be0b0e120b51308938e35bda2a3883517ae4eaa1d40c1b06f159e07d21576c33046501b0e5fcdcabfce665b833125b78d505ca673246352dd27fd1485ef6fb7528e432f977ed5ec10b0b786a819607c7750295556208206f2578925accd4fc26cab678389452aab1b5c737641fd584576f8b5eec4855e12d9ca56215db2dad99e19a7bfb18537fac87fd6e2afd076a160426081c063d883cf07f20a0721161fb081ff18cf9984812240c76a5ff5978697bd7ca060816322c90ad80126347b928a7ee34d2fa4331c9964d45b20e4dd03bfccef79a30ad07bc86b641d7ecde5d7184ccad6aaa66d296fefc304910def84095cb11e85d41fa5aa6b6e164350ec9827f1da24730efcd821b084047abd9dd6c6926526d5ae954086b0c510324f8d63c5d5cf2c7a0ec72078e8935b864d4174b1d65c9e56916404d56e472ada136e932d27cd277edc82d035efc8f117cff743cfd0988b3dd87e8c4af85425b0f856933d7f16194b39760fec8c7ceda5bfc1954cb224bb267af6ca4e2a1e0aee11946cb19fb8bb526017499058df0bfa6e932419e9114c7fa3504bae3aa52a34fba2cd6e0d3e3b57aef491f62d5cbfa2ca81503d2432eb3718df6baddad1042cc2b16fa503487386d885f97a0165000acbcfe60914f7f378bd732ac1632e2d3c231c0dc6f4f73190481327e93a6ea070953061c590c61cac490e057ed68d1041c85c671f90f92b4adf967b23d4b3bd487d88f2d20ddf0892d88fe69e7a732440efc7fcaf1bfe1c4447b2903475b6924ea834909b586dd15210e517abde7302648b539b2d3205bcca3c5e04320030fbed6bd23a791adbae7b600d61cfe58d0ecf9b32496dd8b49a19bc92e151bf569e3cdb51f522c92ad5fbcfb3825b2b4f088764f0347e072898b819fa4b7c650c6bf5bd0e3fa673ce756a382a7d7dd254265f60ed152b172b49ef4a87ec2cbeb0359785bfb85c5cf704640a8d891caef112dde0c9526f702286bf5ca74291dc3d23150254ea8fa8d7153ac3e5992750591938e09df761c13c4b4aa1921e072bfe257472220140d5ebdaa8cae7f560e9f714c95c52b4894db5df6c247c94baf38f12096da6808999fd18aa6670c88065e80226628c983330269161ac7017ffc0a4ffd32a3327d9a7cfe8db6a38ffe148801c6fde9870699e806e07e05aed0ad6e4ae588914c12cc74e63b4f4d30552d5fe051a876daef9218fc92516085c9864b930e810f0b61bfbc9a395b47da39e565817a2fca920d257e9ff9bf3eee1912d6c229cb578f5b0a15337fd914f134119c5153ae19ad490edcc124686d9fc1788c029d6cbb231dab0867395a3ced52cf5992b7aaa6d12151bb3ae8c3473238e72dc9fe1c255632cbcd1fc9481c38e4b3f9421b2aa75e8be1e3298246d5dfb689746af68ca58b6286b688406bb0bafc94c66286f9f12af684c6ebd555ffff7db08ecf3bd2fa20500b05ab8968351747ca14a0c9d64e6beedca294c73ce5617f6978c9b24e519125c94ba09c7ed874a80b7620feab3b353605c010f4b857897d7e39e3a6fbc840c848b4cb68aad5f3479611f2004a749cfe35a02ac5dc89b5393fe30f7c2afdc837b159c7a2ff06e596519c7116760cb3b73de1361f18d384c55c9e3540321a68c4e2e44e5c941b9f665059a1e008a16dce409193ed8cd54ec0b06a24ab4ae94fd5897ed4c4cdc5d1d3dc0b826c0b6ca7c81d0d98b62d261143a762da94b02604b811e3e7c53f79396d23a5b19f8794ad7850f1da3c9462a0716a77fb87fbe85d34c610c65e5a93921bce8160c2049a123f1e6f9357851a2900dec44340ab20ced52a80cd6e5ed17b9dc19471e2ae29c6cf98c7bec7d33c1cc38f39128f019367465a12455f773fd0ad443e0230482f9c17e3187b3ee26ac397edcddeee325f4dc38f7003776bd3f29645ce016d755ce4eaf4730ba5bea43cace5fc4f491b75bf4f40a1ef5d6880f57006f2e0ff39ff9153f842dbbe9239d0c5f12fa491325697746afbeb24c3fe8248ce5170bfa24f3eea9fc6bd7569cf327cc1dabca0c6d9334e669a0bc6fd382665a7bc60d5921f4c5b37845e303ad5f87cc337055eead375da43220395f7f258e2d617a028900a614996a6cc5e37c72feb25276f20428853d95e38dacb125fe86cd90d52c69c68c578991bf0a52e63bb34af47081cf8a6bac755d8a48593965adc95aea177dfc2a3e04caaec905d3c695a91784eca3b126da5e9c866d91c683b6cb3912124fc0d5d6688f89d4ed73b54d5f3e34face890f3958f7f8cb41dece502429942f9901794c73a7c09cf36212a51fd0782cc06fe30ee79f82d5484f23733f6c13233f93f7016dad4e73fff5bca533ac101209ee573f1cce3ff1fee1c719056c5e2ae2322639c3928eed49f97f1f1b48e503958593f036366eb21e23ce36b7214a1f59eae5e142031921a7723ce84d0724174f2ff35cadc02308f5fd66a0d59fab2fc15cd83713002d3fd0a2476d38b7851818a1b01b49ea13b0759ba2508e928807cb3903616113ef1e11f6ca8ab55043807f94c991add5aab254af2f42fc3ddd0cec41674123652d4628b6d3fcb5b051dd82eda1c68d133c33ba1bbd22a595c714a3ec3875595951d46b252c14e88841e72a69079ea1e1a0ac9cdb4d41e799c61a6d52ca385bcb12df3bc6bd2573d385559eb994f25c61406fa247a311066b9b2d6da3eaf9a50dc2d39ca4ea36890d8716745651c84cdece99eb27167760ee320fa5003bb3d07f7bacdd3251bb18c32693721a6558f007fb7831c0a7f1d8f0a7230909ae06b2c389fe18276a448db3414e3bbcaffa39a00de317b42e1d1be8fb0b3426ed4de416d966414a95a64f0006ccc9b753425580ec9c2c84f45dc83ad90471d2c0fd4fe6d06a3a279e72b673320bba822138c7b151d727b6abd414a88111fe52b8b0917acb88c22681ba264f140ab7b2cf227252babd5e83e3fee517361317646fad64650f2d6fc29e552093d4791db40cb5752ea44f8f1f42cb550c90", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000154d2d8b9b5e779fd4a14022ac64137900000000000000000000000000000000fcc98eb31a0ea5ce49a4df5434c3e52d01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } } diff --git a/circuits/benchmarks/results_insecure/report.md b/circuits/benchmarks/results_insecure/report.md index 4127b3532..6556c95da 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-19 15:02:16 UTC +**Generated:** 2026-05-19 20:29:11 UTC **Git Branch:** `feat/1525` -**Git Commit:** `a9da36063d769bd6a7fb70c313315091e702f273` +**Git Commit:** `987d67381fd3f10f38a19515d0ad4f7457b1fef5` **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.12 | 25.42 | 15.88 | -| C1 | 57818 | 0.35 | 26.35 | 15.88 | -| C2a | 142625 | 0.78 | 26.20 | 15.88 | -| C2b | 198355 | 0.92 | 26.92 | 15.88 | -| C3a | 132633 | 0.79 | 27.00 | 15.88 | -| C3b | 132633 | 0.79 | 27.00 | 15.88 | -| C4a | 92515 | 0.50 | 26.01 | 15.88 | -| C4b | 92515 | 0.50 | 26.01 | 15.88 | -| C5 | 151717 | 0.83 | 27.40 | 15.88 | -| user_data_encryption | 53732 | 0.37 | 28.29 | 15.88 | -| C6 | 86927 | 0.52 | 27.28 | 15.88 | -| C7 | 104273 | 0.50 | 26.38 | 15.88 | +| C0 | 6847 | 0.12 | 27.78 | 15.88 | +| C1 | 57818 | 0.34 | 25.42 | 15.88 | +| C2a | 142625 | 0.77 | 25.63 | 15.88 | +| C2b | 198355 | 0.88 | 27.44 | 15.88 | +| C3a | 132633 | 0.79 | 24.87 | 15.88 | +| C3b | 132633 | 0.79 | 24.87 | 15.88 | +| C4a | 92515 | 0.51 | 26.42 | 15.88 | +| C4b | 92515 | 0.51 | 26.42 | 15.88 | +| C5 | 151717 | 0.83 | 29.00 | 15.88 | +| user_data_encryption | 53732 | 0.33 | 25.86 | 15.88 | +| C6 | 86927 | 0.55 | 27.51 | 15.88 | +| C7 | 104273 | 0.54 | 27.78 | 15.88 | ### Artifacts | Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | | -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 10.69 KB | 0.47 KB | 3042712 | 176196 | 3218908 | -| Π_user | 15.88 KB | 0.12 KB | 2972857 | 170200 | 3143057 | +| Π_DKG | 10.69 KB | 0.47 KB | 3042761 | 176256 | 3219017 | +| Π_user | 15.88 KB | 0.12 KB | 2973025 | 170272 | 3143297 | | Π_dec | 10.69 KB | 3.47 KB | 3553819 | 187284 | 3741103 | ### Role / Phase / Activity | Role | Phase | Activity | Prove time | Proof size | Bandwidth | | --------------- | ----- | -------------------------------- | ---------- | ---------- | --------- | -| Each ciphernode | P1 | one-time DKG participation | 299.47 s | 127.00 KB | 128.19 KB | +| Each ciphernode | P1 | one-time DKG participation | 302.76 s | 127.00 KB | 128.19 KB | | Aggregator | P2 | combine folds + C5 | 0.83 s | 10.69 KB | 11.16 KB | -| User | P3 | per user input | 0.70 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) | 78.56 s | 10.69 KB | 14.16 KB | +| User | P3 | per user input | 0.66 s | 15.88 KB | 16.00 KB | +| Each ciphernode | P4 | per computation output (C6) | 0.55 s | 15.88 KB | 16.00 KB | +| Aggregator | P4 | per computation output (C7+fold) | 79.62 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.21 | +| Setup completed | 3.05 | +| Committee Setup Completed | 20.24 | | Committee Finalization Complete | 0.01 | -| ThresholdShares -> PublicKeyAggregated | 299.47 | -| E3Request -> PublicKeyAggregated | 302.03 | +| ThresholdShares -> PublicKeyAggregated | 302.76 | +| E3Request -> PublicKeyAggregated | 305.29 | | Application CT Gen | 0.31 | | Running FHE Application | 0.00 | -| Ciphertext published -> PlaintextAggregated | 78.56 | -| Entire Test | 404.20 | +| Ciphertext published -> PlaintextAggregated | 79.62 | +| Entire Test | 408.53 | ### Thread pool (same process as integration test) @@ -78,23 +78,23 @@ | CalculateDecryptionKey | 0.11 | 3 | 0.33 | | CalculateDecryptionShare | 0.61 | 3 | 1.82 | | CalculateThresholdDecryption | 0.56 | 1 | 0.56 | -| GenEsiSss | 0.13 | 3 | 0.38 | -| GenPkShareAndSkSss | 0.23 | 3 | 0.68 | -| ZkDecryptedSharesAggregation | 8.38 | 1 | 8.38 | -| ZkDecryptionAggregation | 48.47 | 1 | 48.47 | -| ZkDkgAggregation | 20.05 | 1 | 20.05 | -| ZkDkgShareDecryption | 1.48 | 6 | 8.90 | -| ZkNodeDkgFold | 61.02 | 3 | 183.06 | -| ZkPkAggregation | 2.12 | 1 | 2.12 | -| ZkPkBfv | 0.34 | 3 | 1.01 | -| ZkPkGeneration | 1.36 | 3 | 4.09 | -| ZkShareComputation | 2.71 | 6 | 16.29 | -| ZkShareEncryption | 2.52 | 24 | 60.41 | -| ZkThresholdShareDecryption | 6.08 | 3 | 18.25 | -| ZkVerifyShareDecryptionProofs | 0.10 | 3 | 0.29 | -| ZkVerifyShareProofs | 0.21 | 5 | 1.07 | - -Sum of tracked operation wall time: **376.16 s** (often much larger than end-to-end wall clock +| GenEsiSss | 0.12 | 3 | 0.37 | +| GenPkShareAndSkSss | 0.22 | 3 | 0.67 | +| ZkDecryptedSharesAggregation | 8.41 | 1 | 8.41 | +| ZkDecryptionAggregation | 49.38 | 1 | 49.38 | +| ZkDkgAggregation | 20.65 | 1 | 20.65 | +| ZkDkgShareDecryption | 1.47 | 6 | 8.79 | +| ZkNodeDkgFold | 62.10 | 3 | 186.30 | +| ZkPkAggregation | 2.15 | 1 | 2.15 | +| ZkPkBfv | 0.33 | 3 | 0.99 | +| ZkPkGeneration | 1.36 | 3 | 4.07 | +| ZkShareComputation | 2.68 | 6 | 16.08 | +| ZkShareEncryption | 2.50 | 24 | 60.01 | +| ZkThresholdShareDecryption | 6.12 | 3 | 18.37 | +| ZkVerifyShareDecryptionProofs | 0.10 | 3 | 0.31 | +| ZkVerifyShareProofs | 0.22 | 5 | 1.08 | + +Sum of tracked operation wall time: **380.33 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/src/threshold_plaintext_aggregator.rs b/crates/aggregator/src/threshold_plaintext_aggregator.rs index 521111d08..82c4ff961 100644 --- a/crates/aggregator/src/threshold_plaintext_aggregator.rs +++ b/crates/aggregator/src/threshold_plaintext_aggregator.rs @@ -693,6 +693,10 @@ impl ThresholdPlaintextAggregator { let reply = committee_reply .ok_or_else(|| anyhow!("committee reply required to fetch members"))?; let e3_id = self.e3_id.clone(); + info!( + e3_id = %e3_id, + "DecryptionAggregation: fetching committee members from sortition" + ); self.sortition .do_send(GetCommitteeMembersRequest { e3_id, reply }); return Ok(()); @@ -712,6 +716,10 @@ impl ThresholdPlaintextAggregator { return Ok(()); } let Some(honest_c6) = self.honest_c6_proofs_for_agg.as_ref() else { + warn!( + e3_id = %self.e3_id, + "DecryptionAggregation deferred: honest C6 proofs not retained on aggregator" + ); return Ok(()); }; // With proof aggregation enabled we must have a complete C6 set; otherwise we'd publish @@ -759,6 +767,12 @@ impl ThresholdPlaintextAggregator { }); } let corr = CorrelationId::new(); + info!( + e3_id = %self.e3_id, + num_jobs = num_ct, + c6_slots = c6_total_slots, + "DecryptionAggregation: publishing Zk compute request" + ); self.bus.publish( ComputeRequest::zk( ZkRequest::DecryptionAggregation(DecryptionAggregationRequest { diff --git a/crates/sortition/src/sortition.rs b/crates/sortition/src/sortition.rs index 4a9250c66..fc93da7ba 100644 --- a/crates/sortition/src/sortition.rs +++ b/crates/sortition/src/sortition.rs @@ -738,9 +738,18 @@ impl Handler for Sortition { fn handle(&mut self, msg: GetCommitteeMembersRequest, _: &mut Self::Context) -> Self::Result { trap(EType::Sortition, &self.bus.clone(), || { let members = self.get_committee(&msg.e3_id).map(|c| c.members().to_vec()); - msg.reply - .try_send(CommitteeMembersResponse { members }) - .map_err(|e| anyhow!("committee members reply send failed: {e}"))?; + let reply = msg.reply; + // `try_send` can drop the reply when the aggregator mailbox is busy (e.g. mid + // `AggregationProofSigned`), leaving decryption stuck after C7 with no ZK job. + actix::spawn(async move { + if reply + .send(CommitteeMembersResponse { members }) + .await + .is_err() + { + tracing::error!("committee members reply failed: aggregator recipient closed"); + } + }); Ok(()) }) } diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol index 097a3545e..a50396e2b 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol @@ -72,10 +72,16 @@ contract BfvDecryptionVerifier is IDecryptionVerifier { if (publicInputs[1] != expectedC7KeyHash) { return false; } - if (publicInputs[0] != expectedC6FoldKeyHash) { + if ( + publicInputs[COMMITTEE_HASH_HI_IDX] != + CommitteeHashLib.hi(committeeHash) + ) { return false; } - if (publicInputs[1] != expectedC7KeyHash) { + if ( + publicInputs[COMMITTEE_HASH_LO_IDX] != + CommitteeHashLib.lo(committeeHash) + ) { return false; } if (!_verifyPlaintextHash(publicInputs, plaintextOutputHash)) { 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/deployed_contracts.json b/packages/enclave-contracts/deployed_contracts.json index 19734ae22..fed693f62 100644 --- a/packages/enclave-contracts/deployed_contracts.json +++ b/packages/enclave-contracts/deployed_contracts.json @@ -123,5 +123,130 @@ "blockNumber": 10697357, "address": "0x9D55635A78B96A72ee540bdE376722350e9B8B70" } + }, + "localhost": { + "PoseidonT3": { + "blockNumber": 5, + "address": "0x3333333C0A88F9BE4fd23ed0536F9B6c427e3B93" + }, + "MockUSDC": { + "constructorArgs": { + "initialSupply": "1000000" + }, + "blockNumber": 6, + "address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" + }, + "EnclaveToken": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + }, + "blockNumber": 7, + "address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" + }, + "EnclaveTicketToken": { + "constructorArgs": { + "baseToken": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "registry": "0x0000000000000000000000000000000000000001", + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + }, + "blockNumber": 9, + "address": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" + }, + "SlashingManager": { + "constructorArgs": { + "admin": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + }, + "blockNumber": 10, + "address": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" + }, + "CiphernodeRegistryOwnable": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "submissionWindow": "10" + }, + "proxyRecords": { + "initData": "0xcd6dc687000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000000000000000000000000000000000000000000a", + "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "proxyAddress": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853", + "proxyAdminAddress": "0x9bd03768a7DCc129555dE410FF8E85528A4F88b5", + "implementationAddress": "0x0165878A594ca255338adfa4d48449f69242Eb8F" + }, + "blockNumber": 11, + "address": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" + }, + "BondingRegistry": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "ticketToken": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9", + "licenseToken": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0", + "registry": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853", + "slashedFundsTreasury": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "ticketPrice": "10000000", + "licenseRequiredBond": "100000000000000000000", + "minTicketBalance": "1", + "exitDelay": "604800" + }, + "proxyRecords": { + "initData": "0x7333fa82000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000dc64a140aa3e981100a9beca4e685f962f0cf6c90000000000000000000000009fe46736679d2d9a65f0992f2272de9f3c7fa6e0000000000000000000000000a513e6e4b8f2a923d98304ec87f64353c4d5c853000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000000000000000000000000000000000000009896800000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000093a80", + "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "proxyAddress": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318", + "proxyAdminAddress": "0x8aCd85898458400f7Db866d53FCFF6f0D49741FF", + "implementationAddress": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" + }, + "blockNumber": 12, + "address": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" + }, + "Enclave": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "registry": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853", + "bondingRegistry": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318", + "e3RefundManager": "0x0000000000000000000000000000000000000001", + "feeToken": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "maxDuration": "2592000", + "timeoutConfig": "{\"committeeFormationWindow\":3600,\"dkgWindow\":7200,\"computeWindow\":86400,\"decryptionWindow\":3600}" + }, + "proxyRecords": { + "initData": "0x4d600e5d000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000a513e6e4b8f2a923d98304ec87f64353c4d5c8530000000000000000000000008a791620dd6260079bf849dc5567adc3f2fdc3180000000000000000000000000000000000000000000000000000000000000001000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f05120000000000000000000000000000000000000000000000000000000000278d000000000000000000000000000000000000000000000000000000000000001c2000000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000000000000000e10", + "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "proxyAddress": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e", + "proxyAdminAddress": "0x8dAF17A20c9DBA35f005b6324F493785D239719d", + "implementationAddress": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788" + }, + "blockNumber": 15, + "address": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" + }, + "E3RefundManager": { + "constructorArgs": { + "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "enclave": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e", + "treasury": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + }, + "proxyRecords": { + "initData": "0xc0c53b8b000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000b7f8bc63bbcad18155201308c8f3540b07f84f5e000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "proxyAddress": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82", + "proxyAdminAddress": "0x32467b43BFa67273FC7dDda0999Ee9A12F2AaA08", + "implementationAddress": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" + }, + "blockNumber": 17, + "address": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82" + }, + "MockComputeProvider": { + "blockNumber": 19, + "address": "0x9E545E3C0baAB3E08CdfD552C960A1050f373042" + }, + "MockDecryptionVerifier": { + "blockNumber": 20, + "address": "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9" + }, + "MockPkVerifier": { + "blockNumber": 21, + "address": "0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8" + }, + "MockE3Program": { + "blockNumber": 22, + "address": "0x851356ae760d987E095750cCeb3bC6014560891C" + } } } \ No newline at end of file diff --git a/packages/enclave-contracts/tasks/enclave.ts b/packages/enclave-contracts/tasks/enclave.ts index e639b1ddc..bb69284e9 100644 --- a/packages/enclave-contracts/tasks/enclave.ts +++ b/packages/enclave-contracts/tasks/enclave.ts @@ -130,7 +130,7 @@ export const requestCommittee = task( .addOption({ name: "proofAggregationEnabled", description: "whether to enable proof aggregation (default: false)", - defaultValue: true, + defaultValue: false, type: ArgumentType.BOOLEAN, }) .setAction(async () => ({ diff --git a/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts b/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts index f31013e1f..03ef41686 100644 --- a/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts +++ b/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts @@ -195,66 +195,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, 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 6336eba31..5795847c2 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": "0x0000000000000000000000000000000000000000000000001cb3e9df822c791200000000000000000000000000000000000000000000000e6e2eb24f99c6e4fe00000000000000000000000000000000000000000000000f0341c256bc7f8f2c0000000000000000000000000000000000000000000000000002945e9bb27e6800000000000000000000000000000000000000000000000ca22d28f7e6b41a5600000000000000000000000000000000000000000000000fa4ce39d3e2c6c64c000000000000000000000000000000000000000000000000c382057b093f299f0000000000000000000000000000000000000000000000000000d2ab1e847eef00000000000000000000000000000000000000000000000732eae124bf33406c00000000000000000000000000000000000000000000000825bba25f971304c000000000000000000000000000000000000000000000000f31aa002ce9a71b020000000000000000000000000000000000000000000000000001c69dee5a348a0000000000000000000000000000000000000000000000082047502ee34e43830000000000000000000000000000000000000000000000098bc3dc090da06bd200000000000000000000000000000000000000000000000a395a0475f5823e140000000000000000000000000000000000000000000000000001d21f325773d10b2b91c7a8b3d057b7bc490148ca03e7c07ae5285593e8de30cc1ac03ac33ea123a54330466872a485fe60682f01940aecf0a16a29400c85c1ff026756e3feb104e48aa44e3c4435c6a81422971f410e652c2a8dbb6dce04531118b73de26b410850267190a569b1e5c0d99d36551817631476dce47dab6ce7ddfa42abee9cd50d8774b5dbe2992589f46d6b0d3c14df64e1f145ecf602d7f07d6bb8b077ad801635d81fb371fc342b18cf95c1280232e65ec5fdf4f7edc0694727528363aded2adf3a83dc8a210463d985cdb56d2f0d39aaeef44f7e2567bec394a51280aaa41d9718c798bf7003a861694b93dc6114eb66daa0cc69e0dbfc33065a442bff1f24da7df7f66206346b9c7f067dc75502807746037df9556453c8cfe619c13bf2002ffe7ada7eaa20f50572c400c4ffc4942970b82e2e42a5569a83f6f7cb7e11032c18d50c12e995d4aec40ac9bef1f9e92449cc1287a6ae05407339c176d21818bac164c691fda117bc7877f2e7bccc8e7d7257e4bb51996272017b55b3dda603b572716b3071143fad60717290ac7522b0944c91980411ff8755372680bdd7192dd2340cf54de8aed55abc38be8e4d864a57bd60f620f2a66f008218a6b5f41d926dc02c0c633628367cb8e460116ca71dbdb7054035eca657119596c3de48187112be8a3281245ce6e500f797cc1432a4d3c09f50d55d3cf2a76c072123062816948f10550ad9cc66240c3475cc3f7e1be8a364b22c852289e341fa94ef710e359bc943797bfdd06360aab35078dcb0f5f34feae065749d4499ce323c0c26276f732dc63c13091a70d853fd6f9d06c67a9219b36ab6cfc590fc247994b9cc26f3325182becff2efcc85ba9a3c8a5d260edc035d0bfc3e0845aabd9d1b893210c0d54eefaab1eaee3b1703468de3bf3b65973d385114a22b661f1b24e99a9a1df7f33c0a098f95b18ac88841857d37fd0d01befbff8eda91f1ee54b9d9bf4d0972e399e72488027be9753aa1dbcf846b5d614dc2e83df5ed880a51ac5672e90569ae383a1ca0dbc467c684df3bc238b9c05156103aa2aef2eb1fdf18fc875926636c6e183c34d39a42e830a4f8894c70c00a993805d0013500ff4cdd8d014620ce10ca69f17a638c92367872e4f56e8aacb16fe54754cf3f56c74e9296ff6823e9d239478326b2dfda0f711567d671b7c617f59f0369ba10265ba61a2debb21dc7411bc598d5fa875f66505485e1674af48502bfb1c69679777cbe6b7a901314fc90eaf3185389206b54c33a72f351e81beb8f7b0d26511d530a8eab1b28e11b330ff20265875c8ac3b81da94e8665e4a60044918614ed85954f5ee5ab759e1512c2e32b793a0d0acbdf5e4e38d9cacbe258b66de1622bb1fb345e98b6173d2a8df716393a447531eaba0be23f1fbded411e71808271a3a82b4f9975c174051569589970a0511743bf47bd75b9f04e2f7a5d4b7deaab3ca11d0dc741998c2a030c6c5458d176c04428bec5a3e0d8e0e5c56a3eee999b10f4e41008d0ee6c500c4d4285ef91799d520036adce78bb4c4d435e0379a267084b998c065e2b5d2c05da4a111ab012aa81a0bf2854c7c58136cb6e2c629474ecd90a5041606bc645022463e9a9e43918859426adcea475191e505ee6a37d3afb0cda9aa1c7c788bb0a88deee1aefc855d078af3172244c5d3f21ddbfc514c0836a3c498dea40a5511c6fb874ae86b70059e90abb007bcef2ef2c247cbc5c6327d1d53a26e8c39123107878df9ad00525610d70a76cb32b6db209f420c58f9fb3b3053cdd884ab03303467453d17b995bb31d2bb667f55e5a6f98af83e14c7d2f71f661cbcb3e29e42855c0566eec6cd80417f89b09a4dad9bbf28e9aeacde3580fe7fbe92cbc8d6e23394556cab576138d117b28865defcf49b06e5b82ccc8137f314a7b3f7c2bec071311be89cf5d785e50c106985d91efc1623bb02487a838a29ba70228de3cd618e92a7a82355306a10a99302db1b6f705e6e6ae73cd5d6e3e7d5787023be31124704e1185bf63c8a0578e04b8fde9c99c2f4f5b96431334d6b4065ff89d8cf905c47b96cd804bb285ce1d70a99679ce64f47a71354031d01399cdcbd0ad1e0e06ce9eca7c849eba54a12dbe233f25e8e7a041c192512e99f964dbe30ae60c9b0d2152494c0e45fe1ee814a0931add16233f30a51e9865d9f39e6c5e956c872b14b51eaa8a1c3607325a9b7daf83937fb73caa1fe79e4791869cdfd75a2a4d662cbb0dd627932ea4a38efd1bbc223e92e269674601bb0042b816acd8f55aadc0186ec668949e62b5cc325bed81f906800ee00b52c5f0c3dc0fc6aada1fd6b5a823e1d846890c9bf6f66d991dd8547b31d71058b9de07eab2192c8afe637c651f161176928f1ada22ee5450992b6410cd20f9c0cb72ed0aa36e7f92d47592c53b0b5e2f62a81ee85e4454543894d2e45ca07656b56cec384333eb8620438e1dbb07d8b15737f5a26091192263237fb3545f2a7c2cb52e27d82fc63dadc2ef6ec41cbf43034f22b18a487f1571b7f49ad6cb43662c1d88884ddfc4c6c636ae714d0ca4003603acb3a8a4ba957d6cfc7cfda372f89a2f389757dbe339dd2171f5351baa09fa66e772ff12885bbf3be23fcce81cb59e6021505417367c2f49821f7823ba2358ab38328713a2efb8139298fbd22899d5669e32ff033d4e760e7d8f9d1b3399208db97688cd9c02c839ca9843236e13c214a2d8598e601cb5697ebc782d31df4ba699e8ee8ae919ab36f57dc5ca0cb0e5ae37e10a6706eb18514783df1e89de03b764cde27719b9536b5e4b9f15842d91513fcff1171e709145b5e5f215721d3f2ec845743d4e522e9f27aa662317ca94c561e19a62a3aab50e1d093f097730e2abd72721923c2513efd225ebd9e4b87073bc3c5b2bde83296d57572211f90af331fdef28dc12ed2ba3e3457e2d30e25d1cd26a66b9948b4682c7ea34143d8dc9ad72dc0c04b2a9828c7fd365f728364267489a5defaad4506e3528fc2313b419a3513eebddc60db948fd08df616379ac05f981d69e9e6ae19d5216b22b9c725c9f56c6c0542f7d4070281c3d16adfa7280d2f7846be2a75c73b99e5e230445761d0b8c4cbea2c0b6f7bad692ced4087f1b72de9ee1385140d3bf59390dfef89575ea75a6c0006f14bc476665d96fbdd305c046b7785bfa3063e49fe20879f97e36487df6604faaac54f65709de50110e7f64b62567aca3d6d1478b222ec9289fc1f61f423f08706ebb039d8a224625cdb422ade922c3c0d15036b3e80328f349c0cef97e0a1260b8eae02768c399d5b0f0f29946fb8a18437fda491114ddfa6945933cdc3521ebc1f70b5a3cffe275b5fa04d6d087e1142dee32f46919f1de02a266f6698e3e1f7326ea2c0e5a0e45dd29d22f412e7d39f0cb8f0fdb140d8a7588a9664f65018b4119f5a9d841f67618f140b5c0b43d85b4765a1b292e1bfa2fe17a6947d95f1abe578060534da85fa9478affe42e5eac1c93ebdf8e050d285507fa80e7e8720eb1212f85fad61f1bfd5781c2f60717013af98afd0b1018f1d3953038b40c827b29ec08ad490c80187c112ad8cb0516affcf68d114715b47322c59b0e1d0f0d7d05d85f30e3c115d84a815a7ca750f54e65380a208d1797c705b503bdb86142e1b3581dffc3bab4936c6bc2ceddb9ba58168ccf34492e26801b14cfa1d6cbc813c6ca494101f98faf1cc503d28cb425effd439e81aa2a35364d8803d7a3099f3ec344b02fed23158c79ae5cc2ad43741d6cfa830fe21448fc263e12436347d0d59429d15f024abb4c992b911607b0e4f3a31d9496830f80f0049b464f3f1d4cb46954e071c61399cc66260135a1c79012993d59aa1b218b5f0f80f334e9274dcca5fad1e7a857adf7674bb638a9d0e1392968058512045719e05e2b0b9924a33e2769d059a9e92001bf1d8ff4a3c467d10c0226515106794a473cdf41066d07fd466d2d26e02adabc066365f55b758e5a7f7de4f11c1c63e88a4dea4ad07b671448ef5648964f5bf16094882acf6039d3d7eb07fd350a575b3c32e6f01c55e761b811e7a2c76e2ea0c2478f11844f709f68a8ba186d0692161774697b1f7fcd284ec73cec608c4d4970b58619e605e6c720bdc0f4a50ba4d96cbb1641bc70af97f43ee092264f76633be11a88b4c693c07633537e850d91d688b0abdfa27ac8224656f773bde1aa49655d53de94684fd4469d5a98251e3a5b80127d39d1cbcd95ac6b9942655b77c697de2a43fa9791a27c1a6b50dd1aeb9197a93cfdee8d8c51cc1603f26f631a7327ad19003afc88c47469cb195b2f2955b16d08495c1b318cb0b58965dfc7f8c37faf153429001e8c9526753fbd07480dd9cf394444fbf38ae59ab4ea32b0f1d98d4f5615abd67e895e03f9fe072ac6572e3170094d4b8ba9279275f46b832a091d5345ead1b14b425157fdaf93112579386dcf605702a30bceb04ccb587b8c9fd67db5a084ebe2c133e1f0de76153de5b3e8e2f0089b682f578cc0c83be4bcb066e2b8d23e41bf850e18ebf504056a78c10aaf829a5613e43adc2d74df5317560b0f92d1bf9c8a7e028dedd9c914ad2fd6e06e8b56e1043312a8ab2562b8a1811e012f39db2a42d53b53e8862002816727324c27a9099190fa3b9d4ebf24614ae352a18aa9853e9e51e8e9fce807c16f4d1ed3fe76046138217835250863c7372ee35d0e44df6ae7f3354a030614efd55b8fc55f11a1e630f4d62a90e541de4057b60f1e340fe299aaec97842419aa5aab5d02cbb119f8667ebd6596282dfaced456b243a1017e636257c00725145c8c1a7d0ced612168ed7785fbf218546cc3773b80d8fe9a2cd899cc67ab2117ff739c3882ac173dcd0997adaa4836f1d565c5fb896fe92d36be4ea354959626ccc45e32ebe2da8450b4c324e9c84e859826d026dc527092ad112e52ca923617a5a64a750f9973cc25777d4e10bf1bc54f366f5820efd2a4d084b14fde269812ed4006701b1da3b123cfea6825b7408aa2fa247b2d4e48e068400a90ef01fe21067a7e5de43254e905a3a430247e8e945fdcf8301f08064fa14fafb084f0b60ff3820f3a15db26cf3b3d472b6c8e568171b75e01a0687857ee3d697359e81d1e7c35fb460bce208eaf60da69cd3970cc07431a6c7b414c3f14335b6ea545082978787372cac6946fa0a5181a8edff08ae4951f0aabe83c4327f9b16fe126542a07056e734d522aa8e3beda35baccfbaf8f8b07d5861f0a93d872676e20d3c1066991d1944305e34c4dfef3dc1880e5df027313a00f04b9ed2f5c98950ce16a0b54648a6fff62bad46d6201674e4f6c21b626f6c16806c354c68dc9b4aa6c9706851f4626499889d5cb3c148f4cbdbbbfeb07e408def8f627609f9fb4b796ea2f6f8f2aa0c153f339c07e47b7f2a875ab7f7ac5cfce654ec42c87600f21d31616b1c342cbffa7ed1b1d4d288a521634153403519363147c9971f04133d89971045398001304fd850beea80c393312d40c8555cb03bc562af7ac3b349b1bf2cd2b92ae6a6792c1d2f64fed85ef4c1a5c6e8dddf8f9a579a54c916c60e82047dd1600e5431125d76b78b8c2a098f18e46f415869bd5dac6f83169c2c8d29f6b4328a0912196820e9d995762575d0f0debeaf9b5b0e2d270c5ce3b76083e36e08401f94afce6ace19ea8fc1fbb1af3f43e109914b7abc6a65fea2ab48a5000af7223958132cb267dbb555c4bc2399e5426b6b3a27105f22c707becfbacb18ad58306fb6fe953b77d8e16475a79f0a1e4722528ed68fea7f1f534c99c8d2db904cc0cda8066d588098e2046d1ea0630ffbfe2011ec234d0c48e8dc6113a19671866253d11e40aefb19efcd13c463084ac484b02f2dedce09c436be72443f4213baa070dd14cf108dff6cb696f02cd05fa5f3e95bae74dd28547273cc1abd77cdbcb00714b116dd81e1e08a4936266aed104058d594954937b52d87ca8ba959a577108d031a69f7febd6c4168783d44fe7287829c8a07371159878bd11fe51cb232f0921f082d176de4cf1ed7bc8b76c7b56de66ec2f752931bb6b6194ea26e0c6f72a98a7e7145a8975a74091cf36fb93f433c969dd565b1bc5cdfcb205cc15c4642f4084b54cee37e360892808ecc8dff740f475bc40d04195bac4a03e9ec76cad1bebe7afd1732cf16794ef6462ab017a80d75ec20e745783bb35803f4b4b617e11526b6e094e5fdbd4f2ac0531a43d1106a93e8052366edba0ecd93ae8e04f282d1333ca58b422ab5234dbc503685b3b1fc93606c1aa5b712b2379e365bf55a51e63f83c7934394efef92012d8cb6800663808ca41d82a5e76c71495a7eebc7629d59d3741b52d96dec1f9741dd750baa5e3e411bdcf0532fd4a0b394a9d911a13b695df6d5ecb4503c9dad0b958664def47dd332b4058bc0e9b1933ac7503c92aca8608d310e70c33a422f0f3f5417380984574f512371b28f66cddfa883c4006b04a9b3c7a3ba0f6804dd6c4df7feee728a5632d04660c6d954179eaeb44542d11b58d31f9ed821464cd0373912dfe4fa22bd19b7065abbc6ceb26a19981842a3965530707b4befb527c0fda0a0cf69e63eda44b13ef3d14863b69dd6978131b11cabd86e10ffed5d750876db45ed410584991599cf4aca734bd4bf51c781127c6ea4ab85b240cb1bd4facec08e873d35dde95121dbdd9d47a8793cb4870df0a1b9a78b9b685580c9dfb20a5e2a15fa3752130f183edcb97c5be401beb6cf107c0adc97caab3584251942f7286cc06fa9f098e3d246e7dbc07d9608b6f5f991a6cac58ef9eb3e90c9f8fe72538df09b2b7b4a577e8b5ad0a2dccc52226746f09b1124b9ecf6a30f133399dccfa765b5d4700dd4b3b9bb71bc2bfead45447c30a868eb5de7f9664fabc6fd95a16fbc657ed53652cb805c3a819bad87ee0eeea07613f74b099d871c110ad32d1351a8aeadf23e6d0e204def4439207b50b2d4202383e3c044f9459cdbe3b7bd460b412b228e7b2f765cc1c5ef19eaf6e417be81389227ca8073c6176027f2d7e0e8421339891238ac8b089c5d1ea4396b43b650d8c183e6314a3c894dd7e6e201332e5c474a08a6047bad68d93909b92aaede71a80a43546806bad9165fd9c426fcbf14a233da388ddbbc7a4b9e6ea3518db232d05a4a41555db710f110c8f3dec06f2efee927471e7c2956b89410d14ceebc5066d005f93104f18c112b2dfec079cb4063f6af28f97c50ff62e6e6c1e5b5d970ab0cd7969c070c5ace23eafb24c3504cd673d1e5dd2fcafe0fb5c5a8116ea8414c1441534aa5ce52008ee910e68da93e170405862dea3ad15760000906447372ffe6c06baccfc669d8c0abbbee6fbc66a530df1c8a15ddecfece85c3e59c99b2f408d6225a32f1c8a5bbcb092d81020c5c943918ca370ee1066b9cda7ac237f25e442ce78c832078a1cca4ed502d30f1d61f4cb9b31fa1c3339535e317b7ad5291c5ab8955c5fa35886e5ef299dee976d839d1659aa0b7798f3a04053fbb3e00a9540ae0e1e203b830e5076fec300443e13d97564007c833faa0afda19fe75d05b04d2cd5243a1c81ea8ad4d236a1cd7a49e82d0fe8260ef7db074dc985bc53273cabfa4034402fd5f0be8f50f61d64063bed010ddf545a2a0819da87aabd2522b9c66a1023ccdd30183e1c297ccf73678f5342d0f6c3fecdb4ea6549f6f9890570d83521bdafb330e97cda239c6c72c27822f493589f2c3a9a7bc45dab9f230dfbb6680e8f3f8d2d51c3d0ac847f7a3110440318e046edefb4d3a5c854747a2d517240483a68a859c5a2517f9d257642c2a95603c14929a57100056111310713bbee85e2eb28d2f56212ec3df83e9bc2d95901789082be5eb67ccfa5d9fb1802b54fb79467cc5b7d9105117cb0be5c3774ca2b71c5c6ebff3be8981994cfad0e12ccee44ba1686ddb45ca9e05c5e827cba306084515b046578f11a9e25b72c089771b551d6cda59fe84c62744869636eaadc43ec56d5050f7fba28c6bafbe32eee5865d3d6d2f7ae5bce8ceeeed068cb1f022728177e68ac18ffac673bad8d2e5ab2e2f406cbc4592907bf333bd5fc49dd9e01370f3c7a9b006cf71557d51827da6014a008f75d47fb0c6eb5f92f4967de88232ad37605c6100984941f681513d9f213a93bed129c2e3e04ade7b03081573e8dee5d8efe325fed0a720c9810144f53c1a953c7076ee5f1762cd845b22f20de501647c107b71a88d034d1383413369152bb657df8a1586b7fcd8ba48363c9305ab33f7f5b244b97287b02a2d924fac5ad4344918be6b863a65249135b38df4166c6d7dae73a36aca560c91ceb203fdfcbbd96b37cdd2a655d2db5fab215991015eb7aa3a6dfef01849794be8f1159c8fb2e0ac69da8979d7646f5aadd53063ec5ab68bd06439c2b9ce469ccbc19acb857ab262170b62d959f7abe12547d52b38a70b048448428f3d56e7c52e029413e913cfdc8db69f7e7d37ebbe49e29c75ce22209ce58b4ff9f1d9fc9fe802af5b6551d3a1756dfe71637f61bcaac595f3aa1989ccde9acd830305f80fbbd2d16fbd3956488af15e5d4fdeae1b5261646dd5fc86d84bb6b45793e77695de20fb8c84900d6f5e8f54a963dcd6d42e073ce04fbe219e2a4af3a8fcf31c952051db25526c091693d249eb293bd4503d1103aecbc69bbe5bb5631483f1b9594442e7fdac9e909c715dcf47095e393e79f8d5b4ab497648032f1629733d97ef3ee1d8878696954e5fa815d5d1ff3ec0ab8ab9723d6837924e59169682902d3db110d7cd26eecc981d01bcb139557c486c744018ca6bd19b5c388a7e077c6e556f706f4ddc0bf4429db4217165962bb635099628fdd49630587a94143c0c89ad8cc0243fa20eb7f688550b80136aba7bb80ef53272fe7d205c34de7b1300c99023a07a06f52b53dbfd06e521156b855efb8221e0f69072c5635a904f63b7d2d011126e6720cf2927e27280d4518b1cacd709c5918b022b40996e1768dd9d3cb128d1c9fd2a5697a15b330812eb172ce016e47f4bbb5635a1a22c618e23281b471510c77e93d57d882bdea72c28849a4c0516c9ee76b492065afcd0c4fd86d90de9c135ec9a551e0387d1f5937023543e78c0f1e915cf6488565eb451ac3b3e20a6602c1673f7387f668a16c81eb509a15ebe643363b15be3a5b325e0221ec1cf0bc3061a2eaefe0bccd271a1291add175632b619454843e7e1bc46e34441587b61e00a40a910d9fb377770ebd08cc804592ea16e64684237cda52ce30381696281719ca3144b11fe63310d8c20cfe793ff404ae5d1bb37990c661091a17c90f9dc81a12f6e7b6ca63ae4bddd5923718d2b57fe6d4f11c443e06d49767c13e458bca27169799b2d6e904950b62d0ab568bd90296e39f8c03b2d90222508dbcaa76112df2ff50a75f281063fc81340a1ed747062fd26481f85b13c2de3a476b3a73fb1b39392a1f122f0c51a5c476d7895586763837029da273e25e111ba071d6dce602a13dd662cd862fc55adf37d20d67d523839c127dfbd1845c858051e193399c2736016c9c2ff55d2c1eec1456fc6d05a71007b69fa06ffaeff35cbec075bc102dd0e482b0b3e99e947f8c7f8531842fa58ce3f6f7111c6e52d0d54a16365fe52e15426bf9adf5031e27dac309386109c90e1e4780b9d03e42a2f450e2018f902c6fac8c3a202c1a526fe1c0c02ee28473be3fa4bb274cdb80f39cb0f8c958f5128a6d2cbcaf93586738ddf271c23bec5621fb147d08c561a89ae924383c048b086408035394acbc323bda1909c591844a75b4ac76486651352f0fc28cbb946a0d675cdb218ea5d78641584ec8447db6ec0d870e92199d0711e092c7d5fb7a1b0d5b8659b2f4f09d665914906900169696e026127ec615dfc7f72172d4e9c9950e40b402f05affb26f8e17d638becc4998d96780e0dc21262cd79bb5dcac57f00f9fe8c5541078e8a05994bfae65586c513d8e93142fd0f38e02dd1ac04378db089cd4f5d445f6c772cc79813e2803f220cbfa3429929f5498ce561493460b3613ffee4ec8aabaefd4e078e34431c3623277623cae710755721a6889d28dcb682fe59bd2a912c3a0ea267afa0bcbc29decc7598452e8427ec96bd4e4ff7dad93279bf4d91fec451d7e038f87a15f6a8a4b39f7836384879e5a558ac7f12a5fab2c732e618b33458c70a287b9c392bf96e15d064acdb6c85b4ffb27a8d6c08f9403bd525b914f19ccc4e0752c1ba7303f3ba9a414e6e620de995481fd369295cc085ee0d4086cc322511612aeeba8435785e0d747f6ef41113e99f02daeb6b9b016e04769c7707b166fc721ba312ac44869e72db339d8d94f12280ef730872d27180307091a47a51ff40387adfff6cfe5ff23ca72a089110af0f3877e2c35c0111199a2422a0bb1b19e7ceefe12c2683fb846c5be7a1c6f60aaf4ecd576c6b2c62cb5a0bff53fd46873a6782deb555edc23bbc11bdbba75dd1be6a1ad6cf829f70072d47f9170994d097f05cf07012dc2b0b4ec8dd4d9c779b858504a67c6833c22e7d6ac42fd50951f66bf81ae3a02f2f806dc1b77d076b5890bc4f44044ee2f2eafb480f71d11792aa60ad86e548a74684fd1aba10723a92b11ce1a7dd6a5561fe49c7c07442f6276a9508e3f805c833fc367856062c57f21f63a55942ee8fc2e9471647d1417f060bd8d0c66a27d1c1f3901339d4697ff39e6480609fd878b176f95e7a6596bc761cfc02a612cb5807b33e7803fc5b1d79bc27eccc7d3f035177b4134650ab8951b1539c16d7ff813346f0ba46042ab279344cbe1bfb4ac912b800fca5c41a1e01e2e48367606d18a6ac9ac259eab77260b92273b43a0faad0559d05bf0d90c3d1f7b74a54cdc688f619dd581aa440f8417492d917847623a18ef5410b04c88ec70cd12b19d1b4d506a86d8b42ef83bba4b3e5af3c49f3e8026e2eb7d9fec7687354ac5b0887116157de802696db73422719c1ea40e944c3229d5159ce10079adcf9ceb5ff67124799050f8ec9e742b8bb1ab15d470e72d890af040425dc1bf62303ae3a3e7a96d2a43c893e80ffb8a28f4111b111aeb361103ee2e7d8024fd4e508efe2e2c0bea5a56827b8726cacc7dc08fa96011013de61510a2b1b058e3f643c735507536f749772c7baf6cdf30eacb40473ca90c745323467fd21411c1ff380b74345d7e144ad0bd7fa7d7c1fc7251fd916348bd38e0169732cd4a5c892c1ea29cff802ca57db4593be104a408399a46f970c0d6f84325a21b7cf69ee061ccc2a3a50316c840af78877ba416a90b831f59bced3bc2f42a89d5a9b0aa377090344502b3507058d31582b403b5817becf3abf3063e40611dbca2846cc99bc087b5f2324908a49e5e08d09fded93fc0fc1a98e7dc5279ae235dfb49b929674403a5e7215847f2c4263f4a937f45683395abd3fda138a1e8041e083ec5724df8f5f59ee81ab3d8f91243b4748ecb153908f8ceddc7b2a6132bc19e5dbc68d1159619cab24990dde0069d6923ec90219fbc634f0ffdaebd6e2dec866563e71fb0b99b341a3225d9941ed6f84bfdaf764bcf58829b65008f8f1ec19284d1e0d08e4fe2bb65238804b564e62228659033401f5137bffa7748ef26ee4d3cb886995198691f9bd7f6821dafc091f004ead76deff2afa175022fad1d70f5f312bb20b8c34c9b38335493d62414b52515b4c70bb5fd41efeb8bfe54027b2630d780f4d4b4c5f6ec6e38d9ac6e64b0771cf910a02666224d7c9930a400d381db895e295584fd6dd968103a6d4c18e438f5d3cedc0b55b1cccb54a12313c43815f9ee10284257ad86a0ecaa993455d03fe2c4d54daf8197032b9baa401614ced4399d79bb7beef9ce5bcd8932ae1cf525f565402887c83aa92456761d0abd91781abfb5469a2640f41acbf4ce293417771fb35f5ffd261422ea27deb808b8f1e7f676bcb61968d1cf3fc00f94fce26a573eacfc8c1638da80d4eddde126a10c89af520a37d898f88198aafd4d70804ebe05448ad486113edb2b2aebaa18e964f683cbcc9090e294d7a7437be1ca1ea74dcb36d84a2321bf301db8193e2b94705f8aebc28168b8a51c4ca04dcfbac794fab48303ff71240442ef421e250fa2c370d0ace9c55ea508ca6166436d03d3df8315970bbd8bcd844f4bc68f5b06299dab59818f88f7e301b5c4316aeb59aad5d5f7097eb27b86c0cd57fa81c703af3bf34b8c520cc3d2c5a52747788eb8199250fe53a397e6974413b277b9c70a27b0ac6f69139f75109d096a4f17c88c4e93bb3f9ecbe10c1dabf88f8fa6981df485dcd33951e7e060d99193bb414647953005e6a41a281ae92fc0b106ab1d0c649df7df67a84a19fbe57e78f9cd919f876ceb2600cae60afb4d422a6daeff0f3e8b0010a88b0bbbc353e403115f4fd89be0290c657993caed524b65b9e71e1354e3d251eafbfdfad286e16e963cc476f575b88f39d75d4aba1c508b0202150a22b7c9062f93aabc32cd5ee70b08a94da92dd9c39e7d49fef5343e8730231d1cf3ebfeb1b25dbcfb218b5dde3966565d22eac32cc996110a53c0dc12a9721325df2ce3db03f36341907cd84122d2a897bbd9e35d2c5e2827a496bcc38d982c2bcb25cf055472bf29acc26c66698133800299b78ce48a634e6323bae6db88f7211ad69ebad87c01ba4d11ef6b763ba53395eeaf888db5c71163b7d73cd536771ec8d7e9f205f0bdeac8aecfc6ab04715f93f780868392380ff39e7999c6270e0d3997a8e3c12650b84b34333bb27e91818848e7a01a41dc8f603c55fec0e05524fbd9701f9695c6f1a89a4fcdd01babf46de6fa4f6a1abe626ae1e3fb09ad630ba5ddcea5c34b3d7db938ce41fe53f504783b69bacf58a3153bf960af8e7b92248d686e4891e170074d3b614db88a20b1a58e021253a639e04e495a18eeb9502446d5a60190f1b37f202ec7e975678e6a91bdf5f4bb15cca09c259f6d3ad63214fb72a1b7f6880ae472304e65f140fbfca829ca4ebdea50884189a0e34ec4f80d70eedb8edd7539e615f29015d0a2a3533bddccb3ac3319709615a7747ddf91059fa99e04f1fd1d6961ca462b4ae617c861a104f13758d93a106de1f3ef546d2a731e0019756656b099fc3d53d47386b271558ce7f557490f78816a160b9642303f16a6c56c5783ffc1f2bccf9b1c2dd9095fe89596add52b8cd0f27e17e0870deef8f8bb27bb30aeee2710f187ce6177eff64cfb0a0830d2b2c4424784eafc0f2277e643aa11411f9562c2484149e3ad02c0550338fbbd690aeb6eb7f458981086439d92601f693280eb07e9b8d53730775f628b1eeb9ec29e40e1e83b45a015ec2c7f6a2bea0d0a76c6cd0d4eaa26f5b9d8b15f7ae79a36f878ec6899ac75163090de4856d27388b729d8cb911f2dbaac4a61755208607e4d59a2191a1e8a0f92b395ef84f5cca72650a5ec96711783064e0b3038131aedf028cdd0de22210c7e41132f4dc554c726402bd9867726f671be49f37317ee6760d69c5617b6240f2a0cd8114083dc0a60525f9b94e16a3c61893e7b4cd4f91f030662ac3f32b00e35a3e8598f3df274610f9957ea5be083acd8f25279cda1f89f8d5c05a0a1b202eda0b2b2846b1899e37ea945922422c83846190fed65f9e24351f0569ff47d29f0fa3f89529e14f881940aa1a4841ad144c7c7696ea6ccfc4ea4bb7212653c04f248c459caee04c2e37d3e7dc683dab19dcf65ea2b26c5d7cca848625d7efd0dabbb2426915687ef3abc3b069ec8307c050d038c74ab190645aa0dccb1421c04b258ff3ed12c8db75b58a14309d2e26d73d52eedbfb78a0776ece8ff981a7b04b066d7fd4c3379ef0de749d0f0d4cb2fa9ef181a6337578033e287cd292a492a7d9fee18d21a08545b65da4aa812835303c5d899687963bd441be43ccc703e23483ab9796681a8df92a34ab2b18d397028867323f31f7049deb704611ac9cb12fee16283124c9b2428cf4c007af6be15d7b5031a38fca9de3b791871cc113911378a4ab56d049babdbfe707e94528f5a3767e39df784cef4dd84c2f1fbf6ad21654e556af2b701f4a90b0250c325e831293f5b184cc33dd9a4ad1eb0593269089f225f0ebedf94b11fb6f7be7d859e0cfc3eb14313bfea9686703262927b4b146bd2aac0450d1a61d6759af2ebbd40d8eb989f86399f60a5667de00a28fded06200c29f6eebc1eb9e47664d46dafc3afc47d3a4ea9508c16cc7c0a80dde88a02e592616956b9a090e0d519584e21e33221560cedb330523ef2b7c0ff50acfd146fd4d838c633a57c87c264b49a494c28e364f8ef5c6f3fd011cd6b32a0c2c02d60abc970afe83e55897b2581afb4dc367930ac9fffa440dab2ec982dd410b9287a000b0612d4b154560cd9b564e94c8e2aae0bbccc7de765a416650df2174519caefb7804b23bf526429bb01c70f3480caa10501782733990703005f297ee2051ebb04e3c1df02fa1209f95314f1236c8aa37a21641a3bda5f949d8292a718024bad13583e6c37df502842df6e1ce87aaaaf5e5fd5ea2bbbe56be9111b95f20882e244c3d5cbe120a620326c3cf2852b2233aef66bbc6d20a3bed7394c4f49207a510fd873763e5f933d6ec9552bc5956c7c16e579968778d059dc5d3b95f6009e8c3ed9c8afeb8ee33bc36a209435fca7353b43d7ce49d0b7da67d8c3384a", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000adf8670373d6f0424af5f29cb946f164000000000000000000000000000000008f927aa40772310f80a858405907bb7011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + "proof_hex": "0x00000000000000000000000000000000000000000000000c231c04d197a8e708000000000000000000000000000000000000000000000004dff372f7e0128d790000000000000000000000000000000000000000000000075b49d509002a176d00000000000000000000000000000000000000000000000000013555c49a6d66000000000000000000000000000000000000000000000000e6842811517da5170000000000000000000000000000000000000000000000003669759c5c8027dd000000000000000000000000000000000000000000000005a31742b4a58157240000000000000000000000000000000000000000000000000000e79318c631dd000000000000000000000000000000000000000000000003f855d900f178f79700000000000000000000000000000000000000000000000660c1fafd86f70c7900000000000000000000000000000000000000000000000992e486dc1bb3e6960000000000000000000000000000000000000000000000000000e7f1f964c980000000000000000000000000000000000000000000000004ffb738420181a4ac00000000000000000000000000000000000000000000000f05f675e4f309b2960000000000000000000000000000000000000000000000079ba8c5829d9868b700000000000000000000000000000000000000000000000000011dee0a4299770832f879c803fd7b787b6c5ec4e1e0714f5aae691b0542def35cff3251fdc5b30f8d12e5ee82c4f7d3164b41bb4cdaa80237d61a4cb9e779f1462a241f80f1ac1cd23d6f8490793bee73dc357175a2b9ebeac1e5975a21e290d819a4cf1d9ff01eb1cbb093ecc02906356b0797a42cb304014e505f70f1190991810dc812e176297a3c0bad21939a9b1cb32dd6b82db4643d1eb8aec6fbc56dd93fde0996960c1cbdda9ab25913b46b163d4a332e799d201d1cdba04297f438ea4a4efd17ba1a289f4af62ac367ef7ad3197f26868c463bc7f7be75ebcd1f4cb0d8af9ff494a9183660f437d662a4eb7505bdf9d193fcf0c08a95e9fa826b850ae8419aab4e8a25f55283723fcc95ac9eacfe4409b460a0d1d57a6d77f7b524c405002c208fe520bbe73a3e2f784dc967fc117d7f6d480ac9960bad15f20b7ef9103bac9795b123787fc7493bb5e8dd91740730cc2c2acff5fc0b376938de79e664adfa8b9c1f2964d0f06e4c92e958f5fd77a5a49f0d4158de25b55f4a022794d66fcf0d66b61c71274729412aa51cfd6caa29c854f02b0f6381c8969e2927833619fbaea251280a6dfb0c2098a8eeb87c9606c13dd2fdbadee2822d0d474c2c5b2d29a7db431a3377b08f5f1ca652302d0ec96d751aa03a143ec73e27fc7287749e48007f121d76c44c0f508cd42146d4c85da1ba7f0079d44f7ec8d86385facddd56ec7d3b2c2310d8d4896a745ebf3284b67d7dfe6a70bf912feda40d6a7d735e2c6917ed0c4d75fcecb16b258dbfa38b206c4390536979a9fcee7ecbad671b527245fa30194e070f0536b206f4728be22149c5043f9162a2676b1cbbf5970efdc3f09adf15c02013d398517db992673bca32c394646855203b7e50f25af4e859fae1186d10b2529864fb2290e037275f80785700c29a8df6aedbd6742ba5ea46d6b7452909274fcca2b7ab41d0684c1cadebaf11593d6765db6e88be19eaca96735db527250a9d92a04f51e65149dd9b772f2e4c77ef2504a92b0ae5ef7216de2dcff50419e13a3bffdd680ec1c60b461d72df65afac17828feeda81b9a2441f16480b821b5d909d1c8242c008e6b4ad1af1d798bb41a68c767c9b3a45a60871364310b517d0323cd8922d5db1fd5bc98f56ae03e83eadbd0a64b1a960057fecf780d189106993800da22c6d624fcbb23dc2031f3bcf55b2350b901fa3b6c9598cea064913c140a7440455db00bed4312b796f57a058c03446d58c84672705a1af9a15980262ddcce5e03fa8737cbb8d9b59edfb4bb0376593116b6acf935b6ed928d1b7039574bb7faa48a2e45b9abd1b95ce656a2c3fcad52e50644ab4f430b092950713a9be10309a139e4114960f59fd48470ac69c1d3eb77b9ac02e1be09eb1eb571345da560337d679e98eb65ec13e231b25ace4b4f4327c012879a4eff8752252073bc94743f68c6488c2659ed13bc4777e9b31d819d3528d12989616b713b2a21f4561994e2b294dd8451f4d34e7f17f37b31c7659e8ccda79528830ffb2d8600f7028bdc8cc1f247db1c996a7c683e4ba8a621111c4bf18c193aa299e54a818290a2fa13fc299ed1c9655ffaa435d5581e27e321594d33c84721e01e4c976fa2f83abf8d7ad3101c5575de5ed03233a502c87ceeef5c8ac9c4ae7ce55e5a12702c4a8dc4290c18ed54297509ae69fc6e10116bab06916be62c1b66d4c31452e28780558518a9a0677de43d2381aa22d4cb1e05b371a0abb3474990c81e0045a1010f281874e4dc7bf8f4c266bb74d4de791f9444d6a979df2930d4ce8989d5f134d2b517b71ff043acc1e77ec06863b770df40820833ee29309e6d5940c98e20a46b2e7ab26ed6e58880c0c757d00d9e298b2ff637f68e3e0ae78d362684ac71f4ef526adc5a9362aa12348d54dad58410e69e9998c043ef9cbb9ca457f367e2b540b26156b565d77217132ab2cda06e373b5c8e42fb1c549218db94a975eaa04925195341bf71bb44848f66b754f8844df69caafefacf5a80fb7c48d3541b52a54d7f1ff24a7da4cc29a6ca8c63a6bf1f142faa1da9ffb6cbf576a74f1665a2f6cb8998e325cffedcfb1ff8ae10ddbf30553384be2327852ef6cbddbb80c031612944a6367ecc5ddec0bbdb492409ea8ee0b749023820855af207f2771efe415357d2e65146970c4e134ae290cfa6c78765c9f93f822f7442f38be924feed3075d0f4c505e8f412cd52430157769957d983cf833076ac64ff6cb3089a5c23e2a4b7311a60385b29b440ff083a03357b30a7ce34c506b724f7491d6397a5b220bfbb4078dde8510b9516907fc0e26a5249af99436db17ddf3e08b63330a4e1f2fdc4166cb2192df0fd7da156ff33c342789ebbaa8412c362670a8e1d1e3660d04c8ee09dba54e2480700559aaf372ac10b3fa798957ca140d1293c4ab7fde740b5485945480745e0cf536eb74a1b75abf1f942da1cc613621de9fe86c60ab972f724566c306515796c892b36d522b92c2c481fc29f1e711953987999e3898ae038f4fe31d52d3d91f94e01fbbb0d0101568ef1b2d02bc0ef78877e5a9e51cee134025275cd9aa251def979540cc5bc9963016fd04a56f42ea0c384d20bcb51b2815de4721915d31b83934288813e0914eb9388cd661dab0fedae3594e84ea0519ce06222d0c01325eadb2239c4281880b62af0913eb476a3e57fee4ef2564fa14495d5aae740b0a554e1b0155f6451c2bc68e136ef1b76a6d538cfdbbd17c5c2d8ea0571306bfc84903b7de1030e1596d801b8faa51635ffcf1a4c40b9b070f2fa3fa1053239ff9320d966969c616127cd2fc20a65986161a03cd6c3941aef31341535b0568b2a713c86a642f4da67f3141252ab971636722ed12adb2a793c72adddf418e8586058ee51dc6f148ac40c0c26dfb144bbcf5d307c72968ac60f7257b9964070a64942d647ee644678caca09ce457f2e613fbc10526152798a5f624546f7fc22bd2801f4aa585d2483b8472a4b3f5af24b3dd476e671d3cc2a52f05bdd9fb07430c3cc97d6bfb0c577b2f9e8e50d6e1fe74089ebe128aa657e68926183d6cd87dd0ebd530920013a0a301d505c47262a9edebe462a0ac28c6cd2610b59a1787db42188287610d1cedb9df8ccb0c33e846116c2d571ab84660a728198a7806104f6e505519e52bff03fb9eb0a00256556355c4cd7dec17af7147dc071beee6a5686d1ba5a185809baf446d80e20cac33cb693da5da0b962d2bd19e017661aa136cf73ed71baf62505bd5afa4d39bdd0684b582ee6e6f4b7c6137a610b8a5f142ac1cbce5c02b9d078a10b567ecdeea8eac300fbcbff1d8f92d75440396973f60f9c313f2fc46ac28340d60ce96317c2b1fa08c2604b0d939d0d18b1152f44f71949e022b665a3915226bd97337b1873de651fe90f3e70a3cd806f21f82d88ec05123bfe13d32b3fad2e2f874ce2a4ca42ce4276d95b216d0d11aff23e9053c45f28d1ca75a522049373683eb011b3fe4eb644b68274fd6361bea130bf786cc75187bfbff53c6c8809b4ee3962dd87ec04aadd8440848869d77361924a910d6f25dce1bb4afcd04c6d823db6e94f29baba17e10f598331f4db5492a2622397ea1c9c156db10797fd4570223efd35c0fd70bda70794e5a28ff326d03194c82df5490a622bd475ec3b6423cb73dba4eea28b6cb978b7efe60b0da77f31c2adae631cc4fe8ecd22f605c974bb849132a4b3339ce8e68c7a6578a3acde71a7a2c7fe23193fafacc18024a9313cf0b126c6d7c58f503803adbb09809b6090818993e81b3dbc750cbe9ccd56a01cc9266bff7289e2a08a14124af6bff64d9173b277e654acd6db771e75b5cc586e8d8153aa7ab685032632cce7af6e17e7d1f8f30910d5e7a54c8b1972efed7d2f5e1f6aa76bdcba76057cb941950523f532d02800f31fe4bd26167857004a22b83585847bb2b97b487d08a0a164c301c610ed2a41c62ade87f7ea3e1376bfe73a07e8a98e7a68a0e59fadc1beaf37084b302ffc3df53fdfa8cfe80b5a0427f8024d1450f11fa7f2747efb3d30c0b140fd31bc3a87fa0baa6219996c7ba23057aed724685d8806b849f935bb32eef4a605605a81dcf2249ee73ea621b56f520e32aa0b6a3d93d13f29aa0b933361b7b4bc415b2342befe6057b29f81986d412861cdf37c13ce9e02f395067e17eb426edba05ee1ad79588ad90e65a4da63c3a0589d6d7c393c35e1f9c6026cd599c3df53c2f63ca034855057d89de7932ed12821db83c79ff9010773cae04fec7b850ae3f04951f33d5cbe2f4a05d9b5e3369785b1180c4b645c13a811156e17872ab15420d6095cbc95077e025a6ae3cee3fbcf97a3b552e6f10b2903ed11f88f60b4f3d1923b42010f679c50ea946e9ea8a8332e59ddcfa2f9d68b260fbc8b81452adf41022900746861cf9b59b1719e2eef8e882e75922a6be8df309d8694d5ee46d6a10e370ba867c3128ca6dc7883414ad7f3a6b3b01479f965eaa0247cf84938dab303d0942d69328e413e0c5818e8b0d4f7ce1bcc11f65e539de2ed2e3fbb46e4e25a36903fd8576aba8efc706cf8722171a2fecffd79a415a3d3645924c755f8817e4cf0a30e0203e5c32c990022b3ff687cef3c789d018875fe44ad11240af3a1ad14160b802d382e8f576b579de16820563c4e212cf36752246772bd379a5110ba9f0b8e02469515c2c9676c859356e0941689efcdb6a2241d0ee46b580810410afd610f1ee5c4d2965d64153350490ca842b210984ed286463eb79bcddeff41cde9fa4367f246608dfec52ffa6ee6bf08df305c33e96e22593896ffc1e36230ca2aca0629659dc3571f0be7e809e9a510ec9a7f0c1723d69c985600f3833bc02fe173deaa04c41f9b5b1f83ea37d42474dbc81b94c81b05a64581aa57213cc13165676762c6ad6b51ffb3a73f1f10060358b19de35c6d4babbf829b224e95303d69362694e0ac29cc0fba636fb26473b14fc238ac027dd77f8b677693a5a571af4105d307cf0d0696d37f321995ab288e77ce09f4ddc73a252c300d4a00c981b554dacf0415b8da77fa1c4dc28ed4dde3a3573c6c3e7df3f2390d98087ba1d2a97806f0e9fc3c1a16c64c35704079f146cffaea0ef7314b90d21667bf209570875505253493deac384d5044c00e94d50a15a130f8362084459e05f3bf69c982a364cd3bec37ee44649738d102dce02eae15c9133963dc6f00af8027b41704b1e65d7c2c3c445743491863faacee7a449739fa7c5faef8f6fa8091c326f1fd12a2320f689d0be3d099247858f481ff84a1c3a760f54fad6eaaa7461972bb5b1241c2e95617f5eb5ec64c536fd9a884a1d21e74b17d1b6fedfd6524f370ba6b023f770d9877a3968948bb8622d649578f5c39217b8ddbd83c42a9f826848f6ec2c4a78791b282fc5551bad0e1b3f95f63b2ea587b46c4e4b4602fdc1f24824910a59a9a3b3f2ad2b6058ec542fc2b40bf67fe3ea4c4ceec0f171a1736b0c63d901bc7465c249d7b191878584f5254636ad94c076ecdc7402eaa26be71dde4051302af10ab5291187ef10822091e574ead9269789d6132639c25627efd71702202d180e2caa6d96a03e8f02ee3139ba30e989586cab693a0a79e12fd162c12d432407c683bf99ae6a2cda59cf6c90ad591ceb18a99e3c1148215b36b2fb3e39f00486f2ed2caf1477061cb1684be5c51bcfb19901b9c02ec447beb24078171aaa2f64680388b8bb597288b662a0382edf3c14ac070737e86726b6d12c7c500ce72d3480a05120092fdbd1229001fb4fb81231535b9653878afdc3addff421963903786aec552940e39d9ff33460f74a33387f9c70dd4066697c5f2bbfd4c2efb11b86d2cb5f3fc4af8ceea1a93a1103922e43dd160bbdb8f161bafcc7fd31f8191505db967d01b7bf5e5c929ef3469c08f817fd7c7ec78261a5cf10d0c06f43810c2ef26ef7f2e9336ff587f6f8900096de08d6bf739f6313594db9443ec342c30fac34260f3c79034b64928e3081c1ee74b0fff1cd01f9d148601c02d6e482551800e080c9c229d59589ed97ca4af09a2608a599ccb54c19aabf16486ff4db460e7e1472fc208c2ca3ed2a948a06ce66ef010f7172df7bd2831fd060cc233dce03955bb32d144cebd97bca622b5ee2f2e6e8399a2adb6f538bd41181201b029c2f796924efa294745ad0c575cf542fb7a763c43db2cc32ba8db6f775b99b295020596ab671b0038e64f5606a8fa8eafbd10d29ab148e907904f73da636d63ad40a4c6396efab8297934902c7cb657d39c8dcf44eb4cb3a86c795841b0f93be581de8532c7577ec52f4a12537a9956b0ac212c03cf336c0bcf99dbd3b892e45ed2d2a71dc9d826fb9cdd91d43408791e5ec56fa2fba832774e589084086303a0c16ff05c6f5780fa20a52820469bf3b8978a7ed7937b0217479d86ceb9cbc90ac1117c686fe10d0a97288182e9ca7ac5e47384be383b7cc53854d7a5505c6dc000ba57d3623d8d3f995c14f756832ff184559ec8d627221a65026e964e2eb36aa09c11f60eee4a48a5a59465eb6449bde6db4fa670458d925591318b9082c5f0f0257ae6d8b494bc66a2443dead3e49bb7bed337edf061f17c49b264641fb59dd26984407e9accc23b4d11adcb0c10651e75e05ed573edc971df97827ef712b551513e963428dcb136b795ffc909f9c259347fc67ca0cf5d367fdb41d830d91ab02366b303321eb6971795d674b16d05de722e238f9f8dcbbbdf82cdf46e2121222aaa6f521143bda9fabc2237e2fef019f237d6fa666acad9a65f44943d549b515e85e088d8e3ec42b92d49a3994bdbc03ff1b0073d63c0ec74b9bd5008c4fa424585a1fc5b5b5ec1f6ff4d7ba9e1d0708efece885a50bfac097d50b0a8a450527f3d7df5039a8c1b83d0a7c217819cd3fd158d79a679145885730d2da273e422401b4c932c33726c20c3a350f4141a5665d3508cca37ead4b34b4ba27cbc6b9132dcaaf7c522f40b88edb47f434de442846a765c2fa47788196fc9df7c83f5b29ee831a1f7284e6f4e3c133df7d9e54e3ec5e0a7cb3127a1374c12ff4d616de163ab2749853ee6c337caaaf97084d29d86d7fa4743b52d7e37a4689f4a754c42ab7dd83f960a3e90114e53473f486132ececf39a29995d2a8174ae2b547852f2f30d4bacce67216597d1124fbd87ecca5dde59c0f22cfc7c6322f740a909aeb0a973c5b14f33f2054698e7e03e338769b7919224c638db7924a8245e308a43b1f85d7e406c8446e58fc361467f1c6cc21d378faab8ee790ace260454e153e6210807cd049974e1ef0c1cb56c32ba83314f282a98d1153cf2af5665eaeeb6a16139a127f61b33fd3faabb69d6cfa40f9329a58331ab431d59240d8bc2e966fda22d7e1eb4bceb911e8df376d4c050cf65dc124dc5faa21af8758a079d7c0b85b02b6c0ba4355df267525d12e8205f3c8eecd10356464704c7cace51d0a81e9640bc6d63255d3e7e5ec844e84a5230a07bb33c79585fd8267b76abe7150a786a21fd35591ff3238fca651f2d59cc90eeed3ba2253efc0f567ba0f8aba737194af1d11a2a63c20be531387940968da873cd43bd7dc03e3c1d3bc12aa2fbe99ada21e20e2b14daa35699764599763ce95a5506581bc494b271f865932bebec143ca0e6358610510cf53ad2fe1d5f61ad1d24df406b44656c61ed6e7205445faf3a918d391f98dd546d7803485b78a225a4ef29ff7599a16d288af013a297784c0b02a6fbd947ad57219cc3df3cdbf0f7d59e36ce25c95edeaf6245753f62932bd9502cac9c21e636fbcac7c2987640ecc38ada9a81278c7de458bfb76216033d8d313c337b1a163a73ddc3140db57161a4a3fd173ed6b24ffd66be49ff72c6c40dd0fb35f02e279f9f385ab009f147da9aef912956e37c57262166bef70d27b677f103e54368d3cbf57152e241f21a7a3ce73647df2544af9ccd49dd58ce80b31e8192a528fd130ff6b38f3bc4f495bda106ed59c6f3f0fc39c0395b4497dbd5eac0c6b1abff83546d63b037b05b68ef47b5f6b5d8b0436d38f372d731d6fd0ec472f39308f03dd5783ae424871ffc058ac46f5c126810a51504fea799030db97c6077deacd4c8da49de813a7dd9ebd434f32490cff962a047a55647a095a8279760d4eef1aa7a2eea2c7b8d80f82019b08b4e788b538de74f2ba002d414dd6abfe206033b26f0a38b1b88a80fdcbc1e8ab9f37d0de3ee6375c07915af54a986b712dd8a4a75a710c535434b496e4bdcecb0ea8f8ff48287705b3f579b2aff304930528e7d18eb79f7043c9a1aaa1e46dbd5fb9dc284e2b867bfa2c371bcfa0cff8246c4fe2f51460bde5647596fa9e4a8c6e38d838c8e8467b5a684cd45963b5cb04c2fc702e846d4eaeb30abf70328cc7145e757144d261a89ae51b5fc2ed58f01d1ac594165c1ddbed15007d392ab654909ba10443116328aba7c8af8b09265b27d7c53374a87bf6f98d28f04592ec3f7d613a50ef183784e7ad894d47ae991c05ac8a35b6d4307f7931256358077480d2eb99142c278440fd800d9fb8578bbc1af8eab305ee037541e604da2ed341479186d828627024d4f1363067737ee3bd0fc6ae2f1b556ff69d05c598a92d68f6d7953788aa43e8aeb4971ed7bab3fa7b079e231f452192c2b92e2cb0e2305eea4e2f2641a55f50be480beaaf829daa8a2c89801f65112458cd3ae535785e6b84f4430210b454a30372b14fda4e3393772d720d07044a0a99f8f8e247d152f8ec470c6eadf946e52499a5083db06d3bf62b632db82ab142bcc95accecaafa757e9b56640b1f106678cf8109554c8d80be21895139752ea939af20954934cacf705c99630dffe4254f701420309bfae9111a6f243f7fd657036f373dc43c281335fb1a17b843e61da43093df3be32aa0121b61059d9259d23088afb4baf936b92eee94156781c4e381c76c62791f879c0d266b33cff5acabb6b7dbc81d67a22cc97a976aa798d5eb86beaa21da26ef726617683a10d48af74af941aee5b015c31e7c4fb0af21f474a190944e145bea17c727ec1f4de4e389fed639b14cf173b7419caae9c3c33deb11806963024e14107520e3a6f816911548b98f74c0fc5eb6f384807561e18dd0e417c96a062e12453e0515c4ad0a7155e54724b197e6e200e3b61df77b12f853c8f0192e0b38e8bf8b1fc9f87ecfb8749f4651d29c764148128cd0172f37c559032cb1eaf080664e3f167a19ffde30c1c79b7e3e44f715b4b20d5e143ca7715f0300d4e7ee2435c23705f6c29d34d4d9bee5da3e6ac50d25ed57adfb31c71777520c147b0d110f8ba0183e296f88dc0d7dd30cc8c1a552e20d3484781fa7bffe789cfbfbc38252f357273d99464757ad98c4bd83924a89fcb690c3548c35bf6cc93722206b8aa7b998059dbfaf9b597f883df0a4e79ea7a87586c53949d42296315f262e19eb4443541e591182121a4d53c9b009bac47744cd2d655db9645f3b31b46fd1cf213cbe7416a3520270f458d7ec5604157876a5b81be998f854666815e1ed6792252bb2da0ab85a4b49874fbdf69d9ae78e895a82d02855d0025bf8ea19d5a7f67b63a52e0c627bad904e362e90ef868308717e4d00895153f9ba4f20568c3344e505d88b0efaf03ab4c593750c40c438314e010f8aea4aa22933f64f38aae2ac56c5b00c222ea879ad9a723dab3e3324fe8ce6e68b54f9b838a2e265d2a81d091f43a97b1218727895e7980462b651160b79beac0207bfa5c127701088140fdc4741ae4f270a6d0861d29d825910ec8efef266852ad117e8bfb72c4827be08ecdab3ab21214b427bc61b5bd42ff1f6e84198732bab6b043d8eaca51e94815db13c22a5f2178c97d3e67bb633138012c8f19e6edca36f28207be6fd25feb47eacd3f7300a12ffd16717ae1f87b30572f78fe8e9f6c0285864a19d52ef1416c6bafd296ee5274e16e37cb37acb075b842be01f7f577d430667f65154b11070b70437937270300c53983a317d20ef5892fa22fa5ff721fb9be17da55e2f82a64f5a3febf7192ca47d3122f9be516f7a7b8c2c02d0bfd706b6ca47de944ae10e732351daea2a031e416a38ddb62e1af22d03e222ca180a8b69288c999f3b851f5ed999b2a0a61fa27837d952cd8421cbc240d987a37155a8c1e5e1f4eb7c49aff109ac16af6d0784f3728889808e1391e69b25e095af7a018c8a81f1cefb60aee7375879697e14388cbf84fd159c7a4e1882c56bfb3bb5b5b9ac8db66038a0257e777fe0c435090aa91cb05b93a1fe40b21f5a9b40143eb498fb7e030e98a4b9d491ef20ae3a148193295c2c9dfd0c148b0d6e84fd8eb8edcd5ce1337a1998e78ab217561406166e870a1ff737d0401591c5a5b2f4da40bc19a0f246793f62b04bc5df66d6020970535a0d0e97b797d44e58f47891b11c30b5ab101d43e6413f3e5c048781910a1f8ef5c1f90a0c2f60909f5b5109b80e03613cd84103eaec0ad5cad363665e2234e51f0eacdc42f1dc6c74fb844fbf0d7e2ad23e9574946fdb9d7a3afd10910881c68d7025845e58e84554285ac330b2317fe4ea446d6917f98d15e362e1481d3d6a5c2067ff2ba2cd17fd68cd5ad79e440ec065e883123cb3950f2ac4b6480db142b1f16d08c353e2f557be1dc2ff5359e80e8efaf31772170cbdc1deb54d1ea5312f9e915c83584235ed85109d4a59bd9e028c5f44eaae735386f684a6f607ed78682cab8beb171780c9a54e3f57a392378b6051bd47cc4104dc4678c74f0300f5276b1f61679e9c481b80823bfe4fbae5082026c4996e61a319bc1274b00ea1dcf79295bd3c34f1b1fdb77357dea3955457b33db3393905ea35deb49616071ec289dae2a241d2d7715964b2db700909956a819439b1e04a7d1f922043f204570c529d1220e1c5c3a83350978b08bb3d65d6844cc1e615d634f5ae768f6e1ae5775ac84325b9a3c2976dea264f8bf3d7a1e14c31b9cf8d6c4855fc51c750042533b8db0f0ccb4a24085cc029702b9432137bcc39c18a492ce1e62f3c2393070a3d2b973aae4f5dafb491b7ae5e36230fe2c376a48a66f5d516352c8ae01c0cc43121e8b8010835398c313fce1f959c0911058646e807b1640cadac36369f203610cd91f6f96552cc2d9b85d3ed3dfee006928fc67518977a68ba6cb35135164cfec4ff35ae655e66f1586f933e88c2874350c6091f90a682e32c02690ad219264c0c4e73ad397e314da27dd9fc97d941bc619e13826ccb19f58d0651b693270214a4270e230bed23fd16705e90ffb3c8b9b7d9cfe03ee2a094a9e9b2909f2678bcdf76a943c56ca03fa8189eb05a15b10840e4f83e7ceaffbbf07fa822851245572d390a62945618a0a04cf1b830df30e13a4bafc8414b13ffacf3488a9725353f07f6bddca341c57e2a1d437a0fbba07c4e5bb31fa6c41d9d71fe038e6f25ad9aaca4bf67db38dc232dca80ac6cc577f2fe237614c0987331d8569772871276ebbcd824e2d6fdd1adbcedd9e14db8c44abec004b0ac587962949f41b1800c781b7221b31e4e3a02d40e9b2c1b7ea3b73f180ba5b1b27ef069b6f05961800d662205d6e426c30f893bd0cff9b606a3704ddc4fda64f149a583b5e2b1ec5012b58b7e12f4ea84f4f9ab698e625d09d85e8ab36100d477be566bd3aea6264500cb6669de3a9bbcfb18d17dd15a9d6827277fa1a6c57b83cc7aeb2965222df40929e9d749b07a43e36393a92da64323ff33bd3a3cabe072a929c29847450a431c403bf371e626651430d76e2f0f1142ce5ca04d5211a40a3eb5b522492293041a356d17586f9c2a30628c4bdaf8f93868e51faa16f6da0e030dca9a4952eb1c14219210c228064f8846d40e9368ffc5b17c70dceb3c4f13b9e42dca06c872ce0f6cfe8b8d14533b480f908e52f254f91ed84793aaf219683c7d271ab2778dfd30099fcfc1e4ae79674ebce5a1315298c221404b418707036cc69bab53c5313800008570b8cf57caf6020c9829afa07666b66fa1ebda11ced87b18a109dcb1b41aba076719214db7fc4b5ff92778ca481e89f912fe88dcd39ff7794fef20cd9520d52821d92a633a17d70485634a0541c0272f020df6f58af977b0e91d6a4bcf03ae248c091081ccc4e8fffc6a050e05357240b08c0ff78548a041239002452a0ca3dfe850ce5c5ba424344467ed464b57e098456fe57a2ed16b1317685f26ff0f5ed7b176e08e4d9de99f5b344a7d3dd3b0bcb4effca2df187a117c576443e01dd6eb7912779095f7972c7414cf7b72ca7904ef8364f53db792e6cea4abd3422d840981612e99fdd97f2f4389914f0f78cffa1d95e815ebabfac5dce2ecf8412a99be58b4cf34ac6f91f5b4a1dffd405bb782f6189d26f468585fd50761a8a52df5ed597fde9811dfb1d21346499c99146b00cdbb6354a8118f558295fb716b2a9723c61eb8b1427d6bca3db42ac998223f93b5760ce1e263fd58603f18e55a0d532e7cdf42645bdb8cad58eb88dd52f2349c626f08d2426a383deacbde9e251aa2b56f08f371c6d6917381ea4241973ccf1afafadf9e78745d163f0de52db4141c05e1a20ffa115820d0c5d3a1ee1825490c446493fd2ebc52cc34783c5b0304d04162c1609e7340f3c6559cdaef78739a3ecc0e1fdebfc1bd4aa2cdd31d3808275192c8e3cc7921c1fa351a66d0d8f43bb8bce2999756a00a2276d0af4f141934be7f31bde374351e50ccacc048284f6f6ef77f9ffc3c3a7d9f65b4d4b7db1e6e30581acdb9fbf6832fe4e12149b397f857fe633b733e77628b0bda39db6e16572f317cb53137f8dc0c054841faa1c0f6752757873ad0b2c78d29d619ff9d0832d2e39f7cc552cd4203cbb6ba9455ed305398629287b88486b0b914d0be2d1440fddccbdfadec77be5340a0f75035752c4ad485a9ce28f18d0896d358c1560f1faebaba5440acaa383fe25a1a1849a29a56a42d8fba183191983b101a5ac221dca03aedb73584a7be6415ff937bc6bcff8fe8595760af12db068187f52e571d88580aacd45b8723a5c0fb5609f14c5eba1afd3f40f5a164ee4b97a98a72c82150e3ac2ee4314561df798eb3c3a573dcc6aa766f26a84eb7b6e5584c3243911c09193d51120216d2e246fa2edff374a80c1932c39acf41c3e3e040b38373df1e28db8d4179c2cd8efd93091b611d7f70648e832a9301d931c14765a87bb97009b8844ca5d4bfb2ad0934927e11da2fd9832690b28b45793cdb1b80d9b6fe68273abded42460ce3d60f075af10d465818244599cede90585ac63b43c08d7080219e49ddf2bd21ef6119cb9d8aa31cc1f8141373ea1f9d490f15be7ab965c5ad290845de60a58fc9ca397be4eb86efaa128990d4f4441c3445a59e608a666bd10c0c0ce65aeca5e959fd32eafc6212421fcecbd3f10d6ea744d0f65cb21f162b23d804a8d20d2873309f646f8128fb9c9fcd02cca58845c5ec1ae55ed669bc891792260fd1d7b5ea3c3a790494b73f071fa0cd35624889e52c965e136c215e142cd89743e54a6b3988ef9852ca79e456ca5cc1afd24eade12ecb3b27ba89cf1725bdd58395c8594e6a8a01e4b388f76d54d35096af2a521220e268b0dd633f28205bfa81ec110c18221ca77e9bd5bf3f6d090e7b3c925c89ad60d4bd9abb694402a7ebea4ee8be2ab21b00146f7033fb36ee7c6dbcfa20b80d70e2291b46bc8d2761d2dc0d29d23d0cc7a58873bca8114808ac811fc3f45b25cf41787065af9101d6d2ce542984b742c097b92e85ff17d61ae6fd69c597b7dde659a2ea1c201f18aeea00abc08ad5a9d9acafb5316d1054655ec5a02bb2434946b584af7271ca1629090a0cbdf2fe152faf22486825550d29bd8b08899aa562b391cd47deedb726ba0d95a1020b5be53a375ca0c98024b3a961af37170f6b40cb58186c8ad79724ef8fc6d60b44f0de34aa1146f6933f964afc553538f3b2a2b7dc17a83c58701e902854b59f2a7ebe132fb19ae003a24a693386b7fa4d0f7f589542cc6c1c702f9f592898c4de2f94cd9a689da1f0855dd1d29def7e48a900fb989eb7df340e20a9ad75ccc9711d53d4142f02b635db1ad3458617e50c0f91ff6e64858b669909e40555073b0af9e9766e94492a6c509d936e6e678b19ce25fd5aad8a9f9dcb301dca4dd5e47a124900d4ba230002188ffd37f98c29cb0e2f7bd055376b4df01380a76e5ca5017b8b97d887490f494780633d00defdac3346cbce89045a83660e56d2e8a6d8b47211642faca1883a83c23045124d490d982c1218dea8f05e340f95ca48916da15d07fa5f923c3da0c890e36b26c55ffa2f8693485e5b4c67fc29b3be5768259f9af7c17eca51c8ca09d65fa1fb21db1f7c5a8c545b1cf8f78e1c14a5648cd566753b73c05c7b55c1af4005899b442e53f08669ac88dfc0eefc2e5b96c76ae4f5459241fd870544832c2a4be2954852b47df941e647bf7f914e1ddd6f241b5123b5f87aa912ff5c35d8aa7d645b100f1d409338cd9280a0c65515e673654f8fb5de1da42fb7ea5fc5cf1f5afdc4591f7f22f759fd8a9ef709230bddb28064cb8fe3b9a8806b987b14d8e58aaa13c86396832da0d2c440ff95450d3be24a3d6c7c976bc5450ef350a98f129882f8b372c714efacf7a9382285d6046fd45168becff5d64fbf5bdc12073731dabf923bdcab04f43de20c8e95fb66", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000fc4a275557be3acc7e37f1dd383752c0000000000000000000000000000000009800ccaf777e6cc629fb0e8d8ad1652511521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" }, "decryption_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000001f3e77d68ed5db4ee000000000000000000000000000000000000000000000002824efb1dd50dc53d000000000000000000000000000000000000000000000001e287c5b545301ce400000000000000000000000000000000000000000000000000003ac30f94551900000000000000000000000000000000000000000000000425ae327975b900880000000000000000000000000000000000000000000000030450cf304ee5f67d00000000000000000000000000000000000000000000000ddf49ff2fcefc54550000000000000000000000000000000000000000000000000001e6a4e87b9a2000000000000000000000000000000000000000000000000373d5ff3b6917377d00000000000000000000000000000000000000000000000ae9b0b68e3252781a0000000000000000000000000000000000000000000000078bae91658ec6a2b20000000000000000000000000000000000000000000000000001edde5cf5eeec0000000000000000000000000000000000000000000000024db0e0b9187cf4460000000000000000000000000000000000000000000000061da17c01b0ec38f8000000000000000000000000000000000000000000000007228437c29b55d1c600000000000000000000000000000000000000000000000000013d3baa2598e82a7f1abbbf32e6984fcb0010b83c6c7844ea6ca3fd0182ed6cfe12534a9a1ab32d95760c1b06880b626933c5156327a93ae0c04616b6f2cfd6457c4dfeb0b88f115d8837813343413efdc6428b79367f2cea1f6fa8b8d3ec221fa34eb49abfe209fe2bb0aeacf24470fe359956c7a9d3196c4d59810478f5e6c219a0a1ce604b02dc0bd84807a39b811417bd98322a228ff199aab7bf5543ac55c50a2b6509a607be7abd06b0d150fb024b43f0a0264f8f2a2258386a1e23c891c2cb0f8bc84212bdf036a748a87c8949b4fbf035079fe8b10ebd8c3b4e7f3ff7033fab3de191217123f39e02abc543c57376b6a015041fc623668c649686560fe0fdc1def2e6251b41df3fe84d67e48c15e23ddb2a83225f0edceb2c59758b5bd5faa1af92831ebb797fa8d6559e379f0bd0cc77e360b78c17a1e131017d4ecabdc2ee16ad021a31399184a8a78a5ff3667d4ba9ff5ca43d76c39055e64fe36d15d8be71fbf62733a4eed75dd54386c0915aa949a3ed5710627443b8ede3bffcee932e416b52251aae03e2369a82a51fe16dad9fef2b0e96efdcabd3322c1483aac4f532a1171401989403a42325d2afad1973d1a723680a168d3ae569995f8a97a7aecd777e19cde1b4ff4744fb82c63a6d990d2185ff7b003862b25776bd65a5ed0e414fdb227c255f71b15ae0c6933ed5702b3e729e7f826a590387398b121e06df511d5514fac1e05b458c10dfe141aa13ebf0647e24c0765eca7a633a6d82c8cdbf1e281308f82f1605ab9e282cf3731ea329850e8463f81d274c6f12c703ee4027535a287eccb7bc38c8523ec425377fb9e706a8c53c138c98c517ceabc0497f3845672c3b9942b4ec1692c479a8dfa18e55c4ad68c6519198b612a79177ef62c1ddc02d7842bb16583370aebcfa9a055bb2811bb27866c4f05557866b524f27f1e17503d9ab172ac8130eeb870cc0a647bdd39634c84b59cdf975ac0a773c6cbb151c1d126e82ef43f98e082640c22339e906c342e788aaafac7bf0f644097be2628e0232986c6d79f5ce13059c444f38ac33d416a25009f2dc0f86de79f54885b0510a86856cfb4965ef34d9bcf19bd9ab16fbea080afde613668d967b25fb3bc82707f9ec76cf626377bef45c4a0d2568adba49e805e59649f0695412b947af0a520131e9eaacec50553d90ccbca23fa3df01c8d236b1c156382732390af2085e4c0d7b8c85c65f3aab8fab763cec04cdaed0cf90145a204081c1ed3d483732a4ed13d9ffb221838c5f0d8ebc8c9aa98357bcf7fe1c7702f89b83bf5dbea988767c2ef522f59abf597777d99e620b501d519bc981fafb76042b453d1a7167e91a2e1c2b5edd2429647eb7b408c757e9a3c1c19c49bc6401f10816c3ca20a8d2768e19838e2be7eee84a64b232d47264296afebd614484dc8d735a5d28ddcc2a0c840edc31f87870ac41ada9eae239427acad4010ade9baa676779c222f23ebf5245235875eeb04b748a32a4aa8d9ca4cdfe03450077b68715308644eeb2cb1a68f1006d6d41a8e984f8e1114350487c1587996fbfaa19d2e60cb1175558e6a0292c0ed5ff0a5b855c452584fa8af47335737158637c2efd534da21b3853bf89f9242f9c279f94e8e47a25cb8df25ad1787142eea7c1e64d248709147b370f9fb54e25281994695e79395c7c6a8f9506b17f11c9e84beb4ca71f547cc5a24b737c1d139df5ef48dd868001c3ee1213fc3d145b8a0f1a4df3ba5f3b84041faad9c38729836a3cf695c826bada884a9b2765cd2dc3c519a142fe6dce7c61ff3c35b14e1d0f88c8c1042161b3fa888a7f10bd499c9fc4cec04fda3ec346eecfe4b8e64117ba207695a64301d974e223e8613159d26d2466c0bf49ee276f880a715f536b1252d850fc51c0b123ea14e1ee08e305ff2af518b23394984fced28b0d25ed1d053723691e73f00810db57dc33e7668cfa7ee8ea82dfb08ae1157a7ffab67bdc1e65810fab2b117de99f771fe66e33edd6842ab29ee28449a3a711513839b6c425cda1c407664580d3e7c4a68849d1c986366e3ac00ade2de89dc36065bdca0b272f4c4f8230570158f2d1d5f1131d7d7dc9abbd6be93171ab7bc99ad78497702925a81cae48f544226832ca5cf1cd5f97732b9537d9237258c8b8511f10723f057859047d53d2b70ffd0049c6ec0514ec267512b9c08a80a91d5d597b51c57e069c3a0203febcb8ece93238af70b491e7738dcb4fa26835c6dc843869122dfe09dc4f1773676441f3237c9b4417010f6a26a914ea3db681688f37d2c1f749b8111f6705f50a9b212a1f112a0be2c92ef9fbe0890df133252d4b59510aac501522c73c857179241fb523b6cbd447f6e20ffb3cd52797ea66d4c6fdcaa4d73a770bd4db1d004838ee5b6ff558a35fe31ebbe17211aade2d97750277a00aba976002bcacc9932d5aeae711c5c0e2b80ba242938e0d52e1c91ddd667c6f2b74abbf1da41a0b6c35c00e17a6ebc7901f7808ef157a01418d01949389052ac91b2d711c40a79aec70f73e9fdffe0ca944d1cac8d9e4b5ad2601316b5bd7e540f511662ab6ff3f08581ffbf961ee342499b951c63e998e60f4ab34881c9b8dd4e2dbe310cfdda4611947f2faaa558060914e3dad366bd4332aafe4bfc4805f6e84205304a03965d812f279734680b13e6cacac7b23c2228b1acbb93f4af41aa025bbf91ab9a9a42630a755b364b3ef3790337d1da0e315388521831502a260b09813081dcad647b64ede244918a587ff629d2b160c0ed328617eb4cd3df037fa8c30b62fdfc6a29ff02714877f0a4a4f09c29a9af2e85e427e502427651a8e3b304c361d2c9136de21197c24ce33da03e917c925be1923321e11e36bd043e1f730e88b2f174635edae1d9553e512b035b4e0b933d12716ae0346a30d6bc1c111c58df925b6e006c118da59d5e91142cb123ed18216a9a34235d2a452ac4206f0e6550b07c6ecfacd2f7fc86480c80dd178172a575c26c299c307ffa1b8b0a93f3f01be15b861d10213dd8c560e919c5fe5a46c8ca6c639fdde608dd918de75d6567a851b7680b297a2ff4259a48a36d482d1a698a6b16c9df14176812371e52cde3c222f843387102556dae72046097464c095df1ee46c69826480752cf48d60672cdd0299635611bb07626a8273bea0fcea5c90d886657e8c4ed47b62c3aef9191bc612aa8d7a3a8bbff9fb80690060ff6b36c102475b9136c49857634c927dcc9c481cd72807184e90c8e852a0801c946d6411cc2a195f398be1dfc37a2ef718a3091ddfaa1f317f91b550be51b86e43d0ac570a6c47ce50cc98a9905f886e44b69a02447cd1ee0c5ecda92b2d39872b82503583790fff4a195ef071c41ba778253a069b853bfc3cb73bf9c3d93c2ced9f9d1d9a9b44ff81145c0b9a6bdea82eb5fc0631a206074d902529dd3b03bfd38837b9295763ce9a173990da54473658fd092ff440bf4cbe8c837d5c828c80ae4d66f944545c0e27fc240ab1cef2f95b043929d25f3ed4e9ec08892ec6721c85052a6c78521f0feab2acdfcd4c91a7437e5d202aef919da4ecaa22b001155f637b156ba351e5a052c60bebf6953f295b369a27d042baaa94a56aff86ccf43bcd34f6e6080dae752c359e47a030ee0ce2921e0ce36acd288f10068d9904c877c9d62a7d937318f9f7c5f1df900a0e32eae9932c8f06a0b83e30b0b225cdebebd3b42a7131f4893cb5933ca34d249e7c78a9dc022560ea911eb98c0c473c4feccbaf919ba5e1ab1e9a139672bb0c81f0de2d6d236afef4ff6f79135129f6c4d375d5b53d96bf96bc04b2e36f1dce1ed5dfa982103fbd4e1941a5f51b4b114d45ca0397d8cc0b25e7cc9e4efec098fc9619fb31301f54c3195c1283407e2b520120c77daf2b6428d78e5ca5ddc13021c6505efe15f88f53c947bfc4408bfc93856fbdaf8a9c72df53fa60f0a8c91609f67457c703b6fb5b5acbf95186219605ee9245f80b948754af2fac1161b89589ebff5fa9168039fb3c5e72aa8a2adfcd6aa59ad3cf183103a5c1313dba6f6d978aeaa96226ee3f12f03eae59e60fb73db15bff1e51ae67cf88da23bdff5ec9e0b49566fa1683a4f335e2ad10bb52a7445b05a5765175abf26e2d8a05620767255f88f9070f0f0fb28cea2cf72e0745391bdad338e1b728132537f3495f2343514bcb1b3b0d8d39c1b7b17423a59059f96366f5a80c6016296951122774a1bf3f4176b2251fbf1b33040f6c6437af3d6fb09858b7972dea5c1baee1986cbb548c465e502708bd2c3149c3e86d6ad2452b70102fa4a3c748d65ae3a5bb2a90c6de79f60f6f090b044848ebc4579d7cd68132a3fc3a16989090cc37ce21079a152cd3fe47fa1fd48c0313cffed38c172e64da1a3f89942231c2113c5115e2bc85c3debef2ea054c043d9ad9cb32cef65a18f28f1e15b7c9985733c69a52eb6af4287922b6222f5205113943ff5d8e1ed099f5299cf8240527200186239863e36d4a58a2a5fb065d78c66b7136a52ef53de8e5d0319f5d2ac5d1c4b5eb6c6c31eb57e8e00f3b05879a174a1da0394f7602dba05a7cbcd64b297c0650eb8980cdca29736045d724b1959380c73fb0d631a680244a454dd3a4e0bba490eeaaf956f8296d39cb9a00ac3a4c762a8d11fd8645a19ecf3d149dc3de9a91d6fc22fcf41a3f41cf5d2c2cbc5eb7a019d0f1abe491758a5a35f9941edf83ae783c1009d9b32efbcd5773252cf6fc0899360b17905f9665f760f0d05a383645404d7ebe4acbd3cfc0bebe1a3e6067abe34217bd9d63a445ab7d62814ce52e0c7947b155bea4336495c7521729b8332e879ba575cb2b5b17984efcfaf4f2f0016a92233578f09cc64c1c5226e733c382eece007d0a5b2792b13ae338a8edd9034811a0d5a18e1ba453107c2fcd495401b784847149562b319c2ddf5434b54f6f98a55010e2d151b34ac1c61f0f08bd81faed2b6ace599d4a68f5e12eef8b2fda21ba86dbc6307001f9554a29d40fa86deb11a05a25c8dc66ec9e525526aed32573f30014da08c2d9382254292bcd80c5fb3c4aff19006441bf4090d3f5e72244d8967b068ab8a720e2f8f8242a3331efa61b12eb0f568148b190fe0aef1655aee8129c611ff7ab4d2b7e7f0456931fa078ca2b6cd155f04f7a8cf31a2540ad143669812e18153379b610f11a1ebc35a1e580b55c355adff570ccf222d05ba579793ebf482994816ecc57aa1ceb6fe0e137c9ad7d23215013c3d02755009ec47dd527d366d54f7bda9c80501b163775e5f22abeefc0e4d0ab58b3bfbc5c9a6971f5cd29ec40cb312fe43e9b09d53ff3dea50a09c42faa82a943c4cf5b06cb0c46d0e78564929a69cb6ece0c2c5c935dfe7dedf4020a9d2344467a8776559fb824100d798d988c0dda97c42622dc97061e54e3c9c7220c5326b65d25af7785919937794c407761f4273d7d5502c53ff11a6b83114105cb2f09891858a4920eecb5b8b73a5c803f4f8c68e16f2540d7a584736cd89561c2c22b3639d06dc502dde723bfd7d10fc023d7650b780aa52ffc396e1ba4eaee1e79d4ca4e42ac6fac5815ad08ada300c50431875f970511405be5f78f927ba165aeeb6e8349977f8bc883e90fa7946fcf97c403eb8b2906a69f84bd75e4bd3bddae210d7100d285c94009c31cfc067e7f6fdf538a3c085c166dc8c565e5a7fcdda24325b2c61dca091a4e4cb7f42dfd5c26ece367f02788948403bb25fdd624b084a539ba98fb6e00dd50b0021e2ce918b0dec5c6220414cb321509524b921d65134fc959ca8112dd6e7b6645cc9dffd9b0be97512e201d78105bc66c1d6b44fb0dd0d14d1575362cf2d5c4644bf4afbaa963fad75d219e7a9dea5e6a44092c906bed7582cef5b50b36b7120f7e56749f2c23b0667e0b3e97bd8b681cc3e3cf0564c8c508cad783d871a755580ef9cfc3f68511e6011814740377b0ef4c3e85e74f282803f7bf7a60a69ed76269beaf2e541f2f01461f1d3a7b61dccce66f421b1702dae8e1095183bd485317ee1121acc0eed3d9841499f6b9a0adc4a459414f50da7ca00070a63623de5e075e32778ea939f2b5fc2c7681f628866d4e70be2636b34ff2c719b9c49d94241c8993e398a07652126f0eb20da7dfc1543ea992b023d65d006cb4ba0270c324ed1ab10295eba02f28e72a0591f43791564c8edc198a23254b9fe3711abb8ebad8b2a28d76b063829d5b1856a4b156fddb3e21c5fbabcf1a2fcb1aa0aef309dbdd7b30fa9c24a475e1c506bae9cdc0be1efa29becf66ed43a000da45c20f24a5c8dfc08bf490000c17f023acc1af65a33f8ec051411061d7015c07dfd31c7ebfc09a9715008d0523364b2ddfd345073e5059d754260e7578661403ce987d3048e9480c61862d8899b4f7289e8c9f289e87a9f906dd963933d3626632989d2eb26250af5b76cea59070a4012afdb97165ca9da72545f49e15633ea2a26c7735c944e990dc961357e776441813f18afa14f8327bf2db98a0e3e7019f2751879c267bcb6c7863aec69caede24500dfa808bc3ccb710fd545fc116218319df0bab38e404c07ae223d27c9d4f29916205a86f00600a61aa0c2e0dbee33cf3613b31ef280895a78d292170e5911a3cde214de3e8d23939407279f7cd2ef57240e96eeb86081eefd70e5b57a3ad21b8f1063cc1e3b6f6f11d52bf76ebfc3ad91339479a7a3d2c2b8e6f69fd78d010f2dce00d6d8b680c06ecc20f6d67ec19a28f9c20ee9b624b784e05418ff4552d1ceeed6741f7ed9c3d00a3fd38447dad774a539bc99a5052a95f628286a5e62a4ccf79bab6da12d1576c537101a31fced2f45211f914c764e5ed965d5d9419156f62a957a277eee361d36f5d7c60222d27c3376828803d28c6633a8f2110601f35785bd5788bc8a8c47929d7d820cd2a4dab83e704ec786ea5e5fc3886dc9615b5d4a5355cd97beb11573a909572900531439f2a63f421eb5494cbed5ca15503e22f5cfb43b27639d7af28165b4591305706d1f2a9b0d1fdaf068e685508150c3d3d23a9c0a31fc682dff0e845c6317a414395c9e6c4a33ccab5c6395006971edfa8f68e55940b83b22ee4dc105cf2e39f0af7ea4a44a7a2992b76c4c570a30b677986924aa592738f56f95726a1df409d84e7f7dd45727c6c723d7d44300f256dabb802ab4c9a88121fae65192fc5aebfe70ffee877bda75be243ef3c1836206382f14f942875cee4ac22f4207aef59da5dcd208064b367dd78777384b80503be38dd876624f57cc4198a025b37ab52d7229aa2c68239ff2dd43d8994795828a5cddcc20bb663b36de0bcf88564e95318f7d59f159b263745ddfe380abfd71f5c2a6d01a4dfd231fa8c8adaa500d5df747ec3f5fad7f41f4d3307d99d67c019d7a23b048f7d63eb11c1133b5d573c502ecc2f5623f246c183a7d384044883190100bbc33c4aac39a021366c17e24a8e8d897bea7b9aa2b0b0ff17a79a91db2e36a411fe0c982dabbfc76adb74e710dc0cbb2101b4e04c96683f49b0d9e8321f4f529c3d73b380455084bad0671f759bba657fa3b04a41dac8acb156085556126a7b131eb954f4c98ad9a97666023f78a49a1906c542ddc59dc60bbca6c45b121406d8eec540912b0d0b03638fac7205b2556bfb4b096924ee52e816d79310095af3cb21300eb0416e87a6db4efa734e6f54a61d56e45c21f8501a2c18c19311f75013282383feec1caf488b8cf5e1d6bf898e420a0ceba1f47a001c4273d01357402e04c95adb4a05284a08504bd20e45a7ff3b37a127bb457edff631001c2134448d544b87c7cec84445bdedcf77b43978a27227650cf6653e85e62680d91a9a336e6013866aea57a78226ba26021773e81386c7e09f3942eff5c524b8ce0dd795c2959de2311e2b05f5e66cd6a92cffe26a580241663b6b4792cd8b8b872628c340bba16177e26150c31659ccdb694c9871407b0f2ae7c2020bd2d213e726f43ae8d6415189434d3f6053dd0f14fa9ab5e26e384aca8260901a01b46f83222053230900b4e765a5013a7931b2d689ae848d66815bd07bf7bcacd37f7f9e28c5e3f11c8c868570ada9d40a96ef71f80ab55a99eb2bf8e78db8182d518a6c1f8ac5c99cb8be988eed079d489e85c336fcb5f8057bb20d6322fa86e62bd8be01be1982e48210e025aef091643c5cf1d0219e877cf4718183baa29fd1e998b2233110abcdf05adb27255a66180b98862d820fb8220f99ec23d305d9f2be45400c8a6b6b564726412d6cd628efded4bd4d8d3faca4636f44edca4fe191db5bc302d917613326ba93f366cb36705e2ea7aeb69da71610e0546364dd7b8b962d62284be2ca6d0542cf4c387809d6cb0ac49ecf5e41166f72429c711e70927fe10f1357793171f937cba3af6a6d5140e6812dfc3f2377ba7e11204c0f27626e61ca00c3dcc0694e6fd6ab948627668d4eb432f6ce32c38726a5a35211facd2ad25e20b903fb971f880376def335853ccd89ae2b2485dd06ef4fbd72af678f3684d2175ac14729f842e00cfd4f48f8080e536f428c32e16954519e481da94b30b3e00dbc1e2d668931c3cb33526098b89da49bf023a3405526b77df1015c6aa8b88624f585b6022615ec24682a2e594e74c3cc251b082eb238167e9a5bcc7d8f97d20c0439e4fd8e706820a24114509132c61fa129989b7c5624f7dc11ebe3a6a1001bb52e32134ccc60f5e3111bc490086ed2b20eb735312371d60f9a2e69477f820bb45596866f4f846e312b6376de071f1354e67f33abadbfd869ff728c920bf21390824d579f86d9a78736e37eb6a1702cbb0535f3df93abf216055257c126c907b88a19fbede7e3eaf9972c99395a91a86259cf8b6bb7fc3e74365c983df0d3102c4173d44687042228e3dd26d15d4350bc9c99247ea8c96456f864edaa1ffe18a291daf39eb370763f0e0e71727b57dc895c7a0ca61d736fd361d0af7397ce060a77335485fe34cd581d4e971f77bd186535149d78654617f2d6f41d6ce2c825c039324ffcc10a27e50c6e47a5b2a84ad8faae7fd6932a91f3ee2d3009f4942569b4394e802c9207533e7c87ef88ecf09e20249d2e9158466d4e38a4cce7e309a3d2d82cd6c979458b7a60bb3372e544d4650c34c31410d78ce03b80bbdeb81cdc482661fce86890b76c3ce45a72c36b70d441e203c6f385584449176e1fa81827627eb4f2650e88b29856eba0822a44a63cb2c71695be176035a50dbf92b32487ed2ec3637821549f8013c816eb3ccd0d194634a82bf32b0957c6172ecd771769549a3138b690aec5c96013608f624ce42a753b61dbeaded643d8946cd9562b19aac5fd513923122ab8e5b28be59b6228825d403400207e70fca0ebbdf7c11656e662f005a9297c1cf3a5c68a23d88b5065a434aeca985d3947bdd7bf1caf2a904e27a9375bc4e58d025812903d2a8d9c1eefdf2b76e1a8f3bb20809a5b951f963e310b9b296516448e4847e8bf10487cd6704badf264e38036fd74b7c39c01f37047e62239081cd9cd5e3059d1070c9c1f89488626733f2b11bc14eb89481a2176a0bbd96c66575f009518c91670075a6d9488dccc597d27ad2085d5e559188d32c5168cc71152887d8ea71d15bf34ef9cd15cb041c40a6aefe67a530658033b23542baf13eb49ed5f4c0b7b111d7734ae2f88e7b05f9b0cdd7d573db49a19429bf46545942bb14edc07963d2800b5b66414a552a19759e3bee9c57d4e680be3d6503174807ebb9845825a0c70ab68b7d11d4af2cc3f09f078aecab7e88010e194843746e2c4c27893ed916d12d7575bc66eb1a290eccf0513a3e09579072ce06d8a7f38211ea1da1d8bfa710062124f853e1c9261705447defc47a1adf80c6a0ca3ea3d92ceb4b19219cdfbb15c1ffa71cb7fb0a35739a497e640efefdb2ff43e55a01f649aa81a5ffbd71e6dce6d3fd23d2011701aea425fdc6b4b89b62e946be2642b9247e35c6a16e8bbc8dc310107f6b7ce4fa1b22e96fb9d8562f02eda1a5ca567421a816fcd54396eb32759b565c9ea9cb3661a4c8e42986e8d272a33a58ab5bc23b11d2ee27a94d1fc5faa2a0ac039fd46f4f3540e952035f2a01762f1ea6c159137e220650a4de7c37550dfdcf0fb2852288a2dc0d75a13b721252e4413513bdb146f23f6c453c8212964a0ad3cc7e1706d547dd9a6e920c7be07bfc9ccef26adbbb81482041600e5be4608b25b7ade0bd39707d1141a9c38e91d6d6a8710e20c146bd6a167599b91eeba7d279906fc5a99ca5b5316472a92aa0e8b256cfa9df92304e935d1022ba8ee8691311ac01f5acdfe586ee52cd2be6025ba4653066c824575b4d79e762c2da88526218e5d1684647a0b97b5fcc8f97a24e99a4f6e8cab10ccb9181685a81710d9f486c8a9108c547f2d7d9796c1228b0709a12e3fd8326bf7834a1dacd53ba4e2dda1464eb76bf8e10837c5e0c8f11c2ea8f00356fbf4601bfddc1b11e5b391eec85ecdfbb3c9174bb085f3029ef74712def3d1bc2eb8b062e1cd968b374e2ddebb72d61c52ecb61f1deef94b7912361d7cf658040c7c687d5f2c471ae5a16628d632ae7811680f078efea45fd12ebb24dd2fd3480c5d399b9595fc698bd6d0c435ec72ab51a7d09edf66764065d9cb142b7e6d555a11b06605fdc3479afe95b5fad22b5e9b4604d2ca7ba3eb51578f23dead1a272168f78d251f6019c0ef83eeaf64bdee46c83ab0119debf5fc865717d8032f1d166e1de0446dec8737fa858d523c1c8155d4aa8d253b4f46d2973f178b0492d8680a005dca61f448687547ab04454c5c8e0ce84401c2203ae4a65d2a4c297f9b989bebc0d717a7b7b1c26a72ae0c8e838cd6eacbd41c6c66badc10014e4d4de5b875ace180f4562b87c63328a89149e4fe0d1f76c6f468a1f36fa60c7bc7e5fe59eafa01cbcb0000cb0f1498544708be7a7b8419d0abc91fa29728021d27ffcc472552a602c5c809451567cef6341e2c070dbbfdeea42a20968ed6254aa989dc871e3c91a70144102aa5696b7f4f669e1e0b3db2a8c101e1899f4803e8724d5e9407f485851ab9e27f0e7940a436faa458d51734e72c88c953a1cd19bfed29f9a6e503696a0b6823493b253c7878515b54225fa2cc2c43cc938fd1018c70f38a4649a2760f6cc067909d2812897dc8516e28343a51d9a0d987d0e7117eaad11d73c1e43dbd43da5e94f29ee359b1639549c847831283677d2dc6fa0b4e9bdf4affaeac14d82d816a069f303c63ff8bc301ad2cae0e77130b8771412f326f2dadc7c0d69543f99fc3b5b07139ada8c8443a7239af214df474b7515218b6e60c22294e4752120ff00af6b0d8a6f574fde3d9d31ac52559f9c1364efa10aca4626bddf9117a6cde7324503141532c8abd79c4694e62d4cb86596466160840dbfaf9000ffb80391b20e672a5f3f84f69c2d97d498f7449d1bb13f01818002923644f08ea3b3e94a83909315385d2720f9f98bffc8373ec9615900ed48016d62d4b5bbd9db07be2097ec1142cb4ef5789c9761ea7131d74168531c966e226d6dc9ace4464dd4aa2419f4fd2560ae90ee5021d85f58d208de486b6d008262451667f7fbf78bd41f6640410e52c87d7f000983c3282f1a6d7f4bc6c3753842dadd4bd8444fb54dfbbddfb4a627f6cc274263ae1060a69bdf3e0861847cc8810dc714756a05b8ca2ad1e2802a1d702133aec5901fba30e45dc302c7cbc1968132a3146c7832f67796a4c541788345733da795f14cd6049ddf75a49015e970c167f44226bf08bcc0721b0e43312cd33edba128ec39367db196b3f9e22817bc11d3bc0fd02aca7dc97a67b6896ddcef0fb349e9c3b36214fe2efd4cb55709fdd0511c1f18652d148e1959d2e6798bc956b6141176e0ff385b00f6178ae44f8bc2738aa8b71330cd645514f97c7db99e5722f935c22998887b7051d0ece56a1630af166104b50c80dd514eca36ad8feea1457acfa6d77b7991d1cecd96e7bada5161d34c5df4bfa8e7084390684652ddf60aa86f07a3269144e915bcc6e02f3de203dd990d931368a32f6075b3f49f95246e2686285651db5d4a08cfd102858902bfe01c03f1ad9cbf1b7ef45f67e6312ebb0c6b532731a4d26eab88ea7339f2e17582ba8136f2991301a7c24a3a3b5fb5fec1f3fef8b11e29ff40e9790f783af2f1da1cf1c4820e552ca692c2142fb81189dd32ee5b4cd67dc8920701023ef7b04edd473d4dc28377445acb892620abb94d69cbfcdd14f32227020a45a1ac5ba140ac9814b939870f27931e506f0a2676c9f65d88cac80d75b67a4ce57a07f8f2c0dd48cca8b68c2402cac0f8aed4314857f3473f904c7cf90286339fc11daa42b68686f74087bee2d18a1ceb6573d5faaf230e23dee53502595939e6220245606fbef3f3647483c3b7faea0b02e4e1ba5945258c9d51fa75512da79ebc9cc930184dd776227a2ac970d98c26cc7bbcbb699908974f2772729d069f7b8d890ab157aaf2faaf3282fecfe158631803de33736808af53a4febc35e372ff2452905007d266868ef9e28c1c631de830a7a3c38bb3b0664292487848815b002e4d0ed2474460c04b736484a520eaefb1ea931b86147abf7762ab20f703dcc3d607f8b0089caaa2eb682bb184dc81f43d39bc24daded115540412e4ea3ffb5adef53530a886a7b84b585d604f5b132e4f314fb20e59db3d005199a0703a149156a65e32aa0e38754aa43ef903a00c49b9c4a88a05d9f3bf958e07266055dd65088d9332b6c7d29ff611772b29263c0eccc81514b73cebbcf284247c809bd344aa416941c6bd68cc4e5c26c659f096345c1b8d5edc76a9d61ba130345fd5ad74b27a5e92781a44d259141e045f548c11b9679e7161823a61977ffb659471b0271d1000e2b89ad259d73b24aeba57cd6b941067906339e0ca696d048d302751cc0a39acc0b3a7ffbab9093266b59eedc8a12ee36530b7765feb1d69d64940461947246d619a14b4a57cb4bb9d60feeb98d8dedbefa88e3fb63be9ea99f9d65f7694c55462847e8212c9332f8c8e330500161e7eb4d113e3ec89356ea8c57eb739f5f01cf1e1e6a1040be7d194c75cec9fc63d377dd590df604615d5c1391ec17b535d7680dee50b2f96af256227ad345cc93f80822614d3fdb7f50966ef5d5771f6a239c24a3670723bec8bd40f09dd547cee615b07b54866e67f6323f4a6183a87e22b21690068f21e34f58f1854f39f902c2121cf7621a5ab326138284d3f4c54ea61a2cb1f8fc94b90c64a62f15927c4279fe1fd41551c26d4799ef9326809090aebb294270c2a541da191e151e04bca72bb50ea7154e249e8bf58d3248c323ab9efe2c75cc88deba848e0477c2d4f7842823efc16251b55dea0ddf7c06ff6372598913b16600c12af1f085fc8b0d695e469397a0ba7a18a1570930aff5be1b450c1d00b59bff7887dfcf7f2f0bd0a92934561b2e093c3295cd3969b8fd8836b37145271f5d5905e9e81ba87591638170928a90b8e9045055e740fb43cc3276a66c9129e35a0a0ef6fe53bbe1e2745db53e13f623bbdbaaa0bb4203ecf23e85f3838c1a74837fc00299a85ef5897d070b4faf2a7b56998b2e05dc2aeb645f18090d5d232d40f6bc20623c01fb890301fe074c7d09bc63ebe1bb260ac78cec14373add0f6dd4686087eae0e89f6a854adcc3437d01268dd3984957437fc292ff847d0704480c3a484db21e9f0d77969fd778259d723f1b887943f50ea98f75371864780eeecfcf5a77a5498f3379bf8d52e461765acd5ef59a7578df882e599c82b441291216f68f6ca3e602b47ed99aa0c6dfbfe69f1a644d22b66faa012fe35e84031c6cd6640f8cf548e61d4a0205b33dec975e6d8f3f7632b02c952948780ebc89169212ee0c8cbba3869490e0f659f8bbae0bcfed4ba2c81396a26fd929eb167e077a82cd634a5a203ca87729e6057ca8961428828cb53066492efe6ebc2d264b0f0b99506a07addda2986179905ae1744a342d6abfb3c82510551c7d46f176ec19b3fc7a6c4e0d0f0fbe8d495007c2d575b4f6edd0187aa08936a90101deafdf0b4a5912abba298b2d3864ddc9282c756bf0b241a35873151fa04296301519c00fe2804df9545cc47117b30af8cba291e17af28d40c30d05568a0f2c6febf4341ff71b80a9ed4944339aa12387542572592e2456eba9e4af6ea17722834a13602f714effd3c4eaa41db256790c488b7f4cd0c00a7e7be1c0740942fc931c3514016ddfcfcda716b312953b009d3fa126c50e2ea5515e4c0b5a3a66f53f27f261098a8adcbe46d98d5afc16e08ba6c716f18a36c56ed66529113d6b6b707071822cb4593a890ac726c30a87f2a356904b8acb3395fbaa5fa7f7e5c4b1f0c385261c40a7973e6c2847b215e7245b9f9ff0f381c098493bf8934241901d3b0238c82927ac418a390cb7d18d15626062a992593358c3eea37934ccd84161bbd272d021cfe3105284aa92738660327df5994eaa506a85ddc3bb5b12dd2f73c31511b1097640233620fce20826a296910b7a30f774fb572b7a1b60959ee49247fa74412b4c395963a505008da29ef947a6f676686340b98a7f7f8030c44c8bf90d1fb2167e266e7bcc2a6c7dde2496aaedc51f58bf1227b79ff40a60ea3581c19597eb1198139824e4cffed72280b7fcc1bc10a25541c2cde5143424deba327200c291", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000007a6dedcfcddb992da4890c8a97aebf9f00000000000000000000000000000000b4d83f75777e7e4919824dcb4ff934df01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x00000000000000000000000000000000000000000000000416c9296e38cf3689000000000000000000000000000000000000000000000007abf85d3a9f30c50400000000000000000000000000000000000000000000000237b411162b313eba000000000000000000000000000000000000000000000000000156b66ae45ebb00000000000000000000000000000000000000000000000fb163c45b929b1c0800000000000000000000000000000000000000000000000302879a33ee70e101000000000000000000000000000000000000000000000007d3fb89f63848af0800000000000000000000000000000000000000000000000000001191ba15fbd500000000000000000000000000000000000000000000000266d108d2bd6f116300000000000000000000000000000000000000000000000044a3d48dd17dc3d200000000000000000000000000000000000000000000000bc7f72ab82ba9582d00000000000000000000000000000000000000000000000000010d1bf42c416a0000000000000000000000000000000000000000000000021bab3c5cfd31c3d500000000000000000000000000000000000000000000000d01d612334779aeab0000000000000000000000000000000000000000000000090f4390fb5773215b0000000000000000000000000000000000000000000000000002561ec9fb68bf1e61c54fb309fe860b2a599e2c1339c9a0c1d4d4d7c19eb9e1925ecc353851c21f29faa0b2c9e2dce7b0ff6430e42bcab396059f4a009b643e80bb07dfd986a22c753cdc1297ee81add827710dcdce1b3905d8d137d45de8c4b58bb725680e4316c6900efe1d29c325ba03cc43ec30b8df8f7c8cef5da35935b202319ea75c872438635f5b3eebc558a54a7faacc657de20dda2064d0177e5ed3ee3ecd9f7ce014be0008a5b22bf423fb02ded702d843cb50b96102f67478344d3736244b75a9042116c4e0c0968f6fedabaad0ccf9ea1be6d1b6a2b42b566cf78603c24d65f315da1957f66178602e15a9baa053810b896567ba5fbc53b50b48a44400965116157c7f19bd181c7b184650af1f6df0687c64c2b54927fa663a224c6979cbb5a7274367923451bec6afa2d84276037f1dca6505e3ea5f37b5218d7e270ff9cddc001cc131923fe1c5c0cb360571bbde6d7670a3d91fad79d20d130044d4bab7e51ac2a0199f2bb567f4aa9fdae3b8634f70a6d47d4a8ec6829176f075caf523470eef2d4270dd12ee163794ae40acc907a4033b8427230e0a3080bbf4208bedc2169c5e1cf9f47ce448b3c89989c8ad99bd8726360fc259b1c35041205dd88b56028a4398df5cf93caadad64d1962b80ff243973bc4bf4a89700552f80d1e293c25c339bdd767fe4d0b7ee2d9b6c24f5bf65ea3a6e265f9c682b4f1b1a393f98c2d4017693a6eefa1e776ff4db63e9054253ba54e0f6c92613a8e3b55849035c606f4433589d1e140795dfd40198ef021ad53951f2dd41624bf7cc92e59783f3a29d9f6eca1bf91f470e595ece8a85c451e45a50cbacf2aedd775be0fdd413d44301d5db28a25fc8412e2a28e4e33bd176b54eee796bc300cd3d536f889712e1a0aad307d819cedd13a8abbed236376ad97d5acde39e0c8b0dc4d764ee788a0fd027b38b4bc46b09a7da5ad7f8af689326fe9c126a33a8123e47aaed4d5e877aa00d9708ee83d5b9fa03b2e24aa4245fc0d7e9250fae7191b292ede39b6180ad423b152d3cc3db2aed13f19256c56af480663ce8296ca90c2d85c2d694b51e6e20a4c03571d895ac7cf59e35392dc3908895026785cb42194d0559b3242912ea62adf77af10cd491f81184ad570951e233a8648246f3ffbcbdeb4e0f9307df9f824bc8a5dd0cfea8a335721305684c25bed635cfbac10e99964369e01ba3da0fa0df8138a289bef477027be8cb063844e59ff5567c05369b3d7fbc2d13656fee81b95251566f5aecb02dc9365cb51d522c3e368e3753313a9773206e10493eda00a40cebdc64ab5f42e68383442090734101028d4a3b3cc57c973529f6044eb1b1f09ed3188252c949bbccf14141f50b9c0d25f47019e1c0bfcdc01c9291ab7702315d4e4585011faf49227fabdfec64cc580697ea62d342a6d27227dd5a91a5b2a29035ec3e66629a2c99358f95d94ec98b50ef1b5bb59ee704b5470280668351ceeed6dfc8ba9c4627663f1a4b363425195d7bade1870885f1c07a8a2f8e82029d24fc02584248d25570148345a07fad19c756a1bddc07a8e0c3e0f72286441114933efd521c51bea3f3cc320adb2deac91b89e6fd016363d0b7fb6e2d10d4810e91cb4e2713ac83a62d28cb182779fd18be9f3766c7303ecff585f1c0bfd9503eeff54d606f92e1f978c61adb70cf59a3c0790d6c8184b688b47d10f28c5db24fc5858dde57e4fd4316c29873ff0f683c46c50af520b86b6bafc2505fabcaa0292ec8178651acd6b90460e6d65060be11d41ca74111a3bfbdc9b1d99cf7b3b221aa8a5f4789dbe9b007c91120d20e499db36f7abd83f02a1482d752f995987147cdb505bcf5b9968e82a0705a22f689f00bcb5a30ccc8fe0b25a99c0a22a6c27791efd79fe58fffcf59fa1aa0a7d8ca1a8e84fba4698840c6df2e048272a3528637ef304377cadacc8a86df22dcca01c4a63889492f030c519472e1f869ca329e91c181f32cf54a5bca551356b9c4d0c60e538977356b9622d597db2e97fb313fcc82f881eae7053f7aa4bd0edc0686b34acb24bbb90ba6757c84c3b6730ef27bf0f198b9488a471da7334a60d80558b08ced4e9c1b01f79f2358160268c9c1670d5334f325833ba1db22411592c14af3e83369d03ae3da83f8c0e4c9fd3620c897530e597293444e28daa1055f7d6f89f6ece42b1d9973c3f0c897dbfcf96160fa66e6c9c8bed3143148d883ff4fa31c3d6fe1ae6ec6e8b5ac3b840455595109d6b068a85ded28955858559d700697ffcc401df847df334026c21cb932da1072ad661a6469e211b76e5e7eb160581e3b296b2b5d771babaaf6508a29c034817feb2af6bf325448de4c7251b9e24a02dc474c923986346e30980758eb0cf2c19b53befbed9bdbdd43bbbd73b2f04cdbad27b9ebd4df23ed8955052b9a8f6fd1c32bff4a07aff3e1e56df730f2e2fc216b4f9fffdb5f6d53318b4987c68def42c021095a60702d3997ce3eeffeca5543841ae0b4a55349381d1222a2d6632b4244ff2a5766dd407d0cdb88beaf70bdef8c46ca2dccb6cfee6f5b90a61dd965b0186fcf84374a039cabcb975e7d50ebd8982c5a2474c5db21a6fa262cdb79a672220b8fa06bdf4aa029113eef5e332003ce5f03ef7618230c3774ed61c0482860128f589977a08a12e68b2743a80badf484f63bbeb5fb9141c25aeba24bc878a2741422ff3fed8720c2eb136d8d540db48d4428fd9b59150168bc623b40f22bf213fc3d76986485ba4f955af7475afdaff9f1a12c63f2baa6aa7ddd40cced56201d8a974e433e042073171389edb7a7b5e946148cba308dd80a02e78a2ad3e23019ef96b61d3dd0123805d495b486d76d0b9f3d8a48f1310ee4c733bcff3191500cfff383f27955e4ed578f043245bbccde47fd234312e389039a1b5bcf90b0d194297260a62a1ac783a496200362453eacf59e3be3dba243c1e33541a51aab521df84868549940a5b7e545b12a4c1fa0ce838bfc5244ac69e8b2834c7ba509f2801c3e1c0d77b6ab4c4f13a5e459ae4720f3aaa4e5bc98de8236f2a61188ec12326b66e96fbb825b539b41db33e51e53a6aca36b68bac613b91ab1b657d99f2187e9e99857159b991e1878086e6b931d24a8b5c391f1194461942c0ce522d8428dd1095799c7732b2af1df9ada0adecf15a46454aac9374abae6fc565e7aed60db2b57e5a24b226a0484bcf343aea6affba8c8997f19131a083d7da09a5bfaf165988bad45dfcb328512a710823a8a1743dfbec870e83e2352929f624372b97064e9d0dd64209c66dfd55ba091268d173e98baf277f969456077d749ba215a11417fddfbb5e3f7b410bc1350064abc3c0cc74470c845869eb2d57705cb5f11c21b11c833089928354ac0773b5bc6fafa12d4e9ba089acd82035f8a57b5e7d262829fce2cfa4e37be9fc640b5205689609d6ad185c87cf437727cc12b204fb310f676fcfee6c78ee2f3637333626d227af0d92d7912ff4dbe346b094b32bf36108066c5a3b416577bf7d266bbec4812447cabdadfc2e1660ee003e241449709f0196177629bd7a488502404a03c9f48199bc0d241b8f6ce8cbd8da78fce4eac72d68606740bd2a582d2f784d3cb38c610ff9c13bfe676485e7d02062273aad9a0d3aac2f2f3017e6d248adb5ac397957e3bbd8d1ce038afe1ea4b5368f73ea7907fb6239d7054a16a6139c7c9e9a44e66ce18c15d31ba66b18b5b7ab4d399f4706c5d0479decf2b5da336e6d2945c5c8317d9060ba8df6fa55a78def98656728083b6fea725a1e47cc8e7be08a999f1ab21d528816758209157ab26af22d787f196820105c4640ee968e4d67efdc9a46831d6b9623783013ea8361076998a3902fa23d94b1eaf9fd2f4676328df7a1b2081a69ee6b0d2ec1cd8437cfcfe5dcd411102263ba18952e56d9c59d047986456fb27170726bf7d605dc9c784eeed06d0d59a79ec52c11a1425f1aba6149c77a9309e8838992a803b339c6bf84db99de2e9e4148c3c43b740f6b456920ab24f7c0f9f820802051f900e30d3fd65e2ac91b1a57199f8c82c5ed3c86433a63b8cfbbb9745a5335cbae916502221a2789522a06ce883be2fdcac598cd70549e8c9f4927e327b836a98993f5c0352784ab4721bb59a473265104f1443983ba0c1f39583416ffdd1d897cc07d525218bd4200248199c942d674c166ddcc723dec117f0189752a57e4c763a2521f849285d49c0ca1d57d0bab91a530614e9a27da6aeacb64e5afc8878ba1c514d8579b1b2e762a859538f5ec6ea88523485a6e209cf838b4bf1225e681b8618809bec3778e832414d4c62f30678651e1d582a448252a16039e7ef6432892c0736a508c25ec9521de8dbb5d21b94f9d3f7b8520bdcbf31608a972f58ccb140a0f08bf0f3ef76b2f615185f8187636d59208375e12a170ded06c8f4d95db7e5a384c26312af709116fbc8fa440f4770f9131ba7882138ce3395fee9afb912050f9735442c7873311d08205fb16fc22d5fee5654380ddb1d88156a4f652f7aad1b5e23f9bef20b8223b417fed4192d84787d35e607239d822e6d16da4a2bac6f06d21267fde3bec0dafc9f0651e8abbfae8f450051d56071f197afae930935305da4d9dbf63ea1c0f62368f70f8a15866c9150ca5de551b11148581b7dbb3ac3a91104d89e4f4bc125e5a505925cadd64a4d7c104c6fe4792895f26b040c1cc46273327d06eda7c2d3de1f00f8dfcf6f911397b3b2c20c96d905efe0204cbba45adc56fd4ada7f8169ecd4306444313c846aa1d6e7881acc2bb3c32a9dd04bac0cb7043d97e45380502b10e7ee88623edb4ea6ac4aec8de66cff047b3a0b8de7e7030100434f5c11cbb97f6700728797787cad7111709a20288c667d7d4e08e095a40ad9b05be6e0552b75ed7fbd61c8dbe1165f2bd9a0e119b596999e49d83be09dbefb7e529a909c76b5258e8aab2f9b0e9e060a0e5b502f31647fd562b198e7a4aa3fce1b6332e35a4bc64f6d28fb7be4acd22efd02880227c2bb7de981afe547306c8a69f3f1cb20eb9fb9669531084d45b0149539882d12da0394612b2283947a289e615f113772c23cf3f00e8dd3b33e5fa8f024c53b3267b463981f72ce19e96a80dd6e61ada2e13c3f530742cfd36835e8a97c9a506cb699e0f86df3af34c52f4d3b1091213e38fb599a06c87eebc1ba26ba8d888df60d65a4a3554f4a061a9c63d369926e680d16f1db522fc33a0fbbf03153c6e6cb4058a11c4615ee010e70c379b71012acb47a537212ddc7fb695b37befbef71eab971ac03ae4cf7939a7c754e80208c03b9ae2f8efa57d6b4bf62e2ab53bff941b81ab2004d4214c8d1c3e41acd406c7f3f77d48a84e3c60228d1a839558a6c5b040155a3afcad38b03a89e03510128eb4e9892ab0b92c03ba8d4b43b964caba607a7d376bebc5bbe59cfcc543611f71dfdb56ad567e3ab4868bdc28f681e1284d267fee5eacc7938a2fb15539342bf1d9de71f15ceaeee10e13f488ab8e3a45d54c7abd84723bc141f90f1576e805498646c59515ae476fba76baf7609653089dd1b80dea0cc074c25794f48124100a10a5a3953da1e19403d981dccd265f4bdd93dfeb0a3b65f7dbf3a8b06a0a04e1ebe52ece119625511b8d01969c973f33de7b4b3fb6e2022e30de838c07dd12002f5b2ecc6538fb4c520a8f2e7baea4fefb8c5cf1ef2fafb00f0d19c6580c1b5a9c9f9deee13a3e889e50facb2f4538c1c43d325810643c44b1ec7f5526bd029e6c6b1d9a45bc6684109670fc6e0c9f8b1f3feb00fee3a61169e9a83c9dd42be2c6001cf3592b5c2932f494ed9a83bad49f60473d7612ed60a635d3e953d0028e993169bec778afb691c637b0cae6aba585d2cb8905481bd3eb5bf6de1da71a308aa629517e68450eaa8e7ea91580bbbeb30b7998ee27568ebd6145b6e70c20161bdf2491f545c1dcfa7303f7705b6628a1094faf98a5f1a36c6456e074a10401c234e52c9961b76f238172189d91befd0f7ebcd9d79039e29626f293d009100affd6d2254e10219e9b9566f8a923167933d37e586ee3f614a5012d69a00c00cd99154a20de6f0f04d9f32be106015e53d2c23d60fd9da49730d5ce3837402a375894d8f12d1b34e3995b154874c873f9a3037d09783b1b27ca9306bf2c3308dc4c73e1b95df94f830d9b250fc45d5e2370dfa351cbb79791e1c2d94dffbf13d328b9f475e53fda0f9b44c28ea21108032367e8edfef64f4c4e977b1c47a205869eaafa9c978e967a3cac32e4aae0aa895fe2cc67a6ed720d1169b06264e92dfbc6a99923c8e6fed8f6a951c771e259892ecc638a57264a7351b8e94e26d4185960e8ef13c4be85419936c73a289be253b2bda37d06148e481b495152e8ec1e0a7d1fdbd3a0d7de9891fcbd88f215cbcde85b4563f8993450bb2fb117443627b76e42ca03ed9c2718c877c640e216665f316867b5d1517328f980fdab9c0b1cad6ffe013a43c2fda4ced6d18d4a900d42a6397ff6cf7aa04665b6430ef14b04cf5f0b8e827810f4b9e628df101f9cc07b7f9d21aa602466ef25cac111f75b09c0ed411b1e0a0f442764955adc807d2292c032052d814ffb11fe2c4f25f54d26089052c2abc21152d0c63457259245e5dc695b2c3633eabdb6bb64ee7d1f652e5651678b410b12f0e7060b8e744416318503d6545ba957f5592047ca89f52b0ff2a856af7f6ec8051056bf31941000957a5d7e8a1b90f6758abb559a8582e726e70d762a07770a22949a32efad63c88fce8ab69e7ecfc221aa52fd689afca502f80afe8af67e02f63ea1dae245ae6df1691fc2922e963dffb4b40969a2afc401e1bc978955a901660fbc7d320019a8e81c76fc5e7e0719a37eb099913441151a6250590a660df462c08f119d70e9089e5acdb163526fd9d16f3d51d7ad6c612229709e6d3b097463069ade52da3f6e91a6c09d975847a351261525b3ed8b352e7c11480d1b743bf34c1c314e8a2143c012200bc4ccf66adc4cb8ecd828971e1065589ecdcf966626c728a71a42830941c96a4490ad3a3f081750168fead13327718d11cf276a90ba8e97d38e258e68e543300d01be6f2b06c51cc2b37489910233c2b17cc751da4d859d9d4bffd7a3cb89d6e8ecef481ee10e3ea70c3a50340bcc3646db58bd841b4439c17bd7116250020f3e9d0bcae0d122f013145b049309d0b89a89b0b5542a6c071dd247117f7a9c11d92a5813ab2234bd046e5f35682dbbcc16fe1f0d9b1d0fc7947ee0fd7d0fa3f41aaa13e7feb5ac3758b09b43a71d39360b515dd22150b18dcd0f4161ffd6ce130fdf34a1f21af7dadc0b3477492c58230829031e24bbd5947512c07ecbac8974dda0968a88ec196ea2a32988e41960210a0fcbe12fe59cfc251aee2a958577820683d2474c03530b5c3af41daf11471a0ba1ecf08b931a8b592a4341124146f7eea929b5bcdf97f4309388e4832e8acf205614a839dd4c803065ada539c038efa3793fd5d817a5d37150ac380c2688e1818bd1cd9402e785e1ec0bf8f70c0205df96db354c21c841dc5798019f150636540bc651792c2074eb47e8f6b8c8f4a1c18d43da452614900f830f4f6b1dd3cba0b6cdffdf86d4465254183f3b3037e25e5341956ae5bbb3e5b69764722fbccb0f85e82ddf9ac3e1b6c0e3765db51e570310681c3500d64f65fc81879c0755923b6c99e531545dcc3bd316065c7da8959f0adef1052b4371510dce386b17a9bad3a8a4e29ff2ba56067a8b4fe001c8a17667bd3776764b957eb097479e0c0b11340e650a8c5ca4a2fdaaac9f751fdc22b5ad7665a23856456a267cb44f0e0019ad5b49a2017bdf0771f62e195722f18270c350dbac52a3a0d303d9309821aca0436a16f4a3c4ca41dc150249bdd0364f2c89ab3c895b0004c6d626b566124a30f423f8c8b635d0919fdce2053e0414360f57280cd37737b540e65e2d99041a698a36f3ab376d2fde74928c547cc481f89a588176a7509bae404405fe041e6089219e12b9f770c516245400171796d9380cd2985e7eb7cdaa8cb473a3f4074552579d80c0699366babab60854d2c33ac00b5160891b6372947f93e910ea1348213ea777f50a05a88fdbd4b2494a7d16a2d5f6ea737e75ecbc7204b9864f192fe5523992d60c8d84d1b6eba8aeee8014743fba88fcee19d349c13d3d37242e3ddad8d0f0eece179bcd5382678b95d153b45a6172059f81d391c8bbbb815c1d82c2b71be144549c02e0f170ec5a8b1b86d844996b7a89320b61611d12e3cb00775bad99ee32f45f565cfe39d8c90333241b8aeb8c33596316a9b9d652b1ec2d5e8e9748cfe75a2049627dab27a6734e98f15902aa1aa3cd82d3a762fdcb02200ed6da0e18cf3f8afcca5bed31a93dfb79795183d3cb7d1032fd7da336aee52867f3c4d1fbe72a72ab839b3864acdb59b20ac38a1a267f62e4dcf71924a2cf035af3ac007525f75d53985c1ebef4201300f1052dbc5d090342d2ef29b839251024bae62c29cb468ead61bfdad488c919537f50e85fe271c25d7d7d7b550aea0a018522ca71bda2137e14a43499e6da8e8fc21aca461520818a0ed89bec8d8a280eb898c57d587192dbdf7c155004fabce811991a165fadbc3175a4ea707b2829bde1a0ee2ea0a10fc1fd8b041bd6a10ec6ed6549761499d74b7d9fd1e43e9b1642c7004a01c8dad4c27982cb87a76a7cdd92324a1c4a03338e0fb63bc6aee71e0f7ef06ba08da79fc1af81885746f981a1b1ccb86849bd93dc4946da8194560ee663dc8abfb3b4f401e97d4868baeb3cc9e02380dd317eba44b77585d03ad818eaaacb65f3696055f484c63bac79187a5569731e961f428906f70a9df8da601171826be0373379ee40fb7946317f2add3ba941641bc08e1c9e76500ed274c72fd2346a922c9ed939bed51203e6e8434334c8430851bcc79269e800eb376f9d05571558d8ebd306a3f43867406dfec2834d7e60492d786f022ac77e5fe38a4e1f3bcc526e694028e64972abf52a540e5fa9c3657b255b16443b0f94cf1f88450627b6abc191058fa16cee748550de84eabb8051a54cbde1478d527b01fec7d722847c86788c1e29add90ff796f2a4b430c50ac6724b1f50fbe0678f7f63ea8d08ba24f780459ee2118ffe09e17617a9a875f5fa866244a1e0a17929f2d4b67502b26decc17b0a6833b9e1fc9d6aa1014410dacf458da78fdd1cd87aeff21e0001a33b055d9887c58a323281ca2d6f08372098564035043daaadfe13ed795c100fbcfb85ee33450f020afe5c6701108419af3ab19dc36b0136a4e09638cbad8618cf10d5da4bb473b20e2d4f869f6abd9fdbb3125899efcf8145c6dbc8eaa5782dd735114427a7368894596336986dbfc6f83dcf2abfd02dc49e5096425bacaa0f4fefe353c5aed743e4f7de346ded390cdcf6cfa6866b2e26e307463f5e54142266af3ecbae5dfc0f6dc6ae9c95fe0b53369b7fe4d333ac323866d5577a41ea2c6d2a0983c01fa88c910a18d05f4b2ccc64617912fb052f68ec8f732e1c4ff81494d7b3ada40bac069be1534e46036e4fa15b67d82e35b6c4c537cc8d69babc03aae75362394a6489321b24e0767b05b683c261858ceb4252792911c06b625b0a1733c96181dcea552b33390c474d94fba7f60c1e79dd8630594b0e85eeff922521e5e26916a8d161d83d01faa227f2a9d6444fef04c87f4dcaa6039e1777692a97adbeb62a33eb4089559d7cb7dccb4f04431881d57d6d895aae0495f4327e2e4d202b13b3e055f00130e17d7312f1057000096cb0873f6f4e75cc094a879117c3b3c7a747f460fbb0783f83b3b0854477e023042ca514e68f4de8ce774d1626e6ad5c9f8602fe6c6b5fd8b31137dd1233ef55f086f28918764c2e9d456f212fb0444788ce44fd4352e3a05ac3fd55d1da6c5d5242c206256f663ede364e6e2373732b6b18f7611b1e4b63ad4c976c1deadf391b5da979c5b02b25d5ece40228affd372dd8a5500634ea7f14730689049209d5b5d28ed1af43b6a02339af83197d38d1f6964f7ebbeb83383a01410d59e08e3753b29980e6b536ae638590652a7b9a117822013f4bcdaea47c121aba0f791728da022a30d4aada1d57f8c623068f6b97acdf1888e3c9222e48ff811cb06afe6817952a5f84e27bd6ae7e6eb4063954f2271e0d8ed25f91193aa8f92e4a1a8c16fecad358545445169a129aef0d65d2d7d185b145537bc3b2785e0d7bc7ea6aa9309a2d0a00385348c250b3b614b1fbb7aae00ee336a9c40c652b647742a3607a56be6b253cd9152e8afeb67613bb31e1b89554b30c9ddb90be8fd9f795054412260ba7f17a74233ba263c9311c487638106e346d6570371469fd0bfd06b4db971cab7d4ef0990f3af631d7a41b9dffe4983c4c2e984d47ee3327e25e0e1bb4fd786160c4b34c846a56b76bf91c8277ea0a4cf36eb760488a05cc2aa70bcee215bd2d93510b35fc4eed1f1b4915e88f5976f5793046aba0a00dbccd7f3a316beab9510be08c9f05e784540a9e232fe8b33d7ec5ed4a277e7b0c17e27239a133d82cfe4dd3adad6816773967d50b2b89728d1190f312e5f10e8fc7d3ee8218ead95eecc465d5083cb6849c4e6c2c2d359afba7a0809dced78cd7163d88caf325866d1ebf80dba614817c3c47ca0594cbf43b6961415ad022c67b6e16820cca393368b51f821687ace8ab744039174d28be0ce2c8d9b07bbdd2972d52c9bfe6f26f55ddc67f015df08a2bdafd7512a69698ececec58664bbdb7c7389052eddaed595852df40e2666659e661a4972176250fff62ca3380e7f0e2fe8b22b2d710d76e6fcd4ffb0f7e8c9ac6c1127a2a83a5c61ab4c3749440a8f664d7d31bfd430dd80ed0cd48b0a70d592add19760701d3a991136bcb410559e27783a49625e8988733857cf2629c9197d174a94007003e3884b2dcbf2eadcafa5dd6f4c0795d41f78535c9e78424fd46ddb62e50251cec8360443fe70e56dda0d20550006d6fb8144d28755b55e83dda5a22dd8522df0d8beee822666e8eb9e52c43c3fbd074c41ca47c3bacd979ccfb528b2f2b0882f525851801b114af0f703235ca9cf8f9c9e50b2b5224430225b371379e7a1c7239a50c48885d57d708f217488080bd1ecb6d58f5205665fd593c9e0bcddf23b2f4eb1ef9301694a34e8738179e8e1369a60fc3fa11ed46407606363f52cd24ee26f4d3910611f69283f272f9ccc2844c3411baa34e8da7364dae8a75684000f0b2f9f04ff420e60d1d353ac9d13e7faafa17000ae8014df2fbf7d12c3d7817223f92516a4a8bb4f0e93ae2be4207956f7fe6c126daea596e4821329737b12f0e82c3f01cdb5915dcb711359d725ca71d8db88a20194032ee174e0296031b2f696b4545925563e1d1517e2c04b20f117d21ab1f518da2275af79d25e8d41607b2f5e9c795c05bd09e8e258b198a9b8d59b9abc7b6f80225c58cfeede76fb3278d3c307fac2e86cfb0dacebffb44e0c6dd9a82c44b6a09e480a03db77457542526566aaf260a14c32b6116575b481d6207aca05141d8d2a9d491cb9a1a99162696ec670c96835be0dbb7acd8345d7f58bde2465c9411c61efa95cf126e0c660a6cd0352efca0376e792a21ffb8bc5c5c4efda9db0450bb0ace4d4c9fa128b4297c7114a11be0b0e120b51308938e35bda2a3883517ae4eaa1d40c1b06f159e07d21576c33046501b0e5fcdcabfce665b833125b78d505ca673246352dd27fd1485ef6fb7528e432f977ed5ec10b0b786a819607c7750295556208206f2578925accd4fc26cab678389452aab1b5c737641fd584576f8b5eec4855e12d9ca56215db2dad99e19a7bfb18537fac87fd6e2afd076a160426081c063d883cf07f20a0721161fb081ff18cf9984812240c76a5ff5978697bd7ca060816322c90ad80126347b928a7ee34d2fa4331c9964d45b20e4dd03bfccef79a30ad07bc86b641d7ecde5d7184ccad6aaa66d296fefc304910def84095cb11e85d41fa5aa6b6e164350ec9827f1da24730efcd821b084047abd9dd6c6926526d5ae954086b0c510324f8d63c5d5cf2c7a0ec72078e8935b864d4174b1d65c9e56916404d56e472ada136e932d27cd277edc82d035efc8f117cff743cfd0988b3dd87e8c4af85425b0f856933d7f16194b39760fec8c7ceda5bfc1954cb224bb267af6ca4e2a1e0aee11946cb19fb8bb526017499058df0bfa6e932419e9114c7fa3504bae3aa52a34fba2cd6e0d3e3b57aef491f62d5cbfa2ca81503d2432eb3718df6baddad1042cc2b16fa503487386d885f97a0165000acbcfe60914f7f378bd732ac1632e2d3c231c0dc6f4f73190481327e93a6ea070953061c590c61cac490e057ed68d1041c85c671f90f92b4adf967b23d4b3bd487d88f2d20ddf0892d88fe69e7a732440efc7fcaf1bfe1c4447b2903475b6924ea834909b586dd15210e517abde7302648b539b2d3205bcca3c5e04320030fbed6bd23a791adbae7b600d61cfe58d0ecf9b32496dd8b49a19bc92e151bf569e3cdb51f522c92ad5fbcfb3825b2b4f088764f0347e072898b819fa4b7c650c6bf5bd0e3fa673ce756a382a7d7dd254265f60ed152b172b49ef4a87ec2cbeb0359785bfb85c5cf704640a8d891caef112dde0c9526f702286bf5ca74291dc3d23150254ea8fa8d7153ac3e5992750591938e09df761c13c4b4aa1921e072bfe257472220140d5ebdaa8cae7f560e9f714c95c52b4894db5df6c247c94baf38f12096da6808999fd18aa6670c88065e80226628c983330269161ac7017ffc0a4ffd32a3327d9a7cfe8db6a38ffe148801c6fde9870699e806e07e05aed0ad6e4ae588914c12cc74e63b4f4d30552d5fe051a876daef9218fc92516085c9864b930e810f0b61bfbc9a395b47da39e565817a2fca920d257e9ff9bf3eee1912d6c229cb578f5b0a15337fd914f134119c5153ae19ad490edcc124686d9fc1788c029d6cbb231dab0867395a3ced52cf5992b7aaa6d12151bb3ae8c3473238e72dc9fe1c255632cbcd1fc9481c38e4b3f9421b2aa75e8be1e3298246d5dfb689746af68ca58b6286b688406bb0bafc94c66286f9f12af684c6ebd555ffff7db08ecf3bd2fa20500b05ab8968351747ca14a0c9d64e6beedca294c73ce5617f6978c9b24e519125c94ba09c7ed874a80b7620feab3b353605c010f4b857897d7e39e3a6fbc840c848b4cb68aad5f3479611f2004a749cfe35a02ac5dc89b5393fe30f7c2afdc837b159c7a2ff06e596519c7116760cb3b73de1361f18d384c55c9e3540321a68c4e2e44e5c941b9f665059a1e008a16dce409193ed8cd54ec0b06a24ab4ae94fd5897ed4c4cdc5d1d3dc0b826c0b6ca7c81d0d98b62d261143a762da94b02604b811e3e7c53f79396d23a5b19f8794ad7850f1da3c9462a0716a77fb87fbe85d34c610c65e5a93921bce8160c2049a123f1e6f9357851a2900dec44340ab20ced52a80cd6e5ed17b9dc19471e2ae29c6cf98c7bec7d33c1cc38f39128f019367465a12455f773fd0ad443e0230482f9c17e3187b3ee26ac397edcddeee325f4dc38f7003776bd3f29645ce016d755ce4eaf4730ba5bea43cace5fc4f491b75bf4f40a1ef5d6880f57006f2e0ff39ff9153f842dbbe9239d0c5f12fa491325697746afbeb24c3fe8248ce5170bfa24f3eea9fc6bd7569cf327cc1dabca0c6d9334e669a0bc6fd382665a7bc60d5921f4c5b37845e303ad5f87cc337055eead375da43220395f7f258e2d617a028900a614996a6cc5e37c72feb25276f20428853d95e38dacb125fe86cd90d52c69c68c578991bf0a52e63bb34af47081cf8a6bac755d8a48593965adc95aea177dfc2a3e04caaec905d3c695a91784eca3b126da5e9c866d91c683b6cb3912124fc0d5d6688f89d4ed73b54d5f3e34face890f3958f7f8cb41dece502429942f9901794c73a7c09cf36212a51fd0782cc06fe30ee79f82d5484f23733f6c13233f93f7016dad4e73fff5bca533ac101209ee573f1cce3ff1fee1c719056c5e2ae2322639c3928eed49f97f1f1b48e503958593f036366eb21e23ce36b7214a1f59eae5e142031921a7723ce84d0724174f2ff35cadc02308f5fd66a0d59fab2fc15cd83713002d3fd0a2476d38b7851818a1b01b49ea13b0759ba2508e928807cb3903616113ef1e11f6ca8ab55043807f94c991add5aab254af2f42fc3ddd0cec41674123652d4628b6d3fcb5b051dd82eda1c68d133c33ba1bbd22a595c714a3ec3875595951d46b252c14e88841e72a69079ea1e1a0ac9cdb4d41e799c61a6d52ca385bcb12df3bc6bd2573d385559eb994f25c61406fa247a311066b9b2d6da3eaf9a50dc2d39ca4ea36890d8716745651c84cdece99eb27167760ee320fa5003bb3d07f7bacdd3251bb18c32693721a6558f007fb7831c0a7f1d8f0a7230909ae06b2c389fe18276a448db3414e3bbcaffa39a00de317b42e1d1be8fb0b3426ed4de416d966414a95a64f0006ccc9b753425580ec9c2c84f45dc83ad90471d2c0fd4fe6d06a3a279e72b673320bba822138c7b151d727b6abd414a88111fe52b8b0917acb88c22681ba264f140ab7b2cf227252babd5e83e3fee517361317646fad64650f2d6fc29e552093d4791db40cb5752ea44f8f1f42cb550c90", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000154d2d8b9b5e779fd4a14022ac64137900000000000000000000000000000000fcc98eb31a0ea5ce49a4df5434c3e52d01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } diff --git a/tests/integration/base.sh b/tests/integration/base.sh index 4ab21fa39..89450ca2c 100755 --- a/tests/integration/base.sh +++ b/tests/integration/base.sh @@ -72,7 +72,8 @@ pnpm committee:new \ --input-window-start "$INPUT_WINDOW_START" \ --input-window-end "$INPUT_WINDOW_END" \ --e3-params "$ENCODED_PARAMS" \ - --committee-size 0 + --committee-size 0 \ + --proof-aggregation-enabled false wait_for_committee_pubkey 0 "$SCRIPT_DIR/output/pubkey.bin" From df1d27d3c1d5759aaece2bc53ab712a405cff2f0 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Tue, 19 May 2026 22:54:36 +0200 Subject: [PATCH 14/20] fix coderabbit --- .../benchmarks/scripts/sync_bfv_vk_binding_fixture.sh | 8 ++++++-- circuits/lib/src/math/committee_hash.nr | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/circuits/benchmarks/scripts/sync_bfv_vk_binding_fixture.sh b/circuits/benchmarks/scripts/sync_bfv_vk_binding_fixture.sh index 0df8397a8..353a68b3e 100755 --- a/circuits/benchmarks/scripts/sync_bfv_vk_binding_fixture.sh +++ b/circuits/benchmarks/scripts/sync_bfv_vk_binding_fixture.sh @@ -15,8 +15,12 @@ if [ ! -f "${INTEGRATION_JSON}" ]; then exit 0 fi -if ! jq -e '.folded_artifacts.dkg_aggregator.proof_hex and .folded_artifacts.decryption_aggregator.proof_hex' \ - "${INTEGRATION_JSON}" >/dev/null 2>&1; then +if ! jq -e ' + .folded_artifacts.dkg_aggregator.proof_hex + and .folded_artifacts.dkg_aggregator.public_inputs_hex + and .folded_artifacts.decryption_aggregator.proof_hex + and .folded_artifacts.decryption_aggregator.public_inputs_hex +' "${INTEGRATION_JSON}" >/dev/null 2>&1; then echo "Skipping BFV VK binding fixture sync: no valid .folded_artifacts in ${INTEGRATION_JSON}" exit 0 fi diff --git a/circuits/lib/src/math/committee_hash.nr b/circuits/lib/src/math/committee_hash.nr index da3edbbb2..c4b6b7f88 100644 --- a/circuits/lib/src/math/committee_hash.nr +++ b/circuits/lib/src/math/committee_hash.nr @@ -11,6 +11,8 @@ //! (matches `topNodes` on-chain). It is not parameterised by `H` (the //! folded honest-set size) because `topNodes` always carries the full committee. +// Same value as `configs::default::N_PARTIES` (`committee::micro`, via build-circuits). +// `default::N_PARTIES` is not importable here: math -> default -> insecure::threshold -> math. use crate::configs::committee::micro::N_PARTIES; use keccak256::keccak256; From 6e6e3d396b3ddc7b5e9901bb66c404cc58dc06c1 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Wed, 20 May 2026 11:22:22 +0200 Subject: [PATCH 15/20] update secure benches --- .../results_secure/crisp_verify_gas.json | 72 +++--------- .../results_secure/integration_summary.json | 104 +++++++++--------- circuits/benchmarks/results_secure/report.md | 92 ++++++++-------- 3 files changed, 111 insertions(+), 157 deletions(-) diff --git a/circuits/benchmarks/results_secure/crisp_verify_gas.json b/circuits/benchmarks/results_secure/crisp_verify_gas.json index cf6816e9d..17a0e7c57 100644 --- a/circuits/benchmarks/results_secure/crisp_verify_gas.json +++ b/circuits/benchmarks/results_secure/crisp_verify_gas.json @@ -1,82 +1,36 @@ { "verify_gas": { - "dkg": 3037922, - "user": 2972869, - "dec": 3549077 + "dkg": 3042688, + "user": 2972893, + "dec": 3553795 }, "source": "folded_proof_export_plus_crisp_verify_test", "artifact_sizes_bytes": { "dkg": { "proof": 10944, - "public_inputs": 416 + "public_inputs": 480 }, "dec": { "proof": 10944, - "public_inputs": 3488 + "public_inputs": 3552 } }, "calldata_gas": { "dkg": { - "proof": 170028, - "public_inputs": 5528, - "total": 175556 + "proof": 169992, + "public_inputs": 6168, + "total": 176160 }, "dec": { - "proof": 170088, - "public_inputs": 16676, - "total": 186764 - } - }, - "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.599410291, "runs": 3, "total_seconds": 1.798230875 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 2.123328597, "runs": 3, "total_seconds": 6.369985792 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 1.93815725, "runs": 1, "total_seconds": 1.93815725 }, - { "name": "GenEsiSss", "avg_seconds": 0.755760708, "runs": 3, "total_seconds": 2.267282126 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 1.230714874, "runs": 3, "total_seconds": 3.692144624 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 18.895682083, "runs": 1, "total_seconds": 18.895682083 }, - { "name": "ZkDecryptionAggregation", "avg_seconds": 48.057542125, "runs": 1, "total_seconds": 48.057542125 }, - { "name": "ZkDkgAggregation", "avg_seconds": 20.904339167, "runs": 1, "total_seconds": 20.904339167 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 30.160204763, "runs": 6, "total_seconds": 180.961228582 }, - { "name": "ZkNodeDkgFold", "avg_seconds": 102.337581166, "runs": 3, "total_seconds": 307.0127435 }, - { "name": "ZkPkAggregation", "avg_seconds": 49.016015041, "runs": 1, "total_seconds": 49.016015041 }, - { "name": "ZkPkBfv", "avg_seconds": 3.839788277, "runs": 3, "total_seconds": 11.519364833 }, - { "name": "ZkPkGeneration", "avg_seconds": 65.134843083, "runs": 3, "total_seconds": 195.40452925 }, - { "name": "ZkShareComputation", "avg_seconds": 52.426543555, "runs": 6, "total_seconds": 314.559261334 }, - { "name": "ZkShareEncryption", "avg_seconds": 112.963847905, "runs": 54, "total_seconds": 6100.047786911 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 244.367978902, "runs": 3, "total_seconds": 733.103936707 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.092927916, "runs": 3, "total_seconds": 0.27878375 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.266464458, "runs": 5, "total_seconds": 1.332322292 } - ], - "operation_timings_total_seconds": 7997.159336242, - "timings_seconds": [ - { "label": "Starting trbfv actor test", "seconds": 0e-9 }, - { "label": "Setup completed", "seconds": 3.274390084 }, - { "label": "Committee Setup Completed", "seconds": 20.259594916 }, - { "label": "Committee Finalization Complete", "seconds": 0.005869333 }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 7204.022842209 }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 7211.068496417 }, - { "label": "Application CT Gen", "seconds": 7.746761542 }, - { "label": "Running FHE Application", "seconds": 0.088362083 }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 814.168040875 }, - { "label": "Entire Test", "seconds": 8056.619484583 } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000830d9d4344316b026000000000000000000000000000000000000000000000001d383081c39dbc5a000000000000000000000000000000000000000000000000ac70e435f22d64fb500000000000000000000000000000000000000000000000000005bbc150f125200000000000000000000000000000000000000000000000a8a546866290bb770000000000000000000000000000000000000000000000001fd3f2400d703cfb700000000000000000000000000000000000000000000000b9038d2ae3e79290b0000000000000000000000000000000000000000000000000001e3d48d51e5500000000000000000000000000000000000000000000000067a0471dd42faa6b0000000000000000000000000000000000000000000000000e9084f4ea2d5e512000000000000000000000000000000000000000000000008a356e94c183365dc000000000000000000000000000000000000000000000000000254af5f427efd00000000000000000000000000000000000000000000000238e18ad2a5a9480f00000000000000000000000000000000000000000000000bd3b65543dd7dc02f00000000000000000000000000000000000000000000000759321a99ec5a19300000000000000000000000000000000000000000000000000002a525effa8d9000ee44df0d016c13539e3e1de25cb3283bfa0110644e6c3060b51acf455c43f02d5202ac66917f20fae1a39f8cf51134df47884f67caecca680f87bdfdd85ccf0315bef50348a88d4c79a1b50bdc50c9882a2946c73cf72c563058759727f8a009db15cdba20a346d9491fdfdff4ddaf05c2474d9c1f2c4821a5e2e101b7b8e82e181958e78629f078e836c725ba08236958e3445a0dc31fc4c2202707b61c7d0ac53aa35319f776db882a6acee6b04a492720c30b527f6a209efbfb4b22975403a5568ec8e19525b52dfe82dfd9aaab141462570f5c39f9a42f13700ff87603118b076682b92bee70ee8f9b972b8cdeb9ff1fbd603352f945f2c95fd35184f70f989616eff536ee31d0349dc97067aa11f2ab4c8782c4d19a4b89968e13f69f208509774cc987cc232b93c9c49974be00e3ee21c6d743b80d823f9eb1ee13be236cf0e7b7aa0a5a5f4207edc057a37096d17a63c48f2866132c681a904108c02045139ff715913e4d22a696f788d1cb2424d855ef9bfe36f67721de67235cab0e4f4cce9ff0f3588aca137e0740c7dac2a980816ddc38f10be0dd37f01028891d4476773d637a1bee280492d73d68838af602126de7b85d767e835f1ec2f5f7015609620fb34c0980312b5b7f67ba6ad756f683152cd1a95e9e4c9a8fd58bbd1c0b2d262ca7d1cfbfc729424917aa0bccc6e667b7d4e4293e56552cb688b8320ec6d51d706a3d0b516eeac14f7b49e736cb687e82d64d239005709ea7a22af41daaba74d8165e869cf52616a224d38bf14b43f9365e25bdb8f5e3088158cc3e2ec485c6affc47a2b1a4a1d0b691d81aa7efeb0d4f243c3f9862a9b376e7648a2a1f40a23824acd2860b8fd4464a89ad9da06c090a2dc69564011c9749dde2ed15a551e0c27cb57298106bdf016afc08b87f0ac06032ee6860c10be94821fde507c4f9ccbfd67261a4729466f98ee1db61592af1fc7b39e2181034e27abc492409e069c23c577f4749acd5236e349f1c92837155ab5553d1ce276d8aba649f661e4ae3dcd8f73ba139ca807d90d6dec1a9b860762731186ba09357343823fd671a0a8440d758f2714b07ce0b6d8ee3e072af2f999efd2b2f4bd29a0b7e4ee2fb03969abdb773917cdfc033166bdd42ee8c0f0de8fdc1f0b959ffd56c806c06ea1137b28ee248c3176911c6a89f3ac4fbe9294fd2c25740790a660a831c1fb96109efa0ff705b5c893a9243584fbbbef285f05ca9370a3a352ff33e3fbb4ce74222a57e9df2068cf76844517674c6e6a188fa7158768ee6fb34da4c185fb830a8030b0eff418ff974072732b830d30df3199387ea05c52692283f7bcce83b46392ff77a2391ee371a20d92c00b3e9ce77fdd50918e3ca550e914a7a53ee6f8b7709d5a71495d8ed3e4500f7d429da6dc0ce8b1e10e8153d28ed7b0454f33cecd924540395148ae0936911fd4c376a78890ebe5d7fb220351c257b7bd4569d752023276726c1d48dc36a0147a3db2a69a24b00b54e87d42177e95503de125797ab2420f3fa31bbd5c0e70f3038b55ad7e40fbd4380e86fdc7fe89164eb0aacd59012dfde37496f6116d41ff33c74efb86de3dfa2d24cefc0b63c89d79e133cc29905efb2920c4f7da9c8b961871dd18fd44999a25e5dcccc9ccab601a2e675373e100c397861eb058b5755e03a51eae9d52c2e4d4995ae4ca18e4dc2233d7d7aef09e64d43b4813625ee86b3dde7f88897786e1416eddb163c124b159785245e69114691c2e2133c527da43f96a3ce84e6dd4ca5565323c6c315ad829d67ffe1c404621eb8bf726f2e29a09c7859bed166cb8ba83b62b89937330ba87944cfa8ca0ca6890ea5b94cf33b92150635f7eaaa4f6ba748de2c1511f5951f64b2020cc62dd083cfc17c88a46303505006aba555321be0a57109e342280647134ce385c5299579a2c5323a9de3f476deb15b2bef26ae534a4cc8ef85736d2e278da053c606e1876d5599770ec050ca34a7586b97509498768869afbcce36fa99b3bea7e2190eff69b5e545803434ea8ad62014852a371430a727701311a6f04fa47cb2cd08ca37d24e1311f454ce5d64f1be459f3f63bd3c2c5d2d8b894886deb7403c1915a57f7b0dc00c079803ca7502bfe4818087b1bee5e5ef561924cb46df8ee9af2f4791d0d4821ba79925efd25af9808cbb8277cd45e6c8b1c435290fc3a81816113cd7b0348fa54da05b8a621dc0216d78be0a5d5e1a910fdc581e4d05f27a011ecc21fad91def43ccce7539527630fe9efe81a8af34587cb9fee87f922e63d01377a702022f407844e60f563192d097fc190d4b9423e4e7b237edbe025c47b910c6da28d26c5f25a5025265d2a204a519b833f8e64d2a3a4c0f00c1c39ce69e1d0b89313a4d0a35ddcdf13ffeaf15535473b98771e39baf7e4aa6f927264e6119206d1b024b808af816db984825a1ab0c4ec9df14120dbabdcdf4b0bb4445ac022d4f53f6757cec1a02719b166bc272aa26fd3d5a19f053e13fbd00b5d67ada109d29051cd5ec728a99bc53a016164989493cef0a3fcd26825ad0673bf2fd9a17788433a5a33f5dfa3d26c5afbc72d8154c7ef45b0321decf426bc6f37e7b0e0493ee9e522db3775ec83b6d4c69585fa5023553e5d477f6e1ac2a80b4f0477a0f18b2c4014e7596d55b3e6cc268d4b3f0d265cf823feb97355ad804bb1510c5021956e6865c693615eccaa18c8dfd10fd643ae26d30bd4fd07af6309c8385af2ae91ffade4e050bb2a85681ec5eee4e55168bf626743731018ec69c8ff8574700456dc0c1b55946cfd78d2350072c21be9d3081f24bb64349c2dd8f63fee8440c5a2c18c8332898034b2f6b5c774f71d8a436168330e2a43d1501d35bf7264307009ae8c13c7f98a61e2b6294ebd04b54eb61cd67c868e5a70b247a82c56b5000773850ad4fec37329ce608029bf0091939a6a1b6b9f426f2042c0717b61dae1a416763e0be43ca69a9f855685aa38ca1de9230cc19b7a4defaa050052549512770183df9e2acc5a1723ce3d73b96b32c41adfafe5cee370a1e678fa79ea3722461dfd9aeb45139569f0e6f7a3c3d7f60013fa59ec8b7345fa311351a12d2140d8e6ed91a51176926a8dba35e2c65d4ac7320103427b36f20f3411ba8f04ee614427ff6b94415c230ed5f49545dd15a4e1df3fed50437b81ddfac5cf1165e6116e3e7fec6439f5f56494929c6fa96e7e6a49d999026d174126e0deb9eb11aa01cd1e0dca8ffee2d57bbb6a22e1393f6ebd5392244b30634971e9f38de5aa999134af1bc04a03e028bd552b71f42a2b798e649db9214c69412e408467037b7de1f4fd334f1c9f9c7bf1050182d4cb55bfa65f679a1c01e10cce60b0ba019b0a7211b9f3e4e5cf04297d83eeb51a0d4582534a00decceea4c163271f2696053e8164d2dad9506d5bfcb921ae50e0d91b96b81d4c3a805116e90e9c4f6becf24e42fa10833dbf0f0713f1b46fe67f689ec0b3d6e6ae54838dc2b20549c9fead4f317fa104e25740c58fa1089a411c6fe1140e07cd5e859dc3f4b10cad565cd35c21eb743d7d066b0216ef1222ed15d5d2b19901623f0ed9ade841181ef7384ed412f47fbb67ab1620be7c439652920a57f0e24e0489622a90ba284b0f15d8708f105dd4dbc8cedd61f619f19696f3d65d947ac5a37117e42234a0b1ac09891a97d2045fe954f13a507dc665b6f4dc1af44dca049b48f50601239d54963830057c21fea7fbc790dba2a482930e983d06cf20e52f841048ea23fd9becae3c6c56dea16d3627053072fd2eb637138bb5af7f1574e5ea375e8d147294672d93b1ab92a2e625c96a55982d59f2908793089c5d675fb63e56ccdcd0872735cc0c45351970c34f093e20663390986b19081d84681c360a40344a48044e228a23139c07de62bc0d25c0f844edddb2e17b82340e9d73f228772ee64c59f9746aa416f1405860bc6bfd996c2e61c9bc823024de7bdf863bc5133fd6ab0805c4c88ebbd3fcbb12d0f7308c4c486a20860283e6e2320e198f6a2d085cc8980e26f2e51a550933928e561229bfb72912cc955aabeea11b030d1c17551eccda5bb5f5bff83fbbd5013563d0f8a86a2d65ad84d5c95ce6c5a2b656b3b578b73f21d02ba88fe8a9d712e2afeeb9d7e0eb9c1a541f91ade3049ca1f51897a474235342a9a6f88fb499428e9770a22b2dd768a096dc5616a65630e49d6805da5c884ae7e0312ccf8feec24843bd69da995e08aeebb8e5a4c54b61d70edb1c5e62db5e3ff429ae5cafff81dd26c2cb92f24909acd104662f7d74d54466c57386cb437cda86fdbfa342f3011ac3251f702fb97585a11f54408b29243d4eae98f77a096a0ea94e624d152b8267e3d7dfeac3d7df0e8224a2f2d99102031ad8a74dfd347c88c55de823733b90632ec5a50f12be845c37ae121d1b0d662cf8961ca1227bef498feb4928655861f5672914a9fb0b568580a74142716cd5ebb0ce51c1c8d072f6dd5c981ca3ed106bef9c00c32e0246166d0d789aec5041f57d670fb2f0fbb4aa87335a7ef7a512cd1a8bb26b2d343de10f1b05957a69cb3e99e955b9aec21c4fbf7a4b5453c421bc106f05c610f438528f8e733d48e402f42600c1ae3b74318c41b722dd5cc6925154ed14a9928b5a96e2e4b4efe986e226c755e98e61ea76ec944eb78a3d69201231ee7b8063640dd0cd244ae2e598bded49282ed4d79c0f890c098432a534708246110b4e213240c5bf0026fd920a4bb45e2c574295ba581b644d13d762cfd0d43d638af1e48c7e08246e03fed8b6dc6c09ab358b627fc13faff5add1d19751abbdb7ea60b18e3f2b102fc3d30167795ea71badf50e36e7ae300dbcabd1216048274a5622365a76c5e7a50d353fb8e9a1094f8deb532f930fea082eeb6a4b31c1bb329608eefae298673490756fa62d97ca75d15f4441f1e6018de13991f3816bc02756edccbeaf4300ebffada8d6b6c943cae931a17f6a89a5e2b232117c0075f297211fe6a9cfdd0a4f32127175b900f81e70ef7e79e1d1e51e7afebd95a0c91f87a48820ad1236710841d0f4cad093f3173a2d3060d59d33d2de467f0072a5a51e107eb89a2b8dc2ecd12d77db3ec7513ac16154c2f1ce91861514e615724f19f85a4c2c2b99629eff7eb549565ad3f5217d6ed6b00eb83c75e170215ea04b957bb50d43308a0e5993d64b0341182bf7e615835b8a971f1d37d18630b46287d062a775a49374c26263f01e4062cfdc3b934fd8c22029a3d44d0760c116a242d2751cff8a77d499e4b9ba267e85143e5f1a59195d98cc13ff0a4fb3ad7c90907b712c497a116fc6f365a7f4f4285f7f5926487491a9cbed80268203b5e53074c7c275265fea6cbd5555f42dc8f8f45122a9f1a64120f0ed5f2243f410041026d88d6e4179b61e5d74d705240cd0d43730cf5cd444824aa68219d38dce9192f1023376a1d43ccf70c0b7382b4253eecb6aa387b83120cdfe6d02788ae70b3303be8647e4498ca34cae02c5fff879a7d4106c8ece7153abc8db46c12dcb49a23c6dd66ef69852617850c188eb1e9f6051bf69841624f9b98c909b54559b1e42f85ee41dfc9ad92b00495f0fcad184d54f6b3d5bc239541e0a4a1b13750efd50ea8779427d2fcc35a52cf61a4786f9ed6f69c9c842dbb60436c1c8fe999c51e2ace7522b27e4b3b8caff8e1a4ee75455e869e24333e541db868e616d6bcc60720e529e44071d6f59aabbc1921a7a28a662cf5756a53fa23bf56fec80b6ed6b90a65fc7af3019b9a937f10a38e6e9f505a5560cd594fa6c42b52db56befdb9160be7f0de83d3abcab2d9efddec09d43cb6da76a733ffc61bf9db3281f3bb6c3a27bcb2167b53f38d25f1e1b85e1a55f53dbaa39ed09dd99da47dc62ac3e8676d2179043618a209e0cf54b7c1d85281897b86b7fc4d2a03faf6272d30339c9d2029841ba7c096a9f52c3ffd13d354f06ca15bc394b99de23492f7da1757e3769419ed503135e7736210e1f37ddc3bcfb9b7627354bd38b55f3f3d326f2e8a761a17da2abd212372bfc5fe8f07033d8bda07733512982a3092153286ef6f95e4df1cf934d31d0566f00c1b1f1186bb66a3b3083c285f7c08b21eee1d2733784d442063bd18b682dd4e90921766318cb642e3789a1cc4a6a191643c49a9b5be21fa004f66731f08c82f2d433f0ea3a5ce0d4fa2ebe5ece4545d4bf75eef7387c3622006f028a52b5bdafd3a058c16068a4d331df9ad12888e64e8b52b12a514c0e51dc2c154263d1399b3108002ff047d94c86256d801891f8c3d0cac48d7d5503f2bb7074d74e23b2bc7056963f5897a95d9dc32006c6a259166474287b15063b900bc8f035a99d7cf793ef4d706050388921536d3b3bac47fdff272b1f34f94800f0b6de358640c1f7f50cd197faaea1afa3dd005abe027f5406a1d43b4fa753e1bf29ad3b1ddb946fc5ad5aab9c781f13a9bd7fbb4024fc769271c1617b160b62821f77d6b20c09b826540204521241540181ff1156eba68f62c3807d74fb6b82a26ed573505f10bbe1afabf79d4d85accfdde58bacf25838f0fa93d87c276d72f7af912395093ae881e3be578d3dfde5675a7df316907ef4e349b8e0c493c481c7a29583b0eaa6ef46c964cc64dac3aad7c065a79ba92687ce0a98ee8cdb6361a14fce6daec621048466b4a6bbff5176467f07e6a2b69d5a59ce0127c77c05912d44efb3fa2a2d643dd0631c0c420303970e67d144185aab38122ee4d89a180002c26533406e07c05fbbfbbb148d6212908b6368226ea7c7d5b3e839910389e0099bc3354cb8d84d8a9effe2cd41ef669540fa409d96d787b622cbb530f643b09aab6a03e9b83f0a22202c68286417e48731d44a2eb367308d472ab2d860c8803db08bfa695f3f2930a8c664374b927c41dcc3c9c2fcf71e1c3efb95fd866102947e9746b2cf78aa01b14cb5db1b082a352f58fa64c4506028b405e82ed6ebd0826c6541fa085bc93ac86d87386c2042f07b045d93f017366d6c1d93ebcedd014e278d3835050677c5d46e7378734d23c51e67f1fcaebaf567698e4eda0f70e15dd2e99d51ae126ad71067d10cab934a9f0d0f88b6b004e1e1e1092b7cd460f1a170e7efb388dd92ff079c964f5f0191a51b99ba304b159294220629427c66322d192e3cb8014a8e42c965e9f09703949fda898bf106f70e3200faaae85c86e10f790ade8f2875364d93992679319355f0415beb31b498a59f932bcabf37de62a6e3e2cc8ead181b76d7f2f81c89665e5991486881ed6e85957f55d87c4de2f0bb2af449984fad5b85aa1fc465a345e5c098635d36cd5df525866c9925158c120366487c32261f99551943f7bd9b828fd0f805c91af36923afb9a1035a976cf29d7efe8355bfc2823d0d9d26252bcf99df9af15ffa66a2a57d675b8cf6494e20615cc6f195c46ac770f1fe2cbb8f966071ea08c126a5993126a291ee908a96a2214f44d6e3bab7a1f8cc9a535d86930423ab2be7b7f50ab026ae178a7464df6002c08e6f95f946dec9d918fa7c701979e5e7277efd92c2e0391bbcdfba23e102c2d4162f5812fac7806769430c50cf13d041229d170f339e9fb0765ea57b5a90afcb342c8d26ea08634abbcfae382a9292252b2722c90071257e8b6a3542a7d1b202c88abbb094b7a2f6be1d22ea647347d0f43ab767873da3d12382713ae0b132942d7950c80d15ea5a933684f30593ed3517517987dcf7b800fb8eb8dc64b07bc25686cc77bfc09b2d71f441bcff96a0a7c7bb947c6079080f694243530d600844e32e68804d47077fed9246ee42860b7d03ba6cccb59b539553e212fad1304214624e71fa2f1496d36e6ace06ace6f520af8bc12fa6907234b65d64d3a81300ed8c5074835551bc7cbbef0b6dec93fa8eac69ceb2b6fa787ed2b21cd15851840cbc48395ffe7276e88100c327fbf0d4b7ca7d9e7a4a1f17ad4e214a4fac82a7bab24a3c7a0e15438197bad66ca888729446d342811f1aa9a76c4452531e605e7fe0f6cf94dfd74cc7bec4fdcca0269fe0f2356608838073b1a2bcbb0bd130f19d0156490dc9d40165ca992696c5108931f1a0735fcb95193f56f5af1c4a70125c502bd7355b66681cb150761c244f8f1c12b1700a4aa873545c8d44f1dbd2c27884cbc54e96cd0cb82a908f5cb4f98ca4848fd7d6607c7c376fa5fbaf5b80634c59c38c194fa0c158398b64ef453fa5d1d0eecd13fb4c47d1452ed75bebf1874785457afda78924f0f4f191a26da477702053e6b4ea9a9158aeb8be5cba01eed0bea52ba039d30db1021cf35d604bafe3e53e81d7d7a2f54cdcb1a07a1de113f1b5a613d9e4b57b599353128a4e4fd25b865767590ed535d0f8b64a5ba6f1b2ccd91bb5128694deb43752b489f8ef72aa7850b83c921f56b27dcbcd801d8293d9bd61050e6ac52b47d25c4ba3eb7c3268719971b9c5f6b6285e7de73ab861c8a071a0b61d854c12f15e8bccbdb3948f41330dab8f1e1b46401fe630cd9590cbeedb644bca3ab31753ce8759476832ed9eb828375fa41106cbb8812a4ab3f2d2917d97686724f15012e3aa224edbfa79b5703723b9f1da9d8f298e7a424e717c15f97c6bf2975729f8056bec981a39d6c055d27c0b46166a6ada05e93d96d21e8b83dd54e3cb2488d3e4750dc0cc64a5a3f5ecd99eb8598c57f36b698f19105f585c424c2f0c2a8bdb04498b691ae1c26302705c2fcdbb7ac34181f65900108f4a30fa53778b73db14e6d881314fee1b43d119dc7500b68eff4b24650bb7510e8c3728a3ca4e710c32347395b4b203884db0191c4ee2039e21aed587ec1760ca67d37030c26656ebac257d53dd24515d20dd84a5ef465978759d706139a0c259dd389daef6ffa3c56f3052ba2da2a49996bd9e3ceebb79d7338319a51569811ea7cdd52331d67c99185bcad199e61682a180a6f16c021229de73dee8e9eae0e057f1a75896831b980d2376c6baa22430d90781298cd2c76a3f5a5832360d30c6d8c121ced3ea63e682940893df32891a0f20fe3771ac18e8895421c624da426b14707eea022d5814f3d774ecc970a9b1ff47bae53a335b230c2f29334f279215860b96d5c4e332dc5d521628505256bc95ffcaf7e6af856188f82b31088981c8aa702e18474e5df8f3ecac017929b8ac9a6a8f475ddd59c408014b4c8129001e5d9a9a60c8c13482d0837da1dd4b39c69b31f0d514838b6fae82c89e06c572d35b1b48563839c0992ce91307b4d837ed609baf6afb38d48e7972b8b5b99f91fdeca4c830613e7f9b72bd083efe820181fc523820f15ed63fb0822b9230c570859aab904fd73648275e5373ff1af6f61712b1c54db6d978406694e9b6038650f79671116f91705532e3dc721ae876dfb1bd00480f5eb58caac1171e79fc9652aa650cf76380bb290d6cd3732a2d080cc997d2da545fde1027339d4646c4dec09214ce863c782bd94e0a31b28b4752f7941d3a92401bfa4e37d81c61fe0f75e02162aaa21eb2c66e4dcfa0bef894309aebf66b8962fa9e8afaeafba81a283d0125ca9f4cfef741def31d6098c96fbe5bcb15fdaf3385c768f2f8cfc183ef6662a46f7b706b2bea3d914c8e070012297473589bf3e9669b9b4767073c80f7a7626f3ca968a920ce519da4e4f48f62a480065c015d4865430ca207d6582947f7403bc218272d157440faaa13a39e22f45866cad2e2dfdd09b7f7a79b1b21e0dfc0bc45b781f6950f78d4631281d285180ddc7579f12d53da5254468947f88c64d2404ca4392d6b9483901ca83f488b0eb99c6d9e61855f940e349618bb4cdda120cb9fdabf9a482fe39eabdaa2b4f8698a1085a949dc6ee0fc4da4baeb3270e7a153fc2f4912de3780b345be03f1e15bb753897c2a0c7b00b7e6a618c7b3c20e10439bcc07d2377b5dc924375ced34e7d7cc8ba6a0d1fa6697a457dc6e612bccd0964a3ac2316c29aecd74957ea9bcb5f4a70a655a1999fafbd95380d763c261813f0c6ded6f4acdb425e3047a64f187e195afb7aa9399bd775e29d3578af140b224bc801d54c95d3790079c8393ed98a7bc3e5acad2cbea47887b0163c1544a40966e04884b239de2dac9e8aeed580e5f956f8a8e1fb671e75d34e5fbf0054ad0a974b331ccbb54c8e8585d5eb41a5ce017860bb71135724903392d6b2f0017819c9ff87ff6948eba679e1bc202394505e6d368ee5be6dd25b4439c7f75a96501ee54142e584831644a3e8241064c3eed654b6207aad54cd6d499674b43450d811025dac2d3557cb347773d2bd3295ee079df0f1b3efc2fa0a9d82cd1ed20bde2bd6e34585718101ac22e513c10675f3aa2f9bac70f9f870566b54d8ce8a3efd2b60b63b6b6e16f8dd1c78424aef184ffff1a53ad572390d8edd42e12c6e5e9d01c2c362010c018763f2154e1c0a5a03fa54832572603c918595c280962a7d2c01d9259c30854eae881636f2256369627f6f380217b7aa0801cb8f5d486fff811a30fbd4048a0c81d9560ad1462dc5981aa2d8e1fa9c90f2de629b8503a127900b2fa796825e2c150ac4fd3bd7d195cb67af638e3c488b981e0312ca79e038041e241d9cf8886003759b85cd3c669ae378e8ba9422ffc09858b4f2176bd9d5b722c3f11e8c8f7cb8a3d231024e60f79258315938b9f8ab8974c1f0221e8571e00c98ddbea1a84f571064b2477455777ffd10e902a33dccf434232a62c946e557076ff7bc613e5111167319e1b3d2234b3da40a8b4d49e00b4a5d23d953bf2fce089f706c73c7b0e82c212743ece53b7c96cbd5445865fe705539dc7dd0ef0fb60fc255d83f113467e1d8eb4da93dad481732cbe0d4b4a9769deaa83b6abbfc84149e3502435b22304a721cea21100cad0220b078d0f93800b8032d73e592f88e2e3aeda6320f26cd7c4ef4299193396343c12bdbbd9a87cb52b18f2e08027716008ccf50ef396033799f9fffbe9d32766e7d9d3f4d32e6ea7ccb08cec6f2d97227954aef0752083f3ca2d5dc02994622d1e06dfdc7f043b4e947b6ce3e217b7d27b0d8db69361d9a78937e99143d81fc0990dc4dc46583f3cf73db704c2cca2927400622e6bda6427adaa4183ce35abcc598c5526a69e26a3c6dc4af36eb6c160ab48cc09ca6961d808bec4d6084741b1b2f33534553e7d24f630f0bb640c118062730001bebb4e5702ce2fa779104caaa107e1f1899fec2374a7f27764f7ede21a5e12cdb37b4ca76b6951212562a105f92a315b950d8f434ff416b1b9a1b2422542f057fe7d51597880e645ab1714fc229f68002b3d1e269ceeb32969b353e11ac49ca87ea7f88875de12309ddfe3f6cf4501522e68ecc199db40b4001867c123a1e6a293a691d8151ad766f0130fa0bd970aae5ac366b886257423afdd0761a28cd4a57083ebc02a3dabd01693859a12b9cacda074fbe5ccd2ab834b15d65093c17d5ee31d0c180f8c74b42e91330ff9a9de6c39f23c4e7533fb63f284c63304f216f7c4ae3f54a9c627b8d0c4630fa6bfdd917830ed5d3c0235a6e418692237fdcdeb42e7ed028fd7f95aca773bd7573c654e6856604ee0d9d7fa25f6cc91b0021a21d95c9e2ed43c38e92cc99d0858f791f33c7c4096b4073a50a605af92e9a31efb3fa1054ae2b053573c05cf80189cf9cb1eeaa65561a54f721df77ee2ddf01652c0435eb7597ea08e0664181b3f679113845f0e03ed829431541f05d203def0f84f22abe9fec00c6052b290ecd0db4797466aff18461037e202ee54806e756fbd0e3076fbdfc66fe4193483139f60f235dc8f5cddfe98439288a7753155bd0112d2d9810e489a5d98f38170d50ef95978af50b478fcc04ecc50779f40050c9dd0f00897baf1d4d1b06c9aac43016048cfffa660bab245fdada960e4f041af6a74318a9dfea942ab43a0f96759c9202cfac134071297e13a8d2f85ef508175c4f36719cc2a3a4d21955a3402abaf3320f83fede6a173c7414b5883a8f13d47610689ec9fc2ee58b124fa5bf5a35545d41457c16b009cd2a3eb7e109f71ebc8c881ab00c42acab3415b19b798ebc498b1c3082f4569d01e3de6a0508cf2ba3949cf042821c3acf2cb6db150a88b36f7ba6684d39fbfcda0e807dac34871ae08b9eac0b61e5a37433aced9f98b6561b794309d4baa95ded5888f022a8eb1d9f79b2d8e5956fbf4990d68c906bd82638d806a842a601783e4d4597765a60252c2c6119bd618c2ba6d69d08fd192f4a3fd2c3b4c7f010d53cd6e2a7423ad008ec05208111a54223a5273410057b8981019bf1811b991bcc366049469605b42fd28cd0b86bde221037d2b8fd45cb0c9b27e48386a963a84f61dcc58440ca032597de6c3507f1e648311c5525fd00378d3432955cfdf7eb87fd3832624d912a2696d383a5f0090b5ae652a07570818d0455a505c3655c003e79ed841cd1a02119556b5f142928ab98a491ca0b486300c0e126bf9308797faf4d6d4f096c8fed301fa9aff821908059e36c2a2ff6e783796e33140ff21e46a3e19320a162c0a80b574fa11c5610721789046457a8e122dbbc31f9db8e9d60eea6fdb78fb62e002942999bb7ee575ebf42514a77595e1177199575464f7cc47e535f0d0c564a3a1b2e5e37b1d9f7eb0404df692657f7d4d5aa7ac1418d3bb1c0055afda07e6b1e1b6bc8f49f0e6255250aec766ba36d26c78a34c64c379689c4bc13302fc0a2e90923bf1e1915ed0ca385aba72da2f48566569f21a88069fa4600df845309ef7b137e09f44bc8215c0b31d1191797b2a789fcdc460bdce0b58eef3ec23e7f46ee09257488dd2f164e17a31fed823468e23e7791d0897daf3f937ffe7d05bb55511e1c417c6f2fb6fceea5ebfcd854a23ab5ca8fdc7c04908d8b81e436207dbb1f18da479741bd2883269c262a870a23bfc99ca1a714ddcdb769d695aa747bf726173a91cf40d052149374a8809cb89c2ccc1751c07e2b2bc4e286774bbfe4b1712d38e2106b4546a9af90a3008d709994d0adaee76450273eb6715ccb2ed12824070533ab886efef8291c861e327d1198221667f380dd075a86fcd21d5f7c3ff7032d213496e23fc2845413df185d31f73a6198904c94ddccb0fefa0f250b599b0223370e91052e2b4b71b6133793f6f6c9a5f313efb18f2e88001e561b480c7f128af589420a68ab0dabbf4118cad6fe88586d6759c1395cb199cf6590a1d4fd052eb2aab72f8746d2147262fc3c1a64235a45ae1d171c31a917208adc36b84013fe1e6b33fdad0187aaee03b96a3626e4d5006bb0ffcc91d1d57239ce89461e0570641c01456e1c92e19a0054d9c812de26ed8cefee8f1e29903867aca772a11cabe48a842659044e170f37774282a95dca132c110aacc8354be42019156dd92a790c670bcf81ab066867a237e984e6e51b28f0b9be32323a1b76eef7d25c4d0e2079053cbc8ba35f2b1823b437a526f482d892c240f8e8258eb73f29d77f700fa00cfa645e659a661969d96c5d93a03239997e24f2a3d551f74804e235bbc12117b470959358f27fc14086c9d9494b8f6074e98cf99ba4d98607f8054e01c30dd270176d233a81f079282ac2d90d61e6278c0bc08fbff1914b49e432e0039b2b9d3af036e3554540363571f6cf94384af6a31647e2416da794a816dcca9e930ddff997ba2d5d6ca43b8b27ce0e5ef64097365087bba89d904ed998f250f93f134d50ffec261e5d928b0e7ea27594f54febe371db232923a7dd799d8de591cf005909f50abfb4f11d0af2edb4899ca7bd57cf2ab1b8172af3d1cdb4b32072121ba1274c502ba63577953f92f9dd4f715fa87411d6501435ef42d64c3b9941c11c5104477a3a7c216138f889ebe0cbe7ad8687c9cc998c83a0a5d64cd37b08a80411ea0f65a76c75969c095f9103dc1d950454549f95eb2b4139617dcb0c7d9e21ff3f9786b19ff2456a4c77191118917a82179807d42e4b6a03ea7ccc6ce3462f124adb34301fe90c49423e41e8a0f135b76d6653a107f8f91c9d8a3fe14874283fd2c7d4173367a0a729b8c378f396ee73c3310dd807b80a12c3e1f50e76bc235179e6eade026a5b94aeee06760fe269c187c0ca04cd22f8a6933f617ef55221d07bbd98ac54cd8d7e4b63980dadb35296060af0bab5cc9969a7042cf6c8b0000ddd081697143999fb6c1e12bc4134602fdae6180e5a69a1b54d203a0e086b272e29940a796957ac570af02ccaf8e1cbd94013607d2f7bd4be7c466986d14d15b90f5f649d3da06cf9a2d20d684075971fc6589466ec9560f78f95d63af4500e621998656153ca8ba44402b31b19cf7eb932adafb783547e2e019e152fe3361b7e76c63017866dd5eeee8c7ee7c569a1dee58a78b8c3ccb250600c9aab1cd706a210f1d7c892ccf6b68f6dc42c86f8993a9634952c4c9cc3ee0184a64a28a60afcd2605a76a47a34885bf7cb398e70dfba201aff2221f55345464ec01bb36a2ca81d2a0e0e09280964d5c06671235fb5d3976aa2c31f8bc74c0e3f6f9fba500e7650be7a2791e8f7bc78c1033f6e3e829af8f5664e7703f0b48ad9d3e1de8a15f7812ec86a5d13fd88077241559c19a4cc7c174741c6f091405081a809562b1d7b74b4b4c7fe9d36b5d542c28135a6072ffc5ca0d746fb98588ad7d35df9e618b80609be85aec7266ff9bcca42c3714c9f9f2724fef38f698e63d3139c29a6", - "public_inputs_hex": "0x1d420eaa08a65528f470fd3e2a802913b611527f41fca008a0ad4e97475078032a555ccad424037773cae7569cd26f46467d9bd96c32334af741c911096761830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000022197e95de61c6c2844727c848aa819fe89bff180954ba01135f7e3d285aa3d8e240e1f1fe5145cc75fe7b253dd853cded77fb484b8be9d52aaf0a49098afcc7d24e0b65d370477cc168e835d8143177b582294e4a47357f372f960754b56b54f2a318f2fc85748ea90c49625631654288c77791d8dd0792e201c748ca9863ac417b852c2d0067302ffa20e6f7068292fa3304e6e20e60a0c51ed55a2e0c2a2a615de98a2794725029dd283936e25dbbb2d8cf00dc4401eac6e834b094e5efd8e2bad40c7a2fc975f2e4811607e4d5fb72361dd1af1bb2364be799270aab5c6f623db21355d312ad82ff6334e39518d7aec226005c56d124daf506a44749cf370" - }, - "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000d6343409c0afb816a000000000000000000000000000000000000000000000000e0e3142188542d9d000000000000000000000000000000000000000000000004bdda217c121af9d500000000000000000000000000000000000000000000000000014a5db1afe856000000000000000000000000000000000000000000000000ee178f82f74d1cab000000000000000000000000000000000000000000000002e8d0a46d30478dfe00000000000000000000000000000000000000000000000207560c01e3450dbc00000000000000000000000000000000000000000000000000022788c7db5ddc0000000000000000000000000000000000000000000000099ba154d1691f0c0c0000000000000000000000000000000000000000000000077a57fa84b59575440000000000000000000000000000000000000000000000052b18c02725e6860800000000000000000000000000000000000000000000000000017e9657c0fc1c000000000000000000000000000000000000000000000002b224b4548754d742000000000000000000000000000000000000000000000009134fbdf2459e11e8000000000000000000000000000000000000000000000007ae759187042674660000000000000000000000000000000000000000000000000001b7072ac05fcc10fd82a9f5e13726dda894346515567a79c225b241a718f1b252ee23cfcb8dfc134749cb199d7dfa121b0233c0bca54919eea35871367be619fab7d4acde565f21f7d05e4d6075633ee1781849710d0cc9f901e277b8147885e33549518fe8c9235394cb328d148824ef86c283f67c33bd9d74125ce290de790d17252755f12a2c575b654f3c0128b45fc00733497bedb1ab89208d38b1062fbdcd9874d1a1102203a3af11c6d2beeefa926f37841cf1766ee9c1fd8fb937fd7ca88bcf091a3b0d693f463de1bc4d89d66c527f61a537f8c5e09d43384776febd12c1a0b160192419f01ad42306732827a9e2b737e59e99429dafabbc4b63fd1eac9ac2d8f50324e028c3bfc9cb7efabedc43d2c85fd8454cb2ac6edb769ec5aaa71a6813ed252ea7767466292e673a4791482b20189da899063637b2fb233fe7413ebe14ab48172ebbec7b1577ece1ca982dfe9bc31189268e16381b3452830ad955c85334f723b7cd29f8459a2bdce9108984d2445710388a4ec18f02e22446ef9281b115a80a16fb591cb108f0398b7bdd4b096354d07245988cde80562a2e3997dc8d77ac05bada7a25ab6675abb5c42348a44a313a519ade461c43aa3993e73cfc06472e252e3d75992b831f25d123331077a32c38e1377c7e7e41f360cc4f9cb84e70fc047c37bac2870a10065df9c6c9405302c93374fecd0753fc9874bb9889e7e2d8056c734dfc773c2d255b48cb1ba153afd8e628b1a7fcb0ca36233ed95a92e6960811d86a8a694956d28b0187c66038d4aaccb51e0a164bd42d83fc40fb48b8a61af5e281d9149910f3494c80f943107661e722113d7b9a6c0c1deab0beae72c60ec45189b66a1679eca9165ebc7a629d2e71e0a594277d0fbbb1379fa345c9a61c5b614ff22f7abbde64cb1c7c49431e04559ba35828a18bfdba63c1169090a409a9f16759479cadbd816a6eb1116a9a4d0c3ad1b5f36f304a97c7e87643778f0c38118f46b63bbd48cf9b4e18bd76663b71ad6b8312ad607a4664b2f47259ed009c81da4d44d2d89b736a3ca86649ec926b8d9f5ce807ea302b1ecbc0349ba0010534881c9a4b14b496d7dd5146e97bff9978f74eb7cd73dfdde685be41267606e74867136658c65220b3f1515fad8fc3d291f636b9ba4a03f4e288464e246a17c606205cb08af4feda633d7e7358b8a8c1e3a9bb7cc3e0089717bdc29914ea0b923cfac7c47dc0b8d04a4ee9071fc9317c266eec4ccab59ffae46a9cee283d05e0a63d50303298db10d4af510792f740a8bbd35be7057bf1cee727e79394c22a8e2545755f8a0599a1e150671f54850bfbd1743210499c84c3b77b4d1f66431069cb273078a2e10f1f015ccc54118eae036e1947b49f63342d2e5cc69ac3db0e6346f2648d7fec2da28cbec5afb3314ceda07cc2c3ef10078c086658fdb63229c49d8545542435bd4d3b1696c001df587e38707a12cf521214825cac7af47d227e93ed63f59ed33ce031cd61931a57bf28f14f457a815be7267cc3c210ed870dcb1324eecc54a3357207d5d9d2c42815d5e7b2a65b4c9d5f1e208a9ed0a6b4235e122935a8843159470d69ebec0a3383369dee35693d475148f18247247c071a06a44e4197aa7a02bc80ff31e3209552172b66163935d82fd56dc4a6a730ff173fc70ce51c6c9dd197840a1e873489010c6750c0f7c05ba0de148c871f50570359ebea3159661fd9333c2db2b4f5041249f8dd96ad6ecd2623078086bdb0e51b67b47a31148be3689e5bc49025c1182f1ea881ed3c65e72c2944f0928668171ec2d7741c74f5047a6b13a71eb54058979c872ce634aa6b5dc6e060dcc85d1002cd3cbd4ab0b1076ff2360427b4157bf24c8d965d6c836962a84ec8315cc713080b314982a7345e5a38ed87768e95095ab5b39c8736cef5710baa273277ac2e17f55debdc276dc29eb726c7a938b352124bbfb0a07c26605f144d404bbd2bdf149594defc22c1a6717c5147d2f36dd017d5524107ce84c6e97b7a143e29d52f25022de025b573b9eb662e6c3d09660e4d0b423a16661e9cf66f44eaf21945692150a6292c541bb691e59f8ff4fb579f797bc5d89b3138d8a7aeccedb49729c724beb14ae3f33d99876f08496a48a22de7c436ec88364ba02e10bf3c52236ea913e8426d7cc3e2d38c861f8981014f91432d53089ac70e114b86d4a60d150e51107bdebe9f1e40b98059a4313e5bff2e5b3200c6ca3a0545a8f115bde4fb7d7107b2010c1aa5c9d5ae36db4f703cce98712fac6a633da3874a71943809c40cd7260a628aede79ffe3799bbe5d49288b947d1d067f4ba336f26bd7862510ffb4328331991a2f89b9f53a3620b32bc7bb50f76eb9cde2cb2f9cf42c4a22df0c0e20331793e2f642f142bae542cb7e2f0fd200db0b710d3fd3c0f4d37afa38b8d501fc34640655f2776937aefd2c0c15fa5dc91fa5de878ed805b700436d8ea62ec2061cc0c19517812cd43acb92e01018dd857565ff19d40bb8f09a5f2753b26900f1ee465a4bded176bdb8c4cd3589184c8c80c0b95cfa3484ae2655c46508c4b1e06aa30e32f43f454846e4dfea668903fed3bf23762dabf7b4d7fdc33590fa3010b8aa4bdf244b628b32e4468ad96056a848209a65f1c42dca8318754332aa109bbfe7a82cd199fc831c48b5439c09209b31247b258bb665d2190407ba46dad014ed3c854628e09e8ba9da2c4e2b1e658a6b56fc772a101531810ba1efd61040ede249e8bbacc5b7f064e3a0a89f98d64bccfa1d47c52bafc19f615c4df0fcd059b763205544ba63668706829151458142db92203b996a171fdbd9dc8016f54105b72228df5503f7a4484b4e99320c03d7d8d384e83ad9cf5b9ab322a9f81582f48298a3a781a02a0fb4e6a7b116dc94f11cd919b909ae8fef369dd4a7498252a0c3cbf87c8e91ac7e56d9bcf6ddfec337df1e6f2caff2fd04489a0e15f51c00c8bab2bf1e204b2516dde8c6212fbc5d519398e1d7ffb0d3ced8ccad27638a0183755e45c97ed7e8496714a425b0683362ef4cefaa0acb128464c50054393010a3916f001a49abadfaa9c44052829ab1373491eb5afc19c448cc2ec2384cff003b6ef542acbc10237a0355395a4828bf9c356e66a92f998a9860477393e2ecc09d9a7501d79a694699ccd39b0fc5f7518b6ef187bea3938e24ac1e649107ae7134b4819e221010ff3ea8ca2aae7cfd8c913d727339e5e90d8bf9af431f2767f229d00d4bc9a9ab2708b1a268395b66e88603b50f8ec614b1ee8f042247191be099258078233ddbe8699093187d4b87238b8503be1b3a992a66efeb3c15bf46e114045770bc9dea0af8d1b3b9e7f89ad0f0f7b5cb58347f9bc31a1f5b749125503095d5ea015e6af870476964d02d0836687d9e205ff986efa3fa05eaf42567813be91e7d92751fe7dddce9055502b2beab3e4e3b9987491834fe202b41a16142628026d775db7141e97e2d4144abfd41e23c68dac14669bde63aafd40d4a8002c28aa160a0b5b1f57bcf17e056e62e9a6a792e75c194faecf8edf72ab65904b061cfd14a239bc808454c33b080a44af18784da9335cf5d3de85828c5b2a91d113bf19bc7ab5f87b921f7b88712ef3895fbee836d87dd311efc22d394dcb758321c9d1a26147d519764b79da0bc929d245fed23e95e58281d43dc93a611641ad104e9bb58a706ac4f9212e22869a7d7d816c3afc5f2b9275babbc2353dc59c332db15dde7753b62322c8bcf8d41b72e3bbe6ff0a2c61eb466e5512bf1a8be499301fe18941e16b2122f0fc390e191cf3a4532c849b39956f3e318c907163810611034a1a296a7bbe66780ca88c6f599ca09c82d83d70e1bfa4473776b5c068221194dc907822535370338f889825f36a24db7a3b42168201e5e80264e41e7569162cb3669ee45cf5c0810dda88651c91cc32fb2aae51c18f55174129409892050248d04cd731434d63434389f80310114dc92993c9b39e42550954633cf31d241754f6dc46a21f7be676f914feecb63bcfa9d839638e8fd140acbbe7e494540a1da5b4f31a302ecde922b1a10aff437aa96759c3059fa958a3a6e2c50361306d1c4a7b7f63a4abe9b13738ccde8195d2cc4fb4c3c85ad53c78dc8496283d0cc601396dd75bfacebad9ded60880f37b46bc61c87724c7b8c0513f1783675e4d5018f7f30a9115e2b95cbb5052e03b3111d90798ce5272e2c6228c165fce6c347c27711e7de6d91d0b6bdf34677334c6e8ca47a60e0b7bbb5a0f01a6724174d0cf0f38bdebf434e84d92160285043af5227e5f628012786f658f53dca24e3d3cda0841e6a5b1c9be5b9b898e5e71a0eac8825cf7d8b4f38fb0138dfda4cb5a9e5b0cc2d6ad4868dfa422efff32fb32a6535891e60413f524f001d727d786f2970e04e0809642af932031d727274d05962dab59cf469ab5d400f6291950cf78382218b051a65adfd64e4e81a5e31bc3dc22dfe95a810750fbfb0e9bb9e230aec7a3227e15527102ce7d9462df9e992926f691c129f6e38371aa044411006660b45b22ed9c9c0335ccaa9a3771614eda0ee18a5b1293cb713f3e4cf32209bf2b2be91b52a3e0461b00b50bc24212e7a0a168cd4a5670eed7735141ffbb2530bb11151d70bfc2ddd7bf86ec678546a0ba985b6152c5b427e31c612f298280263c0f312db2b751667a6b85ba0fac6656417340203010f463292039979dfe7bc30897160e633531a0d52f9a326a3aee24c35fbccdaa387a801db8f6d9ca2cf6b6641eae06aac300aea7c12bf9a28f3b667beb873af6f9caceec3fc3f0616dc7f4bd22f4146614c7ed9acb39a3915b92ac5b7787a877f46033e414b721b49d8d27e5ab892415cd864a75ef2e316f4ba10efa47a4559fcaa336afc1d9414c0204b98cc0a70626f89136564bfec778ef9807939900337c2616739d4a50f3be2c71830968412b0790e7e1110b375a19e3f5ffc7b3a605e187f41bff4a6283828996fd3f26a213a28f6e3ad328976ed2b72d62ad54cf51d8215f993325d697137dc2a4c1e3c80f6462158846588d3f5b8cfdafa9b1018b7b1348b150b76bcdd0640f57fd2d862dda4449740d5cb065e00bc50146bc70b3dac6a6e9103d0863af505eaab860361ed7accae9e15089da9d1d801ae35817a5f67a28a604771cbf3be04ebc9c20e51368750ba4cdee1f00fa042d805ab474446a90cfbf3fc97a20fe1ca5e4a6d588266864684004140b5150b5cdca08014cec724f1aef668d2ba67254a53f2c59d50ea76d17c1e2160bcab9fa7540b366abfaf5f8413244eaf950547f7571cc057729ea749b5fbf56ab428aab3ed476cd4f68b3f41eac270bd6b29fcda4f75626071b1a20be49701c43b4b16437ccc9fdf48a62fe1a2663a68c352e147f4ae8844c2807157c23509fbd64ac8ef2db22655f9745461382cf08b2d3cfdbf28081198a21ed719565dc566fb084e14c45856676c2a3a981576fad4bad652a20e85092a128f13027717bcd2bfaade88737b4ace18b183076e1876c817935ef5d7ba8b0622f6b2441c65d5d4c293d3a0506639a43c5d085c2df2b838a27263902349be4951f7f12272801748a9b3d4245de5e60f77cbeb1454b069a9f476e59bf36245722244ac46a6da45d3406581f44125c5661200e0d92ebdaaa3d14e973d2618ffc9b2081a5ce7bd73efba7488ec506c08e8100329c8a9f4db5d1d94165c5e0a21f202a8aee42247f7d031bffb09f2d852246350848d82a5c139169e8c8d39e6178bb264a9dd76aea3c0ee71750c7a9287741eeca96cc3e2418426d0e24d5665a4dc22cf017f7fec4b71e1e288e9efff6882b3fe1b39b4a527403b8d87a77b1f7b92511ef72f0386ad6da5c92005b739f5bb3c6aa212453e8e6d343ade9cc24a752631e63d16b0a0d0f0013302d8a6b592cac45f107cbd67975e6c4bccbb48572b6290fdbf02aee0467e7e24d7274351a3fd001627678697c6d18fb80fcc5cdb2b79e294219cb7224ebc6a5ae6ec05376afda73fd323e700aea6553a139f1bfaef34111e8014ae15ce5ddc2568eac8f8621f643468697c0f957c13d13de82457e4310297c09ac3934c2a272a10621472fe0deb9eb13dad684b66f5b19a420d8c010a82fc2de64ae10f7ed94fe5a610ae0f8889fe1d94f92544858c65ec64b95055ac11c4652efe064f85ac337332ad95955bb6db02f2d11040cffb5d6a8f9e632a93b14045aabcba545e42a03bbdb72421e4e08644e587725d0bf29d3c8a235bab183158dc24705ff40a5f24f8a0ea39f30aef722faf69af92a500abef9e2f26a2b642d76adb78616af6e74608fd27de1c8ce87a090d832f57e13ec2534abf164ba0b2be6d35cbaefe0366ab88d79d0f152a4cbff4a40a4dd1585cd47f7c6d39492721b4819f391ffe75d71858e5e7f3b7040d6d363761eb4b596aedbba067d8c4add0183eb7e4c92d2f449f9287ff1c0a8062ca1d719ef98e276c98ed954559047ec1aac319dd0d0e925fd34e61f3b450c6dc69cd7f50d4df31733d0652aef9d20d91b5aa21fe2b9a3c6915f614a7f1616959cc3fc32c5187f0dd24b0f51b32272ea072d7e084d62c3324dabd4f1985dc2c0aa88915960a5c1785ca8683207dbe4792d9d900add917b458ae42c0c8f66b9c414c484644e56fb083a7731d336d210060d5bf4c1b5f807c4692ca713632bd87b26ead2b7182937890b091a15f42d6ca2011026280c64f222ffd8629a17136b4c7a6fada3e2742fe377434073a9cee2bb03246938701a602957d1bb0abfd402035df562b6ac784c99647ac645c7958366136f0024347b1fd8bf330b4d697b97838393c0c91b82582343eb58e2f9d6b9c82b1387eed1f819c4d9a45a7e4d1ec5f2d2f0a8fe5aa96705c2058e1f62b5b2d5254c98a089c8f5eab8617604beb94022770aa9c76fab0cca170d5fdc2dbd40e628da831293d1c24273d9523a420bc585f93d4baa497e68f258121f9647fb2ef90bbad294f96003a9069d13d95465fbfb65362e6c70e61b6056e5b7053477fa0b17f609298e716d5fe9e20b2e49d5779a225f535ffa20c88bdc387a1ea66864d62571705f8745c3d21e0b714826d23644b10d5f3c166f1bd704291c20e7f685e8245e007dd62da8e45374824a7d819c48aeae8cc9beb2988bd8cb3cf074c7af0f2ec4fde771894a79f484ddee1e2af510dc80843c64f2dd12b7fb36d69a4b323b1d0aa5f93a2ea384df044c2132470d4c5aefe263fcdf47e68e9d26e63c81dcd32a389e21e2d56d771c823017fe31533809c3b5d3016afd6f95329ed4db23daf0106cd2e9f2addd7e0f013c1a32c3d5eaff36d796800acb744179cbd788a15a3c037261f29e401178fecd97f8fa01daf54700ce5982b1d58e025ed6ecbd300e7107afb7af7e7ebb538d40acad316ad54db0bab5ada811e395e0ebe6cf74ad149629c2fd131d0ef7d2faa4fbb177351dd1e68135f5ad97bfd6651a2c98ca8ef85926aef7180b5d563c5ad7df270d921a79feafa12e533cd9b8d602fea0ae16899729fc9de73eda204ba93c5c60ec0420cc4bc9d06b189045d9c12c71bd79e673c40c7ccd521d3ff61c7eceb8e965c00de62c8a32acfd1f121c0ee841fa2f756f0618f2d4d6ac96d7bd74f67427e347e2e760446958f9115ad9a35b530e801071be246e5576bd91e99ade8d75b12fbd1a43dd83960b318f970045dd374984b535e72bd2ba51ecd03b9396ad32e9fc4a8fcc22b203b32a45263a795786f4fbdc459b0659c4f1cc8261dfa55ea5ab693197366697b6ade5d4340c8192fceead82ace629a7472f3b13c28a954d467990509f4ce8ca0d37464673ad92a13c2715b7db55250d67b3a7e4d9a735099f8aee1544d0e6fd73f0cfad9c3aa5e40b0e2b8962561ec35f1ad2b11aff259d79d1baaba2fba96eb834e76a9c80b364e97b24307a281424aa2fa30be62314f75d116f5c131ec32bbd3d548ac60e287531ee45b88a9911e2292468a9077985e7f9bf3b9d7c9c7f4f4d58a034168a0846edc8fd6e821919bc426b90b9d31fd35ea2b87b4ae485c0684ab37528cb0f6319003a27dcf72e1bc9e5dc54f4a62cae86cedff569653518ce21fdcc660bbd5e7be989b268663728da72800734456991bb11783aa00a75d9d3e930bc9355f763b4c3d040e1dcf01955668b29288a41828a44bfcce4f3d3d409fd71f61b6a69b12757debf0d01692551a363188bca817da75832de2a3115a69e2cb747b0cb16ae52e6ee572c5de717941f01982ec1c364c7cd5d4d6eff0e3b03c68e7dca325c1e804b2e0abfbfeb1c9c593a923554ecd202ce87adc8dd2f5c4866d494f148edbbd7496abe6faf5023d53378720ce63e06078b8d9e84ca9a1d2a65616bd2def3a8bef72908acca0a10543ea597c8d6d636236f8faaea56d55a20c9142f080147a46a88648bd9dc11017aa88cf3adb1e5c174be8ce4b43d33da7b54a2d92514e12b6b768ec63bf0c31f2a9605389352931a3b69a1f26ac4c2163544780d60fcc85795256c6afa280027b06fcce4b557599d94a87b5c481d9a34c331c396df9656fa89ba922dae138408bffe3ac2706d43bd7fb1c8e56f3718bc24cd0b9e9b0ebe94080cb5eecce6480f8c185f442142926ca3172b2e19c03c0722752911cc8a6c7206f3c2912787122662900584b849d650e0c4eedfaf8187238a97f307266eed20d6e0b6ba01b9f20046fd636ffc52fc2d5daf18ebb043f41bfece62514c98854a64ebc2fdfeaf7c1cec169dd44365b740df2819d9ad1aadd76bdf6033173f06ac4d778fa9d4855822dba8778bcec03e6fe23987933ac5841cbc60c642f2aa3f59f1eca01f7802481358b76b89c085167d1b82e5e893efb5c368bdabf60c4d65c8b526e791bc70fe24da05333484e79a43adf9468afc8a513e75578bc37681732417d87d3d1a1e5a1d7680fc28c20793b079d2fbf91f06483f9faf0d3ed1f7e0c3414ca35b2f25e7277eda67942f851dff283b7a1776dff8760a130b2e4a95fa7a494e866404815d1f8313141a02a6fd2aaf2204dbf452587087026b47df4bde2c459632697f3909173803e820b6a810ead209db8c49720978493b40daa7651c88244627d27ae18420957e21543565fbd8367917180042e123042789206e7a0f382f05630a7233ce23870618eb29d57048ea6baa35a13e7775ea499b77fa2b2939a21948c33c49b80eb128ad72c44ec6b2c43ebc0ebed56c4d357759a372cb724cded062b46bd9a7242e8783d21021596916098b8ba9210ef0df0b8f5065757dfd22b3564c2cf1c62776f8659e98c20f45088864e86e85b8e61803d2e2e8db08c5597633e2884b1312f78654b323134010efe0a5a22f96ff83ab4e467746b698b8d2d68d2937d3132e98b53f62eb12c772cf6b86924ea8990b7fe9f6ca76e36ec6bb71334d5fe7ac0ff44d2a4c39e0447d41ee9144c125ae86e1696d83894163c929b99d5657682c0ccc8739c3c024b7d3b35be279dec63ccbd81e8c0d6e63debbcea29386640dd127985d1724b1ef912fa749d00325fd280af6eef3c77346dd5e2022dd02963caf2169bc8168aa63972cd3c404977532baec258ca30873d54b585b7de7de78081313ad920afd58a88ed6de63c31e93a3c05693b27665cfb4daddccb2d584b0c71f0577d99ae959e0d3295f3b009a1bc1b357cdbd35bd564a90c10c57a52c4d5c9e175b5bf6cda85e75a362d53be71619e033f18a912ac2bb171a8650b6494f385c23cfb56b3e56656966c1c3ad6aa4ff5fe93108e3aa5f266feb88c3654015fee429eeee7128c5e0b683bf8670ef76f2064c82e33acfa4b98fb010516c9758c24c08287df25152362f76b084441cecf158a6a03c74473f7ded3f9a280283f3a94306a6b182ca1c741819fa8676d33f15534ac596a39d184f208664084e86f69f491355d22dec6c7fdd47db8991f40363b46b90b495a01bb12e79bdfc17c015222c072641d13506921a872ac44c9cc8d108225e2aaf54c7d51d7a2b05ef429931aa01430a19e1d3fff8f96d9abb6c0e0cb3f645c059329e2c08162c99890102ba0d05634e00f98c8be1b71cb16bb2844c4cdcd67eea6259c5a9d668c058afd5889e27f0261c1586d2b4cbc5948a248e38240aaf1b6ee10801c93b9f6bc130cb04460efbe72cb208793fce34c92d5cf696958d7d560e5ff6847c635498a769eae2392b0e3ef9384f3ed5a8fc649537d71b6c540bcdbc695313c2d581d999edcbb7992ee1be226490863613c815b8677279673ed4659589212807f82da56e940e674601240ccf06909e2191a4d52f09a513a0acd557e70ee7bb5df0179cd2659547b10fa1792b0ccca44cd07e4b82df1a2c5e017d2f8a32c0842937f182a9f354a63516308aae017cf8a00e316b678fd379054cacb79841e0801c32e8d34e5b52ea8b0c8cd36caf58534592739dbb0ab2de7572923ec1622a98428768e440b38d4ace26bfbe8863b8dca57239ebb222338467db5400732d172f716e449e63c92fadb419e5644af6620b3258e48e198f2ef956a6856143b35a756ac2e35cfcb6eecc7d178c6515c496180fef749ac8e977fa63a28fe6dcfbc73ddb360ed5709fc9b0fe026c4635a79e7f4caefe5299dd7cf4b0210925079bae38b94a1cfd393c36b5730d3b39ddecf9213ed09c1760211d7c8299f793f875acc0e8a1e444a7c8009d5d0df95cf94b07d8ddea9e064bb63e544b266a94c00d0c91b75697c7164fea0f912983f360a60797fda997f5f370162ef1847ae23ebfff5e72a8e8c719631b7f8e24c08cedaad805e39e5373db066ee5705cf40a4641472965ec2e8165fea29ab119fb1fb534d5183820622a1d4500fceb3ae14eac2b420e084526dab4088f174c03abd7cdf5e33ecbcb6c6b1f09875a347c283cb0920de180cb615648c29c203f0ecd1b1a3b9d78569171956034619df0c3f6bce2442e302b4a7a0d6c321313491247bf62042d00eeb61610243fc11ad0d517806fb5f9ae448e7facf517db96571a6a1af751ac14a6383f96870460481013a08d296259a69405f1b94b9c50af4e262e4a32bde88f8a1c7abf78808893ed9a0b293eece0feb9e5fb3c86cd4f874b0a8851b7ec202d182ce02749dba88d816f94c2304cda0c56573114472ae4789903a9782552283c2c2b88ed75ae3a610b32844cbe7e7f7c74f5216fb6cb6615cc2797978cef7d47016bee6da4c77e9f60453cc17e44724812dc958172c38e98ba2407ea8b08e0f194575cee3effe062c4926bce85ba7e338a9a3fce58d4e021650d19a35f54d83ecaef10cbe01579973ff5bb6c07b29feb89ee137864ad11a2a70cb411e2a5b220b3fe8a74bb8d9bc4b624dc5914e52b9ace98e7671e0cf5e92b19536b803836dde90b9e384ffa4d590e141d96cd7944b79c6fb3d80992fc108702956f27608a987c9947ce3fdd637773787e0f976bee528e7ff57a7fb94fb361268bd81710fe3a615437e23e557ea5c485b4a6dbcd89e24eae87ebb9ae61eace002385c62bc8c9c1925ad79428be87cbc883c29c2bd9bfca108086de9053dd5a27999b28a3b0a8e41cf1fe430c3a035940be16521438e35fe5bbd212b8d0bcfb0135f7b015e51ad8ec8fa8df12cd077ad2bcf7a705889b9aa6674aa84c0428eb1ce7c1fa8d1b1adec22d51cc8a501c42b95c783dc1320c9b4d0fcb6f5dd4506b206f292e5d06fa78f52666675fde9819ce5dba63fa92ea8a6463422461f58e622d8937aae3718f509e1cecb3c95c9c82ae33ad610f692d7df28bffb8ffd4b3e4039e63003aa372590c543591e7d89ccbae71b74aa468a70da3db0e3ca463b15f0e6f4cdeb5bbf5ec3d736f1505610aff03ab11756a701f5b77578daf8989bc322dd5c44335a39dae704ad3f23389a598b98b69642f6cd7fa1830b998d64dc6a3250d7e6609e26e1725a4f6255ffe0e73e002b4aad16a1ab58b3fdcb8a26958830810ead864d8a658780beb619fab6c34e15199c1197968c99b38a5bf174478d413830e0056c5c2f967af9b3d26fb050519d4800bfaec15e7f13a94dcf2c5a2bd1c524d5ae0d73b30261003402d404f8e596ef73e98c0c4a6acb82456870f58671d138fdfb25ca284ec0793739a7cee577e6d3ba319efff2cf9f22b90f604e4f7014c701b5b3651d06ef3f3c3d566842801f26b1a8cffa425ec7aeffe6c1205932807754ec92ab14d58fff617ef19940a7e40c95ba9414628d59e6ea2fa9cc1140dd37f315857802b79f5d1b31c3b076de22ae95fdbbf2850886a0c671a67666a0b789aaeb6190fea5776179f028c774445d9c61c76dfe1a263c30a0ce5880bbf04378426e45dced169fdf580d0927fe256cc195db8122edb0732eed159a32cc72cd17f0ff0775670ed47492ab73e9bdb1646d1ddf6fac59a2f1698dbc00da9fa24dc2ab6ee98dcbd0682385e98342ffc86fc964c46b89ccbc2e990c68a761169037021867fd84d7db8c3a74c2a31bea5676045a5d33c4f525d51c11e01f58d200f0fa35b54f19c81087074ca714fed679d10f181f9cc2dfdc8cb1ebd703ac7200150fdd527067f0bc99b5b785afcb640874b3f62159de99771e6a86310818e5f2fb0169c55188588154761e71ffe774e208c335942d4dbe4aadf295d59297039066fe4bd1064d8ad040ada4cbf1f5cbe7b80e29396dccc2e0c93e4c1e0f373db107e57b560504f1e1bafce78380cd492758e1616d3076b68ada6dd12ed8adbdd14e14ff30a2ac375956f6087d8a68b13539c5a45d2b3de89d08a7db022f688050ff38bc3ad29e40c07a6159f119da20b870054811250aeb41f835d074b948a6b0f6f2f89422d726d91317c14441ccda2e382d0cf289c03d98aff6b3d34934bee232323cdc38dbb69f1c9092ada5e3a9cf8aa6e130203f1dadcac0ab7066a3de015dec2ab95501c4ce019c7cf444beb5d20d0e31b74e319c170c52acc6feae34009bdff8a7a5e189aba127f8d488ecec7bfee67c0e4da40da08e1d216c95e4e531569adc78433ec670379654df38bd2ba311657bd0273f3f945c45f7c9883e9042040c5b6cf63fb1069b8e0046174d371d03eb6cdd9029575e42b419a8b72380122ed3efddc9233f58b6c2dbc2a694d2b79011a794943f1a99e6603632ec4085927adb6976fb676e768d1ddb26b2c88158e8cba7b666ab6a20b030432ca67c8eb29c3b535066e0f779fef60ecba39001047452fe610643fb4491c13b86dbef2f409b504bb782009166f4d3069566271c6cc026ef64cee56c321be4aef2e665d47030a7fcc5527f2235722880893ad58c7dcc873095fa719bfbd937803dcc2788302a5ff8a658c1426defc4915c2e49e49ac66eb4e661d97eb9bc1c19d03228a58003a38e29ad1f40ecef9edefa3c9d86d6fa481d8bcdffdf1de0e419883f488bf2684050e1b465beca794930b7de04f78f0001bb18b954352807a99200f935f08133200c22324dff58e9f920bb0e54c8781c29ff6466cd26ada6ce05255248138001cd0f79242fd0768675144caacff113ce2d470a5d98f9ea44b6fd446e8511c1bd7c61a3d26af3415eb3532dc9b7b243b34752c63ba0bc2e81bd6000af3ee3e2f9409ffd485289bfb3e1ff44d8b181e081f9f948aac37c0d7d535cbef1a42501a0050b1ace19cc1779e23acc5aee850bdca8fd5591bbaa08e5b94d30df3f6b32a05139aad16899b7620a46d0be03c740e38704090e4d3d584aec4eb2d56f39a1573706fe29dae924284cffb950843d5e3090b3b59c9ea76a1a2215bbcd869712c7129bccf788ff4ccf5d9f28673b69788488553de67c648560a49f98c9054a30968dd170b7f9ccdc98147a7b6b0552bad5bb39a2eea0dfd942b320ddcac5e382ee141dd1b89eb46cb5e9da7537f63b77523abab01e9574ad161a911a896b867123735ad4c98de40f373bfd62165b0c737953c8c816c27142cd245917969bce31163936dd6680031e303c4086e9c8a6de7195c51fcdafaa9626f4691f201eb851e943c2dcf5a5e90005783bba67b6478fd637e43b5dfefac385e32f640c2e3fe298873ade499d92cca544769a88ccbaedb5cd4eb5e8e7a431a7b10cb0dce44f411aa3a22be6af7f1ef5b93f96b39c28a818225c7961dc5879318e9be4af78d59150260b3b712c8c4c615e4f0558d5d7abb9935a34b4e8dc050990c4ccf01a0432e2397266aaa7a635fbf97199e3dd6cb8ab9ceee4d1843f01e42df99b7b94179272eefb5e7453450e17b2efe24e3b585fe8707101d3d26e7d169e7e083538693066a2cf45e25626f4766328ebeb180f4c9a80d5264794e3395ce216080940d8b2f6709d618e173e4c91acbf76be013ec184526dd9d99b2149c441cf1ec1c7daf170670d5df092b15e582daecf85d140daa23aeaa5008183d5ddf926b0c8ae0ca22a9c8e4093ad0df5874c69c34b912671057b6afeda2fd41e0e223710ba8eb4d28afe45de7a7bfdf7a5d6443da901e2c2c966ef093056c4f62c820c8a0bd8c9c28344c664db4ae049971d8f089bffc11fd4535874c046cef36fa7a398b3ee6410b62b59f2b7c91d2a1d851e94ed0af4367f3a9aad84e12d9fff68aac9fde91bf2223dee4ea05d3500dfaab6f91203b3ed3f2346f1462240763462d1db99e8bd205ee3afb116f9fa13790c13011b967994cda0d7d65a4aefd076870bece6b2b3f2ab8907218701ef6694146c2c0ea8b1d7f40e95f744b957dfc325397e7ef1382", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441124e9634ac86d5322a89fe272dd95ec8f0910e46f753f89c72c622ae351ae9ed29012c2b59ed2e47dbf67687e99852bec5d63ca1680e3ad65cb5c747a9c37eb900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002240e1f1fe5145cc75fe7b253dd853cded77fb484b8be9d52aaf0a49098afcc7d24e0b65d370477cc168e835d8143177b582294e4a47357f372f960754b56b54f17b852c2d0067302ffa20e6f7068292fa3304e6e20e60a0c51ed55a2e0c2a2a615de98a2794725029dd283936e25dbbb2d8cf00dc4401eac6e834b094e5efd8e0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } + "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.957074250,"runs":1,"total_seconds":1.957074250},{"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.902250750},{"name":"ZkThresholdShareDecryption","avg_seconds":251.230740403,"runs":3,"total_seconds":753.692221210},{"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.273315000},{"label":"Committee Setup Completed","seconds":20.279577333},{"label":"Committee Finalization Complete","seconds":0.007173125},{"label":"ThresholdShares -> PublicKeyAggregated","seconds":5158.129704250},{"label":"E3Request -> PublicKeyAggregated","seconds":5165.210807791},{"label":"Application CT Gen","seconds":7.707659625},{"label":"Running FHE Application","seconds":0.071059500},{"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": 1 + "enclave_contracts": 0 } } diff --git a/circuits/benchmarks/results_secure/integration_summary.json b/circuits/benchmarks/results_secure/integration_summary.json index c0ad6e144..1d5dcc6fb 100644 --- a/circuits/benchmarks/results_secure/integration_summary.json +++ b/circuits/benchmarks/results_secure/integration_summary.json @@ -8,164 +8,164 @@ "operation_timings": [ { "name": "CalculateDecryptionKey", - "avg_seconds": 0.599410291, + "avg_seconds": 0.614281708, "runs": 3, - "total_seconds": 1.798230875 + "total_seconds": 1.842845126 }, { "name": "CalculateDecryptionShare", - "avg_seconds": 2.123328597, + "avg_seconds": 2.121472944, "runs": 3, - "total_seconds": 6.369985792 + "total_seconds": 6.364418832 }, { "name": "CalculateThresholdDecryption", - "avg_seconds": 1.93815725, + "avg_seconds": 1.957074250, "runs": 1, - "total_seconds": 1.93815725 + "total_seconds": 1.957074250 }, { "name": "GenEsiSss", - "avg_seconds": 0.755760708, + "avg_seconds": 0.758238652, "runs": 3, - "total_seconds": 2.267282126 + "total_seconds": 2.274715957 }, { "name": "GenPkShareAndSkSss", - "avg_seconds": 1.230714874, + "avg_seconds": 1.242666944, "runs": 3, - "total_seconds": 3.692144624 + "total_seconds": 3.728000834 }, { "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 18.895682083, + "avg_seconds": 18.979502958, "runs": 1, - "total_seconds": 18.895682083 + "total_seconds": 18.979502958 }, { "name": "ZkDecryptionAggregation", - "avg_seconds": 48.057542125, + "avg_seconds": 48.341644417, "runs": 1, - "total_seconds": 48.057542125 + "total_seconds": 48.341644417 }, { "name": "ZkDkgAggregation", - "avg_seconds": 20.904339167, + "avg_seconds": 20.006914333, "runs": 1, - "total_seconds": 20.904339167 + "total_seconds": 20.006914333 }, { "name": "ZkDkgShareDecryption", - "avg_seconds": 30.160204763, + "avg_seconds": 30.277848645, "runs": 6, - "total_seconds": 180.961228582 + "total_seconds": 181.667091874 }, { "name": "ZkNodeDkgFold", - "avg_seconds": 102.337581166, + "avg_seconds": 78.310650236, "runs": 3, - "total_seconds": 307.0127435 + "total_seconds": 234.931950708 }, { "name": "ZkPkAggregation", - "avg_seconds": 49.016015041, + "avg_seconds": 49.050973916, "runs": 1, - "total_seconds": 49.016015041 + "total_seconds": 49.050973916 }, { "name": "ZkPkBfv", - "avg_seconds": 3.839788277, + "avg_seconds": 3.850818819, "runs": 3, - "total_seconds": 11.519364833 + "total_seconds": 11.552456458 }, { "name": "ZkPkGeneration", - "avg_seconds": 65.134843083, + "avg_seconds": 66.056590278, "runs": 3, - "total_seconds": 195.40452925 + "total_seconds": 198.169770834 }, { "name": "ZkShareComputation", - "avg_seconds": 52.426543555, + "avg_seconds": 52.534038875, "runs": 6, - "total_seconds": 314.559261334 + "total_seconds": 315.204233251 }, { "name": "ZkShareEncryption", - "avg_seconds": 112.963847905, - "runs": 54, - "total_seconds": 6100.047786911 + "avg_seconds": 114.608395854, + "runs": 36, + "total_seconds": 4125.902250750 }, { "name": "ZkThresholdShareDecryption", - "avg_seconds": 244.367978902, + "avg_seconds": 251.230740403, "runs": 3, - "total_seconds": 733.103936707 + "total_seconds": 753.692221210 }, { "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.092927916, + "avg_seconds": 0.093863888, "runs": 3, - "total_seconds": 0.27878375 + "total_seconds": 0.281591666 }, { "name": "ZkVerifyShareProofs", - "avg_seconds": 0.266464458, + "avg_seconds": 0.264344016, "runs": 5, - "total_seconds": 1.332322292 + "total_seconds": 1.321720083 } ], - "operation_timings_total_seconds": 7997.159336242, + "operation_timings_total_seconds": 5975.269377457, "timings_seconds": [ { "label": "Starting trbfv actor test", - "seconds": 0e-9 + "seconds": 0E-9 }, { "label": "Setup completed", - "seconds": 3.274390084 + "seconds": 3.273315000 }, { "label": "Committee Setup Completed", - "seconds": 20.259594916 + "seconds": 20.279577333 }, { "label": "Committee Finalization Complete", - "seconds": 0.005869333 + "seconds": 0.007173125 }, { "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 7204.022842209 + "seconds": 5158.129704250 }, { "label": "E3Request -> PublicKeyAggregated", - "seconds": 7211.068496417 + "seconds": 5165.210807791 }, { "label": "Application CT Gen", - "seconds": 7.746761542 + "seconds": 7.707659625 }, { "label": "Running FHE Application", - "seconds": 0.088362083 + "seconds": 0.071059500 }, { "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 814.168040875 + "seconds": 835.140242834 }, { "label": "Entire Test", - "seconds": 8056.619484583 + "seconds": 6031.697821958 } ], "folded_artifacts": { "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000830d9d4344316b026000000000000000000000000000000000000000000000001d383081c39dbc5a000000000000000000000000000000000000000000000000ac70e435f22d64fb500000000000000000000000000000000000000000000000000005bbc150f125200000000000000000000000000000000000000000000000a8a546866290bb770000000000000000000000000000000000000000000000001fd3f2400d703cfb700000000000000000000000000000000000000000000000b9038d2ae3e79290b0000000000000000000000000000000000000000000000000001e3d48d51e5500000000000000000000000000000000000000000000000067a0471dd42faa6b0000000000000000000000000000000000000000000000000e9084f4ea2d5e512000000000000000000000000000000000000000000000008a356e94c183365dc000000000000000000000000000000000000000000000000000254af5f427efd00000000000000000000000000000000000000000000000238e18ad2a5a9480f00000000000000000000000000000000000000000000000bd3b65543dd7dc02f00000000000000000000000000000000000000000000000759321a99ec5a19300000000000000000000000000000000000000000000000000002a525effa8d9000ee44df0d016c13539e3e1de25cb3283bfa0110644e6c3060b51acf455c43f02d5202ac66917f20fae1a39f8cf51134df47884f67caecca680f87bdfdd85ccf0315bef50348a88d4c79a1b50bdc50c9882a2946c73cf72c563058759727f8a009db15cdba20a346d9491fdfdff4ddaf05c2474d9c1f2c4821a5e2e101b7b8e82e181958e78629f078e836c725ba08236958e3445a0dc31fc4c2202707b61c7d0ac53aa35319f776db882a6acee6b04a492720c30b527f6a209efbfb4b22975403a5568ec8e19525b52dfe82dfd9aaab141462570f5c39f9a42f13700ff87603118b076682b92bee70ee8f9b972b8cdeb9ff1fbd603352f945f2c95fd35184f70f989616eff536ee31d0349dc97067aa11f2ab4c8782c4d19a4b89968e13f69f208509774cc987cc232b93c9c49974be00e3ee21c6d743b80d823f9eb1ee13be236cf0e7b7aa0a5a5f4207edc057a37096d17a63c48f2866132c681a904108c02045139ff715913e4d22a696f788d1cb2424d855ef9bfe36f67721de67235cab0e4f4cce9ff0f3588aca137e0740c7dac2a980816ddc38f10be0dd37f01028891d4476773d637a1bee280492d73d68838af602126de7b85d767e835f1ec2f5f7015609620fb34c0980312b5b7f67ba6ad756f683152cd1a95e9e4c9a8fd58bbd1c0b2d262ca7d1cfbfc729424917aa0bccc6e667b7d4e4293e56552cb688b8320ec6d51d706a3d0b516eeac14f7b49e736cb687e82d64d239005709ea7a22af41daaba74d8165e869cf52616a224d38bf14b43f9365e25bdb8f5e3088158cc3e2ec485c6affc47a2b1a4a1d0b691d81aa7efeb0d4f243c3f9862a9b376e7648a2a1f40a23824acd2860b8fd4464a89ad9da06c090a2dc69564011c9749dde2ed15a551e0c27cb57298106bdf016afc08b87f0ac06032ee6860c10be94821fde507c4f9ccbfd67261a4729466f98ee1db61592af1fc7b39e2181034e27abc492409e069c23c577f4749acd5236e349f1c92837155ab5553d1ce276d8aba649f661e4ae3dcd8f73ba139ca807d90d6dec1a9b860762731186ba09357343823fd671a0a8440d758f2714b07ce0b6d8ee3e072af2f999efd2b2f4bd29a0b7e4ee2fb03969abdb773917cdfc033166bdd42ee8c0f0de8fdc1f0b959ffd56c806c06ea1137b28ee248c3176911c6a89f3ac4fbe9294fd2c25740790a660a831c1fb96109efa0ff705b5c893a9243584fbbbef285f05ca9370a3a352ff33e3fbb4ce74222a57e9df2068cf76844517674c6e6a188fa7158768ee6fb34da4c185fb830a8030b0eff418ff974072732b830d30df3199387ea05c52692283f7bcce83b46392ff77a2391ee371a20d92c00b3e9ce77fdd50918e3ca550e914a7a53ee6f8b7709d5a71495d8ed3e4500f7d429da6dc0ce8b1e10e8153d28ed7b0454f33cecd924540395148ae0936911fd4c376a78890ebe5d7fb220351c257b7bd4569d752023276726c1d48dc36a0147a3db2a69a24b00b54e87d42177e95503de125797ab2420f3fa31bbd5c0e70f3038b55ad7e40fbd4380e86fdc7fe89164eb0aacd59012dfde37496f6116d41ff33c74efb86de3dfa2d24cefc0b63c89d79e133cc29905efb2920c4f7da9c8b961871dd18fd44999a25e5dcccc9ccab601a2e675373e100c397861eb058b5755e03a51eae9d52c2e4d4995ae4ca18e4dc2233d7d7aef09e64d43b4813625ee86b3dde7f88897786e1416eddb163c124b159785245e69114691c2e2133c527da43f96a3ce84e6dd4ca5565323c6c315ad829d67ffe1c404621eb8bf726f2e29a09c7859bed166cb8ba83b62b89937330ba87944cfa8ca0ca6890ea5b94cf33b92150635f7eaaa4f6ba748de2c1511f5951f64b2020cc62dd083cfc17c88a46303505006aba555321be0a57109e342280647134ce385c5299579a2c5323a9de3f476deb15b2bef26ae534a4cc8ef85736d2e278da053c606e1876d5599770ec050ca34a7586b97509498768869afbcce36fa99b3bea7e2190eff69b5e545803434ea8ad62014852a371430a727701311a6f04fa47cb2cd08ca37d24e1311f454ce5d64f1be459f3f63bd3c2c5d2d8b894886deb7403c1915a57f7b0dc00c079803ca7502bfe4818087b1bee5e5ef561924cb46df8ee9af2f4791d0d4821ba79925efd25af9808cbb8277cd45e6c8b1c435290fc3a81816113cd7b0348fa54da05b8a621dc0216d78be0a5d5e1a910fdc581e4d05f27a011ecc21fad91def43ccce7539527630fe9efe81a8af34587cb9fee87f922e63d01377a702022f407844e60f563192d097fc190d4b9423e4e7b237edbe025c47b910c6da28d26c5f25a5025265d2a204a519b833f8e64d2a3a4c0f00c1c39ce69e1d0b89313a4d0a35ddcdf13ffeaf15535473b98771e39baf7e4aa6f927264e6119206d1b024b808af816db984825a1ab0c4ec9df14120dbabdcdf4b0bb4445ac022d4f53f6757cec1a02719b166bc272aa26fd3d5a19f053e13fbd00b5d67ada109d29051cd5ec728a99bc53a016164989493cef0a3fcd26825ad0673bf2fd9a17788433a5a33f5dfa3d26c5afbc72d8154c7ef45b0321decf426bc6f37e7b0e0493ee9e522db3775ec83b6d4c69585fa5023553e5d477f6e1ac2a80b4f0477a0f18b2c4014e7596d55b3e6cc268d4b3f0d265cf823feb97355ad804bb1510c5021956e6865c693615eccaa18c8dfd10fd643ae26d30bd4fd07af6309c8385af2ae91ffade4e050bb2a85681ec5eee4e55168bf626743731018ec69c8ff8574700456dc0c1b55946cfd78d2350072c21be9d3081f24bb64349c2dd8f63fee8440c5a2c18c8332898034b2f6b5c774f71d8a436168330e2a43d1501d35bf7264307009ae8c13c7f98a61e2b6294ebd04b54eb61cd67c868e5a70b247a82c56b5000773850ad4fec37329ce608029bf0091939a6a1b6b9f426f2042c0717b61dae1a416763e0be43ca69a9f855685aa38ca1de9230cc19b7a4defaa050052549512770183df9e2acc5a1723ce3d73b96b32c41adfafe5cee370a1e678fa79ea3722461dfd9aeb45139569f0e6f7a3c3d7f60013fa59ec8b7345fa311351a12d2140d8e6ed91a51176926a8dba35e2c65d4ac7320103427b36f20f3411ba8f04ee614427ff6b94415c230ed5f49545dd15a4e1df3fed50437b81ddfac5cf1165e6116e3e7fec6439f5f56494929c6fa96e7e6a49d999026d174126e0deb9eb11aa01cd1e0dca8ffee2d57bbb6a22e1393f6ebd5392244b30634971e9f38de5aa999134af1bc04a03e028bd552b71f42a2b798e649db9214c69412e408467037b7de1f4fd334f1c9f9c7bf1050182d4cb55bfa65f679a1c01e10cce60b0ba019b0a7211b9f3e4e5cf04297d83eeb51a0d4582534a00decceea4c163271f2696053e8164d2dad9506d5bfcb921ae50e0d91b96b81d4c3a805116e90e9c4f6becf24e42fa10833dbf0f0713f1b46fe67f689ec0b3d6e6ae54838dc2b20549c9fead4f317fa104e25740c58fa1089a411c6fe1140e07cd5e859dc3f4b10cad565cd35c21eb743d7d066b0216ef1222ed15d5d2b19901623f0ed9ade841181ef7384ed412f47fbb67ab1620be7c439652920a57f0e24e0489622a90ba284b0f15d8708f105dd4dbc8cedd61f619f19696f3d65d947ac5a37117e42234a0b1ac09891a97d2045fe954f13a507dc665b6f4dc1af44dca049b48f50601239d54963830057c21fea7fbc790dba2a482930e983d06cf20e52f841048ea23fd9becae3c6c56dea16d3627053072fd2eb637138bb5af7f1574e5ea375e8d147294672d93b1ab92a2e625c96a55982d59f2908793089c5d675fb63e56ccdcd0872735cc0c45351970c34f093e20663390986b19081d84681c360a40344a48044e228a23139c07de62bc0d25c0f844edddb2e17b82340e9d73f228772ee64c59f9746aa416f1405860bc6bfd996c2e61c9bc823024de7bdf863bc5133fd6ab0805c4c88ebbd3fcbb12d0f7308c4c486a20860283e6e2320e198f6a2d085cc8980e26f2e51a550933928e561229bfb72912cc955aabeea11b030d1c17551eccda5bb5f5bff83fbbd5013563d0f8a86a2d65ad84d5c95ce6c5a2b656b3b578b73f21d02ba88fe8a9d712e2afeeb9d7e0eb9c1a541f91ade3049ca1f51897a474235342a9a6f88fb499428e9770a22b2dd768a096dc5616a65630e49d6805da5c884ae7e0312ccf8feec24843bd69da995e08aeebb8e5a4c54b61d70edb1c5e62db5e3ff429ae5cafff81dd26c2cb92f24909acd104662f7d74d54466c57386cb437cda86fdbfa342f3011ac3251f702fb97585a11f54408b29243d4eae98f77a096a0ea94e624d152b8267e3d7dfeac3d7df0e8224a2f2d99102031ad8a74dfd347c88c55de823733b90632ec5a50f12be845c37ae121d1b0d662cf8961ca1227bef498feb4928655861f5672914a9fb0b568580a74142716cd5ebb0ce51c1c8d072f6dd5c981ca3ed106bef9c00c32e0246166d0d789aec5041f57d670fb2f0fbb4aa87335a7ef7a512cd1a8bb26b2d343de10f1b05957a69cb3e99e955b9aec21c4fbf7a4b5453c421bc106f05c610f438528f8e733d48e402f42600c1ae3b74318c41b722dd5cc6925154ed14a9928b5a96e2e4b4efe986e226c755e98e61ea76ec944eb78a3d69201231ee7b8063640dd0cd244ae2e598bded49282ed4d79c0f890c098432a534708246110b4e213240c5bf0026fd920a4bb45e2c574295ba581b644d13d762cfd0d43d638af1e48c7e08246e03fed8b6dc6c09ab358b627fc13faff5add1d19751abbdb7ea60b18e3f2b102fc3d30167795ea71badf50e36e7ae300dbcabd1216048274a5622365a76c5e7a50d353fb8e9a1094f8deb532f930fea082eeb6a4b31c1bb329608eefae298673490756fa62d97ca75d15f4441f1e6018de13991f3816bc02756edccbeaf4300ebffada8d6b6c943cae931a17f6a89a5e2b232117c0075f297211fe6a9cfdd0a4f32127175b900f81e70ef7e79e1d1e51e7afebd95a0c91f87a48820ad1236710841d0f4cad093f3173a2d3060d59d33d2de467f0072a5a51e107eb89a2b8dc2ecd12d77db3ec7513ac16154c2f1ce91861514e615724f19f85a4c2c2b99629eff7eb549565ad3f5217d6ed6b00eb83c75e170215ea04b957bb50d43308a0e5993d64b0341182bf7e615835b8a971f1d37d18630b46287d062a775a49374c26263f01e4062cfdc3b934fd8c22029a3d44d0760c116a242d2751cff8a77d499e4b9ba267e85143e5f1a59195d98cc13ff0a4fb3ad7c90907b712c497a116fc6f365a7f4f4285f7f5926487491a9cbed80268203b5e53074c7c275265fea6cbd5555f42dc8f8f45122a9f1a64120f0ed5f2243f410041026d88d6e4179b61e5d74d705240cd0d43730cf5cd444824aa68219d38dce9192f1023376a1d43ccf70c0b7382b4253eecb6aa387b83120cdfe6d02788ae70b3303be8647e4498ca34cae02c5fff879a7d4106c8ece7153abc8db46c12dcb49a23c6dd66ef69852617850c188eb1e9f6051bf69841624f9b98c909b54559b1e42f85ee41dfc9ad92b00495f0fcad184d54f6b3d5bc239541e0a4a1b13750efd50ea8779427d2fcc35a52cf61a4786f9ed6f69c9c842dbb60436c1c8fe999c51e2ace7522b27e4b3b8caff8e1a4ee75455e869e24333e541db868e616d6bcc60720e529e44071d6f59aabbc1921a7a28a662cf5756a53fa23bf56fec80b6ed6b90a65fc7af3019b9a937f10a38e6e9f505a5560cd594fa6c42b52db56befdb9160be7f0de83d3abcab2d9efddec09d43cb6da76a733ffc61bf9db3281f3bb6c3a27bcb2167b53f38d25f1e1b85e1a55f53dbaa39ed09dd99da47dc62ac3e8676d2179043618a209e0cf54b7c1d85281897b86b7fc4d2a03faf6272d30339c9d2029841ba7c096a9f52c3ffd13d354f06ca15bc394b99de23492f7da1757e3769419ed503135e7736210e1f37ddc3bcfb9b7627354bd38b55f3f3d326f2e8a761a17da2abd212372bfc5fe8f07033d8bda07733512982a3092153286ef6f95e4df1cf934d31d0566f00c1b1f1186bb66a3b3083c285f7c08b21eee1d2733784d442063bd18b682dd4e90921766318cb642e3789a1cc4a6a191643c49a9b5be21fa004f66731f08c82f2d433f0ea3a5ce0d4fa2ebe5ece4545d4bf75eef7387c3622006f028a52b5bdafd3a058c16068a4d331df9ad12888e64e8b52b12a514c0e51dc2c154263d1399b3108002ff047d94c86256d801891f8c3d0cac48d7d5503f2bb7074d74e23b2bc7056963f5897a95d9dc32006c6a259166474287b15063b900bc8f035a99d7cf793ef4d706050388921536d3b3bac47fdff272b1f34f94800f0b6de358640c1f7f50cd197faaea1afa3dd005abe027f5406a1d43b4fa753e1bf29ad3b1ddb946fc5ad5aab9c781f13a9bd7fbb4024fc769271c1617b160b62821f77d6b20c09b826540204521241540181ff1156eba68f62c3807d74fb6b82a26ed573505f10bbe1afabf79d4d85accfdde58bacf25838f0fa93d87c276d72f7af912395093ae881e3be578d3dfde5675a7df316907ef4e349b8e0c493c481c7a29583b0eaa6ef46c964cc64dac3aad7c065a79ba92687ce0a98ee8cdb6361a14fce6daec621048466b4a6bbff5176467f07e6a2b69d5a59ce0127c77c05912d44efb3fa2a2d643dd0631c0c420303970e67d144185aab38122ee4d89a180002c26533406e07c05fbbfbbb148d6212908b6368226ea7c7d5b3e839910389e0099bc3354cb8d84d8a9effe2cd41ef669540fa409d96d787b622cbb530f643b09aab6a03e9b83f0a22202c68286417e48731d44a2eb367308d472ab2d860c8803db08bfa695f3f2930a8c664374b927c41dcc3c9c2fcf71e1c3efb95fd866102947e9746b2cf78aa01b14cb5db1b082a352f58fa64c4506028b405e82ed6ebd0826c6541fa085bc93ac86d87386c2042f07b045d93f017366d6c1d93ebcedd014e278d3835050677c5d46e7378734d23c51e67f1fcaebaf567698e4eda0f70e15dd2e99d51ae126ad71067d10cab934a9f0d0f88b6b004e1e1e1092b7cd460f1a170e7efb388dd92ff079c964f5f0191a51b99ba304b159294220629427c66322d192e3cb8014a8e42c965e9f09703949fda898bf106f70e3200faaae85c86e10f790ade8f2875364d93992679319355f0415beb31b498a59f932bcabf37de62a6e3e2cc8ead181b76d7f2f81c89665e5991486881ed6e85957f55d87c4de2f0bb2af449984fad5b85aa1fc465a345e5c098635d36cd5df525866c9925158c120366487c32261f99551943f7bd9b828fd0f805c91af36923afb9a1035a976cf29d7efe8355bfc2823d0d9d26252bcf99df9af15ffa66a2a57d675b8cf6494e20615cc6f195c46ac770f1fe2cbb8f966071ea08c126a5993126a291ee908a96a2214f44d6e3bab7a1f8cc9a535d86930423ab2be7b7f50ab026ae178a7464df6002c08e6f95f946dec9d918fa7c701979e5e7277efd92c2e0391bbcdfba23e102c2d4162f5812fac7806769430c50cf13d041229d170f339e9fb0765ea57b5a90afcb342c8d26ea08634abbcfae382a9292252b2722c90071257e8b6a3542a7d1b202c88abbb094b7a2f6be1d22ea647347d0f43ab767873da3d12382713ae0b132942d7950c80d15ea5a933684f30593ed3517517987dcf7b800fb8eb8dc64b07bc25686cc77bfc09b2d71f441bcff96a0a7c7bb947c6079080f694243530d600844e32e68804d47077fed9246ee42860b7d03ba6cccb59b539553e212fad1304214624e71fa2f1496d36e6ace06ace6f520af8bc12fa6907234b65d64d3a81300ed8c5074835551bc7cbbef0b6dec93fa8eac69ceb2b6fa787ed2b21cd15851840cbc48395ffe7276e88100c327fbf0d4b7ca7d9e7a4a1f17ad4e214a4fac82a7bab24a3c7a0e15438197bad66ca888729446d342811f1aa9a76c4452531e605e7fe0f6cf94dfd74cc7bec4fdcca0269fe0f2356608838073b1a2bcbb0bd130f19d0156490dc9d40165ca992696c5108931f1a0735fcb95193f56f5af1c4a70125c502bd7355b66681cb150761c244f8f1c12b1700a4aa873545c8d44f1dbd2c27884cbc54e96cd0cb82a908f5cb4f98ca4848fd7d6607c7c376fa5fbaf5b80634c59c38c194fa0c158398b64ef453fa5d1d0eecd13fb4c47d1452ed75bebf1874785457afda78924f0f4f191a26da477702053e6b4ea9a9158aeb8be5cba01eed0bea52ba039d30db1021cf35d604bafe3e53e81d7d7a2f54cdcb1a07a1de113f1b5a613d9e4b57b599353128a4e4fd25b865767590ed535d0f8b64a5ba6f1b2ccd91bb5128694deb43752b489f8ef72aa7850b83c921f56b27dcbcd801d8293d9bd61050e6ac52b47d25c4ba3eb7c3268719971b9c5f6b6285e7de73ab861c8a071a0b61d854c12f15e8bccbdb3948f41330dab8f1e1b46401fe630cd9590cbeedb644bca3ab31753ce8759476832ed9eb828375fa41106cbb8812a4ab3f2d2917d97686724f15012e3aa224edbfa79b5703723b9f1da9d8f298e7a424e717c15f97c6bf2975729f8056bec981a39d6c055d27c0b46166a6ada05e93d96d21e8b83dd54e3cb2488d3e4750dc0cc64a5a3f5ecd99eb8598c57f36b698f19105f585c424c2f0c2a8bdb04498b691ae1c26302705c2fcdbb7ac34181f65900108f4a30fa53778b73db14e6d881314fee1b43d119dc7500b68eff4b24650bb7510e8c3728a3ca4e710c32347395b4b203884db0191c4ee2039e21aed587ec1760ca67d37030c26656ebac257d53dd24515d20dd84a5ef465978759d706139a0c259dd389daef6ffa3c56f3052ba2da2a49996bd9e3ceebb79d7338319a51569811ea7cdd52331d67c99185bcad199e61682a180a6f16c021229de73dee8e9eae0e057f1a75896831b980d2376c6baa22430d90781298cd2c76a3f5a5832360d30c6d8c121ced3ea63e682940893df32891a0f20fe3771ac18e8895421c624da426b14707eea022d5814f3d774ecc970a9b1ff47bae53a335b230c2f29334f279215860b96d5c4e332dc5d521628505256bc95ffcaf7e6af856188f82b31088981c8aa702e18474e5df8f3ecac017929b8ac9a6a8f475ddd59c408014b4c8129001e5d9a9a60c8c13482d0837da1dd4b39c69b31f0d514838b6fae82c89e06c572d35b1b48563839c0992ce91307b4d837ed609baf6afb38d48e7972b8b5b99f91fdeca4c830613e7f9b72bd083efe820181fc523820f15ed63fb0822b9230c570859aab904fd73648275e5373ff1af6f61712b1c54db6d978406694e9b6038650f79671116f91705532e3dc721ae876dfb1bd00480f5eb58caac1171e79fc9652aa650cf76380bb290d6cd3732a2d080cc997d2da545fde1027339d4646c4dec09214ce863c782bd94e0a31b28b4752f7941d3a92401bfa4e37d81c61fe0f75e02162aaa21eb2c66e4dcfa0bef894309aebf66b8962fa9e8afaeafba81a283d0125ca9f4cfef741def31d6098c96fbe5bcb15fdaf3385c768f2f8cfc183ef6662a46f7b706b2bea3d914c8e070012297473589bf3e9669b9b4767073c80f7a7626f3ca968a920ce519da4e4f48f62a480065c015d4865430ca207d6582947f7403bc218272d157440faaa13a39e22f45866cad2e2dfdd09b7f7a79b1b21e0dfc0bc45b781f6950f78d4631281d285180ddc7579f12d53da5254468947f88c64d2404ca4392d6b9483901ca83f488b0eb99c6d9e61855f940e349618bb4cdda120cb9fdabf9a482fe39eabdaa2b4f8698a1085a949dc6ee0fc4da4baeb3270e7a153fc2f4912de3780b345be03f1e15bb753897c2a0c7b00b7e6a618c7b3c20e10439bcc07d2377b5dc924375ced34e7d7cc8ba6a0d1fa6697a457dc6e612bccd0964a3ac2316c29aecd74957ea9bcb5f4a70a655a1999fafbd95380d763c261813f0c6ded6f4acdb425e3047a64f187e195afb7aa9399bd775e29d3578af140b224bc801d54c95d3790079c8393ed98a7bc3e5acad2cbea47887b0163c1544a40966e04884b239de2dac9e8aeed580e5f956f8a8e1fb671e75d34e5fbf0054ad0a974b331ccbb54c8e8585d5eb41a5ce017860bb71135724903392d6b2f0017819c9ff87ff6948eba679e1bc202394505e6d368ee5be6dd25b4439c7f75a96501ee54142e584831644a3e8241064c3eed654b6207aad54cd6d499674b43450d811025dac2d3557cb347773d2bd3295ee079df0f1b3efc2fa0a9d82cd1ed20bde2bd6e34585718101ac22e513c10675f3aa2f9bac70f9f870566b54d8ce8a3efd2b60b63b6b6e16f8dd1c78424aef184ffff1a53ad572390d8edd42e12c6e5e9d01c2c362010c018763f2154e1c0a5a03fa54832572603c918595c280962a7d2c01d9259c30854eae881636f2256369627f6f380217b7aa0801cb8f5d486fff811a30fbd4048a0c81d9560ad1462dc5981aa2d8e1fa9c90f2de629b8503a127900b2fa796825e2c150ac4fd3bd7d195cb67af638e3c488b981e0312ca79e038041e241d9cf8886003759b85cd3c669ae378e8ba9422ffc09858b4f2176bd9d5b722c3f11e8c8f7cb8a3d231024e60f79258315938b9f8ab8974c1f0221e8571e00c98ddbea1a84f571064b2477455777ffd10e902a33dccf434232a62c946e557076ff7bc613e5111167319e1b3d2234b3da40a8b4d49e00b4a5d23d953bf2fce089f706c73c7b0e82c212743ece53b7c96cbd5445865fe705539dc7dd0ef0fb60fc255d83f113467e1d8eb4da93dad481732cbe0d4b4a9769deaa83b6abbfc84149e3502435b22304a721cea21100cad0220b078d0f93800b8032d73e592f88e2e3aeda6320f26cd7c4ef4299193396343c12bdbbd9a87cb52b18f2e08027716008ccf50ef396033799f9fffbe9d32766e7d9d3f4d32e6ea7ccb08cec6f2d97227954aef0752083f3ca2d5dc02994622d1e06dfdc7f043b4e947b6ce3e217b7d27b0d8db69361d9a78937e99143d81fc0990dc4dc46583f3cf73db704c2cca2927400622e6bda6427adaa4183ce35abcc598c5526a69e26a3c6dc4af36eb6c160ab48cc09ca6961d808bec4d6084741b1b2f33534553e7d24f630f0bb640c118062730001bebb4e5702ce2fa779104caaa107e1f1899fec2374a7f27764f7ede21a5e12cdb37b4ca76b6951212562a105f92a315b950d8f434ff416b1b9a1b2422542f057fe7d51597880e645ab1714fc229f68002b3d1e269ceeb32969b353e11ac49ca87ea7f88875de12309ddfe3f6cf4501522e68ecc199db40b4001867c123a1e6a293a691d8151ad766f0130fa0bd970aae5ac366b886257423afdd0761a28cd4a57083ebc02a3dabd01693859a12b9cacda074fbe5ccd2ab834b15d65093c17d5ee31d0c180f8c74b42e91330ff9a9de6c39f23c4e7533fb63f284c63304f216f7c4ae3f54a9c627b8d0c4630fa6bfdd917830ed5d3c0235a6e418692237fdcdeb42e7ed028fd7f95aca773bd7573c654e6856604ee0d9d7fa25f6cc91b0021a21d95c9e2ed43c38e92cc99d0858f791f33c7c4096b4073a50a605af92e9a31efb3fa1054ae2b053573c05cf80189cf9cb1eeaa65561a54f721df77ee2ddf01652c0435eb7597ea08e0664181b3f679113845f0e03ed829431541f05d203def0f84f22abe9fec00c6052b290ecd0db4797466aff18461037e202ee54806e756fbd0e3076fbdfc66fe4193483139f60f235dc8f5cddfe98439288a7753155bd0112d2d9810e489a5d98f38170d50ef95978af50b478fcc04ecc50779f40050c9dd0f00897baf1d4d1b06c9aac43016048cfffa660bab245fdada960e4f041af6a74318a9dfea942ab43a0f96759c9202cfac134071297e13a8d2f85ef508175c4f36719cc2a3a4d21955a3402abaf3320f83fede6a173c7414b5883a8f13d47610689ec9fc2ee58b124fa5bf5a35545d41457c16b009cd2a3eb7e109f71ebc8c881ab00c42acab3415b19b798ebc498b1c3082f4569d01e3de6a0508cf2ba3949cf042821c3acf2cb6db150a88b36f7ba6684d39fbfcda0e807dac34871ae08b9eac0b61e5a37433aced9f98b6561b794309d4baa95ded5888f022a8eb1d9f79b2d8e5956fbf4990d68c906bd82638d806a842a601783e4d4597765a60252c2c6119bd618c2ba6d69d08fd192f4a3fd2c3b4c7f010d53cd6e2a7423ad008ec05208111a54223a5273410057b8981019bf1811b991bcc366049469605b42fd28cd0b86bde221037d2b8fd45cb0c9b27e48386a963a84f61dcc58440ca032597de6c3507f1e648311c5525fd00378d3432955cfdf7eb87fd3832624d912a2696d383a5f0090b5ae652a07570818d0455a505c3655c003e79ed841cd1a02119556b5f142928ab98a491ca0b486300c0e126bf9308797faf4d6d4f096c8fed301fa9aff821908059e36c2a2ff6e783796e33140ff21e46a3e19320a162c0a80b574fa11c5610721789046457a8e122dbbc31f9db8e9d60eea6fdb78fb62e002942999bb7ee575ebf42514a77595e1177199575464f7cc47e535f0d0c564a3a1b2e5e37b1d9f7eb0404df692657f7d4d5aa7ac1418d3bb1c0055afda07e6b1e1b6bc8f49f0e6255250aec766ba36d26c78a34c64c379689c4bc13302fc0a2e90923bf1e1915ed0ca385aba72da2f48566569f21a88069fa4600df845309ef7b137e09f44bc8215c0b31d1191797b2a789fcdc460bdce0b58eef3ec23e7f46ee09257488dd2f164e17a31fed823468e23e7791d0897daf3f937ffe7d05bb55511e1c417c6f2fb6fceea5ebfcd854a23ab5ca8fdc7c04908d8b81e436207dbb1f18da479741bd2883269c262a870a23bfc99ca1a714ddcdb769d695aa747bf726173a91cf40d052149374a8809cb89c2ccc1751c07e2b2bc4e286774bbfe4b1712d38e2106b4546a9af90a3008d709994d0adaee76450273eb6715ccb2ed12824070533ab886efef8291c861e327d1198221667f380dd075a86fcd21d5f7c3ff7032d213496e23fc2845413df185d31f73a6198904c94ddccb0fefa0f250b599b0223370e91052e2b4b71b6133793f6f6c9a5f313efb18f2e88001e561b480c7f128af589420a68ab0dabbf4118cad6fe88586d6759c1395cb199cf6590a1d4fd052eb2aab72f8746d2147262fc3c1a64235a45ae1d171c31a917208adc36b84013fe1e6b33fdad0187aaee03b96a3626e4d5006bb0ffcc91d1d57239ce89461e0570641c01456e1c92e19a0054d9c812de26ed8cefee8f1e29903867aca772a11cabe48a842659044e170f37774282a95dca132c110aacc8354be42019156dd92a790c670bcf81ab066867a237e984e6e51b28f0b9be32323a1b76eef7d25c4d0e2079053cbc8ba35f2b1823b437a526f482d892c240f8e8258eb73f29d77f700fa00cfa645e659a661969d96c5d93a03239997e24f2a3d551f74804e235bbc12117b470959358f27fc14086c9d9494b8f6074e98cf99ba4d98607f8054e01c30dd270176d233a81f079282ac2d90d61e6278c0bc08fbff1914b49e432e0039b2b9d3af036e3554540363571f6cf94384af6a31647e2416da794a816dcca9e930ddff997ba2d5d6ca43b8b27ce0e5ef64097365087bba89d904ed998f250f93f134d50ffec261e5d928b0e7ea27594f54febe371db232923a7dd799d8de591cf005909f50abfb4f11d0af2edb4899ca7bd57cf2ab1b8172af3d1cdb4b32072121ba1274c502ba63577953f92f9dd4f715fa87411d6501435ef42d64c3b9941c11c5104477a3a7c216138f889ebe0cbe7ad8687c9cc998c83a0a5d64cd37b08a80411ea0f65a76c75969c095f9103dc1d950454549f95eb2b4139617dcb0c7d9e21ff3f9786b19ff2456a4c77191118917a82179807d42e4b6a03ea7ccc6ce3462f124adb34301fe90c49423e41e8a0f135b76d6653a107f8f91c9d8a3fe14874283fd2c7d4173367a0a729b8c378f396ee73c3310dd807b80a12c3e1f50e76bc235179e6eade026a5b94aeee06760fe269c187c0ca04cd22f8a6933f617ef55221d07bbd98ac54cd8d7e4b63980dadb35296060af0bab5cc9969a7042cf6c8b0000ddd081697143999fb6c1e12bc4134602fdae6180e5a69a1b54d203a0e086b272e29940a796957ac570af02ccaf8e1cbd94013607d2f7bd4be7c466986d14d15b90f5f649d3da06cf9a2d20d684075971fc6589466ec9560f78f95d63af4500e621998656153ca8ba44402b31b19cf7eb932adafb783547e2e019e152fe3361b7e76c63017866dd5eeee8c7ee7c569a1dee58a78b8c3ccb250600c9aab1cd706a210f1d7c892ccf6b68f6dc42c86f8993a9634952c4c9cc3ee0184a64a28a60afcd2605a76a47a34885bf7cb398e70dfba201aff2221f55345464ec01bb36a2ca81d2a0e0e09280964d5c06671235fb5d3976aa2c31f8bc74c0e3f6f9fba500e7650be7a2791e8f7bc78c1033f6e3e829af8f5664e7703f0b48ad9d3e1de8a15f7812ec86a5d13fd88077241559c19a4cc7c174741c6f091405081a809562b1d7b74b4b4c7fe9d36b5d542c28135a6072ffc5ca0d746fb98588ad7d35df9e618b80609be85aec7266ff9bcca42c3714c9f9f2724fef38f698e63d3139c29a6", - "public_inputs_hex": "0x1d420eaa08a65528f470fd3e2a802913b611527f41fca008a0ad4e97475078032a555ccad424037773cae7569cd26f46467d9bd96c32334af741c911096761830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000022197e95de61c6c2844727c848aa819fe89bff180954ba01135f7e3d285aa3d8e240e1f1fe5145cc75fe7b253dd853cded77fb484b8be9d52aaf0a49098afcc7d24e0b65d370477cc168e835d8143177b582294e4a47357f372f960754b56b54f2a318f2fc85748ea90c49625631654288c77791d8dd0792e201c748ca9863ac417b852c2d0067302ffa20e6f7068292fa3304e6e20e60a0c51ed55a2e0c2a2a615de98a2794725029dd283936e25dbbb2d8cf00dc4401eac6e834b094e5efd8e2bad40c7a2fc975f2e4811607e4d5fb72361dd1af1bb2364be799270aab5c6f623db21355d312ad82ff6334e39518d7aec226005c56d124daf506a44749cf370" + "proof_hex": "0x00000000000000000000000000000000000000000000000821730b5f6c7306ca00000000000000000000000000000000000000000000000027d5a4288c59517c0000000000000000000000000000000000000000000000061a32d74244b4e4cd0000000000000000000000000000000000000000000000000002752ab748caed000000000000000000000000000000000000000000000005a4f0140d4a1c7f7300000000000000000000000000000000000000000000000512ac80c5fa266d0800000000000000000000000000000000000000000000000d400822869689e0ec0000000000000000000000000000000000000000000000000000a3ecc527c1e200000000000000000000000000000000000000000000000564b7881fe1989f5500000000000000000000000000000000000000000000000674ecde0680d8bf4900000000000000000000000000000000000000000000000584cd67f303aef6c30000000000000000000000000000000000000000000000000000623ff53a7e1000000000000000000000000000000000000000000000000b27d89433be674a5400000000000000000000000000000000000000000000000d03b476bc4cac62c900000000000000000000000000000000000000000000000d562dd63dce28dce600000000000000000000000000000000000000000000000000007014317d4d7c2bdca3a8f64dcf8eb8b35863e69e18fd4f152ed73eba57c25990e80b7c74b79013d6c8d4d7310cd110d6e43bb18fc56336d2e4da3740b50e8523d5f9594bcdd812036507369a09143ca35760ebd0173b2c22e17b4dc2980119e4fbf1ed9cf2a9021fe43ba1b07e24480cdba82eac2af02f2e755eb1b42e866b12ef874db8f56729f0b56939196bb391b63f552db3f3c7ec39a1d778f2686df8f01eacb03019c80d8bbf928d3be121e24970b738be38dd033d077d66095a42d4f1494b44eb7df0074e45e1376a363bcf636637b442460155ece719bb40d1fc476322ceb66ab5261818431b676c3ee34900e20139f2ce04bedc6ee159747b931b42f2b22b3d7198079fe5ff134d61e1d32094cc83972db87162171f68aff7e77845fde2854f3b9a0d21b74b20d8c2ec809d9db9e189051c0dd8557ec1661fa1a1b0ce322590aecc23e610a0c95bf8e2f1ec67fad3b50a0d2e04a267e7844e7e78b710b17602724a2b1fd0daf5aaae67ae6e02faeb646dbe547f0c699e2cb1379f50d28f36ba218a1b615a728d5b46c9da5f17803b7b0ddfaa6af8a8d6d945602cfcfdb1a8c2845e10ef52b08e4afe6f9717aa1865a5df615627ece311d2bfccb7831e0631a7271a08ce5712e16458da4091348c134bbbc0f6c546f14c8d9c231ab7975bccb9e02e0c991572aba8f415ac14e2e466eb01533edbb3a35795beea6327e635c6b103740f2b82ceef34e542ab504d3202e68b230920bc61d94d1cf18cde724d06e23a482b31011ca389f49559a044f8bc99e2eceffadc140cae4937d763106987d7fe30299aea525ef4ba58a7d10fddd62430590152bdd9c1fe3f5dd865bb2ce4e22a65086dd1e3b14c653011e7a3c89c4abaa1ed50be316fd87f42661de510e98dc9bf0dddcf46263bc755e1f5bad01ba6f03708b0bb47ea5aad02d6a9052b1fce2f05226a3e2e74566f10fe938081e85af521d530facec93e917a64abe8a781e246752f984676b56b5a9c17fb37ec862c78c596c128c75422712251c9e25f0fa4352a2a7bf93147adfa3937ec9733a4e035e4a297fbb1e9599d1de9a59b617dc808f117e08e894d7ebdc106e7f065fa3a096cecca51bf047ded0439a19e86d1f4a358099e717b59523313b12586636c72a0028ee88c7cbf053fbe465dc0d546b698cf132800fbde649562ed1d1c65b70ad4ba7497d90bbaa8dab5316435f6473614f501bf945798b6e787cba40d1202870cc74db20387287ff9fffb4cdf7a0160ea04251adcb89e4bed874a70342a96386f8ab23147019c7e3dc8a6f2d0f6ee77ea1b1683337f4dbdcb041c320a5df0f41519161ce81427ee42e7e55b381564cef1ff1036c245bd9b7f0292937e19a58c6f917d8bc00ffb2c098590ffa91e35aa88bf0f29c027c75a7685fecf83028b01891bf2582da15f30dbdadf169772056758b112d80f494136784c5e55d2ffd210cab43b12b3b22efff7fcd159f532727da6ec05f1b98a8978a14a941d2489d0948799285fecc08732988a56e66e352362a6be2cc8b3127ad5279b02a55f57f785b7e1db4de806714cb6cfc421b1af185f408d2beec04fcf9b045a9fe4911bab199445b8cabc822f63eda13db7ea709458a02c27135db9d0e4ba34442524e335b54395b3c5d1d19d856c4a813e0fc2303a45b50eb4e75e5e2bb32813c7346b9c9c1c0ef2b8cccfde4c1c8f4370c89efceb6be328a05d97d39aed2e4cc7d7536b34af28247ede4000fcc7b428147375abfdfb5516e986f5197eb977a865b855e9772fff94c52ffdf99286ee66fc4bd6ff9fa6540e0da9efe06b9e2cb8c70f7bed562275ad9485849152717537abaa41b09aef482b701578723527aa01c63295dddd454473177bfd0415c06d3d4e1848d50a58252bf4553e8afc869321e3d7796ba736c8b389b852bfec26444709e9d423192bd117d4a7cb64bb293e7c7033de3dc675267289c8ba6fcc0ca6ce0b9050e79e4e2d12956851fb28b58225eb8c0ebc9a45158c59a5655fdde45136ce3888dd460ddb15888a69073d825555fcd88eb1a3d7f67a4ebd571c54d2d1274518f65805c2382c12086776a4e11771b7d00b480fb1c5a78c300cd815f8039dc45ea68e61c51e130a50b94972ea631e451d8eb55ce9868d50b4e67952699e020fe4a972b44a36115a8998e760ca80bfb4b64576d97e16bf0d1c9b703a106482cf6872973e94520ce2037c03b5c1cf4892fe2c779650c50d7b88b19a1f6f4be7f5c78fb11783571256a19df3f4b96c9fce648218195e60d1c2e4c662d66ccb18a71bd0a9fb8a8b23c3faf51f7aef24669b70e1a4274abc0f66b911c67f15fd970be027f76d1aab14d557ba7d4f268f95afb5068341ba0900e85cc7d322674c057004d6fa17189123eae8a63d39bfc00d3630a696781bc7ecdc4a6b1d7677ca60c64c32d111d3c10f75c8f14135d4faacb046778787802723936036487091e65721e7feaf94a87d10cac1095e8bd1785f16c5e7f0f5fb9fbfb5faa9918ce2a047c7c0debec9fd5f2c41c01fe8b3062e6fb6e6961c09ab4db1da10ab612f4c5a7328a5209dba00950b62695a4af7c7304b3eacd7d23b4de36c705a55605659d07d7bf79a382923d82ab46fbaff077b68f5b592be6cac56d52f95b7f67123c6ef30cce46bb673b92b0ad6bccfde38ae2deb094a64332f2645f4cde17c0a64aa6cf9b79a7ed828fee42cca7f36d7f6b946dfd1e01db6832cb6f4eab7ea793718651913922d4f360d4b21f257a41c05813fdec7e7d01cd94552d86eae1bced28135690ba7c02f94c4102e5902efa5fcb489bba7b608b977362ed8ef889df360bb12d3f6fa737cc23ea410e2c92ffcb685b442e3884384753e48b3af21056bb1eb92dd975c59aa54ed01161b2d1f8b8e48050ebebc4226dad4c4efd3ccba5c481e80a25e903ed9bdd7992355c18cd5fab093b281f89fbcf4c49a728b3339e0a50b7c8f3eaf38c0a45a241cd2d8c6780c99d0506d6db1d8b009aac9b761cb438e732bebc6c473618f58a526589895742bb0bc9bf2157b0ac83056acd061226b5155c952999f617205e6231dd54dfe603fac9886d0279e7a02a79d39fc16d9590496af4854499544f0d61623b4c14179daa63917368e6315d7c8044998be25f1fff60ff99cebeaf702094322d12ecbd646a3e725894e99bfa905064f065a4bb9ecf069150adf4f6f234c7203c810c6f79c786b859baf6db1ce8395ded182bfce2fd1385b1fae68ce21305026844979fc3f6f15e3f01a641e070dac977d4c4e8251de5b25aeba717b43bcd322a34d4d3c3c7f307f3cbea1f6c4ff7736b1fcbca1b856a1d39a1de2a09e2ff306d5df092ac06ba92c3a7a18fa39a0a90e5b2c1d4d8645f0ffca3717f752afc42eb754439548876e6b43c3a5d94e1bf5abf0e7f9b5a6686050cbba01a352ecc21598da0ad02780190cf24cdcbc430d5aedfc4770abe9c6863cfeef593d95d28b1b050913d0e34830cae0602973282d94fec6205ec8756b4d696623c67d33e55e05d1cce77fdac4c7fdcfaee946a7baed22eb73fb1af5cea27644c13e2e249b6a0fce2f7340ef3aa4e2fa39063e6c8ed748a9d29f33a38f4b50e12d60c03433b81a80e41a565ac3c7013a1e851151847f3bb22ebefd9ff335ff703efe6189df8611dd23fff4444e25494a43eddb53fd61199cd4c1c04def81d6f77187f8f5e3da1a1129ba1faa492ccfa688a8fab7566278f412deabe1f69458de9a4991000314172a67e9eec385ebcc15e24e8cd28e117a2e885e418a5eff75fa6e0af53d4782282f17a9691bb7192b5b670ab7a577b331366962dd0a36346f28b8f56ec241ea0b6552eb0d11d855088e95a118fd271c8a3a1922ea0bfaf0afe9ea5c758e20492391a1721c25d590aeee4199f47b8e5c32ffea485cde82f7392674c9de1aeb3c183f9916e9789575f1b0b461ed9905831051ce8b19952d6c331efbf48b7dbf4614a5b15d0e87af9e76b285fd9ecce3107a983c726ae5d3d6ec6e2baeb34a9e390ee6e7510fc9579db80f9d8ac57e1cc886eaa4d2e4afe8430ff03b6c6f3aec5b2ebfab036e1b2efe026beca2aaeca416a017afbbf80ccbb2e72791f9cdc6f6b505b1dae159a08ee04351f5f2f240edbe45973e734d98f6f4cc9e2b7da8da6a110f7e97eed170300d1f8541ad1e1cb3bf6b3863a4cb490cb5c39e6d4faae2f6ea0082ec6630d36696ea41f90d0d64da3c8ff4facd1cec120e8736509315d426770883f14157bd101f004e3e63407103842843b512eeef44a77743bcefe483286303ab560fa84c14a40002cd93f22ece518b999cb3121c7e1df64eca2069230193028e8d3507fd7d7be0e586300d994405ce6dd9895c747b49f51f20d494c916c808ec85a1e118a4ba5f6448c66d7ebbbe036e81080e7fb9d38cd1ac591d5667dd060cdc04db6517473b7814ad9d757fca4904ebcbc818ca1f22ecf674dcc0c8ec1bb2763dba05f118fcf7500e370108e9004dc46a567294751b7ca51e51229b22220d0d21392e57177d6e2671cf3e954eceaca2cf87918287bfc1f6bf533708943011f277db9e4f9d9ce32ae3f3a1a3343d87d3f95b52d768e4848a92aff7dfed1aead60fddbfb11590d84445c4e75305bb2313c20df2f95eab7e64eb085ee372282e3acefad52cb23bf8855d760a5ded264e7cf806f650248c6db9c54b48e80709642a094184762433ca772b01c277a6fbf700c2046fac1e7c45852dc4c127ec0307dde473f72467a910331f49b44d665488a4c88895342608a78ef07cead2c305ad0fb767e03b49b8e665584ecbb8651dcafc1b5c116b33ba927fa8d15b98f5214a6f4c52b2e56646a8d9cefa2258846c3de653e2297a4051385c199d278a5926a6a8f3f0356900b0f226530e88bb4714c63d444b3dd57e11227770c75d1d2f225236f50e50cf07d29626531dcab3db32b7c1a3af3e43febe274500247334660014e70f3347b1f65c35c55630d4db54848774e2c140eb7c7dcba10652028cc818c7a328564594c77c3ca4d6cf9b0ce46a65cd4cf70a6820eabf65f863291f0f0c89ae9929974dae20ebeb9d518dd20b2795765cf0b0addfc027806a744965d41d9fe3385a89d328e2f8a5dd616160cda986b7d7ab9ec9543d99fd0f70ca38771d00e76228c39527af15a012f227bff39d231a2682a9bd9c4b45ca1fc2b260aa250577f8fa880edd4a07dd8115511301135921bbc68b53bd2375c7635fd9dee70dfc0e14e099d417eb4d6035da9ff7a9f647f84df2437aa344deb5737717058d0fff3c23d38e8d2548ee3ca6dfb23b0c4dfd22cc50c86134291cda45e0131e1917765092935098bb1a016aad380ecf5edc52dd0f95e566d538900955bcacc4342d16f512ab3e6aac573cb9a03082605ae8a4850e455b5d1641c03a37b0cf6ff90215793fd21ee0fc330133beaa6de3b5d59c40755ce8d0cd2c434b9a228fb79815ac089a2735a487f497d5758fa303de789f3ae7a09298147bea45a9c69ce19e2d058fefcd8ca169e2ba7760c96e7af51de2a28cde2d2174ad0a910dbb954f6e2fab7246831353872aec29ee4df899bca2eb5001464c97b1cce8c138fcc94ec40c8e76d15d58b5353e6b3f9ffc5b871ea1d2cc1fc1ab411671eb7d4f80c28b051dc71f34aaceee78f29a56e4529ca51c856472af3c41d587c25a3bb5ddd190062840ac3eb0fe54d3b1f28f63b89977405cd88ca9c73df613c7aaadc6965661c70747b5fc78ce643b6811e78b86b7f68736e2b356a934001d2ea264a89cfbe99a12dc049b6f9ec05d04083fd34b5647bd585f1be04af90aec97ea1e1ee9d42b841d7f31c6fd3853b6c12510c173a710adacac5b23fee0d5586900c83d9711955a1d5d41d21d27fee28b4989409d645d05feda97d3b12b1bcf31e156c75bed1d3d299392602de76f62b9103cce850a2b5a875ef961bd2f58321bb605928c80a122147f4095ff4fc0c4772cc9eb0052f677550724a61d0bf0d328fc6116e13e20ba1a7ab9f9f8506aaf4d953b7c13ada2220f7f6f4695fffeb8a712b5cac9f185ad15a710203071669acc41cfcb817ac9aa6d2ed7c763d9de8b8c7ac7374b0896db120a5f2e718e228bd07965ea30f302417d9f9ab3e01991d4090fd2b0920194ab038128867db1f90f4a79b4586f7602b80324cbacb580b34263c724430b9b91632d8d497956dc8231e14a4d8a202676f3b8a272bb1e26122f025c4f8f78d5c72c21b998aebd00ed34be2d76e4a43006fe81588d6b23eb5bf4c3367285c9a9ef2927a7877682d3af2d5e33c05808479f4521b3a17168e6c97bdbddb7770f93d2f01b08b7b99d331e78b71e84cb19252fb9f9f1ac65e844728db5722e55ab95b9f41edcecb4f31deb949896cd3db5cd0dc2ef15e4ba0eadb9bbe2657b321e467ae20c88064c8c15e19067e28939f8ee13304d7283a98fb49625609483ae02f2a30d160db7d5914120348062db5917327da32d027a758811aa7fd1c21b6a1dd8c75e09d22df3f7027626e95fcb651e8b7559471fcb5ffb9162d550aa82338c51cb0f02c653bb8a2b59c2959f293a4973748870484c05d4ff511fa666604692f73cbe282d4e0e243d6d9ba21e950ac205857f6d41ba6073034f315ef27929d5dba83205f192e83a1e5003de8d1c38c6dcfa2593bba4898090ff610b0890515f9bd44d06d0e8c2d93df6d36626472b3ceba0994cc76cafb660d6a692e7bf0559db1ed02fc0cb1865d7d25f89fb247bfa0e1a3fdae33f97abf5ae7d9e35033050445f73244d228055862735caebee5db8a119a9d8d11a7035555ada83e299f3387abf5f0000dea04c2732cab2470e3e04688d9f1dedd763e036634d28ba9f8e2c5240ca271898be20fb585b49b60139770919a34305e52be70f0d50192770fc02517f652e1e7ce1906dc26a8fbde6939f69b2c0645b8cb4a3954ad2b43918305a27046603a85deac0c770c7e523137f35e3000a0bf96b8c329525f65957437b757c262a2bc6a9c8527c1359d8e598afc781fc2e7ed7501a730554c58d2a552329f99d5c2635fd5fa08d5535ca03c6ea08662a9c52fbc5959b1a998374ed3f624bab2b5b2c33c8f5aeb856586abd239d13286104a2e90e79983c92ed579bfd5576668f7214523b4f53f4bee84986ad86798ec100d603f863f155fdfd2fd2cd72f5676a852c49103b1c0c67f55c4af21fafed6d1d9ed79f863c9807eb055b1777945f8d4f1e0db4f8234739e50bb75bc8fde5af7ab0688ff97d67209fce8a9b39e7b4d05d25548e0d72bccaffb3b604d3b33ce22039ed319792b1fa674d396a6c9786dcac294231be2234a234be2524f2f7dae91c55e58074b0503b24530a0c6e6297f19b0e695dacbd609ce9ca7cc5b2511ba837f4eca002f7102967d35389efb30000cb181f2a3daffd5d7f9915bf8dd1de5d4e7659638e9584d13714469a9ba5d37d7313784f05375e9c5e34ef8219292be23da93ad73869d0bc71516e4393f5a782062975871276d39f2f41d36b81ebbc221648befd2dd8b9ae5610fabeaf54f1947f17dbe4490a05d99f3ac74e0d537673afb9d993294d2aed3e7e293ea0d280ceb019e6f0704f9b34dd1a8da3dc616c365e09b09075d2fef5b1ede4a3bba6df095b1390ce5a31053ffdb83cb05b9209d37592156cb464012744e90748e708378d9c04416b2fd032528cc00e516f1fa6bb6bead7b0d0a69d97e0a14e3b40ada5065b101efc0f1f4cd612b723e18127513fa2b82d5f5cdeea70c60581191b3c7cd81f1cf121cc958060e951b4e6c76f2d557bdddd63f9f51676352d04e0174aa85a900b4a92fc1ddfd06e8006f1d78401e5f7390ace2e425bed97aec36ff7e00d2f6525871edfb56c38063860c618feb09c8748f76f88ea04b6ed77e5421025254d1b2ed05884ee40dd3e9536f678676913dc230188ced021bcb986b6b18b475fe0ff25ca62e1660243f9d9b164881c2398ee00b789bc8c4444d3682420cb869165b30e224323381cb5cc73620a7a96119458441256a6229f2bd621cb86ecc29165db17aeae0846231e6b67942cec4406b326de3bf6da88d906298be18f50763420171db37eb6250875d716390e1399175ee4eec8b0bd26efb179532d87783746b5521a5304ec07e9daa765361558c3187a2073c503c3981ea7992d9c60bcf15ed2832f492d65ba407662a090ab3ef91cfcdf450a588b143f4cbf6fa16010ea41310c305c41056c1e5d407b1eeff592b7fd4f7321aa7e2be12587c37faba2dae3b5050e6c1530a67f22db6c8febe5f34295ef550fc8bbc338a40623dc6065c708338604f699048cd976fdb44878c7b1059cd85f4b91b964b8df3bf9c70f3c2befd9322551cd9f2b50fe2d058916efe06f883fde51721cc8523939a3350133f533f1810524dd023854b3ae2592e3c34de948a860ca68eab2e7515f901ba4ef130450802767ddf18f04412c57ea96eb43689d7148daf50f18621a2a2f4aa76ff98011bb12d62e69c1c4d4a8baae33a8117b84857e317f8562da97c5769136574dccc90706388d13056cff5c7444a3beacfb4110d60cb4a41e70cfcc8de69c72a693c3c22cc4374fcd2cdef089cdfe6a0c2a0e8f8acf27d8e36b650e13a8d51308a25a6829c36bb3cf8ad207df79717b3a3fc45a0dd3b5db984b8293dbaf91f785fe0fcc0c2b38ec1232d147cc42ac3808a34322fbb5f8306303cffb36614394275526e41f473251fd02c680ab57b99de93a2ef421ab91e4426b4c99f3c6d03504de269b275d2cf9faa91d300dc7bbdb3dda6da71347133816be7b051d48a33903efbf4407f90419f1332cc03e9fd8c41e24a26c8f42481b2e9fed3327ce1798263434e51fb358e785403ba67331bfb0f3133ead7225bd117acbf86d18a8703e1d3ebaef2281f87c90eee0cdd19c503c9e39020cf0a6f0793d93dfc23d0167592855aa6516320c766eb5089fe5089ff9c2a5f43622cca3fa745cf3e61a9ace7b7e1c1efe1bdee1c9bf8561b342a2cacb2584c6ae87cbc23ca7f21561483dc48e65ed74d70e36d273946c8af8fbfac07b8067c7537725fb15dd74d6fd8e06d9560d04fe9e0bb9cc66a552723894d500bcc67d9dbbbb2f7def521d214cde49257da5ebba58127ac3ddcf681962da25f22c4452c92bb8643d2d51cd72af07b36ec40933aded2a7ca13c7f42fad892ec2a215c0b050a497585d5acbef59321362e9ebc54f7d7299414c10a9a83fc9beb917a3272cbfffa4e7da3fe84a0f10dabec1f7e5a897e0962a0e842037b4604c054cd61aac3a07fc1c81f3a72e51e2ec990d4db00b9eb14f32933b1cbd476385d2d315eb05da63cf9aa7392e86f8789f1eecdc9aae8fc14541b5d0cf84ceb4c04ef83b692a470b986b75ae8fbd52ed926f7c50a3f0a5c0025385e05933854303b5fdd3eb968d912173130bf0b081ba8ac3f93cdefc9a8143fa61d6377db816b1367c023307d2fab251d5ca6e846a272a4577a7fc82d0901acf57ea102113af63c159c1f5d0d91078ffe6f22541bdec846436cb3729c8126556cf495ef535047d5bdf6cdb5aff1c08ace3e8ba81eecaaee4bf99b80bf98026d93bd1b87cf7eb21a4085647627e2b0b2504884d459721c7b979153b14d161ecf00bf75bf12b4c944c0096897bc8c53526c746135500931ec160fb77114a317c0ab237646eff1345dd26ad855f8d7f7228ad671ffa4793f582b208bcd2dbb018fafa828663cf93e8ebac379ed326fcd502f1b27bffe1912716dec34b4189d2949271b39f8384eaaa45e64fcc800b5f805dcfe8e9986c26d7541bf962d1166044483913e751c75bae08db36edb57973712fba0c80d6813f1e051c2af60ef6e117a84999eb5ede01635927eed50f201635f19e297f45c406e75470fbfe8a40f0db953affc8f61b592b5b0bbf515eede750bc8a0593edcaf5959a5445a1ae0c1130526a514a02445e3a6978f1849f50a7c23db9340d42577892f4d385814e49b151466ff20534e74439acf791d130b0f765f9c9b2ab66fda9098c7771834d71e0ddad7d678af5183635414a7ee104cf52afc1dd2ce8fdff5ba21534f4d73667501ddc7df289a79ef1a336f930cdbe947883fab991b26adf61eb7837d582b8e9d15ec335ab0809126826fb09abb2da87e9aedca01dcf1a662b74cc555fab8b65c013fbe0dff90977f2a74360eb7fb0e75c59408389757e17ad071d76c112ffbc4014b0ca129f864a2489339ed260cf0796380943a2618f3b13e66649fb04104ff15197fafa3b047eff947a782bea063ee95bc5b8cddbc7db60661db0e6a47be4619fe21e5918752a3f48836a6630ac27392a10fe2b6a6b292903709ee2f82c75e0d667d9b51d5ce9ff23fd6c7f2c5a87f087748824f9bb170c33a51a704be5f042a94345fd5fa26e80dcc485aa26fa6c6061d6cafe422caf1eac52f8405a6c0aa2bff08666e2efbca4f1baed7f5532cb579708baeb3555ae6b984fab00610901e1ba452a848424b71e55829122932331f5306b62a5beb24c70237eb9c0e0207591d783524c1a28c1fe61f050b6bc6c300011f151959e387c0125b787cdbd2c7241f837fb79f84889b30cc8e5e84cb49a895585e78ca301052c5df2a4fc873db770367667202002946d7dfbf953f113a006ad4837ea696baa9272c58c9af1d6d8e1f0f21da94147cbaf178fb414d0a77e22c33c3fda3d0be7ebcfb9d95daed750008c9a1d8ddc601df5867b8277024e8c2f447a739164cc63ea44c03ad55282f181cf08f3933494d3938e6937267d1f54289a4e1f19d16abd449ee8372fce0834b212bfd7c33276b09f5ad9221383f3bea85ff25427d5deb17e7a510d4104e32aa160ae05861837210115dca42c986c81002ca19b8d3645d1b473134a160d34ee3261287fd51ed3dd9518c3ca8a42d99bc095a056a3a51411fe822eed25ac7b9392e319e63cfbdfa8c25b357407e9f6e2e249f9a75b5fc17b8940b10c7a8bdc0c80ce7c78c5a7da26bef638318da682b033778c232ce708d33e025782dd268861c06d6ea5cd80d15066128b7b0b5d5b6f51f240ea176d0580ef7b02eee47a5cce712042e07964c9c052cedc831ad39cb143a888041c8aeca71ddda00e103d609160b7ed571aa0f7dc432de1024a9572b2f46029fe8563ff2fe6046cfbc4589099a139ce636ae588318153e65aeb57232762e7be53e82ec3c6f46ee4f923625378f183f5c778c1d9420a1523b518c7b970ba291de12f4ee0d3abc0daa36f7faf05901731bb21c7b0c1e3fa46a76e005632f8eba62e811233000cd2539f44b06619c0391e67d0b33b06bdfc98775458a4293c9515b87e5c25f6cb6d29918837096890ed9428f0cbbdcb8eb0edc385318bb3e61aebb5774cb91852ccceceae70c38cc2056a75e1d63ae67c774a818ad111bf39acba607cbe4864fe74d45aaee92912e237f27cb4f8d40f7029eca2bae4caef946b9549348e37161a6e56168328e03490e955c18c7bf6cb2ed0f8a4c94bc635ba90724e62b29de6d7fcb2249d3951b32012abdacd304147bf0b87a684c56a84e4654a1fdb1c2109d4210f7e9031f3ff9280148dad4fe9dac49cc6815a9d2910e9676cb332990e6564537f5a787e126bb154cfff073cfacd39e74a57c4d0d382155eea6093b96ee0e0bc1d204f58742131e755596903ab9eeb3e947677bf81b62c73f504acb1adc7e4dee30c034ab75790ebae3861e8c094437ac0d5cfefa3b3b0dd75c18b443e46060bb69f122660a7f0d8147ccd093f2aa6b1ad705f3791229891faa6f27122529f42e149eae990fde118d8dd0656f0fa9f5267beb6ccc40fdbbcbf34de723c173979387831ba95cc811e1c79446cb200b31e31f3200a287f582be616f920f3b24590529f1abea7c180a6ce5ab8515f5498960440ddf5000079fa233245d1df8fd688ade85739eda4906703d70e7215b65746facfcfe1aa1ab8c7c817b137661d535ba08aa63aaf44f2b87690dc15322b2c5cb26d729a557bf30748c7dc60c36a9610c9e758330ee4e1efec3393675cfce7c64da0c1fda8ecd235b8c76e3681cff99daf8473c686d1f0bf6aabadc7acc20b88f11869b498b3d839f679d443073ce6ef84512ebed8821286504bf8db57ddaa7f07b0ef748604de3498c5a8be6c3b5b8121515bb8306782ca30bf541db60bd1eaa09ed005970e3c80a2c7c143f6504d10b208ca352b8880e915045e50f5ae5d45f7401b0f70076e20ca6635e89bd950b832ff0aaf0236e206fc268a7f893c058c73030c70f4c7deb51ed388a3bee3aa16534d5319836be14f0addc9788e212172376c5f8cfae54ee2cd8792ae628061ce47cc1a22869761305fc7a5369bbe86b6aacfeb72427f397e40e8b901f5b3e292d7d29160a11bb05979b806f46ca206ada7f1ac05c54c4f49cfadcc39cba3e49e3ccc263ff77c319ceb2c976e8f0ed37f6a73c371cfd76d1b82af0a1e58a6f8e6c0068076db9c229f59e929d9547d2de2db72aa9b89fd37dc41561ab1809614d396d06eb5689f00b058cb17c2b3cf46fc636066b4632a5023dc55731dcc76db3dc1a0a0ec84c900a469a7d17720805a3c2bc7ff493a2df926d49a394a7a0c45c7620f051b2ef14160d19492657bb67d1bef596d29b79d29a4babaa5428795879ef66d3aa7dfd7807592edaf9d568afc173e452e112179d987725eeb41f1182a9f2bf32db42b63b1898abae8d596b33efe7b6607af0079dac5d356bd7cf13d10d0a4e0768f2114c25ce2889ce6c34db05ff06fbfa4b955b018546e27cd0bbfb2bc0cffa0792cb5823bab8a977dfd68e70f068ad03ee38aae0e7d49a8957c3878d643e6d0f65fcec272c725699c16e846c8638f5cec0fa2d981165b3bcaa7602938f4a2149ed71e10cc23a9c5e7d0a3f5461709b23cca9f4eef68bfd06a6fd5e505e756ab4c40a8e2859e0f20337582b5f4556a47c503934f1b8ce4ba7b27a31560d3809029ea2d2067381928499d6943b040cef2cfc40f7025ab51d66d46db1eaf2dba55ffb9d472f04933ae5c2edea96a0468675bdd6c86d3446009068ea089303a1b9d053a8841d98c5b5a491cc1f8e8e260a3655fd3a6b3dbc20beb8e874272a67260b49afb0213e712579743fb62f8ac169ab6500175cc164c2a5caccaa675159759f708016141c4e101152c0e8e55842f9bad78fa3d8c4ed129f91d6450d6c06e85338347f0f6a91a32ba22a390cff1ceb374f5e2b6745e8905f0866029a4c2f9ea7299cf31ea5b0b47ee4d6329b1ba0f1ed46fb0be938a45a2db24560edd0dafb2f1d5526073075b6b2bb62555d44907340a599754fb868566a768b458d6f6b038866a2512c37b7d743fe40490af6e2267fa03f03e2a8f9b6d95650221eaf0b4c87ccf6d505125e1dcd88c3be809632f750178fb8b6bae40b12c686a256feeb481b20ec7a2024b45fd5c00f5608c9cca96508b2425fa584455c1c79dea9c947c13e54f2bd0b77f70ae6e1aadb54cd3af5dcbd773c96a36c60a6e94d41f051284397b36e4102431a5c1227d7a2e5eb432dcc11f0fd5884c17eecfe66a1ce2a22b0419b751d117401dabda490ef21888c13c136e09941774ff0b070e9b24e762ea7ff7684822e8d26105cc2ca0b833708f06574954f6b582e8053824a60dbf179e50db6f55c069244550b03c11e5cd22653891fe5064198a330115698a1d852f57c32eb855423c9db7c5aa271f155577ae09f6e5a7e32c2aff1f5150feba43b795cc2584c07001cb655353eec53d999060ef4380640747f65e57196198444330db72f8081f5202a4f8e4f598610629f96fdfd5734ff55a7f4031c8538f9fe80d1dfb973e8d61beb98f1f854d9783b319368ae6f9a015484bbad829d30cca00981f840ec563a2e24c11cfd9977d1f787fb0a395ba4b1acfdbf6f2bafee57074e5328affeb76204ba93ee5b82ed1dded622a843db90a4d19c0b4ef46867161b3f6525493b3a3e285408073fd97e2f53fdb86c06e9845e8d410462857a2b9243b348ad17b662cf06686483db4b559d400f33726b232420a522d3a4a63376d60b56f5ee7cac6688203272656b35c2a109ed301e8a298fb397d7b66d22b4867b9806269b28a5c16d2c366e20f6c78ecef995bf41e82d6fc7cc578e293bd917b60d9bd6d1dedd21bd12b872b9d3e443055b534b753d81d39377c29f810995cf6238b69779a358fb6208eadcd48848b22b262ad65f2682172834b865f293d2fc5cc95538f242b21cd12261023dcc253992cbc1c548d996c675b14dac9b530fcc0bf78fb90bad6027e52502201fd2f781fa9dcf4da5c91b6706f2eda965b330521737df12d13d350c2506a779924f2bfd8c634cd73c84811da8534e0b14ee42996d6ed01f879d4d858605adbb356eec9115a7e0a689e0de4c27be333631fcfa9fec8d02744e206dde6110f97142186a44a6f4f220b1295b4e1b0385b0826984a145ac28bcf89a9c3627084ef803b0cae77c7eb29fe2d6ff15e3762928f2f1100f4a8b59adeb81a445e70c54d7b8453eb39ec2e05e612d1c0884eab9b6d8792606e5ae8627d0f4d44beb2d4306f31b6cdc617da0f5374ae79900cd9b9f693debb14d59889d5b802fb8471c0a414faa8abb86d379cf24a883de065656be4205dd39718b1dab3ef93ba2e5107d2c79ae5b564bdaf2971a6ea04febbcf5e2c7b3e3743d8b37631ed2ebfc2a116cc9f8b27037cb4d7bd4078692ed74caa2a1ae3458d1eaef27cf48870bd95a", + "public_inputs_hex": "0x1d420eaa08a65528f470fd3e2a802913b611527f41fca008a0ad4e97475078032a555ccad424037773cae7569cd26f46467d9bd96c32334af741c91109676183000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000008f26088c208eab68279f6f45f234d271000000000000000000000000000000008ec0f15e98d9058c0e708be73acd2b9d2197e95de61c6c2844727c848aa819fe89bff180954ba01135f7e3d285aa3d8e240e1f1fe5145cc75fe7b253dd853cded77fb484b8be9d52aaf0a49098afcc7d24e0b65d370477cc168e835d8143177b582294e4a47357f372f960754b56b54f2a318f2fc85748ea90c49625631654288c77791d8dd0792e201c748ca9863ac417b852c2d0067302ffa20e6f7068292fa3304e6e20e60a0c51ed55a2e0c2a2a615de98a2794725029dd283936e25dbbb2d8cf00dc4401eac6e834b094e5efd8e2bad40c7a2fc975f2e4811607e4d5fb72361dd1af1bb2364be799270aab5c6f623db21355d312ad82ff6334e39518d7aec226005c56d124daf506a44749cf370" }, "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000d6343409c0afb816a000000000000000000000000000000000000000000000000e0e3142188542d9d000000000000000000000000000000000000000000000004bdda217c121af9d500000000000000000000000000000000000000000000000000014a5db1afe856000000000000000000000000000000000000000000000000ee178f82f74d1cab000000000000000000000000000000000000000000000002e8d0a46d30478dfe00000000000000000000000000000000000000000000000207560c01e3450dbc00000000000000000000000000000000000000000000000000022788c7db5ddc0000000000000000000000000000000000000000000000099ba154d1691f0c0c0000000000000000000000000000000000000000000000077a57fa84b59575440000000000000000000000000000000000000000000000052b18c02725e6860800000000000000000000000000000000000000000000000000017e9657c0fc1c000000000000000000000000000000000000000000000002b224b4548754d742000000000000000000000000000000000000000000000009134fbdf2459e11e8000000000000000000000000000000000000000000000007ae759187042674660000000000000000000000000000000000000000000000000001b7072ac05fcc10fd82a9f5e13726dda894346515567a79c225b241a718f1b252ee23cfcb8dfc134749cb199d7dfa121b0233c0bca54919eea35871367be619fab7d4acde565f21f7d05e4d6075633ee1781849710d0cc9f901e277b8147885e33549518fe8c9235394cb328d148824ef86c283f67c33bd9d74125ce290de790d17252755f12a2c575b654f3c0128b45fc00733497bedb1ab89208d38b1062fbdcd9874d1a1102203a3af11c6d2beeefa926f37841cf1766ee9c1fd8fb937fd7ca88bcf091a3b0d693f463de1bc4d89d66c527f61a537f8c5e09d43384776febd12c1a0b160192419f01ad42306732827a9e2b737e59e99429dafabbc4b63fd1eac9ac2d8f50324e028c3bfc9cb7efabedc43d2c85fd8454cb2ac6edb769ec5aaa71a6813ed252ea7767466292e673a4791482b20189da899063637b2fb233fe7413ebe14ab48172ebbec7b1577ece1ca982dfe9bc31189268e16381b3452830ad955c85334f723b7cd29f8459a2bdce9108984d2445710388a4ec18f02e22446ef9281b115a80a16fb591cb108f0398b7bdd4b096354d07245988cde80562a2e3997dc8d77ac05bada7a25ab6675abb5c42348a44a313a519ade461c43aa3993e73cfc06472e252e3d75992b831f25d123331077a32c38e1377c7e7e41f360cc4f9cb84e70fc047c37bac2870a10065df9c6c9405302c93374fecd0753fc9874bb9889e7e2d8056c734dfc773c2d255b48cb1ba153afd8e628b1a7fcb0ca36233ed95a92e6960811d86a8a694956d28b0187c66038d4aaccb51e0a164bd42d83fc40fb48b8a61af5e281d9149910f3494c80f943107661e722113d7b9a6c0c1deab0beae72c60ec45189b66a1679eca9165ebc7a629d2e71e0a594277d0fbbb1379fa345c9a61c5b614ff22f7abbde64cb1c7c49431e04559ba35828a18bfdba63c1169090a409a9f16759479cadbd816a6eb1116a9a4d0c3ad1b5f36f304a97c7e87643778f0c38118f46b63bbd48cf9b4e18bd76663b71ad6b8312ad607a4664b2f47259ed009c81da4d44d2d89b736a3ca86649ec926b8d9f5ce807ea302b1ecbc0349ba0010534881c9a4b14b496d7dd5146e97bff9978f74eb7cd73dfdde685be41267606e74867136658c65220b3f1515fad8fc3d291f636b9ba4a03f4e288464e246a17c606205cb08af4feda633d7e7358b8a8c1e3a9bb7cc3e0089717bdc29914ea0b923cfac7c47dc0b8d04a4ee9071fc9317c266eec4ccab59ffae46a9cee283d05e0a63d50303298db10d4af510792f740a8bbd35be7057bf1cee727e79394c22a8e2545755f8a0599a1e150671f54850bfbd1743210499c84c3b77b4d1f66431069cb273078a2e10f1f015ccc54118eae036e1947b49f63342d2e5cc69ac3db0e6346f2648d7fec2da28cbec5afb3314ceda07cc2c3ef10078c086658fdb63229c49d8545542435bd4d3b1696c001df587e38707a12cf521214825cac7af47d227e93ed63f59ed33ce031cd61931a57bf28f14f457a815be7267cc3c210ed870dcb1324eecc54a3357207d5d9d2c42815d5e7b2a65b4c9d5f1e208a9ed0a6b4235e122935a8843159470d69ebec0a3383369dee35693d475148f18247247c071a06a44e4197aa7a02bc80ff31e3209552172b66163935d82fd56dc4a6a730ff173fc70ce51c6c9dd197840a1e873489010c6750c0f7c05ba0de148c871f50570359ebea3159661fd9333c2db2b4f5041249f8dd96ad6ecd2623078086bdb0e51b67b47a31148be3689e5bc49025c1182f1ea881ed3c65e72c2944f0928668171ec2d7741c74f5047a6b13a71eb54058979c872ce634aa6b5dc6e060dcc85d1002cd3cbd4ab0b1076ff2360427b4157bf24c8d965d6c836962a84ec8315cc713080b314982a7345e5a38ed87768e95095ab5b39c8736cef5710baa273277ac2e17f55debdc276dc29eb726c7a938b352124bbfb0a07c26605f144d404bbd2bdf149594defc22c1a6717c5147d2f36dd017d5524107ce84c6e97b7a143e29d52f25022de025b573b9eb662e6c3d09660e4d0b423a16661e9cf66f44eaf21945692150a6292c541bb691e59f8ff4fb579f797bc5d89b3138d8a7aeccedb49729c724beb14ae3f33d99876f08496a48a22de7c436ec88364ba02e10bf3c52236ea913e8426d7cc3e2d38c861f8981014f91432d53089ac70e114b86d4a60d150e51107bdebe9f1e40b98059a4313e5bff2e5b3200c6ca3a0545a8f115bde4fb7d7107b2010c1aa5c9d5ae36db4f703cce98712fac6a633da3874a71943809c40cd7260a628aede79ffe3799bbe5d49288b947d1d067f4ba336f26bd7862510ffb4328331991a2f89b9f53a3620b32bc7bb50f76eb9cde2cb2f9cf42c4a22df0c0e20331793e2f642f142bae542cb7e2f0fd200db0b710d3fd3c0f4d37afa38b8d501fc34640655f2776937aefd2c0c15fa5dc91fa5de878ed805b700436d8ea62ec2061cc0c19517812cd43acb92e01018dd857565ff19d40bb8f09a5f2753b26900f1ee465a4bded176bdb8c4cd3589184c8c80c0b95cfa3484ae2655c46508c4b1e06aa30e32f43f454846e4dfea668903fed3bf23762dabf7b4d7fdc33590fa3010b8aa4bdf244b628b32e4468ad96056a848209a65f1c42dca8318754332aa109bbfe7a82cd199fc831c48b5439c09209b31247b258bb665d2190407ba46dad014ed3c854628e09e8ba9da2c4e2b1e658a6b56fc772a101531810ba1efd61040ede249e8bbacc5b7f064e3a0a89f98d64bccfa1d47c52bafc19f615c4df0fcd059b763205544ba63668706829151458142db92203b996a171fdbd9dc8016f54105b72228df5503f7a4484b4e99320c03d7d8d384e83ad9cf5b9ab322a9f81582f48298a3a781a02a0fb4e6a7b116dc94f11cd919b909ae8fef369dd4a7498252a0c3cbf87c8e91ac7e56d9bcf6ddfec337df1e6f2caff2fd04489a0e15f51c00c8bab2bf1e204b2516dde8c6212fbc5d519398e1d7ffb0d3ced8ccad27638a0183755e45c97ed7e8496714a425b0683362ef4cefaa0acb128464c50054393010a3916f001a49abadfaa9c44052829ab1373491eb5afc19c448cc2ec2384cff003b6ef542acbc10237a0355395a4828bf9c356e66a92f998a9860477393e2ecc09d9a7501d79a694699ccd39b0fc5f7518b6ef187bea3938e24ac1e649107ae7134b4819e221010ff3ea8ca2aae7cfd8c913d727339e5e90d8bf9af431f2767f229d00d4bc9a9ab2708b1a268395b66e88603b50f8ec614b1ee8f042247191be099258078233ddbe8699093187d4b87238b8503be1b3a992a66efeb3c15bf46e114045770bc9dea0af8d1b3b9e7f89ad0f0f7b5cb58347f9bc31a1f5b749125503095d5ea015e6af870476964d02d0836687d9e205ff986efa3fa05eaf42567813be91e7d92751fe7dddce9055502b2beab3e4e3b9987491834fe202b41a16142628026d775db7141e97e2d4144abfd41e23c68dac14669bde63aafd40d4a8002c28aa160a0b5b1f57bcf17e056e62e9a6a792e75c194faecf8edf72ab65904b061cfd14a239bc808454c33b080a44af18784da9335cf5d3de85828c5b2a91d113bf19bc7ab5f87b921f7b88712ef3895fbee836d87dd311efc22d394dcb758321c9d1a26147d519764b79da0bc929d245fed23e95e58281d43dc93a611641ad104e9bb58a706ac4f9212e22869a7d7d816c3afc5f2b9275babbc2353dc59c332db15dde7753b62322c8bcf8d41b72e3bbe6ff0a2c61eb466e5512bf1a8be499301fe18941e16b2122f0fc390e191cf3a4532c849b39956f3e318c907163810611034a1a296a7bbe66780ca88c6f599ca09c82d83d70e1bfa4473776b5c068221194dc907822535370338f889825f36a24db7a3b42168201e5e80264e41e7569162cb3669ee45cf5c0810dda88651c91cc32fb2aae51c18f55174129409892050248d04cd731434d63434389f80310114dc92993c9b39e42550954633cf31d241754f6dc46a21f7be676f914feecb63bcfa9d839638e8fd140acbbe7e494540a1da5b4f31a302ecde922b1a10aff437aa96759c3059fa958a3a6e2c50361306d1c4a7b7f63a4abe9b13738ccde8195d2cc4fb4c3c85ad53c78dc8496283d0cc601396dd75bfacebad9ded60880f37b46bc61c87724c7b8c0513f1783675e4d5018f7f30a9115e2b95cbb5052e03b3111d90798ce5272e2c6228c165fce6c347c27711e7de6d91d0b6bdf34677334c6e8ca47a60e0b7bbb5a0f01a6724174d0cf0f38bdebf434e84d92160285043af5227e5f628012786f658f53dca24e3d3cda0841e6a5b1c9be5b9b898e5e71a0eac8825cf7d8b4f38fb0138dfda4cb5a9e5b0cc2d6ad4868dfa422efff32fb32a6535891e60413f524f001d727d786f2970e04e0809642af932031d727274d05962dab59cf469ab5d400f6291950cf78382218b051a65adfd64e4e81a5e31bc3dc22dfe95a810750fbfb0e9bb9e230aec7a3227e15527102ce7d9462df9e992926f691c129f6e38371aa044411006660b45b22ed9c9c0335ccaa9a3771614eda0ee18a5b1293cb713f3e4cf32209bf2b2be91b52a3e0461b00b50bc24212e7a0a168cd4a5670eed7735141ffbb2530bb11151d70bfc2ddd7bf86ec678546a0ba985b6152c5b427e31c612f298280263c0f312db2b751667a6b85ba0fac6656417340203010f463292039979dfe7bc30897160e633531a0d52f9a326a3aee24c35fbccdaa387a801db8f6d9ca2cf6b6641eae06aac300aea7c12bf9a28f3b667beb873af6f9caceec3fc3f0616dc7f4bd22f4146614c7ed9acb39a3915b92ac5b7787a877f46033e414b721b49d8d27e5ab892415cd864a75ef2e316f4ba10efa47a4559fcaa336afc1d9414c0204b98cc0a70626f89136564bfec778ef9807939900337c2616739d4a50f3be2c71830968412b0790e7e1110b375a19e3f5ffc7b3a605e187f41bff4a6283828996fd3f26a213a28f6e3ad328976ed2b72d62ad54cf51d8215f993325d697137dc2a4c1e3c80f6462158846588d3f5b8cfdafa9b1018b7b1348b150b76bcdd0640f57fd2d862dda4449740d5cb065e00bc50146bc70b3dac6a6e9103d0863af505eaab860361ed7accae9e15089da9d1d801ae35817a5f67a28a604771cbf3be04ebc9c20e51368750ba4cdee1f00fa042d805ab474446a90cfbf3fc97a20fe1ca5e4a6d588266864684004140b5150b5cdca08014cec724f1aef668d2ba67254a53f2c59d50ea76d17c1e2160bcab9fa7540b366abfaf5f8413244eaf950547f7571cc057729ea749b5fbf56ab428aab3ed476cd4f68b3f41eac270bd6b29fcda4f75626071b1a20be49701c43b4b16437ccc9fdf48a62fe1a2663a68c352e147f4ae8844c2807157c23509fbd64ac8ef2db22655f9745461382cf08b2d3cfdbf28081198a21ed719565dc566fb084e14c45856676c2a3a981576fad4bad652a20e85092a128f13027717bcd2bfaade88737b4ace18b183076e1876c817935ef5d7ba8b0622f6b2441c65d5d4c293d3a0506639a43c5d085c2df2b838a27263902349be4951f7f12272801748a9b3d4245de5e60f77cbeb1454b069a9f476e59bf36245722244ac46a6da45d3406581f44125c5661200e0d92ebdaaa3d14e973d2618ffc9b2081a5ce7bd73efba7488ec506c08e8100329c8a9f4db5d1d94165c5e0a21f202a8aee42247f7d031bffb09f2d852246350848d82a5c139169e8c8d39e6178bb264a9dd76aea3c0ee71750c7a9287741eeca96cc3e2418426d0e24d5665a4dc22cf017f7fec4b71e1e288e9efff6882b3fe1b39b4a527403b8d87a77b1f7b92511ef72f0386ad6da5c92005b739f5bb3c6aa212453e8e6d343ade9cc24a752631e63d16b0a0d0f0013302d8a6b592cac45f107cbd67975e6c4bccbb48572b6290fdbf02aee0467e7e24d7274351a3fd001627678697c6d18fb80fcc5cdb2b79e294219cb7224ebc6a5ae6ec05376afda73fd323e700aea6553a139f1bfaef34111e8014ae15ce5ddc2568eac8f8621f643468697c0f957c13d13de82457e4310297c09ac3934c2a272a10621472fe0deb9eb13dad684b66f5b19a420d8c010a82fc2de64ae10f7ed94fe5a610ae0f8889fe1d94f92544858c65ec64b95055ac11c4652efe064f85ac337332ad95955bb6db02f2d11040cffb5d6a8f9e632a93b14045aabcba545e42a03bbdb72421e4e08644e587725d0bf29d3c8a235bab183158dc24705ff40a5f24f8a0ea39f30aef722faf69af92a500abef9e2f26a2b642d76adb78616af6e74608fd27de1c8ce87a090d832f57e13ec2534abf164ba0b2be6d35cbaefe0366ab88d79d0f152a4cbff4a40a4dd1585cd47f7c6d39492721b4819f391ffe75d71858e5e7f3b7040d6d363761eb4b596aedbba067d8c4add0183eb7e4c92d2f449f9287ff1c0a8062ca1d719ef98e276c98ed954559047ec1aac319dd0d0e925fd34e61f3b450c6dc69cd7f50d4df31733d0652aef9d20d91b5aa21fe2b9a3c6915f614a7f1616959cc3fc32c5187f0dd24b0f51b32272ea072d7e084d62c3324dabd4f1985dc2c0aa88915960a5c1785ca8683207dbe4792d9d900add917b458ae42c0c8f66b9c414c484644e56fb083a7731d336d210060d5bf4c1b5f807c4692ca713632bd87b26ead2b7182937890b091a15f42d6ca2011026280c64f222ffd8629a17136b4c7a6fada3e2742fe377434073a9cee2bb03246938701a602957d1bb0abfd402035df562b6ac784c99647ac645c7958366136f0024347b1fd8bf330b4d697b97838393c0c91b82582343eb58e2f9d6b9c82b1387eed1f819c4d9a45a7e4d1ec5f2d2f0a8fe5aa96705c2058e1f62b5b2d5254c98a089c8f5eab8617604beb94022770aa9c76fab0cca170d5fdc2dbd40e628da831293d1c24273d9523a420bc585f93d4baa497e68f258121f9647fb2ef90bbad294f96003a9069d13d95465fbfb65362e6c70e61b6056e5b7053477fa0b17f609298e716d5fe9e20b2e49d5779a225f535ffa20c88bdc387a1ea66864d62571705f8745c3d21e0b714826d23644b10d5f3c166f1bd704291c20e7f685e8245e007dd62da8e45374824a7d819c48aeae8cc9beb2988bd8cb3cf074c7af0f2ec4fde771894a79f484ddee1e2af510dc80843c64f2dd12b7fb36d69a4b323b1d0aa5f93a2ea384df044c2132470d4c5aefe263fcdf47e68e9d26e63c81dcd32a389e21e2d56d771c823017fe31533809c3b5d3016afd6f95329ed4db23daf0106cd2e9f2addd7e0f013c1a32c3d5eaff36d796800acb744179cbd788a15a3c037261f29e401178fecd97f8fa01daf54700ce5982b1d58e025ed6ecbd300e7107afb7af7e7ebb538d40acad316ad54db0bab5ada811e395e0ebe6cf74ad149629c2fd131d0ef7d2faa4fbb177351dd1e68135f5ad97bfd6651a2c98ca8ef85926aef7180b5d563c5ad7df270d921a79feafa12e533cd9b8d602fea0ae16899729fc9de73eda204ba93c5c60ec0420cc4bc9d06b189045d9c12c71bd79e673c40c7ccd521d3ff61c7eceb8e965c00de62c8a32acfd1f121c0ee841fa2f756f0618f2d4d6ac96d7bd74f67427e347e2e760446958f9115ad9a35b530e801071be246e5576bd91e99ade8d75b12fbd1a43dd83960b318f970045dd374984b535e72bd2ba51ecd03b9396ad32e9fc4a8fcc22b203b32a45263a795786f4fbdc459b0659c4f1cc8261dfa55ea5ab693197366697b6ade5d4340c8192fceead82ace629a7472f3b13c28a954d467990509f4ce8ca0d37464673ad92a13c2715b7db55250d67b3a7e4d9a735099f8aee1544d0e6fd73f0cfad9c3aa5e40b0e2b8962561ec35f1ad2b11aff259d79d1baaba2fba96eb834e76a9c80b364e97b24307a281424aa2fa30be62314f75d116f5c131ec32bbd3d548ac60e287531ee45b88a9911e2292468a9077985e7f9bf3b9d7c9c7f4f4d58a034168a0846edc8fd6e821919bc426b90b9d31fd35ea2b87b4ae485c0684ab37528cb0f6319003a27dcf72e1bc9e5dc54f4a62cae86cedff569653518ce21fdcc660bbd5e7be989b268663728da72800734456991bb11783aa00a75d9d3e930bc9355f763b4c3d040e1dcf01955668b29288a41828a44bfcce4f3d3d409fd71f61b6a69b12757debf0d01692551a363188bca817da75832de2a3115a69e2cb747b0cb16ae52e6ee572c5de717941f01982ec1c364c7cd5d4d6eff0e3b03c68e7dca325c1e804b2e0abfbfeb1c9c593a923554ecd202ce87adc8dd2f5c4866d494f148edbbd7496abe6faf5023d53378720ce63e06078b8d9e84ca9a1d2a65616bd2def3a8bef72908acca0a10543ea597c8d6d636236f8faaea56d55a20c9142f080147a46a88648bd9dc11017aa88cf3adb1e5c174be8ce4b43d33da7b54a2d92514e12b6b768ec63bf0c31f2a9605389352931a3b69a1f26ac4c2163544780d60fcc85795256c6afa280027b06fcce4b557599d94a87b5c481d9a34c331c396df9656fa89ba922dae138408bffe3ac2706d43bd7fb1c8e56f3718bc24cd0b9e9b0ebe94080cb5eecce6480f8c185f442142926ca3172b2e19c03c0722752911cc8a6c7206f3c2912787122662900584b849d650e0c4eedfaf8187238a97f307266eed20d6e0b6ba01b9f20046fd636ffc52fc2d5daf18ebb043f41bfece62514c98854a64ebc2fdfeaf7c1cec169dd44365b740df2819d9ad1aadd76bdf6033173f06ac4d778fa9d4855822dba8778bcec03e6fe23987933ac5841cbc60c642f2aa3f59f1eca01f7802481358b76b89c085167d1b82e5e893efb5c368bdabf60c4d65c8b526e791bc70fe24da05333484e79a43adf9468afc8a513e75578bc37681732417d87d3d1a1e5a1d7680fc28c20793b079d2fbf91f06483f9faf0d3ed1f7e0c3414ca35b2f25e7277eda67942f851dff283b7a1776dff8760a130b2e4a95fa7a494e866404815d1f8313141a02a6fd2aaf2204dbf452587087026b47df4bde2c459632697f3909173803e820b6a810ead209db8c49720978493b40daa7651c88244627d27ae18420957e21543565fbd8367917180042e123042789206e7a0f382f05630a7233ce23870618eb29d57048ea6baa35a13e7775ea499b77fa2b2939a21948c33c49b80eb128ad72c44ec6b2c43ebc0ebed56c4d357759a372cb724cded062b46bd9a7242e8783d21021596916098b8ba9210ef0df0b8f5065757dfd22b3564c2cf1c62776f8659e98c20f45088864e86e85b8e61803d2e2e8db08c5597633e2884b1312f78654b323134010efe0a5a22f96ff83ab4e467746b698b8d2d68d2937d3132e98b53f62eb12c772cf6b86924ea8990b7fe9f6ca76e36ec6bb71334d5fe7ac0ff44d2a4c39e0447d41ee9144c125ae86e1696d83894163c929b99d5657682c0ccc8739c3c024b7d3b35be279dec63ccbd81e8c0d6e63debbcea29386640dd127985d1724b1ef912fa749d00325fd280af6eef3c77346dd5e2022dd02963caf2169bc8168aa63972cd3c404977532baec258ca30873d54b585b7de7de78081313ad920afd58a88ed6de63c31e93a3c05693b27665cfb4daddccb2d584b0c71f0577d99ae959e0d3295f3b009a1bc1b357cdbd35bd564a90c10c57a52c4d5c9e175b5bf6cda85e75a362d53be71619e033f18a912ac2bb171a8650b6494f385c23cfb56b3e56656966c1c3ad6aa4ff5fe93108e3aa5f266feb88c3654015fee429eeee7128c5e0b683bf8670ef76f2064c82e33acfa4b98fb010516c9758c24c08287df25152362f76b084441cecf158a6a03c74473f7ded3f9a280283f3a94306a6b182ca1c741819fa8676d33f15534ac596a39d184f208664084e86f69f491355d22dec6c7fdd47db8991f40363b46b90b495a01bb12e79bdfc17c015222c072641d13506921a872ac44c9cc8d108225e2aaf54c7d51d7a2b05ef429931aa01430a19e1d3fff8f96d9abb6c0e0cb3f645c059329e2c08162c99890102ba0d05634e00f98c8be1b71cb16bb2844c4cdcd67eea6259c5a9d668c058afd5889e27f0261c1586d2b4cbc5948a248e38240aaf1b6ee10801c93b9f6bc130cb04460efbe72cb208793fce34c92d5cf696958d7d560e5ff6847c635498a769eae2392b0e3ef9384f3ed5a8fc649537d71b6c540bcdbc695313c2d581d999edcbb7992ee1be226490863613c815b8677279673ed4659589212807f82da56e940e674601240ccf06909e2191a4d52f09a513a0acd557e70ee7bb5df0179cd2659547b10fa1792b0ccca44cd07e4b82df1a2c5e017d2f8a32c0842937f182a9f354a63516308aae017cf8a00e316b678fd379054cacb79841e0801c32e8d34e5b52ea8b0c8cd36caf58534592739dbb0ab2de7572923ec1622a98428768e440b38d4ace26bfbe8863b8dca57239ebb222338467db5400732d172f716e449e63c92fadb419e5644af6620b3258e48e198f2ef956a6856143b35a756ac2e35cfcb6eecc7d178c6515c496180fef749ac8e977fa63a28fe6dcfbc73ddb360ed5709fc9b0fe026c4635a79e7f4caefe5299dd7cf4b0210925079bae38b94a1cfd393c36b5730d3b39ddecf9213ed09c1760211d7c8299f793f875acc0e8a1e444a7c8009d5d0df95cf94b07d8ddea9e064bb63e544b266a94c00d0c91b75697c7164fea0f912983f360a60797fda997f5f370162ef1847ae23ebfff5e72a8e8c719631b7f8e24c08cedaad805e39e5373db066ee5705cf40a4641472965ec2e8165fea29ab119fb1fb534d5183820622a1d4500fceb3ae14eac2b420e084526dab4088f174c03abd7cdf5e33ecbcb6c6b1f09875a347c283cb0920de180cb615648c29c203f0ecd1b1a3b9d78569171956034619df0c3f6bce2442e302b4a7a0d6c321313491247bf62042d00eeb61610243fc11ad0d517806fb5f9ae448e7facf517db96571a6a1af751ac14a6383f96870460481013a08d296259a69405f1b94b9c50af4e262e4a32bde88f8a1c7abf78808893ed9a0b293eece0feb9e5fb3c86cd4f874b0a8851b7ec202d182ce02749dba88d816f94c2304cda0c56573114472ae4789903a9782552283c2c2b88ed75ae3a610b32844cbe7e7f7c74f5216fb6cb6615cc2797978cef7d47016bee6da4c77e9f60453cc17e44724812dc958172c38e98ba2407ea8b08e0f194575cee3effe062c4926bce85ba7e338a9a3fce58d4e021650d19a35f54d83ecaef10cbe01579973ff5bb6c07b29feb89ee137864ad11a2a70cb411e2a5b220b3fe8a74bb8d9bc4b624dc5914e52b9ace98e7671e0cf5e92b19536b803836dde90b9e384ffa4d590e141d96cd7944b79c6fb3d80992fc108702956f27608a987c9947ce3fdd637773787e0f976bee528e7ff57a7fb94fb361268bd81710fe3a615437e23e557ea5c485b4a6dbcd89e24eae87ebb9ae61eace002385c62bc8c9c1925ad79428be87cbc883c29c2bd9bfca108086de9053dd5a27999b28a3b0a8e41cf1fe430c3a035940be16521438e35fe5bbd212b8d0bcfb0135f7b015e51ad8ec8fa8df12cd077ad2bcf7a705889b9aa6674aa84c0428eb1ce7c1fa8d1b1adec22d51cc8a501c42b95c783dc1320c9b4d0fcb6f5dd4506b206f292e5d06fa78f52666675fde9819ce5dba63fa92ea8a6463422461f58e622d8937aae3718f509e1cecb3c95c9c82ae33ad610f692d7df28bffb8ffd4b3e4039e63003aa372590c543591e7d89ccbae71b74aa468a70da3db0e3ca463b15f0e6f4cdeb5bbf5ec3d736f1505610aff03ab11756a701f5b77578daf8989bc322dd5c44335a39dae704ad3f23389a598b98b69642f6cd7fa1830b998d64dc6a3250d7e6609e26e1725a4f6255ffe0e73e002b4aad16a1ab58b3fdcb8a26958830810ead864d8a658780beb619fab6c34e15199c1197968c99b38a5bf174478d413830e0056c5c2f967af9b3d26fb050519d4800bfaec15e7f13a94dcf2c5a2bd1c524d5ae0d73b30261003402d404f8e596ef73e98c0c4a6acb82456870f58671d138fdfb25ca284ec0793739a7cee577e6d3ba319efff2cf9f22b90f604e4f7014c701b5b3651d06ef3f3c3d566842801f26b1a8cffa425ec7aeffe6c1205932807754ec92ab14d58fff617ef19940a7e40c95ba9414628d59e6ea2fa9cc1140dd37f315857802b79f5d1b31c3b076de22ae95fdbbf2850886a0c671a67666a0b789aaeb6190fea5776179f028c774445d9c61c76dfe1a263c30a0ce5880bbf04378426e45dced169fdf580d0927fe256cc195db8122edb0732eed159a32cc72cd17f0ff0775670ed47492ab73e9bdb1646d1ddf6fac59a2f1698dbc00da9fa24dc2ab6ee98dcbd0682385e98342ffc86fc964c46b89ccbc2e990c68a761169037021867fd84d7db8c3a74c2a31bea5676045a5d33c4f525d51c11e01f58d200f0fa35b54f19c81087074ca714fed679d10f181f9cc2dfdc8cb1ebd703ac7200150fdd527067f0bc99b5b785afcb640874b3f62159de99771e6a86310818e5f2fb0169c55188588154761e71ffe774e208c335942d4dbe4aadf295d59297039066fe4bd1064d8ad040ada4cbf1f5cbe7b80e29396dccc2e0c93e4c1e0f373db107e57b560504f1e1bafce78380cd492758e1616d3076b68ada6dd12ed8adbdd14e14ff30a2ac375956f6087d8a68b13539c5a45d2b3de89d08a7db022f688050ff38bc3ad29e40c07a6159f119da20b870054811250aeb41f835d074b948a6b0f6f2f89422d726d91317c14441ccda2e382d0cf289c03d98aff6b3d34934bee232323cdc38dbb69f1c9092ada5e3a9cf8aa6e130203f1dadcac0ab7066a3de015dec2ab95501c4ce019c7cf444beb5d20d0e31b74e319c170c52acc6feae34009bdff8a7a5e189aba127f8d488ecec7bfee67c0e4da40da08e1d216c95e4e531569adc78433ec670379654df38bd2ba311657bd0273f3f945c45f7c9883e9042040c5b6cf63fb1069b8e0046174d371d03eb6cdd9029575e42b419a8b72380122ed3efddc9233f58b6c2dbc2a694d2b79011a794943f1a99e6603632ec4085927adb6976fb676e768d1ddb26b2c88158e8cba7b666ab6a20b030432ca67c8eb29c3b535066e0f779fef60ecba39001047452fe610643fb4491c13b86dbef2f409b504bb782009166f4d3069566271c6cc026ef64cee56c321be4aef2e665d47030a7fcc5527f2235722880893ad58c7dcc873095fa719bfbd937803dcc2788302a5ff8a658c1426defc4915c2e49e49ac66eb4e661d97eb9bc1c19d03228a58003a38e29ad1f40ecef9edefa3c9d86d6fa481d8bcdffdf1de0e419883f488bf2684050e1b465beca794930b7de04f78f0001bb18b954352807a99200f935f08133200c22324dff58e9f920bb0e54c8781c29ff6466cd26ada6ce05255248138001cd0f79242fd0768675144caacff113ce2d470a5d98f9ea44b6fd446e8511c1bd7c61a3d26af3415eb3532dc9b7b243b34752c63ba0bc2e81bd6000af3ee3e2f9409ffd485289bfb3e1ff44d8b181e081f9f948aac37c0d7d535cbef1a42501a0050b1ace19cc1779e23acc5aee850bdca8fd5591bbaa08e5b94d30df3f6b32a05139aad16899b7620a46d0be03c740e38704090e4d3d584aec4eb2d56f39a1573706fe29dae924284cffb950843d5e3090b3b59c9ea76a1a2215bbcd869712c7129bccf788ff4ccf5d9f28673b69788488553de67c648560a49f98c9054a30968dd170b7f9ccdc98147a7b6b0552bad5bb39a2eea0dfd942b320ddcac5e382ee141dd1b89eb46cb5e9da7537f63b77523abab01e9574ad161a911a896b867123735ad4c98de40f373bfd62165b0c737953c8c816c27142cd245917969bce31163936dd6680031e303c4086e9c8a6de7195c51fcdafaa9626f4691f201eb851e943c2dcf5a5e90005783bba67b6478fd637e43b5dfefac385e32f640c2e3fe298873ade499d92cca544769a88ccbaedb5cd4eb5e8e7a431a7b10cb0dce44f411aa3a22be6af7f1ef5b93f96b39c28a818225c7961dc5879318e9be4af78d59150260b3b712c8c4c615e4f0558d5d7abb9935a34b4e8dc050990c4ccf01a0432e2397266aaa7a635fbf97199e3dd6cb8ab9ceee4d1843f01e42df99b7b94179272eefb5e7453450e17b2efe24e3b585fe8707101d3d26e7d169e7e083538693066a2cf45e25626f4766328ebeb180f4c9a80d5264794e3395ce216080940d8b2f6709d618e173e4c91acbf76be013ec184526dd9d99b2149c441cf1ec1c7daf170670d5df092b15e582daecf85d140daa23aeaa5008183d5ddf926b0c8ae0ca22a9c8e4093ad0df5874c69c34b912671057b6afeda2fd41e0e223710ba8eb4d28afe45de7a7bfdf7a5d6443da901e2c2c966ef093056c4f62c820c8a0bd8c9c28344c664db4ae049971d8f089bffc11fd4535874c046cef36fa7a398b3ee6410b62b59f2b7c91d2a1d851e94ed0af4367f3a9aad84e12d9fff68aac9fde91bf2223dee4ea05d3500dfaab6f91203b3ed3f2346f1462240763462d1db99e8bd205ee3afb116f9fa13790c13011b967994cda0d7d65a4aefd076870bece6b2b3f2ab8907218701ef6694146c2c0ea8b1d7f40e95f744b957dfc325397e7ef1382", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441124e9634ac86d5322a89fe272dd95ec8f0910e46f753f89c72c622ae351ae9ed29012c2b59ed2e47dbf67687e99852bec5d63ca1680e3ad65cb5c747a9c37eb900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002240e1f1fe5145cc75fe7b253dd853cded77fb484b8be9d52aaf0a49098afcc7d24e0b65d370477cc168e835d8143177b582294e4a47357f372f960754b56b54f17b852c2d0067302ffa20e6f7068292fa3304e6e20e60a0c51ed55a2e0c2a2a615de98a2794725029dd283936e25dbbb2d8cf00dc4401eac6e834b094e5efd8e0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x000000000000000000000000000000000000000000000006443e4dd7cafbc6fc000000000000000000000000000000000000000000000009a5b11556957a2fac00000000000000000000000000000000000000000000000481d8c87ec8d9e44400000000000000000000000000000000000000000000000000027e3c69ce5a8800000000000000000000000000000000000000000000000c89919183396d2ee500000000000000000000000000000000000000000000000156b5f6351b4e1677000000000000000000000000000000000000000000000003fa278d2c3f3cc83b000000000000000000000000000000000000000000000000000257547a6577fe00000000000000000000000000000000000000000000000a7acecc082a3669e1000000000000000000000000000000000000000000000004e4ee60a9610ccb0100000000000000000000000000000000000000000000000139a7b0dc64569eb80000000000000000000000000000000000000000000000000002797aa349525b000000000000000000000000000000000000000000000000e4efb10408e5e22300000000000000000000000000000000000000000000000565dff2497b05203c00000000000000000000000000000000000000000000000c3ee606bf31b8fa98000000000000000000000000000000000000000000000000000144da9a140bb701dfc1ca1fdf6f4d4992fcf7bebfab3317fc2caf844b02fb8ac4436d5adea45f18531115019e88ab0ae6f13233c1014519feb8d5364b2b488e9d836182eba9d800f3b9f16b517a82e074902c3c99bf34e7be3eafddb913ebb2c8d05595a159bb2a543541d15947ed4f1c1620e84108ea3ec32503ad7d073162c30b94474e387c0cdcb845177847e28da58f2486d85540c5484ae86f2294cb46b4cb84d0aa3609083d483a8b920f898636ed7798e980729ee4ba163dbecdce8b02670604ec9b920e6c4aa8dca3ce558f7cec8fb7f4bfa1b0d4371b18bc835220613a1409fa080f120c539f947fdfeabe4f2a16449a964098c614d6fc20e3e80d0403f0241f5d8a13147d749da88cd2a0d8266d4380e5070e0fcf87e2e312a3cdb01409afb8ce082549d88678d5f866e68a4d0281b41a6b539a157e18a18aa47a9d801ba82c15b4285ed10e2b52a839fd1fb5a55f8de22855f4af96518bcaccc2dbce494a296ebe26360d30cd1d486f49978b029766d49825f3c54d91bd255e22e279a69caf0742070495855195395e1742793b60d1378c09dc9c7053216512cbd2452209ec4be4071c5a521b696e975b1060278c4f77f59bb6ef505a7223d5245c2faccf2d7a7b17bc6a08396d8036223bd5f0ae7ed2f2891a3c4e8846b79bd36ade7b09fd6627114aa182ae1731eb6b9cf19b8b841c3c2b6d8056888915d6f3833646d375991e22759e6ca02fd6241ed37565eeabe5d1c1ed7734680293580c5485cae983d60911a42e17e7646d7d5bf1b3633d1bf857ea6d0d6dd6ac3947070d93be4e391a921af9366211aa3384aeeca7a871208ff44293b9bc62fc7a31c346b3bc0a41d8ec2faba055c65299d4680c399879ad3b09b91ca75c154614e7c2365c05d9e1e1bf0312970f67ebf20490f3b18aa7c84bfbf44044a62091158a689ffb7eff443a0d006f17dce991bb9008594904f9599b39252e57e6a7ab50656c9e5a8df72b58d51b7c9f4435e6f52a478dad1fa0f66a4117c91477773fef118f1a5b2e2246a83126b004cc496b97eaeb0939d7fe1fe9df4c45268c13f7acd87f8d7a7251397537066d564a275044ff3e9013c7416550403fd3b89f31931a1c9bc4c78890d6039319c12ba5bf473841715e3058c08b58f6c885980ccb7196c78fbab8bfc448f50b23e59de65950bb9e7523d7c99364b96ea7e2b23207d02a56f9a6ba47ffe607b0110784790f007d11c1a16dfc27e06e1ca443bf4740ab87fe60bd2e4719005c8706dac42041fbc232faedc6e10c6a6f75b3fbc206b8f7c1648c1104fa3deecec91ef7ed44600c3779a8af114f9e25af821788f45bd741c89b36e53c6e5e6e983e23f372c640298eb2ca3865a651b2add2714020d6de218a78a66e53d5eb9c037921c9f13da3795f03f8a780a09119c28b795e6e6b97d67250bc77c40a81ca7253285c8d29c99aecf5b4aec5316f45c7934a7c13e79ba5f526153174eac6f0fa7f1d5423429dd2204d5a0749d4f1d72ded1463077de54e2914170e854d559814782791a449f5831160a02643aab224d9ccadbd7c12366cbe5895233711003e08701b69ae17495eb9c96cb4741fa95d3a2342926bdd5a796de050b055178f9e91ba2582ad1dda122e9168349f910fba42f2389be4035b6a4a0776ec380eab4a7e1818da3933777a20728cd32e8aeb3f6dc1c954b5e7ee588996ffb14ff769561b1427200b52b53eb0c2aeeed7d7ddea931eb9dcd23c5652b768cf3fbefce53500b8030754c5c1ca1108f3ed46afb942bfc01b032b26a8ba44e62c0365ca4033b1b219a80f8a2ca3ff9ad952bbfb2493969d7eaa90cc4aadbcbf36dc76a0c848bbed2978369892c56259b660b0a07ee28fba8b592f129d029bb31b80535d8003742403319cbb781f5e4270e2aac914ced8a3fbbaaa3d9f1e834bad9b9f07b44814681c3e7cf0859f312e121eee65169a441b1dadc576870842c917a36a744b4f4da91c33208fd45e9c462ef67ea2f432b555a46ded01dc8ac89fafc61142efa714650817710edcc68e99b475b7ab403a6de405f94656a27d39fe369e561b26a5457c2921ad7d3ef554ad5330d553e322f95d5c40c50efce4a009493e71875eb2182d0f82765a780330127718248a51508491c0d2bd80766fc4007dc818073c337f882d78d25f805d23f12d1db526fca5ffb5be0e54a6ec25e46bbda65b6bf5aec648184761fa18ff033b31c97d42fd3a43ff3f7ecaea4d304d336e0e917a0d2e4425050be6d7fb830cf4562eb87b213a5d6a0dfe7b3d63f8e11e3b1efa9411183811071ad9e3d533211eb93739574879ef787af1314cbf7fb4e874c3ce87ebf8c3f60f93f0b8ef56a6f27811b193c26971d3e773e76e68c5daf56140ba20f67be6242773bd78452b12738a0ec3b6204e93c9377acc0358971d74ac9bfeaf9245d9421a7d20b5a4a2ecd40e4d23d71ca5a7e131b932acc89bfbd6866b38719ce78d212cf028cef1628129d642240f4b2730357494ac27446f5e56c0978e0c2fc239ee112666f0bee00ed47f0164d22a5215d5db82f3ab6cf249bb50703d3dc27d71423028a4f72123edc079901ef7ccb56bef61f6591b0477b23cb07795773f20e2bb12e5e0ef459fea65851cfcc4b387fc42cd5e71bc089a7f4381c913855a4f2f1f0106cae843799f89fa4b279a364f633a37e4b2640c69857e61279dd803ec5c6d039606c9c10584fac7c1d5b10de7b25738d89fc5569329afd79e885fdb953cad2cc05964e713e4c71e4c07ff1cd10c406ff55cbd851532a4839738d7227ed257241f748badd25148e8ca89366948ebeaee84d613987f693e0d213aaa594db3021fe702e566a8ad2d75ae4f812859398669a51eabc475fa34a0d77bb9611932a904ba84084825294979e0ede81171919e8bbab7354cd01123a8d61d9548b311ce05d765d5f5322a49e084b9796079b3196f5a5ef6761db7007dac93649c4a387d02a34ad68acf136903543014ec4b3d4a3fe900211ea22ea18eae5100b319d329034f88cb7f95f41dded0a0ad75b68c71278a61b51c19c035158fbb8259d36bd505735e825c6b8f845e8845155cb93ba122d3e98c3db8da44666609cf7192ff0d15d728de03a78427d9b166b5ad8555e4b65afb3244bd53993eb496c5cd50833d22341df3b6674d72bb8271c4b82e7f03740f15b18a7ce3fc8943f81f826fc8fd227bf5ff41b0989a237271e140ec6ba99a0c2c913033c1dfa00f9613114733a42111810a91eebbb5bea13246518438196d97dabf006219b2203cbaf73568d7f8077e01b07756ea1d188ab0a2d7d2b901634618048e281da916256acd484950b7175c097adab59b620eb4718fc51493f0566c9e30124ed27baae31fcb691fce092acd8a3e8ea251794afe8d35d55ad7037ee89476c111e04610f219a1aee124c22ac4339cb28af586a14d986b328dcbc25636df3bd150a77cea7298745cb7fd6b2ef63e7413dcaf3505f98d6569f1e4c8f3d5082903c2a15cacd73e75fd8df54e2307f9e71217ebbcf22bc61abcb8690a22409214cd7847da52e761f7ced049a31c763c92153a0b2904739905c9cb26452f4f573ea8f9a7740b72e854a50ec596115078a8b11c57010a27a043652ff97f51ca448f30614810e3765b8a4149a63e291d1683a4b9a0b964f6ffece7dfb0cdf1c4ce4f0fc04be99672261f50284503148cb9351ff4662a49ec4803560cfbc9165ad379fc90ce53beb5ab320807a8d91d0a10934a2b9e4363934b89c786d314b1a242e9128fccfa473dd4fa8f8496710aa112f31617661961f5e97fd2c3fea061bbfb54946e7687ec43dec8861421bc140c7c1bdb0a3d9b5909cab572d3434a2431c3236a57baefb073fd1bc6d6d1912532702a84570676a2eb0d0047ac4b2bc9fbe5c927e3dceb3c5c249d3cadcbe929008bdca758870e554bc839114d0ba80cca1fca48d28166f949e52c54e6928b1b9605ffb5f5f6b3db23931f7e59b994d47f7c474a3564aeb179664ee01f91770839111d61a992bd1ce5fe02631dddf3d4ad212e79a3aa20c635f97ea8d6db1a1bcf6ec685335f5a88413279438d62e691fc5848efb92ad7dbf44ea4e6f23a8d20414139bc656a727e0ebe69c8a9dc3802d314028e5a8643622b705ae5ae82e01e2578c9d36a252ed8138a7aacac14849121aaba10a66d6ddde63431e9bae2151950ccdf9487c6874ba926706e38df897c4ca3183893c211c3846182cd2588da2d398a58df3e979a559c498fe06a4a7a0163ee286354b8da07da8b9eef9945c618800d7ddab549c895cc7e2ff3000f1781554884bfeecb44e4124d603ebc19b829730a60168980a8fa287fc8f86c0a8a90ab8f38e71f804bba01d8365d1255930ccb79be29beba86407961ab765e979b3dafc32bb1f393d16702fbc3566aedda089832173ca5a7e5069fad1e0660959d49e0bfefd74d2314b393f2fcccaf682a22bea10fe8179d3ba752e8044853c47f4683b4310914f61e58b7a1df42504a74113e6facddec9a9ae5b220a1d6cf4fc0415e9f725e11104af98a7018d5dbf4b5132e8a07a7b4803847e32293d13af6a33d297cf15e40db4bade0a4774ac86b0b12fbacf70560a28f3f7b4db8fa73364a46bb2eb57964005bd8e58c2e61eefb861e701eadc30caea24196fdfaff46b16dc10ecaea90bfdcda36bd80ec0f006d9d2e1bcc89a79ee552a9d1d32fa60bc21ea126a76871ff91f479eebbf887884f360274da76723b26de1d7a5e8ce9ee53c7bfe8b5f29ec186a407e75ca0e3d661862b867f5fe4b79871eed93603d868ecbefd6a9b1188007d91ab5aa9944fde3f6b18da6194ed89cde701078d634b6c54192dd528fa637977e5c3675f4ada2b502e13be091100049adcf52752f514bf95c3fe99e4e20abd509446b5fc66a8857e1d2001066787b655dfe8931150d5648b382f60bbe72d6e66c73e3e6529d54dcf4811cdcc0314c615042bc66bf5173485c9881e9544493687da1731b6293a9679fb04b9622e9b578c81c7608e01f3053cef3f632d4ef5a58f172e8bf12259f6d5f21e25a74e2fa134a379f6308d57c51335552484fb0e88867c9d243bc181a090cb031f58e0fa9f655929adfa77a05baa4894c7de6a6d37290d3eb16283ff637dbd2fdb3500df9f2418aa9a9f250fde66f091fb6ef5acdb61a27902e22fa6d5fdf0057d28171834d980f73ca8ccbc0b303a5062a068e03e7ecfea78e72f78bf920d00be36994a8ee4f42a36ed3a223fba9826a967f946e75760a85a595015b8af2e24ad7c59e99b87f60a5fc7c76e351614576c43732e32d314ddbb85daa881eeed1b150a822d9945663292ce60728bbac00dc5ba3c649e814005a52df5c0fc59b81011cfe6bed95a7ff1808bf7c6e1984dcfba5975ca1768a00381f2974a32d1151e75c2894ff4581c65de16e354ddf938c056c93a50c61579815a9f0282488c76145951a635f929a71f8e662224c9364e12748d715cbc7bc93c5606f07687844222416ad582df9b9091bb790236a28d090291602b43b80c146036ae1d2d3c911123f37a291933fab7e30deea52324ab9ab3adb486903c9960992b5ad8cf6d89670e608c9b4061890a793116ccb7fbce38857e020824d1db42f14fc651164d22a60aaa8f2ad0f20b63778277b1f641d7fc5d12dae47ddfcb2d55e6b870d8b2814c20ffc73af5fadee89dc3b3cd116e881d5c768520e9fca681290ab5c58ead371022b65ed2116f353e440fdbe5d2fdf3f025e2a6d2e9d3e94c9293ceca8086b0de285a7305750eb30cacef7b5469a0797e42080970c0c3e70f990892b6ebff1044023dd8d11b77a419b3aa19d4bdbfabaac8d553dc042fe6c4555b6a14fd64ec980146ddd60ce32d76980b01b71eedf0bdee8b18f198cc0255d73108f17b509bc13025bafa936bc61579f8c1a9930017d1f0b042c5bb0566743b6c731e05e3bd7d1cef12b035210c3ecf81d2caaa99da32fea59317e383b970e419f861ae2573fd2b3c6b4f0c1ca9dcd2203e4b1be938e7838f970bbbfe78707468e381083605301c190b6c5f06802b1dc8b2280acaab86ac5046231ef1060033cc35ee76fe2baf2d661ab0c19ad8987ab3cbb89c97f3c6368ff099e09d1febe8e7f6ecc1479f972b5c27cc320c875b5f129f3b6ff24b4c8a2eb82af590659ba225f3e190fa4e6b1008897acc6e797f4357055c26eeec55360f23d1da9543b6ca7bb34519652626273e877efaf30df25e18578770476876265c499564d49f34e9f6a51e30b1dabe0c32c7acc142ddde363e9e60696c0fab4def3165b7cf5799816352fd5147cbbd053810b943836eae47e715380acaafecdaa5c56f78ad25cd6beb48b30b71e97224546462e575474e305a384c11241bfb4922692f7ada05ae8ffe3eb5a61f4a432346ea3071cda58fee452fbc4db70cd573ef7d17106659050ca1418bbd2681da0af56fd4cc9e2c6418372609222d7387c272a336e2b34a001899e647750744331b117ae4529b0938e4e4712dc5686b6caa250480220d3e94a35b9632e3a73fcd126572f8c3d4ca698b890cd2cec074e5e7a8a7f6f624f385cd36b9d21144013e1d4d49e7dadd46d3bb09ef8d4358a93c905259d943a18b6a020e9701b77274ef12ca1ec8b6d0d112d3b1dd49f29c7bc13ee5e7ce00c1e5ecce92a8c5b0b5b464094dae99c3561ef02693568d05c04104963444dbfbe0e6cd804ab19753689d2f1254bc80d6a1dde41cae49945131b625cbd58bdcf37f4cf699f10aaa7267e1d62acaedd0bb43e6003a32c7112ec16756df55e6b945e730898cfcbaddc25621cb0f3ea6045ecd0c18e3c46ceeee924bcd8b5931869ec38afe8b79b6dcec6e649f26fe612ab89de39ad9fb9d355f67b6608ea70697d9f7125cfa4831a71b894a0a0bf5eaba34f2e38a421e11ed8e30af9a02bac691e911dfd2c5cc8bab6fdcb23904e79827c342bca3644d0ff0be3bb71ebeddb24e04066ab1b9fbb82705a3266a1b742992abbea7a4b968075d5348ff4e1068b01de9997679713f13c50b558d69224e93ff28adec2874a0f0131ed5937a9a84f1a1d27815ef6d4412936fb2d9ab2c9f461e5eb9aaf0b937abf2c25c62926c3052eebbc13a311af28dc949296b271595a279367d738f840aab4617ae5d46cf23b6a6f6b932ffc79d71daf27bccb2128f76718008bc6be9dcdb3c0b25b5172b1da3752ebfd6f9ea2c8c609d906b690b01c120df01729bd22d73b94dd8afa3b34ecbe74d60ea6d449c60645f1528622ff8a547dac1025fa5fce863dddc98f0b30fad91b413d57072fafb52119e58ca1516eee808b2a74efc1cb43944b1974e17032d21093d286720396ff7399fb63c1f79cd6a75644f67501af7e6d876bc562174d7fe4eb0434c4cfee35ac6be75ac1820791537c5df12a815ba14d92b0f4fd6d78169fa6451cfff0eac6ffe080c5c2bd37fc059beb2f3793a7cff7025761e181b81ddd54b11acb6032db51a7a8ed713315f3711ea9f0a97830ae8b36494b8543ac9b862745c4215db849ec6a8fc6a1c5ee285c997e54b3c9fa212c4eef18b07de6901258f3ccd0dcf9ae10e0cec6028fc22b8bdd77d9f5f5753eb121e13d18332b7ab5e4e1b8da6ed82e9149a8261093fe0c9ae8dea8f167327c5614e10282a690196332af402f400d795d62b79112c1b39336e2d239a9a003fd684b32c1eabe3d12736033faf849ae7b6d13f44770823917fd825b94292e2cc911cc0bd9f55e29597fc6463429b0e3113ce09bf1223150eff80c462c613876c1afb7506910c9d57bcc4294dbf2154e84ded32aee02db67e7e6d6ef0b310397c49c631a159f637d7b54fbba46993bde91e06229569289dd10aabdca78e37e5e65111201ba9db43a5d736d09002d9bb979ba354884f0ddc0e655817c6eca1bf4e8399e6059ce13bae13442905698486b29df39028c226fa402d682da8022ed12079080cfe05a60c2764aa1d7b8ce819ef89b73474a62a32750b401f56656c401f6fbefb83b3fa391cbd494326e63cc172aac33ea37c2ab915cfa8935f6c1b643657122c82828d974eb995919f27f9045f1bdaed542120f563c340c7534d63bd89254d5aeb0962d72358fddf008461ffb28c7ca3331224a274f4a23db46311983cebb2a0c25c643781c581dd1853a98484e7daa303272696b4c3fc3250899de7b73273f0cb8271959eeba9f47d6f27845142bb5a30241b037f5bf9b9a97dc46a8293e61834ce50f783911f3be4c332cebb2a1254d2d02770490657b1d03830ed3ee8deb26801236387b361c6422b4119a60b45c17936281de0179aec58b237faadaec4581354ac0fe01e42e4aa3e7d9660abff18d87106ec366b781a8e7b6815aceeebca6d1742fa5b320e1bbccd556fcab55f5df0fa25613aa330b14f831113d7b0e58ce43bf10ffbb5922d8037fd6e8895bec323a41dfc55b695ae69ae883e77735dc097ad451c8943fcdb85658d42a356aa45a10d079f8b8125a7b007f583ba170dbace2b0baeb316a85c9992f4cbe7463bf97e420595f7e9889b31842f7bcb87ee9e1659e67146d3eb1738237d580ec4057b010007e11de07d72ef853d011846238a94be1d45b6af5221dbbaff5d7f2b345aac312ee1f0ba76c97dc811aaf550748da69a8e703e30d162f6c4d5e3bfb584709c610de2f1e1324166ce349aae1e7925db177f435d4dea9943c13a1a14583bc4806811ec85e76d455b6ecfe19375719b71801e53989ffd31d68393958d5db8d28f5e23b717a3076b3f25ae79b373b4c2f8b74ea54e68d5b611508ca732b86b8e682d0d2fcf22fb341c462a809a437c34275a2b1e99cd2f306ca0e5c471513ca2636b189a2f7b2fd610eef2f1f564c494ccf64b450345ad58e46520438d478c922304080088b7ac282721cda9cfa89dfda2df6b663c9e071514b42a6e3e3d3f4b8a382289a0b58bdcc4d9e19fa811d6cf64b4a703bd072cd660021d4d515e59e6df6b3046f12a2efae22d170874e2650dcef1a24d6974f77cecf09d6fbfd8b34feeb40652708e4d2e7170b5938e06b95d7239537f7ff192bded21492eb0b817d58b4d165b06c7bf692e0321aa3778a74dcadc77994701581806ecddaadf49f7104e7f0bc5921a00718a37a6af9d199e6e7d94a2bf032e92c0eb564adf2a7fc68f24701231b482e813783b1f35ada11c23433530cc7bb6fb050f5e9ecd90b18268a629098693da7c2767302986f142e06ccea3467126f980182235dd26f84f3b4168402ab0ffa139de9f700d94fded6ef3da03e6137e64131ab66dbefb912545cfd9ec2140d43b2b04494a1cb5de412225c593576471f8421345c6461404437cd50bdc05e52284204951c094ac5183937c52eb1f89d5efc2ddad671cfaee747cbe411027d28a630695a70bd84e6c8f2204baa7816303b598333816bd2e069cdf2d900c25394ac8abc092015c03de91897529f88364c5920c96d79d36adaa49fa4ee79a1acf5eb5cc8c68c2d207e2c00a4be93e9f7f68fbcf3b22641cc46af5b00e468b266253abf616d8a7219eebf4ed0c186804c9aa518e6498a95d27870b09e9a7910d7d2897abb953838358a1c632c883fdf1da7f014a0145b925a4d62389afc2d21d320b72198c16c59816aa7980f35282f8161997479712acfcbf2ae7fc939048221d36c6e5386de18e2e31bc9151bf31caf26bedd9d9a294b1582a0ee1fd54da2b29ef6dbc542e0a402324832ab4a568d02661bee1222aca13a8b8fedf6d1a212c033e3b768116702bbd085431c7df39c570843069c428878590357edc235a0d12653312e04bbf99f98c84e15b59ad2dc98ebfbb620ea25d8800361638225f0f26fa394aef30b30116a967ec0c6a1fd541f82a8091afda6a6d6efeb080c1e0ad26577e4b645ddd36cb9658dae88d305d1fdc55f0015d1b457fa072d4873e8fdb1e81db21d53d8c4cd8c3d14ce58e0a65f6e4f36053c3c0c3243d19046bd8adb80790a527dbc5ede9d8ecdf60de807d0e924b52c138cd1ac03a04d8e88d07c6de29ec091c14d2da49d5d8782121da331473e86bf1bfe45605de793e288bb1aa71066aab84a29256bd7b881523d47ecc983a2aa4255097c5898bb5af93edfa26d5015bb0c6cd798907d7713346c22b48aee272d38248ae714ea7ec5b82561ad2311fd201458f45a4dd86e7a0f108886af9c8d7613617ef0163f18e25aa9ecb792929c4b0889000b9a49cbc9fb16aa12d3cfc54184f17e50144038d3a2a3626f31e2d9fbc165df3f9fcd75d9e99f535022ec91b19bf7c24a033acc8944dbe79ccf224ce84f0ab04273fd6974069d53931a3a69ba7714be5da0f234df46855e054e8299b8a573817dda6b09843956df7a0e2e21d4ff9d91853431e11061482a315f001d434871e69df4e1f24275172d544c130a1c6d40b3bd6b1d7a3d4520217577129eb8a792342940a31d20af3a99f0f4e005da9e561e5237598bed6ee3ee3820a170ba23dcdec74b1b3b461944639305c1596d3b64c83582a758a345624f51be201d7688c69fddc0ff0eceebb2de6d06dfd6cbb7047857b1ff42f680eccd1ac212b73a863059add2efe748ac8971207921c1195f099f9ab6eb43dd55a303e7cf52525392301fc899dfbe7408424a67c509243e5eccd4cfeeeb00b78288506331f06eaa5fd76a09b44f56feaf2d7af4d23b05308b70ccd0c1d5ad8ad1d47b6ed200fc31fa90f5a3102d27b3b1245948561a7259fef448d208a4d87472bb58b647024fa6e040e10f43ebfa76dff924cd8eb7b20dd80bd5e8cf3253fc75ba2921ba21a0ec8828f4e0b8b951041f4e6b816ba41c45a187c8a6434614b24a67aad33d81226a98970af6c1c254981f8fd1d3325244b579ba13038c2e15e670049d7add31e5883b5055d27d3764f707a699a7c34dedbdc2dfa2388b865e81f2f476a9d74033cf6829ede66868d1df503a99b0ab65dcc9ff4581d674e3cc0edd62f6144551a124eabc55b48029f4d416c598c41215d2e3517fc86598aee3d3876d356fea301e68d91f485409a6939dd9136e8ec513ec47d57cfb3f3e0c130160725b15d712c4d57fb83fce70a48b1f42a06f7652f13a403c42e096aeffd06aa22ababfa6f16f60d110b6e2b66ea8828bff298724e5b5fcf493b2106050b85f98ed06491ac17b4c24d508d47a33aeff7a156692b0f923db58fae1b5cc154be223e28cf7085008215ea995ef6c42fc4c9fbd66a6bcb70b2b38709054ea35cd475bb49de59e10956147508ba2e4fb0382e90998b369f03ec4b14d8d41369fbac5544b4139cbc2bd1cb184c504f95bd229f7df496b1e8148eab68ff7a47356fcb4370c00f9a452fad6ce685e37ef2fbe60c09063c8269b6f4a4f58501bcdd6516d59ff13bc307194265fc4a499426ae6b3d0dbbd38289a438005d3a1230768fe7f22a6591d73c1897db709e4dddee2ffea8f9cf5f7505c38eee0e056bb84b077f668fa3fe29441212919a9819f2e3e22e177096168d5e245ef96e5c116cf5c79748d55620b9901ef44b6da68ac3effcce28cf482c7117c35f4bef183e4bbbc96512a7fdbfe0381833ea263ebec528a88e53d624614b2be689b4755ffb1d27f661b4f5a41301b90dbebf58ff3eb42dbd35814ea7a002d1e718e722d9adad207374586887a5224a2e1c83282cc07d6cd0aa8d1c5758ba79462ddc323bc3bbb930373ef1c53b92ed2d96d4d238155532d1953410af79f3d08aa233e2fb256778a04dbdd925e7bdbc182a8858a15071189ed105aa6a222a7a4ffccd9aed35c4d7f7ba647273e2393025bcc927bc2fd289d1e434e8d15d038ecef09f44680b726df17fe0aab28e386d24fa7843e6fa06f472c82dfaca32d85fd582afd60ad817bfb11b3cae843c751e02776d66f8bf51a55acfa283bc6442d185512af1be7154b7a2da03abbd4a60c4127af50b915ec98859d5c684edf56b08cfe66418f2a5889f9b3a87aef425f6950fe98c14c6fe622d1d1c2bc47cfea2b721e97c8dbb1d9cb37577cd019545b71f0285121e51f77094251805dbd8d085ecfe8ccea6723dc062bb3fedf7c50593b0205922a540423bc8c3d5e42569bb2e7247ea76f0336bcbccbd1186dc9ce7482d03da45465c43bd0de56532970355c7ef777b177c7fe6a7532f317a18f136fde2224e443ab209e43f772d8074fdfea3b067c5381c65bb75025ee3f9dee77ce4dc03c743c2c27d44ef52d099e299389080c52600286711e9e1c734dcdebff96c9219ba0fc80d6ea186e6c29dc5c3191ebcddb75f9a6fa142abb71b0eb24ea3fb870d74ad181209cf52fb62d6a0b7c45d9f539812c01d85dee2e3de2131e9fc0e9e2df8e785877d96f7453353cd8e66536472753c38f34c924a11f9bcd3e1df5b24225211c57d2be13d95cd10a32f573bd85de9b70bda007117fd932d6670a44a0206cebe7808618526d6bc0230034e66ce9f7e8f7696a54be414b4caa9cd0855400c5b7787ab19599dffb8d491cab2af3858179c4829812ccfa8950148db8b0925224e70668ec89f91317e477701835166eeac74a894791d8eb76b1baaa550f11216c0fd4163f586a4e16ea766eeeda37ace01ec98c628056398c52e154fb4fa782f2af6aceeebd78d7f15b25ad97c9903deecb86333fab6be68bd8c9ff9facfe4214f0826fa40d3f55a50d102bcdcfa14680605bf36c2d142623c49239b8c22c31977245235b6d40d8c19937dd89007a42e79bc006ca2dbd5457f812fa1f32d9f1029f5a1a4d325d1518f606530e4b5d112c7fe4fd991d78467c24a0487c346a3258eb48dbc8290f47244f0dfd8152f2c2b6c12d1825bd4ef6c35da4a958ad6db192114ee30ab3d73821882eb27c746bd064c8d29a5f970c77faa3b11299f4bde099787744f327baf3422124943d9662f27c114be514e8c81cf618c21a009e2f82331c0ce3f8705e19f585abb4fd7adf1b9718854ad54a8bff4281e2dbeea99271874da9862988c3799ddd9031aaf478420767989fb32ad7577fa16523d27518e0a961ab41d2f1a80097e95d8c03919749c46f05967906ae8cc3ea5de2d4f25d8110ada472dc029c62964550f9ce1372c049f582e33fad78b81872c25d5e7c0521a3f7c7a4873e16b9def6fc5cb6b8bd7d918e64498de20f4de19f6d0c344e526175ab4d28c8c1f400c5b7b8cc871003b1d5cdcd40bb673a700a1edf8281ebc4a0e3721c6515c27adbc7da9925914ebab3eb9dae0cf2200cfbde8e446c38d7bb4295bcad28c370cbdd42eaac458f3ebe5cb7fa2ef98f984babcbfdf06f34b8f65091ee205e13701a1c2ea8a843f5c4b6efd809fcfabead21b10bde2d146aee526215a4fe1159a4d459d3e0016eaf546adab9cdf32bc4379606278bade85a725f7158a13d111c9446a19f6b0bce14bf1651b10bc0123a2a09faaf6919b112dddaa0a0ea3a4ecce723e76ff8831e4cd13d7424c4be81605104116740d58fb086a712e326db739292746d1985c1f9ae8899358e00d533a75efb5d7de1ad2742836322d49011ef90a17d991fcea02ac3274e6b2a9d8ac45e7227708a6ff1b1434586a11db76e8211ddca3020b2850fb31e09acc20b25a225d945840db8f189168aaed18b1ffa314189d1a899b0bde3931abf4178309c5e553cd375db69e22ba0131d8234633d63ff52a14b8ee82edebff56b20612aaa1828f1836d47cd48a889358a31c2ed3975a00706170f68e5c4fcf468d90ef43d57ea3f28b737ead30045fc1be2f085a00fca043219f011f86030f7f33ac46e1f9796a788f6b1c600c0cd7f74d21781ea6582c1001799f2d348f46f6a81f723560d7e920c1c8fcbda35d6dd4560fbcc1e3fe95963b4b591935b7506b55e309523f6ef0372aa53f6f6f39a8fced3033a3b0ff7bf32b9347d397c052d1114065d98e7a107ca39c1ebb71d52d79f101b2e9d8bfa85029b731263a28b39c9f0e9400d3dc6dc1c9328a022a74b704f2000bf3cb1c7232b14fed08b9c620894f038b79fa0dcd99081b21527561ef27b71531fbc148df29328c03a958ae4a00e803eac930ffcffab30209fff34b7d3f79102a1b45d43dee8dcff85bda9102cb809b22e2e10f32f6af247f3cd2b5db032c29d5334d06da44474e388dba32fef58f6f41b64ac8c0ec6cfcd4dc3e0f2673f92e5b3ff63c00f9d5296e18b42d1abd074dac590e7c6e16d339e714a8168fe9dc14582423af2f47b9078fea487c501377206684605bcb5724e6dc63c1e7822abb0855c33f180b5b20ebb2b40c27267d315c4c2bc75dd5cf4a2a5bceadea37ed78153883fb91a526e6f2a716b7139fe0b86b2a8900372f0172f3aaf9ee4a2019ff06e9f15dcfbba8dbb9dd9f3a28e9f54bfd43de7e0f8482a383ed5b608df6cbb6107cbdb223a442c91a27b79c4bebfab136990c609a006f94a4b4aa357bf3b4d70581cb1e022e7beb4fec5b5fb12d0c964ae273eb8ab4dbc0ca9e38768d621b831eb161ad789abe47c91993aafa4250b09fbac7fcb9f489d93a0be0cc4980ac561a170026532a309839237bfa1081eae9a778f6b16665de3237f00c8124fa1fd3253636290e6dbe6eeae74358d67aec6b012104749f53bd345b362b75ab3b549d088599bea97a0c7955ee2b01338bda7d05c5579e6822b583147e1d0958e70eee1b1d6559c7b4a21947d81d6d7f82d511133b9ce051588ce4a1a3e381ab5775ad0a93dbe6e14e6f4d040a4aacb7864a1468b61fde0d2aad5d57f75c6f9679afe9", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441124e9634ac86d5322a89fe272dd95ec8f0910e46f753f89c72c622ae351ae9ed00000000000000000000000000000000d50b7cdf578f4412a219947e71659c0f00000000000000000000000000000000585506e2fc08284102cd3ce553aa726a29012c2b59ed2e47dbf67687e99852bec5d63ca1680e3ad65cb5c747a9c37eb900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002240e1f1fe5145cc75fe7b253dd853cded77fb484b8be9d52aaf0a49098afcc7d24e0b65d370477cc168e835d8143177b582294e4a47357f372f960754b56b54f17b852c2d0067302ffa20e6f7068292fa3304e6e20e60a0c51ed55a2e0c2a2a615de98a2794725029dd283936e25dbbb2d8cf00dc4401eac6e834b094e5efd8e0000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } } diff --git a/circuits/benchmarks/results_secure/report.md b/circuits/benchmarks/results_secure/report.md index 0f232b173..c12e1bd71 100644 --- a/circuits/benchmarks/results_secure/report.md +++ b/circuits/benchmarks/results_secure/report.md @@ -1,9 +1,9 @@ # Enclave ZK Circuit Benchmarks -**Generated:** 2026-04-29 13:43:47 UTC +**Generated:** 2026-05-20 09:19:34 UTC -**Git Branch:** `feat/benches` -**Git Commit:** `36c01c62b86e2527279842280337f2f4724d2487` +**Git Branch:** `feat/1525` +**Git Commit:** `e0d477e9e98185779250f4092947741e84169b02` **Committee Size:** `H=3`, `N=3`, `T=1` @@ -15,36 +15,36 @@ | Circuit | Constraints | Prove time (s) | Verify time (ms) | Proof size (KB) | | -------------------- | ----------- | -------------- | ---------------- | --------------- | -| C0 | 287764 | 1.46 | 25.10 | 15.88 | -| C1 | 2432074 | 9.37 | 27.22 | 15.88 | -| C2a | 3879330 | 10.95 | 26.13 | 15.88 | -| C2b | 5739750 | 19.54 | 26.31 | 15.88 | -| C3a | 3764144 | 11.16 | 27.82 | 15.88 | -| C3b | 3764144 | 11.16 | 27.82 | 15.88 | -| C4a | 2564001 | 9.30 | 27.21 | 15.88 | -| C4b | 2564001 | 9.30 | 27.21 | 15.88 | -| C5 | 4395328 | 17.68 | 27.28 | 15.88 | -| user_data_encryption | 1678200 | 5.81 | 25.70 | 15.88 | -| C6 | 3001847 | 10.47 | 27.29 | 15.88 | -| C7 | 128310 | 0.53 | 27.59 | 15.88 | +| 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.41 KB | 3037922 | 175556 | 3213538 | -| Π_user | 15.88 KB | 0.12 KB | 2972869 | 193468 | 3166337 | -| Π_dec | 10.69 KB | 3.41 KB | 3549077 | 186764 | 3735841 | +| Π_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 | 7204.02 s | 127.00 KB | 128.56 KB | -| Aggregator | P2 | combine folds + C5 | 17.68 s | 10.69 KB | 11.09 KB | -| User | P3 | per user input | 11.23 s | 15.88 KB | 16.00 KB | +| 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) | 814.17 s | 10.69 KB | 14.09 KB | +| Aggregator | P4 | per computation output (C7+fold) | 835.14 s | 10.69 KB | 14.16 KB | ## Integration test (`test_trbfv_actor`) @@ -54,14 +54,14 @@ | ------------------------------------------- | ------------ | | Starting trbfv actor test | 0.00 | | Setup completed | 3.27 | -| Committee Setup Completed | 20.26 | +| Committee Setup Completed | 20.28 | | Committee Finalization Complete | 0.01 | -| ThresholdShares -> PublicKeyAggregated | 7204.02 | -| E3Request -> PublicKeyAggregated | 7211.07 | -| Application CT Gen | 7.75 | -| Running FHE Application | 0.09 | -| Ciphertext published -> PlaintextAggregated | 814.17 | -| Entire Test | 8056.62 | +| 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) @@ -75,26 +75,26 @@ | Name | Avg (s) | Runs | Total (s) | | ----------------------------- | ------- | ---- | --------- | -| CalculateDecryptionKey | 0.60 | 3 | 1.80 | -| CalculateDecryptionShare | 2.12 | 3 | 6.37 | -| CalculateThresholdDecryption | 1.94 | 1 | 1.94 | +| 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.23 | 3 | 3.69 | -| ZkDecryptedSharesAggregation | 18.90 | 1 | 18.90 | -| ZkDecryptionAggregation | 48.06 | 1 | 48.06 | -| ZkDkgAggregation | 20.90 | 1 | 20.90 | -| ZkDkgShareDecryption | 30.16 | 6 | 180.96 | -| ZkNodeDkgFold | 102.34 | 3 | 307.01 | -| ZkPkAggregation | 49.02 | 1 | 49.02 | -| ZkPkBfv | 3.84 | 3 | 11.52 | -| ZkPkGeneration | 65.13 | 3 | 195.40 | -| ZkShareComputation | 52.43 | 6 | 314.56 | -| ZkShareEncryption | 112.96 | 54 | 6100.05 | -| ZkThresholdShareDecryption | 244.37 | 3 | 733.10 | +| 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.27 | 5 | 1.33 | +| ZkVerifyShareProofs | 0.26 | 5 | 1.32 | -Sum of tracked operation wall time: **7997.16 s** (often much larger than end-to-end wall clock +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) From c0c670f571445bd6922806d200ed56b6ccc12fe1 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 10:06:46 +0200 Subject: [PATCH 16/20] fix verifier pub in with dyn H and update benches --- circuits/benchmarks/README.md | 5 + .../results_insecure/crisp_verify_gas.json | 64 +- .../results_insecure/integration_summary.json | 8 +- .../benchmarks/results_insecure/report.md | 38 +- .../results_secure/crisp_verify_gas.json | 48 +- .../results_secure/integration_summary.json | 16 +- circuits/benchmarks/scripts/run_benchmarks.sh | 19 +- .../verifiers/bfv/BfvDecryptionVerifier.sol | 37 +- .../contracts/verifiers/bfv/BfvPkVerifier.sol | 41 +- .../bfv/honk/DecryptionAggregatorVerifier.sol | 1635 ++++++++++++----- .../bfv/honk/DkgAggregatorVerifier.sol | 1635 ++++++++++++----- .../ignition/modules/bfvDecryptionVerifier.ts | 2 + .../ignition/modules/bfvPkVerifier.ts | 2 + .../scripts/benchmarkGasFromRaw.ts | 16 +- .../deployAndSave/bfvDecryptionVerifier.ts | 2 + .../scripts/deployAndSave/bfvPkVerifier.ts | 2 + packages/enclave-contracts/scripts/utils.ts | 17 + .../test/BfvDecryptionVerifier.spec.ts | 80 +- .../test/BfvPkVerifier.spec.ts | 16 +- .../test/BfvVkBindingIntegration.spec.ts | 28 +- packages/enclave-dashboard/README.md | 4 +- scripts/build-circuits.ts | 14 +- 22 files changed, 2697 insertions(+), 1032 deletions(-) diff --git a/circuits/benchmarks/README.md b/circuits/benchmarks/README.md index f0fff873b..9d3a11009 100644 --- a/circuits/benchmarks/README.md +++ b/circuits/benchmarks/README.md @@ -146,5 +146,10 @@ Hardhat replay. integration + gas replay). Fails fast unless `dist/circuits//` and `circuits/bin` targets are present for that preset (`check_circuit_preset_artifacts.sh`). +`run_benchmarks.sh` preflight uses the same `ensure` + `--skip-if-built`. When preset artifacts are +ready, per-circuit `nargo compile` is skipped automatically (Stage 1 `ensure` skips too). Generated +`Prover.toml` files under `circuits/bin/` are excluded from the preset source hash so benchmarks do +not invalidate the stamp. Use **`--bench-compile`** to force per-circuit compile timings anyway. + `Calldata gas` is computed from benchmark proof/public-input bytes with EVM calldata costs (`0x00 -> 4`, non-zero byte -> 16) and stored in raw benchmark JSON. diff --git a/circuits/benchmarks/results_insecure/crisp_verify_gas.json b/circuits/benchmarks/results_insecure/crisp_verify_gas.json index 44e97a919..783ed1443 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": 3042761, - "user": 2973025, - "dec": 3553819 + "dkg": 3042369, + "user": 2972905, + "dec": 3553763 }, "source": "folded_proof_export_plus_crisp_verify_test", "artifact_sizes_bytes": { @@ -17,17 +17,63 @@ }, "calldata_gas": { "dkg": { - "proof": 170124, - "public_inputs": 6132, - "total": 176256 + "proof": 169908, + "public_inputs": 6144, + "total": 176052 }, "dec": { - "proof": 169980, + "proof": 170064, "public_inputs": 17304, - "total": 187284 + "total": 187368 + } + }, + "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.110746138, "runs": 3, "total_seconds": 0.332238416 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 0.607811889, "runs": 3, "total_seconds": 1.823435668 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 0.557438458, "runs": 1, "total_seconds": 0.557438458 }, + { "name": "GenEsiSss", "avg_seconds": 0.123049167, "runs": 3, "total_seconds": 0.369147501 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 0.222128986, "runs": 3, "total_seconds": 0.66638696 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.377330709, "runs": 1, "total_seconds": 8.377330709 }, + { "name": "ZkDecryptionAggregation", "avg_seconds": 48.488375583, "runs": 1, "total_seconds": 48.488375583 }, + { "name": "ZkDkgAggregation", "avg_seconds": 20.208865916, "runs": 1, "total_seconds": 20.208865916 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 1.438067534, "runs": 6, "total_seconds": 8.628405207 }, + { "name": "ZkNodeDkgFold", "avg_seconds": 61.160005264, "runs": 3, "total_seconds": 183.480015792 }, + { "name": "ZkPkAggregation", "avg_seconds": 2.138399333, "runs": 1, "total_seconds": 2.138399333 }, + { "name": "ZkPkBfv", "avg_seconds": 0.331717652, "runs": 3, "total_seconds": 0.995152958 }, + { "name": "ZkPkGeneration", "avg_seconds": 1.344757416, "runs": 3, "total_seconds": 4.034272249 }, + { "name": "ZkShareComputation", "avg_seconds": 2.692020006, "runs": 6, "total_seconds": 16.15212004 }, + { "name": "ZkShareEncryption", "avg_seconds": 2.493858539, "runs": 24, "total_seconds": 59.852604959 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 6.018945347, "runs": 3, "total_seconds": 18.056836042 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.096458403, "runs": 3, "total_seconds": 0.289375209 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.215833966, "runs": 5, "total_seconds": 1.079169834 } + ], + "operation_timings_total_seconds": 375.529570834, + "timings_seconds": [ + { "label": "Starting trbfv actor test", "seconds": 0e-9 }, + { "label": "Setup completed", "seconds": 3.025607542 }, + { "label": "Committee Setup Completed", "seconds": 20.220793208 }, + { "label": "Committee Finalization Complete", "seconds": 0.006228708 }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 298.983421792 }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 301.505155708 }, + { "label": "Application CT Gen", "seconds": 0.3085015 }, + { "label": "Running FHE Application", "seconds": 0.003573542 }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 78.392914 }, + { "label": "Entire Test", "seconds": 403.470000625 } + ], + "folded_artifacts": { + "dkg_aggregator": { + "proof_hex": "0x000000000000000000000000000000000000000000000005b511f2d4cb51d35b00000000000000000000000000000000000000000000000b01d51e6030070dc10000000000000000000000000000000000000000000000012a64ca983c611e41000000000000000000000000000000000000000000000000000230545bc8406600000000000000000000000000000000000000000000000718d0715b7e2424890000000000000000000000000000000000000000000000051f93a31ae037bace00000000000000000000000000000000000000000000000e429942b63702d4c80000000000000000000000000000000000000000000000000002dc72bfed656900000000000000000000000000000000000000000000000ceacc1c98d5d99c8e000000000000000000000000000000000000000000000007a0a946c6f426f8220000000000000000000000000000000000000000000000027448cb4671b613af00000000000000000000000000000000000000000000000000015f1870dc3cfd00000000000000000000000000000000000000000000000e8d4c84df6b4bf36b00000000000000000000000000000000000000000000000efe4d1cda5243cd82000000000000000000000000000000000000000000000003582d18fd9745444300000000000000000000000000000000000000000000000000020ac7acba545e23dda2dee6ef7f581e41251092f73146707fce50a8c5247496e8658aa4e1a00023227a50845299af8cb19dde4dad6b9716a41dcb53a7e9ec2dbcf6cb7cedfcf429da34f9bbb7408cb5d6516c98a3daaaee32841452477c975abc613170508ae70bdd2d14bb31a87a356072496352c8ef6f71d0e1abf34a783d93dd555ab7371e2a5d417712e7bdf01665074773edc7006269134b7aff1013c061126d156ad6ab1da8bd9bd28a86e5903cb57cf59b274eccfc1fdd671cff57e56d504c1ccddc4d2999a05a404c606f997c9dc2341e1012f20f715948d689996b03b1114fb94f741b735acc897fced5bd7960e5f7bf3234228341c68c15fdb811824ec7bc48b82018cb47e7ec2646dd5e232ce3e21b9e4ef73d7dcbd33e940d9ac693099e269cd6191a7e959d342e67cab16d13c991150e60fe886e47223dd21b625cb56420f5e911fcd90e994dc8cee3955a0d2d333a4142a2d63d6962552fb1e6a69444daf64e2f861d21c3e0925dd1bea1dbfd3f5f98c216ac78b94b9f6dabac3a2544130201091a24f1b083bc6ea77aa2e9756b5d1894367ae8aeabe9801b33b71f14d6a02e008a33d6197de1bd0ab3918e3fbc86135b9a021720f94abdaccbe1a0e4789b1215ae16b624c3b515734b8dbd24e97cb064bcbe942ea750965a8c98b76d5f92081b3a5b040e0bb98d03e2ebf7f32b0902417d756f56dae3f74eb2584e9e5bd4fc18952e418c5cd520708f7da8f54768e0d2aae6d76852e8ee9354cbcfc332d07220d5548dba68ec1b5480c2666964064319fb2051301ad6e747fa15fb126a515726465c337612d9a7b76798003ed46be30d046582f66b42cfadd8d05a7a44232812da04d4f16039a1c02e86f3761b083d29ab3fe21718f58ca6b0323fbc47b3fe1ec1931e6986a664b56660a932ac9b8a028237f3d8d7963cbc6fd1d98cebe39015c6c76563212709bb91ca7e7765a3a7a9429011fa04febabd30a534495debe108cb2764e0c15113a395b25c338978c083b18fc966f67b2094fd409189b09afc08e3528a88b7be02e8b40aa8aeefed463bc662b29bf6e95f9fd40de8a4b89da02160de581ad5e1f81264df4a96884a8ed60ab981425b2908a2cf60a040725aba0eb17bdd527239fd4846955b0d0292ab4f4700ba51d15c0066cbb05c1a69a0930a979675dc43fc73989c464ab036a9748ff1a3da20955bb6d9b714cb940ad3472f7c5630f35227f77dbc93446a397cd3651389ee2b9d428e646a889f0a496eda0c016f396375b87a07b03ffe6beb01f85605f8681710fbc856bcbae1ec4c354a1763afcb40087e23db7a17c75546a8c32ea416379060c17837cf5fa53f9971450260399a528cfcc78261b62d78d600a7c060fb08db45746ed96425a2376aef2a255cab28bb394deb3483f5861bc40aa58479ca0845c51d9cc489eff8aca255232ad763520431e7439e01418e0981d43e320f09fb84e1e0e3eaad93bc4080159e1ce9b204b0a59be0c1d2421692577370b5f99bd88c007a51f93d900425a417c92e353a7989b05a7d64ac21eae67f7dc0a7230a5f84a5ba309c8ca8b5ce4f94990a774d3e23cfd7058ea309c8dc1617177facea557524196c85350256dd03ffad1921b91c6543f9937abd7ead33bde17ce9b6ff64bc9dac063d0661921da683d017b1082138c0f44c5de5c1a7322767b799cba6780047e4b3d1349c09717267d722377bafe27d7da6dc1d3ede5f6226fcb4b24d6d27caf21a9f1242612e894bad0c3c6f2f75a28f3ba1b10d6b356e39bed4bad6e536bb0aaf592f800866f53c412001fb1f8028a4e8b58be1cbf2bfa0f15314d47a8dba45132695543fc59e20ce1a9000b1f0009915aba0aae926c3b3f957d256c1f9992372d8c78ef6dc79e8aa19dff3757640b912a9b97e2312f67709997c1343b3acf3fc9c896a62cc77e915147c2efd0d87793d9978c46e910a27fa85684b23a387b00eec9b5d75a38cb9bc0f9bf144c57718c065612fa1124391050c731b18617f04cc57919abc8a4f354d12e7e6347e30d4fa05f935edf26ad4b8ad2bd812e2d50a8b00eaa564b2092c4d1f0df7775c3a283edfe8a1085f8964ed02a34e6566a150b07c64f4b028430d080ec145a4066324fdd45c2287342e9b5ea0abad00dc4f26b5e6c63531b9a212380111e83ff15c17d0e888192da388182f5df7fa06c90e9964ef59bf041a6a11df01e3964fc3d25ed0783ec0b3e271e9e1e58d32c742806c26b5d408595c3c164c1bc960a65647fd73a38018ce97a04e599a4e74ccdfe34350d2345a02596e60d51d6e64c05d6b9c2323eb48a3bea008c0e6446d51c7756c1c470942679c0fc3752b3717270f4f361754f0f54486e17946ffc46a824a6b35e765e427c61197248c0473a584a714bfb9afb99abbc1946842b214eba989587d1335dadf2976414ebf21ccf9221302f881275f3b59a78950faf2aafccb0b1c692f13e37c7fca716e6d2e8f1d932e82de40a288c0effbc28a0af05c566d5483b64ba30f86e7e082d8a50bfc77324d3083248bc9e16ad6112513f3a9733212c4b4a2ee75b9daca2966112a1e575438427f88b2be7fa02504211a7fc6a187646026cac05d10801ca77e1322d800b36884029861252e842f560f4403cc9095359f7c3aa7f23bc337dc2e82008f0d67c9a2959dc6bd9a8585ff54a243897bb730833541814bcf7459d19c9b282e416560847a4e1fae7f16570970986a428898be1e09ceaf31ed18283cad5a11b33d363a9be8acd6e60177e802fe79d5fdafab11259bfefe43e9c2719a638f22fa93e7b2063e88921ba6ba3b39ca2c5f18d7389e78a39416415a0a4d17b3f10470bb1c86b7a6eff18f84977d83fd107f0c60e744d5b428515ff684772cc51d08191f948b88189ed9761f120042dce87a71ea5bcfa816bccbd1b4f632964fa522345d8ba5ee821b31e1b9174ba260407d046d71ddddf634e7c010dd75f54a792604442af3a73200bba608021ea5d02197e5d9c94b60f679f2575fab8c7c3f8225f05c6ab7873306dd474b2f13313135c3ae1a56fd74aeb42d8bd81577d554a220cd15e6b6e358d18bd814e38523237976600a36a30ae43e1fc7511976bcd0f32719458cce540f8a1be87184a367e0add5c8f771fd9084ea0a0508f960333684009e8b90ff8fe5e8b0299e6cc1d0cdb393cd76b0adc6c03c47429625d2fbce2c0619fb0f83f77f92fa74905bbe6ce46f858a5152f85fb101fa2c2f96a7a4c0031a168fb540feb76d6ea6797c22045fe3e56ea0bf88c1b99d4716c28f0391e8d521abfce0353e370049938475fae0f781ce7ac2d31b5ccb82faf3ae8843b0292914d6569d289f8d2a84b0b6cfa6d06daf2dbf671ad018d30aa313e00e5859c9cd104b2da7ee903febcd96d11eebd93aacad3eca2d8e3fb927ab283b7791e4765922bfe00693c6418d3bc49d7ad54813b9e04f67df3112bdc58493952805d8d27a250669c1641aa1f3c3f0a10c39706993606855644eabaa247fde62b7a71a753b1e4f5edac839c20082b10ce954a8349ca88146794dcd4f4c1362232bb71a437b16e46a49170c7137a44346005869a32242df156613f57697eb5159e7326b05b4170e8fc8c5dee3fb87c4734cf96adb912b3ebc48876ba1dd0606a9833939ed4806d67c9dcec5f1c7f347fe33a3620330449d8b66203f457539a1b4cae734722f0d35ffe47c44fced110cce34539d4b6238e51936bb2a9b9f8d6be174bc83ea2512b564356e585fbf9e229d904c9701dcee5c1ba30c2abc2affd5fd1ddaf78ade24a5066da4047b1f0fea259fc750816676966aa6013f4d3417e94ba5462f0f2e1a58688bf3e04bc4800d875bf95d6a6bf92310e5ba345cd96c98ebb37d71b9b505080fb8642d5bf2c73173017159f20639019d950164a9d10407d966e13f3805032c27385fb20d1a39e77771d4c3b4e0d4038d9903e942dc25d3b24e08cb16cb299b7e08755cdcdcb7d7468a07be3ece4331942503d54a00d64398b96f2b4735117ae27562d7fe0114c66e08c514baef5efebc9120616c2f2b2771bc6a3c3e0420a41a00edf256d8607b65a933aa0978be18bbf9156450f47088ccaaa59ab9100561a5e3e84492c01acb3f684d9f3e36b3a7ad61c9869228572d9903ed2022732f706453a19e147e6d514d89ea703f19033a87c69dd9ab44b5bd9c757a4c7d152e92380cd3a125689f6b0631c73dacb8b198567a0164b61eb47dfd05d82b906924ed57302ba52ce7635e257a1a31bcd545b997a38922fd3eb550d5d82f124e321de1d93998907e6f4e03d6225fa4c3cb2fd37a81390e9ab79140c3e16c46a2af0806faf6a25d9613cfd28636ca205b20703f39722fcbde4aff456b20f22e44c51de8caba24d92f33a39fd81b1259111d8b34a60226f82f873c9e1a354c4dba9606c23e7ca84dcb3a02e8132d3803bbb64643cfde71fb2ec2b33c890e9c855c61167b871de6fd885b256da4ddd93d2637d9ae6c03e8076c48d07b62c979c9239f157736d3fe9ef70a2af995df9084bb5821093a6e42ad4abd0c66e1f68249d3712d50e693c750e7181030cb92de49902ebc55a3b9b9809e0cff98ea886cdfd8112676b06aa5847cef141054c923a61d4ad77f4198b4d543a6084b5e3549bc38dc174ba2ed996781cbd748e82d5ae19d3cbc1eb0ae738a40200e7e258d73b3a638066c2858a2157ee88c177d284e0a989122ce507d6be98c5f5e2ec14622915a3720cbf50324858227583b1a2ee4fcd200917c926f1c0647373c754f57cfcbe5560f4b1cf084342aa77894606665c04ccf8a42a8c85f6eb976771907a948c44c4c06c08957d8bbb64b4d366da2c85339dc18c03d83e9174002ad1468f4be11bc8f01b2ba2af6d32b065c67597e14b10ab4c5fda0b6ce05da63bf29ac5f6db0febf25b8fa7f36e2dba136c37019b02efd1dfa725779bfae34491ec3dd5e45684d78119ce05e2b93c9dce1a7f7b5b680380be3a99fda4f717edd23e6a7def83d53ca008f3313fcc41efd4be5521b2b6fb3f1f24d56ecd43c17df51eb7c6cdef39959036ad2b02ce4d9d91a67478ed9627a5466443d5d3f8db5e30e171ce618d7b70c238c9238fbc9a16962d7a2ad681214e2868538001ced364bacb18815ca7bd81316f7fe0bd98cb9198711c492af8705c3fed5f8f3f0b0283cae8f77d1975ac3f510cfbe890930c9d6490e808f6dfa26738ddecaddde59de7357f127c1b799a30e0b7cb4dc8b8bba8a5df779fa0f4bf7e1241cb8a681f47d1ceeb249f66dc0693e0fd8a02c387668167f6b49f7ac43760bee734a8800eb53415b2596c60f15d41a2c518e216f57d30274aa50661bbcdf4860fa8f6dc4be49e9abd594bd9af44bf2213c74c95eb0ccb65c8b114938880fac919fb29f701733084568ac2d2c5ec1f92d1b6667167c24869314fb97a3e943df84c79677d05a7a43e447ff27c991991a2485abbade003ba92c987f739c82fd517890c4f9f04a721cc19c1f5084dcadf813b60329661db44285f31c4f4a55bf41ee302f156ce87aebfc49b9f40a0c619d0b04fa295e8a5e1c4948db471c2d5209350a1b3d665ffaff15b77bc36e22a9ed0543c4974cd3be9ed33f5043be25fcda72c5148575cb6a34adfcf8efc729b01925cba94e679c5a6b422b9fe6677e8efefaf763ab0854ea9c1543293211f88e4c3014445cab4f15f7ad77715d46d91c9f6344f00ea838d5254342c7287bb2f2b60a6ed3584f382f01b230cba9b3508742adb5ee207f949de3c54d1eb89e62880c0eb22555ff9a620d43adb90a9af86ba56643806fee7f0148128dbe856a05accb2bc8374e962c5a25d0488aea23ae223af57afd24a11c278b329cf89b665127a71bd24b83f67cee6408fc5416e51338c9389a8209559ae1bb32589b8c19a83dbe1d58e6cf00a2ff11222318c0bb5b7ac08a00bcdd0ee8fc95ec1619b1f67190051a9d0b0371f7d5b2ce78a41e994e8e7fb57b4f13ed5f6f3214faf377bd628fb608f519d97d9ff0a07631af04dcf036ab6f3df5bebafc2a91e37c790ec0136c420a4c08727cce57999c7c185097ae2da1cf333018afedc3145419c828592b198500cafa9b7b948eda0218c87138283d1768569267bc2e46ef038042753731fa142a66a5e7950174fab0e0d39b61beb0ca7553a860d97efb5f4c80ab1a1fd80f4d0ee792f858c032d48246f85bab8df45dad81db6a902131ba4a216e81de09c8ca06d595cad5d7af6214062c2b0245edaeddd03b4b6400b4ed21aa7190b9e0218e00346b980443f699eda84306a85e2274321a2f7da8c02e44a629fa99ee128e8715ef77e8e5ca3bf6185f06674ec4dd9b9f620d8c17c9243fb124fa6546daac531c378bf077fcfda7156ac951780311883f7c75421a1347a0fe04bb8519a9b6af18dc4b0691b44d8a341fc08337d47fedbdaac9f99e37b384739ef1bd8c5e8a5c1f1e4c436d0e81619ce96b58b2039a19f7e8bc4b307c73e5efd12c4b16e19d920147b152a6fb4f5295979a4ddabc8948c5ea29c027c054abb3c6f58a4cf1f5021fa3f3dab4d50566bf244f33e70501c157fb44e7dc75b50328961ed42246c2342f650c566eb35299b8b65d6ef6f10e5aa40ddb3ede8dcfed9adce70f0c32c8601bbcd1e17fe1719e91cc0733fb1e150084b76f60e230eb1017268b67edc5b8740977402fec4892087a38c21e56f49ba12254a531d91d7e39b68a8a26b417fa532e4b08295ec9da45ad16d6f9d8fbaa1e985d995151ad37c01d64b112afba8b6a1108e39e5729ef6a68e0d3606b06a20983a94961c055f624d9e975f20eeff1a212ac7931d1b35e93d77e0423d1eb4d657553d15a5a9b4d85bfe2740f41f2adb61a7653dc70eeebaf0c3b5cc634e72380e7b5b83fafa61c579c053a94eb587afd08b119d4f8175ae8acebad458513d1ff0ae8a12ba51c8ea127f49066302c008218c42b7d9709261b225dbdf05ced37039f6b9ca4d7b51cfb8533ae6aeedb366425797644eee4765c70776ca6e4ec62e7c1829e1ba1d841f3b9dbd3fd71c513a21d965228376642c00a83f036492a7ba0b448e23e54101245251f1f37a0e4748d277721402a320883c90dce40ac487925724f31eca540af37b408d6eac7d6f9291bb390629c533ddddc5d6fb287b9ab77338da698fb1c07b69455aed4832283ba1590c5f9cacbc8cebb1ef3488e3f87bb794a5140bcb84c0572319864ca36ff9d10dd35e6a2d44d1c3fc5b17991499f3851e2192c6d494adff295262d6f6a91a712b49db17116a195641ff55c654d383b41a1b951eef2ef642ea6790d4db7e8372b19955f9a1e9e1bdea242d0defb2b58a0740766b3c359ac0474dcd7ed46ffe003f613b1e00dc756983823bc5f2194f8300cbfb43e203964246e7d6b179fe11a226ad9795f1255f128ec5f1c6194342c97e7a14a2878f7c3eb8c3808729ea91415a99ea2328517627a3f5d4576fa479f70193dde92ff0d5140c45e4672675ff00417e0bd36e49af6f46694ee1483c20233c8ff6d529e7ca5bb907dae9a38e01d0f4b1ca82e447c2dfc3a6ecce73d8f3358f4d49e2491554d442d2f91e4c080f80253ae5eb7afaab8cb4f2bb20b29d21b53895d3fba618c7a955d49d5c88c8d0b01bb37cb808b869f8a2a4419fcd00425dece280928d90e21f2335f77c4c5cda922f7bdf7189caee11c6b04cb41586f63e7ca62104d42f1cf28a2a0d268f469aa12d7e233de2a6aa37affa24755210902a1bfde7b73102e5b3504182508a76c952136f053db95011f8f4e68f74b72bf1f55b12298c19c1e13d34c7fd3e2f6294005e8f0def5f236dd182fdd4f783d363da0fee426cd740ae4d5e75dadcb677c4c250d4ead45c1b308c145755d9d1f1f8d676955a914d872e50713b704a909cd5f1aa80efc9d44a63fe457b038b246222c90aa4ebfa74c1bafa1de5e93c8bf7080038f4e83f8ae4b853dd883b003b99f4613d8020d4d0d97d26d9ff88aaa0ec7cb2f6d7ba11ada7efbb96c7a5d5e32e332b39f35e386561ba4bf8833570dcceff5196fcf07460c3d089d1c6b83b169a564e9222b884c2e347cd658f7af80aefcfb23f9935f647f0ebe1a005cb6fb4ed98a4c8ba448df49604657592264f63c88a602c3cabc2e36bd69cba85fbacc44717922c01e60fdee774fcf21f6d44090441b1cf310323e989608b2d41b281f91020c0ee2594e2e8204f8b03689b06141a9631ddb76e74d376d4bee03656781f3ad28fe0e30e313924c58c494b299c18d49d50b37de3958dbd08fe02aef69338013f54f43fdeea844f438f0a9f955009d865603740933e16cf18ff2fd871e759d6b19ae87ec69ba6b24f86695fa117ea9f5b71881702f3a94e8519c2eda94e52e5db84111e2f0346d0e5f0eb5904812b23c180805df1cd654fbb91d97676825597962dcef709a58097a0c7d3249a20639d1ac21297c97d017bf0a80f2095b47861facf1fac5c516ab5c1a3d3f9020acd9a205220e55dabceeef5c23bd9bfcd19d560282adbb33e091d28b78717ec524b0e193139f358ed59ae7c417d9e6621b3eb2d5c1d386c15166e010415a40eabf67530c00c54f29722675129b1bfba7b46bd72b74707998aad9090270f756f9dde966e60ea9af5059e10bd3458aeb2a805bb7a5799af9a59a2fd6275cd026f965c94c751f013f2a26d71f28160f66ab5c8dcf24ebb397c8c20c88edb467384b6621b120135624b8ed813f343e7f2f3c2e4bea6d59d50146df74c8fbfd2c53473f6f7aec035eddad7076ad50810e6817af77512712b73d6aa0f42a21de536f03f852065112492138dade430d05cda9498589126c98ff1635f3124a4f6d4610583d2bc62f0f061ad56438c47d727632a3c31cbb07e0f9fdd5747efc2fcb5403100388e90e01cd226f1ff7787c472a82df114eb0fe19bf0779ee6c179e69d88b1fa5daa50011e8b4c7ebd77699c7ec27afbbaee6c213aeed3d032fd920f9efa3605b5e5f4806e78d9470fc0aa3669d7cf6887eef9d415ba89e24630c1167cbd25b3a6e1b160835fa6a19a398a47be8f0c2db3759214c9b3f0f8d7b53a15499d5cf06b24169209313005e62cddfe7699c9935f7f70cad1e487ea6cb1bbb0a3f48e6bbd830a420974dbbe0537226413775525c3e0b15461078c7ac68b0982d2f403276d5a4e029e1028b2bea9586f8bbeacb415f778f85dbafcab84be281a54bb56799fde44d1c0685b64c9a5cc416e5196a5760a9ed66f3cc3d1d95185711d7b5dda724668110d60f9f80df45138f8827406796ca4ec6af94f0f391cf7cd234911a066cc625270b16e5a279cf868a6e1a501d98b8401670d3d39377feae26bd06ec15bfd3a323c1995119bb3c7bfa4442b14621f2c0e75965a1583a6e4ccc4661742de62f540020b67bf7e984b63cc93b88147980f416a18ab2ec5d4c8b3226d5497491f29213af31c2a90516ad67a90ca768bf6201339d3bfc5da195c7fc8703d5680442601cf592b80fa6f7b41960ee5854310b612268bc3f3512ccb731b8ddda872fb2840cb8be2d2bebd332721ff68d60a482a44b7bdade02ac3abb9f67ec77d3eb9f701d1e5819fcb02bfc03ed01b2196427f3aac21415ec8d0af17e447da47da6c30d25401845f7397a6489f84aa2bc3c7bdb98845b170fee4c85b026818f8a08ee5c07e9e9cc2977d7c9f7a420acbbc0b6ef0869e33ad0a66554188fe53548f9f880112de90517881d602b24c7fb296657282b5124d6fce2ec513521f71a3f3c218a135714149f5bbf244660b0362d3bd4833bb612bf5c295cf7ba5223d6ad91e0ea1315fd8917761d17ea51e0e07561ab3080fe3a709202b043db065dbdab5db74d0178b959fc331ffad08f8f42857c99327d10aa4213c3528bafab073285dde9f60422c8a6d594e4b8dfae338dc1a0775feeba8551765f900281dec72be4ee70f110f91709a95396a9a754e6ddb20f05eed090141db3e0a08550fdb0fc9d17d9e82a5d8dcecefbfb324ddb485586ee9e3c575578cad6c5a6a5afe204aee69e8e392d536de19e1921d846042488cf32541cdb1a9cf66ecde622b7264e6240a2133b1fc3c6d31a24cd748aba28c32985497e207336c5dfcb93797a15b5b77383fee9154e2a2bd123e8531490d08b040b982571d7c0538d9e1fbf0f5a2bb22924cff71dab362c07d5f539006df328ddf7df204ed2ace5ee9b7447d7ca3332e1c44dc900dddd2b962bcfb67e6ee9909eb31694631b2e16693ca06fc67be0a46c99a031017ef78c941eb2584c958922d9f6b8700e898be0ae8ba7633457239bc15dedd515f3bea21da491e9b05ccbeceb32a1dba49b7dc62eb046956a7a51e3066fa96c1c632801629bcd08d7ceb01285707c53a6ffef2148d44ee4b979aee1c1456cd6019d100106e81616971cadec5c14cf930a19560b342d8528adc048319d2a70f81f79b82fd20a15893eb7020e9dbf412e9e9ba76808ab84af12ca41d78aa59ad8236260c20af26a6140d727619d09b1645eec71f00b177ddf1bc1aa310a07f43e16c0b9e1bd805097a25c36b59b632d3e930801710d57b1c18c63dfd4378f4b250bb5a95c5abf31a50c3a9e8f054ecf212c38b0f84e4ce223b6934797379051571d92450c3746c4c095824005ce5433c44f0715e07038577efe7ed1fd364a246a1f46d34bfd9823111b8faa9bd332677a3491783ad7e72546e7378194d8daa4840fe8828f6a87236f3b596aff2bb4f0a65ff0f295c1a36e8d493f136dbb2581451c5de2721721070a775218f64c21c4c00a767e881492bf8c698311fed46a1e621370448e25d5224c77044bcb743e9101349b7b806316cb2eedb044f86d8de22d0d2ef4a2219ad5268c082d0de7c4fb5f83a0095377e16749b3f0662423ea2160015a08c5596a671ba38be2b62a1dc90360a720d5ec0733f8025e9cfb54009d94082a4a97671aaf5fdd2d0e35aee9ac1ddd5f43da044161d533f05c039ee353c629b98e0d271da96ba68137137fe4aa8ccb6b6108585f06cc6daf9e3f30f8bb8f1b73b4806929edb63a13623fd72369a265cf083a038537707a37e27962a63cc002aa0272c3884104a88e2414fe7f046bf61b07732ab262e2d393053e61e1f1581d2decee715631b375943a6b3bfa6e09d4646fddd168babbb2ca101bd42547350c8861e2d73d1f3955f958e06918f4b2636c09f14780155bdf8014290027d3bb2115604b18a214198ad896d8a525b5e879bf4251bc0968f25a159d4f20c8bfb01f7dd0188eeda951eb132b975f3e92d095e9fc66a1c7f4b2ebaef503974093c9126e1d9fccc4c3e76e84e954b4543b41c14863751364afb2ac255082e5ab388808f41a12246a01ee8d542cbf53ff777ef7d4d0a5e098d0f7159af7b688b2c560209a8c7ea44832a71d00f13df0e7e176bfdce9293750c0eff641517c74e100690727bc0c681e68e7614aabae2afc0fa1328147e3d27539723602e902633965f02029d32be1d25ddc06514ebf7032bfa5ab1e1b986d4c54cf2d5b30d1fc2bf0b629b7f19b8dcb76dae398734ded0a07e91412ae5ba9fea22b8335dc342e91ed801d324d33fc342e9413788c0fb2341fc665853493b215507424bb93eaaca8845e1607c8b0a261dda0241acc28ebf36c53b08d360d8ede5ee3d8642380da62cd96038603eb104c12cce0c26f76c80cef2cbf5d17e5ae9d05f54496242d2658996715d096fde5505b978b1f91e74cc30b4a6b2fbbb9504e2d871ca80511c3b6cb8b2822fb39421a11271e0644243c849337a212b19c296ff47a10a64dcc44d2705b191675c4e63fbe69390fd1bf8f429534977b31a7ef1827ce75f8146327e7741027aa65b7562dee8dde437d21f25237ffe9df93922b89e58d30809755dcc234e011fbbc796f48b8c374c4ed34bbd07350b1ccac9436f1d7383c99aae9881ed3041da3363ba0f47c068fead0e243f197952be3b2efa951dbd79985b52c6f6ceab7252c0e89c3dc82a8524316c37d4c4ffa34ffc101881b5c7cb7ef90dda805eabf13979f6f1f98d6e55e6debdab0ad2769e4e5c81dc65ac0715633b001949853e0130594288fb65b080dfe4d761a229c4db5ce9c6a9ae3fedd81169810bc20dcc4173d2a6c5d3f106780687bfc1dc562c0d7fb4877257797e2786b57ddda98422d0e83582b950ddc09b1af871960eaa9330eedd31c39dfbee458e5496b4ac40c24031dc22d65baaec1f8a523e0ee311d250de228a5a74d945d18bac528da164af30f72eea190a8ba1fa62f4942a16e9fbcc85296672a0057c08536a919d9f9e491209f84c4f0de4b1fe2c1d00ab3b1261507ae62ed9579e94e803a4c495bdc4f9e02b1e81fb55959abfeafdc3640be8025ad4c2efc88782b57726abc340422c3692cc8ed9c24674e2d7ec7219f416d35876d81debbc7d4c39799bb45b394526b6903cbaed9bedf4dc984d48d8900407f0e82fb42dbb88ed87107e3e6b6a5fe3e201db216ad5d483de1b2ecf04ada79df50152c14e274256b08a3fa9ae5836be71f2c7cfb5a13f50b71257cb7aa1ec9efd6237073b2c9c948e5315f46b9e695f58e05b4fec5af27f4017501da0b06571c35d81b3bacb22796580f08bd5d869f8dbd1a6cb57dbfc61d3a8b84fc87a0619a8d06d29a35c3e0099f077a55f922de69ec148cf200c548a4c60da3aba433ac2094964c7ecebb45b155393ba204f11d434528ac82a226f7febc39856a89f08e404c97072b5803f4576aea40d0d2857c4aa127099997a323f86ee40a263b4e6bd0b7c27c29ed2863358d8b2730cee91e9abf2a42cc1ddad268a356cb9e4cc46fb18fd1a38e8448ad7f36aecbc369e938717f22f90a91bcc51c9554f2f62b8f30550d166ef0855884d7e45407840bd57d23ea1d08965b833bdd7586785022937c823d6f6128af467a4704e615d4100416a52b0c140562fbed18ee33f11959ff21f7b7f42a2506deaf28bc72a0fb8aca18ccd22abfce5656f98f63cf0bb77451ed003b41738684e0bf79f020081d185d3de567233ae2cdcf81ea49c4d82460902c843ec281d159a5d34d3e063142a42d4d1f641dc709b092701642d4219cbcfd4def1454e9cfd8b08066235344152a3b0c1bc7062763cdf1556f6e96085f4ed0400e7cae920484d484ed13c1a890ef506e273629818ee4442a9613c69acb71827d6a6b7a0426ed927470089fda98a0ced565fa1f24f179ed5a2163de9b0361fceb03f0ea6fb7e08739d03cc0dac20c15ac1b2413a0e7046c36c8b1e621c72ede3f53c0f03d30b4e928d478371c7b64056d35051b3c28d5d735a8d7d291d803d04da8d1b9a4fef58c16dfbe0ed8d18e0525f39623822ff691f2d59cdce0ff21ec236f944b4fb7ef142877b1e03cb791b168e0fa28a4228ac97686b236947d4a6985016b55702fa7a8bbc22fbde4dd3a16c256201fd789ef6081854810e4e362f8d228666aaf5e19f850a4b19b3e1c7564d098610afba3a2acdc7aebce19c442018a87e7d76082bd289cc61bbb07ec748fea71c30dad31b0b6179b11909596d9ee3a6e7fbb3e2a0c661442190f4e9ceb58ed172d11725301b90452a2e60b0bdb8caed292684c20371758744a8cfc5b628f83d2830c677cf1255a9e2a08b44a5ec378f8e8fdc92c89e04904f5ccd5584e329f871b302d0f8904bd3bd176e6b410b2e32678005cbcd19df64cfd2892a90347485b821602b6f38914c9cf6d49f1950334c92d1f8094f481af8bfea7952cc4cc60dcf102d996778795344e7d940d4ac64a1947b00f2a7dd321eef14244a9999bba047e2e6eea09c9b3117be49017bc6bd03ea26432008367ffac272ef3b0f70b3d17ee25cac94131ac686c5979362d7ab257fcf022011c4da60261d9d2448cd4e52dce033f35bb4c9d98bc8b8a1ec606aca16cfc56cd7d194655442f67ff86d55c020d073a72a8ff94a9e27d67b22c77afc4b89c5eebf998c8d3c0123360cc2fa2405a0c463bb359ea776ab0ed3645a01952d1d129e5b5f23f2eb1553e5e2162bd8ccc2eaf1f7eabe2e90bec8ea0f4086b77cf8240896e58a8a2e33ce33bbcca54299608be84c441cc5ef5ef3d223e1ec9fed515b84f4f2354b34ab348580015240acb15950d0f5476d2d11723cd5e0b21b42838bbbf51238691e6eff0bb95532560d31066ffa9bf93b8776080c55679dea58167c60210f035bc8c074cf8f8bf6a0913194a0019d91151b66cc93f653cc759188ad21c30046d2f8ba2d65ff5924278500263e52d3f2aa1f4ea915960b8f954816d1f28e2e1d9f3cb968d0eb026b4de5e18237990c874a36db06d203b0fe43902d555963195b1bfbcab1a28c93e5e417017eb743a0fb399496fa5c4e09f53b13fd1537615cd39c984560a250e331fb3b226f129b9e725fea56f33dcb62e7434bbd653561fc014a12ea587d67b621045d803472f4155480bcfd19c079091760eb48c3870b30a6388e8a6e5c81c3e9579f405055a9dc88cad5ebd571d0715bfda20adc0f55177454ec6e2180229c9efbc301ab8c2751ad344c30e14d3ba8b1e0d2a7919e4e8d8c6a144ec1d6b3b9f2a9f960021df6baa571d82bf19ac456b8fc62c2a227c8066be3ae7009970c87ee45def09d68e692a0c99287016476b3525518ad3d6d99afe2d40cbf80bb519d1f0c05326635d85c428640c9a1086e9ef29baf268cd2d9217f05c080fc08da6774780e32dcef1e5bfade2aaa6e97f83102407fe1df4309838fa7624d1247cca1e0f0749", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e940000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000095d6f5f2e471af092cf95770b13ef41f0000000000000000000000000000000087f6a82b36692845130a33d755087d5511521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + }, + "decryption_aggregator": { + "proof_hex": "0x000000000000000000000000000000000000000000000008344ffb6611f1eb5e00000000000000000000000000000000000000000000000e570a2487f8f1a2a400000000000000000000000000000000000000000000000a27800d90764dd8dd000000000000000000000000000000000000000000000000000097f1b50115b900000000000000000000000000000000000000000000000f562996237342943a00000000000000000000000000000000000000000000000f7b9376269e52bbe8000000000000000000000000000000000000000000000004c42934421d35f5d6000000000000000000000000000000000000000000000000000196b8049810da0000000000000000000000000000000000000000000000098754eb7764d0762a00000000000000000000000000000000000000000000000541a0197b3510be91000000000000000000000000000000000000000000000008e69d9147d452430100000000000000000000000000000000000000000000000000009d34a13a1ff200000000000000000000000000000000000000000000000b0d9f32235a92d9e800000000000000000000000000000000000000000000000f522191f8231cc6f700000000000000000000000000000000000000000000000c2a8dbc565a2d36d60000000000000000000000000000000000000000000000000000b173522955a01a536284afd1c497001ca9e84b3790201076ed13ebeb69fab013daa22043571e0748540dc8ca4b45711be77a38465dd79c0c40142982b46c68690fe38bee04120c59b2cfd1e34b800de0437cecc91fbfd76e08022456fd80be7973303b08c04414d6caebbf1338f2b9981477087b3033b0889c41c37b520cd7da9fdf60a1c0fb2df16cce8b3a7b413e6d4cf17c6813228b29a1968974c89b0f42faef09ec3a2a2af25ab164ac6ddc648d62beb31938fc918d2c1637c1130a3cd7c3fabdccbd3e2b1141b2aecf3810667de2c24cb5e1a1e3978d2554794af1e1f8f9dce098033010479bd81966836fa46e462c2ba54282f3a9ae5d7a5cb7fb2a404879625a5a63038ff133b794ea77a75f9c7b2c4d9a50a8fddefb89ff55c42d1859460003ad700510548a94446b4256b375c4d34b5865790d6decbb09de3e81d55f278073cd8b093e5dc602bd084065aa147f5bb7af355aff0da078be5f648d9fad84ab9606ba13f1217eed5cd6e53724d25036c76f24e8d44c411259472a9926540ca3a0b1f81edca2bd607c92149588ce53c402a5655f510e0ec1e8305e891fa83f900463d02b5913aca409bfac042b86f86adad8394d87c85719fdd6d4d9bfcda638cfdd5b2a75c23e9ffd3b044270ea864aecd2f5c6023701d4037ac90bd5bc57efa5f4ff230e24473bd1159cb796bdb9edd32f4f748e9e03abe37349fda4ea55f329434e075773fce8018ebc0cb466a7dad3701dd9e45822603029cfe24e0de39924a519138b0a3b78380cddf1e12904a8f8020fbddd9faf81dd5e3c610f4edb0e0025c60694b43ad0a9b56472cc6a7d2e64fc9ff29f578f6329a0bd90b8cfae4fe026ff002dc050988ffc265af9f14c5e455f9b3525321f595b0eeff881043ef4c80ba72069c68512c264f5b8362bf5b30ada4581359f11ad6228f0a3989deef85610b908a7ca6486c81953c35e2dad7815dfbfdc28be0539a577512696f20aa8ed22a9030b268013b6264f063a8d0c25805e76902d97338c43d578629f85ab81db61c1046d4477ef12c410e75926d1ada750bd1c65685e78ca7d0b66d13a3096311b052355dc5a797551b2e06cfea9d6e4fbc8aea55bafd322ab11cbb63bac757f9d990c3f2603424339c7a127c0f9217a5fbd5549141480c4ec5015962bb24036df4429c642d8af58d59abb7ff10ab858c9f12628f922048a1f6b0a61db0b0961da471e651a823c1a982d04d90b43c36fe93d066c0ac0407b6ff80384f4d9a71ecb6603fe50e0f0edc74dd65610f90bb05d30f4075d1c61bc0f0bc4d69115dce1254f19e1175ea92f245f63081c270a617f304c42c307601c9ca0156b9781ead752fc20352762796c8d8da599cf7c58aa10901216c670c7f28c9d1367ff753b0198760c3bd7200ed3c38ef28267501b774e5b0148e6b015f674ceec89559a75ac352106f4f2946aad78fb2dd235f8e2e89aff7932c22f59377baf2cc095b9077c7c6f0699b6ab350ac85570deb6b7e692ddcef5beeaf65a953335dd5fc7353baf823406f0eba8837a794c2e8efbb264d44031dd03fdac9916d2a6b62ec5ddc9c7d72f10b3e3e877cb56cb35596be8d54418e595a03a95800575733eca9a780b312bcd00349c708d1bc49b959b2ac8d3629c6ed90afe49d4d35ebe44c3acf0b9415c1016c3c140753bdf5e8c23a3ec0a7cf7dbc0194bd3568c3bf2ad01b65b82ee3ed42ce7541788ca715a28882f5e5483594809335434a56aa895e3ce9a1c76e529100b688b27a4a18b855af75944ea1e0ba29b3fecf075559d253389beb69cf903a60ac7bb71d5e172fb3852b2795a9ebf55ae61f82dab8cc1146898745396662a9c145d346b6c0a64b3e5c59a65eb67f05dd9ffddc4d06d32c77623ef255913c88522793056e1269741442527ff2665a38590153256d7adc313231adef3179e0bb90f3ae9bab00a761ab7d1accac79a6613a7509097b65be4a69b5a598862dbffdb23ccfd98a8087f5de2ba70059c4b522c74621ae0efd084dc9d1772a216c2dded0d192fc884d5f68777da48efa059cda5402142c84eda81f28d23a7ae17f074512458e88a1614c18b768d34ca16ae992c7dc05dad2956b5ca49ac601cd22e1431198cc4d4e3c2779a62d3516df35ca88b9f3079cea6532549e49203c21d6bbd421a84fba365ea09089dbba629b32b842847e6e10a0b785d4cce81c88bd235894d07821840ebb6eaa367bf8bb4d0928c6c7cd5ae95cb9cbf3cd3b06f1e5fde4d583041bd33a6f36255fc376fdb7b971d7796c15cbc7cd9e42157ad577dacce5aa21e8ce50133e64873448e238afe8a7171dfa0fa38ca9bdb12f11d7e8a91e91a8811187a0283bcfbf9c2bbb20794b50a937a2b72be98ade151aa94e66da62c209f2077abd55e76555cbec1d40637986e058affa82d02fd2998138db502bfce7c7709994c7fa99136015df8c2fbdfcf28cd1a3c4adf4bb8d8a733c35f7c263f2a3714a3d722f984dc0e35e6007c536f4a7b6dcaea273088991dbe2d3bcf4b745527085d14d1e3c29716ef011b5319975ea2fa78c40f92c236f809c08810070af12e2bbd76535a28e63187014f2ccbb5d3273718615d72a674f06c6a397365b71bd614511e01a0ec3b301c7c9b53a523d5813b34e57f1aa97ed05ec2405d839aa4930860ee93c57afa223b1eed64e27d9a5a743cb04aa25a9a8ec1ac30225b495f3619c2c5f4a425e9a01c52cd10487157371c40948bbdfd541195e0eeaf2a556efb1227db38d606878785fb91a3bfbdb5e03250a123d225759b457ddc71f5f19b6b226f75a27d6b6fde1147d7aa64aa20d1b21cc0380be789d620b82da77f6f6b0707501e2b1a5c72021ae5a1f0bd6a05755e8566575a469cb72a780a295a171d971b6e5489e6fc90d60f62b63b2e983568ad664a8641259a23aacb1aa57fa984061d23ef759797bafb41bc1055e2db586c003039c02a998387dc66d7b3a05a3e0f224bf9ff7ae5b4812cd010a814f46d7415caa6b1b2cbbe61e1cf90d341ecba9b17d8f76207c0ef02ec089bb8fff9d01875dd398237b3543ab1ec9711001629e02eea3c546a8f52329f60ea08d84771bcdd55b6f60c1a251a5901aceab0b8961920a36a51886ac1d471fea83aef6d32a8b1407f8322fd72be269f7b2e929d4fcd1a99a3eb192040523ae8cbaf0f8834d9a6b87f757543b4e86a5d36cea672dd5e1622eb88e208c7bff87fc4b56d3d411aeff9c0b9b3b12e517650a7f0500e1e082562181e6ce625c8b9d08eea580ab27bbbd480f80782430622d06473467c6c391099c3b5e3738348e3e9147bf5b3a04519a214854c2273523df7062d92150dd5122242c00fbb9590e63a0e246d3ec45e898e2d52d7336f612151c582fb5ff7bc2b196067cb2209dc592ab4992c39cddf94855375303992205d834ee582791ee10a91b549552dcb5ab5ac82b60de1fbfdd95d13bf7bb7bd41ecd585982ad8f6e523f8b45d340efe99cb374fe96ace51fcb62e0317142fc98283462c5647b58c3e053afc0de3aa26946f838ba86338b0eb0c51d26251fa5f01bbdf810bbad4d9b921383859fcb763b8d6cef9600ece08141efaef3e0c3a4dcd0ab6b5b3dbdfc13b25c547380f629cd0d313f1eb2eee44315ed10d82431934c3ce9e4f86c7e4a6a01fa66cbd0083bbe5e521dd689f3261fb078b75fe60d1daea8fd6db932025ab48243e6af5462e0bf3652c68488c964065ded7aed392475eecedd44cd48132b5e41d7bee005156ce8233e8d493a4f0ed4de4465f3e11e8b854348058f248bbda372488a16e53bb7af4306f70c285309ee014e317e563607fbb4bf987e818b5a20716d140988b761cfc9276e26b97059d7f8ed096839fadfb1c5587d92fe3c1c6fb0cbe97868b23a1404cf147cbfeb938cce0449dbd76f2ef7d4e5336761d4aed491a79fb22f9efd7aae3074c6ef7f625655c1b2ab60ad4a35ff5c58f71e27b95471e391541637830a5bbccf452038838bec96cbe2999f4f9e608e7fa520d964a1c18201a8a8e254c9a8934bc63901f8738e8720282d112f42cbcc557cd530bf2fe074de8f516ffcc8278ff2b9fb5a8c326043f598829840bd55698a583941cd59e281c8e1a805d9bbb014d7cff9e279b9a7094e164d9583f03d402a2cb47f4ba5b1b25fbcc32450457c5f7de5a74056c0da37adace3fc1b28f64be568121746ca61d2296dd20643236f6a62c92ff19c3de7ead56cd0b0ebd9cae96a42f954529862d445d30254f2941932b53e1a4fd146b7a27088a6c961f4c3ce1abb0397d66b32dbc87dc2093f06bccec79219a4da5fce8e323e8bba0e1bb945cdc78931fa6df0a9a915f1c89ac85161e9b8a4afa0fde2c4cb02e4ae55be15c47bc96e94fb9540097a37bbf80174f2d4fab66343f036087813e88eafdeb86c21aa261d00469352c16b820ede824900eefcce4e84e6d1deb609c7bf6b98541b529437731f14664039de3aac50f7f2e65db3610ed1bb39c4904698aa7ec32bde5a26c1eea19ca5f11493f3f468848e2c01bfd2ec698a69c4fb77b78736f5ed0c047ae7a34a113b40c5f32d7ca18ae45669f522c772aabcc19182183ea265deaf2d226c6e46f59d11b1337b4a470c411491b2ca2b18585f4858bf3c625ad2f26aeb6d80f67a0c5740423316dcfede7851e3b231a0e5a9c3617fbbc1f1a90fdef4de8c140ef6a05a90ea9dcc1a23fd2153d6a505969a23b4802aa998b8ccdd41d9825027f445802062e7fb60f115469d7b12b7e20f14c93386f67bba2750b5fb2ec397d23295b193d11c50765ff7a651fe0a2cf6845034130c847c922336d93fe17fa9e6220225c6a0ac1215dc6a671343587b1fe0228e0b660620c5991660809dbada660dd42bc3701960451c2bb3524076f82cae3e24292fab1da0309711c9abda0091ee237e9a41bcfb643d0e062b6e434313069c259d9505a8def12e844d6d496cd50541db9c21d535bab557ab6e09991afae0bc07a6b446317c77ed5ea97492bf01379f8a604250c91c3a43c17a422556d3c894b1d851fc9d5e5530bd1ad664bd2b8ab42403518c7848e39e1d31fd4c014022ce7739f5e864ec9e7f19a4ec4c3f6b0c4bc4d3d1bab608182f2de0d8ea2756ac1b1cbbfe1a4cd1ce2aafe2df90f55398803482b301d29a1d44127e478d390c7a78ceeb5783f7428f7ed38de58dc205a7ee2700803b7953de0f2067776eed0b6fee49816c56667403ed986586d2ff00bbdf128bc07057b39eeba2ca6c907e8f0f42cddf17172241f7763e7146be50bfbe0851842013d92c71b984ea76a54a42d4a2290195c601e998a4b08ea0aeee5ac63b10fd7075bd7e4b875d5a10d431c2dee6adf5a1c788200f0315fd36b6ee2e9a9d1ad2e2aeb83d4772d102af794d2dfc5f80deda52a1535412578d52391f1a17d36de700ecc166df5ffac841192e32ee3f2ddf3b9edc604c724f6de84f31bfa3233806b1ff44a9a4b482278a52222791b0d75b2915589af6dd95a1f994aa8ae3dbe07bf209a841a527308b4d444dce47e9ab9721cf3be96744123742e83f23f5a1edb2622a74b67667e14c32e4379272b7d2c4fa4d8d69d78a6c92d7218809203017c04018962cd8e399c9f6f40fc1a937be3c790cdb959833e921250181349bd108ee71156fe6945aaa58a08cd9991c18ab0d72533e105b70b30c273d74efe03698f551835dee63e09cd5f61fdc312cea44e2ae6c4157dd2d9884d347aa0f0c326e9632fb9245a2c7615a8c515fd6d3f492b41cde9364e42301b55853b90a98dca01b70ff99b6de8d6e4c142aac1a99e64b03d4cbbe79d8881d5f59e4cd61dc5846fe614a2e8985c6cfe883c924eec19952a1af20a1238da8d0a50379c737b365afe2605f23f30d50d912faa8c5c2c761d508810d776d3b9f708d153ef45cd24ed796f197b6f0f1a84037641d56eb9da1a8fb8bc0155389c3553b430a67ab09a211e6e2494a881eaa9434c9b8c87a4bf30f794af8924084a75aca7cec0789190909b64132d79e8972fe529c9f6e9e0fa0df6fc778414f6395a7d2af1f7a99a4a4d390f26da348e1eb4776746a9206306cb432a2d923a591316e700723ff26789f70e802a94291d52182ef8cb4316534eeda33ff85836861168f2b2e18f420075e2685408285e580b0eee48ae7f04dc48ef7540833c37b1c95c1511a6fb93be2461cc8b2afef75c415e32ff514eb00fdc49f702d91bcbd0e417d4bd1be6fdf7da0c10a50bee1a7cb4e79b3cc0f3f367b388ca6e868993fd0c3e8b327ce9ed2e706021b11a8d304c43f65254407478bcfb55ab6505aa2cd3fb967d4aacc774eede98388d102b2ae2dc6a933c43a8c15fad938a36e9947c35a7327ef18c2502fbc105cb4b12681122979a076406d7af0d109fcf1bae96c8646748158ed165bf44cd79f83702a4c0d6d5a754e392cf5dc6d054d7bfd9531001245effc79e7b0365c82e9ae0171d55d900c578569dec328286eb19cb634e3a7a73145f0ce4984e1f2b7b55112c45245d57f2211180e4316742393a2f66371097d9bf61f9be4a404b61f327f12ad39c9b868491deb74ab9cef0bb69730c8ef02f2d099c088280c7f67430df861acccd914b41a77abaeccab933372b3151caff7b9303b1842fab33767bee53911747535547a56dfeee3029441679bb7f459ac5a802d65699b5c361db2410a1fe0f618fb9298afc12bab2586498b1ad5ef91382de412e01f5eae4ebd7f3cacea1273851fea8c663c223da421631586471aba671449591a74d6cfe0469dbc288aa0c505f507aa7fffb10fc3f694b241cf86f0ac4f5041700301b8908201dbbe39526942f234858f899f7b7c2ca6068e4c3e62fcf662019c7c279f073e5802b68b62c79713b392ee04df390c179d13d9e9ad7932579bc13ef25fbf4441042f4a2300dc573695c846eb6e0d41a8e6fede1b9df399bb5156479be577da83f63f94c62174c3e6fcf5a4396cb97d95d61e4a0e853f2e73e9da83b1aaa8a58590b104bd50e101fa4357e6573be76723369111528ad02d503f0f59ba725e68e400161498717e67b30f71630dbb43d7072370f854b5c6dc202b52882ace381c2f4fbe2193d0ff8e94db820358082c14ae1c6c3b70224cc7e3c2ad8f9652c59cc5aa51e8f981fa460844c3e6e60fd367c17a2f066e3e121b26c34cd69396ca11108fe91b20620db4eef62b28e26515cd795984c4c5fb9011e70d1e74e01941ad5a3667c1e5716017b8d71ae844c6dd60777c7aa6db71cbf3449121b88787e6cfb41a3991b601853ff3c69649ac09867bae488ac88045a0f37aa13d6b16f473d8cbe360d2508032b39f8d489293bebf6a3fd7c1f4b3a4ddc9bbaac9723d5b127d589279f3662052d91e27539ff04235d49ba8c730f7cb50f3c3f2fcad97e51b0eced9a8c6a7b0c0b7b829751fa476aa8a48649e69530e18f3f3a99e11a03c653ba485d77f57c0c867957d758021cc017c13c77b68bc061e52895cef5cf7d50bf8d50380580e92ccbb9d797cf59e6003d0ae149575173c761dac04f22630c6287b36f672ed86d032c56dfbb13a6bb200a2592826fe6beb2b72aea4be63fa5e9905f52a4972e9707bb894b623d319e036c74432414998dc39771968a677df92706ec50f0bd86310c263837a856a79edea38b4587217d8720e855a9fa849480f68cecce240534d01ad62350b3cc7a8e2a10485555c4835936c401012dcf2e7b44eedd0171c849110aee2be25ce9c20a692626397536e75a9a6e8c58940ce02adb82aa84454fc9b82453e443cf169b33bab94c1f5de82c5f0fb516bf5893265bbaa072f19ba2ad2c299b4327e0df183c0c76b9df50b5c064e7b23b9ea60075e1bc78b36aee8cf4130adc0ecf6459b4cc9ba33fa31e577832fb1cba5730bf9878b2c08ce4bbec6bcb28ae6ed4ad7d225010ec3b3f6bb4deb26aeaa10b1303d26a28a1c4f746ee6c042e5cebc32cb28f351512f836af8327068d631277707253e25da84c06266b9afa1db4288e385208561132a8fc57c8293521889024a0e9a2bcef578af8a6b5bd0220952722c4bda53ce9c1b0ce705753cccdf4436243f3b095b324903d8fdd6d472d8798382053eff536f9b10341475b587b71da205c8f4e013b00e773a2c620d219b8514e331587d2ae5b55861fa40b2b2a0da5175065fefc5f2a7749225f939b22c0a0f74645eea13ce4469a2620a6b5364c0d9f1e239c5ba87b58cfebaf17cc14ff413fb77a4e66babaab1cc78e86eba6376f7cdc670d63b55ae91b7f134294276d7f4bf992f1fcacdd5ee469c1a900560c0f098d220f006dd18c917d52c2b4047b2ce19045cb65c05bb3c00bbd716667529c92b78e03b9d7a22b0b3611f3272fced2c6e9754680cde0263418a1851c4c5b95a5e43ddd8d575292a4b30b07d51bb8d8807b1e18fc79130363eee76f5b5dc60f3802e238fbb837f982b8616b4723904cd94eb7f21f3995470caf6be80614a779a529cb6b61a566e430106b75a12b88ff67d8cca6420fed1c5ff834234244fe124536a43044ac4ea71682e7fae4225f5a6ab05611761d254db232c9c955f696b002ef5c943ba5901fe86cbc5fce1b567b96af4e85f4f949eb114136cd8d3d255197d71b4fd8859e29876e540cdc22ad4ad58c3d42f1d79fa4862640a6a171a607062559d7c8098f2857dfc74aa30eea9e00f002bd8f02cefe38f8a3aecc6624892256579e855574b9ef2ddfa2110af9113dce4aa22224e9fa8cdcd20445fa32faa2428a4422313c7136b501e13104bf31d55e27fc08f08a9982cd42110c98a970b3de6ef60a053bd33ad3897e480860c4b9d7cb2baf683496f198e71a0afc9fe99abf70de7be8347bb21be7ddb7075e559090c496c792024112125710aadade09f98e2d9db01150cd7e43e6b14d1611a99219b1ff4acc1718660b0d1ee38735c33a2ec24415596661b6d4a5de3f1fd7b99cc0a870bd39910116f228725b6f3855a3be544df094be5392aeaadbcb1753a39b62431b505dc0e64a5f8875a978b680857f1497e2b91c2ef4c9ea6a5d2276579cdfacca2796bd0439b9f7957be69779f3aeb30615b921bd0464e5a1f221db386a5db3df9b6c015ee85c58735f1b71ec58d38b201b00319e97e8bbe15a079cbab3f7c9b2a3523d17b6a4b47aa1c3a31bc848b5741add7880b2f9968f2501f4214a1580bd1d3908739091cbcf78d4876541d32ecb2328234e8f6674eb530326805a9045348d1ebfaa3bbef8ed4581a4046d215af0d8a4879b9906ca919f0660ab129c8f0222158318d704a51334136bfb8cfae63b3ccb3290f45c42837625d4b44902dddebf60d6d11b76a490b3ca02282b4715f439ffb856e7a1f1de48113f5c0b6990823f52d31476fc3df6063285dcf2db3878b89b191edf692263a80c283bdcd1578c81929fa8c99231f767696a7b0e5f7ff7d634149d25c741c14415da4aaa3eb58fb597e6099337804dac04be89ed1a9391a062dee176461d91682d673426b8fdd14f44b01f5c55f04c54f2d1a3543936d5294e9a1e3609e18cfd2f1456e92c5cead259ec3a6698bae4ce1b27b48126ab4a788bb7add48a8410431cf9b2f75bbca0f1fdc2772e02ca82987dd1dacb56fa669e1c110d9f08ec5a5c1643d7738bf4fd7676e38851f604af280636e1a5d777e30c39e19a91f568c05e1335fdf2a0910a0d1922057b6dfd312c60bce9b5b8342002c6ae53c891e88a66211d216dfaa0e0b664c5b61b9500a337898cfeb2a7b12341988af6c33f64ebe90f33db573e5249363801acc945dd968fd1a8a8d0cda2226296c2da586430d7781c8d6e79fc06bd4e8b54156833aff96aa7d210fb588f0fad42dfe95399e31643182a92de6c18dd3cf83a20d85ee618c086dc35b4fcc0bdd0a5408211fd32bcaa28061e0669d7103f1a998d8e2b954d3eb7263ceb42fdc77fae34c6caeeb0d08701741e3bdc002a1461d49bab66eac1e9de6bffcf041a0077075cc88fb0370f48237737cbfdf0e469a2df898c98d3061973a0d3773002bd31b4583fc824b80ac70ef469445b8fe0d0ab3dfc1e57b564a682fa16d3263fc1b58132d5adef33814f05fa48c12532fc3053a2ca3a1a3354158feda23a98bf49acc4e23f256a3307ef16c993ca9a2cb82c0f4c2900da26f4c0652f5bcadb7e2587e3355cd3e350335b15fea311d16828a512b48697f18dd04d0fe71a8ef20ccd75531d0182911d3ab702c5cecf6f4ccab9f416b04208963676003364c3830f8de55b2f11e28a3db5292e391180c507225e8c6fb45bdf7680bd3e66b23bb6715842eef4f17a772615af2e86d46be4674fc24c36c0e08144f6d912b889ec62a60a02ba885504395c5b61250563ba7c95da8cf5795740ee13c126f3a6b4a223ac54646b3a9bb33f6e04ba11915729c6821d02ad7577d896d31acc69a79709ad730a4c474d269372dbffb61cbfda0a1518d0240b1655e212998afd77905cd26211b6c09e45cb87628f99041418c4398048d71275861768f05bc8c509a0fd5d39e16c07c0326f4539f546a22dcac3a723ef933423c93cb409619384db46a89f287557c4334eb008a974c48d0d8f5c8409bf7492ceaa8f87e755d57e93b8e289dc1684600841a07028189a3b228187e8b3bd9e33ba2308a99bd741d2b0d9c49cad47df3eee7d8733ec5b260002e0f1bb74a35dd0f4be3b28a42ef78dbef1d10e00a18c2550b669a1a68945d9136d9c8513dfdf2cb56bfe9d865d33206a6c815998c83dfdc2c15e116c53dc6308a60b28b9110b46e57195aa6eb24c4c55fe2b031d252acd2c64eaf6ccfe1ed22ce490eaa2c946365ddcf2bc5d79be39d129b76297788ac9b9a2a58246e9672f2fce1af89246c35623421f2d0473d9f207713fef493508fa469cb31f9ac6eee80b029b5708299dfbe8bd138c68ac8f62f248062cb918693afdd2d2c5a5abde1808cc3a2af2bc39cf5b9dd252a51d5dec44ed148544b4be30f3bb964fa74d9c7b2ff77e0ee787b99832b79a6dbbdf56a02cb62641e2a74fb75e4690bb0ae9503f2b920c4c95002124892225abfe3605bbb52121499f267fb8c0e8f47a5e5f8a570236c6c880bbae96273bc25bf40250297b4c23a33dd395b7a408e571e19b1c5421c28e69650b34ad799f6128f8ffce5a99956d1290a0b71a1fcd52d3d47663331a111799e6a7e52a2efb9f7fb284ba150006d5782dc12cc0f1558c35e6819fd202d0c2ab7e1d96d102f5701aac2e3f656ee77de899beb76059c2891ed00b1cb0083aadcdf2d94129d842019e49d149ca2219609effec8f0c45df4c428b18b8810f80a28bb42c52fe911456d7f2382b3789cd192fdaa175349b606a23cd79c4291df104de724ac402d14fc2894985f11401b0f77e7650ea4eb480558d59533c79018795ebfb0075a1fc1cd6a2ef480cd0fa8272091dcde2323bd7d879dc08ebbf18955935d19d60b6581b531bdb32417e5475a531f9f7e70d0ad2d992348e9d0b2e93dd96268a231a6df0897791d9e999a3b631255d9bb7fd46fd670c9ba11823303fb923297dcaee9ff4c6d6a3fa8fce604cdc317deef5081d620a7c9824674a2c3dbc2ff52dad283e2ac7b308b256862a1165b1ec4e70a899786b723ce7ffbe1087293856e146460c9dd3338e72898b0bfa8ddfeeaf1eff6104b3a72e10bbca04ef21e8a76d78ba4d295fa078f6728f050b613b702eca9e819f29e4718cc16f229ec40e03034ddd98f5ea5b5460fdad1197105247a93a580eac17892796c30514ac34d6fe1d30bad145a807c00e67e5a55f656d5a4a228dab5cb0af8e418fbd20aeb5dd5e66d84c17128eb5b26eaae2a887aa8d6f5426ff16f375ba68b3b96e0667a1392b3f9a1e7023ef9321382a2cadbb45be0092084f8145f6a863e47ee50db616288376906712bad6b6a2eb5707def54b055bd5bf501d83171316e4596f2c22a3c47f07ea06af8bf577aceee172bb81221fbe4e7c2161a08213d8c23078128a186d4281012caa4b456bf64e13a5b51980b5fc0269be0771bff9784e47220c0f27f8a4eac4cad2ffb62abd2e92c0973f9e504a15dedf46620215df7313a91e8817cc4c4c05d9dfb917b855c14b56dbb0984e1533330c2e0ec82b9f1d65350715d5debd76b462709ad22158262b4ba56487ac1c3b73cf7dc065703e66f5de2986e997a04771c3c3bf005783b58b4e8dcdbf194d24cfa13cee65f8956ff75b13c03fbe025beb2ad7c27f983542f226100a12833dac04c23b478075861d72e506228f425719b1402d2023b016348ee6cc4df37be3815bdd6d62d023296d10e515a8209a3d5f9beb3646fb797ecb7878263adee890b2c31bfe8703062928750a2bde2222eaed039b3c0c9c5134527664473acd03dd649dbd7da3266c395904392c1a2c978f20b53f8efe43542987ab99d7407ac6d10a2ef60e2bc342c174f2401e4805f6c7e641482bc69e5e4697535110a30c96103912ed6d55ec25972e06060b16eef765e7db75195f058b7b1a9cebb540f2e023ca55c972406ccddcc146292daec76c9639b4a14baf550abcd89b7c325663f2790a5ef375977d289960c0d80d6f34390e60900356d9c0e034d9a61796c19e6984db646db83958ebd5fe02b705aded44e83f943cd994362fdeeef9b4e36f9cf28fc6fdc06fbbeb5294cbd293110ffb59e8e1e2e03a3104572650425e94f9e2e9fc3bd902530c2a5d2c9583640722a975e482b03818998255f6d6ea570155a1f86f4b0291f16d08cebe3fb70c19944eb81f63ef7d2d4b3109a45bbd69131b2372a362be99848f9de7280a4c061d7c4dc7a3c413fb664fd848c47ab4c715a1d524263a811844814af8f60a8c2f205ef74340fb0db019f748d6853f685e15cdafc6c3a70ac029974f5f9f21d4c2061d020bc99f55fbdc8822b44cfa382284a58d32ac54256e55c5ee822eb2accb0ceb6f74cb1131fc8017d58a80c5b64d18d52472a7bcae055c0875085e47590d07d5f51f64d91a6c82faa4960da81b4329da5f267db61f3a04bb4d28b748a46c21cd6a5f4d14a5de3d0fcb0ea9b6fa15d21420c860c7296ef79784b2d1ea5792132a3584c36e551ec0fd14863935ebdeb32ec4cd150ab6d76cdf9a7c7793fc001804f66bd236b02297113e27cadb2c56e4021e035dd5aa2b70919de3e5a41de82a8027594ea1b2c18a192095fd8de2f9a55b4bd711669742563729f5e38e3a6c25734b115defee66dd01e23f14251f22f5201a0656cb232f656ca6db0110a4842d85a4125d726a52bd9e1d1ea76ad0385706d669cafec0b580505511556d61a118ca3e7955c38484c84d7a4372d1988bf8ab4231654f040d4caed2850a2db9d216000ac7703e3bc56a50837b8f3ea62a355491a5d1dd721ac5ed44a3d52ad0f6061b07d159022b0812de355f520f4326c83d8d444ad67b8e9fc6c44de299478d172febccac587af4681c64c0c6b66d815cced1a6cd99931f0daeefc060c69e7c04dec5b478e77035233b22d21503ceebc5df4db7fff79ae6ce70bea5bc71dab6203acd627f404eda5681f7ff703acb18ef7da79c10c0e5c01fc010e877c837970813a2aaf89a82f8c07b06ec68123393f712c3c87b61141322a8ae5454f5d58e134e2ccf02970660b53d06e9b7f577c73b4531e7b2bbe57e1777e772035baa2f2e506a055312ea339b1494030371ae9b8dabe7c5d97afc1d9095b11e0a91c4a3009a6a30e4c867c7419e5d9d73f4b8305b7f5f6024c13ef3f0662593f61e10a828dcb3c604463006bee756fc4444eb75b51ce3e0cef07f60bf65a0fb8a2bc36a25390c7f8ce1552af2ff192ecc2fccea9321acbd9668eae05616a42859d846cc05c9e7ffe58957eabb449f225558400be739cae0084011f34ccac0e9c552780d13f612add584b76cdf4382b95af0add3964a6cf179918a9ac19065388baf6cbd04bd234bad5d11a69c81753b511ecd498d6e1acf16db17577bf97a4c9abdde5506139e0f36c309548d1e0938a6ed1256f5c927dce38ce9655dc8c9424edf5e5024c8acb60a6683752318231a905884c7e1860c1eb7be9261b2e268578cc201061d5b3903a64ab09604e123013f2ffd46930b46421822a2f9eb4bb91a5de2c05f0a9e05732a72d6b75ab065830700653c1ac9622b19cdbc9d490fb106f85f8e7f1721fb170c7cfc6a7b281bdde765da23c134b9f0327d0b49c1b10d5f543df0140987e0db44abe510efb67fc7f4d455d922a8ccce777775b380ffa8b15e9c086c0f8da8e1cfd2f177def6e5b543c20df1fbd7b2a1df2484f11291c18754e3a73e2677b768be4aa8fde0986d2603333dce2bc54030733c07d5b6f5b4f32f8d097b28089a929782f099f17b7e80da018be4b7e88263d90d0ea5303e431087fc6e472a4e2c89138c52755839aca9d3e7a7d3da70c5e3a6c656884d23c2cb1c1ef9a71cb37ae6f3fbfca2bc81327d9d6c0a9b34ab22034aec74b0d4fa0369e4e21c1e1ccf46df7e4ede39c119da00c5d7fa4928211ef78f6f5491b7faac12bc04ae782578719179bf34c0308ad751a267e6de6d3dcec4ce0ecab13ecad7e2d0ac534c1c89aaaa7a0507e8e42c525089f3f10b311d46b85358729b1152f072528bb58530283786b8ffe4c9386c11b207f73a91b6c86b0f72c97d2f564bdc8afdd7a0ff018437f7d529202134546b527402941f2e34195f6bf7a2781582adf8000fd10e205c76a3181d33969abac845e36f8c4f0e50f2265c50a0fa78cac2cf1434f808", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000008f1d4f8158740b1b172a191da848c84600000000000000000000000000000000c1112a69c9fa3119311cd7b4b7fe583701cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } } }, - "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.109601222,"runs":3,"total_seconds":0.328803667},{"name":"CalculateDecryptionShare","avg_seconds":0.606677194,"runs":3,"total_seconds":1.820031584},{"name":"CalculateThresholdDecryption","avg_seconds":0.556015083,"runs":1,"total_seconds":0.556015083},{"name":"GenEsiSss","avg_seconds":0.124351986,"runs":3,"total_seconds":0.373055958},{"name":"GenPkShareAndSkSss","avg_seconds":0.223574569,"runs":3,"total_seconds":0.670723708},{"name":"ZkDecryptedSharesAggregation","avg_seconds":8.412974209,"runs":1,"total_seconds":8.412974209},{"name":"ZkDecryptionAggregation","avg_seconds":49.375812292,"runs":1,"total_seconds":49.375812292},{"name":"ZkDkgAggregation","avg_seconds":20.649942958,"runs":1,"total_seconds":20.649942958},{"name":"ZkDkgShareDecryption","avg_seconds":1.465726409,"runs":6,"total_seconds":8.794358459},{"name":"ZkNodeDkgFold","avg_seconds":62.099165625,"runs":3,"total_seconds":186.297496875},{"name":"ZkPkAggregation","avg_seconds":2.150821000,"runs":1,"total_seconds":2.150821000},{"name":"ZkPkBfv","avg_seconds":0.330616986,"runs":3,"total_seconds":0.991850959},{"name":"ZkPkGeneration","avg_seconds":1.356784125,"runs":3,"total_seconds":4.070352376},{"name":"ZkShareComputation","avg_seconds":2.679413639,"runs":6,"total_seconds":16.076481834},{"name":"ZkShareEncryption","avg_seconds":2.500371689,"runs":24,"total_seconds":60.008920539},{"name":"ZkThresholdShareDecryption","avg_seconds":6.122451375,"runs":3,"total_seconds":18.367354125},{"name":"ZkVerifyShareDecryptionProofs","avg_seconds":0.101859736,"runs":3,"total_seconds":0.305579209},{"name":"ZkVerifyShareProofs","avg_seconds":0.215221433,"runs":5,"total_seconds":1.076107167}],"operation_timings_total_seconds":380.326682002,"timings_seconds":[{"label":"Starting trbfv actor test","seconds":0E-9},{"label":"Setup completed","seconds":3.048507083},{"label":"Committee Setup Completed","seconds":20.239957500},{"label":"Committee Finalization Complete","seconds":0.006028542},{"label":"ThresholdShares -> PublicKeyAggregated","seconds":302.757272083},{"label":"E3Request -> PublicKeyAggregated","seconds":305.289772375},{"label":"Application CT Gen","seconds":0.309189375},{"label":"Running FHE Application","seconds":0.004053542},{"label":"Ciphertext published -> PlaintextAggregated","seconds":79.623283542},{"label":"Entire Test","seconds":408.526410584}],"folded_artifacts":{"dkg_aggregator":{"proof_hex":"0x00000000000000000000000000000000000000000000000c231c04d197a8e708000000000000000000000000000000000000000000000004dff372f7e0128d790000000000000000000000000000000000000000000000075b49d509002a176d00000000000000000000000000000000000000000000000000013555c49a6d66000000000000000000000000000000000000000000000000e6842811517da5170000000000000000000000000000000000000000000000003669759c5c8027dd000000000000000000000000000000000000000000000005a31742b4a58157240000000000000000000000000000000000000000000000000000e79318c631dd000000000000000000000000000000000000000000000003f855d900f178f79700000000000000000000000000000000000000000000000660c1fafd86f70c7900000000000000000000000000000000000000000000000992e486dc1bb3e6960000000000000000000000000000000000000000000000000000e7f1f964c980000000000000000000000000000000000000000000000004ffb738420181a4ac00000000000000000000000000000000000000000000000f05f675e4f309b2960000000000000000000000000000000000000000000000079ba8c5829d9868b700000000000000000000000000000000000000000000000000011dee0a4299770832f879c803fd7b787b6c5ec4e1e0714f5aae691b0542def35cff3251fdc5b30f8d12e5ee82c4f7d3164b41bb4cdaa80237d61a4cb9e779f1462a241f80f1ac1cd23d6f8490793bee73dc357175a2b9ebeac1e5975a21e290d819a4cf1d9ff01eb1cbb093ecc02906356b0797a42cb304014e505f70f1190991810dc812e176297a3c0bad21939a9b1cb32dd6b82db4643d1eb8aec6fbc56dd93fde0996960c1cbdda9ab25913b46b163d4a332e799d201d1cdba04297f438ea4a4efd17ba1a289f4af62ac367ef7ad3197f26868c463bc7f7be75ebcd1f4cb0d8af9ff494a9183660f437d662a4eb7505bdf9d193fcf0c08a95e9fa826b850ae8419aab4e8a25f55283723fcc95ac9eacfe4409b460a0d1d57a6d77f7b524c405002c208fe520bbe73a3e2f784dc967fc117d7f6d480ac9960bad15f20b7ef9103bac9795b123787fc7493bb5e8dd91740730cc2c2acff5fc0b376938de79e664adfa8b9c1f2964d0f06e4c92e958f5fd77a5a49f0d4158de25b55f4a022794d66fcf0d66b61c71274729412aa51cfd6caa29c854f02b0f6381c8969e2927833619fbaea251280a6dfb0c2098a8eeb87c9606c13dd2fdbadee2822d0d474c2c5b2d29a7db431a3377b08f5f1ca652302d0ec96d751aa03a143ec73e27fc7287749e48007f121d76c44c0f508cd42146d4c85da1ba7f0079d44f7ec8d86385facddd56ec7d3b2c2310d8d4896a745ebf3284b67d7dfe6a70bf912feda40d6a7d735e2c6917ed0c4d75fcecb16b258dbfa38b206c4390536979a9fcee7ecbad671b527245fa30194e070f0536b206f4728be22149c5043f9162a2676b1cbbf5970efdc3f09adf15c02013d398517db992673bca32c394646855203b7e50f25af4e859fae1186d10b2529864fb2290e037275f80785700c29a8df6aedbd6742ba5ea46d6b7452909274fcca2b7ab41d0684c1cadebaf11593d6765db6e88be19eaca96735db527250a9d92a04f51e65149dd9b772f2e4c77ef2504a92b0ae5ef7216de2dcff50419e13a3bffdd680ec1c60b461d72df65afac17828feeda81b9a2441f16480b821b5d909d1c8242c008e6b4ad1af1d798bb41a68c767c9b3a45a60871364310b517d0323cd8922d5db1fd5bc98f56ae03e83eadbd0a64b1a960057fecf780d189106993800da22c6d624fcbb23dc2031f3bcf55b2350b901fa3b6c9598cea064913c140a7440455db00bed4312b796f57a058c03446d58c84672705a1af9a15980262ddcce5e03fa8737cbb8d9b59edfb4bb0376593116b6acf935b6ed928d1b7039574bb7faa48a2e45b9abd1b95ce656a2c3fcad52e50644ab4f430b092950713a9be10309a139e4114960f59fd48470ac69c1d3eb77b9ac02e1be09eb1eb571345da560337d679e98eb65ec13e231b25ace4b4f4327c012879a4eff8752252073bc94743f68c6488c2659ed13bc4777e9b31d819d3528d12989616b713b2a21f4561994e2b294dd8451f4d34e7f17f37b31c7659e8ccda79528830ffb2d8600f7028bdc8cc1f247db1c996a7c683e4ba8a621111c4bf18c193aa299e54a818290a2fa13fc299ed1c9655ffaa435d5581e27e321594d33c84721e01e4c976fa2f83abf8d7ad3101c5575de5ed03233a502c87ceeef5c8ac9c4ae7ce55e5a12702c4a8dc4290c18ed54297509ae69fc6e10116bab06916be62c1b66d4c31452e28780558518a9a0677de43d2381aa22d4cb1e05b371a0abb3474990c81e0045a1010f281874e4dc7bf8f4c266bb74d4de791f9444d6a979df2930d4ce8989d5f134d2b517b71ff043acc1e77ec06863b770df40820833ee29309e6d5940c98e20a46b2e7ab26ed6e58880c0c757d00d9e298b2ff637f68e3e0ae78d362684ac71f4ef526adc5a9362aa12348d54dad58410e69e9998c043ef9cbb9ca457f367e2b540b26156b565d77217132ab2cda06e373b5c8e42fb1c549218db94a975eaa04925195341bf71bb44848f66b754f8844df69caafefacf5a80fb7c48d3541b52a54d7f1ff24a7da4cc29a6ca8c63a6bf1f142faa1da9ffb6cbf576a74f1665a2f6cb8998e325cffedcfb1ff8ae10ddbf30553384be2327852ef6cbddbb80c031612944a6367ecc5ddec0bbdb492409ea8ee0b749023820855af207f2771efe415357d2e65146970c4e134ae290cfa6c78765c9f93f822f7442f38be924feed3075d0f4c505e8f412cd52430157769957d983cf833076ac64ff6cb3089a5c23e2a4b7311a60385b29b440ff083a03357b30a7ce34c506b724f7491d6397a5b220bfbb4078dde8510b9516907fc0e26a5249af99436db17ddf3e08b63330a4e1f2fdc4166cb2192df0fd7da156ff33c342789ebbaa8412c362670a8e1d1e3660d04c8ee09dba54e2480700559aaf372ac10b3fa798957ca140d1293c4ab7fde740b5485945480745e0cf536eb74a1b75abf1f942da1cc613621de9fe86c60ab972f724566c306515796c892b36d522b92c2c481fc29f1e711953987999e3898ae038f4fe31d52d3d91f94e01fbbb0d0101568ef1b2d02bc0ef78877e5a9e51cee134025275cd9aa251def979540cc5bc9963016fd04a56f42ea0c384d20bcb51b2815de4721915d31b83934288813e0914eb9388cd661dab0fedae3594e84ea0519ce06222d0c01325eadb2239c4281880b62af0913eb476a3e57fee4ef2564fa14495d5aae740b0a554e1b0155f6451c2bc68e136ef1b76a6d538cfdbbd17c5c2d8ea0571306bfc84903b7de1030e1596d801b8faa51635ffcf1a4c40b9b070f2fa3fa1053239ff9320d966969c616127cd2fc20a65986161a03cd6c3941aef31341535b0568b2a713c86a642f4da67f3141252ab971636722ed12adb2a793c72adddf418e8586058ee51dc6f148ac40c0c26dfb144bbcf5d307c72968ac60f7257b9964070a64942d647ee644678caca09ce457f2e613fbc10526152798a5f624546f7fc22bd2801f4aa585d2483b8472a4b3f5af24b3dd476e671d3cc2a52f05bdd9fb07430c3cc97d6bfb0c577b2f9e8e50d6e1fe74089ebe128aa657e68926183d6cd87dd0ebd530920013a0a301d505c47262a9edebe462a0ac28c6cd2610b59a1787db42188287610d1cedb9df8ccb0c33e846116c2d571ab84660a728198a7806104f6e505519e52bff03fb9eb0a00256556355c4cd7dec17af7147dc071beee6a5686d1ba5a185809baf446d80e20cac33cb693da5da0b962d2bd19e017661aa136cf73ed71baf62505bd5afa4d39bdd0684b582ee6e6f4b7c6137a610b8a5f142ac1cbce5c02b9d078a10b567ecdeea8eac300fbcbff1d8f92d75440396973f60f9c313f2fc46ac28340d60ce96317c2b1fa08c2604b0d939d0d18b1152f44f71949e022b665a3915226bd97337b1873de651fe90f3e70a3cd806f21f82d88ec05123bfe13d32b3fad2e2f874ce2a4ca42ce4276d95b216d0d11aff23e9053c45f28d1ca75a522049373683eb011b3fe4eb644b68274fd6361bea130bf786cc75187bfbff53c6c8809b4ee3962dd87ec04aadd8440848869d77361924a910d6f25dce1bb4afcd04c6d823db6e94f29baba17e10f598331f4db5492a2622397ea1c9c156db10797fd4570223efd35c0fd70bda70794e5a28ff326d03194c82df5490a622bd475ec3b6423cb73dba4eea28b6cb978b7efe60b0da77f31c2adae631cc4fe8ecd22f605c974bb849132a4b3339ce8e68c7a6578a3acde71a7a2c7fe23193fafacc18024a9313cf0b126c6d7c58f503803adbb09809b6090818993e81b3dbc750cbe9ccd56a01cc9266bff7289e2a08a14124af6bff64d9173b277e654acd6db771e75b5cc586e8d8153aa7ab685032632cce7af6e17e7d1f8f30910d5e7a54c8b1972efed7d2f5e1f6aa76bdcba76057cb941950523f532d02800f31fe4bd26167857004a22b83585847bb2b97b487d08a0a164c301c610ed2a41c62ade87f7ea3e1376bfe73a07e8a98e7a68a0e59fadc1beaf37084b302ffc3df53fdfa8cfe80b5a0427f8024d1450f11fa7f2747efb3d30c0b140fd31bc3a87fa0baa6219996c7ba23057aed724685d8806b849f935bb32eef4a605605a81dcf2249ee73ea621b56f520e32aa0b6a3d93d13f29aa0b933361b7b4bc415b2342befe6057b29f81986d412861cdf37c13ce9e02f395067e17eb426edba05ee1ad79588ad90e65a4da63c3a0589d6d7c393c35e1f9c6026cd599c3df53c2f63ca034855057d89de7932ed12821db83c79ff9010773cae04fec7b850ae3f04951f33d5cbe2f4a05d9b5e3369785b1180c4b645c13a811156e17872ab15420d6095cbc95077e025a6ae3cee3fbcf97a3b552e6f10b2903ed11f88f60b4f3d1923b42010f679c50ea946e9ea8a8332e59ddcfa2f9d68b260fbc8b81452adf41022900746861cf9b59b1719e2eef8e882e75922a6be8df309d8694d5ee46d6a10e370ba867c3128ca6dc7883414ad7f3a6b3b01479f965eaa0247cf84938dab303d0942d69328e413e0c5818e8b0d4f7ce1bcc11f65e539de2ed2e3fbb46e4e25a36903fd8576aba8efc706cf8722171a2fecffd79a415a3d3645924c755f8817e4cf0a30e0203e5c32c990022b3ff687cef3c789d018875fe44ad11240af3a1ad14160b802d382e8f576b579de16820563c4e212cf36752246772bd379a5110ba9f0b8e02469515c2c9676c859356e0941689efcdb6a2241d0ee46b580810410afd610f1ee5c4d2965d64153350490ca842b210984ed286463eb79bcddeff41cde9fa4367f246608dfec52ffa6ee6bf08df305c33e96e22593896ffc1e36230ca2aca0629659dc3571f0be7e809e9a510ec9a7f0c1723d69c985600f3833bc02fe173deaa04c41f9b5b1f83ea37d42474dbc81b94c81b05a64581aa57213cc13165676762c6ad6b51ffb3a73f1f10060358b19de35c6d4babbf829b224e95303d69362694e0ac29cc0fba636fb26473b14fc238ac027dd77f8b677693a5a571af4105d307cf0d0696d37f321995ab288e77ce09f4ddc73a252c300d4a00c981b554dacf0415b8da77fa1c4dc28ed4dde3a3573c6c3e7df3f2390d98087ba1d2a97806f0e9fc3c1a16c64c35704079f146cffaea0ef7314b90d21667bf209570875505253493deac384d5044c00e94d50a15a130f8362084459e05f3bf69c982a364cd3bec37ee44649738d102dce02eae15c9133963dc6f00af8027b41704b1e65d7c2c3c445743491863faacee7a449739fa7c5faef8f6fa8091c326f1fd12a2320f689d0be3d099247858f481ff84a1c3a760f54fad6eaaa7461972bb5b1241c2e95617f5eb5ec64c536fd9a884a1d21e74b17d1b6fedfd6524f370ba6b023f770d9877a3968948bb8622d649578f5c39217b8ddbd83c42a9f826848f6ec2c4a78791b282fc5551bad0e1b3f95f63b2ea587b46c4e4b4602fdc1f24824910a59a9a3b3f2ad2b6058ec542fc2b40bf67fe3ea4c4ceec0f171a1736b0c63d901bc7465c249d7b191878584f5254636ad94c076ecdc7402eaa26be71dde4051302af10ab5291187ef10822091e574ead9269789d6132639c25627efd71702202d180e2caa6d96a03e8f02ee3139ba30e989586cab693a0a79e12fd162c12d432407c683bf99ae6a2cda59cf6c90ad591ceb18a99e3c1148215b36b2fb3e39f00486f2ed2caf1477061cb1684be5c51bcfb19901b9c02ec447beb24078171aaa2f64680388b8bb597288b662a0382edf3c14ac070737e86726b6d12c7c500ce72d3480a05120092fdbd1229001fb4fb81231535b9653878afdc3addff421963903786aec552940e39d9ff33460f74a33387f9c70dd4066697c5f2bbfd4c2efb11b86d2cb5f3fc4af8ceea1a93a1103922e43dd160bbdb8f161bafcc7fd31f8191505db967d01b7bf5e5c929ef3469c08f817fd7c7ec78261a5cf10d0c06f43810c2ef26ef7f2e9336ff587f6f8900096de08d6bf739f6313594db9443ec342c30fac34260f3c79034b64928e3081c1ee74b0fff1cd01f9d148601c02d6e482551800e080c9c229d59589ed97ca4af09a2608a599ccb54c19aabf16486ff4db460e7e1472fc208c2ca3ed2a948a06ce66ef010f7172df7bd2831fd060cc233dce03955bb32d144cebd97bca622b5ee2f2e6e8399a2adb6f538bd41181201b029c2f796924efa294745ad0c575cf542fb7a763c43db2cc32ba8db6f775b99b295020596ab671b0038e64f5606a8fa8eafbd10d29ab148e907904f73da636d63ad40a4c6396efab8297934902c7cb657d39c8dcf44eb4cb3a86c795841b0f93be581de8532c7577ec52f4a12537a9956b0ac212c03cf336c0bcf99dbd3b892e45ed2d2a71dc9d826fb9cdd91d43408791e5ec56fa2fba832774e589084086303a0c16ff05c6f5780fa20a52820469bf3b8978a7ed7937b0217479d86ceb9cbc90ac1117c686fe10d0a97288182e9ca7ac5e47384be383b7cc53854d7a5505c6dc000ba57d3623d8d3f995c14f756832ff184559ec8d627221a65026e964e2eb36aa09c11f60eee4a48a5a59465eb6449bde6db4fa670458d925591318b9082c5f0f0257ae6d8b494bc66a2443dead3e49bb7bed337edf061f17c49b264641fb59dd26984407e9accc23b4d11adcb0c10651e75e05ed573edc971df97827ef712b551513e963428dcb136b795ffc909f9c259347fc67ca0cf5d367fdb41d830d91ab02366b303321eb6971795d674b16d05de722e238f9f8dcbbbdf82cdf46e2121222aaa6f521143bda9fabc2237e2fef019f237d6fa666acad9a65f44943d549b515e85e088d8e3ec42b92d49a3994bdbc03ff1b0073d63c0ec74b9bd5008c4fa424585a1fc5b5b5ec1f6ff4d7ba9e1d0708efece885a50bfac097d50b0a8a450527f3d7df5039a8c1b83d0a7c217819cd3fd158d79a679145885730d2da273e422401b4c932c33726c20c3a350f4141a5665d3508cca37ead4b34b4ba27cbc6b9132dcaaf7c522f40b88edb47f434de442846a765c2fa47788196fc9df7c83f5b29ee831a1f7284e6f4e3c133df7d9e54e3ec5e0a7cb3127a1374c12ff4d616de163ab2749853ee6c337caaaf97084d29d86d7fa4743b52d7e37a4689f4a754c42ab7dd83f960a3e90114e53473f486132ececf39a29995d2a8174ae2b547852f2f30d4bacce67216597d1124fbd87ecca5dde59c0f22cfc7c6322f740a909aeb0a973c5b14f33f2054698e7e03e338769b7919224c638db7924a8245e308a43b1f85d7e406c8446e58fc361467f1c6cc21d378faab8ee790ace260454e153e6210807cd049974e1ef0c1cb56c32ba83314f282a98d1153cf2af5665eaeeb6a16139a127f61b33fd3faabb69d6cfa40f9329a58331ab431d59240d8bc2e966fda22d7e1eb4bceb911e8df376d4c050cf65dc124dc5faa21af8758a079d7c0b85b02b6c0ba4355df267525d12e8205f3c8eecd10356464704c7cace51d0a81e9640bc6d63255d3e7e5ec844e84a5230a07bb33c79585fd8267b76abe7150a786a21fd35591ff3238fca651f2d59cc90eeed3ba2253efc0f567ba0f8aba737194af1d11a2a63c20be531387940968da873cd43bd7dc03e3c1d3bc12aa2fbe99ada21e20e2b14daa35699764599763ce95a5506581bc494b271f865932bebec143ca0e6358610510cf53ad2fe1d5f61ad1d24df406b44656c61ed6e7205445faf3a918d391f98dd546d7803485b78a225a4ef29ff7599a16d288af013a297784c0b02a6fbd947ad57219cc3df3cdbf0f7d59e36ce25c95edeaf6245753f62932bd9502cac9c21e636fbcac7c2987640ecc38ada9a81278c7de458bfb76216033d8d313c337b1a163a73ddc3140db57161a4a3fd173ed6b24ffd66be49ff72c6c40dd0fb35f02e279f9f385ab009f147da9aef912956e37c57262166bef70d27b677f103e54368d3cbf57152e241f21a7a3ce73647df2544af9ccd49dd58ce80b31e8192a528fd130ff6b38f3bc4f495bda106ed59c6f3f0fc39c0395b4497dbd5eac0c6b1abff83546d63b037b05b68ef47b5f6b5d8b0436d38f372d731d6fd0ec472f39308f03dd5783ae424871ffc058ac46f5c126810a51504fea799030db97c6077deacd4c8da49de813a7dd9ebd434f32490cff962a047a55647a095a8279760d4eef1aa7a2eea2c7b8d80f82019b08b4e788b538de74f2ba002d414dd6abfe206033b26f0a38b1b88a80fdcbc1e8ab9f37d0de3ee6375c07915af54a986b712dd8a4a75a710c535434b496e4bdcecb0ea8f8ff48287705b3f579b2aff304930528e7d18eb79f7043c9a1aaa1e46dbd5fb9dc284e2b867bfa2c371bcfa0cff8246c4fe2f51460bde5647596fa9e4a8c6e38d838c8e8467b5a684cd45963b5cb04c2fc702e846d4eaeb30abf70328cc7145e757144d261a89ae51b5fc2ed58f01d1ac594165c1ddbed15007d392ab654909ba10443116328aba7c8af8b09265b27d7c53374a87bf6f98d28f04592ec3f7d613a50ef183784e7ad894d47ae991c05ac8a35b6d4307f7931256358077480d2eb99142c278440fd800d9fb8578bbc1af8eab305ee037541e604da2ed341479186d828627024d4f1363067737ee3bd0fc6ae2f1b556ff69d05c598a92d68f6d7953788aa43e8aeb4971ed7bab3fa7b079e231f452192c2b92e2cb0e2305eea4e2f2641a55f50be480beaaf829daa8a2c89801f65112458cd3ae535785e6b84f4430210b454a30372b14fda4e3393772d720d07044a0a99f8f8e247d152f8ec470c6eadf946e52499a5083db06d3bf62b632db82ab142bcc95accecaafa757e9b56640b1f106678cf8109554c8d80be21895139752ea939af20954934cacf705c99630dffe4254f701420309bfae9111a6f243f7fd657036f373dc43c281335fb1a17b843e61da43093df3be32aa0121b61059d9259d23088afb4baf936b92eee94156781c4e381c76c62791f879c0d266b33cff5acabb6b7dbc81d67a22cc97a976aa798d5eb86beaa21da26ef726617683a10d48af74af941aee5b015c31e7c4fb0af21f474a190944e145bea17c727ec1f4de4e389fed639b14cf173b7419caae9c3c33deb11806963024e14107520e3a6f816911548b98f74c0fc5eb6f384807561e18dd0e417c96a062e12453e0515c4ad0a7155e54724b197e6e200e3b61df77b12f853c8f0192e0b38e8bf8b1fc9f87ecfb8749f4651d29c764148128cd0172f37c559032cb1eaf080664e3f167a19ffde30c1c79b7e3e44f715b4b20d5e143ca7715f0300d4e7ee2435c23705f6c29d34d4d9bee5da3e6ac50d25ed57adfb31c71777520c147b0d110f8ba0183e296f88dc0d7dd30cc8c1a552e20d3484781fa7bffe789cfbfbc38252f357273d99464757ad98c4bd83924a89fcb690c3548c35bf6cc93722206b8aa7b998059dbfaf9b597f883df0a4e79ea7a87586c53949d42296315f262e19eb4443541e591182121a4d53c9b009bac47744cd2d655db9645f3b31b46fd1cf213cbe7416a3520270f458d7ec5604157876a5b81be998f854666815e1ed6792252bb2da0ab85a4b49874fbdf69d9ae78e895a82d02855d0025bf8ea19d5a7f67b63a52e0c627bad904e362e90ef868308717e4d00895153f9ba4f20568c3344e505d88b0efaf03ab4c593750c40c438314e010f8aea4aa22933f64f38aae2ac56c5b00c222ea879ad9a723dab3e3324fe8ce6e68b54f9b838a2e265d2a81d091f43a97b1218727895e7980462b651160b79beac0207bfa5c127701088140fdc4741ae4f270a6d0861d29d825910ec8efef266852ad117e8bfb72c4827be08ecdab3ab21214b427bc61b5bd42ff1f6e84198732bab6b043d8eaca51e94815db13c22a5f2178c97d3e67bb633138012c8f19e6edca36f28207be6fd25feb47eacd3f7300a12ffd16717ae1f87b30572f78fe8e9f6c0285864a19d52ef1416c6bafd296ee5274e16e37cb37acb075b842be01f7f577d430667f65154b11070b70437937270300c53983a317d20ef5892fa22fa5ff721fb9be17da55e2f82a64f5a3febf7192ca47d3122f9be516f7a7b8c2c02d0bfd706b6ca47de944ae10e732351daea2a031e416a38ddb62e1af22d03e222ca180a8b69288c999f3b851f5ed999b2a0a61fa27837d952cd8421cbc240d987a37155a8c1e5e1f4eb7c49aff109ac16af6d0784f3728889808e1391e69b25e095af7a018c8a81f1cefb60aee7375879697e14388cbf84fd159c7a4e1882c56bfb3bb5b5b9ac8db66038a0257e777fe0c435090aa91cb05b93a1fe40b21f5a9b40143eb498fb7e030e98a4b9d491ef20ae3a148193295c2c9dfd0c148b0d6e84fd8eb8edcd5ce1337a1998e78ab217561406166e870a1ff737d0401591c5a5b2f4da40bc19a0f246793f62b04bc5df66d6020970535a0d0e97b797d44e58f47891b11c30b5ab101d43e6413f3e5c048781910a1f8ef5c1f90a0c2f60909f5b5109b80e03613cd84103eaec0ad5cad363665e2234e51f0eacdc42f1dc6c74fb844fbf0d7e2ad23e9574946fdb9d7a3afd10910881c68d7025845e58e84554285ac330b2317fe4ea446d6917f98d15e362e1481d3d6a5c2067ff2ba2cd17fd68cd5ad79e440ec065e883123cb3950f2ac4b6480db142b1f16d08c353e2f557be1dc2ff5359e80e8efaf31772170cbdc1deb54d1ea5312f9e915c83584235ed85109d4a59bd9e028c5f44eaae735386f684a6f607ed78682cab8beb171780c9a54e3f57a392378b6051bd47cc4104dc4678c74f0300f5276b1f61679e9c481b80823bfe4fbae5082026c4996e61a319bc1274b00ea1dcf79295bd3c34f1b1fdb77357dea3955457b33db3393905ea35deb49616071ec289dae2a241d2d7715964b2db700909956a819439b1e04a7d1f922043f204570c529d1220e1c5c3a83350978b08bb3d65d6844cc1e615d634f5ae768f6e1ae5775ac84325b9a3c2976dea264f8bf3d7a1e14c31b9cf8d6c4855fc51c750042533b8db0f0ccb4a24085cc029702b9432137bcc39c18a492ce1e62f3c2393070a3d2b973aae4f5dafb491b7ae5e36230fe2c376a48a66f5d516352c8ae01c0cc43121e8b8010835398c313fce1f959c0911058646e807b1640cadac36369f203610cd91f6f96552cc2d9b85d3ed3dfee006928fc67518977a68ba6cb35135164cfec4ff35ae655e66f1586f933e88c2874350c6091f90a682e32c02690ad219264c0c4e73ad397e314da27dd9fc97d941bc619e13826ccb19f58d0651b693270214a4270e230bed23fd16705e90ffb3c8b9b7d9cfe03ee2a094a9e9b2909f2678bcdf76a943c56ca03fa8189eb05a15b10840e4f83e7ceaffbbf07fa822851245572d390a62945618a0a04cf1b830df30e13a4bafc8414b13ffacf3488a9725353f07f6bddca341c57e2a1d437a0fbba07c4e5bb31fa6c41d9d71fe038e6f25ad9aaca4bf67db38dc232dca80ac6cc577f2fe237614c0987331d8569772871276ebbcd824e2d6fdd1adbcedd9e14db8c44abec004b0ac587962949f41b1800c781b7221b31e4e3a02d40e9b2c1b7ea3b73f180ba5b1b27ef069b6f05961800d662205d6e426c30f893bd0cff9b606a3704ddc4fda64f149a583b5e2b1ec5012b58b7e12f4ea84f4f9ab698e625d09d85e8ab36100d477be566bd3aea6264500cb6669de3a9bbcfb18d17dd15a9d6827277fa1a6c57b83cc7aeb2965222df40929e9d749b07a43e36393a92da64323ff33bd3a3cabe072a929c29847450a431c403bf371e626651430d76e2f0f1142ce5ca04d5211a40a3eb5b522492293041a356d17586f9c2a30628c4bdaf8f93868e51faa16f6da0e030dca9a4952eb1c14219210c228064f8846d40e9368ffc5b17c70dceb3c4f13b9e42dca06c872ce0f6cfe8b8d14533b480f908e52f254f91ed84793aaf219683c7d271ab2778dfd30099fcfc1e4ae79674ebce5a1315298c221404b418707036cc69bab53c5313800008570b8cf57caf6020c9829afa07666b66fa1ebda11ced87b18a109dcb1b41aba076719214db7fc4b5ff92778ca481e89f912fe88dcd39ff7794fef20cd9520d52821d92a633a17d70485634a0541c0272f020df6f58af977b0e91d6a4bcf03ae248c091081ccc4e8fffc6a050e05357240b08c0ff78548a041239002452a0ca3dfe850ce5c5ba424344467ed464b57e098456fe57a2ed16b1317685f26ff0f5ed7b176e08e4d9de99f5b344a7d3dd3b0bcb4effca2df187a117c576443e01dd6eb7912779095f7972c7414cf7b72ca7904ef8364f53db792e6cea4abd3422d840981612e99fdd97f2f4389914f0f78cffa1d95e815ebabfac5dce2ecf8412a99be58b4cf34ac6f91f5b4a1dffd405bb782f6189d26f468585fd50761a8a52df5ed597fde9811dfb1d21346499c99146b00cdbb6354a8118f558295fb716b2a9723c61eb8b1427d6bca3db42ac998223f93b5760ce1e263fd58603f18e55a0d532e7cdf42645bdb8cad58eb88dd52f2349c626f08d2426a383deacbde9e251aa2b56f08f371c6d6917381ea4241973ccf1afafadf9e78745d163f0de52db4141c05e1a20ffa115820d0c5d3a1ee1825490c446493fd2ebc52cc34783c5b0304d04162c1609e7340f3c6559cdaef78739a3ecc0e1fdebfc1bd4aa2cdd31d3808275192c8e3cc7921c1fa351a66d0d8f43bb8bce2999756a00a2276d0af4f141934be7f31bde374351e50ccacc048284f6f6ef77f9ffc3c3a7d9f65b4d4b7db1e6e30581acdb9fbf6832fe4e12149b397f857fe633b733e77628b0bda39db6e16572f317cb53137f8dc0c054841faa1c0f6752757873ad0b2c78d29d619ff9d0832d2e39f7cc552cd4203cbb6ba9455ed305398629287b88486b0b914d0be2d1440fddccbdfadec77be5340a0f75035752c4ad485a9ce28f18d0896d358c1560f1faebaba5440acaa383fe25a1a1849a29a56a42d8fba183191983b101a5ac221dca03aedb73584a7be6415ff937bc6bcff8fe8595760af12db068187f52e571d88580aacd45b8723a5c0fb5609f14c5eba1afd3f40f5a164ee4b97a98a72c82150e3ac2ee4314561df798eb3c3a573dcc6aa766f26a84eb7b6e5584c3243911c09193d51120216d2e246fa2edff374a80c1932c39acf41c3e3e040b38373df1e28db8d4179c2cd8efd93091b611d7f70648e832a9301d931c14765a87bb97009b8844ca5d4bfb2ad0934927e11da2fd9832690b28b45793cdb1b80d9b6fe68273abded42460ce3d60f075af10d465818244599cede90585ac63b43c08d7080219e49ddf2bd21ef6119cb9d8aa31cc1f8141373ea1f9d490f15be7ab965c5ad290845de60a58fc9ca397be4eb86efaa128990d4f4441c3445a59e608a666bd10c0c0ce65aeca5e959fd32eafc6212421fcecbd3f10d6ea744d0f65cb21f162b23d804a8d20d2873309f646f8128fb9c9fcd02cca58845c5ec1ae55ed669bc891792260fd1d7b5ea3c3a790494b73f071fa0cd35624889e52c965e136c215e142cd89743e54a6b3988ef9852ca79e456ca5cc1afd24eade12ecb3b27ba89cf1725bdd58395c8594e6a8a01e4b388f76d54d35096af2a521220e268b0dd633f28205bfa81ec110c18221ca77e9bd5bf3f6d090e7b3c925c89ad60d4bd9abb694402a7ebea4ee8be2ab21b00146f7033fb36ee7c6dbcfa20b80d70e2291b46bc8d2761d2dc0d29d23d0cc7a58873bca8114808ac811fc3f45b25cf41787065af9101d6d2ce542984b742c097b92e85ff17d61ae6fd69c597b7dde659a2ea1c201f18aeea00abc08ad5a9d9acafb5316d1054655ec5a02bb2434946b584af7271ca1629090a0cbdf2fe152faf22486825550d29bd8b08899aa562b391cd47deedb726ba0d95a1020b5be53a375ca0c98024b3a961af37170f6b40cb58186c8ad79724ef8fc6d60b44f0de34aa1146f6933f964afc553538f3b2a2b7dc17a83c58701e902854b59f2a7ebe132fb19ae003a24a693386b7fa4d0f7f589542cc6c1c702f9f592898c4de2f94cd9a689da1f0855dd1d29def7e48a900fb989eb7df340e20a9ad75ccc9711d53d4142f02b635db1ad3458617e50c0f91ff6e64858b669909e40555073b0af9e9766e94492a6c509d936e6e678b19ce25fd5aad8a9f9dcb301dca4dd5e47a124900d4ba230002188ffd37f98c29cb0e2f7bd055376b4df01380a76e5ca5017b8b97d887490f494780633d00defdac3346cbce89045a83660e56d2e8a6d8b47211642faca1883a83c23045124d490d982c1218dea8f05e340f95ca48916da15d07fa5f923c3da0c890e36b26c55ffa2f8693485e5b4c67fc29b3be5768259f9af7c17eca51c8ca09d65fa1fb21db1f7c5a8c545b1cf8f78e1c14a5648cd566753b73c05c7b55c1af4005899b442e53f08669ac88dfc0eefc2e5b96c76ae4f5459241fd870544832c2a4be2954852b47df941e647bf7f914e1ddd6f241b5123b5f87aa912ff5c35d8aa7d645b100f1d409338cd9280a0c65515e673654f8fb5de1da42fb7ea5fc5cf1f5afdc4591f7f22f759fd8a9ef709230bddb28064cb8fe3b9a8806b987b14d8e58aaa13c86396832da0d2c440ff95450d3be24a3d6c7c976bc5450ef350a98f129882f8b372c714efacf7a9382285d6046fd45168becff5d64fbf5bdc12073731dabf923bdcab04f43de20c8e95fb66","public_inputs_hex":"0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000fc4a275557be3acc7e37f1dd383752c0000000000000000000000000000000009800ccaf777e6cc629fb0e8d8ad1652511521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a"},"decryption_aggregator":{"proof_hex":"0x00000000000000000000000000000000000000000000000416c9296e38cf3689000000000000000000000000000000000000000000000007abf85d3a9f30c50400000000000000000000000000000000000000000000000237b411162b313eba000000000000000000000000000000000000000000000000000156b66ae45ebb00000000000000000000000000000000000000000000000fb163c45b929b1c0800000000000000000000000000000000000000000000000302879a33ee70e101000000000000000000000000000000000000000000000007d3fb89f63848af0800000000000000000000000000000000000000000000000000001191ba15fbd500000000000000000000000000000000000000000000000266d108d2bd6f116300000000000000000000000000000000000000000000000044a3d48dd17dc3d200000000000000000000000000000000000000000000000bc7f72ab82ba9582d00000000000000000000000000000000000000000000000000010d1bf42c416a0000000000000000000000000000000000000000000000021bab3c5cfd31c3d500000000000000000000000000000000000000000000000d01d612334779aeab0000000000000000000000000000000000000000000000090f4390fb5773215b0000000000000000000000000000000000000000000000000002561ec9fb68bf1e61c54fb309fe860b2a599e2c1339c9a0c1d4d4d7c19eb9e1925ecc353851c21f29faa0b2c9e2dce7b0ff6430e42bcab396059f4a009b643e80bb07dfd986a22c753cdc1297ee81add827710dcdce1b3905d8d137d45de8c4b58bb725680e4316c6900efe1d29c325ba03cc43ec30b8df8f7c8cef5da35935b202319ea75c872438635f5b3eebc558a54a7faacc657de20dda2064d0177e5ed3ee3ecd9f7ce014be0008a5b22bf423fb02ded702d843cb50b96102f67478344d3736244b75a9042116c4e0c0968f6fedabaad0ccf9ea1be6d1b6a2b42b566cf78603c24d65f315da1957f66178602e15a9baa053810b896567ba5fbc53b50b48a44400965116157c7f19bd181c7b184650af1f6df0687c64c2b54927fa663a224c6979cbb5a7274367923451bec6afa2d84276037f1dca6505e3ea5f37b5218d7e270ff9cddc001cc131923fe1c5c0cb360571bbde6d7670a3d91fad79d20d130044d4bab7e51ac2a0199f2bb567f4aa9fdae3b8634f70a6d47d4a8ec6829176f075caf523470eef2d4270dd12ee163794ae40acc907a4033b8427230e0a3080bbf4208bedc2169c5e1cf9f47ce448b3c89989c8ad99bd8726360fc259b1c35041205dd88b56028a4398df5cf93caadad64d1962b80ff243973bc4bf4a89700552f80d1e293c25c339bdd767fe4d0b7ee2d9b6c24f5bf65ea3a6e265f9c682b4f1b1a393f98c2d4017693a6eefa1e776ff4db63e9054253ba54e0f6c92613a8e3b55849035c606f4433589d1e140795dfd40198ef021ad53951f2dd41624bf7cc92e59783f3a29d9f6eca1bf91f470e595ece8a85c451e45a50cbacf2aedd775be0fdd413d44301d5db28a25fc8412e2a28e4e33bd176b54eee796bc300cd3d536f889712e1a0aad307d819cedd13a8abbed236376ad97d5acde39e0c8b0dc4d764ee788a0fd027b38b4bc46b09a7da5ad7f8af689326fe9c126a33a8123e47aaed4d5e877aa00d9708ee83d5b9fa03b2e24aa4245fc0d7e9250fae7191b292ede39b6180ad423b152d3cc3db2aed13f19256c56af480663ce8296ca90c2d85c2d694b51e6e20a4c03571d895ac7cf59e35392dc3908895026785cb42194d0559b3242912ea62adf77af10cd491f81184ad570951e233a8648246f3ffbcbdeb4e0f9307df9f824bc8a5dd0cfea8a335721305684c25bed635cfbac10e99964369e01ba3da0fa0df8138a289bef477027be8cb063844e59ff5567c05369b3d7fbc2d13656fee81b95251566f5aecb02dc9365cb51d522c3e368e3753313a9773206e10493eda00a40cebdc64ab5f42e68383442090734101028d4a3b3cc57c973529f6044eb1b1f09ed3188252c949bbccf14141f50b9c0d25f47019e1c0bfcdc01c9291ab7702315d4e4585011faf49227fabdfec64cc580697ea62d342a6d27227dd5a91a5b2a29035ec3e66629a2c99358f95d94ec98b50ef1b5bb59ee704b5470280668351ceeed6dfc8ba9c4627663f1a4b363425195d7bade1870885f1c07a8a2f8e82029d24fc02584248d25570148345a07fad19c756a1bddc07a8e0c3e0f72286441114933efd521c51bea3f3cc320adb2deac91b89e6fd016363d0b7fb6e2d10d4810e91cb4e2713ac83a62d28cb182779fd18be9f3766c7303ecff585f1c0bfd9503eeff54d606f92e1f978c61adb70cf59a3c0790d6c8184b688b47d10f28c5db24fc5858dde57e4fd4316c29873ff0f683c46c50af520b86b6bafc2505fabcaa0292ec8178651acd6b90460e6d65060be11d41ca74111a3bfbdc9b1d99cf7b3b221aa8a5f4789dbe9b007c91120d20e499db36f7abd83f02a1482d752f995987147cdb505bcf5b9968e82a0705a22f689f00bcb5a30ccc8fe0b25a99c0a22a6c27791efd79fe58fffcf59fa1aa0a7d8ca1a8e84fba4698840c6df2e048272a3528637ef304377cadacc8a86df22dcca01c4a63889492f030c519472e1f869ca329e91c181f32cf54a5bca551356b9c4d0c60e538977356b9622d597db2e97fb313fcc82f881eae7053f7aa4bd0edc0686b34acb24bbb90ba6757c84c3b6730ef27bf0f198b9488a471da7334a60d80558b08ced4e9c1b01f79f2358160268c9c1670d5334f325833ba1db22411592c14af3e83369d03ae3da83f8c0e4c9fd3620c897530e597293444e28daa1055f7d6f89f6ece42b1d9973c3f0c897dbfcf96160fa66e6c9c8bed3143148d883ff4fa31c3d6fe1ae6ec6e8b5ac3b840455595109d6b068a85ded28955858559d700697ffcc401df847df334026c21cb932da1072ad661a6469e211b76e5e7eb160581e3b296b2b5d771babaaf6508a29c034817feb2af6bf325448de4c7251b9e24a02dc474c923986346e30980758eb0cf2c19b53befbed9bdbdd43bbbd73b2f04cdbad27b9ebd4df23ed8955052b9a8f6fd1c32bff4a07aff3e1e56df730f2e2fc216b4f9fffdb5f6d53318b4987c68def42c021095a60702d3997ce3eeffeca5543841ae0b4a55349381d1222a2d6632b4244ff2a5766dd407d0cdb88beaf70bdef8c46ca2dccb6cfee6f5b90a61dd965b0186fcf84374a039cabcb975e7d50ebd8982c5a2474c5db21a6fa262cdb79a672220b8fa06bdf4aa029113eef5e332003ce5f03ef7618230c3774ed61c0482860128f589977a08a12e68b2743a80badf484f63bbeb5fb9141c25aeba24bc878a2741422ff3fed8720c2eb136d8d540db48d4428fd9b59150168bc623b40f22bf213fc3d76986485ba4f955af7475afdaff9f1a12c63f2baa6aa7ddd40cced56201d8a974e433e042073171389edb7a7b5e946148cba308dd80a02e78a2ad3e23019ef96b61d3dd0123805d495b486d76d0b9f3d8a48f1310ee4c733bcff3191500cfff383f27955e4ed578f043245bbccde47fd234312e389039a1b5bcf90b0d194297260a62a1ac783a496200362453eacf59e3be3dba243c1e33541a51aab521df84868549940a5b7e545b12a4c1fa0ce838bfc5244ac69e8b2834c7ba509f2801c3e1c0d77b6ab4c4f13a5e459ae4720f3aaa4e5bc98de8236f2a61188ec12326b66e96fbb825b539b41db33e51e53a6aca36b68bac613b91ab1b657d99f2187e9e99857159b991e1878086e6b931d24a8b5c391f1194461942c0ce522d8428dd1095799c7732b2af1df9ada0adecf15a46454aac9374abae6fc565e7aed60db2b57e5a24b226a0484bcf343aea6affba8c8997f19131a083d7da09a5bfaf165988bad45dfcb328512a710823a8a1743dfbec870e83e2352929f624372b97064e9d0dd64209c66dfd55ba091268d173e98baf277f969456077d749ba215a11417fddfbb5e3f7b410bc1350064abc3c0cc74470c845869eb2d57705cb5f11c21b11c833089928354ac0773b5bc6fafa12d4e9ba089acd82035f8a57b5e7d262829fce2cfa4e37be9fc640b5205689609d6ad185c87cf437727cc12b204fb310f676fcfee6c78ee2f3637333626d227af0d92d7912ff4dbe346b094b32bf36108066c5a3b416577bf7d266bbec4812447cabdadfc2e1660ee003e241449709f0196177629bd7a488502404a03c9f48199bc0d241b8f6ce8cbd8da78fce4eac72d68606740bd2a582d2f784d3cb38c610ff9c13bfe676485e7d02062273aad9a0d3aac2f2f3017e6d248adb5ac397957e3bbd8d1ce038afe1ea4b5368f73ea7907fb6239d7054a16a6139c7c9e9a44e66ce18c15d31ba66b18b5b7ab4d399f4706c5d0479decf2b5da336e6d2945c5c8317d9060ba8df6fa55a78def98656728083b6fea725a1e47cc8e7be08a999f1ab21d528816758209157ab26af22d787f196820105c4640ee968e4d67efdc9a46831d6b9623783013ea8361076998a3902fa23d94b1eaf9fd2f4676328df7a1b2081a69ee6b0d2ec1cd8437cfcfe5dcd411102263ba18952e56d9c59d047986456fb27170726bf7d605dc9c784eeed06d0d59a79ec52c11a1425f1aba6149c77a9309e8838992a803b339c6bf84db99de2e9e4148c3c43b740f6b456920ab24f7c0f9f820802051f900e30d3fd65e2ac91b1a57199f8c82c5ed3c86433a63b8cfbbb9745a5335cbae916502221a2789522a06ce883be2fdcac598cd70549e8c9f4927e327b836a98993f5c0352784ab4721bb59a473265104f1443983ba0c1f39583416ffdd1d897cc07d525218bd4200248199c942d674c166ddcc723dec117f0189752a57e4c763a2521f849285d49c0ca1d57d0bab91a530614e9a27da6aeacb64e5afc8878ba1c514d8579b1b2e762a859538f5ec6ea88523485a6e209cf838b4bf1225e681b8618809bec3778e832414d4c62f30678651e1d582a448252a16039e7ef6432892c0736a508c25ec9521de8dbb5d21b94f9d3f7b8520bdcbf31608a972f58ccb140a0f08bf0f3ef76b2f615185f8187636d59208375e12a170ded06c8f4d95db7e5a384c26312af709116fbc8fa440f4770f9131ba7882138ce3395fee9afb912050f9735442c7873311d08205fb16fc22d5fee5654380ddb1d88156a4f652f7aad1b5e23f9bef20b8223b417fed4192d84787d35e607239d822e6d16da4a2bac6f06d21267fde3bec0dafc9f0651e8abbfae8f450051d56071f197afae930935305da4d9dbf63ea1c0f62368f70f8a15866c9150ca5de551b11148581b7dbb3ac3a91104d89e4f4bc125e5a505925cadd64a4d7c104c6fe4792895f26b040c1cc46273327d06eda7c2d3de1f00f8dfcf6f911397b3b2c20c96d905efe0204cbba45adc56fd4ada7f8169ecd4306444313c846aa1d6e7881acc2bb3c32a9dd04bac0cb7043d97e45380502b10e7ee88623edb4ea6ac4aec8de66cff047b3a0b8de7e7030100434f5c11cbb97f6700728797787cad7111709a20288c667d7d4e08e095a40ad9b05be6e0552b75ed7fbd61c8dbe1165f2bd9a0e119b596999e49d83be09dbefb7e529a909c76b5258e8aab2f9b0e9e060a0e5b502f31647fd562b198e7a4aa3fce1b6332e35a4bc64f6d28fb7be4acd22efd02880227c2bb7de981afe547306c8a69f3f1cb20eb9fb9669531084d45b0149539882d12da0394612b2283947a289e615f113772c23cf3f00e8dd3b33e5fa8f024c53b3267b463981f72ce19e96a80dd6e61ada2e13c3f530742cfd36835e8a97c9a506cb699e0f86df3af34c52f4d3b1091213e38fb599a06c87eebc1ba26ba8d888df60d65a4a3554f4a061a9c63d369926e680d16f1db522fc33a0fbbf03153c6e6cb4058a11c4615ee010e70c379b71012acb47a537212ddc7fb695b37befbef71eab971ac03ae4cf7939a7c754e80208c03b9ae2f8efa57d6b4bf62e2ab53bff941b81ab2004d4214c8d1c3e41acd406c7f3f77d48a84e3c60228d1a839558a6c5b040155a3afcad38b03a89e03510128eb4e9892ab0b92c03ba8d4b43b964caba607a7d376bebc5bbe59cfcc543611f71dfdb56ad567e3ab4868bdc28f681e1284d267fee5eacc7938a2fb15539342bf1d9de71f15ceaeee10e13f488ab8e3a45d54c7abd84723bc141f90f1576e805498646c59515ae476fba76baf7609653089dd1b80dea0cc074c25794f48124100a10a5a3953da1e19403d981dccd265f4bdd93dfeb0a3b65f7dbf3a8b06a0a04e1ebe52ece119625511b8d01969c973f33de7b4b3fb6e2022e30de838c07dd12002f5b2ecc6538fb4c520a8f2e7baea4fefb8c5cf1ef2fafb00f0d19c6580c1b5a9c9f9deee13a3e889e50facb2f4538c1c43d325810643c44b1ec7f5526bd029e6c6b1d9a45bc6684109670fc6e0c9f8b1f3feb00fee3a61169e9a83c9dd42be2c6001cf3592b5c2932f494ed9a83bad49f60473d7612ed60a635d3e953d0028e993169bec778afb691c637b0cae6aba585d2cb8905481bd3eb5bf6de1da71a308aa629517e68450eaa8e7ea91580bbbeb30b7998ee27568ebd6145b6e70c20161bdf2491f545c1dcfa7303f7705b6628a1094faf98a5f1a36c6456e074a10401c234e52c9961b76f238172189d91befd0f7ebcd9d79039e29626f293d009100affd6d2254e10219e9b9566f8a923167933d37e586ee3f614a5012d69a00c00cd99154a20de6f0f04d9f32be106015e53d2c23d60fd9da49730d5ce3837402a375894d8f12d1b34e3995b154874c873f9a3037d09783b1b27ca9306bf2c3308dc4c73e1b95df94f830d9b250fc45d5e2370dfa351cbb79791e1c2d94dffbf13d328b9f475e53fda0f9b44c28ea21108032367e8edfef64f4c4e977b1c47a205869eaafa9c978e967a3cac32e4aae0aa895fe2cc67a6ed720d1169b06264e92dfbc6a99923c8e6fed8f6a951c771e259892ecc638a57264a7351b8e94e26d4185960e8ef13c4be85419936c73a289be253b2bda37d06148e481b495152e8ec1e0a7d1fdbd3a0d7de9891fcbd88f215cbcde85b4563f8993450bb2fb117443627b76e42ca03ed9c2718c877c640e216665f316867b5d1517328f980fdab9c0b1cad6ffe013a43c2fda4ced6d18d4a900d42a6397ff6cf7aa04665b6430ef14b04cf5f0b8e827810f4b9e628df101f9cc07b7f9d21aa602466ef25cac111f75b09c0ed411b1e0a0f442764955adc807d2292c032052d814ffb11fe2c4f25f54d26089052c2abc21152d0c63457259245e5dc695b2c3633eabdb6bb64ee7d1f652e5651678b410b12f0e7060b8e744416318503d6545ba957f5592047ca89f52b0ff2a856af7f6ec8051056bf31941000957a5d7e8a1b90f6758abb559a8582e726e70d762a07770a22949a32efad63c88fce8ab69e7ecfc221aa52fd689afca502f80afe8af67e02f63ea1dae245ae6df1691fc2922e963dffb4b40969a2afc401e1bc978955a901660fbc7d320019a8e81c76fc5e7e0719a37eb099913441151a6250590a660df462c08f119d70e9089e5acdb163526fd9d16f3d51d7ad6c612229709e6d3b097463069ade52da3f6e91a6c09d975847a351261525b3ed8b352e7c11480d1b743bf34c1c314e8a2143c012200bc4ccf66adc4cb8ecd828971e1065589ecdcf966626c728a71a42830941c96a4490ad3a3f081750168fead13327718d11cf276a90ba8e97d38e258e68e543300d01be6f2b06c51cc2b37489910233c2b17cc751da4d859d9d4bffd7a3cb89d6e8ecef481ee10e3ea70c3a50340bcc3646db58bd841b4439c17bd7116250020f3e9d0bcae0d122f013145b049309d0b89a89b0b5542a6c071dd247117f7a9c11d92a5813ab2234bd046e5f35682dbbcc16fe1f0d9b1d0fc7947ee0fd7d0fa3f41aaa13e7feb5ac3758b09b43a71d39360b515dd22150b18dcd0f4161ffd6ce130fdf34a1f21af7dadc0b3477492c58230829031e24bbd5947512c07ecbac8974dda0968a88ec196ea2a32988e41960210a0fcbe12fe59cfc251aee2a958577820683d2474c03530b5c3af41daf11471a0ba1ecf08b931a8b592a4341124146f7eea929b5bcdf97f4309388e4832e8acf205614a839dd4c803065ada539c038efa3793fd5d817a5d37150ac380c2688e1818bd1cd9402e785e1ec0bf8f70c0205df96db354c21c841dc5798019f150636540bc651792c2074eb47e8f6b8c8f4a1c18d43da452614900f830f4f6b1dd3cba0b6cdffdf86d4465254183f3b3037e25e5341956ae5bbb3e5b69764722fbccb0f85e82ddf9ac3e1b6c0e3765db51e570310681c3500d64f65fc81879c0755923b6c99e531545dcc3bd316065c7da8959f0adef1052b4371510dce386b17a9bad3a8a4e29ff2ba56067a8b4fe001c8a17667bd3776764b957eb097479e0c0b11340e650a8c5ca4a2fdaaac9f751fdc22b5ad7665a23856456a267cb44f0e0019ad5b49a2017bdf0771f62e195722f18270c350dbac52a3a0d303d9309821aca0436a16f4a3c4ca41dc150249bdd0364f2c89ab3c895b0004c6d626b566124a30f423f8c8b635d0919fdce2053e0414360f57280cd37737b540e65e2d99041a698a36f3ab376d2fde74928c547cc481f89a588176a7509bae404405fe041e6089219e12b9f770c516245400171796d9380cd2985e7eb7cdaa8cb473a3f4074552579d80c0699366babab60854d2c33ac00b5160891b6372947f93e910ea1348213ea777f50a05a88fdbd4b2494a7d16a2d5f6ea737e75ecbc7204b9864f192fe5523992d60c8d84d1b6eba8aeee8014743fba88fcee19d349c13d3d37242e3ddad8d0f0eece179bcd5382678b95d153b45a6172059f81d391c8bbbb815c1d82c2b71be144549c02e0f170ec5a8b1b86d844996b7a89320b61611d12e3cb00775bad99ee32f45f565cfe39d8c90333241b8aeb8c33596316a9b9d652b1ec2d5e8e9748cfe75a2049627dab27a6734e98f15902aa1aa3cd82d3a762fdcb02200ed6da0e18cf3f8afcca5bed31a93dfb79795183d3cb7d1032fd7da336aee52867f3c4d1fbe72a72ab839b3864acdb59b20ac38a1a267f62e4dcf71924a2cf035af3ac007525f75d53985c1ebef4201300f1052dbc5d090342d2ef29b839251024bae62c29cb468ead61bfdad488c919537f50e85fe271c25d7d7d7b550aea0a018522ca71bda2137e14a43499e6da8e8fc21aca461520818a0ed89bec8d8a280eb898c57d587192dbdf7c155004fabce811991a165fadbc3175a4ea707b2829bde1a0ee2ea0a10fc1fd8b041bd6a10ec6ed6549761499d74b7d9fd1e43e9b1642c7004a01c8dad4c27982cb87a76a7cdd92324a1c4a03338e0fb63bc6aee71e0f7ef06ba08da79fc1af81885746f981a1b1ccb86849bd93dc4946da8194560ee663dc8abfb3b4f401e97d4868baeb3cc9e02380dd317eba44b77585d03ad818eaaacb65f3696055f484c63bac79187a5569731e961f428906f70a9df8da601171826be0373379ee40fb7946317f2add3ba941641bc08e1c9e76500ed274c72fd2346a922c9ed939bed51203e6e8434334c8430851bcc79269e800eb376f9d05571558d8ebd306a3f43867406dfec2834d7e60492d786f022ac77e5fe38a4e1f3bcc526e694028e64972abf52a540e5fa9c3657b255b16443b0f94cf1f88450627b6abc191058fa16cee748550de84eabb8051a54cbde1478d527b01fec7d722847c86788c1e29add90ff796f2a4b430c50ac6724b1f50fbe0678f7f63ea8d08ba24f780459ee2118ffe09e17617a9a875f5fa866244a1e0a17929f2d4b67502b26decc17b0a6833b9e1fc9d6aa1014410dacf458da78fdd1cd87aeff21e0001a33b055d9887c58a323281ca2d6f08372098564035043daaadfe13ed795c100fbcfb85ee33450f020afe5c6701108419af3ab19dc36b0136a4e09638cbad8618cf10d5da4bb473b20e2d4f869f6abd9fdbb3125899efcf8145c6dbc8eaa5782dd735114427a7368894596336986dbfc6f83dcf2abfd02dc49e5096425bacaa0f4fefe353c5aed743e4f7de346ded390cdcf6cfa6866b2e26e307463f5e54142266af3ecbae5dfc0f6dc6ae9c95fe0b53369b7fe4d333ac323866d5577a41ea2c6d2a0983c01fa88c910a18d05f4b2ccc64617912fb052f68ec8f732e1c4ff81494d7b3ada40bac069be1534e46036e4fa15b67d82e35b6c4c537cc8d69babc03aae75362394a6489321b24e0767b05b683c261858ceb4252792911c06b625b0a1733c96181dcea552b33390c474d94fba7f60c1e79dd8630594b0e85eeff922521e5e26916a8d161d83d01faa227f2a9d6444fef04c87f4dcaa6039e1777692a97adbeb62a33eb4089559d7cb7dccb4f04431881d57d6d895aae0495f4327e2e4d202b13b3e055f00130e17d7312f1057000096cb0873f6f4e75cc094a879117c3b3c7a747f460fbb0783f83b3b0854477e023042ca514e68f4de8ce774d1626e6ad5c9f8602fe6c6b5fd8b31137dd1233ef55f086f28918764c2e9d456f212fb0444788ce44fd4352e3a05ac3fd55d1da6c5d5242c206256f663ede364e6e2373732b6b18f7611b1e4b63ad4c976c1deadf391b5da979c5b02b25d5ece40228affd372dd8a5500634ea7f14730689049209d5b5d28ed1af43b6a02339af83197d38d1f6964f7ebbeb83383a01410d59e08e3753b29980e6b536ae638590652a7b9a117822013f4bcdaea47c121aba0f791728da022a30d4aada1d57f8c623068f6b97acdf1888e3c9222e48ff811cb06afe6817952a5f84e27bd6ae7e6eb4063954f2271e0d8ed25f91193aa8f92e4a1a8c16fecad358545445169a129aef0d65d2d7d185b145537bc3b2785e0d7bc7ea6aa9309a2d0a00385348c250b3b614b1fbb7aae00ee336a9c40c652b647742a3607a56be6b253cd9152e8afeb67613bb31e1b89554b30c9ddb90be8fd9f795054412260ba7f17a74233ba263c9311c487638106e346d6570371469fd0bfd06b4db971cab7d4ef0990f3af631d7a41b9dffe4983c4c2e984d47ee3327e25e0e1bb4fd786160c4b34c846a56b76bf91c8277ea0a4cf36eb760488a05cc2aa70bcee215bd2d93510b35fc4eed1f1b4915e88f5976f5793046aba0a00dbccd7f3a316beab9510be08c9f05e784540a9e232fe8b33d7ec5ed4a277e7b0c17e27239a133d82cfe4dd3adad6816773967d50b2b89728d1190f312e5f10e8fc7d3ee8218ead95eecc465d5083cb6849c4e6c2c2d359afba7a0809dced78cd7163d88caf325866d1ebf80dba614817c3c47ca0594cbf43b6961415ad022c67b6e16820cca393368b51f821687ace8ab744039174d28be0ce2c8d9b07bbdd2972d52c9bfe6f26f55ddc67f015df08a2bdafd7512a69698ececec58664bbdb7c7389052eddaed595852df40e2666659e661a4972176250fff62ca3380e7f0e2fe8b22b2d710d76e6fcd4ffb0f7e8c9ac6c1127a2a83a5c61ab4c3749440a8f664d7d31bfd430dd80ed0cd48b0a70d592add19760701d3a991136bcb410559e27783a49625e8988733857cf2629c9197d174a94007003e3884b2dcbf2eadcafa5dd6f4c0795d41f78535c9e78424fd46ddb62e50251cec8360443fe70e56dda0d20550006d6fb8144d28755b55e83dda5a22dd8522df0d8beee822666e8eb9e52c43c3fbd074c41ca47c3bacd979ccfb528b2f2b0882f525851801b114af0f703235ca9cf8f9c9e50b2b5224430225b371379e7a1c7239a50c48885d57d708f217488080bd1ecb6d58f5205665fd593c9e0bcddf23b2f4eb1ef9301694a34e8738179e8e1369a60fc3fa11ed46407606363f52cd24ee26f4d3910611f69283f272f9ccc2844c3411baa34e8da7364dae8a75684000f0b2f9f04ff420e60d1d353ac9d13e7faafa17000ae8014df2fbf7d12c3d7817223f92516a4a8bb4f0e93ae2be4207956f7fe6c126daea596e4821329737b12f0e82c3f01cdb5915dcb711359d725ca71d8db88a20194032ee174e0296031b2f696b4545925563e1d1517e2c04b20f117d21ab1f518da2275af79d25e8d41607b2f5e9c795c05bd09e8e258b198a9b8d59b9abc7b6f80225c58cfeede76fb3278d3c307fac2e86cfb0dacebffb44e0c6dd9a82c44b6a09e480a03db77457542526566aaf260a14c32b6116575b481d6207aca05141d8d2a9d491cb9a1a99162696ec670c96835be0dbb7acd8345d7f58bde2465c9411c61efa95cf126e0c660a6cd0352efca0376e792a21ffb8bc5c5c4efda9db0450bb0ace4d4c9fa128b4297c7114a11be0b0e120b51308938e35bda2a3883517ae4eaa1d40c1b06f159e07d21576c33046501b0e5fcdcabfce665b833125b78d505ca673246352dd27fd1485ef6fb7528e432f977ed5ec10b0b786a819607c7750295556208206f2578925accd4fc26cab678389452aab1b5c737641fd584576f8b5eec4855e12d9ca56215db2dad99e19a7bfb18537fac87fd6e2afd076a160426081c063d883cf07f20a0721161fb081ff18cf9984812240c76a5ff5978697bd7ca060816322c90ad80126347b928a7ee34d2fa4331c9964d45b20e4dd03bfccef79a30ad07bc86b641d7ecde5d7184ccad6aaa66d296fefc304910def84095cb11e85d41fa5aa6b6e164350ec9827f1da24730efcd821b084047abd9dd6c6926526d5ae954086b0c510324f8d63c5d5cf2c7a0ec72078e8935b864d4174b1d65c9e56916404d56e472ada136e932d27cd277edc82d035efc8f117cff743cfd0988b3dd87e8c4af85425b0f856933d7f16194b39760fec8c7ceda5bfc1954cb224bb267af6ca4e2a1e0aee11946cb19fb8bb526017499058df0bfa6e932419e9114c7fa3504bae3aa52a34fba2cd6e0d3e3b57aef491f62d5cbfa2ca81503d2432eb3718df6baddad1042cc2b16fa503487386d885f97a0165000acbcfe60914f7f378bd732ac1632e2d3c231c0dc6f4f73190481327e93a6ea070953061c590c61cac490e057ed68d1041c85c671f90f92b4adf967b23d4b3bd487d88f2d20ddf0892d88fe69e7a732440efc7fcaf1bfe1c4447b2903475b6924ea834909b586dd15210e517abde7302648b539b2d3205bcca3c5e04320030fbed6bd23a791adbae7b600d61cfe58d0ecf9b32496dd8b49a19bc92e151bf569e3cdb51f522c92ad5fbcfb3825b2b4f088764f0347e072898b819fa4b7c650c6bf5bd0e3fa673ce756a382a7d7dd254265f60ed152b172b49ef4a87ec2cbeb0359785bfb85c5cf704640a8d891caef112dde0c9526f702286bf5ca74291dc3d23150254ea8fa8d7153ac3e5992750591938e09df761c13c4b4aa1921e072bfe257472220140d5ebdaa8cae7f560e9f714c95c52b4894db5df6c247c94baf38f12096da6808999fd18aa6670c88065e80226628c983330269161ac7017ffc0a4ffd32a3327d9a7cfe8db6a38ffe148801c6fde9870699e806e07e05aed0ad6e4ae588914c12cc74e63b4f4d30552d5fe051a876daef9218fc92516085c9864b930e810f0b61bfbc9a395b47da39e565817a2fca920d257e9ff9bf3eee1912d6c229cb578f5b0a15337fd914f134119c5153ae19ad490edcc124686d9fc1788c029d6cbb231dab0867395a3ced52cf5992b7aaa6d12151bb3ae8c3473238e72dc9fe1c255632cbcd1fc9481c38e4b3f9421b2aa75e8be1e3298246d5dfb689746af68ca58b6286b688406bb0bafc94c66286f9f12af684c6ebd555ffff7db08ecf3bd2fa20500b05ab8968351747ca14a0c9d64e6beedca294c73ce5617f6978c9b24e519125c94ba09c7ed874a80b7620feab3b353605c010f4b857897d7e39e3a6fbc840c848b4cb68aad5f3479611f2004a749cfe35a02ac5dc89b5393fe30f7c2afdc837b159c7a2ff06e596519c7116760cb3b73de1361f18d384c55c9e3540321a68c4e2e44e5c941b9f665059a1e008a16dce409193ed8cd54ec0b06a24ab4ae94fd5897ed4c4cdc5d1d3dc0b826c0b6ca7c81d0d98b62d261143a762da94b02604b811e3e7c53f79396d23a5b19f8794ad7850f1da3c9462a0716a77fb87fbe85d34c610c65e5a93921bce8160c2049a123f1e6f9357851a2900dec44340ab20ced52a80cd6e5ed17b9dc19471e2ae29c6cf98c7bec7d33c1cc38f39128f019367465a12455f773fd0ad443e0230482f9c17e3187b3ee26ac397edcddeee325f4dc38f7003776bd3f29645ce016d755ce4eaf4730ba5bea43cace5fc4f491b75bf4f40a1ef5d6880f57006f2e0ff39ff9153f842dbbe9239d0c5f12fa491325697746afbeb24c3fe8248ce5170bfa24f3eea9fc6bd7569cf327cc1dabca0c6d9334e669a0bc6fd382665a7bc60d5921f4c5b37845e303ad5f87cc337055eead375da43220395f7f258e2d617a028900a614996a6cc5e37c72feb25276f20428853d95e38dacb125fe86cd90d52c69c68c578991bf0a52e63bb34af47081cf8a6bac755d8a48593965adc95aea177dfc2a3e04caaec905d3c695a91784eca3b126da5e9c866d91c683b6cb3912124fc0d5d6688f89d4ed73b54d5f3e34face890f3958f7f8cb41dece502429942f9901794c73a7c09cf36212a51fd0782cc06fe30ee79f82d5484f23733f6c13233f93f7016dad4e73fff5bca533ac101209ee573f1cce3ff1fee1c719056c5e2ae2322639c3928eed49f97f1f1b48e503958593f036366eb21e23ce36b7214a1f59eae5e142031921a7723ce84d0724174f2ff35cadc02308f5fd66a0d59fab2fc15cd83713002d3fd0a2476d38b7851818a1b01b49ea13b0759ba2508e928807cb3903616113ef1e11f6ca8ab55043807f94c991add5aab254af2f42fc3ddd0cec41674123652d4628b6d3fcb5b051dd82eda1c68d133c33ba1bbd22a595c714a3ec3875595951d46b252c14e88841e72a69079ea1e1a0ac9cdb4d41e799c61a6d52ca385bcb12df3bc6bd2573d385559eb994f25c61406fa247a311066b9b2d6da3eaf9a50dc2d39ca4ea36890d8716745651c84cdece99eb27167760ee320fa5003bb3d07f7bacdd3251bb18c32693721a6558f007fb7831c0a7f1d8f0a7230909ae06b2c389fe18276a448db3414e3bbcaffa39a00de317b42e1d1be8fb0b3426ed4de416d966414a95a64f0006ccc9b753425580ec9c2c84f45dc83ad90471d2c0fd4fe6d06a3a279e72b673320bba822138c7b151d727b6abd414a88111fe52b8b0917acb88c22681ba264f140ab7b2cf227252babd5e83e3fee517361317646fad64650f2d6fc29e552093d4791db40cb5752ea44f8f1f42cb550c90","public_inputs_hex":"0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000154d2d8b9b5e779fd4a14022ac64137900000000000000000000000000000000fcc98eb31a0ea5ce49a4df5434c3e52d01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}}}, "test_exit_code": { "crisp": 0, "folded_export": 0, diff --git a/circuits/benchmarks/results_insecure/integration_summary.json b/circuits/benchmarks/results_insecure/integration_summary.json index fd27b1262..9ebe7317c 100644 --- a/circuits/benchmarks/results_insecure/integration_summary.json +++ b/circuits/benchmarks/results_insecure/integration_summary.json @@ -68,9 +68,9 @@ }, { "name": "ZkPkAggregation", - "avg_seconds": 2.150821000, + "avg_seconds": 2.150821, "runs": 1, - "total_seconds": 2.150821000 + "total_seconds": 2.150821 }, { "name": "ZkPkBfv", @@ -119,7 +119,7 @@ "timings_seconds": [ { "label": "Starting trbfv actor test", - "seconds": 0E-9 + "seconds": 0e-9 }, { "label": "Setup completed", @@ -127,7 +127,7 @@ }, { "label": "Committee Setup Completed", - "seconds": 20.239957500 + "seconds": 20.2399575 }, { "label": "Committee Finalization Complete", diff --git a/circuits/benchmarks/results_insecure/report.md b/circuits/benchmarks/results_insecure/report.md index 6556c95da..e188a9da2 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-19 20:29:11 UTC +**Generated:** 2026-05-21 08:04:33 UTC **Git Branch:** `feat/1525` -**Git Commit:** `987d67381fd3f10f38a19515d0ad4f7457b1fef5` +**Git Commit:** `6e6e3d396b3ddc7b5e9901bb66c404cc58dc06c1` **Committee Size:** `H=3`, `N=3`, `T=1` @@ -15,35 +15,35 @@ | Circuit | Constraints | Prove time (s) | Verify time (ms) | Proof size (KB) | | -------------------- | ----------- | -------------- | ---------------- | --------------- | -| C0 | 6847 | 0.12 | 27.78 | 15.88 | -| C1 | 57818 | 0.34 | 25.42 | 15.88 | -| C2a | 142625 | 0.77 | 25.63 | 15.88 | -| C2b | 198355 | 0.88 | 27.44 | 15.88 | -| C3a | 132633 | 0.79 | 24.87 | 15.88 | -| C3b | 132633 | 0.79 | 24.87 | 15.88 | -| C4a | 92515 | 0.51 | 26.42 | 15.88 | -| C4b | 92515 | 0.51 | 26.42 | 15.88 | -| C5 | 151717 | 0.83 | 29.00 | 15.88 | -| user_data_encryption | 53732 | 0.33 | 25.86 | 15.88 | -| C6 | 86927 | 0.55 | 27.51 | 15.88 | -| C7 | 104273 | 0.54 | 27.78 | 15.88 | +| C0 | 6847 | 0.12 | 28.68 | 15.88 | +| C1 | 57818 | 0.33 | 25.71 | 15.88 | +| C2a | 142625 | 0.75 | 24.48 | 15.88 | +| C2b | 198355 | 0.82 | 25.54 | 15.88 | +| C3a | 132633 | 0.80 | 25.02 | 15.88 | +| C3b | 132633 | 0.80 | 25.02 | 15.88 | +| C4a | 92515 | 0.48 | 24.45 | 15.88 | +| C4b | 92515 | 0.48 | 24.45 | 15.88 | +| C5 | 151717 | 0.79 | 24.83 | 15.88 | +| user_data_encryption | 53732 | 0.34 | 24.75 | 15.88 | +| C6 | 86927 | 0.50 | 24.94 | 15.88 | +| C7 | 104273 | 0.47 | 25.49 | 15.88 | ### Artifacts | Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | | -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 10.69 KB | 0.47 KB | 3042761 | 176256 | 3219017 | -| Π_user | 15.88 KB | 0.12 KB | 2973025 | 170272 | 3143297 | -| Π_dec | 10.69 KB | 3.47 KB | 3553819 | 187284 | 3741103 | +| Π_DKG | 10.69 KB | 0.47 KB | 3042369 | 176052 | 3218421 | +| Π_user | 15.88 KB | 0.12 KB | 2972905 | 170392 | 3143297 | +| Π_dec | 10.69 KB | 3.47 KB | 3553763 | 187368 | 3741131 | ### Role / Phase / Activity | Role | Phase | Activity | Prove time | Proof size | Bandwidth | | --------------- | ----- | -------------------------------- | ---------- | ---------- | --------- | | Each ciphernode | P1 | one-time DKG participation | 302.76 s | 127.00 KB | 128.19 KB | -| Aggregator | P2 | combine folds + C5 | 0.83 s | 10.69 KB | 11.16 KB | +| Aggregator | P2 | combine folds + C5 | 0.79 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.55 s | 15.88 KB | 16.00 KB | +| Each ciphernode | P4 | per computation output (C6) | 0.50 s | 15.88 KB | 16.00 KB | | Aggregator | P4 | per computation output (C7+fold) | 79.62 s | 10.69 KB | 14.16 KB | ## Integration test (`test_trbfv_actor`) diff --git a/circuits/benchmarks/results_secure/crisp_verify_gas.json b/circuits/benchmarks/results_secure/crisp_verify_gas.json index 17a0e7c57..143ef2620 100644 --- a/circuits/benchmarks/results_secure/crisp_verify_gas.json +++ b/circuits/benchmarks/results_secure/crisp_verify_gas.json @@ -27,7 +27,53 @@ "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.957074250,"runs":1,"total_seconds":1.957074250},{"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.902250750},{"name":"ZkThresholdShareDecryption","avg_seconds":251.230740403,"runs":3,"total_seconds":753.692221210},{"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.273315000},{"label":"Committee Setup Completed","seconds":20.279577333},{"label":"Committee Finalization Complete","seconds":0.007173125},{"label":"ThresholdShares -> PublicKeyAggregated","seconds":5158.129704250},{"label":"E3Request -> PublicKeyAggregated","seconds":5165.210807791},{"label":"Application CT Gen","seconds":7.707659625},{"label":"Running FHE Application","seconds":0.071059500},{"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, diff --git a/circuits/benchmarks/results_secure/integration_summary.json b/circuits/benchmarks/results_secure/integration_summary.json index 1d5dcc6fb..2c13fd863 100644 --- a/circuits/benchmarks/results_secure/integration_summary.json +++ b/circuits/benchmarks/results_secure/integration_summary.json @@ -20,9 +20,9 @@ }, { "name": "CalculateThresholdDecryption", - "avg_seconds": 1.957074250, + "avg_seconds": 1.95707425, "runs": 1, - "total_seconds": 1.957074250 + "total_seconds": 1.95707425 }, { "name": "GenEsiSss", @@ -94,13 +94,13 @@ "name": "ZkShareEncryption", "avg_seconds": 114.608395854, "runs": 36, - "total_seconds": 4125.902250750 + "total_seconds": 4125.90225075 }, { "name": "ZkThresholdShareDecryption", "avg_seconds": 251.230740403, "runs": 3, - "total_seconds": 753.692221210 + "total_seconds": 753.69222121 }, { "name": "ZkVerifyShareDecryptionProofs", @@ -119,11 +119,11 @@ "timings_seconds": [ { "label": "Starting trbfv actor test", - "seconds": 0E-9 + "seconds": 0e-9 }, { "label": "Setup completed", - "seconds": 3.273315000 + "seconds": 3.273315 }, { "label": "Committee Setup Completed", @@ -135,7 +135,7 @@ }, { "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 5158.129704250 + "seconds": 5158.12970425 }, { "label": "E3Request -> PublicKeyAggregated", @@ -147,7 +147,7 @@ }, { "label": "Running FHE Application", - "seconds": 0.071059500 + "seconds": 0.0710595 }, { "label": "Ciphertext published -> PlaintextAggregated", diff --git a/circuits/benchmarks/scripts/run_benchmarks.sh b/circuits/benchmarks/scripts/run_benchmarks.sh index 30f9626fe..3d56b7661 100755 --- a/circuits/benchmarks/scripts/run_benchmarks.sh +++ b/circuits/benchmarks/scripts/run_benchmarks.sh @@ -1,7 +1,7 @@ #!/bin/bash # run_benchmarks.sh - Main orchestration script for benchmarking circuits -# Usage: ./run_benchmarks.sh [--config ] [--mode insecure|secure] [--circuit ] [--skip-compile] [--clean] [--verbose] +# Usage: ./run_benchmarks.sh [--config ] [--mode insecure|secure] [--circuit ] [--skip-compile] [--bench-compile] [--clean] [--verbose] set -e @@ -11,8 +11,10 @@ CONFIG_FILE="${BENCHMARKS_DIR}/config.json" CLEAN_ARTIFACTS=false MODE_OVERRIDE="" SKIP_COMPILE=false +BENCH_COMPILE=false CIRCUIT_FILTER="" VERBOSE=false +PRESET_ARTIFACTS_READY=false # Parse arguments while [[ $# -gt 0 ]]; do @@ -37,6 +39,10 @@ while [[ $# -gt 0 ]]; do SKIP_COMPILE=true shift ;; + --bench-compile) + BENCH_COMPILE=true + shift + ;; --clean) CLEAN_ARTIFACTS=true shift @@ -47,7 +53,7 @@ while [[ $# -gt 0 ]]; do ;; *) echo "Unknown option: $1" - echo "Usage: $0 [--config ] [--mode insecure|secure] [--circuit ] [--skip-compile] [--clean] [--verbose]" + echo "Usage: $0 [--config ] [--mode insecure|secure] [--circuit ] [--skip-compile] [--bench-compile] [--clean] [--verbose]" exit 1 ;; esac @@ -125,6 +131,10 @@ if [ -n "$CIRCUIT_FILTER" ]; then fi if [ "$SKIP_COMPILE" = true ]; then echo " Skip Compilation: Yes (using existing artifacts)" +elif [ "$BENCH_COMPILE" = true ]; then + echo " Per-circuit compile: Yes (--bench-compile)" +elif [ "$PRESET_ARTIFACTS_READY" = true ]; then + echo " Per-circuit compile: No (preset artifacts ready; use --bench-compile to measure compile time)" fi if [ "$VERBOSE" = true ]; then echo " Verbose Logging: Yes" @@ -150,6 +160,9 @@ if [ "$SKIP_COMPILE" = false ]; then ENSURE_ARGS+=(--verbose) fi "${SCRIPT_DIR}/ensure_circuit_preset_built.sh" "${ENSURE_ARGS[@]}" + if "${SCRIPT_DIR}/check_circuit_preset_artifacts.sh" "$PRESET_NAME"; then + PRESET_ARTIFACTS_READY=true + fi echo "Preflight build complete." echo "" fi @@ -224,7 +237,7 @@ for CIRCUIT in $RUN_CIRCUITS; do # Run benchmark BENCHMARK_ARGS=("$CIRCUIT_PATH" "$ORACLE" "$OUTPUT_FILE" "$MODE") - if [ "$SKIP_COMPILE" = true ]; then + if [ "$BENCH_COMPILE" != true ] && { [ "$SKIP_COMPILE" = true ] || [ "$PRESET_ARTIFACTS_READY" = true ]; }; then BENCHMARK_ARGS+=("--skip-compile") fi "${SCRIPT_DIR}/benchmark_circuit.sh" "${BENCHMARK_ARGS[@]}" diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol index a50396e2b..579ffae4f 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol @@ -19,10 +19,19 @@ import { CommitteeHashLib } from "../../lib/CommitteeHashLib.sol"; * keccak256("fhe.rs:BFV"). The plaintext is exposed as the last * `MESSAGE_COEFFS_COUNT` public inputs, matching * `MAX_MSG_NON_ZERO_COEFFS` in the decryption_aggregator circuit. + * Constructor `threshold` must match the compiled circuit `T` + * (`lib::configs::default::T`). Committee hash limbs are always at + * indices 2 and 3; total public-input length is preset-dependent. */ contract BfvDecryptionVerifier is IDecryptionVerifier { /// @dev Message is always the last 100 public inputs (100 uint64 coeffs = 800 bytes plaintext). - uint256 constant MESSAGE_COEFFS_COUNT = 100; + uint256 internal constant MESSAGE_COEFFS_COUNT = 100; + + /// @dev `decryption_aggregator` return tail: `1 + 3*(T+1) + MESSAGE_COEFFS_COUNT` fields. + uint256 internal constant DEC_RETURN_PREFIX_LEN = 1; + + /// @dev `decryption_aggregator` return columns after the leading key hash (sk, esm, ct). + uint256 internal constant DEC_RETURN_COLUMN_COUNT = 3; /// @dev `publicInputs` index for `committee_hash_hi` (after sub-circuit key hashes). uint256 internal constant COMMITTEE_HASH_HI_IDX = 2; @@ -30,6 +39,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; @@ -42,8 +57,17 @@ contract BfvDecryptionVerifier is IDecryptionVerifier { constructor( address _circuitVerifier, bytes32 _expectedC6FoldKeyHash, - bytes32 _expectedC7KeyHash + bytes32 _expectedC7KeyHash, + uint256 _threshold ) { + require(_threshold > 0, "BfvDecryptionVerifier: threshold=0"); + threshold = _threshold; + expectedPublicInputsLen = + 4 + + DEC_RETURN_PREFIX_LEN + + (DEC_RETURN_COLUMN_COUNT * (_threshold + 1)) + + MESSAGE_COEFFS_COUNT; + circuitVerifier = ICircuitVerifier(_circuitVerifier); expectedC6FoldKeyHash = _expectedC6FoldKeyHash; expectedC7KeyHash = _expectedC7KeyHash; @@ -60,10 +84,7 @@ contract BfvDecryptionVerifier is IDecryptionVerifier { (bytes, bytes32[]) ); - if ( - publicInputs.length < - MESSAGE_COEFFS_COUNT + COMMITTEE_HASH_LO_IDX + 1 - ) { + if (publicInputs.length != expectedPublicInputsLen) { return false; } if (publicInputs[0] != expectedC6FoldKeyHash) { @@ -93,8 +114,8 @@ contract BfvDecryptionVerifier is IDecryptionVerifier { function _verifyPlaintextHash( bytes32[] memory publicInputs, bytes32 plaintextOutputHash - ) internal pure returns (bool) { - uint256 offset = publicInputs.length - MESSAGE_COEFFS_COUNT; + ) internal view returns (bool) { + uint256 offset = expectedPublicInputsLen - MESSAGE_COEFFS_COUNT; bytes memory plaintext = new bytes(MESSAGE_COEFFS_COUNT * 8); for (uint256 i = 0; i < MESSAGE_COEFFS_COUNT; i++) { uint64 coeff = uint64(uint256(publicInputs[offset + i])); diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol index d9872d0a9..40f5aac8b 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol @@ -17,24 +17,27 @@ import { CommitteeHashLib } from "../../lib/CommitteeHashLib.sol"; * and on-chain committee hash. * @dev Used when the Enclave is configured with encryptionSchemeId * keccak256("fhe.rs:BFV"). The aggregator circuit's last public input is - * the hash-based aggregated PK commitment. + * the hash-based aggregated PK commitment. Constructor `h` must match the + * compiled DkgAggregator honest-set size (`lib::configs::default::H`). */ contract BfvPkVerifier is IPkVerifier { - /// @dev Must match `lib::configs::default::H` (micro committee size). - uint256 internal constant H = 3; + /// @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; + + /// @notice Honest-set size `H` (`party_ids` length); must match the compiled DkgAggregator circuit. + uint256 public immutable h; /// @dev `publicInputs` index for `committee_hash_hi` (after `party_ids`). - uint256 internal constant COMMITTEE_HASH_HI_IDX = 2 + H; + uint256 internal immutable committeeHashHiIdx; - /// @dev `publicInputs` index for `committee_hash_lo` (after `party_ids`). - uint256 internal constant COMMITTEE_HASH_LO_IDX = 3 + H; + /// @dev `publicInputs` index for `committee_hash_lo`. + uint256 internal immutable committeeHashLoIdx; - /// @dev `7` pub params + `8` return fields for micro `H = 3` (`dkg_aggregator`). - uint256 internal constant EXPECTED_PUBLIC_INPUTS_LEN = 15; + /// @dev `2 + H + 2 + (2*H + DKG_RETURN_TAIL_LEN)` for `dkg_aggregator` EVM public inputs. + uint256 internal immutable expectedPublicInputsLen; /// @dev Index of `pkCommitment` (last return field). - uint256 internal constant PK_COMMITMENT_IDX = - EXPECTED_PUBLIC_INPUTS_LEN - 1; + uint256 internal immutable pkCommitmentIdx; /// @notice Underlying Honk verifier for the DkgAggregator circuit. ICircuitVerifier public immutable circuitVerifier; @@ -48,8 +51,16 @@ contract BfvPkVerifier is IPkVerifier { constructor( address _circuitVerifier, bytes32 _expectedNodesFoldKeyHash, - bytes32 _expectedC5KeyHash + bytes32 _expectedC5KeyHash, + uint256 _h ) { + require(_h > 0, "BfvPkVerifier: h=0"); + h = _h; + committeeHashHiIdx = 2 + _h; + committeeHashLoIdx = 3 + _h; + expectedPublicInputsLen = (3 * _h) + 6; + pkCommitmentIdx = expectedPublicInputsLen - 1; + circuitVerifier = ICircuitVerifier(_circuitVerifier); expectedNodesFoldKeyHash = _expectedNodesFoldKeyHash; expectedC5KeyHash = _expectedC5KeyHash; @@ -66,7 +77,7 @@ contract BfvPkVerifier is IPkVerifier { (bytes, bytes32[]) ); - if (publicInputs.length != EXPECTED_PUBLIC_INPUTS_LEN) { + if (publicInputs.length != expectedPublicInputsLen) { return false; } if (publicInputs[0] != expectedNodesFoldKeyHash) { @@ -76,18 +87,18 @@ contract BfvPkVerifier is IPkVerifier { return false; } if ( - publicInputs[COMMITTEE_HASH_HI_IDX] != + publicInputs[committeeHashHiIdx] != CommitteeHashLib.hi(committeeHash) ) { return false; } if ( - publicInputs[COMMITTEE_HASH_LO_IDX] != + publicInputs[committeeHashLoIdx] != CommitteeHashLib.lo(committeeHash) ) { return false; } - if (publicInputs[PK_COMMITMENT_IDX] != pkCommitment) { + if (publicInputs[pkCommitmentIdx] != pkCommitment) { return false; } return circuitVerifier.verify(rawProof, publicInputs); 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/packages/enclave-contracts/ignition/modules/bfvDecryptionVerifier.ts b/packages/enclave-contracts/ignition/modules/bfvDecryptionVerifier.ts index 3ce4e43d7..d88a66818 100644 --- a/packages/enclave-contracts/ignition/modules/bfvDecryptionVerifier.ts +++ b/packages/enclave-contracts/ignition/modules/bfvDecryptionVerifier.ts @@ -7,6 +7,7 @@ import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; import { BFV_DECRYPTION_SUB_CIRCUIT_VK_HASH_PATHS, + BFV_THRESHOLD_T, readVkRecursiveHash, } from "../../scripts/utils"; import decryptionAggregatorVerifierModule from "./decryptionAggregatorVerifier"; @@ -27,6 +28,7 @@ export default buildModule("BfvDecryptionVerifier", (m) => { decryptionAggregatorVerifier, expectedC6FoldKeyHash, expectedC7KeyHash, + BFV_THRESHOLD_T, ]); return { bfvDecryptionVerifier }; diff --git a/packages/enclave-contracts/ignition/modules/bfvPkVerifier.ts b/packages/enclave-contracts/ignition/modules/bfvPkVerifier.ts index be7a63292..6cd6fcd2d 100644 --- a/packages/enclave-contracts/ignition/modules/bfvPkVerifier.ts +++ b/packages/enclave-contracts/ignition/modules/bfvPkVerifier.ts @@ -6,6 +6,7 @@ import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; import { + BFV_DKG_H, BFV_PK_SUB_CIRCUIT_VK_HASH_PATHS, readVkRecursiveHash, } from "../../scripts/utils"; @@ -25,6 +26,7 @@ export default buildModule("BfvPkVerifier", (m) => { dkgAggregatorVerifier, expectedNodesFoldKeyHash, expectedC5KeyHash, + BFV_DKG_H, ]); return { bfvPkVerifier }; diff --git a/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts b/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts index 77b32f516..186ca13b8 100644 --- a/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts +++ b/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts @@ -9,7 +9,9 @@ import path from "node:path"; import { BFV_DECRYPTION_SUB_CIRCUIT_VK_HASH_PATHS, + BFV_DKG_H, BFV_PK_SUB_CIRCUIT_VK_HASH_PATHS, + BFV_THRESHOLD_T, committeeHashFromLimbs, readVkRecursiveHash, } from "./utils"; @@ -195,7 +197,12 @@ async function main() { const bfvPk = await ( await ethers.getContractFactory("BfvPkVerifier") - ).deploy(dkgAggAddress, expectedNodesFoldKeyHash, expectedC5KeyHash); + ).deploy( + dkgAggAddress, + expectedNodesFoldKeyHash, + expectedC5KeyHash, + BFV_DKG_H, + ); await bfvPk.waitForDeployment(); const dkgEncodedProof = abiCoder.encode( @@ -230,7 +237,12 @@ async function main() { const bfvDec = await ( await ethers.getContractFactory("BfvDecryptionVerifier") - ).deploy(decAggAddress, expectedC6FoldKeyHash, expectedC7KeyHash); + ).deploy( + decAggAddress, + expectedC6FoldKeyHash, + expectedC7KeyHash, + BFV_THRESHOLD_T, + ); await bfvDec.waitForDeployment(); const decEncodedProof = abiCoder.encode( diff --git a/packages/enclave-contracts/scripts/deployAndSave/bfvDecryptionVerifier.ts b/packages/enclave-contracts/scripts/deployAndSave/bfvDecryptionVerifier.ts index a7072d604..002b04f45 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/bfvDecryptionVerifier.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/bfvDecryptionVerifier.ts @@ -11,6 +11,7 @@ import { } from "../../types"; import { BFV_DECRYPTION_SUB_CIRCUIT_VK_HASH_PATHS, + BFV_THRESHOLD_T, assertBfvDecryptionVerifierSubCircuitVkHashes, readDeploymentArgs, readVkRecursiveHash, @@ -67,6 +68,7 @@ export const deployAndSaveBfvDecryptionVerifier = async ( circuitVerifierArgs.address, expectedC6FoldKeyHash, expectedC7KeyHash, + BFV_THRESHOLD_T, ); await bfvDecryptionVerifier.waitForDeployment(); diff --git a/packages/enclave-contracts/scripts/deployAndSave/bfvPkVerifier.ts b/packages/enclave-contracts/scripts/deployAndSave/bfvPkVerifier.ts index b51dd5440..9f0958398 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/bfvPkVerifier.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/bfvPkVerifier.ts @@ -10,6 +10,7 @@ import { BfvPkVerifier__factory as BfvPkVerifierFactory, } from "../../types"; import { + BFV_DKG_H, BFV_PK_SUB_CIRCUIT_VK_HASH_PATHS, assertBfvPkVerifierSubCircuitVkHashes, readDeploymentArgs, @@ -63,6 +64,7 @@ export const deployAndSaveBfvPkVerifier = async ( circuitVerifierArgs.address, expectedNodesFoldKeyHash, expectedC5KeyHash, + BFV_DKG_H, ); await bfvPkVerifier.waitForDeployment(); diff --git a/packages/enclave-contracts/scripts/utils.ts b/packages/enclave-contracts/scripts/utils.ts index b60a4af72..bc0df8033 100644 --- a/packages/enclave-contracts/scripts/utils.ts +++ b/packages/enclave-contracts/scripts/utils.ts @@ -29,6 +29,23 @@ export const REPO_ROOT = path.resolve( "../../..", ); +/** + * Default insecure-512 / micro committee layout for BFV aggregator verifiers. + * Must match `lib::configs::default::{H, T}` in compiled circuits. + */ +export const BFV_DKG_H = 3; +export const BFV_THRESHOLD_T = 1; + +/** `dkg_aggregator` EVM public-input count for honest-set size `h`. */ +export function bfvPkExpectedPublicInputsLen(h: number): number { + return 3 * h + 6; +} + +/** `decryption_aggregator` EVM public-input count for BFV threshold `t`. */ +export function bfvDecExpectedPublicInputsLen(threshold: number): number { + return 108 + 3 * threshold; +} + /** Recursive VK hashes for `BfvPkVerifier` sub-circuits (from `pnpm compile:circuits`). */ export const BFV_PK_SUB_CIRCUIT_VK_HASH_PATHS = { nodesFold: path.join( diff --git a/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts b/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts index 03ef41686..c8e8006ee 100644 --- a/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts +++ b/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts @@ -7,6 +7,10 @@ import { expect } from "chai"; import { network } from "hardhat"; import MockCircuitVerifierModule from "../ignition/modules/mockSlashingVerifier"; +import { + BFV_THRESHOLD_T, + bfvDecExpectedPublicInputsLen, +} from "../scripts/utils"; import { BfvDecryptionVerifier__factory as BfvDecryptionVerifierFactory, MockCircuitVerifier__factory as MockCircuitVerifierFactory, @@ -21,15 +25,15 @@ const MESSAGE_COEFFS_COUNT = 100; const EXPECTED_C6_FOLD_KEY_HASH = ethers.id("c6_fold"); const EXPECTED_C7_KEY_HASH = ethers.id("c7"); -/** Must match `BfvDecryptionVerifier.COMMITTEE_HASH_LO_IDX` (3). */ -const COMMITTEE_HASH_LO_IDX = 3; +/** Must match `BfvDecryptionVerifier.threshold` / default circuit `T`. */ +const THRESHOLD = BFV_THRESHOLD_T; -/** Minimum `publicInputs.length` for verify (message tail + committee limbs). */ -const MIN_PUBLIC_INPUTS_LEN = MESSAGE_COEFFS_COUNT + COMMITTEE_HASH_LO_IDX + 1; +/** Exact `publicInputs.length` for the configured threshold. */ +const EXPECTED_PUBLIC_INPUTS_LEN = bfvDecExpectedPublicInputsLen(THRESHOLD); function buildPublicInputsWithMessage( messageCoeffs: bigint[], - totalInputs = 402, + totalInputs = EXPECTED_PUBLIC_INPUTS_LEN, subCircuitHashes: [string, string] = [ EXPECTED_C6_FOLD_KEY_HASH, EXPECTED_C7_KEY_HASH, @@ -83,7 +87,12 @@ describe("BfvDecryptionVerifier", function () { const bfvDecryptionVerifier = await ( await ethers.getContractFactory("BfvDecryptionVerifier") - ).deploy(mockAddr, EXPECTED_C6_FOLD_KEY_HASH, EXPECTED_C7_KEY_HASH); + ).deploy( + mockAddr, + EXPECTED_C6_FOLD_KEY_HASH, + EXPECTED_C7_KEY_HASH, + THRESHOLD, + ); await bfvDecryptionVerifier.waitForDeployment(); const dv = BfvDecryptionVerifierFactory.connect( @@ -111,7 +120,29 @@ describe("BfvDecryptionVerifier", function () { ).to.be.revert(ethers); }); - it("returns false when publicInputs.length < MIN_PUBLIC_INPUTS_LEN", async function () { + it("returns false when publicInputs.length is below expected", async function () { + const { bfvDecryptionVerifier, mockCircuit } = await loadFixture( + deployWithMockCircuit, + ); + await mockCircuit.setReturnValue(true); + + const messageCoeffs = [1n, 2n, 3n]; + const publicInputs = buildPublicInputsWithMessage( + messageCoeffs, + EXPECTED_PUBLIC_INPUTS_LEN, + ).slice(0, EXPECTED_PUBLIC_INPUTS_LEN - 1); + const plaintextHash = plaintextToHash(messageCoeffs); + const proof = encodeProof("0x01", publicInputs); + + const result = await bfvDecryptionVerifier.verify.staticCall( + plaintextHash, + ethers.ZeroHash, + proof, + ); + expect(result).to.equal(false); + }); + + it("returns false when publicInputs.length exceeds expected", async function () { const { bfvDecryptionVerifier, mockCircuit } = await loadFixture( deployWithMockCircuit, ); @@ -120,8 +151,8 @@ describe("BfvDecryptionVerifier", function () { const messageCoeffs = [1n, 2n, 3n]; const publicInputs = buildPublicInputsWithMessage( messageCoeffs, - MIN_PUBLIC_INPUTS_LEN, - ).slice(0, MIN_PUBLIC_INPUTS_LEN - 1); + EXPECTED_PUBLIC_INPUTS_LEN + 1, + ); const plaintextHash = plaintextToHash(messageCoeffs); const proof = encodeProof("0x01", publicInputs); @@ -145,14 +176,16 @@ describe("BfvDecryptionVerifier", function () { await revertingVerifier.getAddress(), EXPECTED_C6_FOLD_KEY_HASH, EXPECTED_C7_KEY_HASH, + THRESHOLD, ); await bfvDecryptionVerifier.waitForDeployment(); const messageCoeffs = [1n, 2n, 3n]; - const publicInputs = buildPublicInputsWithMessage(messageCoeffs, 402, [ - ethers.id("wrong-c6"), - EXPECTED_C7_KEY_HASH, - ]); + const publicInputs = buildPublicInputsWithMessage( + messageCoeffs, + EXPECTED_PUBLIC_INPUTS_LEN, + [ethers.id("wrong-c6"), EXPECTED_C7_KEY_HASH], + ); const plaintextHash = plaintextToHash(messageCoeffs); const proof = encodeProof("0x01", publicInputs); @@ -176,14 +209,16 @@ describe("BfvDecryptionVerifier", function () { await revertingVerifier.getAddress(), EXPECTED_C6_FOLD_KEY_HASH, EXPECTED_C7_KEY_HASH, + THRESHOLD, ); await bfvDecryptionVerifier.waitForDeployment(); const messageCoeffs = [1n, 2n, 3n]; - const publicInputs = buildPublicInputsWithMessage(messageCoeffs, 402, [ - EXPECTED_C6_FOLD_KEY_HASH, - ethers.id("wrong-c7"), - ]); + const publicInputs = buildPublicInputsWithMessage( + messageCoeffs, + EXPECTED_PUBLIC_INPUTS_LEN, + [EXPECTED_C6_FOLD_KEY_HASH, ethers.id("wrong-c7")], + ); const plaintextHash = plaintextToHash(messageCoeffs); const proof = encodeProof("0x01", publicInputs); @@ -240,7 +275,12 @@ describe("BfvDecryptionVerifier", function () { const bfvDecryptionVerifier = await ( await ethers.getContractFactory("BfvDecryptionVerifier") - ).deploy(mockAddr, ethers.id("wrong-c6"), ethers.id("wrong-c7")); + ).deploy( + mockAddr, + ethers.id("wrong-c6"), + ethers.id("wrong-c7"), + THRESHOLD, + ); await bfvDecryptionVerifier.waitForDeployment(); const messageCoeffs = [1n, 2n, 3n]; @@ -277,7 +317,7 @@ describe("BfvDecryptionVerifier", function () { expect(result).to.equal(true); }); - it("returns true with minimal public inputs (vk hashes + message tail)", async function () { + it("returns true with exact-length public inputs", async function () { const { bfvDecryptionVerifier, mockCircuit } = await loadFixture( deployWithMockCircuit, ); @@ -286,7 +326,7 @@ describe("BfvDecryptionVerifier", function () { const messageCoeffs = [1n, 2n, 3n]; const publicInputs = buildPublicInputsWithMessage( messageCoeffs, - MIN_PUBLIC_INPUTS_LEN, + EXPECTED_PUBLIC_INPUTS_LEN, ); const plaintextHash = plaintextToHash(messageCoeffs); const proof = encodeProof("0x01", publicInputs); diff --git a/packages/enclave-contracts/test/BfvPkVerifier.spec.ts b/packages/enclave-contracts/test/BfvPkVerifier.spec.ts index 36447a324..88f81fb1a 100644 --- a/packages/enclave-contracts/test/BfvPkVerifier.spec.ts +++ b/packages/enclave-contracts/test/BfvPkVerifier.spec.ts @@ -7,6 +7,7 @@ import { expect } from "chai"; import { network } from "hardhat"; import MockCircuitVerifierModule from "../ignition/modules/mockSlashingVerifier"; +import { BFV_DKG_H } from "../scripts/utils"; import { BfvPkVerifier__factory as BfvPkVerifierFactory, MockCircuitVerifier__factory as MockCircuitVerifierFactory, @@ -17,8 +18,8 @@ const { loadFixture } = networkHelpers; const EXPECTED_NODES_FOLD_KEY_HASH = ethers.id("nodes_fold"); const EXPECTED_C5_KEY_HASH = ethers.id("c5"); -/** Must match `BfvPkVerifier` / default circuit `H`. */ -const H = 3; +/** Must match `BfvPkVerifier.h` / default circuit `H`. */ +const H = BFV_DKG_H; const DKG_RETURN_FIELD_COUNT = 8; function committeeHashLimbs(committeeHash: string): [string, string] { @@ -59,7 +60,7 @@ describe("BfvPkVerifier", function () { const bfvPkVerifier = await ( await ethers.getContractFactory("BfvPkVerifier") - ).deploy(mockAddr, EXPECTED_NODES_FOLD_KEY_HASH, EXPECTED_C5_KEY_HASH); + ).deploy(mockAddr, EXPECTED_NODES_FOLD_KEY_HASH, EXPECTED_C5_KEY_HASH, H); await bfvPkVerifier.waitForDeployment(); const pk = BfvPkVerifierFactory.connect( @@ -192,6 +193,7 @@ describe("BfvPkVerifier", function () { await revertingVerifier.getAddress(), EXPECTED_NODES_FOLD_KEY_HASH, EXPECTED_C5_KEY_HASH, + H, ); await bfvPkVerifier.waitForDeployment(); @@ -223,6 +225,7 @@ describe("BfvPkVerifier", function () { await revertingVerifier.getAddress(), EXPECTED_NODES_FOLD_KEY_HASH, EXPECTED_C5_KEY_HASH, + H, ); await bfvPkVerifier.waitForDeployment(); @@ -284,7 +287,12 @@ describe("BfvPkVerifier", function () { const bfvPkVerifier = await ( await ethers.getContractFactory("BfvPkVerifier") - ).deploy(mockAddr, ethers.id("wrong-nodes-fold"), ethers.id("wrong-c5")); + ).deploy( + mockAddr, + ethers.id("wrong-nodes-fold"), + ethers.id("wrong-c5"), + H, + ); await bfvPkVerifier.waitForDeployment(); const pkCommitment = ethers.keccak256("0xabcd"); diff --git a/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts b/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts index 239ce3cd7..7263b2bf5 100644 --- a/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts +++ b/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts @@ -11,9 +11,13 @@ import { fileURLToPath } from "node:url"; import { BFV_DECRYPTION_SUB_CIRCUIT_VK_HASH_PATHS, + BFV_DKG_H, BFV_PK_SUB_CIRCUIT_VK_HASH_PATHS, + BFV_THRESHOLD_T, assertBfvDecryptionVerifierSubCircuitVkHashes, assertBfvPkVerifierSubCircuitVkHashes, + bfvDecExpectedPublicInputsLen, + bfvPkExpectedPublicInputsLen, committeeHashFromLimbs, readVkRecursiveHash, } from "../scripts/utils"; @@ -106,16 +110,13 @@ function hexToBytes32Array(hex: string): string[] { return out; } -/** Micro `H` — must match `BfvPkVerifier` / default `dkg_aggregator`. */ -const DKG_H = 3; -const DKG_COMMITTEE_HASH_HI_IDX = 2 + DKG_H; -const DKG_COMMITTEE_HASH_LO_IDX = 3 + DKG_H; -/** `7` pub params + `8` return fields for `H = 3`. */ -const DKG_EXPECTED_PUBLIC_INPUT_LEN = 15; +const DKG_COMMITTEE_HASH_HI_IDX = 2 + BFV_DKG_H; +const DKG_COMMITTEE_HASH_LO_IDX = 3 + BFV_DKG_H; +const DKG_EXPECTED_PUBLIC_INPUT_LEN = bfvPkExpectedPublicInputsLen(BFV_DKG_H); const DEC_COMMITTEE_HASH_HI_IDX = 2; const DEC_COMMITTEE_HASH_LO_IDX = 3; -/** `4` pub params + `107` return fields (`T = 1`). */ -const DEC_EXPECTED_PUBLIC_INPUT_LEN = 111; +const DEC_EXPECTED_PUBLIC_INPUT_LEN = + bfvDecExpectedPublicInputsLen(BFV_THRESHOLD_T); function plaintextHashFromPublicInputs(publicInputs: string[]): string { const messageCoeffsCount = 100; @@ -182,6 +183,7 @@ describe("BfvVkBindingIntegration", function () { await dkgAgg.getAddress(), expectedNodesFoldKeyHash, expectedC5KeyHash, + BFV_DKG_H, ); await bfvPk.waitForDeployment(); @@ -191,6 +193,7 @@ describe("BfvVkBindingIntegration", function () { await decAgg.getAddress(), expectedC6FoldKeyHash, expectedC7KeyHash, + BFV_THRESHOLD_T, ); await bfvDec.waitForDeployment(); @@ -210,6 +213,7 @@ describe("BfvVkBindingIntegration", function () { await bfvPk.circuitVerifier(), ethers.id("stale-nodes-fold"), ethers.id("stale-c5"), + BFV_DKG_H, ); await stale.waitForDeployment(); @@ -233,6 +237,7 @@ describe("BfvVkBindingIntegration", function () { await bfvDec.circuitVerifier(), ethers.id("stale-c6"), ethers.id("stale-c7"), + BFV_THRESHOLD_T, ); await stale.waitForDeployment(); @@ -378,7 +383,12 @@ describe("BfvVkBindingIntegration", function () { const bfvPk = await ( await ethers.getContractFactory("BfvPkVerifier") - ).deploy(await dkgAgg.getAddress(), wrongNodesFold, expectedC5KeyHash); + ).deploy( + await dkgAgg.getAddress(), + wrongNodesFold, + expectedC5KeyHash, + BFV_DKG_H, + ); await bfvPk.waitForDeployment(); const abiCoder = ethers.AbiCoder.defaultAbiCoder(); diff --git a/packages/enclave-dashboard/README.md b/packages/enclave-dashboard/README.md index 7a9217887..2e5686184 100644 --- a/packages/enclave-dashboard/README.md +++ b/packages/enclave-dashboard/README.md @@ -78,8 +78,8 @@ This is a separate Vercel **Project** from the CRISP client, both pointing at th - serves `dist/`. 3. Optionally set `VITE_SEPOLIA_RPC` in the project's Environment Variables. -> No "Ignored Build Step" is configured, so every push to the deployed branch builds. If you want -> to skip builds when the dashboard/contracts are untouched, add a `git diff` against +> No "Ignored Build Step" is configured, so every push to the deployed branch builds. If you want to +> skip builds when the dashboard/contracts are untouched, add a `git diff` against > `VERCEL_GIT_PREVIOUS_SHA` (not `HEAD^`, which only sees the latest commit and wrongly cancels > deploys when an unrelated commit is on top). diff --git a/scripts/build-circuits.ts b/scripts/build-circuits.ts index 14b42ff38..a415d5297 100644 --- a/scripts/build-circuits.ts +++ b/scripts/build-circuits.ts @@ -110,8 +110,7 @@ class NoirCircuitBuilder { mkdirSync(this.options.outputDir!, { recursive: true }) for (const preset of presets) { - this.setNoirConfigPreset(modNrPath, preset) - const presetResult = await this.buildForPreset(preset) + const presetResult = await this.buildForPreset(preset, modNrPath) result.compiled.push(...presetResult.compiled) result.errors.push(...presetResult.errors) if (!presetResult.success) result.success = false @@ -200,7 +199,7 @@ class NoirCircuitBuilder { return this.requiredPresetMarkers(preset).every((path) => existsSync(path)) } - private async buildForPreset(preset: CircuitPreset): Promise { + private async buildForPreset(preset: CircuitPreset, modNrPath?: string): Promise { const result: BuildResult = { success: true, compiled: [], errors: [] } const presetOutputDir = join(this.options.outputDir!, preset) @@ -234,6 +233,10 @@ class NoirCircuitBuilder { return result } + if (modNrPath) { + this.setNoirConfigPreset(modNrPath, preset) + } + if (!this.options.noCleanTargets) { this.cleanTargetDirs(circuits) } @@ -678,9 +681,12 @@ class NoirCircuitBuilder { return hash.digest('hex').substring(0, 16) } + /** Generated at bench time; must not invalidate `--skip-if-built` between ensure passes. */ + private static readonly SKIP_SOURCE_HASH_ENTRIES = new Set(['target', 'Prover.toml', 'Witness.toml']) + private hashDir(dirPath: string, hash: ReturnType, relativePath = ''): void { for (const entry of readdirSync(dirPath).sort()) { - if (entry === 'target' || entry.startsWith('.')) continue + if (entry.startsWith('.') || NoirCircuitBuilder.SKIP_SOURCE_HASH_ENTRIES.has(entry)) continue const fullPath = join(dirPath, entry) const entryRelativePath = relativePath ? `${relativePath}/${entry}` : entry const stat = statSync(fullPath) From 40fc576aebd9742bbfec4cf56b917e59414fa816 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 10:19:08 +0200 Subject: [PATCH 17/20] fix wrong skip --- circuits/benchmarks/scripts/run_benchmarks.sh | 6 ++- scripts/build-circuits.ts | 37 +++++++++++++++---- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/circuits/benchmarks/scripts/run_benchmarks.sh b/circuits/benchmarks/scripts/run_benchmarks.sh index 3d56b7661..c5a9a9f5d 100755 --- a/circuits/benchmarks/scripts/run_benchmarks.sh +++ b/circuits/benchmarks/scripts/run_benchmarks.sh @@ -237,8 +237,10 @@ for CIRCUIT in $RUN_CIRCUITS; do # Run benchmark BENCHMARK_ARGS=("$CIRCUIT_PATH" "$ORACLE" "$OUTPUT_FILE" "$MODE") - if [ "$BENCH_COMPILE" != true ] && { [ "$SKIP_COMPILE" = true ] || [ "$PRESET_ARTIFACTS_READY" = true ]; }; then - BENCHMARK_ARGS+=("--skip-compile") + if [ "$BENCH_COMPILE" != true ]; then + if [ "$SKIP_COMPILE" = true ] || [ "$PRESET_ARTIFACTS_READY" = true ]; then + BENCHMARK_ARGS+=("--skip-compile") + fi fi "${SCRIPT_DIR}/benchmark_circuit.sh" "${BENCHMARK_ARGS[@]}" diff --git a/scripts/build-circuits.ts b/scripts/build-circuits.ts index a415d5297..e9d093861 100644 --- a/scripts/build-circuits.ts +++ b/scripts/build-circuits.ts @@ -199,6 +199,25 @@ class NoirCircuitBuilder { return this.requiredPresetMarkers(preset).every((path) => existsSync(path)) } + private logSkipIfBuiltBlocked(preset: string, sourceHash: string): void { + const stamp = this.readPresetStamp(preset) + const stampPath = this.presetStampPath(preset) + if (!stamp?.sourceHash) { + console.log(` ℹ️ --skip-if-built: no stamp at ${stampPath}`) + return + } + if (stamp.sourceHash !== sourceHash) { + console.log( + ` ℹ️ --skip-if-built: circuit sources changed (stamp ${stamp.sourceHash} → ${sourceHash}). ` + + `Run without --skip-if-built or \`pnpm build:circuits --preset ${preset}\` once to refresh.`, + ) + } + const missing = this.requiredPresetMarkers(preset).filter((path) => !existsSync(path)) + if (missing.length > 0) { + console.log(` ℹ️ --skip-if-built: missing ${missing.length} marker artifact(s), e.g. ${missing[0]}`) + } + } + private async buildForPreset(preset: CircuitPreset, modNrPath?: string): Promise { const result: BuildResult = { success: true, compiled: [], errors: [] } const presetOutputDir = join(this.options.outputDir!, preset) @@ -225,12 +244,15 @@ class NoirCircuitBuilder { const sourceHash = this.computeSourceHash(preset) result.sourceHash = sourceHash - if (this.options.skipIfBuilt && this.isPresetUpToDate(preset, sourceHash)) { - console.log( - ` ⏭️ Skipping preset ${preset} (artifacts up to date; source_hash=${sourceHash}). ` + - `Use a full rebuild without --skip-if-built to refresh.`, - ) - return result + if (this.options.skipIfBuilt) { + if (this.isPresetUpToDate(preset, sourceHash)) { + console.log( + ` ⏭️ Skipping preset ${preset} (artifacts up to date; source_hash=${sourceHash}). ` + + `Use a full rebuild without --skip-if-built to refresh.`, + ) + return result + } + this.logSkipIfBuiltBlocked(preset, sourceHash) } if (modNrPath) { @@ -747,7 +769,8 @@ async function main() { const builder = new NoirCircuitBuilder(undefined, options) if (command === 'hash') { - const hash = builder.computeSourceHash() + const preset = options.preset === 'all' ? undefined : options.preset + const hash = builder.computeSourceHash(preset) console.log(hash) if (process.env.GITHUB_OUTPUT) appendFileSync(process.env.GITHUB_OUTPUT, `source_hash=${hash}\n`) } else { From a6455239f48858b46d3a55562def9147c130c18d Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 10:24:42 +0200 Subject: [PATCH 18/20] add committee at construction and vec address --- Cargo.lock | 2 + crates/aggregator/Cargo.toml | 2 + crates/aggregator/src/committee.rs | 20 ++++ crates/aggregator/src/ext.rs | 50 +++++++++- crates/aggregator/src/lib.rs | 1 + crates/aggregator/src/publickey_aggregator.rs | 19 +++- .../src/threshold_plaintext_aggregator.rs | 99 +++++-------------- .../src/enclave_event/compute_request/zk.rs | 4 +- crates/utils/src/committee_hash.rs | 15 ++- .../src/circuits/aggregation/helpers.rs | 9 +- .../src/circuits/aggregation/node_dkg_fold.rs | 19 ++-- 11 files changed, 137 insertions(+), 103 deletions(-) create mode 100644 crates/aggregator/src/committee.rs diff --git a/Cargo.lock b/Cargo.lock index 6518e25e8..aade891fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3046,6 +3046,7 @@ name = "e3-aggregator" version = "0.1.15" dependencies = [ "actix", + "alloy", "anyhow", "async-trait", "bincode 1.3.3", @@ -3065,6 +3066,7 @@ dependencies = [ "e3-utils", "e3-zk-helpers", "fhe-math", + "futures", "num-bigint", "serde", "tracing", diff --git a/crates/aggregator/Cargo.toml b/crates/aggregator/Cargo.toml index 66132a749..078dc82cc 100644 --- a/crates/aggregator/Cargo.toml +++ b/crates/aggregator/Cargo.toml @@ -7,6 +7,7 @@ description = "E3 - Enclave Ciphernode Aggregators" repository = "https://github.com/gnosisguild/enclave/crates/aggregator" [dependencies] +alloy = { workspace = true } actix = { workspace = true } anyhow = { workspace = true } async-trait = { workspace = true } @@ -27,6 +28,7 @@ e3-request = { workspace = true } e3-sortition = { workspace = true } e3-zk-helpers = { workspace = true } e3-utils = { workspace = true } +futures = { workspace = true } serde = { workspace = true } tracing = { workspace = true } diff --git a/crates/aggregator/src/committee.rs b/crates/aggregator/src/committee.rs new file mode 100644 index 000000000..f77289deb --- /dev/null +++ b/crates/aggregator/src/committee.rs @@ -0,0 +1,20 @@ +// 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. + +use alloy::primitives::Address; +use anyhow::{anyhow, Result}; +use e3_events::OrderedSet; +use std::str::FromStr; + +/// Parse ordered committee node strings (`topNodes` / `PublicKeyAggregated.nodes`) once at ingress. +pub fn committee_addresses_from_nodes(nodes: &OrderedSet) -> Result> { + nodes + .iter() + .map(|s| { + Address::from_str(s).map_err(|e| anyhow!("invalid committee node address {s}: {e}")) + }) + .collect() +} diff --git a/crates/aggregator/src/ext.rs b/crates/aggregator/src/ext.rs index 1c794e4db..c2fdc5e1f 100644 --- a/crates/aggregator/src/ext.rs +++ b/crates/aggregator/src/ext.rs @@ -6,6 +6,7 @@ use std::sync::Arc; +use crate::committee::committee_addresses_from_nodes; use crate::decryptionshare_created_buffer::DecryptionshareCreatedBuffer; use crate::keyshare_created_filter_buffer::KeyshareCreatedFilterBuffer; use crate::{ @@ -14,6 +15,7 @@ use crate::{ ThresholdPlaintextAggregatorState, TrBfvPlaintextRepositoryFactory, }; use actix::{Actor, Addr, Recipient}; +use alloy::primitives::Address; use anyhow::{anyhow, Result}; use async_trait::async_trait; use e3_data::{AutoPersist, Persistable, RepositoriesFactory}; @@ -22,9 +24,12 @@ use e3_events::{BusHandle, EType, EnclaveEvent, EnclaveEventData}; use e3_fhe::ext::FHE_KEY; use e3_fhe::Fhe; use e3_fhe_params::BfvPreset; -use e3_request::{E3Context, E3ContextSnapshot, E3Extension, META_KEY}; +use e3_request::{E3Context, E3ContextSnapshot, E3Extension, TypedKey, META_KEY}; use e3_sortition::Sortition; +/// Finalized committee (`PublicKeyAggregated.nodes`) parsed once for downstream ZK requests. +pub const COMMITTEE_ADDRESSES_KEY: TypedKey> = TypedKey::new("committee_addresses"); + pub struct PublicKeyAggregatorExtension { bus: BusHandle, } @@ -168,10 +173,40 @@ impl ThresholdPlaintextAggregatorExtension { } const ERROR_TRBFV_PLAINTEXT_META_MISSING:&str = "Could not create ThresholdPlaintextAggregator because the meta instance it depends on was not set on the context."; +const ERROR_TRBFV_PLAINTEXT_COMMITTEE_MISSING: &str = + "Could not create ThresholdPlaintextAggregator because committee addresses were not set (expected PublicKeyAggregated before CiphertextOutputPublished)."; + +fn load_committee_addresses(ctx: &E3Context, e3_id: &E3id) -> Result> { + if let Some(addrs) = ctx.get_dependency(COMMITTEE_ADDRESSES_KEY) { + return Ok(addrs.clone()); + } + // Restart/hydrate path: read from persisted public-key aggregator state. + let repo = ctx.repositories().publickey(e3_id); + let state = futures::executor::block_on(repo.read())?; + let Some(state) = state else { + return Err(anyhow!(ERROR_TRBFV_PLAINTEXT_COMMITTEE_MISSING)); + }; + let nodes = state + .committee_nodes() + .ok_or_else(|| anyhow!(ERROR_TRBFV_PLAINTEXT_COMMITTEE_MISSING))?; + committee_addresses_from_nodes(nodes) +} #[async_trait] impl E3Extension for ThresholdPlaintextAggregatorExtension { fn on_event(&self, ctx: &mut E3Context, evt: &EnclaveEvent) { + if let EnclaveEventData::PublicKeyAggregated(data) = evt.get_data() { + match committee_addresses_from_nodes(&data.nodes) { + Ok(addrs) => { + let _ = ctx.set_dependency(COMMITTEE_ADDRESSES_KEY, addrs); + } + Err(e) => { + self.bus.err(EType::PlaintextAggregation, e); + } + } + return; + } + if ctx.get_event_recipient("threshold_keyshare").is_none() { return; } @@ -194,6 +229,14 @@ impl E3Extension for ThresholdPlaintextAggregatorExtension { }; let e3_id = data.e3_id.clone(); + let committee_addresses = match load_committee_addresses(ctx, &e3_id) { + Ok(addrs) => addrs, + Err(e) => { + self.bus.err(EType::PlaintextAggregation, e); + return; + } + }; + let repo = ctx.repositories().trbfv_plaintext(&e3_id); let sync_state = repo.send(Some(ThresholdPlaintextAggregatorState::init( meta.threshold_m as u64, @@ -214,6 +257,7 @@ impl E3Extension for ThresholdPlaintextAggregatorExtension { e3_id: e3_id.clone(), params_preset: meta.params_preset, proof_aggregation_enabled: meta.proof_aggregation_enabled, + committee_addresses, }, sync_state, ) @@ -247,6 +291,9 @@ impl E3Extension for ThresholdPlaintextAggregatorExtension { return Ok(()); }; + + let committee_addresses = load_committee_addresses(ctx, &ctx.e3_id)?; + let value = ThresholdPlaintextAggregator::new( ThresholdPlaintextAggregatorParams { bus: self.bus.clone(), @@ -254,6 +301,7 @@ impl E3Extension for ThresholdPlaintextAggregatorExtension { e3_id: ctx.e3_id.clone(), params_preset: meta.params_preset, proof_aggregation_enabled: meta.proof_aggregation_enabled, + committee_addresses, }, sync_state, ) diff --git a/crates/aggregator/src/lib.rs b/crates/aggregator/src/lib.rs index f4bb7c7bf..2b07b2f48 100644 --- a/crates/aggregator/src/lib.rs +++ b/crates/aggregator/src/lib.rs @@ -4,6 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +mod committee; mod committee_finalizer; pub mod committee_hash; mod decryptionshare_created_buffer; diff --git a/crates/aggregator/src/publickey_aggregator.rs b/crates/aggregator/src/publickey_aggregator.rs index e698f2f47..cac4447f4 100644 --- a/crates/aggregator/src/publickey_aggregator.rs +++ b/crates/aggregator/src/publickey_aggregator.rs @@ -4,6 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +use crate::committee::committee_addresses_from_nodes; use actix::prelude::*; use anyhow::Result; use e3_data::Persistable; @@ -96,6 +97,16 @@ pub enum PublicKeyAggregatorState { } impl PublicKeyAggregatorState { + /// Ordered `topNodes` when the committee set is known (post–committee formation). + pub fn committee_nodes(&self) -> Option<&OrderedSet> { + match self { + PublicKeyAggregatorState::Collecting { nodes, .. } if !nodes.is_empty() => Some(nodes), + PublicKeyAggregatorState::GeneratingC5Proof { nodes, .. } => Some(nodes), + PublicKeyAggregatorState::Complete { nodes, .. } => Some(nodes), + _ => None, + } + } + pub fn init(threshold_n: usize, threshold_m: usize, seed: Seed) -> Self { PublicKeyAggregatorState::Collecting { threshold_n, @@ -726,6 +737,8 @@ impl PublicKeyAggregator { return Ok(()); } + let committee_addresses = committee_addresses_from_nodes(nodes)?; + let corr = CorrelationId::new(); self.bus.publish( ComputeRequest::zk( @@ -733,7 +746,7 @@ impl PublicKeyAggregator { node_fold_proofs, c5_proof: c5_proof.clone(), party_ids, - committee_addresses: nodes.iter().cloned().collect::>(), + committee_addresses, params_preset: self.params_preset, }), corr, @@ -1350,7 +1363,9 @@ mod tests { node_fold_proofs: vec![dummy_proof(CircuitName::PkAggregation)], c5_proof: dummy_proof(CircuitName::PkAggregation), party_ids: vec![0], - committee_addresses: vec!["0x0000000000000000000000000000000000000001".to_string()], + committee_addresses: vec!["0x0000000000000000000000000000000000000001" + .parse() + .expect("test address")], params_preset: BfvPreset::InsecureThreshold512, }), correlation_id, diff --git a/crates/aggregator/src/threshold_plaintext_aggregator.rs b/crates/aggregator/src/threshold_plaintext_aggregator.rs index 82c4ff961..a2bd99f04 100644 --- a/crates/aggregator/src/threshold_plaintext_aggregator.rs +++ b/crates/aggregator/src/threshold_plaintext_aggregator.rs @@ -7,6 +7,7 @@ use std::collections::{BTreeMap, BTreeSet}; use actix::prelude::*; +use alloy::primitives::Address; use anyhow::{anyhow, bail, ensure, Result}; use e3_data::Persistable; use e3_events::{ @@ -20,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, @@ -190,7 +188,7 @@ pub struct ThresholdPlaintextAggregator { /// Last event context, reused for ZK and final publish. last_ec: Option>, /// Ordered committee (`topNodes`) for decryption-aggregator `committee_hash_*` inputs. - committee_members: Option>, + committee_addresses: Vec
, } pub struct ThresholdPlaintextAggregatorParams { @@ -199,6 +197,8 @@ pub struct ThresholdPlaintextAggregatorParams { pub e3_id: E3id, pub params_preset: BfvPreset, pub proof_aggregation_enabled: bool, + /// Finalized committee from `PublicKeyAggregated.nodes` (parsed once at construction). + pub committee_addresses: Vec
, } impl ThresholdPlaintextAggregator { @@ -219,7 +219,7 @@ impl ThresholdPlaintextAggregator { c7_proofs_pending: None, decryption_aggregator_proofs: None, last_ec: None, - committee_members: None, + committee_addresses: params.committee_addresses, } } @@ -658,15 +658,11 @@ impl ThresholdPlaintextAggregator { info!("C7 proof signed — awaiting DecryptionAggregation..."); self.c7_proofs_pending = Some(proofs); self.last_ec = Some(ec.clone()); - self.maybe_start_decryption_aggregation(&ec, ctx.address().recipient())?; + self.maybe_start_decryption_aggregation(&ec)?; self.try_publish_complete() } - fn maybe_start_decryption_aggregation( - &mut self, - ec: &EventContext, - reply: Recipient, - ) -> Result<()> { + fn maybe_start_decryption_aggregation(&mut self, ec: &EventContext) -> Result<()> { if self.c7_proofs_pending.is_none() { return Ok(()); } @@ -681,25 +677,16 @@ impl ThresholdPlaintextAggregator { } return Ok(()); } - self.dispatch_decryption_aggregation(ec, Some(reply)) + self.dispatch_decryption_aggregation(ec) } - fn dispatch_decryption_aggregation( - &mut self, - ec: &EventContext, - committee_reply: Option>, - ) -> Result<()> { - if self.committee_members.is_none() { - let reply = committee_reply - .ok_or_else(|| anyhow!("committee reply required to fetch members"))?; - let e3_id = self.e3_id.clone(); - info!( - e3_id = %e3_id, - "DecryptionAggregation: fetching committee members from sortition" + fn dispatch_decryption_aggregation(&mut self, ec: &EventContext) -> Result<()> { + if self.committee_addresses.is_empty() { + warn!( + e3_id = %self.e3_id, + "DecryptionAggregation: committee addresses missing at aggregator construction" ); - self.sortition - .do_send(GetCommitteeMembersRequest { e3_id, reply }); - return Ok(()); + return self.fail_decryption_round(ec.clone()); } let Some(c7_proofs) = self.c7_proofs_pending.as_ref() else { @@ -778,7 +765,7 @@ impl ThresholdPlaintextAggregator { ZkRequest::DecryptionAggregation(DecryptionAggregationRequest { c6_total_slots, jobs, - committee_addresses: self.committee_members.clone().unwrap_or_default(), + committee_addresses: self.committee_addresses.clone(), params_preset: self.params_preset, }), corr, @@ -1087,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( @@ -1311,6 +1263,12 @@ mod tests { .start() } + fn test_committee_address() -> Address { + "0x0000000000000000000000000000000000000001" + .parse() + .expect("test address") + } + async fn build_plaintext_aggregator( initial_state: ThresholdPlaintextAggregatorState, proof_aggregation_enabled: bool, @@ -1329,6 +1287,7 @@ mod tests { e3_id: e3_id.clone(), params_preset: BfvPreset::InsecureThreshold512, proof_aggregation_enabled, + committee_addresses: vec![test_committee_address()], }, test_persistable(initial_state), ); @@ -1437,7 +1396,7 @@ mod tests { ZkRequest::DecryptionAggregation(DecryptionAggregationRequest { c6_total_slots: 1, jobs: Vec::new(), - committee_addresses: vec!["0x0000000000000000000000000000000000000001".to_string()], + committee_addresses: vec![test_committee_address()], params_preset: BfvPreset::InsecureThreshold512, }), correlation_id, @@ -1480,15 +1439,12 @@ mod tests { (1, vec![dummy_proof(CircuitName::ThresholdShareDecryption)]), ]); - aggregator.committee_members = Some(vec![ - "0x0000000000000000000000000000000000000001".to_string() - ]); let ec = test_ctx(E3Failed { e3_id: e3_id.clone(), failed_at_stage: E3Stage::None, reason: FailureReason::None, }); - aggregator.dispatch_decryption_aggregation(&ec, None)?; + aggregator.dispatch_decryption_aggregation(&ec)?; let event = next_event(&history).await?; assert!(matches!( @@ -1511,16 +1467,13 @@ mod tests { let (mut aggregator, _history, _e3_id) = build_plaintext_aggregator(generating_c7_state(), false).await?; aggregator.c7_proofs_pending = Some(vec![dummy_proof(CircuitName::PkAggregation)]); - aggregator.committee_members = Some(vec![ - "0x0000000000000000000000000000000000000001".to_string() - ]); let ec = test_ctx(E3Failed { e3_id: aggregator.e3_id.clone(), failed_at_stage: E3Stage::None, reason: FailureReason::None, }); - aggregator.dispatch_decryption_aggregation(&ec, None)?; + aggregator.dispatch_decryption_aggregation(&ec)?; assert!(aggregator .decryption_aggregator_proofs .as_ref() diff --git a/crates/events/src/enclave_event/compute_request/zk.rs b/crates/events/src/enclave_event/compute_request/zk.rs index 411fe9a66..f08496e2e 100644 --- a/crates/events/src/enclave_event/compute_request/zk.rs +++ b/crates/events/src/enclave_event/compute_request/zk.rs @@ -77,7 +77,7 @@ pub struct DkgAggregationRequest { pub c5_proof: Proof, pub party_ids: Vec, /// Ordered committee addresses (`topNodes`) for `committee_hash_*` public inputs. - pub committee_addresses: Vec, + pub committee_addresses: Vec
, pub params_preset: BfvPreset, } @@ -87,7 +87,7 @@ pub struct DecryptionAggregationRequest { pub c6_total_slots: usize, pub jobs: Vec, /// Ordered committee addresses (`topNodes`) for `committee_hash_*` public inputs. - pub committee_addresses: Vec, + pub committee_addresses: Vec
, pub params_preset: BfvPreset, } diff --git a/crates/utils/src/committee_hash.rs b/crates/utils/src/committee_hash.rs index 148e3414b..a5ab1e11f 100644 --- a/crates/utils/src/committee_hash.rs +++ b/crates/utils/src/committee_hash.rs @@ -43,16 +43,13 @@ pub fn committee_hash_limbs_from_addresses(addresses: &[Address]) -> CommitteeHa split_committee_hash(hash_committee_addresses(addresses)) } -/// Parse checksummed or lowercase hex node addresses (as used in events). -pub fn hash_committee_node_strings(nodes: &[String]) -> anyhow::Result { - let addresses: Vec
= nodes.iter().map(|s| s.parse()).collect::>()?; - Ok(hash_committee_addresses(&addresses)) -} - /// Field hex strings (`0x…`, 32 bytes) for Noir witness `committee_hash_hi` / `committee_hash_lo`. -pub fn committee_hash_field_hex(nodes: &[String]) -> anyhow::Result<(String, String)> { - let limbs = split_committee_hash(hash_committee_node_strings(nodes)?); - Ok((field_hex_from_b256(limbs.hi), field_hex_from_b256(limbs.lo))) +pub fn committee_hash_field_hex(addresses: &[Address]) -> (String, String) { + let limbs = committee_hash_limbs_from_addresses(addresses); + ( + field_hex_from_b256(limbs.hi), + field_hex_from_b256(limbs.lo), + ) } fn field_hex_from_b256(value: B256) -> String { diff --git a/crates/zk-prover/src/circuits/aggregation/helpers.rs b/crates/zk-prover/src/circuits/aggregation/helpers.rs index 1bba328e8..93252fd94 100644 --- a/crates/zk-prover/src/circuits/aggregation/helpers.rs +++ b/crates/zk-prover/src/circuits/aggregation/helpers.rs @@ -43,13 +43,10 @@ pub fn u64_to_field_hex(value: u64) -> String { } /// Encode an EVM address as a 32-byte field hex string (20-byte address right-aligned). -pub fn address_to_field_hex(address: &str) -> Result { - let parsed: alloy::primitives::Address = address - .parse() - .map_err(|e| ZkError::InvalidInput(format!("invalid address {address}: {e}")))?; +pub fn address_to_field_hex(address: &alloy::primitives::Address) -> String { let mut bytes = [0u8; 32]; - bytes[12..].copy_from_slice(parsed.as_slice()); - Ok(format!("0x{}", hex::encode(bytes))) + bytes[12..].copy_from_slice(address.as_slice()); + format!("0x{}", hex::encode(bytes)) } /// Extract a single 32-byte public input/output by name. `kind` must be `"input"` or `"output"`. 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 f348226be..043328b46 100644 --- a/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs +++ b/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs @@ -16,6 +16,7 @@ use crate::circuits::vk; use crate::error::ZkError; use crate::prover::ZkProver; use crate::witness::{CompiledCircuit, WitnessGenerator}; +use alloy::primitives::Address; use e3_events::{CircuitName, CircuitVariant, Proof}; use serde::Serialize; @@ -296,7 +297,7 @@ pub struct DkgAggregationInput<'a> { /// Honest party ids in the same order as `node_fold_proofs` (e.g. sorted ascending). pub party_ids: &'a [u64], /// Ordered committee addresses (`topNodes` / sortition order) for `committee_hash_*` public inputs. - pub committee_addresses: &'a [String], + pub committee_addresses: &'a [Address], } #[derive(Serialize)] @@ -373,14 +374,13 @@ pub fn prove_dkg_aggregation( .collect(); let (committee_hash_hi, committee_hash_lo) = - e3_utils::committee_hash::committee_hash_field_hex(input.committee_addresses) - .map_err(|e| ZkError::InvalidInput(e.to_string()))?; + e3_utils::committee_hash::committee_hash_field_hex(input.committee_addresses); 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(), @@ -444,7 +444,7 @@ pub fn prove_decryption_aggregation_jobs( prover: &ZkProver, c6_total_slots: usize, jobs: &[DecryptionAggregationJob], - committee_addresses: &[String], + committee_addresses: &[Address], e3_id: &str, artifacts_dir: &str, ) -> Result, ZkError> { @@ -475,13 +475,12 @@ pub fn prove_decryption_aggregation_jobs( } let (committee_hash_hi, committee_hash_lo) = - e3_utils::committee_hash::committee_hash_field_hex(committee_addresses) - .map_err(|e| ZkError::InvalidInput(e.to_string()))?; + e3_utils::committee_hash::committee_hash_field_hex(committee_addresses); 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 15308c2bda49c80b828ac3302b255ebe82362000 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 10:57:00 +0200 Subject: [PATCH 19/20] take care of nits --- .../results_insecure/crisp_verify_gas.json | 62 +- .../results_insecure/integration_summary.json | 102 +- .../benchmarks/results_insecure/report.md | 88 +- crates/aggregator/src/publickey_aggregator.rs | 9 + crates/multithread/src/multithread.rs | 6 +- .../src/circuits/aggregation/node_dkg_fold.rs | 41 +- .../interfaces/ICiphernodeRegistry.sol | 9 + .../contracts/lib/CommitteeHashLib.sol | 10 +- .../registry/CiphernodeRegistryOwnable.sol | 6 +- .../bfv/honk/DecryptionAggregatorVerifier.sol | 1635 +++++------------ .../bfv/honk/DkgAggregatorVerifier.sol | 1635 +++++------------ .../scripts/benchmarkGasFromRaw.ts | 20 +- packages/enclave-contracts/scripts/utils.ts | 13 + .../test/BfvVkBindingIntegration.spec.ts | 20 +- .../bfv_vk_binding/folded_artifacts.json | 8 +- 15 files changed, 1112 insertions(+), 2552 deletions(-) diff --git a/circuits/benchmarks/results_insecure/crisp_verify_gas.json b/circuits/benchmarks/results_insecure/crisp_verify_gas.json index 783ed1443..eed2eac81 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": 3042369, - "user": 2972905, - "dec": 3553763 + "dkg": 3042430, + "user": 2972893, + "dec": 3553544 }, "source": "folded_proof_export_plus_crisp_verify_test", "artifact_sizes_bytes": { @@ -17,63 +17,17 @@ }, "calldata_gas": { "dkg": { - "proof": 169908, + "proof": 169968, "public_inputs": 6144, - "total": 176052 + "total": 176112 }, "dec": { - "proof": 170064, + "proof": 169848, "public_inputs": 17304, - "total": 187368 - } - }, - "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.110746138, "runs": 3, "total_seconds": 0.332238416 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 0.607811889, "runs": 3, "total_seconds": 1.823435668 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 0.557438458, "runs": 1, "total_seconds": 0.557438458 }, - { "name": "GenEsiSss", "avg_seconds": 0.123049167, "runs": 3, "total_seconds": 0.369147501 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 0.222128986, "runs": 3, "total_seconds": 0.66638696 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.377330709, "runs": 1, "total_seconds": 8.377330709 }, - { "name": "ZkDecryptionAggregation", "avg_seconds": 48.488375583, "runs": 1, "total_seconds": 48.488375583 }, - { "name": "ZkDkgAggregation", "avg_seconds": 20.208865916, "runs": 1, "total_seconds": 20.208865916 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 1.438067534, "runs": 6, "total_seconds": 8.628405207 }, - { "name": "ZkNodeDkgFold", "avg_seconds": 61.160005264, "runs": 3, "total_seconds": 183.480015792 }, - { "name": "ZkPkAggregation", "avg_seconds": 2.138399333, "runs": 1, "total_seconds": 2.138399333 }, - { "name": "ZkPkBfv", "avg_seconds": 0.331717652, "runs": 3, "total_seconds": 0.995152958 }, - { "name": "ZkPkGeneration", "avg_seconds": 1.344757416, "runs": 3, "total_seconds": 4.034272249 }, - { "name": "ZkShareComputation", "avg_seconds": 2.692020006, "runs": 6, "total_seconds": 16.15212004 }, - { "name": "ZkShareEncryption", "avg_seconds": 2.493858539, "runs": 24, "total_seconds": 59.852604959 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 6.018945347, "runs": 3, "total_seconds": 18.056836042 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.096458403, "runs": 3, "total_seconds": 0.289375209 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.215833966, "runs": 5, "total_seconds": 1.079169834 } - ], - "operation_timings_total_seconds": 375.529570834, - "timings_seconds": [ - { "label": "Starting trbfv actor test", "seconds": 0e-9 }, - { "label": "Setup completed", "seconds": 3.025607542 }, - { "label": "Committee Setup Completed", "seconds": 20.220793208 }, - { "label": "Committee Finalization Complete", "seconds": 0.006228708 }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 298.983421792 }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 301.505155708 }, - { "label": "Application CT Gen", "seconds": 0.3085015 }, - { "label": "Running FHE Application", "seconds": 0.003573542 }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 78.392914 }, - { "label": "Entire Test", "seconds": 403.470000625 } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000005b511f2d4cb51d35b00000000000000000000000000000000000000000000000b01d51e6030070dc10000000000000000000000000000000000000000000000012a64ca983c611e41000000000000000000000000000000000000000000000000000230545bc8406600000000000000000000000000000000000000000000000718d0715b7e2424890000000000000000000000000000000000000000000000051f93a31ae037bace00000000000000000000000000000000000000000000000e429942b63702d4c80000000000000000000000000000000000000000000000000002dc72bfed656900000000000000000000000000000000000000000000000ceacc1c98d5d99c8e000000000000000000000000000000000000000000000007a0a946c6f426f8220000000000000000000000000000000000000000000000027448cb4671b613af00000000000000000000000000000000000000000000000000015f1870dc3cfd00000000000000000000000000000000000000000000000e8d4c84df6b4bf36b00000000000000000000000000000000000000000000000efe4d1cda5243cd82000000000000000000000000000000000000000000000003582d18fd9745444300000000000000000000000000000000000000000000000000020ac7acba545e23dda2dee6ef7f581e41251092f73146707fce50a8c5247496e8658aa4e1a00023227a50845299af8cb19dde4dad6b9716a41dcb53a7e9ec2dbcf6cb7cedfcf429da34f9bbb7408cb5d6516c98a3daaaee32841452477c975abc613170508ae70bdd2d14bb31a87a356072496352c8ef6f71d0e1abf34a783d93dd555ab7371e2a5d417712e7bdf01665074773edc7006269134b7aff1013c061126d156ad6ab1da8bd9bd28a86e5903cb57cf59b274eccfc1fdd671cff57e56d504c1ccddc4d2999a05a404c606f997c9dc2341e1012f20f715948d689996b03b1114fb94f741b735acc897fced5bd7960e5f7bf3234228341c68c15fdb811824ec7bc48b82018cb47e7ec2646dd5e232ce3e21b9e4ef73d7dcbd33e940d9ac693099e269cd6191a7e959d342e67cab16d13c991150e60fe886e47223dd21b625cb56420f5e911fcd90e994dc8cee3955a0d2d333a4142a2d63d6962552fb1e6a69444daf64e2f861d21c3e0925dd1bea1dbfd3f5f98c216ac78b94b9f6dabac3a2544130201091a24f1b083bc6ea77aa2e9756b5d1894367ae8aeabe9801b33b71f14d6a02e008a33d6197de1bd0ab3918e3fbc86135b9a021720f94abdaccbe1a0e4789b1215ae16b624c3b515734b8dbd24e97cb064bcbe942ea750965a8c98b76d5f92081b3a5b040e0bb98d03e2ebf7f32b0902417d756f56dae3f74eb2584e9e5bd4fc18952e418c5cd520708f7da8f54768e0d2aae6d76852e8ee9354cbcfc332d07220d5548dba68ec1b5480c2666964064319fb2051301ad6e747fa15fb126a515726465c337612d9a7b76798003ed46be30d046582f66b42cfadd8d05a7a44232812da04d4f16039a1c02e86f3761b083d29ab3fe21718f58ca6b0323fbc47b3fe1ec1931e6986a664b56660a932ac9b8a028237f3d8d7963cbc6fd1d98cebe39015c6c76563212709bb91ca7e7765a3a7a9429011fa04febabd30a534495debe108cb2764e0c15113a395b25c338978c083b18fc966f67b2094fd409189b09afc08e3528a88b7be02e8b40aa8aeefed463bc662b29bf6e95f9fd40de8a4b89da02160de581ad5e1f81264df4a96884a8ed60ab981425b2908a2cf60a040725aba0eb17bdd527239fd4846955b0d0292ab4f4700ba51d15c0066cbb05c1a69a0930a979675dc43fc73989c464ab036a9748ff1a3da20955bb6d9b714cb940ad3472f7c5630f35227f77dbc93446a397cd3651389ee2b9d428e646a889f0a496eda0c016f396375b87a07b03ffe6beb01f85605f8681710fbc856bcbae1ec4c354a1763afcb40087e23db7a17c75546a8c32ea416379060c17837cf5fa53f9971450260399a528cfcc78261b62d78d600a7c060fb08db45746ed96425a2376aef2a255cab28bb394deb3483f5861bc40aa58479ca0845c51d9cc489eff8aca255232ad763520431e7439e01418e0981d43e320f09fb84e1e0e3eaad93bc4080159e1ce9b204b0a59be0c1d2421692577370b5f99bd88c007a51f93d900425a417c92e353a7989b05a7d64ac21eae67f7dc0a7230a5f84a5ba309c8ca8b5ce4f94990a774d3e23cfd7058ea309c8dc1617177facea557524196c85350256dd03ffad1921b91c6543f9937abd7ead33bde17ce9b6ff64bc9dac063d0661921da683d017b1082138c0f44c5de5c1a7322767b799cba6780047e4b3d1349c09717267d722377bafe27d7da6dc1d3ede5f6226fcb4b24d6d27caf21a9f1242612e894bad0c3c6f2f75a28f3ba1b10d6b356e39bed4bad6e536bb0aaf592f800866f53c412001fb1f8028a4e8b58be1cbf2bfa0f15314d47a8dba45132695543fc59e20ce1a9000b1f0009915aba0aae926c3b3f957d256c1f9992372d8c78ef6dc79e8aa19dff3757640b912a9b97e2312f67709997c1343b3acf3fc9c896a62cc77e915147c2efd0d87793d9978c46e910a27fa85684b23a387b00eec9b5d75a38cb9bc0f9bf144c57718c065612fa1124391050c731b18617f04cc57919abc8a4f354d12e7e6347e30d4fa05f935edf26ad4b8ad2bd812e2d50a8b00eaa564b2092c4d1f0df7775c3a283edfe8a1085f8964ed02a34e6566a150b07c64f4b028430d080ec145a4066324fdd45c2287342e9b5ea0abad00dc4f26b5e6c63531b9a212380111e83ff15c17d0e888192da388182f5df7fa06c90e9964ef59bf041a6a11df01e3964fc3d25ed0783ec0b3e271e9e1e58d32c742806c26b5d408595c3c164c1bc960a65647fd73a38018ce97a04e599a4e74ccdfe34350d2345a02596e60d51d6e64c05d6b9c2323eb48a3bea008c0e6446d51c7756c1c470942679c0fc3752b3717270f4f361754f0f54486e17946ffc46a824a6b35e765e427c61197248c0473a584a714bfb9afb99abbc1946842b214eba989587d1335dadf2976414ebf21ccf9221302f881275f3b59a78950faf2aafccb0b1c692f13e37c7fca716e6d2e8f1d932e82de40a288c0effbc28a0af05c566d5483b64ba30f86e7e082d8a50bfc77324d3083248bc9e16ad6112513f3a9733212c4b4a2ee75b9daca2966112a1e575438427f88b2be7fa02504211a7fc6a187646026cac05d10801ca77e1322d800b36884029861252e842f560f4403cc9095359f7c3aa7f23bc337dc2e82008f0d67c9a2959dc6bd9a8585ff54a243897bb730833541814bcf7459d19c9b282e416560847a4e1fae7f16570970986a428898be1e09ceaf31ed18283cad5a11b33d363a9be8acd6e60177e802fe79d5fdafab11259bfefe43e9c2719a638f22fa93e7b2063e88921ba6ba3b39ca2c5f18d7389e78a39416415a0a4d17b3f10470bb1c86b7a6eff18f84977d83fd107f0c60e744d5b428515ff684772cc51d08191f948b88189ed9761f120042dce87a71ea5bcfa816bccbd1b4f632964fa522345d8ba5ee821b31e1b9174ba260407d046d71ddddf634e7c010dd75f54a792604442af3a73200bba608021ea5d02197e5d9c94b60f679f2575fab8c7c3f8225f05c6ab7873306dd474b2f13313135c3ae1a56fd74aeb42d8bd81577d554a220cd15e6b6e358d18bd814e38523237976600a36a30ae43e1fc7511976bcd0f32719458cce540f8a1be87184a367e0add5c8f771fd9084ea0a0508f960333684009e8b90ff8fe5e8b0299e6cc1d0cdb393cd76b0adc6c03c47429625d2fbce2c0619fb0f83f77f92fa74905bbe6ce46f858a5152f85fb101fa2c2f96a7a4c0031a168fb540feb76d6ea6797c22045fe3e56ea0bf88c1b99d4716c28f0391e8d521abfce0353e370049938475fae0f781ce7ac2d31b5ccb82faf3ae8843b0292914d6569d289f8d2a84b0b6cfa6d06daf2dbf671ad018d30aa313e00e5859c9cd104b2da7ee903febcd96d11eebd93aacad3eca2d8e3fb927ab283b7791e4765922bfe00693c6418d3bc49d7ad54813b9e04f67df3112bdc58493952805d8d27a250669c1641aa1f3c3f0a10c39706993606855644eabaa247fde62b7a71a753b1e4f5edac839c20082b10ce954a8349ca88146794dcd4f4c1362232bb71a437b16e46a49170c7137a44346005869a32242df156613f57697eb5159e7326b05b4170e8fc8c5dee3fb87c4734cf96adb912b3ebc48876ba1dd0606a9833939ed4806d67c9dcec5f1c7f347fe33a3620330449d8b66203f457539a1b4cae734722f0d35ffe47c44fced110cce34539d4b6238e51936bb2a9b9f8d6be174bc83ea2512b564356e585fbf9e229d904c9701dcee5c1ba30c2abc2affd5fd1ddaf78ade24a5066da4047b1f0fea259fc750816676966aa6013f4d3417e94ba5462f0f2e1a58688bf3e04bc4800d875bf95d6a6bf92310e5ba345cd96c98ebb37d71b9b505080fb8642d5bf2c73173017159f20639019d950164a9d10407d966e13f3805032c27385fb20d1a39e77771d4c3b4e0d4038d9903e942dc25d3b24e08cb16cb299b7e08755cdcdcb7d7468a07be3ece4331942503d54a00d64398b96f2b4735117ae27562d7fe0114c66e08c514baef5efebc9120616c2f2b2771bc6a3c3e0420a41a00edf256d8607b65a933aa0978be18bbf9156450f47088ccaaa59ab9100561a5e3e84492c01acb3f684d9f3e36b3a7ad61c9869228572d9903ed2022732f706453a19e147e6d514d89ea703f19033a87c69dd9ab44b5bd9c757a4c7d152e92380cd3a125689f6b0631c73dacb8b198567a0164b61eb47dfd05d82b906924ed57302ba52ce7635e257a1a31bcd545b997a38922fd3eb550d5d82f124e321de1d93998907e6f4e03d6225fa4c3cb2fd37a81390e9ab79140c3e16c46a2af0806faf6a25d9613cfd28636ca205b20703f39722fcbde4aff456b20f22e44c51de8caba24d92f33a39fd81b1259111d8b34a60226f82f873c9e1a354c4dba9606c23e7ca84dcb3a02e8132d3803bbb64643cfde71fb2ec2b33c890e9c855c61167b871de6fd885b256da4ddd93d2637d9ae6c03e8076c48d07b62c979c9239f157736d3fe9ef70a2af995df9084bb5821093a6e42ad4abd0c66e1f68249d3712d50e693c750e7181030cb92de49902ebc55a3b9b9809e0cff98ea886cdfd8112676b06aa5847cef141054c923a61d4ad77f4198b4d543a6084b5e3549bc38dc174ba2ed996781cbd748e82d5ae19d3cbc1eb0ae738a40200e7e258d73b3a638066c2858a2157ee88c177d284e0a989122ce507d6be98c5f5e2ec14622915a3720cbf50324858227583b1a2ee4fcd200917c926f1c0647373c754f57cfcbe5560f4b1cf084342aa77894606665c04ccf8a42a8c85f6eb976771907a948c44c4c06c08957d8bbb64b4d366da2c85339dc18c03d83e9174002ad1468f4be11bc8f01b2ba2af6d32b065c67597e14b10ab4c5fda0b6ce05da63bf29ac5f6db0febf25b8fa7f36e2dba136c37019b02efd1dfa725779bfae34491ec3dd5e45684d78119ce05e2b93c9dce1a7f7b5b680380be3a99fda4f717edd23e6a7def83d53ca008f3313fcc41efd4be5521b2b6fb3f1f24d56ecd43c17df51eb7c6cdef39959036ad2b02ce4d9d91a67478ed9627a5466443d5d3f8db5e30e171ce618d7b70c238c9238fbc9a16962d7a2ad681214e2868538001ced364bacb18815ca7bd81316f7fe0bd98cb9198711c492af8705c3fed5f8f3f0b0283cae8f77d1975ac3f510cfbe890930c9d6490e808f6dfa26738ddecaddde59de7357f127c1b799a30e0b7cb4dc8b8bba8a5df779fa0f4bf7e1241cb8a681f47d1ceeb249f66dc0693e0fd8a02c387668167f6b49f7ac43760bee734a8800eb53415b2596c60f15d41a2c518e216f57d30274aa50661bbcdf4860fa8f6dc4be49e9abd594bd9af44bf2213c74c95eb0ccb65c8b114938880fac919fb29f701733084568ac2d2c5ec1f92d1b6667167c24869314fb97a3e943df84c79677d05a7a43e447ff27c991991a2485abbade003ba92c987f739c82fd517890c4f9f04a721cc19c1f5084dcadf813b60329661db44285f31c4f4a55bf41ee302f156ce87aebfc49b9f40a0c619d0b04fa295e8a5e1c4948db471c2d5209350a1b3d665ffaff15b77bc36e22a9ed0543c4974cd3be9ed33f5043be25fcda72c5148575cb6a34adfcf8efc729b01925cba94e679c5a6b422b9fe6677e8efefaf763ab0854ea9c1543293211f88e4c3014445cab4f15f7ad77715d46d91c9f6344f00ea838d5254342c7287bb2f2b60a6ed3584f382f01b230cba9b3508742adb5ee207f949de3c54d1eb89e62880c0eb22555ff9a620d43adb90a9af86ba56643806fee7f0148128dbe856a05accb2bc8374e962c5a25d0488aea23ae223af57afd24a11c278b329cf89b665127a71bd24b83f67cee6408fc5416e51338c9389a8209559ae1bb32589b8c19a83dbe1d58e6cf00a2ff11222318c0bb5b7ac08a00bcdd0ee8fc95ec1619b1f67190051a9d0b0371f7d5b2ce78a41e994e8e7fb57b4f13ed5f6f3214faf377bd628fb608f519d97d9ff0a07631af04dcf036ab6f3df5bebafc2a91e37c790ec0136c420a4c08727cce57999c7c185097ae2da1cf333018afedc3145419c828592b198500cafa9b7b948eda0218c87138283d1768569267bc2e46ef038042753731fa142a66a5e7950174fab0e0d39b61beb0ca7553a860d97efb5f4c80ab1a1fd80f4d0ee792f858c032d48246f85bab8df45dad81db6a902131ba4a216e81de09c8ca06d595cad5d7af6214062c2b0245edaeddd03b4b6400b4ed21aa7190b9e0218e00346b980443f699eda84306a85e2274321a2f7da8c02e44a629fa99ee128e8715ef77e8e5ca3bf6185f06674ec4dd9b9f620d8c17c9243fb124fa6546daac531c378bf077fcfda7156ac951780311883f7c75421a1347a0fe04bb8519a9b6af18dc4b0691b44d8a341fc08337d47fedbdaac9f99e37b384739ef1bd8c5e8a5c1f1e4c436d0e81619ce96b58b2039a19f7e8bc4b307c73e5efd12c4b16e19d920147b152a6fb4f5295979a4ddabc8948c5ea29c027c054abb3c6f58a4cf1f5021fa3f3dab4d50566bf244f33e70501c157fb44e7dc75b50328961ed42246c2342f650c566eb35299b8b65d6ef6f10e5aa40ddb3ede8dcfed9adce70f0c32c8601bbcd1e17fe1719e91cc0733fb1e150084b76f60e230eb1017268b67edc5b8740977402fec4892087a38c21e56f49ba12254a531d91d7e39b68a8a26b417fa532e4b08295ec9da45ad16d6f9d8fbaa1e985d995151ad37c01d64b112afba8b6a1108e39e5729ef6a68e0d3606b06a20983a94961c055f624d9e975f20eeff1a212ac7931d1b35e93d77e0423d1eb4d657553d15a5a9b4d85bfe2740f41f2adb61a7653dc70eeebaf0c3b5cc634e72380e7b5b83fafa61c579c053a94eb587afd08b119d4f8175ae8acebad458513d1ff0ae8a12ba51c8ea127f49066302c008218c42b7d9709261b225dbdf05ced37039f6b9ca4d7b51cfb8533ae6aeedb366425797644eee4765c70776ca6e4ec62e7c1829e1ba1d841f3b9dbd3fd71c513a21d965228376642c00a83f036492a7ba0b448e23e54101245251f1f37a0e4748d277721402a320883c90dce40ac487925724f31eca540af37b408d6eac7d6f9291bb390629c533ddddc5d6fb287b9ab77338da698fb1c07b69455aed4832283ba1590c5f9cacbc8cebb1ef3488e3f87bb794a5140bcb84c0572319864ca36ff9d10dd35e6a2d44d1c3fc5b17991499f3851e2192c6d494adff295262d6f6a91a712b49db17116a195641ff55c654d383b41a1b951eef2ef642ea6790d4db7e8372b19955f9a1e9e1bdea242d0defb2b58a0740766b3c359ac0474dcd7ed46ffe003f613b1e00dc756983823bc5f2194f8300cbfb43e203964246e7d6b179fe11a226ad9795f1255f128ec5f1c6194342c97e7a14a2878f7c3eb8c3808729ea91415a99ea2328517627a3f5d4576fa479f70193dde92ff0d5140c45e4672675ff00417e0bd36e49af6f46694ee1483c20233c8ff6d529e7ca5bb907dae9a38e01d0f4b1ca82e447c2dfc3a6ecce73d8f3358f4d49e2491554d442d2f91e4c080f80253ae5eb7afaab8cb4f2bb20b29d21b53895d3fba618c7a955d49d5c88c8d0b01bb37cb808b869f8a2a4419fcd00425dece280928d90e21f2335f77c4c5cda922f7bdf7189caee11c6b04cb41586f63e7ca62104d42f1cf28a2a0d268f469aa12d7e233de2a6aa37affa24755210902a1bfde7b73102e5b3504182508a76c952136f053db95011f8f4e68f74b72bf1f55b12298c19c1e13d34c7fd3e2f6294005e8f0def5f236dd182fdd4f783d363da0fee426cd740ae4d5e75dadcb677c4c250d4ead45c1b308c145755d9d1f1f8d676955a914d872e50713b704a909cd5f1aa80efc9d44a63fe457b038b246222c90aa4ebfa74c1bafa1de5e93c8bf7080038f4e83f8ae4b853dd883b003b99f4613d8020d4d0d97d26d9ff88aaa0ec7cb2f6d7ba11ada7efbb96c7a5d5e32e332b39f35e386561ba4bf8833570dcceff5196fcf07460c3d089d1c6b83b169a564e9222b884c2e347cd658f7af80aefcfb23f9935f647f0ebe1a005cb6fb4ed98a4c8ba448df49604657592264f63c88a602c3cabc2e36bd69cba85fbacc44717922c01e60fdee774fcf21f6d44090441b1cf310323e989608b2d41b281f91020c0ee2594e2e8204f8b03689b06141a9631ddb76e74d376d4bee03656781f3ad28fe0e30e313924c58c494b299c18d49d50b37de3958dbd08fe02aef69338013f54f43fdeea844f438f0a9f955009d865603740933e16cf18ff2fd871e759d6b19ae87ec69ba6b24f86695fa117ea9f5b71881702f3a94e8519c2eda94e52e5db84111e2f0346d0e5f0eb5904812b23c180805df1cd654fbb91d97676825597962dcef709a58097a0c7d3249a20639d1ac21297c97d017bf0a80f2095b47861facf1fac5c516ab5c1a3d3f9020acd9a205220e55dabceeef5c23bd9bfcd19d560282adbb33e091d28b78717ec524b0e193139f358ed59ae7c417d9e6621b3eb2d5c1d386c15166e010415a40eabf67530c00c54f29722675129b1bfba7b46bd72b74707998aad9090270f756f9dde966e60ea9af5059e10bd3458aeb2a805bb7a5799af9a59a2fd6275cd026f965c94c751f013f2a26d71f28160f66ab5c8dcf24ebb397c8c20c88edb467384b6621b120135624b8ed813f343e7f2f3c2e4bea6d59d50146df74c8fbfd2c53473f6f7aec035eddad7076ad50810e6817af77512712b73d6aa0f42a21de536f03f852065112492138dade430d05cda9498589126c98ff1635f3124a4f6d4610583d2bc62f0f061ad56438c47d727632a3c31cbb07e0f9fdd5747efc2fcb5403100388e90e01cd226f1ff7787c472a82df114eb0fe19bf0779ee6c179e69d88b1fa5daa50011e8b4c7ebd77699c7ec27afbbaee6c213aeed3d032fd920f9efa3605b5e5f4806e78d9470fc0aa3669d7cf6887eef9d415ba89e24630c1167cbd25b3a6e1b160835fa6a19a398a47be8f0c2db3759214c9b3f0f8d7b53a15499d5cf06b24169209313005e62cddfe7699c9935f7f70cad1e487ea6cb1bbb0a3f48e6bbd830a420974dbbe0537226413775525c3e0b15461078c7ac68b0982d2f403276d5a4e029e1028b2bea9586f8bbeacb415f778f85dbafcab84be281a54bb56799fde44d1c0685b64c9a5cc416e5196a5760a9ed66f3cc3d1d95185711d7b5dda724668110d60f9f80df45138f8827406796ca4ec6af94f0f391cf7cd234911a066cc625270b16e5a279cf868a6e1a501d98b8401670d3d39377feae26bd06ec15bfd3a323c1995119bb3c7bfa4442b14621f2c0e75965a1583a6e4ccc4661742de62f540020b67bf7e984b63cc93b88147980f416a18ab2ec5d4c8b3226d5497491f29213af31c2a90516ad67a90ca768bf6201339d3bfc5da195c7fc8703d5680442601cf592b80fa6f7b41960ee5854310b612268bc3f3512ccb731b8ddda872fb2840cb8be2d2bebd332721ff68d60a482a44b7bdade02ac3abb9f67ec77d3eb9f701d1e5819fcb02bfc03ed01b2196427f3aac21415ec8d0af17e447da47da6c30d25401845f7397a6489f84aa2bc3c7bdb98845b170fee4c85b026818f8a08ee5c07e9e9cc2977d7c9f7a420acbbc0b6ef0869e33ad0a66554188fe53548f9f880112de90517881d602b24c7fb296657282b5124d6fce2ec513521f71a3f3c218a135714149f5bbf244660b0362d3bd4833bb612bf5c295cf7ba5223d6ad91e0ea1315fd8917761d17ea51e0e07561ab3080fe3a709202b043db065dbdab5db74d0178b959fc331ffad08f8f42857c99327d10aa4213c3528bafab073285dde9f60422c8a6d594e4b8dfae338dc1a0775feeba8551765f900281dec72be4ee70f110f91709a95396a9a754e6ddb20f05eed090141db3e0a08550fdb0fc9d17d9e82a5d8dcecefbfb324ddb485586ee9e3c575578cad6c5a6a5afe204aee69e8e392d536de19e1921d846042488cf32541cdb1a9cf66ecde622b7264e6240a2133b1fc3c6d31a24cd748aba28c32985497e207336c5dfcb93797a15b5b77383fee9154e2a2bd123e8531490d08b040b982571d7c0538d9e1fbf0f5a2bb22924cff71dab362c07d5f539006df328ddf7df204ed2ace5ee9b7447d7ca3332e1c44dc900dddd2b962bcfb67e6ee9909eb31694631b2e16693ca06fc67be0a46c99a031017ef78c941eb2584c958922d9f6b8700e898be0ae8ba7633457239bc15dedd515f3bea21da491e9b05ccbeceb32a1dba49b7dc62eb046956a7a51e3066fa96c1c632801629bcd08d7ceb01285707c53a6ffef2148d44ee4b979aee1c1456cd6019d100106e81616971cadec5c14cf930a19560b342d8528adc048319d2a70f81f79b82fd20a15893eb7020e9dbf412e9e9ba76808ab84af12ca41d78aa59ad8236260c20af26a6140d727619d09b1645eec71f00b177ddf1bc1aa310a07f43e16c0b9e1bd805097a25c36b59b632d3e930801710d57b1c18c63dfd4378f4b250bb5a95c5abf31a50c3a9e8f054ecf212c38b0f84e4ce223b6934797379051571d92450c3746c4c095824005ce5433c44f0715e07038577efe7ed1fd364a246a1f46d34bfd9823111b8faa9bd332677a3491783ad7e72546e7378194d8daa4840fe8828f6a87236f3b596aff2bb4f0a65ff0f295c1a36e8d493f136dbb2581451c5de2721721070a775218f64c21c4c00a767e881492bf8c698311fed46a1e621370448e25d5224c77044bcb743e9101349b7b806316cb2eedb044f86d8de22d0d2ef4a2219ad5268c082d0de7c4fb5f83a0095377e16749b3f0662423ea2160015a08c5596a671ba38be2b62a1dc90360a720d5ec0733f8025e9cfb54009d94082a4a97671aaf5fdd2d0e35aee9ac1ddd5f43da044161d533f05c039ee353c629b98e0d271da96ba68137137fe4aa8ccb6b6108585f06cc6daf9e3f30f8bb8f1b73b4806929edb63a13623fd72369a265cf083a038537707a37e27962a63cc002aa0272c3884104a88e2414fe7f046bf61b07732ab262e2d393053e61e1f1581d2decee715631b375943a6b3bfa6e09d4646fddd168babbb2ca101bd42547350c8861e2d73d1f3955f958e06918f4b2636c09f14780155bdf8014290027d3bb2115604b18a214198ad896d8a525b5e879bf4251bc0968f25a159d4f20c8bfb01f7dd0188eeda951eb132b975f3e92d095e9fc66a1c7f4b2ebaef503974093c9126e1d9fccc4c3e76e84e954b4543b41c14863751364afb2ac255082e5ab388808f41a12246a01ee8d542cbf53ff777ef7d4d0a5e098d0f7159af7b688b2c560209a8c7ea44832a71d00f13df0e7e176bfdce9293750c0eff641517c74e100690727bc0c681e68e7614aabae2afc0fa1328147e3d27539723602e902633965f02029d32be1d25ddc06514ebf7032bfa5ab1e1b986d4c54cf2d5b30d1fc2bf0b629b7f19b8dcb76dae398734ded0a07e91412ae5ba9fea22b8335dc342e91ed801d324d33fc342e9413788c0fb2341fc665853493b215507424bb93eaaca8845e1607c8b0a261dda0241acc28ebf36c53b08d360d8ede5ee3d8642380da62cd96038603eb104c12cce0c26f76c80cef2cbf5d17e5ae9d05f54496242d2658996715d096fde5505b978b1f91e74cc30b4a6b2fbbb9504e2d871ca80511c3b6cb8b2822fb39421a11271e0644243c849337a212b19c296ff47a10a64dcc44d2705b191675c4e63fbe69390fd1bf8f429534977b31a7ef1827ce75f8146327e7741027aa65b7562dee8dde437d21f25237ffe9df93922b89e58d30809755dcc234e011fbbc796f48b8c374c4ed34bbd07350b1ccac9436f1d7383c99aae9881ed3041da3363ba0f47c068fead0e243f197952be3b2efa951dbd79985b52c6f6ceab7252c0e89c3dc82a8524316c37d4c4ffa34ffc101881b5c7cb7ef90dda805eabf13979f6f1f98d6e55e6debdab0ad2769e4e5c81dc65ac0715633b001949853e0130594288fb65b080dfe4d761a229c4db5ce9c6a9ae3fedd81169810bc20dcc4173d2a6c5d3f106780687bfc1dc562c0d7fb4877257797e2786b57ddda98422d0e83582b950ddc09b1af871960eaa9330eedd31c39dfbee458e5496b4ac40c24031dc22d65baaec1f8a523e0ee311d250de228a5a74d945d18bac528da164af30f72eea190a8ba1fa62f4942a16e9fbcc85296672a0057c08536a919d9f9e491209f84c4f0de4b1fe2c1d00ab3b1261507ae62ed9579e94e803a4c495bdc4f9e02b1e81fb55959abfeafdc3640be8025ad4c2efc88782b57726abc340422c3692cc8ed9c24674e2d7ec7219f416d35876d81debbc7d4c39799bb45b394526b6903cbaed9bedf4dc984d48d8900407f0e82fb42dbb88ed87107e3e6b6a5fe3e201db216ad5d483de1b2ecf04ada79df50152c14e274256b08a3fa9ae5836be71f2c7cfb5a13f50b71257cb7aa1ec9efd6237073b2c9c948e5315f46b9e695f58e05b4fec5af27f4017501da0b06571c35d81b3bacb22796580f08bd5d869f8dbd1a6cb57dbfc61d3a8b84fc87a0619a8d06d29a35c3e0099f077a55f922de69ec148cf200c548a4c60da3aba433ac2094964c7ecebb45b155393ba204f11d434528ac82a226f7febc39856a89f08e404c97072b5803f4576aea40d0d2857c4aa127099997a323f86ee40a263b4e6bd0b7c27c29ed2863358d8b2730cee91e9abf2a42cc1ddad268a356cb9e4cc46fb18fd1a38e8448ad7f36aecbc369e938717f22f90a91bcc51c9554f2f62b8f30550d166ef0855884d7e45407840bd57d23ea1d08965b833bdd7586785022937c823d6f6128af467a4704e615d4100416a52b0c140562fbed18ee33f11959ff21f7b7f42a2506deaf28bc72a0fb8aca18ccd22abfce5656f98f63cf0bb77451ed003b41738684e0bf79f020081d185d3de567233ae2cdcf81ea49c4d82460902c843ec281d159a5d34d3e063142a42d4d1f641dc709b092701642d4219cbcfd4def1454e9cfd8b08066235344152a3b0c1bc7062763cdf1556f6e96085f4ed0400e7cae920484d484ed13c1a890ef506e273629818ee4442a9613c69acb71827d6a6b7a0426ed927470089fda98a0ced565fa1f24f179ed5a2163de9b0361fceb03f0ea6fb7e08739d03cc0dac20c15ac1b2413a0e7046c36c8b1e621c72ede3f53c0f03d30b4e928d478371c7b64056d35051b3c28d5d735a8d7d291d803d04da8d1b9a4fef58c16dfbe0ed8d18e0525f39623822ff691f2d59cdce0ff21ec236f944b4fb7ef142877b1e03cb791b168e0fa28a4228ac97686b236947d4a6985016b55702fa7a8bbc22fbde4dd3a16c256201fd789ef6081854810e4e362f8d228666aaf5e19f850a4b19b3e1c7564d098610afba3a2acdc7aebce19c442018a87e7d76082bd289cc61bbb07ec748fea71c30dad31b0b6179b11909596d9ee3a6e7fbb3e2a0c661442190f4e9ceb58ed172d11725301b90452a2e60b0bdb8caed292684c20371758744a8cfc5b628f83d2830c677cf1255a9e2a08b44a5ec378f8e8fdc92c89e04904f5ccd5584e329f871b302d0f8904bd3bd176e6b410b2e32678005cbcd19df64cfd2892a90347485b821602b6f38914c9cf6d49f1950334c92d1f8094f481af8bfea7952cc4cc60dcf102d996778795344e7d940d4ac64a1947b00f2a7dd321eef14244a9999bba047e2e6eea09c9b3117be49017bc6bd03ea26432008367ffac272ef3b0f70b3d17ee25cac94131ac686c5979362d7ab257fcf022011c4da60261d9d2448cd4e52dce033f35bb4c9d98bc8b8a1ec606aca16cfc56cd7d194655442f67ff86d55c020d073a72a8ff94a9e27d67b22c77afc4b89c5eebf998c8d3c0123360cc2fa2405a0c463bb359ea776ab0ed3645a01952d1d129e5b5f23f2eb1553e5e2162bd8ccc2eaf1f7eabe2e90bec8ea0f4086b77cf8240896e58a8a2e33ce33bbcca54299608be84c441cc5ef5ef3d223e1ec9fed515b84f4f2354b34ab348580015240acb15950d0f5476d2d11723cd5e0b21b42838bbbf51238691e6eff0bb95532560d31066ffa9bf93b8776080c55679dea58167c60210f035bc8c074cf8f8bf6a0913194a0019d91151b66cc93f653cc759188ad21c30046d2f8ba2d65ff5924278500263e52d3f2aa1f4ea915960b8f954816d1f28e2e1d9f3cb968d0eb026b4de5e18237990c874a36db06d203b0fe43902d555963195b1bfbcab1a28c93e5e417017eb743a0fb399496fa5c4e09f53b13fd1537615cd39c984560a250e331fb3b226f129b9e725fea56f33dcb62e7434bbd653561fc014a12ea587d67b621045d803472f4155480bcfd19c079091760eb48c3870b30a6388e8a6e5c81c3e9579f405055a9dc88cad5ebd571d0715bfda20adc0f55177454ec6e2180229c9efbc301ab8c2751ad344c30e14d3ba8b1e0d2a7919e4e8d8c6a144ec1d6b3b9f2a9f960021df6baa571d82bf19ac456b8fc62c2a227c8066be3ae7009970c87ee45def09d68e692a0c99287016476b3525518ad3d6d99afe2d40cbf80bb519d1f0c05326635d85c428640c9a1086e9ef29baf268cd2d9217f05c080fc08da6774780e32dcef1e5bfade2aaa6e97f83102407fe1df4309838fa7624d1247cca1e0f0749", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e940000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000095d6f5f2e471af092cf95770b13ef41f0000000000000000000000000000000087f6a82b36692845130a33d755087d5511521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" - }, - "decryption_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000008344ffb6611f1eb5e00000000000000000000000000000000000000000000000e570a2487f8f1a2a400000000000000000000000000000000000000000000000a27800d90764dd8dd000000000000000000000000000000000000000000000000000097f1b50115b900000000000000000000000000000000000000000000000f562996237342943a00000000000000000000000000000000000000000000000f7b9376269e52bbe8000000000000000000000000000000000000000000000004c42934421d35f5d6000000000000000000000000000000000000000000000000000196b8049810da0000000000000000000000000000000000000000000000098754eb7764d0762a00000000000000000000000000000000000000000000000541a0197b3510be91000000000000000000000000000000000000000000000008e69d9147d452430100000000000000000000000000000000000000000000000000009d34a13a1ff200000000000000000000000000000000000000000000000b0d9f32235a92d9e800000000000000000000000000000000000000000000000f522191f8231cc6f700000000000000000000000000000000000000000000000c2a8dbc565a2d36d60000000000000000000000000000000000000000000000000000b173522955a01a536284afd1c497001ca9e84b3790201076ed13ebeb69fab013daa22043571e0748540dc8ca4b45711be77a38465dd79c0c40142982b46c68690fe38bee04120c59b2cfd1e34b800de0437cecc91fbfd76e08022456fd80be7973303b08c04414d6caebbf1338f2b9981477087b3033b0889c41c37b520cd7da9fdf60a1c0fb2df16cce8b3a7b413e6d4cf17c6813228b29a1968974c89b0f42faef09ec3a2a2af25ab164ac6ddc648d62beb31938fc918d2c1637c1130a3cd7c3fabdccbd3e2b1141b2aecf3810667de2c24cb5e1a1e3978d2554794af1e1f8f9dce098033010479bd81966836fa46e462c2ba54282f3a9ae5d7a5cb7fb2a404879625a5a63038ff133b794ea77a75f9c7b2c4d9a50a8fddefb89ff55c42d1859460003ad700510548a94446b4256b375c4d34b5865790d6decbb09de3e81d55f278073cd8b093e5dc602bd084065aa147f5bb7af355aff0da078be5f648d9fad84ab9606ba13f1217eed5cd6e53724d25036c76f24e8d44c411259472a9926540ca3a0b1f81edca2bd607c92149588ce53c402a5655f510e0ec1e8305e891fa83f900463d02b5913aca409bfac042b86f86adad8394d87c85719fdd6d4d9bfcda638cfdd5b2a75c23e9ffd3b044270ea864aecd2f5c6023701d4037ac90bd5bc57efa5f4ff230e24473bd1159cb796bdb9edd32f4f748e9e03abe37349fda4ea55f329434e075773fce8018ebc0cb466a7dad3701dd9e45822603029cfe24e0de39924a519138b0a3b78380cddf1e12904a8f8020fbddd9faf81dd5e3c610f4edb0e0025c60694b43ad0a9b56472cc6a7d2e64fc9ff29f578f6329a0bd90b8cfae4fe026ff002dc050988ffc265af9f14c5e455f9b3525321f595b0eeff881043ef4c80ba72069c68512c264f5b8362bf5b30ada4581359f11ad6228f0a3989deef85610b908a7ca6486c81953c35e2dad7815dfbfdc28be0539a577512696f20aa8ed22a9030b268013b6264f063a8d0c25805e76902d97338c43d578629f85ab81db61c1046d4477ef12c410e75926d1ada750bd1c65685e78ca7d0b66d13a3096311b052355dc5a797551b2e06cfea9d6e4fbc8aea55bafd322ab11cbb63bac757f9d990c3f2603424339c7a127c0f9217a5fbd5549141480c4ec5015962bb24036df4429c642d8af58d59abb7ff10ab858c9f12628f922048a1f6b0a61db0b0961da471e651a823c1a982d04d90b43c36fe93d066c0ac0407b6ff80384f4d9a71ecb6603fe50e0f0edc74dd65610f90bb05d30f4075d1c61bc0f0bc4d69115dce1254f19e1175ea92f245f63081c270a617f304c42c307601c9ca0156b9781ead752fc20352762796c8d8da599cf7c58aa10901216c670c7f28c9d1367ff753b0198760c3bd7200ed3c38ef28267501b774e5b0148e6b015f674ceec89559a75ac352106f4f2946aad78fb2dd235f8e2e89aff7932c22f59377baf2cc095b9077c7c6f0699b6ab350ac85570deb6b7e692ddcef5beeaf65a953335dd5fc7353baf823406f0eba8837a794c2e8efbb264d44031dd03fdac9916d2a6b62ec5ddc9c7d72f10b3e3e877cb56cb35596be8d54418e595a03a95800575733eca9a780b312bcd00349c708d1bc49b959b2ac8d3629c6ed90afe49d4d35ebe44c3acf0b9415c1016c3c140753bdf5e8c23a3ec0a7cf7dbc0194bd3568c3bf2ad01b65b82ee3ed42ce7541788ca715a28882f5e5483594809335434a56aa895e3ce9a1c76e529100b688b27a4a18b855af75944ea1e0ba29b3fecf075559d253389beb69cf903a60ac7bb71d5e172fb3852b2795a9ebf55ae61f82dab8cc1146898745396662a9c145d346b6c0a64b3e5c59a65eb67f05dd9ffddc4d06d32c77623ef255913c88522793056e1269741442527ff2665a38590153256d7adc313231adef3179e0bb90f3ae9bab00a761ab7d1accac79a6613a7509097b65be4a69b5a598862dbffdb23ccfd98a8087f5de2ba70059c4b522c74621ae0efd084dc9d1772a216c2dded0d192fc884d5f68777da48efa059cda5402142c84eda81f28d23a7ae17f074512458e88a1614c18b768d34ca16ae992c7dc05dad2956b5ca49ac601cd22e1431198cc4d4e3c2779a62d3516df35ca88b9f3079cea6532549e49203c21d6bbd421a84fba365ea09089dbba629b32b842847e6e10a0b785d4cce81c88bd235894d07821840ebb6eaa367bf8bb4d0928c6c7cd5ae95cb9cbf3cd3b06f1e5fde4d583041bd33a6f36255fc376fdb7b971d7796c15cbc7cd9e42157ad577dacce5aa21e8ce50133e64873448e238afe8a7171dfa0fa38ca9bdb12f11d7e8a91e91a8811187a0283bcfbf9c2bbb20794b50a937a2b72be98ade151aa94e66da62c209f2077abd55e76555cbec1d40637986e058affa82d02fd2998138db502bfce7c7709994c7fa99136015df8c2fbdfcf28cd1a3c4adf4bb8d8a733c35f7c263f2a3714a3d722f984dc0e35e6007c536f4a7b6dcaea273088991dbe2d3bcf4b745527085d14d1e3c29716ef011b5319975ea2fa78c40f92c236f809c08810070af12e2bbd76535a28e63187014f2ccbb5d3273718615d72a674f06c6a397365b71bd614511e01a0ec3b301c7c9b53a523d5813b34e57f1aa97ed05ec2405d839aa4930860ee93c57afa223b1eed64e27d9a5a743cb04aa25a9a8ec1ac30225b495f3619c2c5f4a425e9a01c52cd10487157371c40948bbdfd541195e0eeaf2a556efb1227db38d606878785fb91a3bfbdb5e03250a123d225759b457ddc71f5f19b6b226f75a27d6b6fde1147d7aa64aa20d1b21cc0380be789d620b82da77f6f6b0707501e2b1a5c72021ae5a1f0bd6a05755e8566575a469cb72a780a295a171d971b6e5489e6fc90d60f62b63b2e983568ad664a8641259a23aacb1aa57fa984061d23ef759797bafb41bc1055e2db586c003039c02a998387dc66d7b3a05a3e0f224bf9ff7ae5b4812cd010a814f46d7415caa6b1b2cbbe61e1cf90d341ecba9b17d8f76207c0ef02ec089bb8fff9d01875dd398237b3543ab1ec9711001629e02eea3c546a8f52329f60ea08d84771bcdd55b6f60c1a251a5901aceab0b8961920a36a51886ac1d471fea83aef6d32a8b1407f8322fd72be269f7b2e929d4fcd1a99a3eb192040523ae8cbaf0f8834d9a6b87f757543b4e86a5d36cea672dd5e1622eb88e208c7bff87fc4b56d3d411aeff9c0b9b3b12e517650a7f0500e1e082562181e6ce625c8b9d08eea580ab27bbbd480f80782430622d06473467c6c391099c3b5e3738348e3e9147bf5b3a04519a214854c2273523df7062d92150dd5122242c00fbb9590e63a0e246d3ec45e898e2d52d7336f612151c582fb5ff7bc2b196067cb2209dc592ab4992c39cddf94855375303992205d834ee582791ee10a91b549552dcb5ab5ac82b60de1fbfdd95d13bf7bb7bd41ecd585982ad8f6e523f8b45d340efe99cb374fe96ace51fcb62e0317142fc98283462c5647b58c3e053afc0de3aa26946f838ba86338b0eb0c51d26251fa5f01bbdf810bbad4d9b921383859fcb763b8d6cef9600ece08141efaef3e0c3a4dcd0ab6b5b3dbdfc13b25c547380f629cd0d313f1eb2eee44315ed10d82431934c3ce9e4f86c7e4a6a01fa66cbd0083bbe5e521dd689f3261fb078b75fe60d1daea8fd6db932025ab48243e6af5462e0bf3652c68488c964065ded7aed392475eecedd44cd48132b5e41d7bee005156ce8233e8d493a4f0ed4de4465f3e11e8b854348058f248bbda372488a16e53bb7af4306f70c285309ee014e317e563607fbb4bf987e818b5a20716d140988b761cfc9276e26b97059d7f8ed096839fadfb1c5587d92fe3c1c6fb0cbe97868b23a1404cf147cbfeb938cce0449dbd76f2ef7d4e5336761d4aed491a79fb22f9efd7aae3074c6ef7f625655c1b2ab60ad4a35ff5c58f71e27b95471e391541637830a5bbccf452038838bec96cbe2999f4f9e608e7fa520d964a1c18201a8a8e254c9a8934bc63901f8738e8720282d112f42cbcc557cd530bf2fe074de8f516ffcc8278ff2b9fb5a8c326043f598829840bd55698a583941cd59e281c8e1a805d9bbb014d7cff9e279b9a7094e164d9583f03d402a2cb47f4ba5b1b25fbcc32450457c5f7de5a74056c0da37adace3fc1b28f64be568121746ca61d2296dd20643236f6a62c92ff19c3de7ead56cd0b0ebd9cae96a42f954529862d445d30254f2941932b53e1a4fd146b7a27088a6c961f4c3ce1abb0397d66b32dbc87dc2093f06bccec79219a4da5fce8e323e8bba0e1bb945cdc78931fa6df0a9a915f1c89ac85161e9b8a4afa0fde2c4cb02e4ae55be15c47bc96e94fb9540097a37bbf80174f2d4fab66343f036087813e88eafdeb86c21aa261d00469352c16b820ede824900eefcce4e84e6d1deb609c7bf6b98541b529437731f14664039de3aac50f7f2e65db3610ed1bb39c4904698aa7ec32bde5a26c1eea19ca5f11493f3f468848e2c01bfd2ec698a69c4fb77b78736f5ed0c047ae7a34a113b40c5f32d7ca18ae45669f522c772aabcc19182183ea265deaf2d226c6e46f59d11b1337b4a470c411491b2ca2b18585f4858bf3c625ad2f26aeb6d80f67a0c5740423316dcfede7851e3b231a0e5a9c3617fbbc1f1a90fdef4de8c140ef6a05a90ea9dcc1a23fd2153d6a505969a23b4802aa998b8ccdd41d9825027f445802062e7fb60f115469d7b12b7e20f14c93386f67bba2750b5fb2ec397d23295b193d11c50765ff7a651fe0a2cf6845034130c847c922336d93fe17fa9e6220225c6a0ac1215dc6a671343587b1fe0228e0b660620c5991660809dbada660dd42bc3701960451c2bb3524076f82cae3e24292fab1da0309711c9abda0091ee237e9a41bcfb643d0e062b6e434313069c259d9505a8def12e844d6d496cd50541db9c21d535bab557ab6e09991afae0bc07a6b446317c77ed5ea97492bf01379f8a604250c91c3a43c17a422556d3c894b1d851fc9d5e5530bd1ad664bd2b8ab42403518c7848e39e1d31fd4c014022ce7739f5e864ec9e7f19a4ec4c3f6b0c4bc4d3d1bab608182f2de0d8ea2756ac1b1cbbfe1a4cd1ce2aafe2df90f55398803482b301d29a1d44127e478d390c7a78ceeb5783f7428f7ed38de58dc205a7ee2700803b7953de0f2067776eed0b6fee49816c56667403ed986586d2ff00bbdf128bc07057b39eeba2ca6c907e8f0f42cddf17172241f7763e7146be50bfbe0851842013d92c71b984ea76a54a42d4a2290195c601e998a4b08ea0aeee5ac63b10fd7075bd7e4b875d5a10d431c2dee6adf5a1c788200f0315fd36b6ee2e9a9d1ad2e2aeb83d4772d102af794d2dfc5f80deda52a1535412578d52391f1a17d36de700ecc166df5ffac841192e32ee3f2ddf3b9edc604c724f6de84f31bfa3233806b1ff44a9a4b482278a52222791b0d75b2915589af6dd95a1f994aa8ae3dbe07bf209a841a527308b4d444dce47e9ab9721cf3be96744123742e83f23f5a1edb2622a74b67667e14c32e4379272b7d2c4fa4d8d69d78a6c92d7218809203017c04018962cd8e399c9f6f40fc1a937be3c790cdb959833e921250181349bd108ee71156fe6945aaa58a08cd9991c18ab0d72533e105b70b30c273d74efe03698f551835dee63e09cd5f61fdc312cea44e2ae6c4157dd2d9884d347aa0f0c326e9632fb9245a2c7615a8c515fd6d3f492b41cde9364e42301b55853b90a98dca01b70ff99b6de8d6e4c142aac1a99e64b03d4cbbe79d8881d5f59e4cd61dc5846fe614a2e8985c6cfe883c924eec19952a1af20a1238da8d0a50379c737b365afe2605f23f30d50d912faa8c5c2c761d508810d776d3b9f708d153ef45cd24ed796f197b6f0f1a84037641d56eb9da1a8fb8bc0155389c3553b430a67ab09a211e6e2494a881eaa9434c9b8c87a4bf30f794af8924084a75aca7cec0789190909b64132d79e8972fe529c9f6e9e0fa0df6fc778414f6395a7d2af1f7a99a4a4d390f26da348e1eb4776746a9206306cb432a2d923a591316e700723ff26789f70e802a94291d52182ef8cb4316534eeda33ff85836861168f2b2e18f420075e2685408285e580b0eee48ae7f04dc48ef7540833c37b1c95c1511a6fb93be2461cc8b2afef75c415e32ff514eb00fdc49f702d91bcbd0e417d4bd1be6fdf7da0c10a50bee1a7cb4e79b3cc0f3f367b388ca6e868993fd0c3e8b327ce9ed2e706021b11a8d304c43f65254407478bcfb55ab6505aa2cd3fb967d4aacc774eede98388d102b2ae2dc6a933c43a8c15fad938a36e9947c35a7327ef18c2502fbc105cb4b12681122979a076406d7af0d109fcf1bae96c8646748158ed165bf44cd79f83702a4c0d6d5a754e392cf5dc6d054d7bfd9531001245effc79e7b0365c82e9ae0171d55d900c578569dec328286eb19cb634e3a7a73145f0ce4984e1f2b7b55112c45245d57f2211180e4316742393a2f66371097d9bf61f9be4a404b61f327f12ad39c9b868491deb74ab9cef0bb69730c8ef02f2d099c088280c7f67430df861acccd914b41a77abaeccab933372b3151caff7b9303b1842fab33767bee53911747535547a56dfeee3029441679bb7f459ac5a802d65699b5c361db2410a1fe0f618fb9298afc12bab2586498b1ad5ef91382de412e01f5eae4ebd7f3cacea1273851fea8c663c223da421631586471aba671449591a74d6cfe0469dbc288aa0c505f507aa7fffb10fc3f694b241cf86f0ac4f5041700301b8908201dbbe39526942f234858f899f7b7c2ca6068e4c3e62fcf662019c7c279f073e5802b68b62c79713b392ee04df390c179d13d9e9ad7932579bc13ef25fbf4441042f4a2300dc573695c846eb6e0d41a8e6fede1b9df399bb5156479be577da83f63f94c62174c3e6fcf5a4396cb97d95d61e4a0e853f2e73e9da83b1aaa8a58590b104bd50e101fa4357e6573be76723369111528ad02d503f0f59ba725e68e400161498717e67b30f71630dbb43d7072370f854b5c6dc202b52882ace381c2f4fbe2193d0ff8e94db820358082c14ae1c6c3b70224cc7e3c2ad8f9652c59cc5aa51e8f981fa460844c3e6e60fd367c17a2f066e3e121b26c34cd69396ca11108fe91b20620db4eef62b28e26515cd795984c4c5fb9011e70d1e74e01941ad5a3667c1e5716017b8d71ae844c6dd60777c7aa6db71cbf3449121b88787e6cfb41a3991b601853ff3c69649ac09867bae488ac88045a0f37aa13d6b16f473d8cbe360d2508032b39f8d489293bebf6a3fd7c1f4b3a4ddc9bbaac9723d5b127d589279f3662052d91e27539ff04235d49ba8c730f7cb50f3c3f2fcad97e51b0eced9a8c6a7b0c0b7b829751fa476aa8a48649e69530e18f3f3a99e11a03c653ba485d77f57c0c867957d758021cc017c13c77b68bc061e52895cef5cf7d50bf8d50380580e92ccbb9d797cf59e6003d0ae149575173c761dac04f22630c6287b36f672ed86d032c56dfbb13a6bb200a2592826fe6beb2b72aea4be63fa5e9905f52a4972e9707bb894b623d319e036c74432414998dc39771968a677df92706ec50f0bd86310c263837a856a79edea38b4587217d8720e855a9fa849480f68cecce240534d01ad62350b3cc7a8e2a10485555c4835936c401012dcf2e7b44eedd0171c849110aee2be25ce9c20a692626397536e75a9a6e8c58940ce02adb82aa84454fc9b82453e443cf169b33bab94c1f5de82c5f0fb516bf5893265bbaa072f19ba2ad2c299b4327e0df183c0c76b9df50b5c064e7b23b9ea60075e1bc78b36aee8cf4130adc0ecf6459b4cc9ba33fa31e577832fb1cba5730bf9878b2c08ce4bbec6bcb28ae6ed4ad7d225010ec3b3f6bb4deb26aeaa10b1303d26a28a1c4f746ee6c042e5cebc32cb28f351512f836af8327068d631277707253e25da84c06266b9afa1db4288e385208561132a8fc57c8293521889024a0e9a2bcef578af8a6b5bd0220952722c4bda53ce9c1b0ce705753cccdf4436243f3b095b324903d8fdd6d472d8798382053eff536f9b10341475b587b71da205c8f4e013b00e773a2c620d219b8514e331587d2ae5b55861fa40b2b2a0da5175065fefc5f2a7749225f939b22c0a0f74645eea13ce4469a2620a6b5364c0d9f1e239c5ba87b58cfebaf17cc14ff413fb77a4e66babaab1cc78e86eba6376f7cdc670d63b55ae91b7f134294276d7f4bf992f1fcacdd5ee469c1a900560c0f098d220f006dd18c917d52c2b4047b2ce19045cb65c05bb3c00bbd716667529c92b78e03b9d7a22b0b3611f3272fced2c6e9754680cde0263418a1851c4c5b95a5e43ddd8d575292a4b30b07d51bb8d8807b1e18fc79130363eee76f5b5dc60f3802e238fbb837f982b8616b4723904cd94eb7f21f3995470caf6be80614a779a529cb6b61a566e430106b75a12b88ff67d8cca6420fed1c5ff834234244fe124536a43044ac4ea71682e7fae4225f5a6ab05611761d254db232c9c955f696b002ef5c943ba5901fe86cbc5fce1b567b96af4e85f4f949eb114136cd8d3d255197d71b4fd8859e29876e540cdc22ad4ad58c3d42f1d79fa4862640a6a171a607062559d7c8098f2857dfc74aa30eea9e00f002bd8f02cefe38f8a3aecc6624892256579e855574b9ef2ddfa2110af9113dce4aa22224e9fa8cdcd20445fa32faa2428a4422313c7136b501e13104bf31d55e27fc08f08a9982cd42110c98a970b3de6ef60a053bd33ad3897e480860c4b9d7cb2baf683496f198e71a0afc9fe99abf70de7be8347bb21be7ddb7075e559090c496c792024112125710aadade09f98e2d9db01150cd7e43e6b14d1611a99219b1ff4acc1718660b0d1ee38735c33a2ec24415596661b6d4a5de3f1fd7b99cc0a870bd39910116f228725b6f3855a3be544df094be5392aeaadbcb1753a39b62431b505dc0e64a5f8875a978b680857f1497e2b91c2ef4c9ea6a5d2276579cdfacca2796bd0439b9f7957be69779f3aeb30615b921bd0464e5a1f221db386a5db3df9b6c015ee85c58735f1b71ec58d38b201b00319e97e8bbe15a079cbab3f7c9b2a3523d17b6a4b47aa1c3a31bc848b5741add7880b2f9968f2501f4214a1580bd1d3908739091cbcf78d4876541d32ecb2328234e8f6674eb530326805a9045348d1ebfaa3bbef8ed4581a4046d215af0d8a4879b9906ca919f0660ab129c8f0222158318d704a51334136bfb8cfae63b3ccb3290f45c42837625d4b44902dddebf60d6d11b76a490b3ca02282b4715f439ffb856e7a1f1de48113f5c0b6990823f52d31476fc3df6063285dcf2db3878b89b191edf692263a80c283bdcd1578c81929fa8c99231f767696a7b0e5f7ff7d634149d25c741c14415da4aaa3eb58fb597e6099337804dac04be89ed1a9391a062dee176461d91682d673426b8fdd14f44b01f5c55f04c54f2d1a3543936d5294e9a1e3609e18cfd2f1456e92c5cead259ec3a6698bae4ce1b27b48126ab4a788bb7add48a8410431cf9b2f75bbca0f1fdc2772e02ca82987dd1dacb56fa669e1c110d9f08ec5a5c1643d7738bf4fd7676e38851f604af280636e1a5d777e30c39e19a91f568c05e1335fdf2a0910a0d1922057b6dfd312c60bce9b5b8342002c6ae53c891e88a66211d216dfaa0e0b664c5b61b9500a337898cfeb2a7b12341988af6c33f64ebe90f33db573e5249363801acc945dd968fd1a8a8d0cda2226296c2da586430d7781c8d6e79fc06bd4e8b54156833aff96aa7d210fb588f0fad42dfe95399e31643182a92de6c18dd3cf83a20d85ee618c086dc35b4fcc0bdd0a5408211fd32bcaa28061e0669d7103f1a998d8e2b954d3eb7263ceb42fdc77fae34c6caeeb0d08701741e3bdc002a1461d49bab66eac1e9de6bffcf041a0077075cc88fb0370f48237737cbfdf0e469a2df898c98d3061973a0d3773002bd31b4583fc824b80ac70ef469445b8fe0d0ab3dfc1e57b564a682fa16d3263fc1b58132d5adef33814f05fa48c12532fc3053a2ca3a1a3354158feda23a98bf49acc4e23f256a3307ef16c993ca9a2cb82c0f4c2900da26f4c0652f5bcadb7e2587e3355cd3e350335b15fea311d16828a512b48697f18dd04d0fe71a8ef20ccd75531d0182911d3ab702c5cecf6f4ccab9f416b04208963676003364c3830f8de55b2f11e28a3db5292e391180c507225e8c6fb45bdf7680bd3e66b23bb6715842eef4f17a772615af2e86d46be4674fc24c36c0e08144f6d912b889ec62a60a02ba885504395c5b61250563ba7c95da8cf5795740ee13c126f3a6b4a223ac54646b3a9bb33f6e04ba11915729c6821d02ad7577d896d31acc69a79709ad730a4c474d269372dbffb61cbfda0a1518d0240b1655e212998afd77905cd26211b6c09e45cb87628f99041418c4398048d71275861768f05bc8c509a0fd5d39e16c07c0326f4539f546a22dcac3a723ef933423c93cb409619384db46a89f287557c4334eb008a974c48d0d8f5c8409bf7492ceaa8f87e755d57e93b8e289dc1684600841a07028189a3b228187e8b3bd9e33ba2308a99bd741d2b0d9c49cad47df3eee7d8733ec5b260002e0f1bb74a35dd0f4be3b28a42ef78dbef1d10e00a18c2550b669a1a68945d9136d9c8513dfdf2cb56bfe9d865d33206a6c815998c83dfdc2c15e116c53dc6308a60b28b9110b46e57195aa6eb24c4c55fe2b031d252acd2c64eaf6ccfe1ed22ce490eaa2c946365ddcf2bc5d79be39d129b76297788ac9b9a2a58246e9672f2fce1af89246c35623421f2d0473d9f207713fef493508fa469cb31f9ac6eee80b029b5708299dfbe8bd138c68ac8f62f248062cb918693afdd2d2c5a5abde1808cc3a2af2bc39cf5b9dd252a51d5dec44ed148544b4be30f3bb964fa74d9c7b2ff77e0ee787b99832b79a6dbbdf56a02cb62641e2a74fb75e4690bb0ae9503f2b920c4c95002124892225abfe3605bbb52121499f267fb8c0e8f47a5e5f8a570236c6c880bbae96273bc25bf40250297b4c23a33dd395b7a408e571e19b1c5421c28e69650b34ad799f6128f8ffce5a99956d1290a0b71a1fcd52d3d47663331a111799e6a7e52a2efb9f7fb284ba150006d5782dc12cc0f1558c35e6819fd202d0c2ab7e1d96d102f5701aac2e3f656ee77de899beb76059c2891ed00b1cb0083aadcdf2d94129d842019e49d149ca2219609effec8f0c45df4c428b18b8810f80a28bb42c52fe911456d7f2382b3789cd192fdaa175349b606a23cd79c4291df104de724ac402d14fc2894985f11401b0f77e7650ea4eb480558d59533c79018795ebfb0075a1fc1cd6a2ef480cd0fa8272091dcde2323bd7d879dc08ebbf18955935d19d60b6581b531bdb32417e5475a531f9f7e70d0ad2d992348e9d0b2e93dd96268a231a6df0897791d9e999a3b631255d9bb7fd46fd670c9ba11823303fb923297dcaee9ff4c6d6a3fa8fce604cdc317deef5081d620a7c9824674a2c3dbc2ff52dad283e2ac7b308b256862a1165b1ec4e70a899786b723ce7ffbe1087293856e146460c9dd3338e72898b0bfa8ddfeeaf1eff6104b3a72e10bbca04ef21e8a76d78ba4d295fa078f6728f050b613b702eca9e819f29e4718cc16f229ec40e03034ddd98f5ea5b5460fdad1197105247a93a580eac17892796c30514ac34d6fe1d30bad145a807c00e67e5a55f656d5a4a228dab5cb0af8e418fbd20aeb5dd5e66d84c17128eb5b26eaae2a887aa8d6f5426ff16f375ba68b3b96e0667a1392b3f9a1e7023ef9321382a2cadbb45be0092084f8145f6a863e47ee50db616288376906712bad6b6a2eb5707def54b055bd5bf501d83171316e4596f2c22a3c47f07ea06af8bf577aceee172bb81221fbe4e7c2161a08213d8c23078128a186d4281012caa4b456bf64e13a5b51980b5fc0269be0771bff9784e47220c0f27f8a4eac4cad2ffb62abd2e92c0973f9e504a15dedf46620215df7313a91e8817cc4c4c05d9dfb917b855c14b56dbb0984e1533330c2e0ec82b9f1d65350715d5debd76b462709ad22158262b4ba56487ac1c3b73cf7dc065703e66f5de2986e997a04771c3c3bf005783b58b4e8dcdbf194d24cfa13cee65f8956ff75b13c03fbe025beb2ad7c27f983542f226100a12833dac04c23b478075861d72e506228f425719b1402d2023b016348ee6cc4df37be3815bdd6d62d023296d10e515a8209a3d5f9beb3646fb797ecb7878263adee890b2c31bfe8703062928750a2bde2222eaed039b3c0c9c5134527664473acd03dd649dbd7da3266c395904392c1a2c978f20b53f8efe43542987ab99d7407ac6d10a2ef60e2bc342c174f2401e4805f6c7e641482bc69e5e4697535110a30c96103912ed6d55ec25972e06060b16eef765e7db75195f058b7b1a9cebb540f2e023ca55c972406ccddcc146292daec76c9639b4a14baf550abcd89b7c325663f2790a5ef375977d289960c0d80d6f34390e60900356d9c0e034d9a61796c19e6984db646db83958ebd5fe02b705aded44e83f943cd994362fdeeef9b4e36f9cf28fc6fdc06fbbeb5294cbd293110ffb59e8e1e2e03a3104572650425e94f9e2e9fc3bd902530c2a5d2c9583640722a975e482b03818998255f6d6ea570155a1f86f4b0291f16d08cebe3fb70c19944eb81f63ef7d2d4b3109a45bbd69131b2372a362be99848f9de7280a4c061d7c4dc7a3c413fb664fd848c47ab4c715a1d524263a811844814af8f60a8c2f205ef74340fb0db019f748d6853f685e15cdafc6c3a70ac029974f5f9f21d4c2061d020bc99f55fbdc8822b44cfa382284a58d32ac54256e55c5ee822eb2accb0ceb6f74cb1131fc8017d58a80c5b64d18d52472a7bcae055c0875085e47590d07d5f51f64d91a6c82faa4960da81b4329da5f267db61f3a04bb4d28b748a46c21cd6a5f4d14a5de3d0fcb0ea9b6fa15d21420c860c7296ef79784b2d1ea5792132a3584c36e551ec0fd14863935ebdeb32ec4cd150ab6d76cdf9a7c7793fc001804f66bd236b02297113e27cadb2c56e4021e035dd5aa2b70919de3e5a41de82a8027594ea1b2c18a192095fd8de2f9a55b4bd711669742563729f5e38e3a6c25734b115defee66dd01e23f14251f22f5201a0656cb232f656ca6db0110a4842d85a4125d726a52bd9e1d1ea76ad0385706d669cafec0b580505511556d61a118ca3e7955c38484c84d7a4372d1988bf8ab4231654f040d4caed2850a2db9d216000ac7703e3bc56a50837b8f3ea62a355491a5d1dd721ac5ed44a3d52ad0f6061b07d159022b0812de355f520f4326c83d8d444ad67b8e9fc6c44de299478d172febccac587af4681c64c0c6b66d815cced1a6cd99931f0daeefc060c69e7c04dec5b478e77035233b22d21503ceebc5df4db7fff79ae6ce70bea5bc71dab6203acd627f404eda5681f7ff703acb18ef7da79c10c0e5c01fc010e877c837970813a2aaf89a82f8c07b06ec68123393f712c3c87b61141322a8ae5454f5d58e134e2ccf02970660b53d06e9b7f577c73b4531e7b2bbe57e1777e772035baa2f2e506a055312ea339b1494030371ae9b8dabe7c5d97afc1d9095b11e0a91c4a3009a6a30e4c867c7419e5d9d73f4b8305b7f5f6024c13ef3f0662593f61e10a828dcb3c604463006bee756fc4444eb75b51ce3e0cef07f60bf65a0fb8a2bc36a25390c7f8ce1552af2ff192ecc2fccea9321acbd9668eae05616a42859d846cc05c9e7ffe58957eabb449f225558400be739cae0084011f34ccac0e9c552780d13f612add584b76cdf4382b95af0add3964a6cf179918a9ac19065388baf6cbd04bd234bad5d11a69c81753b511ecd498d6e1acf16db17577bf97a4c9abdde5506139e0f36c309548d1e0938a6ed1256f5c927dce38ce9655dc8c9424edf5e5024c8acb60a6683752318231a905884c7e1860c1eb7be9261b2e268578cc201061d5b3903a64ab09604e123013f2ffd46930b46421822a2f9eb4bb91a5de2c05f0a9e05732a72d6b75ab065830700653c1ac9622b19cdbc9d490fb106f85f8e7f1721fb170c7cfc6a7b281bdde765da23c134b9f0327d0b49c1b10d5f543df0140987e0db44abe510efb67fc7f4d455d922a8ccce777775b380ffa8b15e9c086c0f8da8e1cfd2f177def6e5b543c20df1fbd7b2a1df2484f11291c18754e3a73e2677b768be4aa8fde0986d2603333dce2bc54030733c07d5b6f5b4f32f8d097b28089a929782f099f17b7e80da018be4b7e88263d90d0ea5303e431087fc6e472a4e2c89138c52755839aca9d3e7a7d3da70c5e3a6c656884d23c2cb1c1ef9a71cb37ae6f3fbfca2bc81327d9d6c0a9b34ab22034aec74b0d4fa0369e4e21c1e1ccf46df7e4ede39c119da00c5d7fa4928211ef78f6f5491b7faac12bc04ae782578719179bf34c0308ad751a267e6de6d3dcec4ce0ecab13ecad7e2d0ac534c1c89aaaa7a0507e8e42c525089f3f10b311d46b85358729b1152f072528bb58530283786b8ffe4c9386c11b207f73a91b6c86b0f72c97d2f564bdc8afdd7a0ff018437f7d529202134546b527402941f2e34195f6bf7a2781582adf8000fd10e205c76a3181d33969abac845e36f8c4f0e50f2265c50a0fa78cac2cf1434f808", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000008f1d4f8158740b1b172a191da848c84600000000000000000000000000000000c1112a69c9fa3119311cd7b4b7fe583701cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } + "total": 187152 } }, + "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.071508500},{"label":"Committee Setup Completed","seconds":20.228290750},{"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.211371500}],"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/circuits/benchmarks/results_insecure/integration_summary.json b/circuits/benchmarks/results_insecure/integration_summary.json index 9ebe7317c..ddee53b6d 100644 --- a/circuits/benchmarks/results_insecure/integration_summary.json +++ b/circuits/benchmarks/results_insecure/integration_summary.json @@ -8,164 +8,164 @@ "operation_timings": [ { "name": "CalculateDecryptionKey", - "avg_seconds": 0.109601222, + "avg_seconds": 0.111521499, "runs": 3, - "total_seconds": 0.328803667 + "total_seconds": 0.334564499 }, { "name": "CalculateDecryptionShare", - "avg_seconds": 0.606677194, + "avg_seconds": 0.610321888, "runs": 3, - "total_seconds": 1.820031584 + "total_seconds": 1.830965666 }, { "name": "CalculateThresholdDecryption", - "avg_seconds": 0.556015083, + "avg_seconds": 0.559050209, "runs": 1, - "total_seconds": 0.556015083 + "total_seconds": 0.559050209 }, { "name": "GenEsiSss", - "avg_seconds": 0.124351986, + "avg_seconds": 0.124032833, "runs": 3, - "total_seconds": 0.373055958 + "total_seconds": 0.372098501 }, { "name": "GenPkShareAndSkSss", - "avg_seconds": 0.223574569, + "avg_seconds": 0.226692527, "runs": 3, - "total_seconds": 0.670723708 + "total_seconds": 0.680077583 }, { "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 8.412974209, + "avg_seconds": 8.500284375, "runs": 1, - "total_seconds": 8.412974209 + "total_seconds": 8.500284375 }, { "name": "ZkDecryptionAggregation", - "avg_seconds": 49.375812292, + "avg_seconds": 49.366586083, "runs": 1, - "total_seconds": 49.375812292 + "total_seconds": 49.366586083 }, { "name": "ZkDkgAggregation", - "avg_seconds": 20.649942958, + "avg_seconds": 21.116986167, "runs": 1, - "total_seconds": 20.649942958 + "total_seconds": 21.116986167 }, { "name": "ZkDkgShareDecryption", - "avg_seconds": 1.465726409, + "avg_seconds": 1.465883083, "runs": 6, - "total_seconds": 8.794358459 + "total_seconds": 8.795298501 }, { "name": "ZkNodeDkgFold", - "avg_seconds": 62.099165625, + "avg_seconds": 62.328322972, "runs": 3, - "total_seconds": 186.297496875 + "total_seconds": 186.984968916 }, { "name": "ZkPkAggregation", - "avg_seconds": 2.150821, + "avg_seconds": 2.200691667, "runs": 1, - "total_seconds": 2.150821 + "total_seconds": 2.200691667 }, { "name": "ZkPkBfv", - "avg_seconds": 0.330616986, + "avg_seconds": 0.336088264, "runs": 3, - "total_seconds": 0.991850959 + "total_seconds": 1.008264792 }, { "name": "ZkPkGeneration", - "avg_seconds": 1.356784125, + "avg_seconds": 1.351367042, "runs": 3, - "total_seconds": 4.070352376 + "total_seconds": 4.054101126 }, { "name": "ZkShareComputation", - "avg_seconds": 2.679413639, + "avg_seconds": 2.682164854, "runs": 6, - "total_seconds": 16.076481834 + "total_seconds": 16.092989126 }, { "name": "ZkShareEncryption", - "avg_seconds": 2.500371689, + "avg_seconds": 2.506225536, "runs": 24, - "total_seconds": 60.008920539 + "total_seconds": 60.149412873 }, { "name": "ZkThresholdShareDecryption", - "avg_seconds": 6.122451375, + "avg_seconds": 6.176445291, "runs": 3, - "total_seconds": 18.367354125 + "total_seconds": 18.529335875 }, { "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.101859736, + "avg_seconds": 0.100550749, "runs": 3, - "total_seconds": 0.305579209 + "total_seconds": 0.301652249 }, { "name": "ZkVerifyShareProofs", - "avg_seconds": 0.215221433, + "avg_seconds": 0.221828033, "runs": 5, - "total_seconds": 1.076107167 + "total_seconds": 1.109140168 } ], - "operation_timings_total_seconds": 380.326682002, + "operation_timings_total_seconds": 381.986468376, "timings_seconds": [ { "label": "Starting trbfv actor test", - "seconds": 0e-9 + "seconds": 0E-9 }, { "label": "Setup completed", - "seconds": 3.048507083 + "seconds": 3.071508500 }, { "label": "Committee Setup Completed", - "seconds": 20.2399575 + "seconds": 20.228290750 }, { "label": "Committee Finalization Complete", - "seconds": 0.006028542 + "seconds": 0.006707541 }, { "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 302.757272083 + "seconds": 304.143739958 }, { "label": "E3Request -> PublicKeyAggregated", - "seconds": 305.289772375 + "seconds": 306.698077958 }, { "label": "Application CT Gen", - "seconds": 0.309189375 + "seconds": 0.313315125 }, { "label": "Running FHE Application", - "seconds": 0.004053542 + "seconds": 0.003688125 }, { "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 79.623283542 + "seconds": 79.884513625 }, { "label": "Entire Test", - "seconds": 408.526410584 + "seconds": 410.211371500 } ], "folded_artifacts": { "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000c231c04d197a8e708000000000000000000000000000000000000000000000004dff372f7e0128d790000000000000000000000000000000000000000000000075b49d509002a176d00000000000000000000000000000000000000000000000000013555c49a6d66000000000000000000000000000000000000000000000000e6842811517da5170000000000000000000000000000000000000000000000003669759c5c8027dd000000000000000000000000000000000000000000000005a31742b4a58157240000000000000000000000000000000000000000000000000000e79318c631dd000000000000000000000000000000000000000000000003f855d900f178f79700000000000000000000000000000000000000000000000660c1fafd86f70c7900000000000000000000000000000000000000000000000992e486dc1bb3e6960000000000000000000000000000000000000000000000000000e7f1f964c980000000000000000000000000000000000000000000000004ffb738420181a4ac00000000000000000000000000000000000000000000000f05f675e4f309b2960000000000000000000000000000000000000000000000079ba8c5829d9868b700000000000000000000000000000000000000000000000000011dee0a4299770832f879c803fd7b787b6c5ec4e1e0714f5aae691b0542def35cff3251fdc5b30f8d12e5ee82c4f7d3164b41bb4cdaa80237d61a4cb9e779f1462a241f80f1ac1cd23d6f8490793bee73dc357175a2b9ebeac1e5975a21e290d819a4cf1d9ff01eb1cbb093ecc02906356b0797a42cb304014e505f70f1190991810dc812e176297a3c0bad21939a9b1cb32dd6b82db4643d1eb8aec6fbc56dd93fde0996960c1cbdda9ab25913b46b163d4a332e799d201d1cdba04297f438ea4a4efd17ba1a289f4af62ac367ef7ad3197f26868c463bc7f7be75ebcd1f4cb0d8af9ff494a9183660f437d662a4eb7505bdf9d193fcf0c08a95e9fa826b850ae8419aab4e8a25f55283723fcc95ac9eacfe4409b460a0d1d57a6d77f7b524c405002c208fe520bbe73a3e2f784dc967fc117d7f6d480ac9960bad15f20b7ef9103bac9795b123787fc7493bb5e8dd91740730cc2c2acff5fc0b376938de79e664adfa8b9c1f2964d0f06e4c92e958f5fd77a5a49f0d4158de25b55f4a022794d66fcf0d66b61c71274729412aa51cfd6caa29c854f02b0f6381c8969e2927833619fbaea251280a6dfb0c2098a8eeb87c9606c13dd2fdbadee2822d0d474c2c5b2d29a7db431a3377b08f5f1ca652302d0ec96d751aa03a143ec73e27fc7287749e48007f121d76c44c0f508cd42146d4c85da1ba7f0079d44f7ec8d86385facddd56ec7d3b2c2310d8d4896a745ebf3284b67d7dfe6a70bf912feda40d6a7d735e2c6917ed0c4d75fcecb16b258dbfa38b206c4390536979a9fcee7ecbad671b527245fa30194e070f0536b206f4728be22149c5043f9162a2676b1cbbf5970efdc3f09adf15c02013d398517db992673bca32c394646855203b7e50f25af4e859fae1186d10b2529864fb2290e037275f80785700c29a8df6aedbd6742ba5ea46d6b7452909274fcca2b7ab41d0684c1cadebaf11593d6765db6e88be19eaca96735db527250a9d92a04f51e65149dd9b772f2e4c77ef2504a92b0ae5ef7216de2dcff50419e13a3bffdd680ec1c60b461d72df65afac17828feeda81b9a2441f16480b821b5d909d1c8242c008e6b4ad1af1d798bb41a68c767c9b3a45a60871364310b517d0323cd8922d5db1fd5bc98f56ae03e83eadbd0a64b1a960057fecf780d189106993800da22c6d624fcbb23dc2031f3bcf55b2350b901fa3b6c9598cea064913c140a7440455db00bed4312b796f57a058c03446d58c84672705a1af9a15980262ddcce5e03fa8737cbb8d9b59edfb4bb0376593116b6acf935b6ed928d1b7039574bb7faa48a2e45b9abd1b95ce656a2c3fcad52e50644ab4f430b092950713a9be10309a139e4114960f59fd48470ac69c1d3eb77b9ac02e1be09eb1eb571345da560337d679e98eb65ec13e231b25ace4b4f4327c012879a4eff8752252073bc94743f68c6488c2659ed13bc4777e9b31d819d3528d12989616b713b2a21f4561994e2b294dd8451f4d34e7f17f37b31c7659e8ccda79528830ffb2d8600f7028bdc8cc1f247db1c996a7c683e4ba8a621111c4bf18c193aa299e54a818290a2fa13fc299ed1c9655ffaa435d5581e27e321594d33c84721e01e4c976fa2f83abf8d7ad3101c5575de5ed03233a502c87ceeef5c8ac9c4ae7ce55e5a12702c4a8dc4290c18ed54297509ae69fc6e10116bab06916be62c1b66d4c31452e28780558518a9a0677de43d2381aa22d4cb1e05b371a0abb3474990c81e0045a1010f281874e4dc7bf8f4c266bb74d4de791f9444d6a979df2930d4ce8989d5f134d2b517b71ff043acc1e77ec06863b770df40820833ee29309e6d5940c98e20a46b2e7ab26ed6e58880c0c757d00d9e298b2ff637f68e3e0ae78d362684ac71f4ef526adc5a9362aa12348d54dad58410e69e9998c043ef9cbb9ca457f367e2b540b26156b565d77217132ab2cda06e373b5c8e42fb1c549218db94a975eaa04925195341bf71bb44848f66b754f8844df69caafefacf5a80fb7c48d3541b52a54d7f1ff24a7da4cc29a6ca8c63a6bf1f142faa1da9ffb6cbf576a74f1665a2f6cb8998e325cffedcfb1ff8ae10ddbf30553384be2327852ef6cbddbb80c031612944a6367ecc5ddec0bbdb492409ea8ee0b749023820855af207f2771efe415357d2e65146970c4e134ae290cfa6c78765c9f93f822f7442f38be924feed3075d0f4c505e8f412cd52430157769957d983cf833076ac64ff6cb3089a5c23e2a4b7311a60385b29b440ff083a03357b30a7ce34c506b724f7491d6397a5b220bfbb4078dde8510b9516907fc0e26a5249af99436db17ddf3e08b63330a4e1f2fdc4166cb2192df0fd7da156ff33c342789ebbaa8412c362670a8e1d1e3660d04c8ee09dba54e2480700559aaf372ac10b3fa798957ca140d1293c4ab7fde740b5485945480745e0cf536eb74a1b75abf1f942da1cc613621de9fe86c60ab972f724566c306515796c892b36d522b92c2c481fc29f1e711953987999e3898ae038f4fe31d52d3d91f94e01fbbb0d0101568ef1b2d02bc0ef78877e5a9e51cee134025275cd9aa251def979540cc5bc9963016fd04a56f42ea0c384d20bcb51b2815de4721915d31b83934288813e0914eb9388cd661dab0fedae3594e84ea0519ce06222d0c01325eadb2239c4281880b62af0913eb476a3e57fee4ef2564fa14495d5aae740b0a554e1b0155f6451c2bc68e136ef1b76a6d538cfdbbd17c5c2d8ea0571306bfc84903b7de1030e1596d801b8faa51635ffcf1a4c40b9b070f2fa3fa1053239ff9320d966969c616127cd2fc20a65986161a03cd6c3941aef31341535b0568b2a713c86a642f4da67f3141252ab971636722ed12adb2a793c72adddf418e8586058ee51dc6f148ac40c0c26dfb144bbcf5d307c72968ac60f7257b9964070a64942d647ee644678caca09ce457f2e613fbc10526152798a5f624546f7fc22bd2801f4aa585d2483b8472a4b3f5af24b3dd476e671d3cc2a52f05bdd9fb07430c3cc97d6bfb0c577b2f9e8e50d6e1fe74089ebe128aa657e68926183d6cd87dd0ebd530920013a0a301d505c47262a9edebe462a0ac28c6cd2610b59a1787db42188287610d1cedb9df8ccb0c33e846116c2d571ab84660a728198a7806104f6e505519e52bff03fb9eb0a00256556355c4cd7dec17af7147dc071beee6a5686d1ba5a185809baf446d80e20cac33cb693da5da0b962d2bd19e017661aa136cf73ed71baf62505bd5afa4d39bdd0684b582ee6e6f4b7c6137a610b8a5f142ac1cbce5c02b9d078a10b567ecdeea8eac300fbcbff1d8f92d75440396973f60f9c313f2fc46ac28340d60ce96317c2b1fa08c2604b0d939d0d18b1152f44f71949e022b665a3915226bd97337b1873de651fe90f3e70a3cd806f21f82d88ec05123bfe13d32b3fad2e2f874ce2a4ca42ce4276d95b216d0d11aff23e9053c45f28d1ca75a522049373683eb011b3fe4eb644b68274fd6361bea130bf786cc75187bfbff53c6c8809b4ee3962dd87ec04aadd8440848869d77361924a910d6f25dce1bb4afcd04c6d823db6e94f29baba17e10f598331f4db5492a2622397ea1c9c156db10797fd4570223efd35c0fd70bda70794e5a28ff326d03194c82df5490a622bd475ec3b6423cb73dba4eea28b6cb978b7efe60b0da77f31c2adae631cc4fe8ecd22f605c974bb849132a4b3339ce8e68c7a6578a3acde71a7a2c7fe23193fafacc18024a9313cf0b126c6d7c58f503803adbb09809b6090818993e81b3dbc750cbe9ccd56a01cc9266bff7289e2a08a14124af6bff64d9173b277e654acd6db771e75b5cc586e8d8153aa7ab685032632cce7af6e17e7d1f8f30910d5e7a54c8b1972efed7d2f5e1f6aa76bdcba76057cb941950523f532d02800f31fe4bd26167857004a22b83585847bb2b97b487d08a0a164c301c610ed2a41c62ade87f7ea3e1376bfe73a07e8a98e7a68a0e59fadc1beaf37084b302ffc3df53fdfa8cfe80b5a0427f8024d1450f11fa7f2747efb3d30c0b140fd31bc3a87fa0baa6219996c7ba23057aed724685d8806b849f935bb32eef4a605605a81dcf2249ee73ea621b56f520e32aa0b6a3d93d13f29aa0b933361b7b4bc415b2342befe6057b29f81986d412861cdf37c13ce9e02f395067e17eb426edba05ee1ad79588ad90e65a4da63c3a0589d6d7c393c35e1f9c6026cd599c3df53c2f63ca034855057d89de7932ed12821db83c79ff9010773cae04fec7b850ae3f04951f33d5cbe2f4a05d9b5e3369785b1180c4b645c13a811156e17872ab15420d6095cbc95077e025a6ae3cee3fbcf97a3b552e6f10b2903ed11f88f60b4f3d1923b42010f679c50ea946e9ea8a8332e59ddcfa2f9d68b260fbc8b81452adf41022900746861cf9b59b1719e2eef8e882e75922a6be8df309d8694d5ee46d6a10e370ba867c3128ca6dc7883414ad7f3a6b3b01479f965eaa0247cf84938dab303d0942d69328e413e0c5818e8b0d4f7ce1bcc11f65e539de2ed2e3fbb46e4e25a36903fd8576aba8efc706cf8722171a2fecffd79a415a3d3645924c755f8817e4cf0a30e0203e5c32c990022b3ff687cef3c789d018875fe44ad11240af3a1ad14160b802d382e8f576b579de16820563c4e212cf36752246772bd379a5110ba9f0b8e02469515c2c9676c859356e0941689efcdb6a2241d0ee46b580810410afd610f1ee5c4d2965d64153350490ca842b210984ed286463eb79bcddeff41cde9fa4367f246608dfec52ffa6ee6bf08df305c33e96e22593896ffc1e36230ca2aca0629659dc3571f0be7e809e9a510ec9a7f0c1723d69c985600f3833bc02fe173deaa04c41f9b5b1f83ea37d42474dbc81b94c81b05a64581aa57213cc13165676762c6ad6b51ffb3a73f1f10060358b19de35c6d4babbf829b224e95303d69362694e0ac29cc0fba636fb26473b14fc238ac027dd77f8b677693a5a571af4105d307cf0d0696d37f321995ab288e77ce09f4ddc73a252c300d4a00c981b554dacf0415b8da77fa1c4dc28ed4dde3a3573c6c3e7df3f2390d98087ba1d2a97806f0e9fc3c1a16c64c35704079f146cffaea0ef7314b90d21667bf209570875505253493deac384d5044c00e94d50a15a130f8362084459e05f3bf69c982a364cd3bec37ee44649738d102dce02eae15c9133963dc6f00af8027b41704b1e65d7c2c3c445743491863faacee7a449739fa7c5faef8f6fa8091c326f1fd12a2320f689d0be3d099247858f481ff84a1c3a760f54fad6eaaa7461972bb5b1241c2e95617f5eb5ec64c536fd9a884a1d21e74b17d1b6fedfd6524f370ba6b023f770d9877a3968948bb8622d649578f5c39217b8ddbd83c42a9f826848f6ec2c4a78791b282fc5551bad0e1b3f95f63b2ea587b46c4e4b4602fdc1f24824910a59a9a3b3f2ad2b6058ec542fc2b40bf67fe3ea4c4ceec0f171a1736b0c63d901bc7465c249d7b191878584f5254636ad94c076ecdc7402eaa26be71dde4051302af10ab5291187ef10822091e574ead9269789d6132639c25627efd71702202d180e2caa6d96a03e8f02ee3139ba30e989586cab693a0a79e12fd162c12d432407c683bf99ae6a2cda59cf6c90ad591ceb18a99e3c1148215b36b2fb3e39f00486f2ed2caf1477061cb1684be5c51bcfb19901b9c02ec447beb24078171aaa2f64680388b8bb597288b662a0382edf3c14ac070737e86726b6d12c7c500ce72d3480a05120092fdbd1229001fb4fb81231535b9653878afdc3addff421963903786aec552940e39d9ff33460f74a33387f9c70dd4066697c5f2bbfd4c2efb11b86d2cb5f3fc4af8ceea1a93a1103922e43dd160bbdb8f161bafcc7fd31f8191505db967d01b7bf5e5c929ef3469c08f817fd7c7ec78261a5cf10d0c06f43810c2ef26ef7f2e9336ff587f6f8900096de08d6bf739f6313594db9443ec342c30fac34260f3c79034b64928e3081c1ee74b0fff1cd01f9d148601c02d6e482551800e080c9c229d59589ed97ca4af09a2608a599ccb54c19aabf16486ff4db460e7e1472fc208c2ca3ed2a948a06ce66ef010f7172df7bd2831fd060cc233dce03955bb32d144cebd97bca622b5ee2f2e6e8399a2adb6f538bd41181201b029c2f796924efa294745ad0c575cf542fb7a763c43db2cc32ba8db6f775b99b295020596ab671b0038e64f5606a8fa8eafbd10d29ab148e907904f73da636d63ad40a4c6396efab8297934902c7cb657d39c8dcf44eb4cb3a86c795841b0f93be581de8532c7577ec52f4a12537a9956b0ac212c03cf336c0bcf99dbd3b892e45ed2d2a71dc9d826fb9cdd91d43408791e5ec56fa2fba832774e589084086303a0c16ff05c6f5780fa20a52820469bf3b8978a7ed7937b0217479d86ceb9cbc90ac1117c686fe10d0a97288182e9ca7ac5e47384be383b7cc53854d7a5505c6dc000ba57d3623d8d3f995c14f756832ff184559ec8d627221a65026e964e2eb36aa09c11f60eee4a48a5a59465eb6449bde6db4fa670458d925591318b9082c5f0f0257ae6d8b494bc66a2443dead3e49bb7bed337edf061f17c49b264641fb59dd26984407e9accc23b4d11adcb0c10651e75e05ed573edc971df97827ef712b551513e963428dcb136b795ffc909f9c259347fc67ca0cf5d367fdb41d830d91ab02366b303321eb6971795d674b16d05de722e238f9f8dcbbbdf82cdf46e2121222aaa6f521143bda9fabc2237e2fef019f237d6fa666acad9a65f44943d549b515e85e088d8e3ec42b92d49a3994bdbc03ff1b0073d63c0ec74b9bd5008c4fa424585a1fc5b5b5ec1f6ff4d7ba9e1d0708efece885a50bfac097d50b0a8a450527f3d7df5039a8c1b83d0a7c217819cd3fd158d79a679145885730d2da273e422401b4c932c33726c20c3a350f4141a5665d3508cca37ead4b34b4ba27cbc6b9132dcaaf7c522f40b88edb47f434de442846a765c2fa47788196fc9df7c83f5b29ee831a1f7284e6f4e3c133df7d9e54e3ec5e0a7cb3127a1374c12ff4d616de163ab2749853ee6c337caaaf97084d29d86d7fa4743b52d7e37a4689f4a754c42ab7dd83f960a3e90114e53473f486132ececf39a29995d2a8174ae2b547852f2f30d4bacce67216597d1124fbd87ecca5dde59c0f22cfc7c6322f740a909aeb0a973c5b14f33f2054698e7e03e338769b7919224c638db7924a8245e308a43b1f85d7e406c8446e58fc361467f1c6cc21d378faab8ee790ace260454e153e6210807cd049974e1ef0c1cb56c32ba83314f282a98d1153cf2af5665eaeeb6a16139a127f61b33fd3faabb69d6cfa40f9329a58331ab431d59240d8bc2e966fda22d7e1eb4bceb911e8df376d4c050cf65dc124dc5faa21af8758a079d7c0b85b02b6c0ba4355df267525d12e8205f3c8eecd10356464704c7cace51d0a81e9640bc6d63255d3e7e5ec844e84a5230a07bb33c79585fd8267b76abe7150a786a21fd35591ff3238fca651f2d59cc90eeed3ba2253efc0f567ba0f8aba737194af1d11a2a63c20be531387940968da873cd43bd7dc03e3c1d3bc12aa2fbe99ada21e20e2b14daa35699764599763ce95a5506581bc494b271f865932bebec143ca0e6358610510cf53ad2fe1d5f61ad1d24df406b44656c61ed6e7205445faf3a918d391f98dd546d7803485b78a225a4ef29ff7599a16d288af013a297784c0b02a6fbd947ad57219cc3df3cdbf0f7d59e36ce25c95edeaf6245753f62932bd9502cac9c21e636fbcac7c2987640ecc38ada9a81278c7de458bfb76216033d8d313c337b1a163a73ddc3140db57161a4a3fd173ed6b24ffd66be49ff72c6c40dd0fb35f02e279f9f385ab009f147da9aef912956e37c57262166bef70d27b677f103e54368d3cbf57152e241f21a7a3ce73647df2544af9ccd49dd58ce80b31e8192a528fd130ff6b38f3bc4f495bda106ed59c6f3f0fc39c0395b4497dbd5eac0c6b1abff83546d63b037b05b68ef47b5f6b5d8b0436d38f372d731d6fd0ec472f39308f03dd5783ae424871ffc058ac46f5c126810a51504fea799030db97c6077deacd4c8da49de813a7dd9ebd434f32490cff962a047a55647a095a8279760d4eef1aa7a2eea2c7b8d80f82019b08b4e788b538de74f2ba002d414dd6abfe206033b26f0a38b1b88a80fdcbc1e8ab9f37d0de3ee6375c07915af54a986b712dd8a4a75a710c535434b496e4bdcecb0ea8f8ff48287705b3f579b2aff304930528e7d18eb79f7043c9a1aaa1e46dbd5fb9dc284e2b867bfa2c371bcfa0cff8246c4fe2f51460bde5647596fa9e4a8c6e38d838c8e8467b5a684cd45963b5cb04c2fc702e846d4eaeb30abf70328cc7145e757144d261a89ae51b5fc2ed58f01d1ac594165c1ddbed15007d392ab654909ba10443116328aba7c8af8b09265b27d7c53374a87bf6f98d28f04592ec3f7d613a50ef183784e7ad894d47ae991c05ac8a35b6d4307f7931256358077480d2eb99142c278440fd800d9fb8578bbc1af8eab305ee037541e604da2ed341479186d828627024d4f1363067737ee3bd0fc6ae2f1b556ff69d05c598a92d68f6d7953788aa43e8aeb4971ed7bab3fa7b079e231f452192c2b92e2cb0e2305eea4e2f2641a55f50be480beaaf829daa8a2c89801f65112458cd3ae535785e6b84f4430210b454a30372b14fda4e3393772d720d07044a0a99f8f8e247d152f8ec470c6eadf946e52499a5083db06d3bf62b632db82ab142bcc95accecaafa757e9b56640b1f106678cf8109554c8d80be21895139752ea939af20954934cacf705c99630dffe4254f701420309bfae9111a6f243f7fd657036f373dc43c281335fb1a17b843e61da43093df3be32aa0121b61059d9259d23088afb4baf936b92eee94156781c4e381c76c62791f879c0d266b33cff5acabb6b7dbc81d67a22cc97a976aa798d5eb86beaa21da26ef726617683a10d48af74af941aee5b015c31e7c4fb0af21f474a190944e145bea17c727ec1f4de4e389fed639b14cf173b7419caae9c3c33deb11806963024e14107520e3a6f816911548b98f74c0fc5eb6f384807561e18dd0e417c96a062e12453e0515c4ad0a7155e54724b197e6e200e3b61df77b12f853c8f0192e0b38e8bf8b1fc9f87ecfb8749f4651d29c764148128cd0172f37c559032cb1eaf080664e3f167a19ffde30c1c79b7e3e44f715b4b20d5e143ca7715f0300d4e7ee2435c23705f6c29d34d4d9bee5da3e6ac50d25ed57adfb31c71777520c147b0d110f8ba0183e296f88dc0d7dd30cc8c1a552e20d3484781fa7bffe789cfbfbc38252f357273d99464757ad98c4bd83924a89fcb690c3548c35bf6cc93722206b8aa7b998059dbfaf9b597f883df0a4e79ea7a87586c53949d42296315f262e19eb4443541e591182121a4d53c9b009bac47744cd2d655db9645f3b31b46fd1cf213cbe7416a3520270f458d7ec5604157876a5b81be998f854666815e1ed6792252bb2da0ab85a4b49874fbdf69d9ae78e895a82d02855d0025bf8ea19d5a7f67b63a52e0c627bad904e362e90ef868308717e4d00895153f9ba4f20568c3344e505d88b0efaf03ab4c593750c40c438314e010f8aea4aa22933f64f38aae2ac56c5b00c222ea879ad9a723dab3e3324fe8ce6e68b54f9b838a2e265d2a81d091f43a97b1218727895e7980462b651160b79beac0207bfa5c127701088140fdc4741ae4f270a6d0861d29d825910ec8efef266852ad117e8bfb72c4827be08ecdab3ab21214b427bc61b5bd42ff1f6e84198732bab6b043d8eaca51e94815db13c22a5f2178c97d3e67bb633138012c8f19e6edca36f28207be6fd25feb47eacd3f7300a12ffd16717ae1f87b30572f78fe8e9f6c0285864a19d52ef1416c6bafd296ee5274e16e37cb37acb075b842be01f7f577d430667f65154b11070b70437937270300c53983a317d20ef5892fa22fa5ff721fb9be17da55e2f82a64f5a3febf7192ca47d3122f9be516f7a7b8c2c02d0bfd706b6ca47de944ae10e732351daea2a031e416a38ddb62e1af22d03e222ca180a8b69288c999f3b851f5ed999b2a0a61fa27837d952cd8421cbc240d987a37155a8c1e5e1f4eb7c49aff109ac16af6d0784f3728889808e1391e69b25e095af7a018c8a81f1cefb60aee7375879697e14388cbf84fd159c7a4e1882c56bfb3bb5b5b9ac8db66038a0257e777fe0c435090aa91cb05b93a1fe40b21f5a9b40143eb498fb7e030e98a4b9d491ef20ae3a148193295c2c9dfd0c148b0d6e84fd8eb8edcd5ce1337a1998e78ab217561406166e870a1ff737d0401591c5a5b2f4da40bc19a0f246793f62b04bc5df66d6020970535a0d0e97b797d44e58f47891b11c30b5ab101d43e6413f3e5c048781910a1f8ef5c1f90a0c2f60909f5b5109b80e03613cd84103eaec0ad5cad363665e2234e51f0eacdc42f1dc6c74fb844fbf0d7e2ad23e9574946fdb9d7a3afd10910881c68d7025845e58e84554285ac330b2317fe4ea446d6917f98d15e362e1481d3d6a5c2067ff2ba2cd17fd68cd5ad79e440ec065e883123cb3950f2ac4b6480db142b1f16d08c353e2f557be1dc2ff5359e80e8efaf31772170cbdc1deb54d1ea5312f9e915c83584235ed85109d4a59bd9e028c5f44eaae735386f684a6f607ed78682cab8beb171780c9a54e3f57a392378b6051bd47cc4104dc4678c74f0300f5276b1f61679e9c481b80823bfe4fbae5082026c4996e61a319bc1274b00ea1dcf79295bd3c34f1b1fdb77357dea3955457b33db3393905ea35deb49616071ec289dae2a241d2d7715964b2db700909956a819439b1e04a7d1f922043f204570c529d1220e1c5c3a83350978b08bb3d65d6844cc1e615d634f5ae768f6e1ae5775ac84325b9a3c2976dea264f8bf3d7a1e14c31b9cf8d6c4855fc51c750042533b8db0f0ccb4a24085cc029702b9432137bcc39c18a492ce1e62f3c2393070a3d2b973aae4f5dafb491b7ae5e36230fe2c376a48a66f5d516352c8ae01c0cc43121e8b8010835398c313fce1f959c0911058646e807b1640cadac36369f203610cd91f6f96552cc2d9b85d3ed3dfee006928fc67518977a68ba6cb35135164cfec4ff35ae655e66f1586f933e88c2874350c6091f90a682e32c02690ad219264c0c4e73ad397e314da27dd9fc97d941bc619e13826ccb19f58d0651b693270214a4270e230bed23fd16705e90ffb3c8b9b7d9cfe03ee2a094a9e9b2909f2678bcdf76a943c56ca03fa8189eb05a15b10840e4f83e7ceaffbbf07fa822851245572d390a62945618a0a04cf1b830df30e13a4bafc8414b13ffacf3488a9725353f07f6bddca341c57e2a1d437a0fbba07c4e5bb31fa6c41d9d71fe038e6f25ad9aaca4bf67db38dc232dca80ac6cc577f2fe237614c0987331d8569772871276ebbcd824e2d6fdd1adbcedd9e14db8c44abec004b0ac587962949f41b1800c781b7221b31e4e3a02d40e9b2c1b7ea3b73f180ba5b1b27ef069b6f05961800d662205d6e426c30f893bd0cff9b606a3704ddc4fda64f149a583b5e2b1ec5012b58b7e12f4ea84f4f9ab698e625d09d85e8ab36100d477be566bd3aea6264500cb6669de3a9bbcfb18d17dd15a9d6827277fa1a6c57b83cc7aeb2965222df40929e9d749b07a43e36393a92da64323ff33bd3a3cabe072a929c29847450a431c403bf371e626651430d76e2f0f1142ce5ca04d5211a40a3eb5b522492293041a356d17586f9c2a30628c4bdaf8f93868e51faa16f6da0e030dca9a4952eb1c14219210c228064f8846d40e9368ffc5b17c70dceb3c4f13b9e42dca06c872ce0f6cfe8b8d14533b480f908e52f254f91ed84793aaf219683c7d271ab2778dfd30099fcfc1e4ae79674ebce5a1315298c221404b418707036cc69bab53c5313800008570b8cf57caf6020c9829afa07666b66fa1ebda11ced87b18a109dcb1b41aba076719214db7fc4b5ff92778ca481e89f912fe88dcd39ff7794fef20cd9520d52821d92a633a17d70485634a0541c0272f020df6f58af977b0e91d6a4bcf03ae248c091081ccc4e8fffc6a050e05357240b08c0ff78548a041239002452a0ca3dfe850ce5c5ba424344467ed464b57e098456fe57a2ed16b1317685f26ff0f5ed7b176e08e4d9de99f5b344a7d3dd3b0bcb4effca2df187a117c576443e01dd6eb7912779095f7972c7414cf7b72ca7904ef8364f53db792e6cea4abd3422d840981612e99fdd97f2f4389914f0f78cffa1d95e815ebabfac5dce2ecf8412a99be58b4cf34ac6f91f5b4a1dffd405bb782f6189d26f468585fd50761a8a52df5ed597fde9811dfb1d21346499c99146b00cdbb6354a8118f558295fb716b2a9723c61eb8b1427d6bca3db42ac998223f93b5760ce1e263fd58603f18e55a0d532e7cdf42645bdb8cad58eb88dd52f2349c626f08d2426a383deacbde9e251aa2b56f08f371c6d6917381ea4241973ccf1afafadf9e78745d163f0de52db4141c05e1a20ffa115820d0c5d3a1ee1825490c446493fd2ebc52cc34783c5b0304d04162c1609e7340f3c6559cdaef78739a3ecc0e1fdebfc1bd4aa2cdd31d3808275192c8e3cc7921c1fa351a66d0d8f43bb8bce2999756a00a2276d0af4f141934be7f31bde374351e50ccacc048284f6f6ef77f9ffc3c3a7d9f65b4d4b7db1e6e30581acdb9fbf6832fe4e12149b397f857fe633b733e77628b0bda39db6e16572f317cb53137f8dc0c054841faa1c0f6752757873ad0b2c78d29d619ff9d0832d2e39f7cc552cd4203cbb6ba9455ed305398629287b88486b0b914d0be2d1440fddccbdfadec77be5340a0f75035752c4ad485a9ce28f18d0896d358c1560f1faebaba5440acaa383fe25a1a1849a29a56a42d8fba183191983b101a5ac221dca03aedb73584a7be6415ff937bc6bcff8fe8595760af12db068187f52e571d88580aacd45b8723a5c0fb5609f14c5eba1afd3f40f5a164ee4b97a98a72c82150e3ac2ee4314561df798eb3c3a573dcc6aa766f26a84eb7b6e5584c3243911c09193d51120216d2e246fa2edff374a80c1932c39acf41c3e3e040b38373df1e28db8d4179c2cd8efd93091b611d7f70648e832a9301d931c14765a87bb97009b8844ca5d4bfb2ad0934927e11da2fd9832690b28b45793cdb1b80d9b6fe68273abded42460ce3d60f075af10d465818244599cede90585ac63b43c08d7080219e49ddf2bd21ef6119cb9d8aa31cc1f8141373ea1f9d490f15be7ab965c5ad290845de60a58fc9ca397be4eb86efaa128990d4f4441c3445a59e608a666bd10c0c0ce65aeca5e959fd32eafc6212421fcecbd3f10d6ea744d0f65cb21f162b23d804a8d20d2873309f646f8128fb9c9fcd02cca58845c5ec1ae55ed669bc891792260fd1d7b5ea3c3a790494b73f071fa0cd35624889e52c965e136c215e142cd89743e54a6b3988ef9852ca79e456ca5cc1afd24eade12ecb3b27ba89cf1725bdd58395c8594e6a8a01e4b388f76d54d35096af2a521220e268b0dd633f28205bfa81ec110c18221ca77e9bd5bf3f6d090e7b3c925c89ad60d4bd9abb694402a7ebea4ee8be2ab21b00146f7033fb36ee7c6dbcfa20b80d70e2291b46bc8d2761d2dc0d29d23d0cc7a58873bca8114808ac811fc3f45b25cf41787065af9101d6d2ce542984b742c097b92e85ff17d61ae6fd69c597b7dde659a2ea1c201f18aeea00abc08ad5a9d9acafb5316d1054655ec5a02bb2434946b584af7271ca1629090a0cbdf2fe152faf22486825550d29bd8b08899aa562b391cd47deedb726ba0d95a1020b5be53a375ca0c98024b3a961af37170f6b40cb58186c8ad79724ef8fc6d60b44f0de34aa1146f6933f964afc553538f3b2a2b7dc17a83c58701e902854b59f2a7ebe132fb19ae003a24a693386b7fa4d0f7f589542cc6c1c702f9f592898c4de2f94cd9a689da1f0855dd1d29def7e48a900fb989eb7df340e20a9ad75ccc9711d53d4142f02b635db1ad3458617e50c0f91ff6e64858b669909e40555073b0af9e9766e94492a6c509d936e6e678b19ce25fd5aad8a9f9dcb301dca4dd5e47a124900d4ba230002188ffd37f98c29cb0e2f7bd055376b4df01380a76e5ca5017b8b97d887490f494780633d00defdac3346cbce89045a83660e56d2e8a6d8b47211642faca1883a83c23045124d490d982c1218dea8f05e340f95ca48916da15d07fa5f923c3da0c890e36b26c55ffa2f8693485e5b4c67fc29b3be5768259f9af7c17eca51c8ca09d65fa1fb21db1f7c5a8c545b1cf8f78e1c14a5648cd566753b73c05c7b55c1af4005899b442e53f08669ac88dfc0eefc2e5b96c76ae4f5459241fd870544832c2a4be2954852b47df941e647bf7f914e1ddd6f241b5123b5f87aa912ff5c35d8aa7d645b100f1d409338cd9280a0c65515e673654f8fb5de1da42fb7ea5fc5cf1f5afdc4591f7f22f759fd8a9ef709230bddb28064cb8fe3b9a8806b987b14d8e58aaa13c86396832da0d2c440ff95450d3be24a3d6c7c976bc5450ef350a98f129882f8b372c714efacf7a9382285d6046fd45168becff5d64fbf5bdc12073731dabf923bdcab04f43de20c8e95fb66", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000fc4a275557be3acc7e37f1dd383752c0000000000000000000000000000000009800ccaf777e6cc629fb0e8d8ad1652511521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + "proof_hex": "0x000000000000000000000000000000000000000000000001104c69c76ea8f5f6000000000000000000000000000000000000000000000005c85075d6baa615c900000000000000000000000000000000000000000000000eb6de0b1c015f0f2a0000000000000000000000000000000000000000000000000002a0b5c2857c800000000000000000000000000000000000000000000000036176084b0205127a000000000000000000000000000000000000000000000004549377f025fd0f40000000000000000000000000000000000000000000000003ff07c8a94fbdbbd8000000000000000000000000000000000000000000000000000185ecac0421330000000000000000000000000000000000000000000000044f1f17a53eba29b1000000000000000000000000000000000000000000000004096147c1d40da636000000000000000000000000000000000000000000000007c2eaade9aae157c100000000000000000000000000000000000000000000000000023ccf2dca92b000000000000000000000000000000000000000000000000e02d12f90edb8cac6000000000000000000000000000000000000000000000007c223713eba54496600000000000000000000000000000000000000000000000ec928a9ef7dcf2da9000000000000000000000000000000000000000000000000000282e9bb83bee61a05d33c39ba3812376a494cad4c46e07e18763a4c0cc37c7ac2f3635530c6110f00693d937276e524dafd5fffac373d40ca9ac38bc4003aa97fa25c28eaae471e6801e127832204ea5a325f05bb8550bd90338e17663499f9908e07c1a27a4d25db068d58486c5ed1d571b154613b2a444f9ef333d6490c341531f43898470028e20e0d46d87d6e6a8c672bd88dbdbae7c0e0a9d06da0323fdaf7ee61ceb7611d45afd3c8ec55431dab1b693939c2a7f313b832a28959c3ae3d30629655cc051ff1a5efe96161c60a46f240cb6ca1a531353aec9b48b52ad525bf6481f17e0923676a74fc00629f08348ca4b4467e32b7907ccf75e78538096134b580c00ad016d8f24d8b2251a384465ecf7da74301f64008beb9c8b71af609a0085afa8bb10891080d365806d9ed9f21901108254290c057a4b986687bebd3eae86d9d84d52ba59894f06a73d164ff99fcb0d13873279ce4f307eef63d2c0ba38c4624e95e009c113dba40ba429e2620e10806283dd5fad8df1b6a85051c1f7167194c70620f370484b9bd06cf6e6199ccd69d2c0e0db004474b4ffc62084f7697d21fc04f1518380c30df7edd6752046611f209863dd69ccbd4af39b35e05dbaa35d7aea81dac6609d0b9b649962dd23bd220709758af7743abf5395724f809100dfd19cb13ee772bc80efdcb985314bbe58feda8ac8cb79523fe9ff183ebf3e6835eff81084ebea45358443421fb0f2d31a11171c0537eaf216aa68a5bfefa0d02b1c5b92a10ac9238f294ee18007a6f18f72214951c398ce84c3616bcf3c79e0f2e2f022a0c90527061c47a42c43cace4fc879a5a326863a6f82d9c6bb420290aa900d8244a98b1668bcfcb6023d101fde9f2fa579bbd8947480f7058466d6000b38c5c1a4fd3aa5794aad136c957e92be80cca60120b6d839e54f1036f9e857e14746f2433e79c2a2df9f7ea53e60405fe812eb086dfac1f0b0c638610dbdd208a28f2260c558292be011ce1ad7ea6fc1a18428a3199764be0e5e4169e261b655ffddd06ca1104a26cac71683ee832dcd3cac2ca05e81de30bed5f8b67ce620c6f88f62d90a64a9659cd90458edbd62bb6cc9f8d942669ccb243386ee53c938746a2462ab0830a5ee749ccf6259eb0ddd308297e3093c1e2bf8454dd5e900345907a8608b9832d8e40d08cb3dbf14939398e8c032b1baf82f3b45da26fc2aa8a69639d1379060a52801809840916499ab8679e0af7fa2b5e549f58ddfc8c60090ad5d42fcf59ab9661b29f6aee13a20e279602925e6fffb2328ef1334585e819ea8002085571a91540444e60b8c9350331f5cdfe8ba889466f4b26343d3b179b436d1015fdbeb6b1586fec95a850abe17e3d23dbb519b068c9c59ed740833e71319db516be4631e64a10edbbdfac8daafe9004a1dfb5631249e558132163eb6429c715143679b05e2edc9c236a9a7ca760761c3a117bb03755bf8d04aefc7742ab0ba811710087242ad211047ff4f6b9c4f8faa4640f53721a400815c301a8b079d1ca2b7184bc65e7bc3cd2c380df2376ec233411c4d429fb12795cdaa8b932bff94628703868796573c773ba5a60dbe2dcfdba86fbf73e2cbabe306511dd6f71509607ebc04687500585f37d8135ebac5386803439c01b36e449d3b029c1576fc2f422160f4fa03d8f11c89e134103a7918939563aad5285049c66b3479d416852b11896f8b31c6d91580c0cc03bd04ef40a9b201d686cbfec6170ee5a1aace7c7f2248c548038c030a7c267fe72915cd397e273c42561e11410672a1e41f71827eb247b679c4eebf11bf064f256f418ef2364b7eaa29fa9cfbf018eef1952daebd915d423652908cbd25b28b3b07d28cc918af5ac73226b02d6340d02ee74f643db15c870a4de33c870580fb55142ed86a7b1efb02d4f5d1d6e074137126f61818d0dce8e49b8aff13f2e80fa7d585aff6147ee7265bc92df749635d9cd77bfb87508e4dd08b4f437bb59702e34ec8c390bf6e7455a4619bb3586688b16e1045a9c0c8d0b760cf3d11357e9840fd3e702cce25045c7835bb1b0ce6275af5571d18d1e90731123ccd8bb5b7550b8ba901f3e94958acb3e9583067cdaf68cd913726817755888db2a7a28e1a4748b9ede248fe0ef76aa51bf8e5eaca3049ac9dd7984241212bdbe5b3d0d4b586a7f468056893fbdde4c94203e8ff8238fa1a028ab2b059669f73b36030f5205fcc5958017fd26f9fa8d827b6cf11aee31fa922e8f0c21c5cccf059ac7d29e54a4df6f1e43b8a4b22689859e8569dc98990b9fd1d1b2222e19e76cbb31fff18fcf40798cf42482bc063795fa19dfe65eb6e9a570b4c41e85f9c94ff56701adabc0cdb76def2f5770270aa236e1be1c18ed64e51cdc911a1133e096a0272e0675c680fe24f2cb4f9a201401977945b920b02e60c84f3309e8ba8688c6e982bc9357327d438c5d34986a3ee5c88fc5886a4c66f1d14ef625d0aa01c82cf320cb1543887eea14b4481733f213beaef47ad92251eb2910bb1c23b4c930200102e6ec848b5470cc88a0204f6d4a9aaa1cb29e808e3cf2cc7622ea3e6a331b26e1d2a78f90b99066780df63afffc89bb080ef954828fc64a9b0e589c3d02f1f8f8c03919178dcd7138e1381f54749a684c6c7f852b0e85251427b6dad8334cd11f9f2e16ab2d7654d1dad887e5dce9d872887c6dc9d07f45481835c2ae47a1c0c9b680a009ce8218506f77c3b8b62654b8fb93043f60dca8992c4ca79d52dc8431a240cc5d01a027438fa19c91f638b7cddd6db1a1f6faedbe1c7eda2cda8637e7e0a2f49d2ad0109f7a5bbbadf6c8478ba78e716fab26944e182e437f00afe8873f1030439bfed6f6bb3abc39bd1882eb2b6eda0078c680e924d942cd5b55449783c3a8f192e24aaa6c305167959fe1cf58e9065a7784325c2d0fe06c3e064f99ebc1570a82cd43c33ea443971e0f434ac11883fe84461b9821746609c618a3362bf051334253a22ae38e9b6773a528204c0797297835f3f61549c8cd8ad16bb415ada181c52d843d558dea5cd548c3823ac4e3d6df1838e0073102582f62d7dc0ac66710e9d0e8826ef77755403b7f12a2863faac6dfe4cb1914e29db1b956406e9f46ba2653617ac8ede05dfb69035dca3ee5c2d00d3a612ec25b4fe71e06659ef23bb53a839b6ee48c4e38207109e68d3e073fef16eb062dcc3a22a15afb381581cd7e9db2c78f2b801ef7104a5ffb41dce92caeebf6141190682aae5ce74e6b6adc7c901eb6db76c548746b9170a1c81930149032b19c0c0ab947223f7d82d1f7fe5930eb75cb6dd0bf5394a906376146da02731747ba17ee40b5061140ddbffbf7b01d0e6661cb6d3cca9eeca0a0c4640c5d61c72d9819f1b54c8878b1cacc853f1ca2e3ef455bd082bf5bf60e4b13ba7569e7804d5b1442193595af32b92e91b90c7f7c9999f7c665a9c7a7f5911d73d415c3339c6f0a968a68b6475c1dabe40dd46087630e1fabf2155f2017e4fb42180a3319e7d7063c027b22b6b8389f83c9aba5a25fcb0cee256c1c16760db9bdec0c82d90800249d026ad7e4fa1f8635c4ee66ac2ecc8ac01ec76d44981128e4000cd5a529b4011b6145ece1a1d5404a453d8f28fb6ecedf4584fc8a952cb6f9d6df93cafcd21eac2a8b63973d1643a07daf8ac82dd5b054c0d25445e09978693ac63ee2de6516fc2b9c0d2c59aa561b6029edfe22e85ec79edb34bdc7617ba11cf3d4c0f02b13c2eeb3c0faa2fd0592f8303ed4bac9e9f81e7c627493f4a334c539b8acd96f02bc66d56f149f0139979ef02b7f584ae703925e664182e410729af2b9675cb406169d27e032bfcc6e540bd4b7b1b11054f83ac31d5c5013e0ac3f6d6e6a97ec1b08b22c0d8fcec02e23c3d2f56227fdf155a9a7c363854deaecc5321bc94fc52e50df9ac72c04b4fb74cbf7cd86c427f9049ea0123dbabed674a4e2de9652290ddbf03a1835d5a92ae4ef3237137432ccc8146c33fbd50155eb4e68cabd0208196843c201f2ed0ff53d389b7845fe62eb1e873c6f5c9c1216ad8ffdfeadfb1718e74c50fe500225ee24b08374d2a8b7fc17d324f50220961759194b39f844b12739e80621a5960316675859bed49ede417bf5a5acc2e4b364b70bb92683986c1df944a9a3b8cc87e3951e156af6abca648b28be2b74e6df59df0801edc236b129ba40af8c1c9f93b26fd9a45d102fa7bf90e7f51f14dd59d241c4327642134210376431209d2593785a6a4a6f042505387ab87daf614bbafce0a71426a7e26d2ebe8f7a36e23272d68e4396afe0eb899fe01103e5190ad987834ca9971bed9c1030fa67d27207d4a97e2583383f5904f0a0ef868a6b8a0a2c85fe1c9554e620130e6c08293a578b5a28ea7c0021ceb00c5e4e171a08f2097216d3dced274a6407868f61297f9fe9e95cfa52375e1390c2a034d849bbce454638828ee58aec0b0e171dadb3c2ba34e649b6df427a1a22ec81e2406de5d9d3db670db9198fc1070ef752419ede5f9973c2105bfba9ccb21d5e02ea1e13024a8072d8d2816018970bb8e226425c4c82771c7400941b55dac1a82c6ee13100d8bda00bab5a6d4e0111f101af580b036e177b7e88d70ebf1f57d3d9869f488de7ef2e41923a71cda5153c0f99aa741088d700453e078bda0ac7cf72c4d40e5755f51e9f972d5193161ffbef1f56c0eaf0507bfe67e000aa14c77e2fc597669522fd7553bc3a340e622190378956119d6213836b644a25648d9b3463ff256fa07f431fb1254f38a2540d7d8d1ba4d4000d5470105399683fa97f32adc17549946b7d4e33a06085ea451bb9b0d600fe3202fa84f96bbe9895918438d9d23440f5d086cb4624eefc7a2c09554cefe010fc8412f7dea624ecba0b778302ee742b84e396dc43d3c76eb2b10567ee21da53b51452ef279344c3f4cf1797576436ef727f496641913bc0f28b2dd03f71184034d502788de0d7d8caa15ab98c454d2676e297017823c35c061419bd9a63ba9deda91b4a7f5cc27883af1ce981a40998cb00af959ef782a4971b2bdfd91e38d7045ff5b399c5c097e014ee9959edff45d11affda024527b1c03417b6609dd14487de0f2ab328f2c7066e2fe3d18fdad528fa097746a24139bfcd1361368052ae8ebd22969612d971a162c5cd161da46aefa32d4b540937b80ee404eae6eb13a39ce56f8fff92b54da51403fdfcf7946dd8a43ce26cdccee842af2f6eb15827f305af101464d910fb943488e0e5ba33853c6206eddceb9e2575bf15ab59f5b519e3023a368f7c1584b1f9e4e185f05cdf6b4ae976f075d4db94882711c2ab8e50b028f8f181b95604247e18cefb32c187176e0c4264b9d9e78f7c053277dd97cae1eb03dcf68ccb9c14d6f77256486d60a6ef3c8dfc54254ae5d317284ed44a8c2ef6ddf4aec97a529d841a43fdf3bff0d07776383574af343a6b2504328a61ae7607b1e792ad94fd63e0f5f7a714febd8866ecf4e93be767a16f2c6a4302ed3c88f3e59bf92c5343617ad9051655cd702ff03e0a52af7f0759fa18826dd0d6c0d6b9b58a943c485c911cc2e83d683725ff800e4f44dea9e5747c064f56dec7c38e976a8667949d972a1e8d5cf265e4f07d86e2bd4c2706fea5e50780f6bbcf004fad574131b2a5d060402b25a79ba39adebc44adec286e2641d02c06eb8264faccb1c5ba4cfe130df3ea472e3ad9df249f14eb4db5f2a7b1bc271d67455ab068c58b17a0eb2445f60d9788b899e3779594819895fadc61cc2e080daff29cee7c04c152928572b235c790f229121bf087c155efff507d086f273f11769248554252c8d5972cc79eb93724ca839565986fd0d23149a7d428b2eb18206374aaf52de2629a8215536912ce04303d8308cbe65b8b76a963b097519a531615325c2fd7a1cdd84daeaea5f0951ba4421342db3b0225bd35b659e31cff10180bd6dc65a34a851c7c9d943c9a600c76aa179626a52d8e9f77fb03b5f03100037dc56ffa38081b2a6db99e9299075f3b2c4cf6039d9586cbf123bb759bdb130475385704108ec22012b4742792a05037da6aaa56e84da60c467ea7d3a908e81d7ecf0f95150be7f4d5dec9a1dd9dc832495542d1c376395c5e7aa334e020a21b28b9245119102fd119e81c340311a9ae3749ba3384ba0f1204f6293e3937dc0eeb2fd7a2ac724da6224dfd2dbec16d3a3af4c70ef9ffc653a52eab8d1693a61bf5633124ef3a8eec373e13373abc59d5993e616593eba7dbb5e248ff802881102c6eb060d5d2b8968e9eef372763ca5183b873e5a581e9e865f47b6028bff71c19cd3c08fd68a23f0d9c99aab607387b9c6ec678f9455616311768fa832a5204da68b4a1d18935ff4523e6cf72b1850132058c7f150c2e126196b6723f19ad2fb8db2bfb259f034348221b3d3f87b85780619a6d54e67cf0a55965862b7fe52e23144adc595bc2be1a48f1c1cc41b41dd9da0dbe5adef141730e340d04585616328ade11c06b5003e195f1e3dab9003fa8013ffd3f7e6a67c1f51ebf65dad418a861e2f11f2ffe125ce67efb0eb74f33bd131b5a5d11b6446a8b3fa207ed8b2285a3475847e33e625ed4e7d6533778b595aff6607586bb0412d8210fe340552e3583fceba101476b4fd039726118ac87f80df3cba443836dc2a53fd6fcd34e0e18d5690c79a9e9db8026bb60f070b81acd43c1e5f7030ea7dd2d61bff42ea430279ed1125758d018a344fb17c2592a3d2c34594303a65937825b43c263ceff01c029ef10cc78125dd9f4a55a19c460e1ad391a367f976c8d14a2fb91320efe28825a32c3b76a082a9e42428bac2e25c4892681860db150836148b3b1fdb13d1f64fd78bb318ce7387682b34c821bedafa127f2556a9c2687178ebe195756f609fe81400fb471b4fb1d59f727b378ed82d87dcd5d496097eb293d973564d398058ff3208fb5c9cd219328dff54018be2374f4a66fa489fcdff636810ec5288a0f1428e907d19a12444d9075431e049e594078e1d039e1260075278fb374a93e0105ee257808016fb5c76dee7c048012653bed21b536bffc786e8a1d77a2b8290b41964caad2025868bca96f50308b0e8d7a814ec83b900c8f77583e86bae18a18d2d335215664076fbe4a32999c11aad848aaa1df092c39ba98380a3439943e1ee02ed6a5098b95951eb7e0447b7df1eef98bc02b65dfe974bb92a4ffccc41c2983166df1755e1dfbd5545f0d62e8a27daaead2c734a6e61a196f7c253b967c1528ef00362014c7cae847514cfffb599397f6fe6c6ee72025414d2908b47de40a84cb4e3c3424ba0329707761ff4b3262835631ea1a3e164fedf5077ad2ad20039921b628ef95d8480eb14371130e00af763739537e2d02d6f125589f25eb850ec1140ed345fc070944b3846169aaf337e145e157dba7bb5cc8ae1919e050e01864dcdeb4f64782d440a6e643bd1f58b00fa425237d8a1f8b9a014d7198127601a587ed01e73a590f2bb886b1526b9aa2f577b51ab869e9e11fb795efaaa6c31762c2ab563613d28bfb2c3d59f769eb526bff8eb35ce7a97fb655447093663f1d1095d270f54f28866027ea95f63a6e5715618c99e7e08f17948a1e8c65d0f223eb8346f3a9ca6c43ab9669df0c286376cb610b5a073c5371c2c168935bc1a82162a0e476196e2da58d90892d41a9a9ba1d51aea5d2e8fe1d3554036501ad561776fe8203c0febc1bc49c023b4f2095f7ab99dc9fa4a09c70050ea6c1a6f93d04e66dace55265374deb889c25cf2a56f3cd964ae8d3ac45ee95781743bf701a13b4cb3c6948375a09050916d900fb3d0811a077bf61fad07727b53c2564dfe216679c8ff255da315e1e475b866a908dbbbd515d620c7182b849e8863869ddd808902283f89ce762a42304280edf68f9a76b67fcb61a7a54cda8575384ef380b24d73b1e75b50a6174ae6ab664e0a75f91d4acd60402170a2bf967c9862f0ae40ea80f4d02e46dfc9d22f4c5d3358a3a741b39930b017558af7a8c9e268f37a00cd48b6a5afe6dc0785efacd85eacbf38ff3691f503bd723796d7a2b7a5505471c0a5dfa4b6eec5f56d4b7e4805da697d7a7e2878d717e21d742f9699ac0aa802f8b8c774afd950836e8ec22a520412abd775c4619aafa9dd53a9dba31f385d121f23710baecf82aadad99712d54fbbff07e19d618663216ebf13dd355389ed70ab297bb2e8f14c44d9db5aed00f4272956f940c814e02c329cac3b33e110f7b260c8b643c9225c725f638d415096330c953d46a6e6901548de47f050cea00921c406ed69b8affa8c49e5126b09b7407f1fe39e6c3a67ff985f44b4068d17ca2211a8e6004ac2ce57b738ea79b47a79212fe05f59fc333dfea5b973e0189f8711d2ac25b7da815377b5e0b79bb57edfeb0b3e249284b093f937eb3c8cd5dfc470e799e62210ee5038ca82113eb298d0a91ed7265809b0e3c41842327227bca7e179281876a2d730f88bfcde6a261d12b63b325fc4b8f8a07deaab29e073e24612e721bd4dfbc7a76064f51b97cb8c9aadaebc2219602c4018e412558f0c92cf50f0300b41c3af93217af662fae6c20aba5c5de25678a70e7548eed1465315e04260f3dcd91a642d67ec3157acf46ef336e01231612fd33a33d547b352838ff602202827e24bf1d82e874c4b2e56d6da2211d4f7ca4affadd0e17c9939bae8c861988b22b4b7bb9ef9dffa40b919b3a3182f30d07eb113eb168bd5c30bd8b206a032495cd76b9b5ded766b724b3e8a955b114b6bc8eb936462efea06162c87c2c1caaf7adc04449a6a314f788d3f2e7ad03a89388119434eb73467f6a22898b762192c947c632f83b1d839ac813849f7f22884a21461081a6d636bc2c044241732a226e71541009557abe207a50bb07ee6e5318c4ee2f10bb0bcda8e4fe00e6b71fc41469002dd8f72c043824f87eeaab33b31ac8effef573e9f15bb24f1af840107e0034eaaa8cce6143056e561c708175ebd2bd2b01f7204024529b36328ec107e1079b23231ca0576310db1dcddae6dfb2272b968d23ce655bfaa3b5b578bb051aa676102be135cff656a57bf369ca177e279e14f723c6d567cfa0cce54170301665691ed411acc68341b6e22d99e80c111504fb6e8fa565540666a6c139f21693e4c8d39fee103747a085926e9c36d672e0950b53e0168b556f7cb1a672ec12a0aa0eab6371061b8532a895927a22ff8cd0d1b0f4c4daa831cfdb62551d422abedad3879ba0e5d639f4ea58196f23087af2ffd496a7572303590001f57c652e0d004ca35307c2f8c99a335d6378a3770e5dea4aeea8e5a4493369f406fbdd1bb85993f3834486eb72a18278e27c76aa92986a4b9285143493f120104d628016e1f540e94796eb5b0014ee099328f84ceb614add86ccfbc550f7600f3413651c6d6c29222e50e8a15edef04969562fdbee5ffa843960d92d565d380cbe1def21d8212fb0ef618b09a78a0d3030ea274197521f239b7b82874d575222ec0ba81ee82dca840e340012136ef0b243eba0e650058aac4a5fc88ad5327ad848ecc710d71beeb1dfa88fb932d6125e181271335a9083d7fc6e13cdb92d1ea5b3d60912591a5d8eab8b8db19ca4336fc32c3147ffe8088684fdcaebf5f634cf5e1eab1b68a4d96dbdaa0afd40748e0b4a0ebfba0a627f30c9dbc621e727f7bef5e78e245f34dcd0a1828753e5723eaa14bb4313dbe15d5e76965f8bb765910f417ae403a931a5b653d4fbe31332fd04fc6e69626fb834cda6fe48674b98219ec25e212df043b86786dd8b06d34b0bb7e6cf4c5bbaa01158fc330b05fcee8b4fb9eed20ead836e6b8cdbc6cbc06457ac64cf3a72283181e35a9657ba381ed47169ef3f178a2f40907c36a2de63a99c8cbe92a23d9b10305c3003f5341ef1f9bdef0a99000586c10ea5861a049a13f7c69b0bf6526387aedb3952554218cb5ae0266b3d17107b45cca47a5078acf00a36247bd987db1d331c585953c27b3e7b68e34c5c086f1e38aef9ef67f6bd98ba574b8534c2f71e4ec038565f0b24c6faeeff168926ae5209a04acf180a8c7481af21deeb7a02886f61625e734604feae11bb965306c08ab20127db181eb638f57097f85600f251529faea4299afb46dc539ed447212bbf694211232606e3fc74627321d1188ba6d02f9e8269dd81843182e41575113b3b7b2c3b1a8698546d3aec50985c37a3184edcc69efc421fd3e4f939fde4182255831a27d9b5d835ed40def306d138225b855aa0b81e5707438d4cf7408725dd70cb357f50ec938767a6216c3c45ef2034d3fbc3c8555a67b7dc78b47d8628fd188cef3d2b8dfbf4fc38ef79f480b2efd8c4adfd46fb6205e251212ebaf31e70b2d8a82ef799db3afddc46ae635734ac0c700fe84891c053e63156981b0b0fdba632fd2f3c239ee4bd04254130f5d66e68515143c5f4d6f2b29e17bcb1a303aa93928c09c5312a4d16946121da973879c2af82cf71b14a2ecbeda0e5ec8b02462777d72e300a2f695fe1e4440becc12fea0e2ca14ff1862931e8175d31cb1c9f8844549a6ea22385d3a4f60cd14fd6425aec8890bde9221d42f60e54bca41eea53a6a00c6fe7b66f129dec025d3c05b1a7661322b803efc62b123918b9700799cf50fecd11f0787f2807bf01fbec1f9774eb693afa70d652dd59d292b74809f0b45fd23a36228017f8cfaffd39368d2317dfa86d5eeefabb69e13c7d7ca5128829e70548ddef6f0d1230f3674f752890c754e20a858ff699eb425595132818423036c330b0a4c86f547594d8e34b5abacc9933308d03ff4b5307c9f44b721d9e1d6655d4140a8adca0117a6ac07565e7e8887975eb3965583949cd93c18900bb301068bcbc64aa85a14a8ef8365ea3f57bbe465f80ed8c039a04a7c9e5da0e09ec9c44885c8ab72021e4f8f6c388240c577957dbdd7f64e929aa54c2960a071e8b4cd84f4f659b95605e75c42ca42547f2462b36f75b57972e6f9859d45f067723d028b166e84ad2a5d9921bf762376c3e90ae6b17bd87c27e0fc007a77d1f5f7d8b0028999ef82f084a250d8562793aa0ed27644ad82feb94e80d948a6f09feb51fd95f0632eb2b356c130b50c4f70fb0f967cb7eeddb222c546a5efb49050476a0d41bb023dc5d85b1cd7776f9a2c3c792be49704ef8e0bb09b9e6913728a0b8490ce3a1277aaa62aab1f07ebb41c1f395c34eb9e983884e8dd953bf4a0f0e353000cb746ce229ae769ee10c47f15ec40591d589d798d0c2bd2655751106774bd58d59720491a086b4721fe745209895969578d037e71ae982f8a9320322a859297e7a85a2002ebe2e883aaad92af39668cd0798c08b7eddd7d503b24b1ae2daa5c4eb25393ee6ebf4008bc4e0c072ca90b67a65c586fcd5550b29423f093d0da983505afaafb1eb0771e8e9e1cb3539fbe61f5efca2d4860b1fa1b36121e5effbfaf7611b946208ea448d990b3cfdaf50b40063af0b7cccab49f5c86b16506192e554f89afa326ed0528f6aa248eba2da1825d58628355393113a8fee286a02b1c4a452f8f15011bf27dfa4fd6832f09725a7219294e11954b957f9221166c24763a83391287fc3eefe14933c5c14f4c7d6937f7b75542347d04045a110890ee0f78b861b3af29a624e25b367017d565f468cddce5b7f0cf3c35939a710bf47754178cfcde50bc00ad8afdeb5f65ac80bf3130f8f916859fdd0d676b51a19030af15d64ddc9aefd991cd5cfa036cb80826b4373b9d7bcc892d88b7dc91054be1686426a50d43560b6f5a738e11ac395dab9fe8c808f8f8ceef391350406b72a33744c6e8340275bc6ba889bb2a39e4e44a5ec3608ba7e9ff3169958a41fdf27650f39023fd280fbcd8a14f01e01129e55d1f39690748e99810326fdcd20e052a932671c905b1eaa5c122a28ec8ee55385d4230903c923a10347dfdee01e17b2763b6a7060a5b8096bdf84673c406e1bbeb5fba30c109497c0aa3ce4d91814c363d790b240b21d0c46bca0a903c5fee1e5fd6e2278a36d7209bff11e701244171b2ba258a8c4b611fe9d842c33b90dc2bb751a4c9834b94ee633ff2f0c17e99d90e05e03e67dcc81f0482fe23b3edcf8af33abbd48817fcd2f01e5cbad02174e2613f854965fff32fcfe1b2ceb878c5e8451dd0aa5d75a96db1423038d224a2a1f5665b42ccf0cd2009d22d026f92aed1329fa0eeeac0bd789e8a094c82617ebca343afad708cbf7746a1a7b486be884219e41fc54bc92afa7c4fde27204e3b778f1434022e9a0f5af3cb796f94594c41fe3d1343ce8e95cfa3405e76024841490ba15b8d6e571c78b5fa9bdb4f42cf6939f0ca766f8a33f9af1388e9f0cb4e92a32b3f70b729d395a955e2080b6f0ef0d26e8692148295a7b56b5f6a31596ef09d919d4e9ff98704206e61a8dc0a9ce4662eaf6a10878f3180ecd6bcb0093e2dacd06fe78b044dd4e2f973281e38c793a05bc453015dd5d99c1adfa70234a32f957d93338fde57563ebaf78674ac0ecfd3bfe0a8293a8222791613d2c2ffeacc050d243fae78fecd4172ef8ac8d65605f37137ac2d95706f7fd8f331204bbed683e9949fba2e4bf9941b4939ee707c3973843138bebfee49358309dd60265751d08c2e979f56c6bd140921e7a66e34962c2e608d7326c40f95b797636285580e22fa6e96ffbdfa09b8be7d9c5def6bb69b24b832097fc8501a8ca707c0b7f05f0505eb62b7964b96d0c4913735b728fa4baf217082e8f47e2f582872412c7a05dcc806ce1d8cddcca1d989321c22e660aeba3d8945d7f932b6de9e6591b97a10e4e7f2ac05c0135756d3e162058d1b9e577760bfa12f779dc1b21c04300b8aa9e06aa51ffdcd11ff50c67a307276719bc15611fd86c8098faf7c829da2c4b1f4eb53f6fb595e1961d38512f3ce9827b9e4822b90fe6e5c0c52672cc1d012e591b62f714b671321c5ce756f826cdc2bd630a9a79272f7aa02baf56779d25d42ac69b671532b1d51d23e7c79160bc322015ca838b4e674eea5167aba83c218ea51067f7176b027556fd11238b4bcf8865d89a9cd0bb3e4f43477398ccf22aa043b2ff7867dea2472de15e42e20bfccd0fd02248e6f1ebf7f95edc2eb2312c0a6b7ad4f50c3bf03555011fdf65cd0e2fa85d3bb7df6dd924e7bb5794985118a1d10561c8f03828a912b04d1e50f8bde69235134a4d4ae2da48c118a5dce12e703a9115055cdecba78a75a6d10a5e56613e9dc3efd226cd0756e592f880642c9974ff67e8a91633f52d02e73ef44399b35cab4796c37156f0f9800b7a88660345dcbf63e44f1fa2d1f8eab547a67f70db529978f40217b5422eed2588a4ee300457f11907f9415e349dad8cb86d9910eebe588cc8c0cc798a522e39a669031bcc7172286c9537143a92c965481a89f3c4e860d5ca9824175d9c7a597ee7c61c115ec99fde4909f191e31613746673c5507ab6e01436e6fe5f07d8a929fef51ff7676cbb3def22e44a545acbb9aaee1773f706fc8684a7b465d2f92f8af7dc0a1800216db2c08004b42287c6df656f6b5dedaae8e1b9013c94532463df05952bbf330909de4d033727d32480cae73d2a2e9e50326fc605ae30c0c9f011128b1a678d3619cafef8cf5d5e07d61a849c05e91e9ce8290b8e0ee69c705736ed81117bbbb0fad9bfc93fbeda8ec8b9464853ee161b5fa9b5e954c36d10f7d2e01d0837f15100dc35eb17b2dad7ccd87b482048827c1716d510244380ac5b0934e3124b0552ca7a5902213329c982bf38782b0aa337a618d658aec9d01e4885977f1514eb3c815c8ac482050a2d19638a584180f196c16339e4652b1b7f1ad870891d63a0420da9390f9dc04588fdfb066906fbeda08ee28cf7f06541519f991aa7125c2099036a614739e41d2ee4266b3faec273304a8a7fb09758a64cdae16d281d06427553cb1acf30f1603889e575889f826a832acc44180f68c230647d971413a392f060a527885509f15ad448f8aece5aadc9836862138a8b6d3b52c33dd41faa36de1e5f4355825cc13a32d55f9d84a7337b691d12ab95d7eddcdd3f72a22ee84a7b8932c0aec98b47176220beadc273414c3a7d739e598b932603544fab03f191cf63a65ae49a832011f34c94d75bb99361b17399b27d77231128adfb33018fd3f19e9f95a38b4f4296e1b8ac28f6ee41814be442b889ffd99b621ce5bf1bf69588a55456a7f9911222a4825f68d22106aaf2ee07891bbcae65d952e75b2ce90f558af3f890bff0115d005d1e5f714935cb35aa8e12c3b0f194f53ba20427e5d13527c1a9f7563f61ae1f59a6dc16f2cca1df47ba4d47ad490a54d1a9af10e1bfddf43ad707f810b5b9b48a91b16d892c73403c3f5cd6670bf79e355e1d1cb721ea112226ce6a6d52283c887da649f153a7790ec04f440176987419b5421b7c694c646cbc5618e0f2e9d9ca5a10be1c9e007d519aebc3fcaa904cf4028d0f15c92504ec8655c6f5ec0bdac134b4194ab85c505cca657e295d2041c68be114b83a369cc7707cd41264238945dcc4d2581939579ebdc673ae8a5ec925776e28341d66f9f6eae02473c10b21c2f682ba22fd78f1b25895ac2adad8511422110bdfcc50e8b7e10c22d1061ca72d0945d2ae76eb874cca31ae41ce92e335e6732e0ecb8257b64e85774c95c601b69ccdd6cbdb5bce2bdb755e0f4309e735725f", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e94000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" }, "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000416c9296e38cf3689000000000000000000000000000000000000000000000007abf85d3a9f30c50400000000000000000000000000000000000000000000000237b411162b313eba000000000000000000000000000000000000000000000000000156b66ae45ebb00000000000000000000000000000000000000000000000fb163c45b929b1c0800000000000000000000000000000000000000000000000302879a33ee70e101000000000000000000000000000000000000000000000007d3fb89f63848af0800000000000000000000000000000000000000000000000000001191ba15fbd500000000000000000000000000000000000000000000000266d108d2bd6f116300000000000000000000000000000000000000000000000044a3d48dd17dc3d200000000000000000000000000000000000000000000000bc7f72ab82ba9582d00000000000000000000000000000000000000000000000000010d1bf42c416a0000000000000000000000000000000000000000000000021bab3c5cfd31c3d500000000000000000000000000000000000000000000000d01d612334779aeab0000000000000000000000000000000000000000000000090f4390fb5773215b0000000000000000000000000000000000000000000000000002561ec9fb68bf1e61c54fb309fe860b2a599e2c1339c9a0c1d4d4d7c19eb9e1925ecc353851c21f29faa0b2c9e2dce7b0ff6430e42bcab396059f4a009b643e80bb07dfd986a22c753cdc1297ee81add827710dcdce1b3905d8d137d45de8c4b58bb725680e4316c6900efe1d29c325ba03cc43ec30b8df8f7c8cef5da35935b202319ea75c872438635f5b3eebc558a54a7faacc657de20dda2064d0177e5ed3ee3ecd9f7ce014be0008a5b22bf423fb02ded702d843cb50b96102f67478344d3736244b75a9042116c4e0c0968f6fedabaad0ccf9ea1be6d1b6a2b42b566cf78603c24d65f315da1957f66178602e15a9baa053810b896567ba5fbc53b50b48a44400965116157c7f19bd181c7b184650af1f6df0687c64c2b54927fa663a224c6979cbb5a7274367923451bec6afa2d84276037f1dca6505e3ea5f37b5218d7e270ff9cddc001cc131923fe1c5c0cb360571bbde6d7670a3d91fad79d20d130044d4bab7e51ac2a0199f2bb567f4aa9fdae3b8634f70a6d47d4a8ec6829176f075caf523470eef2d4270dd12ee163794ae40acc907a4033b8427230e0a3080bbf4208bedc2169c5e1cf9f47ce448b3c89989c8ad99bd8726360fc259b1c35041205dd88b56028a4398df5cf93caadad64d1962b80ff243973bc4bf4a89700552f80d1e293c25c339bdd767fe4d0b7ee2d9b6c24f5bf65ea3a6e265f9c682b4f1b1a393f98c2d4017693a6eefa1e776ff4db63e9054253ba54e0f6c92613a8e3b55849035c606f4433589d1e140795dfd40198ef021ad53951f2dd41624bf7cc92e59783f3a29d9f6eca1bf91f470e595ece8a85c451e45a50cbacf2aedd775be0fdd413d44301d5db28a25fc8412e2a28e4e33bd176b54eee796bc300cd3d536f889712e1a0aad307d819cedd13a8abbed236376ad97d5acde39e0c8b0dc4d764ee788a0fd027b38b4bc46b09a7da5ad7f8af689326fe9c126a33a8123e47aaed4d5e877aa00d9708ee83d5b9fa03b2e24aa4245fc0d7e9250fae7191b292ede39b6180ad423b152d3cc3db2aed13f19256c56af480663ce8296ca90c2d85c2d694b51e6e20a4c03571d895ac7cf59e35392dc3908895026785cb42194d0559b3242912ea62adf77af10cd491f81184ad570951e233a8648246f3ffbcbdeb4e0f9307df9f824bc8a5dd0cfea8a335721305684c25bed635cfbac10e99964369e01ba3da0fa0df8138a289bef477027be8cb063844e59ff5567c05369b3d7fbc2d13656fee81b95251566f5aecb02dc9365cb51d522c3e368e3753313a9773206e10493eda00a40cebdc64ab5f42e68383442090734101028d4a3b3cc57c973529f6044eb1b1f09ed3188252c949bbccf14141f50b9c0d25f47019e1c0bfcdc01c9291ab7702315d4e4585011faf49227fabdfec64cc580697ea62d342a6d27227dd5a91a5b2a29035ec3e66629a2c99358f95d94ec98b50ef1b5bb59ee704b5470280668351ceeed6dfc8ba9c4627663f1a4b363425195d7bade1870885f1c07a8a2f8e82029d24fc02584248d25570148345a07fad19c756a1bddc07a8e0c3e0f72286441114933efd521c51bea3f3cc320adb2deac91b89e6fd016363d0b7fb6e2d10d4810e91cb4e2713ac83a62d28cb182779fd18be9f3766c7303ecff585f1c0bfd9503eeff54d606f92e1f978c61adb70cf59a3c0790d6c8184b688b47d10f28c5db24fc5858dde57e4fd4316c29873ff0f683c46c50af520b86b6bafc2505fabcaa0292ec8178651acd6b90460e6d65060be11d41ca74111a3bfbdc9b1d99cf7b3b221aa8a5f4789dbe9b007c91120d20e499db36f7abd83f02a1482d752f995987147cdb505bcf5b9968e82a0705a22f689f00bcb5a30ccc8fe0b25a99c0a22a6c27791efd79fe58fffcf59fa1aa0a7d8ca1a8e84fba4698840c6df2e048272a3528637ef304377cadacc8a86df22dcca01c4a63889492f030c519472e1f869ca329e91c181f32cf54a5bca551356b9c4d0c60e538977356b9622d597db2e97fb313fcc82f881eae7053f7aa4bd0edc0686b34acb24bbb90ba6757c84c3b6730ef27bf0f198b9488a471da7334a60d80558b08ced4e9c1b01f79f2358160268c9c1670d5334f325833ba1db22411592c14af3e83369d03ae3da83f8c0e4c9fd3620c897530e597293444e28daa1055f7d6f89f6ece42b1d9973c3f0c897dbfcf96160fa66e6c9c8bed3143148d883ff4fa31c3d6fe1ae6ec6e8b5ac3b840455595109d6b068a85ded28955858559d700697ffcc401df847df334026c21cb932da1072ad661a6469e211b76e5e7eb160581e3b296b2b5d771babaaf6508a29c034817feb2af6bf325448de4c7251b9e24a02dc474c923986346e30980758eb0cf2c19b53befbed9bdbdd43bbbd73b2f04cdbad27b9ebd4df23ed8955052b9a8f6fd1c32bff4a07aff3e1e56df730f2e2fc216b4f9fffdb5f6d53318b4987c68def42c021095a60702d3997ce3eeffeca5543841ae0b4a55349381d1222a2d6632b4244ff2a5766dd407d0cdb88beaf70bdef8c46ca2dccb6cfee6f5b90a61dd965b0186fcf84374a039cabcb975e7d50ebd8982c5a2474c5db21a6fa262cdb79a672220b8fa06bdf4aa029113eef5e332003ce5f03ef7618230c3774ed61c0482860128f589977a08a12e68b2743a80badf484f63bbeb5fb9141c25aeba24bc878a2741422ff3fed8720c2eb136d8d540db48d4428fd9b59150168bc623b40f22bf213fc3d76986485ba4f955af7475afdaff9f1a12c63f2baa6aa7ddd40cced56201d8a974e433e042073171389edb7a7b5e946148cba308dd80a02e78a2ad3e23019ef96b61d3dd0123805d495b486d76d0b9f3d8a48f1310ee4c733bcff3191500cfff383f27955e4ed578f043245bbccde47fd234312e389039a1b5bcf90b0d194297260a62a1ac783a496200362453eacf59e3be3dba243c1e33541a51aab521df84868549940a5b7e545b12a4c1fa0ce838bfc5244ac69e8b2834c7ba509f2801c3e1c0d77b6ab4c4f13a5e459ae4720f3aaa4e5bc98de8236f2a61188ec12326b66e96fbb825b539b41db33e51e53a6aca36b68bac613b91ab1b657d99f2187e9e99857159b991e1878086e6b931d24a8b5c391f1194461942c0ce522d8428dd1095799c7732b2af1df9ada0adecf15a46454aac9374abae6fc565e7aed60db2b57e5a24b226a0484bcf343aea6affba8c8997f19131a083d7da09a5bfaf165988bad45dfcb328512a710823a8a1743dfbec870e83e2352929f624372b97064e9d0dd64209c66dfd55ba091268d173e98baf277f969456077d749ba215a11417fddfbb5e3f7b410bc1350064abc3c0cc74470c845869eb2d57705cb5f11c21b11c833089928354ac0773b5bc6fafa12d4e9ba089acd82035f8a57b5e7d262829fce2cfa4e37be9fc640b5205689609d6ad185c87cf437727cc12b204fb310f676fcfee6c78ee2f3637333626d227af0d92d7912ff4dbe346b094b32bf36108066c5a3b416577bf7d266bbec4812447cabdadfc2e1660ee003e241449709f0196177629bd7a488502404a03c9f48199bc0d241b8f6ce8cbd8da78fce4eac72d68606740bd2a582d2f784d3cb38c610ff9c13bfe676485e7d02062273aad9a0d3aac2f2f3017e6d248adb5ac397957e3bbd8d1ce038afe1ea4b5368f73ea7907fb6239d7054a16a6139c7c9e9a44e66ce18c15d31ba66b18b5b7ab4d399f4706c5d0479decf2b5da336e6d2945c5c8317d9060ba8df6fa55a78def98656728083b6fea725a1e47cc8e7be08a999f1ab21d528816758209157ab26af22d787f196820105c4640ee968e4d67efdc9a46831d6b9623783013ea8361076998a3902fa23d94b1eaf9fd2f4676328df7a1b2081a69ee6b0d2ec1cd8437cfcfe5dcd411102263ba18952e56d9c59d047986456fb27170726bf7d605dc9c784eeed06d0d59a79ec52c11a1425f1aba6149c77a9309e8838992a803b339c6bf84db99de2e9e4148c3c43b740f6b456920ab24f7c0f9f820802051f900e30d3fd65e2ac91b1a57199f8c82c5ed3c86433a63b8cfbbb9745a5335cbae916502221a2789522a06ce883be2fdcac598cd70549e8c9f4927e327b836a98993f5c0352784ab4721bb59a473265104f1443983ba0c1f39583416ffdd1d897cc07d525218bd4200248199c942d674c166ddcc723dec117f0189752a57e4c763a2521f849285d49c0ca1d57d0bab91a530614e9a27da6aeacb64e5afc8878ba1c514d8579b1b2e762a859538f5ec6ea88523485a6e209cf838b4bf1225e681b8618809bec3778e832414d4c62f30678651e1d582a448252a16039e7ef6432892c0736a508c25ec9521de8dbb5d21b94f9d3f7b8520bdcbf31608a972f58ccb140a0f08bf0f3ef76b2f615185f8187636d59208375e12a170ded06c8f4d95db7e5a384c26312af709116fbc8fa440f4770f9131ba7882138ce3395fee9afb912050f9735442c7873311d08205fb16fc22d5fee5654380ddb1d88156a4f652f7aad1b5e23f9bef20b8223b417fed4192d84787d35e607239d822e6d16da4a2bac6f06d21267fde3bec0dafc9f0651e8abbfae8f450051d56071f197afae930935305da4d9dbf63ea1c0f62368f70f8a15866c9150ca5de551b11148581b7dbb3ac3a91104d89e4f4bc125e5a505925cadd64a4d7c104c6fe4792895f26b040c1cc46273327d06eda7c2d3de1f00f8dfcf6f911397b3b2c20c96d905efe0204cbba45adc56fd4ada7f8169ecd4306444313c846aa1d6e7881acc2bb3c32a9dd04bac0cb7043d97e45380502b10e7ee88623edb4ea6ac4aec8de66cff047b3a0b8de7e7030100434f5c11cbb97f6700728797787cad7111709a20288c667d7d4e08e095a40ad9b05be6e0552b75ed7fbd61c8dbe1165f2bd9a0e119b596999e49d83be09dbefb7e529a909c76b5258e8aab2f9b0e9e060a0e5b502f31647fd562b198e7a4aa3fce1b6332e35a4bc64f6d28fb7be4acd22efd02880227c2bb7de981afe547306c8a69f3f1cb20eb9fb9669531084d45b0149539882d12da0394612b2283947a289e615f113772c23cf3f00e8dd3b33e5fa8f024c53b3267b463981f72ce19e96a80dd6e61ada2e13c3f530742cfd36835e8a97c9a506cb699e0f86df3af34c52f4d3b1091213e38fb599a06c87eebc1ba26ba8d888df60d65a4a3554f4a061a9c63d369926e680d16f1db522fc33a0fbbf03153c6e6cb4058a11c4615ee010e70c379b71012acb47a537212ddc7fb695b37befbef71eab971ac03ae4cf7939a7c754e80208c03b9ae2f8efa57d6b4bf62e2ab53bff941b81ab2004d4214c8d1c3e41acd406c7f3f77d48a84e3c60228d1a839558a6c5b040155a3afcad38b03a89e03510128eb4e9892ab0b92c03ba8d4b43b964caba607a7d376bebc5bbe59cfcc543611f71dfdb56ad567e3ab4868bdc28f681e1284d267fee5eacc7938a2fb15539342bf1d9de71f15ceaeee10e13f488ab8e3a45d54c7abd84723bc141f90f1576e805498646c59515ae476fba76baf7609653089dd1b80dea0cc074c25794f48124100a10a5a3953da1e19403d981dccd265f4bdd93dfeb0a3b65f7dbf3a8b06a0a04e1ebe52ece119625511b8d01969c973f33de7b4b3fb6e2022e30de838c07dd12002f5b2ecc6538fb4c520a8f2e7baea4fefb8c5cf1ef2fafb00f0d19c6580c1b5a9c9f9deee13a3e889e50facb2f4538c1c43d325810643c44b1ec7f5526bd029e6c6b1d9a45bc6684109670fc6e0c9f8b1f3feb00fee3a61169e9a83c9dd42be2c6001cf3592b5c2932f494ed9a83bad49f60473d7612ed60a635d3e953d0028e993169bec778afb691c637b0cae6aba585d2cb8905481bd3eb5bf6de1da71a308aa629517e68450eaa8e7ea91580bbbeb30b7998ee27568ebd6145b6e70c20161bdf2491f545c1dcfa7303f7705b6628a1094faf98a5f1a36c6456e074a10401c234e52c9961b76f238172189d91befd0f7ebcd9d79039e29626f293d009100affd6d2254e10219e9b9566f8a923167933d37e586ee3f614a5012d69a00c00cd99154a20de6f0f04d9f32be106015e53d2c23d60fd9da49730d5ce3837402a375894d8f12d1b34e3995b154874c873f9a3037d09783b1b27ca9306bf2c3308dc4c73e1b95df94f830d9b250fc45d5e2370dfa351cbb79791e1c2d94dffbf13d328b9f475e53fda0f9b44c28ea21108032367e8edfef64f4c4e977b1c47a205869eaafa9c978e967a3cac32e4aae0aa895fe2cc67a6ed720d1169b06264e92dfbc6a99923c8e6fed8f6a951c771e259892ecc638a57264a7351b8e94e26d4185960e8ef13c4be85419936c73a289be253b2bda37d06148e481b495152e8ec1e0a7d1fdbd3a0d7de9891fcbd88f215cbcde85b4563f8993450bb2fb117443627b76e42ca03ed9c2718c877c640e216665f316867b5d1517328f980fdab9c0b1cad6ffe013a43c2fda4ced6d18d4a900d42a6397ff6cf7aa04665b6430ef14b04cf5f0b8e827810f4b9e628df101f9cc07b7f9d21aa602466ef25cac111f75b09c0ed411b1e0a0f442764955adc807d2292c032052d814ffb11fe2c4f25f54d26089052c2abc21152d0c63457259245e5dc695b2c3633eabdb6bb64ee7d1f652e5651678b410b12f0e7060b8e744416318503d6545ba957f5592047ca89f52b0ff2a856af7f6ec8051056bf31941000957a5d7e8a1b90f6758abb559a8582e726e70d762a07770a22949a32efad63c88fce8ab69e7ecfc221aa52fd689afca502f80afe8af67e02f63ea1dae245ae6df1691fc2922e963dffb4b40969a2afc401e1bc978955a901660fbc7d320019a8e81c76fc5e7e0719a37eb099913441151a6250590a660df462c08f119d70e9089e5acdb163526fd9d16f3d51d7ad6c612229709e6d3b097463069ade52da3f6e91a6c09d975847a351261525b3ed8b352e7c11480d1b743bf34c1c314e8a2143c012200bc4ccf66adc4cb8ecd828971e1065589ecdcf966626c728a71a42830941c96a4490ad3a3f081750168fead13327718d11cf276a90ba8e97d38e258e68e543300d01be6f2b06c51cc2b37489910233c2b17cc751da4d859d9d4bffd7a3cb89d6e8ecef481ee10e3ea70c3a50340bcc3646db58bd841b4439c17bd7116250020f3e9d0bcae0d122f013145b049309d0b89a89b0b5542a6c071dd247117f7a9c11d92a5813ab2234bd046e5f35682dbbcc16fe1f0d9b1d0fc7947ee0fd7d0fa3f41aaa13e7feb5ac3758b09b43a71d39360b515dd22150b18dcd0f4161ffd6ce130fdf34a1f21af7dadc0b3477492c58230829031e24bbd5947512c07ecbac8974dda0968a88ec196ea2a32988e41960210a0fcbe12fe59cfc251aee2a958577820683d2474c03530b5c3af41daf11471a0ba1ecf08b931a8b592a4341124146f7eea929b5bcdf97f4309388e4832e8acf205614a839dd4c803065ada539c038efa3793fd5d817a5d37150ac380c2688e1818bd1cd9402e785e1ec0bf8f70c0205df96db354c21c841dc5798019f150636540bc651792c2074eb47e8f6b8c8f4a1c18d43da452614900f830f4f6b1dd3cba0b6cdffdf86d4465254183f3b3037e25e5341956ae5bbb3e5b69764722fbccb0f85e82ddf9ac3e1b6c0e3765db51e570310681c3500d64f65fc81879c0755923b6c99e531545dcc3bd316065c7da8959f0adef1052b4371510dce386b17a9bad3a8a4e29ff2ba56067a8b4fe001c8a17667bd3776764b957eb097479e0c0b11340e650a8c5ca4a2fdaaac9f751fdc22b5ad7665a23856456a267cb44f0e0019ad5b49a2017bdf0771f62e195722f18270c350dbac52a3a0d303d9309821aca0436a16f4a3c4ca41dc150249bdd0364f2c89ab3c895b0004c6d626b566124a30f423f8c8b635d0919fdce2053e0414360f57280cd37737b540e65e2d99041a698a36f3ab376d2fde74928c547cc481f89a588176a7509bae404405fe041e6089219e12b9f770c516245400171796d9380cd2985e7eb7cdaa8cb473a3f4074552579d80c0699366babab60854d2c33ac00b5160891b6372947f93e910ea1348213ea777f50a05a88fdbd4b2494a7d16a2d5f6ea737e75ecbc7204b9864f192fe5523992d60c8d84d1b6eba8aeee8014743fba88fcee19d349c13d3d37242e3ddad8d0f0eece179bcd5382678b95d153b45a6172059f81d391c8bbbb815c1d82c2b71be144549c02e0f170ec5a8b1b86d844996b7a89320b61611d12e3cb00775bad99ee32f45f565cfe39d8c90333241b8aeb8c33596316a9b9d652b1ec2d5e8e9748cfe75a2049627dab27a6734e98f15902aa1aa3cd82d3a762fdcb02200ed6da0e18cf3f8afcca5bed31a93dfb79795183d3cb7d1032fd7da336aee52867f3c4d1fbe72a72ab839b3864acdb59b20ac38a1a267f62e4dcf71924a2cf035af3ac007525f75d53985c1ebef4201300f1052dbc5d090342d2ef29b839251024bae62c29cb468ead61bfdad488c919537f50e85fe271c25d7d7d7b550aea0a018522ca71bda2137e14a43499e6da8e8fc21aca461520818a0ed89bec8d8a280eb898c57d587192dbdf7c155004fabce811991a165fadbc3175a4ea707b2829bde1a0ee2ea0a10fc1fd8b041bd6a10ec6ed6549761499d74b7d9fd1e43e9b1642c7004a01c8dad4c27982cb87a76a7cdd92324a1c4a03338e0fb63bc6aee71e0f7ef06ba08da79fc1af81885746f981a1b1ccb86849bd93dc4946da8194560ee663dc8abfb3b4f401e97d4868baeb3cc9e02380dd317eba44b77585d03ad818eaaacb65f3696055f484c63bac79187a5569731e961f428906f70a9df8da601171826be0373379ee40fb7946317f2add3ba941641bc08e1c9e76500ed274c72fd2346a922c9ed939bed51203e6e8434334c8430851bcc79269e800eb376f9d05571558d8ebd306a3f43867406dfec2834d7e60492d786f022ac77e5fe38a4e1f3bcc526e694028e64972abf52a540e5fa9c3657b255b16443b0f94cf1f88450627b6abc191058fa16cee748550de84eabb8051a54cbde1478d527b01fec7d722847c86788c1e29add90ff796f2a4b430c50ac6724b1f50fbe0678f7f63ea8d08ba24f780459ee2118ffe09e17617a9a875f5fa866244a1e0a17929f2d4b67502b26decc17b0a6833b9e1fc9d6aa1014410dacf458da78fdd1cd87aeff21e0001a33b055d9887c58a323281ca2d6f08372098564035043daaadfe13ed795c100fbcfb85ee33450f020afe5c6701108419af3ab19dc36b0136a4e09638cbad8618cf10d5da4bb473b20e2d4f869f6abd9fdbb3125899efcf8145c6dbc8eaa5782dd735114427a7368894596336986dbfc6f83dcf2abfd02dc49e5096425bacaa0f4fefe353c5aed743e4f7de346ded390cdcf6cfa6866b2e26e307463f5e54142266af3ecbae5dfc0f6dc6ae9c95fe0b53369b7fe4d333ac323866d5577a41ea2c6d2a0983c01fa88c910a18d05f4b2ccc64617912fb052f68ec8f732e1c4ff81494d7b3ada40bac069be1534e46036e4fa15b67d82e35b6c4c537cc8d69babc03aae75362394a6489321b24e0767b05b683c261858ceb4252792911c06b625b0a1733c96181dcea552b33390c474d94fba7f60c1e79dd8630594b0e85eeff922521e5e26916a8d161d83d01faa227f2a9d6444fef04c87f4dcaa6039e1777692a97adbeb62a33eb4089559d7cb7dccb4f04431881d57d6d895aae0495f4327e2e4d202b13b3e055f00130e17d7312f1057000096cb0873f6f4e75cc094a879117c3b3c7a747f460fbb0783f83b3b0854477e023042ca514e68f4de8ce774d1626e6ad5c9f8602fe6c6b5fd8b31137dd1233ef55f086f28918764c2e9d456f212fb0444788ce44fd4352e3a05ac3fd55d1da6c5d5242c206256f663ede364e6e2373732b6b18f7611b1e4b63ad4c976c1deadf391b5da979c5b02b25d5ece40228affd372dd8a5500634ea7f14730689049209d5b5d28ed1af43b6a02339af83197d38d1f6964f7ebbeb83383a01410d59e08e3753b29980e6b536ae638590652a7b9a117822013f4bcdaea47c121aba0f791728da022a30d4aada1d57f8c623068f6b97acdf1888e3c9222e48ff811cb06afe6817952a5f84e27bd6ae7e6eb4063954f2271e0d8ed25f91193aa8f92e4a1a8c16fecad358545445169a129aef0d65d2d7d185b145537bc3b2785e0d7bc7ea6aa9309a2d0a00385348c250b3b614b1fbb7aae00ee336a9c40c652b647742a3607a56be6b253cd9152e8afeb67613bb31e1b89554b30c9ddb90be8fd9f795054412260ba7f17a74233ba263c9311c487638106e346d6570371469fd0bfd06b4db971cab7d4ef0990f3af631d7a41b9dffe4983c4c2e984d47ee3327e25e0e1bb4fd786160c4b34c846a56b76bf91c8277ea0a4cf36eb760488a05cc2aa70bcee215bd2d93510b35fc4eed1f1b4915e88f5976f5793046aba0a00dbccd7f3a316beab9510be08c9f05e784540a9e232fe8b33d7ec5ed4a277e7b0c17e27239a133d82cfe4dd3adad6816773967d50b2b89728d1190f312e5f10e8fc7d3ee8218ead95eecc465d5083cb6849c4e6c2c2d359afba7a0809dced78cd7163d88caf325866d1ebf80dba614817c3c47ca0594cbf43b6961415ad022c67b6e16820cca393368b51f821687ace8ab744039174d28be0ce2c8d9b07bbdd2972d52c9bfe6f26f55ddc67f015df08a2bdafd7512a69698ececec58664bbdb7c7389052eddaed595852df40e2666659e661a4972176250fff62ca3380e7f0e2fe8b22b2d710d76e6fcd4ffb0f7e8c9ac6c1127a2a83a5c61ab4c3749440a8f664d7d31bfd430dd80ed0cd48b0a70d592add19760701d3a991136bcb410559e27783a49625e8988733857cf2629c9197d174a94007003e3884b2dcbf2eadcafa5dd6f4c0795d41f78535c9e78424fd46ddb62e50251cec8360443fe70e56dda0d20550006d6fb8144d28755b55e83dda5a22dd8522df0d8beee822666e8eb9e52c43c3fbd074c41ca47c3bacd979ccfb528b2f2b0882f525851801b114af0f703235ca9cf8f9c9e50b2b5224430225b371379e7a1c7239a50c48885d57d708f217488080bd1ecb6d58f5205665fd593c9e0bcddf23b2f4eb1ef9301694a34e8738179e8e1369a60fc3fa11ed46407606363f52cd24ee26f4d3910611f69283f272f9ccc2844c3411baa34e8da7364dae8a75684000f0b2f9f04ff420e60d1d353ac9d13e7faafa17000ae8014df2fbf7d12c3d7817223f92516a4a8bb4f0e93ae2be4207956f7fe6c126daea596e4821329737b12f0e82c3f01cdb5915dcb711359d725ca71d8db88a20194032ee174e0296031b2f696b4545925563e1d1517e2c04b20f117d21ab1f518da2275af79d25e8d41607b2f5e9c795c05bd09e8e258b198a9b8d59b9abc7b6f80225c58cfeede76fb3278d3c307fac2e86cfb0dacebffb44e0c6dd9a82c44b6a09e480a03db77457542526566aaf260a14c32b6116575b481d6207aca05141d8d2a9d491cb9a1a99162696ec670c96835be0dbb7acd8345d7f58bde2465c9411c61efa95cf126e0c660a6cd0352efca0376e792a21ffb8bc5c5c4efda9db0450bb0ace4d4c9fa128b4297c7114a11be0b0e120b51308938e35bda2a3883517ae4eaa1d40c1b06f159e07d21576c33046501b0e5fcdcabfce665b833125b78d505ca673246352dd27fd1485ef6fb7528e432f977ed5ec10b0b786a819607c7750295556208206f2578925accd4fc26cab678389452aab1b5c737641fd584576f8b5eec4855e12d9ca56215db2dad99e19a7bfb18537fac87fd6e2afd076a160426081c063d883cf07f20a0721161fb081ff18cf9984812240c76a5ff5978697bd7ca060816322c90ad80126347b928a7ee34d2fa4331c9964d45b20e4dd03bfccef79a30ad07bc86b641d7ecde5d7184ccad6aaa66d296fefc304910def84095cb11e85d41fa5aa6b6e164350ec9827f1da24730efcd821b084047abd9dd6c6926526d5ae954086b0c510324f8d63c5d5cf2c7a0ec72078e8935b864d4174b1d65c9e56916404d56e472ada136e932d27cd277edc82d035efc8f117cff743cfd0988b3dd87e8c4af85425b0f856933d7f16194b39760fec8c7ceda5bfc1954cb224bb267af6ca4e2a1e0aee11946cb19fb8bb526017499058df0bfa6e932419e9114c7fa3504bae3aa52a34fba2cd6e0d3e3b57aef491f62d5cbfa2ca81503d2432eb3718df6baddad1042cc2b16fa503487386d885f97a0165000acbcfe60914f7f378bd732ac1632e2d3c231c0dc6f4f73190481327e93a6ea070953061c590c61cac490e057ed68d1041c85c671f90f92b4adf967b23d4b3bd487d88f2d20ddf0892d88fe69e7a732440efc7fcaf1bfe1c4447b2903475b6924ea834909b586dd15210e517abde7302648b539b2d3205bcca3c5e04320030fbed6bd23a791adbae7b600d61cfe58d0ecf9b32496dd8b49a19bc92e151bf569e3cdb51f522c92ad5fbcfb3825b2b4f088764f0347e072898b819fa4b7c650c6bf5bd0e3fa673ce756a382a7d7dd254265f60ed152b172b49ef4a87ec2cbeb0359785bfb85c5cf704640a8d891caef112dde0c9526f702286bf5ca74291dc3d23150254ea8fa8d7153ac3e5992750591938e09df761c13c4b4aa1921e072bfe257472220140d5ebdaa8cae7f560e9f714c95c52b4894db5df6c247c94baf38f12096da6808999fd18aa6670c88065e80226628c983330269161ac7017ffc0a4ffd32a3327d9a7cfe8db6a38ffe148801c6fde9870699e806e07e05aed0ad6e4ae588914c12cc74e63b4f4d30552d5fe051a876daef9218fc92516085c9864b930e810f0b61bfbc9a395b47da39e565817a2fca920d257e9ff9bf3eee1912d6c229cb578f5b0a15337fd914f134119c5153ae19ad490edcc124686d9fc1788c029d6cbb231dab0867395a3ced52cf5992b7aaa6d12151bb3ae8c3473238e72dc9fe1c255632cbcd1fc9481c38e4b3f9421b2aa75e8be1e3298246d5dfb689746af68ca58b6286b688406bb0bafc94c66286f9f12af684c6ebd555ffff7db08ecf3bd2fa20500b05ab8968351747ca14a0c9d64e6beedca294c73ce5617f6978c9b24e519125c94ba09c7ed874a80b7620feab3b353605c010f4b857897d7e39e3a6fbc840c848b4cb68aad5f3479611f2004a749cfe35a02ac5dc89b5393fe30f7c2afdc837b159c7a2ff06e596519c7116760cb3b73de1361f18d384c55c9e3540321a68c4e2e44e5c941b9f665059a1e008a16dce409193ed8cd54ec0b06a24ab4ae94fd5897ed4c4cdc5d1d3dc0b826c0b6ca7c81d0d98b62d261143a762da94b02604b811e3e7c53f79396d23a5b19f8794ad7850f1da3c9462a0716a77fb87fbe85d34c610c65e5a93921bce8160c2049a123f1e6f9357851a2900dec44340ab20ced52a80cd6e5ed17b9dc19471e2ae29c6cf98c7bec7d33c1cc38f39128f019367465a12455f773fd0ad443e0230482f9c17e3187b3ee26ac397edcddeee325f4dc38f7003776bd3f29645ce016d755ce4eaf4730ba5bea43cace5fc4f491b75bf4f40a1ef5d6880f57006f2e0ff39ff9153f842dbbe9239d0c5f12fa491325697746afbeb24c3fe8248ce5170bfa24f3eea9fc6bd7569cf327cc1dabca0c6d9334e669a0bc6fd382665a7bc60d5921f4c5b37845e303ad5f87cc337055eead375da43220395f7f258e2d617a028900a614996a6cc5e37c72feb25276f20428853d95e38dacb125fe86cd90d52c69c68c578991bf0a52e63bb34af47081cf8a6bac755d8a48593965adc95aea177dfc2a3e04caaec905d3c695a91784eca3b126da5e9c866d91c683b6cb3912124fc0d5d6688f89d4ed73b54d5f3e34face890f3958f7f8cb41dece502429942f9901794c73a7c09cf36212a51fd0782cc06fe30ee79f82d5484f23733f6c13233f93f7016dad4e73fff5bca533ac101209ee573f1cce3ff1fee1c719056c5e2ae2322639c3928eed49f97f1f1b48e503958593f036366eb21e23ce36b7214a1f59eae5e142031921a7723ce84d0724174f2ff35cadc02308f5fd66a0d59fab2fc15cd83713002d3fd0a2476d38b7851818a1b01b49ea13b0759ba2508e928807cb3903616113ef1e11f6ca8ab55043807f94c991add5aab254af2f42fc3ddd0cec41674123652d4628b6d3fcb5b051dd82eda1c68d133c33ba1bbd22a595c714a3ec3875595951d46b252c14e88841e72a69079ea1e1a0ac9cdb4d41e799c61a6d52ca385bcb12df3bc6bd2573d385559eb994f25c61406fa247a311066b9b2d6da3eaf9a50dc2d39ca4ea36890d8716745651c84cdece99eb27167760ee320fa5003bb3d07f7bacdd3251bb18c32693721a6558f007fb7831c0a7f1d8f0a7230909ae06b2c389fe18276a448db3414e3bbcaffa39a00de317b42e1d1be8fb0b3426ed4de416d966414a95a64f0006ccc9b753425580ec9c2c84f45dc83ad90471d2c0fd4fe6d06a3a279e72b673320bba822138c7b151d727b6abd414a88111fe52b8b0917acb88c22681ba264f140ab7b2cf227252babd5e83e3fee517361317646fad64650f2d6fc29e552093d4791db40cb5752ea44f8f1f42cb550c90", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000154d2d8b9b5e779fd4a14022ac64137900000000000000000000000000000000fcc98eb31a0ea5ce49a4df5434c3e52d01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x00000000000000000000000000000000000000000000000dfa79688268e94f45000000000000000000000000000000000000000000000003b27d8c4969fe006f000000000000000000000000000000000000000000000008596197ae31284d9a00000000000000000000000000000000000000000000000000025071b1b78e460000000000000000000000000000000000000000000000049f29ba73988453810000000000000000000000000000000000000000000000037166cb36624c1ddc000000000000000000000000000000000000000000000007d050eee5ee699fe8000000000000000000000000000000000000000000000000000258bb7095c7a3000000000000000000000000000000000000000000000005704f0d79a04cd41f000000000000000000000000000000000000000000000001cc8608e7634392ef00000000000000000000000000000000000000000000000af88bb11f4b13a3c200000000000000000000000000000000000000000000000000029db74f7fe7cd00000000000000000000000000000000000000000000000f4a5900047ae9ba6400000000000000000000000000000000000000000000000a2f7b686593ac5c9b000000000000000000000000000000000000000000000006e5e184d039f43481000000000000000000000000000000000000000000000000000265f972d900ce28738b09735682ead7a4c64799135201b3899d799236623c3b56cde6eeffc14c0df0717f0a39df3d76c516e8bf543821b9f4ba63b14188d420b92f5ed32350042061c4c31ac25d95dc3913a7a4720dcbd0d2b502eea4245f0e8e0b648eb3664f0ffcf0587456eae3287e653a00e8edaaebecd3e65d8ec98782517ef25ca5f5ed17e994084f3bba80177c052107cd4673933d4d7d498712c3c1a765d4ddd106cf23121b833a7165f5c9a7cbae738a5033ca28022a37b8399326468eccd785a1650e921736cbaea5f086a1fc0b8d34c9b3cf259d4260e3a0fb734e9ba562371b5d22be915dc76a01693899c6a34254a2ee1d2034d4b4afb734de2afae62fe3949727c3b05c1e3ca04b6abb43df9172b580ce32a1ea52c6a13bdabefe41b291b61b0acf905985d7fe37c2a12230fc1fe3c698d1495d937e0315f8ebc4f08b81f07b054fd658b021f59f1486e8105180cf9ea58448782cea621e76ef6e2b502df53c097f54506c667839a3fd6816ce0201339146d1334deb91f8ea6a14405ad6153a182d352d88c84c0f0d3446535350ae13ab7965e65605009887d7909a2fa1d42e2896551a0829706edd47b1b5786e28188506120cab5c15268750c76c2282e001032b6979a88d90b20a1db9d030107ffc0677ebcb6777c7f4ca2ee0510182362e2657252207babe037b83a8d8b3dc52cc67e771540a593d889433e9cb38eec2d71d52810b29a729959f506e4ee054bd6382f9acfcfa7c288777d3a5f4797e9342229726d66b20423a7f33a255cc294690bea014d9afb0331f91401058a271c34d2ab341191877abb7207f8754970389d7dfcfa00ec4ffaaeb0524dd716cfe7cb11e9223897133507d5227ce3ce11671f837ab1ef85d0aa6e308de0ecae5e49f712bf468ea81ea81e5527ee9e0f1a6128b1d1d32c56b82e3da07269e74c46c9978078a4fc2c3ec815158339b79250151302bb388375cdeada8e2669ddf041f19621eabb305f4d3270b826fa6ed2f25db9ffe39fc47e81839abfbecd91abc210133053d60869e919a115fda8d5046e59bfb2f874261d17b698de9e524a7145be6c521e3e2c29389e805e67940c921c0dd97b619b10e3d3e1822e3b2906d448ec85a031c38b5d1f34c2d4b5b7d5b6360c16fc3f097a28fc83c272b1fce5a6398bbe20a3997e59fc3a0e2317de98fdb917166780523993068ec1cdcd514df1fe9083f1991ee90b63c34c18b2639cc3c3df351ba4b8ee53f5663219c845085116bb81c1639cac4dda73a16cc4d7f9e8e607200c85a8e0fad44becf3891ffe93aa770132a39310b85cbbff14f0bce323cc23f445f6a258707355b3779232a642eff4b1a09e4b99928bfd02b699bf248fd01e881d98aef5772a6043cd5619e1a61af8b3303c7e35dd434b2854019587951ffb7e885f616ef360789979c7851ff5bb479421f42bb812a236b6fca6f7ded6823a28d36810b5eac6732fc2ad4410f715951cb2b5e09eda26e7793afd5c7c865de1570c4e72d9a88ce4b31a88264717a070d802a817a31b2c84d4c1f13a918c3a73c88d8a67f6c81fcf41ecd278894e88485c5062a3ef24ec877338c2cbafd1e61205fa95e2b93c59326cbf8f6468453a1e85e054db79eff791b9a1cb823dd9566f06129dce2b1f1c15e9c6d878629d40f61512eced624da055ff54fd71cf3520e5a759b33d31ce3d711c6e1163e3645a725960c4b1b6a5254cb9b135176e02075b45e9e1ea54e21b1a420b1414abc132d02be133cc2dfd9cdbf2a0a44fd11b51779483b4052c658b345b31ba93b918522167102cd4824015fe51e5b84a6fb335ac4842f9ed11d6f5e69b4d8dd8ba64e0f36270f78b42c71621f1501e1ba49fa4e661de16cb8dd3c698093969b8964809106412790264f0ae9b8007b76649a949b69ac1d6ec2a955902ececb7d33336b485a7c2c3c6a1fadf0fd121f79fface6cdf947dae0f776d069829fa900f514ece4141616bbbedf7de53e30cdfab6d5619028b321d44e93184fdf01e7ab3f5a4424386c23b8e5447413e6639c215e2f7bacbbe08458fe90fcff82f4dc73b1b9fa944e91108a5c28ea9df2a9e1b17874a186d1d5301848073af15ed52a4836a71b9576fb284357773419bdaa332b8130670664dea49b96012f02b091e273421d579b8b7c0db2b2715658dfaf910c32aab84a6eb1daa3f8c4c72cd2b43abc44cdb4aa8a0b0dc67c649a6c3b9cdcbea178401c0abcb272d8d5240a492956cdb2e4a3de78841ec42a7c9a5ab3b10d4264cf887b1c81451ebfb5e4277942e5736a7049bc071c168f11d6111ca6731a245c4f6f0aa9acba6d334d552193f8453f06e454c0f1b30e3264e919b2c667507684454b0045f26da17f27caf045c7b4d2f6be92bfa160277b4cf55287f67b9560a68e53fde3b7b85ee9043123cd7039658e7f874c136d1a2c26f2deaf00a930cd611ae4ca28ec30d65792e9493c2175e557b21e31a5102a8f42f94c87e31eb528d7a5fd4ca83ac3a1b62ed65113bf25e5e0a82dd2cae52cba89c170eaf2134fd4b818630ab330f68c040bca7285e619b2d493d713fbfe079f72b0b80fe7f56ea6eb5b20bf7990fa72a7f26191f159a2eb29d01801330d26bad38bdbd921c0e68d44a56c432329d5477f1e52f1d720e9dfb09f24fcc2f610e4303caced66f68ebcb8d876ec6f7c1904ab11800505a020eb41124069deb92d748417971506e406a675a65eebf63796897742b6080c037c097e742a446ed528783529668433efa5bade2e3f9052c11418f54b8bac5ae047f5fd30ba9e031f2bf38e36125c725b69365847ab2eaffb80c917ac7d3a97a47c1a1a9950db209d0326fe9bb980be5ef2bf21a3e85f236f53aa3843581e6e49d3b4451db82ff50218cdf7411b81f746a1086be6f27cffbf9f2352b4fca25777ae421cb673afe74d276522587e772895b53893a247b5bfb427dacfe13c2992700becb2313d3dc00f1876d430708da7298c907dff0b5984eafba5b26cd6fdfd59363b0bc511769da02db19b422e86e8e4003ad6554001fd10fd87d5f32af8a9d13f23bbe12525038a1a7a3867ea9741941c29e3aaa8491855617b16f140ded2b19a0b03cc4a4cd74c20f4111cdff9aaca53255e23c1c6412be63bccc4930ef87a63fb8e59411f56d4214b5fdb1a07fb3d5e91b25b3df5df862ad9cd72d3a24325c122801358aa149d2c03a7e24a687a2135762b2c1108cf1432e73f5ebb8e20a552ad92a91dbb185f11f9ede94d1ecebb9b30bc4c1a7744e2d121a63bb8fb54c2a83dcb8bb61cd6b600571edfb0c5ae31403c4fdebd9bfba727fe0422077b4bc1b660ad5ec5db84b7279d5845420a92bc08b2e5698b81383cc1490904ea5f20406d04b670ac20b49919e8beffaf81535d507d4ca8b08778bb8b791d016fb0c935e0ad7f1c6146cc4a262ce42077ad7d82e2e3848618c2e9514a3422581329e323cfb5e6153a6131990a4326ca63611c14ab69058cdfe7289463baabde69bfb64c895637ad70f47acb101d099896c7ba7b7aaeb2292c376efdfe9278aeabc23363ec0d2c0fad2cf14325801e832e9d2edeffbe63a053f8d6ac11a13118adbe3cb26f66bdc367bdf2a11bf5271e7b3828c52104b02b5f157695823763e7093e3e9e680921cd0c3dce450f4251845969a5c04cfa21f68c982ad2303130bdb22134d6210746c76817e25a249c64fff8b9d2e3651db73d13eea4360f99ebea5b17fd312fee8b1117e4967004fcf53c8939497fd46ee37dc67a58480fa1d8f2cba62cd1a04755796bdb1dbd0625d390458c05bc57aa6cec0ecf86e5b90d7d7484ce043ba332d42367e4b5310c45f065d59ed77cdf230d709944f6dab4c7782c82a2ce64161caa970c2c071c11e59f3300de1c8e44688c10c9214d8ee5d1402f31710c6c033adcef7c521b550cfbf8b66cb58eaf5b4f600593e7a7337c5b37df382e5df9bb7da69acd0935db2f71443d93c858a05143b20f06298edcc053d6f52755f8791f02c80856e1f55b0eab451543b5001697d60235605a12ca6377956f5161eabb574ea08f2741d0501dc63400f0e57099a68241c1d29056336dd70b0ed371da2615573480f10f933212ec6e0b1f25cbfe5ab5958a232a3c6555bc5d9a88ca2a5433eeb71500a178dc1003e570f070517a79e4ad9d72892bf563aebc21f65a15617fba60ab62749c622bb9822e4621ceeae8fc692ec1384c31b3fa7f85ea545106bbd4f0c8b5b03a6a0f64ca4fc38aaf821910592887b951fe39a88fed773f3c32e9d3afc97ee079d406d44e6772130b54178d8e84cf33390e8b880c554746bf355472185cbff9ba4c218facbf53bb5db501c6c583ac7df90901c1a4c7bfad4dc5a1f811986294c3e30b018f42f8d486433a79cebc05ab5a4861a0a19f413346be6108d93bbb7ab24d0f1e374662c55fbd4000b52009915ae41ace2fd2798513822da5c47b69c627522fa60c93b96255feeb8fead0646f6c03c55bdf0ce8c90d8a4ced28275e6005101dff152a15dedc7a5077b8a6a24fb6ba47511dcf942b097d341439041c53dd82030dff114cb5c1acff5f422b9f9157306540025097b5ca9666848657a1bdd8ac035532d8747b5182f12d2d76caa80b6ef5f2c23ed91c230c03f3c38b667fe41f03c5292df7fcc0485405a81a40666c864a1c0bd0f9797d7f00211a2a4d7073be1a8cc3df21420e1ac68e87a2eb75b32473cae6554a8c5118662b250f3c8e00ae2e2973ca4bf0d376bfeeaabf8f7b5c4eb4761a0b51fc018d7a205f02979d60c2027172019511cbaf761d504bfb9dfc8e01e45cd38b4e10103c13b2146174d8fb073f021f5d8fb8369b690f7a5df4044a2acc9e308e6165e6f7f5ea5d0d5f695a201986328ac16e02384a0f49de5929baa1956458ee19a901e8fbe31ecfa803bd2954fe80592a60132ae23e7c8d4d79dca4d60db2dbfe343b01cbab95a814deef0d2f09686caba0a5e8714860f809bff73ea57ed27ff26c921247633ff0f53d010898444c0522085bf95583c6bc1ea11245794f6442c79c3467afd6de0e938b9905fee8793edd702dfee158d66580b78e7124a0aea44968f9ded2dd0500d8263218735ffa49cc44890afd2777727aa4f9895a7039122d6e333b3520dde3f6096c29ec16859e98a504c88d771cc47aec7ff40a9e4f201ca83dfc7574e01261a9ea214114b3db1469a1cf55002bbd38d0ea429b1e24004d7677a57f16b4b0cbc25803108c1d04f5c0c3ef669689f62188b71909f1c11d95d514ca3a78edc8a7976422a8c196a42444ac4076ff06e369f839e38244111f04d4f74cc3ca2d635bc3001c60ee36b09471a5ed2d5db3a067183c50ddd40f2d74469507e949541d101d7e16c41645a3fc37a00b4708cb588611fe15d807d9861ea110e6883f4df31cc8ec278d447019f5d65f772020851c65d6a10ea601c7c4bcf78e58fc905b2c664f0d2c457a65f6424d6739ccaefc361f1e700d1735c61328bc29e75cb0d76b1c013910c82a1c8b34214adff97e9d94943f069509a46742bcd835da11ebb9b440dd3624c70366a5708a3a10b9688401e4250bf48266aa77d0ec6e5403d5db41f718f30f947098f457716160288bdb4721cea1adc69d2745d7aefc57eafbd2e901013b2ccf7ff182845d214e0fbe248a08364934d0ff522a9fd3005df960972ab255052930025839cab3bfbb81d5127ddf6fa01a61a613a6e8f128a85004743292f3cd2c1792d9ba72f886ed7a121297461743136ce348913ca557297019e2a3aec0a51922836c33d87bb152b06b9b9f3bbd144c703eb94cdf020568660d841a255e5d276bde9c1bf661ff908ef89874dde149fc4dceb010e3a6a382e292716cb4b8d916263774fee4ab4ba83a3ebf0b1064ebd75b9ae887644471f86a6bf8221107cc078182f2a821d52e0cfec07bc6657e50526ef9fd0048ec552d0808a742742aa625f90291e0ca4e5a4bd0c2588f4f2072da4cbc09e48791ccc16cea22ca882cd00d0381fb3133577f3d82a0a0bdcb0db0e88fe939063377ee25026eaabd38be1902111b320f710bb3503857b61c13fed36475db6ed1080063626c619d22f9b0932aa34375d85de0439ab7c194f504bc2523abd9627a8eb0c3ddfd751df66f5c95293d54b5e457bb7a9f8362a8f0e2a8acc4586c5f9109067fce81c82242a2eb5f2558d88d1bb575b0651061ce1f9bc8c75a95c5f785ef7933948632c1cb5b43592f88a62189b6ddad9274e56b188dcf13db455a4801c175f2bf802dd46ce5aae405421fc32aa47ef04f6c1ae84584ad7f02e065e6a7cfe8d1854a0f5431c7dadb0437a9c689c31a4cea1d48dc8bc6b635382ab3f7dcab912b6169fa9550900047047ef7b1b8393d511c82c6551d90fecf2662f336597b24113124e3d13b21b27b05f81ed1c2b356defe173c7cb7e4a5d33e4dda4cf7020e7fb85bf2893696a1a3238660d480aafbb0c4271397203e1a80122314f7b9ad807960830b0cacf78e1b1d45f76d3dce3e1db3071e9055eddca41a84da428fd19241adbc656e5cf7255b0cf5ad55a164b6805f639c3f7202975845aaa64ec03b57fa721daf278099b20c04630ee5a1d2cf8aa318da7097dfc780e843e1e2b8d7a550db5cad5d2ad4cfbf2e115682cc648ca983330ffb7283318b6311873be9977637d3acc6b2eb932bd00f387b1c9aed7bf928c15e9af4b7c165e4b59cc38d8e190b8b3a81d1c6d9f83725e0695fb46317bebde0aebedd01b2ed491ce402d501aefc8f4c1c293705be6b000354686877258d5ec4bec33fe416c64ce5925bc9d9a537b439250967bf458702d23d65decbeb6752b51e297d25b2432a6f5b64c0773b205c4e1446b087a44324cce538d0ae9959129c92b2240f863c773941aa0d1c503d20b91f430a467b4f2bbe0a36f9facb42624703f09f4288e1c7b74a9cbc5b2bdaf212a49f219b618c2580560061039f9eb6c52b7688ff624844a0a4acda0f1db128eef1bd0259ac3c176b1db37e377cb9d25a85c75ba3643e894de5bd3e998a4160837cd75f1cdc723038cec04a4853a6ba6c62cd4918f1632ac30de6ed5c0f8b0a68398ee741e60f184ea704315635056068c59f3feb2e28bba86ca05e77abe071bf4d179a0e9e2d0d2bcc51f8249afca59bf04cfb916937a0183917178db300a3d477e6c83f018f0db99b774c888c7019932c91882095d48b2c84a5004642e7ab200a22e90de77515c4e35e9d6ec6d71aba421ab36b30e7328fa15dd936f1c740838181ff349e122ffeb6ee1157415c7f104ac0f38ed2cfad62acb25a58a89eea6a5f66b4bf392520de5b7a89e66f24a828b7141b01fa5c1067327831d040d0e7a07f565528f2ef250121b6f9948bb2f3f2cb4099853f11073ea5173ed10bf2cd22f90108fc7c4c231b03d6f484e3bf256fd3d4f82388d90090764a9b6eaf6cb2cc2806c5fff469294e11a6fafce22457c6817315bc429ad19853b7957af2af6c3389e10c5a105a2e8d982fe73afb4addc3aa9b344fabc343c6438c8a59098f5936add0947217cc2ab165ac1555e0264108944578a3738595d56b7e274644af28a52c992a33209c05dfa9b8864f3155f77a4b18eb1eb793f4721180a337c7fb5a0b499ea96afd5d00044ba5da625f9a36bc38107cbedf12e0a51a8a2f59f2096e0d20e065018da92bc0a7112a7e48a4444a728d936338120a122db00a096cb486e3b67164120ed11205e12b3245fff17ccc63ec28b2e2a1f3d2bc7c8e7403e0c9c5d5a1514a2b900797eb3f3ea1748dfe9e23cad65e839d8e3074d0fd24a1bec13252b57f821f9d068c1cc3c26382875bc0d27da9d3bed3fdfed96ce2b7ddd3446e35d91b31d301164e3aaaaa7534f8ad4eb17b0621e6e5c876d84fa1937a023d2c29e37bb14c9202c71cc7c96f699ecaacae99aa36e15dd8055793bc6276958cbcad551d2448d1199499f2720dc367e892cbec806b3c9459f49f22dfa2b990fc8282a4ce499ba20799ba426355a8ef8dd0fd899bcfc06372d212c6ac1f63c8bd5e6914b18cbcb61704504f2e1e2ed6d86e2da36bbac86aeca425a6720cb4facfb7fd429b85ad57032d86246b281117624b1b92e7da1c0d27bbbff6a32918fc2f5945cd0b4a0ae312ce4799fc5d07c7180005bab2956d23025adfa9e3e37b7a6166084eaec7740518fa072d7ef56b4c1f8aa21142872240e2dbebec78db08384dac3cf7c36628851830f724379349291aedfc0f9cf05c92d4af5be0a50f586929489f98bc520eb906de5f1f41ebe26c58492fcf53552fa0d1df87ab01ecf27efd93652d85bf06180c0bd83850c4ddf904a68bb0f3a24d6703fbb5e7e3e3d58561e1f21783729acc1c3d2d9b96213fba91b08d5a06eca9adb713d1636c1c20ee1c6ff40a786694870e24656dcaccb56da1ff15761c4128774105f4869db44cd54a07414e76bae9ab2bd6f76e121840d8e8f87364deaa979ff118965f2ccb1ef97b3e45187af7eee1034b6d88504c0eb3f27f35f0f36cced25475b2f62df8cceedd3f4a9c7808fe41219bca2c439d0b29739bea7622a1d7c44d8a1de6d59c63b7ab55283f5df05cb70692e71c660ed45d49f1fb0790bdf274bb6cfadb9fe7d0c436f8f784478cc8df189e594ea67b7432713bf22c1199d90e0043695858aa46cccc397e528741c6b52e19c9ff45986020241cec343c8d8f74989c21f2b86e94a704335942c967bc9c032ae6b105131dbd3e550889cd96d3577f80542ab10e53487ebad39ca428ecbb236b4ac29357495fdbcd24b194b37aef7798539cb7407fc2d16cc02647616fda1bcd71bbaf8633ca0fcd9bb267a051933c14313c8a09c665da7ab26b142e9d811d717384396cecc500f378df1db9683fda74d5bc2c4e78fa1a89f7cf4975ad69066156264ceee9b8b8926b5a7105c90889998882ac9420f532112a6f1f2f2b9c023a3ed64af572f19d017aa3ef6afaab087d3bd768155bd3df0fc9f7f822ca88009d54f9ab76967e873ce437bd0f7e3195cb48599df46f54ef9d2e424a93f59213e02a423870816fe39839111cf60a4d2ec4acd8e43863cecead8f660361b8a903bb39e0d785dcdfdbe89b85c713b3355eee749fb6027ef71e9984d38806510f18a4b230924b0b848a291f8e62922077c1034005c5ee611f550a746bba75289e04ea00ca60efb7b8587281ce5223eaaf8a07771df77a7dfee0d61b4d1e01602517cdae4f6c4ad7205b5c2d781c36bb7f9c36d3a1db2c4eec78e001dc931594700617d097a075cf9b4e688aff67aeef2b5befdca4f21e388b8c655c2b55c24e511251c2d400d40a3946224c052f1ce75a4442306569e701bcc086439a62f9e04c0758fd7bda12e1ce40b209d30d67f2f003fea544afaa1d0420f6608116269508081983a5d8a574b216c19749a1472029898399220ee4a6506f95e7c5787f5b9925e200ee2c2cf08cf40b446659aa9b72d867e89de012b1336c390f8884c2087e09f19183af33cab63051be64938e007258d7bc38158dc0e80b4f2b76d13813ba2926c0c2da99395ffbf9e8cd65c95721cffa67de1b3aacb1e40f9a75ae46d9a11e06d148dfe7adc51debf3a5ed2958ac53f09e30b368648762c53d629ea154aa10cfdd10faa8de5faa738ed3d7bded2740c0187ffd6fb2f51ae27931572d853d27b75276de25a8deb23b075f1581f71d8b45f9525f54c924a3057476123f900f1f8fc748b50883c13789486e4a0750c4c2b32d747542761d6b41b5afdb17086b17b7f3f2125e5df91bd8ff04ec54b3b732214ab81bf6e21560db4780f1f0ce7329d6540a217743efe23cd1c6694b52623718e1478973ba5f76afd37d376b8930082c5adf2d53ad44dd629106b3c3b3ef24829f1570d6fe0e6bc673a33d3f233406fbe881c13361f5495c94f2d43887657f474a5ca6ebdad16ee92ebf3755c619252bb90cee7c61ef1b3729012c54101ff03af47b6444e81ace561d759d309b050a82f6e71cfd7854a68ef7452aa7e48f7c5ae0d48b5f2d54902687a8f0bb78852bbcb6b17cc2f8cac0ec1526e2a60015d3efd9a0426d82aeee4260b71813bde80392ede8d86bf0d16af0977963ce5a573f7d561fcd20ab05e2db8ea7ccb334ab0307670beb2978093fdf7001199dd0d72b0b7752ecfb91da30847c8243072a7c2f199d811b6a6c1635c6ec835b05fd77235cb3d8451194f699293e70133d756f2c9881849ce8eae584597a83c53a47450a578d88f75f6982d17ddea04e9b9ed5095939009049cfc7cd6cf9f412a3cde99720adb91ee077da2aeafbc7a033044a0b672f1ad786fbd543528c38703fb4a37c1db9a2674feabf00ff33a5ceb01bab1aa064e846cd5d748df1714875b6a62c9dfe562519ab164540c0d7530589c288064e7b496b7184387d1d4686c18f22110364c55e53f3a70d016832be570218a012f6b252d06f73d254937d9062a293346adc20823419633ffed17822bc3259dc061a1a8863d10da15f11186f71f04b20e0d152385bc108ff740a68bf824f890f05b422bc8f53d3255aff1e028a79f6da0e0e24e931497d03ca96b04e687a6f6300ef2bf5af84353c1e1a3aec79fe45be15ff7c3791aa7dbb919da84d1106515f2d5ecbc9c4928ef292fc4de434d4b92ac996d21ebf2d7d0b9d99a295622cf2ed2ef5cbf883993dd846bae8f4fdbae671e7338c6e1743fb26ecfd0e40e409c60b03445ba5b1bad93b6d8fd88b0dfb82ea5a9a01db23cf5f74ec68b4ea42a309c624208abd552ce7d9b316865cbfad7d0c232c8f3589cc58f93168ad5fef7c3843002e357ed1e5708133f433b4e0976a4b1d8f75d99556678833e17699e399f9970d0053375c14a0ab5cd8135fe265e537ae1d98e32f64258458b7ca404128db1e2c187a91f43be9f07bbdf161b002391f1b31d31a2c8f62d7dab2f8d7c4e618a71a943d0f30fbce765f0a81a0b86f000a9edca80be34d9fb22fd5959a52dff8b214d45dd690880833365f782eaa2bbdba3cc6758d345dc129399b5ad29e5d8b1f25d06a5eade8a0ef42d3759d662b6c3b27d9ca7f5804c2b3e92f1cbf128efb05252410af5cc57c287a8f8c6c3e7e83ac898b59003c3c39da20e238659b09f1df157216dafe77fb079590574e4b4123bf304f18960ac407b2405de4825cc00c35061d04805f2451cfe3e026bb617a8e284ae1bb816526a9c506a33186bdfacbd016102199b0ef23d253849057f884b691e22de105d33dd87777305d5ea3c564ff00af3dfa4db7f4b2aa305bda33a2476a6b05cce69d61d9f75f986dfc4527af3f19102cb5b69b2842d6d7e9e90e8d575adbc16ad8a3003209e911b2a11d4e200225ec90847799d4821848766737493f708ece7a692e4885692d011ceade273fc4041c5d41854376102a27aceaa0aaa4c3257eb59982599d817fb2605623931306009cb304bbce9135174946a02bb039b7995536a8c4dbb218259ddb2c1c5b9dd411f093e4e518d590f06f8c106fa0f4df4b40f10db27b63d53eb4decb2481d15a1b1f9a4dcf66168ae5f6d7c79354ec366e79b5875482aed5d9a686fdb62b17630dc56a8b87bc1dcb6329bf2d19a56110dcc82f939acb1a6e70e994bb94ce7ff31a3b1db0cf696f5c842eabdc0a6e9d5372ecb94426fc8faef668ab3d9576705e304a945cf241ce3038a11c6f5622a3422ca25d6d0040b99e5cc4e4b39843b7d72534843b4222447511a5174d7c860e10896b36b505a0e16e37cdf8ac5691664e1e5ea49b6f6f35384f56d9faa8b6aa800fd2444e741c31814b3bb2c8c6cdf828187c46187808eac8eed732b528bd82ac8f43e6db3dcd19ed12205172f578245a1589aff49acc083e14f1caa589eabd4d490f6940acbabb055c05319dcded45bf21f0d43708de5ca2383859008ca641deed357917f5ec7ba58101d82a5cc8a6f524f19284b599173697a880f26ba158d39dde2cf107c3dec0a9723aa76b6bad28134544a94eff974c94c041ba956834fb7b8a20cb3f9594b54e896704c00828401eb747c909f8e2fd22eab075a9078c84b13b6bbac635ae9abaadb151c6e827b606742f6ff2939ada26729759830191df4708924fea7ebfebcff8f927d22df05724b550de94617b485e6049826fe78c7dcef0a4b2d23fcfd60d744b89dd19180f1b06d00b6b4c79b71bd446267e49ceaf11951b2d9082b09d0e3489fe070a744a0e64fa7d24d7703e2e82a7f26381ca659b1f66255b7efd0df075b6ba4de7a085030738dcb43875953999c2f7d5392df24b864833ccc9bca50062a633f8e7b8150bb74b7a7c537b12c72b109f8c212bdc542940fcbe580040f301baf107ae48ae17da9f15e0d3439be0bd2a45788973bf8745d03428478327de779fb78c055f53138a8e8df3e15405872ddae7b1ce4515871a20b0787503e861c20abb64c517e0042a2680e7a04527301326182626e560ba34cbcd567a6fbb797077ae12ee618312c49cbfef179c6440930137664d6cc87902a61de833c0aed0a616790b8a9ff71c492e8944bc3f60fe2de37788fef01f939351f4b1fc27098590ebadd75746f60b012641c5ef934924a686fb002c329d97277967b2da3706e342c7418340cef72d0392b4fae0d4ac069893efedb6739f3bf6c4ccc98c201a908dd8f2c46ec94720c00cce419319dfc91c3b842b3e8591e284dca480d39268d31117cdd8efc4e520f283358f61913978ac3430c515e7517268d57226dfab2e09755fd0fbb8654723c336ff3932cd6cd57377fc84ad99e65e92e3e62a10cd8cb84d1eabe63874b70602ec92c43565c29787cfbf0eb1066ce59023d4ae6b066217bdc050dfad86e31c4e357566f631a0ec0d3fa61c328756f7e8e9d5b3b7c8f86cbc08daabc2e8f31bf8eb84ff8a4a6db3643f1495b21c73cad93e3e2fd220aaf09a29cf4c9c5864053a1331b441aa1c585625e656e3e58df740cf3d457c40b0429e10e67b7544310643409645badd52389b0023568f354686018d5cc617ca0be53ea64144ab52b5109a28e74e789766193593c8bad70d10b403c06dfd7ca3b97079f9a4974f563a19019c8c895f9372f50226c3b0976ba2dcba703af39b1c50c7a46b720ba7d6491ee9143c7b653930901b4617eac146fee04757443f36c7156af58c8bb33670bf2928276bea49e7849cc27cafdf183902bfbed16820c68c2238e877290b90a118068947b5a8977360e5a952e0036257dee5e0443433bc39b931f3a78a42a124210a51c58cfd80c56b67a52a3238b4a47524f9e269369e91ec03c1b7ec246c04420d0035d930d1a4936dae183715bee51356873764905f7e7ca33c3f31a56996e22caba046602b6b24130d134eccc1cfaa4eeb4a5eba6fa0cbc022057243d8d478035a50d38b9c15b98af87ef36ccfcb1a41265398b117976d06a7b604b0945f6724c34befafe430b6e687635eb05e98b27b9e57fc05ceac658bbf43be2b055ceb12385938b72f3e4e0ec92cbec13b6b816fa60b5404b418c33fe661142a616b6e24e8f9c3bb35d37e457d1dac5b9c74f471beaf7f8f4d26f608f520ccf0e113801460fb5f0d9b7c6ec1f81756e4486d34752010a0dfd19ff23b9bab9cdcfb1df1251615cb508e4922ecfab788decf45b7e877c73e46391f8e7368dbc5f351ebce144bd31203ce49b741425627c097ec248008f704ab6321f4c59e6e6c1adcf21d255b9c0abc5e5d1e913335b2f5c18fed758628396b06073450bdc8e51b9ac17a15988467eb7f3ec60395c779f28a2a1d9998f24515ca5b47eb5cc29f875c9d1a1165b3fa07fd3b99b18269fa72901035818a9bd02031ad308ff76ede118c97350024db3130d1c0b83bc7b65c3bb1f886c3996c4cd8939139aeb8e749c34ff131274570245f1e762ee28f9876c9e240e56dbafeda6fe7047f8f56148b0440f23f0d011a69b14d6dc35825881a3eba8c695d89526348a2e783135364552adcb2de2bd4bb42f48491930f07c20e89c9f87dc463d93e72a7162f518cab3474af3cd60888cd45bad31620195330ad1fa68c7b800b83761b351b440ee1542ebddff7fc14b018c913564c935e83398d0e28e5cbfd160707ca15b6e4efde70def302103524f2d9162a684c86e6dd17b74948e34b7523839b5557457dae8b84d9679010b82b4f9b6ccffc1777904eda4e4583e195476f62b6b2fc96fa400281d935327130216f637efce4bd7967925161f603ef63854cd24c1637de8a8fb2fcc7031bfba6108270c3dcf4e240a99c15115a88522efafbcb2905c304d35cc0db64799797a10e49bd25c35c762064bf13db5ed47baaf7dd7c0f4a4e768926eb2b5f9f2b7e9b0780258805e32665ef178dd88e89fbeb7c2dbdc7f4c7672d6cfa8a46075a304407aba54c51707cf93558bf6f2bc15e138ce94d38ef015c8abdb37dc627159b601389b40547fdd18c07caa2e8c156ac50719ee2e1e57d111bd79ae6d03a1297a40c36bfcb7110608d12d503545f26013b67f6883ade09afc1a9ec2287c5e0b4fc18961675c195b7025239a4f728b6354262a3b333a9f49a39f5f740652ce6fb271e03230bc5559de174f64dd0d90f02b5f2a2eaf004278215a9d856042f48cb3e2028a458428b511f716a00e9432a28ba15a6eb8cfd6a26c33641dcf788c1f6a71cfdd7f6043c8cf84ada544f719aad323ec1d2ee9f5a4888114e008cf4c467cd2b509709b141b7d822abeb2f1122d0735485f29454e0077ef21016ddcd68fbe9", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7001cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } } diff --git a/circuits/benchmarks/results_insecure/report.md b/circuits/benchmarks/results_insecure/report.md index e188a9da2..38cac38db 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:04:33 UTC +**Generated:** 2026-05-21 08:52:11 UTC **Git Branch:** `feat/1525` -**Git Commit:** `6e6e3d396b3ddc7b5e9901bb66c404cc58dc06c1` +**Git Commit:** `a6455239f48858b46d3a55562def9147c130c18d` **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.12 | 28.68 | 15.88 | -| C1 | 57818 | 0.33 | 25.71 | 15.88 | -| C2a | 142625 | 0.75 | 24.48 | 15.88 | -| C2b | 198355 | 0.82 | 25.54 | 15.88 | -| C3a | 132633 | 0.80 | 25.02 | 15.88 | -| C3b | 132633 | 0.80 | 25.02 | 15.88 | -| C4a | 92515 | 0.48 | 24.45 | 15.88 | -| C4b | 92515 | 0.48 | 24.45 | 15.88 | -| C5 | 151717 | 0.79 | 24.83 | 15.88 | -| user_data_encryption | 53732 | 0.34 | 24.75 | 15.88 | -| C6 | 86927 | 0.50 | 24.94 | 15.88 | -| C7 | 104273 | 0.47 | 25.49 | 15.88 | +| 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 | ### Artifacts | Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | | -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 10.69 KB | 0.47 KB | 3042369 | 176052 | 3218421 | -| Π_user | 15.88 KB | 0.12 KB | 2972905 | 170392 | 3143297 | -| Π_dec | 10.69 KB | 3.47 KB | 3553763 | 187368 | 3741131 | +| Π_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 | ### Role / Phase / Activity | Role | Phase | Activity | Prove time | Proof size | Bandwidth | | --------------- | ----- | -------------------------------- | ---------- | ---------- | --------- | -| Each ciphernode | P1 | one-time DKG participation | 302.76 s | 127.00 KB | 128.19 KB | -| Aggregator | P2 | combine folds + C5 | 0.79 s | 10.69 KB | 11.16 KB | +| 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 | | User | P3 | per user input | 0.66 s | 15.88 KB | 16.00 KB | -| Each ciphernode | P4 | per computation output (C6) | 0.50 s | 15.88 KB | 16.00 KB | -| Aggregator | P4 | per computation output (C7+fold) | 79.62 s | 10.69 KB | 14.16 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 | ## Integration test (`test_trbfv_actor`) @@ -53,15 +53,15 @@ | Phase | Duration (s) | | ------------------------------------------- | ------------ | | Starting trbfv actor test | 0.00 | -| Setup completed | 3.05 | -| Committee Setup Completed | 20.24 | +| Setup completed | 3.07 | +| Committee Setup Completed | 20.23 | | Committee Finalization Complete | 0.01 | -| ThresholdShares -> PublicKeyAggregated | 302.76 | -| E3Request -> PublicKeyAggregated | 305.29 | +| ThresholdShares -> PublicKeyAggregated | 304.14 | +| E3Request -> PublicKeyAggregated | 306.70 | | Application CT Gen | 0.31 | | Running FHE Application | 0.00 | -| Ciphertext published -> PlaintextAggregated | 79.62 | -| Entire Test | 408.53 | +| Ciphertext published -> PlaintextAggregated | 79.88 | +| Entire Test | 410.21 | ### Thread pool (same process as integration test) @@ -76,25 +76,25 @@ | Name | Avg (s) | Runs | Total (s) | | ----------------------------- | ------- | ---- | --------- | | CalculateDecryptionKey | 0.11 | 3 | 0.33 | -| CalculateDecryptionShare | 0.61 | 3 | 1.82 | +| CalculateDecryptionShare | 0.61 | 3 | 1.83 | | CalculateThresholdDecryption | 0.56 | 1 | 0.56 | | GenEsiSss | 0.12 | 3 | 0.37 | -| GenPkShareAndSkSss | 0.22 | 3 | 0.67 | -| ZkDecryptedSharesAggregation | 8.41 | 1 | 8.41 | -| ZkDecryptionAggregation | 49.38 | 1 | 49.38 | -| ZkDkgAggregation | 20.65 | 1 | 20.65 | -| ZkDkgShareDecryption | 1.47 | 6 | 8.79 | -| ZkNodeDkgFold | 62.10 | 3 | 186.30 | -| ZkPkAggregation | 2.15 | 1 | 2.15 | -| ZkPkBfv | 0.33 | 3 | 0.99 | -| ZkPkGeneration | 1.36 | 3 | 4.07 | -| ZkShareComputation | 2.68 | 6 | 16.08 | -| ZkShareEncryption | 2.50 | 24 | 60.01 | -| ZkThresholdShareDecryption | 6.12 | 3 | 18.37 | -| ZkVerifyShareDecryptionProofs | 0.10 | 3 | 0.31 | -| ZkVerifyShareProofs | 0.22 | 5 | 1.08 | - -Sum of tracked operation wall time: **380.33 s** (often much larger than end-to-end wall clock +| 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 because work runs in parallel). ## Raw circuit benchmark JSON (Nargo) diff --git a/crates/aggregator/src/publickey_aggregator.rs b/crates/aggregator/src/publickey_aggregator.rs index cac4447f4..4f9324572 100644 --- a/crates/aggregator/src/publickey_aggregator.rs +++ b/crates/aggregator/src/publickey_aggregator.rs @@ -738,6 +738,15 @@ impl PublicKeyAggregator { } let committee_addresses = committee_addresses_from_nodes(nodes)?; + #[cfg(debug_assertions)] + { + let n_registered = committee_addresses.len(); + debug_assert_eq!( + party_ids.len(), + n_registered, + "honest NodeFold count must equal registered committee size until expulsion enables H < N" + ); + } let corr = CorrelationId::new(); self.bus.publish( diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index edf14211e..4bcbff437 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -727,7 +727,6 @@ fn handle_dkg_aggregation_proof( req: DkgAggregationRequest, request: ComputeRequest, ) -> Result { - let artifacts_dir = req.params_preset.artifacts_dir(); let job_id = zk_bb_work_id(&request); let input = DkgAggregationInput { node_fold_proofs: &req.node_fold_proofs, @@ -735,7 +734,7 @@ fn handle_dkg_aggregation_proof( party_ids: &req.party_ids, committee_addresses: &req.committee_addresses, }; - let proof = prove_dkg_aggregation(prover, &input, &job_id, &artifacts_dir).map_err(|e| { + let proof = prove_dkg_aggregation(prover, &input, &job_id, req.params_preset).map_err(|e| { ComputeRequestError::new( ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), request.clone(), @@ -753,7 +752,6 @@ fn handle_decryption_aggregation_proof( req: DecryptionAggregationRequest, request: ComputeRequest, ) -> Result { - let artifacts_dir = req.params_preset.artifacts_dir(); let job_id = zk_bb_work_id(&request); let jobs: Vec = req .jobs @@ -770,7 +768,7 @@ fn handle_decryption_aggregation_proof( &jobs, &req.committee_addresses, &job_id, - &artifacts_dir, + req.params_preset, ) .map_err(|e| { ComputeRequestError::new( 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 043328b46..87ef773a2 100644 --- a/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs +++ b/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs @@ -18,6 +18,7 @@ use crate::prover::ZkProver; use crate::witness::{CompiledCircuit, WitnessGenerator}; use alloy::primitives::Address; use e3_events::{CircuitName, CircuitVariant, Proof}; +use e3_fhe_params::BfvPreset; use serde::Serialize; fn proof_field_strings(proof: &Proof) -> Result, ZkError> { @@ -322,8 +323,10 @@ pub fn prove_dkg_aggregation( prover: &ZkProver, input: &DkgAggregationInput, e3_id: &str, - artifacts_dir: &str, + preset: BfvPreset, ) -> Result { + let artifacts_dir = preset.artifacts_dir(); + let artifacts_dir = artifacts_dir.as_str(); if input.node_fold_proofs.len() != input.party_ids.len() { return Err(ZkError::InvalidInput( "node_fold_proofs and party_ids length mismatch".into(), @@ -335,17 +338,21 @@ pub fn prove_dkg_aggregation( )); } let h = input.node_fold_proofs.len(); - // `committee_addresses` must cover the full registered committee of size `N_PARTIES` - // (matches on-chain `topNodes`), so `committee_hash_limbs` hashes the right list and - // `party_ids[i]` is a valid index into `committee_members`. Today `H == N_PARTIES`; once - // honest-set selection makes `H < N_PARTIES`, this check should compare against the - // preset's `N_PARTIES` directly rather than `h`. - if input.committee_addresses.len() != h { - return Err(ZkError::InvalidInput(format!( - "committee_addresses length {} does not match expected N_PARTIES ({})", - input.committee_addresses.len(), - h, - ))); + // Full on-chain `topNodes` (must match compiled `N_PARTIES` in the circuit artifact). + // Do not use `preset.metadata().num_parties` — that is BFV search metadata, not circuit size. + let n_registered = input.committee_addresses.len(); + if n_registered == 0 { + return Err(ZkError::InvalidInput( + "prove_dkg_aggregation: committee_addresses must be non-empty (on-chain topNodes)" + .into(), + )); + } + #[cfg(debug_assertions)] + { + debug_assert_eq!( + h, n_registered, + "DkgAggregator honest-set H must equal registered committee size until expulsion enables H < N" + ); } let slot_indices: Vec = (0u32..h as u32).collect(); let nodes_fold_proof = generate_sequential_nodes_fold( @@ -446,8 +453,10 @@ pub fn prove_decryption_aggregation_jobs( jobs: &[DecryptionAggregationJob], committee_addresses: &[Address], e3_id: &str, - artifacts_dir: &str, + preset: BfvPreset, ) -> Result, ZkError> { + let artifacts_dir = preset.artifacts_dir(); + let artifacts_dir = artifacts_dir.as_str(); // VKs and the compiled circuit are job-independent: load once, reuse per ciphertext. let c6_fold_vk = vk::load_vk_artifacts( &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), @@ -464,13 +473,9 @@ pub fn prove_decryption_aggregation_jobs( artifacts_dir, )?; - // `committee_addresses` must cover the full registered committee of size `N_PARTIES` - // (matches on-chain `topNodes`); the Noir circuit's `committee_members: [Field; N_PARTIES]` - // shape enforces this — supplying the wrong length surfaces as `WitnessGenerationFailed`. - // The non-empty guard catches the common misuse early with a clearer error. if committee_addresses.is_empty() { return Err(ZkError::InvalidInput( - "prove_decryption_aggregation_jobs: committee_addresses must equal on-chain topNodes (N_PARTIES)".into(), + "prove_decryption_aggregation_jobs: committee_addresses must be non-empty (on-chain topNodes)".into(), )); } diff --git a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol index 9172ab4c7..310b1b508 100644 --- a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol @@ -211,6 +211,15 @@ interface ICiphernodeRegistry { /// @notice Committee has not been finalized yet for this E3 error CommitteeNotFinalized(); + /// @notice `publishCommittee` requires a non-zero PK commitment + error PkCommitmentRequired(); + + /// @notice Proof aggregation is enabled but no DKG proof was supplied + error DkgProofRequired(); + + /// @notice Supplied DKG aggregator proof failed verification + error InvalidDkgProof(); + /// @notice Node has already submitted a ticket for this E3 error NodeAlreadySubmitted(); diff --git a/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol b/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol index a2e902f20..9d72a15f5 100644 --- a/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol +++ b/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol @@ -15,14 +15,8 @@ library CommitteeHashLib { uint256 private constant _LO_MASK = (uint256(1) << 128) - 1; /// @notice `keccak256(abi.encodePacked(nodes))` for the ordered on-chain committee. - function hash(address[] storage nodes) internal view returns (bytes32) { - return hashMemory(nodes); - } - - /// @notice Same as {hash} for calldata/memory address lists. - function hashMemory( - address[] memory nodes - ) internal pure returns (bytes32) { + /// @dev Callers pass `storage` arrays via implicit copy to this `memory` parameter. + function hash(address[] memory nodes) internal pure returns (bytes32) { return keccak256(abi.encodePacked(nodes)); } diff --git a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol index aaff9c383..0773230d8 100644 --- a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol +++ b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol @@ -207,17 +207,17 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { CommitteeNotFinalized() ); require(c.publicKey == bytes32(0), CommitteeAlreadyPublished()); - require(pkCommitment != bytes32(0), "pkCommitment required"); + require(pkCommitment != bytes32(0), PkCommitmentRequired()); bytes32 committeeHash = CommitteeHashLib.hash(c.topNodes); c.committeeHash = committeeHash; E3 memory e3 = enclave.getE3(e3Id); if (e3.proofAggregationEnabled) { - require(proof.length > 0, "proof required"); + require(proof.length > 0, DkgProofRequired()); require( e3.pkVerifier.verify(pkCommitment, committeeHash, proof), - "Invalid DKG proof" + InvalidDkgProof() ); } 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/scripts/benchmarkGasFromRaw.ts b/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts index 186ca13b8..6043dae8c 100644 --- a/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts +++ b/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts @@ -12,6 +12,8 @@ import { BFV_DKG_H, BFV_PK_SUB_CIRCUIT_VK_HASH_PATHS, BFV_THRESHOLD_T, + bfvDecCommitteeHashIndices, + bfvDkgCommitteeHashIndices, committeeHashFromLimbs, readVkRecursiveHash, } from "./utils"; @@ -27,10 +29,8 @@ function findRawJson(rawDir: string, fragment: string): any { } const MIN_VK_HASH_PUBLIC_INPUTS = 2; -const DKG_COMMITTEE_HASH_IDX_HI = 5; -const DKG_COMMITTEE_HASH_IDX_LO = 6; -const DEC_COMMITTEE_HASH_IDX_HI = 2; -const DEC_COMMITTEE_HASH_IDX_LO = 3; +const DKG_COMMITTEE_HASH_IDX = bfvDkgCommitteeHashIndices(BFV_DKG_H); +const DEC_COMMITTEE_HASH_IDX = bfvDecCommitteeHashIndices(); function requirePublicInputLen( label: string, @@ -212,12 +212,12 @@ async function main() { requirePublicInputLen( "dkg_aggregator committee_hash", dkgPublicInputs, - DKG_COMMITTEE_HASH_IDX_LO + 1, + DKG_COMMITTEE_HASH_IDX.lo + 1, ); const pkCommitment = dkgPublicInputs[dkgPublicInputs.length - 1]; const dkgCommitteeHash = committeeHashFromLimbs( - dkgPublicInputs[DKG_COMMITTEE_HASH_IDX_HI], - dkgPublicInputs[DKG_COMMITTEE_HASH_IDX_LO], + dkgPublicInputs[DKG_COMMITTEE_HASH_IDX.hi], + dkgPublicInputs[DKG_COMMITTEE_HASH_IDX.lo], ); const dkgOk = await bfvPk.verify.staticCall( pkCommitment, @@ -253,11 +253,11 @@ async function main() { requirePublicInputLen( "decryption_aggregator committee_hash", decPublicInputs, - DEC_COMMITTEE_HASH_IDX_LO + 1, + DEC_COMMITTEE_HASH_IDX.lo + 1, ); const decCommitteeHash = committeeHashFromLimbs( - decPublicInputs[DEC_COMMITTEE_HASH_IDX_HI], - decPublicInputs[DEC_COMMITTEE_HASH_IDX_LO], + decPublicInputs[DEC_COMMITTEE_HASH_IDX.hi], + decPublicInputs[DEC_COMMITTEE_HASH_IDX.lo], ); const decOk = await bfvDec.verify.staticCall( plaintextHash, diff --git a/packages/enclave-contracts/scripts/utils.ts b/packages/enclave-contracts/scripts/utils.ts index bc0df8033..33c05e8c8 100644 --- a/packages/enclave-contracts/scripts/utils.ts +++ b/packages/enclave-contracts/scripts/utils.ts @@ -41,11 +41,24 @@ export function bfvPkExpectedPublicInputsLen(h: number): number { return 3 * h + 6; } +/** `publicInputs` indices for `committee_hash_hi` / `committee_hash_lo` (matches `BfvPkVerifier`). */ +export function bfvDkgCommitteeHashIndices(h: number): { + hi: number; + lo: number; +} { + return { hi: 2 + h, lo: 3 + h }; +} + /** `decryption_aggregator` EVM public-input count for BFV threshold `t`. */ export function bfvDecExpectedPublicInputsLen(threshold: number): number { return 108 + 3 * threshold; } +/** `publicInputs` indices for decryption-aggregator committee hash limbs. */ +export function bfvDecCommitteeHashIndices(): { hi: number; lo: number } { + return { hi: 2, lo: 3 }; +} + /** Recursive VK hashes for `BfvPkVerifier` sub-circuits (from `pnpm compile:circuits`). */ export const BFV_PK_SUB_CIRCUIT_VK_HASH_PATHS = { nodesFold: path.join( diff --git a/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts b/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts index 7263b2bf5..d3f1897fa 100644 --- a/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts +++ b/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts @@ -16,7 +16,9 @@ import { BFV_THRESHOLD_T, assertBfvDecryptionVerifierSubCircuitVkHashes, assertBfvPkVerifierSubCircuitVkHashes, + bfvDecCommitteeHashIndices, bfvDecExpectedPublicInputsLen, + bfvDkgCommitteeHashIndices, bfvPkExpectedPublicInputsLen, committeeHashFromLimbs, readVkRecursiveHash, @@ -110,11 +112,9 @@ function hexToBytes32Array(hex: string): string[] { return out; } -const DKG_COMMITTEE_HASH_HI_IDX = 2 + BFV_DKG_H; -const DKG_COMMITTEE_HASH_LO_IDX = 3 + BFV_DKG_H; +const DKG_COMMITTEE_HASH_IDX = bfvDkgCommitteeHashIndices(BFV_DKG_H); const DKG_EXPECTED_PUBLIC_INPUT_LEN = bfvPkExpectedPublicInputsLen(BFV_DKG_H); -const DEC_COMMITTEE_HASH_HI_IDX = 2; -const DEC_COMMITTEE_HASH_LO_IDX = 3; +const DEC_COMMITTEE_HASH_IDX = bfvDecCommitteeHashIndices(); const DEC_EXPECTED_PUBLIC_INPUT_LEN = bfvDecExpectedPublicInputsLen(BFV_THRESHOLD_T); @@ -305,12 +305,12 @@ describe("BfvVkBindingIntegration", function () { } const dkgCommitteeHash = committeeHashFromLimbs( - dkgPublicInputs[DKG_COMMITTEE_HASH_HI_IDX], - dkgPublicInputs[DKG_COMMITTEE_HASH_LO_IDX], + dkgPublicInputs[DKG_COMMITTEE_HASH_IDX.hi], + dkgPublicInputs[DKG_COMMITTEE_HASH_IDX.lo], ); const decCommitteeHash = committeeHashFromLimbs( - decPublicInputs[DEC_COMMITTEE_HASH_HI_IDX], - decPublicInputs[DEC_COMMITTEE_HASH_LO_IDX], + decPublicInputs[DEC_COMMITTEE_HASH_IDX.hi], + decPublicInputs[DEC_COMMITTEE_HASH_IDX.lo], ); const { bfvPk, bfvDec } = await deployHonkAndBfv(); @@ -398,8 +398,8 @@ describe("BfvVkBindingIntegration", function () { ); const pkCommitment = dkgPublicInputs[dkgPublicInputs.length - 1]; const dkgCommitteeHash = committeeHashFromLimbs( - dkgPublicInputs[DKG_COMMITTEE_HASH_HI_IDX], - dkgPublicInputs[DKG_COMMITTEE_HASH_LO_IDX], + dkgPublicInputs[DKG_COMMITTEE_HASH_IDX.hi], + dkgPublicInputs[DKG_COMMITTEE_HASH_IDX.lo], ); expect( 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 5795847c2..fe5f7e9a1 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": "0x00000000000000000000000000000000000000000000000c231c04d197a8e708000000000000000000000000000000000000000000000004dff372f7e0128d790000000000000000000000000000000000000000000000075b49d509002a176d00000000000000000000000000000000000000000000000000013555c49a6d66000000000000000000000000000000000000000000000000e6842811517da5170000000000000000000000000000000000000000000000003669759c5c8027dd000000000000000000000000000000000000000000000005a31742b4a58157240000000000000000000000000000000000000000000000000000e79318c631dd000000000000000000000000000000000000000000000003f855d900f178f79700000000000000000000000000000000000000000000000660c1fafd86f70c7900000000000000000000000000000000000000000000000992e486dc1bb3e6960000000000000000000000000000000000000000000000000000e7f1f964c980000000000000000000000000000000000000000000000004ffb738420181a4ac00000000000000000000000000000000000000000000000f05f675e4f309b2960000000000000000000000000000000000000000000000079ba8c5829d9868b700000000000000000000000000000000000000000000000000011dee0a4299770832f879c803fd7b787b6c5ec4e1e0714f5aae691b0542def35cff3251fdc5b30f8d12e5ee82c4f7d3164b41bb4cdaa80237d61a4cb9e779f1462a241f80f1ac1cd23d6f8490793bee73dc357175a2b9ebeac1e5975a21e290d819a4cf1d9ff01eb1cbb093ecc02906356b0797a42cb304014e505f70f1190991810dc812e176297a3c0bad21939a9b1cb32dd6b82db4643d1eb8aec6fbc56dd93fde0996960c1cbdda9ab25913b46b163d4a332e799d201d1cdba04297f438ea4a4efd17ba1a289f4af62ac367ef7ad3197f26868c463bc7f7be75ebcd1f4cb0d8af9ff494a9183660f437d662a4eb7505bdf9d193fcf0c08a95e9fa826b850ae8419aab4e8a25f55283723fcc95ac9eacfe4409b460a0d1d57a6d77f7b524c405002c208fe520bbe73a3e2f784dc967fc117d7f6d480ac9960bad15f20b7ef9103bac9795b123787fc7493bb5e8dd91740730cc2c2acff5fc0b376938de79e664adfa8b9c1f2964d0f06e4c92e958f5fd77a5a49f0d4158de25b55f4a022794d66fcf0d66b61c71274729412aa51cfd6caa29c854f02b0f6381c8969e2927833619fbaea251280a6dfb0c2098a8eeb87c9606c13dd2fdbadee2822d0d474c2c5b2d29a7db431a3377b08f5f1ca652302d0ec96d751aa03a143ec73e27fc7287749e48007f121d76c44c0f508cd42146d4c85da1ba7f0079d44f7ec8d86385facddd56ec7d3b2c2310d8d4896a745ebf3284b67d7dfe6a70bf912feda40d6a7d735e2c6917ed0c4d75fcecb16b258dbfa38b206c4390536979a9fcee7ecbad671b527245fa30194e070f0536b206f4728be22149c5043f9162a2676b1cbbf5970efdc3f09adf15c02013d398517db992673bca32c394646855203b7e50f25af4e859fae1186d10b2529864fb2290e037275f80785700c29a8df6aedbd6742ba5ea46d6b7452909274fcca2b7ab41d0684c1cadebaf11593d6765db6e88be19eaca96735db527250a9d92a04f51e65149dd9b772f2e4c77ef2504a92b0ae5ef7216de2dcff50419e13a3bffdd680ec1c60b461d72df65afac17828feeda81b9a2441f16480b821b5d909d1c8242c008e6b4ad1af1d798bb41a68c767c9b3a45a60871364310b517d0323cd8922d5db1fd5bc98f56ae03e83eadbd0a64b1a960057fecf780d189106993800da22c6d624fcbb23dc2031f3bcf55b2350b901fa3b6c9598cea064913c140a7440455db00bed4312b796f57a058c03446d58c84672705a1af9a15980262ddcce5e03fa8737cbb8d9b59edfb4bb0376593116b6acf935b6ed928d1b7039574bb7faa48a2e45b9abd1b95ce656a2c3fcad52e50644ab4f430b092950713a9be10309a139e4114960f59fd48470ac69c1d3eb77b9ac02e1be09eb1eb571345da560337d679e98eb65ec13e231b25ace4b4f4327c012879a4eff8752252073bc94743f68c6488c2659ed13bc4777e9b31d819d3528d12989616b713b2a21f4561994e2b294dd8451f4d34e7f17f37b31c7659e8ccda79528830ffb2d8600f7028bdc8cc1f247db1c996a7c683e4ba8a621111c4bf18c193aa299e54a818290a2fa13fc299ed1c9655ffaa435d5581e27e321594d33c84721e01e4c976fa2f83abf8d7ad3101c5575de5ed03233a502c87ceeef5c8ac9c4ae7ce55e5a12702c4a8dc4290c18ed54297509ae69fc6e10116bab06916be62c1b66d4c31452e28780558518a9a0677de43d2381aa22d4cb1e05b371a0abb3474990c81e0045a1010f281874e4dc7bf8f4c266bb74d4de791f9444d6a979df2930d4ce8989d5f134d2b517b71ff043acc1e77ec06863b770df40820833ee29309e6d5940c98e20a46b2e7ab26ed6e58880c0c757d00d9e298b2ff637f68e3e0ae78d362684ac71f4ef526adc5a9362aa12348d54dad58410e69e9998c043ef9cbb9ca457f367e2b540b26156b565d77217132ab2cda06e373b5c8e42fb1c549218db94a975eaa04925195341bf71bb44848f66b754f8844df69caafefacf5a80fb7c48d3541b52a54d7f1ff24a7da4cc29a6ca8c63a6bf1f142faa1da9ffb6cbf576a74f1665a2f6cb8998e325cffedcfb1ff8ae10ddbf30553384be2327852ef6cbddbb80c031612944a6367ecc5ddec0bbdb492409ea8ee0b749023820855af207f2771efe415357d2e65146970c4e134ae290cfa6c78765c9f93f822f7442f38be924feed3075d0f4c505e8f412cd52430157769957d983cf833076ac64ff6cb3089a5c23e2a4b7311a60385b29b440ff083a03357b30a7ce34c506b724f7491d6397a5b220bfbb4078dde8510b9516907fc0e26a5249af99436db17ddf3e08b63330a4e1f2fdc4166cb2192df0fd7da156ff33c342789ebbaa8412c362670a8e1d1e3660d04c8ee09dba54e2480700559aaf372ac10b3fa798957ca140d1293c4ab7fde740b5485945480745e0cf536eb74a1b75abf1f942da1cc613621de9fe86c60ab972f724566c306515796c892b36d522b92c2c481fc29f1e711953987999e3898ae038f4fe31d52d3d91f94e01fbbb0d0101568ef1b2d02bc0ef78877e5a9e51cee134025275cd9aa251def979540cc5bc9963016fd04a56f42ea0c384d20bcb51b2815de4721915d31b83934288813e0914eb9388cd661dab0fedae3594e84ea0519ce06222d0c01325eadb2239c4281880b62af0913eb476a3e57fee4ef2564fa14495d5aae740b0a554e1b0155f6451c2bc68e136ef1b76a6d538cfdbbd17c5c2d8ea0571306bfc84903b7de1030e1596d801b8faa51635ffcf1a4c40b9b070f2fa3fa1053239ff9320d966969c616127cd2fc20a65986161a03cd6c3941aef31341535b0568b2a713c86a642f4da67f3141252ab971636722ed12adb2a793c72adddf418e8586058ee51dc6f148ac40c0c26dfb144bbcf5d307c72968ac60f7257b9964070a64942d647ee644678caca09ce457f2e613fbc10526152798a5f624546f7fc22bd2801f4aa585d2483b8472a4b3f5af24b3dd476e671d3cc2a52f05bdd9fb07430c3cc97d6bfb0c577b2f9e8e50d6e1fe74089ebe128aa657e68926183d6cd87dd0ebd530920013a0a301d505c47262a9edebe462a0ac28c6cd2610b59a1787db42188287610d1cedb9df8ccb0c33e846116c2d571ab84660a728198a7806104f6e505519e52bff03fb9eb0a00256556355c4cd7dec17af7147dc071beee6a5686d1ba5a185809baf446d80e20cac33cb693da5da0b962d2bd19e017661aa136cf73ed71baf62505bd5afa4d39bdd0684b582ee6e6f4b7c6137a610b8a5f142ac1cbce5c02b9d078a10b567ecdeea8eac300fbcbff1d8f92d75440396973f60f9c313f2fc46ac28340d60ce96317c2b1fa08c2604b0d939d0d18b1152f44f71949e022b665a3915226bd97337b1873de651fe90f3e70a3cd806f21f82d88ec05123bfe13d32b3fad2e2f874ce2a4ca42ce4276d95b216d0d11aff23e9053c45f28d1ca75a522049373683eb011b3fe4eb644b68274fd6361bea130bf786cc75187bfbff53c6c8809b4ee3962dd87ec04aadd8440848869d77361924a910d6f25dce1bb4afcd04c6d823db6e94f29baba17e10f598331f4db5492a2622397ea1c9c156db10797fd4570223efd35c0fd70bda70794e5a28ff326d03194c82df5490a622bd475ec3b6423cb73dba4eea28b6cb978b7efe60b0da77f31c2adae631cc4fe8ecd22f605c974bb849132a4b3339ce8e68c7a6578a3acde71a7a2c7fe23193fafacc18024a9313cf0b126c6d7c58f503803adbb09809b6090818993e81b3dbc750cbe9ccd56a01cc9266bff7289e2a08a14124af6bff64d9173b277e654acd6db771e75b5cc586e8d8153aa7ab685032632cce7af6e17e7d1f8f30910d5e7a54c8b1972efed7d2f5e1f6aa76bdcba76057cb941950523f532d02800f31fe4bd26167857004a22b83585847bb2b97b487d08a0a164c301c610ed2a41c62ade87f7ea3e1376bfe73a07e8a98e7a68a0e59fadc1beaf37084b302ffc3df53fdfa8cfe80b5a0427f8024d1450f11fa7f2747efb3d30c0b140fd31bc3a87fa0baa6219996c7ba23057aed724685d8806b849f935bb32eef4a605605a81dcf2249ee73ea621b56f520e32aa0b6a3d93d13f29aa0b933361b7b4bc415b2342befe6057b29f81986d412861cdf37c13ce9e02f395067e17eb426edba05ee1ad79588ad90e65a4da63c3a0589d6d7c393c35e1f9c6026cd599c3df53c2f63ca034855057d89de7932ed12821db83c79ff9010773cae04fec7b850ae3f04951f33d5cbe2f4a05d9b5e3369785b1180c4b645c13a811156e17872ab15420d6095cbc95077e025a6ae3cee3fbcf97a3b552e6f10b2903ed11f88f60b4f3d1923b42010f679c50ea946e9ea8a8332e59ddcfa2f9d68b260fbc8b81452adf41022900746861cf9b59b1719e2eef8e882e75922a6be8df309d8694d5ee46d6a10e370ba867c3128ca6dc7883414ad7f3a6b3b01479f965eaa0247cf84938dab303d0942d69328e413e0c5818e8b0d4f7ce1bcc11f65e539de2ed2e3fbb46e4e25a36903fd8576aba8efc706cf8722171a2fecffd79a415a3d3645924c755f8817e4cf0a30e0203e5c32c990022b3ff687cef3c789d018875fe44ad11240af3a1ad14160b802d382e8f576b579de16820563c4e212cf36752246772bd379a5110ba9f0b8e02469515c2c9676c859356e0941689efcdb6a2241d0ee46b580810410afd610f1ee5c4d2965d64153350490ca842b210984ed286463eb79bcddeff41cde9fa4367f246608dfec52ffa6ee6bf08df305c33e96e22593896ffc1e36230ca2aca0629659dc3571f0be7e809e9a510ec9a7f0c1723d69c985600f3833bc02fe173deaa04c41f9b5b1f83ea37d42474dbc81b94c81b05a64581aa57213cc13165676762c6ad6b51ffb3a73f1f10060358b19de35c6d4babbf829b224e95303d69362694e0ac29cc0fba636fb26473b14fc238ac027dd77f8b677693a5a571af4105d307cf0d0696d37f321995ab288e77ce09f4ddc73a252c300d4a00c981b554dacf0415b8da77fa1c4dc28ed4dde3a3573c6c3e7df3f2390d98087ba1d2a97806f0e9fc3c1a16c64c35704079f146cffaea0ef7314b90d21667bf209570875505253493deac384d5044c00e94d50a15a130f8362084459e05f3bf69c982a364cd3bec37ee44649738d102dce02eae15c9133963dc6f00af8027b41704b1e65d7c2c3c445743491863faacee7a449739fa7c5faef8f6fa8091c326f1fd12a2320f689d0be3d099247858f481ff84a1c3a760f54fad6eaaa7461972bb5b1241c2e95617f5eb5ec64c536fd9a884a1d21e74b17d1b6fedfd6524f370ba6b023f770d9877a3968948bb8622d649578f5c39217b8ddbd83c42a9f826848f6ec2c4a78791b282fc5551bad0e1b3f95f63b2ea587b46c4e4b4602fdc1f24824910a59a9a3b3f2ad2b6058ec542fc2b40bf67fe3ea4c4ceec0f171a1736b0c63d901bc7465c249d7b191878584f5254636ad94c076ecdc7402eaa26be71dde4051302af10ab5291187ef10822091e574ead9269789d6132639c25627efd71702202d180e2caa6d96a03e8f02ee3139ba30e989586cab693a0a79e12fd162c12d432407c683bf99ae6a2cda59cf6c90ad591ceb18a99e3c1148215b36b2fb3e39f00486f2ed2caf1477061cb1684be5c51bcfb19901b9c02ec447beb24078171aaa2f64680388b8bb597288b662a0382edf3c14ac070737e86726b6d12c7c500ce72d3480a05120092fdbd1229001fb4fb81231535b9653878afdc3addff421963903786aec552940e39d9ff33460f74a33387f9c70dd4066697c5f2bbfd4c2efb11b86d2cb5f3fc4af8ceea1a93a1103922e43dd160bbdb8f161bafcc7fd31f8191505db967d01b7bf5e5c929ef3469c08f817fd7c7ec78261a5cf10d0c06f43810c2ef26ef7f2e9336ff587f6f8900096de08d6bf739f6313594db9443ec342c30fac34260f3c79034b64928e3081c1ee74b0fff1cd01f9d148601c02d6e482551800e080c9c229d59589ed97ca4af09a2608a599ccb54c19aabf16486ff4db460e7e1472fc208c2ca3ed2a948a06ce66ef010f7172df7bd2831fd060cc233dce03955bb32d144cebd97bca622b5ee2f2e6e8399a2adb6f538bd41181201b029c2f796924efa294745ad0c575cf542fb7a763c43db2cc32ba8db6f775b99b295020596ab671b0038e64f5606a8fa8eafbd10d29ab148e907904f73da636d63ad40a4c6396efab8297934902c7cb657d39c8dcf44eb4cb3a86c795841b0f93be581de8532c7577ec52f4a12537a9956b0ac212c03cf336c0bcf99dbd3b892e45ed2d2a71dc9d826fb9cdd91d43408791e5ec56fa2fba832774e589084086303a0c16ff05c6f5780fa20a52820469bf3b8978a7ed7937b0217479d86ceb9cbc90ac1117c686fe10d0a97288182e9ca7ac5e47384be383b7cc53854d7a5505c6dc000ba57d3623d8d3f995c14f756832ff184559ec8d627221a65026e964e2eb36aa09c11f60eee4a48a5a59465eb6449bde6db4fa670458d925591318b9082c5f0f0257ae6d8b494bc66a2443dead3e49bb7bed337edf061f17c49b264641fb59dd26984407e9accc23b4d11adcb0c10651e75e05ed573edc971df97827ef712b551513e963428dcb136b795ffc909f9c259347fc67ca0cf5d367fdb41d830d91ab02366b303321eb6971795d674b16d05de722e238f9f8dcbbbdf82cdf46e2121222aaa6f521143bda9fabc2237e2fef019f237d6fa666acad9a65f44943d549b515e85e088d8e3ec42b92d49a3994bdbc03ff1b0073d63c0ec74b9bd5008c4fa424585a1fc5b5b5ec1f6ff4d7ba9e1d0708efece885a50bfac097d50b0a8a450527f3d7df5039a8c1b83d0a7c217819cd3fd158d79a679145885730d2da273e422401b4c932c33726c20c3a350f4141a5665d3508cca37ead4b34b4ba27cbc6b9132dcaaf7c522f40b88edb47f434de442846a765c2fa47788196fc9df7c83f5b29ee831a1f7284e6f4e3c133df7d9e54e3ec5e0a7cb3127a1374c12ff4d616de163ab2749853ee6c337caaaf97084d29d86d7fa4743b52d7e37a4689f4a754c42ab7dd83f960a3e90114e53473f486132ececf39a29995d2a8174ae2b547852f2f30d4bacce67216597d1124fbd87ecca5dde59c0f22cfc7c6322f740a909aeb0a973c5b14f33f2054698e7e03e338769b7919224c638db7924a8245e308a43b1f85d7e406c8446e58fc361467f1c6cc21d378faab8ee790ace260454e153e6210807cd049974e1ef0c1cb56c32ba83314f282a98d1153cf2af5665eaeeb6a16139a127f61b33fd3faabb69d6cfa40f9329a58331ab431d59240d8bc2e966fda22d7e1eb4bceb911e8df376d4c050cf65dc124dc5faa21af8758a079d7c0b85b02b6c0ba4355df267525d12e8205f3c8eecd10356464704c7cace51d0a81e9640bc6d63255d3e7e5ec844e84a5230a07bb33c79585fd8267b76abe7150a786a21fd35591ff3238fca651f2d59cc90eeed3ba2253efc0f567ba0f8aba737194af1d11a2a63c20be531387940968da873cd43bd7dc03e3c1d3bc12aa2fbe99ada21e20e2b14daa35699764599763ce95a5506581bc494b271f865932bebec143ca0e6358610510cf53ad2fe1d5f61ad1d24df406b44656c61ed6e7205445faf3a918d391f98dd546d7803485b78a225a4ef29ff7599a16d288af013a297784c0b02a6fbd947ad57219cc3df3cdbf0f7d59e36ce25c95edeaf6245753f62932bd9502cac9c21e636fbcac7c2987640ecc38ada9a81278c7de458bfb76216033d8d313c337b1a163a73ddc3140db57161a4a3fd173ed6b24ffd66be49ff72c6c40dd0fb35f02e279f9f385ab009f147da9aef912956e37c57262166bef70d27b677f103e54368d3cbf57152e241f21a7a3ce73647df2544af9ccd49dd58ce80b31e8192a528fd130ff6b38f3bc4f495bda106ed59c6f3f0fc39c0395b4497dbd5eac0c6b1abff83546d63b037b05b68ef47b5f6b5d8b0436d38f372d731d6fd0ec472f39308f03dd5783ae424871ffc058ac46f5c126810a51504fea799030db97c6077deacd4c8da49de813a7dd9ebd434f32490cff962a047a55647a095a8279760d4eef1aa7a2eea2c7b8d80f82019b08b4e788b538de74f2ba002d414dd6abfe206033b26f0a38b1b88a80fdcbc1e8ab9f37d0de3ee6375c07915af54a986b712dd8a4a75a710c535434b496e4bdcecb0ea8f8ff48287705b3f579b2aff304930528e7d18eb79f7043c9a1aaa1e46dbd5fb9dc284e2b867bfa2c371bcfa0cff8246c4fe2f51460bde5647596fa9e4a8c6e38d838c8e8467b5a684cd45963b5cb04c2fc702e846d4eaeb30abf70328cc7145e757144d261a89ae51b5fc2ed58f01d1ac594165c1ddbed15007d392ab654909ba10443116328aba7c8af8b09265b27d7c53374a87bf6f98d28f04592ec3f7d613a50ef183784e7ad894d47ae991c05ac8a35b6d4307f7931256358077480d2eb99142c278440fd800d9fb8578bbc1af8eab305ee037541e604da2ed341479186d828627024d4f1363067737ee3bd0fc6ae2f1b556ff69d05c598a92d68f6d7953788aa43e8aeb4971ed7bab3fa7b079e231f452192c2b92e2cb0e2305eea4e2f2641a55f50be480beaaf829daa8a2c89801f65112458cd3ae535785e6b84f4430210b454a30372b14fda4e3393772d720d07044a0a99f8f8e247d152f8ec470c6eadf946e52499a5083db06d3bf62b632db82ab142bcc95accecaafa757e9b56640b1f106678cf8109554c8d80be21895139752ea939af20954934cacf705c99630dffe4254f701420309bfae9111a6f243f7fd657036f373dc43c281335fb1a17b843e61da43093df3be32aa0121b61059d9259d23088afb4baf936b92eee94156781c4e381c76c62791f879c0d266b33cff5acabb6b7dbc81d67a22cc97a976aa798d5eb86beaa21da26ef726617683a10d48af74af941aee5b015c31e7c4fb0af21f474a190944e145bea17c727ec1f4de4e389fed639b14cf173b7419caae9c3c33deb11806963024e14107520e3a6f816911548b98f74c0fc5eb6f384807561e18dd0e417c96a062e12453e0515c4ad0a7155e54724b197e6e200e3b61df77b12f853c8f0192e0b38e8bf8b1fc9f87ecfb8749f4651d29c764148128cd0172f37c559032cb1eaf080664e3f167a19ffde30c1c79b7e3e44f715b4b20d5e143ca7715f0300d4e7ee2435c23705f6c29d34d4d9bee5da3e6ac50d25ed57adfb31c71777520c147b0d110f8ba0183e296f88dc0d7dd30cc8c1a552e20d3484781fa7bffe789cfbfbc38252f357273d99464757ad98c4bd83924a89fcb690c3548c35bf6cc93722206b8aa7b998059dbfaf9b597f883df0a4e79ea7a87586c53949d42296315f262e19eb4443541e591182121a4d53c9b009bac47744cd2d655db9645f3b31b46fd1cf213cbe7416a3520270f458d7ec5604157876a5b81be998f854666815e1ed6792252bb2da0ab85a4b49874fbdf69d9ae78e895a82d02855d0025bf8ea19d5a7f67b63a52e0c627bad904e362e90ef868308717e4d00895153f9ba4f20568c3344e505d88b0efaf03ab4c593750c40c438314e010f8aea4aa22933f64f38aae2ac56c5b00c222ea879ad9a723dab3e3324fe8ce6e68b54f9b838a2e265d2a81d091f43a97b1218727895e7980462b651160b79beac0207bfa5c127701088140fdc4741ae4f270a6d0861d29d825910ec8efef266852ad117e8bfb72c4827be08ecdab3ab21214b427bc61b5bd42ff1f6e84198732bab6b043d8eaca51e94815db13c22a5f2178c97d3e67bb633138012c8f19e6edca36f28207be6fd25feb47eacd3f7300a12ffd16717ae1f87b30572f78fe8e9f6c0285864a19d52ef1416c6bafd296ee5274e16e37cb37acb075b842be01f7f577d430667f65154b11070b70437937270300c53983a317d20ef5892fa22fa5ff721fb9be17da55e2f82a64f5a3febf7192ca47d3122f9be516f7a7b8c2c02d0bfd706b6ca47de944ae10e732351daea2a031e416a38ddb62e1af22d03e222ca180a8b69288c999f3b851f5ed999b2a0a61fa27837d952cd8421cbc240d987a37155a8c1e5e1f4eb7c49aff109ac16af6d0784f3728889808e1391e69b25e095af7a018c8a81f1cefb60aee7375879697e14388cbf84fd159c7a4e1882c56bfb3bb5b5b9ac8db66038a0257e777fe0c435090aa91cb05b93a1fe40b21f5a9b40143eb498fb7e030e98a4b9d491ef20ae3a148193295c2c9dfd0c148b0d6e84fd8eb8edcd5ce1337a1998e78ab217561406166e870a1ff737d0401591c5a5b2f4da40bc19a0f246793f62b04bc5df66d6020970535a0d0e97b797d44e58f47891b11c30b5ab101d43e6413f3e5c048781910a1f8ef5c1f90a0c2f60909f5b5109b80e03613cd84103eaec0ad5cad363665e2234e51f0eacdc42f1dc6c74fb844fbf0d7e2ad23e9574946fdb9d7a3afd10910881c68d7025845e58e84554285ac330b2317fe4ea446d6917f98d15e362e1481d3d6a5c2067ff2ba2cd17fd68cd5ad79e440ec065e883123cb3950f2ac4b6480db142b1f16d08c353e2f557be1dc2ff5359e80e8efaf31772170cbdc1deb54d1ea5312f9e915c83584235ed85109d4a59bd9e028c5f44eaae735386f684a6f607ed78682cab8beb171780c9a54e3f57a392378b6051bd47cc4104dc4678c74f0300f5276b1f61679e9c481b80823bfe4fbae5082026c4996e61a319bc1274b00ea1dcf79295bd3c34f1b1fdb77357dea3955457b33db3393905ea35deb49616071ec289dae2a241d2d7715964b2db700909956a819439b1e04a7d1f922043f204570c529d1220e1c5c3a83350978b08bb3d65d6844cc1e615d634f5ae768f6e1ae5775ac84325b9a3c2976dea264f8bf3d7a1e14c31b9cf8d6c4855fc51c750042533b8db0f0ccb4a24085cc029702b9432137bcc39c18a492ce1e62f3c2393070a3d2b973aae4f5dafb491b7ae5e36230fe2c376a48a66f5d516352c8ae01c0cc43121e8b8010835398c313fce1f959c0911058646e807b1640cadac36369f203610cd91f6f96552cc2d9b85d3ed3dfee006928fc67518977a68ba6cb35135164cfec4ff35ae655e66f1586f933e88c2874350c6091f90a682e32c02690ad219264c0c4e73ad397e314da27dd9fc97d941bc619e13826ccb19f58d0651b693270214a4270e230bed23fd16705e90ffb3c8b9b7d9cfe03ee2a094a9e9b2909f2678bcdf76a943c56ca03fa8189eb05a15b10840e4f83e7ceaffbbf07fa822851245572d390a62945618a0a04cf1b830df30e13a4bafc8414b13ffacf3488a9725353f07f6bddca341c57e2a1d437a0fbba07c4e5bb31fa6c41d9d71fe038e6f25ad9aaca4bf67db38dc232dca80ac6cc577f2fe237614c0987331d8569772871276ebbcd824e2d6fdd1adbcedd9e14db8c44abec004b0ac587962949f41b1800c781b7221b31e4e3a02d40e9b2c1b7ea3b73f180ba5b1b27ef069b6f05961800d662205d6e426c30f893bd0cff9b606a3704ddc4fda64f149a583b5e2b1ec5012b58b7e12f4ea84f4f9ab698e625d09d85e8ab36100d477be566bd3aea6264500cb6669de3a9bbcfb18d17dd15a9d6827277fa1a6c57b83cc7aeb2965222df40929e9d749b07a43e36393a92da64323ff33bd3a3cabe072a929c29847450a431c403bf371e626651430d76e2f0f1142ce5ca04d5211a40a3eb5b522492293041a356d17586f9c2a30628c4bdaf8f93868e51faa16f6da0e030dca9a4952eb1c14219210c228064f8846d40e9368ffc5b17c70dceb3c4f13b9e42dca06c872ce0f6cfe8b8d14533b480f908e52f254f91ed84793aaf219683c7d271ab2778dfd30099fcfc1e4ae79674ebce5a1315298c221404b418707036cc69bab53c5313800008570b8cf57caf6020c9829afa07666b66fa1ebda11ced87b18a109dcb1b41aba076719214db7fc4b5ff92778ca481e89f912fe88dcd39ff7794fef20cd9520d52821d92a633a17d70485634a0541c0272f020df6f58af977b0e91d6a4bcf03ae248c091081ccc4e8fffc6a050e05357240b08c0ff78548a041239002452a0ca3dfe850ce5c5ba424344467ed464b57e098456fe57a2ed16b1317685f26ff0f5ed7b176e08e4d9de99f5b344a7d3dd3b0bcb4effca2df187a117c576443e01dd6eb7912779095f7972c7414cf7b72ca7904ef8364f53db792e6cea4abd3422d840981612e99fdd97f2f4389914f0f78cffa1d95e815ebabfac5dce2ecf8412a99be58b4cf34ac6f91f5b4a1dffd405bb782f6189d26f468585fd50761a8a52df5ed597fde9811dfb1d21346499c99146b00cdbb6354a8118f558295fb716b2a9723c61eb8b1427d6bca3db42ac998223f93b5760ce1e263fd58603f18e55a0d532e7cdf42645bdb8cad58eb88dd52f2349c626f08d2426a383deacbde9e251aa2b56f08f371c6d6917381ea4241973ccf1afafadf9e78745d163f0de52db4141c05e1a20ffa115820d0c5d3a1ee1825490c446493fd2ebc52cc34783c5b0304d04162c1609e7340f3c6559cdaef78739a3ecc0e1fdebfc1bd4aa2cdd31d3808275192c8e3cc7921c1fa351a66d0d8f43bb8bce2999756a00a2276d0af4f141934be7f31bde374351e50ccacc048284f6f6ef77f9ffc3c3a7d9f65b4d4b7db1e6e30581acdb9fbf6832fe4e12149b397f857fe633b733e77628b0bda39db6e16572f317cb53137f8dc0c054841faa1c0f6752757873ad0b2c78d29d619ff9d0832d2e39f7cc552cd4203cbb6ba9455ed305398629287b88486b0b914d0be2d1440fddccbdfadec77be5340a0f75035752c4ad485a9ce28f18d0896d358c1560f1faebaba5440acaa383fe25a1a1849a29a56a42d8fba183191983b101a5ac221dca03aedb73584a7be6415ff937bc6bcff8fe8595760af12db068187f52e571d88580aacd45b8723a5c0fb5609f14c5eba1afd3f40f5a164ee4b97a98a72c82150e3ac2ee4314561df798eb3c3a573dcc6aa766f26a84eb7b6e5584c3243911c09193d51120216d2e246fa2edff374a80c1932c39acf41c3e3e040b38373df1e28db8d4179c2cd8efd93091b611d7f70648e832a9301d931c14765a87bb97009b8844ca5d4bfb2ad0934927e11da2fd9832690b28b45793cdb1b80d9b6fe68273abded42460ce3d60f075af10d465818244599cede90585ac63b43c08d7080219e49ddf2bd21ef6119cb9d8aa31cc1f8141373ea1f9d490f15be7ab965c5ad290845de60a58fc9ca397be4eb86efaa128990d4f4441c3445a59e608a666bd10c0c0ce65aeca5e959fd32eafc6212421fcecbd3f10d6ea744d0f65cb21f162b23d804a8d20d2873309f646f8128fb9c9fcd02cca58845c5ec1ae55ed669bc891792260fd1d7b5ea3c3a790494b73f071fa0cd35624889e52c965e136c215e142cd89743e54a6b3988ef9852ca79e456ca5cc1afd24eade12ecb3b27ba89cf1725bdd58395c8594e6a8a01e4b388f76d54d35096af2a521220e268b0dd633f28205bfa81ec110c18221ca77e9bd5bf3f6d090e7b3c925c89ad60d4bd9abb694402a7ebea4ee8be2ab21b00146f7033fb36ee7c6dbcfa20b80d70e2291b46bc8d2761d2dc0d29d23d0cc7a58873bca8114808ac811fc3f45b25cf41787065af9101d6d2ce542984b742c097b92e85ff17d61ae6fd69c597b7dde659a2ea1c201f18aeea00abc08ad5a9d9acafb5316d1054655ec5a02bb2434946b584af7271ca1629090a0cbdf2fe152faf22486825550d29bd8b08899aa562b391cd47deedb726ba0d95a1020b5be53a375ca0c98024b3a961af37170f6b40cb58186c8ad79724ef8fc6d60b44f0de34aa1146f6933f964afc553538f3b2a2b7dc17a83c58701e902854b59f2a7ebe132fb19ae003a24a693386b7fa4d0f7f589542cc6c1c702f9f592898c4de2f94cd9a689da1f0855dd1d29def7e48a900fb989eb7df340e20a9ad75ccc9711d53d4142f02b635db1ad3458617e50c0f91ff6e64858b669909e40555073b0af9e9766e94492a6c509d936e6e678b19ce25fd5aad8a9f9dcb301dca4dd5e47a124900d4ba230002188ffd37f98c29cb0e2f7bd055376b4df01380a76e5ca5017b8b97d887490f494780633d00defdac3346cbce89045a83660e56d2e8a6d8b47211642faca1883a83c23045124d490d982c1218dea8f05e340f95ca48916da15d07fa5f923c3da0c890e36b26c55ffa2f8693485e5b4c67fc29b3be5768259f9af7c17eca51c8ca09d65fa1fb21db1f7c5a8c545b1cf8f78e1c14a5648cd566753b73c05c7b55c1af4005899b442e53f08669ac88dfc0eefc2e5b96c76ae4f5459241fd870544832c2a4be2954852b47df941e647bf7f914e1ddd6f241b5123b5f87aa912ff5c35d8aa7d645b100f1d409338cd9280a0c65515e673654f8fb5de1da42fb7ea5fc5cf1f5afdc4591f7f22f759fd8a9ef709230bddb28064cb8fe3b9a8806b987b14d8e58aaa13c86396832da0d2c440ff95450d3be24a3d6c7c976bc5450ef350a98f129882f8b372c714efacf7a9382285d6046fd45168becff5d64fbf5bdc12073731dabf923bdcab04f43de20c8e95fb66", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000fc4a275557be3acc7e37f1dd383752c0000000000000000000000000000000009800ccaf777e6cc629fb0e8d8ad1652511521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + "proof_hex": "0x000000000000000000000000000000000000000000000001104c69c76ea8f5f6000000000000000000000000000000000000000000000005c85075d6baa615c900000000000000000000000000000000000000000000000eb6de0b1c015f0f2a0000000000000000000000000000000000000000000000000002a0b5c2857c800000000000000000000000000000000000000000000000036176084b0205127a000000000000000000000000000000000000000000000004549377f025fd0f40000000000000000000000000000000000000000000000003ff07c8a94fbdbbd8000000000000000000000000000000000000000000000000000185ecac0421330000000000000000000000000000000000000000000000044f1f17a53eba29b1000000000000000000000000000000000000000000000004096147c1d40da636000000000000000000000000000000000000000000000007c2eaade9aae157c100000000000000000000000000000000000000000000000000023ccf2dca92b000000000000000000000000000000000000000000000000e02d12f90edb8cac6000000000000000000000000000000000000000000000007c223713eba54496600000000000000000000000000000000000000000000000ec928a9ef7dcf2da9000000000000000000000000000000000000000000000000000282e9bb83bee61a05d33c39ba3812376a494cad4c46e07e18763a4c0cc37c7ac2f3635530c6110f00693d937276e524dafd5fffac373d40ca9ac38bc4003aa97fa25c28eaae471e6801e127832204ea5a325f05bb8550bd90338e17663499f9908e07c1a27a4d25db068d58486c5ed1d571b154613b2a444f9ef333d6490c341531f43898470028e20e0d46d87d6e6a8c672bd88dbdbae7c0e0a9d06da0323fdaf7ee61ceb7611d45afd3c8ec55431dab1b693939c2a7f313b832a28959c3ae3d30629655cc051ff1a5efe96161c60a46f240cb6ca1a531353aec9b48b52ad525bf6481f17e0923676a74fc00629f08348ca4b4467e32b7907ccf75e78538096134b580c00ad016d8f24d8b2251a384465ecf7da74301f64008beb9c8b71af609a0085afa8bb10891080d365806d9ed9f21901108254290c057a4b986687bebd3eae86d9d84d52ba59894f06a73d164ff99fcb0d13873279ce4f307eef63d2c0ba38c4624e95e009c113dba40ba429e2620e10806283dd5fad8df1b6a85051c1f7167194c70620f370484b9bd06cf6e6199ccd69d2c0e0db004474b4ffc62084f7697d21fc04f1518380c30df7edd6752046611f209863dd69ccbd4af39b35e05dbaa35d7aea81dac6609d0b9b649962dd23bd220709758af7743abf5395724f809100dfd19cb13ee772bc80efdcb985314bbe58feda8ac8cb79523fe9ff183ebf3e6835eff81084ebea45358443421fb0f2d31a11171c0537eaf216aa68a5bfefa0d02b1c5b92a10ac9238f294ee18007a6f18f72214951c398ce84c3616bcf3c79e0f2e2f022a0c90527061c47a42c43cace4fc879a5a326863a6f82d9c6bb420290aa900d8244a98b1668bcfcb6023d101fde9f2fa579bbd8947480f7058466d6000b38c5c1a4fd3aa5794aad136c957e92be80cca60120b6d839e54f1036f9e857e14746f2433e79c2a2df9f7ea53e60405fe812eb086dfac1f0b0c638610dbdd208a28f2260c558292be011ce1ad7ea6fc1a18428a3199764be0e5e4169e261b655ffddd06ca1104a26cac71683ee832dcd3cac2ca05e81de30bed5f8b67ce620c6f88f62d90a64a9659cd90458edbd62bb6cc9f8d942669ccb243386ee53c938746a2462ab0830a5ee749ccf6259eb0ddd308297e3093c1e2bf8454dd5e900345907a8608b9832d8e40d08cb3dbf14939398e8c032b1baf82f3b45da26fc2aa8a69639d1379060a52801809840916499ab8679e0af7fa2b5e549f58ddfc8c60090ad5d42fcf59ab9661b29f6aee13a20e279602925e6fffb2328ef1334585e819ea8002085571a91540444e60b8c9350331f5cdfe8ba889466f4b26343d3b179b436d1015fdbeb6b1586fec95a850abe17e3d23dbb519b068c9c59ed740833e71319db516be4631e64a10edbbdfac8daafe9004a1dfb5631249e558132163eb6429c715143679b05e2edc9c236a9a7ca760761c3a117bb03755bf8d04aefc7742ab0ba811710087242ad211047ff4f6b9c4f8faa4640f53721a400815c301a8b079d1ca2b7184bc65e7bc3cd2c380df2376ec233411c4d429fb12795cdaa8b932bff94628703868796573c773ba5a60dbe2dcfdba86fbf73e2cbabe306511dd6f71509607ebc04687500585f37d8135ebac5386803439c01b36e449d3b029c1576fc2f422160f4fa03d8f11c89e134103a7918939563aad5285049c66b3479d416852b11896f8b31c6d91580c0cc03bd04ef40a9b201d686cbfec6170ee5a1aace7c7f2248c548038c030a7c267fe72915cd397e273c42561e11410672a1e41f71827eb247b679c4eebf11bf064f256f418ef2364b7eaa29fa9cfbf018eef1952daebd915d423652908cbd25b28b3b07d28cc918af5ac73226b02d6340d02ee74f643db15c870a4de33c870580fb55142ed86a7b1efb02d4f5d1d6e074137126f61818d0dce8e49b8aff13f2e80fa7d585aff6147ee7265bc92df749635d9cd77bfb87508e4dd08b4f437bb59702e34ec8c390bf6e7455a4619bb3586688b16e1045a9c0c8d0b760cf3d11357e9840fd3e702cce25045c7835bb1b0ce6275af5571d18d1e90731123ccd8bb5b7550b8ba901f3e94958acb3e9583067cdaf68cd913726817755888db2a7a28e1a4748b9ede248fe0ef76aa51bf8e5eaca3049ac9dd7984241212bdbe5b3d0d4b586a7f468056893fbdde4c94203e8ff8238fa1a028ab2b059669f73b36030f5205fcc5958017fd26f9fa8d827b6cf11aee31fa922e8f0c21c5cccf059ac7d29e54a4df6f1e43b8a4b22689859e8569dc98990b9fd1d1b2222e19e76cbb31fff18fcf40798cf42482bc063795fa19dfe65eb6e9a570b4c41e85f9c94ff56701adabc0cdb76def2f5770270aa236e1be1c18ed64e51cdc911a1133e096a0272e0675c680fe24f2cb4f9a201401977945b920b02e60c84f3309e8ba8688c6e982bc9357327d438c5d34986a3ee5c88fc5886a4c66f1d14ef625d0aa01c82cf320cb1543887eea14b4481733f213beaef47ad92251eb2910bb1c23b4c930200102e6ec848b5470cc88a0204f6d4a9aaa1cb29e808e3cf2cc7622ea3e6a331b26e1d2a78f90b99066780df63afffc89bb080ef954828fc64a9b0e589c3d02f1f8f8c03919178dcd7138e1381f54749a684c6c7f852b0e85251427b6dad8334cd11f9f2e16ab2d7654d1dad887e5dce9d872887c6dc9d07f45481835c2ae47a1c0c9b680a009ce8218506f77c3b8b62654b8fb93043f60dca8992c4ca79d52dc8431a240cc5d01a027438fa19c91f638b7cddd6db1a1f6faedbe1c7eda2cda8637e7e0a2f49d2ad0109f7a5bbbadf6c8478ba78e716fab26944e182e437f00afe8873f1030439bfed6f6bb3abc39bd1882eb2b6eda0078c680e924d942cd5b55449783c3a8f192e24aaa6c305167959fe1cf58e9065a7784325c2d0fe06c3e064f99ebc1570a82cd43c33ea443971e0f434ac11883fe84461b9821746609c618a3362bf051334253a22ae38e9b6773a528204c0797297835f3f61549c8cd8ad16bb415ada181c52d843d558dea5cd548c3823ac4e3d6df1838e0073102582f62d7dc0ac66710e9d0e8826ef77755403b7f12a2863faac6dfe4cb1914e29db1b956406e9f46ba2653617ac8ede05dfb69035dca3ee5c2d00d3a612ec25b4fe71e06659ef23bb53a839b6ee48c4e38207109e68d3e073fef16eb062dcc3a22a15afb381581cd7e9db2c78f2b801ef7104a5ffb41dce92caeebf6141190682aae5ce74e6b6adc7c901eb6db76c548746b9170a1c81930149032b19c0c0ab947223f7d82d1f7fe5930eb75cb6dd0bf5394a906376146da02731747ba17ee40b5061140ddbffbf7b01d0e6661cb6d3cca9eeca0a0c4640c5d61c72d9819f1b54c8878b1cacc853f1ca2e3ef455bd082bf5bf60e4b13ba7569e7804d5b1442193595af32b92e91b90c7f7c9999f7c665a9c7a7f5911d73d415c3339c6f0a968a68b6475c1dabe40dd46087630e1fabf2155f2017e4fb42180a3319e7d7063c027b22b6b8389f83c9aba5a25fcb0cee256c1c16760db9bdec0c82d90800249d026ad7e4fa1f8635c4ee66ac2ecc8ac01ec76d44981128e4000cd5a529b4011b6145ece1a1d5404a453d8f28fb6ecedf4584fc8a952cb6f9d6df93cafcd21eac2a8b63973d1643a07daf8ac82dd5b054c0d25445e09978693ac63ee2de6516fc2b9c0d2c59aa561b6029edfe22e85ec79edb34bdc7617ba11cf3d4c0f02b13c2eeb3c0faa2fd0592f8303ed4bac9e9f81e7c627493f4a334c539b8acd96f02bc66d56f149f0139979ef02b7f584ae703925e664182e410729af2b9675cb406169d27e032bfcc6e540bd4b7b1b11054f83ac31d5c5013e0ac3f6d6e6a97ec1b08b22c0d8fcec02e23c3d2f56227fdf155a9a7c363854deaecc5321bc94fc52e50df9ac72c04b4fb74cbf7cd86c427f9049ea0123dbabed674a4e2de9652290ddbf03a1835d5a92ae4ef3237137432ccc8146c33fbd50155eb4e68cabd0208196843c201f2ed0ff53d389b7845fe62eb1e873c6f5c9c1216ad8ffdfeadfb1718e74c50fe500225ee24b08374d2a8b7fc17d324f50220961759194b39f844b12739e80621a5960316675859bed49ede417bf5a5acc2e4b364b70bb92683986c1df944a9a3b8cc87e3951e156af6abca648b28be2b74e6df59df0801edc236b129ba40af8c1c9f93b26fd9a45d102fa7bf90e7f51f14dd59d241c4327642134210376431209d2593785a6a4a6f042505387ab87daf614bbafce0a71426a7e26d2ebe8f7a36e23272d68e4396afe0eb899fe01103e5190ad987834ca9971bed9c1030fa67d27207d4a97e2583383f5904f0a0ef868a6b8a0a2c85fe1c9554e620130e6c08293a578b5a28ea7c0021ceb00c5e4e171a08f2097216d3dced274a6407868f61297f9fe9e95cfa52375e1390c2a034d849bbce454638828ee58aec0b0e171dadb3c2ba34e649b6df427a1a22ec81e2406de5d9d3db670db9198fc1070ef752419ede5f9973c2105bfba9ccb21d5e02ea1e13024a8072d8d2816018970bb8e226425c4c82771c7400941b55dac1a82c6ee13100d8bda00bab5a6d4e0111f101af580b036e177b7e88d70ebf1f57d3d9869f488de7ef2e41923a71cda5153c0f99aa741088d700453e078bda0ac7cf72c4d40e5755f51e9f972d5193161ffbef1f56c0eaf0507bfe67e000aa14c77e2fc597669522fd7553bc3a340e622190378956119d6213836b644a25648d9b3463ff256fa07f431fb1254f38a2540d7d8d1ba4d4000d5470105399683fa97f32adc17549946b7d4e33a06085ea451bb9b0d600fe3202fa84f96bbe9895918438d9d23440f5d086cb4624eefc7a2c09554cefe010fc8412f7dea624ecba0b778302ee742b84e396dc43d3c76eb2b10567ee21da53b51452ef279344c3f4cf1797576436ef727f496641913bc0f28b2dd03f71184034d502788de0d7d8caa15ab98c454d2676e297017823c35c061419bd9a63ba9deda91b4a7f5cc27883af1ce981a40998cb00af959ef782a4971b2bdfd91e38d7045ff5b399c5c097e014ee9959edff45d11affda024527b1c03417b6609dd14487de0f2ab328f2c7066e2fe3d18fdad528fa097746a24139bfcd1361368052ae8ebd22969612d971a162c5cd161da46aefa32d4b540937b80ee404eae6eb13a39ce56f8fff92b54da51403fdfcf7946dd8a43ce26cdccee842af2f6eb15827f305af101464d910fb943488e0e5ba33853c6206eddceb9e2575bf15ab59f5b519e3023a368f7c1584b1f9e4e185f05cdf6b4ae976f075d4db94882711c2ab8e50b028f8f181b95604247e18cefb32c187176e0c4264b9d9e78f7c053277dd97cae1eb03dcf68ccb9c14d6f77256486d60a6ef3c8dfc54254ae5d317284ed44a8c2ef6ddf4aec97a529d841a43fdf3bff0d07776383574af343a6b2504328a61ae7607b1e792ad94fd63e0f5f7a714febd8866ecf4e93be767a16f2c6a4302ed3c88f3e59bf92c5343617ad9051655cd702ff03e0a52af7f0759fa18826dd0d6c0d6b9b58a943c485c911cc2e83d683725ff800e4f44dea9e5747c064f56dec7c38e976a8667949d972a1e8d5cf265e4f07d86e2bd4c2706fea5e50780f6bbcf004fad574131b2a5d060402b25a79ba39adebc44adec286e2641d02c06eb8264faccb1c5ba4cfe130df3ea472e3ad9df249f14eb4db5f2a7b1bc271d67455ab068c58b17a0eb2445f60d9788b899e3779594819895fadc61cc2e080daff29cee7c04c152928572b235c790f229121bf087c155efff507d086f273f11769248554252c8d5972cc79eb93724ca839565986fd0d23149a7d428b2eb18206374aaf52de2629a8215536912ce04303d8308cbe65b8b76a963b097519a531615325c2fd7a1cdd84daeaea5f0951ba4421342db3b0225bd35b659e31cff10180bd6dc65a34a851c7c9d943c9a600c76aa179626a52d8e9f77fb03b5f03100037dc56ffa38081b2a6db99e9299075f3b2c4cf6039d9586cbf123bb759bdb130475385704108ec22012b4742792a05037da6aaa56e84da60c467ea7d3a908e81d7ecf0f95150be7f4d5dec9a1dd9dc832495542d1c376395c5e7aa334e020a21b28b9245119102fd119e81c340311a9ae3749ba3384ba0f1204f6293e3937dc0eeb2fd7a2ac724da6224dfd2dbec16d3a3af4c70ef9ffc653a52eab8d1693a61bf5633124ef3a8eec373e13373abc59d5993e616593eba7dbb5e248ff802881102c6eb060d5d2b8968e9eef372763ca5183b873e5a581e9e865f47b6028bff71c19cd3c08fd68a23f0d9c99aab607387b9c6ec678f9455616311768fa832a5204da68b4a1d18935ff4523e6cf72b1850132058c7f150c2e126196b6723f19ad2fb8db2bfb259f034348221b3d3f87b85780619a6d54e67cf0a55965862b7fe52e23144adc595bc2be1a48f1c1cc41b41dd9da0dbe5adef141730e340d04585616328ade11c06b5003e195f1e3dab9003fa8013ffd3f7e6a67c1f51ebf65dad418a861e2f11f2ffe125ce67efb0eb74f33bd131b5a5d11b6446a8b3fa207ed8b2285a3475847e33e625ed4e7d6533778b595aff6607586bb0412d8210fe340552e3583fceba101476b4fd039726118ac87f80df3cba443836dc2a53fd6fcd34e0e18d5690c79a9e9db8026bb60f070b81acd43c1e5f7030ea7dd2d61bff42ea430279ed1125758d018a344fb17c2592a3d2c34594303a65937825b43c263ceff01c029ef10cc78125dd9f4a55a19c460e1ad391a367f976c8d14a2fb91320efe28825a32c3b76a082a9e42428bac2e25c4892681860db150836148b3b1fdb13d1f64fd78bb318ce7387682b34c821bedafa127f2556a9c2687178ebe195756f609fe81400fb471b4fb1d59f727b378ed82d87dcd5d496097eb293d973564d398058ff3208fb5c9cd219328dff54018be2374f4a66fa489fcdff636810ec5288a0f1428e907d19a12444d9075431e049e594078e1d039e1260075278fb374a93e0105ee257808016fb5c76dee7c048012653bed21b536bffc786e8a1d77a2b8290b41964caad2025868bca96f50308b0e8d7a814ec83b900c8f77583e86bae18a18d2d335215664076fbe4a32999c11aad848aaa1df092c39ba98380a3439943e1ee02ed6a5098b95951eb7e0447b7df1eef98bc02b65dfe974bb92a4ffccc41c2983166df1755e1dfbd5545f0d62e8a27daaead2c734a6e61a196f7c253b967c1528ef00362014c7cae847514cfffb599397f6fe6c6ee72025414d2908b47de40a84cb4e3c3424ba0329707761ff4b3262835631ea1a3e164fedf5077ad2ad20039921b628ef95d8480eb14371130e00af763739537e2d02d6f125589f25eb850ec1140ed345fc070944b3846169aaf337e145e157dba7bb5cc8ae1919e050e01864dcdeb4f64782d440a6e643bd1f58b00fa425237d8a1f8b9a014d7198127601a587ed01e73a590f2bb886b1526b9aa2f577b51ab869e9e11fb795efaaa6c31762c2ab563613d28bfb2c3d59f769eb526bff8eb35ce7a97fb655447093663f1d1095d270f54f28866027ea95f63a6e5715618c99e7e08f17948a1e8c65d0f223eb8346f3a9ca6c43ab9669df0c286376cb610b5a073c5371c2c168935bc1a82162a0e476196e2da58d90892d41a9a9ba1d51aea5d2e8fe1d3554036501ad561776fe8203c0febc1bc49c023b4f2095f7ab99dc9fa4a09c70050ea6c1a6f93d04e66dace55265374deb889c25cf2a56f3cd964ae8d3ac45ee95781743bf701a13b4cb3c6948375a09050916d900fb3d0811a077bf61fad07727b53c2564dfe216679c8ff255da315e1e475b866a908dbbbd515d620c7182b849e8863869ddd808902283f89ce762a42304280edf68f9a76b67fcb61a7a54cda8575384ef380b24d73b1e75b50a6174ae6ab664e0a75f91d4acd60402170a2bf967c9862f0ae40ea80f4d02e46dfc9d22f4c5d3358a3a741b39930b017558af7a8c9e268f37a00cd48b6a5afe6dc0785efacd85eacbf38ff3691f503bd723796d7a2b7a5505471c0a5dfa4b6eec5f56d4b7e4805da697d7a7e2878d717e21d742f9699ac0aa802f8b8c774afd950836e8ec22a520412abd775c4619aafa9dd53a9dba31f385d121f23710baecf82aadad99712d54fbbff07e19d618663216ebf13dd355389ed70ab297bb2e8f14c44d9db5aed00f4272956f940c814e02c329cac3b33e110f7b260c8b643c9225c725f638d415096330c953d46a6e6901548de47f050cea00921c406ed69b8affa8c49e5126b09b7407f1fe39e6c3a67ff985f44b4068d17ca2211a8e6004ac2ce57b738ea79b47a79212fe05f59fc333dfea5b973e0189f8711d2ac25b7da815377b5e0b79bb57edfeb0b3e249284b093f937eb3c8cd5dfc470e799e62210ee5038ca82113eb298d0a91ed7265809b0e3c41842327227bca7e179281876a2d730f88bfcde6a261d12b63b325fc4b8f8a07deaab29e073e24612e721bd4dfbc7a76064f51b97cb8c9aadaebc2219602c4018e412558f0c92cf50f0300b41c3af93217af662fae6c20aba5c5de25678a70e7548eed1465315e04260f3dcd91a642d67ec3157acf46ef336e01231612fd33a33d547b352838ff602202827e24bf1d82e874c4b2e56d6da2211d4f7ca4affadd0e17c9939bae8c861988b22b4b7bb9ef9dffa40b919b3a3182f30d07eb113eb168bd5c30bd8b206a032495cd76b9b5ded766b724b3e8a955b114b6bc8eb936462efea06162c87c2c1caaf7adc04449a6a314f788d3f2e7ad03a89388119434eb73467f6a22898b762192c947c632f83b1d839ac813849f7f22884a21461081a6d636bc2c044241732a226e71541009557abe207a50bb07ee6e5318c4ee2f10bb0bcda8e4fe00e6b71fc41469002dd8f72c043824f87eeaab33b31ac8effef573e9f15bb24f1af840107e0034eaaa8cce6143056e561c708175ebd2bd2b01f7204024529b36328ec107e1079b23231ca0576310db1dcddae6dfb2272b968d23ce655bfaa3b5b578bb051aa676102be135cff656a57bf369ca177e279e14f723c6d567cfa0cce54170301665691ed411acc68341b6e22d99e80c111504fb6e8fa565540666a6c139f21693e4c8d39fee103747a085926e9c36d672e0950b53e0168b556f7cb1a672ec12a0aa0eab6371061b8532a895927a22ff8cd0d1b0f4c4daa831cfdb62551d422abedad3879ba0e5d639f4ea58196f23087af2ffd496a7572303590001f57c652e0d004ca35307c2f8c99a335d6378a3770e5dea4aeea8e5a4493369f406fbdd1bb85993f3834486eb72a18278e27c76aa92986a4b9285143493f120104d628016e1f540e94796eb5b0014ee099328f84ceb614add86ccfbc550f7600f3413651c6d6c29222e50e8a15edef04969562fdbee5ffa843960d92d565d380cbe1def21d8212fb0ef618b09a78a0d3030ea274197521f239b7b82874d575222ec0ba81ee82dca840e340012136ef0b243eba0e650058aac4a5fc88ad5327ad848ecc710d71beeb1dfa88fb932d6125e181271335a9083d7fc6e13cdb92d1ea5b3d60912591a5d8eab8b8db19ca4336fc32c3147ffe8088684fdcaebf5f634cf5e1eab1b68a4d96dbdaa0afd40748e0b4a0ebfba0a627f30c9dbc621e727f7bef5e78e245f34dcd0a1828753e5723eaa14bb4313dbe15d5e76965f8bb765910f417ae403a931a5b653d4fbe31332fd04fc6e69626fb834cda6fe48674b98219ec25e212df043b86786dd8b06d34b0bb7e6cf4c5bbaa01158fc330b05fcee8b4fb9eed20ead836e6b8cdbc6cbc06457ac64cf3a72283181e35a9657ba381ed47169ef3f178a2f40907c36a2de63a99c8cbe92a23d9b10305c3003f5341ef1f9bdef0a99000586c10ea5861a049a13f7c69b0bf6526387aedb3952554218cb5ae0266b3d17107b45cca47a5078acf00a36247bd987db1d331c585953c27b3e7b68e34c5c086f1e38aef9ef67f6bd98ba574b8534c2f71e4ec038565f0b24c6faeeff168926ae5209a04acf180a8c7481af21deeb7a02886f61625e734604feae11bb965306c08ab20127db181eb638f57097f85600f251529faea4299afb46dc539ed447212bbf694211232606e3fc74627321d1188ba6d02f9e8269dd81843182e41575113b3b7b2c3b1a8698546d3aec50985c37a3184edcc69efc421fd3e4f939fde4182255831a27d9b5d835ed40def306d138225b855aa0b81e5707438d4cf7408725dd70cb357f50ec938767a6216c3c45ef2034d3fbc3c8555a67b7dc78b47d8628fd188cef3d2b8dfbf4fc38ef79f480b2efd8c4adfd46fb6205e251212ebaf31e70b2d8a82ef799db3afddc46ae635734ac0c700fe84891c053e63156981b0b0fdba632fd2f3c239ee4bd04254130f5d66e68515143c5f4d6f2b29e17bcb1a303aa93928c09c5312a4d16946121da973879c2af82cf71b14a2ecbeda0e5ec8b02462777d72e300a2f695fe1e4440becc12fea0e2ca14ff1862931e8175d31cb1c9f8844549a6ea22385d3a4f60cd14fd6425aec8890bde9221d42f60e54bca41eea53a6a00c6fe7b66f129dec025d3c05b1a7661322b803efc62b123918b9700799cf50fecd11f0787f2807bf01fbec1f9774eb693afa70d652dd59d292b74809f0b45fd23a36228017f8cfaffd39368d2317dfa86d5eeefabb69e13c7d7ca5128829e70548ddef6f0d1230f3674f752890c754e20a858ff699eb425595132818423036c330b0a4c86f547594d8e34b5abacc9933308d03ff4b5307c9f44b721d9e1d6655d4140a8adca0117a6ac07565e7e8887975eb3965583949cd93c18900bb301068bcbc64aa85a14a8ef8365ea3f57bbe465f80ed8c039a04a7c9e5da0e09ec9c44885c8ab72021e4f8f6c388240c577957dbdd7f64e929aa54c2960a071e8b4cd84f4f659b95605e75c42ca42547f2462b36f75b57972e6f9859d45f067723d028b166e84ad2a5d9921bf762376c3e90ae6b17bd87c27e0fc007a77d1f5f7d8b0028999ef82f084a250d8562793aa0ed27644ad82feb94e80d948a6f09feb51fd95f0632eb2b356c130b50c4f70fb0f967cb7eeddb222c546a5efb49050476a0d41bb023dc5d85b1cd7776f9a2c3c792be49704ef8e0bb09b9e6913728a0b8490ce3a1277aaa62aab1f07ebb41c1f395c34eb9e983884e8dd953bf4a0f0e353000cb746ce229ae769ee10c47f15ec40591d589d798d0c2bd2655751106774bd58d59720491a086b4721fe745209895969578d037e71ae982f8a9320322a859297e7a85a2002ebe2e883aaad92af39668cd0798c08b7eddd7d503b24b1ae2daa5c4eb25393ee6ebf4008bc4e0c072ca90b67a65c586fcd5550b29423f093d0da983505afaafb1eb0771e8e9e1cb3539fbe61f5efca2d4860b1fa1b36121e5effbfaf7611b946208ea448d990b3cfdaf50b40063af0b7cccab49f5c86b16506192e554f89afa326ed0528f6aa248eba2da1825d58628355393113a8fee286a02b1c4a452f8f15011bf27dfa4fd6832f09725a7219294e11954b957f9221166c24763a83391287fc3eefe14933c5c14f4c7d6937f7b75542347d04045a110890ee0f78b861b3af29a624e25b367017d565f468cddce5b7f0cf3c35939a710bf47754178cfcde50bc00ad8afdeb5f65ac80bf3130f8f916859fdd0d676b51a19030af15d64ddc9aefd991cd5cfa036cb80826b4373b9d7bcc892d88b7dc91054be1686426a50d43560b6f5a738e11ac395dab9fe8c808f8f8ceef391350406b72a33744c6e8340275bc6ba889bb2a39e4e44a5ec3608ba7e9ff3169958a41fdf27650f39023fd280fbcd8a14f01e01129e55d1f39690748e99810326fdcd20e052a932671c905b1eaa5c122a28ec8ee55385d4230903c923a10347dfdee01e17b2763b6a7060a5b8096bdf84673c406e1bbeb5fba30c109497c0aa3ce4d91814c363d790b240b21d0c46bca0a903c5fee1e5fd6e2278a36d7209bff11e701244171b2ba258a8c4b611fe9d842c33b90dc2bb751a4c9834b94ee633ff2f0c17e99d90e05e03e67dcc81f0482fe23b3edcf8af33abbd48817fcd2f01e5cbad02174e2613f854965fff32fcfe1b2ceb878c5e8451dd0aa5d75a96db1423038d224a2a1f5665b42ccf0cd2009d22d026f92aed1329fa0eeeac0bd789e8a094c82617ebca343afad708cbf7746a1a7b486be884219e41fc54bc92afa7c4fde27204e3b778f1434022e9a0f5af3cb796f94594c41fe3d1343ce8e95cfa3405e76024841490ba15b8d6e571c78b5fa9bdb4f42cf6939f0ca766f8a33f9af1388e9f0cb4e92a32b3f70b729d395a955e2080b6f0ef0d26e8692148295a7b56b5f6a31596ef09d919d4e9ff98704206e61a8dc0a9ce4662eaf6a10878f3180ecd6bcb0093e2dacd06fe78b044dd4e2f973281e38c793a05bc453015dd5d99c1adfa70234a32f957d93338fde57563ebaf78674ac0ecfd3bfe0a8293a8222791613d2c2ffeacc050d243fae78fecd4172ef8ac8d65605f37137ac2d95706f7fd8f331204bbed683e9949fba2e4bf9941b4939ee707c3973843138bebfee49358309dd60265751d08c2e979f56c6bd140921e7a66e34962c2e608d7326c40f95b797636285580e22fa6e96ffbdfa09b8be7d9c5def6bb69b24b832097fc8501a8ca707c0b7f05f0505eb62b7964b96d0c4913735b728fa4baf217082e8f47e2f582872412c7a05dcc806ce1d8cddcca1d989321c22e660aeba3d8945d7f932b6de9e6591b97a10e4e7f2ac05c0135756d3e162058d1b9e577760bfa12f779dc1b21c04300b8aa9e06aa51ffdcd11ff50c67a307276719bc15611fd86c8098faf7c829da2c4b1f4eb53f6fb595e1961d38512f3ce9827b9e4822b90fe6e5c0c52672cc1d012e591b62f714b671321c5ce756f826cdc2bd630a9a79272f7aa02baf56779d25d42ac69b671532b1d51d23e7c79160bc322015ca838b4e674eea5167aba83c218ea51067f7176b027556fd11238b4bcf8865d89a9cd0bb3e4f43477398ccf22aa043b2ff7867dea2472de15e42e20bfccd0fd02248e6f1ebf7f95edc2eb2312c0a6b7ad4f50c3bf03555011fdf65cd0e2fa85d3bb7df6dd924e7bb5794985118a1d10561c8f03828a912b04d1e50f8bde69235134a4d4ae2da48c118a5dce12e703a9115055cdecba78a75a6d10a5e56613e9dc3efd226cd0756e592f880642c9974ff67e8a91633f52d02e73ef44399b35cab4796c37156f0f9800b7a88660345dcbf63e44f1fa2d1f8eab547a67f70db529978f40217b5422eed2588a4ee300457f11907f9415e349dad8cb86d9910eebe588cc8c0cc798a522e39a669031bcc7172286c9537143a92c965481a89f3c4e860d5ca9824175d9c7a597ee7c61c115ec99fde4909f191e31613746673c5507ab6e01436e6fe5f07d8a929fef51ff7676cbb3def22e44a545acbb9aaee1773f706fc8684a7b465d2f92f8af7dc0a1800216db2c08004b42287c6df656f6b5dedaae8e1b9013c94532463df05952bbf330909de4d033727d32480cae73d2a2e9e50326fc605ae30c0c9f011128b1a678d3619cafef8cf5d5e07d61a849c05e91e9ce8290b8e0ee69c705736ed81117bbbb0fad9bfc93fbeda8ec8b9464853ee161b5fa9b5e954c36d10f7d2e01d0837f15100dc35eb17b2dad7ccd87b482048827c1716d510244380ac5b0934e3124b0552ca7a5902213329c982bf38782b0aa337a618d658aec9d01e4885977f1514eb3c815c8ac482050a2d19638a584180f196c16339e4652b1b7f1ad870891d63a0420da9390f9dc04588fdfb066906fbeda08ee28cf7f06541519f991aa7125c2099036a614739e41d2ee4266b3faec273304a8a7fb09758a64cdae16d281d06427553cb1acf30f1603889e575889f826a832acc44180f68c230647d971413a392f060a527885509f15ad448f8aece5aadc9836862138a8b6d3b52c33dd41faa36de1e5f4355825cc13a32d55f9d84a7337b691d12ab95d7eddcdd3f72a22ee84a7b8932c0aec98b47176220beadc273414c3a7d739e598b932603544fab03f191cf63a65ae49a832011f34c94d75bb99361b17399b27d77231128adfb33018fd3f19e9f95a38b4f4296e1b8ac28f6ee41814be442b889ffd99b621ce5bf1bf69588a55456a7f9911222a4825f68d22106aaf2ee07891bbcae65d952e75b2ce90f558af3f890bff0115d005d1e5f714935cb35aa8e12c3b0f194f53ba20427e5d13527c1a9f7563f61ae1f59a6dc16f2cca1df47ba4d47ad490a54d1a9af10e1bfddf43ad707f810b5b9b48a91b16d892c73403c3f5cd6670bf79e355e1d1cb721ea112226ce6a6d52283c887da649f153a7790ec04f440176987419b5421b7c694c646cbc5618e0f2e9d9ca5a10be1c9e007d519aebc3fcaa904cf4028d0f15c92504ec8655c6f5ec0bdac134b4194ab85c505cca657e295d2041c68be114b83a369cc7707cd41264238945dcc4d2581939579ebdc673ae8a5ec925776e28341d66f9f6eae02473c10b21c2f682ba22fd78f1b25895ac2adad8511422110bdfcc50e8b7e10c22d1061ca72d0945d2ae76eb874cca31ae41ce92e335e6732e0ecb8257b64e85774c95c601b69ccdd6cbdb5bce2bdb755e0f4309e735725f", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e94000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" }, "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000416c9296e38cf3689000000000000000000000000000000000000000000000007abf85d3a9f30c50400000000000000000000000000000000000000000000000237b411162b313eba000000000000000000000000000000000000000000000000000156b66ae45ebb00000000000000000000000000000000000000000000000fb163c45b929b1c0800000000000000000000000000000000000000000000000302879a33ee70e101000000000000000000000000000000000000000000000007d3fb89f63848af0800000000000000000000000000000000000000000000000000001191ba15fbd500000000000000000000000000000000000000000000000266d108d2bd6f116300000000000000000000000000000000000000000000000044a3d48dd17dc3d200000000000000000000000000000000000000000000000bc7f72ab82ba9582d00000000000000000000000000000000000000000000000000010d1bf42c416a0000000000000000000000000000000000000000000000021bab3c5cfd31c3d500000000000000000000000000000000000000000000000d01d612334779aeab0000000000000000000000000000000000000000000000090f4390fb5773215b0000000000000000000000000000000000000000000000000002561ec9fb68bf1e61c54fb309fe860b2a599e2c1339c9a0c1d4d4d7c19eb9e1925ecc353851c21f29faa0b2c9e2dce7b0ff6430e42bcab396059f4a009b643e80bb07dfd986a22c753cdc1297ee81add827710dcdce1b3905d8d137d45de8c4b58bb725680e4316c6900efe1d29c325ba03cc43ec30b8df8f7c8cef5da35935b202319ea75c872438635f5b3eebc558a54a7faacc657de20dda2064d0177e5ed3ee3ecd9f7ce014be0008a5b22bf423fb02ded702d843cb50b96102f67478344d3736244b75a9042116c4e0c0968f6fedabaad0ccf9ea1be6d1b6a2b42b566cf78603c24d65f315da1957f66178602e15a9baa053810b896567ba5fbc53b50b48a44400965116157c7f19bd181c7b184650af1f6df0687c64c2b54927fa663a224c6979cbb5a7274367923451bec6afa2d84276037f1dca6505e3ea5f37b5218d7e270ff9cddc001cc131923fe1c5c0cb360571bbde6d7670a3d91fad79d20d130044d4bab7e51ac2a0199f2bb567f4aa9fdae3b8634f70a6d47d4a8ec6829176f075caf523470eef2d4270dd12ee163794ae40acc907a4033b8427230e0a3080bbf4208bedc2169c5e1cf9f47ce448b3c89989c8ad99bd8726360fc259b1c35041205dd88b56028a4398df5cf93caadad64d1962b80ff243973bc4bf4a89700552f80d1e293c25c339bdd767fe4d0b7ee2d9b6c24f5bf65ea3a6e265f9c682b4f1b1a393f98c2d4017693a6eefa1e776ff4db63e9054253ba54e0f6c92613a8e3b55849035c606f4433589d1e140795dfd40198ef021ad53951f2dd41624bf7cc92e59783f3a29d9f6eca1bf91f470e595ece8a85c451e45a50cbacf2aedd775be0fdd413d44301d5db28a25fc8412e2a28e4e33bd176b54eee796bc300cd3d536f889712e1a0aad307d819cedd13a8abbed236376ad97d5acde39e0c8b0dc4d764ee788a0fd027b38b4bc46b09a7da5ad7f8af689326fe9c126a33a8123e47aaed4d5e877aa00d9708ee83d5b9fa03b2e24aa4245fc0d7e9250fae7191b292ede39b6180ad423b152d3cc3db2aed13f19256c56af480663ce8296ca90c2d85c2d694b51e6e20a4c03571d895ac7cf59e35392dc3908895026785cb42194d0559b3242912ea62adf77af10cd491f81184ad570951e233a8648246f3ffbcbdeb4e0f9307df9f824bc8a5dd0cfea8a335721305684c25bed635cfbac10e99964369e01ba3da0fa0df8138a289bef477027be8cb063844e59ff5567c05369b3d7fbc2d13656fee81b95251566f5aecb02dc9365cb51d522c3e368e3753313a9773206e10493eda00a40cebdc64ab5f42e68383442090734101028d4a3b3cc57c973529f6044eb1b1f09ed3188252c949bbccf14141f50b9c0d25f47019e1c0bfcdc01c9291ab7702315d4e4585011faf49227fabdfec64cc580697ea62d342a6d27227dd5a91a5b2a29035ec3e66629a2c99358f95d94ec98b50ef1b5bb59ee704b5470280668351ceeed6dfc8ba9c4627663f1a4b363425195d7bade1870885f1c07a8a2f8e82029d24fc02584248d25570148345a07fad19c756a1bddc07a8e0c3e0f72286441114933efd521c51bea3f3cc320adb2deac91b89e6fd016363d0b7fb6e2d10d4810e91cb4e2713ac83a62d28cb182779fd18be9f3766c7303ecff585f1c0bfd9503eeff54d606f92e1f978c61adb70cf59a3c0790d6c8184b688b47d10f28c5db24fc5858dde57e4fd4316c29873ff0f683c46c50af520b86b6bafc2505fabcaa0292ec8178651acd6b90460e6d65060be11d41ca74111a3bfbdc9b1d99cf7b3b221aa8a5f4789dbe9b007c91120d20e499db36f7abd83f02a1482d752f995987147cdb505bcf5b9968e82a0705a22f689f00bcb5a30ccc8fe0b25a99c0a22a6c27791efd79fe58fffcf59fa1aa0a7d8ca1a8e84fba4698840c6df2e048272a3528637ef304377cadacc8a86df22dcca01c4a63889492f030c519472e1f869ca329e91c181f32cf54a5bca551356b9c4d0c60e538977356b9622d597db2e97fb313fcc82f881eae7053f7aa4bd0edc0686b34acb24bbb90ba6757c84c3b6730ef27bf0f198b9488a471da7334a60d80558b08ced4e9c1b01f79f2358160268c9c1670d5334f325833ba1db22411592c14af3e83369d03ae3da83f8c0e4c9fd3620c897530e597293444e28daa1055f7d6f89f6ece42b1d9973c3f0c897dbfcf96160fa66e6c9c8bed3143148d883ff4fa31c3d6fe1ae6ec6e8b5ac3b840455595109d6b068a85ded28955858559d700697ffcc401df847df334026c21cb932da1072ad661a6469e211b76e5e7eb160581e3b296b2b5d771babaaf6508a29c034817feb2af6bf325448de4c7251b9e24a02dc474c923986346e30980758eb0cf2c19b53befbed9bdbdd43bbbd73b2f04cdbad27b9ebd4df23ed8955052b9a8f6fd1c32bff4a07aff3e1e56df730f2e2fc216b4f9fffdb5f6d53318b4987c68def42c021095a60702d3997ce3eeffeca5543841ae0b4a55349381d1222a2d6632b4244ff2a5766dd407d0cdb88beaf70bdef8c46ca2dccb6cfee6f5b90a61dd965b0186fcf84374a039cabcb975e7d50ebd8982c5a2474c5db21a6fa262cdb79a672220b8fa06bdf4aa029113eef5e332003ce5f03ef7618230c3774ed61c0482860128f589977a08a12e68b2743a80badf484f63bbeb5fb9141c25aeba24bc878a2741422ff3fed8720c2eb136d8d540db48d4428fd9b59150168bc623b40f22bf213fc3d76986485ba4f955af7475afdaff9f1a12c63f2baa6aa7ddd40cced56201d8a974e433e042073171389edb7a7b5e946148cba308dd80a02e78a2ad3e23019ef96b61d3dd0123805d495b486d76d0b9f3d8a48f1310ee4c733bcff3191500cfff383f27955e4ed578f043245bbccde47fd234312e389039a1b5bcf90b0d194297260a62a1ac783a496200362453eacf59e3be3dba243c1e33541a51aab521df84868549940a5b7e545b12a4c1fa0ce838bfc5244ac69e8b2834c7ba509f2801c3e1c0d77b6ab4c4f13a5e459ae4720f3aaa4e5bc98de8236f2a61188ec12326b66e96fbb825b539b41db33e51e53a6aca36b68bac613b91ab1b657d99f2187e9e99857159b991e1878086e6b931d24a8b5c391f1194461942c0ce522d8428dd1095799c7732b2af1df9ada0adecf15a46454aac9374abae6fc565e7aed60db2b57e5a24b226a0484bcf343aea6affba8c8997f19131a083d7da09a5bfaf165988bad45dfcb328512a710823a8a1743dfbec870e83e2352929f624372b97064e9d0dd64209c66dfd55ba091268d173e98baf277f969456077d749ba215a11417fddfbb5e3f7b410bc1350064abc3c0cc74470c845869eb2d57705cb5f11c21b11c833089928354ac0773b5bc6fafa12d4e9ba089acd82035f8a57b5e7d262829fce2cfa4e37be9fc640b5205689609d6ad185c87cf437727cc12b204fb310f676fcfee6c78ee2f3637333626d227af0d92d7912ff4dbe346b094b32bf36108066c5a3b416577bf7d266bbec4812447cabdadfc2e1660ee003e241449709f0196177629bd7a488502404a03c9f48199bc0d241b8f6ce8cbd8da78fce4eac72d68606740bd2a582d2f784d3cb38c610ff9c13bfe676485e7d02062273aad9a0d3aac2f2f3017e6d248adb5ac397957e3bbd8d1ce038afe1ea4b5368f73ea7907fb6239d7054a16a6139c7c9e9a44e66ce18c15d31ba66b18b5b7ab4d399f4706c5d0479decf2b5da336e6d2945c5c8317d9060ba8df6fa55a78def98656728083b6fea725a1e47cc8e7be08a999f1ab21d528816758209157ab26af22d787f196820105c4640ee968e4d67efdc9a46831d6b9623783013ea8361076998a3902fa23d94b1eaf9fd2f4676328df7a1b2081a69ee6b0d2ec1cd8437cfcfe5dcd411102263ba18952e56d9c59d047986456fb27170726bf7d605dc9c784eeed06d0d59a79ec52c11a1425f1aba6149c77a9309e8838992a803b339c6bf84db99de2e9e4148c3c43b740f6b456920ab24f7c0f9f820802051f900e30d3fd65e2ac91b1a57199f8c82c5ed3c86433a63b8cfbbb9745a5335cbae916502221a2789522a06ce883be2fdcac598cd70549e8c9f4927e327b836a98993f5c0352784ab4721bb59a473265104f1443983ba0c1f39583416ffdd1d897cc07d525218bd4200248199c942d674c166ddcc723dec117f0189752a57e4c763a2521f849285d49c0ca1d57d0bab91a530614e9a27da6aeacb64e5afc8878ba1c514d8579b1b2e762a859538f5ec6ea88523485a6e209cf838b4bf1225e681b8618809bec3778e832414d4c62f30678651e1d582a448252a16039e7ef6432892c0736a508c25ec9521de8dbb5d21b94f9d3f7b8520bdcbf31608a972f58ccb140a0f08bf0f3ef76b2f615185f8187636d59208375e12a170ded06c8f4d95db7e5a384c26312af709116fbc8fa440f4770f9131ba7882138ce3395fee9afb912050f9735442c7873311d08205fb16fc22d5fee5654380ddb1d88156a4f652f7aad1b5e23f9bef20b8223b417fed4192d84787d35e607239d822e6d16da4a2bac6f06d21267fde3bec0dafc9f0651e8abbfae8f450051d56071f197afae930935305da4d9dbf63ea1c0f62368f70f8a15866c9150ca5de551b11148581b7dbb3ac3a91104d89e4f4bc125e5a505925cadd64a4d7c104c6fe4792895f26b040c1cc46273327d06eda7c2d3de1f00f8dfcf6f911397b3b2c20c96d905efe0204cbba45adc56fd4ada7f8169ecd4306444313c846aa1d6e7881acc2bb3c32a9dd04bac0cb7043d97e45380502b10e7ee88623edb4ea6ac4aec8de66cff047b3a0b8de7e7030100434f5c11cbb97f6700728797787cad7111709a20288c667d7d4e08e095a40ad9b05be6e0552b75ed7fbd61c8dbe1165f2bd9a0e119b596999e49d83be09dbefb7e529a909c76b5258e8aab2f9b0e9e060a0e5b502f31647fd562b198e7a4aa3fce1b6332e35a4bc64f6d28fb7be4acd22efd02880227c2bb7de981afe547306c8a69f3f1cb20eb9fb9669531084d45b0149539882d12da0394612b2283947a289e615f113772c23cf3f00e8dd3b33e5fa8f024c53b3267b463981f72ce19e96a80dd6e61ada2e13c3f530742cfd36835e8a97c9a506cb699e0f86df3af34c52f4d3b1091213e38fb599a06c87eebc1ba26ba8d888df60d65a4a3554f4a061a9c63d369926e680d16f1db522fc33a0fbbf03153c6e6cb4058a11c4615ee010e70c379b71012acb47a537212ddc7fb695b37befbef71eab971ac03ae4cf7939a7c754e80208c03b9ae2f8efa57d6b4bf62e2ab53bff941b81ab2004d4214c8d1c3e41acd406c7f3f77d48a84e3c60228d1a839558a6c5b040155a3afcad38b03a89e03510128eb4e9892ab0b92c03ba8d4b43b964caba607a7d376bebc5bbe59cfcc543611f71dfdb56ad567e3ab4868bdc28f681e1284d267fee5eacc7938a2fb15539342bf1d9de71f15ceaeee10e13f488ab8e3a45d54c7abd84723bc141f90f1576e805498646c59515ae476fba76baf7609653089dd1b80dea0cc074c25794f48124100a10a5a3953da1e19403d981dccd265f4bdd93dfeb0a3b65f7dbf3a8b06a0a04e1ebe52ece119625511b8d01969c973f33de7b4b3fb6e2022e30de838c07dd12002f5b2ecc6538fb4c520a8f2e7baea4fefb8c5cf1ef2fafb00f0d19c6580c1b5a9c9f9deee13a3e889e50facb2f4538c1c43d325810643c44b1ec7f5526bd029e6c6b1d9a45bc6684109670fc6e0c9f8b1f3feb00fee3a61169e9a83c9dd42be2c6001cf3592b5c2932f494ed9a83bad49f60473d7612ed60a635d3e953d0028e993169bec778afb691c637b0cae6aba585d2cb8905481bd3eb5bf6de1da71a308aa629517e68450eaa8e7ea91580bbbeb30b7998ee27568ebd6145b6e70c20161bdf2491f545c1dcfa7303f7705b6628a1094faf98a5f1a36c6456e074a10401c234e52c9961b76f238172189d91befd0f7ebcd9d79039e29626f293d009100affd6d2254e10219e9b9566f8a923167933d37e586ee3f614a5012d69a00c00cd99154a20de6f0f04d9f32be106015e53d2c23d60fd9da49730d5ce3837402a375894d8f12d1b34e3995b154874c873f9a3037d09783b1b27ca9306bf2c3308dc4c73e1b95df94f830d9b250fc45d5e2370dfa351cbb79791e1c2d94dffbf13d328b9f475e53fda0f9b44c28ea21108032367e8edfef64f4c4e977b1c47a205869eaafa9c978e967a3cac32e4aae0aa895fe2cc67a6ed720d1169b06264e92dfbc6a99923c8e6fed8f6a951c771e259892ecc638a57264a7351b8e94e26d4185960e8ef13c4be85419936c73a289be253b2bda37d06148e481b495152e8ec1e0a7d1fdbd3a0d7de9891fcbd88f215cbcde85b4563f8993450bb2fb117443627b76e42ca03ed9c2718c877c640e216665f316867b5d1517328f980fdab9c0b1cad6ffe013a43c2fda4ced6d18d4a900d42a6397ff6cf7aa04665b6430ef14b04cf5f0b8e827810f4b9e628df101f9cc07b7f9d21aa602466ef25cac111f75b09c0ed411b1e0a0f442764955adc807d2292c032052d814ffb11fe2c4f25f54d26089052c2abc21152d0c63457259245e5dc695b2c3633eabdb6bb64ee7d1f652e5651678b410b12f0e7060b8e744416318503d6545ba957f5592047ca89f52b0ff2a856af7f6ec8051056bf31941000957a5d7e8a1b90f6758abb559a8582e726e70d762a07770a22949a32efad63c88fce8ab69e7ecfc221aa52fd689afca502f80afe8af67e02f63ea1dae245ae6df1691fc2922e963dffb4b40969a2afc401e1bc978955a901660fbc7d320019a8e81c76fc5e7e0719a37eb099913441151a6250590a660df462c08f119d70e9089e5acdb163526fd9d16f3d51d7ad6c612229709e6d3b097463069ade52da3f6e91a6c09d975847a351261525b3ed8b352e7c11480d1b743bf34c1c314e8a2143c012200bc4ccf66adc4cb8ecd828971e1065589ecdcf966626c728a71a42830941c96a4490ad3a3f081750168fead13327718d11cf276a90ba8e97d38e258e68e543300d01be6f2b06c51cc2b37489910233c2b17cc751da4d859d9d4bffd7a3cb89d6e8ecef481ee10e3ea70c3a50340bcc3646db58bd841b4439c17bd7116250020f3e9d0bcae0d122f013145b049309d0b89a89b0b5542a6c071dd247117f7a9c11d92a5813ab2234bd046e5f35682dbbcc16fe1f0d9b1d0fc7947ee0fd7d0fa3f41aaa13e7feb5ac3758b09b43a71d39360b515dd22150b18dcd0f4161ffd6ce130fdf34a1f21af7dadc0b3477492c58230829031e24bbd5947512c07ecbac8974dda0968a88ec196ea2a32988e41960210a0fcbe12fe59cfc251aee2a958577820683d2474c03530b5c3af41daf11471a0ba1ecf08b931a8b592a4341124146f7eea929b5bcdf97f4309388e4832e8acf205614a839dd4c803065ada539c038efa3793fd5d817a5d37150ac380c2688e1818bd1cd9402e785e1ec0bf8f70c0205df96db354c21c841dc5798019f150636540bc651792c2074eb47e8f6b8c8f4a1c18d43da452614900f830f4f6b1dd3cba0b6cdffdf86d4465254183f3b3037e25e5341956ae5bbb3e5b69764722fbccb0f85e82ddf9ac3e1b6c0e3765db51e570310681c3500d64f65fc81879c0755923b6c99e531545dcc3bd316065c7da8959f0adef1052b4371510dce386b17a9bad3a8a4e29ff2ba56067a8b4fe001c8a17667bd3776764b957eb097479e0c0b11340e650a8c5ca4a2fdaaac9f751fdc22b5ad7665a23856456a267cb44f0e0019ad5b49a2017bdf0771f62e195722f18270c350dbac52a3a0d303d9309821aca0436a16f4a3c4ca41dc150249bdd0364f2c89ab3c895b0004c6d626b566124a30f423f8c8b635d0919fdce2053e0414360f57280cd37737b540e65e2d99041a698a36f3ab376d2fde74928c547cc481f89a588176a7509bae404405fe041e6089219e12b9f770c516245400171796d9380cd2985e7eb7cdaa8cb473a3f4074552579d80c0699366babab60854d2c33ac00b5160891b6372947f93e910ea1348213ea777f50a05a88fdbd4b2494a7d16a2d5f6ea737e75ecbc7204b9864f192fe5523992d60c8d84d1b6eba8aeee8014743fba88fcee19d349c13d3d37242e3ddad8d0f0eece179bcd5382678b95d153b45a6172059f81d391c8bbbb815c1d82c2b71be144549c02e0f170ec5a8b1b86d844996b7a89320b61611d12e3cb00775bad99ee32f45f565cfe39d8c90333241b8aeb8c33596316a9b9d652b1ec2d5e8e9748cfe75a2049627dab27a6734e98f15902aa1aa3cd82d3a762fdcb02200ed6da0e18cf3f8afcca5bed31a93dfb79795183d3cb7d1032fd7da336aee52867f3c4d1fbe72a72ab839b3864acdb59b20ac38a1a267f62e4dcf71924a2cf035af3ac007525f75d53985c1ebef4201300f1052dbc5d090342d2ef29b839251024bae62c29cb468ead61bfdad488c919537f50e85fe271c25d7d7d7b550aea0a018522ca71bda2137e14a43499e6da8e8fc21aca461520818a0ed89bec8d8a280eb898c57d587192dbdf7c155004fabce811991a165fadbc3175a4ea707b2829bde1a0ee2ea0a10fc1fd8b041bd6a10ec6ed6549761499d74b7d9fd1e43e9b1642c7004a01c8dad4c27982cb87a76a7cdd92324a1c4a03338e0fb63bc6aee71e0f7ef06ba08da79fc1af81885746f981a1b1ccb86849bd93dc4946da8194560ee663dc8abfb3b4f401e97d4868baeb3cc9e02380dd317eba44b77585d03ad818eaaacb65f3696055f484c63bac79187a5569731e961f428906f70a9df8da601171826be0373379ee40fb7946317f2add3ba941641bc08e1c9e76500ed274c72fd2346a922c9ed939bed51203e6e8434334c8430851bcc79269e800eb376f9d05571558d8ebd306a3f43867406dfec2834d7e60492d786f022ac77e5fe38a4e1f3bcc526e694028e64972abf52a540e5fa9c3657b255b16443b0f94cf1f88450627b6abc191058fa16cee748550de84eabb8051a54cbde1478d527b01fec7d722847c86788c1e29add90ff796f2a4b430c50ac6724b1f50fbe0678f7f63ea8d08ba24f780459ee2118ffe09e17617a9a875f5fa866244a1e0a17929f2d4b67502b26decc17b0a6833b9e1fc9d6aa1014410dacf458da78fdd1cd87aeff21e0001a33b055d9887c58a323281ca2d6f08372098564035043daaadfe13ed795c100fbcfb85ee33450f020afe5c6701108419af3ab19dc36b0136a4e09638cbad8618cf10d5da4bb473b20e2d4f869f6abd9fdbb3125899efcf8145c6dbc8eaa5782dd735114427a7368894596336986dbfc6f83dcf2abfd02dc49e5096425bacaa0f4fefe353c5aed743e4f7de346ded390cdcf6cfa6866b2e26e307463f5e54142266af3ecbae5dfc0f6dc6ae9c95fe0b53369b7fe4d333ac323866d5577a41ea2c6d2a0983c01fa88c910a18d05f4b2ccc64617912fb052f68ec8f732e1c4ff81494d7b3ada40bac069be1534e46036e4fa15b67d82e35b6c4c537cc8d69babc03aae75362394a6489321b24e0767b05b683c261858ceb4252792911c06b625b0a1733c96181dcea552b33390c474d94fba7f60c1e79dd8630594b0e85eeff922521e5e26916a8d161d83d01faa227f2a9d6444fef04c87f4dcaa6039e1777692a97adbeb62a33eb4089559d7cb7dccb4f04431881d57d6d895aae0495f4327e2e4d202b13b3e055f00130e17d7312f1057000096cb0873f6f4e75cc094a879117c3b3c7a747f460fbb0783f83b3b0854477e023042ca514e68f4de8ce774d1626e6ad5c9f8602fe6c6b5fd8b31137dd1233ef55f086f28918764c2e9d456f212fb0444788ce44fd4352e3a05ac3fd55d1da6c5d5242c206256f663ede364e6e2373732b6b18f7611b1e4b63ad4c976c1deadf391b5da979c5b02b25d5ece40228affd372dd8a5500634ea7f14730689049209d5b5d28ed1af43b6a02339af83197d38d1f6964f7ebbeb83383a01410d59e08e3753b29980e6b536ae638590652a7b9a117822013f4bcdaea47c121aba0f791728da022a30d4aada1d57f8c623068f6b97acdf1888e3c9222e48ff811cb06afe6817952a5f84e27bd6ae7e6eb4063954f2271e0d8ed25f91193aa8f92e4a1a8c16fecad358545445169a129aef0d65d2d7d185b145537bc3b2785e0d7bc7ea6aa9309a2d0a00385348c250b3b614b1fbb7aae00ee336a9c40c652b647742a3607a56be6b253cd9152e8afeb67613bb31e1b89554b30c9ddb90be8fd9f795054412260ba7f17a74233ba263c9311c487638106e346d6570371469fd0bfd06b4db971cab7d4ef0990f3af631d7a41b9dffe4983c4c2e984d47ee3327e25e0e1bb4fd786160c4b34c846a56b76bf91c8277ea0a4cf36eb760488a05cc2aa70bcee215bd2d93510b35fc4eed1f1b4915e88f5976f5793046aba0a00dbccd7f3a316beab9510be08c9f05e784540a9e232fe8b33d7ec5ed4a277e7b0c17e27239a133d82cfe4dd3adad6816773967d50b2b89728d1190f312e5f10e8fc7d3ee8218ead95eecc465d5083cb6849c4e6c2c2d359afba7a0809dced78cd7163d88caf325866d1ebf80dba614817c3c47ca0594cbf43b6961415ad022c67b6e16820cca393368b51f821687ace8ab744039174d28be0ce2c8d9b07bbdd2972d52c9bfe6f26f55ddc67f015df08a2bdafd7512a69698ececec58664bbdb7c7389052eddaed595852df40e2666659e661a4972176250fff62ca3380e7f0e2fe8b22b2d710d76e6fcd4ffb0f7e8c9ac6c1127a2a83a5c61ab4c3749440a8f664d7d31bfd430dd80ed0cd48b0a70d592add19760701d3a991136bcb410559e27783a49625e8988733857cf2629c9197d174a94007003e3884b2dcbf2eadcafa5dd6f4c0795d41f78535c9e78424fd46ddb62e50251cec8360443fe70e56dda0d20550006d6fb8144d28755b55e83dda5a22dd8522df0d8beee822666e8eb9e52c43c3fbd074c41ca47c3bacd979ccfb528b2f2b0882f525851801b114af0f703235ca9cf8f9c9e50b2b5224430225b371379e7a1c7239a50c48885d57d708f217488080bd1ecb6d58f5205665fd593c9e0bcddf23b2f4eb1ef9301694a34e8738179e8e1369a60fc3fa11ed46407606363f52cd24ee26f4d3910611f69283f272f9ccc2844c3411baa34e8da7364dae8a75684000f0b2f9f04ff420e60d1d353ac9d13e7faafa17000ae8014df2fbf7d12c3d7817223f92516a4a8bb4f0e93ae2be4207956f7fe6c126daea596e4821329737b12f0e82c3f01cdb5915dcb711359d725ca71d8db88a20194032ee174e0296031b2f696b4545925563e1d1517e2c04b20f117d21ab1f518da2275af79d25e8d41607b2f5e9c795c05bd09e8e258b198a9b8d59b9abc7b6f80225c58cfeede76fb3278d3c307fac2e86cfb0dacebffb44e0c6dd9a82c44b6a09e480a03db77457542526566aaf260a14c32b6116575b481d6207aca05141d8d2a9d491cb9a1a99162696ec670c96835be0dbb7acd8345d7f58bde2465c9411c61efa95cf126e0c660a6cd0352efca0376e792a21ffb8bc5c5c4efda9db0450bb0ace4d4c9fa128b4297c7114a11be0b0e120b51308938e35bda2a3883517ae4eaa1d40c1b06f159e07d21576c33046501b0e5fcdcabfce665b833125b78d505ca673246352dd27fd1485ef6fb7528e432f977ed5ec10b0b786a819607c7750295556208206f2578925accd4fc26cab678389452aab1b5c737641fd584576f8b5eec4855e12d9ca56215db2dad99e19a7bfb18537fac87fd6e2afd076a160426081c063d883cf07f20a0721161fb081ff18cf9984812240c76a5ff5978697bd7ca060816322c90ad80126347b928a7ee34d2fa4331c9964d45b20e4dd03bfccef79a30ad07bc86b641d7ecde5d7184ccad6aaa66d296fefc304910def84095cb11e85d41fa5aa6b6e164350ec9827f1da24730efcd821b084047abd9dd6c6926526d5ae954086b0c510324f8d63c5d5cf2c7a0ec72078e8935b864d4174b1d65c9e56916404d56e472ada136e932d27cd277edc82d035efc8f117cff743cfd0988b3dd87e8c4af85425b0f856933d7f16194b39760fec8c7ceda5bfc1954cb224bb267af6ca4e2a1e0aee11946cb19fb8bb526017499058df0bfa6e932419e9114c7fa3504bae3aa52a34fba2cd6e0d3e3b57aef491f62d5cbfa2ca81503d2432eb3718df6baddad1042cc2b16fa503487386d885f97a0165000acbcfe60914f7f378bd732ac1632e2d3c231c0dc6f4f73190481327e93a6ea070953061c590c61cac490e057ed68d1041c85c671f90f92b4adf967b23d4b3bd487d88f2d20ddf0892d88fe69e7a732440efc7fcaf1bfe1c4447b2903475b6924ea834909b586dd15210e517abde7302648b539b2d3205bcca3c5e04320030fbed6bd23a791adbae7b600d61cfe58d0ecf9b32496dd8b49a19bc92e151bf569e3cdb51f522c92ad5fbcfb3825b2b4f088764f0347e072898b819fa4b7c650c6bf5bd0e3fa673ce756a382a7d7dd254265f60ed152b172b49ef4a87ec2cbeb0359785bfb85c5cf704640a8d891caef112dde0c9526f702286bf5ca74291dc3d23150254ea8fa8d7153ac3e5992750591938e09df761c13c4b4aa1921e072bfe257472220140d5ebdaa8cae7f560e9f714c95c52b4894db5df6c247c94baf38f12096da6808999fd18aa6670c88065e80226628c983330269161ac7017ffc0a4ffd32a3327d9a7cfe8db6a38ffe148801c6fde9870699e806e07e05aed0ad6e4ae588914c12cc74e63b4f4d30552d5fe051a876daef9218fc92516085c9864b930e810f0b61bfbc9a395b47da39e565817a2fca920d257e9ff9bf3eee1912d6c229cb578f5b0a15337fd914f134119c5153ae19ad490edcc124686d9fc1788c029d6cbb231dab0867395a3ced52cf5992b7aaa6d12151bb3ae8c3473238e72dc9fe1c255632cbcd1fc9481c38e4b3f9421b2aa75e8be1e3298246d5dfb689746af68ca58b6286b688406bb0bafc94c66286f9f12af684c6ebd555ffff7db08ecf3bd2fa20500b05ab8968351747ca14a0c9d64e6beedca294c73ce5617f6978c9b24e519125c94ba09c7ed874a80b7620feab3b353605c010f4b857897d7e39e3a6fbc840c848b4cb68aad5f3479611f2004a749cfe35a02ac5dc89b5393fe30f7c2afdc837b159c7a2ff06e596519c7116760cb3b73de1361f18d384c55c9e3540321a68c4e2e44e5c941b9f665059a1e008a16dce409193ed8cd54ec0b06a24ab4ae94fd5897ed4c4cdc5d1d3dc0b826c0b6ca7c81d0d98b62d261143a762da94b02604b811e3e7c53f79396d23a5b19f8794ad7850f1da3c9462a0716a77fb87fbe85d34c610c65e5a93921bce8160c2049a123f1e6f9357851a2900dec44340ab20ced52a80cd6e5ed17b9dc19471e2ae29c6cf98c7bec7d33c1cc38f39128f019367465a12455f773fd0ad443e0230482f9c17e3187b3ee26ac397edcddeee325f4dc38f7003776bd3f29645ce016d755ce4eaf4730ba5bea43cace5fc4f491b75bf4f40a1ef5d6880f57006f2e0ff39ff9153f842dbbe9239d0c5f12fa491325697746afbeb24c3fe8248ce5170bfa24f3eea9fc6bd7569cf327cc1dabca0c6d9334e669a0bc6fd382665a7bc60d5921f4c5b37845e303ad5f87cc337055eead375da43220395f7f258e2d617a028900a614996a6cc5e37c72feb25276f20428853d95e38dacb125fe86cd90d52c69c68c578991bf0a52e63bb34af47081cf8a6bac755d8a48593965adc95aea177dfc2a3e04caaec905d3c695a91784eca3b126da5e9c866d91c683b6cb3912124fc0d5d6688f89d4ed73b54d5f3e34face890f3958f7f8cb41dece502429942f9901794c73a7c09cf36212a51fd0782cc06fe30ee79f82d5484f23733f6c13233f93f7016dad4e73fff5bca533ac101209ee573f1cce3ff1fee1c719056c5e2ae2322639c3928eed49f97f1f1b48e503958593f036366eb21e23ce36b7214a1f59eae5e142031921a7723ce84d0724174f2ff35cadc02308f5fd66a0d59fab2fc15cd83713002d3fd0a2476d38b7851818a1b01b49ea13b0759ba2508e928807cb3903616113ef1e11f6ca8ab55043807f94c991add5aab254af2f42fc3ddd0cec41674123652d4628b6d3fcb5b051dd82eda1c68d133c33ba1bbd22a595c714a3ec3875595951d46b252c14e88841e72a69079ea1e1a0ac9cdb4d41e799c61a6d52ca385bcb12df3bc6bd2573d385559eb994f25c61406fa247a311066b9b2d6da3eaf9a50dc2d39ca4ea36890d8716745651c84cdece99eb27167760ee320fa5003bb3d07f7bacdd3251bb18c32693721a6558f007fb7831c0a7f1d8f0a7230909ae06b2c389fe18276a448db3414e3bbcaffa39a00de317b42e1d1be8fb0b3426ed4de416d966414a95a64f0006ccc9b753425580ec9c2c84f45dc83ad90471d2c0fd4fe6d06a3a279e72b673320bba822138c7b151d727b6abd414a88111fe52b8b0917acb88c22681ba264f140ab7b2cf227252babd5e83e3fee517361317646fad64650f2d6fc29e552093d4791db40cb5752ea44f8f1f42cb550c90", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000154d2d8b9b5e779fd4a14022ac64137900000000000000000000000000000000fcc98eb31a0ea5ce49a4df5434c3e52d01cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x00000000000000000000000000000000000000000000000dfa79688268e94f45000000000000000000000000000000000000000000000003b27d8c4969fe006f000000000000000000000000000000000000000000000008596197ae31284d9a00000000000000000000000000000000000000000000000000025071b1b78e460000000000000000000000000000000000000000000000049f29ba73988453810000000000000000000000000000000000000000000000037166cb36624c1ddc000000000000000000000000000000000000000000000007d050eee5ee699fe8000000000000000000000000000000000000000000000000000258bb7095c7a3000000000000000000000000000000000000000000000005704f0d79a04cd41f000000000000000000000000000000000000000000000001cc8608e7634392ef00000000000000000000000000000000000000000000000af88bb11f4b13a3c200000000000000000000000000000000000000000000000000029db74f7fe7cd00000000000000000000000000000000000000000000000f4a5900047ae9ba6400000000000000000000000000000000000000000000000a2f7b686593ac5c9b000000000000000000000000000000000000000000000006e5e184d039f43481000000000000000000000000000000000000000000000000000265f972d900ce28738b09735682ead7a4c64799135201b3899d799236623c3b56cde6eeffc14c0df0717f0a39df3d76c516e8bf543821b9f4ba63b14188d420b92f5ed32350042061c4c31ac25d95dc3913a7a4720dcbd0d2b502eea4245f0e8e0b648eb3664f0ffcf0587456eae3287e653a00e8edaaebecd3e65d8ec98782517ef25ca5f5ed17e994084f3bba80177c052107cd4673933d4d7d498712c3c1a765d4ddd106cf23121b833a7165f5c9a7cbae738a5033ca28022a37b8399326468eccd785a1650e921736cbaea5f086a1fc0b8d34c9b3cf259d4260e3a0fb734e9ba562371b5d22be915dc76a01693899c6a34254a2ee1d2034d4b4afb734de2afae62fe3949727c3b05c1e3ca04b6abb43df9172b580ce32a1ea52c6a13bdabefe41b291b61b0acf905985d7fe37c2a12230fc1fe3c698d1495d937e0315f8ebc4f08b81f07b054fd658b021f59f1486e8105180cf9ea58448782cea621e76ef6e2b502df53c097f54506c667839a3fd6816ce0201339146d1334deb91f8ea6a14405ad6153a182d352d88c84c0f0d3446535350ae13ab7965e65605009887d7909a2fa1d42e2896551a0829706edd47b1b5786e28188506120cab5c15268750c76c2282e001032b6979a88d90b20a1db9d030107ffc0677ebcb6777c7f4ca2ee0510182362e2657252207babe037b83a8d8b3dc52cc67e771540a593d889433e9cb38eec2d71d52810b29a729959f506e4ee054bd6382f9acfcfa7c288777d3a5f4797e9342229726d66b20423a7f33a255cc294690bea014d9afb0331f91401058a271c34d2ab341191877abb7207f8754970389d7dfcfa00ec4ffaaeb0524dd716cfe7cb11e9223897133507d5227ce3ce11671f837ab1ef85d0aa6e308de0ecae5e49f712bf468ea81ea81e5527ee9e0f1a6128b1d1d32c56b82e3da07269e74c46c9978078a4fc2c3ec815158339b79250151302bb388375cdeada8e2669ddf041f19621eabb305f4d3270b826fa6ed2f25db9ffe39fc47e81839abfbecd91abc210133053d60869e919a115fda8d5046e59bfb2f874261d17b698de9e524a7145be6c521e3e2c29389e805e67940c921c0dd97b619b10e3d3e1822e3b2906d448ec85a031c38b5d1f34c2d4b5b7d5b6360c16fc3f097a28fc83c272b1fce5a6398bbe20a3997e59fc3a0e2317de98fdb917166780523993068ec1cdcd514df1fe9083f1991ee90b63c34c18b2639cc3c3df351ba4b8ee53f5663219c845085116bb81c1639cac4dda73a16cc4d7f9e8e607200c85a8e0fad44becf3891ffe93aa770132a39310b85cbbff14f0bce323cc23f445f6a258707355b3779232a642eff4b1a09e4b99928bfd02b699bf248fd01e881d98aef5772a6043cd5619e1a61af8b3303c7e35dd434b2854019587951ffb7e885f616ef360789979c7851ff5bb479421f42bb812a236b6fca6f7ded6823a28d36810b5eac6732fc2ad4410f715951cb2b5e09eda26e7793afd5c7c865de1570c4e72d9a88ce4b31a88264717a070d802a817a31b2c84d4c1f13a918c3a73c88d8a67f6c81fcf41ecd278894e88485c5062a3ef24ec877338c2cbafd1e61205fa95e2b93c59326cbf8f6468453a1e85e054db79eff791b9a1cb823dd9566f06129dce2b1f1c15e9c6d878629d40f61512eced624da055ff54fd71cf3520e5a759b33d31ce3d711c6e1163e3645a725960c4b1b6a5254cb9b135176e02075b45e9e1ea54e21b1a420b1414abc132d02be133cc2dfd9cdbf2a0a44fd11b51779483b4052c658b345b31ba93b918522167102cd4824015fe51e5b84a6fb335ac4842f9ed11d6f5e69b4d8dd8ba64e0f36270f78b42c71621f1501e1ba49fa4e661de16cb8dd3c698093969b8964809106412790264f0ae9b8007b76649a949b69ac1d6ec2a955902ececb7d33336b485a7c2c3c6a1fadf0fd121f79fface6cdf947dae0f776d069829fa900f514ece4141616bbbedf7de53e30cdfab6d5619028b321d44e93184fdf01e7ab3f5a4424386c23b8e5447413e6639c215e2f7bacbbe08458fe90fcff82f4dc73b1b9fa944e91108a5c28ea9df2a9e1b17874a186d1d5301848073af15ed52a4836a71b9576fb284357773419bdaa332b8130670664dea49b96012f02b091e273421d579b8b7c0db2b2715658dfaf910c32aab84a6eb1daa3f8c4c72cd2b43abc44cdb4aa8a0b0dc67c649a6c3b9cdcbea178401c0abcb272d8d5240a492956cdb2e4a3de78841ec42a7c9a5ab3b10d4264cf887b1c81451ebfb5e4277942e5736a7049bc071c168f11d6111ca6731a245c4f6f0aa9acba6d334d552193f8453f06e454c0f1b30e3264e919b2c667507684454b0045f26da17f27caf045c7b4d2f6be92bfa160277b4cf55287f67b9560a68e53fde3b7b85ee9043123cd7039658e7f874c136d1a2c26f2deaf00a930cd611ae4ca28ec30d65792e9493c2175e557b21e31a5102a8f42f94c87e31eb528d7a5fd4ca83ac3a1b62ed65113bf25e5e0a82dd2cae52cba89c170eaf2134fd4b818630ab330f68c040bca7285e619b2d493d713fbfe079f72b0b80fe7f56ea6eb5b20bf7990fa72a7f26191f159a2eb29d01801330d26bad38bdbd921c0e68d44a56c432329d5477f1e52f1d720e9dfb09f24fcc2f610e4303caced66f68ebcb8d876ec6f7c1904ab11800505a020eb41124069deb92d748417971506e406a675a65eebf63796897742b6080c037c097e742a446ed528783529668433efa5bade2e3f9052c11418f54b8bac5ae047f5fd30ba9e031f2bf38e36125c725b69365847ab2eaffb80c917ac7d3a97a47c1a1a9950db209d0326fe9bb980be5ef2bf21a3e85f236f53aa3843581e6e49d3b4451db82ff50218cdf7411b81f746a1086be6f27cffbf9f2352b4fca25777ae421cb673afe74d276522587e772895b53893a247b5bfb427dacfe13c2992700becb2313d3dc00f1876d430708da7298c907dff0b5984eafba5b26cd6fdfd59363b0bc511769da02db19b422e86e8e4003ad6554001fd10fd87d5f32af8a9d13f23bbe12525038a1a7a3867ea9741941c29e3aaa8491855617b16f140ded2b19a0b03cc4a4cd74c20f4111cdff9aaca53255e23c1c6412be63bccc4930ef87a63fb8e59411f56d4214b5fdb1a07fb3d5e91b25b3df5df862ad9cd72d3a24325c122801358aa149d2c03a7e24a687a2135762b2c1108cf1432e73f5ebb8e20a552ad92a91dbb185f11f9ede94d1ecebb9b30bc4c1a7744e2d121a63bb8fb54c2a83dcb8bb61cd6b600571edfb0c5ae31403c4fdebd9bfba727fe0422077b4bc1b660ad5ec5db84b7279d5845420a92bc08b2e5698b81383cc1490904ea5f20406d04b670ac20b49919e8beffaf81535d507d4ca8b08778bb8b791d016fb0c935e0ad7f1c6146cc4a262ce42077ad7d82e2e3848618c2e9514a3422581329e323cfb5e6153a6131990a4326ca63611c14ab69058cdfe7289463baabde69bfb64c895637ad70f47acb101d099896c7ba7b7aaeb2292c376efdfe9278aeabc23363ec0d2c0fad2cf14325801e832e9d2edeffbe63a053f8d6ac11a13118adbe3cb26f66bdc367bdf2a11bf5271e7b3828c52104b02b5f157695823763e7093e3e9e680921cd0c3dce450f4251845969a5c04cfa21f68c982ad2303130bdb22134d6210746c76817e25a249c64fff8b9d2e3651db73d13eea4360f99ebea5b17fd312fee8b1117e4967004fcf53c8939497fd46ee37dc67a58480fa1d8f2cba62cd1a04755796bdb1dbd0625d390458c05bc57aa6cec0ecf86e5b90d7d7484ce043ba332d42367e4b5310c45f065d59ed77cdf230d709944f6dab4c7782c82a2ce64161caa970c2c071c11e59f3300de1c8e44688c10c9214d8ee5d1402f31710c6c033adcef7c521b550cfbf8b66cb58eaf5b4f600593e7a7337c5b37df382e5df9bb7da69acd0935db2f71443d93c858a05143b20f06298edcc053d6f52755f8791f02c80856e1f55b0eab451543b5001697d60235605a12ca6377956f5161eabb574ea08f2741d0501dc63400f0e57099a68241c1d29056336dd70b0ed371da2615573480f10f933212ec6e0b1f25cbfe5ab5958a232a3c6555bc5d9a88ca2a5433eeb71500a178dc1003e570f070517a79e4ad9d72892bf563aebc21f65a15617fba60ab62749c622bb9822e4621ceeae8fc692ec1384c31b3fa7f85ea545106bbd4f0c8b5b03a6a0f64ca4fc38aaf821910592887b951fe39a88fed773f3c32e9d3afc97ee079d406d44e6772130b54178d8e84cf33390e8b880c554746bf355472185cbff9ba4c218facbf53bb5db501c6c583ac7df90901c1a4c7bfad4dc5a1f811986294c3e30b018f42f8d486433a79cebc05ab5a4861a0a19f413346be6108d93bbb7ab24d0f1e374662c55fbd4000b52009915ae41ace2fd2798513822da5c47b69c627522fa60c93b96255feeb8fead0646f6c03c55bdf0ce8c90d8a4ced28275e6005101dff152a15dedc7a5077b8a6a24fb6ba47511dcf942b097d341439041c53dd82030dff114cb5c1acff5f422b9f9157306540025097b5ca9666848657a1bdd8ac035532d8747b5182f12d2d76caa80b6ef5f2c23ed91c230c03f3c38b667fe41f03c5292df7fcc0485405a81a40666c864a1c0bd0f9797d7f00211a2a4d7073be1a8cc3df21420e1ac68e87a2eb75b32473cae6554a8c5118662b250f3c8e00ae2e2973ca4bf0d376bfeeaabf8f7b5c4eb4761a0b51fc018d7a205f02979d60c2027172019511cbaf761d504bfb9dfc8e01e45cd38b4e10103c13b2146174d8fb073f021f5d8fb8369b690f7a5df4044a2acc9e308e6165e6f7f5ea5d0d5f695a201986328ac16e02384a0f49de5929baa1956458ee19a901e8fbe31ecfa803bd2954fe80592a60132ae23e7c8d4d79dca4d60db2dbfe343b01cbab95a814deef0d2f09686caba0a5e8714860f809bff73ea57ed27ff26c921247633ff0f53d010898444c0522085bf95583c6bc1ea11245794f6442c79c3467afd6de0e938b9905fee8793edd702dfee158d66580b78e7124a0aea44968f9ded2dd0500d8263218735ffa49cc44890afd2777727aa4f9895a7039122d6e333b3520dde3f6096c29ec16859e98a504c88d771cc47aec7ff40a9e4f201ca83dfc7574e01261a9ea214114b3db1469a1cf55002bbd38d0ea429b1e24004d7677a57f16b4b0cbc25803108c1d04f5c0c3ef669689f62188b71909f1c11d95d514ca3a78edc8a7976422a8c196a42444ac4076ff06e369f839e38244111f04d4f74cc3ca2d635bc3001c60ee36b09471a5ed2d5db3a067183c50ddd40f2d74469507e949541d101d7e16c41645a3fc37a00b4708cb588611fe15d807d9861ea110e6883f4df31cc8ec278d447019f5d65f772020851c65d6a10ea601c7c4bcf78e58fc905b2c664f0d2c457a65f6424d6739ccaefc361f1e700d1735c61328bc29e75cb0d76b1c013910c82a1c8b34214adff97e9d94943f069509a46742bcd835da11ebb9b440dd3624c70366a5708a3a10b9688401e4250bf48266aa77d0ec6e5403d5db41f718f30f947098f457716160288bdb4721cea1adc69d2745d7aefc57eafbd2e901013b2ccf7ff182845d214e0fbe248a08364934d0ff522a9fd3005df960972ab255052930025839cab3bfbb81d5127ddf6fa01a61a613a6e8f128a85004743292f3cd2c1792d9ba72f886ed7a121297461743136ce348913ca557297019e2a3aec0a51922836c33d87bb152b06b9b9f3bbd144c703eb94cdf020568660d841a255e5d276bde9c1bf661ff908ef89874dde149fc4dceb010e3a6a382e292716cb4b8d916263774fee4ab4ba83a3ebf0b1064ebd75b9ae887644471f86a6bf8221107cc078182f2a821d52e0cfec07bc6657e50526ef9fd0048ec552d0808a742742aa625f90291e0ca4e5a4bd0c2588f4f2072da4cbc09e48791ccc16cea22ca882cd00d0381fb3133577f3d82a0a0bdcb0db0e88fe939063377ee25026eaabd38be1902111b320f710bb3503857b61c13fed36475db6ed1080063626c619d22f9b0932aa34375d85de0439ab7c194f504bc2523abd9627a8eb0c3ddfd751df66f5c95293d54b5e457bb7a9f8362a8f0e2a8acc4586c5f9109067fce81c82242a2eb5f2558d88d1bb575b0651061ce1f9bc8c75a95c5f785ef7933948632c1cb5b43592f88a62189b6ddad9274e56b188dcf13db455a4801c175f2bf802dd46ce5aae405421fc32aa47ef04f6c1ae84584ad7f02e065e6a7cfe8d1854a0f5431c7dadb0437a9c689c31a4cea1d48dc8bc6b635382ab3f7dcab912b6169fa9550900047047ef7b1b8393d511c82c6551d90fecf2662f336597b24113124e3d13b21b27b05f81ed1c2b356defe173c7cb7e4a5d33e4dda4cf7020e7fb85bf2893696a1a3238660d480aafbb0c4271397203e1a80122314f7b9ad807960830b0cacf78e1b1d45f76d3dce3e1db3071e9055eddca41a84da428fd19241adbc656e5cf7255b0cf5ad55a164b6805f639c3f7202975845aaa64ec03b57fa721daf278099b20c04630ee5a1d2cf8aa318da7097dfc780e843e1e2b8d7a550db5cad5d2ad4cfbf2e115682cc648ca983330ffb7283318b6311873be9977637d3acc6b2eb932bd00f387b1c9aed7bf928c15e9af4b7c165e4b59cc38d8e190b8b3a81d1c6d9f83725e0695fb46317bebde0aebedd01b2ed491ce402d501aefc8f4c1c293705be6b000354686877258d5ec4bec33fe416c64ce5925bc9d9a537b439250967bf458702d23d65decbeb6752b51e297d25b2432a6f5b64c0773b205c4e1446b087a44324cce538d0ae9959129c92b2240f863c773941aa0d1c503d20b91f430a467b4f2bbe0a36f9facb42624703f09f4288e1c7b74a9cbc5b2bdaf212a49f219b618c2580560061039f9eb6c52b7688ff624844a0a4acda0f1db128eef1bd0259ac3c176b1db37e377cb9d25a85c75ba3643e894de5bd3e998a4160837cd75f1cdc723038cec04a4853a6ba6c62cd4918f1632ac30de6ed5c0f8b0a68398ee741e60f184ea704315635056068c59f3feb2e28bba86ca05e77abe071bf4d179a0e9e2d0d2bcc51f8249afca59bf04cfb916937a0183917178db300a3d477e6c83f018f0db99b774c888c7019932c91882095d48b2c84a5004642e7ab200a22e90de77515c4e35e9d6ec6d71aba421ab36b30e7328fa15dd936f1c740838181ff349e122ffeb6ee1157415c7f104ac0f38ed2cfad62acb25a58a89eea6a5f66b4bf392520de5b7a89e66f24a828b7141b01fa5c1067327831d040d0e7a07f565528f2ef250121b6f9948bb2f3f2cb4099853f11073ea5173ed10bf2cd22f90108fc7c4c231b03d6f484e3bf256fd3d4f82388d90090764a9b6eaf6cb2cc2806c5fff469294e11a6fafce22457c6817315bc429ad19853b7957af2af6c3389e10c5a105a2e8d982fe73afb4addc3aa9b344fabc343c6438c8a59098f5936add0947217cc2ab165ac1555e0264108944578a3738595d56b7e274644af28a52c992a33209c05dfa9b8864f3155f77a4b18eb1eb793f4721180a337c7fb5a0b499ea96afd5d00044ba5da625f9a36bc38107cbedf12e0a51a8a2f59f2096e0d20e065018da92bc0a7112a7e48a4444a728d936338120a122db00a096cb486e3b67164120ed11205e12b3245fff17ccc63ec28b2e2a1f3d2bc7c8e7403e0c9c5d5a1514a2b900797eb3f3ea1748dfe9e23cad65e839d8e3074d0fd24a1bec13252b57f821f9d068c1cc3c26382875bc0d27da9d3bed3fdfed96ce2b7ddd3446e35d91b31d301164e3aaaaa7534f8ad4eb17b0621e6e5c876d84fa1937a023d2c29e37bb14c9202c71cc7c96f699ecaacae99aa36e15dd8055793bc6276958cbcad551d2448d1199499f2720dc367e892cbec806b3c9459f49f22dfa2b990fc8282a4ce499ba20799ba426355a8ef8dd0fd899bcfc06372d212c6ac1f63c8bd5e6914b18cbcb61704504f2e1e2ed6d86e2da36bbac86aeca425a6720cb4facfb7fd429b85ad57032d86246b281117624b1b92e7da1c0d27bbbff6a32918fc2f5945cd0b4a0ae312ce4799fc5d07c7180005bab2956d23025adfa9e3e37b7a6166084eaec7740518fa072d7ef56b4c1f8aa21142872240e2dbebec78db08384dac3cf7c36628851830f724379349291aedfc0f9cf05c92d4af5be0a50f586929489f98bc520eb906de5f1f41ebe26c58492fcf53552fa0d1df87ab01ecf27efd93652d85bf06180c0bd83850c4ddf904a68bb0f3a24d6703fbb5e7e3e3d58561e1f21783729acc1c3d2d9b96213fba91b08d5a06eca9adb713d1636c1c20ee1c6ff40a786694870e24656dcaccb56da1ff15761c4128774105f4869db44cd54a07414e76bae9ab2bd6f76e121840d8e8f87364deaa979ff118965f2ccb1ef97b3e45187af7eee1034b6d88504c0eb3f27f35f0f36cced25475b2f62df8cceedd3f4a9c7808fe41219bca2c439d0b29739bea7622a1d7c44d8a1de6d59c63b7ab55283f5df05cb70692e71c660ed45d49f1fb0790bdf274bb6cfadb9fe7d0c436f8f784478cc8df189e594ea67b7432713bf22c1199d90e0043695858aa46cccc397e528741c6b52e19c9ff45986020241cec343c8d8f74989c21f2b86e94a704335942c967bc9c032ae6b105131dbd3e550889cd96d3577f80542ab10e53487ebad39ca428ecbb236b4ac29357495fdbcd24b194b37aef7798539cb7407fc2d16cc02647616fda1bcd71bbaf8633ca0fcd9bb267a051933c14313c8a09c665da7ab26b142e9d811d717384396cecc500f378df1db9683fda74d5bc2c4e78fa1a89f7cf4975ad69066156264ceee9b8b8926b5a7105c90889998882ac9420f532112a6f1f2f2b9c023a3ed64af572f19d017aa3ef6afaab087d3bd768155bd3df0fc9f7f822ca88009d54f9ab76967e873ce437bd0f7e3195cb48599df46f54ef9d2e424a93f59213e02a423870816fe39839111cf60a4d2ec4acd8e43863cecead8f660361b8a903bb39e0d785dcdfdbe89b85c713b3355eee749fb6027ef71e9984d38806510f18a4b230924b0b848a291f8e62922077c1034005c5ee611f550a746bba75289e04ea00ca60efb7b8587281ce5223eaaf8a07771df77a7dfee0d61b4d1e01602517cdae4f6c4ad7205b5c2d781c36bb7f9c36d3a1db2c4eec78e001dc931594700617d097a075cf9b4e688aff67aeef2b5befdca4f21e388b8c655c2b55c24e511251c2d400d40a3946224c052f1ce75a4442306569e701bcc086439a62f9e04c0758fd7bda12e1ce40b209d30d67f2f003fea544afaa1d0420f6608116269508081983a5d8a574b216c19749a1472029898399220ee4a6506f95e7c5787f5b9925e200ee2c2cf08cf40b446659aa9b72d867e89de012b1336c390f8884c2087e09f19183af33cab63051be64938e007258d7bc38158dc0e80b4f2b76d13813ba2926c0c2da99395ffbf9e8cd65c95721cffa67de1b3aacb1e40f9a75ae46d9a11e06d148dfe7adc51debf3a5ed2958ac53f09e30b368648762c53d629ea154aa10cfdd10faa8de5faa738ed3d7bded2740c0187ffd6fb2f51ae27931572d853d27b75276de25a8deb23b075f1581f71d8b45f9525f54c924a3057476123f900f1f8fc748b50883c13789486e4a0750c4c2b32d747542761d6b41b5afdb17086b17b7f3f2125e5df91bd8ff04ec54b3b732214ab81bf6e21560db4780f1f0ce7329d6540a217743efe23cd1c6694b52623718e1478973ba5f76afd37d376b8930082c5adf2d53ad44dd629106b3c3b3ef24829f1570d6fe0e6bc673a33d3f233406fbe881c13361f5495c94f2d43887657f474a5ca6ebdad16ee92ebf3755c619252bb90cee7c61ef1b3729012c54101ff03af47b6444e81ace561d759d309b050a82f6e71cfd7854a68ef7452aa7e48f7c5ae0d48b5f2d54902687a8f0bb78852bbcb6b17cc2f8cac0ec1526e2a60015d3efd9a0426d82aeee4260b71813bde80392ede8d86bf0d16af0977963ce5a573f7d561fcd20ab05e2db8ea7ccb334ab0307670beb2978093fdf7001199dd0d72b0b7752ecfb91da30847c8243072a7c2f199d811b6a6c1635c6ec835b05fd77235cb3d8451194f699293e70133d756f2c9881849ce8eae584597a83c53a47450a578d88f75f6982d17ddea04e9b9ed5095939009049cfc7cd6cf9f412a3cde99720adb91ee077da2aeafbc7a033044a0b672f1ad786fbd543528c38703fb4a37c1db9a2674feabf00ff33a5ceb01bab1aa064e846cd5d748df1714875b6a62c9dfe562519ab164540c0d7530589c288064e7b496b7184387d1d4686c18f22110364c55e53f3a70d016832be570218a012f6b252d06f73d254937d9062a293346adc20823419633ffed17822bc3259dc061a1a8863d10da15f11186f71f04b20e0d152385bc108ff740a68bf824f890f05b422bc8f53d3255aff1e028a79f6da0e0e24e931497d03ca96b04e687a6f6300ef2bf5af84353c1e1a3aec79fe45be15ff7c3791aa7dbb919da84d1106515f2d5ecbc9c4928ef292fc4de434d4b92ac996d21ebf2d7d0b9d99a295622cf2ed2ef5cbf883993dd846bae8f4fdbae671e7338c6e1743fb26ecfd0e40e409c60b03445ba5b1bad93b6d8fd88b0dfb82ea5a9a01db23cf5f74ec68b4ea42a309c624208abd552ce7d9b316865cbfad7d0c232c8f3589cc58f93168ad5fef7c3843002e357ed1e5708133f433b4e0976a4b1d8f75d99556678833e17699e399f9970d0053375c14a0ab5cd8135fe265e537ae1d98e32f64258458b7ca404128db1e2c187a91f43be9f07bbdf161b002391f1b31d31a2c8f62d7dab2f8d7c4e618a71a943d0f30fbce765f0a81a0b86f000a9edca80be34d9fb22fd5959a52dff8b214d45dd690880833365f782eaa2bbdba3cc6758d345dc129399b5ad29e5d8b1f25d06a5eade8a0ef42d3759d662b6c3b27d9ca7f5804c2b3e92f1cbf128efb05252410af5cc57c287a8f8c6c3e7e83ac898b59003c3c39da20e238659b09f1df157216dafe77fb079590574e4b4123bf304f18960ac407b2405de4825cc00c35061d04805f2451cfe3e026bb617a8e284ae1bb816526a9c506a33186bdfacbd016102199b0ef23d253849057f884b691e22de105d33dd87777305d5ea3c564ff00af3dfa4db7f4b2aa305bda33a2476a6b05cce69d61d9f75f986dfc4527af3f19102cb5b69b2842d6d7e9e90e8d575adbc16ad8a3003209e911b2a11d4e200225ec90847799d4821848766737493f708ece7a692e4885692d011ceade273fc4041c5d41854376102a27aceaa0aaa4c3257eb59982599d817fb2605623931306009cb304bbce9135174946a02bb039b7995536a8c4dbb218259ddb2c1c5b9dd411f093e4e518d590f06f8c106fa0f4df4b40f10db27b63d53eb4decb2481d15a1b1f9a4dcf66168ae5f6d7c79354ec366e79b5875482aed5d9a686fdb62b17630dc56a8b87bc1dcb6329bf2d19a56110dcc82f939acb1a6e70e994bb94ce7ff31a3b1db0cf696f5c842eabdc0a6e9d5372ecb94426fc8faef668ab3d9576705e304a945cf241ce3038a11c6f5622a3422ca25d6d0040b99e5cc4e4b39843b7d72534843b4222447511a5174d7c860e10896b36b505a0e16e37cdf8ac5691664e1e5ea49b6f6f35384f56d9faa8b6aa800fd2444e741c31814b3bb2c8c6cdf828187c46187808eac8eed732b528bd82ac8f43e6db3dcd19ed12205172f578245a1589aff49acc083e14f1caa589eabd4d490f6940acbabb055c05319dcded45bf21f0d43708de5ca2383859008ca641deed357917f5ec7ba58101d82a5cc8a6f524f19284b599173697a880f26ba158d39dde2cf107c3dec0a9723aa76b6bad28134544a94eff974c94c041ba956834fb7b8a20cb3f9594b54e896704c00828401eb747c909f8e2fd22eab075a9078c84b13b6bbac635ae9abaadb151c6e827b606742f6ff2939ada26729759830191df4708924fea7ebfebcff8f927d22df05724b550de94617b485e6049826fe78c7dcef0a4b2d23fcfd60d744b89dd19180f1b06d00b6b4c79b71bd446267e49ceaf11951b2d9082b09d0e3489fe070a744a0e64fa7d24d7703e2e82a7f26381ca659b1f66255b7efd0df075b6ba4de7a085030738dcb43875953999c2f7d5392df24b864833ccc9bca50062a633f8e7b8150bb74b7a7c537b12c72b109f8c212bdc542940fcbe580040f301baf107ae48ae17da9f15e0d3439be0bd2a45788973bf8745d03428478327de779fb78c055f53138a8e8df3e15405872ddae7b1ce4515871a20b0787503e861c20abb64c517e0042a2680e7a04527301326182626e560ba34cbcd567a6fbb797077ae12ee618312c49cbfef179c6440930137664d6cc87902a61de833c0aed0a616790b8a9ff71c492e8944bc3f60fe2de37788fef01f939351f4b1fc27098590ebadd75746f60b012641c5ef934924a686fb002c329d97277967b2da3706e342c7418340cef72d0392b4fae0d4ac069893efedb6739f3bf6c4ccc98c201a908dd8f2c46ec94720c00cce419319dfc91c3b842b3e8591e284dca480d39268d31117cdd8efc4e520f283358f61913978ac3430c515e7517268d57226dfab2e09755fd0fbb8654723c336ff3932cd6cd57377fc84ad99e65e92e3e62a10cd8cb84d1eabe63874b70602ec92c43565c29787cfbf0eb1066ce59023d4ae6b066217bdc050dfad86e31c4e357566f631a0ec0d3fa61c328756f7e8e9d5b3b7c8f86cbc08daabc2e8f31bf8eb84ff8a4a6db3643f1495b21c73cad93e3e2fd220aaf09a29cf4c9c5864053a1331b441aa1c585625e656e3e58df740cf3d457c40b0429e10e67b7544310643409645badd52389b0023568f354686018d5cc617ca0be53ea64144ab52b5109a28e74e789766193593c8bad70d10b403c06dfd7ca3b97079f9a4974f563a19019c8c895f9372f50226c3b0976ba2dcba703af39b1c50c7a46b720ba7d6491ee9143c7b653930901b4617eac146fee04757443f36c7156af58c8bb33670bf2928276bea49e7849cc27cafdf183902bfbed16820c68c2238e877290b90a118068947b5a8977360e5a952e0036257dee5e0443433bc39b931f3a78a42a124210a51c58cfd80c56b67a52a3238b4a47524f9e269369e91ec03c1b7ec246c04420d0035d930d1a4936dae183715bee51356873764905f7e7ca33c3f31a56996e22caba046602b6b24130d134eccc1cfaa4eeb4a5eba6fa0cbc022057243d8d478035a50d38b9c15b98af87ef36ccfcb1a41265398b117976d06a7b604b0945f6724c34befafe430b6e687635eb05e98b27b9e57fc05ceac658bbf43be2b055ceb12385938b72f3e4e0ec92cbec13b6b816fa60b5404b418c33fe661142a616b6e24e8f9c3bb35d37e457d1dac5b9c74f471beaf7f8f4d26f608f520ccf0e113801460fb5f0d9b7c6ec1f81756e4486d34752010a0dfd19ff23b9bab9cdcfb1df1251615cb508e4922ecfab788decf45b7e877c73e46391f8e7368dbc5f351ebce144bd31203ce49b741425627c097ec248008f704ab6321f4c59e6e6c1adcf21d255b9c0abc5e5d1e913335b2f5c18fed758628396b06073450bdc8e51b9ac17a15988467eb7f3ec60395c779f28a2a1d9998f24515ca5b47eb5cc29f875c9d1a1165b3fa07fd3b99b18269fa72901035818a9bd02031ad308ff76ede118c97350024db3130d1c0b83bc7b65c3bb1f886c3996c4cd8939139aeb8e749c34ff131274570245f1e762ee28f9876c9e240e56dbafeda6fe7047f8f56148b0440f23f0d011a69b14d6dc35825881a3eba8c695d89526348a2e783135364552adcb2de2bd4bb42f48491930f07c20e89c9f87dc463d93e72a7162f518cab3474af3cd60888cd45bad31620195330ad1fa68c7b800b83761b351b440ee1542ebddff7fc14b018c913564c935e83398d0e28e5cbfd160707ca15b6e4efde70def302103524f2d9162a684c86e6dd17b74948e34b7523839b5557457dae8b84d9679010b82b4f9b6ccffc1777904eda4e4583e195476f62b6b2fc96fa400281d935327130216f637efce4bd7967925161f603ef63854cd24c1637de8a8fb2fcc7031bfba6108270c3dcf4e240a99c15115a88522efafbcb2905c304d35cc0db64799797a10e49bd25c35c762064bf13db5ed47baaf7dd7c0f4a4e768926eb2b5f9f2b7e9b0780258805e32665ef178dd88e89fbeb7c2dbdc7f4c7672d6cfa8a46075a304407aba54c51707cf93558bf6f2bc15e138ce94d38ef015c8abdb37dc627159b601389b40547fdd18c07caa2e8c156ac50719ee2e1e57d111bd79ae6d03a1297a40c36bfcb7110608d12d503545f26013b67f6883ade09afc1a9ec2287c5e0b4fc18961675c195b7025239a4f728b6354262a3b333a9f49a39f5f740652ce6fb271e03230bc5559de174f64dd0d90f02b5f2a2eaf004278215a9d856042f48cb3e2028a458428b511f716a00e9432a28ba15a6eb8cfd6a26c33641dcf788c1f6a71cfdd7f6043c8cf84ada544f719aad323ec1d2ee9f5a4888114e008cf4c467cd2b509709b141b7d822abeb2f1122d0735485f29454e0077ef21016ddcd68fbe9", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7001cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } From b7914db2d2cfa59cca7add6a7c2b0a849cb006ff Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 10:57:33 +0200 Subject: [PATCH 20/20] fmt --- .../results_insecure/crisp_verify_gas.json | 48 +- .../results_insecure/integration_summary.json | 8 +- crates/utils/src/committee_hash.rs | 5 +- .../bfv/honk/DecryptionAggregatorVerifier.sol | 1635 ++++++++++++----- .../bfv/honk/DkgAggregatorVerifier.sol | 1635 ++++++++++++----- 5 files changed, 2398 insertions(+), 933 deletions(-) diff --git a/circuits/benchmarks/results_insecure/crisp_verify_gas.json b/circuits/benchmarks/results_insecure/crisp_verify_gas.json index eed2eac81..ee6f0369a 100644 --- a/circuits/benchmarks/results_insecure/crisp_verify_gas.json +++ b/circuits/benchmarks/results_insecure/crisp_verify_gas.json @@ -27,7 +27,53 @@ "total": 187152 } }, - "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.071508500},{"label":"Committee Setup Completed","seconds":20.228290750},{"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.211371500}],"folded_artifacts":{"dkg_aggregator":{"proof_hex":"0x000000000000000000000000000000000000000000000001104c69c76ea8f5f6000000000000000000000000000000000000000000000005c85075d6baa615c900000000000000000000000000000000000000000000000eb6de0b1c015f0f2a0000000000000000000000000000000000000000000000000002a0b5c2857c800000000000000000000000000000000000000000000000036176084b0205127a000000000000000000000000000000000000000000000004549377f025fd0f40000000000000000000000000000000000000000000000003ff07c8a94fbdbbd8000000000000000000000000000000000000000000000000000185ecac0421330000000000000000000000000000000000000000000000044f1f17a53eba29b1000000000000000000000000000000000000000000000004096147c1d40da636000000000000000000000000000000000000000000000007c2eaade9aae157c100000000000000000000000000000000000000000000000000023ccf2dca92b000000000000000000000000000000000000000000000000e02d12f90edb8cac6000000000000000000000000000000000000000000000007c223713eba54496600000000000000000000000000000000000000000000000ec928a9ef7dcf2da9000000000000000000000000000000000000000000000000000282e9bb83bee61a05d33c39ba3812376a494cad4c46e07e18763a4c0cc37c7ac2f3635530c6110f00693d937276e524dafd5fffac373d40ca9ac38bc4003aa97fa25c28eaae471e6801e127832204ea5a325f05bb8550bd90338e17663499f9908e07c1a27a4d25db068d58486c5ed1d571b154613b2a444f9ef333d6490c341531f43898470028e20e0d46d87d6e6a8c672bd88dbdbae7c0e0a9d06da0323fdaf7ee61ceb7611d45afd3c8ec55431dab1b693939c2a7f313b832a28959c3ae3d30629655cc051ff1a5efe96161c60a46f240cb6ca1a531353aec9b48b52ad525bf6481f17e0923676a74fc00629f08348ca4b4467e32b7907ccf75e78538096134b580c00ad016d8f24d8b2251a384465ecf7da74301f64008beb9c8b71af609a0085afa8bb10891080d365806d9ed9f21901108254290c057a4b986687bebd3eae86d9d84d52ba59894f06a73d164ff99fcb0d13873279ce4f307eef63d2c0ba38c4624e95e009c113dba40ba429e2620e10806283dd5fad8df1b6a85051c1f7167194c70620f370484b9bd06cf6e6199ccd69d2c0e0db004474b4ffc62084f7697d21fc04f1518380c30df7edd6752046611f209863dd69ccbd4af39b35e05dbaa35d7aea81dac6609d0b9b649962dd23bd220709758af7743abf5395724f809100dfd19cb13ee772bc80efdcb985314bbe58feda8ac8cb79523fe9ff183ebf3e6835eff81084ebea45358443421fb0f2d31a11171c0537eaf216aa68a5bfefa0d02b1c5b92a10ac9238f294ee18007a6f18f72214951c398ce84c3616bcf3c79e0f2e2f022a0c90527061c47a42c43cace4fc879a5a326863a6f82d9c6bb420290aa900d8244a98b1668bcfcb6023d101fde9f2fa579bbd8947480f7058466d6000b38c5c1a4fd3aa5794aad136c957e92be80cca60120b6d839e54f1036f9e857e14746f2433e79c2a2df9f7ea53e60405fe812eb086dfac1f0b0c638610dbdd208a28f2260c558292be011ce1ad7ea6fc1a18428a3199764be0e5e4169e261b655ffddd06ca1104a26cac71683ee832dcd3cac2ca05e81de30bed5f8b67ce620c6f88f62d90a64a9659cd90458edbd62bb6cc9f8d942669ccb243386ee53c938746a2462ab0830a5ee749ccf6259eb0ddd308297e3093c1e2bf8454dd5e900345907a8608b9832d8e40d08cb3dbf14939398e8c032b1baf82f3b45da26fc2aa8a69639d1379060a52801809840916499ab8679e0af7fa2b5e549f58ddfc8c60090ad5d42fcf59ab9661b29f6aee13a20e279602925e6fffb2328ef1334585e819ea8002085571a91540444e60b8c9350331f5cdfe8ba889466f4b26343d3b179b436d1015fdbeb6b1586fec95a850abe17e3d23dbb519b068c9c59ed740833e71319db516be4631e64a10edbbdfac8daafe9004a1dfb5631249e558132163eb6429c715143679b05e2edc9c236a9a7ca760761c3a117bb03755bf8d04aefc7742ab0ba811710087242ad211047ff4f6b9c4f8faa4640f53721a400815c301a8b079d1ca2b7184bc65e7bc3cd2c380df2376ec233411c4d429fb12795cdaa8b932bff94628703868796573c773ba5a60dbe2dcfdba86fbf73e2cbabe306511dd6f71509607ebc04687500585f37d8135ebac5386803439c01b36e449d3b029c1576fc2f422160f4fa03d8f11c89e134103a7918939563aad5285049c66b3479d416852b11896f8b31c6d91580c0cc03bd04ef40a9b201d686cbfec6170ee5a1aace7c7f2248c548038c030a7c267fe72915cd397e273c42561e11410672a1e41f71827eb247b679c4eebf11bf064f256f418ef2364b7eaa29fa9cfbf018eef1952daebd915d423652908cbd25b28b3b07d28cc918af5ac73226b02d6340d02ee74f643db15c870a4de33c870580fb55142ed86a7b1efb02d4f5d1d6e074137126f61818d0dce8e49b8aff13f2e80fa7d585aff6147ee7265bc92df749635d9cd77bfb87508e4dd08b4f437bb59702e34ec8c390bf6e7455a4619bb3586688b16e1045a9c0c8d0b760cf3d11357e9840fd3e702cce25045c7835bb1b0ce6275af5571d18d1e90731123ccd8bb5b7550b8ba901f3e94958acb3e9583067cdaf68cd913726817755888db2a7a28e1a4748b9ede248fe0ef76aa51bf8e5eaca3049ac9dd7984241212bdbe5b3d0d4b586a7f468056893fbdde4c94203e8ff8238fa1a028ab2b059669f73b36030f5205fcc5958017fd26f9fa8d827b6cf11aee31fa922e8f0c21c5cccf059ac7d29e54a4df6f1e43b8a4b22689859e8569dc98990b9fd1d1b2222e19e76cbb31fff18fcf40798cf42482bc063795fa19dfe65eb6e9a570b4c41e85f9c94ff56701adabc0cdb76def2f5770270aa236e1be1c18ed64e51cdc911a1133e096a0272e0675c680fe24f2cb4f9a201401977945b920b02e60c84f3309e8ba8688c6e982bc9357327d438c5d34986a3ee5c88fc5886a4c66f1d14ef625d0aa01c82cf320cb1543887eea14b4481733f213beaef47ad92251eb2910bb1c23b4c930200102e6ec848b5470cc88a0204f6d4a9aaa1cb29e808e3cf2cc7622ea3e6a331b26e1d2a78f90b99066780df63afffc89bb080ef954828fc64a9b0e589c3d02f1f8f8c03919178dcd7138e1381f54749a684c6c7f852b0e85251427b6dad8334cd11f9f2e16ab2d7654d1dad887e5dce9d872887c6dc9d07f45481835c2ae47a1c0c9b680a009ce8218506f77c3b8b62654b8fb93043f60dca8992c4ca79d52dc8431a240cc5d01a027438fa19c91f638b7cddd6db1a1f6faedbe1c7eda2cda8637e7e0a2f49d2ad0109f7a5bbbadf6c8478ba78e716fab26944e182e437f00afe8873f1030439bfed6f6bb3abc39bd1882eb2b6eda0078c680e924d942cd5b55449783c3a8f192e24aaa6c305167959fe1cf58e9065a7784325c2d0fe06c3e064f99ebc1570a82cd43c33ea443971e0f434ac11883fe84461b9821746609c618a3362bf051334253a22ae38e9b6773a528204c0797297835f3f61549c8cd8ad16bb415ada181c52d843d558dea5cd548c3823ac4e3d6df1838e0073102582f62d7dc0ac66710e9d0e8826ef77755403b7f12a2863faac6dfe4cb1914e29db1b956406e9f46ba2653617ac8ede05dfb69035dca3ee5c2d00d3a612ec25b4fe71e06659ef23bb53a839b6ee48c4e38207109e68d3e073fef16eb062dcc3a22a15afb381581cd7e9db2c78f2b801ef7104a5ffb41dce92caeebf6141190682aae5ce74e6b6adc7c901eb6db76c548746b9170a1c81930149032b19c0c0ab947223f7d82d1f7fe5930eb75cb6dd0bf5394a906376146da02731747ba17ee40b5061140ddbffbf7b01d0e6661cb6d3cca9eeca0a0c4640c5d61c72d9819f1b54c8878b1cacc853f1ca2e3ef455bd082bf5bf60e4b13ba7569e7804d5b1442193595af32b92e91b90c7f7c9999f7c665a9c7a7f5911d73d415c3339c6f0a968a68b6475c1dabe40dd46087630e1fabf2155f2017e4fb42180a3319e7d7063c027b22b6b8389f83c9aba5a25fcb0cee256c1c16760db9bdec0c82d90800249d026ad7e4fa1f8635c4ee66ac2ecc8ac01ec76d44981128e4000cd5a529b4011b6145ece1a1d5404a453d8f28fb6ecedf4584fc8a952cb6f9d6df93cafcd21eac2a8b63973d1643a07daf8ac82dd5b054c0d25445e09978693ac63ee2de6516fc2b9c0d2c59aa561b6029edfe22e85ec79edb34bdc7617ba11cf3d4c0f02b13c2eeb3c0faa2fd0592f8303ed4bac9e9f81e7c627493f4a334c539b8acd96f02bc66d56f149f0139979ef02b7f584ae703925e664182e410729af2b9675cb406169d27e032bfcc6e540bd4b7b1b11054f83ac31d5c5013e0ac3f6d6e6a97ec1b08b22c0d8fcec02e23c3d2f56227fdf155a9a7c363854deaecc5321bc94fc52e50df9ac72c04b4fb74cbf7cd86c427f9049ea0123dbabed674a4e2de9652290ddbf03a1835d5a92ae4ef3237137432ccc8146c33fbd50155eb4e68cabd0208196843c201f2ed0ff53d389b7845fe62eb1e873c6f5c9c1216ad8ffdfeadfb1718e74c50fe500225ee24b08374d2a8b7fc17d324f50220961759194b39f844b12739e80621a5960316675859bed49ede417bf5a5acc2e4b364b70bb92683986c1df944a9a3b8cc87e3951e156af6abca648b28be2b74e6df59df0801edc236b129ba40af8c1c9f93b26fd9a45d102fa7bf90e7f51f14dd59d241c4327642134210376431209d2593785a6a4a6f042505387ab87daf614bbafce0a71426a7e26d2ebe8f7a36e23272d68e4396afe0eb899fe01103e5190ad987834ca9971bed9c1030fa67d27207d4a97e2583383f5904f0a0ef868a6b8a0a2c85fe1c9554e620130e6c08293a578b5a28ea7c0021ceb00c5e4e171a08f2097216d3dced274a6407868f61297f9fe9e95cfa52375e1390c2a034d849bbce454638828ee58aec0b0e171dadb3c2ba34e649b6df427a1a22ec81e2406de5d9d3db670db9198fc1070ef752419ede5f9973c2105bfba9ccb21d5e02ea1e13024a8072d8d2816018970bb8e226425c4c82771c7400941b55dac1a82c6ee13100d8bda00bab5a6d4e0111f101af580b036e177b7e88d70ebf1f57d3d9869f488de7ef2e41923a71cda5153c0f99aa741088d700453e078bda0ac7cf72c4d40e5755f51e9f972d5193161ffbef1f56c0eaf0507bfe67e000aa14c77e2fc597669522fd7553bc3a340e622190378956119d6213836b644a25648d9b3463ff256fa07f431fb1254f38a2540d7d8d1ba4d4000d5470105399683fa97f32adc17549946b7d4e33a06085ea451bb9b0d600fe3202fa84f96bbe9895918438d9d23440f5d086cb4624eefc7a2c09554cefe010fc8412f7dea624ecba0b778302ee742b84e396dc43d3c76eb2b10567ee21da53b51452ef279344c3f4cf1797576436ef727f496641913bc0f28b2dd03f71184034d502788de0d7d8caa15ab98c454d2676e297017823c35c061419bd9a63ba9deda91b4a7f5cc27883af1ce981a40998cb00af959ef782a4971b2bdfd91e38d7045ff5b399c5c097e014ee9959edff45d11affda024527b1c03417b6609dd14487de0f2ab328f2c7066e2fe3d18fdad528fa097746a24139bfcd1361368052ae8ebd22969612d971a162c5cd161da46aefa32d4b540937b80ee404eae6eb13a39ce56f8fff92b54da51403fdfcf7946dd8a43ce26cdccee842af2f6eb15827f305af101464d910fb943488e0e5ba33853c6206eddceb9e2575bf15ab59f5b519e3023a368f7c1584b1f9e4e185f05cdf6b4ae976f075d4db94882711c2ab8e50b028f8f181b95604247e18cefb32c187176e0c4264b9d9e78f7c053277dd97cae1eb03dcf68ccb9c14d6f77256486d60a6ef3c8dfc54254ae5d317284ed44a8c2ef6ddf4aec97a529d841a43fdf3bff0d07776383574af343a6b2504328a61ae7607b1e792ad94fd63e0f5f7a714febd8866ecf4e93be767a16f2c6a4302ed3c88f3e59bf92c5343617ad9051655cd702ff03e0a52af7f0759fa18826dd0d6c0d6b9b58a943c485c911cc2e83d683725ff800e4f44dea9e5747c064f56dec7c38e976a8667949d972a1e8d5cf265e4f07d86e2bd4c2706fea5e50780f6bbcf004fad574131b2a5d060402b25a79ba39adebc44adec286e2641d02c06eb8264faccb1c5ba4cfe130df3ea472e3ad9df249f14eb4db5f2a7b1bc271d67455ab068c58b17a0eb2445f60d9788b899e3779594819895fadc61cc2e080daff29cee7c04c152928572b235c790f229121bf087c155efff507d086f273f11769248554252c8d5972cc79eb93724ca839565986fd0d23149a7d428b2eb18206374aaf52de2629a8215536912ce04303d8308cbe65b8b76a963b097519a531615325c2fd7a1cdd84daeaea5f0951ba4421342db3b0225bd35b659e31cff10180bd6dc65a34a851c7c9d943c9a600c76aa179626a52d8e9f77fb03b5f03100037dc56ffa38081b2a6db99e9299075f3b2c4cf6039d9586cbf123bb759bdb130475385704108ec22012b4742792a05037da6aaa56e84da60c467ea7d3a908e81d7ecf0f95150be7f4d5dec9a1dd9dc832495542d1c376395c5e7aa334e020a21b28b9245119102fd119e81c340311a9ae3749ba3384ba0f1204f6293e3937dc0eeb2fd7a2ac724da6224dfd2dbec16d3a3af4c70ef9ffc653a52eab8d1693a61bf5633124ef3a8eec373e13373abc59d5993e616593eba7dbb5e248ff802881102c6eb060d5d2b8968e9eef372763ca5183b873e5a581e9e865f47b6028bff71c19cd3c08fd68a23f0d9c99aab607387b9c6ec678f9455616311768fa832a5204da68b4a1d18935ff4523e6cf72b1850132058c7f150c2e126196b6723f19ad2fb8db2bfb259f034348221b3d3f87b85780619a6d54e67cf0a55965862b7fe52e23144adc595bc2be1a48f1c1cc41b41dd9da0dbe5adef141730e340d04585616328ade11c06b5003e195f1e3dab9003fa8013ffd3f7e6a67c1f51ebf65dad418a861e2f11f2ffe125ce67efb0eb74f33bd131b5a5d11b6446a8b3fa207ed8b2285a3475847e33e625ed4e7d6533778b595aff6607586bb0412d8210fe340552e3583fceba101476b4fd039726118ac87f80df3cba443836dc2a53fd6fcd34e0e18d5690c79a9e9db8026bb60f070b81acd43c1e5f7030ea7dd2d61bff42ea430279ed1125758d018a344fb17c2592a3d2c34594303a65937825b43c263ceff01c029ef10cc78125dd9f4a55a19c460e1ad391a367f976c8d14a2fb91320efe28825a32c3b76a082a9e42428bac2e25c4892681860db150836148b3b1fdb13d1f64fd78bb318ce7387682b34c821bedafa127f2556a9c2687178ebe195756f609fe81400fb471b4fb1d59f727b378ed82d87dcd5d496097eb293d973564d398058ff3208fb5c9cd219328dff54018be2374f4a66fa489fcdff636810ec5288a0f1428e907d19a12444d9075431e049e594078e1d039e1260075278fb374a93e0105ee257808016fb5c76dee7c048012653bed21b536bffc786e8a1d77a2b8290b41964caad2025868bca96f50308b0e8d7a814ec83b900c8f77583e86bae18a18d2d335215664076fbe4a32999c11aad848aaa1df092c39ba98380a3439943e1ee02ed6a5098b95951eb7e0447b7df1eef98bc02b65dfe974bb92a4ffccc41c2983166df1755e1dfbd5545f0d62e8a27daaead2c734a6e61a196f7c253b967c1528ef00362014c7cae847514cfffb599397f6fe6c6ee72025414d2908b47de40a84cb4e3c3424ba0329707761ff4b3262835631ea1a3e164fedf5077ad2ad20039921b628ef95d8480eb14371130e00af763739537e2d02d6f125589f25eb850ec1140ed345fc070944b3846169aaf337e145e157dba7bb5cc8ae1919e050e01864dcdeb4f64782d440a6e643bd1f58b00fa425237d8a1f8b9a014d7198127601a587ed01e73a590f2bb886b1526b9aa2f577b51ab869e9e11fb795efaaa6c31762c2ab563613d28bfb2c3d59f769eb526bff8eb35ce7a97fb655447093663f1d1095d270f54f28866027ea95f63a6e5715618c99e7e08f17948a1e8c65d0f223eb8346f3a9ca6c43ab9669df0c286376cb610b5a073c5371c2c168935bc1a82162a0e476196e2da58d90892d41a9a9ba1d51aea5d2e8fe1d3554036501ad561776fe8203c0febc1bc49c023b4f2095f7ab99dc9fa4a09c70050ea6c1a6f93d04e66dace55265374deb889c25cf2a56f3cd964ae8d3ac45ee95781743bf701a13b4cb3c6948375a09050916d900fb3d0811a077bf61fad07727b53c2564dfe216679c8ff255da315e1e475b866a908dbbbd515d620c7182b849e8863869ddd808902283f89ce762a42304280edf68f9a76b67fcb61a7a54cda8575384ef380b24d73b1e75b50a6174ae6ab664e0a75f91d4acd60402170a2bf967c9862f0ae40ea80f4d02e46dfc9d22f4c5d3358a3a741b39930b017558af7a8c9e268f37a00cd48b6a5afe6dc0785efacd85eacbf38ff3691f503bd723796d7a2b7a5505471c0a5dfa4b6eec5f56d4b7e4805da697d7a7e2878d717e21d742f9699ac0aa802f8b8c774afd950836e8ec22a520412abd775c4619aafa9dd53a9dba31f385d121f23710baecf82aadad99712d54fbbff07e19d618663216ebf13dd355389ed70ab297bb2e8f14c44d9db5aed00f4272956f940c814e02c329cac3b33e110f7b260c8b643c9225c725f638d415096330c953d46a6e6901548de47f050cea00921c406ed69b8affa8c49e5126b09b7407f1fe39e6c3a67ff985f44b4068d17ca2211a8e6004ac2ce57b738ea79b47a79212fe05f59fc333dfea5b973e0189f8711d2ac25b7da815377b5e0b79bb57edfeb0b3e249284b093f937eb3c8cd5dfc470e799e62210ee5038ca82113eb298d0a91ed7265809b0e3c41842327227bca7e179281876a2d730f88bfcde6a261d12b63b325fc4b8f8a07deaab29e073e24612e721bd4dfbc7a76064f51b97cb8c9aadaebc2219602c4018e412558f0c92cf50f0300b41c3af93217af662fae6c20aba5c5de25678a70e7548eed1465315e04260f3dcd91a642d67ec3157acf46ef336e01231612fd33a33d547b352838ff602202827e24bf1d82e874c4b2e56d6da2211d4f7ca4affadd0e17c9939bae8c861988b22b4b7bb9ef9dffa40b919b3a3182f30d07eb113eb168bd5c30bd8b206a032495cd76b9b5ded766b724b3e8a955b114b6bc8eb936462efea06162c87c2c1caaf7adc04449a6a314f788d3f2e7ad03a89388119434eb73467f6a22898b762192c947c632f83b1d839ac813849f7f22884a21461081a6d636bc2c044241732a226e71541009557abe207a50bb07ee6e5318c4ee2f10bb0bcda8e4fe00e6b71fc41469002dd8f72c043824f87eeaab33b31ac8effef573e9f15bb24f1af840107e0034eaaa8cce6143056e561c708175ebd2bd2b01f7204024529b36328ec107e1079b23231ca0576310db1dcddae6dfb2272b968d23ce655bfaa3b5b578bb051aa676102be135cff656a57bf369ca177e279e14f723c6d567cfa0cce54170301665691ed411acc68341b6e22d99e80c111504fb6e8fa565540666a6c139f21693e4c8d39fee103747a085926e9c36d672e0950b53e0168b556f7cb1a672ec12a0aa0eab6371061b8532a895927a22ff8cd0d1b0f4c4daa831cfdb62551d422abedad3879ba0e5d639f4ea58196f23087af2ffd496a7572303590001f57c652e0d004ca35307c2f8c99a335d6378a3770e5dea4aeea8e5a4493369f406fbdd1bb85993f3834486eb72a18278e27c76aa92986a4b9285143493f120104d628016e1f540e94796eb5b0014ee099328f84ceb614add86ccfbc550f7600f3413651c6d6c29222e50e8a15edef04969562fdbee5ffa843960d92d565d380cbe1def21d8212fb0ef618b09a78a0d3030ea274197521f239b7b82874d575222ec0ba81ee82dca840e340012136ef0b243eba0e650058aac4a5fc88ad5327ad848ecc710d71beeb1dfa88fb932d6125e181271335a9083d7fc6e13cdb92d1ea5b3d60912591a5d8eab8b8db19ca4336fc32c3147ffe8088684fdcaebf5f634cf5e1eab1b68a4d96dbdaa0afd40748e0b4a0ebfba0a627f30c9dbc621e727f7bef5e78e245f34dcd0a1828753e5723eaa14bb4313dbe15d5e76965f8bb765910f417ae403a931a5b653d4fbe31332fd04fc6e69626fb834cda6fe48674b98219ec25e212df043b86786dd8b06d34b0bb7e6cf4c5bbaa01158fc330b05fcee8b4fb9eed20ead836e6b8cdbc6cbc06457ac64cf3a72283181e35a9657ba381ed47169ef3f178a2f40907c36a2de63a99c8cbe92a23d9b10305c3003f5341ef1f9bdef0a99000586c10ea5861a049a13f7c69b0bf6526387aedb3952554218cb5ae0266b3d17107b45cca47a5078acf00a36247bd987db1d331c585953c27b3e7b68e34c5c086f1e38aef9ef67f6bd98ba574b8534c2f71e4ec038565f0b24c6faeeff168926ae5209a04acf180a8c7481af21deeb7a02886f61625e734604feae11bb965306c08ab20127db181eb638f57097f85600f251529faea4299afb46dc539ed447212bbf694211232606e3fc74627321d1188ba6d02f9e8269dd81843182e41575113b3b7b2c3b1a8698546d3aec50985c37a3184edcc69efc421fd3e4f939fde4182255831a27d9b5d835ed40def306d138225b855aa0b81e5707438d4cf7408725dd70cb357f50ec938767a6216c3c45ef2034d3fbc3c8555a67b7dc78b47d8628fd188cef3d2b8dfbf4fc38ef79f480b2efd8c4adfd46fb6205e251212ebaf31e70b2d8a82ef799db3afddc46ae635734ac0c700fe84891c053e63156981b0b0fdba632fd2f3c239ee4bd04254130f5d66e68515143c5f4d6f2b29e17bcb1a303aa93928c09c5312a4d16946121da973879c2af82cf71b14a2ecbeda0e5ec8b02462777d72e300a2f695fe1e4440becc12fea0e2ca14ff1862931e8175d31cb1c9f8844549a6ea22385d3a4f60cd14fd6425aec8890bde9221d42f60e54bca41eea53a6a00c6fe7b66f129dec025d3c05b1a7661322b803efc62b123918b9700799cf50fecd11f0787f2807bf01fbec1f9774eb693afa70d652dd59d292b74809f0b45fd23a36228017f8cfaffd39368d2317dfa86d5eeefabb69e13c7d7ca5128829e70548ddef6f0d1230f3674f752890c754e20a858ff699eb425595132818423036c330b0a4c86f547594d8e34b5abacc9933308d03ff4b5307c9f44b721d9e1d6655d4140a8adca0117a6ac07565e7e8887975eb3965583949cd93c18900bb301068bcbc64aa85a14a8ef8365ea3f57bbe465f80ed8c039a04a7c9e5da0e09ec9c44885c8ab72021e4f8f6c388240c577957dbdd7f64e929aa54c2960a071e8b4cd84f4f659b95605e75c42ca42547f2462b36f75b57972e6f9859d45f067723d028b166e84ad2a5d9921bf762376c3e90ae6b17bd87c27e0fc007a77d1f5f7d8b0028999ef82f084a250d8562793aa0ed27644ad82feb94e80d948a6f09feb51fd95f0632eb2b356c130b50c4f70fb0f967cb7eeddb222c546a5efb49050476a0d41bb023dc5d85b1cd7776f9a2c3c792be49704ef8e0bb09b9e6913728a0b8490ce3a1277aaa62aab1f07ebb41c1f395c34eb9e983884e8dd953bf4a0f0e353000cb746ce229ae769ee10c47f15ec40591d589d798d0c2bd2655751106774bd58d59720491a086b4721fe745209895969578d037e71ae982f8a9320322a859297e7a85a2002ebe2e883aaad92af39668cd0798c08b7eddd7d503b24b1ae2daa5c4eb25393ee6ebf4008bc4e0c072ca90b67a65c586fcd5550b29423f093d0da983505afaafb1eb0771e8e9e1cb3539fbe61f5efca2d4860b1fa1b36121e5effbfaf7611b946208ea448d990b3cfdaf50b40063af0b7cccab49f5c86b16506192e554f89afa326ed0528f6aa248eba2da1825d58628355393113a8fee286a02b1c4a452f8f15011bf27dfa4fd6832f09725a7219294e11954b957f9221166c24763a83391287fc3eefe14933c5c14f4c7d6937f7b75542347d04045a110890ee0f78b861b3af29a624e25b367017d565f468cddce5b7f0cf3c35939a710bf47754178cfcde50bc00ad8afdeb5f65ac80bf3130f8f916859fdd0d676b51a19030af15d64ddc9aefd991cd5cfa036cb80826b4373b9d7bcc892d88b7dc91054be1686426a50d43560b6f5a738e11ac395dab9fe8c808f8f8ceef391350406b72a33744c6e8340275bc6ba889bb2a39e4e44a5ec3608ba7e9ff3169958a41fdf27650f39023fd280fbcd8a14f01e01129e55d1f39690748e99810326fdcd20e052a932671c905b1eaa5c122a28ec8ee55385d4230903c923a10347dfdee01e17b2763b6a7060a5b8096bdf84673c406e1bbeb5fba30c109497c0aa3ce4d91814c363d790b240b21d0c46bca0a903c5fee1e5fd6e2278a36d7209bff11e701244171b2ba258a8c4b611fe9d842c33b90dc2bb751a4c9834b94ee633ff2f0c17e99d90e05e03e67dcc81f0482fe23b3edcf8af33abbd48817fcd2f01e5cbad02174e2613f854965fff32fcfe1b2ceb878c5e8451dd0aa5d75a96db1423038d224a2a1f5665b42ccf0cd2009d22d026f92aed1329fa0eeeac0bd789e8a094c82617ebca343afad708cbf7746a1a7b486be884219e41fc54bc92afa7c4fde27204e3b778f1434022e9a0f5af3cb796f94594c41fe3d1343ce8e95cfa3405e76024841490ba15b8d6e571c78b5fa9bdb4f42cf6939f0ca766f8a33f9af1388e9f0cb4e92a32b3f70b729d395a955e2080b6f0ef0d26e8692148295a7b56b5f6a31596ef09d919d4e9ff98704206e61a8dc0a9ce4662eaf6a10878f3180ecd6bcb0093e2dacd06fe78b044dd4e2f973281e38c793a05bc453015dd5d99c1adfa70234a32f957d93338fde57563ebaf78674ac0ecfd3bfe0a8293a8222791613d2c2ffeacc050d243fae78fecd4172ef8ac8d65605f37137ac2d95706f7fd8f331204bbed683e9949fba2e4bf9941b4939ee707c3973843138bebfee49358309dd60265751d08c2e979f56c6bd140921e7a66e34962c2e608d7326c40f95b797636285580e22fa6e96ffbdfa09b8be7d9c5def6bb69b24b832097fc8501a8ca707c0b7f05f0505eb62b7964b96d0c4913735b728fa4baf217082e8f47e2f582872412c7a05dcc806ce1d8cddcca1d989321c22e660aeba3d8945d7f932b6de9e6591b97a10e4e7f2ac05c0135756d3e162058d1b9e577760bfa12f779dc1b21c04300b8aa9e06aa51ffdcd11ff50c67a307276719bc15611fd86c8098faf7c829da2c4b1f4eb53f6fb595e1961d38512f3ce9827b9e4822b90fe6e5c0c52672cc1d012e591b62f714b671321c5ce756f826cdc2bd630a9a79272f7aa02baf56779d25d42ac69b671532b1d51d23e7c79160bc322015ca838b4e674eea5167aba83c218ea51067f7176b027556fd11238b4bcf8865d89a9cd0bb3e4f43477398ccf22aa043b2ff7867dea2472de15e42e20bfccd0fd02248e6f1ebf7f95edc2eb2312c0a6b7ad4f50c3bf03555011fdf65cd0e2fa85d3bb7df6dd924e7bb5794985118a1d10561c8f03828a912b04d1e50f8bde69235134a4d4ae2da48c118a5dce12e703a9115055cdecba78a75a6d10a5e56613e9dc3efd226cd0756e592f880642c9974ff67e8a91633f52d02e73ef44399b35cab4796c37156f0f9800b7a88660345dcbf63e44f1fa2d1f8eab547a67f70db529978f40217b5422eed2588a4ee300457f11907f9415e349dad8cb86d9910eebe588cc8c0cc798a522e39a669031bcc7172286c9537143a92c965481a89f3c4e860d5ca9824175d9c7a597ee7c61c115ec99fde4909f191e31613746673c5507ab6e01436e6fe5f07d8a929fef51ff7676cbb3def22e44a545acbb9aaee1773f706fc8684a7b465d2f92f8af7dc0a1800216db2c08004b42287c6df656f6b5dedaae8e1b9013c94532463df05952bbf330909de4d033727d32480cae73d2a2e9e50326fc605ae30c0c9f011128b1a678d3619cafef8cf5d5e07d61a849c05e91e9ce8290b8e0ee69c705736ed81117bbbb0fad9bfc93fbeda8ec8b9464853ee161b5fa9b5e954c36d10f7d2e01d0837f15100dc35eb17b2dad7ccd87b482048827c1716d510244380ac5b0934e3124b0552ca7a5902213329c982bf38782b0aa337a618d658aec9d01e4885977f1514eb3c815c8ac482050a2d19638a584180f196c16339e4652b1b7f1ad870891d63a0420da9390f9dc04588fdfb066906fbeda08ee28cf7f06541519f991aa7125c2099036a614739e41d2ee4266b3faec273304a8a7fb09758a64cdae16d281d06427553cb1acf30f1603889e575889f826a832acc44180f68c230647d971413a392f060a527885509f15ad448f8aece5aadc9836862138a8b6d3b52c33dd41faa36de1e5f4355825cc13a32d55f9d84a7337b691d12ab95d7eddcdd3f72a22ee84a7b8932c0aec98b47176220beadc273414c3a7d739e598b932603544fab03f191cf63a65ae49a832011f34c94d75bb99361b17399b27d77231128adfb33018fd3f19e9f95a38b4f4296e1b8ac28f6ee41814be442b889ffd99b621ce5bf1bf69588a55456a7f9911222a4825f68d22106aaf2ee07891bbcae65d952e75b2ce90f558af3f890bff0115d005d1e5f714935cb35aa8e12c3b0f194f53ba20427e5d13527c1a9f7563f61ae1f59a6dc16f2cca1df47ba4d47ad490a54d1a9af10e1bfddf43ad707f810b5b9b48a91b16d892c73403c3f5cd6670bf79e355e1d1cb721ea112226ce6a6d52283c887da649f153a7790ec04f440176987419b5421b7c694c646cbc5618e0f2e9d9ca5a10be1c9e007d519aebc3fcaa904cf4028d0f15c92504ec8655c6f5ec0bdac134b4194ab85c505cca657e295d2041c68be114b83a369cc7707cd41264238945dcc4d2581939579ebdc673ae8a5ec925776e28341d66f9f6eae02473c10b21c2f682ba22fd78f1b25895ac2adad8511422110bdfcc50e8b7e10c22d1061ca72d0945d2ae76eb874cca31ae41ce92e335e6732e0ecb8257b64e85774c95c601b69ccdd6cbdb5bce2bdb755e0f4309e735725f","public_inputs_hex":"0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e94000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a"},"decryption_aggregator":{"proof_hex":"0x00000000000000000000000000000000000000000000000dfa79688268e94f45000000000000000000000000000000000000000000000003b27d8c4969fe006f000000000000000000000000000000000000000000000008596197ae31284d9a00000000000000000000000000000000000000000000000000025071b1b78e460000000000000000000000000000000000000000000000049f29ba73988453810000000000000000000000000000000000000000000000037166cb36624c1ddc000000000000000000000000000000000000000000000007d050eee5ee699fe8000000000000000000000000000000000000000000000000000258bb7095c7a3000000000000000000000000000000000000000000000005704f0d79a04cd41f000000000000000000000000000000000000000000000001cc8608e7634392ef00000000000000000000000000000000000000000000000af88bb11f4b13a3c200000000000000000000000000000000000000000000000000029db74f7fe7cd00000000000000000000000000000000000000000000000f4a5900047ae9ba6400000000000000000000000000000000000000000000000a2f7b686593ac5c9b000000000000000000000000000000000000000000000006e5e184d039f43481000000000000000000000000000000000000000000000000000265f972d900ce28738b09735682ead7a4c64799135201b3899d799236623c3b56cde6eeffc14c0df0717f0a39df3d76c516e8bf543821b9f4ba63b14188d420b92f5ed32350042061c4c31ac25d95dc3913a7a4720dcbd0d2b502eea4245f0e8e0b648eb3664f0ffcf0587456eae3287e653a00e8edaaebecd3e65d8ec98782517ef25ca5f5ed17e994084f3bba80177c052107cd4673933d4d7d498712c3c1a765d4ddd106cf23121b833a7165f5c9a7cbae738a5033ca28022a37b8399326468eccd785a1650e921736cbaea5f086a1fc0b8d34c9b3cf259d4260e3a0fb734e9ba562371b5d22be915dc76a01693899c6a34254a2ee1d2034d4b4afb734de2afae62fe3949727c3b05c1e3ca04b6abb43df9172b580ce32a1ea52c6a13bdabefe41b291b61b0acf905985d7fe37c2a12230fc1fe3c698d1495d937e0315f8ebc4f08b81f07b054fd658b021f59f1486e8105180cf9ea58448782cea621e76ef6e2b502df53c097f54506c667839a3fd6816ce0201339146d1334deb91f8ea6a14405ad6153a182d352d88c84c0f0d3446535350ae13ab7965e65605009887d7909a2fa1d42e2896551a0829706edd47b1b5786e28188506120cab5c15268750c76c2282e001032b6979a88d90b20a1db9d030107ffc0677ebcb6777c7f4ca2ee0510182362e2657252207babe037b83a8d8b3dc52cc67e771540a593d889433e9cb38eec2d71d52810b29a729959f506e4ee054bd6382f9acfcfa7c288777d3a5f4797e9342229726d66b20423a7f33a255cc294690bea014d9afb0331f91401058a271c34d2ab341191877abb7207f8754970389d7dfcfa00ec4ffaaeb0524dd716cfe7cb11e9223897133507d5227ce3ce11671f837ab1ef85d0aa6e308de0ecae5e49f712bf468ea81ea81e5527ee9e0f1a6128b1d1d32c56b82e3da07269e74c46c9978078a4fc2c3ec815158339b79250151302bb388375cdeada8e2669ddf041f19621eabb305f4d3270b826fa6ed2f25db9ffe39fc47e81839abfbecd91abc210133053d60869e919a115fda8d5046e59bfb2f874261d17b698de9e524a7145be6c521e3e2c29389e805e67940c921c0dd97b619b10e3d3e1822e3b2906d448ec85a031c38b5d1f34c2d4b5b7d5b6360c16fc3f097a28fc83c272b1fce5a6398bbe20a3997e59fc3a0e2317de98fdb917166780523993068ec1cdcd514df1fe9083f1991ee90b63c34c18b2639cc3c3df351ba4b8ee53f5663219c845085116bb81c1639cac4dda73a16cc4d7f9e8e607200c85a8e0fad44becf3891ffe93aa770132a39310b85cbbff14f0bce323cc23f445f6a258707355b3779232a642eff4b1a09e4b99928bfd02b699bf248fd01e881d98aef5772a6043cd5619e1a61af8b3303c7e35dd434b2854019587951ffb7e885f616ef360789979c7851ff5bb479421f42bb812a236b6fca6f7ded6823a28d36810b5eac6732fc2ad4410f715951cb2b5e09eda26e7793afd5c7c865de1570c4e72d9a88ce4b31a88264717a070d802a817a31b2c84d4c1f13a918c3a73c88d8a67f6c81fcf41ecd278894e88485c5062a3ef24ec877338c2cbafd1e61205fa95e2b93c59326cbf8f6468453a1e85e054db79eff791b9a1cb823dd9566f06129dce2b1f1c15e9c6d878629d40f61512eced624da055ff54fd71cf3520e5a759b33d31ce3d711c6e1163e3645a725960c4b1b6a5254cb9b135176e02075b45e9e1ea54e21b1a420b1414abc132d02be133cc2dfd9cdbf2a0a44fd11b51779483b4052c658b345b31ba93b918522167102cd4824015fe51e5b84a6fb335ac4842f9ed11d6f5e69b4d8dd8ba64e0f36270f78b42c71621f1501e1ba49fa4e661de16cb8dd3c698093969b8964809106412790264f0ae9b8007b76649a949b69ac1d6ec2a955902ececb7d33336b485a7c2c3c6a1fadf0fd121f79fface6cdf947dae0f776d069829fa900f514ece4141616bbbedf7de53e30cdfab6d5619028b321d44e93184fdf01e7ab3f5a4424386c23b8e5447413e6639c215e2f7bacbbe08458fe90fcff82f4dc73b1b9fa944e91108a5c28ea9df2a9e1b17874a186d1d5301848073af15ed52a4836a71b9576fb284357773419bdaa332b8130670664dea49b96012f02b091e273421d579b8b7c0db2b2715658dfaf910c32aab84a6eb1daa3f8c4c72cd2b43abc44cdb4aa8a0b0dc67c649a6c3b9cdcbea178401c0abcb272d8d5240a492956cdb2e4a3de78841ec42a7c9a5ab3b10d4264cf887b1c81451ebfb5e4277942e5736a7049bc071c168f11d6111ca6731a245c4f6f0aa9acba6d334d552193f8453f06e454c0f1b30e3264e919b2c667507684454b0045f26da17f27caf045c7b4d2f6be92bfa160277b4cf55287f67b9560a68e53fde3b7b85ee9043123cd7039658e7f874c136d1a2c26f2deaf00a930cd611ae4ca28ec30d65792e9493c2175e557b21e31a5102a8f42f94c87e31eb528d7a5fd4ca83ac3a1b62ed65113bf25e5e0a82dd2cae52cba89c170eaf2134fd4b818630ab330f68c040bca7285e619b2d493d713fbfe079f72b0b80fe7f56ea6eb5b20bf7990fa72a7f26191f159a2eb29d01801330d26bad38bdbd921c0e68d44a56c432329d5477f1e52f1d720e9dfb09f24fcc2f610e4303caced66f68ebcb8d876ec6f7c1904ab11800505a020eb41124069deb92d748417971506e406a675a65eebf63796897742b6080c037c097e742a446ed528783529668433efa5bade2e3f9052c11418f54b8bac5ae047f5fd30ba9e031f2bf38e36125c725b69365847ab2eaffb80c917ac7d3a97a47c1a1a9950db209d0326fe9bb980be5ef2bf21a3e85f236f53aa3843581e6e49d3b4451db82ff50218cdf7411b81f746a1086be6f27cffbf9f2352b4fca25777ae421cb673afe74d276522587e772895b53893a247b5bfb427dacfe13c2992700becb2313d3dc00f1876d430708da7298c907dff0b5984eafba5b26cd6fdfd59363b0bc511769da02db19b422e86e8e4003ad6554001fd10fd87d5f32af8a9d13f23bbe12525038a1a7a3867ea9741941c29e3aaa8491855617b16f140ded2b19a0b03cc4a4cd74c20f4111cdff9aaca53255e23c1c6412be63bccc4930ef87a63fb8e59411f56d4214b5fdb1a07fb3d5e91b25b3df5df862ad9cd72d3a24325c122801358aa149d2c03a7e24a687a2135762b2c1108cf1432e73f5ebb8e20a552ad92a91dbb185f11f9ede94d1ecebb9b30bc4c1a7744e2d121a63bb8fb54c2a83dcb8bb61cd6b600571edfb0c5ae31403c4fdebd9bfba727fe0422077b4bc1b660ad5ec5db84b7279d5845420a92bc08b2e5698b81383cc1490904ea5f20406d04b670ac20b49919e8beffaf81535d507d4ca8b08778bb8b791d016fb0c935e0ad7f1c6146cc4a262ce42077ad7d82e2e3848618c2e9514a3422581329e323cfb5e6153a6131990a4326ca63611c14ab69058cdfe7289463baabde69bfb64c895637ad70f47acb101d099896c7ba7b7aaeb2292c376efdfe9278aeabc23363ec0d2c0fad2cf14325801e832e9d2edeffbe63a053f8d6ac11a13118adbe3cb26f66bdc367bdf2a11bf5271e7b3828c52104b02b5f157695823763e7093e3e9e680921cd0c3dce450f4251845969a5c04cfa21f68c982ad2303130bdb22134d6210746c76817e25a249c64fff8b9d2e3651db73d13eea4360f99ebea5b17fd312fee8b1117e4967004fcf53c8939497fd46ee37dc67a58480fa1d8f2cba62cd1a04755796bdb1dbd0625d390458c05bc57aa6cec0ecf86e5b90d7d7484ce043ba332d42367e4b5310c45f065d59ed77cdf230d709944f6dab4c7782c82a2ce64161caa970c2c071c11e59f3300de1c8e44688c10c9214d8ee5d1402f31710c6c033adcef7c521b550cfbf8b66cb58eaf5b4f600593e7a7337c5b37df382e5df9bb7da69acd0935db2f71443d93c858a05143b20f06298edcc053d6f52755f8791f02c80856e1f55b0eab451543b5001697d60235605a12ca6377956f5161eabb574ea08f2741d0501dc63400f0e57099a68241c1d29056336dd70b0ed371da2615573480f10f933212ec6e0b1f25cbfe5ab5958a232a3c6555bc5d9a88ca2a5433eeb71500a178dc1003e570f070517a79e4ad9d72892bf563aebc21f65a15617fba60ab62749c622bb9822e4621ceeae8fc692ec1384c31b3fa7f85ea545106bbd4f0c8b5b03a6a0f64ca4fc38aaf821910592887b951fe39a88fed773f3c32e9d3afc97ee079d406d44e6772130b54178d8e84cf33390e8b880c554746bf355472185cbff9ba4c218facbf53bb5db501c6c583ac7df90901c1a4c7bfad4dc5a1f811986294c3e30b018f42f8d486433a79cebc05ab5a4861a0a19f413346be6108d93bbb7ab24d0f1e374662c55fbd4000b52009915ae41ace2fd2798513822da5c47b69c627522fa60c93b96255feeb8fead0646f6c03c55bdf0ce8c90d8a4ced28275e6005101dff152a15dedc7a5077b8a6a24fb6ba47511dcf942b097d341439041c53dd82030dff114cb5c1acff5f422b9f9157306540025097b5ca9666848657a1bdd8ac035532d8747b5182f12d2d76caa80b6ef5f2c23ed91c230c03f3c38b667fe41f03c5292df7fcc0485405a81a40666c864a1c0bd0f9797d7f00211a2a4d7073be1a8cc3df21420e1ac68e87a2eb75b32473cae6554a8c5118662b250f3c8e00ae2e2973ca4bf0d376bfeeaabf8f7b5c4eb4761a0b51fc018d7a205f02979d60c2027172019511cbaf761d504bfb9dfc8e01e45cd38b4e10103c13b2146174d8fb073f021f5d8fb8369b690f7a5df4044a2acc9e308e6165e6f7f5ea5d0d5f695a201986328ac16e02384a0f49de5929baa1956458ee19a901e8fbe31ecfa803bd2954fe80592a60132ae23e7c8d4d79dca4d60db2dbfe343b01cbab95a814deef0d2f09686caba0a5e8714860f809bff73ea57ed27ff26c921247633ff0f53d010898444c0522085bf95583c6bc1ea11245794f6442c79c3467afd6de0e938b9905fee8793edd702dfee158d66580b78e7124a0aea44968f9ded2dd0500d8263218735ffa49cc44890afd2777727aa4f9895a7039122d6e333b3520dde3f6096c29ec16859e98a504c88d771cc47aec7ff40a9e4f201ca83dfc7574e01261a9ea214114b3db1469a1cf55002bbd38d0ea429b1e24004d7677a57f16b4b0cbc25803108c1d04f5c0c3ef669689f62188b71909f1c11d95d514ca3a78edc8a7976422a8c196a42444ac4076ff06e369f839e38244111f04d4f74cc3ca2d635bc3001c60ee36b09471a5ed2d5db3a067183c50ddd40f2d74469507e949541d101d7e16c41645a3fc37a00b4708cb588611fe15d807d9861ea110e6883f4df31cc8ec278d447019f5d65f772020851c65d6a10ea601c7c4bcf78e58fc905b2c664f0d2c457a65f6424d6739ccaefc361f1e700d1735c61328bc29e75cb0d76b1c013910c82a1c8b34214adff97e9d94943f069509a46742bcd835da11ebb9b440dd3624c70366a5708a3a10b9688401e4250bf48266aa77d0ec6e5403d5db41f718f30f947098f457716160288bdb4721cea1adc69d2745d7aefc57eafbd2e901013b2ccf7ff182845d214e0fbe248a08364934d0ff522a9fd3005df960972ab255052930025839cab3bfbb81d5127ddf6fa01a61a613a6e8f128a85004743292f3cd2c1792d9ba72f886ed7a121297461743136ce348913ca557297019e2a3aec0a51922836c33d87bb152b06b9b9f3bbd144c703eb94cdf020568660d841a255e5d276bde9c1bf661ff908ef89874dde149fc4dceb010e3a6a382e292716cb4b8d916263774fee4ab4ba83a3ebf0b1064ebd75b9ae887644471f86a6bf8221107cc078182f2a821d52e0cfec07bc6657e50526ef9fd0048ec552d0808a742742aa625f90291e0ca4e5a4bd0c2588f4f2072da4cbc09e48791ccc16cea22ca882cd00d0381fb3133577f3d82a0a0bdcb0db0e88fe939063377ee25026eaabd38be1902111b320f710bb3503857b61c13fed36475db6ed1080063626c619d22f9b0932aa34375d85de0439ab7c194f504bc2523abd9627a8eb0c3ddfd751df66f5c95293d54b5e457bb7a9f8362a8f0e2a8acc4586c5f9109067fce81c82242a2eb5f2558d88d1bb575b0651061ce1f9bc8c75a95c5f785ef7933948632c1cb5b43592f88a62189b6ddad9274e56b188dcf13db455a4801c175f2bf802dd46ce5aae405421fc32aa47ef04f6c1ae84584ad7f02e065e6a7cfe8d1854a0f5431c7dadb0437a9c689c31a4cea1d48dc8bc6b635382ab3f7dcab912b6169fa9550900047047ef7b1b8393d511c82c6551d90fecf2662f336597b24113124e3d13b21b27b05f81ed1c2b356defe173c7cb7e4a5d33e4dda4cf7020e7fb85bf2893696a1a3238660d480aafbb0c4271397203e1a80122314f7b9ad807960830b0cacf78e1b1d45f76d3dce3e1db3071e9055eddca41a84da428fd19241adbc656e5cf7255b0cf5ad55a164b6805f639c3f7202975845aaa64ec03b57fa721daf278099b20c04630ee5a1d2cf8aa318da7097dfc780e843e1e2b8d7a550db5cad5d2ad4cfbf2e115682cc648ca983330ffb7283318b6311873be9977637d3acc6b2eb932bd00f387b1c9aed7bf928c15e9af4b7c165e4b59cc38d8e190b8b3a81d1c6d9f83725e0695fb46317bebde0aebedd01b2ed491ce402d501aefc8f4c1c293705be6b000354686877258d5ec4bec33fe416c64ce5925bc9d9a537b439250967bf458702d23d65decbeb6752b51e297d25b2432a6f5b64c0773b205c4e1446b087a44324cce538d0ae9959129c92b2240f863c773941aa0d1c503d20b91f430a467b4f2bbe0a36f9facb42624703f09f4288e1c7b74a9cbc5b2bdaf212a49f219b618c2580560061039f9eb6c52b7688ff624844a0a4acda0f1db128eef1bd0259ac3c176b1db37e377cb9d25a85c75ba3643e894de5bd3e998a4160837cd75f1cdc723038cec04a4853a6ba6c62cd4918f1632ac30de6ed5c0f8b0a68398ee741e60f184ea704315635056068c59f3feb2e28bba86ca05e77abe071bf4d179a0e9e2d0d2bcc51f8249afca59bf04cfb916937a0183917178db300a3d477e6c83f018f0db99b774c888c7019932c91882095d48b2c84a5004642e7ab200a22e90de77515c4e35e9d6ec6d71aba421ab36b30e7328fa15dd936f1c740838181ff349e122ffeb6ee1157415c7f104ac0f38ed2cfad62acb25a58a89eea6a5f66b4bf392520de5b7a89e66f24a828b7141b01fa5c1067327831d040d0e7a07f565528f2ef250121b6f9948bb2f3f2cb4099853f11073ea5173ed10bf2cd22f90108fc7c4c231b03d6f484e3bf256fd3d4f82388d90090764a9b6eaf6cb2cc2806c5fff469294e11a6fafce22457c6817315bc429ad19853b7957af2af6c3389e10c5a105a2e8d982fe73afb4addc3aa9b344fabc343c6438c8a59098f5936add0947217cc2ab165ac1555e0264108944578a3738595d56b7e274644af28a52c992a33209c05dfa9b8864f3155f77a4b18eb1eb793f4721180a337c7fb5a0b499ea96afd5d00044ba5da625f9a36bc38107cbedf12e0a51a8a2f59f2096e0d20e065018da92bc0a7112a7e48a4444a728d936338120a122db00a096cb486e3b67164120ed11205e12b3245fff17ccc63ec28b2e2a1f3d2bc7c8e7403e0c9c5d5a1514a2b900797eb3f3ea1748dfe9e23cad65e839d8e3074d0fd24a1bec13252b57f821f9d068c1cc3c26382875bc0d27da9d3bed3fdfed96ce2b7ddd3446e35d91b31d301164e3aaaaa7534f8ad4eb17b0621e6e5c876d84fa1937a023d2c29e37bb14c9202c71cc7c96f699ecaacae99aa36e15dd8055793bc6276958cbcad551d2448d1199499f2720dc367e892cbec806b3c9459f49f22dfa2b990fc8282a4ce499ba20799ba426355a8ef8dd0fd899bcfc06372d212c6ac1f63c8bd5e6914b18cbcb61704504f2e1e2ed6d86e2da36bbac86aeca425a6720cb4facfb7fd429b85ad57032d86246b281117624b1b92e7da1c0d27bbbff6a32918fc2f5945cd0b4a0ae312ce4799fc5d07c7180005bab2956d23025adfa9e3e37b7a6166084eaec7740518fa072d7ef56b4c1f8aa21142872240e2dbebec78db08384dac3cf7c36628851830f724379349291aedfc0f9cf05c92d4af5be0a50f586929489f98bc520eb906de5f1f41ebe26c58492fcf53552fa0d1df87ab01ecf27efd93652d85bf06180c0bd83850c4ddf904a68bb0f3a24d6703fbb5e7e3e3d58561e1f21783729acc1c3d2d9b96213fba91b08d5a06eca9adb713d1636c1c20ee1c6ff40a786694870e24656dcaccb56da1ff15761c4128774105f4869db44cd54a07414e76bae9ab2bd6f76e121840d8e8f87364deaa979ff118965f2ccb1ef97b3e45187af7eee1034b6d88504c0eb3f27f35f0f36cced25475b2f62df8cceedd3f4a9c7808fe41219bca2c439d0b29739bea7622a1d7c44d8a1de6d59c63b7ab55283f5df05cb70692e71c660ed45d49f1fb0790bdf274bb6cfadb9fe7d0c436f8f784478cc8df189e594ea67b7432713bf22c1199d90e0043695858aa46cccc397e528741c6b52e19c9ff45986020241cec343c8d8f74989c21f2b86e94a704335942c967bc9c032ae6b105131dbd3e550889cd96d3577f80542ab10e53487ebad39ca428ecbb236b4ac29357495fdbcd24b194b37aef7798539cb7407fc2d16cc02647616fda1bcd71bbaf8633ca0fcd9bb267a051933c14313c8a09c665da7ab26b142e9d811d717384396cecc500f378df1db9683fda74d5bc2c4e78fa1a89f7cf4975ad69066156264ceee9b8b8926b5a7105c90889998882ac9420f532112a6f1f2f2b9c023a3ed64af572f19d017aa3ef6afaab087d3bd768155bd3df0fc9f7f822ca88009d54f9ab76967e873ce437bd0f7e3195cb48599df46f54ef9d2e424a93f59213e02a423870816fe39839111cf60a4d2ec4acd8e43863cecead8f660361b8a903bb39e0d785dcdfdbe89b85c713b3355eee749fb6027ef71e9984d38806510f18a4b230924b0b848a291f8e62922077c1034005c5ee611f550a746bba75289e04ea00ca60efb7b8587281ce5223eaaf8a07771df77a7dfee0d61b4d1e01602517cdae4f6c4ad7205b5c2d781c36bb7f9c36d3a1db2c4eec78e001dc931594700617d097a075cf9b4e688aff67aeef2b5befdca4f21e388b8c655c2b55c24e511251c2d400d40a3946224c052f1ce75a4442306569e701bcc086439a62f9e04c0758fd7bda12e1ce40b209d30d67f2f003fea544afaa1d0420f6608116269508081983a5d8a574b216c19749a1472029898399220ee4a6506f95e7c5787f5b9925e200ee2c2cf08cf40b446659aa9b72d867e89de012b1336c390f8884c2087e09f19183af33cab63051be64938e007258d7bc38158dc0e80b4f2b76d13813ba2926c0c2da99395ffbf9e8cd65c95721cffa67de1b3aacb1e40f9a75ae46d9a11e06d148dfe7adc51debf3a5ed2958ac53f09e30b368648762c53d629ea154aa10cfdd10faa8de5faa738ed3d7bded2740c0187ffd6fb2f51ae27931572d853d27b75276de25a8deb23b075f1581f71d8b45f9525f54c924a3057476123f900f1f8fc748b50883c13789486e4a0750c4c2b32d747542761d6b41b5afdb17086b17b7f3f2125e5df91bd8ff04ec54b3b732214ab81bf6e21560db4780f1f0ce7329d6540a217743efe23cd1c6694b52623718e1478973ba5f76afd37d376b8930082c5adf2d53ad44dd629106b3c3b3ef24829f1570d6fe0e6bc673a33d3f233406fbe881c13361f5495c94f2d43887657f474a5ca6ebdad16ee92ebf3755c619252bb90cee7c61ef1b3729012c54101ff03af47b6444e81ace561d759d309b050a82f6e71cfd7854a68ef7452aa7e48f7c5ae0d48b5f2d54902687a8f0bb78852bbcb6b17cc2f8cac0ec1526e2a60015d3efd9a0426d82aeee4260b71813bde80392ede8d86bf0d16af0977963ce5a573f7d561fcd20ab05e2db8ea7ccb334ab0307670beb2978093fdf7001199dd0d72b0b7752ecfb91da30847c8243072a7c2f199d811b6a6c1635c6ec835b05fd77235cb3d8451194f699293e70133d756f2c9881849ce8eae584597a83c53a47450a578d88f75f6982d17ddea04e9b9ed5095939009049cfc7cd6cf9f412a3cde99720adb91ee077da2aeafbc7a033044a0b672f1ad786fbd543528c38703fb4a37c1db9a2674feabf00ff33a5ceb01bab1aa064e846cd5d748df1714875b6a62c9dfe562519ab164540c0d7530589c288064e7b496b7184387d1d4686c18f22110364c55e53f3a70d016832be570218a012f6b252d06f73d254937d9062a293346adc20823419633ffed17822bc3259dc061a1a8863d10da15f11186f71f04b20e0d152385bc108ff740a68bf824f890f05b422bc8f53d3255aff1e028a79f6da0e0e24e931497d03ca96b04e687a6f6300ef2bf5af84353c1e1a3aec79fe45be15ff7c3791aa7dbb919da84d1106515f2d5ecbc9c4928ef292fc4de434d4b92ac996d21ebf2d7d0b9d99a295622cf2ed2ef5cbf883993dd846bae8f4fdbae671e7338c6e1743fb26ecfd0e40e409c60b03445ba5b1bad93b6d8fd88b0dfb82ea5a9a01db23cf5f74ec68b4ea42a309c624208abd552ce7d9b316865cbfad7d0c232c8f3589cc58f93168ad5fef7c3843002e357ed1e5708133f433b4e0976a4b1d8f75d99556678833e17699e399f9970d0053375c14a0ab5cd8135fe265e537ae1d98e32f64258458b7ca404128db1e2c187a91f43be9f07bbdf161b002391f1b31d31a2c8f62d7dab2f8d7c4e618a71a943d0f30fbce765f0a81a0b86f000a9edca80be34d9fb22fd5959a52dff8b214d45dd690880833365f782eaa2bbdba3cc6758d345dc129399b5ad29e5d8b1f25d06a5eade8a0ef42d3759d662b6c3b27d9ca7f5804c2b3e92f1cbf128efb05252410af5cc57c287a8f8c6c3e7e83ac898b59003c3c39da20e238659b09f1df157216dafe77fb079590574e4b4123bf304f18960ac407b2405de4825cc00c35061d04805f2451cfe3e026bb617a8e284ae1bb816526a9c506a33186bdfacbd016102199b0ef23d253849057f884b691e22de105d33dd87777305d5ea3c564ff00af3dfa4db7f4b2aa305bda33a2476a6b05cce69d61d9f75f986dfc4527af3f19102cb5b69b2842d6d7e9e90e8d575adbc16ad8a3003209e911b2a11d4e200225ec90847799d4821848766737493f708ece7a692e4885692d011ceade273fc4041c5d41854376102a27aceaa0aaa4c3257eb59982599d817fb2605623931306009cb304bbce9135174946a02bb039b7995536a8c4dbb218259ddb2c1c5b9dd411f093e4e518d590f06f8c106fa0f4df4b40f10db27b63d53eb4decb2481d15a1b1f9a4dcf66168ae5f6d7c79354ec366e79b5875482aed5d9a686fdb62b17630dc56a8b87bc1dcb6329bf2d19a56110dcc82f939acb1a6e70e994bb94ce7ff31a3b1db0cf696f5c842eabdc0a6e9d5372ecb94426fc8faef668ab3d9576705e304a945cf241ce3038a11c6f5622a3422ca25d6d0040b99e5cc4e4b39843b7d72534843b4222447511a5174d7c860e10896b36b505a0e16e37cdf8ac5691664e1e5ea49b6f6f35384f56d9faa8b6aa800fd2444e741c31814b3bb2c8c6cdf828187c46187808eac8eed732b528bd82ac8f43e6db3dcd19ed12205172f578245a1589aff49acc083e14f1caa589eabd4d490f6940acbabb055c05319dcded45bf21f0d43708de5ca2383859008ca641deed357917f5ec7ba58101d82a5cc8a6f524f19284b599173697a880f26ba158d39dde2cf107c3dec0a9723aa76b6bad28134544a94eff974c94c041ba956834fb7b8a20cb3f9594b54e896704c00828401eb747c909f8e2fd22eab075a9078c84b13b6bbac635ae9abaadb151c6e827b606742f6ff2939ada26729759830191df4708924fea7ebfebcff8f927d22df05724b550de94617b485e6049826fe78c7dcef0a4b2d23fcfd60d744b89dd19180f1b06d00b6b4c79b71bd446267e49ceaf11951b2d9082b09d0e3489fe070a744a0e64fa7d24d7703e2e82a7f26381ca659b1f66255b7efd0df075b6ba4de7a085030738dcb43875953999c2f7d5392df24b864833ccc9bca50062a633f8e7b8150bb74b7a7c537b12c72b109f8c212bdc542940fcbe580040f301baf107ae48ae17da9f15e0d3439be0bd2a45788973bf8745d03428478327de779fb78c055f53138a8e8df3e15405872ddae7b1ce4515871a20b0787503e861c20abb64c517e0042a2680e7a04527301326182626e560ba34cbcd567a6fbb797077ae12ee618312c49cbfef179c6440930137664d6cc87902a61de833c0aed0a616790b8a9ff71c492e8944bc3f60fe2de37788fef01f939351f4b1fc27098590ebadd75746f60b012641c5ef934924a686fb002c329d97277967b2da3706e342c7418340cef72d0392b4fae0d4ac069893efedb6739f3bf6c4ccc98c201a908dd8f2c46ec94720c00cce419319dfc91c3b842b3e8591e284dca480d39268d31117cdd8efc4e520f283358f61913978ac3430c515e7517268d57226dfab2e09755fd0fbb8654723c336ff3932cd6cd57377fc84ad99e65e92e3e62a10cd8cb84d1eabe63874b70602ec92c43565c29787cfbf0eb1066ce59023d4ae6b066217bdc050dfad86e31c4e357566f631a0ec0d3fa61c328756f7e8e9d5b3b7c8f86cbc08daabc2e8f31bf8eb84ff8a4a6db3643f1495b21c73cad93e3e2fd220aaf09a29cf4c9c5864053a1331b441aa1c585625e656e3e58df740cf3d457c40b0429e10e67b7544310643409645badd52389b0023568f354686018d5cc617ca0be53ea64144ab52b5109a28e74e789766193593c8bad70d10b403c06dfd7ca3b97079f9a4974f563a19019c8c895f9372f50226c3b0976ba2dcba703af39b1c50c7a46b720ba7d6491ee9143c7b653930901b4617eac146fee04757443f36c7156af58c8bb33670bf2928276bea49e7849cc27cafdf183902bfbed16820c68c2238e877290b90a118068947b5a8977360e5a952e0036257dee5e0443433bc39b931f3a78a42a124210a51c58cfd80c56b67a52a3238b4a47524f9e269369e91ec03c1b7ec246c04420d0035d930d1a4936dae183715bee51356873764905f7e7ca33c3f31a56996e22caba046602b6b24130d134eccc1cfaa4eeb4a5eba6fa0cbc022057243d8d478035a50d38b9c15b98af87ef36ccfcb1a41265398b117976d06a7b604b0945f6724c34befafe430b6e687635eb05e98b27b9e57fc05ceac658bbf43be2b055ceb12385938b72f3e4e0ec92cbec13b6b816fa60b5404b418c33fe661142a616b6e24e8f9c3bb35d37e457d1dac5b9c74f471beaf7f8f4d26f608f520ccf0e113801460fb5f0d9b7c6ec1f81756e4486d34752010a0dfd19ff23b9bab9cdcfb1df1251615cb508e4922ecfab788decf45b7e877c73e46391f8e7368dbc5f351ebce144bd31203ce49b741425627c097ec248008f704ab6321f4c59e6e6c1adcf21d255b9c0abc5e5d1e913335b2f5c18fed758628396b06073450bdc8e51b9ac17a15988467eb7f3ec60395c779f28a2a1d9998f24515ca5b47eb5cc29f875c9d1a1165b3fa07fd3b99b18269fa72901035818a9bd02031ad308ff76ede118c97350024db3130d1c0b83bc7b65c3bb1f886c3996c4cd8939139aeb8e749c34ff131274570245f1e762ee28f9876c9e240e56dbafeda6fe7047f8f56148b0440f23f0d011a69b14d6dc35825881a3eba8c695d89526348a2e783135364552adcb2de2bd4bb42f48491930f07c20e89c9f87dc463d93e72a7162f518cab3474af3cd60888cd45bad31620195330ad1fa68c7b800b83761b351b440ee1542ebddff7fc14b018c913564c935e83398d0e28e5cbfd160707ca15b6e4efde70def302103524f2d9162a684c86e6dd17b74948e34b7523839b5557457dae8b84d9679010b82b4f9b6ccffc1777904eda4e4583e195476f62b6b2fc96fa400281d935327130216f637efce4bd7967925161f603ef63854cd24c1637de8a8fb2fcc7031bfba6108270c3dcf4e240a99c15115a88522efafbcb2905c304d35cc0db64799797a10e49bd25c35c762064bf13db5ed47baaf7dd7c0f4a4e768926eb2b5f9f2b7e9b0780258805e32665ef178dd88e89fbeb7c2dbdc7f4c7672d6cfa8a46075a304407aba54c51707cf93558bf6f2bc15e138ce94d38ef015c8abdb37dc627159b601389b40547fdd18c07caa2e8c156ac50719ee2e1e57d111bd79ae6d03a1297a40c36bfcb7110608d12d503545f26013b67f6883ade09afc1a9ec2287c5e0b4fc18961675c195b7025239a4f728b6354262a3b333a9f49a39f5f740652ce6fb271e03230bc5559de174f64dd0d90f02b5f2a2eaf004278215a9d856042f48cb3e2028a458428b511f716a00e9432a28ba15a6eb8cfd6a26c33641dcf788c1f6a71cfdd7f6043c8cf84ada544f719aad323ec1d2ee9f5a4888114e008cf4c467cd2b509709b141b7d822abeb2f1122d0735485f29454e0077ef21016ddcd68fbe9","public_inputs_hex":"0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7001cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}}}, + "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/circuits/benchmarks/results_insecure/integration_summary.json b/circuits/benchmarks/results_insecure/integration_summary.json index ddee53b6d..3d536b39c 100644 --- a/circuits/benchmarks/results_insecure/integration_summary.json +++ b/circuits/benchmarks/results_insecure/integration_summary.json @@ -119,15 +119,15 @@ "timings_seconds": [ { "label": "Starting trbfv actor test", - "seconds": 0E-9 + "seconds": 0e-9 }, { "label": "Setup completed", - "seconds": 3.071508500 + "seconds": 3.0715085 }, { "label": "Committee Setup Completed", - "seconds": 20.228290750 + "seconds": 20.22829075 }, { "label": "Committee Finalization Complete", @@ -155,7 +155,7 @@ }, { "label": "Entire Test", - "seconds": 410.211371500 + "seconds": 410.2113715 } ], "folded_artifacts": { diff --git a/crates/utils/src/committee_hash.rs b/crates/utils/src/committee_hash.rs index a5ab1e11f..798ecd244 100644 --- a/crates/utils/src/committee_hash.rs +++ b/crates/utils/src/committee_hash.rs @@ -46,10 +46,7 @@ pub fn committee_hash_limbs_from_addresses(addresses: &[Address]) -> CommitteeHa /// Field hex strings (`0x…`, 32 bytes) for Noir witness `committee_hash_hi` / `committee_hash_lo`. pub fn committee_hash_field_hex(addresses: &[Address]) -> (String, String) { let limbs = committee_hash_limbs_from_addresses(addresses); - ( - field_hex_from_b256(limbs.hi), - field_hex_from_b256(limbs.lo), - ) + (field_hex_from_b256(limbs.hi), field_hex_from_b256(limbs.lo)) } fn field_hex_from_b256(value: B256) -> String { 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(); } }