diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml index 97e41746cf..4f3c21f825 100644 --- a/.github/workflows/releases.yml +++ b/.github/workflows/releases.yml @@ -367,7 +367,7 @@ jobs: - name: Pull circuit artifacts id: pull run: | - SOURCE_HASH=$(pnpm tsx scripts/build-circuits.ts hash) + SOURCE_HASH=$(pnpm tsx scripts/build-circuits.ts hash --preset insecure-512 --committee all) echo "source_hash=$SOURCE_HASH" >> $GITHUB_OUTPUT if git fetch origin circuit-artifacts 2>/dev/null; then diff --git a/agent/CONTEXT.md b/agent/CONTEXT.md index 5dfd6e7727..2a6bd5fb4d 100644 --- a/agent/CONTEXT.md +++ b/agent/CONTEXT.md @@ -96,14 +96,14 @@ opentelemetry/tracing. ## Circuit map (IDs ↔ `CircuitName` in `crates/events`) -- **DKG** (`circuits/bin/dkg/`): C0 `pk` (PkBfv) · C2a `sk_share_computation` · C2b - `e_sm_share_computation` · C3 `share_encryption` · C4 `share_decryption` +- **DKG** (`circuits/bin/dkg/`): C0 `pk` (PkBfv) · C2a `sk_share_computation_chunk` · C2b + `esm_share_computation_chunk` · C3 `share_encryption` · C4 `share_decryption` - **Threshold** (`circuits/bin/threshold/`): C1 `pk_generation` · C5 `pk_aggregation` · P3 `user_data_encryption_ct0/ct1` (+ wrapper) · C6 `share_decryption` · C7 `decrypted_shares_aggregation` -- **Recursive aggregation** (`circuits/bin/recursive_aggregation/`): fold kernels (`c2ab_fold`, - `c3_fold`, `c6_fold`, `node_fold`, `nodes_fold`, …) and the top-level `dkg_aggregator` / - `decryption_aggregator`, which produce the on-chain Honk verifiers (committed only for - `(insecure-512, minimum)`). +- **Recursive aggregation** (`circuits/bin/recursive_aggregation/`): fold kernels + (`c2ab_chunk_fold`, `c3_fold`, `c6_fold`, `node_fold`, `nodes_fold`, …) and the top-level + `dkg_aggregator` / `decryption_aggregator`, which produce the on-chain Honk verifiers (committed + only for `(insecure-512, minimum)`). - `config` circuit validates preset constants (CRT moduli, bounds, parity matrices). Parity matrices are generated by the Rust `generate_parity_matrices` binary — never hand-edit. diff --git a/agent/INVARIANTS.md b/agent/INVARIANTS.md index 034d349859..e507004cd1 100644 --- a/agent/INVARIANTS.md +++ b/agent/INVARIANTS.md @@ -165,6 +165,8 @@ skip-proof feature containment (`pnpm check:invariants`, baselines in public-input layout and must be redeployed on committee change. - Parity matrices (`parity_{insecure,secure}.nr`) are derived artifacts regenerated from preset `QIS` + committee `(N, T)`; hand-edits are caught by regenerate-and-diff. +- Smudging constants (`smudging.nr`) are derived artifacts regenerated from the BFV preset and + committee size. C1 and C2b use the same generated bit width. ### Noir / Barretenberg compatibility @@ -183,8 +185,10 @@ skip-proof feature containment (`pnpm check:invariants`, baselines in ### DKG / threshold structure - SK splits into N shares; any **M+1** reconstruct/decrypt. — `flow-trace/04` -- `party_id` derives from the finalized committee normalized by ascending address; 1-indexed, - strictly increasing. Active aggregator = lowest non-expelled `party_id`. — `ARCHITECTURE.md`; +- DKG runtime and NodeFold `party_id` derives from the finalized committee normalized by ascending + address; it is zero-indexed and strictly increasing. Active aggregator = lowest non-expelled + `party_id`. Decryption-aggregator Shamir coordinates are a separate 1-indexed circuit format and + translate to zero-indexed registry slots at the wrapper boundary. — `ARCHITECTURE.md`; `flow-trace/04` - DKG aggregation receives **exactly H** canonical honest NodeFold proofs (unique in-range party IDs) and **exactly N** ordered committee addresses; every preset has `H < N` — never assert @@ -222,6 +226,16 @@ skip-proof feature containment (`pnpm check:invariants`, baselines in - **No proof-disabled bypass (C-02):** both final verifier calls are mandatory in production; `skip_proof_aggregation` works only under the `test-only-skip-proof-aggregation` Cargo feature; production verifiers reject placeholder C5/C7 proofs. — INDEX concern #32 +- **Complete DKG recursive VK binding:** the DKG proof carries the canonical `NodeFold` VK hash and + a recursive VK manifest. The manifest binds the C0/C1, C2 chunk/batch/finalizer/C2AB, C3 + leaf/fold/kernel/C3AB, C4 leaf/C4AB, and NodesFold kernel VK hashes. NodesFold and C3Fold bind + both the current accumulator VK and each prior accumulator's expected kernel or fold VK hash, and + every fold step asserts the accumulator's child-VK hash equals the inner proof's VK hash (IF-005); + a substituted inner VK fails witness generation. C2 terminal proofs + (`SkC2ChunkFinalize`/`ESmC2ChunkFinalize`) are bound to the deployment-time chunk-leaf and + `C2ChunkBatch` VK anchors before generic verification (IF-006). `BfvPkVerifier` checks these + values against deployment-time anchors before it calls the Honk verifier. — `dkg_aggregator`, + `BfvPkVerifier` - Circuit soundness fixes to preserve: `ModU64::div_mod` verifies `result*divisor == dividend (mod modulus)` (IF-001); C7 compares **every** decoded coefficient, including zeros, to the claimed message (IF-002). diff --git a/agent/flow-trace/00_INDEX.md b/agent/flow-trace/00_INDEX.md index 516d09b9cc..02bd7248f0 100644 --- a/agent/flow-trace/00_INDEX.md +++ b/agent/flow-trace/00_INDEX.md @@ -196,12 +196,14 @@ _Found during source-code cross-referencing of these trace documents._ ### Circuit Audit Remediations -| Finding | Status | Implemented behavior | -| ----------------------------------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **IF-001 — unconstrained U64 modular division** | Fixed | `ModU64::div_mod` verifies the hinted quotient satisfies `result * divisor == dividend (mod modulus)`; the Noir suite includes a non-invertible-divisor regression test. | -| **IF-002 — conditional C7 decoding equality** | Fixed | C7 compares every decoded coefficient with the claimed message, including zero coefficients; a focused regression test rejects a nonzero decoded value claimed as zero. | -| **IF-003 — decryption proof phase and party binding** | Fixed | `decryption_aggregator` requires 1-indexed, strictly increasing party IDs, while `BfvDecryptionVerifier` checks the surfaced SK/ESM commitments against the E3's registry-backed DKG anchors. | -| **IF-004 — ciphertext commitment binding** | Fixed | The off-chain SAFE commitment is stored at ciphertext publication, propagated as a final decryption-proof public input, and compared on-chain without attempting BFV decoding or Poseidon2 execution in Solidity. | +| Finding | Status | Implemented behavior | +| ----------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **IF-001 — unconstrained U64 modular division** | Fixed | `ModU64::div_mod` verifies the hinted quotient satisfies `result * divisor == dividend (mod modulus)`; the Noir suite includes a non-invertible-divisor regression test. | +| **IF-002 — conditional C7 decoding equality** | Fixed | C7 compares every decoded coefficient with the claimed message, including zero coefficients; a focused regression test rejects a nonzero decoded value claimed as zero. | +| **IF-003 — decryption proof phase and party binding** | Fixed | `decryption_aggregator` requires 1-indexed, strictly increasing party IDs, while `BfvDecryptionVerifier` checks the surfaced SK/ESM commitments against the E3's registry-backed DKG anchors. | +| **IF-004 — ciphertext commitment binding** | Fixed | The off-chain SAFE commitment is stored at ciphertext publication, propagated as a final decryption-proof public input, and compared on-chain without attempting BFV decoding or Poseidon2 execution in Solidity. | +| **IF-005 — fold-step child VK binding** | Fixed | Every `c3_fold`/`nodes_fold` step asserts the accumulator's child-VK hash equals the verifying key hash of the inner proof folded that step. A substituted (even canonical) inner VK now fails witness generation instead of passing silently. Regression-covered by `fold_vk_binding_adversarial_tests`. | +| **IF-006 — C2 terminal VK binding at ingress** | Fixed | Before generic (bb) verification, each `SkC2ChunkFinalize`/`ESmC2ChunkFinalize` proof is checked against the deployment-time anchors: the child field equals the canonical chunk-leaf VK hash for that proof type, the final field equals the canonical `C2ChunkBatch` VK hash, the public-signal length matches the frozen committee and preset, and every field is canonical. A mismatch marks the signed proof invalid. Covered by `validate_c2_terminal_proof`. | ### Protocol Design Concerns diff --git a/agent/flow-trace/04_DKG_AND_COMPUTATION.md b/agent/flow-trace/04_DKG_AND_COMPUTATION.md index e63e962465..49c2339d5e 100644 --- a/agent/flow-trace/04_DKG_AND_COMPUTATION.md +++ b/agent/flow-trace/04_DKG_AND_COMPUTATION.md @@ -267,9 +267,11 @@ ProofRequestActor receives ThresholdSharePending │ ├─ C2a: ComputeRequest::zk(ZkRequest::ShareComputation { kind: SK }) │ ├─ C2b: ComputeRequest::zk(ZkRequest::ShareComputation { kind: ESM }) │ ├─ C3a[i]: ComputeRequest::zk(ZkRequest::ShareEncryption { recipient, row }) -│ │ → One per recipient party × modulus row +│ │ → One per recipient party × threshold-secret modulus row; the paired DKG parameters +│ │ define the ciphertext CRT limbs │ └─ C3b[i]: ComputeRequest::zk(ZkRequest::ShareEncryption { esi_idx, recipient, row }) -│ → One per ESI × recipient party × modulus row +│ → One per ESI × recipient party × threshold-secret modulus row; the paired DKG +│ parameters define the ciphertext CRT limbs │ ├─ 3. ZkActor generates proofs via bb binary (in parallel via multithread): │ → Each proof takes 1-10 seconds depending on circuit complexity @@ -305,16 +307,66 @@ ProofRequestActor receives ThresholdSharePending → Ensures no incomplete data is gossiped ``` -**C2 proofs:** For each C2a/C2b request, the prover builds a **recursive** proof for -`sk_share_computation` / `e_sm_share_computation`. That `Proof` is what `PendingThresholdProofs` -stores and what gets ECDSA-signed for gossip (`ProofType::C2aSkShareComputation` / -`C2bESmShareComputation`). The old generic `recursive_aggregation/wrapper/*` circuits and two-proof -`recursive_aggregation/fold` were removed; aggregation is done by ad-hoc Noir bins under -`circuits/bin/recursive_aggregation/` (e.g. `c2ab_fold`, `c3ab_fold`, `c6_fold`, `node_fold`, -`nodes_fold`, `dkg_aggregator`, `decryption_aggregator` — `nodes_fold` chains `H` `node_fold` proofs -for `dkg_aggregator`; `decryption_aggregator` folds C6 via non-ZK `c6_fold` then checks C7 with ZK). -The per-circuit `wrapper/` Noir step was removed; aggregator response structs no longer carry a -`wrapped_proof` field — the inner recursive proof itself is what flows between stages. +**C2 proofs:** For each C2a/C2b request, the prover builds a **recursive** proof per coefficient +chunk via `sk_share_computation_chunk` / `esm_share_computation_chunk`. The chunk proofs are batched +(`C2ChunkBatch`) and verified in the type-bound terminal `SkC2ChunkFinalize` / `ESmC2ChunkFinalize`. +That terminal `Proof` is what `PendingThresholdProofs` stores and what gets ECDSA-signed for gossip +(`ProofType::C2aSkShareComputation` / `C2bESmShareComputation`). The old generic +`recursive_aggregation/wrapper/*` circuits and two-proof `recursive_aggregation/fold` were removed; +aggregation is done by ad-hoc Noir bins under `circuits/bin/recursive_aggregation/` (e.g. +`c2ab_chunk_fold`, `c3ab_fold`, `c6_fold`, `node_fold`, `nodes_fold`, `dkg_aggregator`, +`decryption_aggregator` — `nodes_fold` chains `H` `node_fold` proofs for `dkg_aggregator`; +`decryption_aggregator` folds C6 via non-ZK `c6_fold` then checks C7 with ZK). The per-circuit +`wrapper/` Noir step was removed; aggregator response structs no longer carry a `wrapped_proof` +field — the inner recursive proof itself is what flows between stages. + +Every `c3_fold`/`nodes_fold` step asserts the accumulator's child-VK hash (`acc_public_inputs[0]`) +equals the verifying key hash of the inner proof folded that step (IF-005), so a substituted inner +VK fails witness generation. Fold accumulators start from the canonical leaf/kernel proof, whose VK +hash is committed by that circuit's public inputs. The C3 and NodesFold genesis kernels expose the +same six-field accumulator prefix as their corresponding fold circuits, including the expected +kernel and fold VK hashes. This lets the genesis proof enter the first fold step without a +public-input shape conversion. + +The chunked C2 path keeps the same signed proof multiplicity. For each C2a/C2b request, Rust +generates one type-bound recursive proof per chunk. The chunk size is pinned to 512 coefficients; +the `zk_cli --chunk-size` option rejects any other value. The production multithread path uses the +compiled default chunk size. Rust groups the chunk proofs into fixed recursive batches and verifies +all batches in a type-bound terminal circuit. The terminal circuits reconstruct a root commitment +for the secret and for each recipient share. The signed response contains only the type-bound +terminal `SkC2ChunkFinalize` or `ESmC2ChunkFinalize` proof. `C2ChunkBatch` binds the ordered chunk +indices and chunk commitments. + +The chunk size is one value across the DKG pipeline: it threads from the sample into the C2 (share +computation), C3 (share encryption), and C4 (share decryption) witness computation and into the +generated `configs.nr` values (`SHARE_COMPUTATION_CHUNK_SIZE` / `SHARE_COMPUTATION_N_CHUNKS`), so +the witness always matches the circuit parameters the artifacts were generated against. The +generated `configs.nr` `N` and `L` values come from the same parameter object that drives the +witness computation. The compiled circuits and committed `configs.nr` defaults use chunk size 512; +`--chunk-size` accepts only 512, so generated artifacts always match the compiled C2/C3/C4 circuits. +The chunk layout (chunk/batch counts) is derived from a single `C2ChunkLayout` in `e3-zk-prover`, +never from independent runtime constants. + +C1 and C2b import their smudging-noise bit width from the active committee's generated +`smudging.nr`. C1 also imports the matching bound. The build uses the same Rust calculation as +witness generation, so a committee change cannot leave either circuit on another committee's range. + +Before the generic recursive (bb) verification of a received C2 proof, the node validates the +`SkC2ChunkFinalize`/`ESmC2ChunkFinalize` public signals against the deployment-time anchors +(IF-006): the child field must equal the canonical chunk-leaf VK hash for that proof type, the final +field must equal the canonical `C2ChunkBatch` VK hash, the signal length must match the frozen +committee and preset, and every field must be canonical. A mismatch marks the signed proof invalid. + +C1, normal C2, C3, C4 per-share checks, and `NodeFold` now use the same root commitment scheme. C3 +fold steps bind each inner proof's recipient and modulus indices to its accumulator slot, including +the first genesis step. C3Fold and NodesFold also bind the current accumulator VK and the prior +step's expected kernel or fold VK hash. C4 binds every decrypted row to the recipient party's +zero-based C2 commitment domain. C4 aggregate commitments remain on the legacy aggregate scheme at +the C4-to-C6 boundary until that boundary is migrated. The terminal C2 proofs surface the canonical +SK/ESM chunk VK hashes through C2AB, NodeFold, and DkgAggregator. Each recursive fold also +propagates a VK manifest for the child proofs that it verifies. The final DKG proof carries the +`NodeFold` VK hash and this complete manifest; `BfvPkVerifier` compares them with its +deployment-time anchors before it accepts the final proof. **Ciphernode / aggregator integration:** `ZkRequest::FoldProofs` was removed. The multithread actor implements `ZkRequest::NodeDkgFold` (full per-node pipeline to a `NodeFold` proof), @@ -816,17 +868,20 @@ Compute provider runs computation on encrypted data: │ │ → Can only publish once │ │ │ 5. require(activeCount >= threshold[0]) │ │ │ → The request-time committee is still viable │ -│ │ 6. Save output hash and SAFE commitment │ -│ │ Set stage and decryption deadline │ -│ │ → A later revert restores all prior state │ +│ │ 6. Require no ciphertext publication is in progress │ +│ │ Set the per-E3 publication guard │ +│ │ → Reentrant publication attempts revert │ │ │ 7. schemeVerifier.verify(...) │ │ │ → Checks the protocol fields in the compute receipt│ │ │ → Must return true │ │ │ 8. e3Program.verify(...) │ │ │ → Checks the application fields in the same receipt│ │ │ → Must return true │ -│ │ 9. Emit CiphertextOutputPublished(...) │ -│ │ 10. Emit E3StageChanged(CiphertextReady) │ +│ │ 9. Save output hash and SAFE commitment │ +│ │ Set stage and decryption deadline │ +│ │ Clear the publication guard │ +│ │ 10. Emit CiphertextOutputPublished(...) │ +│ │ 11. Emit E3StageChanged(CiphertextReady) │ │ │ } │ │ └─────────────────────────────────────────────────────────┘ ``` diff --git a/agent/flow-trace/05_FAILURE_REFUND_SLASHING.md b/agent/flow-trace/05_FAILURE_REFUND_SLASHING.md index be34503b6e..37956e60a7 100644 --- a/agent/flow-trace/05_FAILURE_REFUND_SLASHING.md +++ b/agent/flow-trace/05_FAILURE_REFUND_SLASHING.md @@ -208,7 +208,7 @@ REQUESTER claims: ├─ Transfer requesterAmount in the per-E3 fee token └─ Emit RefundClaimed(e3Id, requester, amount) -HONEST NODE'S BOND OWNER claims: +FROZEN REWARD RECIPIENT claims an honest-node reward: E3RefundManager.claimHonestNodeReward(e3Id, operator) │ ├─ require(distribution calculated) diff --git a/circuits/README.md b/circuits/README.md index 36206509f7..9f8fc9a6e9 100644 --- a/circuits/README.md +++ b/circuits/README.md @@ -43,22 +43,22 @@ how phases, commitments, and circuit IDs line up end to end, read [Cryptography](https://docs.theinterfold.com/cryptography) (source: [`docs/pages/cryptography.mdx`](../docs/pages/cryptography.mdx)). -**C2** uses **inner** recursive proofs plus an optional **wrapper**: `sk_share_computation` -(**C2a**) and `e_sm_share_computation` (**C2b**) prove the Shamir-share computation; the wrapper -`recursive_aggregation/wrapper/dkg/share_computation` (`CircuitName::ShareComputation`) re-verifies -a single inner C2 proof at a time and compresses public inputs for folding / aggregation. Gossip and -threshold signing use the **inner** proof; the wrapper is produced separately when node proof -aggregation is enabled. +**C2** uses **chunked** recursive proofs: the `sk_share_computation_chunk` (**C2a**) and +`esm_share_computation_chunk` (**C2b**) leaves prove one coefficient chunk of the Shamir-share +computation each. Chunk proofs are grouped by `c2_chunk_batch`, verified in the type-bound terminal +`sk_c2_chunk_finalize` / `esm_c2_chunk_finalize`, and the two terminals are folded by +`c2ab_chunk_fold`. Gossip and threshold signing use the terminal recursive proof; folding and +aggregation consume it. ### DKG (`bin/dkg/`) -| Path | ID | `CircuitName` | Role | -| ------------------------ | --- | --------------------- | --------------------------------------------- | -| `pk` | C0 | `PkBfv` | Commit to individual BFV public key | -| `sk_share_computation` | C2a | `SkShareComputation` | Secret-key track Shamir shares (`y`) | -| `e_sm_share_computation` | C2b | `ESmShareComputation` | Smudging-noise track Shamir shares (`y`) | -| `share_encryption` | C3 | `ShareEncryption` | BFV encryption of shares under recipient keys | -| `share_decryption` | C4 | `DkgShareDecryption` | Decrypt shares; aggregate; commitments for P4 | +| Path | ID | `CircuitName` | Role | +| ----------------------------- | --- | -------------------------- | --------------------------------------------------- | +| `pk` | C0 | `PkBfv` | Commit to individual BFV public key | +| `sk_share_computation_chunk` | C2a | `SkShareComputationChunk` | Secret-key track Shamir shares, one chunk (`y`) | +| `esm_share_computation_chunk` | C2b | `ESmShareComputationChunk` | Smudging-noise track Shamir shares, one chunk (`y`) | +| `share_encryption` | C3 | `ShareEncryption` | BFV encryption of shares under recipient keys | +| `share_decryption` | C4 | `DkgShareDecryption` | Decrypt shares; aggregate; commitments for P4 | ### Threshold (`bin/threshold/`) @@ -74,15 +74,21 @@ aggregation is enabled. ### Recursive aggregation (`bin/recursive_aggregation/`) -| Path | `CircuitName` | Role | -| ------------------------------- | ------------------ | ---------------------------------------------------------------------------------- | -| `fold` | `Fold` | Fold two wrapper outputs | -| `wrapper/dkg/share_computation` | `ShareComputation` | C2 wrapper: one inner C2a or C2b proof per wrap; VK genealogy + compressed outputs | -| `wrapper/dkg/*` | — | Verifies inner DKG proofs; compresses public inputs | -| `wrapper/threshold/*` | — | Verifies inner threshold proofs; compresses public inputs | - -Wrapper parameters are documented in -[`wrapper/README.md`](bin/recursive_aggregation/wrapper/README.md). +| Path | `CircuitName` | Role | +| ------------------------------------------------ | -------------------- | --------------------------------------------------------------------------------- | +| `c2_chunk_batch` | `C2ChunkBatch` | Groups ordered C2 chunk proofs into a fixed-size recursive batch | +| `sk_c2_chunk_finalize` | `SkC2ChunkFinalize` | Type-bound terminal: verifies all SK chunk batches, reconstructs root commitments | +| `esm_c2_chunk_finalize` | `ESmC2ChunkFinalize` | Type-bound terminal: ESM track equivalent of `sk_c2_chunk_finalize` | +| `c2ab_chunk_fold` | `C2abChunkFold` | Fold the two C2 terminal proofs (SK + ESM) | +| `c3_fold`, `c3_fold_kernel` | — | C3 recursive folding / kernel | +| `c6_fold`, `c6_fold_kernel` | — | C6 recursive folding / kernel | +| `c3ab_fold`, `c4ab_fold` | — | Recursive folds for the C3/C4 and C2-to-C4 chains | +| `node_fold` / `nodes_fold` (`nodes_fold_kernel`) | — | Fold a node's `node_fold` chain (`H`-deep) before `dkg_aggregator` | +| `dkg_aggregator` | — | Top-level DKG on-chain Honk verifier | +| `decryption_aggregator` | — | Top-level decryption on-chain Honk verifier | + +For the recursive aggregation flow, see +[`agent/flow-trace/04_DKG_AND_COMPUTATION.md`](../agent/flow-trace/04_DKG_AND_COMPUTATION.md). ### Configuration diff --git a/circuits/benchmarks/README.md b/circuits/benchmarks/README.md index 17cdf3894a..5ae93fe86d 100644 --- a/circuits/benchmarks/README.md +++ b/circuits/benchmarks/README.md @@ -57,16 +57,16 @@ pnpm check:committee pnpm build:circuits --preset insecure-512 --committee minimum ``` -**All `(preset, committee)` pairs are supported.** The parity matrices in -`circuits/lib/src/configs/committee//parity_{insecure,secure}.nr` are auto-generated by the -`generate_parity_matrices` Rust binary, invoked from `scripts/build-circuits.ts` whenever the -committee changes. The values are deterministic functions of the committee's `(N, T)` and the -preset's threshold `QIS`, so any pair compiles and proves correctly. +**All `(preset, committee)` pairs are supported.** The parity matrices and smudging constants in +`circuits/lib/src/configs/committee//` are generated by the `generate_parity_matrices` Rust +binary. `scripts/build-circuits.ts` invokes the binary whenever the committee changes. The values +are deterministic functions of the committee and BFV preset, so each pair compiles with the same +parameters that Rust uses for witness generation. **Skipping the build step:** `pnpm check:committee` runs in the pre-push hook and fails the push if -`active.nr`, `utils.ts`, `.active-preset.json`, or any committed `parity_*.nr` disagree with what -`build:circuits --committee ` would produce. Always use `pnpm build:circuits --committee` to -switch; manual edits to those files will trip the check. +`active.nr`, `utils.ts`, `.active-preset.json`, or any generated committee artifact disagrees with +what `build:circuits --committee ` would produce. Always use +`pnpm build:circuits --committee` to switch; manual edits to those files will trip the check. ### Proof aggregation and folding (integration) diff --git a/circuits/benchmarks/config.json b/circuits/benchmarks/config.json index 6bedcf83fb..8d1db4540b 100644 --- a/circuits/benchmarks/config.json +++ b/circuits/benchmarks/config.json @@ -2,8 +2,8 @@ "circuits": [ { "name": "config", "modes": ["secure"] }, "dkg/pk", - "dkg/sk_share_computation", - "dkg/e_sm_share_computation", + "dkg/sk_share_computation_chunk", + "dkg/esm_share_computation_chunk", "dkg/share_encryption", "dkg/share_decryption", "threshold/user_data_encryption_ct0", diff --git a/circuits/benchmarks/results_insecure_agg_micro/benchmark_run_meta.json b/circuits/benchmarks/results_insecure_agg_micro/benchmark_run_meta.json deleted file mode 100644 index cbe70d92cb..0000000000 --- a/circuits/benchmarks/results_insecure_agg_micro/benchmark_run_meta.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "benchmark_mode": "insecure", - "bfv_preset_subdir": "insecure-512", - "committee": "micro", - "proof_aggregation": true, - "multithread_jobs": 13, - "verbose": true, - "nodes_spawned": 20, - "committee_size_n": 3, - "committee_size_h": 3, - "committee_threshold_t": 1, - "network_model": "in_process_bus", - "testmode_harness": true -} diff --git a/circuits/benchmarks/results_insecure_agg_micro/crisp_verify_gas.json b/circuits/benchmarks/results_insecure_agg_micro/crisp_verify_gas.json deleted file mode 100644 index acd94b141f..0000000000 --- a/circuits/benchmarks/results_insecure_agg_micro/crisp_verify_gas.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "verify_gas": { - "dkg": 3125331, - "user": 2973085, - "dec": 3640972 - }, - "source": "folded_proof_export_plus_crisp_verify_test", - "artifact_sizes_bytes": { - "dkg": { - "proof": 10944, - "public_inputs": 480 - }, - "dec": { - "proof": 10944, - "public_inputs": 3552 - } - }, - "calldata_gas": { - "dkg": { - "proof": 170040, - "public_inputs": 6144, - "total": 176184 - }, - "dec": { - "proof": 169968, - "public_inputs": 17280, - "total": 187248 - } - }, - "integration_summary": { - "integration_test": "test_trbfv_actor", - "benchmark_config": { - "mode": "insecure", - "bfv_preset_subdir": "insecure-512", - "bfv_preset": "InsecureThreshold512", - "lambda": 2, - "proof_aggregation_enabled": true, - "multithread_concurrent_jobs": 13, - "committee_h": 3, - "committee_n": 3, - "committee_t": 1, - "nodes_spawned": 20, - "network_model": "in_process_bus", - "testmode_harness": true - }, - "proof_aggregation_enabled": true, - "dkg_fold_attestation_verifier": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", - "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 13, "cores_available": 14 }, - "operation_timings": [ - { "name": "CalculateDecryptionKey", "avg_seconds": 0.004320611, "runs": 3, "total_seconds": 0.012961834 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 0.021778305, "runs": 3, "total_seconds": 0.065334917 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 0.020453667, "runs": 1, "total_seconds": 0.020453667 }, - { "name": "GenEsiSss", "avg_seconds": 0.007597111, "runs": 3, "total_seconds": 0.022791334 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 0.010288736, "runs": 3, "total_seconds": 0.03086621 }, - { "name": "NodeDkgFold/c2ab_fold", "avg_seconds": 8.096940472, "runs": 3, "total_seconds": 24.290821416 }, - { "name": "NodeDkgFold/c3a_fold", "avg_seconds": 35.482067235, "runs": 3, "total_seconds": 106.446201707 }, - { "name": "NodeDkgFold/c3ab_fold", "avg_seconds": 7.882662333, "runs": 3, "total_seconds": 23.647987 }, - { "name": "NodeDkgFold/c3b_fold", "avg_seconds": 35.908104792, "runs": 3, "total_seconds": 107.724314376 }, - { "name": "NodeDkgFold/c4ab_fold", "avg_seconds": 8.13719925, "runs": 3, "total_seconds": 24.41159775 }, - { "name": "NodeDkgFold/node_fold", "avg_seconds": 18.718641194, "runs": 3, "total_seconds": 56.155923584 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 1.611448542, "runs": 1, "total_seconds": 1.611448542 }, - { "name": "ZkDecryptionAggregation", "avg_seconds": 48.859235667, "runs": 1, "total_seconds": 48.859235667 }, - { "name": "ZkDkgAggregation", "avg_seconds": 19.836521125, "runs": 1, "total_seconds": 19.836521125 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 1.224212729, "runs": 6, "total_seconds": 7.345276376 }, - { "name": "ZkNodeDkgFold", "avg_seconds": 114.228116111, "runs": 3, "total_seconds": 342.684348334 }, - { "name": "ZkPkAggregation", "avg_seconds": 0.615287333, "runs": 1, "total_seconds": 0.615287333 }, - { "name": "ZkPkBfv", "avg_seconds": 0.226558361, "runs": 3, "total_seconds": 0.679675084 }, - { "name": "ZkPkGeneration", "avg_seconds": 2.497946278, "runs": 3, "total_seconds": 7.493838834 }, - { "name": "ZkShareComputation", "avg_seconds": 2.40892843, "runs": 6, "total_seconds": 14.453570585 }, - { "name": "ZkShareEncryption", "avg_seconds": 3.950791532, "runs": 24, "total_seconds": 94.818996789 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 3.383825583, "runs": 3, "total_seconds": 10.151476749 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.102920694, "runs": 3, "total_seconds": 0.308762083 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.280129533, "runs": 5, "total_seconds": 1.400647667 } - ], - "operation_timings_total_seconds": 893.088338963, - "operation_timings_metric": "tracked_job_wall", - "phase_timings": [ - { "label": "Starting trbfv actor test", "seconds": 0e-9, "metric": "wall_clock" }, - { "label": "Setup completed", "seconds": 2.9637735, "metric": "wall_clock" }, - { "label": "Committee Setup Completed", "seconds": 20.091043708, "metric": "wall_clock" }, - { "label": "Committee Finalization Complete", "seconds": 0.007389292, "metric": "wall_clock" }, - { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 133.913944, "metric": "wall_clock" }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 146.239215458, "metric": "wall_clock" }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 146.750428209, "metric": "wall_clock" }, - { "label": "Application CT Gen", "seconds": 0.009458917, "metric": "wall_clock" }, - { "label": "Running FHE Application", "seconds": 0.000062875, "metric": "wall_clock" }, - { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 50.48525, "metric": "wall_clock" }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 54.401319083, "metric": "wall_clock" }, - { "label": "Entire Test", "seconds": 224.22015425, "metric": "wall_clock" } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x0000000000000000000000000000000000000000000000055ce842869ecc798800000000000000000000000000000000000000000000000962d271f2a31d7df700000000000000000000000000000000000000000000000cab13fd160fdd78b200000000000000000000000000000000000000000000000000015b9c8ac1f7af00000000000000000000000000000000000000000000000448914ae5744f2cbe0000000000000000000000000000000000000000000000065a47ab8b7ee000b4000000000000000000000000000000000000000000000002a7228f267b2676cd0000000000000000000000000000000000000000000000000001c08f86a603dd00000000000000000000000000000000000000000000000c08e7c3252f76857a000000000000000000000000000000000000000000000006db65bbcdab667efa00000000000000000000000000000000000000000000000a55066ba466957e060000000000000000000000000000000000000000000000000001d9ef76020bd9000000000000000000000000000000000000000000000002b97faf1a204a616a00000000000000000000000000000000000000000000000362df9706d637d11200000000000000000000000000000000000000000000000f0cc2167e89450dc50000000000000000000000000000000000000000000000000001ef17dfe0bf090c486ea55a1ce3e9a1c58c6d280f64c6cc337f4aaa58296bda4c37e368de55080298c60ac27537b0c7a66f2110eaf284b8c1ede53c49f2e385b77545e4a256420867e2ed0213a17a18c04d0b9dd4c887e3d57672e2d949b0053ad848ec932df90bbd48bb079881a76012522639773799dc13cd501fb540f0ce659049daf592d321c39bbd718505dc5221412f4eba8ab36cb884b887395e583b5e5f6773adb4922963e5f0e9811528de1e4d3cd0a804030827fefb63c74509022aaf5c3c39987c2b93049d138d1aa49ff2a71fbd10acbd91e825852dbb160268db8eb12b480ff61497dd6efa3c6957af87f0623eb5f7abb82b17d4db53f3696f4930018071d10e00a8d860ca77e800cb86d452df258dd4f7383c08e0692d8ddcd73a356c7ada78216294e1fad95e0289e0028fa1fc29194d2cbd14df27e9b5c0f0948d3c0725aa1b202607c7910a2af9a6fdbb5365e40af6f2d529bd97b708c1e5466394c8330e0511de6045511cec4ec3203417aee6b3055924840feeee2cec43ba9aa419adbe1a8a812d9af9d03eb346ce5969a7b5fadca3b0f31450b95bf2f0bdc77f7361e401d563c3856b087f8bd2a15d89774d92dc05b38e704faee3e675238b107b02dc23bb6f60966a057bab79050b8e2f6f5e0eaa3eb8e908371d14134ed57a2806560534f9bb4dfa4115e83882a3b2230249b8057ff8e8c08cd1d52245366811ba341a69016d7240ab6dc55923034e998618e95b4c6915158c01443836eb5d253e99203273029ee38fcbb0bb3e0a804d5eac0d2243a734993465dfe0e4f997f204f0304ff20195494c7109597656675719513d51e0509f06342b4bb3a9beb1f505dc1b858d5015a691d14be28720789d7f5331fa146dba6b83dee83542848bfae502099ab605ebc8bd7b733a5f8699828b1be13dc3dd1e77cb7cb4aa01869f67223420eaa1efac3448573bda5d1ca8387ce8d772c9247e12a38fb93c0f517d341ddf2631765b99f8290a1b3ebbb2440848fd666c891bcf6bf3b70a2fbe53f758e42d089f0a311d621d74eda459982c01bccf40cef4fbfcb1b31d9664f6a50375198d06ba84f6e6ab7a1f7d7b1144c79fe1b0a37b9df6d9322854afc5612476a8adf12f6d341b75bd522bd581cdc19afeef48c3ac919a365339f4f128e5bf5f955a350020c07d403078252f358c479535c8c4b819462a33eae0108b1c909d72a954ed1b704afe5c0f2093bf62d4c255e822d7ed58bae64eef3e2fa1f87055a8954f760e2c0c7eb770bb847105517ed113eb7fa577842b56f05f53039ad97bb4e663c40e4ced08d2214235afaee699bc67ad793e80652267b0e49e7eeb82c2245efc22043539b3f5902b00a2682bc5e10149dbc78edc127512a8eb171f570934d796a0040d351b9f54e8d13541f6fd1338a09480bf8a185e5331440781129513d45c8124fb65bc2766404225b3893c5fe4b369e4017f7090463c07f80d1575841d85c81dcacaf5040ad23865b0750ff15e6ef73f8efaa44adf5de2e8faf23720d0dff214d74858b573644f746be70e92cbefa7eb8052d2750862a15ce2ffd17c6badc61b286005f0b0330e46e603f16130e26d1254fc0bae1a0cb763b49e53287c845d15496ec760d6b385137d7e72bf05688a6ac89b1ff09632650dbd60da405d50482d51b0462944fdcf8ebad761638a43c5bb6671e66ecc61bba2bebbfb8eaa00ae221f62491c1deed21184a4b3543c02ed8a56740504544c00ac6c5943a7627cba21c011fd442067f60600f8ed7cfa65e8105bea7b2560c2a3d531aba8d978c6a61a46197c5cfaeda260d1e5a3d2234ad4d4d15c9809fc0718f2a494bf6c06b8882c0ac29181a0299c2c3ddb6d403109bf38985cb6f7f65d9bf4a27ca16eeaee74113ca3fc1a9872826e441eae2c925c6c6b51b069cf13230ad073ba2cfcf689421d1d968f9997bb791f8fcc0b33d93aaef6fdfba7fa52cc956976e2d233ddf53f23b86d4a0ba47ff0bf3ace001f560024700c4f17a1a63bddb95d79cbea7fc76416edf7b3dd77d39111fb2c428eefb92654da6439b55cde33491217fcfb764996063bd675ea62fb9811c66727b09e567636e6c8d982e6f65fad8dd6f03acd60b30d79a476b76cf8cfea809b1de99986aa5f4211755c4a9606baec8c3dca7770ef1366f4fb6de53e4d3ba838fef144fb0e61ff4ad3f648955db95d2a20c7681252084c442d538d5e4bf0d2027933173276954891dcffd109736f35c2cb1266526724a5bbc506f3d941167fb169289beaf1e5f6f44c2ad2bfc807276cc8b31ce03414720d04542483a9b95db437b80f6c7835e841f6719a3bc1eb59138760dc50071a8161bb4008dd47c756af6cf0adbac7d9d2d6cefd70d01b9d2fcf8f77159b6f1501de921b1f99d288d980a83eb9cd78c3a72cff96655c49d3c91b9a76f998c826c1b6e63fe19f1906fdcab7ce1b4a70e3ee8ccc35d3a74b5d103989a163f1671f9169b894c9b79146ecbf91ed7f2d19b04985b6598a168dcc24e13c8c937b200b687e0a9c32218ac0b353c651d22b6b8bd740b4e4c96b68d8afaa71737475b622efdad1f288db138261c51e7b753c357c6e6f940fde81bd1c23efb4bdd4bb2625fa314fb98094120d400a599da875d2323fe7f784d7f7f02cff4f4261930e6115aceecbbce375a0e0d055de53fbfff07fd8fedd827513b5dc8c91596168a2c4203455de3d5124fc35b50d719d770f1b2a6bb849170609d8991c7ce6657f027824b4d6d75e9c2d0352e4756f8b7e889589e18301ab533ccd69cf826d485669ab0fae342f1e2bc899ca929a034f8fca9268325e85ae9442c8ea29922b614c63bf26168ede3750d1e16dbc1ddb96d8c6eebcfa806091d4ade0e8b57b0939ee335719b870f19df1392d5f3052cb09893175c94d7f583695cccd8dc6dee51c016a8626bd3a072d09cb859aaddec019541f824b88f5cefc3fdeeba33890cd82ab4fb40c310ae931a3d1a5092da0589c32eab840819e36161ae4705574ab02e392010b0c6c7ee76891778e31fdf0c97a1c92a70a0076f372b5d1d2a4e947099126f32800eaa4262f52d440b612e44a8998b7d39b9d3458f67efd9bdb7c338e24b4ef6416ece81fe457a53592b63158bf83107aa1afc365e1da32dfb8eb9e88de6630ad2e6d21ec3d610f11db0e154d2317c707a68e98e7dca8ea832ce0c3b6e87e1c2410db760580d668b81b8c34fa546604dfb83521dd1e97a01a71d203fe81c483e22cc69a685edb4b1e36d77eda636d939773bfde64a736f1c4a61ed9387ff0c2b900e3aa4faa26330034c3dd9d9bd3c21273beb1162849922a37fe7515b720eb4e01177fd2e101efece02db75b0ddffe3f4380da161173c95dae55c3a2f4df8e3401d982890cad9ed87e794925f61a91a18583c26690c6f467dcc6ff3214c98de80b38863acea99f3b29382ef329b4d6a131bd30db294678b995dc48776109dbee2d1d27d807f24c1060d5d4231e51ab8491df45e58a674d0376c6ecfa11e580f610c34b0dbba1a527e1f2cb35da461bc2678fef8526b370870a2d82004f131bfb2716c1ecb028e9463998087aa89bab1bbe2c932b8ca0911da2869eea5c3ee3881760a95f7a8eb50f8dd4a6cd0c0f3e46086dd66bddf46aafb723136d30b06fb11edeacc0d11534b6cd26b54fd2a2b5b7c078625afccad81e5ba3a2b8772f460d10561dcf89c1f0c7b42884b1616e54d802a2c74714e9b7ea8aa0ddd4b6c33451193ba708854c275a9b8010e571817e0000430bab43e34a967163e4f58302123d0938e522095207559bff634aba893be2845e9f21db71bc596fb79d6f0d41c61703125e3cb4b85c6e12a160ff832607289a81e5c338b5e232bd70276727ac854506d6769a8142514417ee8e51e5c6d5451f37cba86ebd6887c7ef292dac585ea00be88df96e7954f1c240169ce169fa3d0b373e5aa01a5cc0000c3b72bca5fcf02e3e6bf9c0479a13f8b6ea053cefec1edff02097d4ae0c786873d701dd471e24224edc91988c78ec44aed74459f1a181b4338bafd7f8aa87d01ae5381d73cd9019890061fbe28e8dc94c43c56ff08cf6c7758d172a6b5e09f8c2a04d622377a613f038ca39dc90c0562b7edaca03899c28e3c7fabde8c898f45512e6e6f750d2259d786142870f0e0cccf6e5d939bfd1494ee6efec567d2e4d95702d53e365c817689c55568e4d1b54f894e694481082b2d2d970cf5a10e938ac8a92e611c67c0184d382309f13a9bdcd55b4324f675670e58fe7e9d8f0cb03169e9122dafffb088529a724422538d01a90bd4e120c0353fb9ca523d6f0612f2e40f4ed973035127a23595b82be133bf9f740c09a935c2322b9fcd332a394e756b7b52f1d987425028e4b45e9dd2cca82e0d43aabf3c06e7dd976e054c93097d46187fd10fbcb27cf8c3affc52742e93d39768323cdd1019fcdc836b6df2676c26e3978de1cec0b48e3ffc5dadcf21e5d41332cb887d517762dd280ab338c2a9008085c0e19af03fcd144adf101f2cbf8bd44412889fdbad5e6f069980d38b132ee9a5bf085d9146326ee3c919d61711627b5a6b555d6634c83920e909c7a2d61180aa6211c0b2248a4390b4ab1a1debc25b9a022e5b2c448541532a46e2c7c2017ee2175f93604fea8aa37458a9f56762d7b765f9d873789e36e7af62697c4e769393abd15171735b08ceb9d53f34249fb3460d91b9b1c3b456a480f7d21058584111054c8121802d811e86c250d56ef3b33620e76d0dcaa4c29375d5995f0d50c132a9572ff2087b3c3167c81679ed9240c6bbedf837f250be21601df31e2a9500a3f92f9a8248faa66666ba4ad609ae2434c867885545e096d2f3146e5f425224601a6852e22c3d325d8aa1b46ac7875249d4612c7640eb8bfad9eb1bfe1b742ccd8359801238fd14d6872d2c3bfce9d495bbaaebb6afb17fe24a25f9941daba0ab3da9bb60e95bf90f00951bba7a1e1059ef16bdbc1f9a03f01cc2e28815ae9008ae0538d13d6194b75fa97641ab1ee517959412ecd458951cb7fc0d83f92e6cf084b315d189b34096cd228d121201451cee19d84a4dd2ee0bce8990b1231c11ac69ff5191ba2fd4ea11485b851a68464a0220f83e25af9c67cb3cda3e41352be9b3bed121dd333040b446a4ff286f39f9ef8e06ad8417ea48e06bffa4856f1ec5dd31c642e49c70e19f968eb21fe0affe9018bf648c15e4a4b98049915ee273e5ec6707122c87bad5c33292874154f4a347192ae7fcf8eaa2e75caa3fb1a9ae3def7bf7a0be2affe3549c7fa4dd0daed1662adc6e7b521e3fa7f739115c1e5d9686647dd28b37a857b0eedb83a7eb254e0848ab6de7dc806679eac0ecb31dafcafc837cd279b43dbf378ce568e3a351fd541031e16c50da25418a3e6a452ffcfc92734dd303fe2fde71771616e6cc8e9f31664367ab2290aaabb9bc088df25cafc2be38b12bf93304c989e32cfbb9482feec80bf145fd3ef14b7956447beac4a4ca47409124d79d39370b82c7fdf161d6453b0e263139ebbf60da8b3c169602d38859f1a2a51486064f893c3e58c61c82eb3745eaf45fab83574fb239edf743d505057e52e256288e00a1a94d0a7688abf6be831c8a1458b46abab2ece16a7c500c74bc620827df055dbeda00ae4dd6ebda939c21e70f1d12a9e77b8a4e5e981e0ba946b2b69aecd0d8d79930c74807fb3c0ce1e63698d128c600f7f48c5ea2f9f83d55e0c8158c826fce72f6b982c04c509fd4144a7fee1fb1abec89a98819b14521a461712ec4dd45ccfd63bbcad521cc7c9456786140310cbef37910ef4fa76a0b9f218cd143faec452d006b0558e48c750fbe49cd83a0e2ed6b05220ea138cb7c2001e05c57a07f84c8d49bd52b3a7b5bb25f58115ca0794a123b6a53c254b3014f02198de9656abb8f641408a683ebbd20a73888d95c4288bcc101526029ba34589015f2febc8ba646c246fb7a1d8202c7f45107c6545ddcbf269ce61b07a61aee601786cf921bd7dd2698cde13a1a9d3046c90f50f78b2dcda8a2c1e35cc7a4590302711c6f39ac220de788bf6373243c083e8bf50297a88dad7513ae23dd90e1508831d3bb6140d38add4852319f93ab237b8b5d8928640f0f80a431a4b7f70941f9cb5ccd1c68cb956b2f473d64050909c7a95f1606f57181d6cca5090f2098512938c7dc06244eba5b7f503ed0ff889c54075d59b1a8be310666bcc5de3e8e60c7b9856e747bd0beba67be4a08f1c5c48fa889eeab0f88c726204a06d8ebcf4237d584209b41d95d0eb897cc54e5d8f7f122cba9ee283ac829dcfc1d38072162d01bed3a0d615eb419eb21248a340dca81e837e888d901b932f609125312834225b9f8245e3c0b04755eb9cf217e44214a7f3c951d0dcdfaa3cd68971fc7d9c1a909756100e65db3927408faf8cc9ca9be4ea96f756a9bcb1546c3d799b37a009457ec6f275471529659bd26f485c8300e9720489dcf2e04dd4e63fe53197ab06bf3ccb731a9d8709f8b62cae790c775dfca53105b3303c1e44c9c11033811c0f232b2277c0a3258506560ed6502924c739fd27afc599b8e648387d91ac59f50e4ecea056480ced0c14726aebaa6a1950e82f200e4df6c5748fb8b231a6900c1505c1107641b05e1dc8b6119e42b5930d423eada653d232d616e04e1489fc482b00394406b931e7e947336c3cc832724f048c9aa07be91219a022c6c0b5918827aa6f17b121e8f013ac553486989dbe67e89172b09e16ea58c43abbabecc27b036bf465ba153f787694774a55759e714485fe02cb757c55ab3c7493b2385bea2c93f2ca81dbd969d9c1bf2d0d8b5db32e7f9ec2a4e19227978c70c00b1dae6e2749aaeda301af6d466d7e9466fdefb3f1988f7bdfd71034f29550c02ad368120b7b37a2af19e538fc2a594e9ff6b34ff1b7a0543015b352725edde565fa3fe80da7b6c0fadabf0d1ce6334648ff88e47fac1318667e172833805213bfb0801523daf456d7b405343dd57cda0a421bc0c0d5fc2179194da627e1ff8927cf28791168d7e6d5f05174c52e481874d74feb11436ec4ef244ebe2196eef2e48eb9a408a6bb5b585158e08d13b38069e7a3540035ff6bb581e1c04aa3bd8deac175bf21226269e17898b88a53068def6ffddd5d83a05bd8c4e5c814375239fa31eb842e679021ebfb4652bd7621f5148b8a75f96869354151b923f5ee8a34941809351833faf1cefb800f4b94476f8aee226f6661a1a7702bd245d17b16dda558a7d4305d320aefdc8f171798e0514edde4a054fec5f961a7ed72e49255c37481107a1dfd33f045acf746bba0e6c61332dcb92b870146f41b6dc69950574e6d1c505a06cf243043d0c5ecac25a72b73100a76289cc2171aeaaf38ce93a601a73b583b24946b4f2d26a8954f9c0ec775af2c38cf418d672cc877917488f3635b76a62c1e2e7f0adc6ec12ad42ff86b9c2c7cec5df920fd22faa123da306058878a90170d3cd59fd235e2dd672696a71474cd0e31b00bfefb6979694ba889180a1ac46310f3597e8b9cbaa0201f6a55a47f169a097d6955faf1f5b2d40b81dac62d1374141a2b990889ceda3a740608e2a10778680050ac7391fece90953d24e7e7416007f2fd904ba98d49f13a66050078f00988943f962b91e5d258e91aee9a7333060c911e1d8c6e9d9b69bd0711b00508e3dcb739b3e3dd51beb7a08ac943dd6fbb1f5ff9a3caf6d071b8e0ad267c2583294658d13b120e1415d60ddc313bb560f8235197ccc477262e92a2856a02372e9b29b200f7fcb377481818cd6a69256f4f256de16566a7fc84eee20ca1dc3dafbee9636213f183b1ff2bf10521f63374801cb0fd3dbe682180d31952b626fe63a8718d5e106bad6314fe42c5158a0e40d4211789a6fe58d00a466c56254886fb88fd8f5adf161d1eaf20d7e89e10fece730c471555dc7b3f3390e656b26843790a3637e73695a46c52ecf5991189fe4bd22fef726444711f2980dc8dc9df57d4d45176a4c007df5613d90e0c5a9200c87a2606dc036b2ad2d15aebcc4ac9a49b7afb75ea836a55ff51acf0d53d1619edc701a4e11d6380d92df0d20c735b42f4d62e99a5b9701e25c81252da0d782eb4630bccb59e8e1ad0fbf269b8784c1101d44e54fcdf1947906973abd97647a5802009760068d3b206e60d23ec95a7f182cd51de316bf06c6ec5922ba12abafb290406c58995a79853011651568e23e0caa55f5d76f21498f9b91e5f89553dab1e890118e124b11ca3020c84df9d9ee08ecbc8ab8ece7c559eda7d0867e4fde6fc90049fa1ff83f7cd07befc7c5b81f85d89ede5561460d69a2b0208dafc991d77b324dc898ba4219b589df712283fcb75674d69c6c65a713a1faf062fecf1abadd503be4f29b6ab35252af32f765eb291b095d991aed2a8f21843c99b2be83e3a4e159042b3256dbef2c442c35edb9cdac6f6a675974777058044e4a04e9601f8920f135e8fa120ebfb22d6c6cbe6ef20c28afa1c466a74048254cb21b103f174ab275ffacb7f0a125f2501695d0ed9a7cee01d11bba2a464dd0cba2c3d1f12a0c912d6b2b3ad5b0dfcb8bed00d936563eedc89f25d3927924789599e0e1583e0d30673fd3a490618304c9fb3276cd145bcd9bfd6b588d030b3a39328611fc7218024814cd704f1b9077165aa00d1ad51111e2ac9feafd03a98cb61904a77e7018f07b69e299cfb5c653144dbcff6547858344f7fa3afb2772273d16cedc591df1a0aafa97a1920d87b2bfebc00fb094769e0872598ba68d1a815d183f91a3128851d93d8261926180df95fe4059db1b37c8062a5a9340fea877bd24924d7dc2571152f2dbc29a725e66d7357d38ef6be04141c2794c001f4a4901e48dc400673d20de83d2f14983c3cccf64941ae15789e01fc2197e4a8f775bdb8985b7c12e9a61cc62c5ca1dde07fcbb9aa9a85d8a66966cf50bb8ba9207dceb90ec4d34d1e0727ae1fca72f1d560735a23a8a7c1990de206f02324b13cd9a1c0946c900bfe320a9f78761402b99ea57086d7e077ee89dc6ce5244a739dd99e0e3d134698191410e6057e266bbe58ecf3e61cded58440bf6f46f2a65f9906143225d1b792383525fcd0e7fb28e4a88c2ab859ff5c2bb2ab8d4ff0c805d5011b500b877cc174932084174241d26df0dc266b21c8920e2d1e106c684b68129146c821cf27fca8482992c51ec232f69df73839c6b1ae4752537ed7a2661a4cd100a6091827f2eb501e478fc9008f37322ca0aed80ec3691925d4b3cb4408b52493ec88a1edbd4b42239a2bdc87fafd27a03b0370a450bdc25a792849c2e7fd69c043a1e94dfe2c3b15c8abcbb3890296a86c229f48be8986083dfcbaa8d3e85b4e6bbbb1fb671ff1122b1427f95a632ddce21fd794e38ebc217295cc82b8813e471287145d2b7cc42907469b168e2cd502a5ba55637f5e7c8c1b59651ab1fee18b1bcad3dde33ac9093477047b20e9c97d60974f166e3b383b1fe71c30f4cb82b3979ce24c40fdfd04590f98be85f1e51adbb774c3063f8b0d3c21e7ad0f52804dad372c15e6fa0e2f30a43c833758dd1abeae6005a5460cda925b2b09848e222f22080bfb0de23210acbf1fd192561493bac336f9a3778623dc4557c6499526cd9c243cf03bc38217a80198f7da0180347c165f9fe6864a56e33c70d43bef93f2946f0b92b46f1d1c0f4c2de377899cdb06913d94942b594b3b9d710d2f13390bdcd2affbd2067f05400310a01b85f77206046604a527ef6aa2bd994c358c5c43758d4fb70c57ff1361074398c50a1afc44737f5dd1d7bd9dbce9a6ed59af38b17c5306f49aea64290ea6cfbba1b9dc79135c0faeef989d29cbd400266c1daa446d83f8e6c8aaae06076f0d193ae7e368146b52012802081d5b0f6ca58a7974bee585a728c4ebca085f469ea3f7a4a87f5cac620c71cec8a82fac220fe552e4714f2ece868708fd158b335fbf8ad3c7d91867bc859e9976f3809412b5bd543cb42afb45a0faf6e11d779286cf918ed0595e91e97bb5189f90cf927856bc338e1c67fe56ba2aa7fb086ac2d3898cc932793a1f056e00093e3a6ef9de713d3c632d731294c8bb3cd929f4759eb55cac2763814b3def4dff19fce96d89afa9611f9448f834efdc8fad097040e09e85c6319e5c43158b761dd245b592335db8216533e8f65e118e061018b1fb6b0b153efa66ca702060f8e3042336680ebed3382333385f8e2cc58dac2858b1ca298937ba6234d0eb7d2c82059dfd83108448087c3af1ec1005c91e2408357ea9dc0ef0e2d3d259b5a386d127c9a90f7131932e3a0737cc76d199d964176cd129f6dba604c108e13c8e3fd0f2ab66b43ae4232e8b56837a0fcd373e200f347219fa0150534ebcb9640077dd28ad7e7da50ee7c8286e85c6803e506bf02d535d2125509c4fd6fec25a4447acd9f6ad70e24a5583221c6f6578e026d1d0301d741b1a65d8bdca25bb3a012fbab62491530ff29dd737916ab61446da60ba0565504d5d4080e32dbd75b8048f4def3d848a5ed64231ea0e07fb4bde0ad3923056492ad1331ac57fa12fec1f0c4b0c180268c2a61ca778f861ddbf150635af1b51bb17fc202d3d0f9b1aba55eb0e0a30232fa01c35b998a67059a7d0242fb31d755fd8ef6782119f4cc4b2674bfe12dade13654010f4984529c9252f116487170ce05574f1aed467512924ca000d6ab9802225d1064296de2dcaac4f26b4dc0d94b717b985a3327ef32efc0b3e0f846c3dbc221206fc80371a5dcc17dcd7c32e4b580d6cc65340fc19425065a9b8742eb5ddbd56c6b41f0a786d5213cacb502ef3267156fc3f839332ff9e24e16d1292c55a0e3ec0d460350dd6072af24f8f2755bda4b053045ffd5527fe70ec43660d5b33f016b95c547acbd302a521a3f216a40c18e811c4c022573890d20265a40e04e37fa2fdd2fa9a2a2733a6cee75e2949d7551ee07aa6971ed0840780492209926c0684747cb6e836a68e844a1b2f150b3508e5606b048c08eebabc90986b5486d39f892c495d59fc35550d3f9a3c2720c8614a86f72e6abe8e7efe82d0db83f10245f11aa1cdc2ed60c6a893792d2d943354c2d0d8be6ed948ee2ad9ac13877904ec2df83381b3b0e0ef1b51e4e62f6c1a24506789b31be6d72d9148e78f94cdfee75ef945017d71939fc0e55bda1ef846c1a2bdc65fc0b60f3f94597f12908e2ce466b873627f50dc8434afc06f22fb9f18a6989ad6b106bd7833d7e86047870be3e34f69481b7b18cd2b294bee1643f06344dc3dc3649e48cccc3eae92a18685dcbe7953e16edd3c907fe11f360e48b621148295135f1b9279d932c1965fdca8b97dfdee3d2dbf0840e79a4c0d1c16383d15a927e49dc0dc02d9d58220300035dae9e9312a21f6dd99f6753acd062b0071b37f847db9cfdd021fe78c7bde6093b9e541c5c9fe7b5e2af3cf2b2f034254be50fa37b02c0b7858a4930b25f966ca158bc646ba09446402753231db07686af291f21b42316f6235a3c938aac2ed7e02424cf094bd6d346c9c63f057078216724677689ebd37b2217d471c8beb32c37f2a51fdfdd4f15537889882642918154f6b5b5e8b034a211c4d097bfd491c66df578ea54c4eca0eee260411c620d77c767fabcd438f857c649baa2bab0dccd9330e20434a44c7e772e327a49b1c3748df69980d9554a644142e27b2aae6b6ee58d2a72e86ae178beaf9dc356315a64214933b1185f371c8f7939b051a658dd40438b51dafbfce0cc948dc108519ea0a9c3d599381a14b13afbea24f5b957d22bffd937a43ebf1c92bc5d1d86e251d5e8d95abb629213f5f4c336402b8c68f3939b81c1494ba5ac072e29877f40b5e3620618df9987720231560cdacb23f291597f193e58c17b92fff7e5b62281a419a1594f24a26f84cc6432d1d08643176a8829aa83d1452e55a3a7dd6527621a99041aa06b5b4171d4e382b0df83872df427e630f50a615bfe31a1930457b284031498574c58800bccc5ff89183a59e552d12149a934f193fc233ddb03921212413ad3e18bec4bc7a1e947e31077f01d4b79a4783270e5578e95b8bf9994712863404560ba919614e933751390da65fa688130d5bb1adc4831f4fa210c8222b6fd543e0996b10efe421d3c46dc7099ac8dcbbceb41cac8bf84de1656ce24b168dc44776fa35c889513e3b2828b08ca01ca55da145e53c60576ad6ee278bef0b4a03ae8869836c04f60f1e27f2658ff309ac27835fd7c707a970dd51563d821d871950c8f6acd1286f4d7541af2d3e02c58629aba8965cfe18f0870a77fda7093ebf63a1776d42f502810feb615ec05cf2a0b7a97e94250ffd90a61008f4ca1a000135be805acccaa61aeddc9f0630bb2989ea04d987a0efdd0107f937e392219a5cf0c720d2134b4d6ade1312aa0cce232d27d821047733d89ebc2b6aa2c108f1dab6411521ccc553953603d8da4c6a123b080cb0d89b73d6795323e33c7a0977d37628f5c6ba44735bbe408c836b5bad0a9e492ecb06a4bdc61fad1491351b0780b33c654b27179815a39f6670517fa3d80b334e7dae784604cc5e3455f90a17427bd8ea172a37c39950d1ea3579ad5faf157d54ce5aa2bcab0bba89a46a0ddc9db66f95ec2cc6d232ae1e5195fb876372d60557ca603289485dd6448eb5053f8b12f41b2f6f45248b8e6aa7dfdd5f34db0a0e5df775528bf9c92d7e65642903c7335e1388adf556f180367cb1e2953d4532c6a721e836b6e8597b84c333193365155bc9df96dd29262d9faec086f796e77ad97fee5d69707773fdd5571f2389d2f44a222144515c2bb8b93ddd28afc91a5d7d92ca7045f8ea21b573d11923cbc74a4df4e3be894d9ccb97a0e7b91b732da7986ad340a08067281dd0288512d7b6cb3a7d2252111b06fa07ebf07766993cd87cf10ee12fdde4fda032de471a26e10bca2dbea1ea37b93106144dc833953acb940ee865ca5bf5ec8cf029411893b965d3c56f12fc76f9a0c43842cad0cc1271baf8c25eca5ff69ff91ff5b30b3ca094af30a19ab9cba6f54b74e974c820fa4bd8d881cfb738014caf86c50a1b081850b4b4e5452dac5e96c1e691f8bf3b818b0623602ee87899b445d2da3e0dd93ccfbf8f7bf36c35ec45298c5e403ec69c92d0929e0acc39c8cebb87e48823184cf019f5f68ceab0613aa9a3d3cdaa66379323f27824287b774a8dcc32080bb6ea7f0dc89095890a928cf858012b19ce40e0e34a49283320a5254c9242291fe69b0164941d85db141d78da30d6d554512049b53762211584d247d1c56966050024cb1667765f81c0e6ec4fbcac11caa785d8283fd071538bc691046aa7c2305e365b6db8f9144227374b8880981b98e08c0d34099a1838d1a0ad49ae999a140739effefec3a087ed2355cd07b962251cec22071846ed571247d749a42f623021b9ec81e467f594457485759a79cf66fd19b3f0741f2e7400d3d8aaf02d3d04bcdc82e0c90264c321200e6d02c72eabc296b7b0d0a4e4b7fe6bdf8e7628602fe16c46a68890809f9de766aace365148fafe1cf51f5b53b20802ebc35ef54003d7468365c6144d5d82f6c1ad7517bedcfcad02a6e208455970e2fb251a860d0a01878111b00ec9fb3a54082e18d6a2b1830dabd38c076e10852acdba5eaea718db7d6bc92fd698ce8d7b559e17e0be53d6d9e8e25f190d59915d81d8f06ae8104d7cdf0e2bb5d37096e93f9279b4263ab4f5417c33fa4c2d98a724f8366d1e1c3d39a5254f2bf1b3766d950359dbd4ec49516cde666632f9041bc2fcc6e3a61698750845795f1439f1e5258927ce8d400db24a1ef6cc56461f95fc873b41e225facff7511bfc915315b06bb705d984db6283b36bed3ae9c9ac6766c42b2e300ba09cde96d329e9ba328a3ee0c4f497463db97ce5eee382a17ec90c591cb9b7024c8cc6c3a78b47aa2c2b26fb9ea1016344708c4042442e73f0c645c3db2bb92d37d5c3dcf71732430590d28c6b10e3d3746ca0addb56d94037156644bae6781365fc2286c3fe00a7e1ea0d022d46b92beeb231c0d53c992b356bf1067385210a5567e9a62aeaab9828557daf5eb1a4d01fa189a6e287035e0433aefde3df63178ff9bc2f9e223cc6a5619aa73a58dff836c547f862996c8cf36a8a3a500edd01bd27c1477a40cc7b58097d4741fa278af8ea4cca5c662a57a5b6bdea0b3a1e0b6d61b5403984b7e05e2c660e3318e63300906fcaaae8fe77b4dd204f5e3df90f95abcf9767c0ff2d4760d7d01ab9f29fa9fa4a8d74220b37a686f1304f014f037cfc0d94c1cfa3522a817adb950c6abe8641f87850078e2c10eb54696102b11db2b0d70eda47b883943a3dd2e4641e498066dcf6d934770a77dd6fea74c697132c4d6d924f68dd73437b955bba7bb43f52b4e751d7a695b960d9f4b83881b1134f135e1839407bce1313342de13206d55e9953b5d3ff5f5fc21a13aebe1d4b1a64a576985b3fb6fb74e1278ea93ff80bc3d2b93925e7e41e15f6a90cd2f16c29e84998439bbbe20b15bd9767e77aec9a1a439d607703c35a25d44e96600f4003f67e652d38d76ab4df561a60e84759ab15ec7cf06526fda23c8e6eb34ed5142dcb9cb1cf911dab416af6c243f32a6e9156715b80e63f856c17862f12bbe6452caa3f72dde551538abb9d2f9f6d743186054fab6e33e8138fa2a0336c640e9b", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c03d33039d45432d4999b9964018d8d6552808849c83503f50a70d594c01b441900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000dfe8f99963b947ff62a9677085fa128d00000000000000000000000000000000651719d455c726d57f7592d491c375c202c2cf87674c88d6103108c767ab2ac48e35bee25bf5e92c02761b47af09b71a0ef8e0d309ff18b3d6b953c338b01b97fa25e4585dcb3d2715bfad3f8b534c1c00d8052e8cd77545bebd67fb24f5e55321a23fd6c4cac362afd5347d83b64a0812e256a3ad805416f7ff7542aea223909155bb34e12d627f4535acee240fbc8f14d67d121dc7e34cee6e339f2837b37ee1d059676ec86d26abe900b1981dc50b2c243cf0f78fbc62ac23338af6f387e7739d8309912a551a2d3103bb652b7b5b1355bd9fb58c4b26e2c8082910b54694eba17179cdd14f23363c598dd4f5ca2408b2f5bb0cf6a4217d15c1e086b1aff8d866b732b95a45d34b55ad379cde70ea" - }, - "decryption_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000005f2218f60b38de08f000000000000000000000000000000000000000000000009ff735fb0aa8f9c9100000000000000000000000000000000000000000000000345271c8cf4b33f4200000000000000000000000000000000000000000000000000020cc2ccb0006f00000000000000000000000000000000000000000000000b5fa1bf66dd6f3016000000000000000000000000000000000000000000000002901de59263b00224000000000000000000000000000000000000000000000006472b7847e3356bff000000000000000000000000000000000000000000000000000249bd9812aa03000000000000000000000000000000000000000000000000714c4b255202de8500000000000000000000000000000000000000000000000d6956453c3db805b5000000000000000000000000000000000000000000000008f18928794975bb8e00000000000000000000000000000000000000000000000000001df754c51e1800000000000000000000000000000000000000000000000cb395274660890a6e0000000000000000000000000000000000000000000000095e68a8b2d4e989f30000000000000000000000000000000000000000000000059805d645f9983726000000000000000000000000000000000000000000000000000158446880ad940d702526d3c16cc3d8612ec9f5ab1a62327fb7f070635171fed1cfab9fb02bc9221c0dfc0f55b09832b6a84878afc2e21c87d4e1d8a87eb424e5d8925c210d9e07172412ad021afa04978190bde455cc48f3b13c12dfe3586d8bacbc33d21dad0201c1d29dd464380aac0a28c4680a60cedd2ca628880dfe1ef15850c97398d3140980f66808c0b14c9e4453eea5445abd16b1105b2bcda4949456ab3101e1222a0312603a4e6f2df28b6629f50f3b75d1b41d5cb948d0c9c403115dbc717e6a03654fefa6377d61e9d29fb073093070787cfb7a4b0b3ec30cde26e163850eda076820f3362d05ed4c34ea9517cbe7350009778810c5299f8b295c908516238c26ce5af7c7c1357caeddbcbcda38130e6cd3241e5e8dfa681f275fefc06783a82ecfdd8852acf679dda466a5ededc1e1de0c75830e71080072b3ef5a39ee8bda20784c461bf4fe943023c69c4a8a80e2a45fc44558452e6215ae47e0ead41a120153167359c0cbcda774be43b125d8a11ace1e56095c79fc32dea956b0e39cee06776a162a29aa02abd8ec6223718d3cd1685078bf3a800d2cb3126260f1e7d00f043fa160234a730fb38797f9a4bc6a87606b469ce5b45c6203d1a4873f38ed146ad349349a9aee02d748d21e7aa1691a8fbedbb127b6a3c77112ee4c0831792c74c847f1c0bc4a6efd16490824b8bb8475f1da1c43d73507e70c149a6319bf197b6f3b7edfe72e0254c8d6f9e925ec1c7a6af14ee97edf125602814ac63aa12247c599da6e712ca5f13212f3f0f2512533375ffd08e193bdf38c9f3ba9dc1a268007e54508b8bccd9f74ede0bf36271627ae75e66e056d189daafbf180042a02b265b492124237a2279d5fc005d099e9bfcc664e31136a23d11bfeee427b430857f0bd419d1e39868f050901beee9e76bee39cf93028da313e3bd1197455f92761af6603b9e68f84531acd38e0664db3970295614bce1e0619bcb180e855510f17ab92678e8d58889b7fd40c25e0a0b172fc4419dcecfa70a776d4a768c0ad24c227ed361f0900d0234be6e041aa3dee023df77131ca5c4f20b76a4c7de061293966e5cc7ea91bf71035b5321e43dbf366baeaf2123d239bf9518dfd4206d1061fd78b5b3cd10843e60b7ad93fe83fbfe85acedf777898443d8f006381cef4180633a4774a6ff9315c59d52a3e82cddfd313d2a65756cbdae9e45c0c5aa6a9254275ce78b21d39dde33173eabfbc681c81df53e5e1937291c82bc55381f7081429ea0e9bff4f359936f343650063ae80cfb205fe4d5505df14d585a2590dce1a6d87a45ace520e5fb37826104e159a7736f3a575eabd42df5857197e4aee180a47b5de368d8fb78de2739753da476ec06a97496dc3049bed74a1fc64bc1e122b5ac08a4694f869833400a9d13c14d62c2b582ef67e076b00a2b48da7c815582aef0b34c5a021d578b1956454fcff047ea232b5b4a02d4f0502c2a4cfd8cd0117c50a71e2db4eaf6a2b74a17ae47c94998ceb3fcd05c73c96a9add350b9077c2dd809e930647b467466f47d983ed8fbdf43a32f6e72c51a691297d20e12848605e09ff184294c3a8491909664881831d337eb81a349702910c11deb4bff99f225f8fdcc9211e9769375fb705925b84428730641554585eb5487c4da3ea9e7440ff3d3abcce49866d314ab0f7705907bf6c500684cab7ab9882ff7e8612a49081e5b363f5e3d6ab435ab1350d25eb7d9f97aa4dfd07fada88fd07f36034efb560c4b0d8820249cce16695d2d94f4bebf8053c7e493b265bc22f8c764a65183682b513a86d2639c1e5aa8e0340beedf6471f141e1b8a8e8d45dfaa926c5c8987e0b9f188a0ea9f8564827981949854017fd42a8619d07cbc0464824f0aea6ba9206f394b9d5260fd699e5832bbe1178cb8e62874c98877fd4b04b1eb0e513aa3b1ddc09310a5fcad3c50f2bad9d5f3c9156f2ca0b4e03b79e1d10a02ccd0870c50ef282b1512d4196513873b063130f7788df1f282c27369f1151ad40f582d6bb16a2b94a4760b80c6bb55b8e4ad2ee4e9a8440d102c868fe6467cad57729eafa0cc616327c0573043e9fbbbb0684122dfb7d20b3d550b2ef4f17e51cbef1fdc614911e2a36c2c17746f37b01985426360e2a9013f6092e5127bbec6272823eaa20afbd5216124ea1fc198cfa2df5aa0e074fef76c0ff35ca8adb52e62ef0fc8c0f9c913552812247ff546ca7d7da8ceff4eff4fd5d898430dd301db1d1902439115723bb71edf060834ec4572f7d4ea861f05cf32312ad5ad55479c704d1921d2c5ab9a7c079748a6b1d7e37369f5a28a83d626022b64045f3fa0b80a453c8041de379d99bdb0b99ccc892f31f80f17da2ed36f0090f5e3094dc1173a711413d254967f32a7e7371911776461ed97ec330d99eff86e25d631826fd520c5eaf220fab467cb09a2a52ffd6c903093d955be3eac414314e49c350d4f3dd15751083231ccd6e2fe779f5d3e5f4b0b59a392e24926f833f88e7f62ff4eda6029e3d1a1f150584b58914fa39228082ac5aee53631c5cc9364383b11218685904dfdad61513bfcca8dc59f74f60ebdc9a36b03c8730930b07820f261bb91cb31ead29b20028e9d48990a5d50e1218e689bc2f934d607196679ee0e7546ffe91c745fbc6083f23bc710245cc9fe362adc0c75a854df1d66a93626ebff3bb2264ea1dffa416a238163b0bae28ae59785d275a8ec59c8464fd75d992557f019378a960f63425e05ca4c3eab58bf0a866ef3d5eea6187f3809c65b94d959735d16eea6de28f1432bb4c8abfb4e9815b4b119b72103e50fc65878db8b1541e4c4c5313f4135225a29215c4cfd502ae3f5942f8d8fb7baa7dff07e365df4f29a6c256ee9170b8205595a47239d5f88aae9de6162ae1332003e7369697be82f2bf955f75ec3a642dd57fd752005bddeed11ed896ee5757d17d5ede4b573c0505f824875179e2eb112a2df36064b8fb9fcaf2290c9ab7a842c64eca3a11593bc346ece99a9b81612a99a5ca0578388e8b8a5e225e409fee6c296931887b26ecbdcd8b8b0c14c19c190dbca881fcde82b43a7b4305b23aeba9b9e903c4ea6be43f03b4333ae4f93109a8bb1ca28457d9326292a6eb1434fe54d0b315fe833dbfcbfc4558fa06ba3f28b7927e73451b4e5ec1be2e14fdfaa723fca09972b11f2f5be411299c23581805a2158f107d8629f024375dac5b41e77e04d31e4a47612a787093240388f5d92673446e08803ec91d7f3e4da3357254fbb85dd613f9c26242f7798e30e8200b2608f77deb7d1c5e2719a0e6705770a407edd4235ee26e860b6c69dc80de486d15a53cfeddcb6f4e890cf26cb916e1f8650e625282d29c03cdab859516c2cae42972342f359f017db84be110eafddce43c189c6516b9150d92db8ff5bceeb4690c86cb5f2bc09a99e6c0cf0e99a69d944f8d3e6df230e70682babdeaa8a1bfc51f7e87399eb0ffb4aa1370c9b4b01ce54403b342c86f1c81e84b324978c9f7bd0668d302a579e82d9a658e81051fd4591b84dfb05d80134d7bd7761b92fda83330459190247c87b440ee6159e47fddebdbc2dc7d1f1fd6ccfe8160fbe82e57f2254da1c224f027a42089646f8fdbeb750207ad222a686bb649f69b727472b1080b0f3bfd31d30bfa6a78029f4dc098c71e472f42bca745cac5fbe192cea70bca1b45cd711f3ee3d3e825a302e399b5e09c862f09eefc3f07870a76b6072c39a809f4bd5560a0ceac9974743df4ec488b876c361d2655f2f3be902d609a100bbc2a77de2397223bce6ad358b03a57e0008b6e4c66febc866021e96c1bc483346a2d8114b596089d6f34016fab7654e77bd2fe6513d714f2924feb52a23156fd5212b6f445e42edb6ceaee423178aea2c45303549206b7b20ffb1bdb3191bd1e0428aee16ab04321ad1eacad7fdc2a8107d9412ab7dd50c7bc342bc045ce4001d505fa508e8f1529c4b17f163a1a77ea740811b15d5962ebe95c5a58ba1632ab352508c28edf99f4f8fb8650172223044709382a1a628a5189c23429696fed90811131b70e7049cbb1b4eafaa0a0ee99a000701f664c4cc4d392eda23359b3ccb421668b16ac00d2d74bb218eca49ae37c13e7e410cb8802baf5b37e085b02e3c10c7243af66e5be7e5180c1c921d1f7b87eafb8e66ef466ce481267df735d131e1d91359213059a47b599f89465d236664752d5ce6324269682b058709509946d184bdf3fe799e092031f4b0a6c1760cbbbb22c3caea02608e3170d378d510939231c3d509aaaa15ccb62ecb814131554e86e180a1cb709629ee5f86ab7d1a7ef2ac08257bdc936b4c829a95b666842778b0e3ea6dc4fe61de92e3d60c01928d92329953245ba406eb19202d54187088faf11c520cd1b273eb8b4fba91c48e70a26b015620762d22a01b104d48d69af46a14a10563597eed134f4b677402d19a028e44273662134c99569c498c0335d13a56f2269591840c47ff19b696c29e28d1d09be778f26af8cc6618eef42357c66d8d65b70d27b96db438e9cbfc28ea81f048f34a4a86158d66b2bf3141249863b1913e42915257f58cf9e9382df14323918b1365e49651ef5f9627a5491b8c00d5e2d2358aa4e771ace9e80a3a4b8e8cf15e114d2d41a95389af530acf1d847969369eb1e2c0f1ae3a93b31e5a63ac12807f0352bd512f4cf44fb30705544d652dfb4bc0589b55fba34a5b3177e3f0e4711869591361d28350d28a386904e44483a9e7153efe49a91d8574911ab67817418a76b1f1c3af09dc74369bf98d0126b5b72c322e4e9f975571b454f1ac1affe25d77532f242cdcbf87ad7b6f75583e876e3f239f912aec3c63d1fcb79238c8604b446fdc64a7b4e11f1d6d25abe20d28412ff685c3d54cc58be9aab29e0d8ae24fc42eb8356a7aed4583e5b0a6b61fb0bd76940483edf38e2bdfd4b43c8967002541b2214ea50989e6ff552b209b7b981cae83d1c6b4bbccd740b281732f841054f3e37c95d278960ee3b512b9d6df72bf52e39dd2c6053d2a6eab3778a821d2de6d16fbc8fec9b4e72bb1f9649feebfd33f12f7428861730b7308e96f4ceab2786475c31def948d54302b7494590bdace0f17b3af521eda992d12be4bf376f23608fc6c9016c7754cb9db557c1ef412fbe7c4672c3019cbdca5213172e629a143a3bd131c75d9ed1f218c18b071151c55ad4a7fe1297bdc9c0e8475b16261604d6900adabb68ee8c2b675fa0030cae02140db32d550031cfbe71e2b0d0d053142d26c3d1474c5c0bf2944f063ef39db46d97147915384a02faecff0d991c9b1ccdb1a2f686701ccfc769f3779ea65716e8697026565295dea7000086f132fa300186285919872ecda05c52328ccf678f55550f80d341447aa5a8c7476c084205a966938acbc7090ec9dbe2510b521797ff0e735d62f8d2f69a530e525c2f092d556062a2532259b37339d43bb56f978e1dd9695686a6ca684a88c09a3012fe1904fde312e2ca2983e85a98d4501967703e6be8afa5ac688c215176d4b4211a017bcdcd5e8d3543a47d996d3c443e843563be932f5df4287e0afc2ce7ddd6af2fae1939da0b097037b0cf342ce82886a692c1ac6e175ad52255b073e0e53ca41c0c9795d1955ff973d53953d547287f5c7ed679740f92ef125ee6c266725d1d00dad30b9f51fc0e34ccaa2ebb0c24e0d6e7812496e93a59d721e4e15f58df8b2ca8fbbbf2e859cdfc65626466daf4ac1b4e8b8812d9ad394e76317fc32e80c8231e67a2fc01a463d65f834d8e2b249ca20effeca6faa42e0566ca584852795723fe9e57ee8b9e60226828bca17559187fe5b0b272f3ed27ff976c41c1d75478165c6ca9ba102acd9cb16ad8d2ea15a86bd43173c3b797a4640167ecc00c224629e20ece2a01712bdc28305307814afb4395b3516a4611da52499b55580a75aa295e4c0865faabf64086bd3623cb777ee2150522663a97411efd1df30ef7c3ab0e6789c0ebc98d74111eb87bf03ff552d543c3992919d9bf200a7d607f7a183a1e14f39623c6236fcb92f4113b775cf602ce79e2819f53302eb8df834cf9535a208ad91bd95e0391f13f30766a9098d450e9c68b8ad9ccc6b3a2adfeff9d9ab702decd05673c364223210fdc4bda9da2b7ce304e46e14a434f3928adce09db7b155443d08b6369d4f776e8326d85ce0023c53ee99645bd0de3feba7f6bb0bc5f09e1299777f3f9b91d644ca3c0ece8af1c5a16e702f02f0a9b750fd3669d019f2f30d8fe4ca82ce52589f5e6476b8b2e890dfc540b186b9747c67faaf572fdbc03378b2102dc41b05ef7727d47325d41c1757890765909b4dc876e46bb4006a20e5033b298e88877c536884c4394615a360bb6421aab40785222ac9886f03cd004bf5c8c38b5d73508373514ce5aaf114a697c6d842f19ec29fb64c28079c8dd207fb47f9671f1594ac148ee9379593a547ef44440af53c3c72822bddbbdfeb303e9686224df7e15c6c25b68e9c87443720c48a9776f59a37a921c306755785b1d934d657e3b458af03e52d6432839d23d419819c48a1659812a713c6e6d374b1cd41e1e41d4f9b51c690797d75eb854b1092f9db0a1a1a599e51f9b306866690c8426778c18e862614404df26178818d703e8358fac2b0fbfd66dabd435a21e02aac9cc0b3f09bd63efdc21dd3e8af6694c60c576c24a7ee7d66c627e9b21d024196e8496697dbe7e87632a5aa74f50c10eed5647de1dd1af2ea9310b26778c12c2717573bc23a151b9abf368b92a94979ae4f243591064f910899e073443c2085c4a37df56bc6990a3d7b1235634378b48f3341539be63e5d971e32679df6d11e6e2940964b11270df8126a038904c252f833e80cac32c9aba80c7c9bc0ac81ac49ef8ff070c273c7704af86a8dca8b04b3584cfdadf7781ee88fc18bbc4480f4b009a21c410c66faa1fe2ead1a6fab5fcf38330d5db3f8b1a4a901113994d07d3f2230336480513e55c9d069f1be764c9b5ff9028836f3116a4861ab804f21515ef308cd69326ad5f7fd1d2ff36bcd49d5cf5874d6dc6883fe0c59831b55708a25788e6f174d4de1db5879954b0cbcd422a42097409368e25cefd72d31c001e14d1458d2c1169a3e151c353dc1f5bc6b548343fc6286783ee2e4fbd96a9031a343bc8eceb04dad8067ab40a4f684853bc85ac5b306740e01140681b06cf1b1e61abd86785541da2124accb154bd4eda020ee978f3966265b461c9cf982195003e3daa4a4d9bc429ee6e499ef0f48732dbdf7e399b47749d612f25b95ff6731bf3cd822b4f61cfa33e5f2302c9177312f1793b8f251152772840ed960061bf0cc119c935e7f43d9393d0bcd9caa842ac41b76b1e3229af0baa4b60a62e8b86286ead8f1c7e94d681e44539c07922c4b055d4e8d7c47631ad995ac843bcbc2124582425774111e835906992ded16883ebaccaa00bd6ab936a13f4002a422e901295999ddcba799c139229547fbee76cea0a2a8988c56a343948140522ebd8bd243b67d1744dcca9d291b4c8cb0b15959b0683a8e089ad6a06e1f7430fe78a6a16515953fa46705833f0d193d0e40212ccb99176bfd70bae77fd2e1b8a95d4f51b2cdcfec53d499fe6dcf5d770d861e869c3cc629b4a8dd3c1dc66a3909eebeb273e0301663c822f28fe85fc8e0cac9a984c7fc007b523a5bb9ec6b36b7c58db1769c76f2f278d5dd6cc536396dca2f025945fd2fa27bf3ecc3df35cbd0828d92db58ecc09d8ceaf1550a40beca9b8a1ddc3dc8397ac9083f0029acc09abd92e0e7c56130a68acaecbbcf213d245937c5cf6fbff8a830134d2e1b268aebccce50b58026622cfba1874a4c28c780f1b60dfd8a100a273d6d7de8262470bb3dfc102509cbbc2ffb76105ca5ff0aa91b65050bd95d9fa193180cbe422e5c18c4bc60fd048426c01b8e7713474b8ce57bd061a6b689fb5bb4626611a6b1aa034694e1c9b3c0c54bb1d69d2dcdef1e9c68f32e38c0cbd89fd5ae33fe136f007fd814f2dc9a60223e64cf7c53e7ae3091b1174c6044e18627cdbbd1c244291cb0646571197786b5893ca5e0679b406bb3d223e468be866a903d61e4de8306f234d041e1a3fa3f5c6eb6aeaab0e88670869698d39e54b3938d06239d74d3c0696879faa1d7646c5f7d5f8784c4b417f9a0f1b6e82c669bfd82874d243f53a0d6c0f761e0bed80e16fc05004946c36fadf1e52bbe354f18f77d936844f8f2837a1dd3066136a228e4d43106f27e0e45ab26d80b9947ef8bbb60b41ac5972a82976c434d503be0e2c00b272bd3aae437edd3e11e8fdd1e1c0906a094be91abf7c92f0baab298cf43fb4db839230ec08eb5368064bd035fda059591b67492ef9effe6121611abbfc7951b7ec1c5edc32c15ddedb962220c97de666e4d659447ec99f39f5201b4ffb8705b824da6bbe18abfc15728f601137ec5b5a1c8d84e8a630446ea7f01c7cad0b9ec04d57104aed1dfd7d78649d823c029eac487a22e8b8db0abef130041b72cf9e4118730c7a37a9802a938aa8e85a16af1e47fbfcef0ccd006aa28129c8cdf677782a58d2b1b178fbf7d1097132f83604e733f6e92ee4ac1f169ece0f08900bc68506d1dd45af9cb1b4136e7912943ea1ca10a735916a98d2a9b17125d1c4d20710b106466203e23e4771769398f777821669a63f9c796cba9912c42fefa3f2c2d343c5b1a189d20de8500f7d3bf8f41c2cafc0cfea257b1d5e0eb70b5eb000e0079fe8e2f94719a9fb6003bfc459fc1aeae399e35954099dfe02690dc9cd7166077a5609146ff55692468ba0581d7f423375db9b58bb59482956e41d0276059c01ae32dc4524104fd547391b1d3da552f400e5695a22bff739bb8a0bf5efdda54933a478f07f734d38dc0e6dfa9fb3f1114e1b4636025dcb52ea6d0082189f57a0305a9bd6bb8d58f85a3ea14b47619153b83189603cb3670791f423a25fc40f9dbc36f9cf6775666eebb8b799c3fc1ec9d530e204fc0a219caeee2914d732dfaf6d90cfa7dbe7c6004d48878ab72c1fefc42970edd609580dac1208464bca46fe66ae3a6e48e44b1ceec1eb0899252aa0472fb01ce9a7d2c9dac511d0aa98a4741b2c03ddce57168f635c9bba0bbfaec05baf26d6a9761a0874b506a1ce1b360af99a58700c1917728c40d4fad6fc7f114238f8e7eca23cc5710923667ce035cc236b421452b6b3e10b41280607f9e2e1cf4b3a0b0cb92de09dd52f9fc09dcc7a9f83b768ff435e724877b8b1047845c644cd427daf2ece6cba802a95a9734ccc369520a3c7d14b47ace1b7ec24b8c1ae07231260dea7758f69a7126144215fd22666e023acab0506da17f1b41b5db4e7aa1e292cff504ba888bc11e08b7299e8cbfa12c25ab95f010be24fd5faa516eb6e663e03e88b958e110a240f1b0a58392051cd475c8d5283582a7a895f88b1e397847082e8400896854e2b174b18eccc0cccfb21fcdbf9c80dfefe1eb43d95a177ed0e90489babd9f203009cb3b55e2d801ac8cf0092fbadeda034035b9b9fae8a82f25275cd9f98fb8e2cf1c87829262f51f453590d5ab815519bc814eeaefdc63b3c038980685ca3ac0e16597a6aeeb6070a014111d76a9dcfec1722b55924609292fa01efb87b2410137d3ef5057949859cc0d0845d38b477f143329fb7f4e17a3672aa2e3e0e553820767739734a4af349051f158325726f05794d64486cc9e3eaf86278956533360bf3899a41f64ff6343e374c95afb448eadad6291d3aae8b9ae35fd90bdf03c026d05a806dee663e1e2b98709a8db8217f7b81f3bfa08a2baf999714a3a9cff0284accea238939b2d128aabc13d77755b72d4546ca1f10fa41b84799718cbddc1059173d7e82d096978f15b4fdd6ec5feba5ee8ba308b650bed6ba8ed98ac69620c914d34a54459427d4ede64a8ac5713d516a9d2c5a3bc9508a0563aee0aa67153990a7c9c56ab31a18ce9f14729eb5840f5dafab6cac449e091d825f0d2d221cbf22723745bf8a3d21f1f063678d1d4f2ccf232c4b9ef2b210a3a87e688b7f14b0127356470742d89e20fbec471bffd389d525018175214bbf8a0ed3985d590707143a50587e7766c6eec90202aa7abe19e628f5524b210d341561da277e45280cec55e758691b77f0d677da529ca66bcd41b91f0b363d77da552d94852e7e244eae0c3eefb1d2dc0d97f4c4262bdb4cae9e618baee661ba6f639b0a26742b238f1da780cb61bd26c3523443f794d5fd138c9d8d99c0dbb49b03a934acabfa01d82b2a0c340a58119733c53c95e45d14d19819ae00303ca8ecf01e893e9bc605f79aa179c86781869959f1bdaa84366d8411fd611facebc2805604d0e52c4d0fc8cdcbb81af78749c9fec45483beef2631a7747e6de63c181bf7397577329d102a147f0dfe10a72550a272061da7229a50f9317781a083c2c76b17d73a98662d068f9daac7b4efb7a2977a6f72b791041e7e2c90b8ff6599bb4e95b40cd7322517ea12b6b3f84d358ff132568a58b3796be4cf6938fb995389176701a8321421828cdf8f8f2365ad7ca92c5a60203d33eb39622aa364dc4a5e3b1eeb039cbc2628884b2e29f1316d2aeaf8ccc9166c0989751051f470a07af8baa6c2f1829c00b1eadee84e10ac8295130a3d318a4fdbb4cd0eb6e2ceffd75cf194e4cedd6a1bcd11a23a294cd6f3633618ff7921f1c7fba81054db3c26349d9596892630f0157893e60d2e627242b0b09dbd9828edf171c9526eff98da7862349534c9c0ee1101958d04b0cccf85e8944da39c259be4a6074270f05c1f4a29ef40e70f52c21cbe9a44966109de6b9f862bf0bd7a16f6dd9c6dbe9ae4c7ba1941746638924a1dab3dce7fc0c547703e9f3f82ec3c1d78518a898ab459fcac1adf8ae501e8f9105acfe79fa8b3b735bc2ef3fca97b7fb91995586b31ba6b539d533f2c31b4640122ea13c67579b817e9b9ec4a8cf1ca621d00addaa4c87f963814e7a46572e924d74c54598496a81449fe3a9bb276da5181665eda779c6a0442cbdadc75f4ba2c289960cfac616450d0661e264b320106c77bc6844452069fe7479118a335472f636b70154f373927177eb41465ccb3f8df8d83fae2cf87fb5f2bb87ac35cd600514cb85f197823acbcc4f6a22c4155332cb29b4767438654848719a0329b8c0da447badf47e77c228e85ec8584a544fb7946e2a56d9d5217c8c6fb8b2118951d43259f872ff6bdaf052b3431997bea7ed5d67b95b96c6b43c935101ea4c6a11de929e90523dbdbed55dd60f220fa0cfb381e86cd1bc5069cd9790e08fd642112fd0cb52ced94d970d9a2a42e42587ed71375beaefbdfce1d2a3ea7d924dd3d27c93e6bb50028f15841cc7871a497fe9e5dac1df5837ca414a27e589737f00e183c2fe4a5af9b91dc146e4afcffcca00ca2f3c58b49b3a161deae582f57d5490ea4ce2f8a8cf06cddef900da54494cc98d445c575542890d49c26395a965a5e1ae65c45619783fc52acc0a6120e5f12b68f7e52ec4b52d71966d1f01ffe5e5d1927afbc8229877850d49ebca124268922854de5c795133dbed04134cfacdf1e0fa606452a1e3d4eb648f3de8e85b7ebf984dbdcc371b6875a28b1c552bad8ef1ec0bf100fff7a9221b0d3e74798945fc46c4c6e95b01d977f84990d482ae36910faad48b0169cd47753388ce499bfdb57b2b212b6eab810baee86220515c6b001c1f56ad998e170f1db27f80cebbdb84e271a90a69de39bc17f93cc1bfc5279131683adc6581ede9f720e48d0d0baa9cfa633f9fd044eeb460493066287613208e31006a8003e0c079cb8e1d62cc0322c76f864846817bd99fa507c1584d64f1a6986061afc0d7bbfcf0431b1a9d0d517d1279dfd18473b5db36129acf565b619cb2b918056aa48e7460902779328a69d6bc5d014d689aa609fa5635f7d082015c833ca75ab3466c51c7571ce015d1cd83dcaa78beebe323a9b7af71588b63e162e9b9c1101d3a2f4f4cdd7e239f663d5433f508a792ae12d40208199a6f79d1541cfc0389d4e850e6334db74b0df336658450bab08b78b741887dd9f517591021357f267bec10adc91699cdb500dfa7f8a604259f0b2a89eeb419c8cf60e0702ed11c3faf7728a7a6e6b44b39b565ef53cf7bf2db11e8e4af084b6874cbfbb1c8a87581e891df1e8cef1443828f5a77871109a03c58ff256a17c401ca9d67f2252929901314381c9eeef8ce6968d3936a7ff09a838094777621ca041071aab1b4d91f1bfc7c1f75e84c062ed419543fcb585885fb564a90c536c96aa02b6b913ded1d13ef20b0eac4a7c389f46265813c0759b182d794c65d02c07fc324b3000d920bc224d902ec8a03317be93cd5ac0b49ed7153c0fa3dd2ed5356688b9e812b0112f1ca337e714ce51d2c61f84d7be3b2265c96ee138861f1c4ada7074bd09e5f0a0bddf585829f59e62e4a4af22188185ebbb5bfa0c2f0231b083a4f8900caf21378789734aa0f5234c7666b08b69dafddb2a5940d99ea588a0c369523323e8a9c8213fc1ba3a26df889fcf23b24a4c256da2f51ff82609d264a082969a2ab5b9ac62269b481c17740dd908d1f56fc2560ee211c4f697f43b0d706d56ae075264869fc8de2d914abb1a71d013f2cb9ab13fb29cd7d3bf51f8fe42fc423d2a899d98f6c7f6ebe76ba652c25bd0940adc42a09cd661b13a4ea5a882287ca80181793a466b37c963e91aa6340b409bba41b0c7281f1b696037fa71bce08642224bf9bd38757de198b7df876f91a93241cf592d96d526b5f9b88af75660112b2f8814e2a8a208c599f1112793a2727113ab6dadad3ca89ae833cf0d6fd0955c259a6b7d7e8914d650bf07ff7ea007fd88a3b8ca4d29ae2d2c78b75638ce1bb52f509daa43eabd1491cef1ca38497861f8afff1dba767367c73594542e0910b7274fa9418e41d1b741c6e9561e2d33679e7d5e54809673c2734224fb7d204c850474e988128ec7884bbee88fd16f1ae662e22aaaafe93b75e98f1443546936991be1b22646f21ba0109e4000eb65dfaedb3bdb686a07a591164f5f9b2e0c93931cab777dab2efbc30459f5e103e56d94eb3c8664ccfc5072660f6b71bd197ed62ea352f641b88b1cf7cffc409db6eef03ab092421510b92e550c1ee770563b7d2b5cd225dc2cbe9daf3adce7f78d919d00ee9a888b1d1931538730448a1234fa23cacdbc55e8a2197fec080aef3b753028782fb55217067fde08d14bff6b203313a0a7cd0bee9cd7f503d36a051215e4cc330e4ed337d65a5862c7fb960ebde60d0182e6c4c27c725d15dcd7317ae2248f6bf457eb4000561557e89ace578830012f467934241ad8353c566400403e1c32718df84b1abcd27e1b3b41453bed902f029aac31e996c58185af2cfbbfc7559b229bec5f0a2cab1be1a4780077648215bb92bf13f3500045a77227c585ccdfee19c884552ab83930fbd2eac2ccff682cf499ffa1e9578ec7d56671ad100ddc8ae91c11c54bdc79a4f9e2fc4cdf11d4077138ec807eb7cc24425efe4ae4844e54496b4528bca4730d2ec2a2c3d913b117bbccbf150fd5cdc249d26a7b32953569f4943269c135e7ddd062cbbc31d39e08335cc653293c1e38dcf14459bd107f4474d957c59cef7d913acc31ba6b1a100801f29e26b448d5609dd13a55db68ae63e8d8ad79328097fb700524a12a71920b2f7763097b5cdf3452f2012e95db3c7b091724f79d56b285e58cc1d7bb9fad02e72758ec7cf0255da295e57b269e148bb4e22d7ed55b6b82d600a42490720f190c985970717aa7038a4659fd6bb449fc696d64d2b29f1f8c37e5ac71792574080b86e629ce561820bddfb81c5ec6f2146aa269def6aca2218c6851eb477f2e24f9006fcc134aa70708115a567ae4f4d2f9d4fbbd944ed838d807ef036ccab90ec3661784d4f9bc991a13bedb10508e647c83063e5407cbc0de306161c4d64905f372a6c8fc678f62557d021bfc1c440df6b50ac833e1d811fe94436049fb970a322a89a1dfd7d10e7978ee4691369448f5fa685699ac8d91b2db2ed8828f0723e78abb7cb9aa7f47a522d43c463371ec5ed097108a2044f8e11ae0e49a3e2f13de49b1e23fcd7d3fea1d4fa4713edac0d8120da252ec07e924fcbfc7302c5529551d781f10d0443a57cdd41667ff645749d945696207c6b2226dcb907e95a4284b421078bdd08597f1d991143c76f7b1d903d88a2cfc0140cac4249eabcd001608b20c3a0c43164859d7e650a590f7e209dc4f640c2cbc34ace9460318f5320c4f30ac648f4a3a4507a8c58f15e4b699aba1ed1b64283c437eedcb24e335f5116b2d20bdb18cf6314dbe1f2efe23c09ee06e5928a09704d08db9f5325a048d167189d92251a9884dd3eed0864936ecd65c5992e41226b00463538a69d457ff12f4589f23fbb25863028f7c41318478807faf120b95edb2c4223b42938adcef30522a8b8167e74804e67d65dfedc6a47cac97fb56d6f2680157109c5764f0481fb82d364fe92f2a5108802e169e37522ccb9d2b998f07e7dac04afae9ac00d4128e8c79fc5b4939ff95d9e3eda7b22d6bbfb581b6c4218d9ada84e8c999532d0ead62a21266b5ba7070ac646586ebc3046b0c7dc9f40201e6224fab8c24914729a5ed6a8d6a511aadee612531ba4666c1c3293f5b896fae7e6f7a2519d4714a", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e4411dfdc7cb6265f100524012f038ee1f205bf8789b70b46c57463dedb4998f7ac800000000000000000000000000000000dfe8f99963b947ff62a9677085fa128d00000000000000000000000000000000651719d455c726d57f7592d491c375c21806572c19ee2f3532e970d617919b3aa8cdc582782dfad0699badc631f75128000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020ef8e0d309ff18b3d6b953c338b01b97fa25e4585dcb3d2715bfad3f8b534c1c00d8052e8cd77545bebd67fb24f5e55321a23fd6c4cac362afd5347d83b64a0814d67d121dc7e34cee6e339f2837b37ee1d059676ec86d26abe900b1981dc50b2c243cf0f78fbc62ac23338af6f387e7739d8309912a551a2d3103bb652b7b5b000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - } - }, - "test_exit_code": { - "crisp": 0, - "folded_export": 0, - "interfold_contracts": 0 - } -} diff --git a/circuits/benchmarks/results_insecure_agg_micro/integration_summary.json b/circuits/benchmarks/results_insecure_agg_micro/integration_summary.json deleted file mode 100644 index 2ded58496b..0000000000 --- a/circuits/benchmarks/results_insecure_agg_micro/integration_summary.json +++ /dev/null @@ -1,244 +0,0 @@ -{ - "integration_test": "test_trbfv_actor", - "benchmark_config": { - "mode": "insecure", - "bfv_preset_subdir": "insecure-512", - "bfv_preset": "InsecureThreshold512", - "lambda": 2, - "proof_aggregation_enabled": true, - "multithread_concurrent_jobs": 13, - "committee_h": 3, - "committee_n": 3, - "committee_t": 1, - "nodes_spawned": 20, - "network_model": "in_process_bus", - "testmode_harness": true - }, - "proof_aggregation_enabled": true, - "dkg_fold_attestation_verifier": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", - "multithread": { - "rayon_threads": 13, - "max_simultaneous_rayon_tasks": 13, - "cores_available": 14 - }, - "operation_timings": [ - { - "name": "CalculateDecryptionKey", - "avg_seconds": 0.004320611, - "runs": 3, - "total_seconds": 0.012961834 - }, - { - "name": "CalculateDecryptionShare", - "avg_seconds": 0.021778305, - "runs": 3, - "total_seconds": 0.065334917 - }, - { - "name": "CalculateThresholdDecryption", - "avg_seconds": 0.020453667, - "runs": 1, - "total_seconds": 0.020453667 - }, - { - "name": "GenEsiSss", - "avg_seconds": 0.007597111, - "runs": 3, - "total_seconds": 0.022791334 - }, - { - "name": "GenPkShareAndSkSss", - "avg_seconds": 0.010288736, - "runs": 3, - "total_seconds": 0.03086621 - }, - { - "name": "NodeDkgFold/c2ab_fold", - "avg_seconds": 8.096940472, - "runs": 3, - "total_seconds": 24.290821416 - }, - { - "name": "NodeDkgFold/c3a_fold", - "avg_seconds": 35.482067235, - "runs": 3, - "total_seconds": 106.446201707 - }, - { - "name": "NodeDkgFold/c3ab_fold", - "avg_seconds": 7.882662333, - "runs": 3, - "total_seconds": 23.647987 - }, - { - "name": "NodeDkgFold/c3b_fold", - "avg_seconds": 35.908104792, - "runs": 3, - "total_seconds": 107.724314376 - }, - { - "name": "NodeDkgFold/c4ab_fold", - "avg_seconds": 8.13719925, - "runs": 3, - "total_seconds": 24.41159775 - }, - { - "name": "NodeDkgFold/node_fold", - "avg_seconds": 18.718641194, - "runs": 3, - "total_seconds": 56.155923584 - }, - { - "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 1.611448542, - "runs": 1, - "total_seconds": 1.611448542 - }, - { - "name": "ZkDecryptionAggregation", - "avg_seconds": 48.859235667, - "runs": 1, - "total_seconds": 48.859235667 - }, - { - "name": "ZkDkgAggregation", - "avg_seconds": 19.836521125, - "runs": 1, - "total_seconds": 19.836521125 - }, - { - "name": "ZkDkgShareDecryption", - "avg_seconds": 1.224212729, - "runs": 6, - "total_seconds": 7.345276376 - }, - { - "name": "ZkNodeDkgFold", - "avg_seconds": 114.228116111, - "runs": 3, - "total_seconds": 342.684348334 - }, - { - "name": "ZkPkAggregation", - "avg_seconds": 0.615287333, - "runs": 1, - "total_seconds": 0.615287333 - }, - { - "name": "ZkPkBfv", - "avg_seconds": 0.226558361, - "runs": 3, - "total_seconds": 0.679675084 - }, - { - "name": "ZkPkGeneration", - "avg_seconds": 2.497946278, - "runs": 3, - "total_seconds": 7.493838834 - }, - { - "name": "ZkShareComputation", - "avg_seconds": 2.40892843, - "runs": 6, - "total_seconds": 14.453570585 - }, - { - "name": "ZkShareEncryption", - "avg_seconds": 3.950791532, - "runs": 24, - "total_seconds": 94.818996789 - }, - { - "name": "ZkThresholdShareDecryption", - "avg_seconds": 3.383825583, - "runs": 3, - "total_seconds": 10.151476749 - }, - { - "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.102920694, - "runs": 3, - "total_seconds": 0.308762083 - }, - { - "name": "ZkVerifyShareProofs", - "avg_seconds": 0.280129533, - "runs": 5, - "total_seconds": 1.400647667 - } - ], - "operation_timings_total_seconds": 893.088338963, - "operation_timings_metric": "tracked_job_wall", - "phase_timings": [ - { - "label": "Starting trbfv actor test", - "seconds": 0e-9, - "metric": "wall_clock" - }, - { - "label": "Setup completed", - "seconds": 2.9637735, - "metric": "wall_clock" - }, - { - "label": "Committee Setup Completed", - "seconds": 20.091043708, - "metric": "wall_clock" - }, - { - "label": "Committee Finalization Complete", - "seconds": 0.007389292, - "metric": "wall_clock" - }, - { - "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", - "seconds": 133.913944, - "metric": "wall_clock" - }, - { - "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 146.239215458, - "metric": "wall_clock" - }, - { - "label": "E3Request -> PublicKeyAggregated", - "seconds": 146.750428209, - "metric": "wall_clock" - }, - { - "label": "Application CT Gen", - "seconds": 0.009458917, - "metric": "wall_clock" - }, - { - "label": "Running FHE Application", - "seconds": 0.000062875, - "metric": "wall_clock" - }, - { - "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", - "seconds": 50.48525, - "metric": "wall_clock" - }, - { - "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 54.401319083, - "metric": "wall_clock" - }, - { - "label": "Entire Test", - "seconds": 224.22015425, - "metric": "wall_clock" - } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x0000000000000000000000000000000000000000000000055ce842869ecc798800000000000000000000000000000000000000000000000962d271f2a31d7df700000000000000000000000000000000000000000000000cab13fd160fdd78b200000000000000000000000000000000000000000000000000015b9c8ac1f7af00000000000000000000000000000000000000000000000448914ae5744f2cbe0000000000000000000000000000000000000000000000065a47ab8b7ee000b4000000000000000000000000000000000000000000000002a7228f267b2676cd0000000000000000000000000000000000000000000000000001c08f86a603dd00000000000000000000000000000000000000000000000c08e7c3252f76857a000000000000000000000000000000000000000000000006db65bbcdab667efa00000000000000000000000000000000000000000000000a55066ba466957e060000000000000000000000000000000000000000000000000001d9ef76020bd9000000000000000000000000000000000000000000000002b97faf1a204a616a00000000000000000000000000000000000000000000000362df9706d637d11200000000000000000000000000000000000000000000000f0cc2167e89450dc50000000000000000000000000000000000000000000000000001ef17dfe0bf090c486ea55a1ce3e9a1c58c6d280f64c6cc337f4aaa58296bda4c37e368de55080298c60ac27537b0c7a66f2110eaf284b8c1ede53c49f2e385b77545e4a256420867e2ed0213a17a18c04d0b9dd4c887e3d57672e2d949b0053ad848ec932df90bbd48bb079881a76012522639773799dc13cd501fb540f0ce659049daf592d321c39bbd718505dc5221412f4eba8ab36cb884b887395e583b5e5f6773adb4922963e5f0e9811528de1e4d3cd0a804030827fefb63c74509022aaf5c3c39987c2b93049d138d1aa49ff2a71fbd10acbd91e825852dbb160268db8eb12b480ff61497dd6efa3c6957af87f0623eb5f7abb82b17d4db53f3696f4930018071d10e00a8d860ca77e800cb86d452df258dd4f7383c08e0692d8ddcd73a356c7ada78216294e1fad95e0289e0028fa1fc29194d2cbd14df27e9b5c0f0948d3c0725aa1b202607c7910a2af9a6fdbb5365e40af6f2d529bd97b708c1e5466394c8330e0511de6045511cec4ec3203417aee6b3055924840feeee2cec43ba9aa419adbe1a8a812d9af9d03eb346ce5969a7b5fadca3b0f31450b95bf2f0bdc77f7361e401d563c3856b087f8bd2a15d89774d92dc05b38e704faee3e675238b107b02dc23bb6f60966a057bab79050b8e2f6f5e0eaa3eb8e908371d14134ed57a2806560534f9bb4dfa4115e83882a3b2230249b8057ff8e8c08cd1d52245366811ba341a69016d7240ab6dc55923034e998618e95b4c6915158c01443836eb5d253e99203273029ee38fcbb0bb3e0a804d5eac0d2243a734993465dfe0e4f997f204f0304ff20195494c7109597656675719513d51e0509f06342b4bb3a9beb1f505dc1b858d5015a691d14be28720789d7f5331fa146dba6b83dee83542848bfae502099ab605ebc8bd7b733a5f8699828b1be13dc3dd1e77cb7cb4aa01869f67223420eaa1efac3448573bda5d1ca8387ce8d772c9247e12a38fb93c0f517d341ddf2631765b99f8290a1b3ebbb2440848fd666c891bcf6bf3b70a2fbe53f758e42d089f0a311d621d74eda459982c01bccf40cef4fbfcb1b31d9664f6a50375198d06ba84f6e6ab7a1f7d7b1144c79fe1b0a37b9df6d9322854afc5612476a8adf12f6d341b75bd522bd581cdc19afeef48c3ac919a365339f4f128e5bf5f955a350020c07d403078252f358c479535c8c4b819462a33eae0108b1c909d72a954ed1b704afe5c0f2093bf62d4c255e822d7ed58bae64eef3e2fa1f87055a8954f760e2c0c7eb770bb847105517ed113eb7fa577842b56f05f53039ad97bb4e663c40e4ced08d2214235afaee699bc67ad793e80652267b0e49e7eeb82c2245efc22043539b3f5902b00a2682bc5e10149dbc78edc127512a8eb171f570934d796a0040d351b9f54e8d13541f6fd1338a09480bf8a185e5331440781129513d45c8124fb65bc2766404225b3893c5fe4b369e4017f7090463c07f80d1575841d85c81dcacaf5040ad23865b0750ff15e6ef73f8efaa44adf5de2e8faf23720d0dff214d74858b573644f746be70e92cbefa7eb8052d2750862a15ce2ffd17c6badc61b286005f0b0330e46e603f16130e26d1254fc0bae1a0cb763b49e53287c845d15496ec760d6b385137d7e72bf05688a6ac89b1ff09632650dbd60da405d50482d51b0462944fdcf8ebad761638a43c5bb6671e66ecc61bba2bebbfb8eaa00ae221f62491c1deed21184a4b3543c02ed8a56740504544c00ac6c5943a7627cba21c011fd442067f60600f8ed7cfa65e8105bea7b2560c2a3d531aba8d978c6a61a46197c5cfaeda260d1e5a3d2234ad4d4d15c9809fc0718f2a494bf6c06b8882c0ac29181a0299c2c3ddb6d403109bf38985cb6f7f65d9bf4a27ca16eeaee74113ca3fc1a9872826e441eae2c925c6c6b51b069cf13230ad073ba2cfcf689421d1d968f9997bb791f8fcc0b33d93aaef6fdfba7fa52cc956976e2d233ddf53f23b86d4a0ba47ff0bf3ace001f560024700c4f17a1a63bddb95d79cbea7fc76416edf7b3dd77d39111fb2c428eefb92654da6439b55cde33491217fcfb764996063bd675ea62fb9811c66727b09e567636e6c8d982e6f65fad8dd6f03acd60b30d79a476b76cf8cfea809b1de99986aa5f4211755c4a9606baec8c3dca7770ef1366f4fb6de53e4d3ba838fef144fb0e61ff4ad3f648955db95d2a20c7681252084c442d538d5e4bf0d2027933173276954891dcffd109736f35c2cb1266526724a5bbc506f3d941167fb169289beaf1e5f6f44c2ad2bfc807276cc8b31ce03414720d04542483a9b95db437b80f6c7835e841f6719a3bc1eb59138760dc50071a8161bb4008dd47c756af6cf0adbac7d9d2d6cefd70d01b9d2fcf8f77159b6f1501de921b1f99d288d980a83eb9cd78c3a72cff96655c49d3c91b9a76f998c826c1b6e63fe19f1906fdcab7ce1b4a70e3ee8ccc35d3a74b5d103989a163f1671f9169b894c9b79146ecbf91ed7f2d19b04985b6598a168dcc24e13c8c937b200b687e0a9c32218ac0b353c651d22b6b8bd740b4e4c96b68d8afaa71737475b622efdad1f288db138261c51e7b753c357c6e6f940fde81bd1c23efb4bdd4bb2625fa314fb98094120d400a599da875d2323fe7f784d7f7f02cff4f4261930e6115aceecbbce375a0e0d055de53fbfff07fd8fedd827513b5dc8c91596168a2c4203455de3d5124fc35b50d719d770f1b2a6bb849170609d8991c7ce6657f027824b4d6d75e9c2d0352e4756f8b7e889589e18301ab533ccd69cf826d485669ab0fae342f1e2bc899ca929a034f8fca9268325e85ae9442c8ea29922b614c63bf26168ede3750d1e16dbc1ddb96d8c6eebcfa806091d4ade0e8b57b0939ee335719b870f19df1392d5f3052cb09893175c94d7f583695cccd8dc6dee51c016a8626bd3a072d09cb859aaddec019541f824b88f5cefc3fdeeba33890cd82ab4fb40c310ae931a3d1a5092da0589c32eab840819e36161ae4705574ab02e392010b0c6c7ee76891778e31fdf0c97a1c92a70a0076f372b5d1d2a4e947099126f32800eaa4262f52d440b612e44a8998b7d39b9d3458f67efd9bdb7c338e24b4ef6416ece81fe457a53592b63158bf83107aa1afc365e1da32dfb8eb9e88de6630ad2e6d21ec3d610f11db0e154d2317c707a68e98e7dca8ea832ce0c3b6e87e1c2410db760580d668b81b8c34fa546604dfb83521dd1e97a01a71d203fe81c483e22cc69a685edb4b1e36d77eda636d939773bfde64a736f1c4a61ed9387ff0c2b900e3aa4faa26330034c3dd9d9bd3c21273beb1162849922a37fe7515b720eb4e01177fd2e101efece02db75b0ddffe3f4380da161173c95dae55c3a2f4df8e3401d982890cad9ed87e794925f61a91a18583c26690c6f467dcc6ff3214c98de80b38863acea99f3b29382ef329b4d6a131bd30db294678b995dc48776109dbee2d1d27d807f24c1060d5d4231e51ab8491df45e58a674d0376c6ecfa11e580f610c34b0dbba1a527e1f2cb35da461bc2678fef8526b370870a2d82004f131bfb2716c1ecb028e9463998087aa89bab1bbe2c932b8ca0911da2869eea5c3ee3881760a95f7a8eb50f8dd4a6cd0c0f3e46086dd66bddf46aafb723136d30b06fb11edeacc0d11534b6cd26b54fd2a2b5b7c078625afccad81e5ba3a2b8772f460d10561dcf89c1f0c7b42884b1616e54d802a2c74714e9b7ea8aa0ddd4b6c33451193ba708854c275a9b8010e571817e0000430bab43e34a967163e4f58302123d0938e522095207559bff634aba893be2845e9f21db71bc596fb79d6f0d41c61703125e3cb4b85c6e12a160ff832607289a81e5c338b5e232bd70276727ac854506d6769a8142514417ee8e51e5c6d5451f37cba86ebd6887c7ef292dac585ea00be88df96e7954f1c240169ce169fa3d0b373e5aa01a5cc0000c3b72bca5fcf02e3e6bf9c0479a13f8b6ea053cefec1edff02097d4ae0c786873d701dd471e24224edc91988c78ec44aed74459f1a181b4338bafd7f8aa87d01ae5381d73cd9019890061fbe28e8dc94c43c56ff08cf6c7758d172a6b5e09f8c2a04d622377a613f038ca39dc90c0562b7edaca03899c28e3c7fabde8c898f45512e6e6f750d2259d786142870f0e0cccf6e5d939bfd1494ee6efec567d2e4d95702d53e365c817689c55568e4d1b54f894e694481082b2d2d970cf5a10e938ac8a92e611c67c0184d382309f13a9bdcd55b4324f675670e58fe7e9d8f0cb03169e9122dafffb088529a724422538d01a90bd4e120c0353fb9ca523d6f0612f2e40f4ed973035127a23595b82be133bf9f740c09a935c2322b9fcd332a394e756b7b52f1d987425028e4b45e9dd2cca82e0d43aabf3c06e7dd976e054c93097d46187fd10fbcb27cf8c3affc52742e93d39768323cdd1019fcdc836b6df2676c26e3978de1cec0b48e3ffc5dadcf21e5d41332cb887d517762dd280ab338c2a9008085c0e19af03fcd144adf101f2cbf8bd44412889fdbad5e6f069980d38b132ee9a5bf085d9146326ee3c919d61711627b5a6b555d6634c83920e909c7a2d61180aa6211c0b2248a4390b4ab1a1debc25b9a022e5b2c448541532a46e2c7c2017ee2175f93604fea8aa37458a9f56762d7b765f9d873789e36e7af62697c4e769393abd15171735b08ceb9d53f34249fb3460d91b9b1c3b456a480f7d21058584111054c8121802d811e86c250d56ef3b33620e76d0dcaa4c29375d5995f0d50c132a9572ff2087b3c3167c81679ed9240c6bbedf837f250be21601df31e2a9500a3f92f9a8248faa66666ba4ad609ae2434c867885545e096d2f3146e5f425224601a6852e22c3d325d8aa1b46ac7875249d4612c7640eb8bfad9eb1bfe1b742ccd8359801238fd14d6872d2c3bfce9d495bbaaebb6afb17fe24a25f9941daba0ab3da9bb60e95bf90f00951bba7a1e1059ef16bdbc1f9a03f01cc2e28815ae9008ae0538d13d6194b75fa97641ab1ee517959412ecd458951cb7fc0d83f92e6cf084b315d189b34096cd228d121201451cee19d84a4dd2ee0bce8990b1231c11ac69ff5191ba2fd4ea11485b851a68464a0220f83e25af9c67cb3cda3e41352be9b3bed121dd333040b446a4ff286f39f9ef8e06ad8417ea48e06bffa4856f1ec5dd31c642e49c70e19f968eb21fe0affe9018bf648c15e4a4b98049915ee273e5ec6707122c87bad5c33292874154f4a347192ae7fcf8eaa2e75caa3fb1a9ae3def7bf7a0be2affe3549c7fa4dd0daed1662adc6e7b521e3fa7f739115c1e5d9686647dd28b37a857b0eedb83a7eb254e0848ab6de7dc806679eac0ecb31dafcafc837cd279b43dbf378ce568e3a351fd541031e16c50da25418a3e6a452ffcfc92734dd303fe2fde71771616e6cc8e9f31664367ab2290aaabb9bc088df25cafc2be38b12bf93304c989e32cfbb9482feec80bf145fd3ef14b7956447beac4a4ca47409124d79d39370b82c7fdf161d6453b0e263139ebbf60da8b3c169602d38859f1a2a51486064f893c3e58c61c82eb3745eaf45fab83574fb239edf743d505057e52e256288e00a1a94d0a7688abf6be831c8a1458b46abab2ece16a7c500c74bc620827df055dbeda00ae4dd6ebda939c21e70f1d12a9e77b8a4e5e981e0ba946b2b69aecd0d8d79930c74807fb3c0ce1e63698d128c600f7f48c5ea2f9f83d55e0c8158c826fce72f6b982c04c509fd4144a7fee1fb1abec89a98819b14521a461712ec4dd45ccfd63bbcad521cc7c9456786140310cbef37910ef4fa76a0b9f218cd143faec452d006b0558e48c750fbe49cd83a0e2ed6b05220ea138cb7c2001e05c57a07f84c8d49bd52b3a7b5bb25f58115ca0794a123b6a53c254b3014f02198de9656abb8f641408a683ebbd20a73888d95c4288bcc101526029ba34589015f2febc8ba646c246fb7a1d8202c7f45107c6545ddcbf269ce61b07a61aee601786cf921bd7dd2698cde13a1a9d3046c90f50f78b2dcda8a2c1e35cc7a4590302711c6f39ac220de788bf6373243c083e8bf50297a88dad7513ae23dd90e1508831d3bb6140d38add4852319f93ab237b8b5d8928640f0f80a431a4b7f70941f9cb5ccd1c68cb956b2f473d64050909c7a95f1606f57181d6cca5090f2098512938c7dc06244eba5b7f503ed0ff889c54075d59b1a8be310666bcc5de3e8e60c7b9856e747bd0beba67be4a08f1c5c48fa889eeab0f88c726204a06d8ebcf4237d584209b41d95d0eb897cc54e5d8f7f122cba9ee283ac829dcfc1d38072162d01bed3a0d615eb419eb21248a340dca81e837e888d901b932f609125312834225b9f8245e3c0b04755eb9cf217e44214a7f3c951d0dcdfaa3cd68971fc7d9c1a909756100e65db3927408faf8cc9ca9be4ea96f756a9bcb1546c3d799b37a009457ec6f275471529659bd26f485c8300e9720489dcf2e04dd4e63fe53197ab06bf3ccb731a9d8709f8b62cae790c775dfca53105b3303c1e44c9c11033811c0f232b2277c0a3258506560ed6502924c739fd27afc599b8e648387d91ac59f50e4ecea056480ced0c14726aebaa6a1950e82f200e4df6c5748fb8b231a6900c1505c1107641b05e1dc8b6119e42b5930d423eada653d232d616e04e1489fc482b00394406b931e7e947336c3cc832724f048c9aa07be91219a022c6c0b5918827aa6f17b121e8f013ac553486989dbe67e89172b09e16ea58c43abbabecc27b036bf465ba153f787694774a55759e714485fe02cb757c55ab3c7493b2385bea2c93f2ca81dbd969d9c1bf2d0d8b5db32e7f9ec2a4e19227978c70c00b1dae6e2749aaeda301af6d466d7e9466fdefb3f1988f7bdfd71034f29550c02ad368120b7b37a2af19e538fc2a594e9ff6b34ff1b7a0543015b352725edde565fa3fe80da7b6c0fadabf0d1ce6334648ff88e47fac1318667e172833805213bfb0801523daf456d7b405343dd57cda0a421bc0c0d5fc2179194da627e1ff8927cf28791168d7e6d5f05174c52e481874d74feb11436ec4ef244ebe2196eef2e48eb9a408a6bb5b585158e08d13b38069e7a3540035ff6bb581e1c04aa3bd8deac175bf21226269e17898b88a53068def6ffddd5d83a05bd8c4e5c814375239fa31eb842e679021ebfb4652bd7621f5148b8a75f96869354151b923f5ee8a34941809351833faf1cefb800f4b94476f8aee226f6661a1a7702bd245d17b16dda558a7d4305d320aefdc8f171798e0514edde4a054fec5f961a7ed72e49255c37481107a1dfd33f045acf746bba0e6c61332dcb92b870146f41b6dc69950574e6d1c505a06cf243043d0c5ecac25a72b73100a76289cc2171aeaaf38ce93a601a73b583b24946b4f2d26a8954f9c0ec775af2c38cf418d672cc877917488f3635b76a62c1e2e7f0adc6ec12ad42ff86b9c2c7cec5df920fd22faa123da306058878a90170d3cd59fd235e2dd672696a71474cd0e31b00bfefb6979694ba889180a1ac46310f3597e8b9cbaa0201f6a55a47f169a097d6955faf1f5b2d40b81dac62d1374141a2b990889ceda3a740608e2a10778680050ac7391fece90953d24e7e7416007f2fd904ba98d49f13a66050078f00988943f962b91e5d258e91aee9a7333060c911e1d8c6e9d9b69bd0711b00508e3dcb739b3e3dd51beb7a08ac943dd6fbb1f5ff9a3caf6d071b8e0ad267c2583294658d13b120e1415d60ddc313bb560f8235197ccc477262e92a2856a02372e9b29b200f7fcb377481818cd6a69256f4f256de16566a7fc84eee20ca1dc3dafbee9636213f183b1ff2bf10521f63374801cb0fd3dbe682180d31952b626fe63a8718d5e106bad6314fe42c5158a0e40d4211789a6fe58d00a466c56254886fb88fd8f5adf161d1eaf20d7e89e10fece730c471555dc7b3f3390e656b26843790a3637e73695a46c52ecf5991189fe4bd22fef726444711f2980dc8dc9df57d4d45176a4c007df5613d90e0c5a9200c87a2606dc036b2ad2d15aebcc4ac9a49b7afb75ea836a55ff51acf0d53d1619edc701a4e11d6380d92df0d20c735b42f4d62e99a5b9701e25c81252da0d782eb4630bccb59e8e1ad0fbf269b8784c1101d44e54fcdf1947906973abd97647a5802009760068d3b206e60d23ec95a7f182cd51de316bf06c6ec5922ba12abafb290406c58995a79853011651568e23e0caa55f5d76f21498f9b91e5f89553dab1e890118e124b11ca3020c84df9d9ee08ecbc8ab8ece7c559eda7d0867e4fde6fc90049fa1ff83f7cd07befc7c5b81f85d89ede5561460d69a2b0208dafc991d77b324dc898ba4219b589df712283fcb75674d69c6c65a713a1faf062fecf1abadd503be4f29b6ab35252af32f765eb291b095d991aed2a8f21843c99b2be83e3a4e159042b3256dbef2c442c35edb9cdac6f6a675974777058044e4a04e9601f8920f135e8fa120ebfb22d6c6cbe6ef20c28afa1c466a74048254cb21b103f174ab275ffacb7f0a125f2501695d0ed9a7cee01d11bba2a464dd0cba2c3d1f12a0c912d6b2b3ad5b0dfcb8bed00d936563eedc89f25d3927924789599e0e1583e0d30673fd3a490618304c9fb3276cd145bcd9bfd6b588d030b3a39328611fc7218024814cd704f1b9077165aa00d1ad51111e2ac9feafd03a98cb61904a77e7018f07b69e299cfb5c653144dbcff6547858344f7fa3afb2772273d16cedc591df1a0aafa97a1920d87b2bfebc00fb094769e0872598ba68d1a815d183f91a3128851d93d8261926180df95fe4059db1b37c8062a5a9340fea877bd24924d7dc2571152f2dbc29a725e66d7357d38ef6be04141c2794c001f4a4901e48dc400673d20de83d2f14983c3cccf64941ae15789e01fc2197e4a8f775bdb8985b7c12e9a61cc62c5ca1dde07fcbb9aa9a85d8a66966cf50bb8ba9207dceb90ec4d34d1e0727ae1fca72f1d560735a23a8a7c1990de206f02324b13cd9a1c0946c900bfe320a9f78761402b99ea57086d7e077ee89dc6ce5244a739dd99e0e3d134698191410e6057e266bbe58ecf3e61cded58440bf6f46f2a65f9906143225d1b792383525fcd0e7fb28e4a88c2ab859ff5c2bb2ab8d4ff0c805d5011b500b877cc174932084174241d26df0dc266b21c8920e2d1e106c684b68129146c821cf27fca8482992c51ec232f69df73839c6b1ae4752537ed7a2661a4cd100a6091827f2eb501e478fc9008f37322ca0aed80ec3691925d4b3cb4408b52493ec88a1edbd4b42239a2bdc87fafd27a03b0370a450bdc25a792849c2e7fd69c043a1e94dfe2c3b15c8abcbb3890296a86c229f48be8986083dfcbaa8d3e85b4e6bbbb1fb671ff1122b1427f95a632ddce21fd794e38ebc217295cc82b8813e471287145d2b7cc42907469b168e2cd502a5ba55637f5e7c8c1b59651ab1fee18b1bcad3dde33ac9093477047b20e9c97d60974f166e3b383b1fe71c30f4cb82b3979ce24c40fdfd04590f98be85f1e51adbb774c3063f8b0d3c21e7ad0f52804dad372c15e6fa0e2f30a43c833758dd1abeae6005a5460cda925b2b09848e222f22080bfb0de23210acbf1fd192561493bac336f9a3778623dc4557c6499526cd9c243cf03bc38217a80198f7da0180347c165f9fe6864a56e33c70d43bef93f2946f0b92b46f1d1c0f4c2de377899cdb06913d94942b594b3b9d710d2f13390bdcd2affbd2067f05400310a01b85f77206046604a527ef6aa2bd994c358c5c43758d4fb70c57ff1361074398c50a1afc44737f5dd1d7bd9dbce9a6ed59af38b17c5306f49aea64290ea6cfbba1b9dc79135c0faeef989d29cbd400266c1daa446d83f8e6c8aaae06076f0d193ae7e368146b52012802081d5b0f6ca58a7974bee585a728c4ebca085f469ea3f7a4a87f5cac620c71cec8a82fac220fe552e4714f2ece868708fd158b335fbf8ad3c7d91867bc859e9976f3809412b5bd543cb42afb45a0faf6e11d779286cf918ed0595e91e97bb5189f90cf927856bc338e1c67fe56ba2aa7fb086ac2d3898cc932793a1f056e00093e3a6ef9de713d3c632d731294c8bb3cd929f4759eb55cac2763814b3def4dff19fce96d89afa9611f9448f834efdc8fad097040e09e85c6319e5c43158b761dd245b592335db8216533e8f65e118e061018b1fb6b0b153efa66ca702060f8e3042336680ebed3382333385f8e2cc58dac2858b1ca298937ba6234d0eb7d2c82059dfd83108448087c3af1ec1005c91e2408357ea9dc0ef0e2d3d259b5a386d127c9a90f7131932e3a0737cc76d199d964176cd129f6dba604c108e13c8e3fd0f2ab66b43ae4232e8b56837a0fcd373e200f347219fa0150534ebcb9640077dd28ad7e7da50ee7c8286e85c6803e506bf02d535d2125509c4fd6fec25a4447acd9f6ad70e24a5583221c6f6578e026d1d0301d741b1a65d8bdca25bb3a012fbab62491530ff29dd737916ab61446da60ba0565504d5d4080e32dbd75b8048f4def3d848a5ed64231ea0e07fb4bde0ad3923056492ad1331ac57fa12fec1f0c4b0c180268c2a61ca778f861ddbf150635af1b51bb17fc202d3d0f9b1aba55eb0e0a30232fa01c35b998a67059a7d0242fb31d755fd8ef6782119f4cc4b2674bfe12dade13654010f4984529c9252f116487170ce05574f1aed467512924ca000d6ab9802225d1064296de2dcaac4f26b4dc0d94b717b985a3327ef32efc0b3e0f846c3dbc221206fc80371a5dcc17dcd7c32e4b580d6cc65340fc19425065a9b8742eb5ddbd56c6b41f0a786d5213cacb502ef3267156fc3f839332ff9e24e16d1292c55a0e3ec0d460350dd6072af24f8f2755bda4b053045ffd5527fe70ec43660d5b33f016b95c547acbd302a521a3f216a40c18e811c4c022573890d20265a40e04e37fa2fdd2fa9a2a2733a6cee75e2949d7551ee07aa6971ed0840780492209926c0684747cb6e836a68e844a1b2f150b3508e5606b048c08eebabc90986b5486d39f892c495d59fc35550d3f9a3c2720c8614a86f72e6abe8e7efe82d0db83f10245f11aa1cdc2ed60c6a893792d2d943354c2d0d8be6ed948ee2ad9ac13877904ec2df83381b3b0e0ef1b51e4e62f6c1a24506789b31be6d72d9148e78f94cdfee75ef945017d71939fc0e55bda1ef846c1a2bdc65fc0b60f3f94597f12908e2ce466b873627f50dc8434afc06f22fb9f18a6989ad6b106bd7833d7e86047870be3e34f69481b7b18cd2b294bee1643f06344dc3dc3649e48cccc3eae92a18685dcbe7953e16edd3c907fe11f360e48b621148295135f1b9279d932c1965fdca8b97dfdee3d2dbf0840e79a4c0d1c16383d15a927e49dc0dc02d9d58220300035dae9e9312a21f6dd99f6753acd062b0071b37f847db9cfdd021fe78c7bde6093b9e541c5c9fe7b5e2af3cf2b2f034254be50fa37b02c0b7858a4930b25f966ca158bc646ba09446402753231db07686af291f21b42316f6235a3c938aac2ed7e02424cf094bd6d346c9c63f057078216724677689ebd37b2217d471c8beb32c37f2a51fdfdd4f15537889882642918154f6b5b5e8b034a211c4d097bfd491c66df578ea54c4eca0eee260411c620d77c767fabcd438f857c649baa2bab0dccd9330e20434a44c7e772e327a49b1c3748df69980d9554a644142e27b2aae6b6ee58d2a72e86ae178beaf9dc356315a64214933b1185f371c8f7939b051a658dd40438b51dafbfce0cc948dc108519ea0a9c3d599381a14b13afbea24f5b957d22bffd937a43ebf1c92bc5d1d86e251d5e8d95abb629213f5f4c336402b8c68f3939b81c1494ba5ac072e29877f40b5e3620618df9987720231560cdacb23f291597f193e58c17b92fff7e5b62281a419a1594f24a26f84cc6432d1d08643176a8829aa83d1452e55a3a7dd6527621a99041aa06b5b4171d4e382b0df83872df427e630f50a615bfe31a1930457b284031498574c58800bccc5ff89183a59e552d12149a934f193fc233ddb03921212413ad3e18bec4bc7a1e947e31077f01d4b79a4783270e5578e95b8bf9994712863404560ba919614e933751390da65fa688130d5bb1adc4831f4fa210c8222b6fd543e0996b10efe421d3c46dc7099ac8dcbbceb41cac8bf84de1656ce24b168dc44776fa35c889513e3b2828b08ca01ca55da145e53c60576ad6ee278bef0b4a03ae8869836c04f60f1e27f2658ff309ac27835fd7c707a970dd51563d821d871950c8f6acd1286f4d7541af2d3e02c58629aba8965cfe18f0870a77fda7093ebf63a1776d42f502810feb615ec05cf2a0b7a97e94250ffd90a61008f4ca1a000135be805acccaa61aeddc9f0630bb2989ea04d987a0efdd0107f937e392219a5cf0c720d2134b4d6ade1312aa0cce232d27d821047733d89ebc2b6aa2c108f1dab6411521ccc553953603d8da4c6a123b080cb0d89b73d6795323e33c7a0977d37628f5c6ba44735bbe408c836b5bad0a9e492ecb06a4bdc61fad1491351b0780b33c654b27179815a39f6670517fa3d80b334e7dae784604cc5e3455f90a17427bd8ea172a37c39950d1ea3579ad5faf157d54ce5aa2bcab0bba89a46a0ddc9db66f95ec2cc6d232ae1e5195fb876372d60557ca603289485dd6448eb5053f8b12f41b2f6f45248b8e6aa7dfdd5f34db0a0e5df775528bf9c92d7e65642903c7335e1388adf556f180367cb1e2953d4532c6a721e836b6e8597b84c333193365155bc9df96dd29262d9faec086f796e77ad97fee5d69707773fdd5571f2389d2f44a222144515c2bb8b93ddd28afc91a5d7d92ca7045f8ea21b573d11923cbc74a4df4e3be894d9ccb97a0e7b91b732da7986ad340a08067281dd0288512d7b6cb3a7d2252111b06fa07ebf07766993cd87cf10ee12fdde4fda032de471a26e10bca2dbea1ea37b93106144dc833953acb940ee865ca5bf5ec8cf029411893b965d3c56f12fc76f9a0c43842cad0cc1271baf8c25eca5ff69ff91ff5b30b3ca094af30a19ab9cba6f54b74e974c820fa4bd8d881cfb738014caf86c50a1b081850b4b4e5452dac5e96c1e691f8bf3b818b0623602ee87899b445d2da3e0dd93ccfbf8f7bf36c35ec45298c5e403ec69c92d0929e0acc39c8cebb87e48823184cf019f5f68ceab0613aa9a3d3cdaa66379323f27824287b774a8dcc32080bb6ea7f0dc89095890a928cf858012b19ce40e0e34a49283320a5254c9242291fe69b0164941d85db141d78da30d6d554512049b53762211584d247d1c56966050024cb1667765f81c0e6ec4fbcac11caa785d8283fd071538bc691046aa7c2305e365b6db8f9144227374b8880981b98e08c0d34099a1838d1a0ad49ae999a140739effefec3a087ed2355cd07b962251cec22071846ed571247d749a42f623021b9ec81e467f594457485759a79cf66fd19b3f0741f2e7400d3d8aaf02d3d04bcdc82e0c90264c321200e6d02c72eabc296b7b0d0a4e4b7fe6bdf8e7628602fe16c46a68890809f9de766aace365148fafe1cf51f5b53b20802ebc35ef54003d7468365c6144d5d82f6c1ad7517bedcfcad02a6e208455970e2fb251a860d0a01878111b00ec9fb3a54082e18d6a2b1830dabd38c076e10852acdba5eaea718db7d6bc92fd698ce8d7b559e17e0be53d6d9e8e25f190d59915d81d8f06ae8104d7cdf0e2bb5d37096e93f9279b4263ab4f5417c33fa4c2d98a724f8366d1e1c3d39a5254f2bf1b3766d950359dbd4ec49516cde666632f9041bc2fcc6e3a61698750845795f1439f1e5258927ce8d400db24a1ef6cc56461f95fc873b41e225facff7511bfc915315b06bb705d984db6283b36bed3ae9c9ac6766c42b2e300ba09cde96d329e9ba328a3ee0c4f497463db97ce5eee382a17ec90c591cb9b7024c8cc6c3a78b47aa2c2b26fb9ea1016344708c4042442e73f0c645c3db2bb92d37d5c3dcf71732430590d28c6b10e3d3746ca0addb56d94037156644bae6781365fc2286c3fe00a7e1ea0d022d46b92beeb231c0d53c992b356bf1067385210a5567e9a62aeaab9828557daf5eb1a4d01fa189a6e287035e0433aefde3df63178ff9bc2f9e223cc6a5619aa73a58dff836c547f862996c8cf36a8a3a500edd01bd27c1477a40cc7b58097d4741fa278af8ea4cca5c662a57a5b6bdea0b3a1e0b6d61b5403984b7e05e2c660e3318e63300906fcaaae8fe77b4dd204f5e3df90f95abcf9767c0ff2d4760d7d01ab9f29fa9fa4a8d74220b37a686f1304f014f037cfc0d94c1cfa3522a817adb950c6abe8641f87850078e2c10eb54696102b11db2b0d70eda47b883943a3dd2e4641e498066dcf6d934770a77dd6fea74c697132c4d6d924f68dd73437b955bba7bb43f52b4e751d7a695b960d9f4b83881b1134f135e1839407bce1313342de13206d55e9953b5d3ff5f5fc21a13aebe1d4b1a64a576985b3fb6fb74e1278ea93ff80bc3d2b93925e7e41e15f6a90cd2f16c29e84998439bbbe20b15bd9767e77aec9a1a439d607703c35a25d44e96600f4003f67e652d38d76ab4df561a60e84759ab15ec7cf06526fda23c8e6eb34ed5142dcb9cb1cf911dab416af6c243f32a6e9156715b80e63f856c17862f12bbe6452caa3f72dde551538abb9d2f9f6d743186054fab6e33e8138fa2a0336c640e9b", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c03d33039d45432d4999b9964018d8d6552808849c83503f50a70d594c01b441900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000dfe8f99963b947ff62a9677085fa128d00000000000000000000000000000000651719d455c726d57f7592d491c375c202c2cf87674c88d6103108c767ab2ac48e35bee25bf5e92c02761b47af09b71a0ef8e0d309ff18b3d6b953c338b01b97fa25e4585dcb3d2715bfad3f8b534c1c00d8052e8cd77545bebd67fb24f5e55321a23fd6c4cac362afd5347d83b64a0812e256a3ad805416f7ff7542aea223909155bb34e12d627f4535acee240fbc8f14d67d121dc7e34cee6e339f2837b37ee1d059676ec86d26abe900b1981dc50b2c243cf0f78fbc62ac23338af6f387e7739d8309912a551a2d3103bb652b7b5b1355bd9fb58c4b26e2c8082910b54694eba17179cdd14f23363c598dd4f5ca2408b2f5bb0cf6a4217d15c1e086b1aff8d866b732b95a45d34b55ad379cde70ea" - }, - "decryption_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000005f2218f60b38de08f000000000000000000000000000000000000000000000009ff735fb0aa8f9c9100000000000000000000000000000000000000000000000345271c8cf4b33f4200000000000000000000000000000000000000000000000000020cc2ccb0006f00000000000000000000000000000000000000000000000b5fa1bf66dd6f3016000000000000000000000000000000000000000000000002901de59263b00224000000000000000000000000000000000000000000000006472b7847e3356bff000000000000000000000000000000000000000000000000000249bd9812aa03000000000000000000000000000000000000000000000000714c4b255202de8500000000000000000000000000000000000000000000000d6956453c3db805b5000000000000000000000000000000000000000000000008f18928794975bb8e00000000000000000000000000000000000000000000000000001df754c51e1800000000000000000000000000000000000000000000000cb395274660890a6e0000000000000000000000000000000000000000000000095e68a8b2d4e989f30000000000000000000000000000000000000000000000059805d645f9983726000000000000000000000000000000000000000000000000000158446880ad940d702526d3c16cc3d8612ec9f5ab1a62327fb7f070635171fed1cfab9fb02bc9221c0dfc0f55b09832b6a84878afc2e21c87d4e1d8a87eb424e5d8925c210d9e07172412ad021afa04978190bde455cc48f3b13c12dfe3586d8bacbc33d21dad0201c1d29dd464380aac0a28c4680a60cedd2ca628880dfe1ef15850c97398d3140980f66808c0b14c9e4453eea5445abd16b1105b2bcda4949456ab3101e1222a0312603a4e6f2df28b6629f50f3b75d1b41d5cb948d0c9c403115dbc717e6a03654fefa6377d61e9d29fb073093070787cfb7a4b0b3ec30cde26e163850eda076820f3362d05ed4c34ea9517cbe7350009778810c5299f8b295c908516238c26ce5af7c7c1357caeddbcbcda38130e6cd3241e5e8dfa681f275fefc06783a82ecfdd8852acf679dda466a5ededc1e1de0c75830e71080072b3ef5a39ee8bda20784c461bf4fe943023c69c4a8a80e2a45fc44558452e6215ae47e0ead41a120153167359c0cbcda774be43b125d8a11ace1e56095c79fc32dea956b0e39cee06776a162a29aa02abd8ec6223718d3cd1685078bf3a800d2cb3126260f1e7d00f043fa160234a730fb38797f9a4bc6a87606b469ce5b45c6203d1a4873f38ed146ad349349a9aee02d748d21e7aa1691a8fbedbb127b6a3c77112ee4c0831792c74c847f1c0bc4a6efd16490824b8bb8475f1da1c43d73507e70c149a6319bf197b6f3b7edfe72e0254c8d6f9e925ec1c7a6af14ee97edf125602814ac63aa12247c599da6e712ca5f13212f3f0f2512533375ffd08e193bdf38c9f3ba9dc1a268007e54508b8bccd9f74ede0bf36271627ae75e66e056d189daafbf180042a02b265b492124237a2279d5fc005d099e9bfcc664e31136a23d11bfeee427b430857f0bd419d1e39868f050901beee9e76bee39cf93028da313e3bd1197455f92761af6603b9e68f84531acd38e0664db3970295614bce1e0619bcb180e855510f17ab92678e8d58889b7fd40c25e0a0b172fc4419dcecfa70a776d4a768c0ad24c227ed361f0900d0234be6e041aa3dee023df77131ca5c4f20b76a4c7de061293966e5cc7ea91bf71035b5321e43dbf366baeaf2123d239bf9518dfd4206d1061fd78b5b3cd10843e60b7ad93fe83fbfe85acedf777898443d8f006381cef4180633a4774a6ff9315c59d52a3e82cddfd313d2a65756cbdae9e45c0c5aa6a9254275ce78b21d39dde33173eabfbc681c81df53e5e1937291c82bc55381f7081429ea0e9bff4f359936f343650063ae80cfb205fe4d5505df14d585a2590dce1a6d87a45ace520e5fb37826104e159a7736f3a575eabd42df5857197e4aee180a47b5de368d8fb78de2739753da476ec06a97496dc3049bed74a1fc64bc1e122b5ac08a4694f869833400a9d13c14d62c2b582ef67e076b00a2b48da7c815582aef0b34c5a021d578b1956454fcff047ea232b5b4a02d4f0502c2a4cfd8cd0117c50a71e2db4eaf6a2b74a17ae47c94998ceb3fcd05c73c96a9add350b9077c2dd809e930647b467466f47d983ed8fbdf43a32f6e72c51a691297d20e12848605e09ff184294c3a8491909664881831d337eb81a349702910c11deb4bff99f225f8fdcc9211e9769375fb705925b84428730641554585eb5487c4da3ea9e7440ff3d3abcce49866d314ab0f7705907bf6c500684cab7ab9882ff7e8612a49081e5b363f5e3d6ab435ab1350d25eb7d9f97aa4dfd07fada88fd07f36034efb560c4b0d8820249cce16695d2d94f4bebf8053c7e493b265bc22f8c764a65183682b513a86d2639c1e5aa8e0340beedf6471f141e1b8a8e8d45dfaa926c5c8987e0b9f188a0ea9f8564827981949854017fd42a8619d07cbc0464824f0aea6ba9206f394b9d5260fd699e5832bbe1178cb8e62874c98877fd4b04b1eb0e513aa3b1ddc09310a5fcad3c50f2bad9d5f3c9156f2ca0b4e03b79e1d10a02ccd0870c50ef282b1512d4196513873b063130f7788df1f282c27369f1151ad40f582d6bb16a2b94a4760b80c6bb55b8e4ad2ee4e9a8440d102c868fe6467cad57729eafa0cc616327c0573043e9fbbbb0684122dfb7d20b3d550b2ef4f17e51cbef1fdc614911e2a36c2c17746f37b01985426360e2a9013f6092e5127bbec6272823eaa20afbd5216124ea1fc198cfa2df5aa0e074fef76c0ff35ca8adb52e62ef0fc8c0f9c913552812247ff546ca7d7da8ceff4eff4fd5d898430dd301db1d1902439115723bb71edf060834ec4572f7d4ea861f05cf32312ad5ad55479c704d1921d2c5ab9a7c079748a6b1d7e37369f5a28a83d626022b64045f3fa0b80a453c8041de379d99bdb0b99ccc892f31f80f17da2ed36f0090f5e3094dc1173a711413d254967f32a7e7371911776461ed97ec330d99eff86e25d631826fd520c5eaf220fab467cb09a2a52ffd6c903093d955be3eac414314e49c350d4f3dd15751083231ccd6e2fe779f5d3e5f4b0b59a392e24926f833f88e7f62ff4eda6029e3d1a1f150584b58914fa39228082ac5aee53631c5cc9364383b11218685904dfdad61513bfcca8dc59f74f60ebdc9a36b03c8730930b07820f261bb91cb31ead29b20028e9d48990a5d50e1218e689bc2f934d607196679ee0e7546ffe91c745fbc6083f23bc710245cc9fe362adc0c75a854df1d66a93626ebff3bb2264ea1dffa416a238163b0bae28ae59785d275a8ec59c8464fd75d992557f019378a960f63425e05ca4c3eab58bf0a866ef3d5eea6187f3809c65b94d959735d16eea6de28f1432bb4c8abfb4e9815b4b119b72103e50fc65878db8b1541e4c4c5313f4135225a29215c4cfd502ae3f5942f8d8fb7baa7dff07e365df4f29a6c256ee9170b8205595a47239d5f88aae9de6162ae1332003e7369697be82f2bf955f75ec3a642dd57fd752005bddeed11ed896ee5757d17d5ede4b573c0505f824875179e2eb112a2df36064b8fb9fcaf2290c9ab7a842c64eca3a11593bc346ece99a9b81612a99a5ca0578388e8b8a5e225e409fee6c296931887b26ecbdcd8b8b0c14c19c190dbca881fcde82b43a7b4305b23aeba9b9e903c4ea6be43f03b4333ae4f93109a8bb1ca28457d9326292a6eb1434fe54d0b315fe833dbfcbfc4558fa06ba3f28b7927e73451b4e5ec1be2e14fdfaa723fca09972b11f2f5be411299c23581805a2158f107d8629f024375dac5b41e77e04d31e4a47612a787093240388f5d92673446e08803ec91d7f3e4da3357254fbb85dd613f9c26242f7798e30e8200b2608f77deb7d1c5e2719a0e6705770a407edd4235ee26e860b6c69dc80de486d15a53cfeddcb6f4e890cf26cb916e1f8650e625282d29c03cdab859516c2cae42972342f359f017db84be110eafddce43c189c6516b9150d92db8ff5bceeb4690c86cb5f2bc09a99e6c0cf0e99a69d944f8d3e6df230e70682babdeaa8a1bfc51f7e87399eb0ffb4aa1370c9b4b01ce54403b342c86f1c81e84b324978c9f7bd0668d302a579e82d9a658e81051fd4591b84dfb05d80134d7bd7761b92fda83330459190247c87b440ee6159e47fddebdbc2dc7d1f1fd6ccfe8160fbe82e57f2254da1c224f027a42089646f8fdbeb750207ad222a686bb649f69b727472b1080b0f3bfd31d30bfa6a78029f4dc098c71e472f42bca745cac5fbe192cea70bca1b45cd711f3ee3d3e825a302e399b5e09c862f09eefc3f07870a76b6072c39a809f4bd5560a0ceac9974743df4ec488b876c361d2655f2f3be902d609a100bbc2a77de2397223bce6ad358b03a57e0008b6e4c66febc866021e96c1bc483346a2d8114b596089d6f34016fab7654e77bd2fe6513d714f2924feb52a23156fd5212b6f445e42edb6ceaee423178aea2c45303549206b7b20ffb1bdb3191bd1e0428aee16ab04321ad1eacad7fdc2a8107d9412ab7dd50c7bc342bc045ce4001d505fa508e8f1529c4b17f163a1a77ea740811b15d5962ebe95c5a58ba1632ab352508c28edf99f4f8fb8650172223044709382a1a628a5189c23429696fed90811131b70e7049cbb1b4eafaa0a0ee99a000701f664c4cc4d392eda23359b3ccb421668b16ac00d2d74bb218eca49ae37c13e7e410cb8802baf5b37e085b02e3c10c7243af66e5be7e5180c1c921d1f7b87eafb8e66ef466ce481267df735d131e1d91359213059a47b599f89465d236664752d5ce6324269682b058709509946d184bdf3fe799e092031f4b0a6c1760cbbbb22c3caea02608e3170d378d510939231c3d509aaaa15ccb62ecb814131554e86e180a1cb709629ee5f86ab7d1a7ef2ac08257bdc936b4c829a95b666842778b0e3ea6dc4fe61de92e3d60c01928d92329953245ba406eb19202d54187088faf11c520cd1b273eb8b4fba91c48e70a26b015620762d22a01b104d48d69af46a14a10563597eed134f4b677402d19a028e44273662134c99569c498c0335d13a56f2269591840c47ff19b696c29e28d1d09be778f26af8cc6618eef42357c66d8d65b70d27b96db438e9cbfc28ea81f048f34a4a86158d66b2bf3141249863b1913e42915257f58cf9e9382df14323918b1365e49651ef5f9627a5491b8c00d5e2d2358aa4e771ace9e80a3a4b8e8cf15e114d2d41a95389af530acf1d847969369eb1e2c0f1ae3a93b31e5a63ac12807f0352bd512f4cf44fb30705544d652dfb4bc0589b55fba34a5b3177e3f0e4711869591361d28350d28a386904e44483a9e7153efe49a91d8574911ab67817418a76b1f1c3af09dc74369bf98d0126b5b72c322e4e9f975571b454f1ac1affe25d77532f242cdcbf87ad7b6f75583e876e3f239f912aec3c63d1fcb79238c8604b446fdc64a7b4e11f1d6d25abe20d28412ff685c3d54cc58be9aab29e0d8ae24fc42eb8356a7aed4583e5b0a6b61fb0bd76940483edf38e2bdfd4b43c8967002541b2214ea50989e6ff552b209b7b981cae83d1c6b4bbccd740b281732f841054f3e37c95d278960ee3b512b9d6df72bf52e39dd2c6053d2a6eab3778a821d2de6d16fbc8fec9b4e72bb1f9649feebfd33f12f7428861730b7308e96f4ceab2786475c31def948d54302b7494590bdace0f17b3af521eda992d12be4bf376f23608fc6c9016c7754cb9db557c1ef412fbe7c4672c3019cbdca5213172e629a143a3bd131c75d9ed1f218c18b071151c55ad4a7fe1297bdc9c0e8475b16261604d6900adabb68ee8c2b675fa0030cae02140db32d550031cfbe71e2b0d0d053142d26c3d1474c5c0bf2944f063ef39db46d97147915384a02faecff0d991c9b1ccdb1a2f686701ccfc769f3779ea65716e8697026565295dea7000086f132fa300186285919872ecda05c52328ccf678f55550f80d341447aa5a8c7476c084205a966938acbc7090ec9dbe2510b521797ff0e735d62f8d2f69a530e525c2f092d556062a2532259b37339d43bb56f978e1dd9695686a6ca684a88c09a3012fe1904fde312e2ca2983e85a98d4501967703e6be8afa5ac688c215176d4b4211a017bcdcd5e8d3543a47d996d3c443e843563be932f5df4287e0afc2ce7ddd6af2fae1939da0b097037b0cf342ce82886a692c1ac6e175ad52255b073e0e53ca41c0c9795d1955ff973d53953d547287f5c7ed679740f92ef125ee6c266725d1d00dad30b9f51fc0e34ccaa2ebb0c24e0d6e7812496e93a59d721e4e15f58df8b2ca8fbbbf2e859cdfc65626466daf4ac1b4e8b8812d9ad394e76317fc32e80c8231e67a2fc01a463d65f834d8e2b249ca20effeca6faa42e0566ca584852795723fe9e57ee8b9e60226828bca17559187fe5b0b272f3ed27ff976c41c1d75478165c6ca9ba102acd9cb16ad8d2ea15a86bd43173c3b797a4640167ecc00c224629e20ece2a01712bdc28305307814afb4395b3516a4611da52499b55580a75aa295e4c0865faabf64086bd3623cb777ee2150522663a97411efd1df30ef7c3ab0e6789c0ebc98d74111eb87bf03ff552d543c3992919d9bf200a7d607f7a183a1e14f39623c6236fcb92f4113b775cf602ce79e2819f53302eb8df834cf9535a208ad91bd95e0391f13f30766a9098d450e9c68b8ad9ccc6b3a2adfeff9d9ab702decd05673c364223210fdc4bda9da2b7ce304e46e14a434f3928adce09db7b155443d08b6369d4f776e8326d85ce0023c53ee99645bd0de3feba7f6bb0bc5f09e1299777f3f9b91d644ca3c0ece8af1c5a16e702f02f0a9b750fd3669d019f2f30d8fe4ca82ce52589f5e6476b8b2e890dfc540b186b9747c67faaf572fdbc03378b2102dc41b05ef7727d47325d41c1757890765909b4dc876e46bb4006a20e5033b298e88877c536884c4394615a360bb6421aab40785222ac9886f03cd004bf5c8c38b5d73508373514ce5aaf114a697c6d842f19ec29fb64c28079c8dd207fb47f9671f1594ac148ee9379593a547ef44440af53c3c72822bddbbdfeb303e9686224df7e15c6c25b68e9c87443720c48a9776f59a37a921c306755785b1d934d657e3b458af03e52d6432839d23d419819c48a1659812a713c6e6d374b1cd41e1e41d4f9b51c690797d75eb854b1092f9db0a1a1a599e51f9b306866690c8426778c18e862614404df26178818d703e8358fac2b0fbfd66dabd435a21e02aac9cc0b3f09bd63efdc21dd3e8af6694c60c576c24a7ee7d66c627e9b21d024196e8496697dbe7e87632a5aa74f50c10eed5647de1dd1af2ea9310b26778c12c2717573bc23a151b9abf368b92a94979ae4f243591064f910899e073443c2085c4a37df56bc6990a3d7b1235634378b48f3341539be63e5d971e32679df6d11e6e2940964b11270df8126a038904c252f833e80cac32c9aba80c7c9bc0ac81ac49ef8ff070c273c7704af86a8dca8b04b3584cfdadf7781ee88fc18bbc4480f4b009a21c410c66faa1fe2ead1a6fab5fcf38330d5db3f8b1a4a901113994d07d3f2230336480513e55c9d069f1be764c9b5ff9028836f3116a4861ab804f21515ef308cd69326ad5f7fd1d2ff36bcd49d5cf5874d6dc6883fe0c59831b55708a25788e6f174d4de1db5879954b0cbcd422a42097409368e25cefd72d31c001e14d1458d2c1169a3e151c353dc1f5bc6b548343fc6286783ee2e4fbd96a9031a343bc8eceb04dad8067ab40a4f684853bc85ac5b306740e01140681b06cf1b1e61abd86785541da2124accb154bd4eda020ee978f3966265b461c9cf982195003e3daa4a4d9bc429ee6e499ef0f48732dbdf7e399b47749d612f25b95ff6731bf3cd822b4f61cfa33e5f2302c9177312f1793b8f251152772840ed960061bf0cc119c935e7f43d9393d0bcd9caa842ac41b76b1e3229af0baa4b60a62e8b86286ead8f1c7e94d681e44539c07922c4b055d4e8d7c47631ad995ac843bcbc2124582425774111e835906992ded16883ebaccaa00bd6ab936a13f4002a422e901295999ddcba799c139229547fbee76cea0a2a8988c56a343948140522ebd8bd243b67d1744dcca9d291b4c8cb0b15959b0683a8e089ad6a06e1f7430fe78a6a16515953fa46705833f0d193d0e40212ccb99176bfd70bae77fd2e1b8a95d4f51b2cdcfec53d499fe6dcf5d770d861e869c3cc629b4a8dd3c1dc66a3909eebeb273e0301663c822f28fe85fc8e0cac9a984c7fc007b523a5bb9ec6b36b7c58db1769c76f2f278d5dd6cc536396dca2f025945fd2fa27bf3ecc3df35cbd0828d92db58ecc09d8ceaf1550a40beca9b8a1ddc3dc8397ac9083f0029acc09abd92e0e7c56130a68acaecbbcf213d245937c5cf6fbff8a830134d2e1b268aebccce50b58026622cfba1874a4c28c780f1b60dfd8a100a273d6d7de8262470bb3dfc102509cbbc2ffb76105ca5ff0aa91b65050bd95d9fa193180cbe422e5c18c4bc60fd048426c01b8e7713474b8ce57bd061a6b689fb5bb4626611a6b1aa034694e1c9b3c0c54bb1d69d2dcdef1e9c68f32e38c0cbd89fd5ae33fe136f007fd814f2dc9a60223e64cf7c53e7ae3091b1174c6044e18627cdbbd1c244291cb0646571197786b5893ca5e0679b406bb3d223e468be866a903d61e4de8306f234d041e1a3fa3f5c6eb6aeaab0e88670869698d39e54b3938d06239d74d3c0696879faa1d7646c5f7d5f8784c4b417f9a0f1b6e82c669bfd82874d243f53a0d6c0f761e0bed80e16fc05004946c36fadf1e52bbe354f18f77d936844f8f2837a1dd3066136a228e4d43106f27e0e45ab26d80b9947ef8bbb60b41ac5972a82976c434d503be0e2c00b272bd3aae437edd3e11e8fdd1e1c0906a094be91abf7c92f0baab298cf43fb4db839230ec08eb5368064bd035fda059591b67492ef9effe6121611abbfc7951b7ec1c5edc32c15ddedb962220c97de666e4d659447ec99f39f5201b4ffb8705b824da6bbe18abfc15728f601137ec5b5a1c8d84e8a630446ea7f01c7cad0b9ec04d57104aed1dfd7d78649d823c029eac487a22e8b8db0abef130041b72cf9e4118730c7a37a9802a938aa8e85a16af1e47fbfcef0ccd006aa28129c8cdf677782a58d2b1b178fbf7d1097132f83604e733f6e92ee4ac1f169ece0f08900bc68506d1dd45af9cb1b4136e7912943ea1ca10a735916a98d2a9b17125d1c4d20710b106466203e23e4771769398f777821669a63f9c796cba9912c42fefa3f2c2d343c5b1a189d20de8500f7d3bf8f41c2cafc0cfea257b1d5e0eb70b5eb000e0079fe8e2f94719a9fb6003bfc459fc1aeae399e35954099dfe02690dc9cd7166077a5609146ff55692468ba0581d7f423375db9b58bb59482956e41d0276059c01ae32dc4524104fd547391b1d3da552f400e5695a22bff739bb8a0bf5efdda54933a478f07f734d38dc0e6dfa9fb3f1114e1b4636025dcb52ea6d0082189f57a0305a9bd6bb8d58f85a3ea14b47619153b83189603cb3670791f423a25fc40f9dbc36f9cf6775666eebb8b799c3fc1ec9d530e204fc0a219caeee2914d732dfaf6d90cfa7dbe7c6004d48878ab72c1fefc42970edd609580dac1208464bca46fe66ae3a6e48e44b1ceec1eb0899252aa0472fb01ce9a7d2c9dac511d0aa98a4741b2c03ddce57168f635c9bba0bbfaec05baf26d6a9761a0874b506a1ce1b360af99a58700c1917728c40d4fad6fc7f114238f8e7eca23cc5710923667ce035cc236b421452b6b3e10b41280607f9e2e1cf4b3a0b0cb92de09dd52f9fc09dcc7a9f83b768ff435e724877b8b1047845c644cd427daf2ece6cba802a95a9734ccc369520a3c7d14b47ace1b7ec24b8c1ae07231260dea7758f69a7126144215fd22666e023acab0506da17f1b41b5db4e7aa1e292cff504ba888bc11e08b7299e8cbfa12c25ab95f010be24fd5faa516eb6e663e03e88b958e110a240f1b0a58392051cd475c8d5283582a7a895f88b1e397847082e8400896854e2b174b18eccc0cccfb21fcdbf9c80dfefe1eb43d95a177ed0e90489babd9f203009cb3b55e2d801ac8cf0092fbadeda034035b9b9fae8a82f25275cd9f98fb8e2cf1c87829262f51f453590d5ab815519bc814eeaefdc63b3c038980685ca3ac0e16597a6aeeb6070a014111d76a9dcfec1722b55924609292fa01efb87b2410137d3ef5057949859cc0d0845d38b477f143329fb7f4e17a3672aa2e3e0e553820767739734a4af349051f158325726f05794d64486cc9e3eaf86278956533360bf3899a41f64ff6343e374c95afb448eadad6291d3aae8b9ae35fd90bdf03c026d05a806dee663e1e2b98709a8db8217f7b81f3bfa08a2baf999714a3a9cff0284accea238939b2d128aabc13d77755b72d4546ca1f10fa41b84799718cbddc1059173d7e82d096978f15b4fdd6ec5feba5ee8ba308b650bed6ba8ed98ac69620c914d34a54459427d4ede64a8ac5713d516a9d2c5a3bc9508a0563aee0aa67153990a7c9c56ab31a18ce9f14729eb5840f5dafab6cac449e091d825f0d2d221cbf22723745bf8a3d21f1f063678d1d4f2ccf232c4b9ef2b210a3a87e688b7f14b0127356470742d89e20fbec471bffd389d525018175214bbf8a0ed3985d590707143a50587e7766c6eec90202aa7abe19e628f5524b210d341561da277e45280cec55e758691b77f0d677da529ca66bcd41b91f0b363d77da552d94852e7e244eae0c3eefb1d2dc0d97f4c4262bdb4cae9e618baee661ba6f639b0a26742b238f1da780cb61bd26c3523443f794d5fd138c9d8d99c0dbb49b03a934acabfa01d82b2a0c340a58119733c53c95e45d14d19819ae00303ca8ecf01e893e9bc605f79aa179c86781869959f1bdaa84366d8411fd611facebc2805604d0e52c4d0fc8cdcbb81af78749c9fec45483beef2631a7747e6de63c181bf7397577329d102a147f0dfe10a72550a272061da7229a50f9317781a083c2c76b17d73a98662d068f9daac7b4efb7a2977a6f72b791041e7e2c90b8ff6599bb4e95b40cd7322517ea12b6b3f84d358ff132568a58b3796be4cf6938fb995389176701a8321421828cdf8f8f2365ad7ca92c5a60203d33eb39622aa364dc4a5e3b1eeb039cbc2628884b2e29f1316d2aeaf8ccc9166c0989751051f470a07af8baa6c2f1829c00b1eadee84e10ac8295130a3d318a4fdbb4cd0eb6e2ceffd75cf194e4cedd6a1bcd11a23a294cd6f3633618ff7921f1c7fba81054db3c26349d9596892630f0157893e60d2e627242b0b09dbd9828edf171c9526eff98da7862349534c9c0ee1101958d04b0cccf85e8944da39c259be4a6074270f05c1f4a29ef40e70f52c21cbe9a44966109de6b9f862bf0bd7a16f6dd9c6dbe9ae4c7ba1941746638924a1dab3dce7fc0c547703e9f3f82ec3c1d78518a898ab459fcac1adf8ae501e8f9105acfe79fa8b3b735bc2ef3fca97b7fb91995586b31ba6b539d533f2c31b4640122ea13c67579b817e9b9ec4a8cf1ca621d00addaa4c87f963814e7a46572e924d74c54598496a81449fe3a9bb276da5181665eda779c6a0442cbdadc75f4ba2c289960cfac616450d0661e264b320106c77bc6844452069fe7479118a335472f636b70154f373927177eb41465ccb3f8df8d83fae2cf87fb5f2bb87ac35cd600514cb85f197823acbcc4f6a22c4155332cb29b4767438654848719a0329b8c0da447badf47e77c228e85ec8584a544fb7946e2a56d9d5217c8c6fb8b2118951d43259f872ff6bdaf052b3431997bea7ed5d67b95b96c6b43c935101ea4c6a11de929e90523dbdbed55dd60f220fa0cfb381e86cd1bc5069cd9790e08fd642112fd0cb52ced94d970d9a2a42e42587ed71375beaefbdfce1d2a3ea7d924dd3d27c93e6bb50028f15841cc7871a497fe9e5dac1df5837ca414a27e589737f00e183c2fe4a5af9b91dc146e4afcffcca00ca2f3c58b49b3a161deae582f57d5490ea4ce2f8a8cf06cddef900da54494cc98d445c575542890d49c26395a965a5e1ae65c45619783fc52acc0a6120e5f12b68f7e52ec4b52d71966d1f01ffe5e5d1927afbc8229877850d49ebca124268922854de5c795133dbed04134cfacdf1e0fa606452a1e3d4eb648f3de8e85b7ebf984dbdcc371b6875a28b1c552bad8ef1ec0bf100fff7a9221b0d3e74798945fc46c4c6e95b01d977f84990d482ae36910faad48b0169cd47753388ce499bfdb57b2b212b6eab810baee86220515c6b001c1f56ad998e170f1db27f80cebbdb84e271a90a69de39bc17f93cc1bfc5279131683adc6581ede9f720e48d0d0baa9cfa633f9fd044eeb460493066287613208e31006a8003e0c079cb8e1d62cc0322c76f864846817bd99fa507c1584d64f1a6986061afc0d7bbfcf0431b1a9d0d517d1279dfd18473b5db36129acf565b619cb2b918056aa48e7460902779328a69d6bc5d014d689aa609fa5635f7d082015c833ca75ab3466c51c7571ce015d1cd83dcaa78beebe323a9b7af71588b63e162e9b9c1101d3a2f4f4cdd7e239f663d5433f508a792ae12d40208199a6f79d1541cfc0389d4e850e6334db74b0df336658450bab08b78b741887dd9f517591021357f267bec10adc91699cdb500dfa7f8a604259f0b2a89eeb419c8cf60e0702ed11c3faf7728a7a6e6b44b39b565ef53cf7bf2db11e8e4af084b6874cbfbb1c8a87581e891df1e8cef1443828f5a77871109a03c58ff256a17c401ca9d67f2252929901314381c9eeef8ce6968d3936a7ff09a838094777621ca041071aab1b4d91f1bfc7c1f75e84c062ed419543fcb585885fb564a90c536c96aa02b6b913ded1d13ef20b0eac4a7c389f46265813c0759b182d794c65d02c07fc324b3000d920bc224d902ec8a03317be93cd5ac0b49ed7153c0fa3dd2ed5356688b9e812b0112f1ca337e714ce51d2c61f84d7be3b2265c96ee138861f1c4ada7074bd09e5f0a0bddf585829f59e62e4a4af22188185ebbb5bfa0c2f0231b083a4f8900caf21378789734aa0f5234c7666b08b69dafddb2a5940d99ea588a0c369523323e8a9c8213fc1ba3a26df889fcf23b24a4c256da2f51ff82609d264a082969a2ab5b9ac62269b481c17740dd908d1f56fc2560ee211c4f697f43b0d706d56ae075264869fc8de2d914abb1a71d013f2cb9ab13fb29cd7d3bf51f8fe42fc423d2a899d98f6c7f6ebe76ba652c25bd0940adc42a09cd661b13a4ea5a882287ca80181793a466b37c963e91aa6340b409bba41b0c7281f1b696037fa71bce08642224bf9bd38757de198b7df876f91a93241cf592d96d526b5f9b88af75660112b2f8814e2a8a208c599f1112793a2727113ab6dadad3ca89ae833cf0d6fd0955c259a6b7d7e8914d650bf07ff7ea007fd88a3b8ca4d29ae2d2c78b75638ce1bb52f509daa43eabd1491cef1ca38497861f8afff1dba767367c73594542e0910b7274fa9418e41d1b741c6e9561e2d33679e7d5e54809673c2734224fb7d204c850474e988128ec7884bbee88fd16f1ae662e22aaaafe93b75e98f1443546936991be1b22646f21ba0109e4000eb65dfaedb3bdb686a07a591164f5f9b2e0c93931cab777dab2efbc30459f5e103e56d94eb3c8664ccfc5072660f6b71bd197ed62ea352f641b88b1cf7cffc409db6eef03ab092421510b92e550c1ee770563b7d2b5cd225dc2cbe9daf3adce7f78d919d00ee9a888b1d1931538730448a1234fa23cacdbc55e8a2197fec080aef3b753028782fb55217067fde08d14bff6b203313a0a7cd0bee9cd7f503d36a051215e4cc330e4ed337d65a5862c7fb960ebde60d0182e6c4c27c725d15dcd7317ae2248f6bf457eb4000561557e89ace578830012f467934241ad8353c566400403e1c32718df84b1abcd27e1b3b41453bed902f029aac31e996c58185af2cfbbfc7559b229bec5f0a2cab1be1a4780077648215bb92bf13f3500045a77227c585ccdfee19c884552ab83930fbd2eac2ccff682cf499ffa1e9578ec7d56671ad100ddc8ae91c11c54bdc79a4f9e2fc4cdf11d4077138ec807eb7cc24425efe4ae4844e54496b4528bca4730d2ec2a2c3d913b117bbccbf150fd5cdc249d26a7b32953569f4943269c135e7ddd062cbbc31d39e08335cc653293c1e38dcf14459bd107f4474d957c59cef7d913acc31ba6b1a100801f29e26b448d5609dd13a55db68ae63e8d8ad79328097fb700524a12a71920b2f7763097b5cdf3452f2012e95db3c7b091724f79d56b285e58cc1d7bb9fad02e72758ec7cf0255da295e57b269e148bb4e22d7ed55b6b82d600a42490720f190c985970717aa7038a4659fd6bb449fc696d64d2b29f1f8c37e5ac71792574080b86e629ce561820bddfb81c5ec6f2146aa269def6aca2218c6851eb477f2e24f9006fcc134aa70708115a567ae4f4d2f9d4fbbd944ed838d807ef036ccab90ec3661784d4f9bc991a13bedb10508e647c83063e5407cbc0de306161c4d64905f372a6c8fc678f62557d021bfc1c440df6b50ac833e1d811fe94436049fb970a322a89a1dfd7d10e7978ee4691369448f5fa685699ac8d91b2db2ed8828f0723e78abb7cb9aa7f47a522d43c463371ec5ed097108a2044f8e11ae0e49a3e2f13de49b1e23fcd7d3fea1d4fa4713edac0d8120da252ec07e924fcbfc7302c5529551d781f10d0443a57cdd41667ff645749d945696207c6b2226dcb907e95a4284b421078bdd08597f1d991143c76f7b1d903d88a2cfc0140cac4249eabcd001608b20c3a0c43164859d7e650a590f7e209dc4f640c2cbc34ace9460318f5320c4f30ac648f4a3a4507a8c58f15e4b699aba1ed1b64283c437eedcb24e335f5116b2d20bdb18cf6314dbe1f2efe23c09ee06e5928a09704d08db9f5325a048d167189d92251a9884dd3eed0864936ecd65c5992e41226b00463538a69d457ff12f4589f23fbb25863028f7c41318478807faf120b95edb2c4223b42938adcef30522a8b8167e74804e67d65dfedc6a47cac97fb56d6f2680157109c5764f0481fb82d364fe92f2a5108802e169e37522ccb9d2b998f07e7dac04afae9ac00d4128e8c79fc5b4939ff95d9e3eda7b22d6bbfb581b6c4218d9ada84e8c999532d0ead62a21266b5ba7070ac646586ebc3046b0c7dc9f40201e6224fab8c24914729a5ed6a8d6a511aadee612531ba4666c1c3293f5b896fae7e6f7a2519d4714a", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e4411dfdc7cb6265f100524012f038ee1f205bf8789b70b46c57463dedb4998f7ac800000000000000000000000000000000dfe8f99963b947ff62a9677085fa128d00000000000000000000000000000000651719d455c726d57f7592d491c375c21806572c19ee2f3532e970d617919b3aa8cdc582782dfad0699badc631f75128000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020ef8e0d309ff18b3d6b953c338b01b97fa25e4585dcb3d2715bfad3f8b534c1c00d8052e8cd77545bebd67fb24f5e55321a23fd6c4cac362afd5347d83b64a0814d67d121dc7e34cee6e339f2837b37ee1d059676ec86d26abe900b1981dc50b2c243cf0f78fbc62ac23338af6f387e7739d8309912a551a2d3103bb652b7b5b000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - } -} diff --git a/circuits/benchmarks/results_insecure_agg_micro/report.md b/circuits/benchmarks/results_insecure_agg_micro/report.md deleted file mode 100644 index 5a0553ebaa..0000000000 --- a/circuits/benchmarks/results_insecure_agg_micro/report.md +++ /dev/null @@ -1,214 +0,0 @@ -# Interfold ZK Circuit Benchmarks - -**Generated:** 2026-06-04 20:35:42 UTC - -**Git Branch:** `compile/all` -**Git Commit:** `5b66ae732e6c8351f58179b73abb25cafa372cec` - -**Committee Size:** `H=3`, `N=3`, `T=1` - -## Run configuration - -Settings for this benchmark run (integration test + Nargo circuit benches on the same host). - -### Integration test (`test_trbfv_actor`) - -| Setting | Value | -| ----------------------------------------------------- | -------------------------------------------- | -| Benchmark mode | `insecure` | -| BFV preset (artifacts) | `insecure-512` | -| BFV preset (enum) | `InsecureThreshold512` | -| λ (smudging / error) | 2 | -| Nodes spawned (builder) | 20 | -| Network model | `in_process_bus` | -| Testmode harness | true | -| `proof_aggregation_enabled` | true | -| `BENCHMARK_MULTITHREAD_JOBS` (max concurrent ZK jobs) | 13 | -| Rayon worker threads | 13 | -| CPU cores (host) | 14 | -| `dkg_fold_attestation_verifier` (EIP-712) | `0x7969c5eD335650692Bc04293B07F5BF2e7A673C0` | -| Verbose logging (`run_benchmarks.sh --verbose`) | true | - -### Hardware & software (Nargo / Barretenberg host) - -| | | -| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **CPU** | Apple M4 Pro | -| **CPU cores** | 14 | -| **RAM** | 48.00 GB | -| **OS** | Darwin | -| **Architecture** | arm64 | -| **Nargo** | nargo version = 1.0.0-beta.16 noirc version = 1.0.0-beta.16+2d46fca7203545cbbfb31a0d0328de6c10a8db95 (git version hash: 2d46fca7203545cbbfb31a0d0328de6c10a8db95, is dirty: false) | -| **Barretenberg** | 3.0.0-nightly.20260102 | - ---- - -## Audit status - -On-chain verify gas: **complete** (CRISP Π_user + Interfold Π_DKG / Π_dec replay). - ---- - -## Measurement methodology - -| Metric kind | Source | Meaning | Do **not** use for | -| -------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------- | -| **wall_clock** | `test_trbfv_actor` phase timers / HLC event span | End-to-end wait in the in-process test harness | Production WAN latency; per-node deployment cost | -| **isolated_nargo** | `benchmark_circuit.sh` per circuit | Single `bb prove` on oracle witness, one circuit at a time | Full protocol pipeline (different witness path) | -| **tracked_job_wall** | `MultithreadReport` per `ComputeRequest` | Wall time of each job on the shared Rayon pool (≤ `BENCHMARK_MULTITHREAD_JOBS` concurrent) | End-to-end time — **sums exceed wall clock** when jobs overlap | - -**Harness limits (integration):** all ciphernodes share one process and bus -(`network_model: in_process_bus`); sortition registers extra nodes; `testmode_*` enabled. Compare -runs only with the same `benchmark_mode`, proof-aggregation flag, `BENCHMARK_MULTITHREAD_JOBS`, -commit, and hardware. - ---- - -## Protocol Summary - -### Circuit Benchmarks (isolated Nargo + Barretenberg) - -Single-circuit `bb prove` on the benchmark oracle witness (not the integration actor pipeline). - -| Circuit | Constraints | Prove (s) | Verify (ms) | Proof (KB) | -| -------------------- | ----------- | --------- | ----------- | ---------- | -| C0 | 6847 | 0.14 | 31.05 | 15.88 | -| C1 | 57818 | 0.34 | 24.39 | 15.88 | -| C2a | 41244 | 0.38 | 31.88 | 15.88 | -| C2b | 79591 | 0.57 | 28.76 | 15.88 | -| C3a | 120114 | 0.57 | 25.32 | 15.88 | -| C3b | 120114 | 0.57 | 25.32 | 15.88 | -| C4a | 67494 | 0.48 | 26.63 | 15.88 | -| C4b | 67494 | 0.48 | 26.63 | 15.88 | -| C5 | 123624 | 0.55 | 25.38 | 15.88 | -| user_data_encryption | 53732 | 0.34 | 26.58 | 15.88 | -| C6 | 86927 | 0.59 | 25.40 | 15.88 | -| C7 | 90841 | 0.46 | 25.33 | 15.88 | - -### Artifacts - -| Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | -| -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 10.69 KB | 0.47 KB | 3125331 | 176184 | 3301515 | -| Π_user | 15.88 KB | 0.12 KB | 2973085 | 170236 | 3143321 | -| Π_dec | 10.69 KB | 3.47 KB | 3640972 | 187248 | 3828220 | - -### Role / Phase / Activity - -| Role | Phase | Activity | Metric | Duration | Proof size | Bandwidth | -| --------------- | ----- | ----------------------------------------- | -------------- | -------- | ---------- | --------- | -| Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 146.24 s | 127.00 KB | 128.19 KB | -| Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 133.91 s | 10.69 KB | 11.16 KB | -| User | P3 | per user input | isolated_nargo | 0.67 s | 15.88 KB | 16.00 KB | -| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 0.59 s | 15.88 KB | 16.00 KB | -| Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 54.40 s | 10.69 KB | 14.16 KB | -| Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 50.49 s | 10.69 KB | 14.16 KB | - -_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **20.45 s** — not -comparable to P2 wall_clock row above._ - -## Integration test (`test_trbfv_actor`) - -### End-to-end phase timings (integration test) - -| Phase | Metric | Duration (s) | -| ------------------------------------------------------------------ | ------------ | ------------ | -| Starting trbfv actor test | `wall_clock` | 0.00 | -| Setup completed | `wall_clock` | 2.96 | -| Committee Setup Completed | `wall_clock` | 20.09 | -| Committee Finalization Complete | `wall_clock` | 0.01 | -| Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall) | `wall_clock` | 133.91 | -| ThresholdShares -> PublicKeyAggregated | `wall_clock` | 146.24 | -| E3Request -> PublicKeyAggregated | `wall_clock` | 146.75 | -| Application CT Gen | `wall_clock` | 0.01 | -| Running FHE Application | `wall_clock` | 0.00 | -| Aggregator P4: Aggregation pending -> PlaintextAggregated (wall) | `wall_clock` | 50.49 | -| Ciphertext published -> PlaintextAggregated | `wall_clock` | 54.40 | -| Entire Test | `wall_clock` | 224.22 | - -### Multithread job timings (`tracked_job_wall`) - -| Name | Avg (s) | Runs | Total (s) | -| ----------------------------- | ------- | ---- | --------- | -| CalculateDecryptionKey | 0.00 | 3 | 0.01 | -| CalculateDecryptionShare | 0.02 | 3 | 0.07 | -| CalculateThresholdDecryption | 0.02 | 1 | 0.02 | -| GenEsiSss | 0.01 | 3 | 0.02 | -| GenPkShareAndSkSss | 0.01 | 3 | 0.03 | -| NodeDkgFold/c2ab_fold | 8.10 | 3 | 24.29 | -| NodeDkgFold/c3a_fold | 35.48 | 3 | 106.45 | -| NodeDkgFold/c3ab_fold | 7.88 | 3 | 23.65 | -| NodeDkgFold/c3b_fold | 35.91 | 3 | 107.72 | -| NodeDkgFold/c4ab_fold | 8.14 | 3 | 24.41 | -| NodeDkgFold/node_fold | 18.72 | 3 | 56.16 | -| ZkDecryptedSharesAggregation | 1.61 | 1 | 1.61 | -| ZkDecryptionAggregation | 48.86 | 1 | 48.86 | -| ZkDkgAggregation | 19.84 | 1 | 19.84 | -| ZkDkgShareDecryption | 1.22 | 6 | 7.35 | -| ZkNodeDkgFold | 114.23 | 3 | 342.68 | -| ZkPkAggregation | 0.62 | 1 | 0.62 | -| ZkPkBfv | 0.23 | 3 | 0.68 | -| ZkPkGeneration | 2.50 | 3 | 7.49 | -| ZkShareComputation | 2.41 | 6 | 14.45 | -| ZkShareEncryption | 3.95 | 24 | 94.82 | -| ZkThresholdShareDecryption | 3.38 | 3 | 10.15 | -| ZkVerifyShareDecryptionProofs | 0.10 | 3 | 0.31 | -| ZkVerifyShareProofs | 0.28 | 5 | 1.40 | - -Sum of tracked job wall time: **893.09 s** — **not** end-to-end latency (jobs run in parallel up to -`BENCHMARK_MULTITHREAD_JOBS`). - -### NodeDkgFold sub-steps (`tracked_job_wall`, per fold prove) - -| Step | Avg (s) | Runs | Total (s) | -| --------- | ------- | ---- | --------- | -| c2ab_fold | 8.10 | 3 | 24.29 | -| c3a_fold | 35.48 | 3 | 106.45 | -| c3ab_fold | 7.88 | 3 | 23.65 | -| c3b_fold | 35.91 | 3 | 107.72 | -| c4ab_fold | 8.14 | 3 | 24.41 | -| node_fold | 18.72 | 3 | 56.16 | - -### Aggregation jobs (`tracked_job_wall`) - -| Operation | Avg (s) | Runs | Total (s) | -| ---------------------------- | ------- | ---- | --------- | -| ZkDecryptedSharesAggregation | 1.61 | 1 | 1.61 | -| ZkDecryptionAggregation | 48.86 | 1 | 48.86 | -| ZkDkgAggregation | 19.84 | 1 | 19.84 | -| ZkNodeDkgFold | 114.23 | 3 | 342.68 | -| ZkPkAggregation | 0.62 | 1 | 0.62 | - -Sum of aggregation job tracked time: **413.61 s** (parallel CPU work; not P1/P2 wall clock). - -### Folded on-chain artifacts (exported for Π_DKG / Π_dec gas) - -| Artifact | Proof (bytes) | Public inputs (bytes) | -| --------------------- | ------------- | --------------------- | -| dkg_aggregator | 10944 | 480 | -| decryption_aggregator | 10944 | 3552 | - -## Raw circuit benchmark JSON (Nargo) - -Source files for the **Circuit Benchmarks** table. Persist this directory with -`crisp_verify_gas.json` (and optional `integration_summary.json`) to regenerate the report without -re-running the integration test. - -| File | -| ----------------------------------------------------- | -| `dkg_e_sm_share_computation_default.json` | -| `dkg_pk_default.json` | -| `dkg_share_decryption_default.json` | -| `dkg_share_encryption_default.json` | -| `dkg_sk_share_computation_default.json` | -| `threshold_decrypted_shares_aggregation_default.json` | -| `threshold_pk_aggregation_default.json` | -| `threshold_pk_generation_default.json` | -| `threshold_share_decryption_default.json` | -| `threshold_user_data_encryption_ct0_default.json` | -| `threshold_user_data_encryption_ct1_default.json` | - -## Notes - -- All nodes are executed on the same machine in this benchmark run, so inter-node network latency is - effectively 0. diff --git a/circuits/benchmarks/results_insecure_minimum/crisp_verify_gas.json b/circuits/benchmarks/results_insecure_minimum/crisp_verify_gas.json index f79dcc5621..e8ad7eaebf 100644 --- a/circuits/benchmarks/results_insecure_minimum/crisp_verify_gas.json +++ b/circuits/benchmarks/results_insecure_minimum/crisp_verify_gas.json @@ -1,14 +1,14 @@ { "verify_gas": { - "dkg": 3125084, - "user": 2982310, - "dec": 3716480 + "dkg": 3129772, + "user": 2982274, + "dec": 3716573 }, "source": "folded_proof_export_plus_crisp_verify_test", "artifact_sizes_bytes": { "dkg": { "proof": 10688, - "public_inputs": 384 + "public_inputs": 448 }, "dec": { "proof": 10688, @@ -17,17 +17,92 @@ }, "calldata_gas": { "dkg": { - "proof": 168476, - "public_inputs": 4980, - "total": 173456 + "proof": 168500, + "public_inputs": 6016, + "total": 174516 }, "dec": { - "proof": 168548, + "proof": 168596, "public_inputs": 18456, - "total": 187004 + "total": 187052 + } + }, + "integration_summary": { + "integration_test": "test_trbfv_actor", + "benchmark_config": { + "mode": "insecure", + "bfv_preset_subdir": "insecure-512", + "bfv_preset": "InsecureThreshold512", + "lambda": 2, + "proof_aggregation_enabled": true, + "multithread_concurrent_jobs": 13, + "committee_h": 2, + "committee_n": 3, + "committee_t": 1, + "nodes_spawned": 7, + "network_model": "in_process_bus", + "testmode_harness": true + }, + "proof_aggregation_enabled": true, + "dkg_fold_attestation_verifier": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", + "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 13, "cores_available": 14 }, + "operation_timings": [ + { "name": "CalculateDecryptionKey", "avg_seconds": 0.004100166, "runs": 3, "total_seconds": 0.0123005 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 0.023830764, "runs": 3, "total_seconds": 0.071492292 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 0.023864584, "runs": 1, "total_seconds": 0.023864584 }, + { "name": "GenEsiSss", "avg_seconds": 0.006982236, "runs": 3, "total_seconds": 0.020946709 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 0.011392764, "runs": 3, "total_seconds": 0.034178292 }, + { "name": "NodeDkgFold/c2ab_chunk_fold", "avg_seconds": 19.208712597, "runs": 3, "total_seconds": 57.626137792 }, + { "name": "NodeDkgFold/c3a_fold", "avg_seconds": 73.755109361, "runs": 3, "total_seconds": 221.265328083 }, + { "name": "NodeDkgFold/c3ab_fold", "avg_seconds": 7.939563041, "runs": 3, "total_seconds": 23.818689124 }, + { "name": "NodeDkgFold/c3b_fold", "avg_seconds": 74.192332597, "runs": 3, "total_seconds": 222.576997791 }, + { "name": "NodeDkgFold/c4ab_fold", "avg_seconds": 8.053911625, "runs": 3, "total_seconds": 24.161734876 }, + { "name": "NodeDkgFold/node_fold", "avg_seconds": 18.833933861, "runs": 3, "total_seconds": 56.501801583 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 1.521341792, "runs": 1, "total_seconds": 1.521341792 }, + { "name": "ZkDecryptionAggregation", "avg_seconds": 40.263324583, "runs": 1, "total_seconds": 40.263324583 }, + { "name": "ZkDkgAggregation", "avg_seconds": 4.224364792, "runs": 1, "total_seconds": 4.224364792 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 0.809992791, "runs": 6, "total_seconds": 4.859956748 }, + { "name": "ZkNodeDkgFold", "avg_seconds": 109.022803694, "runs": 3, "total_seconds": 327.068411084 }, + { "name": "ZkNodesFoldStep", "avg_seconds": 4.647117521, "runs": 2, "total_seconds": 9.294235042 }, + { "name": "ZkPkAggregation", "avg_seconds": 0.364449708, "runs": 1, "total_seconds": 0.364449708 }, + { "name": "ZkPkBfv", "avg_seconds": 0.211209652, "runs": 3, "total_seconds": 0.633628958 }, + { "name": "ZkPkGeneration", "avg_seconds": 4.395378527, "runs": 3, "total_seconds": 13.186135583 }, + { "name": "ZkShareComputation", "avg_seconds": 22.39370002, "runs": 6, "total_seconds": 134.362200125 }, + { "name": "ZkShareComputation/batches", "avg_seconds": 12.171766298, "runs": 6, "total_seconds": 73.030597792 }, + { "name": "ZkShareComputation/chunks", "avg_seconds": 1.902716645, "runs": 6, "total_seconds": 11.416299874 }, + { "name": "ZkShareComputation/finalize", "avg_seconds": 8.28821959, "runs": 6, "total_seconds": 49.729317542 }, + { "name": "ZkShareEncryption", "avg_seconds": 3.985343364, "runs": 24, "total_seconds": 95.648240752 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 2.956951902, "runs": 3, "total_seconds": 8.870855707 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.093147319, "runs": 3, "total_seconds": 0.279441959 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.124994966, "runs": 5, "total_seconds": 0.624974833 } + ], + "operation_timings_total_seconds": 1381.4912485, + "operation_timings_metric": "tracked_job_wall", + "phase_timings": [ + { "label": "Starting trbfv actor test", "seconds": 0e-9, "metric": "wall_clock" }, + { "label": "Setup completed", "seconds": 0.848621875, "metric": "wall_clock" }, + { "label": "Committee Setup Completed", "seconds": 7.025832667, "metric": "wall_clock" }, + { "label": "Committee Finalization Complete", "seconds": 0.002882791, "metric": "wall_clock" }, + { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 121.533931, "metric": "wall_clock" }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 149.002549083, "metric": "wall_clock" }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 149.509563209, "metric": "wall_clock" }, + { "label": "Application CT Gen", "seconds": 0.010494333, "metric": "wall_clock" }, + { "label": "Running FHE Application", "seconds": 0.00006275, "metric": "wall_clock" }, + { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 41.797659, "metric": "wall_clock" }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 44.993263542, "metric": "wall_clock" }, + { "label": "Entire Test", "seconds": 202.389158958, "metric": "wall_clock" } + ], + "folded_artifacts": { + "dkg_aggregator": { + "proof_hex": "0x00000000000000000000000000000033c428c02938ae5db473a3df5b57e14ca000000000000000000000000000000000002e8499250d826b93107b5171b2467200000000000000000000000000000037844a699088bf8bf6ca7a1aefda2c000c00000000000000000000000000000000002816ca1b3c2edd4f2d7fc2be82cf530000000000000000000000000000004224b32d8b419c45019cc4b880d6d941c9000000000000000000000000000000000017d26c752552e1c6ae00c61b2eff940000000000000000000000000000000bfdae1a26be8bbed5fc87f9847d7b13be000000000000000000000000000000000025207c73badaf8370d6e599333218c1409f5e30ff5f4c912106cc4c9ce538df24f8bc749898d21f5a9a08642c32452085940f7bbf660902666053a9958d5165f2e2fce1ed04e965d61a33608fe78301dd35ee04b50cb3205c4c82d32cf32c2958122736d033a7b988dcf66a062a1bf2e3671e5379e3a392fa932d9df2f0a73060cc4d509c857a7619b28a15ff5c6ee03b0644403f490d1b87fc60e9543db645b6ad58c03aed39364a9ed52f85b3aba050e5da3cf2b251ab7fb0f4e239764303c4c9bad6edd358c817166afebdbd8211161cd6cd62bfda4a0b44df725bbcd2f5ce40b2f430d4194a472b401ad23288f0c4ff969fcabeefd60346e654f290e4226331c7cfbc26d71768d5016191d243e217fce0fc29c8ec93fb45023558d87cc63e19f31bae89b79f217bd8caa8d7ca408849463e0fa31f97dd9f7665959ed5f39bb091b9486cea26b1b9b4c63c43fb91534cb89e88e6c1a14852bef469fccd9ed142e22b4f6b50ff3d7a2251816765400da286a7f17558f24bb627f412edff56ec43330d56247127992e6b7d14776022b2f22d58dbdfef29a0bca13dae435f063b1c3ed1ad0b9a744f7f813e8d7e7552b713d4c6fead2035ccc1f7ccf4fb17b7db2b1215fcabef1e750aaff8e83e4dd074bd02e57bc769db069688d8f1543091193a0adc68d81e5249068d64c77e0f22cc3b2c4fa0064b20add822a11927211aa9fa06a3a8a24c78737d94d3fcdbef62cbc068db1090c69ea26149f69d7b2c57fa6e56fb17ce3db82a97d7eb23de2d81e0ffa951578bb37b636148ebf6b5791bfba83bcad3056a5a68e20ea6acbe51e23ab1278565a9f5fc1b91d1cad76c23eb989831c194caf9368ca48bd1a68b678136b7c6c01a64eda91024a3229e919dcc83e0df3b735d4ad2f364fa37ff659282b505f99a7f2ac245ea4f8a1177247accf7d0e64cb4db70ad21cc21411afde32273c2b3dc613e6d4b4c1b4b57e1de10efa799bae5bd0b3de6131c545db6ca37a291ac4246ad440086f3697448cb9ef04b225d40291a465c44200956fda50ee050649a3bc60671dc461cd866990131a00e34ddc1754d586910af92bb48cdca487163029b930ffbe3fc929aeef5db3623e37a545dccc14b6bf079ad8bfeb3b708b26b57334a6bfb076ab8c092742a5af9ce0df8155e7f864ab50318b9b8daa084807226c953c3e1506ca59f3db2f432d19dee35dd2bcd4e0906d3b6988d10e019a28ca6602578ebb7b98c0be4b4464cdd9f80c8426b0c82b6c5aa0f08085a199f91862ce65d8b1c54e8a5cd0a9c4dbad32f2427e57df2a6c42760b77a2d014a9cf26648df6cdc920224620b7b4a9ddb676a9c205369f29bfe251949dede157ae140cfe8a0866d467bcb5dd4ff8f683cb49fea0c32379f148ba3a81adb64c29241d2a9a5f2270cdcd9f40d6f9c1cda312a8126314834fcddf5db49272f55e97eff929157fa99135f6e21d99382ecafb1968bc7119d95e75c49b3ebd8cd10176a97925ee5ea80d77c67f9db5340142250a85d8f167221e67582ea6697d73c1606066124265c89d626822e6be69b4d9a5173b028f0aa7152fe8cf3256dbea4d36a0fe2dadedd2ddd18a6b2a15fd9ab5ffbf12daa40c44eb51f5ca4023dc867959f59e0f6be1e9e4fb8e428b80cc366680c49c11bfe4d4063d0c69508cc02916274ce31052ae27c6bbd996e9acb67fa5e64936a43c470bd98c310015041f96db6c0c9b1c438fa396653cb4f77828e8c2e4caa0dd0585ce27553ea6db6b97d76370f9de00981196b56d1f23505ac467203c301b1f610fb1fc31cf4ab1be986904fddc571a3274209958bf6f1aded85226e96a839aadab6534a9df3653d754431a67f4dd103e3ff89134c56d4ba2910c64459c6570b66e940dc9fc6d2fb8e6ef081c0e471dc5dea20dc12db43dbda342c98048388a58c77a4824e1f5e1d66adf134c2d1126c865a96db0e2731ac805981757d267bd1b524b7c6cb9082158432a3e93461d0205342c30950c0d7eccac5681396242b299acd47ab5b84196e59d06365e3b740d47efb08c0bb21d415f448f3e608e8a4ff8b49c423185b259a5ee1eec6f73303007337c0250efe535f7cde74bfdf719566bce355d8f44840d09e9b39a8f16111c173bafcf0d81fc6eddb26e8de9245bcebee9f65d947bde686f1ea666bf06d110c3ecfeaab5ead648d48c606fea097bcfd7ebbea69c77e411175d7fcd36a0291f30d3eb3e700f5ae4166df0ec1b1757a9aa8de71aa0ae3577185f1c86b4ff4105ab34864302c93e13e84bceeefa0043ad86803972712f0104cad27b27ccfe1f0088bff107bdf970853469b2855762a44a26f7742ec0265e4eec66ae00f4632c16f302a9ee983e724b6ec94b8d73ad1c8fb82ce7ed696d5b69006692e34f3417190973d17d869be93014216c56e8250537af35ba0b1329554e005cdde2222bf10e719c1a231acad72329e505bb310b70e8ea9e885bb521968492bf66879506f71163f945630e4f9aeec1a061161b75e4b0fc804edb06fd2d2602c4eb3a5f5db804769268d1a533bed54184987e0a0f334508988683092ffc1b98080be3d37fc90c8a1f9c0ad9af7d9f7fdb7d1817021b913e56abc4046281e9c9d03ced0bff5009b6d8c474a7fd30e0eba834e71fa2b2c210aec09adcd5f073f77652ac9f865f2fe634d4f20180fb3571cd5da28341910728491a250f7cd4d244d8c4bb6b92991ac2730381bc9f312586739c3c3535117d0367b9d3e27215229ce2e2641a32d802f7246163780445ffd353739f7fb86a55e209048fbc3c5827aea6271a739e09146633f6e895bc4c9c57ae2ec967a624228c30b73ea6ea4dc6bbb09b1de48bd619e8e1ac76dc5873c86219beece17c917a1099574bab88da51390295e51f5d2e2698458facbdcf784f10ce1e2bc67cfee96b1991e217957d1dd5ead66a27e16a265595699fdb026a741eacb6cdc356faeb3d57fff966735f99c1701bba0b9ec8042d0eb9797a620412759075f4bbff25062112e9c8ca14ed78467ca14aa7180816af70028f1e87d15b92780d109e87568ac9528df2099491386f1b88490c038f0d0895c5d3881ad1e7bf69cffa827e4aba430b66d569bfea6f63bc64325bf2231408560ec4643f31f232fbbd165acb86b5156a86c77219fb7723fa662d897b9b03ff00bdb234f155d4058db9b3eeb822d0f534ca350bed3dd2fa069a3b9d799d1e51944bd92ad35238088a0517b88ab30623797419b1e2ce73fff2205812e8db1d54225c9c989c13e9f366e918857715d3f12520cfcc1c40faa5c41c4b4740851ae7b395f562792ddfee0edff2e693ab513706d79331d7bfd34a727efec2002b202b3ce19e1451b961c8d0179cbec41fd61444006dcfe37f1c650ce8bca4a80225e64cb2b96fad74d2c81b43dbf5d3d395d6e3d38fa28eb8039e5976f94950152c2aa5522c268acfefa2e8d66c875510fb855ad2602c2040988a16abe1b8218425c955728069b350b98a539df5966be8470ae1c0d879ca9065726cef5fee57d20487fb1d70a9f942a5c5aa7e76fcd388e92d999fd03b4b08f3893b67531b46f705379c6d377324636fc9b23c338588121e39b1822932ea92f7d9edf04443615f1c873fda49b97a39a92525c3cf4ba158e9ec3c3eeb62203da724c3aac7dccf8b0c2974ab4739ae907bb1a37aff8b5ba600e54a20625f2ff5e9604687435136cd1818ef8b5c392f06b8e0e6e99514a78e11c3bca4a62541c330a3db6676acd83d1c4394ab7bbce3b0b9508ea04f1cb7f8b5d336c083dfd4844002fbd33295d5db0b68f6eaf9cffa8a2b9b933a1754f4b76e622ff637ac80650cd46a50e4638dfc26eae73e163ede4dc4ef979c2b54b3dabad6fb4574763e5252013bea28ad356d01e684638709d113b09c9d89688c6312694fb07a47ab162c8002420149a98d93091f171b48087036d446c9bc00d2945a528966c8dee4323b959e5213f4a097f01c24c82ad1137eae3de1ab4a61f3417cf03e9de1e4fbab28d23f3859478ea5bb090bcd80b6ba4e2fff8fdbd6b12df07c2ad74ab25dd13c9e4a566adac8f5e26609bebc1e14878800762088ffb0bf4209f703dc7a08486b605e08281a1d84f5d021d7cba2981eaeda77dfb5fa1693254ea56f5bdce5787b8fc5fb64bc3597bd3a0c4ae4c5e3bc433251e6a8574dba6fab74382708a1d970db82f1f3fd0739879c18b483ee2d4e43598b64eef711f6a02269bc6c44fe20754133c9122bc0785d312827cce742e3581174ada0f5f925125f10b9388dcd461a3aaf9a03d7e14bf80f03381f52c964c01ed5a492914611aa4f57598bd2095215ed085ff7fd4336c6ad0f4f9e09105b27565026b99cd904d75b3e1ef6238302ba32997eb22fc41d1fa10b671724acddb8f247c6e78253c41418d337e988eb5a79331bb957c1a1b90be72c185e9243b854413e80090fd400547857f33eed4236812efc3df6785a8148272ab028f90c013d4dbde3e9d9c2e41d09a5405e90a5008423b3f2731b07a2eeca04ec5309832279f8b4de85c0f021a8327298a20922cac216f3794ae8780a19f72ad7382851349f27654789bacff9f67af87363e20cd8b4a60a098a1acebd7d1b143b952750e1d3b97a69abc1e87fdd3880a3d54434b1012d543d4614bb23cf860c74251f22f3b64916ef26e15ce24ca38d797ffecce87f54e441caaa6b5b5b6301847a28ec1efd7dab8bc30a7f643c6dc15aaf70f8278481a407394a960d3f0c0621e39d9ff05fb36fe4464c61968ad9a675b20f86cf2af8e791151983bf5a9406ec1edcd2b7be7492e3ac10efd70dedef90004bfa55f61a53879e97080c699a074731a4dfce7488a15537399075df4f9e76483fd827cf84aab3c52cb4af9ca302301a237ece1ed67a3cc5f9e950c6ced6f4abf27479ebbbe1e85956b0247b9d1075578d839c921d4e68dc275b3935873e4215e614b77356aecf793b0562441a036adb424f6f1c7cff3004c7ef3bc97a9fc416da86ce3531cc6ba3c7aee830d00386c604cd0f3011fab5366cfb4a0d7f7d24bec867af5ed07a73c017c797199c2874360d4ca5b5342eb20928cf9c64e08c2e4f86c60b5ae5a69e3d5e3aee7cf2270fd8062e97f2934fa0cb5ff62b2700a28f3740bb02087adda4e9b35cafe66b1d2c921e9065643f8e3c0bd192b2cff240e35b7a8db195ea4c3d30ef9570db7e2e94124a2cc433934c084b294e4d008bf0a8b5141950c8f54f7644140a2dfdb7036c5e1ac0e65e75bc69e493d675ff640f5b5971f71ac06df6334a87272f12310576741b19dd1238d58f9f0b4a29b9c9e0df1c8727aaa01217f308d4bb32e11b263a96beaf90146e79ff3ff7a9b849e9132cc734533a6ee50cd5bcc091c87ca7145aa11c6be8f1494d71c0bf23eda14ebf9bc1dc2cb62c5dc2aa56976d7c94a629bc417e4b22414433f31e41c0b4a9905a4b519b9ae53dbba4237d09cafd3ac620386e1f7ed8bae91f40bed11ffffc0197a959616500fe5fb5af513ed152ac7a2a704ad043bf1a50831d9885ac372b7175a711cad7c8c597054a78c0188d0271118f570874147e146e5b1b9c85fd1c172157a3a58318f5fc901236afce6a3fe4042229b8080a3bba6479fefe51adac026daccf5fc145fe6a37c358c22d7fcfde05844189a5ffd5b64a5a280e2baf779281c130dda5a18c4736c84c08b41a3aaa166cb4926599033844ca65d324834107af8d8dac19a4a86f35cf03beffd8397f1851d2df94ba497ba154b0e107fe13827b5ac01038b343238ce602b71141ada1279a366e4e36a657ebbb663c1ec32caa59de305d31855e1087ea0ed2a75c4c9b05c3578d25d454a1355d0e1f23c44e99840bf82e5926f26478c11c5c39eb513e0ab532b1ad18a321b60d7872bd7b1f22af018a052ebe79c47abdeb6785983b2a13562b08d9b56810436edb5b5d1668547820e41e22d8f2b105cd1536be4590310afb9dee074d4194b9ba0b65eadfc3c2ce78e713dac1c42be17506070157ea2c14e50592d608370e973f2d847c804f61c87627c34cf59f96f4df678f164d82c720eb2c51931b6fabbbe6c742368bab6f7f55f16c149f848c3ff076b0cca9173b13a2a145d3f52923109e484fe8313827af8d90971feb4bdecc10667f50c5dc74302d7bc56b5618b7ef1bbf36890931efe48ab106378cf071a19ec400334ea61b2cd11d9f27416ba78d7d48e39d5fb9fa76869ff3abfce3c4c9d7615bea5b223108d6cdccc5a194414d97828b003464a5a360778874cb1862090d900f9562053b1df1851fed5bf3d6fe81dbf82605064161c48352f4ef77e638d0eaa9333fd4011f243d6d0efcf0b28104c3aef40623ad08e66a0b78858afacdf9970ceed1f58f3032e7691c5a8c96a88ecd2c55ec37379f3612f2e9b1fcb23ea49abd406c7fd107fdad64b17eebb6eadc1e2c5604e7fdfb117c66c9a81c0724013a3dae0de58923fa3f044ac45820b2bcf41b7b23adc3c5cde82cfa6fa4b6600272a62db311c31b65dcb45eaf9187fd1162802734fe3695e9ae81cb4238fff03b889fe03315bf0ca90e98b1b034f802327319ac71e38699ab34b640811173e1dc48e591f1281908a5a7cb74d7d466fd5b6591ca83e6e3bda7e114d0fb16c891070f7c1ac57a650db94a15f441115fbf18444a8335a72663bc742a6ac92ce9b454e0d6dba4bcc60894fac7d616f193e046ecbe63c9572bf42c9d4fae558b0eac3ddb117beb77fb00a241445462a5badf2ef47567d597af1015d2ef11e5eb96a79ee8b49868a3be1d66a8c07c4b3bc8d0427fae85130768d0b0bfe37a733eb7957f2482c66bb148230757c601d49db46fcd43b6d9b6b4f8b666e89ea06a1225027cc1d4b2846b1406cb6d9f65cd6e1b7e61ffee2597b8f731dc0c459f553558c2e966f558f65f89003149c31d154de6b7e2bdd960ed9939995c4fbf2abc8c62477bc836eb7093db0bfcb84ced80c1b8f11d2a58aae10ff44b64c0f879c92b91ab002276220c1294281e6c1a0d510251e5d83c3723c6ae5384c28a7411ebb6dfb8ed311e384fe0d62db9d1b14d4b9c97948041d7ea60e7534ed3eb320b720b12d9b0494473ed89c40431a12f9fcf91d60175037f765b9616fa940cd082e59f001ca5ba9a7d28dcb913302dbb38d60e0b68e1ba353db1258be6744b54866bb0abd429b844b23dc73316ddf20ec57ea3035e9e6f2e604287e35e35c26e6eca8afccbe1ea541293823426451ac08897c831a99db0eeb550cb4c053b17f629bb509833bc60e118c66864050e07eed2c2d71cda38b6c1977c85a4b5a8b42b7e5f9cc755fe76a5ed8294ee1052d9ee8e095edf8f8941d5bd256a9c544a475ebaad8cc3067ce5523880b2910f98b398aa71dfbfd149ce00ec03ffcb47cbf768fd26de0b0f1b16ed37bf896e0b365693cadc883d460d44ce74f1106509ac6b305f11fa262d8966143ca6c7fb0cacaa75429acf16ad50050602f4b2cfb743891d2e79b56d44ffc6e849e90a591b524da12a0d1a71b3642e4c6206c9354bf87a157644695b84dd820f98b8805e2029d7c21e588c089e9b92f49cb2eea86dccc047a115ff6681cc50f7abfdc87c108a3cb16490b6c5da2953ff5af46bfbad95b3bd37335f2246af6fde5bfd94941b682567213a67cff1ca664f048f16e0c1aeef4c83da219bfffdc740b44a8b4b006cd4d364e365ad999d36eac8948f0c1dde2aee7d5097496a6878b59092fa16168e5b27f8d7941c7bb1133a0ea962c855812b63687f821460ea7617ceee931a156215fe35fd1f6b40e1c36691b692118074d7d34f39cfb97a257acb4edb66ba082efccb103ef01e316034bfb8b6c1968d83ea12a1d0e4936d732f24f4461a7c227880e5f939f19e4d8203ae380fb7a1fc57c75b505adeeb93fffd1b6ba931bb23f09d59bb2d33111c34c573f857a47c6c92e4bbe08ee1d29c96dd54ce331c5b29df4861b2d01bc2dc19f0c4cbf1829c3381700b80fd5e6d90fec1359fa4d05a20e2bc24fccf833f6d3e68933c167549d5588b5014f8c84fd9b245a44065fb69077a5347d9c8280112b62ceebd42dff4863e02b140db4af00d2335f9242ca3900bd6b59e4c76908e8b0bb43c750beb856667eefb767622ca5cd969b42c4beef50c360fac77d20fd9a4947a45f3b450fe44d15c6f6f464af91d682f6f0f7dadd10da0a93eed318fd56d04ee97c87c56683b11e5472abf70e0720e30d0a1bc410918e975f196491a9a6150e14ee1a62f36165f594031d8c74d9df6a040dd2080702b17186e127e06212bd73eb1a8cf64941fa2501039e2f88fb2082f30a966f6a9192ac51370487703436e2ffad04a34bcfc77d6780f1d138967d050986e244cce22a5ecea7d3d61b5f97eda79b67dce7cbaf09cdc0d128c912366584263e5143a06304ae2d0b212a3d1405134a60e34fb74c5005c3f36b4d3d4bb7098d8582e1a27a26c8568dd450bb7bcb75cb7c167e625e774969268528c77e9e8b2a6a132d810bd50db5654e8f3a2eaa27e4364fb19c9d261512b3e3882e0c5d76c73fa47732d5b7b36e78d3de9203ff68395ace356e2309815b6322e2f009975ca7bbddeb410e85c2870d4f75b1410c9c7eec177e7cf56282318eb0ff8f2ff5f679a312923240eaf988d07750a1be96040e282cbb1e1459164b8f2fba18a8ba8d4040cec84016e12c088b020f647d2e789ca50889f0f1aa8c9deb75a55b1be997a244a74ae069bae56fe4bc1ec36d4e9a7a7f1df65e7ab17600ea20e6126bfec2a00f0f3581cd83321b03773ccc1850d7a7fb36081c9d2ef8d5cc0da5d778cb0200f52fee9090c2409e57a8a3ef1c592804bab567b53d4245ead397203760beb4982d6638e20aeef0e9037dec10466c6fb060d5294d54559f7e8c93c9e4bab7488c2f837b12448b03688870608351c84ee952aa6f067d8ffd82835f6b9ff9eda5cfb001f9f2d1b5256929efb03d5790823c02b0f7f9e9dddd5b1efc38425ff782fc2419b691da8d8145a5ed18fac99d9a105097290492c9f2aa33edc939987b05e098ff071173fdc1bef9bb391d35a6fe7c7330918450321b9f93a18bd71dbbf480c854ae5128d9af2a357830ffd6ec8811deeb892fcf5dd5840dec6feec858802b74398202ce1341adbede3eaa5f17d72c1c75b21e32bec202476384480e7b49d6cc4e44b22eeba797dcd51039ab67c3fcd649ac6adb1bf2a9ce31493a4235f70560323b529cb2cf3335288e1524b22ea180ce7cad141cfe25d437453a8d8f19db9f1070909d71292d83dddc83eecbb83e3048b602f6ed4379de40df7b8876e1faaba229e05acf479c4719c883815f3a028d9475f3f3d101e8f87abb4fc4f3b176e4518782f2740a0e4bf0246d726971d2c6243948dad1eb5a6472cf52b66241ef221637f194ebe1d596553b4eb08f4f723483437c9109c075482ae5aad6bb41a43ae44901c7f08666362a2f5edcc5acea03cfeba62bf6cc68ebe8603fb5588e5bdf8df3d0dc60942dbb9b2adf60860ffb5eca60a9cfec8e56068a820cdda4ae18263e12620322749163fdb81dd522915a9ee9556c722adacab63446090222032508457fd27d0232a2ea37d03be29126884f9f5a5d347cd0eadea44b890c03b6e1770829504ecafd2bf8048b88b377d3eb5e52256e5179ee5f0e8285237e50fac7ab5392b062b5a33dafb65a0e1e36befc273f27aaa124f63d01644e27f41e06a2a72629015145183d05f134871bb5b4bbe7b3d72de4de579ba49c3f96feaa4fcb29353d81bf2b84824c06838a4d9915da9d5c3018d1cacc37056de4314cc1acdd22dfa9703ef7776b4c3437b4004ca86add8ddc1cb196659bc4b135c7a0a82f9f8bbc6071b61731a15791e2f7b96e5cccd5d718d571f2277e4424525902aac7101f18d230b3a4a78d9f1e19329a957afadec3086a951991da6498be3e3e1bbd0ea84c7381f9429c361df1082ed68719bd7b5c7fb4bc4c67695392c027821cd30a07172561d32c8aeaba598661789891232794e7a3d47d45262ffa69e67c333ad1a8b6de1239814d3e51d1170839bbc555c0e97ab4592551ffa6b44e45ae49a392e59d8111bd08a257475e60a33d80def29038ccdd3ddd3ab4c672e09f2ff41a95cfdf63527fff23f33434a0db7a12648e2340942dc452f42c0583640f20b7b4ea72401922b0d45557dc98b711a731cc317c7a5ebe999355b25451f03ba018c45193da7fa23e8dc1352b23ddb7cd6a4b0d68706d86c93d555456c0e1a6de98e43798d74751599271d2d826001cfc42548d0cb6f554c8b2c34601d52aade8806ad846f8e3b0cb5e102f89d144fca29779d226e4c1739474be795dff3ad673c8593444ad6182a614de064f8011aa444c076fafea052d92a5096281cd8a60c62b76a8d3e8010096d33162f112c3666192af19012f0d9bbad7497341544ec9274553e10d8a6a70bf373bca5cd4f65e0f570c08a0a54ffbc024ad70ad5230174a06f3a5f389346230dec2a987d280f149a926b27a1b8f7ab29d23aab4015767a9dc822802d99af11fac48fb7a33c3d26f008205bc1a74d24dc677a44729dc44327c1d60a7c5820000000000000000000000000000000000000000000000000000000000000000020a6f1fd264e524b3b5c5ebeb4f49f97ddef85bf83d256611be321048ed39c4e1ad11ec8e0f81717a0124eb32fecb2557bce654a44d39b3bcd57b3f99980256f300ad8ca83edacbed783c98ca57fadb094d20fb5ebb4255aef9c67194f34c7ec1a2f30b109cec671f0807f985039f358e93df8070c0a5bcd75671306fe676ba2021ba8fd8a67f9bd690376fb779a40f42a0800f69862d27c91927785b49407902aeac1c3fd24c639edafc3b309a081177eebd3c1a9d5b54bbcb596b7ac1db9141590d559279f95c6aa2260a7870336b592103e484e2ae56278c69333e48204b32aabd3c9ad2cf6af20d5f88a1dd7b9ffadbd55b5356b379942e6a840a9feede8185fa362cae1ac6a7f594ebf80d385cd58f102f658180b175230d27703831d110fadd9c97817d73bc2b75682d781b09d6af30010b1526e5988898c041b024c4311e662e5f22f8945f60a024494e58948989eb6e8523412e8fe43cac586af2fd90c25d8f80579aaa879901a7e447da183d1118a05a57c64814fc764fe7e2023aa21abaa270d2175177c4dcee43d10f653ab480dfdd62d45208d6ab5671703d8071418733030ec7bfe0be838d17a9af0d303828262b2b2562250df953994fc3b86031cd0276a30633b3850b4914d78d5f12d8d4edef281ef1649d6df28e368127526826d1bd68eef92f6db6c2374918deed91fdc13310c9d6b306b20c4343009d514b5bed80857c7b4dbc4c3d7f6040111d14492cbc7fcf753418907f1bae389f51154650456961011760deb18dbc291a9f488c2270d00f9d0ac9ab1c188b00db80694134f183563de9d1620a860e2eaa5d5bca636d53230aeab5b7a54d372023d1229375a6739d56ede083f430df8813b7f6e3f10dc4f7a6e04cf677188fd01ff0950facb8af6e83153a4ac05dbddfb3c6dfbc75c8c9102980fff211b86aa8dda107cd163982008b4684e1f3a4ed5ccd9967fe4cd852c52417620be81a44ff38504d1790a0d84339de3f81cce8e89c6ed64ae195623c6b23e26350e189bf4ecb5294291aeb55042df782d357bb61f19c919b1a0f1f4d0241bca04e6af95f14fa92f0ca081ee357d61a21afb8358c0de0e4576ff63cf376e0deaf9dbe73c8af4c92885e14d033fc4be7ebee281b1cc247b50eba32a04c64405cca6a0e35b4c59af0e1b74351f972fdb241a2107052ad640f9923d5ecf496b31256a4f897d1a9cdc2952270a85a1f03dd368a4359fcf17cc4f050d8e412bb4a3584e908bf8bf12300349a2278d1ef65aa440a80d6ea1a853291802f652c85278fe200ea2804ce411068e27ab9f59228aceaf76ede6363c56a3f94df08cd2d453bf51fda57ec136c6000cdb8c2242ecccc864f90ab8c1db607f9d0df44a04e6b55ae1e37e8f695d251ed6e7e745d2d28638c498be8e4efa7ad5b3856e68cb0d90bdcb7cb53a3627f02ce2af64ff81b0d98ededf696f6534dfee969cc8be133795be6ec6c1cdb754840061368388f6ca75b7d9cebfc675690cb5c18c0443fbbb619cb64f1fcd86d86c2ea09322f8d180006182cada14315cf41ae0c5e798a74e3f28a297cecb797d711b26fced9f07650c37f86422209e98755e02bdb2ba1b58a05eb870c75e94e7880a0a2bb87e32328526e0c6a4ceeaf96ddb964c8c908855eb6466ffab5102ba10142c0033f18bdef1e56e6a280f55a92658b06c5d921bfa6543aa441a30831389035a1e4b6a2945814229a662a08805c3c9802cbce1716f48f2d33ce8be18958c1f4da0f5caed20a9adf65b95ea09039e450d6f52a19339f4e6c66430cf9ceb122d7316b52bda1fdcd60ee0bccd1e8ecb8b64f12867c6d49e27ba1a5c6e1649411ae70ad4527b49d5310c82942118500dc374ddc178fd822c5e866cd6d6178dff268f28c047d94f56e70828ef5ebaaebaad8c1de141ad3a32f35c3ef8fd9837570d883b67a540b9809b679a14a5db75c7bae209425759077d2eaed4089212d0471af976de32207430e002e88962e71a79daf05e1896e0816d78316fbca2a1a72723ca339096ec88962b527b6601f959fd1a37b29dda15cd447cc1ab62b1c08cae0d13791f20fc1f23288de280f42f2b9fee6dd83bf7624701de1470ea2a66a2c70e644fbd75409605f5b84a22ba466913139ab007144a46e4871f20912abae00324cff97a038b83bfbf70c33d3b2278bc8a1f86769a8fd9c34b99fff9dd925b3b232e774974ec96de2a9b0bbb599489584898950c74d4caf91978adb23cdd2e8913eecccdbab8ec3298bae17670386cf7bddb4974a4029e919216fe2829fd340428304927f124a2c8274bf17d9917ab48a8466e7f6075a5f86dd1ad1ac2b9bdb509d94dab5c0e94ca70a1811f28672d0afe0587e76617fcbf14ee236a5838227721a834da084e4a40ca87f01664c6280ffd4f94ea75cad18d4cb2a43a72c6f8fd1d43893d262a14559636d15f8744388642fdf302e5106d7bedb8dc80cfdc859215094ed5d78364e2c09682e8b2092e6d3039b5623e84005b0b997f8de2adb34427b6edd8f7968dcb951af309632c1b63983d07990b2d273fbf85c529f75c8b3b045185eb56385140e523fb6a1f63e106d4127da4a7a7bfb9fdb8ce3957ec28cd03c5d93b17db22d246b1ec5d31c0c443d3443dc19681eb0bb015402297d1281b23bd95afc95ac2f712a63952173ec273de5bec96a6d826c7a70041e18ba7d6bf0c4da7381e4bb1b0ac5fc83cd19d9c760a2cfa57ed2546b1359e438130441531226ba7c323b338b185ba0723fc5309fb1f5cf00b19d369f53d3df7efe40c7beb19c25663aac2f92465e872d49e29a4e708f7f758f640d264784822afd03f7af311f196400509612f4cc59254817ee8cd05133cbf8bb47e1aa6f0823c9606ff1908f5ee6d62619a5b38758789aa7b8b33d2c29402cf0514ca4a19236d6a1d4cf126d44586001a93526d6fdc9bf319bb0eef38a52645f5c3617b8c58b59c2d70a2173cbed7ada1587b71a18ba9865358df390cb6c66e4e84a5efe78b89c14aed2a0d7d04617d8d179a1ea422fd17a2738687d065405cb53a030c67bb97b67cf4c00666637e7de01875d0d55515f070d6be14214b4a97f51af38f9506847ec2ae700956d5e9c78a619331c1fc69bff9bdcca8e800319815da443ff3da8be24a1ecb118f501b6fb1adbef0d20117d0fdc16d453b42d27cddd7f1d3397708919010cb15c220bad7d06a696962a5b0a333c85b053b448643d18d53cbb4841a2e214607163aef06c6b5caf713747611d04bda209fe8dd0197222facd5e6b1bd514421031a666a09fbe72f209571e8062be2ef339619fbf079fee0e206d133e4bfb148d2000bc35f4572d9d2f08c6bd84ebbcc0dc8eff36e5da64a5744c7bb0db627938528d2fea1234d4f73b84402f852d9e64b282b15d1f2a908eae33004a7e10c0183158d5d182d672cc0ca97b5a39d5ff433029a2d7937c55c415d03b6df5ac395e3122f995a6b1d8b6141e9eb4326fb855b69017422adb57dcbfaac5debc6f7a846282fc2298d53c4b81773dfc5e637d5ec4e142fb1c2bd7ef42a59d1b577ccd3361e0972a47a6f105e293ee1b7f66e7954a0484bdbe53d3a59b630e2e199aefc4906a6987a693aa37556de2bebaaddf0690cac84df626b91992fcd699a614932b52b648eb02afca5ad6eb34260df8e6c52bf1156484515d5442c02b96a111a636c1b6618f8884d43df6445091af3dc6ad9d883c28c1298ab874d29c6df98f19ac3246ba3f3ece1f329bc432eafd5bb333a3ae0aaa6781a9bbd439c15a6908c6a190fa30448517ec1ae116c9254587dc4a7da7641f13d69ecf9d9838902789d498310493ad72f3df9a488271bbabb596823c5921f283a3fee1dd4fa118ceddd33860378edd44e54ad378d03f2f5db51cd18efbd7ec5fefd7d6e034d5cadefafd46b06afa5e815346a56d241ec7f573033b1a88b0c8cf6b3079de21dab5dce2d12261278de59485c9f1c8c58771f12acf3cd1161f746fc930aecd32c2b6d74bfdf361e3a256b953e6277d1a99a3c9e94ec42e4e82bf5fd6adceade3c373b4c36b583204e091afc8a3cf41cb794c4665cc056fbab49a8b90688ea2dac288fb0269cf7", + "public_inputs_hex": "0x146ec7a9e91f12638d75559824d4a1794590cfcf575f0bf0e979ab180ec34ab12f2dd8535a4787ea8794523842a3a4f45cd84bc85749861fb660801b495b3270000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000099fdcec25af77d8d2c617140bea064c1000000000000000000000000000000001f225677d8e0b697b5cde006e1f2fc1902ab8c28b318084d7c7bcc0b21d6429c2e96351063372e9aea9214a7e131cdbb151bbdcd1323d901dc5c874189cd37b54f77235c9d7908e5271fa34a4468541d1ac8c71d73acf4c43545fe38a5b8104b533db6a645ad505cc362a9b682e6fe461cb526d3de1f1c4624b030066eebf9c87607d85833438e8cee28ffafc9b3a41213aa3e44b78f8696b3559e6bb4f8933bd3252caa5c4bd7aacf41fdb2a0bc24d7198c8bf5b5f400835e4b0fbe9f10b56fd6b992fb16860ceb4b3c9ddedecb7deb2e1fd67a0bb9ddeb06c05b9f15618dafcff59cd380e10f5d26e6351278d32ab41f57533905ebd79c51d4f6f7331015121cee2a2ec263d55d864e76cfca7d333f" + }, + "decryption_aggregator": { + "proof_hex": "0x000000000000000000000000000000c489e7211f8bdc5fb2a843c924ec6a58e2000000000000000000000000000000000010d820703ce174470f5d55d67650a10000000000000000000000000000004c166c8cd38ff0567fd8d0010d54ecf1ab00000000000000000000000000000000000d4d160ed44bea4708b155129fb9ea0000000000000000000000000000005eabac9c52be08c5ff66f78e7334378cdf00000000000000000000000000000000002eeaef0d86b638aae0c047c169ab9600000000000000000000000000000042e3be4ecb26af6c42b1a097e04af88af80000000000000000000000000000000000255eb39c9b949a41545725786438d11b84c1c25f84b604428ada84fbbf45bfa58bd8bab5d01d5ba751b2dbe212177718b7c4644749243d0e1da5309c180e2a7237b042fa2670c89a8fefe1f3867a0f1c11761554d7a86706c57d994387f16451d7eb4e644c73922014166a30172d6503c19e4f409c54f0985cd55803394cb0b752241ae12cc7103497dde6c35e722e18fa6691376c8c9aa5fd750658d069055c870c3c75b52c4cff6b89c939fa3822257b7180114021bfcd88cfa1e83d0e01446876d3ef152873b3819ca1484fed2601ad4cc29eac6f7f31a7317fc563b8b1acc8ffca92c99371719e10747f9f9f4c082996737807d26dfb0583930732127d3d0f4233827ae2b3f63b0ac7b20c7ea1014a2d4264b7e20544ad5324abcff50ef8ddcd35214fd36a2db59380b63e269d29fa75e77bb42311791ea54a137b4d07a5cd36f45a7133c7e1c0d06e0c1142540501b6ff6feda98e95a0c5084f8215ebd21a7d3935bf4066855bd0e8989148ee1db565bbc14abbbc9be2ab07e3b0296a3e45cf3699eabbba16a4580a8fc383cf1f5806a9749f286642fbf26fd010b0f3e3251fbee12d9fcc4377c8340e76f5b915fe26b21a0bf4ab2fe9093e8ed593e21c73f0bc3eae385fb0873b403c87220825b3107b7e7e7e1ca63a9da9c17f267bce17b0915b8b3255b433c4d41ff1fc1518ee12dfb4a0cae96fe78902029e167659c256efdf5e94955456a6b18fccd3fe02f2f0b728418d759eb78f13d7c72621fef0a5ba5aed298ca9178fd46f258afb00df49eae67b7b22535e94adc59eab453431644dcc2302c7fd366fb13a441d22287a638ed5c10bfddc9a6998b410b1a4f655eaf40093c61a9235f128c44cf1c918494a3d3d1749174d3c5763a8bcc876f5131101db4d006f28377966dfd4341405d53e4411bd146580957529218bc8802847ba1897f61e25dfaaa92a3e8ed45e1c9fddb721a4df30127720518d1d5b5097facd0dfaeb18480862bee4495570f20ea60914cf5b657cc2865c8cddd99b57e6c8563cb064c86ed19cca9a0824dfe3226d8273e1bdc5267914e4329b557747c84146aae28afe051e091517a8e4b14018b8a7522a4d36b7606ef81b3573c733490d404b1eec26ecfa859a1493f06871250790dcd79113d21b0fbf6b7e4c732795255a5d7088166489d9892e33e90989083a98a349cf2f32d4ef46eecdb7057b543f2c7984ece7d833edf20c45c4ec9711ed8b93c54d3eeb1944dc5ae67c3d2915a862ec18023bd4221fd09c12180b2404b6b3de5bb34bc6abcd637c3c1927a99da5f6aadd3fd3e7ab6bd38bf3b717f7285e6064b3f62afaae294aa978a26f62cf494665aa232528e727cf45e384b37507a94209711accc1b9885ea12761c9378c39e5ca1a65f7df51c244c3276457d32c9c6f57327c1c79b824b337b9728eed388f62d50ad95ba33275c1e732102de50540782d3e169d31d600fc70bf48bacc9f9abee13625b92f0246b84f0c8ddd962a702e42636c2d97543e9ec16c9097214807b2290bdf9ef849305e2a1c10738906e52a9eaae0c6d935b34fe3fe988d055536f831806cbf96badbe1cd22d2f19c2d1b3f56d072794d820a40292340d8d47e232b5c59cd193f58be30b44d51bfd31fd2356611268206175119304e770b1e9efabf32634cd27b063887b9d9c964ec1ce8ae11f22f70b69c4e12531cb0bb3093fa73af0a9278694e863b8127b9dadf00f42c79c725310a1e93dfc5d9980849dbc578b73b0161a5c0c6acaa8c9e0dd12e80304fb8b4eac37c3070838473ecaebc8955752dbe2ea9f9e75f2dab3cf1722cbe1eeadd3517e6f744b1be456b69d1c0565b6041114712a1ad8ca89137169529973c67a7feabcbf1faf8fc0eebf7ab4e76dd49cb657cb807588d97c4f29faf01d264fdf1c5c0f2b3e40de2382de009994772b15e74fa800410bc5097f979a601364adeb815943dcfd3cfae3adcd3c417a8c990dc548d1e610e370b0d0b6a57109013616b563c82a6ecacefb7c842d925c600da0cc9f34785d5b76a7a44fd271f7fbd1ffb7ca05108c35b2cd051bb0b56cea9c0bb813865d620bb690f46a4ec0590ea831888459c3dc885c2b56665200170f1599bc638cd025edbe9c41a8e581ebfc04ba60e16c7908d4ec4f238b44fce701cb27dbb784787bc7d7f688dd7410124e3d85ff599edcb6757f198638f10717a3de0d9638e22b192727393676b272a68b34b02ab70d79ce361db0330c5376a89ce59171b5542dc296b0e6a805ff1113801b24d03c6857f43b8244f6a8335e5429c4ed09fef8622fbeb1913b642d32232573fb195b3e646c1d2616c6c4d0951797886e8cf4eb7a969f40958b103f80913f451162a52ce38b17e5f73d9936018e7099d92cac7c1ea430c0c5f86a461228ec26ca5c09952ceb4c892ea37d9a641b710202df6d0e4837ea757a018737113fb566000886af718b5b27f886b14acd82127a21bb8b0601b83c44f251573412019b256e71040303ce2a037dc67f9d60c844ea4242970d4562cb18ec03b2b972d3c6cd5dfde131440f764d91d94ed044a35dfafd638f2a53b532c7e9bcc73310fbbcffca97004897a315d4f6f41d95a748d40d66c68d8c28d34a3804eb8c8e02bc2b066eacb4da9e4daa6d10bcd1647a980ba04ff496d939a7cfc375482dc4d05936ef003a6c940e0ac9240ca2cc3d44a3781e3d7a940b063bda7cb0045a9200827393ca060a468b8094fbe616d537e020ef73e3eea95e8a3cb8465809a982f1e4ad2611688e286d3bb134bd8315fe00f407647b35b32d56342ac4a20f6fa2e252f9809510fc454d32a9c231f5f93827e18963c1b7b6bd12cc3e503f5d9c37e185ad9e3cf1a647bf32e35f167c0b172e435f546f84ddde2f5e895e1f6b2f5df028935769d16a3023bfbecae84fa40de55c500f925495cf82a645cc7638eeba32e67050dad6fbcf424900ea400291398beb2b876360c77a3611959d310d7291f27b64cab9abb464fa9c5f88a33e26f8974df1094d963b65885553302fb3c0534253a1c8c23945e5d07a05f8029b45612c36ed67f490e3a868d033842fa1386c408fa0825f397e035fa6eed030a09d9f21476ce5170dab5baff12175f13d03c112ee6388dec77b8e920d526c442c8843b7f9ee23952fe46a3625c99cdc49ac1270bc9228f4a1dee8b2bbe2396e3672ce98969d326d1cc85d0798bb49991f806e6170e05a7dc184049baa2563913a4a2cb1b904d532163c2b827ef7dab7ccbad062c70fed0b8b1b7a4da3c6dad15e446363d8758ef29d5d8be7a591ae6c450c8421612da0929d71b05db06ccb75e5c370d7e37b2a50ffed7d39d6d7a4d7de0ac2808f1d6091de5e2de53e78e03bb41967cee914fb5cb3b845bdf9fc2e6434abc332fe7475b056dbe0aa70bb354e11905f8b1deec54c63ee6c7d58546fee55e4f540e9e9e02824be4bc133850eaaee7b4f1a3ca6071b4fdfc992341b437175020ab184186451bdc3236df87acb9ed15d8c732ac56e8ed0d56b0e0a5c350f42999de2ff69e248ef0bf96d2e59349f9ed223c457681cebca88165679dd827129dd0c924a4613852473e4e1e530b0b50cd366e56a0131946f92af0eed5e320ca38787b2a5c25a2596a217eaee77d82e5903d2adc77ee8b5bcad99c4d0a5d47f6e517e72eb16f8595f7027fda94a745af57c5a4351bd2446461158a2c499e51bd96c08f215cf17fb2d5abcad8c2c2f7bbfb959c62bff63d636694df8113f9d89b04711f049542d9974c2c08b506cc6520a4791b96486750c0d5f7431c74d5115b930a391a0032cdba415ca64e69f95cc7a5e1ed7328284e4730bced2a7920b92900c9d12bf3ad8d4fc8f7afd8b036a1e09fea7572b9fa356ff11621e6290def47dc7a14043eb15600cd5870bea2dcd1a0fe3cfd1691e7582d4de62671e5fb429808f50909bf894f5df8c90a79877c3b0cb950af23217ea509d3c5447ea7b43f6b7807471acb6c60b3cae03dc4a0130dbe4371e6694e85e661e7468306fc36575c51daf017dee0dea816d23b559cc8e0c92cd444985d968d9814fde2c3da8fbb2060a1d5147f279ae44a484877feca38c48e6f07b9f154d39dbf2296b18116bb23d314bc031a640c53ce3b5544400ab78ec1a465552e953b521696fcff5200e5500e2778242de8e3164ec0bb2951eb879782b2d24b7e3d845a8ae41a7f12097e57a03e4c2d32a898d184ba0905a7c7058ed4f91578dc475c54124bfa775493f903d665e6215249b9e1dac37093283bf75845373626e3e16c1680c2ab8f6ba63dded89a2225a8863f626589b674434aa3247ac5f2f025933e6f19fa0141122c3e095b1084286e2a23902362bdd466ebdc16fe8c9ea134f2e3401110c30eb1563e0d035add1cb74082af22cbf47d77a2c44538d6dfe3ec16027c6f1ce2b07baa536676631602d98a1c20c6511b219a801371833611b7ebafd005121697887741c0da4bb9430bab3c7bc34d01249334051af5a48f98f005af726f88f1e58512ac301f941a020687014579670eb0d5cd6bf4c6e7bbff6a6d094a108c23e7e2bbbb26cce0be1b0f87205655beb8c8460e66f3b2e00918e858837d92b321953dcfde89914448b11334b8738f192a897b0d009b0a8f194c9382b52a45e912bb421bcbb7641171d802c52909b15fa68d1113835d3634e6db10803c1fc1a4b35ec7fe03276455ff1a2b3224ef7978cf5c6b3f0feeabac0cfacf9f6b53ffce00778893a36adfb6e8b51a758ccad6573b1f4725731ffbcf436ddb21c4f9c06aa284b7d1d221fc10862f2af81d2b0db4a77d49afb1bc587da477c31bc5b1b863cae6ee1a7907e061340507e9a100a7d90444c33de4a4d333e750bc95613cae82348091fc2f44604eac8422bf2b4a5e3132acce3a15a1ed9475b3fefe28e82d9c3a37d46134919374b2800b0db514f88442e34d415f0b3c2b40f318dbccf889e5da5682259535d38e517e12c3346a94d97f858a0653bf8ec3a438d467b8257e97c3cc052e5d30260f6ffc0078b3ed66ed6d2db176f093ceec74eec88257e157177fd6402feb34fa6e654326645eb8e9a9e61081b122c67b545c76030f582367ac080e9f9279c974974e47203e4d86cfb8dc5ab98073a24a9b74a157847b4206662e84a6f2f719cbc3e8352316a39bfd9a048b0a4199d5fc9a4b85b066f7f0d32e13a349b401598cf02f461a7501f7a9fb11bb6a039d9ab11030b320a3a58cf1c0ee58855b1e2fc29c125d09374a488c0fcc4a3b3cbb1ce257be54196dd3a1dc131b0e2ed5b6d66aeb549201fbd830bf21b32631e672a18edb132aa94b46ef7c469b299642e9f0c76c93ef2def0d8e433bed6fe3df273e8e4e0740d12e6ee2c78d82a9ee94e9715064efe213714501bfa9bac44a5c8764ee4f1dc46b4098a1699d5572db1129a8907e85831db4d653d369ffef17e4aacc8beb9ebfc8daef8ad0e2743af3c8ae723a2ff4822df5f8e9ef9a0dd6ea438aadf9c101d00397a5e37ee21600a7d4119c3d0c0bd42dfdc23dd9c460980755e7a69a5aed39f35e6fb1e7bbda6c8902ace86a659dba1f721645c593759d0a00625ec534562054363aa484fddd72457ca120c62126ad1f66989e3126978f9456545d9ea5f72c8b179886098942db8ad53bba5d20bd64155c15a32802416116414c130131323fcf7eac6cfa85286b5b7b658cb0a349960f495db897dd4057b8f78b0a6d768c1e0d33bc2327be3ef75213551ed2b2dc2810cf32c234a270304036b00cee61920dae5de63ca6972df355fc2b32b3a72b462586412c7af7136b7822a7b790c70bde95fe5bf995c3bb5c20ac3fc709eea2141754c1a555456f65edb85a495cc5afc6fc5ec725a04193879eb4492c698fb39523e762b048c5767dca8aa01467c5cc736426d3f652df06b0a10bf921064977170f01a3a22190f714d94da077733d0c57c7e0af3961063e0b168a827725df6e110ccfa6dd6493d5a1a9dea979d3e53b190d851574fa5040d28d8d771180a7c51d029a495632cd5534a605d4a7cc400a65e28ccee07a70d7663650d39c3e4b63c1179ecee1d5c7d623e08e03d0c658580a43024bd720540b2f922060ca92ac7081163566cf34d6b46a7edee2e257f208b4af8565b5f5b0a5fbd696d24cdf95f54a016e66cf0ac3015ce91a624cae628378965812795c779f86e3fbdc7fe4f35ab422f9e91ecf5d9a63985830e901dd60528057022e488a16c2cefe51ce19576b542a4832a3068afc679d29a8d0f96810e6fbfdc4bff968bb0b3d6d93c8e929e12c23b0ee302c9d8091ef22505dfee3013c516833a50611d130cb4b84400441b22507bcf38ae554552fddcd5f89341604211ebd297595e819e5d240a0dd9e4ebe0a273c8bcf0f79272cf7b9577db1ec14329289f64c7920e460ea1ddbda0fd975b8147539ce4e458298d1d8c1d82b0f3ef9e2fa66f3e11dd9db15b28ab9196bd24029b29357b9c3bb5986ff42b80027bbd3122f58bca12e160307ac5e93c674f8a9269973936f193f3426fb1f0817cc658d3040956f76c0917470484b8cc73c26b70389f7275b25dddc6c44d49200697550a87b1af671fe591c135592fa9aaa330c246e4d719ac779ef1127056baf031bbd2a3b969671fc1cf4ecda953e1c9aa922141dab8c2bfb28cae0e93023d382b7236d23906df29ee2dc951e3bd7b88e9fde0674eaf1c8d96574f6ea65857f193128ffde37f9e3f73415923a7aa07d45880c2399dc51571064a8ad883e241af6a7a8b44fafa11803b9ecb2610021f928d07b24f52e4d29af6055488dba665dfa99f7caf0922015cecf5c2ba18fdf8e18438c1ff01f70f7fbcb3f3fb7f8fe60d6b7132f8e9e88a5566cfc802699408510b99615ab3043eaf02a17c43a5ae5130ff650a7acf5c49de373b08ada2aeffad507731ac701dbeaa7ba1068977be5ea8d489e3750036c13b8b3a2c9e7429c93df4dc30741d85b50844e0795b5a90836a32f5272364c8adae11694aa1e9b4dbebff68203a675db5246f37708ed841afe6f17c29542cf0baf615468fc0a56d04fe296b601f01fe1a1562293cd3fa04972473f65778036d87548d2e6048050ad8395d61d022f6c9a732ccfd0c65136f952e99e781da94d341b59e1fcab1d0e7bc0d96c4b2300d38a86de72257a817b5a4b6a623d1c9be003d93e7f422cca87bfa4e9099f0c1533145067454a250352d26c74543fcb0f4ddae24fb020762be8cbf52bbe1b132899fd0c51abdfc956b43708290528b8e8f3c4deab08926c7680426ba69918006936ea0e12b4f7fd447ef4a2385fb558642e564e5414bb82b794cbbbfad7ce14d4be294951078c4a52ba7ac4935318f0e80c10a392462dde89cf3a7907e52309e56ef27afa93d3e029949854288c855a15461b97d283e2cb7b135c7ba7f68e2efa01ef9e8b6d10c5019c09118c8aaddb738ea0a43d7457ec0f5d3cf11cd9fe16bf25b169d2f90e8bbdb730515c38ab44b101b9245137787f84ec31734ddedb07ade68447482ae6cd9e8dddf15ee218d1abf8646653eadbf3d2a42ebe0944080a7fc70c88c686571bd3784bf219b115ef10909f6ba60dddd47d42d4d80e548f234e8f04a55ec9d23e40a4225d1a6d6329b427010311377d45d64a1a3c57197d075eff765213a263131bef0b20b06b53ef05cf259ca76ed6db8e98b785b50a3e085618745a09fe434ad7accc15b8477e16ce995efba2e5c0c0486297516b888210eb5686c7c3ba4fe689732b1b25b396f84d1fb9835819aa6a074ce0264a968309cf32084986eec834df563ce08cf87fa4bd23a0f3859f7eedac961bc030f6642595f2c1b58a323b57278c850b26405a69ec1c1631bdebcf061c9b50759f08681838f8c955007c187cf43349af5a774e25b07dc0cb7fc9688bfc10dfe12edb7404ccbfae855e406046bb3bcf079a8e41b1bee610f7153a7695b89546f201408c0ee038164b1b9fe0d774bd7d3d2d7e968785bcd3774437856cee80dec3495f3e248b133c148d67b5b99fbc6b98d991088e925e4b4dd36db65dce5170e7257ff62de1ddc4706d28411dcaca497003888b4f4551b7b26369a66bd264ec39211a510663fa5b87245166da13beddb30098413fc7ed6c007b493f4debd088b330cf981e6507a7171dc311e34c18ff7965ea19da752b4fd2873d7a63d2dd1bbac714582ff9a0a7cad8eb68cfc783f8a9a2241df0704a621bb4c8e464b40fa007a8e2021d8c1c5236d6bde1d3853ca452a73955e96c36af98882c7104b5dc52d32214dc1cfbba04b851852ec3fe4e62156d3c88465d6a0130e57fb6570ea9933c808aa70875ec4cc940b933c7f4cf08f2b1f9cf17d9d4b3a28190423b2cdac45fe3d432222624ef288caf25f9d6beaac78d9f28d445668c1ebaebd75af1538d7658c41413ec299cfcbd8e094f5d18df9cdb0b451d676c6d885ef25d11f138a98aa3545b21ae3d5ff81a3485c5d789249b921748baa06174f321c1c24c0595b4b99a973721a256729c238e9973a595aa4fe5bd32da62b582c60f396c311123525af0459623fcb145ed959fc62ad09f5991a457913c6f1a894fe4fe3241fff03e98caa2dd14457172e5c1a1da9c5d5ce676cab0dbdafc1dadf33de58b70e8054c354d0a7e280a27b92768f41bc01f35a7f1abce64ad795342ca850c0044e7953c5edf43f825445f9f9db9ce26b1e1e8b08bf85896e14f37d4a59f3871df546fea9c5cba8f271dc5d93babb8ba5f53b15dd0cee778de0843c31f69c996132566e399dd6e260c5a7411134b70da0e05bae5abb892ac8a90c1192c429664772460702e6b47b51798e8a955757e6fe652e45d88444e757399881a09baf4f358804d84f498825503e7d939ccc76b17dcbce96f10bd0a95a9b920ad457363daebd4dd2bdc9c938f0869abb36ad1bd2912a70d3c102a718d83ec12b9c050c1ca019ad5e3b7ac5cda1c231b8159f981bb6489f6bb5ab6d3675c51198637d793ea41c3dea7db3fd5b324e8a8f5322edc6844543d931cf087a42c13c0e6b4e0397b586c62c6765a4d220bdf438fda258dedbcb4170607bafbadb760be92f12c59533bbdc97e19cfbd0322f72e694a4dde9464f66140e03cdac2fba947bda4a7c296afc0a71d3ed375940a46f002a59ef5a374b8a5a545834812718563f426b8c893c1d17fe3010c9ac701468b9c14299de0981b092f8f8cdfdc1230b224077e182c0575b4f66b2236e32b130337887d1354473e965bf8767a2b305d1dcaf0b13829d95dd7d8369bbc682fde07fc801529db457c8e56a8ca3f05ed6ffeecef64dc4f7c3ecbb47f36cade0a735590a6f9421595c3bfea646e613489845a0c3e074fc80a05ee5705f50ccc25c242f0a5b228c04b80f0d296d29cdf7d99a8ebf5185bd3403ef0b85bc295f90e004c3cb0fb3aa0dd6bb10f92744254699be31cd4d1136aba02ccc3fa8d97cf2b678ed29ba3dd6ae960924bd6e9a109923c4fbbc3c9d19f5cd4624986dbfeb424917e8da26ec1bf99992925350f40d6ab670e8427749c31644f85e583271dd8173911c93d18852524f8ef3988e69941dac1cc50f6da0c6ce970a9ec0589b58b1eb4240d26a4f3060e12277ec565c940ea1a6c4e3c9036fafd40d0566a3d05b407b82ced2f4d04275f32badcfecadeb88adf457bcdcb94591f1c40e02529d71c2b19efdc4f514039cb7fe2de99c6dbc70f835375990940e948e5b709b8abb54d1d06071e03901b3bc173705b801682419c58dfb5f5c30b47bb339979f969617423962d731c6b87a2b19efa7d244ca1ba440d51231b2e879329313d7b9e616b8a28a3cc17045981971b3f137921d059ee3c613db5ac12fba1abcbec92a9aead8c08724cbe9612ad17051c23f95d5994c8818b49336e95c22e1e0da22ab48ff4fc209c7cc32d9b4333b903dc4476242f8d071435d04ae37f326eae921abacc9b5525abaef1b235fd1667887115dc39fed7f3a0d98e1ec5760acd596719dac91bd90b0ee07bd58f76096ad1b70210eae23d94602e53c74968f7d0b55f8f30d8e2ca28a914fdefeab2ca2c8bb328075bca3397f2134354a3b5af730b8b32f9fca86c26fe886633c04204c2fca68a592af748cd446f503d71a86eebc93a03abf571bc180fb2ecce5412814167ce8a4fef9d516b0827aa294d81b5c2df7fa071b862ec30176298cbb095949d8d16ad237ac3f58aa5e556b64eeaeeb0af5597018f7eb617bda1e9b64af6039d1ee29af3495eeea4999fd3314c0538a52749b5b332841110ab31f61e6ee99500b1030f04b70ea15950e02386fcb12de293a5f5ad39df7f1c3276bea367b684dd3a1bef9b9ec61a40370c70ae5432d50796b84d802fee8a29958b26f5070721de47ef30b5c5e909bea1a2338a2d931fa79cb521763902bf0df3d15eccfee51a7e474e7e1ef4ebc459e344974cb5cba27350ab7aed66a38a02a18a2244568e52c29380f846af8d5137c92a6cac7af6f4dce97f255cc7cdbf0d7f7ff35d723d4124ad7cc6037b0e18fc9779c921c9144bcd9e2557d0570c7d1e72ac828c7e9ac78e88cfb3f346369b4c8f79c78c1321f37077f54f86ec1a6000000000000000000000000000000000000000000000000000000000000000000f5c4c36bb64f932f926895ff20c28fe937d3b576a97a6c472084ceff644c3c514eaea77f290fea77300c64c8d30fe603f35fc830df1c0b8e2dfeefbd2082fe21bb282c157edf4f4ff1a665e4a9ad421e3a4504e3dcb5087263254cb731f9a65106ed7823890d9bd395ff63d9f478b863c03eb42a036a7a7beacf7d8809d88220e92772d59655304af9dc73d2d4220c0ef2f56017c31db54392c702b477588e40383ca8ead3ecd40342cd06c7dd51464a99ee267e24bc3f705466b389826402203bac511bf77b50d55adcf1c001b7d4603c89a105a8e6b1b0b2b80b7ec89210a10d6d4d7dd16cefdefeccbfa6fd26e191c8763f1963b2908b8410e7c6e5cf7f4293bdb0d5ddf0114b7bf4100f8f8c60942833a9650e72e2663ff2c00139eeac71b18763caa653339e680621a911520ae3b57db18f79c02a1fad6212a74a4589c20cf66ad5a192b88c0e79e43260d675c57a95d6c68dce5c12316a32fbed5b06c2eccd3bc1fa1425f2a2321674c88846f0bd35961ee8445b6c70e18b848571f920567b150676c8d044e9853bb70a0f8b5df6e1422dd0f21b7ef45eebeaf04ae5e1d6029699ff3026e71a86f36064f48b6993a6f75f6d5ee70d61d1317ff06627304dbb0959c2e59fe41298edf88c4a6ea4852c7ffe42b1eca777514243fa1e19019c7e5aba8cea1604b2efda8dc33746922cae0a7621ff660b79520f5c65ba7a016983d2f6b1a75b750d7e3fb8243595c12a321518d7d7d90f1a3962f377ee37801ffd86eb5a6fb1ef3b66e876657bda7f4d6f1470162089d654b67927503a9f71341d120177cc3f608b3b5464e3224eef126ed1fa2455b99c04e8cbedf719ced2f4ffea00c9c5d4ef11f7aa6becfc669a1b8fa113cf6e5ff626d3c95706852fc2ef9f25e6133c9dbebe47fd452a2bea676aef9ae9061c495f10b98e930059cb912c65fdeaebfd98ff86f746b0e1502f7a27b10e28a410a999487eb791014c342157f570c111e59fb9c628358968db741176ab9091b861a73152ddb59d3c412831263b81d08d47cf75a3e891e22871ac36d5509ad52f65e28d97209e1664a598628136907f0ceeeb37273bb9c7b338e3b3cc1d97f56b8579bcc11232df38cab150bed4afff72b25dd59ebe1c52a6c4617546508b03e8417e097fb97d6cb2be0970643f4c7cb94d83cc67793f0b74741fc4459cc7ca09831345890e8d12543cba90f90ad75f6b3ef5b11a52152890a7a35bca12ccb644e5fcf210b92cb09e9264e1db70ded6310992f2577dc9da58d6af809dcf7f0d60664f22bfc78d1cd8149ed108d41240691410e07876ec6487658c0cd31262787b84a8c33ab5bcc5c034c771ec6ae2d903d87e7572a8fdd054d29ddbdb81e87e978349c41310eccc538b4062e9bad4bb065bc366bc74539b23376291e1fa052b8b6b27642e8347f7da8b5d82cbfbdfa461737eb7ae96911a343f09e9796671568f930ec150882d7716b7046076fb7b10f12ca0b859d4e12102c0efd16fbc0e49888698b0193b6f90451c71e022b96b4de4e39d95017222ff84f15c02134c483f4fe706c759a1af6778ff0ec263d0983b03c7c5697d5487682d420c45257c0be9e9143cd4991402fe408d1b9127fb70d37c5a94a75b5f5e33c8f5e3a6dd75a4e972c44f4d5111010192c8f0b02bc2be167085376b683a58ce09f7594c2df76992684d46b054a05b65b56f23118a90b63f4536d2481d20af39f0104ab03ea97513338cd8c1e22a411da41225808e54c7f8f93247a97cea983d7ceb7f58d0a0613504e31a24618dea1844482662ac20582381ff8b3c9a227f0971fa6b554092738e7b08d16a4dfa5aa6b95131a0bfc4e24ea0bb608b7af5142f58463cef3b1a610837c6d596e9aa7c17ff0af6e0318fecb102538f078bd972b85f842b84e0db313d866c6bbc84e2ae3970ffcf30d34c9d1251121f8dda89fbd684829545cf55dae6895a89b26a56feaf05c7d621d678190402a7ef24304895ebe992765cd8638069078acc0cf2ad22a490fc0f2262dabee5c762b3770085c5eec4ed7957b3d00b74314cfb3b6a4984345020bed0b61c1ba50e4e568604976d1501ee360ab58a0f9fe8a552914a382f1830acc933030557d1afe915e421336f85f607a8048f7dac10b21d1e796809db415be15b8134724bb3d8590b5848f195f0ac79f92a307ba0c47806f4b7d1127225a433a4715cf7e5c3a5d6e2c00c807d7d595f0b2b483b5f51ab0b911a6998b1fe13f0889019a61ad94f9505fb4c30e472266024eb84247d641451e03f10a2f1ff023640d0ae967422e15e7082cfbad5dd60559aab5959e3d172e86dbd840541d775fd916229070b3fe8d3f0ae9782d5f821b96226bbbc88b3c77752d30aa71e7af32df8b285ce99620953d13203ba81365f754381f336d9e0195161d2348abba946abfaf06e3f6a8f421e3f403538fd9ffd680fcbf3d99a7150e834cbca329deb50b9f6629904d7ab338d687708ca3e4523cf57d26ef59958795607173c4fb20c412f73f2454d0b677a1bd99678f12e090fbb39c4b218eeeb0f6afb43cf83121b06006242f8eaaab89e20d75ba8bc670b7d34696bd9e88ceb400d62cdd1cf34f98f5e4300d386b358e79f2a9907b73e520ac7a6b316fcd18724ad56a7b5fe44d44801b1e1c30768d30ba4afb074b2fdac87c55d58f3f1f975583dd8b6b6ea2155c83265f0fc2bf8ac5f24c80c672f5ee7e0a63ba5695ab4bb0065db3ae0aab378db8708c1d8b15d903db2b44d8d935afcfda51ade3655a4fd9172e1f51d459e1c84ec06e125bdcffaed1c1c5c907b9d3d8829bc730dbf27527a704f9bfd105092177b3cf2f975d08768a8f3fbaa011d938506c2c1aaf87d6d33e95bebb436bb834e1e213189528d9f16e0fa7427e68358c3948104bbe3f3082c71a35b4dbc1d6fe560fae0d7b672c650eafdd0c9014f0802380fb2ebfd698255a6a77662416baf612ec4b071833b93321bb86ac6510c5152ed4f30435dd4afbe8e40ba803446a02a831902af567b47e9728033cc178394c9a7b97f5afd52d1050e15e48f5c783f08ffa641a9c1c9ad5e5102448fd2a8a812267636fd8c6a1baa8df617f99bbffaa5a595428488b6b1175211d51dd278163b314b62ac8bacbed8420916da2d1379bddcc7d2a019d4cebe3b1c95585eba0f97df308d1304bfd820ece00b51bb071f444fc0f22f3d29a87c8082d7fda9598b5df0a9b3beb365114b5e08907c4c9676fd97304186b8176d6d334dc3d31bdea0de63f339da6120678c00dd7959ddf69138ef8b81bfc26d0a761c92d5d729f3303f00388d8a89ee5b55222cf195f2fc078cfecc711f5ec3e2b5704ee0ac3122c27e9e15e1a6878b3217e2da430d99bae58e3668025ce5f2579561989176027a1ca3edb86bd0796acb3822c2a6fe463ee4f28937d2e0d59dff4c8271eb1dc55c14309e21318f8cc7c892c1ebdb74e73d841af18ad26b84b2a6914e811a1e640c7d060da8d0730b33606189ce7f5b13a74e3be7dce142c5d051392857e444e0a9bb4c06f7b09b298d04426492fdb21f58173f49c7b2edff1d6d6ac942a17681d2e91f550fa7f7afbb968f2d7582a23e937f2a4adef0321a87f2758e832272de354dfd8da3de57e05ed621674c1b17abab1618323981aef9bcf3cb318164c155369d2606bd5ff6c45169f0c353e3114fa0a43b8f10d0f0c88f109afefab92176fc894cbcaf0f34b660e0271528af37a3b8688508c140ca3ab549c8ad5a9bedb5b9fc9fab21d482f9280248a3ff62d1bc6800ce486aa179e074b7b4ffc251a86389780a80008076b4ce70493c83930cbfdf61fd82b05146eae00b2ea7499a1ec6f07761d9969c069b1840af8f823b3e67c51898506930ccff971617ed5465d7205b1ffbee279bafd520219c295bb8cf3f8aa489bd5f3222f9efb0bfee5be32e817a5d99a67bdcd05ed61c9477cd917fda25cd5da7fbc104260ab6d616a99359e4554ef12a08a534eda7ebe4fb270dee86e7b46e6ac2d1e7c83657d59fe9c897efe559e87183abc1c2ea46fa7bd6772ae894b065738382db3de4aa466fc8e691ddbdd94c3aec70c982f82b069d3afee7468175de9fe9c", + "public_inputs_hex": "0x0735d58c9896306041928fcb73cd2faeef2a225c22cdcac38d802de73fae0eff1739d924e1b79deab6c8ab6f90b56b0676891aedb26fd3fddd21e54c0ac32fc70000000000000000000000000000000099fdcec25af77d8d2c617140bea064c1000000000000000000000000000000001f225677d8e0b697b5cde006e1f2fc19000000000000000000000000000000002857809356ba32d83ce9536d0567344600000000000000000000000000000000f88dc81f55767fb41a3d04cd718ff7a027b5bcf3db2a429215164e576101cb3bc26e31fb2e149012d6c82552501653b805a2fab20a277a785453b5c51e89cf459b0b43fd7b33c55eafaaf91bb7dcd1db000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000021cb526d3de1f1c4624b030066eebf9c87607d85833438e8cee28ffafc9b3a41213aa3e44b78f8696b3559e6bb4f8933bd3252caa5c4bd7aacf41fdb2a0bc24d7198c8bf5b5f400835e4b0fbe9f10b56fd6b992fb16860ceb4b3c9ddedecb7deb2e1fd67a0bb9ddeb06c05b9f15618dafcff59cd380e10f5d26e6351278d32ab40000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } } }, - "integration_summary": {"integration_test":"test_trbfv_actor","benchmark_config":{"mode":"insecure","bfv_preset_subdir":"insecure-512","bfv_preset":"InsecureThreshold512","lambda":2,"proof_aggregation_enabled":true,"multithread_concurrent_jobs":13,"committee_h":2,"committee_n":3,"committee_t":1,"nodes_spawned":7,"network_model":"in_process_bus","testmode_harness":true},"proof_aggregation_enabled":true,"dkg_fold_attestation_verifier":"0x7969c5eD335650692Bc04293B07F5BF2e7A673C0","multithread":{"rayon_threads":13,"max_simultaneous_rayon_tasks":13,"cores_available":14},"operation_timings":[{"name":"CalculateDecryptionKey","avg_seconds":0.006680708,"runs":3,"total_seconds":0.020042126},{"name":"CalculateDecryptionShare","avg_seconds":0.030206389,"runs":3,"total_seconds":0.090619167},{"name":"CalculateThresholdDecryption","avg_seconds":0.029794250,"runs":1,"total_seconds":0.029794250},{"name":"GenEsiSss","avg_seconds":0.009910861,"runs":3,"total_seconds":0.029732584},{"name":"GenPkShareAndSkSss","avg_seconds":0.014833430,"runs":3,"total_seconds":0.044500291},{"name":"NodeDkgFold/c2ab_fold","avg_seconds":31.622140902,"runs":3,"total_seconds":94.866422707},{"name":"NodeDkgFold/c3a_fold","avg_seconds":118.019850166,"runs":3,"total_seconds":354.059550500},{"name":"NodeDkgFold/c3ab_fold","avg_seconds":12.065775360,"runs":3,"total_seconds":36.197326082},{"name":"NodeDkgFold/c3b_fold","avg_seconds":118.281532805,"runs":3,"total_seconds":354.844598417},{"name":"NodeDkgFold/c4ab_fold","avg_seconds":12.187433402,"runs":3,"total_seconds":36.562300208},{"name":"NodeDkgFold/node_fold","avg_seconds":29.707364944,"runs":3,"total_seconds":89.122094833},{"name":"ZkDecryptedSharesAggregation","avg_seconds":1.942771458,"runs":1,"total_seconds":1.942771458},{"name":"ZkDecryptionAggregation","avg_seconds":52.543871292,"runs":1,"total_seconds":52.543871292},{"name":"ZkDkgAggregation","avg_seconds":5.455903875,"runs":1,"total_seconds":5.455903875},{"name":"ZkDkgShareDecryption","avg_seconds":1.183313840,"runs":6,"total_seconds":7.099883041},{"name":"ZkNodeDkgFold","avg_seconds":172.312935347,"runs":3,"total_seconds":516.938806042},{"name":"ZkNodesFoldStep","avg_seconds":6.133198646,"runs":2,"total_seconds":12.266397292},{"name":"ZkPkAggregation","avg_seconds":0.517702708,"runs":1,"total_seconds":0.517702708},{"name":"ZkPkBfv","avg_seconds":0.246842264,"runs":3,"total_seconds":0.740526792},{"name":"ZkPkGeneration","avg_seconds":3.112043444,"runs":3,"total_seconds":9.336130334},{"name":"ZkShareComputation","avg_seconds":2.979204014,"runs":6,"total_seconds":17.875224085},{"name":"ZkShareEncryption","avg_seconds":5.726563981,"runs":24,"total_seconds":137.437535545},{"name":"ZkThresholdShareDecryption","avg_seconds":4.143115527,"runs":3,"total_seconds":12.429346583},{"name":"ZkVerifyShareDecryptionProofs","avg_seconds":0.070387291,"runs":3,"total_seconds":0.211161875},{"name":"ZkVerifyShareProofs","avg_seconds":0.140556358,"runs":5,"total_seconds":0.702781792}],"operation_timings_total_seconds":1741.365023879,"operation_timings_metric":"tracked_job_wall","phase_timings":[{"label":"Starting trbfv actor test","seconds":0E-9,"metric":"wall_clock"},{"label":"Setup completed","seconds":0.865109333,"metric":"wall_clock"},{"label":"Committee Setup Completed","seconds":7.026051208,"metric":"wall_clock"},{"label":"Committee Finalization Complete","seconds":0.002979125,"metric":"wall_clock"},{"label":"Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)","seconds":189.944065000,"metric":"wall_clock"},{"label":"ThresholdShares -> PublicKeyAggregated","seconds":206.572293083,"metric":"wall_clock"},{"label":"E3Request -> PublicKeyAggregated","seconds":207.078279167,"metric":"wall_clock"},{"label":"Application CT Gen","seconds":0.013127167,"metric":"wall_clock"},{"label":"Running FHE Application","seconds":0.000079333,"metric":"wall_clock"},{"label":"Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)","seconds":54.503758000,"metric":"wall_clock"},{"label":"Ciphertext published -> PlaintextAggregated","seconds":58.932710208,"metric":"wall_clock"},{"label":"Entire Test","seconds":273.916770542,"metric":"wall_clock"}],"folded_artifacts":{"dkg_aggregator":{"proof_hex":"0x000000000000000000000000000000c759cc9ae941af5038c8b97755896fb19600000000000000000000000000000000001c1a3722f951573b5a0295ec634a57000000000000000000000000000000ebebc77ee3eac819e175a341f2f1e7423a00000000000000000000000000000000001761a6553581bea2c3e412c770a83d000000000000000000000000000000bd4f363a701553cbf608568d2de3dd720000000000000000000000000000000000002caccef9014c2923ae4fc77262a79f000000000000000000000000000000322e862d9bba2f86d07a7b5175b02caa75000000000000000000000000000000000019ff9c34c1b63085570e6ccde257e9175c8a0a853c581e7df5985602b20f91e1c6cd45d5923748347fc716cf3a00e604e10e905ac9a9456efd0986dd72c6545353023cd2b9497be947f2f35c62e150000cbae4474670e3b176c4018bc342ce8e951e9ad6a5504ec706d5fd87446b2716e2a9c3ed627155bd5d76ab35901f2e5255bb5d8f85910200e2e91820ad22eb2db7f9f23a6d9a10de6861724a75b6fae6bfe2d01e1c2a18cf616a7747715a461765bd8985b12e9e52e890f2d5d541169fbdc61a29bece944b025719edd3fd6d163f35650c51e3e1eb0c279d971f4e8e6409671826352f4d34d24fe6a10610661c432195a7fcecb8e0b438b288f6302ffee477b505158b87040797ce4f4b4e7005977c4b9ad65a2b48b653643c9a7b4296e4a4c0e5c9db3351305fbcbd6cdbff2e48d28f62f21ffc9917691eb254eaf22c875e58b46daf87978f5d8eccab9cf80830b48af10f7d848945a9d5830c092cba925072a589c9bdcb68f164bfaa5ca60aeb1fac877e0070a79299567b9cdda6dcebe60dc44d1be617ee868ec9fa7e14115420a86c9f26f2d20f48850475cc4b97196277935199d6df233acc66a77b2d239d9837ce927fd18ba57f7c4764be438128dc93ef79b681f3a1290e6734d1a20829bfb6346497e7fd3e0ef0a31ec60db140deb1cbdba1167a2126df1730722d01dc514c81ae1cd51e95fc18ed7ecf9c56a565c7c87b1bb7ea978d45395f4b862f97c5007c5742636595b0e3a787675b9a0faf474a8a83d1c94da33206e3073d188fd1a1a636819328f52a05c0b59d357646d25815174ebf0ae7d012b2ab7640250f5061dc7793feeea12fe461e6366a978b6e79806150e773eeef78dafe10a614b131aca8f036b7f6deb336a51da9af0436a6f95caa0688cab256933131fa7409e73ac456bd47b679257a6ff01087d995410076713b594801cdbfb11a61d907149a4e3370d308872156fd3046ad167d9bc72d05887d0bc5983ba833e1a2485e1ee698e9512018aaa24023891b4f23fc39074acfe47ff31267a8659cae1081902e6d3d532eedaef05f9b61d520223781457c0032d4b0a5bdca621c49c7e3d98911a0cc9690f93771212cd15b6f012b016f3b5a12634447e24f47f415f26048310570d3926f9dd89c607e3105efe9954ee41c2fde40b7447dd58e251ef36da67d2a621e97f3f44c82bf05b1296092cebe5c4566abf559e632f603846a8c6d2bee2cfd52a8470cd5c290a66b4d22b9c30a616c04ada4389614d71ec06df3e39a0905629a96e6a91a59553d96aa8caf2418d8625162c7bea5c6228b01203459d57a19c1101fe9181bcb534cf5d625662f624d2743b10e50a3bfca925f9e1aaf0d7e2daf54e6c41a278ab88401e8b645f173bb15562239be1de98ebed36824681e6d2fda859d6329bf6dbe2a5fc574529007834f3e0ab6852c675fc92a320a3e91a01e715d1f01ff9d9621f5e9083a9946432110ae7ae1ade41e667bdb12ca4ff3340f1d13b06d7b56c799b1b14a8b02d86ab75eaaac7a4c0ecc72e360eabfcb74231b2bdf968ce8c9b1bc2d3e1cd01cfd75b3be2c547d63493439dc68a9dcd26a9611c698f25299df7be0383ce9938461bfc55b77985802383b80189e4306307bcc2531e4c0f06385c71997814c0bf06958c3bbfeb70ac933e786adb5c073eb881114746fbaf5f6c58d77f488f4af5b89a0d36d612d3a7e0ef0b9d4ec9a068fc4df08ffb7b65068ec9cbb9db4fa4a991e426c6cf7a78d85b139d74df4ff231878782745d5ec0d810ba7c5aff7261cba33d6e56b1df25fe9dbc9a77c8328b215e5dc067878a9aaa6954a8fed0e61da7592d5d2ab592208ed9d558dd486fa507b08f5056ea32aa0780507691b2c4c8d3d6ebe064b1fe33568ed71d2e5535b858b02b42fff93bbafb67aed7476217d93ab37f2ac4af525a772b52d77750be4625c440a0699b553facb40f8d8655b1bdb4e699e3c78a757af45faa9cdd3958859997b3b226a26176de19212ab88a28779118202e4b92a0d41ef5382087639c78181ddbc08e152b6eb762728082f19ea45fbfbf03d95282ffc166d46fd587917ee2f3f98256127e38ce5dde8ed2dc5ac3399ff1a6fe06e37e2eb22af166a7b43f90ffa670d2f40c1923d03d1f754e58f9c8dddee4a4c6f9a95f74115b688cdd31e17d21924da32c7ed8a2f1001163ab1181e6d2e74668aa6b64245ebdf8f4d11dcb01ec801ea0eb5b81f3067dd4df939f909924174e37291563c3d378b2cfb156b6e61d009532be9a0ba2d2f6695fccc0cebb9bfece2b8e64c42bb76f9adb2404761970f2ca979abc7bd5cc419ceea720cdc6e23e99003a4fa07038cb1e90cc0c49c8f8611d03e68f76273bdbba4d2464c92047f90c7be5b84847d421375c8a2ea8eaee013a7a6d6466ee5761f3bd67a9cde275a9fbd9c5b50d1edf9d4005f0f5cff488203cb0597e2ca59a86bc5f49d09a865eb8199561658a8008c64fc3524779f7f730ad302ef30349fcc9b62adcf392ead44757563ce2ed43bb92d1aebb0a9e03c8a07c8476d229f9b266e785c05d53e9b73254fe9b86f91e0ec53e9c7d58c10dc961437d6660646e18f3c443a58fb1739ead7832aa5716e1cc55567758260b230622bb96af1734a6ac2507fd32a660f097b251f42f949dba2f2642ff20d82c781831d20eccf81f8d3ac8bfcc2b9670c25bbe2fae7910c4999f92775fbee38ba1f062fba2f46eab74cf60c2c8ebf9a80aed866cca5e8818d0ac75b4e919cdbab8f7524d82783405a845a029584d51f25c71ece080011afda4f014c0e34d9cf7aae6f282c1fb743d6211caa8eb0e5364cb3ee879af9bd59a0914d6db401f38cd3e49a305fbb6de0e1d26881bc656993ee06067cc85811dbe2d7304719cc90c0792d9f273f310a1c6ec30aa4786c1616372f469adafa8cb642ece2078ee7542ac7483b1155cd3f69d2b080c84baa0be3c7f716996fe18f096aaa7677ab78a850807cd8057522bbca9290b0da94684336ccce567727780a5b3e44e645e6146bc6936ea80016339538da2188baaf589b9f04e497309af6e95b393d44ec52e6f4b850351e0d6a0e5ea4a4d0dc742704872c165ed96dc9756e146cf9af3d51e8b1122f2bdc289beae7844d461916c9639f79cc5ce03628f379b7b47882d89abbfc3a2b75710659278fe184e844f01e6b8ec9999cbcc0c2112212aba0543b0bd1a84e6ef85e28a2ed95cf561b96140ac12c1a04f4c6bcfb7ca8ddbe9b3602f6b14d4fc4cd3806fb768f2fa3045c59b79dbc66a70a59084ee7fcf905793e3baf8473cb6c82f224db7982176efede43f0b508954ad6f5186023c5c1cbd04689bd671a03c576ba179394606793005d3ab5bd2cd7ffbcf5189f555bc75d017e2ce579e1d5158ad11b5236e4a64929561277c9364a875f1188549e4454b681dd5c36bf6e651ab1e606957ded828d14f9ebc366e4a83a7bc1cf2a5d9da18cadd51ca9fd41e4a963bb13303a3b95149db5d339e833014077231525072b27e7a9f99f7fba9d74893817186a46dfc9382c3dee6d4b96765692bc8aadda2499b90881fe8c0f8db1ce745c0806ba962e92c265175151afe26f82c6fa1079430b7333947f8db354b606d41108f97398b93cdd0d5440c2967721427ab5f769b492f824eae2cb86642df2e0c02b210995564219432087deecc469eabcad8d53b68c72913b97195bf0f8a0fa4b299a1437085d87a988eb22142265f86dcfef64917e2bd6d4ed2bca0b28f4e15814ba6ee18934157ce4a2d021367afcd9bcab394067bb5fd5e1505e2c81245baa1f633a656cbc45e5d189d1a46b09c1e4e11807b5ceca6555f996602d27aecff519561fb9a830552b56712384d54be8d727a63b3507d5cca73488abd5da7871932533439655dbaa9d4c5334c5024b77b0ee25f547bb982a195342629b9c82e5fc1c9e023e0fd789102a92b9a71d09dd99161ac059fd2e3e1879f85b2ac341188d1e2a9c8f6dd0d2f6e556b5ce1e293a4e9a1621cd58403eb8b511aef70d96b7d808d0bd937de5e2b5e315a554e0b9dff8383d4efd199c655a3cf071a8dd0f37ce1cae0232e2e580ec70fe042ed0bf9e83a61da452364f65d9e79a51e75067c19312c26d49bcdd756df7f69642776dc082454e35948dca833162399174d8b104f6000016c3fa686b3a66c62cd7359a57181893bae64b79c083e773667b2cc1130910090e1f596042bf38eb0281b6c1552b8f51f84ead05efc56782e43c1d9d87500a95d8b848bc389fe7c6d87c339b77add806c26f14d22638748a61bb9a9eeb022ef9cf3bea64718fec066c36221779762876c02afe32558a9fdfe7cba4c739381baebbf2588f844eba3930a48f304d44db1618e84e10b8b49f6beee4e7820cd72f42d252c0cb5a30a84f184244f27882da77bdd8faa2ea30a10e18b7e1dd3e0c230d26015a91cd6715e25db1c96cb052cab75f7cc63f127d70ada6d985b56dc1047704c29ca22cfad3459f4626cc2bc6f18b80c02b4af9b662a13d223ebe80082fb19654c5b8769e49b29fade59af2f107dd82ebed8136fb83203c166a09fa6024c6dc33c48be2d4284bca08cae0fcb17b2fdb73087940d7dea14c6269f22a1914282cec8699544204a9f530c1cb83d7f7f57149f15c5ced3a5bc319d7a5fa3612c039eed45839e89fe8087578f61b6e57d680b69bb0529d67a198271911efc01d0e72b28451c757ed721fe770b45baf36aa5178df3863a7f3dd873857d681ec16322cac84631562e2bc0da187fe1c8297a833792c67af86630d0bd06e355d5b09c22b433e4fbdcd6374c73d34e9f766e8a6303794f64565076984ab6d7abef52c7361523d4bfb2280de609faac1783471a10256c47beab2553cb3c4c33898e627725d2674f4cb49877277e36cddd5165bb8003be4078a3a9f7517fc4901b01e2ea77fbe636915f60917c49c6a1e2b9a621ad8fd516825120439202e131f329f16d349fe78c6299782f6693490807be8f39e48ef17b3d68ee5e21adf4f61a17e132871af83e4b29ff2e5c8e3b59bbba2e7d8c86485c9f20073922548e0021dde0e89f31b4a1dde97ade10df8c4bd71fb200f87bdc1c39de5434e766627202efb2f804bbd9e5bc106a04249185970eae01cd72fb10c4601ff1925d9a2bf8b51b214128506e3747027750f97959324f700a69b36ed5dbd95daa5f223f093b58a721502839c69d8a7f95382e43a0feacc70a594ca65b7f7544590816fb7771c2d0f05e559f8d94fbd7fb7a555f7dc8264747d3526b739fcb4a993193504ef34774f22f5cfec7bf5378a4fea1b51f2862f34caf8545b68234e0b7e37d8c481760e13151b9d85496ad479812b968676fdd2100b534875aa88d2fb33df3c4c58744b0223621e3a669f667514ce179a06659f0a6e0940784655b16fb3f42036106e758f16abc4d29b9cb767e95ad8c9485e3a7e7be09c595617f4415258b2d2c585401703d73f4f24541fd6c452b3975e6b4bc8e3400a6c66acb09647e804aa7853196e1b7d3dbc88bc7adc2f59ecedafee3dc6b923b87ba173fe27cd086b95f4bcc5601ca9a786ddb4e25dfe9648f322d18308a393c082149427ddc4d4329531222c8129cb21139f18c946ef5b677399e2602472069f63c81fd2141dc472b2e6975832157bf2d61291028efa3e2a21ba8491620202054afe148aa88c0d95cbc12c2e3009342051fefc24bb90c64512a321e1de49e70d3ddee5f2d911d4ecc55ca287640077539459a07b6f7620153f564a80945000382df390ec2ef00f4a7bdbabf11818290ccf8e0448a34ce084a98786c9c797006827f273c70b828a45a08c4e436c01b61bdb3ae03e23c7fac42a9e37359c9b07c3d05a5b6674025edbc3cb7aec07043743952eb9d16dd927cb89e1e8e8c0250cf0304e06f85294feb53bb38ab7c52de8e24812322a30f10ae7b2018deca3abb410082bcf158916a0bc3e2bf7783d2c60f46c6f6b4d58612ba6a4234b33b4d8edeec6b470263f9782828ccbdd343f011deb47f4e681f1ff127efd11a1d0988abf496ed35a805dc43dd59dee4d97dc2a1033a75e8b3c59d27b760b3dcca7d5b6727b6c8f69b6a3f0e62bf04b4ff63c2adac9eb4ef1871f2f185f4cf43c5692fb33df91f2cc72c5344bc82b1a1951c502f9120c1901c57e2e82560146a761c70974ae50e12722a7f61c3f2bb25ced6b2b61dd8d279aec6121a6baa0815c3b99d15e34b56c839a095bb442b4797291b71c047251c00e59845ba2da715d271fc947160931eef48a245717cadb767a52cc281eec7635d27b9852fba5ab66b662b0951f552c96ffba05c8b5dea1696eadf7187b3ed16187fcbf0771f0763f087e893fcc4409b52487bce7c3c1cd43acc85528191d6894976a26d5f22e35f51f1f700d47089cb31d549bd38c6f3d2e17490108882096e0fb41948a8ee44ad8e53a98d93755cc1af4a71fb5634c6ca68f9b93038fb9cbfe722bdff8ec9c1dcd5af0692c5d55de5e8554a374b6715e2763dfa80e6f6ce95cf667c273d14cc782ef60840d4228de706dab6f02e17fdb33adf8c022a533e3bd128e495c7d94470588e198a2cfadb8137d5c0833d5e2206902c2fa0e17e2d9916495baddf57bd56911aa2b8d2e34cfb00d339151cfd7cca2c8e2360ea069ca354da044e99704aa7966e51918995f709f14ba06aaa55369e41a529801a1e969f34165ef5fcc6afa7be3e81c6127b14eab343bfb10396334cd7bd8ff2f263c869292a68d3b3f536bcde3fad616f373354bb71e9c3be094145fcc92ae2129d1d86a6ada118af3b2cfef4018050a195d3b6023e8dee36ec41cd3bd51cc19a5e1064032b019ef1842884b1f03a2cb32980834acd3e071b1683ad634bb820d221b114896761bc5543531d7b7bbdf1f49811f3713401f8633956f87305be2176d2b32d2d66e90f91d00eabe4241848633ac8e5976812a0629e356727f4fde22ab61960e61dd1fea2113ef9e69fd89e9aff6e788dace00d7acadd32a505e0e161ae6a21d443cd891009eeb872665d78f5c767078494a1b74f4110dd9c2a43e11e1518775728ef06699ed7cce5acd7da177c7b7c763e76177ed2c2535f066310d3754f86e7015fb57e07fde37353ae768e174793e8303b33d1006b4694cd26c170af15ad6d3debc2942865bd564c77fa64b394fcb74788a4cd20f99cbbfc68e29f3a0505f6ae69e49380c3c4d0bc0378550c9779e6790a438a79fb64c528e230b7bb13fdc511c3db733aedf97ec3ff96012108eca4d2ac47d7ca5e223c6439729148b956a062163e14583abc517e7122547cc1cd48475cbff014acc189e75c022e68dc348062b95f0c3a965aeebac0134f9654f6e1eabbd30b884e37e7eaf1500a93d125272bd9cbd0e28d8886d5f54c57feec8aa5bc45914d6c0bcafcc56e72402baf2b317446ff133dfad6fe40b43a154050e1554bd2c965a661c829017e60f80eed3ad2e084e20972f00a21f426ea554403a47267be9a09240017337b63e27d248d17d3aae35c36d91b71c9b76335b9d95ecf43418b33b9446e85981b7cc03c4672ad0d46cabe5606b021d75d4e2f10e0630c15d085172baf53c8260c147015c4e0fd1f0394e38c9760ce9c518277d1421096b5420d0d9f482ba639a5b0627117d187324006b59e4bae4a7f20b29cf661013c9458b5f1040dd1dbdd45fd72775dfbc9f2555a5e5b1d2b32faceafef1c6d3928dde9ef612069719d64c3c1311744cc013d0e906ba643e80ef12771c1ad1c9fea56147eaa5bf9a18548abb2b0bde62af5e97e9bb617a553acc02444ce0225df09199be1b9e606ccf41bc7f9c0876910e3bab81c0c9c06be401be5513b780e687216d4f69ddb943f75e1e5d1f26c97b4f70a4d7ee11b64fda33c1180c146225af5a165793cfe07bfed94e4d4325353fcc7b86a240b762b860fa359e4cf09cd4629fd6002223bb24d6feb869ed0259f5e538180c6cb2de659d28bc4f68f938c4ab06577f54e22e3c0785af3c8611b6702bd458c4c18c55aac0bcbf1d0ce7809afef216c4619fe8c943f270c7650427a5b9b9afb212fae766852904faa21577865a48f1403bfbb8ea1aac50ca6518dc739a15a0b4a56ad59d88b5dfe1f575123784bd076fba1729f3ac2f7e1fda0137387fb527bbb588f4a348429e98a9d4ff83fd26a6d497f15cf68f3c04866b26740d7c3cee92417ff39c60d93765ca5fa6c3253e647cca67b10d48fd98acd309e360e95dd91c1b289c4ec43f4846196ed36b18f5a6ddfce46b358cb6d3d15b0aa10590cad06daafe5738ac4ef1f19dec98c3c776a6b5dc2feebf49134e800427273e4272ea3f609f3db92446df60fde2c3b97c6182b8ce3fc70f3f1c5ea1c913944a724bf5c1eea1758c60b4d8e65972d80859d897e2717f0b408dbd01ba7115b449c6c18cc2d1dc71887249c52a5a61889fcd96e1d00a92a6f33ceadeae141072c84df6c67fa4bd35530aff5e5054f1cfba3d529563c2e5af0007af7261680c74305830a823c25ab8eda632f5cd905f0e39de50da8a97b465538f679ce0d312bf96b846402482cd2aab0207eab96de39c815ae7ef43b34368c5a9e53e20f62b2d42b6b44f39a5b8297739f63601ad8cbe8320b25096b5d971bddcbe8a3a4522c5269a5499984d9a7643633d91c466bc1c0d08476da96e46cf64d20cc3c755046d980cdc172c4e20ddad7f34fb278cd2f20e11f8de085f5b082aaa464f0d79240cc939b42728884f938f5a00767e119d4ec9cfb2d72db9f408784be32dd14609325c1260f70eacf3168c470416bd1add0b95b46408127114ef8a379529c43704831cb1f563e7dfc5021f9f6ab2a392b692b22bd5efa6a250efa327b6fe54a21f53426f41098c61d9a312aeef2d50322a67e0705c975803f2d1e19e1f227d63054c0b7d389615b62be4b2143df9e227b3b992004450572c4061dbbac17916ec023cfd919c6637c192791c43cfa2bd117c4422529ca40c846bb6b721eb2e97d9266a6c835bb266ed12f5ff261d42e7b69be6e09a04ca373b59412b9b299821d90668f8b7844f1c832fb796ccec3a22f7c021b9d91300a8a0545bfe3336ce3b9d12f434ae016820db0efa0606dbde578371d9547bac26871da965df77546585ae18cfd9e924c8760087da3b169280d22fc4fb0d5a20164cbd66fe690128c996b411360f7a8f72d6516c73e2a5c1c9716d601c19995fd150f1fe8371248cecd391302698f7b58cc6804ac7b44f89fa4b9fc5c9b24e1a121767031cd8ccb748db4c0e697d7d7392798311b37b50866a3b7d3d3101ef0cdca74e5dd2176e2255c196213fef8aa4547e3688c703a9323af3597392f466c7da6c2807be00201e3b09fa1ffac45e4f03b2c9e734ac5885ecb4fa16a67378a353389c4a39418de787133a22024ff842cbc7653c15ab0b46971c4b5e1387073bda10dde0370890885b616c106f45f485fbae119c4ac08d02333f2c6a8b594322f5f03647b001a17b224d5f0c64f295539bac409493a3d1c82b3f14c5218ef6416e155ad518992b5683440d2817185a6ed36f36ecf47968ae5b6d72a38848794181dfc6d9b5ec3b49863c321c9ccac54f01cc4a9c2b3907c2618469d3e6fe8b407eabe57cbdf9eaaffdebce16d1b551439f1ad8561eff7eeb2d5bc4d08a914406a6279c3c72717c71dea06d1193c6fb73b1f962685288bdeddc04638596de80a18198f44c520e83ccfa2a8507b870a82ac4bb7273990895fe1d9ddb84c7059c106d7b183d17201a79443f9b1a933486880cf1535e2d738a63d203defe2120e93ba685b946ca132101ffbd061cbe1e151bafe989035b7424abefc57608ee22c29301802344149751e7af0cf10df41f7ef566fac5aa02c5692c2a53ed72f8423cccdebff3f24ab6dd1cd6c7012e612725c3dc34c8b15110392de34db8c0c0c07a22eb983746f2a823eea3e05322f340b866bd3052f3d426ef3d9213920714bac789fcc379c7daedfe5382011a25d31ee05471387717daaf277ed08d0c3a058bd8eb5b94e944842c05ad596a8d03189f1e59e39e8cf0e81f321ee83a511ec2813596fcd0408fa932b36331ef650b95dc32f67b1c352c13dbbf6eed9dc7d14688d36f040b9e88f294d30ea0e0520167c66d018a95285e9cdcb84fb779c7ea985d16edc4521dd73b9ce27fd5680c0f67ce8a93ff555326ce409c4aa4b865574a48895071f506566c85ebb87d3eb62c276feba915c5a659ab516c4580a8a55f90139b5750127bb969ce28799b173d29aa70c6ae4774974ae60bd2da6d3017b0a2e0e4f130c7b8f68d197b1b24cd0c13678f30fed1f49b47ffe2054721cf2f8339006eb4febfc1eaff7cf1c7b1853e2ad3e911f4060b2e074a4b6753bc5c7a1f19554ee06600be6e9e91818cad711d21309d1176b7f2c97f007a08aefa8e7b4d3250e8ac0960900246ceacf99942990d56c59c721b2309bdf354272aa6555ec8707a655935fca59c2635bcebda2af22689d41b7905bd64fac9ca8fca4093888dfce5e9ee121e0288c63d13dd489a342b2ae1d5339d29a32aae0fdb3e565b91aa6f4db2379f0f50c3ab03d39d1ec3d00000000000000000000000000000000000000000000000000000000000000000261282c276ddd3ba00f19704d16881bfd2c6cc2d2e2491fb1b3b0f5c706f36fc15f9f7edae044bb3b268c8978fea31c350b9fc5e7f44644345b9c2a095b756b30debade73053ee9f7d4c5c4b9e90cbce76c25236c7fbdaf92f77d1bf852a2405024249cf915a06031e094c8598814ee69bd5ad3d601427ca887bf16745c1a4000afcc74b0c72ec4d6ee0e7f398b98a1f3538e83b373657dfa08a69fa3727ef841064c74325ff3da5bca1b1f7199e741bad357df2729eab95be1b0762d33594ce0ea50106b3a5ff8fbc411d1ecd33b30bf21910cfeadaff986629c7b844de3dea2aaed28886ce5d4b3a1db9465ca4740ba8e60633b5e2064d044d0981283d99fa13a0c64e3cf459b13066e3848eaa6bf30087f12f11755a22bbb98c5fbc8ede4c06813611edb24aff3688c03e41550557725fe6c922f74fbe4dc98d72126205e71d3e9182c955c7554db86552ea52c6b6c5c5e0f4108a14b28d90f029ece478c81f73a3aff25fb738c2831d8f689c1b2a68b58618a82621630d066afd7c8fbf5122e7086ff042034f042df58a835692874b31c0c408f3e17d71dee33f9ff85d0f277b4333773e83c2ff68ba3a58194752d1530f8db8f75edd7caf01f0792aaf6f2f2b551333c3a7b7e2db8acde56ac5a66288439fe8d53a8c533bc45256f3b1751c8790459323d0e5774d3bcc44a239badadfcbf754d96b8b984b66271c86625c19139146649a6b7ee207ae93e90093c3df5efd8bc8b715610469914474e70f1d2e9677f073c69c7be83b853d168c270a05b5471c6d05eced76a1de69e16cfab72ad169c91ce9f00929e79a8678e829af2e2d77e825d809af30315c81865f0c0129183d54200dc52ba05f69beb4cfc3b573cae01f7f22e4ec70c95d72fc9b513f032dac7e3b5e94d40928c1d9d98babd68065a9f3c518e3fcb69d1f06fdecea7c26b5180407731f2e99307ba131ca01ee0e3798f7b9f1defea51cc50709aeda4525b06dacb8a2478faa8cd16af0f5ff8f413fcfacf8081eac3647a12c932a371f03d6e67a3ed918e3ed48f38ccfe975bb2eb41cd168bea677255a367b3f4dc84d25f2a7aca85eec91f8fe0f7e19bb597cda0f658a4c8effe478d0917438545dec16b8c905656ca8ee45fdc9d6d925e8bd4f327277c7525e8dbed41acca178c3211da131f846f3a1cb5d8d9a5eb802865b4477815515291094fc5352367f6401781dca6822cd3aa9e8d7d258284d1723fdb50a0b2defcab75504df6691dcbac9f22e2dab865c272425cc0fb73390db4a52950ac2339244494559cbe1f17b4bd6ca184db16cc912c3aabb5174c11082faa8699d4704c1d1cd0de780fe7a1b9f60922d7f25f27f9ef71d7132e99b53b029dfbc4930d7272d22318ea9aca18a3af0d61119a2efe76dd5651c4692a8479e2d036f605b69ec03c653d461191cb43a5923088d1bda9e19d6fdb77d65430ebcd3decd8525f8c932f905037a2dc970f1c901273a4b8ab65be2bdb514fad758e80f7d1d753bee3018fdc9e010dde8829c82f72f62d252adafd58a8f5f626309c6c15fba5973d6059f292fbf53258b649e3b6e079e5cd7a73d5cd303a5a95a12a5827546a962769e4005b1a66bdd4bd8534b8515159b5b9e1bfd4b4fcff2c7779807e6b6424dfedc5d50ab2756c6994fd103701e19886f7075281d6839b4141694e4692cc6a2d8d04447f54995944c3dbb23040d76954908c955ccb3e789f2a2a3d5ab957883af153ceacfbe04c97bf328c39220734394db28db4ad1143f5fd052af09aee51f2a36149223d2b994e535740bb620a31537e8f781a80c96166a577fb3669856a3795dc3ba328f909027cde2dbc01071a1bdc68d88e6605a5c4e2feb96ca7ad5e5faaf94fe4e4b54515bc4d42f13200d282ff4743184ac58d7bf56de98ebdc19be21e21635ff824c556d30119cff25e3a23738a406232cfd2738cd735d9f79c7b1aab77af66a2446dc12f95d64bd2c2977dddea52d2f3ab3deccc07c3791ac3d8190f1f2bbcf1f0976dce68b292f08a020e73ec0e5ed3fe2a301fb88b4df014c41e083f596cfc41852ef2b2f026c2e622275d2f8a63bf006606005edbbd704af36d38df9329f617640bcc65eb05614a5ef6d424902ad5e216fc5d275f993e4597314313e5fa4f97e606c7aa2ec072dbaa37dbecf991133bc0cc77b0edd7e4ee24367a4e910be08355da277b9c2c1283e7e07d7de85765f2c6453e2c72a90e650c741664d47d5e08959cd9229c0701550d87fb9eaf31f7b01dbc21ca0ee938cc23b3922a620e53da029c1048637691389ff9dfe87fda2803221deebac973d42b1946d838000e850f3531f4f24704f2954660b51ba732394ef19155986a1980e3a48c7ea32585243f855913146bc2903752ff3907f3186688a941365dc09901385104e38e91d1aeaebebf20f2cb467019454e86935cda6432105af49aefdd31f1c7781406f79e0efc697eed800bf980910029430aea827a60dcf1f1c8761def92555a1bc008d6e910a0895696632cc2b54b5bbc0a4a23b07554ac7871ad53a4e2cef65bc17c4fefcff37581fe142f807150248372169b521b0064c208f3abc3be6c95246bb6f04237785fab97719400ae9f8947d50ae7679096a1a2a59c666df7041a0f8f8672610fa4d9bd8bb03e409b746160ae855681046c0f5e8f2db33d26b159621de58886583f561dafc18780aa73acb6d03c39e4cda1d0e996b3d471a8042bd79c097524e09b1b91c06f59925f6f1596ec051dbcd1f4419836b33d011712eefe8bd2f9a0850b3a2d1d1262c1c18ccdd75913b4f7cc20f2c3f321cb2d1cd44fa44f590e745db27e9b6ecbd202ca32319c125f7f975da8eb6d999cb8d6061d9eb4e63e43c0a3088ab9da910940795fbc18e0071334b7a7f04eb4c218d92c2794706472f7f590a8070d987db610ceef1f0a67d5014029f1143541dda731673bc68d436023eecdc57d88c6601760a905ef363c3a119d83cdc92f82f7f643f29dc328ecc4211cd779962fb430f411903ad6e25f96b874230272e5d22c3902750798f9d604ecef1c848208f68bdfd10930227bcc255459e32b643dc9a9b6a4e9591b6581b7de69447748649cf35e20585cb1744018cf6c19066f137147acdea05a54a517ba75f936f2ce91fc6427c0ee67371a18021d9ac1ee7bdfea83068bbef7fe5e44612a2e8b2c873a53bc8f61205839e3b6239f4a0b934c417df361cee751fd98915980c43b8bce48a535a540232fd1496e9b4acc7ef2992e01b43a5e26cc1b2817f4c91cc83cca9d665f66508ddaf37f0c501e5e16b5e8d59cfa2e1c0291b8cb7fd540e5c85ae9074636b082dcaa633c48566a4215505923f7ad8d98e8f9a0c3bdfd034f09cd3a2d0f6659b1d31e031471d44517f4e06a8ea1c7d25892bfee9576b47a252fb31e55ad08f761c6a1c37fb4c7dfb1ba6263b2353492e87017eb4f8bc7b776bf0b3eedf12924025cbcb79d0e926dec98781067a847c4222004555db8ccf632b859f5acdcf54c01329df2367949272ff02089fc02e4359f14b4da7c02043ecdc6dcbcf6bc8255101b1640b10c3fcbe83f2022c701fbe82cc511b827ec6abaeadcb6643a68c20d4123ffb4ca646155506870850d2aedb9c4d32a037eeb5eaf28fb0c262e3f76a1a2aa4f6e7980c793d60dff0a90539aa94c777a7e9e8f28417bad16349d2161f5712fd3cdcd4fd2a09f72a0a1a9fe3912db808d98388c9ecfc6685be463ab0e14c2142f049df4f1628fa31cad4e7522021488cea8051925133aa476860d45711b6082146f4cc8a149ba9b47289aa42b207eefe0850e3244ac801320071cdd6e3cc285cddf5e04fb7a28a2b7a35f60c0ddba1ea782350f1900096001c96ecee95001f1c47a8af1ef6f61aa0e6c77344b206c6c86841958b9453801b4b6eb1282ecd06509df36bd55ea8bdf91d7bbd8746001d9e48bf9a9009c51306acd99a3d1fac25dd02cc4176dbf6074588416b222e2bbbabf68c0328b90dd79723c632eb059e1b123e32f9b91ebd49d0a48338b55f3bb9250dae6b81796232094ba6e5c3b3bb2d0d138dff99858aefcd966707a8e5f813227225e267b47d633dcf9e4a654b91","public_inputs_hex":"0x224ce3d9bca060eeade7369297dcc3b23ac24e688fb6d20b500077dca6b9e3532f2dd8535a4787ea8794523842a3a4f45cd84bc85749861fb660801b495b32700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000ed885dc0873d41b86e03b401da548aae000000000000000000000000000000008ae9fa5f57d2c0195f1138fd669fe8352a4f0c291b36b9970cead06bcf01559b0d2f7b3296daeaafc5edc881f8174d620a9dc790b726d4b30e4d8865f33f50004a47a309fa92e169d5ad235f41eb12542c77628973a693c21303168ba71bde796957f944c3a3494bbdb558527b5ba0c6071f98da6c5d2594c5ec88e9d1b4b90759aa7be42eea0aae2489f0b863dfba9b1cfb95c79fb965b14ddeae2ac0ae58e6f2b8af40f21222f9a964c0e33eafa2510fd2e0c74ac08efb87cf1059f1e30dcc9468113293de905e1b6098606e2dbcc3"},"decryption_aggregator":{"proof_hex":"0x000000000000000000000000000000bc96d0538fca262eca3f3a175a2506ecae00000000000000000000000000000000002405dc0060d7ad9bf68d09752bd941000000000000000000000000000000ed8e01384c7d6b576060a26ae07f7f53ed00000000000000000000000000000000002f138f537e526e50b682f98bede894000000000000000000000000000000d9c70249009578b7dbb8549bb3fc5229b300000000000000000000000000000000001ed544ee7e0865dcf082904a91e664000000000000000000000000000000344b89579d5dae172baba8134c2dd49e13000000000000000000000000000000000002be76c3a8f17381dbf90cd95ae00512afdd01584e2937036193e1bc741749a3450bb3210a67cfff4cffb51b9c57bb04516878a8cccdac84076db0fd6a14ba2526b911eeb59fdb268706153f41878a0667aac6eed94876f8352d35739c16ab1d9f6bafea31c6f58936978bbf40dbfc0826c861f7dbf8ac3d317f69786b62d2bdb8a87b1a08ad534c70acab3857ebcf0993fb91fcb026ed3c50b587350ecb4f55255734294652a4e9922683305c6a0600cde5cc1bf5c3f4961262093ef555de0869886b2d93be75fe79eba7abf6bfed20fc0b7c24baf83f74a032b8373d92f21993d6182761f8ab55c4d85a070a5fd8190cc3878ac311c64fbbc0539e8b392fde7525d8bf17772eaae3c70570e522a11c14b30d68db30dee06a4d524c4ece4ff424e2ff9f1a69be29f1255c339c914016b42ba042cf43da3759887c145d63c390de00851f761ab9cfd9c6faf2512aa50c6753d21de84de06c37adab260f77b7936b0c44b6480a6ab0f2e3122ad87e310aace4034e34b53a49fc9ecdb1483a03edca34ddb794c170bc3c581a1a4f7e8e12d89afc87f8035d32a60ab5612ad0b4b231d8d6ea050a39310cdd764db045de1f72fa99e5822248fbc73037375c37fe2294c2bbdd855ca89587c43d05af40a206b187f8f88a086dc00a9c07a7159cf65a3c42a32218d9e04c263666bdf87e8a10edac214abef42f85564bbadcfcea8ae059a75953b735fd8e68e571035c4e4c2faccadc09fdf9ad93ae4226d5116f60aca908398357193a73e90883cc3c7ff2005edee4d35d841ca7dae7e03b062d50d5586d06085138534ab3e705faaf895f0f875116883ddbecd75741011371a16082ca1b49d3404f7eeebba7d4c7f88e8930324da8888ba37df163c6151b13bc2fb1c316e2b12d9f958e409514c248fa290acfd80696b225ea88590447718ec17c5b796bbba118abb21d6fe6b076634a6c23904bf3ca14ef1763d05c406d58aca932df42f80011a768e460d95eed48451b1bd0780c7dd3ca1586c6189093c11c4bf573809590caa9e0c5eaaf43e8a145da27b30a3cb1fab7c7f128cac57f5548d3e3c67e86a033535d8ca84ecc128826a21cbb30ca2f1754528421714ee134d69f02948a1d99cc6f10efae0f2dfc3f9bf21a717fff53305852764acefcf1d6eb9925a2102421aa0d57bad423600f64697f07dfc45cb3b4d655cd1f6b0927ef379cc6139722c54d03d2ab8bf8c5f085a26202fe015ea1c212b3b2624bd68aad04303e80708a1479a4745a2ab307d5826ceb07c2b6ee52f163e8af6143847f379b07af36ab5dd16159c42682b317980fb0df0f49a286b1e71b3002a5e399c2ae3324f93267a4a21ac56f74b38dd83f9a0d0b2959020ac19e1e580726b34c08ec23ed72473a46d82e7cc436662d0b75ea11fd2802c4e55bb1f55616fb153a63d9693d06dbfd963fa4574239af428a1f0a29c611c5a005745e62958a4a552236ab2694f01380f056b915f7aba92a5afe24b9402b4db2b55a8e3fcae34fb449b89e6897c92a01009b04702f538ff6e71b9767261d4dde7fb8c60f6f88eb6258523a22bca4988ecf1c541b00aa0ac1b96492ec7e26b4df0dd6b138f3f2328a84c6952e6dc98fb0636bd9be7034bef275f84d46923036d1104f4af82d2bbf9283f8ed163cc63c5aafdcbf21cd4b3ac76a3cae6cc9056da70ed5f90febd1c99afe625966d1ab565b59e664707e6f838b2333c5c2201bc1c887523d29055a3faaff2c2d86afd560907bc4cec3c73748468927d337d003ed9e69e4e6ae6e3faa1a9e9716be6ff96ea11683e9094bbb98ccac4e457b050aa7cb7d9daf60be0a1fc4cde853c97043f77d2ee85eaf570c23fc3ccd83c6df060d99bda068172b6aaf5b558fa6a2be31badec375d70317602b499793bdfbe812d706dff901460d088caac4689dbd00ceee3595cba912c4e04478af243b2d5e2e382459e2f419cb0684e2579bcf295f6c77bcaa8184e3078b3349d017abd4d814d49d78466cc2a395c96c3dd2e060015b4a076ef5079dd51347dc1e1e35b9ea1afff659ba63fceb905cec9dd228cce84ca063dff23d25778ba39e1e23c276ae275ffab5d82f63526330124d69ecdbcf83902dae2dfe74743980da6f4d1384db0cae659207173c164f6afe28f376ee52505ff83abfa91a221eed248404cef7a603c9d392a76090f487d113d6687af57a4740df5bf7be5c51fe6d5facbbb05f230e37ba78d57eb7517506e8c67c42a6d7c28f3feec0f8d8a6a3e4181c4eca35331d2b6a6d2bfd4d8fc13c74eb3df82f42eb9a816d946d8949b9c0773320f9aa792f4743fe52af9933f434962c5a6d6712d99216f90b6adfac6a5800c7f8aa30852f67d3b82b0e334de0e26f5eba013504b4e9f925b0ae39095d5a3d79d407f0d52edf3528813001cad53db592c38f083c51b40eca3f8d271fb40e0e5f30cd0f523023d6e76357fadecf7dda8fd192a6ae41963082cae9117ddc7aab76965e0ab22c278e0009b458750f62e589bb2089ddefc6e1b8340184e3a43cf0350aa377242ca6204f0a81c63b4352fe2c5b72cfa0e09377fa45f035db35b61477aadeb9fe2f95eefed8c915412cda1f26aba74c1a858b7440eea4e022a422931cb14a747421a98b1b565ed1d352bd3cae7404347f2514c459b6c4602af12d0598d146433a1e9784c6cd77f9dc9e59fbf0e27323f371e0470912fe7195183794094cb3d4c6028baebb5030cd170a4e20ea8cfab9289e5675d2f20b3f69afcaa9271f854a4d16b5efd806e893a40ab731e1c410cb9602caa37b061943fa5dbfb0cacd4b981805ebeee17f74569607e869df9e12a7c7dc61d0a6334c23c4872b75df9151642a009a9b4240bcedef09885e69815370b8dda9d2f019e87b55273b27a779c32c3117680545e1ae213aa888f5c0cb23f9f975079dfbc54a7d844b6bd248d2e9312f25342105e1cd058f08c1a0d7ba857320b7e837283f54b0d88e788bad0416c60c002dc6c60d7c62cdb5973ac154e2fc903750e2684f43c383013936c03d6c91d32d6ed02382e18ce85dbb092bfc17ca854e455d496784c5e8de3faff988840ac71a52cae9786662f618ee88248cf3767dcf068d1ad0ba60c0f104ffb7ed7b47b22aa2af169a69e8b99ab3252377cde793f2af45a8b053d981d075b6338dac083b0ab8e963bc7f61050bc43c894243eb2ffe0df3478edf5494d7337a97e22c9f4314a2a1bb29c6df3fdd996ced04a459221d8eb83d0d25321decbf3077ed22a79e1ce73dd18ed5e66ab2e9ee9868f60f13c6c61a1a26c514c1e720563ad91c68c812131a5b6802fd63504820c21eacd850b1c84010ddf1f1df60f0f57121e1fedb0f40e1bbc592fae3071203d7d6deb2593a0932a1679cc59c6ae2ce3cf3ec2a0619d02d05f1b1cddae0144baa68bcaf11281ab3eb23a213e472338590271947dc012a1c39101b5f09b93578b8ebbe79323ff1e76e73d5f633a3d5236c95bf61fd24ba8abc83e67d9bfe4f8ba975a55deb8ea9a5cbc90a571ed40383c5cfa2d88603dd5fce0304dbc7daad12b6c12da88baa0572e9dade80d10ecf1137278dc5bc2ad88e74e8b34a0f80e54afa325023a65d6d7df8c282bbd52284bf8f50dbd7912c65d8458a87811522e8e43d1947ea6e08827d23c94eaa749914666b491a809d092c13f7a14ed9b84a855c67bfcfcd41e0615d1b63251166709be28dff77d31d0931c5219698d8d0701983b502488eaed66c57f3a3479183731695e4955e7e1e2d67244c478d1ce81a97d3db0459a38bd3a01cd69229d4cb232d921ac20a289219fd2251b56dc54a749bd8bff0f5315863ed08b11e06b08f0daa8fdb51b045a520d3c9b2a069a65b954561f3e3404094a9d3a04466d91083ecdc40f78edf5b7d1c7b6fd1bb013cabf8e69a4fa265a7f0b1f1e6c1936ca56367b2ad609938e4421b682ddeb2c6df53c24b4e60395cdbda337471e80a423c9d1205d6774d9882c82fb717f2655a84d4821610875d973ca834640b1cf337dc996847fc2ae8df028727340ea87796f42f8750ab8dc8f93d460544bde04f1f5830b9f666b26f1fe523000637187ddbf9ab82e990a0c7419885084cc6bbd47c9cb3575f56d43d9a06942efe9fbc02bcd2b38f1be1f1ea1753e68d197e713287ed3a1aae35fb71b6ab7b274e9fd1a6b64ba0e0a2b0c9c499034cb6df66c8bbcdc50afa671f374d0701980838460c129b6156066fe569870c00e8bf243907309f3e9f11027fc4b8e0499922061d481cc584a6048f1d9d7801e5e510db33eb3bd0cc60f4df3198f5de239d0deb152110ae11eb3999e77dbb63fcc25e551cb0df977aee851f7ab22678fa761ade737512fb1096f839cb496f2c6b176f35538015be8105f785a1fa46421dd90c19d03f6dcf610e7c0a21987be11c9ec8674af552a9e8bc70c6ebd4dda021262d3763b8649d418dbd2ece94726127fe3ab67b77eefdf0ce5bc815e5a9a14266256230b240d837dc108e4b3c33c2156e007efe28b89d008cb4d2c3035989274102e59040af902839c1d02dc9028415f545aeb95e2723d066046b1e6e309524492466ebdafa5f674d357cc633a04bf1c5e2a2e46d8f45d348f6b007f110c66aad2208f42595eed8f676d1402e80deddb96779baba3606acf6b8f3347ae89a1ea024d2634d631b7c11ee934fcb194740c963814f86cff8bc7ae89e35f5e4481bdd172114e526d5a14c317a3abb260cfe202e071ea270024a8dec7d8c27dcedff3e1256ad36f605d247031b5b98aca084a0ccf1ab16ee091d060d674c22ba9bbba31ac39fa2c5ccd1296cfa5206ea2ff71f2c994c43e4a13752211efb3ac8cce9380b26f3a915a74cf18055103440aa5e42eaa9a0631dd17dc08f6326e56a148a24292b155b32e860804d721094d93d132eaf0cdeea60406f83ed7a36894d8cce6022e225a9301fdd6680543eee7563da27e229fa2c354c0abbdd0f9b13f75db0cc228c030bfaac76a0a8e8d1e7b7507cd164ee01f9f134d8ae0dcdc769334d217f0f02944ccb8d42f84455f74eaeead9a5c5d5badd98b800f888f0d5503a07a1431782b6b000cbe3f90a219d8d9d385666d9b8013082f47edf494f04dbc682d3090984f0752cff98206de8c311aad5217198a5032a23c864771cbc64c65d89f83e270870edd20a16af87f484e19c0b4a81d0d9fa360dc0201194df07d6fd0699d816e1591946ff35e2ccdb92d7aefb10c6d0977138f266738413a4002899081bad1b58ef7663603fe04e34df7861b25da5970153d043f4d86d55ebe37036ff0b4c1f8f79db062cf6ff825d1dad33dc4b9a98a901b6a6727d846ea030fd6116c03024803db18d13e4962525c1687a0b56011fc0964ff6a3738fdac2c03b75f63ab91c36ec120a66f931065dad13d2c00becd94af8055868f1232c89b0a7fe108ef42631c25c926cca36efee47a6ce4c65f8cc65ec45f6f4104a738550c9e078d9d321b74b1aaac36c7cab363382c50a3ee97d1484599b98a16d7b535ef90b890a250dd8195ec565b405a2e9ed233c93be8ff18750dd0e4464b0b79299d6e53337d02c9d5630ee9d8cd206788148a626e4451cc7ef3f9f467edb204601cd4c3acbc01be12f30e6400a8d924e4c887539978dc987e537cc388c52ad20db05a2d64fa82066d23541a96eec5f79f542aa2973ed771bad079190e9f3a9d0f48f5916aefa0822282cdb70e779f9f396160ed0141952b6274a8c05e380309759c962c2269d261f7713162b208290936dd9fd8df5dce134905d0a8e6bc27244940a1b7e916e13d6bc0978e8b25849fe2720b2dd10083d04ef1c020ddc628ecf7d47291bbf6808e411c2be3c79d2907e2fe292ada38239655626c449e3ba0a47225fa635bc2e28f19719d31980c6a6901ac47c1736e19b9e066485986ffd953f3f7eb09545cd0321c288eff02a8cb675719c0bf9a5c975dfd95f71df4432949dd382b0f475060b2fc48a2f5a87c3bda8d2256f4f45a171f694a710df872ec60a806c55cad69502a3bb6def08e48906645dfda0793a14eeb567ff7c6cb8e2e49a5cf8f74eea7c0b7a572378316a67bc933b087f1a126315f50b790ddcd96794f3db597c284cb103e93f1a1deefddfc386d2e37d22f83c0695ead19fda8f58e9c88451efd2d5da012e9e178d13a34e77f1d8f392cfd12b2fefff4882a9a79e7e97266ae20d4db900430c1551c5a2f91e4d5f8f41b32db0be2cbdda94d2c6a3b1387c3a40e90a1c283f5bf2170fbc21a71c22e57cdfcf7d23a069b2e58381c0e147fb8baa5e580a14f9a027613596f73cf997bad1aff33bfe2a79dcf467f103e41ce84a95c1f2f1114249ac71275e99cd5ed73935fdb0a4b728eaeb12642317ce06ad5c3ddaa44016ac4e08c5f384c67284a724426f92654c49f671fee576746f9bdd429770ae0d0e90e3e0cd9d18ec2342e8e6f0e796be33d8aa67304a048b165409be1273daca29644ba681676c8bc306e0aa41f5b87549657e24d9117b4309d2cecde487946e0ff7d25a1cb33d1d841dbcee49307d8515b283ce87e904439c839dfa050243a92d346b7dceed618937270a3e7c870ab80f626448e2e8e71e238220480ea26b4c1e2b96d7205d4944193d951dfd41cd9081f5f9c6907ee8b21b81d0739a51cb301b702cc7c529441441183025abaa3b5f939a2526526ca413d7750ffdfb33fc4110c1acc042f1af276b4ed380035e141a8ce3863dadb0fff0d191658d9c69efc0265f22b367262d633d8f855231e68ff69665617ec44a572420158af96b344c31156860db0e2d5c18aef202bdb2e917880237cecdf0e443bbd236939fc25217cb1eaf8e3ac15e3637bbb78a2ccd510d75eeb119cc5748a4d7dd1524f98db2aa7b19dedb9cdd1ab1de33c47393dc0e18505aedfa9daa4dbdc236240d3f8203b95c2e454e7b7df43bb41928540606282ea508b2c0f4c1a669f2a59f5693e78240662db56e6edaf2a693922cc06349d4c497abb72e1893ab12d8fe6cbdb6a76eb9d2206523420347b845912fcacaf97c5152f31ec1b001a3d8a7711feacf4b6f25fc2bd34d46e5318c7f45d748a1fd678ce7bd01020bd9a2418d7cecc72768f933411deea420542f8c26bd33fc20d1b9243b4ede6fc3e6fea720b4e2840976f7e341287a45eda3e347e08d987e0152cca64132234ba71c4b5e97ec5dd4290e17818025c827782668caade4e586c97f2a448832c052c3896dd32420128b44435281440e5e38c2b6f49806e90b91365e360ef6de5b97965dad8e139681ca0dc9ca1de4258a87e2c896a0b26e6e839d21fc5b65219344822119bf1531542d769b10e5512dec0c9df046d250a8bba907318336f698312361def22d821bf339118e2b002d230e89587f62d5ae77c9b8b57367999ffc454bfc63f6dad59c99a8934de6861a1e798790d75fa4b7a9b2ed0ae5975b3d8cc86e22d3bf382043c2347d4b23ef331958e83a3735259a7586792852390b8bd9ea465085f7932a55e45616dba802f83048d7973b1bcfd0508bd05543ce4f91ae218071632032a84751db87a9e156280a50058c53190fdb07cd4da0b26e499565152a1fb37347c2a2114add8edcd22505a125015a3e26a93905e24565fd25c9171daf5c562269aec392ea86d1ed741a1599dec50b4352e6c441310d598c9712b28fe526b0a84fd1e7ae52f289b2b69a06ea30bb6cc1d9c376caa06d04f168bc6715c40d124590baae60671e0fbe3c7e117a430d21c524388e5173e34890606d0e54f353cd5f8a80fa47d095d2d0ba2c0b6345b284273c77ea2aa727a648441312fd9d8401945e3496068855941d03180c4cb91075be6615770a540d19d79f5a594b6221c1921b14b3a047440dc556790ede6c003bf9b126c49a782d5c22f8a322237bf2ffb95284cedd81f8d80fc5750faa0cf58c06ec0ca268d39537c0be50720152fea206b50ce8dd9a57d6abd43f02509ba7da5365cc43024ff0bac39566f27345d2b2364d078b038fa67d10ec131f24167a74b99940610c56dc526c5015ff54e4631facbd77e18d9031b9ee305226a3159deaf9b0dc2fd3a7fc78ade27c4c2b47777bf70c9758d45597ba13c3f71d4da0dd780d50e25fdacfbfb74837d19438a6273c4dcaead1d06deeeb9c948d130f064ef140b9224911cc5485b8119ed8b05ca81e239d11d83387529d74dd23157efe057e6ead118ca64aebba45bbc9bb758627a7f3ae0db60a98b6364074810028e5e5e6ef4134c9b181245044d9c9add82630fab4f272f277422c86e1d4842c1c638aa541a85119fcda98dfd1630ddccdbdcb270fe62ddc74d29fdaded8950336a2115c4fa72410b6245af33b9c7d8c41e99b3b22494e8fd96cdabaa162560306064c141b3d6160cb90356044fb43ef0d88439a6795691150722683c9a320006c2492070bba3427555d78f123f512ae93c17aa504fe1557d296fc19ae8f6815b60ac4fd7a0855019bef36c6591f06dc4a8519e726e8c8b3f84e8abb05d914065bf15fa5b569ef988cd9b391e99f911fe964a06791357f443331dbf35e30f117e64fd3c3674016862991bfe70c37d8778ff8123b06bda8256af0a28ae020eb27dc718963085585437c63ef0ffff850ed510a0172eaca0d650a17188331c3e8199ee02aa2b0b0298ef73494a98310bd81f9e6c88372b0eb01c74d42d75587722d656ac88684c418c2f5e46052783fb90afe058f923aa967758644f04d2aa8ce16c0e95aaf8a95e7a67fac56e7f9486b49c9e9fe9b2bfee60301116fb47fb5f92b59d0fddab29c9b91018f9dac9ab7cbc927d1be9be727a3e780a3232be97bed1cb986e63dcb100b8f451e940fe4a3b400a962bb9456daaa1c2ab63d459df45e30346b510f81f31f0a29571156e2de0bc46b69cb05a1515c912ead1ad59929862aedb43a1ea0ed151372c91814f912e007386f636f1dd30328c8cabff2211c5c093a74da592b9d5c5da83532d8d2fcb24cca63f7696edfe5573752436065c07a1138726222e334d83eeb5a8d09ad7190589fe517afbc4024fc79d8d8c903febc2ae38965b2d4e7c27112c6ba358244cb215b0ba017c9933b2da4bad749369a522c3b603898472f8e44daa4d6733f9375720edf5f2f5df0cddf7aaff381ff08e60841ac46d2cdd26df208b1b81b5b4f4830959ed8c4aab9323974249bcf8e8419305bd3af49bd86b4dcd985bc7eb86b650a16129503dd4e7ff33def895f266664298bdb6224d743704e49b87c73d364109e8f1e34d2e24127c494a712267cd0892715686c0075b9daa2b09b7f14946b1b1bd19c0d3cc6ad45fcc508f8a51b005c2fc5e7942e8b4b50598ca0ed1b5dc525cbf38bf7d4aba56ea94470b4ba34fe5a15af8f67971d9af9cb8735227d522af0489f8e4ca6b46db9e84b10d8a317b272197428f03dfc19fe78c9935d8fb9c442d92de470355afe58871c768b2ab03b902a4cccd217530cb9fbf0d5cd6624eca2d724f6fcec9103847e3c932df0b073250a84e6babc5cd8b3bafc3045dede571bea8e9276fc64d8d8cc21545908ba564519598726be27610b418d8c2e836f557a80531cc031a1d6439ce57d6fd2a4e2db0ab08d51576c28e6b0b6cb1fe3c28c26477cfb8b1d0431d3d48f45cf0c6daf4f0765bcc41c6b82500721d06786df1163b97e1d85e3d81d1cf599835d7e14cb02206b23b3fedfb8d203f983966029450b2a88496d37eb2e080f7b28ea4a8f982225c84052d11f7f9d2c38b3f3b9bc01c14711cdc78e9448ae996bfc90dedec04c221633b7ef1ac3384e1899453bdb52eab9f89626940cce87a7b8e5c821b4523c070383a1560e84d09d7137b27f25ee99397a7f8693566cdbf7ea9f2505e90b8a1fe800e2d6ae98cc779fe708ad85e18b110d5f7c6a2afe0c8e02e0e6892facde01e80d5d925a6025dcb2af64ee3e2d806f4a35d456613bbed9c5605258cff91e0119fabaca716eb23e06411015fcbdef1d8525ae8ea158b2ae1022a47cf32d3d16f8ef754e6b1855dcf6f35f8b1bbceabf2bdf87cb89bc22147fcdd760026aa92c9b60b03d87250b91b888391083cba614a1db81ad74d8368ba6d5ddceb8bc9c20f45a28b04503b38f4cf95b9858288719e95940ca183ffaa69daeb24b6ce26928b48958f9a3b94b0591b4f3e08f9d05d2f62673f8fa30908dc19f48c6dec02b2a89f635a4538f8dfbd510fb12acd28f8701ea96063968012db76bb9a11cd2eb0052cc417afdb657e983916496acb46f22e7bbd86bb576136ac22ef674b089402cfb8b89097aa787003d672e8a0fa8f73e701105fe0fa4a58092af72262739c70e6dd8411c9673c779a66cdf0d66b5a5a75b488579fbc8d8721def4cc0934a212c7b4671e01da0439f936e47374f3bc6f0c5b694409c96e771159a7aa57d9340122c6d97e82a141944f0c074bd87da8c4ac369e4aef92447f98c98420720213a2a98a281819fdc918862971501334269bcd5ed15d5d405b6c8daf01abb4b6362303788e44f691991050045f7492a309818f327245906f8a58866afce03b7f4ad2aaf55cc58cd9238661122edbde8249494ef74653f8b269a9401032a241902e3000000000000000000000000000000000000000000000000000000000000000019580d61d1c7753149f90f2da340c29216008e1133d65577b3d54f48ef3f8e3216a452c1c48a73eb1b8a5e558ca646a5c30a55ec71b3ca8ed757f5efbfa20038069a224d1faf7b130574b42e45ecf0083740b949df53b7ec67a1103998b4d6222fa3fbb78aed33c874ad1aa4b87633e0296e4827b1d172d483fd6a0578d1b32a29f5bb5a197b3c0d95972afc77e01d8f53452a75c29bfc9f509d582277958b08138f909e37e46adcd103ba123a956c683d076110fcba213a0e40e0f5453d1ca10ba45436129c22ddb1df1efc756ace1080f7c658134f39c1d42fb75ae077bf6104e76169480b94f7afadba348a55e5f784ffdb41212c4f82454aa9a38f8bd0e2059194ff8225b9f1a37f31e4d4d98945713f15cdaa072015b3f0c43fac9c79e30dcc3a6a64b8d8733a3a348e2a60d407473131895a48acd85ecaf1c1a7ff200613e3bfd0535f009022889a42430e8bd1e7a856a701dde6b4c651d506b4c41c37183a41937a6b2af6b4b1e6d6e2d63f3e978fedf204e039cf2e9a522a2345f6c12efcc77bb4acede0a742718a1594d7ca3efbe93139c3f9fecce6c2c275517bf82fc3ece51941187c8ff587f13a040fa2314d58e39713a328e2a755c4813c1f68057c19049be7f137ebe9f0ad967767c694f2b49cb74b8b2e204f4223cf832c4a0d9dbd8cb593dbc44bfa78bf9efde6d4abf88db13b083734bc08ea0227adaab728678afafb07ff737870a8dc20078f76309f7b09ee9a7e0a9952c7fd82340b2a2c7f4bf1a4ac1d8e6328cb475235dad988984ae8eb687d788584b17da606e9e3250089be90b9b2e86c740af3822d2e3c490042c15aab381a1343f9295515a13617c992885dbd426c8f79cdbd61cdda01e681829ceca10649af6821e62e465df72f5b0d9b33c7d6f414681925ef28644d57ed54c2a093bd512d0150c62cd678f72ff4fc39aa44c5c065a536a2b44244e28551b15f9d38a262e4abe9b0b8e128e42d910583179b30476b2997d9a38fb6b59db6a6beba53466417d08aa099f5e5fd2150e4c0a87b3cfbf84b0497cae44e5f8763f5b7c13862c101a8c82eb48590e21c092b1ee129636cd99ca0cf04d7bd67d92fb43bd47c9f7afc416edba68bbe8912f97f80acdf13fcc645f50a61af8431a2ad354cd25d72d31546195f1c8c4f6e0789de44abc1b869ad9379b950b146f2e89cfcfa1ed4b126b673c68310eb54da16cab040f8ce58d8a03b4d801cda72ca8fdffd75480870e328979702a922c62419788d106da718405955694420022c5e0e7ec413abb99480200ab8e9c53c77d01e438deb0981c790c12ee463c412d00ffe4045c5c6ac860fae361d4fa357dff124030ca85ac1fe1e56df3e0e0c044b5b951f33e97dac8e3f6365bced9e27b5851a437605c42d6454f9b568754281b40f123694d46bd498400360d03ffbf22eda152c42abade99d57468bf0f395fda8e6ada8aac3c8c9a7e36d17f56233f945b5291cc8888930ec5f4a5b2937da9be51285811b8cad5e677dccd40fc708eca00b0388168c509f0eff3444aa0c7d74b91a3b2faff20de7d2046ee07a32c22ab84c07e0a078562ccc2f34d563fd03e2cd821aa50c5d0f16c2891a2fd2e5c9b514a70f73f0ac86f37c58b5eddf34d8becf6a288fdcdeaca88248215e68032e36374b261c214fc5f0525d345bfaf2d6e5d59922d85fe851e2db240a427cb7853eb83c2cd2abc9cf87de4f6cb384d34c75118aa95dd317959598181ac708c8a039d021279aaa67bd43d6f1535d8ebf50a0bc56a45a888d0c241114d56a3ad860fca14005433532eaea9bcd9e7f8a104ce737dda9de250a5cfa7f77bd625a2c4df302db2fdf6e3b234a05e82e66da226c39d10d241579bb94196c1e14491b1cd072f88d1538af04b9faa7b2de2c2ec1cb78636f8d94b16b301603f181f0ac84ffbb4ae421f3c1645477c176ca241ba14b2d6c95da6816fa52c23fb2c6914887b11f83b4295fd921e5c8e1caced07c0efd3763d8a6a221bea3b2b253606b24a5e0e8fd7501db3cac38421f49e5698b43340fc962a6113473bd481f695fa1b8352c6b5726205169159b02f95c6233a7bd5ab47a31c3894f91b4f6e16e0b886295442d718309cdb643a5b2af5293130365efa58da6e0bfb1fde171db0c6e0d62886c7181b42377c265ebef2b8c59f5b0706d16d64dfae1dede5a8fa6c0a46a9470c9cfe7fd1d20bd68a7fcf32994ef60b3eb2e092fc49f9af7c9799d4fe72b711f662bebc614047f226b6911ebad90e79ebcb63e98fbdf3ce1d7572ffcbe46658d03620f940a0e9fd42a5d129cb610fd70e577a870de2d0c64ad65640a84e47c812fd98d8407cbc1a19c2cd34ca02b231bce720b7d2556da42212bfd0065b201b85218285912f2bbf5722cdb7e4eda59a65f86aa5a7991c90442891bf88cb706b06c1ba7580a81f17ecd76d465dc5c700e6e240caa0c1bf0f6ee4484ba6f154050f5267300302c2d7582411301b84d894906745b96a4c7baee6ae1e9b4e1fced0ab56ae37a2bf52e52f93136b0696f5903823d064b00a9b6a61f18e5dc6bcc940d0cd2fbf12fb21a9f53cf49dd4cb64c411aa9f1cdde43d7d3924e0260aa160af8d89f5240181af0d1572b3ce9e4866d39f55dbe6da30cbc094c4eac1e9798a6c8bd80e1c7085a884a77fb225bb559f52a9361d2f2e0937548eb03e9e7902c31067c3047650ca87e165b5a6416bba148e3c78df82bff1a777f411b5fc86c1449fbfc899e3b265acd250607a1b9c41aa27772ebb89adf3d4cb4280ef427052a7c32c622bc6b2633a0f86a2e2bdeb62c0b856667eb0fb609531fe1b4c0928e0ea230b88f879400b118df4eaa7c66153a1a76c30da654d86819530431bccf84be8a140b1a1b5f0574f6be68ac394b019d71f7a8b0e855e62fb22fbb1577059ca3f3f73e624da50dea5f2befb0a893ad9aadc2919939a61bdc986d65236db203707173d84d033f0d4ea0437d809120b9808fca706e93a9ee2b2595c9ad7c1d2bea3f46e1796b831ebfc686db8192abed84f9223236cb002dcaf3648cfaa507d773eca2d39fbb441c94ddd27aeea1b9b195026735642bef8ab61411d7b9350b8ea2d74af76a18251e0d853e0a3746e2d3c2616c65dab34d5ae5ba0809d6edf810d07a4991e7d8d9293ddb530550fc7680b1cda2f211566d3cc247166bcb033e253f38b4de6aad792690d9637ff335ae90075552bfe4e6fa0931481134dbfed4afe87fa73ab126922576f9228d76689090c5d573053a0a6a8937e30392d435c66c338fd258d671ee21f0da4180639a866ddeb2912a71f17107b23b691895c1b6bf8ffdaccb00c8e10efff64d66ca3030aee49a69616d4aace7db4bd2a1c874f1b1249fc9528ac614021999b81e1be517d6a7eaac84720632fc9b29db8a984fe2892cb951e871f729122084d129e8d9c92c633dc995f820e7dd63b62eaf72da81dbb328c976f022a413cfb365237169517ab9c1b39c7dcc07cfd41501281a8a3296182eca374282df16b77996181c7f888bdecaa4bbfc581cb830174cfc5eb63da1066ef3165a4dbd2976a089770bd8d633e399472255ad53ddca6435ea17d1b6fb4bb0df815638c90d2ae2c80dcf3a928c5a4b7c61a8b71b686b6030acdd3411f09bce00707496452b4c0b7c8bc01e2efe2181672b8406956c89011a25efe7d609a89f923904e8ce00150b063bc2dcbe47ee8f125810afee983495b76b8618360e8e132d3056f7950af7e995a2ab667d46b86bfb815f7a268ca4b468411385f65692a7772624a9d71e1a7baf4b9a72a23ec62a7e63223252b75de871811df99f12637a376aca03fd275d1d917b7bd9bd8ef3943e1e5979ed0e645e80f6899bf77cb6cb1464fee92b0a773df1495f34c590ee0d5af6ec3b41e50e61c42d2e967869cacd02d8e2cf4708c58d3039ba9768ed6e17a90bb3d303ab6e201f4188f86972798515bb191018074a292523cd2f3906fd66bc2cea4cd269eeff5d93a9b0cbd78666e4d712c2cf194b94842f9e1c75b055525f2de2112964ba53900373e81db11a0c4477755bf21cff0ae9d30ce0d7f6fcf069b9824a3fe663e82cbc73a377bdad6da874259b13","public_inputs_hex":"0x0735d58c9896306041928fcb73cd2faeef2a225c22cdcac38d802de73fae0eff1739d924e1b79deab6c8ab6f90b56b0676891aedb26fd3fddd21e54c0ac32fc700000000000000000000000000000000ed885dc0873d41b86e03b401da548aae000000000000000000000000000000008ae9fa5f57d2c0195f1138fd669fe83500000000000000000000000000000000c803e0614c2e62b8a0c47186569f52cd00000000000000000000000000000000dc453a74dd94d318c80f0875a5ad17cb0f7ee1bb52c323e82413b0d1903bc8d2d6653fb7b3e3ff408fa7b4844e4dd96905a2fab20a277a785453b5c51e89cf459b0b43fd7b33c55eafaaf91bb7dcd1db000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020a9dc790b726d4b30e4d8865f33f50004a47a309fa92e169d5ad235f41eb12542c77628973a693c21303168ba71bde796957f944c3a3494bbdb558527b5ba0c6071f98da6c5d2594c5ec88e9d1b4b90759aa7be42eea0aae2489f0b863dfba9b1cfb95c79fb965b14ddeae2ac0ae58e6f2b8af40f21222f9a964c0e33eafa2510000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}}}, "test_exit_code": { "crisp": 0, "folded_export": 0, diff --git a/circuits/benchmarks/results_insecure_minimum/integration_summary.json b/circuits/benchmarks/results_insecure_minimum/integration_summary.json index 11380f31f6..adea372479 100644 --- a/circuits/benchmarks/results_insecure_minimum/integration_summary.json +++ b/circuits/benchmarks/results_insecure_minimum/integration_summary.json @@ -24,227 +24,245 @@ "operation_timings": [ { "name": "CalculateDecryptionKey", - "avg_seconds": 0.006680708, + "avg_seconds": 0.004100166, "runs": 3, - "total_seconds": 0.020042126 + "total_seconds": 0.0123005 }, { "name": "CalculateDecryptionShare", - "avg_seconds": 0.030206389, + "avg_seconds": 0.023830764, "runs": 3, - "total_seconds": 0.090619167 + "total_seconds": 0.071492292 }, { "name": "CalculateThresholdDecryption", - "avg_seconds": 0.029794250, + "avg_seconds": 0.023864584, "runs": 1, - "total_seconds": 0.029794250 + "total_seconds": 0.023864584 }, { "name": "GenEsiSss", - "avg_seconds": 0.009910861, + "avg_seconds": 0.006982236, "runs": 3, - "total_seconds": 0.029732584 + "total_seconds": 0.020946709 }, { "name": "GenPkShareAndSkSss", - "avg_seconds": 0.014833430, + "avg_seconds": 0.011392764, "runs": 3, - "total_seconds": 0.044500291 + "total_seconds": 0.034178292 }, { - "name": "NodeDkgFold/c2ab_fold", - "avg_seconds": 31.622140902, + "name": "NodeDkgFold/c2ab_chunk_fold", + "avg_seconds": 19.208712597, "runs": 3, - "total_seconds": 94.866422707 + "total_seconds": 57.626137792 }, { "name": "NodeDkgFold/c3a_fold", - "avg_seconds": 118.019850166, + "avg_seconds": 73.755109361, "runs": 3, - "total_seconds": 354.059550500 + "total_seconds": 221.265328083 }, { "name": "NodeDkgFold/c3ab_fold", - "avg_seconds": 12.065775360, + "avg_seconds": 7.939563041, "runs": 3, - "total_seconds": 36.197326082 + "total_seconds": 23.818689124 }, { "name": "NodeDkgFold/c3b_fold", - "avg_seconds": 118.281532805, + "avg_seconds": 74.192332597, "runs": 3, - "total_seconds": 354.844598417 + "total_seconds": 222.576997791 }, { "name": "NodeDkgFold/c4ab_fold", - "avg_seconds": 12.187433402, + "avg_seconds": 8.053911625, "runs": 3, - "total_seconds": 36.562300208 + "total_seconds": 24.161734876 }, { "name": "NodeDkgFold/node_fold", - "avg_seconds": 29.707364944, + "avg_seconds": 18.833933861, "runs": 3, - "total_seconds": 89.122094833 + "total_seconds": 56.501801583 }, { "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 1.942771458, + "avg_seconds": 1.521341792, "runs": 1, - "total_seconds": 1.942771458 + "total_seconds": 1.521341792 }, { "name": "ZkDecryptionAggregation", - "avg_seconds": 52.543871292, + "avg_seconds": 40.263324583, "runs": 1, - "total_seconds": 52.543871292 + "total_seconds": 40.263324583 }, { "name": "ZkDkgAggregation", - "avg_seconds": 5.455903875, + "avg_seconds": 4.224364792, "runs": 1, - "total_seconds": 5.455903875 + "total_seconds": 4.224364792 }, { "name": "ZkDkgShareDecryption", - "avg_seconds": 1.183313840, + "avg_seconds": 0.809992791, "runs": 6, - "total_seconds": 7.099883041 + "total_seconds": 4.859956748 }, { "name": "ZkNodeDkgFold", - "avg_seconds": 172.312935347, + "avg_seconds": 109.022803694, "runs": 3, - "total_seconds": 516.938806042 + "total_seconds": 327.068411084 }, { "name": "ZkNodesFoldStep", - "avg_seconds": 6.133198646, + "avg_seconds": 4.647117521, "runs": 2, - "total_seconds": 12.266397292 + "total_seconds": 9.294235042 }, { "name": "ZkPkAggregation", - "avg_seconds": 0.517702708, + "avg_seconds": 0.364449708, "runs": 1, - "total_seconds": 0.517702708 + "total_seconds": 0.364449708 }, { "name": "ZkPkBfv", - "avg_seconds": 0.246842264, + "avg_seconds": 0.211209652, "runs": 3, - "total_seconds": 0.740526792 + "total_seconds": 0.633628958 }, { "name": "ZkPkGeneration", - "avg_seconds": 3.112043444, + "avg_seconds": 4.395378527, "runs": 3, - "total_seconds": 9.336130334 + "total_seconds": 13.186135583 }, { "name": "ZkShareComputation", - "avg_seconds": 2.979204014, + "avg_seconds": 22.39370002, "runs": 6, - "total_seconds": 17.875224085 + "total_seconds": 134.362200125 + }, + { + "name": "ZkShareComputation/batches", + "avg_seconds": 12.171766298, + "runs": 6, + "total_seconds": 73.030597792 + }, + { + "name": "ZkShareComputation/chunks", + "avg_seconds": 1.902716645, + "runs": 6, + "total_seconds": 11.416299874 + }, + { + "name": "ZkShareComputation/finalize", + "avg_seconds": 8.28821959, + "runs": 6, + "total_seconds": 49.729317542 }, { "name": "ZkShareEncryption", - "avg_seconds": 5.726563981, + "avg_seconds": 3.985343364, "runs": 24, - "total_seconds": 137.437535545 + "total_seconds": 95.648240752 }, { "name": "ZkThresholdShareDecryption", - "avg_seconds": 4.143115527, + "avg_seconds": 2.956951902, "runs": 3, - "total_seconds": 12.429346583 + "total_seconds": 8.870855707 }, { "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.070387291, + "avg_seconds": 0.093147319, "runs": 3, - "total_seconds": 0.211161875 + "total_seconds": 0.279441959 }, { "name": "ZkVerifyShareProofs", - "avg_seconds": 0.140556358, + "avg_seconds": 0.124994966, "runs": 5, - "total_seconds": 0.702781792 + "total_seconds": 0.624974833 } ], - "operation_timings_total_seconds": 1741.365023879, + "operation_timings_total_seconds": 1381.4912485, "operation_timings_metric": "tracked_job_wall", "phase_timings": [ { "label": "Starting trbfv actor test", - "seconds": 0E-9, + "seconds": 0e-9, "metric": "wall_clock" }, { "label": "Setup completed", - "seconds": 0.865109333, + "seconds": 0.848621875, "metric": "wall_clock" }, { "label": "Committee Setup Completed", - "seconds": 7.026051208, + "seconds": 7.025832667, "metric": "wall_clock" }, { "label": "Committee Finalization Complete", - "seconds": 0.002979125, + "seconds": 0.002882791, "metric": "wall_clock" }, { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", - "seconds": 189.944065000, + "seconds": 121.533931, "metric": "wall_clock" }, { "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 206.572293083, + "seconds": 149.002549083, "metric": "wall_clock" }, { "label": "E3Request -> PublicKeyAggregated", - "seconds": 207.078279167, + "seconds": 149.509563209, "metric": "wall_clock" }, { "label": "Application CT Gen", - "seconds": 0.013127167, + "seconds": 0.010494333, "metric": "wall_clock" }, { "label": "Running FHE Application", - "seconds": 0.000079333, + "seconds": 0.00006275, "metric": "wall_clock" }, { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", - "seconds": 54.503758000, + "seconds": 41.797659, "metric": "wall_clock" }, { "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 58.932710208, + "seconds": 44.993263542, "metric": "wall_clock" }, { "label": "Entire Test", - "seconds": 273.916770542, + "seconds": 202.389158958, "metric": "wall_clock" } ], "folded_artifacts": { "dkg_aggregator": { - "proof_hex": "0x000000000000000000000000000000c759cc9ae941af5038c8b97755896fb19600000000000000000000000000000000001c1a3722f951573b5a0295ec634a57000000000000000000000000000000ebebc77ee3eac819e175a341f2f1e7423a00000000000000000000000000000000001761a6553581bea2c3e412c770a83d000000000000000000000000000000bd4f363a701553cbf608568d2de3dd720000000000000000000000000000000000002caccef9014c2923ae4fc77262a79f000000000000000000000000000000322e862d9bba2f86d07a7b5175b02caa75000000000000000000000000000000000019ff9c34c1b63085570e6ccde257e9175c8a0a853c581e7df5985602b20f91e1c6cd45d5923748347fc716cf3a00e604e10e905ac9a9456efd0986dd72c6545353023cd2b9497be947f2f35c62e150000cbae4474670e3b176c4018bc342ce8e951e9ad6a5504ec706d5fd87446b2716e2a9c3ed627155bd5d76ab35901f2e5255bb5d8f85910200e2e91820ad22eb2db7f9f23a6d9a10de6861724a75b6fae6bfe2d01e1c2a18cf616a7747715a461765bd8985b12e9e52e890f2d5d541169fbdc61a29bece944b025719edd3fd6d163f35650c51e3e1eb0c279d971f4e8e6409671826352f4d34d24fe6a10610661c432195a7fcecb8e0b438b288f6302ffee477b505158b87040797ce4f4b4e7005977c4b9ad65a2b48b653643c9a7b4296e4a4c0e5c9db3351305fbcbd6cdbff2e48d28f62f21ffc9917691eb254eaf22c875e58b46daf87978f5d8eccab9cf80830b48af10f7d848945a9d5830c092cba925072a589c9bdcb68f164bfaa5ca60aeb1fac877e0070a79299567b9cdda6dcebe60dc44d1be617ee868ec9fa7e14115420a86c9f26f2d20f48850475cc4b97196277935199d6df233acc66a77b2d239d9837ce927fd18ba57f7c4764be438128dc93ef79b681f3a1290e6734d1a20829bfb6346497e7fd3e0ef0a31ec60db140deb1cbdba1167a2126df1730722d01dc514c81ae1cd51e95fc18ed7ecf9c56a565c7c87b1bb7ea978d45395f4b862f97c5007c5742636595b0e3a787675b9a0faf474a8a83d1c94da33206e3073d188fd1a1a636819328f52a05c0b59d357646d25815174ebf0ae7d012b2ab7640250f5061dc7793feeea12fe461e6366a978b6e79806150e773eeef78dafe10a614b131aca8f036b7f6deb336a51da9af0436a6f95caa0688cab256933131fa7409e73ac456bd47b679257a6ff01087d995410076713b594801cdbfb11a61d907149a4e3370d308872156fd3046ad167d9bc72d05887d0bc5983ba833e1a2485e1ee698e9512018aaa24023891b4f23fc39074acfe47ff31267a8659cae1081902e6d3d532eedaef05f9b61d520223781457c0032d4b0a5bdca621c49c7e3d98911a0cc9690f93771212cd15b6f012b016f3b5a12634447e24f47f415f26048310570d3926f9dd89c607e3105efe9954ee41c2fde40b7447dd58e251ef36da67d2a621e97f3f44c82bf05b1296092cebe5c4566abf559e632f603846a8c6d2bee2cfd52a8470cd5c290a66b4d22b9c30a616c04ada4389614d71ec06df3e39a0905629a96e6a91a59553d96aa8caf2418d8625162c7bea5c6228b01203459d57a19c1101fe9181bcb534cf5d625662f624d2743b10e50a3bfca925f9e1aaf0d7e2daf54e6c41a278ab88401e8b645f173bb15562239be1de98ebed36824681e6d2fda859d6329bf6dbe2a5fc574529007834f3e0ab6852c675fc92a320a3e91a01e715d1f01ff9d9621f5e9083a9946432110ae7ae1ade41e667bdb12ca4ff3340f1d13b06d7b56c799b1b14a8b02d86ab75eaaac7a4c0ecc72e360eabfcb74231b2bdf968ce8c9b1bc2d3e1cd01cfd75b3be2c547d63493439dc68a9dcd26a9611c698f25299df7be0383ce9938461bfc55b77985802383b80189e4306307bcc2531e4c0f06385c71997814c0bf06958c3bbfeb70ac933e786adb5c073eb881114746fbaf5f6c58d77f488f4af5b89a0d36d612d3a7e0ef0b9d4ec9a068fc4df08ffb7b65068ec9cbb9db4fa4a991e426c6cf7a78d85b139d74df4ff231878782745d5ec0d810ba7c5aff7261cba33d6e56b1df25fe9dbc9a77c8328b215e5dc067878a9aaa6954a8fed0e61da7592d5d2ab592208ed9d558dd486fa507b08f5056ea32aa0780507691b2c4c8d3d6ebe064b1fe33568ed71d2e5535b858b02b42fff93bbafb67aed7476217d93ab37f2ac4af525a772b52d77750be4625c440a0699b553facb40f8d8655b1bdb4e699e3c78a757af45faa9cdd3958859997b3b226a26176de19212ab88a28779118202e4b92a0d41ef5382087639c78181ddbc08e152b6eb762728082f19ea45fbfbf03d95282ffc166d46fd587917ee2f3f98256127e38ce5dde8ed2dc5ac3399ff1a6fe06e37e2eb22af166a7b43f90ffa670d2f40c1923d03d1f754e58f9c8dddee4a4c6f9a95f74115b688cdd31e17d21924da32c7ed8a2f1001163ab1181e6d2e74668aa6b64245ebdf8f4d11dcb01ec801ea0eb5b81f3067dd4df939f909924174e37291563c3d378b2cfb156b6e61d009532be9a0ba2d2f6695fccc0cebb9bfece2b8e64c42bb76f9adb2404761970f2ca979abc7bd5cc419ceea720cdc6e23e99003a4fa07038cb1e90cc0c49c8f8611d03e68f76273bdbba4d2464c92047f90c7be5b84847d421375c8a2ea8eaee013a7a6d6466ee5761f3bd67a9cde275a9fbd9c5b50d1edf9d4005f0f5cff488203cb0597e2ca59a86bc5f49d09a865eb8199561658a8008c64fc3524779f7f730ad302ef30349fcc9b62adcf392ead44757563ce2ed43bb92d1aebb0a9e03c8a07c8476d229f9b266e785c05d53e9b73254fe9b86f91e0ec53e9c7d58c10dc961437d6660646e18f3c443a58fb1739ead7832aa5716e1cc55567758260b230622bb96af1734a6ac2507fd32a660f097b251f42f949dba2f2642ff20d82c781831d20eccf81f8d3ac8bfcc2b9670c25bbe2fae7910c4999f92775fbee38ba1f062fba2f46eab74cf60c2c8ebf9a80aed866cca5e8818d0ac75b4e919cdbab8f7524d82783405a845a029584d51f25c71ece080011afda4f014c0e34d9cf7aae6f282c1fb743d6211caa8eb0e5364cb3ee879af9bd59a0914d6db401f38cd3e49a305fbb6de0e1d26881bc656993ee06067cc85811dbe2d7304719cc90c0792d9f273f310a1c6ec30aa4786c1616372f469adafa8cb642ece2078ee7542ac7483b1155cd3f69d2b080c84baa0be3c7f716996fe18f096aaa7677ab78a850807cd8057522bbca9290b0da94684336ccce567727780a5b3e44e645e6146bc6936ea80016339538da2188baaf589b9f04e497309af6e95b393d44ec52e6f4b850351e0d6a0e5ea4a4d0dc742704872c165ed96dc9756e146cf9af3d51e8b1122f2bdc289beae7844d461916c9639f79cc5ce03628f379b7b47882d89abbfc3a2b75710659278fe184e844f01e6b8ec9999cbcc0c2112212aba0543b0bd1a84e6ef85e28a2ed95cf561b96140ac12c1a04f4c6bcfb7ca8ddbe9b3602f6b14d4fc4cd3806fb768f2fa3045c59b79dbc66a70a59084ee7fcf905793e3baf8473cb6c82f224db7982176efede43f0b508954ad6f5186023c5c1cbd04689bd671a03c576ba179394606793005d3ab5bd2cd7ffbcf5189f555bc75d017e2ce579e1d5158ad11b5236e4a64929561277c9364a875f1188549e4454b681dd5c36bf6e651ab1e606957ded828d14f9ebc366e4a83a7bc1cf2a5d9da18cadd51ca9fd41e4a963bb13303a3b95149db5d339e833014077231525072b27e7a9f99f7fba9d74893817186a46dfc9382c3dee6d4b96765692bc8aadda2499b90881fe8c0f8db1ce745c0806ba962e92c265175151afe26f82c6fa1079430b7333947f8db354b606d41108f97398b93cdd0d5440c2967721427ab5f769b492f824eae2cb86642df2e0c02b210995564219432087deecc469eabcad8d53b68c72913b97195bf0f8a0fa4b299a1437085d87a988eb22142265f86dcfef64917e2bd6d4ed2bca0b28f4e15814ba6ee18934157ce4a2d021367afcd9bcab394067bb5fd5e1505e2c81245baa1f633a656cbc45e5d189d1a46b09c1e4e11807b5ceca6555f996602d27aecff519561fb9a830552b56712384d54be8d727a63b3507d5cca73488abd5da7871932533439655dbaa9d4c5334c5024b77b0ee25f547bb982a195342629b9c82e5fc1c9e023e0fd789102a92b9a71d09dd99161ac059fd2e3e1879f85b2ac341188d1e2a9c8f6dd0d2f6e556b5ce1e293a4e9a1621cd58403eb8b511aef70d96b7d808d0bd937de5e2b5e315a554e0b9dff8383d4efd199c655a3cf071a8dd0f37ce1cae0232e2e580ec70fe042ed0bf9e83a61da452364f65d9e79a51e75067c19312c26d49bcdd756df7f69642776dc082454e35948dca833162399174d8b104f6000016c3fa686b3a66c62cd7359a57181893bae64b79c083e773667b2cc1130910090e1f596042bf38eb0281b6c1552b8f51f84ead05efc56782e43c1d9d87500a95d8b848bc389fe7c6d87c339b77add806c26f14d22638748a61bb9a9eeb022ef9cf3bea64718fec066c36221779762876c02afe32558a9fdfe7cba4c739381baebbf2588f844eba3930a48f304d44db1618e84e10b8b49f6beee4e7820cd72f42d252c0cb5a30a84f184244f27882da77bdd8faa2ea30a10e18b7e1dd3e0c230d26015a91cd6715e25db1c96cb052cab75f7cc63f127d70ada6d985b56dc1047704c29ca22cfad3459f4626cc2bc6f18b80c02b4af9b662a13d223ebe80082fb19654c5b8769e49b29fade59af2f107dd82ebed8136fb83203c166a09fa6024c6dc33c48be2d4284bca08cae0fcb17b2fdb73087940d7dea14c6269f22a1914282cec8699544204a9f530c1cb83d7f7f57149f15c5ced3a5bc319d7a5fa3612c039eed45839e89fe8087578f61b6e57d680b69bb0529d67a198271911efc01d0e72b28451c757ed721fe770b45baf36aa5178df3863a7f3dd873857d681ec16322cac84631562e2bc0da187fe1c8297a833792c67af86630d0bd06e355d5b09c22b433e4fbdcd6374c73d34e9f766e8a6303794f64565076984ab6d7abef52c7361523d4bfb2280de609faac1783471a10256c47beab2553cb3c4c33898e627725d2674f4cb49877277e36cddd5165bb8003be4078a3a9f7517fc4901b01e2ea77fbe636915f60917c49c6a1e2b9a621ad8fd516825120439202e131f329f16d349fe78c6299782f6693490807be8f39e48ef17b3d68ee5e21adf4f61a17e132871af83e4b29ff2e5c8e3b59bbba2e7d8c86485c9f20073922548e0021dde0e89f31b4a1dde97ade10df8c4bd71fb200f87bdc1c39de5434e766627202efb2f804bbd9e5bc106a04249185970eae01cd72fb10c4601ff1925d9a2bf8b51b214128506e3747027750f97959324f700a69b36ed5dbd95daa5f223f093b58a721502839c69d8a7f95382e43a0feacc70a594ca65b7f7544590816fb7771c2d0f05e559f8d94fbd7fb7a555f7dc8264747d3526b739fcb4a993193504ef34774f22f5cfec7bf5378a4fea1b51f2862f34caf8545b68234e0b7e37d8c481760e13151b9d85496ad479812b968676fdd2100b534875aa88d2fb33df3c4c58744b0223621e3a669f667514ce179a06659f0a6e0940784655b16fb3f42036106e758f16abc4d29b9cb767e95ad8c9485e3a7e7be09c595617f4415258b2d2c585401703d73f4f24541fd6c452b3975e6b4bc8e3400a6c66acb09647e804aa7853196e1b7d3dbc88bc7adc2f59ecedafee3dc6b923b87ba173fe27cd086b95f4bcc5601ca9a786ddb4e25dfe9648f322d18308a393c082149427ddc4d4329531222c8129cb21139f18c946ef5b677399e2602472069f63c81fd2141dc472b2e6975832157bf2d61291028efa3e2a21ba8491620202054afe148aa88c0d95cbc12c2e3009342051fefc24bb90c64512a321e1de49e70d3ddee5f2d911d4ecc55ca287640077539459a07b6f7620153f564a80945000382df390ec2ef00f4a7bdbabf11818290ccf8e0448a34ce084a98786c9c797006827f273c70b828a45a08c4e436c01b61bdb3ae03e23c7fac42a9e37359c9b07c3d05a5b6674025edbc3cb7aec07043743952eb9d16dd927cb89e1e8e8c0250cf0304e06f85294feb53bb38ab7c52de8e24812322a30f10ae7b2018deca3abb410082bcf158916a0bc3e2bf7783d2c60f46c6f6b4d58612ba6a4234b33b4d8edeec6b470263f9782828ccbdd343f011deb47f4e681f1ff127efd11a1d0988abf496ed35a805dc43dd59dee4d97dc2a1033a75e8b3c59d27b760b3dcca7d5b6727b6c8f69b6a3f0e62bf04b4ff63c2adac9eb4ef1871f2f185f4cf43c5692fb33df91f2cc72c5344bc82b1a1951c502f9120c1901c57e2e82560146a761c70974ae50e12722a7f61c3f2bb25ced6b2b61dd8d279aec6121a6baa0815c3b99d15e34b56c839a095bb442b4797291b71c047251c00e59845ba2da715d271fc947160931eef48a245717cadb767a52cc281eec7635d27b9852fba5ab66b662b0951f552c96ffba05c8b5dea1696eadf7187b3ed16187fcbf0771f0763f087e893fcc4409b52487bce7c3c1cd43acc85528191d6894976a26d5f22e35f51f1f700d47089cb31d549bd38c6f3d2e17490108882096e0fb41948a8ee44ad8e53a98d93755cc1af4a71fb5634c6ca68f9b93038fb9cbfe722bdff8ec9c1dcd5af0692c5d55de5e8554a374b6715e2763dfa80e6f6ce95cf667c273d14cc782ef60840d4228de706dab6f02e17fdb33adf8c022a533e3bd128e495c7d94470588e198a2cfadb8137d5c0833d5e2206902c2fa0e17e2d9916495baddf57bd56911aa2b8d2e34cfb00d339151cfd7cca2c8e2360ea069ca354da044e99704aa7966e51918995f709f14ba06aaa55369e41a529801a1e969f34165ef5fcc6afa7be3e81c6127b14eab343bfb10396334cd7bd8ff2f263c869292a68d3b3f536bcde3fad616f373354bb71e9c3be094145fcc92ae2129d1d86a6ada118af3b2cfef4018050a195d3b6023e8dee36ec41cd3bd51cc19a5e1064032b019ef1842884b1f03a2cb32980834acd3e071b1683ad634bb820d221b114896761bc5543531d7b7bbdf1f49811f3713401f8633956f87305be2176d2b32d2d66e90f91d00eabe4241848633ac8e5976812a0629e356727f4fde22ab61960e61dd1fea2113ef9e69fd89e9aff6e788dace00d7acadd32a505e0e161ae6a21d443cd891009eeb872665d78f5c767078494a1b74f4110dd9c2a43e11e1518775728ef06699ed7cce5acd7da177c7b7c763e76177ed2c2535f066310d3754f86e7015fb57e07fde37353ae768e174793e8303b33d1006b4694cd26c170af15ad6d3debc2942865bd564c77fa64b394fcb74788a4cd20f99cbbfc68e29f3a0505f6ae69e49380c3c4d0bc0378550c9779e6790a438a79fb64c528e230b7bb13fdc511c3db733aedf97ec3ff96012108eca4d2ac47d7ca5e223c6439729148b956a062163e14583abc517e7122547cc1cd48475cbff014acc189e75c022e68dc348062b95f0c3a965aeebac0134f9654f6e1eabbd30b884e37e7eaf1500a93d125272bd9cbd0e28d8886d5f54c57feec8aa5bc45914d6c0bcafcc56e72402baf2b317446ff133dfad6fe40b43a154050e1554bd2c965a661c829017e60f80eed3ad2e084e20972f00a21f426ea554403a47267be9a09240017337b63e27d248d17d3aae35c36d91b71c9b76335b9d95ecf43418b33b9446e85981b7cc03c4672ad0d46cabe5606b021d75d4e2f10e0630c15d085172baf53c8260c147015c4e0fd1f0394e38c9760ce9c518277d1421096b5420d0d9f482ba639a5b0627117d187324006b59e4bae4a7f20b29cf661013c9458b5f1040dd1dbdd45fd72775dfbc9f2555a5e5b1d2b32faceafef1c6d3928dde9ef612069719d64c3c1311744cc013d0e906ba643e80ef12771c1ad1c9fea56147eaa5bf9a18548abb2b0bde62af5e97e9bb617a553acc02444ce0225df09199be1b9e606ccf41bc7f9c0876910e3bab81c0c9c06be401be5513b780e687216d4f69ddb943f75e1e5d1f26c97b4f70a4d7ee11b64fda33c1180c146225af5a165793cfe07bfed94e4d4325353fcc7b86a240b762b860fa359e4cf09cd4629fd6002223bb24d6feb869ed0259f5e538180c6cb2de659d28bc4f68f938c4ab06577f54e22e3c0785af3c8611b6702bd458c4c18c55aac0bcbf1d0ce7809afef216c4619fe8c943f270c7650427a5b9b9afb212fae766852904faa21577865a48f1403bfbb8ea1aac50ca6518dc739a15a0b4a56ad59d88b5dfe1f575123784bd076fba1729f3ac2f7e1fda0137387fb527bbb588f4a348429e98a9d4ff83fd26a6d497f15cf68f3c04866b26740d7c3cee92417ff39c60d93765ca5fa6c3253e647cca67b10d48fd98acd309e360e95dd91c1b289c4ec43f4846196ed36b18f5a6ddfce46b358cb6d3d15b0aa10590cad06daafe5738ac4ef1f19dec98c3c776a6b5dc2feebf49134e800427273e4272ea3f609f3db92446df60fde2c3b97c6182b8ce3fc70f3f1c5ea1c913944a724bf5c1eea1758c60b4d8e65972d80859d897e2717f0b408dbd01ba7115b449c6c18cc2d1dc71887249c52a5a61889fcd96e1d00a92a6f33ceadeae141072c84df6c67fa4bd35530aff5e5054f1cfba3d529563c2e5af0007af7261680c74305830a823c25ab8eda632f5cd905f0e39de50da8a97b465538f679ce0d312bf96b846402482cd2aab0207eab96de39c815ae7ef43b34368c5a9e53e20f62b2d42b6b44f39a5b8297739f63601ad8cbe8320b25096b5d971bddcbe8a3a4522c5269a5499984d9a7643633d91c466bc1c0d08476da96e46cf64d20cc3c755046d980cdc172c4e20ddad7f34fb278cd2f20e11f8de085f5b082aaa464f0d79240cc939b42728884f938f5a00767e119d4ec9cfb2d72db9f408784be32dd14609325c1260f70eacf3168c470416bd1add0b95b46408127114ef8a379529c43704831cb1f563e7dfc5021f9f6ab2a392b692b22bd5efa6a250efa327b6fe54a21f53426f41098c61d9a312aeef2d50322a67e0705c975803f2d1e19e1f227d63054c0b7d389615b62be4b2143df9e227b3b992004450572c4061dbbac17916ec023cfd919c6637c192791c43cfa2bd117c4422529ca40c846bb6b721eb2e97d9266a6c835bb266ed12f5ff261d42e7b69be6e09a04ca373b59412b9b299821d90668f8b7844f1c832fb796ccec3a22f7c021b9d91300a8a0545bfe3336ce3b9d12f434ae016820db0efa0606dbde578371d9547bac26871da965df77546585ae18cfd9e924c8760087da3b169280d22fc4fb0d5a20164cbd66fe690128c996b411360f7a8f72d6516c73e2a5c1c9716d601c19995fd150f1fe8371248cecd391302698f7b58cc6804ac7b44f89fa4b9fc5c9b24e1a121767031cd8ccb748db4c0e697d7d7392798311b37b50866a3b7d3d3101ef0cdca74e5dd2176e2255c196213fef8aa4547e3688c703a9323af3597392f466c7da6c2807be00201e3b09fa1ffac45e4f03b2c9e734ac5885ecb4fa16a67378a353389c4a39418de787133a22024ff842cbc7653c15ab0b46971c4b5e1387073bda10dde0370890885b616c106f45f485fbae119c4ac08d02333f2c6a8b594322f5f03647b001a17b224d5f0c64f295539bac409493a3d1c82b3f14c5218ef6416e155ad518992b5683440d2817185a6ed36f36ecf47968ae5b6d72a38848794181dfc6d9b5ec3b49863c321c9ccac54f01cc4a9c2b3907c2618469d3e6fe8b407eabe57cbdf9eaaffdebce16d1b551439f1ad8561eff7eeb2d5bc4d08a914406a6279c3c72717c71dea06d1193c6fb73b1f962685288bdeddc04638596de80a18198f44c520e83ccfa2a8507b870a82ac4bb7273990895fe1d9ddb84c7059c106d7b183d17201a79443f9b1a933486880cf1535e2d738a63d203defe2120e93ba685b946ca132101ffbd061cbe1e151bafe989035b7424abefc57608ee22c29301802344149751e7af0cf10df41f7ef566fac5aa02c5692c2a53ed72f8423cccdebff3f24ab6dd1cd6c7012e612725c3dc34c8b15110392de34db8c0c0c07a22eb983746f2a823eea3e05322f340b866bd3052f3d426ef3d9213920714bac789fcc379c7daedfe5382011a25d31ee05471387717daaf277ed08d0c3a058bd8eb5b94e944842c05ad596a8d03189f1e59e39e8cf0e81f321ee83a511ec2813596fcd0408fa932b36331ef650b95dc32f67b1c352c13dbbf6eed9dc7d14688d36f040b9e88f294d30ea0e0520167c66d018a95285e9cdcb84fb779c7ea985d16edc4521dd73b9ce27fd5680c0f67ce8a93ff555326ce409c4aa4b865574a48895071f506566c85ebb87d3eb62c276feba915c5a659ab516c4580a8a55f90139b5750127bb969ce28799b173d29aa70c6ae4774974ae60bd2da6d3017b0a2e0e4f130c7b8f68d197b1b24cd0c13678f30fed1f49b47ffe2054721cf2f8339006eb4febfc1eaff7cf1c7b1853e2ad3e911f4060b2e074a4b6753bc5c7a1f19554ee06600be6e9e91818cad711d21309d1176b7f2c97f007a08aefa8e7b4d3250e8ac0960900246ceacf99942990d56c59c721b2309bdf354272aa6555ec8707a655935fca59c2635bcebda2af22689d41b7905bd64fac9ca8fca4093888dfce5e9ee121e0288c63d13dd489a342b2ae1d5339d29a32aae0fdb3e565b91aa6f4db2379f0f50c3ab03d39d1ec3d00000000000000000000000000000000000000000000000000000000000000000261282c276ddd3ba00f19704d16881bfd2c6cc2d2e2491fb1b3b0f5c706f36fc15f9f7edae044bb3b268c8978fea31c350b9fc5e7f44644345b9c2a095b756b30debade73053ee9f7d4c5c4b9e90cbce76c25236c7fbdaf92f77d1bf852a2405024249cf915a06031e094c8598814ee69bd5ad3d601427ca887bf16745c1a4000afcc74b0c72ec4d6ee0e7f398b98a1f3538e83b373657dfa08a69fa3727ef841064c74325ff3da5bca1b1f7199e741bad357df2729eab95be1b0762d33594ce0ea50106b3a5ff8fbc411d1ecd33b30bf21910cfeadaff986629c7b844de3dea2aaed28886ce5d4b3a1db9465ca4740ba8e60633b5e2064d044d0981283d99fa13a0c64e3cf459b13066e3848eaa6bf30087f12f11755a22bbb98c5fbc8ede4c06813611edb24aff3688c03e41550557725fe6c922f74fbe4dc98d72126205e71d3e9182c955c7554db86552ea52c6b6c5c5e0f4108a14b28d90f029ece478c81f73a3aff25fb738c2831d8f689c1b2a68b58618a82621630d066afd7c8fbf5122e7086ff042034f042df58a835692874b31c0c408f3e17d71dee33f9ff85d0f277b4333773e83c2ff68ba3a58194752d1530f8db8f75edd7caf01f0792aaf6f2f2b551333c3a7b7e2db8acde56ac5a66288439fe8d53a8c533bc45256f3b1751c8790459323d0e5774d3bcc44a239badadfcbf754d96b8b984b66271c86625c19139146649a6b7ee207ae93e90093c3df5efd8bc8b715610469914474e70f1d2e9677f073c69c7be83b853d168c270a05b5471c6d05eced76a1de69e16cfab72ad169c91ce9f00929e79a8678e829af2e2d77e825d809af30315c81865f0c0129183d54200dc52ba05f69beb4cfc3b573cae01f7f22e4ec70c95d72fc9b513f032dac7e3b5e94d40928c1d9d98babd68065a9f3c518e3fcb69d1f06fdecea7c26b5180407731f2e99307ba131ca01ee0e3798f7b9f1defea51cc50709aeda4525b06dacb8a2478faa8cd16af0f5ff8f413fcfacf8081eac3647a12c932a371f03d6e67a3ed918e3ed48f38ccfe975bb2eb41cd168bea677255a367b3f4dc84d25f2a7aca85eec91f8fe0f7e19bb597cda0f658a4c8effe478d0917438545dec16b8c905656ca8ee45fdc9d6d925e8bd4f327277c7525e8dbed41acca178c3211da131f846f3a1cb5d8d9a5eb802865b4477815515291094fc5352367f6401781dca6822cd3aa9e8d7d258284d1723fdb50a0b2defcab75504df6691dcbac9f22e2dab865c272425cc0fb73390db4a52950ac2339244494559cbe1f17b4bd6ca184db16cc912c3aabb5174c11082faa8699d4704c1d1cd0de780fe7a1b9f60922d7f25f27f9ef71d7132e99b53b029dfbc4930d7272d22318ea9aca18a3af0d61119a2efe76dd5651c4692a8479e2d036f605b69ec03c653d461191cb43a5923088d1bda9e19d6fdb77d65430ebcd3decd8525f8c932f905037a2dc970f1c901273a4b8ab65be2bdb514fad758e80f7d1d753bee3018fdc9e010dde8829c82f72f62d252adafd58a8f5f626309c6c15fba5973d6059f292fbf53258b649e3b6e079e5cd7a73d5cd303a5a95a12a5827546a962769e4005b1a66bdd4bd8534b8515159b5b9e1bfd4b4fcff2c7779807e6b6424dfedc5d50ab2756c6994fd103701e19886f7075281d6839b4141694e4692cc6a2d8d04447f54995944c3dbb23040d76954908c955ccb3e789f2a2a3d5ab957883af153ceacfbe04c97bf328c39220734394db28db4ad1143f5fd052af09aee51f2a36149223d2b994e535740bb620a31537e8f781a80c96166a577fb3669856a3795dc3ba328f909027cde2dbc01071a1bdc68d88e6605a5c4e2feb96ca7ad5e5faaf94fe4e4b54515bc4d42f13200d282ff4743184ac58d7bf56de98ebdc19be21e21635ff824c556d30119cff25e3a23738a406232cfd2738cd735d9f79c7b1aab77af66a2446dc12f95d64bd2c2977dddea52d2f3ab3deccc07c3791ac3d8190f1f2bbcf1f0976dce68b292f08a020e73ec0e5ed3fe2a301fb88b4df014c41e083f596cfc41852ef2b2f026c2e622275d2f8a63bf006606005edbbd704af36d38df9329f617640bcc65eb05614a5ef6d424902ad5e216fc5d275f993e4597314313e5fa4f97e606c7aa2ec072dbaa37dbecf991133bc0cc77b0edd7e4ee24367a4e910be08355da277b9c2c1283e7e07d7de85765f2c6453e2c72a90e650c741664d47d5e08959cd9229c0701550d87fb9eaf31f7b01dbc21ca0ee938cc23b3922a620e53da029c1048637691389ff9dfe87fda2803221deebac973d42b1946d838000e850f3531f4f24704f2954660b51ba732394ef19155986a1980e3a48c7ea32585243f855913146bc2903752ff3907f3186688a941365dc09901385104e38e91d1aeaebebf20f2cb467019454e86935cda6432105af49aefdd31f1c7781406f79e0efc697eed800bf980910029430aea827a60dcf1f1c8761def92555a1bc008d6e910a0895696632cc2b54b5bbc0a4a23b07554ac7871ad53a4e2cef65bc17c4fefcff37581fe142f807150248372169b521b0064c208f3abc3be6c95246bb6f04237785fab97719400ae9f8947d50ae7679096a1a2a59c666df7041a0f8f8672610fa4d9bd8bb03e409b746160ae855681046c0f5e8f2db33d26b159621de58886583f561dafc18780aa73acb6d03c39e4cda1d0e996b3d471a8042bd79c097524e09b1b91c06f59925f6f1596ec051dbcd1f4419836b33d011712eefe8bd2f9a0850b3a2d1d1262c1c18ccdd75913b4f7cc20f2c3f321cb2d1cd44fa44f590e745db27e9b6ecbd202ca32319c125f7f975da8eb6d999cb8d6061d9eb4e63e43c0a3088ab9da910940795fbc18e0071334b7a7f04eb4c218d92c2794706472f7f590a8070d987db610ceef1f0a67d5014029f1143541dda731673bc68d436023eecdc57d88c6601760a905ef363c3a119d83cdc92f82f7f643f29dc328ecc4211cd779962fb430f411903ad6e25f96b874230272e5d22c3902750798f9d604ecef1c848208f68bdfd10930227bcc255459e32b643dc9a9b6a4e9591b6581b7de69447748649cf35e20585cb1744018cf6c19066f137147acdea05a54a517ba75f936f2ce91fc6427c0ee67371a18021d9ac1ee7bdfea83068bbef7fe5e44612a2e8b2c873a53bc8f61205839e3b6239f4a0b934c417df361cee751fd98915980c43b8bce48a535a540232fd1496e9b4acc7ef2992e01b43a5e26cc1b2817f4c91cc83cca9d665f66508ddaf37f0c501e5e16b5e8d59cfa2e1c0291b8cb7fd540e5c85ae9074636b082dcaa633c48566a4215505923f7ad8d98e8f9a0c3bdfd034f09cd3a2d0f6659b1d31e031471d44517f4e06a8ea1c7d25892bfee9576b47a252fb31e55ad08f761c6a1c37fb4c7dfb1ba6263b2353492e87017eb4f8bc7b776bf0b3eedf12924025cbcb79d0e926dec98781067a847c4222004555db8ccf632b859f5acdcf54c01329df2367949272ff02089fc02e4359f14b4da7c02043ecdc6dcbcf6bc8255101b1640b10c3fcbe83f2022c701fbe82cc511b827ec6abaeadcb6643a68c20d4123ffb4ca646155506870850d2aedb9c4d32a037eeb5eaf28fb0c262e3f76a1a2aa4f6e7980c793d60dff0a90539aa94c777a7e9e8f28417bad16349d2161f5712fd3cdcd4fd2a09f72a0a1a9fe3912db808d98388c9ecfc6685be463ab0e14c2142f049df4f1628fa31cad4e7522021488cea8051925133aa476860d45711b6082146f4cc8a149ba9b47289aa42b207eefe0850e3244ac801320071cdd6e3cc285cddf5e04fb7a28a2b7a35f60c0ddba1ea782350f1900096001c96ecee95001f1c47a8af1ef6f61aa0e6c77344b206c6c86841958b9453801b4b6eb1282ecd06509df36bd55ea8bdf91d7bbd8746001d9e48bf9a9009c51306acd99a3d1fac25dd02cc4176dbf6074588416b222e2bbbabf68c0328b90dd79723c632eb059e1b123e32f9b91ebd49d0a48338b55f3bb9250dae6b81796232094ba6e5c3b3bb2d0d138dff99858aefcd966707a8e5f813227225e267b47d633dcf9e4a654b91", - "public_inputs_hex": "0x224ce3d9bca060eeade7369297dcc3b23ac24e688fb6d20b500077dca6b9e3532f2dd8535a4787ea8794523842a3a4f45cd84bc85749861fb660801b495b32700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000ed885dc0873d41b86e03b401da548aae000000000000000000000000000000008ae9fa5f57d2c0195f1138fd669fe8352a4f0c291b36b9970cead06bcf01559b0d2f7b3296daeaafc5edc881f8174d620a9dc790b726d4b30e4d8865f33f50004a47a309fa92e169d5ad235f41eb12542c77628973a693c21303168ba71bde796957f944c3a3494bbdb558527b5ba0c6071f98da6c5d2594c5ec88e9d1b4b90759aa7be42eea0aae2489f0b863dfba9b1cfb95c79fb965b14ddeae2ac0ae58e6f2b8af40f21222f9a964c0e33eafa2510fd2e0c74ac08efb87cf1059f1e30dcc9468113293de905e1b6098606e2dbcc3" + "proof_hex": "0x00000000000000000000000000000033c428c02938ae5db473a3df5b57e14ca000000000000000000000000000000000002e8499250d826b93107b5171b2467200000000000000000000000000000037844a699088bf8bf6ca7a1aefda2c000c00000000000000000000000000000000002816ca1b3c2edd4f2d7fc2be82cf530000000000000000000000000000004224b32d8b419c45019cc4b880d6d941c9000000000000000000000000000000000017d26c752552e1c6ae00c61b2eff940000000000000000000000000000000bfdae1a26be8bbed5fc87f9847d7b13be000000000000000000000000000000000025207c73badaf8370d6e599333218c1409f5e30ff5f4c912106cc4c9ce538df24f8bc749898d21f5a9a08642c32452085940f7bbf660902666053a9958d5165f2e2fce1ed04e965d61a33608fe78301dd35ee04b50cb3205c4c82d32cf32c2958122736d033a7b988dcf66a062a1bf2e3671e5379e3a392fa932d9df2f0a73060cc4d509c857a7619b28a15ff5c6ee03b0644403f490d1b87fc60e9543db645b6ad58c03aed39364a9ed52f85b3aba050e5da3cf2b251ab7fb0f4e239764303c4c9bad6edd358c817166afebdbd8211161cd6cd62bfda4a0b44df725bbcd2f5ce40b2f430d4194a472b401ad23288f0c4ff969fcabeefd60346e654f290e4226331c7cfbc26d71768d5016191d243e217fce0fc29c8ec93fb45023558d87cc63e19f31bae89b79f217bd8caa8d7ca408849463e0fa31f97dd9f7665959ed5f39bb091b9486cea26b1b9b4c63c43fb91534cb89e88e6c1a14852bef469fccd9ed142e22b4f6b50ff3d7a2251816765400da286a7f17558f24bb627f412edff56ec43330d56247127992e6b7d14776022b2f22d58dbdfef29a0bca13dae435f063b1c3ed1ad0b9a744f7f813e8d7e7552b713d4c6fead2035ccc1f7ccf4fb17b7db2b1215fcabef1e750aaff8e83e4dd074bd02e57bc769db069688d8f1543091193a0adc68d81e5249068d64c77e0f22cc3b2c4fa0064b20add822a11927211aa9fa06a3a8a24c78737d94d3fcdbef62cbc068db1090c69ea26149f69d7b2c57fa6e56fb17ce3db82a97d7eb23de2d81e0ffa951578bb37b636148ebf6b5791bfba83bcad3056a5a68e20ea6acbe51e23ab1278565a9f5fc1b91d1cad76c23eb989831c194caf9368ca48bd1a68b678136b7c6c01a64eda91024a3229e919dcc83e0df3b735d4ad2f364fa37ff659282b505f99a7f2ac245ea4f8a1177247accf7d0e64cb4db70ad21cc21411afde32273c2b3dc613e6d4b4c1b4b57e1de10efa799bae5bd0b3de6131c545db6ca37a291ac4246ad440086f3697448cb9ef04b225d40291a465c44200956fda50ee050649a3bc60671dc461cd866990131a00e34ddc1754d586910af92bb48cdca487163029b930ffbe3fc929aeef5db3623e37a545dccc14b6bf079ad8bfeb3b708b26b57334a6bfb076ab8c092742a5af9ce0df8155e7f864ab50318b9b8daa084807226c953c3e1506ca59f3db2f432d19dee35dd2bcd4e0906d3b6988d10e019a28ca6602578ebb7b98c0be4b4464cdd9f80c8426b0c82b6c5aa0f08085a199f91862ce65d8b1c54e8a5cd0a9c4dbad32f2427e57df2a6c42760b77a2d014a9cf26648df6cdc920224620b7b4a9ddb676a9c205369f29bfe251949dede157ae140cfe8a0866d467bcb5dd4ff8f683cb49fea0c32379f148ba3a81adb64c29241d2a9a5f2270cdcd9f40d6f9c1cda312a8126314834fcddf5db49272f55e97eff929157fa99135f6e21d99382ecafb1968bc7119d95e75c49b3ebd8cd10176a97925ee5ea80d77c67f9db5340142250a85d8f167221e67582ea6697d73c1606066124265c89d626822e6be69b4d9a5173b028f0aa7152fe8cf3256dbea4d36a0fe2dadedd2ddd18a6b2a15fd9ab5ffbf12daa40c44eb51f5ca4023dc867959f59e0f6be1e9e4fb8e428b80cc366680c49c11bfe4d4063d0c69508cc02916274ce31052ae27c6bbd996e9acb67fa5e64936a43c470bd98c310015041f96db6c0c9b1c438fa396653cb4f77828e8c2e4caa0dd0585ce27553ea6db6b97d76370f9de00981196b56d1f23505ac467203c301b1f610fb1fc31cf4ab1be986904fddc571a3274209958bf6f1aded85226e96a839aadab6534a9df3653d754431a67f4dd103e3ff89134c56d4ba2910c64459c6570b66e940dc9fc6d2fb8e6ef081c0e471dc5dea20dc12db43dbda342c98048388a58c77a4824e1f5e1d66adf134c2d1126c865a96db0e2731ac805981757d267bd1b524b7c6cb9082158432a3e93461d0205342c30950c0d7eccac5681396242b299acd47ab5b84196e59d06365e3b740d47efb08c0bb21d415f448f3e608e8a4ff8b49c423185b259a5ee1eec6f73303007337c0250efe535f7cde74bfdf719566bce355d8f44840d09e9b39a8f16111c173bafcf0d81fc6eddb26e8de9245bcebee9f65d947bde686f1ea666bf06d110c3ecfeaab5ead648d48c606fea097bcfd7ebbea69c77e411175d7fcd36a0291f30d3eb3e700f5ae4166df0ec1b1757a9aa8de71aa0ae3577185f1c86b4ff4105ab34864302c93e13e84bceeefa0043ad86803972712f0104cad27b27ccfe1f0088bff107bdf970853469b2855762a44a26f7742ec0265e4eec66ae00f4632c16f302a9ee983e724b6ec94b8d73ad1c8fb82ce7ed696d5b69006692e34f3417190973d17d869be93014216c56e8250537af35ba0b1329554e005cdde2222bf10e719c1a231acad72329e505bb310b70e8ea9e885bb521968492bf66879506f71163f945630e4f9aeec1a061161b75e4b0fc804edb06fd2d2602c4eb3a5f5db804769268d1a533bed54184987e0a0f334508988683092ffc1b98080be3d37fc90c8a1f9c0ad9af7d9f7fdb7d1817021b913e56abc4046281e9c9d03ced0bff5009b6d8c474a7fd30e0eba834e71fa2b2c210aec09adcd5f073f77652ac9f865f2fe634d4f20180fb3571cd5da28341910728491a250f7cd4d244d8c4bb6b92991ac2730381bc9f312586739c3c3535117d0367b9d3e27215229ce2e2641a32d802f7246163780445ffd353739f7fb86a55e209048fbc3c5827aea6271a739e09146633f6e895bc4c9c57ae2ec967a624228c30b73ea6ea4dc6bbb09b1de48bd619e8e1ac76dc5873c86219beece17c917a1099574bab88da51390295e51f5d2e2698458facbdcf784f10ce1e2bc67cfee96b1991e217957d1dd5ead66a27e16a265595699fdb026a741eacb6cdc356faeb3d57fff966735f99c1701bba0b9ec8042d0eb9797a620412759075f4bbff25062112e9c8ca14ed78467ca14aa7180816af70028f1e87d15b92780d109e87568ac9528df2099491386f1b88490c038f0d0895c5d3881ad1e7bf69cffa827e4aba430b66d569bfea6f63bc64325bf2231408560ec4643f31f232fbbd165acb86b5156a86c77219fb7723fa662d897b9b03ff00bdb234f155d4058db9b3eeb822d0f534ca350bed3dd2fa069a3b9d799d1e51944bd92ad35238088a0517b88ab30623797419b1e2ce73fff2205812e8db1d54225c9c989c13e9f366e918857715d3f12520cfcc1c40faa5c41c4b4740851ae7b395f562792ddfee0edff2e693ab513706d79331d7bfd34a727efec2002b202b3ce19e1451b961c8d0179cbec41fd61444006dcfe37f1c650ce8bca4a80225e64cb2b96fad74d2c81b43dbf5d3d395d6e3d38fa28eb8039e5976f94950152c2aa5522c268acfefa2e8d66c875510fb855ad2602c2040988a16abe1b8218425c955728069b350b98a539df5966be8470ae1c0d879ca9065726cef5fee57d20487fb1d70a9f942a5c5aa7e76fcd388e92d999fd03b4b08f3893b67531b46f705379c6d377324636fc9b23c338588121e39b1822932ea92f7d9edf04443615f1c873fda49b97a39a92525c3cf4ba158e9ec3c3eeb62203da724c3aac7dccf8b0c2974ab4739ae907bb1a37aff8b5ba600e54a20625f2ff5e9604687435136cd1818ef8b5c392f06b8e0e6e99514a78e11c3bca4a62541c330a3db6676acd83d1c4394ab7bbce3b0b9508ea04f1cb7f8b5d336c083dfd4844002fbd33295d5db0b68f6eaf9cffa8a2b9b933a1754f4b76e622ff637ac80650cd46a50e4638dfc26eae73e163ede4dc4ef979c2b54b3dabad6fb4574763e5252013bea28ad356d01e684638709d113b09c9d89688c6312694fb07a47ab162c8002420149a98d93091f171b48087036d446c9bc00d2945a528966c8dee4323b959e5213f4a097f01c24c82ad1137eae3de1ab4a61f3417cf03e9de1e4fbab28d23f3859478ea5bb090bcd80b6ba4e2fff8fdbd6b12df07c2ad74ab25dd13c9e4a566adac8f5e26609bebc1e14878800762088ffb0bf4209f703dc7a08486b605e08281a1d84f5d021d7cba2981eaeda77dfb5fa1693254ea56f5bdce5787b8fc5fb64bc3597bd3a0c4ae4c5e3bc433251e6a8574dba6fab74382708a1d970db82f1f3fd0739879c18b483ee2d4e43598b64eef711f6a02269bc6c44fe20754133c9122bc0785d312827cce742e3581174ada0f5f925125f10b9388dcd461a3aaf9a03d7e14bf80f03381f52c964c01ed5a492914611aa4f57598bd2095215ed085ff7fd4336c6ad0f4f9e09105b27565026b99cd904d75b3e1ef6238302ba32997eb22fc41d1fa10b671724acddb8f247c6e78253c41418d337e988eb5a79331bb957c1a1b90be72c185e9243b854413e80090fd400547857f33eed4236812efc3df6785a8148272ab028f90c013d4dbde3e9d9c2e41d09a5405e90a5008423b3f2731b07a2eeca04ec5309832279f8b4de85c0f021a8327298a20922cac216f3794ae8780a19f72ad7382851349f27654789bacff9f67af87363e20cd8b4a60a098a1acebd7d1b143b952750e1d3b97a69abc1e87fdd3880a3d54434b1012d543d4614bb23cf860c74251f22f3b64916ef26e15ce24ca38d797ffecce87f54e441caaa6b5b5b6301847a28ec1efd7dab8bc30a7f643c6dc15aaf70f8278481a407394a960d3f0c0621e39d9ff05fb36fe4464c61968ad9a675b20f86cf2af8e791151983bf5a9406ec1edcd2b7be7492e3ac10efd70dedef90004bfa55f61a53879e97080c699a074731a4dfce7488a15537399075df4f9e76483fd827cf84aab3c52cb4af9ca302301a237ece1ed67a3cc5f9e950c6ced6f4abf27479ebbbe1e85956b0247b9d1075578d839c921d4e68dc275b3935873e4215e614b77356aecf793b0562441a036adb424f6f1c7cff3004c7ef3bc97a9fc416da86ce3531cc6ba3c7aee830d00386c604cd0f3011fab5366cfb4a0d7f7d24bec867af5ed07a73c017c797199c2874360d4ca5b5342eb20928cf9c64e08c2e4f86c60b5ae5a69e3d5e3aee7cf2270fd8062e97f2934fa0cb5ff62b2700a28f3740bb02087adda4e9b35cafe66b1d2c921e9065643f8e3c0bd192b2cff240e35b7a8db195ea4c3d30ef9570db7e2e94124a2cc433934c084b294e4d008bf0a8b5141950c8f54f7644140a2dfdb7036c5e1ac0e65e75bc69e493d675ff640f5b5971f71ac06df6334a87272f12310576741b19dd1238d58f9f0b4a29b9c9e0df1c8727aaa01217f308d4bb32e11b263a96beaf90146e79ff3ff7a9b849e9132cc734533a6ee50cd5bcc091c87ca7145aa11c6be8f1494d71c0bf23eda14ebf9bc1dc2cb62c5dc2aa56976d7c94a629bc417e4b22414433f31e41c0b4a9905a4b519b9ae53dbba4237d09cafd3ac620386e1f7ed8bae91f40bed11ffffc0197a959616500fe5fb5af513ed152ac7a2a704ad043bf1a50831d9885ac372b7175a711cad7c8c597054a78c0188d0271118f570874147e146e5b1b9c85fd1c172157a3a58318f5fc901236afce6a3fe4042229b8080a3bba6479fefe51adac026daccf5fc145fe6a37c358c22d7fcfde05844189a5ffd5b64a5a280e2baf779281c130dda5a18c4736c84c08b41a3aaa166cb4926599033844ca65d324834107af8d8dac19a4a86f35cf03beffd8397f1851d2df94ba497ba154b0e107fe13827b5ac01038b343238ce602b71141ada1279a366e4e36a657ebbb663c1ec32caa59de305d31855e1087ea0ed2a75c4c9b05c3578d25d454a1355d0e1f23c44e99840bf82e5926f26478c11c5c39eb513e0ab532b1ad18a321b60d7872bd7b1f22af018a052ebe79c47abdeb6785983b2a13562b08d9b56810436edb5b5d1668547820e41e22d8f2b105cd1536be4590310afb9dee074d4194b9ba0b65eadfc3c2ce78e713dac1c42be17506070157ea2c14e50592d608370e973f2d847c804f61c87627c34cf59f96f4df678f164d82c720eb2c51931b6fabbbe6c742368bab6f7f55f16c149f848c3ff076b0cca9173b13a2a145d3f52923109e484fe8313827af8d90971feb4bdecc10667f50c5dc74302d7bc56b5618b7ef1bbf36890931efe48ab106378cf071a19ec400334ea61b2cd11d9f27416ba78d7d48e39d5fb9fa76869ff3abfce3c4c9d7615bea5b223108d6cdccc5a194414d97828b003464a5a360778874cb1862090d900f9562053b1df1851fed5bf3d6fe81dbf82605064161c48352f4ef77e638d0eaa9333fd4011f243d6d0efcf0b28104c3aef40623ad08e66a0b78858afacdf9970ceed1f58f3032e7691c5a8c96a88ecd2c55ec37379f3612f2e9b1fcb23ea49abd406c7fd107fdad64b17eebb6eadc1e2c5604e7fdfb117c66c9a81c0724013a3dae0de58923fa3f044ac45820b2bcf41b7b23adc3c5cde82cfa6fa4b6600272a62db311c31b65dcb45eaf9187fd1162802734fe3695e9ae81cb4238fff03b889fe03315bf0ca90e98b1b034f802327319ac71e38699ab34b640811173e1dc48e591f1281908a5a7cb74d7d466fd5b6591ca83e6e3bda7e114d0fb16c891070f7c1ac57a650db94a15f441115fbf18444a8335a72663bc742a6ac92ce9b454e0d6dba4bcc60894fac7d616f193e046ecbe63c9572bf42c9d4fae558b0eac3ddb117beb77fb00a241445462a5badf2ef47567d597af1015d2ef11e5eb96a79ee8b49868a3be1d66a8c07c4b3bc8d0427fae85130768d0b0bfe37a733eb7957f2482c66bb148230757c601d49db46fcd43b6d9b6b4f8b666e89ea06a1225027cc1d4b2846b1406cb6d9f65cd6e1b7e61ffee2597b8f731dc0c459f553558c2e966f558f65f89003149c31d154de6b7e2bdd960ed9939995c4fbf2abc8c62477bc836eb7093db0bfcb84ced80c1b8f11d2a58aae10ff44b64c0f879c92b91ab002276220c1294281e6c1a0d510251e5d83c3723c6ae5384c28a7411ebb6dfb8ed311e384fe0d62db9d1b14d4b9c97948041d7ea60e7534ed3eb320b720b12d9b0494473ed89c40431a12f9fcf91d60175037f765b9616fa940cd082e59f001ca5ba9a7d28dcb913302dbb38d60e0b68e1ba353db1258be6744b54866bb0abd429b844b23dc73316ddf20ec57ea3035e9e6f2e604287e35e35c26e6eca8afccbe1ea541293823426451ac08897c831a99db0eeb550cb4c053b17f629bb509833bc60e118c66864050e07eed2c2d71cda38b6c1977c85a4b5a8b42b7e5f9cc755fe76a5ed8294ee1052d9ee8e095edf8f8941d5bd256a9c544a475ebaad8cc3067ce5523880b2910f98b398aa71dfbfd149ce00ec03ffcb47cbf768fd26de0b0f1b16ed37bf896e0b365693cadc883d460d44ce74f1106509ac6b305f11fa262d8966143ca6c7fb0cacaa75429acf16ad50050602f4b2cfb743891d2e79b56d44ffc6e849e90a591b524da12a0d1a71b3642e4c6206c9354bf87a157644695b84dd820f98b8805e2029d7c21e588c089e9b92f49cb2eea86dccc047a115ff6681cc50f7abfdc87c108a3cb16490b6c5da2953ff5af46bfbad95b3bd37335f2246af6fde5bfd94941b682567213a67cff1ca664f048f16e0c1aeef4c83da219bfffdc740b44a8b4b006cd4d364e365ad999d36eac8948f0c1dde2aee7d5097496a6878b59092fa16168e5b27f8d7941c7bb1133a0ea962c855812b63687f821460ea7617ceee931a156215fe35fd1f6b40e1c36691b692118074d7d34f39cfb97a257acb4edb66ba082efccb103ef01e316034bfb8b6c1968d83ea12a1d0e4936d732f24f4461a7c227880e5f939f19e4d8203ae380fb7a1fc57c75b505adeeb93fffd1b6ba931bb23f09d59bb2d33111c34c573f857a47c6c92e4bbe08ee1d29c96dd54ce331c5b29df4861b2d01bc2dc19f0c4cbf1829c3381700b80fd5e6d90fec1359fa4d05a20e2bc24fccf833f6d3e68933c167549d5588b5014f8c84fd9b245a44065fb69077a5347d9c8280112b62ceebd42dff4863e02b140db4af00d2335f9242ca3900bd6b59e4c76908e8b0bb43c750beb856667eefb767622ca5cd969b42c4beef50c360fac77d20fd9a4947a45f3b450fe44d15c6f6f464af91d682f6f0f7dadd10da0a93eed318fd56d04ee97c87c56683b11e5472abf70e0720e30d0a1bc410918e975f196491a9a6150e14ee1a62f36165f594031d8c74d9df6a040dd2080702b17186e127e06212bd73eb1a8cf64941fa2501039e2f88fb2082f30a966f6a9192ac51370487703436e2ffad04a34bcfc77d6780f1d138967d050986e244cce22a5ecea7d3d61b5f97eda79b67dce7cbaf09cdc0d128c912366584263e5143a06304ae2d0b212a3d1405134a60e34fb74c5005c3f36b4d3d4bb7098d8582e1a27a26c8568dd450bb7bcb75cb7c167e625e774969268528c77e9e8b2a6a132d810bd50db5654e8f3a2eaa27e4364fb19c9d261512b3e3882e0c5d76c73fa47732d5b7b36e78d3de9203ff68395ace356e2309815b6322e2f009975ca7bbddeb410e85c2870d4f75b1410c9c7eec177e7cf56282318eb0ff8f2ff5f679a312923240eaf988d07750a1be96040e282cbb1e1459164b8f2fba18a8ba8d4040cec84016e12c088b020f647d2e789ca50889f0f1aa8c9deb75a55b1be997a244a74ae069bae56fe4bc1ec36d4e9a7a7f1df65e7ab17600ea20e6126bfec2a00f0f3581cd83321b03773ccc1850d7a7fb36081c9d2ef8d5cc0da5d778cb0200f52fee9090c2409e57a8a3ef1c592804bab567b53d4245ead397203760beb4982d6638e20aeef0e9037dec10466c6fb060d5294d54559f7e8c93c9e4bab7488c2f837b12448b03688870608351c84ee952aa6f067d8ffd82835f6b9ff9eda5cfb001f9f2d1b5256929efb03d5790823c02b0f7f9e9dddd5b1efc38425ff782fc2419b691da8d8145a5ed18fac99d9a105097290492c9f2aa33edc939987b05e098ff071173fdc1bef9bb391d35a6fe7c7330918450321b9f93a18bd71dbbf480c854ae5128d9af2a357830ffd6ec8811deeb892fcf5dd5840dec6feec858802b74398202ce1341adbede3eaa5f17d72c1c75b21e32bec202476384480e7b49d6cc4e44b22eeba797dcd51039ab67c3fcd649ac6adb1bf2a9ce31493a4235f70560323b529cb2cf3335288e1524b22ea180ce7cad141cfe25d437453a8d8f19db9f1070909d71292d83dddc83eecbb83e3048b602f6ed4379de40df7b8876e1faaba229e05acf479c4719c883815f3a028d9475f3f3d101e8f87abb4fc4f3b176e4518782f2740a0e4bf0246d726971d2c6243948dad1eb5a6472cf52b66241ef221637f194ebe1d596553b4eb08f4f723483437c9109c075482ae5aad6bb41a43ae44901c7f08666362a2f5edcc5acea03cfeba62bf6cc68ebe8603fb5588e5bdf8df3d0dc60942dbb9b2adf60860ffb5eca60a9cfec8e56068a820cdda4ae18263e12620322749163fdb81dd522915a9ee9556c722adacab63446090222032508457fd27d0232a2ea37d03be29126884f9f5a5d347cd0eadea44b890c03b6e1770829504ecafd2bf8048b88b377d3eb5e52256e5179ee5f0e8285237e50fac7ab5392b062b5a33dafb65a0e1e36befc273f27aaa124f63d01644e27f41e06a2a72629015145183d05f134871bb5b4bbe7b3d72de4de579ba49c3f96feaa4fcb29353d81bf2b84824c06838a4d9915da9d5c3018d1cacc37056de4314cc1acdd22dfa9703ef7776b4c3437b4004ca86add8ddc1cb196659bc4b135c7a0a82f9f8bbc6071b61731a15791e2f7b96e5cccd5d718d571f2277e4424525902aac7101f18d230b3a4a78d9f1e19329a957afadec3086a951991da6498be3e3e1bbd0ea84c7381f9429c361df1082ed68719bd7b5c7fb4bc4c67695392c027821cd30a07172561d32c8aeaba598661789891232794e7a3d47d45262ffa69e67c333ad1a8b6de1239814d3e51d1170839bbc555c0e97ab4592551ffa6b44e45ae49a392e59d8111bd08a257475e60a33d80def29038ccdd3ddd3ab4c672e09f2ff41a95cfdf63527fff23f33434a0db7a12648e2340942dc452f42c0583640f20b7b4ea72401922b0d45557dc98b711a731cc317c7a5ebe999355b25451f03ba018c45193da7fa23e8dc1352b23ddb7cd6a4b0d68706d86c93d555456c0e1a6de98e43798d74751599271d2d826001cfc42548d0cb6f554c8b2c34601d52aade8806ad846f8e3b0cb5e102f89d144fca29779d226e4c1739474be795dff3ad673c8593444ad6182a614de064f8011aa444c076fafea052d92a5096281cd8a60c62b76a8d3e8010096d33162f112c3666192af19012f0d9bbad7497341544ec9274553e10d8a6a70bf373bca5cd4f65e0f570c08a0a54ffbc024ad70ad5230174a06f3a5f389346230dec2a987d280f149a926b27a1b8f7ab29d23aab4015767a9dc822802d99af11fac48fb7a33c3d26f008205bc1a74d24dc677a44729dc44327c1d60a7c5820000000000000000000000000000000000000000000000000000000000000000020a6f1fd264e524b3b5c5ebeb4f49f97ddef85bf83d256611be321048ed39c4e1ad11ec8e0f81717a0124eb32fecb2557bce654a44d39b3bcd57b3f99980256f300ad8ca83edacbed783c98ca57fadb094d20fb5ebb4255aef9c67194f34c7ec1a2f30b109cec671f0807f985039f358e93df8070c0a5bcd75671306fe676ba2021ba8fd8a67f9bd690376fb779a40f42a0800f69862d27c91927785b49407902aeac1c3fd24c639edafc3b309a081177eebd3c1a9d5b54bbcb596b7ac1db9141590d559279f95c6aa2260a7870336b592103e484e2ae56278c69333e48204b32aabd3c9ad2cf6af20d5f88a1dd7b9ffadbd55b5356b379942e6a840a9feede8185fa362cae1ac6a7f594ebf80d385cd58f102f658180b175230d27703831d110fadd9c97817d73bc2b75682d781b09d6af30010b1526e5988898c041b024c4311e662e5f22f8945f60a024494e58948989eb6e8523412e8fe43cac586af2fd90c25d8f80579aaa879901a7e447da183d1118a05a57c64814fc764fe7e2023aa21abaa270d2175177c4dcee43d10f653ab480dfdd62d45208d6ab5671703d8071418733030ec7bfe0be838d17a9af0d303828262b2b2562250df953994fc3b86031cd0276a30633b3850b4914d78d5f12d8d4edef281ef1649d6df28e368127526826d1bd68eef92f6db6c2374918deed91fdc13310c9d6b306b20c4343009d514b5bed80857c7b4dbc4c3d7f6040111d14492cbc7fcf753418907f1bae389f51154650456961011760deb18dbc291a9f488c2270d00f9d0ac9ab1c188b00db80694134f183563de9d1620a860e2eaa5d5bca636d53230aeab5b7a54d372023d1229375a6739d56ede083f430df8813b7f6e3f10dc4f7a6e04cf677188fd01ff0950facb8af6e83153a4ac05dbddfb3c6dfbc75c8c9102980fff211b86aa8dda107cd163982008b4684e1f3a4ed5ccd9967fe4cd852c52417620be81a44ff38504d1790a0d84339de3f81cce8e89c6ed64ae195623c6b23e26350e189bf4ecb5294291aeb55042df782d357bb61f19c919b1a0f1f4d0241bca04e6af95f14fa92f0ca081ee357d61a21afb8358c0de0e4576ff63cf376e0deaf9dbe73c8af4c92885e14d033fc4be7ebee281b1cc247b50eba32a04c64405cca6a0e35b4c59af0e1b74351f972fdb241a2107052ad640f9923d5ecf496b31256a4f897d1a9cdc2952270a85a1f03dd368a4359fcf17cc4f050d8e412bb4a3584e908bf8bf12300349a2278d1ef65aa440a80d6ea1a853291802f652c85278fe200ea2804ce411068e27ab9f59228aceaf76ede6363c56a3f94df08cd2d453bf51fda57ec136c6000cdb8c2242ecccc864f90ab8c1db607f9d0df44a04e6b55ae1e37e8f695d251ed6e7e745d2d28638c498be8e4efa7ad5b3856e68cb0d90bdcb7cb53a3627f02ce2af64ff81b0d98ededf696f6534dfee969cc8be133795be6ec6c1cdb754840061368388f6ca75b7d9cebfc675690cb5c18c0443fbbb619cb64f1fcd86d86c2ea09322f8d180006182cada14315cf41ae0c5e798a74e3f28a297cecb797d711b26fced9f07650c37f86422209e98755e02bdb2ba1b58a05eb870c75e94e7880a0a2bb87e32328526e0c6a4ceeaf96ddb964c8c908855eb6466ffab5102ba10142c0033f18bdef1e56e6a280f55a92658b06c5d921bfa6543aa441a30831389035a1e4b6a2945814229a662a08805c3c9802cbce1716f48f2d33ce8be18958c1f4da0f5caed20a9adf65b95ea09039e450d6f52a19339f4e6c66430cf9ceb122d7316b52bda1fdcd60ee0bccd1e8ecb8b64f12867c6d49e27ba1a5c6e1649411ae70ad4527b49d5310c82942118500dc374ddc178fd822c5e866cd6d6178dff268f28c047d94f56e70828ef5ebaaebaad8c1de141ad3a32f35c3ef8fd9837570d883b67a540b9809b679a14a5db75c7bae209425759077d2eaed4089212d0471af976de32207430e002e88962e71a79daf05e1896e0816d78316fbca2a1a72723ca339096ec88962b527b6601f959fd1a37b29dda15cd447cc1ab62b1c08cae0d13791f20fc1f23288de280f42f2b9fee6dd83bf7624701de1470ea2a66a2c70e644fbd75409605f5b84a22ba466913139ab007144a46e4871f20912abae00324cff97a038b83bfbf70c33d3b2278bc8a1f86769a8fd9c34b99fff9dd925b3b232e774974ec96de2a9b0bbb599489584898950c74d4caf91978adb23cdd2e8913eecccdbab8ec3298bae17670386cf7bddb4974a4029e919216fe2829fd340428304927f124a2c8274bf17d9917ab48a8466e7f6075a5f86dd1ad1ac2b9bdb509d94dab5c0e94ca70a1811f28672d0afe0587e76617fcbf14ee236a5838227721a834da084e4a40ca87f01664c6280ffd4f94ea75cad18d4cb2a43a72c6f8fd1d43893d262a14559636d15f8744388642fdf302e5106d7bedb8dc80cfdc859215094ed5d78364e2c09682e8b2092e6d3039b5623e84005b0b997f8de2adb34427b6edd8f7968dcb951af309632c1b63983d07990b2d273fbf85c529f75c8b3b045185eb56385140e523fb6a1f63e106d4127da4a7a7bfb9fdb8ce3957ec28cd03c5d93b17db22d246b1ec5d31c0c443d3443dc19681eb0bb015402297d1281b23bd95afc95ac2f712a63952173ec273de5bec96a6d826c7a70041e18ba7d6bf0c4da7381e4bb1b0ac5fc83cd19d9c760a2cfa57ed2546b1359e438130441531226ba7c323b338b185ba0723fc5309fb1f5cf00b19d369f53d3df7efe40c7beb19c25663aac2f92465e872d49e29a4e708f7f758f640d264784822afd03f7af311f196400509612f4cc59254817ee8cd05133cbf8bb47e1aa6f0823c9606ff1908f5ee6d62619a5b38758789aa7b8b33d2c29402cf0514ca4a19236d6a1d4cf126d44586001a93526d6fdc9bf319bb0eef38a52645f5c3617b8c58b59c2d70a2173cbed7ada1587b71a18ba9865358df390cb6c66e4e84a5efe78b89c14aed2a0d7d04617d8d179a1ea422fd17a2738687d065405cb53a030c67bb97b67cf4c00666637e7de01875d0d55515f070d6be14214b4a97f51af38f9506847ec2ae700956d5e9c78a619331c1fc69bff9bdcca8e800319815da443ff3da8be24a1ecb118f501b6fb1adbef0d20117d0fdc16d453b42d27cddd7f1d3397708919010cb15c220bad7d06a696962a5b0a333c85b053b448643d18d53cbb4841a2e214607163aef06c6b5caf713747611d04bda209fe8dd0197222facd5e6b1bd514421031a666a09fbe72f209571e8062be2ef339619fbf079fee0e206d133e4bfb148d2000bc35f4572d9d2f08c6bd84ebbcc0dc8eff36e5da64a5744c7bb0db627938528d2fea1234d4f73b84402f852d9e64b282b15d1f2a908eae33004a7e10c0183158d5d182d672cc0ca97b5a39d5ff433029a2d7937c55c415d03b6df5ac395e3122f995a6b1d8b6141e9eb4326fb855b69017422adb57dcbfaac5debc6f7a846282fc2298d53c4b81773dfc5e637d5ec4e142fb1c2bd7ef42a59d1b577ccd3361e0972a47a6f105e293ee1b7f66e7954a0484bdbe53d3a59b630e2e199aefc4906a6987a693aa37556de2bebaaddf0690cac84df626b91992fcd699a614932b52b648eb02afca5ad6eb34260df8e6c52bf1156484515d5442c02b96a111a636c1b6618f8884d43df6445091af3dc6ad9d883c28c1298ab874d29c6df98f19ac3246ba3f3ece1f329bc432eafd5bb333a3ae0aaa6781a9bbd439c15a6908c6a190fa30448517ec1ae116c9254587dc4a7da7641f13d69ecf9d9838902789d498310493ad72f3df9a488271bbabb596823c5921f283a3fee1dd4fa118ceddd33860378edd44e54ad378d03f2f5db51cd18efbd7ec5fefd7d6e034d5cadefafd46b06afa5e815346a56d241ec7f573033b1a88b0c8cf6b3079de21dab5dce2d12261278de59485c9f1c8c58771f12acf3cd1161f746fc930aecd32c2b6d74bfdf361e3a256b953e6277d1a99a3c9e94ec42e4e82bf5fd6adceade3c373b4c36b583204e091afc8a3cf41cb794c4665cc056fbab49a8b90688ea2dac288fb0269cf7", + "public_inputs_hex": "0x146ec7a9e91f12638d75559824d4a1794590cfcf575f0bf0e979ab180ec34ab12f2dd8535a4787ea8794523842a3a4f45cd84bc85749861fb660801b495b3270000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000099fdcec25af77d8d2c617140bea064c1000000000000000000000000000000001f225677d8e0b697b5cde006e1f2fc1902ab8c28b318084d7c7bcc0b21d6429c2e96351063372e9aea9214a7e131cdbb151bbdcd1323d901dc5c874189cd37b54f77235c9d7908e5271fa34a4468541d1ac8c71d73acf4c43545fe38a5b8104b533db6a645ad505cc362a9b682e6fe461cb526d3de1f1c4624b030066eebf9c87607d85833438e8cee28ffafc9b3a41213aa3e44b78f8696b3559e6bb4f8933bd3252caa5c4bd7aacf41fdb2a0bc24d7198c8bf5b5f400835e4b0fbe9f10b56fd6b992fb16860ceb4b3c9ddedecb7deb2e1fd67a0bb9ddeb06c05b9f15618dafcff59cd380e10f5d26e6351278d32ab41f57533905ebd79c51d4f6f7331015121cee2a2ec263d55d864e76cfca7d333f" }, "decryption_aggregator": { - "proof_hex": "0x000000000000000000000000000000bc96d0538fca262eca3f3a175a2506ecae00000000000000000000000000000000002405dc0060d7ad9bf68d09752bd941000000000000000000000000000000ed8e01384c7d6b576060a26ae07f7f53ed00000000000000000000000000000000002f138f537e526e50b682f98bede894000000000000000000000000000000d9c70249009578b7dbb8549bb3fc5229b300000000000000000000000000000000001ed544ee7e0865dcf082904a91e664000000000000000000000000000000344b89579d5dae172baba8134c2dd49e13000000000000000000000000000000000002be76c3a8f17381dbf90cd95ae00512afdd01584e2937036193e1bc741749a3450bb3210a67cfff4cffb51b9c57bb04516878a8cccdac84076db0fd6a14ba2526b911eeb59fdb268706153f41878a0667aac6eed94876f8352d35739c16ab1d9f6bafea31c6f58936978bbf40dbfc0826c861f7dbf8ac3d317f69786b62d2bdb8a87b1a08ad534c70acab3857ebcf0993fb91fcb026ed3c50b587350ecb4f55255734294652a4e9922683305c6a0600cde5cc1bf5c3f4961262093ef555de0869886b2d93be75fe79eba7abf6bfed20fc0b7c24baf83f74a032b8373d92f21993d6182761f8ab55c4d85a070a5fd8190cc3878ac311c64fbbc0539e8b392fde7525d8bf17772eaae3c70570e522a11c14b30d68db30dee06a4d524c4ece4ff424e2ff9f1a69be29f1255c339c914016b42ba042cf43da3759887c145d63c390de00851f761ab9cfd9c6faf2512aa50c6753d21de84de06c37adab260f77b7936b0c44b6480a6ab0f2e3122ad87e310aace4034e34b53a49fc9ecdb1483a03edca34ddb794c170bc3c581a1a4f7e8e12d89afc87f8035d32a60ab5612ad0b4b231d8d6ea050a39310cdd764db045de1f72fa99e5822248fbc73037375c37fe2294c2bbdd855ca89587c43d05af40a206b187f8f88a086dc00a9c07a7159cf65a3c42a32218d9e04c263666bdf87e8a10edac214abef42f85564bbadcfcea8ae059a75953b735fd8e68e571035c4e4c2faccadc09fdf9ad93ae4226d5116f60aca908398357193a73e90883cc3c7ff2005edee4d35d841ca7dae7e03b062d50d5586d06085138534ab3e705faaf895f0f875116883ddbecd75741011371a16082ca1b49d3404f7eeebba7d4c7f88e8930324da8888ba37df163c6151b13bc2fb1c316e2b12d9f958e409514c248fa290acfd80696b225ea88590447718ec17c5b796bbba118abb21d6fe6b076634a6c23904bf3ca14ef1763d05c406d58aca932df42f80011a768e460d95eed48451b1bd0780c7dd3ca1586c6189093c11c4bf573809590caa9e0c5eaaf43e8a145da27b30a3cb1fab7c7f128cac57f5548d3e3c67e86a033535d8ca84ecc128826a21cbb30ca2f1754528421714ee134d69f02948a1d99cc6f10efae0f2dfc3f9bf21a717fff53305852764acefcf1d6eb9925a2102421aa0d57bad423600f64697f07dfc45cb3b4d655cd1f6b0927ef379cc6139722c54d03d2ab8bf8c5f085a26202fe015ea1c212b3b2624bd68aad04303e80708a1479a4745a2ab307d5826ceb07c2b6ee52f163e8af6143847f379b07af36ab5dd16159c42682b317980fb0df0f49a286b1e71b3002a5e399c2ae3324f93267a4a21ac56f74b38dd83f9a0d0b2959020ac19e1e580726b34c08ec23ed72473a46d82e7cc436662d0b75ea11fd2802c4e55bb1f55616fb153a63d9693d06dbfd963fa4574239af428a1f0a29c611c5a005745e62958a4a552236ab2694f01380f056b915f7aba92a5afe24b9402b4db2b55a8e3fcae34fb449b89e6897c92a01009b04702f538ff6e71b9767261d4dde7fb8c60f6f88eb6258523a22bca4988ecf1c541b00aa0ac1b96492ec7e26b4df0dd6b138f3f2328a84c6952e6dc98fb0636bd9be7034bef275f84d46923036d1104f4af82d2bbf9283f8ed163cc63c5aafdcbf21cd4b3ac76a3cae6cc9056da70ed5f90febd1c99afe625966d1ab565b59e664707e6f838b2333c5c2201bc1c887523d29055a3faaff2c2d86afd560907bc4cec3c73748468927d337d003ed9e69e4e6ae6e3faa1a9e9716be6ff96ea11683e9094bbb98ccac4e457b050aa7cb7d9daf60be0a1fc4cde853c97043f77d2ee85eaf570c23fc3ccd83c6df060d99bda068172b6aaf5b558fa6a2be31badec375d70317602b499793bdfbe812d706dff901460d088caac4689dbd00ceee3595cba912c4e04478af243b2d5e2e382459e2f419cb0684e2579bcf295f6c77bcaa8184e3078b3349d017abd4d814d49d78466cc2a395c96c3dd2e060015b4a076ef5079dd51347dc1e1e35b9ea1afff659ba63fceb905cec9dd228cce84ca063dff23d25778ba39e1e23c276ae275ffab5d82f63526330124d69ecdbcf83902dae2dfe74743980da6f4d1384db0cae659207173c164f6afe28f376ee52505ff83abfa91a221eed248404cef7a603c9d392a76090f487d113d6687af57a4740df5bf7be5c51fe6d5facbbb05f230e37ba78d57eb7517506e8c67c42a6d7c28f3feec0f8d8a6a3e4181c4eca35331d2b6a6d2bfd4d8fc13c74eb3df82f42eb9a816d946d8949b9c0773320f9aa792f4743fe52af9933f434962c5a6d6712d99216f90b6adfac6a5800c7f8aa30852f67d3b82b0e334de0e26f5eba013504b4e9f925b0ae39095d5a3d79d407f0d52edf3528813001cad53db592c38f083c51b40eca3f8d271fb40e0e5f30cd0f523023d6e76357fadecf7dda8fd192a6ae41963082cae9117ddc7aab76965e0ab22c278e0009b458750f62e589bb2089ddefc6e1b8340184e3a43cf0350aa377242ca6204f0a81c63b4352fe2c5b72cfa0e09377fa45f035db35b61477aadeb9fe2f95eefed8c915412cda1f26aba74c1a858b7440eea4e022a422931cb14a747421a98b1b565ed1d352bd3cae7404347f2514c459b6c4602af12d0598d146433a1e9784c6cd77f9dc9e59fbf0e27323f371e0470912fe7195183794094cb3d4c6028baebb5030cd170a4e20ea8cfab9289e5675d2f20b3f69afcaa9271f854a4d16b5efd806e893a40ab731e1c410cb9602caa37b061943fa5dbfb0cacd4b981805ebeee17f74569607e869df9e12a7c7dc61d0a6334c23c4872b75df9151642a009a9b4240bcedef09885e69815370b8dda9d2f019e87b55273b27a779c32c3117680545e1ae213aa888f5c0cb23f9f975079dfbc54a7d844b6bd248d2e9312f25342105e1cd058f08c1a0d7ba857320b7e837283f54b0d88e788bad0416c60c002dc6c60d7c62cdb5973ac154e2fc903750e2684f43c383013936c03d6c91d32d6ed02382e18ce85dbb092bfc17ca854e455d496784c5e8de3faff988840ac71a52cae9786662f618ee88248cf3767dcf068d1ad0ba60c0f104ffb7ed7b47b22aa2af169a69e8b99ab3252377cde793f2af45a8b053d981d075b6338dac083b0ab8e963bc7f61050bc43c894243eb2ffe0df3478edf5494d7337a97e22c9f4314a2a1bb29c6df3fdd996ced04a459221d8eb83d0d25321decbf3077ed22a79e1ce73dd18ed5e66ab2e9ee9868f60f13c6c61a1a26c514c1e720563ad91c68c812131a5b6802fd63504820c21eacd850b1c84010ddf1f1df60f0f57121e1fedb0f40e1bbc592fae3071203d7d6deb2593a0932a1679cc59c6ae2ce3cf3ec2a0619d02d05f1b1cddae0144baa68bcaf11281ab3eb23a213e472338590271947dc012a1c39101b5f09b93578b8ebbe79323ff1e76e73d5f633a3d5236c95bf61fd24ba8abc83e67d9bfe4f8ba975a55deb8ea9a5cbc90a571ed40383c5cfa2d88603dd5fce0304dbc7daad12b6c12da88baa0572e9dade80d10ecf1137278dc5bc2ad88e74e8b34a0f80e54afa325023a65d6d7df8c282bbd52284bf8f50dbd7912c65d8458a87811522e8e43d1947ea6e08827d23c94eaa749914666b491a809d092c13f7a14ed9b84a855c67bfcfcd41e0615d1b63251166709be28dff77d31d0931c5219698d8d0701983b502488eaed66c57f3a3479183731695e4955e7e1e2d67244c478d1ce81a97d3db0459a38bd3a01cd69229d4cb232d921ac20a289219fd2251b56dc54a749bd8bff0f5315863ed08b11e06b08f0daa8fdb51b045a520d3c9b2a069a65b954561f3e3404094a9d3a04466d91083ecdc40f78edf5b7d1c7b6fd1bb013cabf8e69a4fa265a7f0b1f1e6c1936ca56367b2ad609938e4421b682ddeb2c6df53c24b4e60395cdbda337471e80a423c9d1205d6774d9882c82fb717f2655a84d4821610875d973ca834640b1cf337dc996847fc2ae8df028727340ea87796f42f8750ab8dc8f93d460544bde04f1f5830b9f666b26f1fe523000637187ddbf9ab82e990a0c7419885084cc6bbd47c9cb3575f56d43d9a06942efe9fbc02bcd2b38f1be1f1ea1753e68d197e713287ed3a1aae35fb71b6ab7b274e9fd1a6b64ba0e0a2b0c9c499034cb6df66c8bbcdc50afa671f374d0701980838460c129b6156066fe569870c00e8bf243907309f3e9f11027fc4b8e0499922061d481cc584a6048f1d9d7801e5e510db33eb3bd0cc60f4df3198f5de239d0deb152110ae11eb3999e77dbb63fcc25e551cb0df977aee851f7ab22678fa761ade737512fb1096f839cb496f2c6b176f35538015be8105f785a1fa46421dd90c19d03f6dcf610e7c0a21987be11c9ec8674af552a9e8bc70c6ebd4dda021262d3763b8649d418dbd2ece94726127fe3ab67b77eefdf0ce5bc815e5a9a14266256230b240d837dc108e4b3c33c2156e007efe28b89d008cb4d2c3035989274102e59040af902839c1d02dc9028415f545aeb95e2723d066046b1e6e309524492466ebdafa5f674d357cc633a04bf1c5e2a2e46d8f45d348f6b007f110c66aad2208f42595eed8f676d1402e80deddb96779baba3606acf6b8f3347ae89a1ea024d2634d631b7c11ee934fcb194740c963814f86cff8bc7ae89e35f5e4481bdd172114e526d5a14c317a3abb260cfe202e071ea270024a8dec7d8c27dcedff3e1256ad36f605d247031b5b98aca084a0ccf1ab16ee091d060d674c22ba9bbba31ac39fa2c5ccd1296cfa5206ea2ff71f2c994c43e4a13752211efb3ac8cce9380b26f3a915a74cf18055103440aa5e42eaa9a0631dd17dc08f6326e56a148a24292b155b32e860804d721094d93d132eaf0cdeea60406f83ed7a36894d8cce6022e225a9301fdd6680543eee7563da27e229fa2c354c0abbdd0f9b13f75db0cc228c030bfaac76a0a8e8d1e7b7507cd164ee01f9f134d8ae0dcdc769334d217f0f02944ccb8d42f84455f74eaeead9a5c5d5badd98b800f888f0d5503a07a1431782b6b000cbe3f90a219d8d9d385666d9b8013082f47edf494f04dbc682d3090984f0752cff98206de8c311aad5217198a5032a23c864771cbc64c65d89f83e270870edd20a16af87f484e19c0b4a81d0d9fa360dc0201194df07d6fd0699d816e1591946ff35e2ccdb92d7aefb10c6d0977138f266738413a4002899081bad1b58ef7663603fe04e34df7861b25da5970153d043f4d86d55ebe37036ff0b4c1f8f79db062cf6ff825d1dad33dc4b9a98a901b6a6727d846ea030fd6116c03024803db18d13e4962525c1687a0b56011fc0964ff6a3738fdac2c03b75f63ab91c36ec120a66f931065dad13d2c00becd94af8055868f1232c89b0a7fe108ef42631c25c926cca36efee47a6ce4c65f8cc65ec45f6f4104a738550c9e078d9d321b74b1aaac36c7cab363382c50a3ee97d1484599b98a16d7b535ef90b890a250dd8195ec565b405a2e9ed233c93be8ff18750dd0e4464b0b79299d6e53337d02c9d5630ee9d8cd206788148a626e4451cc7ef3f9f467edb204601cd4c3acbc01be12f30e6400a8d924e4c887539978dc987e537cc388c52ad20db05a2d64fa82066d23541a96eec5f79f542aa2973ed771bad079190e9f3a9d0f48f5916aefa0822282cdb70e779f9f396160ed0141952b6274a8c05e380309759c962c2269d261f7713162b208290936dd9fd8df5dce134905d0a8e6bc27244940a1b7e916e13d6bc0978e8b25849fe2720b2dd10083d04ef1c020ddc628ecf7d47291bbf6808e411c2be3c79d2907e2fe292ada38239655626c449e3ba0a47225fa635bc2e28f19719d31980c6a6901ac47c1736e19b9e066485986ffd953f3f7eb09545cd0321c288eff02a8cb675719c0bf9a5c975dfd95f71df4432949dd382b0f475060b2fc48a2f5a87c3bda8d2256f4f45a171f694a710df872ec60a806c55cad69502a3bb6def08e48906645dfda0793a14eeb567ff7c6cb8e2e49a5cf8f74eea7c0b7a572378316a67bc933b087f1a126315f50b790ddcd96794f3db597c284cb103e93f1a1deefddfc386d2e37d22f83c0695ead19fda8f58e9c88451efd2d5da012e9e178d13a34e77f1d8f392cfd12b2fefff4882a9a79e7e97266ae20d4db900430c1551c5a2f91e4d5f8f41b32db0be2cbdda94d2c6a3b1387c3a40e90a1c283f5bf2170fbc21a71c22e57cdfcf7d23a069b2e58381c0e147fb8baa5e580a14f9a027613596f73cf997bad1aff33bfe2a79dcf467f103e41ce84a95c1f2f1114249ac71275e99cd5ed73935fdb0a4b728eaeb12642317ce06ad5c3ddaa44016ac4e08c5f384c67284a724426f92654c49f671fee576746f9bdd429770ae0d0e90e3e0cd9d18ec2342e8e6f0e796be33d8aa67304a048b165409be1273daca29644ba681676c8bc306e0aa41f5b87549657e24d9117b4309d2cecde487946e0ff7d25a1cb33d1d841dbcee49307d8515b283ce87e904439c839dfa050243a92d346b7dceed618937270a3e7c870ab80f626448e2e8e71e238220480ea26b4c1e2b96d7205d4944193d951dfd41cd9081f5f9c6907ee8b21b81d0739a51cb301b702cc7c529441441183025abaa3b5f939a2526526ca413d7750ffdfb33fc4110c1acc042f1af276b4ed380035e141a8ce3863dadb0fff0d191658d9c69efc0265f22b367262d633d8f855231e68ff69665617ec44a572420158af96b344c31156860db0e2d5c18aef202bdb2e917880237cecdf0e443bbd236939fc25217cb1eaf8e3ac15e3637bbb78a2ccd510d75eeb119cc5748a4d7dd1524f98db2aa7b19dedb9cdd1ab1de33c47393dc0e18505aedfa9daa4dbdc236240d3f8203b95c2e454e7b7df43bb41928540606282ea508b2c0f4c1a669f2a59f5693e78240662db56e6edaf2a693922cc06349d4c497abb72e1893ab12d8fe6cbdb6a76eb9d2206523420347b845912fcacaf97c5152f31ec1b001a3d8a7711feacf4b6f25fc2bd34d46e5318c7f45d748a1fd678ce7bd01020bd9a2418d7cecc72768f933411deea420542f8c26bd33fc20d1b9243b4ede6fc3e6fea720b4e2840976f7e341287a45eda3e347e08d987e0152cca64132234ba71c4b5e97ec5dd4290e17818025c827782668caade4e586c97f2a448832c052c3896dd32420128b44435281440e5e38c2b6f49806e90b91365e360ef6de5b97965dad8e139681ca0dc9ca1de4258a87e2c896a0b26e6e839d21fc5b65219344822119bf1531542d769b10e5512dec0c9df046d250a8bba907318336f698312361def22d821bf339118e2b002d230e89587f62d5ae77c9b8b57367999ffc454bfc63f6dad59c99a8934de6861a1e798790d75fa4b7a9b2ed0ae5975b3d8cc86e22d3bf382043c2347d4b23ef331958e83a3735259a7586792852390b8bd9ea465085f7932a55e45616dba802f83048d7973b1bcfd0508bd05543ce4f91ae218071632032a84751db87a9e156280a50058c53190fdb07cd4da0b26e499565152a1fb37347c2a2114add8edcd22505a125015a3e26a93905e24565fd25c9171daf5c562269aec392ea86d1ed741a1599dec50b4352e6c441310d598c9712b28fe526b0a84fd1e7ae52f289b2b69a06ea30bb6cc1d9c376caa06d04f168bc6715c40d124590baae60671e0fbe3c7e117a430d21c524388e5173e34890606d0e54f353cd5f8a80fa47d095d2d0ba2c0b6345b284273c77ea2aa727a648441312fd9d8401945e3496068855941d03180c4cb91075be6615770a540d19d79f5a594b6221c1921b14b3a047440dc556790ede6c003bf9b126c49a782d5c22f8a322237bf2ffb95284cedd81f8d80fc5750faa0cf58c06ec0ca268d39537c0be50720152fea206b50ce8dd9a57d6abd43f02509ba7da5365cc43024ff0bac39566f27345d2b2364d078b038fa67d10ec131f24167a74b99940610c56dc526c5015ff54e4631facbd77e18d9031b9ee305226a3159deaf9b0dc2fd3a7fc78ade27c4c2b47777bf70c9758d45597ba13c3f71d4da0dd780d50e25fdacfbfb74837d19438a6273c4dcaead1d06deeeb9c948d130f064ef140b9224911cc5485b8119ed8b05ca81e239d11d83387529d74dd23157efe057e6ead118ca64aebba45bbc9bb758627a7f3ae0db60a98b6364074810028e5e5e6ef4134c9b181245044d9c9add82630fab4f272f277422c86e1d4842c1c638aa541a85119fcda98dfd1630ddccdbdcb270fe62ddc74d29fdaded8950336a2115c4fa72410b6245af33b9c7d8c41e99b3b22494e8fd96cdabaa162560306064c141b3d6160cb90356044fb43ef0d88439a6795691150722683c9a320006c2492070bba3427555d78f123f512ae93c17aa504fe1557d296fc19ae8f6815b60ac4fd7a0855019bef36c6591f06dc4a8519e726e8c8b3f84e8abb05d914065bf15fa5b569ef988cd9b391e99f911fe964a06791357f443331dbf35e30f117e64fd3c3674016862991bfe70c37d8778ff8123b06bda8256af0a28ae020eb27dc718963085585437c63ef0ffff850ed510a0172eaca0d650a17188331c3e8199ee02aa2b0b0298ef73494a98310bd81f9e6c88372b0eb01c74d42d75587722d656ac88684c418c2f5e46052783fb90afe058f923aa967758644f04d2aa8ce16c0e95aaf8a95e7a67fac56e7f9486b49c9e9fe9b2bfee60301116fb47fb5f92b59d0fddab29c9b91018f9dac9ab7cbc927d1be9be727a3e780a3232be97bed1cb986e63dcb100b8f451e940fe4a3b400a962bb9456daaa1c2ab63d459df45e30346b510f81f31f0a29571156e2de0bc46b69cb05a1515c912ead1ad59929862aedb43a1ea0ed151372c91814f912e007386f636f1dd30328c8cabff2211c5c093a74da592b9d5c5da83532d8d2fcb24cca63f7696edfe5573752436065c07a1138726222e334d83eeb5a8d09ad7190589fe517afbc4024fc79d8d8c903febc2ae38965b2d4e7c27112c6ba358244cb215b0ba017c9933b2da4bad749369a522c3b603898472f8e44daa4d6733f9375720edf5f2f5df0cddf7aaff381ff08e60841ac46d2cdd26df208b1b81b5b4f4830959ed8c4aab9323974249bcf8e8419305bd3af49bd86b4dcd985bc7eb86b650a16129503dd4e7ff33def895f266664298bdb6224d743704e49b87c73d364109e8f1e34d2e24127c494a712267cd0892715686c0075b9daa2b09b7f14946b1b1bd19c0d3cc6ad45fcc508f8a51b005c2fc5e7942e8b4b50598ca0ed1b5dc525cbf38bf7d4aba56ea94470b4ba34fe5a15af8f67971d9af9cb8735227d522af0489f8e4ca6b46db9e84b10d8a317b272197428f03dfc19fe78c9935d8fb9c442d92de470355afe58871c768b2ab03b902a4cccd217530cb9fbf0d5cd6624eca2d724f6fcec9103847e3c932df0b073250a84e6babc5cd8b3bafc3045dede571bea8e9276fc64d8d8cc21545908ba564519598726be27610b418d8c2e836f557a80531cc031a1d6439ce57d6fd2a4e2db0ab08d51576c28e6b0b6cb1fe3c28c26477cfb8b1d0431d3d48f45cf0c6daf4f0765bcc41c6b82500721d06786df1163b97e1d85e3d81d1cf599835d7e14cb02206b23b3fedfb8d203f983966029450b2a88496d37eb2e080f7b28ea4a8f982225c84052d11f7f9d2c38b3f3b9bc01c14711cdc78e9448ae996bfc90dedec04c221633b7ef1ac3384e1899453bdb52eab9f89626940cce87a7b8e5c821b4523c070383a1560e84d09d7137b27f25ee99397a7f8693566cdbf7ea9f2505e90b8a1fe800e2d6ae98cc779fe708ad85e18b110d5f7c6a2afe0c8e02e0e6892facde01e80d5d925a6025dcb2af64ee3e2d806f4a35d456613bbed9c5605258cff91e0119fabaca716eb23e06411015fcbdef1d8525ae8ea158b2ae1022a47cf32d3d16f8ef754e6b1855dcf6f35f8b1bbceabf2bdf87cb89bc22147fcdd760026aa92c9b60b03d87250b91b888391083cba614a1db81ad74d8368ba6d5ddceb8bc9c20f45a28b04503b38f4cf95b9858288719e95940ca183ffaa69daeb24b6ce26928b48958f9a3b94b0591b4f3e08f9d05d2f62673f8fa30908dc19f48c6dec02b2a89f635a4538f8dfbd510fb12acd28f8701ea96063968012db76bb9a11cd2eb0052cc417afdb657e983916496acb46f22e7bbd86bb576136ac22ef674b089402cfb8b89097aa787003d672e8a0fa8f73e701105fe0fa4a58092af72262739c70e6dd8411c9673c779a66cdf0d66b5a5a75b488579fbc8d8721def4cc0934a212c7b4671e01da0439f936e47374f3bc6f0c5b694409c96e771159a7aa57d9340122c6d97e82a141944f0c074bd87da8c4ac369e4aef92447f98c98420720213a2a98a281819fdc918862971501334269bcd5ed15d5d405b6c8daf01abb4b6362303788e44f691991050045f7492a309818f327245906f8a58866afce03b7f4ad2aaf55cc58cd9238661122edbde8249494ef74653f8b269a9401032a241902e3000000000000000000000000000000000000000000000000000000000000000019580d61d1c7753149f90f2da340c29216008e1133d65577b3d54f48ef3f8e3216a452c1c48a73eb1b8a5e558ca646a5c30a55ec71b3ca8ed757f5efbfa20038069a224d1faf7b130574b42e45ecf0083740b949df53b7ec67a1103998b4d6222fa3fbb78aed33c874ad1aa4b87633e0296e4827b1d172d483fd6a0578d1b32a29f5bb5a197b3c0d95972afc77e01d8f53452a75c29bfc9f509d582277958b08138f909e37e46adcd103ba123a956c683d076110fcba213a0e40e0f5453d1ca10ba45436129c22ddb1df1efc756ace1080f7c658134f39c1d42fb75ae077bf6104e76169480b94f7afadba348a55e5f784ffdb41212c4f82454aa9a38f8bd0e2059194ff8225b9f1a37f31e4d4d98945713f15cdaa072015b3f0c43fac9c79e30dcc3a6a64b8d8733a3a348e2a60d407473131895a48acd85ecaf1c1a7ff200613e3bfd0535f009022889a42430e8bd1e7a856a701dde6b4c651d506b4c41c37183a41937a6b2af6b4b1e6d6e2d63f3e978fedf204e039cf2e9a522a2345f6c12efcc77bb4acede0a742718a1594d7ca3efbe93139c3f9fecce6c2c275517bf82fc3ece51941187c8ff587f13a040fa2314d58e39713a328e2a755c4813c1f68057c19049be7f137ebe9f0ad967767c694f2b49cb74b8b2e204f4223cf832c4a0d9dbd8cb593dbc44bfa78bf9efde6d4abf88db13b083734bc08ea0227adaab728678afafb07ff737870a8dc20078f76309f7b09ee9a7e0a9952c7fd82340b2a2c7f4bf1a4ac1d8e6328cb475235dad988984ae8eb687d788584b17da606e9e3250089be90b9b2e86c740af3822d2e3c490042c15aab381a1343f9295515a13617c992885dbd426c8f79cdbd61cdda01e681829ceca10649af6821e62e465df72f5b0d9b33c7d6f414681925ef28644d57ed54c2a093bd512d0150c62cd678f72ff4fc39aa44c5c065a536a2b44244e28551b15f9d38a262e4abe9b0b8e128e42d910583179b30476b2997d9a38fb6b59db6a6beba53466417d08aa099f5e5fd2150e4c0a87b3cfbf84b0497cae44e5f8763f5b7c13862c101a8c82eb48590e21c092b1ee129636cd99ca0cf04d7bd67d92fb43bd47c9f7afc416edba68bbe8912f97f80acdf13fcc645f50a61af8431a2ad354cd25d72d31546195f1c8c4f6e0789de44abc1b869ad9379b950b146f2e89cfcfa1ed4b126b673c68310eb54da16cab040f8ce58d8a03b4d801cda72ca8fdffd75480870e328979702a922c62419788d106da718405955694420022c5e0e7ec413abb99480200ab8e9c53c77d01e438deb0981c790c12ee463c412d00ffe4045c5c6ac860fae361d4fa357dff124030ca85ac1fe1e56df3e0e0c044b5b951f33e97dac8e3f6365bced9e27b5851a437605c42d6454f9b568754281b40f123694d46bd498400360d03ffbf22eda152c42abade99d57468bf0f395fda8e6ada8aac3c8c9a7e36d17f56233f945b5291cc8888930ec5f4a5b2937da9be51285811b8cad5e677dccd40fc708eca00b0388168c509f0eff3444aa0c7d74b91a3b2faff20de7d2046ee07a32c22ab84c07e0a078562ccc2f34d563fd03e2cd821aa50c5d0f16c2891a2fd2e5c9b514a70f73f0ac86f37c58b5eddf34d8becf6a288fdcdeaca88248215e68032e36374b261c214fc5f0525d345bfaf2d6e5d59922d85fe851e2db240a427cb7853eb83c2cd2abc9cf87de4f6cb384d34c75118aa95dd317959598181ac708c8a039d021279aaa67bd43d6f1535d8ebf50a0bc56a45a888d0c241114d56a3ad860fca14005433532eaea9bcd9e7f8a104ce737dda9de250a5cfa7f77bd625a2c4df302db2fdf6e3b234a05e82e66da226c39d10d241579bb94196c1e14491b1cd072f88d1538af04b9faa7b2de2c2ec1cb78636f8d94b16b301603f181f0ac84ffbb4ae421f3c1645477c176ca241ba14b2d6c95da6816fa52c23fb2c6914887b11f83b4295fd921e5c8e1caced07c0efd3763d8a6a221bea3b2b253606b24a5e0e8fd7501db3cac38421f49e5698b43340fc962a6113473bd481f695fa1b8352c6b5726205169159b02f95c6233a7bd5ab47a31c3894f91b4f6e16e0b886295442d718309cdb643a5b2af5293130365efa58da6e0bfb1fde171db0c6e0d62886c7181b42377c265ebef2b8c59f5b0706d16d64dfae1dede5a8fa6c0a46a9470c9cfe7fd1d20bd68a7fcf32994ef60b3eb2e092fc49f9af7c9799d4fe72b711f662bebc614047f226b6911ebad90e79ebcb63e98fbdf3ce1d7572ffcbe46658d03620f940a0e9fd42a5d129cb610fd70e577a870de2d0c64ad65640a84e47c812fd98d8407cbc1a19c2cd34ca02b231bce720b7d2556da42212bfd0065b201b85218285912f2bbf5722cdb7e4eda59a65f86aa5a7991c90442891bf88cb706b06c1ba7580a81f17ecd76d465dc5c700e6e240caa0c1bf0f6ee4484ba6f154050f5267300302c2d7582411301b84d894906745b96a4c7baee6ae1e9b4e1fced0ab56ae37a2bf52e52f93136b0696f5903823d064b00a9b6a61f18e5dc6bcc940d0cd2fbf12fb21a9f53cf49dd4cb64c411aa9f1cdde43d7d3924e0260aa160af8d89f5240181af0d1572b3ce9e4866d39f55dbe6da30cbc094c4eac1e9798a6c8bd80e1c7085a884a77fb225bb559f52a9361d2f2e0937548eb03e9e7902c31067c3047650ca87e165b5a6416bba148e3c78df82bff1a777f411b5fc86c1449fbfc899e3b265acd250607a1b9c41aa27772ebb89adf3d4cb4280ef427052a7c32c622bc6b2633a0f86a2e2bdeb62c0b856667eb0fb609531fe1b4c0928e0ea230b88f879400b118df4eaa7c66153a1a76c30da654d86819530431bccf84be8a140b1a1b5f0574f6be68ac394b019d71f7a8b0e855e62fb22fbb1577059ca3f3f73e624da50dea5f2befb0a893ad9aadc2919939a61bdc986d65236db203707173d84d033f0d4ea0437d809120b9808fca706e93a9ee2b2595c9ad7c1d2bea3f46e1796b831ebfc686db8192abed84f9223236cb002dcaf3648cfaa507d773eca2d39fbb441c94ddd27aeea1b9b195026735642bef8ab61411d7b9350b8ea2d74af76a18251e0d853e0a3746e2d3c2616c65dab34d5ae5ba0809d6edf810d07a4991e7d8d9293ddb530550fc7680b1cda2f211566d3cc247166bcb033e253f38b4de6aad792690d9637ff335ae90075552bfe4e6fa0931481134dbfed4afe87fa73ab126922576f9228d76689090c5d573053a0a6a8937e30392d435c66c338fd258d671ee21f0da4180639a866ddeb2912a71f17107b23b691895c1b6bf8ffdaccb00c8e10efff64d66ca3030aee49a69616d4aace7db4bd2a1c874f1b1249fc9528ac614021999b81e1be517d6a7eaac84720632fc9b29db8a984fe2892cb951e871f729122084d129e8d9c92c633dc995f820e7dd63b62eaf72da81dbb328c976f022a413cfb365237169517ab9c1b39c7dcc07cfd41501281a8a3296182eca374282df16b77996181c7f888bdecaa4bbfc581cb830174cfc5eb63da1066ef3165a4dbd2976a089770bd8d633e399472255ad53ddca6435ea17d1b6fb4bb0df815638c90d2ae2c80dcf3a928c5a4b7c61a8b71b686b6030acdd3411f09bce00707496452b4c0b7c8bc01e2efe2181672b8406956c89011a25efe7d609a89f923904e8ce00150b063bc2dcbe47ee8f125810afee983495b76b8618360e8e132d3056f7950af7e995a2ab667d46b86bfb815f7a268ca4b468411385f65692a7772624a9d71e1a7baf4b9a72a23ec62a7e63223252b75de871811df99f12637a376aca03fd275d1d917b7bd9bd8ef3943e1e5979ed0e645e80f6899bf77cb6cb1464fee92b0a773df1495f34c590ee0d5af6ec3b41e50e61c42d2e967869cacd02d8e2cf4708c58d3039ba9768ed6e17a90bb3d303ab6e201f4188f86972798515bb191018074a292523cd2f3906fd66bc2cea4cd269eeff5d93a9b0cbd78666e4d712c2cf194b94842f9e1c75b055525f2de2112964ba53900373e81db11a0c4477755bf21cff0ae9d30ce0d7f6fcf069b9824a3fe663e82cbc73a377bdad6da874259b13", - "public_inputs_hex": "0x0735d58c9896306041928fcb73cd2faeef2a225c22cdcac38d802de73fae0eff1739d924e1b79deab6c8ab6f90b56b0676891aedb26fd3fddd21e54c0ac32fc700000000000000000000000000000000ed885dc0873d41b86e03b401da548aae000000000000000000000000000000008ae9fa5f57d2c0195f1138fd669fe83500000000000000000000000000000000c803e0614c2e62b8a0c47186569f52cd00000000000000000000000000000000dc453a74dd94d318c80f0875a5ad17cb0f7ee1bb52c323e82413b0d1903bc8d2d6653fb7b3e3ff408fa7b4844e4dd96905a2fab20a277a785453b5c51e89cf459b0b43fd7b33c55eafaaf91bb7dcd1db000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020a9dc790b726d4b30e4d8865f33f50004a47a309fa92e169d5ad235f41eb12542c77628973a693c21303168ba71bde796957f944c3a3494bbdb558527b5ba0c6071f98da6c5d2594c5ec88e9d1b4b90759aa7be42eea0aae2489f0b863dfba9b1cfb95c79fb965b14ddeae2ac0ae58e6f2b8af40f21222f9a964c0e33eafa2510000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x000000000000000000000000000000c489e7211f8bdc5fb2a843c924ec6a58e2000000000000000000000000000000000010d820703ce174470f5d55d67650a10000000000000000000000000000004c166c8cd38ff0567fd8d0010d54ecf1ab00000000000000000000000000000000000d4d160ed44bea4708b155129fb9ea0000000000000000000000000000005eabac9c52be08c5ff66f78e7334378cdf00000000000000000000000000000000002eeaef0d86b638aae0c047c169ab9600000000000000000000000000000042e3be4ecb26af6c42b1a097e04af88af80000000000000000000000000000000000255eb39c9b949a41545725786438d11b84c1c25f84b604428ada84fbbf45bfa58bd8bab5d01d5ba751b2dbe212177718b7c4644749243d0e1da5309c180e2a7237b042fa2670c89a8fefe1f3867a0f1c11761554d7a86706c57d994387f16451d7eb4e644c73922014166a30172d6503c19e4f409c54f0985cd55803394cb0b752241ae12cc7103497dde6c35e722e18fa6691376c8c9aa5fd750658d069055c870c3c75b52c4cff6b89c939fa3822257b7180114021bfcd88cfa1e83d0e01446876d3ef152873b3819ca1484fed2601ad4cc29eac6f7f31a7317fc563b8b1acc8ffca92c99371719e10747f9f9f4c082996737807d26dfb0583930732127d3d0f4233827ae2b3f63b0ac7b20c7ea1014a2d4264b7e20544ad5324abcff50ef8ddcd35214fd36a2db59380b63e269d29fa75e77bb42311791ea54a137b4d07a5cd36f45a7133c7e1c0d06e0c1142540501b6ff6feda98e95a0c5084f8215ebd21a7d3935bf4066855bd0e8989148ee1db565bbc14abbbc9be2ab07e3b0296a3e45cf3699eabbba16a4580a8fc383cf1f5806a9749f286642fbf26fd010b0f3e3251fbee12d9fcc4377c8340e76f5b915fe26b21a0bf4ab2fe9093e8ed593e21c73f0bc3eae385fb0873b403c87220825b3107b7e7e7e1ca63a9da9c17f267bce17b0915b8b3255b433c4d41ff1fc1518ee12dfb4a0cae96fe78902029e167659c256efdf5e94955456a6b18fccd3fe02f2f0b728418d759eb78f13d7c72621fef0a5ba5aed298ca9178fd46f258afb00df49eae67b7b22535e94adc59eab453431644dcc2302c7fd366fb13a441d22287a638ed5c10bfddc9a6998b410b1a4f655eaf40093c61a9235f128c44cf1c918494a3d3d1749174d3c5763a8bcc876f5131101db4d006f28377966dfd4341405d53e4411bd146580957529218bc8802847ba1897f61e25dfaaa92a3e8ed45e1c9fddb721a4df30127720518d1d5b5097facd0dfaeb18480862bee4495570f20ea60914cf5b657cc2865c8cddd99b57e6c8563cb064c86ed19cca9a0824dfe3226d8273e1bdc5267914e4329b557747c84146aae28afe051e091517a8e4b14018b8a7522a4d36b7606ef81b3573c733490d404b1eec26ecfa859a1493f06871250790dcd79113d21b0fbf6b7e4c732795255a5d7088166489d9892e33e90989083a98a349cf2f32d4ef46eecdb7057b543f2c7984ece7d833edf20c45c4ec9711ed8b93c54d3eeb1944dc5ae67c3d2915a862ec18023bd4221fd09c12180b2404b6b3de5bb34bc6abcd637c3c1927a99da5f6aadd3fd3e7ab6bd38bf3b717f7285e6064b3f62afaae294aa978a26f62cf494665aa232528e727cf45e384b37507a94209711accc1b9885ea12761c9378c39e5ca1a65f7df51c244c3276457d32c9c6f57327c1c79b824b337b9728eed388f62d50ad95ba33275c1e732102de50540782d3e169d31d600fc70bf48bacc9f9abee13625b92f0246b84f0c8ddd962a702e42636c2d97543e9ec16c9097214807b2290bdf9ef849305e2a1c10738906e52a9eaae0c6d935b34fe3fe988d055536f831806cbf96badbe1cd22d2f19c2d1b3f56d072794d820a40292340d8d47e232b5c59cd193f58be30b44d51bfd31fd2356611268206175119304e770b1e9efabf32634cd27b063887b9d9c964ec1ce8ae11f22f70b69c4e12531cb0bb3093fa73af0a9278694e863b8127b9dadf00f42c79c725310a1e93dfc5d9980849dbc578b73b0161a5c0c6acaa8c9e0dd12e80304fb8b4eac37c3070838473ecaebc8955752dbe2ea9f9e75f2dab3cf1722cbe1eeadd3517e6f744b1be456b69d1c0565b6041114712a1ad8ca89137169529973c67a7feabcbf1faf8fc0eebf7ab4e76dd49cb657cb807588d97c4f29faf01d264fdf1c5c0f2b3e40de2382de009994772b15e74fa800410bc5097f979a601364adeb815943dcfd3cfae3adcd3c417a8c990dc548d1e610e370b0d0b6a57109013616b563c82a6ecacefb7c842d925c600da0cc9f34785d5b76a7a44fd271f7fbd1ffb7ca05108c35b2cd051bb0b56cea9c0bb813865d620bb690f46a4ec0590ea831888459c3dc885c2b56665200170f1599bc638cd025edbe9c41a8e581ebfc04ba60e16c7908d4ec4f238b44fce701cb27dbb784787bc7d7f688dd7410124e3d85ff599edcb6757f198638f10717a3de0d9638e22b192727393676b272a68b34b02ab70d79ce361db0330c5376a89ce59171b5542dc296b0e6a805ff1113801b24d03c6857f43b8244f6a8335e5429c4ed09fef8622fbeb1913b642d32232573fb195b3e646c1d2616c6c4d0951797886e8cf4eb7a969f40958b103f80913f451162a52ce38b17e5f73d9936018e7099d92cac7c1ea430c0c5f86a461228ec26ca5c09952ceb4c892ea37d9a641b710202df6d0e4837ea757a018737113fb566000886af718b5b27f886b14acd82127a21bb8b0601b83c44f251573412019b256e71040303ce2a037dc67f9d60c844ea4242970d4562cb18ec03b2b972d3c6cd5dfde131440f764d91d94ed044a35dfafd638f2a53b532c7e9bcc73310fbbcffca97004897a315d4f6f41d95a748d40d66c68d8c28d34a3804eb8c8e02bc2b066eacb4da9e4daa6d10bcd1647a980ba04ff496d939a7cfc375482dc4d05936ef003a6c940e0ac9240ca2cc3d44a3781e3d7a940b063bda7cb0045a9200827393ca060a468b8094fbe616d537e020ef73e3eea95e8a3cb8465809a982f1e4ad2611688e286d3bb134bd8315fe00f407647b35b32d56342ac4a20f6fa2e252f9809510fc454d32a9c231f5f93827e18963c1b7b6bd12cc3e503f5d9c37e185ad9e3cf1a647bf32e35f167c0b172e435f546f84ddde2f5e895e1f6b2f5df028935769d16a3023bfbecae84fa40de55c500f925495cf82a645cc7638eeba32e67050dad6fbcf424900ea400291398beb2b876360c77a3611959d310d7291f27b64cab9abb464fa9c5f88a33e26f8974df1094d963b65885553302fb3c0534253a1c8c23945e5d07a05f8029b45612c36ed67f490e3a868d033842fa1386c408fa0825f397e035fa6eed030a09d9f21476ce5170dab5baff12175f13d03c112ee6388dec77b8e920d526c442c8843b7f9ee23952fe46a3625c99cdc49ac1270bc9228f4a1dee8b2bbe2396e3672ce98969d326d1cc85d0798bb49991f806e6170e05a7dc184049baa2563913a4a2cb1b904d532163c2b827ef7dab7ccbad062c70fed0b8b1b7a4da3c6dad15e446363d8758ef29d5d8be7a591ae6c450c8421612da0929d71b05db06ccb75e5c370d7e37b2a50ffed7d39d6d7a4d7de0ac2808f1d6091de5e2de53e78e03bb41967cee914fb5cb3b845bdf9fc2e6434abc332fe7475b056dbe0aa70bb354e11905f8b1deec54c63ee6c7d58546fee55e4f540e9e9e02824be4bc133850eaaee7b4f1a3ca6071b4fdfc992341b437175020ab184186451bdc3236df87acb9ed15d8c732ac56e8ed0d56b0e0a5c350f42999de2ff69e248ef0bf96d2e59349f9ed223c457681cebca88165679dd827129dd0c924a4613852473e4e1e530b0b50cd366e56a0131946f92af0eed5e320ca38787b2a5c25a2596a217eaee77d82e5903d2adc77ee8b5bcad99c4d0a5d47f6e517e72eb16f8595f7027fda94a745af57c5a4351bd2446461158a2c499e51bd96c08f215cf17fb2d5abcad8c2c2f7bbfb959c62bff63d636694df8113f9d89b04711f049542d9974c2c08b506cc6520a4791b96486750c0d5f7431c74d5115b930a391a0032cdba415ca64e69f95cc7a5e1ed7328284e4730bced2a7920b92900c9d12bf3ad8d4fc8f7afd8b036a1e09fea7572b9fa356ff11621e6290def47dc7a14043eb15600cd5870bea2dcd1a0fe3cfd1691e7582d4de62671e5fb429808f50909bf894f5df8c90a79877c3b0cb950af23217ea509d3c5447ea7b43f6b7807471acb6c60b3cae03dc4a0130dbe4371e6694e85e661e7468306fc36575c51daf017dee0dea816d23b559cc8e0c92cd444985d968d9814fde2c3da8fbb2060a1d5147f279ae44a484877feca38c48e6f07b9f154d39dbf2296b18116bb23d314bc031a640c53ce3b5544400ab78ec1a465552e953b521696fcff5200e5500e2778242de8e3164ec0bb2951eb879782b2d24b7e3d845a8ae41a7f12097e57a03e4c2d32a898d184ba0905a7c7058ed4f91578dc475c54124bfa775493f903d665e6215249b9e1dac37093283bf75845373626e3e16c1680c2ab8f6ba63dded89a2225a8863f626589b674434aa3247ac5f2f025933e6f19fa0141122c3e095b1084286e2a23902362bdd466ebdc16fe8c9ea134f2e3401110c30eb1563e0d035add1cb74082af22cbf47d77a2c44538d6dfe3ec16027c6f1ce2b07baa536676631602d98a1c20c6511b219a801371833611b7ebafd005121697887741c0da4bb9430bab3c7bc34d01249334051af5a48f98f005af726f88f1e58512ac301f941a020687014579670eb0d5cd6bf4c6e7bbff6a6d094a108c23e7e2bbbb26cce0be1b0f87205655beb8c8460e66f3b2e00918e858837d92b321953dcfde89914448b11334b8738f192a897b0d009b0a8f194c9382b52a45e912bb421bcbb7641171d802c52909b15fa68d1113835d3634e6db10803c1fc1a4b35ec7fe03276455ff1a2b3224ef7978cf5c6b3f0feeabac0cfacf9f6b53ffce00778893a36adfb6e8b51a758ccad6573b1f4725731ffbcf436ddb21c4f9c06aa284b7d1d221fc10862f2af81d2b0db4a77d49afb1bc587da477c31bc5b1b863cae6ee1a7907e061340507e9a100a7d90444c33de4a4d333e750bc95613cae82348091fc2f44604eac8422bf2b4a5e3132acce3a15a1ed9475b3fefe28e82d9c3a37d46134919374b2800b0db514f88442e34d415f0b3c2b40f318dbccf889e5da5682259535d38e517e12c3346a94d97f858a0653bf8ec3a438d467b8257e97c3cc052e5d30260f6ffc0078b3ed66ed6d2db176f093ceec74eec88257e157177fd6402feb34fa6e654326645eb8e9a9e61081b122c67b545c76030f582367ac080e9f9279c974974e47203e4d86cfb8dc5ab98073a24a9b74a157847b4206662e84a6f2f719cbc3e8352316a39bfd9a048b0a4199d5fc9a4b85b066f7f0d32e13a349b401598cf02f461a7501f7a9fb11bb6a039d9ab11030b320a3a58cf1c0ee58855b1e2fc29c125d09374a488c0fcc4a3b3cbb1ce257be54196dd3a1dc131b0e2ed5b6d66aeb549201fbd830bf21b32631e672a18edb132aa94b46ef7c469b299642e9f0c76c93ef2def0d8e433bed6fe3df273e8e4e0740d12e6ee2c78d82a9ee94e9715064efe213714501bfa9bac44a5c8764ee4f1dc46b4098a1699d5572db1129a8907e85831db4d653d369ffef17e4aacc8beb9ebfc8daef8ad0e2743af3c8ae723a2ff4822df5f8e9ef9a0dd6ea438aadf9c101d00397a5e37ee21600a7d4119c3d0c0bd42dfdc23dd9c460980755e7a69a5aed39f35e6fb1e7bbda6c8902ace86a659dba1f721645c593759d0a00625ec534562054363aa484fddd72457ca120c62126ad1f66989e3126978f9456545d9ea5f72c8b179886098942db8ad53bba5d20bd64155c15a32802416116414c130131323fcf7eac6cfa85286b5b7b658cb0a349960f495db897dd4057b8f78b0a6d768c1e0d33bc2327be3ef75213551ed2b2dc2810cf32c234a270304036b00cee61920dae5de63ca6972df355fc2b32b3a72b462586412c7af7136b7822a7b790c70bde95fe5bf995c3bb5c20ac3fc709eea2141754c1a555456f65edb85a495cc5afc6fc5ec725a04193879eb4492c698fb39523e762b048c5767dca8aa01467c5cc736426d3f652df06b0a10bf921064977170f01a3a22190f714d94da077733d0c57c7e0af3961063e0b168a827725df6e110ccfa6dd6493d5a1a9dea979d3e53b190d851574fa5040d28d8d771180a7c51d029a495632cd5534a605d4a7cc400a65e28ccee07a70d7663650d39c3e4b63c1179ecee1d5c7d623e08e03d0c658580a43024bd720540b2f922060ca92ac7081163566cf34d6b46a7edee2e257f208b4af8565b5f5b0a5fbd696d24cdf95f54a016e66cf0ac3015ce91a624cae628378965812795c779f86e3fbdc7fe4f35ab422f9e91ecf5d9a63985830e901dd60528057022e488a16c2cefe51ce19576b542a4832a3068afc679d29a8d0f96810e6fbfdc4bff968bb0b3d6d93c8e929e12c23b0ee302c9d8091ef22505dfee3013c516833a50611d130cb4b84400441b22507bcf38ae554552fddcd5f89341604211ebd297595e819e5d240a0dd9e4ebe0a273c8bcf0f79272cf7b9577db1ec14329289f64c7920e460ea1ddbda0fd975b8147539ce4e458298d1d8c1d82b0f3ef9e2fa66f3e11dd9db15b28ab9196bd24029b29357b9c3bb5986ff42b80027bbd3122f58bca12e160307ac5e93c674f8a9269973936f193f3426fb1f0817cc658d3040956f76c0917470484b8cc73c26b70389f7275b25dddc6c44d49200697550a87b1af671fe591c135592fa9aaa330c246e4d719ac779ef1127056baf031bbd2a3b969671fc1cf4ecda953e1c9aa922141dab8c2bfb28cae0e93023d382b7236d23906df29ee2dc951e3bd7b88e9fde0674eaf1c8d96574f6ea65857f193128ffde37f9e3f73415923a7aa07d45880c2399dc51571064a8ad883e241af6a7a8b44fafa11803b9ecb2610021f928d07b24f52e4d29af6055488dba665dfa99f7caf0922015cecf5c2ba18fdf8e18438c1ff01f70f7fbcb3f3fb7f8fe60d6b7132f8e9e88a5566cfc802699408510b99615ab3043eaf02a17c43a5ae5130ff650a7acf5c49de373b08ada2aeffad507731ac701dbeaa7ba1068977be5ea8d489e3750036c13b8b3a2c9e7429c93df4dc30741d85b50844e0795b5a90836a32f5272364c8adae11694aa1e9b4dbebff68203a675db5246f37708ed841afe6f17c29542cf0baf615468fc0a56d04fe296b601f01fe1a1562293cd3fa04972473f65778036d87548d2e6048050ad8395d61d022f6c9a732ccfd0c65136f952e99e781da94d341b59e1fcab1d0e7bc0d96c4b2300d38a86de72257a817b5a4b6a623d1c9be003d93e7f422cca87bfa4e9099f0c1533145067454a250352d26c74543fcb0f4ddae24fb020762be8cbf52bbe1b132899fd0c51abdfc956b43708290528b8e8f3c4deab08926c7680426ba69918006936ea0e12b4f7fd447ef4a2385fb558642e564e5414bb82b794cbbbfad7ce14d4be294951078c4a52ba7ac4935318f0e80c10a392462dde89cf3a7907e52309e56ef27afa93d3e029949854288c855a15461b97d283e2cb7b135c7ba7f68e2efa01ef9e8b6d10c5019c09118c8aaddb738ea0a43d7457ec0f5d3cf11cd9fe16bf25b169d2f90e8bbdb730515c38ab44b101b9245137787f84ec31734ddedb07ade68447482ae6cd9e8dddf15ee218d1abf8646653eadbf3d2a42ebe0944080a7fc70c88c686571bd3784bf219b115ef10909f6ba60dddd47d42d4d80e548f234e8f04a55ec9d23e40a4225d1a6d6329b427010311377d45d64a1a3c57197d075eff765213a263131bef0b20b06b53ef05cf259ca76ed6db8e98b785b50a3e085618745a09fe434ad7accc15b8477e16ce995efba2e5c0c0486297516b888210eb5686c7c3ba4fe689732b1b25b396f84d1fb9835819aa6a074ce0264a968309cf32084986eec834df563ce08cf87fa4bd23a0f3859f7eedac961bc030f6642595f2c1b58a323b57278c850b26405a69ec1c1631bdebcf061c9b50759f08681838f8c955007c187cf43349af5a774e25b07dc0cb7fc9688bfc10dfe12edb7404ccbfae855e406046bb3bcf079a8e41b1bee610f7153a7695b89546f201408c0ee038164b1b9fe0d774bd7d3d2d7e968785bcd3774437856cee80dec3495f3e248b133c148d67b5b99fbc6b98d991088e925e4b4dd36db65dce5170e7257ff62de1ddc4706d28411dcaca497003888b4f4551b7b26369a66bd264ec39211a510663fa5b87245166da13beddb30098413fc7ed6c007b493f4debd088b330cf981e6507a7171dc311e34c18ff7965ea19da752b4fd2873d7a63d2dd1bbac714582ff9a0a7cad8eb68cfc783f8a9a2241df0704a621bb4c8e464b40fa007a8e2021d8c1c5236d6bde1d3853ca452a73955e96c36af98882c7104b5dc52d32214dc1cfbba04b851852ec3fe4e62156d3c88465d6a0130e57fb6570ea9933c808aa70875ec4cc940b933c7f4cf08f2b1f9cf17d9d4b3a28190423b2cdac45fe3d432222624ef288caf25f9d6beaac78d9f28d445668c1ebaebd75af1538d7658c41413ec299cfcbd8e094f5d18df9cdb0b451d676c6d885ef25d11f138a98aa3545b21ae3d5ff81a3485c5d789249b921748baa06174f321c1c24c0595b4b99a973721a256729c238e9973a595aa4fe5bd32da62b582c60f396c311123525af0459623fcb145ed959fc62ad09f5991a457913c6f1a894fe4fe3241fff03e98caa2dd14457172e5c1a1da9c5d5ce676cab0dbdafc1dadf33de58b70e8054c354d0a7e280a27b92768f41bc01f35a7f1abce64ad795342ca850c0044e7953c5edf43f825445f9f9db9ce26b1e1e8b08bf85896e14f37d4a59f3871df546fea9c5cba8f271dc5d93babb8ba5f53b15dd0cee778de0843c31f69c996132566e399dd6e260c5a7411134b70da0e05bae5abb892ac8a90c1192c429664772460702e6b47b51798e8a955757e6fe652e45d88444e757399881a09baf4f358804d84f498825503e7d939ccc76b17dcbce96f10bd0a95a9b920ad457363daebd4dd2bdc9c938f0869abb36ad1bd2912a70d3c102a718d83ec12b9c050c1ca019ad5e3b7ac5cda1c231b8159f981bb6489f6bb5ab6d3675c51198637d793ea41c3dea7db3fd5b324e8a8f5322edc6844543d931cf087a42c13c0e6b4e0397b586c62c6765a4d220bdf438fda258dedbcb4170607bafbadb760be92f12c59533bbdc97e19cfbd0322f72e694a4dde9464f66140e03cdac2fba947bda4a7c296afc0a71d3ed375940a46f002a59ef5a374b8a5a545834812718563f426b8c893c1d17fe3010c9ac701468b9c14299de0981b092f8f8cdfdc1230b224077e182c0575b4f66b2236e32b130337887d1354473e965bf8767a2b305d1dcaf0b13829d95dd7d8369bbc682fde07fc801529db457c8e56a8ca3f05ed6ffeecef64dc4f7c3ecbb47f36cade0a735590a6f9421595c3bfea646e613489845a0c3e074fc80a05ee5705f50ccc25c242f0a5b228c04b80f0d296d29cdf7d99a8ebf5185bd3403ef0b85bc295f90e004c3cb0fb3aa0dd6bb10f92744254699be31cd4d1136aba02ccc3fa8d97cf2b678ed29ba3dd6ae960924bd6e9a109923c4fbbc3c9d19f5cd4624986dbfeb424917e8da26ec1bf99992925350f40d6ab670e8427749c31644f85e583271dd8173911c93d18852524f8ef3988e69941dac1cc50f6da0c6ce970a9ec0589b58b1eb4240d26a4f3060e12277ec565c940ea1a6c4e3c9036fafd40d0566a3d05b407b82ced2f4d04275f32badcfecadeb88adf457bcdcb94591f1c40e02529d71c2b19efdc4f514039cb7fe2de99c6dbc70f835375990940e948e5b709b8abb54d1d06071e03901b3bc173705b801682419c58dfb5f5c30b47bb339979f969617423962d731c6b87a2b19efa7d244ca1ba440d51231b2e879329313d7b9e616b8a28a3cc17045981971b3f137921d059ee3c613db5ac12fba1abcbec92a9aead8c08724cbe9612ad17051c23f95d5994c8818b49336e95c22e1e0da22ab48ff4fc209c7cc32d9b4333b903dc4476242f8d071435d04ae37f326eae921abacc9b5525abaef1b235fd1667887115dc39fed7f3a0d98e1ec5760acd596719dac91bd90b0ee07bd58f76096ad1b70210eae23d94602e53c74968f7d0b55f8f30d8e2ca28a914fdefeab2ca2c8bb328075bca3397f2134354a3b5af730b8b32f9fca86c26fe886633c04204c2fca68a592af748cd446f503d71a86eebc93a03abf571bc180fb2ecce5412814167ce8a4fef9d516b0827aa294d81b5c2df7fa071b862ec30176298cbb095949d8d16ad237ac3f58aa5e556b64eeaeeb0af5597018f7eb617bda1e9b64af6039d1ee29af3495eeea4999fd3314c0538a52749b5b332841110ab31f61e6ee99500b1030f04b70ea15950e02386fcb12de293a5f5ad39df7f1c3276bea367b684dd3a1bef9b9ec61a40370c70ae5432d50796b84d802fee8a29958b26f5070721de47ef30b5c5e909bea1a2338a2d931fa79cb521763902bf0df3d15eccfee51a7e474e7e1ef4ebc459e344974cb5cba27350ab7aed66a38a02a18a2244568e52c29380f846af8d5137c92a6cac7af6f4dce97f255cc7cdbf0d7f7ff35d723d4124ad7cc6037b0e18fc9779c921c9144bcd9e2557d0570c7d1e72ac828c7e9ac78e88cfb3f346369b4c8f79c78c1321f37077f54f86ec1a6000000000000000000000000000000000000000000000000000000000000000000f5c4c36bb64f932f926895ff20c28fe937d3b576a97a6c472084ceff644c3c514eaea77f290fea77300c64c8d30fe603f35fc830df1c0b8e2dfeefbd2082fe21bb282c157edf4f4ff1a665e4a9ad421e3a4504e3dcb5087263254cb731f9a65106ed7823890d9bd395ff63d9f478b863c03eb42a036a7a7beacf7d8809d88220e92772d59655304af9dc73d2d4220c0ef2f56017c31db54392c702b477588e40383ca8ead3ecd40342cd06c7dd51464a99ee267e24bc3f705466b389826402203bac511bf77b50d55adcf1c001b7d4603c89a105a8e6b1b0b2b80b7ec89210a10d6d4d7dd16cefdefeccbfa6fd26e191c8763f1963b2908b8410e7c6e5cf7f4293bdb0d5ddf0114b7bf4100f8f8c60942833a9650e72e2663ff2c00139eeac71b18763caa653339e680621a911520ae3b57db18f79c02a1fad6212a74a4589c20cf66ad5a192b88c0e79e43260d675c57a95d6c68dce5c12316a32fbed5b06c2eccd3bc1fa1425f2a2321674c88846f0bd35961ee8445b6c70e18b848571f920567b150676c8d044e9853bb70a0f8b5df6e1422dd0f21b7ef45eebeaf04ae5e1d6029699ff3026e71a86f36064f48b6993a6f75f6d5ee70d61d1317ff06627304dbb0959c2e59fe41298edf88c4a6ea4852c7ffe42b1eca777514243fa1e19019c7e5aba8cea1604b2efda8dc33746922cae0a7621ff660b79520f5c65ba7a016983d2f6b1a75b750d7e3fb8243595c12a321518d7d7d90f1a3962f377ee37801ffd86eb5a6fb1ef3b66e876657bda7f4d6f1470162089d654b67927503a9f71341d120177cc3f608b3b5464e3224eef126ed1fa2455b99c04e8cbedf719ced2f4ffea00c9c5d4ef11f7aa6becfc669a1b8fa113cf6e5ff626d3c95706852fc2ef9f25e6133c9dbebe47fd452a2bea676aef9ae9061c495f10b98e930059cb912c65fdeaebfd98ff86f746b0e1502f7a27b10e28a410a999487eb791014c342157f570c111e59fb9c628358968db741176ab9091b861a73152ddb59d3c412831263b81d08d47cf75a3e891e22871ac36d5509ad52f65e28d97209e1664a598628136907f0ceeeb37273bb9c7b338e3b3cc1d97f56b8579bcc11232df38cab150bed4afff72b25dd59ebe1c52a6c4617546508b03e8417e097fb97d6cb2be0970643f4c7cb94d83cc67793f0b74741fc4459cc7ca09831345890e8d12543cba90f90ad75f6b3ef5b11a52152890a7a35bca12ccb644e5fcf210b92cb09e9264e1db70ded6310992f2577dc9da58d6af809dcf7f0d60664f22bfc78d1cd8149ed108d41240691410e07876ec6487658c0cd31262787b84a8c33ab5bcc5c034c771ec6ae2d903d87e7572a8fdd054d29ddbdb81e87e978349c41310eccc538b4062e9bad4bb065bc366bc74539b23376291e1fa052b8b6b27642e8347f7da8b5d82cbfbdfa461737eb7ae96911a343f09e9796671568f930ec150882d7716b7046076fb7b10f12ca0b859d4e12102c0efd16fbc0e49888698b0193b6f90451c71e022b96b4de4e39d95017222ff84f15c02134c483f4fe706c759a1af6778ff0ec263d0983b03c7c5697d5487682d420c45257c0be9e9143cd4991402fe408d1b9127fb70d37c5a94a75b5f5e33c8f5e3a6dd75a4e972c44f4d5111010192c8f0b02bc2be167085376b683a58ce09f7594c2df76992684d46b054a05b65b56f23118a90b63f4536d2481d20af39f0104ab03ea97513338cd8c1e22a411da41225808e54c7f8f93247a97cea983d7ceb7f58d0a0613504e31a24618dea1844482662ac20582381ff8b3c9a227f0971fa6b554092738e7b08d16a4dfa5aa6b95131a0bfc4e24ea0bb608b7af5142f58463cef3b1a610837c6d596e9aa7c17ff0af6e0318fecb102538f078bd972b85f842b84e0db313d866c6bbc84e2ae3970ffcf30d34c9d1251121f8dda89fbd684829545cf55dae6895a89b26a56feaf05c7d621d678190402a7ef24304895ebe992765cd8638069078acc0cf2ad22a490fc0f2262dabee5c762b3770085c5eec4ed7957b3d00b74314cfb3b6a4984345020bed0b61c1ba50e4e568604976d1501ee360ab58a0f9fe8a552914a382f1830acc933030557d1afe915e421336f85f607a8048f7dac10b21d1e796809db415be15b8134724bb3d8590b5848f195f0ac79f92a307ba0c47806f4b7d1127225a433a4715cf7e5c3a5d6e2c00c807d7d595f0b2b483b5f51ab0b911a6998b1fe13f0889019a61ad94f9505fb4c30e472266024eb84247d641451e03f10a2f1ff023640d0ae967422e15e7082cfbad5dd60559aab5959e3d172e86dbd840541d775fd916229070b3fe8d3f0ae9782d5f821b96226bbbc88b3c77752d30aa71e7af32df8b285ce99620953d13203ba81365f754381f336d9e0195161d2348abba946abfaf06e3f6a8f421e3f403538fd9ffd680fcbf3d99a7150e834cbca329deb50b9f6629904d7ab338d687708ca3e4523cf57d26ef59958795607173c4fb20c412f73f2454d0b677a1bd99678f12e090fbb39c4b218eeeb0f6afb43cf83121b06006242f8eaaab89e20d75ba8bc670b7d34696bd9e88ceb400d62cdd1cf34f98f5e4300d386b358e79f2a9907b73e520ac7a6b316fcd18724ad56a7b5fe44d44801b1e1c30768d30ba4afb074b2fdac87c55d58f3f1f975583dd8b6b6ea2155c83265f0fc2bf8ac5f24c80c672f5ee7e0a63ba5695ab4bb0065db3ae0aab378db8708c1d8b15d903db2b44d8d935afcfda51ade3655a4fd9172e1f51d459e1c84ec06e125bdcffaed1c1c5c907b9d3d8829bc730dbf27527a704f9bfd105092177b3cf2f975d08768a8f3fbaa011d938506c2c1aaf87d6d33e95bebb436bb834e1e213189528d9f16e0fa7427e68358c3948104bbe3f3082c71a35b4dbc1d6fe560fae0d7b672c650eafdd0c9014f0802380fb2ebfd698255a6a77662416baf612ec4b071833b93321bb86ac6510c5152ed4f30435dd4afbe8e40ba803446a02a831902af567b47e9728033cc178394c9a7b97f5afd52d1050e15e48f5c783f08ffa641a9c1c9ad5e5102448fd2a8a812267636fd8c6a1baa8df617f99bbffaa5a595428488b6b1175211d51dd278163b314b62ac8bacbed8420916da2d1379bddcc7d2a019d4cebe3b1c95585eba0f97df308d1304bfd820ece00b51bb071f444fc0f22f3d29a87c8082d7fda9598b5df0a9b3beb365114b5e08907c4c9676fd97304186b8176d6d334dc3d31bdea0de63f339da6120678c00dd7959ddf69138ef8b81bfc26d0a761c92d5d729f3303f00388d8a89ee5b55222cf195f2fc078cfecc711f5ec3e2b5704ee0ac3122c27e9e15e1a6878b3217e2da430d99bae58e3668025ce5f2579561989176027a1ca3edb86bd0796acb3822c2a6fe463ee4f28937d2e0d59dff4c8271eb1dc55c14309e21318f8cc7c892c1ebdb74e73d841af18ad26b84b2a6914e811a1e640c7d060da8d0730b33606189ce7f5b13a74e3be7dce142c5d051392857e444e0a9bb4c06f7b09b298d04426492fdb21f58173f49c7b2edff1d6d6ac942a17681d2e91f550fa7f7afbb968f2d7582a23e937f2a4adef0321a87f2758e832272de354dfd8da3de57e05ed621674c1b17abab1618323981aef9bcf3cb318164c155369d2606bd5ff6c45169f0c353e3114fa0a43b8f10d0f0c88f109afefab92176fc894cbcaf0f34b660e0271528af37a3b8688508c140ca3ab549c8ad5a9bedb5b9fc9fab21d482f9280248a3ff62d1bc6800ce486aa179e074b7b4ffc251a86389780a80008076b4ce70493c83930cbfdf61fd82b05146eae00b2ea7499a1ec6f07761d9969c069b1840af8f823b3e67c51898506930ccff971617ed5465d7205b1ffbee279bafd520219c295bb8cf3f8aa489bd5f3222f9efb0bfee5be32e817a5d99a67bdcd05ed61c9477cd917fda25cd5da7fbc104260ab6d616a99359e4554ef12a08a534eda7ebe4fb270dee86e7b46e6ac2d1e7c83657d59fe9c897efe559e87183abc1c2ea46fa7bd6772ae894b065738382db3de4aa466fc8e691ddbdd94c3aec70c982f82b069d3afee7468175de9fe9c", + "public_inputs_hex": "0x0735d58c9896306041928fcb73cd2faeef2a225c22cdcac38d802de73fae0eff1739d924e1b79deab6c8ab6f90b56b0676891aedb26fd3fddd21e54c0ac32fc70000000000000000000000000000000099fdcec25af77d8d2c617140bea064c1000000000000000000000000000000001f225677d8e0b697b5cde006e1f2fc19000000000000000000000000000000002857809356ba32d83ce9536d0567344600000000000000000000000000000000f88dc81f55767fb41a3d04cd718ff7a027b5bcf3db2a429215164e576101cb3bc26e31fb2e149012d6c82552501653b805a2fab20a277a785453b5c51e89cf459b0b43fd7b33c55eafaaf91bb7dcd1db000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000021cb526d3de1f1c4624b030066eebf9c87607d85833438e8cee28ffafc9b3a41213aa3e44b78f8696b3559e6bb4f8933bd3252caa5c4bd7aacf41fdb2a0bc24d7198c8bf5b5f400835e4b0fbe9f10b56fd6b992fb16860ceb4b3c9ddedecb7deb2e1fd67a0bb9ddeb06c05b9f15618dafcff59cd380e10f5d26e6351278d32ab40000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } } diff --git a/circuits/benchmarks/results_insecure_minimum/report.md b/circuits/benchmarks/results_insecure_minimum/report.md index 2fe7ffcb4b..ce1b28c3ef 100644 --- a/circuits/benchmarks/results_insecure_minimum/report.md +++ b/circuits/benchmarks/results_insecure_minimum/report.md @@ -1,9 +1,9 @@ # Interfold ZK Circuit Benchmarks -**Generated:** 2026-08-05 19:38:58 UTC +**Generated:** 2026-08-07 16:09:38 UTC -**Git Branch:** `nargo22` -**Git Commit:** `2ae5713c04eecd7392441a0fa2628c0188af3a97` +**Git Branch:** `chunk-circuits` +**Git Commit:** `2858844afa6275d93de4a4cbd0923353d4fce8e5` **Committee Size:** `H=2`, `N=3`, `T=1` @@ -13,33 +13,33 @@ Settings for this benchmark run (integration test + Nargo circuit benches on the ### Integration test (`test_trbfv_actor`) -| Setting | Value | -|---------|-------| -| Benchmark mode | `insecure` | -| BFV preset (artifacts) | `insecure-512` | -| BFV preset (enum) | `InsecureThreshold512` | -| λ (smudging / error) | 2 | -| Nodes spawned (builder) | 7 | -| Network model | `in_process_bus` | -| Testmode harness | true | -| `proof_aggregation_enabled` | true | -| `BENCHMARK_MULTITHREAD_JOBS` (max concurrent ZK jobs) | 13 | -| Rayon worker threads | 13 | -| CPU cores (host) | 14 | -| `dkg_fold_attestation_verifier` (EIP-712) | `0x7969c5eD335650692Bc04293B07F5BF2e7A673C0` | -| Verbose logging (`run_benchmarks.sh --verbose`) | true | +| Setting | Value | +| ----------------------------------------------------- | -------------------------------------------- | +| Benchmark mode | `insecure` | +| BFV preset (artifacts) | `insecure-512` | +| BFV preset (enum) | `InsecureThreshold512` | +| λ (smudging / error) | 2 | +| Nodes spawned (builder) | 7 | +| Network model | `in_process_bus` | +| Testmode harness | true | +| `proof_aggregation_enabled` | true | +| `BENCHMARK_MULTITHREAD_JOBS` (max concurrent ZK jobs) | 13 | +| Rayon worker threads | 13 | +| CPU cores (host) | 14 | +| `dkg_fold_attestation_verifier` (EIP-712) | `0x7969c5eD335650692Bc04293B07F5BF2e7A673C0` | +| Verbose logging (`run_benchmarks.sh --verbose`) | true | ### Hardware & software (Nargo / Barretenberg host) -| | | -|--|--| -| **CPU** | Apple M4 Pro | -| **CPU cores** | 14 | -| **RAM** | 48.00 GB | -| **OS** | Darwin | -| **Architecture** | arm64 | -| **Nargo** | nargo version = 1.0.0-beta.26 noirc version = 1.0.0-beta.26+40d6574f851d926f93e0c3a271bac3e6e82ac905 (git version hash: 40d6574f851d926f93e0c3a271bac3e6e82ac905, is dirty: false) | -| **Barretenberg** | 5.1.0 | +| | | +| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **CPU** | Apple M4 Pro | +| **CPU cores** | 14 | +| **RAM** | 48.00 GB | +| **OS** | Darwin | +| **Architecture** | arm64 | +| **Nargo** | nargo version = 1.0.0-beta.26 noirc version = 1.0.0-beta.26+40d6574f851d926f93e0c3a271bac3e6e82ac905 (git version hash: 40d6574f851d926f93e0c3a271bac3e6e82ac905, is dirty: false) | +| **Barretenberg** | 5.1.0 | --- @@ -51,156 +51,171 @@ On-chain verify gas: **complete** (CRISP Π_user + Interfold Π_DKG / Π_dec rep ## Measurement methodology -| Metric kind | Source | Meaning | Do **not** use for | -|-------------|--------|---------|-------------------| -| **wall_clock** | `test_trbfv_actor` phase timers / HLC event span | End-to-end wait in the in-process test harness | Production WAN latency; per-node deployment cost | -| **isolated_nargo** | `benchmark_circuit.sh` per circuit | Single `bb prove` on oracle witness, one circuit at a time | Full protocol pipeline (different witness path) | -| **tracked_job_wall** | `MultithreadReport` per `ComputeRequest` | Wall time of each job on the shared Rayon pool (≤ `BENCHMARK_MULTITHREAD_JOBS` concurrent) | End-to-end time — **sums exceed wall clock** when jobs overlap | +| Metric kind | Source | Meaning | Do **not** use for | +| -------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------- | +| **wall_clock** | `test_trbfv_actor` phase timers / HLC event span | End-to-end wait in the in-process test harness | Production WAN latency; per-node deployment cost | +| **isolated_nargo** | `benchmark_circuit.sh` per circuit | Single `bb prove` on oracle witness, one circuit at a time | Full protocol pipeline (different witness path) | +| **tracked_job_wall** | `MultithreadReport` per `ComputeRequest` | Wall time of each job on the shared Rayon pool (≤ `BENCHMARK_MULTITHREAD_JOBS` concurrent) | End-to-end time — **sums exceed wall clock** when jobs overlap | -**Harness limits (integration):** all ciphernodes share one process and bus (`network_model: in_process_bus`); sortition registers extra nodes; `testmode_*` enabled; proof aggregation always enabled. Compare runs only with the same `benchmark_mode`, committee, `BENCHMARK_MULTITHREAD_JOBS`, commit, and hardware. +**Harness limits (integration):** all ciphernodes share one process and bus +(`network_model: in_process_bus`); sortition registers extra nodes; `testmode_*` enabled; proof +aggregation always enabled. Compare runs only with the same `benchmark_mode`, committee, +`BENCHMARK_MULTITHREAD_JOBS`, commit, and hardware. --- + ## Protocol Summary ### Circuit Benchmarks (isolated Nargo + Barretenberg) Single-circuit `bb prove` on the benchmark oracle witness (not the integration actor pipeline). -| Circuit | Constraints | Prove (s) | Verify (ms) | Proof (KiB) | -|---------|-------------|-----------|-------------|------------| -| C0 | 6810 | 0.14 | 15.25 | 14.31 | -| C1 | 53448 | 0.35 | 14.75 | 14.31 | -| C2a | 41207 | 0.31 | 15.34 | 14.31 | -| C2b | 79554 | 0.46 | 15.17 | 14.31 | -| C3a | 120078 | 0.73 | 15.46 | 14.31 | -| C3b | 120078 | 0.73 | 15.46 | 14.31 | -| C4a | 62713 | 0.41 | 15.86 | 14.31 | -| C4b | 62713 | 0.41 | 15.86 | 14.31 | -| C5 | 21464 | 0.21 | 14.59 | 14.31 | -| user_data_encryption | 53695 | 0.36 | 15.27 | 14.31 | -| C6 | 86892 | 0.50 | 14.81 | 14.31 | -| C7 | 89602 | 0.48 | 14.89 | 14.31 | +| Circuit | Constraints | Prove (s) | Verify (ms) | Proof (KB) | +| -------------------- | ----------- | --------- | ----------- | ---------- | +| C0 | 6810 | 0.12 | 11.28 | 14.31 | +| C1 | 53679 | 0.30 | 11.53 | 14.31 | +| C2a chunk (SK) | 41301 | 0.25 | 11.18 | 14.31 | +| C2b chunk (ESM) | 79575 | 0.38 | 11.60 | 14.31 | +| C3a | 120462 | 0.53 | 11.24 | 14.31 | +| C3b | 120462 | 0.53 | 11.24 | 14.31 | +| C4a | 63482 | 0.32 | 11.44 | 14.31 | +| C4b | 63482 | 0.32 | 11.44 | 14.31 | +| C5 | 21464 | 0.18 | 11.52 | 14.31 | +| user_data_encryption | 53695 | 0.30 | 11.44 | 14.31 | +| C6 | 86892 | 0.44 | 11.47 | 14.31 | +| C7 | 89602 | 0.40 | 11.57 | 14.31 | ### Artifacts | Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | -|----------|------------|-------------------|------------|--------------|-----------| -| Π_DKG | 10.44 KiB | 0.38 KiB | 3125084 | 173456 | 3298540 | -| Π_user | 14.31 KiB | 0.12 KiB | 2982310 | 200640 | 3182950 | -| Π_dec | 10.44 KiB | 3.56 KiB | 3716480 | 187004 | 3903484 | +| -------- | ---------- | ----------------- | ---------- | ------------ | --------- | +| Π_DKG | 10.44 KB | 0.44 KB | 3129772 | 174516 | 3304288 | +| Π_user | 14.31 KB | 0.12 KB | 2982274 | 200640 | 3182914 | +| Π_dec | 10.44 KB | 3.56 KB | 3716573 | 187052 | 3903625 | ### Role / Phase / Activity -| Role | Phase | Activity | Metric | Duration | Proof size | Bandwidth | -|------|-------|----------|--------|----------|------------|-----------| -| Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 206.57 s | 114.50 KiB | 115.56 KiB | -| Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 189.94 s | 10.44 KiB | 10.81 KiB | -| User | P3 | per user input | isolated_nargo | 0.69 s | 14.31 KiB | 14.44 KiB | -| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 0.50 s | 14.31 KiB | 14.50 KiB | -| Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 58.93 s | 10.44 KiB | 14.00 KiB | -| Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 54.50 s | 10.44 KiB | 14.00 KiB | +| Role | Phase | Activity | Metric | Duration | Proof size | Bandwidth | +| --------------- | ----- | ----------------------------------------- | -------------- | -------- | ---------- | --------- | +| Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 149.00 s | 114.50 KB | 115.75 KB | +| Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 121.53 s | 10.44 KB | 10.88 KB | +| User | P3 | per user input | isolated_nargo | 0.59 s | 14.31 KB | 14.44 KB | +| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 0.44 s | 14.31 KB | 14.50 KB | +| Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 44.99 s | 10.44 KB | 14.00 KB | +| Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 41.80 s | 10.44 KB | 14.00 KB | -_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **5.97 s** — not comparable to P2 wall_clock row above._ +_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **4.59 s** — not +comparable to P2 wall_clock row above._ ## Integration test (`test_trbfv_actor`) ### End-to-end phase timings (integration test) -| Phase | Metric | Duration (s) | -|-------|--------|---------------| -| Starting trbfv actor test | `wall_clock` | 0.00 | -| Setup completed | `wall_clock` | 0.87 | -| Committee Setup Completed | `wall_clock` | 7.03 | -| Committee Finalization Complete | `wall_clock` | 0.00 | -| Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall) | `wall_clock` | 189.94 | -| ThresholdShares -> PublicKeyAggregated | `wall_clock` | 206.57 | -| E3Request -> PublicKeyAggregated | `wall_clock` | 207.08 | -| Application CT Gen | `wall_clock` | 0.01 | -| Running FHE Application | `wall_clock` | 0.00 | -| Aggregator P4: Aggregation pending -> PlaintextAggregated (wall) | `wall_clock` | 54.50 | -| Ciphertext published -> PlaintextAggregated | `wall_clock` | 58.93 | -| Entire Test | `wall_clock` | 273.92 | +| Phase | Metric | Duration (s) | +| ------------------------------------------------------------------ | ------------ | ------------ | +| Starting trbfv actor test | `wall_clock` | 0.00 | +| Setup completed | `wall_clock` | 0.85 | +| Committee Setup Completed | `wall_clock` | 7.03 | +| Committee Finalization Complete | `wall_clock` | 0.00 | +| Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall) | `wall_clock` | 121.53 | +| ThresholdShares -> PublicKeyAggregated | `wall_clock` | 149.00 | +| E3Request -> PublicKeyAggregated | `wall_clock` | 149.51 | +| Application CT Gen | `wall_clock` | 0.01 | +| Running FHE Application | `wall_clock` | 0.00 | +| Aggregator P4: Aggregation pending -> PlaintextAggregated (wall) | `wall_clock` | 41.80 | +| Ciphertext published -> PlaintextAggregated | `wall_clock` | 44.99 | +| Entire Test | `wall_clock` | 202.39 | ### Multithread job timings (`tracked_job_wall`) -| Name | Avg (s) | Runs | Total (s) | -|------|---------|------|-----------| -| CalculateDecryptionKey | 0.01 | 3 | 0.02 | -| CalculateDecryptionShare | 0.03 | 3 | 0.09 | -| CalculateThresholdDecryption | 0.03 | 1 | 0.03 | -| GenEsiSss | 0.01 | 3 | 0.03 | -| GenPkShareAndSkSss | 0.01 | 3 | 0.04 | -| NodeDkgFold/c2ab_fold | 31.62 | 3 | 94.87 | -| NodeDkgFold/c3a_fold | 118.02 | 3 | 354.06 | -| NodeDkgFold/c3ab_fold | 12.07 | 3 | 36.20 | -| NodeDkgFold/c3b_fold | 118.28 | 3 | 354.84 | -| NodeDkgFold/c4ab_fold | 12.19 | 3 | 36.56 | -| NodeDkgFold/node_fold | 29.71 | 3 | 89.12 | -| ZkDecryptedSharesAggregation | 1.94 | 1 | 1.94 | -| ZkDecryptionAggregation | 52.54 | 1 | 52.54 | -| ZkDkgAggregation | 5.46 | 1 | 5.46 | -| ZkDkgShareDecryption | 1.18 | 6 | 7.10 | -| ZkNodeDkgFold | 172.31 | 3 | 516.94 | -| ZkNodesFoldStep | 6.13 | 2 | 12.27 | -| ZkPkAggregation | 0.52 | 1 | 0.52 | -| ZkPkBfv | 0.25 | 3 | 0.74 | -| ZkPkGeneration | 3.11 | 3 | 9.34 | -| ZkShareComputation | 2.98 | 6 | 17.88 | -| ZkShareEncryption | 5.73 | 24 | 137.44 | -| ZkThresholdShareDecryption | 4.14 | 3 | 12.43 | -| ZkVerifyShareDecryptionProofs | 0.07 | 3 | 0.21 | -| ZkVerifyShareProofs | 0.14 | 5 | 0.70 | - -Sum of tracked job wall time: **1741.37 s** — **not** end-to-end latency (jobs run in parallel up to `BENCHMARK_MULTITHREAD_JOBS`). - -### NodeDkgFold sub-steps (`tracked_job_wall`, per fold prove) - -| Step | Avg (s) | Runs | Total (s) | -|------|---------|------|-----------| -| c2ab_fold | 31.62 | 3 | 94.87 | -| c3a_fold | 118.02 | 3 | 354.06 | -| c3ab_fold | 12.07 | 3 | 36.20 | -| c3b_fold | 118.28 | 3 | 354.84 | -| c4ab_fold | 12.19 | 3 | 36.56 | -| node_fold | 29.71 | 3 | 89.12 | +| Name | Avg (s) | Runs | Total (s) | +| ----------------------------- | ------- | ---- | --------- | +| CalculateDecryptionKey | 0.00 | 3 | 0.01 | +| CalculateDecryptionShare | 0.02 | 3 | 0.07 | +| CalculateThresholdDecryption | 0.02 | 1 | 0.02 | +| GenEsiSss | 0.01 | 3 | 0.02 | +| GenPkShareAndSkSss | 0.01 | 3 | 0.03 | +| NodeDkgFold/c2ab_chunk_fold | 19.21 | 3 | 57.63 | +| NodeDkgFold/c3a_fold | 73.76 | 3 | 221.27 | +| NodeDkgFold/c3ab_fold | 7.94 | 3 | 23.82 | +| NodeDkgFold/c3b_fold | 74.19 | 3 | 222.58 | +| NodeDkgFold/c4ab_fold | 8.05 | 3 | 24.16 | +| NodeDkgFold/node_fold | 18.83 | 3 | 56.50 | +| ZkDecryptedSharesAggregation | 1.52 | 1 | 1.52 | +| ZkDecryptionAggregation | 40.26 | 1 | 40.26 | +| ZkDkgAggregation | 4.22 | 1 | 4.22 | +| ZkDkgShareDecryption | 0.81 | 6 | 4.86 | +| ZkNodeDkgFold | 109.02 | 3 | 327.07 | +| ZkNodesFoldStep | 4.65 | 2 | 9.29 | +| ZkPkAggregation | 0.36 | 1 | 0.36 | +| ZkPkBfv | 0.21 | 3 | 0.63 | +| ZkPkGeneration | 4.40 | 3 | 13.19 | +| ZkShareComputation | 22.39 | 6 | 134.36 | +| ZkShareComputation/batches | 12.17 | 6 | 73.03 | +| ZkShareComputation/chunks | 1.90 | 6 | 11.42 | +| ZkShareComputation/finalize | 8.29 | 6 | 49.73 | +| ZkShareEncryption | 3.99 | 24 | 95.65 | +| ZkThresholdShareDecryption | 2.96 | 3 | 8.87 | +| ZkVerifyShareDecryptionProofs | 0.09 | 3 | 0.28 | +| ZkVerifyShareProofs | 0.12 | 5 | 0.62 | + +Sum of tracked job wall time: **1381.49 s** — **not** end-to-end latency (jobs run in parallel up to +`BENCHMARK_MULTITHREAD_JOBS`). + +### Operation sub-steps (`tracked_job_wall`) + +| Step | Avg (s) | Runs | Total (s) | +| --------------- | ------- | ---- | --------- | +| c2ab_chunk_fold | 19.21 | 3 | 57.63 | +| c3a_fold | 73.76 | 3 | 221.27 | +| c3ab_fold | 7.94 | 3 | 23.82 | +| c3b_fold | 74.19 | 3 | 222.58 | +| c4ab_fold | 8.05 | 3 | 24.16 | +| node_fold | 18.83 | 3 | 56.50 | +| batches | 12.17 | 6 | 73.03 | +| chunks | 1.90 | 6 | 11.42 | +| finalize | 8.29 | 6 | 49.73 | ### Aggregation jobs (`tracked_job_wall`) -| Operation | Avg (s) | Runs | Total (s) | -|-----------|---------|------|-----------| -| ZkDecryptedSharesAggregation | 1.94 | 1 | 1.94 | -| ZkDecryptionAggregation | 52.54 | 1 | 52.54 | -| ZkDkgAggregation | 5.46 | 1 | 5.46 | -| ZkNodeDkgFold | 172.31 | 3 | 516.94 | -| ZkPkAggregation | 0.52 | 1 | 0.52 | +| Operation | Avg (s) | Runs | Total (s) | +| ---------------------------- | ------- | ---- | --------- | +| ZkDecryptedSharesAggregation | 1.52 | 1 | 1.52 | +| ZkDecryptionAggregation | 40.26 | 1 | 40.26 | +| ZkDkgAggregation | 4.22 | 1 | 4.22 | +| ZkNodeDkgFold | 109.02 | 3 | 327.07 | +| ZkPkAggregation | 0.36 | 1 | 0.36 | -Sum of aggregation job tracked time: **577.40 s** (parallel CPU work; not P1/P2 wall clock). +Sum of aggregation job tracked time: **373.44 s** (parallel CPU work; not P1/P2 wall clock). ### Folded on-chain artifacts (exported for Π_DKG / Π_dec gas) -| Artifact | Proof (bytes) | Public inputs (bytes) | -|----------|---------------|------------------------| -| dkg_aggregator | 10688 | 384 | -| decryption_aggregator | 10688 | 3648 | +| Artifact | Proof (bytes) | Public inputs (bytes) | +| --------------------- | ------------- | --------------------- | +| dkg_aggregator | 10688 | 448 | +| decryption_aggregator | 10688 | 3648 | ## Raw circuit benchmark JSON (Nargo) -Source files for the **Circuit Benchmarks** table. Persist this directory with `crisp_verify_gas.json` (and optional `integration_summary.json`) to regenerate the report without re-running the integration test. - -| File | -|------| -| `dkg_e_sm_share_computation_default.json` | -| `dkg_pk_default.json` | -| `dkg_share_decryption_default.json` | -| `dkg_share_encryption_default.json` | -| `dkg_sk_share_computation_default.json` | +Source files for the **Circuit Benchmarks** table. Persist this directory with +`crisp_verify_gas.json` (and optional `integration_summary.json`) to regenerate the report without +re-running the integration test. + +| File | +| ----------------------------------------------------- | +| `dkg_esm_share_computation_chunk_default.json` | +| `dkg_pk_default.json` | +| `dkg_share_decryption_default.json` | +| `dkg_share_encryption_default.json` | +| `dkg_sk_share_computation_chunk_default.json` | | `threshold_decrypted_shares_aggregation_default.json` | -| `threshold_pk_aggregation_default.json` | -| `threshold_pk_generation_default.json` | -| `threshold_share_decryption_default.json` | -| `threshold_user_data_encryption_ct0_default.json` | -| `threshold_user_data_encryption_ct1_default.json` | +| `threshold_pk_aggregation_default.json` | +| `threshold_pk_generation_default.json` | +| `threshold_share_decryption_default.json` | +| `threshold_user_data_encryption_ct0_default.json` | +| `threshold_user_data_encryption_ct1_default.json` | ## Notes -- All nodes are executed on the same machine in this benchmark run, so inter-node network latency is effectively 0. +- All nodes are executed on the same machine in this benchmark run, so inter-node network latency is + effectively 0. diff --git a/circuits/benchmarks/results_secure_agg_micro/benchmark_run_meta.json b/circuits/benchmarks/results_secure_agg_micro/benchmark_run_meta.json deleted file mode 100644 index a9fab9ab34..0000000000 --- a/circuits/benchmarks/results_secure_agg_micro/benchmark_run_meta.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "benchmark_mode": "secure", - "bfv_preset_subdir": "secure-8192", - "committee": "micro", - "proof_aggregation": true, - "multithread_jobs": 13, - "verbose": true, - "nodes_spawned": 20, - "committee_size_n": 3, - "committee_size_h": 3, - "committee_threshold_t": 1, - "network_model": "in_process_bus", - "testmode_harness": true -} diff --git a/circuits/benchmarks/results_secure_agg_micro/crisp_verify_gas.json b/circuits/benchmarks/results_secure_agg_micro/crisp_verify_gas.json deleted file mode 100644 index 8dcf3f1deb..0000000000 --- a/circuits/benchmarks/results_secure_agg_micro/crisp_verify_gas.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "verify_gas": { - "dkg": 3125343, - "user": 2972977, - "dec": 3640899 - }, - "source": "folded_proof_export_plus_crisp_verify_test", - "artifact_sizes_bytes": { - "dkg": { - "proof": 10944, - "public_inputs": 480 - }, - "dec": { - "proof": 10944, - "public_inputs": 3552 - } - }, - "calldata_gas": { - "dkg": { - "proof": 170052, - "public_inputs": 6144, - "total": 176196 - }, - "dec": { - "proof": 169896, - "public_inputs": 17280, - "total": 187176 - } - }, - "integration_summary": { - "integration_test": "test_trbfv_actor", - "benchmark_config": { - "mode": "secure", - "bfv_preset_subdir": "secure-8192", - "bfv_preset": "SecureThreshold8192", - "lambda": 50, - "proof_aggregation_enabled": true, - "multithread_concurrent_jobs": 13, - "committee_h": 3, - "committee_n": 3, - "committee_t": 1, - "nodes_spawned": 20, - "network_model": "in_process_bus", - "testmode_harness": true - }, - "proof_aggregation_enabled": true, - "dkg_fold_attestation_verifier": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", - "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 13, "cores_available": 14 }, - "operation_timings": [ - { "name": "CalculateDecryptionKey", "avg_seconds": 0.060314222, "runs": 3, "total_seconds": 0.180942667 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 0.165855291, "runs": 3, "total_seconds": 0.497565875 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 0.24252575, "runs": 1, "total_seconds": 0.24252575 }, - { "name": "GenEsiSss", "avg_seconds": 0.063742361, "runs": 3, "total_seconds": 0.191227084 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 0.109152652, "runs": 3, "total_seconds": 0.327457958 }, - { "name": "NodeDkgFold/c2ab_fold", "avg_seconds": 7.286771014, "runs": 3, "total_seconds": 21.860313042 }, - { "name": "NodeDkgFold/c3a_fold", "avg_seconds": 59.072488527, "runs": 3, "total_seconds": 177.217465583 }, - { "name": "NodeDkgFold/c3ab_fold", "avg_seconds": 7.221067, "runs": 3, "total_seconds": 21.663201 }, - { "name": "NodeDkgFold/c3b_fold", "avg_seconds": 49.741351916, "runs": 3, "total_seconds": 149.224055749 }, - { "name": "NodeDkgFold/c4ab_fold", "avg_seconds": 8.275522861, "runs": 3, "total_seconds": 24.826568583 }, - { "name": "NodeDkgFold/node_fold", "avg_seconds": 14.942494861, "runs": 3, "total_seconds": 44.827484584 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 2.7863485, "runs": 1, "total_seconds": 2.7863485 }, - { "name": "ZkDecryptionAggregation", "avg_seconds": 47.8196105, "runs": 1, "total_seconds": 47.8196105 }, - { "name": "ZkDkgAggregation", "avg_seconds": 19.611985042, "runs": 1, "total_seconds": 19.611985042 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 23.107511138, "runs": 6, "total_seconds": 138.645066833 }, - { "name": "ZkNodeDkgFold", "avg_seconds": 146.543567555, "runs": 3, "total_seconds": 439.630702666 }, - { "name": "ZkPkAggregation", "avg_seconds": 24.754375917, "runs": 1, "total_seconds": 24.754375917 }, - { "name": "ZkPkBfv", "avg_seconds": 3.826272513, "runs": 3, "total_seconds": 11.478817541 }, - { "name": "ZkPkGeneration", "avg_seconds": 54.048450264, "runs": 3, "total_seconds": 162.145350792 }, - { "name": "ZkShareComputation", "avg_seconds": 51.343368145, "runs": 6, "total_seconds": 308.060208875 }, - { "name": "ZkShareEncryption", "avg_seconds": 119.190663328, "runs": 36, "total_seconds": 4290.863879835 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 91.265730263, "runs": 3, "total_seconds": 273.797190791 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.119331555, "runs": 3, "total_seconds": 0.357994667 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.317396308, "runs": 5, "total_seconds": 1.586981543 } - ], - "operation_timings_total_seconds": 6162.597321377, - "operation_timings_metric": "tracked_job_wall", - "phase_timings": [ - { "label": "Starting trbfv actor test", "seconds": 0e-9, "metric": "wall_clock" }, - { "label": "Setup completed", "seconds": 2.980480584, "metric": "wall_clock" }, - { "label": "Committee Setup Completed", "seconds": 20.093927583, "metric": "wall_clock" }, - { "label": "Committee Finalization Complete", "seconds": 0.001328958, "metric": "wall_clock" }, - { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 163.129884, "metric": "wall_clock" }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 603.098846875, "metric": "wall_clock" }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 603.6922475, "metric": "wall_clock" }, - { "label": "Application CT Gen", "seconds": 0.298300125, "metric": "wall_clock" }, - { "label": "Running FHE Application", "seconds": 0.001343791, "metric": "wall_clock" }, - { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 50.75193, "metric": "wall_clock" }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 176.864929208, "metric": "wall_clock" }, - { "label": "Entire Test", "seconds": 803.935495959, "metric": "wall_clock" } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000bd233316133760191000000000000000000000000000000000000000000000004cd042387c7e76741000000000000000000000000000000000000000000000000c0a95baa318bf7950000000000000000000000000000000000000000000000000000eafe989190ab00000000000000000000000000000000000000000000000f4f85589bfde2c15c00000000000000000000000000000000000000000000000937e155db4ab4cda90000000000000000000000000000000000000000000000098b0ae0c2c3a32739000000000000000000000000000000000000000000000000000250fed55753d6000000000000000000000000000000000000000000000002932574ea65c59469000000000000000000000000000000000000000000000001d2627c24bbe173b2000000000000000000000000000000000000000000000000fd394824418aebd30000000000000000000000000000000000000000000000000002eeff4248c986000000000000000000000000000000000000000000000003dc5e5c57a7636d0e000000000000000000000000000000000000000000000000a7eda52c27e0be5e00000000000000000000000000000000000000000000000ff3acfac6342c0d5e00000000000000000000000000000000000000000000000000010511ca1b2e2c2c88f85ace6098930170a7f72ae5090886f78577513621aa6c231b86f9764b0922104789fe82b7baa4017d26b9a0e245e2ed5dbfc065aa5b802260baf7deba3b2d7fbaf835e5696af3f5fd069fa2c671ec53f8c2c2e24500b34191069123b4a002905654f910d2dbd67eb302c5d214796ca04df744d36b03611da063f1e227972575b01ba241b58070f2055b70a998d7313d2c9e3e55a166095864faa78ccbc2116a337fa2bf2615cc3e06c5882ae73e1482cfac8472397f0f5cb0447b092b1326b6ac92cfb7fecb05d2e8a9ede6e7a307dfebdcb05e9b96a4652957eb733b312b22130acabc0811aa530d060083d2cd3126b6937a19728d84db0427591de7120f6596d709315fae9cf83cf5b3d6dcb65b7ed9fe80bbee5966e75e6681b7f8e00ae1f6bb39051093d17915e71df5b0423ee39b0950abb34a8adf84ca7a2fa5162482cb550df61b5385ad9c64d97f705dc1d3d2c74291249bfbffcdb03a0010bc1fef777fe30e812035b8bcb70dac6ef3e80a3e419057ab501ee5d2347c4cbfc9274cb123beddfd7ece7fc849d9da4461f6b9a0646443f8893b7cc5fa62fdb350286ca019562cec1243bedbd6bc47ab45696dffadd2a77fc89080241671e9d055295281730fac3e7806becb3102324c58a579376cf7cc75e26d300a5cd1bf0f2f15a8c1b815e0b10ed9c3dbc2011417232d91d4ba36ae56b9bde57cc8ad2830920bb88ebee4f5244ea8111b94cf22bcf785657a3d71997e4f0297881c2e62651d1b9e8c937740ded53ccf51c54f9e795e0af9b4583e34f9551403dbfd33a497f80d189d8ae82bf240746eb430420ad354bb720a0656d94033b6bc12f8625b41bf1cb20922e437ed7828c462b40322a641c45fdf752246080c5a0603f03183208504989246a9d2e678603a8f9f4a20eb90928d43f758e1ca5dcf5d255309a699ac0e6a29fac9062524969e0b411517eab3eb8e59afbc67cbf6c6b07bfdc14a353b0bd8206cbc26fc03c523b09e88667a6f5771143250bffb5af452be4c84e8a1062feb73521ad2daa52d0ffa204da0652d5d3e7b6b62cbc1889495a9a31d5398162533f2fa6c568c3cd8a267e5f171bc35482ad6eb7f8c55a19fb9f6d3e46e61a50a0df9aa552e30e4437d6e0111c2bcf86841f81a1d55d9443b35ee471f95243e0c2f8de4e1d6cd79397bb65f9cb31aba90b88ad990f7af3cb0ffce69c82bbdcf115c4415174f366734c7ffe5dc41178a9d7c79c53d265930a85f73f7e28fc50c0fbd52fde0547a43a840b0b9c68397ff9add5f08670436ae7058af376e1267772a77b281840fcc3b087f6c9a83fd5c7d37290742607a3e11e14fcfd7bc1e1754248fd5f48ff35dd68b6b91e1db8972fdc41a11ec0ffa929354a28fb84e5b9a3e25c339ab1d8d2595db5e02f801699cd3ce271bff94f36de12df9a52169fcfd660df72b469be47060657ac88580627ba7a728cc6f7820f5d1a395459a5389acc42ffffb83898ef6e9c6e4baed14e5fe754d00d8c18fda3c8a02857a74d49156fa1f33f8c3053fa7d028419ce7b2d740f4c1d4ae8c9085ec1e12507bd60a283eda2063e8b528529b4c4e0b91d2f074866ea9deadc3fc14a29ee17c23ace8b5b9930d39dcb1ddd10456e9aa2d3a57d1f23052b536614ef655fb3da6d4ed9deee086089c68a8a5ce7641753de546445ee36803f551e6ffc64d6001aebd40f3343cab28bf2cd90d187d270b86d92e94d1b32bc27af1dd52c2f85c1943c5b065c7541407dce38eb327a8695fb6132bf2971f9a05b54ef620a332a3ceec7e48bb33ee6c0cd43d69381c51c2c9c7135800cb051c1dc122c0339a55ad802ab0b3530d33a00b70ac323ab8db68b6516df1d8750c4dedcef214cedc1d17dd55eaf5a58ff0980d466eb419f3fc4d7b6940585ceb122e0eddb918d817fe05d93f51d57b1ab2241e5e2ce85445d90b549c66aec0acf25ec7f8f73cd35fadf411e7a43b45727fb52bb25a56d97ba7551f39fd51342e846db89a31df2e32a22c4738883984e8c2a42750ed3588bdef4b7a38e8040f26ca79932e56c128374e2c9bfd911ecf26af472ddd91459b2045efb53379394e540d91c85aafcdd33276d2ce0253cf30851aa31adb1a824e55eba89d38a3e6923a95a0420a5aa5e6a04373256b78a3edc3012828eabca70af39e3ef8dc5641b7a5c99264da7cef39a45ea655bc8fcd39f49b6e1854895d405ecbb925a878bbfbc3b84b6bce331704d04c4bd928806ad2700f6a2b4cedfb4754ba6213e3c5dab0a5ca9cfb115e30212901cf13155c2d4e0a34b6037f56e6a6dfc60c6b10f3d48e17f1e672cb07b90f580e0f541012d8c4c1eb14130f14d1f04e91b543bfcef5b8b2e71f7fed7f34b45c9dd72db66631adf392f703ed647c6535d2523406d8ad6577abb6c408ed785d18716226fd187bd4845f3020d78f9fee6869ea9027d69790841fc5ed615e486b79766681739ee6ee0add2a21665ecaf41afb2473466f13f847b8b15e843557a2a6a9fbe5d4a9c05a54d3c1214bab93b7d5fb8866572ef2d65d9a6295b82498c7100c718fa9475c499211a205b1c1bf5487825ece6e7841e028ba7eb15119e33d88e0533a5153aa59465db90555ea4eda3e5b6bf4b52863953d2daa4812bca663ea0abb1db62b54fadb61c3170a3e654083efa9f90e55a883b555b33efff0f35dd3070395fbda63d30b9e101448f395f20631ef693a0669b8ef237134c4174425a7121e77bf0d3945c5a2c81e9eb2ed4fbb6f6a5bfcf44f54a1e1e790b1cba07124381debefa312d40116fa08f9151c0286e10824723d1a66773458f26c6308f38da87b33d4ea8a1115ddbf02c48b8df6c5e306a6b3f244cdec97f3214d1fb35ddfe51be997289107a63a3c1f30172928a6250437eaee7e00c38f3afc71e209afddb4e63b906324201e80a429cd98ceb3eccc533cdc861916e98751003ac2ce9eb4917156867eed66d6d0210cf8b340d8971a40b31a6b4a039140cbe496afa2a9b5233386de7c1f885e8bf615d9ff58758c7862575a577180958b980b240b853b8dcbafe66cbbe9d7531ebb26e7ca822d9c4688b4227af9c5f7e377053294a578a2fef2b28c1cbf60060fb1116adfbfbecbec5fda3dfe6fd2449ec4192c43692bac3ba4bb98685f4cbac5131e2d16a1ce646c845396c37fed1bb7b0e1b6e20cbb76e478199244c48a780619198f6a2cec1e9483d9d505945e971fbbb0766e2b0b1fdbf7e6f7571dcc40723513c585c11a3d145b11a2162a037618b877d735592c4c90b093ac3c80819b9c7b18da36ae47746f1da8856ce213bd72909320db8c87e8f5a3a5b3f9d9e51530e4252cb6a810ba0f6b7041a94e317232bfac1241b2635c8f158cc15df350e2644f07fa101afc651e9761e2d7ed115664e9194411c49b8ec0d5bffd4d8bc7097c00275ef1c7e0127e4f195a7c23ef08670363dab58de7263856480f59428995b1ad14a8483016e70e38d5536335202266bfeeb3f877e1fde38a25ccc9305392a00d1c9078d16611fe1f2a5a6e798fc0424fbbb755a183ee1ad94841c86c86891ff029380730549e7baec88de39f4e242d8f36039ae7d5aea0b3f0abd8a125a9424500322bdafd7b629d1f76c488c6610e3dfbcb751f9b6828635271cc0e08969f8d1886f67397d5ec56a4aee15c79b819d7d863ecfa16e473d0fe0ea4d8fae5d3af132d0c75c54dc9e2c68c1110235f49ad3acbb1cf8a69a754ab1d998fa63e93190fb62b409996d1d4f0a41075175bedf8e824db79caba1239eff0535d8a2a99bc22fb28f30d643fad9b4885f5586c6ac4a4e1c31bb9217b4ba4a00cf5d2d16d27117256b1582615b08e29aaff25c5e0f1c107c0c20d25461a7795b028a4d787e12f32ec3ec74081e7d32db71bd3584f5e49243c385b2c1104c860a172e69309452f9d9ffe6f3694e409ad1b77fd8c32066dcca4edf8885e9312da3faa5df33f1426044e2a2ca8fc4b049c1d1998debeda0486b4a2d706ce6d6ad321a17c78400b1268db45a8268d43313e16da0b0ed8e95459287363c25162a22dba774e65b45601c84572efc0117c980cb05b0a714f5a540c829f29c3eaa243fcca246aa1bb8a2eead7be00e5693155c1c5f9cec08911b60f4ab78ee96646a722bcfdbf8108131aa51a3ba92e77782eb6adb6d80b377e5f1d5247c1012c3a8a92aea66deb53ff00ea565e5e1835645fd7d0854458d63102a336207a96139ad25b7338c70095de228305c746d7ed9f8d3cb5606682c4b7a42b90e792bf2341f7c3f7cbcbc1ea45130068122f2930d60e53da4d715655f871348a60db6555a140e6b6c2ad8459c821691aea967ccf5e33d3cbdee44b19a7b2623ecc0de3dd4f6a08b899a36dbb5917b0ad0957328912b433b674122c000942f6c169a37bd73896453c4cb5820cc1073cd3c14779951b32d32d62b43c5027ac5f7f8380ecfb0ac84693c062a167652c7fbf45ce1e11cbb845cbbb7c79d5a2b22a58f3188b8c0d1a9a2226a90172491a6589d8d54d118626d5a7e7116399fff7c6d5269042500d4b284937181b9316190f871997a8233817fb1e2561fa9c8760506447dcc9d9ad2d08744bbabf19d124e80583b3fd23b4e9d9cd50fd310539845e13e5a2d27f32f349a5faf0c2eeb418f248f3b463584b953e386c6c6a72ab1687650224649aa8c617592cf93612792d62d045455d65c32140a7bbb8334e9c362767650bfdc93dae2cf85abfc79797296d4c161ad9afad0c1a10daee6e24132986af038db53b84ba7134dad9d7b0842230e3e0257f30951424c5f3d3cd5bc5cff353737736ddaaffb8643dd21ee59b041af422012452b88f1fa5c395405b1689eb6e91575f7a7a4ca927a938f68cac023b04bf4f264e52f056a289012f99dff02c1db2a3fdf19a4bfd9ffcc1d93b800049f38fbb2b4a3435ace62d6e913833cd506f49f396ebad923a4a4159186e350801b2682821401731d49cccf89720972d673dc1e8addf9cf1fddc5e909442470028443306460dc7959327adc9678a095c280e034e55fd6351f17e14167fd83d260cc1b143d5fe9746548580c2a6ed2aefc4c1578b24dae2eac5566125726a7d273beb9e4f4b95a1bfc13ffacc990d10aba35eefbca151089b02b8f9765123a41508d11852e7e1a043af39c90dd7cdc7dc48537bc2ec8249e7385edf6d24d53b14a9722baf448dc1a87bd28e6f8d9175dc27031055c31295233859e3ab5e0360170b7e9dda0d83e4d12304fad65120c241121d21eebfca8a77dc0eb3fbd88f8705392dcdf2844b614a1c6793b953e9fae15853b76a5e10a8e7d0824c29ecc382204e5eede626048bf1a0feafc1c40f366eedb458ed11c66087f50a74616e001d06a3e5ef32eef8da5dde6c79a6002b5c2d22a4595a61ec35ac0ece52c3ac15d823b71eabc645fbe8a42cc494a719f571ea0eed6c66fb308e81d9431bff9531e70e317b916baed829e74dc4e27bcab18bc01c2fc235a52f23cb0a9624b30b02420e8faf738b5ab29eac1ae8476d0980b8fee696a8ce0aef5e55d14259d7694f1511209e7356f7bb8ae62c868a8ac8b64cf15bcaede815d85ff5e49e348f6eac2b01b43a9ec10e379ece042e62453cd876989da18ee2d5bbf767159d595faafc2b0d8e5f410f2b979ff760cb562c4fdfb6aff73ba165ec4084335089c589ba0d1328376430f45ff6feea719317f5458ba49b78370d441fd7b91e468d201ff31cae01487d84f9265f6fceaee32cc5e3aad36d0824b6d79bd87b0bb1e17e956ba52721db06c159ef8ce2ace4fb6eec50a46cf7ab9bf69fea33b43df03ef6d472f157064eb51d26572bb9b3ec49f05522ba675109b8864d99e2db94f865cdf0fad9bb286f2a7c6cb0e87dbe8f98f39a7353854ea8323b6be0a7296d5f8d1ccdfb13750d41c03d303839615efa57f91d475f515c48546f1acf05833b2bb8bf89b38c701e5ae6e1a61cf9f70b287b529dc3ce9dd44c42c31ae85bb2fe0da427d125fc392073d4bed7772b1fe94982c53e89b4e49ea654554992e1b68fdb7b164a41e6cc14570958d4c0a81b936342fcf8573a8bb8a8823506e6230d525a2e14fb78566c0d8ee8357db2483bcec6246a1a8f3dcefea29f96d27850e6a38235754ae5fed712026e44da2544e01c4e5cd71ca3d6be920b352c57d372b85f97ac0eef7531592cc1db1e5694d5bcb6802294a0db4b0fa1a613f15752fcc789747541bf7ebd4a2ef2cc5a3b26128a80ad37da64d99fa104f89f0352017cd6028fd2938d8ab50c175ab862283227666077a494213df3029758ffa02b994e2f501c3bdfb7d75a942962bc47477c70d4a742503a3246bdb2ab49069e385d7e524d0e2936235b9bdd0342010d4af6483b3e3b8bf07382751cd54ae3db4bb12278dc570f35fe36b0e81c5b9fee51e6026512deb69f94d38467d621b48b9bd22b5625e0c3c0ed3582680c13f5a9f57d49e32e2f5060b669193f210f436d27ed3e07e26cf97ff96252922f3e4fa2b4940a57eb0595a72c7cb69a23cb816cd758a7574183f3631867470019299dba34398b5e7bf4ecf10a29394f0b64edd4d832c94379eec40a1d38d50e184d196e0b18cbc747a6c18daf67aaffbf0fc659cd68087adef9287c5a779b4f2fe4fe3c5b38cacec902fd8a977c873891ae17d3675504c55f58c68fbbb17a0d0459e2e567a80979458a1fae7b7ff6379a662f178c5b001eaaa00a8a79386a301d38f78ee428713a229a615b0c0e19a034c14528660a11f74c7713e840b203d915ad928d67417b30653770639267209e626b7789cf9c4be00c409e4b9b3fdc860bdd9b6dc989ff6cfd1f8d84ce3b516bb3a985887a9569d27f82e1935add639923618fae5897fbef40d86b9c129fb534b9c194aaade3be069f6b9c2a70ea8c8f18050a152a41d94dfd5f8aa404ead86fbbb70126dbb3816d5fe890f7b1a1182c12edaae21e5769fe098e9e1e34d4b921065590fb14cfeccddc1944581520eb1c15dad8fad5f92097c72bb00e3f7b75a296ef697dc1737a1ebeba5ecf481b2c591c293ef946df0ff1a48419f0015fd904e2d7e047088fb5d57ff7adeae7e5f12b284b386febe4b5c59785c349019f58fee940078e7213963a4d065498838d8eb00794aafa219f56a96851d41b10a888a2f20e8fb1e78b5b2a5fca02583871e33a2eb62c79e2010271bdea50e5bda4a3b4ffe85de462a6294a67c22f0b330ae25a2562d6806e66e4aab66ee7311990af8080a875f920485e25e70143d9bbf30cdf267cfc807d8f36267294a3705c53ae9ea4b15d16cf0faf10a997468a5ddbf59f190f48e1faaf992d3fce76c4bc8ea2f8403b6309fd8201dfe26153a4ee60110e076465a17279433baae104eeb81f251c544142c6fe00c5af6ee95b051c5711a1273feabf1537a9e07036b3a4f16ef0d8a5b23fdcff76ad005ed83b7d0874d873101196a320b4dba2184bf3114a5820956f7c02e0266cefb3d4aba12334c0d8f61f7447c2b718fb4c02d9a65fcbe7cf7db45086f265c76d3df939019905779fe72d7e042ee3430da2eac1cd9c929ce16c18d7b37b3f69760c95bc7af28ba1dc6b05b70ae8b9ef2d4d19f22941d3b49fb7afad6a31c2746e5b291c0e7c55ca34612e60e5a17a60507cb1579f31b0a2fe81b3b386df5bb5b3d2ddc2b454796ad633165808c46caa2d0979a836997cfded05266f6954a083fffc6294261e3f2d022b0211bce1f7cd80165b83d87892a9a7610ca4e0bd77884deec7fe66a1feafd9fa2e1aab6da94128c2dd58b63434708c259c53baed64d813eee8569a7acc59e11f10ee2624d3f485b043bd5bc256b103428f5ee599f97e2a1afdf1259cae3ff6ee2ac8fa3f16a703c5ae131ce0f81e651e78422604f1db5f6fffaa3f5624f9749820d6f86831763f19dcc7c85e51367e1d4f6693f468c577336b9f7b5c04c081572508f7873c8b9b8c37058dc70f068ea9e2ba9efda14c3adc6c88ba3ba080168c26e48b031b9e133f2e164a0f1ddfc4fd5aa43fd35593317ca46851129ae8cd5721a2c970d49fd4cdf955b5f6d7333c357bd665aeab15a3384b57d859aa4a1b76128a3a42159c59ceb6738ca5ec116dcc4fcd02073436a0c1f8e5d336f301a3840275fd8a4956439cb837c567a52a7d6268ac26d63a141c250ecff3fbba7f82c215203d7a17ab5f8d4ebca880aae8d3d7e37970cb4fd50021d881a60c8fe8f5941300dea436606734b6d6abeab06e16ef53dcc6c10d3ce6d9119e954a102d04601166dce67734f0aeb698c05dbc0564337e2f876ac705f5214eaddd75313aa2d40b588918f7f9609322e31613a1139752dfeca5c647e7c986070f86bb6e21e3660c96e066e5676fa4b4d7cd601a5f08f4fd404b9f31fa7debd54c539527e2de4b235421e110d278b6a85ff6a55711357f1ea268eb0989815601c8b86d996d8adb1aa4794be20b4a3efb33ac6384b72c2ecd87355cea68b7467895eb1852fdd3ba18079cf4795302cd09b80b586ce85f3a683bd10e9c5f621c46fbc1d7bfad31af2b2ce5dd320571970a71932a6d22c32931dcfb794786bc4870e12b87ceba7ba122d75efadd03f41072e57bc18a44658581d23fbc01ffd9b322fe03c6cc783dc704c78bcd80badc9cac868c9779bdb2fb63b8c78bb92d8bcdec7828fcfb9d69b12e40e3becebf0e23dafaa9cf36484699a073ee4a46add748385f4f499689bc07250f85f60abeb0af7a01d761b2e44337f6513d1dabe36e190fbcfd2515aee10200dc9e4857d0507282c172c7568b898233ba6c62ee35ed5757938cbddf730db706be3ce53df0564857712c67011c36c4e41559c40e82dea454cc3415a8795ff70d4a31e786da04264636f3827b454abce56602542dca8503e00a27a96dd414e32d09c480c59160c61e53966bf730ba6e7d0038e98fdf176dfbdf4b896bfc86d909385220b2b5a85adfef7116b81ce5daa5d80ae58328964aaf1cb42062c3588b23d8225e20218eafe15a96c1e413c1cac81a44d7399c1180b2f50fe2a8d0caba2d273c4120967a5ad40aa9e4249561bc347c99118eb604bbe07ddd199c51970b0c809b7a02b33480caa2a3d9bf44829458b0b1ced8de0eb9b659853b01acc90b19c4e79b6b25c335bfd89176b37efce3b1a30e127a8091f9638d8a105c608eff29ec3e5f76ada9f9af3928dd75ab47650dc236a93a128e68e3cb68be254ad6f1218bed6c2f1a2a5759af08d78a99a9b297df8d83102eb1eda0a0d6159ecfa6cd067bf88f831e6b36ac64f7c7db2a4c446d300b0890b4b6f835118373747060992727702af9ff71656e744f5f99bcdff9c75e44e555642e457e44a07cfc7a02392c770b080ff157ffbfa07ff90ef50da628c01017252a761edfc1bcd67964293708b34e86d37639f8986a1b5e0ef91848e1a1e65cd2d84b6c6fbc23710d7a6aef08cd48025d533f781a94a683607085a1bc1d01d5ea23adced4ec38700c27de5f12436c37506c7f06bd1b9df987be833c6ca7acdcde0ac582febb23fe1b03b2cd2c5231e1851d16ca7522f2b27eea98026f033a380c357051cd56eb4b127351610c93aff6f7a2de98807197265ec4d0f2f733c1d656ce2a4101b561fce781660b1f7bf53105ff49bfa52b110d78c23ab6a2c37ba50771855bdbca44d697f2dd0603ce8a70ff157414a3b807c4e64eea019b3ff83996741aa5f0533274dd5b4bdc06db5d9cec67dd612526418206827fa58bd5bcd8d4e3d496b1d214d950abe2e72bbd6733c88ba481d6b91bd6311588d4c55fe6957ff7e67d92052d086f92e4d413fc21b7e486aa4c198bbd630ddc54800ff5380a0f7c50d7d21e7448f4aa130c2820f358ae3cec2940d2278c1984161485fc9a2f7cb131dd07fd8fdb768832bf2fbdb1a39954912810de60ba861ee19cc98a7c4d1a0d9bfd34b96ab4248dab5e2839aa8c828bad0a1e52bfc5dc41cc68e9d8db13b1b4232be687646aaea567322ab52e1e663b7772251f8c886587a6d0cf9b7007fcb70613cfa7f9808f9517dd00f8942e6ab9b996c89614689c9ca6fc481459632c449d8780d6420e0723db820d99b54b59a3aa215bbc5b29bdb754608c575ad25e869fd900965ac42b48bd001215e5b1cc02fb739580d74129ecc0424f058df187eaafc04fa7080d3553eb6209c1d0043ba16bdc0ef206f2d6177f51c6fde6cfca6e5c19dc43c6eaa928402604170158bd6b9a6b48e08108ade9ef98613a0bd63dd80e553e5deb4778661a5203f4400f3d5f3a7e99b1d50d6b340a26c6af337ff8526cf0d45521249188353624b486cbe7796547a78d0ae60a713d06210dbf7012ca5e9793a60583278f3f6c20c053509af9e2e413c54fc15503b3928bfe9d65030f215899ce4dfbd58f602f0facb187e3ce156a80768c44981a7b51d46ab54352503bcbb43f8176023cefed2b752921fadb0334953cd7285bd8f5dfa4b4440e40c1ba1c11a23b8c04491402011e8936bc597d8364109f145861febc356f0e293a05c06e8a9e2615f310769927e2e6334f4fc76ff2a94d7813b5e511e2eb9b091dbc783a767070c92ce7c4781af43af8151ad41e257694bf89bdd15ac0a874d78622daf8aa307078e6cf49d823af3b47c093467df7d7f187d476da8529dfb9045837085282c9ff4e17ab70f01a146e615e1bc2d5d8bfef51105a7fa97ef45fd150112863e883ffd48d8e005d15b63d18856cfd202cb1fa3fa35ff6d4b96ad68d34026c689389d0ae4155f96427e775a31f5d9631b517d647eee684c1b8b938f03f5627f7cf3925df6f3a8d1e059d4c1406d4a1e2741baa039df5dcfd55d429ae9d9e7b6e5b821a86214beca00dfa91ac9d5ca6a3f2e9ef325d8fbfb4467903e6621e6a1120337debfe0bb08501d27fddb4ee48fad2168fe08667a03db274a0baa96562c3651146d98c1a81470bfd1ed8b7bbe939e1211c6e964d8634f4a4b54f2d4fe51044a8b14426647493020584809bcc39b08065247f6872f49b5c55077f0942ff9563b780ad31cf0415018538b40883db6cfe97c7556ec6ce791bee24de237ffc7ce3a2e17a7bb6afce07eb690ddd8bfd6445493d07b9ee688f8a6c504754b7c454b95937f6e84bb0842346eea54d7c3f3d8cb80248a29fd6b5ef383be399bbcc34cd792e524bf2de4018672d3cbadd69e7d72819bb4406d241b4001d1fc24d0aa7b380590bc725280c0b627e5abd4181385475b65df5b363e1b7187d62cdc8ecbbeda7ba724ac15e9a07e7523344e40a5d9cff1d5176dc4a4bcf64205504012409fe8a6fb098826d23201aa668a24588c24f6b20f9b1782b425a86cf311d8300f9516bf2c2a64e6a782de8f1d417001d524b0df66c926a62f179a8a5d6843c4968a81a5aa3c049c3eb186495d63d4397d3d87f37172e1c8f90b2d75fb892a8083552db78e6a99233ec2c1752dfc1114d5af842c4ae71f54d6c60d9f88a0199c68ddd6e408daa80f5ec0e319530d48021cc31d82567b23504a3106bd6fb16cb9e0d4e62ce71c4286fdf01e724b5b45d24e55f3bf0be715e984e30ab360ba0c89aa9ebb283d696ba0740031be6ffa66eaed319bf741c34a6852a503aa87aad91f74b58968fe0a183bdea1329e193e4c724a90dbac6dec96742e62e05a023ce544b963c853aa182d63fc801d3915d5293d097c26efb412b3ef3b9437cfd0a8c06705846e2f6a595060f381bc6bce1b82287bea135d17a722991d2a32d2244a0a589b567508167be36da730086bfb0d59d62ddb2876ccaf638686d2077eba1b2dd679663085242a09ca77e1091a76b1cce62a1a931254f145ea4b0a0e202343c8c83eadbeaf3f2fd1d6f1e056ac8aa83b27a0c7d281b42688df0cb1ccb4924e95839a06d70f83b9049a86a2f33a21efbda023d63a0864fd17607edbdb94e6887cf1a0408b955b0994c00be1d526eb3611b2da91c9852a13b26fb0cb5ba55c3da6b31777c9cf040ac18112e1c2130f9dd37e2a09844d0b6b81bd59c0912a6faef3dd46898cf6cb157571b0d18e12202dc361bcf4f58f1b72d7c64f41b1c3b44de5c80a4b6bfcd0dde979b72273e2be3e25df7fe3f4c27629286a5579f46e00f4af84dbb41027f8a70f8d7692db2e8bd7723280a50b294f55bc241f9e5b696e8cd5924847f025a98fb17145c042763c89db51c8fd9e8827f8d7125448e94aded63e57edaf65cd194214ffee41dfda7a41e1f30519e939e0e5e2af96dfe128e90ee63ca5575f318e8cd83a5c2040547d943cfb96bf470d2e4192f1adf01dbfe82daaba5c6547aca73f8d02d8d030226ec5298a5ed3698e51953986966cbafc96f819a30edeeef9fdf0ecadf97005a2075b29b22405b5ab64045c1c1709e017e3ad3d8db0f1969bf8e32c394a61fe0c88d58144f251196c65719c351be4013a906013010d104d27aa14215c1970063e8ba4ef185fcf9aed08276acd0fc05bc93e4589a94e710fe273c1784ea7929f5014848888e413e05a88b5713c63831dbf40cb3cf10ffdba511fd3736054314f5ed6e52529f110e4197d7546e31cf833bf82b05101f5b6d0d3d761f979d670f96db8ccf324b989a7ca6d2b061c73871f9e6f6ebe5926dc92de0c57093167a0e43280b5476b8e0c2bb59e3ee0c9fdb9101fe3235a661ea16c68a2d45864e15294667f2c6f66f2933f3b24cf97a3eb5e6cbf3eb5bc3c5d22130ed78078d3e8f17d7bc646707140f6adc7ca8af7328b46b031c9d7c0c6dff1d8a508d5cde4daa2b2333c99ff5a6452feda1356e9731057ce2bd32de650ff1bd886e1de8cb2b9213f15adb80ca5aac125f6a7a0fc2b9a607f6ec9b4f82f2825e9f3ba64c050cfb1d1f0de1fcf20843e3a6c114f5949ce7a681503d96d758b6cd6516d439025d3e0670c68689f5050eb388798cec5bfb0d32f8441f589cf5e2bca613399ea600040f0f96ca179da997df17d5b5b4e9673c4c9d4ba2f34fd9652d1d199ec7266cc81e8de87bb66134e87b344bb201fd69fb6c3a9ab0bac3dd2d5b24647e03467cbe1773c88395ae3161fbc8df580be4c4edfb3a7689c600dfd1fd8ce3e02e5c01b821520a6dc270ea35487c9db4da760bf52554bb62dddf9c28bdd1191c2901953e2efb8882ba2437119e421ef6b9b81c1ebd62eadf3fb7fd6fc929b4a01fda74c4033d0daaee5d026aae7ea7df23c60f2e05c3017c4ed4d257328ccfcd70cca77d1ceef1bc361b2df4fe6ae30cb8e8ff640d2cfb4a2816a2365cbcc8835f002a920cab8ada2fa6ada359edefd143a2c4c94078d370dc306430dcbf587a725f4c200fd6f1cdff764e7a680219db70837dc9bd47fc466ed1b6c8df08c0a4d238d4f820104b37596cf0bc69bdb49cd0bab6223c3b8a3ec7f8a3e7bf27c5941e5525551b220dfad49e34a1226e3f594471beed98796cc2e0b7073a6639ea7e2e83ef0928f04fdfd122d0c8b65c3b3dc0f9fca9225efe708c2723f5aeafb4e143829c910413a0723312dea972ab757e4c3ad76cdeebe6d43c3bdbc8d17409ff02bf08a62df9843ef911970553ea66046e96b1e7793158a03f3d6b6ec9398c4ef8f7d5b603b7df992b8b01af39f8964cde950884ca581157d3bfcbf29a6d15bbb05a576f14aaf52b93e8ec1bf1d34a63667371fd10a05431c1cd9b084d142fd1cba785ab0cc241e4bafdbaee566c19f1e6ad3260973b33c8083b739622fdd130dc13ea431c84678cb09c5746970781b552bca5c74b5aea2f80b26c6e8376271efdf4cf841c0c6fbcdaa7d0f90c57a776d243df95880c53048f90cdbe9b506704e0a349ed06149c87d190636d87e4b56ffe7f3ea3a21e4735c996dafe50ad49fb09ed81e1082e24566da807189b2230af5d251906b10adece0549ecc52206092368b3811a059f5b4de39940388f4afed8459a7e305b328f5b7085f65879b6f6d6dd79fd14122be6ffa857ba67d1c7368d61f9a2de2be7054876b02f15f369ad89a2fead3514245ac68e34c9e0199bfe3442c1bfc755421f2a8536c3c06d30596d10ccb42a2efebbc113f3b5ca11ddb17b7a22386c050bd7ac790a32c5dcbe72565a6cbba60767c7b9d7093260f9886b3e378bc4f20f423daad7d8a868c2d0aa9a6fd6e85c1d32351f956e424d08282c5a40ecb0b2a6b7a1831093dc5636d442fae0a3ce9a17cf27d87b1d0eeb1d578316f3da1c1f8076b0aeeb93091caac06b335f33b89e020e6f5d90db3120b31d48026f47680f8a8fdfdec73cbe0cbfb9d6509168ebfa0bbc825a37743f0cf8ebf573da1593242b90a07112930d104f6761d9b8833f9a190f70a4c200d01d79c2f12fe57f1237c55db6abb255c19f7991f845278e459116cc9d6bb80cd15d92c5fd81cc1fa757b7a203522b91b937145a88398cc7d60108c7bb3461ee8a6f6b5febb2f2611cd17d082c69602228db65cde8b2150a39c525ae60ba22f3f0083f4179115a756c1cabef701e9f299d0587210fd6d7973d9a0ca9c937db0b8d2fefd2fd9dc43d66e2af6e7e82962465a88e30f94e862e5cae24f2bde49cac5173dd44bca18ddd0a02d01e82f7191f2876ccdfbb5831df607a2634ec64b1cf29483a2fc6f68f3f1dfcd3b5ab118a83c6df495916ec59ae49b52b48de85238c04198be572ccb949bbed364a7992bc0ffdc085bc49af7f79bfc00adb7c54e20221ac48feb584f4ad49f8055b5d059f2b1ff3e1a8ae637abbad6d0e7ef016e15d8bd5ec6d7ec25c193a927bed985f6e3d458da8f1040fed0737a5", - "public_inputs_hex": "0x1d420eaa08a65528f470fd3e2a802913b611527f41fca008a0ad4e974750780314124f85666837486f1e8a234427c2a03fcaa651cd26e7f648116568dc294a7a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000ca7dd9181fb990af4173ed8bc797be72000000000000000000000000000000005720482e46b084a93e0c80c18a2ce90d1306b6665e7c8100659a2d57ad42b46296a1d6d5c4f23bb1b7bbee394d7182a8234e2191863d749a921a9ba23ce96fee3445e2989d7b03e6f558149a7c72536812f67fef27455b7cdd53d7dc2b9dca1505a960987ec9c9a7a823b3519e453ed403ebc0fd1fd65ea0e27f649ecd9f2da0bc3a50ed3f330bcb5ac06205bfcdb662302aaef600b5d72770aecd785f476b2a348b28d745dc6dda97e5246e924c2b810dea249216df43db9887f19e17547969994f891e2594fee031495261d1eb47f82eac46d710f18b5e50378c0e360dccfddf0f63519ad4d4c6e116f27886c571bf28d071fc4554c2fd9ffe1c45cb6ab7985b8806e67e39edc87d973d3be15694e4" - }, - "decryption_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000006f6a250a4a1febb7300000000000000000000000000000000000000000000000c59a1af8af26f3ac5000000000000000000000000000000000000000000000004a3c606a0e0f03f890000000000000000000000000000000000000000000000000001d8ff02b1949300000000000000000000000000000000000000000000000f8463282a1081366d00000000000000000000000000000000000000000000000e52a6fa72361773670000000000000000000000000000000000000000000000004dd0b3cf1dd7ba4c0000000000000000000000000000000000000000000000000002aa4ccdc1dd480000000000000000000000000000000000000000000000099c677445ed8ded8a00000000000000000000000000000000000000000000000778991a1741d454c3000000000000000000000000000000000000000000000005b0da14a7da205c0700000000000000000000000000000000000000000000000000016828b4702b1d00000000000000000000000000000000000000000000000ed1f3a4db3097256f0000000000000000000000000000000000000000000000021cda601a7590b48900000000000000000000000000000000000000000000000107e39b59506f625b0000000000000000000000000000000000000000000000000001e3211e681e3b2eebd7a56611911b93fc686350e88a8a65ed58a595480d04fbac1d55fd9c0ebc2b0245583b71a1db60cfbd36ea9d58cab4cdb1b4eb935658cb8699161e4a45371db5fd191ce3d867aecd08a188f46a34a1c5e735f9625d6d4e2c74114dbcbc36151218ea79fcfebd9c55715d7b1e1e20673acdb923a0fd057e35660ccd55df1b2eeaef0b527e4bcb83e87eab2e9a3cac45b6af7dcfe835d302ae7ad42e9fe47e2840f53a0f72e5165f147bdeef5460b87fdad96a1e48fb2937b3c191ff86a86f2858742a0f0afb470908887a7ab3ace39ceb527df346161ad02f1c529341ea510c4dba0465b1e6eb158c5d9c6acba29fda6d74a7a3912066b398145ec000b4f72954904605cdd3a19ad3bc8fa8a6d69bdbcbe469e9462a9e140e98cd5962564b0e1c49661f0d6dffe5ff6d29389038359770234b8eca87fe87f2c60aaeae6bdb12eb3ef85c8e2d8971f9388c28e4b2787e724bb9610400458bd87e3ce4cffbe129e0764e6637beafc60727c2ce766be40c01daaf305adfd2d51a9320a4bd449e28824ff6256a3049936a02348634400f4aafebd2418f5b1e403798dfcfa290a026043252fdd05203a28dcb0e3619b49c762236da277f896b47f619137c3b73d521371bda5fb136f511df9839d0d0828d7b817cb58b6f0cea159adc08daf6d78c1be7d878066553514df64004d5cb66dc821c57b7af04adf847cccddc85b8c5632b7fdfd8319623dce9e8afa159e3f412d5137ca336f059c0a7fea1a2fb3838a013de41c8d8dd53cd2614574b1b77dea6cd0f425266b276438bf95acb750f33b61c2d1d7602f576eab4c1c45506c70d3abf137ac4b08f81c355917e9f470962be2bef8b0e44b219e571bbcfa226911bdf65881e142976b3d81e8c7b1d2567ffcc1fad534a0d72ff98c868ae24e8d001c948e993f07bb42e2a0068ddcc7c5afbf6066d0da75ae57e56f28e0276d4fb0ce2cc47496ae818350be2b36e2b1b7a465724040a810a5fe13c0fd37e280a84ce71b0439a092c946956561966b1c0286362274ea32b275133afe856871f0e793bb5929620e980d028906e491c790f3ea5f4000a74bfca18178f0d059c5e520c4be6be17003f31d94c129175a54b7fe94af500362cb207b1026e8a1d7609de8428a8a8bbd3ad8f136cf9d68e801042821c432995f04b4e8c49a499413136ac1e09615ecb0a3d3c6e9f64470551488e4990bf0b461f69e3e8454dba24ba14e5ad4ceac4395673339f7e405d4786e9642182bd0f3713965065ae67e9b807428334dd2fc75d89c79dc956bbed889c7d0647d33c06f8d6954fb910313fcf489e6b8cb9af4da7b9e7a8abee4c6c64a3c122fc60e213ab8ad37945ec00d34ce1a87bdb4396e7174da58932a18ff22c78e9230c7ed219a311a3b3d4d841c865cb303ebc539671d9e20a174e8a6f898247617903c38d1a68a623e94de5a4f77833ef3588fa4b8859c204f8db36a8ce5155480a18238e058d866f6045b0bab9b71bab7141620936a16c5d4de49a654ab98bf53d58da711dc182cae60289b2f0daa0f6dbba016c9c38a3c55b82d376d6e64a333bb9030418bc0960d69698fd5578f948908b03558847faafcbe68e98c526cba756c8b7de263a08e96054f095aaeaa1280e303ea25696f2901ae6abcba9a64a669fe47db603206f370b9fcb574369c5635593055af1b4d56853989f30f61ab44805c1665e010deb6608e053ef1db71ad837bfd7c7974945be9b9f76cb8303fe2888ef57ed2807ca25720b486b369c04ce936f5e9c58217081e7ce8b23424965721b2779ed1dfea598fd0ee145093ecc34e8ea405396e5be61556a2e3d22788ac6aad9cfb41f769812870fa17056e2a5a05c94e5f8e760bf2c49ec706fc3cd90ff789d60920157664f597f1486ed5b2dc6c04435bd6f89b502416c2adb134190de2fa3fa7b07014e9a82391751a21b20f1b8babf3381bf45d80fa8655e229f0089804bdaef0fd94cae83a861b00bc1c0ac02c9dd258b4ffb19f8be22626b6f6e95e74e7abe137fb2da679c219384f2dd6cbad1424fcb90739469de44e1bd40bf64efa0b5d4224c9712a6295ff474b10f53d7ba74b63f663d13a5a54fd63ee5fca7ffe8ec3c01485f6617f200f31af70e74569b289480ffe7d316b96b60787d5052025d74081ca11e4ea9c47153e507f36a7fbc2777afff5726091a50a2f33f024e8cb337311c239793e288ea081e44088a7ff3208291831a5946019a8dfabb80117e7e57222de532511eb02d9bd27954890ca5d4453884d0b49db75f2e91029fe32287988f1a86ca90734bd54081a788f073cf1bb67a2db62208cc072bbdcd5fcfb7d09a6800cc2073538f2383e799f53490299bccf66e236cc6f9d9863c88711ee89f4b5d293a31a4068535b5b549abc5416d7bf4f651af578d086487ab128ad66ea5c67406117e6a70aa5df2074ecec211bfed976249a9640a17371753633bfe6540844b02472900222a3dc9b437fd6236ae893b24ae71a85cbb56d5464ddd02a8231c2a0cf89ee27e19e3c5a9628cefb60c12821427f0f04be03ea5263e2ebfd6acdded2b1413ea3e58241f5ea6c907730e1ab1fa7d20e2d36b35bdef8d442fe153707e0cfe1fe11d6ef0822e77e63d3b095b5238058d65c47e37bd0ed58661e249a539292c668d7cf0adbbb4b70fcd2d4f102160c666c3b0b52a4613f499fc364a1dad1e057938fcd5d1d1ecde76e7137579f4fd177b07fb00a0be3b543d4627d240411a603a64b5adebab7f73968d95daee8306193092539d7ec3951e1206f6767e1a1719ef82d99b12cd8f0649671145ffb54f021eb7d9950664e86189eefc53291c08616be5b938b919026893cdb62d712a0e8a94741c71582333a1fec2ff7ce1ae05bbdd516a213dcd4ea1de57fc041aa14ed3bbe58e83b294404b816f7789f84f20b260f663dc4cd0a4ebbf8834ba15a3b17148b619409d24670aa3eebf83a301165685ad493f3e20a5c9d1c79edd8ea4b96ee4be6fe14e235b437e75add1cddb27364962cc4be11d6ebe61e963c83ad79d2a308c9a5364906c360cc3a562758e27f0758e5cd6af677f4b11351815824d101a3f64bd24e11e388afd7c053286f923b2a998d37a0ae15a64f7aa581bb3df7addf563729d5db423934eaabcafe5ec0cc957736c4bac0d9d03292a067def613c6c59bbedcf772fc2bdbd8b0007258715333451900c68d1dcc18ecf27ad1b55f8c7012f0cf0601d87916ca0355b3cba098bd805f933f11fcefecd5f57d21907ce5e7584e01cfd026b8f6bb31016fe7a2c4185a450fe19daa4d166d1c8617216a06a5adc7fe2bcc99ea75053959977941cbf4ed4a7a090b65bcb0f33e5ca5383bf3b4b6c669282b5d3d63ed4f5c02beb2a167690b5f5c2de8be567babeeea8be6eb3c40033cbfad9dc753a99c02965e90d499f37011f3d2eccdbc5aa35133fa5f5534474af15d1c3056bbdab15a298d32b048d3e96688628f7dde691dfcea1f8978ace1eb95f17a962d3166589c32fac1a3213818ec71925e571d45fff508fb5b87a388f825b82d7617809b8fcd545d121f5a78be2764a2b92312c20dfdcf1ff11256604b49bb821fe6ca9b41516b66c2d6b291b7a6798bffdd7fec302a77404b4aef95db0e03d4254cc98571e31da4d2d39fc07bdaa371acdc2a3480c6ed862afb519d6bc7909efa24c718d5498f046264a5fc5f572796359d46225542ebb7ff8415c201dbe77f2392b9a08981f7e252049edf02ad6ae8eb75dc83b1c9eede5fd7d3eadcf233ef7fe6dbe6314002cd52fb36e36e9fb1c63d4ac4848c9ed4e85ca2a987a46bbf8b20c69338632d4ff530607f89ee61763bafe048e67ca2d34fe43f55a0b5a5ea811d91e62873588a9f020bcc8b0aec019a07228f654870f8312b4e56be7d06638f17658e3b3babc1c3f1394aa1c6c7540f90dc2fd228882153812bb16e50f66bc1b3f75877f675e0449296c1afe73cd6654793ee603289a7cca9c277774bec8804182fcb41ba47441441232257cdba59f6ed4003b45b87ca9e375b5df17bca251311c83e9c7069be041091c3fb4e4cd94ad8d916061e8fe807bc756ea11347d15909f0132ee1205a961156c257efb4477aeba91f9d3efb3a4b47145c58362fe64eac997fdf21cb486a41954720168a7409d743b49bc7795776876674140260b5901ba1de71f4cc798201ac47eafbebe38f681c6a7a0a7929da39faab4f1e0ee2980d3c07279b578a21e195817a39179b2f0ba31a24d1cc5810bb303d2ab8fd75f8f7f1f84508ce1224b222f5368d9d2ef5fa72feae9490562ee47946240a8aba30e386e23e67723d024081e1c47f7d9e15d77543765fdb5d946ef24e664d8a79d940dd66844734734502fb251a50fe946427d174223195b54fc64a7ff7d7f97cd93a4b37e98197fdd322128f4250f9263e9a8b67fe5ecd74e6dab5b6c5054cb46cda9fd1f16ab4140f61b502c075fd23263be264e26b8318e93ba00f7e03239cf63bc80e666efc2c1bb1b22e2005325a89dd3f3d2479d051ef24fe5143b15c095e923a538e3a069ede011f2ce96ecaf03694984d3da5b5257f5a358a362c4b402e047a2627d0b76c7e92bafb9ae02257011ce2cadacd52c5ada851aa77c68ce4f6c983a8bac1a3b6ea20b112c2648b421ad4930c22ebd76161042a49d0594286c8baf2c9950ac816db61dcf89cd34d49a8945ac94287e87e4f009397c9dd023579f4f4c83fbff5c24dc05af294f233e1cd482c74b89f426b77a2099386dcd9c9057c218ba64ffb9e9f41e834a833682f1887f1b01984808e368558617596c2ab9311d71d8bbdc422e860a4ed2930ddad4e4b8e9ff78a7a435403200386922e78685931cb45238eec3ba2b469469b13ebddd9b01d3e58713111976415afc949b1b84721be8cdea6a6ad01f91849d3b5e797917755636bd5b0d786626929d79f05bc38d9710a48e15fdce1dde1231805cf8c6ef93ffb13fd2c5b2f5af7419a633a465016787d6b78c29de15c07c9db63fd939d159aa1a9efb230c2db25b6eaf521678b69df7ac95646d9c1ea45a1812b625b0200556acd22915d71dd02c3a468bf468f43b7fdf8a8b1fe32607f9c81787086f4bdfc1be68c697151cd941142d3f9c0947dcdf117fbfa2d1205ecd79af77df27a988d14afa9cd1683a2069f4490a1623877451b142fde124002b2348833466ef62d68e4fd920f71769a0a0298e6ab92a831263b0b20b5b6013dfa805f77c6cf434af3ae39038bf1449c63aaadd38075f752f4e47613577d408e8b5ca142756d41e2eb0472e6540487a22bac42602e0423a5cac79772641f104a9517101b1f47e832e86abe635383846dec172a28f1180dd2c32214b7164c504f44fa9a327725a9b4e34d93777e0fca741b72b4a7346ac0b3b9a6282defe17178506cadf065f4a9fcc71e6d449c9a6ba0540c8a036da84dea1411b4649db250a59c742e8cc0caf27dcb93f0bff21379b7f4856919b76b86a469a823f767880144c7e236b2ad27b7f56620f9794bfb17608e370bee2b57150aca3d662b130dd2813e57c20c3e2b344c84a6cdc8ac3aec8af233b746097711a7cdbb9c156195a1cd24f402d6177ad9171d6d4997bdb82874f865c66dd2b623d14a512b7e9cf95158fb257bed0b67dab648a9ec7204dbc15748fb7abaed65fc04285093a3884a012bf22c65fc59af960deda8ca2375c1223f82b0b91ad33bf9b0bbfe1253b4bb5162126a4543e0ebdde3d63b3c73cb0720944ccef3df2a6e20bc95f23ce9964d805f91c129143d1f20208755600143ff6bd77038127e3f2b49b7f61af75efda1f018ac17d068ddee47de06993bdd32d8d405e0fdcd0c8f7f57af041f8621bb2b50a72166b2129f3545cd3ea5d8c7218ea93005e68c04e29f281e6a250038e92f1225c6f6da2e50b576c1d2a2d8903acdf27812d6eade8069a54827ac48dccce402d6244de8fe03e82cfbcf6f89202fd91468ec97774b147e34032fe2042a778e0251a5e9a205936055c6790f62ad08e3d5f0815f4508aea11c0b1a11460cab4380c8cfda8b2adea7508c7cbbadb3fb08877d0dea4ee35b2a46b8ea08ac5643f4f296c6bff97b0fe932f6e9de7f47e8f0d79e1b03787ff65181aef6120337e911a277d627e5738b9eb2bcd905af7c1ca217c1d51146e3504ccd575b369be35fc1e28ad1f53f59c0960282d6ac4f3cdaac4b4aa023d24218873609d64de748ef41f054babefea7d464af9ecdb67942ad38b4771722fde7a0ce089fc0fd461aeaf4829c5732fb27052bbdb6ad511071122bf33f325bda10926bd5341dc32cb5b207a2ec5544de17c7c8bd4760efaec7f21163c3265a86721f9c99df3d2bea2d4eb601c069ac0824ed575cb4150e7a69bd42ade35154d0770ed9b97cdbed427ef43e6108b201adb8b9ca93cf5f1c6f8a3b903419060ea692a2704c2b3007f14e0e01322b41a37a328e60f416e05ab97a12183ba2d7e623b6e2b6c7be770ed38fe4d17035922ad5f86d8bc646c56eca82869ff5b4f49009e71885211f70113a11a953718d8d02b13406937f272f5af3cf7d9d9f0344f14be1d0b8892d426fd09a7149918336798ab7328397eb3cbdac775cb5b222c15d5ed0aa19d9321c07a78aeec6d03ed73a58efa2c2a8e5fe8bdadf785e29338f1da6d11604b7b05368df594d9490c2d4befc0e34aeb1d2bc14da6af61e9cdda78eb6cb3a1c5417f7ad04ed48e222b63e7aa8cc6da502199c9b68e350db0ac8930efcd1591fdcb90c4416e778b492112adc65c1ce0ad7b74eeec48b87f905dc9f898fc4ba4af25b555743ef54e670b600e07ed5f17aa02c20e4255eaa760152f1e64ecfd0e98e2c3d2fc8a3668221c43fbccb1df45a8826438ce4cfbd3577f3d2f757d73633dd52fc34732415dbd0dca9c353e4a2727c089f61865457884aa1b471655220ec5759e4c1602c7707824a7ae13961f5a2cb2f0bfaf389d2114847145eacd4ae566d6cff1cbff8eadd7059a20b77b8aa5928ff8b8b4164f6fe756f19b148c526da7eeddf00f76ab88f5156992f5d7bceec8118c576f71dfc563f7a18f06f427cad422deaf7282d4e8c4075c76b106e12f661f43fbba18af93aa83ced3470c41e016ed364cb9e5a5e6280093b4aaa21a36f8d9b4b64fe9f6db3e85fdff6b1824475e632d89834ace04a013063e97ba41d0af5ce3f3c2030489999d587a9bbac31b8abf6114ee6646ef5915bb056f4d5b91a13b8d3e0ea83c49ca68f96cc4110cb85c7eec1727fa66fffb1db3826013b72c0b0616c7d99585c16498397eed44f369e9c4b4a90226275c98165cc5042eb5be31964e506c4a9db02b2767c0921774e2820a0994cdd851787119c1b56dc0e4ff44da97bfecb9bae37225e5b6cfaee9dd055defdc0791e69b9a21c7fb428cfd1974bfbbe9d12cc74bbc39321fb1772e44bab85d7b9dd947638e16c471673e961ee695ef15ab9d4a22c0372cd1507ff9c823872001f6e233f30a235d1896a0e6403ca7b322d3ff755196cc594ff984e0dccc0c38418aea5d2ac21ca5477b9e09f1138c77de8008874b22dfaf1ae69da6619085039bd72efd4e28069c026869dc377336c12a8ece4e596f75f3bee6f64e226bc7e1ddc1155f56e62a51e7c41832ad559269b9aad3cdd42d4365ceeddac936798a5b4fa5977115110bc6f45867b5d36c734ab53f934e7637aa52288fd7976fef8afa8d4a5c5319fe157d1e9ca37fedd5fdf1152724af58814859d227e4ef6b08c20c2d1038bc08990cea560af1f2d393752d843e3dba9bb3149c9750c3bd290549517943f54a4e1307849cf8413e07a3189059cd6835c4aad2a2c6615ce766c887498d5f69307b1c08cd012295948687dd6ec8f62e00a597da3f9f087e470a7925bb8f65654dd7ba1650e95522be76c8a494655578a5c81de44103df5679a9b02af615ab32566a9f271a2071644ac29256f6c4f54013127231306c880eb02c6658a335b3581f781e27429eadfbb812e523e59875914fbd471f17cb02e41a530ab33bbf16c5f3f20c0965069572b498973af828f53bcee71f1791e1e3494859fe61b843b1f51351f20f12f3b45c9200782ed36b86714271fdc819a71d8c01945edbf8db323e39aff901d9fa499d1304344994790d80698dc4b12ed57a023b57f9750cd47f9c281f6922348e01972c2a8a6dfe14204e48f7fbac8e5c1762c15b65940358e528d902ab2b61244100df3e012cc52a75dc132bc44cb380baf7e503cbd6fe5cb6e5883ccb230b486761e3142e3f5a5f8633278de594b8a2fc67a4ff19880a838debd7867a216ec382e63e9af8de73ad3ebbaf0d3742cc6720a382ea7cb9dc0aa1d1795a0a0f72f6e4bcd8b415f83405ee3ac113d5938cb569acf48ff37626d387834edd7b2a411b666ab96aca9fb1ddc8e95e1b9db837b28ea1729ddc9f50dc1eee348a23130bf8c08433547c59c2b70f614ad7a38a0964a509065bb9c15851ae79e1d3b324f11890ba70c0d912ed1b5e35ec97d6ff7673d6da743571394c02e423eb8a7f09edac8a4c3ca0a97091d68008df10e944f059cde0a3e233f79b8ad0daab80072f18c94ae73470019098c6cf86312712749cb05600933d37dfc7e9c8b7e2950a2ea52bab40df8246529b0c39ec7becb44fb03ba7928a6b2fb37198d4acc7fa3a162c4a68ea9f8ff14fcd035d41de13a59a7860f87b0c7193b125fdb3f80541742ebb70f9906ecc4ffa2b468d55d1bb2c996dbf33c7a6c0ed7f4ae6cf2a9aa7450d84714ed3567a93bc64f7d5aea0fcf0f043778bcf4bf9259b06856bdd7491a72d154858c6590c95d73a9d0558a512475a946824439cf2ef400bd2647aa7919622e284544b464e5ea4d08e03d3c6334fbc5ea9432750ecbf41e5d69a8c1f08542e7c95657c432bb5d13ef5c497b43d0c7491916b506ffab68f3aa3026b19f93b1043e5b052d5abb343dd9b0790b725dd299f46b3ff06d6645be87865fed66d7408a1b7c0e55081f6a46ca63edc7a73a88475f30affb8479d481f72642a5c61982e6f965e2122dfbce0506f5e2aab3e1745c0fb3fbddea54b334f2dba8e71f6aa2b5e8f4c2d4cad58e23b573305c5e2cd0570a6b28d49e318eb3bfa313fdbf8f503f5740bc3311cba6d238a765d8c23ffc6c5e79d7a58ede2e49b6b44406726ec2e592845b54c81f6ce11e971f2a2f0cdcc656cf04849554749c3fba1bc7569c62f31d273b1c92d707e9311cad083b84379b22ee286f80299a6c5708a2022540b2025a183bc4c894de71208a73887687fa3009e725bb9bf05f43eef71f6d734bf09c282340ad928c7d5b5f101ad0c2c793146e9ce590d6107702063dd97dd41060406175edd4bd9ae0a2c2812726d0adb4538b2dda7d0610b02a67a089cc5bace276c19c2ed12d174a9d230a822941a2b0013e15b77525ce713b265706a27e16f2fc96578b7ec1983cb00e81d92ee19ef9713b6bd0271e245316cb683bc24d2ae0a1e55fd1d112ed44d4516c3a9b57517f75ac98154131ebb544af5592398ec5629394e073a9ba886a3ae84b9196979fe2fe093a025b6cac3530c58e4b04bf4e10a4666ae1d748f197b0daf6b367cab12885888990ad4dbe0ed48ea7bff243aec0ef869160cce3c9e889be0ded99f9da8b839d231417a4ee783c7ad0f93ed974f0caf7b30f5cadb0dad9f054e1aa03c656d2fb6a36aaf7a0da7b9e5b3f4a5e3a62921a1d7c3630a67c29bb80db439c963e097f4f3ef1ae1c027ee64608ca6b9e32622181e111eabd9dea68f659217a71e88bd1c8824ae51c58af20c6bc257cebf001ef6d744f9baf5117162c76dcc82eda8c87cc5f7dd7a557b01efe86b37fdc12518c0002e1b1db2ad66d51d561373612efb497cd9f79466683733c336e539cb29047f72eeced8a323fd3b989822c9609f41b906faee18713e6ee2337f296f99252e68c55e44e8d9a0052834055e0fdb29d2f7094c21894e4e7521a284a17a8e1d81437369646908fb00e0c96de760302934068bc5f5ccc911c276f9a42501c8065d31643ae26287248f206207d1c59032685979b2c981e597d33b044be8781c2d9ca34345c52a18871b0ad8692711a2996701d854fe595d73c9938047a77f3c21675a797a38d20460db2597029f1b8eb063fa74db3dc055b32d2d98687a7f5e163046ddefd4de8321460d648daf417961d725757694765e9c523172c83fcbd92f66255b9fc67e3d1afd5026388eb9be53c490724285a7acbe8f0d5b8e64586f01cf5fc9c296203597940e3550e6aa12d6ebb60a52b3cca5a5898b0717390b7f15073a8a06305b10640618fe3e1f8459e78f1b9ce75081db94431fcad8694e4718103653d6c8b4900db74464454ed99564fe6e9d1f8199d377711322b51fc8cf23975c39d2e48497bc537899456154ffece7bc2ac7aaf6b71538e13c989f1fcd0012aac1181a2828a9524f377a08f0b550c4597dcb9fffeb2c108e76b29371c80b3793c8aebc3393bffaa9e6a5a91c080c23419afbaf2973ae8f962ad5ce22d423c9b40da51244514d70ddcc0b7fe75220b395900384512bf2498701fd519fbc02ea9203d3287b166f53e76691fc750c03354cc1aa12b39942638245606241fc13bc33245ddf5225219b011f988c6bb62389ac2580bafcec152955aa5476369912117600d1c106cafe5b18059fc160dfde5887c531b65d987e6806662cf31b060f43f0a7e74069858ab93bd747cb188b00a1999438c4476776d1a71e4e3ec33b293e88bfda664258db385abf028dc3126567b8e6d7d21f8a880a9620ebc9e824275e8f08282c9978ce6e13e9100890cf8c53cb481d440b38aaa733a42cfa9de018e320ec6406f6c1e3803c1d17eb0b2e76574a3e8dbeffff1d4c832fcc5f5c33218329574d558e7242392a585e38dd552acf6497e72de2971efc2425071b67330cf9712de52a7987f410861196ed0adcbe23455cf38e5f40d4b52c9da40358ab071888e7b0c3e28724ea53c64f87a61dd4a7df8328e28c5b73a51255602213551fe4cdd758110bd99ac6cd00eb3a9b5cbdeb5f0f4423b8f43432440b6755001003740951e77852e1ec468b7cf571e730b88526c9ef0bfbf0153a61eb8314165c15356eb9b9b0d7529ccfc0100a1d04a592b2c9fdf1f3d2b1d8fdb7054e921f301233a66b52133a42399ad5ce7fe5a8b890ed355d6fc1e8edcfacdb2c18e009ab0406151c20bf42f82b1d54f97deeee62546657481509e6107786a3ce2da74a8111450b94b5d7c745061ef01f67f99b9c96674f17f3b64fc0b835023982d03d311a11d53e0a72b54908034423fb6ec6c2ed0f5f5760835500a798c06a6940f50f27c5cf3ab70b8689aa86a8b1f645d2dce02cbfda013aba3afbbb9b7e1d414f390dc745ea3e22e68509852bfc2846c1934a3403bfc259fcedd1c5149bc3ad56b02ce3dd6124c57b4710cf44d4b23d8897809c6fbe8405162c058b6676dfb857b410a7cf60fc2495f1f10b2800abb2a8c79d3d4883de1d02526e98e9323dd88ebb230deddcd64882e717f4d09c5760747c0070ecd5c717b7f66ad0a06c528375d001deefdf357142a95506a15de98a8d9126ddef3cd1dd266f8f3b38ef673090c517b5707de5e581711f15e1a53a511b099d3da0b7a46911937b556a2ce6dfd5b61f48db4ed8db388f708d693009329478498f08036afd3e4ba5942f2a7e5f51ea0f31edfaa3d988da9544002184d463e2389e0575de1f0715e2ffe85855a69da21eab4ee0b2f8f6db05c62507c92dadad49d407e96227ee5f01f332e217c9f6be123b4f730069a68962cb29b3ce2592c5082b3b74d1e3cd8ba8ea9e1337e6f8ba17dd5d74aac622966cccdf068fbf6d56eca771af04137a97929102b317d8ab8309f07d759e08e198b036174ea24870db6a13e21ba945b2f0b50f89049ce6725e200bc2f9e8eb699698fddf3aa0b9238b615ab6b77c4519179faf828a236f67ec275d8466b87e8a0e594d6efba91052cb5d3525772648581bca12de50531d26901760bd614104e07e6b0a45220b21c3fc854a2586c4fb75b402d42dcc7a08a9a51f502227daea5b4328de5dad1c1aac628fb70bee83daf8c63fda2b35fdde5907207bc7864b1d2f45f204ffb00faac527e5012b89b11f52dc081afca53e9f08192a6c34919406abb76e6596b431a95271488bf390804a82fe9e57d3aa0e10ecdc04cb3e7c991ea9bdf6f7cb405d49b2c12e18bcec869df8ee1d58eeeeb357155c2d14181b98fc068cfa186ce8cc4536c71cd2be00f057eb086989bc32ea246a0e0b5e5af095005a1825533b7ac950d0cda041fb00c5616f1e3d79506dd9a1b01b17dd00278b4f21bf46cef84152d5878ba8ef0a66b83948567bc534cf34d0fcbd29d923eef2dbb99fd2a1c7f55647af268829c85505c6b69f6c3a6665ca5401c80532d1ed0796d47ffbf240db9d00ed462403799ef8e2a3751944c9ed93c05d5004e47d2f0eacba95627b4c7ed48b715b00d85f040cdd03a2669ad9db56eb3f290226c35b9aedcaf5a7ecbfa7da4fd43238d73f10e45164e5b2f24edfd56a1a6e21cc4e810a5e06c20b1e15f0f2d4e23f716d97077504d849b505de7885066f1f00413198266b5acede42ed3d9ae9348005b0e9bd433f5b9b361c3c84ad8cce1c2a437eabaf3572a469c4182eedaf4db74e2ff28e12c3bc535f0f075eb4b5493d0cb5c77dc3e419163be17674978c4ef367b967edb640dd4561ffba0eb4cafd10026b691592be18e5b8c0da378fbd5b5e35ede59b066f8800ce6beec29b6c02e610e507211a418a93c23d0e0c6d2c69c31e6222d5de8424c9c7dc94b4c35bec5e00959d8b099af2337f125d0787e512e696e763d1db7b8523c25418c1ad290539182099a8486dd939c507ad25118a4347bb571a0f25eebdf41f892ab8cb8a12590c61571d44ccb6c08bce5e1bf10e04b4ca4348d8db617fc0b9da03edb12597f7036baa40b39d67b7c375afe358e47cd0128eeb04f5f76b62fa7037bdddbdaa752cfee52a7b50fae3fe01a5630cb5ea3122b7278a8aeeff4f4006b8726a691f512fad5a1f3a6f551a465e1f6f181247641798cdccb1b7e9b565f14057eb4c1c91030e37cb712f162ce69e21f64c8ccb97c931c43da0299766fe8907e929d3899f06fd7458e8abf47dc00cb5117acec257810c281c56c86c65e009c153d84a7a5a1d4a1570993b43939715d6e9c0022f1d95c924b78b4c8895c95ed1ec2fc556c922b63a859de09a7308644e14be5632d19dc52eceb5e19239650daa36b984c1c10e9d258418e929a134838385373d167a2b2b4766602933df32f571a5baba2d320c0c9333e9a0c806b65de2904dfbe12e8ae58cb76a70b8a56e52b99ee6dbf393261ef37658daa3fb2326c4b677fdbb4bb40f98961476b565e964142b29080c072b1812dcdbd6736c644b67237372d8ef495fe04783fa8991859e638e5162687126f3c740ece45a0b91f58cb12f1f33e7cb3675ebe5bf2427a220aab7ff941d5a01f10d5bbe75b4e17c60f22cab190dc60f2eea623ce4cbfa6e70e8c02993c03c0ab1ba7eea1d18a776824ee53b14aace8b3449de43b682e0db6d2c1577b5254b128dbb6a11b65c6eaafcc1ca744ba9e1dd0f683d0de2416008e518d38a934c5b1fac7124ee955660f312898a2097a77268332159a2329bad2b8da0d2f5ab5b12068eda7e6f65ec67e1465e9b321f604e42aa8497ce1b03a7acc77d636f9afc7517fb31402ebd486a323f7a4aaeb4702bbaf7c34df1d5d62fe7b7ec0ae286e1ec02e034b237106a48bacedd038f6657e87c9d6d1f9508e699764b29113a529af81bb282c274fc66366986b98f72b633c7a35dff97eaac7a22eb2810eb45a4870f10857f77ae324cebe7af6585312c2f07bb72113fbcaa59467bb2ab3d4060c7992e635da05eb5de493ce9f0a91505c6892c95aa55b7b88383823aec99c24d80c921809093c179db569b4261de0f20e3cb1593097648ec2721ae9e783f3edc98050a985fa4eeb9f58e4907b05bae82ec4cd28806c6079d03c241012e88a7b34e7e18b780283824b66200fa56dd4184dc0b3067b95daf7ca4dba8bddb7f34d60e3d1b156944907883dfd4e3fb50789021a21f8e762d3dc5acc2036ca75deefc922e11cd11ae4bf5e5b76dfc39e3cf240a11f7edaf9f692778d98fd8fb44b3e51f072870540e9741261280d6ed9f1ec4a8c5f9a290574df1ae180c339980a28391d22dc7432de5cc09ccf81687c96b08225760e2a404458b684037e613cda0b6a6a710009226c21f32319f684ff9feca6b6a0e4a9f84a443718b7bdc49b80d08f1ce0fb5e775229a7e694e69837bd802edae2398cd31e6e230f483be76b0044e93881ad2d6e1c3281ae4dcd0d43f4dcdeffb8083609876740cae81de3c2034b7c4d40ee256b0e50f921e13b2e6efbadab5f9340fad1f0c2bd6aff8bb3ffbec6555530b5d1f87365de5efcf81bb99614c6d2978b38ff409485a99607dc8849a175c26229eb80987c833260712e409bc0343a9217d2114b23603f790d75e90717d33401c9ca9fc9695014140372a4e5faa744c3607cbf13edaf95ac1ca98e7b0cc071a2a11f067421264cee37de5900dc07d458c8f2a724579c5009fcc784ee6302be32f044777edce221fb8dc25bda4c6c451db26887b3078944746f30bbce4fd54892d51c86267a12e08f077a10b659b7557129ab5cea0be9541cd5071a3e634f0c7", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e4412165f58bbdd51389e19f359b95755ee0c47120177439bc8ed45ec7a6eee7f42d00000000000000000000000000000000ca7dd9181fb990af4173ed8bc797be72000000000000000000000000000000005720482e46b084a93e0c80c18a2ce90d268b63ab57f71e917a79017400ac69404ad136220c9e12bec413a6d3d3007cfc00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002234e2191863d749a921a9ba23ce96fee3445e2989d7b03e6f558149a7c72536812f67fef27455b7cdd53d7dc2b9dca1505a960987ec9c9a7a823b3519e453ed4302aaef600b5d72770aecd785f476b2a348b28d745dc6dda97e5246e924c2b810dea249216df43db9887f19e17547969994f891e2594fee031495261d1eb47f80000000000000000000000000000000000000000000000000000000000000013000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - } - }, - "test_exit_code": { - "crisp": 0, - "folded_export": 0, - "interfold_contracts": 0 - } -} diff --git a/circuits/benchmarks/results_secure_agg_micro/integration_summary.json b/circuits/benchmarks/results_secure_agg_micro/integration_summary.json deleted file mode 100644 index c4a829343a..0000000000 --- a/circuits/benchmarks/results_secure_agg_micro/integration_summary.json +++ /dev/null @@ -1,244 +0,0 @@ -{ - "integration_test": "test_trbfv_actor", - "benchmark_config": { - "mode": "secure", - "bfv_preset_subdir": "secure-8192", - "bfv_preset": "SecureThreshold8192", - "lambda": 50, - "proof_aggregation_enabled": true, - "multithread_concurrent_jobs": 13, - "committee_h": 3, - "committee_n": 3, - "committee_t": 1, - "nodes_spawned": 20, - "network_model": "in_process_bus", - "testmode_harness": true - }, - "proof_aggregation_enabled": true, - "dkg_fold_attestation_verifier": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", - "multithread": { - "rayon_threads": 13, - "max_simultaneous_rayon_tasks": 13, - "cores_available": 14 - }, - "operation_timings": [ - { - "name": "CalculateDecryptionKey", - "avg_seconds": 0.060314222, - "runs": 3, - "total_seconds": 0.180942667 - }, - { - "name": "CalculateDecryptionShare", - "avg_seconds": 0.165855291, - "runs": 3, - "total_seconds": 0.497565875 - }, - { - "name": "CalculateThresholdDecryption", - "avg_seconds": 0.24252575, - "runs": 1, - "total_seconds": 0.24252575 - }, - { - "name": "GenEsiSss", - "avg_seconds": 0.063742361, - "runs": 3, - "total_seconds": 0.191227084 - }, - { - "name": "GenPkShareAndSkSss", - "avg_seconds": 0.109152652, - "runs": 3, - "total_seconds": 0.327457958 - }, - { - "name": "NodeDkgFold/c2ab_fold", - "avg_seconds": 7.286771014, - "runs": 3, - "total_seconds": 21.860313042 - }, - { - "name": "NodeDkgFold/c3a_fold", - "avg_seconds": 59.072488527, - "runs": 3, - "total_seconds": 177.217465583 - }, - { - "name": "NodeDkgFold/c3ab_fold", - "avg_seconds": 7.221067, - "runs": 3, - "total_seconds": 21.663201 - }, - { - "name": "NodeDkgFold/c3b_fold", - "avg_seconds": 49.741351916, - "runs": 3, - "total_seconds": 149.224055749 - }, - { - "name": "NodeDkgFold/c4ab_fold", - "avg_seconds": 8.275522861, - "runs": 3, - "total_seconds": 24.826568583 - }, - { - "name": "NodeDkgFold/node_fold", - "avg_seconds": 14.942494861, - "runs": 3, - "total_seconds": 44.827484584 - }, - { - "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 2.7863485, - "runs": 1, - "total_seconds": 2.7863485 - }, - { - "name": "ZkDecryptionAggregation", - "avg_seconds": 47.8196105, - "runs": 1, - "total_seconds": 47.8196105 - }, - { - "name": "ZkDkgAggregation", - "avg_seconds": 19.611985042, - "runs": 1, - "total_seconds": 19.611985042 - }, - { - "name": "ZkDkgShareDecryption", - "avg_seconds": 23.107511138, - "runs": 6, - "total_seconds": 138.645066833 - }, - { - "name": "ZkNodeDkgFold", - "avg_seconds": 146.543567555, - "runs": 3, - "total_seconds": 439.630702666 - }, - { - "name": "ZkPkAggregation", - "avg_seconds": 24.754375917, - "runs": 1, - "total_seconds": 24.754375917 - }, - { - "name": "ZkPkBfv", - "avg_seconds": 3.826272513, - "runs": 3, - "total_seconds": 11.478817541 - }, - { - "name": "ZkPkGeneration", - "avg_seconds": 54.048450264, - "runs": 3, - "total_seconds": 162.145350792 - }, - { - "name": "ZkShareComputation", - "avg_seconds": 51.343368145, - "runs": 6, - "total_seconds": 308.060208875 - }, - { - "name": "ZkShareEncryption", - "avg_seconds": 119.190663328, - "runs": 36, - "total_seconds": 4290.863879835 - }, - { - "name": "ZkThresholdShareDecryption", - "avg_seconds": 91.265730263, - "runs": 3, - "total_seconds": 273.797190791 - }, - { - "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.119331555, - "runs": 3, - "total_seconds": 0.357994667 - }, - { - "name": "ZkVerifyShareProofs", - "avg_seconds": 0.317396308, - "runs": 5, - "total_seconds": 1.586981543 - } - ], - "operation_timings_total_seconds": 6162.597321377, - "operation_timings_metric": "tracked_job_wall", - "phase_timings": [ - { - "label": "Starting trbfv actor test", - "seconds": 0e-9, - "metric": "wall_clock" - }, - { - "label": "Setup completed", - "seconds": 2.980480584, - "metric": "wall_clock" - }, - { - "label": "Committee Setup Completed", - "seconds": 20.093927583, - "metric": "wall_clock" - }, - { - "label": "Committee Finalization Complete", - "seconds": 0.001328958, - "metric": "wall_clock" - }, - { - "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", - "seconds": 163.129884, - "metric": "wall_clock" - }, - { - "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 603.098846875, - "metric": "wall_clock" - }, - { - "label": "E3Request -> PublicKeyAggregated", - "seconds": 603.6922475, - "metric": "wall_clock" - }, - { - "label": "Application CT Gen", - "seconds": 0.298300125, - "metric": "wall_clock" - }, - { - "label": "Running FHE Application", - "seconds": 0.001343791, - "metric": "wall_clock" - }, - { - "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", - "seconds": 50.75193, - "metric": "wall_clock" - }, - { - "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 176.864929208, - "metric": "wall_clock" - }, - { - "label": "Entire Test", - "seconds": 803.935495959, - "metric": "wall_clock" - } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000bd233316133760191000000000000000000000000000000000000000000000004cd042387c7e76741000000000000000000000000000000000000000000000000c0a95baa318bf7950000000000000000000000000000000000000000000000000000eafe989190ab00000000000000000000000000000000000000000000000f4f85589bfde2c15c00000000000000000000000000000000000000000000000937e155db4ab4cda90000000000000000000000000000000000000000000000098b0ae0c2c3a32739000000000000000000000000000000000000000000000000000250fed55753d6000000000000000000000000000000000000000000000002932574ea65c59469000000000000000000000000000000000000000000000001d2627c24bbe173b2000000000000000000000000000000000000000000000000fd394824418aebd30000000000000000000000000000000000000000000000000002eeff4248c986000000000000000000000000000000000000000000000003dc5e5c57a7636d0e000000000000000000000000000000000000000000000000a7eda52c27e0be5e00000000000000000000000000000000000000000000000ff3acfac6342c0d5e00000000000000000000000000000000000000000000000000010511ca1b2e2c2c88f85ace6098930170a7f72ae5090886f78577513621aa6c231b86f9764b0922104789fe82b7baa4017d26b9a0e245e2ed5dbfc065aa5b802260baf7deba3b2d7fbaf835e5696af3f5fd069fa2c671ec53f8c2c2e24500b34191069123b4a002905654f910d2dbd67eb302c5d214796ca04df744d36b03611da063f1e227972575b01ba241b58070f2055b70a998d7313d2c9e3e55a166095864faa78ccbc2116a337fa2bf2615cc3e06c5882ae73e1482cfac8472397f0f5cb0447b092b1326b6ac92cfb7fecb05d2e8a9ede6e7a307dfebdcb05e9b96a4652957eb733b312b22130acabc0811aa530d060083d2cd3126b6937a19728d84db0427591de7120f6596d709315fae9cf83cf5b3d6dcb65b7ed9fe80bbee5966e75e6681b7f8e00ae1f6bb39051093d17915e71df5b0423ee39b0950abb34a8adf84ca7a2fa5162482cb550df61b5385ad9c64d97f705dc1d3d2c74291249bfbffcdb03a0010bc1fef777fe30e812035b8bcb70dac6ef3e80a3e419057ab501ee5d2347c4cbfc9274cb123beddfd7ece7fc849d9da4461f6b9a0646443f8893b7cc5fa62fdb350286ca019562cec1243bedbd6bc47ab45696dffadd2a77fc89080241671e9d055295281730fac3e7806becb3102324c58a579376cf7cc75e26d300a5cd1bf0f2f15a8c1b815e0b10ed9c3dbc2011417232d91d4ba36ae56b9bde57cc8ad2830920bb88ebee4f5244ea8111b94cf22bcf785657a3d71997e4f0297881c2e62651d1b9e8c937740ded53ccf51c54f9e795e0af9b4583e34f9551403dbfd33a497f80d189d8ae82bf240746eb430420ad354bb720a0656d94033b6bc12f8625b41bf1cb20922e437ed7828c462b40322a641c45fdf752246080c5a0603f03183208504989246a9d2e678603a8f9f4a20eb90928d43f758e1ca5dcf5d255309a699ac0e6a29fac9062524969e0b411517eab3eb8e59afbc67cbf6c6b07bfdc14a353b0bd8206cbc26fc03c523b09e88667a6f5771143250bffb5af452be4c84e8a1062feb73521ad2daa52d0ffa204da0652d5d3e7b6b62cbc1889495a9a31d5398162533f2fa6c568c3cd8a267e5f171bc35482ad6eb7f8c55a19fb9f6d3e46e61a50a0df9aa552e30e4437d6e0111c2bcf86841f81a1d55d9443b35ee471f95243e0c2f8de4e1d6cd79397bb65f9cb31aba90b88ad990f7af3cb0ffce69c82bbdcf115c4415174f366734c7ffe5dc41178a9d7c79c53d265930a85f73f7e28fc50c0fbd52fde0547a43a840b0b9c68397ff9add5f08670436ae7058af376e1267772a77b281840fcc3b087f6c9a83fd5c7d37290742607a3e11e14fcfd7bc1e1754248fd5f48ff35dd68b6b91e1db8972fdc41a11ec0ffa929354a28fb84e5b9a3e25c339ab1d8d2595db5e02f801699cd3ce271bff94f36de12df9a52169fcfd660df72b469be47060657ac88580627ba7a728cc6f7820f5d1a395459a5389acc42ffffb83898ef6e9c6e4baed14e5fe754d00d8c18fda3c8a02857a74d49156fa1f33f8c3053fa7d028419ce7b2d740f4c1d4ae8c9085ec1e12507bd60a283eda2063e8b528529b4c4e0b91d2f074866ea9deadc3fc14a29ee17c23ace8b5b9930d39dcb1ddd10456e9aa2d3a57d1f23052b536614ef655fb3da6d4ed9deee086089c68a8a5ce7641753de546445ee36803f551e6ffc64d6001aebd40f3343cab28bf2cd90d187d270b86d92e94d1b32bc27af1dd52c2f85c1943c5b065c7541407dce38eb327a8695fb6132bf2971f9a05b54ef620a332a3ceec7e48bb33ee6c0cd43d69381c51c2c9c7135800cb051c1dc122c0339a55ad802ab0b3530d33a00b70ac323ab8db68b6516df1d8750c4dedcef214cedc1d17dd55eaf5a58ff0980d466eb419f3fc4d7b6940585ceb122e0eddb918d817fe05d93f51d57b1ab2241e5e2ce85445d90b549c66aec0acf25ec7f8f73cd35fadf411e7a43b45727fb52bb25a56d97ba7551f39fd51342e846db89a31df2e32a22c4738883984e8c2a42750ed3588bdef4b7a38e8040f26ca79932e56c128374e2c9bfd911ecf26af472ddd91459b2045efb53379394e540d91c85aafcdd33276d2ce0253cf30851aa31adb1a824e55eba89d38a3e6923a95a0420a5aa5e6a04373256b78a3edc3012828eabca70af39e3ef8dc5641b7a5c99264da7cef39a45ea655bc8fcd39f49b6e1854895d405ecbb925a878bbfbc3b84b6bce331704d04c4bd928806ad2700f6a2b4cedfb4754ba6213e3c5dab0a5ca9cfb115e30212901cf13155c2d4e0a34b6037f56e6a6dfc60c6b10f3d48e17f1e672cb07b90f580e0f541012d8c4c1eb14130f14d1f04e91b543bfcef5b8b2e71f7fed7f34b45c9dd72db66631adf392f703ed647c6535d2523406d8ad6577abb6c408ed785d18716226fd187bd4845f3020d78f9fee6869ea9027d69790841fc5ed615e486b79766681739ee6ee0add2a21665ecaf41afb2473466f13f847b8b15e843557a2a6a9fbe5d4a9c05a54d3c1214bab93b7d5fb8866572ef2d65d9a6295b82498c7100c718fa9475c499211a205b1c1bf5487825ece6e7841e028ba7eb15119e33d88e0533a5153aa59465db90555ea4eda3e5b6bf4b52863953d2daa4812bca663ea0abb1db62b54fadb61c3170a3e654083efa9f90e55a883b555b33efff0f35dd3070395fbda63d30b9e101448f395f20631ef693a0669b8ef237134c4174425a7121e77bf0d3945c5a2c81e9eb2ed4fbb6f6a5bfcf44f54a1e1e790b1cba07124381debefa312d40116fa08f9151c0286e10824723d1a66773458f26c6308f38da87b33d4ea8a1115ddbf02c48b8df6c5e306a6b3f244cdec97f3214d1fb35ddfe51be997289107a63a3c1f30172928a6250437eaee7e00c38f3afc71e209afddb4e63b906324201e80a429cd98ceb3eccc533cdc861916e98751003ac2ce9eb4917156867eed66d6d0210cf8b340d8971a40b31a6b4a039140cbe496afa2a9b5233386de7c1f885e8bf615d9ff58758c7862575a577180958b980b240b853b8dcbafe66cbbe9d7531ebb26e7ca822d9c4688b4227af9c5f7e377053294a578a2fef2b28c1cbf60060fb1116adfbfbecbec5fda3dfe6fd2449ec4192c43692bac3ba4bb98685f4cbac5131e2d16a1ce646c845396c37fed1bb7b0e1b6e20cbb76e478199244c48a780619198f6a2cec1e9483d9d505945e971fbbb0766e2b0b1fdbf7e6f7571dcc40723513c585c11a3d145b11a2162a037618b877d735592c4c90b093ac3c80819b9c7b18da36ae47746f1da8856ce213bd72909320db8c87e8f5a3a5b3f9d9e51530e4252cb6a810ba0f6b7041a94e317232bfac1241b2635c8f158cc15df350e2644f07fa101afc651e9761e2d7ed115664e9194411c49b8ec0d5bffd4d8bc7097c00275ef1c7e0127e4f195a7c23ef08670363dab58de7263856480f59428995b1ad14a8483016e70e38d5536335202266bfeeb3f877e1fde38a25ccc9305392a00d1c9078d16611fe1f2a5a6e798fc0424fbbb755a183ee1ad94841c86c86891ff029380730549e7baec88de39f4e242d8f36039ae7d5aea0b3f0abd8a125a9424500322bdafd7b629d1f76c488c6610e3dfbcb751f9b6828635271cc0e08969f8d1886f67397d5ec56a4aee15c79b819d7d863ecfa16e473d0fe0ea4d8fae5d3af132d0c75c54dc9e2c68c1110235f49ad3acbb1cf8a69a754ab1d998fa63e93190fb62b409996d1d4f0a41075175bedf8e824db79caba1239eff0535d8a2a99bc22fb28f30d643fad9b4885f5586c6ac4a4e1c31bb9217b4ba4a00cf5d2d16d27117256b1582615b08e29aaff25c5e0f1c107c0c20d25461a7795b028a4d787e12f32ec3ec74081e7d32db71bd3584f5e49243c385b2c1104c860a172e69309452f9d9ffe6f3694e409ad1b77fd8c32066dcca4edf8885e9312da3faa5df33f1426044e2a2ca8fc4b049c1d1998debeda0486b4a2d706ce6d6ad321a17c78400b1268db45a8268d43313e16da0b0ed8e95459287363c25162a22dba774e65b45601c84572efc0117c980cb05b0a714f5a540c829f29c3eaa243fcca246aa1bb8a2eead7be00e5693155c1c5f9cec08911b60f4ab78ee96646a722bcfdbf8108131aa51a3ba92e77782eb6adb6d80b377e5f1d5247c1012c3a8a92aea66deb53ff00ea565e5e1835645fd7d0854458d63102a336207a96139ad25b7338c70095de228305c746d7ed9f8d3cb5606682c4b7a42b90e792bf2341f7c3f7cbcbc1ea45130068122f2930d60e53da4d715655f871348a60db6555a140e6b6c2ad8459c821691aea967ccf5e33d3cbdee44b19a7b2623ecc0de3dd4f6a08b899a36dbb5917b0ad0957328912b433b674122c000942f6c169a37bd73896453c4cb5820cc1073cd3c14779951b32d32d62b43c5027ac5f7f8380ecfb0ac84693c062a167652c7fbf45ce1e11cbb845cbbb7c79d5a2b22a58f3188b8c0d1a9a2226a90172491a6589d8d54d118626d5a7e7116399fff7c6d5269042500d4b284937181b9316190f871997a8233817fb1e2561fa9c8760506447dcc9d9ad2d08744bbabf19d124e80583b3fd23b4e9d9cd50fd310539845e13e5a2d27f32f349a5faf0c2eeb418f248f3b463584b953e386c6c6a72ab1687650224649aa8c617592cf93612792d62d045455d65c32140a7bbb8334e9c362767650bfdc93dae2cf85abfc79797296d4c161ad9afad0c1a10daee6e24132986af038db53b84ba7134dad9d7b0842230e3e0257f30951424c5f3d3cd5bc5cff353737736ddaaffb8643dd21ee59b041af422012452b88f1fa5c395405b1689eb6e91575f7a7a4ca927a938f68cac023b04bf4f264e52f056a289012f99dff02c1db2a3fdf19a4bfd9ffcc1d93b800049f38fbb2b4a3435ace62d6e913833cd506f49f396ebad923a4a4159186e350801b2682821401731d49cccf89720972d673dc1e8addf9cf1fddc5e909442470028443306460dc7959327adc9678a095c280e034e55fd6351f17e14167fd83d260cc1b143d5fe9746548580c2a6ed2aefc4c1578b24dae2eac5566125726a7d273beb9e4f4b95a1bfc13ffacc990d10aba35eefbca151089b02b8f9765123a41508d11852e7e1a043af39c90dd7cdc7dc48537bc2ec8249e7385edf6d24d53b14a9722baf448dc1a87bd28e6f8d9175dc27031055c31295233859e3ab5e0360170b7e9dda0d83e4d12304fad65120c241121d21eebfca8a77dc0eb3fbd88f8705392dcdf2844b614a1c6793b953e9fae15853b76a5e10a8e7d0824c29ecc382204e5eede626048bf1a0feafc1c40f366eedb458ed11c66087f50a74616e001d06a3e5ef32eef8da5dde6c79a6002b5c2d22a4595a61ec35ac0ece52c3ac15d823b71eabc645fbe8a42cc494a719f571ea0eed6c66fb308e81d9431bff9531e70e317b916baed829e74dc4e27bcab18bc01c2fc235a52f23cb0a9624b30b02420e8faf738b5ab29eac1ae8476d0980b8fee696a8ce0aef5e55d14259d7694f1511209e7356f7bb8ae62c868a8ac8b64cf15bcaede815d85ff5e49e348f6eac2b01b43a9ec10e379ece042e62453cd876989da18ee2d5bbf767159d595faafc2b0d8e5f410f2b979ff760cb562c4fdfb6aff73ba165ec4084335089c589ba0d1328376430f45ff6feea719317f5458ba49b78370d441fd7b91e468d201ff31cae01487d84f9265f6fceaee32cc5e3aad36d0824b6d79bd87b0bb1e17e956ba52721db06c159ef8ce2ace4fb6eec50a46cf7ab9bf69fea33b43df03ef6d472f157064eb51d26572bb9b3ec49f05522ba675109b8864d99e2db94f865cdf0fad9bb286f2a7c6cb0e87dbe8f98f39a7353854ea8323b6be0a7296d5f8d1ccdfb13750d41c03d303839615efa57f91d475f515c48546f1acf05833b2bb8bf89b38c701e5ae6e1a61cf9f70b287b529dc3ce9dd44c42c31ae85bb2fe0da427d125fc392073d4bed7772b1fe94982c53e89b4e49ea654554992e1b68fdb7b164a41e6cc14570958d4c0a81b936342fcf8573a8bb8a8823506e6230d525a2e14fb78566c0d8ee8357db2483bcec6246a1a8f3dcefea29f96d27850e6a38235754ae5fed712026e44da2544e01c4e5cd71ca3d6be920b352c57d372b85f97ac0eef7531592cc1db1e5694d5bcb6802294a0db4b0fa1a613f15752fcc789747541bf7ebd4a2ef2cc5a3b26128a80ad37da64d99fa104f89f0352017cd6028fd2938d8ab50c175ab862283227666077a494213df3029758ffa02b994e2f501c3bdfb7d75a942962bc47477c70d4a742503a3246bdb2ab49069e385d7e524d0e2936235b9bdd0342010d4af6483b3e3b8bf07382751cd54ae3db4bb12278dc570f35fe36b0e81c5b9fee51e6026512deb69f94d38467d621b48b9bd22b5625e0c3c0ed3582680c13f5a9f57d49e32e2f5060b669193f210f436d27ed3e07e26cf97ff96252922f3e4fa2b4940a57eb0595a72c7cb69a23cb816cd758a7574183f3631867470019299dba34398b5e7bf4ecf10a29394f0b64edd4d832c94379eec40a1d38d50e184d196e0b18cbc747a6c18daf67aaffbf0fc659cd68087adef9287c5a779b4f2fe4fe3c5b38cacec902fd8a977c873891ae17d3675504c55f58c68fbbb17a0d0459e2e567a80979458a1fae7b7ff6379a662f178c5b001eaaa00a8a79386a301d38f78ee428713a229a615b0c0e19a034c14528660a11f74c7713e840b203d915ad928d67417b30653770639267209e626b7789cf9c4be00c409e4b9b3fdc860bdd9b6dc989ff6cfd1f8d84ce3b516bb3a985887a9569d27f82e1935add639923618fae5897fbef40d86b9c129fb534b9c194aaade3be069f6b9c2a70ea8c8f18050a152a41d94dfd5f8aa404ead86fbbb70126dbb3816d5fe890f7b1a1182c12edaae21e5769fe098e9e1e34d4b921065590fb14cfeccddc1944581520eb1c15dad8fad5f92097c72bb00e3f7b75a296ef697dc1737a1ebeba5ecf481b2c591c293ef946df0ff1a48419f0015fd904e2d7e047088fb5d57ff7adeae7e5f12b284b386febe4b5c59785c349019f58fee940078e7213963a4d065498838d8eb00794aafa219f56a96851d41b10a888a2f20e8fb1e78b5b2a5fca02583871e33a2eb62c79e2010271bdea50e5bda4a3b4ffe85de462a6294a67c22f0b330ae25a2562d6806e66e4aab66ee7311990af8080a875f920485e25e70143d9bbf30cdf267cfc807d8f36267294a3705c53ae9ea4b15d16cf0faf10a997468a5ddbf59f190f48e1faaf992d3fce76c4bc8ea2f8403b6309fd8201dfe26153a4ee60110e076465a17279433baae104eeb81f251c544142c6fe00c5af6ee95b051c5711a1273feabf1537a9e07036b3a4f16ef0d8a5b23fdcff76ad005ed83b7d0874d873101196a320b4dba2184bf3114a5820956f7c02e0266cefb3d4aba12334c0d8f61f7447c2b718fb4c02d9a65fcbe7cf7db45086f265c76d3df939019905779fe72d7e042ee3430da2eac1cd9c929ce16c18d7b37b3f69760c95bc7af28ba1dc6b05b70ae8b9ef2d4d19f22941d3b49fb7afad6a31c2746e5b291c0e7c55ca34612e60e5a17a60507cb1579f31b0a2fe81b3b386df5bb5b3d2ddc2b454796ad633165808c46caa2d0979a836997cfded05266f6954a083fffc6294261e3f2d022b0211bce1f7cd80165b83d87892a9a7610ca4e0bd77884deec7fe66a1feafd9fa2e1aab6da94128c2dd58b63434708c259c53baed64d813eee8569a7acc59e11f10ee2624d3f485b043bd5bc256b103428f5ee599f97e2a1afdf1259cae3ff6ee2ac8fa3f16a703c5ae131ce0f81e651e78422604f1db5f6fffaa3f5624f9749820d6f86831763f19dcc7c85e51367e1d4f6693f468c577336b9f7b5c04c081572508f7873c8b9b8c37058dc70f068ea9e2ba9efda14c3adc6c88ba3ba080168c26e48b031b9e133f2e164a0f1ddfc4fd5aa43fd35593317ca46851129ae8cd5721a2c970d49fd4cdf955b5f6d7333c357bd665aeab15a3384b57d859aa4a1b76128a3a42159c59ceb6738ca5ec116dcc4fcd02073436a0c1f8e5d336f301a3840275fd8a4956439cb837c567a52a7d6268ac26d63a141c250ecff3fbba7f82c215203d7a17ab5f8d4ebca880aae8d3d7e37970cb4fd50021d881a60c8fe8f5941300dea436606734b6d6abeab06e16ef53dcc6c10d3ce6d9119e954a102d04601166dce67734f0aeb698c05dbc0564337e2f876ac705f5214eaddd75313aa2d40b588918f7f9609322e31613a1139752dfeca5c647e7c986070f86bb6e21e3660c96e066e5676fa4b4d7cd601a5f08f4fd404b9f31fa7debd54c539527e2de4b235421e110d278b6a85ff6a55711357f1ea268eb0989815601c8b86d996d8adb1aa4794be20b4a3efb33ac6384b72c2ecd87355cea68b7467895eb1852fdd3ba18079cf4795302cd09b80b586ce85f3a683bd10e9c5f621c46fbc1d7bfad31af2b2ce5dd320571970a71932a6d22c32931dcfb794786bc4870e12b87ceba7ba122d75efadd03f41072e57bc18a44658581d23fbc01ffd9b322fe03c6cc783dc704c78bcd80badc9cac868c9779bdb2fb63b8c78bb92d8bcdec7828fcfb9d69b12e40e3becebf0e23dafaa9cf36484699a073ee4a46add748385f4f499689bc07250f85f60abeb0af7a01d761b2e44337f6513d1dabe36e190fbcfd2515aee10200dc9e4857d0507282c172c7568b898233ba6c62ee35ed5757938cbddf730db706be3ce53df0564857712c67011c36c4e41559c40e82dea454cc3415a8795ff70d4a31e786da04264636f3827b454abce56602542dca8503e00a27a96dd414e32d09c480c59160c61e53966bf730ba6e7d0038e98fdf176dfbdf4b896bfc86d909385220b2b5a85adfef7116b81ce5daa5d80ae58328964aaf1cb42062c3588b23d8225e20218eafe15a96c1e413c1cac81a44d7399c1180b2f50fe2a8d0caba2d273c4120967a5ad40aa9e4249561bc347c99118eb604bbe07ddd199c51970b0c809b7a02b33480caa2a3d9bf44829458b0b1ced8de0eb9b659853b01acc90b19c4e79b6b25c335bfd89176b37efce3b1a30e127a8091f9638d8a105c608eff29ec3e5f76ada9f9af3928dd75ab47650dc236a93a128e68e3cb68be254ad6f1218bed6c2f1a2a5759af08d78a99a9b297df8d83102eb1eda0a0d6159ecfa6cd067bf88f831e6b36ac64f7c7db2a4c446d300b0890b4b6f835118373747060992727702af9ff71656e744f5f99bcdff9c75e44e555642e457e44a07cfc7a02392c770b080ff157ffbfa07ff90ef50da628c01017252a761edfc1bcd67964293708b34e86d37639f8986a1b5e0ef91848e1a1e65cd2d84b6c6fbc23710d7a6aef08cd48025d533f781a94a683607085a1bc1d01d5ea23adced4ec38700c27de5f12436c37506c7f06bd1b9df987be833c6ca7acdcde0ac582febb23fe1b03b2cd2c5231e1851d16ca7522f2b27eea98026f033a380c357051cd56eb4b127351610c93aff6f7a2de98807197265ec4d0f2f733c1d656ce2a4101b561fce781660b1f7bf53105ff49bfa52b110d78c23ab6a2c37ba50771855bdbca44d697f2dd0603ce8a70ff157414a3b807c4e64eea019b3ff83996741aa5f0533274dd5b4bdc06db5d9cec67dd612526418206827fa58bd5bcd8d4e3d496b1d214d950abe2e72bbd6733c88ba481d6b91bd6311588d4c55fe6957ff7e67d92052d086f92e4d413fc21b7e486aa4c198bbd630ddc54800ff5380a0f7c50d7d21e7448f4aa130c2820f358ae3cec2940d2278c1984161485fc9a2f7cb131dd07fd8fdb768832bf2fbdb1a39954912810de60ba861ee19cc98a7c4d1a0d9bfd34b96ab4248dab5e2839aa8c828bad0a1e52bfc5dc41cc68e9d8db13b1b4232be687646aaea567322ab52e1e663b7772251f8c886587a6d0cf9b7007fcb70613cfa7f9808f9517dd00f8942e6ab9b996c89614689c9ca6fc481459632c449d8780d6420e0723db820d99b54b59a3aa215bbc5b29bdb754608c575ad25e869fd900965ac42b48bd001215e5b1cc02fb739580d74129ecc0424f058df187eaafc04fa7080d3553eb6209c1d0043ba16bdc0ef206f2d6177f51c6fde6cfca6e5c19dc43c6eaa928402604170158bd6b9a6b48e08108ade9ef98613a0bd63dd80e553e5deb4778661a5203f4400f3d5f3a7e99b1d50d6b340a26c6af337ff8526cf0d45521249188353624b486cbe7796547a78d0ae60a713d06210dbf7012ca5e9793a60583278f3f6c20c053509af9e2e413c54fc15503b3928bfe9d65030f215899ce4dfbd58f602f0facb187e3ce156a80768c44981a7b51d46ab54352503bcbb43f8176023cefed2b752921fadb0334953cd7285bd8f5dfa4b4440e40c1ba1c11a23b8c04491402011e8936bc597d8364109f145861febc356f0e293a05c06e8a9e2615f310769927e2e6334f4fc76ff2a94d7813b5e511e2eb9b091dbc783a767070c92ce7c4781af43af8151ad41e257694bf89bdd15ac0a874d78622daf8aa307078e6cf49d823af3b47c093467df7d7f187d476da8529dfb9045837085282c9ff4e17ab70f01a146e615e1bc2d5d8bfef51105a7fa97ef45fd150112863e883ffd48d8e005d15b63d18856cfd202cb1fa3fa35ff6d4b96ad68d34026c689389d0ae4155f96427e775a31f5d9631b517d647eee684c1b8b938f03f5627f7cf3925df6f3a8d1e059d4c1406d4a1e2741baa039df5dcfd55d429ae9d9e7b6e5b821a86214beca00dfa91ac9d5ca6a3f2e9ef325d8fbfb4467903e6621e6a1120337debfe0bb08501d27fddb4ee48fad2168fe08667a03db274a0baa96562c3651146d98c1a81470bfd1ed8b7bbe939e1211c6e964d8634f4a4b54f2d4fe51044a8b14426647493020584809bcc39b08065247f6872f49b5c55077f0942ff9563b780ad31cf0415018538b40883db6cfe97c7556ec6ce791bee24de237ffc7ce3a2e17a7bb6afce07eb690ddd8bfd6445493d07b9ee688f8a6c504754b7c454b95937f6e84bb0842346eea54d7c3f3d8cb80248a29fd6b5ef383be399bbcc34cd792e524bf2de4018672d3cbadd69e7d72819bb4406d241b4001d1fc24d0aa7b380590bc725280c0b627e5abd4181385475b65df5b363e1b7187d62cdc8ecbbeda7ba724ac15e9a07e7523344e40a5d9cff1d5176dc4a4bcf64205504012409fe8a6fb098826d23201aa668a24588c24f6b20f9b1782b425a86cf311d8300f9516bf2c2a64e6a782de8f1d417001d524b0df66c926a62f179a8a5d6843c4968a81a5aa3c049c3eb186495d63d4397d3d87f37172e1c8f90b2d75fb892a8083552db78e6a99233ec2c1752dfc1114d5af842c4ae71f54d6c60d9f88a0199c68ddd6e408daa80f5ec0e319530d48021cc31d82567b23504a3106bd6fb16cb9e0d4e62ce71c4286fdf01e724b5b45d24e55f3bf0be715e984e30ab360ba0c89aa9ebb283d696ba0740031be6ffa66eaed319bf741c34a6852a503aa87aad91f74b58968fe0a183bdea1329e193e4c724a90dbac6dec96742e62e05a023ce544b963c853aa182d63fc801d3915d5293d097c26efb412b3ef3b9437cfd0a8c06705846e2f6a595060f381bc6bce1b82287bea135d17a722991d2a32d2244a0a589b567508167be36da730086bfb0d59d62ddb2876ccaf638686d2077eba1b2dd679663085242a09ca77e1091a76b1cce62a1a931254f145ea4b0a0e202343c8c83eadbeaf3f2fd1d6f1e056ac8aa83b27a0c7d281b42688df0cb1ccb4924e95839a06d70f83b9049a86a2f33a21efbda023d63a0864fd17607edbdb94e6887cf1a0408b955b0994c00be1d526eb3611b2da91c9852a13b26fb0cb5ba55c3da6b31777c9cf040ac18112e1c2130f9dd37e2a09844d0b6b81bd59c0912a6faef3dd46898cf6cb157571b0d18e12202dc361bcf4f58f1b72d7c64f41b1c3b44de5c80a4b6bfcd0dde979b72273e2be3e25df7fe3f4c27629286a5579f46e00f4af84dbb41027f8a70f8d7692db2e8bd7723280a50b294f55bc241f9e5b696e8cd5924847f025a98fb17145c042763c89db51c8fd9e8827f8d7125448e94aded63e57edaf65cd194214ffee41dfda7a41e1f30519e939e0e5e2af96dfe128e90ee63ca5575f318e8cd83a5c2040547d943cfb96bf470d2e4192f1adf01dbfe82daaba5c6547aca73f8d02d8d030226ec5298a5ed3698e51953986966cbafc96f819a30edeeef9fdf0ecadf97005a2075b29b22405b5ab64045c1c1709e017e3ad3d8db0f1969bf8e32c394a61fe0c88d58144f251196c65719c351be4013a906013010d104d27aa14215c1970063e8ba4ef185fcf9aed08276acd0fc05bc93e4589a94e710fe273c1784ea7929f5014848888e413e05a88b5713c63831dbf40cb3cf10ffdba511fd3736054314f5ed6e52529f110e4197d7546e31cf833bf82b05101f5b6d0d3d761f979d670f96db8ccf324b989a7ca6d2b061c73871f9e6f6ebe5926dc92de0c57093167a0e43280b5476b8e0c2bb59e3ee0c9fdb9101fe3235a661ea16c68a2d45864e15294667f2c6f66f2933f3b24cf97a3eb5e6cbf3eb5bc3c5d22130ed78078d3e8f17d7bc646707140f6adc7ca8af7328b46b031c9d7c0c6dff1d8a508d5cde4daa2b2333c99ff5a6452feda1356e9731057ce2bd32de650ff1bd886e1de8cb2b9213f15adb80ca5aac125f6a7a0fc2b9a607f6ec9b4f82f2825e9f3ba64c050cfb1d1f0de1fcf20843e3a6c114f5949ce7a681503d96d758b6cd6516d439025d3e0670c68689f5050eb388798cec5bfb0d32f8441f589cf5e2bca613399ea600040f0f96ca179da997df17d5b5b4e9673c4c9d4ba2f34fd9652d1d199ec7266cc81e8de87bb66134e87b344bb201fd69fb6c3a9ab0bac3dd2d5b24647e03467cbe1773c88395ae3161fbc8df580be4c4edfb3a7689c600dfd1fd8ce3e02e5c01b821520a6dc270ea35487c9db4da760bf52554bb62dddf9c28bdd1191c2901953e2efb8882ba2437119e421ef6b9b81c1ebd62eadf3fb7fd6fc929b4a01fda74c4033d0daaee5d026aae7ea7df23c60f2e05c3017c4ed4d257328ccfcd70cca77d1ceef1bc361b2df4fe6ae30cb8e8ff640d2cfb4a2816a2365cbcc8835f002a920cab8ada2fa6ada359edefd143a2c4c94078d370dc306430dcbf587a725f4c200fd6f1cdff764e7a680219db70837dc9bd47fc466ed1b6c8df08c0a4d238d4f820104b37596cf0bc69bdb49cd0bab6223c3b8a3ec7f8a3e7bf27c5941e5525551b220dfad49e34a1226e3f594471beed98796cc2e0b7073a6639ea7e2e83ef0928f04fdfd122d0c8b65c3b3dc0f9fca9225efe708c2723f5aeafb4e143829c910413a0723312dea972ab757e4c3ad76cdeebe6d43c3bdbc8d17409ff02bf08a62df9843ef911970553ea66046e96b1e7793158a03f3d6b6ec9398c4ef8f7d5b603b7df992b8b01af39f8964cde950884ca581157d3bfcbf29a6d15bbb05a576f14aaf52b93e8ec1bf1d34a63667371fd10a05431c1cd9b084d142fd1cba785ab0cc241e4bafdbaee566c19f1e6ad3260973b33c8083b739622fdd130dc13ea431c84678cb09c5746970781b552bca5c74b5aea2f80b26c6e8376271efdf4cf841c0c6fbcdaa7d0f90c57a776d243df95880c53048f90cdbe9b506704e0a349ed06149c87d190636d87e4b56ffe7f3ea3a21e4735c996dafe50ad49fb09ed81e1082e24566da807189b2230af5d251906b10adece0549ecc52206092368b3811a059f5b4de39940388f4afed8459a7e305b328f5b7085f65879b6f6d6dd79fd14122be6ffa857ba67d1c7368d61f9a2de2be7054876b02f15f369ad89a2fead3514245ac68e34c9e0199bfe3442c1bfc755421f2a8536c3c06d30596d10ccb42a2efebbc113f3b5ca11ddb17b7a22386c050bd7ac790a32c5dcbe72565a6cbba60767c7b9d7093260f9886b3e378bc4f20f423daad7d8a868c2d0aa9a6fd6e85c1d32351f956e424d08282c5a40ecb0b2a6b7a1831093dc5636d442fae0a3ce9a17cf27d87b1d0eeb1d578316f3da1c1f8076b0aeeb93091caac06b335f33b89e020e6f5d90db3120b31d48026f47680f8a8fdfdec73cbe0cbfb9d6509168ebfa0bbc825a37743f0cf8ebf573da1593242b90a07112930d104f6761d9b8833f9a190f70a4c200d01d79c2f12fe57f1237c55db6abb255c19f7991f845278e459116cc9d6bb80cd15d92c5fd81cc1fa757b7a203522b91b937145a88398cc7d60108c7bb3461ee8a6f6b5febb2f2611cd17d082c69602228db65cde8b2150a39c525ae60ba22f3f0083f4179115a756c1cabef701e9f299d0587210fd6d7973d9a0ca9c937db0b8d2fefd2fd9dc43d66e2af6e7e82962465a88e30f94e862e5cae24f2bde49cac5173dd44bca18ddd0a02d01e82f7191f2876ccdfbb5831df607a2634ec64b1cf29483a2fc6f68f3f1dfcd3b5ab118a83c6df495916ec59ae49b52b48de85238c04198be572ccb949bbed364a7992bc0ffdc085bc49af7f79bfc00adb7c54e20221ac48feb584f4ad49f8055b5d059f2b1ff3e1a8ae637abbad6d0e7ef016e15d8bd5ec6d7ec25c193a927bed985f6e3d458da8f1040fed0737a5", - "public_inputs_hex": "0x1d420eaa08a65528f470fd3e2a802913b611527f41fca008a0ad4e974750780314124f85666837486f1e8a234427c2a03fcaa651cd26e7f648116568dc294a7a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000ca7dd9181fb990af4173ed8bc797be72000000000000000000000000000000005720482e46b084a93e0c80c18a2ce90d1306b6665e7c8100659a2d57ad42b46296a1d6d5c4f23bb1b7bbee394d7182a8234e2191863d749a921a9ba23ce96fee3445e2989d7b03e6f558149a7c72536812f67fef27455b7cdd53d7dc2b9dca1505a960987ec9c9a7a823b3519e453ed403ebc0fd1fd65ea0e27f649ecd9f2da0bc3a50ed3f330bcb5ac06205bfcdb662302aaef600b5d72770aecd785f476b2a348b28d745dc6dda97e5246e924c2b810dea249216df43db9887f19e17547969994f891e2594fee031495261d1eb47f82eac46d710f18b5e50378c0e360dccfddf0f63519ad4d4c6e116f27886c571bf28d071fc4554c2fd9ffe1c45cb6ab7985b8806e67e39edc87d973d3be15694e4" - }, - "decryption_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000006f6a250a4a1febb7300000000000000000000000000000000000000000000000c59a1af8af26f3ac5000000000000000000000000000000000000000000000004a3c606a0e0f03f890000000000000000000000000000000000000000000000000001d8ff02b1949300000000000000000000000000000000000000000000000f8463282a1081366d00000000000000000000000000000000000000000000000e52a6fa72361773670000000000000000000000000000000000000000000000004dd0b3cf1dd7ba4c0000000000000000000000000000000000000000000000000002aa4ccdc1dd480000000000000000000000000000000000000000000000099c677445ed8ded8a00000000000000000000000000000000000000000000000778991a1741d454c3000000000000000000000000000000000000000000000005b0da14a7da205c0700000000000000000000000000000000000000000000000000016828b4702b1d00000000000000000000000000000000000000000000000ed1f3a4db3097256f0000000000000000000000000000000000000000000000021cda601a7590b48900000000000000000000000000000000000000000000000107e39b59506f625b0000000000000000000000000000000000000000000000000001e3211e681e3b2eebd7a56611911b93fc686350e88a8a65ed58a595480d04fbac1d55fd9c0ebc2b0245583b71a1db60cfbd36ea9d58cab4cdb1b4eb935658cb8699161e4a45371db5fd191ce3d867aecd08a188f46a34a1c5e735f9625d6d4e2c74114dbcbc36151218ea79fcfebd9c55715d7b1e1e20673acdb923a0fd057e35660ccd55df1b2eeaef0b527e4bcb83e87eab2e9a3cac45b6af7dcfe835d302ae7ad42e9fe47e2840f53a0f72e5165f147bdeef5460b87fdad96a1e48fb2937b3c191ff86a86f2858742a0f0afb470908887a7ab3ace39ceb527df346161ad02f1c529341ea510c4dba0465b1e6eb158c5d9c6acba29fda6d74a7a3912066b398145ec000b4f72954904605cdd3a19ad3bc8fa8a6d69bdbcbe469e9462a9e140e98cd5962564b0e1c49661f0d6dffe5ff6d29389038359770234b8eca87fe87f2c60aaeae6bdb12eb3ef85c8e2d8971f9388c28e4b2787e724bb9610400458bd87e3ce4cffbe129e0764e6637beafc60727c2ce766be40c01daaf305adfd2d51a9320a4bd449e28824ff6256a3049936a02348634400f4aafebd2418f5b1e403798dfcfa290a026043252fdd05203a28dcb0e3619b49c762236da277f896b47f619137c3b73d521371bda5fb136f511df9839d0d0828d7b817cb58b6f0cea159adc08daf6d78c1be7d878066553514df64004d5cb66dc821c57b7af04adf847cccddc85b8c5632b7fdfd8319623dce9e8afa159e3f412d5137ca336f059c0a7fea1a2fb3838a013de41c8d8dd53cd2614574b1b77dea6cd0f425266b276438bf95acb750f33b61c2d1d7602f576eab4c1c45506c70d3abf137ac4b08f81c355917e9f470962be2bef8b0e44b219e571bbcfa226911bdf65881e142976b3d81e8c7b1d2567ffcc1fad534a0d72ff98c868ae24e8d001c948e993f07bb42e2a0068ddcc7c5afbf6066d0da75ae57e56f28e0276d4fb0ce2cc47496ae818350be2b36e2b1b7a465724040a810a5fe13c0fd37e280a84ce71b0439a092c946956561966b1c0286362274ea32b275133afe856871f0e793bb5929620e980d028906e491c790f3ea5f4000a74bfca18178f0d059c5e520c4be6be17003f31d94c129175a54b7fe94af500362cb207b1026e8a1d7609de8428a8a8bbd3ad8f136cf9d68e801042821c432995f04b4e8c49a499413136ac1e09615ecb0a3d3c6e9f64470551488e4990bf0b461f69e3e8454dba24ba14e5ad4ceac4395673339f7e405d4786e9642182bd0f3713965065ae67e9b807428334dd2fc75d89c79dc956bbed889c7d0647d33c06f8d6954fb910313fcf489e6b8cb9af4da7b9e7a8abee4c6c64a3c122fc60e213ab8ad37945ec00d34ce1a87bdb4396e7174da58932a18ff22c78e9230c7ed219a311a3b3d4d841c865cb303ebc539671d9e20a174e8a6f898247617903c38d1a68a623e94de5a4f77833ef3588fa4b8859c204f8db36a8ce5155480a18238e058d866f6045b0bab9b71bab7141620936a16c5d4de49a654ab98bf53d58da711dc182cae60289b2f0daa0f6dbba016c9c38a3c55b82d376d6e64a333bb9030418bc0960d69698fd5578f948908b03558847faafcbe68e98c526cba756c8b7de263a08e96054f095aaeaa1280e303ea25696f2901ae6abcba9a64a669fe47db603206f370b9fcb574369c5635593055af1b4d56853989f30f61ab44805c1665e010deb6608e053ef1db71ad837bfd7c7974945be9b9f76cb8303fe2888ef57ed2807ca25720b486b369c04ce936f5e9c58217081e7ce8b23424965721b2779ed1dfea598fd0ee145093ecc34e8ea405396e5be61556a2e3d22788ac6aad9cfb41f769812870fa17056e2a5a05c94e5f8e760bf2c49ec706fc3cd90ff789d60920157664f597f1486ed5b2dc6c04435bd6f89b502416c2adb134190de2fa3fa7b07014e9a82391751a21b20f1b8babf3381bf45d80fa8655e229f0089804bdaef0fd94cae83a861b00bc1c0ac02c9dd258b4ffb19f8be22626b6f6e95e74e7abe137fb2da679c219384f2dd6cbad1424fcb90739469de44e1bd40bf64efa0b5d4224c9712a6295ff474b10f53d7ba74b63f663d13a5a54fd63ee5fca7ffe8ec3c01485f6617f200f31af70e74569b289480ffe7d316b96b60787d5052025d74081ca11e4ea9c47153e507f36a7fbc2777afff5726091a50a2f33f024e8cb337311c239793e288ea081e44088a7ff3208291831a5946019a8dfabb80117e7e57222de532511eb02d9bd27954890ca5d4453884d0b49db75f2e91029fe32287988f1a86ca90734bd54081a788f073cf1bb67a2db62208cc072bbdcd5fcfb7d09a6800cc2073538f2383e799f53490299bccf66e236cc6f9d9863c88711ee89f4b5d293a31a4068535b5b549abc5416d7bf4f651af578d086487ab128ad66ea5c67406117e6a70aa5df2074ecec211bfed976249a9640a17371753633bfe6540844b02472900222a3dc9b437fd6236ae893b24ae71a85cbb56d5464ddd02a8231c2a0cf89ee27e19e3c5a9628cefb60c12821427f0f04be03ea5263e2ebfd6acdded2b1413ea3e58241f5ea6c907730e1ab1fa7d20e2d36b35bdef8d442fe153707e0cfe1fe11d6ef0822e77e63d3b095b5238058d65c47e37bd0ed58661e249a539292c668d7cf0adbbb4b70fcd2d4f102160c666c3b0b52a4613f499fc364a1dad1e057938fcd5d1d1ecde76e7137579f4fd177b07fb00a0be3b543d4627d240411a603a64b5adebab7f73968d95daee8306193092539d7ec3951e1206f6767e1a1719ef82d99b12cd8f0649671145ffb54f021eb7d9950664e86189eefc53291c08616be5b938b919026893cdb62d712a0e8a94741c71582333a1fec2ff7ce1ae05bbdd516a213dcd4ea1de57fc041aa14ed3bbe58e83b294404b816f7789f84f20b260f663dc4cd0a4ebbf8834ba15a3b17148b619409d24670aa3eebf83a301165685ad493f3e20a5c9d1c79edd8ea4b96ee4be6fe14e235b437e75add1cddb27364962cc4be11d6ebe61e963c83ad79d2a308c9a5364906c360cc3a562758e27f0758e5cd6af677f4b11351815824d101a3f64bd24e11e388afd7c053286f923b2a998d37a0ae15a64f7aa581bb3df7addf563729d5db423934eaabcafe5ec0cc957736c4bac0d9d03292a067def613c6c59bbedcf772fc2bdbd8b0007258715333451900c68d1dcc18ecf27ad1b55f8c7012f0cf0601d87916ca0355b3cba098bd805f933f11fcefecd5f57d21907ce5e7584e01cfd026b8f6bb31016fe7a2c4185a450fe19daa4d166d1c8617216a06a5adc7fe2bcc99ea75053959977941cbf4ed4a7a090b65bcb0f33e5ca5383bf3b4b6c669282b5d3d63ed4f5c02beb2a167690b5f5c2de8be567babeeea8be6eb3c40033cbfad9dc753a99c02965e90d499f37011f3d2eccdbc5aa35133fa5f5534474af15d1c3056bbdab15a298d32b048d3e96688628f7dde691dfcea1f8978ace1eb95f17a962d3166589c32fac1a3213818ec71925e571d45fff508fb5b87a388f825b82d7617809b8fcd545d121f5a78be2764a2b92312c20dfdcf1ff11256604b49bb821fe6ca9b41516b66c2d6b291b7a6798bffdd7fec302a77404b4aef95db0e03d4254cc98571e31da4d2d39fc07bdaa371acdc2a3480c6ed862afb519d6bc7909efa24c718d5498f046264a5fc5f572796359d46225542ebb7ff8415c201dbe77f2392b9a08981f7e252049edf02ad6ae8eb75dc83b1c9eede5fd7d3eadcf233ef7fe6dbe6314002cd52fb36e36e9fb1c63d4ac4848c9ed4e85ca2a987a46bbf8b20c69338632d4ff530607f89ee61763bafe048e67ca2d34fe43f55a0b5a5ea811d91e62873588a9f020bcc8b0aec019a07228f654870f8312b4e56be7d06638f17658e3b3babc1c3f1394aa1c6c7540f90dc2fd228882153812bb16e50f66bc1b3f75877f675e0449296c1afe73cd6654793ee603289a7cca9c277774bec8804182fcb41ba47441441232257cdba59f6ed4003b45b87ca9e375b5df17bca251311c83e9c7069be041091c3fb4e4cd94ad8d916061e8fe807bc756ea11347d15909f0132ee1205a961156c257efb4477aeba91f9d3efb3a4b47145c58362fe64eac997fdf21cb486a41954720168a7409d743b49bc7795776876674140260b5901ba1de71f4cc798201ac47eafbebe38f681c6a7a0a7929da39faab4f1e0ee2980d3c07279b578a21e195817a39179b2f0ba31a24d1cc5810bb303d2ab8fd75f8f7f1f84508ce1224b222f5368d9d2ef5fa72feae9490562ee47946240a8aba30e386e23e67723d024081e1c47f7d9e15d77543765fdb5d946ef24e664d8a79d940dd66844734734502fb251a50fe946427d174223195b54fc64a7ff7d7f97cd93a4b37e98197fdd322128f4250f9263e9a8b67fe5ecd74e6dab5b6c5054cb46cda9fd1f16ab4140f61b502c075fd23263be264e26b8318e93ba00f7e03239cf63bc80e666efc2c1bb1b22e2005325a89dd3f3d2479d051ef24fe5143b15c095e923a538e3a069ede011f2ce96ecaf03694984d3da5b5257f5a358a362c4b402e047a2627d0b76c7e92bafb9ae02257011ce2cadacd52c5ada851aa77c68ce4f6c983a8bac1a3b6ea20b112c2648b421ad4930c22ebd76161042a49d0594286c8baf2c9950ac816db61dcf89cd34d49a8945ac94287e87e4f009397c9dd023579f4f4c83fbff5c24dc05af294f233e1cd482c74b89f426b77a2099386dcd9c9057c218ba64ffb9e9f41e834a833682f1887f1b01984808e368558617596c2ab9311d71d8bbdc422e860a4ed2930ddad4e4b8e9ff78a7a435403200386922e78685931cb45238eec3ba2b469469b13ebddd9b01d3e58713111976415afc949b1b84721be8cdea6a6ad01f91849d3b5e797917755636bd5b0d786626929d79f05bc38d9710a48e15fdce1dde1231805cf8c6ef93ffb13fd2c5b2f5af7419a633a465016787d6b78c29de15c07c9db63fd939d159aa1a9efb230c2db25b6eaf521678b69df7ac95646d9c1ea45a1812b625b0200556acd22915d71dd02c3a468bf468f43b7fdf8a8b1fe32607f9c81787086f4bdfc1be68c697151cd941142d3f9c0947dcdf117fbfa2d1205ecd79af77df27a988d14afa9cd1683a2069f4490a1623877451b142fde124002b2348833466ef62d68e4fd920f71769a0a0298e6ab92a831263b0b20b5b6013dfa805f77c6cf434af3ae39038bf1449c63aaadd38075f752f4e47613577d408e8b5ca142756d41e2eb0472e6540487a22bac42602e0423a5cac79772641f104a9517101b1f47e832e86abe635383846dec172a28f1180dd2c32214b7164c504f44fa9a327725a9b4e34d93777e0fca741b72b4a7346ac0b3b9a6282defe17178506cadf065f4a9fcc71e6d449c9a6ba0540c8a036da84dea1411b4649db250a59c742e8cc0caf27dcb93f0bff21379b7f4856919b76b86a469a823f767880144c7e236b2ad27b7f56620f9794bfb17608e370bee2b57150aca3d662b130dd2813e57c20c3e2b344c84a6cdc8ac3aec8af233b746097711a7cdbb9c156195a1cd24f402d6177ad9171d6d4997bdb82874f865c66dd2b623d14a512b7e9cf95158fb257bed0b67dab648a9ec7204dbc15748fb7abaed65fc04285093a3884a012bf22c65fc59af960deda8ca2375c1223f82b0b91ad33bf9b0bbfe1253b4bb5162126a4543e0ebdde3d63b3c73cb0720944ccef3df2a6e20bc95f23ce9964d805f91c129143d1f20208755600143ff6bd77038127e3f2b49b7f61af75efda1f018ac17d068ddee47de06993bdd32d8d405e0fdcd0c8f7f57af041f8621bb2b50a72166b2129f3545cd3ea5d8c7218ea93005e68c04e29f281e6a250038e92f1225c6f6da2e50b576c1d2a2d8903acdf27812d6eade8069a54827ac48dccce402d6244de8fe03e82cfbcf6f89202fd91468ec97774b147e34032fe2042a778e0251a5e9a205936055c6790f62ad08e3d5f0815f4508aea11c0b1a11460cab4380c8cfda8b2adea7508c7cbbadb3fb08877d0dea4ee35b2a46b8ea08ac5643f4f296c6bff97b0fe932f6e9de7f47e8f0d79e1b03787ff65181aef6120337e911a277d627e5738b9eb2bcd905af7c1ca217c1d51146e3504ccd575b369be35fc1e28ad1f53f59c0960282d6ac4f3cdaac4b4aa023d24218873609d64de748ef41f054babefea7d464af9ecdb67942ad38b4771722fde7a0ce089fc0fd461aeaf4829c5732fb27052bbdb6ad511071122bf33f325bda10926bd5341dc32cb5b207a2ec5544de17c7c8bd4760efaec7f21163c3265a86721f9c99df3d2bea2d4eb601c069ac0824ed575cb4150e7a69bd42ade35154d0770ed9b97cdbed427ef43e6108b201adb8b9ca93cf5f1c6f8a3b903419060ea692a2704c2b3007f14e0e01322b41a37a328e60f416e05ab97a12183ba2d7e623b6e2b6c7be770ed38fe4d17035922ad5f86d8bc646c56eca82869ff5b4f49009e71885211f70113a11a953718d8d02b13406937f272f5af3cf7d9d9f0344f14be1d0b8892d426fd09a7149918336798ab7328397eb3cbdac775cb5b222c15d5ed0aa19d9321c07a78aeec6d03ed73a58efa2c2a8e5fe8bdadf785e29338f1da6d11604b7b05368df594d9490c2d4befc0e34aeb1d2bc14da6af61e9cdda78eb6cb3a1c5417f7ad04ed48e222b63e7aa8cc6da502199c9b68e350db0ac8930efcd1591fdcb90c4416e778b492112adc65c1ce0ad7b74eeec48b87f905dc9f898fc4ba4af25b555743ef54e670b600e07ed5f17aa02c20e4255eaa760152f1e64ecfd0e98e2c3d2fc8a3668221c43fbccb1df45a8826438ce4cfbd3577f3d2f757d73633dd52fc34732415dbd0dca9c353e4a2727c089f61865457884aa1b471655220ec5759e4c1602c7707824a7ae13961f5a2cb2f0bfaf389d2114847145eacd4ae566d6cff1cbff8eadd7059a20b77b8aa5928ff8b8b4164f6fe756f19b148c526da7eeddf00f76ab88f5156992f5d7bceec8118c576f71dfc563f7a18f06f427cad422deaf7282d4e8c4075c76b106e12f661f43fbba18af93aa83ced3470c41e016ed364cb9e5a5e6280093b4aaa21a36f8d9b4b64fe9f6db3e85fdff6b1824475e632d89834ace04a013063e97ba41d0af5ce3f3c2030489999d587a9bbac31b8abf6114ee6646ef5915bb056f4d5b91a13b8d3e0ea83c49ca68f96cc4110cb85c7eec1727fa66fffb1db3826013b72c0b0616c7d99585c16498397eed44f369e9c4b4a90226275c98165cc5042eb5be31964e506c4a9db02b2767c0921774e2820a0994cdd851787119c1b56dc0e4ff44da97bfecb9bae37225e5b6cfaee9dd055defdc0791e69b9a21c7fb428cfd1974bfbbe9d12cc74bbc39321fb1772e44bab85d7b9dd947638e16c471673e961ee695ef15ab9d4a22c0372cd1507ff9c823872001f6e233f30a235d1896a0e6403ca7b322d3ff755196cc594ff984e0dccc0c38418aea5d2ac21ca5477b9e09f1138c77de8008874b22dfaf1ae69da6619085039bd72efd4e28069c026869dc377336c12a8ece4e596f75f3bee6f64e226bc7e1ddc1155f56e62a51e7c41832ad559269b9aad3cdd42d4365ceeddac936798a5b4fa5977115110bc6f45867b5d36c734ab53f934e7637aa52288fd7976fef8afa8d4a5c5319fe157d1e9ca37fedd5fdf1152724af58814859d227e4ef6b08c20c2d1038bc08990cea560af1f2d393752d843e3dba9bb3149c9750c3bd290549517943f54a4e1307849cf8413e07a3189059cd6835c4aad2a2c6615ce766c887498d5f69307b1c08cd012295948687dd6ec8f62e00a597da3f9f087e470a7925bb8f65654dd7ba1650e95522be76c8a494655578a5c81de44103df5679a9b02af615ab32566a9f271a2071644ac29256f6c4f54013127231306c880eb02c6658a335b3581f781e27429eadfbb812e523e59875914fbd471f17cb02e41a530ab33bbf16c5f3f20c0965069572b498973af828f53bcee71f1791e1e3494859fe61b843b1f51351f20f12f3b45c9200782ed36b86714271fdc819a71d8c01945edbf8db323e39aff901d9fa499d1304344994790d80698dc4b12ed57a023b57f9750cd47f9c281f6922348e01972c2a8a6dfe14204e48f7fbac8e5c1762c15b65940358e528d902ab2b61244100df3e012cc52a75dc132bc44cb380baf7e503cbd6fe5cb6e5883ccb230b486761e3142e3f5a5f8633278de594b8a2fc67a4ff19880a838debd7867a216ec382e63e9af8de73ad3ebbaf0d3742cc6720a382ea7cb9dc0aa1d1795a0a0f72f6e4bcd8b415f83405ee3ac113d5938cb569acf48ff37626d387834edd7b2a411b666ab96aca9fb1ddc8e95e1b9db837b28ea1729ddc9f50dc1eee348a23130bf8c08433547c59c2b70f614ad7a38a0964a509065bb9c15851ae79e1d3b324f11890ba70c0d912ed1b5e35ec97d6ff7673d6da743571394c02e423eb8a7f09edac8a4c3ca0a97091d68008df10e944f059cde0a3e233f79b8ad0daab80072f18c94ae73470019098c6cf86312712749cb05600933d37dfc7e9c8b7e2950a2ea52bab40df8246529b0c39ec7becb44fb03ba7928a6b2fb37198d4acc7fa3a162c4a68ea9f8ff14fcd035d41de13a59a7860f87b0c7193b125fdb3f80541742ebb70f9906ecc4ffa2b468d55d1bb2c996dbf33c7a6c0ed7f4ae6cf2a9aa7450d84714ed3567a93bc64f7d5aea0fcf0f043778bcf4bf9259b06856bdd7491a72d154858c6590c95d73a9d0558a512475a946824439cf2ef400bd2647aa7919622e284544b464e5ea4d08e03d3c6334fbc5ea9432750ecbf41e5d69a8c1f08542e7c95657c432bb5d13ef5c497b43d0c7491916b506ffab68f3aa3026b19f93b1043e5b052d5abb343dd9b0790b725dd299f46b3ff06d6645be87865fed66d7408a1b7c0e55081f6a46ca63edc7a73a88475f30affb8479d481f72642a5c61982e6f965e2122dfbce0506f5e2aab3e1745c0fb3fbddea54b334f2dba8e71f6aa2b5e8f4c2d4cad58e23b573305c5e2cd0570a6b28d49e318eb3bfa313fdbf8f503f5740bc3311cba6d238a765d8c23ffc6c5e79d7a58ede2e49b6b44406726ec2e592845b54c81f6ce11e971f2a2f0cdcc656cf04849554749c3fba1bc7569c62f31d273b1c92d707e9311cad083b84379b22ee286f80299a6c5708a2022540b2025a183bc4c894de71208a73887687fa3009e725bb9bf05f43eef71f6d734bf09c282340ad928c7d5b5f101ad0c2c793146e9ce590d6107702063dd97dd41060406175edd4bd9ae0a2c2812726d0adb4538b2dda7d0610b02a67a089cc5bace276c19c2ed12d174a9d230a822941a2b0013e15b77525ce713b265706a27e16f2fc96578b7ec1983cb00e81d92ee19ef9713b6bd0271e245316cb683bc24d2ae0a1e55fd1d112ed44d4516c3a9b57517f75ac98154131ebb544af5592398ec5629394e073a9ba886a3ae84b9196979fe2fe093a025b6cac3530c58e4b04bf4e10a4666ae1d748f197b0daf6b367cab12885888990ad4dbe0ed48ea7bff243aec0ef869160cce3c9e889be0ded99f9da8b839d231417a4ee783c7ad0f93ed974f0caf7b30f5cadb0dad9f054e1aa03c656d2fb6a36aaf7a0da7b9e5b3f4a5e3a62921a1d7c3630a67c29bb80db439c963e097f4f3ef1ae1c027ee64608ca6b9e32622181e111eabd9dea68f659217a71e88bd1c8824ae51c58af20c6bc257cebf001ef6d744f9baf5117162c76dcc82eda8c87cc5f7dd7a557b01efe86b37fdc12518c0002e1b1db2ad66d51d561373612efb497cd9f79466683733c336e539cb29047f72eeced8a323fd3b989822c9609f41b906faee18713e6ee2337f296f99252e68c55e44e8d9a0052834055e0fdb29d2f7094c21894e4e7521a284a17a8e1d81437369646908fb00e0c96de760302934068bc5f5ccc911c276f9a42501c8065d31643ae26287248f206207d1c59032685979b2c981e597d33b044be8781c2d9ca34345c52a18871b0ad8692711a2996701d854fe595d73c9938047a77f3c21675a797a38d20460db2597029f1b8eb063fa74db3dc055b32d2d98687a7f5e163046ddefd4de8321460d648daf417961d725757694765e9c523172c83fcbd92f66255b9fc67e3d1afd5026388eb9be53c490724285a7acbe8f0d5b8e64586f01cf5fc9c296203597940e3550e6aa12d6ebb60a52b3cca5a5898b0717390b7f15073a8a06305b10640618fe3e1f8459e78f1b9ce75081db94431fcad8694e4718103653d6c8b4900db74464454ed99564fe6e9d1f8199d377711322b51fc8cf23975c39d2e48497bc537899456154ffece7bc2ac7aaf6b71538e13c989f1fcd0012aac1181a2828a9524f377a08f0b550c4597dcb9fffeb2c108e76b29371c80b3793c8aebc3393bffaa9e6a5a91c080c23419afbaf2973ae8f962ad5ce22d423c9b40da51244514d70ddcc0b7fe75220b395900384512bf2498701fd519fbc02ea9203d3287b166f53e76691fc750c03354cc1aa12b39942638245606241fc13bc33245ddf5225219b011f988c6bb62389ac2580bafcec152955aa5476369912117600d1c106cafe5b18059fc160dfde5887c531b65d987e6806662cf31b060f43f0a7e74069858ab93bd747cb188b00a1999438c4476776d1a71e4e3ec33b293e88bfda664258db385abf028dc3126567b8e6d7d21f8a880a9620ebc9e824275e8f08282c9978ce6e13e9100890cf8c53cb481d440b38aaa733a42cfa9de018e320ec6406f6c1e3803c1d17eb0b2e76574a3e8dbeffff1d4c832fcc5f5c33218329574d558e7242392a585e38dd552acf6497e72de2971efc2425071b67330cf9712de52a7987f410861196ed0adcbe23455cf38e5f40d4b52c9da40358ab071888e7b0c3e28724ea53c64f87a61dd4a7df8328e28c5b73a51255602213551fe4cdd758110bd99ac6cd00eb3a9b5cbdeb5f0f4423b8f43432440b6755001003740951e77852e1ec468b7cf571e730b88526c9ef0bfbf0153a61eb8314165c15356eb9b9b0d7529ccfc0100a1d04a592b2c9fdf1f3d2b1d8fdb7054e921f301233a66b52133a42399ad5ce7fe5a8b890ed355d6fc1e8edcfacdb2c18e009ab0406151c20bf42f82b1d54f97deeee62546657481509e6107786a3ce2da74a8111450b94b5d7c745061ef01f67f99b9c96674f17f3b64fc0b835023982d03d311a11d53e0a72b54908034423fb6ec6c2ed0f5f5760835500a798c06a6940f50f27c5cf3ab70b8689aa86a8b1f645d2dce02cbfda013aba3afbbb9b7e1d414f390dc745ea3e22e68509852bfc2846c1934a3403bfc259fcedd1c5149bc3ad56b02ce3dd6124c57b4710cf44d4b23d8897809c6fbe8405162c058b6676dfb857b410a7cf60fc2495f1f10b2800abb2a8c79d3d4883de1d02526e98e9323dd88ebb230deddcd64882e717f4d09c5760747c0070ecd5c717b7f66ad0a06c528375d001deefdf357142a95506a15de98a8d9126ddef3cd1dd266f8f3b38ef673090c517b5707de5e581711f15e1a53a511b099d3da0b7a46911937b556a2ce6dfd5b61f48db4ed8db388f708d693009329478498f08036afd3e4ba5942f2a7e5f51ea0f31edfaa3d988da9544002184d463e2389e0575de1f0715e2ffe85855a69da21eab4ee0b2f8f6db05c62507c92dadad49d407e96227ee5f01f332e217c9f6be123b4f730069a68962cb29b3ce2592c5082b3b74d1e3cd8ba8ea9e1337e6f8ba17dd5d74aac622966cccdf068fbf6d56eca771af04137a97929102b317d8ab8309f07d759e08e198b036174ea24870db6a13e21ba945b2f0b50f89049ce6725e200bc2f9e8eb699698fddf3aa0b9238b615ab6b77c4519179faf828a236f67ec275d8466b87e8a0e594d6efba91052cb5d3525772648581bca12de50531d26901760bd614104e07e6b0a45220b21c3fc854a2586c4fb75b402d42dcc7a08a9a51f502227daea5b4328de5dad1c1aac628fb70bee83daf8c63fda2b35fdde5907207bc7864b1d2f45f204ffb00faac527e5012b89b11f52dc081afca53e9f08192a6c34919406abb76e6596b431a95271488bf390804a82fe9e57d3aa0e10ecdc04cb3e7c991ea9bdf6f7cb405d49b2c12e18bcec869df8ee1d58eeeeb357155c2d14181b98fc068cfa186ce8cc4536c71cd2be00f057eb086989bc32ea246a0e0b5e5af095005a1825533b7ac950d0cda041fb00c5616f1e3d79506dd9a1b01b17dd00278b4f21bf46cef84152d5878ba8ef0a66b83948567bc534cf34d0fcbd29d923eef2dbb99fd2a1c7f55647af268829c85505c6b69f6c3a6665ca5401c80532d1ed0796d47ffbf240db9d00ed462403799ef8e2a3751944c9ed93c05d5004e47d2f0eacba95627b4c7ed48b715b00d85f040cdd03a2669ad9db56eb3f290226c35b9aedcaf5a7ecbfa7da4fd43238d73f10e45164e5b2f24edfd56a1a6e21cc4e810a5e06c20b1e15f0f2d4e23f716d97077504d849b505de7885066f1f00413198266b5acede42ed3d9ae9348005b0e9bd433f5b9b361c3c84ad8cce1c2a437eabaf3572a469c4182eedaf4db74e2ff28e12c3bc535f0f075eb4b5493d0cb5c77dc3e419163be17674978c4ef367b967edb640dd4561ffba0eb4cafd10026b691592be18e5b8c0da378fbd5b5e35ede59b066f8800ce6beec29b6c02e610e507211a418a93c23d0e0c6d2c69c31e6222d5de8424c9c7dc94b4c35bec5e00959d8b099af2337f125d0787e512e696e763d1db7b8523c25418c1ad290539182099a8486dd939c507ad25118a4347bb571a0f25eebdf41f892ab8cb8a12590c61571d44ccb6c08bce5e1bf10e04b4ca4348d8db617fc0b9da03edb12597f7036baa40b39d67b7c375afe358e47cd0128eeb04f5f76b62fa7037bdddbdaa752cfee52a7b50fae3fe01a5630cb5ea3122b7278a8aeeff4f4006b8726a691f512fad5a1f3a6f551a465e1f6f181247641798cdccb1b7e9b565f14057eb4c1c91030e37cb712f162ce69e21f64c8ccb97c931c43da0299766fe8907e929d3899f06fd7458e8abf47dc00cb5117acec257810c281c56c86c65e009c153d84a7a5a1d4a1570993b43939715d6e9c0022f1d95c924b78b4c8895c95ed1ec2fc556c922b63a859de09a7308644e14be5632d19dc52eceb5e19239650daa36b984c1c10e9d258418e929a134838385373d167a2b2b4766602933df32f571a5baba2d320c0c9333e9a0c806b65de2904dfbe12e8ae58cb76a70b8a56e52b99ee6dbf393261ef37658daa3fb2326c4b677fdbb4bb40f98961476b565e964142b29080c072b1812dcdbd6736c644b67237372d8ef495fe04783fa8991859e638e5162687126f3c740ece45a0b91f58cb12f1f33e7cb3675ebe5bf2427a220aab7ff941d5a01f10d5bbe75b4e17c60f22cab190dc60f2eea623ce4cbfa6e70e8c02993c03c0ab1ba7eea1d18a776824ee53b14aace8b3449de43b682e0db6d2c1577b5254b128dbb6a11b65c6eaafcc1ca744ba9e1dd0f683d0de2416008e518d38a934c5b1fac7124ee955660f312898a2097a77268332159a2329bad2b8da0d2f5ab5b12068eda7e6f65ec67e1465e9b321f604e42aa8497ce1b03a7acc77d636f9afc7517fb31402ebd486a323f7a4aaeb4702bbaf7c34df1d5d62fe7b7ec0ae286e1ec02e034b237106a48bacedd038f6657e87c9d6d1f9508e699764b29113a529af81bb282c274fc66366986b98f72b633c7a35dff97eaac7a22eb2810eb45a4870f10857f77ae324cebe7af6585312c2f07bb72113fbcaa59467bb2ab3d4060c7992e635da05eb5de493ce9f0a91505c6892c95aa55b7b88383823aec99c24d80c921809093c179db569b4261de0f20e3cb1593097648ec2721ae9e783f3edc98050a985fa4eeb9f58e4907b05bae82ec4cd28806c6079d03c241012e88a7b34e7e18b780283824b66200fa56dd4184dc0b3067b95daf7ca4dba8bddb7f34d60e3d1b156944907883dfd4e3fb50789021a21f8e762d3dc5acc2036ca75deefc922e11cd11ae4bf5e5b76dfc39e3cf240a11f7edaf9f692778d98fd8fb44b3e51f072870540e9741261280d6ed9f1ec4a8c5f9a290574df1ae180c339980a28391d22dc7432de5cc09ccf81687c96b08225760e2a404458b684037e613cda0b6a6a710009226c21f32319f684ff9feca6b6a0e4a9f84a443718b7bdc49b80d08f1ce0fb5e775229a7e694e69837bd802edae2398cd31e6e230f483be76b0044e93881ad2d6e1c3281ae4dcd0d43f4dcdeffb8083609876740cae81de3c2034b7c4d40ee256b0e50f921e13b2e6efbadab5f9340fad1f0c2bd6aff8bb3ffbec6555530b5d1f87365de5efcf81bb99614c6d2978b38ff409485a99607dc8849a175c26229eb80987c833260712e409bc0343a9217d2114b23603f790d75e90717d33401c9ca9fc9695014140372a4e5faa744c3607cbf13edaf95ac1ca98e7b0cc071a2a11f067421264cee37de5900dc07d458c8f2a724579c5009fcc784ee6302be32f044777edce221fb8dc25bda4c6c451db26887b3078944746f30bbce4fd54892d51c86267a12e08f077a10b659b7557129ab5cea0be9541cd5071a3e634f0c7", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e4412165f58bbdd51389e19f359b95755ee0c47120177439bc8ed45ec7a6eee7f42d00000000000000000000000000000000ca7dd9181fb990af4173ed8bc797be72000000000000000000000000000000005720482e46b084a93e0c80c18a2ce90d268b63ab57f71e917a79017400ac69404ad136220c9e12bec413a6d3d3007cfc00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002234e2191863d749a921a9ba23ce96fee3445e2989d7b03e6f558149a7c72536812f67fef27455b7cdd53d7dc2b9dca1505a960987ec9c9a7a823b3519e453ed4302aaef600b5d72770aecd785f476b2a348b28d745dc6dda97e5246e924c2b810dea249216df43db9887f19e17547969994f891e2594fee031495261d1eb47f80000000000000000000000000000000000000000000000000000000000000013000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - } -} diff --git a/circuits/benchmarks/results_secure_agg_micro/report.md b/circuits/benchmarks/results_secure_agg_micro/report.md deleted file mode 100644 index 75a394f9ad..0000000000 --- a/circuits/benchmarks/results_secure_agg_micro/report.md +++ /dev/null @@ -1,215 +0,0 @@ -# Interfold ZK Circuit Benchmarks - -**Generated:** 2026-06-04 11:57:41 UTC - -**Git Branch:** `main` -**Git Commit:** `334123af5e1f043fd91d5b4928c461bda17951e4` - -**Committee Size:** `H=3`, `N=3`, `T=1` - -## Run configuration - -Settings for this benchmark run (integration test + Nargo circuit benches on the same host). - -### Integration test (`test_trbfv_actor`) - -| Setting | Value | -| ----------------------------------------------------- | -------------------------------------------- | -| Benchmark mode | `secure` | -| BFV preset (artifacts) | `secure-8192` | -| BFV preset (enum) | `SecureThreshold8192` | -| λ (smudging / error) | 50 | -| Nodes spawned (builder) | 20 | -| Network model | `in_process_bus` | -| Testmode harness | true | -| `proof_aggregation_enabled` | true | -| `BENCHMARK_MULTITHREAD_JOBS` (max concurrent ZK jobs) | 13 | -| Rayon worker threads | 13 | -| CPU cores (host) | 14 | -| `dkg_fold_attestation_verifier` (EIP-712) | `0x7969c5eD335650692Bc04293B07F5BF2e7A673C0` | -| Verbose logging (`run_benchmarks.sh --verbose`) | true | - -### Hardware & software (Nargo / Barretenberg host) - -| | | -| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **CPU** | Apple M4 Pro | -| **CPU cores** | 14 | -| **RAM** | 48.00 GB | -| **OS** | Darwin | -| **Architecture** | arm64 | -| **Nargo** | nargo version = 1.0.0-beta.16 noirc version = 1.0.0-beta.16+2d46fca7203545cbbfb31a0d0328de6c10a8db95 (git version hash: 2d46fca7203545cbbfb31a0d0328de6c10a8db95, is dirty: false) | -| **Barretenberg** | 3.0.0-nightly.20260102 | - ---- - -## Audit status - -On-chain verify gas: **complete** (CRISP Π_user + Interfold Π_DKG / Π_dec replay). - ---- - -## Measurement methodology - -| Metric kind | Source | Meaning | Do **not** use for | -| -------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------- | -| **wall_clock** | `test_trbfv_actor` phase timers / HLC event span | End-to-end wait in the in-process test harness | Production WAN latency; per-node deployment cost | -| **isolated_nargo** | `benchmark_circuit.sh` per circuit | Single `bb prove` on oracle witness, one circuit at a time | Full protocol pipeline (different witness path) | -| **tracked_job_wall** | `MultithreadReport` per `ComputeRequest` | Wall time of each job on the shared Rayon pool (≤ `BENCHMARK_MULTITHREAD_JOBS` concurrent) | End-to-end time — **sums exceed wall clock** when jobs overlap | - -**Harness limits (integration):** all ciphernodes share one process and bus -(`network_model: in_process_bus`); sortition registers extra nodes; `testmode_*` enabled. Compare -runs only with the same `benchmark_mode`, proof-aggregation flag, `BENCHMARK_MULTITHREAD_JOBS`, -commit, and hardware. - ---- - -## Protocol Summary - -### Circuit Benchmarks (isolated Nargo + Barretenberg) - -Single-circuit `bb prove` on the benchmark oracle witness (not the integration actor pipeline). - -| Circuit | Constraints | Prove (s) | Verify (ms) | Proof (KB) | -| -------------------- | ----------- | --------- | ----------- | ---------- | -| C0 | 287764 | 1.42 | 27.90 | 15.88 | -| C1 | 2430707 | 9.34 | 25.96 | 15.88 | -| C2a | 1446348 | 5.13 | 24.04 | 15.88 | -| C2b | 2889001 | 9.68 | 24.89 | 15.88 | -| C3a | 3475239 | 10.75 | 24.47 | 15.88 | -| C3b | 3475239 | 10.75 | 24.47 | 15.88 | -| C4a | 1961956 | 5.82 | 24.93 | 15.88 | -| C4b | 1961956 | 5.82 | 24.93 | 15.88 | -| C5 | 3719555 | 10.89 | 25.11 | 15.88 | -| user_data_encryption | 1688676 | 5.75 | 25.31 | 15.88 | -| C6 | 2977263 | 10.22 | 25.89 | 15.88 | -| C7 | 109424 | 0.51 | 26.84 | 15.88 | - -### Artifacts - -| Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | -| -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 10.69 KB | 0.47 KB | 3125343 | 176196 | 3301539 | -| Π_user | 15.88 KB | 0.12 KB | 2972977 | 193336 | 3166313 | -| Π_dec | 10.69 KB | 3.47 KB | 3640899 | 187176 | 3828075 | - -### Role / Phase / Activity - -| Role | Phase | Activity | Metric | Duration | Proof size | Bandwidth | -| --------------- | ----- | ----------------------------------------- | -------------- | -------- | ---------- | --------- | -| Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 603.10 s | 127.00 KB | 128.56 KB | -| Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 163.13 s | 10.69 KB | 11.16 KB | -| User | P3 | per user input | isolated_nargo | 11.01 s | 15.88 KB | 16.00 KB | -| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 10.22 s | 15.88 KB | 16.00 KB | -| Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 176.86 s | 10.69 KB | 14.16 KB | -| Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 50.75 s | 10.69 KB | 14.16 KB | - -_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **44.37 s** — not -comparable to P2 wall_clock row above._ - -## Integration test (`test_trbfv_actor`) - -### End-to-end phase timings (integration test) - -| Phase | Metric | Duration (s) | -| ------------------------------------------------------------------ | ------------ | ------------ | -| Starting trbfv actor test | `wall_clock` | 0.00 | -| Setup completed | `wall_clock` | 2.98 | -| Committee Setup Completed | `wall_clock` | 20.09 | -| Committee Finalization Complete | `wall_clock` | 0.00 | -| Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall) | `wall_clock` | 163.13 | -| ThresholdShares -> PublicKeyAggregated | `wall_clock` | 603.10 | -| E3Request -> PublicKeyAggregated | `wall_clock` | 603.69 | -| Application CT Gen | `wall_clock` | 0.30 | -| Running FHE Application | `wall_clock` | 0.00 | -| Aggregator P4: Aggregation pending -> PlaintextAggregated (wall) | `wall_clock` | 50.75 | -| Ciphertext published -> PlaintextAggregated | `wall_clock` | 176.86 | -| Entire Test | `wall_clock` | 803.94 | - -### Multithread job timings (`tracked_job_wall`) - -| Name | Avg (s) | Runs | Total (s) | -| ----------------------------- | ------- | ---- | --------- | -| CalculateDecryptionKey | 0.06 | 3 | 0.18 | -| CalculateDecryptionShare | 0.17 | 3 | 0.50 | -| CalculateThresholdDecryption | 0.24 | 1 | 0.24 | -| GenEsiSss | 0.06 | 3 | 0.19 | -| GenPkShareAndSkSss | 0.11 | 3 | 0.33 | -| NodeDkgFold/c2ab_fold | 7.29 | 3 | 21.86 | -| NodeDkgFold/c3a_fold | 59.07 | 3 | 177.22 | -| NodeDkgFold/c3ab_fold | 7.22 | 3 | 21.66 | -| NodeDkgFold/c3b_fold | 49.74 | 3 | 149.22 | -| NodeDkgFold/c4ab_fold | 8.28 | 3 | 24.83 | -| NodeDkgFold/node_fold | 14.94 | 3 | 44.83 | -| ZkDecryptedSharesAggregation | 2.79 | 1 | 2.79 | -| ZkDecryptionAggregation | 47.82 | 1 | 47.82 | -| ZkDkgAggregation | 19.61 | 1 | 19.61 | -| ZkDkgShareDecryption | 23.11 | 6 | 138.65 | -| ZkNodeDkgFold | 146.54 | 3 | 439.63 | -| ZkPkAggregation | 24.75 | 1 | 24.75 | -| ZkPkBfv | 3.83 | 3 | 11.48 | -| ZkPkGeneration | 54.05 | 3 | 162.15 | -| ZkShareComputation | 51.34 | 6 | 308.06 | -| ZkShareEncryption | 119.19 | 36 | 4290.86 | -| ZkThresholdShareDecryption | 91.27 | 3 | 273.80 | -| ZkVerifyShareDecryptionProofs | 0.12 | 3 | 0.36 | -| ZkVerifyShareProofs | 0.32 | 5 | 1.59 | - -Sum of tracked job wall time: **6162.60 s** — **not** end-to-end latency (jobs run in parallel up to -`BENCHMARK_MULTITHREAD_JOBS`). - -### NodeDkgFold sub-steps (`tracked_job_wall`, per fold prove) - -| Step | Avg (s) | Runs | Total (s) | -| --------- | ------- | ---- | --------- | -| c2ab_fold | 7.29 | 3 | 21.86 | -| c3a_fold | 59.07 | 3 | 177.22 | -| c3ab_fold | 7.22 | 3 | 21.66 | -| c3b_fold | 49.74 | 3 | 149.22 | -| c4ab_fold | 8.28 | 3 | 24.83 | -| node_fold | 14.94 | 3 | 44.83 | - -### Aggregation jobs (`tracked_job_wall`) - -| Operation | Avg (s) | Runs | Total (s) | -| ---------------------------- | ------- | ---- | --------- | -| ZkDecryptedSharesAggregation | 2.79 | 1 | 2.79 | -| ZkDecryptionAggregation | 47.82 | 1 | 47.82 | -| ZkDkgAggregation | 19.61 | 1 | 19.61 | -| ZkNodeDkgFold | 146.54 | 3 | 439.63 | -| ZkPkAggregation | 24.75 | 1 | 24.75 | - -Sum of aggregation job tracked time: **534.60 s** (parallel CPU work; not P1/P2 wall clock). - -### Folded on-chain artifacts (exported for Π_DKG / Π_dec gas) - -| Artifact | Proof (bytes) | Public inputs (bytes) | -| --------------------- | ------------- | --------------------- | -| dkg_aggregator | 10944 | 480 | -| decryption_aggregator | 10944 | 3552 | - -## Raw circuit benchmark JSON (Nargo) - -Source files for the **Circuit Benchmarks** table. Persist this directory with -`crisp_verify_gas.json` (and optional `integration_summary.json`) to regenerate the report without -re-running the integration test. - -| File | -| ----------------------------------------------------- | -| `config_default.json` | -| `dkg_e_sm_share_computation_default.json` | -| `dkg_pk_default.json` | -| `dkg_share_decryption_default.json` | -| `dkg_share_encryption_default.json` | -| `dkg_sk_share_computation_default.json` | -| `threshold_decrypted_shares_aggregation_default.json` | -| `threshold_pk_aggregation_default.json` | -| `threshold_pk_generation_default.json` | -| `threshold_share_decryption_default.json` | -| `threshold_user_data_encryption_ct0_default.json` | -| `threshold_user_data_encryption_ct1_default.json` | - -## Notes - -- All nodes are executed on the same machine in this benchmark run, so inter-node network latency is - effectively 0. diff --git a/circuits/benchmarks/results_secure_agg_small/benchmark_run_meta.json b/circuits/benchmarks/results_secure_agg_small/benchmark_run_meta.json deleted file mode 100644 index 3c2d06ae3a..0000000000 --- a/circuits/benchmarks/results_secure_agg_small/benchmark_run_meta.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "benchmark_mode": "secure", - "bfv_preset_subdir": "secure-8192", - "committee": "small", - "proof_aggregation": true, - "multithread_jobs": 13, - "verbose": true, - "nodes_spawned": 20, - "committee_size_n": 5, - "committee_size_h": 5, - "committee_threshold_t": 2, - "network_model": "in_process_bus", - "testmode_harness": true -} diff --git a/circuits/benchmarks/results_secure_agg_small/integration_summary.json b/circuits/benchmarks/results_secure_agg_small/integration_summary.json deleted file mode 100644 index 8053fffe52..0000000000 --- a/circuits/benchmarks/results_secure_agg_small/integration_summary.json +++ /dev/null @@ -1,244 +0,0 @@ -{ - "integration_test": "test_trbfv_actor", - "benchmark_config": { - "mode": "secure", - "bfv_preset_subdir": "secure-8192", - "bfv_preset": "SecureThreshold8192", - "lambda": 60, - "proof_aggregation_enabled": true, - "multithread_concurrent_jobs": 13, - "committee_h": 5, - "committee_n": 5, - "committee_t": 2, - "nodes_spawned": 20, - "network_model": "in_process_bus", - "testmode_harness": true - }, - "proof_aggregation_enabled": true, - "dkg_fold_attestation_verifier": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", - "multithread": { - "rayon_threads": 13, - "max_simultaneous_rayon_tasks": 13, - "cores_available": 14 - }, - "operation_timings": [ - { - "name": "CalculateDecryptionKey", - "avg_seconds": 0.059818383, - "runs": 5, - "total_seconds": 0.299091917 - }, - { - "name": "CalculateDecryptionShare", - "avg_seconds": 0.159473866, - "runs": 5, - "total_seconds": 0.797369333 - }, - { - "name": "CalculateThresholdDecryption", - "avg_seconds": 0.231732417, - "runs": 1, - "total_seconds": 0.231732417 - }, - { - "name": "GenEsiSss", - "avg_seconds": 0.156219799, - "runs": 5, - "total_seconds": 0.781098998 - }, - { - "name": "GenPkShareAndSkSss", - "avg_seconds": 0.313501966, - "runs": 5, - "total_seconds": 1.567509833 - }, - { - "name": "NodeDkgFold/c2ab_fold", - "avg_seconds": 11.847578675, - "runs": 5, - "total_seconds": 59.237893376 - }, - { - "name": "NodeDkgFold/c3a_fold", - "avg_seconds": 169.184619283, - "runs": 5, - "total_seconds": 845.923096416 - }, - { - "name": "NodeDkgFold/c3ab_fold", - "avg_seconds": 12.817567216, - "runs": 5, - "total_seconds": 64.087836083 - }, - { - "name": "NodeDkgFold/c3b_fold", - "avg_seconds": 151.491728074, - "runs": 5, - "total_seconds": 757.458640373 - }, - { - "name": "NodeDkgFold/c4ab_fold", - "avg_seconds": 10.847684408, - "runs": 5, - "total_seconds": 54.238422042 - }, - { - "name": "NodeDkgFold/node_fold", - "avg_seconds": 23.955023783, - "runs": 5, - "total_seconds": 119.775118917 - }, - { - "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 3.327329542, - "runs": 1, - "total_seconds": 3.327329542 - }, - { - "name": "ZkDecryptionAggregation", - "avg_seconds": 60.962331541, - "runs": 1, - "total_seconds": 60.962331541 - }, - { - "name": "ZkDkgAggregation", - "avg_seconds": 28.085369792, - "runs": 1, - "total_seconds": 28.085369792 - }, - { - "name": "ZkDkgShareDecryption", - "avg_seconds": 61.25314797, - "runs": 10, - "total_seconds": 612.531479708 - }, - { - "name": "ZkNodeDkgFold", - "avg_seconds": 380.154250233, - "runs": 5, - "total_seconds": 1900.771251167 - }, - { - "name": "ZkPkAggregation", - "avg_seconds": 49.192114, - "runs": 1, - "total_seconds": 49.192114 - }, - { - "name": "ZkPkBfv", - "avg_seconds": 5.465944433, - "runs": 5, - "total_seconds": 27.329722166 - }, - { - "name": "ZkPkGeneration", - "avg_seconds": 67.431268691, - "runs": 5, - "total_seconds": 337.156343458 - }, - { - "name": "ZkShareComputation", - "avg_seconds": 50.505588075, - "runs": 10, - "total_seconds": 505.05588075 - }, - { - "name": "ZkShareEncryption", - "avg_seconds": 112.371622883, - "runs": 120, - "total_seconds": 13484.594745965 - }, - { - "name": "ZkThresholdShareDecryption", - "avg_seconds": 133.8429849, - "runs": 5, - "total_seconds": 669.2149245 - }, - { - "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.290990533, - "runs": 5, - "total_seconds": 1.454952667 - }, - { - "name": "ZkVerifyShareProofs", - "avg_seconds": 1.076807577, - "runs": 7, - "total_seconds": 7.537653043 - } - ], - "operation_timings_total_seconds": 19591.611908004, - "operation_timings_metric": "tracked_job_wall", - "phase_timings": [ - { - "label": "Starting trbfv actor test", - "seconds": 0e-9, - "metric": "wall_clock" - }, - { - "label": "Setup completed", - "seconds": 2.8164285, - "metric": "wall_clock" - }, - { - "label": "Committee Setup Completed", - "seconds": 20.127044, - "metric": "wall_clock" - }, - { - "label": "Committee Finalization Complete", - "seconds": 0.001253209, - "metric": "wall_clock" - }, - { - "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", - "seconds": 400.034844, - "metric": "wall_clock" - }, - { - "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 1669.469802042, - "metric": "wall_clock" - }, - { - "label": "E3Request -> PublicKeyAggregated", - "seconds": 1669.997398541, - "metric": "wall_clock" - }, - { - "label": "Application CT Gen", - "seconds": 0.304409375, - "metric": "wall_clock" - }, - { - "label": "Running FHE Application", - "seconds": 0.000809917, - "metric": "wall_clock" - }, - { - "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", - "seconds": 64.513878, - "metric": "wall_clock" - }, - { - "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 205.700746333, - "metric": "wall_clock" - }, - { - "label": "Entire Test", - "seconds": 1898.949991209, - "metric": "wall_clock" - } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000f8cf4eb44ea7bb973000000000000000000000000000000000000000000000002fe6a2e9f65b54987000000000000000000000000000000000000000000000004cb9cdf5f722d0d31000000000000000000000000000000000000000000000000000120dd7263dd0d00000000000000000000000000000000000000000000000a7c765c503af0ca8800000000000000000000000000000000000000000000000c438e0a1837d19462000000000000000000000000000000000000000000000000fe6a7967e27240350000000000000000000000000000000000000000000000000002ed1b06d5e46a00000000000000000000000000000000000000000000000073ae7e558975da6900000000000000000000000000000000000000000000000b40a9060c3659b72f00000000000000000000000000000000000000000000000271a38c15d43fed70000000000000000000000000000000000000000000000000000159384b1efd6c000000000000000000000000000000000000000000000004b79f2d04172cfb6b00000000000000000000000000000000000000000000000831e8bbaa05d116b400000000000000000000000000000000000000000000000942afbf6aa7e7798b0000000000000000000000000000000000000000000000000002332f302695aa1682b09c868798d9b80459f108450027d8b7e34032a713a8f0f5a51c1d3c161421ad456a75c6872dbc5ed9cc8562afc387b6d64df0218debb3d3bfd3c8a18b4d2dfe9df17302f9e5b42959bd451047d6e6d232e589db3b06a3184826472d70dc108bf072502ec19531b39949e5aa2cff83796033315791f2e3e9cec19d880aec1bf9af6dc3795931b567b91cd9187ffc1d59e2bbb0ea5963ea0192cc43bf3e710f41e632e8ea500c510870f4482962d448611d964d7c1017bd3893923b7fd4c2208ee98b855d511584781bdfd7bc7010a5c7c56a5a5b256bd052a906d81c48db175bb09885e5437fa9774c36e0ad5cfae43ffc9b0afcca6c68b0fbf991d9e9e30f14e2b1bac451200f190ea0417688be9e9e7326ba36aaf30cbf403cf7a3e36a033cd178d883aa7126af75245f5edb60b8077181b8f97a60d8f0268724c8a97e2a13d2f0b35513703c939ed6c63be19a4ad007a30b0f3ba65067f50664b364f018eb0329e6f2875a150d0142d7136d839186a4761906c494891a0b54021524ee2b8f9409866c5e0896f2f7b98c44aec8acf5299f4605d308e657301f0987109b114f608775844e74b54aad341abdd8b483a965b840eb8a09586605c74c9fcedc2fc3a34aee8e9b3ccf6d489dcfe2df004342a8111589cf1dcd23d828a059de700fde0b1012a7dc4912df91bcb908258a79c863141c092e2e7c871cdbe4ce658b178e3ed9471fbeda851d23cc5516b0c925106d1a96b03469c336af0d1c93dfb81d9f3c8e0e6b7d9877c0291f7063f4aff4767be58df30d0ba7cf6cd887fe62da0c35d308e50ef2b97b0c038fac5c962ca7b93eca3855eddbb47ab4da6f42e3ff0fefa9e4c64879b112311f2504b5800e961a4cd4d519f5dbfc751048376ed4d625b05663d489824178cf171a8001b084fd7a8cdc96870f1914ae35ec0525600b22086c87843a73afd95f57f3197e11e4f8b4aa1279ccedc39b19ce30e195bfa121e8cbb1c2253a644f3c40a08b34fb36aab9a6a4487371e0445647e2383d43d51df61b7fed26ac4468d0b09a5af2c0dd96be4d8b88eece6a840e69626ecc8c3e1f535e681d68f7e4db85d0b7836721660c53bb50ae39285454e13dce7eda5bca09e63b630ee447041e02022b66b6bb043923f20ff0160aae10e63910ab8497ae1f856fec2a2e5cd9f5a4665c808372dafe8a7b29cc542904228984c059241b5126a9d31a4b9bdc246d0d1d57e2bd2ba5a5aa64c6bdd85d15838f29188e4ff6fa0c677776b475721085d4811d1dfc89939ccc310d3737579be2c80beca9b7c8e427f1d24d1ec75fef9748e80e26731b09b708336826b274d44a87a77da07367d318cdd42c458484a1da9aa86f3a86d5712b5f760a75fe29d66a9e4a71c44552bf2b7c4b784097c3c4880a04b51b438b29f715f596b5e69843983d28ba16ef9e9a2ec90fa646ad8eef1b55a2a114199e3e5e33bd941c4196b654fe8b827654ce5321a89d763c7754c6234e4015d0a55d2ab9641614edb8395cd7ba18eb1b7dfe0f149ca7a39e880fc1a864daaf822f8b237f112d8d59eb993fb49ce75f12fc9a8f2b7b797b534986b38964f3c77bda62cc2f8cf329b457806fa5b0d9cf7aaabb2419161b4b8d4a35d3c609e1d0d45cb1cfa796ab1273ea177c577888b2f5ace9c204a5d4ae630b6885ad89cb3844d7860ff0423cf6cf8329c0d7f27c990db3fb882457b618471527753abd61adedfc6a068edc792c7c5d26661b5002e9bce6bef91fc14bd2149e1d8487dc51620c7ea7243f2ab729592f4ca595dc60c74426717a0226cb14a5021437c020ad312030fa0cf34beddd06e7ea342563dfe2ce64a1b11f28a59a53732754b62297ca0c280949393e00ebc1d9b5113490ab370062a3510b7c296fbe4d2e509ea850fdb0c50631ffce02b6362e00b8d363b3086dcc34c31dc69a97c93d2f5cd3cb491511d9eb8aa6d09edceed0a4aefd633a3cc4fb8cb602222d2d962062a89951027c580823f7a54236adf185a1a51a7716c6851378da0cdbeb52e7e08aed22fdc09e687e79d5101542088ce0d38a7e1d4c898a6b573c0ce3bf102237bd0d6178fdb1b7804915fae9aecdb5cd66a7b03e047de559412b2d38e49e554cb25993f7422f0876f7128c1e78244511656cf77af02a0cd9e91401aadfa2577e9c460e4f37818c5fd08fadf6d8feb5d186e0ce7fffe1e96282fc16f929180250cf6e04e756103cb11a94f527d10e7152d9dd898de648dae773f815f071be728e768086b8d8ddc3a24c6310b636db66b6edfaa79454f456f8a63b22efaa28d6079b861677ae63c9be81aa53f08cf522823dcf77b358a3e73be85417ab64897fff7840a3c81a375d17138b77597f8ba1de3fb81c04afdbdeb6c70e1d75741eab64ed54ac88a00c82fbb83509f53f211ec80a53c7f9f929d237546a1f04592e0ce043b501037fb064072351c7110a5354f882a5f3438beba7c818a80526b7287378b7b02bac41b8dbaa535a7ad36576a5dba5ddc8eb9a57952ce9a91181f80a457904433cb2f4735c2d56348fa8962fce5e030a021777799fc7a9db0600ab5818ca2a4157abd6cb9aafdfe1a76cf6f853e578885caed79b04d9dbd002b5e7aaef46d81adbf2c3f726371ff2dbbf2908656b28b54027174e0314fed21fdb1f9f4f5b721065972e8a15e21685a1e1a089bccdf1578ae75a26a4cd76d921d5f42ec9cacbd4d07535cac21e2fbd05433f9f3a18be2b6b01cfe96e8ec15b269bffdba83eabed4bea3eca373a402b2bea20065a6ce1880ff57d22a591249a1b488a56f4db4ac84a88cf6706981492618fe93c037c4d40b24ee5b151486fe821ab713f4e3eaa8a3f1af81d701bb75771d5434485562b8d38f236fe57ff1f9c104145d00e69eb57f7eab51e67bba351430240d039cb677bde6e6f2fa77642bc200319a8dea6735aca2d7a682003fe8150812aed472eeba46bcd8c0cae9aa9590da57b0d4271354277a1357bb93b8ed003a90cac91366a1561fb20fdc507a0360ae18d51eef2886db725890bad5ce1985018fdf064b72c6851119dca94aae60b1f28dee8ab6508311be986a3acfdd81422838e4a1b54ae7d85f73f2fdd7260471a8b6e012ecff6d3c51b52cabda738a0df4618b30b719d5f4fe8b258fea7589e28894748a5d6e0f6861e90b8e67866b055089c3e4b6602e1541537bc39c4509c18796a05a33ac38821d3fdf11540e1d3658384f45ce5438b05d720312b2f09ed2a4b9a8afa0b289cbb84df58c22e065fbafb6d7a1c00fa00dfded916976769f60f62d9e67524aa0388e220f1e93b5735c6a90aad57cb9ba38e7d88cc74137d38180c1a2df6459e7f0aa21562a68b91b825fcaedfe7463d19393094fc6c4ba6ec001bbf4b689dd55c592f244bc5bc7490360441f412a73b256def2164051539d6043e38cbff1cedc18a9aa2f8896b688671e9a7535fb5fa9ca2573b4d079c1e100e2b3f9b73e2a30ce84422bb96eaad707c461078f7531db516bcd027702c82a21c53fe5709550a1037abdbb0715eecfd8a71f64aa5dec3e7df1fd7074496534e0d077a0e6f2179e6c159600c0611dca8719901ba76150553546ca625122c96491ffc6e0372529455bb468f79b309f613b589576015b8e5df9091047ab3e9bbe407883a58019032c61a315109f7cfc60ca1b162a2129d2510a461140c48777ed42aac70d7a5d1564e92f8376c6631c68b53b942555db5af89d0683196029500ae21b9114997061d126a66d39f04a3207b7f979a5ed9a0e07153cb3b6999cfe39d18fb0bc43b87736d69768d162662dc5676ec6303c3bae7046bfb53aaa856353500d26528623ba1e2b96ca0b794636b19ae843b80674371543aa5bb5c9c12d9d624816dee09cea3ac10a450349dd556cfe0b6cb3aeb6bf3d0cfaf8e773346c83b1318851056d6d268c9ae3a7f81c15634283ae2c80a279a0654c66748caf0a3ff1fe384a9610f357139f9f10f50af50ae44566a66eeb779206d02b482775fca152db2bf3ff68b269bdce69272b7eaf203d957201e3f7a77cc6d082dd3155692292fbceb5ee42ecd18040607684cdc2a30df4647c3769b11f248118a9b16926c0225f2d136c04fa8aec89ace848062d98b2733ead5abbb86e47415eb7b706cbe172e445488a5921dcf7b00fde2957ca5985858ac613a3658c09b7f8c49855cbbf407972d2d7d02e77ba1af60ed4ca833ca94ae90d16b62ef16a0e16e92b515824a033dfda8cb5b472cb415b21fdaf0a68cb55fc957c4b170a588648f5af8947aa81640f89ebd26e164a95aa84b3ca629f5c852b7bf41032b3f50b100ba452945dd02d098f8cae107d21ac95c720cdfa34f68902deb208ddb631bf8ec45a4cdcb0d02c9b9f04629fa45e016f42ea1b7c114212f08eef69ee4cd9dae331f8bb370aa1b3845c84eed88741ec11ad341a8bd2749d2b13195d7c02d890938e10fec14e51564fd177aeded08e90323e208ad1267d50ad5ae493f416c43196dda4eea87b828a28e93703ac0ae95c8a8014ea151ec1471104b75b21351578dc7aad638c5412bd99a63b19b1da253bc2107f7e8b4eb7fc5dfc61457e8ac7381693c3e59567d2509211887cc1ba0b9f0497052e8a8662d2f3187dd2a0f96485c606e7dcbb2e413392610ad2440026831551fff760ece740c26af9f2c69c288a7a75f7107b56b2709867b0e96087e99336e0c3cbb27b6ee59112d541000bda27643a992bdb64722831b803aa8bfaa372e88fa3a6a1d2280dc5621acad38514a9d6fbbd1f7cc500fd872c5ec9d6ccd74b0f77013442f08abcc4cd3299e1cbf6d19a154e205b1620e34b6fe0705e7774fa60307d218defe6b489e8631e7e45b84fceeb0df0fcfae048bc55c1922199a0ea0365b83702fbdbbaaf68b8c5319c9265f630bc5353ffa162d02a4f75a0f05b25e65788dc00c393b8441b323fc74a8ab4c9d125f90457e180a59c842f6e1c2ac90698b07733b954724098342f1ca8bb6715576aea2defe08e98494d0bd897d1f630d5d0e3872ccec2f6357f8901017b352ee3637c4960306e502e2b781b17372b08827b3639076643aa83a754dfbdebe64e9e58d4203480e0877c3456b04f057d52d1caebf21a1c6efb523d9ce78aff2ec71c675504cee2cd77d623a9f09a241d38a26a3ca59a521a8dc22ee304838f97ae352a721fcf607b960758d2970f849b37aaba64d2795405570433bdc3693fdc791b4d25b837d2f3e1a611cdaebe5071905b723a70a9469edc5225484a806d2d1ad86df2758481b3d5a6ba262f76e0c42663ce543ea49eace1c0ae9a6a45f247a7c267ce2d2be2002337e07aef72d17a1594e7e27171f08017cc067e59dca287a61c7b37687f711ad81bdf74060145d2a4bbae427d2d2fbf8a4926338bf97afa40b87de4b1b3f04352c6e671783e41daf549156c01e95ec1c9e294770e34776f89f516ab590821dd34ece728c26fe3bf8d5758e3181a882b5847a8d49fba164bbae46849ea1ad096561db5c553a6b3b0581693ba295bf258be7fd47f7164f18ddd1626c9f2f012bc3cc3bad66bcc6e88436590edcbd82cd17d02800e664f5bb417935d5c96b561b2ffb65c5a9f8ec9d0e1792a95ac9220200d51be059d46f3b035b875f7ccc65178ff7b1198d38bbebccca0f7f7e8f3fedc27eda3948b47d8b54aee682f3b64617c9a4622543660da65201488ac25a30c756d3d7f9f1d0c5c80c4fffde42bef61090f6a01ef9e9ec31a1b724ba21192e1512fff832f6917d93f4cc7ac25bd32f24df54d405c3d87a41ac1a590ee40b1abf1f0de5834bb26256b68e048da10bfd001482c637ab4572e53b0e794a2f6908139ba237b81394bce3bc6afcb9c684ce279cc943938fcb26dde13ad80910537b1b5fe43c6d60bb339d40e5a40215739a0b1005f8bb6b0189d2fbe89a5b00b1b543238e7fbc7fcea521d0e1fae0f9116618b248d8d4e3072bbcdf0b2fe50607ced2b67f89bc5d046f837e3f2715e9775701da2355354f5c6ed2fabc49662208d7b7452a129b9ea299de46e0cd655453671c821652dcd2ab87ef2e61cb72293aa27b5fdf95017a94fdc4bb5e8bf8fc39f725fccfb818685ad276f5df10db5f3873b2f8562c79d977909e1b4743922866d902a652d8a3b57afa097b51b78ea574c466a13647e94d42c129d97ddcccb54b542a3ef4511ed8e06217823e2eae64ae32dadb252f676827e708f65da94bd622ca0cc97c714ca3905980a4f55e35f8559b0b376dc879bdb801b68c0dedc44e53682f78c7af9f48c5a3a4d936af5117eae715b5d9d0690a5946bb5f76dc226bd49b0a116f5d01e49f8f55d351b2e9cb60a96426985e48accb388fa1c884b011341415b2cb90265307152ace284bb397bc3d86146434d6814f68a36b61a05f7c5ed62a4c5c6fab4c22fad3d73d3cbc74f0ef00a513f7d9aca5635505bf82bfc6a70525bb597fc69e219b7d72dfc1f11daf52850c0f6f494373384c2995e74c518f7e0dd35dfe88b3febc99ecdb7a643c09bea95d8bb8e2c9c2483c4d7dd4c22a13f919f02b199b339fb250412c9eae6b785fe36cdf1c2805c5b8c9321720370aba270764a45048933ae256fce1790638eaa43073118e4947185020f11b01f592dd002aaff0025762ee2b02e665d7cabeca7cfec6a9a8c98e27993d878ed4f60dfafa2aada3b27025c0bace89da4d121353c04f1f31b4a5e27e548344ba501a95a19b1deb7337e7ace684065c4b7852ccaf33cd564080094058f840fd10af1fd46f2a1517e323a23c62f5b3fae6b841adb4ac330ba581fa047b3b842a2aa40089900b1404b57576a411243ab4b7d63a0c560bb3a9c37464c27037b5a3a94f288b33db2a1ba702b83cbc4e1c6673ac8e687416019f1fd156dfc5919d4da977becce88b026229566e127838c6ad037cbd46a8c3ae7295dd4a6a9ad307f0ce6bbe8003e915593cba42b17949940cd753cffee93ef1978aaa214eee88a7802d9168dce4772b022c0ad7ee08786899d13d958d311e9aca8ae0591f3dd21be472aab41db23115b775053084283bc5d8d0c88ed1bcba7ae6b389b001b6cdffb3fd4bb99e47702bf294f20eba8b2694d2626dc7e5e0aba944496988a669ef2b26300692a689d40bcb1efb3e396b2d75219e11b852d18a358f117413840e23cd68e99321fccb7a256b902e1acba57023b193f41fe5b16741034303534ff42547c3d6b366cd29ce110263c3d812fa8d4be863b63a245903347d9ae3278e5cd0d8391d9510ec96222e0a50840c6e02c3cd831731ede6929e3d2cd7301d9017be2775cf1307f4f81a14589f24b785cb8c80354f811a38c7d0dfbb175c1223b9394501bcf2701a98692004a787961c23bd5cc83ca31c6979df58eb2bf72bb5245acf6730427ddd9ce2078c798d75734619ee5fc01c550f8bc26c05ba9248fd21885874f8bf3959ebc012ba20af3955912889abfeacbb17efb14013cdd8e79b61152c9145ae7c1cfa2e2c847c046d2ad74eee19910ff6643faa6c5763a0d2ed0022f50b11aebe38d7ec0837446cdb3d84c7dfe4b153b3a2e6ed2e6c42cf57c0e5109581ae882ed7c9c804b85a3fb7468c9db5db232b17acc83bf928d3928ab67d0f71b673232ff50ff8140756140b6eacd949e5771d8482f5b26261b5df04f38b923f60d9ac9b6192d3136047faf725baf1408beb5ec71752f89f7330ee60a842ba372ea58f0f0c238d284fa9818fb94f0323be5dedee392b7d034e1322b90aac38a9eab3e4854d5af82efe40526d62eaf14028de72b50a2d5bdef706474c945f7ce29fc469121a982b0b27424460ba39bead9d9ab64ad650ea35d0d2da6479dc53d36771d5cefa65de00a8aed65fcba7eccc5bae30bff27b7accf17575147286beca6f8db5d32382611dfecae871564aeee4b8b82b3879508245d363c523fd54b460492a1733b998ec0c2c5b3eafb4ab51cc3a6641d7afd108767235107c5326cdd627fbaa7346685221323abeae65f40678a6320eb1db8b2fb6e719f22f42059f049095c46b00cd4602cb6d3f08602a61d96b35a318ab2031fa422edecddf04581779972c1473730417f60bc84942265911d86d6affb9621288585b5bddec0a0be9686bc651cf95eb17400cb285c35bd85d198dd9f5d8874544efaeae5cc9f27a03359aee1b1a1a4e0dda704fbdf9af9389fe21e342e995778a0134d0c4c348cdec8e0fc1d94377e51598b96a5f97fbb81ae4fa7a3cec37793cb59ece462e39f7f2d8c9f66392744012244b522c41207a0db8649da6e085a1b331857a8520166fceea86bab7a3ed9313fd0bad2a0dcc83fc7139b93787be412eade7af7c89259079037550a43778452cadb8c2d5ffa4afa1d73d0f9984c3c921b51964530e1d1a82f87b6310e054740f8c69aabf9a9392bb26efc05a7acc12c453517b25b8eca5e159b75b72f65d051807bcf35c918ec2601bc2e135b6596315f627aa75399b93643141e78a11ef4917097ba3a32f23a48ed55f7012b79a1ac54fc813c5875e32e7526483e3f06548143e7a553a396d2fbcbd28c83ff13522ef8462c1e7ebbcd52169fac9c474ab4804f7103581731d0332925b62674f496fcc3ee9dfe9ee25eb8d0ca6ab378a300e0b5250f2160baea730eaff92a27dac2f08f8d752be32e1077a3430180214d15b10059503004fc2da16b9179202b1afb4c15a288af31c364033c3234d451f70c40fd13a566fe5d11e54a3b9bd3446676330bbb6eddcf087f00384a5f16050e0fd27147a9aef6114df318e35163792ee8093c0887c73ab832f405b64e58a46c5b303d6c9dc4cafef1b5db5b98b188effe25a31b0a3449284bf268576cbb58af4260e990f595a7390946b64ab7af90903e83ad987ad7ce5a2adfb1731bcdb2d907d1160e18f81d234c75422830245afd17a155737e2866620f8dac2b7eba6fca8c600313a019a45e414a261fdc423ef6b6642fa8fd3d15a6700057e5a57b0dcc50a1f5f9d1cd1c01e4859bf98715370390f424bde2932afc1c98adcb237ee5fc08b0b8c5432e5c131d310833bafe8860abd0455d7de8679ebc90e2dd0609c0071572299808ccbe8064e08fa4e976ca970262869972fda098faa9b646c19021dedcc1f65955d1ad5da522b2fdcb142a0d2ef1e7a3a2cfe418edb958fb53f18241db92d465e20f6f851f9ae0a156c22c89d26d09075c4aac45e0ea0662f6a018b74c5159f08474fda3a9c7ee549318100b55bffc8dcec15a535ec62a688dddc6cbdad03e529bb9869497cc54d9e8e39f0fbec222b9ce84a36d9e58d05cf0adbaccfae272b3984053c38ca321a350b63482893b15ed5cb60a5b16e82572b3365db06a40797b2553322c5124325755745250d54928b6f089ab26967a32f19e4db2a951c1fba3bb3fb2150aaa39cc0d1a85389403a040eb628834779a649bc02539e1a912fe457b6c5787318b33f23ab78ea50b9923cd0f945f9263b28897ba8659c7cbe15c064eb89884c8921389b18069775c728866a1f61972cfd4c4c90e196a147c10b16cfd4a6967bf1aa06b1e7d083fa3c2a9e6a92b979a4862ef6afe578d985b1038e222d206f88d03ee1bed0b56b456e3b638c5fa335b273032af7a868262e012ccc5994daadef166712a114049499e2b6b2aec2581fb7d494d5918683a5007b0482cd1d4b1bc998b6151c299b62fc530faaf63ea54abdf345020b127ce53f912b1ad6c886d57f6c2a5ef0ab54f51103a998794859791cae76c1eb75269d4046277fa14eb7542eb03aed1a898a1bce9663fa930570a5dea6829990cc2e3ba8811be656c21e2dbaf61141e5bd746faf82a16705498e8d97479337a14147eae99900c8c7ca20fea54a4675f653dc880f7d8a72cd47329c5d448ff4df9925daa2bc2c2ddd7a98ca8799c8096758dd9ccaf66634f05cf391c6d05e9055eb159db94f29ce7e5fb6330e2afc8da4bda2f8ea401b099360cc7e7afc7e69054c008837250b87ca67aa7b8c34cd119ead62bde06d6ca2ab1362311e0ee5478aaaa417bc022ca6a91c85d4590d74ede6399cc3f4472cd2bfcacbf2fb6d418e4ca4e144d1fc07c207d8c753a5f9d3ddf3a0fd91bed472a460d857a80c8d4b4b80621a821fc6050084ea350b6aaad2080fd12ac01b78e21d6e83b49fb00a177e8730728f9109002d6e4faee663c76b3df3234fbdcd455d6634366b9d766aad9d15fe3eb84fa404fb261913768d1b9c20ef14a13104090c2a6e58500b09de45a44ef81a5ee3d8005251ec28110ef1aa5250e899c86efbb23c86479cd91ee628e31b30352a242a2c3e59104e63809d99634b58a363c7834ba5210d1841fbd8de5daafcfc2f9c890f91bc9c661dae903de8b646a122adc92a7e661d81e3f1e12b310c91b4a4e5f726a56a5261d3bcd4ce400d39f7ad167a63d04267fef7e4fe489e93e90a43f2680348b307e0bc58f24d6d4af797076dfa758726dc8e2c95631c39a002eb62388f2652b2b578befc62242acbc58c28859b2be225d3f3875bd10fa16bd857aa179021c0a999ead5003e0df2ddd90585ef1cd1b85e28d40a423c3141bd2260634b4e0494371e2d406c39895d2bc4ccf823a25ca2247c5e8dffe41addf4d40219043a0f8d3c338c53e68cb53b95231a9efab53fd6a3430ffb4633c1061e8a8abde34b1151c4e65bf3aecddf6e83c6a55b8acb37bd200b1db884d3c277f5cbf8573fbf098e4f23575d6d4e0ca088b718c592192c2b6a2eb2d78b9f773a3a476c0b110d25205bfdaecd530a971fc3fb229104c6e7259fed783694adadd557fedb4894ae0345b5b345a1ecd52ce7dff2b27eaf0c699aff68e1f06c1abb734daf81932d8816b43f3aea81b233ec33d0a0ab15bffa9d32240ebda4fd497344e55c35e56473133e41fcbbce8d172762281828ed8fed593ff1ea6eb711df35fb01d61cfdc2fe20faeee0134d91e60953e71151adf5e180b8ed5076779b63259a10b415976f9e2898bfcd129061351321d89760c0da2a56ab37e0025e0a83e4c251920c8b0ce61be4f8cd54792a512d193869c11c4e51f698207c1c80e507ecd3a05af9d56b6d26e77769c5fbafcf610255c2fb65c81887944d4d6db30e885fa87b1c4cd23a4715fe9965afe173636dc1724c6ddd7ee52901b6996e48188c555f7aef66bfc8411248015534059bfe3d6214ad93ab052d0c6cb72233c67f190be26e04d91b164c0249d109b4224a90a699583c104db3d9ee91061a71efa00f4b3caa5a3b77da472a2a87f50f6c2ae8a3f258449b62008474045806dcb3273031db418e079277a01e808b86c1e5eb913a504a9159ec365e56f8dc92425378f117d09f78caa81a792ea83e12ee7637fdbd98dd49f43916b143ad6c8b5acdc80ecd2fdb11ebe25acd0abc429231a8c1721cb2285de27f7d5efc412d195c7c038137b4ee460bb73f6126391658eb5c07447074a37ada33f388c3b6d3a97e7a1948cdcb456c972ece361820c2041c104108b9323864d7ab030ee8c81b4d7f7bfe13b72394dd1be3bc8227a3fa9c452a8599d24a10ca420a0722667d8fb5cb8f7035ee36a3f6e80ced5601c6c1babc095ded8f037bf6c49644dcb67dbf9dfe895aceb7a03d44faafc9ba180a4f13b44e01e41cbd911af18446ead17edaf20d95125f543f4ce384c56cbe099f42753e88b53ff2080c6a9a3bee8c1afedc2626612f092f1d5c6a7d38eecb1dac82b0725bd094f8bfc59a84d3d5e97813d2d8997c09b80b918dbbef0237f42ced7f609f50632afaba049397eeb8487baffca713fe54b66102790c801d5b40280c740f26d9b50a581ff91836ba7a6583f74cc2d01a99b908d9e869daa0c1cc2772966b46f4647a9b556fe5dcdf6b779191f6590a175c4ba4148aaf5126fe4d20495503aaa68c53fdec0db78b669ab31327d8d58efaa3624af26cf2ba298e1e0698c8d2bdd12df70932e3fcea5855d7595aa31d9aa3211e3b1a3a89002dbf7723e5b69005d825fff1f8d43390e5698327dec64e4963307d230ea41089cf77dd0313b52121b2235d2fc288d50a9914d3cc3cbd54ce4e41a0b1dddf9c8f86e61226bae62c6f0364778e9c037391a16c8a23cead8ed29a4fef2bf53fb3b3608fa218607dc97dbc236e3aaef81bfdb8eb516444cb361306411dab415f828c7a9c2b2918861b45136294dfe194a8789ffc0901255e214545a4ebcf4eea444996488e27eb9082d232bf4da14ce3d2d4e31c99f8e11854f31933e9e7b3e4feb81a338f04270a8aede63a8181e7415bf0d8959c97826aec25e182d940ffb7c728ede3e222370bd7f558924eed74cd1a1aa1d92124d5f7aba040d12889c3747511b73b08285e79ba795b70f2b47a1bf101af78f5682d315647e61af7563acd53cb01869521c788ba1b5726dae79205418d35ceb4e8bb9d6f488eea0734e71eff5fed92900c13c5306642f757a2ca9e5df619e000d84d2a3de52901f275ba2b481506ef2b24e027bfb5cf184d29fa7bf609a35f6f70d0b29df3c880c676b370c14c6e3e4c08e61d86bb5ddd3c57f3ab7d69a75f3fb3cb06918b65c356623c7ba763bb59c02121d2e7a834a53709b0b27d4da4420f645dcead8645d7c9c9f798c29fe6a58a007dfc5709a867a2c8d5c72522509fb497e5c1779bacd54b3daaa6ee3537d4c325b5b644f9d3738060485c5367fb1f313f9b325777cbf14b971bf2b18d2f3b0a16a10eeb72486b9ea42883440336269aa6f67b2345892c160d0952c18ea4f19217f6d1f45dd3bc180c1d95350e582795f6a489076769819737126cb2b9ba84912873231286c955d96d7bf607c0d40081ecc7a4373c4213313cb83c39a8c3fc61185a43ac96a7dacd144b2c3321841dcce6975b393511555887147a563c247343033f3bba0711fc94edd980ec770c77ee9e0c885adec6266e214e7397f8d0248f2bbbdc08ecc4f1affe4a0e37cdf4c5c348f6f69ebf873aa253daddb0ed03fc931698c76804072c87467734688d793a3a4bbe752b6e22e6865fa5146d7635dbd51f49efbe4f8ba41dcba1dcd126f17f0504c4217d7c9a162a2444021bfc5aa08118b38a728e2a965e2fd2c80bbe873ab5530f07aed1965066ba4847e462cef5f60a913a3c5167e9be7e5d3c4eb3c313e519438f2d3b0411c5d3c0141df3c505a12fd59b673a1f3bf65003f97caeb69e12c6f565b56a56e02eda5d9d89ddeb99c9087ff17454d49668a2644099452e814caa55fb0869f258396cd5199100525c3b05cfe82be0a62fac2afbd15f4b2b2978ffecb4977ab84926d5de6556bdc9816c0bcae0aba9e3b5904df14a520ea50d42a6bb2e26e53f94300795e2876f9859791d911b3789837e5480a0f7ae0afa612dfdba1b4df5e210adf7be6184fbb2383f14820c15259bdc8dfa1543626556ca55693ff7c4555b61adeab78896d2a9063b05c8659042106fc5931c63d71e476b13f06f9d98532ae17fe2bd3b110ece7ad700082e80e054e62040274fb5047fba7944cdca52ad452355a5146dcaee345b591625f9c39568c8d9af72c180eb3c4e74d9341dcee2a58b44d519a93f59b845180e4452741aa2c4670ec9806ac629c58183da2337b3b2daa732a2d5b17cb78f7815aeacae2f193df2bf1a4c19f80194db9995a681170c9d848a5d2ffc60d388761db857b06708f48cae537039ecdff66f6072ccb56a0714fd07f1b98cacb68bdb068db409a0458654641887a1fb759284db98f3bad52f9ac23eebc802fdccb5611b4fbfdf700992ac3028523ea9b4daf4f29ef45143aefa1253ce1c7b2989ee031469f406cbed78ababc15d42738cf03a69a848e17c78a435aa0fec644000fade113f1b196e500f213712754c4706f8dcd3bb322d4251b76d094dde2d29cf0d4a29b835a7fec34e06850b89a3f37b26a95ed6824181a384191467de119e390144268853925ecaa1bcf020103f1df03abf895c572299f56dd7d6bcf3cad24aa4fb25fa4d234ac8b74a430b77dc237233c8a34938a45d2dd8430014317d42447f740c475aef6a62da133f53afddc4147d651a69ee0ea69275305a527cc9213e539812dde358b128fa10a21317c2493b4ff02dff20f68a65f6507d873a79f930a4b921c1fa025cec11d426eb10edfc22024206c0f02f4e8cb99f17ae3912b112547c14d39d54ab1be07e9fe721a516ca526a401627728c001e111a3cf37f25e059901182996e5b44282112e23b9f464c6c3ba4cbe77500adc916acf1478ee9f74ec3052628afd8c41b85c4978caf9ec9b6b74472cb32ba4814bb63f67aa55c39b7c3230b726d3d8cdeea05f02d90c9411f85d35442669703d785d88e5b6a1f33689600babd6b741402d1a294754e33da00ea3703abb5e71a47905b6dbfede5ed1d5d2cd7608960386fad09e730c5afcad0441ca37de3751b6cbf42b17e2f7c2981c1015d74a71fddf10e7f87657ab424f0cbe33be68a75eabfc3d1553f5ed8a105fd2fea2cc9ff9b3ae99596fde121ceade67aaca0a51c3275b67642c1282a44a04e1e2449bc4310625b7c9fc5f1d0b4a0f14f5ac5e2a9baecd06ef9e636624a953318cb8a50078552f4fbf8a85f24b8fb02d5efd4b084fceb0a80b9afae437b14400e1eacf3e5c70a99de3b23dcdd9bb3b0a93810490e7464886d8829dbe31de580169cf11e68ad484fbabd1bf643846fd5deb3777be149c12d51a4466ab654c02b17d1c7f78cce8fa36c279d0e2492231473bd0c4e0719f692ec83eb7c900f1011", - "public_inputs_hex": "0x0f49557e21b4c1e191c610875aeb6702f871442427b85ffe7800672edea8c1d60b324b37de91c33f34ff507deaac47e46ac9414b19c85359cad868c69fd6e38b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000077117e59dd1feeaa796907ee32a6005e00000000000000000000000000000000eef1d7464190461ca1a8dd367f48f2691c4b3169d5138d89963b08a431685e575ed0dae5acf6924f1458f622166a0bee1ccd2641e1e9bba64242cefdf3aba968b556698db1967dd559f0b465375ed0e41a4f5a9fe72cf1cc5a1512597f770cebf0b305ebcf8253761101a5746bc6d44006de2cdb6761ecafb3a9875fe5f28694e7749e5733edecbe3b49a1a34903f4c22ff5a850b79b88858917917684517dffd3ed24b2aa5e274c405269ea13bbf06700ece9c8a24d22aad11d49040b0158cb07a4ef0975dc217e02e5781867add686033ff88e898ca0a5be229028e8fbf251abf458ec5f8d1ad7f5d6dcfbbd2d51d02a27c7b79209d048001b37b0c550f30837dd7d8b86cc16371235304c6bc2f9dd2b8ccac82963e057a58b2e299874d166843e4b273356423f5a4e25ea487d0ca81249c2f4e26761665140c88d1a0ddfacce3a75bd7c135214b95b701073f01b851f639e4a8567bdb7fbb16c1c024fae67efa5bbabcbb8071d51a65ba263648247289c5c3fb4e27fb6bf47d3355d5b73a486b026213dc9d8d45325fae14740177c" - }, - "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000c89c6d537dc17c06800000000000000000000000000000000000000000000000abea37864d8d1a4bc00000000000000000000000000000000000000000000000e4cb9d9b55b3cc4440000000000000000000000000000000000000000000000000001d3f866ce9aca000000000000000000000000000000000000000000000006b7a8a9b76244e8e00000000000000000000000000000000000000000000000054804db9064a04ce500000000000000000000000000000000000000000000000a0cc0944ff2fd3b3a0000000000000000000000000000000000000000000000000001e3710e20776f00000000000000000000000000000000000000000000000af2374a1255cbb795000000000000000000000000000000000000000000000001f42106ae3c71e6060000000000000000000000000000000000000000000000093e40600e5880ac680000000000000000000000000000000000000000000000000000cfceacb62bc9000000000000000000000000000000000000000000000007006384dc97ba668800000000000000000000000000000000000000000000000e88607f6385664f96000000000000000000000000000000000000000000000004a95a2e0e0d8546d30000000000000000000000000000000000000000000000000002b9a55392ce9d20399faa4f45f0b5fd87663551d18cbc7e1e7bad5f2915d9a8c63b2bfc8bf16928177752a23de28d0af0ccc90d6ed411b66e9b1118c664d2cf694a04d7fb75db08671fe02dbe87691b8866298ca7325099594c113c1cd88938ed63018d65a32525ca353e36e25f50fcd403dbb31602da618db03b721dfb6b8ac68bad4b4ab1de1e3abf3b30a88ffa6ff867d426b8ccba06c56950e7d347a13f07c99f6c0441b02c294ead4ca0b1d62f3c192c6e040e68189d7ef936c05734cb759e946d4dd4e60bbe4692392ec64c98ae9bfb79676bc33892805afa7f201f277b54906713ce0b00211627a7aea8ec047dcf9ffb14cf724330ad3b489d1cf6d7f132495f85da1d1623643dbca86366658f114b3298ab73bef71bb6751ac6cc139f9beb8266ad1b080e3a38d3c12c60abf3fbb3d318308c6307b22a8b71bfc12d0c032595e362080494370b121569e8e2610dfe14e8aaa054ede111e30aea159918186bf2acd0a015e1d86a606b53484cb42f0a4ad9d18da5904d4ca189c5390f2e2188d048ed1208b7fbd3f62965fa0bb074ca8874602f31d2858f90acc480e2a06d521d53501001848dd16d21bc334654463586270607ad62a34a98913e56c670f5c223829a7d201bd4bde38d28cbd799099914a7788567562564a9902a2b318209d71d84f8982e03d5f68ef5a4427edfc0f2c4f2205defea40db91417c4b84e6edeb640154140c2dd440e2ad98b51f5cabdd687ad9c8928cbc02a51ec32acd5ab418ad441e181a546853ceb1e68df0713dce6a32ca5cefa7bda2dc1b12fb4ce27741b42e5da41e2eaf7a36c90126ad33023f64dd7e5813f0a4f1c26201e6c405b8f6bbec785008491423a4cb455b62534e676fd55c8c02cc5b918241039351d6d00e7b79a15e0a3241db4b42abf14fc6ed7a231c563b190eba472eee285438209400eae43c060e231ad2fe8b5d4a34d2619109e7cfa7b5d64af2f03af7b63e957c80e20db7000a70c24aa51af6ee2add7a06bde08c83dd8204bbc1c5255cd39ad602e13e6d9928c6b45e624a4935bc1eb76b8240b1c34d1cc605e486a7b53da04bcc4e1696400bf14ca73fd3a4df6dcef76dc88b24e20628ef9c38954bd2f4d0ad27493b69d71e53b5a2ddf3e1595732703159e3a4705d99aacc650d7ceeaebf013165cc728f0514a00179184d0fae94c1b2e40c315d47b6b17000e59030dd029318070c560d03cffad0aff9f24ab7ef3b75aa2d1ee2b65c35e6ca303cc49b25b5cc7a7e4af7238b08386ea58b1dc539e87dd2aa6f1858839625e45e24239222eaafa5a2a9a2209a59250c01aa9ff998ad22681e3d94367fdd3a6665d039e1a1ed3096d8b7cc23e56856817598477cbd49c345951f1992ce3ae8b7f8c3bd4ee4a57ac26663cf113a59f5907e4cf2aa2f6d7643b1fd08a8c6d730b2ff804a54477f326c6eead90453ffa88bd2e94099ee8c518e31937ac840df0eb3d9a37a56c126f00b48d58027d2406eb20dc4f83c7ee2938ad6ccd917cf64dcb7141c86c6923e37279ed09e10a84225bc6fe2ad5e1d15168aefb2e0a7fb67584081215595c020434741a3d82f3d39518bd7549c6411574228b6d50c5471ae0496cde505042f15d8fa0515f5121848aef65bc2b519fe97d669f0c7203db55a6bbfa6989a4e5772147e8d2c9d0fb07a6fd4a03af65fba4d42398c79948678ffede5a54b752ae0f13a39d316bc27dd4524c1b116537f44603fcd89e841715fc5ce79051d295815475761f0db7b2eb70a6d50e7f18ffc8553818cc0bfb56145dbba2c25070d3b186df877de01662956052fcae18b6b408214e2b0f55d473bcbef95a0dbc62aebf45ec1f5f056310b579c9982326609b818988dc83126b68675c351aff660541d2507a7efd2e0001ecaf16c9ed3112e1033aa70a8c13fc73c9d85fd09496626e439cb874479a3d421be2645fc870419c171e0cdc870af8d21b817d91d13a8e7aa78d91d18fca68728ef61abda744559114f6f496986d2cc8df0db894fc7d1bc327ff3c5849bc9f30ebb980ca907c6a26caa0595c78309b2b5f860617dff116f392b33214bedd3210c949c8962da611212662ef1a1198f6827186a5b9c2466c121228836541989c910d3aa1047884f46a7a63525ff069b6d02a2ae0383bb8d8ea8996a5579296665245390f605af2b34c8f459814940922c5ca726c4ed7144885fc959b43edd0f6515d7b70f4f6427f7e82980a71921ddfd9622a8d407a5d1fdb502f881b3de1dc70eed60b8ff4d4430268d71e2aaae46f474bf760df4779015375aebfec78240c427bd285a0a0a8cfcdc200a00f40811f554a8744463080dc26eba7434803f06192ff92016b363b6987cb8d3b7411ac47e07e668b45a9cd839a6a7b5b14a138ca925885e95a6f19c2e884f4b9a6a93788d629aee6ecf157274a514c18580ad785611c0910ddf81f8dbf1d3e57fbe4169fd6e59ac403662813a67dcc0da913117d30bc33121916704d31f9b972b432b9621fce608a1440f992bcce07641745c3fbf1718d0bfeb7d62ac55e0325a15a137307b0b1f97d2b205eb5d6b961669d7b986291c571a26dbe60ae868a0b2bd3c9daf91d2c384e0cc3d67453e5511d304ccf106102f18a6306ffe870df0e6d0473aca0010cb2aefcf29a9a8771b859f7db3cb2c42d250c35c0f74e3630537628961087188dbd94510d97b024e97fb6c26032201b4d76b354fef07d7b608f6e10f52b457d5beae97b43c323e4db538bdc26ed71af65c36e85ace2d80f79b138c010fd892a5051b0ab3ed55e535eaaeb293a58d2d8fba9edbdab3223a4a6d9c6aa64033e68bbdf51e6ff450bd6ebe5c13f43b23103a0ee17ea5a7cc61175e56c0c7ce50884f850825ee45a3e07edb0aba545f4129e7ad569d707fdd5353e8206c4f57dfb13e314e6b6cff00f8577b827e5ca560009d83b40420a631a51d16a12988202c3531221b5126d4eb6b45ba008919601d2c85139688273b7a912d9378cddd42cf4f9fe2c2591d775f65748dc64733a1281ee676b61b20d780b60770ac947f6958df183df76f61c861316aa1f90946c397270831e5927d3b2c3d172a5ad77654838c198cf28357cbfc2be906754b6478b31119346af4397662f1250d996820dda07aa1323329d6a067f1d9549aa01e688b30114ebaaeb319424615aacfe626f6ecc2847d7e9bf277ab232c205315b34f1e0b8506572704459c92bf13573f4ba64060c976b3255bf9cd99a2080e988197cf1b6f9249ac718607deac4ff9e11ae12367220d8f8d8dcabea4f0e7663c7f59b813094b4f4dce74b68461cf994770d6693fed5f8ec86d589719f134706e72b3de2f6273f9ca3170c7364ba64c627f679c706ffd9ac9e585a6931553bc32f38cc40a83feb406ae89a4a0d60ac2958d4166b1dc932477cb6e9594357e24d9e143c82906c5789bcfe0a99626170739be491a23cb48cf0d83da111b58991845915db019b9f5a9b851686bf22e2e7f703f5d646b90cf1ef39e8fc72b42e4112f1f4edd182f2bb6f3c86634ab973102e8c93a3843b0e0a337a70c6d7f060c71b2dedc6222c66a6871c742cdd3dd5c00633cfcc49bde2792c741a6e52ea4a1c33ab1aa480f2bc7afd5fb64009acf0c2f6bd318f9e91c705ab3247d3b290b6f414289798801a9553d74b13e457c54215a7a5206711946f76c240d3b93e98957301aeb0e97144f51cada9ad7206d5d684a4ed9ce5e28f8d8d0780ae0f1baeb6f324d26a98f294d8e4f06d7dc6abb5fedfa0294ddf410b43df8ae94a71e98c82b3579cfe92b1d5dd17f4aac660baeaa09646cfa4dc073c53081ab42c8932b1d5b13591619b60b6b527d5e6cbcf107faf382d3d96314c61cb3a822cc3022984600339609f24e28bdb3802e418d8dbdee8058a36fb20120817e83e59f744644e92757ebbc11b2286edd3d83956028c3f5d58dcea863db877dbaae40aea6dc4cf2b094e38017a70d54f771fe61acf6f2e4e09df6cc683ddaf0bc8f7a08c3838ebfa945a53d391e0a145204009ee574a99eb6c606623d7ed090dc3e074cd7152a7a423dc98abb4a116d04adb6203592009e92d070801878a6095dc68c222336389815d4245aed0d1d58149b353f2715514ec64bfda809e279802e804783942e7b4346a191486d602a6a74605f461c1c638f99e3ad0b377bc6cd95c110ae36bdc3902df86159ae91165da7bfa47cc52894712b9adf10276f5cc1c7eab31c0f2cc3e8f4fb0c2961c4142c9463aa263292a1ff073d4bbf5b2c1d7c0471997d757103ccd9fd904d6be71a8b13aebe660d0836600ece14bffab2e6e2f71756666aafc7aa07c62e3b1eec2497e0664fd9d726b753cd062d7a95ec2edcb0e152596fdb4c748bc97f25fa912174c8b49dc0bd751a11c96739f9144e529d711319524e989446e8a6f632a5df1a1df982d6e8741fa0ff391c04a1b1aeb794f6f78e2c4090d6afaeb0d45248d309a4de86b9c603f0b9be48cd9b3da994f3998ee76edffae2160ce10f9b100bfe280acf39ad339ec311925eed926024efbf2caf5ab442cb6412b65669dadd448906acf752f209ca42e906a204a2a1c2bd8e24267fdf62f7ea7b24a78f93fac69802eee7c25dc4c49ccbb410e55937787bd6ce5a42159b3441f548212cb7eda00f2ff88884c12b96a676d0154443159a1f6b72f01e08ba30fc03ceda9d4ca9f72d0895d29763a5e317a36e87747130090e2ea3f17cf3ba9f2f4c51ae18e32b5c8728a082be630596cf61ab1d3d6383f7a78d4148f1aaa348d26f7309c98cc3f3cb1e12947d0a21ba19b53d4e479b952b268dafac63352546791758a344c4b9ad4c042037903c3e972a4900bf220a21ee32d799589e635830a260839a97dc4f5a5508d43ceee2225bd217dcc4b8b853cc49d0f86da0b0a7d1ff25924041ecd8e48715c0f0f6b864470dbdbb63eac1975ca815db7a18799f7d3a02aec970c3ed3c4e158ddb8f649748e6e8dfa451f1ddf78fee322c6c50eba8c8bcc1e7ec64e2da640eb97c1cf3e9282911011ee95935e08db09ce3da4d7cd66a60d5a5178dfbb98e10625f5828fa3736f8654df927599d6a11fd019263d07e780fe58c25fb91911c2f9bbe551faad0e6aa4ff2e2d2878be45cc0fab413ef253b81fd186bc75ab69a2bc1cbb7f944fff72da778484c84de2496c525d757d9c6c5e273053d2fa4eb300041edcc8c15022c6b5b0268c58adf1284630ee5a064ad2d25c6426bae954a530a980d6a154ff0533ec9ef62a37ebb3addf1c6d3af97d7a5db0526ecbafcb32028d5af720af2661efa9abe7defc30699039784c7192ff1ff35e33132a16dff3d076d01884ed88148ce4515f829d7b11d79f71008a688229933e8c3b6ca8944722498d74aabfb9a7ef6ebe4ef83d9520b15dcf0be3c0789946f814cc9a2ce95341adc0cc2e042b151f08cb81cf13bc74690381e23ed2648be36fd1eed6a84eadb029c4aa9ffeec101b0f7ca9fa8a76ac1ab4a567821f58876fe0c7a540f0760b31354a067fbba520c30ad6a35666429fd210f93aa2e95f2c8b3b75bd6b68c506b019a973330f98e900cd1fdc7a44c8abfb4715274b57a4d17766d6944be602ceb0dbd3afbeedb8fc5eb12b443eef44ab6f6049446dbf0e228e6029a6e8a4655321d538a8ac2fe637eb8d7c2d3ea495fefbead174a503bbd80715cd9d914b0fa391b36f27d1ce2a46f43356f70e56c740ca47334d2569fb6f4a312367fc5efe34303a6ca94b9dd1e61dca11199549fb4d4a8b0c789e658250ba944053332ad461e2aeba9eb564bcdc0b44518cff46e9193a8ea344680e19144f8c798c67ef6e25f033a5f163e1221aa863ebb1e0d19dc9da8dcf641f4555b44bbe475ee6da738d4141ec8e7b098acb7b8f06f3280740690d52d31d5154e5d3ca19102ff1630e73a278236dff0334c674aea7b44981d0aa3f3316e0de17bcdd919b028e1fa2a745e010503791d46b9d277f533a4ff66004d93b7d67061f81f16da634eedaaa69bff0fb11f989e7acda3e19eb5a0164bdec36695ee787380b25e5054ab875eca29670e11bf0dfa5dc4fbe8168762b8ff6088998244b54f78f73358b3e09d79efcfff16131ba42806241a5d92b6681ebc8e2105a39e38eb7154c70620788d3791319802a301dccae44351b67c1dff95ef2c1280a8be53d4f630de77a56980c912de7625368cbcaf0616fc46972eea11c8837e693fb2050890f2ae61494b7328805e530313d033a56aee98ff24709e09fdae163b19bb8e5bd3478ab4fb1fdab6df7eea2d39c2439c7b762d641d7103384403443772756c7f4e474c461255fc78eef7502f2be6e196906c516d597d172d0b2b068f6df3b7e3452895952ac95363f2a0650d0e3b408a7b482df002b71a0428f19cfdd2081094b775ac9fc1d151e810313c03d6dbda32758fa2ae34f06bdd8de0f1a30e58259ab810124054c6fee1ddc7891669d12248e3e53b31132e6740873f2d60c1f6455914f70e21fc4d8a25765a2f026d93cf4dbe32a22173c09019f9cd25a27c52cb6b15a1eff5f3cdeb19a0dec406372614804cb4e1b4326d15ed3eb8f972c47280999028dbae6c5bafd757153c1eb447d9f1fc3b066a0b1e09d0fd18655e36c6081fceaa29c0029befddc2006609585113af587f61eb5fa1835cc892d40585bd1d5698f051564dd511f44f912c277b54c9af80e887198b9a94f8bc2d03b62b2272cef760a6687d0998196b1f5624bdb12c15974cd89cad11caa70428ea70c25470658c9f273c73c0a8ca2254712941d318b86be07221c1c996549d3dde7447d2a32cf2fa6a634a1db5c67cf31a03208e53ee766d564d0a6e8571b703ae063e4784718fa3522df36e9f437b986509fdd3a9cdf45154d66006b8618e385414711f57e946da0bf97667747e1db24b14d2fd3ebd7da3314e4ac46ac9b007713a744b5a216d248d07b6e95bf97cceed2c377fd627fa0e4d55d4d9ac7a0677d62311b36a331016e833442363ddca763e29d8ddd02aa806fc880d57b993fb91e95150699d121f2c919b44bbaa7da99f501c3753f981def7c102e19d480edda037efc0936a7ad9df50b1376b91292f72160f125ea8eff631272ae19157d8b22b143666108577ff3847d7b66ce4851827bb06ac08d872dd463b08e6b9fab8baee4718dd4d4176a41a1b212cdcc89a9cf3ae0b6f523d34a58363732b0002467da559b72b6648354ab640ed26ede6189e14dd244325f66e019776ab18755061be47e154ce39e4717bd3e82d41e33bb2a38f950f90fc2bbb2cefb2faf687791f677e9a2ced18135beb8a02e3c914c7f76f344d22be36bd30322545e47f9dfbe39ab444c127238629df49e04d4f2a78404c89262d9b6c14eacc5130dfca23eac58a8b9c1239f809e521851610b2bebeb87eb6ed10722e1eb4ee99afc987f656e28fa4c94ce00f450c2252e8b0bcb63d7f4b1daf189d4fec6a0eff27cd720199233bfdc79bea4c91989e9babefb5b5ad1a5777a120bb989820521753144cd511c9d37afa24d25398e945fd1c10510876d909a0d414d6abb1dc6f08decff25e2751b27bcd485d210f08738c533ef2f4283c2f25490e92d5edfdab7dbf61f89c1e255138aed746b4aa831c90c6b204480ea92331c61357a9aa02a6dd527aacf387400b323e7bc7516dc54cd1822917b7bed12ae2510ea4bc05b01193fad22ce67752a0e96fe5f015d0575c68676cd786b97a1cbb4729191e9e6636e17a18e0ab7a83427a2beaa701bac17d8f3c421fe9fbf1421eaa2f9cbdf7adcd5ba37fd5bd11362397abb015d6d28e66bdc3b94a147223a06f633044145a0bd28815fb92b515573d51d9b5c5baab513e8c4128c994f4d11871f4279cacce2e914c5d1edd41a9a1b83151e037f5cc223c89a04495484717f0d019294103222f7219fa5aac902a925bd3d07495581ac613c82c4979960bf3b17a2b1abf55a0c3a193f4359594c466125a9f97a7fc2dc51650872ae7554ce37969cc148cc2cc447107b4aab04902ea7699e868c333863d75bdcb64136dd680f474f62d4dc965082959f0a127201846c843f1aef4ae044f80e41e74c39d51b6c8ba9502d7bb4c2e9dc0c7872c11ea07da7ff3635c4ac8ccfbfff8489cd8d09ccea02b04f74bd235a6bac2f501a6721a5d202168b828d6b106b141400cb39a5d5ed77d13b87d15fd1efaacbb24ae5eeb854c3263dd2aa28c3ed98afcfe9c087811af0a1a17107671953e3431780da900b4f917bdc92027263da96b4d60f542cc2c82e509be2e2b97e4bffa9c3cd204d3d2cc19c8960078e8e74ade1a3cf15fe27ef6b20172e772a3dc884bf612a80ab2eace1e48cf3648f3adca4abc3214b8955003cc003e578536d2ec7331b59490ece686e352e8b85c7ecf37b685d668480a3a17ca0170ed17ef491496e42584995fe4a3374f222e01026d8ab67436113c264aca1c19c8f2d8e0297a873069fc76c743766555556f26f56e9b64584ab29012ddd9270ce0e65ee8da15e3197f0e0ba16c0289f55542ffabd7b3760ad0528a60ca332400cdcb19bb3820fafa568c51b192fd3242446efe7f297bb539165932d989d1000e8c197a075b68ce08d92b47d4859cbf75060202b7d5a8bdfbd8f3f4b026796104a4501d6b9b0ac4d0c288e4b3bf14cd043909750b54dd89dfba0af1883702ee0648207f313993b9a8669bdc97b6f8e1d880f27e9638b8b919b3f35df0d4a4cb13e79388eb6e11206cf680d0665efafcfb6f7a634cbaded364b22224a971aa3e2bf89dca518517c860d8dbf455865efbae865648cba9e8bdc7cd32e9af0bc7c32aa25fc0b0c35493c8bd144b55dd8dea0db112ce0b11bcdbaa266abba170df3e1b862f038b5fafc43e249434b4868df74be6d0010aa4d44c09b91bf8bfbae3e7276b73347df76d954af4a210c94bc03315190f6a90b988e210fd37ab1cf8b5352625dd786537207de459105e02ceae54571c391fa25a2ea3ce38c7c48bf3965d291af2670efc172e2b9ad90128146162e705eec643aeca5f1a0209c9230d1cf61e9c39bbfc9e9219da275aa1cbe22147fe193ac2330830130e66c3e7eb9b6c4f0c67062f5204a407aa1ef69fd65e88c7212ce545c52c8c66ffcc82487f8594b629f87ee67617f9bfa157b9e98450938cc8ceeb548aafb27731a6db910c2f3d202c63540025366f9d89c39cb0b686d936d2e3836e6c341d7e276069b8601b299a1d19cf2ed96cf7769dad1f6d0848456e758a130e2b5a71346ceedd076dc2500d115e52496919ec8b4a9c90a1256d0921d71c46e69e2742af220d959e7737ebae17952950241397ec0766e7922f06b2942a0fcaf9e31dd2fad3e223612da6a91e121135039f1def7539c63dfc5ca99614f13e97e5c1445c6b76610686fb720d9e228c8b3894d56b4509e39c2c7b15062cf197a7f6b7d97afc08a4612495107fe706b8c5cdbb040dd23f8c9cb732dbd224d178e16aa554b8301e485b59e74121a802db623780ff63442de371fb5a77fa649994d37abdd8f37e9e5c4d1b2d0f42c320ac1c7061ad7ff3396f338966e21b0d7bbd2aa165cdd6f2a279934a7ef1ea0b145f8116afa4921473eeac90f43e6751baa607563814d97d8e5981f4e08663d60aaa136b5252b7b6407974decf3a4a709bb9ec0d95e0ccba44e7eaadd58a5a7414d88abdabb208b20f7ea49b3bcad4770757644454769f27946e9e289c7d582d0e8d351c17bdef0985b3d1eab178d114d350e118515b0f2017a2d173dbd0f9d105977b281e273e7a0161c0a7d98da4fa432edb214d0f373336a89cabb9baa08920e60b0b53760d0428dd28a57e05eac32380fd9e160e19daa8891ca35395014f16ed8d012d33f22e7d41d5201d12dd7939c49bb6361c1bdda7fbaf6bce7550fc014123f6a31e0b82d529a6d0f54bfbc364c60c911f486321236091e7f01e77b31dcb3f844b13078b75de943429987d5ce353a7f6ebd892272d0b16b04d34cac31253abd9d1ed6c38c78d38c15e5d8840190f45b718b4025ce35b79fa5edb416a1c8a2b6da3049ad2756f045aa9b8fdc8e370ee37bfcbb64940783912d98d00222badf84f989b1694925c313f2079e6b4e785ece0bb6ecf9d308bd4eb07ad0b7010da33e4046baba11da3706c8654284da3fc40e944342bbf8b9ddda67974a5082e92e98c8872e2892da7cfc7eec88f12edf372ec6c62aab4d853ffe00f8829071fcbd437983e196a54d1ebc802d2e07004e53ff074e776bada1eb9c7c95dbd511bb89d20bfc0e563e85165bbb8820ec35ddccd3a181a7d8dcb1cb930830b64f307a2e001b1931cfeb11744c8b5d5e4ba316462158381a48272cf70c85474a3c6257448805443e1fbb464f1d59f6c9e7c73c3faf9e7c49cef4725e22e85a314cb1a81f6a8d20dadb4d7656d51157e400462aafc88f0821ac59174b614f0cb4b1d1fb2b54961adbe66a6e3c633c8ebeb09e82366f2f8ad9cc12a03097a6c62462e22525c15fa397556d997133dc53dfe32553453c422d55a842dbe6a01f4f368481dcad44cc429803caf6b140ff493ead1d4fce834b73c12ac73489c5c82d1d0091833e95b095c84a052b0e5b66b3e3b276360db86276ed736d39c65a04dada032291b3237ea5cdd8f9ab325f169b2866ac990754405562f9831ce895ff2d0df961800f3af75ec1ed1cf38b93303e81552ff284447d2ea46166f94027431376d6f1a847fb254374aac9f9ece3e0b383cafa1b6c224c8eb3a96b017e52a8cb4a93611b26156e338d9365c02fdabaee829613cffc2b5a0c5cb95757f45f28c94bd7f2457bb006461ce518ff3801a7e382db239c999acf8ca898e67d17d72ecb696780208aae8938d49205be2d4a5d2094ae8912c57d8e9232196f15ba1f459b035fe12f00d11d33bddcb331bc867e6f1c4c2a52c2c30acae767a1d6c265606607a55084f33e47aae6fd9f6e79503f5697161a40fc994c7bf0146496b9dc41c408fa202514b2d4c7f04f6961eca1fe17391846d7fc84757bdf4f4a6060cd4f98ed4f7098ec5bc944178ef9c49b88b1a2e53de1d99237336b4c0b6973f234964305a1808e6e0c1858df2daa7b1877d90afdb028c3c89a43ef7042fe9c38904360fbc262d69c32b05e446110ee2d8603bffbc0fdb4481f33e517698dcd54bbd48de06b70f07b23c65850c68063ca8557ebbc7e796cc7306d755cf701b7f7cf896007f2f0fc38e421e1a38efde29e2a129ca309f8980e1d4896a061e7d6d963b9fc6a52830144780fae450fc3957fdfb39bc2ec75cefe759f393c866e341ffbb8a2b60c4188df90bf20e1bf41235141b8a259b56009a5c716379288b75a4fa9006fc3185113f52b0c20e5686598d2d281fb35235151c959549a0cedc08d51634bfffa0392f3d72d923acd03d487baca86d252c8da92dee59605a742ac030c9a89b9867f8033d91040102dc0f1b3517fd43144bacc5ae8ca9ee36e0fbd0dbfeac61b1b36e154ca49951e1338ba2f92487a6aa36fb47927431a24a69d337cd8ea5e680958322312add93ad09b6885953ad9003ae124afa391981037c10aabed8118bc04b98162524c11dc8cb722db3ac6803cbee486cdf8c33513c22cd41251988a251e96419c6be87356bc9621efae31b1896e931301e2229ec61e93d4db5b41077f5fc7b188a9751200bd4e58032e702b45ef22e0af05fc05123b333a7a121725a9c61fd02575e449d694602ed596b1b335040f388de51a2452e9ff83e213e3e38c6f52a1b046f24e07e265515112c56ea29bf39253c9a86c9f57dca7345a00a4cdf775706f1e95b16799e4ba12038fbcc2e08709bf658ccfbd1483b4069d0db57f6376a2b31e619856943621b9729d6a6e74dbaa22485792ac25154f65e60ec5acf0c430659c71f52cb233f0448e5e68581abb5582f04d9768b01a55f32b5e9ce141ea51cb4edd9362ff5cfab9c62c22130bd7a02f5c81fd23e2fd3739606558c7e0fb40192e09d0084c6a2710c59d820ded9b0dfb52e305756e0c61edb1ae25a70ea800dd31c9e48109b517c961fe9acfde7071ae40ff98cc4121bc322256f9ab36d6d108e72d7dae149a54ab46cfe8f241861e3485334f33da713ab44fcc3d01966a905ccbd32a363d22fe2781d4c08e0e1fac22f78bca526c0133a72c236eb8a765513d0dbde4270cc3ec7baeed504e673ba128b5b01abb64b2602a4c6ad85634b19110de95ae4ee45349b57ee12c10ed2362836c4fd24a5831b8216cebdc8a43af22bbaffa79dcedf5476c38a0c0f6256efe1f149225568dcd0773b66a0f5591a9c05c20af8d94c7d25aaacbf83f94c21882788bbc3e4b39c28b4ee8a996bb1f10821695e01a394c003d10daecd0d4dbbc07b583d27cc13f37000921f3af89c7a3a19459449376c83ab8ff2dd441343709b2a8b289f21efcb73ffcabaf7c0510d370fbb0248aaee14e17980e4d275c88789cd31a4d6cd2087dd53530365d448714b1789bae49ff1dbd36fa3eca86cb86406a1426453279f8877a9ef3c02afd141b60444570d61f15c5762a79891f438ff47136a11efe3cea777a50fc3aa052abc500a9c6aca82b30a6e8813a8f4374b74f7f55726f93c329917f8b30e1ab59c98801b143d1bb0f80574d08726efb0b1f21f480b56de8c8b115b06c64297e58f936b2cc18773b7c21b694442b8617e4417a1531464ad91e901db9e348f50350f4c3f216d1e25b6584b9d33dab2c84cb0d6e684f6a0eed67bad450c575efc4c0d594e206887a1573529b89fc2631f98a4ec17f03733fa0ff5e67d600f480f5610955b146b73ec0b092980f0d33c0045bf95a8eaab0f0a421a261992b0db14f8778be101f6b0ef437b0890cc015c63fdeb518f4fc8235c5bc445f76fa70474c34ca5b8248fc167187c38a539762a5b0e3dab0b6d69b5fcea61d187d53a882119b9eab118a7f09c3955d823e048c83be5b6b71dff45ad8690a0fb2a510ecf3c37e54ae31b48215c527f02d9b013fb0138c7e2f9d370ea123012bd03fe1e925f469cae5903ea70b93dfae0911b49a1f3e7878ec80d333a557003aefbb777c2c1341a61322d45b23e319beb981b89d42d6d28500672ce4a244e357d0be894af6fd6c3691311610518f1764098418a910a498ac6b8978e4432b79f5f7a60376458c1b593ca2fe4563bfb08a32688623dfd3b861bb0aa45fb8f9cd3b48f268088155ac9112205bc557c7931c1e37d018ef027384710268e5a91a7ff7301b4dd5534f9130ae20df0927c7fad23ea0a0270660946c46ad6ce067cbbbd7bdca6afc18614fbe2f2098158b1111f78af9afc775df64cb81d2bfa5a59ceaa3b52b091216bf6d8facd118aa56c8c098b4da0926502cafe647aa42cea8f7dfe592d03662b335a456d4419fc9e76413861f02caf52a4f4489c24649b317472983e5dd984f81a17d81d89227676376516733962432d1167e2d1663c3035369c5d9edd006ab5a9b34d5c670c64f32218f939710b9107a5bfbe4dc3b378c9a4941a98fb8a78632150d02e13174d118c62e479cc5df24825f4013e21a06b282813668ad425634728d622c7942dbfa145133ad697c75789c89a5507331bf91a05bd83a91a7403f91e2f8c279d2700a3f990b809827fc5b8b44c2a63d289d3de4cb742aea0c3547c278bda86490783ef2164e4be77de089f9aca8c96bdd0f4025ec1b28c46b23b63ef246f6fe9112c9770df978d0b1e1c49c839454ec4b5d2e56a3fa1e666743a89aca491a86d084555685085b93a82aa0b80bf0dc52cbc6b69995f398e3ec9a439bda8f3e71d16ba82a35c75ef65f8e7136ad53d95cb1987ad134d0e01829b382651dfe2d08b2b10f4725cd179cb2edeaad869bebf0d2479bf66f50a23e413249486e0db8a0016d65db73ba629a925c5fbad59b0b91c4081d1290868064b2bf615afbbc9048f01658fb240208041c3e282c7cf6c433a4defd52989300104b975661ca620abee10fabe7ef8fc6a53325afbffba8dd31053389f1db562dfe1a4471510d02cb6bd2d6f47af21e48047838eab00bf0d30b7bb22e68c5e477cf7e6876a05dcd85f4029302348ab9dd41f3385055edc3621db38543d9e9bee69633f504d39a21e10990617fb839efe0c159e8d66eb0d79bf3bde28b5cf5ad8e59d7d2c95205ab7aff62e055183a5aa5fbf7a87d3eeac44971fbc79afbed69007d01ef3838b3b859125220413f9cbbea087d0c890980915ec1860fc7c0d190babdbcc6a92b96d235ed426c0334dd23410c81279726e388a829d6bd677eb2d678cefb2ac120ca725b1e32f9fae15667a3d8e0b2cc51df7ae0512c26b87daa571de0d6645f230f0e6747c225bc2d01348dedc4f2e3a8789c3a20d552ef06ff669a0f44c834ca2aee3034d05ce81e93d3ae5e1b71de9f0e3f5f003e1dffe2fd8f4b0c447dcfa4f7aed18fa17945e0d4cf92deed799619f0a09acb6ae81046f0047dd510493a56cb1498e712d37e585731a65c80f4653abb50f5f5f855a5e3b504aff482443df1d44ddf40408d651d48b444aadc22b5b49687122e1147e4a50f3578b891520f53c62f225c42e0490eb430ae8e33ffe09f0e39d300ced42067da91fc6cd02e311fac20b172b1dec4a4c5c5019d5a1482f38df0876f89ad2734571416300ebd8572beda8e05f1a31d972f9aa895ff52c782404c739333b8c4d65a0da7562c1effd99cf0376050666893ad9320ccb9873c574dc1f61940c5300f58f7b6471295825ce3cc53447", - "public_inputs_hex": "0x0ce1b376e5479afb4df722f877c26647f4e29e254ece539a333093ef73fac2e711cbf664e6e1ab436d8227ba889283c5b88c37545dfcc186a393533996b7bdc90000000000000000000000000000000077117e59dd1feeaa796907ee32a6005e00000000000000000000000000000000eef1d7464190461ca1a8dd367f48f2690da5935cc20672e95aa834ee487f402f7598651b4db414611358e24d7afc7f9e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000031ccd2641e1e9bba64242cefdf3aba968b556698db1967dd559f0b465375ed0e41a4f5a9fe72cf1cc5a1512597f770cebf0b305ebcf8253761101a5746bc6d44006de2cdb6761ecafb3a9875fe5f28694e7749e5733edecbe3b49a1a34903f4c2033ff88e898ca0a5be229028e8fbf251abf458ec5f8d1ad7f5d6dcfbbd2d51d02a27c7b79209d048001b37b0c550f30837dd7d8b86cc16371235304c6bc2f9dd2b8ccac82963e057a58b2e299874d166843e4b273356423f5a4e25ea487d0ca8000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - } -} diff --git a/circuits/benchmarks/results_secure_agg_small/report.md b/circuits/benchmarks/results_secure_agg_small/report.md deleted file mode 100644 index 7ec9c2d4ec..0000000000 --- a/circuits/benchmarks/results_secure_agg_small/report.md +++ /dev/null @@ -1,215 +0,0 @@ -# Interfold ZK Circuit Benchmarks - -**Generated:** 2026-05-27 18:28:14 UTC - -**Git Branch:** `params/dyn-conf` -**Git Commit:** `de480d631ab48f652be8ca1c8c136a80262c0c19` - -**Committee Size:** `H=5`, `N=5`, `T=2` - -## Run configuration - -Settings for this benchmark run (integration test + Nargo circuit benches on the same host). - -### Integration test (`test_trbfv_actor`) - -| Setting | Value | -| ----------------------------------------------------- | -------------------------------------------- | -| Benchmark mode | `secure` | -| BFV preset (artifacts) | `secure-8192` | -| BFV preset (enum) | `SecureThreshold8192` | -| λ (smudging / error) | 60 | -| Nodes spawned (builder) | 20 | -| Network model | `in_process_bus` | -| Testmode harness | true | -| `proof_aggregation_enabled` | true | -| `BENCHMARK_MULTITHREAD_JOBS` (max concurrent ZK jobs) | 13 | -| Rayon worker threads | 13 | -| CPU cores (host) | 14 | -| `dkg_fold_attestation_verifier` (EIP-712) | `0x7969c5eD335650692Bc04293B07F5BF2e7A673C0` | -| Verbose logging (`run_benchmarks.sh --verbose`) | true | - -### Hardware & software (Nargo / Barretenberg host) - -| | | -| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **CPU** | Apple M4 Pro | -| **CPU cores** | 14 | -| **RAM** | 48.00 GB | -| **OS** | Darwin | -| **Architecture** | arm64 | -| **Nargo** | nargo version = 1.0.0-beta.16 noirc version = 1.0.0-beta.16+2d46fca7203545cbbfb31a0d0328de6c10a8db95 (git version hash: 2d46fca7203545cbbfb31a0d0328de6c10a8db95, is dirty: false) | -| **Barretenberg** | 3.0.0-nightly.20260102 | - ---- - -## Audit status - -On-chain verify gas: **complete** (CRISP Π_user + Interfold Π_DKG / Π_dec replay). - ---- - -## Measurement methodology - -| Metric kind | Source | Meaning | Do **not** use for | -| -------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------- | -| **wall_clock** | `test_trbfv_actor` phase timers / HLC event span | End-to-end wait in the in-process test harness | Production WAN latency; per-node deployment cost | -| **isolated_nargo** | `benchmark_circuit.sh` per circuit | Single `bb prove` on oracle witness, one circuit at a time | Full protocol pipeline (different witness path) | -| **tracked_job_wall** | `MultithreadReport` per `ComputeRequest` | Wall time of each job on the shared Rayon pool (≤ `BENCHMARK_MULTITHREAD_JOBS` concurrent) | End-to-end time — **sums exceed wall clock** when jobs overlap | - -**Harness limits (integration):** all ciphernodes share one process and bus -(`network_model: in_process_bus`); sortition registers extra nodes; `testmode_*` enabled. Compare -runs only with the same `benchmark_mode`, proof-aggregation flag, `BENCHMARK_MULTITHREAD_JOBS`, -commit, and hardware. - ---- - -## Protocol Summary - -### Circuit Benchmarks (isolated Nargo + Barretenberg) - -Single-circuit `bb prove` on the benchmark oracle witness (not the integration actor pipeline). - -| Circuit | Constraints | Prove (s) | Verify (ms) | Proof (KB) | -| -------------------- | ----------- | --------- | ----------- | ---------- | -| C0 | 287764 | 1.42 | 25.93 | 15.88 | -| C1 | 2432074 | 9.16 | 25.45 | 15.88 | -| C2a | 2424942 | 9.17 | 24.93 | 15.88 | -| C2b | 3867595 | 10.90 | 24.73 | 15.88 | -| C3a | 3563512 | 10.76 | 25.30 | 15.88 | -| C3b | 3563512 | 10.76 | 25.30 | 15.88 | -| C4a | 2418310 | 9.14 | 25.53 | 15.88 | -| C4b | 2418310 | 9.14 | 25.53 | 15.88 | -| C5 | 4656265 | 18.41 | 25.33 | 15.88 | -| user_data_encryption | 1678200 | 5.59 | 25.44 | 15.88 | -| C6 | 3001847 | 10.18 | 25.04 | 15.88 | -| C7 | 136374 | 0.74 | 24.68 | 15.88 | - -### Artifacts - -| Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | -| -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 10.69 KB | 0.66 KB | 3136885 | 178428 | 3315313 | -| Π_user | 15.88 KB | 0.12 KB | 2972977 | 193300 | 3166277 | -| Π_dec | 10.69 KB | 3.56 KB | 3646908 | 188532 | 3835440 | - -### Role / Phase / Activity - -| Role | Phase | Activity | Metric | Duration | Proof size | Bandwidth | -| --------------- | ----- | ----------------------------------------- | -------------- | --------- | ---------- | --------- | -| Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 1669.47 s | 127.00 KB | 129.31 KB | -| Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 400.03 s | 10.69 KB | 11.34 KB | -| User | P3 | per user input | isolated_nargo | 10.75 s | 15.88 KB | 16.00 KB | -| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 10.18 s | 15.88 KB | 16.00 KB | -| Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 205.70 s | 10.69 KB | 14.25 KB | -| Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 64.51 s | 10.69 KB | 14.25 KB | - -_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **77.28 s** — not -comparable to P2 wall_clock row above._ - -## Integration test (`test_trbfv_actor`) - -### End-to-end phase timings (integration test) - -| Phase | Metric | Duration (s) | -| ------------------------------------------------------------------ | ------------ | ------------ | -| Starting trbfv actor test | `wall_clock` | 0.00 | -| Setup completed | `wall_clock` | 2.82 | -| Committee Setup Completed | `wall_clock` | 20.13 | -| Committee Finalization Complete | `wall_clock` | 0.00 | -| Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall) | `wall_clock` | 400.03 | -| ThresholdShares -> PublicKeyAggregated | `wall_clock` | 1669.47 | -| E3Request -> PublicKeyAggregated | `wall_clock` | 1670.00 | -| Application CT Gen | `wall_clock` | 0.30 | -| Running FHE Application | `wall_clock` | 0.00 | -| Aggregator P4: Aggregation pending -> PlaintextAggregated (wall) | `wall_clock` | 64.51 | -| Ciphertext published -> PlaintextAggregated | `wall_clock` | 205.70 | -| Entire Test | `wall_clock` | 1898.95 | - -### Multithread job timings (`tracked_job_wall`) - -| Name | Avg (s) | Runs | Total (s) | -| ----------------------------- | ------- | ---- | --------- | -| CalculateDecryptionKey | 0.06 | 5 | 0.30 | -| CalculateDecryptionShare | 0.16 | 5 | 0.80 | -| CalculateThresholdDecryption | 0.23 | 1 | 0.23 | -| GenEsiSss | 0.16 | 5 | 0.78 | -| GenPkShareAndSkSss | 0.31 | 5 | 1.57 | -| NodeDkgFold/c2ab_fold | 11.85 | 5 | 59.24 | -| NodeDkgFold/c3a_fold | 169.18 | 5 | 845.92 | -| NodeDkgFold/c3ab_fold | 12.82 | 5 | 64.09 | -| NodeDkgFold/c3b_fold | 151.49 | 5 | 757.46 | -| NodeDkgFold/c4ab_fold | 10.85 | 5 | 54.24 | -| NodeDkgFold/node_fold | 23.96 | 5 | 119.78 | -| ZkDecryptedSharesAggregation | 3.33 | 1 | 3.33 | -| ZkDecryptionAggregation | 60.96 | 1 | 60.96 | -| ZkDkgAggregation | 28.09 | 1 | 28.09 | -| ZkDkgShareDecryption | 61.25 | 10 | 612.53 | -| ZkNodeDkgFold | 380.15 | 5 | 1900.77 | -| ZkPkAggregation | 49.19 | 1 | 49.19 | -| ZkPkBfv | 5.47 | 5 | 27.33 | -| ZkPkGeneration | 67.43 | 5 | 337.16 | -| ZkShareComputation | 50.51 | 10 | 505.06 | -| ZkShareEncryption | 112.37 | 120 | 13484.59 | -| ZkThresholdShareDecryption | 133.84 | 5 | 669.21 | -| ZkVerifyShareDecryptionProofs | 0.29 | 5 | 1.45 | -| ZkVerifyShareProofs | 1.08 | 7 | 7.54 | - -Sum of tracked job wall time: **19591.61 s** — **not** end-to-end latency (jobs run in parallel up -to `BENCHMARK_MULTITHREAD_JOBS`). - -### NodeDkgFold sub-steps (`tracked_job_wall`, per fold prove) - -| Step | Avg (s) | Runs | Total (s) | -| --------- | ------- | ---- | --------- | -| c2ab_fold | 11.85 | 5 | 59.24 | -| c3a_fold | 169.18 | 5 | 845.92 | -| c3ab_fold | 12.82 | 5 | 64.09 | -| c3b_fold | 151.49 | 5 | 757.46 | -| c4ab_fold | 10.85 | 5 | 54.24 | -| node_fold | 23.96 | 5 | 119.78 | - -### Aggregation jobs (`tracked_job_wall`) - -| Operation | Avg (s) | Runs | Total (s) | -| ---------------------------- | ------- | ---- | --------- | -| ZkDecryptedSharesAggregation | 3.33 | 1 | 3.33 | -| ZkDecryptionAggregation | 60.96 | 1 | 60.96 | -| ZkDkgAggregation | 28.09 | 1 | 28.09 | -| ZkNodeDkgFold | 380.15 | 5 | 1900.77 | -| ZkPkAggregation | 49.19 | 1 | 49.19 | - -Sum of aggregation job tracked time: **2042.34 s** (parallel CPU work; not P1/P2 wall clock). - -### Folded on-chain artifacts (exported for Π_DKG / Π_dec gas) - -| Artifact | Proof (bytes) | Public inputs (bytes) | -| --------------------- | ------------- | --------------------- | -| dkg_aggregator | 10944 | 672 | -| decryption_aggregator | 10944 | 3648 | - -## Raw circuit benchmark JSON (Nargo) - -Source files for the **Circuit Benchmarks** table. Persist this directory with -`crisp_verify_gas.json` (and optional `integration_summary.json`) to regenerate the report without -re-running the integration test. - -| File | -| ----------------------------------------------------- | -| `config_default.json` | -| `dkg_e_sm_share_computation_default.json` | -| `dkg_pk_default.json` | -| `dkg_share_decryption_default.json` | -| `dkg_share_encryption_default.json` | -| `dkg_sk_share_computation_default.json` | -| `threshold_decrypted_shares_aggregation_default.json` | -| `threshold_pk_aggregation_default.json` | -| `threshold_pk_generation_default.json` | -| `threshold_share_decryption_default.json` | -| `threshold_user_data_encryption_ct0_default.json` | -| `threshold_user_data_encryption_ct1_default.json` | - -## Notes - -- All nodes are executed on the same machine in this benchmark run, so inter-node network latency is - effectively 0. diff --git a/circuits/benchmarks/results_secure_micro/crisp_verify_gas.json b/circuits/benchmarks/results_secure_micro/crisp_verify_gas.json index a53f27b389..f4e8611ffb 100644 --- a/circuits/benchmarks/results_secure_micro/crisp_verify_gas.json +++ b/circuits/benchmarks/results_secure_micro/crisp_verify_gas.json @@ -1,30 +1,30 @@ { "verify_gas": { - "dkg": 3136910, - "user": 2972965, - "dec": 3658366 + "dkg": 3222303, + "user": 2982226, + "dec": 3761654 }, "source": "folded_proof_export_plus_crisp_verify_test", "artifact_sizes_bytes": { "dkg": { - "proof": 10944, - "public_inputs": 672 + "proof": 10688, + "public_inputs": 1248 }, "dec": { - "proof": 10944, - "public_inputs": 3840 + "proof": 10688, + "public_inputs": 3936 } }, "calldata_gas": { "dkg": { - "proof": 169956, - "public_inputs": 8484, - "total": 178440 + "proof": 168632, + "public_inputs": 17664, + "total": 186296 }, "dec": { - "proof": 169872, - "public_inputs": 20784, - "total": 190656 + "proof": 168500, + "public_inputs": 21924, + "total": 190424 } }, "integration_summary": { @@ -47,56 +47,59 @@ "dkg_fold_attestation_verifier": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 13, "cores_available": 14 }, "operation_timings": [ - { "name": "CalculateDecryptionKey", "avg_seconds": 0.041923838, "runs": 9, "total_seconds": 0.377314543 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 0.169763022, "runs": 9, "total_seconds": 1.527867206 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 0.188830667, "runs": 1, "total_seconds": 0.188830667 }, - { "name": "GenEsiSss", "avg_seconds": 0.517591101, "runs": 9, "total_seconds": 4.658319917 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 0.519758712, "runs": 9, "total_seconds": 4.677828416 }, - { "name": "NodeDkgFold/c2ab_fold", "avg_seconds": 28.564699092, "runs": 9, "total_seconds": 257.082291836 }, - { "name": "NodeDkgFold/c3a_fold", "avg_seconds": 665.751029958, "runs": 9, "total_seconds": 5991.759269624 }, - { "name": "NodeDkgFold/c3ab_fold", "avg_seconds": 13.677920254, "runs": 9, "total_seconds": 123.101282292 }, - { "name": "NodeDkgFold/c3b_fold", "avg_seconds": 627.044043615, "runs": 9, "total_seconds": 5643.39639254 }, - { "name": "NodeDkgFold/c4ab_fold", "avg_seconds": 12.85089624, "runs": 9, "total_seconds": 115.658066167 }, - { "name": "NodeDkgFold/node_fold", "avg_seconds": 29.335869893, "runs": 9, "total_seconds": 264.022829042 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 5.934307209, "runs": 1, "total_seconds": 5.934307209 }, - { "name": "ZkDecryptionAggregation", "avg_seconds": 97.371421875, "runs": 1, "total_seconds": 97.371421875 }, - { "name": "ZkDkgAggregation", "avg_seconds": 5.380075417, "runs": 1, "total_seconds": 5.380075417 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 76.340271504, "runs": 18, "total_seconds": 1374.124887084 }, - { "name": "ZkNodeDkgFold", "avg_seconds": 904.325249439, "runs": 9, "total_seconds": 8138.927244957 }, - { "name": "ZkNodesFoldStep", "avg_seconds": 4.391163124, "runs": 5, "total_seconds": 21.955815624 }, - { "name": "ZkPkAggregation", "avg_seconds": 31.237663333, "runs": 1, "total_seconds": 31.237663333 }, - { "name": "ZkPkBfv", "avg_seconds": 9.470989805, "runs": 9, "total_seconds": 85.238908249 }, - { "name": "ZkPkGeneration", "avg_seconds": 87.110795134, "runs": 9, "total_seconds": 783.997156209 }, - { "name": "ZkShareComputation", "avg_seconds": 94.84146134, "runs": 18, "total_seconds": 1707.146304126 }, - { "name": "ZkShareEncryption", "avg_seconds": 105.638965935, "runs": 432, "total_seconds": 45636.033284039 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 187.468467925, "runs": 9, "total_seconds": 1687.216211332 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.583209972, "runs": 9, "total_seconds": 5.248889749 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 2.362957439, "runs": 11, "total_seconds": 25.992531833 } + { "name": "CalculateDecryptionKey", "avg_seconds": 0.042596574, "runs": 9, "total_seconds": 0.383369166 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 0.167140962, "runs": 9, "total_seconds": 1.504268665 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 0.202590375, "runs": 1, "total_seconds": 0.202590375 }, + { "name": "GenEsiSss", "avg_seconds": 16.803065226, "runs": 9, "total_seconds": 151.227587042 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 0.613419351, "runs": 9, "total_seconds": 5.520774166 }, + { "name": "NodeDkgFold/c2ab_chunk_fold", "avg_seconds": 25.016611324, "runs": 9, "total_seconds": 225.149501916 }, + { "name": "NodeDkgFold/c3a_fold", "avg_seconds": 610.816555162, "runs": 9, "total_seconds": 5497.348996459 }, + { "name": "NodeDkgFold/c3ab_fold", "avg_seconds": 11.11227249, "runs": 9, "total_seconds": 100.010452418 }, + { "name": "NodeDkgFold/c3b_fold", "avg_seconds": 574.685986412, "runs": 9, "total_seconds": 5172.173877709 }, + { "name": "NodeDkgFold/c4ab_fold", "avg_seconds": 9.864018078, "runs": 9, "total_seconds": 88.77616271 }, + { "name": "NodeDkgFold/node_fold", "avg_seconds": 22.548706759, "runs": 9, "total_seconds": 202.938360834 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 4.759221333, "runs": 1, "total_seconds": 4.759221333 }, + { "name": "ZkDecryptionAggregation", "avg_seconds": 81.313042375, "runs": 1, "total_seconds": 81.313042375 }, + { "name": "ZkDkgAggregation", "avg_seconds": 3.955149166, "runs": 1, "total_seconds": 3.955149166 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 56.45816899, "runs": 18, "total_seconds": 1016.24704183 }, + { "name": "ZkNodeDkgFold", "avg_seconds": 826.46726424, "runs": 9, "total_seconds": 7438.205378166 }, + { "name": "ZkNodesFoldStep", "avg_seconds": 6.416547058, "runs": 5, "total_seconds": 32.082735292 }, + { "name": "ZkPkAggregation", "avg_seconds": 37.316978208, "runs": 1, "total_seconds": 37.316978208 }, + { "name": "ZkPkBfv", "avg_seconds": 6.641598305, "runs": 9, "total_seconds": 59.774384751 }, + { "name": "ZkPkGeneration", "avg_seconds": 68.692219514, "runs": 9, "total_seconds": 618.229975626 }, + { "name": "ZkShareComputation", "avg_seconds": 374.55872068, "runs": 18, "total_seconds": 6742.05697225 }, + { "name": "ZkShareComputation/batches", "avg_seconds": 86.114743074, "runs": 18, "total_seconds": 1550.065375333 }, + { "name": "ZkShareComputation/chunks", "avg_seconds": 260.809911569, "runs": 18, "total_seconds": 4694.578408251 }, + { "name": "ZkShareComputation/finalize", "avg_seconds": 26.735104601, "runs": 18, "total_seconds": 481.231882832 }, + { "name": "ZkShareEncryption", "avg_seconds": 94.545070392, "runs": 432, "total_seconds": 40843.470409586 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 166.007461671, "runs": 9, "total_seconds": 1494.067155042 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.137594467, "runs": 9, "total_seconds": 1.238350209 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.788292632, "runs": 11, "total_seconds": 8.671218958 } ], - "operation_timings_total_seconds": 72012.254993286, + "operation_timings_total_seconds": 76552.499620668, "operation_timings_metric": "tracked_job_wall", "phase_timings": [ { "label": "Starting trbfv actor test", "seconds": 0e-9, "metric": "wall_clock" }, - { "label": "Setup completed", "seconds": 2.393101875, "metric": "wall_clock" }, - { "label": "Committee Setup Completed", "seconds": 16.092655959, "metric": "wall_clock" }, - { "label": "Committee Finalization Complete", "seconds": 0.001054875, "metric": "wall_clock" }, - { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 406.98499, "metric": "wall_clock" }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 5251.559821667, "metric": "wall_clock" }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 5252.065132542, "metric": "wall_clock" }, - { "label": "Application CT Gen", "seconds": 0.288574125, "metric": "wall_clock" }, - { "label": "Running FHE Application", "seconds": 0.001384625, "metric": "wall_clock" }, - { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 103.520856, "metric": "wall_clock" }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 334.1983295, "metric": "wall_clock" }, - { "label": "Entire Test", "seconds": 5605.042426708, "metric": "wall_clock" } + { "label": "Setup completed", "seconds": 1.977860041, "metric": "wall_clock" }, + { "label": "Committee Setup Completed", "seconds": 16.112527834, "metric": "wall_clock" }, + { "label": "Committee Finalization Complete", "seconds": 0.001757417, "metric": "wall_clock" }, + { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 408.346358, "metric": "wall_clock" }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 5166.649068375, "metric": "wall_clock" }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 5167.154574709, "metric": "wall_clock" }, + { "label": "Application CT Gen", "seconds": 0.285191208, "metric": "wall_clock" }, + { "label": "Running FHE Application", "seconds": 0.001317041, "metric": "wall_clock" }, + { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 86.286508, "metric": "wall_clock" }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 293.920426667, "metric": "wall_clock" }, + { "label": "Entire Test", "seconds": 5479.456071958, "metric": "wall_clock" } ], "folded_artifacts": { "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000954c24ebda205a1890000000000000000000000000000000000000000000000047891ca0a612eea510000000000000000000000000000000000000000000000032cd6988555d9375700000000000000000000000000000000000000000000000000020a71945a61890000000000000000000000000000000000000000000000013f378b0dc552f37000000000000000000000000000000000000000000000000578d3973b8a5ff2e600000000000000000000000000000000000000000000000e6230cb8ef3f2874b00000000000000000000000000000000000000000000000000005fb56392be3b0000000000000000000000000000000000000000000000037d2ab4998b6b2f2b00000000000000000000000000000000000000000000000753c7e1e41947b7e100000000000000000000000000000000000000000000000c913f223f3cc93109000000000000000000000000000000000000000000000000000015ad2d9cb057000000000000000000000000000000000000000000000009bf71e3c026d6750600000000000000000000000000000000000000000000000a7b65e0bdbb03a37a00000000000000000000000000000000000000000000000bcfd70795f55b571d0000000000000000000000000000000000000000000000000000fd6d2df268130479d738c4ee38577d65b445b420ec265f9c9d743e868525978879a50fb2676526d4c60047e4b4b5b366ab2903818a6e23f01a9a19dcf15005c869f763f0d3a718603f91d677484afd67bdf49862b7a0d8a7885eb94dbf0fdce2a0f2e8e3fea707a3d0275aef9b17f71236978cb2d218add2a58fe2bb5d47f0c77792d4b03de917df3288ea86583d86a6c706ccd356ff13ac09352c7fdb9ee1b7eb870144a5a0032279b0f26b994415350699f0584f685cdcc4fa9c6012523e64b359b3a0a315180949863db19097494da78086af7ab28a4f30051af93bd8b457341fa4e3a4b82c51f9ca9639322c8b2cc935c697a662cfd7e1bdc24b820869db5c02d4224bba03b871237ad99247a84e3f4ae2aee3397f2ac4b1e67d13d9992d2355b35d473e1caf903eb3681d0612cf7b84fe084821333d5a7a5ede332609f37c05f346a5972c7b427180c02d447856d1a00e12b702cc7808b995b49ae9d0886881532077771470d6d7b36709f696e70fd45260daad0dd7c05f60714838cac65febcf5920b40afaae846ac976914cd712ea5525e57ab68c7fb495ca0b6565a2461aa6987ff00d6f129c8f0052cc9f3593a4e650c2aaad3f7438876d40ca5a2f2744e3d6a2b121cabbd1a2de7dd994ecf1515a7542cddc8021886e53e71214c4c49fe0019b931df5198d8f3c27deb3cb9278452f7fd8be2bd3ab23e32357aaab0dcc639d43a50e87245b3eb0b4d17c3d94e4489c1116440347973ac5d1b3ffdbdb5bda4bd4a317115e9b5ddaced77884f0873a639ea45c628ca552fdd96aa39716fbe01ff66e29d5f35b4dfe51961e9cf22346931a9a9fb30c45c41d94c687ccfd9c19163b1e20f390f8e062e1c738b0a3a4bba05672aa3048f39921e74b5f07d1d7185e2dd321115ac66fa437573482a2818e18ef87eee118edc795d190b7633af3b407a46a1e34a9856ce1037c5bebafea03332cc8eb47d8a50ad8130bb7fd81013c311297244810ea5dc9183c9d13c47a8e666863d80c64c05baa20c9c227903425e2c9e52988b98d1c607835da46b657ab73038f9fc26deb30d37c2a6de109bf1b03140519d9c12414a86a64ca1ddb999e61db596798fa182d101a32a40344bbe759dc070fb4c4faf6cad2d5f53df2abcbdf4485058be55470c36a2cd6ebaefeeebef439001882b6f4b8d3a0a9b74dc3ade61be5f3aebd4230cd36e7d7b6a5e9374b03b411c376a48ffa289b5cd0382fb39c1183f2c227c5ca891ff7ecf504b566914e030a1db94582c46e2f1ff6ff257c3e9416096b49a61fa2c435cb5248abaa6b758b0457295b1f05f279ed93cd004f1b5c814f5930f61d95d0164061c4310d3c08681ba02bc39592fb7636ffc11815de6e56fe6d757674658670d7e92f80acd7681e212be07766aac444e30079100453253785ed9f3bc2e264c0705bcf655f4a18dd2224af0f6d6cf1ae1a9950e751a011863db40254bf6a2ef87d564a245d51e5f11c2542d9e1797e8faeb7d4ad847f94985829650ad4927c98f2e5cbfe53c50f1120b905eb3f2a192d3ddbbdad5be7bf6e139bdd9eba0b72f063103ddcf3169daa28f9993f0052a2714b0108368bb1bb68b3b69bb7c43711e3ede8fec1fbf54e440bdf8f48b3c5ab0d5816ec621766b5bad522677ed2dcd4e30b34b0f5e8bf56b006d4b03918e8137a4b84714d55cd3a9f9157c07609d443a9b3a10447e76c4cf31831a1e30b388e8086e8aeb6251fc2eaca53ffb56b036b3ec9c675ed816d877911f7396949a5a45782d14ae2f8fb0d4496cb76be92f3123c4494fcb88bfa88782cd0b15b923c1063ce41d0be55059cca8900361437b919b821beb588f752c49e1a3306d750f05ea5eb43fd147d7754169f9cbb137cbe051f26964070cb8248c1152ee00b158348359946dd6a052b56b812cde74bdcdfb0d8706bba44093ea007225560f11fae351928424dab17684a8146db2a1870dd9e0766422fc8dee7c930181fd2a5f180a80981e004f48600a546163bc214e0dab04eccd47acde05e2f08265430e92984148699b8ad94d8b237249e845e61ecca660b38a9ecbd28b4a57f13f9063635b386d27e21a6890bdf58841ce614dd0b019ee4a95c35ed5e5841261823380c9dd2307bfbfca69b30afc09ef68afd3bc4bb6826fc673ede3921013f21fef391da29cfc93e74b4eda833bd820e760a9dcf30baf8348ee2efe2c801b8233826304bff681879221e2762b80640e5e70b065d879d4892aacb8040e3b94a0fe3fc8764545d4cd3a06ca5eb919e773b8ccf6e77544b77b08b5c2c3e6c52c3215ff7fdd83fbcdce4d1c48035f505f9918ff2c91d74ff3dac351affc1e09cc81dd589e2cf4edc0a0a30c660bc592e296037dfe165f9c9ef958d9510ed93931206760b3f2dbfac735bbcd4fea1596e6ece49bb58eca25fcaf2193db71c1bfbc407dd9482cc4e4353dcc6af030062ec7306a92e143da95f847998e0baaf690eed0cd3c79f560db4ebfbde0c969eaf9990785b8f105742a8ef523fea869992e1790b932637305e08eea241cb84740016d04d1181e1ee258e0dceef1373f4ee9b492692c9dd0c0aca9f9c1169ebd6cc3ea15e1dcdf9afaf00d9ef34da272beb108f1b6934a6e6cd0162853152259ea61f7152d01006ee9d4cdb7f39c4f468095375295e5d021cfc1cb744b26cb49fbe2de5889e996502439a7918a7853ab85986872be94b747ea2ff3b4fe25c9742fae8556c4d881b339205fb8d92e8ee31873d3303a87e48f886ad41a6ad621508757282b3490e8d8fe2da22844614359cfee3c623eb476c158647f316928f903ad86c67e6d04b905a788caef1a4066c7a3e36ac24db37b6d796c23d77e7a4a80e7fbe5312e3290b80a3097a65fc13f955b2edef0b72616a71073e5ab28ae16343cac84591281313d71eb8b676d324b4cf3bf7e02a5454fc081168e6070b9ef6683990545b34069c5bfd3079efff96f4d8503a6c25ccb480b918e97636b68e2152daafd94bbfded22e615f3f0fb3ab18ec022d832cb930987ec9bd0c8d90705b362dc20123a6edf2267359b7692061ed7390e7722f714ee99dbd02816dec267537222a4efe2260684077840542ad5608d226c458214961234d6b9df88ac960bb145da06ee967330c2f941441dde2860de102b56003dc5dd86bc2691a6136f98dffbd6a6bde83059e71dad335abfd47ee7f265715242692b67dd1ca15da007b880531fee6d551fbf63978d744445e7c047001d27e2c5a20ffe2e0412e14eabd0cac98df969d10364918cfb0c14b09db809991c80710dd08b4dca4bb4752983e00ac7d80929ba4261dc45433f657d06c79d8d3411f1e33e768ffbe9f120620405857b4d2009236536d8ba4f960c30633b60f9807b2255cf915b29b6d204ad6bda65eb98a76584d048729a2aa6950bd5497f31834812f9f03764a12918634d150dadfef911bdb6b9345b654bf5077c65a37df70cb2409c682dbdc44f157e14e9a538e1724609bcdf9f19223a2d2ad5441a3249d5cad021befff0c472ff884d903a8e263c0d6ec25e771df369378b77e2c7c49bd9ed32eac8025ed81eec539e40a29167412ce07768f524ab397345d6b583d7491181b2061fec9445e09787f5ba53674471a06613034d8e3e977c6aaf1921300143bb6263c15dbfebe951fc8fe5d5fee441e1a61259e24b9ba8539738eae9dca67a93226aad08653953fa4671e46be63098b3a7af0569ebcb58754c1c2ce89291112c6238de55e26acb61dd66c6e141d63045cc092931d884495b05f01742e6d0da04708b1a336f53b269a0ed09920fb119177d4a57e46865d739292e02d1963b72b222140070855347cef1811823f94e295526e5debb4b8a9b8e6ff071b0c3da6f3c50a82030a7b71a6188c761bfae5555dd8fcc8052494099fedc0dac2e2149621a618ee11b2de3283ff999e1e7b75f0dc41c3cab1f82982fd25d4c5219285335e3a08b5d32f53149e2c627d124203d708f4bf689c0bfd205c23bd7287864f43fc5505f5db8722397932ede80faa4502abef2a335d7dd8a6bee0aee2bd0935bef824027e22058f7d0231aaac32bd283c1d21c243284efa116c12cd62f6996d9745090af0fd01bc81e5db7a7a72a02e9aa349f5c3680be7573a9592ac24298c99730b235d74057eb525fa9db4c88c386fb7c0b46f9fdcfe7544e163b8d878bf5637d02513a462b231640c1d7da3924460347c7de8413addc5d90e28213b0861f0e1312ad87102d3ced97628b7a51796d9202d6931f53b7e97f813898c3904c6c6ed792557baf1872893751a1199da549e8d40e6b767b2e617de5efc68ed43f80bc3d31893ed70d476c3b224725c08b418cea498cd805ffdaa309aab6e7fff9be15a6e24be8f3fe1edc8135ad3cfb453f91f8f3b28f00f5e1cdb413c8011b887c467141a4a9c8a75d9cca761c00badd5ef5f2d9c972c9653444c9fb39a9b8c153a38eb10002a870704163cda0c9f3c8794adabf8829b3cfe43c76db4b3e070204c19c300406d3c59112f4fd8f6d49864b41d3bdcda00b91ae3c38cf4715554dc3ad9cf2c05da85f79b5e1467c590cf088a8374f80ecb58ad120b2e010f8e04dc890cc42aa372e47c3af4fbf8b47f9bd6672bcfbabf3d48bcb37128c6e4960c57f23c1c25b7dd1f37782125d0d2f68d963f91dc2821dc5440ab5c8684df689bd36106001a9872c04f1e304a64f1ea42cb4d890c92d8631d3898e7a35d561498f8db69282264fe269eb5ec83b76cb73f81bb42821205e36e90b8b0e3550a48a47dad2042088cbc58567746b617df4891ceea45deb36c71b044e6999ccb3bacd0d7bffce80f839b549c474af9bd63e5d94fb55ce9334faca560233ca30f7cfb7e3500989805b4d31e5225e33ea6fcbc9fb9568707144034a248380cd18a4fe71161659fde2a6677fbcce9e7d655fc95c69900ccf6b85bc59c803cd83c9d617c9fe7cf6206051e944b53d5ac4b5f6b8d149c84d3cd97eb7afd61242757bd95b537da02cd5624e1041188c6d776ec38b7002128126520a7b090be289ed5f9394b615638365d1547bcaf555d5e9b44a663e9a490fd058c2b86c578fcce7b52636197d04831ec0e99ea9b8691a14d30f294c1363cba67e813dd3d39b232d0fac06be2e09c782a23f133762b99d99eb458e24bacda09221812d0625fd6ccf763487058ee3d219f16ec60eb16f4b582d0af287cbf885c5de32d182ae8e433348e49929438fc1b0922feb1a9eb418e1493bdee91f15ff98821d95660b3f00d1f69cad59d6bb045a92b942fed2a7ac1711f1b5a340bb79ed772eeeec1ea605a0ec7763682397a146f0546a02fb8944e603d538b10cade7b1fcc99e4dcb4cb49890d9c73aac43eb1b317844df125648d183828baeef070bcaa0c9ae252c6b0088c8180bfe1944139d50825a6afb3f84f161505c8148211127101eaff10682410887d14fa2a6389f5621357679db21bff9216fffb04e994baacf43bbfca9e792ab32a894ace72cb62870e96989fa31e08cfa5f848eb137862f0ed4704e6320516cea852551328ddc36903642426fd362d4afdce884c67d22a312346c4e2d0ec078b8b41c6884abeea122439a1b8174258e1dcf3cb3bfcc773258204c7ee55f77ca6ae3d17a16cb603fe01ec3c26f6dfc003fd1eeadd595b7e46d10fd9552bca5c465f835e833cd8cd9119f1f5e43f0e7f7205f1667c4d7fb0670c284fd2929360ff5599d098ebc5f8661534d76ddaabbe567c74c0f4ce1d1ae50ba2bfc4295d610ecb0d82a6261a7f601ec8512a5887e008af2e49eeb2ea1948d7def7dcfe123bc65d45b6792ac776b30105aa64b0a6564f9a66f1ddddc84a2c67503c34c9fdbe10833db3ef9fde04b12295e8d7cb26d10e81420ee58da3e655fbf0716af6fdb599a8711486cc59dade18b30575632002d0d21b08ce2cc8a54a215c163f3c06030064ce164320252a30038ac91a9f9f6f328420a1ad9ac21964974470784e5e302d4027f3fbb047bea425d60a13d3982f35ae8c86577f1973318a04e85f6d202695b9c5fa41c35b5b852ef84c6eabdb2de8fba547a0b65c05371103030bca01e04bd2306684ba47b76017b1141fcaa56b2206f78226fbb76b0533a7ce50aa0a4b7371372b3570b09cd800d8850bc681af074c1035450dbfdf530331d46c582056e37b8de5244935908a26898b967ddbfcd28228687c7e84fa51b15c096efe9da672f2593589a82e3e14165839aad3a2b0d866257b6a8c9fd9407653a5edf16e6cda91435c4448b74a810b0bb5a263f6a644b58bd759d466e4d7ff683b1cac40f53b529605baac0f1f942c5cc7cc12d073eb337f3b16d1114d1588f979ff8b0b8a9fa7d36c31f7e68a770686258566d7107d246ac0852ba207e065dc52b50d4a6e8956d1f6f5ab68ca7e04d806cebf24c27a061a0e70337fa2eff7263a4aa71c78dc47ac83c0c81e35bc2affc894bda4d88ce418e8234ca460fea23c17f43675e86d654e6cc89a318bca0aabbe885732619a129a42cafeace52ad24ea837d1b002cad6712569457181220603d7ee974e9716aef6df02646394d0e9dd1e1746bb06590d34edbdbcc921c3010311bef8312cc8095a1f4ed732d83cf6039432968804b89c0952a449263a822da1f05a1a42835b7d24f2e9f2b59e167e9ed8d7e14f4051e9880eff09c6792825be65ea3e21d8d02a24758e60f6c7e6565cb823c867114e915db0c7f4cc47b612a7c3019467cd469a422ca3d7587217b52c0e5653749f9affcf36752131a10e07c9844fb8450ff657aa8eaf021bcce295748e121851660e0dc4909a2719af5c28eb96158dae3557d186b9743dadf29d9f11f5d0dcfb4dede3e2ab2739679a0916ef6888b12b324634fb62e4c91ff6d99a61293a7841c20854dd20d6ceb2b85d01f905854e23020044ea2b96c48ff0559706fdf8a1a9a513d5df18ee4e556328026e9e34ab8a4ea4f365688afd3c346599782ad10e1de844aaf27c873fd7c5c626721ef8b7ffd2dba851c17bd7dfeff96d141b5d1e2058eb2f17f313f9ae321c1c7831ffd39c03a5d6027505796fb09cf775ff79f24e8ad288815bce3ed9aac72df73050ccbcd65699633d4fb70fb67217fdda5b617bd4ed7276a22ce3e2eb6a1935fe82954a6a5590f8cf1c91500da83487b87e30ccecab155ae62145bdff181a31844bab00b615677c78ca3cfe697de5b49c3eb357f52ff3a7f2ec7915128b2d237ccefa11241cc47a81b9a8d2541c8fe2d52e0cf5d135ea1b4b1b8a9d15cd149914a3da61606e033e9d0e4a7b63b2cb13cb9ea370f63e2414d3418a5c7d991b4633b0149d8f03c9c367d03cfec76c5109d53f21596e32da280cd5cf45776a0b87b21b258bbd20d69fd51c1a9558b1dc2138ce33b072a8049a8f2638d28c150f5fb9090cd0e2d31c9812d5480d0009df8454e1417d6f46dbd05ba4a8ad6ca618bdb22a6cc8ab63943c13b2ea5d193b20e6b08227a589122dbe3a73f3ef1cc32fff616e1c095353330c96cc982404527938bf88505d3657ebfc50977be0020b03541e480230514809974e9e69fddb437d174359ce3223a21e390b97878a8fb2201c227f3b6fa370bd3a16acfb7e6b0894bbc720511cad2f1b2ddd28f29f83eb026e011a09b10faa9329ba60f4f1593fd15c3b698d255ce4faf6453bad13cd760de3427a971293a2d5dff7360740304f5474deffc50a4604840053da7652b9b02d6b0e28d6cb789ba636d32f86267196d2614812552594e7dcb0d55c7f3c065f19bfc2883d105f9f2750943abab64249cc002750c21f7f17139b88a18118d396265ab2edaa8e1d15dc3fae0eeab9526a3656d667066afd2513be00e48a1f19810bc20f53fdbe2daf96da6ffa1a4cfd89bac34441a116580f9dc70b1d90b6343722c892b4b34410f5d72c564c7801d46f78d3f810140089ca81e696f9c168de1b09a60966a24563f814100aeedcf627f0420d292ba9dd663ac76f35d77493972809de34afa8fc2cbec1507bfd4d8214150fb7bc4c776d270a7ac59e09a2ba0442132e7feac7eb2f0885f4232c77ced69d98bf587b9e25a03b51fff2032e1623b32a92d8fd82069c99cc1a1eadf51bce00bcec8f13ae581f506ab4cab1a949a4162bc9a368764de7a6e6374b3958f61b422db4c8ab79b3aabf61318650260e09d5197faf7ac774048996e153e153285fe70e22c0ef8de78bcd7f82dce646766eeb0759a9e4e6695edded54aec59e271655a908a67e9a978a6cc7117b7c838f884e1ab20ade1ade3220a5290330da0a30ac5fd6b27c0a20fbdb6a86831467607f421a740222ab535e73c0bbf14d4d5dc94398e7dc07e3a68461621ec44f22a1dffc04f3eeba280588b6bc54d4cfa12ddf74f0eb41d28d71b7e1f40154b650de01f40002f96e89c482024cd807c4dce65f95154862969cc7af25f3e593d3ff75668d23dc884e8873c8868de377ac148524fea10091dc26e256c0fae4a865669cc23b109c1435222a257e8f81d0138390d41fd15e24554484d2bda75ba68fd9ebd5fb0e0a6eb696c1d53f1c2c6d1876ecc4db74a7468c5f1101e616e5f9601200ada7229859cab751c62d3150c969d594ebc26a09aa2b452bab04cc0926ffcff77e0f2728a6df1187a01c19ca1246a6e3fa46fc745f1ccb321ab3510783dd3f9164ae029c8c0374acf4a62099c0cb553840f77ac7e8892630a753baf8b659d33246ee13910d2643c85c3a652e98e9074d305614b27a4bfed9b19b58912c068f131f9c27db076544b0f37fd76a9037f5279f369dcbd7e5ea53fa5ea1b4cebe86d84da003da622a4f51e5b780b50502de2bebb6d08e5783da1e99ea0516eeda99b29b58172d82a6ee6bc082520e3f65fe65153d3b116b6bd4e5a516a973fd01e45013f806b49e863c12caf6dc51bf201335d03da3c7baec7132824a9be95ecacf49bee405c2ec72bbd0674d967d5e247aa1048cd42d3c93abf4e94d99be54b3a686dc65108ad51a31ec40a8feba4f1643319baf8bc0371f0286cc4b8182b1b8ebb5676c07494ecb86ef09692147160fe4453ee58a4fd06fd13a3ea30f0df887e31f61f011c9b2e48b84e9780898bc2cfd18c8dddf03fe98451d43f8785d851a3f16d1101466576200d8b8d94e0111eba9d87f04e429f26bd24562269463991ef5c4f90a1c396159f9e1367471f9c652b7ba1f85a8f39f14676e57f27bc71ba0ee37de16119f8d03f603ee5bcc29aa33ed04c99ae110fc380985e9f78f14034824f7d60d09caa5b032fd1365d586d7d0ef008bf007107ba2851865fe1e85a38a3623b7f60c7944bd39da759d1a9c0904258bbb43668c05073f1c47797c64ba7476105c5a3032e52bd90dca1f89445a24e680b21946b78714d905c686757da1f7bb325f930ead078ca2b4da812d06ae76f795250a9614c84a4de7ebab91698a1d932e9c3914d19e4fe62fbb06dc685ff96f1ee2ab685a1b2dd11bc574814e55bbbb8b4bf118e9abd351095e04b308b0a080b423a0290b2c240b68813c56ec447be417d1660b9ba2c932ec8ae62cbe94a966dc1d97905e336e2fc104e71963aceb2434a0f00f213d112e2fb79dcf5bf9109a5a89171bc77e74f677c1bd91adaadd7bcee37e08085211f4f3830aee88958efb8b9abc5d048c9060e19bab14c71a134359961d047f23e809c640d4bc2f8c1013073ef2bb7d47092229a058d9a8a0e368e438051920b215b9536191b75c45babf5539c2013a15dc0168d950289aac025398df260b5e8266859c2ccf7734b2abc57cab97559e1690e3b46c53efbd701733356fcc024d0527b5d1868e60f847d2850ad78d1b0e14a6c8d0fe88b8fac37a22ecdbc9249f53b74629a75beb4065e8c37d7d6f4642ca9fe336fa422498b32c8bc4f7512a2be4b259a1b59cdecf3130eefee0e2e5df6b2c9bdbf668b75aabab1576d53303db0d44ec971fe3d1321a9350d8fa6896a2cc2e1098698da58eb3a7a35eb4000e4853fa959ba339a30c0ef0c013234b5a28a5b452c624c8f8fb8ea2b42968eb19af260b94cd35e4261a065151badeb645494e07d6861cb5525808b37b42ffef0e2b0adc409b0b654134cff58049bc938132ec45d9ef3a29c44364310129aae112d8301156c8ef325358488ef5d3efbb02b572deba3a29af4933865fd6c1bc490708844e738a7826a8ba820c8951fbcc346c05d6f2272d758aa1492512d270ef0254b7bcbc7663ac5f7df79f7aa876dda879d8641724107d51745986adf8fa0d1c7be3449daa3316310805fc39038801b6bc2839218b89d9599bd4a97059cfb71af65732d4ad5f7f085bc5fabf031d31fea3654a4d01a951e7f5b01886c42e3d21b27b018697ecc90219da4e2862891264fa808c13cd091d0480b09f49c7af9701d675d72e6bb13a542bc0d8778deeddb084d33610d8487f9560985cf4a8de49254372d2f7b5b359e27531954d74588044d27b0699fab23a1f402d1b7fdbc24826ecc129e4234ba397dda35d7bb9ea7a843ed5aa132e4800ac530b5d453734ce0be7f134c2b0b0082a14407e58bf0b9268d6c8f71a4e6e32027dd544bdef3c96160d0fb52d0b455efe22dd5d80e27444882381d80c00381306846ffc5f3061a521b205b976fe30137a433bf9b17472ae316a82e9a56764b1815775de43e5dc3b2a589baa1dff943708db4e6699cbc5fb855c6d65d3797722a085c536b5c3c5a42e386bef06c7b38ea71ab55d213dfb86b5808e4b9567e35aab3464d56470873903ae9e4fa7fb9b93bca027fb8b7f1342d177584ab04bab2f054007f74c13f8c018e6362f609c16447db1630a9f2d4f7adb816214579991be4bd2005a868c888f28244c9734714284120aedc3dbc9ab237f85e2d0831d2d670893351e5c57ae77086d2e0e84ba6b90355a564c2bd7f2975fd8a1e83ccd2f9e02bec6b8c37e598823af1676dbb3390568d8b9720dd95fa5fee9befbae00270e23a91a87e544e39d0678ce6640c5a6158db874fc7999eb07693828e7f6bc17714e56692cb25fea7b2e2dabb545915cdb9dfff5d5b6db877f8c6b431946ef463eff26e15e8f31854d11d3a836eeb99da348301c8f9c050e520f4cb05a84d178678f3712e05b864ea31a516435a747f2565bccdcf0b67daab6049aa9865df90ff6daf8d0aefa01dd5606bfa36a679b0e172066572f74741ffe72fcacfcfedc85bfb61595b539cc6e890251fb014b1e88e762f64420d34542bd3c66de5ee997e1783de8a9b527e0320f07c6e0071967dc2e82aefb43ac97abe08d9384e0f6bd8f70435d65d0804a480a24ef56af6c2a94db8af52b40844d3b5216787e134ac6b9aa1957d9e5f4e0aa122e15140bd00dfc578b008ff196a929dc8699083b3104307d78979e927d164e182a2dac33cc1d55fa5f02fd3bfc82f608a68eeddf64745a9e7ebcc8a733412c711297d9219cfe2c3c4a281566293b6fd5212e051f11c2a414a35e72fbf9b427542543ac075358882948274f15d6301f1b1c86627c77a116d4d9c06caafa3a8727038e8041967a62abc6c4b07e8b52868234e840bdfbc588883cf7963a0eb4b447186c802ada3390a6516ffdb9969ee1ef8f0f922ffeb2a32225adf999709e56fd10734cca02bc98215c779cc2c1cbd097fc18d335fcf4e1c133b1e7aeaa345e7323cc0c97adb0df8d596ae452c79ec2516777d0f5fa170079eec45a62676117ce0b87d58ac85b3bbe61ffaa2dcf252cd9edd9c7d5eb7695e3302818734d295e791a8198161c5422b10688131459328a435a3935768e805b534fc41fe31b6f882e0ed1b3cd7c1cb61c5c7125e291e96297acf09f44375e434da29cb00024729fed15747faf7337d199a12148eb0aceb829e87ef46dd5da65f635b788eddb52f975041cfdb370f2fa6c1d7185325050e76e07f1b7ca7bc0bc3457346e39467540590c92e4a82e8da7e7574481a096f68ad1a1e7c7218ff2f63fd2290d5239feb49e2b3522fe944280b6dc2ef1af9e137a458d956398c7b668dd3cdd6e46dfd05077081d99ff8047a33d91fd12d5260251c55e116c495c623cb5becc11cf1f6bce340a2a4015ab47366084fe7afa4f6ed6afde94640034686b9ee7910443a840dd781825d575f1365f2c47f660c1a09bfcb48f94342708904696ae07027691713af3150866bdd1d28b613bb3fba63052b0fd65ff1cde3a26f85d3f845891c89089552d8fdb9b2042ca080d659f7ff593d9f83ca330624afa999edbfff2c4050631251809b6200b186582198ce248618be9533a3760145f26acfbc7f01f4b22a8bc8111bc30770b490db35c289a89bb0a57bd0ef49623c5f7016aedbb299dbc8aadbe10577093fa4c2c12a107687154203d73ede9041a4e883271481da373d537976419dba316d7cd066a23790e24aa5796a6217d8afc2b27c1cb5521f2896f0f1c82276cb6e2fd185d9d11b6ec8c4b7957787d64dd7fd64e03f01bd8b968590137610fc26595c927377cb7e0473f53a2d8b67458a8e1dab91a38c40e67bb799a95372fc8208b0fcb17b344902b85e74d9e657c4a5ee2815dcc59152baac7ac152d7a0de4a6b22a20fe4b35513b95d694ef98bbda78a01de6075c642351f30bf246621dc199d582026984759c97b15393a0acc5d6d3b04667fd9e5ac67743afddf5aa07ef5e51331df9e4fc770e98199323b08e9c1e523bfad993ad0e4ebf674bb476054e8dcc4f4ca9de4b892da265c18c375bfad551d7b48e9babd153c9f4da59fd0e889e9d0809c27438d70d6dbb442021e70bc4523b1f8fa4cf4f1c83cf41a87b247e24a2efcc3eed818878d18d5af4ff7a406a34b1e267abc9eb73bbad288704039946c740d20368f36b51b905316ec8631c42154ef93719115297b35f5553f8090bea916e568e1abe9ff9d67c00fc50d30089baab588674beebd374ec41e4f52892e8c129dabf6c4452513b2294fbc8e3f01bffd128cd33bde349e7efe92a9b2acfa186941bc9f0f28746a2daa6c1a29f014254a592d0f7b79b4e7fd2c037752f9b7b5f0b6b6b22805bd517ba131ffeffb08adec919bc56f4e842bf99d209ae25b1578a8507e7427f947da702fb1dc013eff4c276eea7e87e3b0fa62ea8c0050bba25b30db28558b4af4f523b2b2435b563fec70d0e7d592a4cdc685656df030ca1ee2dd95a75abbf407f2172194663f318f678a83eaaa08e42c5aff919aee101580a7cd016fa1f2604e196f1d073c2029857904e78cb627a77b188364cc8531a9255179a2f86972d5adaa0d57c6cb3d6324dba2fb08fff2079286041e396ae2dbae93ad2de194c7cdaab18ca1e86b9b4da00c92b839adc172b3a7e2958778d21bd1ae2d19865b9de3c6f621c281424ea494bb0e3116978ef9bfac44077e21123146399626189c1adde7441069e728a64fed893ebf03b9ba7566a82114937e31a14caa89c9e1408d23f20af3e7b60f4187404918ed4347623a032223072ecfd0be0093a0b2244085e38311914e2c4b51b8e4fb1e534e5aaed3c2d23e09d09b519711e0ab303c58be6ae3fceab557c6a48f490a7eda2e99dc4e71fc3d035ed61226fd2b6e1c607254ac45148b05fe1f6b4927c284fdefc4dd5d011d56dbc621d0c765f5510db7aed1c290f3ecc956c0b280d20b9736bce299c89966bf31017fb1075a74a5a55b21b468658e5712e2c4eaaaa0c3c5f28211a121798f8efed38411c3dd26d5d56552f13e56ec646818978f9f81ce61d9cce7847742d9cb0f03c0605a5076b1454099c2a90d975eb63093ee11b0972f1dd12a98d7b6f9d7542df3318329042264375893c8952c49a963c6613aa0a2a7184919c667aaeb4ca3dbe7a16cbcf934e32bbdaa1ccf5f3181decebad10f51becff7d86a213b83c5dd3e952294cec8bb5bb87cf11d7a47da6dfe755cd4d4f775bae27edf884edf868a112190b09a50305c6a6ff212d16c1cc3da81c60a84082471b28114a27bc911ee054772b3ece09c17b74a236f0bfcea3b28067b800037e9ee4926d4e4b106ceb740e3b240274d2a2901f4efa32332051aef11a9fd263eee1b84def9a79133f299a75570ae17d67567962eeb27384f796c3fcd35eff160a32a15526beb0f53c7efb8ed119a9732149e2db128ce2919a5ff7a4d6ac9da5e8680bd8dc4f02d75d280bcce507ba19d19b518d756604fed9656b50ed5c312eb299d5fab45c24b93813bc98b1127581dc7e6ca399b27434360a547b61a7b4d6ee4f500a42a95ae69730a1978b27da01e89e23f06246fec4fdd2f84686dde0d3232d961c52deee6be3e23194dc07ed4f4f1e32df19bf5910d0e6ce55fd5412bf6d2e9f24c77fdf038d689048640a6f31e45b940f585a8d793a5bf7e1844d7edafadfaf9110d1e3baa5b33987de29c2786664670caa4b75390afe5ecbd2727e1ff0db2660d0da53d04bb87fe4fd22a004791bc0d8dc2cb9ca4404c71be948bfdbe8666c5f26db6fa1b4fe9763c91b8ad82b6251e2714fb0a295bad966180ddf19333b1eecc2a87a259e595888052fabe42ffcec38882b078f484bd369fc14d4fd0ad8e779d74e996a80c7546e6a0b2847a75f8d439dca9b43556c59c25546db6206b486f84aef9e4cadf02184c92c8d437c84e405774a31862453cbc764512bfb22ab9b3108dc4fa5a742f4e8a80a2f6b1f06f92d6d888284ef955d6fe80fff1caca9205d55381f701542d26b2a2b50e97fb99b0438f01356573a01bd15ecb89109b904c770644692947ec0a1ac", - "public_inputs_hex": "0x23820b662b29c1eb92181286c78069e2c68d9bb3f3f95e131510bb674adafe902573360a3431f9fb0997d1717493aeb1edcc58f966d0a1587066b4b2f8851e4c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000090331489384274c12b43fb7cd86990be000000000000000000000000000000007b6b44c05ca9dfc4194f954eb77bc8f50bdada746c10c4cbcd2821cccbd15a0e64a76fb9566b3912b9d9c5e6ec89f27305f1a42155a63345c51416db9134e24a473534e1bc7264e47c3d8ceeb0048f3f00f750355551fc21f4f8b099e8aa9d146476b5a3eba3f12ec324353d52c1498e0f0a7cb8a62c14a214735dc01b43a24c55e0f368bb80c5740f91d32c79b109a902adda50f3a68b086921840211e0be05add7ad8933f82d689d427af65040b9e42190c3823163063956218b83ca1ae19999d5986c495dcf68fabffe666e78796909ec0921581b707660aae7e03a29bcf5f6310e32bb1ef1bfa21f265e211ea0dc1d34da8f75be8d59fdd8e87f406948e636b3c1dce3485b81e8db02be6a2b619d04097be17ce35a4c426eea7846e3cf55a6ecca6207bf3e1453d1e46a962e47c704c3f121cbb02760f51ff659d1a9699301fb413fc3021b4ffff5c1d00a7b57162a19c999035857bf3a4db9a179b5166dd723b3db90b27a94e60f886b23dcd1b10beacc821d87be0996601e9ea55375f31f80de63e7631046d945065e07a8821c" + "proof_hex": "0x000000000000000000000000000000f5cbd1db87fc283c12c0d4b24227e9db9400000000000000000000000000000000002eff3a040ffbbbe08a43a91e611c27000000000000000000000000000000f716be9639b08314302bbd9205afa4858700000000000000000000000000000000001cac91d2c32e9543f868863c3330b0000000000000000000000000000000576e2373c97edf5d40863467ff965606a900000000000000000000000000000000001195ecf07843a49cbc9356a2582c34000000000000000000000000000000ba3987fd41a56b63e27b49f4adc4f5ceac000000000000000000000000000000000024fdd9b00db6e9a183b96ca3b8941a240f26ea65958e654f99910da406b5d53286206735245e3c6f358b97011374961738e97405c3e8b7bf8cc754ff361c6d9df3d4d989a895357a8adbef7388e7f61b67a38fd84d77a2a505aa694e2aa379fa480e8926d9921ef8dad81187a0ea371b340dc8c7b002dbf10911af71edf407447aa8f1f418071ce303b6207ade1b941c31d3042004cde940ce9f51be538a13954b95c3279270bb87465cc2c1c2cd220a16911aa35dcd43ce52bb9d8917e37a9c67b5b156ce6933a132735352971c6c23131b965a1f4fc04fc29d0c7d89b9fc8267645731a96a501647d1eeb71d36a12498067f8060361e55f78be30780022c2577536711cc1e0f17d3c057f8af2a9f01afcc54d381abf6a4de2d1e4778b22c0845eb39f2a835d2137eb351fd35c2d12fbd5cde6a671e2640d941c76faa566d28d018944b30b149fa3a9f2820e26afc09ffde68fa76e3e870af7a2977532b9fa1027054dae5e022048107e4f1af354304d098ac2d0adc1d746b5c48db075cc248c5952c36eed6f47ae08e94049de3fc106561c409f35903cf3dc38fc5a1a0a61546dea85c7f4f8289637aca42640245041036ee6f33806e8200e9de48218c139339b73fc0de48ef8835bb5d108410f72b608168f619fe91a6b50919a56971546f8f211a92f45fb7bcf42800ca2bebff2179c79924d7765bed5f5f1958b015ff3c123eceea2ea5a84e79387b51c73a012aa850237741c452f2b0db18879d5b5da9d338958b32f289fa2068b901b2fe0903288e788665f12e76a5677436dafd2d6cdae91f6dd2c0f313d1abe57506d9f02ac06431c2c258829f27bb67fa6565b33f760d2e0a5e65eccfd91b847e408a9a2a5ca57246522c72bc4085895088ab240a385ffa2eaa7e75d6d3944fd4ba2087100bb5cdfc42e9ce114b512377f9e5545003d3fda81479847ae4ab1e32eeffb1066d84d707cba53b4bf08883dd3188590b816af7a94e36206bff8df3719b77d50cceef8e4e2d29d95f612b76b2e451bbdf0b13b40ed4d931c39f87ffda852ff80ef613df472219e320cf77715205ab3afdda21440f550bd983a34de9d68e543c19ac3c56d11eb474f80db7ef783d0be9c72a14df1702c3281b3849b95c457eb815596035fb8e75dc5d4bd217763ab601ea401378102f88cd35c8797b2a8727c4160b47cc025cb241e99f88e600b6e8d677325dca39c0c27f6f8aac963a6ec875278d080ff81372b5028ee77527833081c22688e117bfd96cbb3c783e8bc8b6b015ae23571401aee95b55d67db5f4fb6736100bef1d74a743115b562941cbbeee0e656dd0071c5af34c4991d27c041bd2d54522b5fa79b45c984148e5be3379f62e9b0796074c1407047b344b87b3ac60530226c2be52b7fc5a641759cca57c261be4e188ead830c02633c3422c756a2090cff2f8103a0371912796d52a8e32c515f52e2d8316980c3b5e48787ed8269fbf23e06fb489c7118675ddd32b5beafc18e4f441c724e294822dffb6352b82634c0c2d15fda1d6c7a4670c113f6158c91569c2c2362e029c65087dc4367783399a9facbd02f417b25e7b2a9d8e9ab57e057019050a3e29c949ac5fb1a21fe1143bc15c5bd94595238e6e5613e2e2f728061c593a46f621a280f03864bf47e8ef9b09a4cabfd707c611b7d23f3b254e501eabe02b6e06927aa326f3d0d8f13acdf9d968db76a2b2413c36d720b7e1802a2dac88677b719e957956eb99734c8336832bafdfe5966d8feb17d97f8852d42f14ab8c31ef0692c866ff329d59e0b2fa28715a2826c2a31483b17ad7874ba75820bc5c5927352d849ffeb98de0f4f7a930612dbbb3409b9e6d63c85b338440251ecd8856d84946c0f3c67e0ef49db37fa1cebe2722b4faafbf09ca51de749b8a164302902550a9b70c31eb4afc939bf8827e0556c48e3ad370f4398e7d62f7e30234883fe8f884a5d5dd160879741e549539afa74012e1047cda923fcbd55a42182685eb7bcc89bbbb8035534acb8044eef33f69a19e6551fba3bdc1b407dae60f6c58f639c09a6a5dd44cde7ae6a3cf05d07e863b53bd2a87fce7179b00f2da15e339d29dbde6a040282d629a8957bdc0b82943b3f05e5e20ed36c24ae1c9390e0a33372aa5dd49b72c554847f87ffec6b90a26c45234cff6386b96d2e208882a7b1eb1e0e337bde1727ed544a985a32482e29058a0ab0d5584513716d6864e023f2a5ab489140f44d50c0d6c385667f09d051b5200233fda9977e001a41729273fca988215642a2429862be6aab9f797db5cf134cf1c3c487bb485b56e7a052bce8160d035267fb47452dcecb1d71f5e2948248547e038ef776a8b4edaa4b711974d4538a67bb577ae40158494752f4d78eeba888f60d6cd677074f093097925e27b3e3f9f35a235f82c70800df6a35d5267af9c88b8e6625d85dfe46adec400ab513d9b7d04b203b12e73c17e1c862dd7e0adbae49670c18ac0a003c10fbb179c7ff5f847e5a661cbdc2df6ce5f3e21d8710d73f02887bdb5faad248eb2e4269cc0fb3dbb4c5d9ab0dfea395bb9951b6587d9962d2e44a032a7f08554e7511473e705790d93410f7f3029a1495155cb47eb984fad9b2ecebd1cf6423712ab1d1c99e755da97df5e31b5798c153105fbb77b9c7530d2d313e9cef5e91216490ba0992be21ffeecd966c53f207c1a8b6c018c3922ae7ec689e6a2b1e9b9e20a0fcbb16f5ad7e733d1ad0bdc3d102c4600ca406b701ed33ba075ba4abbbd81e42e8a5c0c3fc577802ff0c4bcdb442bdad989a552e5f8d3eb4a73a9798926836d1d59454afb583180b6c09245e5872e832b84b51a850c4d86eeb12d123afaa048057d0a19ea84ca8cdb93276dc70523ba241731c83b349b0e7dd7f2d31f2109910cf70753f4eb6cf7c191b944283c9fda82dbe5ee98a03df1fa2064e52d0430421393bc34d19ceb4807850b7895972345e8efd845966ed56c9f65f595ea683e4100af6ced76dbfd8f9d69e658911be280977bf8b5e6141706e55650b9be8bcf4b1b3191319ad82f7be26d53f2dfe2d245a2fd1893099917ddae2afa217df8de5528c8e09940ff9fc7ece3e9daeb2e5ebab283ff5f8f011354437fbec1e90348aa12466560fe12145e73c90498ed727762e3866120bab74aa9b90a53fc0ff87ed521a021d9b3b560f2af100070339a0fba25b2bf9f5d4b561b4e01f7194b925b4d296d65d4b7f1f6b7eb61900b03874e52352c545207067444a333935679579255082d6a6d7e4711dc788a40c6a7b205d7ff475c77fc48e17d0b589c2e1ebdcf1c0afac0defd087e46a7fe1bb0bee36d2f516069cfd440481f5e1746919fd696c62dca49c7283d1e733c5c167d99465a0bd64f2f4b310b40b201c337a6d607567a155a18745fc64281f3ff36d3a4dde1a9c55b230b3bbb5ec06121b7be93c05088150f69288502f9394ecc741b370f1c911b63d6225066e52162dc02e4da1a295b1dc0c0425e33eb7430ada6ffaa58b0c5e630d7a4d091440a5e35ecd60f13c7ba1dba007e0935fca98efc86d0d995061b8867416cf3c5678fc645cab7beeddb5e14ab20555e81931999d787dd600dc0e9fe18584595f14393015c4ebcd7c3d563190c93b31c95a79631340271b17cb5de1d3d18bf28e43de3a2f95bf48d1f30a21fbd900752efa7b29305c794635091e3211e03ae33baa02ee65b2122229156be24fe7ef5fb232d643e2c9392ea56749e7d645b925a7359172938c551656130271003c5f717b663c916423d1120b5fdd9f7a2edf8f458c468007ebe3e9cb60d2a26a5cdbabd8d564ffe3c372a63bcd8c1196073072dc4bf2bae1ff02221a733651c73cb58188f67cb103158d996f6506180c7a9a7a7d23469654c97d761faa35419fc7d3b8cb4bc4982e84b6794616ce1f82db35038fc14a2d6cff759066ca74a0996f6f0d9244cb7b5211fabe06b1676a74be1f32a96d074e099707424f9bb71035c92fd29b61e0572bef4d3a4b3c745e92f1b4dff4e719c70eeb3e6e6c38cc207c019b87a6c21eca4f3fcf48e2d4f00d4b5aa725be5241914a00e7c21108bd5235729516641a5e276b43f7688d49979807544d579f56640ad6bbbe671c107271a621b8cd8915977ed7153fa990ad75e10d9d0435b92e32d2a2b78d73830a49b1696d7513c6295d4e75a7967e0108118a7b57fc1690b89ad4495076426148cd71ba6cb343d9d74f7d3e5e5be941a44537bb01f5b6035b490a52d4738708e6013203935472cdf1e5ce38da55de364e717db61cfb51bffbcb2b981464d9871cc2503fa5b4eecc22c338342a5e02b6ae80a508fb80e1e457fc131a3f7c758d4cfc519ef00ab0fc3fa54020368f32afef5e4c5b11a1fca85d714ddf7d2315682e85d16a51b1aeb6e5aea949bf986e6e0c092be9348ff7e71255bf98c28f857d5160a055ccc9e203bd76045f84db2f285488ca1afbcd8210c544630826c18209fdea80a76b788458ade99c52588d71db153a583ff98672b7a91c8448ae83570d4b2611f1211538306d2b9ee9ff6d3d5d7fb22e5faa611b2935a2594cce1166b13c1201b38184511f8890223a5c183ca423340c722ce3d2442739884567688da078947148f4ca8b22fc402baee738700f8aa87b9087d938a36c40388c02307ddd9a8f111628f95e8ff6bd4630af7b415d1fc014053740eedb64c9af16823c7cb7f840b0059fdb94d01ddcca5698721449516e8a057b5658e97dc7fb843fa8aea8f0f4227a3e5b20323817a94a71ca39a45ee26f786419926cb4829dbdbed0975e2cd740ef92ca019595b53708bbc703a8535652d91ad8ff16736fec58bfe262eb7c29a140c008c92265ddf94e69f6a8a413cc05067af37bde067911cd0310ddbb06cd62c23b043fd9e370830a2b8c50a2109e87ced928d82ae0047fa145d30e50a9eab110ba7000e93c92662ddbba49c0f41c0c1327ee9e4a198a4787dddc9496a9ffc25f509daa47cc2dbb46af858cffc7b17c9ce6f2ef77e175aef6ca3dd16602c961004a3f6d38ceaecf543755ef5e9969e9748a2b3ab58bf5cf25ca4c93b83f5701c5301bff21d0e9fa48b3168741bfdd04b611d605a274e53bab6f2795d503ed21fb9106e3de22637812910eeb2feec8914de88c3a55ddd34e2ebace6bc5687fa23192b8a38810b8fb22a27a07aacca945014ba1ea78ff0fe7d30ecbac09dad44206cc278143c8d8d8fafa494d9db88b76fa7826940c5300618abfb8a62842ffd20f4bae8f04646f6435f333c206299a1f5027848326a1a8fe99495055588a3380fcade78647016ab03b1410556ab63a8d89163b9ab8626b0fe917c58c80f34e30d45f6e8124f1b4a41d89d249dfe63ebb73ad2741928e167625b7edac53b708a24ca22aaa58f46096c674e6e764208e3460dad7ada64b9983ecc1f5280f2a63212f49728bcdbc1b49aaf36a3e7071d4bfb84023139a7331f2028f391cb4c0e8403546a0522e070f01de34b64b444d85e6b1ce1c07a1eadd259e82c0b4fac93c419c881b6bc7a73846a8dca09f9af488780cf60c6993e888fe6640629c370a49f1e69820bc0afad16e66d2ee0bb8579135452d0926f11a40f8d7d84f0cf1bedae2e1a0ad11804b07a217d7f598669319f32c6b51aadfa67016f5d930bbab0a7c921cc85b1efd8c6d3a68cafa7fc2d220d1c8c51037d91bc2510e4180b7e01a96e2aada608eca662b5dfdce2b17338f7e5e907d4cafb4c611cd8fbbd0402c4f8600055758c3a8c78eb3f8e3ea7961d49272bf0ca38b97c67b7408a81ba38d904000ac034cad65b2ccfeaaf96557398d8583116a5b5647abe27dff4458c09202e2b0bd5344aeb23b551a5d42a544b7885105fa5a7d323d2cd1089c156d6b3591c93161fc531ffae90709b13013b6eaaee9830ab8ed176ccb7e5e6d19c1d15b6a5e52727a6660a7ebb8bd389c1256b1eaa626c85a48dc0170ab8d11a5a1bd2b766f72e749746d16bffa6a0adc8b64dfc0213bcdb5e290f231aeb95dee37e6080fa9a267df98330513b8e73e76fbfe6c91f94dc591ef1476d0d8020882a431c651069234b6450d5461cbed852fd3395176b8a671c750402fed4ef345721159299bf26121ab2c511c8d48e9c86e0878783f8aa00f6b7731d994b72ac696447c2bc724220ebd5eeda6e30ea0ea6906d567d1d5e12fbc8af5087780a248257635d9cead11ed7e1bb61c8c49bd9525c0748371a6e2ee68af0f6a0787633e6b5c7aa89af63050628fe3043ebcf8e708c741e9d32a73951846154ee7e520722470c13037b7c01eac979321f3c229f7af396e3d05c5291ce4858cc438d63321d657f4decfb101efe368212fea2b8857e410164cc4792d3e12fa5402fd9e94a93b3bc24b6f1e91b34185c6777347d54ad4d813811d0f4ec41aec6902a3c72698142cf325f7d1a2ca3cfd4f1bad4ae93cdc2d9aa7a3c197645a1cefbd96b6e76031ae2bc3bf5bd12835264985651e9abc7de312585f7c2ce43f8ab403dbca639b8976e2e6d11051922b26f9e7b72b9d5249fbe6f70b3702603352e7d36797c563b669861fcf9111a4a0a6505e28b7f68a58baba92e1b23743af70a3500ed8c0f9bef8334d22d020e487f8e60fd1f741d6463ff0699bb6d39659230ea7dc5c0b550961107ac187823e26273081350f39e0db08d61c236a0f87daf21bad6059cd1a01746fdd6ab47275fc7b8956e9dd907107628859376a0c8921d09f60a3de8c81c443eaa1131f303e42d46f0cd82882febcf553c21ad8dac2a9a6a11b61aa85f8dbb2fc86a63f625f92858640892446a626c3a5555928977b98d3091a4b570e8c8e2af8755c3492410238c03caab5a6ecf3ac1ab32fa0dbe13c9cd9bd3406fe66756dc03369fcf0e32930d5145a8745d52238bc3aecf0c7ff2f4d503eab10b87bfb353a39ac48820f024a5c2d086cbdc809d3e441865e2b053fbd511276e754a0372ae0f125ca206e8fcbf98aa6bbade33db4c108e35d7e9817a081f4be0a918e7cde03d37fb1903252356d307e94d70595391361e96848eb9b45229364f85a53c2d1e1eaa0b3b098ad9776fc53c24e4256bdcc54dabf5a6c2e97082b88388de4436bbe63fd68118f2e95bc1301287ba5f186f12c09ffdb44ed0e2e4cff32e1815e398c74c957922d26cc8ce97c42c300f2d75e509896f3740e2e1d3769eabda19a77e32c285d72a50d994b08680c790469e2053bdaf8fd7be15fcbac0cb3ca668283f9212f32a29105f5d1ce6f5cc620ae77f13397ed09fccf4491fa4eec3e6c86ec29416c1061e8808f96e9b3289abf234a4975c0607a530d16390d8f95a0ee1f2b045366d2d1d8276cacd015e1c2c88158765c00ffdda9a5f700d70f8a1e5c06d8821e83ea32c956ce9cbdbb25d2da02c256f82eb7be4590ce2c2362816380f625bf8d3bae622b11eb45af3f67ea0e10fd73e16851c263b2e4f350476aee1cc9ae94f79271b0e4bfebce9a058762ccfaf599f82f00d3f2471512bec6cc122ac016ae7ea0b0e1f371e9475f35abb6ce3f0e769b6af62e4cbd123a132211322f4d814154fac9e01f132f0175cbc5d1ffd73f77db34821c8db1c8798c325d3f86bc93081e6dbf21afdfd9ac31934738bfe9599eae2b7d781299427a091575a94500dedaf42b9911fd8b46cba2acd6b5ad1a75112ece1d98205a76fd7cdf21f4453f4224fe191b60ac285b558fdca97e55e093e6d7a36253b7fe81bb92baff227f8d4a87416772b1d01269c0ed62069a9a8c212d07fa0904fe12a4985ec4b69b11dcedade2535d926f558f9e37682b5a98af393d3119dfba045916a7a5eb96191fcb7ebb916d8ed041896de00ed789cad5aed69166d50be31877505c44ae8ddc98f9b544e74250106d20067e476910453d85cd85068404e20a8f03ccae9f13d69f3f94477b633a41474a50e8713347d01c24e7f8b02096f3667ef759fc2082ee82e01e85a47f6820e3dcf4fcfb90a95231cebcedc7fd79d06d6665c9b10d15afde409523a3d72931b650c821385f8d01226f216b358bd6557327b07208972e052da1f9466d9a4ab0f9622a214e609288f25f900b755062d2da4b26ba7603eebe04b13e2088c5011129cd6dd6c8b65354890880acdc4a267e46c847d48556162bcabf0b2f709696408de108d6c0cc5badd1371518642aea07e4f15d1582bfef5fff9a39374b3e41910eb989c3d8bc043ddb5fa5067059a8692bad81fc74488f059b374a1b2323c5d1b112d2b5f4a9127fc5676c1f382698b81b0484ffc1667a1508c978c6f58a1e5014af1fd93ee6fcd6a29a2eab01ea3994946e3267aa3a481603aa9512fcec03c29e58028a43ee59c74bdd3f82982eb314d865a7881101e9ca8c5e09c90e496dd13dc282530b90f14dea3137d88a2b64c9f19e66db20ec1a36088ff9cca8bbb88142b6fcd496207549cfeb131c81be28a5e9ca30820e77ebe52e1580ea693fa340463e7cf8463f66f0bcf4ba2571e7fe957d0b6316fed0eb01f7a4e11a3d2958908167f9e330522d2602de2f9bef8adf64bba1d6a93c2aa57479e11306c02011a162e20916461d1e362cdc0df28b4c3cec7345b064fd046a718f2361c1fcf4cce04513901179ef6347059c7aac3aec1c4d4ac797008169e0c1e84f5d7537d3c8514d8807c098668df7c6fa73985c3abe105922336eb176ff0af7ec701ccddef08130b1dd3cfe74baedaf25267f8aefa4770d1368d5414adc8284806c06a8335ee11576c300dfe9a3109d69233c205998109049855e002fdf9f1527d95e96c705c27549b71e1aba9e41d41dbbd7a4b19fdf1a21fd50798df1a2b600181b2e3cfbe211e04fc9fe91cf78e2158995f64ab2ce4e23ae78d5f5330299d55a3c79273560ce6d35163f0842863049474f5ff71c23602dc56e272b1a2a6b5e255073d7003044a68d459dafd15a7c7cee706d8414a4ab7261bd3eaf1d05264362e7c4ed1a602ccf785c35df58090773408d83fb11e04882dfbf0fe58a9b6e55c4420956cfa2f0130b34476bc248d1c8dd5a974af38a73be2e88666cb25aa68243157a2377a2fc2cbb598b05e189229d8fd4bbb4c648eeac0fadb726a090de7ee6b86b9463423076acb6f1d88ab5359da22b82d6537a97ac7d729a42b0a25036db703fde11e07c4801cffc3930dcb156b5d7a9fcd011dc1539cc56c127eddbc9414220b259f068d4ff2e9995c24b3b653b4d3eec84771e3af38ad834436108986643a47dffb0ac236b5a8e7ee05a9e9b6cb555ef4a9b34b73d0e1a7560cc5b6175483f13570213e52e516bbf6fb841d34f348807145a1631920b6047e79a254bbfd53b14f9f1c4bf143b9367e3e9de3c9795118c74dd6a4e0e9ac9029d9540f55b30564114d236657d64bacbeb1d93d9a14bdcaace7524e4ea8b02f5fefcda494f6e495aca108ed14d839a7ec62a5c9b1ac07408a4d61c2237fc07753c8a4a6583ac062052e2a7845dab14e83a329a0c34118f16fbe2aced7cd3c5d21a0b4f85c656c9b5c4c2953ef4f749b536f33ba252601b8a06dc5ef2112f002601516837096f9a9bad51ce8dd4659e79619292fbf037dcc533948defab328d0cf56ff9233e9e776c8f707afaa151dd633e85e2da491cefdff3d23177e1ecc147c401287888696f4e7df0e0c3deaab958a3ebd1efd7c37eed7d905bd35815781fa14e063f5971c38a58d134af92e676e58da31b333b0f787c352dfe0e2dfdf0774c6e046dad7f73e659f03e9ae7110a47849e404918fabc1148ea5203b6921c779515dd7569461ab250b1924adb207a87a9ee0c7f7ae8536146527801c8ca956d06185b8bdf3eddbac3d2d6be1bfe42ddaecd73f4cca1c71adeb86591c13044bd628968f4899aedcecc412186234f9d8f1c1dafe57d3785ef5e50d67d45c9c12b247519e3e1c161162790e12dc609afa23341b494efdb8cd5aa29d5d07634d08126f3d6039a999b4cc450232abc9684c1228b9b6191024cc966af1a22ff5149cab9ec9ded5eae90b2401219955e033b9a5226c201edf1c382ef107238abcf271c869758cb32fba3f34fe0a791422689305651e48f19da68dfa3886a8509a775ab423bc92f057c7c4734f21fb990aa7a41a8eab0e299cccd42d695f1dbd35763b6abaca4b365f4a812d962b92317abe5f8994aff1b657fb038255b6bdce3be58f29f54ef18aefb649f22c2d187126fa091d2957d07e1bc36965c6e0467d08905e3c9aef26d12c712ade4b2c74fd26cf6b8b1e8c524e34ec546e0389e849dbfe5b16a9ef16d60849db464514a15f6e69d7403369325f37281e5153ca78a4009aba5b87af9b1cf979caf80b064d30f29898679a33c514a9827779fe0986a82734023461a6e4e61a1f6cf8c72577d15f48af8cbd748b9521b3f0245cf8b82d2347df252cdf70329e5e42a02302551f46a7239483cf5fca4cda60bfa8d28f7477781834d15d79742feb1b82d22774d1295cfcf5e1b8487226b7ba0624d6085a08908170c0ccc6fabeef94ecf609c3315a035bf559c1b18e8843a2a1c48c8aee430424ea62dfba2126bffda6612feba61de65c5cf70493929fdec2dab8ac8b774f9dbbea1576ef462404f471850000000000000000000000000000000000000000000000000000000000000000164bcdce21ae7a6feb8494b49250263e638d24838f3ad4820d5cb41451c103390ebb8166acae682fabe3bf38879bfd63578b5755e5bc2ea1dc964262f9a5c1ab1361f94b297af7bc113e4b043ee6834d43f94cf801f1a3a7fc230eb11cf1738a057f700223850e09dbf52339fcc048328157e460c8507cab2fe1bf90839add7917ad162e5c7f0c46e685df64781d28b29e3b75caaed90417c8d65c30aa44f2e220e4351ba05afd1f29115a4735a53a7d46e6693c77f77fa9f052686973fe0a8509e1648ec75deec32fa248096377b57f68d30474c0fb13e9f9d25dd3a81a889629062ca58574c973f79057099156bf8217bb6a8c895a2e98f1ad0402b949d9d71f9a7f1a1d996fcce8f4534824182ca2d0c29e36de739473758916eed5e1ec45037d8bfd1ec5dd45c559861aefdcdd6026461219e6f988f53ad81173059263d32af0f3b66e55e4cf60c987b0abb5468db6410d5a9d9583afdc55502b3b9a63e529c55599e349f7ceb139627d822a5100473fedf3374ca9d7f9541b7f690ef5382f8e37ec0ecd70ec6110afdba2c5aef25f2d73c555c008a7fa6882397432f0f714eed647ec232aa6bb1d87fefbdf5953826b3d4893e6b219467e1f984320b54929175309e22011ab37817792123ca22b299464cc220f9912319023ddff7101b509fa9af4af6482d782ae2e38a87e5834f31ec0829d0f4e0b1d8546748f98aefc04bdb0aeb9938bffc2178dc195705687cc159af32e5dc34b026ee2f4aeb471ef0c0b2a518edf26534ed22654aeee1c00288afe36991989638f1dc06bb96572f8286d4642bb2f8b054b12fd30ec938a979db68daf85b3b125757280a1a9b1bb012f8e268061d821dcd40f4331590d95f77f81701794e1173739c12fcf8f5f2ec81b8375230b44b3d8fa5943d793589e71855b9987975cbbcd8772d90d53681aae0204a799cd9cc4506787d63bd20628d29e8d5dad8d210e9a022ba8be9a797a1f2a8b7b298fd655ba3b15d80594f0a6d7835b261811c57268a78046921563f5a90562ff84873c88b93819bda3ce61c07abfbf9d2a50c551a66c872fdde52de431296a66c7ef10750c99ec695d95201b282d2a0c87e25456c794e2bd3a61808abb18e774c6b86eff5d95032a19c096e2c8ad8dcb435a4e48a6613ae0fbef7065b22c45fafa71e50e8dd360381559649bdc4072d1921d8a53ad2a0bea74883090db2ac13ad4cbc00e403122046e222612c4817ec05d3e0b2d9329471f1274fc906e120d1077db201bebf8fde4812cc2a688ffc860af243e0e841df73ec47cf7a04c1bddbbf4e5f352b9ed874932cf302186b86e63792c0e691faa73f32c3de2e251164d90e61f499a93cead92ccfeb970785001570332ad41aab65f8cffff11c29627eb1ff526184243e5738a84283deac273e0f7c0b3878c5062ca455c48e7187d08ca79acebee2d71693b725feea06504ca40b01d5c010c458be844919dcdab8207ded0a86a4c44dd7737719963ffeba365fd37b8584bed03d64c6ff61bad484c2769d28653872008b108cd99c74314721fdda85c08288c5bf58162cb92e199a4005c77d6433fe0efdc156fd15fd65199842d850a92209dfef840c4cabc28119a024a9195b4c7cb797c0bc65202462ef2967ba517c86b33309c70c4c24e43dfff0d105893da79463341ea6d73918018dd05c24418845c01e785918439d709e32a149419f2393fad32763b974e9c47a82b1c5eae283d6690c5905577180e44a000301ae9ccc30846f962cac96302bd13e72d8813c28c30625c70c2abb41203b88f2ac1429b051f2a1515a33e3b4694c82c16494f7122d0c0db94306e590a0626a81e10da4e01576ff6a8c17f75470490d5a3d67a7b1d69a6cbc84d10b53c08a7c115c55dce00a08ea1a68812543906bd60d98d676ead2a6915e6787e633ff4096105190f6dd28f05f05ba57cb55a5611636bc307a7c83c7a9d743c974ac0e2614d1bd40d7c10ca6a65a87b5e30b108ffca451d5bb0d2ad3cfed908482787d0be780752dd38be1ef43d76608e00fa02c1aa14c0d580221710bedf799e156de365f3279ad22ff0257bb966ac5e73c610f2a2e99cf2bc095b46e61cae2a422509f19d1879a17f44cb9ec42597699add6768a1df56aa99db31a2b0be179f923cda21561a36dd35050f45f0f4f05335d8d9915a8eaaf68c62037866bb2beaffee16d9880c09bcea7ea2f9b32cf3babbeaf5797872dbd10b1c9f962849dd7e60070d48bb297a3ac70abbf1bab0915f82afc7060d6fbd85ceb295047881a18e77c46c61570c0120b1492dcb2cd4d48471f844eeb20e586e8b861609c149ae946ffa867aba2050776ac2a694f0eee81541beab2c8b6da081fa6f9d095902261b00329fed412712472252fc3e3b6432ae4797d61eebf2ab6109847a6feca0752994054d388604032b058890305ce4cc8a32ab2b02033be99e0879948992fd0512351bc52fda2af2288beaf6504fc07d8bacd4ec799cd78f88d7c255d26e25a5e7ff07ec156d068bb02cb40fc5234e9301ba10b214ff9e5e8134570836894e523f8ce098b3d50c39aae2890c479be5ab18a7e408f2e7371432a30c1aef0d9cc81e0014688bcf2e5598a7ae1861b971ef69d7c7739198c95e83e7b776a5a88994f0d62f3635f423cffbf934b2cd9ca18d3b14b69fbb36763f868ba6940fc8accc8f235be28aa4001a984a5322a4de703c744758d5fc4bafb809867861ef8690b252251493b86220efd6360ea614c64185434859628452d0f0c4fb2bcf81b70536e2b1afa395b50d63c45e0d12de51e1159acb9225fc7a3ad3382c1ee0b0f82ae6fab1931799c12c6e3f0e2ca6059c945c5e77d9f597d280ee183fc22b484c8b6ea97b88a462740fe8d06816f2c50f90fcd195de8f6f7ca9931da30d83f70d010c1be5517e9ac723cbcee8cc08998704b161a96fc1398e7d10145c555b48898e28670903a6bf202c943b8baad401b5d94aff755be4088120a81002ffbbe147d55b2a7665617a172a2e0bca1d060f562511586cbb34d13b8ee2382d77967a42b703710ccc2e880020c73e80d23f1c370f312c5035a09d27c17d2b0a3a7fe8cc552f2aa221016715205b10371ecb96470129e5c03c2d393a07a423d68eb4c9ff7fc9b4e08f419b4e0cc7fd3931ba7fc33bbccfa528b1e0e7bc4ab06ce019dfc433edb73c25e09fbd23030a3f1b7c0cb290f6751550720b4e77968341f4ed223be5f7f87024b6e5250f6570ce5a7d6b0999a835fac7e687be79d61adfdb6344a70510d43cf550abe11d84010eed8f6bd67c13847ba268f30d44eefcee222d646f0ad5811b45b890840bf8e8cdca8e38f37cb9516f38b3432842dafefcc2581c57a7c28c379a7091dc067e67fe5bba67c0919a6c0a1ac2f98fcff7bfa0928bd52b3f25558e615d8d7e00327c117c986af0b633ba845d0745b173c74a6d0a2f825af15bdab82752929a2617ac0e9387a6f8d43c731687491707488d14e71fcf4752b0bb81b5b153428c2c190a5c4e998fd1d06b98a755523d679cae8c5f57c0d07b3b7067f80ad8fa68280c359ac018ea933a28c21b95aecb4a238b42bf4556626a349a496991cf57372a6a86a2694cc027cca8a641e70835a699843ad75d4af358695e580c2ae6f06c20c4293b374ec01e771260088ff7e2030df249c4ce473d1a2c29e05821a6de9116181dcb1dc9356edeee2ce033af7de94039d6c79b0d9c635837e2bda7009fdf1027bea6e56311fc49da2b9837388f4e146cedf189052043fe0e3f7ce8b4e27127da4d337ce7483977d9df1a72789e8a5694989fd4f4f35a158a9769ff89a787122017455a38e56af3c46004910b78c0291155e3ac08f742349e87f4a5665ab30dcc173e98a63c4baf5d78056d2043b9147a818889aa51c7c43ada3adbd840cf27b9dc3271b2d8dfb869a3ffb687f935353522d228c923903db31e2a03485590083fde809d8e72b244a341e19967358a2b287be92909e5cff6a567b9026257da2c25bd4e92459d5555a9dd0c175027c559ed69f77f2409141e42d53a7276400511a80c5fb492d1faeea7b9793502821370ad0a10c0db171493f373d97231c9cd", + "public_inputs_hex": "0x1f4fb320b39a3449ca428dd3600b0e2523f359729bd7030cfb5d552b593e1b7d12b41b13b8ad8c86d81aa9d8f81a92e082ee58152c86753779692c6937f09c510000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000e6cbb0fb234ee2d6c16efa7300877ba50000000000000000000000000000000071443b4c6219d9646c0705dffc34f20e149d96a850d9c9452d723b8f7630024f40fc1eb1cd8dd08f54b08ec8a840ac5a04ddf21e1657f4d9dba4721b22edf2fa177a98cf06ea991e8602388466d690da19c3e11d0d3bd5a341c8c4deca0995a18515c3e758692236465f6a26a5d50bba06187d5f914ef9268517c305d07cf2e8be7c7519b2243df85e2f62769c07c41c09b550fc00f6cc2ea94810ed1831636d53caf5f12750fc386918665a974196e42ca445a6e029616be05e3e677a70f1cbe58ba39d2bf29171e3325d93b06d802e10afa542c1811371bd1d48514e83e1bd0b08d6168c68eb380f6760254d3159601c95742d598606d179e1aaa386cd9b199a2206ccd3d2b963f8acd9d6978aeb48039c4153e89da512d8c278d4cbe4d913d09375b42c7236cf5ef0b4d772a004a327dcb63446fdddbf4f63de43d1ed4c6c4446552a48c57fed1b6ac41e64605420169b31279dc1df67b0df4c3067be693fb741d887281d912bf84c7454775cacfd04461d84bea9016960052bc3f611f464b4df248f01e6d44e74f1e2086072bb5b2fdee79456cda239c7510f0e9e9d4970a3fe73e06fbb06625c40cd813cca87761ca019333507a3163d5b78323784d9e395759b6ccdd9f1d6e71220abc6d015841c619a4b2c461e1db426b7a685d89bc6d3d1d896d6fb0564619933df1456799410fed84fce3a1eb8fd364a536f6013109a6e5bd74cdeb6481788e17ecd6d985a128c629684d4b29a55cfaa8f73ba33b69b679409fb45248988241b432bbbfafc27dcb63446fdddbf4f63de43d1ed4c6c4446552a48c57fed1b6ac41e64605420169b31279dc1df67b0df4c3067be693fb741d887281d912bf84c7454775cacfd28d93f3bc2749c2de32c6b7d48ee70d6fb6af2dda4ba0b5d0366c249712b3fb80c519df7d1240f04c29be318da0dee2544931a9f8cee06be97e71a1c99b1a64e105e348119c8567a1d9cc13e0b00a14f537be9506cf6a083cbf12152513598d121d9ef6109c9ecb38140c915ef89767ab4272d2c4967c9761215f47d7a3cd89a269e088ebde5f26c9cd7d8eb564840cf9f3d722d5b19434ce618947673f64f9a0937e2bd02fd06c1f2dfcd1dfeb733a6491c4c2aa01d9a08835f21cc1a3c1049194270376cb5600bcb613b1995ef33dc78955ff83dc2eec29c40c953bae367c324acd210d229e1c3471a876911679d666c7fb725cc96bbe8abf32c5bb88fa275204416916168e13ed3bf53c7185bd13e4499dbda8bdb22adc8b380719cc269781f2f1462e0545189c750b490c7096b9bd187f61b2dd99357902a9dec25eccb6c08b0c4418a5df4a900ba1bccd47db2dc8628e0b0d12f5d142ae5f0cbb9858b7a" }, "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000274e50c43238015ea0000000000000000000000000000000000000000000000059f17e17ea294782f00000000000000000000000000000000000000000000000c9df3c7d1c1f90d5d0000000000000000000000000000000000000000000000000002ad5a9fbf030c00000000000000000000000000000000000000000000000de213a227f53473e9000000000000000000000000000000000000000000000008fbcde03fda652920000000000000000000000000000000000000000000000000e11c34b9200625f200000000000000000000000000000000000000000000000000018d110cb7a1b20000000000000000000000000000000000000000000000009332ce29e5cb0ec9000000000000000000000000000000000000000000000000fda69bfd2f73cdde000000000000000000000000000000000000000000000001976ff26b1855e86a00000000000000000000000000000000000000000000000000001e966433079700000000000000000000000000000000000000000000000ff21e58564f35f893000000000000000000000000000000000000000000000006a888f7863669e42000000000000000000000000000000000000000000000000279b9affcf271f2bf0000000000000000000000000000000000000000000000000002ae5e7a39e6df27ac299d3526550e7124432b7b9a1228e997ac7e5b1a8bcf79f341054a22273d158994c9d7430c98cdc139b5b19dd89ab4cbcd9e51c3653866d0ee3bf6bf754b128afa7679a3ee03477bcc89d48282db791a17b21c9ac3582658b5dd91d7b87d20d0dd06b89d789a5acce06e0842930486b35e3541d0cb7e29459ebe8c29401209d4963b35dd572cb21a1ed37b60fc510868b4776b94bfe295982a342e2e5dbe0b933598e687fd3aa3964746f600177ebfe703de2c891c0717cc6de4862675791eff43b86c3e360401f844c91e23ad9fbc215e1ed26c5d2bfcce4e828fdc867910644afbbfcd0191e1d1bed4a42e4456da443e5403037855abccaf4fea7791e92274b7e5fbb06146901b5ef9bf21c7021199477d79186812151d90c441ac53ea0445085a26005f443b8bb9767ffe9f2626db8016c3e2fb2303e3e62079abe3e42cf4336e62f409088a23582b0fab5544c8e4d8218a4b24cdca589c38dbb4c4731a29051c26930a5d74a77df2f06bbfeac457fb9f8acd6cff6b7f4650de62cd6216acf35c813087c35074389c393e94ad19cffe7cdfdd45e8636a97abc6089edb08497c82d25a2840bab820f941dc403213f2f1df22ce4b47c0845a3a5dbae20114d0baad277734f5e1d172e3364f30c9300acbf66ab70ad240b92fa513765c0026dc2207778f3d6190a41bcf8c69f9dabc02933b0d679290dded9c6d603749f810fcf1c6d28da75a8cda9921c463871626aee206404c314ef3a029ad5241332a23036bd7fc56a06a444528090c82bffeedacb0cbd2f506a8d7fefe82907ee5f72da8b2311e27987ad01f51a7ffabbe54ab0e281ef2cac78d991c5bcb91a5e9b3038bd7c2b57793de7ea75b84ae06f2c022b281119517cdaf1f6a0bc5494d017b020530b0271760f59fdf0cdc7e044c0432e5947bbc91cf12d477020e590d772904450b143927ceac383338e9a271ab0935a523e8976dd77879d8326668df946425180389af7d344d31016edb5179cc0dcb71c78c0eb8076aa43704690af226f502502d0f9cd0454dccd15ce172f9fd1de22d15ef4d8ede4860cb4abf3a66a6a10b7bd16430bfd4e01885115404059a203393fa43d74a10b5d271d5860163e619075969c54590c6c4ea43253bec367db52dfb47e5fc5d1b4d32b4c6907862986e12c6096cfbeb0dc76711b435d4328b247c13cadb44834ca40b8df0195ec8933d0da081b7fa40a36384bed824503f20db2c49bd1757e3e73fbb14ea500db7e9ed18a60bdfa0eea4bb6bda048a545f6e8bee5259a030f463275265166e5254c6551d0da8977a24824f2e2ce529a72b9803a739f5fae408cac6f51741477ca989b80ec09d7a9a284c8de0ff2feaee8055a4a71fa01090d6538a7b2b596582206e660a3f8fa31d8128b30836559cb3f930d2f7f4922f4196240b994c29c1363c92510e79ef7ee1b0a675603c5eb7ec15d17d62a668bee92c6a97da7f6eed06f0a1fd26a2308abe247506731af691f19b442bc9a58b0bbdfb19d116ccc687a24ff3c427e64faca338475c96e05c8986c5eb26e080a27985920f6ddf2bfd06c53f853f28c8c04257bbaf1d07d4bf5a0abc91ea53726ce5b7d829778e642c8828e0ca6106e24be30f342518f66b1bbc2a6ebb9e0b78c2f692a74dd5e4c08becc1ea5f2b19f5018f6acd7edb9d6d5b356e09a7c8e6d4ae8110418d47d164a113ab6e9fb803f692d6ffbc33f0a03122f83e8097e9de5dab9f5432b7a067bd72b110c0447505b883e02603fe8dbfb31f90df1fde45f99f7279926c24fd909c9bfa62f776f12c99d35fff14d217ec307cc454a60746c6e51cb6e87589ac945c189fc07298b726a5838880b7bee93461a1354ec39ee6f289e880ea0c56d3e299982cc9165249298666c54bdaed4ae49242460ef710de3668a621dcb6d420f4a502092b9fd54724c7720dcad871aa2dbd838d66338807e1c900d735376566b5e3dbc5859b1de62d98b29430ceaf92d0ad7b13be18501f6b3c6ee3d5fd8ae6ca1fb5f0461e2c0b1ea500dc5abf4f9046326a758b190b3c9724be069a666ae6bf824f2cb41370990b313298d3aac27edbf9876b39b9793002f253732e579a2284c852db39f0596f17fa08ef8293aa965d7d528a306dd85cd62c0a0b3b1ff9cbaaec43c1f59467a809ce53db01c2ec74e356c0932d803eeecd2e89e3406a838d761fc1200cbc93b90de0bc4cad5a6990ece0daf51408ec05b81126abddcc78ca3cab92c21c1ca2d6214cd47029334379b17cdfaebf31278c1222859f186d6be68c937dff68bf5b7d0570f5a7c9bb806731bb1a27f9472c7ac1089c020c1259d71e060a61ff6717df044ef06408f0bee60d68beb07a958174dbc522b8f1da88f625378dc78a4331a3212a501b1cab8d65e95595df5ff009fc972b9e68331f3b52a5e6ab57840d6c98132713b7ed6899bbf362d8a3ce1de600c532ba062fe090c147b06e692a84184b0ef0562323083dab4c75f905f4b12456d5ea2a30b7e0f9f550898514011178bd0caca0dd287be62c72f3d7ceb7258527469169036e961c27dbe305b73225b15e013c616966ce84a0ef385b8992e69b4577b94867054d4f1be64b7568d12ba0ac2f6561bd6ba1b002a5741fe0c30e35a5374937263a6bc2ee81a896ae77be14712936b73225f423be009558db37314545bb54d7b72e93fe3a05009e54c334fc3f12d7332c38fe0c0c1a5ea14c23bf0c0161034a9101649551f7eeb61b1e91c6061c72004c85318979426d5115e3d23a76a8e00701cada72ca3a4e1fc09d96a0c9015acc104c51483effe786b91c7201cfa2cf81ad2f83ad6af8ff8f3367acc01418d223baae41fd64d9c5081528c209131a697ee656853836d24d5f266a6581970551531f3e5b1da57eb35e6ec73b98ce38b88cf661f25a0cb2fd3967277b29c928ca5c24004e83fdaf8810d838b2955e04613446fbea306a4beb875a463d118a2d58faf03175b910a088ed0b3f21d1c582d0ae314723f4b9d570cc14bfcbb04c16a1978d784d0f01004d401ef9f6f8cc93903cbcbbaf1d82dc25680d1839bfda230d45ab9178bbd752c543902f383180122901bc672fff31dc749cb2fd4e155421eafc60963b91198077c55af3485011d9dfb40304dbbb40cdea29d243ef045f2b80c8f661f2b9e98b732c3ac435e7fea439855d56b75ff8b3dad072f5191100064d26f2f9bade02f99ae1778c1cbd5b67b21323279a917b6485441cefa9869a008f99bc45d2fdb462320933a3b89947d22c244bddee86c2ac05ed7456e3d3972b2ac1aa7f752ff8ed765561d02d85918d260a7f9f4b6660f559a8dab724f2f212bbc55e76f73293b2febf6449ea68485dd6a3dce9af0983b159a4362bb1efcd2a8f2f95e4f13c9c570f69f61e4a66817cbd536634a16a789a2d4ce32590aa7406320ea737188269f8dc1b3321f3c52867794b38b2444250abf69a656466d0df11a1af729be8a3751830047ec0e363828777ab3cf6a606f66b962ac164c26cdb048de3f0d86cb0c520474e945c194a36aee899e5fc22b70e76d70ef55dea8db70e83015fb98872130bc9a0f88db0975a2b915c7ac77459b722820adb69a12ffb255b573884ac653b62a76a349bdbe5618f43360bedbc2dee105a4731de59681c0b4c5bc3e620d67ac078324e6a70134fc90d994065ed5685f509b03cbea0b09c2084818b486256f8c3fd3dd8dbb31e87cbe853be43db69e5196c0649d9949a181fa798f847250419c22d8354f7296d954ab5a950865357359bfc11746e5b32cd2310eaf6766323dcc76133e773f4573f7e06d3711a6c617379cccd5c5273f17a0687a86ae4843e6745e85d46197434292e99ecd6e94956f7de41adbc71b5e3260d384ac3ab70617b1a0827e050c3206f707478c27717216f269c849d03f4a0fc2d8b4f0a2d1cfd8e91b4eafd322dff635b9c141bd526244d95a791e0ef39afad25683d367dc245a3137e31f2732e9aa85e60713c10e9f1cdf2e7800d6cec807517738d67795cba0e54f3979ee0b6b355b3a9e9b1e0f023cfaf7b503214a9e4d317672a16039f93ec5505f748afd302a8d165e66a5b1b8d2be91c4c2ec4c558e82397a06d0c9ceeabfb63767108cea6a56738132873dbeb99b1ffa05203139bea1a9ea13d709036926b508a343c7f714833ec2d47235ab35885c7c79266c0e74004ebc60d754dbf647d700cda950a2f156e08dce144987cb3ea3cc276c83fea1c0559cee3b97a3cd94fb36a1f1d61bc913acf5323961f569907d01ed05f0aca4b154ee2deb7c3212b6cc19c39575fc57287f812a93bce77235df506ba9d75dc3325d9d4b7c2e1000ab4e5162fd6790d21ad791378d09f6b8d0bb9bb0f4030814c10ab4ccac4de07f03217fbb07c39e404384fb1134d577f66fb14e31655509d4617e88de6ef335a667ef2fa652515fd48b5b9891453858ee0880ffb26bedd349d2d84d40d7bdd5adf773c9ca93f2a04e49bcc49c17553762d3ccf39fcd1dfe2fc2ab7b1e25af6022ca5c4b201ced659841628d77d381075752f1337ca443d247603c594e585804fe8ca91420f60b279da2c7cc7a5bb502bf8a1b00474384e536e14fc2ccae26658c8064c573cd385313ef4da868a6736b0cc6772c1e9a49471a62ecf1f997fbc3511cbb8dfa5b83c1b75a5afd2993a4211f7000d24ce4d39da2d20c7a4d21dc13bd740130e6ac6677ebaa54a00cb9b1895f5907f918898d0f2ab2195b05cd7fce248b7b1356b0c476f4f6238705a75e1075ab34855a64a22603a2d49931302311cfe1fa1558b79dd6361098c827cf8de6ecdd455340565de95f6047c4ae833f47124a4a76f209ed426a39872b55b3d8b02aad35a6eca92bacc87046c41f8ad45f520c090d289111144b5fc3cde94f8ce93502552200b29e1756e1df3d1bbdaa407a21f20fe0c91a6c3496fc2f560474f31cdb3c0126b2ca06ae72629d92bdeaa93235a9c6dd36d5457bd217a0013424710662cd691f7a65363d32955f09763623b3448223c675a12a1f558c292842af84035acdc07e5be8caa341b293bd328232f70b4b1d9c789840db2ded2670791d63042f7402c061ab30dad136b5bc0dce353f75d97f26a372e8ee4627e94276737f0a1416d5d89ea19bf9110eb6f6ef9441016e322a2ab487b72a756356018089d2124897b6136aaeea11412bf15c214030f5c778dce5d38aa2dc924ad5ccf1deba1d1e0c51227e3feeb7615f380c14b8bde53e5b666f0c2102e2a0a54641afb8d90f2fa045b30db868ecc2a97a6738f9c70fe550bcb8470101e571d5539989700d29b2a59c52d9dc1f1700cce3124df00e287dc3d7fc1a0716ca5da98f28041e20375b62f742cc1de4a521d7932002414a663afbacf2a2d8e19b45acf35c11e679b5f6d34fd324c7e0301154b567209c7e52bb9d800df593fa1ba125b26833ba877cb66fccd6107a0f4fe177d7aaf90a56b8863321f5c3c4ecbdd2c1a0b8ce49397e6f9d1a9fc4b2ed0bb2b3b7673ad9b6339ce2e127aa83bb4994f22fcc602a7bd8ef2b00be4f62e24bb04ba69ddb98750e773625c816ea897316d2d063d6bcc9b00759e9fa279c3705a17b2ecdd3adb9af7dcf14b5a21cb1d0c8c20829f902f5cb3cba2d3aa705f6dd70a812ef571e2641d794a52aa575b1eff7ba75e0108cb1455b7e725f6299255022ef41b4b1747c43f596725dea53d6b4e431bfff009ad1af4143c20cf6370301516a8b9172b9133f3f924b146e95d960d95a837142938f647a8ee341ecf52448613b36cd7c7e2d33405c1f931e17fc7fb8622e830209d1830ea8f3ed65c544f1d0851cc7647db121b4f15bdf6e5afad37d4accfa5d67991e9dec26d059a7cbc9c22e51042e13c1bffa7364c9302c502b7680c1c098064122eeaf8d7c986fbe5e12defec49ec61e43e2708445e6393df995a94ddc666f1dd323fd45853638ff61f0f65d3b273d213e7a006098c0c495ccca8fdf649e0e6dba1840982ac6fa367620cf9b0f1e48159ab822da05d248c4d7c748e1067f3ec6340136bb7bb89f2f67223ab3d06ff27ff8a9f382d122c3b8250f2daaf97997cc6215fb62b39269e82b218e8fe4eddc867c358acfb154ca425ab6f89536b56240a69558a24a9f2fe833e2cc0cd6fdd6c7fda67861212bb2aa5b15b7f3604d20973923d92500d69b60d292916aaeda3b2409f038c7ced9f2821d0aa09df325e8ee4deb25695f107a199d52075284bbc500a1d8ff500e2a29f1af08605ed3a4dd9fcfaee896cfd508093ec0795a99961a9d8b7a7ebd429c142f1a52935aadc417a1494e16ccd582a8f606a27cd0d699b98ae46be499a9be50979783a2dfee266702d614bc17334a4fa5cf803235c71f610f627f5651c286c3548390f4864588258a4fc5bc704c9fc04e9a32f29146aba8d2871dc62a779772d5b0e60fbb9c5a3067f0e09550230774342060cdb2f0b888483c433df32452ef6f972fdbe85137cd277dd05dacef6da6ce836069f2a979d656e3123fa1f583b0e96bf24b9fcce569d04a5875ebc5ad8a757b60d1955b9b8d8f2ab846882f7264d4b958a610d286f0de3ad78f3e8ad2d36c9d617fac1844665306220f58a2e078a3a7b9beed30714b1c98ccc97835aa72fb08e04dc73f09b52f608ec9ff811866da284ffeba9b1dc991fcc9c1123bb4ec9b40b22e1203591edd3008336ede805a586af5f55e025e0c5eadd9b1e15030ce5f7b90820aa339f10bc3c697d3f87e8f2328d5a9854509969718a3677c029ff42753b0f1d11cbbb6a8301816ae0e3df81c6845e70495dd1f23514e899893f232e1ee3004212ca190421bceb21670d40f6e24d8ed22a77e08a643eac2c2697b3b4a1b024d04b2e31b4a594b25bbad952af7746c5cacd7c547869157bad1fdd6c6f136c1d96e60b2450748c8038b3f062310ef969ccd09d9b23214c0263a7f514767d51264ef41d32d69cec979817b0fe3a7392e4da263887edbaef9bb602d2a19e4f4f058c30dab386d374b1765c9d0f63fb160c333a612f6b619184df5e80173669b22e8c33565af82730493c4584f6b4253ebbb3c47eff603e26a6f2335d24a180c4044d9915eba47b365bf8009b9ab3af799ac5a92a2a7591851237b12182e052851ca4a9519b6c3ce54392dc00e31213d1987c972c8ea0dd5f191b74c912773b201e900a8fee5f7ab2a88f8f7487b31f4bc93765698d56df2c5a1f381034986add1ad05db3e4d1e4f09fed5ab1e4bbc7c4472041c9cb4972f1332117c2c44a469704ec71515df6ee7073afb4c5bfd91aa1a3c4300d3c7d1d5a760ff17f984d10eb092d51b9f8b619a50fead55d961176e2d127c4b7aafe0cd618eb5e45d2f1192714c17292f9fbcae5109189c56451ec540ae5186160d1dbb923a30ac966cf559829e60bd688a491f036b7496acef7d001c40affc1c12a4a14f91627e95f2068390f644ca843c7c7f885c85de7686b664f037ef0094cf81b7b9d369538267872ab15c1ec9138e8000ff1b5f093470408765a24606539cd61eea3ed83b3e4b409da080691ce3c77e782449f6fc0013f835fb9801f91fc0d909b4c0507cb16b885e619a6bf0facdac14fa8fabad867299b1e0e15987815017e5ed11a2e41206148422c140343c29fc377852181a168f9f8943e2ef69ff35232bdc60c4c2ce9e1008e20c21970bfb9159377cdb8fbeeadc9ae6b397ee5fea1891580598c3a6a69073e05a8c0f4f50c97ceea01f628baad9a9f2c31bbd13571121404a0c47e2ba0e6650583e8c8007859d694f25427a9b11930cc15259f9ed269287beb265de0a2cd5c0599673fdc867e1105b15cf31c7df65da2d668f560a942bbe1e79eb6a0153bee1dccc266a1a93ab21627de45588a6df7a1bd63424fc022134813c81b09d46b091337f7928a5d3c12b844166b15b29f6147d12b226d8d72ee760cdc97ddb6559d009557f41bf25615402879a1123d23c437faf99e87158a6b079235f7dbe70da82af54040a6eaca28ab4887a90a644c8156fc756cc2646924180e33ea18efd3611e50024f90653d78bba33a553272c82d4944b4eeb7e092a0fb811e0e9b57bd761bc388db2ba75b019e1e5356e208934e450437de9c66f044ebcff954dd24fe541ee32319b4cc155fc7c328d539d0d9545719a2baa3520c6f75a3ce5fb920ce0104ce1468873394faa870c861e1b36320b9acb37aa867e63343443d86e30654f92975424db8bd6bad25528cb645b8b13dc5ac11bd59b25da99b002b54d28462ac1ec32e39bd0d42cbeb5d2d886b0eedeae9fd1a2d0bf63896d7881029a6d364f0001267cbe17552d99cd0c6186f834b30a186b1b2336d9b551c487bb1b4c467031871224f6243c5661cf161bc1f30abaa1cb0815a27880b10b3008e06f92d8b6d1c527b4bd1c1d9ae9495f5d2781b9eb915beefd0f646fbbfc70dc6a1e041de74280ced9871585e00e4f4ed6c7ad09ec3cade833f314532383eb59fa436bcd7fd13c1bf25e05097498dd1d8b5c10a7c03b4ec44d950b5678ff3e36f4268873f7b243a5beb6bde7464b980ca4048b894db5be0bf4bee0dd8f035c04c39b8a364162dd0c75e01f65a4853e0dfd4cd85d9f2df213d090c025227b1a604d426da9f420e392217489b4d6961f5e059abdff7e19b2a40c5fdcc3601b08fa591e15625ec1d23b784a1383efedcc10a0f2bff1ed1412d769eb62d43d2b3591e25449b46ec0b81440c61e8469b92bcb3e63cdaf153fb51a3ae7c4899545f64b3f5b8bd0da70b9be9795714c934a47ca512b8de45fd2073b37128e0679554c8a1310ded4009230401de2d429b87afcd8bf213992707db742de262d29ad9b5d919ac8c4aac2c195b1c16e8a75e4bc087c79a18b386e8c4fe7c354b39581b5271910be90c6e6e183cbea1329f34e6d42fdbd9f87479e806eac3a2ba72d1edf68d801e1e234bfe0bc686d2da396aa64f6e45f44c0ab4a98f1f27f98fb6075f4a8dc3ec788aef5913446a33bd535b8e18b788f0d133ea6b614f51b42ff22dd904843ab590f371fa17bcb96cdf86658d2fb52fd18d7387f54476ef9af4c96fecaa2767c7b7ed8d3e1fd053b3d7562a404cac627846b565e56086a595f3e3939fc5820ef8e132d0652c1c4389334c37a151b076eaacb18cdf52546e7f6a570b8dfb051e29ec156fed0ab39db430469900dcf281b81f4b0b229dc65cc1cfb9ad673ae6e2cc52bdb9ab152cc689c163d10e151f16aa5838c4410aa49d241c69a90b7c0a70b1ecb1ad4520d750e60414326413f46f13e486712e1ec52d5615542e3cceb0e2699b89a24b2b3bea775489bb6e930446875984970366632fe619a2746a06014ce0c1f321cc158005d63bedca1d65a9b1415f8e3bc2bd4b631fd581e22568643237c3a7034029a5f8ebbc4baf602065fdd8070b7e111a6c9962b0ed6558b47f87420450980f2c7685c703e7a93cc0f9c9e1d7442f9993d7936a337b4342507e3391b3942246116c8007283d673a4f4c67504f06a21297a6eba41f92e6bc7aa2851919d5cce410ea7e206d399897438f3ce005086d367db59c1da8ff90141fdfb83aa71cae9c0e6cfd3a2ce1082dea0076f3a70e9641c23a25bed51928210379a22a20a44363259903c82576c27417f8892c7d2ad7d9e7ad32d458ae13eec4307925f5ced51200eaf15cb0a83be99cc92ce5452d4684130a463e58a9579276b84a528147f51d23dabb1213e833e9aeac5c3cd28957f090493a01f2d2a4bde724ce48a56218662507aa48f1967215d5715f760e70a0e3a5fe457d81a16edeb2d2fea71a358b9915fc9aa20c2a3079422d7eea48dd2281e606d81944419c14561c8162e2be05721576cdd4598b653cbfdb069ff3d659bc500a985de13975c0f03b751e5c959c2126976e6cb104bd8e800e18949783b4428475ecbc87a0381341ded87dc075f18a1e1c94a43acc261ea8732ecfbc98a75f093a2714c49a93cf5fb63d8e8919b2f317a543766769585be32d9847670abd090d1dca7b02362d09c5a9c3d5bd89e0f12b8f35148f2741b38debfa80d8bfc9b3b3304a7d16a0334d67c15bd9e59b63a5094b80128e75f1c72fd4869cde46c22a41f4e928bf12c07ed7f1179d6901d7eb244ec2b2d265b5c4fa37470ba64e697ce2b512843d2667a50129b34e218c1f8c1796f117c3d604e26f41df33b435c1f2b5a223deb47403f34337ccb3f103d27e18b9cc27b33ed1cff29f5196b89592d1093f112d8ff251caf6fedb445c2d83b50c0cbc652286df8f53d7b692398531189e0b9885f067a48b28f56720f064b09e2302555f53f43aba84c4fc5d295dcc9424bd891955ecf80497e8016c875f3687112bb8ffe8fa8e242fc2f70a4dd5eea97729f7124faa4dad5c3b55add76b938005d37fe85401ac0b0165e58f3c2ed63be8b24091d43fc90bfb9760b1399b321c243f86d81ac6ebd2c6e964a86f2088c9399e310c83df00be01ad1567c63f68ef1396fa0625b6e1aa243560d1a4ca16c9c7f654238255854d680d2a47e39ecf161a5f9739b80822204acd81b0aadb4f4ed5c0ec01ad655997aa94be0350edb3d02201fa3856a2adbcced5a73023b4ac48b107533020841e267bb92ccdd5a0b5442855ad43057c3e50ce71b6207b9eb5e592c3049cbd3899d8b93659152e15a3d305e36a8cf3b8d1d223d5741cb7e3e5409556dd3857dc47f762241f417604ea101cfeba0e4e49430f12fdcdab2d2bfe41e03ca0a6d0097370ac820a85d44643b23063848bff646413831b237fcdbe0dd52f46157559b095ff1e205e13771a3d2000ccb559fabd0b57a7b4250968aa3aab83ed4904066104bc0aa30f674d268ebb01fcd5980db3033877a5b8b8a347179a97ae2980a1e021f9f422b3c73186781a1be5d7b40e9994409acb7c1ecffb33695a4a2ef950adce004671ac200d9c9ec11fdf0e2045b05a11880de00a6f543ec757141569d140848ce721a47520a046720501a1d27ff21224663b017936e1b5f42740ba5b14183c8dfe9205af97bfc40c2df0aeb19d15d1e0eda8357834c30ade0a39e05cda38e4d876b6b92413996d1112e07dca6f16e69a42e18f625ae4878ab71324d6b2bcbcfe94bca2d7cc7658510b2d810c41833fb673074ed5bc02709ec24c5d1a8be0caa6dad7b8e63b8f43b11feee70fc19f71786fdb92b98cbc9acf462b020be1c6f56516eb5b1dbfe5c5232cdac53c4765f5d3fe3c2f23e56bbff3ce4362f55aa40426a62b0792d39ee5370146c50e27924119d7fe95869ea7b3cd20553d2f1b8f534cd244d6db046ae5e303d2d35f245aed74e09d173515b6c8d7b04a58a9f8d7a1fb315e65dd5a1596241bfafe81afb4643b8bdb63448380c5e560a55aca12785f1c1d8890ae344d5f970d28cf753227028b1450a848b3f4ee01c7d2152f4baf3371d0f0672d9dafddef25051770b9c248ce4ccbd7feb035765b83270bd8519dff799a253a19a70f9ff316c6c4523b65e011c281e6c01ff208e9348c3f5042e454602ff194dccb84413625b684264dfb015dabb8ac6455aedeea7db7b17d62a93e2e697233ce7b16657601095fe4ab7e752fceada9f771f780bbef5aca23b18f28930bf8f4da3b87125a23030f1ba1ef7ae5d0494e6d014737262d7cc80f7f9c5ea40a667d690ab5eb080e804e248b500cd1ec41c6195b57e6b2be6f006829df7794c251ead90935f0e00217daf4ce82e6ed33d3253f6cf91d482a466ea04efd6ebeddb929e485d29a7617335f8f4166a6d239d67845875d520b2e09b89f3d4149f0947de8dbcd9e53fc04f163b282d76662d28a2d640c32e75e14b62a8cffcd36fc70dc803d686ccbde0a0e56205b59a863fc6c3a04dc12139765da7f3372bc9b61a20304b6c5247a45149c05ed4dd039f4526fd97d260b2ce48324b4c60b76bd78fa8299ff808782e117aa90d0dd2a7d5d97c3cd4584c46aa2e6e5ed8339d4ca3c4c6ab220b222f37201f6fc11a247203fc11ef0178efd35c4b465eed3e1c1ad6ce0b722958251ec380891a83cb4fa4474328dcaa24a4a2552a96b90c0048d4fa09bf55e37460a6153271c1319c302a279ec6e72dd16f68a19a5148fd66c6ea2168041225c8d174e5e0baf33b60269a6d1eb136cf14c134b6cf48103eec542916aa381526079fe55e30b55423a11b8b409970e30304278d10daa1730951e6553df0d14d1d00b4853f12af7bde8ef3b7271630c34548575581b4472ac89942cb99d5c2ddaff1cb0e7df23ea1eb4e1a01a2565a553672bfe3e4de8ef6e8221f051386bb025f6b7d93a960b10d5bdc36ac410a77909e908ea8f82bd8fd262fc195f431707d7ea9e9917561ae93a944477cfb8d5e858062e03c0dfa36049dd6e08d83b3a6f66bc0cb3724b2d2cf2ccd32c04528207cff392cf3215001248b480cae65bac809a60bbb275f02e837fcf8601e5e49f4bf4e9904965177e3fc78cf54db4bf5fda8ebe3adeff0a0ee52bba99ffef884bf1c679efe5c1fdd82bbfa9118427379bf7319c6e403b3c15212499fe05d579c5bb29a9647cdd504affd929e472c8c0a765b263e20412221a4143e8ecd008005e4172a80864b481fe334eca6dd6345916d61c9dc53e727528a7fe6b5fe07802c8255582348f0f89acd339960485ca37a3e93db8cba2dabe0429460a225d9c7d11980630ac4428a974b457a348bf548a1bb39028ee90e1a72aaf8cc0dcf1b3640cdfad8bab296de26f2c27f2ba59c10a5ad9816c6d135f2f0e30b18b389446ff804d15028276a2c6cacff95ac841df51cae506041c2230d123ae9e84f21d4b8b035f0c767d2ee38a181a6cd888962e19561e41810d25eb530e20982647f2a6c88f8a042914379e4cad105da371a9bd7d018deda79bf0179f09c7c4c22c20c17f2894a3574df4ea32086a99ca101b942eff7d9468299c2e64216b751e6a1ad4647cf14ea164d2afe1dabac100b74f72f674842ba39f41a4e421398d5f96385b6eed0dbdd088ece9a2c4883e1cee362426a69f4f00ff868a6e1ecc411663ff9029247d94409330cfec1da09ce738137ad51659eb54f164c92c2866afcaec6a312524aa3ae724f3821d09879c8b2964f383c4aa5ddd70aa504601ec959d30178335b6c0534894143f89173f1c2178940bf93fc90b29733d51f315cd28ec05cbbf19046ff479fb94699400de6e5a0a343e750ef15defa06587d72eff79f85d1f409725f62338aa1b73b59fd1dc64d547bd554fe0e784aad3345f178209c19e7fb529cd0732c1d31ebf5062c79119f4c5774758a0d3c090404ad5187b5cf6ede0b0c3f73527cbba47e2e133a7238549a59e7fa5f9c52350f0c4b8268b61e4dc217ec409f10cf07ca353ff16e7c9ce15d7e4b4704ec4f5979fd6c1217a56d589d9d94a594722984500ebf99c141180bd471dd2e59c1b5161c894ef25016ffd3f6de21ae544e50608de70afc1f10b21e2befd09f3f729bb417975032233f30a13690359b3743382ea3a61de248a0b44b79e72d6bdfa04727bd8e2181359219df4fa79de9bb68167143b18460063d3263eb948f7f3e4ac55ba1e83e12e8e03b44059d8e7aa97f9b8122ba0f91c805420f2dd299c8f0d40c74a928154029df76d0084dad12b3d11e65527be81fc8c15b50fd3cd13da6eb84708c2a34d2ca2f10e6789c1d9102b61813357d37eb282352af21cb38accb85ceabd719b9717a606345f550071685ce5d7b4e8c49f2fc1562a05ae44786162380948ca988121e0bd48e7918d566279898e28c3456e9f48b7c6659baead75aecaaf48470aa40528c23d9448aed33bcefd7605f2bbf5daf4ef9774c6cd73aeadc18bb451a7b325c37bfe73729f527cd465eb4bd5dc46a094e6e1aceccfd2593198c3d72c66c40950bb4ff6f949f4e2e3650af16d58fbe21282016c4f786f668a4591468e7853126e1d414cd3279c13f814803c68ac17807d86d493d7400ba54bb4820982c5e1072dcad6588b1f80286e384cba4f86c63ac2c381e9a82f138c5777de89601f1e026ba4d12f3970f0c1720a1174e6d30e87f5046717293806db61ec712f2f2af9250eb7571c5c3fc64177f78cd7419798fc1e3c4cf81b5855af937340dc9d2507260707458e85049f268bde1875f7ab5b0450f7c6d2f3a2357aa2f2af75be18ca22e07db0f5cace9cd5c3bcb7d14c3c4229ec33415eda000f200cfad4481a74e000f788fa9d0b6af042338db2b28a086d75d573886f76effb2493a0c1c6079b160508fec14e24aee940397fe51796ba8715c9edef1e64b0276e2e1bf57e9da9561380ae3b40ffc99b38802d763391b64a4d6f06a437da9b37801718960de0299121b661515752c04b38002b3d050dfd5e73bf5cfe05c777fb84b7ee06b59744de1cedd3f90abb9038e67e34502199b94711d7991b84f6fe6d97415a66252da87e167df5b14c7fce6bff4fde042c64283b7fc884405dd880a9ec579d40882f20bc0bbb22bc01826b565f74898cce22f8dcf8a33ede13f9804b5d346746074b619014de9aced457d5733f04b24e3283e1aff240a5c99b1f8cee22c886fb5df77b20257b9abd7bd2f337b78294e966df5f644251e97c6484692cbce4a066876cd0ba19828ea5e5f25f0e4dd5c434d40514a86ee67443b99f5b92f48a046044e797a9120fee14c2536100e99c34111357a8561ff2275749e362a783dede851482cccf11ef8c9cd95200ef80fddf456a96c065ecd15f98a62fe1db7863607f503abe39", - "public_inputs_hex": "0x1486143d0564ca941cac449924e8ee74c1b4433cf73a6b4920737c223ba6b1e113d3a6e1ee0c2390b252eea2ac285eb7ab99ff1ac03a2a446a00a036dfa953780000000000000000000000000000000090331489384274c12b43fb7cd86990be000000000000000000000000000000007b6b44c05ca9dfc4194f954eb77bc8f51e9919742ce609e9e07b6a91eec28f3d0f8cc4fe94c8f02f425b601258f389400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000505f1a42155a63345c51416db9134e24a473534e1bc7264e47c3d8ceeb0048f3f00f750355551fc21f4f8b099e8aa9d146476b5a3eba3f12ec324353d52c1498e0f0a7cb8a62c14a214735dc01b43a24c55e0f368bb80c5740f91d32c79b109a902adda50f3a68b086921840211e0be05add7ad8933f82d689d427af65040b9e42190c3823163063956218b83ca1ae19999d5986c495dcf68fabffe666e78796909ec0921581b707660aae7e03a29bcf5f6310e32bb1ef1bfa21f265e211ea0dc1d34da8f75be8d59fdd8e87f406948e636b3c1dce3485b81e8db02be6a2b619d04097be17ce35a4c426eea7846e3cf55a6ecca6207bf3e1453d1e46a962e47c704c3f121cbb02760f51ff659d1a9699301fb413fc3021b4ffff5c1d00a7b57162a19c999035857bf3a4db9a179b5166dd723b3db90b27a94e60f886b23dcd1b1000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x00000000000000000000000000000086128b152735e8da12c1ac033ec692f44c00000000000000000000000000000000001e21534a3a2232ee90f8e2f9ede57000000000000000000000000000000089e9c48cfcc85b7048d5052388b110bd8400000000000000000000000000000000000aa9a9e43aeda54a772f9fc8d5d9a2000000000000000000000000000000381311f8eb7dd4445d90726bb26fd72bfb00000000000000000000000000000000002dae99793be3ab745147e821e170690000000000000000000000000000002ba60584ddd5bda982981460e1215366c80000000000000000000000000000000000160cb4d56a8c527aa83d1b9d79f19418cfe52e051aff2cbf53b9cf7bc2c5f3bbfa3d740a22b2dbf949800973f546cb0e4ca7825cdce5dbf21c2fb42c29769dd166352d60506007c52d2b57015588762ed98abeedbee9e187cfcb08268ef03668a382c81605e774b98b5e3b9a655fa52b226ba7eba6214be6c69f68bd6d7a476570b05544ffbf4e938198cc75473e2313d282bf8d43940740d15e6e94be994b2e75e6d4697adb95d145160a76c8cf151f7cdb0091edcb16eb8e4f9a2dc318d1d88fc24c176b134c8a87518085cca9bb1586698712b45de27b1867ae6e746b642247c57c88df21b6d4e517cf5b6beccc116f67fcb2eef6e51e463f45e68751c0964871d704aa7a64bcdebf54350fa23c0fd92cedb564c7a4e7a9a232478f5824c552a15c2840da9874adc0ad87bf842b1d546b3ec948076e8137207b748f80eb414866c033e9420492282a8d84dfeb691e4902742254a708163bee0a0c5fe53207fff179e3b5516aa3831bdf003d38c213b7169efeeea6f54c5763fd246bcdca76e0ff570cae3c64525480cf29235c821c2e5da08e84e646fde20c21f493251da8d53bff3d4ab6609b95a963e457aa7e2af4c860544a0cb6a77038dd9ceab55c12f6272155083366afa12e0867604c8807d8bd5e7083e3c078ecab1b51ceadf8fd1d1788cc508613afea83bf9dbf78a106be4242470ac1e254ed76019c06567e73aae6669962ed40efb469318906af57117c534b16c0494f2f2199fcedb4f95b89fe361dc08a5516d787d7f69a861f4f2d99cfbe09a264ff864903885662c0d021ec94288bea6213bdf757ddac8c19bd16552f1c3e101f82e34c4947f0878949f25f3137f90c410433144b8778070dc7186d9a2c8765e0dc215692929b15025c4e5c5ce83971f629fd1c87795babfa6f012f250bec1dbcdeaace9d6a0d0854a023227b1ea286feb92f048fa7d360ea6e1f7457b99af5360dbe9936338600c0249f677bc174b33ffa80a0dfd7e793b42b044ff80961b1729783f7a20955649e1fe26e96b562fd04aad30d5fc95852ad831e8c993d26bbdf48409a2378db4d1b2ad79489efde3e5dd2fd595fec9bc7dd381323370dc9ea2318b82bc430f9ee4ee7d401a1000797f5078e89a528cda6a0c40fb00d6452f8a9563ba70002d6e06ba7713e7e5bc5da023eae91f0601c6d87922d91b2dd46b7058075a3c115044e021f8971a0f8b9a992c48e4cd27bb544348f1bc93830689f3e58ce58796d891928025c4996703b8b0ff4b781fd1bc0da62e529a471e0b65ef6014a0be6c4ac54b584e8b14665a3ddcbc742f3caee1abe0ef50469a9102cdcd2932ab106bd7dc714d58c881b72b99793c89bd653722fa7818c04d18de5843424fcee3f1661dbd1e80080abd0714df0f64309ea774c968e10780597b2ec17490a43deffafa29d72ccdaae56be45c949335716e9eea3553c541c27cd3d58bcc3064387554995f8430eeb8f83ce263ac112b171110609b1e1e5540999ffd86e179dec29e8263588668c8c6221496ed033e3df624d4338a91fb04e2b300452140c43b468bd264543bd4d5bc74b0da98177adf0823a2c58150de89314a0d5f05f246eec7f3fb65a30c535e05c1333e48f0425edc67a5fbe38e7d70011ec7265be12a68009cc8cb1b7629eafebadffa0068200554056f974e1b8f2371aec6f327dfa4917ac0de5db60120df4dc673dfc69ee630e2e1a4f8430f7442c26d6e8f37c5db272df37c072620d76b953517f6a15346defac4a420780015bf602bd175239ce3c2232bd8824f0993f6e058e1637fd660b940e5924abbdf7636b265c9a290f11679e7901c16f58fb9e979eb7bff2622ea0bd181f0b287bcd735c0b2922203f180d1f1fb62f1c75791a7fa2e8acd4a3a43d13e5719738dd945b4b1c78cce566c3ed6463d3bcd4665d502a3766d6bb72c3b45768ba530ec5efc4dd2aafafa6ee4ce6d71644239881e21146ba49aa1111e35fb31b6719d35d3d0edb2305a0ed541979664939cb34948e7c6535b0f71a78ef2c77f537ba62d4ea35412d59d7c5fd6ee36467a9bfa13b153106c726746138e5cf48673342a410c454e70c9329b09cf2959cb46de0e5cec4dbc2fdd02aab300262ee370ded6c5eda07582b435acc5c05e7447c69fbc1a5569499fcba5f00fca839912a719b96b9126c050007abc11d0ade19f73b8164b6af7bbf0fcf1577ee11a7851ce4884e0877d5382b24a56e4af6f87e5c8b663cc90ec4b0b7e47824c6fe09bf9db64f2ad98dfafe289a2137db1850977dcdcd955aa791716125cb1455e67b3fff3db37de9043c48095fcc74c139529751336785887b24681818a8990658e592a5786f025696f3e50977b2a78f1e47b66823cef57b530fcddab3cdd7053cc8016476d704d148a8cb24767b4ae5ce1eafa759f1adf8afb27d284d4185b1c740c1fd55b3ff0b1d4e9a0eb035845bdefa603034eb26d0c16af3ae78f76c26aae6b77821cb8a8f03fc3617642ac055b38882e3aeda065f6d9f5c282c85abcbea9174cac665e79b5cd62608d218d55b3062f00b8bb644a76df017b73a758040aefd3d2ecca53c18356d9d26b92cebc3bcbe270cafff6033f063c559c1e2d6ff22b8db957e23b4cc1965672acd9457bcc0d57c9f4bd369541060e1dacf7a323aa9cd783af02c75498647740e217f464a44eb7d2a098eea75a5a760704fd1b6b6c464e7815ec6477332c8c917e1f9dc0fda484876d44ef505248c28410224ba5f56721b5fc088733d0f551a05b99bf2b23006842e17ac626cacee1ecc0f7f843b05bd1da95595d0cc6bf1fc00e7042038fe938ad732340226ab09ec6c9ccdf3de3047da97facc7f6f193fe70d9c822cd274abfc6cb6579120d265cb0ea44df68a0e0784f2e770813198f1920dfaa18a6890ef7de12bc8e29f929bb64d95ba6dd18e930e651b48e7870e2cff2c219a5d638a74a2e94bd77e9e56dbe01059d7284d97a076f9421f8c80640b5c25453388d43dbb095de6b59541336f324f8c5f47aa6b47cd9496d1ee0efeebde1a851b3af81c4ffe342d06c10a44ce98d32be26cd800b4e79f2ba3123989a12f0fa9e957f9961ca43baa64c12b11619afafb83f704cb809562dac54c0798045017c6cc0f2e42025d657a62bd0e8a2c5956778158723436c2c935c5b1a5423de325846c73f1d46c39cd71401ebcb2fadc0508687b6d49e64a50a8594c508079da1df79722023e73216a83446146e03d520c5514793b84b42302e0ef74044baae220b0c1373641637998b6a1555ada2ba65d3d74758e6b2f45844cd0fc381e4e9b17bfc22a65177af2d2d63674b76a2d52dabf70d381bb355144e89a76a51114cc0a040ff9255119c53e38fd24214d32e6fc347e28e107a4852edd555edef57754049d85d3b2b751a3a98ae7ba755b5cdb4abdb0ac608d0c6db62bb6748502f11827819731f1495ce494327629f18c5ac82da5f225b36002f47af9c56b02b4dbdf259f21404b085d148c77b8568fe767a336367a28863c24286a31174fb23fb16b150c6b8d89aac494db0abe8af5487e4d6bf0d9e81a28a5de8eed92d40b2a20100e7751f59e9731e1c9b6682dc8690996f465aacc4f9b4a07fcf1f986d907d6d11a4a2fa44d50d3376db347c4fc3d5a89ff6ebbdd9ef3bb2367bfd6da52d2a2881f3f1f42999c042e64b96a3627701e5199cf4a48b63e315401864767a4db3e2e1245735f162b8532a74f7e5d4187d01e1e9a22a08c8e7bb6a71970df30fa0cb60fd134e19dd5e6868d8fcf2022d9ba1906600bc242061e6e49210e0869c9a7d10c0ec0e23b7830779efdb3817e7a4e2f480e9a2574f7944fd9fde66028edcfc21d7598296da352afe2cdbc58ef94ca0e9d6da2a53e1c15d6fbfc441ddc4ddffb24671fd74bc94a72a2965be254ebac6025fba6ec59d98d2e25c5c9da9be9112e13cc4ec419e9d97ead61921cc0e24e7e4af2d02d5ac2f6f51549a19b43ecf2b6005f957ca966fd5a436a327e42b5352d45bf76cbe983c3ac18160be26701d7c30d0160484581f9de884c34d5d27d5e16fa12c45dd495e222db7bb3cfc0c13b6b1f75a45c5a5048742d57a191d60fd06afe3a1361a8f2ceb17bafebae67a5efb602f8312affb4cb40aebef3e6a34db5a2f9924086b7cc973e616e9f4bc62234a22e97ce2bd427922283b544e46f9fc6a5b5af4a65d9d280c9063e37d4f8da9e600661302e1b54ed5323e1d086afc796242a30060d4059603cb7a053d61fd8abe32e870c4e252f36f4dc4e49943543d5b9eec239aef5b26d493231b86ffd35289e2953a262b2370b819586426a129145e77ef773c4e3dd3e063b31283d6a838097191aed2bd843f1b8da649a499480771f8c88b65846d3fb8b55469198136344451c00007d51332b0a1fccdcd10d5791b660b4faba9943704123b533ae4d1bac7a2f260b34cc9ac84c8b31ecf35934c7fa38738940b44f5eb0fa12d530033ce33e2efd89da52922bb93e6e76f7a1d0f78549c1c3acbad376c09cb1f1f99032859a1fd676dc8c4aaa85ca5aef8e4907d8b6a36a4a72e820e4f3435967a66c25a06d1e2eea0e1cd786ad4ea1a1754673376aca17ada68ca2f1a78193c01056b70b871e8c63590563b83401ee02361e719ef7a296362cc6788d1267b5e5a1d2a853f30ba8502bb06215506f04e23a063cf8688ebe43906eca116da1dc1ac3973cf65301864399e69c64fb731b35efd0c61943fec830dd3991bfd4b3aae29d261a83141f6a02e7734022a410b2d710e3724ac4f16d004a16b09a66e5b87985d05098f02bbfe1d024aea55cbc0276811109a3fd88cfd32377e362e03f99f9617830bdc20dfa78a8d674c2cf707f1286e0812a3360ea31d57662ac380106816dce99ec4820a3d9130a8315b42a246100252f643a77d921b4cc5da77609c7c3604b64058217bec2aa116a340fd1ad2da925b8f89f73af9f4ba51ccf6b6482f9dfee2702df2f65334f8fea473a894d10975289ae7c747dbdb4bd8f4ca6a1c4a3e4ddae0e94293904653047989744b35d9dee0320e24d339b2a59b398dcf08064ecc0254b9f2b2da73f0dd7219aced9ab6b11e9dec997ec9a6e45588e150a15897e417eee09126285414b8c15189c67fa8dc408bc0105fe5e562bb07335396453ce6e146cc12f162ce08f80b75062366e7e02bcbfc94ab48d11f3e425af3139e14b2a66c55f0f3555ff358e43effb55ab3e020b6179b64a25d7796dead12eb4dde28209a93d2b4b2a6b3e8d59ad981e3332c84b7d7b35beb55d3e856bf2024991217ac4e70912b5246d375b121bb943a483916fcf68f9fe465ee5e475602a8f41e0bfc9d02d019bdc8ea13815142c01fb1d48615664d3ee7133f2dc8203fd65af36ac4e9d9c146f31d3b50a7cb505a9fc377ece631b6aa50494228db876fe380b2aeee9c1a5225965b0e0f9843c7ab9915eaea6712b05d99bcce85b076ef287757cc36c49272c3a70962c6d007a121a23cb4103bb096ba5058c4be409e9754c27d2c96c073d100f84ffd0de3daed64f3d9d4fb275aa5f16cb657834ccf1fcaa272bf476b2f91352607d4765e61aeef286517f2a4e36917c75b047605b1ad6d2b584a111ed4e114e01404ba1f03a1584484a32da6593df0015d736d3141490dc1d31006b81711e75b08410d934c1766304d2c6f7711b7a395351cc756221eda4edec6e482b0210d8e3fca6bc4ca028af348e9e7b795b8da645bb27326f0be5c9d70983f02be4186e0b448d51a564f8c89e3327586f28daa4ee835003fa619b3bd94cc8cdcdcf264b13c49cadf740fba472e99183ce6806dc8187b7308b427f3676cf57461a471ea746a15d459ada3d1a7bb349eb57e19b09f67f488fa2c82db63df69d5142d5171459583ee099325c72eb43afe5f75e9ab2da951a3a4d2f9599c4d6a19105a124da57fef420a7df1ba47bc647b9d47630da041559729e6c52e052e3250997860722414cbf3c2a5c3dcd10e1008c924952f3e86fc01cf1ee9a7d5615a587f41304449860b2818771c0eb0ea1ddc0b530825004894342f0a8be32b8c9599f9faa03fecb26c574bfcfd4984d1c74f9087d619b60b412d8876bf2d8058109ca8d691aa245b4fa5a15f7462c2d4dc9589f8489a3ff7d0abfb37beae97693b11b226427bb265b4efb7617b0df974fb3bcd731e77efabf8e30eb9e8e607118c12740d50a1071fa4df6b3ad1b1e7f8106a7d51ad565f5509ff3478221620914e9ab02520d4b9fe83400af48552c4b958051805049649358688037a2ebfbe6d2528a067f29988a1fd0043a7e9f5bacd4fe0ebb1b8be35d989a88f1fc94e4d720d944d513242b066e2d627f1c1e48c375b9127c1d8e6ee3b16ba926dfe047d4a5580b3d4b10c74775813a94b810bab26329bf31d5712537bf8e35ad35ade0fc27392d4a5311c92f326ceaa71fa83d19b92838fb6322498c2513393737ab58208a5041ed1816a473e145520c1c94fe863a8e240e7038423d0410dbb3f907426e6f505063970eb8e2c8c4b4ddf8a6f3418b1fe4f52f261cfac03340562053da433427cea2ff00358df2c6f82e3b086925f26360b3ef2e884f939b3f42aa209c24ace0eb467915c1ee10852c10464682409f02ec7a0835846a36cae1e6fdabeee4df631e9a5b2a156b344ad0561f098d3be4713f3af988877de3b0a801c88e642821dd15707120a49d1d331bec6a881e8e0d5bc2f693e9414aea0d0c89e2cd66a646ddba083b20ee63c2142a4e95f1d389c916db1c30c01f1024ccf35562943acd2b2b2bd3852c197e8bea301c8431a5815aaf8c14313a246bcef59ce31ef4199665ba740e7c0bb2ca85c164a3cfc7ccb8065af3d2fe103c7a8cbda1178cd06341dc6c99fdc60d961b9f04329bb04609575de89ed4b7380efb8adfb61cdf16b596d773fbde6f235dfd8cd8e397123ebf7b3327dfc60168424b7d84b6def20c11c179fdb2866b03c518356e82c0f755acb4bf2de5601275f8f3ff05f490e0e8bba26bc89b406116a300e1825c144cb7ee293a31d43398018ebe0e72818306e157a8bfb925c92621d26112975ae750726251419034f79e96dd36cc31384b99fb54ffffb032b3612fd95b004e5ddf91daa3d9a7bbdfd9363d4e34b3164a66972bbd759b28ff984a0af63902ceaa89ffafafe65f8fb12a0b41e5cd4643075600d1058f13a1b60b8324c11834a71ccfca6e37bcf29c3582c3cfa59a1111f1650f0f98f555fe8973d1239769fc7f8378cce2bdca2e86ce80a4f309580c0fd094e9c17bbd886c1c07140e0dff336cc308fe51fb194212b7789f02e77c7e0db445ce350d9a65002ec634269bc53fca2ab8436e6a6d07a8c4ccb860d92bb26656657ca8e35df6d569fe0f0e8cf483a4e3beebfd8b2f2882317baf9072a55909691ae66fbd2bfebf9b3f320737caa53fd3353160b3d0f073ef53ec6f6aa819db3204024e4226e88edca1c40badb43bef4596695b4d92ef6e8d59fe2cb1f226efaecbc615b86a94a9066d9e1e40c322df46acbb6aae4010598a7c4837b885e725037dc5ddbc3eb51fcaae0f1fbbc7719dc703dd8c51fc3adf0fd233ca097c4e0c0eaeb6dbbff0c50265435e01071e49d685295034554b409144b6bcbff311177c65c9f2cd359fd5dbd81bd220b843646ad6bea3779f2c9172198ea6a8c95fb5a401174bcca5ed573d193a902d7cfe239c6dac4eff8640346af3364754e1ede6f43b2ed0239889cd4b95886b2b40d17a89a3a1567a602a23ebfeaf17784bdca382fc7517bddee037e90de1ac233535507858d8d472064f19c2acf97b671d1995ce50b2a1733de3e85e51a130292fdfa975cbfb22a7fb7aa2c98ac257a4da362592e7cd4a461a237d3da55c8f0349a786b514870ff8b802fcd403fbeeb712ff48fb3826f4bfbcda6a3720592820f780b3fe823de35d8d667fce752957734efbc715624f8305d407aa4bf7a4fb2905aa57df6ecbc1fc2ad73e269df78d60f8062ab802c2435319de44f21ae4f621cb51b81e8fc5331f57526185628477d3c48f182058afd34ceaee757fb3328701700d04bdc8147b7df87413863553d308f2ffe11b37666a45576c4fbb58039b148a967397fc8c78957e0b24228a0c3777cc563a7c2aa0f73e3af265b560d4c226b17f8804ac7903c082ce419b82d4171a8dca5a185c7c8be843f15020f7575609f7a8dc6901dc145bbe1ea05ead7dfb20a4b7661dcd2da5ad7d9789efebed27192d93b959553c1818ac522beed93609289cbf13fb231519da8bfc4117d3900d2c994a2ae85bcfea4e26bf1c079f7acd0f5a8f006150c5c1f31c5e7220843d5f0e2e56be61d24cec741f7e09d6acb499b7503966b9b2e77113c8f4d54c46d81e0311e15ee5e20ed3b9e8157f44ac9669d695530088a3141473ffdc597b1d02e02d630b0af564405d51e1da3573ccea4937cb1969441fae7d0de92d0cdce9fcd70e59093c8e8405cfa8aa74c037138c670805bfed4342e2580424bb4eaeccf96d0e4e7bd1eb20f6a72b983e076617f80616ac27d07204336f4c8e6dc6f9654708231d648430cfb48fca6ed8f35a705e9819b0aa97cdd71ef6f96a367fead7d4f72af11e97351be71155faf0d2c5a3d34a85aefbca5c92bf242fd0996478afb0e718b8a0b2c08e0aa1c562c23518f1de3cb0ae3853f8f60eb75d9f5c5f499ece711dbeca14874049ebf3112af22724452c4ca74531dca7de7d0e736d19d762d5010fbe3b754171bb72acf6c2142a8a253fd21268bf2aa60acc1d3b1775b1ae934c094ee19bc450943171004a917bcdb66ded00b57e1e3c58e856f6804e5aa7bbf72d70590e36f6fd14b1ba340fb82b6cc541ab55fb78ca501f35c2a7b3d704108a25af3ff86f20f612705ddf982b653f4beb657b237b0230de57fc8deeeeb64f87060f15cfd9138a8bfd9ffb2ee216298cd61b66777a0beba316fc7ddb60d381f41a4b0c27b0c087d65fa8c546d2b0f25d4fae550393a270c0440de2e10b40f5660872cc17e4c1eefcd262471d7ebf992547122e7291ff8bea890c28c191c5258a305e75fc5247653ea99111627202aa465d4ff3710292e3a12ad435bd84e5a4f322629cc534c31c102988fcb5c5c6f8237d2b909034ac24d751c69348f8a04f312c978939342bf1e71dd7e286ee01a670f5df5a540b29a97750d8cedbebb83282121c39706fcc5486816f58876c39133b9500e964d5da48178f03a74ceafcae1c2da7875200cedb5e602bf499f463299e9db359174587b91cf40856091454e4d01de38193563dae24ea6e3633e778004c4d4ffc25ca26d3346b88f40ecd8d4e2c0f218218c1f4ff602355c47ed2055a1a8dcb3b7e7dfc9f4b447c883ca86fb5820cba62dd7ca17a4c9f07675095c9482fded3eebc24566a195a22f9aea7ce1e1b0ff7c932f63e947ad880801cfb73d2fb8c5541fe97124d6bcfa049d964388c3424604373f514e081cc37eaae54ccfd8972899641c3155237a4960ddef345d7e92016dd5ad4101022a6cdbd8a5728844b285219aef9f25f803efc2953b5362dec0102e3e8c7e9b8b9192a395b04a52b7424b81a5d204e46e3afb4cc0e7233414510886fe2b418ba902add2c5fee7732e8eeda405e40125d76fd0c60149fcdefde11817bfa94715704a99bb304c8198ed21519adf74cddb4ef735dc878b61c12bb067e2237da9d3e01aa140aac4783f50323be215517bcce483a8859641a9a2e24029123179741ed19f08d63f3ac2ebab45bd481577239fc178e1a520e7539948f10dfe8c4d5d47a9713e66fce5918346374fc4825a1157fae57d6eecc62a4faec19321b67aac7f6c9f272bb639c8579d4f368540c68e616a1631d366c4862397d11cc7683a2033202827680af4d48278628125230048360a21627c18f09a340932845facab0662af55a50bddc640b6e628ed43c6d4efaa7cd0d0ec62779b28ac100f3e06d8dbd6f71f0f510ea21955675f8b3457cab71119e7e01c92f4a572ecf0a01f118cc6a40dbe88765a0374d51ba18b0b1651f710cfdbd8237191dfa45ee1e59b37d285ad77d41bec8da69a481369cb38b054a14c95084679602f12cc6c80f222ab0d3bdad4967943fbc5a995dd6f32a0e31b09d310dae9f1c21f188681130487664f7547e0c35f69d0456b4946474ac7a1547e26ba69b634e75ce9af1c0291500d58d09e56d0057986394fcde61367f24b832429ca00e6861abf7eb152b23e0251fa593f555509a71b62f1f8fe86b52465f81070f9e829ba86822453ea321b7b2e4c9c4dab6f6402776ecf71aff06b1c91ab79eb70452968908e0e482982cd53c39a2055da678ed03f8be0432d3ceac4dafa9aef43e33b3ad9688cbf13e16ba8017b1ad83c47382d6ddf0ff6b328626b47e8b32b1ec9aa40f95b0c5c0510bf1236d53b41bcec1a503c28b4bd096a4c660ffe0a9bcf31f14fdb4e034ec5920cc23b4479813963034c2cfdc9d14bdaa89e463cf1c4fdaa4df3b07ee3a80810d97a2f3b6b23a065584be3737a1782b149f3459acb615488b7e454d72dc5cb912086c661ab9b97900dfca7c39b7cf6ca3fcb61357a150aca31b8b8019a11ca800000000000000000000000000000000000000000000000000000000000000002068a0cb67561c225dd67a920b37957065bb433275b4ccb90e6a78a633fcf5e4282edfdb66903672ccf13d2bd4e9fc8f784a1f0e64ba352614ba855b9adb326015cf6f27a015cf5be6ad61689a027e8b7526ce8328a7d03b5d791f8fbf0f86c32548e880f4e1837558a8ee54c55d9797ba87c5b97d1d479871686b9859ea08672218e78749cb163e8a91f5518a85f1d96f22dc7b75f19a43763241b593d2da8f1cc53c386c778f3cfa4ad6b68d06887c95602fb982ef2726fb6686198b6ff8fc15483eee57ce406ec079cc88a2dff12ae5cb5eb3cb21abd5a674549ed6c629f508d6da5aa8d32a24f7cae0e1b0e2ba99cafb810eaa9eb0efe93e39295d5035db1db4e274d3b3a0faa915003bc26c949e71b42a0a7fcdacfcae3cae7445216a9f2b3bee929768f7bb096e3f387b4d727bf6d4d1158f259a661584c02b161484e50a300e91856eb5e99daa3ce6fb7d9d2f352fbb65b8a695f1b1e5478afb0fe3e416a4e42c4d207e04949916c2d27ced4b7067e4058a8f1c51b3b72715fea076152cffd14fd9d52652b9b917c901464bcaf2f1c748d54887cdf79789809377320d29dbf1c6696df1d68db6a89f802e721f78574aa9170f8fd65ac8991cf89495c1126dac0f452e570f28aa148660ca02b950946e0b985ec1bf75bd6fb98c15ff0326454bef7ed3fd667c99a074abf1e613a612c4619cc2c97baef5dfaed2718c850a8466f6b02a01bc77ead78d33b7dbdb5a920195a1946f45ef7ecffa48a61b230574194dcd872bbecde78669fe902783bea7d93c458978616eb1ce5e29c7adf00f9b2e7619e3f9aa49d55e5d18cdc2f057fef5ef30b0466ff0a0cee76870f1fc1f3022cc9e50b074e6c6182396609fd2902eaae0b10a3191a6f2d9ef59eb708214dacab0e29635f0935c7b78a6f12f338ab6517357686638ca16dfb21dda12a51feb2547964ebda05e9e22b5e495870d46f03564cf1027bc78042476dfbd9d5103def1f0ca1a812ef15c8d793f74b0e618645fdbb90aa33a325744e28d59db7a0562e1850dd4b84f15e99e98adbe5f55fdb86ce68e7f8b1df8c8229743d4ae88254d2aa45845079d3334d015198fa4e582a848dad6863c0762dd28bbbbab2e57180351ca16fe7941495e0899536099057fe95e0df8223f8f751b24f3c08cb87f2e8ede1c1674423713c0151f002be591ed048e1f0c463eea1caa28c4af7216c42903c8f07b9a3ee0a6a2570a71058df40658a31f71b40c0b48033835f2b47fa40d03e7560fabbffff30ec2d8e2f05aea5f2e1f47b9d421148603eaa46b0bfc622e0217fbd6cd52397db7662d865b6ce29691c34397f82a2b5798c1b4db66c5ea2189a64b0b019ea7613ca143f5e5deb74de3eba86d6779b552e4099cf2645afa1a80358ed480053b2c73b2da781dd4d2092ccade8c2b807dfba6d710550239992ff49973a3856f1d77fffd166c7c0312b4759edd09237fac28b5f66ec5ee6e5307f1ecf344baa95dbb44f6eeb58f47b5fddafdf145ea08b0250e9143697c546c122bc69b0117468ca755b098a9f8de7d0dd8b39dc47c84a9bfc3911888ae815c25f8776f0cea630d883828fb103840fcb129926a9a998068092c4401d96acbf9195be0e567f48e49f53317d51045b5b65c6bb15476b989dc67465e0d3780b7ac020e6a855b0f34662492a2478e3d3891c1965e7252e22f93c44ed52b774c304113f77821a9134186daf42c6f9dd508e707303b3cf816070b35a96c76f274df07254925a2c6eb79a692de7ea02f22145d9598336c5f4de96d72cc387005fc371318b48ac51cf60f81a235a50c6298309497e87ead7a6e6f197fbb36204905ab122ca4ad9f2335a3456b097de0383a13ef5d5b6a18edcd89b84a435110c072a85227e597d947a0068903234a09651a674b625c15b83313abdc71729b8b5368c4de0e124b1210458489e5534fe28323913c9f3774c31e41dd6a6953a028ca8293b007e341f2412aa0bcb8746a667a9b91083540ce999b50fd86fdc6d9ed73eacec6249a69907c960c709d56e36a7b78070981cb66a3dd763fa069190b82e0d999212528f97816cb7405310730fa4948790cf06a1c39692dfdc67d26348f430743ac1b79387712ff2daa58887afdd8c147cf17ed0379fac66cdcf580a5df5f9b2c4c071fa85a9dd4c2eb657084aa80964590de98a0426968f486b81da7ff79f80451133a64f93c52393d92548164635d770a9bdb6a1b727c457514c86259ae0ac1fa2c6d5b283275f0fa3fb6bb304126b602d2b07d33b8ab7482d48cf8ca231fe26f007e045c8f992ca0d1a5091c467aaf1992a880088de713bccf5f0d22f19996111d9f8150babd565be19510ac8fd07373d23276a288c71650f7f46e116f366f9e2ca0f35ebe7c7c6840af7ac4ecca1d711a45a0b6597ec47e6834ccf248bd97be06d20fca1bddc09bf479961cfbc487a5a6ce1548b04817c91e5955644825b15a097dcb7ceebb24a1c71cf97241e010fef306a41203721ddb6b8ad224e0d2704c026dc222b75b227724df1e9a526269f1e3fa256b0930832cfcee03eea89f4ce92b046a6c44df4391ed52b9af3cb861d9de8d0768f337551479a43acc0019728f0ca1f910a43f3e6034edf628c1c3542cf452235903a3a2a4d60204b12be5d14e10bbde5dcfb36876c51c44b485f085c49720006ad8dd6c348c53f01b2eacf7c6148d1e0f426f0cb38d7d2a2d0f7038f6ddfe3bf0eac0e530148b4326595a0bc221f365cab3f612a0406cf6255c739a884bb73aed0bec6deb1bd3c65bdc77dddf2ce8f3e33463d2af466bbcd48284d26463e711ffff27779219bc315ec1f70d3323516d5d5e4348e5377287797903ee2793b335faa1250a6f716f52b3880600431b7d84bf3d60c5bd00910bd7bda5c5c4869abad246070489776e21ee7dd92894045bb73cca8dd7155cd03da0bd0c8336f7a5433e37bf89da7f50a545fd9087ef1795df34bac738c36547302a65282f3eb4c1662c90df96cd698096ddf90431a11338f59b28e8087de1eb30611d5e385e0c04f97c8186ac70ff4741f2f5a759762dd0c410135da76bbfecb2c78a895fea75542b1f3949be83792a5c443d78ebee153a52884d633efb8e468445c9436afe55ca1638837de0f0ab4a333160956067012866ce8645c5c099259e69bd6733eb5cd507d47aab8c0465aaa417d57c4a8814241f1c64ae13da75adc7983b4a96ff0ed9f87e99fc6ac00e5544fb3b8af3f513d269c75e886b77ca22d38102d70d6cc2f8468b9be6a5d865b0fe1ba58448521bffc0fc8099ab03d04bb1f3d788b875cef4959f1e8338df6ffcadca0e27b08913a794f4a00424782e1925639e3200bac02de04a6528b241023c919d095d3d072853620323a594c97eecc083f499dc290acc1593c4f4d28ea3be7e45626f2b5a2e63281482c002e704d8f523e38982d54f218eb5823f91d5443484efe4eb9aad1eb697e46b11a373f939fcd9dee5cda2bc79f6acd3d1134d6be22aba381a27421762c3fefb855af95184ddac731f0f5b7eccc8e9cbc4b0015a4b24e5c00ca88a0420c70ebbf24975b6924b2dbb924de6e168b59a75af060248eb57c555cc077a2749ce68d298918662b23551c277493f05be30a3521799fd5ebd945b0e4b27751a2262e8675899ef6490899374abe0aab9a21f8af09a62d74ebb7233d9783c2823d52814c54588f5f49de4eb249f18dee7720e7fadb807ebaa4143d5c110d2141d1cca9595aca275f5a123f3b1ec26e014bddf00c56fefa223a3fe9a06910cba17f857b0f4cc5535493429f9412a9c32050ea16b08f8b33004d89064ac1ed5f81728c4cdcc85f2c8e85e98c214262ca61bfa97b0fb69357f422bfda6b810118d05f7bd8b431ef0fb104f045f47b5d4547516642367032d5546d96b4c3a04cb70234e4aa90115d8b641cc0c29b500868d7a7a8de824c387d71c30ec5f9e5b2dd70bdadc269baae93d9cfecba60ca4e7d9dbae1d7240130e4abb9fa486a9e7700b0d73df8a358b86977c7166068d7cd49b385e007b3d3a23d421bc61d6925a3e5807560214c0bb25ffe2559c3a2f5a9dade882fb30e87e75a5f346408cfb599d77", + "public_inputs_hex": "0x1aee18e09ee58c039515ee9d127a1a6da642e12f25647495cc22376fac0e553c1e67015d8203bdd68489734560b59936ab952ee7949330f7b7e0276a348676df00000000000000000000000000000000e6cbb0fb234ee2d6c16efa7300877ba50000000000000000000000000000000071443b4c6219d9646c0705dffc34f20e000000000000000000000000000000002edd7e8bdbfce13b889c575d182327b40000000000000000000000000000000000d9c333379312013b1e5f727a8bbe5412fe3cc456b8f0d0d517ea548414b888c6ac4092ad76e33b2d668ca827e33d3e02419efaa270209298711c1cfc30814a5ff279e39592074604181ecadb2f7f7b0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000528d93f3bc2749c2de32c6b7d48ee70d6fb6af2dda4ba0b5d0366c249712b3fb80c519df7d1240f04c29be318da0dee2544931a9f8cee06be97e71a1c99b1a64e105e348119c8567a1d9cc13e0b00a14f537be9506cf6a083cbf12152513598d121d9ef6109c9ecb38140c915ef89767ab4272d2c4967c9761215f47d7a3cd89a269e088ebde5f26c9cd7d8eb564840cf9f3d722d5b19434ce618947673f64f9a0937e2bd02fd06c1f2dfcd1dfeb733a6491c4c2aa01d9a08835f21cc1a3c1049194270376cb5600bcb613b1995ef33dc78955ff83dc2eec29c40c953bae367c324acd210d229e1c3471a876911679d666c7fb725cc96bbe8abf32c5bb88fa275204416916168e13ed3bf53c7185bd13e4499dbda8bdb22adc8b380719cc269781f2f1462e0545189c750b490c7096b9bd187f61b2dd99357902a9dec25eccb6c0000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } }, diff --git a/circuits/benchmarks/results_secure_micro/integration_summary.json b/circuits/benchmarks/results_secure_micro/integration_summary.json index ace99603b9..42072c3173 100644 --- a/circuits/benchmarks/results_secure_micro/integration_summary.json +++ b/circuits/benchmarks/results_secure_micro/integration_summary.json @@ -24,156 +24,174 @@ "operation_timings": [ { "name": "CalculateDecryptionKey", - "avg_seconds": 0.041923838, + "avg_seconds": 0.042596574, "runs": 9, - "total_seconds": 0.377314543 + "total_seconds": 0.383369166 }, { "name": "CalculateDecryptionShare", - "avg_seconds": 0.169763022, + "avg_seconds": 0.167140962, "runs": 9, - "total_seconds": 1.527867206 + "total_seconds": 1.504268665 }, { "name": "CalculateThresholdDecryption", - "avg_seconds": 0.188830667, + "avg_seconds": 0.202590375, "runs": 1, - "total_seconds": 0.188830667 + "total_seconds": 0.202590375 }, { "name": "GenEsiSss", - "avg_seconds": 0.517591101, + "avg_seconds": 16.803065226, "runs": 9, - "total_seconds": 4.658319917 + "total_seconds": 151.227587042 }, { "name": "GenPkShareAndSkSss", - "avg_seconds": 0.519758712, + "avg_seconds": 0.613419351, "runs": 9, - "total_seconds": 4.677828416 + "total_seconds": 5.520774166 }, { - "name": "NodeDkgFold/c2ab_fold", - "avg_seconds": 28.564699092, + "name": "NodeDkgFold/c2ab_chunk_fold", + "avg_seconds": 25.016611324, "runs": 9, - "total_seconds": 257.082291836 + "total_seconds": 225.149501916 }, { "name": "NodeDkgFold/c3a_fold", - "avg_seconds": 665.751029958, + "avg_seconds": 610.816555162, "runs": 9, - "total_seconds": 5991.759269624 + "total_seconds": 5497.348996459 }, { "name": "NodeDkgFold/c3ab_fold", - "avg_seconds": 13.677920254, + "avg_seconds": 11.11227249, "runs": 9, - "total_seconds": 123.101282292 + "total_seconds": 100.010452418 }, { "name": "NodeDkgFold/c3b_fold", - "avg_seconds": 627.044043615, + "avg_seconds": 574.685986412, "runs": 9, - "total_seconds": 5643.39639254 + "total_seconds": 5172.173877709 }, { "name": "NodeDkgFold/c4ab_fold", - "avg_seconds": 12.85089624, + "avg_seconds": 9.864018078, "runs": 9, - "total_seconds": 115.658066167 + "total_seconds": 88.77616271 }, { "name": "NodeDkgFold/node_fold", - "avg_seconds": 29.335869893, + "avg_seconds": 22.548706759, "runs": 9, - "total_seconds": 264.022829042 + "total_seconds": 202.938360834 }, { "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 5.934307209, + "avg_seconds": 4.759221333, "runs": 1, - "total_seconds": 5.934307209 + "total_seconds": 4.759221333 }, { "name": "ZkDecryptionAggregation", - "avg_seconds": 97.371421875, + "avg_seconds": 81.313042375, "runs": 1, - "total_seconds": 97.371421875 + "total_seconds": 81.313042375 }, { "name": "ZkDkgAggregation", - "avg_seconds": 5.380075417, + "avg_seconds": 3.955149166, "runs": 1, - "total_seconds": 5.380075417 + "total_seconds": 3.955149166 }, { "name": "ZkDkgShareDecryption", - "avg_seconds": 76.340271504, + "avg_seconds": 56.45816899, "runs": 18, - "total_seconds": 1374.124887084 + "total_seconds": 1016.24704183 }, { "name": "ZkNodeDkgFold", - "avg_seconds": 904.325249439, + "avg_seconds": 826.46726424, "runs": 9, - "total_seconds": 8138.927244957 + "total_seconds": 7438.205378166 }, { "name": "ZkNodesFoldStep", - "avg_seconds": 4.391163124, + "avg_seconds": 6.416547058, "runs": 5, - "total_seconds": 21.955815624 + "total_seconds": 32.082735292 }, { "name": "ZkPkAggregation", - "avg_seconds": 31.237663333, + "avg_seconds": 37.316978208, "runs": 1, - "total_seconds": 31.237663333 + "total_seconds": 37.316978208 }, { "name": "ZkPkBfv", - "avg_seconds": 9.470989805, + "avg_seconds": 6.641598305, "runs": 9, - "total_seconds": 85.238908249 + "total_seconds": 59.774384751 }, { "name": "ZkPkGeneration", - "avg_seconds": 87.110795134, + "avg_seconds": 68.692219514, "runs": 9, - "total_seconds": 783.997156209 + "total_seconds": 618.229975626 }, { "name": "ZkShareComputation", - "avg_seconds": 94.84146134, + "avg_seconds": 374.55872068, "runs": 18, - "total_seconds": 1707.146304126 + "total_seconds": 6742.05697225 + }, + { + "name": "ZkShareComputation/batches", + "avg_seconds": 86.114743074, + "runs": 18, + "total_seconds": 1550.065375333 + }, + { + "name": "ZkShareComputation/chunks", + "avg_seconds": 260.809911569, + "runs": 18, + "total_seconds": 4694.578408251 + }, + { + "name": "ZkShareComputation/finalize", + "avg_seconds": 26.735104601, + "runs": 18, + "total_seconds": 481.231882832 }, { "name": "ZkShareEncryption", - "avg_seconds": 105.638965935, + "avg_seconds": 94.545070392, "runs": 432, - "total_seconds": 45636.033284039 + "total_seconds": 40843.470409586 }, { "name": "ZkThresholdShareDecryption", - "avg_seconds": 187.468467925, + "avg_seconds": 166.007461671, "runs": 9, - "total_seconds": 1687.216211332 + "total_seconds": 1494.067155042 }, { "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.583209972, + "avg_seconds": 0.137594467, "runs": 9, - "total_seconds": 5.248889749 + "total_seconds": 1.238350209 }, { "name": "ZkVerifyShareProofs", - "avg_seconds": 2.362957439, + "avg_seconds": 0.788292632, "runs": 11, - "total_seconds": 25.992531833 + "total_seconds": 8.671218958 } ], - "operation_timings_total_seconds": 72012.254993286, + "operation_timings_total_seconds": 76552.499620668, "operation_timings_metric": "tracked_job_wall", "phase_timings": [ { @@ -183,68 +201,68 @@ }, { "label": "Setup completed", - "seconds": 2.393101875, + "seconds": 1.977860041, "metric": "wall_clock" }, { "label": "Committee Setup Completed", - "seconds": 16.092655959, + "seconds": 16.112527834, "metric": "wall_clock" }, { "label": "Committee Finalization Complete", - "seconds": 0.001054875, + "seconds": 0.001757417, "metric": "wall_clock" }, { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", - "seconds": 406.98499, + "seconds": 408.346358, "metric": "wall_clock" }, { "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 5251.559821667, + "seconds": 5166.649068375, "metric": "wall_clock" }, { "label": "E3Request -> PublicKeyAggregated", - "seconds": 5252.065132542, + "seconds": 5167.154574709, "metric": "wall_clock" }, { "label": "Application CT Gen", - "seconds": 0.288574125, + "seconds": 0.285191208, "metric": "wall_clock" }, { "label": "Running FHE Application", - "seconds": 0.001384625, + "seconds": 0.001317041, "metric": "wall_clock" }, { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", - "seconds": 103.520856, + "seconds": 86.286508, "metric": "wall_clock" }, { "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 334.1983295, + "seconds": 293.920426667, "metric": "wall_clock" }, { "label": "Entire Test", - "seconds": 5605.042426708, + "seconds": 5479.456071958, "metric": "wall_clock" } ], "folded_artifacts": { "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000954c24ebda205a1890000000000000000000000000000000000000000000000047891ca0a612eea510000000000000000000000000000000000000000000000032cd6988555d9375700000000000000000000000000000000000000000000000000020a71945a61890000000000000000000000000000000000000000000000013f378b0dc552f37000000000000000000000000000000000000000000000000578d3973b8a5ff2e600000000000000000000000000000000000000000000000e6230cb8ef3f2874b00000000000000000000000000000000000000000000000000005fb56392be3b0000000000000000000000000000000000000000000000037d2ab4998b6b2f2b00000000000000000000000000000000000000000000000753c7e1e41947b7e100000000000000000000000000000000000000000000000c913f223f3cc93109000000000000000000000000000000000000000000000000000015ad2d9cb057000000000000000000000000000000000000000000000009bf71e3c026d6750600000000000000000000000000000000000000000000000a7b65e0bdbb03a37a00000000000000000000000000000000000000000000000bcfd70795f55b571d0000000000000000000000000000000000000000000000000000fd6d2df268130479d738c4ee38577d65b445b420ec265f9c9d743e868525978879a50fb2676526d4c60047e4b4b5b366ab2903818a6e23f01a9a19dcf15005c869f763f0d3a718603f91d677484afd67bdf49862b7a0d8a7885eb94dbf0fdce2a0f2e8e3fea707a3d0275aef9b17f71236978cb2d218add2a58fe2bb5d47f0c77792d4b03de917df3288ea86583d86a6c706ccd356ff13ac09352c7fdb9ee1b7eb870144a5a0032279b0f26b994415350699f0584f685cdcc4fa9c6012523e64b359b3a0a315180949863db19097494da78086af7ab28a4f30051af93bd8b457341fa4e3a4b82c51f9ca9639322c8b2cc935c697a662cfd7e1bdc24b820869db5c02d4224bba03b871237ad99247a84e3f4ae2aee3397f2ac4b1e67d13d9992d2355b35d473e1caf903eb3681d0612cf7b84fe084821333d5a7a5ede332609f37c05f346a5972c7b427180c02d447856d1a00e12b702cc7808b995b49ae9d0886881532077771470d6d7b36709f696e70fd45260daad0dd7c05f60714838cac65febcf5920b40afaae846ac976914cd712ea5525e57ab68c7fb495ca0b6565a2461aa6987ff00d6f129c8f0052cc9f3593a4e650c2aaad3f7438876d40ca5a2f2744e3d6a2b121cabbd1a2de7dd994ecf1515a7542cddc8021886e53e71214c4c49fe0019b931df5198d8f3c27deb3cb9278452f7fd8be2bd3ab23e32357aaab0dcc639d43a50e87245b3eb0b4d17c3d94e4489c1116440347973ac5d1b3ffdbdb5bda4bd4a317115e9b5ddaced77884f0873a639ea45c628ca552fdd96aa39716fbe01ff66e29d5f35b4dfe51961e9cf22346931a9a9fb30c45c41d94c687ccfd9c19163b1e20f390f8e062e1c738b0a3a4bba05672aa3048f39921e74b5f07d1d7185e2dd321115ac66fa437573482a2818e18ef87eee118edc795d190b7633af3b407a46a1e34a9856ce1037c5bebafea03332cc8eb47d8a50ad8130bb7fd81013c311297244810ea5dc9183c9d13c47a8e666863d80c64c05baa20c9c227903425e2c9e52988b98d1c607835da46b657ab73038f9fc26deb30d37c2a6de109bf1b03140519d9c12414a86a64ca1ddb999e61db596798fa182d101a32a40344bbe759dc070fb4c4faf6cad2d5f53df2abcbdf4485058be55470c36a2cd6ebaefeeebef439001882b6f4b8d3a0a9b74dc3ade61be5f3aebd4230cd36e7d7b6a5e9374b03b411c376a48ffa289b5cd0382fb39c1183f2c227c5ca891ff7ecf504b566914e030a1db94582c46e2f1ff6ff257c3e9416096b49a61fa2c435cb5248abaa6b758b0457295b1f05f279ed93cd004f1b5c814f5930f61d95d0164061c4310d3c08681ba02bc39592fb7636ffc11815de6e56fe6d757674658670d7e92f80acd7681e212be07766aac444e30079100453253785ed9f3bc2e264c0705bcf655f4a18dd2224af0f6d6cf1ae1a9950e751a011863db40254bf6a2ef87d564a245d51e5f11c2542d9e1797e8faeb7d4ad847f94985829650ad4927c98f2e5cbfe53c50f1120b905eb3f2a192d3ddbbdad5be7bf6e139bdd9eba0b72f063103ddcf3169daa28f9993f0052a2714b0108368bb1bb68b3b69bb7c43711e3ede8fec1fbf54e440bdf8f48b3c5ab0d5816ec621766b5bad522677ed2dcd4e30b34b0f5e8bf56b006d4b03918e8137a4b84714d55cd3a9f9157c07609d443a9b3a10447e76c4cf31831a1e30b388e8086e8aeb6251fc2eaca53ffb56b036b3ec9c675ed816d877911f7396949a5a45782d14ae2f8fb0d4496cb76be92f3123c4494fcb88bfa88782cd0b15b923c1063ce41d0be55059cca8900361437b919b821beb588f752c49e1a3306d750f05ea5eb43fd147d7754169f9cbb137cbe051f26964070cb8248c1152ee00b158348359946dd6a052b56b812cde74bdcdfb0d8706bba44093ea007225560f11fae351928424dab17684a8146db2a1870dd9e0766422fc8dee7c930181fd2a5f180a80981e004f48600a546163bc214e0dab04eccd47acde05e2f08265430e92984148699b8ad94d8b237249e845e61ecca660b38a9ecbd28b4a57f13f9063635b386d27e21a6890bdf58841ce614dd0b019ee4a95c35ed5e5841261823380c9dd2307bfbfca69b30afc09ef68afd3bc4bb6826fc673ede3921013f21fef391da29cfc93e74b4eda833bd820e760a9dcf30baf8348ee2efe2c801b8233826304bff681879221e2762b80640e5e70b065d879d4892aacb8040e3b94a0fe3fc8764545d4cd3a06ca5eb919e773b8ccf6e77544b77b08b5c2c3e6c52c3215ff7fdd83fbcdce4d1c48035f505f9918ff2c91d74ff3dac351affc1e09cc81dd589e2cf4edc0a0a30c660bc592e296037dfe165f9c9ef958d9510ed93931206760b3f2dbfac735bbcd4fea1596e6ece49bb58eca25fcaf2193db71c1bfbc407dd9482cc4e4353dcc6af030062ec7306a92e143da95f847998e0baaf690eed0cd3c79f560db4ebfbde0c969eaf9990785b8f105742a8ef523fea869992e1790b932637305e08eea241cb84740016d04d1181e1ee258e0dceef1373f4ee9b492692c9dd0c0aca9f9c1169ebd6cc3ea15e1dcdf9afaf00d9ef34da272beb108f1b6934a6e6cd0162853152259ea61f7152d01006ee9d4cdb7f39c4f468095375295e5d021cfc1cb744b26cb49fbe2de5889e996502439a7918a7853ab85986872be94b747ea2ff3b4fe25c9742fae8556c4d881b339205fb8d92e8ee31873d3303a87e48f886ad41a6ad621508757282b3490e8d8fe2da22844614359cfee3c623eb476c158647f316928f903ad86c67e6d04b905a788caef1a4066c7a3e36ac24db37b6d796c23d77e7a4a80e7fbe5312e3290b80a3097a65fc13f955b2edef0b72616a71073e5ab28ae16343cac84591281313d71eb8b676d324b4cf3bf7e02a5454fc081168e6070b9ef6683990545b34069c5bfd3079efff96f4d8503a6c25ccb480b918e97636b68e2152daafd94bbfded22e615f3f0fb3ab18ec022d832cb930987ec9bd0c8d90705b362dc20123a6edf2267359b7692061ed7390e7722f714ee99dbd02816dec267537222a4efe2260684077840542ad5608d226c458214961234d6b9df88ac960bb145da06ee967330c2f941441dde2860de102b56003dc5dd86bc2691a6136f98dffbd6a6bde83059e71dad335abfd47ee7f265715242692b67dd1ca15da007b880531fee6d551fbf63978d744445e7c047001d27e2c5a20ffe2e0412e14eabd0cac98df969d10364918cfb0c14b09db809991c80710dd08b4dca4bb4752983e00ac7d80929ba4261dc45433f657d06c79d8d3411f1e33e768ffbe9f120620405857b4d2009236536d8ba4f960c30633b60f9807b2255cf915b29b6d204ad6bda65eb98a76584d048729a2aa6950bd5497f31834812f9f03764a12918634d150dadfef911bdb6b9345b654bf5077c65a37df70cb2409c682dbdc44f157e14e9a538e1724609bcdf9f19223a2d2ad5441a3249d5cad021befff0c472ff884d903a8e263c0d6ec25e771df369378b77e2c7c49bd9ed32eac8025ed81eec539e40a29167412ce07768f524ab397345d6b583d7491181b2061fec9445e09787f5ba53674471a06613034d8e3e977c6aaf1921300143bb6263c15dbfebe951fc8fe5d5fee441e1a61259e24b9ba8539738eae9dca67a93226aad08653953fa4671e46be63098b3a7af0569ebcb58754c1c2ce89291112c6238de55e26acb61dd66c6e141d63045cc092931d884495b05f01742e6d0da04708b1a336f53b269a0ed09920fb119177d4a57e46865d739292e02d1963b72b222140070855347cef1811823f94e295526e5debb4b8a9b8e6ff071b0c3da6f3c50a82030a7b71a6188c761bfae5555dd8fcc8052494099fedc0dac2e2149621a618ee11b2de3283ff999e1e7b75f0dc41c3cab1f82982fd25d4c5219285335e3a08b5d32f53149e2c627d124203d708f4bf689c0bfd205c23bd7287864f43fc5505f5db8722397932ede80faa4502abef2a335d7dd8a6bee0aee2bd0935bef824027e22058f7d0231aaac32bd283c1d21c243284efa116c12cd62f6996d9745090af0fd01bc81e5db7a7a72a02e9aa349f5c3680be7573a9592ac24298c99730b235d74057eb525fa9db4c88c386fb7c0b46f9fdcfe7544e163b8d878bf5637d02513a462b231640c1d7da3924460347c7de8413addc5d90e28213b0861f0e1312ad87102d3ced97628b7a51796d9202d6931f53b7e97f813898c3904c6c6ed792557baf1872893751a1199da549e8d40e6b767b2e617de5efc68ed43f80bc3d31893ed70d476c3b224725c08b418cea498cd805ffdaa309aab6e7fff9be15a6e24be8f3fe1edc8135ad3cfb453f91f8f3b28f00f5e1cdb413c8011b887c467141a4a9c8a75d9cca761c00badd5ef5f2d9c972c9653444c9fb39a9b8c153a38eb10002a870704163cda0c9f3c8794adabf8829b3cfe43c76db4b3e070204c19c300406d3c59112f4fd8f6d49864b41d3bdcda00b91ae3c38cf4715554dc3ad9cf2c05da85f79b5e1467c590cf088a8374f80ecb58ad120b2e010f8e04dc890cc42aa372e47c3af4fbf8b47f9bd6672bcfbabf3d48bcb37128c6e4960c57f23c1c25b7dd1f37782125d0d2f68d963f91dc2821dc5440ab5c8684df689bd36106001a9872c04f1e304a64f1ea42cb4d890c92d8631d3898e7a35d561498f8db69282264fe269eb5ec83b76cb73f81bb42821205e36e90b8b0e3550a48a47dad2042088cbc58567746b617df4891ceea45deb36c71b044e6999ccb3bacd0d7bffce80f839b549c474af9bd63e5d94fb55ce9334faca560233ca30f7cfb7e3500989805b4d31e5225e33ea6fcbc9fb9568707144034a248380cd18a4fe71161659fde2a6677fbcce9e7d655fc95c69900ccf6b85bc59c803cd83c9d617c9fe7cf6206051e944b53d5ac4b5f6b8d149c84d3cd97eb7afd61242757bd95b537da02cd5624e1041188c6d776ec38b7002128126520a7b090be289ed5f9394b615638365d1547bcaf555d5e9b44a663e9a490fd058c2b86c578fcce7b52636197d04831ec0e99ea9b8691a14d30f294c1363cba67e813dd3d39b232d0fac06be2e09c782a23f133762b99d99eb458e24bacda09221812d0625fd6ccf763487058ee3d219f16ec60eb16f4b582d0af287cbf885c5de32d182ae8e433348e49929438fc1b0922feb1a9eb418e1493bdee91f15ff98821d95660b3f00d1f69cad59d6bb045a92b942fed2a7ac1711f1b5a340bb79ed772eeeec1ea605a0ec7763682397a146f0546a02fb8944e603d538b10cade7b1fcc99e4dcb4cb49890d9c73aac43eb1b317844df125648d183828baeef070bcaa0c9ae252c6b0088c8180bfe1944139d50825a6afb3f84f161505c8148211127101eaff10682410887d14fa2a6389f5621357679db21bff9216fffb04e994baacf43bbfca9e792ab32a894ace72cb62870e96989fa31e08cfa5f848eb137862f0ed4704e6320516cea852551328ddc36903642426fd362d4afdce884c67d22a312346c4e2d0ec078b8b41c6884abeea122439a1b8174258e1dcf3cb3bfcc773258204c7ee55f77ca6ae3d17a16cb603fe01ec3c26f6dfc003fd1eeadd595b7e46d10fd9552bca5c465f835e833cd8cd9119f1f5e43f0e7f7205f1667c4d7fb0670c284fd2929360ff5599d098ebc5f8661534d76ddaabbe567c74c0f4ce1d1ae50ba2bfc4295d610ecb0d82a6261a7f601ec8512a5887e008af2e49eeb2ea1948d7def7dcfe123bc65d45b6792ac776b30105aa64b0a6564f9a66f1ddddc84a2c67503c34c9fdbe10833db3ef9fde04b12295e8d7cb26d10e81420ee58da3e655fbf0716af6fdb599a8711486cc59dade18b30575632002d0d21b08ce2cc8a54a215c163f3c06030064ce164320252a30038ac91a9f9f6f328420a1ad9ac21964974470784e5e302d4027f3fbb047bea425d60a13d3982f35ae8c86577f1973318a04e85f6d202695b9c5fa41c35b5b852ef84c6eabdb2de8fba547a0b65c05371103030bca01e04bd2306684ba47b76017b1141fcaa56b2206f78226fbb76b0533a7ce50aa0a4b7371372b3570b09cd800d8850bc681af074c1035450dbfdf530331d46c582056e37b8de5244935908a26898b967ddbfcd28228687c7e84fa51b15c096efe9da672f2593589a82e3e14165839aad3a2b0d866257b6a8c9fd9407653a5edf16e6cda91435c4448b74a810b0bb5a263f6a644b58bd759d466e4d7ff683b1cac40f53b529605baac0f1f942c5cc7cc12d073eb337f3b16d1114d1588f979ff8b0b8a9fa7d36c31f7e68a770686258566d7107d246ac0852ba207e065dc52b50d4a6e8956d1f6f5ab68ca7e04d806cebf24c27a061a0e70337fa2eff7263a4aa71c78dc47ac83c0c81e35bc2affc894bda4d88ce418e8234ca460fea23c17f43675e86d654e6cc89a318bca0aabbe885732619a129a42cafeace52ad24ea837d1b002cad6712569457181220603d7ee974e9716aef6df02646394d0e9dd1e1746bb06590d34edbdbcc921c3010311bef8312cc8095a1f4ed732d83cf6039432968804b89c0952a449263a822da1f05a1a42835b7d24f2e9f2b59e167e9ed8d7e14f4051e9880eff09c6792825be65ea3e21d8d02a24758e60f6c7e6565cb823c867114e915db0c7f4cc47b612a7c3019467cd469a422ca3d7587217b52c0e5653749f9affcf36752131a10e07c9844fb8450ff657aa8eaf021bcce295748e121851660e0dc4909a2719af5c28eb96158dae3557d186b9743dadf29d9f11f5d0dcfb4dede3e2ab2739679a0916ef6888b12b324634fb62e4c91ff6d99a61293a7841c20854dd20d6ceb2b85d01f905854e23020044ea2b96c48ff0559706fdf8a1a9a513d5df18ee4e556328026e9e34ab8a4ea4f365688afd3c346599782ad10e1de844aaf27c873fd7c5c626721ef8b7ffd2dba851c17bd7dfeff96d141b5d1e2058eb2f17f313f9ae321c1c7831ffd39c03a5d6027505796fb09cf775ff79f24e8ad288815bce3ed9aac72df73050ccbcd65699633d4fb70fb67217fdda5b617bd4ed7276a22ce3e2eb6a1935fe82954a6a5590f8cf1c91500da83487b87e30ccecab155ae62145bdff181a31844bab00b615677c78ca3cfe697de5b49c3eb357f52ff3a7f2ec7915128b2d237ccefa11241cc47a81b9a8d2541c8fe2d52e0cf5d135ea1b4b1b8a9d15cd149914a3da61606e033e9d0e4a7b63b2cb13cb9ea370f63e2414d3418a5c7d991b4633b0149d8f03c9c367d03cfec76c5109d53f21596e32da280cd5cf45776a0b87b21b258bbd20d69fd51c1a9558b1dc2138ce33b072a8049a8f2638d28c150f5fb9090cd0e2d31c9812d5480d0009df8454e1417d6f46dbd05ba4a8ad6ca618bdb22a6cc8ab63943c13b2ea5d193b20e6b08227a589122dbe3a73f3ef1cc32fff616e1c095353330c96cc982404527938bf88505d3657ebfc50977be0020b03541e480230514809974e9e69fddb437d174359ce3223a21e390b97878a8fb2201c227f3b6fa370bd3a16acfb7e6b0894bbc720511cad2f1b2ddd28f29f83eb026e011a09b10faa9329ba60f4f1593fd15c3b698d255ce4faf6453bad13cd760de3427a971293a2d5dff7360740304f5474deffc50a4604840053da7652b9b02d6b0e28d6cb789ba636d32f86267196d2614812552594e7dcb0d55c7f3c065f19bfc2883d105f9f2750943abab64249cc002750c21f7f17139b88a18118d396265ab2edaa8e1d15dc3fae0eeab9526a3656d667066afd2513be00e48a1f19810bc20f53fdbe2daf96da6ffa1a4cfd89bac34441a116580f9dc70b1d90b6343722c892b4b34410f5d72c564c7801d46f78d3f810140089ca81e696f9c168de1b09a60966a24563f814100aeedcf627f0420d292ba9dd663ac76f35d77493972809de34afa8fc2cbec1507bfd4d8214150fb7bc4c776d270a7ac59e09a2ba0442132e7feac7eb2f0885f4232c77ced69d98bf587b9e25a03b51fff2032e1623b32a92d8fd82069c99cc1a1eadf51bce00bcec8f13ae581f506ab4cab1a949a4162bc9a368764de7a6e6374b3958f61b422db4c8ab79b3aabf61318650260e09d5197faf7ac774048996e153e153285fe70e22c0ef8de78bcd7f82dce646766eeb0759a9e4e6695edded54aec59e271655a908a67e9a978a6cc7117b7c838f884e1ab20ade1ade3220a5290330da0a30ac5fd6b27c0a20fbdb6a86831467607f421a740222ab535e73c0bbf14d4d5dc94398e7dc07e3a68461621ec44f22a1dffc04f3eeba280588b6bc54d4cfa12ddf74f0eb41d28d71b7e1f40154b650de01f40002f96e89c482024cd807c4dce65f95154862969cc7af25f3e593d3ff75668d23dc884e8873c8868de377ac148524fea10091dc26e256c0fae4a865669cc23b109c1435222a257e8f81d0138390d41fd15e24554484d2bda75ba68fd9ebd5fb0e0a6eb696c1d53f1c2c6d1876ecc4db74a7468c5f1101e616e5f9601200ada7229859cab751c62d3150c969d594ebc26a09aa2b452bab04cc0926ffcff77e0f2728a6df1187a01c19ca1246a6e3fa46fc745f1ccb321ab3510783dd3f9164ae029c8c0374acf4a62099c0cb553840f77ac7e8892630a753baf8b659d33246ee13910d2643c85c3a652e98e9074d305614b27a4bfed9b19b58912c068f131f9c27db076544b0f37fd76a9037f5279f369dcbd7e5ea53fa5ea1b4cebe86d84da003da622a4f51e5b780b50502de2bebb6d08e5783da1e99ea0516eeda99b29b58172d82a6ee6bc082520e3f65fe65153d3b116b6bd4e5a516a973fd01e45013f806b49e863c12caf6dc51bf201335d03da3c7baec7132824a9be95ecacf49bee405c2ec72bbd0674d967d5e247aa1048cd42d3c93abf4e94d99be54b3a686dc65108ad51a31ec40a8feba4f1643319baf8bc0371f0286cc4b8182b1b8ebb5676c07494ecb86ef09692147160fe4453ee58a4fd06fd13a3ea30f0df887e31f61f011c9b2e48b84e9780898bc2cfd18c8dddf03fe98451d43f8785d851a3f16d1101466576200d8b8d94e0111eba9d87f04e429f26bd24562269463991ef5c4f90a1c396159f9e1367471f9c652b7ba1f85a8f39f14676e57f27bc71ba0ee37de16119f8d03f603ee5bcc29aa33ed04c99ae110fc380985e9f78f14034824f7d60d09caa5b032fd1365d586d7d0ef008bf007107ba2851865fe1e85a38a3623b7f60c7944bd39da759d1a9c0904258bbb43668c05073f1c47797c64ba7476105c5a3032e52bd90dca1f89445a24e680b21946b78714d905c686757da1f7bb325f930ead078ca2b4da812d06ae76f795250a9614c84a4de7ebab91698a1d932e9c3914d19e4fe62fbb06dc685ff96f1ee2ab685a1b2dd11bc574814e55bbbb8b4bf118e9abd351095e04b308b0a080b423a0290b2c240b68813c56ec447be417d1660b9ba2c932ec8ae62cbe94a966dc1d97905e336e2fc104e71963aceb2434a0f00f213d112e2fb79dcf5bf9109a5a89171bc77e74f677c1bd91adaadd7bcee37e08085211f4f3830aee88958efb8b9abc5d048c9060e19bab14c71a134359961d047f23e809c640d4bc2f8c1013073ef2bb7d47092229a058d9a8a0e368e438051920b215b9536191b75c45babf5539c2013a15dc0168d950289aac025398df260b5e8266859c2ccf7734b2abc57cab97559e1690e3b46c53efbd701733356fcc024d0527b5d1868e60f847d2850ad78d1b0e14a6c8d0fe88b8fac37a22ecdbc9249f53b74629a75beb4065e8c37d7d6f4642ca9fe336fa422498b32c8bc4f7512a2be4b259a1b59cdecf3130eefee0e2e5df6b2c9bdbf668b75aabab1576d53303db0d44ec971fe3d1321a9350d8fa6896a2cc2e1098698da58eb3a7a35eb4000e4853fa959ba339a30c0ef0c013234b5a28a5b452c624c8f8fb8ea2b42968eb19af260b94cd35e4261a065151badeb645494e07d6861cb5525808b37b42ffef0e2b0adc409b0b654134cff58049bc938132ec45d9ef3a29c44364310129aae112d8301156c8ef325358488ef5d3efbb02b572deba3a29af4933865fd6c1bc490708844e738a7826a8ba820c8951fbcc346c05d6f2272d758aa1492512d270ef0254b7bcbc7663ac5f7df79f7aa876dda879d8641724107d51745986adf8fa0d1c7be3449daa3316310805fc39038801b6bc2839218b89d9599bd4a97059cfb71af65732d4ad5f7f085bc5fabf031d31fea3654a4d01a951e7f5b01886c42e3d21b27b018697ecc90219da4e2862891264fa808c13cd091d0480b09f49c7af9701d675d72e6bb13a542bc0d8778deeddb084d33610d8487f9560985cf4a8de49254372d2f7b5b359e27531954d74588044d27b0699fab23a1f402d1b7fdbc24826ecc129e4234ba397dda35d7bb9ea7a843ed5aa132e4800ac530b5d453734ce0be7f134c2b0b0082a14407e58bf0b9268d6c8f71a4e6e32027dd544bdef3c96160d0fb52d0b455efe22dd5d80e27444882381d80c00381306846ffc5f3061a521b205b976fe30137a433bf9b17472ae316a82e9a56764b1815775de43e5dc3b2a589baa1dff943708db4e6699cbc5fb855c6d65d3797722a085c536b5c3c5a42e386bef06c7b38ea71ab55d213dfb86b5808e4b9567e35aab3464d56470873903ae9e4fa7fb9b93bca027fb8b7f1342d177584ab04bab2f054007f74c13f8c018e6362f609c16447db1630a9f2d4f7adb816214579991be4bd2005a868c888f28244c9734714284120aedc3dbc9ab237f85e2d0831d2d670893351e5c57ae77086d2e0e84ba6b90355a564c2bd7f2975fd8a1e83ccd2f9e02bec6b8c37e598823af1676dbb3390568d8b9720dd95fa5fee9befbae00270e23a91a87e544e39d0678ce6640c5a6158db874fc7999eb07693828e7f6bc17714e56692cb25fea7b2e2dabb545915cdb9dfff5d5b6db877f8c6b431946ef463eff26e15e8f31854d11d3a836eeb99da348301c8f9c050e520f4cb05a84d178678f3712e05b864ea31a516435a747f2565bccdcf0b67daab6049aa9865df90ff6daf8d0aefa01dd5606bfa36a679b0e172066572f74741ffe72fcacfcfedc85bfb61595b539cc6e890251fb014b1e88e762f64420d34542bd3c66de5ee997e1783de8a9b527e0320f07c6e0071967dc2e82aefb43ac97abe08d9384e0f6bd8f70435d65d0804a480a24ef56af6c2a94db8af52b40844d3b5216787e134ac6b9aa1957d9e5f4e0aa122e15140bd00dfc578b008ff196a929dc8699083b3104307d78979e927d164e182a2dac33cc1d55fa5f02fd3bfc82f608a68eeddf64745a9e7ebcc8a733412c711297d9219cfe2c3c4a281566293b6fd5212e051f11c2a414a35e72fbf9b427542543ac075358882948274f15d6301f1b1c86627c77a116d4d9c06caafa3a8727038e8041967a62abc6c4b07e8b52868234e840bdfbc588883cf7963a0eb4b447186c802ada3390a6516ffdb9969ee1ef8f0f922ffeb2a32225adf999709e56fd10734cca02bc98215c779cc2c1cbd097fc18d335fcf4e1c133b1e7aeaa345e7323cc0c97adb0df8d596ae452c79ec2516777d0f5fa170079eec45a62676117ce0b87d58ac85b3bbe61ffaa2dcf252cd9edd9c7d5eb7695e3302818734d295e791a8198161c5422b10688131459328a435a3935768e805b534fc41fe31b6f882e0ed1b3cd7c1cb61c5c7125e291e96297acf09f44375e434da29cb00024729fed15747faf7337d199a12148eb0aceb829e87ef46dd5da65f635b788eddb52f975041cfdb370f2fa6c1d7185325050e76e07f1b7ca7bc0bc3457346e39467540590c92e4a82e8da7e7574481a096f68ad1a1e7c7218ff2f63fd2290d5239feb49e2b3522fe944280b6dc2ef1af9e137a458d956398c7b668dd3cdd6e46dfd05077081d99ff8047a33d91fd12d5260251c55e116c495c623cb5becc11cf1f6bce340a2a4015ab47366084fe7afa4f6ed6afde94640034686b9ee7910443a840dd781825d575f1365f2c47f660c1a09bfcb48f94342708904696ae07027691713af3150866bdd1d28b613bb3fba63052b0fd65ff1cde3a26f85d3f845891c89089552d8fdb9b2042ca080d659f7ff593d9f83ca330624afa999edbfff2c4050631251809b6200b186582198ce248618be9533a3760145f26acfbc7f01f4b22a8bc8111bc30770b490db35c289a89bb0a57bd0ef49623c5f7016aedbb299dbc8aadbe10577093fa4c2c12a107687154203d73ede9041a4e883271481da373d537976419dba316d7cd066a23790e24aa5796a6217d8afc2b27c1cb5521f2896f0f1c82276cb6e2fd185d9d11b6ec8c4b7957787d64dd7fd64e03f01bd8b968590137610fc26595c927377cb7e0473f53a2d8b67458a8e1dab91a38c40e67bb799a95372fc8208b0fcb17b344902b85e74d9e657c4a5ee2815dcc59152baac7ac152d7a0de4a6b22a20fe4b35513b95d694ef98bbda78a01de6075c642351f30bf246621dc199d582026984759c97b15393a0acc5d6d3b04667fd9e5ac67743afddf5aa07ef5e51331df9e4fc770e98199323b08e9c1e523bfad993ad0e4ebf674bb476054e8dcc4f4ca9de4b892da265c18c375bfad551d7b48e9babd153c9f4da59fd0e889e9d0809c27438d70d6dbb442021e70bc4523b1f8fa4cf4f1c83cf41a87b247e24a2efcc3eed818878d18d5af4ff7a406a34b1e267abc9eb73bbad288704039946c740d20368f36b51b905316ec8631c42154ef93719115297b35f5553f8090bea916e568e1abe9ff9d67c00fc50d30089baab588674beebd374ec41e4f52892e8c129dabf6c4452513b2294fbc8e3f01bffd128cd33bde349e7efe92a9b2acfa186941bc9f0f28746a2daa6c1a29f014254a592d0f7b79b4e7fd2c037752f9b7b5f0b6b6b22805bd517ba131ffeffb08adec919bc56f4e842bf99d209ae25b1578a8507e7427f947da702fb1dc013eff4c276eea7e87e3b0fa62ea8c0050bba25b30db28558b4af4f523b2b2435b563fec70d0e7d592a4cdc685656df030ca1ee2dd95a75abbf407f2172194663f318f678a83eaaa08e42c5aff919aee101580a7cd016fa1f2604e196f1d073c2029857904e78cb627a77b188364cc8531a9255179a2f86972d5adaa0d57c6cb3d6324dba2fb08fff2079286041e396ae2dbae93ad2de194c7cdaab18ca1e86b9b4da00c92b839adc172b3a7e2958778d21bd1ae2d19865b9de3c6f621c281424ea494bb0e3116978ef9bfac44077e21123146399626189c1adde7441069e728a64fed893ebf03b9ba7566a82114937e31a14caa89c9e1408d23f20af3e7b60f4187404918ed4347623a032223072ecfd0be0093a0b2244085e38311914e2c4b51b8e4fb1e534e5aaed3c2d23e09d09b519711e0ab303c58be6ae3fceab557c6a48f490a7eda2e99dc4e71fc3d035ed61226fd2b6e1c607254ac45148b05fe1f6b4927c284fdefc4dd5d011d56dbc621d0c765f5510db7aed1c290f3ecc956c0b280d20b9736bce299c89966bf31017fb1075a74a5a55b21b468658e5712e2c4eaaaa0c3c5f28211a121798f8efed38411c3dd26d5d56552f13e56ec646818978f9f81ce61d9cce7847742d9cb0f03c0605a5076b1454099c2a90d975eb63093ee11b0972f1dd12a98d7b6f9d7542df3318329042264375893c8952c49a963c6613aa0a2a7184919c667aaeb4ca3dbe7a16cbcf934e32bbdaa1ccf5f3181decebad10f51becff7d86a213b83c5dd3e952294cec8bb5bb87cf11d7a47da6dfe755cd4d4f775bae27edf884edf868a112190b09a50305c6a6ff212d16c1cc3da81c60a84082471b28114a27bc911ee054772b3ece09c17b74a236f0bfcea3b28067b800037e9ee4926d4e4b106ceb740e3b240274d2a2901f4efa32332051aef11a9fd263eee1b84def9a79133f299a75570ae17d67567962eeb27384f796c3fcd35eff160a32a15526beb0f53c7efb8ed119a9732149e2db128ce2919a5ff7a4d6ac9da5e8680bd8dc4f02d75d280bcce507ba19d19b518d756604fed9656b50ed5c312eb299d5fab45c24b93813bc98b1127581dc7e6ca399b27434360a547b61a7b4d6ee4f500a42a95ae69730a1978b27da01e89e23f06246fec4fdd2f84686dde0d3232d961c52deee6be3e23194dc07ed4f4f1e32df19bf5910d0e6ce55fd5412bf6d2e9f24c77fdf038d689048640a6f31e45b940f585a8d793a5bf7e1844d7edafadfaf9110d1e3baa5b33987de29c2786664670caa4b75390afe5ecbd2727e1ff0db2660d0da53d04bb87fe4fd22a004791bc0d8dc2cb9ca4404c71be948bfdbe8666c5f26db6fa1b4fe9763c91b8ad82b6251e2714fb0a295bad966180ddf19333b1eecc2a87a259e595888052fabe42ffcec38882b078f484bd369fc14d4fd0ad8e779d74e996a80c7546e6a0b2847a75f8d439dca9b43556c59c25546db6206b486f84aef9e4cadf02184c92c8d437c84e405774a31862453cbc764512bfb22ab9b3108dc4fa5a742f4e8a80a2f6b1f06f92d6d888284ef955d6fe80fff1caca9205d55381f701542d26b2a2b50e97fb99b0438f01356573a01bd15ecb89109b904c770644692947ec0a1ac", - "public_inputs_hex": "0x23820b662b29c1eb92181286c78069e2c68d9bb3f3f95e131510bb674adafe902573360a3431f9fb0997d1717493aeb1edcc58f966d0a1587066b4b2f8851e4c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000090331489384274c12b43fb7cd86990be000000000000000000000000000000007b6b44c05ca9dfc4194f954eb77bc8f50bdada746c10c4cbcd2821cccbd15a0e64a76fb9566b3912b9d9c5e6ec89f27305f1a42155a63345c51416db9134e24a473534e1bc7264e47c3d8ceeb0048f3f00f750355551fc21f4f8b099e8aa9d146476b5a3eba3f12ec324353d52c1498e0f0a7cb8a62c14a214735dc01b43a24c55e0f368bb80c5740f91d32c79b109a902adda50f3a68b086921840211e0be05add7ad8933f82d689d427af65040b9e42190c3823163063956218b83ca1ae19999d5986c495dcf68fabffe666e78796909ec0921581b707660aae7e03a29bcf5f6310e32bb1ef1bfa21f265e211ea0dc1d34da8f75be8d59fdd8e87f406948e636b3c1dce3485b81e8db02be6a2b619d04097be17ce35a4c426eea7846e3cf55a6ecca6207bf3e1453d1e46a962e47c704c3f121cbb02760f51ff659d1a9699301fb413fc3021b4ffff5c1d00a7b57162a19c999035857bf3a4db9a179b5166dd723b3db90b27a94e60f886b23dcd1b10beacc821d87be0996601e9ea55375f31f80de63e7631046d945065e07a8821c" + "proof_hex": "0x000000000000000000000000000000f5cbd1db87fc283c12c0d4b24227e9db9400000000000000000000000000000000002eff3a040ffbbbe08a43a91e611c27000000000000000000000000000000f716be9639b08314302bbd9205afa4858700000000000000000000000000000000001cac91d2c32e9543f868863c3330b0000000000000000000000000000000576e2373c97edf5d40863467ff965606a900000000000000000000000000000000001195ecf07843a49cbc9356a2582c34000000000000000000000000000000ba3987fd41a56b63e27b49f4adc4f5ceac000000000000000000000000000000000024fdd9b00db6e9a183b96ca3b8941a240f26ea65958e654f99910da406b5d53286206735245e3c6f358b97011374961738e97405c3e8b7bf8cc754ff361c6d9df3d4d989a895357a8adbef7388e7f61b67a38fd84d77a2a505aa694e2aa379fa480e8926d9921ef8dad81187a0ea371b340dc8c7b002dbf10911af71edf407447aa8f1f418071ce303b6207ade1b941c31d3042004cde940ce9f51be538a13954b95c3279270bb87465cc2c1c2cd220a16911aa35dcd43ce52bb9d8917e37a9c67b5b156ce6933a132735352971c6c23131b965a1f4fc04fc29d0c7d89b9fc8267645731a96a501647d1eeb71d36a12498067f8060361e55f78be30780022c2577536711cc1e0f17d3c057f8af2a9f01afcc54d381abf6a4de2d1e4778b22c0845eb39f2a835d2137eb351fd35c2d12fbd5cde6a671e2640d941c76faa566d28d018944b30b149fa3a9f2820e26afc09ffde68fa76e3e870af7a2977532b9fa1027054dae5e022048107e4f1af354304d098ac2d0adc1d746b5c48db075cc248c5952c36eed6f47ae08e94049de3fc106561c409f35903cf3dc38fc5a1a0a61546dea85c7f4f8289637aca42640245041036ee6f33806e8200e9de48218c139339b73fc0de48ef8835bb5d108410f72b608168f619fe91a6b50919a56971546f8f211a92f45fb7bcf42800ca2bebff2179c79924d7765bed5f5f1958b015ff3c123eceea2ea5a84e79387b51c73a012aa850237741c452f2b0db18879d5b5da9d338958b32f289fa2068b901b2fe0903288e788665f12e76a5677436dafd2d6cdae91f6dd2c0f313d1abe57506d9f02ac06431c2c258829f27bb67fa6565b33f760d2e0a5e65eccfd91b847e408a9a2a5ca57246522c72bc4085895088ab240a385ffa2eaa7e75d6d3944fd4ba2087100bb5cdfc42e9ce114b512377f9e5545003d3fda81479847ae4ab1e32eeffb1066d84d707cba53b4bf08883dd3188590b816af7a94e36206bff8df3719b77d50cceef8e4e2d29d95f612b76b2e451bbdf0b13b40ed4d931c39f87ffda852ff80ef613df472219e320cf77715205ab3afdda21440f550bd983a34de9d68e543c19ac3c56d11eb474f80db7ef783d0be9c72a14df1702c3281b3849b95c457eb815596035fb8e75dc5d4bd217763ab601ea401378102f88cd35c8797b2a8727c4160b47cc025cb241e99f88e600b6e8d677325dca39c0c27f6f8aac963a6ec875278d080ff81372b5028ee77527833081c22688e117bfd96cbb3c783e8bc8b6b015ae23571401aee95b55d67db5f4fb6736100bef1d74a743115b562941cbbeee0e656dd0071c5af34c4991d27c041bd2d54522b5fa79b45c984148e5be3379f62e9b0796074c1407047b344b87b3ac60530226c2be52b7fc5a641759cca57c261be4e188ead830c02633c3422c756a2090cff2f8103a0371912796d52a8e32c515f52e2d8316980c3b5e48787ed8269fbf23e06fb489c7118675ddd32b5beafc18e4f441c724e294822dffb6352b82634c0c2d15fda1d6c7a4670c113f6158c91569c2c2362e029c65087dc4367783399a9facbd02f417b25e7b2a9d8e9ab57e057019050a3e29c949ac5fb1a21fe1143bc15c5bd94595238e6e5613e2e2f728061c593a46f621a280f03864bf47e8ef9b09a4cabfd707c611b7d23f3b254e501eabe02b6e06927aa326f3d0d8f13acdf9d968db76a2b2413c36d720b7e1802a2dac88677b719e957956eb99734c8336832bafdfe5966d8feb17d97f8852d42f14ab8c31ef0692c866ff329d59e0b2fa28715a2826c2a31483b17ad7874ba75820bc5c5927352d849ffeb98de0f4f7a930612dbbb3409b9e6d63c85b338440251ecd8856d84946c0f3c67e0ef49db37fa1cebe2722b4faafbf09ca51de749b8a164302902550a9b70c31eb4afc939bf8827e0556c48e3ad370f4398e7d62f7e30234883fe8f884a5d5dd160879741e549539afa74012e1047cda923fcbd55a42182685eb7bcc89bbbb8035534acb8044eef33f69a19e6551fba3bdc1b407dae60f6c58f639c09a6a5dd44cde7ae6a3cf05d07e863b53bd2a87fce7179b00f2da15e339d29dbde6a040282d629a8957bdc0b82943b3f05e5e20ed36c24ae1c9390e0a33372aa5dd49b72c554847f87ffec6b90a26c45234cff6386b96d2e208882a7b1eb1e0e337bde1727ed544a985a32482e29058a0ab0d5584513716d6864e023f2a5ab489140f44d50c0d6c385667f09d051b5200233fda9977e001a41729273fca988215642a2429862be6aab9f797db5cf134cf1c3c487bb485b56e7a052bce8160d035267fb47452dcecb1d71f5e2948248547e038ef776a8b4edaa4b711974d4538a67bb577ae40158494752f4d78eeba888f60d6cd677074f093097925e27b3e3f9f35a235f82c70800df6a35d5267af9c88b8e6625d85dfe46adec400ab513d9b7d04b203b12e73c17e1c862dd7e0adbae49670c18ac0a003c10fbb179c7ff5f847e5a661cbdc2df6ce5f3e21d8710d73f02887bdb5faad248eb2e4269cc0fb3dbb4c5d9ab0dfea395bb9951b6587d9962d2e44a032a7f08554e7511473e705790d93410f7f3029a1495155cb47eb984fad9b2ecebd1cf6423712ab1d1c99e755da97df5e31b5798c153105fbb77b9c7530d2d313e9cef5e91216490ba0992be21ffeecd966c53f207c1a8b6c018c3922ae7ec689e6a2b1e9b9e20a0fcbb16f5ad7e733d1ad0bdc3d102c4600ca406b701ed33ba075ba4abbbd81e42e8a5c0c3fc577802ff0c4bcdb442bdad989a552e5f8d3eb4a73a9798926836d1d59454afb583180b6c09245e5872e832b84b51a850c4d86eeb12d123afaa048057d0a19ea84ca8cdb93276dc70523ba241731c83b349b0e7dd7f2d31f2109910cf70753f4eb6cf7c191b944283c9fda82dbe5ee98a03df1fa2064e52d0430421393bc34d19ceb4807850b7895972345e8efd845966ed56c9f65f595ea683e4100af6ced76dbfd8f9d69e658911be280977bf8b5e6141706e55650b9be8bcf4b1b3191319ad82f7be26d53f2dfe2d245a2fd1893099917ddae2afa217df8de5528c8e09940ff9fc7ece3e9daeb2e5ebab283ff5f8f011354437fbec1e90348aa12466560fe12145e73c90498ed727762e3866120bab74aa9b90a53fc0ff87ed521a021d9b3b560f2af100070339a0fba25b2bf9f5d4b561b4e01f7194b925b4d296d65d4b7f1f6b7eb61900b03874e52352c545207067444a333935679579255082d6a6d7e4711dc788a40c6a7b205d7ff475c77fc48e17d0b589c2e1ebdcf1c0afac0defd087e46a7fe1bb0bee36d2f516069cfd440481f5e1746919fd696c62dca49c7283d1e733c5c167d99465a0bd64f2f4b310b40b201c337a6d607567a155a18745fc64281f3ff36d3a4dde1a9c55b230b3bbb5ec06121b7be93c05088150f69288502f9394ecc741b370f1c911b63d6225066e52162dc02e4da1a295b1dc0c0425e33eb7430ada6ffaa58b0c5e630d7a4d091440a5e35ecd60f13c7ba1dba007e0935fca98efc86d0d995061b8867416cf3c5678fc645cab7beeddb5e14ab20555e81931999d787dd600dc0e9fe18584595f14393015c4ebcd7c3d563190c93b31c95a79631340271b17cb5de1d3d18bf28e43de3a2f95bf48d1f30a21fbd900752efa7b29305c794635091e3211e03ae33baa02ee65b2122229156be24fe7ef5fb232d643e2c9392ea56749e7d645b925a7359172938c551656130271003c5f717b663c916423d1120b5fdd9f7a2edf8f458c468007ebe3e9cb60d2a26a5cdbabd8d564ffe3c372a63bcd8c1196073072dc4bf2bae1ff02221a733651c73cb58188f67cb103158d996f6506180c7a9a7a7d23469654c97d761faa35419fc7d3b8cb4bc4982e84b6794616ce1f82db35038fc14a2d6cff759066ca74a0996f6f0d9244cb7b5211fabe06b1676a74be1f32a96d074e099707424f9bb71035c92fd29b61e0572bef4d3a4b3c745e92f1b4dff4e719c70eeb3e6e6c38cc207c019b87a6c21eca4f3fcf48e2d4f00d4b5aa725be5241914a00e7c21108bd5235729516641a5e276b43f7688d49979807544d579f56640ad6bbbe671c107271a621b8cd8915977ed7153fa990ad75e10d9d0435b92e32d2a2b78d73830a49b1696d7513c6295d4e75a7967e0108118a7b57fc1690b89ad4495076426148cd71ba6cb343d9d74f7d3e5e5be941a44537bb01f5b6035b490a52d4738708e6013203935472cdf1e5ce38da55de364e717db61cfb51bffbcb2b981464d9871cc2503fa5b4eecc22c338342a5e02b6ae80a508fb80e1e457fc131a3f7c758d4cfc519ef00ab0fc3fa54020368f32afef5e4c5b11a1fca85d714ddf7d2315682e85d16a51b1aeb6e5aea949bf986e6e0c092be9348ff7e71255bf98c28f857d5160a055ccc9e203bd76045f84db2f285488ca1afbcd8210c544630826c18209fdea80a76b788458ade99c52588d71db153a583ff98672b7a91c8448ae83570d4b2611f1211538306d2b9ee9ff6d3d5d7fb22e5faa611b2935a2594cce1166b13c1201b38184511f8890223a5c183ca423340c722ce3d2442739884567688da078947148f4ca8b22fc402baee738700f8aa87b9087d938a36c40388c02307ddd9a8f111628f95e8ff6bd4630af7b415d1fc014053740eedb64c9af16823c7cb7f840b0059fdb94d01ddcca5698721449516e8a057b5658e97dc7fb843fa8aea8f0f4227a3e5b20323817a94a71ca39a45ee26f786419926cb4829dbdbed0975e2cd740ef92ca019595b53708bbc703a8535652d91ad8ff16736fec58bfe262eb7c29a140c008c92265ddf94e69f6a8a413cc05067af37bde067911cd0310ddbb06cd62c23b043fd9e370830a2b8c50a2109e87ced928d82ae0047fa145d30e50a9eab110ba7000e93c92662ddbba49c0f41c0c1327ee9e4a198a4787dddc9496a9ffc25f509daa47cc2dbb46af858cffc7b17c9ce6f2ef77e175aef6ca3dd16602c961004a3f6d38ceaecf543755ef5e9969e9748a2b3ab58bf5cf25ca4c93b83f5701c5301bff21d0e9fa48b3168741bfdd04b611d605a274e53bab6f2795d503ed21fb9106e3de22637812910eeb2feec8914de88c3a55ddd34e2ebace6bc5687fa23192b8a38810b8fb22a27a07aacca945014ba1ea78ff0fe7d30ecbac09dad44206cc278143c8d8d8fafa494d9db88b76fa7826940c5300618abfb8a62842ffd20f4bae8f04646f6435f333c206299a1f5027848326a1a8fe99495055588a3380fcade78647016ab03b1410556ab63a8d89163b9ab8626b0fe917c58c80f34e30d45f6e8124f1b4a41d89d249dfe63ebb73ad2741928e167625b7edac53b708a24ca22aaa58f46096c674e6e764208e3460dad7ada64b9983ecc1f5280f2a63212f49728bcdbc1b49aaf36a3e7071d4bfb84023139a7331f2028f391cb4c0e8403546a0522e070f01de34b64b444d85e6b1ce1c07a1eadd259e82c0b4fac93c419c881b6bc7a73846a8dca09f9af488780cf60c6993e888fe6640629c370a49f1e69820bc0afad16e66d2ee0bb8579135452d0926f11a40f8d7d84f0cf1bedae2e1a0ad11804b07a217d7f598669319f32c6b51aadfa67016f5d930bbab0a7c921cc85b1efd8c6d3a68cafa7fc2d220d1c8c51037d91bc2510e4180b7e01a96e2aada608eca662b5dfdce2b17338f7e5e907d4cafb4c611cd8fbbd0402c4f8600055758c3a8c78eb3f8e3ea7961d49272bf0ca38b97c67b7408a81ba38d904000ac034cad65b2ccfeaaf96557398d8583116a5b5647abe27dff4458c09202e2b0bd5344aeb23b551a5d42a544b7885105fa5a7d323d2cd1089c156d6b3591c93161fc531ffae90709b13013b6eaaee9830ab8ed176ccb7e5e6d19c1d15b6a5e52727a6660a7ebb8bd389c1256b1eaa626c85a48dc0170ab8d11a5a1bd2b766f72e749746d16bffa6a0adc8b64dfc0213bcdb5e290f231aeb95dee37e6080fa9a267df98330513b8e73e76fbfe6c91f94dc591ef1476d0d8020882a431c651069234b6450d5461cbed852fd3395176b8a671c750402fed4ef345721159299bf26121ab2c511c8d48e9c86e0878783f8aa00f6b7731d994b72ac696447c2bc724220ebd5eeda6e30ea0ea6906d567d1d5e12fbc8af5087780a248257635d9cead11ed7e1bb61c8c49bd9525c0748371a6e2ee68af0f6a0787633e6b5c7aa89af63050628fe3043ebcf8e708c741e9d32a73951846154ee7e520722470c13037b7c01eac979321f3c229f7af396e3d05c5291ce4858cc438d63321d657f4decfb101efe368212fea2b8857e410164cc4792d3e12fa5402fd9e94a93b3bc24b6f1e91b34185c6777347d54ad4d813811d0f4ec41aec6902a3c72698142cf325f7d1a2ca3cfd4f1bad4ae93cdc2d9aa7a3c197645a1cefbd96b6e76031ae2bc3bf5bd12835264985651e9abc7de312585f7c2ce43f8ab403dbca639b8976e2e6d11051922b26f9e7b72b9d5249fbe6f70b3702603352e7d36797c563b669861fcf9111a4a0a6505e28b7f68a58baba92e1b23743af70a3500ed8c0f9bef8334d22d020e487f8e60fd1f741d6463ff0699bb6d39659230ea7dc5c0b550961107ac187823e26273081350f39e0db08d61c236a0f87daf21bad6059cd1a01746fdd6ab47275fc7b8956e9dd907107628859376a0c8921d09f60a3de8c81c443eaa1131f303e42d46f0cd82882febcf553c21ad8dac2a9a6a11b61aa85f8dbb2fc86a63f625f92858640892446a626c3a5555928977b98d3091a4b570e8c8e2af8755c3492410238c03caab5a6ecf3ac1ab32fa0dbe13c9cd9bd3406fe66756dc03369fcf0e32930d5145a8745d52238bc3aecf0c7ff2f4d503eab10b87bfb353a39ac48820f024a5c2d086cbdc809d3e441865e2b053fbd511276e754a0372ae0f125ca206e8fcbf98aa6bbade33db4c108e35d7e9817a081f4be0a918e7cde03d37fb1903252356d307e94d70595391361e96848eb9b45229364f85a53c2d1e1eaa0b3b098ad9776fc53c24e4256bdcc54dabf5a6c2e97082b88388de4436bbe63fd68118f2e95bc1301287ba5f186f12c09ffdb44ed0e2e4cff32e1815e398c74c957922d26cc8ce97c42c300f2d75e509896f3740e2e1d3769eabda19a77e32c285d72a50d994b08680c790469e2053bdaf8fd7be15fcbac0cb3ca668283f9212f32a29105f5d1ce6f5cc620ae77f13397ed09fccf4491fa4eec3e6c86ec29416c1061e8808f96e9b3289abf234a4975c0607a530d16390d8f95a0ee1f2b045366d2d1d8276cacd015e1c2c88158765c00ffdda9a5f700d70f8a1e5c06d8821e83ea32c956ce9cbdbb25d2da02c256f82eb7be4590ce2c2362816380f625bf8d3bae622b11eb45af3f67ea0e10fd73e16851c263b2e4f350476aee1cc9ae94f79271b0e4bfebce9a058762ccfaf599f82f00d3f2471512bec6cc122ac016ae7ea0b0e1f371e9475f35abb6ce3f0e769b6af62e4cbd123a132211322f4d814154fac9e01f132f0175cbc5d1ffd73f77db34821c8db1c8798c325d3f86bc93081e6dbf21afdfd9ac31934738bfe9599eae2b7d781299427a091575a94500dedaf42b9911fd8b46cba2acd6b5ad1a75112ece1d98205a76fd7cdf21f4453f4224fe191b60ac285b558fdca97e55e093e6d7a36253b7fe81bb92baff227f8d4a87416772b1d01269c0ed62069a9a8c212d07fa0904fe12a4985ec4b69b11dcedade2535d926f558f9e37682b5a98af393d3119dfba045916a7a5eb96191fcb7ebb916d8ed041896de00ed789cad5aed69166d50be31877505c44ae8ddc98f9b544e74250106d20067e476910453d85cd85068404e20a8f03ccae9f13d69f3f94477b633a41474a50e8713347d01c24e7f8b02096f3667ef759fc2082ee82e01e85a47f6820e3dcf4fcfb90a95231cebcedc7fd79d06d6665c9b10d15afde409523a3d72931b650c821385f8d01226f216b358bd6557327b07208972e052da1f9466d9a4ab0f9622a214e609288f25f900b755062d2da4b26ba7603eebe04b13e2088c5011129cd6dd6c8b65354890880acdc4a267e46c847d48556162bcabf0b2f709696408de108d6c0cc5badd1371518642aea07e4f15d1582bfef5fff9a39374b3e41910eb989c3d8bc043ddb5fa5067059a8692bad81fc74488f059b374a1b2323c5d1b112d2b5f4a9127fc5676c1f382698b81b0484ffc1667a1508c978c6f58a1e5014af1fd93ee6fcd6a29a2eab01ea3994946e3267aa3a481603aa9512fcec03c29e58028a43ee59c74bdd3f82982eb314d865a7881101e9ca8c5e09c90e496dd13dc282530b90f14dea3137d88a2b64c9f19e66db20ec1a36088ff9cca8bbb88142b6fcd496207549cfeb131c81be28a5e9ca30820e77ebe52e1580ea693fa340463e7cf8463f66f0bcf4ba2571e7fe957d0b6316fed0eb01f7a4e11a3d2958908167f9e330522d2602de2f9bef8adf64bba1d6a93c2aa57479e11306c02011a162e20916461d1e362cdc0df28b4c3cec7345b064fd046a718f2361c1fcf4cce04513901179ef6347059c7aac3aec1c4d4ac797008169e0c1e84f5d7537d3c8514d8807c098668df7c6fa73985c3abe105922336eb176ff0af7ec701ccddef08130b1dd3cfe74baedaf25267f8aefa4770d1368d5414adc8284806c06a8335ee11576c300dfe9a3109d69233c205998109049855e002fdf9f1527d95e96c705c27549b71e1aba9e41d41dbbd7a4b19fdf1a21fd50798df1a2b600181b2e3cfbe211e04fc9fe91cf78e2158995f64ab2ce4e23ae78d5f5330299d55a3c79273560ce6d35163f0842863049474f5ff71c23602dc56e272b1a2a6b5e255073d7003044a68d459dafd15a7c7cee706d8414a4ab7261bd3eaf1d05264362e7c4ed1a602ccf785c35df58090773408d83fb11e04882dfbf0fe58a9b6e55c4420956cfa2f0130b34476bc248d1c8dd5a974af38a73be2e88666cb25aa68243157a2377a2fc2cbb598b05e189229d8fd4bbb4c648eeac0fadb726a090de7ee6b86b9463423076acb6f1d88ab5359da22b82d6537a97ac7d729a42b0a25036db703fde11e07c4801cffc3930dcb156b5d7a9fcd011dc1539cc56c127eddbc9414220b259f068d4ff2e9995c24b3b653b4d3eec84771e3af38ad834436108986643a47dffb0ac236b5a8e7ee05a9e9b6cb555ef4a9b34b73d0e1a7560cc5b6175483f13570213e52e516bbf6fb841d34f348807145a1631920b6047e79a254bbfd53b14f9f1c4bf143b9367e3e9de3c9795118c74dd6a4e0e9ac9029d9540f55b30564114d236657d64bacbeb1d93d9a14bdcaace7524e4ea8b02f5fefcda494f6e495aca108ed14d839a7ec62a5c9b1ac07408a4d61c2237fc07753c8a4a6583ac062052e2a7845dab14e83a329a0c34118f16fbe2aced7cd3c5d21a0b4f85c656c9b5c4c2953ef4f749b536f33ba252601b8a06dc5ef2112f002601516837096f9a9bad51ce8dd4659e79619292fbf037dcc533948defab328d0cf56ff9233e9e776c8f707afaa151dd633e85e2da491cefdff3d23177e1ecc147c401287888696f4e7df0e0c3deaab958a3ebd1efd7c37eed7d905bd35815781fa14e063f5971c38a58d134af92e676e58da31b333b0f787c352dfe0e2dfdf0774c6e046dad7f73e659f03e9ae7110a47849e404918fabc1148ea5203b6921c779515dd7569461ab250b1924adb207a87a9ee0c7f7ae8536146527801c8ca956d06185b8bdf3eddbac3d2d6be1bfe42ddaecd73f4cca1c71adeb86591c13044bd628968f4899aedcecc412186234f9d8f1c1dafe57d3785ef5e50d67d45c9c12b247519e3e1c161162790e12dc609afa23341b494efdb8cd5aa29d5d07634d08126f3d6039a999b4cc450232abc9684c1228b9b6191024cc966af1a22ff5149cab9ec9ded5eae90b2401219955e033b9a5226c201edf1c382ef107238abcf271c869758cb32fba3f34fe0a791422689305651e48f19da68dfa3886a8509a775ab423bc92f057c7c4734f21fb990aa7a41a8eab0e299cccd42d695f1dbd35763b6abaca4b365f4a812d962b92317abe5f8994aff1b657fb038255b6bdce3be58f29f54ef18aefb649f22c2d187126fa091d2957d07e1bc36965c6e0467d08905e3c9aef26d12c712ade4b2c74fd26cf6b8b1e8c524e34ec546e0389e849dbfe5b16a9ef16d60849db464514a15f6e69d7403369325f37281e5153ca78a4009aba5b87af9b1cf979caf80b064d30f29898679a33c514a9827779fe0986a82734023461a6e4e61a1f6cf8c72577d15f48af8cbd748b9521b3f0245cf8b82d2347df252cdf70329e5e42a02302551f46a7239483cf5fca4cda60bfa8d28f7477781834d15d79742feb1b82d22774d1295cfcf5e1b8487226b7ba0624d6085a08908170c0ccc6fabeef94ecf609c3315a035bf559c1b18e8843a2a1c48c8aee430424ea62dfba2126bffda6612feba61de65c5cf70493929fdec2dab8ac8b774f9dbbea1576ef462404f471850000000000000000000000000000000000000000000000000000000000000000164bcdce21ae7a6feb8494b49250263e638d24838f3ad4820d5cb41451c103390ebb8166acae682fabe3bf38879bfd63578b5755e5bc2ea1dc964262f9a5c1ab1361f94b297af7bc113e4b043ee6834d43f94cf801f1a3a7fc230eb11cf1738a057f700223850e09dbf52339fcc048328157e460c8507cab2fe1bf90839add7917ad162e5c7f0c46e685df64781d28b29e3b75caaed90417c8d65c30aa44f2e220e4351ba05afd1f29115a4735a53a7d46e6693c77f77fa9f052686973fe0a8509e1648ec75deec32fa248096377b57f68d30474c0fb13e9f9d25dd3a81a889629062ca58574c973f79057099156bf8217bb6a8c895a2e98f1ad0402b949d9d71f9a7f1a1d996fcce8f4534824182ca2d0c29e36de739473758916eed5e1ec45037d8bfd1ec5dd45c559861aefdcdd6026461219e6f988f53ad81173059263d32af0f3b66e55e4cf60c987b0abb5468db6410d5a9d9583afdc55502b3b9a63e529c55599e349f7ceb139627d822a5100473fedf3374ca9d7f9541b7f690ef5382f8e37ec0ecd70ec6110afdba2c5aef25f2d73c555c008a7fa6882397432f0f714eed647ec232aa6bb1d87fefbdf5953826b3d4893e6b219467e1f984320b54929175309e22011ab37817792123ca22b299464cc220f9912319023ddff7101b509fa9af4af6482d782ae2e38a87e5834f31ec0829d0f4e0b1d8546748f98aefc04bdb0aeb9938bffc2178dc195705687cc159af32e5dc34b026ee2f4aeb471ef0c0b2a518edf26534ed22654aeee1c00288afe36991989638f1dc06bb96572f8286d4642bb2f8b054b12fd30ec938a979db68daf85b3b125757280a1a9b1bb012f8e268061d821dcd40f4331590d95f77f81701794e1173739c12fcf8f5f2ec81b8375230b44b3d8fa5943d793589e71855b9987975cbbcd8772d90d53681aae0204a799cd9cc4506787d63bd20628d29e8d5dad8d210e9a022ba8be9a797a1f2a8b7b298fd655ba3b15d80594f0a6d7835b261811c57268a78046921563f5a90562ff84873c88b93819bda3ce61c07abfbf9d2a50c551a66c872fdde52de431296a66c7ef10750c99ec695d95201b282d2a0c87e25456c794e2bd3a61808abb18e774c6b86eff5d95032a19c096e2c8ad8dcb435a4e48a6613ae0fbef7065b22c45fafa71e50e8dd360381559649bdc4072d1921d8a53ad2a0bea74883090db2ac13ad4cbc00e403122046e222612c4817ec05d3e0b2d9329471f1274fc906e120d1077db201bebf8fde4812cc2a688ffc860af243e0e841df73ec47cf7a04c1bddbbf4e5f352b9ed874932cf302186b86e63792c0e691faa73f32c3de2e251164d90e61f499a93cead92ccfeb970785001570332ad41aab65f8cffff11c29627eb1ff526184243e5738a84283deac273e0f7c0b3878c5062ca455c48e7187d08ca79acebee2d71693b725feea06504ca40b01d5c010c458be844919dcdab8207ded0a86a4c44dd7737719963ffeba365fd37b8584bed03d64c6ff61bad484c2769d28653872008b108cd99c74314721fdda85c08288c5bf58162cb92e199a4005c77d6433fe0efdc156fd15fd65199842d850a92209dfef840c4cabc28119a024a9195b4c7cb797c0bc65202462ef2967ba517c86b33309c70c4c24e43dfff0d105893da79463341ea6d73918018dd05c24418845c01e785918439d709e32a149419f2393fad32763b974e9c47a82b1c5eae283d6690c5905577180e44a000301ae9ccc30846f962cac96302bd13e72d8813c28c30625c70c2abb41203b88f2ac1429b051f2a1515a33e3b4694c82c16494f7122d0c0db94306e590a0626a81e10da4e01576ff6a8c17f75470490d5a3d67a7b1d69a6cbc84d10b53c08a7c115c55dce00a08ea1a68812543906bd60d98d676ead2a6915e6787e633ff4096105190f6dd28f05f05ba57cb55a5611636bc307a7c83c7a9d743c974ac0e2614d1bd40d7c10ca6a65a87b5e30b108ffca451d5bb0d2ad3cfed908482787d0be780752dd38be1ef43d76608e00fa02c1aa14c0d580221710bedf799e156de365f3279ad22ff0257bb966ac5e73c610f2a2e99cf2bc095b46e61cae2a422509f19d1879a17f44cb9ec42597699add6768a1df56aa99db31a2b0be179f923cda21561a36dd35050f45f0f4f05335d8d9915a8eaaf68c62037866bb2beaffee16d9880c09bcea7ea2f9b32cf3babbeaf5797872dbd10b1c9f962849dd7e60070d48bb297a3ac70abbf1bab0915f82afc7060d6fbd85ceb295047881a18e77c46c61570c0120b1492dcb2cd4d48471f844eeb20e586e8b861609c149ae946ffa867aba2050776ac2a694f0eee81541beab2c8b6da081fa6f9d095902261b00329fed412712472252fc3e3b6432ae4797d61eebf2ab6109847a6feca0752994054d388604032b058890305ce4cc8a32ab2b02033be99e0879948992fd0512351bc52fda2af2288beaf6504fc07d8bacd4ec799cd78f88d7c255d26e25a5e7ff07ec156d068bb02cb40fc5234e9301ba10b214ff9e5e8134570836894e523f8ce098b3d50c39aae2890c479be5ab18a7e408f2e7371432a30c1aef0d9cc81e0014688bcf2e5598a7ae1861b971ef69d7c7739198c95e83e7b776a5a88994f0d62f3635f423cffbf934b2cd9ca18d3b14b69fbb36763f868ba6940fc8accc8f235be28aa4001a984a5322a4de703c744758d5fc4bafb809867861ef8690b252251493b86220efd6360ea614c64185434859628452d0f0c4fb2bcf81b70536e2b1afa395b50d63c45e0d12de51e1159acb9225fc7a3ad3382c1ee0b0f82ae6fab1931799c12c6e3f0e2ca6059c945c5e77d9f597d280ee183fc22b484c8b6ea97b88a462740fe8d06816f2c50f90fcd195de8f6f7ca9931da30d83f70d010c1be5517e9ac723cbcee8cc08998704b161a96fc1398e7d10145c555b48898e28670903a6bf202c943b8baad401b5d94aff755be4088120a81002ffbbe147d55b2a7665617a172a2e0bca1d060f562511586cbb34d13b8ee2382d77967a42b703710ccc2e880020c73e80d23f1c370f312c5035a09d27c17d2b0a3a7fe8cc552f2aa221016715205b10371ecb96470129e5c03c2d393a07a423d68eb4c9ff7fc9b4e08f419b4e0cc7fd3931ba7fc33bbccfa528b1e0e7bc4ab06ce019dfc433edb73c25e09fbd23030a3f1b7c0cb290f6751550720b4e77968341f4ed223be5f7f87024b6e5250f6570ce5a7d6b0999a835fac7e687be79d61adfdb6344a70510d43cf550abe11d84010eed8f6bd67c13847ba268f30d44eefcee222d646f0ad5811b45b890840bf8e8cdca8e38f37cb9516f38b3432842dafefcc2581c57a7c28c379a7091dc067e67fe5bba67c0919a6c0a1ac2f98fcff7bfa0928bd52b3f25558e615d8d7e00327c117c986af0b633ba845d0745b173c74a6d0a2f825af15bdab82752929a2617ac0e9387a6f8d43c731687491707488d14e71fcf4752b0bb81b5b153428c2c190a5c4e998fd1d06b98a755523d679cae8c5f57c0d07b3b7067f80ad8fa68280c359ac018ea933a28c21b95aecb4a238b42bf4556626a349a496991cf57372a6a86a2694cc027cca8a641e70835a699843ad75d4af358695e580c2ae6f06c20c4293b374ec01e771260088ff7e2030df249c4ce473d1a2c29e05821a6de9116181dcb1dc9356edeee2ce033af7de94039d6c79b0d9c635837e2bda7009fdf1027bea6e56311fc49da2b9837388f4e146cedf189052043fe0e3f7ce8b4e27127da4d337ce7483977d9df1a72789e8a5694989fd4f4f35a158a9769ff89a787122017455a38e56af3c46004910b78c0291155e3ac08f742349e87f4a5665ab30dcc173e98a63c4baf5d78056d2043b9147a818889aa51c7c43ada3adbd840cf27b9dc3271b2d8dfb869a3ffb687f935353522d228c923903db31e2a03485590083fde809d8e72b244a341e19967358a2b287be92909e5cff6a567b9026257da2c25bd4e92459d5555a9dd0c175027c559ed69f77f2409141e42d53a7276400511a80c5fb492d1faeea7b9793502821370ad0a10c0db171493f373d97231c9cd", + "public_inputs_hex": "0x1f4fb320b39a3449ca428dd3600b0e2523f359729bd7030cfb5d552b593e1b7d12b41b13b8ad8c86d81aa9d8f81a92e082ee58152c86753779692c6937f09c510000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000e6cbb0fb234ee2d6c16efa7300877ba50000000000000000000000000000000071443b4c6219d9646c0705dffc34f20e149d96a850d9c9452d723b8f7630024f40fc1eb1cd8dd08f54b08ec8a840ac5a04ddf21e1657f4d9dba4721b22edf2fa177a98cf06ea991e8602388466d690da19c3e11d0d3bd5a341c8c4deca0995a18515c3e758692236465f6a26a5d50bba06187d5f914ef9268517c305d07cf2e8be7c7519b2243df85e2f62769c07c41c09b550fc00f6cc2ea94810ed1831636d53caf5f12750fc386918665a974196e42ca445a6e029616be05e3e677a70f1cbe58ba39d2bf29171e3325d93b06d802e10afa542c1811371bd1d48514e83e1bd0b08d6168c68eb380f6760254d3159601c95742d598606d179e1aaa386cd9b199a2206ccd3d2b963f8acd9d6978aeb48039c4153e89da512d8c278d4cbe4d913d09375b42c7236cf5ef0b4d772a004a327dcb63446fdddbf4f63de43d1ed4c6c4446552a48c57fed1b6ac41e64605420169b31279dc1df67b0df4c3067be693fb741d887281d912bf84c7454775cacfd04461d84bea9016960052bc3f611f464b4df248f01e6d44e74f1e2086072bb5b2fdee79456cda239c7510f0e9e9d4970a3fe73e06fbb06625c40cd813cca87761ca019333507a3163d5b78323784d9e395759b6ccdd9f1d6e71220abc6d015841c619a4b2c461e1db426b7a685d89bc6d3d1d896d6fb0564619933df1456799410fed84fce3a1eb8fd364a536f6013109a6e5bd74cdeb6481788e17ecd6d985a128c629684d4b29a55cfaa8f73ba33b69b679409fb45248988241b432bbbfafc27dcb63446fdddbf4f63de43d1ed4c6c4446552a48c57fed1b6ac41e64605420169b31279dc1df67b0df4c3067be693fb741d887281d912bf84c7454775cacfd28d93f3bc2749c2de32c6b7d48ee70d6fb6af2dda4ba0b5d0366c249712b3fb80c519df7d1240f04c29be318da0dee2544931a9f8cee06be97e71a1c99b1a64e105e348119c8567a1d9cc13e0b00a14f537be9506cf6a083cbf12152513598d121d9ef6109c9ecb38140c915ef89767ab4272d2c4967c9761215f47d7a3cd89a269e088ebde5f26c9cd7d8eb564840cf9f3d722d5b19434ce618947673f64f9a0937e2bd02fd06c1f2dfcd1dfeb733a6491c4c2aa01d9a08835f21cc1a3c1049194270376cb5600bcb613b1995ef33dc78955ff83dc2eec29c40c953bae367c324acd210d229e1c3471a876911679d666c7fb725cc96bbe8abf32c5bb88fa275204416916168e13ed3bf53c7185bd13e4499dbda8bdb22adc8b380719cc269781f2f1462e0545189c750b490c7096b9bd187f61b2dd99357902a9dec25eccb6c08b0c4418a5df4a900ba1bccd47db2dc8628e0b0d12f5d142ae5f0cbb9858b7a" }, "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000274e50c43238015ea0000000000000000000000000000000000000000000000059f17e17ea294782f00000000000000000000000000000000000000000000000c9df3c7d1c1f90d5d0000000000000000000000000000000000000000000000000002ad5a9fbf030c00000000000000000000000000000000000000000000000de213a227f53473e9000000000000000000000000000000000000000000000008fbcde03fda652920000000000000000000000000000000000000000000000000e11c34b9200625f200000000000000000000000000000000000000000000000000018d110cb7a1b20000000000000000000000000000000000000000000000009332ce29e5cb0ec9000000000000000000000000000000000000000000000000fda69bfd2f73cdde000000000000000000000000000000000000000000000001976ff26b1855e86a00000000000000000000000000000000000000000000000000001e966433079700000000000000000000000000000000000000000000000ff21e58564f35f893000000000000000000000000000000000000000000000006a888f7863669e42000000000000000000000000000000000000000000000000279b9affcf271f2bf0000000000000000000000000000000000000000000000000002ae5e7a39e6df27ac299d3526550e7124432b7b9a1228e997ac7e5b1a8bcf79f341054a22273d158994c9d7430c98cdc139b5b19dd89ab4cbcd9e51c3653866d0ee3bf6bf754b128afa7679a3ee03477bcc89d48282db791a17b21c9ac3582658b5dd91d7b87d20d0dd06b89d789a5acce06e0842930486b35e3541d0cb7e29459ebe8c29401209d4963b35dd572cb21a1ed37b60fc510868b4776b94bfe295982a342e2e5dbe0b933598e687fd3aa3964746f600177ebfe703de2c891c0717cc6de4862675791eff43b86c3e360401f844c91e23ad9fbc215e1ed26c5d2bfcce4e828fdc867910644afbbfcd0191e1d1bed4a42e4456da443e5403037855abccaf4fea7791e92274b7e5fbb06146901b5ef9bf21c7021199477d79186812151d90c441ac53ea0445085a26005f443b8bb9767ffe9f2626db8016c3e2fb2303e3e62079abe3e42cf4336e62f409088a23582b0fab5544c8e4d8218a4b24cdca589c38dbb4c4731a29051c26930a5d74a77df2f06bbfeac457fb9f8acd6cff6b7f4650de62cd6216acf35c813087c35074389c393e94ad19cffe7cdfdd45e8636a97abc6089edb08497c82d25a2840bab820f941dc403213f2f1df22ce4b47c0845a3a5dbae20114d0baad277734f5e1d172e3364f30c9300acbf66ab70ad240b92fa513765c0026dc2207778f3d6190a41bcf8c69f9dabc02933b0d679290dded9c6d603749f810fcf1c6d28da75a8cda9921c463871626aee206404c314ef3a029ad5241332a23036bd7fc56a06a444528090c82bffeedacb0cbd2f506a8d7fefe82907ee5f72da8b2311e27987ad01f51a7ffabbe54ab0e281ef2cac78d991c5bcb91a5e9b3038bd7c2b57793de7ea75b84ae06f2c022b281119517cdaf1f6a0bc5494d017b020530b0271760f59fdf0cdc7e044c0432e5947bbc91cf12d477020e590d772904450b143927ceac383338e9a271ab0935a523e8976dd77879d8326668df946425180389af7d344d31016edb5179cc0dcb71c78c0eb8076aa43704690af226f502502d0f9cd0454dccd15ce172f9fd1de22d15ef4d8ede4860cb4abf3a66a6a10b7bd16430bfd4e01885115404059a203393fa43d74a10b5d271d5860163e619075969c54590c6c4ea43253bec367db52dfb47e5fc5d1b4d32b4c6907862986e12c6096cfbeb0dc76711b435d4328b247c13cadb44834ca40b8df0195ec8933d0da081b7fa40a36384bed824503f20db2c49bd1757e3e73fbb14ea500db7e9ed18a60bdfa0eea4bb6bda048a545f6e8bee5259a030f463275265166e5254c6551d0da8977a24824f2e2ce529a72b9803a739f5fae408cac6f51741477ca989b80ec09d7a9a284c8de0ff2feaee8055a4a71fa01090d6538a7b2b596582206e660a3f8fa31d8128b30836559cb3f930d2f7f4922f4196240b994c29c1363c92510e79ef7ee1b0a675603c5eb7ec15d17d62a668bee92c6a97da7f6eed06f0a1fd26a2308abe247506731af691f19b442bc9a58b0bbdfb19d116ccc687a24ff3c427e64faca338475c96e05c8986c5eb26e080a27985920f6ddf2bfd06c53f853f28c8c04257bbaf1d07d4bf5a0abc91ea53726ce5b7d829778e642c8828e0ca6106e24be30f342518f66b1bbc2a6ebb9e0b78c2f692a74dd5e4c08becc1ea5f2b19f5018f6acd7edb9d6d5b356e09a7c8e6d4ae8110418d47d164a113ab6e9fb803f692d6ffbc33f0a03122f83e8097e9de5dab9f5432b7a067bd72b110c0447505b883e02603fe8dbfb31f90df1fde45f99f7279926c24fd909c9bfa62f776f12c99d35fff14d217ec307cc454a60746c6e51cb6e87589ac945c189fc07298b726a5838880b7bee93461a1354ec39ee6f289e880ea0c56d3e299982cc9165249298666c54bdaed4ae49242460ef710de3668a621dcb6d420f4a502092b9fd54724c7720dcad871aa2dbd838d66338807e1c900d735376566b5e3dbc5859b1de62d98b29430ceaf92d0ad7b13be18501f6b3c6ee3d5fd8ae6ca1fb5f0461e2c0b1ea500dc5abf4f9046326a758b190b3c9724be069a666ae6bf824f2cb41370990b313298d3aac27edbf9876b39b9793002f253732e579a2284c852db39f0596f17fa08ef8293aa965d7d528a306dd85cd62c0a0b3b1ff9cbaaec43c1f59467a809ce53db01c2ec74e356c0932d803eeecd2e89e3406a838d761fc1200cbc93b90de0bc4cad5a6990ece0daf51408ec05b81126abddcc78ca3cab92c21c1ca2d6214cd47029334379b17cdfaebf31278c1222859f186d6be68c937dff68bf5b7d0570f5a7c9bb806731bb1a27f9472c7ac1089c020c1259d71e060a61ff6717df044ef06408f0bee60d68beb07a958174dbc522b8f1da88f625378dc78a4331a3212a501b1cab8d65e95595df5ff009fc972b9e68331f3b52a5e6ab57840d6c98132713b7ed6899bbf362d8a3ce1de600c532ba062fe090c147b06e692a84184b0ef0562323083dab4c75f905f4b12456d5ea2a30b7e0f9f550898514011178bd0caca0dd287be62c72f3d7ceb7258527469169036e961c27dbe305b73225b15e013c616966ce84a0ef385b8992e69b4577b94867054d4f1be64b7568d12ba0ac2f6561bd6ba1b002a5741fe0c30e35a5374937263a6bc2ee81a896ae77be14712936b73225f423be009558db37314545bb54d7b72e93fe3a05009e54c334fc3f12d7332c38fe0c0c1a5ea14c23bf0c0161034a9101649551f7eeb61b1e91c6061c72004c85318979426d5115e3d23a76a8e00701cada72ca3a4e1fc09d96a0c9015acc104c51483effe786b91c7201cfa2cf81ad2f83ad6af8ff8f3367acc01418d223baae41fd64d9c5081528c209131a697ee656853836d24d5f266a6581970551531f3e5b1da57eb35e6ec73b98ce38b88cf661f25a0cb2fd3967277b29c928ca5c24004e83fdaf8810d838b2955e04613446fbea306a4beb875a463d118a2d58faf03175b910a088ed0b3f21d1c582d0ae314723f4b9d570cc14bfcbb04c16a1978d784d0f01004d401ef9f6f8cc93903cbcbbaf1d82dc25680d1839bfda230d45ab9178bbd752c543902f383180122901bc672fff31dc749cb2fd4e155421eafc60963b91198077c55af3485011d9dfb40304dbbb40cdea29d243ef045f2b80c8f661f2b9e98b732c3ac435e7fea439855d56b75ff8b3dad072f5191100064d26f2f9bade02f99ae1778c1cbd5b67b21323279a917b6485441cefa9869a008f99bc45d2fdb462320933a3b89947d22c244bddee86c2ac05ed7456e3d3972b2ac1aa7f752ff8ed765561d02d85918d260a7f9f4b6660f559a8dab724f2f212bbc55e76f73293b2febf6449ea68485dd6a3dce9af0983b159a4362bb1efcd2a8f2f95e4f13c9c570f69f61e4a66817cbd536634a16a789a2d4ce32590aa7406320ea737188269f8dc1b3321f3c52867794b38b2444250abf69a656466d0df11a1af729be8a3751830047ec0e363828777ab3cf6a606f66b962ac164c26cdb048de3f0d86cb0c520474e945c194a36aee899e5fc22b70e76d70ef55dea8db70e83015fb98872130bc9a0f88db0975a2b915c7ac77459b722820adb69a12ffb255b573884ac653b62a76a349bdbe5618f43360bedbc2dee105a4731de59681c0b4c5bc3e620d67ac078324e6a70134fc90d994065ed5685f509b03cbea0b09c2084818b486256f8c3fd3dd8dbb31e87cbe853be43db69e5196c0649d9949a181fa798f847250419c22d8354f7296d954ab5a950865357359bfc11746e5b32cd2310eaf6766323dcc76133e773f4573f7e06d3711a6c617379cccd5c5273f17a0687a86ae4843e6745e85d46197434292e99ecd6e94956f7de41adbc71b5e3260d384ac3ab70617b1a0827e050c3206f707478c27717216f269c849d03f4a0fc2d8b4f0a2d1cfd8e91b4eafd322dff635b9c141bd526244d95a791e0ef39afad25683d367dc245a3137e31f2732e9aa85e60713c10e9f1cdf2e7800d6cec807517738d67795cba0e54f3979ee0b6b355b3a9e9b1e0f023cfaf7b503214a9e4d317672a16039f93ec5505f748afd302a8d165e66a5b1b8d2be91c4c2ec4c558e82397a06d0c9ceeabfb63767108cea6a56738132873dbeb99b1ffa05203139bea1a9ea13d709036926b508a343c7f714833ec2d47235ab35885c7c79266c0e74004ebc60d754dbf647d700cda950a2f156e08dce144987cb3ea3cc276c83fea1c0559cee3b97a3cd94fb36a1f1d61bc913acf5323961f569907d01ed05f0aca4b154ee2deb7c3212b6cc19c39575fc57287f812a93bce77235df506ba9d75dc3325d9d4b7c2e1000ab4e5162fd6790d21ad791378d09f6b8d0bb9bb0f4030814c10ab4ccac4de07f03217fbb07c39e404384fb1134d577f66fb14e31655509d4617e88de6ef335a667ef2fa652515fd48b5b9891453858ee0880ffb26bedd349d2d84d40d7bdd5adf773c9ca93f2a04e49bcc49c17553762d3ccf39fcd1dfe2fc2ab7b1e25af6022ca5c4b201ced659841628d77d381075752f1337ca443d247603c594e585804fe8ca91420f60b279da2c7cc7a5bb502bf8a1b00474384e536e14fc2ccae26658c8064c573cd385313ef4da868a6736b0cc6772c1e9a49471a62ecf1f997fbc3511cbb8dfa5b83c1b75a5afd2993a4211f7000d24ce4d39da2d20c7a4d21dc13bd740130e6ac6677ebaa54a00cb9b1895f5907f918898d0f2ab2195b05cd7fce248b7b1356b0c476f4f6238705a75e1075ab34855a64a22603a2d49931302311cfe1fa1558b79dd6361098c827cf8de6ecdd455340565de95f6047c4ae833f47124a4a76f209ed426a39872b55b3d8b02aad35a6eca92bacc87046c41f8ad45f520c090d289111144b5fc3cde94f8ce93502552200b29e1756e1df3d1bbdaa407a21f20fe0c91a6c3496fc2f560474f31cdb3c0126b2ca06ae72629d92bdeaa93235a9c6dd36d5457bd217a0013424710662cd691f7a65363d32955f09763623b3448223c675a12a1f558c292842af84035acdc07e5be8caa341b293bd328232f70b4b1d9c789840db2ded2670791d63042f7402c061ab30dad136b5bc0dce353f75d97f26a372e8ee4627e94276737f0a1416d5d89ea19bf9110eb6f6ef9441016e322a2ab487b72a756356018089d2124897b6136aaeea11412bf15c214030f5c778dce5d38aa2dc924ad5ccf1deba1d1e0c51227e3feeb7615f380c14b8bde53e5b666f0c2102e2a0a54641afb8d90f2fa045b30db868ecc2a97a6738f9c70fe550bcb8470101e571d5539989700d29b2a59c52d9dc1f1700cce3124df00e287dc3d7fc1a0716ca5da98f28041e20375b62f742cc1de4a521d7932002414a663afbacf2a2d8e19b45acf35c11e679b5f6d34fd324c7e0301154b567209c7e52bb9d800df593fa1ba125b26833ba877cb66fccd6107a0f4fe177d7aaf90a56b8863321f5c3c4ecbdd2c1a0b8ce49397e6f9d1a9fc4b2ed0bb2b3b7673ad9b6339ce2e127aa83bb4994f22fcc602a7bd8ef2b00be4f62e24bb04ba69ddb98750e773625c816ea897316d2d063d6bcc9b00759e9fa279c3705a17b2ecdd3adb9af7dcf14b5a21cb1d0c8c20829f902f5cb3cba2d3aa705f6dd70a812ef571e2641d794a52aa575b1eff7ba75e0108cb1455b7e725f6299255022ef41b4b1747c43f596725dea53d6b4e431bfff009ad1af4143c20cf6370301516a8b9172b9133f3f924b146e95d960d95a837142938f647a8ee341ecf52448613b36cd7c7e2d33405c1f931e17fc7fb8622e830209d1830ea8f3ed65c544f1d0851cc7647db121b4f15bdf6e5afad37d4accfa5d67991e9dec26d059a7cbc9c22e51042e13c1bffa7364c9302c502b7680c1c098064122eeaf8d7c986fbe5e12defec49ec61e43e2708445e6393df995a94ddc666f1dd323fd45853638ff61f0f65d3b273d213e7a006098c0c495ccca8fdf649e0e6dba1840982ac6fa367620cf9b0f1e48159ab822da05d248c4d7c748e1067f3ec6340136bb7bb89f2f67223ab3d06ff27ff8a9f382d122c3b8250f2daaf97997cc6215fb62b39269e82b218e8fe4eddc867c358acfb154ca425ab6f89536b56240a69558a24a9f2fe833e2cc0cd6fdd6c7fda67861212bb2aa5b15b7f3604d20973923d92500d69b60d292916aaeda3b2409f038c7ced9f2821d0aa09df325e8ee4deb25695f107a199d52075284bbc500a1d8ff500e2a29f1af08605ed3a4dd9fcfaee896cfd508093ec0795a99961a9d8b7a7ebd429c142f1a52935aadc417a1494e16ccd582a8f606a27cd0d699b98ae46be499a9be50979783a2dfee266702d614bc17334a4fa5cf803235c71f610f627f5651c286c3548390f4864588258a4fc5bc704c9fc04e9a32f29146aba8d2871dc62a779772d5b0e60fbb9c5a3067f0e09550230774342060cdb2f0b888483c433df32452ef6f972fdbe85137cd277dd05dacef6da6ce836069f2a979d656e3123fa1f583b0e96bf24b9fcce569d04a5875ebc5ad8a757b60d1955b9b8d8f2ab846882f7264d4b958a610d286f0de3ad78f3e8ad2d36c9d617fac1844665306220f58a2e078a3a7b9beed30714b1c98ccc97835aa72fb08e04dc73f09b52f608ec9ff811866da284ffeba9b1dc991fcc9c1123bb4ec9b40b22e1203591edd3008336ede805a586af5f55e025e0c5eadd9b1e15030ce5f7b90820aa339f10bc3c697d3f87e8f2328d5a9854509969718a3677c029ff42753b0f1d11cbbb6a8301816ae0e3df81c6845e70495dd1f23514e899893f232e1ee3004212ca190421bceb21670d40f6e24d8ed22a77e08a643eac2c2697b3b4a1b024d04b2e31b4a594b25bbad952af7746c5cacd7c547869157bad1fdd6c6f136c1d96e60b2450748c8038b3f062310ef969ccd09d9b23214c0263a7f514767d51264ef41d32d69cec979817b0fe3a7392e4da263887edbaef9bb602d2a19e4f4f058c30dab386d374b1765c9d0f63fb160c333a612f6b619184df5e80173669b22e8c33565af82730493c4584f6b4253ebbb3c47eff603e26a6f2335d24a180c4044d9915eba47b365bf8009b9ab3af799ac5a92a2a7591851237b12182e052851ca4a9519b6c3ce54392dc00e31213d1987c972c8ea0dd5f191b74c912773b201e900a8fee5f7ab2a88f8f7487b31f4bc93765698d56df2c5a1f381034986add1ad05db3e4d1e4f09fed5ab1e4bbc7c4472041c9cb4972f1332117c2c44a469704ec71515df6ee7073afb4c5bfd91aa1a3c4300d3c7d1d5a760ff17f984d10eb092d51b9f8b619a50fead55d961176e2d127c4b7aafe0cd618eb5e45d2f1192714c17292f9fbcae5109189c56451ec540ae5186160d1dbb923a30ac966cf559829e60bd688a491f036b7496acef7d001c40affc1c12a4a14f91627e95f2068390f644ca843c7c7f885c85de7686b664f037ef0094cf81b7b9d369538267872ab15c1ec9138e8000ff1b5f093470408765a24606539cd61eea3ed83b3e4b409da080691ce3c77e782449f6fc0013f835fb9801f91fc0d909b4c0507cb16b885e619a6bf0facdac14fa8fabad867299b1e0e15987815017e5ed11a2e41206148422c140343c29fc377852181a168f9f8943e2ef69ff35232bdc60c4c2ce9e1008e20c21970bfb9159377cdb8fbeeadc9ae6b397ee5fea1891580598c3a6a69073e05a8c0f4f50c97ceea01f628baad9a9f2c31bbd13571121404a0c47e2ba0e6650583e8c8007859d694f25427a9b11930cc15259f9ed269287beb265de0a2cd5c0599673fdc867e1105b15cf31c7df65da2d668f560a942bbe1e79eb6a0153bee1dccc266a1a93ab21627de45588a6df7a1bd63424fc022134813c81b09d46b091337f7928a5d3c12b844166b15b29f6147d12b226d8d72ee760cdc97ddb6559d009557f41bf25615402879a1123d23c437faf99e87158a6b079235f7dbe70da82af54040a6eaca28ab4887a90a644c8156fc756cc2646924180e33ea18efd3611e50024f90653d78bba33a553272c82d4944b4eeb7e092a0fb811e0e9b57bd761bc388db2ba75b019e1e5356e208934e450437de9c66f044ebcff954dd24fe541ee32319b4cc155fc7c328d539d0d9545719a2baa3520c6f75a3ce5fb920ce0104ce1468873394faa870c861e1b36320b9acb37aa867e63343443d86e30654f92975424db8bd6bad25528cb645b8b13dc5ac11bd59b25da99b002b54d28462ac1ec32e39bd0d42cbeb5d2d886b0eedeae9fd1a2d0bf63896d7881029a6d364f0001267cbe17552d99cd0c6186f834b30a186b1b2336d9b551c487bb1b4c467031871224f6243c5661cf161bc1f30abaa1cb0815a27880b10b3008e06f92d8b6d1c527b4bd1c1d9ae9495f5d2781b9eb915beefd0f646fbbfc70dc6a1e041de74280ced9871585e00e4f4ed6c7ad09ec3cade833f314532383eb59fa436bcd7fd13c1bf25e05097498dd1d8b5c10a7c03b4ec44d950b5678ff3e36f4268873f7b243a5beb6bde7464b980ca4048b894db5be0bf4bee0dd8f035c04c39b8a364162dd0c75e01f65a4853e0dfd4cd85d9f2df213d090c025227b1a604d426da9f420e392217489b4d6961f5e059abdff7e19b2a40c5fdcc3601b08fa591e15625ec1d23b784a1383efedcc10a0f2bff1ed1412d769eb62d43d2b3591e25449b46ec0b81440c61e8469b92bcb3e63cdaf153fb51a3ae7c4899545f64b3f5b8bd0da70b9be9795714c934a47ca512b8de45fd2073b37128e0679554c8a1310ded4009230401de2d429b87afcd8bf213992707db742de262d29ad9b5d919ac8c4aac2c195b1c16e8a75e4bc087c79a18b386e8c4fe7c354b39581b5271910be90c6e6e183cbea1329f34e6d42fdbd9f87479e806eac3a2ba72d1edf68d801e1e234bfe0bc686d2da396aa64f6e45f44c0ab4a98f1f27f98fb6075f4a8dc3ec788aef5913446a33bd535b8e18b788f0d133ea6b614f51b42ff22dd904843ab590f371fa17bcb96cdf86658d2fb52fd18d7387f54476ef9af4c96fecaa2767c7b7ed8d3e1fd053b3d7562a404cac627846b565e56086a595f3e3939fc5820ef8e132d0652c1c4389334c37a151b076eaacb18cdf52546e7f6a570b8dfb051e29ec156fed0ab39db430469900dcf281b81f4b0b229dc65cc1cfb9ad673ae6e2cc52bdb9ab152cc689c163d10e151f16aa5838c4410aa49d241c69a90b7c0a70b1ecb1ad4520d750e60414326413f46f13e486712e1ec52d5615542e3cceb0e2699b89a24b2b3bea775489bb6e930446875984970366632fe619a2746a06014ce0c1f321cc158005d63bedca1d65a9b1415f8e3bc2bd4b631fd581e22568643237c3a7034029a5f8ebbc4baf602065fdd8070b7e111a6c9962b0ed6558b47f87420450980f2c7685c703e7a93cc0f9c9e1d7442f9993d7936a337b4342507e3391b3942246116c8007283d673a4f4c67504f06a21297a6eba41f92e6bc7aa2851919d5cce410ea7e206d399897438f3ce005086d367db59c1da8ff90141fdfb83aa71cae9c0e6cfd3a2ce1082dea0076f3a70e9641c23a25bed51928210379a22a20a44363259903c82576c27417f8892c7d2ad7d9e7ad32d458ae13eec4307925f5ced51200eaf15cb0a83be99cc92ce5452d4684130a463e58a9579276b84a528147f51d23dabb1213e833e9aeac5c3cd28957f090493a01f2d2a4bde724ce48a56218662507aa48f1967215d5715f760e70a0e3a5fe457d81a16edeb2d2fea71a358b9915fc9aa20c2a3079422d7eea48dd2281e606d81944419c14561c8162e2be05721576cdd4598b653cbfdb069ff3d659bc500a985de13975c0f03b751e5c959c2126976e6cb104bd8e800e18949783b4428475ecbc87a0381341ded87dc075f18a1e1c94a43acc261ea8732ecfbc98a75f093a2714c49a93cf5fb63d8e8919b2f317a543766769585be32d9847670abd090d1dca7b02362d09c5a9c3d5bd89e0f12b8f35148f2741b38debfa80d8bfc9b3b3304a7d16a0334d67c15bd9e59b63a5094b80128e75f1c72fd4869cde46c22a41f4e928bf12c07ed7f1179d6901d7eb244ec2b2d265b5c4fa37470ba64e697ce2b512843d2667a50129b34e218c1f8c1796f117c3d604e26f41df33b435c1f2b5a223deb47403f34337ccb3f103d27e18b9cc27b33ed1cff29f5196b89592d1093f112d8ff251caf6fedb445c2d83b50c0cbc652286df8f53d7b692398531189e0b9885f067a48b28f56720f064b09e2302555f53f43aba84c4fc5d295dcc9424bd891955ecf80497e8016c875f3687112bb8ffe8fa8e242fc2f70a4dd5eea97729f7124faa4dad5c3b55add76b938005d37fe85401ac0b0165e58f3c2ed63be8b24091d43fc90bfb9760b1399b321c243f86d81ac6ebd2c6e964a86f2088c9399e310c83df00be01ad1567c63f68ef1396fa0625b6e1aa243560d1a4ca16c9c7f654238255854d680d2a47e39ecf161a5f9739b80822204acd81b0aadb4f4ed5c0ec01ad655997aa94be0350edb3d02201fa3856a2adbcced5a73023b4ac48b107533020841e267bb92ccdd5a0b5442855ad43057c3e50ce71b6207b9eb5e592c3049cbd3899d8b93659152e15a3d305e36a8cf3b8d1d223d5741cb7e3e5409556dd3857dc47f762241f417604ea101cfeba0e4e49430f12fdcdab2d2bfe41e03ca0a6d0097370ac820a85d44643b23063848bff646413831b237fcdbe0dd52f46157559b095ff1e205e13771a3d2000ccb559fabd0b57a7b4250968aa3aab83ed4904066104bc0aa30f674d268ebb01fcd5980db3033877a5b8b8a347179a97ae2980a1e021f9f422b3c73186781a1be5d7b40e9994409acb7c1ecffb33695a4a2ef950adce004671ac200d9c9ec11fdf0e2045b05a11880de00a6f543ec757141569d140848ce721a47520a046720501a1d27ff21224663b017936e1b5f42740ba5b14183c8dfe9205af97bfc40c2df0aeb19d15d1e0eda8357834c30ade0a39e05cda38e4d876b6b92413996d1112e07dca6f16e69a42e18f625ae4878ab71324d6b2bcbcfe94bca2d7cc7658510b2d810c41833fb673074ed5bc02709ec24c5d1a8be0caa6dad7b8e63b8f43b11feee70fc19f71786fdb92b98cbc9acf462b020be1c6f56516eb5b1dbfe5c5232cdac53c4765f5d3fe3c2f23e56bbff3ce4362f55aa40426a62b0792d39ee5370146c50e27924119d7fe95869ea7b3cd20553d2f1b8f534cd244d6db046ae5e303d2d35f245aed74e09d173515b6c8d7b04a58a9f8d7a1fb315e65dd5a1596241bfafe81afb4643b8bdb63448380c5e560a55aca12785f1c1d8890ae344d5f970d28cf753227028b1450a848b3f4ee01c7d2152f4baf3371d0f0672d9dafddef25051770b9c248ce4ccbd7feb035765b83270bd8519dff799a253a19a70f9ff316c6c4523b65e011c281e6c01ff208e9348c3f5042e454602ff194dccb84413625b684264dfb015dabb8ac6455aedeea7db7b17d62a93e2e697233ce7b16657601095fe4ab7e752fceada9f771f780bbef5aca23b18f28930bf8f4da3b87125a23030f1ba1ef7ae5d0494e6d014737262d7cc80f7f9c5ea40a667d690ab5eb080e804e248b500cd1ec41c6195b57e6b2be6f006829df7794c251ead90935f0e00217daf4ce82e6ed33d3253f6cf91d482a466ea04efd6ebeddb929e485d29a7617335f8f4166a6d239d67845875d520b2e09b89f3d4149f0947de8dbcd9e53fc04f163b282d76662d28a2d640c32e75e14b62a8cffcd36fc70dc803d686ccbde0a0e56205b59a863fc6c3a04dc12139765da7f3372bc9b61a20304b6c5247a45149c05ed4dd039f4526fd97d260b2ce48324b4c60b76bd78fa8299ff808782e117aa90d0dd2a7d5d97c3cd4584c46aa2e6e5ed8339d4ca3c4c6ab220b222f37201f6fc11a247203fc11ef0178efd35c4b465eed3e1c1ad6ce0b722958251ec380891a83cb4fa4474328dcaa24a4a2552a96b90c0048d4fa09bf55e37460a6153271c1319c302a279ec6e72dd16f68a19a5148fd66c6ea2168041225c8d174e5e0baf33b60269a6d1eb136cf14c134b6cf48103eec542916aa381526079fe55e30b55423a11b8b409970e30304278d10daa1730951e6553df0d14d1d00b4853f12af7bde8ef3b7271630c34548575581b4472ac89942cb99d5c2ddaff1cb0e7df23ea1eb4e1a01a2565a553672bfe3e4de8ef6e8221f051386bb025f6b7d93a960b10d5bdc36ac410a77909e908ea8f82bd8fd262fc195f431707d7ea9e9917561ae93a944477cfb8d5e858062e03c0dfa36049dd6e08d83b3a6f66bc0cb3724b2d2cf2ccd32c04528207cff392cf3215001248b480cae65bac809a60bbb275f02e837fcf8601e5e49f4bf4e9904965177e3fc78cf54db4bf5fda8ebe3adeff0a0ee52bba99ffef884bf1c679efe5c1fdd82bbfa9118427379bf7319c6e403b3c15212499fe05d579c5bb29a9647cdd504affd929e472c8c0a765b263e20412221a4143e8ecd008005e4172a80864b481fe334eca6dd6345916d61c9dc53e727528a7fe6b5fe07802c8255582348f0f89acd339960485ca37a3e93db8cba2dabe0429460a225d9c7d11980630ac4428a974b457a348bf548a1bb39028ee90e1a72aaf8cc0dcf1b3640cdfad8bab296de26f2c27f2ba59c10a5ad9816c6d135f2f0e30b18b389446ff804d15028276a2c6cacff95ac841df51cae506041c2230d123ae9e84f21d4b8b035f0c767d2ee38a181a6cd888962e19561e41810d25eb530e20982647f2a6c88f8a042914379e4cad105da371a9bd7d018deda79bf0179f09c7c4c22c20c17f2894a3574df4ea32086a99ca101b942eff7d9468299c2e64216b751e6a1ad4647cf14ea164d2afe1dabac100b74f72f674842ba39f41a4e421398d5f96385b6eed0dbdd088ece9a2c4883e1cee362426a69f4f00ff868a6e1ecc411663ff9029247d94409330cfec1da09ce738137ad51659eb54f164c92c2866afcaec6a312524aa3ae724f3821d09879c8b2964f383c4aa5ddd70aa504601ec959d30178335b6c0534894143f89173f1c2178940bf93fc90b29733d51f315cd28ec05cbbf19046ff479fb94699400de6e5a0a343e750ef15defa06587d72eff79f85d1f409725f62338aa1b73b59fd1dc64d547bd554fe0e784aad3345f178209c19e7fb529cd0732c1d31ebf5062c79119f4c5774758a0d3c090404ad5187b5cf6ede0b0c3f73527cbba47e2e133a7238549a59e7fa5f9c52350f0c4b8268b61e4dc217ec409f10cf07ca353ff16e7c9ce15d7e4b4704ec4f5979fd6c1217a56d589d9d94a594722984500ebf99c141180bd471dd2e59c1b5161c894ef25016ffd3f6de21ae544e50608de70afc1f10b21e2befd09f3f729bb417975032233f30a13690359b3743382ea3a61de248a0b44b79e72d6bdfa04727bd8e2181359219df4fa79de9bb68167143b18460063d3263eb948f7f3e4ac55ba1e83e12e8e03b44059d8e7aa97f9b8122ba0f91c805420f2dd299c8f0d40c74a928154029df76d0084dad12b3d11e65527be81fc8c15b50fd3cd13da6eb84708c2a34d2ca2f10e6789c1d9102b61813357d37eb282352af21cb38accb85ceabd719b9717a606345f550071685ce5d7b4e8c49f2fc1562a05ae44786162380948ca988121e0bd48e7918d566279898e28c3456e9f48b7c6659baead75aecaaf48470aa40528c23d9448aed33bcefd7605f2bbf5daf4ef9774c6cd73aeadc18bb451a7b325c37bfe73729f527cd465eb4bd5dc46a094e6e1aceccfd2593198c3d72c66c40950bb4ff6f949f4e2e3650af16d58fbe21282016c4f786f668a4591468e7853126e1d414cd3279c13f814803c68ac17807d86d493d7400ba54bb4820982c5e1072dcad6588b1f80286e384cba4f86c63ac2c381e9a82f138c5777de89601f1e026ba4d12f3970f0c1720a1174e6d30e87f5046717293806db61ec712f2f2af9250eb7571c5c3fc64177f78cd7419798fc1e3c4cf81b5855af937340dc9d2507260707458e85049f268bde1875f7ab5b0450f7c6d2f3a2357aa2f2af75be18ca22e07db0f5cace9cd5c3bcb7d14c3c4229ec33415eda000f200cfad4481a74e000f788fa9d0b6af042338db2b28a086d75d573886f76effb2493a0c1c6079b160508fec14e24aee940397fe51796ba8715c9edef1e64b0276e2e1bf57e9da9561380ae3b40ffc99b38802d763391b64a4d6f06a437da9b37801718960de0299121b661515752c04b38002b3d050dfd5e73bf5cfe05c777fb84b7ee06b59744de1cedd3f90abb9038e67e34502199b94711d7991b84f6fe6d97415a66252da87e167df5b14c7fce6bff4fde042c64283b7fc884405dd880a9ec579d40882f20bc0bbb22bc01826b565f74898cce22f8dcf8a33ede13f9804b5d346746074b619014de9aced457d5733f04b24e3283e1aff240a5c99b1f8cee22c886fb5df77b20257b9abd7bd2f337b78294e966df5f644251e97c6484692cbce4a066876cd0ba19828ea5e5f25f0e4dd5c434d40514a86ee67443b99f5b92f48a046044e797a9120fee14c2536100e99c34111357a8561ff2275749e362a783dede851482cccf11ef8c9cd95200ef80fddf456a96c065ecd15f98a62fe1db7863607f503abe39", - "public_inputs_hex": "0x1486143d0564ca941cac449924e8ee74c1b4433cf73a6b4920737c223ba6b1e113d3a6e1ee0c2390b252eea2ac285eb7ab99ff1ac03a2a446a00a036dfa953780000000000000000000000000000000090331489384274c12b43fb7cd86990be000000000000000000000000000000007b6b44c05ca9dfc4194f954eb77bc8f51e9919742ce609e9e07b6a91eec28f3d0f8cc4fe94c8f02f425b601258f389400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000505f1a42155a63345c51416db9134e24a473534e1bc7264e47c3d8ceeb0048f3f00f750355551fc21f4f8b099e8aa9d146476b5a3eba3f12ec324353d52c1498e0f0a7cb8a62c14a214735dc01b43a24c55e0f368bb80c5740f91d32c79b109a902adda50f3a68b086921840211e0be05add7ad8933f82d689d427af65040b9e42190c3823163063956218b83ca1ae19999d5986c495dcf68fabffe666e78796909ec0921581b707660aae7e03a29bcf5f6310e32bb1ef1bfa21f265e211ea0dc1d34da8f75be8d59fdd8e87f406948e636b3c1dce3485b81e8db02be6a2b619d04097be17ce35a4c426eea7846e3cf55a6ecca6207bf3e1453d1e46a962e47c704c3f121cbb02760f51ff659d1a9699301fb413fc3021b4ffff5c1d00a7b57162a19c999035857bf3a4db9a179b5166dd723b3db90b27a94e60f886b23dcd1b1000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x00000000000000000000000000000086128b152735e8da12c1ac033ec692f44c00000000000000000000000000000000001e21534a3a2232ee90f8e2f9ede57000000000000000000000000000000089e9c48cfcc85b7048d5052388b110bd8400000000000000000000000000000000000aa9a9e43aeda54a772f9fc8d5d9a2000000000000000000000000000000381311f8eb7dd4445d90726bb26fd72bfb00000000000000000000000000000000002dae99793be3ab745147e821e170690000000000000000000000000000002ba60584ddd5bda982981460e1215366c80000000000000000000000000000000000160cb4d56a8c527aa83d1b9d79f19418cfe52e051aff2cbf53b9cf7bc2c5f3bbfa3d740a22b2dbf949800973f546cb0e4ca7825cdce5dbf21c2fb42c29769dd166352d60506007c52d2b57015588762ed98abeedbee9e187cfcb08268ef03668a382c81605e774b98b5e3b9a655fa52b226ba7eba6214be6c69f68bd6d7a476570b05544ffbf4e938198cc75473e2313d282bf8d43940740d15e6e94be994b2e75e6d4697adb95d145160a76c8cf151f7cdb0091edcb16eb8e4f9a2dc318d1d88fc24c176b134c8a87518085cca9bb1586698712b45de27b1867ae6e746b642247c57c88df21b6d4e517cf5b6beccc116f67fcb2eef6e51e463f45e68751c0964871d704aa7a64bcdebf54350fa23c0fd92cedb564c7a4e7a9a232478f5824c552a15c2840da9874adc0ad87bf842b1d546b3ec948076e8137207b748f80eb414866c033e9420492282a8d84dfeb691e4902742254a708163bee0a0c5fe53207fff179e3b5516aa3831bdf003d38c213b7169efeeea6f54c5763fd246bcdca76e0ff570cae3c64525480cf29235c821c2e5da08e84e646fde20c21f493251da8d53bff3d4ab6609b95a963e457aa7e2af4c860544a0cb6a77038dd9ceab55c12f6272155083366afa12e0867604c8807d8bd5e7083e3c078ecab1b51ceadf8fd1d1788cc508613afea83bf9dbf78a106be4242470ac1e254ed76019c06567e73aae6669962ed40efb469318906af57117c534b16c0494f2f2199fcedb4f95b89fe361dc08a5516d787d7f69a861f4f2d99cfbe09a264ff864903885662c0d021ec94288bea6213bdf757ddac8c19bd16552f1c3e101f82e34c4947f0878949f25f3137f90c410433144b8778070dc7186d9a2c8765e0dc215692929b15025c4e5c5ce83971f629fd1c87795babfa6f012f250bec1dbcdeaace9d6a0d0854a023227b1ea286feb92f048fa7d360ea6e1f7457b99af5360dbe9936338600c0249f677bc174b33ffa80a0dfd7e793b42b044ff80961b1729783f7a20955649e1fe26e96b562fd04aad30d5fc95852ad831e8c993d26bbdf48409a2378db4d1b2ad79489efde3e5dd2fd595fec9bc7dd381323370dc9ea2318b82bc430f9ee4ee7d401a1000797f5078e89a528cda6a0c40fb00d6452f8a9563ba70002d6e06ba7713e7e5bc5da023eae91f0601c6d87922d91b2dd46b7058075a3c115044e021f8971a0f8b9a992c48e4cd27bb544348f1bc93830689f3e58ce58796d891928025c4996703b8b0ff4b781fd1bc0da62e529a471e0b65ef6014a0be6c4ac54b584e8b14665a3ddcbc742f3caee1abe0ef50469a9102cdcd2932ab106bd7dc714d58c881b72b99793c89bd653722fa7818c04d18de5843424fcee3f1661dbd1e80080abd0714df0f64309ea774c968e10780597b2ec17490a43deffafa29d72ccdaae56be45c949335716e9eea3553c541c27cd3d58bcc3064387554995f8430eeb8f83ce263ac112b171110609b1e1e5540999ffd86e179dec29e8263588668c8c6221496ed033e3df624d4338a91fb04e2b300452140c43b468bd264543bd4d5bc74b0da98177adf0823a2c58150de89314a0d5f05f246eec7f3fb65a30c535e05c1333e48f0425edc67a5fbe38e7d70011ec7265be12a68009cc8cb1b7629eafebadffa0068200554056f974e1b8f2371aec6f327dfa4917ac0de5db60120df4dc673dfc69ee630e2e1a4f8430f7442c26d6e8f37c5db272df37c072620d76b953517f6a15346defac4a420780015bf602bd175239ce3c2232bd8824f0993f6e058e1637fd660b940e5924abbdf7636b265c9a290f11679e7901c16f58fb9e979eb7bff2622ea0bd181f0b287bcd735c0b2922203f180d1f1fb62f1c75791a7fa2e8acd4a3a43d13e5719738dd945b4b1c78cce566c3ed6463d3bcd4665d502a3766d6bb72c3b45768ba530ec5efc4dd2aafafa6ee4ce6d71644239881e21146ba49aa1111e35fb31b6719d35d3d0edb2305a0ed541979664939cb34948e7c6535b0f71a78ef2c77f537ba62d4ea35412d59d7c5fd6ee36467a9bfa13b153106c726746138e5cf48673342a410c454e70c9329b09cf2959cb46de0e5cec4dbc2fdd02aab300262ee370ded6c5eda07582b435acc5c05e7447c69fbc1a5569499fcba5f00fca839912a719b96b9126c050007abc11d0ade19f73b8164b6af7bbf0fcf1577ee11a7851ce4884e0877d5382b24a56e4af6f87e5c8b663cc90ec4b0b7e47824c6fe09bf9db64f2ad98dfafe289a2137db1850977dcdcd955aa791716125cb1455e67b3fff3db37de9043c48095fcc74c139529751336785887b24681818a8990658e592a5786f025696f3e50977b2a78f1e47b66823cef57b530fcddab3cdd7053cc8016476d704d148a8cb24767b4ae5ce1eafa759f1adf8afb27d284d4185b1c740c1fd55b3ff0b1d4e9a0eb035845bdefa603034eb26d0c16af3ae78f76c26aae6b77821cb8a8f03fc3617642ac055b38882e3aeda065f6d9f5c282c85abcbea9174cac665e79b5cd62608d218d55b3062f00b8bb644a76df017b73a758040aefd3d2ecca53c18356d9d26b92cebc3bcbe270cafff6033f063c559c1e2d6ff22b8db957e23b4cc1965672acd9457bcc0d57c9f4bd369541060e1dacf7a323aa9cd783af02c75498647740e217f464a44eb7d2a098eea75a5a760704fd1b6b6c464e7815ec6477332c8c917e1f9dc0fda484876d44ef505248c28410224ba5f56721b5fc088733d0f551a05b99bf2b23006842e17ac626cacee1ecc0f7f843b05bd1da95595d0cc6bf1fc00e7042038fe938ad732340226ab09ec6c9ccdf3de3047da97facc7f6f193fe70d9c822cd274abfc6cb6579120d265cb0ea44df68a0e0784f2e770813198f1920dfaa18a6890ef7de12bc8e29f929bb64d95ba6dd18e930e651b48e7870e2cff2c219a5d638a74a2e94bd77e9e56dbe01059d7284d97a076f9421f8c80640b5c25453388d43dbb095de6b59541336f324f8c5f47aa6b47cd9496d1ee0efeebde1a851b3af81c4ffe342d06c10a44ce98d32be26cd800b4e79f2ba3123989a12f0fa9e957f9961ca43baa64c12b11619afafb83f704cb809562dac54c0798045017c6cc0f2e42025d657a62bd0e8a2c5956778158723436c2c935c5b1a5423de325846c73f1d46c39cd71401ebcb2fadc0508687b6d49e64a50a8594c508079da1df79722023e73216a83446146e03d520c5514793b84b42302e0ef74044baae220b0c1373641637998b6a1555ada2ba65d3d74758e6b2f45844cd0fc381e4e9b17bfc22a65177af2d2d63674b76a2d52dabf70d381bb355144e89a76a51114cc0a040ff9255119c53e38fd24214d32e6fc347e28e107a4852edd555edef57754049d85d3b2b751a3a98ae7ba755b5cdb4abdb0ac608d0c6db62bb6748502f11827819731f1495ce494327629f18c5ac82da5f225b36002f47af9c56b02b4dbdf259f21404b085d148c77b8568fe767a336367a28863c24286a31174fb23fb16b150c6b8d89aac494db0abe8af5487e4d6bf0d9e81a28a5de8eed92d40b2a20100e7751f59e9731e1c9b6682dc8690996f465aacc4f9b4a07fcf1f986d907d6d11a4a2fa44d50d3376db347c4fc3d5a89ff6ebbdd9ef3bb2367bfd6da52d2a2881f3f1f42999c042e64b96a3627701e5199cf4a48b63e315401864767a4db3e2e1245735f162b8532a74f7e5d4187d01e1e9a22a08c8e7bb6a71970df30fa0cb60fd134e19dd5e6868d8fcf2022d9ba1906600bc242061e6e49210e0869c9a7d10c0ec0e23b7830779efdb3817e7a4e2f480e9a2574f7944fd9fde66028edcfc21d7598296da352afe2cdbc58ef94ca0e9d6da2a53e1c15d6fbfc441ddc4ddffb24671fd74bc94a72a2965be254ebac6025fba6ec59d98d2e25c5c9da9be9112e13cc4ec419e9d97ead61921cc0e24e7e4af2d02d5ac2f6f51549a19b43ecf2b6005f957ca966fd5a436a327e42b5352d45bf76cbe983c3ac18160be26701d7c30d0160484581f9de884c34d5d27d5e16fa12c45dd495e222db7bb3cfc0c13b6b1f75a45c5a5048742d57a191d60fd06afe3a1361a8f2ceb17bafebae67a5efb602f8312affb4cb40aebef3e6a34db5a2f9924086b7cc973e616e9f4bc62234a22e97ce2bd427922283b544e46f9fc6a5b5af4a65d9d280c9063e37d4f8da9e600661302e1b54ed5323e1d086afc796242a30060d4059603cb7a053d61fd8abe32e870c4e252f36f4dc4e49943543d5b9eec239aef5b26d493231b86ffd35289e2953a262b2370b819586426a129145e77ef773c4e3dd3e063b31283d6a838097191aed2bd843f1b8da649a499480771f8c88b65846d3fb8b55469198136344451c00007d51332b0a1fccdcd10d5791b660b4faba9943704123b533ae4d1bac7a2f260b34cc9ac84c8b31ecf35934c7fa38738940b44f5eb0fa12d530033ce33e2efd89da52922bb93e6e76f7a1d0f78549c1c3acbad376c09cb1f1f99032859a1fd676dc8c4aaa85ca5aef8e4907d8b6a36a4a72e820e4f3435967a66c25a06d1e2eea0e1cd786ad4ea1a1754673376aca17ada68ca2f1a78193c01056b70b871e8c63590563b83401ee02361e719ef7a296362cc6788d1267b5e5a1d2a853f30ba8502bb06215506f04e23a063cf8688ebe43906eca116da1dc1ac3973cf65301864399e69c64fb731b35efd0c61943fec830dd3991bfd4b3aae29d261a83141f6a02e7734022a410b2d710e3724ac4f16d004a16b09a66e5b87985d05098f02bbfe1d024aea55cbc0276811109a3fd88cfd32377e362e03f99f9617830bdc20dfa78a8d674c2cf707f1286e0812a3360ea31d57662ac380106816dce99ec4820a3d9130a8315b42a246100252f643a77d921b4cc5da77609c7c3604b64058217bec2aa116a340fd1ad2da925b8f89f73af9f4ba51ccf6b6482f9dfee2702df2f65334f8fea473a894d10975289ae7c747dbdb4bd8f4ca6a1c4a3e4ddae0e94293904653047989744b35d9dee0320e24d339b2a59b398dcf08064ecc0254b9f2b2da73f0dd7219aced9ab6b11e9dec997ec9a6e45588e150a15897e417eee09126285414b8c15189c67fa8dc408bc0105fe5e562bb07335396453ce6e146cc12f162ce08f80b75062366e7e02bcbfc94ab48d11f3e425af3139e14b2a66c55f0f3555ff358e43effb55ab3e020b6179b64a25d7796dead12eb4dde28209a93d2b4b2a6b3e8d59ad981e3332c84b7d7b35beb55d3e856bf2024991217ac4e70912b5246d375b121bb943a483916fcf68f9fe465ee5e475602a8f41e0bfc9d02d019bdc8ea13815142c01fb1d48615664d3ee7133f2dc8203fd65af36ac4e9d9c146f31d3b50a7cb505a9fc377ece631b6aa50494228db876fe380b2aeee9c1a5225965b0e0f9843c7ab9915eaea6712b05d99bcce85b076ef287757cc36c49272c3a70962c6d007a121a23cb4103bb096ba5058c4be409e9754c27d2c96c073d100f84ffd0de3daed64f3d9d4fb275aa5f16cb657834ccf1fcaa272bf476b2f91352607d4765e61aeef286517f2a4e36917c75b047605b1ad6d2b584a111ed4e114e01404ba1f03a1584484a32da6593df0015d736d3141490dc1d31006b81711e75b08410d934c1766304d2c6f7711b7a395351cc756221eda4edec6e482b0210d8e3fca6bc4ca028af348e9e7b795b8da645bb27326f0be5c9d70983f02be4186e0b448d51a564f8c89e3327586f28daa4ee835003fa619b3bd94cc8cdcdcf264b13c49cadf740fba472e99183ce6806dc8187b7308b427f3676cf57461a471ea746a15d459ada3d1a7bb349eb57e19b09f67f488fa2c82db63df69d5142d5171459583ee099325c72eb43afe5f75e9ab2da951a3a4d2f9599c4d6a19105a124da57fef420a7df1ba47bc647b9d47630da041559729e6c52e052e3250997860722414cbf3c2a5c3dcd10e1008c924952f3e86fc01cf1ee9a7d5615a587f41304449860b2818771c0eb0ea1ddc0b530825004894342f0a8be32b8c9599f9faa03fecb26c574bfcfd4984d1c74f9087d619b60b412d8876bf2d8058109ca8d691aa245b4fa5a15f7462c2d4dc9589f8489a3ff7d0abfb37beae97693b11b226427bb265b4efb7617b0df974fb3bcd731e77efabf8e30eb9e8e607118c12740d50a1071fa4df6b3ad1b1e7f8106a7d51ad565f5509ff3478221620914e9ab02520d4b9fe83400af48552c4b958051805049649358688037a2ebfbe6d2528a067f29988a1fd0043a7e9f5bacd4fe0ebb1b8be35d989a88f1fc94e4d720d944d513242b066e2d627f1c1e48c375b9127c1d8e6ee3b16ba926dfe047d4a5580b3d4b10c74775813a94b810bab26329bf31d5712537bf8e35ad35ade0fc27392d4a5311c92f326ceaa71fa83d19b92838fb6322498c2513393737ab58208a5041ed1816a473e145520c1c94fe863a8e240e7038423d0410dbb3f907426e6f505063970eb8e2c8c4b4ddf8a6f3418b1fe4f52f261cfac03340562053da433427cea2ff00358df2c6f82e3b086925f26360b3ef2e884f939b3f42aa209c24ace0eb467915c1ee10852c10464682409f02ec7a0835846a36cae1e6fdabeee4df631e9a5b2a156b344ad0561f098d3be4713f3af988877de3b0a801c88e642821dd15707120a49d1d331bec6a881e8e0d5bc2f693e9414aea0d0c89e2cd66a646ddba083b20ee63c2142a4e95f1d389c916db1c30c01f1024ccf35562943acd2b2b2bd3852c197e8bea301c8431a5815aaf8c14313a246bcef59ce31ef4199665ba740e7c0bb2ca85c164a3cfc7ccb8065af3d2fe103c7a8cbda1178cd06341dc6c99fdc60d961b9f04329bb04609575de89ed4b7380efb8adfb61cdf16b596d773fbde6f235dfd8cd8e397123ebf7b3327dfc60168424b7d84b6def20c11c179fdb2866b03c518356e82c0f755acb4bf2de5601275f8f3ff05f490e0e8bba26bc89b406116a300e1825c144cb7ee293a31d43398018ebe0e72818306e157a8bfb925c92621d26112975ae750726251419034f79e96dd36cc31384b99fb54ffffb032b3612fd95b004e5ddf91daa3d9a7bbdfd9363d4e34b3164a66972bbd759b28ff984a0af63902ceaa89ffafafe65f8fb12a0b41e5cd4643075600d1058f13a1b60b8324c11834a71ccfca6e37bcf29c3582c3cfa59a1111f1650f0f98f555fe8973d1239769fc7f8378cce2bdca2e86ce80a4f309580c0fd094e9c17bbd886c1c07140e0dff336cc308fe51fb194212b7789f02e77c7e0db445ce350d9a65002ec634269bc53fca2ab8436e6a6d07a8c4ccb860d92bb26656657ca8e35df6d569fe0f0e8cf483a4e3beebfd8b2f2882317baf9072a55909691ae66fbd2bfebf9b3f320737caa53fd3353160b3d0f073ef53ec6f6aa819db3204024e4226e88edca1c40badb43bef4596695b4d92ef6e8d59fe2cb1f226efaecbc615b86a94a9066d9e1e40c322df46acbb6aae4010598a7c4837b885e725037dc5ddbc3eb51fcaae0f1fbbc7719dc703dd8c51fc3adf0fd233ca097c4e0c0eaeb6dbbff0c50265435e01071e49d685295034554b409144b6bcbff311177c65c9f2cd359fd5dbd81bd220b843646ad6bea3779f2c9172198ea6a8c95fb5a401174bcca5ed573d193a902d7cfe239c6dac4eff8640346af3364754e1ede6f43b2ed0239889cd4b95886b2b40d17a89a3a1567a602a23ebfeaf17784bdca382fc7517bddee037e90de1ac233535507858d8d472064f19c2acf97b671d1995ce50b2a1733de3e85e51a130292fdfa975cbfb22a7fb7aa2c98ac257a4da362592e7cd4a461a237d3da55c8f0349a786b514870ff8b802fcd403fbeeb712ff48fb3826f4bfbcda6a3720592820f780b3fe823de35d8d667fce752957734efbc715624f8305d407aa4bf7a4fb2905aa57df6ecbc1fc2ad73e269df78d60f8062ab802c2435319de44f21ae4f621cb51b81e8fc5331f57526185628477d3c48f182058afd34ceaee757fb3328701700d04bdc8147b7df87413863553d308f2ffe11b37666a45576c4fbb58039b148a967397fc8c78957e0b24228a0c3777cc563a7c2aa0f73e3af265b560d4c226b17f8804ac7903c082ce419b82d4171a8dca5a185c7c8be843f15020f7575609f7a8dc6901dc145bbe1ea05ead7dfb20a4b7661dcd2da5ad7d9789efebed27192d93b959553c1818ac522beed93609289cbf13fb231519da8bfc4117d3900d2c994a2ae85bcfea4e26bf1c079f7acd0f5a8f006150c5c1f31c5e7220843d5f0e2e56be61d24cec741f7e09d6acb499b7503966b9b2e77113c8f4d54c46d81e0311e15ee5e20ed3b9e8157f44ac9669d695530088a3141473ffdc597b1d02e02d630b0af564405d51e1da3573ccea4937cb1969441fae7d0de92d0cdce9fcd70e59093c8e8405cfa8aa74c037138c670805bfed4342e2580424bb4eaeccf96d0e4e7bd1eb20f6a72b983e076617f80616ac27d07204336f4c8e6dc6f9654708231d648430cfb48fca6ed8f35a705e9819b0aa97cdd71ef6f96a367fead7d4f72af11e97351be71155faf0d2c5a3d34a85aefbca5c92bf242fd0996478afb0e718b8a0b2c08e0aa1c562c23518f1de3cb0ae3853f8f60eb75d9f5c5f499ece711dbeca14874049ebf3112af22724452c4ca74531dca7de7d0e736d19d762d5010fbe3b754171bb72acf6c2142a8a253fd21268bf2aa60acc1d3b1775b1ae934c094ee19bc450943171004a917bcdb66ded00b57e1e3c58e856f6804e5aa7bbf72d70590e36f6fd14b1ba340fb82b6cc541ab55fb78ca501f35c2a7b3d704108a25af3ff86f20f612705ddf982b653f4beb657b237b0230de57fc8deeeeb64f87060f15cfd9138a8bfd9ffb2ee216298cd61b66777a0beba316fc7ddb60d381f41a4b0c27b0c087d65fa8c546d2b0f25d4fae550393a270c0440de2e10b40f5660872cc17e4c1eefcd262471d7ebf992547122e7291ff8bea890c28c191c5258a305e75fc5247653ea99111627202aa465d4ff3710292e3a12ad435bd84e5a4f322629cc534c31c102988fcb5c5c6f8237d2b909034ac24d751c69348f8a04f312c978939342bf1e71dd7e286ee01a670f5df5a540b29a97750d8cedbebb83282121c39706fcc5486816f58876c39133b9500e964d5da48178f03a74ceafcae1c2da7875200cedb5e602bf499f463299e9db359174587b91cf40856091454e4d01de38193563dae24ea6e3633e778004c4d4ffc25ca26d3346b88f40ecd8d4e2c0f218218c1f4ff602355c47ed2055a1a8dcb3b7e7dfc9f4b447c883ca86fb5820cba62dd7ca17a4c9f07675095c9482fded3eebc24566a195a22f9aea7ce1e1b0ff7c932f63e947ad880801cfb73d2fb8c5541fe97124d6bcfa049d964388c3424604373f514e081cc37eaae54ccfd8972899641c3155237a4960ddef345d7e92016dd5ad4101022a6cdbd8a5728844b285219aef9f25f803efc2953b5362dec0102e3e8c7e9b8b9192a395b04a52b7424b81a5d204e46e3afb4cc0e7233414510886fe2b418ba902add2c5fee7732e8eeda405e40125d76fd0c60149fcdefde11817bfa94715704a99bb304c8198ed21519adf74cddb4ef735dc878b61c12bb067e2237da9d3e01aa140aac4783f50323be215517bcce483a8859641a9a2e24029123179741ed19f08d63f3ac2ebab45bd481577239fc178e1a520e7539948f10dfe8c4d5d47a9713e66fce5918346374fc4825a1157fae57d6eecc62a4faec19321b67aac7f6c9f272bb639c8579d4f368540c68e616a1631d366c4862397d11cc7683a2033202827680af4d48278628125230048360a21627c18f09a340932845facab0662af55a50bddc640b6e628ed43c6d4efaa7cd0d0ec62779b28ac100f3e06d8dbd6f71f0f510ea21955675f8b3457cab71119e7e01c92f4a572ecf0a01f118cc6a40dbe88765a0374d51ba18b0b1651f710cfdbd8237191dfa45ee1e59b37d285ad77d41bec8da69a481369cb38b054a14c95084679602f12cc6c80f222ab0d3bdad4967943fbc5a995dd6f32a0e31b09d310dae9f1c21f188681130487664f7547e0c35f69d0456b4946474ac7a1547e26ba69b634e75ce9af1c0291500d58d09e56d0057986394fcde61367f24b832429ca00e6861abf7eb152b23e0251fa593f555509a71b62f1f8fe86b52465f81070f9e829ba86822453ea321b7b2e4c9c4dab6f6402776ecf71aff06b1c91ab79eb70452968908e0e482982cd53c39a2055da678ed03f8be0432d3ceac4dafa9aef43e33b3ad9688cbf13e16ba8017b1ad83c47382d6ddf0ff6b328626b47e8b32b1ec9aa40f95b0c5c0510bf1236d53b41bcec1a503c28b4bd096a4c660ffe0a9bcf31f14fdb4e034ec5920cc23b4479813963034c2cfdc9d14bdaa89e463cf1c4fdaa4df3b07ee3a80810d97a2f3b6b23a065584be3737a1782b149f3459acb615488b7e454d72dc5cb912086c661ab9b97900dfca7c39b7cf6ca3fcb61357a150aca31b8b8019a11ca800000000000000000000000000000000000000000000000000000000000000002068a0cb67561c225dd67a920b37957065bb433275b4ccb90e6a78a633fcf5e4282edfdb66903672ccf13d2bd4e9fc8f784a1f0e64ba352614ba855b9adb326015cf6f27a015cf5be6ad61689a027e8b7526ce8328a7d03b5d791f8fbf0f86c32548e880f4e1837558a8ee54c55d9797ba87c5b97d1d479871686b9859ea08672218e78749cb163e8a91f5518a85f1d96f22dc7b75f19a43763241b593d2da8f1cc53c386c778f3cfa4ad6b68d06887c95602fb982ef2726fb6686198b6ff8fc15483eee57ce406ec079cc88a2dff12ae5cb5eb3cb21abd5a674549ed6c629f508d6da5aa8d32a24f7cae0e1b0e2ba99cafb810eaa9eb0efe93e39295d5035db1db4e274d3b3a0faa915003bc26c949e71b42a0a7fcdacfcae3cae7445216a9f2b3bee929768f7bb096e3f387b4d727bf6d4d1158f259a661584c02b161484e50a300e91856eb5e99daa3ce6fb7d9d2f352fbb65b8a695f1b1e5478afb0fe3e416a4e42c4d207e04949916c2d27ced4b7067e4058a8f1c51b3b72715fea076152cffd14fd9d52652b9b917c901464bcaf2f1c748d54887cdf79789809377320d29dbf1c6696df1d68db6a89f802e721f78574aa9170f8fd65ac8991cf89495c1126dac0f452e570f28aa148660ca02b950946e0b985ec1bf75bd6fb98c15ff0326454bef7ed3fd667c99a074abf1e613a612c4619cc2c97baef5dfaed2718c850a8466f6b02a01bc77ead78d33b7dbdb5a920195a1946f45ef7ecffa48a61b230574194dcd872bbecde78669fe902783bea7d93c458978616eb1ce5e29c7adf00f9b2e7619e3f9aa49d55e5d18cdc2f057fef5ef30b0466ff0a0cee76870f1fc1f3022cc9e50b074e6c6182396609fd2902eaae0b10a3191a6f2d9ef59eb708214dacab0e29635f0935c7b78a6f12f338ab6517357686638ca16dfb21dda12a51feb2547964ebda05e9e22b5e495870d46f03564cf1027bc78042476dfbd9d5103def1f0ca1a812ef15c8d793f74b0e618645fdbb90aa33a325744e28d59db7a0562e1850dd4b84f15e99e98adbe5f55fdb86ce68e7f8b1df8c8229743d4ae88254d2aa45845079d3334d015198fa4e582a848dad6863c0762dd28bbbbab2e57180351ca16fe7941495e0899536099057fe95e0df8223f8f751b24f3c08cb87f2e8ede1c1674423713c0151f002be591ed048e1f0c463eea1caa28c4af7216c42903c8f07b9a3ee0a6a2570a71058df40658a31f71b40c0b48033835f2b47fa40d03e7560fabbffff30ec2d8e2f05aea5f2e1f47b9d421148603eaa46b0bfc622e0217fbd6cd52397db7662d865b6ce29691c34397f82a2b5798c1b4db66c5ea2189a64b0b019ea7613ca143f5e5deb74de3eba86d6779b552e4099cf2645afa1a80358ed480053b2c73b2da781dd4d2092ccade8c2b807dfba6d710550239992ff49973a3856f1d77fffd166c7c0312b4759edd09237fac28b5f66ec5ee6e5307f1ecf344baa95dbb44f6eeb58f47b5fddafdf145ea08b0250e9143697c546c122bc69b0117468ca755b098a9f8de7d0dd8b39dc47c84a9bfc3911888ae815c25f8776f0cea630d883828fb103840fcb129926a9a998068092c4401d96acbf9195be0e567f48e49f53317d51045b5b65c6bb15476b989dc67465e0d3780b7ac020e6a855b0f34662492a2478e3d3891c1965e7252e22f93c44ed52b774c304113f77821a9134186daf42c6f9dd508e707303b3cf816070b35a96c76f274df07254925a2c6eb79a692de7ea02f22145d9598336c5f4de96d72cc387005fc371318b48ac51cf60f81a235a50c6298309497e87ead7a6e6f197fbb36204905ab122ca4ad9f2335a3456b097de0383a13ef5d5b6a18edcd89b84a435110c072a85227e597d947a0068903234a09651a674b625c15b83313abdc71729b8b5368c4de0e124b1210458489e5534fe28323913c9f3774c31e41dd6a6953a028ca8293b007e341f2412aa0bcb8746a667a9b91083540ce999b50fd86fdc6d9ed73eacec6249a69907c960c709d56e36a7b78070981cb66a3dd763fa069190b82e0d999212528f97816cb7405310730fa4948790cf06a1c39692dfdc67d26348f430743ac1b79387712ff2daa58887afdd8c147cf17ed0379fac66cdcf580a5df5f9b2c4c071fa85a9dd4c2eb657084aa80964590de98a0426968f486b81da7ff79f80451133a64f93c52393d92548164635d770a9bdb6a1b727c457514c86259ae0ac1fa2c6d5b283275f0fa3fb6bb304126b602d2b07d33b8ab7482d48cf8ca231fe26f007e045c8f992ca0d1a5091c467aaf1992a880088de713bccf5f0d22f19996111d9f8150babd565be19510ac8fd07373d23276a288c71650f7f46e116f366f9e2ca0f35ebe7c7c6840af7ac4ecca1d711a45a0b6597ec47e6834ccf248bd97be06d20fca1bddc09bf479961cfbc487a5a6ce1548b04817c91e5955644825b15a097dcb7ceebb24a1c71cf97241e010fef306a41203721ddb6b8ad224e0d2704c026dc222b75b227724df1e9a526269f1e3fa256b0930832cfcee03eea89f4ce92b046a6c44df4391ed52b9af3cb861d9de8d0768f337551479a43acc0019728f0ca1f910a43f3e6034edf628c1c3542cf452235903a3a2a4d60204b12be5d14e10bbde5dcfb36876c51c44b485f085c49720006ad8dd6c348c53f01b2eacf7c6148d1e0f426f0cb38d7d2a2d0f7038f6ddfe3bf0eac0e530148b4326595a0bc221f365cab3f612a0406cf6255c739a884bb73aed0bec6deb1bd3c65bdc77dddf2ce8f3e33463d2af466bbcd48284d26463e711ffff27779219bc315ec1f70d3323516d5d5e4348e5377287797903ee2793b335faa1250a6f716f52b3880600431b7d84bf3d60c5bd00910bd7bda5c5c4869abad246070489776e21ee7dd92894045bb73cca8dd7155cd03da0bd0c8336f7a5433e37bf89da7f50a545fd9087ef1795df34bac738c36547302a65282f3eb4c1662c90df96cd698096ddf90431a11338f59b28e8087de1eb30611d5e385e0c04f97c8186ac70ff4741f2f5a759762dd0c410135da76bbfecb2c78a895fea75542b1f3949be83792a5c443d78ebee153a52884d633efb8e468445c9436afe55ca1638837de0f0ab4a333160956067012866ce8645c5c099259e69bd6733eb5cd507d47aab8c0465aaa417d57c4a8814241f1c64ae13da75adc7983b4a96ff0ed9f87e99fc6ac00e5544fb3b8af3f513d269c75e886b77ca22d38102d70d6cc2f8468b9be6a5d865b0fe1ba58448521bffc0fc8099ab03d04bb1f3d788b875cef4959f1e8338df6ffcadca0e27b08913a794f4a00424782e1925639e3200bac02de04a6528b241023c919d095d3d072853620323a594c97eecc083f499dc290acc1593c4f4d28ea3be7e45626f2b5a2e63281482c002e704d8f523e38982d54f218eb5823f91d5443484efe4eb9aad1eb697e46b11a373f939fcd9dee5cda2bc79f6acd3d1134d6be22aba381a27421762c3fefb855af95184ddac731f0f5b7eccc8e9cbc4b0015a4b24e5c00ca88a0420c70ebbf24975b6924b2dbb924de6e168b59a75af060248eb57c555cc077a2749ce68d298918662b23551c277493f05be30a3521799fd5ebd945b0e4b27751a2262e8675899ef6490899374abe0aab9a21f8af09a62d74ebb7233d9783c2823d52814c54588f5f49de4eb249f18dee7720e7fadb807ebaa4143d5c110d2141d1cca9595aca275f5a123f3b1ec26e014bddf00c56fefa223a3fe9a06910cba17f857b0f4cc5535493429f9412a9c32050ea16b08f8b33004d89064ac1ed5f81728c4cdcc85f2c8e85e98c214262ca61bfa97b0fb69357f422bfda6b810118d05f7bd8b431ef0fb104f045f47b5d4547516642367032d5546d96b4c3a04cb70234e4aa90115d8b641cc0c29b500868d7a7a8de824c387d71c30ec5f9e5b2dd70bdadc269baae93d9cfecba60ca4e7d9dbae1d7240130e4abb9fa486a9e7700b0d73df8a358b86977c7166068d7cd49b385e007b3d3a23d421bc61d6925a3e5807560214c0bb25ffe2559c3a2f5a9dade882fb30e87e75a5f346408cfb599d77", + "public_inputs_hex": "0x1aee18e09ee58c039515ee9d127a1a6da642e12f25647495cc22376fac0e553c1e67015d8203bdd68489734560b59936ab952ee7949330f7b7e0276a348676df00000000000000000000000000000000e6cbb0fb234ee2d6c16efa7300877ba50000000000000000000000000000000071443b4c6219d9646c0705dffc34f20e000000000000000000000000000000002edd7e8bdbfce13b889c575d182327b40000000000000000000000000000000000d9c333379312013b1e5f727a8bbe5412fe3cc456b8f0d0d517ea548414b888c6ac4092ad76e33b2d668ca827e33d3e02419efaa270209298711c1cfc30814a5ff279e39592074604181ecadb2f7f7b0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000528d93f3bc2749c2de32c6b7d48ee70d6fb6af2dda4ba0b5d0366c249712b3fb80c519df7d1240f04c29be318da0dee2544931a9f8cee06be97e71a1c99b1a64e105e348119c8567a1d9cc13e0b00a14f537be9506cf6a083cbf12152513598d121d9ef6109c9ecb38140c915ef89767ab4272d2c4967c9761215f47d7a3cd89a269e088ebde5f26c9cd7d8eb564840cf9f3d722d5b19434ce618947673f64f9a0937e2bd02fd06c1f2dfcd1dfeb733a6491c4c2aa01d9a08835f21cc1a3c1049194270376cb5600bcb613b1995ef33dc78955ff83dc2eec29c40c953bae367c324acd210d229e1c3471a876911679d666c7fb725cc96bbe8abf32c5bb88fa275204416916168e13ed3bf53c7185bd13e4499dbda8bdb22adc8b380719cc269781f2f1462e0545189c750b490c7096b9bd187f61b2dd99357902a9dec25eccb6c0000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } } diff --git a/circuits/benchmarks/results_secure_micro/report.md b/circuits/benchmarks/results_secure_micro/report.md index 0e28bbe383..03c2055bb7 100644 --- a/circuits/benchmarks/results_secure_micro/report.md +++ b/circuits/benchmarks/results_secure_micro/report.md @@ -1,9 +1,9 @@ # Interfold ZK Circuit Benchmarks -**Generated:** 2026-06-13 18:13:56 UTC +**Generated:** 2026-08-09 21:00:51 UTC -**Git Branch:** `lock/fhers` -**Git Commit:** `c2dfd37d36d9e85b45b03db03bc7267964a4a6da` +**Git Branch:** `chunk-circuits` +**Git Commit:** `2e370db3935cb9a2f34db7aa52541b69b3615f7c` **Committee Size:** `H=5`, `N=9`, `T=4` @@ -38,8 +38,8 @@ Settings for this benchmark run (integration test + Nargo circuit benches on the | **RAM** | 48.00 GB | | **OS** | Darwin | | **Architecture** | arm64 | -| **Nargo** | nargo version = 1.0.0-beta.16 noirc version = 1.0.0-beta.16+2d46fca7203545cbbfb31a0d0328de6c10a8db95 (git version hash: 2d46fca7203545cbbfb31a0d0328de6c10a8db95, is dirty: false) | -| **Barretenberg** | 3.0.0-nightly.20260102 | +| **Nargo** | nargo version = 1.0.0-beta.26 noirc version = 1.0.0-beta.26+40d6574f851d926f93e0c3a271bac3e6e82ac905 (git version hash: 40d6574f851d926f93e0c3a271bac3e6e82ac905, is dirty: false) | +| **Barretenberg** | 5.1.0 | --- @@ -70,41 +70,41 @@ aggregation always enabled. Compare runs only with the same `benchmark_mode`, co Single-circuit `bb prove` on the benchmark oracle witness (not the integration actor pipeline). -| Circuit | Constraints | Prove (s) | Verify (ms) | Proof (KB) | -| -------------------- | ----------- | --------- | ----------- | ---------- | -| C0 | 287764 | 1.52 | 27.60 | 15.88 | -| C1 | 2223151 | 8.98 | 25.48 | 15.88 | -| C2a | 4283826 | 17.92 | 24.61 | 15.88 | -| C2b | 5726479 | 19.99 | 25.41 | 15.88 | -| C3a | 3475239 | 10.76 | 25.48 | 15.88 | -| C3b | 3475239 | 10.76 | 25.48 | 15.88 | -| C4a | 2418310 | 9.17 | 24.96 | 15.88 | -| C4b | 2418310 | 9.17 | 24.96 | 15.88 | -| C5 | 1426408 | 5.27 | 25.33 | 15.88 | -| user_data_encryption | 1688676 | 5.71 | 24.50 | 15.88 | -| C6 | 2977263 | 10.14 | 25.04 | 15.88 | -| C7 | 191104 | 0.81 | 25.24 | 15.88 | +| Circuit | Constraints | Prove (s) | Verify (ms) | Proof (KiB) | +| -------------------- | ----------- | --------- | ----------- | ----------- | +| C0 | 287727 | 0.98 | 10.43 | 14.31 | +| C1 | 2226972 | 5.99 | 10.85 | 14.31 | +| C2a chunk (SK) | 274757 | 0.91 | 10.63 | 14.31 | +| C2b chunk (ESM) | 364915 | 1.14 | 10.39 | 14.31 | +| C3a | 3478343 | 8.98 | 10.46 | 14.31 | +| C3b | 3478343 | 8.98 | 10.46 | 14.31 | +| C4a | 2447701 | 6.51 | 11.52 | 14.31 | +| C4b | 2447701 | 6.51 | 11.52 | 14.31 | +| C5 | 1426371 | 4.08 | 10.71 | 14.31 | +| user_data_encryption | 1688639 | 4.72 | 10.94 | 14.31 | +| C6 | 2977228 | 7.88 | 10.68 | 14.31 | +| C7 | 191201 | 0.65 | 10.92 | 14.31 | ### Artifacts | Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | | -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 10.69 KB | 0.66 KB | 3136910 | 178440 | 3315350 | -| Π_user | 15.88 KB | 0.12 KB | 2972965 | 193180 | 3166145 | -| Π_dec | 10.69 KB | 3.75 KB | 3658366 | 190656 | 3849022 | +| Π_DKG | 10.44 KiB | 1.22 KiB | 3222303 | 186296 | 3408599 | +| Π_user | 14.31 KiB | 0.12 KiB | 2982226 | 200664 | 3182890 | +| Π_dec | 10.44 KiB | 3.84 KiB | 3761654 | 190424 | 3952078 | ### Role / Phase / Activity -| Role | Phase | Activity | Metric | Duration | Proof size | Bandwidth | -| --------------- | ----- | ----------------------------------------- | -------------- | --------- | ---------- | --------- | -| Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 5251.56 s | 127.00 KB | 130.06 KB | -| Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 406.98 s | 10.69 KB | 11.34 KB | -| User | P3 | per user input | isolated_nargo | 10.96 s | 15.88 KB | 16.00 KB | -| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 10.14 s | 15.88 KB | 16.00 KB | -| Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 334.20 s | 10.69 KB | 14.44 KB | -| Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 103.52 s | 10.69 KB | 14.44 KB | +| Role | Phase | Activity | Metric | Duration | Proof size | Bandwidth | +| --------------- | ----- | ----------------------------------------- | -------------- | --------- | ---------- | ---------- | +| Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 5166.65 s | 114.50 KiB | 117.75 KiB | +| Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 408.35 s | 10.44 KiB | 11.66 KiB | +| User | P3 | per user input | isolated_nargo | 8.73 s | 14.31 KiB | 14.44 KiB | +| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 7.88 s | 14.31 KiB | 14.50 KiB | +| Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 293.92 s | 10.44 KiB | 14.28 KiB | +| Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 86.29 s | 10.44 KiB | 14.28 KiB | -_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **36.62 s** — not +_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **41.27 s** — not comparable to P2 wall_clock row above._ ## Integration test (`test_trbfv_actor`) @@ -114,80 +114,86 @@ comparable to P2 wall_clock row above._ | Phase | Metric | Duration (s) | | ------------------------------------------------------------------ | ------------ | ------------ | | Starting trbfv actor test | `wall_clock` | 0.00 | -| Setup completed | `wall_clock` | 2.39 | -| Committee Setup Completed | `wall_clock` | 16.09 | +| Setup completed | `wall_clock` | 1.98 | +| Committee Setup Completed | `wall_clock` | 16.11 | | Committee Finalization Complete | `wall_clock` | 0.00 | -| Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall) | `wall_clock` | 406.98 | -| ThresholdShares -> PublicKeyAggregated | `wall_clock` | 5251.56 | -| E3Request -> PublicKeyAggregated | `wall_clock` | 5252.07 | +| Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall) | `wall_clock` | 408.35 | +| ThresholdShares -> PublicKeyAggregated | `wall_clock` | 5166.65 | +| E3Request -> PublicKeyAggregated | `wall_clock` | 5167.15 | | Application CT Gen | `wall_clock` | 0.29 | | Running FHE Application | `wall_clock` | 0.00 | -| Aggregator P4: Aggregation pending -> PlaintextAggregated (wall) | `wall_clock` | 103.52 | -| Ciphertext published -> PlaintextAggregated | `wall_clock` | 334.20 | -| Entire Test | `wall_clock` | 5605.04 | +| Aggregator P4: Aggregation pending -> PlaintextAggregated (wall) | `wall_clock` | 86.29 | +| Ciphertext published -> PlaintextAggregated | `wall_clock` | 293.92 | +| Entire Test | `wall_clock` | 5479.46 | ### Multithread job timings (`tracked_job_wall`) | Name | Avg (s) | Runs | Total (s) | | ----------------------------- | ------- | ---- | --------- | | CalculateDecryptionKey | 0.04 | 9 | 0.38 | -| CalculateDecryptionShare | 0.17 | 9 | 1.53 | -| CalculateThresholdDecryption | 0.19 | 1 | 0.19 | -| GenEsiSss | 0.52 | 9 | 4.66 | -| GenPkShareAndSkSss | 0.52 | 9 | 4.68 | -| NodeDkgFold/c2ab_fold | 28.56 | 9 | 257.08 | -| NodeDkgFold/c3a_fold | 665.75 | 9 | 5991.76 | -| NodeDkgFold/c3ab_fold | 13.68 | 9 | 123.10 | -| NodeDkgFold/c3b_fold | 627.04 | 9 | 5643.40 | -| NodeDkgFold/c4ab_fold | 12.85 | 9 | 115.66 | -| NodeDkgFold/node_fold | 29.34 | 9 | 264.02 | -| ZkDecryptedSharesAggregation | 5.93 | 1 | 5.93 | -| ZkDecryptionAggregation | 97.37 | 1 | 97.37 | -| ZkDkgAggregation | 5.38 | 1 | 5.38 | -| ZkDkgShareDecryption | 76.34 | 18 | 1374.12 | -| ZkNodeDkgFold | 904.33 | 9 | 8138.93 | -| ZkNodesFoldStep | 4.39 | 5 | 21.96 | -| ZkPkAggregation | 31.24 | 1 | 31.24 | -| ZkPkBfv | 9.47 | 9 | 85.24 | -| ZkPkGeneration | 87.11 | 9 | 784.00 | -| ZkShareComputation | 94.84 | 18 | 1707.15 | -| ZkShareEncryption | 105.64 | 432 | 45636.03 | -| ZkThresholdShareDecryption | 187.47 | 9 | 1687.22 | -| ZkVerifyShareDecryptionProofs | 0.58 | 9 | 5.25 | -| ZkVerifyShareProofs | 2.36 | 11 | 25.99 | - -Sum of tracked job wall time: **72012.25 s** — **not** end-to-end latency (jobs run in parallel up +| CalculateDecryptionShare | 0.17 | 9 | 1.50 | +| CalculateThresholdDecryption | 0.20 | 1 | 0.20 | +| GenEsiSss | 16.80 | 9 | 151.23 | +| GenPkShareAndSkSss | 0.61 | 9 | 5.52 | +| NodeDkgFold/c2ab_chunk_fold | 25.02 | 9 | 225.15 | +| NodeDkgFold/c3a_fold | 610.82 | 9 | 5497.35 | +| NodeDkgFold/c3ab_fold | 11.11 | 9 | 100.01 | +| NodeDkgFold/c3b_fold | 574.69 | 9 | 5172.17 | +| NodeDkgFold/c4ab_fold | 9.86 | 9 | 88.78 | +| NodeDkgFold/node_fold | 22.55 | 9 | 202.94 | +| ZkDecryptedSharesAggregation | 4.76 | 1 | 4.76 | +| ZkDecryptionAggregation | 81.31 | 1 | 81.31 | +| ZkDkgAggregation | 3.96 | 1 | 3.96 | +| ZkDkgShareDecryption | 56.46 | 18 | 1016.25 | +| ZkNodeDkgFold | 826.47 | 9 | 7438.21 | +| ZkNodesFoldStep | 6.42 | 5 | 32.08 | +| ZkPkAggregation | 37.32 | 1 | 37.32 | +| ZkPkBfv | 6.64 | 9 | 59.77 | +| ZkPkGeneration | 68.69 | 9 | 618.23 | +| ZkShareComputation | 374.56 | 18 | 6742.06 | +| ZkShareComputation/batches | 86.11 | 18 | 1550.07 | +| ZkShareComputation/chunks | 260.81 | 18 | 4694.58 | +| ZkShareComputation/finalize | 26.74 | 18 | 481.23 | +| ZkShareEncryption | 94.55 | 432 | 40843.47 | +| ZkThresholdShareDecryption | 166.01 | 9 | 1494.07 | +| ZkVerifyShareDecryptionProofs | 0.14 | 9 | 1.24 | +| ZkVerifyShareProofs | 0.79 | 11 | 8.67 | + +Sum of tracked job wall time: **76552.50 s** — **not** end-to-end latency (jobs run in parallel up to `BENCHMARK_MULTITHREAD_JOBS`). -### NodeDkgFold sub-steps (`tracked_job_wall`, per fold prove) +### Operation sub-steps (`tracked_job_wall`) -| Step | Avg (s) | Runs | Total (s) | -| --------- | ------- | ---- | --------- | -| c2ab_fold | 28.56 | 9 | 257.08 | -| c3a_fold | 665.75 | 9 | 5991.76 | -| c3ab_fold | 13.68 | 9 | 123.10 | -| c3b_fold | 627.04 | 9 | 5643.40 | -| c4ab_fold | 12.85 | 9 | 115.66 | -| node_fold | 29.34 | 9 | 264.02 | +| Step | Avg (s) | Runs | Total (s) | +| --------------- | ------- | ---- | --------- | +| c2ab_chunk_fold | 25.02 | 9 | 225.15 | +| c3a_fold | 610.82 | 9 | 5497.35 | +| c3ab_fold | 11.11 | 9 | 100.01 | +| c3b_fold | 574.69 | 9 | 5172.17 | +| c4ab_fold | 9.86 | 9 | 88.78 | +| node_fold | 22.55 | 9 | 202.94 | +| batches | 86.11 | 18 | 1550.07 | +| chunks | 260.81 | 18 | 4694.58 | +| finalize | 26.74 | 18 | 481.23 | ### Aggregation jobs (`tracked_job_wall`) | Operation | Avg (s) | Runs | Total (s) | | ---------------------------- | ------- | ---- | --------- | -| ZkDecryptedSharesAggregation | 5.93 | 1 | 5.93 | -| ZkDecryptionAggregation | 97.37 | 1 | 97.37 | -| ZkDkgAggregation | 5.38 | 1 | 5.38 | -| ZkNodeDkgFold | 904.33 | 9 | 8138.93 | -| ZkPkAggregation | 31.24 | 1 | 31.24 | +| ZkDecryptedSharesAggregation | 4.76 | 1 | 4.76 | +| ZkDecryptionAggregation | 81.31 | 1 | 81.31 | +| ZkDkgAggregation | 3.96 | 1 | 3.96 | +| ZkNodeDkgFold | 826.47 | 9 | 7438.21 | +| ZkPkAggregation | 37.32 | 1 | 37.32 | -Sum of aggregation job tracked time: **8278.85 s** (parallel CPU work; not P1/P2 wall clock). +Sum of aggregation job tracked time: **7565.55 s** (parallel CPU work; not P1/P2 wall clock). ### Folded on-chain artifacts (exported for Π_DKG / Π_dec gas) | Artifact | Proof (bytes) | Public inputs (bytes) | | --------------------- | ------------- | --------------------- | -| dkg_aggregator | 10944 | 672 | -| decryption_aggregator | 10944 | 3840 | +| dkg_aggregator | 10688 | 1248 | +| decryption_aggregator | 10688 | 3936 | ## Raw circuit benchmark JSON (Nargo) @@ -198,11 +204,11 @@ re-running the integration test. | File | | ----------------------------------------------------- | | `config_default.json` | -| `dkg_e_sm_share_computation_default.json` | +| `dkg_esm_share_computation_chunk_default.json` | | `dkg_pk_default.json` | | `dkg_share_decryption_default.json` | | `dkg_share_encryption_default.json` | -| `dkg_sk_share_computation_default.json` | +| `dkg_sk_share_computation_chunk_default.json` | | `threshold_decrypted_shares_aggregation_default.json` | | `threshold_pk_aggregation_default.json` | | `threshold_pk_generation_default.json` | diff --git a/circuits/benchmarks/results_secure_minimum/benchmark_run_meta.json b/circuits/benchmarks/results_secure_minimum/benchmark_run_meta.json deleted file mode 100644 index 3f1a0e8e64..0000000000 --- a/circuits/benchmarks/results_secure_minimum/benchmark_run_meta.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "benchmark_mode": "secure", - "bfv_preset_subdir": "secure-8192", - "committee": "minimum", - "proof_aggregation": true, - "multithread_jobs": 13, - "verbose": false, - "nodes_spawned": 20, - "committee_size_n": 3, - "committee_size_h": 2, - "committee_threshold_t": 1, - "network_model": "in_process_bus", - "testmode_harness": true -} diff --git a/circuits/benchmarks/results_secure_minimum/crisp_verify_gas.json b/circuits/benchmarks/results_secure_minimum/crisp_verify_gas.json deleted file mode 100644 index ed734e0a1c..0000000000 --- a/circuits/benchmarks/results_secure_minimum/crisp_verify_gas.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "verify_gas": { - "dkg": 3119554, - "user": 2972808, - "dec": 3640997 - }, - "source": "folded_proof_export_plus_crisp_verify_test", - "artifact_sizes_bytes": { - "dkg": { - "proof": 10944, - "public_inputs": 384 - }, - "dec": { - "proof": 10944, - "public_inputs": 3552 - } - }, - "calldata_gas": { - "dkg": { - "proof": 170088, - "public_inputs": 4992, - "total": 175080 - }, - "dec": { - "proof": 169980, - "public_inputs": 17292, - "total": 187272 - } - }, - "integration_summary": { - "integration_test": "test_trbfv_actor", - "benchmark_config": { - "mode": "secure", - "bfv_preset_subdir": "secure-8192", - "bfv_preset": "SecureThreshold8192", - "lambda": 50, - "proof_aggregation_enabled": true, - "multithread_concurrent_jobs": 13, - "committee_h": 2, - "committee_n": 3, - "committee_t": 1, - "nodes_spawned": 7, - "network_model": "in_process_bus", - "testmode_harness": true - }, - "proof_aggregation_enabled": true, - "dkg_fold_attestation_verifier": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", - "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 13, "cores_available": 14 }, - "operation_timings": [ - { "name": "CalculateDecryptionKey", "avg_seconds": 0.034920472, "runs": 3, "total_seconds": 0.104761417 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 0.163728264, "runs": 3, "total_seconds": 0.491184792 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 0.155451458, "runs": 1, "total_seconds": 0.155451458 }, - { "name": "GenEsiSss", "avg_seconds": 0.086142097, "runs": 3, "total_seconds": 0.258426292 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 0.101574055, "runs": 3, "total_seconds": 0.304722167 }, - { "name": "NodeDkgFold/c2ab_fold", "avg_seconds": 15.959258972, "runs": 3, "total_seconds": 47.877776917 }, - { "name": "NodeDkgFold/c3a_fold", "avg_seconds": 93.866109638, "runs": 3, "total_seconds": 281.598328916 }, - { "name": "NodeDkgFold/c3ab_fold", "avg_seconds": 5.200028763, "runs": 3, "total_seconds": 15.60008629 }, - { "name": "NodeDkgFold/c3b_fold", "avg_seconds": 93.118229222, "runs": 3, "total_seconds": 279.354687667 }, - { "name": "NodeDkgFold/c4ab_fold", "avg_seconds": 5.402757042, "runs": 3, "total_seconds": 16.208271126 }, - { "name": "NodeDkgFold/node_fold", "avg_seconds": 12.258274152, "runs": 3, "total_seconds": 36.774822458 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 2.742254625, "runs": 1, "total_seconds": 2.742254625 }, - { "name": "ZkDecryptionAggregation", "avg_seconds": 46.409371833, "runs": 1, "total_seconds": 46.409371833 }, - { "name": "ZkDkgAggregation", "avg_seconds": 5.355026792, "runs": 1, "total_seconds": 5.355026792 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 26.991641868, "runs": 6, "total_seconds": 161.949851209 }, - { "name": "ZkNodeDkgFold", "avg_seconds": 131.495591819, "runs": 3, "total_seconds": 394.486775458 }, - { "name": "ZkNodesFoldStep", "avg_seconds": 5.025035041, "runs": 2, "total_seconds": 10.050070083 }, - { "name": "ZkPkAggregation", "avg_seconds": 14.269228416, "runs": 1, "total_seconds": 14.269228416 }, - { "name": "ZkPkBfv", "avg_seconds": 3.484502041, "runs": 3, "total_seconds": 10.453506125 }, - { "name": "ZkPkGeneration", "avg_seconds": 111.11701825, "runs": 3, "total_seconds": 333.351054751 }, - { "name": "ZkShareComputation", "avg_seconds": 46.703856173, "runs": 6, "total_seconds": 280.223137041 }, - { "name": "ZkShareEncryption", "avg_seconds": 114.189265579, "runs": 36, "total_seconds": 4110.813560877 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 94.550274083, "runs": 3, "total_seconds": 283.65082225 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.084805514, "runs": 3, "total_seconds": 0.254416542 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.681836558, "runs": 5, "total_seconds": 3.409182791 } - ], - "operation_timings_total_seconds": 6336.146778293, - "operation_timings_metric": "tracked_job_wall", - "phase_timings": [ - { "label": "Starting trbfv actor test", "seconds": 0e-9, "metric": "wall_clock" }, - { "label": "Setup completed", "seconds": 1.010659958, "metric": "wall_clock" }, - { "label": "Committee Setup Completed", "seconds": 7.030232042, "metric": "wall_clock" }, - { "label": "Committee Finalization Complete", "seconds": 0.002351166, "metric": "wall_clock" }, - { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 150.009414, "metric": "wall_clock" }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 591.111398042, "metric": "wall_clock" }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 591.618044375, "metric": "wall_clock" }, - { "label": "Application CT Gen", "seconds": 0.284318666, "metric": "wall_clock" }, - { "label": "Running FHE Application", "seconds": 0.001311708, "metric": "wall_clock" }, - { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 49.252521, "metric": "wall_clock" }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 145.377759459, "metric": "wall_clock" }, - { "label": "Entire Test", "seconds": 745.328859166, "metric": "wall_clock" } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000003364d5f86765e55730000000000000000000000000000000000000000000000013898d5b3217f0139000000000000000000000000000000000000000000000009cba581f8861c2c480000000000000000000000000000000000000000000000000001d14afdc0a921000000000000000000000000000000000000000000000002552a58ae13c3c75200000000000000000000000000000000000000000000000e7e4b93bc2201f7c500000000000000000000000000000000000000000000000877df7ddd752964920000000000000000000000000000000000000000000000000001c9527c8df7a900000000000000000000000000000000000000000000000320dbaa2a0494ba0900000000000000000000000000000000000000000000000acc0a46c1ded6ada7000000000000000000000000000000000000000000000002f7880f0ef167051f000000000000000000000000000000000000000000000000000254e3c3c490a100000000000000000000000000000000000000000000000e0d5dde4b4b253cef00000000000000000000000000000000000000000000000de5dd68040249256e00000000000000000000000000000000000000000000000210c9990fd7aaed99000000000000000000000000000000000000000000000000000122c6e8f8ddbc05637036a9cbc03d47db8a97b8c809100c4b4b596676cba3d015162923686f7d1c9ad10daa62194503463b71646f34355473ba99b7e1153407d04205429b92212bbdc28e4086fda571be87a08652031984d0fb154b6bcc98951723e7413e1df219d31cbb7e938bc530ca471a9862d1724fd4ba4da122090b0f7e30316cef27432fd4a5b925bbfa211bdd9c0c73a088b4c00d7776f80093af20da4da7f40e594a07f5542473fa8234672d8ad016a03543ca60eef8e412ed75255966400e32517b2707f3a24c8e0a04f537b31b5197238e6538af0b1301d16f811c8da1bb9dea4d0c7903aea1115ea068c1e1a6997fdd5ecaf9b2322ac97bf2a008b5879e2f3a791516f064eb657817d1e7ee76334f92f499efef59bd9a601c3e9ffb50608ead9102f5049691cf851cdc6766fda8285028e9db62adb27814487a9429a337e6936a1a0e3f2848cab68d52f8679da46288fc560a7e7b821c3213621fa4ff366a755a1ea795beadc9c33fe40383b14fe8a5859d79b7f3a086e3f4c2be0ab43f234165077dae46dbe0b053fda32855bc8eb56e1b6746644577bd2e275715a300b2969418bf7f536056cd9087b7843f299fe8af3dbd855b47ea8e9c5f6bdcb1ef2b38c21aa412804758ac55e54c0b70d491d71dcfb501cdaf701cb751f71447e862fac80bd95e527c5f42a372bd21e3a81c9a4595f3c13412dcff124da1ab9f44da346504cd5bac58a9b5cbb4b3733ae99956c09b0f85a41b60fb4eac4497d79b6484f404a058d405aab191ade77e47f4cdb2803eb991a8d7ca04d0f59ccc979a79c95d161663b62b58bab1c53cb7a61ace050df500d9e677f2de4c16c3a6ff25e2b419071ef6d964157b3ce9bbf395d6bcefe2484125e8dc164f8aa02f1c0a28f204bd186565adbb463427fffbe71b7787da500ddb9bf4051259bae376bb857e439b3d275d2d2242a2a9020ce2f8815616242aae437ae13d0cf20bbea6e10779a3c1af12f02141e01601d804f257e99b94fd469ea830620de7bee3cef0cfbbba6e88420a2ae0df2aa04ec990e57645976ff7785f3e123f8afa494667517986754b9fa82b3a93c222262ef490921618d2485c9bef7065930aa21d4d94a76880ec7285ff2bb27ca0d5035d42d98655f6764737988c4a67b3a248e11c548fd4e9b14710b42bfacb0d19d5fe4a37542626673dec4d9525d7cad05370cc8eaf9a2f0455568c06d975e4fd71720c84ea07073abaa298f1a8ffd6ed2fd12da28228931b19e6352a746fa580060822888dbcd526feb165d58ba6c99fc68bedeccb5d124ee149731e0bfb35ddeaae20355fea45a27d0f14e6fb0da9407eba7f9c09a7cb935662b02ea5f9ec15fcaff08acf13147948f8551a02eca367216c36e3087a706ee1e06016c47cd145b7336817e7d1e93c80f9db7637cd5abf0058d9c1ce9090d0785a5225da889e8bb42ba213b5bb4c094e99e815abf1c92390b5f4088527b5afbb98b42ee99e1602040c8fb0e5e643cdb7c2c4fd53705cc1e7955964c26d8f98ffb08a1992d81cbcbdc6a27dc75bab392273db8129a6468594e99666e26d271194e25d295496ec3dbdd52b75c4aacd22e122165ed94e35bac87c74824920041cc66b1c165cb012d54f44eaf8466993009b52cdebc6b3412a6ea077f3446f37c02c21e218690942782d0c3ba2f46540f8e1772843f3ba30dd62e61987cf5986631caba81b2db82ce18aaa22b79a69f2c13aafb48fb82b3ec09f11f8eaec68bab6eb08e000a00c177d0ad8a07b873ed0028da01153d0bda3444cbb2894cb11b9fbbebb492703a0158032c6b4175e7879701e3f610b6974c9947c6ad15fffe1b5bda878ed26b6a12eeaaec5c0a13c379bdc9a13e3a126a57a72f20326b4fbafd739822a6f03b1b2d3f783578e2b90aa1ba5602f361187949209582c0060b40bfe5e0ca9da25d94fe2b3e258d69acb28e84f71a1aaace0996114749369f0fe56c1ca59d8320f78bc0c2690bd95df04d8c93979136d516b0bb816dd837a4cfef67f382ad53b1b1be671ea0819547bd142cb60a6b6213a0f7a97ff4be899d7aa4a7a93fb5e0205962e6ac6e7faa7050b432a4ab5c8483ca7f6d26081dbbb47d23fba895df9b30d005331e92edd87ba6af3328bdbe197fca7e75174a4ffc39c1fe3468099f79804ef965b09ddc14b89256a74e4eaf39ed029c9c0c237031325539c9fed74c7db1b8140afed5bd54a5b5a3fab63f6f5de98c35a0a85f5a6293d8df599c82b5d7d1aca8d3f78020899f907e13309876b09cd9356722d5f8413e8ead926b7608bcf01261e10f50190f4047a5b92c010eb6bb89bf609c82e03eb14054393c794729026b527d94188d0cf11411ea77a82030e9db876d798a2effaafd69e9d86d8a9be0ad490ded533b093513a777ed55b2ba7a5638cdd98a70c668a5260541ea17cfc2f5bee858237f65e8cf286bb49a722541341a054da56e94822683305fa063621218319be7e0c0b6ffabbf6d8a9e4b6a85e5639fd6bbe150d36e1f55b7892f4a9095531329a5de69186f0c83b3ddbad58436dcc173fb358e027864c3953f35e96289555cb806e8364a865ecf1aaf46d4787ff9fb38cf89f0ca6738184519432db2d8553fc01edef897fa879013e5a63cf0283bc43e79e30d812589c941757a0a0001ea5e1b04a12a752e546c5771417556c7a363ed5aaa795b61052d65e118a2900191a1705e7b3d201a293d2b3929aa9a9fb6917166d3bd3106f09caa1bc063c233d56c3083eb21929e973bd8addb5a9cc5131b5440ad595b98777eb13ed60b60665d9c3d99c0324bc9df401fd5044c647e299aa2083888fac2e47b343289f482a210e81e343fb483732848311b03f6a746bba4d67833f0b5cdbdd7a428bd8a01f3b4efc08dfc8e3e7d240f83fba5c757c1cce545fd550ebed97a77244936b3322ac455f5ea2115d1ba5ca855394e8d3ed996890efe2656fabfbb1b7625d4b76049a94a53410ee40abc977ecf05997a733ed0342d38dbbea39af4ddfdb89912717a2c247d288a993ef5fa86ac0ed5b1f2a9b4b7a698d42a9d4fca9ed44d54bbc097103b2a071b5a5eca3f4d4902907725af8c199d9eb8223a520c80d295eccc71b16ed986926558c9b653b43182e741e74e15fa725d854381abf5208441a0a660e3b14fd0c2c9ef201e9782684aaea7343ea49447238e7dbaf0690d03b9f275f0cb7c6be8eb2dbb9308cda5dbcf24b6bd226d5690731b79f98f0725517e2a02b255c80f9e7fc4160bbd8a68afa1a800d38d01bb36221ed9f71e19dddc570067127062bb0dd269641f7772639267199c5eabb57d0360301177f4ed053439611e2003ab44cf77b5ce969c060523d35041de3ca1a778a0850c9e5d70553a66e54eb0138c67501ec1c07bbd8cfb8d95e56cc1d475f3f8797cdd23c638b374e219c820d7105995be2799a498b963d92cdc40547c7b58baa9580d3cc7e321c1f14a9e6166f67f602df0a0c9ff858f22675f004f288b3f5cdd1e46aaf7575965c59045f0548c20f8a97ed6c216ff0f74b7727240833a8c598d79c721916adb569cf12d22b525638618576ff06c5fe30e92a3a35dfe43f19fc065517f5a3dc7e7945a87409cc4c99045e308d225f05241b1e19c1361c6d0797cc6201af05e10f4c06249e1673f4e88db2dda4c24afd9126ccf8212a956dd2e5264484dc61a6bd30ab85f82016a89e96b443da09871a57cc5c4bfd3009548a23fd9bcd09a74e16fbe68f9a0976f03c71e32682c539a01a0cceb98985ac04ed3d51520c05151a0fb59a826b24ab0aab396851d321ec410dca8aaef709ab76e3e5d869bfcb59a2adffd5af6a05f09b0c7242f08a0dec3f85536d4c998bc257634b02c95afa77d34b9e5e43950ac6df6dd32308386d9e86ebac07940253592405c7477dc9c97027f6b15e824f013e7b4da0052341810169806ac0666fed6d5fe1e2c07e35acbf763fe6014a272f9b944e5c0347748055cb3acb55366c570fb653bfe3d64b45c8802d11aaef541cb377130fdbcfd6e31cbb8d15d7de4ae452a9f8cb0fdad9c042981d1ef3d2fc1964f2a8640ff3f41be296d43c7185044ffbd642d6e9e0c0c91add01d309a62c260dd0988a99bb688892fade80a992639d7ef52ca0bb1ab9da9507064cd7f6ab1855ece02143612d8324dc37ebbe79f0dfc850338603d5ba0800d73cf931aae323440501ac612a78fb9fba847edff4fce9024bc94da89589e144b6aa26ad7a982621f284d00f1a43f4c44abcd1bb14692afd0ad75cdd7a6535f557bbe18b36dd04a8d776cdc1dccc176fe09b3419bea3b43560155aff182d4dc3910957a439772c1a1a8dbe869d8fd104619d3aae677dd2392608ad2b6c9e8945d05fa50a6ef0072906522aca65115f76ad329b82032e74775df197ab66081eb5afada71ac0022154c39778810d61e95f1e76d97249f06a74c834760e11da15f71ec1ea76c86a0637340304734146813502507bd71137a9efe846c4ecaff6948e0b0bb80fda89304f46ff2721e68dd21e398524160d8ad5789104557734c37f1a1bc03d5b83b72435373f869a50576c81874a5b919c76c242cb6fea01c276af84022a88db859e235814ea7ecdd00cb07f0db60f6aa3fc30dbf73ac145769da4ecb57664fa5adf2832af2f9997d0127de3b0bfb7e39bfb4e5bb5583352d3c9b002b229114ba8f41474b1d84a76175bc3b574b5e538e13207f46401228052bceb67c1c5d519b1c22abbfc7d11b54ddbb8622025063c37091c2420668387d2018d8731ea32dd830c2dfbafa2e31b7094bd322df003ebe9f751fd286b51974b18dc29546bba66f3c50f242bea465e8bfd53ffdc9bf04ccbb247a6450c34787ef47233b44177a5a08c0bbd986de3e99682d3376f585c85d847091d587b7a4830b7028f7aa97aac02512b536e679c183b2831b0e6c40e978a506b9b457c410cfa3fe1288849d7825ce2207396d1b3b2cae8b9f6f20c06914e2edb01654eca0c2620e38c1ae059ec919a030c5f2fbe8c4ace11d53d57a09f17ce34c917662d2d232e0a5e1a3e9cc02e7217b49557cf354b5ebd4fa65c85e80db713ac5b1cae66af95419b5735a81f81a82c671ac96a0cb0b783383db55b6da011641a40ea7a24736dd0c72ff152ed78791f792bed18d00ae1157525d79030644d6301423eaa3a038dce0e49f2a06ffca7234b8a2549d22e679f7f92314371078c40bce6596bec7e3ffead71723b74a34c2f7b62f8b2a35b889deb5677f125d63845c8a40f03cecec94e3cc28e39721f542d0def23c2883cccfcd8298a5023ab43ca7cf2bf76b96f30aad5bd0b7309a2960b4abb445d101191a8dfff6c2a81f4673790d6954c05d39af9d4e0e0361285990b016dbb1ddbd105f56e8202d24c8e0ca73ae73a9d59eef07b6099f5b9175f450d04f47fecb947cbfceef0345276abc482cb90d6d9fc15313407b3b2fa40eee620e0c0978cbd1c856a076a133fb68537977b111559aa7fc756b9e9b081dc2465105b44de68d9569b5d33655dbf08b59c65962e728b5ba280cdcc3854f540a85303cd08f556cbdb4b4121569f6a0a18c5ba999e53fa2a43bde6f259a14e2d9c3c16322bb8971549288b9ff3623085c7722d703f192579e2699503e41e726790fd2a8f51b0db4aad2dc01554cce7e9025c33c8ef23989c91fcee2a28c872cb62ea0e4f30affa601d0817c8647d889656007008f40b9af5d34a30c9c809fddd7b7814b6d9f8e094a84ef4f18f41bf3a9e7b7baf814db20d0ab5bb9b42b37979774528c463891841071583850fdd6af365abfb11c565392c6baa2bcad2c6fadabc32133092b0dbabfbf54586eef0c4b4bf0383f7572049c6f9df4bf2ab9d31ed0c721e5c4c223c2bb707c1830b1b17662861ce1f8941986b90a76b1cf2bc3260cae30d1dd89f9a6c098effb6ee1ca412f714723eb1cf4c473b34a5cb217dea8b74c12c63a31396ad87e3dba94b721fa06a7f47d1eebc27bfaed3348c9c12b782f0ed1d4b145766f696f025b0d88d6aad1d3f74686406240b5fa2d2339da942573aa50557b326ec15502b993dfd5b40a8b7d905f626fcc83332325576e3a2831a1aac146701e9b1e33e3fa56f6882945aef0ccd7bfbdbf2c6eece329676a37d7b289a1029438218425b5720d8ddf4c1d23fc5eae3ccd8fd974ddb90164fcab8f8f91b2b4f1da7c3df06544a2c9f8950a41475f928689bbc122b35212c0a44fe8b856e11336e9872e0b316c6dd24872a23c6ec9e970ea3300892fed0dd8ca27b3a2a5e0f2bccd951065094b96178eeec940ac746696db9f3dd3ff7786328ad3a75b1e0102fd261d3d410f86772eccb3fc7d26631e3c14c69cf07cfa787b6195f04743f1fd6445eb97161e79e12594398e84b995cda9237ab7a6133f697bb10f2695b570e3a3e94d2fddc6cd5780149bf9f7256d8f32830c9c6b40df6534a02c8efe12a0bd30a5930cae51940cbc81dd13b1b9a9f9881baf8c0ea68166733de5fd416580320b7c942e935a457e8fe37e9ed9064055ff294247105a2e77e90f2924edd242ca138bea4bd135795700f1a00bd6906bea58fec8942a16e25a820bac9bac787229730ae4fe107f5734bb7eabf2a7c5d9bc01ebf94d6a126bc3113f664d8db0512fc47b492bd4c5aae7bfd1f952b9ffd51f62aa32e89eb34db3bfc9d235655371482b7131b14896e77f27bd7f4dce4674e00ca166519019cb04e4fee1b61aed80254cc7c579fb5444c73f8116a8532ed99d171095c72f9eec53f1ad40274f0e2271cf4d3a88f46a987dd76485db6980ad155fca47838377811752a4b6b6c23a2228a9c58f4cb708077d04f6716c695fe2fc191019baee88058e521558305d6e112e513afab7e0d8a61b4da20460549c7771f31ecb1cb17a759624fc6d7266ae62e8e56f844e004011611d0df1cad0b5bb16d37d857663e9db3fb0072c0f4af3f10882047424878b57d73d5a23fb78d61ad9d2b64bed929737a901bd2c59197010158a8e5962b62f98f6b7a3ca5404cdd4266e5503378afec3231e67723a025180f3e558b293500ac2941af4f4f878ec45f9608c19fbfb134f0f32e1d3191e51e2a50479651924f39e7046c472b3a8bb9a695fd29febec0af951b39e6cd3856ab0cd1e3a917031ed4af790602e6f5d2f3ea21d4888aa80b6b6296dd0b1a9bad4d26a026bba5f8d95b78f39e0ce1dae773e5799cd03001c6017aac624a8663d3f225c2900b434394a66ef6d9aa682e1a77f7ca6127c1d5bb7e17fa9502f47ac056242b1cfd8d4e0cf452e8079a4ba0fe40f49abf4f0b16bb8de55cc30e15c0f27b17a577988b0ec0bf5be389785775b9448d4e3b2e7218f5583c3eb2f9bc4ccb6726ccb5efbe2bbdabe054059d359e264aab3b09ec0378f867268ccb958a55748a20ff83cea8813e01360dbe25c66848cc6ead1eb8fc3c20ba192b358cbd68070a2c9fd0fccd5dacce5881cb0ef3416f33a04f0dc61e5fa1c15e146937a6bc96be28f20891377d66aab5f049178e2d08ab303da363f9be92a05d26ad5a7212122e22faef662f1a2a51d8d10746473f1a7492c77f4254df2fef01ceefccd943e6182adc780f621902759452bb0996969289b307580215cd8c10d211e2f104ebe6b01971d3078393ac86bc70859704d44dc6620b37aaf7ef8efa6d65818ffdb7a08608d3c702c93eb0d1a30c1c2f4349eddd954c9c98a84d9b2a9f1f4e35b1e00619049ab58954f39329942cf00235914f673389a190287154545d8d815d101772f2157b1a8851c7bd2f00a6524cb42bb03943f8ee017f73f798ecdff703f03de7e1062472fe05fdb8a50573df3a0a36df3894852cd85ad36c52f41f4f9cbc6360800e7fc727230a7b17b23eaed31882b36233e763e6c8b42520acb4260e7a12d51626d3b8f71fc768cfe9c631007b855d613ca296de24f12f56b7b5c0e889496cb60b82805abeda0621176175664792ca8a1c7cae9c9a1e4c082b4ce4e9157aa6980526fb88c2942eb796f0e899583b51bce83f2fba4e5e7cee6817f146a37b850d2237aa2da7f46be0eeac4db2fdf7078093bde6ca19f5d388efabe1166895c5db0c1277ae223bc981a8fcaa3a96cb6f1b75b06420a7929468f3b09d2b95402f7c0359d0c6c90cf4e6953216f6bb3cb134ee16d132c88d0e189c6924a10e9a50c611e732137d9832b05b7641bfec77cbcd5dce54bb8445fc7430829b5be5b947a71c0a977383835d0a5e1b02326ec9de0202b035228e73a562e07d6e1bfa8b02750375a33067790d0df9f629bd9a3089652f9bcde90e48a78123a2042f7733a9d211fa4677f0cefd67c9752452a421ce7410e40b8d8bc83571b77012ab273ddfd210712485ef4bfb9d31194b5c9fae82bff89ff7aa4feec8fb98ee88654d3f4b0d088e671bbbfc49c58b80824f4889e3d8b88a263721027d2038a21791d5a4327b299691b2ca39b3b8d6af41b881e8d62520bd4c5f87c39ccbc9ef996964f99f651b54c52ebb215e8c6b7ffdbadb59405fe665d02e552db2af279f356094421add15ea7f9458298a8bf4f9f13a889567697b8b3998e7c0572d7c97c895a631604c2102f60a1d86647cd85b0d2bbe66951053d77470519a458a5d56e68114f2050a208f92a19b93299270a3e57e1747b6268df01fb686418131c64e50b5c0773c99215f62d793a906aec162412f16514037ce6d0d7041edc67477d17df417db07b0279d799949b09c4e1a066e84497d1503174ea0ad027e00542bae3b99002e5e3b2ad8599ae922629c69729a505ea77f2d939c10ea01f6bba1560a9683ad556f020bc134abeb4bee4213d59ad2abaef4e8b42aac8409dd4f3851a0608068a44ff302207172752cf7a87b1e152c794abcc334ae7737bdc1b885f46b1e62c2a68db2202305f867161dbf9fa9c4526b4450a0e1d67ceef70d3daf00953a4dbcf816a31a54de181ad068de5694b5e4f66e117b739b14fc374d92a65ebdd40d7f613c9a29aac040b1afa38249c41eb33d75a8f94b8dfc99f8b0151fbc955cae401489ad018c9e712e4e2604e1101175f66ad231354cb97de45045818dd0a0fc2e15f1be27e780231cdaa1d3f3a187e8204e9c8a5528f9652f971e1d039aedda2b54e09d067a78504fb71b302b76e1edff7ab78e01ca97af4ff8393970babe8edde24d2e15bc04041cb3b2563295fa52a6104f8eb2900e318cf9a5b8ff1d04433f58b926244721245c8add99d9980044aaa0403d271d513a2f231ae9a0caadd0921c23c02ecd1e43a5821ef9eadd113b7de3a995abdfc86dced8396f229b2d08b08c338405d4df556e1704a43b392da30c1ad5786d811a2030f8cfe61d1a550ce1d5e4b508d6281145edeccde846f716204121d2fec605b8659fe4995f7e53bbec59d1f6049eb314e2b0647f2ab0a4d668eceb480b12cbb3b2812960a5b452313a2a568d1d56ed7eb4fa5c592367a95cb81d819c10743fb7d0cc90941fd0b2241b9f0cfd1db58a7239018e44a123afaee85daef8f85edcb3874d7824cd77acd12959c2e2072013c61ef8f31b66b6d4b11b4cd119047595b0ddcc2d6a89662a7731add70c0df75cf6df5a7f7e8f2a9246f0b382d74fe2dafeba90501d1e3caee4a2f5fc7903b4628d22ad2e32c40a6ac9e99ba6226cd821d95d01ae439c721634454f0100202f16180e2d91c211085bfb37ed4472db32bd8f9ec7fdff9e4dd24594b9972127224d12f654e31ecb85984a5a0b467c028fb37638e67eb434af1f5d7230ec290f7fc290da8eb85daae9d9697fea4610a07373fd2d2764697c2737b3bc93fff624712313de850b3e3f57a5a3fc797c9ce9641efca66d1015154d68931437e4fa20f3244a0003071d1a13e374470ac154a3b413bb2dee2f6d331ca946051524142d969a9c123a712d88ca4c665b3457a84145c27a7c4e2078e21386c0ffdd6dc321c5a45a0c889613e4131409caa9dd89eb3a1eaf86b78ba0aed49a7fbaf74b1d065bac18e74f5a7320f88842db40ca2ffb7d79f024128f61e310da43dc2ab00c2dd7d68884e3a287457d5cc3d8c00da381f993c7c9ae61dc254b298e0d3846d32d0c85c60def081cc7b3d3a81a0decf3aa512294e1da52d83fe6cddd667685ce0c643c9f7c2537e3c6e921dc2e9aeb65bbc42efa14b59611d71cfe317c3f61fd0a449e51a7d5e218012b4e227b6a92165d99cc3bb430af58fd2c2c682052ae7223d501835eac40ff6348208181b98125f48a7780638f143b72ef36d64da20f072a756c581e23987031ec4f4c7a171ca87c000e64749965f53a2dd523e7914fc1075c356b4e54299c777a760ecf0d7fbc7596754013c629acd2549de9158efe75038ee98a28902285d479e57cdabf8642c48d1d2280dbd0e637024a5113c0d90920a507b41fe804ca87e7d0ac6aee703e60fb163fb34bc4986b9e8f8b18a7a6810058ffb28f214447db4de719447bad2de445f56f0cd524130c65e7c642ce55ec0366b8ca1ce5f0358d9897eb56d0d32defa3025a49169248d6a633a6da325c2524343fd09f1b5514879e21e829f286121ecb6663b79a89c63c6f77b9f7ae7cf725e98fbca39650a45c154a4e2e09ab3d8a8a053a8ea9ccf02a3ae8d513aa20c32e18b2f822a389705d508f1bf47e36139625cf0d0021c262ef49f3b72659a8b81513ff0cff62557411eab205e7f2aa722c30a61e314d9e90cafcd4e3fe78f8ff1908bf49948453668fdeb112c04b518074dad6110f04901e056f8c0ee9bc423a27ffd028d2d9674b738140d1d558fad5da13e27e7daa818cd8345100a31279cd13709d6f5958427696d22e29453db4eb0bd708eedb0410e18478cdb6c2f44d8a01d0871af70d6a084e92af84ae29dda995fa46230db588019cbd1a1a5289503d135a99c4d85ed6fcdc15fba51eb3986e4a9df695576671efe798c859dc83a9c3110b048d58682e4de817f7d738369cc90b1032451600ed7733bbe2a27d9968390ac18f3479778588d4bbcd89818546d989f31d113ba16429c5386555ea4d100404dca977bc2186d5d7fe423f31d87616bdcdfa8c3d35e7c5d7543138d7d731d50bc72e4c1187f047279f9c16d300374b2cb74764c174d877ccc8793cbdfb9e85015c49be6190e89ec87934836fe33bf0a624ecf5960f919501fdb37501b4d68d2984855bc233814353d611548486405fb206cddcc4a89693cc31b4a785dd02291b0c790fd693a3ec406762157dbe321d027aed3ef19fe54bf0aa27b1109950f629b61d6733ab4d83843f0e2f1c1adb043c1050ae74f5379697a8e4b2cb43932b17f09a024c3d1e44798c0c48a2f9364c532ed5aef0a30b0cee4bcff1941c331d25c0c7686e17b4c85111b8649b431d68cf569b066bbe8066552f2c7571897ce401c2f237ac1624be353aed25ceb217659d8b96eead1c3043256bbca6c528ac90149f4c9f9da3056c744f08b857984fd7786ba76bedee82e2a2e79bc126cceabe1d7a063991de0ee777cc5f22b835b993d04e93ff34779ffc8485639081659556072231ca8906567ee435543b14ab80a45a87e10ef8bf3d807c7b7944dcfc80382ccb58f4d9c04c0891d20e47e8ae3ebeb84d8c925bdeaad7c913572b78853b91103057d92007d2f4551c7d89ac1e9163cd7576bb9796ee8fabea518189f92c20262f7db81b291d588106a666ac2c66fa5b15c89eb7631d575d9110bc1e05ab0a1f8f18078101c6302a4d736dfa3de33a77bf53acd5dfbe1342b41efddffa52f40597b837719bbd9d5c7055f0a6f47f89817adca48679179da0087b7d8dbecdda1dcc901a2da3ddd777cffde59c1ef312010fa47548b2d863d180310bea3dbaf7002d3f3b527ceb76d8cc513819d9d8618e8592fb490a503e4463c21ae5c111dc089912e97a209255a7f5cfec652f2258961c8b79085541b2678d6da31d7af135292e5291a9501da4aff3bddb2249d4c7700b4880769dc67ef852232029a6162b0c36f30f4747d1f1a6945a0bc3c1a0eb64354a78ce7c883367bea39e26fcffbf0993538d9376eff4754f6ac742f7551d3b7b11388b2c27b210d0d808bff4f9b92211c2a13304f5e6919f2eb5ba8f2b497fccad8b911758e1c2507ff3e7f0024509a55eefcbb0a560ec117006bb9ffa4ed67293494c798a4aceb53ec6ca69d3f11e9afa8c87318d0c0696639a977e1f0778c7900c413b8b67208b44d0ecbf8d2115cd52c52aa7d7fada2227369576832a8e4aa8f850543b331f395d52463f16fa0d50c07ae6999000930660fb3db9d13af1f860096e12ef4582b5f8d961e89ced02d909280c211d4870257c11299f498a6d34d24219e780c09115368b8a96cdb62b57899e7262ae7a69b5b99b7acc03e8521bcef49747a44f49cf70622288f210295c2d27e2a19354515f218641cb79d78d7d52713000eac7d6091a74ef6f80570186d3bdda8b409488f8f31cadee2fb225615d455a53c67ca1dbe607178c50fa23b6ac0e16c1bbe14d28a39dfca53ffcd79f2ee386e2b5a8ee84b0186bbedad6274a9cfd101121b4086547a9c8e3e97d0381d2d5d2353153a92e7bacf28c266f2594ef6c2611e13c3100389e1df66ede344fff47ad74c7cab91a562e52a2a1de2d7bc75ca8202b28376273244ffaf231fb9e16da51675e4a4798e77bf7a74cce16cd50ee9f0e54a127d2d417b63e10ac4b7c6b63f3f9d129038b474f3bbdb823087ddc2f0d6b3adf457851131f2007e1cc5865187224f1a1fb5a74523c59d55e1abf6b90690d7974f4626e325c4f701af20c894c8d1876f9d206f10250b436952440a84e54ee45c609f94f007b9e2ea5a806aaa2969262efb94e67cc246d4436097f262044019e0e4b3c0eb96b3372ed34b48c104c47d2d3623305d5fab2a50225017d2959ff9694c7a5a542a19f583d0ed7d6f2ac446ad30416c23c8a28e3bb2d94fc4da5c42b66e4e85071063d094baa942d05bb1ae0c37629bc6b9b20855903a6b73afc31be8998c9769e7cfc88a09fe45aab322172914e16071245f141ac1ba6466fc1bc888b760b05da93fa334cfd7442dd27cd30295550df94e73e2c8708f19d851af9945dc45e42f4543f9cac4ca9e80a2a3c51f41a0bf55d5d03781b0f2361092221c7f7982c4ce306e5d71c1a6ffed900ed0f31f599990c9aa6522e128359d13e1b4abc444cf3ea3ea9a6f2d9a4f4ff51c4a8769da4e0625ec014310d6a71022ef9b71ec64b8e3f7ae91ce90acedbc587d3c5b98d415d2a9938fd99046ea6b7a086b138d79aeb97ba91a2053048bb1861ff5b89ab91378bf18c6a031a58c62105b965ea464e4eb056ad2f671690462e0714e99d561dad85c1ef0c69260993df4b1260b7ee1f11cede551038cc716259ac737a9aaa843b86811bbe4004cbffa38b937132bce1ec02ab973aa718929f3d0e5bb67ddcf0ce6d1930a43e1a3e79fbfb181dc2963f0163f88292ae56570b423ea959902127207052eba80f2fa85d81487949f9d7d684c809d452708a2444391542d154af2b1b380a79374504e6b1b526c3050d7074ccac463d60e8a015393d0af29ea8a6dc854f874ccebd1a3ec86d5ca906e455245651ecdd09a22b1ac302e70ae5fc87349266dde22a531a53ca21085e4911c3b748197bf9a0c1807a015bc3d394f5de94d090b06cf08b1793a89662ba0d4835add6e466c56c735e9139ac00693624f3dbe4ec1fc3bd3e19682f5d75564154e55dddbe4a06ee65abab6f319dea27e7a1b0bf679b367e2a29014182e251682d9617f9cfa463a0a0c0f653b59b6525f99c11da07a3230d492f291e2457c5db90c05c0b7f69d9e05da18d3d48441933ce56f3a136a2fe21b72b50208c3b2dba63124c200a0cbe65c880505577dcfcf67f76466f8378f873e80fc3404c2f513f182d81e94c86428b0f33b4a45f2916ce6a2998a10cbc3145682d87426141fb4ab6f7f17bba159fdfb135acfd30d3218a5fd639cbc31c61e16f07839c78d315adb71832869b7a2ad67fff394fb2f33701f5d3f5f716be54ce5c2ac388d68447fb2f96478345724d446e0d4a97d9ba3a5624eb0f638d2a627f23106ab6518cba364e89bdd5a08c1ebea792617985ea3d481665503ee697fb8b40295d3751a6a15e710193b9cf1dd32230170a360b0e22f292b876606113d449fe180b2ead206cccf57e60354cc7d7376f27b39ce21324ef16bc09843f00b516382200567f0be95b34517262d4cd4e89c364e1f6c1917ccf31bf404672711638cd17a97f0f517714d4feeb91d2f8c709b4c4cf3d8c760355700e6125959a1c8d171e3a86c2cb5798f08fba5968bad233aafac364ebb2d6c97f6933e25eb623ae5c0def1ef42877f541ae8cf843d6c9ab630e0564a6670b68fcd21b3970851c33a80e0db49d208a50919d20ac5bd18b69006108c3dc3d8b2bddb35ed4727ba260f0115b131d90fa93495810a8ef4511947bc83a60dd86a6f9af45c2cc8ba526963424ec2985479a41f00ce56e92f611c701343bdc509d77610be6ea114a71b1b52e27c2fd2b5e6d988c4d4fda59a63a56066e3058f0b8fc87cc4eba06121588d69b190ed8e124521ecfad13f8469b60820e2e4ccf3187c27d58e613d3fbb9b7b3230024793f04722fced6592f899ba04c0410fe1fea1b98ce1999b346202d215a80101790539dd95717219cd8da7ebb78af873b38103559a71a77d218b3aab3f73b1b2381dc08353d67fefe08ade38da6e1c28d14fad8ce792008aa06f4e4c7535e", - "public_inputs_hex": "0x2ce3aa0626c5cf6359c69f607b5ca3149dc73e9b022e333ccaf22730a09d3fd702d799cf98e79aff23abefe11513495038ec8953dfcf4548422ce68dff572aaf0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000f664ef83cbc214b6485c19a70e17f8df000000000000000000000000000000005d9b229a79d0f717361c6cd1574153e60c9c41697310eb57eee62b606a492a01cfe53ae9bd5f2ede0616dd25ec4bf1082bd2a61584d2ae0287a98d2b3e3f3cdeb3b3582244367a14fe46c733423f21792e6d2b6cdcf0542f58fc2cbf0103f2c332c99654fa3151cc1cf346bbb0ce499d1f7cce37859a677eeb7d36d8fe2112e0b1f445605ee4bd6626931d8993fa30dd22cfc2030bdfe1d0c50b8fd63d2fb2262054debb3b672771fe31a0e8c6d92d442b34773371ad4adf21175fc1626e47693709df3d9dbc25a452e28c000523b61a" - }, - "decryption_aggregator": { - "proof_hex": "0x0000000000000000000000000000000000000000000000060fbf86699f40ed8800000000000000000000000000000000000000000000000a30f4d2fdb3067af3000000000000000000000000000000000000000000000009937586deb824df120000000000000000000000000000000000000000000000000001403796c2b817000000000000000000000000000000000000000000000000e44ee329911eea6a00000000000000000000000000000000000000000000000af565dc090918333e000000000000000000000000000000000000000000000005ad30cd59e713023800000000000000000000000000000000000000000000000000010922f18bf64a000000000000000000000000000000000000000000000000db033559f53a86e3000000000000000000000000000000000000000000000001b3d9d630fb02270b000000000000000000000000000000000000000000000005740d616fa3ed2b2600000000000000000000000000000000000000000000000000006485c9fc331100000000000000000000000000000000000000000000000c47dd961b922b653100000000000000000000000000000000000000000000000a2d66858576412dc500000000000000000000000000000000000000000000000d4ab986bdea2183e90000000000000000000000000000000000000000000000000002896050f026752e685e4d103a3f1f6cfbb11ce43377147ec6ecdbe1bb1aa2422fe3f626ed5d461a28dcdda31ad9f5490bcd5a29fd2a1c6d29f706c6123a1fb2021178b97027d71eea4ba7b21b1b1eaf72da90fe2cfa805f410cf0f49c4540a3629e3d0032aab80433f0af5f08931d494aa5b55f37647e91b81b1a4716fbe0b3fed37fffea44b701ec5018f873be24317d4a0e306f8e49807432a97aa2b1be47086597de726b031d850133136bfc9ed32d72c2faa8b2b007f417cafc1a081a8a98e5d3c29179ad20028693e01d353fe5b9a910a388281252b72d22cc39191a315865725df9c3921edecb02bcfee7d961eab7498e59594516b590313991c89b614a9721d5aed33a040605a1af9ef8edcb1be3a75f12dcc959cf6b034c9fd0a72f62a95843b046c621c034706b2a32dff39c582eb30b968f8d6686cd79bf262a0bd6b910bfa5a07b055c9cf093c46ba97a7ee7c68f7f6ae401294aba6178379d41363386b7ba77e6265de321c6de817d02beb9ec24b259c00e4d69685dbd855ad0b0929a6a6abc9f0419d3cbb169495dbbbd4571285a3134dd3e8eed44f4ae86915e1e7751b2875f07f00183f0c70691a5f945590ba496633096baa75aa72acffc960b7e0f994ff62125cafce256f4c61763371f56f58eaedbfdbc2b6308dda23dd4067b456e518329e542d89c3f3c1faf03aa9681a0aad9d82005e53347cd8671688af297f706771daef1f6a6b98cb4444dbf449237b2d88626232765f2d471e84bf6ee3b08aae81c7cb06d2272db499f616f0ce3fa5a1b3af0272508e3e6fad3ba82b5bb86d4b3132b3b5320eb22f23a79fcea83d5f7f41835e02576c683a7781c1fa8136b40e82d8454e1bd6a7d1f51a780bf35eace93d0224fee4f844ab53ce93e8ca78516160f6ece6d94996061c2e982a24240d7ec5aefa15a8e54bce54102a2fbac18acfd295862e4590603cac9b242f4c858f91413125bb3ee00f1dd3ec2668260f006ae1c2fbde69ccfd76f7ecc1b26ca741f7a182e7c53547adac1251b7a7892c6dc872ae895bddd09323fc6e46400cdea469e733c0bb4068d36274fe42f460a06b15c2fbe488ab0f181e73966f18c7653d337f244f5dc873cc2065040c92324883054198306681fb918fce8641f289ba3052ea3aa1ebaf5c8e0d4ba4a06857b23abd1136e8d798c2d0d37a3a1397deb21f26182b95e39f9eb5f395e52ed85ca4eeaf527e96d627793123d61fd126869c5a15ffd578f75d1512e2d78a6724e0f88dcbf13530fe9bce872d44d9b8626c59cfd4b84401cbc070757ea3a123a199545c4b40c5497f42c35c932a19d95c89c79fa45d669c64537b159ed971cc37886b87aab032cb5db7543ed932b56ebe0303b1ce39ba93cd2253f0dc15730bb89350c7e180c9d3397905b2c8385e0d56ea58662d04e8b6b60430c3dfc231cbff85ea2442a0eb846070cbf9b6a67513d13da5fb603bb94b5fee7d5ef993349afc6ba99a3cd10149b5a20de41909768298b601e6690d796eebf1dbe1e333cdebe53116a76932f8fc6e7cf56278a464df7a01ea51d18f374cd93a0928376ff9bfe60f8c200851578b6f4c8096902d8bfc98266fe142a2ee3430584f970fed0f9bf4f2642cab51d78bf7ef9e63bbae8e9a8db9e8d1c55a04704d5ae31661ea09801287eddbc400c42f8d7aef288d09c121054a7db59ea8cfa5c97315afbf8fb7eb5a6f2f380681e0dbf4d620663e3641739153c791a24467b77117724ba24825fcdd76c8f769617ffbef48186983d2561e883fb494a367f4228237499b82d1dfb0680d74867a80f67f5a2997b66a3913252062b81c4adda4e180bf81e1fede0676e0a2ed2ed8d08365fd51552668232a64e43d63efcd918ac3cc26c0c64831759b26ea5fa71ba0a9c43b97866fbea5ee2e04cd9bcdec085942e2528092dfb734d543a6c8d7a3528fa85ce8cbf3ccaee13f349afd9992464f7e62387f4aa94cab21dbe35f35eb81ef5ddf272f467ee55b2a8e3c8c4f51f98564cf678e55bb5482eb727d41008fc27cd624a38b439cf3320767b9f81b93b02290b68025ffe7aa732cb89651d1c1517a6c17c6c56c3cf3b6b68816c8bab791e485295c6c420132d5d76064ea1dd511af8af841ae645b528177897379879bf3826aaf3fc82f6f786d33fc1440839260d952d944398d33929476d4c9c3bc05289570fecef98a6be31049e491f1281fa0738a3d6be18668ea452fdb936536029cf6aca24c869f4478bc130f7dfee5cec0fa41c1927e03ffda49e4a5f75cf58455d74bf7143173dd9988663b59b7156d5127b01e954f45ec35cf71fa511cb4598852f07f2f6394d5347d267ee2edc7e8a119753001fb573f3be4ee8e8f959292c5fda1226a2d1da77d201ca3cacfda30a03efafd2c6b90d82ce3c49e01da5fae881a25a18826d8f3b814c3a116a5fe14d019cccc623ce8a11399e476a37915f9f2f72fc20858f59656e4d46f8225cf4c22e89958655a4e5c8c8c25b5ff724540ef22ec566efeacd780ae36e79ae0ff36502fc05e2e5fa559b00d83957d720eca3e857585d11fa6659ff0cfb9127ede629173ba3df37fa2844e086c9f48873f9aaad2ff088ed575b75933dbd633fd6b21a23f404775a4c0b8d130655158b2cad89340586771ab167f55c66d93574e7a8252375ec1fe293c7999dc55343a601ba45e4d81033076ea2abed2bee99564ec20d0839129e222fc8059809c1498e1f004dd9af09eb9d7f63df8370570e99b50e0d2860b6d584172e13fe8823432411e0b4fc2016f38bc2cbcdfac61aaed4af70ff290a594474af723b14fb50aa411232c388d1127f925df337dd94686a5be48cd32627d5093fe2c9d1281ba18209c95a69e3cddb51935adbe961274f7c27bd4a6b0abf90c213e7483f1f959c3ce0766ee84b96f1eb1401cbac8f42ab71ff2c8c6d0d14dbd3abe19e6bed45067e94630cef2cfe454b65dd3619c0116984536c563e10e9756fe592dd8fd645aa6d2b93ebb06cf10de48ac272a215303eac8792d7da1956c70500663f705e4b5d2a5e6f3664f881504dced400c842dd901cd182e5131d3be695c2cdee2176672596c83a8b1a4bcf11d5ccc8c579cff5ca482b8bd4e31095d0d8f07df0e65dfff78692fc9802b68ffa0c7940747e43a8129c2cafa72f19cd83a7f4bdd7de4541550d075a62469d6c13fe151adb12eaeb4aa0f2429b1a305fe3658aee5a538ddab4a651979d31725c380bec91db7c768192033325958818a0f7a4cd4833d3b72179510b735e197d49a71b00efaf1f297ae3df08933f0128efda5b94cc635dbf32d88143194bb630fa61b67b3d52f7abfe4fb43d17b20c03590d5e7b6840f32e9130e461d2265396dffb15225b43de936fd469e419c4a012016ac388364b45731b97050013129ccf7ccd1506bd5397c622ba31ef26bb3f1e2e62ad96e6d542e6b548968118fb672bcc39ceb6c9a30e27df82878ff2e50e183f9eff1d2589ce9cf40d3eced8daf69d989af7bb42e2eccd66dfd3f091f0db20e51f5d6e56bd06a8323fd2f926ce215655e79d044cf96fd4c7dad4d3294e51193af3f58677014368cbee87398d59a04249f217b10a264927fde95269a21bd9254b953c957453d68593e396ce2e93f6e51a128da4729cf39ba5d377306f20ef124e77be5e642a5592b1bbc13cd793bb5a9d97ad076dc900b6eec3110555e07e1c7ce43e07ef52123bc96bb98e55f9cda1c10f4fe1716a59ebb23441fce942f02559dbad8b5cd566b55e55778e52dcffcbc7e5e3f2977ca5a37fbb561e5769e30564f17420b147d4551a70853129294fcaf62b5fccc87cd681d8f2402c8128712c9bc29a6ed8f61b5437189e9de21d171508769f58fd65c4084574f46509894329e392d2743104ca97ab9ff01b553046f6840592aadbb76cfac70078654884741fd8400b8c1de1e56fc630708ceafee772e8b9cf3ca3566644c0dc932157871824e06c055e14e3c6decb9741c9c59e8b0bcea4321575ec9d35c89b1f91cf26690384ea0f51a2623f4447d7bf2ec728d1d53b9c046f1901295c73dd490e31abf429d572d80082503137d75bb420bcbf0cc62de78037998ad9ebb5ef006ee6e5be1fe507b7cc26c2864c45e37e64cb709c3485d9c930c524990d8339ae302d6dd50dea2cea9d4b26741eed15e89d7ba41387104b61ee3d068f3e2a2d4743a48ab62b9fd12592037bdda46e3fb3744dea8326f335ee66f7f3d82b339514b7e85ede1bf6f0f11a740f53535add715596a3d7f188ae24c738788f06e57ed631233cbe2ad85dd44fcd6556487f38a03c6f44f6e803afb13260ecdfe64688adfbfc42e50dbccd2b69b12fe4ca91cbec2ab388c40e21ddf1b4bb1c191509de9c9306645d1e4080e643a64722772be374c6ac1527ffb147d49fc5acdbe1257dc83ab9b52c0e64973fc0abcb722f8c1d1006f4ec8de3bed6e03ee035fc7757537918e4296504446b42f1c334a580f2347efb6752c4b667fb3a86714c991b312cd843a4fed019d0a3ff44a20bcd721de96add28f01e25e1dfcd9d9b1fe315c37c02cfc073170ccf049477a4db0a888960220bc083d818ff21db35b1221d2eeb638165c7ac3418ab18e57698a1d965849e6d523920f15e0cfb8eb36a46565e13f9e05526f13e01dd7d38507837dbbee7ffcc3f3df55f0f18848f95b4fcfd9ea9ceff5ba94966247e68478bc06c14a0bc0157fdfb444aa9ddf93528b57c157d2e5f54e7d6b1b0300ab24a0330de049aa4f313fe5845345aa9aa7b098bf77ba28086497721a5341aaec8d792edf5bba881f3afcf21aeac0792c38e28520ded17ba1581db9312cb12acd65dad53443712e75326b20a980a099ff300b0b54b74a4bd8e2736ad7bae1de0f3adc92c0304f5bd018fc1509deca5507bf24f4133445f46712ec3f582f30868d0cbc35162734e9ff01615a8f313c131114be4bc3fa46f006a6a7c23587e23649c562487742c50ebe4761d05d5c2a5e70111032440a5e9ad32c7d2eb60760e2d73a9b278ced5cd1a4200bfd5b97291cdf29665ea9dba5e0bda1d6fcbe8a81547ddd5ed53966ca509c934bec3b79ccf1091c0b217e4d74598da2e6d8fcaa21376cf7443fc8dbb9ed63c28933431430c48cda256d9f56c238839b6538709ea0480035e7e37b57c625bf18db4b34117f55272be50e4dae872723cc3a320fb7a04fdfd10c756395f6cd9db95a6a35ad68bfbc5807a3e0f8c697485a2c730940e0031b19417e64a17915204e654c12d7044124536249c46839598e6320a4bbf0d29aa05759ea321b9fcfab0811363654bd142bf48888a30612c00dbb3092598142a1c5316924bd451d3ec09e58eda50ed5f2a16bfa7e550a5d77e38841afff3aa0efba292747920208d097ca4976b8fcc766ca037bd37cdd81957c2017755ad350f1ca3c6ee7dbbfcc37a6993e092ec54023b47bdff503202cfa2143c783048400317ed6dca8fdf6aa9f10c7a238ba0bbe40af0f71f9fe82debf0017d998653ea1aeebb3605c4874bb12b95cf591875e36667e920b548e8b2ceb98a7414c3a09203b971991a24e7d616cca52f25edae46dfa875ac167603b1879bd9361884e71d089e3b3e3253618e872c23f9366bed3dfbc00c03fc3532b0cf848a75059eec5308dd51b2ce882a26a6a94ca2640ae57f589b8d80e4aa4045b38d13552d2ee8da1b430a995a0273ef88d6fbd68bb447669bdf2ebb2bbc8a5da33b5502ba9f0ace0d9ff925eacb7003bc0c0e9b50574c58967a733c35168110ece84411651d38e31934fdd99db98fba9e78895783c824a2c2afa4e639da8d63269a9981a2bde5991894826dc91b5ffaf01e982f2015020a1ebcb4c935cfa70bec96e7dc1033f7380402421ee95ac2e82b414cd93052d129418baa9355bd88b6a6f1be20f705cbc7184c918b707c21f95e02b18e771e0019584c16f99ade4b2e942c89c6968cd8ec0d457583546f61d31cc69362036b64ff58e51a4503f45526400bd78933612ade176ff9a731e39bd6c06fcae8fea626b084c8448ff5f4907af0ff855c7949791d1ea1185322653f643318b95023475cff51fa3e891aa1217f25b0c49e13fb35c10fc3102f1d0fabc12880f866b08e9f0f52c1d566313d0a3f9afdcead76ed7474116c404db3062474126f4ee23bb77511149f59a30fad9e64b0bd1302366abafb2aef121fb9973a055a08ec3a05f508a061e7b48c0f3f0cdc83e6dfa22cc869891809b156a1855da5f9e266476f629cea525cd567920e3d7e7d8ebe1d6e6fee550807f99e0b9067a68d892a0def92e9c036f307938741e49f7c82a535b48abc9c13e647edebd5ebc64b7521cf707b7027ff94a8ccb12b5fcf8ae286e0b11034e42f67a06cdd4cf532950ee7d07b039e79b719e4eafa3af83f8fdbd4fa1758efba02cc4641ee515aa588281c060f45928bdc4fd77f18a5eea79819c15037a0fdba2549f033866efd35100b02c492b162613772b7dc06a0c0280ddbddf1c07d507c2723746a81ad2130e3b55869272def6fdb8065b7f676f83a90e7ec13fa6212102e71e16c286c708139cbdbf9baca366e02a7f8158d91976ff84686dbb0cb015f1d3f1c99d0d54180c1917a4092fa220ac7b8b66ab2be93b36df92503bad0c042165a5f327380740848ff65b1937355bd0e70baa0c409aca29b4b2769c66590d1232836f7e81a33682b487e4355ef74fc5cb7126ecf82adc070c418ef8cd07cef0fbd50ef4024a91d3055d7e10ab5722a919296734771807383ea004af034243028204681a24225a9e5d3c0ec6c744c0ee1e50b386af05ac03faa0498fe1ba667168c072ca79ba12401e29f00ab35edd9bebe08ed68f05fd7d798e101b6f99427268e29f66fbb4c24abd9205b54744084a0f53835c9d25fab4dc586f76a32ea52261b8e385d5fad02bdbb6037867a8dec048286cac202083620c8a80dabecb7691d013bf25d369e7252df82e8f620e2f991e9c950057b05078338b75ff4b57e2f069466cbbf0886716494576deb9b778a20d4b3c18c0a5ac5c6dc08ba6c027f46118a63c455b09bd85ff53ce9dd20fa4b603abaa97e3e32a419ea7ba7d66758731902dd76c53e234fa38cf4b3679513481fe26aeb21b6a61db724b7d309c286f50bbf0f431b02827f3811395c1eb997f3805969a29da7f49b34f56f8719e719a823f91a82b30d410e9cbb1be9fca07e63cd913330244816493c4c6261dc0a1d57016cbf646a96d06c022a3fab1a7ab77c8cb90ce232a81aaaaacbb05cd9ffe9721a8eb146ea2a76275bd94d95542b8f38b5609e15bdc55182cc90ca86d52c22cd0dd1408d8fd3721191436f54b8d1fbf6f14ed3919db10f9bf7a7e5b18a8dcfcc0e97c01d8ed6a8f502fd0873f8a97977950338cca761b115db4b35621cad5a520b17c7ffc4d378aaf2f01020aa2e31182f097c1004284fb4c0a6ebc43108f3011006c345bcc505bcb5c5fcf21f584733fae1ca115939d29076b51c0f27a217390c54a335e6ad690c23e06f0a454f30d49b591a0ad044f5e26ae87b36420f8e402dba6f63f9674bc81692152be81c18acbf3e9ef95ac616448a91a60eaf30a49b0c68a47dfcb199de36f0ab560d4f8a289dc9e89504f1923ee7b54977eee68f81148faf462dfc2b954273690025a301cb5e5b5d4d1671caf22b96bb21ea117edf1079f0cde55a935f5affb721a8a576bd23dcb8beb838f96d7608e9205f565cd902559b893204749748edef9d257821a92f959d4a0034ae8a297360f1ceac976723f34f4a4aff6e476ebc7b4862f9fe83c9cd766890bd99704e09fde2858914f30db5f7db7c8cc0627a0d2f61401ef4ead2aeacf2381622ef53fa0aadb67519772f196ea3ad53c32aa5bd848f9b08d77006c54e44f54e032b92206dde013abd1524c8aec63a89a4f69384e91495adee716a9ad3c7c0bc41ba7cf770736dc558051d9bad33e9805c9bc031cb9f8b5b6286c37501a5c1a78539f02c784536145a0b145d5a893d5437043cd005c404c7b9a4f3f775c2850829bcbb7f0a0bccdbebca2d1e423f00e747e3f59a76bf7dff1283eac06cb95239650b0e53d985f23e1d841c058339b1c731a507224916e3f4770a8d8fc28c62c5670331bee8e27e390fd519000715fbaa39f2e41967ea58039b6fd53b21205bcb8f4e2b278bab4df75755204eee084e98d16a33b175369f71a51733e625c0f9204cbe3191ab007a8ace7a2f814f68e71377a83dd0547d8dd85a1511f2ae8c81883e1327d5fa8c5d0c1a23027eb270086511e40bbdec3d94c9639e6b4309cef26da95dde9c89ff6ea04b062f0dfb33d6dd960e3946c5c1049bbceb2a90b77d3b6d9bf808fc42f2616958882412c154c511545efca7259198479401bf62a9a273563230efea26c4d58a7699115c4faebc98c573c7ce24abf01aeab9b9d1e516df3ab09bbd9d52c331cbdf570619a825ba6ed9c244dffab1e3169f1dc91026e32ecbe8d5b2883f8a408565fb032b9700bbc3c96f8a0f8f6dc235607a7bb7133a1a7bc7293e64e097a18479330bd811efca59ce5669487c7b8c5faa943755612d7d7e202c3895880ac3b235c311097bae1e758da51bfc8d8d0fe935f81cd6a093db59fd418cb72dd67a8f99e724a7d88890ea240480277ab67e4e810cb2131dd5c22db13d30acd4158174e865259ad9255b3b6294160f6944a62fee12e8b980af815c95ccf2f3ea84ed74025414199c56a55d9c7604ccf3abffe38b50a358e26eee05c28afde97b0aefb4a33806920db5ae7b60c40de7856e74b41832aba80eec743c9a39489107ac3785b4b100e260e8c2dffb024f06b125a66cec842ed17ab46d1d5aeb2a8fa5766dd787ff02f40060b89e6f98a939fe7bec24a337f3c4ac6e7f3856a7f4cffb362eaa251801390463ba2016649e1f1d5f72e71247fbc34c9e4c4c215d61828a540a2fdf1c1b33624fd91cf4f0cd99ab307447040a1bd00007562440e6052773e4754f5a8a299dea73972473ed0dd4fe703862c160475b1750d121c5fc5fe00d73ad03910c1ca99d3e317444eefad830ea6b79ec13a50cce26efbd299a6be470eb1a5ed4211f8abc5018a8aebea72b4e9fbec39fcff1d6eac0a998aee72475e7407a53a6a307f6fd826604e3f1cdfaec869407b52362520f193163c2faa2f0f4b0e13364101f25c026f0da3d1dc9a16865b52f2c1ab8c70b272a514955e09703dd2e65999f2b0284074a8a2516ebce867f7ea94879c167f6eb9ff9f228bde711c90da8f1321dbed6c4875b9c50920e235cd337544fdab8b56ca1c11b5fcb200555c3c4ce62057fcfa04fe945c8275e91810f104c75e6bb93a6d327adbf08ea20747865eab31351ef871a1d16308dc64c114a14b69583c101aff27014fc8e6d4440241a7cac05d9442f08a14b77fafde02d1a2c6bc22be9c4045df57b3b18fca2f2e0a732d6245c97dfcced0a19de52d218c674be7357e068769bb7dae1b0c31a283a5932cf2603a99afc692f590da3fcee46de8f431a89f40bdf29bb2da90f1e19aa9c86762dfbf0c60fbdd4199e1efb4aa9cc40e803d05ef633b4a09fbb2e6eb72334cea21838ebb7f777df6cfd7b61a1e04fff48f9a1538df4945b3568258ba389e62b9d078c7162192641e8555b2296966643333389bf2b0b5f2d4337c11ed2e806ac2f09433dd34c731bf06429ecca72f8cceb4a6040fc15c6a1d5b4e6f88c51426d17077f935c5e466cb9ff9c329ab0154041463c147decfe028c7a9358adf97444c71613c324bf5799b9ddbb2072dfd46f63018cef40c9310d3d7ed2cc33ca208278158b56da384a028372a52b7b01b16e589bd5bc34a9d8ae73b16845c73d8102cf1c5d7b172d70ed89ae9d07376723ae230913785e1b83e5a53d5cc429fddf694e08100e0e024c2077d177f02dcdf99bcd3aa4b8faa36269267eaba93fafc8fc54148bd3c4f337773591fa715b56a3897caefb9f7d61275be0723eabf5585271d41e18e6d3cb48286dae638831406be26a3898598f5926e215e055495600bd28f908d76e7b0cb37bb5b7c19f63265ce0bfbe190ab87e8d7ff1294a6260545381ca05a5ce9af63809c9e3b8a668316a17be565e117e7d7ce1bab7df8ca5129998951a699df5f22e84c2490dec2c87d207e78ea416c63cf5f353bb93455d3bd613801ff6e9d60c894305c141bde5b2ea25855c531e1f497e7b60d3e58c09aa365aeb2917c540c3449aeaa6b8ced30971092f68a43eb1ef4609378c4e486e6a7297d6079eb3bca80fbdc130313610abeb7be9349d1a913a00788abb46de21cc86d83400c6577057dc10cd510149f3e2e67976add26a945478cd1cef5d01532a0cf12a05571767f8954dd4d6fe8cd206ba3a494db558314119b99258b18db932c4b66501d2425cc0e99ef9b8fc15e55df1dca4195cb83c3a538d602ef74a4b2c2a98960216d2142a2e3c710210c9ec27da3372b3188f97529f582a01f565f7067754200da8c8220fee260d17120d32b904006fb1c85b84ff532232e65aab56f106686b20fdd4f1cf88fc644df8fe261f7430eaf35d958f5e728ed39284493e9650a3a01367652d3e29659ef4290b5b2aca9d4d5880197a59012be51a39ef7f5f0019931d13d48a5a6400e63d1a1c07e301dff8e5ae640c227c6d06c32742c3148b89e512c1932885d0327fdabbaa5a39ce08a72da022d17f20b9dde1150afa1901723218321e776880505f5b81cc6625a1fa205f7b1067ec7f0d81449c59f3e79779a00169dbea15097b8aa92716140c39703058a6df25e24ee5f6288e7ec4b256896f0137cbf93dfa7f89e0abc185adac91f5b3089a2ed5f1119cf1a59a7729c79152056f517d69335a24cd124c32e602b5eca97ebb1919e964fd2523d24737a54c1a0f229442a2dbe9bdfcbfc3551a81761e472df1c663a005e2d0f791d704bcd63e08c34e38a7bf692f8affa5103366f6521eeea297a01503b492104472db10369525a49653b4f83f0d328d0869718c8741228d091a4dbb23783872f67e8b2e883a2466bfb29902325cece5c9dd171566528be02c06195196a3187cc58f5f31da41165c0a5af740de5f82da7b9657d856bc8d8f7ec85bfe475869cf9ef124422ea004baa144ad680e14307b673c353c1e8ecda20bb59a72d4915ce3070744be39b71e62424ab5805fe466b91b597159c7982dc0b5d6e42f5e332e054e39fa8b411209583324d86f259aa1762bcd1f9bd2bd55065ab68e08933d2c95d06923398a7a21c6063b3fb908e242344ba2d9ac403bb28d592474c4b7fda9864cedb1b2958d103a0b7711afab7cc061698610416dc015e8922d571aa592ee56ec27b767426d096384394ca6d88673d2bf3dcebd8a5940a1e0aa7043fc57177f0050b3b5c00c199ff187fbe802408ef25a8dcb6707063b88160c16e651669812ab9f3b3a4aa923d04c91a6392047e3c1eec765c4e4f2866e5f65af7e7ba40c2206713fa08c5318e20a767107d6022cb1259674095eabf21f08d3687b60a1ed7eff879051c29d0216e9cc829808ab1121c5f29ee63e983f5e141f7046fa2b50a5bba4146e0d992488d48d15b5b778435e3e4f8ed4161a3299bfe5d211286e904b16fb90797af31d656c305304f649ca0137b3b024d42ae6767558ebb1211693eaf3fa1994132e28116812d2d5836c4f557fc2c21ecc3b69ae524b7b1c987f2ed075ac1d064d1a05e9cd1caf6bf7352c8e7e863bb60414d69e45bd1636d632eddda9ca8a2a69be2120d69f73e42e5b2836db27e2d5fab6550068e1db9b2f7248df0ea999cb98610759f3d8a39bc496652271bb795770bde7d73f7af0e7dd97b314e0245f2d9a3406529d9881669eae80ee239bcafec8e7e9c8c69dc6f990877364518911f119410029c052fa8473b84dc70fe2180e8e9942db9adcdf27dc8cc9469e71ccab015c03c8f7b5bfe26cd58a41c0f9ecd624e8972137d2f5e153a4fbca5747379a7c472723305fe4454697b11c17c19f4adba3d696d442e292fca66891d339412580c5288add23cf9504b9a1436171afc828536bf8962a968f8504abef232aa409b11c0ab507c2b0ae464b36a442987e4ee2305135f7e80079a384229c95ecb55915902b168dd08d79806e7c49921adc235096056cf3ca67feb762683f8ee5dee635d6048dc41aa5c9729d4535513c310a694749c815d758081d65e841acac4780c1810c7dcaedda05e67e2123d92a32facecc807053b1cc7906e46d789b68431d8f50191167820aaa226839eb4185fa415fe287034cb9e1a5f8752ba93761dea5ba47203eb14dc8e83dc35300967820c83a7b6ef50ae1ea3698d93e1dd204fc93988719d3e3c06c97143874a6ca856c4863470f544682c088d0d81978d50e484b70340e4d4447356176d84b867cb179542297bd99d29082465269b6a2ef2c00a44c3b010c244126810be2805228ef44c3c23a31c52c486732e0c9fd8e7996ac1f2a1910d26c34e5a13eb0e2e38c3e75a03bb04ee034efc308217f29593e5183b2a6d91e94d0c8626d8b017b85c24f3bb7721a922f6508743026cf3295bf5aaeacc2741aeba0759d64cae6348940183d0acba06cbc60aa5a731c811a01634f3dfebdb91cb0f61816f10606ae72f492b694b005f0d74340d3b47594fa14e9d42ee6ee811be53a80def707f9e127130ba3bda7497706ec2d358896abc9506b19171b87d42fdf71082f1ab195a94c0e33b0a4a2c4c9472aebd7607a24c1fd5924c16d6e982a506fe6a43abee0d62f2c1a9e41326129201d368d52867c168b18d38417a02208a0c82a262162bf98770f849d3b3d2d7b38e6b0bc457ace7f24dc70b3aac272158d811e74b1231fdd6b3c05f3a5ecb728ff63d42d7257c0419a94f24071fe272bed7e3ad65efa0e4d97e72f4bf3f1c9022c27bd0ee542adf7be27d806c6ee772705a4918303bf230543cd6c5a6af62409c3a3dbaf58a95954a3850d29c052ac00487e56c628afe653ec4236585648129651685ff27794cfe0971bd613a11dae28b6da19e2c5f353552b7a0207ea9754d5f631383ac5fbbc492f4b732f2fa5f10ec2bc2e79480d1fc19930949c5f3cd9f5d25036d797c30967574e110d01f2c42000499d98a62ce0c632490d8ba4d1433c063d6b8a2e38f87ac7cf028f0c7d252c84e3c27c55bdf0d8f6c1c6c27bab2c9c3a4e2261991a1b33d7cbc9e81bdcad2c1ce50ece47ce52ca60b0c73423befd41138161dec7a42bd21913877aa57cc029009966be72eb5690adc4a8b01ce9505d30b5b724c666b9cf6d9b84a6f8810e219a8a338c704d0a2ccc80931b5dbe16aade261ff9131f19475633f45ca3776c11dec5c6bee7f8ce783e237a0a412f5ee4d7e668463abcf84548e7041f13540a0dba462ac361f099418c4876dd3014aa6c31e9925a985d302a978563897474702541fcf9135d254b0cd3504bcf516342e7590f2c867ee93d635d4d4a80c6a5942f08bc88d43652fa73dfd216e3fa519cb62a96c96f30d78e14eae6493534c9fb0381cdbb2a8f450da71a5adc96b30b262b8e90b253fd4ec2aa092d728c1ad03a24f3591dc427421ba451d03b5c52bf35c0c1cd4de84d6fb832960e0e3d0c3fd50354cf50873dd3233e552b8084c6f5dd9f5d8b9c2d6a6701b173e1e2b7260c5f30056c7b53884a2c5a659e061a22a1411631fe9bdb6d3609ecaaaa22da214de4159905473efa6d78ad59c7c0de63a30ebd7df30f6f4fb030450cdf42b38eac691b4b50376eab2cc67d2f14349f8a216a72a62da8192bb8335760824f9c9a441c2ce4ac7c9bf4e10ea62791a8a2453ed87812b75fa509223b9c4cf63cf2d1f1c70e4636f09a0e721e26d8ac68a1a5b540563b07b78aeda7c8be8bca2b87956c5022a0a8e676d0b951c0ce75d75c5a0fcbeedd746a7fbeafa9c23e1cf0bf56a0b029580d63411373e281c08686cbe8a50d12ef979633240d0f25cf84a3cb3c7903043b65799c7bade21dbd56f3dd5ac7984ee33a978dc468869fdf4b2dfbfb723424a763c3d16559034ada91cd7f6a0e4aba0913c21e6429235c01b51ade7bc9902b5356e422fcc4ab1287153cf14ffd5932b22d988f62696ca7f0af95e20d7cd910605dd18d56f89d807da70a8b56832eb0b88a86c0544cf5ca62896d5ff366af2ceef3146ffd89a84d95d09de8ba376d7a6c197b667ea01d90f91b15224a94a322adf84ad278e3a08a12fe797f4400964d4322e735d31c2c87d77b067e593d3e073a6e457ec20a6b95390cbba0bffa2f8765b8acaf27946b2f0651e8278cca0f19a0bf3813b872346c82492f62735a81d884914b4c6819e15dfc342b2293dcc72a11f1a5579bebd7e3e8af6702b2881be8096ec2e57cbce183ac58a2124ad58128a590203e76a0ccd6e66c173d160b756b25ae4ff1869da33b9c7393567707352fe2e7088b7c3950f6867ad639d0ef4a0cbe28ca461378d3dffe44639ada6ed22d7064bb81e36e71ea7eb36ac1e950709070e379f2ffd9521b0cb1483214ec4f0b047eb546c078df3655ec96ddd13cb3e5d2a67c3ac2f98e8ae52785736765361fccbbcb4136f7ffe1841b5c6e944f20396c4cbe32529f0954a3da6a1dd8cd9e07e6705137a71e41c85b8776be680bd02090ed401a880d7ce5b5be223baba50c17992c88e3abbcd6d2175313995c04dd3cebd70cd7a4f4a9f23368a0ede9f0280cb16fa6089ff1062762cbcc314ed1c235cbf0995f1f8bb0dfc4b6402f8ef52c", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e4412165f58bbdd51389e19f359b95755ee0c47120177439bc8ed45ec7a6eee7f42d00000000000000000000000000000000f664ef83cbc214b6485c19a70e17f8df000000000000000000000000000000005d9b229a79d0f717361c6cd1574153e6268b63ab57f71e917a79017400ac69404ad136220c9e12bec413a6d3d3007cfc000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000022bd2a61584d2ae0287a98d2b3e3f3cdeb3b3582244367a14fe46c733423f21792e6d2b6cdcf0542f58fc2cbf0103f2c332c99654fa3151cc1cf346bbb0ce499d1f7cce37859a677eeb7d36d8fe2112e0b1f445605ee4bd6626931d8993fa30dd22cfc2030bdfe1d0c50b8fd63d2fb2262054debb3b672771fe31a0e8c6d92d44000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - } - }, - "test_exit_code": { - "crisp": 0, - "folded_export": 0, - "interfold_contracts": 0 - } -} diff --git a/circuits/benchmarks/results_secure_minimum/integration_summary.json b/circuits/benchmarks/results_secure_minimum/integration_summary.json deleted file mode 100644 index 2981da6ea8..0000000000 --- a/circuits/benchmarks/results_secure_minimum/integration_summary.json +++ /dev/null @@ -1,250 +0,0 @@ -{ - "integration_test": "test_trbfv_actor", - "benchmark_config": { - "mode": "secure", - "bfv_preset_subdir": "secure-8192", - "bfv_preset": "SecureThreshold8192", - "lambda": 50, - "proof_aggregation_enabled": true, - "multithread_concurrent_jobs": 13, - "committee_h": 2, - "committee_n": 3, - "committee_t": 1, - "nodes_spawned": 7, - "network_model": "in_process_bus", - "testmode_harness": true - }, - "proof_aggregation_enabled": true, - "dkg_fold_attestation_verifier": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", - "multithread": { - "rayon_threads": 13, - "max_simultaneous_rayon_tasks": 13, - "cores_available": 14 - }, - "operation_timings": [ - { - "name": "CalculateDecryptionKey", - "avg_seconds": 0.034920472, - "runs": 3, - "total_seconds": 0.104761417 - }, - { - "name": "CalculateDecryptionShare", - "avg_seconds": 0.163728264, - "runs": 3, - "total_seconds": 0.491184792 - }, - { - "name": "CalculateThresholdDecryption", - "avg_seconds": 0.155451458, - "runs": 1, - "total_seconds": 0.155451458 - }, - { - "name": "GenEsiSss", - "avg_seconds": 0.086142097, - "runs": 3, - "total_seconds": 0.258426292 - }, - { - "name": "GenPkShareAndSkSss", - "avg_seconds": 0.101574055, - "runs": 3, - "total_seconds": 0.304722167 - }, - { - "name": "NodeDkgFold/c2ab_fold", - "avg_seconds": 15.959258972, - "runs": 3, - "total_seconds": 47.877776917 - }, - { - "name": "NodeDkgFold/c3a_fold", - "avg_seconds": 93.866109638, - "runs": 3, - "total_seconds": 281.598328916 - }, - { - "name": "NodeDkgFold/c3ab_fold", - "avg_seconds": 5.200028763, - "runs": 3, - "total_seconds": 15.60008629 - }, - { - "name": "NodeDkgFold/c3b_fold", - "avg_seconds": 93.118229222, - "runs": 3, - "total_seconds": 279.354687667 - }, - { - "name": "NodeDkgFold/c4ab_fold", - "avg_seconds": 5.402757042, - "runs": 3, - "total_seconds": 16.208271126 - }, - { - "name": "NodeDkgFold/node_fold", - "avg_seconds": 12.258274152, - "runs": 3, - "total_seconds": 36.774822458 - }, - { - "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 2.742254625, - "runs": 1, - "total_seconds": 2.742254625 - }, - { - "name": "ZkDecryptionAggregation", - "avg_seconds": 46.409371833, - "runs": 1, - "total_seconds": 46.409371833 - }, - { - "name": "ZkDkgAggregation", - "avg_seconds": 5.355026792, - "runs": 1, - "total_seconds": 5.355026792 - }, - { - "name": "ZkDkgShareDecryption", - "avg_seconds": 26.991641868, - "runs": 6, - "total_seconds": 161.949851209 - }, - { - "name": "ZkNodeDkgFold", - "avg_seconds": 131.495591819, - "runs": 3, - "total_seconds": 394.486775458 - }, - { - "name": "ZkNodesFoldStep", - "avg_seconds": 5.025035041, - "runs": 2, - "total_seconds": 10.050070083 - }, - { - "name": "ZkPkAggregation", - "avg_seconds": 14.269228416, - "runs": 1, - "total_seconds": 14.269228416 - }, - { - "name": "ZkPkBfv", - "avg_seconds": 3.484502041, - "runs": 3, - "total_seconds": 10.453506125 - }, - { - "name": "ZkPkGeneration", - "avg_seconds": 111.11701825, - "runs": 3, - "total_seconds": 333.351054751 - }, - { - "name": "ZkShareComputation", - "avg_seconds": 46.703856173, - "runs": 6, - "total_seconds": 280.223137041 - }, - { - "name": "ZkShareEncryption", - "avg_seconds": 114.189265579, - "runs": 36, - "total_seconds": 4110.813560877 - }, - { - "name": "ZkThresholdShareDecryption", - "avg_seconds": 94.550274083, - "runs": 3, - "total_seconds": 283.65082225 - }, - { - "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.084805514, - "runs": 3, - "total_seconds": 0.254416542 - }, - { - "name": "ZkVerifyShareProofs", - "avg_seconds": 0.681836558, - "runs": 5, - "total_seconds": 3.409182791 - } - ], - "operation_timings_total_seconds": 6336.146778293, - "operation_timings_metric": "tracked_job_wall", - "phase_timings": [ - { - "label": "Starting trbfv actor test", - "seconds": 0e-9, - "metric": "wall_clock" - }, - { - "label": "Setup completed", - "seconds": 1.010659958, - "metric": "wall_clock" - }, - { - "label": "Committee Setup Completed", - "seconds": 7.030232042, - "metric": "wall_clock" - }, - { - "label": "Committee Finalization Complete", - "seconds": 0.002351166, - "metric": "wall_clock" - }, - { - "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", - "seconds": 150.009414, - "metric": "wall_clock" - }, - { - "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 591.111398042, - "metric": "wall_clock" - }, - { - "label": "E3Request -> PublicKeyAggregated", - "seconds": 591.618044375, - "metric": "wall_clock" - }, - { - "label": "Application CT Gen", - "seconds": 0.284318666, - "metric": "wall_clock" - }, - { - "label": "Running FHE Application", - "seconds": 0.001311708, - "metric": "wall_clock" - }, - { - "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", - "seconds": 49.252521, - "metric": "wall_clock" - }, - { - "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 145.377759459, - "metric": "wall_clock" - }, - { - "label": "Entire Test", - "seconds": 745.328859166, - "metric": "wall_clock" - } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000003364d5f86765e55730000000000000000000000000000000000000000000000013898d5b3217f0139000000000000000000000000000000000000000000000009cba581f8861c2c480000000000000000000000000000000000000000000000000001d14afdc0a921000000000000000000000000000000000000000000000002552a58ae13c3c75200000000000000000000000000000000000000000000000e7e4b93bc2201f7c500000000000000000000000000000000000000000000000877df7ddd752964920000000000000000000000000000000000000000000000000001c9527c8df7a900000000000000000000000000000000000000000000000320dbaa2a0494ba0900000000000000000000000000000000000000000000000acc0a46c1ded6ada7000000000000000000000000000000000000000000000002f7880f0ef167051f000000000000000000000000000000000000000000000000000254e3c3c490a100000000000000000000000000000000000000000000000e0d5dde4b4b253cef00000000000000000000000000000000000000000000000de5dd68040249256e00000000000000000000000000000000000000000000000210c9990fd7aaed99000000000000000000000000000000000000000000000000000122c6e8f8ddbc05637036a9cbc03d47db8a97b8c809100c4b4b596676cba3d015162923686f7d1c9ad10daa62194503463b71646f34355473ba99b7e1153407d04205429b92212bbdc28e4086fda571be87a08652031984d0fb154b6bcc98951723e7413e1df219d31cbb7e938bc530ca471a9862d1724fd4ba4da122090b0f7e30316cef27432fd4a5b925bbfa211bdd9c0c73a088b4c00d7776f80093af20da4da7f40e594a07f5542473fa8234672d8ad016a03543ca60eef8e412ed75255966400e32517b2707f3a24c8e0a04f537b31b5197238e6538af0b1301d16f811c8da1bb9dea4d0c7903aea1115ea068c1e1a6997fdd5ecaf9b2322ac97bf2a008b5879e2f3a791516f064eb657817d1e7ee76334f92f499efef59bd9a601c3e9ffb50608ead9102f5049691cf851cdc6766fda8285028e9db62adb27814487a9429a337e6936a1a0e3f2848cab68d52f8679da46288fc560a7e7b821c3213621fa4ff366a755a1ea795beadc9c33fe40383b14fe8a5859d79b7f3a086e3f4c2be0ab43f234165077dae46dbe0b053fda32855bc8eb56e1b6746644577bd2e275715a300b2969418bf7f536056cd9087b7843f299fe8af3dbd855b47ea8e9c5f6bdcb1ef2b38c21aa412804758ac55e54c0b70d491d71dcfb501cdaf701cb751f71447e862fac80bd95e527c5f42a372bd21e3a81c9a4595f3c13412dcff124da1ab9f44da346504cd5bac58a9b5cbb4b3733ae99956c09b0f85a41b60fb4eac4497d79b6484f404a058d405aab191ade77e47f4cdb2803eb991a8d7ca04d0f59ccc979a79c95d161663b62b58bab1c53cb7a61ace050df500d9e677f2de4c16c3a6ff25e2b419071ef6d964157b3ce9bbf395d6bcefe2484125e8dc164f8aa02f1c0a28f204bd186565adbb463427fffbe71b7787da500ddb9bf4051259bae376bb857e439b3d275d2d2242a2a9020ce2f8815616242aae437ae13d0cf20bbea6e10779a3c1af12f02141e01601d804f257e99b94fd469ea830620de7bee3cef0cfbbba6e88420a2ae0df2aa04ec990e57645976ff7785f3e123f8afa494667517986754b9fa82b3a93c222262ef490921618d2485c9bef7065930aa21d4d94a76880ec7285ff2bb27ca0d5035d42d98655f6764737988c4a67b3a248e11c548fd4e9b14710b42bfacb0d19d5fe4a37542626673dec4d9525d7cad05370cc8eaf9a2f0455568c06d975e4fd71720c84ea07073abaa298f1a8ffd6ed2fd12da28228931b19e6352a746fa580060822888dbcd526feb165d58ba6c99fc68bedeccb5d124ee149731e0bfb35ddeaae20355fea45a27d0f14e6fb0da9407eba7f9c09a7cb935662b02ea5f9ec15fcaff08acf13147948f8551a02eca367216c36e3087a706ee1e06016c47cd145b7336817e7d1e93c80f9db7637cd5abf0058d9c1ce9090d0785a5225da889e8bb42ba213b5bb4c094e99e815abf1c92390b5f4088527b5afbb98b42ee99e1602040c8fb0e5e643cdb7c2c4fd53705cc1e7955964c26d8f98ffb08a1992d81cbcbdc6a27dc75bab392273db8129a6468594e99666e26d271194e25d295496ec3dbdd52b75c4aacd22e122165ed94e35bac87c74824920041cc66b1c165cb012d54f44eaf8466993009b52cdebc6b3412a6ea077f3446f37c02c21e218690942782d0c3ba2f46540f8e1772843f3ba30dd62e61987cf5986631caba81b2db82ce18aaa22b79a69f2c13aafb48fb82b3ec09f11f8eaec68bab6eb08e000a00c177d0ad8a07b873ed0028da01153d0bda3444cbb2894cb11b9fbbebb492703a0158032c6b4175e7879701e3f610b6974c9947c6ad15fffe1b5bda878ed26b6a12eeaaec5c0a13c379bdc9a13e3a126a57a72f20326b4fbafd739822a6f03b1b2d3f783578e2b90aa1ba5602f361187949209582c0060b40bfe5e0ca9da25d94fe2b3e258d69acb28e84f71a1aaace0996114749369f0fe56c1ca59d8320f78bc0c2690bd95df04d8c93979136d516b0bb816dd837a4cfef67f382ad53b1b1be671ea0819547bd142cb60a6b6213a0f7a97ff4be899d7aa4a7a93fb5e0205962e6ac6e7faa7050b432a4ab5c8483ca7f6d26081dbbb47d23fba895df9b30d005331e92edd87ba6af3328bdbe197fca7e75174a4ffc39c1fe3468099f79804ef965b09ddc14b89256a74e4eaf39ed029c9c0c237031325539c9fed74c7db1b8140afed5bd54a5b5a3fab63f6f5de98c35a0a85f5a6293d8df599c82b5d7d1aca8d3f78020899f907e13309876b09cd9356722d5f8413e8ead926b7608bcf01261e10f50190f4047a5b92c010eb6bb89bf609c82e03eb14054393c794729026b527d94188d0cf11411ea77a82030e9db876d798a2effaafd69e9d86d8a9be0ad490ded533b093513a777ed55b2ba7a5638cdd98a70c668a5260541ea17cfc2f5bee858237f65e8cf286bb49a722541341a054da56e94822683305fa063621218319be7e0c0b6ffabbf6d8a9e4b6a85e5639fd6bbe150d36e1f55b7892f4a9095531329a5de69186f0c83b3ddbad58436dcc173fb358e027864c3953f35e96289555cb806e8364a865ecf1aaf46d4787ff9fb38cf89f0ca6738184519432db2d8553fc01edef897fa879013e5a63cf0283bc43e79e30d812589c941757a0a0001ea5e1b04a12a752e546c5771417556c7a363ed5aaa795b61052d65e118a2900191a1705e7b3d201a293d2b3929aa9a9fb6917166d3bd3106f09caa1bc063c233d56c3083eb21929e973bd8addb5a9cc5131b5440ad595b98777eb13ed60b60665d9c3d99c0324bc9df401fd5044c647e299aa2083888fac2e47b343289f482a210e81e343fb483732848311b03f6a746bba4d67833f0b5cdbdd7a428bd8a01f3b4efc08dfc8e3e7d240f83fba5c757c1cce545fd550ebed97a77244936b3322ac455f5ea2115d1ba5ca855394e8d3ed996890efe2656fabfbb1b7625d4b76049a94a53410ee40abc977ecf05997a733ed0342d38dbbea39af4ddfdb89912717a2c247d288a993ef5fa86ac0ed5b1f2a9b4b7a698d42a9d4fca9ed44d54bbc097103b2a071b5a5eca3f4d4902907725af8c199d9eb8223a520c80d295eccc71b16ed986926558c9b653b43182e741e74e15fa725d854381abf5208441a0a660e3b14fd0c2c9ef201e9782684aaea7343ea49447238e7dbaf0690d03b9f275f0cb7c6be8eb2dbb9308cda5dbcf24b6bd226d5690731b79f98f0725517e2a02b255c80f9e7fc4160bbd8a68afa1a800d38d01bb36221ed9f71e19dddc570067127062bb0dd269641f7772639267199c5eabb57d0360301177f4ed053439611e2003ab44cf77b5ce969c060523d35041de3ca1a778a0850c9e5d70553a66e54eb0138c67501ec1c07bbd8cfb8d95e56cc1d475f3f8797cdd23c638b374e219c820d7105995be2799a498b963d92cdc40547c7b58baa9580d3cc7e321c1f14a9e6166f67f602df0a0c9ff858f22675f004f288b3f5cdd1e46aaf7575965c59045f0548c20f8a97ed6c216ff0f74b7727240833a8c598d79c721916adb569cf12d22b525638618576ff06c5fe30e92a3a35dfe43f19fc065517f5a3dc7e7945a87409cc4c99045e308d225f05241b1e19c1361c6d0797cc6201af05e10f4c06249e1673f4e88db2dda4c24afd9126ccf8212a956dd2e5264484dc61a6bd30ab85f82016a89e96b443da09871a57cc5c4bfd3009548a23fd9bcd09a74e16fbe68f9a0976f03c71e32682c539a01a0cceb98985ac04ed3d51520c05151a0fb59a826b24ab0aab396851d321ec410dca8aaef709ab76e3e5d869bfcb59a2adffd5af6a05f09b0c7242f08a0dec3f85536d4c998bc257634b02c95afa77d34b9e5e43950ac6df6dd32308386d9e86ebac07940253592405c7477dc9c97027f6b15e824f013e7b4da0052341810169806ac0666fed6d5fe1e2c07e35acbf763fe6014a272f9b944e5c0347748055cb3acb55366c570fb653bfe3d64b45c8802d11aaef541cb377130fdbcfd6e31cbb8d15d7de4ae452a9f8cb0fdad9c042981d1ef3d2fc1964f2a8640ff3f41be296d43c7185044ffbd642d6e9e0c0c91add01d309a62c260dd0988a99bb688892fade80a992639d7ef52ca0bb1ab9da9507064cd7f6ab1855ece02143612d8324dc37ebbe79f0dfc850338603d5ba0800d73cf931aae323440501ac612a78fb9fba847edff4fce9024bc94da89589e144b6aa26ad7a982621f284d00f1a43f4c44abcd1bb14692afd0ad75cdd7a6535f557bbe18b36dd04a8d776cdc1dccc176fe09b3419bea3b43560155aff182d4dc3910957a439772c1a1a8dbe869d8fd104619d3aae677dd2392608ad2b6c9e8945d05fa50a6ef0072906522aca65115f76ad329b82032e74775df197ab66081eb5afada71ac0022154c39778810d61e95f1e76d97249f06a74c834760e11da15f71ec1ea76c86a0637340304734146813502507bd71137a9efe846c4ecaff6948e0b0bb80fda89304f46ff2721e68dd21e398524160d8ad5789104557734c37f1a1bc03d5b83b72435373f869a50576c81874a5b919c76c242cb6fea01c276af84022a88db859e235814ea7ecdd00cb07f0db60f6aa3fc30dbf73ac145769da4ecb57664fa5adf2832af2f9997d0127de3b0bfb7e39bfb4e5bb5583352d3c9b002b229114ba8f41474b1d84a76175bc3b574b5e538e13207f46401228052bceb67c1c5d519b1c22abbfc7d11b54ddbb8622025063c37091c2420668387d2018d8731ea32dd830c2dfbafa2e31b7094bd322df003ebe9f751fd286b51974b18dc29546bba66f3c50f242bea465e8bfd53ffdc9bf04ccbb247a6450c34787ef47233b44177a5a08c0bbd986de3e99682d3376f585c85d847091d587b7a4830b7028f7aa97aac02512b536e679c183b2831b0e6c40e978a506b9b457c410cfa3fe1288849d7825ce2207396d1b3b2cae8b9f6f20c06914e2edb01654eca0c2620e38c1ae059ec919a030c5f2fbe8c4ace11d53d57a09f17ce34c917662d2d232e0a5e1a3e9cc02e7217b49557cf354b5ebd4fa65c85e80db713ac5b1cae66af95419b5735a81f81a82c671ac96a0cb0b783383db55b6da011641a40ea7a24736dd0c72ff152ed78791f792bed18d00ae1157525d79030644d6301423eaa3a038dce0e49f2a06ffca7234b8a2549d22e679f7f92314371078c40bce6596bec7e3ffead71723b74a34c2f7b62f8b2a35b889deb5677f125d63845c8a40f03cecec94e3cc28e39721f542d0def23c2883cccfcd8298a5023ab43ca7cf2bf76b96f30aad5bd0b7309a2960b4abb445d101191a8dfff6c2a81f4673790d6954c05d39af9d4e0e0361285990b016dbb1ddbd105f56e8202d24c8e0ca73ae73a9d59eef07b6099f5b9175f450d04f47fecb947cbfceef0345276abc482cb90d6d9fc15313407b3b2fa40eee620e0c0978cbd1c856a076a133fb68537977b111559aa7fc756b9e9b081dc2465105b44de68d9569b5d33655dbf08b59c65962e728b5ba280cdcc3854f540a85303cd08f556cbdb4b4121569f6a0a18c5ba999e53fa2a43bde6f259a14e2d9c3c16322bb8971549288b9ff3623085c7722d703f192579e2699503e41e726790fd2a8f51b0db4aad2dc01554cce7e9025c33c8ef23989c91fcee2a28c872cb62ea0e4f30affa601d0817c8647d889656007008f40b9af5d34a30c9c809fddd7b7814b6d9f8e094a84ef4f18f41bf3a9e7b7baf814db20d0ab5bb9b42b37979774528c463891841071583850fdd6af365abfb11c565392c6baa2bcad2c6fadabc32133092b0dbabfbf54586eef0c4b4bf0383f7572049c6f9df4bf2ab9d31ed0c721e5c4c223c2bb707c1830b1b17662861ce1f8941986b90a76b1cf2bc3260cae30d1dd89f9a6c098effb6ee1ca412f714723eb1cf4c473b34a5cb217dea8b74c12c63a31396ad87e3dba94b721fa06a7f47d1eebc27bfaed3348c9c12b782f0ed1d4b145766f696f025b0d88d6aad1d3f74686406240b5fa2d2339da942573aa50557b326ec15502b993dfd5b40a8b7d905f626fcc83332325576e3a2831a1aac146701e9b1e33e3fa56f6882945aef0ccd7bfbdbf2c6eece329676a37d7b289a1029438218425b5720d8ddf4c1d23fc5eae3ccd8fd974ddb90164fcab8f8f91b2b4f1da7c3df06544a2c9f8950a41475f928689bbc122b35212c0a44fe8b856e11336e9872e0b316c6dd24872a23c6ec9e970ea3300892fed0dd8ca27b3a2a5e0f2bccd951065094b96178eeec940ac746696db9f3dd3ff7786328ad3a75b1e0102fd261d3d410f86772eccb3fc7d26631e3c14c69cf07cfa787b6195f04743f1fd6445eb97161e79e12594398e84b995cda9237ab7a6133f697bb10f2695b570e3a3e94d2fddc6cd5780149bf9f7256d8f32830c9c6b40df6534a02c8efe12a0bd30a5930cae51940cbc81dd13b1b9a9f9881baf8c0ea68166733de5fd416580320b7c942e935a457e8fe37e9ed9064055ff294247105a2e77e90f2924edd242ca138bea4bd135795700f1a00bd6906bea58fec8942a16e25a820bac9bac787229730ae4fe107f5734bb7eabf2a7c5d9bc01ebf94d6a126bc3113f664d8db0512fc47b492bd4c5aae7bfd1f952b9ffd51f62aa32e89eb34db3bfc9d235655371482b7131b14896e77f27bd7f4dce4674e00ca166519019cb04e4fee1b61aed80254cc7c579fb5444c73f8116a8532ed99d171095c72f9eec53f1ad40274f0e2271cf4d3a88f46a987dd76485db6980ad155fca47838377811752a4b6b6c23a2228a9c58f4cb708077d04f6716c695fe2fc191019baee88058e521558305d6e112e513afab7e0d8a61b4da20460549c7771f31ecb1cb17a759624fc6d7266ae62e8e56f844e004011611d0df1cad0b5bb16d37d857663e9db3fb0072c0f4af3f10882047424878b57d73d5a23fb78d61ad9d2b64bed929737a901bd2c59197010158a8e5962b62f98f6b7a3ca5404cdd4266e5503378afec3231e67723a025180f3e558b293500ac2941af4f4f878ec45f9608c19fbfb134f0f32e1d3191e51e2a50479651924f39e7046c472b3a8bb9a695fd29febec0af951b39e6cd3856ab0cd1e3a917031ed4af790602e6f5d2f3ea21d4888aa80b6b6296dd0b1a9bad4d26a026bba5f8d95b78f39e0ce1dae773e5799cd03001c6017aac624a8663d3f225c2900b434394a66ef6d9aa682e1a77f7ca6127c1d5bb7e17fa9502f47ac056242b1cfd8d4e0cf452e8079a4ba0fe40f49abf4f0b16bb8de55cc30e15c0f27b17a577988b0ec0bf5be389785775b9448d4e3b2e7218f5583c3eb2f9bc4ccb6726ccb5efbe2bbdabe054059d359e264aab3b09ec0378f867268ccb958a55748a20ff83cea8813e01360dbe25c66848cc6ead1eb8fc3c20ba192b358cbd68070a2c9fd0fccd5dacce5881cb0ef3416f33a04f0dc61e5fa1c15e146937a6bc96be28f20891377d66aab5f049178e2d08ab303da363f9be92a05d26ad5a7212122e22faef662f1a2a51d8d10746473f1a7492c77f4254df2fef01ceefccd943e6182adc780f621902759452bb0996969289b307580215cd8c10d211e2f104ebe6b01971d3078393ac86bc70859704d44dc6620b37aaf7ef8efa6d65818ffdb7a08608d3c702c93eb0d1a30c1c2f4349eddd954c9c98a84d9b2a9f1f4e35b1e00619049ab58954f39329942cf00235914f673389a190287154545d8d815d101772f2157b1a8851c7bd2f00a6524cb42bb03943f8ee017f73f798ecdff703f03de7e1062472fe05fdb8a50573df3a0a36df3894852cd85ad36c52f41f4f9cbc6360800e7fc727230a7b17b23eaed31882b36233e763e6c8b42520acb4260e7a12d51626d3b8f71fc768cfe9c631007b855d613ca296de24f12f56b7b5c0e889496cb60b82805abeda0621176175664792ca8a1c7cae9c9a1e4c082b4ce4e9157aa6980526fb88c2942eb796f0e899583b51bce83f2fba4e5e7cee6817f146a37b850d2237aa2da7f46be0eeac4db2fdf7078093bde6ca19f5d388efabe1166895c5db0c1277ae223bc981a8fcaa3a96cb6f1b75b06420a7929468f3b09d2b95402f7c0359d0c6c90cf4e6953216f6bb3cb134ee16d132c88d0e189c6924a10e9a50c611e732137d9832b05b7641bfec77cbcd5dce54bb8445fc7430829b5be5b947a71c0a977383835d0a5e1b02326ec9de0202b035228e73a562e07d6e1bfa8b02750375a33067790d0df9f629bd9a3089652f9bcde90e48a78123a2042f7733a9d211fa4677f0cefd67c9752452a421ce7410e40b8d8bc83571b77012ab273ddfd210712485ef4bfb9d31194b5c9fae82bff89ff7aa4feec8fb98ee88654d3f4b0d088e671bbbfc49c58b80824f4889e3d8b88a263721027d2038a21791d5a4327b299691b2ca39b3b8d6af41b881e8d62520bd4c5f87c39ccbc9ef996964f99f651b54c52ebb215e8c6b7ffdbadb59405fe665d02e552db2af279f356094421add15ea7f9458298a8bf4f9f13a889567697b8b3998e7c0572d7c97c895a631604c2102f60a1d86647cd85b0d2bbe66951053d77470519a458a5d56e68114f2050a208f92a19b93299270a3e57e1747b6268df01fb686418131c64e50b5c0773c99215f62d793a906aec162412f16514037ce6d0d7041edc67477d17df417db07b0279d799949b09c4e1a066e84497d1503174ea0ad027e00542bae3b99002e5e3b2ad8599ae922629c69729a505ea77f2d939c10ea01f6bba1560a9683ad556f020bc134abeb4bee4213d59ad2abaef4e8b42aac8409dd4f3851a0608068a44ff302207172752cf7a87b1e152c794abcc334ae7737bdc1b885f46b1e62c2a68db2202305f867161dbf9fa9c4526b4450a0e1d67ceef70d3daf00953a4dbcf816a31a54de181ad068de5694b5e4f66e117b739b14fc374d92a65ebdd40d7f613c9a29aac040b1afa38249c41eb33d75a8f94b8dfc99f8b0151fbc955cae401489ad018c9e712e4e2604e1101175f66ad231354cb97de45045818dd0a0fc2e15f1be27e780231cdaa1d3f3a187e8204e9c8a5528f9652f971e1d039aedda2b54e09d067a78504fb71b302b76e1edff7ab78e01ca97af4ff8393970babe8edde24d2e15bc04041cb3b2563295fa52a6104f8eb2900e318cf9a5b8ff1d04433f58b926244721245c8add99d9980044aaa0403d271d513a2f231ae9a0caadd0921c23c02ecd1e43a5821ef9eadd113b7de3a995abdfc86dced8396f229b2d08b08c338405d4df556e1704a43b392da30c1ad5786d811a2030f8cfe61d1a550ce1d5e4b508d6281145edeccde846f716204121d2fec605b8659fe4995f7e53bbec59d1f6049eb314e2b0647f2ab0a4d668eceb480b12cbb3b2812960a5b452313a2a568d1d56ed7eb4fa5c592367a95cb81d819c10743fb7d0cc90941fd0b2241b9f0cfd1db58a7239018e44a123afaee85daef8f85edcb3874d7824cd77acd12959c2e2072013c61ef8f31b66b6d4b11b4cd119047595b0ddcc2d6a89662a7731add70c0df75cf6df5a7f7e8f2a9246f0b382d74fe2dafeba90501d1e3caee4a2f5fc7903b4628d22ad2e32c40a6ac9e99ba6226cd821d95d01ae439c721634454f0100202f16180e2d91c211085bfb37ed4472db32bd8f9ec7fdff9e4dd24594b9972127224d12f654e31ecb85984a5a0b467c028fb37638e67eb434af1f5d7230ec290f7fc290da8eb85daae9d9697fea4610a07373fd2d2764697c2737b3bc93fff624712313de850b3e3f57a5a3fc797c9ce9641efca66d1015154d68931437e4fa20f3244a0003071d1a13e374470ac154a3b413bb2dee2f6d331ca946051524142d969a9c123a712d88ca4c665b3457a84145c27a7c4e2078e21386c0ffdd6dc321c5a45a0c889613e4131409caa9dd89eb3a1eaf86b78ba0aed49a7fbaf74b1d065bac18e74f5a7320f88842db40ca2ffb7d79f024128f61e310da43dc2ab00c2dd7d68884e3a287457d5cc3d8c00da381f993c7c9ae61dc254b298e0d3846d32d0c85c60def081cc7b3d3a81a0decf3aa512294e1da52d83fe6cddd667685ce0c643c9f7c2537e3c6e921dc2e9aeb65bbc42efa14b59611d71cfe317c3f61fd0a449e51a7d5e218012b4e227b6a92165d99cc3bb430af58fd2c2c682052ae7223d501835eac40ff6348208181b98125f48a7780638f143b72ef36d64da20f072a756c581e23987031ec4f4c7a171ca87c000e64749965f53a2dd523e7914fc1075c356b4e54299c777a760ecf0d7fbc7596754013c629acd2549de9158efe75038ee98a28902285d479e57cdabf8642c48d1d2280dbd0e637024a5113c0d90920a507b41fe804ca87e7d0ac6aee703e60fb163fb34bc4986b9e8f8b18a7a6810058ffb28f214447db4de719447bad2de445f56f0cd524130c65e7c642ce55ec0366b8ca1ce5f0358d9897eb56d0d32defa3025a49169248d6a633a6da325c2524343fd09f1b5514879e21e829f286121ecb6663b79a89c63c6f77b9f7ae7cf725e98fbca39650a45c154a4e2e09ab3d8a8a053a8ea9ccf02a3ae8d513aa20c32e18b2f822a389705d508f1bf47e36139625cf0d0021c262ef49f3b72659a8b81513ff0cff62557411eab205e7f2aa722c30a61e314d9e90cafcd4e3fe78f8ff1908bf49948453668fdeb112c04b518074dad6110f04901e056f8c0ee9bc423a27ffd028d2d9674b738140d1d558fad5da13e27e7daa818cd8345100a31279cd13709d6f5958427696d22e29453db4eb0bd708eedb0410e18478cdb6c2f44d8a01d0871af70d6a084e92af84ae29dda995fa46230db588019cbd1a1a5289503d135a99c4d85ed6fcdc15fba51eb3986e4a9df695576671efe798c859dc83a9c3110b048d58682e4de817f7d738369cc90b1032451600ed7733bbe2a27d9968390ac18f3479778588d4bbcd89818546d989f31d113ba16429c5386555ea4d100404dca977bc2186d5d7fe423f31d87616bdcdfa8c3d35e7c5d7543138d7d731d50bc72e4c1187f047279f9c16d300374b2cb74764c174d877ccc8793cbdfb9e85015c49be6190e89ec87934836fe33bf0a624ecf5960f919501fdb37501b4d68d2984855bc233814353d611548486405fb206cddcc4a89693cc31b4a785dd02291b0c790fd693a3ec406762157dbe321d027aed3ef19fe54bf0aa27b1109950f629b61d6733ab4d83843f0e2f1c1adb043c1050ae74f5379697a8e4b2cb43932b17f09a024c3d1e44798c0c48a2f9364c532ed5aef0a30b0cee4bcff1941c331d25c0c7686e17b4c85111b8649b431d68cf569b066bbe8066552f2c7571897ce401c2f237ac1624be353aed25ceb217659d8b96eead1c3043256bbca6c528ac90149f4c9f9da3056c744f08b857984fd7786ba76bedee82e2a2e79bc126cceabe1d7a063991de0ee777cc5f22b835b993d04e93ff34779ffc8485639081659556072231ca8906567ee435543b14ab80a45a87e10ef8bf3d807c7b7944dcfc80382ccb58f4d9c04c0891d20e47e8ae3ebeb84d8c925bdeaad7c913572b78853b91103057d92007d2f4551c7d89ac1e9163cd7576bb9796ee8fabea518189f92c20262f7db81b291d588106a666ac2c66fa5b15c89eb7631d575d9110bc1e05ab0a1f8f18078101c6302a4d736dfa3de33a77bf53acd5dfbe1342b41efddffa52f40597b837719bbd9d5c7055f0a6f47f89817adca48679179da0087b7d8dbecdda1dcc901a2da3ddd777cffde59c1ef312010fa47548b2d863d180310bea3dbaf7002d3f3b527ceb76d8cc513819d9d8618e8592fb490a503e4463c21ae5c111dc089912e97a209255a7f5cfec652f2258961c8b79085541b2678d6da31d7af135292e5291a9501da4aff3bddb2249d4c7700b4880769dc67ef852232029a6162b0c36f30f4747d1f1a6945a0bc3c1a0eb64354a78ce7c883367bea39e26fcffbf0993538d9376eff4754f6ac742f7551d3b7b11388b2c27b210d0d808bff4f9b92211c2a13304f5e6919f2eb5ba8f2b497fccad8b911758e1c2507ff3e7f0024509a55eefcbb0a560ec117006bb9ffa4ed67293494c798a4aceb53ec6ca69d3f11e9afa8c87318d0c0696639a977e1f0778c7900c413b8b67208b44d0ecbf8d2115cd52c52aa7d7fada2227369576832a8e4aa8f850543b331f395d52463f16fa0d50c07ae6999000930660fb3db9d13af1f860096e12ef4582b5f8d961e89ced02d909280c211d4870257c11299f498a6d34d24219e780c09115368b8a96cdb62b57899e7262ae7a69b5b99b7acc03e8521bcef49747a44f49cf70622288f210295c2d27e2a19354515f218641cb79d78d7d52713000eac7d6091a74ef6f80570186d3bdda8b409488f8f31cadee2fb225615d455a53c67ca1dbe607178c50fa23b6ac0e16c1bbe14d28a39dfca53ffcd79f2ee386e2b5a8ee84b0186bbedad6274a9cfd101121b4086547a9c8e3e97d0381d2d5d2353153a92e7bacf28c266f2594ef6c2611e13c3100389e1df66ede344fff47ad74c7cab91a562e52a2a1de2d7bc75ca8202b28376273244ffaf231fb9e16da51675e4a4798e77bf7a74cce16cd50ee9f0e54a127d2d417b63e10ac4b7c6b63f3f9d129038b474f3bbdb823087ddc2f0d6b3adf457851131f2007e1cc5865187224f1a1fb5a74523c59d55e1abf6b90690d7974f4626e325c4f701af20c894c8d1876f9d206f10250b436952440a84e54ee45c609f94f007b9e2ea5a806aaa2969262efb94e67cc246d4436097f262044019e0e4b3c0eb96b3372ed34b48c104c47d2d3623305d5fab2a50225017d2959ff9694c7a5a542a19f583d0ed7d6f2ac446ad30416c23c8a28e3bb2d94fc4da5c42b66e4e85071063d094baa942d05bb1ae0c37629bc6b9b20855903a6b73afc31be8998c9769e7cfc88a09fe45aab322172914e16071245f141ac1ba6466fc1bc888b760b05da93fa334cfd7442dd27cd30295550df94e73e2c8708f19d851af9945dc45e42f4543f9cac4ca9e80a2a3c51f41a0bf55d5d03781b0f2361092221c7f7982c4ce306e5d71c1a6ffed900ed0f31f599990c9aa6522e128359d13e1b4abc444cf3ea3ea9a6f2d9a4f4ff51c4a8769da4e0625ec014310d6a71022ef9b71ec64b8e3f7ae91ce90acedbc587d3c5b98d415d2a9938fd99046ea6b7a086b138d79aeb97ba91a2053048bb1861ff5b89ab91378bf18c6a031a58c62105b965ea464e4eb056ad2f671690462e0714e99d561dad85c1ef0c69260993df4b1260b7ee1f11cede551038cc716259ac737a9aaa843b86811bbe4004cbffa38b937132bce1ec02ab973aa718929f3d0e5bb67ddcf0ce6d1930a43e1a3e79fbfb181dc2963f0163f88292ae56570b423ea959902127207052eba80f2fa85d81487949f9d7d684c809d452708a2444391542d154af2b1b380a79374504e6b1b526c3050d7074ccac463d60e8a015393d0af29ea8a6dc854f874ccebd1a3ec86d5ca906e455245651ecdd09a22b1ac302e70ae5fc87349266dde22a531a53ca21085e4911c3b748197bf9a0c1807a015bc3d394f5de94d090b06cf08b1793a89662ba0d4835add6e466c56c735e9139ac00693624f3dbe4ec1fc3bd3e19682f5d75564154e55dddbe4a06ee65abab6f319dea27e7a1b0bf679b367e2a29014182e251682d9617f9cfa463a0a0c0f653b59b6525f99c11da07a3230d492f291e2457c5db90c05c0b7f69d9e05da18d3d48441933ce56f3a136a2fe21b72b50208c3b2dba63124c200a0cbe65c880505577dcfcf67f76466f8378f873e80fc3404c2f513f182d81e94c86428b0f33b4a45f2916ce6a2998a10cbc3145682d87426141fb4ab6f7f17bba159fdfb135acfd30d3218a5fd639cbc31c61e16f07839c78d315adb71832869b7a2ad67fff394fb2f33701f5d3f5f716be54ce5c2ac388d68447fb2f96478345724d446e0d4a97d9ba3a5624eb0f638d2a627f23106ab6518cba364e89bdd5a08c1ebea792617985ea3d481665503ee697fb8b40295d3751a6a15e710193b9cf1dd32230170a360b0e22f292b876606113d449fe180b2ead206cccf57e60354cc7d7376f27b39ce21324ef16bc09843f00b516382200567f0be95b34517262d4cd4e89c364e1f6c1917ccf31bf404672711638cd17a97f0f517714d4feeb91d2f8c709b4c4cf3d8c760355700e6125959a1c8d171e3a86c2cb5798f08fba5968bad233aafac364ebb2d6c97f6933e25eb623ae5c0def1ef42877f541ae8cf843d6c9ab630e0564a6670b68fcd21b3970851c33a80e0db49d208a50919d20ac5bd18b69006108c3dc3d8b2bddb35ed4727ba260f0115b131d90fa93495810a8ef4511947bc83a60dd86a6f9af45c2cc8ba526963424ec2985479a41f00ce56e92f611c701343bdc509d77610be6ea114a71b1b52e27c2fd2b5e6d988c4d4fda59a63a56066e3058f0b8fc87cc4eba06121588d69b190ed8e124521ecfad13f8469b60820e2e4ccf3187c27d58e613d3fbb9b7b3230024793f04722fced6592f899ba04c0410fe1fea1b98ce1999b346202d215a80101790539dd95717219cd8da7ebb78af873b38103559a71a77d218b3aab3f73b1b2381dc08353d67fefe08ade38da6e1c28d14fad8ce792008aa06f4e4c7535e", - "public_inputs_hex": "0x2ce3aa0626c5cf6359c69f607b5ca3149dc73e9b022e333ccaf22730a09d3fd702d799cf98e79aff23abefe11513495038ec8953dfcf4548422ce68dff572aaf0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000f664ef83cbc214b6485c19a70e17f8df000000000000000000000000000000005d9b229a79d0f717361c6cd1574153e60c9c41697310eb57eee62b606a492a01cfe53ae9bd5f2ede0616dd25ec4bf1082bd2a61584d2ae0287a98d2b3e3f3cdeb3b3582244367a14fe46c733423f21792e6d2b6cdcf0542f58fc2cbf0103f2c332c99654fa3151cc1cf346bbb0ce499d1f7cce37859a677eeb7d36d8fe2112e0b1f445605ee4bd6626931d8993fa30dd22cfc2030bdfe1d0c50b8fd63d2fb2262054debb3b672771fe31a0e8c6d92d442b34773371ad4adf21175fc1626e47693709df3d9dbc25a452e28c000523b61a" - }, - "decryption_aggregator": { - "proof_hex": "0x0000000000000000000000000000000000000000000000060fbf86699f40ed8800000000000000000000000000000000000000000000000a30f4d2fdb3067af3000000000000000000000000000000000000000000000009937586deb824df120000000000000000000000000000000000000000000000000001403796c2b817000000000000000000000000000000000000000000000000e44ee329911eea6a00000000000000000000000000000000000000000000000af565dc090918333e000000000000000000000000000000000000000000000005ad30cd59e713023800000000000000000000000000000000000000000000000000010922f18bf64a000000000000000000000000000000000000000000000000db033559f53a86e3000000000000000000000000000000000000000000000001b3d9d630fb02270b000000000000000000000000000000000000000000000005740d616fa3ed2b2600000000000000000000000000000000000000000000000000006485c9fc331100000000000000000000000000000000000000000000000c47dd961b922b653100000000000000000000000000000000000000000000000a2d66858576412dc500000000000000000000000000000000000000000000000d4ab986bdea2183e90000000000000000000000000000000000000000000000000002896050f026752e685e4d103a3f1f6cfbb11ce43377147ec6ecdbe1bb1aa2422fe3f626ed5d461a28dcdda31ad9f5490bcd5a29fd2a1c6d29f706c6123a1fb2021178b97027d71eea4ba7b21b1b1eaf72da90fe2cfa805f410cf0f49c4540a3629e3d0032aab80433f0af5f08931d494aa5b55f37647e91b81b1a4716fbe0b3fed37fffea44b701ec5018f873be24317d4a0e306f8e49807432a97aa2b1be47086597de726b031d850133136bfc9ed32d72c2faa8b2b007f417cafc1a081a8a98e5d3c29179ad20028693e01d353fe5b9a910a388281252b72d22cc39191a315865725df9c3921edecb02bcfee7d961eab7498e59594516b590313991c89b614a9721d5aed33a040605a1af9ef8edcb1be3a75f12dcc959cf6b034c9fd0a72f62a95843b046c621c034706b2a32dff39c582eb30b968f8d6686cd79bf262a0bd6b910bfa5a07b055c9cf093c46ba97a7ee7c68f7f6ae401294aba6178379d41363386b7ba77e6265de321c6de817d02beb9ec24b259c00e4d69685dbd855ad0b0929a6a6abc9f0419d3cbb169495dbbbd4571285a3134dd3e8eed44f4ae86915e1e7751b2875f07f00183f0c70691a5f945590ba496633096baa75aa72acffc960b7e0f994ff62125cafce256f4c61763371f56f58eaedbfdbc2b6308dda23dd4067b456e518329e542d89c3f3c1faf03aa9681a0aad9d82005e53347cd8671688af297f706771daef1f6a6b98cb4444dbf449237b2d88626232765f2d471e84bf6ee3b08aae81c7cb06d2272db499f616f0ce3fa5a1b3af0272508e3e6fad3ba82b5bb86d4b3132b3b5320eb22f23a79fcea83d5f7f41835e02576c683a7781c1fa8136b40e82d8454e1bd6a7d1f51a780bf35eace93d0224fee4f844ab53ce93e8ca78516160f6ece6d94996061c2e982a24240d7ec5aefa15a8e54bce54102a2fbac18acfd295862e4590603cac9b242f4c858f91413125bb3ee00f1dd3ec2668260f006ae1c2fbde69ccfd76f7ecc1b26ca741f7a182e7c53547adac1251b7a7892c6dc872ae895bddd09323fc6e46400cdea469e733c0bb4068d36274fe42f460a06b15c2fbe488ab0f181e73966f18c7653d337f244f5dc873cc2065040c92324883054198306681fb918fce8641f289ba3052ea3aa1ebaf5c8e0d4ba4a06857b23abd1136e8d798c2d0d37a3a1397deb21f26182b95e39f9eb5f395e52ed85ca4eeaf527e96d627793123d61fd126869c5a15ffd578f75d1512e2d78a6724e0f88dcbf13530fe9bce872d44d9b8626c59cfd4b84401cbc070757ea3a123a199545c4b40c5497f42c35c932a19d95c89c79fa45d669c64537b159ed971cc37886b87aab032cb5db7543ed932b56ebe0303b1ce39ba93cd2253f0dc15730bb89350c7e180c9d3397905b2c8385e0d56ea58662d04e8b6b60430c3dfc231cbff85ea2442a0eb846070cbf9b6a67513d13da5fb603bb94b5fee7d5ef993349afc6ba99a3cd10149b5a20de41909768298b601e6690d796eebf1dbe1e333cdebe53116a76932f8fc6e7cf56278a464df7a01ea51d18f374cd93a0928376ff9bfe60f8c200851578b6f4c8096902d8bfc98266fe142a2ee3430584f970fed0f9bf4f2642cab51d78bf7ef9e63bbae8e9a8db9e8d1c55a04704d5ae31661ea09801287eddbc400c42f8d7aef288d09c121054a7db59ea8cfa5c97315afbf8fb7eb5a6f2f380681e0dbf4d620663e3641739153c791a24467b77117724ba24825fcdd76c8f769617ffbef48186983d2561e883fb494a367f4228237499b82d1dfb0680d74867a80f67f5a2997b66a3913252062b81c4adda4e180bf81e1fede0676e0a2ed2ed8d08365fd51552668232a64e43d63efcd918ac3cc26c0c64831759b26ea5fa71ba0a9c43b97866fbea5ee2e04cd9bcdec085942e2528092dfb734d543a6c8d7a3528fa85ce8cbf3ccaee13f349afd9992464f7e62387f4aa94cab21dbe35f35eb81ef5ddf272f467ee55b2a8e3c8c4f51f98564cf678e55bb5482eb727d41008fc27cd624a38b439cf3320767b9f81b93b02290b68025ffe7aa732cb89651d1c1517a6c17c6c56c3cf3b6b68816c8bab791e485295c6c420132d5d76064ea1dd511af8af841ae645b528177897379879bf3826aaf3fc82f6f786d33fc1440839260d952d944398d33929476d4c9c3bc05289570fecef98a6be31049e491f1281fa0738a3d6be18668ea452fdb936536029cf6aca24c869f4478bc130f7dfee5cec0fa41c1927e03ffda49e4a5f75cf58455d74bf7143173dd9988663b59b7156d5127b01e954f45ec35cf71fa511cb4598852f07f2f6394d5347d267ee2edc7e8a119753001fb573f3be4ee8e8f959292c5fda1226a2d1da77d201ca3cacfda30a03efafd2c6b90d82ce3c49e01da5fae881a25a18826d8f3b814c3a116a5fe14d019cccc623ce8a11399e476a37915f9f2f72fc20858f59656e4d46f8225cf4c22e89958655a4e5c8c8c25b5ff724540ef22ec566efeacd780ae36e79ae0ff36502fc05e2e5fa559b00d83957d720eca3e857585d11fa6659ff0cfb9127ede629173ba3df37fa2844e086c9f48873f9aaad2ff088ed575b75933dbd633fd6b21a23f404775a4c0b8d130655158b2cad89340586771ab167f55c66d93574e7a8252375ec1fe293c7999dc55343a601ba45e4d81033076ea2abed2bee99564ec20d0839129e222fc8059809c1498e1f004dd9af09eb9d7f63df8370570e99b50e0d2860b6d584172e13fe8823432411e0b4fc2016f38bc2cbcdfac61aaed4af70ff290a594474af723b14fb50aa411232c388d1127f925df337dd94686a5be48cd32627d5093fe2c9d1281ba18209c95a69e3cddb51935adbe961274f7c27bd4a6b0abf90c213e7483f1f959c3ce0766ee84b96f1eb1401cbac8f42ab71ff2c8c6d0d14dbd3abe19e6bed45067e94630cef2cfe454b65dd3619c0116984536c563e10e9756fe592dd8fd645aa6d2b93ebb06cf10de48ac272a215303eac8792d7da1956c70500663f705e4b5d2a5e6f3664f881504dced400c842dd901cd182e5131d3be695c2cdee2176672596c83a8b1a4bcf11d5ccc8c579cff5ca482b8bd4e31095d0d8f07df0e65dfff78692fc9802b68ffa0c7940747e43a8129c2cafa72f19cd83a7f4bdd7de4541550d075a62469d6c13fe151adb12eaeb4aa0f2429b1a305fe3658aee5a538ddab4a651979d31725c380bec91db7c768192033325958818a0f7a4cd4833d3b72179510b735e197d49a71b00efaf1f297ae3df08933f0128efda5b94cc635dbf32d88143194bb630fa61b67b3d52f7abfe4fb43d17b20c03590d5e7b6840f32e9130e461d2265396dffb15225b43de936fd469e419c4a012016ac388364b45731b97050013129ccf7ccd1506bd5397c622ba31ef26bb3f1e2e62ad96e6d542e6b548968118fb672bcc39ceb6c9a30e27df82878ff2e50e183f9eff1d2589ce9cf40d3eced8daf69d989af7bb42e2eccd66dfd3f091f0db20e51f5d6e56bd06a8323fd2f926ce215655e79d044cf96fd4c7dad4d3294e51193af3f58677014368cbee87398d59a04249f217b10a264927fde95269a21bd9254b953c957453d68593e396ce2e93f6e51a128da4729cf39ba5d377306f20ef124e77be5e642a5592b1bbc13cd793bb5a9d97ad076dc900b6eec3110555e07e1c7ce43e07ef52123bc96bb98e55f9cda1c10f4fe1716a59ebb23441fce942f02559dbad8b5cd566b55e55778e52dcffcbc7e5e3f2977ca5a37fbb561e5769e30564f17420b147d4551a70853129294fcaf62b5fccc87cd681d8f2402c8128712c9bc29a6ed8f61b5437189e9de21d171508769f58fd65c4084574f46509894329e392d2743104ca97ab9ff01b553046f6840592aadbb76cfac70078654884741fd8400b8c1de1e56fc630708ceafee772e8b9cf3ca3566644c0dc932157871824e06c055e14e3c6decb9741c9c59e8b0bcea4321575ec9d35c89b1f91cf26690384ea0f51a2623f4447d7bf2ec728d1d53b9c046f1901295c73dd490e31abf429d572d80082503137d75bb420bcbf0cc62de78037998ad9ebb5ef006ee6e5be1fe507b7cc26c2864c45e37e64cb709c3485d9c930c524990d8339ae302d6dd50dea2cea9d4b26741eed15e89d7ba41387104b61ee3d068f3e2a2d4743a48ab62b9fd12592037bdda46e3fb3744dea8326f335ee66f7f3d82b339514b7e85ede1bf6f0f11a740f53535add715596a3d7f188ae24c738788f06e57ed631233cbe2ad85dd44fcd6556487f38a03c6f44f6e803afb13260ecdfe64688adfbfc42e50dbccd2b69b12fe4ca91cbec2ab388c40e21ddf1b4bb1c191509de9c9306645d1e4080e643a64722772be374c6ac1527ffb147d49fc5acdbe1257dc83ab9b52c0e64973fc0abcb722f8c1d1006f4ec8de3bed6e03ee035fc7757537918e4296504446b42f1c334a580f2347efb6752c4b667fb3a86714c991b312cd843a4fed019d0a3ff44a20bcd721de96add28f01e25e1dfcd9d9b1fe315c37c02cfc073170ccf049477a4db0a888960220bc083d818ff21db35b1221d2eeb638165c7ac3418ab18e57698a1d965849e6d523920f15e0cfb8eb36a46565e13f9e05526f13e01dd7d38507837dbbee7ffcc3f3df55f0f18848f95b4fcfd9ea9ceff5ba94966247e68478bc06c14a0bc0157fdfb444aa9ddf93528b57c157d2e5f54e7d6b1b0300ab24a0330de049aa4f313fe5845345aa9aa7b098bf77ba28086497721a5341aaec8d792edf5bba881f3afcf21aeac0792c38e28520ded17ba1581db9312cb12acd65dad53443712e75326b20a980a099ff300b0b54b74a4bd8e2736ad7bae1de0f3adc92c0304f5bd018fc1509deca5507bf24f4133445f46712ec3f582f30868d0cbc35162734e9ff01615a8f313c131114be4bc3fa46f006a6a7c23587e23649c562487742c50ebe4761d05d5c2a5e70111032440a5e9ad32c7d2eb60760e2d73a9b278ced5cd1a4200bfd5b97291cdf29665ea9dba5e0bda1d6fcbe8a81547ddd5ed53966ca509c934bec3b79ccf1091c0b217e4d74598da2e6d8fcaa21376cf7443fc8dbb9ed63c28933431430c48cda256d9f56c238839b6538709ea0480035e7e37b57c625bf18db4b34117f55272be50e4dae872723cc3a320fb7a04fdfd10c756395f6cd9db95a6a35ad68bfbc5807a3e0f8c697485a2c730940e0031b19417e64a17915204e654c12d7044124536249c46839598e6320a4bbf0d29aa05759ea321b9fcfab0811363654bd142bf48888a30612c00dbb3092598142a1c5316924bd451d3ec09e58eda50ed5f2a16bfa7e550a5d77e38841afff3aa0efba292747920208d097ca4976b8fcc766ca037bd37cdd81957c2017755ad350f1ca3c6ee7dbbfcc37a6993e092ec54023b47bdff503202cfa2143c783048400317ed6dca8fdf6aa9f10c7a238ba0bbe40af0f71f9fe82debf0017d998653ea1aeebb3605c4874bb12b95cf591875e36667e920b548e8b2ceb98a7414c3a09203b971991a24e7d616cca52f25edae46dfa875ac167603b1879bd9361884e71d089e3b3e3253618e872c23f9366bed3dfbc00c03fc3532b0cf848a75059eec5308dd51b2ce882a26a6a94ca2640ae57f589b8d80e4aa4045b38d13552d2ee8da1b430a995a0273ef88d6fbd68bb447669bdf2ebb2bbc8a5da33b5502ba9f0ace0d9ff925eacb7003bc0c0e9b50574c58967a733c35168110ece84411651d38e31934fdd99db98fba9e78895783c824a2c2afa4e639da8d63269a9981a2bde5991894826dc91b5ffaf01e982f2015020a1ebcb4c935cfa70bec96e7dc1033f7380402421ee95ac2e82b414cd93052d129418baa9355bd88b6a6f1be20f705cbc7184c918b707c21f95e02b18e771e0019584c16f99ade4b2e942c89c6968cd8ec0d457583546f61d31cc69362036b64ff58e51a4503f45526400bd78933612ade176ff9a731e39bd6c06fcae8fea626b084c8448ff5f4907af0ff855c7949791d1ea1185322653f643318b95023475cff51fa3e891aa1217f25b0c49e13fb35c10fc3102f1d0fabc12880f866b08e9f0f52c1d566313d0a3f9afdcead76ed7474116c404db3062474126f4ee23bb77511149f59a30fad9e64b0bd1302366abafb2aef121fb9973a055a08ec3a05f508a061e7b48c0f3f0cdc83e6dfa22cc869891809b156a1855da5f9e266476f629cea525cd567920e3d7e7d8ebe1d6e6fee550807f99e0b9067a68d892a0def92e9c036f307938741e49f7c82a535b48abc9c13e647edebd5ebc64b7521cf707b7027ff94a8ccb12b5fcf8ae286e0b11034e42f67a06cdd4cf532950ee7d07b039e79b719e4eafa3af83f8fdbd4fa1758efba02cc4641ee515aa588281c060f45928bdc4fd77f18a5eea79819c15037a0fdba2549f033866efd35100b02c492b162613772b7dc06a0c0280ddbddf1c07d507c2723746a81ad2130e3b55869272def6fdb8065b7f676f83a90e7ec13fa6212102e71e16c286c708139cbdbf9baca366e02a7f8158d91976ff84686dbb0cb015f1d3f1c99d0d54180c1917a4092fa220ac7b8b66ab2be93b36df92503bad0c042165a5f327380740848ff65b1937355bd0e70baa0c409aca29b4b2769c66590d1232836f7e81a33682b487e4355ef74fc5cb7126ecf82adc070c418ef8cd07cef0fbd50ef4024a91d3055d7e10ab5722a919296734771807383ea004af034243028204681a24225a9e5d3c0ec6c744c0ee1e50b386af05ac03faa0498fe1ba667168c072ca79ba12401e29f00ab35edd9bebe08ed68f05fd7d798e101b6f99427268e29f66fbb4c24abd9205b54744084a0f53835c9d25fab4dc586f76a32ea52261b8e385d5fad02bdbb6037867a8dec048286cac202083620c8a80dabecb7691d013bf25d369e7252df82e8f620e2f991e9c950057b05078338b75ff4b57e2f069466cbbf0886716494576deb9b778a20d4b3c18c0a5ac5c6dc08ba6c027f46118a63c455b09bd85ff53ce9dd20fa4b603abaa97e3e32a419ea7ba7d66758731902dd76c53e234fa38cf4b3679513481fe26aeb21b6a61db724b7d309c286f50bbf0f431b02827f3811395c1eb997f3805969a29da7f49b34f56f8719e719a823f91a82b30d410e9cbb1be9fca07e63cd913330244816493c4c6261dc0a1d57016cbf646a96d06c022a3fab1a7ab77c8cb90ce232a81aaaaacbb05cd9ffe9721a8eb146ea2a76275bd94d95542b8f38b5609e15bdc55182cc90ca86d52c22cd0dd1408d8fd3721191436f54b8d1fbf6f14ed3919db10f9bf7a7e5b18a8dcfcc0e97c01d8ed6a8f502fd0873f8a97977950338cca761b115db4b35621cad5a520b17c7ffc4d378aaf2f01020aa2e31182f097c1004284fb4c0a6ebc43108f3011006c345bcc505bcb5c5fcf21f584733fae1ca115939d29076b51c0f27a217390c54a335e6ad690c23e06f0a454f30d49b591a0ad044f5e26ae87b36420f8e402dba6f63f9674bc81692152be81c18acbf3e9ef95ac616448a91a60eaf30a49b0c68a47dfcb199de36f0ab560d4f8a289dc9e89504f1923ee7b54977eee68f81148faf462dfc2b954273690025a301cb5e5b5d4d1671caf22b96bb21ea117edf1079f0cde55a935f5affb721a8a576bd23dcb8beb838f96d7608e9205f565cd902559b893204749748edef9d257821a92f959d4a0034ae8a297360f1ceac976723f34f4a4aff6e476ebc7b4862f9fe83c9cd766890bd99704e09fde2858914f30db5f7db7c8cc0627a0d2f61401ef4ead2aeacf2381622ef53fa0aadb67519772f196ea3ad53c32aa5bd848f9b08d77006c54e44f54e032b92206dde013abd1524c8aec63a89a4f69384e91495adee716a9ad3c7c0bc41ba7cf770736dc558051d9bad33e9805c9bc031cb9f8b5b6286c37501a5c1a78539f02c784536145a0b145d5a893d5437043cd005c404c7b9a4f3f775c2850829bcbb7f0a0bccdbebca2d1e423f00e747e3f59a76bf7dff1283eac06cb95239650b0e53d985f23e1d841c058339b1c731a507224916e3f4770a8d8fc28c62c5670331bee8e27e390fd519000715fbaa39f2e41967ea58039b6fd53b21205bcb8f4e2b278bab4df75755204eee084e98d16a33b175369f71a51733e625c0f9204cbe3191ab007a8ace7a2f814f68e71377a83dd0547d8dd85a1511f2ae8c81883e1327d5fa8c5d0c1a23027eb270086511e40bbdec3d94c9639e6b4309cef26da95dde9c89ff6ea04b062f0dfb33d6dd960e3946c5c1049bbceb2a90b77d3b6d9bf808fc42f2616958882412c154c511545efca7259198479401bf62a9a273563230efea26c4d58a7699115c4faebc98c573c7ce24abf01aeab9b9d1e516df3ab09bbd9d52c331cbdf570619a825ba6ed9c244dffab1e3169f1dc91026e32ecbe8d5b2883f8a408565fb032b9700bbc3c96f8a0f8f6dc235607a7bb7133a1a7bc7293e64e097a18479330bd811efca59ce5669487c7b8c5faa943755612d7d7e202c3895880ac3b235c311097bae1e758da51bfc8d8d0fe935f81cd6a093db59fd418cb72dd67a8f99e724a7d88890ea240480277ab67e4e810cb2131dd5c22db13d30acd4158174e865259ad9255b3b6294160f6944a62fee12e8b980af815c95ccf2f3ea84ed74025414199c56a55d9c7604ccf3abffe38b50a358e26eee05c28afde97b0aefb4a33806920db5ae7b60c40de7856e74b41832aba80eec743c9a39489107ac3785b4b100e260e8c2dffb024f06b125a66cec842ed17ab46d1d5aeb2a8fa5766dd787ff02f40060b89e6f98a939fe7bec24a337f3c4ac6e7f3856a7f4cffb362eaa251801390463ba2016649e1f1d5f72e71247fbc34c9e4c4c215d61828a540a2fdf1c1b33624fd91cf4f0cd99ab307447040a1bd00007562440e6052773e4754f5a8a299dea73972473ed0dd4fe703862c160475b1750d121c5fc5fe00d73ad03910c1ca99d3e317444eefad830ea6b79ec13a50cce26efbd299a6be470eb1a5ed4211f8abc5018a8aebea72b4e9fbec39fcff1d6eac0a998aee72475e7407a53a6a307f6fd826604e3f1cdfaec869407b52362520f193163c2faa2f0f4b0e13364101f25c026f0da3d1dc9a16865b52f2c1ab8c70b272a514955e09703dd2e65999f2b0284074a8a2516ebce867f7ea94879c167f6eb9ff9f228bde711c90da8f1321dbed6c4875b9c50920e235cd337544fdab8b56ca1c11b5fcb200555c3c4ce62057fcfa04fe945c8275e91810f104c75e6bb93a6d327adbf08ea20747865eab31351ef871a1d16308dc64c114a14b69583c101aff27014fc8e6d4440241a7cac05d9442f08a14b77fafde02d1a2c6bc22be9c4045df57b3b18fca2f2e0a732d6245c97dfcced0a19de52d218c674be7357e068769bb7dae1b0c31a283a5932cf2603a99afc692f590da3fcee46de8f431a89f40bdf29bb2da90f1e19aa9c86762dfbf0c60fbdd4199e1efb4aa9cc40e803d05ef633b4a09fbb2e6eb72334cea21838ebb7f777df6cfd7b61a1e04fff48f9a1538df4945b3568258ba389e62b9d078c7162192641e8555b2296966643333389bf2b0b5f2d4337c11ed2e806ac2f09433dd34c731bf06429ecca72f8cceb4a6040fc15c6a1d5b4e6f88c51426d17077f935c5e466cb9ff9c329ab0154041463c147decfe028c7a9358adf97444c71613c324bf5799b9ddbb2072dfd46f63018cef40c9310d3d7ed2cc33ca208278158b56da384a028372a52b7b01b16e589bd5bc34a9d8ae73b16845c73d8102cf1c5d7b172d70ed89ae9d07376723ae230913785e1b83e5a53d5cc429fddf694e08100e0e024c2077d177f02dcdf99bcd3aa4b8faa36269267eaba93fafc8fc54148bd3c4f337773591fa715b56a3897caefb9f7d61275be0723eabf5585271d41e18e6d3cb48286dae638831406be26a3898598f5926e215e055495600bd28f908d76e7b0cb37bb5b7c19f63265ce0bfbe190ab87e8d7ff1294a6260545381ca05a5ce9af63809c9e3b8a668316a17be565e117e7d7ce1bab7df8ca5129998951a699df5f22e84c2490dec2c87d207e78ea416c63cf5f353bb93455d3bd613801ff6e9d60c894305c141bde5b2ea25855c531e1f497e7b60d3e58c09aa365aeb2917c540c3449aeaa6b8ced30971092f68a43eb1ef4609378c4e486e6a7297d6079eb3bca80fbdc130313610abeb7be9349d1a913a00788abb46de21cc86d83400c6577057dc10cd510149f3e2e67976add26a945478cd1cef5d01532a0cf12a05571767f8954dd4d6fe8cd206ba3a494db558314119b99258b18db932c4b66501d2425cc0e99ef9b8fc15e55df1dca4195cb83c3a538d602ef74a4b2c2a98960216d2142a2e3c710210c9ec27da3372b3188f97529f582a01f565f7067754200da8c8220fee260d17120d32b904006fb1c85b84ff532232e65aab56f106686b20fdd4f1cf88fc644df8fe261f7430eaf35d958f5e728ed39284493e9650a3a01367652d3e29659ef4290b5b2aca9d4d5880197a59012be51a39ef7f5f0019931d13d48a5a6400e63d1a1c07e301dff8e5ae640c227c6d06c32742c3148b89e512c1932885d0327fdabbaa5a39ce08a72da022d17f20b9dde1150afa1901723218321e776880505f5b81cc6625a1fa205f7b1067ec7f0d81449c59f3e79779a00169dbea15097b8aa92716140c39703058a6df25e24ee5f6288e7ec4b256896f0137cbf93dfa7f89e0abc185adac91f5b3089a2ed5f1119cf1a59a7729c79152056f517d69335a24cd124c32e602b5eca97ebb1919e964fd2523d24737a54c1a0f229442a2dbe9bdfcbfc3551a81761e472df1c663a005e2d0f791d704bcd63e08c34e38a7bf692f8affa5103366f6521eeea297a01503b492104472db10369525a49653b4f83f0d328d0869718c8741228d091a4dbb23783872f67e8b2e883a2466bfb29902325cece5c9dd171566528be02c06195196a3187cc58f5f31da41165c0a5af740de5f82da7b9657d856bc8d8f7ec85bfe475869cf9ef124422ea004baa144ad680e14307b673c353c1e8ecda20bb59a72d4915ce3070744be39b71e62424ab5805fe466b91b597159c7982dc0b5d6e42f5e332e054e39fa8b411209583324d86f259aa1762bcd1f9bd2bd55065ab68e08933d2c95d06923398a7a21c6063b3fb908e242344ba2d9ac403bb28d592474c4b7fda9864cedb1b2958d103a0b7711afab7cc061698610416dc015e8922d571aa592ee56ec27b767426d096384394ca6d88673d2bf3dcebd8a5940a1e0aa7043fc57177f0050b3b5c00c199ff187fbe802408ef25a8dcb6707063b88160c16e651669812ab9f3b3a4aa923d04c91a6392047e3c1eec765c4e4f2866e5f65af7e7ba40c2206713fa08c5318e20a767107d6022cb1259674095eabf21f08d3687b60a1ed7eff879051c29d0216e9cc829808ab1121c5f29ee63e983f5e141f7046fa2b50a5bba4146e0d992488d48d15b5b778435e3e4f8ed4161a3299bfe5d211286e904b16fb90797af31d656c305304f649ca0137b3b024d42ae6767558ebb1211693eaf3fa1994132e28116812d2d5836c4f557fc2c21ecc3b69ae524b7b1c987f2ed075ac1d064d1a05e9cd1caf6bf7352c8e7e863bb60414d69e45bd1636d632eddda9ca8a2a69be2120d69f73e42e5b2836db27e2d5fab6550068e1db9b2f7248df0ea999cb98610759f3d8a39bc496652271bb795770bde7d73f7af0e7dd97b314e0245f2d9a3406529d9881669eae80ee239bcafec8e7e9c8c69dc6f990877364518911f119410029c052fa8473b84dc70fe2180e8e9942db9adcdf27dc8cc9469e71ccab015c03c8f7b5bfe26cd58a41c0f9ecd624e8972137d2f5e153a4fbca5747379a7c472723305fe4454697b11c17c19f4adba3d696d442e292fca66891d339412580c5288add23cf9504b9a1436171afc828536bf8962a968f8504abef232aa409b11c0ab507c2b0ae464b36a442987e4ee2305135f7e80079a384229c95ecb55915902b168dd08d79806e7c49921adc235096056cf3ca67feb762683f8ee5dee635d6048dc41aa5c9729d4535513c310a694749c815d758081d65e841acac4780c1810c7dcaedda05e67e2123d92a32facecc807053b1cc7906e46d789b68431d8f50191167820aaa226839eb4185fa415fe287034cb9e1a5f8752ba93761dea5ba47203eb14dc8e83dc35300967820c83a7b6ef50ae1ea3698d93e1dd204fc93988719d3e3c06c97143874a6ca856c4863470f544682c088d0d81978d50e484b70340e4d4447356176d84b867cb179542297bd99d29082465269b6a2ef2c00a44c3b010c244126810be2805228ef44c3c23a31c52c486732e0c9fd8e7996ac1f2a1910d26c34e5a13eb0e2e38c3e75a03bb04ee034efc308217f29593e5183b2a6d91e94d0c8626d8b017b85c24f3bb7721a922f6508743026cf3295bf5aaeacc2741aeba0759d64cae6348940183d0acba06cbc60aa5a731c811a01634f3dfebdb91cb0f61816f10606ae72f492b694b005f0d74340d3b47594fa14e9d42ee6ee811be53a80def707f9e127130ba3bda7497706ec2d358896abc9506b19171b87d42fdf71082f1ab195a94c0e33b0a4a2c4c9472aebd7607a24c1fd5924c16d6e982a506fe6a43abee0d62f2c1a9e41326129201d368d52867c168b18d38417a02208a0c82a262162bf98770f849d3b3d2d7b38e6b0bc457ace7f24dc70b3aac272158d811e74b1231fdd6b3c05f3a5ecb728ff63d42d7257c0419a94f24071fe272bed7e3ad65efa0e4d97e72f4bf3f1c9022c27bd0ee542adf7be27d806c6ee772705a4918303bf230543cd6c5a6af62409c3a3dbaf58a95954a3850d29c052ac00487e56c628afe653ec4236585648129651685ff27794cfe0971bd613a11dae28b6da19e2c5f353552b7a0207ea9754d5f631383ac5fbbc492f4b732f2fa5f10ec2bc2e79480d1fc19930949c5f3cd9f5d25036d797c30967574e110d01f2c42000499d98a62ce0c632490d8ba4d1433c063d6b8a2e38f87ac7cf028f0c7d252c84e3c27c55bdf0d8f6c1c6c27bab2c9c3a4e2261991a1b33d7cbc9e81bdcad2c1ce50ece47ce52ca60b0c73423befd41138161dec7a42bd21913877aa57cc029009966be72eb5690adc4a8b01ce9505d30b5b724c666b9cf6d9b84a6f8810e219a8a338c704d0a2ccc80931b5dbe16aade261ff9131f19475633f45ca3776c11dec5c6bee7f8ce783e237a0a412f5ee4d7e668463abcf84548e7041f13540a0dba462ac361f099418c4876dd3014aa6c31e9925a985d302a978563897474702541fcf9135d254b0cd3504bcf516342e7590f2c867ee93d635d4d4a80c6a5942f08bc88d43652fa73dfd216e3fa519cb62a96c96f30d78e14eae6493534c9fb0381cdbb2a8f450da71a5adc96b30b262b8e90b253fd4ec2aa092d728c1ad03a24f3591dc427421ba451d03b5c52bf35c0c1cd4de84d6fb832960e0e3d0c3fd50354cf50873dd3233e552b8084c6f5dd9f5d8b9c2d6a6701b173e1e2b7260c5f30056c7b53884a2c5a659e061a22a1411631fe9bdb6d3609ecaaaa22da214de4159905473efa6d78ad59c7c0de63a30ebd7df30f6f4fb030450cdf42b38eac691b4b50376eab2cc67d2f14349f8a216a72a62da8192bb8335760824f9c9a441c2ce4ac7c9bf4e10ea62791a8a2453ed87812b75fa509223b9c4cf63cf2d1f1c70e4636f09a0e721e26d8ac68a1a5b540563b07b78aeda7c8be8bca2b87956c5022a0a8e676d0b951c0ce75d75c5a0fcbeedd746a7fbeafa9c23e1cf0bf56a0b029580d63411373e281c08686cbe8a50d12ef979633240d0f25cf84a3cb3c7903043b65799c7bade21dbd56f3dd5ac7984ee33a978dc468869fdf4b2dfbfb723424a763c3d16559034ada91cd7f6a0e4aba0913c21e6429235c01b51ade7bc9902b5356e422fcc4ab1287153cf14ffd5932b22d988f62696ca7f0af95e20d7cd910605dd18d56f89d807da70a8b56832eb0b88a86c0544cf5ca62896d5ff366af2ceef3146ffd89a84d95d09de8ba376d7a6c197b667ea01d90f91b15224a94a322adf84ad278e3a08a12fe797f4400964d4322e735d31c2c87d77b067e593d3e073a6e457ec20a6b95390cbba0bffa2f8765b8acaf27946b2f0651e8278cca0f19a0bf3813b872346c82492f62735a81d884914b4c6819e15dfc342b2293dcc72a11f1a5579bebd7e3e8af6702b2881be8096ec2e57cbce183ac58a2124ad58128a590203e76a0ccd6e66c173d160b756b25ae4ff1869da33b9c7393567707352fe2e7088b7c3950f6867ad639d0ef4a0cbe28ca461378d3dffe44639ada6ed22d7064bb81e36e71ea7eb36ac1e950709070e379f2ffd9521b0cb1483214ec4f0b047eb546c078df3655ec96ddd13cb3e5d2a67c3ac2f98e8ae52785736765361fccbbcb4136f7ffe1841b5c6e944f20396c4cbe32529f0954a3da6a1dd8cd9e07e6705137a71e41c85b8776be680bd02090ed401a880d7ce5b5be223baba50c17992c88e3abbcd6d2175313995c04dd3cebd70cd7a4f4a9f23368a0ede9f0280cb16fa6089ff1062762cbcc314ed1c235cbf0995f1f8bb0dfc4b6402f8ef52c", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e4412165f58bbdd51389e19f359b95755ee0c47120177439bc8ed45ec7a6eee7f42d00000000000000000000000000000000f664ef83cbc214b6485c19a70e17f8df000000000000000000000000000000005d9b229a79d0f717361c6cd1574153e6268b63ab57f71e917a79017400ac69404ad136220c9e12bec413a6d3d3007cfc000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000022bd2a61584d2ae0287a98d2b3e3f3cdeb3b3582244367a14fe46c733423f21792e6d2b6cdcf0542f58fc2cbf0103f2c332c99654fa3151cc1cf346bbb0ce499d1f7cce37859a677eeb7d36d8fe2112e0b1f445605ee4bd6626931d8993fa30dd22cfc2030bdfe1d0c50b8fd63d2fb2262054debb3b672771fe31a0e8c6d92d44000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - } -} diff --git a/circuits/benchmarks/results_secure_minimum/report.md b/circuits/benchmarks/results_secure_minimum/report.md deleted file mode 100644 index 78a2103791..0000000000 --- a/circuits/benchmarks/results_secure_minimum/report.md +++ /dev/null @@ -1,216 +0,0 @@ -# Interfold ZK Circuit Benchmarks - -**Generated:** 2026-06-11 11:21:27 UTC - -**Git Branch:** `update/committee` -**Git Commit:** `876ab64e1921c6e109fd9551435afe8f12b8c546` - -**Committee Size:** `H=2`, `N=3`, `T=1` - -## Run configuration - -Settings for this benchmark run (integration test + Nargo circuit benches on the same host). - -### Integration test (`test_trbfv_actor`) - -| Setting | Value | -| ----------------------------------------------------- | -------------------------------------------- | -| Benchmark mode | `secure` | -| BFV preset (artifacts) | `secure-8192` | -| BFV preset (enum) | `SecureThreshold8192` | -| λ (smudging / error) | 50 | -| Nodes spawned (builder) | 7 | -| Network model | `in_process_bus` | -| Testmode harness | true | -| `proof_aggregation_enabled` | true | -| `BENCHMARK_MULTITHREAD_JOBS` (max concurrent ZK jobs) | 13 | -| Rayon worker threads | 13 | -| CPU cores (host) | 14 | -| `dkg_fold_attestation_verifier` (EIP-712) | `0x7969c5eD335650692Bc04293B07F5BF2e7A673C0` | -| Verbose logging (`run_benchmarks.sh --verbose`) | false | - -### Hardware & software (Nargo / Barretenberg host) - -| | | -| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **CPU** | Apple M4 Pro | -| **CPU cores** | 14 | -| **RAM** | 48.00 GB | -| **OS** | Darwin | -| **Architecture** | arm64 | -| **Nargo** | nargo version = 1.0.0-beta.16 noirc version = 1.0.0-beta.16+2d46fca7203545cbbfb31a0d0328de6c10a8db95 (git version hash: 2d46fca7203545cbbfb31a0d0328de6c10a8db95, is dirty: false) | -| **Barretenberg** | 3.0.0-nightly.20260102 | - ---- - -## Audit status - -On-chain verify gas: **complete** (CRISP Π_user + Interfold Π_DKG / Π_dec replay). - ---- - -## Measurement methodology - -| Metric kind | Source | Meaning | Do **not** use for | -| -------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------- | -| **wall_clock** | `test_trbfv_actor` phase timers / HLC event span | End-to-end wait in the in-process test harness | Production WAN latency; per-node deployment cost | -| **isolated_nargo** | `benchmark_circuit.sh` per circuit | Single `bb prove` on oracle witness, one circuit at a time | Full protocol pipeline (different witness path) | -| **tracked_job_wall** | `MultithreadReport` per `ComputeRequest` | Wall time of each job on the shared Rayon pool (≤ `BENCHMARK_MULTITHREAD_JOBS` concurrent) | End-to-end time — **sums exceed wall clock** when jobs overlap | - -**Harness limits (integration):** all ciphernodes share one process and bus -(`network_model: in_process_bus`); sortition registers extra nodes; `testmode_*` enabled; proof -aggregation always enabled. Compare runs only with the same `benchmark_mode`, committee, -`BENCHMARK_MULTITHREAD_JOBS`, commit, and hardware. - ---- - -## Protocol Summary - -### Circuit Benchmarks (isolated Nargo + Barretenberg) - -Single-circuit `bb prove` on the benchmark oracle witness (not the integration actor pipeline). - -| Circuit | Constraints | Prove (s) | Verify (ms) | Proof (KB) | -| -------------------- | ----------- | --------- | ----------- | ---------- | -| C0 | 287764 | 1.44 | 26.23 | 15.88 | -| C1 | 2223151 | 9.08 | 26.70 | 15.88 | -| C2a | 1446348 | 5.16 | 29.68 | 15.88 | -| C2b | 2889001 | 9.65 | 26.32 | 15.88 | -| C3a | 3475239 | 10.80 | 26.51 | 15.88 | -| C3b | 3475239 | 10.80 | 26.51 | 15.88 | -| C4a | 1746067 | 5.48 | 25.81 | 15.88 | -| C4b | 1746067 | 5.48 | 25.81 | 15.88 | -| C5 | 754597 | 2.88 | 26.86 | 15.88 | -| user_data_encryption | 1688676 | 5.78 | 25.85 | 15.88 | -| C6 | 2977263 | 10.26 | 26.40 | 15.88 | -| C7 | 109424 | 0.50 | 26.41 | 15.88 | - -### Artifacts - -| Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | -| -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 10.69 KB | 0.38 KB | 3119554 | 175080 | 3294634 | -| Π_user | 15.88 KB | 0.12 KB | 2972808 | 193240 | 3166048 | -| Π_dec | 10.69 KB | 3.47 KB | 3640997 | 187272 | 3828269 | - -### Role / Phase / Activity - -| Role | Phase | Activity | Metric | Duration | Proof size | Bandwidth | -| --------------- | ----- | ----------------------------------------- | -------------- | -------- | ---------- | --------- | -| Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 591.11 s | 127.00 KB | 128.38 KB | -| Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 150.01 s | 10.69 KB | 11.06 KB | -| User | P3 | per user input | isolated_nargo | 11.00 s | 15.88 KB | 16.00 KB | -| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 10.26 s | 15.88 KB | 16.00 KB | -| Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 145.38 s | 10.69 KB | 14.16 KB | -| Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 49.25 s | 10.69 KB | 14.16 KB | - -_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **19.62 s** — not -comparable to P2 wall_clock row above._ - -## Integration test (`test_trbfv_actor`) - -### End-to-end phase timings (integration test) - -| Phase | Metric | Duration (s) | -| ------------------------------------------------------------------ | ------------ | ------------ | -| Starting trbfv actor test | `wall_clock` | 0.00 | -| Setup completed | `wall_clock` | 1.01 | -| Committee Setup Completed | `wall_clock` | 7.03 | -| Committee Finalization Complete | `wall_clock` | 0.00 | -| Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall) | `wall_clock` | 150.01 | -| ThresholdShares -> PublicKeyAggregated | `wall_clock` | 591.11 | -| E3Request -> PublicKeyAggregated | `wall_clock` | 591.62 | -| Application CT Gen | `wall_clock` | 0.28 | -| Running FHE Application | `wall_clock` | 0.00 | -| Aggregator P4: Aggregation pending -> PlaintextAggregated (wall) | `wall_clock` | 49.25 | -| Ciphertext published -> PlaintextAggregated | `wall_clock` | 145.38 | -| Entire Test | `wall_clock` | 745.33 | - -### Multithread job timings (`tracked_job_wall`) - -| Name | Avg (s) | Runs | Total (s) | -| ----------------------------- | ------- | ---- | --------- | -| CalculateDecryptionKey | 0.03 | 3 | 0.10 | -| CalculateDecryptionShare | 0.16 | 3 | 0.49 | -| CalculateThresholdDecryption | 0.16 | 1 | 0.16 | -| GenEsiSss | 0.09 | 3 | 0.26 | -| GenPkShareAndSkSss | 0.10 | 3 | 0.30 | -| NodeDkgFold/c2ab_fold | 15.96 | 3 | 47.88 | -| NodeDkgFold/c3a_fold | 93.87 | 3 | 281.60 | -| NodeDkgFold/c3ab_fold | 5.20 | 3 | 15.60 | -| NodeDkgFold/c3b_fold | 93.12 | 3 | 279.35 | -| NodeDkgFold/c4ab_fold | 5.40 | 3 | 16.21 | -| NodeDkgFold/node_fold | 12.26 | 3 | 36.77 | -| ZkDecryptedSharesAggregation | 2.74 | 1 | 2.74 | -| ZkDecryptionAggregation | 46.41 | 1 | 46.41 | -| ZkDkgAggregation | 5.36 | 1 | 5.36 | -| ZkDkgShareDecryption | 26.99 | 6 | 161.95 | -| ZkNodeDkgFold | 131.50 | 3 | 394.49 | -| ZkNodesFoldStep | 5.03 | 2 | 10.05 | -| ZkPkAggregation | 14.27 | 1 | 14.27 | -| ZkPkBfv | 3.48 | 3 | 10.45 | -| ZkPkGeneration | 111.12 | 3 | 333.35 | -| ZkShareComputation | 46.70 | 6 | 280.22 | -| ZkShareEncryption | 114.19 | 36 | 4110.81 | -| ZkThresholdShareDecryption | 94.55 | 3 | 283.65 | -| ZkVerifyShareDecryptionProofs | 0.08 | 3 | 0.25 | -| ZkVerifyShareProofs | 0.68 | 5 | 3.41 | - -Sum of tracked job wall time: **6336.15 s** — **not** end-to-end latency (jobs run in parallel up to -`BENCHMARK_MULTITHREAD_JOBS`). - -### NodeDkgFold sub-steps (`tracked_job_wall`, per fold prove) - -| Step | Avg (s) | Runs | Total (s) | -| --------- | ------- | ---- | --------- | -| c2ab_fold | 15.96 | 3 | 47.88 | -| c3a_fold | 93.87 | 3 | 281.60 | -| c3ab_fold | 5.20 | 3 | 15.60 | -| c3b_fold | 93.12 | 3 | 279.35 | -| c4ab_fold | 5.40 | 3 | 16.21 | -| node_fold | 12.26 | 3 | 36.77 | - -### Aggregation jobs (`tracked_job_wall`) - -| Operation | Avg (s) | Runs | Total (s) | -| ---------------------------- | ------- | ---- | --------- | -| ZkDecryptedSharesAggregation | 2.74 | 1 | 2.74 | -| ZkDecryptionAggregation | 46.41 | 1 | 46.41 | -| ZkDkgAggregation | 5.36 | 1 | 5.36 | -| ZkNodeDkgFold | 131.50 | 3 | 394.49 | -| ZkPkAggregation | 14.27 | 1 | 14.27 | - -Sum of aggregation job tracked time: **463.26 s** (parallel CPU work; not P1/P2 wall clock). - -### Folded on-chain artifacts (exported for Π_DKG / Π_dec gas) - -| Artifact | Proof (bytes) | Public inputs (bytes) | -| --------------------- | ------------- | --------------------- | -| dkg_aggregator | 10944 | 384 | -| decryption_aggregator | 10944 | 3552 | - -## Raw circuit benchmark JSON (Nargo) - -Source files for the **Circuit Benchmarks** table. Persist this directory with -`crisp_verify_gas.json` (and optional `integration_summary.json`) to regenerate the report without -re-running the integration test. - -| File | -| ----------------------------------------------------- | -| `config_default.json` | -| `dkg_e_sm_share_computation_default.json` | -| `dkg_pk_default.json` | -| `dkg_share_decryption_default.json` | -| `dkg_share_encryption_default.json` | -| `dkg_sk_share_computation_default.json` | -| `threshold_decrypted_shares_aggregation_default.json` | -| `threshold_pk_aggregation_default.json` | -| `threshold_pk_generation_default.json` | -| `threshold_share_decryption_default.json` | -| `threshold_user_data_encryption_ct0_default.json` | -| `threshold_user_data_encryption_ct1_default.json` | - -## Notes - -- All nodes are executed on the same machine in this benchmark run, so inter-node network latency is - effectively 0. diff --git a/circuits/benchmarks/scripts/generate_prover_toml.sh b/circuits/benchmarks/scripts/generate_prover_toml.sh index 3ace7bea4b..666d81dabc 100755 --- a/circuits/benchmarks/scripts/generate_prover_toml.sh +++ b/circuits/benchmarks/scripts/generate_prover_toml.sh @@ -61,12 +61,12 @@ get_zk_args() { echo "pk" return ;; - dkg/sk_share_computation) - echo "share-computation secret-key" + dkg/sk_share_computation_chunk) + echo "share-computation-chunk secret-key" return ;; - dkg/e_sm_share_computation) - echo "share-computation smudging-noise" + dkg/esm_share_computation_chunk) + echo "share-computation-chunk smudging-noise" return ;; dkg/share_encryption) diff --git a/circuits/benchmarks/scripts/generate_report.sh b/circuits/benchmarks/scripts/generate_report.sh index 2a41d7806e..3a35a0effa 100755 --- a/circuits/benchmarks/scripts/generate_report.sh +++ b/circuits/benchmarks/scripts/generate_report.sh @@ -624,8 +624,8 @@ EOF emit_circuit_row "C0" "/dkg/pk" emit_circuit_row "C1" "/threshold/pk_generation" -emit_circuit_row "C2a" "/dkg/sk_share_computation" -emit_circuit_row "C2b" "/dkg/e_sm_share_computation" +emit_circuit_row "C2a chunk (SK)" "/dkg/sk_share_computation_chunk" +emit_circuit_row "C2b chunk (ESM)" "/dkg/esm_share_computation_chunk" emit_circuit_row "C3a" "/dkg/share_encryption" emit_circuit_row "C3b" "/dkg/share_encryption" emit_circuit_row "C4a" "/dkg/share_decryption" @@ -647,7 +647,7 @@ artifact_metrics "Π_DKG" "/threshold/pk_aggregation" "$(verify_gas_for_artifact artifact_metrics "Π_user" "user_data_encryption" "$(verify_gas_for_artifact Π_user)" artifact_metrics "Π_dec" "/threshold/decrypted_shares_aggregation" "$(verify_gas_for_artifact Π_dec)" -p1=$(sum_phase_metrics "/dkg/pk /threshold/pk_generation /dkg/sk_share_computation /dkg/e_sm_share_computation /dkg/share_encryption /dkg/share_encryption /dkg/share_decryption /dkg/share_decryption /recursive_aggregation/c2ab_fold /recursive_aggregation/c3ab_fold /recursive_aggregation/c4ab_fold /recursive_aggregation/node_fold") +p1=$(sum_phase_metrics "/dkg/pk /threshold/pk_generation /dkg/sk_share_computation_chunk /dkg/esm_share_computation_chunk /dkg/share_encryption /dkg/share_encryption /dkg/share_decryption /dkg/share_decryption /recursive_aggregation/c2ab_chunk_fold /recursive_aggregation/c3ab_fold /recursive_aggregation/c4ab_fold /recursive_aggregation/node_fold") p2=$(sum_phase_metrics "/threshold/pk_aggregation") p3=$(sum_phase_metrics "/threshold/user_data_encryption_ct0 /threshold/user_data_encryption_ct1") p4n=$(sum_phase_metrics "/threshold/share_decryption") @@ -779,7 +779,7 @@ if [ -n "$INTEGRATION_BLOB" ]; then fold_rows=$( jq -r ' .operation_timings[]? - | select(.name | startswith("NodeDkgFold/")) + | select(.name | test("^(NodeDkgFold|ZkShareComputation)/")) | [.name, .avg_seconds, .runs, .total_seconds] | @tsv ' <<<"$INTEGRATION_BLOB" @@ -787,7 +787,7 @@ if [ -n "$INTEGRATION_BLOB" ]; then if [ -n "$fold_rows" ]; then { echo "" - echo "### NodeDkgFold sub-steps (\`tracked_job_wall\`, per fold prove)" + echo "### Operation sub-steps (\`tracked_job_wall\`)" echo "" echo "| Step | Avg (s) | Runs | Total (s) |" echo "|------|---------|------|-----------|" @@ -795,6 +795,7 @@ if [ -n "$INTEGRATION_BLOB" ]; then while IFS=$'\t' read -r name avgr runs tot; do [ -z "$name" ] && continue step="${name#NodeDkgFold/}" + step="${step#ZkShareComputation/}" echo "| $step | $(format_s "$avgr") | $runs | $(format_s "$tot") |" >> "$OUTPUT_FILE" done <<<"$fold_rows" fi diff --git a/circuits/bin/config/src/main.nr b/circuits/bin/config/src/main.nr index 5efb1d6b8f..214bd0bd1e 100644 --- a/circuits/bin/config/src/main.nr +++ b/circuits/bin/config/src/main.nr @@ -30,7 +30,7 @@ use lib::configs::secure::dkg::{ use lib::configs::secure::threshold::{ // Structural parameters - N as THRESHOLD_N, L as THRESHOLD_L, PARAMS_SEARCH_N, PARAMS_SEARCH_Z as N_CIPHERTEXTS, + N as THRESHOLD_N, L as THRESHOLD_L, PARAMS_SEARCH_Z as N_CIPHERTEXTS, // Moduli QIS as THRESHOLD_QIS, // Derived values @@ -376,15 +376,16 @@ fn verify_pk_generation_bounds() { /// /// The smudging noise bound is critical for threshold decryption security. /// It's computed as: e_sm_bound = 2^lambda * N_CIPHERTEXTS * (b_fresh + Q_MOD_T) -/// where b_fresh = N * e_norm + b_enc + N * b_e * sk_norm and lambda = 80. +/// where b_fresh = N * e_norm + b_enc + N * b_e * sk_norm and lambda = 50. /// -/// The 2^80 factor provides 80 bits of statistical security, ensuring that +/// The 2^50 factor provides 50 bits of statistical security, ensuring that /// decryption shares don't leak secret key information. fn verify_e_sm_bound() { let n: Field = THRESHOLD_N as Field; - let e_norm: Field = 20; + let committee_n: Field = N_PARTIES as Field; + let e_norm: Field = committee_n * 20; let b_e: Field = 20; - let sk_norm: Field = PARAMS_SEARCH_N as Field; + let sk_norm: Field = committee_n; let num_ciphertexts: Field = N_CIPHERTEXTS as Field; // b_fresh = N * e_norm + b_enc + N * b_e * sk_norm @@ -393,11 +394,11 @@ fn verify_e_sm_bound() { // b_c = N_CIPHERTEXTS * (b_fresh + Q_MOD_T) let b_c = num_ciphertexts * (b_fresh + THRESHOLD_Q_MOD_T); - // 2^60 - let two_pow_60: Field = 0x1000000000000000; + // 2^50 + let two_pow_50: Field = 0x4000000000000; - // e_sm_bound = 2^60 * b_c - let expected_e_sm_bound = two_pow_60 * b_c; + // e_sm_bound = 2^50 * b_c + let expected_e_sm_bound = two_pow_50 * b_c; assert(expected_e_sm_bound == PK_GENERATION_E_SM_BOUND, "PK_GENERATION_E_SM_BOUND mismatch"); } diff --git a/circuits/bin/dkg/Nargo.toml b/circuits/bin/dkg/Nargo.toml index 636381163a..5f27445506 100644 --- a/circuits/bin/dkg/Nargo.toml +++ b/circuits/bin/dkg/Nargo.toml @@ -3,7 +3,7 @@ members = [ "pk", "share_encryption", "share_decryption", - "sk_share_computation", - "e_sm_share_computation", + "sk_share_computation_chunk", + "esm_share_computation_chunk", + "dummy_c3_inner", ] - diff --git a/circuits/bin/dkg/dummy_c3_inner/Nargo.toml b/circuits/bin/dkg/dummy_c3_inner/Nargo.toml new file mode 100644 index 0000000000..cc07464feb --- /dev/null +++ b/circuits/bin/dkg/dummy_c3_inner/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "dummy_c3_inner" +type = "bin" +authors = ["Gnosis Guild / Interfold"] + +[dependencies] +lib = { path = "../../../lib" } diff --git a/circuits/bin/dkg/dummy_c3_inner/src/main.nr b/circuits/bin/dkg/dummy_c3_inner/src/main.nr new file mode 100644 index 0000000000..b524f97d21 --- /dev/null +++ b/circuits/bin/dkg/dummy_c3_inner/src/main.nr @@ -0,0 +1,26 @@ +// 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. + +//! Deliberately noncanonical compatible dummy inner circuit for the `c3_fold` adversarial tests. +//! +//! It exposes the exact 5-field public ABI of `share_encryption` — `[pk_commitment, +//! msg_commitment, party_idx, mod_idx, ct_commitment]` — so its proof is verifiable inside +//! `c3_fold`'s `verify_honk_proof` call, but its VK hash differs from the canonical +//! `share_encryption` VK hash. `c3_fold` must reject a proof minted under this VK. + +fn main( + expected_pk_commitment: pub Field, + expected_message_commitment: pub Field, + party_idx: pub u32, + mod_idx: pub u32, +) -> pub Field { + // No constraints beyond the public ABI shape; `ct_commitment` is the public return. + let _ = expected_pk_commitment; + let _ = expected_message_commitment; + let _ = party_idx; + let _ = mod_idx; + expected_message_commitment +} diff --git a/circuits/bin/dkg/e_sm_share_computation/README.md b/circuits/bin/dkg/e_sm_share_computation/README.md deleted file mode 100644 index 8a462b0ba4..0000000000 --- a/circuits/bin/dkg/e_sm_share_computation/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# `e_sm_share_computation` — C2b (share computation) - -Correct Threshold Smudging Noise Share Computation (**Circuit 2b**). Verifies the expected secret -commitment, checks secret consistency (`y[i][j][0] == e_sm[j][i]` per modulus), performs range -checks (`y` in `[0, q_j)`), and enforces Reed–Solomon parity using the preset `PARITY_MATRIX`. -Commits computed party shares for downstream aggregation. - -| | | -| --------- | ----------------------------------------------------------------------------------------- | -| **Core** | [`lib/src/core/dkg/share_computation.nr`](../../../lib/src/core/dkg/share_computation.nr) | -| **Index** | [Circuit package index](../../../README.md#circuit-package-index) | -| **Docs** | [Noir Circuits](../../../../docs/pages/noir-circuits.mdx) | diff --git a/circuits/bin/dkg/e_sm_share_computation/src/main.nr b/circuits/bin/dkg/e_sm_share_computation/src/main.nr deleted file mode 100644 index 49b28c143e..0000000000 --- a/circuits/bin/dkg/e_sm_share_computation/src/main.nr +++ /dev/null @@ -1,29 +0,0 @@ -// 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 lib::configs::default::dkg::{ - L_THRESHOLD, N, PARITY_MATRIX, SHARE_COMPUTATION_BIT_SHARE, SHARE_COMPUTATION_E_SM_BIT_SECRET, - SHARE_COMPUTATION_E_SM_CONFIGS, -}; -use lib::configs::default::{N_PARTIES, T}; -use lib::core::dkg::share_computation::SmudgingNoiseShareComputation; -use lib::math::polynomial::Polynomial; - -fn main( - expected_secret_commitment: pub Field, - e_sm_secret: [Polynomial; L_THRESHOLD], - y: [[[Field; N_PARTIES + 1]; L_THRESHOLD]; N], -) -> pub [[Field; L_THRESHOLD]; N_PARTIES] { - let share_computation_e_sm: SmudgingNoiseShareComputation = SmudgingNoiseShareComputation::new( - SHARE_COMPUTATION_E_SM_CONFIGS, - expected_secret_commitment, - e_sm_secret, - y, - PARITY_MATRIX, - ); - - share_computation_e_sm.execute() -} diff --git a/circuits/bin/dkg/esm_share_computation_chunk/Nargo.toml b/circuits/bin/dkg/esm_share_computation_chunk/Nargo.toml new file mode 100644 index 0000000000..9dad123143 --- /dev/null +++ b/circuits/bin/dkg/esm_share_computation_chunk/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "esm_share_computation_chunk" +type = "bin" +authors = ["Gnosis Guild / Interfold"] + +[dependencies] +lib = { path = "../../../lib" } diff --git a/circuits/bin/dkg/esm_share_computation_chunk/src/main.nr b/circuits/bin/dkg/esm_share_computation_chunk/src/main.nr new file mode 100644 index 0000000000..df41acf81f --- /dev/null +++ b/circuits/bin/dkg/esm_share_computation_chunk/src/main.nr @@ -0,0 +1,88 @@ +// 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 lib::configs::default::dkg::{ + L_THRESHOLD, N, PARITY_MATRIX, SHARE_COMPUTATION_BIT_SHARE, SHARE_COMPUTATION_CHUNK_SIZE, + SHARE_COMPUTATION_E_SM_BIT_SECRET, SHARE_COMPUTATION_E_SM_CONFIGS, SHARE_COMPUTATION_N_CHUNKS, +}; +use lib::configs::default::{N_PARTIES, T}; +use lib::core::dkg::share_computation::{check_range_bounds, verify_parity_check}; +use lib::math::commitments::{ + compute_sc_esm_secret_chunk_commitment, compute_sc_party_share_chunk_commitment, +}; +use lib::math::polynomial::Polynomial; + +pub global CHUNK_PUBLIC_LEN: u32 = 1 + (N_PARTIES * L_THRESHOLD); + +fn main( + chunk_idx: pub u32, + secret_chunk: [Polynomial; L_THRESHOLD], + y_chunk: [[[Field; N_PARTIES + 1]; L_THRESHOLD]; SHARE_COMPUTATION_CHUNK_SIZE], +) -> pub [Field; CHUNK_PUBLIC_LEN] { + assert(chunk_idx < SHARE_COMPUTATION_N_CHUNKS); + + for coeff_idx in 0..SHARE_COMPUTATION_CHUNK_SIZE { + for mod_idx in 0..L_THRESHOLD { + assert(y_chunk[coeff_idx][mod_idx][0] == secret_chunk[mod_idx].coefficients[coeff_idx]); + } + } + + check_range_bounds::( + SHARE_COMPUTATION_E_SM_CONFIGS.qis, + y_chunk, + ); + verify_parity_check::( + SHARE_COMPUTATION_E_SM_CONFIGS.qis, + PARITY_MATRIX, + y_chunk, + ); + + let mut out: [Field; CHUNK_PUBLIC_LEN] = [0; CHUNK_PUBLIC_LEN]; + let mut normalized: [Polynomial; L_THRESHOLD] = + [Polynomial::new([0; SHARE_COMPUTATION_CHUNK_SIZE]); L_THRESHOLD]; + for mod_idx in 0..L_THRESHOLD { + let q = SHARE_COMPUTATION_E_SM_CONFIGS.qis[mod_idx]; + let half = (q - 1) / 2; + let mut coefficients: [Field; SHARE_COMPUTATION_CHUNK_SIZE] = + [0; SHARE_COMPUTATION_CHUNK_SIZE]; + for i in 0..SHARE_COMPUTATION_CHUNK_SIZE { + let coefficient = secret_chunk[mod_idx].coefficients[i]; + let centered = if (coefficient as u64) > (half as u64) { + coefficient - q + } else { + coefficient + }; + coefficients[SHARE_COMPUTATION_CHUNK_SIZE - 1 - i] = centered; + } + normalized[mod_idx] = Polynomial::new(coefficients); + } + + out[0] = compute_sc_esm_secret_chunk_commitment::( + chunk_idx, + N, + normalized, + ); + + for party_idx in 0..N_PARTIES { + for mod_idx in 0..L_THRESHOLD { + let mut reversed_share: [Field; SHARE_COMPUTATION_CHUNK_SIZE] = + [0; SHARE_COMPUTATION_CHUNK_SIZE]; + for i in 0..SHARE_COMPUTATION_CHUNK_SIZE { + reversed_share[SHARE_COMPUTATION_CHUNK_SIZE - 1 - i] = + y_chunk[i][mod_idx][party_idx + 1]; + } + out[1 + party_idx * L_THRESHOLD + mod_idx] = compute_sc_party_share_chunk_commitment::( + party_idx, + mod_idx, + chunk_idx, + N, + Polynomial::new(reversed_share), + ); + } + } + + out +} diff --git a/circuits/bin/dkg/share_decryption/src/main.nr b/circuits/bin/dkg/share_decryption/src/main.nr index 76b85484fb..3d94616e5c 100644 --- a/circuits/bin/dkg/share_decryption/src/main.nr +++ b/circuits/bin/dkg/share_decryption/src/main.nr @@ -5,7 +5,8 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use lib::configs::default::dkg::{ - L_THRESHOLD, N, QIS_THRESHOLD, SHARE_DECRYPTION_BIT_AGG, SHARE_DECRYPTION_BIT_MSG, + L_THRESHOLD, N, QIS_THRESHOLD, SHARE_COMPUTATION_CHUNK_SIZE, SHARE_COMPUTATION_N_CHUNKS, + SHARE_DECRYPTION_BIT_AGG, SHARE_DECRYPTION_BIT_MSG, }; use lib::configs::default::H; use lib::core::dkg::share_decryption::ShareDecryption; @@ -14,9 +15,10 @@ use lib::math::polynomial::Polynomial; fn main( expected_commitments: pub [[Field; L_THRESHOLD]; H], decrypted_shares: [[Polynomial; L_THRESHOLD]; H], + recipient_party_idx: u32, ) -> pub Field { - let share_decryption: ShareDecryption = - ShareDecryption::new(expected_commitments, decrypted_shares); + let share_decryption: ShareDecryption = + ShareDecryption::new(expected_commitments, decrypted_shares, recipient_party_idx); share_decryption.execute(QIS_THRESHOLD) } diff --git a/circuits/bin/dkg/share_encryption/src/main.nr b/circuits/bin/dkg/share_encryption/src/main.nr index 846f9d32a3..e4149d892c 100644 --- a/circuits/bin/dkg/share_encryption/src/main.nr +++ b/circuits/bin/dkg/share_encryption/src/main.nr @@ -5,10 +5,11 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use lib::configs::default::dkg::{ - L, N, SHARE_ENCRYPTION_BIT_CT, SHARE_ENCRYPTION_BIT_E0, SHARE_ENCRYPTION_BIT_E1, - SHARE_ENCRYPTION_BIT_MSG, SHARE_ENCRYPTION_BIT_P1, SHARE_ENCRYPTION_BIT_P2, - SHARE_ENCRYPTION_BIT_PK, SHARE_ENCRYPTION_BIT_R1, SHARE_ENCRYPTION_BIT_R2, - SHARE_ENCRYPTION_BIT_U, SHARE_ENCRYPTION_CONFIGS, + L, N, SHARE_COMPUTATION_CHUNK_SIZE, SHARE_COMPUTATION_N_CHUNKS, SHARE_ENCRYPTION_BIT_CT, + SHARE_ENCRYPTION_BIT_E0, SHARE_ENCRYPTION_BIT_E1, SHARE_ENCRYPTION_BIT_MSG, + SHARE_ENCRYPTION_BIT_P1, SHARE_ENCRYPTION_BIT_P2, SHARE_ENCRYPTION_BIT_PK, + SHARE_ENCRYPTION_BIT_R1, SHARE_ENCRYPTION_BIT_R2, SHARE_ENCRYPTION_BIT_U, + SHARE_ENCRYPTION_CONFIGS, }; use lib::core::dkg::share_encryption::ShareEncryption; use lib::math::polynomial::Polynomial; @@ -16,6 +17,8 @@ use lib::math::polynomial::Polynomial; fn main( expected_pk_commitment: pub Field, expected_message_commitment: pub Field, + party_idx: pub u32, + mod_idx: pub u32, pk0is: [Polynomial; L], pk1is: [Polynomial; L], ct0is: [Polynomial; L], @@ -31,10 +34,12 @@ fn main( p1is: [Polynomial<(2 * N) - 1>; L], p2is: [Polynomial; L], ) -> pub Field { - let share_encryption: ShareEncryption = ShareEncryption::new( + let share_encryption: ShareEncryption = ShareEncryption::new( SHARE_ENCRYPTION_CONFIGS, expected_pk_commitment, expected_message_commitment, + party_idx, + mod_idx, pk0is, pk1is, ct0is, diff --git a/circuits/bin/dkg/sk_share_computation/README.md b/circuits/bin/dkg/sk_share_computation/README.md deleted file mode 100644 index e69a95c15a..0000000000 --- a/circuits/bin/dkg/sk_share_computation/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# `sk_share_computation` — C2a (share computation) - -Correct Threshold Secret Key Share Computation (**Circuit 2a**). Verifies the expected secret -commitment, checks secret consistency (`y[i][j][0] == sk_secret[i]`), performs range checks (`y` in -`[0, q_j)`), and enforces Reed–Solomon parity using the preset `PARITY_MATRIX`. Commits computed -party shares for downstream aggregation. - -| | | -| --------- | ----------------------------------------------------------------------------------------- | -| **Core** | [`lib/src/core/dkg/share_computation.nr`](../../../lib/src/core/dkg/share_computation.nr) | -| **Index** | [Circuit package index](../../../README.md#circuit-package-index) | -| **Docs** | [Noir Circuits](../../../../docs/pages/noir-circuits.mdx) | diff --git a/circuits/bin/dkg/sk_share_computation/src/main.nr b/circuits/bin/dkg/sk_share_computation/src/main.nr deleted file mode 100644 index 755fc878f6..0000000000 --- a/circuits/bin/dkg/sk_share_computation/src/main.nr +++ /dev/null @@ -1,29 +0,0 @@ -// 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 lib::configs::default::dkg::{ - L_THRESHOLD, N, PARITY_MATRIX, SHARE_COMPUTATION_BIT_SHARE, SHARE_COMPUTATION_SK_BIT_SECRET, - SHARE_COMPUTATION_SK_CONFIGS, -}; -use lib::configs::default::{N_PARTIES, T}; -use lib::core::dkg::share_computation::SecretKeyShareComputation; -use lib::math::polynomial::Polynomial; - -fn main( - expected_secret_commitment: pub Field, - sk_secret: Polynomial, - y: [[[Field; N_PARTIES + 1]; L_THRESHOLD]; N], -) -> pub [[Field; L_THRESHOLD]; N_PARTIES] { - let sk_share_computation: SecretKeyShareComputation = SecretKeyShareComputation::new( - SHARE_COMPUTATION_SK_CONFIGS, - expected_secret_commitment, - sk_secret, - y, - PARITY_MATRIX, - ); - - sk_share_computation.execute() -} diff --git a/circuits/bin/dkg/sk_share_computation_chunk/Nargo.toml b/circuits/bin/dkg/sk_share_computation_chunk/Nargo.toml new file mode 100644 index 0000000000..e57e7beb27 --- /dev/null +++ b/circuits/bin/dkg/sk_share_computation_chunk/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "sk_share_computation_chunk" +type = "bin" +authors = ["Gnosis Guild / Interfold"] + +[dependencies] +lib = { path = "../../../lib" } diff --git a/circuits/bin/dkg/sk_share_computation_chunk/src/main.nr b/circuits/bin/dkg/sk_share_computation_chunk/src/main.nr new file mode 100644 index 0000000000..f9476f9429 --- /dev/null +++ b/circuits/bin/dkg/sk_share_computation_chunk/src/main.nr @@ -0,0 +1,76 @@ +// 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 lib::configs::default::dkg::{ + L_THRESHOLD, N, PARITY_MATRIX, SHARE_COMPUTATION_BIT_SHARE, SHARE_COMPUTATION_CHUNK_SIZE, + SHARE_COMPUTATION_N_CHUNKS, SHARE_COMPUTATION_SK_BIT_SECRET, SHARE_COMPUTATION_SK_CONFIGS, +}; +use lib::configs::default::{N_PARTIES, T}; +use lib::core::dkg::share_computation::check_range_bounds; +use lib::core::dkg::share_computation::verify_parity_check; +use lib::math::commitments::{ + compute_sc_party_share_chunk_commitment, compute_sc_secret_chunk_commitment, +}; +use lib::math::polynomial::Polynomial; + +pub global CHUNK_PUBLIC_LEN: u32 = 1 + (N_PARTIES * L_THRESHOLD); + +fn main( + chunk_idx: pub u32, + secret_chunk: Polynomial, + y_chunk: [[[Field; N_PARTIES + 1]; L_THRESHOLD]; SHARE_COMPUTATION_CHUNK_SIZE], +) -> pub [Field; CHUNK_PUBLIC_LEN] { + assert(chunk_idx < SHARE_COMPUTATION_N_CHUNKS); + + for coeff_idx in 0..SHARE_COMPUTATION_CHUNK_SIZE { + for mod_idx in 0..L_THRESHOLD { + assert(y_chunk[coeff_idx][mod_idx][0] == secret_chunk.coefficients[coeff_idx]); + } + } + + check_range_bounds::( + SHARE_COMPUTATION_SK_CONFIGS.qis, + y_chunk, + ); + verify_parity_check::( + SHARE_COMPUTATION_SK_CONFIGS.qis, + PARITY_MATRIX, + y_chunk, + ); + + let mut out: [Field; CHUNK_PUBLIC_LEN] = [0; CHUNK_PUBLIC_LEN]; + let mut reversed_secret: [Field; SHARE_COMPUTATION_CHUNK_SIZE] = + [0; SHARE_COMPUTATION_CHUNK_SIZE]; + for i in 0..SHARE_COMPUTATION_CHUNK_SIZE { + reversed_secret[SHARE_COMPUTATION_CHUNK_SIZE - 1 - i] = secret_chunk.coefficients[i]; + } + out[0] = compute_sc_secret_chunk_commitment::( + 0, + chunk_idx, + N, + Polynomial::new(reversed_secret), + ); + + for party_idx in 0..N_PARTIES { + for mod_idx in 0..L_THRESHOLD { + let mut reversed_share: [Field; SHARE_COMPUTATION_CHUNK_SIZE] = + [0; SHARE_COMPUTATION_CHUNK_SIZE]; + for i in 0..SHARE_COMPUTATION_CHUNK_SIZE { + reversed_share[SHARE_COMPUTATION_CHUNK_SIZE - 1 - i] = + y_chunk[i][mod_idx][party_idx + 1]; + } + out[1 + party_idx * L_THRESHOLD + mod_idx] = compute_sc_party_share_chunk_commitment::( + party_idx, + mod_idx, + chunk_idx, + N, + Polynomial::new(reversed_share), + ); + } + } + + out +} diff --git a/circuits/bin/recursive_aggregation/c2ab_fold/Nargo.toml b/circuits/bin/recursive_aggregation/c2_chunk_batch/Nargo.toml similarity index 91% rename from circuits/bin/recursive_aggregation/c2ab_fold/Nargo.toml rename to circuits/bin/recursive_aggregation/c2_chunk_batch/Nargo.toml index d0e83e50ef..439643ce36 100644 --- a/circuits/bin/recursive_aggregation/c2ab_fold/Nargo.toml +++ b/circuits/bin/recursive_aggregation/c2_chunk_batch/Nargo.toml @@ -1,8 +1,8 @@ [package] -name = "c2ab_fold" +name = "c2_chunk_batch" type = "bin" authors = ["Gnosis Guild / Interfold"] [dependencies] -lib = { path = "../../../lib" } bb_proof_verification = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v5.1.0", directory = "barretenberg/noir/bb_proof_verification" } +lib = { path = "../../../lib" } diff --git a/circuits/bin/recursive_aggregation/c2_chunk_batch/src/main.nr b/circuits/bin/recursive_aggregation/c2_chunk_batch/src/main.nr new file mode 100644 index 0000000000..703fa9dc72 --- /dev/null +++ b/circuits/bin/recursive_aggregation/c2_chunk_batch/src/main.nr @@ -0,0 +1,52 @@ +// 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. + +//! Verifies a fixed batch of independent C2 chunk proofs. + +use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; +use lib::configs::default::dkg::{ + L_THRESHOLD, SHARE_COMPUTATION_CHUNKS_PER_BATCH as CHUNKS_PER_BATCH, + SHARE_COMPUTATION_N_CHUNKS as N_CHUNKS, +}; +use lib::configs::default::N_PARTIES; +use lib::math::commitments::compute_recursive_aggregation_commitment; + +pub global CHUNK_COMMITMENT_LEN: u32 = 1 + (N_PARTIES * L_THRESHOLD); +pub global CHUNK_PUBLIC_LEN: u32 = 1 + CHUNK_COMMITMENT_LEN; +pub global BATCH_RETURN_LEN: u32 = 2 + (CHUNKS_PER_BATCH * CHUNK_COMMITMENT_LEN); + +fn main( + chunk_vk: UltraHonkVerificationKey, + chunk_proofs: [UltraHonkZKProof; CHUNKS_PER_BATCH], + chunk_public_inputs: [[Field; CHUNK_PUBLIC_LEN]; CHUNKS_PER_BATCH], + chunk_key_hash: pub Field, + batch_idx: pub u32, +) -> pub [Field; BATCH_RETURN_LEN] { + let mut batch_payload = [batch_idx as Field].as_vector(); + let mut output: [Field; BATCH_RETURN_LEN] = [0; BATCH_RETURN_LEN]; + + for i in 0..CHUNKS_PER_BATCH { + let expected_chunk_idx = batch_idx * CHUNKS_PER_BATCH + i; + assert(expected_chunk_idx < N_CHUNKS); + verify_honk_proof( + chunk_vk, + chunk_proofs[i], + chunk_public_inputs[i], + chunk_key_hash, + ); + assert(chunk_public_inputs[i][0] == expected_chunk_idx as Field); + + batch_payload = batch_payload.push_back(expected_chunk_idx as Field); + for j in 1..CHUNK_PUBLIC_LEN { + output[2 + i * CHUNK_COMMITMENT_LEN + (j - 1)] = chunk_public_inputs[i][j]; + batch_payload = batch_payload.push_back(chunk_public_inputs[i][j]); + } + } + + output[0] = batch_idx as Field; + output[1] = compute_recursive_aggregation_commitment(batch_payload); + output +} diff --git a/circuits/bin/dkg/e_sm_share_computation/Nargo.toml b/circuits/bin/recursive_aggregation/c2ab_chunk_fold/Nargo.toml similarity index 78% rename from circuits/bin/dkg/e_sm_share_computation/Nargo.toml rename to circuits/bin/recursive_aggregation/c2ab_chunk_fold/Nargo.toml index 46007acaf7..4d725a4781 100644 --- a/circuits/bin/dkg/e_sm_share_computation/Nargo.toml +++ b/circuits/bin/recursive_aggregation/c2ab_chunk_fold/Nargo.toml @@ -1,7 +1,7 @@ [package] -name = "e_sm_share_computation" +name = "c2ab_chunk_fold" type = "bin" -authors = [""] +authors = ["Gnosis Guild / Interfold"] [dependencies] lib = { path = "../../../lib" } diff --git a/circuits/bin/recursive_aggregation/c2ab_chunk_fold/src/main.nr b/circuits/bin/recursive_aggregation/c2ab_chunk_fold/src/main.nr new file mode 100644 index 0000000000..fd621a8bcc --- /dev/null +++ b/circuits/bin/recursive_aggregation/c2ab_chunk_fold/src/main.nr @@ -0,0 +1,52 @@ +// 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. + +//! Combines type-bound terminal C2a and C2b chunk proofs. + +use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; +use lib::configs::default::dkg::L_THRESHOLD; +use lib::configs::default::N_PARTIES; +use lib::math::commitments::compute_vk_hash; + +pub global C2_PUBLIC_LEN: u32 = 1 + (N_PARTIES * L_THRESHOLD); +pub global C2_FINALIZED_PUBLIC_LEN: u32 = C2_PUBLIC_LEN + 2; +pub global C2AB_FOLD_PUBLIC_LEN: u32 = 6 + (2 * C2_PUBLIC_LEN); + +fn main( + c2a_vk: UltraHonkVerificationKey, + c2a_proof: UltraHonkZKProof, + c2a_public: [Field; C2_FINALIZED_PUBLIC_LEN], + c2b_vk: UltraHonkVerificationKey, + c2b_proof: UltraHonkZKProof, + c2b_public: [Field; C2_FINALIZED_PUBLIC_LEN], + c2a_key_hash: pub Field, + c2b_key_hash: pub Field, + ) -> pub (Field, Field, Field, Field, [[Field; L_THRESHOLD]; N_PARTIES], Field, [[Field; L_THRESHOLD]; N_PARTIES], Field) { + verify_honk_proof(c2a_vk, c2a_proof, c2a_public, c2a_key_hash); + verify_honk_proof(c2b_vk, c2b_proof, c2b_public, c2b_key_hash); + let key_hash = compute_vk_hash([c2a_key_hash, c2b_key_hash].as_vector()); + let vk_manifest = compute_vk_hash([ + c2a_key_hash, + c2b_key_hash, + c2a_public[C2_FINALIZED_PUBLIC_LEN - 1], + c2b_public[C2_FINALIZED_PUBLIC_LEN - 1], + c2a_public[0], + c2b_public[0], + ] + .as_vector()); + let mut c2a_shares: [[Field; L_THRESHOLD]; N_PARTIES] = [[0; L_THRESHOLD]; N_PARTIES]; + let mut c2b_shares: [[Field; L_THRESHOLD]; N_PARTIES] = [[0; L_THRESHOLD]; N_PARTIES]; + for party_idx in 0..N_PARTIES { + for mod_idx in 0..L_THRESHOLD { + c2a_shares[party_idx][mod_idx] = c2a_public[2 + party_idx * L_THRESHOLD + mod_idx]; + c2b_shares[party_idx][mod_idx] = c2b_public[2 + party_idx * L_THRESHOLD + mod_idx]; + } + } + ( + key_hash, c2a_public[0], c2b_public[0], c2a_public[1], c2a_shares, c2b_public[1], + c2b_shares, vk_manifest, + ) +} diff --git a/circuits/bin/recursive_aggregation/c2ab_fold/src/main.nr b/circuits/bin/recursive_aggregation/c2ab_fold/src/main.nr deleted file mode 100644 index 6c26fce001..0000000000 --- a/circuits/bin/recursive_aggregation/c2ab_fold/src/main.nr +++ /dev/null @@ -1,53 +0,0 @@ -// 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. - -//! Combines C2a + C2b (two ZK Honk proofs) and surfaces their public inputs for `node_fold`. - -use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; -use lib::configs::default::dkg::L_THRESHOLD; -use lib::configs::default::N_PARTIES; -use lib::math::commitments::compute_vk_hash; - -/// C2 inner public IO: `expected_secret_commitment` + `[[Field; L_THRESHOLD]; N_PARTIES]`. -pub global C2_PUBLIC_LEN: u32 = 1 + (N_PARTIES * L_THRESHOLD); - -/// Two key-hash params + combined key hash + two C2-sized public vectors (a and b). -pub global C2AB_FOLD_PUBLIC_LEN: u32 = 3 + (2 * C2_PUBLIC_LEN); - -fn main( - c2a_vk: UltraHonkVerificationKey, - c2a_proof: UltraHonkZKProof, - c2a_public: [Field; C2_PUBLIC_LEN], - c2b_vk: UltraHonkVerificationKey, - c2b_proof: UltraHonkZKProof, - c2b_public: [Field; C2_PUBLIC_LEN], - c2a_key_hash: pub Field, - c2b_key_hash: pub Field, - ) -> pub (Field, Field, [[Field; L_THRESHOLD]; N_PARTIES], Field, [[Field; L_THRESHOLD]; N_PARTIES]) { - verify_honk_proof(c2a_vk, c2a_proof, c2a_public, c2a_key_hash); - verify_honk_proof(c2b_vk, c2b_proof, c2b_public, c2b_key_hash); - - let vk_hashes = [c2a_key_hash, c2b_key_hash].as_vector(); - let key_hash = compute_vk_hash(vk_hashes); - - let c2a_expected = c2a_public[0]; - let mut c2a_shares: [[Field; L_THRESHOLD]; N_PARTIES] = [[0; L_THRESHOLD]; N_PARTIES]; - for j in 0..N_PARTIES { - for l in 0..L_THRESHOLD { - c2a_shares[j][l] = c2a_public[1 + j * L_THRESHOLD + l]; - } - } - - let c2b_expected = c2b_public[0]; - let mut c2b_shares: [[Field; L_THRESHOLD]; N_PARTIES] = [[0; L_THRESHOLD]; N_PARTIES]; - for j in 0..N_PARTIES { - for l in 0..L_THRESHOLD { - c2b_shares[j][l] = c2b_public[1 + j * L_THRESHOLD + l]; - } - } - - (key_hash, c2a_expected, c2a_shares, c2b_expected, c2b_shares) -} diff --git a/circuits/bin/recursive_aggregation/c3_fold/src/main.nr b/circuits/bin/recursive_aggregation/c3_fold/src/main.nr index acb49aa817..67ce4182c9 100644 --- a/circuits/bin/recursive_aggregation/c3_fold/src/main.nr +++ b/circuits/bin/recursive_aggregation/c3_fold/src/main.nr @@ -18,15 +18,24 @@ use lib::configs::default::N_PARTIES; /// Total slots: one per (party, threshold-modulus) pair. pub global C3_SLOTS: u32 = N_PARTIES * L_THRESHOLD; -/// Total public fields for a `c3_fold` proof: 4 pub parameters + 3*C3_SLOTS (pk, msg, ct). -pub global C3_FOLD_PUBLIC_LEN: u32 = 4 + (3 * C3_SLOTS); +/// Total public fields for a `c3_fold` proof: 6 pub parameters + 3*C3_SLOTS (pk, msg, ct). +pub global C3_FOLD_PUBLIC_LEN: u32 = 6 + (3 * C3_SLOTS); pub global C3_FOLD_PREFIX_LEN: u32 = C3_FOLD_PUBLIC_LEN - (3 * C3_SLOTS); +/// Named indices into the 6-field accumulator prefix (`c3_fold` and `c3_fold_kernel` share this +/// layout; the 3-field-wide per-slot tail starts at `C3_FOLD_PREFIX_LEN`). +pub global ACC_CHILD_VK_IDX: u32 = 0; +pub global ACC_ACC_VK_IDX: u32 = 1; +pub global ACC_IS_FIRST_STEP_IDX: u32 = 2; +pub global ACC_SLOT_INDEX_IDX: u32 = 3; +pub global ACC_EXPECTED_KERNEL_IDX: u32 = 4; +pub global ACC_EXPECTED_FOLD_IDX: u32 = 5; + fn main( inner_vk: UltraHonkVerificationKey, inner_proof: UltraHonkZKProof, - c3_public_inputs: [Field; 3], + c3_public_inputs: [Field; 5], acc_vk: UltraHonkVerificationKey, acc_proof: UltraHonkProof, acc_public_inputs: [Field; C3_FOLD_PUBLIC_LEN], @@ -34,16 +43,36 @@ fn main( acc_key_hash: pub Field, is_first_step: pub bool, slot_index: pub u32, + expected_kernel_key_hash: pub Field, + expected_fold_key_hash: pub Field, ) -> pub ([Field; C3_SLOTS], [Field; C3_SLOTS], [Field; C3_SLOTS]) { assert(slot_index < C3_SLOTS); verify_honk_proof(inner_vk, inner_proof, c3_public_inputs, inner_key_hash); + assert(c3_public_inputs[2] == (slot_index / L_THRESHOLD) as Field); + assert(c3_public_inputs[3] == (slot_index % L_THRESHOLD) as Field); + + // Bind every fold step to one canonical child VK: the previous accumulator's child VK hash + // (public input 0) must equal this step's inner `ShareEncryption` VK hash. This closes the VK + // substitution attack where an intermediate step is proven under a noncanonical child VK. + assert(acc_public_inputs[ACC_CHILD_VK_IDX] == inner_key_hash); + + if is_first_step { + assert(acc_key_hash == expected_kernel_key_hash); + } else { + assert(acc_key_hash == expected_fold_key_hash); + if acc_public_inputs[ACC_IS_FIRST_STEP_IDX] == 1 { + assert(acc_public_inputs[ACC_EXPECTED_KERNEL_IDX] == expected_kernel_key_hash); + } else { + assert(acc_public_inputs[ACC_EXPECTED_FOLD_IDX] == expected_fold_key_hash); + } + } verify_honk_proof_non_zk(acc_vk, acc_proof, acc_public_inputs, acc_key_hash); let pk_new = c3_public_inputs[0]; let msg_new = c3_public_inputs[1]; - let ct_new = c3_public_inputs[2]; + let ct_new = c3_public_inputs[4]; let mut out_pk: [Field; C3_SLOTS] = [0; C3_SLOTS]; let mut out_msg: [Field; C3_SLOTS] = [0; C3_SLOTS]; diff --git a/circuits/bin/recursive_aggregation/c3_fold_kernel/src/main.nr b/circuits/bin/recursive_aggregation/c3_fold_kernel/src/main.nr index 6fb95dce68..9e3670d0cb 100644 --- a/circuits/bin/recursive_aggregation/c3_fold_kernel/src/main.nr +++ b/circuits/bin/recursive_aggregation/c3_fold_kernel/src/main.nr @@ -16,12 +16,12 @@ use lib::configs::default::N_PARTIES; pub global C3_SLOTS: u32 = N_PARTIES * L_THRESHOLD; -pub global C3_FOLD_PUBLIC_LEN: u32 = 4 + (3 * C3_SLOTS); +pub global C3_FOLD_PUBLIC_LEN: u32 = 6 + (3 * C3_SLOTS); fn main( inner_vk: UltraHonkVerificationKey, inner_proof: UltraHonkZKProof, - c3_public_inputs: [Field; 3], + c3_public_inputs: [Field; 5], acc_vk: UltraHonkVerificationKey, acc_proof: UltraHonkProof, acc_public_inputs: [Field; C3_FOLD_PUBLIC_LEN], @@ -29,19 +29,24 @@ fn main( acc_key_hash: pub Field, is_first_step: pub bool, slot_index: pub u32, + expected_kernel_key_hash: pub Field, + expected_fold_key_hash: pub Field, ) -> pub ([Field; C3_SLOTS], [Field; C3_SLOTS], [Field; C3_SLOTS]) { assert(slot_index < C3_SLOTS); verify_honk_proof(inner_vk, inner_proof, c3_public_inputs, inner_key_hash); + assert(c3_public_inputs[2] == (slot_index / L_THRESHOLD) as Field); + assert(c3_public_inputs[3] == (slot_index % L_THRESHOLD) as Field); // Signature parity with `c3_fold`: kernel mints the genesis accumulator, so acc_* is unused. - let _ = (acc_vk, acc_proof, acc_key_hash, acc_public_inputs); + let _ = (acc_vk, acc_proof, acc_key_hash, acc_public_inputs, expected_fold_key_hash); assert(is_first_step); + assert(acc_key_hash == expected_kernel_key_hash); let pk_new = c3_public_inputs[0]; let msg_new = c3_public_inputs[1]; - let ct_new = c3_public_inputs[2]; + let ct_new = c3_public_inputs[4]; let mut out_pk: [Field; C3_SLOTS] = [0; C3_SLOTS]; let mut out_msg: [Field; C3_SLOTS] = [0; C3_SLOTS]; diff --git a/circuits/bin/recursive_aggregation/c3ab_fold/src/main.nr b/circuits/bin/recursive_aggregation/c3ab_fold/src/main.nr index b23bdc960e..30247bc7ad 100644 --- a/circuits/bin/recursive_aggregation/c3ab_fold/src/main.nr +++ b/circuits/bin/recursive_aggregation/c3ab_fold/src/main.nr @@ -14,13 +14,13 @@ use lib::math::commitments::compute_vk_hash; /// Must match `c3_fold`: one slot per (party, threshold-modulus) pair. pub global C3_SLOTS: u32 = N_PARTIES * L_THRESHOLD; -/// Must match `c3_fold`: `4 + 3*C3_SLOTS` public fields per final proof. -pub global C3_FOLD_PUBLIC_LEN: u32 = 4 + (3 * C3_SLOTS); +/// Must match `c3_fold`: `6 + 3*C3_SLOTS` public fields per final proof. +pub global C3_FOLD_PUBLIC_LEN: u32 = 6 + (3 * C3_SLOTS); pub global C3_FOLD_PREFIX_LEN: u32 = C3_FOLD_PUBLIC_LEN - (3 * C3_SLOTS); /// Total public fields for `c3ab_fold` (two key hashes + flattened return tuple). -pub global C3AB_FOLD_PUBLIC_LEN: u32 = 2 + 1 + (6 * C3_SLOTS); +pub global C3AB_FOLD_PUBLIC_LEN: u32 = 2 + 2 + (6 * C3_SLOTS); fn main( c3a_vk: UltraHonkVerificationKey, @@ -31,12 +31,25 @@ fn main( c3b_public: [Field; C3_FOLD_PUBLIC_LEN], c3a_key_hash: pub Field, c3b_key_hash: pub Field, - ) -> pub (Field, [Field; C3_SLOTS], [Field; C3_SLOTS], [Field; C3_SLOTS], [Field; C3_SLOTS], [Field; C3_SLOTS], [Field; C3_SLOTS]) { + ) -> pub (Field, [Field; C3_SLOTS], [Field; C3_SLOTS], [Field; C3_SLOTS], [Field; C3_SLOTS], [Field; C3_SLOTS], [Field; C3_SLOTS], Field) { verify_honk_proof_non_zk(c3a_vk, c3a_proof, c3a_public, c3a_key_hash); verify_honk_proof_non_zk(c3b_vk, c3b_proof, c3b_public, c3b_key_hash); let vk_hashes = [c3a_key_hash, c3b_key_hash].as_vector(); let key_hash = compute_vk_hash(vk_hashes); + let vk_manifest = compute_vk_hash([ + c3a_key_hash, + c3b_key_hash, + c3a_public[0], + c3b_public[0], + c3a_public[1], + c3b_public[1], + c3a_public[4], + c3b_public[4], + c3a_public[5], + c3b_public[5], + ] + .as_vector()); let mut pk_a: [Field; C3_SLOTS] = [0; C3_SLOTS]; let mut msg_a: [Field; C3_SLOTS] = [0; C3_SLOTS]; @@ -54,5 +67,5 @@ fn main( ct_b[j] = c3b_public[C3_FOLD_PREFIX_LEN + C3_SLOTS + C3_SLOTS + j]; } - (key_hash, pk_a, msg_a, ct_a, pk_b, msg_b, ct_b) + (key_hash, pk_a, msg_a, ct_a, pk_b, msg_b, ct_b, vk_manifest) } diff --git a/circuits/bin/recursive_aggregation/dkg_aggregator/src/main.nr b/circuits/bin/recursive_aggregation/dkg_aggregator/src/main.nr index 75f7a2fe06..6b52044ac0 100644 --- a/circuits/bin/recursive_aggregation/dkg_aggregator/src/main.nr +++ b/circuits/bin/recursive_aggregation/dkg_aggregator/src/main.nr @@ -22,14 +22,14 @@ 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); +pub global NODE_FOLD_PUBLIC_LEN: u32 = 14 + N_PARTIES + (2 * (N_PARTIES + H) * L_THRESHOLD); /// Must match `nodes_fold::NODES_FOLD_PUBLIC_LEN` (pub params + flattened `H` node-fold rows). -pub global NODES_FOLD_PUBLIC_LEN: u32 = 4 + (H * NODE_FOLD_PUBLIC_LEN); +pub global NODES_FOLD_PUBLIC_LEN: u32 = 6 + (H * NODE_FOLD_PUBLIC_LEN); /// Flattened `node_fold` return tuple length (key hash + pks + share grids + agg commits). pub global NODE_FOLD_RETURN_PUBLIC_LEN: u32 = - 5 + N_PARTIES + (2 * N_PARTIES * L_THRESHOLD) + (2 * H * L_THRESHOLD); + 8 + N_PARTIES + (2 * N_PARTIES * L_THRESHOLD) + (2 * H * L_THRESHOLD); /// Leading `node_fold` public inputs (party id + five key hashes) before the return tuple. pub global NODE_FOLD_PARAM_PUBLIC_LEN: u32 = NODE_FOLD_PUBLIC_LEN - NODE_FOLD_RETURN_PUBLIC_LEN; @@ -39,15 +39,38 @@ pub global NODES_FOLD_PARAM_PUBLIC_LEN: u32 = NODES_FOLD_PUBLIC_LEN - (H * NODE_ /// Indices within one `node_fold` public row (length `NODE_FOLD_PUBLIC_LEN`). pub global NF_PARTY_ID_IDX: u32 = 0; -pub global NF_C0_PK_IDX: u32 = NODE_FOLD_PARAM_PUBLIC_LEN + 1; -pub global NF_C1_PK_IDX: u32 = NODE_FOLD_PARAM_PUBLIC_LEN + 2; -pub global NF_C3_PK_BASE_IDX: u32 = NODE_FOLD_PARAM_PUBLIC_LEN + 3; -pub global NF_C2A_BASE_IDX: u32 = NODE_FOLD_PARAM_PUBLIC_LEN + 3 + N_PARTIES; +pub global NF_C2A_CHUNK_HASH_IDX: u32 = NODE_FOLD_PARAM_PUBLIC_LEN + 1; +pub global NF_C2B_CHUNK_HASH_IDX: u32 = NODE_FOLD_PARAM_PUBLIC_LEN + 2; +pub global NF_C0_PK_IDX: u32 = NODE_FOLD_PARAM_PUBLIC_LEN + 3; +pub global NF_C1_PK_IDX: u32 = NODE_FOLD_PARAM_PUBLIC_LEN + 4; +pub global NF_C3_PK_BASE_IDX: u32 = NODE_FOLD_PARAM_PUBLIC_LEN + 5; +pub global NF_C2A_BASE_IDX: u32 = NODE_FOLD_PARAM_PUBLIC_LEN + 5 + N_PARTIES; pub global NF_C2B_BASE_IDX: u32 = NF_C2A_BASE_IDX + (N_PARTIES * L_THRESHOLD); pub global NF_C4A_BASE_IDX: u32 = NF_C2B_BASE_IDX + (N_PARTIES * L_THRESHOLD); pub global NF_C4B_BASE_IDX: u32 = NF_C4A_BASE_IDX + (H * L_THRESHOLD); pub global NF_SK_AGG_IDX: u32 = NF_C4B_BASE_IDX + (H * L_THRESHOLD); pub global NF_ESM_AGG_IDX: u32 = NF_SK_AGG_IDX + 1; +pub global NF_NODE_VK_MANIFEST_IDX: u32 = NODE_FOLD_PUBLIC_LEN - 1; + +/// Expected recursive VK hashes, in the order used by the node manifest. +/// The values are public inputs and the Solidity wrapper anchors them. +pub global VK_BINDING_LEN: u32 = 16; +pub global VK_NODE_FOLD: u32 = 0; +pub global VK_C0: u32 = 1; +pub global VK_C1: u32 = 2; +pub global VK_C2AB: u32 = 3; +pub global VK_C3AB: u32 = 4; +pub global VK_C4AB: u32 = 5; +pub global VK_C2A_FINALIZE: u32 = 6; +pub global VK_C2B_FINALIZE: u32 = 7; +pub global VK_C2_BATCH: u32 = 8; +pub global VK_C2A_CHUNK: u32 = 9; +pub global VK_C2B_CHUNK: u32 = 10; +pub global VK_C3_FOLD: u32 = 11; +pub global VK_SHARE_ENCRYPTION: u32 = 12; +pub global VK_C4: u32 = 13; +pub global VK_C3_FOLD_KERNEL: u32 = 14; +pub global VK_NODES_FOLD_KERNEL: u32 = 15; fn main( nodes_fold_vk: UltraHonkVerificationKey, @@ -62,7 +85,8 @@ fn main( committee_members: [Field; N_PARTIES], committee_hash_hi: pub Field, committee_hash_lo: pub Field, -) -> pub (Field, [Field; H], [Field; H], Field) { + vk_binding: pub [Field; VK_BINDING_LEN], +) -> pub (Field, Field, 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); @@ -71,10 +95,82 @@ fn main( // 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. + assert(nodes_fold_public[0] == vk_binding[VK_NODE_FOLD]); + if nodes_fold_public[2] == 1 { + assert(nodes_fold_public[1] == vk_binding[VK_NODES_FOLD_KERNEL]); + } else { + assert(nodes_fold_public[1] == nodes_fold_key_hash); + } + assert(nodes_fold_public[4] == vk_binding[VK_NODES_FOLD_KERNEL]); + assert(nodes_fold_public[5] == nodes_fold_key_hash); + + let expected_c2_manifest = compute_vk_hash([ + vk_binding[VK_C2A_FINALIZE], + vk_binding[VK_C2B_FINALIZE], + vk_binding[VK_C2_BATCH], + vk_binding[VK_C2_BATCH], + vk_binding[VK_C2A_CHUNK], + vk_binding[VK_C2B_CHUNK], + ] + .as_vector()); + let expected_c3_manifest = compute_vk_hash([ + vk_binding[VK_C3_FOLD], + vk_binding[VK_C3_FOLD], + vk_binding[VK_SHARE_ENCRYPTION], + vk_binding[VK_SHARE_ENCRYPTION], + vk_binding[VK_C3_FOLD], + vk_binding[VK_C3_FOLD], + vk_binding[VK_C3_FOLD_KERNEL], + vk_binding[VK_C3_FOLD_KERNEL], + vk_binding[VK_C3_FOLD], + vk_binding[VK_C3_FOLD], + ] + .as_vector()); + let expected_c4_manifest = compute_vk_hash([vk_binding[VK_C4], vk_binding[VK_C4]].as_vector()); + let expected_node_manifest = compute_vk_hash([ + vk_binding[VK_C0], + vk_binding[VK_C1], + vk_binding[VK_C2AB], + vk_binding[VK_C3AB], + vk_binding[VK_C4AB], + expected_c2_manifest, + expected_c3_manifest, + expected_c4_manifest, + ] + .as_vector()); + + for i in 0..H { + let row = NODES_FOLD_PARAM_PUBLIC_LEN + i * NODE_FOLD_PUBLIC_LEN; + assert(nodes_fold_public[row + 1] == vk_binding[VK_C0]); + assert(nodes_fold_public[row + 2] == vk_binding[VK_C1]); + assert(nodes_fold_public[row + 3] == vk_binding[VK_C2AB]); + assert(nodes_fold_public[row + 4] == vk_binding[VK_C3AB]); + assert(nodes_fold_public[row + 5] == vk_binding[VK_C4AB]); + assert(nodes_fold_public[row + NF_NODE_VK_MANIFEST_IDX] == expected_node_manifest); + } + + for i in 0..H { + assert( + nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN + + i * NODE_FOLD_PUBLIC_LEN + + NF_C2A_CHUNK_HASH_IDX] + == nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN + NF_C2A_CHUNK_HASH_IDX], + ); + assert( + nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN + + i * NODE_FOLD_PUBLIC_LEN + + NF_C2B_CHUNK_HASH_IDX] + == nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN + NF_C2B_CHUNK_HASH_IDX], + ); + } + for i in 0..H { let id = party_ids[i] as u32; assert(id < N_PARTIES); } + for i in 1..H { + assert((party_ids[i - 1] as u32) < (party_ids[i] as u32)); + } verify_honk_proof_non_zk( nodes_fold_vk, @@ -95,15 +191,16 @@ fn main( ); } - // Cross-node C0 to C3: each row's c3_pk[j] (recipient pk used in party i's C3 encryption for - // recipient j) must equal party j's own c0_pk. + // Cross-node C0 to C3: each row's c3_pk[party_ids[j]] (recipient pk used in party i's C3 + // encryption for honest recipient j) must equal that recipient row's own c0_pk. for i in 0..H { for j in 0..H { + let recipient_id = party_ids[j] as u32; assert( nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN + i * NODE_FOLD_PUBLIC_LEN + NF_C3_PK_BASE_IDX - + j] + + recipient_id] == nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN + j * NODE_FOLD_PUBLIC_LEN + NF_C0_PK_IDX], @@ -114,6 +211,7 @@ fn main( // Cross-party C2 to C4 (first chain). for i in 0..H { for j in 0..H { + let recipient_id = party_ids[j] as u32; for l in 0..L_THRESHOLD { assert( nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN @@ -124,7 +222,7 @@ fn main( == nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN + i * NODE_FOLD_PUBLIC_LEN + NF_C2A_BASE_IDX - + j * L_THRESHOLD + + recipient_id * L_THRESHOLD + l], ); } @@ -134,6 +232,7 @@ fn main( // Cross-party C2 to C4 (second chain). for i in 0..H { for j in 0..H { + let recipient_id = party_ids[j] as u32; for l in 0..L_THRESHOLD { assert( nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN @@ -144,7 +243,7 @@ fn main( == nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN + i * NODE_FOLD_PUBLIC_LEN + NF_C2B_BASE_IDX - + j * L_THRESHOLD + + recipient_id * L_THRESHOLD + l], ); } @@ -176,5 +275,9 @@ fn main( } let aggregated_pk_commit = c5_public[H]; - (key_hash, sk_agg_commits, esm_agg_commits, aggregated_pk_commit) + ( + key_hash, nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN + NF_C2A_CHUNK_HASH_IDX], + nodes_fold_public[NODES_FOLD_PARAM_PUBLIC_LEN + NF_C2B_CHUNK_HASH_IDX], sk_agg_commits, + esm_agg_commits, aggregated_pk_commit, + ) } diff --git a/circuits/bin/recursive_aggregation/dummy_nodes_inner/Nargo.toml b/circuits/bin/recursive_aggregation/dummy_nodes_inner/Nargo.toml new file mode 100644 index 0000000000..a91403d4d8 --- /dev/null +++ b/circuits/bin/recursive_aggregation/dummy_nodes_inner/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "dummy_nodes_inner" +type = "bin" +authors = ["Gnosis Guild / Interfold"] + +[dependencies] +lib = { path = "../../../lib" } diff --git a/circuits/bin/recursive_aggregation/dummy_nodes_inner/src/main.nr b/circuits/bin/recursive_aggregation/dummy_nodes_inner/src/main.nr new file mode 100644 index 0000000000..f7051923a0 --- /dev/null +++ b/circuits/bin/recursive_aggregation/dummy_nodes_inner/src/main.nr @@ -0,0 +1,25 @@ +// 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. + +//! Deliberately noncanonical compatible dummy inner circuit for the `nodes_fold` adversarial tests. +//! +//! It exposes the exact public ABI of `node_fold` — `NODE_FOLD_PUBLIC_LEN` public fields — so its +//! proof is verifiable inside `nodes_fold`'s `verify_honk_proof_non_zk` call, but its VK hash +//! differs from the canonical `node_fold` VK hash. `nodes_fold` must reject a proof minted under +//! this VK. + +use lib::configs::default::dkg::L_THRESHOLD; +use lib::configs::default::{H, N_PARTIES}; + +/// Must match `node_fold::NODE_FOLD_PUBLIC_LEN`. +pub global NODE_FOLD_PUBLIC_LEN: u32 = 14 + N_PARTIES + (2 * (N_PARTIES + H) * L_THRESHOLD); + +fn main( + node_fold_public_inputs: [Field; NODE_FOLD_PUBLIC_LEN], +) -> pub [Field; NODE_FOLD_PUBLIC_LEN] { + // Identity circuit: no constraints beyond the public ABI shape. + node_fold_public_inputs +} diff --git a/circuits/bin/recursive_aggregation/esm_c2_chunk_finalize/Nargo.toml b/circuits/bin/recursive_aggregation/esm_c2_chunk_finalize/Nargo.toml new file mode 100644 index 0000000000..4037d94dd3 --- /dev/null +++ b/circuits/bin/recursive_aggregation/esm_c2_chunk_finalize/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "esm_c2_chunk_finalize" +type = "bin" +authors = ["Gnosis Guild / Interfold"] + +[dependencies] +lib = { path = "../../../lib" } +bb_proof_verification = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v5.1.0", directory = "barretenberg/noir/bb_proof_verification" } diff --git a/circuits/bin/recursive_aggregation/esm_c2_chunk_finalize/src/main.nr b/circuits/bin/recursive_aggregation/esm_c2_chunk_finalize/src/main.nr new file mode 100644 index 0000000000..1fecae3b68 --- /dev/null +++ b/circuits/bin/recursive_aggregation/esm_c2_chunk_finalize/src/main.nr @@ -0,0 +1,78 @@ +// 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. + +//! Verifies C2 chunk batches and reconstructs the existing ESM C2 public layout. + +use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; +use lib::configs::default::dkg::{ + L_THRESHOLD, N, SHARE_COMPUTATION_CHUNK_SIZE as CHUNK_SIZE, + SHARE_COMPUTATION_CHUNKS_PER_BATCH as CHUNKS_PER_BATCH, + SHARE_COMPUTATION_N_BATCHES as N_BATCHES, SHARE_COMPUTATION_N_CHUNKS as N_CHUNKS, +}; +use lib::configs::default::N_PARTIES; +use lib::math::commitments::{ + compute_sc_party_share_chunk_root_commitment, compute_sc_secret_chunk_root_commitment, +}; + +pub global CHUNK_COMMITMENT_LEN: u32 = 1 + (N_PARTIES * L_THRESHOLD); +pub global BATCH_RETURN_LEN: u32 = 2 + (CHUNKS_PER_BATCH * CHUNK_COMMITMENT_LEN); +pub global BATCH_PUBLIC_LEN: u32 = 2 + BATCH_RETURN_LEN; +pub global C2_PUBLIC_LEN: u32 = 1 + (N_PARTIES * L_THRESHOLD); +pub global C2_FINALIZED_PUBLIC_LEN: u32 = C2_PUBLIC_LEN + 2; + +fn main( + batch_vk: UltraHonkVerificationKey, + batch_proofs: [UltraHonkProof; N_BATCHES], + batch_public_inputs: [[Field; BATCH_PUBLIC_LEN]; N_BATCHES], + batch_key_hash: Field, +) -> pub [Field; C2_FINALIZED_PUBLIC_LEN] { + assert(N_BATCHES * CHUNKS_PER_BATCH == N_CHUNKS); + + let mut secret_chunks: [Field; N_CHUNKS] = [0; N_CHUNKS]; + let mut share_chunks: [[Field; N_CHUNKS]; N_PARTIES * L_THRESHOLD] = + [[0; N_CHUNKS]; N_PARTIES * L_THRESHOLD]; + + for batch_idx in 0..N_BATCHES { + verify_honk_proof_non_zk( + batch_vk, + batch_proofs[batch_idx], + batch_public_inputs[batch_idx], + batch_key_hash, + ); + assert(batch_public_inputs[batch_idx][1] == batch_idx as Field); + assert(batch_public_inputs[batch_idx][2] == batch_idx as Field); + assert(batch_public_inputs[batch_idx][0] == batch_public_inputs[0][0]); + + for chunk_in_batch in 0..CHUNKS_PER_BATCH { + let chunk_idx = batch_idx * CHUNKS_PER_BATCH + chunk_in_batch; + let start = 4 + chunk_in_batch * CHUNK_COMMITMENT_LEN; + secret_chunks[chunk_idx] = batch_public_inputs[batch_idx][start]; + for slot in 0..(N_PARTIES * L_THRESHOLD) { + share_chunks[slot][chunk_idx] = batch_public_inputs[batch_idx][start + 1 + slot]; + } + } + } + + let mut output: [Field; C2_FINALIZED_PUBLIC_LEN] = [0; C2_FINALIZED_PUBLIC_LEN]; + output[0] = batch_public_inputs[0][0]; + output[1] = compute_sc_secret_chunk_root_commitment(1, N, CHUNK_SIZE, secret_chunks); + for party_idx in 0..N_PARTIES { + for mod_idx in 0..L_THRESHOLD { + let slot = party_idx * L_THRESHOLD + mod_idx; + output[2 + slot] = compute_sc_party_share_chunk_root_commitment( + party_idx, + mod_idx, + N, + CHUNK_SIZE, + share_chunks[slot], + ); + } + } + // Preserve the batch VK hash in the terminal statement. This lets the + // parent C2AB fold bind the complete batch -> finalizer chain. + output[C2_FINALIZED_PUBLIC_LEN - 1] = batch_key_hash; + output +} diff --git a/circuits/bin/recursive_aggregation/node_fold/src/main.nr b/circuits/bin/recursive_aggregation/node_fold/src/main.nr index 37446c9e2c..8adc375dd6 100644 --- a/circuits/bin/recursive_aggregation/node_fold/src/main.nr +++ b/circuits/bin/recursive_aggregation/node_fold/src/main.nr @@ -4,7 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -//! Per-node fold: C0, C1, `c2ab_fold`, `c3ab_fold`, `c4ab_fold` with same-party assertions. +//! Per-node fold: C0, C1, `c2ab_chunk_fold`, `c3ab_fold`, `c4ab_fold` with same-party assertions. use bb_proof_verification::{ UltraHonkProof, UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof, @@ -15,18 +15,20 @@ use lib::configs::default::{H, N_PARTIES}; use lib::math::commitments::compute_vk_hash; pub global C2_PUBLIC_LEN: u32 = 1 + (N_PARTIES * L_THRESHOLD); -pub global C2AB_FOLD_PUBLIC_LEN: u32 = 3 + (2 * C2_PUBLIC_LEN); +pub global C2AB_FOLD_PUBLIC_LEN: u32 = 6 + (2 * C2_PUBLIC_LEN); /// One slot per (party, threshold-modulus) pair - must match `c3ab_fold::C3AB_FOLD_PUBLIC_LEN`. pub global C3_SLOTS: u32 = N_PARTIES * L_THRESHOLD; -pub global C3AB_FOLD_PUBLIC_LEN: u32 = 2 + 1 + (6 * C3_SLOTS); +pub global C3AB_FOLD_PUBLIC_LEN: u32 = 2 + 2 + (6 * C3_SLOTS); /// Prefix (two inner key hashes + combined hash) before six `C3_SLOTS` columns in `c3ab_fold` public IO. -pub global C3AB_PREFIX_LEN: u32 = C3AB_FOLD_PUBLIC_LEN - (6 * C3_SLOTS); +pub global C3AB_PREFIX_LEN: u32 = 3; pub global C4_PUBLIC_LEN: u32 = (H * L_THRESHOLD) + 1; pub global C4AB_FOLD_PUBLIC_LEN: u32 = 3 + (2 * C4_PUBLIC_LEN); -/// Key-hash prefix length in `c2ab_fold` / `c4ab_fold` flattened public IO. -pub global C2AB_PREFIX_LEN: u32 = C2AB_FOLD_PUBLIC_LEN - (2 * C2_PUBLIC_LEN); +/// Key-hash prefix length in `c2ab_chunk_fold` / `c4ab_fold` flattened public IO. +pub global C2AB_PREFIX_LEN: u32 = 5; pub global C4AB_PREFIX_LEN: u32 = C4AB_FOLD_PUBLIC_LEN - (2 * C4_PUBLIC_LEN); +pub global C2AB_MANIFEST_IDX: u32 = C2AB_FOLD_PUBLIC_LEN - 1; +pub global C3AB_MANIFEST_IDX: u32 = C3AB_FOLD_PUBLIC_LEN - 1; /// Inner C2 / C4 column offsets inside `c2ab_public` / `c4ab_public` /// (parallel to `NF_*` in `dkg_aggregator`; keep in sync). @@ -47,7 +49,8 @@ pub global C3AB_PK_B_START: u32 = C3AB_PREFIX_LEN + (3 * C3_SLOTS); pub global C3AB_MSG_B_START: u32 = C3AB_PREFIX_LEN + (4 * C3_SLOTS); /// Total public parameters + outputs for `node_fold`. -pub global NODE_FOLD_PUBLIC_LEN: u32 = 11 + N_PARTIES + (2 * (N_PARTIES + H) * L_THRESHOLD); +pub global NODE_FOLD_PUBLIC_LEN: u32 = 14 + N_PARTIES + (2 * (N_PARTIES + H) * L_THRESHOLD); +pub global NODE_VK_MANIFEST_IDX: u32 = NODE_FOLD_PUBLIC_LEN - 1; fn main( c0_vk: UltraHonkVerificationKey, @@ -72,7 +75,7 @@ fn main( c2ab_key_hash: pub Field, c3ab_key_hash: pub Field, c4ab_key_hash: pub Field, - ) -> pub (Field, Field, Field, [Field; N_PARTIES], [[Field; L_THRESHOLD]; N_PARTIES], [[Field; L_THRESHOLD]; N_PARTIES], [[Field; L_THRESHOLD]; H], [[Field; L_THRESHOLD]; H], Field, Field) { + ) -> pub (Field, Field, Field, Field, Field, [Field; N_PARTIES], [[Field; L_THRESHOLD]; N_PARTIES], [[Field; L_THRESHOLD]; N_PARTIES], [[Field; L_THRESHOLD]; H], [[Field; L_THRESHOLD]; H], Field, Field, Field) { verify_honk_proof(c0_vk, c0_proof, c0_public, c0_key_hash); verify_honk_proof(c1_vk, c1_proof, c1_public, c1_key_hash); verify_honk_proof_non_zk(c2ab_vk, c2ab_proof, c2ab_public, c2ab_key_hash); @@ -87,9 +90,10 @@ fn main( let c0_pk = c0_public[0]; let c1_pk = c1_public[1]; - // Same-party C1 to C2: `c2ab_public` lays out two full C2 public vectors after a 3-field - // prefix (`C2AB_FOLD_PUBLIC_LEN` = 3 + 2 * `C2_PUBLIC_LEN`). For each inner C2 block, index 0 - // is `expected_secret_commitment`; these equal C1's sk- and e_sm-side public inputs [0] and [2]. + // Same-party C1 to C2: `c2ab_public` lays out two full C2 public vectors after a 5-field + // prefix, with a trailing manifest field (`C2AB_FOLD_PUBLIC_LEN` = 6 + 2 * `C2_PUBLIC_LEN`). + // For each inner C2 block, index 0 + // is the secret root commitment; these equal C1's sk- and e_sm-side public inputs [0] and [2]. assert(c1_public[0] == c2ab_public[C2A_INNER_START]); assert(c1_public[2] == c2ab_public[C2B_INNER_START]); @@ -150,6 +154,21 @@ fn main( } let sk_agg = c4ab_public[SK_AGG_INDEX]; let esm_agg = c4ab_public[ESM_AGG_INDEX]; - - (key_hash, c0_pk, c1_pk, c3_pk, c2a, c2b, c4a_exp, c4b_exp, sk_agg, esm_agg) + let c4_manifest = compute_vk_hash([c4ab_public[0], c4ab_public[1]].as_vector()); + let vk_manifest = compute_vk_hash([ + c0_key_hash, + c1_key_hash, + c2ab_key_hash, + c3ab_key_hash, + c4ab_key_hash, + c2ab_public[C2AB_MANIFEST_IDX], + c3ab_public[C3AB_MANIFEST_IDX], + c4_manifest, + ] + .as_vector()); + + ( + key_hash, c2ab_public[3], c2ab_public[4], c0_pk, c1_pk, c3_pk, c2a, c2b, c4a_exp, c4b_exp, + sk_agg, esm_agg, vk_manifest, + ) } diff --git a/circuits/bin/recursive_aggregation/nodes_fold/src/main.nr b/circuits/bin/recursive_aggregation/nodes_fold/src/main.nr index 60599b3bcd..8a8219bfaf 100644 --- a/circuits/bin/recursive_aggregation/nodes_fold/src/main.nr +++ b/circuits/bin/recursive_aggregation/nodes_fold/src/main.nr @@ -13,21 +13,30 @@ use lib::configs::default::dkg::L_THRESHOLD; use lib::configs::default::{H, N_PARTIES}; pub global C2_PUBLIC_LEN: u32 = 1 + (N_PARTIES * L_THRESHOLD); -pub global C2AB_FOLD_PUBLIC_LEN: u32 = 3 + (2 * C2_PUBLIC_LEN); +pub global C2AB_FOLD_PUBLIC_LEN: u32 = 6 + (2 * C2_PUBLIC_LEN); /// Must match `c3ab_fold::C3AB_FOLD_PUBLIC_LEN` (one C3 slot per party and CRT limb). pub global C3_SLOTS: u32 = N_PARTIES * L_THRESHOLD; -pub global C3AB_FOLD_PUBLIC_LEN: u32 = 2 + 1 + (6 * C3_SLOTS); +pub global C3AB_FOLD_PUBLIC_LEN: u32 = 2 + 2 + (6 * C3_SLOTS); pub global C4_PUBLIC_LEN: u32 = (H * L_THRESHOLD) + 1; pub global C4AB_FOLD_PUBLIC_LEN: u32 = 3 + (2 * C4_PUBLIC_LEN); /// Must match `node_fold::NODE_FOLD_PUBLIC_LEN`. -pub global NODE_FOLD_PUBLIC_LEN: u32 = 11 + N_PARTIES + (2 * (N_PARTIES + H) * L_THRESHOLD); +pub global NODE_FOLD_PUBLIC_LEN: u32 = 14 + N_PARTIES + (2 * (N_PARTIES + H) * L_THRESHOLD); -/// Same shape as `c6_fold`: 4 pub parameters + `H` full `node_fold` public rows (flattened). -pub global NODES_FOLD_PUBLIC_LEN: u32 = 4 + (H * NODE_FOLD_PUBLIC_LEN); +/// Same shape as `c6_fold`: 6 pub parameters + `H` full `node_fold` public rows (flattened). +pub global NODES_FOLD_PUBLIC_LEN: u32 = 6 + (H * NODE_FOLD_PUBLIC_LEN); pub global NODES_FOLD_PARAM_PUBLIC_LEN: u32 = NODES_FOLD_PUBLIC_LEN - (H * NODE_FOLD_PUBLIC_LEN); +/// Named indices into the 6-field accumulator prefix (`nodes_fold` and `nodes_fold_kernel` +/// share this layout; `dkg_aggregator` uses the same offsets). +pub global ACC_CHILD_VK_IDX: u32 = 0; +pub global ACC_ACC_VK_IDX: u32 = 1; +pub global ACC_IS_FIRST_STEP_IDX: u32 = 2; +pub global ACC_SLOT_INDEX_IDX: u32 = 3; +pub global ACC_EXPECTED_KERNEL_IDX: u32 = 4; +pub global ACC_EXPECTED_FOLD_IDX: u32 = 5; + fn main( inner_vk: UltraHonkVerificationKey, inner_proof: UltraHonkProof, @@ -39,9 +48,27 @@ fn main( acc_key_hash: pub Field, is_first_step: pub bool, slot_index: pub u32, + expected_kernel_key_hash: pub Field, + expected_fold_key_hash: pub Field, ) -> pub [[Field; NODE_FOLD_PUBLIC_LEN]; H] { assert(slot_index < H); + // Bind every fold step to one canonical child VK: the previous accumulator's child VK hash + // (public input 0) must equal this step's inner `node_fold` VK hash. This closes the VK + // substitution attack where an intermediate step is proven under a noncanonical child VK. + assert(acc_public_inputs[ACC_CHILD_VK_IDX] == inner_key_hash); + + if is_first_step { + assert(acc_key_hash == expected_kernel_key_hash); + } else { + assert(acc_key_hash == expected_fold_key_hash); + if acc_public_inputs[ACC_IS_FIRST_STEP_IDX] == 1 { + assert(acc_public_inputs[ACC_EXPECTED_KERNEL_IDX] == expected_kernel_key_hash); + } else { + assert(acc_public_inputs[ACC_EXPECTED_FOLD_IDX] == expected_fold_key_hash); + } + } + verify_honk_proof_non_zk( inner_vk, inner_proof, diff --git a/circuits/bin/recursive_aggregation/nodes_fold_kernel/src/main.nr b/circuits/bin/recursive_aggregation/nodes_fold_kernel/src/main.nr index adbda9b37f..7ed2d60398 100644 --- a/circuits/bin/recursive_aggregation/nodes_fold_kernel/src/main.nr +++ b/circuits/bin/recursive_aggregation/nodes_fold_kernel/src/main.nr @@ -13,18 +13,18 @@ use lib::configs::default::dkg::L_THRESHOLD; use lib::configs::default::{H, N_PARTIES}; pub global C2_PUBLIC_LEN: u32 = 1 + (N_PARTIES * L_THRESHOLD); -pub global C2AB_FOLD_PUBLIC_LEN: u32 = 3 + (2 * C2_PUBLIC_LEN); +pub global C2AB_FOLD_PUBLIC_LEN: u32 = 6 + (2 * C2_PUBLIC_LEN); /// Must match `c3ab_fold::C3AB_FOLD_PUBLIC_LEN` (one C3 slot per party and CRT limb). pub global C3_SLOTS: u32 = N_PARTIES * L_THRESHOLD; -pub global C3AB_FOLD_PUBLIC_LEN: u32 = 2 + 1 + (6 * C3_SLOTS); +pub global C3AB_FOLD_PUBLIC_LEN: u32 = 2 + 2 + (6 * C3_SLOTS); pub global C4_PUBLIC_LEN: u32 = (H * L_THRESHOLD) + 1; pub global C4AB_FOLD_PUBLIC_LEN: u32 = 3 + (2 * C4_PUBLIC_LEN); /// Must match `node_fold::NODE_FOLD_PUBLIC_LEN`. -pub global NODE_FOLD_PUBLIC_LEN: u32 = 11 + N_PARTIES + (2 * (N_PARTIES + H) * L_THRESHOLD); +pub global NODE_FOLD_PUBLIC_LEN: u32 = 14 + N_PARTIES + (2 * (N_PARTIES + H) * L_THRESHOLD); -/// Same shape as `nodes_fold`: 4 pub parameters + `H` full `node_fold` public rows (flattened). -pub global NODES_FOLD_PUBLIC_LEN: u32 = 4 + (H * NODE_FOLD_PUBLIC_LEN); +/// Same shape as `nodes_fold`: 6 pub parameters + `H` full `node_fold` public rows (flattened). +pub global NODES_FOLD_PUBLIC_LEN: u32 = 6 + (H * NODE_FOLD_PUBLIC_LEN); fn main( inner_vk: UltraHonkVerificationKey, @@ -37,6 +37,8 @@ fn main( acc_key_hash: pub Field, is_first_step: pub bool, slot_index: pub u32, + expected_kernel_key_hash: pub Field, + expected_fold_key_hash: pub Field, ) -> pub [[Field; NODE_FOLD_PUBLIC_LEN]; H] { assert(slot_index < H); @@ -48,9 +50,10 @@ fn main( ); // Signature parity with `nodes_fold`: see `c3_fold_kernel`. - let _ = (acc_vk, acc_proof, acc_key_hash, acc_public_inputs); + let _ = (acc_vk, acc_proof, acc_key_hash, acc_public_inputs, expected_fold_key_hash); assert(is_first_step); + assert(acc_key_hash == expected_kernel_key_hash); let mut out: [[Field; NODE_FOLD_PUBLIC_LEN]; H] = [[0; NODE_FOLD_PUBLIC_LEN]; H]; diff --git a/circuits/bin/dkg/sk_share_computation/Nargo.toml b/circuits/bin/recursive_aggregation/sk_c2_chunk_finalize/Nargo.toml similarity index 76% rename from circuits/bin/dkg/sk_share_computation/Nargo.toml rename to circuits/bin/recursive_aggregation/sk_c2_chunk_finalize/Nargo.toml index 5c2994be70..8bdfa72626 100644 --- a/circuits/bin/dkg/sk_share_computation/Nargo.toml +++ b/circuits/bin/recursive_aggregation/sk_c2_chunk_finalize/Nargo.toml @@ -1,7 +1,7 @@ [package] -name = "sk_share_computation" +name = "sk_c2_chunk_finalize" type = "bin" -authors = [""] +authors = ["Gnosis Guild / Interfold"] [dependencies] lib = { path = "../../../lib" } diff --git a/circuits/bin/recursive_aggregation/sk_c2_chunk_finalize/src/main.nr b/circuits/bin/recursive_aggregation/sk_c2_chunk_finalize/src/main.nr new file mode 100644 index 0000000000..c3fcbd6d30 --- /dev/null +++ b/circuits/bin/recursive_aggregation/sk_c2_chunk_finalize/src/main.nr @@ -0,0 +1,78 @@ +// 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. + +//! Verifies C2 chunk batches and reconstructs the existing SK C2 public layout. + +use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; +use lib::configs::default::dkg::{ + L_THRESHOLD, N, SHARE_COMPUTATION_CHUNK_SIZE as CHUNK_SIZE, + SHARE_COMPUTATION_CHUNKS_PER_BATCH as CHUNKS_PER_BATCH, + SHARE_COMPUTATION_N_BATCHES as N_BATCHES, SHARE_COMPUTATION_N_CHUNKS as N_CHUNKS, +}; +use lib::configs::default::N_PARTIES; +use lib::math::commitments::{ + compute_sc_party_share_chunk_root_commitment, compute_sc_secret_chunk_root_commitment, +}; + +pub global CHUNK_COMMITMENT_LEN: u32 = 1 + (N_PARTIES * L_THRESHOLD); +pub global BATCH_RETURN_LEN: u32 = 2 + (CHUNKS_PER_BATCH * CHUNK_COMMITMENT_LEN); +pub global BATCH_PUBLIC_LEN: u32 = 2 + BATCH_RETURN_LEN; +pub global C2_PUBLIC_LEN: u32 = 1 + (N_PARTIES * L_THRESHOLD); +pub global C2_FINALIZED_PUBLIC_LEN: u32 = C2_PUBLIC_LEN + 2; + +fn main( + batch_vk: UltraHonkVerificationKey, + batch_proofs: [UltraHonkProof; N_BATCHES], + batch_public_inputs: [[Field; BATCH_PUBLIC_LEN]; N_BATCHES], + batch_key_hash: Field, +) -> pub [Field; C2_FINALIZED_PUBLIC_LEN] { + assert(N_BATCHES * CHUNKS_PER_BATCH == N_CHUNKS); + + let mut secret_chunks: [Field; N_CHUNKS] = [0; N_CHUNKS]; + let mut share_chunks: [[Field; N_CHUNKS]; N_PARTIES * L_THRESHOLD] = + [[0; N_CHUNKS]; N_PARTIES * L_THRESHOLD]; + + for batch_idx in 0..N_BATCHES { + verify_honk_proof_non_zk( + batch_vk, + batch_proofs[batch_idx], + batch_public_inputs[batch_idx], + batch_key_hash, + ); + assert(batch_public_inputs[batch_idx][1] == batch_idx as Field); + assert(batch_public_inputs[batch_idx][2] == batch_idx as Field); + assert(batch_public_inputs[batch_idx][0] == batch_public_inputs[0][0]); + + for chunk_in_batch in 0..CHUNKS_PER_BATCH { + let chunk_idx = batch_idx * CHUNKS_PER_BATCH + chunk_in_batch; + let start = 4 + chunk_in_batch * CHUNK_COMMITMENT_LEN; + secret_chunks[chunk_idx] = batch_public_inputs[batch_idx][start]; + for slot in 0..(N_PARTIES * L_THRESHOLD) { + share_chunks[slot][chunk_idx] = batch_public_inputs[batch_idx][start + 1 + slot]; + } + } + } + + let mut output: [Field; C2_FINALIZED_PUBLIC_LEN] = [0; C2_FINALIZED_PUBLIC_LEN]; + output[0] = batch_public_inputs[0][0]; + output[1] = compute_sc_secret_chunk_root_commitment(0, N, CHUNK_SIZE, secret_chunks); + for party_idx in 0..N_PARTIES { + for mod_idx in 0..L_THRESHOLD { + let slot = party_idx * L_THRESHOLD + mod_idx; + output[2 + slot] = compute_sc_party_share_chunk_root_commitment( + party_idx, + mod_idx, + N, + CHUNK_SIZE, + share_chunks[slot], + ); + } + } + // Preserve the batch VK hash in the terminal statement. This lets the + // parent C2AB fold bind the complete batch -> finalizer chain. + output[C2_FINALIZED_PUBLIC_LEN - 1] = batch_key_hash; + output +} diff --git a/circuits/bin/threshold/pk_generation/src/main.nr b/circuits/bin/threshold/pk_generation/src/main.nr index 287fbac7e2..a2cb179fbe 100644 --- a/circuits/bin/threshold/pk_generation/src/main.nr +++ b/circuits/bin/threshold/pk_generation/src/main.nr @@ -4,6 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +use lib::configs::default::dkg::{SHARE_COMPUTATION_CHUNK_SIZE, SHARE_COMPUTATION_N_CHUNKS}; use lib::configs::default::threshold::{ CRP, L, N, PK_GENERATION_BIT_E_SM, PK_GENERATION_BIT_EEK, PK_GENERATION_BIT_PK, PK_GENERATION_BIT_R1, PK_GENERATION_BIT_R2, PK_GENERATION_BIT_SK, PK_GENERATION_CONFIGS, @@ -19,7 +20,7 @@ fn main( r2is: [Polynomial; L], pk0is: [Polynomial; L], ) -> pub (Field, Field, Field) { - let pk_generation: PkGeneration = + let pk_generation: PkGeneration = PkGeneration::new(PK_GENERATION_CONFIGS, CRP, eek, sk, e_sm, r1is, r2is, pk0is); pk_generation.execute() } diff --git a/circuits/lib/src/configs/committee/active.nr b/circuits/lib/src/configs/committee/active.nr index e69c7c894f..76b1132c3d 100644 --- a/circuits/lib/src/configs/committee/active.nr +++ b/circuits/lib/src/configs/committee/active.nr @@ -26,3 +26,6 @@ pub global H: u32 = crate::configs::committee::minimum::H; /// `configs::{insecure,secure}::dkg` re-exports the relevant one as `PARITY_MATRIX`. pub use crate::configs::committee::minimum::parity_insecure::PARITY_MATRIX as PARITY_MATRIX_INSECURE; pub use crate::configs::committee::minimum::parity_secure::PARITY_MATRIX as PARITY_MATRIX_SECURE; +pub use crate::configs::committee::minimum::smudging::{ + INSECURE_E_SM_BIT, INSECURE_E_SM_BOUND, SECURE_E_SM_BIT, SECURE_E_SM_BOUND, +}; diff --git a/circuits/lib/src/configs/committee/micro/mod.nr b/circuits/lib/src/configs/committee/micro/mod.nr index 5d10b0e01a..d24d684739 100644 --- a/circuits/lib/src/configs/committee/micro/mod.nr +++ b/circuits/lib/src/configs/committee/micro/mod.nr @@ -16,3 +16,4 @@ pub global H: u32 = 5; pub mod parity_insecure; pub mod parity_secure; +pub mod smudging; diff --git a/circuits/lib/src/configs/committee/micro/smudging.nr b/circuits/lib/src/configs/committee/micro/smudging.nr new file mode 100644 index 0000000000..2fd32f4fd7 --- /dev/null +++ b/circuits/lib/src/configs/committee/micro/smudging.nr @@ -0,0 +1,15 @@ +// 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. +// +// AUTO-GENERATED by `generate_parity_matrices`. DO NOT EDIT BY HAND. +// Regenerate with `pnpm build:circuits --committee micro`. +// +// C1 and C2b must use the same smudging-noise width as Rust witness generation. + +pub global INSECURE_E_SM_BIT: u32 = 30; +pub global INSECURE_E_SM_BOUND: Field = 755367936; +pub global SECURE_E_SM_BIT: u32 = 143; +pub global SECURE_E_SM_BOUND: Field = 8307674973655727997247334808482217984000000; diff --git a/circuits/lib/src/configs/committee/minimum/mod.nr b/circuits/lib/src/configs/committee/minimum/mod.nr index 5834eee659..8fc1a31bd8 100644 --- a/circuits/lib/src/configs/committee/minimum/mod.nr +++ b/circuits/lib/src/configs/committee/minimum/mod.nr @@ -16,3 +16,4 @@ pub global H: u32 = 2; pub mod parity_insecure; pub mod parity_secure; +pub mod smudging; diff --git a/circuits/lib/src/configs/committee/minimum/smudging.nr b/circuits/lib/src/configs/committee/minimum/smudging.nr new file mode 100644 index 0000000000..c50d1d9b8a --- /dev/null +++ b/circuits/lib/src/configs/committee/minimum/smudging.nr @@ -0,0 +1,15 @@ +// 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. +// +// AUTO-GENERATED by `generate_parity_matrices`. DO NOT EDIT BY HAND. +// Regenerate with `pnpm build:circuits --committee minimum`. +// +// C1 and C2b must use the same smudging-noise width as Rust witness generation. + +pub global INSECURE_E_SM_BIT: u32 = 28; +pub global INSECURE_E_SM_BOUND: Field = 252051456; +pub global SECURE_E_SM_BIT: u32 = 143; +pub global SECURE_E_SM_BOUND: Field = 8307674973655725783638045963336024064000000; diff --git a/circuits/lib/src/configs/committee/small/mod.nr b/circuits/lib/src/configs/committee/small/mod.nr index 06b26af221..9a0a9c9181 100644 --- a/circuits/lib/src/configs/committee/small/mod.nr +++ b/circuits/lib/src/configs/committee/small/mod.nr @@ -13,3 +13,4 @@ pub global H: u32 = 10; pub mod parity_insecure; pub mod parity_secure; +pub mod smudging; diff --git a/circuits/lib/src/configs/committee/small/smudging.nr b/circuits/lib/src/configs/committee/small/smudging.nr new file mode 100644 index 0000000000..d5fccca90c --- /dev/null +++ b/circuits/lib/src/configs/committee/small/smudging.nr @@ -0,0 +1,15 @@ +// 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. +// +// AUTO-GENERATED by `generate_parity_matrices`. DO NOT EDIT BY HAND. +// Regenerate with `pnpm build:circuits --committee small`. +// +// C1 and C2b must use the same smudging-noise width as Rust witness generation. + +pub global INSECURE_E_SM_BIT: u32 = 31; +pub global INSECURE_E_SM_BOUND: Field = 1594228736; +pub global SECURE_E_SM_BIT: u32 = 143; +pub global SECURE_E_SM_BOUND: Field = 8307674973655731686596149550392541184000000; diff --git a/circuits/lib/src/configs/insecure/dkg.nr b/circuits/lib/src/configs/insecure/dkg.nr index 2f2dc95c1a..e2b8dc2725 100644 --- a/circuits/lib/src/configs/insecure/dkg.nr +++ b/circuits/lib/src/configs/insecure/dkg.nr @@ -5,8 +5,8 @@ // or FITNESS FOR A PARTICULAR PURPOSE. pub use crate::configs::insecure::threshold::{ - L as L_THRESHOLD, QIS as QIS_THRESHOLD, - THRESHOLD_SHARE_DECRYPTION_BIT_SK as SHARE_DECRYPTION_BIT_AGG, + L as L_THRESHOLD, PK_GENERATION_BIT_E_SM as SHARE_COMPUTATION_E_SM_BIT_SECRET, + QIS as QIS_THRESHOLD, THRESHOLD_SHARE_DECRYPTION_BIT_SK as SHARE_DECRYPTION_BIT_AGG, }; use crate::core::dkg::share_computation::Configs as ShareComputationConfigs; use crate::core::dkg::share_encryption::Configs as ShareEncryptionConfigs; @@ -40,6 +40,10 @@ share_computation_sk (CIRCUIT 2a) pub global SHARE_COMPUTATION_BIT_SHARE: u32 = 36; pub global SHARE_COMPUTATION_SK_BIT_SECRET: u32 = 1; +pub global SHARE_COMPUTATION_CHUNK_SIZE: u32 = 512; +pub global SHARE_COMPUTATION_N_CHUNKS: u32 = 1; +pub global SHARE_COMPUTATION_CHUNKS_PER_BATCH: u32 = 1; +pub global SHARE_COMPUTATION_N_BATCHES: u32 = 1; pub global SHARE_COMPUTATION_SK_CONFIGS: ShareComputationConfigs = ShareComputationConfigs::new(QIS_THRESHOLD); @@ -50,8 +54,6 @@ share_computation_e_sm (CIRCUIT 2b) ------------------------------------- ************************************/ -pub global SHARE_COMPUTATION_E_SM_BIT_SECRET: u32 = 28; - pub global SHARE_COMPUTATION_E_SM_CONFIGS: ShareComputationConfigs = ShareComputationConfigs::new(QIS_THRESHOLD); diff --git a/circuits/lib/src/configs/insecure/threshold.nr b/circuits/lib/src/configs/insecure/threshold.nr index 8163f6e1a9..35c9f846bf 100644 --- a/circuits/lib/src/configs/insecure/threshold.nr +++ b/circuits/lib/src/configs/insecure/threshold.nr @@ -12,6 +12,10 @@ use crate::core::threshold::user_data_encryption_ct0::Configs as UserDataEncrypt use crate::core::threshold::user_data_encryption_ct1::Configs as UserDataEncryptionCt1Configs; use crate::math::polynomial::Polynomial; +pub use crate::configs::committee::active::{ + INSECURE_E_SM_BIT as PK_GENERATION_BIT_E_SM, INSECURE_E_SM_BOUND as PK_GENERATION_E_SM_BOUND, +}; + // Global configs for threshold insecure preset pub global N: u32 = 512; pub global L: u32 = 2; @@ -1060,14 +1064,12 @@ pk_generation (CIRCUIT 1 - PUBLIC KEY THRESHOLD BFV) pub global PK_GENERATION_BIT_EEK: u32 = 5; pub global PK_GENERATION_BIT_SK: u32 = 1; -pub global PK_GENERATION_BIT_E_SM: u32 = 28; pub global PK_GENERATION_BIT_R1: u32 = 9; pub global PK_GENERATION_BIT_R2: u32 = 35; pub global PK_GENERATION_BIT_PK: u32 = 35; pub global PK_GENERATION_EEK_BOUND: Field = 20; pub global PK_GENERATION_SK_BOUND: Field = 1; -pub global PK_GENERATION_E_SM_BOUND: Field = 252051456; pub global PK_GENERATION_R1_BOUNDS: [Field; L] = [256, 256]; pub global PK_GENERATION_R2_BOUNDS: [Field; L] = [34359701504, 34359615488]; diff --git a/circuits/lib/src/configs/secure/dkg.nr b/circuits/lib/src/configs/secure/dkg.nr index 6d95e76527..6a69e39516 100644 --- a/circuits/lib/src/configs/secure/dkg.nr +++ b/circuits/lib/src/configs/secure/dkg.nr @@ -5,8 +5,8 @@ // or FITNESS FOR A PARTICULAR PURPOSE. pub use crate::configs::secure::threshold::{ - L as L_THRESHOLD, QIS as QIS_THRESHOLD, - THRESHOLD_SHARE_DECRYPTION_BIT_SK as SHARE_DECRYPTION_BIT_AGG, + L as L_THRESHOLD, PK_GENERATION_BIT_E_SM as SHARE_COMPUTATION_E_SM_BIT_SECRET, + QIS as QIS_THRESHOLD, THRESHOLD_SHARE_DECRYPTION_BIT_SK as SHARE_DECRYPTION_BIT_AGG, }; use crate::core::dkg::share_computation::Configs as ShareComputationConfigs; use crate::core::dkg::share_encryption::Configs as ShareEncryptionConfigs; @@ -42,6 +42,10 @@ share_computation_sk (CIRCUIT 2a) // share_computation_sk - bit parameters pub global SHARE_COMPUTATION_BIT_SHARE: u32 = 58; pub global SHARE_COMPUTATION_SK_BIT_SECRET: u32 = 1; +pub global SHARE_COMPUTATION_CHUNK_SIZE: u32 = 512; +pub global SHARE_COMPUTATION_N_CHUNKS: u32 = 16; +pub global SHARE_COMPUTATION_CHUNKS_PER_BATCH: u32 = 4; +pub global SHARE_COMPUTATION_N_BATCHES: u32 = 4; // share_computation_sk - configs pub global SHARE_COMPUTATION_SK_CONFIGS: ShareComputationConfigs = @@ -53,9 +57,6 @@ share_computation_e_sm (CIRCUIT 2b) ------------------------------------- ************************************/ -// share_computation_e_sm - bit parameters -pub global SHARE_COMPUTATION_E_SM_BIT_SECRET: u32 = 143; - // verify_shares - configs pub global SHARE_COMPUTATION_E_SM_CONFIGS: ShareComputationConfigs = ShareComputationConfigs::new(QIS_THRESHOLD); diff --git a/circuits/lib/src/configs/secure/threshold.nr b/circuits/lib/src/configs/secure/threshold.nr index 93cece8081..3b4819dc60 100644 --- a/circuits/lib/src/configs/secure/threshold.nr +++ b/circuits/lib/src/configs/secure/threshold.nr @@ -12,6 +12,10 @@ use crate::core::threshold::user_data_encryption_ct0::Configs as UserDataEncrypt use crate::core::threshold::user_data_encryption_ct1::Configs as UserDataEncryptionCt1Configs; use crate::math::polynomial::Polynomial; +pub use crate::configs::committee::active::{ + SECURE_E_SM_BIT as PK_GENERATION_BIT_E_SM, SECURE_E_SM_BOUND as PK_GENERATION_E_SM_BOUND, +}; + /// Threshold BFV parameter set search defaults configurations. /// These are for the SecureThreshold8192 preset. /// The InsecureThreshold512 preset has been generated manually. @@ -24620,14 +24624,12 @@ pk_generation (CIRCUIT 1 - PUBLIC KEY THRESHOLD BFV) pub global PK_GENERATION_BIT_EEK: u32 = 5; pub global PK_GENERATION_BIT_SK: u32 = 1; -pub global PK_GENERATION_BIT_E_SM: u32 = 143; pub global PK_GENERATION_BIT_R1: u32 = 13; pub global PK_GENERATION_BIT_R2: u32 = 57; pub global PK_GENERATION_BIT_PK: u32 = 57; pub global PK_GENERATION_EEK_BOUND: Field = 20; pub global PK_GENERATION_SK_BOUND: Field = 1; -pub global PK_GENERATION_E_SM_BOUND: Field = 8307674973655731686596149550392541184000000; pub global PK_GENERATION_R1_BOUNDS: [Field; L] = [4096, 4096, 4096]; pub global PK_GENERATION_R2_BOUNDS: [Field; L] = [72057594049265664, 72057594048610304, 72057594047397888]; diff --git a/circuits/lib/src/core/dkg/share_computation.nr b/circuits/lib/src/core/dkg/share_computation.nr index ad904beda5..733bc9b331 100644 --- a/circuits/lib/src/core/dkg/share_computation.nr +++ b/circuits/lib/src/core/dkg/share_computation.nr @@ -5,8 +5,8 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use crate::math::commitments::{ - compute_share_computation_e_sm_commitment, compute_share_computation_sk_commitment, - compute_share_encryption_commitment_from_message, + compute_sc_esm_secret_root_from_polynomial, compute_sc_party_share_root_from_polynomial, + compute_sc_sk_secret_root_from_polynomial, }; use crate::math::modulo::U64::ModU64; use crate::math::polynomial::Polynomial; @@ -32,7 +32,7 @@ impl Configs { /// 4. Parity check: H[j] * y[i][j]^T == 0 mod q_j for all i, j /// /// For SK: sk_secret is the trinary coefficients -pub struct SecretKeyShareComputation { +pub struct SecretKeyShareComputation { configs: Configs, /// Expected commitment to secret (from C1) /// (public witness) @@ -61,7 +61,7 @@ pub struct SecretKeyShareComputation { +pub struct SmudgingNoiseShareComputation { configs: Configs, /// Expected commitment to secret (from C1) /// This is computed from all L RNS polynomials (matching @@ -79,7 +79,7 @@ pub struct SmudgingNoiseShareComputation SecretKeyShareComputation { +impl SecretKeyShareComputation { pub fn new( configs: Configs, expected_secret_commitment: Field, @@ -105,7 +105,7 @@ impl(self.configs.qis, self.h, self.y); // Step 5: Commit to shares for each party and modulus - commit_to_party_shares::(self.y) + commit_to_party_shares::(self.y) } /// Verifies that secret hashes to expected_secret_commitment. @@ -120,7 +120,9 @@ impl(reversed_sk) + compute_sc_sk_secret_root_from_polynomial::( + reversed_sk, + ) == self.expected_secret_commitment, "SK commitment mismatch", ); @@ -149,7 +151,7 @@ impl SmudgingNoiseShareComputation { +impl SmudgingNoiseShareComputation { pub fn new( configs: Configs, expected_secret_commitment: Field, @@ -175,7 +177,7 @@ impl(self.configs.qis, self.h, self.y); // Step 5: Commit to shares for each party and modulus - commit_to_party_shares::(self.y) + commit_to_party_shares::(self.y) } /// Verifies that secret hashes to expected_secret_commitment. @@ -201,7 +203,9 @@ impl(normalized) + compute_sc_esm_secret_root_from_polynomial::( + normalized, + ) == self.expected_secret_commitment, "ESM commitment mismatch", ); @@ -302,8 +306,8 @@ pub fn verify_parity_check( +/// equals the root commitment for the reversed share polynomial. +pub fn commit_to_party_shares( y: [[[Field; N_PARTIES + 1]; L]; N], ) -> [[Field; L]; N_PARTIES] { let mut commitments: [[Field; L]; N_PARTIES] = [[0; L]; N_PARTIES]; @@ -319,8 +323,11 @@ pub fn commit_to_party_shares(share_poly); + commitments[party_idx][mod_idx] = compute_sc_party_share_root_from_polynomial::( + party_idx, + mod_idx, + share_poly, + ); } } diff --git a/circuits/lib/src/core/dkg/share_decryption.nr b/circuits/lib/src/core/dkg/share_decryption.nr index 43afd4990a..f4b8be486e 100644 --- a/circuits/lib/src/core/dkg/share_decryption.nr +++ b/circuits/lib/src/core/dkg/share_decryption.nr @@ -5,7 +5,7 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use crate::math::commitments::{ - compute_aggregated_shares_commitment, compute_share_encryption_commitment_from_message, + compute_aggregated_shares_commitment, compute_sc_party_share_root_from_polynomial, }; use crate::math::modulo::U64::ModU64; use crate::math::polynomial::Polynomial; @@ -23,7 +23,7 @@ use crate::math::polynomial::Polynomial; /// /// `BIT_MSG` hashes per-share plaintexts (aligned with C2). `BIT_AGG` hashes the CRT aggregate /// (`compute_aggregated_shares_commitment`) and must match threshold C6 `BIT_SK` / `BIT_E_SM`. -pub struct ShareDecryption { +pub struct ShareDecryption { /// Expected commitments to the share polynomials, produced in C2a (for C4a) or C2b (for C4b) /// via commit_to_party_shares. Organised as [party_idx][mod_idx], covering all H honest /// parties and L CRT moduli. @@ -34,14 +34,18 @@ pub struct ShareDecryption; L]; H], + + /// Zero-based recipient party index used in the C2 commitment domain. + recipient_party_idx: u32, } -impl ShareDecryption { +impl ShareDecryption { pub fn new( expected_commitments: [[Field; L]; H], decrypted_shares: [[Polynomial; L]; H], + recipient_party_idx: u32, ) -> Self { - ShareDecryption { expected_commitments, decrypted_shares } + ShareDecryption { expected_commitments, decrypted_shares, recipient_party_idx } } /// Verifies that each decrypted share matches its expected commitment from C2a/C2b. @@ -61,9 +65,11 @@ impl Sha reversed_coeffs[N - 1 - i] = share.coefficients[i]; } assert( - compute_share_encryption_commitment_from_message::(Polynomial::new( - reversed_coeffs, - )) + compute_sc_party_share_root_from_polynomial::( + self.recipient_party_idx, + mod_idx, + Polynomial::new(reversed_coeffs), + ) == self.expected_commitments[party_idx][mod_idx], "Commitment mismatch", ); diff --git a/circuits/lib/src/core/dkg/share_encryption.nr b/circuits/lib/src/core/dkg/share_encryption.nr index 26c3de08d6..c7df0d04c6 100644 --- a/circuits/lib/src/core/dkg/share_encryption.nr +++ b/circuits/lib/src/core/dkg/share_encryption.nr @@ -5,8 +5,8 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use crate::math::commitments::{ - compute_ciphertext_commitment, compute_share_encryption_challenge, - compute_share_encryption_commitment_from_message, + compute_ciphertext_commitment, compute_sc_party_share_root_from_polynomial, + compute_share_encryption_challenge, }; use crate::math::commitments::compute_dkg_pk_commitment; use crate::math::helpers::flatten; @@ -89,9 +89,11 @@ impl Configs { /// **Consumes:** `expected_pk_commitment` (C0); `expected_message_commitment` (C2 sk or C2 e_sm). /// /// **Produces:** Public `ct_commitment` (hash of ct0/ct1 limbs) for cross-phase binding; witness ct0/ct1 feed C4 off-transcript. -pub struct ShareEncryption { +pub struct ShareEncryption { /// Circuit parameters configs: Configs, + party_idx: u32, + mod_idx: u32, /// Expected commitment to the DKG public key (from C0: pk). /// (public witness) expected_pk_commitment: Field, @@ -130,11 +132,13 @@ pub struct ShareEncryption; L], } -impl ShareEncryption { +impl ShareEncryption { pub fn new( configs: Configs, expected_pk_commitment: Field, expected_message_commitment: Field, + party_idx: u32, + mod_idx: u32, pk0is: [Polynomial; L], pk1is: [Polynomial; L], ct0is: [Polynomial; L], @@ -152,6 +156,8 @@ impl Self { ShareEncryption { configs, + party_idx, + mod_idx, expected_pk_commitment, expected_message_commitment, pk0is, @@ -191,7 +197,11 @@ impl(self.message) + compute_sc_party_share_root_from_polynomial::( + self.party_idx, + self.mod_idx, + self.message, + ) == self.expected_message_commitment, "Message commitment mismatch", ); diff --git a/circuits/lib/src/core/threshold/pk_generation.nr b/circuits/lib/src/core/threshold/pk_generation.nr index b15dbf7703..3541f48684 100644 --- a/circuits/lib/src/core/threshold/pk_generation.nr +++ b/circuits/lib/src/core/threshold/pk_generation.nr @@ -5,7 +5,7 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use crate::math::commitments::{ - compute_share_computation_e_sm_commitment, compute_share_computation_sk_commitment, + compute_sc_esm_secret_root_from_polynomial, compute_sc_sk_secret_root_from_polynomial, compute_threshold_pk_challenge, compute_threshold_pk_commitment, }; use crate::math::helpers::flatten; @@ -51,7 +51,7 @@ impl Configs { /// - `commit(sk)` -> C2a (share computation). /// - `commit(pk_trbfv)` -> C5 (pk aggregation). /// - `commit(e_sm)` -> C2b (share computation). -pub struct PkGeneration { +pub struct PkGeneration { /// Cryptographic parameters including bounds, moduli, and constants. configs: Configs, @@ -81,7 +81,7 @@ pub struct PkGeneration; L], } -impl PkGeneration { +impl PkGeneration { pub fn new( configs: Configs, a: [Polynomial; L], @@ -127,9 +127,11 @@ impl(self.sk); - let e_sm_commitment = - compute_share_computation_e_sm_commitment::(self.e_sm); + let sk_commitment = + compute_sc_sk_secret_root_from_polynomial::(self.sk); + let e_sm_commitment = compute_sc_esm_secret_root_from_polynomial::( + self.e_sm, + ); let pk_commitment = compute_threshold_pk_commitment::(self.pk0); // Step 3: Fiat-Shamir challenge and per-modulus evaluation checks. diff --git a/circuits/lib/src/math/commitments.nr b/circuits/lib/src/math/commitments.nr index b606be1173..2fdd264457 100644 --- a/circuits/lib/src/math/commitments.nr +++ b/circuits/lib/src/math/commitments.nr @@ -30,6 +30,13 @@ pub global DS_SHARE_COMPUTATION: [u8; 64] = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; +// Domain separator - "SHARE_COMPUTATION_CHUNK" +pub global DS_SHARE_COMPUTATION_CHUNK: [u8; 64] = [ + 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f, + 0x4e, 0x5f, 0x43, 0x48, 0x55, 0x4e, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; // Domain separator - "SHARE_ENCRYPTION" pub global DS_SHARE_ENCRYPTION: [u8; 64] = [ 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50, 0x54, 0x49, 0x4f, 0x4e, @@ -197,6 +204,149 @@ pub fn compute_share_encryption_commitment_from_message( + kind_tag: Field, + chunk_idx: u32, + n: u32, + chunk_poly: Polynomial, +) -> Field { + let mut payload = + [0, kind_tag, chunk_idx as Field, n as Field, CHUNK_SIZE as Field].as_vector(); + payload = single_polynomial_payload::(payload, chunk_poly); + compute_commitment(payload, DS_SHARE_COMPUTATION_CHUNK) +} + +/// Compute a domain-separated commitment for one ESM chunk across all CRT limbs. +pub fn compute_sc_esm_secret_chunk_commitment( + chunk_idx: u32, + n: u32, + polynomials: [Polynomial; L], +) -> Field { + let mut payload = [0, 1, chunk_idx as Field, n as Field, CHUNK_SIZE as Field].as_vector(); + payload = multiple_polynomial_payload::(payload, polynomials); + compute_commitment(payload, DS_SHARE_COMPUTATION_CHUNK) +} + +/// Compute a domain-separated commitment for one party share chunk. +pub fn compute_sc_party_share_chunk_commitment( + party_idx: u32, + mod_idx: u32, + chunk_idx: u32, + n: u32, + chunk_poly: Polynomial, +) -> Field { + let mut payload = [ + 0, + 2, + party_idx as Field, + mod_idx as Field, + chunk_idx as Field, + n as Field, + CHUNK_SIZE as Field, + ] + .as_vector(); + payload = single_polynomial_payload::(payload, chunk_poly); + compute_commitment(payload, DS_SHARE_COMPUTATION_CHUNK) +} + +/// Compute the root commitment for secret chunk commitments. +pub fn compute_sc_secret_chunk_root_commitment( + kind_tag: Field, + n: u32, + chunk_size: u32, + chunk_commitments: [Field], +) -> Field { + let mut payload = [1, kind_tag, n as Field, chunk_size as Field].as_vector(); + for i in 0..chunk_commitments.len() { + payload = payload.push_back(chunk_commitments[i]); + } + compute_commitment(payload, DS_SHARE_COMPUTATION_CHUNK) +} + +/// Compute the root commitment for one party share and CRT modulus. +pub fn compute_sc_party_share_chunk_root_commitment( + party_idx: u32, + mod_idx: u32, + n: u32, + chunk_size: u32, + chunk_commitments: [Field], +) -> Field { + let mut payload = + [1, 2, party_idx as Field, mod_idx as Field, n as Field, chunk_size as Field].as_vector(); + for i in 0..chunk_commitments.len() { + payload = payload.push_back(chunk_commitments[i]); + } + compute_commitment(payload, DS_SHARE_COMPUTATION_CHUNK) +} + +/// Compute the SK root from the C1 polynomial orientation. +pub fn compute_sc_sk_secret_root_from_polynomial( + sk: Polynomial, +) -> Field { + let mut chunks: [Field; N_CHUNKS] = [0; N_CHUNKS]; + for chunk_idx in 0..N_CHUNKS { + let start = N - ((chunk_idx + 1) * CHUNK_SIZE); + let mut coefficients: [Field; CHUNK_SIZE] = [0; CHUNK_SIZE]; + for offset in 0..CHUNK_SIZE { + coefficients[offset] = sk.coefficients[start + offset]; + } + chunks[chunk_idx] = compute_sc_secret_chunk_commitment::( + 0, + chunk_idx, + N, + Polynomial::new(coefficients), + ); + } + compute_sc_secret_chunk_root_commitment(0, N, CHUNK_SIZE, chunks) +} + +/// Compute the ESM root from the C1 polynomial orientation. +pub fn compute_sc_esm_secret_root_from_polynomial( + e_sm: [Polynomial; L], +) -> Field { + let mut chunks: [Field; N_CHUNKS] = [0; N_CHUNKS]; + for chunk_idx in 0..N_CHUNKS { + let start = N - ((chunk_idx + 1) * CHUNK_SIZE); + let mut polynomials: [Polynomial; L] = [Polynomial::new([0; CHUNK_SIZE]); L]; + for mod_idx in 0..L { + let mut coefficients: [Field; CHUNK_SIZE] = [0; CHUNK_SIZE]; + for offset in 0..CHUNK_SIZE { + coefficients[offset] = e_sm[mod_idx].coefficients[start + offset]; + } + polynomials[mod_idx] = Polynomial::new(coefficients); + } + let mut payload = [0, 1, chunk_idx as Field, N as Field, CHUNK_SIZE as Field].as_vector(); + payload = multiple_polynomial_payload::(payload, polynomials); + chunks[chunk_idx] = compute_commitment(payload, DS_SHARE_COMPUTATION_CHUNK); + } + compute_sc_secret_chunk_root_commitment(1, N, CHUNK_SIZE, chunks) +} + +/// Compute a share root from a C3/C4 message polynomial. +pub fn compute_sc_party_share_root_from_polynomial( + party_idx: u32, + mod_idx: u32, + message: Polynomial, +) -> Field { + let mut chunks: [Field; N_CHUNKS] = [0; N_CHUNKS]; + for chunk_idx in 0..N_CHUNKS { + let start = N - ((chunk_idx + 1) * CHUNK_SIZE); + let mut coefficients: [Field; CHUNK_SIZE] = [0; CHUNK_SIZE]; + for offset in 0..CHUNK_SIZE { + coefficients[offset] = message.coefficients[start + offset]; + } + chunks[chunk_idx] = compute_sc_party_share_chunk_commitment::( + party_idx, + mod_idx, + chunk_idx, + N, + Polynomial::new(coefficients), + ); + } + compute_sc_party_share_chunk_root_commitment(party_idx, mod_idx, N, CHUNK_SIZE, chunks) +} + pub fn compute_aggregated_shares_commitment( agg_shares: [Polynomial; L], ) -> Field { diff --git a/crates/events/src/interfold_event/compute_request/zk.rs b/crates/events/src/interfold_event/compute_request/zk.rs index cefac7d858..4c0ac80943 100644 --- a/crates/events/src/interfold_event/compute_request/zk.rs +++ b/crates/events/src/interfold_event/compute_request/zk.rs @@ -37,7 +37,7 @@ pub enum ZkRequest { ThresholdShareDecryption(ThresholdShareDecryptionProofRequest), /// Generate proof for decrypted shares aggregation (C7). DecryptedSharesAggregation(DecryptedSharesAggregationProofRequest), - /// Per-node DKG recursive fold (C2abFold → … → NodeFold). + /// Per-node DKG recursive fold (C2abChunkFold → … → NodeFold). NodeDkgFold(NodeDkgFoldRequest), /// Single step of the streaming cross-node nodes_fold accumulation. NodesFoldStep(NodesFoldStepRequest), @@ -189,8 +189,31 @@ impl ShareEncryptionProofRequest { /// This is a protocol invariant shared between the proof-request producer and the C3 fold /// driver; it is defined here (next to the request type) so all callers reference one /// formula. - pub fn c3_slot_index(&self, n_moduli: usize) -> u32 { - (self.recipient_party_id as u32) * (n_moduli as u32) + (self.row_index as u32) + pub fn c3_slot_index(&self, n_moduli: usize) -> Option { + checked_c3_slot_index(self.recipient_party_id, n_moduli, self.row_index) + } +} + +fn checked_c3_slot_index( + recipient_party_id: usize, + n_moduli: usize, + row_index: usize, +) -> Option { + let slot = recipient_party_id + .checked_mul(n_moduli)? + .checked_add(row_index)?; + u32::try_from(slot).ok() +} + +#[cfg(test)] +mod tests { + use super::checked_c3_slot_index; + + #[test] + fn c3_slot_index_rejects_unrepresentable_values() { + assert_eq!(checked_c3_slot_index(3, 2, 1), Some(7)); + assert_eq!(checked_c3_slot_index(usize::MAX, 2, 0), None); + assert_eq!(checked_c3_slot_index(u32::MAX as usize, 2, 0), None); } } @@ -216,6 +239,8 @@ pub struct DkgShareDecryptionProofRequest { /// Position of the own party within the H ascending-party_id ordering. The prover /// splices `own_share_raw` into this slot when assembling C4 inputs. pub own_plaintext_idx: usize, + /// Zero-based recipient party ID whose share row each C4 proof decrypts. + pub recipient_party_id: u64, /// Bincode-serialised `Vec>` of shape `[L][N]` — the own party's plaintext /// share row per modulus (witness — encrypted at rest). pub own_share_raw: SensitiveBytes, diff --git a/crates/events/src/interfold_event/proof.rs b/crates/events/src/interfold_event/proof.rs index c3bfbc4bc4..e30afe24b8 100644 --- a/crates/events/src/interfold_event/proof.rs +++ b/crates/events/src/interfold_event/proof.rs @@ -110,10 +110,6 @@ pub enum CircuitName { PkBfv, /// TrBFV public key share proof (C1). PkGeneration, - /// Sk share computation inner proof (C2a, recursive). - SkShareComputation, - /// E_SM share computation inner proof (C2b, recursive). - ESmShareComputation, /// Share encryption proof (C3). ShareEncryption, /// DKG share decryption proof (C4). @@ -132,8 +128,6 @@ pub enum CircuitName { C6Fold, /// Bootstrap circuit for [`CircuitName::C6Fold`] genesis accumulator proof (same ABI, no acc verify). C6FoldKernel, - /// Ad-hoc recursive aggregation: C2a + C2b. - C2abFold, /// Ad-hoc: final sk `c3_fold` + final e_sm `c3_fold`. C3abFold, /// Ad-hoc: C4a + C4b. @@ -148,6 +142,18 @@ pub enum CircuitName { DkgAggregator, /// Phase-7 decryption aggregator (folded C6 via `c6_fold` + C7). DecryptionAggregator, + /// SK coefficient-range proof used by the root-committed chunk pipeline. + SkShareComputationChunk, + /// ESM coefficient-range proof used by the root-committed chunk pipeline. + ESmShareComputationChunk, + /// Recursive batch of C2 chunk proofs. + C2ChunkBatch, + /// Type-bound SK terminal projection from a complete C2 chunk accumulator. + SkC2ChunkFinalize, + /// Type-bound ESM terminal projection from a complete C2 chunk accumulator. + ESmC2ChunkFinalize, + /// Combines type-bound terminal C2a and C2b chunk proofs. + C2abChunkFold, } impl CircuitName { @@ -155,8 +161,9 @@ impl CircuitName { match self { CircuitName::PkBfv => "pk", CircuitName::PkGeneration => "pk_generation", - CircuitName::SkShareComputation => "sk_share_computation", - CircuitName::ESmShareComputation => "e_sm_share_computation", + CircuitName::SkShareComputationChunk => "sk_share_computation_chunk", + CircuitName::ESmShareComputationChunk => "esm_share_computation_chunk", + CircuitName::C2ChunkBatch => "c2_chunk_batch", CircuitName::ShareEncryption => "share_encryption", CircuitName::DkgShareDecryption => "share_decryption", CircuitName::PkAggregation => "pk_aggregation", @@ -164,9 +171,11 @@ impl CircuitName { CircuitName::DecryptedSharesAggregation => "decrypted_shares_aggregation", CircuitName::C3Fold => "c3_fold", CircuitName::C3FoldKernel => "c3_fold_kernel", + CircuitName::SkC2ChunkFinalize => "sk_c2_chunk_finalize", + CircuitName::ESmC2ChunkFinalize => "esm_c2_chunk_finalize", + CircuitName::C2abChunkFold => "c2ab_chunk_fold", CircuitName::C6Fold => "c6_fold", CircuitName::C6FoldKernel => "c6_fold_kernel", - CircuitName::C2abFold => "c2ab_fold", CircuitName::C3abFold => "c3ab_fold", CircuitName::C4abFold => "c4ab_fold", CircuitName::NodeFold => "node_fold", @@ -180,8 +189,8 @@ impl CircuitName { pub fn group(&self) -> &'static str { match self { CircuitName::PkBfv => "dkg", - CircuitName::SkShareComputation => "dkg", - CircuitName::ESmShareComputation => "dkg", + CircuitName::SkShareComputationChunk => "dkg", + CircuitName::ESmShareComputationChunk => "dkg", CircuitName::ShareEncryption => "dkg", CircuitName::DkgShareDecryption => "dkg", CircuitName::PkGeneration => "threshold", @@ -190,9 +199,12 @@ impl CircuitName { CircuitName::DecryptedSharesAggregation => "threshold", CircuitName::C3Fold | CircuitName::C3FoldKernel + | CircuitName::C2ChunkBatch + | CircuitName::SkC2ChunkFinalize + | CircuitName::ESmC2ChunkFinalize + | CircuitName::C2abChunkFold | CircuitName::C6Fold | CircuitName::C6FoldKernel - | CircuitName::C2abFold | CircuitName::C3abFold | CircuitName::C4abFold | CircuitName::NodeFold @@ -218,8 +230,8 @@ impl CircuitName { CircuitName::PkGeneration => CircuitOutputLayout::Fixed { fields: PK_GENERATION_OUTPUTS, }, - CircuitName::SkShareComputation | CircuitName::ESmShareComputation => { - CircuitOutputLayout::Dynamic + CircuitName::SkShareComputationChunk | CircuitName::ESmShareComputationChunk => { + CircuitOutputLayout::None } CircuitName::DkgShareDecryption => CircuitOutputLayout::Fixed { fields: DKG_SHARE_DECRYPTION_OUTPUTS, @@ -236,9 +248,12 @@ impl CircuitName { CircuitName::DecryptedSharesAggregation => CircuitOutputLayout::None, CircuitName::C3Fold | CircuitName::C3FoldKernel + | CircuitName::C2ChunkBatch + | CircuitName::SkC2ChunkFinalize + | CircuitName::ESmC2ChunkFinalize + | CircuitName::C2abChunkFold | CircuitName::C6Fold | CircuitName::C6FoldKernel - | CircuitName::C2abFold | CircuitName::C3abFold | CircuitName::C4abFold | CircuitName::NodeFold @@ -398,7 +413,9 @@ mod tests { #[test] fn input_layout_share_encryption() { let layout = CircuitName::ShareEncryption.input_layout(); - assert_eq!(layout.field_count(), Some(2)); + // C3 has 4 public inputs: expected_pk_commitment, expected_message_commitment, + // party_idx, mod_idx (matches the Noir main and SHARE_ENCRYPTION_INPUTS). + assert_eq!(layout.field_count(), Some(4)); } #[test] diff --git a/crates/events/src/interfold_event/signed_proof.rs b/crates/events/src/interfold_event/signed_proof.rs index e4a58827c4..4712dfc270 100644 --- a/crates/events/src/interfold_event/signed_proof.rs +++ b/crates/events/src/interfold_event/signed_proof.rs @@ -57,8 +57,8 @@ impl ProofType { match self { ProofType::C0PkBfv => vec![CircuitName::PkBfv], ProofType::C1PkGeneration => vec![CircuitName::PkGeneration], - ProofType::C2aSkShareComputation => vec![CircuitName::SkShareComputation], - ProofType::C2bESmShareComputation => vec![CircuitName::ESmShareComputation], + ProofType::C2aSkShareComputation => vec![CircuitName::SkC2ChunkFinalize], + ProofType::C2bESmShareComputation => vec![CircuitName::ESmC2ChunkFinalize], ProofType::C3aSkShareEncryption => vec![CircuitName::ShareEncryption], ProofType::C3bESmShareEncryption => vec![CircuitName::ShareEncryption], ProofType::C4aSkShareDecryption | ProofType::C4bESmShareDecryption => { @@ -405,11 +405,11 @@ mod tests { ); assert_eq!( ProofType::C2aSkShareComputation.circuit_names(), - vec![CircuitName::SkShareComputation] + vec![CircuitName::SkC2ChunkFinalize] ); assert_eq!( ProofType::C2bESmShareComputation.circuit_names(), - vec![CircuitName::ESmShareComputation] + vec![CircuitName::ESmC2ChunkFinalize] ); assert_eq!( ProofType::C6ThresholdShareDecryption.circuit_names(), diff --git a/crates/keyshare/src/threshold_keyshare/derive_decryption_key.rs b/crates/keyshare/src/threshold_keyshare/derive_decryption_key.rs index dd773324f2..7a8ec8a49d 100644 --- a/crates/keyshare/src/threshold_keyshare/derive_decryption_key.rs +++ b/crates/keyshare/src/threshold_keyshare/derive_decryption_key.rs @@ -336,6 +336,7 @@ pub(crate) fn build_decryption_key_plan( num_honest_parties: num_honest, num_moduli: num_moduli_sk, own_plaintext_idx, + recipient_party_id: party_id as u64, own_share_raw: current.own_sk_share_raw.clone(), dkg_input_type: DkgInputType::SecretKey, params_preset: threshold_preset, @@ -351,6 +352,7 @@ pub(crate) fn build_decryption_key_plan( num_honest_parties: num_honest, num_moduli: num_moduli_esi, own_plaintext_idx, + recipient_party_id: party_id as u64, own_share_raw: current.own_esi_shares_raw[esi_idx].clone(), dkg_input_type: DkgInputType::SmudgingNoise, params_preset: threshold_preset, diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index 3945200706..bf6d0bc6c2 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -65,17 +65,19 @@ use e3_zk_helpers::circuits::threshold::pk_generation::circuit::{ PkGenerationCircuit, PkGenerationCircuitData, }; use e3_zk_helpers::computation::DkgInputType; -use e3_zk_helpers::dkg::share_computation::{ShareComputationCircuit, ShareComputationCircuitData}; +use e3_zk_helpers::dkg::share_computation::ShareComputationCircuitData; use e3_zk_helpers::dkg::share_decryption::{ShareDecryptionCircuit, ShareDecryptionCircuitData}; use e3_zk_helpers::dkg::share_encryption::{ShareEncryptionCircuit, ShareEncryptionCircuitData}; use e3_zk_helpers::threshold::pk_aggregation::PkAggregationCircuit; use e3_zk_helpers::threshold::pk_aggregation::PkAggregationCircuitData; use e3_zk_helpers::CiphernodesCommittee; use e3_zk_helpers::CiphernodesCommitteeSize; +use e3_zk_prover::DEFAULT_C2_CHUNK_SIZE; use e3_zk_prover::{ - generate_nodes_fold_step, prove_decryption_aggregation_jobs, prove_dkg_aggregation, - prove_node_dkg_fold, CircuitVariant, DecryptionAggregationJob, DkgAggregationInput, - NodeDkgFoldInput, NodeDkgFoldProveResult, Provable, ZkBackend, ZkError, ZkProver, + generate_nodes_fold_step, prove_chunked_share_computation, prove_decryption_aggregation_jobs, + prove_dkg_aggregation, prove_node_dkg_fold, validate_c2_terminal_proof, C2TerminalAnchors, + CircuitVariant, DecryptionAggregationJob, DkgAggregationInput, NodeDkgFoldInput, + NodeDkgFoldProveResult, Provable, ZkBackend, ZkError, ZkProver, }; use fhe::bfv::{Ciphertext, Encoding, Plaintext, PublicKey, SecretKey}; use fhe::mbfv::PublicKeyShare; @@ -659,7 +661,7 @@ fn handle_zk_request( handle_pk_generation_proof(&prover, &cipher, req, request.clone()) }), ZkRequest::ShareComputation(req) => timefunc("zk_share_computation", id, || { - handle_share_computation_proof(&prover, &cipher, req, request.clone()) + handle_share_computation_proof(&prover, &cipher, req, request.clone(), report.clone()) }), ZkRequest::ShareEncryption(req) => timefunc("zk_share_encryption", id, || { handle_share_encryption_proof(&prover, &cipher, req, request.clone()) @@ -877,6 +879,7 @@ fn handle_share_computation_proof( cipher: &Cipher, req: ShareComputationProofRequest, request: ComputeRequest, + report: Option>, ) -> Result { // 1. Build BFV threshold parameters let (threshold_params, _dkg_params) = build_pair_for_preset(req.params_preset) @@ -927,6 +930,7 @@ fn handle_share_computation_proof( parity_matrix, n_parties: committee.n as u32, threshold: committee.threshold as u32, + chunk_size: DEFAULT_C2_CHUNK_SIZE as u32, }; let bb_work = zk_bb_work_id(&request); @@ -934,22 +938,30 @@ fn handle_share_computation_proof( let artifacts_dir = prover.resolve_artifacts_dir(req.params_preset, req.committee_size.as_str()); - // 7. Inner C2 proof (sk_share_computation or e_sm_share_computation) - let circuit = ShareComputationCircuit; - let proof = circuit - .prove( - prover, - &req.params_preset, - &circuit_data, - &inner_job_id, - &artifacts_dir, + // 7. Chunk proofs and terminal C2 projection. The production path uses the compiled default + // chunk size; the `zk_cli --chunk-size` option does not reach this handler. + let result = prove_chunked_share_computation( + prover, + req.params_preset, + &circuit_data, + &inner_job_id, + &artifacts_dir, + ) + .map_err(|e| { + ComputeRequestError::new( + ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), + request.clone(), ) - .map_err(|e| { - ComputeRequestError::new( - ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), - request.clone(), - ) - })?; + })?; + if let Some(report) = report { + for step in result.step_timings { + report.do_send(TrackDuration::new( + format!("ZkShareComputation/{}", step.step), + Duration::from_secs_f64(step.seconds), + )); + } + } + let proof = result.proof; Ok(ComputeResponse::zk( ZkResponse::ShareComputation(ShareComputationProofResponse { @@ -1099,7 +1111,7 @@ fn handle_share_encryption_proof( request: ComputeRequest, ) -> Result { // 1. Build DKG params from threshold preset - let (_threshold_params, dkg_params) = build_pair_for_preset(req.params_preset) + let (threshold_params, dkg_params) = build_pair_for_preset(req.params_preset) .map_err(|e| make_zk_error(&request, format!("build_pair_for_preset: {}", e)))?; // 2. Decrypt sensitive witness data @@ -1138,10 +1150,38 @@ fn handle_share_encryption_proof( let e1_rns = try_poly_ntt_from_bytes(&e1_rns_bytes, &dkg_params) .map_err(|e| make_zk_error(&request, format!("e1_rns: {}", e)))?; + let committee_n = req.committee_size.values().n; + if req.recipient_party_id >= committee_n { + return Err(make_zk_error( + &request, + format!( + "recipient_party_id {} is outside committee size {}", + req.recipient_party_id, committee_n + ), + )); + } + // C3 encrypts one threshold Shamir row per proof. The ciphertext itself uses DKG parameters, + // but row_index belongs to the threshold secret's modulus domain. + let n_moduli = threshold_params.moduli().len(); + if req.row_index >= n_moduli { + return Err(make_zk_error( + &request, + format!( + "row_index {} is outside modulus count {}", + req.row_index, n_moduli + ), + )); + } + let party_idx = u32::try_from(req.recipient_party_id) + .map_err(|e| make_zk_error(&request, format!("recipient_party_id: {e}")))?; + let mod_idx = u32::try_from(req.row_index) + .map_err(|e| make_zk_error(&request, format!("row_index: {e}")))?; + // 4. Dummy SecretKey (not used by Inputs::compute) let dummy_sk = SecretKey::random(&dkg_params, &mut rand::rng()); // 5. Build circuit data + let committee_val = req.committee_size.values(); let circuit_data = ShareEncryptionCircuitData { plaintext, ciphertext, @@ -1151,6 +1191,10 @@ fn handle_share_encryption_proof( e0_rns, e1_rns, dkg_input_type: req.dkg_input_type, + party_idx, + mod_idx, + chunk_size: DEFAULT_C2_CHUNK_SIZE as u32, + committee: committee_val, }; // 6. Generate proof (preset = threshold preset; Inputs::compute derives DKG internally) @@ -1214,6 +1258,15 @@ fn handle_dkg_share_decryption_proof( ), )); } + if req.recipient_party_id >= req.committee_size.values().n as u64 { + return Err(make_zk_error( + &request, + format!( + "recipient_party_id {} is outside committee N", + req.recipient_party_id + ), + )); + } let expected_external_cts = h.saturating_sub(1) * l; if req.honest_ciphertexts_raw.len() != expected_external_cts { return Err(make_zk_error( @@ -1296,8 +1349,11 @@ fn handle_dkg_share_decryption_proof( let circuit_data = ShareDecryptionCircuitData { secret_key, honest_ciphertexts, + recipient_party_id: req.recipient_party_id, own_plaintext_share, dkg_input_type: req.dkg_input_type, + chunk_size: DEFAULT_C2_CHUNK_SIZE as u32, + committee: req.committee_size.values(), }; let circuit = ShareDecryptionCircuit; @@ -1377,6 +1433,38 @@ fn handle_verify_share_proofs( }; } + // 1.5 Bind C2 terminal proofs to their deployment-time VK anchors + // before the expensive generic (bb) verification runs. A failure + // here marks the signed proof invalid, exactly like a ZK failure. + let proof_type = signed_proof.payload.proof_type; + if matches!( + proof_type, + e3_events::ProofType::C2aSkShareComputation + | e3_events::ProofType::C2bESmShareComputation + ) { + let anchor_result = C2TerminalAnchors::load(prover, proof_type, &artifacts_dir) + .and_then(|anchors| { + validate_c2_terminal_proof( + req.params_preset, + req.committee_size, + proof_type, + &signed_proof.payload.proof, + &anchors, + ) + }); + if let Err(error) = anchor_result { + info!( + "C2 terminal proof VK binding failed for party {sender} ({proof_type:?}): {error}" + ); + return PartyVerificationResult { + sender_party_id: sender, + all_verified: false, + failed_signed_payload: Some(signed_proof.clone()), + recovered_address: None, + }; + } + } + // 2. ZK proof verification let proof = &signed_proof.payload.proof; let result = diff --git a/crates/support/README.md b/crates/support/README.md index 4e5607a4d0..8d39a93309 100644 --- a/crates/support/README.md +++ b/crates/support/README.md @@ -183,7 +183,7 @@ curl -X POST http://localhost:13151/run_compute \ "chain_id": 31337, "interfold_address": "0x1111111111111111111111111111111111111111", "encryption_scheme_id": "0x...", - "committee_public_key": "0x...", + "committee_public_key_hash": "0x...", "params": "0x...", "ciphertext_inputs": [["0x...", 0], ["0x...", 1]], "callback_url": "http://host.local:4000/state/add-result" diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index af5848a291..38bceab4f1 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -431,9 +431,21 @@ async fn setup_test_zk_backend( .join("recursive_aggregation") .join("c6_fold_kernel") .join("target"); - let c2ab_fold_target = circuits_build_root + let c2ab_chunk_fold_target = circuits_build_root .join("recursive_aggregation") - .join("c2ab_fold") + .join("c2ab_chunk_fold") + .join("target"); + let c2_chunk_batch_target = circuits_build_root + .join("recursive_aggregation") + .join("c2_chunk_batch") + .join("target"); + let sk_c2_chunk_finalize_target = circuits_build_root + .join("recursive_aggregation") + .join("sk_c2_chunk_finalize") + .join("target"); + let esm_c2_chunk_finalize_target = circuits_build_root + .join("recursive_aggregation") + .join("esm_c2_chunk_finalize") .join("target"); let c3ab_fold_target = circuits_build_root .join("recursive_aggregation") @@ -530,20 +542,37 @@ async fn setup_test_zk_backend( ".vk_noir_hash", ) .await?; - // C2a (sk_share_computation) + // C2a (sk_share_computation_chunk) — recursive leaf + terminal fold copy_circuit( &dkg_target, - &rv.join("dkg/sk_share_computation"), - "sk_share_computation", + &rv.join("dkg/sk_share_computation_chunk"), + "sk_share_computation_chunk", ".vk_noir", ".vk_noir_hash", ) .await?; - // C2b (e_sm_share_computation) + // C2b (esm_share_computation_chunk) — recursive leaf + terminal fold copy_circuit( &dkg_target, - &rv.join("dkg/e_sm_share_computation"), - "e_sm_share_computation", + &rv.join("dkg/esm_share_computation_chunk"), + "esm_share_computation_chunk", + ".vk_noir", + ".vk_noir_hash", + ) + .await?; + // C2 chunk finalize (recursive leaf) + copy_circuit( + &sk_c2_chunk_finalize_target, + &rv.join("recursive_aggregation/sk_c2_chunk_finalize"), + "sk_c2_chunk_finalize", + ".vk_noir", + ".vk_noir_hash", + ) + .await?; + copy_circuit( + &esm_c2_chunk_finalize_target, + &rv.join("recursive_aggregation/esm_c2_chunk_finalize"), + "esm_c2_chunk_finalize", ".vk_noir", ".vk_noir_hash", ) @@ -641,10 +670,20 @@ async fn setup_test_zk_backend( ".vk_recursive_hash", ) .await?; + // C2 chunk batch (default variant, recursive aggregation) + copy_circuit( + &c2_chunk_batch_target, + &dv.join("recursive_aggregation/c2_chunk_batch"), + "c2_chunk_batch", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + // C2ab chunk fold (default variant, recursive aggregation) copy_circuit( - &c2ab_fold_target, - &dv.join("recursive_aggregation/c2ab_fold"), - "c2ab_fold", + &c2ab_chunk_fold_target, + &dv.join("recursive_aggregation/c2ab_chunk_fold"), + "c2ab_chunk_fold", ".vk_recursive", ".vk_recursive_hash", ) diff --git a/crates/zk-helpers/README.md b/crates/zk-helpers/README.md index d1c4b7982a..1ae95f86dd 100644 --- a/crates/zk-helpers/README.md +++ b/crates/zk-helpers/README.md @@ -11,13 +11,17 @@ cargo run -p e3-zk-helpers --bin zk_cli -- --list_circuits # Generate Prover.toml + configs.nr for a circuit cargo run -p e3-zk-helpers --bin zk_cli -- --circuit pk-generation --preset insecure --committee minimum +# C2 chunked configs (default: 512 coefficients) +cargo run -p e3-zk-helpers --bin zk_cli -- --circuit share-computation --preset secure --inputs secret-key --chunk-size 512 + # Micro or small committee (must match active circuits lib selection) cargo run -p e3-zk-helpers --bin zk_cli -- --circuit pk-generation --preset insecure --committee micro cargo run -p e3-zk-helpers --bin zk_cli -- --circuit pk-generation --preset insecure --committee small ``` -| Flag | Description | -| -------------------- | ----------------------------------------------------------------------------------------------------- | -| `--circuit ` | Circuit to generate artifacts for | -| `--preset ` | BFV preset: `insecure` (512), `secure` (8192), or aliases `2` / `80` | -| `--committee ` | Committee size: `minimum` (default), `micro`, or `small` — must match `circuits/lib` active committee | +| Flag | Description | +| -------------------- | ------------------------------------------------------------------------------------------------------------ | +| `--circuit ` | Circuit to generate artifacts for | +| `--preset ` | BFV preset: `insecure` (512), `secure` (8192), or aliases `2` / `80` | +| `--committee ` | Committee size: `minimum` (default), `micro`, or `small` — must match `circuits/lib` active committee | +| `--chunk-size ` | C2 coefficient chunk size; only `512` is supported (the compiled chunk layout is pinned to 512 coefficients) | diff --git a/crates/zk-helpers/src/bin/generate_parity_matrices.rs b/crates/zk-helpers/src/bin/generate_parity_matrices.rs index 3d96b03844..b9659277f2 100644 --- a/crates/zk-helpers/src/bin/generate_parity_matrices.rs +++ b/crates/zk-helpers/src/bin/generate_parity_matrices.rs @@ -4,10 +4,10 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -//! Regenerates `circuits/lib/src/configs/committee//parity_{insecure,secure}.nr` +//! Regenerates the derived Noir artifacts in `circuits/lib/src/configs/committee/` //! for the given committee. Invoked by `scripts/build-circuits.ts` whenever the active -//! committee is set, so the Reed-Solomon parity matrices stay in lockstep with `(N, T)` -//! and the threshold `QIS` of each preset. +//! committee is set, so parity matrices and smudging constants stay in lockstep with +//! the Rust witness computation. //! //! Usage: //! cargo run --release --bin generate_parity_matrices -- \ @@ -27,11 +27,13 @@ use clap::Parser; use e3_fhe_params::{build_pair_for_preset, BfvPreset}; use e3_zk_helpers::ciphernodes_committee::CiphernodesCommitteeSize; use e3_zk_helpers::circuits::dkg::share_computation::utils::parity_matrix_constant_string; +use e3_zk_helpers::circuits::threshold::pk_generation::computation::Configs as PkGenerationConfigs; +use e3_zk_helpers::Computation; #[derive(Parser, Debug)] #[command( name = "generate_parity_matrices", - about = "Regenerate parity_{insecure,secure}.nr for a committee." + about = "Regenerate derived Noir artifacts for a committee." )] struct Args { /// Committee name (`minimum`, `micro`, `small`). Determines `(N, T)`. @@ -81,6 +83,10 @@ fn file_for(root: &Path, committee: &str, preset: BfvPreset) -> PathBuf { root.join(committee).join(format!("parity_{suffix}.nr")) } +fn smudging_file(root: &Path, committee: &str) -> PathBuf { + root.join(committee).join("smudging.nr") +} + /// File template. Threading `committee` / `preset` / `matrix` keeps the on-disk format /// stable across regenerations (so the lefthook check can diff against a fresh run). fn render(committee: &str, preset_suffix: &str, l_module: &str, matrix_literal: &str) -> String { @@ -109,6 +115,34 @@ pub use crate::configs::{l_module}::threshold::L as L_THRESHOLD;\n\ ) } +fn render_smudging( + committee: &str, + insecure: &PkGenerationConfigs, + secure: &PkGenerationConfigs, +) -> String { + format!( + "// SPDX-License-Identifier: LGPL-3.0-only\n\ +//\n\ +// This file is provided WITHOUT ANY WARRANTY;\n\ +// without even the implied warranty of MERCHANTABILITY\n\ +// or FITNESS FOR A PARTICULAR PURPOSE.\n\ +//\n\ +// AUTO-GENERATED by `generate_parity_matrices`. DO NOT EDIT BY HAND.\n\ +// Regenerate with `pnpm build:circuits --committee {committee}`.\n\ +//\n\ +// C1 and C2b must use the same smudging-noise width as Rust witness generation.\n\ +\n\ +pub global INSECURE_E_SM_BIT: u32 = {};\n\ +pub global INSECURE_E_SM_BOUND: Field = {};\n\ +pub global SECURE_E_SM_BIT: u32 = {};\n\ +pub global SECURE_E_SM_BOUND: Field = {};\n", + insecure.bits.e_sm_bit, + insecure.bounds.e_sm_bound, + secure.bits.e_sm_bit, + secure.bounds.e_sm_bound, + ) +} + fn main() -> Result<()> { let args = Args::parse(); let size = CiphernodesCommitteeSize::from_str(&args.committee) @@ -152,5 +186,14 @@ fn main() -> Result<()> { ); } + let insecure = PkGenerationConfigs::compute(BfvPreset::InsecureThreshold512, ¶ms) + .context("computing insecure smudging constants")?; + let secure = PkGenerationConfigs::compute(BfvPreset::SecureThreshold8192, ¶ms) + .context("computing secure smudging constants")?; + let path = smudging_file(&root, &args.committee); + std::fs::write(&path, render_smudging(&args.committee, &insecure, &secure)) + .with_context(|| format!("writing {}", path.display()))?; + println!("✓ {}", path.display()); + Ok(()) } diff --git a/crates/zk-helpers/src/bin/zk_cli.rs b/crates/zk-helpers/src/bin/zk_cli.rs index 2600a88a3c..6d1046ecb9 100644 --- a/crates/zk-helpers/src/bin/zk_cli.rs +++ b/crates/zk-helpers/src/bin/zk_cli.rs @@ -12,10 +12,10 @@ //! //! **Share-computation (C2) configs.nr:** set `INTERFOLD_CIRCUITS_ROOT` to the repo `circuits` //! directory (or run from the Interfold repo so it is auto-discovered). After `pnpm build:circuits`, -//! `circuits/bin/dkg/target/` contains `sk_share_computation.vk_recursive_hash` and -//! `e_sm_share_computation.vk_recursive_hash` for the inner recursive circuits; the aggregation -//! wrapper emits `share_computation.vk_recursive_hash` under -//! `circuits/bin/recursive_aggregation/wrapper/dkg/share_computation/target/` (from +//! the C2 chunk leaf circuits emit `sk_share_computation_chunk.vk_recursive_hash` / +//! `esm_share_computation_chunk.vk_recursive_hash` under their `target/` dirs; the recursive +//! aggregation fold emits `c2ab_chunk_fold.vk_recursive_hash` under +//! `circuits/bin/recursive_aggregation/c2ab_chunk_fold/target/` (from //! `scripts/build-circuits.ts`). If `INTERFOLD_CIRCUITS_ROOT` is set and those files are missing, //! codegen fails; if unset and artifacts are absent, the C2 literals are omitted from the generated //! fragment. @@ -26,9 +26,13 @@ use e3_fhe_params::{BfvPreset, ParameterType}; use e3_zk_helpers::ciphernodes_committee::CiphernodesCommitteeSize; use e3_zk_helpers::circuits::dkg::pk::circuit::{PkCircuit, PkCircuitData}; use e3_zk_helpers::circuits::dkg::share_computation::circuit::{ - ShareComputationCircuit, ShareComputationCircuitData, + ShareComputationChunkCircuit, ShareComputationCircuit, ShareComputationCircuitData, }; -use e3_zk_helpers::codegen::{write_artifacts, write_toml, CircuitCodegen}; +use e3_zk_helpers::circuits::dkg::share_computation::codegen::{ + generate_chunk_toml, generate_configs_with_chunk_size, +}; +use e3_zk_helpers::circuits::CircuitComputation; +use e3_zk_helpers::codegen::{write_artifacts, write_toml, Artifacts, CircuitCodegen}; use e3_zk_helpers::computation::DkgInputType; use e3_zk_helpers::dkg::share_decryption::{ ShareDecryptionCircuit as DkgShareDecryptionCircuit, @@ -188,6 +192,13 @@ struct Cli { /// When used with --toml: do not write configs.nr (e.g. for benchmarks where circuits use lib configs). #[arg(long, default_value = "false")] no_configs: bool, + /// C2 coefficient chunk size. Only 512 is supported; the compiled artifact + /// layout is pinned to a 512-coefficient chunk size. + #[arg(long, default_value_t = 512)] + chunk_size: usize, + /// Chunk index (0-based) to emit when circuit is `share-computation-chunk`. + #[arg(long, default_value_t = 0)] + chunk_index: usize, } fn main() -> Result<()> { @@ -197,6 +208,7 @@ fn main() -> Result<()> { let mut registry = CircuitRegistry::new(); registry.register(Arc::new(PkCircuit)); registry.register(Arc::new(ShareComputationCircuit)); + registry.register(Arc::new(ShareComputationChunkCircuit)); registry.register(Arc::new(UserDataEncryptionCircuit)); registry.register(Arc::new(PkGenerationCircuit)); registry.register(Arc::new(ShareEncryptionCircuit)); @@ -250,6 +262,15 @@ fn main() -> Result<()> { )); } + // The compiled C2 artifact set is pinned to a 512-coefficient chunk layout. + // A different chunk size produces artifacts that cannot verify against the + // deployed circuits, so reject it here instead of writing invalid configs. + if args.chunk_size != 512 { + return Err(anyhow!( + "--chunk-size must be 512; the compiled C2 chunk layout is pinned to a 512-coefficient chunk size" + )); + } + let write_prover_toml = args.toml; let no_configs = args.no_configs && args.toml; let circuit_name = circuit_meta.name(); @@ -257,12 +278,18 @@ fn main() -> Result<()> { // Some circuits reuse one helper entrypoint for multiple witness families, so `--inputs` // selects whether we derive secret-key or smudging-noise sample data. let requires_inputs_arg = circuit_name == ShareComputationCircuit::NAME + || circuit_name == ShareComputationChunkCircuit::NAME || circuit_meta.name() == ShareEncryptionCircuit::NAME || circuit_meta.name() == DkgShareDecryptionCircuit::NAME; - let show_input_type = requires_inputs_arg || circuit_name == ShareComputationCircuit::NAME; + let show_input_type = requires_inputs_arg + || circuit_name == ShareComputationCircuit::NAME + || circuit_name == ShareComputationChunkCircuit::NAME; - let dkg_input_type = if circuit_name == ShareComputationCircuit::NAME || requires_inputs_arg { + let dkg_input_type = if circuit_name == ShareComputationCircuit::NAME + || circuit_name == ShareComputationChunkCircuit::NAME + || requires_inputs_arg + { let inputs_str = if !args.toml { args.inputs.as_deref().unwrap_or("secret-key") } else { @@ -298,6 +325,8 @@ fn main() -> Result<()> { run_with_spinner(|| { let committee = committee_size.values(); + let committee_n = committee.n; + let committee_threshold = committee.threshold; let artifacts = match circuit_name { name if name == ::NAME => { let sample = PkCircuitData::generate_sample(preset)?; @@ -306,23 +335,64 @@ fn main() -> Result<()> { circuit.codegen(preset, &sample)? } name if name == ::NAME => { - let sample = ShareComputationCircuitData::generate_sample( + let mut sample = ShareComputationCircuitData::generate_sample( preset, committee, dkg_input_type, )?; + sample.chunk_size = args.chunk_size as u32; let circuit = ShareComputationCircuit; - circuit.codegen(preset, &sample)? + let mut artifacts = circuit.codegen(preset, &sample)?; + let output = ShareComputationCircuit::compute(preset, &sample)?; + artifacts.configs = generate_configs_with_chunk_size( + preset, + &output.bits, + committee_n, + committee_threshold, + sample.chunk_size as usize, + )?; + artifacts + } + name if name == ::NAME => { + let mut sample = ShareComputationCircuitData::generate_sample( + preset, + committee, + dkg_input_type, + )?; + sample.chunk_size = args.chunk_size as u32; + + let output = ShareComputationCircuit::compute(preset, &sample)?; + let toml = generate_chunk_toml( + &output.inputs, + sample.chunk_size as usize, + args.chunk_index, + )?; + + let mut artifacts = Artifacts { + toml, + configs: String::new(), + }; + if !no_configs { + artifacts.configs = generate_configs_with_chunk_size( + preset, + &output.bits, + committee_n, + committee_threshold, + sample.chunk_size as usize, + )?; + } + artifacts } name if name == ::NAME => { let sd = preset.search_defaults().unwrap(); - let sample = ShareEncryptionCircuitData::generate_sample( + let mut sample = ShareEncryptionCircuitData::generate_sample( preset, committee, dkg_input_type, sd.z, )?; + sample.chunk_size = args.chunk_size as u32; let circuit = ShareEncryptionCircuit; circuit.codegen(preset, &sample)? @@ -340,11 +410,12 @@ fn main() -> Result<()> { circuit.codegen(preset, &sample)? } name if name == ::NAME => { - let sample = DkgShareDecryptionCircuitData::generate_sample( + let mut sample = DkgShareDecryptionCircuitData::generate_sample( preset, committee, dkg_input_type, )?; + sample.chunk_size = args.chunk_size as u32; let circuit = DkgShareDecryptionCircuit; circuit.codegen(preset, &sample)? diff --git a/crates/zk-helpers/src/ciphernodes_committee.rs b/crates/zk-helpers/src/ciphernodes_committee.rs index e751dc6fb7..13921aa336 100644 --- a/crates/zk-helpers/src/ciphernodes_committee.rs +++ b/crates/zk-helpers/src/ciphernodes_committee.rs @@ -121,6 +121,15 @@ pub fn canonical_committee_for_circuit( Ok(expected) } +/// Resolve a canonical committee row from `(n, threshold)` alone, deriving `h` from the table. +/// +/// Used by codegen paths that parameterize the parity matrix from `(N, T)` only. Returns the +/// canonical row when `(n, threshold)` matches a known committee size; otherwise errors. +pub fn try_canonical_from_t_n(n: usize, threshold: usize) -> Result { + let size = CiphernodesCommitteeSize::from_threshold(threshold, n)?; + Ok(size.values()) +} + impl FromStr for CiphernodesCommitteeSize { type Err = anyhow::Error; diff --git a/crates/zk-helpers/src/circuits/commitments.rs b/crates/zk-helpers/src/circuits/commitments.rs index 2210af499c..11bd2953d1 100644 --- a/crates/zk-helpers/src/circuits/commitments.rs +++ b/crates/zk-helpers/src/circuits/commitments.rs @@ -50,6 +50,14 @@ const DS_SHARE_COMPUTATION: [u8; 64] = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; +/// String: "SHARE_COMPUTATION_CHUNK" +const DS_SHARE_COMPUTATION_CHUNK: [u8; 64] = [ + 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f, + 0x4e, 0x5f, 0x43, 0x48, 0x55, 0x4e, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + /// String: "SHARE_ENCRYPTION" const DS_SHARE_ENCRYPTION: [u8; 64] = [ 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50, 0x54, 0x49, 0x4f, 0x4e, @@ -346,6 +354,144 @@ pub fn compute_share_computation_e_sm_commitment(e_sm: &CrtPolynomial, bit_e_sm: BigInt::from_bytes_le(num_bigint::Sign::Plus, &commitment_bytes) } +fn field_to_bigint(field: Field) -> BigInt { + BigInt::from_bytes_le(num_bigint::Sign::Plus, &field.into_bigint().to_bytes_le()) +} + +fn compute_chunk_commitment(payload: Vec) -> BigInt { + let input_size = payload.len() as u32; + field_to_bigint( + compute_commitments( + payload, + DS_SHARE_COMPUTATION_CHUNK, + [0x80000000 | input_size, 1], + )[0], + ) +} + +fn chunk_polynomial(polynomial: &Polynomial, chunk_idx: usize, chunk_size: usize) -> Polynomial { + let coefficients = polynomial.coefficients(); + let start = coefficients.len() - ((chunk_idx + 1) * chunk_size); + Polynomial::new(coefficients[start..start + chunk_size].to_vec()) +} + +/// Compute the SK root for the C1 polynomial orientation. +pub fn compute_sc_sk_secret_root_commitment( + sk: &Polynomial, + bit_sk: u32, + chunk_size: usize, +) -> BigInt { + assert!(chunk_size > 0 && sk.coefficients().len().is_multiple_of(chunk_size)); + let chunk_count = sk.coefficients().len() / chunk_size; + let mut leaves = Vec::with_capacity(chunk_count); + for chunk_idx in 0..chunk_count { + let chunk = chunk_polynomial(sk, chunk_idx, chunk_size); + let mut payload = vec![ + Field::from(0u64), + Field::from(0u64), + Field::from(chunk_idx as u64), + Field::from(sk.coefficients().len() as u64), + Field::from(chunk_size as u64), + ]; + payload = flatten(payload, std::slice::from_ref(&chunk), bit_sk); + leaves.push(compute_chunk_commitment(payload)); + } + let mut payload = vec![ + Field::from(1u64), + Field::from(0u64), + Field::from(sk.coefficients().len() as u64), + Field::from(chunk_size as u64), + ]; + payload.extend(leaves.into_iter().map(|leaf| field_from_bigint(&leaf))); + compute_chunk_commitment(payload) +} + +/// Compute the ESM root for the C1 polynomial orientation. +pub fn compute_sc_esm_secret_root_commitment( + e_sm: &CrtPolynomial, + bit_e_sm: u32, + chunk_size: usize, +) -> BigInt { + assert!(!e_sm.limbs.is_empty()); + assert!( + chunk_size > 0 + && e_sm.limbs[0] + .coefficients() + .len() + .is_multiple_of(chunk_size) + ); + let degree = e_sm.limbs[0].coefficients().len(); + let chunk_count = degree / chunk_size; + let mut leaves = Vec::with_capacity(chunk_count); + for chunk_idx in 0..chunk_count { + let mut payload = vec![ + Field::from(0u64), + Field::from(1u64), + Field::from(chunk_idx as u64), + Field::from(degree as u64), + Field::from(chunk_size as u64), + ]; + let chunks = e_sm + .limbs + .iter() + .map(|limb| chunk_polynomial(limb, chunk_idx, chunk_size)) + .collect::>(); + payload = flatten(payload, &chunks, bit_e_sm); + leaves.push(compute_chunk_commitment(payload)); + } + let mut payload = vec![ + Field::from(1u64), + Field::from(1u64), + Field::from(degree as u64), + Field::from(chunk_size as u64), + ]; + payload.extend(leaves.into_iter().map(|leaf| field_from_bigint(&leaf))); + compute_chunk_commitment(payload) +} + +/// Compute a recipient share root from its C3/C4 polynomial orientation. +pub fn compute_sc_party_share_root_commitment( + party_idx: usize, + mod_idx: usize, + message: &Polynomial, + bit_share: u32, + chunk_size: usize, +) -> BigInt { + assert!(chunk_size > 0 && message.coefficients().len().is_multiple_of(chunk_size)); + let degree = message.coefficients().len(); + let chunk_count = degree / chunk_size; + let mut leaves = Vec::with_capacity(chunk_count); + for chunk_idx in 0..chunk_count { + let chunk = chunk_polynomial(message, chunk_idx, chunk_size); + let mut payload = vec![ + Field::from(0u64), + Field::from(2u64), + Field::from(party_idx as u64), + Field::from(mod_idx as u64), + Field::from(chunk_idx as u64), + Field::from(degree as u64), + Field::from(chunk_size as u64), + ]; + payload = flatten(payload, std::slice::from_ref(&chunk), bit_share); + leaves.push(compute_chunk_commitment(payload)); + } + let mut payload = vec![ + Field::from(1u64), + Field::from(2u64), + Field::from(party_idx as u64), + Field::from(mod_idx as u64), + Field::from(degree as u64), + Field::from(chunk_size as u64), + ]; + payload.extend(leaves.into_iter().map(|leaf| field_from_bigint(&leaf))); + compute_chunk_commitment(payload) +} + +fn field_from_bigint(value: &BigInt) -> Field { + let (_, bytes) = value.to_bytes_le(); + Field::from_le_bytes_mod_order(&bytes) +} + /// Compute share encryption commitment from message polynomial. /// /// This matches the Noir `compute_share_encryption_commitment_from_message` function exactly. diff --git a/crates/zk-helpers/src/circuits/dkg/pk/codegen.rs b/crates/zk-helpers/src/circuits/dkg/pk/codegen.rs index 6cf0008ffe..55eb7accc8 100644 --- a/crates/zk-helpers/src/circuits/dkg/pk/codegen.rs +++ b/crates/zk-helpers/src/circuits/dkg/pk/codegen.rs @@ -8,7 +8,7 @@ use crate::circuits::dkg::pk::circuit::PkCircuit; use crate::circuits::dkg::pk::circuit::PkCircuitData; -use crate::circuits::dkg::pk::computation::{Bits, Inputs, PkComputationOutput}; +use crate::circuits::dkg::pk::computation::{Bits, Configs, Inputs, PkComputationOutput}; use crate::Artifacts; use crate::Circuit; use crate::CircuitCodegen; @@ -28,9 +28,10 @@ impl CircuitCodegen for PkCircuit { fn codegen(&self, preset: Self::Preset, data: &Self::Data) -> Result { let PkComputationOutput { inputs, bits, .. } = PkCircuit::compute(preset, data)?; + let configs_data = Configs::compute(preset, &())?; let toml = generate_toml(inputs)?; - let configs = generate_configs(preset, &bits); + let configs = generate_configs(&configs_data, &bits); Ok(Artifacts { toml, configs }) } @@ -44,7 +45,7 @@ pub fn generate_toml(inputs: Inputs) -> Result { } /// Builds the configs.nr string (N, L, bit parameters) for the Noir prover. -pub fn generate_configs(preset: BfvPreset, bits: &Bits) -> CodegenConfigs { +pub fn generate_configs(configs: &Configs, bits: &Bits) -> CodegenConfigs { format!( r#"pub global N: u32 = {}; pub global L: u32 = {}; @@ -58,8 +59,8 @@ pk (CIRCUIT 0 - DKG BFV PUBLIC KEY) // pk - bit parameters pub global {}_BIT_PK: u32 = {}; "#, - preset.dkg_counterpart().unwrap().metadata().degree, - preset.dkg_counterpart().unwrap().metadata().num_moduli, + configs.n, + configs.l, ::PREFIX, bits.pk_bit, ) diff --git a/crates/zk-helpers/src/circuits/dkg/share_computation/circuit.rs b/crates/zk-helpers/src/circuits/dkg/share_computation/circuit.rs index 001c022461..d17680456f 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_computation/circuit.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_computation/circuit.rs @@ -23,6 +23,20 @@ impl Circuit for ShareComputationCircuit { const DKG_INPUT_TYPE: Option = None; } +/// Metadata for the per-coefficient-chunk C2 circuit (`sk_share_computation_chunk` / +/// `esm_share_computation_chunk`). Proves one `chunk_size`-coefficient slice of the Shamir secret +/// shares. Consumes the same derived witness as [`ShareComputationCircuit`] but only the +/// `chunk_size`-wide slice starting at `chunk_index * chunk_size`. +#[derive(Debug)] +pub struct ShareComputationChunkCircuit; + +impl Circuit for ShareComputationChunkCircuit { + const NAME: &'static str = "share-computation-chunk"; + const PREFIX: &'static str = "SHARE_COMPUTATION"; + const SUPPORTED_PARAMETER: ParameterType = ParameterType::THRESHOLD; + const DKG_INPUT_TYPE: Option = None; +} + pub struct ShareComputationCircuitData { /// Which secret type this data is for (determines which branch to use in data). pub dkg_input_type: DkgInputType, @@ -31,4 +45,7 @@ pub struct ShareComputationCircuitData { pub parity_matrix: Vec, pub n_parties: u32, pub threshold: u32, + /// C2 coefficient chunk size (must divide the polynomial degree). Must equal the + /// `SHARE_COMPUTATION_CHUNK_SIZE` compiled into the Noir circuit. + pub chunk_size: u32, } diff --git a/crates/zk-helpers/src/circuits/dkg/share_computation/codegen.rs b/crates/zk-helpers/src/circuits/dkg/share_computation/codegen.rs index a59dd53674..a6c8f06596 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_computation/codegen.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_computation/codegen.rs @@ -14,9 +14,11 @@ use crate::circuits::dkg::share_computation::{ }; use crate::circuits::{Artifacts, CircuitCodegen, CircuitsErrors, CodegenToml}; use crate::codegen::CodegenConfigs; +use crate::computation::DkgInputType; use crate::registry::Circuit; use e3_fhe_params::build_pair_for_preset; use e3_fhe_params::BfvPreset; +use serde_json::Value; /// Implementation of [`CircuitCodegen`] for [`ShareComputationCircuit`]. impl CircuitCodegen for ShareComputationCircuit { @@ -34,6 +36,7 @@ impl CircuitCodegen for ShareComputationCircuit { &bits, data.n_parties as usize, data.threshold as usize, + data.chunk_size as usize, )?; Ok(Artifacts { toml, configs }) @@ -46,6 +49,114 @@ pub fn generate_toml(witness: &Inputs) -> Result { Ok(toml::to_string(&json)?) } +/// Builds the Prover.toml for a single C2 coefficient chunk (`sk_share_computation_chunk` / +/// `esm_share_computation_chunk`): `chunk_idx`, a `chunk_size`-coefficient `secret_chunk`, and the +/// matching `y_chunk` slice. This mirrors the slice that `e3-zk-prover` computes at prove time, so +/// the isolated benchmark uses the same witness shape the runtime proves per chunk. +pub fn generate_chunk_toml( + witness: &Inputs, + chunk_size: usize, + chunk_idx: usize, +) -> Result { + let full = witness.to_json().map_err(CircuitsErrors::SerdeJson)?; + let obj = full + .as_object() + .ok_or_else(|| CircuitsErrors::Sample("chunk toml root must be an object".into()))?; + + let start = chunk_idx * chunk_size; + let end = start + chunk_size; + + let secret_key = match witness.dkg_input_type { + DkgInputType::SecretKey => "sk_secret", + DkgInputType::SmudgingNoise => "e_sm_secret", + }; + let secret = obj + .get(secret_key) + .ok_or_else(|| CircuitsErrors::Sample(format!("chunk toml missing {secret_key}")))?; + let chunk_json = match secret { + Value::Object(_) => { + // SK limb: {"coefficients": [c0..cn]} + let coeffs = secret + .get("coefficients") + .and_then(Value::as_array) + .ok_or_else(|| CircuitsErrors::Sample("chunk SK secret malformed".into()))?; + if end > coeffs.len() { + return Err(CircuitsErrors::Sample(format!( + "chunk [{start},{end}) out of bounds for secret of length {}", + coeffs.len() + ))); + } + Value::Object( + [( + "coefficients".into(), + Value::Array(coeffs[start..end].to_vec()), + )] + .into_iter() + .collect(), + ) + } + Value::Array(limbs) => { + // ESM crt limbs: [{ "coefficients": [..] }, ..] + let sliced = limbs + .iter() + .map(|limb| { + let coeffs = limb + .get("coefficients") + .and_then(Value::as_array) + .ok_or_else(|| { + CircuitsErrors::Sample("chunk e-sm limb malformed".into()) + })?; + if end > coeffs.len() { + return Err(CircuitsErrors::Sample(format!( + "chunk [{start},{end}) out of bounds for e-sm limb of length {}", + coeffs.len() + ))); + } + Ok::(Value::Object( + [( + "coefficients".into(), + Value::Array(coeffs[start..end].to_vec()), + )] + .into_iter() + .collect(), + )) + }) + .collect::, _>>()?; + Value::Array(sliced) + } + other => { + return Err(CircuitsErrors::Sample(format!( + "chunk secret has unexpected JSON kind: {}", + match other { + Value::Null => "null", + Value::Bool(_) => "bool", + Value::Number(_) => "number", + Value::String(_) => "string", + _ => "unknown", + } + ))) + } + }; + + let y_chunk = obj + .get("y") + .and_then(Value::as_array) + .ok_or_else(|| CircuitsErrors::Sample("chunk toml missing y".into()))? + .get(start..end) + .ok_or_else(|| { + CircuitsErrors::Sample(format!("chunk [{start},{end}) out of bounds for witness y")) + })? + .to_vec(); + + let json = serde_json::json!({ + "chunk_idx": chunk_idx, + "secret_chunk": chunk_json, + "y_chunk": y_chunk, + }); + + Ok(toml::to_string(&json)?) +} + /// Builds the configs.nr string (N, L, parity matrix, bit parameters, configs) for the Noir prover. /// /// `n_parties` and `threshold` are used to build the parity matrix (Reed–Solomon generator null space) @@ -55,9 +166,37 @@ pub fn generate_configs( bits: &Bits, n_parties: usize, threshold: usize, + chunk_size: usize, +) -> Result { + generate_configs_with_chunk_size(preset, bits, n_parties, threshold, chunk_size) +} + +pub fn generate_configs_with_chunk_size( + preset: BfvPreset, + bits: &Bits, + n_parties: usize, + threshold: usize, + chunk_size: usize, ) -> Result { let (threshold_params, _) = build_pair_for_preset(preset).map_err(|e| CircuitsErrors::Sample(e.to_string()))?; + if chunk_size == 0 { + return Err(CircuitsErrors::Sample( + "C2 chunk size must be greater than zero".into(), + )); + } + // C2's parity matrix must match a canonical (T, N) committee; reject arbitrary values. + crate::ciphernodes_committee::try_canonical_from_t_n(n_parties, threshold) + .map_err(|e| CircuitsErrors::Sample(e.to_string()))?; + let degree = threshold_params.degree(); + // The threshold polynomial degree is authoritative for C2 chunking. Both codegen.rs and + // computation.rs derive the chunk partition from it, and it must equal the secret limb length. + if !degree.is_multiple_of(chunk_size) { + return Err(CircuitsErrors::Sample(format!( + "C2 chunk size {chunk_size} must divide polynomial degree {degree}" + ))); + } + let chunk_count = degree / chunk_size; let config_name = preset.metadata().security.as_config_str(); let parity_matrix_str = parity_matrix_constant_string(&threshold_params, n_parties, threshold)?; let prefix = ::PREFIX; @@ -66,6 +205,8 @@ pub fn generate_configs( pub use crate::configs::{}::threshold::{{L as L_THRESHOLD, QIS as QIS_THRESHOLD}}; pub global N: u32 = {}; +pub global SHARE_COMPUTATION_CHUNK_SIZE: u32 = {}; +pub global SHARE_COMPUTATION_N_CHUNKS: u32 = {}; {} /************************************ @@ -96,7 +237,9 @@ pub global {}_E_SM_CONFIGS: ShareComputationConfigs = ShareComputationConfigs::new(QIS_THRESHOLD); "#, config_name, - preset.metadata().degree, + degree, + chunk_size, + chunk_count, parity_matrix_str, prefix, bits.bit_share, @@ -116,9 +259,9 @@ mod tests { use super::*; use crate::ciphernodes_committee::CiphernodesCommitteeSize; use crate::circuits::computation::Computation; + use crate::circuits::computation::DkgInputType; use crate::circuits::dkg::share_computation::{Bits, Bounds}; use crate::codegen::write_artifacts; - use crate::computation::DkgInputType; use crate::Circuit; use e3_fhe_params::BfvPreset; use tempfile::TempDir; @@ -186,4 +329,82 @@ mod tests { format!("{}_E_SM_BIT_SECRET: u32 = {}", prefix, bits.bit_e_sm_secret).as_str() )); } + + #[test] + fn test_chunk_toml_shape() { + let committee = CiphernodesCommitteeSize::Small.values(); + let mut sample = ShareComputationCircuitData::generate_sample( + BfvPreset::InsecureThreshold512, + committee, + DkgInputType::SecretKey, + ) + .unwrap(); + sample.chunk_size = 256; + let output = + ShareComputationCircuit::compute(BfvPreset::InsecureThreshold512, &sample).unwrap(); + let chunk = generate_chunk_toml(&output.inputs, 256, 1).unwrap(); + + let parsed: toml::Value = chunk.parse().unwrap(); + assert_eq!(parsed.get("chunk_idx").unwrap().as_integer(), Some(1)); + let secret_chunk = parsed.get("secret_chunk").unwrap(); + let coeffs = secret_chunk + .get("coefficients") + .unwrap() + .as_array() + .unwrap(); + assert_eq!(coeffs.len(), 256); + let y_chunk = parsed.get("y_chunk").unwrap().as_array().unwrap(); + assert_eq!(y_chunk.len(), 256); + } + + #[test] + fn test_chunk_size_is_written_to_configs() { + let committee = CiphernodesCommitteeSize::Minimum.values(); + let n_parties = committee.n; + let threshold = committee.threshold; + let sample = ShareComputationCircuitData::generate_sample( + BfvPreset::InsecureThreshold512, + committee, + DkgInputType::SecretKey, + ) + .unwrap(); + let bounds = Bounds::compute(BfvPreset::InsecureThreshold512, &sample).unwrap(); + let bits = Bits::compute(BfvPreset::InsecureThreshold512, &bounds).unwrap(); + let configs = generate_configs_with_chunk_size( + BfvPreset::InsecureThreshold512, + &bits, + n_parties, + threshold, + 256, + ) + .unwrap(); + + assert!(configs.contains("SHARE_COMPUTATION_CHUNK_SIZE: u32 = 256")); + assert!(configs.contains("SHARE_COMPUTATION_N_CHUNKS: u32 = 2")); + } + + #[test] + fn test_chunk_size_must_divide_degree() { + let committee = CiphernodesCommitteeSize::Minimum.values(); + let n_parties = committee.n; + let threshold = committee.threshold; + let sample = ShareComputationCircuitData::generate_sample( + BfvPreset::InsecureThreshold512, + committee, + DkgInputType::SecretKey, + ) + .unwrap(); + let bounds = Bounds::compute(BfvPreset::InsecureThreshold512, &sample).unwrap(); + let bits = Bits::compute(BfvPreset::InsecureThreshold512, &bounds).unwrap(); + let error = generate_configs_with_chunk_size( + BfvPreset::InsecureThreshold512, + &bits, + n_parties, + threshold, + 255, + ) + .unwrap_err(); + + assert!(error.to_string().contains("must divide polynomial degree")); + } } diff --git a/crates/zk-helpers/src/circuits/dkg/share_computation/computation.rs b/crates/zk-helpers/src/circuits/dkg/share_computation/computation.rs index c6aa208c7f..6a0e72b75a 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_computation/computation.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_computation/computation.rs @@ -12,7 +12,7 @@ use crate::bigint_3d_to_json_values; use crate::circuits::commitments::{ - compute_share_computation_e_sm_commitment, compute_share_computation_sk_commitment, + compute_sc_esm_secret_root_commitment, compute_sc_sk_secret_root_commitment, }; use crate::computation::DkgInputType; use crate::dkg::share_computation::ShareComputationCircuit; @@ -198,11 +198,30 @@ impl Computation for Inputs { let mut secret_crt = data.secret.clone(); let sss = &data.secret_sss; - if data.dkg_input_type == DkgInputType::SmudgingNoise { - // Normalize secret_crt to [0, q_j) per limb so it matches what we put in y and what the circuit expects (e_sm_secret[j][i] == y[i][j][0]). + // The threshold polynomial degree is authoritative for C2 chunking. The emitted + // SHARE_COMPUTATION_CHUNK_SIZE / SHARE_COMPUTATION_N_CHUNKS globals, the y vector, and + // the secret root commitments are all derived from it (see codegen.rs). Guard the + // secret limb length up front so a mismatched secret fails here instead of panicking + // in the commitment helper. + if secret_crt + .limbs + .first() + .is_none_or(|limb| limb.coefficients().len() != degree) + { + return Err(CircuitsErrors::Sample(format!( + "C2 secret limb length must equal polynomial degree {degree}" + ))); + } + + // Normalize both secret types to [0, q_j) so the chunk and normal circuits use the same + // field representation before they center values for the root commitment. + secret_crt + .reduce(moduli) + .map_err(|e| CircuitsErrors::Sample(format!("secret_crt reduce: {:?}", e)))?; + if data.dkg_input_type == DkgInputType::SecretKey { secret_crt - .reduce(moduli) - .map_err(|e| CircuitsErrors::Sample(format!("secret_crt reduce: {:?}", e)))?; + .center(moduli) + .map_err(|e| CircuitsErrors::Sample(format!("secret_crt center: {:?}", e)))?; } // y[coeff_idx][mod_idx][0] = secret_crt[mod_idx][coeff_idx] (already in [0, q_j)); y[coeff_idx][mod_idx][1+party] = share in [0, q_j). @@ -224,6 +243,20 @@ impl Computation for Inputs { let bounds = Bounds::compute(preset, data)?; let bits = Bits::compute(preset, &bounds)?; + let chunk_size = data.chunk_size as usize; + if chunk_size == 0 { + return Err(CircuitsErrors::Sample( + "C2 chunk size must be greater than zero".to_string(), + )); + } + // Validate against the same authoritative threshold degree used by codegen.rs to emit + // SHARE_COMPUTATION_CHUNK_SIZE / SHARE_COMPUTATION_N_CHUNKS, so the runtime chunk size + // can never disagree with the compiled chunk count. + if !degree.is_multiple_of(chunk_size) { + return Err(CircuitsErrors::Sample(format!( + "C2 chunk size {chunk_size} must divide polynomial degree {degree}" + ))); + } // Reverse+center before committing to match C1 (PkGeneration)'s convention: // C1 applies reverse then center to sk/e_sm before computing the commitment. // For SK the values are already centered ({-1,0,1}) so centering is a no-op. @@ -232,7 +265,7 @@ impl Computation for Inputs { DkgInputType::SecretKey => { let mut reversed = secret_crt.limb(0).clone(); reversed.reverse(); - compute_share_computation_sk_commitment(&reversed, bits.bit_sk_secret) + compute_sc_sk_secret_root_commitment(&reversed, bits.bit_sk_secret, chunk_size) } DkgInputType::SmudgingNoise => { let centered_reversed_crt = e3_polynomial::CrtPolynomial::new( @@ -249,9 +282,10 @@ impl Computation for Inputs { }) .collect(), ); - compute_share_computation_e_sm_commitment( + compute_sc_esm_secret_root_commitment( ¢ered_reversed_crt, bits.bit_e_sm_secret, + chunk_size, ) } }; @@ -316,6 +350,31 @@ mod tests { assert_eq!(bits.bit_sk_secret, expected_sk_bits); } + #[test] + fn insecure_smudging_ranges_match_pk_generation() { + let preset = BfvPreset::InsecureThreshold512; + for (size, expected_bits) in [ + (CiphernodesCommitteeSize::Minimum, 28), + (CiphernodesCommitteeSize::Micro, 30), + (CiphernodesCommitteeSize::Small, 31), + ] { + let committee = size.values(); + let sample = ShareComputationCircuitData::generate_sample( + preset, + committee.clone(), + DkgInputType::SmudgingNoise, + ) + .unwrap(); + let c2_bounds = Bounds::compute(preset, &sample).unwrap(); + let c2_bits = Bits::compute(preset, &c2_bounds).unwrap(); + let c1 = crate::threshold::pk_generation::Configs::compute(preset, &committee).unwrap(); + + assert_eq!(c1.bounds.e_sm_bound, c2_bounds.e_sm_bound); + assert_eq!(c1.bits.e_sm_bit, c2_bits.bit_e_sm_secret); + assert_eq!(c1.bits.e_sm_bit, expected_bits); + } + } + #[test] fn test_input_smudging_noise_secret_consistency() { let committee = CiphernodesCommitteeSize::Small.values(); diff --git a/crates/zk-helpers/src/circuits/dkg/share_computation/mod.rs b/crates/zk-helpers/src/circuits/dkg/share_computation/mod.rs index d379ad5768..ce01acb4cd 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_computation/mod.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_computation/mod.rs @@ -10,6 +10,8 @@ pub mod computation; pub mod sample; pub mod utils; -pub use circuit::{ShareComputationCircuit, ShareComputationCircuitData}; +pub use circuit::{ + ShareComputationChunkCircuit, ShareComputationCircuit, ShareComputationCircuitData, +}; pub use computation::{Bits, Bounds, Configs, Inputs, ShareComputationOutput}; pub use sample::SecretShares; diff --git a/crates/zk-helpers/src/circuits/dkg/share_computation/sample.rs b/crates/zk-helpers/src/circuits/dkg/share_computation/sample.rs index a1c7a8bff5..eb15e028db 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_computation/sample.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_computation/sample.rs @@ -121,6 +121,7 @@ impl ShareComputationCircuitData { dkg_input_type, n_parties: committee.n as u32, threshold: committee.threshold as u32, + chunk_size: 512, secret, secret_sss, parity_matrix, diff --git a/crates/zk-helpers/src/circuits/dkg/share_decryption/circuit.rs b/crates/zk-helpers/src/circuits/dkg/share_decryption/circuit.rs index 20f46a9f74..3b84fbfa37 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_decryption/circuit.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_decryption/circuit.rs @@ -8,6 +8,7 @@ use crate::computation::DkgInputType; use crate::registry::Circuit; +use crate::CiphernodesCommittee; use e3_fhe_params::ParameterType; use fhe::bfv::Ciphertext; use fhe::bfv::SecretKey; @@ -34,10 +35,16 @@ pub struct ShareDecryptionCircuitData { /// party does not self-encrypt during DKG); `Some(cts)` carries one ciphertext per /// CRT modulus for an external honest party. pub honest_ciphertexts: Vec>>, + /// Recipient party ID whose share row is decrypted. + pub recipient_party_id: u64, /// Own party's plaintext share row per modulus, shape `[L][N]` (length L, each /// inner Vec length N). Spliced into the H-sized list at the `None` slot when /// computing commitments and decrypted-share inputs. pub own_plaintext_share: Vec>, /// Which input type (SecretKey or SmudgingNoise) to resolve circuit path. pub dkg_input_type: DkgInputType, + /// Share-root chunk size (must equal `SHARE_COMPUTATION_CHUNK_SIZE` in Noir). + pub chunk_size: u32, + /// Committee this data was generated for (validated against the canonical table). + pub committee: CiphernodesCommittee, } diff --git a/crates/zk-helpers/src/circuits/dkg/share_decryption/codegen.rs b/crates/zk-helpers/src/circuits/dkg/share_decryption/codegen.rs index ecf054a9ef..140e93f94e 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_decryption/codegen.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_decryption/codegen.rs @@ -46,12 +46,14 @@ pub fn generate_toml(inputs: &Inputs) -> Result { } /// Builds the configs.nr string (N, L, bit parameters, and ShareDecryptionConfigs) for the Noir prover. -pub fn generate_configs(preset: BfvPreset, configs: &Configs) -> CodegenConfigs { +pub fn generate_configs(_preset: BfvPreset, configs: &Configs) -> CodegenConfigs { let prefix = ::PREFIX; format!( r#"pub global N: u32 = {}; pub global L: u32 = {}; +pub global SHARE_COMPUTATION_CHUNK_SIZE: u32 = {}; +pub global SHARE_COMPUTATION_N_CHUNKS: u32 = {}; /************************************ ------------------------------------- @@ -63,8 +65,10 @@ share_decryption_e_sm (CIRCUIT 4b - BFV DECRYPTION E_SM) pub global {}_BIT_MSG: u32 = {}; pub global {}_BIT_AGG: u32 = {}; "#, - preset.dkg_counterpart().unwrap().metadata().degree, - preset.dkg_counterpart().unwrap().metadata().num_moduli, + configs.n, + configs.l, + configs.chunk_size, + configs.n / configs.chunk_size, prefix, configs.bits.msg_bit, prefix, diff --git a/crates/zk-helpers/src/circuits/dkg/share_decryption/computation.rs b/crates/zk-helpers/src/circuits/dkg/share_decryption/computation.rs index 0155fb1b44..745b184dc9 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_decryption/computation.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_decryption/computation.rs @@ -19,7 +19,7 @@ //! aggregate hashing. Emitted as `SHARE_DECRYPTION_BIT_AGG`; the Noir C4 circuit uses it for //! `compute_aggregated_shares_commitment` on the sum (per-share verification still uses `BIT_MSG`). -use crate::circuits::commitments::compute_share_encryption_commitment_from_message; +use crate::circuits::commitments::compute_sc_party_share_root_commitment; use crate::dkg::share_decryption::ShareDecryptionCircuit; use crate::dkg::share_decryption::ShareDecryptionCircuitData; use crate::math::plaintext_poly_u64; @@ -71,6 +71,8 @@ pub struct Configs { pub n: usize, /// Number of CRT moduli (L). pub l: usize, + /// Share computation chunk size (matches SHARE_COMPUTATION_CHUNK_SIZE). + pub chunk_size: usize, /// Number of honest parties (H). pub h: usize, pub bits: Bits, @@ -102,6 +104,8 @@ pub struct Inputs { pub expected_commitments: Vec>, // [H][L] /// Decrypted share coefficients per party and modulus: [party_idx][mod_idx][coeff_idx]. pub decrypted_shares: Vec>>, // [H][L][N] + /// Zero-based recipient party index used in the C2 commitment domain. + pub recipient_party_idx: u64, } impl Computation for Configs { @@ -123,6 +127,7 @@ impl Computation for Configs { Ok(Configs { n, l, + chunk_size: data.chunk_size as usize, h, bits, bounds, @@ -166,6 +171,23 @@ impl Computation for Inputs { build_pair_for_preset(preset).map_err(|e| CircuitsErrors::Sample(e.to_string()))?; let threshold_l = threshold_params.moduli().len(); + let canonical = + crate::ciphernodes_committee::canonical_committee_for_circuit(&data.committee) + .map_err(|e| CircuitsErrors::Other(e.to_string()))?; + if data.honest_ciphertexts.len() != canonical.h { + return Err(CircuitsErrors::Other(format!( + "honest_ciphertexts has {} slots but canonical H is {}", + data.honest_ciphertexts.len(), + canonical.h + ))); + } + if data.recipient_party_id as usize >= canonical.n { + return Err(CircuitsErrors::Other(format!( + "C4 recipient_party_id {} out of range: committee N is {}", + data.recipient_party_id, canonical.n + ))); + } + let mut expected_commitments: Vec> = Vec::new(); let mut decrypted_shares: Vec>> = Vec::new(); @@ -183,6 +205,13 @@ impl Computation for Inputs { // Iterate H slots in ascending honest-party order: external slots BFV-decrypt and // commit; the own slot uses the supplied plaintext directly. + let chunk_size = data.chunk_size as usize; + let dkg_degree = dkg_params.degree(); + if chunk_size == 0 || !dkg_degree.is_multiple_of(chunk_size) { + return Err(CircuitsErrors::Sample(format!( + "C4 chunk size {chunk_size} must divide share degree {dkg_degree}" + ))); + } for slot in data.honest_ciphertexts.iter() { let mut party_commitments = Vec::with_capacity(threshold_l); let mut party_shares = Vec::with_capacity(threshold_l); @@ -208,9 +237,12 @@ impl Computation for Inputs { // Reverse to match C3's reversed commitment convention. let mut reversed_coeffs = share_coeffs.clone(); reversed_coeffs.reverse(); - party_commitments.push(compute_share_encryption_commitment_from_message( + party_commitments.push(compute_sc_party_share_root_commitment( + data.recipient_party_id as usize, + mod_idx, &Polynomial::from_u64_vector(reversed_coeffs), msg_bit, + chunk_size, )); party_shares.push( share_coeffs @@ -226,9 +258,12 @@ impl Computation for Inputs { // Same reverse-then-commit as the BFV-decrypted branch. let mut reversed_coeffs = share_coeffs.clone(); reversed_coeffs.reverse(); - party_commitments.push(compute_share_encryption_commitment_from_message( + party_commitments.push(compute_sc_party_share_root_commitment( + data.recipient_party_id as usize, + mod_idx, &Polynomial::from_u64_vector(reversed_coeffs), msg_bit, + chunk_size, )); party_shares.push( share_coeffs @@ -247,6 +282,7 @@ impl Computation for Inputs { Ok(Inputs { expected_commitments, decrypted_shares, + recipient_party_idx: data.recipient_party_id, }) } @@ -269,6 +305,7 @@ impl Computation for Inputs { let json = serde_json::json!({ "expected_commitments": expected_commitments, "decrypted_shares": decrypted_shares, + "recipient_party_idx": self.recipient_party_idx, }); Ok(json) @@ -379,9 +416,12 @@ mod tests { // with C2's commit_to_party_shares (highest-degree-first convention). let mut reversed = share_coeffs.clone(); reversed.reverse(); - let direct_commitment = compute_share_encryption_commitment_from_message( + let direct_commitment = compute_sc_party_share_root_commitment( + inputs.recipient_party_idx as usize, + mod_idx, &Polynomial::from_u64_vector(reversed), msg_bit, + sample.chunk_size as usize, ); assert_eq!( inputs.expected_commitments[party_idx][mod_idx], direct_commitment, diff --git a/crates/zk-helpers/src/circuits/dkg/share_decryption/sample.rs b/crates/zk-helpers/src/circuits/dkg/share_decryption/sample.rs index f937c9cd57..ce1a61f2e2 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_decryption/sample.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_decryption/sample.rs @@ -151,9 +151,12 @@ impl ShareDecryptionCircuitData { Ok(ShareDecryptionCircuitData { honest_ciphertexts, + recipient_party_id: 0, own_plaintext_share, secret_key: dkg_secret_key, dkg_input_type, + chunk_size: 512, + committee, }) } } diff --git a/crates/zk-helpers/src/circuits/dkg/share_encryption/circuit.rs b/crates/zk-helpers/src/circuits/dkg/share_encryption/circuit.rs index 0ccd9359e6..b3db9cff98 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_encryption/circuit.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_encryption/circuit.rs @@ -8,6 +8,7 @@ use crate::computation::DkgInputType; use crate::registry::Circuit; +use crate::CiphernodesCommittee; use e3_fhe_params::ParameterType; use fhe::bfv::Ciphertext; use fhe::bfv::Plaintext; @@ -45,4 +46,12 @@ pub struct ShareEncryptionCircuitData { pub e1_rns: Poly, /// Type of DKG input (SecretKey or SmudgingNoise) to determine which circuit variant to use. pub dkg_input_type: DkgInputType, + /// Zero-based recipient party index used in the share-root commitment. + pub party_idx: u32, + /// Zero-based CRT modulus index used in the share-root commitment. + pub mod_idx: u32, + /// Share-root chunk size (must equal `SHARE_COMPUTATION_CHUNK_SIZE` in Noir). + pub chunk_size: u32, + /// Committee this data was generated for (validated against the canonical table). + pub committee: CiphernodesCommittee, } diff --git a/crates/zk-helpers/src/circuits/dkg/share_encryption/codegen.rs b/crates/zk-helpers/src/circuits/dkg/share_encryption/codegen.rs index cab5533dce..53c28e38d7 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_encryption/codegen.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_encryption/codegen.rs @@ -47,7 +47,7 @@ pub fn generate_toml(inputs: &Inputs) -> Result { } /// Builds the configs.nr string (N, L, bit parameters, bounds, and ShareEncryptionConfigs) for the Noir prover. -pub fn generate_configs(preset: BfvPreset, configs: &Configs) -> CodegenConfigs { +pub fn generate_configs(_preset: BfvPreset, configs: &Configs) -> CodegenConfigs { let prefix = ::PREFIX; let qis_str = join_display(&configs.moduli, ", "); @@ -64,6 +64,8 @@ pub fn generate_configs(preset: BfvPreset, configs: &Configs) -> CodegenConfigs pub global N: u32 = {}; pub global L: u32 = {}; +pub global SHARE_COMPUTATION_CHUNK_SIZE: u32 = {}; +pub global SHARE_COMPUTATION_N_CHUNKS: u32 = {}; pub global QIS: [Field; L] = [{}]; pub global PLAINTEXT_MODULUS: Field = {}; pub global Q_MOD_T: Field = {}; @@ -116,8 +118,10 @@ pub global {}_CONFIGS: ShareEncryptionConfigs = ShareEncryptionConfigs::new( {}_MSG_BOUND, ); "#, - preset.dkg_counterpart().unwrap().metadata().degree, - preset.dkg_counterpart().unwrap().metadata().num_moduli, + configs.n, + configs.l, + configs.chunk_size, + configs.n / configs.chunk_size, qis_str, configs.t, configs.q_mod_t, @@ -244,4 +248,43 @@ mod tests { .contains(format!("{}_BIT_MSG: u32 = {}", prefix, bits.msg_bit).as_str())); assert!(artifacts.configs.contains("SHARE_ENCRYPTION_CONFIGS")); } + + #[test] + fn test_codegen_secure_preset_configs_match_dkg_params() { + let committee = CiphernodesCommitteeSize::Small.values(); + let sd = BfvPreset::SecureThreshold8192.search_defaults().unwrap(); + let sample = ShareEncryptionCircuitData::generate_sample( + BfvPreset::SecureThreshold8192, + committee.clone(), + DkgInputType::SecretKey, + sd.z, + ) + .unwrap(); + + let artifacts = ShareEncryptionCircuit + .codegen(BfvPreset::SecureThreshold8192, &sample) + .unwrap(); + + let (_, dkg_params) = + e3_fhe_params::build_pair_for_preset(BfvPreset::SecureThreshold8192).unwrap(); + let n = dkg_params.degree(); + let l = dkg_params.moduli().len(); + + assert!(artifacts + .configs + .contains(format!("N: u32 = {}", n).as_str())); + assert!(artifacts + .configs + .contains(format!("L: u32 = {}", l).as_str())); + assert!(artifacts.configs.contains( + format!("SHARE_COMPUTATION_CHUNK_SIZE: u32 = {}", sample.chunk_size).as_str() + )); + assert!(artifacts.configs.contains( + format!( + "SHARE_COMPUTATION_N_CHUNKS: u32 = {}", + n / sample.chunk_size as usize + ) + .as_str() + )); + } } diff --git a/crates/zk-helpers/src/circuits/dkg/share_encryption/computation.rs b/crates/zk-helpers/src/circuits/dkg/share_encryption/computation.rs index 19553c5258..03aeca4dd3 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_encryption/computation.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_encryption/computation.rs @@ -11,7 +11,7 @@ //! normalized for the ZKP field so the Noir circuit's range checks and commitment checks succeed. use crate::circuits::commitments::{ - compute_dkg_pk_commitment, compute_share_encryption_commitment_from_message, + compute_dkg_pk_commitment, compute_sc_party_share_root_commitment, }; use crate::dkg::share_encryption::ShareEncryptionCircuit; use crate::dkg::share_encryption::ShareEncryptionCircuitData; @@ -71,6 +71,12 @@ impl CircuitComputation for ShareEncryptionCircuit { /// Global configs for the share-encryption circuit: plaintext modulus, [q]_t, moduli, k0is, bits, and bounds. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Configs { + /// Circuit degree N. + pub n: usize, + /// Number of CRT moduli L. + pub l: usize, + /// Share computation chunk size (matches SHARE_COMPUTATION_CHUNK_SIZE). + pub chunk_size: usize, /// Plaintext modulus (as usize). pub t: usize, /// [q]_t reduced to ZKP field modulus. @@ -121,6 +127,8 @@ pub struct Bounds { /// that the ciphertext and commitments match the public inputs. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Inputs { + pub party_idx: u32, + pub mod_idx: u32, /// Public key and ciphertext polynomials in CRT form (per modulus). pub pk0is: CrtPolynomial, pub pk1is: CrtPolynomial, @@ -160,6 +168,9 @@ impl Computation for Configs { let bits = Bits::compute(preset, &bounds)?; Ok(Configs { + n: dkg_params.degree(), + l: moduli.len(), + chunk_size: data.chunk_size as usize, t: t as usize, q_mod_t, q_mod_t_centered, @@ -339,8 +350,11 @@ impl Computation for Inputs { type Error = CircuitsErrors; fn compute(_preset: Self::Preset, data: &Self::Data) -> Result { - let (_, dkg_params) = + let (threshold_params, dkg_params) = build_pair_for_preset(_preset).map_err(|e| CircuitsErrors::Sample(e.to_string()))?; + // row_index (mod_idx) indexes the threshold secret's modulus domain (one C3 proof per + // threshold Shamir row); it is not bounded by the DKG moduli count. + let threshold_l = threshold_params.moduli().len(); let pk = data.public_key.clone(); let pt = data.plaintext.clone(); @@ -404,6 +418,29 @@ impl Computation for Inputs { pk1.center(moduli)?; e0_crt.center(moduli)?; + crate::utils::verify_crt_shapes( + &[&ct0, &ct1, &pk0, &pk1, &e0_crt], + moduli.len(), + n as usize, + ) + .map_err(|e| CircuitsErrors::Other(format!("C3 CRT shape mismatch: {e}")))?; + + let canonical = + crate::ciphernodes_committee::canonical_committee_for_circuit(&data.committee) + .map_err(|e| CircuitsErrors::Other(e.to_string()))?; + if data.party_idx as usize >= canonical.n { + return Err(CircuitsErrors::Other(format!( + "C3 party_idx {} out of range: committee N is {}", + data.party_idx, canonical.n + ))); + } + if data.mod_idx as usize >= threshold_l { + return Err(CircuitsErrors::Other(format!( + "C3 mod_idx {} out of range: threshold L is {}", + data.mod_idx, threshold_l + ))); + } + let CrtPolynomial { limbs: ct0_limbs } = ct0; let CrtPolynomial { limbs: ct1_limbs } = ct1; let CrtPolynomial { limbs: pk0_limbs } = pk0; @@ -516,9 +553,33 @@ impl Computation for Inputs { let pk_bit = compute_modulus_bit(&dkg_params); let msg_bit = compute_msg_bit(&dkg_params); let pk_commitment = compute_dkg_pk_commitment(&pk0is, &pk1is, pk_bit); - let msg_commitment = compute_share_encryption_commitment_from_message(&message, msg_bit); + if data.chunk_size == 0 { + return Err(CircuitsErrors::Sample( + "C3 chunk size must be greater than zero".to_string(), + )); + } + if !message + .coefficients() + .len() + .is_multiple_of(data.chunk_size as usize) + { + return Err(CircuitsErrors::Sample(format!( + "C3 chunk size {} must divide message degree {}", + data.chunk_size, + message.coefficients().len() + ))); + } + let msg_commitment = compute_sc_party_share_root_commitment( + data.party_idx as usize, + data.mod_idx as usize, + &message, + msg_bit, + data.chunk_size as usize, + ); Ok(Inputs { + party_idx: data.party_idx, + mod_idx: data.mod_idx, pk0is, pk1is, ct0is, @@ -574,6 +635,8 @@ impl Computation for Inputs { "p2is": p2is, "expected_pk_commitment": pk_commitment, "expected_message_commitment": msg_commitment, + "party_idx": self.party_idx, + "mod_idx": self.mod_idx, }); Ok(json) diff --git a/crates/zk-helpers/src/circuits/dkg/share_encryption/sample.rs b/crates/zk-helpers/src/circuits/dkg/share_encryption/sample.rs index 5d9f63838c..1049180113 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_encryption/sample.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_encryption/sample.rs @@ -113,6 +113,10 @@ impl ShareEncryptionCircuitData { e0_rns, e1_rns, dkg_input_type, + party_idx: 0, + mod_idx: 0, + chunk_size: 512, + committee, }) } } diff --git a/crates/zk-helpers/src/circuits/output_layout.rs b/crates/zk-helpers/src/circuits/output_layout.rs index f04833f5fa..53d07a862d 100644 --- a/crates/zk-helpers/src/circuits/output_layout.rs +++ b/crates/zk-helpers/src/circuits/output_layout.rs @@ -28,28 +28,21 @@ pub struct OutputField { /// /// `fields` lists them in the order they appear in `public_signals`, /// which is the same order as the Noir `-> pub (A, B, C)` tuple. -/// -/// Circuits whose output count depends on runtime parameters (e.g. -/// `SkShareComputation` / `ESmShareComputation` whose return is `[[Field; L]; N]`) -/// use [`CircuitOutputLayout::Dynamic`]. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum CircuitOutputLayout { /// Fixed number of `Field`-sized outputs, names known at compile time. Fixed { fields: &'static [OutputField] }, /// The circuit returns no public values (void). None, - /// Output count depends on runtime parameters — callers must supply the - /// element count themselves. - Dynamic, } impl CircuitOutputLayout { - /// Number of fixed output fields, or `None` for dynamic / void layouts. + /// Number of fixed output fields. Void layouts report `Some(0)`, because no arm + /// returns `None`. pub fn field_count(&self) -> Option { match self { CircuitOutputLayout::Fixed { fields } => Some(fields.len()), CircuitOutputLayout::None => Some(0), - CircuitOutputLayout::Dynamic => None, } } @@ -57,7 +50,7 @@ impl CircuitOutputLayout { pub fn field_index(&self, name: &str) -> Option { match self { CircuitOutputLayout::Fixed { fields } => fields.iter().position(|f| f.name == name), - _ => None, + CircuitOutputLayout::None => None, } } @@ -68,7 +61,7 @@ impl CircuitOutputLayout { pub fn extract_field<'a>(&self, public_signals: &'a [u8], name: &str) -> Option<&'a [u8]> { let fields = match self { CircuitOutputLayout::Fixed { fields } => fields, - _ => return None, + CircuitOutputLayout::None => return None, }; let idx = fields.iter().position(|f| f.name == name)?; let total_output_bytes = fields.len() * FIELD_BYTE_LEN; @@ -90,7 +83,6 @@ impl CircuitOutputLayout { let fields = match self { CircuitOutputLayout::Fixed { fields } => fields, CircuitOutputLayout::None => return Some(Vec::new()), - CircuitOutputLayout::Dynamic => return None, }; let total_output_bytes = fields.len() * FIELD_BYTE_LEN; if public_signals.len() < total_output_bytes { @@ -150,6 +142,8 @@ pub const THRESHOLD_SHARE_DECRYPTION_OUTPUTS: &[OutputField] = &[f("d_commitment pub const SHARE_ENCRYPTION_INPUTS: &[OutputField] = &[ f("expected_pk_commitment"), f("expected_message_commitment"), + f("party_idx"), + f("mod_idx"), ]; /// Describes the public input layout of a circuit. @@ -268,13 +262,6 @@ mod tests { assert!(layout.extract_field(&signals, "anything").is_none()); } - #[test] - fn extract_from_dynamic_circuit_returns_none() { - let layout = CircuitOutputLayout::Dynamic; - let signals = vec![0u8; 256]; - assert!(layout.extract_field(&signals, "anything").is_none()); - } - #[test] fn signals_too_short_returns_none() { let layout = CircuitOutputLayout::Fixed { @@ -313,7 +300,6 @@ mod tests { Some(3) ); assert_eq!(CircuitOutputLayout::None.field_count(), Some(0)); - assert_eq!(CircuitOutputLayout::Dynamic.field_count(), None); } #[test] @@ -441,7 +427,7 @@ mod tests { fields: SHARE_ENCRYPTION_INPUTS } .field_count(), - Some(2) + Some(4) ); assert_eq!(CircuitInputLayout::None.field_count(), Some(0)); } diff --git a/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs b/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs index 97e15137db..63089241e9 100644 --- a/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs +++ b/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs @@ -217,14 +217,60 @@ impl Computation for Inputs { let d_share_polys: Vec> = data.d_share_polys.clone(); - if d_share_polys.len() < threshold + 1 { + let dropped_non_zero = data + .message_vec + .iter() + .enumerate() + .skip(max_msg_non_zero_coeffs) + .filter(|(_, v)| **v != 0) + .count(); + if dropped_non_zero > 0 { + return Err(CircuitsErrors::Other(format!( + "message_vec has {} non-zero coefficient(s) at index >= MAX_MSG_NON_ZERO_COEFFS ({}); they would be silently dropped by truncation", + dropped_non_zero, max_msg_non_zero_coeffs + ))); + } + + if d_share_polys.len() != threshold + 1 { return Err(CircuitsErrors::Other(format!( - "d_share_polys.len() {} < threshold + 1 ({}); need at least {} polynomials", + "d_share_polys.len() {} != threshold + 1 ({}); the C7 circuit requires exactly T + 1 shares", d_share_polys.len(), - threshold + 1, threshold + 1 ))); } + let reconstructing_parties = &data.reconstructing_parties; + if reconstructing_parties.len() != threshold + 1 { + return Err(CircuitsErrors::Other(format!( + "reconstructing_parties.len() {} != threshold + 1 ({}); must match the d_share_polys count", + reconstructing_parties.len(), + threshold + 1 + ))); + } + { + let mut seen = std::collections::BTreeSet::new(); + for &party in reconstructing_parties { + if party == 0 { + return Err(CircuitsErrors::Other( + "reconstructing_parties must be 1-based; found 0".to_string(), + )); + } + if party > data.committee.n { + return Err(CircuitsErrors::Other(format!( + "reconstructing party {party} exceeds committee size N={}", + data.committee.n + ))); + } + if !seen.insert(party) { + return Err(CircuitsErrors::Other(format!( + "reconstructing party {party} appears more than once" + ))); + } + } + } + // Validate committee shape against the canonical table (T, N, H must match the + // compiled Noir committee). + crate::ciphernodes_committee::canonical_committee_for_circuit(&data.committee) + .map_err(|e| CircuitsErrors::Other(e.to_string()))?; // Decryption shares: one CrtPolynomial per party (from_fhe + reduce) let mut decryption_shares: Vec = Vec::with_capacity(d_share_polys.len()); diff --git a/crates/zk-helpers/src/circuits/threshold/pk_aggregation/computation.rs b/crates/zk-helpers/src/circuits/threshold/pk_aggregation/computation.rs index ea98885739..cdcdb4c62d 100644 --- a/crates/zk-helpers/src/circuits/threshold/pk_aggregation/computation.rs +++ b/crates/zk-helpers/src/circuits/threshold/pk_aggregation/computation.rs @@ -155,6 +155,18 @@ impl Computation for Inputs { let (threshold_params, _) = build_pair_for_preset(preset).map_err(|e| CircuitsErrors::Other(e.to_string()))?; + let canonical = + crate::ciphernodes_committee::canonical_committee_for_circuit(&data.committee) + .map_err(|e| CircuitsErrors::Other(e.to_string()))?; + if data.pk0_shares.len() < canonical.h { + return Err(CircuitsErrors::Other(format!( + "pk0_shares has {} shares but canonical H is {}", + data.pk0_shares.len(), + canonical.h + ))); + } + let h = canonical.h; + let bit_pk = compute_modulus_bit(&threshold_params); let mut pk0: Vec = data.pk0_shares.clone(); @@ -166,7 +178,7 @@ impl Computation for Inputs { let mut expected_threshold_pk_commitments = Vec::new(); - for pk in pk0.iter_mut().take(data.committee.h) { + for pk in pk0.iter_mut().take(h) { pk.reverse(); pk.center(threshold_params.moduli())?; diff --git a/crates/zk-helpers/src/circuits/threshold/pk_generation/computation.rs b/crates/zk-helpers/src/circuits/threshold/pk_generation/computation.rs index d2b148a6a1..5b4bc26e41 100644 --- a/crates/zk-helpers/src/circuits/threshold/pk_generation/computation.rs +++ b/crates/zk-helpers/src/circuits/threshold/pk_generation/computation.rs @@ -258,7 +258,15 @@ impl Computation for Inputs { let (threshold_params, _) = build_pair_for_preset(preset).map_err(|e| CircuitsErrors::Other(e.to_string()))?; + let n = threshold_params.degree(); + let l = threshold_params.moduli().len(); let a = deterministic_crp_crt_polynomial(&threshold_params)?; + crate::utils::verify_crt_shapes( + &[&data.pk0_share, &a, &data.eek, &data.e_sm, &data.sk], + l, + n, + ) + .map_err(|e| CircuitsErrors::Other(format!("C1 CRT shape mismatch: {e}")))?; let moduli: Vec = threshold_params .moduli() diff --git a/crates/zk-helpers/src/circuits/threshold/share_decryption/computation.rs b/crates/zk-helpers/src/circuits/threshold/share_decryption/computation.rs index 4313d5226c..311f00bdc4 100644 --- a/crates/zk-helpers/src/circuits/threshold/share_decryption/computation.rs +++ b/crates/zk-helpers/src/circuits/threshold/share_decryption/computation.rs @@ -266,6 +266,13 @@ impl Computation for Inputs { let ct0 = CrtPolynomial::from_fhe_polynomial(&data.ciphertext[0]); let ct1 = CrtPolynomial::from_fhe_polynomial(&data.ciphertext[1]); + crate::utils::verify_crt_shapes( + &[&ct0, &ct1, &data.s, &data.e, &data.d_share], + moduli.len(), + n as usize, + ) + .map_err(|e| CircuitsErrors::Other(format!("C6 CRT shape mismatch: {e}")))?; + // Create cyclotomic polynomial x^N + 1 let mut cyclo = vec![BigInt::from(0u64); (n + 1) as usize]; cyclo[0] = BigInt::from(1u64); // constant (x^0) term diff --git a/crates/zk-helpers/src/circuits/threshold/user_data_encryption/computation.rs b/crates/zk-helpers/src/circuits/threshold/user_data_encryption/computation.rs index fdeb715f19..7a81c06851 100644 --- a/crates/zk-helpers/src/circuits/threshold/user_data_encryption/computation.rs +++ b/crates/zk-helpers/src/circuits/threshold/user_data_encryption/computation.rs @@ -402,6 +402,11 @@ impl Computation for Inputs { e0.center(moduli)?; + crate::utils::verify_crt_shapes(&[&ct0, &ct1, &pk0, &pk1, &e0], moduli.len(), n as usize) + .map_err(|e| { + CircuitsErrors::Other(format!("user-data-encryption CRT shape mismatch: {e}")) + })?; + let CrtPolynomial { limbs: ct0_limbs } = ct0; let CrtPolynomial { limbs: ct1_limbs } = ct1; let CrtPolynomial { limbs: pk0_limbs } = pk0; diff --git a/crates/zk-helpers/src/utils.rs b/crates/zk-helpers/src/utils.rs index da46e794a9..cac5e21fe0 100644 --- a/crates/zk-helpers/src/utils.rs +++ b/crates/zk-helpers/src/utils.rs @@ -281,6 +281,34 @@ pub fn polynomial_to_toml_json(polynomial: &Polynomial) -> serde_json::Value { poly_coefficients_to_toml_json(polynomial.coefficients()) } +/// Verify that a CRT polynomial has exactly `l` limbs, each with exactly `degree` coefficients. +/// +/// Returns an error when the shape does not match so that zipped computations fail closed +/// instead of silently truncating mismatched limbs. +pub fn validate_crt_shape(crt: &CrtPolynomial, l: usize, degree: usize) -> anyhow::Result<()> { + if crt.limbs.len() != l { + anyhow::bail!("expected {l} CRT limbs, got {}", crt.limbs.len()); + } + for (i, limb) in crt.limbs.iter().enumerate() { + if limb.coefficients().len() != degree { + anyhow::bail!( + "CRT limb {i} has {} coefficients; expected {degree}", + limb.coefficients().len() + ); + } + } + Ok(()) +} + +/// Verify that every CRT polynomial in `crts` matches the expected `(l, degree)` shape. +pub fn verify_crt_shapes(crts: &[&CrtPolynomial], l: usize, degree: usize) -> anyhow::Result<()> { + for (i, crt) in crts.iter().enumerate() { + validate_crt_shape(crt, l, degree) + .map_err(|e| anyhow::Error::msg(format!("CRT polynomial at index {i}: {e}")))?; + } + Ok(()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/zk-prover/src/circuits/aggregation/c2_chunk_batch.rs b/crates/zk-prover/src/circuits/aggregation/c2_chunk_batch.rs new file mode 100644 index 0000000000..86866c924b --- /dev/null +++ b/crates/zk-prover/src/circuits/aggregation/c2_chunk_batch.rs @@ -0,0 +1,226 @@ +// 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. + +//! Batch C2 chunk proofs and root finalization. + +use crate::circuits::aggregation::c2_chunk_layout::C2ChunkLayout; +use crate::circuits::utils::{bytes_to_field_strings, inputs_json_to_input_map}; +use crate::circuits::vk; +use crate::error::ZkError; +use crate::prover::ZkProver; +use crate::witness::{CompiledCircuit, WitnessGenerator}; +use e3_events::{CircuitName, CircuitVariant, Proof}; +use rayon::prelude::*; +use serde::Serialize; + +const BATCH_PUBLIC_PREFIX_LEN: usize = 2; +#[derive(Serialize)] +struct C2ChunkBatchInput { + chunk_vk: Vec, + chunk_proofs: Vec>, + chunk_public_inputs: Vec>, + chunk_key_hash: String, + batch_idx: u32, +} + +#[derive(Serialize)] +struct C2ChunkBatchFinalizeInput { + batch_vk: Vec, + batch_proofs: Vec>, + batch_public_inputs: Vec>, + batch_key_hash: String, +} + +fn public_fields(proof: &Proof, context: &str) -> Result, ZkError> { + bytes_to_field_strings(proof.public_signals.as_ref()) + .map_err(|error| ZkError::InvalidInput(format!("{context} public signals: {error}"))) +} + +/// Aggregate fixed-size groups of independent C2 chunk proofs. +pub fn generate_c2_chunk_batches( + prover: &ZkProver, + chunk_circuit: CircuitName, + chunk_proofs: &[Proof], + chunk_count: usize, + degree: usize, + e3_id: &str, + artifacts_dir: &str, +) -> Result, ZkError> { + if chunk_count == 0 || chunk_proofs.len() != chunk_count { + return Err(ZkError::InvalidInput(format!( + "C2 chunk batches require {chunk_count} chunk proofs, got {}", + chunk_proofs.len() + ))); + } + let layout = C2ChunkLayout::compiled(degree)?; + if chunk_count != layout.chunk_count { + return Err(ZkError::InvalidInput(format!( + "C2 chunk count {chunk_count} does not match compiled artifact count {}", + layout.chunk_count + ))); + } + if !chunk_count.is_multiple_of(layout.chunks_per_batch) { + return Err(ZkError::InvalidInput(format!( + "C2 chunk count {chunk_count} is not divisible by batch size {}", + layout.chunks_per_batch + ))); + } + let batch_count = chunk_count / layout.chunks_per_batch; + if batch_count != layout.batch_count { + return Err(ZkError::InvalidInput(format!( + "C2 batch count {batch_count} does not match compiled artifact count {}", + layout.batch_count + ))); + } + + let circuits_dir = prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir); + let chunk_vk = vk::load_vk_artifacts(&circuits_dir, chunk_circuit)?; + let batch_circuit_path = prover + .circuits_dir(CircuitVariant::Default, artifacts_dir) + .join(CircuitName::C2ChunkBatch.dir_path()) + .join(format!("{}.json", CircuitName::C2ChunkBatch.as_str())); + let compiled = CompiledCircuit::from_file(&batch_circuit_path)?; + + let batches = chunk_proofs + .par_chunks(layout.chunks_per_batch) + .enumerate() + .map(|(batch_idx, proofs)| { + let mut chunk_public_inputs = Vec::with_capacity(layout.chunks_per_batch); + let mut chunk_proof_fields = Vec::with_capacity(layout.chunks_per_batch); + for proof in proofs { + if proof.circuit != chunk_circuit { + return Err(ZkError::InvalidInput(format!( + "C2 chunk batch expected {}, got {}", + chunk_circuit, proof.circuit + ))); + } + chunk_public_inputs.push(public_fields(proof, "C2 chunk")?); + chunk_proof_fields.push(bytes_to_field_strings(&proof.data)?); + } + + let input = C2ChunkBatchInput { + chunk_vk: chunk_vk.verification_key.clone(), + chunk_proofs: chunk_proof_fields, + chunk_public_inputs, + chunk_key_hash: chunk_vk.key_hash.clone(), + batch_idx: batch_idx as u32, + }; + let json = serde_json::to_value(&input) + .map_err(|error| ZkError::SerializationError(error.to_string()))?; + let input_map = inputs_json_to_input_map(&json)?; + let witness = WitnessGenerator::new() + .generate_witness(&compiled, input_map) + .map_err(|error| { + ZkError::WitnessGenerationFailed(format!( + "C2 batch {batch_idx} witness: {error}" + )) + })?; + prover.generate_recursive_aggregation_bin_proof( + CircuitName::C2ChunkBatch, + &witness, + &format!("{e3_id}-c2-batch-{batch_idx}"), + artifacts_dir, + ) + }) + .collect::, _>>()?; + Ok(batches) +} + +/// Verify all C2 chunk batches and reconstruct the C2 root commitments. +pub fn finalize_c2_chunk_batches( + prover: &ZkProver, + batch_proofs: &[Proof], + finalizer_circuit: CircuitName, + e3_id: &str, + artifacts_dir: &str, +) -> Result { + if finalizer_circuit != CircuitName::SkC2ChunkFinalize + && finalizer_circuit != CircuitName::ESmC2ChunkFinalize + { + return Err(ZkError::InvalidInput(format!( + "invalid C2 chunk finalizer circuit {finalizer_circuit}" + ))); + } + if batch_proofs.is_empty() { + return Err(ZkError::InvalidInput( + "C2 chunk finalizer requires at least one batch".into(), + )); + } + let batch_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::C2ChunkBatch, + )?; + let mut batch_public_inputs = Vec::with_capacity(batch_proofs.len()); + let mut batch_proof_fields = Vec::with_capacity(batch_proofs.len()); + let mut public_len = None; + for proof in batch_proofs { + if proof.circuit != CircuitName::C2ChunkBatch { + return Err(ZkError::InvalidInput(format!( + "C2 finalizer expected {}, got {}", + CircuitName::C2ChunkBatch, + proof.circuit + ))); + } + let fields = public_fields(proof, "C2 batch")?; + if public_len + .replace(fields.len()) + .is_some_and(|len| len != fields.len()) + { + return Err(ZkError::InvalidInput( + "C2 batch proofs have different public input lengths".into(), + )); + } + if fields.len() <= BATCH_PUBLIC_PREFIX_LEN { + return Err(ZkError::InvalidInput( + "C2 batch proof has no returned commitments".into(), + )); + } + batch_public_inputs.push(fields); + batch_proof_fields.push(bytes_to_field_strings(&proof.data)?); + } + + let circuit_path = prover + .circuits_dir(CircuitVariant::Default, artifacts_dir) + .join(finalizer_circuit.dir_path()) + .join(format!("{}.json", finalizer_circuit.as_str())); + let compiled = CompiledCircuit::from_file(&circuit_path)?; + let input = C2ChunkBatchFinalizeInput { + batch_vk: batch_vk.verification_key, + batch_proofs: batch_proof_fields, + batch_public_inputs, + batch_key_hash: batch_vk.key_hash, + }; + let json = serde_json::to_value(&input) + .map_err(|error| ZkError::SerializationError(error.to_string()))?; + let input_map = inputs_json_to_input_map(&json)?; + let witness = WitnessGenerator::new() + .generate_witness(&compiled, input_map) + .map_err(|error| { + ZkError::WitnessGenerationFailed(format!("C2 finalizer witness: {error}")) + })?; + prover.generate_proof_with_variant( + finalizer_circuit, + &witness, + &format!("{e3_id}-c2-finalize"), + CircuitVariant::Recursive, + artifacts_dir, + ) +} + +#[cfg(test)] +mod tests { + use super::C2ChunkLayout; + + #[test] + fn uses_one_batch_chunk_for_insecure_degree() { + assert_eq!(C2ChunkLayout::compiled(512).unwrap().chunks_per_batch, 1); + } + + #[test] + fn uses_four_batch_chunks_for_secure_degree() { + assert_eq!(C2ChunkLayout::compiled(8192).unwrap().chunks_per_batch, 4); + } +} diff --git a/crates/zk-prover/src/circuits/aggregation/c2_chunk_config.rs b/crates/zk-prover/src/circuits/aggregation/c2_chunk_config.rs new file mode 100644 index 0000000000..a49c1839ef --- /dev/null +++ b/crates/zk-prover/src/circuits/aggregation/c2_chunk_config.rs @@ -0,0 +1,34 @@ +// 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. + +/// Default coefficient count per C2 chunk in the compiled Noir artifacts. +pub const DEFAULT_C2_CHUNK_SIZE: usize = 512; +/// Default chunk count per C2 batch in the compiled Noir artifacts. +pub const DEFAULT_C2_CHUNKS_PER_BATCH: usize = 4; + +// The derived layout (`chunk_count`, `chunks_per_batch`, `batch_count`) is +// computed by `c2_chunk_layout::C2ChunkLayout` from these two constants. + +#[cfg(test)] +mod tests { + use crate::circuits::aggregation::c2_chunk_layout::C2ChunkLayout; + + #[test] + fn insecure_artifacts_use_one_chunk_and_batch() { + let layout = C2ChunkLayout::compiled(512).unwrap(); + assert_eq!(layout.chunk_count, 1); + assert_eq!(layout.chunks_per_batch, 1); + assert_eq!(layout.batch_count, 1); + } + + #[test] + fn secure_artifacts_use_sixteen_chunks_and_four_batches() { + let layout = C2ChunkLayout::compiled(8192).unwrap(); + assert_eq!(layout.chunk_count, 16); + assert_eq!(layout.chunks_per_batch, 4); + assert_eq!(layout.batch_count, 4); + } +} diff --git a/crates/zk-prover/src/circuits/aggregation/c2_chunk_layout.rs b/crates/zk-prover/src/circuits/aggregation/c2_chunk_layout.rs new file mode 100644 index 0000000000..5df218e082 --- /dev/null +++ b/crates/zk-prover/src/circuits/aggregation/c2_chunk_layout.rs @@ -0,0 +1,148 @@ +// 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. + +//! Versioned C2 chunk layout — the single derivation point for how a compiled +//! artifact set slices a polynomial degree into chunk and batch counts. +//! +//! Runtime code must not re-derive `chunk_count`, `chunks_per_batch`, or +//! `batch_count` from independent constants; use [`C2ChunkLayout`] instead. + +use crate::circuits::aggregation::c2_chunk_config::{ + DEFAULT_C2_CHUNKS_PER_BATCH, DEFAULT_C2_CHUNK_SIZE, +}; +use crate::error::ZkError; + +/// The complete C2 chunk layout implied by a compiled artifact set. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct C2ChunkLayout { + /// Polynomial degree of the preset (`N`). + pub degree: usize, + /// Coefficients per C2 leaf proof. + pub chunk_size: usize, + /// Number of leaf proofs per degree: `degree / chunk_size`. + pub chunk_count: usize, + /// Number of leaf proofs grouped into one `C2ChunkBatch` proof. + pub chunks_per_batch: usize, + /// Number of batch proofs: `chunk_count / chunks_per_batch`. + pub batch_count: usize, +} + +impl C2ChunkLayout { + /// Builds the layout for the compiled default chunk size (512). + pub fn compiled(degree: usize) -> Result { + Self::from_degree_chunk_size(degree, DEFAULT_C2_CHUNK_SIZE) + } + + /// Builds a layout with the compiled `chunks_per_batch` rule: a degree that + /// fits in a single chunk uses one batch; larger degrees use the compiled + /// constant batch width. + pub fn from_degree_chunk_size(degree: usize, chunk_size: usize) -> Result { + let chunks_per_batch = if degree <= chunk_size { + 1 + } else { + DEFAULT_C2_CHUNKS_PER_BATCH + }; + Self::new(degree, chunk_size, chunks_per_batch) + } + + /// Validates and builds a layout. Enforces every structural invariant the + /// C2 chunk pipeline depends on. + pub fn new(degree: usize, chunk_size: usize, chunks_per_batch: usize) -> Result { + if degree == 0 { + return Err(ZkError::InvalidInput( + "C2 chunk layout requires degree > 0".into(), + )); + } + if chunk_size == 0 { + return Err(ZkError::InvalidInput( + "C2 chunk layout requires chunk_size > 0".into(), + )); + } + if !degree.is_multiple_of(chunk_size) { + return Err(ZkError::InvalidInput(format!( + "C2 chunk size {chunk_size} must divide polynomial degree {degree}" + ))); + } + let chunk_count = degree / chunk_size; + if chunks_per_batch == 0 { + return Err(ZkError::InvalidInput( + "C2 chunk layout requires chunks_per_batch > 0".into(), + )); + } + if !chunk_count.is_multiple_of(chunks_per_batch) { + return Err(ZkError::InvalidInput(format!( + "C2 chunk count {chunk_count} is not divisible by chunks_per_batch {chunks_per_batch}" + ))); + } + Ok(Self { + degree, + chunk_size, + chunk_count, + chunks_per_batch, + batch_count: chunk_count / chunks_per_batch, + }) + } +} + +#[cfg(test)] +mod tests { + use super::C2ChunkLayout; + + #[test] + fn insecure_compiled_layout_is_one_chunk_one_batch() { + let layout = C2ChunkLayout::compiled(512).unwrap(); + assert_eq!( + layout, + C2ChunkLayout { + degree: 512, + chunk_size: 512, + chunk_count: 1, + chunks_per_batch: 1, + batch_count: 1, + } + ); + } + + #[test] + fn secure_compiled_layout_is_sixteen_chunks_four_batches() { + let layout = C2ChunkLayout::compiled(8192).unwrap(); + assert_eq!(layout.chunk_count, 16); + assert_eq!(layout.chunks_per_batch, 4); + assert_eq!(layout.batch_count, 4); + } + + #[test] + fn rejects_zero_degree() { + assert!(C2ChunkLayout::new(0, 512, 4).is_err()); + } + + #[test] + fn rejects_zero_chunk_size() { + assert!(C2ChunkLayout::new(512, 0, 4).is_err()); + } + + #[test] + fn rejects_chunk_size_not_dividing_degree() { + assert!(C2ChunkLayout::new(8192, 1000, 4).is_err()); + } + + #[test] + fn rejects_zero_chunks_per_batch() { + assert!(C2ChunkLayout::new(512, 512, 0).is_err()); + } + + #[test] + fn rejects_chunk_count_not_dividing_batch_width() { + assert!(C2ChunkLayout::new(8192, 512, 3).is_err()); + } + + #[test] + fn single_chunk_degree_uses_single_batch() { + let layout = C2ChunkLayout::from_degree_chunk_size(512, 512).unwrap(); + assert_eq!(layout.chunks_per_batch, 1); + assert_eq!(layout.batch_count, 1); + } +} diff --git a/crates/zk-prover/src/circuits/aggregation/c2_terminal_validation.rs b/crates/zk-prover/src/circuits/aggregation/c2_terminal_validation.rs new file mode 100644 index 0000000000..148dd39396 --- /dev/null +++ b/crates/zk-prover/src/circuits/aggregation/c2_terminal_validation.rs @@ -0,0 +1,427 @@ +// 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. + +//! C2 terminal-proof ingress validation. +//! +//! Binds each `SkC2ChunkFinalize` / `ESmC2ChunkFinalize` proof to the +//! deployment-time VK anchors (the canonical leaf chunk VK and the canonical +//! `C2ChunkBatch` VK) before the expensive recursive proof is verified. A +//! validation failure means the signed proof is invalid, not that the node is +//! misconfigured — the caller must treat it as an invalid signed proof. + +use crate::circuits::vk; +use crate::error::ZkError; +use crate::prover::ZkProver; +use e3_events::{CircuitName, CircuitVariant, Proof, ProofType}; +use e3_fhe_params::BfvPreset; +use e3_zk_helpers::CiphernodesCommitteeSize; +use num_bigint::BigUint; + +/// BN254 scalar field modulus `r` (field elements are canonical in `[0, r)`). +/// `0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001`. +const SCALAR_FIELD_MODULUS_HEX: &str = + "30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"; + +fn scalar_field_modulus() -> BigUint { + BigUint::parse_bytes(SCALAR_FIELD_MODULUS_HEX.as_bytes(), 16) + .expect("BN254 scalar field modulus hex is valid") +} + +/// Parses a `0x`-prefixed VK-anchor hex string into its reduced field value. +fn parse_anchor_hex(hex_str: &str) -> Result { + let bytes = hex::decode(hex_str.trim_start_matches("0x")) + .map_err(|error| ZkError::InvalidInput(format!("invalid VK anchor hex: {error}")))?; + Ok(BigUint::from_bytes_be(&bytes) % scalar_field_modulus()) +} + +/// Deployment-time VK anchors for C2 terminal-proof ingress validation. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct C2TerminalAnchors { + /// `0x`-prefixed hex hash of the canonical leaf chunk VK for this proof type. + pub chunk_vk_hash: String, + /// `0x`-prefixed hex hash of the canonical `C2ChunkBatch` VK. + pub batch_vk_hash: String, +} + +impl C2TerminalAnchors { + /// Loads the deployment anchors for a C2 terminal proof type from the + /// selected artifacts directory. Returns an error for non-C2 proof types. + pub fn load( + prover: &ZkProver, + proof_type: ProofType, + artifacts_dir: &str, + ) -> Result { + let chunk_circuit = match proof_type { + ProofType::C2aSkShareComputation => CircuitName::SkShareComputationChunk, + ProofType::C2bESmShareComputation => CircuitName::ESmShareComputationChunk, + _ => { + return Err(ZkError::InvalidInput(format!( + "C2 terminal anchors requested for non-C2 proof type {proof_type:?}" + ))); + } + }; + let recursive_dir = prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir); + let default_dir = prover.circuits_dir(CircuitVariant::Default, artifacts_dir); + let chunk_vk = vk::load_vk_artifacts(&recursive_dir, chunk_circuit)?; + let batch_vk = vk::load_vk_artifacts(&default_dir, CircuitName::C2ChunkBatch)?; + Ok(Self { + chunk_vk_hash: chunk_vk.key_hash, + batch_vk_hash: batch_vk.key_hash, + }) + } +} + +/// Expected terminal public-field count for a C2 proof: `1` child VK hash, +/// `N_PARTIES * L` share commitments, and `1` batch VK hash. +fn expected_public_field_count( + preset: BfvPreset, + committee_size: CiphernodesCommitteeSize, +) -> usize { + let n_parties = committee_size.values().n; + // `L` (number of CRT moduli) comes from the threshold config the C2 + // circuits were compiled against, never from the DKG preset's moduli. + let l = preset + .threshold_counterpart() + .unwrap_or(preset) + .metadata() + .num_moduli; + 1 + n_parties * l + 2 +} + +/// Validates a C2 terminal proof against its deployment-time VK anchors before +/// generic proof verification. +/// +/// The validator is a no-op for non-C2 proof types. Every failure returns a +/// descriptive [`ZkError::InvalidInput`] so the caller can treat the signed +/// proof as invalid. +pub fn validate_c2_terminal_proof( + preset: BfvPreset, + committee_size: CiphernodesCommitteeSize, + proof_type: ProofType, + proof: &Proof, + anchors: &C2TerminalAnchors, +) -> Result<(), ZkError> { + let expected_circuit = match proof_type { + ProofType::C2aSkShareComputation => CircuitName::SkC2ChunkFinalize, + ProofType::C2bESmShareComputation => CircuitName::ESmC2ChunkFinalize, + _ => return Ok(()), + }; + if proof.circuit != expected_circuit { + return Err(ZkError::InvalidInput(format!( + "C2 terminal proof for {proof_type:?} must be the {} circuit, got {}", + expected_circuit.as_str(), + proof.circuit.as_str() + ))); + } + + let signals: &[u8] = proof.public_signals.as_ref(); + if !signals.len().is_multiple_of(32) { + return Err(ZkError::InvalidInput(format!( + "C2 terminal proof public signals are not 32-byte aligned (len {})", + signals.len() + ))); + } + let expected_fields = expected_public_field_count(preset, committee_size); + let actual_fields = signals.len() / 32; + if actual_fields != expected_fields { + return Err(ZkError::InvalidInput(format!( + "C2 terminal proof public signal length {actual_fields} does not match the selected committee and preset (expected {expected_fields})" + ))); + } + + let r = scalar_field_modulus(); + for (index, chunk) in signals.chunks(32).enumerate() { + let value = BigUint::from_bytes_be(chunk); + if value >= r { + return Err(ZkError::InvalidInput(format!( + "C2 terminal proof public field {index} is not a canonical field element" + ))); + } + } + + let chunk_vk_hash = BigUint::from_bytes_be(&signals[0..32]); + let expected_chunk = parse_anchor_hex(&anchors.chunk_vk_hash)?; + if chunk_vk_hash != expected_chunk { + return Err(ZkError::InvalidInput(format!( + "C2 terminal proof child VK hash does not match the canonical {} chunk VK", + expected_circuit.as_str() + ))); + } + + let batch_vk_hash = BigUint::from_bytes_be(&signals[signals.len() - 32..]); + let expected_batch = parse_anchor_hex(&anchors.batch_vk_hash)?; + if batch_vk_hash != expected_batch { + return Err(ZkError::InvalidInput( + "C2 terminal proof batch VK hash does not match the canonical C2ChunkBatch VK".into(), + )); + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::{ + expected_public_field_count, scalar_field_modulus, validate_c2_terminal_proof, + C2TerminalAnchors, + }; + use crate::error::ZkError; + use e3_events::{CircuitName, Proof, ProofType}; + use e3_fhe_params::BfvPreset; + use e3_utils::ArcBytes; + use e3_zk_helpers::CiphernodesCommitteeSize; + use num_bigint::BigUint; + + fn hex_field(value: &BigUint) -> String { + format!("{:0>64x}", value) + } + + fn field_bytes(value: &BigUint) -> [u8; 32] { + let mut bytes = [0u8; 32]; + let be = value.to_bytes_be(); + bytes[32 - be.len()..].copy_from_slice(&be); + bytes + } + + /// Builds a synthetic C2 terminal proof with `fields` public signals. + fn terminal_proof(circuit: CircuitName, fields: &[BigUint]) -> Proof { + let mut public_signals = Vec::with_capacity(fields.len() * 32); + for field in fields { + public_signals.extend_from_slice(&field_bytes(field)); + } + Proof { + circuit, + data: ArcBytes::from_bytes(&[0u8; 64]), + public_signals: ArcBytes::from_bytes(&public_signals), + } + } + + /// Minimal valid signal set for insecure-512 + minimum committee. + /// `1 + 1 + 3*2 + 1 = 9` fields (child VK, secret root, shares, batch VK), + /// all canonical. + fn valid_signals(chunk_hash: &BigUint, batch_hash: &BigUint) -> Vec { + let mut fields = vec![chunk_hash.clone(), BigUint::from(3u8)]; + fields.extend(std::iter::repeat_with(|| BigUint::from(7u8)).take(6)); + fields.push(batch_hash.clone()); + fields + } + + #[test] + fn expected_field_count_matches_compiled_layouts() { + assert_eq!( + expected_public_field_count( + BfvPreset::InsecureDkg512, + CiphernodesCommitteeSize::Minimum + ), + 9 + ); + assert_eq!( + expected_public_field_count( + BfvPreset::SecureDkg8192, + CiphernodesCommitteeSize::Minimum + ), + 12 + ); + assert_eq!( + expected_public_field_count(BfvPreset::InsecureDkg512, CiphernodesCommitteeSize::Micro), + 1 + 9 * 2 + 2 + ); + } + + #[test] + fn accepts_canonical_proof_for_both_c2_types() { + let anchors = C2TerminalAnchors { + chunk_vk_hash: format!("0x{}", hex_field(&BigUint::from(11u8))), + batch_vk_hash: format!("0x{}", hex_field(&BigUint::from(13u8))), + }; + for (proof_type, circuit) in [ + ( + ProofType::C2aSkShareComputation, + CircuitName::SkC2ChunkFinalize, + ), + ( + ProofType::C2bESmShareComputation, + CircuitName::ESmC2ChunkFinalize, + ), + ] { + let signals = valid_signals(&BigUint::from(11u8), &BigUint::from(13u8)); + let proof = terminal_proof(circuit, &signals); + validate_c2_terminal_proof( + BfvPreset::InsecureDkg512, + CiphernodesCommitteeSize::Minimum, + proof_type, + &proof, + &anchors, + ) + .expect("canonical C2 terminal proof must pass"); + } + } + + #[test] + fn non_c2_proof_types_are_ignored() { + let proof = terminal_proof(CircuitName::ShareEncryption, &[]); + validate_c2_terminal_proof( + BfvPreset::InsecureDkg512, + CiphernodesCommitteeSize::Minimum, + ProofType::C3aSkShareEncryption, + &proof, + &C2TerminalAnchors { + chunk_vk_hash: "0x00".into(), + batch_vk_hash: "0x00".into(), + }, + ) + .expect("non-C2 proof types must be ignored"); + } + + #[test] + fn rejects_wrong_terminal_circuit() { + let anchors = C2TerminalAnchors { + chunk_vk_hash: "0x00".into(), + batch_vk_hash: "0x00".into(), + }; + let proof = terminal_proof( + CircuitName::ESmC2ChunkFinalize, + &valid_signals(&BigUint::from(0u8), &BigUint::from(0u8)), + ); + let result = validate_c2_terminal_proof( + BfvPreset::InsecureDkg512, + CiphernodesCommitteeSize::Minimum, + ProofType::C2aSkShareComputation, + &proof, + &anchors, + ); + assert!(matches!(result, Err(ZkError::InvalidInput(_)))); + } + + #[test] + fn rejects_truncated_or_extended_public_signals() { + let anchors = C2TerminalAnchors { + chunk_vk_hash: "0x00".into(), + batch_vk_hash: "0x00".into(), + }; + // Truncated: 8 fields instead of 9. + let truncated = terminal_proof( + CircuitName::SkC2ChunkFinalize, + &valid_signals(&BigUint::from(0u8), &BigUint::from(0u8))[..8], + ); + assert!(validate_c2_terminal_proof( + BfvPreset::InsecureDkg512, + CiphernodesCommitteeSize::Minimum, + ProofType::C2aSkShareComputation, + &truncated, + &anchors, + ) + .is_err()); + // Extended: 10 fields instead of 9. + let mut extended = valid_signals(&BigUint::from(0u8), &BigUint::from(0u8)); + extended.push(BigUint::from(1u8)); + let proof = terminal_proof(CircuitName::SkC2ChunkFinalize, &extended); + assert!(validate_c2_terminal_proof( + BfvPreset::InsecureDkg512, + CiphernodesCommitteeSize::Minimum, + ProofType::C2aSkShareComputation, + &proof, + &anchors, + ) + .is_err()); + } + + #[test] + fn rejects_sk_proof_with_esm_chunk_vk() { + // SK proof whose child VK hash equals the ESM chunk anchor. + let sk_anchor = BigUint::from(11u8); + let esm_anchor = BigUint::from(22u8); + let anchors = C2TerminalAnchors { + chunk_vk_hash: format!("0x{}", hex_field(&sk_anchor)), + batch_vk_hash: "0x00".into(), + }; + let signals = valid_signals(&esm_anchor, &BigUint::from(0u8)); + let proof = terminal_proof(CircuitName::SkC2ChunkFinalize, &signals); + assert!(validate_c2_terminal_proof( + BfvPreset::InsecureDkg512, + CiphernodesCommitteeSize::Minimum, + ProofType::C2aSkShareComputation, + &proof, + &anchors, + ) + .is_err()); + } + + #[test] + fn rejects_noncanonical_chunk_or_batch_vk() { + let anchors = C2TerminalAnchors { + chunk_vk_hash: format!("0x{}", hex_field(&BigUint::from(11u8))), + batch_vk_hash: format!("0x{}", hex_field(&BigUint::from(13u8))), + }; + // Wrong chunk hash. + let signals = valid_signals(&BigUint::from(99u8), &BigUint::from(13u8)); + let proof = terminal_proof(CircuitName::SkC2ChunkFinalize, &signals); + assert!(validate_c2_terminal_proof( + BfvPreset::InsecureDkg512, + CiphernodesCommitteeSize::Minimum, + ProofType::C2aSkShareComputation, + &proof, + &anchors, + ) + .is_err()); + // Wrong batch hash. + let signals = valid_signals(&BigUint::from(11u8), &BigUint::from(99u8)); + let proof = terminal_proof(CircuitName::SkC2ChunkFinalize, &signals); + assert!(validate_c2_terminal_proof( + BfvPreset::InsecureDkg512, + CiphernodesCommitteeSize::Minimum, + ProofType::C2aSkShareComputation, + &proof, + &anchors, + ) + .is_err()); + } + + #[test] + fn rejects_non_canonical_field_encoding() { + let anchors = C2TerminalAnchors { + chunk_vk_hash: format!("0x{}", hex_field(&BigUint::from(11u8))), + batch_vk_hash: format!("0x{}", hex_field(&BigUint::from(13u8))), + }; + let r = scalar_field_modulus(); + // Place a non-canonical field (>= r) in the secret-root slot. + let mut fields = vec![BigUint::from(11u8), r.clone()]; + fields.extend(std::iter::repeat_with(|| BigUint::from(7u8)).take(6)); + fields.push(BigUint::from(13u8)); + let proof = terminal_proof(CircuitName::SkC2ChunkFinalize, &fields); + assert!(validate_c2_terminal_proof( + BfvPreset::InsecureDkg512, + CiphernodesCommitteeSize::Minimum, + ProofType::C2aSkShareComputation, + &proof, + &anchors, + ) + .is_err()); + } + + #[test] + fn canonical_fields_pass_with_anchor_above_scalar_order() { + // A VK hash whose raw 32 bytes exceed the scalar modulus must still be + // accepted when the proof field carries the reduced value. + let r = scalar_field_modulus(); + let raw_hash = &r + BigUint::from(5u8); + let reduced = BigUint::from(5u8); + let anchors = C2TerminalAnchors { + chunk_vk_hash: format!("0x{}", hex_field(&raw_hash)), + batch_vk_hash: format!("0x{}", hex_field(&reduced)), + }; + let signals = valid_signals(&reduced, &reduced); + let proof = terminal_proof(CircuitName::SkC2ChunkFinalize, &signals); + validate_c2_terminal_proof( + BfvPreset::InsecureDkg512, + CiphernodesCommitteeSize::Minimum, + ProofType::C2aSkShareComputation, + &proof, + &anchors, + ) + .expect("reduced anchor hash must be accepted"); + } +} diff --git a/crates/zk-prover/src/circuits/aggregation/c3_accumulator.rs b/crates/zk-prover/src/circuits/aggregation/c3_accumulator.rs index 676d9098d8..146a6b6596 100644 --- a/crates/zk-prover/src/circuits/aggregation/c3_accumulator.rs +++ b/crates/zk-prover/src/circuits/aggregation/c3_accumulator.rs @@ -25,11 +25,11 @@ use serde::Serialize; /// `total_slots` = N_PARTIES * L_THRESHOLD (one slot per party-modulus pair). fn c3_fold_public_input_field_count(total_slots: usize) -> usize { - 4 + 3 * total_slots + 6 + 3 * total_slots } -/// Public-signal layout of `c3_fold`: 4-field prefix, then 3-field-wide per-slot tail. -const C3_FOLD_PREFIX_LEN: usize = 4; +/// Public-signal layout of `c3_fold`: 6-field prefix, then 3-field-wide per-slot tail. +const C3_FOLD_PREFIX_LEN: usize = 6; const C3_FOLD_SLOT_WIDTH: usize = 3; struct C3FoldVks { @@ -64,6 +64,7 @@ impl C3FoldVks { fn generate_c3_fold_kernel_genesis_proof( prover: &ZkProver, inner: &Proof, + slot_index: u32, total_slots: usize, artifacts_dir: &str, job_id: &str, @@ -89,9 +90,15 @@ fn generate_c3_fold_kernel_genesis_proof( acc_proof: acc_pf, acc_public_inputs: acc_pi, inner_key_hash: inner_vk.key_hash, - acc_key_hash: kernel_vk.key_hash, + acc_key_hash: kernel_vk.key_hash.clone(), is_first_step: true, - slot_index: 0, + slot_index, + expected_kernel_key_hash: kernel_vk.key_hash.clone(), + expected_fold_key_hash: vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::C3Fold, + )? + .key_hash, }; let circuit_path = prover @@ -115,8 +122,8 @@ fn generate_c3_fold_kernel_genesis_proof( Ok(proof) } -/// Inner C3 public transcript: two inputs + `ct_commitment` output. -fn share_encryption_inner_public_inputs(proof: &Proof) -> Result<[String; 3], ZkError> { +/// Inner C3 public transcript: two commitments, two slot indices, and `ct_commitment`. +fn share_encryption_inner_public_inputs(proof: &Proof) -> Result<[String; 5], ZkError> { if proof.circuit != CircuitName::ShareEncryption { return Err(ZkError::InvalidInput(format!( "expected ShareEncryption inner proof, got {}", @@ -124,18 +131,21 @@ fn share_encryption_inner_public_inputs(proof: &Proof) -> Result<[String; 3], Zk ))); } let ctx = "C3 inner ShareEncryption proof"; - Ok([ + let fields = [ extract_single_field(proof, "input", field_keys::EXPECTED_PK_COMMITMENT, ctx)?, extract_single_field(proof, "input", field_keys::EXPECTED_MESSAGE_COMMITMENT, ctx)?, + extract_single_field(proof, "input", "party_idx", ctx)?, + extract_single_field(proof, "input", "mod_idx", ctx)?, extract_single_field(proof, "output", field_keys::CT_COMMITMENT, ctx)?, - ]) + ]; + Ok(fields) } #[derive(Serialize)] struct C3FoldStepInput { inner_vk: Vec, inner_proof: Vec, - c3_public_inputs: [String; 3], + c3_public_inputs: [String; 5], acc_vk: Vec, acc_proof: Vec, acc_public_inputs: Vec, @@ -143,6 +153,8 @@ struct C3FoldStepInput { acc_key_hash: String, is_first_step: bool, slot_index: u32, + expected_kernel_key_hash: String, + expected_fold_key_hash: String, } fn parse_c3_fold_public_field_strings(proof: &Proof) -> Result, ZkError> { @@ -175,6 +187,7 @@ fn generate_c3_fold_step_with_vks( let kernel_proof = generate_c3_fold_kernel_genesis_proof( prover, inner, + slot_index, total_slots, artifacts_dir, &kernel_job_id, @@ -197,7 +210,12 @@ fn generate_c3_fold_step_with_vks( } else { let p = prior_fold.expect("prior_fold required when is_first_step is false"); let acc_pi = parse_c3_fold_public_field_strings(p)?; - let prior_slots = (acc_pi.len() - 4) / 3; + if acc_pi.len() < C3_FOLD_PREFIX_LEN { + return Err(ZkError::InvalidInput( + "c3_fold proof public inputs are shorter than the prefix".into(), + )); + } + let prior_slots = (acc_pi.len() - C3_FOLD_PREFIX_LEN) / 3; if prior_slots == 0 { return Err(ZkError::InvalidInput( "c3_fold proof implies zero slots".into(), @@ -236,6 +254,8 @@ fn generate_c3_fold_step_with_vks( acc_key_hash: acc_vk_hash, is_first_step, slot_index, + expected_kernel_key_hash: vks.kernel_vk.key_hash.clone(), + expected_fold_key_hash: vks.fold_vk.key_hash.clone(), }; let circuit_path = prover @@ -317,3 +337,32 @@ pub fn generate_sequential_c3_fold( }, ) } + +#[cfg(test)] +mod tests { + use super::*; + + fn zero_input() -> C3FoldStepInput { + C3FoldStepInput { + inner_vk: Vec::new(), + inner_proof: Vec::new(), + c3_public_inputs: std::array::from_fn(|_| "0".to_string()), + acc_vk: Vec::new(), + acc_proof: Vec::new(), + acc_public_inputs: Vec::new(), + inner_key_hash: "0".to_string(), + acc_key_hash: "0".to_string(), + is_first_step: true, + slot_index: 0, + expected_kernel_key_hash: "0".to_string(), + expected_fold_key_hash: "0".to_string(), + } + } + + #[test] + fn c3_kernel_witness_includes_accumulator_binding_parameters() { + let value = serde_json::to_value(zero_input()).unwrap(); + assert!(value.get("expected_kernel_key_hash").is_some()); + assert!(value.get("expected_fold_key_hash").is_some()); + } +} diff --git a/crates/zk-prover/src/circuits/aggregation/mod.rs b/crates/zk-prover/src/circuits/aggregation/mod.rs index 4fe1ac5f59..0c18931949 100644 --- a/crates/zk-prover/src/circuits/aggregation/mod.rs +++ b/crates/zk-prover/src/circuits/aggregation/mod.rs @@ -6,6 +6,10 @@ //! Recursive proof aggregation (Noir fold / `recursive_aggregation` binaries). +pub mod c2_chunk_batch; +pub(crate) mod c2_chunk_config; +pub mod c2_chunk_layout; +pub mod c2_terminal_validation; pub mod c3_accumulator; pub mod c6_accumulator; pub mod helpers; 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 5e08848f62..004c6a376d 100644 --- a/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs +++ b/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs @@ -67,7 +67,7 @@ fn build_and_prove_recursive_bin( } #[derive(Serialize)] -struct C2abFoldWitness { +struct C2abChunkFoldWitness { c2a_vk: Vec, c2a_proof: Vec, c2a_public: Vec, @@ -164,9 +164,9 @@ fn push_step(timings: &mut Vec, step: &str, started: Instan }); } -/// Run C2abFold || (C3a fold || C3b fold) → C3abFold → C4abFold → NodeFold; returns a [`CircuitName::NodeFold`] proof. +/// Run C2abChunkFold || (C3a fold || C3b fold) → C3abFold → C4abFold → NodeFold; returns a [`CircuitName::NodeFold`] proof. /// -/// C2abFold and the two C3 fold chains are mutually independent and run concurrently via +/// C2abChunkFold and the two C3 fold chains are mutually independent and run concurrently via /// `rayon::join`. C3a and C3b are also independent of each other and run as a nested join. pub fn prove_node_dkg_fold( prover: &ZkProver, @@ -175,16 +175,33 @@ pub fn prove_node_dkg_fold( artifacts_dir: &str, ) -> Result { let mut step_timings = Vec::with_capacity(6); + let c2a_circuit = match input.c2a_proof.circuit { + CircuitName::SkC2ChunkFinalize => input.c2a_proof.circuit, + other => { + return Err(ZkError::InvalidInput(format!( + "invalid C2a proof circuit {other}" + ))) + } + }; + let c2b_circuit = match input.c2b_proof.circuit { + CircuitName::ESmC2ChunkFinalize => input.c2b_proof.circuit, + other => { + return Err(ZkError::InvalidInput(format!( + "invalid C2b proof circuit {other}" + ))) + } + }; let c2a_vk = vk::load_vk_artifacts( &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), - CircuitName::SkShareComputation, + c2a_circuit, )?; let c2b_vk = vk::load_vk_artifacts( &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), - CircuitName::ESmShareComputation, + c2b_circuit, )?; + let c2ab_circuit = CircuitName::C2abChunkFold; - let c2ab = C2abFoldWitness { + let c2ab = C2abChunkFoldWitness { c2a_vk: c2a_vk.verification_key.clone(), c2a_proof: proof_field_strings(input.c2a_proof)?, c2a_public: proof_public_field_strings(input.c2a_proof)?, @@ -195,7 +212,7 @@ pub fn prove_node_dkg_fold( c2b_key_hash: c2b_vk.key_hash.clone(), }; - // c2ab_fold is independent of the c3 chains; c3a and c3b are independent of each other. + // c2ab_chunk_fold is independent of the c3 chains; c3a and c3b are independent of each other. // Run all three concurrently: c2ab || (c3a || c3b). let ((c2ab_result, c2ab_elapsed), ((c3a_result, c3a_elapsed), (c3b_result, c3b_elapsed))) = rayon::join( @@ -203,7 +220,7 @@ pub fn prove_node_dkg_fold( let t = Instant::now(); let r = build_and_prove_recursive_bin( prover, - CircuitName::C2abFold, + c2ab_circuit, &c2ab, &format!("{e3_id}-c2ab"), artifacts_dir, @@ -242,7 +259,7 @@ pub fn prove_node_dkg_fold( let c2ab_proof = c2ab_result?; step_timings.push(FoldProveStepTiming { - step: "c2ab_fold".to_string(), + step: c2ab_circuit.as_str().to_string(), seconds: c2ab_elapsed.as_secs_f64(), }); let c3a_folded = c3a_result?; @@ -316,7 +333,7 @@ pub fn prove_node_dkg_fold( )?; let c2ab_fold_vk = vk::load_vk_artifacts( &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), - CircuitName::C2abFold, + c2ab_circuit, )?; let c3ab_fold_vk = vk::load_vk_artifacts( &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), @@ -405,6 +422,7 @@ fn validate_dkg_aggregation_shape( } let mut seen = HashSet::with_capacity(party_ids.len()); + let mut prev: Option = None; for &party_id in party_ids { let party_index = usize::try_from(party_id).map_err(|_| { ZkError::InvalidInput(format!( @@ -422,6 +440,16 @@ fn validate_dkg_aggregation_shape( "DkgAggregator party id {party_id} is duplicated" ))); } + // The dkg_aggregator circuit requires strictly increasing party ids; reject an + // unsorted input here instead of failing later inside the circuit. + if let Some(prev_id) = prev { + if prev_id >= party_id { + return Err(ZkError::InvalidInput(format!( + "DkgAggregator party ids must be strictly increasing, got {party_id} after {prev_id}" + ))); + } + } + prev = Some(party_id); } Ok(()) } @@ -440,6 +468,7 @@ struct DkgAggregatorWitness { committee_members: Vec, committee_hash_hi: String, committee_hash_lo: String, + vk_binding: Vec, } /// [`CircuitName::DkgAggregator`] over sequential [`CircuitName::NodesFold`] + C5, proved with @@ -482,6 +511,70 @@ pub fn prove_dkg_aggregation( &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), CircuitName::PkAggregation, )?; + let node_fold_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::NodeFold, + )?; + let c0_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), + CircuitName::PkBfv, + )?; + let c1_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), + CircuitName::PkGeneration, + )?; + let c2ab_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::C2abChunkFold, + )?; + let c3ab_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::C3abFold, + )?; + let c4ab_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::C4abFold, + )?; + let c2a_finalize_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), + CircuitName::SkC2ChunkFinalize, + )?; + let c2b_finalize_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), + CircuitName::ESmC2ChunkFinalize, + )?; + let c2_batch_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::C2ChunkBatch, + )?; + let c2a_chunk_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), + CircuitName::SkShareComputationChunk, + )?; + let c2b_chunk_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), + CircuitName::ESmShareComputationChunk, + )?; + let c3_fold_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::C3Fold, + )?; + let share_encryption_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), + CircuitName::ShareEncryption, + )?; + let c4_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), + CircuitName::DkgShareDecryption, + )?; + let c3_fold_kernel_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::C3FoldKernel, + )?; + let nodes_fold_kernel_vk = vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::NodesFoldKernel, + )?; let party_id_fields: Vec = input .party_ids @@ -512,6 +605,24 @@ pub fn prove_dkg_aggregation( committee_members, committee_hash_hi, committee_hash_lo, + vk_binding: vec![ + node_fold_vk.key_hash, + c0_vk.key_hash, + c1_vk.key_hash, + c2ab_vk.key_hash, + c3ab_vk.key_hash, + c4ab_vk.key_hash, + c2a_finalize_vk.key_hash, + c2b_finalize_vk.key_hash, + c2_batch_vk.key_hash, + c2a_chunk_vk.key_hash, + c2b_chunk_vk.key_hash, + c3_fold_vk.key_hash, + share_encryption_vk.key_hash, + c4_vk.key_hash, + c3_fold_kernel_vk.key_hash, + nodes_fold_kernel_vk.key_hash, + ], }; let json = @@ -713,4 +824,12 @@ mod tests { .unwrap_err(); assert!(out_of_range.to_string().contains("outside committee N=3")); } + + #[test] + fn dkg_aggregation_rejects_unsorted_party_ids() { + let unsorted = + validate_dkg_aggregation_shape(2, &[1, 0], 3, CiphernodesCommitteeSize::Minimum) + .unwrap_err(); + assert!(unsorted.to_string().contains("strictly increasing")); + } } diff --git a/crates/zk-prover/src/circuits/aggregation/nodes_fold_accumulator.rs b/crates/zk-prover/src/circuits/aggregation/nodes_fold_accumulator.rs index 4bf9d9bf4c..69b1e0ef05 100644 --- a/crates/zk-prover/src/circuits/aggregation/nodes_fold_accumulator.rs +++ b/crates/zk-prover/src/circuits/aggregation/nodes_fold_accumulator.rs @@ -21,8 +21,8 @@ use crate::witness::{CompiledCircuit, WitnessGenerator}; use e3_events::{CircuitName, CircuitVariant, Proof}; use serde::Serialize; -/// Public-signal layout of `nodes_fold`: 4-field prefix, then `node_fold_fields`-wide tail. -const NODES_FOLD_PREFIX_LEN: usize = 4; +/// Public-signal layout of `nodes_fold`: 6-field prefix, then `node_fold_fields`-wide tail. +const NODES_FOLD_PREFIX_LEN: usize = 6; fn node_fold_statement_field_count(proof: &Proof) -> Result { if proof.circuit != CircuitName::NodeFold { @@ -41,7 +41,7 @@ fn node_fold_statement_field_count(proof: &Proof) -> Result { } fn nodes_fold_acc_public_len(node_fold_fields: usize, total_slots: usize) -> usize { - 4 + total_slots * node_fold_fields + 6 + total_slots * node_fold_fields } #[derive(Serialize)] @@ -56,6 +56,8 @@ struct NodesFoldStepInput { acc_key_hash: String, is_first_step: bool, slot_index: u32, + expected_kernel_key_hash: String, + expected_fold_key_hash: String, } struct NodesFoldVks { @@ -120,9 +122,15 @@ fn generate_nodes_fold_kernel_genesis_proof( acc_proof: acc_pf, acc_public_inputs: acc_pi, inner_key_hash: inner_vk.key_hash, - acc_key_hash: kernel_vk.key_hash, + acc_key_hash: kernel_vk.key_hash.clone(), is_first_step: true, slot_index, + expected_kernel_key_hash: kernel_vk.key_hash, + expected_fold_key_hash: vk::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), + CircuitName::NodesFold, + )? + .key_hash, }; let circuit_path = prover @@ -231,6 +239,8 @@ fn generate_nodes_fold_step_with_vks( acc_key_hash: acc_vk_hash, is_first_step, slot_index, + expected_kernel_key_hash: vks.kernel_vk.key_hash.clone(), + expected_fold_key_hash: vks.fold_vk.key_hash.clone(), }; let circuit_path = prover @@ -309,3 +319,29 @@ pub fn generate_sequential_nodes_fold( }, ) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn nodes_fold_kernel_witness_includes_accumulator_binding_parameters() { + let value = serde_json::to_value(NodesFoldStepInput { + inner_vk: Vec::new(), + inner_proof: Vec::new(), + node_fold_public_inputs: Vec::new(), + acc_vk: Vec::new(), + acc_proof: Vec::new(), + acc_public_inputs: Vec::new(), + inner_key_hash: "0".to_string(), + acc_key_hash: "0".to_string(), + is_first_step: true, + slot_index: 0, + expected_kernel_key_hash: "0".to_string(), + expected_fold_key_hash: "0".to_string(), + }) + .unwrap(); + assert!(value.get("expected_kernel_key_hash").is_some()); + assert!(value.get("expected_fold_key_hash").is_some()); + } +} diff --git a/crates/zk-prover/src/circuits/dkg/share_computation.rs b/crates/zk-prover/src/circuits/dkg/share_computation.rs index 8d9becef6f..76a6906b58 100644 --- a/crates/zk-prover/src/circuits/dkg/share_computation.rs +++ b/crates/zk-prover/src/circuits/dkg/share_computation.rs @@ -4,34 +4,263 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use crate::traits::Provable; +use crate::circuits::aggregation::c2_chunk_batch::{ + finalize_c2_chunk_batches, generate_c2_chunk_batches, +}; +use crate::circuits::aggregation::c2_chunk_layout::C2ChunkLayout; +use crate::circuits::aggregation::node_dkg_fold::FoldProveStepTiming; +use crate::circuits::utils::inputs_json_to_input_map; +use crate::error::ZkError; +use crate::prover::ZkProver; use e3_events::CircuitName; use e3_fhe_params::BfvPreset; -use e3_zk_helpers::computation::DkgInputType; -use e3_zk_helpers::dkg::share_computation::{ - Inputs, ShareComputationCircuit, ShareComputationCircuitData, -}; +use e3_zk_helpers::computation::{Computation, DkgInputType}; +use e3_zk_helpers::dkg::share_computation::{Inputs, ShareComputationCircuitData}; +use rayon::prelude::*; +use serde_json::Value; +use std::time::Instant; -impl Provable for ShareComputationCircuit { - type Params = BfvPreset; - type Input = ShareComputationCircuitData; - type Inputs = Inputs; +pub use crate::circuits::aggregation::c2_chunk_config::DEFAULT_C2_CHUNK_SIZE; - fn resolve_circuit_name(&self, _params: &Self::Params, input: &Self::Input) -> CircuitName { - match input.dkg_input_type { - DkgInputType::SecretKey => CircuitName::SkShareComputation, - DkgInputType::SmudgingNoise => CircuitName::ESmShareComputation, - } +fn validate_c2_chunk_layout(degree: usize, chunk_size: usize) -> Result<(usize, usize), ZkError> { + let layout = C2ChunkLayout::from_degree_chunk_size(degree, chunk_size)?; + let compiled = C2ChunkLayout::compiled(degree)?; + if layout.chunk_count != compiled.chunk_count { + return Err(ZkError::InvalidInput(format!( + "C2 chunk size {chunk_size} produces {} chunks, but the selected artifacts require {}", + layout.chunk_count, compiled.chunk_count + ))); } + if layout.batch_count != compiled.batch_count { + return Err(ZkError::InvalidInput(format!( + "C2 chunk size {chunk_size} produces {} batches, but the selected artifacts require {}", + layout.batch_count, compiled.batch_count + ))); + } + Ok((layout.chunk_count, layout.batch_count)) +} + +pub struct ChunkedShareComputationProofs { + pub proof: e3_events::Proof, + pub chunk_count: usize, + /// Per-step prove wall time inside [`prove_chunked_share_computation`] (for benchmarks / audit reports). + pub step_timings: Vec, +} + +fn push_step(timings: &mut Vec, step: &str, started: Instant) { + timings.push(FoldProveStepTiming { + step: step.to_string(), + seconds: started.elapsed().as_secs_f64(), + }); +} - fn valid_circuits(&self) -> Vec { - vec![ - CircuitName::SkShareComputation, - CircuitName::ESmShareComputation, - ] +fn slice_coefficients( + values: &[Value], + start: usize, + chunk_size: usize, + context: &str, +) -> Result, ZkError> { + let end = start.checked_add(chunk_size).ok_or_else(|| { + ZkError::InvalidInput(format!( + "{context} coefficient range overflows: start={start}, chunk_size={chunk_size}" + )) + })?; + values + .get(start..end) + .map(<[Value]>::to_vec) + .ok_or_else(|| { + ZkError::InvalidInput(format!( + "{context} has {} coefficients, need at least {}", + values.len(), + end + )) + }) +} + +/// Generate the terminal C2 proof from all deterministic coefficient chunks. +pub fn prove_chunked_share_computation( + prover: &ZkProver, + preset: BfvPreset, + data: &ShareComputationCircuitData, + e3_id: &str, + artifacts_dir: &str, +) -> Result { + prove_chunked_share_computation_with_chunk_size( + prover, + preset, + data, + e3_id, + artifacts_dir, + DEFAULT_C2_CHUNK_SIZE, + ) +} + +pub fn prove_chunked_share_computation_with_chunk_size( + prover: &ZkProver, + preset: BfvPreset, + data: &ShareComputationCircuitData, + e3_id: &str, + artifacts_dir: &str, + chunk_size: usize, +) -> Result { + let degree = preset + .threshold_counterpart() + .unwrap_or(preset) + .metadata() + .degree; + let (chunk_count, _batch_count) = validate_c2_chunk_layout(degree, chunk_size)?; + let inputs = Inputs::compute(preset, data) + .map_err(|e| ZkError::InputsGenerationFailed(e.to_string()))?; + let base_json = inputs + .to_json() + .map_err(|e| ZkError::SerializationError(e.to_string()))?; + let chunk_circuit = match data.dkg_input_type { + DkgInputType::SecretKey => CircuitName::SkShareComputationChunk, + DkgInputType::SmudgingNoise => CircuitName::ESmShareComputationChunk, + }; + let y = base_json + .get("y") + .and_then(Value::as_array) + .ok_or_else(|| ZkError::SerializationError("C2 input is missing y".into()))?; + if y.len() != degree { + return Err(ZkError::InvalidInput(format!( + "C2 y has {} coefficients, expected {degree}", + y.len() + ))); } - fn circuit(&self) -> CircuitName { - CircuitName::SkShareComputation + let mut step_timings = Vec::new(); + let t_chunks = Instant::now(); + let chunks: Vec = (0..chunk_count) + .into_par_iter() + .map(|chunk_idx| { + let start = chunk_idx * chunk_size; + let mut chunk_json = serde_json::Map::new(); + chunk_json.insert("chunk_idx".into(), Value::from(chunk_idx as u64)); + let secret_key = match data.dkg_input_type { + DkgInputType::SecretKey => "sk_secret", + DkgInputType::SmudgingNoise => "e_sm_secret", + }; + let secret = base_json.get(secret_key).ok_or_else(|| { + ZkError::SerializationError(format!("C2 input is missing {secret_key}")) + })?; + let secret_chunk = if data.dkg_input_type == DkgInputType::SecretKey { + let coefficients = secret + .as_object() + .and_then(|object| object.get("coefficients")) + .and_then(Value::as_array) + .ok_or_else(|| { + ZkError::SerializationError("SK secret JSON is malformed".into()) + })?; + Value::Object( + [( + "coefficients".into(), + Value::Array(slice_coefficients( + coefficients, + start, + chunk_size, + "SK secret", + )?), + )] + .into_iter() + .collect(), + ) + } else { + let limbs = secret.as_array().ok_or_else(|| { + ZkError::SerializationError("ESM secret JSON must contain CRT limbs".into()) + })?; + Value::Array( + limbs + .iter() + .map(|limb| { + let values = limb + .as_object() + .and_then(|object| object.get("coefficients")) + .and_then(Value::as_array) + .ok_or_else(|| { + ZkError::SerializationError( + "ESM secret JSON must contain CRT limbs".into(), + ) + })?; + Ok::(Value::Object( + [( + "coefficients".into(), + Value::Array(slice_coefficients( + values, + start, + chunk_size, + "ESM secret limb", + )?), + )] + .into_iter() + .collect(), + )) + }) + .collect::, _>>()?, + ) + }; + chunk_json.insert("secret_chunk".into(), secret_chunk); + chunk_json.insert( + "y_chunk".into(), + Value::Array(y[start..start + chunk_size].to_vec()), + ); + let input_map = inputs_json_to_input_map(&Value::Object(chunk_json))?; + let circuit_path = prover + .circuits_dir(e3_events::CircuitVariant::Recursive, artifacts_dir) + .join(chunk_circuit.dir_path()) + .join(format!("{}.json", chunk_circuit.as_str())); + let compiled = crate::witness::CompiledCircuit::from_file(&circuit_path)?; + let witness = crate::witness::WitnessGenerator::new() + .generate_witness(&compiled, input_map) + .map_err(|error| { + ZkError::WitnessGenerationFailed(format!( + "C2 chunk {chunk_idx} witness: {error}" + )) + })?; + let proof = prover.generate_proof_with_variant( + chunk_circuit, + &witness, + &format!("{e3_id}-c2-chunk-{chunk_idx}"), + e3_events::CircuitVariant::Recursive, + artifacts_dir, + )?; + Ok::<_, ZkError>(proof) + }) + .collect::, _>>()?; + push_step(&mut step_timings, "chunks", t_chunks); + + let t_batches = Instant::now(); + let batches = generate_c2_chunk_batches( + prover, + chunk_circuit, + &chunks, + chunk_count, + degree, + e3_id, + artifacts_dir, + )?; + push_step(&mut step_timings, "batches", t_batches); + let finalizer_circuit = match data.dkg_input_type { + DkgInputType::SecretKey => CircuitName::SkC2ChunkFinalize, + DkgInputType::SmudgingNoise => CircuitName::ESmC2ChunkFinalize, + }; + let t_finalize = Instant::now(); + let proof = + finalize_c2_chunk_batches(prover, &batches, finalizer_circuit, e3_id, artifacts_dir)?; + push_step(&mut step_timings, "finalize", t_finalize); + Ok(ChunkedShareComputationProofs { + proof, + chunk_count, + step_timings, + }) +} + +#[cfg(test)] +mod tests { + use super::validate_c2_chunk_layout; + + #[test] + fn rejects_chunk_size_with_a_different_compiled_chunk_count() { + assert!(validate_c2_chunk_layout(8192, 256).is_err()); } } diff --git a/crates/zk-prover/src/commitment_links/c1_to_c2.rs b/crates/zk-prover/src/commitment_links/c1_to_c2.rs index 439969e9f0..1c35526ed7 100644 --- a/crates/zk-prover/src/commitment_links/c1_to_c2.rs +++ b/crates/zk-prover/src/commitment_links/c1_to_c2.rs @@ -14,9 +14,8 @@ //! - field 1: `pk_commitment` (byte offset 32..64) //! - field 2: `e_sm_commitment` (byte offset 64..96) //! -//! **C2a/C2b (ShareComputation inner circuit)** takes `expected_secret_commitment` -//! as its first public input (head, bytes 0..32). The remaining fields are -//! per-party-per-modulus share commitment outputs from `commit_to_party_shares`. +//! **C2a/C2b** expose the recursive child VK hash at field 0 and the +//! expected secret root commitment at field 1 (chunk-finalizer proof). //! //! ## Checks //! @@ -63,10 +62,10 @@ impl CommitmentLink for C1ToC2aSkCommitmentLink { } fn check_signals(&self, source_values: &[FieldValue], target_public_signals: &[u8]) -> bool { - if source_values.is_empty() || target_public_signals.len() < FIELD_BYTE_LEN { + if source_values.is_empty() || target_public_signals.len() < 2 * FIELD_BYTE_LEN { return false; } - target_public_signals[..FIELD_BYTE_LEN] == source_values[0] + target_public_signals[FIELD_BYTE_LEN..2 * FIELD_BYTE_LEN] == source_values[0] } } @@ -101,10 +100,10 @@ impl CommitmentLink for C1ToC2bESmCommitmentLink { } fn check_signals(&self, source_values: &[FieldValue], target_public_signals: &[u8]) -> bool { - if source_values.is_empty() || target_public_signals.len() < FIELD_BYTE_LEN { + if source_values.is_empty() || target_public_signals.len() < 2 * FIELD_BYTE_LEN { return false; } - target_public_signals[..FIELD_BYTE_LEN] == source_values[0] + target_public_signals[FIELD_BYTE_LEN..2 * FIELD_BYTE_LEN] == source_values[0] } } @@ -127,9 +126,10 @@ mod tests { v } - /// C2 inner public signals: [expected_secret_commitment] + share commitments... + /// C2 terminal public signals: [child_vk_hash, expected_secret_commitment] + share commitments... fn c2_signals(secret_commitment: [u8; 32], share_commitments: &[[u8; 32]]) -> Vec { - let mut v = Vec::with_capacity(32 + share_commitments.len() * 32); + let mut v = Vec::with_capacity(64 + share_commitments.len() * 32); + v.extend_from_slice(&make_field(0xFE)); v.extend_from_slice(&secret_commitment); for c in share_commitments { v.extend_from_slice(c); @@ -163,6 +163,16 @@ mod tests { assert!(!link.check_signals(&[make_field(42)], &c2)); } + #[test] + fn c2a_does_not_match_the_child_vk_field() { + let link = C1ToC2aSkCommitmentLink; + let sk = make_field(42); + let mut c2 = Vec::new(); + c2.extend_from_slice(&sk); + c2.extend_from_slice(&make_field(99)); + assert!(!link.check_signals(&[sk], &c2)); + } + #[test] fn c2a_short_or_empty_signals() { let link = C1ToC2aSkCommitmentLink; diff --git a/crates/zk-prover/src/commitment_links/c2_to_c3.rs b/crates/zk-prover/src/commitment_links/c2_to_c3.rs index a0a4390297..6cb921fa63 100644 --- a/crates/zk-prover/src/commitment_links/c2_to_c3.rs +++ b/crates/zk-prover/src/commitment_links/c2_to_c3.rs @@ -10,18 +10,20 @@ //! `commit_to_party_shares`. C3 claims `expected_message_commitment` which must //! match the commitment C2 produced for the share being encrypted. //! -//! The signed C2 proof is the **inner** circuit proof (SkShareComputation / -//! ESmShareComputation, `CircuitVariant::Recursive`). Its public signals layout: -//! - field 0: `expected_secret_commitment` (public input, skip) -//! - fields 1..(N_PARTIES × L_THRESHOLD): share commitments from `commit_to_party_shares` +//! The signed C2 proof is the **inner** terminal proof (SkC2ChunkFinalize / +//! ESmC2ChunkFinalize, `CircuitVariant::Recursive`). Its public signals layout: +//! - field 0: recursive child VK hash (skip) +//! - field 1: secret root commitment (skip) +//! - fields 2..: share commitments from the chunk finalizer //! //! Source is C3 (the claimant — it declares what commitment it encrypts). //! Target is C2 (the provider — it produced the actual share commitments). //! Fault is attributed to C3 when its `expected_message_commitment` does not //! appear anywhere in C2's share commitment section. //! -//! C2a/C3a and C2b/C3b use the same Noir circuits (`ShareComputation` / -//! `ShareEncryption`) but different [`ProofType`] values, so we register two links. +//! C2a/C3a and C2b/C3b use the same Noir circuits (`sk_share_computation_chunk` / +//! `esm_share_computation_chunk`, `ShareEncryption`) but different [`ProofType`] +//! values, so we register two links. use super::{CommitmentLink, FieldValue, LinkScope}; use e3_events::{CircuitName, ProofType}; @@ -105,9 +107,10 @@ fn extract_message_commitment(public_signals: &[u8]) -> Vec { /// Check whether `source_values[0]` (from a C3 proof) appears in the share /// commitment section of a C2 inner proof's public signals. /// -/// C2 inner circuit public signals layout: -/// - field 0: `expected_secret_commitment` (public input, skipped) -/// - fields 1..: `commit_to_party_shares[party_idx][mod_idx]` outputs +/// C2 terminal public signals layout: +/// - field 0: recursive child VK hash (skipped) +/// - field 1: secret root commitment (skipped) +/// - fields 2..: per-party share root commitments /// /// Barretenberg's `noir-recursive` variant sometimes doubles the signal /// buffer (448 = 2×224 bytes for a 7-field circuit). We detect and @@ -118,11 +121,13 @@ fn commitment_in_c2_outputs(source_values: &[FieldValue], target_public_signals: } let expected = &source_values[0]; let signals = deduplicate(target_public_signals); - // Skip first field (expected_secret_commitment public input). - if signals.len() < 2 * FIELD_BYTE_LEN { + // Skip the child VK hash and secret root commitment. + if signals.len() < 4 * FIELD_BYTE_LEN { return false; } - signals[FIELD_BYTE_LEN..] + // The final field is the batch VK hash. It is part of the recursive VK + // manifest, not part of the share commitment section. + signals[2 * FIELD_BYTE_LEN..signals.len() - FIELD_BYTE_LEN] .chunks(FIELD_BYTE_LEN) .any(|chunk| chunk == expected.as_slice()) } @@ -157,13 +162,16 @@ mod tests { v } - /// C2 inner signals: [expected_secret_commitment] + N share commitments. + /// C2 terminal signals: [child VK hash, secret root] + N share commitments. fn c2_signals(commitments: &[[u8; 32]]) -> Vec { - let mut v = vec![0u8; 32 + commitments.len() * 32]; - v[0..32].copy_from_slice(&make_field(0xFF)); // expected_secret_commitment + let mut v = vec![0u8; 96 + commitments.len() * 32]; + v[0..32].copy_from_slice(&make_field(0xFE)); // child VK hash + v[32..64].copy_from_slice(&make_field(0xFF)); // secret root commitment for (i, c) in commitments.iter().enumerate() { - v[32 + i * 32..32 + (i + 1) * 32].copy_from_slice(c); + v[64 + i * 32..64 + (i + 1) * 32].copy_from_slice(c); } + let batch_start = 64 + commitments.len() * 32; + v[batch_start..batch_start + 32].copy_from_slice(&make_field(0xFD)); v } @@ -194,13 +202,21 @@ mod tests { #[test] fn consistency_ignores_first_field_secret_commitment() { - // The first field is expected_secret_commitment and must not be matched. + // The first two fields are not share commitments and must not be matched. let link = C3aToC2aShareEncryptionLink; let msg = make_field(0xFF); // same value as the secret_commitment placeholder let c2 = c2_signals(&[make_field(1)]); // share commitments don't include 0xFF assert!(!link.check_signals(&[msg], &c2)); } + #[test] + fn consistency_ignores_batch_vk_field() { + let link = C3aToC2aShareEncryptionLink; + let batch_vk = make_field(0xFD); + let c2 = c2_signals(&[make_field(1)]); + assert!(!link.check_signals(&[batch_vk], &c2)); + } + #[test] fn consistency_handles_bb_duplicated_buffer() { let link = C3aToC2aShareEncryptionLink; diff --git a/crates/zk-prover/src/commitment_links/c2_to_c4/mod.rs b/crates/zk-prover/src/commitment_links/c2_to_c4/mod.rs index 35e3499779..7773d6f5db 100644 --- a/crates/zk-prover/src/commitment_links/c2_to_c4/mod.rs +++ b/crates/zk-prover/src/commitment_links/c2_to_c4/mod.rs @@ -22,19 +22,21 @@ //! commitments for the C4 recipient do not exactly match the corresponding //! row in C4's `expected_commitments`. //! -//! ## C2 inner-circuit public signals layout +//! ## C2 terminal public signals layout //! //! ```text -//! [expected_secret_commitment (32 B, skip)] +//! [child_vk_hash (32 B, skip)] +//! [secret_root_commitment (32 B, skip)] //! [party_0_mod_0 (32 B)] [party_0_mod_1] ... [party_0_mod_{L-1}] //! [party_1_mod_0] ... //! [party_{N-1}_mod_{L-1}] +//! [batch_vk_hash (32 B, skip)] //! ``` //! -//! The first field is the `expected_secret_commitment` public input and is -//! skipped. The remaining N_PARTIES × L fields are share commitments output -//! by `commit_to_party_shares`, indexed in row-major order (party first, then -//! modulus). +//! The first two fields are the child VK hash and secret root commitment. The +//! final field is the batch VK hash. The remaining N_PARTIES × L fields are +//! share commitments output by the chunk finalizer, indexed in row-major order +//! (party first, then modulus). //! //! ## C4 public signals layout //! @@ -71,6 +73,8 @@ pub struct C2aToC4aShareCommitmentLink { /// Number of threshold CRT moduli (L). Determines the block size in both /// C2 and C4 public signals. pub l: usize, + /// Number of non-commitment fields at the start of the C2 terminal proof. + pub source_prefix_fields: usize, } impl CommitmentLink for C2aToC4aShareCommitmentLink { @@ -91,7 +95,7 @@ impl CommitmentLink for C2aToC4aShareCommitmentLink { } fn extract_source_values(&self, public_signals: &[u8]) -> Vec { - extract_share_commitments(public_signals) + extract_share_commitments(public_signals, self.source_prefix_fields) } fn check_consistency( @@ -115,6 +119,8 @@ impl CommitmentLink for C2aToC4aShareCommitmentLink { pub struct C2bToC4bShareCommitmentLink { /// Number of threshold CRT moduli (L). pub l: usize, + /// Number of non-commitment fields at the start of the C2 terminal proof. + pub source_prefix_fields: usize, } impl CommitmentLink for C2bToC4bShareCommitmentLink { @@ -135,7 +141,7 @@ impl CommitmentLink for C2bToC4bShareCommitmentLink { } fn extract_source_values(&self, public_signals: &[u8]) -> Vec { - extract_share_commitments(public_signals) + extract_share_commitments(public_signals, self.source_prefix_fields) } fn check_consistency( @@ -155,19 +161,17 @@ impl CommitmentLink for C2bToC4bShareCommitmentLink { } } -/// Extract all share commitments from C2's public signals. -/// -/// C2 inner-circuit public signals: -/// - field 0: `expected_secret_commitment` (skipped) -/// - fields 1..: `commit_to_party_shares[party_idx][mod_idx]` outputs, -/// row-major (party first, modulus second) -/// -/// Returns every 32-byte chunk after the first field. -fn extract_share_commitments(public_signals: &[u8]) -> Vec { - if public_signals.len() < 2 * FIELD_BYTE_LEN { +/// Extract all share commitments between the explicit C2 terminal prefix and +/// the trailing batch VK hash. +fn extract_share_commitments( + public_signals: &[u8], + source_prefix_fields: usize, +) -> Vec { + let prefix_bytes = source_prefix_fields.saturating_mul(FIELD_BYTE_LEN); + if source_prefix_fields == 0 || public_signals.len() < prefix_bytes + FIELD_BYTE_LEN { return vec![]; } - public_signals[FIELD_BYTE_LEN..] + public_signals[prefix_bytes..public_signals.len() - FIELD_BYTE_LEN] .chunks(FIELD_BYTE_LEN) .filter_map(|chunk| { if chunk.len() == FIELD_BYTE_LEN { @@ -184,7 +188,8 @@ fn extract_share_commitments(public_signals: &[u8]) -> Vec { /// Precise L-way check: verifies that the L share commitments C2_X computed /// for recipient R exactly match C4_R's expected_commitments row for sender X. /// -/// - `source_values`: all N_PARTIES × L commits from C2_X (from `extract_share_commitments`) +/// - `source_values`: C2 commitments after the explicit prefix and before the +/// trailing batch VK hash /// - `target_public_signals`: C4_R's public signals /// - `src_party_id`: C2 sender X (0-based committee index) /// - `tgt_party_id`: C4 recipient R (0-based committee index) @@ -206,9 +211,17 @@ fn check_exact_l_commitments( let tgt_idx = tgt_party_id as usize; let src_idx = src_party_id as usize; + if source_values.len() % l != 0 { + return false; + } + // Slice L commits from C2 at slot tgt_idx (the C4 recipient's position). - let c2_start = tgt_idx * l; - let c2_end = c2_start + l; + let Some(c2_start) = tgt_idx.checked_mul(l) else { + return false; + }; + let Some(c2_end) = c2_start.checked_add(l) else { + return false; + }; if source_values.len() < c2_end { return false; } @@ -216,9 +229,22 @@ fn check_exact_l_commitments( // C4 row for src_idx (the C2 sender): bytes [X*L*32 .. (X+1)*L*32]. // C4 must also have the aggregated output as the last field. - let c4_row_start = src_idx * l * FIELD_BYTE_LEN; - let c4_row_end = c4_row_start + l * FIELD_BYTE_LEN; - if target_public_signals.len() < c4_row_end + FIELD_BYTE_LEN { + let Some(c4_row_start) = src_idx + .checked_mul(l) + .and_then(|offset| offset.checked_mul(FIELD_BYTE_LEN)) + else { + return false; + }; + let Some(c4_row_len) = l.checked_mul(FIELD_BYTE_LEN) else { + return false; + }; + let Some(c4_row_end) = c4_row_start.checked_add(c4_row_len) else { + return false; + }; + let Some(c4_row_end_plus_field) = c4_row_end.checked_add(FIELD_BYTE_LEN) else { + return false; + }; + if target_public_signals.len() < c4_row_end_plus_field { return false; } diff --git a/crates/zk-prover/src/commitment_links/c2_to_c4/tests.rs b/crates/zk-prover/src/commitment_links/c2_to_c4/tests.rs index 6ce304165a..a7d181d4d2 100644 --- a/crates/zk-prover/src/commitment_links/c2_to_c4/tests.rs +++ b/crates/zk-prover/src/commitment_links/c2_to_c4/tests.rs @@ -12,13 +12,15 @@ fn make_field(val: u8) -> [u8; 32] { f } -/// C2 inner signals: [expected_secret_commitment] + share commitments (row-major: party, mod). +/// C2 terminal signals: [child VK hash, secret root] + share commitments + batch VK hash. fn c2_signals(share_commits: &[[u8; 32]]) -> Vec { let mut v = Vec::new(); - v.extend_from_slice(&make_field(0xFF)); // expected_secret_commitment (skipped) + v.extend_from_slice(&make_field(0xFE)); // child VK hash (skipped) + v.extend_from_slice(&make_field(0xFF)); // secret root (skipped) for c in share_commits { v.extend_from_slice(c); } + v.extend_from_slice(&make_field(0xFD)); // batch VK hash (skipped) v } @@ -36,7 +38,10 @@ fn c4_signals(rows: &[Vec<[u8; 32]>], aggregated: [u8; 32]) -> Vec { #[test] fn extract_share_commitments_from_c2() { - let link = C2aToC4aShareCommitmentLink { l: 2 }; + let link = C2aToC4aShareCommitmentLink { + l: 2, + source_prefix_fields: 2, + }; // C2 with 3 parties × 2 moduli = 6 share commits let commits: Vec<[u8; 32]> = (1u8..=6).map(make_field).collect(); let c2 = c2_signals(&commits); @@ -48,7 +53,10 @@ fn extract_share_commitments_from_c2() { #[test] fn extract_skips_secret_commitment() { - let link = C2aToC4aShareCommitmentLink { l: 2 }; + let link = C2aToC4aShareCommitmentLink { + l: 2, + source_prefix_fields: 2, + }; let c2 = c2_signals(&[make_field(1), make_field(2)]); let values = link.extract_source_values(&c2); assert_eq!(values.len(), 2); @@ -63,7 +71,10 @@ fn extract_skips_secret_commitment() { #[test] fn consistency_passes_precise_l_way_check() { let l = 2; - let link = C2aToC4aShareCommitmentLink { l }; + let link = C2aToC4aShareCommitmentLink { + l, + source_prefix_fields: 2, + }; // C2 from sender X=1: 3 parties × 2 moduli let c2 = c2_signals(&[ @@ -92,7 +103,10 @@ fn consistency_passes_precise_l_way_check() { #[test] fn consistency_fails_when_wrong_modulus_commitment() { let l = 2; - let link = C2aToC4aShareCommitmentLink { l }; + let link = C2aToC4aShareCommitmentLink { + l, + source_prefix_fields: 2, + }; let c2 = c2_signals(&[ make_field(10), @@ -119,7 +133,10 @@ fn consistency_fails_when_wrong_modulus_commitment() { #[test] fn consistency_fails_when_wrong_party_slot() { let l = 2; - let link = C2aToC4aShareCommitmentLink { l }; + let link = C2aToC4aShareCommitmentLink { + l, + source_prefix_fields: 2, + }; let c2 = c2_signals(&[ make_field(10), @@ -139,7 +156,10 @@ fn consistency_fails_when_wrong_party_slot() { #[test] fn consistency_does_not_match_aggregated_output() { let l = 1; - let link = C2aToC4aShareCommitmentLink { l }; + let link = C2aToC4aShareCommitmentLink { + l, + source_prefix_fields: 2, + }; // C2: 1 party × 1 modulus = commit 99 let c2 = c2_signals(&[make_field(99)]); @@ -154,7 +174,10 @@ fn consistency_does_not_match_aggregated_output() { #[test] fn short_or_empty_signals() { - let link = C2aToC4aShareCommitmentLink { l: 2 }; + let link = C2aToC4aShareCommitmentLink { + l: 2, + source_prefix_fields: 2, + }; assert!(link.extract_source_values(&[0u8; 32]).is_empty()); assert!(!link.check_consistency(&[], &[0u8; 256], 0, 0)); assert!(!link.check_consistency(&[make_field(1)], &[0u8; 16], 0, 0)); @@ -163,7 +186,10 @@ fn short_or_empty_signals() { #[test] fn c2b_to_c4b_variant() { let l = 2; - let link = C2bToC4bShareCommitmentLink { l }; + let link = C2bToC4bShareCommitmentLink { + l, + source_prefix_fields: 2, + }; let c2 = c2_signals(&[make_field(7), make_field(8)]); let source_values = link.extract_source_values(&c2); diff --git a/crates/zk-prover/src/commitment_links/mod.rs b/crates/zk-prover/src/commitment_links/mod.rs index 2a8d2c81ed..2e13140696 100644 --- a/crates/zk-prover/src/commitment_links/mod.rs +++ b/crates/zk-prover/src/commitment_links/mod.rs @@ -43,8 +43,14 @@ pub fn default_links(preset: BfvPreset) -> Vec> { Box::new(c1_to_c5::C1ToC5PkCommitmentLink), Box::new(c2_to_c3::C3aToC2aShareEncryptionLink), Box::new(c2_to_c3::C3bToC2bShareEncryptionLink), - Box::new(c2_to_c4::C2aToC4aShareCommitmentLink { l }), - Box::new(c2_to_c4::C2bToC4bShareCommitmentLink { l }), + Box::new(c2_to_c4::C2aToC4aShareCommitmentLink { + l, + source_prefix_fields: 2, + }), + Box::new(c2_to_c4::C2bToC4bShareCommitmentLink { + l, + source_prefix_fields: 2, + }), Box::new(c6_to_c7::C6ToC7DCommitmentLink), Box::new(c4a_to_c6::C4aToC6SkCommitmentLink), Box::new(c4b_to_c6::C4bToC6ESmCommitmentLink), diff --git a/crates/zk-prover/src/lib.rs b/crates/zk-prover/src/lib.rs index 47fa5b765d..7a73448095 100644 --- a/crates/zk-prover/src/lib.rs +++ b/crates/zk-prover/src/lib.rs @@ -27,6 +27,9 @@ pub use actors::{ pub use domain::commitment_links::default_links; pub use backend::{SetupStatus, ZkBackend}; +pub use circuits::aggregation::c2_terminal_validation::{ + validate_c2_terminal_proof, C2TerminalAnchors, +}; pub use circuits::aggregation::c3_accumulator::generate_sequential_c3_fold; pub use circuits::aggregation::c6_accumulator::generate_sequential_c6_fold; pub use circuits::aggregation::node_dkg_fold::{ @@ -37,6 +40,11 @@ pub use circuits::aggregation::node_dkg_fold::{ pub use circuits::aggregation::nodes_fold_accumulator::{ generate_nodes_fold_step, generate_sequential_nodes_fold, }; +pub use circuits::dkg::share_computation::{ + prove_chunked_share_computation, prove_chunked_share_computation_with_chunk_size, + ChunkedShareComputationProofs, DEFAULT_C2_CHUNK_SIZE, +}; +pub use circuits::utils::inputs_json_to_input_map; pub use config::{verify_checksum, BbTarget, CircuitInfo, VersionInfo, ZkConfig}; pub use dkg_attestation_bundle::encode_dkg_attestation_bundle; pub use e3_events::CircuitVariant; diff --git a/crates/zk-prover/src/node_fold_public.rs b/crates/zk-prover/src/node_fold_public.rs index 6f83f87238..817d34ac0f 100644 --- a/crates/zk-prover/src/node_fold_public.rs +++ b/crates/zk-prover/src/node_fold_public.rs @@ -5,6 +5,7 @@ // or FITNESS FOR A PARTICULAR PURPOSE. //! Public IO layout for [`CircuitName::NodeFold`] (must stay aligned with `node_fold/src/main.nr`). +//! The final field is the recursive VK manifest after the SK/ESM aggregate commitments. use crate::circuits::utils::bytes_to_field_strings; use crate::error::ZkError; @@ -12,7 +13,7 @@ use e3_events::{CircuitName, DkgFoldAggCommits, Proof}; /// Total public field count for `node_fold` at committee size `n`, honest `h`, threshold moduli `l`. pub fn node_fold_public_field_count(n: usize, h: usize, l: usize) -> usize { - 11 + n + 2 * (n + h) * l + 14 + n + 2 * (n + h) * l } fn field_hex_to_bytes32(field: &str) -> Result<[u8; 32], ZkError> { @@ -59,8 +60,8 @@ pub fn extract_node_fold_agg_commits( ))); } let party_id = field_hex_to_u64(&fields[0])?; - let sk_agg_commit = field_hex_to_bytes32(&fields[fields.len() - 2])?; - let esm_agg_commit = field_hex_to_bytes32(&fields[fields.len() - 1])?; + let sk_agg_commit = field_hex_to_bytes32(&fields[fields.len() - 3])?; + let esm_agg_commit = field_hex_to_bytes32(&fields[fields.len() - 2])?; Ok(( party_id, DkgFoldAggCommits { @@ -86,8 +87,8 @@ mod tests { let mut fields = vec![[0u8; 32]; field_count]; fields[0][31] = 2; // party_id = 2 - fields[field_count - 2] = [0x11; 32]; - fields[field_count - 1] = [0x22; 32]; + fields[field_count - 3] = [0x11; 32]; + fields[field_count - 2] = [0x22; 32]; let mut public_signals = Vec::with_capacity(field_count * 32); for f in fields { diff --git a/crates/zk-prover/src/node_proof_aggregation/effects.rs b/crates/zk-prover/src/node_proof_aggregation/effects.rs index f0db925023..7a94a5216a 100644 --- a/crates/zk-prover/src/node_proof_aggregation/effects.rs +++ b/crates/zk-prover/src/node_proof_aggregation/effects.rs @@ -158,7 +158,39 @@ impl NodeProofAggregator { return; } - let req = state.build_fold_request(); + let req = match state.build_fold_request() { + Ok(req) => req, + Err(err) => { + let ec = state.last_ec.clone(); + let party_id = state.meta.party_id; + error!( + "NodeProofAggregator: invalid C3 slot metadata for E3 {} party {}: {}", + e3_id, party_id, err + ); + // Publish the terminal event before dropping the aggregation state; + // only drop the state once the failure was actually published, so a + // transient bus failure does not lose the terminal event. + match self.bus.publish( + E3Failed { + e3_id: e3_id.clone(), + failed_at_stage: E3Stage::CommitteeFinalized, + reason: FailureReason::DKGInvalidShares, + }, + ec, + ) { + Ok(_) => { + self.states.remove(e3_id); + } + Err(err) => { + error!( + "NodeProofAggregator: failed to publish E3Failed for E3 {} — retaining state for retry: {err}", + e3_id + ); + } + } + return; + } + }; let corr = CorrelationId::new(); let ec = state.last_ec.clone(); let party_id = state.meta.party_id; diff --git a/crates/zk-prover/src/node_proof_aggregation/workflow.rs b/crates/zk-prover/src/node_proof_aggregation/workflow.rs index 66fc6418cc..60dba21baf 100644 --- a/crates/zk-prover/src/node_proof_aggregation/workflow.rs +++ b/crates/zk-prover/src/node_proof_aggregation/workflow.rs @@ -74,19 +74,33 @@ impl DkgProofCollectionState { /// /// Callers must ensure [`is_ready`](Self::is_ready) is `true` first; this /// method panics if any expected `seq` is missing. - pub(crate) fn build_fold_request(&self) -> NodeDkgFoldRequest { + pub(crate) fn build_fold_request(&self) -> Result { let meta = &self.meta; let c3_total_slots = meta.committee_n * meta.n_moduli; let slots_a: Vec = meta .sk_share_encryption_requests .iter() - .map(|r| r.c3_slot_index(meta.n_moduli)) - .collect(); + .map(|r| { + r.c3_slot_index(meta.n_moduli).ok_or_else(|| { + format!( + "C3a slot does not fit u32: recipient={}, row={}, moduli={}", + r.recipient_party_id, r.row_index, meta.n_moduli + ) + }) + }) + .collect::>()?; let slots_b: Vec = meta .e_sm_share_encryption_requests .iter() - .map(|r| r.c3_slot_index(meta.n_moduli)) - .collect(); + .map(|r| { + r.c3_slot_index(meta.n_moduli).ok_or_else(|| { + format!( + "C3b slot does not fit u32: recipient={}, row={}, moduli={}", + r.recipient_party_id, r.row_index, meta.n_moduli + ) + }) + }) + .collect::>()?; let sk = meta.sk_enc_count; let esm = meta.e_sm_enc_count; @@ -113,7 +127,7 @@ impl DkgProofCollectionState { let c4a_proof = get(c4a_seq); let c4b_proof = get(c4a_seq + 1); - NodeDkgFoldRequest { + Ok(NodeDkgFoldRequest { c0_proof, c1_proof, c2a_proof, @@ -128,7 +142,7 @@ impl DkgProofCollectionState { party_id: meta.party_id, params_preset: meta.params_preset, committee_size: meta.committee_size, - } + }) } } @@ -195,7 +209,7 @@ mod tests { state.buffer.insert(seq, dummy_proof(seq as u8)); } assert!(state.is_ready()); - let req = state.build_fold_request(); + let req = state.build_fold_request().unwrap(); assert_eq!(req.c0_proof, dummy_proof(0)); assert_eq!(req.c1_proof, dummy_proof(1)); assert_eq!(req.c2a_proof, dummy_proof(2)); diff --git a/crates/zk-prover/src/proof_request/workflow_tests.rs b/crates/zk-prover/src/proof_request/workflow_tests.rs index 0185994cce..c0ee56c69e 100644 --- a/crates/zk-prover/src/proof_request/workflow_tests.rs +++ b/crates/zk-prover/src/proof_request/workflow_tests.rs @@ -78,6 +78,7 @@ fn dkg_share_decryption_req() -> DkgShareDecryptionProofRequest { num_honest_parties: 0, num_moduli: 0, own_plaintext_idx: 0, + recipient_party_id: 0, own_share_raw: sensitive(), dkg_input_type: DkgInputType::SecretKey, params_preset: BfvPreset::default(), diff --git a/crates/zk-prover/tests/common/helpers.rs b/crates/zk-prover/tests/common/helpers.rs index 572746fbc3..e89572c024 100644 --- a/crates/zk-prover/tests/common/helpers.rs +++ b/crates/zk-prover/tests/common/helpers.rs @@ -6,6 +6,7 @@ use e3_config::BBPath; use e3_events::CircuitName; +use e3_fhe_params::BfvPreset; #[allow(unused_imports)] pub use e3_test_helpers::{find_anvil, find_bb}; use e3_zk_prover::{ZkBackend, ZkConfig}; @@ -23,7 +24,14 @@ fn circuits_build_root() -> PathBuf { } pub async fn setup_compiled_circuit(backend: &ZkBackend, group: &str, circuit_name: &str) { - setup_compiled_circuit_for_committee(backend, group, circuit_name, "minimum").await; + setup_compiled_circuit_for_preset( + backend, + group, + circuit_name, + BfvPreset::InsecureThreshold512, + "minimum", + ) + .await; } pub async fn setup_compiled_circuit_for_committee( @@ -32,6 +40,24 @@ pub async fn setup_compiled_circuit_for_committee( circuit_name: &str, committee: &str, ) { + setup_compiled_circuit_for_preset( + backend, + group, + circuit_name, + BfvPreset::InsecureThreshold512, + committee, + ) + .await; +} + +pub async fn setup_compiled_circuit_for_preset( + backend: &ZkBackend, + group: &str, + circuit_name: &str, + preset: BfvPreset, + committee: &str, +) { + assert_active_preset_matches(preset, committee); let target_dir = circuits_build_root().join(group).join("target"); let json_path = target_dir.join(format!("{circuit_name}.json")); let vk_evm_path = target_dir.join(format!("{circuit_name}.vk")); @@ -52,8 +78,9 @@ pub async fn setup_compiled_circuit_for_committee( vk_evm_path.display() ); - // Tests use insecure params — fixtures go under insecure-512/{committee}/ - let preset_dir = backend.circuits_dir.join("insecure-512").join(committee); + let preset_dir = backend + .circuits_dir + .join(preset.artifacts_dir_for_committee(committee)); // Set up the evm variant directory (keccak VK + hash) let evm_dir = preset_dir.join("evm").join(group).join(circuit_name); @@ -139,6 +166,22 @@ pub async fn setup_compiled_circuit_for_committee( /// [`CircuitName::DkgAggregator`] also gets `{package}.vk` / `.vk_hash` (`bb write_vk -t evm`); when present, they are /// copied into `insecure-512/evm/...` for [`CircuitVariant::Evm`] proving. pub async fn setup_recursive_aggregation_fold_circuit(backend: &ZkBackend, circuit: CircuitName) { + setup_recursive_aggregation_fold_circuit_for_preset( + backend, + circuit, + BfvPreset::InsecureThreshold512, + "minimum", + ) + .await; +} + +pub async fn setup_recursive_aggregation_fold_circuit_for_preset( + backend: &ZkBackend, + circuit: CircuitName, + preset: BfvPreset, + committee: &str, +) { + assert_active_preset_matches(preset, committee); let pkg = circuit.as_str(); let target_dir = circuits_build_root() .join("recursive_aggregation") @@ -150,6 +193,8 @@ pub async fn setup_recursive_aggregation_fold_circuit(backend: &ZkBackend, circu let vk_recursive_hash_path = target_dir.join(format!("{pkg}.vk_recursive_hash")); let vk_evm_path = target_dir.join(format!("{pkg}.vk")); let vk_evm_hash_path = target_dir.join(format!("{pkg}.vk_hash")); + let vk_noir_path = target_dir.join(format!("{pkg}.vk_noir")); + let vk_noir_hash_path = target_dir.join(format!("{pkg}.vk_noir_hash")); assert!( json_path.exists(), @@ -162,7 +207,9 @@ pub async fn setup_recursive_aggregation_fold_circuit(backend: &ZkBackend, circu vk_recursive_path.display() ); - let preset_dir = backend.circuits_dir.join("insecure-512").join("minimum"); + let preset_dir = backend + .circuits_dir + .join(preset.artifacts_dir_for_committee(committee)); let default_dir = preset_dir.join("default").join(circuit.group()).join(pkg); fs::create_dir_all(&default_dir).await.unwrap(); fs::copy(&json_path, default_dir.join(format!("{pkg}.json"))) @@ -195,6 +242,25 @@ pub async fn setup_recursive_aggregation_fold_circuit(backend: &ZkBackend, circu .unwrap(); } } + + if vk_noir_path.exists() { + let recursive_dir = preset_dir.join("recursive").join(circuit.group()).join(pkg); + fs::create_dir_all(&recursive_dir).await.unwrap(); + fs::copy(&json_path, recursive_dir.join(format!("{pkg}.json"))) + .await + .unwrap(); + fs::copy(&vk_noir_path, recursive_dir.join(format!("{pkg}.vk"))) + .await + .unwrap(); + if vk_noir_hash_path.exists() { + fs::copy( + &vk_noir_hash_path, + recursive_dir.join(format!("{pkg}.vk_hash")), + ) + .await + .unwrap(); + } + } } /// Creates a temp ZkBackend with the real bb binary symlinked in. @@ -233,6 +299,56 @@ pub fn active_bin_committee() -> Option { v.get("committee")?.as_str().map(|s| s.to_lowercase()) } +pub fn active_bin_preset() -> Option { + let path = circuits_build_root().join(".active-preset.json"); + let raw = std::fs::read_to_string(path).ok()?; + let v: serde_json::Value = serde_json::from_str(&raw).ok()?; + v.get("preset")?.as_str().map(str::to_owned) +} + +fn assert_active_preset_matches(preset: BfvPreset, committee: &str) { + let active = active_bin_preset().expect("active circuit preset stamp is required"); + let active_committee = + active_bin_committee().expect("active circuit committee stamp is required"); + let expected = expected_preset_name(preset, committee); + assert_eq!( + active, expected, + "compiled artifacts use preset {active}, but the test requested {expected}" + ); + assert_eq!( + active_committee, committee, + "compiled artifacts use committee {active_committee}, but the test requested {committee}" + ); +} + +pub fn compiled_circuit_artifacts_available(group: &str, circuit_name: &str) -> bool { + let target_dir = circuits_build_root().join(group).join("target"); + target_dir.join(format!("{circuit_name}.json")).is_file() + && target_dir.join(format!("{circuit_name}.vk")).is_file() +} + +pub fn recursive_circuit_artifacts_available(circuit: CircuitName) -> bool { + let target_dir = circuits_build_root() + .join(circuit.group()) + .join(circuit.as_str()) + .join("target"); + target_dir + .join(format!("{}.json", circuit.as_str())) + .is_file() + && target_dir + .join(format!("{}.vk_recursive", circuit.as_str())) + .is_file() +} + +fn expected_preset_name(preset: BfvPreset, committee: &str) -> String { + preset + .artifacts_dir_for_committee(committee) + .split('/') + .next() + .unwrap() + .to_owned() +} + /// Returns `true` when the compiled `pk_aggregation` (C5) circuit was built for the **minimum** /// committee (H=2): `expected_threshold_pk_commitments` array length == 2. /// @@ -273,6 +389,29 @@ pub fn circuits_compiled_for_minimum() -> bool { .is_some_and(|len| len == 2) // minimum H == 2 } +pub fn require_minimum_circuits_for_preset(preset: BfvPreset) -> Option<()> { + let Some(active) = active_bin_preset() else { + println!("skipping: active circuit preset stamp is missing"); + return None; + }; + let Some(committee) = active_bin_committee() else { + println!("skipping: active circuit committee stamp is missing"); + return None; + }; + if committee != "minimum" { + println!( + "skipping: circuits not compiled for minimum committee. Rebuild with `pnpm build:circuits --committee minimum` to run this test." + ); + return None; + } + let expected = expected_preset_name(preset, "minimum"); + if active != expected { + println!("skipping: active preset is {active}, expected {expected}"); + return None; + } + Some(()) +} + /// Lightweight backend for tests that need to override config (e.g. inject bad checksums). pub fn test_backend(temp_path: &std::path::Path, config: ZkConfig) -> ZkBackend { let noir_dir = temp_path.join("noir"); diff --git a/crates/zk-prover/tests/common/mod.rs b/crates/zk-prover/tests/common/mod.rs index 5468da2f90..6825dcc49a 100644 --- a/crates/zk-prover/tests/common/mod.rs +++ b/crates/zk-prover/tests/common/mod.rs @@ -9,20 +9,13 @@ mod helpers; #[allow(dead_code)] pub use helpers::*; +use e3_fhe_params::BfvPreset; + /// Call at the top of any setup function that hard-codes `CiphernodesCommitteeSize::Minimum`. -/// Returns `None` (causing the test to skip) when the compiled circuits were built for a -/// non-minimum committee — the Minimum-sized samples would not satisfy the circuit ABI. +/// Returns `None` when the compiled circuits use a different preset or committee. #[allow(dead_code)] pub fn require_minimum_circuits() -> Option<()> { - if circuits_compiled_for_minimum() { - Some(()) - } else { - println!( - "skipping: circuits not compiled for minimum committee. \ - Rebuild with `pnpm build:circuits --committee minimum` to run this test." - ); - None - } + require_minimum_circuits_for_preset(BfvPreset::InsecureThreshold512) } use std::path::PathBuf; diff --git a/crates/zk-prover/tests/common/node_fold_witness.rs b/crates/zk-prover/tests/common/node_fold_witness.rs index 77aff34319..299e9ece59 100644 --- a/crates/zk-prover/tests/common/node_fold_witness.rs +++ b/crates/zk-prover/tests/common/node_fold_witness.rs @@ -20,6 +20,7 @@ use e3_zk_helpers::dkg::share_encryption::ShareEncryptionCircuitData; use e3_zk_helpers::threshold::pk_generation::PkGenerationCircuitData; use e3_zk_helpers::CiphernodesCommittee; use e3_zk_helpers::CircuitsErrors; +use e3_zk_prover::DEFAULT_C2_CHUNK_SIZE; use fhe::bfv::Encoding; use fhe::bfv::SecretKey; use fhe::mbfv::PublicKeyShare; @@ -141,6 +142,7 @@ pub fn share_computation_sk_from_pk( parity_matrix, n_parties: committee.n as u32, threshold: committee.threshold as u32, + chunk_size: DEFAULT_C2_CHUNK_SIZE as u32, }) } @@ -184,6 +186,7 @@ pub fn share_computation_esm_from_esi( parity_matrix, n_parties: committee.n as u32, threshold: committee.threshold as u32, + chunk_size: DEFAULT_C2_CHUNK_SIZE as u32, }) } @@ -195,6 +198,7 @@ pub fn share_encryption_for_slot( share_inputs: &ShareComputationInputs, slot: usize, dkg_input_type: DkgInputType, + committee: CiphernodesCommittee, ) -> Result { let (_, dkg_params) = build_pair_for_preset(preset).map_err(|e| CircuitsErrors::Sample(e.to_string()))?; @@ -237,5 +241,9 @@ pub fn share_encryption_for_slot( e0_rns, e1_rns, dkg_input_type, + party_idx: party as u32, + mod_idx: mod_ix as u32, + chunk_size: DEFAULT_C2_CHUNK_SIZE as u32, + committee, }) } diff --git a/crates/zk-prover/tests/fold_accumulators_e2e_tests.rs b/crates/zk-prover/tests/fold_accumulators_e2e_tests.rs index 1ad6670079..17c1f4489c 100644 --- a/crates/zk-prover/tests/fold_accumulators_e2e_tests.rs +++ b/crates/zk-prover/tests/fold_accumulators_e2e_tests.rs @@ -9,7 +9,7 @@ //! compiled `c3_fold` / `c6_fold` JSON, and artifact staging under [`CircuitVariant::Default`] //! (`noir-recursive-no-zk` VKs — see `scripts/build-circuits.ts`). //! -//! Loads compiled JSON for the node-fold **pipeline** ([`CircuitName::C2abFold`] … [`CircuitName::NodeFold`]) +//! Loads compiled JSON for the node-fold **pipeline** ([`CircuitName::C2abChunkFold`] … [`CircuitName::NodeFold`]) //! and stages those artifacts; it does **not** run a full correlated `node_fold` proof — use //! `node_fold_correlated_e2e_tests.rs` for that. //! @@ -58,15 +58,15 @@ fn recursive_aggregation_compiled_json_path(circuit: CircuitName) -> PathBuf { .join(format!("{}.json", circuit.as_str())) } -/// `c2ab_fold` → `c3ab_fold` → `c4ab_fold` → inputs to `node_fold` (see `node_fold/src/main.nr`). +/// `c2ab_chunk_fold` → `c3ab_fold` → `c4ab_fold` → inputs to `node_fold` (see `node_fold/src/main.nr`). const NODE_FOLD_PIPELINE: &[CircuitName] = &[ - CircuitName::C2abFold, + CircuitName::C2abChunkFold, CircuitName::C3abFold, CircuitName::C4abFold, CircuitName::NodeFold, ]; -/// Reads `C3_SLOTS` from the compiled `c3_fold` ABI (`acc_public_inputs` length is `4 + 3 * C3_SLOTS`). +/// Reads `C3_SLOTS` from the compiled `c3_fold` ABI (`acc_public_inputs` length is `6 + 3 * C3_SLOTS`). fn c3_fold_total_slots_from_compiled_json() -> usize { let path = c3_fold_json_path(); let raw = std::fs::read_to_string(&path).unwrap_or_else(|e| { @@ -89,11 +89,11 @@ fn c3_fold_total_slots_from_compiled_json() -> usize { }) .expect("c3_fold.json: abi.parameters.acc_public_inputs.length") as usize; assert!( - len >= 4 && (len - 4).is_multiple_of(3), - "unexpected acc_public_inputs length {} (expected 4 + 3 * slots)", + len >= 6 && (len - 6).is_multiple_of(3), + "unexpected acc_public_inputs length {} (expected 6 + 3 * slots)", len ); - (len - 4) / 3 + (len - 6) / 3 } /// Reads slot count from the compiled `c6_fold` ABI diff --git a/crates/zk-prover/tests/fold_vk_binding_adversarial_tests.rs b/crates/zk-prover/tests/fold_vk_binding_adversarial_tests.rs new file mode 100644 index 0000000000..519833aa30 --- /dev/null +++ b/crates/zk-prover/tests/fold_vk_binding_adversarial_tests.rs @@ -0,0 +1,809 @@ +// 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. + +//! Adversarial tests for the recursive VK binding in `c3_fold` / `nodes_fold` (plan section 1: +//! "Bind Every Fold Step"). +//! +//! Every fold step must assert that the previous accumulator's child VK hash (public input 0) +//! equals the current step's `inner_key_hash`, so one canonical child VK applies to the complete +//! chain. A "compatible dummy circuit" — a real circuit with the exact public ABI of the canonical +//! inner circuit (`share_encryption` / `node_fold`) but a different VK — must be rejected at every +//! step. +//! +//! Because `recursive_aggregation` is a no-op during witness generation (see +//! `acvm-repo/acvm/src/pwg/blackbox/mod.rs`), the tests construct fold-step inputs directly and run +//! the fold circuit's [`WitnessGenerator`]. The positive controls establish that a fully canonical +//! step succeeds; each adversarial case flips exactly one binding and must fail. +//! +//! Artifact reads are cheap and deterministic; the `*_with_real_dummy_proof` tests additionally +//! mint a real `bb prove` proof of the dummy circuit to show a valid, ABI-compatible, noncanonical +//! proof is still rejected. + +mod common; + +use std::path::{Path, PathBuf}; +use std::process::Command as StdCommand; + +use common::{find_bb, setup_test_prover}; +use e3_zk_prover::{inputs_json_to_input_map, CompiledCircuit, WitnessGenerator, ZkBackend}; +use serde::Serialize; + +// --------------------------------------------------------------------------------------------- +// Artifact paths (compiled output tree under `circuits/bin/`) +// --------------------------------------------------------------------------------------------- + +fn ra_pkg_dir(pkg: &str) -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../circuits/bin/recursive_aggregation") + .join(pkg) +} + +fn ra_artifact(pkg: &str, ext: &str) -> PathBuf { + ra_pkg_dir(pkg).join("target").join(format!("{pkg}.{ext}")) +} + +fn dkg_artifact(pkg: &str, ext: &str) -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../circuits/bin/dkg/target") + .join(format!("{pkg}.{ext}")) +} + +// --------------------------------------------------------------------------------------------- +// Field / hash helpers +// --------------------------------------------------------------------------------------------- + +fn zero_fields(n: usize) -> Vec { + vec!["0x00".to_string(); n] +} + +fn read_vk_hash(path: &Path) -> Option { + let bytes = std::fs::read(path).ok()?; + (bytes.len() == 32).then(|| format!("0x{}", hex::encode(bytes))) +} + +fn read_vk_fields(path: &Path) -> Option> { + let bytes = std::fs::read(path).ok()?; + if !bytes.len().is_multiple_of(32) { + return None; + } + Some( + bytes + .chunks(32) + .map(|c| format!("0x{}", hex::encode(c))) + .collect(), + ) +} + +/// Reads a fixed-width array length from the compiled circuit ABI (e.g. `acc_public_inputs`). +fn abi_array_len(json_path: &Path, param: &str) -> Option { + let raw = std::fs::read_to_string(json_path).ok()?; + let v: serde_json::Value = serde_json::from_str(&raw).ok()?; + v["abi"]["parameters"] + .as_array()? + .iter() + .find(|p| p.get("name") == Some(&serde_json::Value::String(param.into())))? + .get("type")? + .get("length")? + .as_u64() + .map(|x| x as usize) +} + +// --------------------------------------------------------------------------------------------- +// Fold-step input builders +// --------------------------------------------------------------------------------------------- + +#[derive(Serialize)] +struct C3FoldStepInput { + inner_vk: Vec, + inner_proof: Vec, + c3_public_inputs: Vec, + acc_vk: Vec, + acc_proof: Vec, + acc_public_inputs: Vec, + inner_key_hash: String, + acc_key_hash: String, + is_first_step: bool, + slot_index: u32, + expected_kernel_key_hash: String, + expected_fold_key_hash: String, +} + +/// A fully canonical `c3_fold` step for `slot_index = 0` (party 0, modulus limb 0), so the +/// `c3_public_inputs[2] == slot/L` / `[3] == slot%L` assertions hold for any `L`. +/// +/// `acc_child_vk` / `acc_vk_hash` model the accumulator's child VK (position 0) and the VK that +/// actually produced the accumulator. `acc_is_first` is the accumulator's own `is_first_step` flag +/// (position 2). The per-slot tail is zeroed, which satisfies the empty-slot assertions. +#[allow(clippy::too_many_arguments)] +fn c3_fold_step_input( + inner_vk: Vec, + inner_proof: Vec, + inner_key_hash: &str, + acc_vk: Vec, + acc_child_vk: &str, + acc_vk_hash: &str, + acc_is_first: u32, + is_first_step: bool, + expected_kernel_key_hash: &str, + expected_fold_key_hash: &str, + total_slots: usize, +) -> C3FoldStepInput { + let mut acc_public_inputs = zero_fields(6 + 3 * total_slots); + acc_public_inputs[0] = acc_child_vk.to_string(); + acc_public_inputs[1] = acc_vk_hash.to_string(); + acc_public_inputs[2] = acc_is_first.to_string(); + acc_public_inputs[4] = expected_kernel_key_hash.to_string(); + acc_public_inputs[5] = expected_fold_key_hash.to_string(); + + C3FoldStepInput { + inner_vk, + inner_proof, + c3_public_inputs: vec!["0x00".to_string(); 5], + acc_vk, + acc_proof: zero_fields(410), + acc_public_inputs, + inner_key_hash: inner_key_hash.to_string(), + acc_key_hash: acc_vk_hash.to_string(), + is_first_step, + slot_index: 0, + expected_kernel_key_hash: expected_kernel_key_hash.to_string(), + expected_fold_key_hash: expected_fold_key_hash.to_string(), + } +} + +#[derive(Serialize)] +struct NodesFoldStepInput { + inner_vk: Vec, + inner_proof: Vec, + node_fold_public_inputs: Vec, + acc_vk: Vec, + acc_proof: Vec, + acc_public_inputs: Vec, + inner_key_hash: String, + acc_key_hash: String, + is_first_step: bool, + slot_index: u32, + expected_kernel_key_hash: String, + expected_fold_key_hash: String, +} + +#[allow(clippy::too_many_arguments)] +fn nodes_fold_step_input( + inner_vk: Vec, + inner_proof: Vec, + inner_key_hash: &str, + acc_vk: Vec, + acc_child_vk: &str, + acc_vk_hash: &str, + acc_is_first: u32, + is_first_step: bool, + expected_kernel_key_hash: &str, + expected_fold_key_hash: &str, + node_fold_len: usize, + total_slots: usize, +) -> NodesFoldStepInput { + let mut acc_public_inputs = zero_fields(6 + total_slots * node_fold_len); + acc_public_inputs[0] = acc_child_vk.to_string(); + acc_public_inputs[1] = acc_vk_hash.to_string(); + acc_public_inputs[2] = acc_is_first.to_string(); + acc_public_inputs[4] = expected_kernel_key_hash.to_string(); + acc_public_inputs[5] = expected_fold_key_hash.to_string(); + + NodesFoldStepInput { + inner_vk, + inner_proof, + node_fold_public_inputs: zero_fields(node_fold_len), + acc_vk, + acc_proof: zero_fields(410), + acc_public_inputs, + inner_key_hash: inner_key_hash.to_string(), + acc_key_hash: acc_vk_hash.to_string(), + is_first_step, + slot_index: 0, + expected_kernel_key_hash: expected_kernel_key_hash.to_string(), + expected_fold_key_hash: expected_fold_key_hash.to_string(), + } +} + +// --------------------------------------------------------------------------------------------- +// Witness generation harness +// --------------------------------------------------------------------------------------------- + +fn run_witness_gen(json_path: &Path, input: &impl Serialize) -> Result<(), String> { + let compiled = + CompiledCircuit::from_file(json_path).map_err(|e| format!("load circuit: {e}"))?; + let json = serde_json::to_value(input).map_err(|e| format!("serialize input: {e}"))?; + let input_map = inputs_json_to_input_map(&json).map_err(|e| format!("input map: {e}"))?; + WitnessGenerator::new() + .generate_witness(&compiled, input_map) + .map(|_| ()) + .map_err(|e| format!("witness gen: {e}")) +} + +fn expect_success(result: Result<(), String>, label: &str) { + assert!( + result.is_ok(), + "{label}: expected witness generation to succeed, got Err({})", + result.err().unwrap_or_default() + ); +} + +fn expect_failure(result: Result<(), String>, label: &str) { + assert!( + result.is_err(), + "{label}: expected witness generation to FAIL (VK binding violated)" + ); +} + +// --------------------------------------------------------------------------------------------- +// Shared artifact set +// --------------------------------------------------------------------------------------------- + +struct C3Artifacts { + json: PathBuf, + canonical_inner_vk: Vec, + canonical_inner_hash: String, + dummy_inner_vk: Vec, + dummy_inner_hash: String, + fold_vk: Vec, + fold_hash: String, + kernel_vk: Vec, + kernel_hash: String, + total_slots: usize, +} + +fn load_c3_artifacts() -> Option { + let json = ra_artifact("c3_fold", "json"); + let canonical_inner_vk = read_vk_fields(&dkg_artifact("share_encryption", "vk_noir"))?; + let canonical_inner_hash = read_vk_hash(&dkg_artifact("share_encryption", "vk_noir_hash"))?; + let dummy_inner_vk = read_vk_fields(&dkg_artifact("dummy_c3_inner", "vk_noir"))?; + let dummy_inner_hash = read_vk_hash(&dkg_artifact("dummy_c3_inner", "vk_noir_hash"))?; + let fold_vk = read_vk_fields(&ra_artifact("c3_fold", "vk_recursive"))?; + let fold_hash = read_vk_hash(&ra_artifact("c3_fold", "vk_recursive_hash"))?; + let kernel_vk = read_vk_fields(&ra_artifact("c3_fold_kernel", "vk_recursive"))?; + let kernel_hash = read_vk_hash(&ra_artifact("c3_fold_kernel", "vk_recursive_hash"))?; + let total_slots = abi_array_len(&json, "acc_public_inputs").map(|n| (n - 6) / 3)?; + + assert_ne!( + canonical_inner_hash, dummy_inner_hash, + "dummy circuit must have a distinct VK" + ); + + Some(C3Artifacts { + json, + canonical_inner_vk, + canonical_inner_hash, + dummy_inner_vk, + dummy_inner_hash, + fold_vk, + fold_hash, + kernel_vk, + kernel_hash, + total_slots, + }) +} + +struct NodesArtifacts { + json: PathBuf, + canonical_inner_vk: Vec, + canonical_inner_hash: String, + dummy_inner_vk: Vec, + dummy_inner_hash: String, + fold_vk: Vec, + fold_hash: String, + kernel_vk: Vec, + kernel_hash: String, + node_fold_len: usize, + total_slots: usize, +} + +fn load_nodes_artifacts() -> Option { + let json = ra_artifact("nodes_fold", "json"); + let canonical_inner_vk = read_vk_fields(&ra_artifact("node_fold", "vk_recursive"))?; + let canonical_inner_hash = read_vk_hash(&ra_artifact("node_fold", "vk_recursive_hash"))?; + let dummy_inner_vk = read_vk_fields(&ra_artifact("dummy_nodes_inner", "vk_recursive"))?; + let dummy_inner_hash = read_vk_hash(&ra_artifact("dummy_nodes_inner", "vk_recursive_hash"))?; + let fold_vk = read_vk_fields(&ra_artifact("nodes_fold", "vk_recursive"))?; + let fold_hash = read_vk_hash(&ra_artifact("nodes_fold", "vk_recursive_hash"))?; + let kernel_vk = read_vk_fields(&ra_artifact("nodes_fold_kernel", "vk_recursive"))?; + let kernel_hash = read_vk_hash(&ra_artifact("nodes_fold_kernel", "vk_recursive_hash"))?; + let node_fold_len = abi_array_len(&json, "node_fold_public_inputs")?; + let total_slots = abi_array_len(&json, "acc_public_inputs").map(|n| (n - 6) / node_fold_len)?; + + assert_ne!( + canonical_inner_hash, dummy_inner_hash, + "dummy circuit must have a distinct VK" + ); + + Some(NodesArtifacts { + json, + canonical_inner_vk, + canonical_inner_hash, + dummy_inner_vk, + dummy_inner_hash, + fold_vk, + fold_hash, + kernel_vk, + kernel_hash, + node_fold_len, + total_slots, + }) +} + +// --------------------------------------------------------------------------------------------- +// c3_fold adversarial scenarios +// --------------------------------------------------------------------------------------------- + +#[test] +fn c3_fold_rejects_vk_substitution() { + let Some(a) = load_c3_artifacts() else { + println!( + "skipping: c3_fold / share_encryption / dummy_c3_inner artifacts not found \ + (run `pnpm build:circuits --group recursive_aggregation --group dkg`)" + ); + return; + }; + let zero_proof = zero_fields(458); + + // --- Positive controls: a fully canonical step (first and non-first) must generate a witness. + let first_canonical = c3_fold_step_input( + a.canonical_inner_vk.clone(), + zero_proof.clone(), + &a.canonical_inner_hash, + a.kernel_vk.clone(), + &a.canonical_inner_hash, + &a.kernel_hash, + 1, + true, + &a.kernel_hash, + &a.fold_hash, + a.total_slots, + ); + expect_success( + run_witness_gen(&a.json, &first_canonical), + "c3_fold canonical first step", + ); + + let mid_canonical = c3_fold_step_input( + a.canonical_inner_vk.clone(), + zero_proof.clone(), + &a.canonical_inner_hash, + a.fold_vk.clone(), + &a.canonical_inner_hash, + &a.fold_hash, + 0, + false, + &a.kernel_hash, + &a.fold_hash, + a.total_slots, + ); + expect_success( + run_witness_gen(&a.json, &mid_canonical), + "c3_fold canonical middle step", + ); + + // --- Scenario 1: reject a noncanonical FIRST proof (dummy child on the genesis step). + let bad_first = c3_fold_step_input( + a.dummy_inner_vk.clone(), + zero_proof.clone(), + &a.dummy_inner_hash, + a.kernel_vk.clone(), + &a.canonical_inner_hash, + &a.kernel_hash, + 1, + true, + &a.kernel_hash, + &a.fold_hash, + a.total_slots, + ); + expect_failure( + run_witness_gen(&a.json, &bad_first), + "c3_fold noncanonical first proof", + ); + + // --- Scenario 2: reject a noncanonical MIDDLE proof. + let bad_middle = c3_fold_step_input( + a.dummy_inner_vk.clone(), + zero_proof.clone(), + &a.dummy_inner_hash, + a.fold_vk.clone(), + &a.canonical_inner_hash, + &a.fold_hash, + 0, + false, + &a.kernel_hash, + &a.fold_hash, + a.total_slots, + ); + expect_failure( + run_witness_gen(&a.json, &bad_middle), + "c3_fold noncanonical middle proof", + ); + + // --- Scenario 3: reject a canonical FINAL proof over an accumulator already poisoned by a + // noncanonical child VK (the accumulator's child VK hash is the dummy hash). + let bad_then_canonical = c3_fold_step_input( + a.canonical_inner_vk.clone(), + zero_proof.clone(), + &a.canonical_inner_hash, + a.fold_vk.clone(), + &a.dummy_inner_hash, + &a.fold_hash, + 0, + false, + &a.kernel_hash, + &a.fold_hash, + a.total_slots, + ); + expect_failure( + run_witness_gen(&a.json, &bad_then_canonical), + "c3_fold noncanonical accumulator then canonical final proof", + ); + + // --- Scenario 4: reject a chain whose KERNEL VK changes (`expected_kernel_key_hash` mismatch). + let bad_kernel = c3_fold_step_input( + a.canonical_inner_vk.clone(), + zero_proof.clone(), + &a.canonical_inner_hash, + a.kernel_vk.clone(), + &a.canonical_inner_hash, + &a.kernel_hash, + 1, + true, + &a.fold_hash, // noncanonical: expected kernel hash swapped for the fold hash + &a.fold_hash, + a.total_slots, + ); + expect_failure( + run_witness_gen(&a.json, &bad_kernel), + "c3_fold kernel VK change", + ); + + // --- Scenario 5: reject a chain whose FOLD VK changes (`expected_fold_key_hash` mismatch). + let bad_fold = c3_fold_step_input( + a.canonical_inner_vk.clone(), + zero_proof.clone(), + &a.canonical_inner_hash, + a.fold_vk.clone(), + &a.canonical_inner_hash, + &a.fold_hash, + 0, + false, + &a.kernel_hash, + &a.kernel_hash, // noncanonical: expected fold hash swapped for the kernel hash + a.total_slots, + ); + expect_failure( + run_witness_gen(&a.json, &bad_fold), + "c3_fold fold VK change", + ); +} + +// --------------------------------------------------------------------------------------------- +// nodes_fold adversarial scenarios +// --------------------------------------------------------------------------------------------- + +#[test] +fn nodes_fold_rejects_vk_substitution() { + let Some(a) = load_nodes_artifacts() else { + println!( + "skipping: nodes_fold / node_fold / dummy_nodes_inner artifacts not found \ + (run `pnpm build:circuits --group recursive_aggregation`)" + ); + return; + }; + let zero_proof = zero_fields(410); + + let first_canonical = nodes_fold_step_input( + a.canonical_inner_vk.clone(), + zero_proof.clone(), + &a.canonical_inner_hash, + a.kernel_vk.clone(), + &a.canonical_inner_hash, + &a.kernel_hash, + 1, + true, + &a.kernel_hash, + &a.fold_hash, + a.node_fold_len, + a.total_slots, + ); + expect_success( + run_witness_gen(&a.json, &first_canonical), + "nodes_fold canonical first step", + ); + + let mid_canonical = nodes_fold_step_input( + a.canonical_inner_vk.clone(), + zero_proof.clone(), + &a.canonical_inner_hash, + a.fold_vk.clone(), + &a.canonical_inner_hash, + &a.fold_hash, + 0, + false, + &a.kernel_hash, + &a.fold_hash, + a.node_fold_len, + a.total_slots, + ); + expect_success( + run_witness_gen(&a.json, &mid_canonical), + "nodes_fold canonical middle step", + ); + + let bad_first = nodes_fold_step_input( + a.dummy_inner_vk.clone(), + zero_proof.clone(), + &a.dummy_inner_hash, + a.kernel_vk.clone(), + &a.canonical_inner_hash, + &a.kernel_hash, + 1, + true, + &a.kernel_hash, + &a.fold_hash, + a.node_fold_len, + a.total_slots, + ); + expect_failure( + run_witness_gen(&a.json, &bad_first), + "nodes_fold noncanonical first proof", + ); + + let bad_middle = nodes_fold_step_input( + a.dummy_inner_vk.clone(), + zero_proof.clone(), + &a.dummy_inner_hash, + a.fold_vk.clone(), + &a.canonical_inner_hash, + &a.fold_hash, + 0, + false, + &a.kernel_hash, + &a.fold_hash, + a.node_fold_len, + a.total_slots, + ); + expect_failure( + run_witness_gen(&a.json, &bad_middle), + "nodes_fold noncanonical middle proof", + ); + + let bad_then_canonical = nodes_fold_step_input( + a.canonical_inner_vk.clone(), + zero_proof.clone(), + &a.canonical_inner_hash, + a.fold_vk.clone(), + &a.dummy_inner_hash, + &a.fold_hash, + 0, + false, + &a.kernel_hash, + &a.fold_hash, + a.node_fold_len, + a.total_slots, + ); + expect_failure( + run_witness_gen(&a.json, &bad_then_canonical), + "nodes_fold noncanonical accumulator then canonical final proof", + ); + + let bad_kernel = nodes_fold_step_input( + a.canonical_inner_vk.clone(), + zero_proof.clone(), + &a.canonical_inner_hash, + a.kernel_vk.clone(), + &a.canonical_inner_hash, + &a.kernel_hash, + 1, + true, + &a.fold_hash, + &a.fold_hash, + a.node_fold_len, + a.total_slots, + ); + expect_failure( + run_witness_gen(&a.json, &bad_kernel), + "nodes_fold kernel VK change", + ); + + let bad_fold = nodes_fold_step_input( + a.canonical_inner_vk.clone(), + zero_proof.clone(), + &a.canonical_inner_hash, + a.fold_vk.clone(), + &a.canonical_inner_hash, + &a.fold_hash, + 0, + false, + &a.kernel_hash, + &a.kernel_hash, + a.node_fold_len, + a.total_slots, + ); + expect_failure( + run_witness_gen(&a.json, &bad_fold), + "nodes_fold fold VK change", + ); +} + +// --------------------------------------------------------------------------------------------- +// Real dummy-proof rejection (bb-gated) +// --------------------------------------------------------------------------------------------- + +/// Proves an arbitrary staged circuit with `bb` and returns `(proof, public_inputs)`. +fn prove_named_bin( + backend: &ZkBackend, + json: &Path, + vk: &Path, + witness: &[u8], + verifier_target: &str, + job_id: &str, +) -> Result<(Vec, Vec), String> { + let job_dir = backend.work_dir.join(job_id); + std::fs::create_dir_all(&job_dir).map_err(|e| e.to_string())?; + let witness_path = job_dir.join("witness.gz"); + std::fs::write(&witness_path, witness).map_err(|e| e.to_string())?; + let out_dir = job_dir.join("out"); + std::fs::create_dir_all(&out_dir).map_err(|e| e.to_string())?; + + let status = StdCommand::new(&backend.bb_binary) + .arg("prove") + .arg("-b") + .arg(json) + .arg("-w") + .arg(&witness_path) + .arg("-k") + .arg(vk) + .arg("-o") + .arg(&out_dir) + .arg("-v") + .arg("-t") + .arg(verifier_target) + .status() + .map_err(|e| format!("bb spawn: {e}"))?; + if !status.success() { + return Err(format!("bb prove failed with status {status}")); + } + let proof = std::fs::read(out_dir.join("proof")).map_err(|e| e.to_string())?; + let public_inputs = std::fs::read(out_dir.join("public_inputs")).map_err(|e| e.to_string())?; + Ok((proof, public_inputs)) +} + +fn dummy_c3_witness() -> Result, String> { + let compiled = CompiledCircuit::from_file(&dkg_artifact("dummy_c3_inner", "json")) + .map_err(|e| e.to_string())?; + let input = serde_json::json!({ + "expected_pk_commitment": "0x00", + "expected_message_commitment": "0x01", + "party_idx": 0, + "mod_idx": 0, + }); + let input_map = inputs_json_to_input_map(&input).map_err(|e| e.to_string())?; + WitnessGenerator::new() + .generate_witness(&compiled, input_map) + .map_err(|e| e.to_string()) +} + +fn dummy_nodes_witness(node_fold_len: usize) -> Result, String> { + let compiled = CompiledCircuit::from_file(&ra_artifact("dummy_nodes_inner", "json")) + .map_err(|e| e.to_string())?; + let input = serde_json::json!({ "node_fold_public_inputs": zero_fields(node_fold_len) }); + let input_map = inputs_json_to_input_map(&input).map_err(|e| e.to_string())?; + WitnessGenerator::new() + .generate_witness(&compiled, input_map) + .map_err(|e| e.to_string()) +} + +/// A real, valid UltraHonk proof of a compatible dummy circuit, fed into a canonical `c3_fold` +/// middle step, must be rejected at witness generation. +#[tokio::test] +async fn c3_fold_rejects_real_dummy_proof() { + let Some(bb) = find_bb().await else { + println!("skipping: bb not found"); + return; + }; + let Some(a) = load_c3_artifacts() else { + println!("skipping: c3_fold artifacts not found"); + return; + }; + + let (backend, _temp) = setup_test_prover(&bb).await; + let witness = dummy_c3_witness().expect("dummy_c3_inner witness"); + let (proof, _pi) = prove_named_bin( + &backend, + &dkg_artifact("dummy_c3_inner", "json"), + &dkg_artifact("dummy_c3_inner", "vk_noir"), + &witness, + "noir-recursive", + "dummy-c3-inner", + ) + .expect("real dummy_c3_inner proof"); + + let mut proof_fields = zero_fields(458); + for (i, f) in proof + .chunks(32) + .map(|c| format!("0x{}", hex::encode(c))) + .enumerate() + .take(proof_fields.len()) + { + proof_fields[i] = f; + } + + let step = c3_fold_step_input( + a.dummy_inner_vk.clone(), + proof_fields, + &a.dummy_inner_hash, + a.fold_vk.clone(), + &a.canonical_inner_hash, + &a.fold_hash, + 0, + false, + &a.kernel_hash, + &a.fold_hash, + a.total_slots, + ); + expect_failure( + run_witness_gen(&a.json, &step), + "c3_fold real dummy proof must be rejected", + ); +} + +/// A real, valid UltraHonk proof of a compatible dummy circuit, fed into a canonical `nodes_fold` +/// middle step, must be rejected at witness generation. +#[tokio::test] +async fn nodes_fold_rejects_real_dummy_proof() { + let Some(bb) = find_bb().await else { + println!("skipping: bb not found"); + return; + }; + let Some(a) = load_nodes_artifacts() else { + println!("skipping: nodes_fold artifacts not found"); + return; + }; + + let (backend, _temp) = setup_test_prover(&bb).await; + let witness = dummy_nodes_witness(a.node_fold_len).expect("dummy_nodes_inner witness"); + let (proof, _pi) = prove_named_bin( + &backend, + &ra_artifact("dummy_nodes_inner", "json"), + &ra_artifact("dummy_nodes_inner", "vk_recursive"), + &witness, + "noir-recursive-no-zk", + "dummy-nodes-inner", + ) + .expect("real dummy_nodes_inner proof"); + + let mut proof_fields = zero_fields(410); + for (i, f) in proof + .chunks(32) + .map(|c| format!("0x{}", hex::encode(c))) + .enumerate() + .take(proof_fields.len()) + { + proof_fields[i] = f; + } + + let step = nodes_fold_step_input( + a.dummy_inner_vk.clone(), + proof_fields, + &a.dummy_inner_hash, + a.fold_vk.clone(), + &a.canonical_inner_hash, + &a.fold_hash, + 0, + false, + &a.kernel_hash, + &a.fold_hash, + a.node_fold_len, + a.total_slots, + ); + expect_failure( + run_witness_gen(&a.json, &step), + "nodes_fold real dummy proof must be rejected", + ); +} diff --git a/crates/zk-prover/tests/local_e2e_tests.rs b/crates/zk-prover/tests/local_e2e_tests.rs index 83e5289e5b..ca6cd26322 100644 --- a/crates/zk-prover/tests/local_e2e_tests.rs +++ b/crates/zk-prover/tests/local_e2e_tests.rs @@ -21,8 +21,11 @@ use std::sync::Arc; use ark_bn254::Fr; use ark_ff::{BigInteger, PrimeField, Zero}; use common::{ - extract_field, extract_field_from_end, find_bb, require_minimum_circuits, - setup_compiled_circuit, setup_test_prover, + active_bin_preset, compiled_circuit_artifacts_available, extract_field, extract_field_from_end, + find_bb, recursive_circuit_artifacts_available, require_minimum_circuits, + require_minimum_circuits_for_preset, setup_compiled_circuit, setup_compiled_circuit_for_preset, + setup_recursive_aggregation_fold_circuit, setup_recursive_aggregation_fold_circuit_for_preset, + setup_test_prover, }; use e3_events::CircuitName; use e3_fhe_params::{build_pair_for_preset, BfvPreset}; @@ -32,7 +35,7 @@ use e3_zk_helpers::circuits::dkg::pk::circuit::PkCircuitData; use e3_zk_helpers::circuits::{ commitments::{ compute_aggregated_shares_commitment, compute_dkg_pk_commitment, - compute_threshold_decryption_share_commitment, + compute_sc_sk_secret_root_commitment, compute_threshold_decryption_share_commitment, }, threshold::decrypted_shares_aggregation::MAX_MSG_NON_ZERO_COEFFS, CircuitComputation, @@ -56,10 +59,7 @@ use e3_zk_helpers::threshold::{ }, }; use e3_zk_helpers::CiphernodesCommitteeSize; -use e3_zk_helpers::{ - compute_pk_aggregation_commitment, compute_share_computation_sk_commitment, - compute_threshold_pk_commitment, -}; +use e3_zk_helpers::{compute_pk_aggregation_commitment, compute_threshold_pk_commitment}; use e3_zk_prover::{CircuitVariant, Provable, ZkBackend, ZkProver}; use fhe::trbfv::TRBFV; @@ -230,8 +230,10 @@ async fn setup_share_computation_sk_test() -> Option<( require_minimum_circuits()?; let (backend, temp) = setup_test_prover(&bb).await; - setup_compiled_circuit(&backend, "dkg", "sk_share_computation").await; - setup_compiled_circuit(&backend, "dkg", "e_sm_share_computation").await; + setup_compiled_circuit(&backend, "dkg", "sk_share_computation_chunk").await; + setup_recursive_aggregation_fold_circuit(&backend, CircuitName::C2ChunkBatch).await; + setup_recursive_aggregation_fold_circuit(&backend, CircuitName::SkC2ChunkFinalize).await; + setup_recursive_aggregation_fold_circuit(&backend, CircuitName::C2abChunkFold).await; let sample = ShareComputationCircuitData::generate_sample(preset, committee, DkgInputType::SecretKey) @@ -264,8 +266,10 @@ async fn setup_share_computation_e_sm_test() -> Option<( require_minimum_circuits()?; let (backend, temp) = setup_test_prover(&bb).await; - setup_compiled_circuit(&backend, "dkg", "sk_share_computation").await; - setup_compiled_circuit(&backend, "dkg", "e_sm_share_computation").await; + setup_compiled_circuit(&backend, "dkg", "esm_share_computation_chunk").await; + setup_recursive_aggregation_fold_circuit(&backend, CircuitName::C2ChunkBatch).await; + setup_recursive_aggregation_fold_circuit(&backend, CircuitName::ESmC2ChunkFinalize).await; + setup_recursive_aggregation_fold_circuit(&backend, CircuitName::C2abChunkFold).await; let sample = ShareComputationCircuitData::generate_sample( preset, @@ -501,8 +505,6 @@ macro_rules! e2e_proof_tests { e2e_proof_tests! { (pk_generation, setup_pk_generation_test(), CircuitVariant::Recursive), (pk, setup_pk_test(), CircuitVariant::Recursive), - (share_computation_sk, setup_share_computation_sk_test(), CircuitVariant::Recursive), - (share_computation_e_sm, setup_share_computation_e_sm_test(), CircuitVariant::Recursive), (share_encryption_sk, setup_share_encryption_sk_test(), CircuitVariant::Recursive), (share_encryption_e_sm, setup_share_encryption_e_sm_test(), CircuitVariant::Recursive), (share_decryption, setup_share_decryption_test(), CircuitVariant::Recursive), @@ -532,9 +534,10 @@ async fn test_pk_generation_commitment_consistency() { let pk_commitment_from_proof = extract_field_from_end(&proof.public_signals, 1); // Recompute commitments from the witness - let sk_commitment_expected = compute_share_computation_sk_commitment( + let sk_commitment_expected = compute_sc_sk_secret_root_commitment( &computation_output.inputs.sk, computation_output.bits.sk_bit, + 512, ); let pk_commitment_expected = compute_threshold_pk_commitment( &computation_output.inputs.pk0is, @@ -597,8 +600,8 @@ async fn test_pk_bfv_commitment_consistency() { } #[tokio::test] -async fn test_share_computation_sk_commitment_consistency() { - let Some((_backend, _temp, prover, circuit, sample, preset, e3_id)) = +async fn test_chunked_share_computation_proof() { + let Some((_backend, _temp, prover, _circuit, sample, preset, e3_id)) = setup_share_computation_sk_test().await else { println!("skipping: bb not found"); @@ -607,32 +610,48 @@ async fn test_share_computation_sk_commitment_consistency() { let artifacts_dir = preset.artifacts_dir_for_committee(CiphernodesCommitteeSize::Minimum.as_str()); - let proof = circuit - .prove(&prover, &preset, &sample, e3_id, &artifacts_dir) - .expect("inner sk_share_computation proof should succeed"); + let result = e3_zk_prover::prove_chunked_share_computation( + &prover, + preset, + &sample, + e3_id, + &artifacts_dir, + ) + .expect("chunked C2 proof generation should succeed"); - assert_eq!( - proof.circuit, - CircuitName::SkShareComputation, - "expected SkShareComputation inner circuit tag" - ); - assert!( - !proof.public_signals.is_empty() && proof.public_signals.len() % 32 == 0, - "inner C2 public signals should be non-empty 32-byte field chunks" + assert_eq!(result.proof.circuit, CircuitName::SkC2ChunkFinalize); + assert_eq!(result.chunk_count, 1); + let chunk_vk = e3_zk_prover::test_utils::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, &artifacts_dir), + CircuitName::SkShareComputationChunk, + ) + .expect("SK C2 chunk VK"); + let chunk_key_hash = Fr::from_be_bytes_mod_order( + &hex::decode(chunk_vk.key_hash.trim_start_matches("0x")).expect("chunk VK hash"), ); - - let fields = public_signals_to_fields(&proof.public_signals); - assert!( - fields.iter().any(|f| !f.is_zero()), - "inner share computation public signals should not all be zero" + assert_eq!( + public_signals_to_fields(&result.proof.public_signals)[0], + chunk_key_hash ); + let expected_fields = + 3 + CiphernodesCommitteeSize::Minimum.values().n * preset.metadata().num_moduli; + assert_eq!(result.proof.public_signals.len() / 32, expected_fields); + assert!(prover + .verify_proof_with_variant( + &result.proof, + e3_id, + 1, + CircuitVariant::Recursive, + &artifacts_dir, + ) + .expect("chunked C2 terminal proof verification should succeed")); prover.cleanup(e3_id).unwrap(); } #[tokio::test] -async fn test_share_computation_e_sm_commitment_consistency() { - let Some((_backend, _temp, prover, circuit, sample, preset, e3_id)) = +async fn test_chunked_esm_share_computation_proof() { + let Some((_backend, _temp, prover, _circuit, sample, preset, e3_id)) = setup_share_computation_e_sm_test().await else { println!("skipping: bb not found"); @@ -641,26 +660,156 @@ async fn test_share_computation_e_sm_commitment_consistency() { let artifacts_dir = preset.artifacts_dir_for_committee(CiphernodesCommitteeSize::Minimum.as_str()); - let proof = circuit - .prove(&prover, &preset, &sample, e3_id, &artifacts_dir) - .expect("inner e_sm_share_computation proof should succeed"); - - assert_eq!( - proof.circuit, - CircuitName::ESmShareComputation, - "expected ESmShareComputation inner circuit tag" - ); - assert!( - !proof.public_signals.is_empty() && proof.public_signals.len() % 32 == 0, - "inner C2 public signals should be non-empty 32-byte field chunks" + let result = e3_zk_prover::prove_chunked_share_computation( + &prover, + preset, + &sample, + e3_id, + &artifacts_dir, + ) + .expect("chunked ESM C2 proof generation should succeed"); + + assert_eq!(result.proof.circuit, CircuitName::ESmC2ChunkFinalize); + assert_eq!(result.chunk_count, 1); + let fields = public_signals_to_fields(&result.proof.public_signals); + let chunk_vk = e3_zk_prover::test_utils::load_vk_artifacts( + &prover.circuits_dir(CircuitVariant::Recursive, &artifacts_dir), + CircuitName::ESmShareComputationChunk, + ) + .expect("ESM C2 chunk VK"); + let chunk_key_hash = Fr::from_be_bytes_mod_order( + &hex::decode(chunk_vk.key_hash.trim_start_matches("0x")).expect("chunk VK hash"), ); + assert_eq!(fields[0], chunk_key_hash); + let expected_fields = + 3 + CiphernodesCommitteeSize::Minimum.values().n * preset.metadata().num_moduli; + assert_eq!(fields.len(), expected_fields); + assert!(prover + .verify_proof_with_variant( + &result.proof, + e3_id, + 1, + CircuitVariant::Recursive, + &artifacts_dir, + ) + .expect("chunked ESM C2 terminal proof verification should succeed")); - let fields = public_signals_to_fields(&proof.public_signals); - assert!( - fields.iter().any(|f| !f.is_zero()), - "inner share computation public signals should not all be zero" - ); + prover.cleanup(e3_id).unwrap(); +} + +async fn setup_secure_chunked_share_computation_test( + input_type: DkgInputType, + e3_id: &'static str, +) -> Option<( + ZkBackend, + tempfile::TempDir, + ZkProver, + ShareComputationCircuitData, + BfvPreset, + &'static str, +)> { + if active_bin_preset().as_deref() != Some("secure-8192") { + println!("skipping: secure-8192 circuit artifacts are not active"); + return None; + } + let committee = CiphernodesCommitteeSize::Minimum.values(); + let preset = BfvPreset::SecureThreshold8192; + let bb = find_bb().await?; + if require_minimum_circuits_for_preset(preset).is_none() { + return None; + } + if ["sk_share_computation_chunk", "esm_share_computation_chunk"] + .iter() + .any(|circuit| !compiled_circuit_artifacts_available("dkg", circuit)) + || [ + CircuitName::C2ChunkBatch, + CircuitName::SkC2ChunkFinalize, + CircuitName::ESmC2ChunkFinalize, + ] + .iter() + .any(|circuit| !recursive_circuit_artifacts_available(*circuit)) + { + println!("skipping: secure-8192 circuit artifacts are not staged"); + return None; + } + let (backend, temp) = setup_test_prover(&bb).await; + for circuit in ["sk_share_computation_chunk", "esm_share_computation_chunk"] { + setup_compiled_circuit_for_preset(&backend, "dkg", circuit, preset, "minimum").await; + } + for circuit in [ + CircuitName::C2ChunkBatch, + CircuitName::SkC2ChunkFinalize, + CircuitName::ESmC2ChunkFinalize, + ] { + setup_recursive_aggregation_fold_circuit_for_preset(&backend, circuit, preset, "minimum") + .await; + } + + let sample = + ShareComputationCircuitData::generate_sample(preset, committee, input_type).ok()?; + let prover = ZkProver::new(&backend); + Some((backend, temp, prover, sample, preset, e3_id)) +} + +#[tokio::test] +async fn test_secure_chunked_share_computation_proof() { + let Some((_backend, _temp, prover, sample, preset, e3_id)) = + setup_secure_chunked_share_computation_test(DkgInputType::SecretKey, "secure-sk").await + else { + return; + }; + let artifacts_dir = preset.artifacts_dir_for_committee("minimum"); + let result = e3_zk_prover::prove_chunked_share_computation( + &prover, + preset, + &sample, + e3_id, + &artifacts_dir, + ) + .expect("secure chunked C2a proof generation should succeed"); + assert_eq!(result.proof.circuit, CircuitName::SkC2ChunkFinalize); + assert_eq!(result.chunk_count, 16); + assert!(prover + .verify_proof_with_variant( + &result.proof, + e3_id, + 1, + CircuitVariant::Recursive, + &artifacts_dir, + ) + .expect("secure chunked C2a terminal proof verification should succeed")); + prover.cleanup(e3_id).unwrap(); +} + +#[tokio::test] +async fn test_secure_chunked_esm_share_computation_proof() { + let Some((_backend, _temp, prover, sample, preset, e3_id)) = + setup_secure_chunked_share_computation_test(DkgInputType::SmudgingNoise, "secure-esm") + .await + else { + return; + }; + let artifacts_dir = preset.artifacts_dir_for_committee("minimum"); + let result = e3_zk_prover::prove_chunked_share_computation( + &prover, + preset, + &sample, + e3_id, + &artifacts_dir, + ) + .expect("secure chunked C2b proof generation should succeed"); + assert_eq!(result.proof.circuit, CircuitName::ESmC2ChunkFinalize); + assert_eq!(result.chunk_count, 16); + assert!(prover + .verify_proof_with_variant( + &result.proof, + e3_id, + 1, + CircuitVariant::Recursive, + &artifacts_dir, + ) + .expect("secure chunked C2b terminal proof verification should succeed")); prover.cleanup(e3_id).unwrap(); } diff --git a/crates/zk-prover/tests/node_fold_correlated_e2e_tests.rs b/crates/zk-prover/tests/node_fold_correlated_e2e_tests.rs index 45d3c49f36..89875e6e1f 100644 --- a/crates/zk-prover/tests/node_fold_correlated_e2e_tests.rs +++ b/crates/zk-prover/tests/node_fold_correlated_e2e_tests.rs @@ -17,32 +17,26 @@ mod node_fold_witness; use std::path::PathBuf; use common::{ - find_bb, require_minimum_circuits, setup_compiled_circuit, - setup_recursive_aggregation_fold_circuit, setup_test_prover, + active_bin_preset, compiled_circuit_artifacts_available, find_bb, + recursive_circuit_artifacts_available, require_minimum_circuits_for_preset, + setup_compiled_circuit_for_preset, setup_recursive_aggregation_fold_circuit_for_preset, + setup_test_prover, }; -use e3_events::{CircuitName, Proof}; +use e3_events::CircuitName; use e3_fhe_params::BfvPreset; use e3_zk_helpers::computation::Computation; use e3_zk_helpers::computation::DkgInputType; use e3_zk_helpers::dkg::pk::circuit::{PkCircuit, PkCircuitData}; -use e3_zk_helpers::dkg::share_computation::{ - Inputs as ShareComputationInputs, ShareComputationCircuit, -}; +use e3_zk_helpers::dkg::share_computation::Inputs as ShareComputationInputs; use e3_zk_helpers::dkg::share_decryption::{ShareDecryptionCircuit, ShareDecryptionCircuitData}; use e3_zk_helpers::dkg::share_encryption::ShareEncryptionCircuit; use e3_zk_helpers::threshold::pk_generation::PkGenerationCircuit; use e3_zk_helpers::CiphernodesCommitteeSize; -use e3_zk_prover::test_utils::{ - fold_witness_field_strings, fold_witness_input_map, load_vk_artifacts, -}; -use e3_zk_prover::{generate_sequential_c3_fold, CircuitVariant, Provable, ZkProver}; -use e3_zk_prover::{CompiledCircuit, WitnessGenerator}; +use e3_zk_prover::{CircuitVariant, NodeDkgFoldInput, Provable, ZkProver}; use node_fold_witness::{ pk_generation_sample_with_esi, share_computation_esm_from_esi, share_computation_sk_from_pk, share_encryption_for_slot, }; -use serde::Serialize; -use serde_json::json; fn recursive_aggregation_compiled_json_path(circuit: CircuitName) -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")) @@ -73,51 +67,11 @@ fn c3_fold_total_slots_from_compiled_json() -> usize { .and_then(|p| p.get("type")?.get("length")?.as_u64()) }) .expect("c3_fold acc_public_inputs length") as usize; - (len - 4) / 3 -} - -fn field_str_zero() -> String { - format!("0x{}", hex::encode([0u8; 32])) -} - -fn proof_public_fields(proof: &Proof) -> Vec { - fold_witness_field_strings(proof.public_signals.as_ref()).expect("public_signals as fields") -} - -#[derive(Serialize)] -struct C2abFoldWitness { - c2a_vk: Vec, - c2a_proof: Vec, - c2a_public: Vec, - c2b_vk: Vec, - c2b_proof: Vec, - c2b_public: Vec, - c2a_key_hash: String, - c2b_key_hash: String, -} - -#[derive(Serialize)] -struct C3abFoldWitness { - c3a_vk: Vec, - c3a_proof: Vec, - c3a_public: Vec, - c3b_vk: Vec, - c3b_proof: Vec, - c3b_public: Vec, - c3a_key_hash: String, - c3b_key_hash: String, -} - -#[derive(Serialize)] -struct C4abFoldWitness { - c4a_vk: Vec, - c4a_proof: Vec, - c4a_public: Vec, - c4b_vk: Vec, - c4b_proof: Vec, - c4b_public: Vec, - c4a_key_hash: String, - c4b_key_hash: String, + assert!( + len >= 6 && (len - 6).is_multiple_of(3), + "unexpected acc_public_inputs length {len} (expected 6 + 3 * slots)" + ); + (len - 6) / 3 } fn triplicate_honest_rows(mut d: ShareDecryptionCircuitData) -> ShareDecryptionCircuitData { @@ -128,14 +82,13 @@ fn triplicate_honest_rows(mut d: ShareDecryptionCircuitData) -> ShareDecryptionC d } -#[tokio::test] -async fn node_fold_correlated_sparse_self_slot_proves_and_verifies() { +async fn run_node_fold_correlated_sparse_self_slot(preset: BfvPreset) { let Some(bb) = find_bb().await else { println!("skipping: bb not found"); return; }; - if require_minimum_circuits().is_none() { + if require_minimum_circuits_for_preset(preset).is_none() { return; } @@ -153,7 +106,6 @@ async fn node_fold_correlated_sparse_self_slot_proves_and_verifies() { } let committee = CiphernodesCommitteeSize::Minimum.values(); - let preset = BfvPreset::InsecureThreshold512; let (backend, temp) = setup_test_prover(&bb).await; let prover = ZkProver::new(&backend); @@ -162,32 +114,28 @@ async fn node_fold_correlated_sparse_self_slot_proves_and_verifies() { for g in [ "pk", - "sk_share_computation", - "e_sm_share_computation", + "sk_share_computation_chunk", + "esm_share_computation_chunk", "share_encryption", "share_decryption", ] { - let name = match g { - "pk" => "pk", - "sk_share_computation" => "sk_share_computation", - "e_sm_share_computation" => "e_sm_share_computation", - "share_encryption" => "share_encryption", - "share_decryption" => "share_decryption", - _ => unreachable!(), - }; - setup_compiled_circuit(&backend, "dkg", name).await; + setup_compiled_circuit_for_preset(&backend, "dkg", g, preset, "minimum").await; } - setup_compiled_circuit(&backend, "threshold", "pk_generation").await; + setup_compiled_circuit_for_preset(&backend, "threshold", "pk_generation", preset, "minimum") + .await; for c in [ - CircuitName::C2abFold, + CircuitName::C2ChunkBatch, + CircuitName::SkC2ChunkFinalize, + CircuitName::ESmC2ChunkFinalize, + CircuitName::C2abChunkFold, CircuitName::C3Fold, CircuitName::C3FoldKernel, CircuitName::C3abFold, CircuitName::C4abFold, CircuitName::NodeFold, ] { - setup_recursive_aggregation_fold_circuit(&backend, c).await; + setup_recursive_aggregation_fold_circuit_for_preset(&backend, c, preset, "minimum").await; } let (pk_gen, esi, pk_secret_key) = pk_generation_sample_with_esi(preset, committee.clone()) @@ -203,9 +151,6 @@ async fn node_fold_correlated_sparse_self_slot_proves_and_verifies() { let pk_bfv_data = PkCircuitData::generate_sample(preset).expect("C0 pk sample"); let c0_e3 = "e3-nf-c0"; let c1_e3 = "e3-nf-c1"; - let c2a_e3 = "e3-nf-c2a"; - let c2b_e3 = "e3-nf-c2b"; - let c2ab_e3 = "e3-nf-c2ab"; let c0_proof = PkCircuit .prove_with_variant( @@ -228,79 +173,14 @@ async fn node_fold_correlated_sparse_self_slot_proves_and_verifies() { ) .expect("C1 pk_generation proof"); - let c2a_proof = ShareComputationCircuit - .prove_with_variant( - &prover, - &preset, - &share_sk, - c2a_e3, - CircuitVariant::Recursive, - &artifacts_dir, - ) - .expect("C2a proof"); - let c2b_proof = ShareComputationCircuit - .prove_with_variant( - &prover, - &preset, - &share_esm, - c2b_e3, - CircuitVariant::Recursive, - &artifacts_dir, - ) - .expect("C2b proof"); - - let c2a_vk = load_vk_artifacts( - &prover.circuits_dir(CircuitVariant::Recursive, &artifacts_dir), - CircuitName::SkShareComputation, - ) - .expect("c2a vk"); - let c2b_vk = load_vk_artifacts( - &prover.circuits_dir(CircuitVariant::Recursive, &artifacts_dir), - CircuitName::ESmShareComputation, - ) - .expect("c2b vk"); - - let c2a_pub = proof_public_fields(&c2a_proof); - let c2b_pub = proof_public_fields(&c2b_proof); - let c2ab = C2abFoldWitness { - c2a_vk: c2a_vk.verification_key, - c2a_proof: fold_witness_field_strings(&c2a_proof.data).expect("c2a proof fields"), - c2a_public: c2a_pub.clone(), - c2b_vk: c2b_vk.verification_key, - c2b_proof: fold_witness_field_strings(&c2b_proof.data).expect("c2b proof fields"), - c2b_public: c2b_pub.clone(), - c2a_key_hash: c2a_vk.key_hash.clone(), - c2b_key_hash: c2b_vk.key_hash.clone(), - }; - - let c2ab_json = serde_json::to_value(&c2ab).expect("c2ab json"); - let c2ab_map = fold_witness_input_map(&c2ab_json).expect("c2ab input map"); - let c2ab_compiled = CompiledCircuit::from_file( - &prover - .circuits_dir(CircuitVariant::Default, &artifacts_dir) - .join(CircuitName::C2abFold.dir_path()) - .join(format!("{}.json", CircuitName::C2abFold.as_str())), - ) - .expect("c2ab compiled"); - let c2ab_witness = WitnessGenerator::new() - .generate_witness(&c2ab_compiled, c2ab_map) - .expect("c2ab witness"); - let c2ab_proof = prover - .generate_recursive_aggregation_bin_proof( - CircuitName::C2abFold, - &c2ab_witness, - c2ab_e3, - &artifacts_dir, - ) - .expect("c2ab_fold proof"); - let (_dkg_th, dkg_dkg) = e3_fhe_params::build_pair_for_preset(preset).expect("pair"); let mut rng = rand::rng(); let dkg_sk = fhe::bfv::SecretKey::random(&dkg_dkg, &mut rng); let dkg_pk = fhe::bfv::PublicKey::new(&dkg_sk, &mut rng); let total_slots = c3_fold_total_slots_from_compiled_json(); - assert_eq!(total_slots, 6, "Micro / insecure preset uses 3×2 C3 slots"); + let expected_slots = committee.n * preset.metadata().num_moduli; + assert_eq!(total_slots, expected_slots); let slots_per_party = total_slots / committee.n; let own_party_id = 0usize; @@ -319,6 +199,7 @@ async fn node_fold_correlated_sparse_self_slot_proves_and_verifies() { &sk_inputs, slot, DkgInputType::SecretKey, + committee.clone(), ) .expect("C3a slot encrypt"); let db = share_encryption_for_slot( @@ -328,6 +209,7 @@ async fn node_fold_correlated_sparse_self_slot_proves_and_verifies() { &esm_inputs, slot, DkgInputType::SmudgingNoise, + committee.clone(), ) .expect("C3b slot encrypt"); @@ -357,83 +239,10 @@ async fn node_fold_correlated_sparse_self_slot_proves_and_verifies() { ); slot_indices.push(slot as u32); } - assert_eq!(slot_indices, vec![2, 3, 4, 5]); - - let c3a_folded = generate_sequential_c3_fold( - &prover, - &c3a_inners, - &slot_indices, - total_slots, - "e3-nf-c3fold-a", - &artifacts_dir, - ) - .expect("c3 fold sk chain"); - let c3b_folded = generate_sequential_c3_fold( - &prover, - &c3b_inners, - &slot_indices, - total_slots, - "e3-nf-c3fold-b", - &artifacts_dir, - ) - .expect("c3 fold e_sm chain"); - - let c3a_vk = load_vk_artifacts( - &prover.circuits_dir(CircuitVariant::Default, &artifacts_dir), - CircuitName::C3Fold, - ) - .expect("c3a fold vk"); - let c3b_vk = load_vk_artifacts( - &prover.circuits_dir(CircuitVariant::Default, &artifacts_dir), - CircuitName::C3Fold, - ) - .expect("c3b fold vk"); - - let c3a_pub = proof_public_fields(&c3a_folded); - let c3b_pub = proof_public_fields(&c3b_folded); - let c3_prefix_len = c3a_pub.len() - (3 * total_slots); - for slot in 0..slots_per_party { - assert_eq!(c3a_pub[c3_prefix_len + slot], field_str_zero()); - assert_eq!( - c3a_pub[c3_prefix_len + total_slots + slot], - field_str_zero() - ); - assert_eq!(c3b_pub[c3_prefix_len + slot], field_str_zero()); - assert_eq!( - c3b_pub[c3_prefix_len + total_slots + slot], - field_str_zero() - ); - } - let c3ab = C3abFoldWitness { - c3a_vk: c3a_vk.verification_key, - c3a_proof: fold_witness_field_strings(&c3a_folded.data).expect("c3a fold proof"), - c3a_public: c3a_pub, - c3b_vk: c3b_vk.verification_key, - c3b_proof: fold_witness_field_strings(&c3b_folded.data).expect("c3b fold proof"), - c3b_public: c3b_pub, - c3a_key_hash: c3a_vk.key_hash.clone(), - c3b_key_hash: c3b_vk.key_hash.clone(), - }; - let c3ab_json = serde_json::to_value(&c3ab).expect("c3ab json"); - let c3ab_map = fold_witness_input_map(&c3ab_json).expect("c3ab map"); - let c3ab_compiled = CompiledCircuit::from_file( - &prover - .circuits_dir(CircuitVariant::Default, &artifacts_dir) - .join(CircuitName::C3abFold.dir_path()) - .join(format!("{}.json", CircuitName::C3abFold.as_str())), - ) - .expect("c3ab compiled"); - let c3ab_witness = WitnessGenerator::new() - .generate_witness(&c3ab_compiled, c3ab_map) - .expect("c3ab witness"); - let c3ab_proof = prover - .generate_recursive_aggregation_bin_proof( - CircuitName::C3abFold, - &c3ab_witness, - "e3-nf-c3ab", - &artifacts_dir, - ) - .expect("c3ab_fold proof"); + let expected_slot_indices: Vec = (slots_per_party..total_slots) + .map(|slot| slot as u32) + .collect(); + assert_eq!(slot_indices, expected_slot_indices); let c4a_sample = ShareDecryptionCircuitData::generate_sample( preset, @@ -473,116 +282,103 @@ async fn node_fold_correlated_sparse_self_slot_proves_and_verifies() { ) .expect("C4b"); - let c4a_vk = load_vk_artifacts( - &prover.circuits_dir(CircuitVariant::Recursive, &artifacts_dir), - CircuitName::DkgShareDecryption, + let c2a_chunked = e3_zk_prover::prove_chunked_share_computation( + &prover, + preset, + &share_sk, + "e3-nf-c2a-chunked", + &artifacts_dir, ) - .expect("c4a vk"); - let c4b_vk = load_vk_artifacts( - &prover.circuits_dir(CircuitVariant::Recursive, &artifacts_dir), - CircuitName::DkgShareDecryption, + .expect("chunked C2a proof"); + let c2b_chunked = e3_zk_prover::prove_chunked_share_computation( + &prover, + preset, + &share_esm, + "e3-nf-c2b-chunked", + &artifacts_dir, ) - .expect("c4b vk"); - - let c4ab = C4abFoldWitness { - c4a_vk: c4a_vk.verification_key, - c4a_proof: fold_witness_field_strings(&c4a_proof.data).expect("c4a"), - c4a_public: proof_public_fields(&c4a_proof), - c4b_vk: c4b_vk.verification_key, - c4b_proof: fold_witness_field_strings(&c4b_proof.data).expect("c4b"), - c4b_public: proof_public_fields(&c4b_proof), - c4a_key_hash: c4a_vk.key_hash.clone(), - c4b_key_hash: c4b_vk.key_hash.clone(), - }; - let c4ab_json = serde_json::to_value(&c4ab).expect("c4ab json"); - let c4ab_map = fold_witness_input_map(&c4ab_json).expect("c4ab map"); - let c4ab_compiled = CompiledCircuit::from_file( - &prover - .circuits_dir(CircuitVariant::Default, &artifacts_dir) - .join(CircuitName::C4abFold.dir_path()) - .join(format!("{}.json", CircuitName::C4abFold.as_str())), + .expect("chunked C2b proof"); + assert_eq!(c2a_chunked.proof.circuit, CircuitName::SkC2ChunkFinalize); + assert_eq!(c2b_chunked.proof.circuit, CircuitName::ESmC2ChunkFinalize); + let expected_chunk_count = preset.metadata().degree / 512; + assert_eq!(c2a_chunked.chunk_count, expected_chunk_count); + assert_eq!(c2b_chunked.chunk_count, expected_chunk_count); + + let chunked_node = e3_zk_prover::prove_node_dkg_fold( + &prover, + &NodeDkgFoldInput { + c0_proof: &c0_proof, + c1_proof: &c1_proof, + c2a_proof: &c2a_chunked.proof, + c2b_proof: &c2b_chunked.proof, + c3a_inner_proofs: &c3a_inners, + c3b_inner_proofs: &c3b_inners, + c3_slot_indices_a: &slot_indices, + c3_slot_indices_b: &slot_indices, + c3_total_slots: total_slots, + c4a_proof: &c4a_proof, + c4b_proof: &c4b_proof, + party_id: own_party_id as u64, + }, + "e3-nf-node-chunked", + &artifacts_dir, ) - .expect("c4ab compiled"); - let c4ab_witness = WitnessGenerator::new() - .generate_witness(&c4ab_compiled, c4ab_map) - .expect("c4ab witness"); - let c4ab_proof = prover - .generate_recursive_aggregation_bin_proof( - CircuitName::C4abFold, - &c4ab_witness, - "e3-nf-c4ab", + .expect("chunked node fold proof"); + assert!(chunked_node + .step_timings + .iter() + .any(|step| step.step == CircuitName::C2abChunkFold.as_str())); + assert!(prover + .verify_fold_proof( + &chunked_node.proof, + "e3-nf-node-chunked-nodefold", + 0, &artifacts_dir, ) - .expect("c4ab_fold proof"); - - let c0_vk = load_vk_artifacts( - &prover.circuits_dir(CircuitVariant::Recursive, &artifacts_dir), - CircuitName::PkBfv, - ) - .expect("c0 vk"); - let c1_vk = load_vk_artifacts( - &prover.circuits_dir(CircuitVariant::Recursive, &artifacts_dir), - CircuitName::PkGeneration, - ) - .expect("c1 vk"); - let c2ab_fold_vk = load_vk_artifacts( - &prover.circuits_dir(CircuitVariant::Default, &artifacts_dir), - CircuitName::C2abFold, - ) - .expect("c2ab fold vk"); - let c3ab_fold_vk = load_vk_artifacts( - &prover.circuits_dir(CircuitVariant::Default, &artifacts_dir), - CircuitName::C3abFold, - ) - .expect("c3ab fold vk"); - let c4ab_fold_vk = load_vk_artifacts( - &prover.circuits_dir(CircuitVariant::Default, &artifacts_dir), - CircuitName::C4abFold, - ) - .expect("c4ab fold vk"); - - let nf = json!({ - "c0_vk": c0_vk.verification_key, - "c0_proof": fold_witness_field_strings(&c0_proof.data).expect("c0 proof"), - "c0_public": proof_public_fields(&c0_proof), - "c1_vk": c1_vk.verification_key, - "c1_proof": fold_witness_field_strings(&c1_proof.data).expect("c1 proof"), - "c1_public": proof_public_fields(&c1_proof), - "c2ab_vk": c2ab_fold_vk.verification_key, - "c2ab_proof": fold_witness_field_strings(&c2ab_proof.data).expect("c2ab"), - "c2ab_public": proof_public_fields(&c2ab_proof), - "c3ab_vk": c3ab_fold_vk.verification_key, - "c3ab_proof": fold_witness_field_strings(&c3ab_proof.data).expect("c3ab"), - "c3ab_public": proof_public_fields(&c3ab_proof), - "c4ab_vk": c4ab_fold_vk.verification_key, - "c4ab_proof": fold_witness_field_strings(&c4ab_proof.data).expect("c4ab"), - "c4ab_public": proof_public_fields(&c4ab_proof), - "party_id": field_str_zero(), - "c0_key_hash": c0_vk.key_hash, - "c1_key_hash": c1_vk.key_hash, - "c2ab_key_hash": c2ab_fold_vk.key_hash, - "c3ab_key_hash": c3ab_fold_vk.key_hash, - "c4ab_key_hash": c4ab_fold_vk.key_hash, - }); + .expect("verify chunked node_fold")); - let nf_map = fold_witness_input_map(&nf).expect("node_fold map"); - let nf_compiled = CompiledCircuit::from_file(&gate).expect("node_fold compiled"); - let nf_witness = WitnessGenerator::new() - .generate_witness(&nf_compiled, nf_map) - .expect("node_fold witness"); - let nf_proof = prover - .generate_recursive_aggregation_bin_proof( - CircuitName::NodeFold, - &nf_witness, - "e3-nf-node", - &artifacts_dir, - ) - .expect("node_fold proof"); + drop(temp); +} - let ok = prover - .verify_fold_proof(&nf_proof, "e3-nf-node", 0, &artifacts_dir) - .expect("verify node_fold"); - assert!(ok, "node_fold should verify"); +#[tokio::test] +async fn node_fold_correlated_sparse_self_slot_proves_and_verifies() { + run_node_fold_correlated_sparse_self_slot(BfvPreset::InsecureThreshold512).await; +} - drop(temp); +#[tokio::test] +async fn node_fold_correlated_secure_multi_chunk_proves_and_verifies() { + if active_bin_preset().as_deref() != Some("secure-8192") { + println!("skipping: secure-8192 circuit artifacts are not active"); + return; + } + let dkg_circuits = [ + "pk", + "sk_share_computation_chunk", + "esm_share_computation_chunk", + "share_encryption", + "share_decryption", + ]; + let recursive_circuits = [ + CircuitName::C2ChunkBatch, + CircuitName::SkC2ChunkFinalize, + CircuitName::ESmC2ChunkFinalize, + CircuitName::C2abChunkFold, + CircuitName::C3Fold, + CircuitName::C3FoldKernel, + CircuitName::C3abFold, + CircuitName::C4abFold, + CircuitName::NodeFold, + ]; + if dkg_circuits + .iter() + .any(|circuit| !compiled_circuit_artifacts_available("dkg", circuit)) + || !compiled_circuit_artifacts_available("threshold", "pk_generation") + || recursive_circuits + .iter() + .any(|circuit| !recursive_circuit_artifacts_available(*circuit)) + { + println!("skipping: secure-8192 circuit artifacts are not staged"); + return; + } + run_node_fold_correlated_sparse_self_slot(BfvPreset::SecureThreshold8192).await; } diff --git a/docs/pages/cryptography.mdx b/docs/pages/cryptography.mdx index d0b53fa1ef..34167e2d87 100644 --- a/docs/pages/cryptography.mdx +++ b/docs/pages/cryptography.mdx @@ -106,13 +106,13 @@ logs, and the [`circuits/README.md`](https://github.com/theinterfold/interfold/blob/main/circuits/README.md) index. -| Phase | Circuits | What you are looking at | -| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | -| **Config** | [`config`](https://github.com/theinterfold/interfold/tree/main/circuits/bin/config) | One-time consistency of presets before any E3. | -| **P1 — DKG** | [**C0**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/pk), [**C1**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/pk_generation), [**C2**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/sk_share_computation), [**C3**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/share_encryption), [**C4**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/share_decryption) | Individual pk; TrBFV contribution; Shamir + parity (recursive **C2**); encrypt shares; decrypt and aggregate. | -| **P2 — Aggregation** | [**C5**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/pk_aggregation) | Sum honest public key shares into the threshold pk. | -| **P3 — User encryption** | [`user_data_encryption_*`](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/user_data_encryption_ct0) | Valid BFV encryption under the aggregated key. | -| **P4 — Decryption** | [**C6**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/share_decryption), [**C7**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/decrypted_shares_aggregation) | Partial decryptions; Lagrange combination, CRT lift, decode to plaintext. | +| Phase | Circuits | What you are looking at | +| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | +| **Config** | [`config`](https://github.com/theinterfold/interfold/tree/main/circuits/bin/config) | One-time consistency of presets before any E3. | +| **P1 — DKG** | [**C0**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/pk), [**C1**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/pk_generation), [**C2**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/sk_share_computation_chunk), [**C3**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/share_encryption), [**C4**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/share_decryption) | Individual pk; TrBFV contribution; Shamir + parity (chunked recursive **C2**); encrypt shares; decrypt and aggregate. | +| **P2 — Aggregation** | [**C5**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/pk_aggregation) | Sum honest public key shares into the threshold pk. | +| **P3 — User encryption** | [`user_data_encryption_*`](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/user_data_encryption_ct0) | Valid BFV encryption under the aggregated key. | +| **P4 — Decryption** | [**C6**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/share_decryption), [**C7**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/decrypted_shares_aggregation) | Partial decryptions; Lagrange combination, CRT lift, decode to plaintext. | ### Sequence diagram @@ -202,7 +202,7 @@ Individual circuit proofs cover each step in isolation, but the protocol must la a compact, verifiable package. A naive fold of all proofs into one recursive aggregate produces an opaque commitment that the contract cannot interpret without the original public inputs, and if cross-circuit consistency (e.g. that -[**C2**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/sk_share_computation)'s +[**C2**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/sk_share_computation_chunk)'s expected commitments match [**C1**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/pk_generation)'s outputs) is only checked off-chain, end-to-end correctness still depends on trusting the committee @@ -343,22 +343,22 @@ decryption actually rely on. Mixing those two notions is the most common source table below keeps the vocabulary straight. Treat it as a glossary you can return to while reading logs or Noir sources. -| Term | Meaning | -| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Individual key pair** | Per-node BFV keys used **only** to encrypt **Shamir shares** in transit during DKG. [**C0**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/pk) commits the individual public key; [**C3**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/share_encryption) binds encryption to that commitment. | -| **Secret key contribution** | One node’s additive piece of the threshold secret **before** Shamir splitting in the usual DKG story. | -| **Public key share** | The TrBFV public material for that contribution; [**C1**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/pk_generation) proves BFV keygen relations; [**C5**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/pk_aggregation) sums these shares. | -| **Secret key share** | After P1, a node’s **Shamir** share of the threshold secret (not the same as “contribution”). | -| **Threshold public key** | The BFV key users encrypt to after [**C5**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/pk_aggregation); no single party holds the full matching secret. | -| **Smudging noise** | Noise shared across parties so decryption shares do not leak **sk**; **e_sm** in Noir; proved in [**C1**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/pk_generation)–[**C4**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/share_decryption) and used in [**C6**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/share_decryption). | -| **Smudging noise contribution / share** | Per-node piece of smudging material and its Shamir shares, parallel to the secret-key track ([**C2**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/sk_share_computation) / [**C3**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/share_encryption) / [**C4**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/share_decryption) smudging track). | +| Term | Meaning | +| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Individual key pair** | Per-node BFV keys used **only** to encrypt **Shamir shares** in transit during DKG. [**C0**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/pk) commits the individual public key; [**C3**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/share_encryption) binds encryption to that commitment. | +| **Secret key contribution** | One node’s additive piece of the threshold secret **before** Shamir splitting in the usual DKG story. | +| **Public key share** | The TrBFV public material for that contribution; [**C1**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/pk_generation) proves BFV keygen relations; [**C5**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/pk_aggregation) sums these shares. | +| **Secret key share** | After P1, a node’s **Shamir** share of the threshold secret (not the same as “contribution”). | +| **Threshold public key** | The BFV key users encrypt to after [**C5**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/pk_aggregation); no single party holds the full matching secret. | +| **Smudging noise** | Noise shared across parties so decryption shares do not leak **sk**; **e_sm** in Noir; proved in [**C1**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/pk_generation)–[**C4**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/share_decryption) and used in [**C6**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/share_decryption). | +| **Smudging noise contribution / share** | Per-node piece of smudging material and its Shamir shares, parallel to the secret-key track ([**C2**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/esm_share_computation_chunk) / [**C3**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/share_encryption) / [**C4**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/share_decryption) smudging track). | In Noir, [**C1**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/pk_generation) names the BFV encryption-error polynomial **`eek`**. Ring dimension **N**, CRT limbs **L**, plaintext modulus **t**, and threshold **T** are fixed per deployment; the **PARITY_MATRIX** that constrains Shamir structure in -[**C2**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/sk_share_computation) +[**C2**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/sk_share_computation_chunk) ships inside the secure preset that [`config`](https://github.com/theinterfold/interfold/tree/main/circuits/bin/config) checks. @@ -381,7 +381,7 @@ Next, (`threshold/pk_generation`) proves the TrBFV contribution satisfies BFV key generation—bounded coefficients, Fiat–Shamir challenge points, Schwartz–Zippel style checks—and emits commitments that feed both the share pipeline -([**C2**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/sk_share_computation), +([**C2**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/sk_share_computation_chunk), for secret and smudging tracks) and aggregation ([**C5**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/pk_aggregation)). At one Fiat–Shamir challenge point γ (per CRT limb), the TrBFV public key legs are checked in the @@ -397,14 +397,14 @@ shape below (the implementation names the error polynomial **eek**): public-key leg is the CRS polynomial **a**, so **pk1**[\ell] = **a**[\ell] by construction—the circuit’s `verify_evaluations` only evaluates the **pk0** identity at γ.) -[**C2**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/sk_share_computation) -is not a single monolithic proof: the repository uses inner share-computation circuits -`sk_share_computation` and `e_sm_share_computation` to check Shamir-style sharing and Reed–Solomon -parity, and a recursive aggregation wrapper (`recursive_aggregation/c2ab_fold`) to fold the batch -proofs while keeping verifier work small. Intuitively, for each CRT limb and coefficient column, the -shares laid out as a vector **y** must satisfy parity with a fixed matrix **H** so that only -low-degree Shamir-style codewords pass (the exact **PARITY_MATRIX** is preset and checked by -`config`): +[**C2**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/sk_share_computation_chunk) +is not a single monolithic proof: the repository uses chunked share-computation circuits +`sk_share_computation_chunk` and `esm_share_computation_chunk` to check Shamir-style sharing and +Reed–Solomon parity, groups chunks with `c2_chunk_batch`, and verifies them in the type-bound +terminals `sk_c2_chunk_finalize` / `esm_c2_chunk_finalize` before folding with `c2ab_chunk_fold`. +Intuitively, for each CRT limb and coefficient column, the shares laid out as a vector **y** must +satisfy parity with a fixed matrix **H** so that only low-degree Shamir-style codewords pass (the +exact **PARITY_MATRIX** is preset and checked by `config`): ```math \mathbf{H}_j\,\mathbf{y}_{i,j}^{\mathsf{T}} \equiv \mathbf{0} \pmod{q_j} @@ -515,7 +515,7 @@ Across the stack you will see the same few ideas reused in different algebraic c **Schwartz–Zippel** lets the circuits test identities at random points instead of coefficient-by-coefficient. **Reed–Solomon parity** via **PARITY_MATRIX** enforces valid Shamir structure inside -[**C2**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/sk_share_computation) +[**C2**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/dkg/sk_share_computation_chunk) without reconstructing secrets in the clear. **CRT / RNS** arithmetic shows up everywhere in BFV, with explicit reconstruction where the statement needs a single ring element—notably in [**C7**](https://github.com/theinterfold/interfold/tree/main/circuits/bin/threshold/decrypted_shares_aggregation). diff --git a/docs/pages/internals/dkg.mdx b/docs/pages/internals/dkg.mdx index 28b3a0b278..4a17e1580e 100644 --- a/docs/pages/internals/dkg.mdx +++ b/docs/pages/internals/dkg.mdx @@ -43,37 +43,43 @@ full cryptographic specification, see the ## Circuit Map -| ID | Circuit | Phase | Proves | Source → Target | -| ------- | ---------------------------- | ------ | -------------------------------------------------------------------------------------------- | -------------------------- | -| **C0** | `PkBfv` | P1 DKG | Commits to individual BFV public key (binding for C3) | Each node → all peers | -| **C1** | `PkGeneration` | P1 DKG | Public key share correctly derived from secret key contribution (Schwartz-Zippel on BFV eq.) | Each node → all peers | -| **C2a** | `SkShareComputation` | P1 DKG | Secret key Shamir shares consistent with C1 commitment and satisfy Reed-Solomon parity | Each node → all peers | -| **C2b** | `ESmShareComputation` | P1 DKG | Smudging noise Shamir shares consistent with C1 commitment and satisfy Reed-Solomon parity | Each node → all peers | -| **C3a** | `ShareEncryption` (sk) | P1 DKG | BFV encryption of secret key share uses correct share (C2) and correct recipient pk (C0) | Per sender → per recipient | -| **C3b** | `ShareEncryption` (e_sm) | P1 DKG | BFV encryption of smudging noise share uses correct share (C2) and correct recipient pk (C0) | Per sender → per recipient | -| **C4a** | `DkgShareDecryption` (sk) | P1 DKG | Decrypted shares match C2 commitments; aggregated into local secret key share | Each node → all peers | -| **C4b** | `DkgShareDecryption` (e_sm) | P1 DKG | Decrypted shares match C2 commitments; aggregated into local smudging noise share | Each node → all peers | -| **C5** | `PkAggregation` | P2 Agg | Sum of all pk shares (verified against C1 commitments) equals published threshold public key | Aggregator → on-chain | -| **C6** | `ThresholdShareDecryption` | P4 Dec | Partial decryption uses correct secret key share and smudging noise share from C4 | Each node → aggregator | -| **C7** | `DecryptedSharesAggregation` | P4 Dec | Lagrange interpolation + CRT reconstruction + decoding is correct | Aggregator → on-chain | +| ID | Circuit | Phase | Proves | Source → Target | +| ------- | ---------------------------- | ------ | ---------------------------------------------------------------------------------------------------- | -------------------------- | +| **C0** | `PkBfv` | P1 DKG | Commits to individual BFV public key (binding for C3) | Each node → all peers | +| **C1** | `PkGeneration` | P1 DKG | Public key share correctly derived from secret key contribution (Schwartz-Zippel on BFV eq.) | Each node → all peers | +| **C2a** | `SkShareComputationChunk` | P1 DKG | Secret key Shamir shares (chunked) consistent with C1 commitment and satisfy Reed-Solomon parity | Each node → all peers | +| **C2b** | `ESmShareComputationChunk` | P1 DKG | Smudging noise Shamir shares (chunked) consistent with C1 commitment and satisfy Reed-Solomon parity | Each node → all peers | +| **C3a** | `ShareEncryption` (sk) | P1 DKG | BFV encryption of secret key share uses correct share (C2) and correct recipient pk (C0) | Per sender → per recipient | +| **C3b** | `ShareEncryption` (e_sm) | P1 DKG | BFV encryption of smudging noise share uses correct share (C2) and correct recipient pk (C0) | Per sender → per recipient | +| **C4a** | `DkgShareDecryption` (sk) | P1 DKG | Decrypted shares match C2 commitments; aggregated into local secret key share | Each node → all peers | +| **C4b** | `DkgShareDecryption` (e_sm) | P1 DKG | Decrypted shares match C2 commitments; aggregated into local smudging noise share | Each node → all peers | +| **C5** | `PkAggregation` | P2 Agg | Sum of all pk shares (verified against C1 commitments) equals published threshold public key | Aggregator → on-chain | +| **C6** | `ThresholdShareDecryption` | P4 Dec | Partial decryption uses correct secret key share and smudging noise share from C4 | Each node → aggregator | +| **C7** | `DecryptedSharesAggregation` | P4 Dec | Lagrange interpolation + CRT reconstruction + decoding is correct | Aggregator → on-chain | ### Proof Count Per E3 For a committee of **N** nodes with **L** CRT moduli: -| Circuit | Count per node | Total across committee | -| ------- | ------------------- | ---------------------- | -| C0 | 1 | N | -| C1 | 1 | N | -| C2a | 1 | N | -| C2b | 1 | N | -| C3a | (N-1) × L | N × (N-1) × L | -| C3b | (N-1) × L × ESI | N × (N-1) × L × ESI | -| C4a | 1 | N | -| C4b | 1 | N | -| C5 | 1 (aggregator only) | 1 | -| C6 | 1 | N | -| C7 | 1 (aggregator only) | 1 | +| Circuit | Count per node | Total across committee | +| ------- | -------------------------------------------------------------------------------- | ------------------------------------- | +| C0 | 1 | N | +| C1 | 1 | N | +| C2a | `degree/chunk_size` chunks + `chunk_count/chunks_per_batch` batches + 1 finalize | N × (chunk + batch + finalize proofs) | +| C2b | `degree/chunk_size` chunks + `chunk_count/chunks_per_batch` batches + 1 finalize | N × (chunk + batch + finalize proofs) | +| C3a | (N-1) × L | N × (N-1) × L | +| C3b | (N-1) × L × ESI | N × (N-1) × L × ESI | +| C4a | 1 | N | +| C4b | 1 | N | +| C5 | 1 (aggregator only) | 1 | +| C6 | 1 | N | +| C7 | 1 (aggregator only) | 1 | + +C2a/C2b are chunked: each node emits `degree / chunk_size` chunk proofs, folds +`chunk_count / chunks_per_batch` batches, then one finalize proof. With the default +`chunk_size = 512` and `chunks_per_batch = 4`, the insecure preset (`degree = 512`) produces +`1 + 1 + 1 = 3` proofs per node and the secure preset (`degree = 8192`) produces `16 + 4 + 1 = 21` +proofs per node. C3 is by far the most proof-intensive circuit. With N=3 and L=4 (secure params), each node generates 8 C3a proofs alone. This is why proof generation runs on a multithread pool. diff --git a/packages/interfold-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json b/packages/interfold-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json index 37f5d84a48..6c34f81f89 100644 --- a/packages/interfold-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json +++ b/packages/interfold-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json @@ -13,6 +13,32 @@ "name": "ArrayLengthMismatch", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "manager", + "type": "address" + }, + { + "internalType": "uint256", + "name": "e3Assignments", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "openSlashLocks", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pendingRoutes", + "type": "uint256" + } + ], + "name": "AssetConfigurationInUse", + "type": "error" + }, { "inputs": [ { @@ -50,11 +76,70 @@ "name": "BondOwnerTransferViolatesLock", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "uint8", + "name": "expected", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "actual", + "type": "uint8" + } + ], + "name": "BondingAssetDecimalsMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "BondingAssetDecimalsUnavailable", + "type": "error" + }, { "inputs": [], "name": "CiphernodeBanned", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "manager", + "type": "address" + }, + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + } + ], + "name": "E3AssignmentNotFound", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + } + ], + "name": "E3AssignmentNotTerminal", + "type": "error" + }, { "inputs": [ { @@ -87,6 +172,17 @@ "name": "IncompatibleLicenseToken", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "manager", + "type": "address" + } + ], + "name": "IncompatibleSlashingManager", + "type": "error" + }, { "inputs": [], "name": "InsufficientBalance", @@ -113,6 +209,70 @@ "name": "InvalidConfiguration", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "manager", + "type": "address" + }, + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + } + ], + "name": "ManagerHasActiveBans", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "manager", + "type": "address" + }, + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + } + ], + "name": "ManagerHasE3Assignments", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "manager", + "type": "address" + }, + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + } + ], + "name": "ManagerHasOpenSlashLocks", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "manager", + "type": "address" + }, + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + } + ], + "name": "ManagerHasPendingSlashRoutes", + "type": "error" + }, { "inputs": [], "name": "MaxAuthorizedDistributors", @@ -154,6 +314,11 @@ "name": "OnlyRewardDistributor", "type": "error" }, + { + "inputs": [], + "name": "OperatorInActiveCommittee", + "type": "error" + }, { "inputs": [], "name": "OperatorUnderSlash", @@ -185,6 +350,102 @@ "name": "ReservedSlashedFunds", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "manager", + "type": "address" + }, + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "SlashLockAlreadyExists", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "manager", + "type": "address" + }, + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "SlashLockNotFound", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "manager", + "type": "address" + }, + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "SlashReservationAlreadyExists", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "manager", + "type": "address" + }, + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "SlashReservationNotFound", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "manager", + "type": "address" + }, + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + } + ], + "name": "SlashRouteDestinationNotFound", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "manager", + "type": "address" + }, + { + "internalType": "address", + "name": "configuredBondingRegistry", + "type": "address" + } + ], + "name": "SlashingManagerBondingMismatch", + "type": "error" + }, { "inputs": [], "name": "Unauthorized", @@ -244,6 +505,55 @@ "name": "BondOwnerTransferProposed", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract InterfoldTicketToken", + "name": "ticketToken", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IERC20", + "name": "licenseToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "ticketPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "licenseRequiredBond", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "expectedTicketDecimals", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "expectedLicenseDecimals", + "type": "uint8" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "configurationVersion", + "type": "uint64" + } + ], + "name": "BondingAssetConfigUpdated", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -263,6 +573,37 @@ "name": "CiphernodeDeregistrationRequested", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "registry", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "active", + "type": "bool" + } + ], + "name": "CommitteeObligationUpdated", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -363,11 +704,23 @@ { "indexed": true, "internalType": "address", - "name": "licenseToken", + "name": "recipient", "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "expectedAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "actualAmount", + "type": "uint256" } ], - "name": "LicenseTokenSet", + "name": "LicenseTransferShortfall", "type": "event" }, { @@ -376,23 +729,23 @@ { "indexed": true, "internalType": "address", - "name": "recipient", + "name": "slashingManager", "type": "address" }, { - "indexed": false, - "internalType": "uint256", - "name": "expectedAmount", - "type": "uint256" + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" }, { "indexed": false, - "internalType": "uint256", - "name": "actualAmount", - "type": "uint256" + "internalType": "bool", + "name": "banned", + "type": "bool" } ], - "name": "LicenseTransferShortfall", + "name": "ManagerBanUpdated", "type": "event" }, { @@ -427,6 +780,37 @@ "name": "RegistrySet", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "slashingManager", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "refundManager", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ReservedSlashedTicketFundsRouted", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -446,6 +830,81 @@ "name": "RewardDistributorUpdated", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "slashingManager", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "active", + "type": "bool" + } + ], + "name": "SlashLockUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "slashingManager", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + } + ], + "name": "SlashRouteDestinationReleased", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "slashingManager", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "refundManager", + "type": "address" + } + ], + "name": "SlashRouteDestinationSnapshotted", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -484,6 +943,43 @@ "name": "SlashedFundsWithdrawn", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "slashingManager", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "refundManager", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "SlashedTicketFundsReserved", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -541,29 +1037,16 @@ "indexed": false, "internalType": "uint256", "name": "newBalance", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "reason", - "type": "bytes32" - } - ], - "name": "TicketBalanceUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ + "type": "uint256" + }, { "indexed": true, - "internalType": "address", - "name": "ticketToken", - "type": "address" + "internalType": "bytes32", + "name": "reason", + "type": "bytes32" } ], - "name": "TicketTokenSet", + "name": "TicketBalanceUpdated", "type": "event" }, { @@ -685,6 +1168,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "bondingAssetConfigurationVersion", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -708,6 +1204,42 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "manager", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "clearSlashingManagerBan", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "closeSlashLock", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -744,6 +1276,35 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "timepoint", + "type": "uint256" + } + ], + "name": "eligibilityAt", + "outputs": [ + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "activeOperatorCount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "eligibilityConfigurationVersion", @@ -802,6 +1363,40 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "manager", + "type": "address" + }, + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "getSlashedTicketReservation", + "outputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "refundManager", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -830,7 +1425,7 @@ }, { "internalType": "uint256", - "name": "blockNumber", + "name": "timepoint", "type": "uint256" } ], @@ -992,6 +1587,29 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "openSlashLock", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1035,6 +1653,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "manager", + "type": "address" + } + ], + "name": "pendingSlashRouteCount", + "outputs": [ + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -1079,14 +1716,9 @@ }, { "inputs": [ - { - "internalType": "address", - "name": "to", - "type": "address" - }, { "internalType": "uint256", - "name": "amount", + "name": "proposalId", "type": "uint256" } ], @@ -1134,6 +1766,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + } + ], + "name": "releaseSlashRouteDestination", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1154,6 +1799,16 @@ }, { "inputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + }, { "internalType": "uint256", "name": "amount", @@ -1220,12 +1875,44 @@ { "inputs": [ { - "internalType": "uint64", - "name": "newExitDelay", - "type": "uint64" - } - ], - "name": "setExitDelay", + "components": [ + { + "internalType": "address", + "name": "ticketToken", + "type": "address" + }, + { + "internalType": "address", + "name": "licenseToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "ticketPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "licenseRequiredBond", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "expectedTicketDecimals", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "expectedLicenseDecimals", + "type": "uint8" + } + ], + "internalType": "struct IBondingRegistry.BondingAssetConfig", + "name": "config", + "type": "tuple" + } + ], + "name": "setBondingAssetConfig", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -1234,11 +1921,21 @@ "inputs": [ { "internalType": "uint256", - "name": "newBps", + "name": "e3Id", "type": "uint256" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" } ], - "name": "setLicenseActiveBps", + "name": "setCommitteeObligation", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -1246,12 +1943,12 @@ { "inputs": [ { - "internalType": "uint256", - "name": "newLicenseRequiredBond", - "type": "uint256" + "internalType": "uint64", + "name": "newExitDelay", + "type": "uint64" } ], - "name": "setLicenseRequiredBond", + "name": "setExitDelay", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -1259,12 +1956,12 @@ { "inputs": [ { - "internalType": "contract IERC20", - "name": "newLicenseToken", - "type": "address" + "internalType": "uint256", + "name": "newBps", + "type": "uint256" } ], - "name": "setLicenseToken", + "name": "setLicenseActiveBps", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -1285,12 +1982,17 @@ { "inputs": [ { - "internalType": "contract ICiphernodeRegistry", - "name": "newRegistry", + "internalType": "address", + "name": "operator", "type": "address" + }, + { + "internalType": "bool", + "name": "banned", + "type": "bool" } ], - "name": "setRegistry", + "name": "setOperatorBan", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -1298,12 +2000,12 @@ { "inputs": [ { - "internalType": "address", - "name": "newRewardDistributor", + "internalType": "contract ICiphernodeRegistry", + "name": "newRegistry", "type": "address" } ], - "name": "setRewardDistributor", + "name": "setRegistry", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -1312,11 +2014,11 @@ "inputs": [ { "internalType": "address", - "name": "newSlashedFundsTreasury", + "name": "newRewardDistributor", "type": "address" } ], - "name": "setSlashedFundsTreasury", + "name": "setRewardDistributor", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -1325,24 +2027,11 @@ "inputs": [ { "internalType": "address", - "name": "newSlashingManager", + "name": "newSlashedFundsTreasury", "type": "address" } ], - "name": "setSlashingManager", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "newTicketPrice", - "type": "uint256" - } - ], - "name": "setTicketPrice", + "name": "setSlashedFundsTreasury", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -1350,12 +2039,12 @@ { "inputs": [ { - "internalType": "contract InterfoldTicketToken", - "name": "newTicketToken", + "internalType": "address", + "name": "newSlashingManager", "type": "address" } ], - "name": "setTicketToken", + "name": "setSlashingManager", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -1457,6 +2146,29 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "refundManager", + "type": "address" + }, + { + "internalType": "address", + "name": "interfold", + "type": "address" + } + ], + "name": "snapshotSlashRouteDestination", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "sweepLicenseSurplus", @@ -1571,5 +2283,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IBondingRegistry.sol", - "buildInfoId": "solc-0_8_28-cc4ef405e46c0d79d4e49bd03d30b46f575c43f3" + "buildInfoId": "solc-0_8_28-2ef6293a067980a83f37ca7836396a8d11e97afd" } \ No newline at end of file diff --git a/packages/interfold-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json b/packages/interfold-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json index 6e971ee58c..3dc974adf8 100644 --- a/packages/interfold-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json +++ b/packages/interfold-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json @@ -101,11 +101,33 @@ "name": "CommitteeNotRequested", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + } + ], + "name": "CommitteeObligationsAlreadyReleased", + "type": "error" + }, { "inputs": [], "name": "DkgProofRequired", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + } + ], + "name": "E3NotTerminal", + "type": "error" + }, { "inputs": [], "name": "FoldAttestationVerifierAlreadySet", @@ -147,6 +169,22 @@ "name": "InvalidFoldAttestation", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "supplied", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maximum", + "type": "uint256" + } + ], + "name": "InvalidPublicKeyLength", + "type": "error" + }, { "inputs": [], "name": "InvalidTicketNumber", @@ -468,6 +506,37 @@ "name": "CommitteeMemberExpelled", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "nodes", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "pkCommitment", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "proof", + "type": "bytes" + } + ], + "name": "CommitteeProofPublished", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -537,6 +606,12 @@ "internalType": "uint256", "name": "committeeDeadline", "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "ticketPrice", + "type": "uint256" } ], "name": "CommitteeRequested", @@ -1200,11 +1275,6 @@ "name": "e3Id", "type": "uint256" }, - { - "internalType": "bytes", - "name": "publicKey", - "type": "bytes" - }, { "internalType": "bytes32", "name": "pkCommitment", @@ -1226,6 +1296,37 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + } + ], + "name": "publishCommitteePublicKey", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + } + ], + "name": "releaseCommittee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1403,5 +1504,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ICiphernodeRegistry.sol", - "buildInfoId": "solc-0_8_28-cc4ef405e46c0d79d4e49bd03d30b46f575c43f3" + "buildInfoId": "solc-0_8_28-2ef6293a067980a83f37ca7836396a8d11e97afd" } \ No newline at end of file diff --git a/packages/interfold-contracts/artifacts/contracts/interfaces/IInterfold.sol/IInterfold.json b/packages/interfold-contracts/artifacts/contracts/interfaces/IInterfold.sol/IInterfold.json index 57b7714dad..f582472536 100644 --- a/packages/interfold-contracts/artifacts/contracts/interfaces/IInterfold.sol/IInterfold.json +++ b/packages/interfold-contracts/artifacts/contracts/interfaces/IInterfold.sol/IInterfold.json @@ -209,6 +209,38 @@ "name": "FailureConditionNotMet", "type": "error" }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "feeToken", + "type": "address" + }, + { + "internalType": "uint8", + "name": "expected", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "actual", + "type": "uint8" + } + ], + "name": "FeeTokenDecimalsMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "feeToken", + "type": "address" + } + ], + "name": "FeeTokenDecimalsUnavailable", + "type": "error" + }, { "inputs": [ { @@ -440,6 +472,17 @@ "name": "OnlySlashingManager", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "paramSet", + "type": "uint8" + } + ], + "name": "ParamSetAlreadyRegistered", + "type": "error" + }, { "inputs": [ { @@ -483,6 +526,11 @@ "name": "TreasuryRequired", "type": "error" }, + { + "inputs": [], + "name": "UnsupportedCryptoConfig", + "type": "error" + }, { "inputs": [ { @@ -494,6 +542,22 @@ "name": "UtilizationBpsExceedsMax", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "actual", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expected", + "type": "uint256" + } + ], + "name": "VerifierThresholdMismatch", + "type": "error" + }, { "anonymous": false, "inputs": [ @@ -545,6 +609,25 @@ "name": "CiphertextOutputPublished", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "encryptionSchemeId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "contract ICiphertextVerifier", + "name": "ciphertextVerifier", + "type": "address" + } + ], + "name": "CiphertextVerifierSet", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -830,25 +913,114 @@ }, { "indexed": false, - "internalType": "bool", - "name": "allowed", - "type": "bool" + "internalType": "uint8", + "name": "expectedDecimals", + "type": "uint8" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "keyGenFixedPerNode", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "keyGenPerEncryptionProof", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "coordinationPerPair", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "availabilityPerNodePerSec", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "decryptionPerNode", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "publicationBase", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "verificationPerProof", + "type": "uint256" + }, + { + "internalType": "address", + "name": "protocolTreasury", + "type": "address" + }, + { + "internalType": "uint16", + "name": "marginBps", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "protocolShareBps", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "dkgUtilizationBps", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "computeUtilizationBps", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "decryptUtilizationBps", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "minCommitteeSize", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "minThreshold", + "type": "uint32" + } + ], + "indexed": false, + "internalType": "struct IInterfold.PricingConfig", + "name": "pricing", + "type": "tuple" } ], - "name": "FeeTokenAllowed", + "name": "FeeAssetConfigUpdated", "type": "event" }, { "anonymous": false, "inputs": [ { - "indexed": false, - "internalType": "address", - "name": "feeToken", + "indexed": true, + "internalType": "contract IERC20", + "name": "token", "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "allowed", + "type": "bool" } ], - "name": "FeeTokenSet", + "name": "FeeTokenAllowed", "type": "event" }, { @@ -914,31 +1086,6 @@ "name": "ParamSetRegistered", "type": "event" }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "paramSet", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "previousEncodedParams", - "type": "bytes" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "newEncodedParams", - "type": "bytes" - } - ], - "name": "ParamSetUpdated", - "type": "event" - }, { "anonymous": false, "inputs": [ @@ -983,96 +1130,6 @@ "name": "PlaintextOutputPublished", "type": "event" }, - { - "anonymous": false, - "inputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "keyGenFixedPerNode", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "keyGenPerEncryptionProof", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "coordinationPerPair", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "availabilityPerNodePerSec", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "decryptionPerNode", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "publicationBase", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "verificationPerProof", - "type": "uint256" - }, - { - "internalType": "address", - "name": "protocolTreasury", - "type": "address" - }, - { - "internalType": "uint16", - "name": "marginBps", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "protocolShareBps", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "dkgUtilizationBps", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "computeUtilizationBps", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "decryptUtilizationBps", - "type": "uint16" - }, - { - "internalType": "uint32", - "name": "minCommitteeSize", - "type": "uint32" - }, - { - "internalType": "uint32", - "name": "minThreshold", - "type": "uint32" - } - ], - "indexed": false, - "internalType": "struct IInterfold.PricingConfig", - "name": "config", - "type": "tuple" - } - ], - "name": "PricingConfigUpdated", - "type": "event" - }, { "anonymous": false, "inputs": [ @@ -1373,6 +1430,16 @@ "name": "e3Id", "type": "uint256" }, + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + }, { "internalType": "contract IERC20", "name": "token", @@ -1402,6 +1469,38 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "feeTokenDecimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "encryptionSchemeId", + "type": "bytes32" + } + ], + "name": "getCiphertextVerifier", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -2183,6 +2282,24 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "encryptionSchemeId", + "type": "bytes32" + }, + { + "internalType": "contract ICiphertextVerifier", + "name": "ciphertextVerifier", + "type": "address" + } + ], + "name": "setCiphertextVerifier", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -2222,12 +2339,106 @@ { "inputs": [ { - "internalType": "contract IERC20", - "name": "_feeToken", - "type": "address" + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "expectedDecimals", + "type": "uint8" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "keyGenFixedPerNode", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "keyGenPerEncryptionProof", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "coordinationPerPair", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "availabilityPerNodePerSec", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "decryptionPerNode", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "publicationBase", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "verificationPerProof", + "type": "uint256" + }, + { + "internalType": "address", + "name": "protocolTreasury", + "type": "address" + }, + { + "internalType": "uint16", + "name": "marginBps", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "protocolShareBps", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "dkgUtilizationBps", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "computeUtilizationBps", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "decryptUtilizationBps", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "minCommitteeSize", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "minThreshold", + "type": "uint32" + } + ], + "internalType": "struct IInterfold.PricingConfig", + "name": "pricing", + "type": "tuple" + } + ], + "internalType": "struct IInterfold.FeeAssetConfig", + "name": "config", + "type": "tuple" } ], - "name": "setFeeToken", + "name": "setFeeAssetConfig", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -2305,118 +2516,41 @@ "components": [ { "internalType": "uint256", - "name": "keyGenFixedPerNode", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "keyGenPerEncryptionProof", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "coordinationPerPair", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "availabilityPerNodePerSec", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "decryptionPerNode", + "name": "dkgWindow", "type": "uint256" }, { "internalType": "uint256", - "name": "publicationBase", + "name": "computeWindow", "type": "uint256" }, { "internalType": "uint256", - "name": "verificationPerProof", + "name": "decryptionWindow", "type": "uint256" - }, - { - "internalType": "address", - "name": "protocolTreasury", - "type": "address" - }, - { - "internalType": "uint16", - "name": "marginBps", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "protocolShareBps", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "dkgUtilizationBps", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "computeUtilizationBps", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "decryptUtilizationBps", - "type": "uint16" - }, - { - "internalType": "uint32", - "name": "minCommitteeSize", - "type": "uint32" - }, - { - "internalType": "uint32", - "name": "minThreshold", - "type": "uint32" } ], - "internalType": "struct IInterfold.PricingConfig", + "internalType": "struct IInterfold.E3TimeoutConfig", "name": "config", "type": "tuple" } ], - "name": "setPricingConfig", + "name": "setTimeoutConfig", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { - "inputs": [ + "inputs": [], + "name": "slashingManager", + "outputs": [ { - "components": [ - { - "internalType": "uint256", - "name": "dkgWindow", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "computeWindow", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "decryptionWindow", - "type": "uint256" - } - ], - "internalType": "struct IInterfold.E3TimeoutConfig", - "name": "config", - "type": "tuple" + "internalType": "contract ISlashingManager", + "name": "", + "type": "address" } ], - "name": "setTimeoutConfig", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { @@ -2445,5 +2579,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IInterfold.sol", - "buildInfoId": "solc-0_8_28-cc4ef405e46c0d79d4e49bd03d30b46f575c43f3" + "buildInfoId": "solc-0_8_28-2ef6293a067980a83f37ca7836396a8d11e97afd" } \ No newline at end of file diff --git a/packages/interfold-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json b/packages/interfold-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json index b2dc5b1fd2..b30de0463e 100644 --- a/packages/interfold-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json +++ b/packages/interfold-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json @@ -143,6 +143,17 @@ "name": "SignerIsNotOperator", "type": "error" }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "reason", + "type": "bytes32" + } + ], + "name": "SlashPolicyAssetConfigurationMismatch", + "type": "error" + }, { "inputs": [], "name": "SlashReasonDisabled", @@ -326,6 +337,19 @@ "name": "CiphernodeRegistrySet", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + } + ], + "name": "E3DependenciesReleased", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -671,6 +695,19 @@ "name": "SlashedFundsEscrowedToRefund", "type": "event" }, + { + "inputs": [], + "name": "SLASHING_MANAGER_API_VERSION", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -736,6 +773,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + } + ], + "name": "closeE3", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -863,6 +913,11 @@ "internalType": "bool", "name": "pending", "type": "bool" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" } ], "internalType": "struct ISlashingManager.PendingSlashRoute", @@ -1358,6 +1413,30 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "reason", + "type": "bytes32" + } + ], + "name": "slashPolicyAssetContexts", + "outputs": [ + { + "internalType": "address", + "name": "registry", + "type": "address" + }, + { + "internalType": "uint64", + "name": "configurationVersion", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -1432,5 +1511,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ISlashingManager.sol", - "buildInfoId": "solc-0_8_28-cc4ef405e46c0d79d4e49bd03d30b46f575c43f3" + "buildInfoId": "solc-0_8_28-2ef6293a067980a83f37ca7836396a8d11e97afd" } \ No newline at end of file diff --git a/packages/interfold-contracts/contracts/E3RefundManager.sol b/packages/interfold-contracts/contracts/E3RefundManager.sol index 0c12ed0220..93080ff30f 100644 --- a/packages/interfold-contracts/contracts/E3RefundManager.sol +++ b/packages/interfold-contracts/contracts/E3RefundManager.sol @@ -1086,9 +1086,9 @@ contract E3RefundManager is IE3RefundManager, Ownable2StepUpgradeable { OperatorEntitlement storage entitlement = _operatorEntitlements[e3Id][ operator ]; - proposal.holdsBaseReward = !_distributions[e3Id].calculated; + proposal.holdsBaseReward = true; entitlement.pendingExpulsions++; - if (proposal.holdsBaseReward) entitlement.baseRiskExpulsions++; + entitlement.baseRiskExpulsions++; emit ExpulsionProposalStatusChanged( e3Id, proposalId, diff --git a/packages/interfold-contracts/contracts/interfaces/ICiphernodeRegistry.sol b/packages/interfold-contracts/contracts/interfaces/ICiphernodeRegistry.sol index 68a9562ea4..79d5454348 100644 --- a/packages/interfold-contracts/contracts/interfaces/ICiphernodeRegistry.sol +++ b/packages/interfold-contracts/contracts/interfaces/ICiphernodeRegistry.sol @@ -74,6 +74,7 @@ interface ICiphernodeRegistry { /// @param threshold The viability threshold and total member count [H, N]. /// @param requestBlock Block number for snapshot validation. /// @param committeeDeadline Deadline for committee formation (ticket submission). + /// @param ticketPrice Ticket price frozen for this E3 sortition. event CommitteeRequested( uint256 indexed e3Id, uint256 seed, diff --git a/packages/interfold-contracts/contracts/interfaces/IPkVerifier.sol b/packages/interfold-contracts/contracts/interfaces/IPkVerifier.sol index 56d0b80c6e..bc6f99eaf3 100644 --- a/packages/interfold-contracts/contracts/interfaces/IPkVerifier.sol +++ b/packages/interfold-contracts/contracts/interfaces/IPkVerifier.sol @@ -11,7 +11,7 @@ pragma solidity 0.8.28; * @dev The DkgAggregator circuit internally verifies the node-fold and C5 * (pk_aggregation) sub-proofs; this on-chain wrapper verifies the final * EVM proof and enforces: - * - the immutable recursive sub-circuit VK hashes + * - the immutable recursive sub-circuit VK hashes and VK manifest * - the aggregated public-key commitment slot * - a domain-binding slot binding the proof to * (chainId, this, e3Id, committeeRoot, sortedNodes, pkCommitment) @@ -25,8 +25,8 @@ interface IPkVerifier { /// verifier rejected it. Used in place of a `bool false` return. error InvalidProof(); /// @notice `publicInputs` is shorter than the layout the wrapper expects - /// (must hold at least the two VK-hash slots, the domain-binding slot - /// and the pk-commitment slot). + /// (must hold the VK-hash slots, the VK manifest, the domain-binding + /// slots, and the pk-commitment slot). error InvalidPublicInputsLength(); /// @notice One of the recursive-aggregation sub-circuit VK hashes embedded /// in the proof does not match the immutable value committed at diff --git a/packages/interfold-contracts/contracts/lib/InterfoldLifecycle.sol b/packages/interfold-contracts/contracts/lib/InterfoldLifecycle.sol index 739f3f5366..b55c840ad0 100644 --- a/packages/interfold-contracts/contracts/lib/InterfoldLifecycle.sol +++ b/packages/interfold-contracts/contracts/lib/InterfoldLifecycle.sol @@ -27,6 +27,14 @@ library InterfoldLifecycle { // keccak256(abi.encode(uint256(keccak256("interfold.storage.CiphertextVerifier")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant CIPHERTEXT_VERIFIER_STORAGE_SLOT = 0xfc399dd26441dab88259cd69fffcf8b5f96dd87f2db63f29285d86101a4d1500; + // ERC-7201 slot for "interfold.storage.CiphertextPublication". + // keccak256(abi.encode(uint256(keccak256(namespace)) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant CIPHERTEXT_PUBLICATION_STORAGE_SLOT = + 0x8cf8f319e286c91067df5865ff01615e7f3fced906e24ea85acdc2dfd6704200; + + struct CiphertextPublicationLayout { + mapping(uint256 e3Id => bool inProgress) inProgress; + } /// @notice Validates finalization and freezes committee reward recipients. function validateAndSnapshotCommitteeFinalization( @@ -158,14 +166,10 @@ library InterfoldLifecycle { revert IInterfold.InputDeadlineNotReached(e3Id, e3.inputWindow[1]); if (e3.ciphertextOutput != bytes32(0)) revert IInterfold.CiphertextOutputAlreadyPublished(e3Id); + _beginCiphertextPublication(e3Id); _requireViableCommittee(registryAddress, e3Id); bytes32 ciphertextOutputHash = keccak256(ciphertextOutput); - e3.ciphertextOutput = ciphertextOutputHash; - e3.ciphertextCommitment = ciphertextCommitment; - stages[e3Id] = IInterfold.E3Stage.CiphertextReady; - deadlines[e3Id].decryptionDeadline = block.timestamp + decryptionWindow; - _verifyCiphertext( e3, e3Id, @@ -175,6 +179,12 @@ library InterfoldLifecycle { proof ); + _endCiphertextPublication(e3Id); + e3.ciphertextOutput = ciphertextOutputHash; + e3.ciphertextCommitment = ciphertextCommitment; + stages[e3Id] = IInterfold.E3Stage.CiphertextReady; + deadlines[e3Id].decryptionDeadline = block.timestamp + decryptionWindow; + emit IInterfold.CiphertextOutputPublished( e3Id, ciphertextOutput, @@ -276,6 +286,31 @@ library InterfoldLifecycle { } } + function _ciphertextPublicationLayout() + private + pure + returns (CiphertextPublicationLayout storage state) + { + bytes32 slot = CIPHERTEXT_PUBLICATION_STORAGE_SLOT; + assembly { + state.slot := slot + } + } + + function _beginCiphertextPublication(uint256 e3Id) private { + CiphertextPublicationLayout + storage publication = _ciphertextPublicationLayout(); + require( + !publication.inProgress[e3Id], + "Ciphertext publication is already in progress" + ); + publication.inProgress[e3Id] = true; + } + + function _endCiphertextPublication(uint256 e3Id) private { + _ciphertextPublicationLayout().inProgress[e3Id] = false; + } + /// @notice Checks whether an E3 stage can enter the failure path. /// @param current The current E3 stage, encoded as `uint8`. function validateMarkFailedStage( diff --git a/packages/interfold-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol b/packages/interfold-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol index 0ca8d54e9f..53baf2231f 100644 --- a/packages/interfold-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol +++ b/packages/interfold-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol @@ -183,10 +183,10 @@ contract DkgFoldAttestationVerifier is IDkgFoldAttestationVerifier { bytes32[] memory publicInputs ) private pure returns (uint256 h) { require( - publicInputs.length >= 6 && (publicInputs.length - 6) % 3 == 0, + publicInputs.length >= 27 && (publicInputs.length - 24) % 3 == 0, ICiphernodeRegistry.InvalidFoldAttestation() ); - h = (publicInputs.length - 6) / 3; + h = (publicInputs.length - 24) / 3; // Defense in depth: the BFV pk-verifier already rejects `h == 0`, but // a zero-honest-party proof would otherwise pass this verifier with no // attestations to check and write empty anchors to the registry. @@ -243,8 +243,8 @@ contract DkgFoldAttestationVerifier is IDkgFoldAttestationVerifier { ); uint256 partyIdOffset = 2; - uint256 skOffset = 5 + data.h; - uint256 esmOffset = 5 + (2 * data.h); + uint256 skOffset = 23 + data.h; + uint256 esmOffset = 23 + (2 * data.h); slot = _partySlot( data.publicInputs, diff --git a/packages/interfold-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol b/packages/interfold-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol index 5f14b93012..8ce42ba9fd 100644 --- a/packages/interfold-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol +++ b/packages/interfold-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol @@ -24,13 +24,17 @@ import { CommitteeHashLib } from "../../lib/CommitteeHashLib.sol"; * [2 .. 2+H) = party_ids (H slots) * [2+H] = committee_hash_hi * [3+H] = committee_hash_lo - * [4+H .. 4+3H) = expected_pk (2*H slots) - * [4+3H] = pk_commitment - * Total: expectedPublicInputsLen = 3*H + 6. + * [4+H .. 20+H) = recursive VK binding manifest (16 fields) + * [20+H] = DKG return VK hash + * [21+H] = expected SK C2 chunk VK hash + * [22+H] = expected ESM C2 chunk VK hash + * [23+H .. 23+3H) = expected SK/ESM anchors (2*H slots) + * [23+3H] = pk_commitment + * Total: expectedPublicInputsLen = 3*H + 24. * - * The two VK-hash slots are checked against contract immutables set at - * construction; this anchors the recursive aggregation trust and - * prevents a malicious aggregator from substituting a forged sub-VK. + * The direct VK slots and the recursive VK binding manifest are checked + * against deployment-time values. This anchors the complete recursive + * aggregation trust chain. * * NOTE — domain binding relaxation: wrapper-level chainId/deployment/e3Id * binding requires a dedicated circuit public input. The current circuits @@ -60,6 +64,15 @@ contract BfvPkVerifier is IPkVerifier { /// @dev Index of `pkCommitment` (last return field). uint256 internal immutable pkCommitmentIdx; + /// @notice Canonical SK C2 chunk VK hash. + bytes32 public immutable expectedSkC2ChunkKeyHash; + + /// @notice Canonical ESM C2 chunk VK hash. + bytes32 public immutable expectedESmC2ChunkKeyHash; + + /// @notice Canonical recursive VK hashes used by the DKG proof pipeline. + bytes32[16] public expectedVkBinding; + /// @notice Underlying Honk verifier for the DkgAggregator circuit. ICircuitVerifier public immutable circuitVerifier; @@ -78,6 +91,9 @@ contract BfvPkVerifier is IPkVerifier { address _circuitVerifier, bytes32 _expectedNodesFoldKeyHash, bytes32 _expectedC5KeyHash, + bytes32 _expectedSkC2ChunkKeyHash, + bytes32 _expectedESmC2ChunkKeyHash, + bytes32[16] memory _expectedVkBinding, uint256 _h ) { require(_h > 0, "BfvPkVerifier: h=0"); @@ -86,17 +102,28 @@ contract BfvPkVerifier is IPkVerifier { } if ( _expectedNodesFoldKeyHash == bytes32(0) || - _expectedC5KeyHash == bytes32(0) + _expectedC5KeyHash == bytes32(0) || + _expectedSkC2ChunkKeyHash == bytes32(0) || + _expectedESmC2ChunkKeyHash == bytes32(0) ) revert InvalidVerificationKeyHash(); + for (uint256 i = 0; i < _expectedVkBinding.length; i++) { + if (_expectedVkBinding[i] == bytes32(0)) { + revert InvalidVerificationKeyHash(); + } + expectedVkBinding[i] = _expectedVkBinding[i]; + } h = _h; committeeHashHiIdx = 2 + _h; committeeHashLoIdx = 3 + _h; - expectedPublicInputsLen = (3 * _h) + 6; + // The generated Honk VK includes eight pairing-point slots outside the public-input array. + expectedPublicInputsLen = (3 * _h) + 24; pkCommitmentIdx = expectedPublicInputsLen - 1; circuitVerifier = ICircuitVerifier(_circuitVerifier); expectedNodesFoldKeyHash = _expectedNodesFoldKeyHash; expectedC5KeyHash = _expectedC5KeyHash; + expectedSkC2ChunkKeyHash = _expectedSkC2ChunkKeyHash; + expectedESmC2ChunkKeyHash = _expectedESmC2ChunkKeyHash; } /// @inheritdoc IPkVerifier @@ -124,6 +151,17 @@ contract BfvPkVerifier is IPkVerifier { if (publicInputs[1] != expectedC5KeyHash) { revert VkHashMismatch(); } + for (uint256 i = 0; i < expectedVkBinding.length; i++) { + if (publicInputs[4 + h + i] != expectedVkBinding[i]) { + revert VkHashMismatch(); + } + } + if (publicInputs[21 + h] != expectedSkC2ChunkKeyHash) { + revert VkHashMismatch(); + } + if (publicInputs[22 + h] != expectedESmC2ChunkKeyHash) { + revert VkHashMismatch(); + } // Bind to the on-chain committee hash (hi/lo split per Noir field convention). if ( diff --git a/packages/interfold-contracts/contracts/verifiers/bfv/Risc0BfvCiphertextVerifier.sol b/packages/interfold-contracts/contracts/verifiers/bfv/Risc0BfvCiphertextVerifier.sol index 6ab8a915d9..154ef1558d 100644 --- a/packages/interfold-contracts/contracts/verifiers/bfv/Risc0BfvCiphertextVerifier.sol +++ b/packages/interfold-contracts/contracts/verifiers/bfv/Risc0BfvCiphertextVerifier.sol @@ -28,7 +28,10 @@ contract Risc0BfvCiphertextVerifier is ICiphertextVerifier { bytes32 public immutable imageId; constructor(IRisc0Verifier verifier, bytes32 guestImageId) { - if (address(verifier) == address(0)) revert InvalidVerifier(); + if ( + address(verifier) == address(0) || + address(verifier).code.length == 0 + ) revert InvalidVerifier(); if (guestImageId == bytes32(0)) revert InvalidImageId(); risc0Verifier = verifier; imageId = guestImageId; diff --git a/packages/interfold-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol b/packages/interfold-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol index e95de4b5b4..0584f11447 100644 --- a/packages/interfold-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol +++ b/packages/interfold-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol @@ -7,8 +7,8 @@ pragma solidity >=0.8.21; uint256 constant N = 2097152; uint256 constant LOG_N = 21; -uint256 constant NUMBER_OF_PUBLIC_INPUTS = 20; -uint256 constant VK_HASH = 0x2789163aa48d76e693150c11399d50b5a0bdef0fd952e018a9be9f3754667f6c; +uint256 constant NUMBER_OF_PUBLIC_INPUTS = 38; +uint256 constant VK_HASH = 0x2d2b7a67b3be3a1684127ff35cb64495a97b386fe50160542ac214a0f249da0a; library HonkVerificationKey { function loadVerificationKey() internal @@ -18,77 +18,77 @@ library HonkVerificationKey { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), - publicInputsSize: uint256(20), + publicInputsSize: uint256(38), ql: Honk.G1Point({ x: uint256( - 0x285f60ef240cb7e12792be41e97fa20509c8865724b347057365432e79e675d4 + 0x289502c7491c555fe9eb5f5186a610987b27cbcafdc71c7f819a19a1c0a90c0d ), y: uint256( - 0x1327b3ec6a8e40ab3a999f5562c816991d2308580f391ddc37165c316ab165e0 + 0x0b85fc29192a8821da6a69744d32c33b7b7da68a7d0085e18d6d3a03b43d22b0 ) }), qr: Honk.G1Point({ x: uint256( - 0x18dde938a2831c736ea7dd71878bb45a4341c8b418628426df5c08975f0cea9c + 0x17b56a141ce218bd8d6c0a6933147e460439f5ed65faf84651ec82533b09a7b9 ), y: uint256( - 0x01d9e85cb89a13ef09fa6c5822e667edd9f5b7e332aefafc7c57a1bff4ada397 + 0x2fc54db9775b5980eeb9c19cf6a67898290df6fed16fe0355e70a55bed8a6156 ) }), qo: Honk.G1Point({ x: uint256( - 0x042b927009b9938e7246e2b43b7bb4c96d7333f8c6f7b50b3fcc44d9246b8309 + 0x234afd34c4f18be170b275451a168b3c684bb3ac8df6140c786aba665bd18109 ), y: uint256( - 0x262eee8deb45b8a6b0bfc5258a9ac477b5f21b32a17def775db31ef0cf0a88c3 + 0x28f2169db063aa657b3cadfdca566f1d7fc4a11d8129b5747c2a877d05959a33 ) }), q4: Honk.G1Point({ x: uint256( - 0x04ed7f8d55c4ded3006a4fe6541ddfafe13da0fea4c0238b6d7004c5afd30aca + 0x1ef16ea660decdd4f31ed23d1982fb1e91ef31333628ed97ee688d677ece9911 ), y: uint256( - 0x05a4bf637825561b2efbf25612a176154936550f3385c8252e2bd784bb00fba5 + 0x24d5d7ff7d67191664b9b2c7cc12a9a521ee02a9a6131f7e5448820b16981309 ) }), qm: Honk.G1Point({ x: uint256( - 0x19ef2acf13da7fd47b8c47f0b4b18ebfbabb8589676a8d954e581138f9f2a53b + 0x2f163b097ded63a3ca50cbbf22b11b088251bee823ca65bfaa75a0ca812f584b ), y: uint256( - 0x0162740a49c1e8527a7f091c8d6139bf63c3e6c648f97ada0ab51d7ee6f2fa6c + 0x13eab09311e5648a2fc3ffc33995d48861b170afdb9f7b5dc8b9a438736163e1 ) }), qc: Honk.G1Point({ x: uint256( - 0x27d638a6724892e24c7a262fd802f74c97007052be60bea711615c7ee3f51bec + 0x1ac85db32dabf142187223dfc80a07334f2e753f4c295f0d4a0f30d4be7e7040 ), y: uint256( - 0x2f5e46212275fedb8bc69a6c59030e60b165efdc8e994ccbe9437f040bf332c7 + 0x17cfadf390a1f0f25f1588c8b9ea5d2680c1682675f19ea01d40483fedaac15d ) }), qLookup: Honk.G1Point({ x: uint256( - 0x07bae8da7d5838ebf83a5ce0bd78ac81464174b8163d7eeaf9d0f3e7a0ea6f0d + 0x09ae66bef2d0ad77a28f9a475135d083aab02eefbc32edc5bb697cf92400c48c ), y: uint256( - 0x055094c0ec8060afb828a7c57aa0e83e6c69010c3a2961ef7fbc95da7d926687 + 0x2641833fdb404a23873dbd1dbeaa222b88e748c8c8ff780d945e7fd9a4870e48 ) }), qArith: Honk.G1Point({ x: uint256( - 0x158613882a583691608ce4ff09498e5a19c3f7eb8e2c609d9a8b7f647e06b954 + 0x0ad5a5404184ab5c23982322c7248564bf334243bd232904c12ac9d587d6bb9d ), y: uint256( - 0x0afc25dd3444ad64743ba4f67cea61a32a3534854504046e75565b676805b9ad + 0x287d3e93a922ad414a7fbab272043c438b6d4cc3a90db825b6195ad38efa4024 ) }), qDeltaRange: Honk.G1Point({ x: uint256( - 0x029aa590a162ddac4cc8e62211c44d3b569cfdc0bed9599dcb56b1e8ba53e34b + 0x0c8bdb723739d56aa773dc4cff9917e9846db953663334b54c3200f0aa3f78c8 ), y: uint256( - 0x1315239ef481dda03aea19bf410a3e5a01395efe509f7c0b4254974dd2366837 + 0x1b54d9b0f614bf2c0f9efc8905d6d0941de9e7d9428e1d9a61bd845ff7368170 ) }), qElliptic: Honk.G1Point({ @@ -101,130 +101,130 @@ library HonkVerificationKey { }), qMemory: Honk.G1Point({ x: uint256( - 0x0ebceb992522f50e7468cf818d4930012117477c0db24e9f195f34cdef85cb7d + 0x0f2a53a3b3468b16b6b565b23d7af9ecf477dd211bb150d5187428a6ded1af19 ), y: uint256( - 0x0e29ff1932a1c13e4433395cbdee9364ec1bf3aabb1a4af09e580fbd81150a7b + 0x2aa7751917503f40fdd2ba7222405bcdc46568132cff31185dcd9bb6527b592e ) }), qNnf: Honk.G1Point({ x: uint256( - 0x25ebbe8a6954541e9352ad6f0db4cc83dbae54da3e80a331f7f01892b9bfc679 + 0x292ec4c72dc9121c7cab55b63db73ad8457fdb32a9ee1ce87b51071af694bc41 ), y: uint256( - 0x104498f872357944027385e9d246513bf48be735ad0e374440f467da6ec5bd86 + 0x2a3fc755073235e91da005cc09a1b898f8fb7260c446d50162d02b665b4c4bd7 ) }), qPoseidon2External: Honk.G1Point({ x: uint256( - 0x0d964272d238277e204747251791bcdce19c914ddda57fdec0d7bd09ffb6553b + 0x1bb1cf93b27560bd3147bbb54724c08a1aeede6311c8e391f664861ad9322666 ), y: uint256( - 0x2c9ebf0dbb1aaf2ae2a408aa737218e398754439185baf3fc3b93d8629a376e8 + 0x19439d6d87f40af6b24c3bc04cef40c2e602124521c1ecf50a77dbda6c3b7a37 ) }), qPoseidon2Internal: Honk.G1Point({ x: uint256( - 0x1cda500a1fb81a241849fff12a60f1c15a9a2ccccf56aa2d4bf9b8c37fcb1836 + 0x1652c9f80d7dd47803de9de4ae711b0f104065c614f55f944b55d8aa7ee36b7d ), y: uint256( - 0x17d0acbc2a8ec176ba6052d5e4bd42c613dfc92687d46a0a3dbe7575aa73f483 + 0x2ce580a48a76fa657e1f052a37863509cf669a9e3b20b48067cd9a777876325b ) }), s1: Honk.G1Point({ x: uint256( - 0x04cf30f7dc6988df84552275f7763e9072349bf4f204d49fc761ac1577c84a14 + 0x26115193d649d79027a1cbbb856174f8e868ad13fc7f7ec86f5b1366a01fb022 ), y: uint256( - 0x11d34b9b8d9962cdea1a02ff1066302fe148649f3af5b0d7c3a375e499f8345a + 0x29696a447ccf2a8002115a388d39de35de226c26ecf88158d7fe1ecc3669866b ) }), s2: Honk.G1Point({ x: uint256( - 0x145367228334edf5a6258a4ccc8e071eb356abb07fc8efa8fc1f1e653598d72e + 0x0ba08167e24a6b68aa05b147c0759aeb06dd57d843f69479c5b2b21fab868e03 ), y: uint256( - 0x2630c5490d3a33896e9a0cd34146ff76dd26e866bcac668f82eb130fea974d98 + 0x043685f425518969ab5939b2677f8df80762e2a8db70b31c1b3b85ac00b6b99d ) }), s3: Honk.G1Point({ x: uint256( - 0x19a0bc3fd46f0245635784800da9392260ecedf09bf2cf9342f404259da64581 + 0x06b47b432b15c4fce781eb891641026600d2b9a1d8346f2264997d327d1b31e7 ), y: uint256( - 0x0ad9d4aa5f1fc5c81c6d2d1130b0bcd684a889ccd49c13b36a19583820e84553 + 0x0f15337c1196d7f49641f2eda40cc51c69741457596b8e5d2483604b2dfcac00 ) }), s4: Honk.G1Point({ x: uint256( - 0x1391e65cc22811e784ba4e87ab249fa444eb42b7612a31c220e7fc1eb297730d + 0x1385e074ef7df9d854a159371f628920f3d1771bf7f0fe799923b682df08429d ), y: uint256( - 0x132df8ea210dda11ca1f97244620f5a5ddc243a69e16869473dc776185332b42 + 0x2e51fa14415fdb62dba01b6f88d6bd27f0199a604821e6e0542c926d7fb1b9c4 ) }), t1: Honk.G1Point({ x: uint256( - 0x1e97dc4796dae38598049f73a02e7c33d2c059852c1af19c9f0ee3312df4d41f + 0x23e3dc399cfb420ca1d4c5d1647660e33a85abed22b19a35a3e9bc1ac453671f ), y: uint256( - 0x1836391c7d6f155fa5590e68e400846add58e66d7a8221d801a8550b648f5aa5 + 0x182cc6d1ef5e4db7dd3d7ffacc94dda14e22cde6a65817d29bec9cd5477c3d05 ) }), t2: Honk.G1Point({ x: uint256( - 0x27d11740d6fa02881bde033f583973b6d612009b751197f76934d08a4737c442 + 0x1cf491137dd2886b0aa1f52e1c33b1a42e137942ec903272fee32835ec2af9b4 ), y: uint256( - 0x2ba0de31ae49034e4275f72290f88801444ae2d8375791da6e327c83e22d854d + 0x01aa859ed86b51dd160af31231f485de835f15c502df5e4b65af46eb034be89f ) }), t3: Honk.G1Point({ x: uint256( - 0x1c073330c1151fdb3ddfba1578362fcb65485225f8c219d459db039890724a8e + 0x22a78d6fd55592cbaad2152296b9cb77a6de8ab2c5ade4a32961129682f5f852 ), y: uint256( - 0x20362388f013a2def8eb9e2629526a010e67cf43ddcb0b2ef0cd3520e0ce06cb + 0x29d63f029f9f6d0a8ea7f2e01d4e90c63ae63d06d202538cab4ca55c695934a7 ) }), t4: Honk.G1Point({ x: uint256( - 0x008d92aef0b2d4a3e1f9346ab724d6e847b0a21e1e3b044702acb005115f81b5 + 0x1db64843e94d5b8f623d28b39f49c0226ce8d47ffecdd01377980b4ce7c52dba ), y: uint256( - 0x07520001001210440d025b116e975fd669f25df48d45f0340cf0590ff394e56b + 0x04a0bebb79e4324efa91aa9373f756a80b643beddd20ae24bdd74d2ce4c2336c ) }), id1: Honk.G1Point({ x: uint256( - 0x0e294cc16c7e15ebf626a0124e962f51c2510761516acd3d186e2749599d5a74 + 0x14492b310cf8109ca9c22596133868e549654a36638f96e6e03b2e687c1b16e3 ), y: uint256( - 0x289f78dacc556b3a1cfaf03fb071ef5ba9dd19c208fe683de13e735d99eca2c0 + 0x002e1213e20217fe6d7c3264c85d5428ec70713ed6a9f5efea76f8db92e98c93 ) }), id2: Honk.G1Point({ x: uint256( - 0x26630f42089f38ab8a99afb9406b4bf0991a87c24046c2a210620359c50bdabe + 0x2213e9d455b4f42b142e97d131eb8781c885ccb59b73313ede2436fd48496c36 ), y: uint256( - 0x26fd280618dddf1986d39c95f092d12b5f0f2c7442ab0deb93c826176c08f506 + 0x12e460d4e9d9e70913333252bf2bfebfe5f41d9504aefd74498100a76361460e ) }), id3: Honk.G1Point({ x: uint256( - 0x088a9428290ed27e89d62a21c013d807595a277fbb3bf160deb0af821560e3fe + 0x04ad960ca85f72f493487e55986e781623e86fb237b3cb95aa64f58773bac338 ), y: uint256( - 0x15d236cd054eec32c1b50a417fadce802651b6ec68b05e2843b105cd460f5c3d + 0x011e01d560aeebe271651c2bfd6bc3f053b3a970d2e81a855599f2419788fd96 ) }), id4: Honk.G1Point({ x: uint256( - 0x04782ce47eb669358f53ddd3173b1e42880699e40548c86061e0af3d80c276ff + 0x108853c937c54cfc3112860b80346c8f0f6c17939d8c9e3a18868359c23108ae ), y: uint256( - 0x0ce237c07a9fb708e0cc7021e06c7efc4ae681331612c4d002ae3219c637116e + 0x1d22b066661d2a588b8649aca923a5831e5ddca8c65542cb2ddb230528cc5938 ) }), lagrangeFirst: Honk.G1Point({ @@ -237,10 +237,10 @@ library HonkVerificationKey { }), lagrangeLast: Honk.G1Point({ x: uint256( - 0x07eeed453907945238abb27c9b710b9eb2ce9524a7632e9018f45209d948f613 + 0x16492a83885014ce7c93e8083eba49869724ef8b5a068b9fc6a7e61b79389495 ), y: uint256( - 0x196773b40628ecf1e743865717afcb184f81ad00d02024c4f39ddc95d9d509c0 + 0x2f58a57306d7e3d273c17d92b1a92343049de81cf1cc67eeb0ed51715b86e787 ) }) }); diff --git a/packages/interfold-contracts/ignition/modules/bfvPkVerifier.ts b/packages/interfold-contracts/ignition/modules/bfvPkVerifier.ts index 4ff8f6c082..e5c5df0d3b 100644 --- a/packages/interfold-contracts/ignition/modules/bfvPkVerifier.ts +++ b/packages/interfold-contracts/ignition/modules/bfvPkVerifier.ts @@ -8,6 +8,7 @@ import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; import { BFV_DKG_H, getBfvPkSubCircuitVkHashPaths, + getBfvPkVkBindingHashPaths, readVkRecursiveHash, } from "../../scripts/utils"; import dkgAggregatorVerifierModule from "./dkgAggregatorVerifier"; @@ -19,11 +20,21 @@ export default buildModule("BfvPkVerifier", (m) => { getBfvPkSubCircuitVkHashPaths().nodesFold, ); const c5KeyHash = readVkRecursiveHash(getBfvPkSubCircuitVkHashPaths().c5); + const skC2ChunkKeyHash = readVkRecursiveHash( + getBfvPkSubCircuitVkHashPaths().skC2Chunk, + ); + const esmC2ChunkKeyHash = readVkRecursiveHash( + getBfvPkSubCircuitVkHashPaths().esmC2Chunk, + ); + const vkBinding = getBfvPkVkBindingHashPaths().map(readVkRecursiveHash); const bfvPkVerifier = m.contract("BfvPkVerifier", [ dkgAggregatorVerifier, nodesFoldKeyHash, c5KeyHash, + skC2ChunkKeyHash, + esmC2ChunkKeyHash, + vkBinding, BFV_DKG_H, ]); diff --git a/packages/interfold-contracts/scripts/benchmarkGasFromRaw.ts b/packages/interfold-contracts/scripts/benchmarkGasFromRaw.ts index ae33115762..49d9b716b0 100644 --- a/packages/interfold-contracts/scripts/benchmarkGasFromRaw.ts +++ b/packages/interfold-contracts/scripts/benchmarkGasFromRaw.ts @@ -19,6 +19,7 @@ import { committeeHashFromLimbs, getBfvDecryptionSubCircuitVkHashPaths, getBfvPkSubCircuitVkHashPaths, + getBfvPkVkBindingHashPaths, getRepoRoot, readVkRecursiveHash, } from "./utils"; @@ -310,6 +311,14 @@ async function main() { const expectedC5KeyHash = readVkRecursiveHash( getBfvPkSubCircuitVkHashPaths().c5, ); + const expectedSkC2ChunkKeyHash = readVkRecursiveHash( + getBfvPkSubCircuitVkHashPaths().skC2Chunk, + ); + const expectedESmC2ChunkKeyHash = readVkRecursiveHash( + getBfvPkSubCircuitVkHashPaths().esmC2Chunk, + ); + const expectedVkBinding = + getBfvPkVkBindingHashPaths().map(readVkRecursiveHash); const expectedC6FoldKeyHash = readVkRecursiveHash( getBfvDecryptionSubCircuitVkHashPaths().c6Fold, ); @@ -319,10 +328,15 @@ async function main() { if ( dkgPublicInputs[0] !== expectedNodesFoldKeyHash || - dkgPublicInputs[1] !== expectedC5KeyHash + dkgPublicInputs[1] !== expectedC5KeyHash || + dkgPublicInputs[21 + BFV_DKG_H] !== expectedSkC2ChunkKeyHash || + dkgPublicInputs[22 + BFV_DKG_H] !== expectedESmC2ChunkKeyHash || + expectedVkBinding.some( + (value, index) => dkgPublicInputs[4 + BFV_DKG_H + index] !== value, + ) ) { throw new Error( - "DKG aggregator proof publicInputs[0..1] do not match nodes_fold / pk_aggregation .vk_recursive_hash artifacts", + "DKG aggregator proof public inputs do not match nodes_fold, pk_aggregation, sk_share_computation_chunk, or esm_share_computation_chunk .vk_recursive_hash artifacts", ); } if ( @@ -361,6 +375,9 @@ async function main() { dkgAggAddress, expectedNodesFoldKeyHash, expectedC5KeyHash, + expectedSkC2ChunkKeyHash, + expectedESmC2ChunkKeyHash, + expectedVkBinding, BFV_DKG_H, ); await bfvPk.waitForDeployment(); diff --git a/packages/interfold-contracts/scripts/deployAndSave/bfvPkVerifier.ts b/packages/interfold-contracts/scripts/deployAndSave/bfvPkVerifier.ts index 8b81ac8f6f..35e6556b3b 100644 --- a/packages/interfold-contracts/scripts/deployAndSave/bfvPkVerifier.ts +++ b/packages/interfold-contracts/scripts/deployAndSave/bfvPkVerifier.ts @@ -13,6 +13,7 @@ import { BFV_DKG_H, assertBfvPkVerifierSubCircuitVkHashes, getBfvPkSubCircuitVkHashPaths, + getBfvPkVkBindingHashPaths, readDeploymentArgs, readVkRecursiveHash, storeDeploymentArgs, @@ -55,10 +56,18 @@ export const deployAndSaveBfvPkVerifier = async ( "Redeploy after the circuit verifier changes.", ); } - await assertBfvPkVerifierSubCircuitVkHashes( - bfvPkVerifier, - existing.address, - ); + try { + await assertBfvPkVerifierSubCircuitVkHashes( + bfvPkVerifier, + existing.address, + ); + } catch (error) { + throw new Error( + `BfvPkVerifier at ${existing.address} is incompatible with the current VK-anchor ABI. ` + + "Redeploy the verifier before reuse.", + { cause: error }, + ); + } return { bfvPkVerifier }; } @@ -68,12 +77,23 @@ export const deployAndSaveBfvPkVerifier = async ( const expectedC5KeyHash = readVkRecursiveHash( getBfvPkSubCircuitVkHashPaths().c5, ); + const expectedSkC2ChunkKeyHash = readVkRecursiveHash( + getBfvPkSubCircuitVkHashPaths().skC2Chunk, + ); + const expectedESmC2ChunkKeyHash = readVkRecursiveHash( + getBfvPkSubCircuitVkHashPaths().esmC2Chunk, + ); + const expectedVkBinding = + getBfvPkVkBindingHashPaths().map(readVkRecursiveHash); const bfvPkVerifierFactory = await ethers.getContractFactory("BfvPkVerifier"); const bfvPkVerifier = await bfvPkVerifierFactory.deploy( circuitVerifierArgs.address, expectedNodesFoldKeyHash, expectedC5KeyHash, + expectedSkC2ChunkKeyHash, + expectedESmC2ChunkKeyHash, + expectedVkBinding, BFV_DKG_H, ); diff --git a/packages/interfold-contracts/scripts/utils.ts b/packages/interfold-contracts/scripts/utils.ts index ba37000d11..c8506ee87a 100644 --- a/packages/interfold-contracts/scripts/utils.ts +++ b/packages/interfold-contracts/scripts/utils.ts @@ -87,7 +87,7 @@ export function getRepoRoot(): string { /** * - * Default insecure-512 / minimum committee layout for BFV aggregator verifiers. + * Active insecure-512 / minimum committee layout for BFV aggregator verifiers. * Must match `lib::configs::default::{H, T}` in compiled circuits. * Minimum committee: N=3, T=1, H=2. * @@ -100,7 +100,10 @@ export const ACTIVE_BFV_COMMITTEE_N = 3; /** `dkg_aggregator` EVM public-input count for honest-set size `h`. */ export function bfvPkExpectedPublicInputsLen(h: number): number { - return 3 * h + 6; + // dkg_aggregator public inputs: nodes_fold + c5 key hashes (2), party_ids (h), + // committee hash limbs (2), vk_binding (16), returned key hash (1), + // C2A/C2B chunk hashes (2), sk/esm agg commits (2h), aggregated pk commit (1). + return 3 * h + 24; } /** `publicInputs` indices for `committee_hash_hi` / `committee_hash_lo` (matches `BfvPkVerifier`). */ @@ -159,9 +162,85 @@ export function getBfvPkSubCircuitVkHashPaths() { root, "circuits/bin/threshold/target/pk_aggregation.vk_recursive_hash", ), + skC2Chunk: path.join( + root, + "circuits/bin/dkg/target/sk_share_computation_chunk.vk_recursive_hash", + ), + esmC2Chunk: path.join( + root, + "circuits/bin/dkg/target/esm_share_computation_chunk.vk_recursive_hash", + ), } as const; } +/** Recursive VK hashes used to build the DKG pipeline binding manifest. */ +export function getBfvPkVkBindingHashPaths() { + const root = getRepoRoot(); + return [ + path.join( + root, + "circuits/bin/recursive_aggregation/node_fold/target/node_fold.vk_recursive_hash", + ), + path.join(root, "circuits/bin/dkg/target/pk.vk_recursive_hash"), + path.join( + root, + "circuits/bin/threshold/target/pk_generation.vk_recursive_hash", + ), + path.join( + root, + "circuits/bin/recursive_aggregation/c2ab_chunk_fold/target/c2ab_chunk_fold.vk_recursive_hash", + ), + path.join( + root, + "circuits/bin/recursive_aggregation/c3ab_fold/target/c3ab_fold.vk_recursive_hash", + ), + path.join( + root, + "circuits/bin/recursive_aggregation/c4ab_fold/target/c4ab_fold.vk_recursive_hash", + ), + path.join( + root, + "circuits/bin/recursive_aggregation/sk_c2_chunk_finalize/target/sk_c2_chunk_finalize.vk_recursive_hash", + ), + path.join( + root, + "circuits/bin/recursive_aggregation/esm_c2_chunk_finalize/target/esm_c2_chunk_finalize.vk_recursive_hash", + ), + path.join( + root, + "circuits/bin/recursive_aggregation/c2_chunk_batch/target/c2_chunk_batch.vk_recursive_hash", + ), + path.join( + root, + "circuits/bin/dkg/target/sk_share_computation_chunk.vk_recursive_hash", + ), + path.join( + root, + "circuits/bin/dkg/target/esm_share_computation_chunk.vk_recursive_hash", + ), + path.join( + root, + "circuits/bin/recursive_aggregation/c3_fold/target/c3_fold.vk_recursive_hash", + ), + path.join( + root, + "circuits/bin/dkg/target/share_encryption.vk_recursive_hash", + ), + path.join( + root, + "circuits/bin/dkg/target/share_decryption.vk_recursive_hash", + ), + path.join( + root, + "circuits/bin/recursive_aggregation/c3_fold_kernel/target/c3_fold_kernel.vk_recursive_hash", + ), + path.join( + root, + "circuits/bin/recursive_aggregation/nodes_fold_kernel/target/nodes_fold_kernel.vk_recursive_hash", + ), + ] as const; +} + /** Recursive VK hashes for `BfvDecryptionVerifier` sub-circuits (from `pnpm compile:circuits`). */ export function getBfvDecryptionSubCircuitVkHashPaths() { const root = getRepoRoot(); @@ -202,6 +281,9 @@ export function readVkRecursiveHash(filePath: string): string { export interface BfvPkVerifierVkReader { expectedNodesFoldKeyHash(): Promise; expectedC5KeyHash(): Promise; + expectedSkC2ChunkKeyHash(): Promise; + expectedESmC2ChunkKeyHash(): Promise; + expectedVkBinding(index: bigint): Promise; } /** On-chain `BfvDecryptionVerifier` sub-circuit VK immutables (for deploy-time staleness checks). */ @@ -222,19 +304,44 @@ export async function assertBfvPkVerifierSubCircuitVkHashes( getBfvPkSubCircuitVkHashPaths().nodesFold, ); const expectedC5 = readVkRecursiveHash(getBfvPkSubCircuitVkHashPaths().c5); - const [onChainNodesFold, onChainC5] = await Promise.all([ - verifier.expectedNodesFoldKeyHash(), - verifier.expectedC5KeyHash(), - ]); + const expectedSkC2Chunk = readVkRecursiveHash( + getBfvPkSubCircuitVkHashPaths().skC2Chunk, + ); + const expectedESmC2Chunk = readVkRecursiveHash( + getBfvPkSubCircuitVkHashPaths().esmC2Chunk, + ); + const expectedVkBinding = + getBfvPkVkBindingHashPaths().map(readVkRecursiveHash); + const [onChainNodesFold, onChainC5, onChainSkC2Chunk, onChainESmC2Chunk] = + await Promise.all([ + verifier.expectedNodesFoldKeyHash(), + verifier.expectedC5KeyHash(), + verifier.expectedSkC2ChunkKeyHash(), + verifier.expectedESmC2ChunkKeyHash(), + ]); + const onChainVkBinding = await Promise.all( + expectedVkBinding.map((_, index) => + verifier.expectedVkBinding(BigInt(index)), + ), + ); - if (onChainNodesFold === expectedNodesFold && onChainC5 === expectedC5) { + if ( + onChainNodesFold === expectedNodesFold && + onChainC5 === expectedC5 && + onChainSkC2Chunk === expectedSkC2Chunk && + onChainESmC2Chunk === expectedESmC2Chunk && + onChainVkBinding.every((value, index) => value === expectedVkBinding[index]) + ) { return; } throw new Error( `BfvPkVerifier at ${address} has stale sub-circuit VK immutables. ` + `On-chain nodes_fold=${onChainNodesFold} expected=${expectedNodesFold}; ` + - `on-chain c5=${onChainC5} expected=${expectedC5}. ` + + `on-chain c5=${onChainC5} expected=${expectedC5}; ` + + `on-chain sk_c2_chunk=${onChainSkC2Chunk} expected=${expectedSkC2Chunk}; ` + + `on-chain esm_c2_chunk=${onChainESmC2Chunk} expected=${expectedESmC2Chunk}; ` + + `recursive VK binding mismatch at one or more indices. ` + `Redeploy after pnpm compile:circuits or remove the stale entry from deployed_contracts.json.`, ); } diff --git a/packages/interfold-contracts/test/BfvPkVerifier.spec.ts b/packages/interfold-contracts/test/BfvPkVerifier.spec.ts index 2aa0e795b3..a1ca34b3c2 100644 --- a/packages/interfold-contracts/test/BfvPkVerifier.spec.ts +++ b/packages/interfold-contracts/test/BfvPkVerifier.spec.ts @@ -19,10 +19,13 @@ const [testSigner] = await ethers.getSigners(); const EXPECTED_NODES_FOLD_KEY_HASH = ethers.id("nodes_fold"); const EXPECTED_C5_KEY_HASH = ethers.id("c5"); +const EXPECTED_SK_C2_CHUNK_KEY_HASH = ethers.id("sk_c2_chunk"); +const EXPECTED_ESM_C2_CHUNK_KEY_HASH = ethers.id("esm_c2_chunk"); +const EXPECTED_VK_BINDING = Array.from({ length: 16 }, (_, index) => + ethers.id(`vk-binding-${index}`), +); /** Must match `BfvPkVerifier.h` / default circuit `H`. */ const H = BFV_DKG_H; -/** Aggregator return slots after committee hash: `2*H + 1` padding + `pkCommitment`. */ -const DKG_RETURN_FIELD_COUNT = 2 * H + 2; /** Exact `publicInputs.length` for the configured H. */ const EXPECTED_PUBLIC_INPUTS_LEN = bfvPkExpectedPublicInputsLen(H); @@ -45,7 +48,11 @@ function minimalDkgPublicInputs( ...Array(H).fill(ethers.ZeroHash), hi, lo, - ...Array(DKG_RETURN_FIELD_COUNT - 1).fill(ethers.ZeroHash), + ...EXPECTED_VK_BINDING, + ethers.ZeroHash, + EXPECTED_SK_C2_CHUNK_KEY_HASH, + EXPECTED_ESM_C2_CHUNK_KEY_HASH, + ...Array(2 * H).fill(ethers.ZeroHash), pkCommitment, ]; } @@ -65,7 +72,15 @@ describe("BfvPkVerifier", function () { const bfvPkVerifier = await ( await ethers.getContractFactory("BfvPkVerifier") - ).deploy(mockAddr, EXPECTED_NODES_FOLD_KEY_HASH, EXPECTED_C5_KEY_HASH, H); + ).deploy( + mockAddr, + EXPECTED_NODES_FOLD_KEY_HASH, + EXPECTED_C5_KEY_HASH, + EXPECTED_SK_C2_CHUNK_KEY_HASH, + EXPECTED_ESM_C2_CHUNK_KEY_HASH, + EXPECTED_VK_BINDING, + H, + ); await bfvPkVerifier.waitForDeployment(); const pk = BfvPkVerifierFactory.connect( @@ -92,6 +107,9 @@ describe("BfvPkVerifier", function () { ethers.ZeroAddress, EXPECTED_NODES_FOLD_KEY_HASH, EXPECTED_C5_KEY_HASH, + EXPECTED_SK_C2_CHUNK_KEY_HASH, + EXPECTED_ESM_C2_CHUNK_KEY_HASH, + EXPECTED_VK_BINDING, H, ), ) @@ -102,6 +120,9 @@ describe("BfvPkVerifier", function () { testSigner.address, EXPECTED_NODES_FOLD_KEY_HASH, EXPECTED_C5_KEY_HASH, + EXPECTED_SK_C2_CHUNK_KEY_HASH, + EXPECTED_ESM_C2_CHUNK_KEY_HASH, + EXPECTED_VK_BINDING, H, ), ) @@ -114,6 +135,9 @@ describe("BfvPkVerifier", function () { await mockCircuit.getAddress(), ethers.ZeroHash, EXPECTED_C5_KEY_HASH, + EXPECTED_SK_C2_CHUNK_KEY_HASH, + EXPECTED_ESM_C2_CHUNK_KEY_HASH, + EXPECTED_VK_BINDING, H, ), ).to.be.revertedWithCustomError(factory, "InvalidVerificationKeyHash"); @@ -122,6 +146,9 @@ describe("BfvPkVerifier", function () { await mockCircuit.getAddress(), EXPECTED_NODES_FOLD_KEY_HASH, ethers.ZeroHash, + EXPECTED_SK_C2_CHUNK_KEY_HASH, + EXPECTED_ESM_C2_CHUNK_KEY_HASH, + EXPECTED_VK_BINDING, H, ), ).to.be.revertedWithCustomError(factory, "InvalidVerificationKeyHash"); @@ -259,6 +286,47 @@ describe("BfvPkVerifier", function () { ).to.be.revertedWithCustomError(bfvPkVerifier, "VkHashMismatch"); }); + it("reverts VkHashMismatch when the SK C2 chunk key hash does not match", async function () { + const { bfvPkVerifier } = await loadFixture(deployWithMockCircuit); + const { e3Id, root, nodes } = ctx(); + const pkCommitment = ethers.keccak256("0xabcd"); + const publicInputs = minimalDkgPublicInputs(pkCommitment).map((v, i) => + i === 21 + H ? ethers.id("wrong-sk-c2-chunk") : v, + ); + const proof = encodeProof("0x01", publicInputs); + + await expect( + bfvPkVerifier.verify.staticCall( + e3Id, + root, + nodes, + pkCommitment, + ethers.ZeroHash, + proof, + ), + ).to.be.revertedWithCustomError(bfvPkVerifier, "VkHashMismatch"); + }); + + it("reverts VkHashMismatch when a recursive VK manifest field does not match", async function () { + const { bfvPkVerifier } = await loadFixture(deployWithMockCircuit); + const { e3Id, root, nodes } = ctx(); + const pkCommitment = ethers.keccak256("0x1234"); + const publicInputs = minimalDkgPublicInputs(pkCommitment); + publicInputs[4 + H + 3] = ethers.id("wrong-c2ab-vk"); + const proof = encodeProof("0x01", publicInputs); + + await expect( + bfvPkVerifier.verify.staticCall( + e3Id, + root, + nodes, + pkCommitment, + ethers.ZeroHash, + proof, + ), + ).to.be.revertedWithCustomError(bfvPkVerifier, "VkHashMismatch"); + }); + it("reverts DomainBindingMismatch when committeeHash hi/lo does not match public inputs (C-08)", async function () { const { bfvPkVerifier, mockCircuit } = await loadFixture( deployWithMockCircuit, @@ -348,6 +416,9 @@ describe("BfvPkVerifier", function () { mockAddr, ethers.id("wrong-nodes-fold"), ethers.id("wrong-c5"), + EXPECTED_SK_C2_CHUNK_KEY_HASH, + EXPECTED_ESM_C2_CHUNK_KEY_HASH, + EXPECTED_VK_BINDING, H, ); await bfvPkVerifier.waitForDeployment(); diff --git a/packages/interfold-contracts/test/BfvVkBindingIntegration.spec.ts b/packages/interfold-contracts/test/BfvVkBindingIntegration.spec.ts index 6a54b265bc..e7c12ccc02 100644 --- a/packages/interfold-contracts/test/BfvVkBindingIntegration.spec.ts +++ b/packages/interfold-contracts/test/BfvVkBindingIntegration.spec.ts @@ -25,6 +25,7 @@ import { committeeHashFromLimbs, getBfvDecryptionSubCircuitVkHashPaths, getBfvPkSubCircuitVkHashPaths, + getBfvPkVkBindingHashPaths, readVkRecursiveHash, } from "../scripts/utils"; import type { @@ -46,6 +47,8 @@ const INSECURE_INTEGRATION_SUMMARY = path.join( repoRoot, "circuits/benchmarks/results_insecure_minimum/integration_summary.json", ); +const readExpectedVkBinding = () => + getBfvPkVkBindingHashPaths().map(readVkRecursiveHash); type FoldedArtifacts = { dkg_aggregator: { proof_hex: string; public_inputs_hex: string }; @@ -81,7 +84,13 @@ const readFoldedArtifactsFromFile = ( return isValidFoldedArtifacts(parsed) ? parsed : null; }; -/** Prefer env override, then fresh insecure benchmark output, then committed fixture. */ +const hasExpectedFoldedLayout = (value: FoldedArtifacts): boolean => + hexToBytes32Array(value.dkg_aggregator.public_inputs_hex).length === + bfvPkExpectedPublicInputsLen(BFV_DKG_H) && + hexToBytes32Array(value.decryption_aggregator.public_inputs_hex).length === + bfvDecExpectedPublicInputsLen(BFV_THRESHOLD_T); + +/** Prefer env override, then current-layout benchmark output, then the committed fixture. */ const resolveFoldedArtifacts = (): FoldedArtifacts | null => { const envPath = process.env.BFV_VK_BINDING_FOLDED_ARTIFACTS; if (envPath) { @@ -90,7 +99,7 @@ const resolveFoldedArtifacts = (): FoldedArtifacts | null => { const fromBenchmark = readFoldedArtifactsFromFile( INSECURE_INTEGRATION_SUMMARY, ); - if (fromBenchmark !== null) { + if (fromBenchmark !== null && hasExpectedFoldedLayout(fromBenchmark)) { return fromBenchmark; } return readFoldedArtifactsFromFile(COMMITTED_FOLDED_ARTIFACTS_FIXTURE); @@ -207,6 +216,13 @@ describe("BfvVkBindingIntegration", function () { const expectedC5KeyHash = readVkRecursiveHash( getBfvPkSubCircuitVkHashPaths().c5, ); + const expectedSkC2ChunkKeyHash = readVkRecursiveHash( + getBfvPkSubCircuitVkHashPaths().skC2Chunk, + ); + const expectedESmC2ChunkKeyHash = readVkRecursiveHash( + getBfvPkSubCircuitVkHashPaths().esmC2Chunk, + ); + const expectedVkBinding = readExpectedVkBinding(); const expectedC6FoldKeyHash = readVkRecursiveHash( getBfvDecryptionSubCircuitVkHashPaths().c6Fold, ); @@ -220,6 +236,9 @@ describe("BfvVkBindingIntegration", function () { await dkgAgg.getAddress(), expectedNodesFoldKeyHash, expectedC5KeyHash, + expectedSkC2ChunkKeyHash, + expectedESmC2ChunkKeyHash, + expectedVkBinding, BFV_DKG_H, ); await bfvPk.waitForDeployment(); @@ -247,12 +266,16 @@ describe("BfvVkBindingIntegration", function () { it("rejects BfvPkVerifier with stale immutables", async function () { const { bfvPk } = await loadFixture(deployHonkAndBfv); const address = await bfvPk.getAddress(); + const expectedVkBinding = readExpectedVkBinding(); const stale = await ( await ethers.getContractFactory("BfvPkVerifier") ).deploy( await bfvPk.circuitVerifier(), ethers.id("stale-nodes-fold"), ethers.id("stale-c5"), + readVkRecursiveHash(getBfvPkSubCircuitVkHashPaths().skC2Chunk), + readVkRecursiveHash(getBfvPkSubCircuitVkHashPaths().esmC2Chunk), + expectedVkBinding, BFV_DKG_H, ); await stale.waitForDeployment(); @@ -314,6 +337,17 @@ describe("BfvVkBindingIntegration", function () { const decPublicInputs = hexToBytes32Array( folded.decryption_aggregator.public_inputs_hex, ); + if ( + dkgPublicInputs.length !== DKG_EXPECTED_PUBLIC_INPUT_LEN || + decPublicInputs.length !== DEC_EXPECTED_PUBLIC_INPUT_LEN + ) { + console.warn( + "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(); + } const expectedNodesFoldKeyHash = readVkRecursiveHash( getBfvPkSubCircuitVkHashPaths().nodesFold, @@ -321,6 +355,13 @@ describe("BfvVkBindingIntegration", function () { const expectedC5KeyHash = readVkRecursiveHash( getBfvPkSubCircuitVkHashPaths().c5, ); + const expectedSkC2ChunkKeyHash = readVkRecursiveHash( + getBfvPkSubCircuitVkHashPaths().skC2Chunk, + ); + const expectedESmC2ChunkKeyHash = readVkRecursiveHash( + getBfvPkSubCircuitVkHashPaths().esmC2Chunk, + ); + const expectedVkBinding = readExpectedVkBinding(); const expectedC6FoldKeyHash = readVkRecursiveHash( getBfvDecryptionSubCircuitVkHashPaths().c6Fold, ); @@ -330,21 +371,18 @@ describe("BfvVkBindingIntegration", function () { expect(dkgPublicInputs[0]).to.equal(expectedNodesFoldKeyHash); expect(dkgPublicInputs[1]).to.equal(expectedC5KeyHash); + expectedVkBinding.forEach((expected, index) => { + expect(dkgPublicInputs[4 + BFV_DKG_H + index]).to.equal(expected); + }); + expect(dkgPublicInputs[21 + BFV_DKG_H]).to.equal( + expectedSkC2ChunkKeyHash, + ); + expect(dkgPublicInputs[22 + BFV_DKG_H]).to.equal( + expectedESmC2ChunkKeyHash, + ); 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: 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(); - } - const dkgCommitteeHash = committeeHashFromLimbs( dkgPublicInputs[DKG_COMMITTEE_HASH_IDX.hi], dkgPublicInputs[DKG_COMMITTEE_HASH_IDX.lo], @@ -461,6 +499,9 @@ describe("BfvVkBindingIntegration", function () { const dkgPublicInputs = hexToBytes32Array( folded.dkg_aggregator.public_inputs_hex, ); + if (dkgPublicInputs.length !== bfvPkExpectedPublicInputsLen(BFV_DKG_H)) { + this.skip(); + } const expectedC5KeyHash = readVkRecursiveHash( getBfvPkSubCircuitVkHashPaths().c5, ); @@ -501,6 +542,9 @@ describe("BfvVkBindingIntegration", function () { await dkgAgg.getAddress(), wrongNodesFold, expectedC5KeyHash, + readVkRecursiveHash(getBfvPkSubCircuitVkHashPaths().skC2Chunk), + readVkRecursiveHash(getBfvPkSubCircuitVkHashPaths().esmC2Chunk), + readExpectedVkBinding(), BFV_DKG_H, ); await bfvPk.waitForDeployment(); diff --git a/packages/interfold-contracts/test/E3Lifecycle/E3Integration.spec.ts b/packages/interfold-contracts/test/E3Lifecycle/E3Integration.spec.ts index ce6a33017d..41a635877f 100644 --- a/packages/interfold-contracts/test/E3Lifecycle/E3Integration.spec.ts +++ b/packages/interfold-contracts/test/E3Lifecycle/E3Integration.spec.ts @@ -1697,9 +1697,11 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { )); expect(recipientClaims).to.equal(ethers.parseUnits("50", 6)); - await e3RefundManager - .connect(requester) - .claimHonestNodeReward(0, await operator1.getAddress()); + await expect( + e3RefundManager + .connect(requester) + .claimHonestNodeReward(0, await operator1.getAddress()), + ).to.not.be.reverted; }); it("holds an accused member's base reward and reallocates it after expulsion", async function () { diff --git a/packages/interfold-contracts/test/Governance/AccessAndBounds.spec.ts b/packages/interfold-contracts/test/Governance/AccessAndBounds.spec.ts index bcae901d01..d204f55f96 100644 --- a/packages/interfold-contracts/test/Governance/AccessAndBounds.spec.ts +++ b/packages/interfold-contracts/test/Governance/AccessAndBounds.spec.ts @@ -237,6 +237,9 @@ describe("Governance — access control, bounds & events", function () { await circuitVerifier.getAddress(), vkHashA, vkHashB, + vkHashA, + vkHashB, + Array(16).fill(vkHashA), 5, ]); const wrongDecryptionVerifier = await ethers.deployContract( diff --git a/packages/interfold-contracts/test/Risc0BfvCiphertextVerifier.spec.ts b/packages/interfold-contracts/test/Risc0BfvCiphertextVerifier.spec.ts index 9c1910619a..550e9e092a 100644 --- a/packages/interfold-contracts/test/Risc0BfvCiphertextVerifier.spec.ts +++ b/packages/interfold-contracts/test/Risc0BfvCiphertextVerifier.spec.ts @@ -16,6 +16,17 @@ describe("Risc0BfvCiphertextVerifier", function () { const paramsHash = `0x${"77".repeat(32)}`; const inputRoot = `0x${"88".repeat(32)}`; + it("rejects an EOA as the RISC Zero verifier", async function () { + const [signer] = await ethers.getSigners(); + const factory = await ethers.getContractFactory( + "Risc0BfvCiphertextVerifier", + ); + + await expect( + factory.deploy(await signer.getAddress(), imageId), + ).to.be.revertedWithCustomError(factory, "InvalidVerifier"); + }); + function encodeVec32(value: string) { const encoded = [32, 0, 0, 0]; for (const byte of ethers.getBytes(value)) encoded.push(byte, 0, 0, 0); diff --git a/packages/interfold-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json b/packages/interfold-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json index ae57a1f490..f0a6a8ed77 100644 --- a/packages/interfold-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json +++ b/packages/interfold-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json @@ -1,10 +1,10 @@ { "dkg_aggregator": { - "proof_hex": "0x000000000000000000000000000000c759cc9ae941af5038c8b97755896fb19600000000000000000000000000000000001c1a3722f951573b5a0295ec634a57000000000000000000000000000000ebebc77ee3eac819e175a341f2f1e7423a00000000000000000000000000000000001761a6553581bea2c3e412c770a83d000000000000000000000000000000bd4f363a701553cbf608568d2de3dd720000000000000000000000000000000000002caccef9014c2923ae4fc77262a79f000000000000000000000000000000322e862d9bba2f86d07a7b5175b02caa75000000000000000000000000000000000019ff9c34c1b63085570e6ccde257e9175c8a0a853c581e7df5985602b20f91e1c6cd45d5923748347fc716cf3a00e604e10e905ac9a9456efd0986dd72c6545353023cd2b9497be947f2f35c62e150000cbae4474670e3b176c4018bc342ce8e951e9ad6a5504ec706d5fd87446b2716e2a9c3ed627155bd5d76ab35901f2e5255bb5d8f85910200e2e91820ad22eb2db7f9f23a6d9a10de6861724a75b6fae6bfe2d01e1c2a18cf616a7747715a461765bd8985b12e9e52e890f2d5d541169fbdc61a29bece944b025719edd3fd6d163f35650c51e3e1eb0c279d971f4e8e6409671826352f4d34d24fe6a10610661c432195a7fcecb8e0b438b288f6302ffee477b505158b87040797ce4f4b4e7005977c4b9ad65a2b48b653643c9a7b4296e4a4c0e5c9db3351305fbcbd6cdbff2e48d28f62f21ffc9917691eb254eaf22c875e58b46daf87978f5d8eccab9cf80830b48af10f7d848945a9d5830c092cba925072a589c9bdcb68f164bfaa5ca60aeb1fac877e0070a79299567b9cdda6dcebe60dc44d1be617ee868ec9fa7e14115420a86c9f26f2d20f48850475cc4b97196277935199d6df233acc66a77b2d239d9837ce927fd18ba57f7c4764be438128dc93ef79b681f3a1290e6734d1a20829bfb6346497e7fd3e0ef0a31ec60db140deb1cbdba1167a2126df1730722d01dc514c81ae1cd51e95fc18ed7ecf9c56a565c7c87b1bb7ea978d45395f4b862f97c5007c5742636595b0e3a787675b9a0faf474a8a83d1c94da33206e3073d188fd1a1a636819328f52a05c0b59d357646d25815174ebf0ae7d012b2ab7640250f5061dc7793feeea12fe461e6366a978b6e79806150e773eeef78dafe10a614b131aca8f036b7f6deb336a51da9af0436a6f95caa0688cab256933131fa7409e73ac456bd47b679257a6ff01087d995410076713b594801cdbfb11a61d907149a4e3370d308872156fd3046ad167d9bc72d05887d0bc5983ba833e1a2485e1ee698e9512018aaa24023891b4f23fc39074acfe47ff31267a8659cae1081902e6d3d532eedaef05f9b61d520223781457c0032d4b0a5bdca621c49c7e3d98911a0cc9690f93771212cd15b6f012b016f3b5a12634447e24f47f415f26048310570d3926f9dd89c607e3105efe9954ee41c2fde40b7447dd58e251ef36da67d2a621e97f3f44c82bf05b1296092cebe5c4566abf559e632f603846a8c6d2bee2cfd52a8470cd5c290a66b4d22b9c30a616c04ada4389614d71ec06df3e39a0905629a96e6a91a59553d96aa8caf2418d8625162c7bea5c6228b01203459d57a19c1101fe9181bcb534cf5d625662f624d2743b10e50a3bfca925f9e1aaf0d7e2daf54e6c41a278ab88401e8b645f173bb15562239be1de98ebed36824681e6d2fda859d6329bf6dbe2a5fc574529007834f3e0ab6852c675fc92a320a3e91a01e715d1f01ff9d9621f5e9083a9946432110ae7ae1ade41e667bdb12ca4ff3340f1d13b06d7b56c799b1b14a8b02d86ab75eaaac7a4c0ecc72e360eabfcb74231b2bdf968ce8c9b1bc2d3e1cd01cfd75b3be2c547d63493439dc68a9dcd26a9611c698f25299df7be0383ce9938461bfc55b77985802383b80189e4306307bcc2531e4c0f06385c71997814c0bf06958c3bbfeb70ac933e786adb5c073eb881114746fbaf5f6c58d77f488f4af5b89a0d36d612d3a7e0ef0b9d4ec9a068fc4df08ffb7b65068ec9cbb9db4fa4a991e426c6cf7a78d85b139d74df4ff231878782745d5ec0d810ba7c5aff7261cba33d6e56b1df25fe9dbc9a77c8328b215e5dc067878a9aaa6954a8fed0e61da7592d5d2ab592208ed9d558dd486fa507b08f5056ea32aa0780507691b2c4c8d3d6ebe064b1fe33568ed71d2e5535b858b02b42fff93bbafb67aed7476217d93ab37f2ac4af525a772b52d77750be4625c440a0699b553facb40f8d8655b1bdb4e699e3c78a757af45faa9cdd3958859997b3b226a26176de19212ab88a28779118202e4b92a0d41ef5382087639c78181ddbc08e152b6eb762728082f19ea45fbfbf03d95282ffc166d46fd587917ee2f3f98256127e38ce5dde8ed2dc5ac3399ff1a6fe06e37e2eb22af166a7b43f90ffa670d2f40c1923d03d1f754e58f9c8dddee4a4c6f9a95f74115b688cdd31e17d21924da32c7ed8a2f1001163ab1181e6d2e74668aa6b64245ebdf8f4d11dcb01ec801ea0eb5b81f3067dd4df939f909924174e37291563c3d378b2cfb156b6e61d009532be9a0ba2d2f6695fccc0cebb9bfece2b8e64c42bb76f9adb2404761970f2ca979abc7bd5cc419ceea720cdc6e23e99003a4fa07038cb1e90cc0c49c8f8611d03e68f76273bdbba4d2464c92047f90c7be5b84847d421375c8a2ea8eaee013a7a6d6466ee5761f3bd67a9cde275a9fbd9c5b50d1edf9d4005f0f5cff488203cb0597e2ca59a86bc5f49d09a865eb8199561658a8008c64fc3524779f7f730ad302ef30349fcc9b62adcf392ead44757563ce2ed43bb92d1aebb0a9e03c8a07c8476d229f9b266e785c05d53e9b73254fe9b86f91e0ec53e9c7d58c10dc961437d6660646e18f3c443a58fb1739ead7832aa5716e1cc55567758260b230622bb96af1734a6ac2507fd32a660f097b251f42f949dba2f2642ff20d82c781831d20eccf81f8d3ac8bfcc2b9670c25bbe2fae7910c4999f92775fbee38ba1f062fba2f46eab74cf60c2c8ebf9a80aed866cca5e8818d0ac75b4e919cdbab8f7524d82783405a845a029584d51f25c71ece080011afda4f014c0e34d9cf7aae6f282c1fb743d6211caa8eb0e5364cb3ee879af9bd59a0914d6db401f38cd3e49a305fbb6de0e1d26881bc656993ee06067cc85811dbe2d7304719cc90c0792d9f273f310a1c6ec30aa4786c1616372f469adafa8cb642ece2078ee7542ac7483b1155cd3f69d2b080c84baa0be3c7f716996fe18f096aaa7677ab78a850807cd8057522bbca9290b0da94684336ccce567727780a5b3e44e645e6146bc6936ea80016339538da2188baaf589b9f04e497309af6e95b393d44ec52e6f4b850351e0d6a0e5ea4a4d0dc742704872c165ed96dc9756e146cf9af3d51e8b1122f2bdc289beae7844d461916c9639f79cc5ce03628f379b7b47882d89abbfc3a2b75710659278fe184e844f01e6b8ec9999cbcc0c2112212aba0543b0bd1a84e6ef85e28a2ed95cf561b96140ac12c1a04f4c6bcfb7ca8ddbe9b3602f6b14d4fc4cd3806fb768f2fa3045c59b79dbc66a70a59084ee7fcf905793e3baf8473cb6c82f224db7982176efede43f0b508954ad6f5186023c5c1cbd04689bd671a03c576ba179394606793005d3ab5bd2cd7ffbcf5189f555bc75d017e2ce579e1d5158ad11b5236e4a64929561277c9364a875f1188549e4454b681dd5c36bf6e651ab1e606957ded828d14f9ebc366e4a83a7bc1cf2a5d9da18cadd51ca9fd41e4a963bb13303a3b95149db5d339e833014077231525072b27e7a9f99f7fba9d74893817186a46dfc9382c3dee6d4b96765692bc8aadda2499b90881fe8c0f8db1ce745c0806ba962e92c265175151afe26f82c6fa1079430b7333947f8db354b606d41108f97398b93cdd0d5440c2967721427ab5f769b492f824eae2cb86642df2e0c02b210995564219432087deecc469eabcad8d53b68c72913b97195bf0f8a0fa4b299a1437085d87a988eb22142265f86dcfef64917e2bd6d4ed2bca0b28f4e15814ba6ee18934157ce4a2d021367afcd9bcab394067bb5fd5e1505e2c81245baa1f633a656cbc45e5d189d1a46b09c1e4e11807b5ceca6555f996602d27aecff519561fb9a830552b56712384d54be8d727a63b3507d5cca73488abd5da7871932533439655dbaa9d4c5334c5024b77b0ee25f547bb982a195342629b9c82e5fc1c9e023e0fd789102a92b9a71d09dd99161ac059fd2e3e1879f85b2ac341188d1e2a9c8f6dd0d2f6e556b5ce1e293a4e9a1621cd58403eb8b511aef70d96b7d808d0bd937de5e2b5e315a554e0b9dff8383d4efd199c655a3cf071a8dd0f37ce1cae0232e2e580ec70fe042ed0bf9e83a61da452364f65d9e79a51e75067c19312c26d49bcdd756df7f69642776dc082454e35948dca833162399174d8b104f6000016c3fa686b3a66c62cd7359a57181893bae64b79c083e773667b2cc1130910090e1f596042bf38eb0281b6c1552b8f51f84ead05efc56782e43c1d9d87500a95d8b848bc389fe7c6d87c339b77add806c26f14d22638748a61bb9a9eeb022ef9cf3bea64718fec066c36221779762876c02afe32558a9fdfe7cba4c739381baebbf2588f844eba3930a48f304d44db1618e84e10b8b49f6beee4e7820cd72f42d252c0cb5a30a84f184244f27882da77bdd8faa2ea30a10e18b7e1dd3e0c230d26015a91cd6715e25db1c96cb052cab75f7cc63f127d70ada6d985b56dc1047704c29ca22cfad3459f4626cc2bc6f18b80c02b4af9b662a13d223ebe80082fb19654c5b8769e49b29fade59af2f107dd82ebed8136fb83203c166a09fa6024c6dc33c48be2d4284bca08cae0fcb17b2fdb73087940d7dea14c6269f22a1914282cec8699544204a9f530c1cb83d7f7f57149f15c5ced3a5bc319d7a5fa3612c039eed45839e89fe8087578f61b6e57d680b69bb0529d67a198271911efc01d0e72b28451c757ed721fe770b45baf36aa5178df3863a7f3dd873857d681ec16322cac84631562e2bc0da187fe1c8297a833792c67af86630d0bd06e355d5b09c22b433e4fbdcd6374c73d34e9f766e8a6303794f64565076984ab6d7abef52c7361523d4bfb2280de609faac1783471a10256c47beab2553cb3c4c33898e627725d2674f4cb49877277e36cddd5165bb8003be4078a3a9f7517fc4901b01e2ea77fbe636915f60917c49c6a1e2b9a621ad8fd516825120439202e131f329f16d349fe78c6299782f6693490807be8f39e48ef17b3d68ee5e21adf4f61a17e132871af83e4b29ff2e5c8e3b59bbba2e7d8c86485c9f20073922548e0021dde0e89f31b4a1dde97ade10df8c4bd71fb200f87bdc1c39de5434e766627202efb2f804bbd9e5bc106a04249185970eae01cd72fb10c4601ff1925d9a2bf8b51b214128506e3747027750f97959324f700a69b36ed5dbd95daa5f223f093b58a721502839c69d8a7f95382e43a0feacc70a594ca65b7f7544590816fb7771c2d0f05e559f8d94fbd7fb7a555f7dc8264747d3526b739fcb4a993193504ef34774f22f5cfec7bf5378a4fea1b51f2862f34caf8545b68234e0b7e37d8c481760e13151b9d85496ad479812b968676fdd2100b534875aa88d2fb33df3c4c58744b0223621e3a669f667514ce179a06659f0a6e0940784655b16fb3f42036106e758f16abc4d29b9cb767e95ad8c9485e3a7e7be09c595617f4415258b2d2c585401703d73f4f24541fd6c452b3975e6b4bc8e3400a6c66acb09647e804aa7853196e1b7d3dbc88bc7adc2f59ecedafee3dc6b923b87ba173fe27cd086b95f4bcc5601ca9a786ddb4e25dfe9648f322d18308a393c082149427ddc4d4329531222c8129cb21139f18c946ef5b677399e2602472069f63c81fd2141dc472b2e6975832157bf2d61291028efa3e2a21ba8491620202054afe148aa88c0d95cbc12c2e3009342051fefc24bb90c64512a321e1de49e70d3ddee5f2d911d4ecc55ca287640077539459a07b6f7620153f564a80945000382df390ec2ef00f4a7bdbabf11818290ccf8e0448a34ce084a98786c9c797006827f273c70b828a45a08c4e436c01b61bdb3ae03e23c7fac42a9e37359c9b07c3d05a5b6674025edbc3cb7aec07043743952eb9d16dd927cb89e1e8e8c0250cf0304e06f85294feb53bb38ab7c52de8e24812322a30f10ae7b2018deca3abb410082bcf158916a0bc3e2bf7783d2c60f46c6f6b4d58612ba6a4234b33b4d8edeec6b470263f9782828ccbdd343f011deb47f4e681f1ff127efd11a1d0988abf496ed35a805dc43dd59dee4d97dc2a1033a75e8b3c59d27b760b3dcca7d5b6727b6c8f69b6a3f0e62bf04b4ff63c2adac9eb4ef1871f2f185f4cf43c5692fb33df91f2cc72c5344bc82b1a1951c502f9120c1901c57e2e82560146a761c70974ae50e12722a7f61c3f2bb25ced6b2b61dd8d279aec6121a6baa0815c3b99d15e34b56c839a095bb442b4797291b71c047251c00e59845ba2da715d271fc947160931eef48a245717cadb767a52cc281eec7635d27b9852fba5ab66b662b0951f552c96ffba05c8b5dea1696eadf7187b3ed16187fcbf0771f0763f087e893fcc4409b52487bce7c3c1cd43acc85528191d6894976a26d5f22e35f51f1f700d47089cb31d549bd38c6f3d2e17490108882096e0fb41948a8ee44ad8e53a98d93755cc1af4a71fb5634c6ca68f9b93038fb9cbfe722bdff8ec9c1dcd5af0692c5d55de5e8554a374b6715e2763dfa80e6f6ce95cf667c273d14cc782ef60840d4228de706dab6f02e17fdb33adf8c022a533e3bd128e495c7d94470588e198a2cfadb8137d5c0833d5e2206902c2fa0e17e2d9916495baddf57bd56911aa2b8d2e34cfb00d339151cfd7cca2c8e2360ea069ca354da044e99704aa7966e51918995f709f14ba06aaa55369e41a529801a1e969f34165ef5fcc6afa7be3e81c6127b14eab343bfb10396334cd7bd8ff2f263c869292a68d3b3f536bcde3fad616f373354bb71e9c3be094145fcc92ae2129d1d86a6ada118af3b2cfef4018050a195d3b6023e8dee36ec41cd3bd51cc19a5e1064032b019ef1842884b1f03a2cb32980834acd3e071b1683ad634bb820d221b114896761bc5543531d7b7bbdf1f49811f3713401f8633956f87305be2176d2b32d2d66e90f91d00eabe4241848633ac8e5976812a0629e356727f4fde22ab61960e61dd1fea2113ef9e69fd89e9aff6e788dace00d7acadd32a505e0e161ae6a21d443cd891009eeb872665d78f5c767078494a1b74f4110dd9c2a43e11e1518775728ef06699ed7cce5acd7da177c7b7c763e76177ed2c2535f066310d3754f86e7015fb57e07fde37353ae768e174793e8303b33d1006b4694cd26c170af15ad6d3debc2942865bd564c77fa64b394fcb74788a4cd20f99cbbfc68e29f3a0505f6ae69e49380c3c4d0bc0378550c9779e6790a438a79fb64c528e230b7bb13fdc511c3db733aedf97ec3ff96012108eca4d2ac47d7ca5e223c6439729148b956a062163e14583abc517e7122547cc1cd48475cbff014acc189e75c022e68dc348062b95f0c3a965aeebac0134f9654f6e1eabbd30b884e37e7eaf1500a93d125272bd9cbd0e28d8886d5f54c57feec8aa5bc45914d6c0bcafcc56e72402baf2b317446ff133dfad6fe40b43a154050e1554bd2c965a661c829017e60f80eed3ad2e084e20972f00a21f426ea554403a47267be9a09240017337b63e27d248d17d3aae35c36d91b71c9b76335b9d95ecf43418b33b9446e85981b7cc03c4672ad0d46cabe5606b021d75d4e2f10e0630c15d085172baf53c8260c147015c4e0fd1f0394e38c9760ce9c518277d1421096b5420d0d9f482ba639a5b0627117d187324006b59e4bae4a7f20b29cf661013c9458b5f1040dd1dbdd45fd72775dfbc9f2555a5e5b1d2b32faceafef1c6d3928dde9ef612069719d64c3c1311744cc013d0e906ba643e80ef12771c1ad1c9fea56147eaa5bf9a18548abb2b0bde62af5e97e9bb617a553acc02444ce0225df09199be1b9e606ccf41bc7f9c0876910e3bab81c0c9c06be401be5513b780e687216d4f69ddb943f75e1e5d1f26c97b4f70a4d7ee11b64fda33c1180c146225af5a165793cfe07bfed94e4d4325353fcc7b86a240b762b860fa359e4cf09cd4629fd6002223bb24d6feb869ed0259f5e538180c6cb2de659d28bc4f68f938c4ab06577f54e22e3c0785af3c8611b6702bd458c4c18c55aac0bcbf1d0ce7809afef216c4619fe8c943f270c7650427a5b9b9afb212fae766852904faa21577865a48f1403bfbb8ea1aac50ca6518dc739a15a0b4a56ad59d88b5dfe1f575123784bd076fba1729f3ac2f7e1fda0137387fb527bbb588f4a348429e98a9d4ff83fd26a6d497f15cf68f3c04866b26740d7c3cee92417ff39c60d93765ca5fa6c3253e647cca67b10d48fd98acd309e360e95dd91c1b289c4ec43f4846196ed36b18f5a6ddfce46b358cb6d3d15b0aa10590cad06daafe5738ac4ef1f19dec98c3c776a6b5dc2feebf49134e800427273e4272ea3f609f3db92446df60fde2c3b97c6182b8ce3fc70f3f1c5ea1c913944a724bf5c1eea1758c60b4d8e65972d80859d897e2717f0b408dbd01ba7115b449c6c18cc2d1dc71887249c52a5a61889fcd96e1d00a92a6f33ceadeae141072c84df6c67fa4bd35530aff5e5054f1cfba3d529563c2e5af0007af7261680c74305830a823c25ab8eda632f5cd905f0e39de50da8a97b465538f679ce0d312bf96b846402482cd2aab0207eab96de39c815ae7ef43b34368c5a9e53e20f62b2d42b6b44f39a5b8297739f63601ad8cbe8320b25096b5d971bddcbe8a3a4522c5269a5499984d9a7643633d91c466bc1c0d08476da96e46cf64d20cc3c755046d980cdc172c4e20ddad7f34fb278cd2f20e11f8de085f5b082aaa464f0d79240cc939b42728884f938f5a00767e119d4ec9cfb2d72db9f408784be32dd14609325c1260f70eacf3168c470416bd1add0b95b46408127114ef8a379529c43704831cb1f563e7dfc5021f9f6ab2a392b692b22bd5efa6a250efa327b6fe54a21f53426f41098c61d9a312aeef2d50322a67e0705c975803f2d1e19e1f227d63054c0b7d389615b62be4b2143df9e227b3b992004450572c4061dbbac17916ec023cfd919c6637c192791c43cfa2bd117c4422529ca40c846bb6b721eb2e97d9266a6c835bb266ed12f5ff261d42e7b69be6e09a04ca373b59412b9b299821d90668f8b7844f1c832fb796ccec3a22f7c021b9d91300a8a0545bfe3336ce3b9d12f434ae016820db0efa0606dbde578371d9547bac26871da965df77546585ae18cfd9e924c8760087da3b169280d22fc4fb0d5a20164cbd66fe690128c996b411360f7a8f72d6516c73e2a5c1c9716d601c19995fd150f1fe8371248cecd391302698f7b58cc6804ac7b44f89fa4b9fc5c9b24e1a121767031cd8ccb748db4c0e697d7d7392798311b37b50866a3b7d3d3101ef0cdca74e5dd2176e2255c196213fef8aa4547e3688c703a9323af3597392f466c7da6c2807be00201e3b09fa1ffac45e4f03b2c9e734ac5885ecb4fa16a67378a353389c4a39418de787133a22024ff842cbc7653c15ab0b46971c4b5e1387073bda10dde0370890885b616c106f45f485fbae119c4ac08d02333f2c6a8b594322f5f03647b001a17b224d5f0c64f295539bac409493a3d1c82b3f14c5218ef6416e155ad518992b5683440d2817185a6ed36f36ecf47968ae5b6d72a38848794181dfc6d9b5ec3b49863c321c9ccac54f01cc4a9c2b3907c2618469d3e6fe8b407eabe57cbdf9eaaffdebce16d1b551439f1ad8561eff7eeb2d5bc4d08a914406a6279c3c72717c71dea06d1193c6fb73b1f962685288bdeddc04638596de80a18198f44c520e83ccfa2a8507b870a82ac4bb7273990895fe1d9ddb84c7059c106d7b183d17201a79443f9b1a933486880cf1535e2d738a63d203defe2120e93ba685b946ca132101ffbd061cbe1e151bafe989035b7424abefc57608ee22c29301802344149751e7af0cf10df41f7ef566fac5aa02c5692c2a53ed72f8423cccdebff3f24ab6dd1cd6c7012e612725c3dc34c8b15110392de34db8c0c0c07a22eb983746f2a823eea3e05322f340b866bd3052f3d426ef3d9213920714bac789fcc379c7daedfe5382011a25d31ee05471387717daaf277ed08d0c3a058bd8eb5b94e944842c05ad596a8d03189f1e59e39e8cf0e81f321ee83a511ec2813596fcd0408fa932b36331ef650b95dc32f67b1c352c13dbbf6eed9dc7d14688d36f040b9e88f294d30ea0e0520167c66d018a95285e9cdcb84fb779c7ea985d16edc4521dd73b9ce27fd5680c0f67ce8a93ff555326ce409c4aa4b865574a48895071f506566c85ebb87d3eb62c276feba915c5a659ab516c4580a8a55f90139b5750127bb969ce28799b173d29aa70c6ae4774974ae60bd2da6d3017b0a2e0e4f130c7b8f68d197b1b24cd0c13678f30fed1f49b47ffe2054721cf2f8339006eb4febfc1eaff7cf1c7b1853e2ad3e911f4060b2e074a4b6753bc5c7a1f19554ee06600be6e9e91818cad711d21309d1176b7f2c97f007a08aefa8e7b4d3250e8ac0960900246ceacf99942990d56c59c721b2309bdf354272aa6555ec8707a655935fca59c2635bcebda2af22689d41b7905bd64fac9ca8fca4093888dfce5e9ee121e0288c63d13dd489a342b2ae1d5339d29a32aae0fdb3e565b91aa6f4db2379f0f50c3ab03d39d1ec3d00000000000000000000000000000000000000000000000000000000000000000261282c276ddd3ba00f19704d16881bfd2c6cc2d2e2491fb1b3b0f5c706f36fc15f9f7edae044bb3b268c8978fea31c350b9fc5e7f44644345b9c2a095b756b30debade73053ee9f7d4c5c4b9e90cbce76c25236c7fbdaf92f77d1bf852a2405024249cf915a06031e094c8598814ee69bd5ad3d601427ca887bf16745c1a4000afcc74b0c72ec4d6ee0e7f398b98a1f3538e83b373657dfa08a69fa3727ef841064c74325ff3da5bca1b1f7199e741bad357df2729eab95be1b0762d33594ce0ea50106b3a5ff8fbc411d1ecd33b30bf21910cfeadaff986629c7b844de3dea2aaed28886ce5d4b3a1db9465ca4740ba8e60633b5e2064d044d0981283d99fa13a0c64e3cf459b13066e3848eaa6bf30087f12f11755a22bbb98c5fbc8ede4c06813611edb24aff3688c03e41550557725fe6c922f74fbe4dc98d72126205e71d3e9182c955c7554db86552ea52c6b6c5c5e0f4108a14b28d90f029ece478c81f73a3aff25fb738c2831d8f689c1b2a68b58618a82621630d066afd7c8fbf5122e7086ff042034f042df58a835692874b31c0c408f3e17d71dee33f9ff85d0f277b4333773e83c2ff68ba3a58194752d1530f8db8f75edd7caf01f0792aaf6f2f2b551333c3a7b7e2db8acde56ac5a66288439fe8d53a8c533bc45256f3b1751c8790459323d0e5774d3bcc44a239badadfcbf754d96b8b984b66271c86625c19139146649a6b7ee207ae93e90093c3df5efd8bc8b715610469914474e70f1d2e9677f073c69c7be83b853d168c270a05b5471c6d05eced76a1de69e16cfab72ad169c91ce9f00929e79a8678e829af2e2d77e825d809af30315c81865f0c0129183d54200dc52ba05f69beb4cfc3b573cae01f7f22e4ec70c95d72fc9b513f032dac7e3b5e94d40928c1d9d98babd68065a9f3c518e3fcb69d1f06fdecea7c26b5180407731f2e99307ba131ca01ee0e3798f7b9f1defea51cc50709aeda4525b06dacb8a2478faa8cd16af0f5ff8f413fcfacf8081eac3647a12c932a371f03d6e67a3ed918e3ed48f38ccfe975bb2eb41cd168bea677255a367b3f4dc84d25f2a7aca85eec91f8fe0f7e19bb597cda0f658a4c8effe478d0917438545dec16b8c905656ca8ee45fdc9d6d925e8bd4f327277c7525e8dbed41acca178c3211da131f846f3a1cb5d8d9a5eb802865b4477815515291094fc5352367f6401781dca6822cd3aa9e8d7d258284d1723fdb50a0b2defcab75504df6691dcbac9f22e2dab865c272425cc0fb73390db4a52950ac2339244494559cbe1f17b4bd6ca184db16cc912c3aabb5174c11082faa8699d4704c1d1cd0de780fe7a1b9f60922d7f25f27f9ef71d7132e99b53b029dfbc4930d7272d22318ea9aca18a3af0d61119a2efe76dd5651c4692a8479e2d036f605b69ec03c653d461191cb43a5923088d1bda9e19d6fdb77d65430ebcd3decd8525f8c932f905037a2dc970f1c901273a4b8ab65be2bdb514fad758e80f7d1d753bee3018fdc9e010dde8829c82f72f62d252adafd58a8f5f626309c6c15fba5973d6059f292fbf53258b649e3b6e079e5cd7a73d5cd303a5a95a12a5827546a962769e4005b1a66bdd4bd8534b8515159b5b9e1bfd4b4fcff2c7779807e6b6424dfedc5d50ab2756c6994fd103701e19886f7075281d6839b4141694e4692cc6a2d8d04447f54995944c3dbb23040d76954908c955ccb3e789f2a2a3d5ab957883af153ceacfbe04c97bf328c39220734394db28db4ad1143f5fd052af09aee51f2a36149223d2b994e535740bb620a31537e8f781a80c96166a577fb3669856a3795dc3ba328f909027cde2dbc01071a1bdc68d88e6605a5c4e2feb96ca7ad5e5faaf94fe4e4b54515bc4d42f13200d282ff4743184ac58d7bf56de98ebdc19be21e21635ff824c556d30119cff25e3a23738a406232cfd2738cd735d9f79c7b1aab77af66a2446dc12f95d64bd2c2977dddea52d2f3ab3deccc07c3791ac3d8190f1f2bbcf1f0976dce68b292f08a020e73ec0e5ed3fe2a301fb88b4df014c41e083f596cfc41852ef2b2f026c2e622275d2f8a63bf006606005edbbd704af36d38df9329f617640bcc65eb05614a5ef6d424902ad5e216fc5d275f993e4597314313e5fa4f97e606c7aa2ec072dbaa37dbecf991133bc0cc77b0edd7e4ee24367a4e910be08355da277b9c2c1283e7e07d7de85765f2c6453e2c72a90e650c741664d47d5e08959cd9229c0701550d87fb9eaf31f7b01dbc21ca0ee938cc23b3922a620e53da029c1048637691389ff9dfe87fda2803221deebac973d42b1946d838000e850f3531f4f24704f2954660b51ba732394ef19155986a1980e3a48c7ea32585243f855913146bc2903752ff3907f3186688a941365dc09901385104e38e91d1aeaebebf20f2cb467019454e86935cda6432105af49aefdd31f1c7781406f79e0efc697eed800bf980910029430aea827a60dcf1f1c8761def92555a1bc008d6e910a0895696632cc2b54b5bbc0a4a23b07554ac7871ad53a4e2cef65bc17c4fefcff37581fe142f807150248372169b521b0064c208f3abc3be6c95246bb6f04237785fab97719400ae9f8947d50ae7679096a1a2a59c666df7041a0f8f8672610fa4d9bd8bb03e409b746160ae855681046c0f5e8f2db33d26b159621de58886583f561dafc18780aa73acb6d03c39e4cda1d0e996b3d471a8042bd79c097524e09b1b91c06f59925f6f1596ec051dbcd1f4419836b33d011712eefe8bd2f9a0850b3a2d1d1262c1c18ccdd75913b4f7cc20f2c3f321cb2d1cd44fa44f590e745db27e9b6ecbd202ca32319c125f7f975da8eb6d999cb8d6061d9eb4e63e43c0a3088ab9da910940795fbc18e0071334b7a7f04eb4c218d92c2794706472f7f590a8070d987db610ceef1f0a67d5014029f1143541dda731673bc68d436023eecdc57d88c6601760a905ef363c3a119d83cdc92f82f7f643f29dc328ecc4211cd779962fb430f411903ad6e25f96b874230272e5d22c3902750798f9d604ecef1c848208f68bdfd10930227bcc255459e32b643dc9a9b6a4e9591b6581b7de69447748649cf35e20585cb1744018cf6c19066f137147acdea05a54a517ba75f936f2ce91fc6427c0ee67371a18021d9ac1ee7bdfea83068bbef7fe5e44612a2e8b2c873a53bc8f61205839e3b6239f4a0b934c417df361cee751fd98915980c43b8bce48a535a540232fd1496e9b4acc7ef2992e01b43a5e26cc1b2817f4c91cc83cca9d665f66508ddaf37f0c501e5e16b5e8d59cfa2e1c0291b8cb7fd540e5c85ae9074636b082dcaa633c48566a4215505923f7ad8d98e8f9a0c3bdfd034f09cd3a2d0f6659b1d31e031471d44517f4e06a8ea1c7d25892bfee9576b47a252fb31e55ad08f761c6a1c37fb4c7dfb1ba6263b2353492e87017eb4f8bc7b776bf0b3eedf12924025cbcb79d0e926dec98781067a847c4222004555db8ccf632b859f5acdcf54c01329df2367949272ff02089fc02e4359f14b4da7c02043ecdc6dcbcf6bc8255101b1640b10c3fcbe83f2022c701fbe82cc511b827ec6abaeadcb6643a68c20d4123ffb4ca646155506870850d2aedb9c4d32a037eeb5eaf28fb0c262e3f76a1a2aa4f6e7980c793d60dff0a90539aa94c777a7e9e8f28417bad16349d2161f5712fd3cdcd4fd2a09f72a0a1a9fe3912db808d98388c9ecfc6685be463ab0e14c2142f049df4f1628fa31cad4e7522021488cea8051925133aa476860d45711b6082146f4cc8a149ba9b47289aa42b207eefe0850e3244ac801320071cdd6e3cc285cddf5e04fb7a28a2b7a35f60c0ddba1ea782350f1900096001c96ecee95001f1c47a8af1ef6f61aa0e6c77344b206c6c86841958b9453801b4b6eb1282ecd06509df36bd55ea8bdf91d7bbd8746001d9e48bf9a9009c51306acd99a3d1fac25dd02cc4176dbf6074588416b222e2bbbabf68c0328b90dd79723c632eb059e1b123e32f9b91ebd49d0a48338b55f3bb9250dae6b81796232094ba6e5c3b3bb2d0d138dff99858aefcd966707a8e5f813227225e267b47d633dcf9e4a654b91", - "public_inputs_hex": "0x224ce3d9bca060eeade7369297dcc3b23ac24e688fb6d20b500077dca6b9e3532f2dd8535a4787ea8794523842a3a4f45cd84bc85749861fb660801b495b32700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000ed885dc0873d41b86e03b401da548aae000000000000000000000000000000008ae9fa5f57d2c0195f1138fd669fe8352a4f0c291b36b9970cead06bcf01559b0d2f7b3296daeaafc5edc881f8174d620a9dc790b726d4b30e4d8865f33f50004a47a309fa92e169d5ad235f41eb12542c77628973a693c21303168ba71bde796957f944c3a3494bbdb558527b5ba0c6071f98da6c5d2594c5ec88e9d1b4b90759aa7be42eea0aae2489f0b863dfba9b1cfb95c79fb965b14ddeae2ac0ae58e6f2b8af40f21222f9a964c0e33eafa2510fd2e0c74ac08efb87cf1059f1e30dcc9468113293de905e1b6098606e2dbcc3" + "proof_hex": "0x000000000000000000000000000000a629a36eb7110d0e90557a5b3678a07e8f000000000000000000000000000000000019d88cd448b644c854465aa5a13146000000000000000000000000000000754405772863b0382355b961b07ad93d2b00000000000000000000000000000000001278edddc7661ab9eea1d44b05df3f0000000000000000000000000000003c2a04cefbe9fe8ed4eb669e4a09d0520a00000000000000000000000000000000002aa96b90aa57715372e0c7569fecaa0000000000000000000000000000008328b72f6b0f3f86f82201e13783805f9c000000000000000000000000000000000009df4b0840b7a8d66a145976403e5602eec10bd3cebcd83ab40b69e6a359abab4c4326394d597bb770fad29eeb215c2b4c19d1254f3f7512559ebf12d4e10dc6412f20b64334db9655ca4ae6e7e803223bfc829bbc4cb6533f4a8e98f3b6f60c3e5ef161125e2ee02b91f2f20fd75220fb57d01b451aa81ab769beaf5b22f1e1087baf05ff09fb732b03f35506ee9b04cdf4afb6bfb0e4ec79f8fdb79745487901e1123083669904a2aef22df574db2791b9241d03a819a1cfcd5d97b416dcc7d1ec2be7c9be484f3b5e92a49f64dd0ad92624c61f2fed345068cdde399c99d3bcb37df6500619949695004615615e0c5153261a5dabcadb383d66365774e73276339bdbe55577863a3ce9bf745b7729c32b71093e6e915c281adaf751f6f8d3653560f2ad8998c761031de6fe0586031b7cb242babc9cb8089f0a7eb7569906e5957d85dde086868e53a7c2efc6300c7875e54a90d31339f361650a6734fc5ee5d1300e70e6235b4dc54d558414ec0c99107eeb1e9a30c6f1a3e68e3e0b656406e694b5fb9c67a8ace6490257746115f22e2ecb0b911ea0fd79c4cebcc1f0365e363fa1c83507bd55e272254a9d9316d7cfee96ce9da9916805f56802733fd2dda028a6a03ba8ab43bd4474a9ec02010949064033f2067afb00d04c3806d586d52ea362480e529ae5beb53f57071f2eaf7d413827e9c2593d1825a341db0c00593a302e84a7ffa168382b8095d69306f0019e4c06398a21a0611757fd8a8ae7cba342c63106c3706e3a38293529a410a17fdc4a65bf9399d744b4000fe82ed6a47c139e78899cf18421d05bc36d4f06911ffc36b8c974a13b322a7925d094956aabac6aee912a02c43d1823bc112612d2e20daa43467f43053f3b7370da56af2b8365b35319cee67168593d90f4e507a802d52f607f6d43a4d3509fd6cecbc83877ed954fbe1a910b24286997f7071999daef95d8676c88e22d6b7cfef65deefca84b01be1ec359c0386b03f5a6c230253150addea9a70f2073b634da9d441c36c2a63db7122bca9edb1a1b63f7df22076857bbae7f2379ed6d7ab10a5fdd0cb2ee484bf2441a899f8314f755737a0c2bc81fbe7c0dc026ba4171c508b0da95915756fdb555fd2baa3b56f337d5be0b7277e852e875639e45779eddabef71251d3f44ba3a6def8d1dcb79a9633f430ff7808f76997ab2453bbf82e62ce0251fd2df7739ebb47d2a3d6f7d90e5b60728663625d1ee833cb265ad4f79eb24e4d01877618a166d1727c021dd9b686ae222497b9e4200d9bc326937d031e9f4e3cc492a1cea37576b333a013813b903af2b81100d1b9a37f4d9faa0f67dd449b872af98caed9fd40b39c2c84491cc93bf055995576b224f1276cf6d71dae2b72786635498e1ae5b6d95a7c636bfeafe442140a4d4c42fc121dbdac3149744b41ada3fa3a49d84599bc0fbfe968a1e117f29bae2fdd4e54be67e088c456a59e39fbc5efe636cfd3edeab7b48939436638e180df4f94df5d1f6441c1d5b810800e4de73b15a1561f37d6e7ed1166deadc8a05f16837c6a7eb225eeacbf7d526c022f155c8264054a711c758d3d6b68e3ce510046bffb4e1d643de73c21efcb3db661262e3f5c095be55b85dc7333eadae1626602bc737b921d2d402fae3195f1c5b58a78a4a57fd2be04f9e4886fa7cda5314d6ab67933b98d530a7ca79d49b81e787107edcc71ed4a63579ec028b98e4790fef94fac44a67f8ffa4c8018484bde573dd6e1b09e6739cf0679dc96e4717ea019acaf7cd8952f486d72ae44f0e92f3e7de782d02de1a685586df84a384c0e517757b20b31f2fbc3144461b7022a90138579e0b413a03edb45b5472dcbae6d10e3b077891b40fb0faf02c2c2f71d6692ea9ce71fce5eeba32e67fec5199559b2713e80481134e872c14c8d2b2ed36f0e9b4855e3c4e554592c2f48a9956297d1ac15b903ce49a81d2794953f420661232e9b7d1e4da895de5e9cccc6a5dc5531eb1b4498054c55cc683fbc40402119d48a95fe4a4f6ba45d32290a0362c858a2776258dbb1851da9d1ac8ddd45a002f870132bdde4b84808d56ca42db80257c19db58d868c16a8cd0181b9594c1f239b7e3d799c8902325494206ed2660fc1729bf608446840be842fe3dd774c5c0e879aa928db3c67026248b6c9f16cafa4917b1975a7a0089dcc17eb8ec447f8acda85c68dff0ae8ab692c814b920904bcc08728f8f6ee552d79fc49d5bb184b847d13e533d9eee3af83c4f4b1cf0f3d0fa04f1bb0e5c889892e55637d409e0e29a0cfc38f160f906afff815a9f6e57a9902cf5b4199bd7a83af22ad6fc2e60e8150e4326f0a6663ae4c8c7edfb414d62171e04889b7953e8fb712ae58b164ba9f16b6a55fe9e16301a9e3b8f87e14e3866165906dfe8b66673046dd71ae1224fdfa2ba93a1fe9dfb2e6b59d184b0c5d48b229138718953675fb17a0d53b70c573867917119dcbef256ac1c91bcdb614cc51eec2bbf02bbc360eb04fb53e10095fd9c0819507410d05b00c195ab6557312011e98f272f6a490c0293c32dd7d68dc6fada717bc7153b416163faf77987e491210d96e4c53bbd1a9ff4886006765a5830d6fd7fb11ab353b2280293cff155d80801373f602e44dadfcdf9d8554957326c40c2fe9422ddea11925e65254436d822d17b58ce3449b67571752745fbb0c893cc3931191a9cd0cb2c804d476318a120d392c9565f4991d3056b6f432cfd59a48152e6b3f610bbd784eb9f56b839e713e97bf5e526e9d8f31b7c2781020e322809ffe0a0c30383a74498d66f7c309623a4203b3c2f54946dd95f083c23a48fdafb1fb4854078f12d7c6da2b48f3fc302dcdecd2743abfb2972ccc47be6c900310d796789842ec23765877c54588cf4003b4a7fcc2a59d79994e295f578e9c81a2a09b34ccb899ae391a9673b5b71da15454ab9b8362052f6125da21f29a8fd48eb889cc289341d8b83c681f5285a5429482df697aa61b11789705e755957ec9a38df70ef545db99bc3c4d665d3b846020dc63cd90c1f3b076a4d4bec57456d23d7c8c5dfcc1ca743e2b0bf2c7202ae10a5ecb54233ecf7682cb6587fed3ddc0c70e7ad6397f1b1df28bbc11bc9283309b5390c2a161e3ed68261abed5ccb8bc8d852d9306efbbd8eeee266975260f51fc908d6f6ac0955b68efff4aeb2ceac84879c8d7a7eb9d1dbccb3822aa95e721bded057245fcff29dc18d0355ab473fb1311e4a628c01b176a6c3ab014c524f262e2de65ded3ff130df96f8241f91b4c2ab9729533df5fad68166636460369508e27036830e90ebb92951f8a1b7ef0c044b5ff10435a56fdfc7e5c64b0d991117d1b0efbe045ff50c4c83163fcd530a4d3d3d81caf61c6633770bf18b989b0b23516d539c5c3506623aff6fc6ff0972a596b28f1e6544a1130a2f0858666cd919913ae3dade92c6221d561d67f9121250f913b10e3350101f8efaf6034aa50e2b73ce4b842ed5b77a886e5e37dae7a7430ff7af7c037486d1511a614958cf3c14865c0dad63f8645752aff6082c63aaa67f78ff4f096f5ee069643bf6420732116e4ee693fca555c9c453b6d9f444e09a076feea46db81dc7abba7e360f676e1e4d13b1bb288fbd051cdd2056aaf407e1423a39caa1469a769e045602bb321f13bf4dd6cdc5abb7802a470db4b135985ede95c989fbd13103273a3fb21af3630e410806ebd14c48feab3442f2c3e0420647df948f80d7f4dfbdeb282c81ea812710c4d3780056645ef9c01e5fe9b4f6b77cc020b2b855ab480990e7f0d5874b0164b0ece5e722d3e7f6b26305374dd115e3bb9d85b11ba498e3a7cb7552f71a16730dd5f37426bf75a871c6a808c197bbaaec8a9d364f668c80869f73716d5b01a18a1dcd00acb88ecc920781e15cdcdefef4e6a6a8988136539d0e9181dde520ec5c2f25a20a24012d0968303f6f230f6b50dd86f2fa5a837fdaeb42aa58c71ddd621f1cd454aaef5f6717073ed188f1acbdffd615e0322554bd1b810005fb2fea0cde87f7dd7e8e7ee68c5819070efdcbc2653489344566434baad50f045d1dbc9fc86f6a64f62fb7e592d2b91fab87bc5dd62622baf4f1a2a1165e58f45708631c477b23b258686ac89d567f30c31a3dd76302b83047f0c2cd2b230a388c2f4d7eb8a14bdf81fe300becbb1ee5331e6295938f5746e2ed45b7368d9ade851ffdd7c3341eabcac725798a2f6db19d1cc4cb5c792025086196a1d5e969697a1a126f40468f7703254a67f283f9996b976dd9f4cfb6e7899e8ec7525c3f622c04e8e7328fd97108e31b79207eafd3d984718c2caba192d863aff2ec4cc569aa23823825e814a46fa711ed35a0bc0b99eb8a3191ffbdd2c3c0865a91aed0c2c3006b793093641f63ef192fb233dc1e908667afe5e9843964a0f0588a19931bba1afcc2d1c867480b73684657fc421cd4b15ffe2b1b8bc372909d662bc0caaa1d1652cb4c225877aba882e5e85f795785af0c0a55df0d2a7f01a71a92cf5a9e6f0a0e40adae5fe6010cb88ea1ce8f0e543daaa1e2855a4c4dad218aa98e272ed52928ebac99da03756cac780ac2d7cf686373b477ba7fcd627b9ecbdef7af6ab70c8125f7c3dc10e06da60480d248855b10bb098ddc1a58ffee59397fc5b7b4c7154525a6617e80ec1ddfb325bebd64cfc324dc07e4e6f12272b88cb5b2c17ec40a90264f712c51b9d50e29fbb569709824e5986c44db96f2eca9975dc17a478f1316fd02250b3290c82ff37593bdddabaf7ca413ca013ff2c87f52f525bd037527bcc1c376d318fdba68afc380ce36256507646191a355ee1bcb5a4f35afe9e30fea1c5d99e094d446960f4ca09ecde64b01caab316fa5cc1e12ee36f64039882ed9d1e435737af161b8cba7cc7603a5afa64d42ef4226e9355b37577135766c0893704766cdefa8588bd1db9c5c056fb605ace7eb9ad52f74f7793fc59c39260c8d72d0535ff9cc54547e77b59a713871096f2b1cfefb77f548f7f25c7818a505dc6c54c13be6fb1d3b21e67f0fc56c16489872e4a622d367447a42f3c3381e1c5d71aae50d0fa528e7c1289319a90f2e32d22e0a52799b2ea8cb964b89cdfe22d8e9fe1c2d666e26b0437b3c0cd2d8d303452adfa39a1e83ff1a95d8d0666b0559723ee349b4ab7465a74154745590d96e152d6e4fef03acdcbe58d9e7337b14525b6893aa39b62e8d9713d7a4d7d5684b7c723992f152e7803541c6ec16b5025bd3a09ed45f700f945b4f4a281dd29ebdd649a5aa584419291126d23f18160a926ece8f0dd3c9f0080a0d9273244265c068c7b546ba7bcf314163f6e91b712122bac323e3e2b196d27ec418142da1bef80d75090c5fb594c9bfa1d763f48e2e83e93afd105848c0ab8cc1c41278c9447d22f3676e40fc87602a9e9f0618f60875ea756409d525228996affb541ea964e34473895e77264c9376623a2e8c271a0984094ed327c7921dc185ddb24dee890909e945c830789ee0a96e524366e519db40aaf46a37d5eff46622de17c076a23fc435f729a7598217a4b78f55ea4c2a373a86dbe167b76810b8b6cc810b9bb0bd5d8b53364bddeba4dccaf7453dee0c48dd15461976db0bf259a1c4da60ba3adab5bc8e01950ebb82a95e28c5683222b75b8f681fdcd9877eccab8be3e715884309b85e200208f25d402ca0e4e70607103c9076dd7dc6f92154dfcf7bfb4d46ea44108373839890e6e48ae3b50a231d340d8110f2520fe7ed40425cb747af1106b9747dfd507c7ca3e14ef36afb0e23e564841e713306694353c587c65377f858ae4c3e80bbcd32855bc682647ba116183938d01a19990e7317219f12091c6fe23d184d84fc3c419de37f1be091fd19a180839c703f4c61a44bd84bc7d738dc712e12ba7d706efe19ff4e3f376ed707b15161e1f4b9d00cb8bc7ba599f5d67c0ead26ea8010c167513df216c5f7902596e606d78841414cb84c9eb145bc2788aa6b5e1eaaa6e48dc37878a78c8023245feb5756b920f70408a9ad9545463fad7b8cde86b21464727d3f663082817d286d96fe9f71527a32841b93726dc33aaa60a7f04d030189369bbd563257a8a02d67b43651271a9c725621a4688bed8a2722e62a82c60d3df7e1c87e5515638f239de61d9b87270913f029939d89488091809f35c8833e719b0d47795c18666f23b8de4fbca6fb053ec017ebfc22e86294d0d6cbb8b23d3142a41fe4fe129a821f0b27528820ca90c37b91a769d6506f04c4909f6fe60298526a1060252c5dae1df9e791862be99bd4f962151cc5dec70abd7ec3cd6d94034f7b8cd2ce5134a92bbe200cbb1360b62de60c52aaa0f8a17b6a94a8cb9a9217dbeee51c40dd2bb7159f94cc8b24a5fa61b8816120f44aac0833702bc46dd7965c920eb02af437eb03c89af35247bda15e2d6eee0dfaa482245a261fb55c33ea77c7d67eeb07c2e62aa9c2e75a046b61e734a507d922b809f9e00395332703fda831fde72e561d1028a02a1cbcdf215c99897145a3a88e3e34ddfcdf708b3ebbc54c5701cc13ffc1298ffb9b44fe4d1793e7d7d10733083f0ec195bf170d57ebc7d4d325a5229fe10584530ae67e9393eca905f2642197f503940734bfda0b89cc18efe8b7ab67c3242919c15fa4d0e9a064b863e7bc90b91501e10ee931e3b54e025ccca576bc2212bfbe4df669437fbfc0b36c3f7d2c16a24ad8f360d6e24d25e7d075d1ec86832cadf4a0a71db54b17c36f172ca9094b864bc2643c58161debc34b73aa0cb2f920038c88fccca67564ac66a181b382d36dda045ec39f8c41989b9e1ef5b7b44207af902373ed595b4614c53cdfaefb38e8cc3f4432df6e6ecc7f7c6794644c490b39c426badfd69b7d4889a06c791c78c6f5f354e57c35f7af41d473076cdf1809b8be53f8f99a75476d3f563e2891d595758bacc17a55076c1ec75f552055f31971b668f99b954b6bff069bbd2cf39927266fe2e933071905020310b001a9b720e77f492af6959d6a7add4ca2b02fef58205cfcb016ab1192be5787709c0c8b2361e40b39ffc683bf63c9df450e215f6be913d4f7bd9f96753065d110d3421d27cf6357166d338f5bc73f3c25174c894aca96ca1678f7a415bdba0d47570fed057e825f9fd84dc69452ce212b40680adeeea96629464d8beb749628a666dfd11db04a30b4dad61634c58d6e421916fab36f273b965d236eadf00bc5daccaf14094ce2d10c4d92fea0916bfc9e23d9ce76c98f3a9e989e0fe364190f61250b762bc77263679a60f3ea94b1a4cb958ceebd4727aed306f5f4c4e091f4356794dc097ecf1ddc4f28fb6bc14779be8231668cbcedc43202c17dbef29f83e8377f8a08dcfa4f20d6d40cf634f710df117f95aa0efa18de99c148440839fa51fdd1571a4c4bc8f21b9c32fbbf69f24664ae9fb05c33a478e543cad6042fa2372780d61fae93f12eb7fa410ac034dbac12b9fc9b8f9dfc2f90934f4248f3b6230edb29063c3a4aa684fe0f56ef829a8ab481d06f97e21c2278b96f43826324aea45fdc02c1117bbe782508ac942284611c8b09c9790662fd56afe768298fcd380beaab0e2063cd0cd2a64adb3852fecdeda1117c1974622504f9070ea92c2af3bdf2c42d1f82b96074cbcb8ec05e5cf9cb9ccd1a84fe67e0051984b497e6a0cc663bd91a6076ec6bef21613c828b6ae031727e061545f108426e8a7c7ff32166484d382a611795625ccce2d4a9235e067b185814cca346cfd56a0dade8b03d05c850501143359ff9fe227b71e0adb521643a1a854e64372f8ba56fdd625d940fc1a5a10836898027d3fe61b06a94049edb7610f40d8a09d9c0595066a33719f3ca20060c45e5e9325adcf990be2e953f254e69b65a9b7d0bd12353a87645b53a3e88bd2eda9a86968f089a73158da8c57e359df7c44fa4c4ab0266ec04c6e308b7ace701fb2ab1577e5360d8c599dd2a7ee9afe91abb521fd56e1e887531fdda38e4bd2099f4d3eecf4599fd1e17757b07a6dd0d7468e37426113047dd075e1851043118603390cbefe628e618fa5e93c8a6aff52f5d14eca290310572e066866ec8892e7e864d90233847873ed2125d158200535333b0e15547ee5e73f6adfa31b59c164514ecb762d182b2a15cbc9ad5aa7dd957e62c2433f776a60f112eae5969142c94250fe15d51f5d014a744141817cc4727a4fba0817773a0f3bced3d145fab0af105975164eb5e0994cc282a5beb49b6022d1989e9bd0ba91dd828707abd1f21709ab5aaf3773837439812a679cf6e0932a97178ac4a757569e3ed252c20c816f772c0cfd19bee8251694ffbc193f3451023baefe4a8608a80c2052409cbab185521bb2775f8bc8cfdf82f26db9f7fdb3c085d3421d5aa7d3435838952c42f246f798aa7808797099e79e005a3687dd237c954bd7bcbb9907ae4a81189d04e1fc4c67e3cde46635353469afda8c91d033c600394526e11e758a4a20a450b99022088eaeee6e95186711835caa58a720ec503a9137870c02a6ada8dda194a8101cc5e3604d40cfae9ce55ac79f887eb811074b32200e40efc06c5b122c39dab1b7e18bccbfca43ae2f5def8cf38e5d5d785c29e382bc724baede95feef8e3112174616b731ab1b88e04bccbbaf12baf90795d3fd9335ee6427f8a68f496dc35034a50b4c2d65aa26c91e44826e4f9a34dc9ed8c0a078d50329851290ec7a9321612d1052e80f5e69f00a5b2226bde296f4e9340646f1f478821e6fc02b28f50286c3f6903aace05bf9d7431cba0577539b319b38c502dc206950bc06da5bda510f0a2d3199d460e844c50b6e63970eb424f39b3909e21539a4b383301027f4f2d15ef46f4c0aa453057366cf25a0b8752971370a4c65b91e7f3f75acb31ab58122aaaae1c55cc083897d3b9c87fc388aa06823694b9a33b43cf75698b55636601ea7f858e6d25e415b6a468337564930540f1e97985301eb7619d9c9895479a2b84abddf791174143889489922c1032eace4a47d8cba1f838e70a7190e0f1e30d66525191317428971c7b49cd0b8b9fd2817543d319340e53a99f178359c3aa0a6bf4dac7e007ec3454afcdcc3fd1282ae7fcf33b6ce5b70606c0da6e279ed5042951e7e8736c7dc59762208b71f55d5cb4ac6a1243eb0af86c4af5f8d8850d078a54a34601cd8ca5806592520049cf9fd3ab5efba90e4a3d8c7fd7a8fc4c36109e12a3607b205c4d83548ca569339cd1d819abb98bd58f34ff34be2c9115450b72dc874a848918165c14c4dbe693479c23e41396edf0b01528676e1dd91c05083585be758e0a42c53852690e176003920b01fffce897af3d38273b7c39d9b506f32533effa741b89e680f33cf142429666c930d824b97e933721ae925ba2ed11863390efe24a10514a7fef40975f262aabc5c81b1bf546abd5c4393582f7422a3ecbd5a9b162ae49bfc42e27a983650a358f53ca97cd08246fdee0f7828a77283ba4889168408602a059b4a4f42d052e62ee9364718b2be3e1ea665c1d009a2d0e032035cabd5bb348ee64bb9171fdf88bac40e434b04b95b6c376aef4b9a42674e1bbab7991ec29f944243c2ff731e8c4ad5b38ef2f741d336ca254af7f360ab6bcd5bf0662444c43ddf7358514cfc96962a5d702a6d84a6597c73f34da162341ad066887f958baae1fe3fe9be90fbc61d78609fa6c33d0add29968b98e17032cf15ab6a17cc68e80cc95d62673c0709e19cd5c65e2a40d54341f624927ae1fc9f315cb0257e98056419cdc32d0955e8b130b07889f5f341f2205810363660ebedec8f843f52b189b5e1a764fa4f182b6fc83a1acc7e4924aa61842c155141b92d02d0a981c17929ce26de73fa4551b312766aedd97a9d579f5dd5db01727151d151f985cd24d44a2107a19488aa04046ad0a9fa10b6d5e03fd8d44b1760b270e1db25c1076c9a0c14d0657ea01496be01378f83334e4726dac787945ee1c235ea77972ee91afe73b57847056d68e3fe5752c668b9a6d548300fcbe6764440dfbbf47ca357093ec5f3d189c9371a1cf1040ca36cc6855d1f59042b977248b07c2482782de996752d0e6a7849e1e8d1fede5bf559ddd7644ed1ec596cdb1b0276bd2280043a008b05ab803f0eae30d1e535d7539131abf22a2eaaa3fcec6c42edbd368ae7e30674910c4be899886c282e698297ff9702201dce5e3a3c448110488ee1687e41e690f0b70b6aac865bd22edf45f46ca92d0aa9f6f1625e0e2b82d833b11857ad209da83e2f35e336d5cdd61c84ee05142df44ce8fa3eb07e02220a6abb86a9cc06ee8d7daa9b4773f3b5a91f5b9bf738d5efe07d26d34d75a0f0786a91b2b599d84100a5e192c55ba0d8e623417bd48bf58bb7a2633b98c521a106415cc90c4eb1f220c47c23f4560d7a17d11187bac9a7a59b6982e6995426a2b4e876c8086a2281a4cbb369b2862a5052761f399295d7209b5f38f05c3c1522a664abdf05d3c80e676d3cad2a82e9ccfa1ab1e4ad52fe8894b1d0bf56a9d5c2127df88af96f06425d83fe77f8ab20ac539644020e6e83fcb173f7234b92b5e00000000000000000000000000000000000000000000000000000000000000001bfd2dc6576efce45c66f8a830171414f39998949463545b366d98f73ff350170caef73278f6d4c8440f535b6d33e94458b8e03cd328fa19ebf39d1c3ff96ea313bcfd087c1e377ccc8d9629d075d40002da2827f731a3c5a1070d0b12b537ae022a5b425b6b8b2353c1155789a279553a70d31043e6e07db541d588eb3d4a2407ec450e25b3b24c67f49cf778dced8640e628d02d7808c723354be00705b90d1fb79e969967e094d4985a70f5f93919a2d2e41e6e5a753355718b5b40bb9b301245c53e1b50aee4337beca2f32b982b57fd62dad5d0830c1a4fbb064001a96001f9e154fec60e4fa0205e3616a5409220c7aa04d2f38ac6199d3674166c4aca0be358b958fae9048918d6a030a1414c465b549f39398c808b1603f524a603df19243b4fce60e34a0e41f615584c1bddc881a58b035b1c3e31fb35823ec95d63181a6bf2d5cab2db18a6fbb6c45c9f52ff5c540155fa2c2633b62a93acd9a0fe1972bb1f8c5363bc13b059b14340865023fded9e9906f71d98249aa660d8f3012caec9e9b2b18ca2a1baaa20f97f6834ca0df0afae1d78efed7e86f873eb6b6426e0884848ce34a83004555f28edcfd891bd22c80823b71e8093061cdea3e21f14d9d283a482d3b667306bcf97ed8eb7422d4e40cc031ddb137e5aa3b02202d909b0f102c0ccb07add70b80f2dd603f05645d635e1ba50411f60d9f449fbd80e296aa76f5d0aa7acf8a49e9faecfe8877ed2132e94e73b9dacd7d41d23002644070c0f935fcd6ef76f80771819e25686beb6a50d5456b21fc8de1a2b4ff98a270d60174413686e069c7b059ba0d984827bd4bda181b643f41bb60a05e4bdb2da115b1c5d1288ad6008a963f3d825e7641db9f1a1bdf132ed0a6336093ef0072b1a1c696fceb399dbaf5d69d7377accf1841d6d3c3187a1c14212ff6cfcf3829a07f97626e8a7cc74325dfcd7b8c3330562695ce6608a23c8bad7a32614ead54f171bc121280fe6b5d106ae668b211749f09638f65c100c7f9cdb6ba5bc15bbca0556275a6ac98cbf02c2bb4f8bfb597aa18357a5660b280dbc6a82d81f14042115c7d396416b3cbb135753306604eb23ce58740423e5379d9e9cdb9ae87b4e8a1fec5d1b03f0e118e40d8311b8f5ab6cfd8f96d0b2aa223f7c2dcba42ef890d628b03a3cecd4fc3752442dc01ecd6b8c1e562255fdeeb921cedd9f209410f4652d0b435f7d385f39ff737d0131e186b7425c20d259a5a28406e9026416eaf0dd103983bd097bad59ba94615494b5e1f81fb652b17a1a6f40871c06ffa9f01ae50ad2f96a2f4f3ab3fda8a0e5578a030df93184925222bebfd4b6a615c1d751c6244f34f738517cf4f8a3b8fcbb06cc2490070198856bafa54d7ae45875d2e4400622513c100064b83fe61d93b85d94cd3f4d411da493dc43a04330e77467a4822cccd00dd3af186a991ac95436a9d59d159ab40f18939fc67a11ebd09ef419600f59c951d780f151aeff9ab7a917786455bdd599213b1fdf7c5880878cc28f6d0077740d763914a4069a890dea88a349e87919e03831e28bb6dce206b243a39422b66266e1808c08c60c1f19e6b266d65441dc6db73b1d99b2ddb679a8dd03530312840145514d10f1a8f2ea02c3bd92adb47dc3994fd5e65afd0558850f38ad1d5c6b7ebb989f18a062e7c98184388b59dee636b988ad0c13181accfbc931c02c3ffb59fb99e18ee51fb66dea29303ceeaeb39306176bcaf62770e02f94b1d22fee35c9cc543001605ec89f8db3f1a73546ef252aa9ec4b1f411c2884a13c9a06ac3b3436f2885f27298d862deef64a8d90be30f4ba4de182782cfb37cbc7ee1c97474835be3bbf33f23753baabb73ba1234b7e09411eda05e4678c507bad110c5ffb1971c23e2e762797e8d2490a5662e51cfe7ef56b4c035b99a8ad32342306c1d2df5b4f805712bcb1f77714fe02c0cd2289e5384d899b07037a9377eb621cd3b7cc50aace8a924acafe05fbfa034569679e81c75f3d432d2dfbb24c92a5165575ec5a7bbd02b41511373166efa882970e725c807d81e2890e328d8d98ec266edd94db8c66eea229ca2f4ee952189a4472e74b7f3b3d00f71cec023012cd17b442948eed2f8c3afefde784aadc07e659cb28b332bc0bb73e2f5ff2bb84be0b24d80c48ef45c7cdca212ce00cfe75fee7e00ef1afaea89f461154ec09e5c8303da51730fdfff21be72db82706b08073d8e43adac352b780b74018141dd4f61971f16b20076901787c2da69b4949dca51b8f394c4664bae066035366e596692ec3a2c57064e117b379255d641c20959817a15829cbfb7e39c2f43654f97a150b3cc0e90e9fcfeadaedada7dc295eb18aac33302eb31b27367da87d10a6d9390f28f7def9f3bf30ca9e88c5d547f5db05e5c99afc1c83d8fed2242c43f62dce0a283acbfe626f6b008921f1003dde9c39e649736179350efb1d1034a961e19b03c2522247b07a1ec0cfc161024315a3fab4824390d6f6dbf4f160f56cc814b412ccfcc6794cf1f4101a6852a02208fc9315d434a94ab1e7cf94a17fa34dfe1c0b6242f198b25b6b4338978b03fe32f62ce91fa4703b80188edbae95c811a86a261c60a9f0d27c41b759f23749db4180c988b726cb057511f5baa8c1b133bee601149bc438c3a755e57b8b9f63d8a74d7860bef8ff377c7d483aa56cf681fa971d6789e111e76cc6c7e991cf3415695843f6bad03330d0404056f659123ca477217b5b7327819a659aca214368ebff92a98df136eeb38debd5e97ec2bd8b820f25cfbbb25d0e2d0dea30e80d3f62aba2b32146f19b855104add209901a0a0384046535a202893042963c4d2c6ee5eb5dc7c20fa728a75e9be98c7b26d0658be106d8caa3440213d34e413b7d6ee246a9697d37df0ca8509d1e5391008b6655d918c85dd01b489595eb2db555035e381005455f6c37c3d1437c1cc1f2757b8f2b00f8c974985f9cd1749b54be608a080b4c696ac7f55abdad1c39ff5228bf9732237361af7ae7c942c315ea842aa71f1f83d53a8ea684eb70486d51390a4967c200e56bd4cf4fd68e8beb090c91bdbc70fcf9396d7d27bf608aaba9842c310d2d196a6d4ca48b7b452578ffa4378fbd94b6076cc46ea2f9e003ecb973b792c4151e2d6a57ac8fdea8f60753fdd2cb986ca421975da5f015e8679e386bdfbd180329c66626636a79b35e87395097ef7b76d27c5cd21cc43d988d7817c0bd70916b2e52a269ee076fba1282857fab43e46d8c5c1afceba11d247fa26dec2f9b66aa2c909426687b4bcd9ce59a925708d48ba4fc991dbc017e97de7625312a81e60607c6b6249ca120ad56224011f552ceacd3cf1d86950004285bfd27fce24070ba0d8fd669de98456cac54a304a9cda2112bb68a6328447884dbe3583027a524ca0039de468cdddbbf6fa8ecc20e9561e75bdb0041ee9f41b2e536503b77c774062573755752631243480292eec0b3222083888493e60e1ce8f68ba1e4b5f86b511e320bb1b2b921052bfbbb2492238d9dd044afe1a1c88b6f791a052271ec3cc51c32000d5caac2152d544ac334c887357c228842a0658781f497450d5c3dd1e421093131e893deddf52706dc43809881cbbf13c34e0cf5ccca6f10cdc05719c12a28eb1761cf4f4b29a0dd8f348ded683ba2a0f1a720d3a13f9e1d0ec0d657292c0012c1ebdd0f43ec0bbfda484fdce163480734a9bd59c9f698b8c298a3822526d06db0b562ca8ae9a65dd307825f0b02a004399cf1f277ec1be8ae26e18c8320b961a81c5ceb9b0e7993b94343a6059390aeade7ab42d0fc06137f4ccef4f02eccb6eb7fe6f1d1a7d02d2d52378c7937fe93969622bdb737f4070a81efbcc3138a5331d9fe4cd8f9140c73267dc9923b3da671a2ee6d7f147aeb896ca3585719471493214ecbcae4993400cd0b235729f7a90b40ad0d4377f396a0410f9a2909c928a2fce929dce6f1adb92e9e37cea9f0347a57014b8bfe8b7de4c3f41b78157822143039d27b97ed110404d098089d885c5dc47ecd7dc090ba3547b15577116844ccb5149ed44009bacb98cfd919e3068182b8464d5c16ab33c2fe216939", + "public_inputs_hex": "0x1b61aeb8c860c09accc9d3b206cb78415b938900463b02c43c590e01fb26ba4c2f2dd8535a4787ea8794523842a3a4f45cd84bc85749861fb660801b495b327000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000009c71b606e3534e1f20b720f72ba4a04300000000000000000000000000000000a9447fb86a0b375b772ec97ddaf912bc1344a7b4768cb3057b291d131994eaa7652cdacbfa289cbbd0750825b96178100c0ec371320ab18bef9527e8f067d634c64b646c5ccdcc1b6ad7f65abe7b22530ff38a19aba07f89241aa0da8ac1dde25ee4b6872c022d21a6827b7cd359a62f09d7c9200baac6123789cfe51962e4cbe7f8c6bab3936ee3c6e1e50892bac30b154185d801f461f9568e2e620bd28f890c097a514cc7843c7e2b5d9a73919ca8231aa9903031b9af03c8e42941464755bb0f5e07ac55a7f50a03a614b4e0561e2d404da0422bf0b0d0e1cb1baedfbe40eb0274ed52ca42822f4267851541bd60255cdbf485b04b82215e305c3af08705360c2aefd9a1e5d539064b0439c6eee102d6b0493e96a6cf3b2ccf7f7b3f9d23ba486e8675fc6a98b9414938263b23b7151bbdcd1323d901dc5c874189cd37b54f77235c9d7908e5271fa34a4468541d1ac8c71d73acf4c43545fe38a5b8104b533db6a645ad505cc362a9b682e6fe462138e7674bfd50d586d2c71b60a1254243ecf70e2f8a239a215320243234d4d91a0bd986a50c8fc4c4f5719bdd3e177dcf06c628a12cf957bc19dda77545900b17e9e3d6571969d9fbcf824cb303497cc73cdaf518009683e759f0117dcbd36c12d24463bcf83d7e6f7c2d790cd62ba716f08a23d45ceb4916714a027b63c0c9300822cc5bc1a651f62022d40b4b2b2f4449c1522caacf57d20b71d10d4c7c0d2f0e5ccaf796f282595666fe9f4151619c7f5c524d824b5aa834360a30de9b70151bbdcd1323d901dc5c874189cd37b54f77235c9d7908e5271fa34a4468541d1ac8c71d73acf4c43545fe38a5b8104b533db6a645ad505cc362a9b682e6fe46271206d84006fb97f06e10ef11bc431c7ae5e92579cbf01d430ce1d3ded707e30cc25aedbd41a661690c531173e4164107165bd6d38fd15ff71f1f74c2433e4a06ec4f43c34fb4e87d253e07920a15491bfb7b65ba9694b47c95df442184c5ba0674cf3cb2a333066124d5d0cb5b9efed0ea8f09cd019f44ae894099125333291338b1fd2e5f5d4b882c0d10c84cc035f73dc40e97a71aa2826ffa24de1aeca9" }, "decryption_aggregator": { - "proof_hex": "0x000000000000000000000000000000bc96d0538fca262eca3f3a175a2506ecae00000000000000000000000000000000002405dc0060d7ad9bf68d09752bd941000000000000000000000000000000ed8e01384c7d6b576060a26ae07f7f53ed00000000000000000000000000000000002f138f537e526e50b682f98bede894000000000000000000000000000000d9c70249009578b7dbb8549bb3fc5229b300000000000000000000000000000000001ed544ee7e0865dcf082904a91e664000000000000000000000000000000344b89579d5dae172baba8134c2dd49e13000000000000000000000000000000000002be76c3a8f17381dbf90cd95ae00512afdd01584e2937036193e1bc741749a3450bb3210a67cfff4cffb51b9c57bb04516878a8cccdac84076db0fd6a14ba2526b911eeb59fdb268706153f41878a0667aac6eed94876f8352d35739c16ab1d9f6bafea31c6f58936978bbf40dbfc0826c861f7dbf8ac3d317f69786b62d2bdb8a87b1a08ad534c70acab3857ebcf0993fb91fcb026ed3c50b587350ecb4f55255734294652a4e9922683305c6a0600cde5cc1bf5c3f4961262093ef555de0869886b2d93be75fe79eba7abf6bfed20fc0b7c24baf83f74a032b8373d92f21993d6182761f8ab55c4d85a070a5fd8190cc3878ac311c64fbbc0539e8b392fde7525d8bf17772eaae3c70570e522a11c14b30d68db30dee06a4d524c4ece4ff424e2ff9f1a69be29f1255c339c914016b42ba042cf43da3759887c145d63c390de00851f761ab9cfd9c6faf2512aa50c6753d21de84de06c37adab260f77b7936b0c44b6480a6ab0f2e3122ad87e310aace4034e34b53a49fc9ecdb1483a03edca34ddb794c170bc3c581a1a4f7e8e12d89afc87f8035d32a60ab5612ad0b4b231d8d6ea050a39310cdd764db045de1f72fa99e5822248fbc73037375c37fe2294c2bbdd855ca89587c43d05af40a206b187f8f88a086dc00a9c07a7159cf65a3c42a32218d9e04c263666bdf87e8a10edac214abef42f85564bbadcfcea8ae059a75953b735fd8e68e571035c4e4c2faccadc09fdf9ad93ae4226d5116f60aca908398357193a73e90883cc3c7ff2005edee4d35d841ca7dae7e03b062d50d5586d06085138534ab3e705faaf895f0f875116883ddbecd75741011371a16082ca1b49d3404f7eeebba7d4c7f88e8930324da8888ba37df163c6151b13bc2fb1c316e2b12d9f958e409514c248fa290acfd80696b225ea88590447718ec17c5b796bbba118abb21d6fe6b076634a6c23904bf3ca14ef1763d05c406d58aca932df42f80011a768e460d95eed48451b1bd0780c7dd3ca1586c6189093c11c4bf573809590caa9e0c5eaaf43e8a145da27b30a3cb1fab7c7f128cac57f5548d3e3c67e86a033535d8ca84ecc128826a21cbb30ca2f1754528421714ee134d69f02948a1d99cc6f10efae0f2dfc3f9bf21a717fff53305852764acefcf1d6eb9925a2102421aa0d57bad423600f64697f07dfc45cb3b4d655cd1f6b0927ef379cc6139722c54d03d2ab8bf8c5f085a26202fe015ea1c212b3b2624bd68aad04303e80708a1479a4745a2ab307d5826ceb07c2b6ee52f163e8af6143847f379b07af36ab5dd16159c42682b317980fb0df0f49a286b1e71b3002a5e399c2ae3324f93267a4a21ac56f74b38dd83f9a0d0b2959020ac19e1e580726b34c08ec23ed72473a46d82e7cc436662d0b75ea11fd2802c4e55bb1f55616fb153a63d9693d06dbfd963fa4574239af428a1f0a29c611c5a005745e62958a4a552236ab2694f01380f056b915f7aba92a5afe24b9402b4db2b55a8e3fcae34fb449b89e6897c92a01009b04702f538ff6e71b9767261d4dde7fb8c60f6f88eb6258523a22bca4988ecf1c541b00aa0ac1b96492ec7e26b4df0dd6b138f3f2328a84c6952e6dc98fb0636bd9be7034bef275f84d46923036d1104f4af82d2bbf9283f8ed163cc63c5aafdcbf21cd4b3ac76a3cae6cc9056da70ed5f90febd1c99afe625966d1ab565b59e664707e6f838b2333c5c2201bc1c887523d29055a3faaff2c2d86afd560907bc4cec3c73748468927d337d003ed9e69e4e6ae6e3faa1a9e9716be6ff96ea11683e9094bbb98ccac4e457b050aa7cb7d9daf60be0a1fc4cde853c97043f77d2ee85eaf570c23fc3ccd83c6df060d99bda068172b6aaf5b558fa6a2be31badec375d70317602b499793bdfbe812d706dff901460d088caac4689dbd00ceee3595cba912c4e04478af243b2d5e2e382459e2f419cb0684e2579bcf295f6c77bcaa8184e3078b3349d017abd4d814d49d78466cc2a395c96c3dd2e060015b4a076ef5079dd51347dc1e1e35b9ea1afff659ba63fceb905cec9dd228cce84ca063dff23d25778ba39e1e23c276ae275ffab5d82f63526330124d69ecdbcf83902dae2dfe74743980da6f4d1384db0cae659207173c164f6afe28f376ee52505ff83abfa91a221eed248404cef7a603c9d392a76090f487d113d6687af57a4740df5bf7be5c51fe6d5facbbb05f230e37ba78d57eb7517506e8c67c42a6d7c28f3feec0f8d8a6a3e4181c4eca35331d2b6a6d2bfd4d8fc13c74eb3df82f42eb9a816d946d8949b9c0773320f9aa792f4743fe52af9933f434962c5a6d6712d99216f90b6adfac6a5800c7f8aa30852f67d3b82b0e334de0e26f5eba013504b4e9f925b0ae39095d5a3d79d407f0d52edf3528813001cad53db592c38f083c51b40eca3f8d271fb40e0e5f30cd0f523023d6e76357fadecf7dda8fd192a6ae41963082cae9117ddc7aab76965e0ab22c278e0009b458750f62e589bb2089ddefc6e1b8340184e3a43cf0350aa377242ca6204f0a81c63b4352fe2c5b72cfa0e09377fa45f035db35b61477aadeb9fe2f95eefed8c915412cda1f26aba74c1a858b7440eea4e022a422931cb14a747421a98b1b565ed1d352bd3cae7404347f2514c459b6c4602af12d0598d146433a1e9784c6cd77f9dc9e59fbf0e27323f371e0470912fe7195183794094cb3d4c6028baebb5030cd170a4e20ea8cfab9289e5675d2f20b3f69afcaa9271f854a4d16b5efd806e893a40ab731e1c410cb9602caa37b061943fa5dbfb0cacd4b981805ebeee17f74569607e869df9e12a7c7dc61d0a6334c23c4872b75df9151642a009a9b4240bcedef09885e69815370b8dda9d2f019e87b55273b27a779c32c3117680545e1ae213aa888f5c0cb23f9f975079dfbc54a7d844b6bd248d2e9312f25342105e1cd058f08c1a0d7ba857320b7e837283f54b0d88e788bad0416c60c002dc6c60d7c62cdb5973ac154e2fc903750e2684f43c383013936c03d6c91d32d6ed02382e18ce85dbb092bfc17ca854e455d496784c5e8de3faff988840ac71a52cae9786662f618ee88248cf3767dcf068d1ad0ba60c0f104ffb7ed7b47b22aa2af169a69e8b99ab3252377cde793f2af45a8b053d981d075b6338dac083b0ab8e963bc7f61050bc43c894243eb2ffe0df3478edf5494d7337a97e22c9f4314a2a1bb29c6df3fdd996ced04a459221d8eb83d0d25321decbf3077ed22a79e1ce73dd18ed5e66ab2e9ee9868f60f13c6c61a1a26c514c1e720563ad91c68c812131a5b6802fd63504820c21eacd850b1c84010ddf1f1df60f0f57121e1fedb0f40e1bbc592fae3071203d7d6deb2593a0932a1679cc59c6ae2ce3cf3ec2a0619d02d05f1b1cddae0144baa68bcaf11281ab3eb23a213e472338590271947dc012a1c39101b5f09b93578b8ebbe79323ff1e76e73d5f633a3d5236c95bf61fd24ba8abc83e67d9bfe4f8ba975a55deb8ea9a5cbc90a571ed40383c5cfa2d88603dd5fce0304dbc7daad12b6c12da88baa0572e9dade80d10ecf1137278dc5bc2ad88e74e8b34a0f80e54afa325023a65d6d7df8c282bbd52284bf8f50dbd7912c65d8458a87811522e8e43d1947ea6e08827d23c94eaa749914666b491a809d092c13f7a14ed9b84a855c67bfcfcd41e0615d1b63251166709be28dff77d31d0931c5219698d8d0701983b502488eaed66c57f3a3479183731695e4955e7e1e2d67244c478d1ce81a97d3db0459a38bd3a01cd69229d4cb232d921ac20a289219fd2251b56dc54a749bd8bff0f5315863ed08b11e06b08f0daa8fdb51b045a520d3c9b2a069a65b954561f3e3404094a9d3a04466d91083ecdc40f78edf5b7d1c7b6fd1bb013cabf8e69a4fa265a7f0b1f1e6c1936ca56367b2ad609938e4421b682ddeb2c6df53c24b4e60395cdbda337471e80a423c9d1205d6774d9882c82fb717f2655a84d4821610875d973ca834640b1cf337dc996847fc2ae8df028727340ea87796f42f8750ab8dc8f93d460544bde04f1f5830b9f666b26f1fe523000637187ddbf9ab82e990a0c7419885084cc6bbd47c9cb3575f56d43d9a06942efe9fbc02bcd2b38f1be1f1ea1753e68d197e713287ed3a1aae35fb71b6ab7b274e9fd1a6b64ba0e0a2b0c9c499034cb6df66c8bbcdc50afa671f374d0701980838460c129b6156066fe569870c00e8bf243907309f3e9f11027fc4b8e0499922061d481cc584a6048f1d9d7801e5e510db33eb3bd0cc60f4df3198f5de239d0deb152110ae11eb3999e77dbb63fcc25e551cb0df977aee851f7ab22678fa761ade737512fb1096f839cb496f2c6b176f35538015be8105f785a1fa46421dd90c19d03f6dcf610e7c0a21987be11c9ec8674af552a9e8bc70c6ebd4dda021262d3763b8649d418dbd2ece94726127fe3ab67b77eefdf0ce5bc815e5a9a14266256230b240d837dc108e4b3c33c2156e007efe28b89d008cb4d2c3035989274102e59040af902839c1d02dc9028415f545aeb95e2723d066046b1e6e309524492466ebdafa5f674d357cc633a04bf1c5e2a2e46d8f45d348f6b007f110c66aad2208f42595eed8f676d1402e80deddb96779baba3606acf6b8f3347ae89a1ea024d2634d631b7c11ee934fcb194740c963814f86cff8bc7ae89e35f5e4481bdd172114e526d5a14c317a3abb260cfe202e071ea270024a8dec7d8c27dcedff3e1256ad36f605d247031b5b98aca084a0ccf1ab16ee091d060d674c22ba9bbba31ac39fa2c5ccd1296cfa5206ea2ff71f2c994c43e4a13752211efb3ac8cce9380b26f3a915a74cf18055103440aa5e42eaa9a0631dd17dc08f6326e56a148a24292b155b32e860804d721094d93d132eaf0cdeea60406f83ed7a36894d8cce6022e225a9301fdd6680543eee7563da27e229fa2c354c0abbdd0f9b13f75db0cc228c030bfaac76a0a8e8d1e7b7507cd164ee01f9f134d8ae0dcdc769334d217f0f02944ccb8d42f84455f74eaeead9a5c5d5badd98b800f888f0d5503a07a1431782b6b000cbe3f90a219d8d9d385666d9b8013082f47edf494f04dbc682d3090984f0752cff98206de8c311aad5217198a5032a23c864771cbc64c65d89f83e270870edd20a16af87f484e19c0b4a81d0d9fa360dc0201194df07d6fd0699d816e1591946ff35e2ccdb92d7aefb10c6d0977138f266738413a4002899081bad1b58ef7663603fe04e34df7861b25da5970153d043f4d86d55ebe37036ff0b4c1f8f79db062cf6ff825d1dad33dc4b9a98a901b6a6727d846ea030fd6116c03024803db18d13e4962525c1687a0b56011fc0964ff6a3738fdac2c03b75f63ab91c36ec120a66f931065dad13d2c00becd94af8055868f1232c89b0a7fe108ef42631c25c926cca36efee47a6ce4c65f8cc65ec45f6f4104a738550c9e078d9d321b74b1aaac36c7cab363382c50a3ee97d1484599b98a16d7b535ef90b890a250dd8195ec565b405a2e9ed233c93be8ff18750dd0e4464b0b79299d6e53337d02c9d5630ee9d8cd206788148a626e4451cc7ef3f9f467edb204601cd4c3acbc01be12f30e6400a8d924e4c887539978dc987e537cc388c52ad20db05a2d64fa82066d23541a96eec5f79f542aa2973ed771bad079190e9f3a9d0f48f5916aefa0822282cdb70e779f9f396160ed0141952b6274a8c05e380309759c962c2269d261f7713162b208290936dd9fd8df5dce134905d0a8e6bc27244940a1b7e916e13d6bc0978e8b25849fe2720b2dd10083d04ef1c020ddc628ecf7d47291bbf6808e411c2be3c79d2907e2fe292ada38239655626c449e3ba0a47225fa635bc2e28f19719d31980c6a6901ac47c1736e19b9e066485986ffd953f3f7eb09545cd0321c288eff02a8cb675719c0bf9a5c975dfd95f71df4432949dd382b0f475060b2fc48a2f5a87c3bda8d2256f4f45a171f694a710df872ec60a806c55cad69502a3bb6def08e48906645dfda0793a14eeb567ff7c6cb8e2e49a5cf8f74eea7c0b7a572378316a67bc933b087f1a126315f50b790ddcd96794f3db597c284cb103e93f1a1deefddfc386d2e37d22f83c0695ead19fda8f58e9c88451efd2d5da012e9e178d13a34e77f1d8f392cfd12b2fefff4882a9a79e7e97266ae20d4db900430c1551c5a2f91e4d5f8f41b32db0be2cbdda94d2c6a3b1387c3a40e90a1c283f5bf2170fbc21a71c22e57cdfcf7d23a069b2e58381c0e147fb8baa5e580a14f9a027613596f73cf997bad1aff33bfe2a79dcf467f103e41ce84a95c1f2f1114249ac71275e99cd5ed73935fdb0a4b728eaeb12642317ce06ad5c3ddaa44016ac4e08c5f384c67284a724426f92654c49f671fee576746f9bdd429770ae0d0e90e3e0cd9d18ec2342e8e6f0e796be33d8aa67304a048b165409be1273daca29644ba681676c8bc306e0aa41f5b87549657e24d9117b4309d2cecde487946e0ff7d25a1cb33d1d841dbcee49307d8515b283ce87e904439c839dfa050243a92d346b7dceed618937270a3e7c870ab80f626448e2e8e71e238220480ea26b4c1e2b96d7205d4944193d951dfd41cd9081f5f9c6907ee8b21b81d0739a51cb301b702cc7c529441441183025abaa3b5f939a2526526ca413d7750ffdfb33fc4110c1acc042f1af276b4ed380035e141a8ce3863dadb0fff0d191658d9c69efc0265f22b367262d633d8f855231e68ff69665617ec44a572420158af96b344c31156860db0e2d5c18aef202bdb2e917880237cecdf0e443bbd236939fc25217cb1eaf8e3ac15e3637bbb78a2ccd510d75eeb119cc5748a4d7dd1524f98db2aa7b19dedb9cdd1ab1de33c47393dc0e18505aedfa9daa4dbdc236240d3f8203b95c2e454e7b7df43bb41928540606282ea508b2c0f4c1a669f2a59f5693e78240662db56e6edaf2a693922cc06349d4c497abb72e1893ab12d8fe6cbdb6a76eb9d2206523420347b845912fcacaf97c5152f31ec1b001a3d8a7711feacf4b6f25fc2bd34d46e5318c7f45d748a1fd678ce7bd01020bd9a2418d7cecc72768f933411deea420542f8c26bd33fc20d1b9243b4ede6fc3e6fea720b4e2840976f7e341287a45eda3e347e08d987e0152cca64132234ba71c4b5e97ec5dd4290e17818025c827782668caade4e586c97f2a448832c052c3896dd32420128b44435281440e5e38c2b6f49806e90b91365e360ef6de5b97965dad8e139681ca0dc9ca1de4258a87e2c896a0b26e6e839d21fc5b65219344822119bf1531542d769b10e5512dec0c9df046d250a8bba907318336f698312361def22d821bf339118e2b002d230e89587f62d5ae77c9b8b57367999ffc454bfc63f6dad59c99a8934de6861a1e798790d75fa4b7a9b2ed0ae5975b3d8cc86e22d3bf382043c2347d4b23ef331958e83a3735259a7586792852390b8bd9ea465085f7932a55e45616dba802f83048d7973b1bcfd0508bd05543ce4f91ae218071632032a84751db87a9e156280a50058c53190fdb07cd4da0b26e499565152a1fb37347c2a2114add8edcd22505a125015a3e26a93905e24565fd25c9171daf5c562269aec392ea86d1ed741a1599dec50b4352e6c441310d598c9712b28fe526b0a84fd1e7ae52f289b2b69a06ea30bb6cc1d9c376caa06d04f168bc6715c40d124590baae60671e0fbe3c7e117a430d21c524388e5173e34890606d0e54f353cd5f8a80fa47d095d2d0ba2c0b6345b284273c77ea2aa727a648441312fd9d8401945e3496068855941d03180c4cb91075be6615770a540d19d79f5a594b6221c1921b14b3a047440dc556790ede6c003bf9b126c49a782d5c22f8a322237bf2ffb95284cedd81f8d80fc5750faa0cf58c06ec0ca268d39537c0be50720152fea206b50ce8dd9a57d6abd43f02509ba7da5365cc43024ff0bac39566f27345d2b2364d078b038fa67d10ec131f24167a74b99940610c56dc526c5015ff54e4631facbd77e18d9031b9ee305226a3159deaf9b0dc2fd3a7fc78ade27c4c2b47777bf70c9758d45597ba13c3f71d4da0dd780d50e25fdacfbfb74837d19438a6273c4dcaead1d06deeeb9c948d130f064ef140b9224911cc5485b8119ed8b05ca81e239d11d83387529d74dd23157efe057e6ead118ca64aebba45bbc9bb758627a7f3ae0db60a98b6364074810028e5e5e6ef4134c9b181245044d9c9add82630fab4f272f277422c86e1d4842c1c638aa541a85119fcda98dfd1630ddccdbdcb270fe62ddc74d29fdaded8950336a2115c4fa72410b6245af33b9c7d8c41e99b3b22494e8fd96cdabaa162560306064c141b3d6160cb90356044fb43ef0d88439a6795691150722683c9a320006c2492070bba3427555d78f123f512ae93c17aa504fe1557d296fc19ae8f6815b60ac4fd7a0855019bef36c6591f06dc4a8519e726e8c8b3f84e8abb05d914065bf15fa5b569ef988cd9b391e99f911fe964a06791357f443331dbf35e30f117e64fd3c3674016862991bfe70c37d8778ff8123b06bda8256af0a28ae020eb27dc718963085585437c63ef0ffff850ed510a0172eaca0d650a17188331c3e8199ee02aa2b0b0298ef73494a98310bd81f9e6c88372b0eb01c74d42d75587722d656ac88684c418c2f5e46052783fb90afe058f923aa967758644f04d2aa8ce16c0e95aaf8a95e7a67fac56e7f9486b49c9e9fe9b2bfee60301116fb47fb5f92b59d0fddab29c9b91018f9dac9ab7cbc927d1be9be727a3e780a3232be97bed1cb986e63dcb100b8f451e940fe4a3b400a962bb9456daaa1c2ab63d459df45e30346b510f81f31f0a29571156e2de0bc46b69cb05a1515c912ead1ad59929862aedb43a1ea0ed151372c91814f912e007386f636f1dd30328c8cabff2211c5c093a74da592b9d5c5da83532d8d2fcb24cca63f7696edfe5573752436065c07a1138726222e334d83eeb5a8d09ad7190589fe517afbc4024fc79d8d8c903febc2ae38965b2d4e7c27112c6ba358244cb215b0ba017c9933b2da4bad749369a522c3b603898472f8e44daa4d6733f9375720edf5f2f5df0cddf7aaff381ff08e60841ac46d2cdd26df208b1b81b5b4f4830959ed8c4aab9323974249bcf8e8419305bd3af49bd86b4dcd985bc7eb86b650a16129503dd4e7ff33def895f266664298bdb6224d743704e49b87c73d364109e8f1e34d2e24127c494a712267cd0892715686c0075b9daa2b09b7f14946b1b1bd19c0d3cc6ad45fcc508f8a51b005c2fc5e7942e8b4b50598ca0ed1b5dc525cbf38bf7d4aba56ea94470b4ba34fe5a15af8f67971d9af9cb8735227d522af0489f8e4ca6b46db9e84b10d8a317b272197428f03dfc19fe78c9935d8fb9c442d92de470355afe58871c768b2ab03b902a4cccd217530cb9fbf0d5cd6624eca2d724f6fcec9103847e3c932df0b073250a84e6babc5cd8b3bafc3045dede571bea8e9276fc64d8d8cc21545908ba564519598726be27610b418d8c2e836f557a80531cc031a1d6439ce57d6fd2a4e2db0ab08d51576c28e6b0b6cb1fe3c28c26477cfb8b1d0431d3d48f45cf0c6daf4f0765bcc41c6b82500721d06786df1163b97e1d85e3d81d1cf599835d7e14cb02206b23b3fedfb8d203f983966029450b2a88496d37eb2e080f7b28ea4a8f982225c84052d11f7f9d2c38b3f3b9bc01c14711cdc78e9448ae996bfc90dedec04c221633b7ef1ac3384e1899453bdb52eab9f89626940cce87a7b8e5c821b4523c070383a1560e84d09d7137b27f25ee99397a7f8693566cdbf7ea9f2505e90b8a1fe800e2d6ae98cc779fe708ad85e18b110d5f7c6a2afe0c8e02e0e6892facde01e80d5d925a6025dcb2af64ee3e2d806f4a35d456613bbed9c5605258cff91e0119fabaca716eb23e06411015fcbdef1d8525ae8ea158b2ae1022a47cf32d3d16f8ef754e6b1855dcf6f35f8b1bbceabf2bdf87cb89bc22147fcdd760026aa92c9b60b03d87250b91b888391083cba614a1db81ad74d8368ba6d5ddceb8bc9c20f45a28b04503b38f4cf95b9858288719e95940ca183ffaa69daeb24b6ce26928b48958f9a3b94b0591b4f3e08f9d05d2f62673f8fa30908dc19f48c6dec02b2a89f635a4538f8dfbd510fb12acd28f8701ea96063968012db76bb9a11cd2eb0052cc417afdb657e983916496acb46f22e7bbd86bb576136ac22ef674b089402cfb8b89097aa787003d672e8a0fa8f73e701105fe0fa4a58092af72262739c70e6dd8411c9673c779a66cdf0d66b5a5a75b488579fbc8d8721def4cc0934a212c7b4671e01da0439f936e47374f3bc6f0c5b694409c96e771159a7aa57d9340122c6d97e82a141944f0c074bd87da8c4ac369e4aef92447f98c98420720213a2a98a281819fdc918862971501334269bcd5ed15d5d405b6c8daf01abb4b6362303788e44f691991050045f7492a309818f327245906f8a58866afce03b7f4ad2aaf55cc58cd9238661122edbde8249494ef74653f8b269a9401032a241902e3000000000000000000000000000000000000000000000000000000000000000019580d61d1c7753149f90f2da340c29216008e1133d65577b3d54f48ef3f8e3216a452c1c48a73eb1b8a5e558ca646a5c30a55ec71b3ca8ed757f5efbfa20038069a224d1faf7b130574b42e45ecf0083740b949df53b7ec67a1103998b4d6222fa3fbb78aed33c874ad1aa4b87633e0296e4827b1d172d483fd6a0578d1b32a29f5bb5a197b3c0d95972afc77e01d8f53452a75c29bfc9f509d582277958b08138f909e37e46adcd103ba123a956c683d076110fcba213a0e40e0f5453d1ca10ba45436129c22ddb1df1efc756ace1080f7c658134f39c1d42fb75ae077bf6104e76169480b94f7afadba348a55e5f784ffdb41212c4f82454aa9a38f8bd0e2059194ff8225b9f1a37f31e4d4d98945713f15cdaa072015b3f0c43fac9c79e30dcc3a6a64b8d8733a3a348e2a60d407473131895a48acd85ecaf1c1a7ff200613e3bfd0535f009022889a42430e8bd1e7a856a701dde6b4c651d506b4c41c37183a41937a6b2af6b4b1e6d6e2d63f3e978fedf204e039cf2e9a522a2345f6c12efcc77bb4acede0a742718a1594d7ca3efbe93139c3f9fecce6c2c275517bf82fc3ece51941187c8ff587f13a040fa2314d58e39713a328e2a755c4813c1f68057c19049be7f137ebe9f0ad967767c694f2b49cb74b8b2e204f4223cf832c4a0d9dbd8cb593dbc44bfa78bf9efde6d4abf88db13b083734bc08ea0227adaab728678afafb07ff737870a8dc20078f76309f7b09ee9a7e0a9952c7fd82340b2a2c7f4bf1a4ac1d8e6328cb475235dad988984ae8eb687d788584b17da606e9e3250089be90b9b2e86c740af3822d2e3c490042c15aab381a1343f9295515a13617c992885dbd426c8f79cdbd61cdda01e681829ceca10649af6821e62e465df72f5b0d9b33c7d6f414681925ef28644d57ed54c2a093bd512d0150c62cd678f72ff4fc39aa44c5c065a536a2b44244e28551b15f9d38a262e4abe9b0b8e128e42d910583179b30476b2997d9a38fb6b59db6a6beba53466417d08aa099f5e5fd2150e4c0a87b3cfbf84b0497cae44e5f8763f5b7c13862c101a8c82eb48590e21c092b1ee129636cd99ca0cf04d7bd67d92fb43bd47c9f7afc416edba68bbe8912f97f80acdf13fcc645f50a61af8431a2ad354cd25d72d31546195f1c8c4f6e0789de44abc1b869ad9379b950b146f2e89cfcfa1ed4b126b673c68310eb54da16cab040f8ce58d8a03b4d801cda72ca8fdffd75480870e328979702a922c62419788d106da718405955694420022c5e0e7ec413abb99480200ab8e9c53c77d01e438deb0981c790c12ee463c412d00ffe4045c5c6ac860fae361d4fa357dff124030ca85ac1fe1e56df3e0e0c044b5b951f33e97dac8e3f6365bced9e27b5851a437605c42d6454f9b568754281b40f123694d46bd498400360d03ffbf22eda152c42abade99d57468bf0f395fda8e6ada8aac3c8c9a7e36d17f56233f945b5291cc8888930ec5f4a5b2937da9be51285811b8cad5e677dccd40fc708eca00b0388168c509f0eff3444aa0c7d74b91a3b2faff20de7d2046ee07a32c22ab84c07e0a078562ccc2f34d563fd03e2cd821aa50c5d0f16c2891a2fd2e5c9b514a70f73f0ac86f37c58b5eddf34d8becf6a288fdcdeaca88248215e68032e36374b261c214fc5f0525d345bfaf2d6e5d59922d85fe851e2db240a427cb7853eb83c2cd2abc9cf87de4f6cb384d34c75118aa95dd317959598181ac708c8a039d021279aaa67bd43d6f1535d8ebf50a0bc56a45a888d0c241114d56a3ad860fca14005433532eaea9bcd9e7f8a104ce737dda9de250a5cfa7f77bd625a2c4df302db2fdf6e3b234a05e82e66da226c39d10d241579bb94196c1e14491b1cd072f88d1538af04b9faa7b2de2c2ec1cb78636f8d94b16b301603f181f0ac84ffbb4ae421f3c1645477c176ca241ba14b2d6c95da6816fa52c23fb2c6914887b11f83b4295fd921e5c8e1caced07c0efd3763d8a6a221bea3b2b253606b24a5e0e8fd7501db3cac38421f49e5698b43340fc962a6113473bd481f695fa1b8352c6b5726205169159b02f95c6233a7bd5ab47a31c3894f91b4f6e16e0b886295442d718309cdb643a5b2af5293130365efa58da6e0bfb1fde171db0c6e0d62886c7181b42377c265ebef2b8c59f5b0706d16d64dfae1dede5a8fa6c0a46a9470c9cfe7fd1d20bd68a7fcf32994ef60b3eb2e092fc49f9af7c9799d4fe72b711f662bebc614047f226b6911ebad90e79ebcb63e98fbdf3ce1d7572ffcbe46658d03620f940a0e9fd42a5d129cb610fd70e577a870de2d0c64ad65640a84e47c812fd98d8407cbc1a19c2cd34ca02b231bce720b7d2556da42212bfd0065b201b85218285912f2bbf5722cdb7e4eda59a65f86aa5a7991c90442891bf88cb706b06c1ba7580a81f17ecd76d465dc5c700e6e240caa0c1bf0f6ee4484ba6f154050f5267300302c2d7582411301b84d894906745b96a4c7baee6ae1e9b4e1fced0ab56ae37a2bf52e52f93136b0696f5903823d064b00a9b6a61f18e5dc6bcc940d0cd2fbf12fb21a9f53cf49dd4cb64c411aa9f1cdde43d7d3924e0260aa160af8d89f5240181af0d1572b3ce9e4866d39f55dbe6da30cbc094c4eac1e9798a6c8bd80e1c7085a884a77fb225bb559f52a9361d2f2e0937548eb03e9e7902c31067c3047650ca87e165b5a6416bba148e3c78df82bff1a777f411b5fc86c1449fbfc899e3b265acd250607a1b9c41aa27772ebb89adf3d4cb4280ef427052a7c32c622bc6b2633a0f86a2e2bdeb62c0b856667eb0fb609531fe1b4c0928e0ea230b88f879400b118df4eaa7c66153a1a76c30da654d86819530431bccf84be8a140b1a1b5f0574f6be68ac394b019d71f7a8b0e855e62fb22fbb1577059ca3f3f73e624da50dea5f2befb0a893ad9aadc2919939a61bdc986d65236db203707173d84d033f0d4ea0437d809120b9808fca706e93a9ee2b2595c9ad7c1d2bea3f46e1796b831ebfc686db8192abed84f9223236cb002dcaf3648cfaa507d773eca2d39fbb441c94ddd27aeea1b9b195026735642bef8ab61411d7b9350b8ea2d74af76a18251e0d853e0a3746e2d3c2616c65dab34d5ae5ba0809d6edf810d07a4991e7d8d9293ddb530550fc7680b1cda2f211566d3cc247166bcb033e253f38b4de6aad792690d9637ff335ae90075552bfe4e6fa0931481134dbfed4afe87fa73ab126922576f9228d76689090c5d573053a0a6a8937e30392d435c66c338fd258d671ee21f0da4180639a866ddeb2912a71f17107b23b691895c1b6bf8ffdaccb00c8e10efff64d66ca3030aee49a69616d4aace7db4bd2a1c874f1b1249fc9528ac614021999b81e1be517d6a7eaac84720632fc9b29db8a984fe2892cb951e871f729122084d129e8d9c92c633dc995f820e7dd63b62eaf72da81dbb328c976f022a413cfb365237169517ab9c1b39c7dcc07cfd41501281a8a3296182eca374282df16b77996181c7f888bdecaa4bbfc581cb830174cfc5eb63da1066ef3165a4dbd2976a089770bd8d633e399472255ad53ddca6435ea17d1b6fb4bb0df815638c90d2ae2c80dcf3a928c5a4b7c61a8b71b686b6030acdd3411f09bce00707496452b4c0b7c8bc01e2efe2181672b8406956c89011a25efe7d609a89f923904e8ce00150b063bc2dcbe47ee8f125810afee983495b76b8618360e8e132d3056f7950af7e995a2ab667d46b86bfb815f7a268ca4b468411385f65692a7772624a9d71e1a7baf4b9a72a23ec62a7e63223252b75de871811df99f12637a376aca03fd275d1d917b7bd9bd8ef3943e1e5979ed0e645e80f6899bf77cb6cb1464fee92b0a773df1495f34c590ee0d5af6ec3b41e50e61c42d2e967869cacd02d8e2cf4708c58d3039ba9768ed6e17a90bb3d303ab6e201f4188f86972798515bb191018074a292523cd2f3906fd66bc2cea4cd269eeff5d93a9b0cbd78666e4d712c2cf194b94842f9e1c75b055525f2de2112964ba53900373e81db11a0c4477755bf21cff0ae9d30ce0d7f6fcf069b9824a3fe663e82cbc73a377bdad6da874259b13", - "public_inputs_hex": "0x0735d58c9896306041928fcb73cd2faeef2a225c22cdcac38d802de73fae0eff1739d924e1b79deab6c8ab6f90b56b0676891aedb26fd3fddd21e54c0ac32fc700000000000000000000000000000000ed885dc0873d41b86e03b401da548aae000000000000000000000000000000008ae9fa5f57d2c0195f1138fd669fe83500000000000000000000000000000000c803e0614c2e62b8a0c47186569f52cd00000000000000000000000000000000dc453a74dd94d318c80f0875a5ad17cb0f7ee1bb52c323e82413b0d1903bc8d2d6653fb7b3e3ff408fa7b4844e4dd96905a2fab20a277a785453b5c51e89cf459b0b43fd7b33c55eafaaf91bb7dcd1db000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020a9dc790b726d4b30e4d8865f33f50004a47a309fa92e169d5ad235f41eb12542c77628973a693c21303168ba71bde796957f944c3a3494bbdb558527b5ba0c6071f98da6c5d2594c5ec88e9d1b4b90759aa7be42eea0aae2489f0b863dfba9b1cfb95c79fb965b14ddeae2ac0ae58e6f2b8af40f21222f9a964c0e33eafa2510000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x000000000000000000000000000000ced82cf6ff4bbb3df7706231e71e6b59970000000000000000000000000000000000256cbcdbd05167f6b4ce25d0da6068000000000000000000000000000000166a7f1bc5dcef0a6876195f2b1982e50f00000000000000000000000000000000000435ae6971f95d4383045ec9bd8c5d000000000000000000000000000000837560a732b0bb9171a9c3743f0df2d19200000000000000000000000000000000002de6b425fca5a6899c6eca38e130d60000000000000000000000000000009163dd9362545346b3be858a61c00c058100000000000000000000000000000000000f44920fd3bec79acd21f21879870402c0524c4e6623c5c4227acba420d78b5e60c68b33bbbdae2396fb8cb7ec054727782717bbc6f01e313b502f969b21568859c3e706b714025f80ee8c7bcdb0c82526cde2cab30bcd5797b5fd4e55627c06c2de06c17e1e10cec220e014196a6a1eb542fde436cafbeab8970ee4caabb62b8ab17baa7e41552d961d7a9d914e9b2291f3a3d4ed8a08b191eaae83a7e45a44379483772f01a6ced13f281d2596ce2a836d7ed72c75eb89d8ab3814cb1933d097f7eaa90e5fae47a581c54f8ff3831e2392180255fb810c2bc9bc1e95e41e0b3a6c1bcf0e9fd9b636928be6272fce02b74537db88ed1768f9843ea941aab50c68102efbb5d8da6e7cf591b536453a0a9bdbaa9383e0918bb49b0d6c1548acb9e6f5d9dac14399501cff1b796cd2f5264f7c7f17f2438f042e76a6364028164f15fdbf017936921832a0829676f5852f67df6d756b2146d520e796b21145175e90e352852ba80ce10ee2e142ff76241b42aeb86791abd8b11685616f99e1a952a98b5d69ead073730262f370aa12531d932a4a18b52c673bbb3c68b66f950a2c080d21c06a84bc35b3a5b31ffcfbcf2a17d381c4bb7c14b9324c0652efadcc8fa4cc75ba1429844131e6230fb1e89815c676fa498ab8849af89aa841e8a39a51dc1bd73aecde581277cd50c0ed1c5307066c0e364799076e0faca5fac1227be9abe97f4a34e4076fb82c50a54c661f2d9361969cdbcf73b5ba4dba9cadce38be3ec440f8472b2e337376518d811d000b75824483db1f0505b70ac6bdf4a5a62f590ef9c2bee3f515cb3d02dd9d6f441aae5904aef6b1b02eda278b9c0c772e50fbed558ccf1c737b0d2ffa3628668d2746f045be1a70989d1b414427af7fbf4d984517ff272dd178ad84a9f5cb935320c3931473ab04f7448fb410e5ef14866c9c9bc58d243026b0014a6397a1ea9d2d89d15fca3dfbea340e0ab06b58399c773a03b4263be2331e48dacf73eac83c1df17d2d1ce98f055bfcb6f962164cca22647c9a84653548c367cb634fca8fe60605dbb13bb9aae1f750c84ba58ee45cb5edfc9d3a7d6a8e1888f37d844b96160c397adb77ba7db9953442f3003eb78e517df7803f3700563a43d4a72545797030455d51b84430f0a1f522e57a6e19287922a109597b55325cfd0d51ee6126ba2ae7b3a94d989052640202a05c28f3a983900050db85989ad9f64b879b59c3982a6c828905e73274a2b092db8d2da58453fb3271e2ac63ca86e359dcf43e96122267126a573f12cc0cb89dc469f84838f5053d725c91690054e261cf68ef14362fc905b29a990197d008e94b06fc17ce56b55e8cd2ace8cf9fb778504650497914cddb241b1cf7a8eb394a2c445b82d597c27fcce35bbef1c96c59d0f54083441f43c15a5195c71aae5900f2bcf9dd6855be168e0e163ce48cfc4bfa7b91f87116fdd658e975eb86f74dafe6d6bf147966e13c76caea06f75f044f1554b47add166fd58e90c7f0643e1a79e66d51172d16cbc7d3775009dad8c2fe9df796e48d1e23b50ffd217608b3b1442d3e112abb0648b21b3998ea1ee2875c41521c737a2664907860405a5c1968ad5517bb37a30237b5c2ef25c9cc27f8e53bf0f7339d13214e3e8824439a327873fe8697eafa20f2582b1f89f73dddb347014594c14f1df3842e19fc15bc3ecbde7276f9a628968660a199d365d8f0d75c80a93c58892c9247eea7066235f4cb480edc01427c7562ee288cc2132f40c17213e1888267097d64151bf495e326c75c2ad667761766c6a8e0abcac33e709c7c769a639a6f2d1635cdef382ee0608e9a0a9fa19328e44eb09c859377eef1a3030a52cfa4a400e35c63539d4b912c3700b428bc7689d67b3eadd9f703ee400835c824a2a8051d18624bd9ffb47a5dcd843adf56f40fba5203aff66f2a3c627baf1eca1ea91f2fb29d93e900876fecef95ef570b538b1ac1a759e7defacdfc8ca37f4051ee900538609f4f575621429283ef3a17789db0295d4a059d0a44372ecaf3822b8ff72e1deee56044cc6b3177a95e93b0f7874214e55f010f72b2a05179710e87255e020b5cce24f4030c1be04cf814e29690545a4a036b51cda55367ebfe9718b52e1734fba1b215187bbc74319dfbce36520dabbdbae97e36e10c6d7ac828ae43a60fb657681c183353b56a6f556df217c2d5d70465afda7f8cdddc91c799ff6d2427addd7d81b02a9a0aef1f114e64024e80d0f73ad9171dca221645c22a4ad085048bd8769457880dedcb73493e5eed8fdc5b891781d504646ca5be1882dfb5dc1a92b79f7dd45458b4cadcc0abac9099782f419bdda6c2432ca560032d83e1900fa0089f4c41c3ab7f593b7d0a9594fc4d6ae0e02f15e0ed0543250c3fd38a3225182a352baacbf45cd142b4964e85b01207fd6b714dbefd9ab5165e95edb61713585577d9c48e22206a707b4c9860c112132f5bff3127466b8caec895c7e86c13ea2eca1fcb4ff6a8b487213b077bda1b560cb33179cc27966a1a2f14de3d1c07a86759245581306716c4d178bd67113ee2fa61e4b85618b4ece0b509a8754717a840d947c5a4938747522bcf96cae0f8cefe341cf118fdc70533c54d2ab0f020c6f1d925676b44edf5caa39b8cfc59d0061ca839bdc2a4c64362a2ebb5281c00dd2046142977a439e8a3128f2744cd3ae734de459d7768fc1b8cf65cc8e5a62897e5c0ab0206663fb3174ed79e82393e09dfb85af3e2b1f6b564ea12dd394703f3195b900c23407ad7fb20faf7373b37d441e056ff60cb9b8f8fa9413c697401a29d37be46af4df6091439db13cb6de7453fc0901dcd41f6a1effdc6e565710ee5b20237dc1e66a2ec85d86afcaf450df73aabbacbdb3cf4dc89bb06c22e800a21358c205a6bbfbdc1309d4523f0b21604322810a72ac45b58b9a29c6230411eefa92199223ef5b0c17c3f25863a76dd1801c6b3f14ae3e7b09adb40ff444408ac8e6e33d4d85f5f91509f09c96f71246b2d27734cf9dfbeb63f374f910f452455e43364d5cfea176244375c2402b6618bf3bae61a868b9789318c7a11d61c0b0ecb2ad64540db84b890b16eb22174a5ff4103fdc5589e43ac3688b4d09d921b88f58442a4d3b6fd38ca764b779a25f118c06755af5ddcc50dbd64582100162a7c8ae09e1c06608c20949541edb1b5497a6ac89f3d0ededc84dadffaf7b8920ac2a691c150a9a5f6c327c7ed43d34fd393d70e6d4f96056202f74923d504e20b25d79cd3ab3543cd134249527bb07f4ba4870c03236ed94bfd9a4a85505abc11117db2f3bc5719d5f8bc36ec4f39051ed1aa59250f6760ff1e592c22e4df3514647886224bb302a48988cecb3348db26ef93ff845466d5c0b8c1b25d50d65f261b4352e7fd7c06aeceb65d1915246c9465b5a701c97c04e7b104aa593ef2d0178a6411023941afd436ad3f75fad7f2c6bc5c2fe2bde4aaf08cb614dc5284011c4679577f60af1519ba428c0b8624ce048fbc16f471f44333829d6260b62cc22c06ef59bda71d641fda5209794916fa685b7ddf6772daf05c51ccecdf5541bc286b4b2c23ecd5d90f58f5012b56a6001d4255e3b7a114a807d795c4bff6c4760532a17e4288d7313c14f11575c7727eb52952c5898d787b86a901537b0b6d122519e337ea1e6af2267ef97e3cae9d35b3b59f52cd0b515f04187f66641146772419e112ee4cc437b7b3ccaf3c82e5b60daa3e090f3f35f9078148efa078b5dd0d8710a54cef659d76d4b14a9ff54e18eb9fe426ff64dddd0150be1f6bc486f32e975cfdf84946cefc7b325457dbed182078c8596b2b0168b39f3322b55528ba0a691523ce2b0e04e85c24b4d7b8a24d02303400e773af705e9fda124763251303e11ca7b6fc36ca8f00734572a9c9d4fedf21d5a9c988cb29728a0c97a87bda13df156d0912d698d95e8cb43ca8dc77946a6252e470f7f8275d225bb5df90f40fc376db27ee94813620ebf5c32bafe8b6ceab0cead0888d66aecb6789b885c60bfef66df8eb8fd4af0599fbecac7d9dee09c3fbae59fdb9e9255324ae8e8b032b5b383c3ed1bc9c1daf2904165064c163d48f01f8760dc53306fece5c507cd90c5f4dcd1c62bbded852da2a37c2660ce1463392401cfc27235abf55827650c4209c13a12143535f66f7c40846060fd2c46356971e456fab16fac9caa14ea8c60bb7d851d4771d47fe6ed917981dc3b06d21ee4c6f9c2b4ba3228387aa5cf47119245a00a5eeb37082a44c7335ceefb0a456bbe6f413c614c218b1b160452dba244982e0968842364fe90b1a78c4923fe0c7f172e1ea9b94321a8b673c0d5ba0195533bd9256694285a067068ce051de0662b245d65920600a2c41ff5f8eb13a06666031f88231dd0eca2a75f9329fddab94fc8e38804a2f0cf47385a56fc1eb035ed456014d657957b1825da5db1c8b1b202503b67f681991086614a66c97e624d27bc0e76ec1783a518d3cb1caa28c9e16d8f7fa261d634446bf5c915667de2331b8bf817a959834e3dade375357d6e68ab64e0c2b66f9cfbdca9bcd44619507f0c9812b5e78f526bcb19d742052fe5c61989c619f9728a8c8c278d2093d692d1507791b4a9b1ce3951b149e3b5c3b46e684dd5d8ce83b87deb6ddfbea1fde06db572b0cafea1504d60a4f0295936e29f2b7537de028104f4ef8d4ab0904472b2d5c665274274f9be61e4e14c7e744b3aca697b4db8ba5e27375b1f4e6ef9a0ac6e7e346fd039adbc19582daaf899a766947d074d4363705b2cb23bfdd11e31889fb0ee6e1ba3e93bc5c12725f7a8a491b4d3e1a24f7013927781adca46dd01ee5731bc6f153c0c56f67db1de29c271979441b7817f7812c3730609ffb2e540be7b40a3eeeedb347f9927a0d976095a2aec5ae5c42d7ae9cefc5a235c9d1fe2b2785189b2b5e80509d7b96001d318c40a8759b3413db651968006dced0f0ed05e05719e6d3166e7e1e849e619fb1c82b31cd82c485414dc494bc338dc7edab21bf3fc2a9e0d31b0c3c26accd0bce4a9b091290a965b255eb5eca2566f5ca04239b307866856a9c107ffcd070a19f4c223ddb99563ab23140e99da36d5c47aa1955a577e5ecac29ca056bfa74ebefbe24f455cec4f3ef527f19284247028e702874ab6401a9a376b49b3947b66e0356899a7dfb8f52df845e6c0b4b4c7b1c341d4027a640a673f1efb41a08c5c7eb4ecf25ce46bad783383fc2e04d55e31c121f22c77d110e961efd7acf990150062d644ebeeb318ee27c8cfdbb6e7bef7b4524ec4840d2ca7a65490d0ce8adf6b072a032af7ea382615b135671fcf1cc84300e422c69e52535ebfa7e9bb1437862580482da17c0a4232009e52cfd025c338b225ef257709f505fdca755cc1cc46fd3b2715f8fdb8da6224f85cf9021012ad212789930f70b14957152b7527e4dc6021d7f2bdc60bbaa098f0bcf94f08c8d44107841a7b62d851ebf77f7bda273228259dd161e8f7fdafc66ec339e7f55d9882a5899d9a94e4b7ae04d8194076db1fe7a4e9419d0f09c9da684953143d4ed561e1f8350d9c82ffd2d4a40216f1204b9954c12ccb44bffbb723fe305c2e77f2b1a020e0930fdac8670dea856629ec2980e1de453fef3865d533c31df6d2f5ead0dabf4be023ff347e89552536eb212cf5292cb83db3b23f16c367e4f43ab1ebd2d40f213eee2f3a0aee01f3268972dbd2bfad067c644ed506f3f272c01d81d5a024c4f2c9d35a0aebac5d40901eef04011d96de43e2d89aaecd074073dcb4fb31550f11803dcf1ca8dd83ee581ed0251da3d738519ec57f200aa18c55db30270018cbfb1415823be2dc4e4886cd607115fa5320478a7a7eec42949dee9d9b0aa2a520e5c647e73532c1438b8762da544da0911ff785b7008d97e46824fc8f3c827c94606014eae227f19079ddd0ad4b16b9bf95efdec4b1d1a595e95cf7c2fb01e7cf4a1b19db7b93d7916f0a52abd246de17f92d06d923d02e07db0882bc2130cbde3686326d6ef2e58ca5754e934e53d63637b8ce36ef9f4f63e8d2cc759c51d0331f4e3fea2bece58686066318361c3ecd9afa916cbc454a553d4649886d6080e07734f6225c6dbaae02747b89312c21eccbfcc1cf1df3331d0f452a63afd17e713d1a304073f179df383572c9ecb962bae7dfe82da243b87084792f53a1c169468b1544b0eb69964386600d5071094ffd751806aca843ea2d7e3a8a374481186f4680c36156a039fe4baa7ea250bddd85e4ca48bbd3a82416ff2139779852b362130152f5e59008e5572d302e59cb9a7e92d819e43211b2d0262fa556ddb188cb537bc81644e369d1d984e1ea645ae47f1244ca287274859ed70e3d2c920208e84aaaba83c78cd9a82c2c0caa7abe9833fb5652279ff0dae858f1fbc9e601996caec66e61ac0b784555478a12198034b738cf65f0a46c6da3df60110a8450a0e0e2c676cc18a11facad370a8906cdb5dff047ffbbd5be54026d8b3daac2902187aa12f1a01ec7fda0b6bcb550b3fabffa138047446ba0a3e94c7659eab7c1b33f37e07f6bbf6f126db56df85b6353750791682e664b15ffc027461ff13de273c2922ff68d238c1c6fa37e958c5adfab508c4f887362c9f1350787441a04b301adff667adb0d36684372425e4f2c3a616ead139fbde1e7d7b64829e63510f06084c79d12ca82480e5c773a27ab613f8614e781eb7ace8db785976d7ebac45178cb72381f5f8fb4a32cd5d568a88474da284075d0d71bbbdff9c8bce6a8b5b123b477b02ea410e140da53f5ce6b21df351947b07a653c79bbea18ad3663ef129195b63f9fe25a1461de8465c8f544f38568fe0967d5bf8d2adadbf8fcdfae50d8951a9514a09d82cc298406b0dca0370cf313ac8dad98b7bdf2e37d0a9cf2e1d8fb69a6bf8454e9f0db61ff25fcd75ef17a0ad08bf3c3de8fe0306dd8fabaf0dc81dac2c084962c6e0f277b0ba250ca916b69c37e8831c95b3a8b3374807390daec75c0791c737461cfb22f4755cd72e85c85a11037b7fc965fe9fe0ee5de805a90c6871ef276e39017e347a8f8f81c5f60f8dd159397d55e784d950b0e251228992732e4ec7e6a728d93aef046dfd389cf93a4f97519dfb57c29e3111e8f62cbc7b0be0fd370510af570285497e1099c94e5ca3db6fc1ba477cb32b021095090ccff1b05ee345d0b2f2af1062d4f44f7328cf658686494116cad0130541562bc8af389413177e9a76dee157c5512e8d1f7f7e94d82c4fc656708e755cef05147d8b958bc37e7829ae9e8077e2d75689da4930d5a7774b933c0be446258c33132747bef33bb7a37a2d436b7be749145b18fd5860942d0af3c72c1c42b46d2310ee9b5b8df2d6f0ff49b13b808e4974e68bdeef15289dafab4b30e2a033f554257f82b3591c963ca427f696bf54b9d741fcb9ea7c48d248d0714b110022b5a0132b5b74ba3e4b7ea0e6e428b904f67f4b841cc11380327a6ddd9d5b16c9351f08b5f2efeebac503f7144f1f09b3492d5e2a1a162efd6b47167660256680a0551f5250ae04a3f5d9cd8f86bdfdbb5437cd0af4d994b2a300cc566b828882f9781bf3126abdbde1f2b4a37ea30684a442b7b8a7f00c6b431cb3b90b368a56737b0d2fb96bc23cfece80049481578deaa6e4a3230a2230b030cd8b58194d96866c22a362cfcdbdd3921c850b074d6868521d1b8ead1e907d4193a7186ad4d51fd714be37f105098e6a14e48f647445f2f262dbea72dac95c26f66dff4ccdc9ab4f00ef6c1635928805d31cfe7d64526a475c1e7b561a8e3aa1f8a036b2590ad8752710c2a6b94f5421d195d504bef8cf553fcedc7d89f75a0165cbb4cbe9758f5517ebeaf4fbac2d8824c3bc01a05a0367049575b1c3c68aec8fc97280f4ec1a6907a6c2a714f1b1109caa3962a6a582be13ccab61fecec61651842a2ab9b551da22576a201d0333b156f1853a18a3c3e56e3b5f1b5a615849887ef4cbc9fceb0408259a8e72588a9f63cfdca70d15afd8059c517886ee0e13704f463430ea829f27840511ee58891c6fb5d7a70c5b90cb8260b8989df8597fa4e23c48a313fd8504a2b157da6a7e441b89d8ada319b00027710305f19bae0360935bc7729eade419cd212a8282367125e3d73437ab40bcd1aece629cd3908f9296b14684a7ad8800a35a19a0eec6f90d8dcaa7e620a4505434a7fef0d00d54d231522dff4afe5b022368e3396bc3b36e830f309814d4e919a85a11e69897e30c916e989db97abd2376190f45e45afaa6bc191555fcc04f5317b9e1d5e484d2e2682fa5077bdc7c000ba161c2a241a494e6fa28162b7fe46c2689c8efd7a878895ea48384e3af3024a5418c385364aa6891ce65ba6464382f1446fbe49875901fb6851218c731520be7965c121483e25a82f95ea15e71bd9dffb1abd9e6a01e2e3c49eeac1072d9178e10ace7d5478dd7f14038a22f47b94d34625c01323c3d3ec963a75b42a93a25fab20b145563c1691a30b3bbb7e49f6a70e5f44c285d8e5cd57bdc9d9690b61d0805ca10d972eb217fc3b381187f096b595c738f85a111f947cf8fcc0739e903dd6ab73c3c59a19ca2509d99e452e051aaa7714f4d2706f1e4073f89e8d6bc277fdd10799b6f30c5efa27852c3fa26e440a9d3460a547f3da1a9044120dd3c2789c2abf55ab2d5de134e891aa710c9c73e9b6c31dc1661e2bd644270240fb515e6098edca9ca4f845711120907bae3fae4b329e09eb5b24bb81b472d348ddd00df79a7a8833fd104a5ee5a46164599e18ee33eb71e18dfc2de24b8074d88701a53c03e764a8885459a5a1fce03f40d5b09b0a2220f9c14b587e7ffeb0956d3078b00c2b2c7cb1673e725bfb8e7ed3dd5173c849304e4c877d8c722e421704a244a2d104807792654caf8f431d98b60f921f0041d378fdd044236364168ee8624900ae7c779a6382041583ce17baf9b49d3d0c8bd5b0018f7184c57419301c6232f1d025e56ccb98d3fc317bd27b280cc2e7d8cb64989637ecd8535d771fc9429d5bfcfc8628a1a19a2014dd87b4855ff9563a40910f40448f7357e6c4131260c4d5709dc3dd3322594fd42bd784a9f1eac0422088ba80dc85530da485b233d2921bf2f96ae8515f3214818a9b53a7934c8b1f8e3d86c0d52a6547d8b34657e07d0c54db50c32fc80aef4241bbb9a0b84b2026c86cd58874ddc8d8cf4bda9911480003bb6d06ea51a2ecb11b9ed8201e06e81df351871181032da6190088f880c2e374ab792db8802ec6df8584ce88d582341b3a9d29205a9fd97df83850e490ca5a14a13fe682d82a11044484a3e2b90760ce1f610236cde84768d2322708728b6133dbdbe2a43a2c4df5a9b06b9a13e6161bfdc9027728df55b8fa4e2b86201e77243f09a2af562f78f7586423200fbcf7ea0703c2c43c6b959cb34041490188a2006cb6a6be813802f5345ed0da5ddfe876096d104faf1d33c84a74b5c31270091932312f38d19f57f1cd0295b65bb4a94560aad96ee8c8299d66b246af82ff34ab6475ba66b96efaed0040c556374ff5ceba279bd523cab53d316c66f240898b785b1ad59f540c0e5ca54cefd9fdf528e21a8f9abc9264449b8151d25d40f5496e47ff24a3afc6f92d34e77efbc5aa8ab93fd4a97e7b83f699ab398af7218f6cba3f7a60a86fa4594859fa33f35e6b55f3d0023acb6813a50da918110ec1b294777aa80a6bc107234b57bdcd553019c5575e32c490c41c7be4d7f8fedc500e51907f4bde574474902ed3418ac08fe0390577489f0268e38d10b1f3f477116dfe356a85559fefb054971f731025fbc8a0768f6e0fa45290ecb8d6c9e29bc2ad3bd958e18c6c69c24748c34f802821444b116fb1be6c8910489ec9d89352a2babd4c34aae4c614d6b24c83361b81e3a413fa6ef1f4cb93a8e5c008517924f011da76885273f6aa7ffa689d9ada5f22d33c87f7b59822b2785ba702a36a3040653bbf426ee4357b72285be07f705cdf35194f822901af06174b6873f78a7d62fb6b7ea68593f80c796bb2cc7c3c05ff7f66ee791c6683b5ba4a8fe1f74d0eb08434e7f54a71794ae709cc9b6ab6c5430b75bac83e8b0130f82d24727fd00d128bfada84a90dae29ba310d44a92ed46d38f7d8394292cb7083cb2db448ee14b182e04bd7a29a68d5211c175f85fc9ff6f818fa744392bfe9b644aea5ae53c3104859dbe6084fdc8a7a9e82a4918f523c9a992dfe129c2b1aeb0422460595f1807c6321b0e26492e599d25238a253eebe9c15aa7d64a537a87e83fffcedf14e21209c4cb5cf8d5c3cb451be77432fbb11ccc9c4b87ab7808e03b81fc166567d7075ddf494b443724e8d8074fc3a41bd5b7faea48a8dda163134940257bf23af60727cecf9fbcbad289141c741f24c3697cb7d1c274b7a897a7891ee98c919fb222126256a6d28cf1cffd90c425ed932f8f6ccf75ed4f83cccf593332466722ed0d6ea587918ee2445ddd92387e81a5b02d0476ae69a8b67d609cb788e48880242a9bf9cf2a8efd3c6029384aa94e9c815f7d721f028b0401c25c7954ef36cd0f00000000000000000000000000000000000000000000000000000000000000002043e29b1c08be74117027255cba53a732d6c004cf46daabda07a21c4eca8ddd07ab79843f780fd583434c3faed617df336f3bf598e4a80a02686ceda5d0a06b1a0f896a61c7974b67775fbb456189fa0cb4a03e1505a8a45b5d1be52f44fc2c1a36e3eaf8387bda06325b928ddfdac8268afaebf04964c76561586ef796b3cd1f1d0067d4f99d57fe9cfbd0adf1845c0b7767306fa9499f146fdd2daea8e317180b196a3a4a78038bf4b44295c778810e7996d24935e6ba874afcd78e702682184ffdfac21f46e0e3e1d2c8263cebe33c4c5494386c66f2d75b9c06c78783930fec1c5522884be338d86e8222ba39ec1e6462517c817b5e2c0ccffc3d3b86ea120d544465a16e1cba00fb176a9153f661194940e398278cf0bafcc1e19dce050174581d53f1588fb8186342cb4237b13dd3238a125aa544f4e65eecc239fe9510d55b5512c89cb5aa8d364c793a39d1fc7dda79a37df81c30a436f36096e76a037eddb6d7041cd632dee731d18f0184d5450219b77c6ec3231c8ddac91da4ca02d61a08e196872035666c2d5e448d1be5c29576b612a89604293d95460c898b24439aedfa77a045aef40e79d4d57177eadcb9d02c1b62be86c7f7ead08d369007d0c66aa551ad3decb22e5ae42d49d86fc4564cf644809de845f6f379d744df0901374bdf4c627a4c622c0d8f482d5b0b794683975b4e650db68e3c1405d689163b116fd3cd1d576e4ef4eb936235cb0f84b639561c59b34a8f0445deace04701b2935e813400b975839fe8e7c0a003c4d0db079a4b9ec5785c0fa7638fe7041a8702bb3f792656b60a724ea004b248bf9750261eedb51ffdb71b6eab1acbab18f3f44a45032b1522f494883621263a151c797c13c98ce77ee384df69d210082f32289e356caeb4857b215a5d1d29521a35cd027f6dad4adc6e2ba95df6b263099d9cdc6c0be99f85c5ce7ce01fd6254044d6ee7d578df1c9fbd3dc42d63209201b36fff88c70bc6821c643bd99c8b7787154f5d1de8cc075536c67ce1fb20c1a8b80d555347d07d979c9ca7bfb19110b8ff2d9f59e79672203916ab8d0a6920955014aa77c17b110b2be10c060c9eedc083f5f7ce533e7d389c853d04e8cda10d205cf4456c5294d5a1bf7fa641f265a77882fcad4530d9b9fc2ce3e8628391628531427c8bf71f8d0cb1f4496ba59e0b501c2a86d7bea49442173b20e11f42e36bfae0c33b9d2a4d17bc72594359d5fe399ae60fe5d273807ed6fb7c43cc72abab0d7c030b3cd6401bb124b830170b6b6e382f9f35ffca5eab00e3919d84f2af886e19d8b6c89b434737e065caf6276b74fad035ee9040c6d6bb322b3a0ef0288dc9aefcbaa0fd46d615754ec73bc7f4bb7467a7d486ac1189b119ea41ca7012b6ab5deb94fc6b7875affc3aff802249b026063ab02126e96a54077dbcfd504ad832b5565f6d7ee155ff58a5605ee62de29176379725f0278f8030df115e42d38edd713e03799b42f9f5b10a446323e5f098578053d983e813051265815b12edec587ffa0e0cc617aea9c85a88e776ca352847a0d7c5497397a816e85b0991fc20fa9c343fae892952c5d7e62ba1d6183c24b260ba960a4d683b74c22e2412f8505091d61c7b973f83f10a47a70f9f6ad3591db2001fe34b66982514990742ad5905bac7c9d46f62557938ee0783e2bf01bef79c058069fa8621845484de71bcf4dbdfdc9593dcab8ccceb86ef3b69058ab173e9c129476fddc5e1ef76fe12cfe0a5fcace18f45a9baca8424bc6aadfbe73c3646edb5b0ec95cdbc77b89ad040ded8bf4cad265f0fcadad7cdf8fb54096ec8dd49af41abcc6dd16be63d2c7199beec263b6283f4ea660481411fc9f19c49508e920cb7665f076226d15f78c1afbddb2a636109993213ddce696658bd0f827f0d869d8a53ff2b2c4056b27c9136d445eaeae454d6387cad47ddc731e7cbcb34eb19d4876d5d4fea4deffaec6060679953ca4207c5bbb1b133131d3608905a2f023d28fdd0c330434ad26c7c413941ff7a1ec879371575fdba650e44e505f33e0ae5d9212917e2a438793d0d12ff7ba0364ba68ee10b3c6012078c3d606b449b2b294f05b6c3ac5e8e70c3d9312b0f38049630cf2d5330d2fda6b510b75667691a49398a312fe42fe1c26a75f2a4ac3aae9fb0558cf5fe1473c0a4452f305832bbfdbf6530afac7c3cda905ef266ce6992bb25e7b4c7eadfff0b2626118956b69393267ad921886615ffc291f1c855f01a20bf3aed6037095b664ac4acbb5856df8fd2c88d8be3d0ff97706392d00e6690812e902b92b71d2596ba3f6333778d7cc161acc20da5571bae3611429a734f253a61c93f642928c4dd22e7c1b02ff735bd2313ef838fa2fd02c51c02fd0d01d001e007760bc6f4261814c48db6e9c4c5cf6752a05d23da69c8c33db1415f9a8e88c45b9f8b88e769016ee349207d3367055d635d6c737e47010b9d20c890b1b3a91137e98c07ebd0d5d13b881e128abeead1c1c3c0fd581e123f0101effa2ec41c961b3dac64bbaabf0a6717372a3b0b41f4dc1eaa7a73a490d663207709e928c9bc18854c9160950aaab7f9f55ae1745ea1937080253a982935d8807bf41b57cb1322a0c0c37783868de76c31ae95194026d9109044d860c2ea19b1b7c6e39a83223050986d96dd2624bf253b433d2dc5b90ddbe65e67f74f6896c0cfae2c68110e5e3b5029b9656103742f76dc01abf59311c38ad824ae5f122a70509d91435b5e865004e57883a9cf6d403a427be74170ab89637f49dee12264b2a9a48bf2a7936af7c792dfeda5c14e26232f34347a9a726eaf5bb09cec471340c38d3aafd8a7b26eb9e003b58c180180e2349de3d86a20229e7be48dcbc211a2ed57e01459b5f4edf1c4248acb3cd204f95aad469acdd677258fdb5905daf67240bb2ec3d44346e1122a320706e07bf9098437be6471c2cff0ba0847e31f7a5187879a0afc6ddecfb31c40753c742f4e372f52755aa64f8da7001755cc2f1231d226befaf1e544e37c838f85d9a257b943eb0c056663dce29747b82d3ecc93a1824ee8bea9bd23ba123f5329cd45b8e995a8a7aa404d35ecdb228705b17dfa22c4c095b3a5cadc5f73aca2a893d28de6c2081206537b58fb897a3a64b55bd470b9c0b505063ecf5339820fe274fb1e14d07280129e5ee7f90605cecb77db00920cf70ea97b4cc406c2f2a6e0f74e96a0c732720b43440865e9969d944751f4706706434dd096ea1cec818ab257473b759f80a74ab86bf357d3974d0204f335e2c5d405dec1c1d7716510a180998d16afea27c988741802b75479e4f8fc69c1d166141a169c43bcea4053df2e0dde2988d441029bf4943d30757b6206ca25a2c0a8d6c172d6a676b5a9f99c7a677e3dc2cba3f44b413a9a38568453544f1602c0e577c85ef0144d7df1479783ac4ed97102c5d2f9517552125ddc1d800f406201ac74f4c1f9b5e92b3601e36e77672b5cbd80b74b7c0942514b5cbcd5d06d02907745fc48ca19c70b70b7ff8793e7ff84c5756e3780b0872a5e89d51d6868ba024e2fa6faaecd26d8ee0bd7138ddd85b8aaed9f11d205c336b85dcd2043cdca5304f86ed1ef9f4170a145ade35ea5c29d75ca3b8a8bfffa50f4775f4769056ff138b346d3885ef85b24bf8a1984eef4a3a7a65fbe5f21ff3205dabefe30c6db206ace211a5e41c852dd8b01b3ddc6ed3cf50d74f64a52504cbad920feb4ab4792668d4dcb9e47c29b00c7fd9750b69ea921a6b0916404007c40e922d0034bfbb1ed46edb3e5473f3c24194c6e5735347f6f78345e2bd0498188d971609996916215963c422e5571d12995bbe79de07c1489d99718f41bd992f65baea63f6ba5516c413376e2e387544b6be64e9fc90132b0502491cd413e537b94710e9ca1ef315bbafae2f56d3cf279c97cdd2d968973f941c0237bd081316f29a7c2f97231c26be83ea9ff9d197ef146435e9d8bb3eaa14cca974ef4084b16253c372af643423e3988c1c763b0c573a92a7c17d6a830079099e2b2b37864f0149116f61986511d736abcc82d0ef4fadf64571b33cad6bcdb0b18451d4b3ec3bec0ee05780ec", + "public_inputs_hex": "0x0735d58c9896306041928fcb73cd2faeef2a225c22cdcac38d802de73fae0eff1739d924e1b79deab6c8ab6f90b56b0676891aedb26fd3fddd21e54c0ac32fc7000000000000000000000000000000009c71b606e3534e1f20b720f72ba4a04300000000000000000000000000000000a9447fb86a0b375b772ec97ddaf912bc000000000000000000000000000000005b2a7a918af686606809d175c19223f0000000000000000000000000000000005420049a7188032e6a3f5abc071ce6c2240d5ac3fa81b30445d9f6d99d5bbcfa301a7b25f9fbaca6880fb0c922b525f105a2fab20a277a785453b5c51e89cf459b0b43fd7b33c55eafaaf91bb7dcd1db00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002271206d84006fb97f06e10ef11bc431c7ae5e92579cbf01d430ce1d3ded707e30cc25aedbd41a661690c531173e4164107165bd6d38fd15ff71f1f74c2433e4a06ec4f43c34fb4e87d253e07920a15491bfb7b65ba9694b47c95df442184c5ba0674cf3cb2a333066124d5d0cb5b9efed0ea8f09cd019f44ae894099125333290000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } diff --git a/packages/interfold-contracts/test/fixtures/dkgAttestation.ts b/packages/interfold-contracts/test/fixtures/dkgAttestation.ts index 3824263f33..21d2f423b0 100644 --- a/packages/interfold-contracts/test/fixtures/dkgAttestation.ts +++ b/packages/interfold-contracts/test/fixtures/dkgAttestation.ts @@ -28,14 +28,14 @@ export function encodeMockDkgProofForAttestation( ): string { const h = partyIds.length; const publicInputs: string[] = Array.from( - { length: 6 + 3 * h }, + { length: 24 + 3 * h }, () => ethers.ZeroHash, ); publicInputs[publicInputs.length - 1] = pkCommitment; for (let i = 0; i < h; i++) { publicInputs[2 + i] = ethers.zeroPadValue(ethers.toBeHex(partyIds[i]), 32); - publicInputs[5 + h + i] = skCommits[i]; - publicInputs[5 + 2 * h + i] = esmCommits[i]; + publicInputs[23 + h + i] = skCommits[i]; + publicInputs[23 + 2 * h + i] = esmCommits[i]; } return ethers.AbiCoder.defaultAbiCoder().encode( ["bytes", "bytes32[]"], diff --git a/scripts/build-circuits.ts b/scripts/build-circuits.ts index b2a3f41bb6..6320576435 100644 --- a/scripts/build-circuits.ts +++ b/scripts/build-circuits.ts @@ -215,21 +215,17 @@ class NoirCircuitBuilder { '/// `configs::{insecure,secure}::dkg` re-exports the relevant one as `PARITY_MATRIX`.', `pub use crate::configs::committee::${committee}::parity_insecure::PARITY_MATRIX as PARITY_MATRIX_INSECURE;`, `pub use crate::configs::committee::${committee}::parity_secure::PARITY_MATRIX as PARITY_MATRIX_SECURE;`, + `pub use crate::configs::committee::${committee}::smudging::{`, + ' INSECURE_E_SM_BIT, INSECURE_E_SM_BOUND, SECURE_E_SM_BIT, SECURE_E_SM_BOUND,', + '};', '', ].join('\n') writeFileSync(activeNrPath, content) console.log(` 📋 Set Noir committee to: ${committee}`) } - /** - * Regenerates `circuits/lib/src/configs/committee//parity_{insecure,secure}.nr` - * by invoking the Rust `generate_parity_matrices` binary. The Reed-Solomon parity matrix is - * a deterministic function of `(N, T, QIS)` — committing the output keeps `nargo check` - * working standalone, but the build script always overwrites it so a change in committee - * or BFV preset constants can never silently desync the on-disk literal from what the - * prover would compute at witness time. - */ - private regenerateParityMatrices(committee: CircuitCommittee): void { + /** Regenerates the active committee's derived Noir artifacts with the Rust generator. */ + private regenerateCommitteeArtifacts(committee: CircuitCommittee): void { const libDir = join(this.rootDir, 'circuits', 'lib') try { execSync(`cargo run --quiet --release --bin generate_parity_matrices -- --committee ${committee}`, { @@ -237,10 +233,10 @@ class NoirCircuitBuilder { stdio: ['ignore', 'pipe', 'inherit'], }) execSync('nargo fmt', { cwd: libDir, stdio: ['ignore', 'pipe', 'inherit'] }) - console.log(` 📋 Regenerated parity_{insecure,secure}.nr for committee: ${committee}`) + console.log(` 📋 Regenerated parity and smudging artifacts for committee: ${committee}`) } catch (err: any) { throw new Error( - `Failed to regenerate parity matrices for committee=${committee}: ${err.message}\n` + + `Failed to regenerate committee artifacts for committee=${committee}: ${err.message}\n` + ` Try: cargo run --release --bin generate_parity_matrices -- --committee ${committee}`, ) } @@ -394,12 +390,10 @@ library ActiveCryptoConfig { * - the Rust integration tests (cross-checked against `INTERFOLD_COMMITTEE_SIZE`). * A drift between this stamp and what the consumer expects fails fast. */ - private writeActiveBinPresetStamp(preset: string, sourceHash: string): void { + private writeActiveBinPresetStamp(preset: string, committee: CircuitCommittee, sourceHash: string): void { const stamp: PresetBuildStamp = { preset, - // Narrow out 'all': this runs per resolved preset/committee, so 'all' is meaningless here - // and must never be persisted into the stamp that --skip-if-built later compares against. - committee: this.options.committee === 'all' ? undefined : this.options.committee, + committee, sourceHash, builtAt: new Date().toISOString(), } @@ -442,7 +436,7 @@ library ActiveCryptoConfig { } console.log(` Copied ${copied} artifact file(s) into circuits/bin targets.`) - this.writeActiveBinPresetStamp(preset, sourceHash) + this.writeActiveBinPresetStamp(preset, committee as CircuitCommittee, sourceHash) } private requiredDistMarkers(preset: string, committee: string): string[] { @@ -520,7 +514,7 @@ library ActiveCryptoConfig { this.setNoirConfigPreset(modNrPath, preset) if (committee) { this.setNoirCommittee(committee) - this.regenerateParityMatrices(committee) + this.regenerateCommitteeArtifacts(committee) this.patchUtilsTs(preset, committee) this.writeActiveCryptoConfig(preset, committee) } @@ -549,13 +543,13 @@ library ActiveCryptoConfig { return result } - const sourceHash = this.computeSourceHash(preset) - result.sourceHash = sourceHash - if (modNrPath) { this.syncPresetAndCommittee(modNrPath, preset, committee) } + const sourceHash = this.computeSourceHash(preset, committee) + result.sourceHash = sourceHash + if (this.options.hydrateBinOnly) { if (!this.isDistPresetUpToDate(preset, committee, sourceHash)) { throw new Error( @@ -606,7 +600,7 @@ library ActiveCryptoConfig { this.copyArtifacts(result.compiled, presetOutputDir, preset) if (result.errors.length === 0) { this.writePresetStamp(preset, committee, sourceHash) - this.writeActiveBinPresetStamp(preset, sourceHash) + this.writeActiveBinPresetStamp(preset, committee, sourceHash) } console.log(`\n✅ Built ${result.compiled.length} circuits for preset: ${preset}/${committee}`) if (result.errors.length > 0) { @@ -781,7 +775,9 @@ library ActiveCryptoConfig { } private isFoldOrAggregation(circuit: CircuitInfo): boolean { - return circuit.group === CIRCUIT_GROUPS.AGGREGATION + // C2 terminal projections are ZK leaves for C2abChunkFold, not non-ZK accumulators. + const name = basename(circuit.path) + return circuit.group === CIRCUIT_GROUPS.AGGREGATION && !['sk_c2_chunk_finalize', 'esm_c2_chunk_finalize'].includes(name) } /** Aggregation circuits that are also published on-chain as EVM verifiable proofs. */ @@ -1024,20 +1020,27 @@ library ActiveCryptoConfig { return outputDir } - computeSourceHash(preset?: CircuitPreset): string { + computeSourceHash(preset?: CircuitPreset, committee?: CircuitCommittee): string { const hash = createHash('sha256') - if (preset !== undefined) { - hash.update(`preset:${preset}\n`) - const tier = PRESET_NOIR_CONFIG[preset] + const presets = preset ? [preset] : this.options.preset === 'all' ? ALL_PRESETS : [this.options.preset ?? CIRCUIT_PRESETS.INSECURE_512] + for (const selectedPreset of presets) { + hash.update(`preset:${selectedPreset}\n`) + const tier = PRESET_NOIR_CONFIG[selectedPreset] hash.update(`noir_config:${tier}\n`) - // Hash the contents of the preset's Noir config, not just its name: the baked crypto - // constants (e.g. PK_GENERATION_E_SM_BOUND) live here and must invalidate --skip-if-built - // when they change. Otherwise a bound update silently reuses a stale compiled circuit. + // Hash the preset's baked cryptographic constants, not only the preset name. const tierConfigDir = join(this.rootDir, 'circuits', 'lib', 'src', 'configs', tier) if (existsSync(tierConfigDir)) this.hashDir(tierConfigDir, hash) } - if (this.options.committee) { - hash.update(`committee:${this.options.committee}\n`) + const committees = committee + ? [committee] + : this.options.committee === 'all' + ? ALL_COMMITTEES + : [this.options.committee ?? CIRCUIT_COMMITTEES.MINIMUM] + for (const selectedCommittee of committees) { + hash.update(`committee:${selectedCommittee}\n`) + // Committee artifacts include parity matrices and smudging bounds. + const committeeConfigDir = join(this.rootDir, 'circuits', 'lib', 'src', 'configs', 'committee', selectedCommittee) + if (existsSync(committeeConfigDir)) this.hashDir(committeeConfigDir, hash) } 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) @@ -1120,7 +1123,8 @@ async function main() { if (command === 'hash') { const preset = options.preset === 'all' ? undefined : options.preset - const hash = builder.computeSourceHash(preset) + const committee = options.committee === 'all' ? undefined : options.committee + const hash = builder.computeSourceHash(preset, committee) console.log(hash) if (process.env.GITHUB_OUTPUT) appendFileSync(process.env.GITHUB_OUTPUT, `source_hash=${hash}\n`) } else { diff --git a/scripts/check-committee.sh b/scripts/check-committee.sh index 04d692c6ef..4f7869744b 100755 --- a/scripts/check-committee.sh +++ b/scripts/check-committee.sh @@ -88,6 +88,7 @@ case "$ACTIVE_COMMITTEE" in minimum) EXPECTED_SIZE=0 ;; micro) EXPECTED_SIZE=1 ;; small) EXPECTED_SIZE=2 ;; + *) fail "unknown committee '$ACTIVE_COMMITTEE' in $ACTIVE_NR" ;; esac if [[ "$SOL_H" != "$EXPECTED_H" || "$SOL_T" != "$EXPECTED_T" || "$SOL_N" != "$EXPECTED_N" || "$SOL_SIZE" != "$EXPECTED_SIZE" ]]; then fail "drift: $ACTIVE_SOL has (size=$SOL_SIZE, N=$SOL_N, T=$SOL_T, H=$SOL_H), expected (size=$EXPECTED_SIZE, N=$EXPECTED_N, T=$EXPECTED_T, H=$EXPECTED_H)" @@ -139,22 +140,28 @@ $COMMITTEE_RS has (N=$rust_n, T=$rust_t, H=$rust_h) for $capitalized" fi done -# 7. Parity matrices for every committee must match what `generate_parity_matrices` would -# write right now. Hand-edits to parity_*.nr would slip past every other check, so verify -# them by regenerating into a tempdir and diffing. On-disk files are kept `nargo fmt`-clean +# 7. Derived Noir artifacts for every committee must match what `generate_parity_matrices` would +# write right now. Hand-edits would slip past every other check, so verify them by +# regenerating into a tempdir and diffing. On-disk files are kept `nargo fmt`-clean # (see `scripts/lint-circuits.sh`), so we format the generator output before comparing. # Skipped when the binary is unavailable (fresh clone before `cargo build`); the build step # will re-emit them anyway. GEN_BIN="target/release/generate_parity_matrices" NOIR_LIB="circuits/lib" -format_parity_matrices_for_committee() { +for c in minimum micro small; do + for artifact in parity_insecure.nr parity_secure.nr smudging.nr; do + [[ -f "$NOIR_LIB/src/configs/committee/$c/$artifact" ]] || fail "missing generated committee artifact: $c/$artifact" + done +done + +format_committee_artifacts() { local committee="$1" local tmp="$2" local variant live fresh backup formatted local -a swapped_live=() local -a swapped_backup=() - _restore_swapped_parity_live() { + _restore_swapped_committee_live() { local i for i in "${!swapped_live[@]}"; do if [[ -f "${swapped_backup[$i]}" ]]; then @@ -163,7 +170,7 @@ format_parity_matrices_for_committee() { done } - trap '_restore_swapped_parity_live' ERR + trap '_restore_swapped_committee_live' ERR for variant in insecure secure; do live="$NOIR_LIB/src/configs/committee/$committee/parity_${variant}.nr" @@ -177,13 +184,23 @@ format_parity_matrices_for_committee() { swapped_backup+=("$backup") done + live="$NOIR_LIB/src/configs/committee/$committee/smudging.nr" + fresh="$tmp/$committee/smudging.nr" + if [[ -f "$live" && -f "$fresh" ]]; then + backup="$tmp/$committee/smudging.live.bak" + cp "$live" "$backup" + cp "$fresh" "$live" + swapped_live+=("$live") + swapped_backup+=("$backup") + fi + if ((${#swapped_live[@]} == 0)); then trap - ERR return 0 fi if ! (cd "$NOIR_LIB" && nargo fmt) >/dev/null; then - _restore_swapped_parity_live + _restore_swapped_committee_live trap - ERR return 1 fi @@ -198,38 +215,48 @@ format_parity_matrices_for_committee() { cp "$backup" "$live" cp "$formatted" "$fresh" done + live="$NOIR_LIB/src/configs/committee/$committee/smudging.nr" + fresh="$tmp/$committee/smudging.nr" + backup="$tmp/$committee/smudging.live.bak" + formatted="$tmp/$committee/smudging.formatted.nr" + if [[ -f "$backup" ]]; then + cp "$live" "$formatted" + cp "$backup" "$live" + cp "$formatted" "$fresh" + fi trap - ERR } -if [[ -x "$GEN_BIN" ]]; then - if ! command -v nargo >/dev/null 2>&1; then - echo " (skipping parity-matrix drift check: nargo not found. Install nargo to enable formatted parity comparison.)" >&2 - else - RAN_PARITY_CHECK=true - TMP=$(mktemp -d) - trap 'rm -rf "$TMP"' EXIT - # Mirror the committee dir layout so the bin can write into //. - for c in minimum micro small; do - if [[ -d "circuits/lib/src/configs/committee/$c" ]]; then - mkdir -p "$TMP/$c" - fi - done - for c in minimum micro small; do - [[ -d "$TMP/$c" ]] || continue - "$GEN_BIN" --committee "$c" --output-root "$TMP" >/dev/null - format_parity_matrices_for_committee "$c" "$TMP" - for variant in insecure secure; do - live="circuits/lib/src/configs/committee/$c/parity_${variant}.nr" - fresh="$TMP/$c/parity_${variant}.nr" - if [[ -f "$live" && -f "$fresh" ]] && ! diff -q "$live" "$fresh" >/dev/null; then - fail "$live drift vs generator output. Run: pnpm build:circuits --committee $c" - fi - done - done +command -v nargo >/dev/null 2>&1 || fail "nargo is required to verify generated committee artifacts" +cargo build --quiet --release -p e3-zk-helpers --bin generate_parity_matrices +RAN_PARITY_CHECK=true +TMP=$(mktemp -d) +trap 'rm -rf "$TMP"' EXIT +# Mirror the committee dir layout so the bin can write into //. +for c in minimum micro small; do + if [[ -d "circuits/lib/src/configs/committee/$c" ]]; then + mkdir -p "$TMP/$c" fi -else - echo " (skipping parity-matrix drift check: $GEN_BIN not built. Run \`cargo build -p e3-zk-helpers --bin generate_parity_matrices --release\` to enable.)" >&2 -fi +done +for c in minimum micro small; do + [[ -d "$TMP/$c" ]] || continue + "$GEN_BIN" --committee "$c" --output-root "$TMP" >/dev/null + format_committee_artifacts "$c" "$TMP" + for variant in insecure secure; do + live="circuits/lib/src/configs/committee/$c/parity_${variant}.nr" + fresh="$TMP/$c/parity_${variant}.nr" + [[ -f "$fresh" ]] || fail "$GEN_BIN did not generate $fresh" + if ! diff -q "$live" "$fresh" >/dev/null; then + fail "$live drift vs generator output. Run: pnpm build:circuits --committee $c" + fi + done + live="circuits/lib/src/configs/committee/$c/smudging.nr" + fresh="$TMP/$c/smudging.nr" + [[ -f "$fresh" ]] || fail "$GEN_BIN did not generate $fresh" + if ! diff -q "$live" "$fresh" >/dev/null; then + fail "$live drift vs generator output. Run: pnpm build:circuits --committee $c" + fi +done -echo "✓ check:committee: $ACTIVE_COMMITTEE (H=$EXPECTED_H, T=$EXPECTED_T) consistent across active.nr, ActiveCryptoConfig.sol, utils.ts, Rust committee rows$([ "$RAN_STAMP_CHECK" = true ] && echo ', .active-preset.json')$([ "$RAN_PARITY_CHECK" = true ] && echo ', parity_*.nr')" +echo "✓ check:committee: $ACTIVE_COMMITTEE (H=$EXPECTED_H, T=$EXPECTED_T) consistent across active.nr, ActiveCryptoConfig.sol, utils.ts, Rust committee rows$([ "$RAN_STAMP_CHECK" = true ] && echo ', .active-preset.json')$([ "$RAN_PARITY_CHECK" = true ] && echo ', generated committee artifacts')" diff --git a/scripts/circuit-artifacts.ts b/scripts/circuit-artifacts.ts index 8ad6933cf9..c1f7cdafd1 100644 --- a/scripts/circuit-artifacts.ts +++ b/scripts/circuit-artifacts.ts @@ -22,7 +22,7 @@ async function push() { process.exit(1) } - const hash = run('pnpm tsx scripts/build-circuits.ts hash') + const hash = run('pnpm tsx scripts/build-circuits.ts hash --preset insecure-512 --committee all') const remote = run('git remote get-url origin') const tmp = join(ROOT, '.tmp-circuits') diff --git a/scripts/circuit-constants.ts b/scripts/circuit-constants.ts index 6c3d2b3b61..ddee8478d4 100644 --- a/scripts/circuit-constants.ts +++ b/scripts/circuit-constants.ts @@ -92,10 +92,10 @@ export const COMMITTEE_PARAMS: Record = { } /** - * Every `(preset, committee)` pair is supported because the parity matrices are now regenerated - * automatically from the BFV preset's `QIS` and the committee's `(N, T)` by the + * Every `(preset, committee)` pair is supported because committee-dependent circuit artifacts are + * regenerated automatically from the BFV presets and the committee's `(N, T)` by the * `generate_parity_matrices` Rust binary, invoked from `scripts/build-circuits.ts` whenever - * the committee is set. The matrix files on disk are derived artifacts, not hand-tuned data. + * the committee is set. The parity matrices and smudging constants are derived artifacts. * * This constant is kept for future use (e.g. if a particular pair is ever known-broken at * a higher level than the parity matrix) and currently returns the full Cartesian product. diff --git a/scripts/generate-verifiers.ts b/scripts/generate-verifiers.ts index 6ecb0d69d7..635de2bb81 100644 --- a/scripts/generate-verifiers.ts +++ b/scripts/generate-verifiers.ts @@ -432,8 +432,7 @@ class VerifierGenerator { if (emittedCount === 0 && correctedCount === 1) return solidity if (emittedCount !== 1 || correctedCount !== 0) { throw new Error( - `Expected one generated FRLIB_MODEXP_FAILED_SELECTOR declaration ` + - `(found ${emittedCount} emitted, ${correctedCount} corrected)`, + `Expected one generated FRLIB_MODEXP_FAILED_SELECTOR declaration ` + `(found ${emittedCount} emitted, ${correctedCount} corrected)`, ) } @@ -445,12 +444,10 @@ class VerifierGenerator { if (solidity.split(errorMarker).length !== 2) { throw new Error('Expected one generated Errors.ConsistencyCheckFailed declaration') } - solidity = solidity.replace( - errorMarker, - `${errorMarker}\n error VerificationKeyConfigurationMismatch();`, - ) + solidity = solidity.replace(errorMarker, `${errorMarker}\n error VerificationKeyConfigurationMismatch();`) - const constructorPattern = /(constructor\(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs\)\s*\{[\s\S]*?\n\s*\})(\s*function verify\s*\()/ + const constructorPattern = + /(constructor\(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs\)\s*\{[\s\S]*?\n\s*\})(\s*function verify\s*\()/ if (!constructorPattern.test(solidity)) { throw new Error('Expected generated Honk verifier base constructor') } diff --git a/scripts/invariant-baselines.env b/scripts/invariant-baselines.env index 18051a5f0a..934126e337 100644 --- a/scripts/invariant-baselines.env +++ b/scripts/invariant-baselines.env @@ -4,4 +4,7 @@ # Call sites of `.do_send(` in crates/**/*.rs — fire-and-forget sends, allowed only for # best-effort telemetry (agent/ARCHITECTURE.md §Routing, agent/INVARIANTS.md). -DO_SEND_BASELINE=107 +# Raised 107 -> 108 in the chunked-C2 PR: multithread.rs reports each chunked +# ZkShareComputation sub-step via the existing telemetry `report` channel +# (`report.do_send(TrackDuration::new(...))`, same pattern as the 3 prior sites). +DO_SEND_BASELINE=108 diff --git a/scripts/lint-circuits.sh b/scripts/lint-circuits.sh index 354d7a8ecf..b5458e3c99 100755 --- a/scripts/lint-circuits.sh +++ b/scripts/lint-circuits.sh @@ -14,7 +14,10 @@ cd circuits DIRS=( "lib" "bin/config" - "bin/recursive_aggregation/c2ab_fold" + "bin/recursive_aggregation/c2_chunk_batch" + "bin/recursive_aggregation/sk_c2_chunk_finalize" + "bin/recursive_aggregation/esm_c2_chunk_finalize" + "bin/recursive_aggregation/c2ab_chunk_fold" "bin/recursive_aggregation/c3_fold" "bin/recursive_aggregation/c3_fold_kernel" "bin/recursive_aggregation/c6_fold" @@ -24,9 +27,11 @@ DIRS=( "bin/recursive_aggregation/node_fold" "bin/recursive_aggregation/nodes_fold" "bin/recursive_aggregation/nodes_fold_kernel" + "bin/recursive_aggregation/dummy_nodes_inner" "bin/recursive_aggregation/dkg_aggregator" "bin/recursive_aggregation/decryption_aggregator" "bin/dkg" + "bin/dkg/dummy_c3_inner" "bin/threshold" )