From ce128a240ccf5066d499e47576b211f1a716b757 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 4 Jun 2026 17:25:08 +0200 Subject: [PATCH 01/16] make script for compilation to take --all flag --- circuits/benchmarks/scripts/run_benchmarks.sh | 6 +- .../src/actors/publickey_aggregator.rs | 7 ++ .../actors/threshold_plaintext_aggregator.rs | 7 ++ crates/aggregator/src/ext.rs | 20 ++++ .../src/enclave_event/compute_request/zk.rs | 3 + crates/fhe-params/src/presets.rs | 10 +- crates/multithread/src/multithread.rs | 14 ++- .../zk-helpers/src/ciphernodes_committee.rs | 11 ++ .../src/actors/node_proof_aggregator.rs | 5 + .../src/actors/proof_verification.rs | 15 ++- .../src/circuits/aggregation/node_dkg_fold.rs | 7 +- crates/zk-prover/src/domain/node_dkg_fold.rs | 5 + crates/zk-prover/tests/common/helpers.rs | 15 ++- scripts/build-circuits.ts | 102 +++++++++--------- 14 files changed, 161 insertions(+), 66 deletions(-) diff --git a/circuits/benchmarks/scripts/run_benchmarks.sh b/circuits/benchmarks/scripts/run_benchmarks.sh index 8453650eb..8fef37bee 100755 --- a/circuits/benchmarks/scripts/run_benchmarks.sh +++ b/circuits/benchmarks/scripts/run_benchmarks.sh @@ -41,9 +41,9 @@ while [[ $# -gt 0 ]]; do --committee) COMMITTEE_OVERRIDE="$2" case "$COMMITTEE_OVERRIDE" in - micro|small|medium) ;; + micro|small|medium|large) ;; *) - echo "Error: --committee must be micro|small|medium (got: $COMMITTEE_OVERRIDE)" + echo "Error: --committee must be micro|small|medium|large (got: $COMMITTEE_OVERRIDE)" exit 1 ;; esac @@ -83,7 +83,7 @@ while [[ $# -gt 0 ]]; do ;; *) echo "Unknown option: $1" - echo "Usage: $0 [--config ] [--mode insecure|secure] [--committee micro|small|medium] [--circuit ] [--skip-compile] [--bench-compile] [--clean] [--verbose] [--proof-aggregation on|off] [--no-proof-aggregation] [--multithread-jobs N]" + echo "Usage: $0 [--config ] [--mode insecure|secure] [--committee micro|small|medium|large] [--circuit ] [--skip-compile] [--bench-compile] [--clean] [--verbose] [--proof-aggregation on|off] [--no-proof-aggregation] [--multithread-jobs N]" exit 1 ;; esac diff --git a/crates/aggregator/src/actors/publickey_aggregator.rs b/crates/aggregator/src/actors/publickey_aggregator.rs index 77d72b3a8..85ca7b063 100644 --- a/crates/aggregator/src/actors/publickey_aggregator.rs +++ b/crates/aggregator/src/actors/publickey_aggregator.rs @@ -26,6 +26,7 @@ use e3_fhe::{Fhe, GetAggregatePublicKey}; use e3_fhe_params::BfvPreset; use e3_utils::NotifySync; use e3_utils::{ArcBytes, MAILBOX_LIMIT}; +use e3_zk_helpers::CiphernodesCommitteeSize; use std::collections::HashMap; use std::sync::Arc; use tracing::{error, info, warn}; @@ -41,6 +42,7 @@ pub struct PublicKeyAggregator { e3_id: E3id, state: Persistable, params_preset: BfvPreset, + committee_size: CiphernodesCommitteeSize, /// DKG recursive aggregation events received before entering GeneratingC5Proof. early_dkg_proofs: Vec>, } @@ -50,6 +52,7 @@ pub struct PublicKeyAggregatorParams { pub bus: BusHandle, pub e3_id: E3id, pub params_preset: BfvPreset, + pub committee_size: CiphernodesCommitteeSize, } /// Aggregate PublicKey for a committee of nodes. This actor listens for KeyshareCreated events @@ -66,6 +69,7 @@ impl PublicKeyAggregator { e3_id: params.e3_id, state, params_preset: params.params_preset, + committee_size: params.committee_size, early_dkg_proofs: Vec::new(), } } @@ -724,6 +728,7 @@ impl PublicKeyAggregator { party_ids, committee_addresses, params_preset: self.params_preset, + committee_size: self.committee_size, }), corr, self.e3_id.clone(), @@ -1329,6 +1334,7 @@ mod tests { bus, e3_id: e3_id.clone(), params_preset: BfvPreset::InsecureThreshold512, + committee_size: CiphernodesCommitteeSize::Micro, }, test_state(initial_state), ); @@ -1357,6 +1363,7 @@ mod tests { .parse() .expect("test address")], params_preset: BfvPreset::InsecureThreshold512, + committee_size: CiphernodesCommitteeSize::Micro, }), correlation_id, e3_id.clone(), diff --git a/crates/aggregator/src/actors/threshold_plaintext_aggregator.rs b/crates/aggregator/src/actors/threshold_plaintext_aggregator.rs index 4fba1ddce..c2e4e0745 100644 --- a/crates/aggregator/src/actors/threshold_plaintext_aggregator.rs +++ b/crates/aggregator/src/actors/threshold_plaintext_aggregator.rs @@ -33,6 +33,7 @@ use e3_trbfv::{ }; use e3_utils::NotifySync; use e3_utils::{utility_types::ArcBytes, MAILBOX_LIMIT}; +use e3_zk_helpers::CiphernodesCommitteeSize; use tracing::{debug, info, trace, warn}; /// Env var overriding the decryption-share collection timeout (seconds). @@ -77,6 +78,7 @@ pub struct ThresholdPlaintextAggregator { sortition: Addr, e3_id: E3id, params_preset: BfvPreset, + committee_size: CiphernodesCommitteeSize, proof_aggregation_enabled: bool, state: Persistable, /// Honest parties' C6 inner proofs (sorted by party id) for [`ZkRequest::DecryptionAggregation`]. @@ -110,6 +112,7 @@ pub struct ThresholdPlaintextAggregatorParams { pub sortition: Addr, pub e3_id: E3id, pub params_preset: BfvPreset, + pub committee_size: CiphernodesCommitteeSize, pub proof_aggregation_enabled: bool, /// Full committee from `PublicKeyAggregated.committee_addresses` (length `N`). /// Used for `committee_hash_*` payload binding to on-chain `topNodes`. @@ -129,6 +132,7 @@ impl ThresholdPlaintextAggregator { sortition: params.sortition, e3_id: params.e3_id, params_preset: params.params_preset, + committee_size: params.committee_size, proof_aggregation_enabled: params.proof_aggregation_enabled, state, honest_c6_proofs_for_agg: None, @@ -508,6 +512,7 @@ impl ThresholdPlaintextAggregator { jobs, committee_addresses: self.committee_addresses.clone(), params_preset: self.params_preset, + committee_size: self.committee_size, }), corr, self.e3_id.clone(), @@ -1099,6 +1104,7 @@ mod tests { sortition: start_sortition(&bus), e3_id: e3_id.clone(), params_preset: BfvPreset::InsecureThreshold512, + committee_size: CiphernodesCommitteeSize::Micro, proof_aggregation_enabled, committee_addresses: vec![test_committee_address()], honest_committee_addresses: vec![test_committee_address()], @@ -1238,6 +1244,7 @@ mod tests { jobs: Vec::new(), committee_addresses: vec![test_committee_address()], params_preset: BfvPreset::InsecureThreshold512, + committee_size: CiphernodesCommitteeSize::Micro, }), correlation_id, e3_id.clone(), diff --git a/crates/aggregator/src/ext.rs b/crates/aggregator/src/ext.rs index 49159ddcc..fba5d9031 100644 --- a/crates/aggregator/src/ext.rs +++ b/crates/aggregator/src/ext.rs @@ -26,6 +26,7 @@ use e3_fhe::Fhe; use e3_fhe_params::BfvPreset; use e3_request::{E3Context, E3ContextSnapshot, E3Extension, TypedKey, META_KEY}; use e3_sortition::Sortition; +use e3_zk_helpers::CiphernodesCommitteeSize; /// Full finalized committee (`PublicKeyAggregated.committee_addresses`, length `N`) /// for `committee_hash_*` binding in downstream ZK requests. @@ -83,12 +84,15 @@ impl E3Extension for PublicKeyAggregatorExtension { seed, ))); + let committee_size = CiphernodesCommitteeSize::from_threshold(threshold_m, threshold_n) + .unwrap_or(CiphernodesCommitteeSize::Micro); let value = create_publickey_aggregator( fhe.clone(), self.bus.clone(), e3_id, sync_state, params_preset, + committee_size, ); ctx.set_event_recipient("publickey", Some(value)); @@ -125,12 +129,16 @@ impl E3Extension for PublicKeyAggregatorExtension { return Ok(()); }; + let committee_size = + CiphernodesCommitteeSize::from_threshold(meta.threshold_m, meta.threshold_n) + .unwrap_or(CiphernodesCommitteeSize::Micro); let value = create_publickey_aggregator( fhe.clone(), self.bus.clone(), ctx.e3_id.clone(), sync_state, meta.params_preset, + committee_size, ); // send to context @@ -146,6 +154,7 @@ fn create_publickey_aggregator( e3_id: E3id, sync_state: Persistable, params_preset: BfvPreset, + committee_size: CiphernodesCommitteeSize, ) -> Recipient { KeyshareCreatedFilterBuffer::new( PublicKeyAggregator::new( @@ -154,6 +163,7 @@ fn create_publickey_aggregator( bus, e3_id, params_preset, + committee_size, }, sync_state, ) @@ -309,6 +319,11 @@ impl E3Extension for ThresholdPlaintextAggregatorExtension { sortition: self.sortition.clone(), e3_id: e3_id.clone(), params_preset: meta.params_preset, + committee_size: CiphernodesCommitteeSize::from_threshold( + meta.threshold_m, + meta.threshold_n, + ) + .unwrap_or(CiphernodesCommitteeSize::Micro), proof_aggregation_enabled: meta.proof_aggregation_enabled, committee_addresses, honest_committee_addresses, @@ -355,6 +370,11 @@ impl E3Extension for ThresholdPlaintextAggregatorExtension { sortition: self.sortition.clone(), e3_id: ctx.e3_id.clone(), params_preset: meta.params_preset, + committee_size: CiphernodesCommitteeSize::from_threshold( + meta.threshold_m, + meta.threshold_n, + ) + .unwrap_or(CiphernodesCommitteeSize::Micro), proof_aggregation_enabled: meta.proof_aggregation_enabled, committee_addresses, honest_committee_addresses, diff --git a/crates/events/src/enclave_event/compute_request/zk.rs b/crates/events/src/enclave_event/compute_request/zk.rs index f08496e2e..d4a8c133f 100644 --- a/crates/events/src/enclave_event/compute_request/zk.rs +++ b/crates/events/src/enclave_event/compute_request/zk.rs @@ -68,6 +68,7 @@ pub struct NodeDkgFoldRequest { pub c3_total_slots: usize, pub party_id: u64, pub params_preset: BfvPreset, + pub committee_size: CiphernodesCommitteeSize, } /// Cross-node DKG aggregation (NodesFold + C5 + DkgAggregator). @@ -79,6 +80,7 @@ pub struct DkgAggregationRequest { /// Ordered committee addresses (`topNodes`) for `committee_hash_*` public inputs. pub committee_addresses: Vec
, pub params_preset: BfvPreset, + pub committee_size: CiphernodesCommitteeSize, } /// Decryption aggregation: sequential C6 fold + DecryptionAggregator per job. @@ -89,6 +91,7 @@ pub struct DecryptionAggregationRequest { /// Ordered committee addresses (`topNodes`) for `committee_hash_*` public inputs. pub committee_addresses: Vec
, pub params_preset: BfvPreset, + pub committee_size: CiphernodesCommitteeSize, } /// Request to generate a proof for public key aggregation (C5). diff --git a/crates/fhe-params/src/presets.rs b/crates/fhe-params/src/presets.rs index 02ee42310..4a533c43d 100644 --- a/crates/fhe-params/src/presets.rs +++ b/crates/fhe-params/src/presets.rs @@ -386,13 +386,21 @@ impl BfvPreset { self.metadata().security } - /// Returns the directory name for circuit artifacts (e.g. `"insecure-512"`, `"secure-8192"`). + /// Returns the base directory name for circuit artifacts (e.g. `"insecure-512"`, `"secure-8192"`). /// Threshold and DKG presets at the same degree share the same compiled circuits. pub fn artifacts_dir(&self) -> String { let meta = self.metadata(); format!("{}-{}", meta.security.as_config_str(), meta.degree) } + /// Returns the per-committee artifact directory: `"{preset}/{committee}"`. + /// + /// Use this at runtime so each committee size resolves to its own compiled artifacts + /// (e.g. `"secure-8192/medium"`, `"insecure-512/micro"`). + pub fn artifacts_dir_for_committee>(&self, committee: C) -> String { + format!("{}/{}", self.artifacts_dir(), committee.as_ref()) + } + pub fn search_defaults(&self) -> Option { match self { BfvPreset::InsecureThreshold512 => Some(PresetSearchDefaults { diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index 283f9c5e0..bece5425f 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -696,7 +696,9 @@ fn handle_node_dkg_fold_proof( request: ComputeRequest, report: Option>, ) -> Result { - let artifacts_dir = req.params_preset.artifacts_dir(); + let artifacts_dir = req + .params_preset + .artifacts_dir_for_committee(req.committee_size.as_str()); let job_id = zk_bb_work_id(&request); let input = NodeDkgFoldInput { c0_proof: &req.c0_proof, @@ -748,7 +750,14 @@ fn handle_dkg_aggregation_proof( party_ids: &req.party_ids, committee_addresses: &req.committee_addresses, }; - let proof = prove_dkg_aggregation(prover, &input, &job_id, req.params_preset).map_err(|e| { + let proof = prove_dkg_aggregation( + prover, + &input, + &job_id, + req.params_preset, + req.committee_size, + ) + .map_err(|e| { ComputeRequestError::new( ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), request.clone(), @@ -783,6 +792,7 @@ fn handle_decryption_aggregation_proof( &req.committee_addresses, &job_id, req.params_preset, + req.committee_size, ) .map_err(|e| { ComputeRequestError::new( diff --git a/crates/zk-helpers/src/ciphernodes_committee.rs b/crates/zk-helpers/src/ciphernodes_committee.rs index dd3de585e..730c33796 100644 --- a/crates/zk-helpers/src/ciphernodes_committee.rs +++ b/crates/zk-helpers/src/ciphernodes_committee.rs @@ -52,6 +52,17 @@ impl CiphernodesCommitteeSize { } } + /// Derives the committee size from total parties (N) and honest count (H). + pub fn from_n_h(n: usize, h: usize) -> Result { + match (n, h) { + (3, 3) => Ok(Self::Micro), + (5, 5) => Ok(Self::Small), + (10, 8) => Ok(Self::Medium), + (20, 15) => Ok(Self::Large), + _ => bail!("Unknown committee size for (n={n}, h={h})"), + } + } + /// Lower-case name as written into `circuits/bin/.active-preset.json` and the /// `--committee` flag of `scripts/build-circuits.ts`. Use this for stamp/env cross-checks. pub fn as_str(self) -> &'static str { diff --git a/crates/zk-prover/src/actors/node_proof_aggregator.rs b/crates/zk-prover/src/actors/node_proof_aggregator.rs index fb914d193..ae7d1b792 100644 --- a/crates/zk-prover/src/actors/node_proof_aggregator.rs +++ b/crates/zk-prover/src/actors/node_proof_aggregator.rs @@ -132,6 +132,7 @@ impl NodeProofAggregator { committee_h, n_moduli, params_preset: msg.proof_request.params_preset, + committee_size: msg.proof_request.committee_size, }; info!( @@ -498,6 +499,7 @@ mod tests { NodeDkgFoldRequest, TakeEvents, Unsequenced, ZkError, }; use e3_test_helpers::get_common_setup; + use e3_zk_helpers::CiphernodesCommitteeSize; fn test_ctx(data: impl Into) -> EventContext { EventContext::::from(data.into()).sequence(0) @@ -544,6 +546,7 @@ mod tests { committee_h: 0, n_moduli: 0, params_preset: e3_fhe_params::BfvPreset::InsecureThreshold512, + committee_size: CiphernodesCommitteeSize::Micro, }, buffer: BTreeMap::new(), fold_correlation: Some(correlation_id), @@ -574,6 +577,7 @@ mod tests { c3_total_slots: 0, party_id: 7, params_preset: e3_fhe_params::BfvPreset::InsecureThreshold512, + committee_size: CiphernodesCommitteeSize::Micro, }), correlation_id, e3_id.clone(), @@ -649,6 +653,7 @@ mod tests { committee_h: 0, n_moduli: 0, params_preset: e3_fhe_params::BfvPreset::InsecureThreshold512, + committee_size: CiphernodesCommitteeSize::Micro, }, test_ctx(DKGRecursiveAggregationComplete { e3_id: e3_id.clone(), diff --git a/crates/zk-prover/src/actors/proof_verification.rs b/crates/zk-prover/src/actors/proof_verification.rs index 175966b3a..762e150da 100644 --- a/crates/zk-prover/src/actors/proof_verification.rs +++ b/crates/zk-prover/src/actors/proof_verification.rs @@ -22,6 +22,7 @@ use e3_events::{ }; use e3_fhe_params::BfvPreset; use e3_utils::NotifySync; +use e3_zk_helpers::CiphernodesCommitteeSize; use tracing::{error, info, warn}; use crate::domain::proof_verification::validate_external_key; @@ -55,8 +56,8 @@ pub struct ProofVerificationActor { bus: BusHandle, verifier: Recipient>, pending: HashMap<(E3id, u64), PendingVerification>, - /// Tracks `BfvPreset` per E3 so we can derive `artifacts_dir` for proof verification. - presets: HashMap, + /// Tracks preset + committee per E3 so we can derive `artifacts_dir` for proof verification. + presets: HashMap, } impl ProofVerificationActor { @@ -111,7 +112,7 @@ impl ProofVerificationActor { }, ); - let Some(preset) = self.presets.get(&msg.e3_id).copied() else { + let Some((preset, committee)) = self.presets.get(&msg.e3_id).copied() else { error!( "No BfvPreset known for e3_id={} — cannot determine circuit artifacts directory. \ This can happen if CiphernodeSelected was missed (e.g. after restart). Rejecting key from party {}.", @@ -119,7 +120,7 @@ impl ProofVerificationActor { ); return; }; - let artifacts_dir = preset.artifacts_dir(); + let artifacts_dir = preset.artifacts_dir_for_committee(committee.as_str()); let request = TypedEvent::new( ZkVerificationRequest { @@ -165,7 +166,11 @@ impl Handler for ProofVerificationActor { let (msg, ec) = msg.into_components(); match msg { EnclaveEventData::CiphernodeSelected(data) => { - self.presets.insert(data.e3_id.clone(), data.params_preset); + let committee = + CiphernodesCommitteeSize::from_threshold(data.threshold_m, data.threshold_n) + .unwrap_or(CiphernodesCommitteeSize::Micro); + self.presets + .insert(data.e3_id.clone(), (data.params_preset, committee)); } EnclaveEventData::EncryptionKeyReceived(data) => { self.notify_sync(ctx, TypedEvent::new(data, ec)) 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 1ea2888d1..471244ad9 100644 --- a/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs +++ b/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs @@ -19,6 +19,7 @@ use crate::witness::{CompiledCircuit, WitnessGenerator}; use alloy::primitives::Address; use e3_events::{CircuitName, CircuitVariant, Proof}; use e3_fhe_params::BfvPreset; +use e3_zk_helpers::CiphernodesCommitteeSize; use serde::Serialize; use std::time::Instant; @@ -365,8 +366,9 @@ pub fn prove_dkg_aggregation( input: &DkgAggregationInput, e3_id: &str, preset: BfvPreset, + committee: CiphernodesCommitteeSize, ) -> Result { - let artifacts_dir = preset.artifacts_dir(); + let artifacts_dir = preset.artifacts_dir_for_committee(committee.as_str()); let artifacts_dir = artifacts_dir.as_str(); if input.node_fold_proofs.len() != input.party_ids.len() { return Err(ZkError::InvalidInput( @@ -495,8 +497,9 @@ pub fn prove_decryption_aggregation_jobs( committee_addresses: &[Address], e3_id: &str, preset: BfvPreset, + committee: CiphernodesCommitteeSize, ) -> Result, ZkError> { - let artifacts_dir = preset.artifacts_dir(); + let artifacts_dir = preset.artifacts_dir_for_committee(committee.as_str()); let artifacts_dir = artifacts_dir.as_str(); // VKs and the compiled circuit are job-independent: load once, reuse per ciphertext. let c6_fold_vk = vk::load_vk_artifacts( diff --git a/crates/zk-prover/src/domain/node_dkg_fold.rs b/crates/zk-prover/src/domain/node_dkg_fold.rs index baf5eae19..20fb7980f 100644 --- a/crates/zk-prover/src/domain/node_dkg_fold.rs +++ b/crates/zk-prover/src/domain/node_dkg_fold.rs @@ -13,6 +13,8 @@ use std::collections::BTreeMap; +use e3_zk_helpers::CiphernodesCommitteeSize; + use e3_events::{ CorrelationId, EventContext, NodeDkgFoldRequest, Proof, Sequenced, ShareEncryptionProofRequest, }; @@ -29,6 +31,7 @@ pub(crate) struct NodeDkgFoldMeta { pub(crate) committee_h: usize, pub(crate) n_moduli: usize, pub(crate) params_preset: e3_fhe_params::BfvPreset, + pub(crate) committee_size: CiphernodesCommitteeSize, } impl NodeDkgFoldMeta { @@ -124,6 +127,7 @@ impl DkgProofCollectionState { c3_total_slots, party_id: meta.party_id, params_preset: meta.params_preset, + committee_size: meta.committee_size, } } } @@ -146,6 +150,7 @@ mod tests { committee_h: 2, n_moduli: 2, params_preset: e3_fhe_params::BfvPreset::InsecureThreshold512, + committee_size: CiphernodesCommitteeSize::Micro, } } diff --git a/crates/zk-prover/tests/common/helpers.rs b/crates/zk-prover/tests/common/helpers.rs index 28bfd3556..e6f12103a 100644 --- a/crates/zk-prover/tests/common/helpers.rs +++ b/crates/zk-prover/tests/common/helpers.rs @@ -22,6 +22,15 @@ 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, "micro").await; +} + +pub async fn setup_compiled_circuit_for_committee( + backend: &ZkBackend, + group: &str, + circuit_name: &str, + committee: &str, +) { 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")); @@ -42,8 +51,8 @@ pub async fn setup_compiled_circuit(backend: &ZkBackend, group: &str, circuit_na vk_evm_path.display() ); - // Tests use insecure params — fixtures go under insecure-512/ - let preset_dir = backend.circuits_dir.join("insecure-512"); + // Tests use insecure params — fixtures go under insecure-512/{committee}/ + let preset_dir = backend.circuits_dir.join("insecure-512").join(committee); // Set up the evm variant directory (keccak VK + hash) let evm_dir = preset_dir.join("evm").join(group).join(circuit_name); @@ -152,7 +161,7 @@ pub async fn setup_recursive_aggregation_fold_circuit(backend: &ZkBackend, circu vk_recursive_path.display() ); - let preset_dir = backend.circuits_dir.join("insecure-512"); + let preset_dir = backend.circuits_dir.join("insecure-512").join("micro"); 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"))) diff --git a/scripts/build-circuits.ts b/scripts/build-circuits.ts index 1f91ed754..66e1172bc 100644 --- a/scripts/build-circuits.ts +++ b/scripts/build-circuits.ts @@ -66,8 +66,8 @@ interface BuildOptions { hydrateBinOnly?: boolean dryRun?: boolean preset?: CircuitPreset | 'all' - /** Active committee size — drives `committee/active.nr` and verifier H/T. */ - committee?: CircuitCommittee + /** Active committee size — drives `committee/active.nr` and verifier H/T. Pass `'all'` to build every committee. */ + committee?: CircuitCommittee | 'all' /** Skip writing BFV_DKG_H/T into `packages/enclave-contracts/scripts/utils.ts`. */ skipUtilsPatch?: boolean } @@ -106,7 +106,7 @@ class NoirCircuitBuilder { ...options, } - if (this.options.preset !== 'all' && this.options.committee) { + if (this.options.preset !== 'all' && this.options.committee && this.options.committee !== 'all') { if (!isPresetCommitteeSupported(this.options.preset as CircuitPreset, this.options.committee)) { throw new Error( `Unsupported preset/committee pair (${this.options.preset}, ${this.options.committee}). ` + @@ -119,8 +119,10 @@ class NoirCircuitBuilder { async buildAll(): Promise { const result: BuildResult = { success: true, compiled: [], errors: [] } const presets: CircuitPreset[] = this.options.preset === 'all' ? ALL_PRESETS : [this.options.preset!] + const committees: CircuitCommittee[] = + this.options.committee === 'all' ? ALL_COMMITTEES : [this.options.committee ?? CIRCUIT_COMMITTEES.MICRO] - console.log(`🔮 Building Noir circuits for preset(s): ${presets.join(', ')}...`) + console.log(`🔮 Building Noir circuits for preset(s): ${presets.join(', ')}, committee(s): ${committees.join(', ')}...`) const modNrPath = join(this.rootDir, 'circuits', 'lib', 'src', 'configs', 'default', 'mod.nr') @@ -135,11 +137,13 @@ class NoirCircuitBuilder { mkdirSync(this.options.outputDir!, { recursive: true }) for (const preset of presets) { - const presetResult = await this.buildForPreset(preset, modNrPath) - result.compiled.push(...presetResult.compiled) - result.errors.push(...presetResult.errors) - if (!presetResult.success) result.success = false - if (presetResult.sourceHash && !result.sourceHash) result.sourceHash = presetResult.sourceHash + for (const committee of committees) { + const presetResult = await this.buildForPreset(preset, committee, modNrPath) + result.compiled.push(...presetResult.compiled) + result.errors.push(...presetResult.errors) + if (!presetResult.success) result.success = false + if (presetResult.sourceHash && !result.sourceHash) result.sourceHash = presetResult.sourceHash + } } if (!this.options.skipChecksums && result.compiled.length > 0) { @@ -290,12 +294,12 @@ class NoirCircuitBuilder { } } - private presetStampPath(preset: string): string { - return join(this.options.outputDir!, preset, '.build-stamp.json') + private presetStampPath(preset: string, committee: string): string { + return join(this.options.outputDir!, preset, committee, '.build-stamp.json') } - private readPresetStamp(preset: string): PresetBuildStamp | null { - const stampPath = this.presetStampPath(preset) + private readPresetStamp(preset: string, committee: string): PresetBuildStamp | null { + const stampPath = this.presetStampPath(preset, committee) if (!existsSync(stampPath)) return null try { return JSON.parse(readFileSync(stampPath, 'utf-8')) as PresetBuildStamp @@ -304,15 +308,15 @@ class NoirCircuitBuilder { } } - private writePresetStamp(preset: string, sourceHash: string): void { + private writePresetStamp(preset: string, committee: string, sourceHash: string): void { const stamp: PresetBuildStamp = { preset, - committee: this.options.committee, + committee: committee as CircuitCommittee, sourceHash, builtAt: new Date().toISOString(), } - mkdirSync(join(this.options.outputDir!, preset), { recursive: true }) - writeFileSync(this.presetStampPath(preset), JSON.stringify(stamp, null, 2) + '\n') + mkdirSync(join(this.options.outputDir!, preset, committee), { recursive: true }) + writeFileSync(this.presetStampPath(preset, committee), JSON.stringify(stamp, null, 2) + '\n') } /** @@ -401,29 +405,26 @@ class NoirCircuitBuilder { } } - private isDistPresetUpToDate(preset: string, sourceHash: string): boolean { - const stamp = this.readPresetStamp(preset) + private isDistPresetUpToDate(preset: string, committee: string, sourceHash: string): boolean { + const stamp = this.readPresetStamp(preset, committee) if (!stamp?.sourceHash || stamp.sourceHash !== sourceHash) return false return this.requiredDistMarkers(preset).every((path) => existsSync(path)) } - private isBinReadyForPreset(preset: string, sourceHash: string): boolean { + private isBinReadyForPreset(preset: string, committee: string, sourceHash: string): boolean { const active = this.readActiveBinPreset() if (!active || active.preset !== preset || active.sourceHash !== sourceHash) return false - // Committee mismatch is also a cache miss: the same circuit sources can compile to - // different bytecode under a different committee, which the source hash captures, but - // we double-check the stamp field for clearer diagnostics when --skip-if-built kicks in. - if (this.options.committee && active.committee && active.committee !== this.options.committee) return false + if (active.committee && active.committee !== committee) return false return this.requiredBinMarkers().every((path) => existsSync(path)) } - private isPresetUpToDate(preset: string, sourceHash: string): boolean { - return this.isDistPresetUpToDate(preset, sourceHash) && this.isBinReadyForPreset(preset, sourceHash) + private isPresetUpToDate(preset: string, committee: string, sourceHash: string): boolean { + return this.isDistPresetUpToDate(preset, committee, sourceHash) && this.isBinReadyForPreset(preset, committee, sourceHash) } - private logSkipIfBuiltBlocked(preset: string, sourceHash: string): void { - const stamp = this.readPresetStamp(preset) - const stampPath = this.presetStampPath(preset) + private logSkipIfBuiltBlocked(preset: string, committee: string, sourceHash: string): void { + const stamp = this.readPresetStamp(preset, committee) + const stampPath = this.presetStampPath(preset, committee) if (!stamp?.sourceHash) { console.log(` ℹ️ --skip-if-built: no stamp at ${stampPath}`) return @@ -453,11 +454,11 @@ class NoirCircuitBuilder { } } - private async buildForPreset(preset: CircuitPreset, modNrPath?: string): Promise { + private async buildForPreset(preset: CircuitPreset, committee: CircuitCommittee, modNrPath?: string): Promise { const result: BuildResult = { success: true, compiled: [], errors: [] } - const presetOutputDir = join(this.options.outputDir!, preset) + const presetOutputDir = join(this.options.outputDir!, preset, committee) - console.log(`\n🔮 Building preset: ${preset}...`) + console.log(`\n🔮 Building preset: ${preset}, committee: ${committee}...`) try { this.checkTool('nargo --version', 'nargo') @@ -480,39 +481,40 @@ class NoirCircuitBuilder { result.sourceHash = sourceHash if (modNrPath) { - this.syncPresetAndCommittee(modNrPath, preset, this.options.committee) + this.syncPresetAndCommittee(modNrPath, preset, committee) } if (this.options.hydrateBinOnly) { - if (!this.isDistPresetUpToDate(preset, sourceHash)) { + if (!this.isDistPresetUpToDate(preset, committee, sourceHash)) { throw new Error( - `Cannot hydrate circuits/bin: dist/circuits/${preset} is missing or stale. ` + `Run: pnpm build:circuits --preset ${preset}`, + `Cannot hydrate circuits/bin: dist/circuits/${preset}/${committee} is missing or stale. ` + + `Run: pnpm build:circuits --preset ${preset} --committee ${committee}`, ) } - console.log(` 💧 Hydrating circuits/bin from dist/circuits/${preset} (no nargo compile)...`) + console.log(` 💧 Hydrating circuits/bin from dist/circuits/${preset}/${committee} (no nargo compile)...`) this.hydrateBinFromDist(preset, sourceHash) - console.log(`\n✅ Hydrated circuits/bin for preset: ${preset}`) + console.log(`\n✅ Hydrated circuits/bin for preset: ${preset}/${committee}`) return result } if (this.options.skipIfBuilt) { - if (this.isPresetUpToDate(preset, sourceHash)) { + if (this.isPresetUpToDate(preset, committee, sourceHash)) { console.log( - ` ⏭️ Skipping preset ${preset} (dist + circuits/bin up to date; source_hash=${sourceHash}). ` + + ` ⏭️ Skipping preset ${preset}/${committee} (dist + circuits/bin up to date; source_hash=${sourceHash}). ` + `Use a full rebuild without --skip-if-built to refresh.`, ) return result } - if (this.isDistPresetUpToDate(preset, sourceHash)) { + if (this.isDistPresetUpToDate(preset, committee, sourceHash)) { console.log( - ` 💧 dist/circuits/${preset} is current; hydrating circuits/bin from dist ` + + ` 💧 dist/circuits/${preset}/${committee} is current; hydrating circuits/bin from dist ` + `(fast — avoids a full ~50m secure recompile when switching presets).`, ) this.hydrateBinFromDist(preset, sourceHash) - console.log(`\n✅ Hydrated circuits/bin for preset: ${preset}`) + console.log(`\n✅ Hydrated circuits/bin for preset: ${preset}/${committee}`) return result } - this.logSkipIfBuiltBlocked(preset, sourceHash) + this.logSkipIfBuiltBlocked(preset, committee, sourceHash) } if (!this.options.noCleanTargets) { @@ -524,17 +526,17 @@ class NoirCircuitBuilder { try { result.compiled.push(this.buildCircuit(circuit, preset)) } catch (error: any) { - result.errors.push(`${preset}/${circuit.name}: ${error.message}`) + result.errors.push(`${preset}/${committee}/${circuit.name}: ${error.message}`) result.success = false } } this.copyArtifacts(result.compiled, presetOutputDir, preset) if (result.errors.length === 0) { - this.writePresetStamp(preset, sourceHash) + this.writePresetStamp(preset, committee, sourceHash) this.writeActiveBinPresetStamp(preset, sourceHash) } - console.log(`\n✅ Built ${result.compiled.length} circuits for preset: ${preset}`) + console.log(`\n✅ Built ${result.compiled.length} circuits for preset: ${preset}/${committee}`) if (result.errors.length > 0) { console.error('\n❌ Failed circuits:') for (const err of result.errors) console.error(` ${err}`) @@ -1026,11 +1028,11 @@ async function main() { options.preset = val as CircuitPreset | 'all' } else if (arg === '--committee') { const val = args[++i] - if (!ALL_COMMITTEES.includes(val as CircuitCommittee)) { - console.error(`Unknown committee: ${val}. Valid values: ${ALL_COMMITTEES.join(', ')}`) + if (val !== 'all' && !ALL_COMMITTEES.includes(val as CircuitCommittee)) { + console.error(`Unknown committee: ${val}. Valid values: ${ALL_COMMITTEES.join(', ')}, all`) process.exit(1) } - options.committee = val as CircuitCommittee + options.committee = val as CircuitCommittee | 'all' } else if (arg === '--skip-utils-patch') options.skipUtilsPatch = true else if (['hash', 'build'].includes(arg)) command = arg } @@ -1059,7 +1061,7 @@ Options: --group Circuit groups (comma-separated: dkg,threshold) --circuit Build specific circuit(s) --preset Parameter preset: insecure-512 (default), secure-8192, or all - --committee Committee size: micro (default), small, medium, large + --committee Committee size: micro (default), small, medium, large, or all --skip-utils-patch Don't rewrite BFV_DKG_H/T in packages/enclave-contracts/scripts/utils.ts --skip-vk Skip verification key generation --skip-checksums Skip checksum generation From c9bae4b62814e968c07f707b9d8c1574f721db3a Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 4 Jun 2026 17:50:19 +0200 Subject: [PATCH 02/16] coderabbits issues --- crates/aggregator/src/ext.rs | 30 ++++++++++++++----- crates/multithread/src/multithread.rs | 11 ++++--- .../src/actors/proof_verification.rs | 18 +++++++---- scripts/build-circuits.ts | 16 +++++----- 4 files changed, 51 insertions(+), 24 deletions(-) diff --git a/crates/aggregator/src/ext.rs b/crates/aggregator/src/ext.rs index fba5d9031..47375bbc8 100644 --- a/crates/aggregator/src/ext.rs +++ b/crates/aggregator/src/ext.rs @@ -84,8 +84,16 @@ impl E3Extension for PublicKeyAggregatorExtension { seed, ))); - let committee_size = CiphernodesCommitteeSize::from_threshold(threshold_m, threshold_n) - .unwrap_or(CiphernodesCommitteeSize::Micro); + let committee_size = match CiphernodesCommitteeSize::from_threshold(threshold_m, threshold_n) { + Ok(c) => c, + Err(e) => { + self.bus.err( + EType::PublickeyAggregation, + anyhow!("Unknown committee size for E3 {e3_id} (threshold_m={threshold_m}, threshold_n={threshold_n}): {e}"), + ); + return; + } + }; let value = create_publickey_aggregator( fhe.clone(), self.bus.clone(), @@ -131,7 +139,7 @@ impl E3Extension for PublicKeyAggregatorExtension { }; let committee_size = CiphernodesCommitteeSize::from_threshold(meta.threshold_m, meta.threshold_n) - .unwrap_or(CiphernodesCommitteeSize::Micro); + .map_err(|e| anyhow!("Unknown committee size (threshold_m={}, threshold_n={}): {e}", meta.threshold_m, meta.threshold_n))?; let value = create_publickey_aggregator( fhe.clone(), self.bus.clone(), @@ -319,11 +327,19 @@ impl E3Extension for ThresholdPlaintextAggregatorExtension { sortition: self.sortition.clone(), e3_id: e3_id.clone(), params_preset: meta.params_preset, - committee_size: CiphernodesCommitteeSize::from_threshold( + committee_size: match CiphernodesCommitteeSize::from_threshold( meta.threshold_m, meta.threshold_n, - ) - .unwrap_or(CiphernodesCommitteeSize::Micro), + ) { + Ok(c) => c, + Err(e) => { + self.bus.err( + EType::PlaintextAggregation, + anyhow!("Unknown committee size for E3 {e3_id} (threshold_m={}, threshold_n={}): {e}", meta.threshold_m, meta.threshold_n), + ); + return; + } + }, proof_aggregation_enabled: meta.proof_aggregation_enabled, committee_addresses, honest_committee_addresses, @@ -374,7 +390,7 @@ impl E3Extension for ThresholdPlaintextAggregatorExtension { meta.threshold_m, meta.threshold_n, ) - .unwrap_or(CiphernodesCommitteeSize::Micro), + .map_err(|e| anyhow!("Unknown committee size (threshold_m={}, threshold_n={}): {e}", meta.threshold_m, meta.threshold_n))?, proof_aggregation_enabled: meta.proof_aggregation_enabled, committee_addresses, honest_committee_addresses, diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index bece5425f..c4d4cd272 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -70,6 +70,7 @@ use e3_zk_helpers::dkg::share_encryption::{ShareEncryptionCircuit, ShareEncrypti 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::{ prove_decryption_aggregation_jobs, prove_dkg_aggregation, prove_node_dkg_fold, CircuitVariant, DecryptionAggregationJob, DkgAggregationInput, NodeDkgFoldInput, NodeDkgFoldProveResult, @@ -365,7 +366,9 @@ fn handle_pk_aggregation_proof( // `verify_honk_proof_non_zk`. The EVM-facing proof for on-chain is `CircuitName::DkgAggregator`. let circuit = PkAggregationCircuit; let bb_work_id = zk_bb_work_id(&request); - let artifacts_dir = req.params_preset.artifacts_dir(); + let committee = CiphernodesCommitteeSize::from_n_h(req.committee_n, req.committee_h) + .map_err(|e| make_zk_error(&request, format!("unknown committee (n={}, h={}): {e}", req.committee_n, req.committee_h)))?; + let artifacts_dir = req.params_preset.artifacts_dir_for_committee(committee.as_str()); let proof = circuit .prove_with_variant( prover, @@ -889,7 +892,7 @@ fn handle_share_computation_proof( let bb_work = zk_bb_work_id(&request); let inner_job_id = format!("{bb_work}_c2_inner"); - let artifacts_dir = req.params_preset.artifacts_dir(); + let artifacts_dir = req.params_preset.artifacts_dir_for_committee(req.committee_size.as_str()); // 7. Inner C2 proof (sk_share_computation or e_sm_share_computation) let circuit = ShareComputationCircuit; @@ -973,7 +976,7 @@ fn handle_pk_generation_proof( // 5. Generate proof via Provable trait let circuit = PkGenerationCircuit; let bb_work = zk_bb_work_id(&request); - let artifacts_dir = req.params_preset.artifacts_dir(); + let artifacts_dir = req.params_preset.artifacts_dir_for_committee(req.committee_size.as_str()); let proof = circuit .prove( @@ -1111,7 +1114,7 @@ fn handle_share_encryption_proof( // 6. Generate proof (preset = threshold preset; Inputs::compute derives DKG internally) let circuit = ShareEncryptionCircuit; let bb_work = zk_bb_work_id(&request); - let artifacts_dir = req.params_preset.artifacts_dir(); + let artifacts_dir = req.params_preset.artifacts_dir_for_committee(req.committee_size.as_str()); let proof = circuit .prove( prover, diff --git a/crates/zk-prover/src/actors/proof_verification.rs b/crates/zk-prover/src/actors/proof_verification.rs index 762e150da..701a8dcc7 100644 --- a/crates/zk-prover/src/actors/proof_verification.rs +++ b/crates/zk-prover/src/actors/proof_verification.rs @@ -166,11 +166,19 @@ impl Handler for ProofVerificationActor { let (msg, ec) = msg.into_components(); match msg { EnclaveEventData::CiphernodeSelected(data) => { - let committee = - CiphernodesCommitteeSize::from_threshold(data.threshold_m, data.threshold_n) - .unwrap_or(CiphernodesCommitteeSize::Micro); - self.presets - .insert(data.e3_id.clone(), (data.params_preset, committee)); + match CiphernodesCommitteeSize::from_threshold(data.threshold_m, data.threshold_n) { + Ok(committee) => { + self.presets.insert(data.e3_id.clone(), (data.params_preset, committee)); + } + Err(e) => { + error!( + "ProofVerificationActor: unrecognised committee for E3 {} \ + (threshold_m={}, threshold_n={}): {e} — skipping preset registration, \ + proof verification will be rejected if a key arrives", + data.e3_id, data.threshold_m, data.threshold_n + ); + } + } } EnclaveEventData::EncryptionKeyReceived(data) => { self.notify_sync(ctx, TypedEvent::new(data, ec)) diff --git a/scripts/build-circuits.ts b/scripts/build-circuits.ts index 66e1172bc..9c36829f5 100644 --- a/scripts/build-circuits.ts +++ b/scripts/build-circuits.ts @@ -340,8 +340,8 @@ class NoirCircuitBuilder { * Used when dist is fresh but bin still holds another preset (common after --mode insecure * then --mode secure benchmark runs). */ - private hydrateBinFromDist(preset: string, sourceHash: string): void { - const distRoot = join(this.options.outputDir!, preset) + private hydrateBinFromDist(preset: string, committee: string, sourceHash: string): void { + const distRoot = join(this.options.outputDir!, preset, committee) const circuits = this.discoverCircuits() let copied = 0 @@ -374,8 +374,8 @@ class NoirCircuitBuilder { this.writeActiveBinPresetStamp(preset, sourceHash) } - private requiredDistMarkers(preset: string): string[] { - const dist = join(this.options.outputDir!, preset) + private requiredDistMarkers(preset: string, committee: string): string[] { + const dist = join(this.options.outputDir!, preset, committee) return [ join(dist, CIRCUIT_VARIANTS.DEFAULT, CIRCUIT_GROUPS.AGGREGATION, 'dkg_aggregator', 'dkg_aggregator.json'), join(dist, CIRCUIT_VARIANTS.DEFAULT, CIRCUIT_GROUPS.AGGREGATION, 'decryption_aggregator', 'decryption_aggregator.json'), @@ -408,7 +408,7 @@ class NoirCircuitBuilder { private isDistPresetUpToDate(preset: string, committee: string, sourceHash: string): boolean { const stamp = this.readPresetStamp(preset, committee) if (!stamp?.sourceHash || stamp.sourceHash !== sourceHash) return false - return this.requiredDistMarkers(preset).every((path) => existsSync(path)) + return this.requiredDistMarkers(preset, committee).every((path) => existsSync(path)) } private isBinReadyForPreset(preset: string, committee: string, sourceHash: string): boolean { @@ -435,7 +435,7 @@ class NoirCircuitBuilder { `Run without --skip-if-built or \`pnpm build:circuits --preset ${preset}\` once to refresh.`, ) } - const missing = [...this.requiredDistMarkers(preset), ...this.requiredBinMarkers()].filter((path) => !existsSync(path)) + const missing = [...this.requiredDistMarkers(preset, committee), ...this.requiredBinMarkers()].filter((path) => !existsSync(path)) if (missing.length > 0) { console.log(` ℹ️ --skip-if-built: missing ${missing.length} marker artifact(s), e.g. ${missing[0]}`) } @@ -492,7 +492,7 @@ class NoirCircuitBuilder { ) } console.log(` 💧 Hydrating circuits/bin from dist/circuits/${preset}/${committee} (no nargo compile)...`) - this.hydrateBinFromDist(preset, sourceHash) + this.hydrateBinFromDist(preset, committee, sourceHash) console.log(`\n✅ Hydrated circuits/bin for preset: ${preset}/${committee}`) return result } @@ -510,7 +510,7 @@ class NoirCircuitBuilder { ` 💧 dist/circuits/${preset}/${committee} is current; hydrating circuits/bin from dist ` + `(fast — avoids a full ~50m secure recompile when switching presets).`, ) - this.hydrateBinFromDist(preset, sourceHash) + this.hydrateBinFromDist(preset, committee, sourceHash) console.log(`\n✅ Hydrated circuits/bin for preset: ${preset}/${committee}`) return result } From 155053776f0152910cb0dd5938593773b767b073 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 4 Jun 2026 17:50:57 +0200 Subject: [PATCH 03/16] fmt --- crates/aggregator/src/ext.rs | 24 +++++++++++++--- crates/multithread/src/multithread.rs | 28 +++++++++++++++---- .../src/actors/proof_verification.rs | 3 +- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/crates/aggregator/src/ext.rs b/crates/aggregator/src/ext.rs index 47375bbc8..edbc01cdf 100644 --- a/crates/aggregator/src/ext.rs +++ b/crates/aggregator/src/ext.rs @@ -84,7 +84,10 @@ impl E3Extension for PublicKeyAggregatorExtension { seed, ))); - let committee_size = match CiphernodesCommitteeSize::from_threshold(threshold_m, threshold_n) { + let committee_size = match CiphernodesCommitteeSize::from_threshold( + threshold_m, + threshold_n, + ) { Ok(c) => c, Err(e) => { self.bus.err( @@ -138,8 +141,15 @@ impl E3Extension for PublicKeyAggregatorExtension { return Ok(()); }; let committee_size = - CiphernodesCommitteeSize::from_threshold(meta.threshold_m, meta.threshold_n) - .map_err(|e| anyhow!("Unknown committee size (threshold_m={}, threshold_n={}): {e}", meta.threshold_m, meta.threshold_n))?; + CiphernodesCommitteeSize::from_threshold(meta.threshold_m, meta.threshold_n).map_err( + |e| { + anyhow!( + "Unknown committee size (threshold_m={}, threshold_n={}): {e}", + meta.threshold_m, + meta.threshold_n + ) + }, + )?; let value = create_publickey_aggregator( fhe.clone(), self.bus.clone(), @@ -390,7 +400,13 @@ impl E3Extension for ThresholdPlaintextAggregatorExtension { meta.threshold_m, meta.threshold_n, ) - .map_err(|e| anyhow!("Unknown committee size (threshold_m={}, threshold_n={}): {e}", meta.threshold_m, meta.threshold_n))?, + .map_err(|e| { + anyhow!( + "Unknown committee size (threshold_m={}, threshold_n={}): {e}", + meta.threshold_m, + meta.threshold_n + ) + })?, proof_aggregation_enabled: meta.proof_aggregation_enabled, committee_addresses, honest_committee_addresses, diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index c4d4cd272..2b7913e07 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -366,9 +366,19 @@ fn handle_pk_aggregation_proof( // `verify_honk_proof_non_zk`. The EVM-facing proof for on-chain is `CircuitName::DkgAggregator`. let circuit = PkAggregationCircuit; let bb_work_id = zk_bb_work_id(&request); - let committee = CiphernodesCommitteeSize::from_n_h(req.committee_n, req.committee_h) - .map_err(|e| make_zk_error(&request, format!("unknown committee (n={}, h={}): {e}", req.committee_n, req.committee_h)))?; - let artifacts_dir = req.params_preset.artifacts_dir_for_committee(committee.as_str()); + let committee = + CiphernodesCommitteeSize::from_n_h(req.committee_n, req.committee_h).map_err(|e| { + make_zk_error( + &request, + format!( + "unknown committee (n={}, h={}): {e}", + req.committee_n, req.committee_h + ), + ) + })?; + let artifacts_dir = req + .params_preset + .artifacts_dir_for_committee(committee.as_str()); let proof = circuit .prove_with_variant( prover, @@ -892,7 +902,9 @@ fn handle_share_computation_proof( let bb_work = zk_bb_work_id(&request); let inner_job_id = format!("{bb_work}_c2_inner"); - let artifacts_dir = req.params_preset.artifacts_dir_for_committee(req.committee_size.as_str()); + let artifacts_dir = req + .params_preset + .artifacts_dir_for_committee(req.committee_size.as_str()); // 7. Inner C2 proof (sk_share_computation or e_sm_share_computation) let circuit = ShareComputationCircuit; @@ -976,7 +988,9 @@ fn handle_pk_generation_proof( // 5. Generate proof via Provable trait let circuit = PkGenerationCircuit; let bb_work = zk_bb_work_id(&request); - let artifacts_dir = req.params_preset.artifacts_dir_for_committee(req.committee_size.as_str()); + let artifacts_dir = req + .params_preset + .artifacts_dir_for_committee(req.committee_size.as_str()); let proof = circuit .prove( @@ -1114,7 +1128,9 @@ fn handle_share_encryption_proof( // 6. Generate proof (preset = threshold preset; Inputs::compute derives DKG internally) let circuit = ShareEncryptionCircuit; let bb_work = zk_bb_work_id(&request); - let artifacts_dir = req.params_preset.artifacts_dir_for_committee(req.committee_size.as_str()); + let artifacts_dir = req + .params_preset + .artifacts_dir_for_committee(req.committee_size.as_str()); let proof = circuit .prove( prover, diff --git a/crates/zk-prover/src/actors/proof_verification.rs b/crates/zk-prover/src/actors/proof_verification.rs index 701a8dcc7..51613ad37 100644 --- a/crates/zk-prover/src/actors/proof_verification.rs +++ b/crates/zk-prover/src/actors/proof_verification.rs @@ -168,7 +168,8 @@ impl Handler for ProofVerificationActor { EnclaveEventData::CiphernodeSelected(data) => { match CiphernodesCommitteeSize::from_threshold(data.threshold_m, data.threshold_n) { Ok(committee) => { - self.presets.insert(data.e3_id.clone(), (data.params_preset, committee)); + self.presets + .insert(data.e3_id.clone(), (data.params_preset, committee)); } Err(e) => { error!( From 1e80066f8cfc2c5b463e98df47f80a7b1b313c25 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 4 Jun 2026 18:38:52 +0200 Subject: [PATCH 04/16] fix tests --- crates/tests/tests/integration.rs | 72 ++++++++++++++--------- crates/zk-prover/tests/local_e2e_tests.rs | 29 +++++---- 2 files changed, 63 insertions(+), 38 deletions(-) diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index 98a84885d..4c9eb0600 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -170,21 +170,25 @@ fn repo_root() -> PathBuf { .join("..") } -/// Whether `setup_test_zk_backend` copies from `dist/circuits//` (same marker as below). -fn uses_dist_preset_artifacts(preset_subdir: &str) -> bool { +/// Whether `setup_test_zk_backend` copies from `dist/circuits///`. +/// Under the new per-committee layout artifacts live under `{preset}/{committee}/recursive/...`. +fn uses_dist_preset_artifacts(preset_subdir: &str, committee_str: &str) -> bool { repo_root() .join("dist/circuits") .join(preset_subdir) + .join(committee_str) .join("recursive/dkg/pk/pk.json") .exists() } -/// Preset stamp for committee/metadata — aligned with `setup_test_zk_backend` artifact source: -/// dist tree when present, otherwise `circuits/bin/.active-preset.json`. -fn resolve_preset_stamp_path(preset_subdir: &str) -> PathBuf { - let dist_preset = repo_root().join("dist/circuits").join(preset_subdir); - if uses_dist_preset_artifacts(preset_subdir) { - dist_preset.join(".build-stamp.json") +/// Preset stamp path — under the new layout each `{preset}/{committee}` dir has its own stamp. +fn resolve_preset_stamp_path(preset_subdir: &str, committee_str: &str) -> PathBuf { + if uses_dist_preset_artifacts(preset_subdir, committee_str) { + repo_root() + .join("dist/circuits") + .join(preset_subdir) + .join(committee_str) + .join(".build-stamp.json") } else { repo_root() .join("circuits") @@ -193,16 +197,22 @@ fn resolve_preset_stamp_path(preset_subdir: &str) -> PathBuf { } } -/// Reads the active committee from the preset stamp that matches the circuit artifacts in use. -/// Uses `resolve_preset_stamp_path` so committee metadata stays aligned with -/// `setup_test_zk_backend` (dist vs `circuits/bin`). +/// Reads the active committee from `circuits/bin/.active-preset.json`, which is written by every +/// `pnpm build:circuits` invocation and is the canonical source for the new per-committee layout. /// /// Falls back to `Micro` (and warns) when the stamp is missing or pre-dates the `committee` /// field — same default as the build script, so a freshly cloned repo's micro circuits work /// out of the box. -fn active_committee(preset_subdir: &str) -> e3_zk_helpers::CiphernodesCommitteeSize { +fn active_committee(_preset_subdir: &str) -> e3_zk_helpers::CiphernodesCommitteeSize { use std::str::FromStr; - let stamp_path = resolve_preset_stamp_path(preset_subdir); + // `circuits/bin/.active-preset.json` is written by every build and hydrate. It is the + // authoritative source under the new per-committee layout because the per-committee dist + // stamp is nested under `dist/circuits/{preset}/{committee}/`, which requires already + // knowing the committee to locate. + let stamp_path = repo_root() + .join("circuits") + .join("bin") + .join(".active-preset.json"); let fallback = e3_zk_helpers::CiphernodesCommitteeSize::Micro; let Ok(raw) = std::fs::read_to_string(&stamp_path) else { @@ -342,7 +352,14 @@ async fn setup_test_zk_backend( let circuits_dir = noir_dir.join("circuits"); let work_dir = noir_dir.join("work").join("test_node"); let repo_root = repo_root(); - let dist_preset = repo_root.join("dist/circuits").join(preset_subdir); + // Derive committee before constructing any paths — it determines the subdirectory under + // `dist/circuits/{preset}/{committee}/` in the new per-committee layout. + let committee = active_committee(preset_subdir); + let committee_str = committee.as_str(); + let dist_preset = repo_root + .join("dist/circuits") + .join(preset_subdir) + .join(committee_str); if let Some(bb) = find_bb().await { tokio::fs::create_dir_all(bb_binary.parent().unwrap()) @@ -356,26 +373,25 @@ async fn setup_test_zk_backend( #[cfg(not(unix))] compile_error!("Integration tests require unix symlink support"); - let preset_out = circuits_dir.join(preset_subdir); + let preset_out = circuits_dir.join(preset_subdir).join(committee_str); let circuits_bin_marker = repo_root.join("circuits/bin/dkg/target/pk.json"); - // `circuits/bin` is preset-agnostic on disk — only `dist/circuits//.build-stamp.json` - // (written by `pnpm build:circuits --preset `) records which preset the - // most recent local build targeted. Without it we cannot tell whether `circuits/bin` - // matches `preset_subdir`, and copying the wrong preset's artifacts would silently - // produce invalid proofs. - let preset_build_stamp = dist_preset.join(".build-stamp.json"); - - if uses_dist_preset_artifacts(preset_subdir) { + // `circuits/bin` is preset-agnostic on disk — only `.active-preset.json` records which + // preset+committee the most recent local build targeted. Without it we cannot tell + // whether `circuits/bin` matches `preset_subdir`, and copying wrong artifacts would + // silently produce invalid proofs. + let preset_build_stamp = resolve_preset_stamp_path(preset_subdir, committee_str); + + if uses_dist_preset_artifacts(preset_subdir, committee_str) { copy_dir_recursive(&dist_preset, &preset_out).await?; } else if !circuits_bin_marker.exists() || !preset_build_stamp.exists() { // Either no local build exists, or the local build cannot be proven to match // the requested preset; download the pinned release tarball instead. println!( - "No verifiable local circuit fixtures for preset `{}` \ - (need either dist/circuits/{}/recursive/dkg/pk/pk.json \ - or circuits/bin + dist/circuits/{}/.build-stamp.json); \ + "No verifiable local circuit fixtures for preset `{}/{}` \ + (need either dist/circuits/{}/{}/recursive/dkg/pk/pk.json \ + or circuits/bin + circuits/bin/.active-preset.json); \ downloading release circuits via ensure_installed()...", - preset_subdir, preset_subdir, preset_subdir + preset_subdir, committee_str, preset_subdir, committee_str ); let backend = ZkBackend::new(BBPath::Default(bb_binary), circuits_dir, work_dir); backend @@ -480,7 +496,7 @@ async fn setup_test_zk_backend( } // ── recursive/ variant (inner/base proofs, uses .vk_noir) ────────── - let preset_dir = circuits_dir.join(preset_subdir); + let preset_dir = circuits_dir.join(preset_subdir).join(committee_str); let rv = preset_dir.join("recursive"); diff --git a/crates/zk-prover/tests/local_e2e_tests.rs b/crates/zk-prover/tests/local_e2e_tests.rs index d260f2b4e..424ca593c 100644 --- a/crates/zk-prover/tests/local_e2e_tests.rs +++ b/crates/zk-prover/tests/local_e2e_tests.rs @@ -478,7 +478,7 @@ macro_rules! e2e_proof_tests { return; }; - let artifacts_dir = preset.artifacts_dir(); + let artifacts_dir = preset.artifacts_dir_for_committee(CiphernodesCommitteeSize::Micro.as_str()); let proof = circuit .prove_with_variant(&prover, &preset, &sample, e3_id, $variant, &artifacts_dir) .expect("proof generation should succeed"); @@ -523,7 +523,8 @@ async fn test_pk_generation_commitment_consistency() { return; }; - let artifacts_dir = preset.artifacts_dir(); + let artifacts_dir = + preset.artifacts_dir_for_committee(CiphernodesCommitteeSize::Micro.as_str()); let proof = circuit .prove(&prover, &preset, &sample, e3_id, &artifacts_dir) .expect("proof generation should succeed"); @@ -571,7 +572,8 @@ async fn test_pk_bfv_commitment_consistency() { return; }; - let artifacts_dir = preset.artifacts_dir(); + let artifacts_dir = + preset.artifacts_dir_for_committee(CiphernodesCommitteeSize::Micro.as_str()); let proof = circuit .prove(&prover, &preset, &sample, e3_id, &artifacts_dir) .expect("proof generation should succeed"); @@ -610,7 +612,8 @@ async fn test_share_computation_sk_commitment_consistency() { return; }; - let artifacts_dir = preset.artifacts_dir(); + let artifacts_dir = + preset.artifacts_dir_for_committee(CiphernodesCommitteeSize::Micro.as_str()); let proof = circuit .prove(&prover, &preset, &sample, e3_id, &artifacts_dir) .expect("inner sk_share_computation proof should succeed"); @@ -643,7 +646,8 @@ async fn test_share_computation_e_sm_commitment_consistency() { return; }; - let artifacts_dir = preset.artifacts_dir(); + let artifacts_dir = + preset.artifacts_dir_for_committee(CiphernodesCommitteeSize::Micro.as_str()); let proof = circuit .prove(&prover, &preset, &sample, e3_id, &artifacts_dir) .expect("inner e_sm_share_computation proof should succeed"); @@ -676,7 +680,8 @@ async fn test_pk_aggregation_commitment_consistency() { return; }; - let artifacts_dir = preset.artifacts_dir(); + let artifacts_dir = + preset.artifacts_dir_for_committee(CiphernodesCommitteeSize::Micro.as_str()); let proof = circuit .prove_with_variant( &prover, @@ -727,7 +732,8 @@ async fn test_threshold_share_decryption_commitment_consistency() { return; }; - let artifacts_dir = preset.artifacts_dir(); + let artifacts_dir = + preset.artifacts_dir_for_committee(CiphernodesCommitteeSize::Micro.as_str()); let proof = circuit .prove_with_variant( &prover, @@ -770,7 +776,8 @@ async fn test_c4_sk_commitment_is_c6_expected_sk_input_e2e() { let e3_id_c4 = "c4-e2e"; let e3_id_c6 = "c6-e2e"; - let artifacts_dir = preset.artifacts_dir(); + let artifacts_dir = + preset.artifacts_dir_for_committee(CiphernodesCommitteeSize::Micro.as_str()); let c4_proof = DkgShareDecryptionCircuit .prove_with_variant( @@ -874,7 +881,8 @@ async fn test_c4_c6_sk_commitment_aligned_transcript_e2e() { let e3_id_c4 = "c4-align"; let e3_id_c6 = "c6-align"; - let artifacts_dir = preset.artifacts_dir(); + let artifacts_dir = + preset.artifacts_dir_for_committee(CiphernodesCommitteeSize::Micro.as_str()); let c4_proof = DkgShareDecryptionCircuit .prove_with_variant( @@ -945,7 +953,8 @@ async fn test_decrypted_shares_aggregation_commitment_consistency() { return; }; - let artifacts_dir = preset.artifacts_dir(); + let artifacts_dir = + preset.artifacts_dir_for_committee(CiphernodesCommitteeSize::Micro.as_str()); let proof = circuit .prove_with_variant( &prover, From 5b66ae732e6c8351f58179b73abb25cafa372cec Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 4 Jun 2026 18:59:31 +0200 Subject: [PATCH 05/16] update path for generate-verifiers script --- scripts/generate-verifiers.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/scripts/generate-verifiers.ts b/scripts/generate-verifiers.ts index d063148e0..2effc4958 100644 --- a/scripts/generate-verifiers.ts +++ b/scripts/generate-verifiers.ts @@ -556,16 +556,17 @@ class VerifierGenerator { } /** - * Refuse to run unless `dist/circuits//.build-stamp.json` exists for the target preset. + * Refuse to run unless `dist/circuits///.build-stamp.json` exists. */ private assertPresetBuilt(preset: string): void { - const stampPath = join(this.rootDir, 'dist', 'circuits', preset, '.build-stamp.json') + const committee = this.targetCommittee() + const stampPath = join(this.rootDir, 'dist', 'circuits', preset, committee, '.build-stamp.json') if (!existsSync(stampPath)) { throw new Error( `Preset '${preset}' is not built (missing ${stampPath}).\n` + `\n` + ` To fix, run from the repo root:\n` + - ` pnpm build:circuits --preset ${preset}\n` + + ` pnpm build:circuits --preset ${preset} --committee ${committee}\n` + ` then retry.`, ) } @@ -579,11 +580,11 @@ class VerifierGenerator { throw new Error( `Build stamp at ${stampPath} reports preset '${stamp.preset ?? '(missing)'}', expected '${preset}'.\n` + ` Run:\n` + - ` pnpm build:circuits --preset ${preset}\n` + + ` pnpm build:circuits --preset ${preset} --committee ${committee}\n` + ` then retry.`, ) } - console.log(` ✓ Preset '${preset}' build stamp present in dist/circuits.\n`) + console.log(` ✓ Preset '${preset}/${committee}' build stamp present in dist/circuits.\n`) } /** From 7970f1a342907655e84a4a4946219e1fcd53b4a1 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 4 Jun 2026 22:37:09 +0200 Subject: [PATCH 06/16] fix missing committee size around --- Cargo.lock | 1 + .../benchmark_run_meta.json | 3 + .../crisp_verify_gas.json | 96 ++++++------- .../integration_summary.json | 128 +++++++++--------- .../results_insecure_agg_micro/report.md | 126 ++++++++--------- .../scripts/check_circuit_preset_artifacts.sh | 36 +++-- .../scripts/extract_crisp_verify_gas.sh | 2 +- circuits/benchmarks/scripts/run_benchmarks.sh | 2 +- .../src/actors/publickey_aggregator.rs | 1 + .../actors/threshold_plaintext_aggregator.rs | 2 + .../src/enclave_event/compute_request/zk.rs | 20 ++- .../enclave_event/encryption_key_pending.rs | 2 + .../src/enclave_event/share_verification.rs | 3 + .../keyshare/src/actors/threshold_keyshare.rs | 22 +++ .../src/domain/decryption_key_calculation.rs | 2 + crates/multithread/src/multithread.rs | 24 +++- crates/slashing/Cargo.toml | 1 + .../slashing/src/domain/accusation_voting.rs | 11 ++ crates/zk-prover/src/actors/proof_request.rs | 2 + .../src/actors/share_verification.rs | 8 ++ crates/zk-prover/src/domain/proof_request.rs | 1 + .../src/domain/share_verification.rs | 5 + 22 files changed, 305 insertions(+), 193 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4d827d245..409b7673c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3721,6 +3721,7 @@ dependencies = [ "e3-fhe-params", "e3-request", "e3-utils", + "e3-zk-helpers", "hex", "serde", "sha2", diff --git a/circuits/benchmarks/results_insecure_agg_micro/benchmark_run_meta.json b/circuits/benchmarks/results_insecure_agg_micro/benchmark_run_meta.json index fb21f2bcb..cbe70d92c 100644 --- a/circuits/benchmarks/results_insecure_agg_micro/benchmark_run_meta.json +++ b/circuits/benchmarks/results_insecure_agg_micro/benchmark_run_meta.json @@ -1,11 +1,14 @@ { "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 index 5b0a531f6..97d1cffa7 100644 --- a/circuits/benchmarks/results_insecure_agg_micro/crisp_verify_gas.json +++ b/circuits/benchmarks/results_insecure_agg_micro/crisp_verify_gas.json @@ -1,8 +1,8 @@ { "verify_gas": { - "dkg": 3125379, - "user": 2972989, - "dec": 3640997 + "dkg": 3125331, + "user": 2973085, + "dec": 3640972 }, "source": "folded_proof_export_plus_crisp_verify_test", "artifact_sizes_bytes": { @@ -17,14 +17,14 @@ }, "calldata_gas": { "dkg": { - "proof": 170064, - "public_inputs": 6168, - "total": 176232 + "proof": 170040, + "public_inputs": 6144, + "total": 176184 }, "dec": { "proof": 169968, - "public_inputs": 17304, - "total": 187272 + "public_inputs": 17280, + "total": 187248 } }, "integration_summary": { @@ -47,55 +47,55 @@ "dkg_fold_attestation_verifier": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 13, "cores_available": 14 }, "operation_timings": [ - { "name": "CalculateDecryptionKey", "avg_seconds": 0.003899597, "runs": 3, "total_seconds": 0.011698792 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 0.020771278, "runs": 3, "total_seconds": 0.062313835 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 0.019628875, "runs": 1, "total_seconds": 0.019628875 }, - { "name": "GenEsiSss", "avg_seconds": 0.007804847, "runs": 3, "total_seconds": 0.023414541 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 0.009254708, "runs": 3, "total_seconds": 0.027764126 }, - { "name": "NodeDkgFold/c2ab_fold", "avg_seconds": 8.419060625, "runs": 3, "total_seconds": 25.257181876 }, - { "name": "NodeDkgFold/c3a_fold", "avg_seconds": 36.69000318, "runs": 3, "total_seconds": 110.070009541 }, - { "name": "NodeDkgFold/c3ab_fold", "avg_seconds": 7.678847138, "runs": 3, "total_seconds": 23.036541416 }, - { "name": "NodeDkgFold/c3b_fold", "avg_seconds": 35.819565458, "runs": 3, "total_seconds": 107.458696375 }, - { "name": "NodeDkgFold/c4ab_fold", "avg_seconds": 8.047481542, "runs": 3, "total_seconds": 24.142444626 }, - { "name": "NodeDkgFold/node_fold", "avg_seconds": 19.279656625, "runs": 3, "total_seconds": 57.838969875 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 1.60953675, "runs": 1, "total_seconds": 1.60953675 }, - { "name": "ZkDecryptionAggregation", "avg_seconds": 49.763333667, "runs": 1, "total_seconds": 49.763333667 }, - { "name": "ZkDkgAggregation", "avg_seconds": 20.434941375, "runs": 1, "total_seconds": 20.434941375 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 1.318937159, "runs": 6, "total_seconds": 7.913622958 }, - { "name": "ZkNodeDkgFold", "avg_seconds": 115.937274027, "runs": 3, "total_seconds": 347.811822083 }, - { "name": "ZkPkAggregation", "avg_seconds": 0.863779417, "runs": 1, "total_seconds": 0.863779417 }, - { "name": "ZkPkBfv", "avg_seconds": 0.234095361, "runs": 3, "total_seconds": 0.702286084 }, - { "name": "ZkPkGeneration", "avg_seconds": 2.564455153, "runs": 3, "total_seconds": 7.693365459 }, - { "name": "ZkShareComputation", "avg_seconds": 2.554805451, "runs": 6, "total_seconds": 15.328832709 }, - { "name": "ZkShareEncryption", "avg_seconds": 4.018045519, "runs": 24, "total_seconds": 96.433092457 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 3.493319013, "runs": 3, "total_seconds": 10.479957041 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.103515208, "runs": 3, "total_seconds": 0.310545626 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.269947275, "runs": 5, "total_seconds": 1.349736376 } + { "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": 908.64351588, + "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.96616925, "metric": "wall_clock" }, - { "label": "Committee Setup Completed", "seconds": 20.093379417, "metric": "wall_clock" }, - { "label": "Committee Finalization Complete", "seconds": 0.005040209, "metric": "wall_clock" }, - { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 136.237549, "metric": "wall_clock" }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 148.9261515, "metric": "wall_clock" }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 149.434793375, "metric": "wall_clock" }, - { "label": "Application CT Gen", "seconds": 0.009646083, "metric": "wall_clock" }, - { "label": "Running FHE Application", "seconds": 0.000103333, "metric": "wall_clock" }, - { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 51.386342, "metric": "wall_clock" }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 55.335645666, "metric": "wall_clock" }, - { "label": "Entire Test", "seconds": 227.841801083, "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": "0x000000000000000000000000000000000000000000000001fd49ea2995c1805a000000000000000000000000000000000000000000000009e130f8d07cbd35d1000000000000000000000000000000000000000000000002e5ab83f7a0ec41a9000000000000000000000000000000000000000000000000000180f1d364bc1200000000000000000000000000000000000000000000000b30855f54e294c86600000000000000000000000000000000000000000000000e219fb2ae146a494e000000000000000000000000000000000000000000000004d22012f74a7396a9000000000000000000000000000000000000000000000000000215c2c000275000000000000000000000000000000000000000000000000eac34294d15070d38000000000000000000000000000000000000000000000004e87191632119f7f70000000000000000000000000000000000000000000000015038dbe31f6ab0390000000000000000000000000000000000000000000000000000cf331e3e636f00000000000000000000000000000000000000000000000dd9f98878c5b6f7d6000000000000000000000000000000000000000000000007ee2134f16e8d33160000000000000000000000000000000000000000000000077df2fea764d8310f0000000000000000000000000000000000000000000000000001b58f64c88996077edaeb00d787972da96dd0edbb7c2b101fde53e5028931cdfa8158fa8937632cf53d683e41360f6e529b02d216642bc54fc3b5493f89225f4cdc35ec2dd75e008be81ba59678bc2845a0a790af2b8124f8730d49d964132d0a028970ab563a06cca51c91027c577ff402f93bd200243f9d344e8b2a354f1aaa5da3b43a24e11b6fc61db08de25bc5fd4bb73d7df7383cf857691020be5880f05c25bc8d19710d7eb2427e444196714589588ebf61cc54c552518e950b56b9a6fe08b9bbabd52b0db8963a40cc8dcf97ed101f513d60c9f7ce4bdd3fa84c9c7aa8e5706ef3af1cbdb85e131c9695dde95e1d161deca5430a0adec644f6fdf7c962cc0686cebc0e0f4d1d4c431a4ac5c17aa11206f288f79299605006038c506c284084f626822891b514eac670d39ab672fa43f20c0b65b0e10624a2e638ba001a4291f1740b24777273c445fe315e2193fb45060a7010a34afdaa48d5b4e215b66fbb18d3ba21045761d06795541b65751d20790ac3e1d74cfba39a6027cb9ac310ee2aea83024ab126d473d57e2cdcb9b5af0beb957c65d4308f1a4f7294a1b1ba16e047e50e92d2d26422ae9ca51949ebba20c96db2e5c1b6c8ba2898abf038e018d0e5f90d575b2f9b331683b451b53e7effee6c815296c427b181ae57448fa667f3e8d802eaa8611c3b5b5c25e74c64bc7f45e3112c669ef9f4e0d756670e89c34de6db2cdce2ee36eab74073def9ebb3fde8b4aeb2ee658c9acba864d80fb569c75a77022cb90ab0b77907d8103f901c21ff013851f12f8d4b6d24323694b2cdde560e0ad79dfa9ba2a692ea68660f563b9ca963f67786eacb35794db407e839a365a007463b9e1d2f0df8602e1bdf84d818a0395a5242aebdce60bb8bd4258fb126b52b9bb460323411c99fd5c4b42072c524a975df523f28156065e824e35dcb1d9c1f9a105ad7dc6269609922d8cae5d7c6ad37425e957cb25135476748a8beb332262959fdecd94ecff5648621ae55cb4cfeead8f1042dd04593040a8c76108e9a2c17f170d854dfbb9c9493bd58da3029edad3a4a5c2199ba8948515cb54dd87e14dc69acdfa3e5e67c481bc78cedec6e1af6ae498e8884c5482853dc3161a6dc1e301746d84e8d4f07dbb2de4b7789683203870a9ca01147cebc327a1f2a28e4273678b16eec7b05d7f220111d7b89455daea44633e1d1b73a1a5d698e87248a04a7d9705a4b7af209a598156203cb008220c25975ae7bf3c6e8ba9d3f2c678a09d66e4b7bfca0211c9e3a4a3114eadd1105982c019fa2c8520bff6a6a2fcb9e2cafb862a18a64b540895760e4cb1e8e82771188cb43ab3f0f2a81aa98f416c90a9aac4fba1a822c08cafb8ab98475aaace9e064745616e2eb1471cca54a219b0b6a7e7e884e9e938f7dbef5134e11e9a00492bada044d8eac8e1151a74775310af775f458352ce39e593aadfa030f8343b9c9cdb3c0a51eb8996724a40598f02d3b2758c53427318b585617fc8810f08e1aea10a43ac4d7c25fdbec155e32402d2bdec92c262abc30f56e45f1184e3b5b9190a16efbd59d4aeba4af3a9ec98e0c229a521676816c507fd4f92cb86b6aea59e8dd336b9e7fc1d7c1d38e4349b010332e0457598a26c2c4a3e0d02c20af948d75bbbd56465cc016af714c6ed89205b25caab1ef5e5d4753587b2c423911427ee11eafaa007459e515e54a2b068f128e5f87f9b760a72c83d37430c6ddb70270dcc64319d6416c3b30d37f80f4f209a81ea9f1d236a7b9bd55dc6ced0ec4e526908687831f3d95acd61aff8ff2372b0e2d95a1361900a977f9253a6c9d45f681e2f4011ff13db0c7219085bc33b223460a7bc760f25bdc85f1f16eadf15a59acd2cece7ca3eb789e926f49f7c786161f3e86f116612b5a5a429c31a1fa51e8fd9343031442f5c94fa2987182845a2c1ccb716fd77606020045903b109f92cf24f209754bb0f28a364db55c9b43aa2e51b7498f6e68e2abaf0b76b36d851b29a09217cdaf13c7ee7d0cabea08eccf11fa5693c9691c14321ed941fd08d7204931bc6e514b48ec12e985c8b557ee3708f89ec5b313a1f429eceedc627cbc8ac7b7b50086f6cd9afc75997bb3507c120c8bb726570f72529caf1535ee80af24f911081531cf149a92ed499a134b0eba01b8640b24f5cc592f7e79add4472256c4a49bedc5801a22b7ea65f8361e77ab2156b74036d7c9bdeaa69b37fdc5ec0b8dab05587f48e430f701e3163a917047170bbbccf4b84a16ea42894d2e25d01fbde3702619c8c38d7a06c6846f9ba67215ea2a312148e210efc156d4ebc28d6758efbce0d8d41839febeb131f0945fc01c84af60616bde2a2a4fa6d1715314c1ac81490793c91017a88de75fac5e20d3080548014b07a60120d857b6bf9f57967ba843d74a34204504e29671e4195b3b24281c43c58ee0b93d2af3f13e6314ecc938001ab4d296749dddb73ee17273382e2630ac48d1b66eeb3be980576c76ac6e7add3e4878cbee0724917f0ba962ff243a2e4a055851abd682ecf0908821cf3c1eeb788029b1335670075439c398190673bcc78e40c71398b948d57471fc669d174d2740c405199e6584f871b9e3012a0acab2e96a2afe838043a16becd5c3e687cc6bd2efcae982386f32962b7a88245310639393ccaa5331e97b7db1919bc2d50e86f065cae12b76b07d3348b6180fefa11f9b377c565b5b40383a76bd59af2437e9c72a05e49837b5ba60d7b1712fa7b9e47e883d1d4551c92330f85e8e8a3597879115735095c251d0bd3786bd22ee5a0c362112a3cd554387c3973d608c2aa579b5136e0de6e93d354f4c5a1408a644400d96627bd7b10ef88796326dcbfeeec6916e7462a5e90fd6e417bed428b6e56c827fee917c101bd208fba6a6a96b26c8609660036db1aab7fc68b01a0f050acb41424de2746c4680e9f50d6b1902287390809d57c071bda340980ade048787cf4d2dc03c343eca0db49299fb8b7e69303cbf16384971194fb4c68e3c2eba457fcfbae5f78a3ba7bd14147fc79cf5337190691398b6bd459251c131442a5aa476d922a21867112a84ae48ecd5b6fa90280f477bde61f721c68ee2d2f513ee827cedbf43918a4411f6845084504144059b545de93677a73f32296037de049c8166781f6e1424f72a05999f984b97fadc1ba8d10a7ee32be56c80adbf751ca15b8240f73f458e5b7048fa8db1c7a4abcd252e1f5862aaf75d9dc7ee243a1a0a8fc6f86b636a8616ddc653172189accd3bcdbf647c30f91d364c81c206962ff31db29a42d1aa4e88acc875d3eff57728bb3450ef218f7ce61a98e885b5772c08b80788b8e0221b56ffdb2e044a99df3ce955d77c486dc8ad7b19cc3f20eb2fdf6044932d8a24a9b47713da7d64bafdd7e72db9e8e9d1ce17c51ac428fc991f9b8d2ec0c1e7d1cadc63db8032a4246c2d93b41402b11ef34f0b3052e6b6101b1092f7a6d83b1aae323a8d093b0c50965514a1ccdd249ce909260d2e81ba4921c116c4dd85c556af575139c86bdc4b43305b9290fd471de29fc332e88e094c02253af4b0ddcc70e3c853ff8370f241c6a5af7a9124e46fb711d4710c8e0d860c83253e57c909cef046bded3d5ebaff650057ca72ed08a066a9dd3c5206d7f4293e361d99a9b24098ec04a441e46c7b2b8064dae8c70de8bf385ecca60724131d03a978a8f846bf6ed6b464b43514a9585a961de9dd2df10ef0ead69581821a2426aad64c076e2ef8c465ba244a8f3ab53eb4074c4687c2068724dc4dfb7fb40da880fa666b2ff11fee4198e828d0a81a16b67ab497f5fc6f9763c45cc5a3721d3747876e7d1f7597a136b34a7558eb5a2c775c4ea3a7d587ffeb9e363d52681203eb04847206683a5894d351f7ac067b90c6e2857fdcbbb0de3ae1b88c7e7025bb867319c0650dbb7199954bbb3dde0f1848f61f262b85139607a8194c25820b86479d8130ce090230b101052095e37928cd7b35cec64a52bf2f2d9309fa620a96840836aaa24508174d058c59d3243dfbb8dbfa910180139920b5bba0ba612a3137d9139def0d4c2dc96ead633a57f0deb2f385ef106a6b6e1f8552dcb04a1c7e438c02df964ad4cdf994b96bb5145852e4cff61b67e9d2207bd1f98742861128971620fc09258924db77d5849d4b01c133d5eb0ba35bb715676a8f0760b1209dbdeee1b98148722437e311c449871e52f424bf8af6d022cc9a8bf86c381a0ef751ddb94d31062c176215cc10a722cb13a3edc840154b075046e3d73a49511fa65de3f652272d12117f4922c89ebf154622a90cc22d48d852238d0d8f722a120f665f4406f4560fed8ccf003cc551e15a4619cf80218263ba803fd1f4f751188ce7efb3271294fd4e09cf38d5f5e94bcd90c48b69ba9033a907f50b9058f302d8bd87bf079867a8a7e546035a866bccd46a6b7ae786cf3bcb106b0ca4eea90e7a05252c11dbc7b11bc30a304735bde3abe0bebe3964700f93f6464297b8b708d1241352fe66286724eeaa7fe21b2a1b58fd9f125e39e8e30d6c6281e7177710661aee44cde894457f7694b7e7a097f95bc4b343132fb9bb618a26e7952b6d2db56ffb8b361ffe0789871aac4141603a1877b9da3b9cf0719a17683da02a8717c2b38f5d89e3719efa94ac694f52bc00b4a11822e900f54dfac530acaa4b2c28a91d4295d27ded396d129a08cfd2c717fa74161e1b9f7926288a32abce912e079096a999aad3cd00fa09f8caf340e2e49e665d4d008c12015303de4057f9972dd2677eb529ddbf4a050ab90c6c2c2eeaa88d9b13d277cbc7245c65e2ef0c42230b0a5b03a713209c518fb9ed18faf4b266b4f21c9977677bff73fa324431b70f757eda054f648fdf26a5211123ba1d0ea9dfd5d124e377cd0aa11312ffb0c420c23c32f1c3cdd37dd268e2feb48b54b0e6f19c0ee67a2ae7c01472d2623d1e2fbc69c45daa678a391a5504a0ed5a0a5e4cb250f36501072c265af823abb45b1dc5739f9eb5e10a37d356027db2f6faadfadffdb2ce7ac184bc7f997b53deae162a8ae642df97184ae988c8607ba165006f0dc926cde19a62ba777bfee040ad0da536704fe3046961eea59de7f504851c2b42f0948a576d02146d802592bea5296be00ec01d6bacdae3e08981c5d1e75bc6882c1694ddafe5f01450f0a019131667b2a26bf0f497d5e379f9e70b233d24e3d43d7ffa468c5d04a6ed530c07d20548fae6cd942426fbad40b064d09ba495711ff9ecbb7f8fa128841dadb433d9275b8602a28769edfc46311c99d70a33e6244c69d63d5f1f70312907faabe2b424ff92a16a1011d0b94ab5c70e20e3ddfd84ad4ec7a7cb8c7346a164394319ab2c7c716668fc8d1e4a01cacd713815860faa08a649f03d54cae0a85d453496f807d9c9d01d661377cbf497b95050d15852b24c069aefe635bd35329d9baa8f64108ea7e4af7d45bfd70af8aa9abc564b259e813614a906a560ee7a15f46cd3782c4257ad3ee80e97cb60726b65a4f5a2701c277e8edad07984f7c3753f6139b9276d4512a0541ea808942cf25dbf676e996b9f45c576baf6447a12bd93cffb930a398fc4ca770004b59848f3adedc44b1acbd0248ba516c564d1953f177e73eb288458a12f11a83a53ddd929bcff5c28f195b01cf27c1aafb14b1db691ea07882c25272f45280f7222fe7d7bff564dbbe9c1532e79807ca0d8709f610fdb292606b4d4023d10bcae31fa0f6389085e9e291905031f7c7c07f726cee059974210170ff701c030db637dc65b0c4855a7518fd3f6e08745e376cb32e37b932ae0ad0c5e118818c267aa7c8e9603614da1384b8cdf398b5afc98449b5f454f40b16b02625cbec8c5c8f6e191c0b5fbf55c922fcc68258b33a39ddf72c0beb9ac218911081f65e7af22fc123c58adf831317451c202135ac4d35bd4dac00ddd18f5692e7345c12abdc477eb8b415fda1bd3b6ff88009c5b6b6cf9623d6df79213a82c055506730507438bc795dc759a7c4057b3aafcd52e3d58cc90f9d2709add84cc01b3f56a26ced3f69913748de5d3b4c24b50051ade9e9700dfa8d1d0c5f14d5d1ad7644a107beae994ea0ff4532c54b89514c2d06af3d634ac0a53764eca55210b2f46facc611e557559920cf65593b6616db1eb394d0c20b040ee8a4b48537e1198823ed6cd160de66eb43e9022ffa950caf79c4b4e405d325b8e69bbc9454302ec177251fe8cb4c6c57542b2be93f91666a93ae052a355a731f806b16477b82d44c4d0b255663e6b548a9d8f69d6cd9e2d6a14d12768e38847a5bbafd3b94020502aecccde69569270bf06687f6617cf3eb0903c36f0449823d51a3393f9de19dce60cb1aae13a19b50e34b65ffbc680f412012ae0063d362a77c85be265ef2c7443d2dae210dd161a8c217b18620dcad17590cbae456a61d250fdbba547fd29b3c7854a3a36887dd4ec8c4e93e56907c62a4140aabc9da587617f961fca8d24d85a8c59e11cd64c84fea5e13d586d3e88fa439507f58e26d3c317a8bb7d32177abceee45d0b521eba525527ba8d739426e9c7f3e695a68b59414c0265e9752805ea9635e406b084a400034ba3da86ecc6c4b6217ef5fa2dd4f344913e08fe059c5a303964df6df32cd2788e65facb109fefad82dc156fe1c8b06aa84bf0ee033b4f7d9c3773cf9376080223b5c36409f8e94cf3d3f1d8c62e32276b2112a219cf162993c5c1e2e75c8d2167232efd83178a7570c0bf482866f3c0ab3d16dc2cb07d71751a27546ddf0c335b0f31edab3e88e7796d9347733c18786d5a570414c29fced4459d8dc19627c59aee6008f5e140d510490357cd98931a93de019217ea68fe14ac058df3fa9e9cae7727b03d933a66a23e217e97a5be9d14c762ff034f3d0afadfe715e5da693a3140044e0f392e07659b7fdc04c2fdcbded6fbdf2d4a4ceb366ba2ef2abc371969dfc1735ea35bb48f927360d3059877fd3631fc2604d08c98df80b00821a6a7ae5c5244fbd65539e4af2c0e50d6992fd4fa7184076abfd3c49e37a997c8db51c9e0cc01f39c1afcd92f34131292e2700f8a1957198b7ed6cffb3a7c201fd6ca40409bde83950f8de3f35e81d9fedbc3750eba372baf58a23be51087dea664645af910ca2c8e5044971d829aa0832cb8c72c574521740bc1aa918a1109b5bc99e0e1039c6e585e505a9db5531b76a4bd14a8b1442e37c1c4dfa7150fb43733a9b157c7a601526920599572946feabf80137ba1ad2322871b6aac745a9f874b9bb8ef9ee182a3db6d33de228bca5096f018e3e8a9023650fd0a202af87cfcf8c29efca5d3b980149ea855d69489f595554fb2f23c29a9aff0de611a87627cda3c879b542baaa1677827282a960542661baf13045c14836a3abd071e95bb8e0954b7655fba14a5712b67f5c2a8b44c8b2d9928fc6f0e10a87895332d36c58622031ee8d2d6ccf1973c0c77042c42d618a3dd2c9e512473afbf6d4180bac27744f2881457b5c9fb4b3ab84b4cf8416f1a8570899ff228bf7ad3970337fd65fc3e884d8fff552a7824001e73724072ad01b639d82f630229f0c3b29a4844d16a0e66fd9ab50c1a2a4c4f388c36143449e931aae4bcc22bd450a3e3bfaee4a3102afd669ba6f1b9e9e8f3404f8905bfbd2e49f9ed48db03174c95e1e7078a63f676e989df81004ad1c1d818676bc710aec939c791a2170f20a480ef9719f1f4b7e93c951af6c035835d60e312d54c3199a282f5da2ad40bf4385f43eae8609d03334049837d361c4ab902f4bdc2f8060a51888687db0a1b0af92cb490eefd3f631ad0a9002bcc844b686dfd15ee545a2d408d5b88e9f42171fe53d283a88b7188eb944c0cfb22d8e8f8e667944b5c62a1c7f1adb65e520b864b06dbed7fb1a6c446e24a39a476820bd68ff5b1d23584fe190a99a1e64724b1dac25560fdb393debfb31da4470b28448d42a031efa989617232d2a6ee2b1c258020f28d1b98e3c58a6a66fdc9dceef00668e30a409a699babce4913ebfb0dfaad87dfb55c5852970a9e232469b152967bb67d84b6ea2cccb2393a9ad06f06e79ef7e5f2ee98079b5f97b152dadb8e536ffae0af9209b7e97a18a6eb2a322ef63191d9f65612edbc1c6e9d997ed8415faea2a69318114dc55bb2256f7d522d88aab1575bdebbabb5c233d83e80258dadf60a1cadf436b1ef7536a545a50924af2ebeeddd230c21a822f1f1a1462a7f721f84702e55abecdb1d19adc11cbc1a40d54138980ed32bc41efc10e97fd6f73693807938a5dd3d094630acd94db10dd62a2c9d39431ee742dcd012aaf7b9b05606e9763bc56dd4ce73a00055cbbe2b1b95a359abe81c0fba341e408beb4ebf5fcb99d77386ae932f46597e059a652132dca997945d68bc03f4b8e6fdba705a4df8d264f03227a1d2417dc653698506f05f3ed316b551b2308c5cd275e979b84b05b00dbadea8b8c1f07a5dcdb5d4031bf03e7614209d0576cf3767c632dc3da608817b46a002e256ea39efc23c9f0b636c1539a4e8b2e7ead1b0f0922c4226513eae31d34a5c23aaeefa0c1035fd2217a8a1cf0f64f5bc124d8cdb72b7c110eb58e3895f987dc4a9a2f98cd43eca04b9354718910b3db7336377fb91fa15ecda37a5c97f4e0e845fb0b697f883c51ef3cc44eb53eadc94257571d17b8b2d9664ed59069a47d20d7c9f60c2dc2bb72589069de73a880be8c2d3dc8af7b126a1bfcd539ebf499d735976c4f9416dd125cda14c2fd6b9aadc9fa8e8742674406c850640b91434dbed6db5f6f955a45203f22c60fae57f247f5737a7d76d1e8fbb1c500b0a74103cb671b24a11773ad61ffd241db5e210987b5e0f4f684aded901be922f34095dc04db8cf2df743f08a2e344da58c25e72b35ba59db4cf80a8a405f34d801af11c44ea66a7fde80cd780ec70d8c6f21002b9ae5a48c55d4eaab17fc852ededb4fb7612793e41681769323a5ce7f25cc21e052cf512ca46a3e7d88831681aa60bbdea745630e53f0ba2a0b778aafce261c61c2b2735a8831f86673336cde49256abe49da15549eabbc37047e4676f2e6ba738e6b30aa7b42a99b3d446e938ea33223754a9f7fc73f8e892b868e2c6999aa25e43d53fd63bd8f7fe3ef79733fbac819dd9637b0960086a0244a05a8b2b7f32a0b15e666cd1febe8132991cb7b0ff170207b222e4cd62ac826db1dbce473329f0dc729173335506d3caf2b6c2d3a0390c2d7387e92b875850565046bef359a9fcfe33325f12ac75c3c655f4e21de7ff860fa67b6afe0ce390262e1c1c7fa0c9805fba8b8305755f70c1604a914ae41989a70318a63f0535b178ad7aae2288338fd806347b82943448c52c16da16a4c627f69aea4711e1db02ae912e02611df5100590a244bc928798d13fe02c7b549e060e36a271f556a41145aca25895b56b415c9d11281506c5255c43f0a0da9cd112962a4a1ef37f7842cc962938de54f19839eebba286c3d05d87c64b22667e356b168023df18001b70d89289947bc9fcd0132a253beeff9b1098d308094f88e798eb8756304917799135a1cad102a521af6050b358fb030b2ee513e7d19b214a1956050f85fa6f8c5212b8fd410b568adcd7299767779a372332406e5ebeaeb7f91ab37c7a480b51c09bccc0886917c3cc20c864ed96d4be02a4052219f904fc82992cb9c331c569816ef560c14a12c8a4eea11ee769ef46b124dc807b63015ca546098df6e6a16612abc9a2fd6238b510bc4c17a285359ea54583c384ae62ce76853124e43a9154029d7c520e41153b7dd343f0d7abf1c5220bde7bf5acbbf388a8d92c3100dd9271840cbf35b3829e7720df75a541f0f80423cedb23ced4e458c827ac2ff4e5bc204d8f76b69e4af9233c6965fd34486bb6384e35e4fbdfa557e37e859aa9286db27ac09426cac12567646aebc490e23eaecbcee175485dbb0f7473260a683b729096cdb72801ff55dcf60c26aceccad0274b30697220f5794e03320428b31f94c212aec9328b9e7d0e94f04173fa15d5c975b9993be40b68ebc56373f134721521c4a577a9469eb0c82b41ee48bab8feb84921bb8b8817fc2c6922a6656c9bfc7295954bc31ebe7e13058df8c65c765bed2e0f539adfa3fa1155e295b2dd7c9f41610853333dc74f9f586aca7724f1be833fda34ac29248f8e1c2e1f55f35c408005784fe91eaac93552a5583ecfdb07dc455382a4feecad5e0ea3a6037a3bf9f2e5a1168aa423832bc59965a9fb8d3fc3e4d70105170f5e1df8a269a4b960a8302ef2418b6a730095d73fda3d93f0eee8f2f8f4be4e509d6ebd50e5635ba142802125bd0c7afb84d93c47c4b97c17a5fe600d9a0b3dad54aec6cc7b120f4edee087ebac3ad362292ba295da087132bd932e8effce9cfc1ccee32154b81d601d50994b7bd9fd26dfaa7b9dab924162e185db0f748409770ae64782c447f469d5805cc69bc75589155860294f5a034495d3ae3e09ae450822bccf3a102d1c45fd71d395aac77280661e67c53d84b2b54d4f8d5b463b194a6feed38f1410a22bc990301c57a9c377515d508ce17531560f9430e20b6a2b3cc32d83bb6adbfb2de06170c93eb7f359b3e7a43b193d199f960f14c76c9a014bb8ff6943abf635594130789373290ef2c99000271cc417a41091ecb315478c020a26545812048ec1fe82c1420a8f5627d159eecc865ad5508424323165990eed29599b667fe076289d317ecdca2f802326072891f66f220cee8dc73725e2cb3651f85d42c07eb9bd79c261fc099cfabbb7c637d98e8eb3e1ce7d6a32b22a27e12f9d6c53c2d73d509ec2783d739fb5e079f0a7a3e9818e2b3eb0a5356043c05a3bc5a9a977865d9f5542fcc861efcba31be85280ca013d7f18f62ccc24f8018b60057536d7b36950eda19c4fdfbafb64660fe8d5675483ad49b01162a25a492b43cf00c6d7dae14b2cd2472eb6bfc77291b5cb46d852b9e4932c5a2fb92fdffed5ff915fc7f70459b1b18fb25990ca267319b5b37c6fa91d009aee19b3e9262b363b1e9613298edeb0319294bfc2e5978d4fb62e930ee84c478a6dd17acf6d6d14c01b0829180cf3d352822de17e8a508ceae2804b577cf25382714318836f288c434fcba684506ee27240c4821bb1504a26ff7dbf76dc53dddffa2a8fd185d4e74137c093c0926634809023f91f5b1d22d19b7708d0276f1ac904d1df90443a6feed3f0e58de3c57e71a84bea94561f1c51424b32361e96ec955f5797d3144be8c8f229550bc94fa8f1df6afb0d8842227c0cba3199c994e658e6746f9768093c95930862e3a0b56ac0c1c932e4bf356017cb2ca19e29be7a842b152d800ab0aedfa834a3c7d52a8c00f55f6eccfd21e6775f704cf030f8bc93c9b9f3cdacf89cedea1f828ba2a967c2ef571f0d8981360cc509a91c5c2bfc406a7c72906303d8d8e29c6abccae4cd406b0d0da7d2529d7a8cdb4dbbc5142972291b660b547e2afea0030d70045bad015942a73853124febd5be8350b3d83037a6b57ddfb1251faba5e36f34f01614c2cc4ac0a2353e74a9fcb3e1207eff81c29b29a4cd90d839ac0303ca4dc3c100a1309c1dab3e87b68141dc7e282f96d2913b3a11f5101a2dab52b3e4ad02d8db20ffd228dd53135b5f9a2e5f40efd33b2bba6caaa5579ca941e34290a8019ccbb0c05d9492aacfe8444ebba8ea2d51cd95037aaacb110ae0dd86a51ff39a4a7cf1ebaa014bebd420901a2aa991e18795c8bff01cb64436ade5c1720fff5defe252c89a6983fce2e13997238b6c375cd5838163219451cd3d6a95d617a3757e98627baf0249e295fb4a1cc27f404f1395e6cf613177748c35b5fb9dc002f5532a62e7bd8bac7a6e5374a45b2c066b8dfc9a40facce7765aaa525440acad0513cd71e9ba1123a92b3d6da0f6a6e4c29ce5ef71ca704217e6c985ae5c12409c83b2b2ccaeca49db869ce155e16b30675acdfb4e74f236b7abbdc3b25bc6654aeb3cd0bdd2fa5f0e8bde0fb255102c9980d85225b5f1122ac38bdfefb51ceb05d453f1772c63be41b33fde57974f26a4630df3f9b115b032591c02511255cc052375c145bdb795ea0561c0e86e47f2c40745beb4a9244543a8d8ebeba6553979389602296b4f2d074bd2251c2e7a693a2ab3efea312b8517a1186811cb1703d62c60d1e5b4add004cca09a56964444396391ac57d653f33c2cdfc44a6e63bb5bb073927613dcf926775993dffb6ea2c7e2f2f6ddbc7f534b6f023dbb43fb40518741e0c69103cce484b13de07024b6cd9c698e83d0829fc1dbb5f8f1ff8f7b4dca69a0eee9d7777297b8ae2276377c82b930dd6908de907c96815c2127f4446371dfd013a464b7a07c13da0527242c9f3e885f17879c6004d6031c1dc37a5308ed1eb2ab46a965af357c739981f9aea280d8fdeef48dfc08b31cfaf5f085764459bc611ec28126db3fefb6cd2e5877fa445354a4a3fda6568e11050764a9f17a33b770d5692ff0ea347dfde2feacd5f50bffac4b852fb04f3cc7be151bc6176433692219a35ace66f97b9ef140eda3e578da58e12b0c4d0ca5bd24cec0ad9de1f0aae19c2823c4d79be5f747898b6c1612659254ad4980954587145c25bcfc7bcd24513fb7fbd6388193b305c1f34d893636da9e249cab876bc6bee69c68ddb647f5c2570fbd44116ba2c6588b93e791d2804db739151114201602c986f95461869a8230689ed1771845fa544d40f0dcf069039547f671777d8adae4ba0da5ac598760d03b4a0e2e2debfbcb15663d48f629c06bd90495df5a05c07a4aacf80db859c09d245207ba23e0956ef6f43f3016cd3a16916134f0f30e7a9353c7ecb525d6a226a4d7b349c6b2cb215c1333ea2d15d89f46234fcde79a6ae903dc8194549b92bf37fa219e3847fa332380b064dd59f22d5979687959c467a626d72bc67dcdb272daa654f1e5de7bfc1753944c74f252b10d2a4ae5ebe6a5f8394bf074393d902c6efcd5235081ed472513b81daf4b503e9d47c61be4c45703cba89ebe3347a18a0a87208087cbca1983ff1b3e723d251c254ef889b459219b56c9de83451f7094e5686ecab23149bd755480e155babc78e1dfeb981650331250ad50c773c662a80c521d5493b6b56f4200127656346cda7705b9879f7cc66fe32096dc3b29627082dbc564b34ba021af2300fc84ae6f1d967e4c230b6bcd931e90f553dfd6728f4ebbea547f837170d218c2d2e12abbf0e8721ff8678032bf4d4d62027b58b0d26234f32b61b7c2c7d91619375132318e552b6703061f0b914192b04347c04202e8bddae95e7316edda12a71954f1c0704256f60a180bb3945e00ff5edfbd2056f61610e44aa5d9c5c74efc9720ac89a5c3dba37d12ba8e20b6fcd89db55a72069e3eb9892988392f3185035c36a5cd1aa98553e0c6524378377dff4550af1272462c0881513c64b2a36304cbb5f0673f25f1b2c23fe37cf3f61e6b7f610041ac1b1543baefb5c05f8e135161322189fb52dc0f48d205a1b4469d3f47a6768244ff729077a9e0fd3eb881ff244c1c6a1e034518c85a9990aae5080cd3285f91d23bc261f6b2c62274e887a801fb717eb48ffc0394b133bcaedda178d35be6021282281b29500af25729a69d63df92ae7a38ed24326954b0028d91bccc607371bf84f236dfb952bf8c507b41c9e02828eeeeda60fae8e851939145e2f31726f2af3ec7f3dac53b2ab60efd08863f1d8f007bd851f6a4415949fd7877fcc57221151595feadfe852d6a6104cc1ec027efb3a9bb0aea0bf1b6715826bb2d8712117ec6ce953cdb96931186fa1a153382393864e9da9ea6c814d835f9f3f1cfa0a0953d6acdb9a2b0ee74018584e4b68f0e81092b375c04419c008ea6a44f37e221cdeee906342a11f940350bd5cf4d30deb46f036619ee91884fe5b64189899411adcae708b69806db24bd9c4ce984a56a5761faa3befa8009f863b54a1346e333025e5975353f3f9f00372473e4cb843e9c3027367a9f3050f91563f19912a9b08ffc15b607e5786241168cb7d80cba40efe61cce234f567c163db710e5437720dd7a6cd1d1af665c65904142f41910247881e72c2a99af525c79b3944bc526a0c60c553eca5a1ba6f699890601f6bea59f612944687067a49eeff6af4cc194626a76ae13d56d858c1b172cca005ecd2485f74a469226a3d4664f4d4bc8986b11ead53845d95677719c36ed2cda007b01139be8ad9a496eb700a8f25203d091f15cf780427ba6f545e02c59df445463b2af51b6b3f7381276e6acfad2dfe6b0d2abf08e79e7052e6f723908e5c65f5d8fe6cace05eafacda5533ef8f894ac5160fb90825ae09b542780386f8844d77ae94cecb831fe9686c93ad0fc1b0d40ada1cbbcad58aa5834ceb17b57bf4c04a7e365ea6bc52759b30caf849534590eb3f1a5b815544cf5a2ee9d2a9d1c421347892aa0b527caf180f6ebf4c0c0f9d7ba6143e0e69024c0eb1a32b8ca2315d67fbbddf7d77d90250a9e2909cfa7d965bb4184e1975f16650a489901c9d18270414efd99e1557c7bc31aac748da48763aad10bcf3cfd2dff30bfee0d3517c9653e506aff2b776870c108282eca658a306402393c386217d6af987707aadb011bd56c1524f376166df9784f42c36004f36b106c2afa3130f132b708e79ebc8494aae79f21e15f24197fce5790ed32b7d4877", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c03d33039d45432d4999b9964018d8d6552808849c83503f50a70d594c01b441900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000a81b7087e1431ea26777fc20238aa980000000000000000000000000000000001991a803e262c7a38f41ff63f803b3c202c2cf87674c88d6103108c767ab2ac48e35bee25bf5e92c02761b47af09b71a1301f6e7fbbb73aa8fa2046f5a9854e60b7c72f0b56d2cdef4e3cb5e3d1a660b0827372d8ac27abb349e731af617d334968a1a677756357ab1b3eb22654a72341345ab0ed28ab1183dd8d63e3beb4a6335b88bb03a12fdf4bd39c23c8ac80cce0ed445f4ba59e36bb04bedc22d61894e7f47177273eb6efce052396d70477f050dd6ae6474cac3235c0eb911e38027c416320fdca3d180736d3a3359939e922b2811e91f225484bfbc68da2f299388cf367b3de96e1a14934bcfdeec50d9cacd2aeac2bfcecc16e20fe23d8faa581b380bdf19f50db602737bf9076c967d4a63" + "proof_hex": "0x0000000000000000000000000000000000000000000000055ce842869ecc798800000000000000000000000000000000000000000000000962d271f2a31d7df700000000000000000000000000000000000000000000000cab13fd160fdd78b200000000000000000000000000000000000000000000000000015b9c8ac1f7af00000000000000000000000000000000000000000000000448914ae5744f2cbe0000000000000000000000000000000000000000000000065a47ab8b7ee000b4000000000000000000000000000000000000000000000002a7228f267b2676cd0000000000000000000000000000000000000000000000000001c08f86a603dd00000000000000000000000000000000000000000000000c08e7c3252f76857a000000000000000000000000000000000000000000000006db65bbcdab667efa00000000000000000000000000000000000000000000000a55066ba466957e060000000000000000000000000000000000000000000000000001d9ef76020bd9000000000000000000000000000000000000000000000002b97faf1a204a616a00000000000000000000000000000000000000000000000362df9706d637d11200000000000000000000000000000000000000000000000f0cc2167e89450dc50000000000000000000000000000000000000000000000000001ef17dfe0bf090c486ea55a1ce3e9a1c58c6d280f64c6cc337f4aaa58296bda4c37e368de55080298c60ac27537b0c7a66f2110eaf284b8c1ede53c49f2e385b77545e4a256420867e2ed0213a17a18c04d0b9dd4c887e3d57672e2d949b0053ad848ec932df90bbd48bb079881a76012522639773799dc13cd501fb540f0ce659049daf592d321c39bbd718505dc5221412f4eba8ab36cb884b887395e583b5e5f6773adb4922963e5f0e9811528de1e4d3cd0a804030827fefb63c74509022aaf5c3c39987c2b93049d138d1aa49ff2a71fbd10acbd91e825852dbb160268db8eb12b480ff61497dd6efa3c6957af87f0623eb5f7abb82b17d4db53f3696f4930018071d10e00a8d860ca77e800cb86d452df258dd4f7383c08e0692d8ddcd73a356c7ada78216294e1fad95e0289e0028fa1fc29194d2cbd14df27e9b5c0f0948d3c0725aa1b202607c7910a2af9a6fdbb5365e40af6f2d529bd97b708c1e5466394c8330e0511de6045511cec4ec3203417aee6b3055924840feeee2cec43ba9aa419adbe1a8a812d9af9d03eb346ce5969a7b5fadca3b0f31450b95bf2f0bdc77f7361e401d563c3856b087f8bd2a15d89774d92dc05b38e704faee3e675238b107b02dc23bb6f60966a057bab79050b8e2f6f5e0eaa3eb8e908371d14134ed57a2806560534f9bb4dfa4115e83882a3b2230249b8057ff8e8c08cd1d52245366811ba341a69016d7240ab6dc55923034e998618e95b4c6915158c01443836eb5d253e99203273029ee38fcbb0bb3e0a804d5eac0d2243a734993465dfe0e4f997f204f0304ff20195494c7109597656675719513d51e0509f06342b4bb3a9beb1f505dc1b858d5015a691d14be28720789d7f5331fa146dba6b83dee83542848bfae502099ab605ebc8bd7b733a5f8699828b1be13dc3dd1e77cb7cb4aa01869f67223420eaa1efac3448573bda5d1ca8387ce8d772c9247e12a38fb93c0f517d341ddf2631765b99f8290a1b3ebbb2440848fd666c891bcf6bf3b70a2fbe53f758e42d089f0a311d621d74eda459982c01bccf40cef4fbfcb1b31d9664f6a50375198d06ba84f6e6ab7a1f7d7b1144c79fe1b0a37b9df6d9322854afc5612476a8adf12f6d341b75bd522bd581cdc19afeef48c3ac919a365339f4f128e5bf5f955a350020c07d403078252f358c479535c8c4b819462a33eae0108b1c909d72a954ed1b704afe5c0f2093bf62d4c255e822d7ed58bae64eef3e2fa1f87055a8954f760e2c0c7eb770bb847105517ed113eb7fa577842b56f05f53039ad97bb4e663c40e4ced08d2214235afaee699bc67ad793e80652267b0e49e7eeb82c2245efc22043539b3f5902b00a2682bc5e10149dbc78edc127512a8eb171f570934d796a0040d351b9f54e8d13541f6fd1338a09480bf8a185e5331440781129513d45c8124fb65bc2766404225b3893c5fe4b369e4017f7090463c07f80d1575841d85c81dcacaf5040ad23865b0750ff15e6ef73f8efaa44adf5de2e8faf23720d0dff214d74858b573644f746be70e92cbefa7eb8052d2750862a15ce2ffd17c6badc61b286005f0b0330e46e603f16130e26d1254fc0bae1a0cb763b49e53287c845d15496ec760d6b385137d7e72bf05688a6ac89b1ff09632650dbd60da405d50482d51b0462944fdcf8ebad761638a43c5bb6671e66ecc61bba2bebbfb8eaa00ae221f62491c1deed21184a4b3543c02ed8a56740504544c00ac6c5943a7627cba21c011fd442067f60600f8ed7cfa65e8105bea7b2560c2a3d531aba8d978c6a61a46197c5cfaeda260d1e5a3d2234ad4d4d15c9809fc0718f2a494bf6c06b8882c0ac29181a0299c2c3ddb6d403109bf38985cb6f7f65d9bf4a27ca16eeaee74113ca3fc1a9872826e441eae2c925c6c6b51b069cf13230ad073ba2cfcf689421d1d968f9997bb791f8fcc0b33d93aaef6fdfba7fa52cc956976e2d233ddf53f23b86d4a0ba47ff0bf3ace001f560024700c4f17a1a63bddb95d79cbea7fc76416edf7b3dd77d39111fb2c428eefb92654da6439b55cde33491217fcfb764996063bd675ea62fb9811c66727b09e567636e6c8d982e6f65fad8dd6f03acd60b30d79a476b76cf8cfea809b1de99986aa5f4211755c4a9606baec8c3dca7770ef1366f4fb6de53e4d3ba838fef144fb0e61ff4ad3f648955db95d2a20c7681252084c442d538d5e4bf0d2027933173276954891dcffd109736f35c2cb1266526724a5bbc506f3d941167fb169289beaf1e5f6f44c2ad2bfc807276cc8b31ce03414720d04542483a9b95db437b80f6c7835e841f6719a3bc1eb59138760dc50071a8161bb4008dd47c756af6cf0adbac7d9d2d6cefd70d01b9d2fcf8f77159b6f1501de921b1f99d288d980a83eb9cd78c3a72cff96655c49d3c91b9a76f998c826c1b6e63fe19f1906fdcab7ce1b4a70e3ee8ccc35d3a74b5d103989a163f1671f9169b894c9b79146ecbf91ed7f2d19b04985b6598a168dcc24e13c8c937b200b687e0a9c32218ac0b353c651d22b6b8bd740b4e4c96b68d8afaa71737475b622efdad1f288db138261c51e7b753c357c6e6f940fde81bd1c23efb4bdd4bb2625fa314fb98094120d400a599da875d2323fe7f784d7f7f02cff4f4261930e6115aceecbbce375a0e0d055de53fbfff07fd8fedd827513b5dc8c91596168a2c4203455de3d5124fc35b50d719d770f1b2a6bb849170609d8991c7ce6657f027824b4d6d75e9c2d0352e4756f8b7e889589e18301ab533ccd69cf826d485669ab0fae342f1e2bc899ca929a034f8fca9268325e85ae9442c8ea29922b614c63bf26168ede3750d1e16dbc1ddb96d8c6eebcfa806091d4ade0e8b57b0939ee335719b870f19df1392d5f3052cb09893175c94d7f583695cccd8dc6dee51c016a8626bd3a072d09cb859aaddec019541f824b88f5cefc3fdeeba33890cd82ab4fb40c310ae931a3d1a5092da0589c32eab840819e36161ae4705574ab02e392010b0c6c7ee76891778e31fdf0c97a1c92a70a0076f372b5d1d2a4e947099126f32800eaa4262f52d440b612e44a8998b7d39b9d3458f67efd9bdb7c338e24b4ef6416ece81fe457a53592b63158bf83107aa1afc365e1da32dfb8eb9e88de6630ad2e6d21ec3d610f11db0e154d2317c707a68e98e7dca8ea832ce0c3b6e87e1c2410db760580d668b81b8c34fa546604dfb83521dd1e97a01a71d203fe81c483e22cc69a685edb4b1e36d77eda636d939773bfde64a736f1c4a61ed9387ff0c2b900e3aa4faa26330034c3dd9d9bd3c21273beb1162849922a37fe7515b720eb4e01177fd2e101efece02db75b0ddffe3f4380da161173c95dae55c3a2f4df8e3401d982890cad9ed87e794925f61a91a18583c26690c6f467dcc6ff3214c98de80b38863acea99f3b29382ef329b4d6a131bd30db294678b995dc48776109dbee2d1d27d807f24c1060d5d4231e51ab8491df45e58a674d0376c6ecfa11e580f610c34b0dbba1a527e1f2cb35da461bc2678fef8526b370870a2d82004f131bfb2716c1ecb028e9463998087aa89bab1bbe2c932b8ca0911da2869eea5c3ee3881760a95f7a8eb50f8dd4a6cd0c0f3e46086dd66bddf46aafb723136d30b06fb11edeacc0d11534b6cd26b54fd2a2b5b7c078625afccad81e5ba3a2b8772f460d10561dcf89c1f0c7b42884b1616e54d802a2c74714e9b7ea8aa0ddd4b6c33451193ba708854c275a9b8010e571817e0000430bab43e34a967163e4f58302123d0938e522095207559bff634aba893be2845e9f21db71bc596fb79d6f0d41c61703125e3cb4b85c6e12a160ff832607289a81e5c338b5e232bd70276727ac854506d6769a8142514417ee8e51e5c6d5451f37cba86ebd6887c7ef292dac585ea00be88df96e7954f1c240169ce169fa3d0b373e5aa01a5cc0000c3b72bca5fcf02e3e6bf9c0479a13f8b6ea053cefec1edff02097d4ae0c786873d701dd471e24224edc91988c78ec44aed74459f1a181b4338bafd7f8aa87d01ae5381d73cd9019890061fbe28e8dc94c43c56ff08cf6c7758d172a6b5e09f8c2a04d622377a613f038ca39dc90c0562b7edaca03899c28e3c7fabde8c898f45512e6e6f750d2259d786142870f0e0cccf6e5d939bfd1494ee6efec567d2e4d95702d53e365c817689c55568e4d1b54f894e694481082b2d2d970cf5a10e938ac8a92e611c67c0184d382309f13a9bdcd55b4324f675670e58fe7e9d8f0cb03169e9122dafffb088529a724422538d01a90bd4e120c0353fb9ca523d6f0612f2e40f4ed973035127a23595b82be133bf9f740c09a935c2322b9fcd332a394e756b7b52f1d987425028e4b45e9dd2cca82e0d43aabf3c06e7dd976e054c93097d46187fd10fbcb27cf8c3affc52742e93d39768323cdd1019fcdc836b6df2676c26e3978de1cec0b48e3ffc5dadcf21e5d41332cb887d517762dd280ab338c2a9008085c0e19af03fcd144adf101f2cbf8bd44412889fdbad5e6f069980d38b132ee9a5bf085d9146326ee3c919d61711627b5a6b555d6634c83920e909c7a2d61180aa6211c0b2248a4390b4ab1a1debc25b9a022e5b2c448541532a46e2c7c2017ee2175f93604fea8aa37458a9f56762d7b765f9d873789e36e7af62697c4e769393abd15171735b08ceb9d53f34249fb3460d91b9b1c3b456a480f7d21058584111054c8121802d811e86c250d56ef3b33620e76d0dcaa4c29375d5995f0d50c132a9572ff2087b3c3167c81679ed9240c6bbedf837f250be21601df31e2a9500a3f92f9a8248faa66666ba4ad609ae2434c867885545e096d2f3146e5f425224601a6852e22c3d325d8aa1b46ac7875249d4612c7640eb8bfad9eb1bfe1b742ccd8359801238fd14d6872d2c3bfce9d495bbaaebb6afb17fe24a25f9941daba0ab3da9bb60e95bf90f00951bba7a1e1059ef16bdbc1f9a03f01cc2e28815ae9008ae0538d13d6194b75fa97641ab1ee517959412ecd458951cb7fc0d83f92e6cf084b315d189b34096cd228d121201451cee19d84a4dd2ee0bce8990b1231c11ac69ff5191ba2fd4ea11485b851a68464a0220f83e25af9c67cb3cda3e41352be9b3bed121dd333040b446a4ff286f39f9ef8e06ad8417ea48e06bffa4856f1ec5dd31c642e49c70e19f968eb21fe0affe9018bf648c15e4a4b98049915ee273e5ec6707122c87bad5c33292874154f4a347192ae7fcf8eaa2e75caa3fb1a9ae3def7bf7a0be2affe3549c7fa4dd0daed1662adc6e7b521e3fa7f739115c1e5d9686647dd28b37a857b0eedb83a7eb254e0848ab6de7dc806679eac0ecb31dafcafc837cd279b43dbf378ce568e3a351fd541031e16c50da25418a3e6a452ffcfc92734dd303fe2fde71771616e6cc8e9f31664367ab2290aaabb9bc088df25cafc2be38b12bf93304c989e32cfbb9482feec80bf145fd3ef14b7956447beac4a4ca47409124d79d39370b82c7fdf161d6453b0e263139ebbf60da8b3c169602d38859f1a2a51486064f893c3e58c61c82eb3745eaf45fab83574fb239edf743d505057e52e256288e00a1a94d0a7688abf6be831c8a1458b46abab2ece16a7c500c74bc620827df055dbeda00ae4dd6ebda939c21e70f1d12a9e77b8a4e5e981e0ba946b2b69aecd0d8d79930c74807fb3c0ce1e63698d128c600f7f48c5ea2f9f83d55e0c8158c826fce72f6b982c04c509fd4144a7fee1fb1abec89a98819b14521a461712ec4dd45ccfd63bbcad521cc7c9456786140310cbef37910ef4fa76a0b9f218cd143faec452d006b0558e48c750fbe49cd83a0e2ed6b05220ea138cb7c2001e05c57a07f84c8d49bd52b3a7b5bb25f58115ca0794a123b6a53c254b3014f02198de9656abb8f641408a683ebbd20a73888d95c4288bcc101526029ba34589015f2febc8ba646c246fb7a1d8202c7f45107c6545ddcbf269ce61b07a61aee601786cf921bd7dd2698cde13a1a9d3046c90f50f78b2dcda8a2c1e35cc7a4590302711c6f39ac220de788bf6373243c083e8bf50297a88dad7513ae23dd90e1508831d3bb6140d38add4852319f93ab237b8b5d8928640f0f80a431a4b7f70941f9cb5ccd1c68cb956b2f473d64050909c7a95f1606f57181d6cca5090f2098512938c7dc06244eba5b7f503ed0ff889c54075d59b1a8be310666bcc5de3e8e60c7b9856e747bd0beba67be4a08f1c5c48fa889eeab0f88c726204a06d8ebcf4237d584209b41d95d0eb897cc54e5d8f7f122cba9ee283ac829dcfc1d38072162d01bed3a0d615eb419eb21248a340dca81e837e888d901b932f609125312834225b9f8245e3c0b04755eb9cf217e44214a7f3c951d0dcdfaa3cd68971fc7d9c1a909756100e65db3927408faf8cc9ca9be4ea96f756a9bcb1546c3d799b37a009457ec6f275471529659bd26f485c8300e9720489dcf2e04dd4e63fe53197ab06bf3ccb731a9d8709f8b62cae790c775dfca53105b3303c1e44c9c11033811c0f232b2277c0a3258506560ed6502924c739fd27afc599b8e648387d91ac59f50e4ecea056480ced0c14726aebaa6a1950e82f200e4df6c5748fb8b231a6900c1505c1107641b05e1dc8b6119e42b5930d423eada653d232d616e04e1489fc482b00394406b931e7e947336c3cc832724f048c9aa07be91219a022c6c0b5918827aa6f17b121e8f013ac553486989dbe67e89172b09e16ea58c43abbabecc27b036bf465ba153f787694774a55759e714485fe02cb757c55ab3c7493b2385bea2c93f2ca81dbd969d9c1bf2d0d8b5db32e7f9ec2a4e19227978c70c00b1dae6e2749aaeda301af6d466d7e9466fdefb3f1988f7bdfd71034f29550c02ad368120b7b37a2af19e538fc2a594e9ff6b34ff1b7a0543015b352725edde565fa3fe80da7b6c0fadabf0d1ce6334648ff88e47fac1318667e172833805213bfb0801523daf456d7b405343dd57cda0a421bc0c0d5fc2179194da627e1ff8927cf28791168d7e6d5f05174c52e481874d74feb11436ec4ef244ebe2196eef2e48eb9a408a6bb5b585158e08d13b38069e7a3540035ff6bb581e1c04aa3bd8deac175bf21226269e17898b88a53068def6ffddd5d83a05bd8c4e5c814375239fa31eb842e679021ebfb4652bd7621f5148b8a75f96869354151b923f5ee8a34941809351833faf1cefb800f4b94476f8aee226f6661a1a7702bd245d17b16dda558a7d4305d320aefdc8f171798e0514edde4a054fec5f961a7ed72e49255c37481107a1dfd33f045acf746bba0e6c61332dcb92b870146f41b6dc69950574e6d1c505a06cf243043d0c5ecac25a72b73100a76289cc2171aeaaf38ce93a601a73b583b24946b4f2d26a8954f9c0ec775af2c38cf418d672cc877917488f3635b76a62c1e2e7f0adc6ec12ad42ff86b9c2c7cec5df920fd22faa123da306058878a90170d3cd59fd235e2dd672696a71474cd0e31b00bfefb6979694ba889180a1ac46310f3597e8b9cbaa0201f6a55a47f169a097d6955faf1f5b2d40b81dac62d1374141a2b990889ceda3a740608e2a10778680050ac7391fece90953d24e7e7416007f2fd904ba98d49f13a66050078f00988943f962b91e5d258e91aee9a7333060c911e1d8c6e9d9b69bd0711b00508e3dcb739b3e3dd51beb7a08ac943dd6fbb1f5ff9a3caf6d071b8e0ad267c2583294658d13b120e1415d60ddc313bb560f8235197ccc477262e92a2856a02372e9b29b200f7fcb377481818cd6a69256f4f256de16566a7fc84eee20ca1dc3dafbee9636213f183b1ff2bf10521f63374801cb0fd3dbe682180d31952b626fe63a8718d5e106bad6314fe42c5158a0e40d4211789a6fe58d00a466c56254886fb88fd8f5adf161d1eaf20d7e89e10fece730c471555dc7b3f3390e656b26843790a3637e73695a46c52ecf5991189fe4bd22fef726444711f2980dc8dc9df57d4d45176a4c007df5613d90e0c5a9200c87a2606dc036b2ad2d15aebcc4ac9a49b7afb75ea836a55ff51acf0d53d1619edc701a4e11d6380d92df0d20c735b42f4d62e99a5b9701e25c81252da0d782eb4630bccb59e8e1ad0fbf269b8784c1101d44e54fcdf1947906973abd97647a5802009760068d3b206e60d23ec95a7f182cd51de316bf06c6ec5922ba12abafb290406c58995a79853011651568e23e0caa55f5d76f21498f9b91e5f89553dab1e890118e124b11ca3020c84df9d9ee08ecbc8ab8ece7c559eda7d0867e4fde6fc90049fa1ff83f7cd07befc7c5b81f85d89ede5561460d69a2b0208dafc991d77b324dc898ba4219b589df712283fcb75674d69c6c65a713a1faf062fecf1abadd503be4f29b6ab35252af32f765eb291b095d991aed2a8f21843c99b2be83e3a4e159042b3256dbef2c442c35edb9cdac6f6a675974777058044e4a04e9601f8920f135e8fa120ebfb22d6c6cbe6ef20c28afa1c466a74048254cb21b103f174ab275ffacb7f0a125f2501695d0ed9a7cee01d11bba2a464dd0cba2c3d1f12a0c912d6b2b3ad5b0dfcb8bed00d936563eedc89f25d3927924789599e0e1583e0d30673fd3a490618304c9fb3276cd145bcd9bfd6b588d030b3a39328611fc7218024814cd704f1b9077165aa00d1ad51111e2ac9feafd03a98cb61904a77e7018f07b69e299cfb5c653144dbcff6547858344f7fa3afb2772273d16cedc591df1a0aafa97a1920d87b2bfebc00fb094769e0872598ba68d1a815d183f91a3128851d93d8261926180df95fe4059db1b37c8062a5a9340fea877bd24924d7dc2571152f2dbc29a725e66d7357d38ef6be04141c2794c001f4a4901e48dc400673d20de83d2f14983c3cccf64941ae15789e01fc2197e4a8f775bdb8985b7c12e9a61cc62c5ca1dde07fcbb9aa9a85d8a66966cf50bb8ba9207dceb90ec4d34d1e0727ae1fca72f1d560735a23a8a7c1990de206f02324b13cd9a1c0946c900bfe320a9f78761402b99ea57086d7e077ee89dc6ce5244a739dd99e0e3d134698191410e6057e266bbe58ecf3e61cded58440bf6f46f2a65f9906143225d1b792383525fcd0e7fb28e4a88c2ab859ff5c2bb2ab8d4ff0c805d5011b500b877cc174932084174241d26df0dc266b21c8920e2d1e106c684b68129146c821cf27fca8482992c51ec232f69df73839c6b1ae4752537ed7a2661a4cd100a6091827f2eb501e478fc9008f37322ca0aed80ec3691925d4b3cb4408b52493ec88a1edbd4b42239a2bdc87fafd27a03b0370a450bdc25a792849c2e7fd69c043a1e94dfe2c3b15c8abcbb3890296a86c229f48be8986083dfcbaa8d3e85b4e6bbbb1fb671ff1122b1427f95a632ddce21fd794e38ebc217295cc82b8813e471287145d2b7cc42907469b168e2cd502a5ba55637f5e7c8c1b59651ab1fee18b1bcad3dde33ac9093477047b20e9c97d60974f166e3b383b1fe71c30f4cb82b3979ce24c40fdfd04590f98be85f1e51adbb774c3063f8b0d3c21e7ad0f52804dad372c15e6fa0e2f30a43c833758dd1abeae6005a5460cda925b2b09848e222f22080bfb0de23210acbf1fd192561493bac336f9a3778623dc4557c6499526cd9c243cf03bc38217a80198f7da0180347c165f9fe6864a56e33c70d43bef93f2946f0b92b46f1d1c0f4c2de377899cdb06913d94942b594b3b9d710d2f13390bdcd2affbd2067f05400310a01b85f77206046604a527ef6aa2bd994c358c5c43758d4fb70c57ff1361074398c50a1afc44737f5dd1d7bd9dbce9a6ed59af38b17c5306f49aea64290ea6cfbba1b9dc79135c0faeef989d29cbd400266c1daa446d83f8e6c8aaae06076f0d193ae7e368146b52012802081d5b0f6ca58a7974bee585a728c4ebca085f469ea3f7a4a87f5cac620c71cec8a82fac220fe552e4714f2ece868708fd158b335fbf8ad3c7d91867bc859e9976f3809412b5bd543cb42afb45a0faf6e11d779286cf918ed0595e91e97bb5189f90cf927856bc338e1c67fe56ba2aa7fb086ac2d3898cc932793a1f056e00093e3a6ef9de713d3c632d731294c8bb3cd929f4759eb55cac2763814b3def4dff19fce96d89afa9611f9448f834efdc8fad097040e09e85c6319e5c43158b761dd245b592335db8216533e8f65e118e061018b1fb6b0b153efa66ca702060f8e3042336680ebed3382333385f8e2cc58dac2858b1ca298937ba6234d0eb7d2c82059dfd83108448087c3af1ec1005c91e2408357ea9dc0ef0e2d3d259b5a386d127c9a90f7131932e3a0737cc76d199d964176cd129f6dba604c108e13c8e3fd0f2ab66b43ae4232e8b56837a0fcd373e200f347219fa0150534ebcb9640077dd28ad7e7da50ee7c8286e85c6803e506bf02d535d2125509c4fd6fec25a4447acd9f6ad70e24a5583221c6f6578e026d1d0301d741b1a65d8bdca25bb3a012fbab62491530ff29dd737916ab61446da60ba0565504d5d4080e32dbd75b8048f4def3d848a5ed64231ea0e07fb4bde0ad3923056492ad1331ac57fa12fec1f0c4b0c180268c2a61ca778f861ddbf150635af1b51bb17fc202d3d0f9b1aba55eb0e0a30232fa01c35b998a67059a7d0242fb31d755fd8ef6782119f4cc4b2674bfe12dade13654010f4984529c9252f116487170ce05574f1aed467512924ca000d6ab9802225d1064296de2dcaac4f26b4dc0d94b717b985a3327ef32efc0b3e0f846c3dbc221206fc80371a5dcc17dcd7c32e4b580d6cc65340fc19425065a9b8742eb5ddbd56c6b41f0a786d5213cacb502ef3267156fc3f839332ff9e24e16d1292c55a0e3ec0d460350dd6072af24f8f2755bda4b053045ffd5527fe70ec43660d5b33f016b95c547acbd302a521a3f216a40c18e811c4c022573890d20265a40e04e37fa2fdd2fa9a2a2733a6cee75e2949d7551ee07aa6971ed0840780492209926c0684747cb6e836a68e844a1b2f150b3508e5606b048c08eebabc90986b5486d39f892c495d59fc35550d3f9a3c2720c8614a86f72e6abe8e7efe82d0db83f10245f11aa1cdc2ed60c6a893792d2d943354c2d0d8be6ed948ee2ad9ac13877904ec2df83381b3b0e0ef1b51e4e62f6c1a24506789b31be6d72d9148e78f94cdfee75ef945017d71939fc0e55bda1ef846c1a2bdc65fc0b60f3f94597f12908e2ce466b873627f50dc8434afc06f22fb9f18a6989ad6b106bd7833d7e86047870be3e34f69481b7b18cd2b294bee1643f06344dc3dc3649e48cccc3eae92a18685dcbe7953e16edd3c907fe11f360e48b621148295135f1b9279d932c1965fdca8b97dfdee3d2dbf0840e79a4c0d1c16383d15a927e49dc0dc02d9d58220300035dae9e9312a21f6dd99f6753acd062b0071b37f847db9cfdd021fe78c7bde6093b9e541c5c9fe7b5e2af3cf2b2f034254be50fa37b02c0b7858a4930b25f966ca158bc646ba09446402753231db07686af291f21b42316f6235a3c938aac2ed7e02424cf094bd6d346c9c63f057078216724677689ebd37b2217d471c8beb32c37f2a51fdfdd4f15537889882642918154f6b5b5e8b034a211c4d097bfd491c66df578ea54c4eca0eee260411c620d77c767fabcd438f857c649baa2bab0dccd9330e20434a44c7e772e327a49b1c3748df69980d9554a644142e27b2aae6b6ee58d2a72e86ae178beaf9dc356315a64214933b1185f371c8f7939b051a658dd40438b51dafbfce0cc948dc108519ea0a9c3d599381a14b13afbea24f5b957d22bffd937a43ebf1c92bc5d1d86e251d5e8d95abb629213f5f4c336402b8c68f3939b81c1494ba5ac072e29877f40b5e3620618df9987720231560cdacb23f291597f193e58c17b92fff7e5b62281a419a1594f24a26f84cc6432d1d08643176a8829aa83d1452e55a3a7dd6527621a99041aa06b5b4171d4e382b0df83872df427e630f50a615bfe31a1930457b284031498574c58800bccc5ff89183a59e552d12149a934f193fc233ddb03921212413ad3e18bec4bc7a1e947e31077f01d4b79a4783270e5578e95b8bf9994712863404560ba919614e933751390da65fa688130d5bb1adc4831f4fa210c8222b6fd543e0996b10efe421d3c46dc7099ac8dcbbceb41cac8bf84de1656ce24b168dc44776fa35c889513e3b2828b08ca01ca55da145e53c60576ad6ee278bef0b4a03ae8869836c04f60f1e27f2658ff309ac27835fd7c707a970dd51563d821d871950c8f6acd1286f4d7541af2d3e02c58629aba8965cfe18f0870a77fda7093ebf63a1776d42f502810feb615ec05cf2a0b7a97e94250ffd90a61008f4ca1a000135be805acccaa61aeddc9f0630bb2989ea04d987a0efdd0107f937e392219a5cf0c720d2134b4d6ade1312aa0cce232d27d821047733d89ebc2b6aa2c108f1dab6411521ccc553953603d8da4c6a123b080cb0d89b73d6795323e33c7a0977d37628f5c6ba44735bbe408c836b5bad0a9e492ecb06a4bdc61fad1491351b0780b33c654b27179815a39f6670517fa3d80b334e7dae784604cc5e3455f90a17427bd8ea172a37c39950d1ea3579ad5faf157d54ce5aa2bcab0bba89a46a0ddc9db66f95ec2cc6d232ae1e5195fb876372d60557ca603289485dd6448eb5053f8b12f41b2f6f45248b8e6aa7dfdd5f34db0a0e5df775528bf9c92d7e65642903c7335e1388adf556f180367cb1e2953d4532c6a721e836b6e8597b84c333193365155bc9df96dd29262d9faec086f796e77ad97fee5d69707773fdd5571f2389d2f44a222144515c2bb8b93ddd28afc91a5d7d92ca7045f8ea21b573d11923cbc74a4df4e3be894d9ccb97a0e7b91b732da7986ad340a08067281dd0288512d7b6cb3a7d2252111b06fa07ebf07766993cd87cf10ee12fdde4fda032de471a26e10bca2dbea1ea37b93106144dc833953acb940ee865ca5bf5ec8cf029411893b965d3c56f12fc76f9a0c43842cad0cc1271baf8c25eca5ff69ff91ff5b30b3ca094af30a19ab9cba6f54b74e974c820fa4bd8d881cfb738014caf86c50a1b081850b4b4e5452dac5e96c1e691f8bf3b818b0623602ee87899b445d2da3e0dd93ccfbf8f7bf36c35ec45298c5e403ec69c92d0929e0acc39c8cebb87e48823184cf019f5f68ceab0613aa9a3d3cdaa66379323f27824287b774a8dcc32080bb6ea7f0dc89095890a928cf858012b19ce40e0e34a49283320a5254c9242291fe69b0164941d85db141d78da30d6d554512049b53762211584d247d1c56966050024cb1667765f81c0e6ec4fbcac11caa785d8283fd071538bc691046aa7c2305e365b6db8f9144227374b8880981b98e08c0d34099a1838d1a0ad49ae999a140739effefec3a087ed2355cd07b962251cec22071846ed571247d749a42f623021b9ec81e467f594457485759a79cf66fd19b3f0741f2e7400d3d8aaf02d3d04bcdc82e0c90264c321200e6d02c72eabc296b7b0d0a4e4b7fe6bdf8e7628602fe16c46a68890809f9de766aace365148fafe1cf51f5b53b20802ebc35ef54003d7468365c6144d5d82f6c1ad7517bedcfcad02a6e208455970e2fb251a860d0a01878111b00ec9fb3a54082e18d6a2b1830dabd38c076e10852acdba5eaea718db7d6bc92fd698ce8d7b559e17e0be53d6d9e8e25f190d59915d81d8f06ae8104d7cdf0e2bb5d37096e93f9279b4263ab4f5417c33fa4c2d98a724f8366d1e1c3d39a5254f2bf1b3766d950359dbd4ec49516cde666632f9041bc2fcc6e3a61698750845795f1439f1e5258927ce8d400db24a1ef6cc56461f95fc873b41e225facff7511bfc915315b06bb705d984db6283b36bed3ae9c9ac6766c42b2e300ba09cde96d329e9ba328a3ee0c4f497463db97ce5eee382a17ec90c591cb9b7024c8cc6c3a78b47aa2c2b26fb9ea1016344708c4042442e73f0c645c3db2bb92d37d5c3dcf71732430590d28c6b10e3d3746ca0addb56d94037156644bae6781365fc2286c3fe00a7e1ea0d022d46b92beeb231c0d53c992b356bf1067385210a5567e9a62aeaab9828557daf5eb1a4d01fa189a6e287035e0433aefde3df63178ff9bc2f9e223cc6a5619aa73a58dff836c547f862996c8cf36a8a3a500edd01bd27c1477a40cc7b58097d4741fa278af8ea4cca5c662a57a5b6bdea0b3a1e0b6d61b5403984b7e05e2c660e3318e63300906fcaaae8fe77b4dd204f5e3df90f95abcf9767c0ff2d4760d7d01ab9f29fa9fa4a8d74220b37a686f1304f014f037cfc0d94c1cfa3522a817adb950c6abe8641f87850078e2c10eb54696102b11db2b0d70eda47b883943a3dd2e4641e498066dcf6d934770a77dd6fea74c697132c4d6d924f68dd73437b955bba7bb43f52b4e751d7a695b960d9f4b83881b1134f135e1839407bce1313342de13206d55e9953b5d3ff5f5fc21a13aebe1d4b1a64a576985b3fb6fb74e1278ea93ff80bc3d2b93925e7e41e15f6a90cd2f16c29e84998439bbbe20b15bd9767e77aec9a1a439d607703c35a25d44e96600f4003f67e652d38d76ab4df561a60e84759ab15ec7cf06526fda23c8e6eb34ed5142dcb9cb1cf911dab416af6c243f32a6e9156715b80e63f856c17862f12bbe6452caa3f72dde551538abb9d2f9f6d743186054fab6e33e8138fa2a0336c640e9b", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c03d33039d45432d4999b9964018d8d6552808849c83503f50a70d594c01b441900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000dfe8f99963b947ff62a9677085fa128d00000000000000000000000000000000651719d455c726d57f7592d491c375c202c2cf87674c88d6103108c767ab2ac48e35bee25bf5e92c02761b47af09b71a0ef8e0d309ff18b3d6b953c338b01b97fa25e4585dcb3d2715bfad3f8b534c1c00d8052e8cd77545bebd67fb24f5e55321a23fd6c4cac362afd5347d83b64a0812e256a3ad805416f7ff7542aea223909155bb34e12d627f4535acee240fbc8f14d67d121dc7e34cee6e339f2837b37ee1d059676ec86d26abe900b1981dc50b2c243cf0f78fbc62ac23338af6f387e7739d8309912a551a2d3103bb652b7b5b1355bd9fb58c4b26e2c8082910b54694eba17179cdd14f23363c598dd4f5ca2408b2f5bb0cf6a4217d15c1e086b1aff8d866b732b95a45d34b55ad379cde70ea" }, "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000d6c9a4126c3a603e7000000000000000000000000000000000000000000000008cb1f7bf2a218a271000000000000000000000000000000000000000000000005220283c0e4cf6a610000000000000000000000000000000000000000000000000002591f9a02cd3c000000000000000000000000000000000000000000000006b4d77dbcef69b44200000000000000000000000000000000000000000000000b66320eaf86a9871f00000000000000000000000000000000000000000000000cfa07aed851a7101e00000000000000000000000000000000000000000000000000016c1cb8b2fcc800000000000000000000000000000000000000000000000f6b54c12ff26b41660000000000000000000000000000000000000000000000076495127c7c04d56100000000000000000000000000000000000000000000000e1ec3aad4e5396ed60000000000000000000000000000000000000000000000000000c6ac8a0fbfd90000000000000000000000000000000000000000000000041e7e2af39eb6aefa00000000000000000000000000000000000000000000000130bbb0ec2348df5300000000000000000000000000000000000000000000000251eff1b962479d7c0000000000000000000000000000000000000000000000000002107a5bda7e69199f2a94bd552a20234800516d5c52446581bda405093494e043bdcf1801a76310ba6c8e991f0f1bb10efd9ba15e43b131799be90a874af8846173da178d91200e38f0d23809e4d22a0a3767df9079919265d5776dee38416f9e54d05efa9f21026b6afddc8486b2987c54a70a1fb90ce7b0dd72dda6973dab43142f3b7c62bc2443e268c7406b5b01022182c3afb35673d07f621b0bcc7a747e62360eeea8861a546fa7cb23c3a401f794ed59faacfc55d7765e050998d9ad542712f29da780041ff587976aaf1421ccd4435f03007f6c37e7dafb835f0a37406ceaa510b1fc0437344273d8e01c1a631f6bf40db17dc5e594cc1d8e13419d94db116a6f63ef2b25a3a0f0f43fdeb8df3b943dcb414dbc186b40075f9dd19c440d4f13e2ea4f1261011370f9d84096dfcecea4ac545ec8bd01b2b552c415b27635e12737dea80af400ea25a45b8a026bb811f5f77e6e5e560349bd500475780f3af5ba16dfd30fc3ee3b89fc56bc2b3f2063c45ba5122e78195e3cb250a46c151bbef8bf71231be39c0dcfd76204d23ae6d83df17d8388410dff5064eedecbb755b8d52a598708e5eaaef7ef3968626bb7989ff7b194254d91d19098ef22fd8fc7cd6c97da8f25913e0cd96488d96dd964aea8ee93c7b50a73328f986c6d022218af78d87d921f33d4df8c65a0de3880854b70a6fd3df79f4d46d652ba9cd18c708038152e9b21fe174f0132d48a6be51930c56f8675ff96a7501ee1345d8983e3f2efcf4b3d0635ff07bd1b0feb29b73fc08a8e6d44bc17adc0d7d721057573e78b494863ac113f7174975f5f8cbdc3582841f7a981ee7287554fb5e0904fb2f26d9d059c5802319633bd5657d8f256fac6d8270e2b0e700311c79cbd1ae54bdb995626e4e43057c3beadcfd029e9fc48b0cf9556ac1e2b5bdab07f3382b71da4cf18bd26ff199bddbe6d355810c13d7c8497215aa1553a7bcd65c2419e6c49743a57f9172016cb4480cff30256429a68654722c993e0cf5d1e02273253188f6e0f2fe02b831c8dc40c2c8ae783307fa58ce6b21600dd9d4c3bb32d0a43cdfc51d857632d1603cd9cf7070c6c25cf6376751f793e33661e47138e502bd104041df30e46ed8916e2935294fcf05139704ef7e4dce91000022db750ffbe816d8682c01735553d0fdf8965d9f0b60a0d5942a38cc0c8cdd801df32e6a3c32f715606733db762c1170630083b9750f29e2c81801a47c041e265cc098a9b56fb71c880f9855157a90b5c006ee62f80ae2f4502f3a9836a0f398fd19e33c30b011925034d2279508b2fc478e725d008b82a40c48b2a0e14b6947b85165b0421150a93e5c3b9a90d0312ffe681ca700827dedf57d5e1ea269fc6aa78d7d94dd5569ec8acc6356721951df72cd0b0a898b3e27f9045435a934420c2f822442973bfcfdf3d70512cec520409ba8be9c92513ac863214905b29b828a06f6ebbd8e6fa38e5c741b562348e17bfabce3fc3da4b3c11befccfa4e1ab94ca6ba8e47a7d51670c5d445a695ca90b41b8163c2ca2ae3df50498e4ba161d1cadad55432b1aae3c02b9b5a3ba5ef6051f498e2af0bfa316f0b5b7294aca4a796a3fb96b12ecc2dbd30eee2f8914e9175566fd353df69a6b30d68e43dc4c73da4fe6b118d427601f2a1097416ca37a2ae870a24a6bf7dec462fc575c31b3e3b27afd9d9f487de88512f8568aa7b9af033886004ee63dd5244a2a0a2a244a3c12c276760d5f7b661bd55ab675d0788e0d2010921dd6930d887853fc2825aa1d4f656fdf9d6651ad241b618c67aaff811f67ffdea89b2d84a49eeaa676c46f87bf1486aba21669c58cf79984e1b5ad4e03f2d2a7620cf1dc77efaef972101aefeef67dfa6b173e359a3c8dd588da4dff0145a95fe6ac10646b4f0039fc29404b9624bd376e7aa99775661f66a13658ed14e6e8e9071187e03ff2ec4197a886923a6d63f398143b8722f4b8d7e40621330fab2dbec944dbb294003a13275ed98772a2bc7c0309bbb3f267c62928eb0bb11c2a818c976dae9ea75f1db179da83afd5fab47a9d928b359b8296b3e7c5681e17ef34746579024874cfd4a2e91b0e25923867f0d683e60629588204b3f436a90453e44188d45a90441ee017dcadea9436511978d98e9feec41e6b4b0f64c9280bd57cdadcc695310630a881c2b8c879e50dbbf7af09b160e3af7004a412562b293932c7eb279063afbf7b5e7e29612bd37742ed3e78b96625184b4c2ff16ade00b53c4ab08dda1da933d9cbff8684f0089d20a9844970ebf3847467cd601f170efd71790b8f8f52899d4acb3013fc16a232704cb553bc03c0bf865bbc23e6620666d9e72d6636e942cef862dee9fba615c2c22f1a34c0f8173e63b12696718b19f9622624b218fcb2d87cfb2d1dc92442219873c5b79af021e6f240b1cd847c1aa40e772dd587203f5f419de22222d938236825aed8171228c123b47843a74e0751ee5b40ab328d7a33a98cf24eb02ca51c5f2023f5fd95fa43b5c238c096ac0ba82b1b166abc43e4a1b1e278bb0dc077809363661e34a1c30ef77c602a26fe28e63baff200a767377346995d6ad4892217a4896553be0ac025e1ede41ef53c0c1ab2df79278b24ad61d06ff2cf1e99b92966957817fa789565baebc4a9fef22c02e38b7cf2d338b14dd6bc9e9261c3a84358cc539b268d7e96ca2a560d62912e053dc8801b13a56bfd925be4f10b74a2d33db592593fe15f5b6ae37456a3db1bb592240e3c0e590ba7c7e570df56c52a8503aa726b9cd5084ef22604894be71653cd1372f1dea72ea6bfb27392c770c26eea1c428c34d2582e5b5113389b1327eee91a17d2d318619887eb7a108180798422b4e11839a665595b54dda52df423d6f85f95466bdafbc0b2e538d53a7d4f733a199c2cda89c3dab1bc31df32c50ae7bd187e664d05a964be01278b6f03b6c6da1432a6601a8ca27279f48a83d820de21149942e08fba15ef61f8f3c0260c7fec1e896f45fce5f717e68e6ad2e300f38827fad7d0591c8ecabb0312d20b4ba0d020fedd669cd16a2f249156de381587ff4a99c4c13e53f8186f56ab498d45067270751d5987782280866b8e377e034e8ab6590cb70665fa7966e3d4bd41536809913d45a3cdac73d54ff80d57b92b7935cb1c6243785dbcce5290f25c7cf59d6797fdaa8a2ef5edc5b8cda7ae7c0e04359721793eed6b29d0e1184dc50ead2075cb3569f05088fd68cedd4c38c82a5912260dd73dace2d9730cc8a448f9d40ebf74c49e40386bc6fcc0e1ea13e922cc424204850dda1c80424c5f4e70415783b45deb7a73e5ec46d53eafd10b0d2a4e6a15f72fb98d519fd708580ba6fb60d00cc7bbe849c8e5d574c6363800171605ada29a408f35d469ce8fcd5b63cc85e07643e6e9cfbb9159e74a85dfd3be0a6ce1e71fdef69fce06a5b6727a42a067ca81b7703558c0b1a8633ed66a5d75153bcea20e7baeef5ce7809e9b8c9f45086236128821a9b67b5d6ac74bad8b3c29f163c95c73b6f42f5807f40b55957a876eb2516a2825c8fc72f236778358a10696d52a36de7e93b3cdd1f394d5468ca71509dd2cb1afe0ca81276bd2b6e0681b663a1debc6de673483e96ce68c81b360440963fec2467d817d22c0337418be2e5201dc40241fd68105880e2848a822ab5bead6b956cfe38c37d219d1b181472a0d5868d78fc5d850c491c9532d1f59668b9a56026d3961b0611f3c5104579b265fce9fb13c67f876520e45417c923ac9aca521380e224dada03f9ebeb77c0d10f915f95affba13b231ba5543f85eaaa0ab392a90f3e93e4d252747863c86bb2b3bf82bad04cc91244f5371a7d0d68ab571456e77a3f6ff3e20cdb96161a3a00cfc8472c1c127c63107a7c94d23f8889452efc2ae6169659384855065b6067c2b24571e4896c3f2dcfb11d3897158601a0b5471282ab5891952b06321c532ba1eb386862d8d3b0686a7c99c178b5d3a32c7366ce8b8ad1062b6abc970821e2700469ae25a747acd5c3c6a03af6c7e85ec16627ee42b81cc9cd43aabc2416ab8265f5ca8f478c6760cbe3113c6b93497a0539042a7a3af49921ba8665c65cec32fb368ded3f4d89f8bff985f141b608c2bced985360313e04b7e19550d6df36806c0a29be7c7b37c27266eed5abf57195d855358db574843efe2a391b89ce88a272a33820cf99cf2088d8910e313d0764bdd1ca0e5bb2bb820c10c95b3c00b45250702d02cf0b3ce811e4dc5ac5b66b362325c9b68038cba8aa5232a79f2f45f2725890d9df66abf32181f24ffced0aa242cc221894a4d6817d5020394a9360822f251caf9082777de6b2b15f498232343238d0b6e90cf6921e6c6bf4d195171276bccae75dd0e48d618a01ec2ae7991247be2be45dccac73b20c9b2053c77171c28b2fd03c8d84440375e372e41ff6388f7bc589de81ea6a1d7ff4e8b331edc04d4168c04e70b35a450c4afdad791a4fa3513b19d276a0fffe50cd1869dbaae213a8ed73c39708cc2a4d032ee933f66e1b87e1973ac316a2026597246b14ef418188aab93dc1cc6e59a141eea6e7720bc28ae4c8e2958c129122b2e04399ff41063f60c4decf651d58774db8f5b881b26f22b0f01a672bfea51bed9ea26faab04a2fb96a880434196ca20aaccf4c97788359624aec9211bc108805c85fc3052288af408b04b4fee3556cd03541fbba5d2d5b14e0f6f828e13e2ee735093525800959b3fd7690e499cffcb85593a4daa08b6fcb8efea6777fdc54e28727458c81ade87c5fa2af0051347096a16c332beab21ba626d6e379b103ba7e543f6f5bd17c14cf881bf41ba471b821338b2dc52932f26363119366f49d4128833dba8290bd01fd27ee7340f46e5e7a624ca523c38def233755241c181bf12196588dde9096521920f4da8a2e09564ec78a72d92dd426bcccb070d4584efe10a39a4107d1438277c3f16a7e143ee690e0a852c9f740db2b8b4b41540f846d2aef734ce030a51f13f8d59a70aaf71cc7cc12fd395a4db636b5e74c83432c41fe61dd4cf9d03f7e1a35ca11c2e63e06dc48e7699d855b74d87cc50391eb8add9f75130db9c006f564cfac2a2c9360dd4f4707af7998aba1e49e881f5903b7667645bc81f851f138e589d303fbb9cbdc2f8b5fedb9d26ef8ff61fa38446c82a07d11d9f6d7403aecbb153c040dbf359d766fec4fb69487b910064c9d80dbbc212e040ea14ed0e3382b7e1b8ff62702059bdccd159a537da8c15cc3d934ea057d3f57b1815571b5944be7baf52ee28d3107bdb2dd8089a3b393012278996570a17ac785a010220f67ca35abef4256ac6dbb8acd27271fc42c11655cdacd5092607fe9c7ef105079e12fd6c3dfd3374318c9fc2ce1cb9042fde86344e62ea85205e6cf67184c214e2113b10ba0d246b330b65a2eb0eedbd559f30b76b2219d0299886433161e818cfe2068fe1012d9ab865942ba25fc3f5ed0f0c94bd74a189d6d8d8235c29a300a4a2c70b7eccee907343da09fc48aa03434a327bf4acddbad2e7c90f8e12c72e91d56142ba079d2864270121fa800ce229172d6d15b7edeebb006a61f3d93e2b5fd60cf7cee2787e78b919dddf2c69b16e56dd21f1b893af7a7bb42ebdad71279926d9650b95fa6d48d5f846880d83a759ee476ac90e4ab08ae7e9bb7254f72a1f9abedf335b7f3e5c194aff4aba68ffbc852ca3b2c1f9bee4ef135ceb63f5289dbfe20b413366895ec66b87d4718e9446e20eea8e80b4cac7e1b9a7988f4a25cfe80d31fe157ce96bb88b314e9b41a107335b3b433ff463e3aaa6cdb786c82fba795d5e57ea0b79f497a5f6bed28a25cad31f1fec901e10e19df77e7d2c380dcff49f691f6f96120c180db4f6f7e6a59c6e49b9974ab4aed86fc6715af5b70dd6e5cf3bef9e6ed6e8ea8f5657a296e0f30627533f24d73ff70c7a183cae5f00b844d94926af4e01bcd9a890f34c26c8e52f7b43c4d92d060abcfaa444dee2216ac436e277931eda67d179f8aa67c06714344452c5774ac784c30c441b638220ad1975ba226f13a81410750450ed3f4a6a02064578f28b111bf86f26e0b8250d256e1372e7e3b5dfa47445803a509501f547d05fb3a24e74a34af1e7d45df704c57a824adcff7851b4d7d67cdbbade3114f492fb634ec2e088f2783b978e5121515a83b8b4a6950d28c922efd67283140ce93baffb5d15eb72ef544c5c2e531a051ea8ae08f1f87f55fc446f7fe8e4fb25093dbb38bf03a2e399137d541b2504cd4288543c7b1c51cd9f54551d339149340ebae8a52401d0e26ee5341d89a31fa179ce5dfd2a4a9428cd64be345723b717dcd64b2929ad87b243fb7bcb3bb52339cd359dc854082703b9fbc15e87e87f39589a2054bc9828d92ee0b9cd165c13f35b457b0b7f484e3025192770ff1e333014cff16a594775ef17440e92212227553ce62d0ed4fc5417fa3d4863a6ac133aaa1ef8a238493863330d26841be91b6d417967ecd5590fe852fa8c692b2da8a96d836aeb6483c921a5e73bc19366199a1b5c847d7bf11b1c85f29c33fbc6f57aae6d2b799c31b2cf855e0c221ac623b42500d0e4995dc9729189c53e4ad9a8918244d5770d1846b14e13b64826b918f632917e0cf629542ba751f9ce57df94911db2e9d57c43a90a6b0d0b325d3f0be9c1625d2ac88f9915da498f34a6613cba927f2ab56416083992a378bc72aa09af520fcc520a1a8e5991b2a7271249afb3c848d6057ab4950d92579f62f7b5119ac63e6782976db8e9977e3fd5289a9296b70f899070308c729839490b27581ff7d90a4fe1578d2f895108b2c28c1c1b8ec174a37146ff4397c87b0cdf51261b88c198f4bec0b60838a72080f25236b6f6db072b3a4effd09d378c84d82bc703beb7ebf047d1b3f2bf4ece8b5e36284a1cfc0e0b57b862d781cbe696e77e650ebe5eadc8e3bfff1a4daf73bb4dc7ac9d88f49208d4e454f8d52018fb4c69570a3b0972fcb7a44ba524e43d779bcecc8f0f91d2ed80882db28686113fd6cbea26cea97ab08e431c705da6558f9c003d48fb2f9ac5f288c186cd189909aa9b7f0b25cf05b0bd940479d99dbceca591f5e2bdec7636476cf3ca59e3c4f912594200f45c0bd06867a9e8a24df50920f4d827a373be6582fefc1c9d179525908261032ae4d0bc4e51053b9e551927c993e92036b1a23f32832fe5040b9d0765235b03fd56870bdd2ccc031334058a9ecbd853d70df1f1840b45b98848285447b4a413ba58b32a3d3eeec6af4ce9ab146afc6551817d059f8e5612dd6d31ebbe415c23d9ba4b98e0ec420542b8fb7693cde2a6461a996e9b3b4d203bf66515a1af0116671f9de44bfffad700fce97fb29d638d0adf936f689b8c806fb26b26d89fa30c7d6149448ca434faf792c3e6a2a986a3ee36cc881ffb75f14c78d8245084a703beddd3684696503f46572e92f37ed233472a287f601d3751206d83bb17173c22b471f3255ea19fcac979fc8cf5a1c4acdb637b9d06352c8181bcbeef1e8d7c1222ed4147920c465875fbf43993b7673e938add0abca2c077afd4146d36d7890232c17223a3a6dbee9eb4b9c926a9f7972d06a8f4eda222b6a53f055be94f3116380494ecd15f4e289b533c34bb469e60a891e400696090772f3c29c3f4df121d2e1303f08114842b9c87903a7f3bcdead25ef8186dc00e3454c4f452841faf2893737960e1408a394ed081f3330b6eecbf38da082548930573dd0502171a3208a04300a6125ba86df0bae3f730a87bc9c7bfc78369d73ece8d708442b71d2a28ef0dfc69afd35e8d6714ba33c3f3ae54e2177544bd7a6585f6b232422ac44b246ee00aa511a2ed1daacf38ee5dbef3593914ce4bec707b8c5d1fa4d04988192a3802679d17817fa6c801337c7d3e80a1a8799f6cf86e1e54084340af41d4d809ef97be1ed4c7160a9ef5c8c83225828dce83d892a437660843010fb50b61b614ed4e745a80fd6bce6bf44b8fc9390ec64bc7ac5c1edbe74016a00e4dc0b04f0e46fe11d66f805d8b50b24bbe95d066deffe72b8f18b24e785f0507776005741bd95ce935894f12dfe7ccee1906bd6ef4d272d823f5bb92a0229f0f29f95ffd181491eb6278dd5f17ff1fc66f8d4f14aa8b0e833a93f74fe818e32db2dc767c15d5d03ee42965104bbce10bd5d66b871ba2a09f2269cbba8432f52327cc1e2710758e1804ea1fe8377f9a1b6b4ba850efc3004cca5203a7d7c473b88d91e6930b1c7c195ba3c47a130eb5e6354018b8907f00acae6f53702b63b3900f6a184c07d52665a7dd1dd94916dc7ba95cf75c8fb5f07e3ee904beaed3e7a58e6c17611b7048ff802d22dfc068e70bd9ab87a251fa151230592ba0c82bc43eba06b2391aa30bd3454373d556018c830c24afb90ae0d29d4fe1829478826c7052e092a32f27a9d63d5defa8f03d813eb6572fbff32dd85d8987a76d31daba9faf2cbed424bf6c47fbdf816450039199a834ec8989e7da9bd6bc285721d08af2de1767a11434eb257ebbdc034b7565e0cc0ceaff92f19dbf1431f32000b178409512a2ae24f4cbee8adafb602f38aba2f7a0344c7135fde5f898a69e622f0243ae7f543c1637128680ef50cb07eabb8bd5f303c697a150f41cccf2fbce4cc643ebaf65cf1a3d199248db86f69b82ab604c6866c26a4268f1154a8f94ec3ceea3531bb5fb1ac951dfa99496f21cec96baa359c2c08da23765578d5e585d527e6bc043d3da29612378f918674b1ef552973770f0c674d02dbb4f63a8f5cc8f896ba59e3f900c077ed8f8f6ac6083501296dba428a1180e1ceb6ead1e4fb31bb4fe70079d720d4dbb22e823d9167e12fc831d7092d1397f30a1366d5cc8731ffac2e0db9289080a91eafdcf29d90fb797f81d785dbf8e2481de57829cb2f9d17bb894e048e7051f449250b7c31255d056189f6c90090b81be3570d7c71ae78cb0f5169d6f142f73130d9283d0551adb3c268d1e169333b5864a226b7b4f86f7e24c44a991592304ff28559b195f8b32dcb97d1dcaaffbd16fbccd854c568e3e420fad1a486e0caa99c46aacad0268870424959687352b688151632a5dd7a5efe11f95b645fd1df236d479ffb918af56dcea8fd0781c37fdc3f7cf0092b7ca962ba71110aed51249b400935c39ee2fc263a7d70bbe850db6c570ce8aa7a7a25978169b7e2fff2a94971885cfb3433f62b6f6af4c3c129461b5613d2e12cca0772cc52fefeb4a1a54283b497d1d1e4fe8cc24b6f6cfb897077f1ecb105e53f2e0df0295540b1d24be8120438db8243c68d26e2bdfef3da4f1369d8dbf7866ce3169772fcaf62317bdead50b3feaf0b55b2163f8e59c60c40c5f1bf659534219fcec999193055020bb3a4687b4daf25f77d9c02846053c7458442a26410638a42baee5158416e00b240ee91f5c3805f2c500ac2231bd4b896c030f4e9d5f33430d3340736cfd9108a10e9b4dc893a57a260421736c988dc975dcde25675f1f5afee819576d1aaa04a4b84cf90de3d13757e48c22726470ca958b919db4296a41518e7ce62e7a7a208b17c527b8a6d1b068ee2f4858d83c6a748110b20fffdbe44916f3871cc6d20aea3df516f83318de00669e8124e47d2d05fad743b8a9d8f0cfcddce8355c8217a7e599be1bbc649224b1a9686f8f08dd6a333966f9d67841080a8285d912d51d0cd7a48d3d797f1d1da9d14b3af4d569fcce49ce0a08f29ab27e78df5775bd0da3985654618b91578ca1c44e9118e97fdfde9a44c2c6a01dd033e1afd9502613f050bb5f40cfb1ed125552ae4bcbb02cef49dcbd8d2ed1df54c12489007935023d9bd5bfb68106a5172232ac2f0adf0b8d46bebd8c319b1f293ec6ea0dcf2a1bcfd340f0a46e5d791d7d5c947b7d7606747d163c56fdd7dc1e5713b8a720c802f68cfec8f3426146da621b15d9183024fc5dfc6036ecad7d337ef4aec8de650465136cafb39b8b1d9e4adfa84f973c3e7c74aa3e42ce1c90c3e216cc26bdc80d2623a78eda113305e72f2f0723d87ea6444f1f62b0edb667eb67d5c8d43f1129c6db6410d513e3c04be3a3015c2f50235937731c558e0bed04877bd7d990ab2c551e977d18eb1d792ea80e561018cccbedf3c84f79e149cee13fe421a5c0af2339f442a974492692c512d4736fbda6926722de7d821cf2d141e4cbb17e516e13f2f2201c7f219c8958fe2fcdff5f4e5c4da2b19729e480820303c8359b4c1f16de9e6612a1230f882a6588c81e1d9d1ec50c15e2fd56eb57df3352d7e9c97b2c3cdfc11b2b771ca6da29f5dd9952868f39c8171074260fd700ef04dc5541521f36e6f861833c85d1199dc6efe365b8415583b002816cdd257b59fe2b2813bd0675f26629c1451b66772d23c1264d941b003abf41a3b7ec72799e08bd23c93326d02bf901b3314b130b48d5697d7ee7b21022dceb0de7a693a33e43c10f77d624b3e57dc0513d9ab7dda81c2b5230e9a9797ed9df075b1a9a04388b75e909bf13fc6a8e0963a6d09fe6feab2ca94fad001b3f2303b76b0da8953a6f7b864f4a10e5d487df213f00453d3b41de3f2af983ebae984c50722e4070dc76cee5e0ce2dd35f69ab5162d3cfa3ca47416420d1a6ae5736872c95f3cf4bd758b053abce06a3f3d27234d5351d377d19dbc31128c73104719af176fda95db7204fd783992e8f4d59931736663a97d75ca9fe5601fd6a89a0aac7967616304106a12073271eef8a88a6e435eed5c639b63aba53bdc40b039e0fc0c93d610cd5d8e66d17471ac366ff4f4c0a1b6cd7f7ea41e2357e1d43ca2d5b0ea75f120369fb9c8a21351872b158b652d6c6e15cc381c306c1013b6e1102bd43e75161742415d7c65456135e076db98d1f67573f1cd2d3937af97a66e2920f026cb6af506b49054bf5cd1bbd6f7cd5f654ba5454ea1f6afa635b9836ae0cdc637721c23fec7e36fc5b181cf56fd52dac7d9f1058fe069388c8d8f4aca742c881e2ade54160b68a6b36e51e973fdcfd0a8bc1a8c72682743423f4270dda04a6be272a1646a15eeee9e9960019a34eeae05129cd8c9b0d12ad5b3a1b4910ab269cea75a18cdc6602c90a3c2b0a7c79b306c76638f2f2eb54cbc41109fb4780479e8dd882dfa563e655b300177732817cde20e4dcfbe62477f7bd375d66ffeccb335787ab4069c3ea492fc80b24f697b3c496b0e39b5bf7a1e985673afc24a32ad2aec7045991e3a80acd80181dd6b84aefedc94d484f80faf20e43cc740c40eaf29a2eb6b459cb834db07912804bffbcad366f36d02398ff25b1b7d10e1eac43dffdc071bb0135bacb68ae0be269fd54ecaf04d6fc6e8ae3865d1a6723ca38e1a837e79529b1c53f8652c417a5f356703cc7a7728e38472edf6d2a7322eac5e1ea44360d99380c7f1ea79f067ece250223c72cee699b995d9e03c7e4fa7559f7fe31342cca0ce2cd31586311c664bacd8676d116691e2111774761f13bc228eb8c5246f2496c79a08e275608560bcc059db58b4fd957831e60ef939f1785f95e96e574f211781f6500937b02f0b4f9e89a0ac6ddb388fdb8bef0bfee2a1fa8b55030be4064653058876cec1bb0d419372104957e3826fce12a86acab2edb95f0466e8d974f72951a5850b42c417fb7da12f369945299761215b461b853087e5c72e543a420fbe81e08be59045685818d5d7b478095d711c6eb686827f6e20ccf3e1e378e5b1ca7df6324081b9c26e478a35ac337c3d835ec68e0e328fb30f2c608620d4c0896e0e57aa0180b17223b0607ee9e21a031712eacb1e784c4fd08540014cf3fc8d19726feca721e365bb366388683526e599a294927bc546a4d5011da30c7b2fab02efbdb18ab035ad5bbe7bfadd53b0885c220a897d3d8c2cb428e38273d250245cf1b65e0c81b39b2f0ecf38a82930cac7d2400315f74b76e918f5914155e9d2a59cc9cb4f107599dcba86033fa5b95e6a78e083a46613528b12d9df4156903cf53deefc83d00211d70b9cb5f95a01fda9d023008e546e99e3666b50e92e252bd5a24127fd3186841ec436a72ad2364426aa74bfd2283fe3478f06feced6398b87f34378fe81b599968d8a2284d9e7c9d90c75a2690a0b9654e2fa9d0a33fbab902a53da9160119d39eff9c63f7877567d4fff228def31cb88f95fcb9e40e7a5843472c2f231b185edcd6ca67c84bc81efbcce3e10a043706c74c42ff30e668e94b53f12eb70615289857ab1eef98b079d754e9f66c61ec68d56dd23b9bc806c6710357a1d92ee58e02b874474f90a619c06241a5ba212e60a6110e7f09bd3c4602f2e9c47e2a11dc54d9f8ab979bfb3371f85b99bf6c051a2f5fa64579f31538f959a56ad5099d741e40b4c985eca2a2fea2e28980c9056f07465c983e9fa28d34e104f13015cae6067451d4c5d18715e725ab97558b7f71519c013c91f4f00ae1b94e044a248647b6d63e0ff1ad866423770acf0264ba1bae38ba54540bfda09b51bea8c426b26292e241b03f8e86e62cbb982df0590a043c213b648e3e2de09c43af183918b224b74f1449fadf68e31b87fa1e3b8874ad883fa3f57958bf5580f487e35424d96689668d012f282397cb8470e003681f6327131c6dc5abc41ba6795657950729b0c57a17a548d7e59c09ab4d0af29f3986683963a4af08bde254ecee7d7e24f4eb820d5e11e1284ef470949a5caa0d59884b2e059cebcf42aac0ec87a4c2009982f7f2fcb6c54324d1ed3f1f2d20335d2c79e56669a45e0bf2094e53dfff22ee388b1d1874eb6261be6f441d1b61aabb25e280c3b1cca1210067228a29a52d5c10663d69b3740753607c6b92e4add07af47ad92c467bd5f150797aea8ad909f1ad4d166d9525968cba944c3a3b5df5dd3ece0882f273929ac009910aa4c029fb76bf4360b153fc4129ad496296b093fd7895225bd8c38ca9e6a13fd90f21115553ba51127185ab32cacef875f5d9485ff9d05d0462a440cdc5dc56543a210874b509ba6e118ad0ccc3e6f27f79aa521ee68c844cc89ffaf40c2c4923112404acc8365bddc685de4e41eeed9a989119f61727e61855007baa4f6d5aa47a6f069c7504953acaf19ebb02ca658da58c38e3eea0c710d1670dfd1586746f4bcc066990a6fe3f5c7b4f942179bb7f6cb3d0eff2841108b5f9fb6f0aa3dc297c750fbc9a532e247d09966e8fc9c3c08d3b93cda4d7da15ccb2c3d55aafaef7f6e720657e3b9c9a7aa110bfc42043b8e382020744ee0a38ffcbdcb176dace99c5782118e3412549d1e50c110eee24cf8a7ad5668fb5b059365b164cc6e2f9c51e7d17d578e1f940873003a4772cafd0e5e0fe61b2b818ef183d3c8442d4365671a32b2d023a91972ce6168a6cf4d6a9438a553c6dd4be9780dc514828202de442e926d2735a11b5e30445898962a6612abff243b02ecef4c52788f64ed2eec6dde32063b15d6e7b170e5159d78e57dd0649b116bdc9b3e1cfb2a5a713a546f7869000722c25bcfb1beac0e535fa3277f0120043a101b8116f60e8572a6325ac71bb159a939845b53d191316abbfd5228668258281c52411281cbdad0fb98efe18a81647a67703fc3259981967efe0e3dfb88f6e8de37db781960d5ad649b9cacea60c0daa9256ebac451ee59c50c337f68f1ce7c6c0b0d90fcfa20cb9ff0478be6a03e4bb40668370d05fb1a940ae15df5cb9c128c6daae54b73a0846e8a45a3bd31b6218aa542c305cc3ef949c30fd8234dd18cc5f927882215ab9d6d748b4518f04afa5d7e4c164916d9313b76d88b5f2d01130f40bdb3930412e44603c16bfa02751cab86e87f5a912d21a6df597b3f5cf207cbff7943cd983e7eedd529164f91fef3f5f440261cb789a9ca6899fe66a2b6f3eef2f821807c9abc02744a0957d1e2409b845583184de73db7dc7d896d609ef37de8b523289390b38b38f687f10026ec8d00353098475c6ded11fd4de5228422528eb710995599e5f284519ca7c29709736fe6a73c37092fc83a5737682a11aae30dfb0a6bf4791bcc7f63530341aa1b36a58ea685c0bc4aeb065dc63a50f54f0536a7364be2181f13c0b4d8cf607f474033d183eba62d93b7451932b6cfa092cc03c347474bf4906e0c8ddcbee13a7d725baf5ac8703784f84aeb7dd994e776a17c921b5d4dffaf56d4492fde123f2b26aa4213ed6ee5847ba93f0d779a0e33e9e76e9759d6dad50d74b225f8e0803b5466ae26cf2977d03370661ef5073a7258019a875f67b03e6dd5fd9cc512084a695aad7902800ceb7aaf22dabc4d9dc4479be57248513a57e0518b1223f1b313fd82807e35e2259298ba409c83ce0424f3639a13f759de733741acad8a10660a955e0ce6e2492dfe14637fea0770e02533eaadddd05d4d99a04f9620f4e106934ef962dce3a6d408321df0a65eb716ee892d98d3e1d6a347139e21b0ae81cc8a047185f77036988b57e5b11f79c73a9913aaa924c10318fcae1b8157a73162a5c5beee053165d8b1d1e2a63c28bd65a2a2384afeced0f75f59b2161275528e889bfd1f21d0268b13da9c623cc7ef6b36e3887cd85d9ff79f17f9e95bba02c644f4a592314c6e20543f17f0e5851cb56c4d8fc3494bc1db19de3eb9b7e27121ce1d4d7d89669e5805c2bce49735e67c217a479f00a31793c09b072cc379d00c4c5745c7492cd691c3b5a9217156aa14ba97c0143719d9437ed682fe6695a", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e4411dfdc7cb6265f100524012f038ee1f205bf8789b70b46c57463dedb4998f7ac800000000000000000000000000000000a81b7087e1431ea26777fc20238aa980000000000000000000000000000000001991a803e262c7a38f41ff63f803b3c21806572c19ee2f3532e970d617919b3aa8cdc582782dfad0699badc631f75128000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000021301f6e7fbbb73aa8fa2046f5a9854e60b7c72f0b56d2cdef4e3cb5e3d1a660b0827372d8ac27abb349e731af617d334968a1a677756357ab1b3eb22654a72340ed445f4ba59e36bb04bedc22d61894e7f47177273eb6efce052396d70477f050dd6ae6474cac3235c0eb911e38027c416320fdca3d180736d3a3359939e922b000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x000000000000000000000000000000000000000000000005f2218f60b38de08f000000000000000000000000000000000000000000000009ff735fb0aa8f9c9100000000000000000000000000000000000000000000000345271c8cf4b33f4200000000000000000000000000000000000000000000000000020cc2ccb0006f00000000000000000000000000000000000000000000000b5fa1bf66dd6f3016000000000000000000000000000000000000000000000002901de59263b00224000000000000000000000000000000000000000000000006472b7847e3356bff000000000000000000000000000000000000000000000000000249bd9812aa03000000000000000000000000000000000000000000000000714c4b255202de8500000000000000000000000000000000000000000000000d6956453c3db805b5000000000000000000000000000000000000000000000008f18928794975bb8e00000000000000000000000000000000000000000000000000001df754c51e1800000000000000000000000000000000000000000000000cb395274660890a6e0000000000000000000000000000000000000000000000095e68a8b2d4e989f30000000000000000000000000000000000000000000000059805d645f9983726000000000000000000000000000000000000000000000000000158446880ad940d702526d3c16cc3d8612ec9f5ab1a62327fb7f070635171fed1cfab9fb02bc9221c0dfc0f55b09832b6a84878afc2e21c87d4e1d8a87eb424e5d8925c210d9e07172412ad021afa04978190bde455cc48f3b13c12dfe3586d8bacbc33d21dad0201c1d29dd464380aac0a28c4680a60cedd2ca628880dfe1ef15850c97398d3140980f66808c0b14c9e4453eea5445abd16b1105b2bcda4949456ab3101e1222a0312603a4e6f2df28b6629f50f3b75d1b41d5cb948d0c9c403115dbc717e6a03654fefa6377d61e9d29fb073093070787cfb7a4b0b3ec30cde26e163850eda076820f3362d05ed4c34ea9517cbe7350009778810c5299f8b295c908516238c26ce5af7c7c1357caeddbcbcda38130e6cd3241e5e8dfa681f275fefc06783a82ecfdd8852acf679dda466a5ededc1e1de0c75830e71080072b3ef5a39ee8bda20784c461bf4fe943023c69c4a8a80e2a45fc44558452e6215ae47e0ead41a120153167359c0cbcda774be43b125d8a11ace1e56095c79fc32dea956b0e39cee06776a162a29aa02abd8ec6223718d3cd1685078bf3a800d2cb3126260f1e7d00f043fa160234a730fb38797f9a4bc6a87606b469ce5b45c6203d1a4873f38ed146ad349349a9aee02d748d21e7aa1691a8fbedbb127b6a3c77112ee4c0831792c74c847f1c0bc4a6efd16490824b8bb8475f1da1c43d73507e70c149a6319bf197b6f3b7edfe72e0254c8d6f9e925ec1c7a6af14ee97edf125602814ac63aa12247c599da6e712ca5f13212f3f0f2512533375ffd08e193bdf38c9f3ba9dc1a268007e54508b8bccd9f74ede0bf36271627ae75e66e056d189daafbf180042a02b265b492124237a2279d5fc005d099e9bfcc664e31136a23d11bfeee427b430857f0bd419d1e39868f050901beee9e76bee39cf93028da313e3bd1197455f92761af6603b9e68f84531acd38e0664db3970295614bce1e0619bcb180e855510f17ab92678e8d58889b7fd40c25e0a0b172fc4419dcecfa70a776d4a768c0ad24c227ed361f0900d0234be6e041aa3dee023df77131ca5c4f20b76a4c7de061293966e5cc7ea91bf71035b5321e43dbf366baeaf2123d239bf9518dfd4206d1061fd78b5b3cd10843e60b7ad93fe83fbfe85acedf777898443d8f006381cef4180633a4774a6ff9315c59d52a3e82cddfd313d2a65756cbdae9e45c0c5aa6a9254275ce78b21d39dde33173eabfbc681c81df53e5e1937291c82bc55381f7081429ea0e9bff4f359936f343650063ae80cfb205fe4d5505df14d585a2590dce1a6d87a45ace520e5fb37826104e159a7736f3a575eabd42df5857197e4aee180a47b5de368d8fb78de2739753da476ec06a97496dc3049bed74a1fc64bc1e122b5ac08a4694f869833400a9d13c14d62c2b582ef67e076b00a2b48da7c815582aef0b34c5a021d578b1956454fcff047ea232b5b4a02d4f0502c2a4cfd8cd0117c50a71e2db4eaf6a2b74a17ae47c94998ceb3fcd05c73c96a9add350b9077c2dd809e930647b467466f47d983ed8fbdf43a32f6e72c51a691297d20e12848605e09ff184294c3a8491909664881831d337eb81a349702910c11deb4bff99f225f8fdcc9211e9769375fb705925b84428730641554585eb5487c4da3ea9e7440ff3d3abcce49866d314ab0f7705907bf6c500684cab7ab9882ff7e8612a49081e5b363f5e3d6ab435ab1350d25eb7d9f97aa4dfd07fada88fd07f36034efb560c4b0d8820249cce16695d2d94f4bebf8053c7e493b265bc22f8c764a65183682b513a86d2639c1e5aa8e0340beedf6471f141e1b8a8e8d45dfaa926c5c8987e0b9f188a0ea9f8564827981949854017fd42a8619d07cbc0464824f0aea6ba9206f394b9d5260fd699e5832bbe1178cb8e62874c98877fd4b04b1eb0e513aa3b1ddc09310a5fcad3c50f2bad9d5f3c9156f2ca0b4e03b79e1d10a02ccd0870c50ef282b1512d4196513873b063130f7788df1f282c27369f1151ad40f582d6bb16a2b94a4760b80c6bb55b8e4ad2ee4e9a8440d102c868fe6467cad57729eafa0cc616327c0573043e9fbbbb0684122dfb7d20b3d550b2ef4f17e51cbef1fdc614911e2a36c2c17746f37b01985426360e2a9013f6092e5127bbec6272823eaa20afbd5216124ea1fc198cfa2df5aa0e074fef76c0ff35ca8adb52e62ef0fc8c0f9c913552812247ff546ca7d7da8ceff4eff4fd5d898430dd301db1d1902439115723bb71edf060834ec4572f7d4ea861f05cf32312ad5ad55479c704d1921d2c5ab9a7c079748a6b1d7e37369f5a28a83d626022b64045f3fa0b80a453c8041de379d99bdb0b99ccc892f31f80f17da2ed36f0090f5e3094dc1173a711413d254967f32a7e7371911776461ed97ec330d99eff86e25d631826fd520c5eaf220fab467cb09a2a52ffd6c903093d955be3eac414314e49c350d4f3dd15751083231ccd6e2fe779f5d3e5f4b0b59a392e24926f833f88e7f62ff4eda6029e3d1a1f150584b58914fa39228082ac5aee53631c5cc9364383b11218685904dfdad61513bfcca8dc59f74f60ebdc9a36b03c8730930b07820f261bb91cb31ead29b20028e9d48990a5d50e1218e689bc2f934d607196679ee0e7546ffe91c745fbc6083f23bc710245cc9fe362adc0c75a854df1d66a93626ebff3bb2264ea1dffa416a238163b0bae28ae59785d275a8ec59c8464fd75d992557f019378a960f63425e05ca4c3eab58bf0a866ef3d5eea6187f3809c65b94d959735d16eea6de28f1432bb4c8abfb4e9815b4b119b72103e50fc65878db8b1541e4c4c5313f4135225a29215c4cfd502ae3f5942f8d8fb7baa7dff07e365df4f29a6c256ee9170b8205595a47239d5f88aae9de6162ae1332003e7369697be82f2bf955f75ec3a642dd57fd752005bddeed11ed896ee5757d17d5ede4b573c0505f824875179e2eb112a2df36064b8fb9fcaf2290c9ab7a842c64eca3a11593bc346ece99a9b81612a99a5ca0578388e8b8a5e225e409fee6c296931887b26ecbdcd8b8b0c14c19c190dbca881fcde82b43a7b4305b23aeba9b9e903c4ea6be43f03b4333ae4f93109a8bb1ca28457d9326292a6eb1434fe54d0b315fe833dbfcbfc4558fa06ba3f28b7927e73451b4e5ec1be2e14fdfaa723fca09972b11f2f5be411299c23581805a2158f107d8629f024375dac5b41e77e04d31e4a47612a787093240388f5d92673446e08803ec91d7f3e4da3357254fbb85dd613f9c26242f7798e30e8200b2608f77deb7d1c5e2719a0e6705770a407edd4235ee26e860b6c69dc80de486d15a53cfeddcb6f4e890cf26cb916e1f8650e625282d29c03cdab859516c2cae42972342f359f017db84be110eafddce43c189c6516b9150d92db8ff5bceeb4690c86cb5f2bc09a99e6c0cf0e99a69d944f8d3e6df230e70682babdeaa8a1bfc51f7e87399eb0ffb4aa1370c9b4b01ce54403b342c86f1c81e84b324978c9f7bd0668d302a579e82d9a658e81051fd4591b84dfb05d80134d7bd7761b92fda83330459190247c87b440ee6159e47fddebdbc2dc7d1f1fd6ccfe8160fbe82e57f2254da1c224f027a42089646f8fdbeb750207ad222a686bb649f69b727472b1080b0f3bfd31d30bfa6a78029f4dc098c71e472f42bca745cac5fbe192cea70bca1b45cd711f3ee3d3e825a302e399b5e09c862f09eefc3f07870a76b6072c39a809f4bd5560a0ceac9974743df4ec488b876c361d2655f2f3be902d609a100bbc2a77de2397223bce6ad358b03a57e0008b6e4c66febc866021e96c1bc483346a2d8114b596089d6f34016fab7654e77bd2fe6513d714f2924feb52a23156fd5212b6f445e42edb6ceaee423178aea2c45303549206b7b20ffb1bdb3191bd1e0428aee16ab04321ad1eacad7fdc2a8107d9412ab7dd50c7bc342bc045ce4001d505fa508e8f1529c4b17f163a1a77ea740811b15d5962ebe95c5a58ba1632ab352508c28edf99f4f8fb8650172223044709382a1a628a5189c23429696fed90811131b70e7049cbb1b4eafaa0a0ee99a000701f664c4cc4d392eda23359b3ccb421668b16ac00d2d74bb218eca49ae37c13e7e410cb8802baf5b37e085b02e3c10c7243af66e5be7e5180c1c921d1f7b87eafb8e66ef466ce481267df735d131e1d91359213059a47b599f89465d236664752d5ce6324269682b058709509946d184bdf3fe799e092031f4b0a6c1760cbbbb22c3caea02608e3170d378d510939231c3d509aaaa15ccb62ecb814131554e86e180a1cb709629ee5f86ab7d1a7ef2ac08257bdc936b4c829a95b666842778b0e3ea6dc4fe61de92e3d60c01928d92329953245ba406eb19202d54187088faf11c520cd1b273eb8b4fba91c48e70a26b015620762d22a01b104d48d69af46a14a10563597eed134f4b677402d19a028e44273662134c99569c498c0335d13a56f2269591840c47ff19b696c29e28d1d09be778f26af8cc6618eef42357c66d8d65b70d27b96db438e9cbfc28ea81f048f34a4a86158d66b2bf3141249863b1913e42915257f58cf9e9382df14323918b1365e49651ef5f9627a5491b8c00d5e2d2358aa4e771ace9e80a3a4b8e8cf15e114d2d41a95389af530acf1d847969369eb1e2c0f1ae3a93b31e5a63ac12807f0352bd512f4cf44fb30705544d652dfb4bc0589b55fba34a5b3177e3f0e4711869591361d28350d28a386904e44483a9e7153efe49a91d8574911ab67817418a76b1f1c3af09dc74369bf98d0126b5b72c322e4e9f975571b454f1ac1affe25d77532f242cdcbf87ad7b6f75583e876e3f239f912aec3c63d1fcb79238c8604b446fdc64a7b4e11f1d6d25abe20d28412ff685c3d54cc58be9aab29e0d8ae24fc42eb8356a7aed4583e5b0a6b61fb0bd76940483edf38e2bdfd4b43c8967002541b2214ea50989e6ff552b209b7b981cae83d1c6b4bbccd740b281732f841054f3e37c95d278960ee3b512b9d6df72bf52e39dd2c6053d2a6eab3778a821d2de6d16fbc8fec9b4e72bb1f9649feebfd33f12f7428861730b7308e96f4ceab2786475c31def948d54302b7494590bdace0f17b3af521eda992d12be4bf376f23608fc6c9016c7754cb9db557c1ef412fbe7c4672c3019cbdca5213172e629a143a3bd131c75d9ed1f218c18b071151c55ad4a7fe1297bdc9c0e8475b16261604d6900adabb68ee8c2b675fa0030cae02140db32d550031cfbe71e2b0d0d053142d26c3d1474c5c0bf2944f063ef39db46d97147915384a02faecff0d991c9b1ccdb1a2f686701ccfc769f3779ea65716e8697026565295dea7000086f132fa300186285919872ecda05c52328ccf678f55550f80d341447aa5a8c7476c084205a966938acbc7090ec9dbe2510b521797ff0e735d62f8d2f69a530e525c2f092d556062a2532259b37339d43bb56f978e1dd9695686a6ca684a88c09a3012fe1904fde312e2ca2983e85a98d4501967703e6be8afa5ac688c215176d4b4211a017bcdcd5e8d3543a47d996d3c443e843563be932f5df4287e0afc2ce7ddd6af2fae1939da0b097037b0cf342ce82886a692c1ac6e175ad52255b073e0e53ca41c0c9795d1955ff973d53953d547287f5c7ed679740f92ef125ee6c266725d1d00dad30b9f51fc0e34ccaa2ebb0c24e0d6e7812496e93a59d721e4e15f58df8b2ca8fbbbf2e859cdfc65626466daf4ac1b4e8b8812d9ad394e76317fc32e80c8231e67a2fc01a463d65f834d8e2b249ca20effeca6faa42e0566ca584852795723fe9e57ee8b9e60226828bca17559187fe5b0b272f3ed27ff976c41c1d75478165c6ca9ba102acd9cb16ad8d2ea15a86bd43173c3b797a4640167ecc00c224629e20ece2a01712bdc28305307814afb4395b3516a4611da52499b55580a75aa295e4c0865faabf64086bd3623cb777ee2150522663a97411efd1df30ef7c3ab0e6789c0ebc98d74111eb87bf03ff552d543c3992919d9bf200a7d607f7a183a1e14f39623c6236fcb92f4113b775cf602ce79e2819f53302eb8df834cf9535a208ad91bd95e0391f13f30766a9098d450e9c68b8ad9ccc6b3a2adfeff9d9ab702decd05673c364223210fdc4bda9da2b7ce304e46e14a434f3928adce09db7b155443d08b6369d4f776e8326d85ce0023c53ee99645bd0de3feba7f6bb0bc5f09e1299777f3f9b91d644ca3c0ece8af1c5a16e702f02f0a9b750fd3669d019f2f30d8fe4ca82ce52589f5e6476b8b2e890dfc540b186b9747c67faaf572fdbc03378b2102dc41b05ef7727d47325d41c1757890765909b4dc876e46bb4006a20e5033b298e88877c536884c4394615a360bb6421aab40785222ac9886f03cd004bf5c8c38b5d73508373514ce5aaf114a697c6d842f19ec29fb64c28079c8dd207fb47f9671f1594ac148ee9379593a547ef44440af53c3c72822bddbbdfeb303e9686224df7e15c6c25b68e9c87443720c48a9776f59a37a921c306755785b1d934d657e3b458af03e52d6432839d23d419819c48a1659812a713c6e6d374b1cd41e1e41d4f9b51c690797d75eb854b1092f9db0a1a1a599e51f9b306866690c8426778c18e862614404df26178818d703e8358fac2b0fbfd66dabd435a21e02aac9cc0b3f09bd63efdc21dd3e8af6694c60c576c24a7ee7d66c627e9b21d024196e8496697dbe7e87632a5aa74f50c10eed5647de1dd1af2ea9310b26778c12c2717573bc23a151b9abf368b92a94979ae4f243591064f910899e073443c2085c4a37df56bc6990a3d7b1235634378b48f3341539be63e5d971e32679df6d11e6e2940964b11270df8126a038904c252f833e80cac32c9aba80c7c9bc0ac81ac49ef8ff070c273c7704af86a8dca8b04b3584cfdadf7781ee88fc18bbc4480f4b009a21c410c66faa1fe2ead1a6fab5fcf38330d5db3f8b1a4a901113994d07d3f2230336480513e55c9d069f1be764c9b5ff9028836f3116a4861ab804f21515ef308cd69326ad5f7fd1d2ff36bcd49d5cf5874d6dc6883fe0c59831b55708a25788e6f174d4de1db5879954b0cbcd422a42097409368e25cefd72d31c001e14d1458d2c1169a3e151c353dc1f5bc6b548343fc6286783ee2e4fbd96a9031a343bc8eceb04dad8067ab40a4f684853bc85ac5b306740e01140681b06cf1b1e61abd86785541da2124accb154bd4eda020ee978f3966265b461c9cf982195003e3daa4a4d9bc429ee6e499ef0f48732dbdf7e399b47749d612f25b95ff6731bf3cd822b4f61cfa33e5f2302c9177312f1793b8f251152772840ed960061bf0cc119c935e7f43d9393d0bcd9caa842ac41b76b1e3229af0baa4b60a62e8b86286ead8f1c7e94d681e44539c07922c4b055d4e8d7c47631ad995ac843bcbc2124582425774111e835906992ded16883ebaccaa00bd6ab936a13f4002a422e901295999ddcba799c139229547fbee76cea0a2a8988c56a343948140522ebd8bd243b67d1744dcca9d291b4c8cb0b15959b0683a8e089ad6a06e1f7430fe78a6a16515953fa46705833f0d193d0e40212ccb99176bfd70bae77fd2e1b8a95d4f51b2cdcfec53d499fe6dcf5d770d861e869c3cc629b4a8dd3c1dc66a3909eebeb273e0301663c822f28fe85fc8e0cac9a984c7fc007b523a5bb9ec6b36b7c58db1769c76f2f278d5dd6cc536396dca2f025945fd2fa27bf3ecc3df35cbd0828d92db58ecc09d8ceaf1550a40beca9b8a1ddc3dc8397ac9083f0029acc09abd92e0e7c56130a68acaecbbcf213d245937c5cf6fbff8a830134d2e1b268aebccce50b58026622cfba1874a4c28c780f1b60dfd8a100a273d6d7de8262470bb3dfc102509cbbc2ffb76105ca5ff0aa91b65050bd95d9fa193180cbe422e5c18c4bc60fd048426c01b8e7713474b8ce57bd061a6b689fb5bb4626611a6b1aa034694e1c9b3c0c54bb1d69d2dcdef1e9c68f32e38c0cbd89fd5ae33fe136f007fd814f2dc9a60223e64cf7c53e7ae3091b1174c6044e18627cdbbd1c244291cb0646571197786b5893ca5e0679b406bb3d223e468be866a903d61e4de8306f234d041e1a3fa3f5c6eb6aeaab0e88670869698d39e54b3938d06239d74d3c0696879faa1d7646c5f7d5f8784c4b417f9a0f1b6e82c669bfd82874d243f53a0d6c0f761e0bed80e16fc05004946c36fadf1e52bbe354f18f77d936844f8f2837a1dd3066136a228e4d43106f27e0e45ab26d80b9947ef8bbb60b41ac5972a82976c434d503be0e2c00b272bd3aae437edd3e11e8fdd1e1c0906a094be91abf7c92f0baab298cf43fb4db839230ec08eb5368064bd035fda059591b67492ef9effe6121611abbfc7951b7ec1c5edc32c15ddedb962220c97de666e4d659447ec99f39f5201b4ffb8705b824da6bbe18abfc15728f601137ec5b5a1c8d84e8a630446ea7f01c7cad0b9ec04d57104aed1dfd7d78649d823c029eac487a22e8b8db0abef130041b72cf9e4118730c7a37a9802a938aa8e85a16af1e47fbfcef0ccd006aa28129c8cdf677782a58d2b1b178fbf7d1097132f83604e733f6e92ee4ac1f169ece0f08900bc68506d1dd45af9cb1b4136e7912943ea1ca10a735916a98d2a9b17125d1c4d20710b106466203e23e4771769398f777821669a63f9c796cba9912c42fefa3f2c2d343c5b1a189d20de8500f7d3bf8f41c2cafc0cfea257b1d5e0eb70b5eb000e0079fe8e2f94719a9fb6003bfc459fc1aeae399e35954099dfe02690dc9cd7166077a5609146ff55692468ba0581d7f423375db9b58bb59482956e41d0276059c01ae32dc4524104fd547391b1d3da552f400e5695a22bff739bb8a0bf5efdda54933a478f07f734d38dc0e6dfa9fb3f1114e1b4636025dcb52ea6d0082189f57a0305a9bd6bb8d58f85a3ea14b47619153b83189603cb3670791f423a25fc40f9dbc36f9cf6775666eebb8b799c3fc1ec9d530e204fc0a219caeee2914d732dfaf6d90cfa7dbe7c6004d48878ab72c1fefc42970edd609580dac1208464bca46fe66ae3a6e48e44b1ceec1eb0899252aa0472fb01ce9a7d2c9dac511d0aa98a4741b2c03ddce57168f635c9bba0bbfaec05baf26d6a9761a0874b506a1ce1b360af99a58700c1917728c40d4fad6fc7f114238f8e7eca23cc5710923667ce035cc236b421452b6b3e10b41280607f9e2e1cf4b3a0b0cb92de09dd52f9fc09dcc7a9f83b768ff435e724877b8b1047845c644cd427daf2ece6cba802a95a9734ccc369520a3c7d14b47ace1b7ec24b8c1ae07231260dea7758f69a7126144215fd22666e023acab0506da17f1b41b5db4e7aa1e292cff504ba888bc11e08b7299e8cbfa12c25ab95f010be24fd5faa516eb6e663e03e88b958e110a240f1b0a58392051cd475c8d5283582a7a895f88b1e397847082e8400896854e2b174b18eccc0cccfb21fcdbf9c80dfefe1eb43d95a177ed0e90489babd9f203009cb3b55e2d801ac8cf0092fbadeda034035b9b9fae8a82f25275cd9f98fb8e2cf1c87829262f51f453590d5ab815519bc814eeaefdc63b3c038980685ca3ac0e16597a6aeeb6070a014111d76a9dcfec1722b55924609292fa01efb87b2410137d3ef5057949859cc0d0845d38b477f143329fb7f4e17a3672aa2e3e0e553820767739734a4af349051f158325726f05794d64486cc9e3eaf86278956533360bf3899a41f64ff6343e374c95afb448eadad6291d3aae8b9ae35fd90bdf03c026d05a806dee663e1e2b98709a8db8217f7b81f3bfa08a2baf999714a3a9cff0284accea238939b2d128aabc13d77755b72d4546ca1f10fa41b84799718cbddc1059173d7e82d096978f15b4fdd6ec5feba5ee8ba308b650bed6ba8ed98ac69620c914d34a54459427d4ede64a8ac5713d516a9d2c5a3bc9508a0563aee0aa67153990a7c9c56ab31a18ce9f14729eb5840f5dafab6cac449e091d825f0d2d221cbf22723745bf8a3d21f1f063678d1d4f2ccf232c4b9ef2b210a3a87e688b7f14b0127356470742d89e20fbec471bffd389d525018175214bbf8a0ed3985d590707143a50587e7766c6eec90202aa7abe19e628f5524b210d341561da277e45280cec55e758691b77f0d677da529ca66bcd41b91f0b363d77da552d94852e7e244eae0c3eefb1d2dc0d97f4c4262bdb4cae9e618baee661ba6f639b0a26742b238f1da780cb61bd26c3523443f794d5fd138c9d8d99c0dbb49b03a934acabfa01d82b2a0c340a58119733c53c95e45d14d19819ae00303ca8ecf01e893e9bc605f79aa179c86781869959f1bdaa84366d8411fd611facebc2805604d0e52c4d0fc8cdcbb81af78749c9fec45483beef2631a7747e6de63c181bf7397577329d102a147f0dfe10a72550a272061da7229a50f9317781a083c2c76b17d73a98662d068f9daac7b4efb7a2977a6f72b791041e7e2c90b8ff6599bb4e95b40cd7322517ea12b6b3f84d358ff132568a58b3796be4cf6938fb995389176701a8321421828cdf8f8f2365ad7ca92c5a60203d33eb39622aa364dc4a5e3b1eeb039cbc2628884b2e29f1316d2aeaf8ccc9166c0989751051f470a07af8baa6c2f1829c00b1eadee84e10ac8295130a3d318a4fdbb4cd0eb6e2ceffd75cf194e4cedd6a1bcd11a23a294cd6f3633618ff7921f1c7fba81054db3c26349d9596892630f0157893e60d2e627242b0b09dbd9828edf171c9526eff98da7862349534c9c0ee1101958d04b0cccf85e8944da39c259be4a6074270f05c1f4a29ef40e70f52c21cbe9a44966109de6b9f862bf0bd7a16f6dd9c6dbe9ae4c7ba1941746638924a1dab3dce7fc0c547703e9f3f82ec3c1d78518a898ab459fcac1adf8ae501e8f9105acfe79fa8b3b735bc2ef3fca97b7fb91995586b31ba6b539d533f2c31b4640122ea13c67579b817e9b9ec4a8cf1ca621d00addaa4c87f963814e7a46572e924d74c54598496a81449fe3a9bb276da5181665eda779c6a0442cbdadc75f4ba2c289960cfac616450d0661e264b320106c77bc6844452069fe7479118a335472f636b70154f373927177eb41465ccb3f8df8d83fae2cf87fb5f2bb87ac35cd600514cb85f197823acbcc4f6a22c4155332cb29b4767438654848719a0329b8c0da447badf47e77c228e85ec8584a544fb7946e2a56d9d5217c8c6fb8b2118951d43259f872ff6bdaf052b3431997bea7ed5d67b95b96c6b43c935101ea4c6a11de929e90523dbdbed55dd60f220fa0cfb381e86cd1bc5069cd9790e08fd642112fd0cb52ced94d970d9a2a42e42587ed71375beaefbdfce1d2a3ea7d924dd3d27c93e6bb50028f15841cc7871a497fe9e5dac1df5837ca414a27e589737f00e183c2fe4a5af9b91dc146e4afcffcca00ca2f3c58b49b3a161deae582f57d5490ea4ce2f8a8cf06cddef900da54494cc98d445c575542890d49c26395a965a5e1ae65c45619783fc52acc0a6120e5f12b68f7e52ec4b52d71966d1f01ffe5e5d1927afbc8229877850d49ebca124268922854de5c795133dbed04134cfacdf1e0fa606452a1e3d4eb648f3de8e85b7ebf984dbdcc371b6875a28b1c552bad8ef1ec0bf100fff7a9221b0d3e74798945fc46c4c6e95b01d977f84990d482ae36910faad48b0169cd47753388ce499bfdb57b2b212b6eab810baee86220515c6b001c1f56ad998e170f1db27f80cebbdb84e271a90a69de39bc17f93cc1bfc5279131683adc6581ede9f720e48d0d0baa9cfa633f9fd044eeb460493066287613208e31006a8003e0c079cb8e1d62cc0322c76f864846817bd99fa507c1584d64f1a6986061afc0d7bbfcf0431b1a9d0d517d1279dfd18473b5db36129acf565b619cb2b918056aa48e7460902779328a69d6bc5d014d689aa609fa5635f7d082015c833ca75ab3466c51c7571ce015d1cd83dcaa78beebe323a9b7af71588b63e162e9b9c1101d3a2f4f4cdd7e239f663d5433f508a792ae12d40208199a6f79d1541cfc0389d4e850e6334db74b0df336658450bab08b78b741887dd9f517591021357f267bec10adc91699cdb500dfa7f8a604259f0b2a89eeb419c8cf60e0702ed11c3faf7728a7a6e6b44b39b565ef53cf7bf2db11e8e4af084b6874cbfbb1c8a87581e891df1e8cef1443828f5a77871109a03c58ff256a17c401ca9d67f2252929901314381c9eeef8ce6968d3936a7ff09a838094777621ca041071aab1b4d91f1bfc7c1f75e84c062ed419543fcb585885fb564a90c536c96aa02b6b913ded1d13ef20b0eac4a7c389f46265813c0759b182d794c65d02c07fc324b3000d920bc224d902ec8a03317be93cd5ac0b49ed7153c0fa3dd2ed5356688b9e812b0112f1ca337e714ce51d2c61f84d7be3b2265c96ee138861f1c4ada7074bd09e5f0a0bddf585829f59e62e4a4af22188185ebbb5bfa0c2f0231b083a4f8900caf21378789734aa0f5234c7666b08b69dafddb2a5940d99ea588a0c369523323e8a9c8213fc1ba3a26df889fcf23b24a4c256da2f51ff82609d264a082969a2ab5b9ac62269b481c17740dd908d1f56fc2560ee211c4f697f43b0d706d56ae075264869fc8de2d914abb1a71d013f2cb9ab13fb29cd7d3bf51f8fe42fc423d2a899d98f6c7f6ebe76ba652c25bd0940adc42a09cd661b13a4ea5a882287ca80181793a466b37c963e91aa6340b409bba41b0c7281f1b696037fa71bce08642224bf9bd38757de198b7df876f91a93241cf592d96d526b5f9b88af75660112b2f8814e2a8a208c599f1112793a2727113ab6dadad3ca89ae833cf0d6fd0955c259a6b7d7e8914d650bf07ff7ea007fd88a3b8ca4d29ae2d2c78b75638ce1bb52f509daa43eabd1491cef1ca38497861f8afff1dba767367c73594542e0910b7274fa9418e41d1b741c6e9561e2d33679e7d5e54809673c2734224fb7d204c850474e988128ec7884bbee88fd16f1ae662e22aaaafe93b75e98f1443546936991be1b22646f21ba0109e4000eb65dfaedb3bdb686a07a591164f5f9b2e0c93931cab777dab2efbc30459f5e103e56d94eb3c8664ccfc5072660f6b71bd197ed62ea352f641b88b1cf7cffc409db6eef03ab092421510b92e550c1ee770563b7d2b5cd225dc2cbe9daf3adce7f78d919d00ee9a888b1d1931538730448a1234fa23cacdbc55e8a2197fec080aef3b753028782fb55217067fde08d14bff6b203313a0a7cd0bee9cd7f503d36a051215e4cc330e4ed337d65a5862c7fb960ebde60d0182e6c4c27c725d15dcd7317ae2248f6bf457eb4000561557e89ace578830012f467934241ad8353c566400403e1c32718df84b1abcd27e1b3b41453bed902f029aac31e996c58185af2cfbbfc7559b229bec5f0a2cab1be1a4780077648215bb92bf13f3500045a77227c585ccdfee19c884552ab83930fbd2eac2ccff682cf499ffa1e9578ec7d56671ad100ddc8ae91c11c54bdc79a4f9e2fc4cdf11d4077138ec807eb7cc24425efe4ae4844e54496b4528bca4730d2ec2a2c3d913b117bbccbf150fd5cdc249d26a7b32953569f4943269c135e7ddd062cbbc31d39e08335cc653293c1e38dcf14459bd107f4474d957c59cef7d913acc31ba6b1a100801f29e26b448d5609dd13a55db68ae63e8d8ad79328097fb700524a12a71920b2f7763097b5cdf3452f2012e95db3c7b091724f79d56b285e58cc1d7bb9fad02e72758ec7cf0255da295e57b269e148bb4e22d7ed55b6b82d600a42490720f190c985970717aa7038a4659fd6bb449fc696d64d2b29f1f8c37e5ac71792574080b86e629ce561820bddfb81c5ec6f2146aa269def6aca2218c6851eb477f2e24f9006fcc134aa70708115a567ae4f4d2f9d4fbbd944ed838d807ef036ccab90ec3661784d4f9bc991a13bedb10508e647c83063e5407cbc0de306161c4d64905f372a6c8fc678f62557d021bfc1c440df6b50ac833e1d811fe94436049fb970a322a89a1dfd7d10e7978ee4691369448f5fa685699ac8d91b2db2ed8828f0723e78abb7cb9aa7f47a522d43c463371ec5ed097108a2044f8e11ae0e49a3e2f13de49b1e23fcd7d3fea1d4fa4713edac0d8120da252ec07e924fcbfc7302c5529551d781f10d0443a57cdd41667ff645749d945696207c6b2226dcb907e95a4284b421078bdd08597f1d991143c76f7b1d903d88a2cfc0140cac4249eabcd001608b20c3a0c43164859d7e650a590f7e209dc4f640c2cbc34ace9460318f5320c4f30ac648f4a3a4507a8c58f15e4b699aba1ed1b64283c437eedcb24e335f5116b2d20bdb18cf6314dbe1f2efe23c09ee06e5928a09704d08db9f5325a048d167189d92251a9884dd3eed0864936ecd65c5992e41226b00463538a69d457ff12f4589f23fbb25863028f7c41318478807faf120b95edb2c4223b42938adcef30522a8b8167e74804e67d65dfedc6a47cac97fb56d6f2680157109c5764f0481fb82d364fe92f2a5108802e169e37522ccb9d2b998f07e7dac04afae9ac00d4128e8c79fc5b4939ff95d9e3eda7b22d6bbfb581b6c4218d9ada84e8c999532d0ead62a21266b5ba7070ac646586ebc3046b0c7dc9f40201e6224fab8c24914729a5ed6a8d6a511aadee612531ba4666c1c3293f5b896fae7e6f7a2519d4714a", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e4411dfdc7cb6265f100524012f038ee1f205bf8789b70b46c57463dedb4998f7ac800000000000000000000000000000000dfe8f99963b947ff62a9677085fa128d00000000000000000000000000000000651719d455c726d57f7592d491c375c21806572c19ee2f3532e970d617919b3aa8cdc582782dfad0699badc631f75128000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020ef8e0d309ff18b3d6b953c338b01b97fa25e4585dcb3d2715bfad3f8b534c1c00d8052e8cd77545bebd67fb24f5e55321a23fd6c4cac362afd5347d83b64a0814d67d121dc7e34cee6e339f2837b37ee1d059676ec86d26abe900b1981dc50b2c243cf0f78fbc62ac23338af6f387e7739d8309912a551a2d3103bb652b7b5b000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } }, diff --git a/circuits/benchmarks/results_insecure_agg_micro/integration_summary.json b/circuits/benchmarks/results_insecure_agg_micro/integration_summary.json index 16442f414..2ded58496 100644 --- a/circuits/benchmarks/results_insecure_agg_micro/integration_summary.json +++ b/circuits/benchmarks/results_insecure_agg_micro/integration_summary.json @@ -24,150 +24,150 @@ "operation_timings": [ { "name": "CalculateDecryptionKey", - "avg_seconds": 0.003899597, + "avg_seconds": 0.004320611, "runs": 3, - "total_seconds": 0.011698792 + "total_seconds": 0.012961834 }, { "name": "CalculateDecryptionShare", - "avg_seconds": 0.020771278, + "avg_seconds": 0.021778305, "runs": 3, - "total_seconds": 0.062313835 + "total_seconds": 0.065334917 }, { "name": "CalculateThresholdDecryption", - "avg_seconds": 0.019628875, + "avg_seconds": 0.020453667, "runs": 1, - "total_seconds": 0.019628875 + "total_seconds": 0.020453667 }, { "name": "GenEsiSss", - "avg_seconds": 0.007804847, + "avg_seconds": 0.007597111, "runs": 3, - "total_seconds": 0.023414541 + "total_seconds": 0.022791334 }, { "name": "GenPkShareAndSkSss", - "avg_seconds": 0.009254708, + "avg_seconds": 0.010288736, "runs": 3, - "total_seconds": 0.027764126 + "total_seconds": 0.03086621 }, { "name": "NodeDkgFold/c2ab_fold", - "avg_seconds": 8.419060625, + "avg_seconds": 8.096940472, "runs": 3, - "total_seconds": 25.257181876 + "total_seconds": 24.290821416 }, { "name": "NodeDkgFold/c3a_fold", - "avg_seconds": 36.69000318, + "avg_seconds": 35.482067235, "runs": 3, - "total_seconds": 110.070009541 + "total_seconds": 106.446201707 }, { "name": "NodeDkgFold/c3ab_fold", - "avg_seconds": 7.678847138, + "avg_seconds": 7.882662333, "runs": 3, - "total_seconds": 23.036541416 + "total_seconds": 23.647987 }, { "name": "NodeDkgFold/c3b_fold", - "avg_seconds": 35.819565458, + "avg_seconds": 35.908104792, "runs": 3, - "total_seconds": 107.458696375 + "total_seconds": 107.724314376 }, { "name": "NodeDkgFold/c4ab_fold", - "avg_seconds": 8.047481542, + "avg_seconds": 8.13719925, "runs": 3, - "total_seconds": 24.142444626 + "total_seconds": 24.41159775 }, { "name": "NodeDkgFold/node_fold", - "avg_seconds": 19.279656625, + "avg_seconds": 18.718641194, "runs": 3, - "total_seconds": 57.838969875 + "total_seconds": 56.155923584 }, { "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 1.60953675, + "avg_seconds": 1.611448542, "runs": 1, - "total_seconds": 1.60953675 + "total_seconds": 1.611448542 }, { "name": "ZkDecryptionAggregation", - "avg_seconds": 49.763333667, + "avg_seconds": 48.859235667, "runs": 1, - "total_seconds": 49.763333667 + "total_seconds": 48.859235667 }, { "name": "ZkDkgAggregation", - "avg_seconds": 20.434941375, + "avg_seconds": 19.836521125, "runs": 1, - "total_seconds": 20.434941375 + "total_seconds": 19.836521125 }, { "name": "ZkDkgShareDecryption", - "avg_seconds": 1.318937159, + "avg_seconds": 1.224212729, "runs": 6, - "total_seconds": 7.913622958 + "total_seconds": 7.345276376 }, { "name": "ZkNodeDkgFold", - "avg_seconds": 115.937274027, + "avg_seconds": 114.228116111, "runs": 3, - "total_seconds": 347.811822083 + "total_seconds": 342.684348334 }, { "name": "ZkPkAggregation", - "avg_seconds": 0.863779417, + "avg_seconds": 0.615287333, "runs": 1, - "total_seconds": 0.863779417 + "total_seconds": 0.615287333 }, { "name": "ZkPkBfv", - "avg_seconds": 0.234095361, + "avg_seconds": 0.226558361, "runs": 3, - "total_seconds": 0.702286084 + "total_seconds": 0.679675084 }, { "name": "ZkPkGeneration", - "avg_seconds": 2.564455153, + "avg_seconds": 2.497946278, "runs": 3, - "total_seconds": 7.693365459 + "total_seconds": 7.493838834 }, { "name": "ZkShareComputation", - "avg_seconds": 2.554805451, + "avg_seconds": 2.40892843, "runs": 6, - "total_seconds": 15.328832709 + "total_seconds": 14.453570585 }, { "name": "ZkShareEncryption", - "avg_seconds": 4.018045519, + "avg_seconds": 3.950791532, "runs": 24, - "total_seconds": 96.433092457 + "total_seconds": 94.818996789 }, { "name": "ZkThresholdShareDecryption", - "avg_seconds": 3.493319013, + "avg_seconds": 3.383825583, "runs": 3, - "total_seconds": 10.479957041 + "total_seconds": 10.151476749 }, { "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.103515208, + "avg_seconds": 0.102920694, "runs": 3, - "total_seconds": 0.310545626 + "total_seconds": 0.308762083 }, { "name": "ZkVerifyShareProofs", - "avg_seconds": 0.269947275, + "avg_seconds": 0.280129533, "runs": 5, - "total_seconds": 1.349736376 + "total_seconds": 1.400647667 } ], - "operation_timings_total_seconds": 908.64351588, + "operation_timings_total_seconds": 893.088338963, "operation_timings_metric": "tracked_job_wall", "phase_timings": [ { @@ -177,68 +177,68 @@ }, { "label": "Setup completed", - "seconds": 2.96616925, + "seconds": 2.9637735, "metric": "wall_clock" }, { "label": "Committee Setup Completed", - "seconds": 20.093379417, + "seconds": 20.091043708, "metric": "wall_clock" }, { "label": "Committee Finalization Complete", - "seconds": 0.005040209, + "seconds": 0.007389292, "metric": "wall_clock" }, { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", - "seconds": 136.237549, + "seconds": 133.913944, "metric": "wall_clock" }, { "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 148.9261515, + "seconds": 146.239215458, "metric": "wall_clock" }, { "label": "E3Request -> PublicKeyAggregated", - "seconds": 149.434793375, + "seconds": 146.750428209, "metric": "wall_clock" }, { "label": "Application CT Gen", - "seconds": 0.009646083, + "seconds": 0.009458917, "metric": "wall_clock" }, { "label": "Running FHE Application", - "seconds": 0.000103333, + "seconds": 0.000062875, "metric": "wall_clock" }, { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", - "seconds": 51.386342, + "seconds": 50.48525, "metric": "wall_clock" }, { "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 55.335645666, + "seconds": 54.401319083, "metric": "wall_clock" }, { "label": "Entire Test", - "seconds": 227.841801083, + "seconds": 224.22015425, "metric": "wall_clock" } ], "folded_artifacts": { "dkg_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000001fd49ea2995c1805a000000000000000000000000000000000000000000000009e130f8d07cbd35d1000000000000000000000000000000000000000000000002e5ab83f7a0ec41a9000000000000000000000000000000000000000000000000000180f1d364bc1200000000000000000000000000000000000000000000000b30855f54e294c86600000000000000000000000000000000000000000000000e219fb2ae146a494e000000000000000000000000000000000000000000000004d22012f74a7396a9000000000000000000000000000000000000000000000000000215c2c000275000000000000000000000000000000000000000000000000eac34294d15070d38000000000000000000000000000000000000000000000004e87191632119f7f70000000000000000000000000000000000000000000000015038dbe31f6ab0390000000000000000000000000000000000000000000000000000cf331e3e636f00000000000000000000000000000000000000000000000dd9f98878c5b6f7d6000000000000000000000000000000000000000000000007ee2134f16e8d33160000000000000000000000000000000000000000000000077df2fea764d8310f0000000000000000000000000000000000000000000000000001b58f64c88996077edaeb00d787972da96dd0edbb7c2b101fde53e5028931cdfa8158fa8937632cf53d683e41360f6e529b02d216642bc54fc3b5493f89225f4cdc35ec2dd75e008be81ba59678bc2845a0a790af2b8124f8730d49d964132d0a028970ab563a06cca51c91027c577ff402f93bd200243f9d344e8b2a354f1aaa5da3b43a24e11b6fc61db08de25bc5fd4bb73d7df7383cf857691020be5880f05c25bc8d19710d7eb2427e444196714589588ebf61cc54c552518e950b56b9a6fe08b9bbabd52b0db8963a40cc8dcf97ed101f513d60c9f7ce4bdd3fa84c9c7aa8e5706ef3af1cbdb85e131c9695dde95e1d161deca5430a0adec644f6fdf7c962cc0686cebc0e0f4d1d4c431a4ac5c17aa11206f288f79299605006038c506c284084f626822891b514eac670d39ab672fa43f20c0b65b0e10624a2e638ba001a4291f1740b24777273c445fe315e2193fb45060a7010a34afdaa48d5b4e215b66fbb18d3ba21045761d06795541b65751d20790ac3e1d74cfba39a6027cb9ac310ee2aea83024ab126d473d57e2cdcb9b5af0beb957c65d4308f1a4f7294a1b1ba16e047e50e92d2d26422ae9ca51949ebba20c96db2e5c1b6c8ba2898abf038e018d0e5f90d575b2f9b331683b451b53e7effee6c815296c427b181ae57448fa667f3e8d802eaa8611c3b5b5c25e74c64bc7f45e3112c669ef9f4e0d756670e89c34de6db2cdce2ee36eab74073def9ebb3fde8b4aeb2ee658c9acba864d80fb569c75a77022cb90ab0b77907d8103f901c21ff013851f12f8d4b6d24323694b2cdde560e0ad79dfa9ba2a692ea68660f563b9ca963f67786eacb35794db407e839a365a007463b9e1d2f0df8602e1bdf84d818a0395a5242aebdce60bb8bd4258fb126b52b9bb460323411c99fd5c4b42072c524a975df523f28156065e824e35dcb1d9c1f9a105ad7dc6269609922d8cae5d7c6ad37425e957cb25135476748a8beb332262959fdecd94ecff5648621ae55cb4cfeead8f1042dd04593040a8c76108e9a2c17f170d854dfbb9c9493bd58da3029edad3a4a5c2199ba8948515cb54dd87e14dc69acdfa3e5e67c481bc78cedec6e1af6ae498e8884c5482853dc3161a6dc1e301746d84e8d4f07dbb2de4b7789683203870a9ca01147cebc327a1f2a28e4273678b16eec7b05d7f220111d7b89455daea44633e1d1b73a1a5d698e87248a04a7d9705a4b7af209a598156203cb008220c25975ae7bf3c6e8ba9d3f2c678a09d66e4b7bfca0211c9e3a4a3114eadd1105982c019fa2c8520bff6a6a2fcb9e2cafb862a18a64b540895760e4cb1e8e82771188cb43ab3f0f2a81aa98f416c90a9aac4fba1a822c08cafb8ab98475aaace9e064745616e2eb1471cca54a219b0b6a7e7e884e9e938f7dbef5134e11e9a00492bada044d8eac8e1151a74775310af775f458352ce39e593aadfa030f8343b9c9cdb3c0a51eb8996724a40598f02d3b2758c53427318b585617fc8810f08e1aea10a43ac4d7c25fdbec155e32402d2bdec92c262abc30f56e45f1184e3b5b9190a16efbd59d4aeba4af3a9ec98e0c229a521676816c507fd4f92cb86b6aea59e8dd336b9e7fc1d7c1d38e4349b010332e0457598a26c2c4a3e0d02c20af948d75bbbd56465cc016af714c6ed89205b25caab1ef5e5d4753587b2c423911427ee11eafaa007459e515e54a2b068f128e5f87f9b760a72c83d37430c6ddb70270dcc64319d6416c3b30d37f80f4f209a81ea9f1d236a7b9bd55dc6ced0ec4e526908687831f3d95acd61aff8ff2372b0e2d95a1361900a977f9253a6c9d45f681e2f4011ff13db0c7219085bc33b223460a7bc760f25bdc85f1f16eadf15a59acd2cece7ca3eb789e926f49f7c786161f3e86f116612b5a5a429c31a1fa51e8fd9343031442f5c94fa2987182845a2c1ccb716fd77606020045903b109f92cf24f209754bb0f28a364db55c9b43aa2e51b7498f6e68e2abaf0b76b36d851b29a09217cdaf13c7ee7d0cabea08eccf11fa5693c9691c14321ed941fd08d7204931bc6e514b48ec12e985c8b557ee3708f89ec5b313a1f429eceedc627cbc8ac7b7b50086f6cd9afc75997bb3507c120c8bb726570f72529caf1535ee80af24f911081531cf149a92ed499a134b0eba01b8640b24f5cc592f7e79add4472256c4a49bedc5801a22b7ea65f8361e77ab2156b74036d7c9bdeaa69b37fdc5ec0b8dab05587f48e430f701e3163a917047170bbbccf4b84a16ea42894d2e25d01fbde3702619c8c38d7a06c6846f9ba67215ea2a312148e210efc156d4ebc28d6758efbce0d8d41839febeb131f0945fc01c84af60616bde2a2a4fa6d1715314c1ac81490793c91017a88de75fac5e20d3080548014b07a60120d857b6bf9f57967ba843d74a34204504e29671e4195b3b24281c43c58ee0b93d2af3f13e6314ecc938001ab4d296749dddb73ee17273382e2630ac48d1b66eeb3be980576c76ac6e7add3e4878cbee0724917f0ba962ff243a2e4a055851abd682ecf0908821cf3c1eeb788029b1335670075439c398190673bcc78e40c71398b948d57471fc669d174d2740c405199e6584f871b9e3012a0acab2e96a2afe838043a16becd5c3e687cc6bd2efcae982386f32962b7a88245310639393ccaa5331e97b7db1919bc2d50e86f065cae12b76b07d3348b6180fefa11f9b377c565b5b40383a76bd59af2437e9c72a05e49837b5ba60d7b1712fa7b9e47e883d1d4551c92330f85e8e8a3597879115735095c251d0bd3786bd22ee5a0c362112a3cd554387c3973d608c2aa579b5136e0de6e93d354f4c5a1408a644400d96627bd7b10ef88796326dcbfeeec6916e7462a5e90fd6e417bed428b6e56c827fee917c101bd208fba6a6a96b26c8609660036db1aab7fc68b01a0f050acb41424de2746c4680e9f50d6b1902287390809d57c071bda340980ade048787cf4d2dc03c343eca0db49299fb8b7e69303cbf16384971194fb4c68e3c2eba457fcfbae5f78a3ba7bd14147fc79cf5337190691398b6bd459251c131442a5aa476d922a21867112a84ae48ecd5b6fa90280f477bde61f721c68ee2d2f513ee827cedbf43918a4411f6845084504144059b545de93677a73f32296037de049c8166781f6e1424f72a05999f984b97fadc1ba8d10a7ee32be56c80adbf751ca15b8240f73f458e5b7048fa8db1c7a4abcd252e1f5862aaf75d9dc7ee243a1a0a8fc6f86b636a8616ddc653172189accd3bcdbf647c30f91d364c81c206962ff31db29a42d1aa4e88acc875d3eff57728bb3450ef218f7ce61a98e885b5772c08b80788b8e0221b56ffdb2e044a99df3ce955d77c486dc8ad7b19cc3f20eb2fdf6044932d8a24a9b47713da7d64bafdd7e72db9e8e9d1ce17c51ac428fc991f9b8d2ec0c1e7d1cadc63db8032a4246c2d93b41402b11ef34f0b3052e6b6101b1092f7a6d83b1aae323a8d093b0c50965514a1ccdd249ce909260d2e81ba4921c116c4dd85c556af575139c86bdc4b43305b9290fd471de29fc332e88e094c02253af4b0ddcc70e3c853ff8370f241c6a5af7a9124e46fb711d4710c8e0d860c83253e57c909cef046bded3d5ebaff650057ca72ed08a066a9dd3c5206d7f4293e361d99a9b24098ec04a441e46c7b2b8064dae8c70de8bf385ecca60724131d03a978a8f846bf6ed6b464b43514a9585a961de9dd2df10ef0ead69581821a2426aad64c076e2ef8c465ba244a8f3ab53eb4074c4687c2068724dc4dfb7fb40da880fa666b2ff11fee4198e828d0a81a16b67ab497f5fc6f9763c45cc5a3721d3747876e7d1f7597a136b34a7558eb5a2c775c4ea3a7d587ffeb9e363d52681203eb04847206683a5894d351f7ac067b90c6e2857fdcbbb0de3ae1b88c7e7025bb867319c0650dbb7199954bbb3dde0f1848f61f262b85139607a8194c25820b86479d8130ce090230b101052095e37928cd7b35cec64a52bf2f2d9309fa620a96840836aaa24508174d058c59d3243dfbb8dbfa910180139920b5bba0ba612a3137d9139def0d4c2dc96ead633a57f0deb2f385ef106a6b6e1f8552dcb04a1c7e438c02df964ad4cdf994b96bb5145852e4cff61b67e9d2207bd1f98742861128971620fc09258924db77d5849d4b01c133d5eb0ba35bb715676a8f0760b1209dbdeee1b98148722437e311c449871e52f424bf8af6d022cc9a8bf86c381a0ef751ddb94d31062c176215cc10a722cb13a3edc840154b075046e3d73a49511fa65de3f652272d12117f4922c89ebf154622a90cc22d48d852238d0d8f722a120f665f4406f4560fed8ccf003cc551e15a4619cf80218263ba803fd1f4f751188ce7efb3271294fd4e09cf38d5f5e94bcd90c48b69ba9033a907f50b9058f302d8bd87bf079867a8a7e546035a866bccd46a6b7ae786cf3bcb106b0ca4eea90e7a05252c11dbc7b11bc30a304735bde3abe0bebe3964700f93f6464297b8b708d1241352fe66286724eeaa7fe21b2a1b58fd9f125e39e8e30d6c6281e7177710661aee44cde894457f7694b7e7a097f95bc4b343132fb9bb618a26e7952b6d2db56ffb8b361ffe0789871aac4141603a1877b9da3b9cf0719a17683da02a8717c2b38f5d89e3719efa94ac694f52bc00b4a11822e900f54dfac530acaa4b2c28a91d4295d27ded396d129a08cfd2c717fa74161e1b9f7926288a32abce912e079096a999aad3cd00fa09f8caf340e2e49e665d4d008c12015303de4057f9972dd2677eb529ddbf4a050ab90c6c2c2eeaa88d9b13d277cbc7245c65e2ef0c42230b0a5b03a713209c518fb9ed18faf4b266b4f21c9977677bff73fa324431b70f757eda054f648fdf26a5211123ba1d0ea9dfd5d124e377cd0aa11312ffb0c420c23c32f1c3cdd37dd268e2feb48b54b0e6f19c0ee67a2ae7c01472d2623d1e2fbc69c45daa678a391a5504a0ed5a0a5e4cb250f36501072c265af823abb45b1dc5739f9eb5e10a37d356027db2f6faadfadffdb2ce7ac184bc7f997b53deae162a8ae642df97184ae988c8607ba165006f0dc926cde19a62ba777bfee040ad0da536704fe3046961eea59de7f504851c2b42f0948a576d02146d802592bea5296be00ec01d6bacdae3e08981c5d1e75bc6882c1694ddafe5f01450f0a019131667b2a26bf0f497d5e379f9e70b233d24e3d43d7ffa468c5d04a6ed530c07d20548fae6cd942426fbad40b064d09ba495711ff9ecbb7f8fa128841dadb433d9275b8602a28769edfc46311c99d70a33e6244c69d63d5f1f70312907faabe2b424ff92a16a1011d0b94ab5c70e20e3ddfd84ad4ec7a7cb8c7346a164394319ab2c7c716668fc8d1e4a01cacd713815860faa08a649f03d54cae0a85d453496f807d9c9d01d661377cbf497b95050d15852b24c069aefe635bd35329d9baa8f64108ea7e4af7d45bfd70af8aa9abc564b259e813614a906a560ee7a15f46cd3782c4257ad3ee80e97cb60726b65a4f5a2701c277e8edad07984f7c3753f6139b9276d4512a0541ea808942cf25dbf676e996b9f45c576baf6447a12bd93cffb930a398fc4ca770004b59848f3adedc44b1acbd0248ba516c564d1953f177e73eb288458a12f11a83a53ddd929bcff5c28f195b01cf27c1aafb14b1db691ea07882c25272f45280f7222fe7d7bff564dbbe9c1532e79807ca0d8709f610fdb292606b4d4023d10bcae31fa0f6389085e9e291905031f7c7c07f726cee059974210170ff701c030db637dc65b0c4855a7518fd3f6e08745e376cb32e37b932ae0ad0c5e118818c267aa7c8e9603614da1384b8cdf398b5afc98449b5f454f40b16b02625cbec8c5c8f6e191c0b5fbf55c922fcc68258b33a39ddf72c0beb9ac218911081f65e7af22fc123c58adf831317451c202135ac4d35bd4dac00ddd18f5692e7345c12abdc477eb8b415fda1bd3b6ff88009c5b6b6cf9623d6df79213a82c055506730507438bc795dc759a7c4057b3aafcd52e3d58cc90f9d2709add84cc01b3f56a26ced3f69913748de5d3b4c24b50051ade9e9700dfa8d1d0c5f14d5d1ad7644a107beae994ea0ff4532c54b89514c2d06af3d634ac0a53764eca55210b2f46facc611e557559920cf65593b6616db1eb394d0c20b040ee8a4b48537e1198823ed6cd160de66eb43e9022ffa950caf79c4b4e405d325b8e69bbc9454302ec177251fe8cb4c6c57542b2be93f91666a93ae052a355a731f806b16477b82d44c4d0b255663e6b548a9d8f69d6cd9e2d6a14d12768e38847a5bbafd3b94020502aecccde69569270bf06687f6617cf3eb0903c36f0449823d51a3393f9de19dce60cb1aae13a19b50e34b65ffbc680f412012ae0063d362a77c85be265ef2c7443d2dae210dd161a8c217b18620dcad17590cbae456a61d250fdbba547fd29b3c7854a3a36887dd4ec8c4e93e56907c62a4140aabc9da587617f961fca8d24d85a8c59e11cd64c84fea5e13d586d3e88fa439507f58e26d3c317a8bb7d32177abceee45d0b521eba525527ba8d739426e9c7f3e695a68b59414c0265e9752805ea9635e406b084a400034ba3da86ecc6c4b6217ef5fa2dd4f344913e08fe059c5a303964df6df32cd2788e65facb109fefad82dc156fe1c8b06aa84bf0ee033b4f7d9c3773cf9376080223b5c36409f8e94cf3d3f1d8c62e32276b2112a219cf162993c5c1e2e75c8d2167232efd83178a7570c0bf482866f3c0ab3d16dc2cb07d71751a27546ddf0c335b0f31edab3e88e7796d9347733c18786d5a570414c29fced4459d8dc19627c59aee6008f5e140d510490357cd98931a93de019217ea68fe14ac058df3fa9e9cae7727b03d933a66a23e217e97a5be9d14c762ff034f3d0afadfe715e5da693a3140044e0f392e07659b7fdc04c2fdcbded6fbdf2d4a4ceb366ba2ef2abc371969dfc1735ea35bb48f927360d3059877fd3631fc2604d08c98df80b00821a6a7ae5c5244fbd65539e4af2c0e50d6992fd4fa7184076abfd3c49e37a997c8db51c9e0cc01f39c1afcd92f34131292e2700f8a1957198b7ed6cffb3a7c201fd6ca40409bde83950f8de3f35e81d9fedbc3750eba372baf58a23be51087dea664645af910ca2c8e5044971d829aa0832cb8c72c574521740bc1aa918a1109b5bc99e0e1039c6e585e505a9db5531b76a4bd14a8b1442e37c1c4dfa7150fb43733a9b157c7a601526920599572946feabf80137ba1ad2322871b6aac745a9f874b9bb8ef9ee182a3db6d33de228bca5096f018e3e8a9023650fd0a202af87cfcf8c29efca5d3b980149ea855d69489f595554fb2f23c29a9aff0de611a87627cda3c879b542baaa1677827282a960542661baf13045c14836a3abd071e95bb8e0954b7655fba14a5712b67f5c2a8b44c8b2d9928fc6f0e10a87895332d36c58622031ee8d2d6ccf1973c0c77042c42d618a3dd2c9e512473afbf6d4180bac27744f2881457b5c9fb4b3ab84b4cf8416f1a8570899ff228bf7ad3970337fd65fc3e884d8fff552a7824001e73724072ad01b639d82f630229f0c3b29a4844d16a0e66fd9ab50c1a2a4c4f388c36143449e931aae4bcc22bd450a3e3bfaee4a3102afd669ba6f1b9e9e8f3404f8905bfbd2e49f9ed48db03174c95e1e7078a63f676e989df81004ad1c1d818676bc710aec939c791a2170f20a480ef9719f1f4b7e93c951af6c035835d60e312d54c3199a282f5da2ad40bf4385f43eae8609d03334049837d361c4ab902f4bdc2f8060a51888687db0a1b0af92cb490eefd3f631ad0a9002bcc844b686dfd15ee545a2d408d5b88e9f42171fe53d283a88b7188eb944c0cfb22d8e8f8e667944b5c62a1c7f1adb65e520b864b06dbed7fb1a6c446e24a39a476820bd68ff5b1d23584fe190a99a1e64724b1dac25560fdb393debfb31da4470b28448d42a031efa989617232d2a6ee2b1c258020f28d1b98e3c58a6a66fdc9dceef00668e30a409a699babce4913ebfb0dfaad87dfb55c5852970a9e232469b152967bb67d84b6ea2cccb2393a9ad06f06e79ef7e5f2ee98079b5f97b152dadb8e536ffae0af9209b7e97a18a6eb2a322ef63191d9f65612edbc1c6e9d997ed8415faea2a69318114dc55bb2256f7d522d88aab1575bdebbabb5c233d83e80258dadf60a1cadf436b1ef7536a545a50924af2ebeeddd230c21a822f1f1a1462a7f721f84702e55abecdb1d19adc11cbc1a40d54138980ed32bc41efc10e97fd6f73693807938a5dd3d094630acd94db10dd62a2c9d39431ee742dcd012aaf7b9b05606e9763bc56dd4ce73a00055cbbe2b1b95a359abe81c0fba341e408beb4ebf5fcb99d77386ae932f46597e059a652132dca997945d68bc03f4b8e6fdba705a4df8d264f03227a1d2417dc653698506f05f3ed316b551b2308c5cd275e979b84b05b00dbadea8b8c1f07a5dcdb5d4031bf03e7614209d0576cf3767c632dc3da608817b46a002e256ea39efc23c9f0b636c1539a4e8b2e7ead1b0f0922c4226513eae31d34a5c23aaeefa0c1035fd2217a8a1cf0f64f5bc124d8cdb72b7c110eb58e3895f987dc4a9a2f98cd43eca04b9354718910b3db7336377fb91fa15ecda37a5c97f4e0e845fb0b697f883c51ef3cc44eb53eadc94257571d17b8b2d9664ed59069a47d20d7c9f60c2dc2bb72589069de73a880be8c2d3dc8af7b126a1bfcd539ebf499d735976c4f9416dd125cda14c2fd6b9aadc9fa8e8742674406c850640b91434dbed6db5f6f955a45203f22c60fae57f247f5737a7d76d1e8fbb1c500b0a74103cb671b24a11773ad61ffd241db5e210987b5e0f4f684aded901be922f34095dc04db8cf2df743f08a2e344da58c25e72b35ba59db4cf80a8a405f34d801af11c44ea66a7fde80cd780ec70d8c6f21002b9ae5a48c55d4eaab17fc852ededb4fb7612793e41681769323a5ce7f25cc21e052cf512ca46a3e7d88831681aa60bbdea745630e53f0ba2a0b778aafce261c61c2b2735a8831f86673336cde49256abe49da15549eabbc37047e4676f2e6ba738e6b30aa7b42a99b3d446e938ea33223754a9f7fc73f8e892b868e2c6999aa25e43d53fd63bd8f7fe3ef79733fbac819dd9637b0960086a0244a05a8b2b7f32a0b15e666cd1febe8132991cb7b0ff170207b222e4cd62ac826db1dbce473329f0dc729173335506d3caf2b6c2d3a0390c2d7387e92b875850565046bef359a9fcfe33325f12ac75c3c655f4e21de7ff860fa67b6afe0ce390262e1c1c7fa0c9805fba8b8305755f70c1604a914ae41989a70318a63f0535b178ad7aae2288338fd806347b82943448c52c16da16a4c627f69aea4711e1db02ae912e02611df5100590a244bc928798d13fe02c7b549e060e36a271f556a41145aca25895b56b415c9d11281506c5255c43f0a0da9cd112962a4a1ef37f7842cc962938de54f19839eebba286c3d05d87c64b22667e356b168023df18001b70d89289947bc9fcd0132a253beeff9b1098d308094f88e798eb8756304917799135a1cad102a521af6050b358fb030b2ee513e7d19b214a1956050f85fa6f8c5212b8fd410b568adcd7299767779a372332406e5ebeaeb7f91ab37c7a480b51c09bccc0886917c3cc20c864ed96d4be02a4052219f904fc82992cb9c331c569816ef560c14a12c8a4eea11ee769ef46b124dc807b63015ca546098df6e6a16612abc9a2fd6238b510bc4c17a285359ea54583c384ae62ce76853124e43a9154029d7c520e41153b7dd343f0d7abf1c5220bde7bf5acbbf388a8d92c3100dd9271840cbf35b3829e7720df75a541f0f80423cedb23ced4e458c827ac2ff4e5bc204d8f76b69e4af9233c6965fd34486bb6384e35e4fbdfa557e37e859aa9286db27ac09426cac12567646aebc490e23eaecbcee175485dbb0f7473260a683b729096cdb72801ff55dcf60c26aceccad0274b30697220f5794e03320428b31f94c212aec9328b9e7d0e94f04173fa15d5c975b9993be40b68ebc56373f134721521c4a577a9469eb0c82b41ee48bab8feb84921bb8b8817fc2c6922a6656c9bfc7295954bc31ebe7e13058df8c65c765bed2e0f539adfa3fa1155e295b2dd7c9f41610853333dc74f9f586aca7724f1be833fda34ac29248f8e1c2e1f55f35c408005784fe91eaac93552a5583ecfdb07dc455382a4feecad5e0ea3a6037a3bf9f2e5a1168aa423832bc59965a9fb8d3fc3e4d70105170f5e1df8a269a4b960a8302ef2418b6a730095d73fda3d93f0eee8f2f8f4be4e509d6ebd50e5635ba142802125bd0c7afb84d93c47c4b97c17a5fe600d9a0b3dad54aec6cc7b120f4edee087ebac3ad362292ba295da087132bd932e8effce9cfc1ccee32154b81d601d50994b7bd9fd26dfaa7b9dab924162e185db0f748409770ae64782c447f469d5805cc69bc75589155860294f5a034495d3ae3e09ae450822bccf3a102d1c45fd71d395aac77280661e67c53d84b2b54d4f8d5b463b194a6feed38f1410a22bc990301c57a9c377515d508ce17531560f9430e20b6a2b3cc32d83bb6adbfb2de06170c93eb7f359b3e7a43b193d199f960f14c76c9a014bb8ff6943abf635594130789373290ef2c99000271cc417a41091ecb315478c020a26545812048ec1fe82c1420a8f5627d159eecc865ad5508424323165990eed29599b667fe076289d317ecdca2f802326072891f66f220cee8dc73725e2cb3651f85d42c07eb9bd79c261fc099cfabbb7c637d98e8eb3e1ce7d6a32b22a27e12f9d6c53c2d73d509ec2783d739fb5e079f0a7a3e9818e2b3eb0a5356043c05a3bc5a9a977865d9f5542fcc861efcba31be85280ca013d7f18f62ccc24f8018b60057536d7b36950eda19c4fdfbafb64660fe8d5675483ad49b01162a25a492b43cf00c6d7dae14b2cd2472eb6bfc77291b5cb46d852b9e4932c5a2fb92fdffed5ff915fc7f70459b1b18fb25990ca267319b5b37c6fa91d009aee19b3e9262b363b1e9613298edeb0319294bfc2e5978d4fb62e930ee84c478a6dd17acf6d6d14c01b0829180cf3d352822de17e8a508ceae2804b577cf25382714318836f288c434fcba684506ee27240c4821bb1504a26ff7dbf76dc53dddffa2a8fd185d4e74137c093c0926634809023f91f5b1d22d19b7708d0276f1ac904d1df90443a6feed3f0e58de3c57e71a84bea94561f1c51424b32361e96ec955f5797d3144be8c8f229550bc94fa8f1df6afb0d8842227c0cba3199c994e658e6746f9768093c95930862e3a0b56ac0c1c932e4bf356017cb2ca19e29be7a842b152d800ab0aedfa834a3c7d52a8c00f55f6eccfd21e6775f704cf030f8bc93c9b9f3cdacf89cedea1f828ba2a967c2ef571f0d8981360cc509a91c5c2bfc406a7c72906303d8d8e29c6abccae4cd406b0d0da7d2529d7a8cdb4dbbc5142972291b660b547e2afea0030d70045bad015942a73853124febd5be8350b3d83037a6b57ddfb1251faba5e36f34f01614c2cc4ac0a2353e74a9fcb3e1207eff81c29b29a4cd90d839ac0303ca4dc3c100a1309c1dab3e87b68141dc7e282f96d2913b3a11f5101a2dab52b3e4ad02d8db20ffd228dd53135b5f9a2e5f40efd33b2bba6caaa5579ca941e34290a8019ccbb0c05d9492aacfe8444ebba8ea2d51cd95037aaacb110ae0dd86a51ff39a4a7cf1ebaa014bebd420901a2aa991e18795c8bff01cb64436ade5c1720fff5defe252c89a6983fce2e13997238b6c375cd5838163219451cd3d6a95d617a3757e98627baf0249e295fb4a1cc27f404f1395e6cf613177748c35b5fb9dc002f5532a62e7bd8bac7a6e5374a45b2c066b8dfc9a40facce7765aaa525440acad0513cd71e9ba1123a92b3d6da0f6a6e4c29ce5ef71ca704217e6c985ae5c12409c83b2b2ccaeca49db869ce155e16b30675acdfb4e74f236b7abbdc3b25bc6654aeb3cd0bdd2fa5f0e8bde0fb255102c9980d85225b5f1122ac38bdfefb51ceb05d453f1772c63be41b33fde57974f26a4630df3f9b115b032591c02511255cc052375c145bdb795ea0561c0e86e47f2c40745beb4a9244543a8d8ebeba6553979389602296b4f2d074bd2251c2e7a693a2ab3efea312b8517a1186811cb1703d62c60d1e5b4add004cca09a56964444396391ac57d653f33c2cdfc44a6e63bb5bb073927613dcf926775993dffb6ea2c7e2f2f6ddbc7f534b6f023dbb43fb40518741e0c69103cce484b13de07024b6cd9c698e83d0829fc1dbb5f8f1ff8f7b4dca69a0eee9d7777297b8ae2276377c82b930dd6908de907c96815c2127f4446371dfd013a464b7a07c13da0527242c9f3e885f17879c6004d6031c1dc37a5308ed1eb2ab46a965af357c739981f9aea280d8fdeef48dfc08b31cfaf5f085764459bc611ec28126db3fefb6cd2e5877fa445354a4a3fda6568e11050764a9f17a33b770d5692ff0ea347dfde2feacd5f50bffac4b852fb04f3cc7be151bc6176433692219a35ace66f97b9ef140eda3e578da58e12b0c4d0ca5bd24cec0ad9de1f0aae19c2823c4d79be5f747898b6c1612659254ad4980954587145c25bcfc7bcd24513fb7fbd6388193b305c1f34d893636da9e249cab876bc6bee69c68ddb647f5c2570fbd44116ba2c6588b93e791d2804db739151114201602c986f95461869a8230689ed1771845fa544d40f0dcf069039547f671777d8adae4ba0da5ac598760d03b4a0e2e2debfbcb15663d48f629c06bd90495df5a05c07a4aacf80db859c09d245207ba23e0956ef6f43f3016cd3a16916134f0f30e7a9353c7ecb525d6a226a4d7b349c6b2cb215c1333ea2d15d89f46234fcde79a6ae903dc8194549b92bf37fa219e3847fa332380b064dd59f22d5979687959c467a626d72bc67dcdb272daa654f1e5de7bfc1753944c74f252b10d2a4ae5ebe6a5f8394bf074393d902c6efcd5235081ed472513b81daf4b503e9d47c61be4c45703cba89ebe3347a18a0a87208087cbca1983ff1b3e723d251c254ef889b459219b56c9de83451f7094e5686ecab23149bd755480e155babc78e1dfeb981650331250ad50c773c662a80c521d5493b6b56f4200127656346cda7705b9879f7cc66fe32096dc3b29627082dbc564b34ba021af2300fc84ae6f1d967e4c230b6bcd931e90f553dfd6728f4ebbea547f837170d218c2d2e12abbf0e8721ff8678032bf4d4d62027b58b0d26234f32b61b7c2c7d91619375132318e552b6703061f0b914192b04347c04202e8bddae95e7316edda12a71954f1c0704256f60a180bb3945e00ff5edfbd2056f61610e44aa5d9c5c74efc9720ac89a5c3dba37d12ba8e20b6fcd89db55a72069e3eb9892988392f3185035c36a5cd1aa98553e0c6524378377dff4550af1272462c0881513c64b2a36304cbb5f0673f25f1b2c23fe37cf3f61e6b7f610041ac1b1543baefb5c05f8e135161322189fb52dc0f48d205a1b4469d3f47a6768244ff729077a9e0fd3eb881ff244c1c6a1e034518c85a9990aae5080cd3285f91d23bc261f6b2c62274e887a801fb717eb48ffc0394b133bcaedda178d35be6021282281b29500af25729a69d63df92ae7a38ed24326954b0028d91bccc607371bf84f236dfb952bf8c507b41c9e02828eeeeda60fae8e851939145e2f31726f2af3ec7f3dac53b2ab60efd08863f1d8f007bd851f6a4415949fd7877fcc57221151595feadfe852d6a6104cc1ec027efb3a9bb0aea0bf1b6715826bb2d8712117ec6ce953cdb96931186fa1a153382393864e9da9ea6c814d835f9f3f1cfa0a0953d6acdb9a2b0ee74018584e4b68f0e81092b375c04419c008ea6a44f37e221cdeee906342a11f940350bd5cf4d30deb46f036619ee91884fe5b64189899411adcae708b69806db24bd9c4ce984a56a5761faa3befa8009f863b54a1346e333025e5975353f3f9f00372473e4cb843e9c3027367a9f3050f91563f19912a9b08ffc15b607e5786241168cb7d80cba40efe61cce234f567c163db710e5437720dd7a6cd1d1af665c65904142f41910247881e72c2a99af525c79b3944bc526a0c60c553eca5a1ba6f699890601f6bea59f612944687067a49eeff6af4cc194626a76ae13d56d858c1b172cca005ecd2485f74a469226a3d4664f4d4bc8986b11ead53845d95677719c36ed2cda007b01139be8ad9a496eb700a8f25203d091f15cf780427ba6f545e02c59df445463b2af51b6b3f7381276e6acfad2dfe6b0d2abf08e79e7052e6f723908e5c65f5d8fe6cace05eafacda5533ef8f894ac5160fb90825ae09b542780386f8844d77ae94cecb831fe9686c93ad0fc1b0d40ada1cbbcad58aa5834ceb17b57bf4c04a7e365ea6bc52759b30caf849534590eb3f1a5b815544cf5a2ee9d2a9d1c421347892aa0b527caf180f6ebf4c0c0f9d7ba6143e0e69024c0eb1a32b8ca2315d67fbbddf7d77d90250a9e2909cfa7d965bb4184e1975f16650a489901c9d18270414efd99e1557c7bc31aac748da48763aad10bcf3cfd2dff30bfee0d3517c9653e506aff2b776870c108282eca658a306402393c386217d6af987707aadb011bd56c1524f376166df9784f42c36004f36b106c2afa3130f132b708e79ebc8494aae79f21e15f24197fce5790ed32b7d4877", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c03d33039d45432d4999b9964018d8d6552808849c83503f50a70d594c01b441900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000a81b7087e1431ea26777fc20238aa980000000000000000000000000000000001991a803e262c7a38f41ff63f803b3c202c2cf87674c88d6103108c767ab2ac48e35bee25bf5e92c02761b47af09b71a1301f6e7fbbb73aa8fa2046f5a9854e60b7c72f0b56d2cdef4e3cb5e3d1a660b0827372d8ac27abb349e731af617d334968a1a677756357ab1b3eb22654a72341345ab0ed28ab1183dd8d63e3beb4a6335b88bb03a12fdf4bd39c23c8ac80cce0ed445f4ba59e36bb04bedc22d61894e7f47177273eb6efce052396d70477f050dd6ae6474cac3235c0eb911e38027c416320fdca3d180736d3a3359939e922b2811e91f225484bfbc68da2f299388cf367b3de96e1a14934bcfdeec50d9cacd2aeac2bfcecc16e20fe23d8faa581b380bdf19f50db602737bf9076c967d4a63" + "proof_hex": "0x0000000000000000000000000000000000000000000000055ce842869ecc798800000000000000000000000000000000000000000000000962d271f2a31d7df700000000000000000000000000000000000000000000000cab13fd160fdd78b200000000000000000000000000000000000000000000000000015b9c8ac1f7af00000000000000000000000000000000000000000000000448914ae5744f2cbe0000000000000000000000000000000000000000000000065a47ab8b7ee000b4000000000000000000000000000000000000000000000002a7228f267b2676cd0000000000000000000000000000000000000000000000000001c08f86a603dd00000000000000000000000000000000000000000000000c08e7c3252f76857a000000000000000000000000000000000000000000000006db65bbcdab667efa00000000000000000000000000000000000000000000000a55066ba466957e060000000000000000000000000000000000000000000000000001d9ef76020bd9000000000000000000000000000000000000000000000002b97faf1a204a616a00000000000000000000000000000000000000000000000362df9706d637d11200000000000000000000000000000000000000000000000f0cc2167e89450dc50000000000000000000000000000000000000000000000000001ef17dfe0bf090c486ea55a1ce3e9a1c58c6d280f64c6cc337f4aaa58296bda4c37e368de55080298c60ac27537b0c7a66f2110eaf284b8c1ede53c49f2e385b77545e4a256420867e2ed0213a17a18c04d0b9dd4c887e3d57672e2d949b0053ad848ec932df90bbd48bb079881a76012522639773799dc13cd501fb540f0ce659049daf592d321c39bbd718505dc5221412f4eba8ab36cb884b887395e583b5e5f6773adb4922963e5f0e9811528de1e4d3cd0a804030827fefb63c74509022aaf5c3c39987c2b93049d138d1aa49ff2a71fbd10acbd91e825852dbb160268db8eb12b480ff61497dd6efa3c6957af87f0623eb5f7abb82b17d4db53f3696f4930018071d10e00a8d860ca77e800cb86d452df258dd4f7383c08e0692d8ddcd73a356c7ada78216294e1fad95e0289e0028fa1fc29194d2cbd14df27e9b5c0f0948d3c0725aa1b202607c7910a2af9a6fdbb5365e40af6f2d529bd97b708c1e5466394c8330e0511de6045511cec4ec3203417aee6b3055924840feeee2cec43ba9aa419adbe1a8a812d9af9d03eb346ce5969a7b5fadca3b0f31450b95bf2f0bdc77f7361e401d563c3856b087f8bd2a15d89774d92dc05b38e704faee3e675238b107b02dc23bb6f60966a057bab79050b8e2f6f5e0eaa3eb8e908371d14134ed57a2806560534f9bb4dfa4115e83882a3b2230249b8057ff8e8c08cd1d52245366811ba341a69016d7240ab6dc55923034e998618e95b4c6915158c01443836eb5d253e99203273029ee38fcbb0bb3e0a804d5eac0d2243a734993465dfe0e4f997f204f0304ff20195494c7109597656675719513d51e0509f06342b4bb3a9beb1f505dc1b858d5015a691d14be28720789d7f5331fa146dba6b83dee83542848bfae502099ab605ebc8bd7b733a5f8699828b1be13dc3dd1e77cb7cb4aa01869f67223420eaa1efac3448573bda5d1ca8387ce8d772c9247e12a38fb93c0f517d341ddf2631765b99f8290a1b3ebbb2440848fd666c891bcf6bf3b70a2fbe53f758e42d089f0a311d621d74eda459982c01bccf40cef4fbfcb1b31d9664f6a50375198d06ba84f6e6ab7a1f7d7b1144c79fe1b0a37b9df6d9322854afc5612476a8adf12f6d341b75bd522bd581cdc19afeef48c3ac919a365339f4f128e5bf5f955a350020c07d403078252f358c479535c8c4b819462a33eae0108b1c909d72a954ed1b704afe5c0f2093bf62d4c255e822d7ed58bae64eef3e2fa1f87055a8954f760e2c0c7eb770bb847105517ed113eb7fa577842b56f05f53039ad97bb4e663c40e4ced08d2214235afaee699bc67ad793e80652267b0e49e7eeb82c2245efc22043539b3f5902b00a2682bc5e10149dbc78edc127512a8eb171f570934d796a0040d351b9f54e8d13541f6fd1338a09480bf8a185e5331440781129513d45c8124fb65bc2766404225b3893c5fe4b369e4017f7090463c07f80d1575841d85c81dcacaf5040ad23865b0750ff15e6ef73f8efaa44adf5de2e8faf23720d0dff214d74858b573644f746be70e92cbefa7eb8052d2750862a15ce2ffd17c6badc61b286005f0b0330e46e603f16130e26d1254fc0bae1a0cb763b49e53287c845d15496ec760d6b385137d7e72bf05688a6ac89b1ff09632650dbd60da405d50482d51b0462944fdcf8ebad761638a43c5bb6671e66ecc61bba2bebbfb8eaa00ae221f62491c1deed21184a4b3543c02ed8a56740504544c00ac6c5943a7627cba21c011fd442067f60600f8ed7cfa65e8105bea7b2560c2a3d531aba8d978c6a61a46197c5cfaeda260d1e5a3d2234ad4d4d15c9809fc0718f2a494bf6c06b8882c0ac29181a0299c2c3ddb6d403109bf38985cb6f7f65d9bf4a27ca16eeaee74113ca3fc1a9872826e441eae2c925c6c6b51b069cf13230ad073ba2cfcf689421d1d968f9997bb791f8fcc0b33d93aaef6fdfba7fa52cc956976e2d233ddf53f23b86d4a0ba47ff0bf3ace001f560024700c4f17a1a63bddb95d79cbea7fc76416edf7b3dd77d39111fb2c428eefb92654da6439b55cde33491217fcfb764996063bd675ea62fb9811c66727b09e567636e6c8d982e6f65fad8dd6f03acd60b30d79a476b76cf8cfea809b1de99986aa5f4211755c4a9606baec8c3dca7770ef1366f4fb6de53e4d3ba838fef144fb0e61ff4ad3f648955db95d2a20c7681252084c442d538d5e4bf0d2027933173276954891dcffd109736f35c2cb1266526724a5bbc506f3d941167fb169289beaf1e5f6f44c2ad2bfc807276cc8b31ce03414720d04542483a9b95db437b80f6c7835e841f6719a3bc1eb59138760dc50071a8161bb4008dd47c756af6cf0adbac7d9d2d6cefd70d01b9d2fcf8f77159b6f1501de921b1f99d288d980a83eb9cd78c3a72cff96655c49d3c91b9a76f998c826c1b6e63fe19f1906fdcab7ce1b4a70e3ee8ccc35d3a74b5d103989a163f1671f9169b894c9b79146ecbf91ed7f2d19b04985b6598a168dcc24e13c8c937b200b687e0a9c32218ac0b353c651d22b6b8bd740b4e4c96b68d8afaa71737475b622efdad1f288db138261c51e7b753c357c6e6f940fde81bd1c23efb4bdd4bb2625fa314fb98094120d400a599da875d2323fe7f784d7f7f02cff4f4261930e6115aceecbbce375a0e0d055de53fbfff07fd8fedd827513b5dc8c91596168a2c4203455de3d5124fc35b50d719d770f1b2a6bb849170609d8991c7ce6657f027824b4d6d75e9c2d0352e4756f8b7e889589e18301ab533ccd69cf826d485669ab0fae342f1e2bc899ca929a034f8fca9268325e85ae9442c8ea29922b614c63bf26168ede3750d1e16dbc1ddb96d8c6eebcfa806091d4ade0e8b57b0939ee335719b870f19df1392d5f3052cb09893175c94d7f583695cccd8dc6dee51c016a8626bd3a072d09cb859aaddec019541f824b88f5cefc3fdeeba33890cd82ab4fb40c310ae931a3d1a5092da0589c32eab840819e36161ae4705574ab02e392010b0c6c7ee76891778e31fdf0c97a1c92a70a0076f372b5d1d2a4e947099126f32800eaa4262f52d440b612e44a8998b7d39b9d3458f67efd9bdb7c338e24b4ef6416ece81fe457a53592b63158bf83107aa1afc365e1da32dfb8eb9e88de6630ad2e6d21ec3d610f11db0e154d2317c707a68e98e7dca8ea832ce0c3b6e87e1c2410db760580d668b81b8c34fa546604dfb83521dd1e97a01a71d203fe81c483e22cc69a685edb4b1e36d77eda636d939773bfde64a736f1c4a61ed9387ff0c2b900e3aa4faa26330034c3dd9d9bd3c21273beb1162849922a37fe7515b720eb4e01177fd2e101efece02db75b0ddffe3f4380da161173c95dae55c3a2f4df8e3401d982890cad9ed87e794925f61a91a18583c26690c6f467dcc6ff3214c98de80b38863acea99f3b29382ef329b4d6a131bd30db294678b995dc48776109dbee2d1d27d807f24c1060d5d4231e51ab8491df45e58a674d0376c6ecfa11e580f610c34b0dbba1a527e1f2cb35da461bc2678fef8526b370870a2d82004f131bfb2716c1ecb028e9463998087aa89bab1bbe2c932b8ca0911da2869eea5c3ee3881760a95f7a8eb50f8dd4a6cd0c0f3e46086dd66bddf46aafb723136d30b06fb11edeacc0d11534b6cd26b54fd2a2b5b7c078625afccad81e5ba3a2b8772f460d10561dcf89c1f0c7b42884b1616e54d802a2c74714e9b7ea8aa0ddd4b6c33451193ba708854c275a9b8010e571817e0000430bab43e34a967163e4f58302123d0938e522095207559bff634aba893be2845e9f21db71bc596fb79d6f0d41c61703125e3cb4b85c6e12a160ff832607289a81e5c338b5e232bd70276727ac854506d6769a8142514417ee8e51e5c6d5451f37cba86ebd6887c7ef292dac585ea00be88df96e7954f1c240169ce169fa3d0b373e5aa01a5cc0000c3b72bca5fcf02e3e6bf9c0479a13f8b6ea053cefec1edff02097d4ae0c786873d701dd471e24224edc91988c78ec44aed74459f1a181b4338bafd7f8aa87d01ae5381d73cd9019890061fbe28e8dc94c43c56ff08cf6c7758d172a6b5e09f8c2a04d622377a613f038ca39dc90c0562b7edaca03899c28e3c7fabde8c898f45512e6e6f750d2259d786142870f0e0cccf6e5d939bfd1494ee6efec567d2e4d95702d53e365c817689c55568e4d1b54f894e694481082b2d2d970cf5a10e938ac8a92e611c67c0184d382309f13a9bdcd55b4324f675670e58fe7e9d8f0cb03169e9122dafffb088529a724422538d01a90bd4e120c0353fb9ca523d6f0612f2e40f4ed973035127a23595b82be133bf9f740c09a935c2322b9fcd332a394e756b7b52f1d987425028e4b45e9dd2cca82e0d43aabf3c06e7dd976e054c93097d46187fd10fbcb27cf8c3affc52742e93d39768323cdd1019fcdc836b6df2676c26e3978de1cec0b48e3ffc5dadcf21e5d41332cb887d517762dd280ab338c2a9008085c0e19af03fcd144adf101f2cbf8bd44412889fdbad5e6f069980d38b132ee9a5bf085d9146326ee3c919d61711627b5a6b555d6634c83920e909c7a2d61180aa6211c0b2248a4390b4ab1a1debc25b9a022e5b2c448541532a46e2c7c2017ee2175f93604fea8aa37458a9f56762d7b765f9d873789e36e7af62697c4e769393abd15171735b08ceb9d53f34249fb3460d91b9b1c3b456a480f7d21058584111054c8121802d811e86c250d56ef3b33620e76d0dcaa4c29375d5995f0d50c132a9572ff2087b3c3167c81679ed9240c6bbedf837f250be21601df31e2a9500a3f92f9a8248faa66666ba4ad609ae2434c867885545e096d2f3146e5f425224601a6852e22c3d325d8aa1b46ac7875249d4612c7640eb8bfad9eb1bfe1b742ccd8359801238fd14d6872d2c3bfce9d495bbaaebb6afb17fe24a25f9941daba0ab3da9bb60e95bf90f00951bba7a1e1059ef16bdbc1f9a03f01cc2e28815ae9008ae0538d13d6194b75fa97641ab1ee517959412ecd458951cb7fc0d83f92e6cf084b315d189b34096cd228d121201451cee19d84a4dd2ee0bce8990b1231c11ac69ff5191ba2fd4ea11485b851a68464a0220f83e25af9c67cb3cda3e41352be9b3bed121dd333040b446a4ff286f39f9ef8e06ad8417ea48e06bffa4856f1ec5dd31c642e49c70e19f968eb21fe0affe9018bf648c15e4a4b98049915ee273e5ec6707122c87bad5c33292874154f4a347192ae7fcf8eaa2e75caa3fb1a9ae3def7bf7a0be2affe3549c7fa4dd0daed1662adc6e7b521e3fa7f739115c1e5d9686647dd28b37a857b0eedb83a7eb254e0848ab6de7dc806679eac0ecb31dafcafc837cd279b43dbf378ce568e3a351fd541031e16c50da25418a3e6a452ffcfc92734dd303fe2fde71771616e6cc8e9f31664367ab2290aaabb9bc088df25cafc2be38b12bf93304c989e32cfbb9482feec80bf145fd3ef14b7956447beac4a4ca47409124d79d39370b82c7fdf161d6453b0e263139ebbf60da8b3c169602d38859f1a2a51486064f893c3e58c61c82eb3745eaf45fab83574fb239edf743d505057e52e256288e00a1a94d0a7688abf6be831c8a1458b46abab2ece16a7c500c74bc620827df055dbeda00ae4dd6ebda939c21e70f1d12a9e77b8a4e5e981e0ba946b2b69aecd0d8d79930c74807fb3c0ce1e63698d128c600f7f48c5ea2f9f83d55e0c8158c826fce72f6b982c04c509fd4144a7fee1fb1abec89a98819b14521a461712ec4dd45ccfd63bbcad521cc7c9456786140310cbef37910ef4fa76a0b9f218cd143faec452d006b0558e48c750fbe49cd83a0e2ed6b05220ea138cb7c2001e05c57a07f84c8d49bd52b3a7b5bb25f58115ca0794a123b6a53c254b3014f02198de9656abb8f641408a683ebbd20a73888d95c4288bcc101526029ba34589015f2febc8ba646c246fb7a1d8202c7f45107c6545ddcbf269ce61b07a61aee601786cf921bd7dd2698cde13a1a9d3046c90f50f78b2dcda8a2c1e35cc7a4590302711c6f39ac220de788bf6373243c083e8bf50297a88dad7513ae23dd90e1508831d3bb6140d38add4852319f93ab237b8b5d8928640f0f80a431a4b7f70941f9cb5ccd1c68cb956b2f473d64050909c7a95f1606f57181d6cca5090f2098512938c7dc06244eba5b7f503ed0ff889c54075d59b1a8be310666bcc5de3e8e60c7b9856e747bd0beba67be4a08f1c5c48fa889eeab0f88c726204a06d8ebcf4237d584209b41d95d0eb897cc54e5d8f7f122cba9ee283ac829dcfc1d38072162d01bed3a0d615eb419eb21248a340dca81e837e888d901b932f609125312834225b9f8245e3c0b04755eb9cf217e44214a7f3c951d0dcdfaa3cd68971fc7d9c1a909756100e65db3927408faf8cc9ca9be4ea96f756a9bcb1546c3d799b37a009457ec6f275471529659bd26f485c8300e9720489dcf2e04dd4e63fe53197ab06bf3ccb731a9d8709f8b62cae790c775dfca53105b3303c1e44c9c11033811c0f232b2277c0a3258506560ed6502924c739fd27afc599b8e648387d91ac59f50e4ecea056480ced0c14726aebaa6a1950e82f200e4df6c5748fb8b231a6900c1505c1107641b05e1dc8b6119e42b5930d423eada653d232d616e04e1489fc482b00394406b931e7e947336c3cc832724f048c9aa07be91219a022c6c0b5918827aa6f17b121e8f013ac553486989dbe67e89172b09e16ea58c43abbabecc27b036bf465ba153f787694774a55759e714485fe02cb757c55ab3c7493b2385bea2c93f2ca81dbd969d9c1bf2d0d8b5db32e7f9ec2a4e19227978c70c00b1dae6e2749aaeda301af6d466d7e9466fdefb3f1988f7bdfd71034f29550c02ad368120b7b37a2af19e538fc2a594e9ff6b34ff1b7a0543015b352725edde565fa3fe80da7b6c0fadabf0d1ce6334648ff88e47fac1318667e172833805213bfb0801523daf456d7b405343dd57cda0a421bc0c0d5fc2179194da627e1ff8927cf28791168d7e6d5f05174c52e481874d74feb11436ec4ef244ebe2196eef2e48eb9a408a6bb5b585158e08d13b38069e7a3540035ff6bb581e1c04aa3bd8deac175bf21226269e17898b88a53068def6ffddd5d83a05bd8c4e5c814375239fa31eb842e679021ebfb4652bd7621f5148b8a75f96869354151b923f5ee8a34941809351833faf1cefb800f4b94476f8aee226f6661a1a7702bd245d17b16dda558a7d4305d320aefdc8f171798e0514edde4a054fec5f961a7ed72e49255c37481107a1dfd33f045acf746bba0e6c61332dcb92b870146f41b6dc69950574e6d1c505a06cf243043d0c5ecac25a72b73100a76289cc2171aeaaf38ce93a601a73b583b24946b4f2d26a8954f9c0ec775af2c38cf418d672cc877917488f3635b76a62c1e2e7f0adc6ec12ad42ff86b9c2c7cec5df920fd22faa123da306058878a90170d3cd59fd235e2dd672696a71474cd0e31b00bfefb6979694ba889180a1ac46310f3597e8b9cbaa0201f6a55a47f169a097d6955faf1f5b2d40b81dac62d1374141a2b990889ceda3a740608e2a10778680050ac7391fece90953d24e7e7416007f2fd904ba98d49f13a66050078f00988943f962b91e5d258e91aee9a7333060c911e1d8c6e9d9b69bd0711b00508e3dcb739b3e3dd51beb7a08ac943dd6fbb1f5ff9a3caf6d071b8e0ad267c2583294658d13b120e1415d60ddc313bb560f8235197ccc477262e92a2856a02372e9b29b200f7fcb377481818cd6a69256f4f256de16566a7fc84eee20ca1dc3dafbee9636213f183b1ff2bf10521f63374801cb0fd3dbe682180d31952b626fe63a8718d5e106bad6314fe42c5158a0e40d4211789a6fe58d00a466c56254886fb88fd8f5adf161d1eaf20d7e89e10fece730c471555dc7b3f3390e656b26843790a3637e73695a46c52ecf5991189fe4bd22fef726444711f2980dc8dc9df57d4d45176a4c007df5613d90e0c5a9200c87a2606dc036b2ad2d15aebcc4ac9a49b7afb75ea836a55ff51acf0d53d1619edc701a4e11d6380d92df0d20c735b42f4d62e99a5b9701e25c81252da0d782eb4630bccb59e8e1ad0fbf269b8784c1101d44e54fcdf1947906973abd97647a5802009760068d3b206e60d23ec95a7f182cd51de316bf06c6ec5922ba12abafb290406c58995a79853011651568e23e0caa55f5d76f21498f9b91e5f89553dab1e890118e124b11ca3020c84df9d9ee08ecbc8ab8ece7c559eda7d0867e4fde6fc90049fa1ff83f7cd07befc7c5b81f85d89ede5561460d69a2b0208dafc991d77b324dc898ba4219b589df712283fcb75674d69c6c65a713a1faf062fecf1abadd503be4f29b6ab35252af32f765eb291b095d991aed2a8f21843c99b2be83e3a4e159042b3256dbef2c442c35edb9cdac6f6a675974777058044e4a04e9601f8920f135e8fa120ebfb22d6c6cbe6ef20c28afa1c466a74048254cb21b103f174ab275ffacb7f0a125f2501695d0ed9a7cee01d11bba2a464dd0cba2c3d1f12a0c912d6b2b3ad5b0dfcb8bed00d936563eedc89f25d3927924789599e0e1583e0d30673fd3a490618304c9fb3276cd145bcd9bfd6b588d030b3a39328611fc7218024814cd704f1b9077165aa00d1ad51111e2ac9feafd03a98cb61904a77e7018f07b69e299cfb5c653144dbcff6547858344f7fa3afb2772273d16cedc591df1a0aafa97a1920d87b2bfebc00fb094769e0872598ba68d1a815d183f91a3128851d93d8261926180df95fe4059db1b37c8062a5a9340fea877bd24924d7dc2571152f2dbc29a725e66d7357d38ef6be04141c2794c001f4a4901e48dc400673d20de83d2f14983c3cccf64941ae15789e01fc2197e4a8f775bdb8985b7c12e9a61cc62c5ca1dde07fcbb9aa9a85d8a66966cf50bb8ba9207dceb90ec4d34d1e0727ae1fca72f1d560735a23a8a7c1990de206f02324b13cd9a1c0946c900bfe320a9f78761402b99ea57086d7e077ee89dc6ce5244a739dd99e0e3d134698191410e6057e266bbe58ecf3e61cded58440bf6f46f2a65f9906143225d1b792383525fcd0e7fb28e4a88c2ab859ff5c2bb2ab8d4ff0c805d5011b500b877cc174932084174241d26df0dc266b21c8920e2d1e106c684b68129146c821cf27fca8482992c51ec232f69df73839c6b1ae4752537ed7a2661a4cd100a6091827f2eb501e478fc9008f37322ca0aed80ec3691925d4b3cb4408b52493ec88a1edbd4b42239a2bdc87fafd27a03b0370a450bdc25a792849c2e7fd69c043a1e94dfe2c3b15c8abcbb3890296a86c229f48be8986083dfcbaa8d3e85b4e6bbbb1fb671ff1122b1427f95a632ddce21fd794e38ebc217295cc82b8813e471287145d2b7cc42907469b168e2cd502a5ba55637f5e7c8c1b59651ab1fee18b1bcad3dde33ac9093477047b20e9c97d60974f166e3b383b1fe71c30f4cb82b3979ce24c40fdfd04590f98be85f1e51adbb774c3063f8b0d3c21e7ad0f52804dad372c15e6fa0e2f30a43c833758dd1abeae6005a5460cda925b2b09848e222f22080bfb0de23210acbf1fd192561493bac336f9a3778623dc4557c6499526cd9c243cf03bc38217a80198f7da0180347c165f9fe6864a56e33c70d43bef93f2946f0b92b46f1d1c0f4c2de377899cdb06913d94942b594b3b9d710d2f13390bdcd2affbd2067f05400310a01b85f77206046604a527ef6aa2bd994c358c5c43758d4fb70c57ff1361074398c50a1afc44737f5dd1d7bd9dbce9a6ed59af38b17c5306f49aea64290ea6cfbba1b9dc79135c0faeef989d29cbd400266c1daa446d83f8e6c8aaae06076f0d193ae7e368146b52012802081d5b0f6ca58a7974bee585a728c4ebca085f469ea3f7a4a87f5cac620c71cec8a82fac220fe552e4714f2ece868708fd158b335fbf8ad3c7d91867bc859e9976f3809412b5bd543cb42afb45a0faf6e11d779286cf918ed0595e91e97bb5189f90cf927856bc338e1c67fe56ba2aa7fb086ac2d3898cc932793a1f056e00093e3a6ef9de713d3c632d731294c8bb3cd929f4759eb55cac2763814b3def4dff19fce96d89afa9611f9448f834efdc8fad097040e09e85c6319e5c43158b761dd245b592335db8216533e8f65e118e061018b1fb6b0b153efa66ca702060f8e3042336680ebed3382333385f8e2cc58dac2858b1ca298937ba6234d0eb7d2c82059dfd83108448087c3af1ec1005c91e2408357ea9dc0ef0e2d3d259b5a386d127c9a90f7131932e3a0737cc76d199d964176cd129f6dba604c108e13c8e3fd0f2ab66b43ae4232e8b56837a0fcd373e200f347219fa0150534ebcb9640077dd28ad7e7da50ee7c8286e85c6803e506bf02d535d2125509c4fd6fec25a4447acd9f6ad70e24a5583221c6f6578e026d1d0301d741b1a65d8bdca25bb3a012fbab62491530ff29dd737916ab61446da60ba0565504d5d4080e32dbd75b8048f4def3d848a5ed64231ea0e07fb4bde0ad3923056492ad1331ac57fa12fec1f0c4b0c180268c2a61ca778f861ddbf150635af1b51bb17fc202d3d0f9b1aba55eb0e0a30232fa01c35b998a67059a7d0242fb31d755fd8ef6782119f4cc4b2674bfe12dade13654010f4984529c9252f116487170ce05574f1aed467512924ca000d6ab9802225d1064296de2dcaac4f26b4dc0d94b717b985a3327ef32efc0b3e0f846c3dbc221206fc80371a5dcc17dcd7c32e4b580d6cc65340fc19425065a9b8742eb5ddbd56c6b41f0a786d5213cacb502ef3267156fc3f839332ff9e24e16d1292c55a0e3ec0d460350dd6072af24f8f2755bda4b053045ffd5527fe70ec43660d5b33f016b95c547acbd302a521a3f216a40c18e811c4c022573890d20265a40e04e37fa2fdd2fa9a2a2733a6cee75e2949d7551ee07aa6971ed0840780492209926c0684747cb6e836a68e844a1b2f150b3508e5606b048c08eebabc90986b5486d39f892c495d59fc35550d3f9a3c2720c8614a86f72e6abe8e7efe82d0db83f10245f11aa1cdc2ed60c6a893792d2d943354c2d0d8be6ed948ee2ad9ac13877904ec2df83381b3b0e0ef1b51e4e62f6c1a24506789b31be6d72d9148e78f94cdfee75ef945017d71939fc0e55bda1ef846c1a2bdc65fc0b60f3f94597f12908e2ce466b873627f50dc8434afc06f22fb9f18a6989ad6b106bd7833d7e86047870be3e34f69481b7b18cd2b294bee1643f06344dc3dc3649e48cccc3eae92a18685dcbe7953e16edd3c907fe11f360e48b621148295135f1b9279d932c1965fdca8b97dfdee3d2dbf0840e79a4c0d1c16383d15a927e49dc0dc02d9d58220300035dae9e9312a21f6dd99f6753acd062b0071b37f847db9cfdd021fe78c7bde6093b9e541c5c9fe7b5e2af3cf2b2f034254be50fa37b02c0b7858a4930b25f966ca158bc646ba09446402753231db07686af291f21b42316f6235a3c938aac2ed7e02424cf094bd6d346c9c63f057078216724677689ebd37b2217d471c8beb32c37f2a51fdfdd4f15537889882642918154f6b5b5e8b034a211c4d097bfd491c66df578ea54c4eca0eee260411c620d77c767fabcd438f857c649baa2bab0dccd9330e20434a44c7e772e327a49b1c3748df69980d9554a644142e27b2aae6b6ee58d2a72e86ae178beaf9dc356315a64214933b1185f371c8f7939b051a658dd40438b51dafbfce0cc948dc108519ea0a9c3d599381a14b13afbea24f5b957d22bffd937a43ebf1c92bc5d1d86e251d5e8d95abb629213f5f4c336402b8c68f3939b81c1494ba5ac072e29877f40b5e3620618df9987720231560cdacb23f291597f193e58c17b92fff7e5b62281a419a1594f24a26f84cc6432d1d08643176a8829aa83d1452e55a3a7dd6527621a99041aa06b5b4171d4e382b0df83872df427e630f50a615bfe31a1930457b284031498574c58800bccc5ff89183a59e552d12149a934f193fc233ddb03921212413ad3e18bec4bc7a1e947e31077f01d4b79a4783270e5578e95b8bf9994712863404560ba919614e933751390da65fa688130d5bb1adc4831f4fa210c8222b6fd543e0996b10efe421d3c46dc7099ac8dcbbceb41cac8bf84de1656ce24b168dc44776fa35c889513e3b2828b08ca01ca55da145e53c60576ad6ee278bef0b4a03ae8869836c04f60f1e27f2658ff309ac27835fd7c707a970dd51563d821d871950c8f6acd1286f4d7541af2d3e02c58629aba8965cfe18f0870a77fda7093ebf63a1776d42f502810feb615ec05cf2a0b7a97e94250ffd90a61008f4ca1a000135be805acccaa61aeddc9f0630bb2989ea04d987a0efdd0107f937e392219a5cf0c720d2134b4d6ade1312aa0cce232d27d821047733d89ebc2b6aa2c108f1dab6411521ccc553953603d8da4c6a123b080cb0d89b73d6795323e33c7a0977d37628f5c6ba44735bbe408c836b5bad0a9e492ecb06a4bdc61fad1491351b0780b33c654b27179815a39f6670517fa3d80b334e7dae784604cc5e3455f90a17427bd8ea172a37c39950d1ea3579ad5faf157d54ce5aa2bcab0bba89a46a0ddc9db66f95ec2cc6d232ae1e5195fb876372d60557ca603289485dd6448eb5053f8b12f41b2f6f45248b8e6aa7dfdd5f34db0a0e5df775528bf9c92d7e65642903c7335e1388adf556f180367cb1e2953d4532c6a721e836b6e8597b84c333193365155bc9df96dd29262d9faec086f796e77ad97fee5d69707773fdd5571f2389d2f44a222144515c2bb8b93ddd28afc91a5d7d92ca7045f8ea21b573d11923cbc74a4df4e3be894d9ccb97a0e7b91b732da7986ad340a08067281dd0288512d7b6cb3a7d2252111b06fa07ebf07766993cd87cf10ee12fdde4fda032de471a26e10bca2dbea1ea37b93106144dc833953acb940ee865ca5bf5ec8cf029411893b965d3c56f12fc76f9a0c43842cad0cc1271baf8c25eca5ff69ff91ff5b30b3ca094af30a19ab9cba6f54b74e974c820fa4bd8d881cfb738014caf86c50a1b081850b4b4e5452dac5e96c1e691f8bf3b818b0623602ee87899b445d2da3e0dd93ccfbf8f7bf36c35ec45298c5e403ec69c92d0929e0acc39c8cebb87e48823184cf019f5f68ceab0613aa9a3d3cdaa66379323f27824287b774a8dcc32080bb6ea7f0dc89095890a928cf858012b19ce40e0e34a49283320a5254c9242291fe69b0164941d85db141d78da30d6d554512049b53762211584d247d1c56966050024cb1667765f81c0e6ec4fbcac11caa785d8283fd071538bc691046aa7c2305e365b6db8f9144227374b8880981b98e08c0d34099a1838d1a0ad49ae999a140739effefec3a087ed2355cd07b962251cec22071846ed571247d749a42f623021b9ec81e467f594457485759a79cf66fd19b3f0741f2e7400d3d8aaf02d3d04bcdc82e0c90264c321200e6d02c72eabc296b7b0d0a4e4b7fe6bdf8e7628602fe16c46a68890809f9de766aace365148fafe1cf51f5b53b20802ebc35ef54003d7468365c6144d5d82f6c1ad7517bedcfcad02a6e208455970e2fb251a860d0a01878111b00ec9fb3a54082e18d6a2b1830dabd38c076e10852acdba5eaea718db7d6bc92fd698ce8d7b559e17e0be53d6d9e8e25f190d59915d81d8f06ae8104d7cdf0e2bb5d37096e93f9279b4263ab4f5417c33fa4c2d98a724f8366d1e1c3d39a5254f2bf1b3766d950359dbd4ec49516cde666632f9041bc2fcc6e3a61698750845795f1439f1e5258927ce8d400db24a1ef6cc56461f95fc873b41e225facff7511bfc915315b06bb705d984db6283b36bed3ae9c9ac6766c42b2e300ba09cde96d329e9ba328a3ee0c4f497463db97ce5eee382a17ec90c591cb9b7024c8cc6c3a78b47aa2c2b26fb9ea1016344708c4042442e73f0c645c3db2bb92d37d5c3dcf71732430590d28c6b10e3d3746ca0addb56d94037156644bae6781365fc2286c3fe00a7e1ea0d022d46b92beeb231c0d53c992b356bf1067385210a5567e9a62aeaab9828557daf5eb1a4d01fa189a6e287035e0433aefde3df63178ff9bc2f9e223cc6a5619aa73a58dff836c547f862996c8cf36a8a3a500edd01bd27c1477a40cc7b58097d4741fa278af8ea4cca5c662a57a5b6bdea0b3a1e0b6d61b5403984b7e05e2c660e3318e63300906fcaaae8fe77b4dd204f5e3df90f95abcf9767c0ff2d4760d7d01ab9f29fa9fa4a8d74220b37a686f1304f014f037cfc0d94c1cfa3522a817adb950c6abe8641f87850078e2c10eb54696102b11db2b0d70eda47b883943a3dd2e4641e498066dcf6d934770a77dd6fea74c697132c4d6d924f68dd73437b955bba7bb43f52b4e751d7a695b960d9f4b83881b1134f135e1839407bce1313342de13206d55e9953b5d3ff5f5fc21a13aebe1d4b1a64a576985b3fb6fb74e1278ea93ff80bc3d2b93925e7e41e15f6a90cd2f16c29e84998439bbbe20b15bd9767e77aec9a1a439d607703c35a25d44e96600f4003f67e652d38d76ab4df561a60e84759ab15ec7cf06526fda23c8e6eb34ed5142dcb9cb1cf911dab416af6c243f32a6e9156715b80e63f856c17862f12bbe6452caa3f72dde551538abb9d2f9f6d743186054fab6e33e8138fa2a0336c640e9b", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c03d33039d45432d4999b9964018d8d6552808849c83503f50a70d594c01b441900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000dfe8f99963b947ff62a9677085fa128d00000000000000000000000000000000651719d455c726d57f7592d491c375c202c2cf87674c88d6103108c767ab2ac48e35bee25bf5e92c02761b47af09b71a0ef8e0d309ff18b3d6b953c338b01b97fa25e4585dcb3d2715bfad3f8b534c1c00d8052e8cd77545bebd67fb24f5e55321a23fd6c4cac362afd5347d83b64a0812e256a3ad805416f7ff7542aea223909155bb34e12d627f4535acee240fbc8f14d67d121dc7e34cee6e339f2837b37ee1d059676ec86d26abe900b1981dc50b2c243cf0f78fbc62ac23338af6f387e7739d8309912a551a2d3103bb652b7b5b1355bd9fb58c4b26e2c8082910b54694eba17179cdd14f23363c598dd4f5ca2408b2f5bb0cf6a4217d15c1e086b1aff8d866b732b95a45d34b55ad379cde70ea" }, "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000d6c9a4126c3a603e7000000000000000000000000000000000000000000000008cb1f7bf2a218a271000000000000000000000000000000000000000000000005220283c0e4cf6a610000000000000000000000000000000000000000000000000002591f9a02cd3c000000000000000000000000000000000000000000000006b4d77dbcef69b44200000000000000000000000000000000000000000000000b66320eaf86a9871f00000000000000000000000000000000000000000000000cfa07aed851a7101e00000000000000000000000000000000000000000000000000016c1cb8b2fcc800000000000000000000000000000000000000000000000f6b54c12ff26b41660000000000000000000000000000000000000000000000076495127c7c04d56100000000000000000000000000000000000000000000000e1ec3aad4e5396ed60000000000000000000000000000000000000000000000000000c6ac8a0fbfd90000000000000000000000000000000000000000000000041e7e2af39eb6aefa00000000000000000000000000000000000000000000000130bbb0ec2348df5300000000000000000000000000000000000000000000000251eff1b962479d7c0000000000000000000000000000000000000000000000000002107a5bda7e69199f2a94bd552a20234800516d5c52446581bda405093494e043bdcf1801a76310ba6c8e991f0f1bb10efd9ba15e43b131799be90a874af8846173da178d91200e38f0d23809e4d22a0a3767df9079919265d5776dee38416f9e54d05efa9f21026b6afddc8486b2987c54a70a1fb90ce7b0dd72dda6973dab43142f3b7c62bc2443e268c7406b5b01022182c3afb35673d07f621b0bcc7a747e62360eeea8861a546fa7cb23c3a401f794ed59faacfc55d7765e050998d9ad542712f29da780041ff587976aaf1421ccd4435f03007f6c37e7dafb835f0a37406ceaa510b1fc0437344273d8e01c1a631f6bf40db17dc5e594cc1d8e13419d94db116a6f63ef2b25a3a0f0f43fdeb8df3b943dcb414dbc186b40075f9dd19c440d4f13e2ea4f1261011370f9d84096dfcecea4ac545ec8bd01b2b552c415b27635e12737dea80af400ea25a45b8a026bb811f5f77e6e5e560349bd500475780f3af5ba16dfd30fc3ee3b89fc56bc2b3f2063c45ba5122e78195e3cb250a46c151bbef8bf71231be39c0dcfd76204d23ae6d83df17d8388410dff5064eedecbb755b8d52a598708e5eaaef7ef3968626bb7989ff7b194254d91d19098ef22fd8fc7cd6c97da8f25913e0cd96488d96dd964aea8ee93c7b50a73328f986c6d022218af78d87d921f33d4df8c65a0de3880854b70a6fd3df79f4d46d652ba9cd18c708038152e9b21fe174f0132d48a6be51930c56f8675ff96a7501ee1345d8983e3f2efcf4b3d0635ff07bd1b0feb29b73fc08a8e6d44bc17adc0d7d721057573e78b494863ac113f7174975f5f8cbdc3582841f7a981ee7287554fb5e0904fb2f26d9d059c5802319633bd5657d8f256fac6d8270e2b0e700311c79cbd1ae54bdb995626e4e43057c3beadcfd029e9fc48b0cf9556ac1e2b5bdab07f3382b71da4cf18bd26ff199bddbe6d355810c13d7c8497215aa1553a7bcd65c2419e6c49743a57f9172016cb4480cff30256429a68654722c993e0cf5d1e02273253188f6e0f2fe02b831c8dc40c2c8ae783307fa58ce6b21600dd9d4c3bb32d0a43cdfc51d857632d1603cd9cf7070c6c25cf6376751f793e33661e47138e502bd104041df30e46ed8916e2935294fcf05139704ef7e4dce91000022db750ffbe816d8682c01735553d0fdf8965d9f0b60a0d5942a38cc0c8cdd801df32e6a3c32f715606733db762c1170630083b9750f29e2c81801a47c041e265cc098a9b56fb71c880f9855157a90b5c006ee62f80ae2f4502f3a9836a0f398fd19e33c30b011925034d2279508b2fc478e725d008b82a40c48b2a0e14b6947b85165b0421150a93e5c3b9a90d0312ffe681ca700827dedf57d5e1ea269fc6aa78d7d94dd5569ec8acc6356721951df72cd0b0a898b3e27f9045435a934420c2f822442973bfcfdf3d70512cec520409ba8be9c92513ac863214905b29b828a06f6ebbd8e6fa38e5c741b562348e17bfabce3fc3da4b3c11befccfa4e1ab94ca6ba8e47a7d51670c5d445a695ca90b41b8163c2ca2ae3df50498e4ba161d1cadad55432b1aae3c02b9b5a3ba5ef6051f498e2af0bfa316f0b5b7294aca4a796a3fb96b12ecc2dbd30eee2f8914e9175566fd353df69a6b30d68e43dc4c73da4fe6b118d427601f2a1097416ca37a2ae870a24a6bf7dec462fc575c31b3e3b27afd9d9f487de88512f8568aa7b9af033886004ee63dd5244a2a0a2a244a3c12c276760d5f7b661bd55ab675d0788e0d2010921dd6930d887853fc2825aa1d4f656fdf9d6651ad241b618c67aaff811f67ffdea89b2d84a49eeaa676c46f87bf1486aba21669c58cf79984e1b5ad4e03f2d2a7620cf1dc77efaef972101aefeef67dfa6b173e359a3c8dd588da4dff0145a95fe6ac10646b4f0039fc29404b9624bd376e7aa99775661f66a13658ed14e6e8e9071187e03ff2ec4197a886923a6d63f398143b8722f4b8d7e40621330fab2dbec944dbb294003a13275ed98772a2bc7c0309bbb3f267c62928eb0bb11c2a818c976dae9ea75f1db179da83afd5fab47a9d928b359b8296b3e7c5681e17ef34746579024874cfd4a2e91b0e25923867f0d683e60629588204b3f436a90453e44188d45a90441ee017dcadea9436511978d98e9feec41e6b4b0f64c9280bd57cdadcc695310630a881c2b8c879e50dbbf7af09b160e3af7004a412562b293932c7eb279063afbf7b5e7e29612bd37742ed3e78b96625184b4c2ff16ade00b53c4ab08dda1da933d9cbff8684f0089d20a9844970ebf3847467cd601f170efd71790b8f8f52899d4acb3013fc16a232704cb553bc03c0bf865bbc23e6620666d9e72d6636e942cef862dee9fba615c2c22f1a34c0f8173e63b12696718b19f9622624b218fcb2d87cfb2d1dc92442219873c5b79af021e6f240b1cd847c1aa40e772dd587203f5f419de22222d938236825aed8171228c123b47843a74e0751ee5b40ab328d7a33a98cf24eb02ca51c5f2023f5fd95fa43b5c238c096ac0ba82b1b166abc43e4a1b1e278bb0dc077809363661e34a1c30ef77c602a26fe28e63baff200a767377346995d6ad4892217a4896553be0ac025e1ede41ef53c0c1ab2df79278b24ad61d06ff2cf1e99b92966957817fa789565baebc4a9fef22c02e38b7cf2d338b14dd6bc9e9261c3a84358cc539b268d7e96ca2a560d62912e053dc8801b13a56bfd925be4f10b74a2d33db592593fe15f5b6ae37456a3db1bb592240e3c0e590ba7c7e570df56c52a8503aa726b9cd5084ef22604894be71653cd1372f1dea72ea6bfb27392c770c26eea1c428c34d2582e5b5113389b1327eee91a17d2d318619887eb7a108180798422b4e11839a665595b54dda52df423d6f85f95466bdafbc0b2e538d53a7d4f733a199c2cda89c3dab1bc31df32c50ae7bd187e664d05a964be01278b6f03b6c6da1432a6601a8ca27279f48a83d820de21149942e08fba15ef61f8f3c0260c7fec1e896f45fce5f717e68e6ad2e300f38827fad7d0591c8ecabb0312d20b4ba0d020fedd669cd16a2f249156de381587ff4a99c4c13e53f8186f56ab498d45067270751d5987782280866b8e377e034e8ab6590cb70665fa7966e3d4bd41536809913d45a3cdac73d54ff80d57b92b7935cb1c6243785dbcce5290f25c7cf59d6797fdaa8a2ef5edc5b8cda7ae7c0e04359721793eed6b29d0e1184dc50ead2075cb3569f05088fd68cedd4c38c82a5912260dd73dace2d9730cc8a448f9d40ebf74c49e40386bc6fcc0e1ea13e922cc424204850dda1c80424c5f4e70415783b45deb7a73e5ec46d53eafd10b0d2a4e6a15f72fb98d519fd708580ba6fb60d00cc7bbe849c8e5d574c6363800171605ada29a408f35d469ce8fcd5b63cc85e07643e6e9cfbb9159e74a85dfd3be0a6ce1e71fdef69fce06a5b6727a42a067ca81b7703558c0b1a8633ed66a5d75153bcea20e7baeef5ce7809e9b8c9f45086236128821a9b67b5d6ac74bad8b3c29f163c95c73b6f42f5807f40b55957a876eb2516a2825c8fc72f236778358a10696d52a36de7e93b3cdd1f394d5468ca71509dd2cb1afe0ca81276bd2b6e0681b663a1debc6de673483e96ce68c81b360440963fec2467d817d22c0337418be2e5201dc40241fd68105880e2848a822ab5bead6b956cfe38c37d219d1b181472a0d5868d78fc5d850c491c9532d1f59668b9a56026d3961b0611f3c5104579b265fce9fb13c67f876520e45417c923ac9aca521380e224dada03f9ebeb77c0d10f915f95affba13b231ba5543f85eaaa0ab392a90f3e93e4d252747863c86bb2b3bf82bad04cc91244f5371a7d0d68ab571456e77a3f6ff3e20cdb96161a3a00cfc8472c1c127c63107a7c94d23f8889452efc2ae6169659384855065b6067c2b24571e4896c3f2dcfb11d3897158601a0b5471282ab5891952b06321c532ba1eb386862d8d3b0686a7c99c178b5d3a32c7366ce8b8ad1062b6abc970821e2700469ae25a747acd5c3c6a03af6c7e85ec16627ee42b81cc9cd43aabc2416ab8265f5ca8f478c6760cbe3113c6b93497a0539042a7a3af49921ba8665c65cec32fb368ded3f4d89f8bff985f141b608c2bced985360313e04b7e19550d6df36806c0a29be7c7b37c27266eed5abf57195d855358db574843efe2a391b89ce88a272a33820cf99cf2088d8910e313d0764bdd1ca0e5bb2bb820c10c95b3c00b45250702d02cf0b3ce811e4dc5ac5b66b362325c9b68038cba8aa5232a79f2f45f2725890d9df66abf32181f24ffced0aa242cc221894a4d6817d5020394a9360822f251caf9082777de6b2b15f498232343238d0b6e90cf6921e6c6bf4d195171276bccae75dd0e48d618a01ec2ae7991247be2be45dccac73b20c9b2053c77171c28b2fd03c8d84440375e372e41ff6388f7bc589de81ea6a1d7ff4e8b331edc04d4168c04e70b35a450c4afdad791a4fa3513b19d276a0fffe50cd1869dbaae213a8ed73c39708cc2a4d032ee933f66e1b87e1973ac316a2026597246b14ef418188aab93dc1cc6e59a141eea6e7720bc28ae4c8e2958c129122b2e04399ff41063f60c4decf651d58774db8f5b881b26f22b0f01a672bfea51bed9ea26faab04a2fb96a880434196ca20aaccf4c97788359624aec9211bc108805c85fc3052288af408b04b4fee3556cd03541fbba5d2d5b14e0f6f828e13e2ee735093525800959b3fd7690e499cffcb85593a4daa08b6fcb8efea6777fdc54e28727458c81ade87c5fa2af0051347096a16c332beab21ba626d6e379b103ba7e543f6f5bd17c14cf881bf41ba471b821338b2dc52932f26363119366f49d4128833dba8290bd01fd27ee7340f46e5e7a624ca523c38def233755241c181bf12196588dde9096521920f4da8a2e09564ec78a72d92dd426bcccb070d4584efe10a39a4107d1438277c3f16a7e143ee690e0a852c9f740db2b8b4b41540f846d2aef734ce030a51f13f8d59a70aaf71cc7cc12fd395a4db636b5e74c83432c41fe61dd4cf9d03f7e1a35ca11c2e63e06dc48e7699d855b74d87cc50391eb8add9f75130db9c006f564cfac2a2c9360dd4f4707af7998aba1e49e881f5903b7667645bc81f851f138e589d303fbb9cbdc2f8b5fedb9d26ef8ff61fa38446c82a07d11d9f6d7403aecbb153c040dbf359d766fec4fb69487b910064c9d80dbbc212e040ea14ed0e3382b7e1b8ff62702059bdccd159a537da8c15cc3d934ea057d3f57b1815571b5944be7baf52ee28d3107bdb2dd8089a3b393012278996570a17ac785a010220f67ca35abef4256ac6dbb8acd27271fc42c11655cdacd5092607fe9c7ef105079e12fd6c3dfd3374318c9fc2ce1cb9042fde86344e62ea85205e6cf67184c214e2113b10ba0d246b330b65a2eb0eedbd559f30b76b2219d0299886433161e818cfe2068fe1012d9ab865942ba25fc3f5ed0f0c94bd74a189d6d8d8235c29a300a4a2c70b7eccee907343da09fc48aa03434a327bf4acddbad2e7c90f8e12c72e91d56142ba079d2864270121fa800ce229172d6d15b7edeebb006a61f3d93e2b5fd60cf7cee2787e78b919dddf2c69b16e56dd21f1b893af7a7bb42ebdad71279926d9650b95fa6d48d5f846880d83a759ee476ac90e4ab08ae7e9bb7254f72a1f9abedf335b7f3e5c194aff4aba68ffbc852ca3b2c1f9bee4ef135ceb63f5289dbfe20b413366895ec66b87d4718e9446e20eea8e80b4cac7e1b9a7988f4a25cfe80d31fe157ce96bb88b314e9b41a107335b3b433ff463e3aaa6cdb786c82fba795d5e57ea0b79f497a5f6bed28a25cad31f1fec901e10e19df77e7d2c380dcff49f691f6f96120c180db4f6f7e6a59c6e49b9974ab4aed86fc6715af5b70dd6e5cf3bef9e6ed6e8ea8f5657a296e0f30627533f24d73ff70c7a183cae5f00b844d94926af4e01bcd9a890f34c26c8e52f7b43c4d92d060abcfaa444dee2216ac436e277931eda67d179f8aa67c06714344452c5774ac784c30c441b638220ad1975ba226f13a81410750450ed3f4a6a02064578f28b111bf86f26e0b8250d256e1372e7e3b5dfa47445803a509501f547d05fb3a24e74a34af1e7d45df704c57a824adcff7851b4d7d67cdbbade3114f492fb634ec2e088f2783b978e5121515a83b8b4a6950d28c922efd67283140ce93baffb5d15eb72ef544c5c2e531a051ea8ae08f1f87f55fc446f7fe8e4fb25093dbb38bf03a2e399137d541b2504cd4288543c7b1c51cd9f54551d339149340ebae8a52401d0e26ee5341d89a31fa179ce5dfd2a4a9428cd64be345723b717dcd64b2929ad87b243fb7bcb3bb52339cd359dc854082703b9fbc15e87e87f39589a2054bc9828d92ee0b9cd165c13f35b457b0b7f484e3025192770ff1e333014cff16a594775ef17440e92212227553ce62d0ed4fc5417fa3d4863a6ac133aaa1ef8a238493863330d26841be91b6d417967ecd5590fe852fa8c692b2da8a96d836aeb6483c921a5e73bc19366199a1b5c847d7bf11b1c85f29c33fbc6f57aae6d2b799c31b2cf855e0c221ac623b42500d0e4995dc9729189c53e4ad9a8918244d5770d1846b14e13b64826b918f632917e0cf629542ba751f9ce57df94911db2e9d57c43a90a6b0d0b325d3f0be9c1625d2ac88f9915da498f34a6613cba927f2ab56416083992a378bc72aa09af520fcc520a1a8e5991b2a7271249afb3c848d6057ab4950d92579f62f7b5119ac63e6782976db8e9977e3fd5289a9296b70f899070308c729839490b27581ff7d90a4fe1578d2f895108b2c28c1c1b8ec174a37146ff4397c87b0cdf51261b88c198f4bec0b60838a72080f25236b6f6db072b3a4effd09d378c84d82bc703beb7ebf047d1b3f2bf4ece8b5e36284a1cfc0e0b57b862d781cbe696e77e650ebe5eadc8e3bfff1a4daf73bb4dc7ac9d88f49208d4e454f8d52018fb4c69570a3b0972fcb7a44ba524e43d779bcecc8f0f91d2ed80882db28686113fd6cbea26cea97ab08e431c705da6558f9c003d48fb2f9ac5f288c186cd189909aa9b7f0b25cf05b0bd940479d99dbceca591f5e2bdec7636476cf3ca59e3c4f912594200f45c0bd06867a9e8a24df50920f4d827a373be6582fefc1c9d179525908261032ae4d0bc4e51053b9e551927c993e92036b1a23f32832fe5040b9d0765235b03fd56870bdd2ccc031334058a9ecbd853d70df1f1840b45b98848285447b4a413ba58b32a3d3eeec6af4ce9ab146afc6551817d059f8e5612dd6d31ebbe415c23d9ba4b98e0ec420542b8fb7693cde2a6461a996e9b3b4d203bf66515a1af0116671f9de44bfffad700fce97fb29d638d0adf936f689b8c806fb26b26d89fa30c7d6149448ca434faf792c3e6a2a986a3ee36cc881ffb75f14c78d8245084a703beddd3684696503f46572e92f37ed233472a287f601d3751206d83bb17173c22b471f3255ea19fcac979fc8cf5a1c4acdb637b9d06352c8181bcbeef1e8d7c1222ed4147920c465875fbf43993b7673e938add0abca2c077afd4146d36d7890232c17223a3a6dbee9eb4b9c926a9f7972d06a8f4eda222b6a53f055be94f3116380494ecd15f4e289b533c34bb469e60a891e400696090772f3c29c3f4df121d2e1303f08114842b9c87903a7f3bcdead25ef8186dc00e3454c4f452841faf2893737960e1408a394ed081f3330b6eecbf38da082548930573dd0502171a3208a04300a6125ba86df0bae3f730a87bc9c7bfc78369d73ece8d708442b71d2a28ef0dfc69afd35e8d6714ba33c3f3ae54e2177544bd7a6585f6b232422ac44b246ee00aa511a2ed1daacf38ee5dbef3593914ce4bec707b8c5d1fa4d04988192a3802679d17817fa6c801337c7d3e80a1a8799f6cf86e1e54084340af41d4d809ef97be1ed4c7160a9ef5c8c83225828dce83d892a437660843010fb50b61b614ed4e745a80fd6bce6bf44b8fc9390ec64bc7ac5c1edbe74016a00e4dc0b04f0e46fe11d66f805d8b50b24bbe95d066deffe72b8f18b24e785f0507776005741bd95ce935894f12dfe7ccee1906bd6ef4d272d823f5bb92a0229f0f29f95ffd181491eb6278dd5f17ff1fc66f8d4f14aa8b0e833a93f74fe818e32db2dc767c15d5d03ee42965104bbce10bd5d66b871ba2a09f2269cbba8432f52327cc1e2710758e1804ea1fe8377f9a1b6b4ba850efc3004cca5203a7d7c473b88d91e6930b1c7c195ba3c47a130eb5e6354018b8907f00acae6f53702b63b3900f6a184c07d52665a7dd1dd94916dc7ba95cf75c8fb5f07e3ee904beaed3e7a58e6c17611b7048ff802d22dfc068e70bd9ab87a251fa151230592ba0c82bc43eba06b2391aa30bd3454373d556018c830c24afb90ae0d29d4fe1829478826c7052e092a32f27a9d63d5defa8f03d813eb6572fbff32dd85d8987a76d31daba9faf2cbed424bf6c47fbdf816450039199a834ec8989e7da9bd6bc285721d08af2de1767a11434eb257ebbdc034b7565e0cc0ceaff92f19dbf1431f32000b178409512a2ae24f4cbee8adafb602f38aba2f7a0344c7135fde5f898a69e622f0243ae7f543c1637128680ef50cb07eabb8bd5f303c697a150f41cccf2fbce4cc643ebaf65cf1a3d199248db86f69b82ab604c6866c26a4268f1154a8f94ec3ceea3531bb5fb1ac951dfa99496f21cec96baa359c2c08da23765578d5e585d527e6bc043d3da29612378f918674b1ef552973770f0c674d02dbb4f63a8f5cc8f896ba59e3f900c077ed8f8f6ac6083501296dba428a1180e1ceb6ead1e4fb31bb4fe70079d720d4dbb22e823d9167e12fc831d7092d1397f30a1366d5cc8731ffac2e0db9289080a91eafdcf29d90fb797f81d785dbf8e2481de57829cb2f9d17bb894e048e7051f449250b7c31255d056189f6c90090b81be3570d7c71ae78cb0f5169d6f142f73130d9283d0551adb3c268d1e169333b5864a226b7b4f86f7e24c44a991592304ff28559b195f8b32dcb97d1dcaaffbd16fbccd854c568e3e420fad1a486e0caa99c46aacad0268870424959687352b688151632a5dd7a5efe11f95b645fd1df236d479ffb918af56dcea8fd0781c37fdc3f7cf0092b7ca962ba71110aed51249b400935c39ee2fc263a7d70bbe850db6c570ce8aa7a7a25978169b7e2fff2a94971885cfb3433f62b6f6af4c3c129461b5613d2e12cca0772cc52fefeb4a1a54283b497d1d1e4fe8cc24b6f6cfb897077f1ecb105e53f2e0df0295540b1d24be8120438db8243c68d26e2bdfef3da4f1369d8dbf7866ce3169772fcaf62317bdead50b3feaf0b55b2163f8e59c60c40c5f1bf659534219fcec999193055020bb3a4687b4daf25f77d9c02846053c7458442a26410638a42baee5158416e00b240ee91f5c3805f2c500ac2231bd4b896c030f4e9d5f33430d3340736cfd9108a10e9b4dc893a57a260421736c988dc975dcde25675f1f5afee819576d1aaa04a4b84cf90de3d13757e48c22726470ca958b919db4296a41518e7ce62e7a7a208b17c527b8a6d1b068ee2f4858d83c6a748110b20fffdbe44916f3871cc6d20aea3df516f83318de00669e8124e47d2d05fad743b8a9d8f0cfcddce8355c8217a7e599be1bbc649224b1a9686f8f08dd6a333966f9d67841080a8285d912d51d0cd7a48d3d797f1d1da9d14b3af4d569fcce49ce0a08f29ab27e78df5775bd0da3985654618b91578ca1c44e9118e97fdfde9a44c2c6a01dd033e1afd9502613f050bb5f40cfb1ed125552ae4bcbb02cef49dcbd8d2ed1df54c12489007935023d9bd5bfb68106a5172232ac2f0adf0b8d46bebd8c319b1f293ec6ea0dcf2a1bcfd340f0a46e5d791d7d5c947b7d7606747d163c56fdd7dc1e5713b8a720c802f68cfec8f3426146da621b15d9183024fc5dfc6036ecad7d337ef4aec8de650465136cafb39b8b1d9e4adfa84f973c3e7c74aa3e42ce1c90c3e216cc26bdc80d2623a78eda113305e72f2f0723d87ea6444f1f62b0edb667eb67d5c8d43f1129c6db6410d513e3c04be3a3015c2f50235937731c558e0bed04877bd7d990ab2c551e977d18eb1d792ea80e561018cccbedf3c84f79e149cee13fe421a5c0af2339f442a974492692c512d4736fbda6926722de7d821cf2d141e4cbb17e516e13f2f2201c7f219c8958fe2fcdff5f4e5c4da2b19729e480820303c8359b4c1f16de9e6612a1230f882a6588c81e1d9d1ec50c15e2fd56eb57df3352d7e9c97b2c3cdfc11b2b771ca6da29f5dd9952868f39c8171074260fd700ef04dc5541521f36e6f861833c85d1199dc6efe365b8415583b002816cdd257b59fe2b2813bd0675f26629c1451b66772d23c1264d941b003abf41a3b7ec72799e08bd23c93326d02bf901b3314b130b48d5697d7ee7b21022dceb0de7a693a33e43c10f77d624b3e57dc0513d9ab7dda81c2b5230e9a9797ed9df075b1a9a04388b75e909bf13fc6a8e0963a6d09fe6feab2ca94fad001b3f2303b76b0da8953a6f7b864f4a10e5d487df213f00453d3b41de3f2af983ebae984c50722e4070dc76cee5e0ce2dd35f69ab5162d3cfa3ca47416420d1a6ae5736872c95f3cf4bd758b053abce06a3f3d27234d5351d377d19dbc31128c73104719af176fda95db7204fd783992e8f4d59931736663a97d75ca9fe5601fd6a89a0aac7967616304106a12073271eef8a88a6e435eed5c639b63aba53bdc40b039e0fc0c93d610cd5d8e66d17471ac366ff4f4c0a1b6cd7f7ea41e2357e1d43ca2d5b0ea75f120369fb9c8a21351872b158b652d6c6e15cc381c306c1013b6e1102bd43e75161742415d7c65456135e076db98d1f67573f1cd2d3937af97a66e2920f026cb6af506b49054bf5cd1bbd6f7cd5f654ba5454ea1f6afa635b9836ae0cdc637721c23fec7e36fc5b181cf56fd52dac7d9f1058fe069388c8d8f4aca742c881e2ade54160b68a6b36e51e973fdcfd0a8bc1a8c72682743423f4270dda04a6be272a1646a15eeee9e9960019a34eeae05129cd8c9b0d12ad5b3a1b4910ab269cea75a18cdc6602c90a3c2b0a7c79b306c76638f2f2eb54cbc41109fb4780479e8dd882dfa563e655b300177732817cde20e4dcfbe62477f7bd375d66ffeccb335787ab4069c3ea492fc80b24f697b3c496b0e39b5bf7a1e985673afc24a32ad2aec7045991e3a80acd80181dd6b84aefedc94d484f80faf20e43cc740c40eaf29a2eb6b459cb834db07912804bffbcad366f36d02398ff25b1b7d10e1eac43dffdc071bb0135bacb68ae0be269fd54ecaf04d6fc6e8ae3865d1a6723ca38e1a837e79529b1c53f8652c417a5f356703cc7a7728e38472edf6d2a7322eac5e1ea44360d99380c7f1ea79f067ece250223c72cee699b995d9e03c7e4fa7559f7fe31342cca0ce2cd31586311c664bacd8676d116691e2111774761f13bc228eb8c5246f2496c79a08e275608560bcc059db58b4fd957831e60ef939f1785f95e96e574f211781f6500937b02f0b4f9e89a0ac6ddb388fdb8bef0bfee2a1fa8b55030be4064653058876cec1bb0d419372104957e3826fce12a86acab2edb95f0466e8d974f72951a5850b42c417fb7da12f369945299761215b461b853087e5c72e543a420fbe81e08be59045685818d5d7b478095d711c6eb686827f6e20ccf3e1e378e5b1ca7df6324081b9c26e478a35ac337c3d835ec68e0e328fb30f2c608620d4c0896e0e57aa0180b17223b0607ee9e21a031712eacb1e784c4fd08540014cf3fc8d19726feca721e365bb366388683526e599a294927bc546a4d5011da30c7b2fab02efbdb18ab035ad5bbe7bfadd53b0885c220a897d3d8c2cb428e38273d250245cf1b65e0c81b39b2f0ecf38a82930cac7d2400315f74b76e918f5914155e9d2a59cc9cb4f107599dcba86033fa5b95e6a78e083a46613528b12d9df4156903cf53deefc83d00211d70b9cb5f95a01fda9d023008e546e99e3666b50e92e252bd5a24127fd3186841ec436a72ad2364426aa74bfd2283fe3478f06feced6398b87f34378fe81b599968d8a2284d9e7c9d90c75a2690a0b9654e2fa9d0a33fbab902a53da9160119d39eff9c63f7877567d4fff228def31cb88f95fcb9e40e7a5843472c2f231b185edcd6ca67c84bc81efbcce3e10a043706c74c42ff30e668e94b53f12eb70615289857ab1eef98b079d754e9f66c61ec68d56dd23b9bc806c6710357a1d92ee58e02b874474f90a619c06241a5ba212e60a6110e7f09bd3c4602f2e9c47e2a11dc54d9f8ab979bfb3371f85b99bf6c051a2f5fa64579f31538f959a56ad5099d741e40b4c985eca2a2fea2e28980c9056f07465c983e9fa28d34e104f13015cae6067451d4c5d18715e725ab97558b7f71519c013c91f4f00ae1b94e044a248647b6d63e0ff1ad866423770acf0264ba1bae38ba54540bfda09b51bea8c426b26292e241b03f8e86e62cbb982df0590a043c213b648e3e2de09c43af183918b224b74f1449fadf68e31b87fa1e3b8874ad883fa3f57958bf5580f487e35424d96689668d012f282397cb8470e003681f6327131c6dc5abc41ba6795657950729b0c57a17a548d7e59c09ab4d0af29f3986683963a4af08bde254ecee7d7e24f4eb820d5e11e1284ef470949a5caa0d59884b2e059cebcf42aac0ec87a4c2009982f7f2fcb6c54324d1ed3f1f2d20335d2c79e56669a45e0bf2094e53dfff22ee388b1d1874eb6261be6f441d1b61aabb25e280c3b1cca1210067228a29a52d5c10663d69b3740753607c6b92e4add07af47ad92c467bd5f150797aea8ad909f1ad4d166d9525968cba944c3a3b5df5dd3ece0882f273929ac009910aa4c029fb76bf4360b153fc4129ad496296b093fd7895225bd8c38ca9e6a13fd90f21115553ba51127185ab32cacef875f5d9485ff9d05d0462a440cdc5dc56543a210874b509ba6e118ad0ccc3e6f27f79aa521ee68c844cc89ffaf40c2c4923112404acc8365bddc685de4e41eeed9a989119f61727e61855007baa4f6d5aa47a6f069c7504953acaf19ebb02ca658da58c38e3eea0c710d1670dfd1586746f4bcc066990a6fe3f5c7b4f942179bb7f6cb3d0eff2841108b5f9fb6f0aa3dc297c750fbc9a532e247d09966e8fc9c3c08d3b93cda4d7da15ccb2c3d55aafaef7f6e720657e3b9c9a7aa110bfc42043b8e382020744ee0a38ffcbdcb176dace99c5782118e3412549d1e50c110eee24cf8a7ad5668fb5b059365b164cc6e2f9c51e7d17d578e1f940873003a4772cafd0e5e0fe61b2b818ef183d3c8442d4365671a32b2d023a91972ce6168a6cf4d6a9438a553c6dd4be9780dc514828202de442e926d2735a11b5e30445898962a6612abff243b02ecef4c52788f64ed2eec6dde32063b15d6e7b170e5159d78e57dd0649b116bdc9b3e1cfb2a5a713a546f7869000722c25bcfb1beac0e535fa3277f0120043a101b8116f60e8572a6325ac71bb159a939845b53d191316abbfd5228668258281c52411281cbdad0fb98efe18a81647a67703fc3259981967efe0e3dfb88f6e8de37db781960d5ad649b9cacea60c0daa9256ebac451ee59c50c337f68f1ce7c6c0b0d90fcfa20cb9ff0478be6a03e4bb40668370d05fb1a940ae15df5cb9c128c6daae54b73a0846e8a45a3bd31b6218aa542c305cc3ef949c30fd8234dd18cc5f927882215ab9d6d748b4518f04afa5d7e4c164916d9313b76d88b5f2d01130f40bdb3930412e44603c16bfa02751cab86e87f5a912d21a6df597b3f5cf207cbff7943cd983e7eedd529164f91fef3f5f440261cb789a9ca6899fe66a2b6f3eef2f821807c9abc02744a0957d1e2409b845583184de73db7dc7d896d609ef37de8b523289390b38b38f687f10026ec8d00353098475c6ded11fd4de5228422528eb710995599e5f284519ca7c29709736fe6a73c37092fc83a5737682a11aae30dfb0a6bf4791bcc7f63530341aa1b36a58ea685c0bc4aeb065dc63a50f54f0536a7364be2181f13c0b4d8cf607f474033d183eba62d93b7451932b6cfa092cc03c347474bf4906e0c8ddcbee13a7d725baf5ac8703784f84aeb7dd994e776a17c921b5d4dffaf56d4492fde123f2b26aa4213ed6ee5847ba93f0d779a0e33e9e76e9759d6dad50d74b225f8e0803b5466ae26cf2977d03370661ef5073a7258019a875f67b03e6dd5fd9cc512084a695aad7902800ceb7aaf22dabc4d9dc4479be57248513a57e0518b1223f1b313fd82807e35e2259298ba409c83ce0424f3639a13f759de733741acad8a10660a955e0ce6e2492dfe14637fea0770e02533eaadddd05d4d99a04f9620f4e106934ef962dce3a6d408321df0a65eb716ee892d98d3e1d6a347139e21b0ae81cc8a047185f77036988b57e5b11f79c73a9913aaa924c10318fcae1b8157a73162a5c5beee053165d8b1d1e2a63c28bd65a2a2384afeced0f75f59b2161275528e889bfd1f21d0268b13da9c623cc7ef6b36e3887cd85d9ff79f17f9e95bba02c644f4a592314c6e20543f17f0e5851cb56c4d8fc3494bc1db19de3eb9b7e27121ce1d4d7d89669e5805c2bce49735e67c217a479f00a31793c09b072cc379d00c4c5745c7492cd691c3b5a9217156aa14ba97c0143719d9437ed682fe6695a", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e4411dfdc7cb6265f100524012f038ee1f205bf8789b70b46c57463dedb4998f7ac800000000000000000000000000000000a81b7087e1431ea26777fc20238aa980000000000000000000000000000000001991a803e262c7a38f41ff63f803b3c21806572c19ee2f3532e970d617919b3aa8cdc582782dfad0699badc631f75128000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000021301f6e7fbbb73aa8fa2046f5a9854e60b7c72f0b56d2cdef4e3cb5e3d1a660b0827372d8ac27abb349e731af617d334968a1a677756357ab1b3eb22654a72340ed445f4ba59e36bb04bedc22d61894e7f47177273eb6efce052396d70477f050dd6ae6474cac3235c0eb911e38027c416320fdca3d180736d3a3359939e922b000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "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 index ae02a4a76..7db8a2560 100644 --- a/circuits/benchmarks/results_insecure_agg_micro/report.md +++ b/circuits/benchmarks/results_insecure_agg_micro/report.md @@ -1,9 +1,9 @@ # Enclave ZK Circuit Benchmarks -**Generated:** 2026-05-27 13:55:12 UTC +**Generated:** 2026-06-04 20:35:42 UTC -**Git Branch:** `params/dyn-conf` -**Git Commit:** `e6189e08754c90981a3c359911e081c5f5ff3a90` +**Git Branch:** `compile/all` +**Git Commit:** `5b66ae732e6c8351f58179b73abb25cafa372cec` **Committee Size:** `H=3`, `N=3`, `T=1` @@ -72,39 +72,39 @@ Single-circuit `bb prove` on the benchmark oracle witness (not the integration a | Circuit | Constraints | Prove (s) | Verify (ms) | Proof (KB) | | -------------------- | ----------- | --------- | ----------- | ---------- | -| C0 | 6847 | 0.14 | 28.39 | 15.88 | -| C1 | 57818 | 0.36 | 28.35 | 15.88 | -| C2a | 41244 | 0.32 | 27.93 | 15.88 | -| C2b | 79591 | 0.53 | 27.27 | 15.88 | -| C3a | 120114 | 0.60 | 27.47 | 15.88 | -| C3b | 120114 | 0.60 | 27.47 | 15.88 | -| C4a | 67494 | 0.46 | 25.09 | 15.88 | -| C4b | 67494 | 0.46 | 25.09 | 15.88 | -| C5 | 123624 | 0.58 | 35.81 | 15.88 | -| user_data_encryption | 53732 | 0.33 | 26.08 | 15.88 | -| C6 | 86927 | 0.58 | 27.25 | 15.88 | -| C7 | 90841 | 0.52 | 28.18 | 15.88 | +| 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 | 3125379 | 176232 | 3301611 | -| Π_user | 15.88 KB | 0.12 KB | 2972989 | 170272 | 3143261 | -| Π_dec | 10.69 KB | 3.47 KB | 3640997 | 187272 | 3828269 | +| Π_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 | 148.93 s | 127.00 KB | 128.19 KB | -| Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 136.24 s | 10.69 KB | 11.16 KB | -| User | P3 | per user input | isolated_nargo | 0.65 s | 15.88 KB | 16.00 KB | -| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 0.58 s | 15.88 KB | 16.00 KB | -| Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 55.34 s | 10.69 KB | 14.16 KB | -| Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 51.39 s | 10.69 KB | 14.16 KB | - -_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **21.30 s** — not +| 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`) @@ -114,72 +114,72 @@ comparable to P2 wall_clock row above._ | Phase | Metric | Duration (s) | | ------------------------------------------------------------------ | ------------ | ------------ | | Starting trbfv actor test | `wall_clock` | 0.00 | -| Setup completed | `wall_clock` | 2.97 | +| 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` | 136.24 | -| ThresholdShares -> PublicKeyAggregated | `wall_clock` | 148.93 | -| E3Request -> PublicKeyAggregated | `wall_clock` | 149.43 | +| 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` | 51.39 | -| Ciphertext published -> PlaintextAggregated | `wall_clock` | 55.34 | -| Entire Test | `wall_clock` | 227.84 | +| 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.06 | +| 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.42 | 3 | 25.26 | -| NodeDkgFold/c3a_fold | 36.69 | 3 | 110.07 | -| NodeDkgFold/c3ab_fold | 7.68 | 3 | 23.04 | -| NodeDkgFold/c3b_fold | 35.82 | 3 | 107.46 | -| NodeDkgFold/c4ab_fold | 8.05 | 3 | 24.14 | -| NodeDkgFold/node_fold | 19.28 | 3 | 57.84 | +| 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 | 49.76 | 1 | 49.76 | -| ZkDkgAggregation | 20.43 | 1 | 20.43 | -| ZkDkgShareDecryption | 1.32 | 6 | 7.91 | -| ZkNodeDkgFold | 115.94 | 3 | 347.81 | -| ZkPkAggregation | 0.86 | 1 | 0.86 | -| ZkPkBfv | 0.23 | 3 | 0.70 | -| ZkPkGeneration | 2.56 | 3 | 7.69 | -| ZkShareComputation | 2.55 | 6 | 15.33 | -| ZkShareEncryption | 4.02 | 24 | 96.43 | -| ZkThresholdShareDecryption | 3.49 | 3 | 10.48 | +| 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.27 | 5 | 1.35 | +| ZkVerifyShareProofs | 0.28 | 5 | 1.40 | -Sum of tracked job wall time: **908.64 s** — **not** end-to-end latency (jobs run in parallel up to +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.42 | 3 | 25.26 | -| c3a_fold | 36.69 | 3 | 110.07 | -| c3ab_fold | 7.68 | 3 | 23.04 | -| c3b_fold | 35.82 | 3 | 107.46 | -| c4ab_fold | 8.05 | 3 | 24.14 | -| node_fold | 19.28 | 3 | 57.84 | +| 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 | 49.76 | 1 | 49.76 | -| ZkDkgAggregation | 20.43 | 1 | 20.43 | -| ZkNodeDkgFold | 115.94 | 3 | 347.81 | -| ZkPkAggregation | 0.86 | 1 | 0.86 | +| 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: **420.48 s** (parallel CPU work; not P1/P2 wall clock). +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) diff --git a/circuits/benchmarks/scripts/check_circuit_preset_artifacts.sh b/circuits/benchmarks/scripts/check_circuit_preset_artifacts.sh index 645c7b526..52f048679 100755 --- a/circuits/benchmarks/scripts/check_circuit_preset_artifacts.sh +++ b/circuits/benchmarks/scripts/check_circuit_preset_artifacts.sh @@ -2,13 +2,13 @@ # Verify dist + circuits/bin artifacts exist for a benchmark preset. # Exit 0 if ready, 1 if not (prints missing paths on stderr). # -# Usage: ./check_circuit_preset_artifacts.sh +# Usage: ./check_circuit_preset_artifacts.sh [--committee micro|small|medium|large] set -e PRESET="${1:-}" if [ -z "$PRESET" ]; then - echo "Usage: $0 " >&2 + echo "Usage: $0 [--committee micro|small|medium|large]" >&2 exit 1 fi if [ "$PRESET" != "insecure-512" ] && [ "$PRESET" != "secure-8192" ]; then @@ -16,10 +16,30 @@ if [ "$PRESET" != "insecure-512" ] && [ "$PRESET" != "secure-8192" ]; then exit 1 fi +shift +COMMITTEE="" +while [[ $# -gt 0 ]]; do + case $1 in + --committee) COMMITTEE="$2"; shift 2 ;; + *) shift ;; + esac +done + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)" -DIST="${REPO_ROOT}/dist/circuits/${PRESET}" BIN="${REPO_ROOT}/circuits/bin" + +# Resolve committee from .active-preset.json if not supplied +if [ -z "$COMMITTEE" ]; then + ACTIVE_PRESET="${BIN}/.active-preset.json" + if [ -f "$ACTIVE_PRESET" ]; then + COMMITTEE=$(jq -r '.committee // "micro"' "$ACTIVE_PRESET" 2>/dev/null || echo "micro") + else + COMMITTEE="micro" + fi +fi + +DIST="${REPO_ROOT}/dist/circuits/${PRESET}/${COMMITTEE}" STAMP="${DIST}/.build-stamp.json" MARKERS=( @@ -46,7 +66,7 @@ if [ ! -f "$STAMP" ]; then missing+=("$STAMP") elif ! jq -e --arg p "$PRESET" '.preset == $p' "$STAMP" >/dev/null 2>&1; then echo "Error: ${STAMP} is for a different preset (expected ${PRESET})." >&2 - echo " Run: pnpm build:circuits --preset ${PRESET}" >&2 + echo " Run: pnpm build:circuits --preset ${PRESET} --committee ${COMMITTEE}" >&2 exit 1 fi @@ -55,14 +75,14 @@ if [ ! -f "$ACTIVE" ]; then elif ! jq -e --arg p "$PRESET" '.preset == $p' "$ACTIVE" >/dev/null 2>&1; then echo "Error: circuits/bin was last built for a different preset (see ${ACTIVE})." >&2 echo " Fast fix (no full recompile if dist is ready):" >&2 - echo " pnpm build:circuits --preset ${PRESET} --skip-if-built --no-clean --no-clean-targets" >&2 + echo " pnpm build:circuits --preset ${PRESET} --committee ${COMMITTEE} --skip-if-built --no-clean --no-clean-targets" >&2 exit 1 fi if [ ${#missing[@]} -gt 0 ]; then - echo "Error: circuit artifacts for preset '${PRESET}' are missing or stale." >&2 - echo " circuits/bin/target reflects the last preset built; dist/circuits// must exist for this mode." >&2 - echo " Fix: pnpm build:circuits --preset ${PRESET}" >&2 + echo "Error: circuit artifacts for preset '${PRESET}/${COMMITTEE}' are missing or stale." >&2 + echo " circuits/bin/target reflects the last preset built; dist/circuits/// must exist for this mode." >&2 + echo " Fix: pnpm build:circuits --preset ${PRESET} --committee ${COMMITTEE}" >&2 echo " Or run this script without --skip-build." >&2 echo "Missing:" >&2 printf ' %s\n' "${missing[@]}" >&2 diff --git a/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh b/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh index f5922f77a..154d28768 100755 --- a/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh +++ b/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh @@ -117,7 +117,7 @@ else fi require_preset_artifacts() { - if ! "${SCRIPT_DIR}/check_circuit_preset_artifacts.sh" "$PRESET_NAME"; then + if ! "${SCRIPT_DIR}/check_circuit_preset_artifacts.sh" "$PRESET_NAME" --committee "$COMMITTEE"; then exit 1 fi } diff --git a/circuits/benchmarks/scripts/run_benchmarks.sh b/circuits/benchmarks/scripts/run_benchmarks.sh index 8fef37bee..b6a5acc16 100755 --- a/circuits/benchmarks/scripts/run_benchmarks.sh +++ b/circuits/benchmarks/scripts/run_benchmarks.sh @@ -243,7 +243,7 @@ if [ "$SKIP_COMPILE" = false ]; then ENSURE_ARGS+=(--verbose) fi "${SCRIPT_DIR}/ensure_circuit_preset_built.sh" "${ENSURE_ARGS[@]}" - if "${SCRIPT_DIR}/check_circuit_preset_artifacts.sh" "$PRESET_NAME"; then + if "${SCRIPT_DIR}/check_circuit_preset_artifacts.sh" "$PRESET_NAME" --committee "$OUTPUT_COMMITTEE"; then PRESET_ARTIFACTS_READY=true fi echo "Preflight build complete." diff --git a/crates/aggregator/src/actors/publickey_aggregator.rs b/crates/aggregator/src/actors/publickey_aggregator.rs index 85ca7b063..54d3d7c9c 100644 --- a/crates/aggregator/src/actors/publickey_aggregator.rs +++ b/crates/aggregator/src/actors/publickey_aggregator.rs @@ -138,6 +138,7 @@ impl PublicKeyAggregator { decryption_proofs: vec![], pre_dishonest: no_proof_parties.into_iter().collect(), params_preset: self.params_preset, + committee_size: self.committee_size, }, ec, )?; diff --git a/crates/aggregator/src/actors/threshold_plaintext_aggregator.rs b/crates/aggregator/src/actors/threshold_plaintext_aggregator.rs index c2e4e0745..3506af105 100644 --- a/crates/aggregator/src/actors/threshold_plaintext_aggregator.rs +++ b/crates/aggregator/src/actors/threshold_plaintext_aggregator.rs @@ -212,6 +212,7 @@ impl ThresholdPlaintextAggregator { decryption_proofs: vec![], pre_dishonest: BTreeSet::new(), params_preset: self.params_preset, + committee_size: self.committee_size, }, ec, )?; @@ -370,6 +371,7 @@ impl ThresholdPlaintextAggregator { params_preset: self.params_preset, threshold_m, threshold_n, + committee_size: self.committee_size, }, plaintext, shares, diff --git a/crates/events/src/enclave_event/compute_request/zk.rs b/crates/events/src/enclave_event/compute_request/zk.rs index d4a8c133f..fa5e88225 100644 --- a/crates/events/src/enclave_event/compute_request/zk.rs +++ b/crates/events/src/enclave_event/compute_request/zk.rs @@ -200,6 +200,8 @@ pub struct DkgShareDecryptionProofRequest { pub dkg_input_type: DkgInputType, /// BFV preset for parameter resolution. pub params_preset: BfvPreset, + /// Committee size for circuit artifact resolution. + pub committee_size: CiphernodesCommitteeSize, } /// Request to generate a proof for BFV public key generation (C0). @@ -210,6 +212,7 @@ pub struct PkBfvProofRequest { #[derivative(Debug(format_with = "e3_utils::formatters::hexf"))] pub pk_bfv: ArcBytes, pub params_preset: BfvPreset, + pub committee_size: CiphernodesCommitteeSize, } /// Request to generate a proof for PK share generation (C1). @@ -232,10 +235,15 @@ pub struct PkGenerationProofRequest { } impl PkBfvProofRequest { - pub fn new(pk_bfv: impl Into, params_preset: BfvPreset) -> Self { + pub fn new( + pk_bfv: impl Into, + params_preset: BfvPreset, + committee_size: CiphernodesCommitteeSize, + ) -> Self { Self { pk_bfv: pk_bfv.into(), params_preset, + committee_size, } } } @@ -332,6 +340,8 @@ pub struct ThresholdShareDecryptionProofRequest { pub d_share_bytes: Vec, /// BFV preset for parameter resolution. pub params_preset: BfvPreset, + /// Committee size for per-committee circuit artifact resolution. + pub committee_size: CiphernodesCommitteeSize, } /// Response containing generated proofs for threshold share decryption (C6). @@ -417,6 +427,8 @@ pub struct VerifyShareProofsRequest { pub party_proofs: Vec, /// BFV preset for parameter resolution (determines circuit artifact directory). pub params_preset: BfvPreset, + /// Committee size for per-committee circuit artifact resolution. + pub committee_size: CiphernodesCommitteeSize, } /// All signed proofs from a single sender to verify. @@ -459,6 +471,8 @@ pub struct VerifyShareDecryptionProofsRequest { pub party_proofs: Vec, /// BFV preset for parameter resolution (determines circuit artifact directory). pub params_preset: BfvPreset, + /// Committee size for per-committee circuit artifact resolution. + pub committee_size: CiphernodesCommitteeSize, } /// C4 proofs from a single sender to verify. @@ -493,8 +507,10 @@ pub struct DecryptedSharesAggregationProofRequest { pub params_preset: BfvPreset, /// Threshold required for decryption. pub threshold_m: u64, - /// Committee size. + /// Committee size (N). pub threshold_n: u64, + /// Committee size for per-committee circuit artifact resolution. + pub committee_size: CiphernodesCommitteeSize, } /// Response containing generated proofs for decrypted shares aggregation (C7). diff --git a/crates/events/src/enclave_event/encryption_key_pending.rs b/crates/events/src/enclave_event/encryption_key_pending.rs index 8d197d6e8..525cf7c40 100644 --- a/crates/events/src/enclave_event/encryption_key_pending.rs +++ b/crates/events/src/enclave_event/encryption_key_pending.rs @@ -7,6 +7,7 @@ use crate::{E3id, EncryptionKey}; use actix::Message; use e3_fhe_params::BfvPreset; +use e3_zk_helpers::CiphernodesCommitteeSize; use serde::{Deserialize, Serialize}; use std::fmt::{self, Display}; use std::sync::Arc; @@ -20,6 +21,7 @@ pub struct EncryptionKeyPending { pub e3_id: E3id, pub key: Arc, pub params_preset: BfvPreset, + pub committee_size: CiphernodesCommitteeSize, } impl Display for EncryptionKeyPending { diff --git a/crates/events/src/enclave_event/share_verification.rs b/crates/events/src/enclave_event/share_verification.rs index f67f5a210..f99fd8e0e 100644 --- a/crates/events/src/enclave_event/share_verification.rs +++ b/crates/events/src/enclave_event/share_verification.rs @@ -14,6 +14,7 @@ //! when verification finishes, carrying the set of dishonest party IDs. use crate::{E3id, PartyProofsToVerify, PartyShareDecryptionProofsToVerify}; +use e3_zk_helpers::CiphernodesCommitteeSize; use serde::{Deserialize, Serialize}; use std::collections::BTreeSet; @@ -44,6 +45,8 @@ pub struct ShareVerificationDispatched { pub pre_dishonest: BTreeSet, /// BFV preset for circuit artifact resolution. pub params_preset: e3_fhe_params::BfvPreset, + /// Committee size for per-committee circuit artifact resolution. + pub committee_size: CiphernodesCommitteeSize, } /// ShareVerificationActor → ThresholdKeyshare: verification results. diff --git a/crates/keyshare/src/actors/threshold_keyshare.rs b/crates/keyshare/src/actors/threshold_keyshare.rs index a346548d9..978f9ae89 100644 --- a/crates/keyshare/src/actors/threshold_keyshare.rs +++ b/crates/keyshare/src/actors/threshold_keyshare.rs @@ -22,6 +22,7 @@ use e3_events::{ }; use e3_fhe_params::create_deterministic_crp_from_default_seed; use e3_fhe_params::BfvPreset; +use e3_zk_helpers::CiphernodesCommitteeSize; use e3_trbfv::{ calculate_decryption_key::CalculateDecryptionKeyResponse, calculate_decryption_share::{ @@ -599,11 +600,16 @@ impl ThresholdKeyshare { )) })?; + let committee_size = CiphernodesCommitteeSize::from_threshold( + state.threshold_m as usize, + state.threshold_n as usize, + )?; self.bus.publish( EncryptionKeyPending { e3_id, key: Arc::new(EncryptionKey::new(state.party_id, pk_bfv_bytes)), params_preset: self.share_enc_preset, + committee_size, }, ec, )?; @@ -1070,6 +1076,10 @@ impl ThresholdKeyshare { pre_dishonest.len() ); + let committee_size = CiphernodesCommitteeSize::from_threshold( + state.threshold_m as usize, + state.threshold_n as usize, + )?; self.bus.publish( ShareVerificationDispatched { e3_id: e3_id.clone(), @@ -1078,6 +1088,7 @@ impl ThresholdKeyshare { decryption_proofs: Vec::new(), pre_dishonest, params_preset: self.share_enc_preset, + committee_size, }, ec, )?; @@ -1467,6 +1478,10 @@ impl ThresholdKeyshare { pre_dishonest.len() ); + let committee_size = CiphernodesCommitteeSize::from_threshold( + state.threshold_m as usize, + state.threshold_n as usize, + )?; self.bus.publish( ShareVerificationDispatched { e3_id: e3_id.clone(), @@ -1475,6 +1490,7 @@ impl ThresholdKeyshare { decryption_proofs: party_proofs, pre_dishonest, params_preset: self.share_enc_preset, + committee_size, }, ec, )?; @@ -1603,6 +1619,11 @@ impl ThresholdKeyshare { info!("Publishing ShareDecryptionProofPending for C6 proof generation..."); + let committee_size = CiphernodesCommitteeSize::from_threshold( + state.threshold_m as usize, + state.threshold_n as usize, + )?; + // Publish pending event before transitioning state so a publish // failure leaves us in Decrypting (retryable) rather than // GeneratingDecryptionProof (no retry path). @@ -1619,6 +1640,7 @@ impl ThresholdKeyshare { es_poly_sum: decrypting.es_poly_sum, d_share_bytes: d_share_poly.clone(), params_preset: threshold_preset, + committee_size, }, }, ec.clone(), diff --git a/crates/keyshare/src/domain/decryption_key_calculation.rs b/crates/keyshare/src/domain/decryption_key_calculation.rs index df5a2ba25..dd773324f 100644 --- a/crates/keyshare/src/domain/decryption_key_calculation.rs +++ b/crates/keyshare/src/domain/decryption_key_calculation.rs @@ -339,6 +339,7 @@ pub(crate) fn build_decryption_key_plan( own_share_raw: current.own_sk_share_raw.clone(), dkg_input_type: DkgInputType::SecretKey, params_preset: threshold_preset, + committee_size: committee, }; let esm_requests: Vec = esi_ciphertexts_raw @@ -353,6 +354,7 @@ pub(crate) fn build_decryption_key_plan( own_share_raw: current.own_esi_shares_raw[esi_idx].clone(), dkg_input_type: DkgInputType::SmudgingNoise, params_preset: threshold_preset, + committee_size: committee, }) .collect(); diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index 2b7913e07..0dc20ca65 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -439,7 +439,9 @@ fn handle_threshold_share_decryption_proof( } let mut proofs = Vec::with_capacity(num_indices); let bb_work_base = zk_bb_work_id(&request); - let artifacts_dir = req.params_preset.artifacts_dir(); + let artifacts_dir = req + .params_preset + .artifacts_dir_for_committee(req.committee_size.as_str()); for i in 0..num_indices { // Deserialize ciphertext @@ -1039,7 +1041,9 @@ fn handle_pk_bfv_proof( .params_preset .threshold_counterpart() .unwrap_or_else(|| BfvPreset::InsecureThreshold512); - let artifacts_dir = req.params_preset.artifacts_dir(); + let artifacts_dir = req + .params_preset + .artifacts_dir_for_committee(req.committee_size.as_str()); // But here we have to pass the InsecureThreshold512 preset because the underlaying witness generator // builds both params, but will only use the DKG one let proof = circuit @@ -1275,7 +1279,9 @@ fn handle_dkg_share_decryption_proof( let circuit = ShareDecryptionCircuit; let bb_work = zk_bb_work_id(&request); - let artifacts_dir = req.params_preset.artifacts_dir(); + let artifacts_dir = req + .params_preset + .artifacts_dir_for_committee(req.committee_size.as_str()); let proof = circuit .prove( prover, @@ -1318,7 +1324,9 @@ fn handle_verify_share_proofs( request: ComputeRequest, ) -> Result { let e3_id_str = request.e3_id.to_string(); - let artifacts_dir = req.params_preset.artifacts_dir(); + let artifacts_dir = req + .params_preset + .artifacts_dir_for_committee(req.committee_size.as_str()); // ECDSA validation (signature recovery, signer consistency, e3_id match) // is handled by ShareVerificationActor before dispatching to multithread. @@ -1390,7 +1398,9 @@ fn handle_verify_share_decryption_proofs( request: ComputeRequest, ) -> Result { let e3_id_str = request.e3_id.to_string(); - let artifacts_dir = req.params_preset.artifacts_dir(); + let artifacts_dir = req + .params_preset + .artifacts_dir_for_committee(req.committee_size.as_str()); // ECDSA validation (signature recovery, signer consistency, e3_id match) // is handled by ShareVerificationActor before dispatching to multithread. @@ -1554,7 +1564,9 @@ fn handle_decrypted_shares_aggregation_proof( let circuit = DecryptedSharesAggregationCircuit; let idx_work_id = format!("{}_c7_{}", zk_bb_work_id(&request), i); - let artifacts_dir = req.params_preset.artifacts_dir(); + let artifacts_dir = req + .params_preset + .artifacts_dir_for_committee(req.committee_size.as_str()); let proof = circuit .prove_with_variant( prover, diff --git a/crates/slashing/Cargo.toml b/crates/slashing/Cargo.toml index d0c1f55e6..bf2d80471 100644 --- a/crates/slashing/Cargo.toml +++ b/crates/slashing/Cargo.toml @@ -16,6 +16,7 @@ e3-events = { workspace = true } e3-fhe-params = { workspace = true } e3-request = { workspace = true } e3-utils = { workspace = true } +e3-zk-helpers = { workspace = true } hex = { workspace = true } serde = { workspace = true } sha2 = { workspace = true } diff --git a/crates/slashing/src/domain/accusation_voting.rs b/crates/slashing/src/domain/accusation_voting.rs index f72dc4552..3461cf044 100644 --- a/crates/slashing/src/domain/accusation_voting.rs +++ b/crates/slashing/src/domain/accusation_voting.rs @@ -876,10 +876,21 @@ impl AccusationVoting { sender_party_id: accused_party_id, signed_proofs: vec![forwarded_clone], }; + let committee_size = match e3_zk_helpers::CiphernodesCommitteeSize::from_threshold( + self.threshold_m, + self.committee.len(), + ) { + Ok(c) => c, + Err(e) => { + warn!("Cannot derive committee size for ZK re-verification: {e}"); + return; + } + }; let request = ComputeRequest::zk( ZkRequest::VerifyShareProofs(VerifyShareProofsRequest { party_proofs: vec![party_proof], params_preset: self.params_preset, + committee_size, }), correlation_id, self.e3_id.clone(), diff --git a/crates/zk-prover/src/actors/proof_request.rs b/crates/zk-prover/src/actors/proof_request.rs index 5bfbe4a08..e85c8bd69 100644 --- a/crates/zk-prover/src/actors/proof_request.rs +++ b/crates/zk-prover/src/actors/proof_request.rs @@ -110,6 +110,7 @@ impl ProofRequestActor { ZkRequest::PkBfv(PkBfvProofRequest::new( msg.key.pk_bfv.clone(), msg.params_preset, + msg.committee_size, )), correlation_id, msg.e3_id, @@ -1249,6 +1250,7 @@ mod tests { ZkRequest::PkBfv(PkBfvProofRequest::new( ArcBytes::from_bytes(&[1]), e3_fhe_params::BfvPreset::InsecureThreshold512, + e3_zk_helpers::CiphernodesCommitteeSize::Micro, )), correlation_id, e3_id.clone(), diff --git a/crates/zk-prover/src/actors/share_verification.rs b/crates/zk-prover/src/actors/share_verification.rs index 7998cfb47..c92675140 100644 --- a/crates/zk-prover/src/actors/share_verification.rs +++ b/crates/zk-prover/src/actors/share_verification.rs @@ -95,6 +95,7 @@ impl ShareVerificationActor { ); let params_preset = msg.params_preset; + let committee_size = msg.committee_size; match msg.kind { VerificationKind::ShareProofs | VerificationKind::ThresholdDecryptionProofs @@ -107,6 +108,7 @@ impl ShareVerificationActor { msg.pre_dishonest, ec, params_preset, + committee_size, |pending, passed| { pending.ecdsa_passed_share_proofs = passed; }, @@ -120,6 +122,7 @@ impl ShareVerificationActor { msg.pre_dishonest, ec, params_preset, + committee_size, |pending, passed| { pending.ecdsa_passed_decryption_proofs = passed; }, @@ -141,6 +144,7 @@ impl ShareVerificationActor { pre_dishonest: BTreeSet, ec: EventContext, params_preset: e3_fhe_params::BfvPreset, + committee_size: e3_zk_helpers::CiphernodesCommitteeSize, store_passed_proofs: impl FnOnce(&mut PendingConsistencyCheck, Vec

), ) { let e3_id_str = e3_id.to_string(); @@ -184,6 +188,7 @@ impl ShareVerificationActor { ecdsa_passed_share_proofs: Vec::new(), ecdsa_passed_decryption_proofs: Vec::new(), params_preset, + committee_size, }; store_passed_proofs(&mut pending, outcome.ecdsa_passed_parties); self.pending_consistency.insert(correlation_id, pending); @@ -271,6 +276,7 @@ impl ShareVerificationActor { ZkRequest::VerifyShareProofs(VerifyShareProofsRequest { party_proofs: passed, params_preset: pending.params_preset, + committee_size: pending.committee_size, }), zk_correlation_id, pending.e3_id.clone(), @@ -295,6 +301,7 @@ impl ShareVerificationActor { ZkRequest::VerifyShareDecryptionProofs(VerifyShareDecryptionProofsRequest { party_proofs: passed, params_preset: pending.params_preset, + committee_size: pending.committee_size, }), zk_correlation_id, pending.e3_id.clone(), @@ -343,6 +350,7 @@ impl ShareVerificationActor { party_public_signals, party_proof_data, params_preset: pending.params_preset, + committee_size: pending.committee_size, }, ); diff --git a/crates/zk-prover/src/domain/proof_request.rs b/crates/zk-prover/src/domain/proof_request.rs index 723542183..7dd7c5476 100644 --- a/crates/zk-prover/src/domain/proof_request.rs +++ b/crates/zk-prover/src/domain/proof_request.rs @@ -386,6 +386,7 @@ mod tests { own_share_raw: sensitive(), dkg_input_type: DkgInputType::SecretKey, params_preset: BfvPreset::default(), + committee_size: CiphernodesCommitteeSize::Medium, } } diff --git a/crates/zk-prover/src/domain/share_verification.rs b/crates/zk-prover/src/domain/share_verification.rs index 6f5b01b5c..dd92138ff 100644 --- a/crates/zk-prover/src/domain/share_verification.rs +++ b/crates/zk-prover/src/domain/share_verification.rs @@ -21,6 +21,7 @@ use e3_events::{ E3id, EventContext, PartyProofData, PartyProofsToVerify, PartyShareDecryptionProofsToVerify, PartyVerificationResult, ProofType, Sequenced, SignedProofPayload, VerificationKind, }; +use e3_zk_helpers::CiphernodesCommitteeSize; use e3_utils::utility_types::ArcBytes; use tracing::{info, warn}; @@ -100,6 +101,8 @@ pub(crate) struct PendingVerification { pub(crate) party_proof_data: HashMap>, /// BFV preset for circuit artifact resolution. pub(crate) params_preset: e3_fhe_params::BfvPreset, + /// Committee size for per-committee circuit artifact resolution. + pub(crate) committee_size: CiphernodesCommitteeSize, } /// Pending consistency check — stored between ECDSA pass and ZK dispatch. @@ -125,6 +128,8 @@ pub(crate) struct PendingConsistencyCheck { pub(crate) ecdsa_passed_decryption_proofs: Vec, /// BFV preset for circuit artifact resolution. pub(crate) params_preset: e3_fhe_params::BfvPreset, + /// Committee size for per-committee circuit artifact resolution. + pub(crate) committee_size: CiphernodesCommitteeSize, } /// Filter out inconsistent parties and collect dispatched party IDs. From 1ac23c20e28507a9df6ede749f14a9bdd8ebec9e Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 4 Jun 2026 22:37:37 +0200 Subject: [PATCH 07/16] cargo fmt --- crates/keyshare/src/actors/threshold_keyshare.rs | 2 +- crates/zk-prover/src/domain/share_verification.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/keyshare/src/actors/threshold_keyshare.rs b/crates/keyshare/src/actors/threshold_keyshare.rs index 978f9ae89..6cdcc800d 100644 --- a/crates/keyshare/src/actors/threshold_keyshare.rs +++ b/crates/keyshare/src/actors/threshold_keyshare.rs @@ -22,7 +22,6 @@ use e3_events::{ }; use e3_fhe_params::create_deterministic_crp_from_default_seed; use e3_fhe_params::BfvPreset; -use e3_zk_helpers::CiphernodesCommitteeSize; use e3_trbfv::{ calculate_decryption_key::CalculateDecryptionKeyResponse, calculate_decryption_share::{ @@ -35,6 +34,7 @@ use e3_trbfv::{ }; use e3_utils::utility_types::ArcBytes; use e3_utils::{NotifySync, MAILBOX_LIMIT}; +use e3_zk_helpers::CiphernodesCommitteeSize; use fhe_traits::Serialize; use std::{ collections::{BTreeSet, HashMap, HashSet}, diff --git a/crates/zk-prover/src/domain/share_verification.rs b/crates/zk-prover/src/domain/share_verification.rs index dd92138ff..2b6a59bcf 100644 --- a/crates/zk-prover/src/domain/share_verification.rs +++ b/crates/zk-prover/src/domain/share_verification.rs @@ -21,8 +21,8 @@ use e3_events::{ E3id, EventContext, PartyProofData, PartyProofsToVerify, PartyShareDecryptionProofsToVerify, PartyVerificationResult, ProofType, Sequenced, SignedProofPayload, VerificationKind, }; -use e3_zk_helpers::CiphernodesCommitteeSize; use e3_utils::utility_types::ArcBytes; +use e3_zk_helpers::CiphernodesCommitteeSize; use tracing::{info, warn}; /// Trait for party types whose signed proofs can be ECDSA-validated and ZK-verified. From 46c444e6042b86bb3cb924285e5097a98bb15841 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 4 Jun 2026 22:52:30 +0200 Subject: [PATCH 08/16] fix committee slashing --- .../slashing/src/domain/accusation_voting.rs | 52 +++++++++++++++---- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/crates/slashing/src/domain/accusation_voting.rs b/crates/slashing/src/domain/accusation_voting.rs index 3461cf044..b4067bbed 100644 --- a/crates/slashing/src/domain/accusation_voting.rs +++ b/crates/slashing/src/domain/accusation_voting.rs @@ -41,6 +41,7 @@ use e3_events::{ VOTE_TYPEHASH_STR, }; use e3_utils::ArcBytes; +use e3_zk_helpers::CiphernodesCommitteeSize; use tracing::{error, info, warn}; use crate::actors::accusation_manager::Clock; @@ -135,6 +136,9 @@ pub(crate) struct AccusationVoting { committee: Vec

, /// Quorum threshold — matches the cryptographic threshold M. threshold_m: usize, + /// Original committee N fixed at construction for ZK circuit resolution. + /// Do not derive from [`Self::committee`] after [`Self::on_slash_executed`] shrinks the roster. + committee_n: usize, /// Active accusations keyed by accusation_id. pending: HashMap<[u8; 32], PendingAccusation>, @@ -180,6 +184,7 @@ impl AccusationVoting { clock: Arc, ) -> Self { let my_address = signer.address(); + let committee_n = committee.len(); Self { e3_id, my_address, @@ -187,6 +192,7 @@ impl AccusationVoting { slashing_manager, committee, threshold_m, + committee_n, pending: HashMap::new(), accused_proofs: HashSet::new(), received_data: HashMap::new(), @@ -841,6 +847,17 @@ impl AccusationVoting { let accused_party_id = accusation.accused_party_id; let forwarded_clone = forwarded.clone(); + let committee_size = match CiphernodesCommitteeSize::from_threshold( + self.threshold_m, + self.committee_n, + ) { + Ok(c) => c, + Err(e) => { + warn!("Cannot derive committee size for ZK re-verification: {e}"); + return; + } + }; + // Create PendingAccusation without our vote — it arrives after ZK completes. actions.push(VoteAction::StartTimeout(accusation_id)); self.pending.insert( @@ -876,16 +893,6 @@ impl AccusationVoting { sender_party_id: accused_party_id, signed_proofs: vec![forwarded_clone], }; - let committee_size = match e3_zk_helpers::CiphernodesCommitteeSize::from_threshold( - self.threshold_m, - self.committee.len(), - ) { - Ok(c) => c, - Err(e) => { - warn!("Cannot derive committee size for ZK re-verification: {e}"); - return; - } - }; let request = ComputeRequest::zk( ZkRequest::VerifyShareProofs(VerifyShareProofsRequest { party_proofs: vec![party_proof], @@ -1626,4 +1633,29 @@ mod tests { assert_eq!(quorum.outcome, AccusationOutcome::Inconclusive); assert!(v.on_vote_timeout(id).is_none(), "second timeout is a no-op"); } + + /// After a slash shrinks the live roster, ZK re-verification must still use the + /// canonical circuit committee size cached at construction. + #[test] + fn committee_size_unchanged_after_slash() { + let me = signer(1); + let committee: Vec
= (1..=10u8).map(|b| signer(b).address()).collect(); + let mut v = voting_with(&me, committee.clone(), 4); + + let slashed = committee[9]; + v.on_slash_executed(SlashExecuted { + e3_id: v.e3_id.clone(), + proposal_id: 1, + operator: slashed, + reason: [0u8; 32], + ticket_amount: 0, + license_amount: 0, + }); + assert_eq!(v.committee.len(), 9); + assert!(CiphernodesCommitteeSize::from_threshold(4, v.committee.len()).is_err()); + assert_eq!( + CiphernodesCommitteeSize::from_threshold(v.threshold_m, v.committee_n).unwrap(), + CiphernodesCommitteeSize::Medium + ); + } } From ac31cb3e999c00c3c6e50788be949ff944e5291a Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 4 Jun 2026 23:24:50 +0200 Subject: [PATCH 09/16] fix crisp e2e --- .github/workflows/ci.yml | 5 ++ crates/fhe-params/src/presets.rs | 49 ++++++++++++++++ crates/multithread/src/multithread.rs | 56 ++++++++----------- crates/zk-prover/src/actors/mod.rs | 3 +- .../src/actors/proof_verification.rs | 15 ++++- .../src/circuits/aggregation/node_dkg_fold.rs | 4 +- crates/zk-prover/src/prover.rs | 9 +++ examples/CRISP/scripts/dev_cipher.sh | 4 ++ .../CRISP/scripts/stage_enclave_circuits.sh | 34 +++++++++++ 9 files changed, 140 insertions(+), 39 deletions(-) create mode 100755 examples/CRISP/scripts/stage_enclave_circuits.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8642a52e3..d83b3f3db 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -772,6 +772,11 @@ jobs: run: | chmod +x ~/.cargo/bin/enclave + - name: Install Nargo + uses: noir-lang/noirup@v0.1.4 + with: + toolchain: ${{ env.NOIR_TOOLCHAIN }} + - name: Setup ZK prover run: | enclave noir setup diff --git a/crates/fhe-params/src/presets.rs b/crates/fhe-params/src/presets.rs index 4a533c43d..e8b15b058 100644 --- a/crates/fhe-params/src/presets.rs +++ b/crates/fhe-params/src/presets.rs @@ -18,6 +18,7 @@ use crate::constants::{ secure_8192, }; use serde::{Deserialize, Serialize}; +use std::path::Path; use std::sync::Arc; use thiserror::Error as ThisError; @@ -401,6 +402,37 @@ impl BfvPreset { format!("{}/{}", self.artifacts_dir(), committee.as_ref()) } + /// Resolve the artifact directory to use at runtime. + /// + /// Prefers the per-committee layout (`{preset}/{committee}`) when present. Falls back to the + /// legacy release layout (`{preset}/…`) from `enclave noir setup` until a circuits release + /// ships per-committee trees. + pub fn resolve_artifacts_dir_for_committee>( + &self, + committee: C, + circuits_base: &Path, + ) -> String { + let per_committee = self.artifacts_dir_for_committee(committee.as_ref()); + let legacy = self.artifacts_dir(); + if Self::artifacts_tree_has_pk_bfv(circuits_base, &per_committee) { + per_committee + } else if Self::artifacts_tree_has_pk_bfv(circuits_base, &legacy) { + legacy + } else { + per_committee + } + } + + fn artifacts_tree_has_pk_bfv(circuits_base: &Path, artifacts_dir: &str) -> bool { + ["recursive", "default"].iter().any(|variant| { + circuits_base + .join(artifacts_dir) + .join(variant) + .join("dkg/pk/pk.json") + .exists() + }) + } + pub fn search_defaults(&self) -> Option { match self { BfvPreset::InsecureThreshold512 => Some(PresetSearchDefaults { @@ -575,4 +607,21 @@ mod tests { ); assert_eq!(BfvPreset::SecureDkg8192.artifacts_dir(), "secure-8192"); } + + #[test] + fn resolve_artifacts_dir_falls_back_to_legacy_layout() { + let temp = + std::env::temp_dir().join(format!("enclave-artifacts-test-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&temp); + let legacy = temp.join("insecure-512/recursive/dkg/pk"); + std::fs::create_dir_all(&legacy).unwrap(); + std::fs::write(legacy.join("pk.json"), b"{}").unwrap(); + + let preset = BfvPreset::InsecureDkg512; + assert_eq!( + preset.resolve_artifacts_dir_for_committee("micro", &temp), + "insecure-512" + ); + let _ = std::fs::remove_dir_all(&temp); + } } diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index 0dc20ca65..97fd7ac44 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -376,9 +376,7 @@ fn handle_pk_aggregation_proof( ), ) })?; - let artifacts_dir = req - .params_preset - .artifacts_dir_for_committee(committee.as_str()); + let artifacts_dir = prover.resolve_artifacts_dir(req.params_preset, committee.as_str()); let proof = circuit .prove_with_variant( prover, @@ -439,9 +437,8 @@ fn handle_threshold_share_decryption_proof( } let mut proofs = Vec::with_capacity(num_indices); let bb_work_base = zk_bb_work_id(&request); - let artifacts_dir = req - .params_preset - .artifacts_dir_for_committee(req.committee_size.as_str()); + let artifacts_dir = + prover.resolve_artifacts_dir(req.params_preset, req.committee_size.as_str()); for i in 0..num_indices { // Deserialize ciphertext @@ -711,9 +708,8 @@ fn handle_node_dkg_fold_proof( request: ComputeRequest, report: Option>, ) -> Result { - let artifacts_dir = req - .params_preset - .artifacts_dir_for_committee(req.committee_size.as_str()); + let artifacts_dir = + prover.resolve_artifacts_dir(req.params_preset, req.committee_size.as_str()); let job_id = zk_bb_work_id(&request); let input = NodeDkgFoldInput { c0_proof: &req.c0_proof, @@ -904,9 +900,8 @@ fn handle_share_computation_proof( let bb_work = zk_bb_work_id(&request); let inner_job_id = format!("{bb_work}_c2_inner"); - let artifacts_dir = req - .params_preset - .artifacts_dir_for_committee(req.committee_size.as_str()); + 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; @@ -990,9 +985,8 @@ fn handle_pk_generation_proof( // 5. Generate proof via Provable trait let circuit = PkGenerationCircuit; let bb_work = zk_bb_work_id(&request); - let artifacts_dir = req - .params_preset - .artifacts_dir_for_committee(req.committee_size.as_str()); + let artifacts_dir = + prover.resolve_artifacts_dir(req.params_preset, req.committee_size.as_str()); let proof = circuit .prove( @@ -1041,9 +1035,8 @@ fn handle_pk_bfv_proof( .params_preset .threshold_counterpart() .unwrap_or_else(|| BfvPreset::InsecureThreshold512); - let artifacts_dir = req - .params_preset - .artifacts_dir_for_committee(req.committee_size.as_str()); + let artifacts_dir = + prover.resolve_artifacts_dir(req.params_preset, req.committee_size.as_str()); // But here we have to pass the InsecureThreshold512 preset because the underlaying witness generator // builds both params, but will only use the DKG one let proof = circuit @@ -1132,9 +1125,8 @@ fn handle_share_encryption_proof( // 6. Generate proof (preset = threshold preset; Inputs::compute derives DKG internally) let circuit = ShareEncryptionCircuit; let bb_work = zk_bb_work_id(&request); - let artifacts_dir = req - .params_preset - .artifacts_dir_for_committee(req.committee_size.as_str()); + let artifacts_dir = + prover.resolve_artifacts_dir(req.params_preset, req.committee_size.as_str()); let proof = circuit .prove( prover, @@ -1279,9 +1271,8 @@ fn handle_dkg_share_decryption_proof( let circuit = ShareDecryptionCircuit; let bb_work = zk_bb_work_id(&request); - let artifacts_dir = req - .params_preset - .artifacts_dir_for_committee(req.committee_size.as_str()); + let artifacts_dir = + prover.resolve_artifacts_dir(req.params_preset, req.committee_size.as_str()); let proof = circuit .prove( prover, @@ -1324,9 +1315,8 @@ fn handle_verify_share_proofs( request: ComputeRequest, ) -> Result { let e3_id_str = request.e3_id.to_string(); - let artifacts_dir = req - .params_preset - .artifacts_dir_for_committee(req.committee_size.as_str()); + let artifacts_dir = + prover.resolve_artifacts_dir(req.params_preset, req.committee_size.as_str()); // ECDSA validation (signature recovery, signer consistency, e3_id match) // is handled by ShareVerificationActor before dispatching to multithread. @@ -1398,9 +1388,8 @@ fn handle_verify_share_decryption_proofs( request: ComputeRequest, ) -> Result { let e3_id_str = request.e3_id.to_string(); - let artifacts_dir = req - .params_preset - .artifacts_dir_for_committee(req.committee_size.as_str()); + let artifacts_dir = + prover.resolve_artifacts_dir(req.params_preset, req.committee_size.as_str()); // ECDSA validation (signature recovery, signer consistency, e3_id match) // is handled by ShareVerificationActor before dispatching to multithread. @@ -1564,9 +1553,10 @@ fn handle_decrypted_shares_aggregation_proof( let circuit = DecryptedSharesAggregationCircuit; let idx_work_id = format!("{}_c7_{}", zk_bb_work_id(&request), i); - let artifacts_dir = req - .params_preset - .artifacts_dir_for_committee(req.committee_size.as_str()); + let artifacts_dir = req.params_preset.resolve_artifacts_dir_for_committee( + req.committee_size.as_str(), + prover.circuits_base(), + ); let proof = circuit .prove_with_variant( prover, diff --git a/crates/zk-prover/src/actors/mod.rs b/crates/zk-prover/src/actors/mod.rs index 66e76afea..99bf59afa 100644 --- a/crates/zk-prover/src/actors/mod.rs +++ b/crates/zk-prover/src/actors/mod.rs @@ -79,7 +79,8 @@ pub fn setup_zk_actors( let verifier = zk_actor.clone().recipient(); let proof_request = ProofRequestActor::setup(bus, signer.clone()); - let proof_verification = ProofVerificationActor::setup(bus, verifier); + let proof_verification = + ProofVerificationActor::setup(bus, verifier, backend.circuits_dir.clone()); let share_verification = ShareVerificationActor::setup(bus); let node_proof_aggregator = NodeProofAggregator::setup(bus, signer, dkg_fold_attestation_verifiers_by_chain); diff --git a/crates/zk-prover/src/actors/proof_verification.rs b/crates/zk-prover/src/actors/proof_verification.rs index 51613ad37..8a7ffb382 100644 --- a/crates/zk-prover/src/actors/proof_verification.rs +++ b/crates/zk-prover/src/actors/proof_verification.rs @@ -9,6 +9,7 @@ //! on-chain fault attribution. use std::collections::HashMap; +use std::path::PathBuf; use std::sync::Arc; use actix::{Actor, Addr, AsyncContext, Context, Handler, Message, Recipient}; @@ -58,23 +59,30 @@ pub struct ProofVerificationActor { pending: HashMap<(E3id, u64), PendingVerification>, /// Tracks preset + committee per E3 so we can derive `artifacts_dir` for proof verification. presets: HashMap, + circuits_base: PathBuf, } impl ProofVerificationActor { - pub fn new(bus: &BusHandle, verifier: Recipient>) -> Self { + pub fn new( + bus: &BusHandle, + verifier: Recipient>, + circuits_base: PathBuf, + ) -> Self { Self { bus: bus.clone(), verifier, pending: HashMap::new(), presets: HashMap::new(), + circuits_base, } } pub fn setup( bus: &BusHandle, verifier: Recipient>, + circuits_base: PathBuf, ) -> Addr { - let addr = Self::new(bus, verifier).start(); + let addr = Self::new(bus, verifier, circuits_base).start(); bus.subscribe(EventType::CiphernodeSelected, addr.clone().into()); bus.subscribe(EventType::EncryptionKeyReceived, addr.clone().into()); addr @@ -120,7 +128,8 @@ impl ProofVerificationActor { ); return; }; - let artifacts_dir = preset.artifacts_dir_for_committee(committee.as_str()); + let artifacts_dir = + preset.resolve_artifacts_dir_for_committee(committee.as_str(), &self.circuits_base); let request = TypedEvent::new( ZkVerificationRequest { 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 471244ad9..c2d6081f5 100644 --- a/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs +++ b/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs @@ -368,7 +368,7 @@ pub fn prove_dkg_aggregation( preset: BfvPreset, committee: CiphernodesCommitteeSize, ) -> Result { - let artifacts_dir = preset.artifacts_dir_for_committee(committee.as_str()); + let artifacts_dir = prover.resolve_artifacts_dir(preset, committee.as_str()); let artifacts_dir = artifacts_dir.as_str(); if input.node_fold_proofs.len() != input.party_ids.len() { return Err(ZkError::InvalidInput( @@ -499,7 +499,7 @@ pub fn prove_decryption_aggregation_jobs( preset: BfvPreset, committee: CiphernodesCommitteeSize, ) -> Result, ZkError> { - let artifacts_dir = preset.artifacts_dir_for_committee(committee.as_str()); + let artifacts_dir = prover.resolve_artifacts_dir(preset, committee.as_str()); let artifacts_dir = artifacts_dir.as_str(); // VKs and the compiled circuit are job-independent: load once, reuse per ciphertext. let c6_fold_vk = vk::load_vk_artifacts( diff --git a/crates/zk-prover/src/prover.rs b/crates/zk-prover/src/prover.rs index 49fa0cead..9685ba5c0 100644 --- a/crates/zk-prover/src/prover.rs +++ b/crates/zk-prover/src/prover.rs @@ -7,6 +7,7 @@ use crate::backend::ZkBackend; use crate::error::ZkError; use e3_events::{CircuitName, CircuitVariant, Proof}; +use e3_fhe_params::BfvPreset; use e3_utils::utility_types::ArcBytes; use std::fs; use std::path::PathBuf; @@ -38,10 +39,18 @@ impl ZkProver { } } + pub fn circuits_base(&self) -> &PathBuf { + &self.circuits_dir + } + pub fn circuits_dir(&self, variant: CircuitVariant, artifacts_dir: &str) -> PathBuf { self.circuits_dir.join(artifacts_dir).join(variant.as_str()) } + pub fn resolve_artifacts_dir(&self, preset: BfvPreset, committee: &str) -> String { + preset.resolve_artifacts_dir_for_committee(committee, &self.circuits_dir) + } + pub fn work_dir(&self) -> &PathBuf { &self.work_dir } diff --git a/examples/CRISP/scripts/dev_cipher.sh b/examples/CRISP/scripts/dev_cipher.sh index 9be991388..a0bec68b5 100755 --- a/examples/CRISP/scripts/dev_cipher.sh +++ b/examples/CRISP/scripts/dev_cipher.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" READYFILE=$1 # nuke past installations as we are adding these nodes to the contract @@ -23,6 +24,9 @@ enclave wallet set --name cn5 --private-key "$PRIVATE_KEY_CN5" echo "Setting up ZK prover..." enclave noir setup +echo "Staging per-committee circuit artifacts..." +"${SCRIPT_DIR}/stage_enclave_circuits.sh" + # using & instead of -d so that wait works below enclave nodes up -v & diff --git a/examples/CRISP/scripts/stage_enclave_circuits.sh b/examples/CRISP/scripts/stage_enclave_circuits.sh new file mode 100755 index 000000000..bff944c3f --- /dev/null +++ b/examples/CRISP/scripts/stage_enclave_circuits.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# Overlay per-committee circuit artifacts for CRISP ciphernodes. +# +# `enclave noir setup` installs the legacy release layout (`{preset}/recursive/...`). +# Runtime provers resolve `{preset}/{committee}/recursive/...` when present; this script +# builds or hydrates that tree into the CRISP `.enclave` dir. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "${SCRIPT_DIR}/lib/dev_config.sh" + +load_crisp_dev_config + +COMMITTEE="${CRISP_COMMITTEE:-micro}" +CIRCUITS_OUT="${CRISP_ROOT}/.enclave/noir/circuits" +BB_BIN="${CRISP_ROOT}/.enclave/noir/bin/bb" + +if [[ -x "${BB_BIN}" ]]; then + export PATH="${CRISP_ROOT}/.enclave/noir/bin:${PATH}" +fi + +echo "Staging enclave circuits (${CRISP_BFV_PRESET}/${COMMITTEE}) → ${CIRCUITS_OUT}" +mkdir -p "${CIRCUITS_OUT}" + +( + cd "${REPO_ROOT}" + pnpm build:circuits \ + --preset "${CRISP_BFV_PRESET}" \ + --committee "${COMMITTEE}" \ + --skip-if-built \ + -o "${CIRCUITS_OUT}" +) From 76238250ee9fcd9ec2efc9ee104c05a0705eaa77 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 4 Jun 2026 23:42:35 +0200 Subject: [PATCH 10/16] move circuit compile to setup --- examples/CRISP/Cargo.lock | 9 ----- examples/CRISP/scripts/dev_cipher.sh | 7 ++-- examples/CRISP/scripts/lib/dev_config.sh | 31 ++++++++++++----- examples/CRISP/scripts/setup.sh | 2 +- .../CRISP/scripts/stage_enclave_circuits.sh | 34 ------------------- 5 files changed, 29 insertions(+), 54 deletions(-) delete mode 100755 examples/CRISP/scripts/stage_enclave_circuits.sh diff --git a/examples/CRISP/Cargo.lock b/examples/CRISP/Cargo.lock index 06631f305..fac991062 100644 --- a/examples/CRISP/Cargo.lock +++ b/examples/CRISP/Cargo.lock @@ -2434,14 +2434,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "e3-committee-hash" -version = "0.2.0" -dependencies = [ - "alloy", - "hex", -] - [[package]] name = "e3-compute-provider" version = "0.2.0" @@ -2596,7 +2588,6 @@ dependencies = [ "alloy", "anyhow", "derivative", - "e3-committee-hash", "e3-utils-derive", "hex", "rand 0.9.2", diff --git a/examples/CRISP/scripts/dev_cipher.sh b/examples/CRISP/scripts/dev_cipher.sh index a0bec68b5..c1a9236e1 100755 --- a/examples/CRISP/scripts/dev_cipher.sh +++ b/examples/CRISP/scripts/dev_cipher.sh @@ -2,6 +2,8 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "${SCRIPT_DIR}/lib/dev_config.sh" READYFILE=$1 # nuke past installations as we are adding these nodes to the contract @@ -21,11 +23,12 @@ enclave wallet set --name cn3 --private-key "$PRIVATE_KEY_CN3" enclave wallet set --name cn4 --private-key "$PRIVATE_KEY_CN4" enclave wallet set --name cn5 --private-key "$PRIVATE_KEY_CN5" +load_crisp_dev_config + echo "Setting up ZK prover..." enclave noir setup -echo "Staging per-committee circuit artifacts..." -"${SCRIPT_DIR}/stage_enclave_circuits.sh" +sync_enclave_circuit_artifacts # using & instead of -d so that wait works below enclave nodes up -v & diff --git a/examples/CRISP/scripts/lib/dev_config.sh b/examples/CRISP/scripts/lib/dev_config.sh index 6b6411489..8cde4d363 100644 --- a/examples/CRISP/scripts/lib/dev_config.sh +++ b/examples/CRISP/scripts/lib/dev_config.sh @@ -68,19 +68,34 @@ apply_crisp_dev_config_to_server_env() { _set_env_kv "$server_env" "E3_PROOF_AGGREGATION_ENABLED" "$CRISP_PROOF_AGGREGATION_ENABLED" } -compile_enclave_dkg_circuits_if_needed() { - if [[ "$CRISP_PROOF_AGGREGATION_ENABLED" != "true" ]]; then - echo "Skipping enclave DKG circuit build (CRISP_PROOF_AGGREGATION_ENABLED=false in crisp.dev.env)" - return 0 - fi - - echo "Building enclave DKG circuits (preset=${CRISP_BFV_PRESET})..." +build_enclave_circuits_at_setup() { + local committee="${CRISP_COMMITTEE:-micro}" + echo "Building enclave circuits (preset=${CRISP_BFV_PRESET}, committee=${committee})..." ( cd "${REPO_ROOT}" && - pnpm build:circuits --preset "${CRISP_BFV_PRESET}" --skip-if-built + pnpm build:circuits \ + --preset "${CRISP_BFV_PRESET}" \ + --committee "${committee}" \ + --skip-if-built ) } +sync_enclave_circuit_artifacts() { + local committee="${CRISP_COMMITTEE:-micro}" + local src="${REPO_ROOT}/dist/circuits/${CRISP_BFV_PRESET}/${committee}" + local dst="${CRISP_ROOT}/.enclave/noir/circuits/${CRISP_BFV_PRESET}/${committee}" + + if [[ ! -f "${src}/recursive/dkg/pk/pk.json" ]]; then + echo "No built circuits at ${src}; run pnpm dev:setup first. Using enclave noir setup release layout." + return 0 + fi + + echo "Syncing circuits ${CRISP_BFV_PRESET}/${committee} → ${dst}" + mkdir -p "$(dirname "${dst}")" + rm -rf "${dst}" + cp -R "${src}" "$(dirname "${dst}")/" +} + print_crisp_dev_config_summary() { cat < Date: Thu, 4 Jun 2026 23:55:55 +0200 Subject: [PATCH 11/16] update template --- .github/workflows/ci.yml | 11 ++++ templates/default/package.json | 1 + templates/default/scripts/dev_ciphernodes.sh | 6 ++ templates/default/scripts/lib/dev_config.sh | 63 ++++++++++++++++++++ templates/default/scripts/setup.sh | 35 +++++++++++ 5 files changed, 116 insertions(+) create mode 100644 templates/default/scripts/lib/dev_config.sh create mode 100755 templates/default/scripts/setup.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d83b3f3db..f8964d8b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -777,6 +777,9 @@ jobs: with: toolchain: ${{ env.NOIR_TOOLCHAIN }} + - name: Build enclave circuits + run: pnpm build:circuits --preset insecure-512 --committee micro --skip-if-built + - name: Setup ZK prover run: | enclave noir setup @@ -1184,6 +1187,14 @@ jobs: chmod +x ~/.cargo/bin/enclave chmod +x templates/default/target/debug/e3-support-scripts-dev + - name: Install Nargo + uses: noir-lang/noirup@v0.1.4 + with: + toolchain: ${{ env.NOIR_TOOLCHAIN }} + + - name: Build enclave circuits + run: pnpm build:circuits --preset insecure-512 --committee micro --skip-if-built + - name: Setup ZK prover run: | enclave noir setup diff --git a/templates/default/package.json b/templates/default/package.json index 1f4d5498d..d0d1d83d4 100644 --- a/templates/default/package.json +++ b/templates/default/package.json @@ -12,6 +12,7 @@ "deploy:dev": "hardhat run scripts/deploy-local.ts", "dev:all": "./scripts/dev_all.sh", "dev:ciphernodes": "./scripts/dev_ciphernodes.sh", + "dev:setup": "bash ./scripts/setup.sh", "dev:evm": "hardhat node", "dev:frontend": "./scripts/dev_frontend.sh", "dev:program": "./scripts/dev_program.sh", diff --git a/templates/default/scripts/dev_ciphernodes.sh b/templates/default/scripts/dev_ciphernodes.sh index 62e5e196a..d78fb057b 100755 --- a/templates/default/scripts/dev_ciphernodes.sh +++ b/templates/default/scripts/dev_ciphernodes.sh @@ -4,6 +4,10 @@ set -euo pipefail cd "$(dirname "${BASH_SOURCE[0]}")/.." +# shellcheck disable=SC1091 +source "$(dirname "${BASH_SOURCE[0]}")/lib/dev_config.sh" +load_template_dev_config + SIGNAL_FILE=/tmp/enclave_ciphernodes_ready cleanup() { @@ -46,6 +50,8 @@ enclave wallet set --name cn5 --private-key "$PRIVATE_KEY_CN5" echo "Setting up ZK prover..." enclave noir setup +sync_enclave_circuit_artifacts + # Deploy before starting nodes so enclave.config.yaml addresses match the chain. echo "Deploying protocol + MyProgram..." pnpm exec hardhat utils:clean-deployments --network localhost diff --git a/templates/default/scripts/lib/dev_config.sh b/templates/default/scripts/lib/dev_config.sh new file mode 100644 index 000000000..a84db7d68 --- /dev/null +++ b/templates/default/scripts/lib/dev_config.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# Shared paths and optional monorepo circuit build helpers for the default template. + +_template_dev_config_root() { + (cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) +} + +load_template_dev_config() { + TEMPLATE_ROOT="$(_template_dev_config_root)" + ENCLAVE_REPO_ROOT="$(cd "${TEMPLATE_ROOT}/../.." && pwd)" + + BFV_PRESET="${BFV_PRESET:-insecure-512}" + COMMITTEE="${COMMITTEE:-micro}" + + case "$BFV_PRESET" in + insecure-512 | secure-8192) ;; + *) + echo "Invalid BFV_PRESET='${BFV_PRESET}' (use insecure-512 or secure-8192)" >&2 + exit 1 + ;; + esac + + export TEMPLATE_ROOT ENCLAVE_REPO_ROOT BFV_PRESET COMMITTEE +} + +template_monorepo_build_available() { + [[ -f "${ENCLAVE_REPO_ROOT}/scripts/build-circuits.ts" ]] +} + +build_enclave_circuits_at_setup() { + if ! template_monorepo_build_available; then + echo "Skipping circuit build (standalone template; use enclave noir setup release artifacts)." + return 0 + fi + + echo "Building enclave circuits (preset=${BFV_PRESET}, committee=${COMMITTEE})..." + ( + cd "${ENCLAVE_REPO_ROOT}" && + pnpm build:circuits \ + --preset "${BFV_PRESET}" \ + --committee "${COMMITTEE}" \ + --skip-if-built + ) +} + +sync_enclave_circuit_artifacts() { + if ! template_monorepo_build_available; then + return 0 + fi + + local src="${ENCLAVE_REPO_ROOT}/dist/circuits/${BFV_PRESET}/${COMMITTEE}" + local dst="${TEMPLATE_ROOT}/.enclave/noir/circuits/${BFV_PRESET}/${COMMITTEE}" + + if [[ ! -f "${src}/recursive/dkg/pk/pk.json" ]]; then + echo "No built circuits at ${src}; run pnpm dev:setup first. Using enclave noir setup release layout." + return 0 + fi + + echo "Syncing circuits ${BFV_PRESET}/${COMMITTEE} → ${dst}" + mkdir -p "$(dirname "${dst}")" + rm -rf "${dst}" + cp -R "${src}" "$(dirname "${dst}")/" +} diff --git a/templates/default/scripts/setup.sh b/templates/default/scripts/setup.sh new file mode 100755 index 000000000..57ea88904 --- /dev/null +++ b/templates/default/scripts/setup.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "${SCRIPT_DIR}/lib/dev_config.sh" + +load_template_dev_config +cd "${TEMPLATE_ROOT}" + +echo "Installing dependencies..." +pnpm install --frozen-lockfile + +echo "Compiling guest program..." +if [[ ! -f './.enclave/generated/contracts/ImageID.sol' ]]; then + enclave program compile +fi + +build_enclave_circuits_at_setup + +echo "Compiling contracts..." +pnpm compile + +if [[ ! -f ~/.cargo/bin/enclave ]]; then + echo "Building and installing enclave CLI..." + (cd "${ENCLAVE_REPO_ROOT}" && cargo build --locked -p e3-cli && cargo install --locked --path crates/cli) +else + echo "enclave CLI already installed, skipping build" +fi + +echo "Running enclave noir setup..." +enclave noir setup + +echo "Template setup complete." From 66f5b8791999dc559c024f4c7970eda5197bbf30 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Fri, 5 Jun 2026 00:08:50 +0200 Subject: [PATCH 12/16] missing bb --- .github/workflows/ci.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f8964d8b1..6e520df8a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -777,6 +777,22 @@ jobs: with: toolchain: ${{ env.NOIR_TOOLCHAIN }} + - name: Cache Barretenberg binary + id: cache-bb + uses: actions/cache@v4 + with: + path: /usr/local/bin/bb + key: bb-bin-${{ env.BB_VERSION }}-amd64-linux + + - name: Install Barretenberg (bb) + if: steps.cache-bb.outputs.cache-hit != 'true' + run: | + curl -fsSL "https://github.com/gnosisguild/aztec-packages/releases/download/v${{ env.BB_VERSION }}/barretenberg-amd64-linux.tar.gz" -o bb.tar.gz + mkdir -p bb_extract && tar -xzf bb.tar.gz -C bb_extract + BB_BIN=$(find bb_extract -name bb -type f | head -n 1) + sudo mv "$BB_BIN" /usr/local/bin/bb + chmod +x /usr/local/bin/bb + - name: Build enclave circuits run: pnpm build:circuits --preset insecure-512 --committee micro --skip-if-built @@ -1192,6 +1208,22 @@ jobs: with: toolchain: ${{ env.NOIR_TOOLCHAIN }} + - name: Cache Barretenberg binary + id: cache-bb + uses: actions/cache@v4 + with: + path: /usr/local/bin/bb + key: bb-bin-${{ env.BB_VERSION }}-amd64-linux + + - name: Install Barretenberg (bb) + if: steps.cache-bb.outputs.cache-hit != 'true' + run: | + curl -fsSL "https://github.com/gnosisguild/aztec-packages/releases/download/v${{ env.BB_VERSION }}/barretenberg-amd64-linux.tar.gz" -o bb.tar.gz + mkdir -p bb_extract && tar -xzf bb.tar.gz -C bb_extract + BB_BIN=$(find bb_extract -name bb -type f | head -n 1) + sudo mv "$BB_BIN" /usr/local/bin/bb + chmod +x /usr/local/bin/bb + - name: Build enclave circuits run: pnpm build:circuits --preset insecure-512 --committee micro --skip-if-built From 53a58c37c6c40c3c334ecdd803c76381e1dcb729 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Fri, 5 Jun 2026 00:36:39 +0200 Subject: [PATCH 13/16] make e3Id indexed --- crates/evm/src/contracts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/evm/src/contracts.rs b/crates/evm/src/contracts.rs index 599fc31ac..51107f950 100644 --- a/crates/evm/src/contracts.rs +++ b/crates/evm/src/contracts.rs @@ -55,7 +55,7 @@ sol! { event E3Requested(uint256 e3Id, E3 e3, address indexed e3Program); event CiphertextOutputPublished(uint256 indexed e3Id, bytes ciphertextOutput); event E3Failed(uint256 e3Id, uint8 failedAtStage, uint8 reason); - event E3StageChanged(uint256 e3Id, uint8 previousStage, uint8 newStage); + event E3StageChanged(uint256 indexed e3Id, uint8 previousStage, uint8 newStage); // ── Errors (only those our called functions can revert with) ──────── error CiphertextOutputNotPublished(uint256 e3Id); From 0bb07f73826a872c433a04115c6e18d84a0e6ffa Mon Sep 17 00:00:00 2001 From: 0xjei Date: Fri, 5 Jun 2026 11:04:07 +0200 Subject: [PATCH 14/16] remove legacy support for paths --- crates/aggregator/src/ext.rs | 4 +- crates/fhe-params/src/presets.rs | 48 ------------------- crates/multithread/src/multithread.rs | 7 ++- .../src/actors/proof_verification.rs | 3 +- crates/zk-prover/src/prover.rs | 2 +- 5 files changed, 6 insertions(+), 58 deletions(-) diff --git a/crates/aggregator/src/ext.rs b/crates/aggregator/src/ext.rs index edbc01cdf..847b00c45 100644 --- a/crates/aggregator/src/ext.rs +++ b/crates/aggregator/src/ext.rs @@ -243,9 +243,7 @@ fn load_honest_committee_addresses(ctx: &E3Context, e3_id: &E3id) -> Result>( - &self, - committee: C, - circuits_base: &Path, - ) -> String { - let per_committee = self.artifacts_dir_for_committee(committee.as_ref()); - let legacy = self.artifacts_dir(); - if Self::artifacts_tree_has_pk_bfv(circuits_base, &per_committee) { - per_committee - } else if Self::artifacts_tree_has_pk_bfv(circuits_base, &legacy) { - legacy - } else { - per_committee - } - } - - fn artifacts_tree_has_pk_bfv(circuits_base: &Path, artifacts_dir: &str) -> bool { - ["recursive", "default"].iter().any(|variant| { - circuits_base - .join(artifacts_dir) - .join(variant) - .join("dkg/pk/pk.json") - .exists() - }) - } - pub fn search_defaults(&self) -> Option { match self { BfvPreset::InsecureThreshold512 => Some(PresetSearchDefaults { @@ -607,21 +576,4 @@ mod tests { ); assert_eq!(BfvPreset::SecureDkg8192.artifacts_dir(), "secure-8192"); } - - #[test] - fn resolve_artifacts_dir_falls_back_to_legacy_layout() { - let temp = - std::env::temp_dir().join(format!("enclave-artifacts-test-{}", std::process::id())); - let _ = std::fs::remove_dir_all(&temp); - let legacy = temp.join("insecure-512/recursive/dkg/pk"); - std::fs::create_dir_all(&legacy).unwrap(); - std::fs::write(legacy.join("pk.json"), b"{}").unwrap(); - - let preset = BfvPreset::InsecureDkg512; - assert_eq!( - preset.resolve_artifacts_dir_for_committee("micro", &temp), - "insecure-512" - ); - let _ = std::fs::remove_dir_all(&temp); - } } diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index 97fd7ac44..1afacb965 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -1553,10 +1553,9 @@ fn handle_decrypted_shares_aggregation_proof( let circuit = DecryptedSharesAggregationCircuit; let idx_work_id = format!("{}_c7_{}", zk_bb_work_id(&request), i); - let artifacts_dir = req.params_preset.resolve_artifacts_dir_for_committee( - req.committee_size.as_str(), - prover.circuits_base(), - ); + let artifacts_dir = req + .params_preset + .artifacts_dir_for_committee(req.committee_size.as_str()); let proof = circuit .prove_with_variant( prover, diff --git a/crates/zk-prover/src/actors/proof_verification.rs b/crates/zk-prover/src/actors/proof_verification.rs index 8a7ffb382..a33bce951 100644 --- a/crates/zk-prover/src/actors/proof_verification.rs +++ b/crates/zk-prover/src/actors/proof_verification.rs @@ -128,8 +128,7 @@ impl ProofVerificationActor { ); return; }; - let artifacts_dir = - preset.resolve_artifacts_dir_for_committee(committee.as_str(), &self.circuits_base); + let artifacts_dir = preset.artifacts_dir_for_committee(committee.as_str()); let request = TypedEvent::new( ZkVerificationRequest { diff --git a/crates/zk-prover/src/prover.rs b/crates/zk-prover/src/prover.rs index 9685ba5c0..06cc0f314 100644 --- a/crates/zk-prover/src/prover.rs +++ b/crates/zk-prover/src/prover.rs @@ -48,7 +48,7 @@ impl ZkProver { } pub fn resolve_artifacts_dir(&self, preset: BfvPreset, committee: &str) -> String { - preset.resolve_artifacts_dir_for_committee(committee, &self.circuits_dir) + preset.artifacts_dir_for_committee(committee) } pub fn work_dir(&self) -> &PathBuf { From 4600c671582187c8bbbd98424433f1960cacfaa9 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Fri, 5 Jun 2026 11:18:56 +0200 Subject: [PATCH 15/16] remove dead circuit base field --- crates/zk-prover/src/actors/mod.rs | 3 +-- crates/zk-prover/src/actors/proof_verification.rs | 12 ++---------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/crates/zk-prover/src/actors/mod.rs b/crates/zk-prover/src/actors/mod.rs index 99bf59afa..66e76afea 100644 --- a/crates/zk-prover/src/actors/mod.rs +++ b/crates/zk-prover/src/actors/mod.rs @@ -79,8 +79,7 @@ pub fn setup_zk_actors( let verifier = zk_actor.clone().recipient(); let proof_request = ProofRequestActor::setup(bus, signer.clone()); - let proof_verification = - ProofVerificationActor::setup(bus, verifier, backend.circuits_dir.clone()); + let proof_verification = ProofVerificationActor::setup(bus, verifier); let share_verification = ShareVerificationActor::setup(bus); let node_proof_aggregator = NodeProofAggregator::setup(bus, signer, dkg_fold_attestation_verifiers_by_chain); diff --git a/crates/zk-prover/src/actors/proof_verification.rs b/crates/zk-prover/src/actors/proof_verification.rs index a33bce951..51613ad37 100644 --- a/crates/zk-prover/src/actors/proof_verification.rs +++ b/crates/zk-prover/src/actors/proof_verification.rs @@ -9,7 +9,6 @@ //! on-chain fault attribution. use std::collections::HashMap; -use std::path::PathBuf; use std::sync::Arc; use actix::{Actor, Addr, AsyncContext, Context, Handler, Message, Recipient}; @@ -59,30 +58,23 @@ pub struct ProofVerificationActor { pending: HashMap<(E3id, u64), PendingVerification>, /// Tracks preset + committee per E3 so we can derive `artifacts_dir` for proof verification. presets: HashMap, - circuits_base: PathBuf, } impl ProofVerificationActor { - pub fn new( - bus: &BusHandle, - verifier: Recipient>, - circuits_base: PathBuf, - ) -> Self { + pub fn new(bus: &BusHandle, verifier: Recipient>) -> Self { Self { bus: bus.clone(), verifier, pending: HashMap::new(), presets: HashMap::new(), - circuits_base, } } pub fn setup( bus: &BusHandle, verifier: Recipient>, - circuits_base: PathBuf, ) -> Addr { - let addr = Self::new(bus, verifier, circuits_base).start(); + let addr = Self::new(bus, verifier).start(); bus.subscribe(EventType::CiphernodeSelected, addr.clone().into()); bus.subscribe(EventType::EncryptionKeyReceived, addr.clone().into()); addr From 586f19efd1c1ff08b388da88b69227f09a5337f2 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Fri, 5 Jun 2026 11:19:55 +0200 Subject: [PATCH 16/16] remove dead circuit base field from zkprover --- crates/zk-prover/src/prover.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/zk-prover/src/prover.rs b/crates/zk-prover/src/prover.rs index 06cc0f314..4526fdf07 100644 --- a/crates/zk-prover/src/prover.rs +++ b/crates/zk-prover/src/prover.rs @@ -39,10 +39,6 @@ impl ZkProver { } } - pub fn circuits_base(&self) -> &PathBuf { - &self.circuits_dir - } - pub fn circuits_dir(&self, variant: CircuitVariant, artifacts_dir: &str) -> PathBuf { self.circuits_dir.join(artifacts_dir).join(variant.as_str()) }