diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f7716655f..80bb26afae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -722,6 +722,9 @@ jobs: path: | circuits/bin/dkg/target/ circuits/bin/threshold/target/ + circuits/bin/recursive_aggregation/wrapper/dkg/target/ + circuits/bin/recursive_aggregation/wrapper/threshold/target/ + circuits/bin/recursive_aggregation/fold/target/ retention-days: 1 if-no-files-found: error diff --git a/circuits/bin/recursive_aggregation/wrapper/dkg/pk/src/main.nr b/circuits/bin/recursive_aggregation/wrapper/dkg/pk/src/main.nr index c0527ba639..9797f1246a 100644 --- a/circuits/bin/recursive_aggregation/wrapper/dkg/pk/src/main.nr +++ b/circuits/bin/recursive_aggregation/wrapper/dkg/pk/src/main.nr @@ -4,7 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; +use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; use lib::math::commitments::compute_recursive_aggregation_commitment; // Number of proofs. @@ -14,12 +14,12 @@ pub global N_PUBLIC_INPUTS: u32 = 1; fn main( verification_key: UltraHonkVerificationKey, - proofs: [UltraHonkProof; N_PROOFS], + proofs: [UltraHonkZKProof; N_PROOFS], public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS], key_hash: pub Field, ) -> pub Field { for i in 0..N_PROOFS { - verify_honk_proof_non_zk(verification_key, proofs[i], public_inputs[i], key_hash); + verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash); } let mut aggregated_public_inputs = Vec::new(); diff --git a/circuits/bin/recursive_aggregation/wrapper/dkg/share_computation/src/main.nr b/circuits/bin/recursive_aggregation/wrapper/dkg/share_computation/src/main.nr index a04e07af09..e87b30c5a6 100644 --- a/circuits/bin/recursive_aggregation/wrapper/dkg/share_computation/src/main.nr +++ b/circuits/bin/recursive_aggregation/wrapper/dkg/share_computation/src/main.nr @@ -4,23 +4,23 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; +use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; use lib::configs::default::dkg::L_THRESHOLD; use lib::{configs::default::N_PARTIES, math::commitments::compute_recursive_aggregation_commitment}; // Number of proofs. -pub global N_PROOFS: u32 = 2; +pub global N_PROOFS: u32 = 1; /// Number of public inputs/outputs per proof. pub global N_PUBLIC_INPUTS: u32 = (L_THRESHOLD * N_PARTIES) + 1; fn main( verification_key: UltraHonkVerificationKey, - proofs: [UltraHonkProof; N_PROOFS], + proofs: [UltraHonkZKProof; N_PROOFS], public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS], key_hash: pub Field, ) -> pub Field { for i in 0..N_PROOFS { - verify_honk_proof_non_zk(verification_key, proofs[i], public_inputs[i], key_hash); + verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash); } let mut aggregated_public_inputs = Vec::new(); diff --git a/circuits/bin/recursive_aggregation/wrapper/dkg/share_decryption/src/main.nr b/circuits/bin/recursive_aggregation/wrapper/dkg/share_decryption/src/main.nr index eed1959848..eaa94ca197 100644 --- a/circuits/bin/recursive_aggregation/wrapper/dkg/share_decryption/src/main.nr +++ b/circuits/bin/recursive_aggregation/wrapper/dkg/share_decryption/src/main.nr @@ -4,24 +4,24 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; +use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; use lib::configs::default::dkg::L_THRESHOLD; use lib::configs::default::H; use lib::math::commitments::compute_recursive_aggregation_commitment; // Number of proofs. -pub global N_PROOFS: u32 = 2; +pub global N_PROOFS: u32 = 1; /// Number of public inputs/outputs per proof. pub global N_PUBLIC_INPUTS: u32 = (H * L_THRESHOLD) + 1; fn main( verification_key: UltraHonkVerificationKey, - proofs: [UltraHonkProof; N_PROOFS], + proofs: [UltraHonkZKProof; N_PROOFS], public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS], key_hash: pub Field, ) -> pub Field { for i in 0..N_PROOFS { - verify_honk_proof_non_zk(verification_key, proofs[i], public_inputs[i], key_hash); + verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash); } let mut aggregated_public_inputs = Vec::new(); diff --git a/circuits/bin/recursive_aggregation/wrapper/dkg/share_encryption/src/main.nr b/circuits/bin/recursive_aggregation/wrapper/dkg/share_encryption/src/main.nr index ef537eded9..2d9b92e56d 100644 --- a/circuits/bin/recursive_aggregation/wrapper/dkg/share_encryption/src/main.nr +++ b/circuits/bin/recursive_aggregation/wrapper/dkg/share_encryption/src/main.nr @@ -4,25 +4,25 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; +use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; use lib::{ configs::default::dkg::{L, N}, math::commitments::compute_recursive_aggregation_commitment, }; // Number of proofs. -pub global N_PROOFS: u32 = 2; +pub global N_PROOFS: u32 = 1; /// Number of public inputs/outputs per proof. pub global N_PUBLIC_INPUTS: u32 = (2 * L * N) + 2; fn main( verification_key: UltraHonkVerificationKey, - proofs: [UltraHonkProof; N_PROOFS], + proofs: [UltraHonkZKProof; N_PROOFS], public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS], key_hash: pub Field, ) -> pub Field { for i in 0..N_PROOFS { - verify_honk_proof_non_zk(verification_key, proofs[i], public_inputs[i], key_hash); + verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash); } let mut aggregated_public_inputs = Vec::new(); diff --git a/circuits/bin/recursive_aggregation/wrapper/threshold/decrypted_shares_aggregation/src/main.nr b/circuits/bin/recursive_aggregation/wrapper/threshold/decrypted_shares_aggregation/src/main.nr index 1cbaddd271..6d13064071 100644 --- a/circuits/bin/recursive_aggregation/wrapper/threshold/decrypted_shares_aggregation/src/main.nr +++ b/circuits/bin/recursive_aggregation/wrapper/threshold/decrypted_shares_aggregation/src/main.nr @@ -4,7 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; +use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; use lib::{ configs::default::{MAX_MSG_NON_ZERO_COEFFS, T, threshold::L}, math::commitments::compute_recursive_aggregation_commitment, @@ -18,12 +18,12 @@ pub global N_PUBLIC_INPUTS: u32 = fn main( verification_key: UltraHonkVerificationKey, - proofs: [UltraHonkProof; N_PROOFS], + proofs: [UltraHonkZKProof; N_PROOFS], public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS], key_hash: pub Field, ) -> pub Field { for i in 0..N_PROOFS { - verify_honk_proof_non_zk(verification_key, proofs[i], public_inputs[i], key_hash); + verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash); } let mut aggregated_public_inputs = Vec::new(); diff --git a/circuits/bin/recursive_aggregation/wrapper/threshold/pk_aggregation/src/main.nr b/circuits/bin/recursive_aggregation/wrapper/threshold/pk_aggregation/src/main.nr index 9c003c1ae1..030bfaee5c 100644 --- a/circuits/bin/recursive_aggregation/wrapper/threshold/pk_aggregation/src/main.nr +++ b/circuits/bin/recursive_aggregation/wrapper/threshold/pk_aggregation/src/main.nr @@ -4,7 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; +use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; use lib::{configs::default::H, math::commitments::compute_recursive_aggregation_commitment}; // Number of proofs. @@ -14,12 +14,12 @@ pub global N_PUBLIC_INPUTS: u32 = H + 1; fn main( verification_key: UltraHonkVerificationKey, - proofs: [UltraHonkProof; N_PROOFS], + proofs: [UltraHonkZKProof; N_PROOFS], public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS], key_hash: pub Field, ) -> pub Field { for i in 0..N_PROOFS { - verify_honk_proof_non_zk(verification_key, proofs[i], public_inputs[i], key_hash); + verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash); } let mut aggregated_public_inputs = Vec::new(); diff --git a/circuits/bin/recursive_aggregation/wrapper/threshold/pk_generation/src/main.nr b/circuits/bin/recursive_aggregation/wrapper/threshold/pk_generation/src/main.nr index c3d7da198c..76c79b0c21 100644 --- a/circuits/bin/recursive_aggregation/wrapper/threshold/pk_generation/src/main.nr +++ b/circuits/bin/recursive_aggregation/wrapper/threshold/pk_generation/src/main.nr @@ -4,23 +4,22 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; -use lib::configs::default::threshold::{L, N}; +use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; use lib::math::commitments::compute_recursive_aggregation_commitment; // Number of proofs. pub global N_PROOFS: u32 = 1; -/// Number of public inputs/outputs per proof. -pub global N_PUBLIC_INPUTS: u32 = (L * N) + 3; +/// Number of public inputs/outputs per proof (sk_commitment, pk_commitment, e_sm_commitment). +pub global N_PUBLIC_INPUTS: u32 = 3; fn main( verification_key: UltraHonkVerificationKey, - proofs: [UltraHonkProof; N_PROOFS], + proofs: [UltraHonkZKProof; N_PROOFS], public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS], key_hash: pub Field, ) -> pub Field { for i in 0..N_PROOFS { - verify_honk_proof_non_zk(verification_key, proofs[i], public_inputs[i], key_hash); + verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash); } let mut aggregated_public_inputs = Vec::new(); diff --git a/circuits/bin/recursive_aggregation/wrapper/threshold/share_decryption/src/main.nr b/circuits/bin/recursive_aggregation/wrapper/threshold/share_decryption/src/main.nr index 8dc4bc0cff..02deffe03f 100644 --- a/circuits/bin/recursive_aggregation/wrapper/threshold/share_decryption/src/main.nr +++ b/circuits/bin/recursive_aggregation/wrapper/threshold/share_decryption/src/main.nr @@ -4,23 +4,23 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; +use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof}; use lib::configs::default::threshold::{L, N}; use lib::math::commitments::compute_recursive_aggregation_commitment; // Number of proofs. pub global N_PROOFS: u32 = 1; /// Number of public inputs/outputs per proof. -pub global N_PUBLIC_INPUTS: u32 = 2 + 3 * L * N; +pub global N_PUBLIC_INPUTS: u32 = 2 + 2 * L * N; fn main( verification_key: UltraHonkVerificationKey, - proofs: [UltraHonkProof; N_PROOFS], + proofs: [UltraHonkZKProof; N_PROOFS], public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS], key_hash: pub Field, ) -> pub Field { for i in 0..N_PROOFS { - verify_honk_proof_non_zk(verification_key, proofs[i], public_inputs[i], key_hash); + verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash); } let mut aggregated_public_inputs = Vec::new(); diff --git a/crates/aggregator/src/lib.rs b/crates/aggregator/src/lib.rs index 6695841161..e6ba05955d 100644 --- a/crates/aggregator/src/lib.rs +++ b/crates/aggregator/src/lib.rs @@ -7,6 +7,7 @@ mod committee_finalizer; pub mod ext; mod keyshare_created_filter_buffer; +mod proof_fold; mod publickey_aggregator; mod repo; mod threshold_plaintext_aggregator; diff --git a/crates/aggregator/src/proof_fold.rs b/crates/aggregator/src/proof_fold.rs new file mode 100644 index 0000000000..8a3ac67575 --- /dev/null +++ b/crates/aggregator/src/proof_fold.rs @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +use anyhow::Result; +use e3_events::{ + prelude::*, BusHandle, ComputeRequest, CorrelationId, E3id, EventContext, Proof, Sequenced, + ZkRequest, +}; +use tracing::{error, info, warn}; + +/// Manages the state of a sequential `FoldProofs` operation. +/// +/// Takes an ordered list of proofs and folds them pairwise via `ZkRequest::FoldProofs` +/// until a single aggregated proof remains. The caller owns the struct and checks +/// `result` (or calls `is_complete`) to know when folding is done. +/// +/// Serialization support enables persistence during VerifyingC1/GeneratingC5Proof for restart recovery. +#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)] +pub struct ProofFoldState { + correlation: Option, + accumulated: Option, + remaining: Vec, + /// Total fold steps (for progress logging). Set when fold starts. + #[serde(default, skip_serializing_if = "Option::is_none")] + total_steps: Option, + /// Set when all fold steps have completed. + pub result: Option, +} + +impl ProofFoldState { + pub fn new() -> Self { + ProofFoldState { + correlation: None, + accumulated: None, + remaining: Vec::new(), + total_steps: None, + result: None, + } + } + + /// Returns `true` if a fold step was dispatched but the in-flight proof was consumed + /// and the response will never arrive (e.g. after a restart). The caller should reset + /// and re-initiate the fold from the original proofs. + pub fn needs_restart(&self) -> bool { + self.correlation.is_some() && self.accumulated.is_none() && self.result.is_none() + } + + /// Returns `true` if no fold has been initiated yet (idle / not started). + pub fn is_idle(&self) -> bool { + self.correlation.is_none() + && self.accumulated.is_none() + && self.remaining.is_empty() + && self.total_steps.is_none() + && self.result.is_none() + } + + /// Begin folding `proofs` sequentially. + /// + /// - 0 proofs → `result` stays `None` + /// - 1 proof → `result` is set immediately, no ZK request dispatched + /// - N proofs → first fold step dispatched; subsequent steps follow via `handle_response` + pub fn start( + &mut self, + mut proofs: Vec, + label: &str, + bus: &BusHandle, + e3_id: &E3id, + ec: &EventContext, + ) -> Result<()> { + match proofs.len() { + 0 => { + info!("{label}: no proofs to fold"); + Ok(()) + } + 1 => { + info!("{label}: single proof — no fold needed"); + self.result = Some(proofs.remove(0)); + Ok(()) + } + _ => { + let first = proofs.remove(0); + self.accumulated = Some(first); + self.remaining = proofs; + let total = self.remaining.len(); + self.total_steps = Some(total); + info!( + "{label}: starting fold ({} steps total, {} proofs remaining)", + total, + self.remaining.len() + ); + self.advance(label, bus, e3_id, ec) + } + } + } + + /// Handle a `FoldProofs` response. Returns `true` if `correlation_id` matched this fold. + /// + /// On match, either dispatches the next step or finalises `result`. + pub fn handle_response( + &mut self, + correlation_id: &CorrelationId, + proof: Proof, + label: &str, + bus: &BusHandle, + e3_id: &E3id, + ec: &EventContext, + ) -> Result { + let Some(expected) = self.correlation else { + return Ok(false); + }; + if expected != *correlation_id { + return Ok(false); + } + + self.correlation = None; + self.accumulated = Some(proof); + + let step_done = self + .total_steps + .map(|t| t - self.remaining.len()) + .unwrap_or(0); + info!( + "{label}: fold step {}/{} done ({} remaining)", + step_done, + self.total_steps.unwrap_or(0), + self.remaining.len() + ); + + if self.remaining.is_empty() { + self.result = self.accumulated.take(); + } else { + self.advance(label, bus, e3_id, ec)?; + } + + Ok(true) + } + + fn advance( + &mut self, + label: &str, + bus: &BusHandle, + e3_id: &E3id, + ec: &EventContext, + ) -> Result<()> { + if self.correlation.is_some() { + return Ok(()); + } + + let Some(acc) = self.accumulated.take() else { + return Ok(()); + }; + + let Some(next) = self.remaining.first().cloned() else { + self.result = Some(acc); + return Ok(()); + }; + self.remaining.remove(0); + let target_evm = self.remaining.is_empty(); + + let corr = CorrelationId::new(); + self.correlation = Some(corr); + + let step = self + .total_steps + .map(|t| t - self.remaining.len()) + .unwrap_or(0); + info!( + "{label}: dispatching fold step {}/{} ({} proofs remaining, target_evm={})", + step, + self.total_steps.unwrap_or(0), + self.remaining.len(), + target_evm + ); + + if let Err(err) = bus.publish( + ComputeRequest::zk( + ZkRequest::FoldProofs { + proof1: acc, + proof2: next, + target_evm, + }, + corr, + e3_id.clone(), + ), + ec.clone(), + ) { + error!("{label}: failed to publish fold request: {err}"); + self.correlation = None; + } + + Ok(()) + } +} diff --git a/crates/aggregator/src/publickey_aggregator.rs b/crates/aggregator/src/publickey_aggregator.rs index 4618dcc2d7..9768b72b82 100644 --- a/crates/aggregator/src/publickey_aggregator.rs +++ b/crates/aggregator/src/publickey_aggregator.rs @@ -4,22 +4,25 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +use crate::proof_fold::ProofFoldState; use actix::prelude::*; use anyhow::Result; use e3_bfv_client::client::compute_pk_commitment; use e3_data::Persistable; use e3_events::{ - prelude::*, BusHandle, Die, E3id, EnclaveEvent, EnclaveEventData, EventContext, - KeyshareCreated, OrderedSet, PartyProofsToVerify, PkAggregationProofPending, - PkAggregationProofRequest, PkAggregationProofSigned, PublicKeyAggregated, Seed, Sequenced, + prelude::*, BusHandle, ComputeResponse, ComputeResponseKind, DKGRecursiveAggregationComplete, + Die, E3id, EnclaveEvent, EnclaveEventData, EventContext, KeyshareCreated, OrderedSet, + PartyProofsToVerify, PkAggregationProofPending, PkAggregationProofRequest, + PkAggregationProofSigned, Proof, PublicKeyAggregated, Seed, Sequenced, ShareVerificationComplete, ShareVerificationDispatched, SignedProofPayload, TypedEvent, - VerificationKind, + VerificationKind, ZkResponse, }; use e3_events::{trap, EType}; use e3_fhe::{Fhe, GetAggregatePublicKey}; use e3_fhe_params::BfvPreset; use e3_utils::NotifySync; use e3_utils::{ArcBytes, MAILBOX_LIMIT}; +use std::collections::{BTreeSet, HashMap}; use std::sync::Arc; use tracing::{error, info, warn}; @@ -53,6 +56,13 @@ pub enum PublicKeyAggregatorState { public_key_hash: [u8; 32], keyshare_bytes: Vec, nodes: OrderedSet, + /// DKG recursive proofs per party (restart-critical). + dkg_node_proofs: HashMap, + honest_party_ids: BTreeSet, + dishonest_parties: BTreeSet, + cross_node_fold: ProofFoldState, + c5_proof_pending: Option, + last_ec: Option>, }, Complete { public_key: ArcBytes, @@ -247,6 +257,7 @@ impl PublicKeyAggregator { }; let dishonest_parties = &msg.dishonest_parties; + let total_parties = submission_order.len(); // Filter out dishonest parties using submission_order (insertion-order indexed, // matching the party IDs sent to dispatch_c1_verification). @@ -265,6 +276,10 @@ impl PublicKeyAggregator { ); } + let honest_party_ids: BTreeSet = (0..total_parties as u64) + .filter(|id| !dishonest_parties.contains(id)) + .collect(); + // Need at least threshold + 1 honest parties for aggregation if honest_keyshares.len() <= threshold_m { return Err(anyhow::anyhow!( @@ -320,15 +335,24 @@ impl PublicKeyAggregator { ec.clone(), )?; - // Transition to GeneratingC5Proof + // Transition to GeneratingC5Proof with restart-critical fold fields self.state.try_mutate(&ec, |_| { Ok(PublicKeyAggregatorState::GeneratingC5Proof { public_key: pubkey.clone(), public_key_hash, keyshare_bytes: honest_keyshares, nodes: honest_nodes_set, + dkg_node_proofs: HashMap::new(), + honest_party_ids: honest_party_ids.clone(), + dishonest_parties: dishonest_parties.clone(), + cross_node_fold: ProofFoldState::new(), + c5_proof_pending: None, + last_ec: Some(ec.clone()), }) })?; + + self.try_start_cross_node_fold(&ec)?; + Ok(()) } @@ -342,34 +366,225 @@ impl PublicKeyAggregator { return Ok(()); } + if !matches!( + self.state.get(), + Some(PublicKeyAggregatorState::GeneratingC5Proof { .. }) + ) { + return Err(anyhow::anyhow!( + "handle_pk_aggregation_proof_signed called outside GeneratingC5Proof state" + )); + } + + info!("C5 proof signed — waiting for cross-node DKG fold to complete..."); + + let c5_proof = msg.signed_proof.payload.proof.clone(); + self.state.try_mutate(&ec, |state| { + let PublicKeyAggregatorState::GeneratingC5Proof { + public_key, + public_key_hash, + keyshare_bytes, + nodes, + dkg_node_proofs, + honest_party_ids, + dishonest_parties, + cross_node_fold, + .. + } = state + else { + return Ok(state); + }; + Ok(PublicKeyAggregatorState::GeneratingC5Proof { + public_key, + public_key_hash, + keyshare_bytes, + nodes, + dkg_node_proofs, + honest_party_ids, + dishonest_parties, + cross_node_fold, + c5_proof_pending: Some(c5_proof), + last_ec: Some(ec.clone()), + }) + })?; + self.try_publish_complete() + } + + // -- Cross-node DKG proof aggregation -------------------------------------------------- + + fn handle_dkg_recursive_aggregation_complete( + &mut self, + msg: TypedEvent, + ) -> Result<()> { + let (msg, ec) = msg.into_components(); + + if msg.e3_id != self.e3_id { + return Ok(()); + } + + let state = self.state.get(); + let Some(PublicKeyAggregatorState::GeneratingC5Proof { + dkg_node_proofs, .. + }) = state.as_ref() + else { + return Ok(()); + }; + if dkg_node_proofs.contains_key(&msg.party_id) { + warn!( + "Duplicate DKGRecursiveAggregationComplete for party {} — ignoring", + msg.party_id + ); + return Ok(()); + } + + info!( + "PublicKeyAggregator: buffered DKG proof from party {} (buffered={})", + msg.party_id, + dkg_node_proofs.len() + 1 + ); + + self.state.try_mutate(&ec, |state| { + let PublicKeyAggregatorState::GeneratingC5Proof { + public_key, + public_key_hash, + keyshare_bytes, + nodes, + mut dkg_node_proofs, + honest_party_ids, + dishonest_parties, + cross_node_fold, + c5_proof_pending, + last_ec: _, + } = state + else { + return Ok(state); + }; + dkg_node_proofs.insert(msg.party_id, msg.aggregated_proof); + Ok(PublicKeyAggregatorState::GeneratingC5Proof { + public_key, + public_key_hash, + keyshare_bytes, + nodes, + dkg_node_proofs, + honest_party_ids, + dishonest_parties, + cross_node_fold, + c5_proof_pending, + last_ec: Some(ec.clone()), + }) + })?; + + self.try_start_cross_node_fold(&ec) + } + + /// Start cross-node fold once we have DKG proofs from all verified honest parties. + fn try_start_cross_node_fold(&mut self, ec: &EventContext) -> Result<()> { + let state = self.state.get(); + let Some(PublicKeyAggregatorState::GeneratingC5Proof { + dkg_node_proofs, + honest_party_ids, + cross_node_fold, + .. + }) = state.as_ref() + else { + return Ok(()); + }; + let all_honest_proofs_present = honest_party_ids + .iter() + .all(|id| dkg_node_proofs.contains_key(id)); + if !all_honest_proofs_present + || (!cross_node_fold.is_idle() && !cross_node_fold.needs_restart()) + { + return Ok(()); + } + + let mut pairs: Vec<_> = dkg_node_proofs + .iter() + .filter(|(pid, _)| honest_party_ids.contains(pid)) + .map(|(pid, p)| (*pid, p.clone())) + .collect(); + pairs.sort_by_key(|(pid, _)| *pid); + let proofs: Vec = pairs.into_iter().map(|(_, p)| p).collect(); + + self.state.try_mutate(ec, |state| { + let PublicKeyAggregatorState::GeneratingC5Proof { + public_key, + public_key_hash, + keyshare_bytes, + nodes, + dkg_node_proofs, + honest_party_ids, + dishonest_parties, + mut cross_node_fold, + c5_proof_pending, + last_ec, + } = state + else { + return Ok(state); + }; + if cross_node_fold.needs_restart() { + warn!("cross-node fold stuck mid-step on restart — resetting and re-folding from persisted proofs"); + cross_node_fold = ProofFoldState::new(); + } + cross_node_fold.start( + proofs, + "PublicKeyAggregator cross-node DKG fold", + &self.bus, + &self.e3_id, + ec, + )?; + Ok(PublicKeyAggregatorState::GeneratingC5Proof { + public_key, + public_key_hash, + keyshare_bytes, + nodes, + dkg_node_proofs, + honest_party_ids, + dishonest_parties, + cross_node_fold, + c5_proof_pending, + last_ec, + }) + })?; + self.try_publish_complete() + } + + /// Publish `PublicKeyAggregated` when both C5 and cross-node fold are complete. + fn try_publish_complete(&mut self) -> Result<()> { let PublicKeyAggregatorState::GeneratingC5Proof { public_key, public_key_hash, nodes, + c5_proof_pending, + cross_node_fold, + last_ec, .. } = self .state .get() .ok_or_else(|| anyhow::anyhow!("Expected GeneratingC5Proof state"))? else { - return Err(anyhow::anyhow!( - "handle_pk_aggregation_proof_signed called outside GeneratingC5Proof state" - )); + return Ok(()); + }; + + let (Some(c5_proof), Some(cross_node_proof)) = + (c5_proof_pending.as_ref(), cross_node_fold.result.as_ref()) + else { + return Ok(()); }; - info!("C5 proof signed, publishing PublicKeyAggregated..."); + let ec = last_ec + .clone() + .ok_or_else(|| anyhow::anyhow!("No EventContext for publish"))?; - let proof = msg.signed_proof.payload.proof; + info!("Both C5 and cross-node DKG proof ready — publishing PublicKeyAggregated"); - // Publish PublicKeyAggregated before transitioning state so a publish - // failure leaves us in GeneratingC5Proof (retryable) rather than - // Complete (no retry path). let event = PublicKeyAggregated { pubkey: public_key.clone(), public_key_hash, e3_id: self.e3_id.clone(), nodes: nodes.clone(), - pk_aggregation_proof: Some(proof), + pk_aggregation_proof: Some(c5_proof.clone()), + dkg_aggregated_proof: Some(cross_node_proof.clone()), }; self.bus.publish(event, ec.clone())?; @@ -381,6 +596,64 @@ impl PublicKeyAggregator { nodes, }) })?; + + Ok(()) + } + + fn handle_compute_response(&mut self, msg: TypedEvent) -> Result<()> { + let (msg, _ec) = msg.into_components(); + if let ComputeResponseKind::Zk(ZkResponse::FoldProofs(resp)) = msg.response { + if msg.e3_id != self.e3_id { + return Ok(()); + } + let state = self.state.get(); + let Some(PublicKeyAggregatorState::GeneratingC5Proof { last_ec, .. }) = state.as_ref() + else { + // Late response after transitioning out of GeneratingC5Proof — ignore. + return Ok(()); + }; + let Some(ec) = last_ec.clone() else { + return Err(anyhow::anyhow!("No EventContext for fold response")); + }; + self.state.try_mutate_without_context(|state| { + let PublicKeyAggregatorState::GeneratingC5Proof { + public_key, + public_key_hash, + keyshare_bytes, + nodes, + dkg_node_proofs, + honest_party_ids, + dishonest_parties, + mut cross_node_fold, + c5_proof_pending, + last_ec, + } = state + else { + return Ok(state); + }; + cross_node_fold.handle_response( + &msg.correlation_id, + resp.proof.clone(), + "PublicKeyAggregator cross-node DKG fold", + &self.bus, + &self.e3_id, + &ec, + )?; + Ok(PublicKeyAggregatorState::GeneratingC5Proof { + public_key, + public_key_hash, + keyshare_bytes, + nodes, + dkg_node_proofs, + honest_party_ids, + dishonest_parties, + cross_node_fold, + c5_proof_pending, + last_ec, + }) + })?; + self.try_publish_complete()?; + } Ok(()) } @@ -468,6 +741,12 @@ impl Handler for PublicKeyAggregator { EnclaveEventData::PkAggregationProofSigned(data) => { self.notify_sync(ctx, TypedEvent::new(data, ec)) } + EnclaveEventData::DKGRecursiveAggregationComplete(data) => { + self.notify_sync(ctx, TypedEvent::new(data, ec)) + } + EnclaveEventData::ComputeResponse(data) => { + self.notify_sync(ctx, TypedEvent::new(data, ec)) + } EnclaveEventData::ComputeRequestError(data) => { error!("PublicKeyAggregator received ComputeRequestError: {}", data); } @@ -581,6 +860,38 @@ impl Handler> for PublicKeyAggregator { } } +impl Handler> for PublicKeyAggregator { + type Result = (); + + fn handle( + &mut self, + msg: TypedEvent, + _ctx: &mut Self::Context, + ) -> Self::Result { + trap( + EType::PublickeyAggregation, + &self.bus.with_ec(msg.get_ctx()), + || self.handle_dkg_recursive_aggregation_complete(msg), + ) + } +} + +impl Handler> for PublicKeyAggregator { + type Result = (); + + fn handle( + &mut self, + msg: TypedEvent, + _ctx: &mut Self::Context, + ) -> Self::Result { + trap( + EType::PublickeyAggregation, + &self.bus.with_ec(msg.get_ctx()), + || self.handle_compute_response(msg), + ) + } +} + impl Handler for PublicKeyAggregator { type Result = (); fn handle(&mut self, _: Die, ctx: &mut Self::Context) -> Self::Result { diff --git a/crates/aggregator/src/threshold_plaintext_aggregator.rs b/crates/aggregator/src/threshold_plaintext_aggregator.rs index 4ce3ff0d84..d1e51d2334 100644 --- a/crates/aggregator/src/threshold_plaintext_aggregator.rs +++ b/crates/aggregator/src/threshold_plaintext_aggregator.rs @@ -6,6 +6,7 @@ use std::collections::{BTreeSet, HashMap}; +use crate::proof_fold::ProofFoldState; use actix::prelude::*; use anyhow::{anyhow, bail, ensure, Result}; use e3_data::Persistable; @@ -13,8 +14,8 @@ use e3_events::{ prelude::*, trap, AggregationProofPending, AggregationProofSigned, BusHandle, ComputeRequest, ComputeResponse, ComputeResponseKind, CorrelationId, DecryptedSharesAggregationProofRequest, DecryptionshareCreated, Die, E3id, EType, EnclaveEvent, EnclaveEventData, EventContext, - PartyProofsToVerify, PlaintextAggregated, Seed, Sequenced, ShareVerificationComplete, - ShareVerificationDispatched, SignedProofPayload, TypedEvent, VerificationKind, + PartyProofsToVerify, PlaintextAggregated, Proof, Seed, Sequenced, ShareVerificationComplete, + ShareVerificationDispatched, SignedProofPayload, TypedEvent, VerificationKind, ZkResponse, }; use e3_fhe_params::BfvPreset; use e3_sortition::{E3CommitteeContainsRequest, E3CommitteeContainsResponse, Sortition}; @@ -31,7 +32,11 @@ pub struct Collecting { threshold_m: u64, threshold_n: u64, shares: HashMap>, + /// Signed raw C6 proofs for ShareVerification. c6_proofs: HashMap>, + /// Wrapped C6 proofs for cross-node fold. + #[serde(default)] + c6_wrapped_proofs: HashMap>, seed: Seed, ciphertext_output: Vec, params: ArcBytes, @@ -43,6 +48,8 @@ pub struct VerifyingC6 { threshold_n: u64, shares: HashMap>, c6_proofs: HashMap>, + #[serde(default)] + c6_wrapped_proofs: HashMap>, ciphertext_output: Vec, params: ArcBytes, } @@ -152,6 +159,7 @@ impl ThresholdPlaintextAggregatorState { threshold_n, shares: HashMap::new(), c6_proofs: HashMap::new(), + c6_wrapped_proofs: HashMap::new(), seed, ciphertext_output, params, @@ -165,6 +173,12 @@ pub struct ThresholdPlaintextAggregator { e3_id: E3id, params_preset: BfvPreset, state: Persistable, + /// C6 cross-node proof fold state. + c6_fold: ProofFoldState, + /// C7 proofs stored while waiting for C6 fold completion. + c7_proofs_pending: Option>, + /// Last event context, reused for fold steps and final publish. + last_ec: Option>, } pub struct ThresholdPlaintextAggregatorParams { @@ -185,6 +199,9 @@ impl ThresholdPlaintextAggregator { e3_id: params.e3_id, params_preset: params.params_preset, state, + c6_fold: ProofFoldState::new(), + c7_proofs_pending: None, + last_ec: None, } } @@ -192,7 +209,8 @@ impl ThresholdPlaintextAggregator { &mut self, party_id: u64, share: Vec, - decryption_proofs: Vec, + signed_decryption_proofs: Vec, + wrapped_proofs: Vec, ec: &EventContext, ) -> Result<()> { self.state.try_mutate(ec, |state| { @@ -204,10 +222,12 @@ impl ThresholdPlaintextAggregator { let params = current.params.clone(); let mut shares = current.shares; let mut c6_proofs = current.c6_proofs; + let mut c6_wrapped_proofs = current.c6_wrapped_proofs; info!("pushing to share collection {} {:?}", party_id, share); shares.insert(party_id, share); - c6_proofs.insert(party_id, decryption_proofs); + c6_proofs.insert(party_id, signed_decryption_proofs); + c6_wrapped_proofs.insert(party_id, wrapped_proofs); if (shares.len() as u64) < threshold_n { return Ok(ThresholdPlaintextAggregatorState::Collecting(Collecting { @@ -217,6 +237,7 @@ impl ThresholdPlaintextAggregator { ciphertext_output, shares, c6_proofs, + c6_wrapped_proofs, seed: current.seed, })); } @@ -230,6 +251,7 @@ impl ThresholdPlaintextAggregator { VerifyingC6 { shares, c6_proofs, + c6_wrapped_proofs, ciphertext_output, threshold_m, threshold_n, @@ -316,6 +338,15 @@ impl ThresholdPlaintextAggregator { honest_shares.len() ); + // Collect honest C6 wrapped proofs sorted by party_id for cross-node folding. + let mut honest_c6_wrapped: Vec<(u64, Vec)> = state + .c6_wrapped_proofs + .iter() + .filter(|(id, _)| !dishonest_parties.contains(id)) + .map(|(id, proofs)| (*id, proofs.clone())) + .collect(); + honest_c6_wrapped.sort_by_key(|(id, _)| *id); + // Publish ComputeRequest before transitioning state so a publish // failure leaves us in VerifyingC6 (retryable) rather than // Computing (no retry path). @@ -346,6 +377,21 @@ impl ThresholdPlaintextAggregator { })) })?; + // Start C6 cross-node fold concurrently with threshold decryption. + self.last_ec = Some(ec.clone()); + let proofs: Vec = honest_c6_wrapped + .into_iter() + .flat_map(|(_, proofs)| proofs) + .collect(); + self.c6_fold.start( + proofs, + "ThresholdPlaintextAggregator C6 fold", + &self.bus, + &self.e3_id, + &ec, + )?; + self.try_publish_complete()?; + Ok(()) } @@ -376,7 +422,7 @@ impl ThresholdPlaintextAggregator { Ok(()) } - /// Handle AggregationProofSigned: transition to Complete and publish PlaintextAggregated. + /// Handle AggregationProofSigned: store C7 proofs and wait for C6 fold before publishing. pub fn handle_aggregation_proof_signed( &mut self, msg: TypedEvent, @@ -393,9 +439,6 @@ impl ThresholdPlaintextAggregator { .ok_or(anyhow!("Could not get state"))? .try_into()?; - let plaintext = state.plaintext.clone(); - let shares = state.shares.clone(); - // Extract raw proofs from signed payloads for PlaintextAggregated let proofs: Vec<_> = msg .signed_proofs @@ -404,31 +447,16 @@ impl ThresholdPlaintextAggregator { .collect(); ensure!( - proofs.len() == plaintext.len(), + proofs.len() == state.plaintext.len(), "C7 proof count mismatch: got {} proofs for {} ciphertext indices", proofs.len(), - plaintext.len() + state.plaintext.len() ); - // Publish PlaintextAggregated before transitioning state so a publish - // failure leaves us in GeneratingC7Proof (retryable) rather than - // Complete (no retry path). - let event = PlaintextAggregated { - decrypted_output: plaintext.clone(), - e3_id: self.e3_id.clone(), - aggregation_proofs: proofs, - }; - - info!("Dispatching plaintext event with C7 proofs {:?}", event); - self.bus.publish(event, ec.clone())?; - - self.state.try_mutate(&ec, |_| { - Ok(ThresholdPlaintextAggregatorState::Complete(Complete { - decrypted: plaintext, - shares, - })) - })?; - Ok(()) + info!("C7 proof signed — waiting for C6 cross-node fold to complete..."); + self.c7_proofs_pending = Some(proofs); + self.last_ec = Some(ec); + self.try_publish_complete() } pub fn handle_compute_response(&mut self, msg: TypedEvent) -> Result<()> { @@ -438,6 +466,7 @@ impl ThresholdPlaintextAggregator { "PlaintextAggregator should never receive incorrect e3_id msgs" ); + let correlation_id = msg.correlation_id; match msg.response { // TrBFV threshold decryption response -> transition to GeneratingC7Proof ComputeResponseKind::TrBFV(TrBFVResponse::CalculateThresholdDecryption(response)) => { @@ -478,6 +507,24 @@ impl ThresholdPlaintextAggregator { })?; } + // C6 cross-node fold response + ComputeResponseKind::Zk(ZkResponse::FoldProofs(resp)) => { + let ec = self + .last_ec + .clone() + .ok_or_else(|| anyhow!("No EventContext for C6 fold response"))?; + if self.c6_fold.handle_response( + &correlation_id, + resp.proof, + "ThresholdPlaintextAggregator C6 fold", + &self.bus, + &self.e3_id, + &ec, + )? { + self.try_publish_complete()?; + } + } + _ => { // Not a response we handle — ignore } @@ -485,6 +532,51 @@ impl ThresholdPlaintextAggregator { Ok(()) } + + /// Publish `PlaintextAggregated` when both C7 proofs and C6 fold are complete. + fn try_publish_complete(&mut self) -> Result<()> { + let (Some(c7_proofs), Some(c6_proof)) = ( + self.c7_proofs_pending.as_ref(), + self.c6_fold.result.as_ref(), + ) else { + return Ok(()); + }; + + let state: GeneratingC7Proof = self + .state + .get() + .ok_or_else(|| anyhow!("Expected GeneratingC7Proof state"))? + .try_into()?; + + let ec = self + .last_ec + .clone() + .ok_or_else(|| anyhow!("No EventContext for publish"))?; + + info!("Both C7 and C6 fold proof ready — publishing PlaintextAggregated"); + + let event = PlaintextAggregated { + decrypted_output: state.plaintext.clone(), + e3_id: self.e3_id.clone(), + aggregation_proofs: c7_proofs.clone(), + c6_aggregated_proof: Some(c6_proof.clone()), + }; + + info!( + "Dispatching plaintext event with C7 and C6 proofs {:?}", + event + ); + self.bus.publish(event, ec.clone())?; + + self.state.try_mutate(&ec, |_| { + Ok(ThresholdPlaintextAggregatorState::Complete(Complete { + decrypted: state.plaintext, + shares: state.shares, + })) + })?; + + Ok(()) + } } impl Actor for ThresholdPlaintextAggregator { @@ -572,12 +664,19 @@ impl Handler>> party_id, decryption_share, signed_decryption_proofs, + wrapped_proofs, .. }, ec, ) = msg.into_inner().into_components(); - self.add_share(party_id, decryption_share, signed_decryption_proofs, &ec)?; + self.add_share( + party_id, + decryption_share, + signed_decryption_proofs, + wrapped_proofs, + &ec, + )?; // If we transitioned to VerifyingC6, dispatch C6 verification // using the proofs persisted in state diff --git a/crates/events/src/enclave_event/compute_request/mod.rs b/crates/events/src/enclave_event/compute_request/mod.rs index 3d8efabc23..c7b32f1e7b 100644 --- a/crates/events/src/enclave_event/compute_request/mod.rs +++ b/crates/events/src/enclave_event/compute_request/mod.rs @@ -91,6 +91,7 @@ impl ToString for ComputeRequest { ZkRequest::PkAggregation(_) => "ZkPkAggregation", ZkRequest::ThresholdShareDecryption(_) => "ZkThresholdShareDecryption", ZkRequest::DecryptedSharesAggregation(_) => "ZkDecryptedSharesAggregation", + ZkRequest::FoldProofs { .. } => "ZkFoldProofs", }, } .to_string() diff --git a/crates/events/src/enclave_event/compute_request/zk.rs b/crates/events/src/enclave_event/compute_request/zk.rs index 9258b46187..724c823b56 100644 --- a/crates/events/src/enclave_event/compute_request/zk.rs +++ b/crates/events/src/enclave_event/compute_request/zk.rs @@ -36,6 +36,13 @@ pub enum ZkRequest { ThresholdShareDecryption(ThresholdShareDecryptionProofRequest), /// Generate proof for decrypted shares aggregation (C7). DecryptedSharesAggregation(DecryptedSharesAggregationProofRequest), + /// Fold two proofs into one (incremental recursive aggregation). + /// When `target_evm` is true, the output proof is generated for on-chain EVM verification. + FoldProofs { + proof1: Proof, + proof2: Proof, + target_evm: bool, + }, } /// Request to generate a proof for public key aggregation (C5). @@ -207,6 +214,8 @@ pub enum ZkResponse { ThresholdShareDecryption(ThresholdShareDecryptionProofResponse), /// Proof for decrypted shares aggregation (C7). DecryptedSharesAggregation(DecryptedSharesAggregationProofResponse), + /// Folded proof (output of two-proof fold). + FoldProofs(FoldProofsResponse), } /// Response containing a generated proof for public key aggregation (C5). @@ -237,14 +246,17 @@ pub struct ThresholdShareDecryptionProofRequest { /// Response containing generated proofs for threshold share decryption (C6). #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ThresholdShareDecryptionProofResponse { - /// One C6 proof per ciphertext index. + /// One raw C6 proof per ciphertext index. pub proofs: Vec, + /// Wrapper proof for each C6 proof (same order as `proofs`), ready for recursive folding. + pub wrapped_proofs: Vec, } /// Response containing a generated share computation proof. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ShareComputationProofResponse { pub proof: Proof, + pub wrapped_proof: Proof, pub dkg_input_type: DkgInputType, } @@ -252,6 +264,7 @@ pub struct ShareComputationProofResponse { #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ShareEncryptionProofResponse { pub proof: Proof, + pub wrapped_proof: Proof, pub dkg_input_type: DkgInputType, pub recipient_party_id: usize, pub row_index: usize, @@ -263,48 +276,59 @@ pub struct ShareEncryptionProofResponse { #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct PkBfvProofResponse { pub proof: Proof, + pub wrapped_proof: Proof, } /// Response containing a generated PK generation proof. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct PkGenerationProofResponse { pub proof: Proof, + pub wrapped_proof: Proof, } /// Response containing a generated DKG share decryption proof. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct DkgShareDecryptionProofResponse { pub proof: Proof, + pub wrapped_proof: Proof, pub dkg_input_type: DkgInputType, } impl DkgShareDecryptionProofResponse { - pub fn new(proof: Proof, dkg_input_type: DkgInputType) -> Self { + pub fn new(proof: Proof, wrapped_proof: Proof, dkg_input_type: DkgInputType) -> Self { Self { proof, + wrapped_proof, dkg_input_type, } } } impl ShareComputationProofResponse { - pub fn new(proof: Proof, dkg_input_type: DkgInputType) -> Self { + pub fn new(proof: Proof, wrapped_proof: Proof, dkg_input_type: DkgInputType) -> Self { Self { proof, + wrapped_proof, dkg_input_type, } } } impl PkBfvProofResponse { - pub fn new(proof: Proof) -> Self { - Self { proof } + pub fn new(proof: Proof, wrapped_proof: Proof) -> Self { + Self { + proof, + wrapped_proof, + } } } impl PkGenerationProofResponse { - pub fn new(proof: Proof) -> Self { - Self { proof } + pub fn new(proof: Proof, wrapped_proof: Proof) -> Self { + Self { + proof, + wrapped_proof, + } } } @@ -400,6 +424,12 @@ pub struct DecryptedSharesAggregationProofResponse { pub proofs: Vec, } +/// Response containing the folded proof. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct FoldProofsResponse { + pub proof: Proof, +} + /// ZK-specific error variants. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum ZkError { diff --git a/crates/events/src/enclave_event/decryptionshare_created.rs b/crates/events/src/enclave_event/decryptionshare_created.rs index e34e096b8f..b959096ba7 100644 --- a/crates/events/src/enclave_event/decryptionshare_created.rs +++ b/crates/events/src/enclave_event/decryptionshare_created.rs @@ -4,7 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use crate::{E3id, SignedProofPayload}; +use crate::{E3id, Proof, SignedProofPayload}; use actix::Message; use e3_utils::utility_types::ArcBytes; use serde::{Deserialize, Serialize}; @@ -18,9 +18,12 @@ pub struct DecryptionshareCreated { // ciphertext pub e3_id: E3id, pub node: String, - /// C6 proofs: one signed proof of correct decryption per ciphertext index. + /// C6 raw proofs (signed): one per ciphertext index, used for ShareVerification. #[serde(default)] pub signed_decryption_proofs: Vec, + /// C6 wrapped proofs: one per ciphertext index, used for cross-node fold. + #[serde(default)] + pub wrapped_proofs: Vec, } impl Display for DecryptionshareCreated { diff --git a/crates/events/src/enclave_event/dkg_inner_proof_ready.rs b/crates/events/src/enclave_event/dkg_inner_proof_ready.rs new file mode 100644 index 0000000000..5d257ad388 --- /dev/null +++ b/crates/events/src/enclave_event/dkg_inner_proof_ready.rs @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Event emitted by [`ProofRequestActor`] for each inner DKG proof as it completes. +//! +//! [`NodeProofAggregator`] consumes these events and folds them incrementally +//! in strict `seq` order into a single aggregated proof. + +use crate::{E3id, Proof}; +use serde::{Deserialize, Serialize}; + +/// A single wrapped inner proof ready for incremental aggregation. +/// +/// Emitted for every inner circuit (C0-C4) as soon as its wrapped proof +/// is available. `seq` gives the deterministic ordering. +/// +/// The total count of expected proofs is communicated separately via +/// [`ThresholdSharePending`], which is always published before the first +/// `DKGInnerProofReady` for a given E3. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct DKGInnerProofReady { + pub e3_id: E3id, + pub party_id: u64, + /// Already-wrapped proof (single-element RecursiveAggregation output). + pub wrapped_proof: Proof, + /// Deterministic sequence index for ordered folding. + pub seq: usize, +} diff --git a/crates/events/src/enclave_event/dkg_recursive_aggregation_complete.rs b/crates/events/src/enclave_event/dkg_recursive_aggregation_complete.rs new file mode 100644 index 0000000000..820f483683 --- /dev/null +++ b/crates/events/src/enclave_event/dkg_recursive_aggregation_complete.rs @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Event published by [`NodeProofAggregator`] when all inner proofs for a +//! DKG node have been incrementally folded into a single aggregated proof. +//! +//! [`PublicKeyAggregator`] collects these from all honest nodes for the +//! cross-node aggregation phase. + +use crate::{E3id, Proof}; +use serde::{Deserialize, Serialize}; + +/// NodeProofAggregator -> PublicKeyAggregator: fully aggregated DKG node proof. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct DKGRecursiveAggregationComplete { + pub e3_id: E3id, + pub party_id: u64, + pub aggregated_proof: Proof, +} diff --git a/crates/events/src/enclave_event/mod.rs b/crates/events/src/enclave_event/mod.rs index ca01d963db..0ac3145177 100644 --- a/crates/events/src/enclave_event/mod.rs +++ b/crates/events/src/enclave_event/mod.rs @@ -23,6 +23,8 @@ mod decryption_share_proof_signed; mod decryption_share_proofs; mod decryptionshare_created; mod die; +mod dkg_inner_proof_ready; +mod dkg_recursive_aggregation_complete; mod e3_failed; mod e3_request_complete; mod e3_requested; @@ -85,6 +87,8 @@ pub use decryption_share_proof_signed::*; pub use decryption_share_proofs::*; pub use decryptionshare_created::*; pub use die::*; +pub use dkg_inner_proof_ready::*; +pub use dkg_recursive_aggregation_complete::*; pub use e3_failed::*; pub use e3_request_complete::*; pub use e3_requested::*; @@ -290,6 +294,8 @@ pub enum EnclaveEventData { PkAggregationProofSigned(PkAggregationProofSigned), AggregationProofPending(AggregationProofPending), AggregationProofSigned(AggregationProofSigned), + DKGInnerProofReady(DKGInnerProofReady), + DKGRecursiveAggregationComplete(DKGRecursiveAggregationComplete), /// This is a test event to use in testing TestEvent(TestEvent), } @@ -563,6 +569,8 @@ impl EnclaveEventData { EnclaveEventData::PkAggregationProofSigned(ref data) => Some(data.e3_id.clone()), EnclaveEventData::AggregationProofPending(ref data) => Some(data.e3_id.clone()), EnclaveEventData::AggregationProofSigned(ref data) => Some(data.e3_id.clone()), + EnclaveEventData::DKGRecursiveAggregationComplete(ref data) => Some(data.e3_id.clone()), + EnclaveEventData::DKGInnerProofReady(ref data) => Some(data.e3_id.clone()), _ => None, } } @@ -652,7 +660,9 @@ impl_event_types!( PkAggregationProofPending, PkAggregationProofSigned, AggregationProofPending, - AggregationProofSigned + AggregationProofSigned, + DKGInnerProofReady, + DKGRecursiveAggregationComplete ); impl TryFrom<&EnclaveEvent> for EnclaveError { diff --git a/crates/events/src/enclave_event/plaintext_aggregated.rs b/crates/events/src/enclave_event/plaintext_aggregated.rs index bda9a2f3a0..aa77de0a25 100644 --- a/crates/events/src/enclave_event/plaintext_aggregated.rs +++ b/crates/events/src/enclave_event/plaintext_aggregated.rs @@ -20,6 +20,9 @@ pub struct PlaintextAggregated { /// C7 proofs: one proof of correct aggregation per ciphertext index. #[serde(default)] pub aggregation_proofs: Vec, + /// Cross-node folded C6 proof: all honest nodes' threshold share decryption proofs folded into one. + #[serde(default)] + pub c6_aggregated_proof: Option, } impl Display for PlaintextAggregated { diff --git a/crates/events/src/enclave_event/publickey_aggregated.rs b/crates/events/src/enclave_event/publickey_aggregated.rs index bcf4563890..5baa0033ae 100644 --- a/crates/events/src/enclave_event/publickey_aggregated.rs +++ b/crates/events/src/enclave_event/publickey_aggregated.rs @@ -24,6 +24,9 @@ pub struct PublicKeyAggregated { /// C5 proof: proof of correct pk aggregation. #[serde(default)] pub pk_aggregation_proof: Option, + /// Cross-node aggregated DKG proof: all honest nodes' recursive proofs folded into one. + #[serde(default)] + pub dkg_aggregated_proof: Option, } impl Display for PublicKeyAggregated { diff --git a/crates/evm/src/evm_read_interface.rs b/crates/evm/src/evm_read_interface.rs index cc5b3d8e9c..a7bafb47d2 100644 --- a/crates/evm/src/evm_read_interface.rs +++ b/crates/evm/src/evm_read_interface.rs @@ -35,6 +35,11 @@ const PROVIDER_RECREATE_MAX_ATTEMPTS: u32 = 3; const PROVIDER_RECREATE_INITIAL_DELAY_MS: u64 = 2000; /// Consecutive failures before we assume the provider is dead and recreate it. const MAX_RETRIES_BEFORE_RECREATE: u32 = 3; +/// How often to check if the subscription is still delivering events (seconds). +/// If the chain head has advanced beyond our last received block and no events +/// arrive within this window, we assume the subscription is stale and +/// resubscribe. +const SUBSCRIPTION_STALENESS_CHECK_SECS: u64 = 30; #[derive(Default, serde::Serialize, serde::Deserialize, Clone)] pub struct EvmReadInterfaceState { @@ -404,11 +409,18 @@ async fn stream_from_evm( let mut stream = subscription.into_stream(); info!(chain_id, "Live event subscription active"); + let mut staleness_check = + tokio::time::interval(Duration::from_secs(SUBSCRIPTION_STALENESS_CHECK_SECS)); + // Skip the immediate first tick + staleness_check.tick().await; + loop { select! { maybe_log = stream.next() => { match maybe_log { Some(log) => { + // Reset staleness timer on every received event + staleness_check.reset(); if let Some(bn) = log.block_number { last_block = last_block.max(bn); } @@ -425,6 +437,29 @@ async fn stream_from_evm( } } } + _ = staleness_check.tick() => { + // Periodically check if chain has advanced past our last block. + // If so, the subscription may be stale (server stopped pushing). + match current_provider.provider().get_block_number().await { + Ok(head) if head > last_block => { + warn!( + chain_id, + last_block, + head, + "Subscription appears stale (chain advanced but no events received), resubscribing" + ); + let _ = current_provider.provider().unsubscribe(sub_id).await; + break; + } + Err(e) => { + warn!(chain_id, error = %e, "Staleness check failed, resubscribing"); + break; + } + _ => { + // Chain hasn't advanced, subscription is fine + } + } + } _ = &mut shutdown => { info!("Shutdown signal received, stopping EVM stream"); let _ = current_provider.provider().unsubscribe(sub_id).await; diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index a27436aed2..18c7d6c7ca 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -27,10 +27,10 @@ use e3_events::{ ComputeResponse, DecryptedSharesAggregationProofRequest, DecryptedSharesAggregationProofResponse, DkgShareDecryptionProofRequest, DkgShareDecryptionProofResponse, EnclaveEvent, EnclaveEventData, EventPublisher, - EventSubscriber, EventType, PartyVerificationResult, PkAggregationProofRequest, - PkAggregationProofResponse, PkBfvProofRequest, PkBfvProofResponse, PkGenerationProofRequest, - PkGenerationProofResponse, ShareComputationProofRequest, ShareComputationProofResponse, - ShareEncryptionProofRequest, ShareEncryptionProofResponse, + EventSubscriber, EventType, FoldProofsResponse, PartyVerificationResult, + PkAggregationProofRequest, PkAggregationProofResponse, PkBfvProofRequest, PkBfvProofResponse, + PkGenerationProofRequest, PkGenerationProofResponse, ShareComputationProofRequest, + ShareComputationProofResponse, ShareEncryptionProofRequest, ShareEncryptionProofResponse, ThresholdShareDecryptionProofRequest, ThresholdShareDecryptionProofResponse, TypedEvent, VerifyShareDecryptionProofsRequest, VerifyShareDecryptionProofsResponse, VerifyShareProofsRequest, VerifyShareProofsResponse, ZkError as ZkEventError, ZkRequest, @@ -67,7 +67,9 @@ 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_prover::{Provable, ZkBackend, ZkProver}; +use e3_zk_prover::{ + generate_fold_proof, generate_wrapper_proof, CircuitVariant, Provable, ZkBackend, ZkProver, +}; use fhe::bfv::{Ciphertext, Encoding, Plaintext, PublicKey, SecretKey}; use fhe::mbfv::PublicKeyShare; use fhe_traits::{DeserializeParametrized, FheEncoder}; @@ -314,11 +316,17 @@ fn handle_pk_aggregation_proof( a, }; - // 7. Generate proof via Provable trait + // 7. Generate proof via Provable trait (C5 is always EVM-targeted for on-chain verification) let circuit = PkAggregationCircuit; let e3_id_str = request.e3_id.to_string(); let proof = circuit - .prove(prover, &req.params_preset, &circuit_data, &e3_id_str) + .prove_with_variant( + prover, + &req.params_preset, + &circuit_data, + &e3_id_str, + CircuitVariant::Evm, + ) .map_err(|e| { ComputeRequestError::new( ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), @@ -369,6 +377,8 @@ fn handle_threshold_share_decryption_proof( )); } let mut proofs = Vec::with_capacity(num_indices); + let mut wrapped_proofs = Vec::with_capacity(num_indices); + let e3_id_str = request.e3_id.to_string(); for i in 0..num_indices { // Deserialize ciphertext @@ -405,7 +415,6 @@ fn handle_threshold_share_decryption_proof( // Generate proof let circuit = e3_zk_helpers::threshold::share_decryption::ShareDecryptionCircuit; - let e3_id_str = request.e3_id.to_string(); let proof = circuit .prove(prover, &req.params_preset, &circuit_data, &e3_id_str) .map_err(|e| { @@ -417,11 +426,27 @@ fn handle_threshold_share_decryption_proof( request.clone(), ) })?; + + // Wrap for recursive folding + let wrapped = generate_wrapper_proof(prover, &proof, &e3_id_str).map_err(|e| { + ComputeRequestError::new( + ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(format!( + "C6 wrapper proof[{}]: {}", + i, e + ))), + request.clone(), + ) + })?; + proofs.push(proof); + wrapped_proofs.push(wrapped); } Ok(ComputeResponse::zk( - ZkResponse::ThresholdShareDecryption(ThresholdShareDecryptionProofResponse { proofs }), + ZkResponse::ThresholdShareDecryption(ThresholdShareDecryptionProofResponse { + proofs, + wrapped_proofs, + }), request.correlation_id, request.e3_id, )) @@ -613,6 +638,24 @@ fn handle_zk_request( handle_decrypted_shares_aggregation_proof(&prover, req, request.clone()) }) } + ZkRequest::FoldProofs { + proof1, + proof2, + target_evm, + } => timefunc("zk_fold_proofs", id, || { + let e3_id_str = request.e3_id.to_string(); + match generate_fold_proof(&prover, &proof1, &proof2, &e3_id_str, target_evm) { + Ok(proof) => Ok(ComputeResponse::zk( + ZkResponse::FoldProofs(FoldProofsResponse { proof }), + request.correlation_id, + request.e3_id.clone(), + )), + Err(e) => Err(ComputeRequestError::new( + ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), + request.clone(), + )), + } + }), } } @@ -694,10 +737,18 @@ fn handle_share_computation_proof( ) })?; + let wrapped_proof = generate_wrapper_proof(prover, &proof, &e3_id_str).map_err(|e| { + ComputeRequestError::new( + ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), + request.clone(), + ) + })?; + // 7. Return response Ok(ComputeResponse::zk( ZkResponse::ShareComputation(ShareComputationProofResponse { proof, + wrapped_proof, dkg_input_type: req.dkg_input_type, }), request.correlation_id, @@ -770,9 +821,16 @@ fn handle_pk_generation_proof( ) })?; + let wrapped_proof = generate_wrapper_proof(prover, &proof, &e3_id_str).map_err(|e| { + ComputeRequestError::new( + ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), + request.clone(), + ) + })?; + // 6. Return response Ok(ComputeResponse::zk( - ZkResponse::PkGeneration(PkGenerationProofResponse::new(proof)), + ZkResponse::PkGeneration(PkGenerationProofResponse::new(proof, wrapped_proof)), request.correlation_id, request.e3_id, )) @@ -814,8 +872,15 @@ fn handle_pk_bfv_proof( ) })?; + let wrapped_proof = generate_wrapper_proof(prover, &proof, &e3_id_str).map_err(|e| { + ComputeRequestError::new( + ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), + request.clone(), + ) + })?; + Ok(ComputeResponse::zk( - ZkResponse::PkBfv(PkBfvProofResponse::new(proof)), + ZkResponse::PkBfv(PkBfvProofResponse::new(proof, wrapped_proof)), request.correlation_id, request.e3_id, )) @@ -894,9 +959,17 @@ fn handle_share_encryption_proof( ) })?; + let wrapped_proof = generate_wrapper_proof(prover, &proof, &e3_id_str).map_err(|e| { + ComputeRequestError::new( + ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), + request.clone(), + ) + })?; + Ok(ComputeResponse::zk( ZkResponse::ShareEncryption(ShareEncryptionProofResponse { proof, + wrapped_proof, dkg_input_type: req.dkg_input_type, recipient_party_id: req.recipient_party_id, row_index: req.row_index, @@ -979,10 +1052,18 @@ fn handle_dkg_share_decryption_proof( ) })?; + let wrapped_proof = generate_wrapper_proof(prover, &proof, &e3_id_str).map_err(|e| { + ComputeRequestError::new( + ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), + request.clone(), + ) + })?; + // 6. Return response Ok(ComputeResponse::zk( ZkResponse::DkgShareDecryption(DkgShareDecryptionProofResponse { proof, + wrapped_proof, dkg_input_type: req.dkg_input_type, }), request.correlation_id, @@ -1217,7 +1298,7 @@ fn handle_decrypted_shares_aggregation_proof( threshold: req.threshold_m as usize, }; - // e. Build circuit data and generate proof + // e. Build circuit data and generate proof (C7 is always EVM-targeted for on-chain verification) let circuit_data = DecryptedSharesAggregationCircuitData { committee, d_share_polys, @@ -1228,7 +1309,13 @@ fn handle_decrypted_shares_aggregation_proof( let circuit = DecryptedSharesAggregationCircuit; let e3_id_str = request.e3_id.to_string(); let proof = circuit - .prove(prover, &req.params_preset, &circuit_data, &e3_id_str) + .prove_with_variant( + prover, + &req.params_preset, + &circuit_data, + &e3_id_str, + CircuitVariant::Evm, + ) .map_err(|e| { ComputeRequestError::new( ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(format!( diff --git a/crates/net/src/net_event_translator.rs b/crates/net/src/net_event_translator.rs index f018899f9e..215c0da073 100644 --- a/crates/net/src/net_event_translator.rs +++ b/crates/net/src/net_event_translator.rs @@ -92,6 +92,7 @@ impl NetEventTranslator { // Add a list of events allowed to be forwarded to libp2p match event.get_data() { EnclaveEventData::DecryptionshareCreated(_) => true, + EnclaveEventData::DKGRecursiveAggregationComplete(_) => true, EnclaveEventData::KeyshareCreated(_) => true, EnclaveEventData::PlaintextAggregated(_) => true, EnclaveEventData::PublicKeyAggregated(_) => true, diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index 97b162aeb9..1cd07afa63 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -95,181 +95,287 @@ async fn setup_test_zk_backend() -> (ZkBackend, tempfile::TempDir) { #[cfg(not(unix))] compile_error!("Integration tests require unix symlink support"); - // Copy circuit artifacts from the compiled circuit build output let circuits_build_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("..") .join("..") .join("circuits") .join("bin"); - - // Copy circuit artifacts under the "recursive" variant subdirectory, - // because prove() defaults to CircuitVariant::Recursive. - // - // The build system (build-circuits.ts) generates three VK flavors per circuit: - // - {name}.vk → evm verifier target (for on-chain verification) - // - {name}.vk_recursive → noir-recursive-no-zk target (Default variant) - // - {name}.vk_noir → noir-recursive target (Recursive variant) - // - // Each variant directory stores its VK as just ".vk", so for the recursive/ - // directory we must copy .vk_noir as .vk (matching what the build system does). - let recursive_dir = circuits_dir.join("recursive"); - - // Copy T0 (pk) circuit - let pk_circuit_dir = recursive_dir.join("dkg").join("pk"); - tokio::fs::create_dir_all(&pk_circuit_dir).await.unwrap(); let dkg_target = circuits_build_root.join("dkg").join("target"); - tokio::fs::copy(dkg_target.join("pk.json"), pk_circuit_dir.join("pk.json")) + let threshold_target = circuits_build_root.join("threshold").join("target"); + let wrapper_dkg_target = circuits_build_root + .join("recursive_aggregation") + .join("wrapper") + .join("dkg") + .join("target"); + let wrapper_threshold_target = circuits_build_root + .join("recursive_aggregation") + .join("wrapper") + .join("threshold") + .join("target"); + let fold_target = circuits_build_root + .join("recursive_aggregation") + .join("fold") + .join("target"); + + // Helper: copy {name}.json + VK artifacts into a destination directory. + // vk_suffix/vk_hash_suffix select the source VK flavor: + // ".vk_noir" / ".vk_noir_hash" → Recursive variant (inner proofs) + // ".vk_recursive" / ".vk_recursive_hash" → Default variant (wrapper/fold proofs) + // ".vk" / ".vk_hash" → EVM variant + async fn copy_circuit( + src_dir: &std::path::Path, + dst_dir: &std::path::Path, + name: &str, + vk_suffix: &str, + vk_hash_suffix: &str, + ) { + tokio::fs::create_dir_all(dst_dir).await.unwrap(); + tokio::fs::copy( + src_dir.join(format!("{name}.json")), + dst_dir.join(format!("{name}.json")), + ) .await .unwrap(); - tokio::fs::copy(dkg_target.join("pk.vk_noir"), pk_circuit_dir.join("pk.vk")) + tokio::fs::copy( + src_dir.join(format!("{name}{vk_suffix}")), + dst_dir.join(format!("{name}.vk")), + ) .await .unwrap(); - - // Copy C1 (pk_generation) circuit - let pk_gen_circuit_dir = recursive_dir.join("threshold").join("pk_generation"); - tokio::fs::create_dir_all(&pk_gen_circuit_dir) + tokio::fs::copy( + src_dir.join(format!("{name}{vk_hash_suffix}")), + dst_dir.join(format!("{name}.vk_hash")), + ) .await .unwrap(); - let threshold_target = circuits_build_root.join("threshold").join("target"); - tokio::fs::copy( - threshold_target.join("pk_generation.json"), - pk_gen_circuit_dir.join("pk_generation.json"), + } + + // ── recursive/ variant (inner/base proofs, uses .vk_noir) ────────── + + let rv = circuits_dir.join("recursive"); + + // T0 (pk) + copy_circuit( + &dkg_target, + &rv.join("dkg/pk"), + "pk", + ".vk_noir", + ".vk_noir_hash", ) - .await - .unwrap(); - tokio::fs::copy( - threshold_target.join("pk_generation.vk_noir"), - pk_gen_circuit_dir.join("pk_generation.vk"), + .await; + // C1 (pk_generation) + copy_circuit( + &threshold_target, + &rv.join("threshold/pk_generation"), + "pk_generation", + ".vk_noir", + ".vk_noir_hash", ) - .await - .unwrap(); - - // Copy C2a (sk_share_computation) circuit - let sk_share_comp_circuit_dir = recursive_dir.join("dkg").join("sk_share_computation"); - tokio::fs::create_dir_all(&sk_share_comp_circuit_dir) - .await - .unwrap(); - tokio::fs::copy( - dkg_target.join("sk_share_computation.json"), - sk_share_comp_circuit_dir.join("sk_share_computation.json"), + .await; + // C2a (sk_share_computation) + copy_circuit( + &dkg_target, + &rv.join("dkg/sk_share_computation"), + "sk_share_computation", + ".vk_noir", + ".vk_noir_hash", ) - .await - .unwrap(); - tokio::fs::copy( - dkg_target.join("sk_share_computation.vk_noir"), - sk_share_comp_circuit_dir.join("sk_share_computation.vk"), + .await; + // C2b (e_sm_share_computation) + copy_circuit( + &dkg_target, + &rv.join("dkg/e_sm_share_computation"), + "e_sm_share_computation", + ".vk_noir", + ".vk_noir_hash", ) - .await - .unwrap(); - - // Copy C2b (e_sm_share_computation) circuit - let e_sm_share_comp_circuit_dir = recursive_dir.join("dkg").join("e_sm_share_computation"); - tokio::fs::create_dir_all(&e_sm_share_comp_circuit_dir) - .await - .unwrap(); - tokio::fs::copy( - dkg_target.join("e_sm_share_computation.json"), - e_sm_share_comp_circuit_dir.join("e_sm_share_computation.json"), + .await; + // C3 (share_encryption) + copy_circuit( + &dkg_target, + &rv.join("dkg/share_encryption"), + "share_encryption", + ".vk_noir", + ".vk_noir_hash", ) - .await - .unwrap(); - tokio::fs::copy( - dkg_target.join("e_sm_share_computation.vk_noir"), - e_sm_share_comp_circuit_dir.join("e_sm_share_computation.vk"), + .await; + // C4 (dkg/share_decryption) + copy_circuit( + &dkg_target, + &rv.join("dkg/share_decryption"), + "share_decryption", + ".vk_noir", + ".vk_noir_hash", ) - .await - .unwrap(); - - // Copy C3 (share_encryption) circuit — single circuit used for both SK and E_SM - let share_enc_circuit_dir = recursive_dir.join("dkg").join("share_encryption"); - tokio::fs::create_dir_all(&share_enc_circuit_dir) - .await - .unwrap(); - tokio::fs::copy( - dkg_target.join("share_encryption.json"), - share_enc_circuit_dir.join("share_encryption.json"), + .await; + // C5 (pk_aggregation) + copy_circuit( + &threshold_target, + &rv.join("threshold/pk_aggregation"), + "pk_aggregation", + ".vk_noir", + ".vk_noir_hash", ) - .await - .unwrap(); - tokio::fs::copy( - dkg_target.join("share_encryption.vk_noir"), - share_enc_circuit_dir.join("share_encryption.vk"), + .await; + // C6 (threshold/share_decryption) + copy_circuit( + &threshold_target, + &rv.join("threshold/share_decryption"), + "share_decryption", + ".vk_noir", + ".vk_noir_hash", ) - .await - .unwrap(); + .await; + // C7 (decrypted_shares_aggregation_mod) + copy_circuit( + &threshold_target, + &rv.join("threshold/decrypted_shares_aggregation_mod"), + "decrypted_shares_aggregation_mod", + ".vk_noir", + ".vk_noir_hash", + ) + .await; - // Copy C4 (share_decryption) circuit — used for DKG share decryption proofs (Exchange #3) - let share_dec_circuit_dir = recursive_dir.join("dkg").join("share_decryption"); - tokio::fs::create_dir_all(&share_dec_circuit_dir) - .await - .unwrap(); - tokio::fs::copy( - dkg_target.join("share_decryption.json"), - share_dec_circuit_dir.join("share_decryption.json"), + // ── default/ variant (wrapper & fold proofs, uses .vk_recursive) ─── + + let dv = circuits_dir.join("default"); + + // DKG wrapper circuits + let dkg_wrapper_base = dv.join("recursive_aggregation/wrapper/dkg"); + copy_circuit( + &wrapper_dkg_target, + &dkg_wrapper_base.join("pk"), + "pk", + ".vk_recursive", + ".vk_recursive_hash", ) - .await - .unwrap(); - tokio::fs::copy( - dkg_target.join("share_decryption.vk_noir"), - share_dec_circuit_dir.join("share_decryption.vk"), + .await; + copy_circuit( + &wrapper_dkg_target, + &dkg_wrapper_base.join("share_computation"), + "share_computation", + ".vk_recursive", + ".vk_recursive_hash", ) - .await - .unwrap(); - - // Copy C5 (pk_aggregation) circuit - let pk_agg_circuit_dir = recursive_dir.join("threshold").join("pk_aggregation"); - tokio::fs::create_dir_all(&pk_agg_circuit_dir) - .await - .unwrap(); - tokio::fs::copy( - threshold_target.join("pk_aggregation.json"), - pk_agg_circuit_dir.join("pk_aggregation.json"), + .await; + copy_circuit( + &wrapper_dkg_target, + &dkg_wrapper_base.join("share_encryption"), + "share_encryption", + ".vk_recursive", + ".vk_recursive_hash", ) - .await - .unwrap(); - tokio::fs::copy( - threshold_target.join("pk_aggregation.vk_noir"), - pk_agg_circuit_dir.join("pk_aggregation.vk"), + .await; + copy_circuit( + &wrapper_dkg_target, + &dkg_wrapper_base.join("share_decryption"), + "share_decryption", + ".vk_recursive", + ".vk_recursive_hash", ) - .await - .unwrap(); - - // Copy C6 (threshold/share_decryption) circuit for C6 verification - let threshold_share_dec_circuit_dir = - recursive_dir.join("threshold").join("share_decryption"); - tokio::fs::create_dir_all(&threshold_share_dec_circuit_dir) + .await; + + // share_computation aliases (sk_share_computation, e_sm_share_computation) + for alias in ["sk_share_computation", "e_sm_share_computation"] { + let alias_dir = dkg_wrapper_base.join(alias); + tokio::fs::create_dir_all(&alias_dir).await.unwrap(); + let sc_dir = dkg_wrapper_base.join("share_computation"); + tokio::fs::copy( + sc_dir.join("share_computation.json"), + alias_dir.join(format!("{alias}.json")), + ) + .await + .unwrap(); + tokio::fs::copy( + sc_dir.join("share_computation.vk"), + alias_dir.join(format!("{alias}.vk")), + ) + .await + .unwrap(); + tokio::fs::copy( + sc_dir.join("share_computation.vk_hash"), + alias_dir.join(format!("{alias}.vk_hash")), + ) .await .unwrap(); - tokio::fs::copy( - threshold_target.join("share_decryption.json"), - threshold_share_dec_circuit_dir.join("share_decryption.json"), + } + + // Threshold wrapper circuits + let threshold_wrapper_base = dv.join("recursive_aggregation/wrapper/threshold"); + copy_circuit( + &wrapper_threshold_target, + &threshold_wrapper_base.join("pk_generation"), + "pk_generation", + ".vk_recursive", + ".vk_recursive_hash", ) - .await - .unwrap(); - tokio::fs::copy( - threshold_target.join("share_decryption.vk_noir"), - threshold_share_dec_circuit_dir.join("share_decryption.vk"), + .await; + copy_circuit( + &wrapper_threshold_target, + &threshold_wrapper_base.join("pk_aggregation"), + "pk_aggregation", + ".vk_recursive", + ".vk_recursive_hash", ) - .await - .unwrap(); + .await; + copy_circuit( + &wrapper_threshold_target, + &threshold_wrapper_base.join("share_decryption"), + "share_decryption", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await; + copy_circuit( + &wrapper_threshold_target, + &threshold_wrapper_base.join("decrypted_shares_aggregation"), + "decrypted_shares_aggregation", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await; + + // Fold circuit (default variant) + copy_circuit( + &fold_target, + &dv.join("recursive_aggregation/fold"), + "fold", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await; - // Copy C7 (decrypted_shares_aggregation_mod) circuit - let dsa_circuit_dir = recursive_dir - .join("threshold") - .join("decrypted_shares_aggregation_mod"); - tokio::fs::create_dir_all(&dsa_circuit_dir).await.unwrap(); - tokio::fs::copy( - threshold_target.join("decrypted_shares_aggregation_mod.json"), - dsa_circuit_dir.join("decrypted_shares_aggregation_mod.json"), + // ── evm/ variant (on-chain verification: C5, C7, fold) ─────────── + + let ev = circuits_dir.join("evm"); + + // C5 (pk_aggregation) — EVM-targeted + copy_circuit( + &threshold_target, + &ev.join("threshold/pk_aggregation"), + "pk_aggregation", + ".vk", + ".vk_hash", ) - .await - .unwrap(); - tokio::fs::copy( - threshold_target.join("decrypted_shares_aggregation_mod.vk_noir"), - dsa_circuit_dir.join("decrypted_shares_aggregation_mod.vk"), + .await; + // C7 (decrypted_shares_aggregation_mod) — EVM-targeted + copy_circuit( + &threshold_target, + &ev.join("threshold/decrypted_shares_aggregation_mod"), + "decrypted_shares_aggregation_mod", + ".vk", + ".vk_hash", ) - .await - .unwrap(); + .await; + // Fold circuit — final EVM fold + copy_circuit( + &fold_target, + &ev.join("recursive_aggregation/fold"), + "fold", + ".vk", + ".vk_hash", + ) + .await; let backend = ZkBackend::new(BBPath::Default(bb_binary), circuits_dir, work_dir); (backend, temp) @@ -653,7 +759,7 @@ async fn test_trbfv_actor() -> Result<()> { committee_finalized_timer.elapsed(), )); - // Wait for KeyshareCreated + C1 verification + C5 proof + PublicKeyAggregated + // Wait for KeyshareCreated + C1 verification + C5 proof + cross-node DKG fold + PublicKeyAggregated // - KeyshareCreated × 3 (forwarded from committee nodes) // - ShareVerificationDispatched (C1 proof verification dispatched by PublicKeyAggregator) // - ComputeRequest (C1 ZK verification) @@ -664,6 +770,9 @@ async fn test_trbfv_actor() -> Result<()> { // - ComputeRequest (C5 proof generation) // - ComputeResponse (C5 proof result) // - PkAggregationProofSigned (C5 proof signed by ProofRequestActor) + // - DKGRecursiveAggregationComplete × 3 (per-node aggregation from NodeProofAggregator) + // - ComputeRequest × 2 (cross-node DKG fold, 3 proofs → 2 pairwise steps) + // - ComputeResponse × 2 (cross-node DKG fold results) // - PublicKeyAggregated × 1 let shares_to_pubkey_agg_timer = Instant::now(); let h = nodes @@ -683,6 +792,13 @@ async fn test_trbfv_actor() -> Result<()> { "ComputeRequest", "ComputeResponse", "PkAggregationProofSigned", + "DKGRecursiveAggregationComplete", + "DKGRecursiveAggregationComplete", + "DKGRecursiveAggregationComplete", + "ComputeRequest", + "ComputeResponse", + "ComputeRequest", + "ComputeResponse", "PublicKeyAggregated", ], Duration::from_secs(5000), @@ -771,11 +887,15 @@ async fn test_trbfv_actor() -> Result<()> { // - 1 ComputeRequest (C7 proof generation) // - 1 ComputeResponse (C7 proof result) // - 1 AggregationProofSigned (C7 proof signed by ProofRequestActor) - // - 1 PlaintextAggregated (with C7 proofs) + // - 8 ComputeRequest + 8 ComputeResponse (C6 fold: 9 proofs -> 8 pairwise steps) + // - 1 PlaintextAggregated (with C7 + C6 proofs) + // - 1 E3RequestComplete (published after PlaintextAggregated by request router) // Internal events from committee nodes (ComputeRequest/Response for CalculateDecryptionShare) // stay on their local buses. - // Total: 1 + 3 + 1 + 2 + 9 + 1 + 2 + 1 + 2 + 1 + 1 = 24 events - let expected_count = 1 + 3 + 1 + 2 + 9 + 1 + 2 + 1 + 2 + 1 + 1; + let c6_proof_count = threshold_n as usize * num_votes_per_voter; + let c6_fold_steps = c6_proof_count.saturating_sub(1); + let c6_fold_events = 2 * c6_fold_steps; + let expected_count = 1 + 3 + 1 + 2 + 9 + 1 + 2 + 1 + 2 + 1 + 2 + c6_fold_events + 1; let h = nodes .take_history_with_timeouts( @@ -791,17 +911,27 @@ async fn test_trbfv_actor() -> Result<()> { publishing_ct_timer.elapsed(), )); - let Some(EnclaveEventData::PlaintextAggregated(PlaintextAggregated { - decrypted_output: plaintext, - aggregation_proofs, - .. - })) = h.last().map(|e| e.get_data()) - else { - bail!( - "Expected last event to be PlaintextAggregated, got: {:?}", - h.event_types() - ) - }; + let (plaintext, aggregation_proofs) = h + .iter() + .rev() + .find_map(|e| { + if let EnclaveEventData::PlaintextAggregated(PlaintextAggregated { + decrypted_output, + aggregation_proofs, + .. + }) = e.get_data() + { + Some((decrypted_output.clone(), aggregation_proofs.clone())) + } else { + None + } + }) + .ok_or_else(|| { + anyhow::anyhow!( + "Expected PlaintextAggregated in events, got: {:?}", + h.event_types() + ) + })?; assert!( !aggregation_proofs.is_empty(), @@ -897,12 +1027,14 @@ async fn test_p2p_actor_forwards_events_to_network() -> Result<()> { e3_id: E3id::new("1235", 1), decrypted_output: vec![ArcBytes::from_bytes(&[1, 2, 3, 4])], aggregation_proofs: vec![], + c6_aggregated_proof: None, }; let evt_2 = PlaintextAggregated { e3_id: E3id::new("1236", 1), decrypted_output: vec![ArcBytes::from_bytes(&[1, 2, 3, 4])], aggregation_proofs: vec![], + c6_aggregated_proof: None, }; let local_evt_3 = CiphernodeSelected { @@ -1365,6 +1497,7 @@ async fn test_duplicate_e3_id_with_different_chain_id() -> Result<()> { e3_id: E3id::new("1234", 1), nodes: OrderedSet::from(eth_addrs.clone()), pk_aggregation_proof: None, + dkg_aggregated_proof: None, } .into() ); @@ -1408,6 +1541,7 @@ async fn test_duplicate_e3_id_with_different_chain_id() -> Result<()> { e3_id: E3id::new("1234", 2), nodes: OrderedSet::from(eth_addrs.clone()), pk_aggregation_proof: None, + dkg_aggregated_proof: None, } .into() ); diff --git a/crates/zk-prover/src/actors/mod.rs b/crates/zk-prover/src/actors/mod.rs index ec09c73964..0edbb62178 100644 --- a/crates/zk-prover/src/actors/mod.rs +++ b/crates/zk-prover/src/actors/mod.rs @@ -35,6 +35,7 @@ pub mod accusation_manager; pub mod accusation_manager_ext; +pub mod node_proof_aggregator; pub mod proof_request; pub mod proof_verification; pub mod share_verification; @@ -42,6 +43,7 @@ pub mod zk_actor; pub use accusation_manager::AccusationManager; pub use accusation_manager_ext::AccusationManagerExtension; +pub use node_proof_aggregator::NodeProofAggregator; pub use proof_request::ProofRequestActor; pub use proof_verification::{ ProofVerificationActor, ZkVerificationRequest, ZkVerificationResponse, @@ -66,12 +68,14 @@ pub fn setup_zk_actors(bus: &BusHandle, backend: &ZkBackend, signer: PrivateKeyS let proof_request = ProofRequestActor::setup(bus, signer); let proof_verification = ProofVerificationActor::setup(bus, verifier); let share_verification = ShareVerificationActor::setup(bus); + let node_proof_aggregator = NodeProofAggregator::setup(bus); ZkActors { zk_actor, proof_request, proof_verification, share_verification, + node_proof_aggregator, } } @@ -81,4 +85,5 @@ pub struct ZkActors { pub proof_request: Addr, pub proof_verification: Addr, pub share_verification: Addr, + pub node_proof_aggregator: Addr, } diff --git a/crates/zk-prover/src/actors/node_proof_aggregator.rs b/crates/zk-prover/src/actors/node_proof_aggregator.rs new file mode 100644 index 0000000000..881acb67d3 --- /dev/null +++ b/crates/zk-prover/src/actors/node_proof_aggregator.rs @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Incremental node-level proof aggregation for DKG. +//! +//! `NodeProofAggregator` subscribes to `ThresholdSharePending` to learn how +//! many proofs to expect per E3, then folds each `DKGInnerProofReady` wrapped +//! proof into a running aggregate in strict `seq` order, using a reorder +//! buffer for out-of-order arrivals. +//! +//! When all expected proofs have been folded, it publishes +//! `DKGRecursiveAggregationComplete`. +//! +//! Ordering guarantee: `ThresholdSharePending` is always published to the bus +//! before any `DKGInnerProofReady` for the same E3 (ProofRequestActor holds +//! back C0's event until after `ThresholdSharePending` is processed). +//! Arriving out of that order is treated as a programming error and logged. + +use std::collections::{BTreeMap, HashMap}; + +use actix::{Actor, Addr, Context, Handler}; +use e3_events::{ + BusHandle, ComputeRequest, ComputeRequestError, ComputeResponse, ComputeResponseKind, + CorrelationId, DKGInnerProofReady, DKGRecursiveAggregationComplete, E3id, EnclaveEvent, + EnclaveEventData, EventContext, EventPublisher, EventSubscriber, EventType, Proof, Sequenced, + ThresholdSharePending, TypedEvent, ZkRequest, ZkResponse, +}; +use tracing::{error, info, warn}; + +/// Per-E3 rolling aggregation state for one node's proofs. +struct RollingAggregationState { + party_id: u64, + /// Total proofs expected (for progress logging). + total_expected: usize, + /// Proofs buffered out-of-order, keyed by seq index. + buffer: BTreeMap, + /// The running accumulated (folded) proof. + accumulated: Option, + /// Next seq index expected for folding. + next_to_aggregate: usize, + /// Number of proofs remaining to process (decrements on first store + each fold completion). + remaining: usize, + /// Correlation ID for the in-flight fold, if any. + fold_correlation: Option, + /// EventContext for publishing. + last_ec: EventContext, +} + +/// Actor that incrementally folds DKG inner proofs into a single node-level proof. +pub struct NodeProofAggregator { + bus: BusHandle, + states: HashMap, + /// Reverse map: fold correlation_id -> e3_id. + fold_correlation: HashMap, +} + +impl NodeProofAggregator { + pub fn new(bus: &BusHandle) -> Self { + Self { + bus: bus.clone(), + states: HashMap::new(), + fold_correlation: HashMap::new(), + } + } + + pub fn setup(bus: &BusHandle) -> Addr { + let addr = Self::new(bus).start(); + bus.subscribe(EventType::ThresholdSharePending, addr.clone().into()); + bus.subscribe(EventType::DKGInnerProofReady, addr.clone().into()); + bus.subscribe(EventType::ComputeResponse, addr.clone().into()); + bus.subscribe(EventType::ComputeRequestError, addr.clone().into()); + addr + } + + fn handle_threshold_share_pending(&mut self, msg: TypedEvent) { + let (msg, ec) = msg.into_components(); + let e3_id = msg.e3_id.clone(); + + let sk_enc_count = msg.sk_share_encryption_requests.len(); + let e_sm_enc_count = msg.e_sm_share_encryption_requests.len(); + // Must mirror the formula in ProofRequestActor::handle_threshold_share_pending: + // C0 + C1 + C2a + C2b + C3a×sk_enc + C3b×e_sm_enc + C4a + C4b + let total_expected = 4 + sk_enc_count + e_sm_enc_count + 2; + + self.states.entry(e3_id.clone()).or_insert_with(|| { + info!( + "NodeProofAggregator: initializing state for E3 {} party {} (total_expected={}, ~{} fold steps)", + e3_id, msg.full_share.party_id, total_expected, + total_expected.saturating_sub(1), + ); + RollingAggregationState { + party_id: msg.full_share.party_id, + total_expected, + buffer: BTreeMap::new(), + accumulated: None, + next_to_aggregate: 0, + remaining: total_expected, + fold_correlation: None, + last_ec: ec, + } + }); + } + + fn handle_inner_proof_ready(&mut self, msg: TypedEvent) { + let (msg, ec) = msg.into_components(); + let e3_id = msg.e3_id.clone(); + + let Some(state) = self.states.get_mut(&e3_id) else { + error!( + "NodeProofAggregator: received DKGInnerProofReady for E3 {} before ThresholdSharePending — proof dropped", + e3_id + ); + return; + }; + + state.buffer.insert(msg.seq, msg.wrapped_proof); + state.last_ec = ec; + + info!( + "NodeProofAggregator: buffered seq={} for E3 {} (remaining={})", + msg.seq, e3_id, state.remaining + ); + + self.try_advance(&e3_id); + } + + fn try_advance(&mut self, e3_id: &E3id) { + loop { + let state = match self.states.get_mut(e3_id) { + Some(s) => s, + None => return, + }; + + if state.fold_correlation.is_some() || state.remaining == 0 { + return; + } + + let next_proof = match state.buffer.remove(&state.next_to_aggregate) { + Some(p) => p, + None => return, // not yet available + }; + + if state.accumulated.is_none() { + // First proof: store as accumulated + info!( + "NodeProofAggregator: storing first proof (seq={}) for E3 {}", + state.next_to_aggregate, e3_id + ); + state.accumulated = Some(next_proof); + state.remaining -= 1; + state.next_to_aggregate += 1; + + if state.remaining == 0 { + // Only one proof total — done immediately + self.publish_complete(e3_id); + return; + } + } else { + // Fold accumulated + next_proof + let acc = state.accumulated.take().expect("checked above"); + let acc_restore = acc.clone(); + let next_proof_restore = next_proof.clone(); + let seq = state.next_to_aggregate; + let corr = CorrelationId::new(); + let ec = state.last_ec.clone(); + let e3_id_clone = e3_id.clone(); + + let folds_completed = state.total_expected - state.remaining - 1; + let total_folds = state.total_expected.saturating_sub(1); + info!( + "NodeProofAggregator: dispatching fold step {}/{} (seq={}) for E3 {}", + folds_completed + 1, + total_folds, + seq, + e3_id + ); + + match self.bus.publish( + ComputeRequest::zk( + ZkRequest::FoldProofs { + proof1: acc, + proof2: next_proof, + target_evm: false, + }, + corr, + e3_id_clone, + ), + ec, + ) { + Ok(()) => { + state.fold_correlation = Some(corr); + state.next_to_aggregate += 1; + self.fold_correlation.insert(corr, e3_id.clone()); + } + Err(err) => { + error!( + "NodeProofAggregator: failed to publish fold request for E3 {}: {err}", + e3_id + ); + state.accumulated = Some(acc_restore); + state.buffer.insert(seq, next_proof_restore); + } + } + + return; // wait for fold response + } + } + } + + fn handle_fold_response(&mut self, correlation_id: &CorrelationId, proof: Proof) { + let Some(e3_id) = self.fold_correlation.remove(correlation_id) else { + return; + }; + + let Some(state) = self.states.get_mut(&e3_id) else { + error!( + "NodeProofAggregator: received fold response for unknown E3 {}", + e3_id + ); + return; + }; + + state.remaining -= 1; + let folds_completed = state.total_expected - state.remaining - 1; + let total_folds = state.total_expected.saturating_sub(1); + info!( + "NodeProofAggregator: fold step {}/{} complete for E3 {} ({} proofs remaining)", + folds_completed, total_folds, e3_id, state.remaining + ); + + state.accumulated = Some(proof); + state.fold_correlation = None; + + if state.remaining == 0 { + self.publish_complete(&e3_id); + } else { + self.try_advance(&e3_id); + } + } + + fn publish_complete(&mut self, e3_id: &E3id) { + let Some(state) = self.states.remove(e3_id) else { + return; + }; + + let Some(aggregated_proof) = state.accumulated else { + error!( + "NodeProofAggregator: no accumulated proof for E3 {} at completion", + e3_id + ); + return; + }; + + info!( + "NodeProofAggregator: all proofs folded for E3 {} party {} — publishing DKGRecursiveAggregationComplete", + e3_id, state.party_id + ); + + if let Err(err) = self.bus.publish( + DKGRecursiveAggregationComplete { + e3_id: e3_id.clone(), + party_id: state.party_id, + aggregated_proof, + }, + state.last_ec, + ) { + error!( + "NodeProofAggregator: failed to publish DKGRecursiveAggregationComplete for E3 {}: {err}", + e3_id + ); + } + } +} + +impl Actor for NodeProofAggregator { + type Context = Context; +} + +impl Handler for NodeProofAggregator { + type Result = (); + + fn handle(&mut self, msg: EnclaveEvent, _ctx: &mut Self::Context) -> Self::Result { + let (data, ec) = msg.into_components(); + match data { + EnclaveEventData::ThresholdSharePending(data) => { + self.handle_threshold_share_pending(TypedEvent::new(data, ec)); + } + EnclaveEventData::DKGInnerProofReady(data) => { + self.handle_inner_proof_ready(TypedEvent::new(data, ec)); + } + EnclaveEventData::ComputeResponse(data) => { + self.handle_compute_response(TypedEvent::new(data, ec)); + } + EnclaveEventData::ComputeRequestError(data) => { + self.handle_compute_request_error(TypedEvent::new(data, ec)); + } + _ => {} + } + } +} + +impl Handler> for NodeProofAggregator { + type Result = (); + + fn handle( + &mut self, + msg: TypedEvent, + _ctx: &mut Self::Context, + ) -> Self::Result { + self.handle_threshold_share_pending(msg); + } +} + +impl Handler> for NodeProofAggregator { + type Result = (); + + fn handle( + &mut self, + msg: TypedEvent, + _ctx: &mut Self::Context, + ) -> Self::Result { + self.handle_inner_proof_ready(msg); + } +} + +impl Handler> for NodeProofAggregator { + type Result = (); + + fn handle( + &mut self, + msg: TypedEvent, + _ctx: &mut Self::Context, + ) -> Self::Result { + self.handle_compute_response(msg); + } +} + +impl Handler> for NodeProofAggregator { + type Result = (); + + fn handle( + &mut self, + msg: TypedEvent, + _ctx: &mut Self::Context, + ) -> Self::Result { + self.handle_compute_request_error(msg); + } +} + +impl NodeProofAggregator { + fn handle_compute_response(&mut self, msg: TypedEvent) { + let (msg, _ec) = msg.into_components(); + if let ComputeResponseKind::Zk(ZkResponse::FoldProofs(resp)) = msg.response { + self.handle_fold_response(&msg.correlation_id, resp.proof); + } + } + + fn handle_compute_request_error(&mut self, msg: TypedEvent) { + let (msg, _ec) = msg.into_components(); + if let Some(e3_id) = self.fold_correlation.remove(msg.correlation_id()) { + error!( + "NodeProofAggregator: fold request failed for E3 {}: {:?} — aggregation aborted", + e3_id, + msg.get_err() + ); + self.states.remove(&e3_id); + warn!( + "NodeProofAggregator: E3 {} aggregation state discarded due to fold error", + e3_id + ); + } + } +} diff --git a/crates/zk-prover/src/actors/proof_request.rs b/crates/zk-prover/src/actors/proof_request.rs index 818ee6b5c1..64d71ca593 100644 --- a/crates/zk-prover/src/actors/proof_request.rs +++ b/crates/zk-prover/src/actors/proof_request.rs @@ -12,13 +12,14 @@ use alloy::signers::local::PrivateKeySigner; use e3_events::{ AggregationProofPending, AggregationProofSigned, BusHandle, ComputeRequest, ComputeRequestError, ComputeRequestErrorKind, ComputeResponse, ComputeResponseKind, - CorrelationId, DecryptionKeyShared, DecryptionShareProofSigned, DecryptionShareProofsPending, - DecryptionshareCreated, DkgProofSigned, E3Failed, E3Stage, E3id, EnclaveEvent, - EnclaveEventData, EncryptionKey, EncryptionKeyCreated, EncryptionKeyPending, EventContext, - EventPublisher, EventSubscriber, EventType, FailureReason, PkAggregationProofPending, - PkAggregationProofSigned, PkBfvProofRequest, PkGenerationProofSigned, Proof, ProofPayload, - ProofType, Sequenced, ShareDecryptionProofPending, SignedProofPayload, ThresholdShare, - ThresholdShareCreated, ThresholdSharePending, TypedEvent, ZkRequest, ZkResponse, + CorrelationId, DKGInnerProofReady, DecryptionKeyShared, DecryptionShareProofSigned, + DecryptionShareProofsPending, DecryptionshareCreated, DkgProofSigned, E3Failed, E3Stage, E3id, + EnclaveEvent, EnclaveEventData, EncryptionKey, EncryptionKeyCreated, EncryptionKeyPending, + EventContext, EventPublisher, EventSubscriber, EventType, FailureReason, + PkAggregationProofPending, PkAggregationProofSigned, PkBfvProofRequest, + PkGenerationProofSigned, Proof, ProofPayload, ProofType, Sequenced, + ShareDecryptionProofPending, SignedProofPayload, ThresholdShare, ThresholdShareCreated, + ThresholdSharePending, TypedEvent, ZkRequest, ZkResponse, }; use e3_utils::utility_types::ArcBytes; use e3_utils::NotifySync; @@ -40,6 +41,15 @@ enum ThresholdProofKind { }, } +/// Per-E3 metadata for streaming DKG inner proof aggregation. +#[derive(Clone, Debug)] +struct NodeAggregationMeta { + party_id: u64, + total_expected: usize, + /// Buffered C0 wrapped proof, if it arrived before meta was stored. + pending_c0: Option, +} + #[derive(Clone, Debug)] struct PendingProofRequest { e3_id: E3id, @@ -195,10 +205,12 @@ pub struct ProofRequestActor { bus: BusHandle, signer: PrivateKeySigner, pending: HashMap, - threshold_correlation: HashMap, + threshold_correlation: HashMap, pending_threshold: HashMap, - /// C4 proof staging: correlation -> (e3_id, kind) - decryption_correlation: HashMap, + /// C4 proof staging: correlation -> (e3_id, kind, seq) + decryption_correlation: HashMap, + /// Per-E3 metadata for DKGInnerProofReady emission. + node_agg_meta: HashMap, /// C4 pending proofs per E3 pending_decryption: HashMap, /// C6 proof staging: correlation -> e3_id @@ -225,6 +237,7 @@ impl ProofRequestActor { threshold_correlation: HashMap::new(), decryption_correlation: HashMap::new(), pending_decryption: HashMap::new(), + node_agg_meta: HashMap::new(), share_decryption_correlation: HashMap::new(), pending_share_decryption: HashMap::new(), pk_aggregation_correlation: HashMap::new(), @@ -281,6 +294,34 @@ impl ProofRequestActor { let sk_enc_count = msg.sk_share_encryption_requests.len(); let e_sm_enc_count = msg.e_sm_share_encryption_requests.len(); + let total_expected = 4 + sk_enc_count + e_sm_enc_count + 2; + let pending_c0 = self + .node_agg_meta + .get(&e3_id) + .and_then(|m| m.pending_c0.clone()); + self.node_agg_meta.insert( + e3_id.clone(), + NodeAggregationMeta { + party_id: msg.full_share.party_id, + total_expected, + pending_c0: None, + }, + ); + // If C0 wrapped proof arrived before meta, emit DKGInnerProofReady now + if let Some(c0_proof) = pending_c0 { + if let Err(err) = self.bus.publish( + DKGInnerProofReady { + e3_id: e3_id.clone(), + party_id: msg.full_share.party_id, + wrapped_proof: c0_proof, + seq: 0, + }, + ec.clone(), + ) { + error!("Failed to publish DKGInnerProofReady for C0: {err}"); + } + } + self.pending_threshold.insert( e3_id.clone(), PendingThresholdProofs::new( @@ -295,8 +336,10 @@ impl ProofRequestActor { // C1: PkGeneration let t1_corr = CorrelationId::new(); - self.threshold_correlation - .insert(t1_corr, (e3_id.clone(), ThresholdProofKind::PkGeneration)); + self.threshold_correlation.insert( + t1_corr, + (e3_id.clone(), ThresholdProofKind::PkGeneration, 1), + ); info!("Requesting C1 PkGeneration proof"); if let Err(err) = self.bus.publish( ComputeRequest::zk( @@ -316,7 +359,7 @@ impl ProofRequestActor { let t2a_corr = CorrelationId::new(); self.threshold_correlation.insert( t2a_corr, - (e3_id.clone(), ThresholdProofKind::SkShareComputation), + (e3_id.clone(), ThresholdProofKind::SkShareComputation, 2), ); info!("Requesting C2a SkShareComputation proof"); if let Err(err) = self.bus.publish( @@ -329,7 +372,7 @@ impl ProofRequestActor { ) { error!("Failed to publish C2a proof request: {err}"); self.threshold_correlation - .retain(|_, (eid, _)| *eid != e3_id); + .retain(|_, (eid, _, _)| *eid != e3_id); self.pending_threshold.remove(&e3_id); return; } @@ -338,7 +381,7 @@ impl ProofRequestActor { let t2b_corr = CorrelationId::new(); self.threshold_correlation.insert( t2b_corr, - (e3_id.clone(), ThresholdProofKind::ESmShareComputation), + (e3_id.clone(), ThresholdProofKind::ESmShareComputation, 3), ); info!("Requesting C2b ESmShareComputation proof"); if let Err(err) = self.bus.publish( @@ -351,7 +394,7 @@ impl ProofRequestActor { ) { error!("Failed to publish C2b proof request: {err}"); self.threshold_correlation - .retain(|_, (eid, _)| *eid != e3_id); + .retain(|_, (eid, _, _)| *eid != e3_id); self.pending_threshold.remove(&e3_id); return; } @@ -361,7 +404,7 @@ impl ProofRequestActor { "Requesting {} C3a SkShareEncryption proofs for E3 {}", sk_enc_count, e3_id ); - for req in msg.sk_share_encryption_requests { + for (i, req) in msg.sk_share_encryption_requests.into_iter().enumerate() { let corr = CorrelationId::new(); self.threshold_correlation.insert( corr, @@ -371,6 +414,7 @@ impl ProofRequestActor { recipient_party_id: req.recipient_party_id, row_index: req.row_index, }, + 4 + i, ), ); if let Err(err) = self.bus.publish( @@ -379,7 +423,7 @@ impl ProofRequestActor { ) { error!("Failed to publish C3a proof request: {err}"); self.threshold_correlation - .retain(|_, (eid, _)| *eid != e3_id); + .retain(|_, (eid, _, _)| *eid != e3_id); self.pending_threshold.remove(&e3_id); return; } @@ -390,7 +434,7 @@ impl ProofRequestActor { "Requesting {} C3b ESmShareEncryption proofs for E3 {}", e_sm_enc_count, e3_id ); - for req in msg.e_sm_share_encryption_requests { + for (j, req) in msg.e_sm_share_encryption_requests.into_iter().enumerate() { let corr = CorrelationId::new(); self.threshold_correlation.insert( corr, @@ -401,6 +445,7 @@ impl ProofRequestActor { recipient_party_id: req.recipient_party_id, row_index: req.row_index, }, + 4 + sk_enc_count + j, ), ); if let Err(err) = self.bus.publish( @@ -409,7 +454,7 @@ impl ProofRequestActor { ) { error!("Failed to publish C3b proof request: {err}"); self.threshold_correlation - .retain(|_, (eid, _)| *eid != e3_id); + .retain(|_, (eid, _, _)| *eid != e3_id); self.pending_threshold.remove(&e3_id); return; } @@ -420,16 +465,36 @@ impl ProofRequestActor { let (msg, ec) = msg.into_components(); match &msg.response { ComputeResponseKind::Zk(ZkResponse::PkBfv(resp)) => { - self.handle_pk_bfv_response(&msg.correlation_id, resp.proof.clone(), &ec); + self.handle_pk_bfv_response( + &msg.correlation_id, + resp.proof.clone(), + resp.wrapped_proof.clone(), + &ec, + ); } ComputeResponseKind::Zk(ZkResponse::PkGeneration(resp)) => { - self.handle_threshold_proof_response(&msg.correlation_id, resp.proof.clone()); + self.handle_threshold_proof_response( + &msg.correlation_id, + resp.proof.clone(), + resp.wrapped_proof.clone(), + &ec, + ); } ComputeResponseKind::Zk(ZkResponse::ShareComputation(resp)) => { - self.handle_threshold_proof_response(&msg.correlation_id, resp.proof.clone()); + self.handle_threshold_proof_response( + &msg.correlation_id, + resp.proof.clone(), + resp.wrapped_proof.clone(), + &ec, + ); } ComputeResponseKind::Zk(ZkResponse::ShareEncryption(resp)) => { - self.handle_threshold_proof_response(&msg.correlation_id, resp.proof.clone()); + self.handle_threshold_proof_response( + &msg.correlation_id, + resp.proof.clone(), + resp.wrapped_proof.clone(), + &ec, + ); } ComputeResponseKind::Zk(ZkResponse::DkgShareDecryption(resp)) => { // Try C4 decryption proof first, then fall back to C1/C2/C3 threshold @@ -437,15 +502,26 @@ impl ProofRequestActor { .decryption_correlation .contains_key(&msg.correlation_id) { - self.handle_decryption_proof_response(&msg.correlation_id, resp.proof.clone()); + self.handle_decryption_proof_response( + &msg.correlation_id, + resp.proof.clone(), + resp.wrapped_proof.clone(), + &ec, + ); } else { - self.handle_threshold_proof_response(&msg.correlation_id, resp.proof.clone()); + self.handle_threshold_proof_response( + &msg.correlation_id, + resp.proof.clone(), + resp.wrapped_proof.clone(), + &ec, + ); } } ComputeResponseKind::Zk(ZkResponse::ThresholdShareDecryption(resp)) => { self.handle_share_decryption_proof_response( &msg.correlation_id, resp.proofs.clone(), + resp.wrapped_proofs.clone(), ); } ComputeResponseKind::Zk(ZkResponse::PkAggregation(resp)) => { @@ -491,8 +567,15 @@ impl ProofRequestActor { // C4a: SecretKey decryption proof let sk_corr = CorrelationId::new(); - self.decryption_correlation - .insert(sk_corr, (e3_id.clone(), DecryptionProofKind::SecretKey)); + let c4_base_seq = self + .node_agg_meta + .get(&e3_id) + .map(|m| m.total_expected.saturating_sub(2)) + .unwrap_or(0); + self.decryption_correlation.insert( + sk_corr, + (e3_id.clone(), DecryptionProofKind::SecretKey, c4_base_seq), + ); info!( "Requesting C4a DkgShareDecryption proof (SecretKey) for E3 {}", e3_id @@ -507,7 +590,7 @@ impl ProofRequestActor { ) { error!("Failed to publish C4a proof request: {err}"); self.decryption_correlation - .retain(|_, (eid, _)| *eid != e3_id); + .retain(|_, (eid, _, _)| *eid != e3_id); self.pending_decryption.remove(&e3_id); return; } @@ -520,6 +603,7 @@ impl ProofRequestActor { ( e3_id.clone(), DecryptionProofKind::SmudgingNoise { esi_idx }, + c4_base_seq + 1 + esi_idx, ), ); info!( @@ -536,7 +620,7 @@ impl ProofRequestActor { ) { error!("Failed to publish C4b proof request: {err}"); self.decryption_correlation - .retain(|_, (eid, _)| *eid != e3_id); + .retain(|_, (eid, _, _)| *eid != e3_id); self.pending_decryption.remove(&e3_id); return; } @@ -544,8 +628,14 @@ impl ProofRequestActor { } /// Handle a C4 proof response — store and check completeness. - fn handle_decryption_proof_response(&mut self, correlation_id: &CorrelationId, proof: Proof) { - let Some((e3_id, kind)) = self.decryption_correlation.remove(correlation_id) else { + fn handle_decryption_proof_response( + &mut self, + correlation_id: &CorrelationId, + proof: Proof, + wrapped_proof: Proof, + ec: &EventContext, + ) { + let Some((e3_id, kind, seq)) = self.decryption_correlation.remove(correlation_id) else { return; }; @@ -571,6 +661,23 @@ impl ProofRequestActor { } } + if let Some(meta) = self.node_agg_meta.get(&e3_id) { + if let Err(err) = self.bus.publish( + DKGInnerProofReady { + e3_id: e3_id.clone(), + party_id: meta.party_id, + wrapped_proof, + seq, + }, + ec.clone(), + ) { + error!( + "Failed to publish DKGInnerProofReady for C4 seq={}: {err}", + seq + ); + } + } + if pending.is_complete() { info!( "All C4 proofs complete for E3 {} — signing and publishing DecryptionKeyShared", @@ -688,11 +795,12 @@ impl ProofRequestActor { } } - /// Handle C6 proof response — sign proofs and publish DecryptionshareCreated. + /// Handle C6 proof response — sign raw proofs, keep wrapped for fold, publish DecryptionshareCreated. fn handle_share_decryption_proof_response( &mut self, correlation_id: &CorrelationId, proofs: Vec, + wrapped_proofs: Vec, ) { let Some(e3_id) = self.share_decryption_correlation.remove(correlation_id) else { return; @@ -706,7 +814,16 @@ impl ProofRequestActor { return; }; - // Sign each C6 proof + if proofs.len() != wrapped_proofs.len() { + error!( + "C6 proofs and wrapped_proofs length mismatch: {} vs {} — DecryptionshareCreated will not be published", + proofs.len(), + wrapped_proofs.len() + ); + return; + } + + // Sign raw C6 proofs (for ShareVerification) let mut signed_proofs = Vec::with_capacity(proofs.len()); for proof in proofs { let Some(signed) = self.sign_proof(&e3_id, ProofType::T5ShareDecryption, proof) else { @@ -732,6 +849,7 @@ impl ProofRequestActor { e3_id: e3_id.clone(), decryption_share: pending.decryption_share, signed_decryption_proofs: signed_proofs, + wrapped_proofs, }, ec.clone(), ) { @@ -911,8 +1029,14 @@ impl ProofRequestActor { } } - fn handle_threshold_proof_response(&mut self, correlation_id: &CorrelationId, proof: Proof) { - let Some((e3_id, kind)) = self.threshold_correlation.remove(correlation_id) else { + fn handle_threshold_proof_response( + &mut self, + correlation_id: &CorrelationId, + proof: Proof, + wrapped_proof: Proof, + ec: &EventContext, + ) { + let Some((e3_id, kind, seq)) = self.threshold_correlation.remove(correlation_id) else { return; }; @@ -933,6 +1057,23 @@ impl ProofRequestActor { pending.total_expected() ); + if let Some(meta) = self.node_agg_meta.get(&e3_id) { + if let Err(err) = self.bus.publish( + DKGInnerProofReady { + e3_id: e3_id.clone(), + party_id: meta.party_id, + wrapped_proof, + seq, + }, + ec.clone(), + ) { + error!( + "Failed to publish DKGInnerProofReady for {:?} seq={}: {err}", + kind, seq + ); + } + } + if pending.is_complete() { info!( "All {} threshold proofs complete for E3 {}", @@ -1158,6 +1299,7 @@ impl ProofRequestActor { &mut self, correlation_id: &CorrelationId, proof: Proof, + wrapped_proof: Proof, ec: &EventContext, ) { let Some(pending) = self.pending.remove(&correlation_id) else { @@ -1168,12 +1310,14 @@ impl ProofRequestActor { return; }; + let e3_id = pending.e3_id.clone(); + let mut key = (*pending.key).clone(); key.proof = Some(proof.clone()); // Always sign the proof payload — unsigned proofs are not published let payload = ProofPayload { - e3_id: pending.e3_id.clone(), + e3_id: e3_id.clone(), proof_type: ProofType::C0PkBfv, proof: proof.clone(), }; @@ -1195,7 +1339,7 @@ impl ProofRequestActor { if let Err(err) = self.bus.publish( EncryptionKeyCreated { - e3_id: pending.e3_id, + e3_id: e3_id.clone(), key: Arc::new(key), external: false, }, @@ -1203,6 +1347,31 @@ impl ProofRequestActor { ) { error!("Failed to publish EncryptionKeyCreated: {err}"); } + + // Emit DKGInnerProofReady for C0, or buffer if meta not yet available + if let Some(meta) = self.node_agg_meta.get(&e3_id) { + if let Err(err) = self.bus.publish( + DKGInnerProofReady { + e3_id: e3_id.clone(), + party_id: meta.party_id, + wrapped_proof, + seq: 0, + }, + ec.clone(), + ) { + error!("Failed to publish DKGInnerProofReady for C0: {err}"); + } + } else { + // ThresholdSharePending hasn't arrived yet — buffer C0 wrapped proof + self.node_agg_meta.insert( + e3_id.clone(), + NodeAggregationMeta { + party_id: 0, + total_expected: 0, + pending_c0: Some(wrapped_proof), + }, + ); + } } fn handle_compute_request_error(&mut self, msg: TypedEvent) { @@ -1219,24 +1388,25 @@ impl ProofRequestActor { return; } - if let Some((e3_id, kind)) = self.threshold_correlation.remove(msg.correlation_id()) { + if let Some((e3_id, kind, _seq)) = self.threshold_correlation.remove(msg.correlation_id()) { error!( "DKG {:?} proof request failed for E3 {}: {err} — threshold share will not be published without proof", kind, e3_id ); self.threshold_correlation - .retain(|_, (eid, _)| *eid != e3_id); + .retain(|_, (eid, _, _)| *eid != e3_id); self.pending_threshold.remove(&e3_id); return; } - if let Some((e3_id, kind)) = self.decryption_correlation.remove(msg.correlation_id()) { + if let Some((e3_id, kind, _seq)) = self.decryption_correlation.remove(msg.correlation_id()) + { error!( "C4 {:?} proof request failed for E3 {}: {err} — DecryptionKeyShared will not be published", kind, e3_id ); self.decryption_correlation - .retain(|_, (eid, _)| *eid != e3_id); + .retain(|_, (eid, _, _)| *eid != e3_id); self.pending_decryption.remove(&e3_id); return; } diff --git a/crates/zk-prover/src/circuits/recursive_aggregation/mod.rs b/crates/zk-prover/src/circuits/recursive_aggregation/mod.rs index 754750c789..bcb10a1dcb 100644 --- a/crates/zk-prover/src/circuits/recursive_aggregation/mod.rs +++ b/crates/zk-prover/src/circuits/recursive_aggregation/mod.rs @@ -54,7 +54,7 @@ impl serde::Serialize for WrapperInput { /// /// # Arguments /// * `prover` - ZK prover with bb and circuits configured -/// * `proofs` - 1 or 2 proofs to aggregate (must share the same circuit) +/// * `proof` - Single proof to wrap (each wrapper circuit accepts exactly 1 proof) /// * `e3_id` - Job identifier for work dir /// /// # Notes @@ -62,39 +62,12 @@ impl serde::Serialize for WrapperInput { /// Requires `{circuit}.vk` and `{circuit}.vk_hash` in the inner circuit dir (generated by build script). pub fn generate_wrapper_proof( prover: &ZkProver, - proofs: &[Proof], + proof: &Proof, e3_id: &str, ) -> Result { - let (proof_fields, public_inputs) = match proofs { - [p] => ( - vec![bytes_to_field_strings(&p.data)?], - vec![bytes_to_field_strings(&p.public_signals)?], - ), - [a, b] => { - if a.circuit != b.circuit { - return Err(ZkError::InvalidInput( - "all proofs must share the same circuit".into(), - )); - } - - ( - vec![ - bytes_to_field_strings(&a.data)?, - bytes_to_field_strings(&b.data)?, - ], - vec![ - bytes_to_field_strings(&a.public_signals)?, - bytes_to_field_strings(&b.public_signals)?, - ], - ) - } - _ => { - return Err(ZkError::InvalidInput( - "wrapper circuit requires 1 or 2 proofs".into(), - )) - } - }; - let circuit = proofs[0].circuit; + let proof_fields = vec![bytes_to_field_strings(&proof.data)?]; + let public_inputs = vec![bytes_to_field_strings(&proof.public_signals)?]; + let circuit = proof.circuit; let vk_artifacts = vk::load_vk_artifacts(&prover.circuits_dir(CircuitVariant::Recursive), circuit)?; @@ -162,11 +135,13 @@ impl serde::Serialize for FoldInput { /// Generates the fold proof by folding two proofs. /// VK path is chosen by circuit type: Fold uses dir_path, wrappers use wrapper_dir_path. +/// When `target_evm` is true, the proof is generated for on-chain EVM verification. pub fn generate_fold_proof( prover: &ZkProver, proof1: &Proof, proof2: &Proof, e3_id: &str, + target_evm: bool, ) -> Result { let vk1 = vk::load_vk_for_fold_input( &prover.circuits_dir(CircuitVariant::Default), @@ -205,9 +180,14 @@ pub fn generate_fold_proof( proof2_key_hash: vk2.key_hash, }; + let variant = if target_evm { + CircuitVariant::Evm + } else { + CircuitVariant::Default + }; let dir_path = CircuitName::Fold.dir_path(); let circuit_path = prover - .circuits_dir(CircuitVariant::Default) + .circuits_dir(variant) .join(&dir_path) .join(format!("{}.json", CircuitName::Fold.as_str())); let compiled = CompiledCircuit::from_file(&circuit_path)?; @@ -218,12 +198,16 @@ pub fn generate_fold_proof( let witness_gen = WitnessGenerator::new(); let witness = witness_gen.generate_witness(&compiled, input_map)?; - prover.generate_fold_proof(&witness, e3_id) + if target_evm { + prover.generate_final_fold_proof(&witness, e3_id) + } else { + prover.generate_fold_proof(&witness, e3_id) + } } #[cfg(all(test, feature = "integration-tests"))] mod tests { - use super::*; + use super::{generate_fold_proof, generate_wrapper_proof}; use crate::prover::ZkProver; use crate::test_utils::get_tempdir; use crate::traits::Provable; @@ -320,9 +304,8 @@ mod tests { .expect("inner prove() should succeed"); let start = std::time::Instant::now(); - let wrapper_proof = PkCircuit - .aggregate_proof(&prover, &[inner_proof], None, e3_id) - .expect("aggregate_proof (1 proof) should succeed"); + let wrapper_proof = + generate_wrapper_proof(&prover, &inner_proof, e3_id).expect("wrapper (1 proof)"); let elapsed = start.elapsed(); eprintln!("1-proof wrapper generation: {:?}", elapsed); @@ -399,19 +382,22 @@ mod tests { .expect("inner prove() B should succeed"); let start = std::time::Instant::now(); - let wrapper_proof = ShareDecryptionCircuit - .aggregate_proof(&prover, &[inner_proof_a, inner_proof_b], None, e3_id) - .expect("aggregate_proof (2 proofs) should succeed"); + let wrapper_a = + generate_wrapper_proof(&prover, &inner_proof_a, e3_id).expect("wrapper (proof A)"); + let wrapper_b = + generate_wrapper_proof(&prover, &inner_proof_b, e3_id).expect("wrapper (proof B)"); + let fold_proof = generate_fold_proof(&prover, &wrapper_a, &wrapper_b, e3_id, false) + .expect("fold 2 wrappers"); let elapsed = start.elapsed(); - eprintln!("2-proof wrapper generation: {:?}", elapsed); + eprintln!("2-proof (wrapper each + fold) generation: {:?}", elapsed); - assert!(!wrapper_proof.data.is_empty()); - assert!(!wrapper_proof.public_signals.is_empty()); + assert!(!fold_proof.data.is_empty()); + assert!(!fold_proof.public_signals.is_empty()); let verified = prover - .verify_wrapper_proof(&wrapper_proof, e3_id, 0) + .verify_fold_proof(&fold_proof, e3_id, 0) .expect("verification should not error"); - assert!(verified, "2-proof wrapper should verify successfully"); + assert!(verified, "fold of 2 wrappers should verify successfully"); prover .cleanup("aggregation-2proof-wrapper_inner_0") @@ -506,9 +492,8 @@ mod tests { .prove(&prover, &preset, &pk_sample, &format!("{e3_id}_pk_inner_0")) .expect("pk inner prove() should succeed"); - let pk_wrapper_proof = PkCircuit - .aggregate_proof(&prover, &[pk_inner_proof], None, e3_id) - .expect("pk aggregate_proof (1 proof) should succeed"); + let pk_wrapper_proof = + generate_wrapper_proof(&prover, &pk_inner_proof, e3_id).expect("pk wrapper"); let enc_inner_secret = ShareEncryptionCircuit .prove( @@ -527,14 +512,26 @@ mod tests { ) .expect("share_encryption inner prove() (noise) should succeed"); - let fold_proof = ShareEncryptionCircuit - .aggregate_proof( - &prover, - &[enc_inner_secret, enc_inner_noise], - Some(&pk_wrapper_proof), - e3_id, - ) - .expect("share_encryption aggregate_proof with fold should succeed"); + let share_enc_wrapper_1 = generate_wrapper_proof(&prover, &enc_inner_secret, e3_id) + .expect("share_encryption wrapper (secret)"); + let share_enc_wrapper_2 = generate_wrapper_proof(&prover, &enc_inner_noise, e3_id) + .expect("share_encryption wrapper (noise)"); + let share_enc_fold_proof = generate_fold_proof( + &prover, + &share_enc_wrapper_1, + &share_enc_wrapper_2, + e3_id, + false, + ) + .expect("fold share_enc wrappers"); + let fold_proof = generate_fold_proof( + &prover, + &share_enc_fold_proof, + &pk_wrapper_proof, + e3_id, + false, + ) + .expect("fold"); assert!(!fold_proof.data.is_empty()); assert!(!fold_proof.public_signals.is_empty()); diff --git a/crates/zk-prover/src/traits.rs b/crates/zk-prover/src/traits.rs index 0152b87479..fbf7fcd8a2 100644 --- a/crates/zk-prover/src/traits.rs +++ b/crates/zk-prover/src/traits.rs @@ -6,7 +6,6 @@ use std::fmt::Display; -use crate::circuits::recursive_aggregation::{generate_fold_proof, generate_wrapper_proof}; use crate::circuits::utils::inputs_json_to_input_map; use crate::error::ZkError; use crate::prover::ZkProver; @@ -95,34 +94,6 @@ pub trait Provable: Send + Sync { prover.generate_proof_with_variant(resolved_name, &witness, e3_id, variant) } - /// Wraps 1–2 proofs (from `prove()`) and optionally folds with `aggregated_proof`. - fn aggregate_proof( - &self, - prover: &ZkProver, - proofs: &[Proof], - aggregated_proof: Option<&Proof>, - e3_id: &str, - ) -> Result { - if !matches!(proofs.len(), 1 | 2) { - return Err(ZkError::InvalidInput( - "aggregate_proof requires 1 or 2 proofs".into(), - )); - } - - if proofs.len() == 2 && proofs[0].circuit != proofs[1].circuit { - return Err(ZkError::InvalidInput( - "aggregate_proof requires all proofs to use the same circuit".into(), - )); - } - - let wrapper_proof = generate_wrapper_proof(prover, proofs, e3_id)?; - - match aggregated_proof { - Some(ap) => generate_fold_proof(prover, &wrapper_proof, ap, e3_id), - None => Ok(wrapper_proof), - } - } - fn verify( &self, prover: &ZkProver, diff --git a/crates/zk-prover/tests/onchain_verification_tests.rs b/crates/zk-prover/tests/onchain_verification_tests.rs deleted file mode 100644 index a79c6f53a5..0000000000 --- a/crates/zk-prover/tests/onchain_verification_tests.rs +++ /dev/null @@ -1,213 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -//! On-chain ZK proof verification tests. -//! Requires: `bb`, `anvil`, and compiled contract artifacts -//! (`npx hardhat compile` in packages/enclave-contracts). - -mod common; - -use alloy::{ - network::TransactionBuilder, - primitives::{Bytes, FixedBytes}, - providers::{Provider, ProviderBuilder}, - rpc::types::TransactionRequest, - sol, -}; -use common::{find_anvil, find_bb, setup_compiled_circuit, setup_test_prover}; -use e3_fhe_params::BfvPreset; -use e3_zk_helpers::circuits::dkg::pk::circuit::{PkCircuit, PkCircuitData}; -use e3_zk_prover::{CircuitVariant, Provable, ZkProver}; -use std::path::PathBuf; - -sol! { - #[sol(rpc)] - contract DkgPkVerifier { - function verify(bytes calldata proof, bytes32[] calldata publicInputs) external view returns (bool verified); - } -} - -/// Linker placeholder that gets replaced with the deployed ZKTranscriptLib address. -const ZK_TRANSCRIPT_LIB_PLACEHOLDER: &str = "__$3f925933ac313a1c84f3f4c25b9ea43c90$__"; - -fn artifacts_dir() -> PathBuf { - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("../../packages/enclave-contracts/artifacts/contracts/verifier/DkgPkVerifier.sol") -} - -fn read_artifact_bytecode_hex(artifact_name: &str) -> Option { - let path = artifacts_dir().join(artifact_name); - let json_str = std::fs::read_to_string(&path).ok()?; - let json: serde_json::Value = serde_json::from_str(&json_str).ok()?; - json["bytecode"].as_str().map(|s| s.to_string()) -} - -fn decode_bytecode(hex_str: &str) -> Vec { - let clean = hex_str.strip_prefix("0x").unwrap_or(hex_str); - hex::decode(clean).expect("failed to decode bytecode hex") -} - -fn link_transcript_lib(bytecode_hex: &str, lib_address: &alloy::primitives::Address) -> Vec { - let addr_hex = hex::encode(lib_address.as_slice()); - let linked = bytecode_hex.replace(ZK_TRANSCRIPT_LIB_PLACEHOLDER, &addr_hex); - decode_bytecode(&linked) -} - -#[tokio::test] -async fn test_pk_bfv_onchain_verification() { - // Generate ZK proof - - let bb = match find_bb().await { - Some(bb) => bb, - None => { - println!("skipping: bb not found"); - return; - } - }; - - let preset = BfvPreset::InsecureThreshold512; - let (backend, _temp) = setup_test_prover(&bb).await; - setup_compiled_circuit(&backend, "dkg", "pk").await; - - let sample = match PkCircuitData::generate_sample(preset) { - Ok(s) => s, - Err(e) => { - println!("skipping: failed to generate sample: {e}"); - return; - } - }; - - let prover = ZkProver::new(&backend); - let e3_id = "0"; - - // Generate proof with EVM variant (keccak) for on-chain Solidity verification - let proof = PkCircuit - .prove_with_variant(&prover, &preset, &sample, e3_id, CircuitVariant::Evm) - .expect("proof generation should succeed"); - - assert!(!proof.data.is_empty(), "proof data should not be empty"); - assert!( - !proof.public_signals.is_empty(), - "public signals should not be empty" - ); - - let local_ok = PkCircuit.verify_with_variant(&prover, &proof, e3_id, 1, CircuitVariant::Evm); - assert!( - local_ok.as_ref().is_ok_and(|&v| v), - "local proof verification failed: {local_ok:?}" - ); - - println!( - "proof: {} bytes, public_inputs: {} bytes", - proof.data.len(), - proof.public_signals.len() - ); - - // Deploy verifier contract to Anvil - - let lib_bytecode_hex = match read_artifact_bytecode_hex("ZKTranscriptLib.json") { - Some(h) => h, - None => { - println!( - "skipping: ZKTranscriptLib artifact not found \ - (run `npx hardhat compile` in packages/enclave-contracts)" - ); - return; - } - }; - let verifier_bytecode_hex = match read_artifact_bytecode_hex("DkgPkVerifier.json") { - Some(h) => h, - None => { - println!( - "skipping: DkgPkVerifier artifact not found \ - (run `npx hardhat compile` in packages/enclave-contracts)" - ); - return; - } - }; - - if !find_anvil().await { - println!("skipping: anvil not found on PATH"); - return; - } - - let provider = ProviderBuilder::new().connect_anvil_with_wallet(); - - let lib_bytecode = decode_bytecode(&lib_bytecode_hex); - let lib_deploy_tx = TransactionRequest::default().with_deploy_code(Bytes::from(lib_bytecode)); - let lib_receipt = provider - .send_transaction(lib_deploy_tx) - .await - .expect("failed to send ZKTranscriptLib deploy tx") - .get_receipt() - .await - .expect("failed to get ZKTranscriptLib deploy receipt"); - let lib_address = lib_receipt - .contract_address - .expect("ZKTranscriptLib deploy receipt missing contract address"); - println!("ZKTranscriptLib deployed at: {lib_address}"); - - let linked_bytecode = link_transcript_lib(&verifier_bytecode_hex, &lib_address); - let verifier_deploy_tx = - TransactionRequest::default().with_deploy_code(Bytes::from(linked_bytecode)); - let verifier_receipt = provider - .send_transaction(verifier_deploy_tx) - .await - .expect("failed to send DkgPkVerifier deploy tx") - .get_receipt() - .await - .expect("failed to get DkgPkVerifier deploy receipt"); - let verifier_address = verifier_receipt - .contract_address - .expect("DkgPkVerifier deploy receipt missing contract address"); - println!("DkgPkVerifier deployed at: {verifier_address}"); - - let verifier = DkgPkVerifier::new(verifier_address, &provider); - - // Verify proof on-chain - - let proof_bytes = Bytes::copy_from_slice(&proof.data); - - // pk_bfv has 17 public inputs, 16 are pairing points baked into the proof, - // so only 1 (the pk commitment) gets passed as publicInputs to the contract. - let public_inputs: Vec> = proof - .public_signals - .chunks(32) - .map(|chunk| { - let mut buf = [0u8; 32]; - buf[..chunk.len()].copy_from_slice(chunk); - FixedBytes::from(buf) - }) - .collect(); - - assert_eq!( - public_inputs.len(), - 1, - "pk_bfv circuit should produce exactly 1 public input (commitment), got {}", - public_inputs.len() - ); - - println!( - "calling on-chain verify with {} proof bytes, {} public input(s)", - proof_bytes.len(), - public_inputs.len() - ); - - let verified = verifier - .verify(proof_bytes, public_inputs) - .call() - .await - .expect("on-chain verification call reverted — the proof should be valid"); - - assert!( - verified, - "on-chain ZK proof verification should return true" - ); - - println!("on-chain verification passed"); - - prover.cleanup(e3_id).unwrap(); -} diff --git a/docs/pages/ciphernode-operators/index.mdx b/docs/pages/ciphernode-operators/index.mdx index 271d01186a..6907157c60 100644 --- a/docs/pages/ciphernode-operators/index.mdx +++ b/docs/pages/ciphernode-operators/index.mdx @@ -49,7 +49,15 @@ The Interfold protocol uses several contracts that work together: | EnclaveTicketToken | `0x7D3D17F0A60cF227aaC78cB0cE6B25E00768dbe8` | 10395613 | | EnclaveToken (ENCL) | `0x24b28471AE7BdF1fdBcfDd183c73D13ff0689B99` | 10395612 | | MockUSDC (fee token) | `0x01AbD7D8e6547c943c2fE082C3Ce194fDCe57396` | 10395611 | -| DkgPkVerifier | `0x6997eCF2926a94c439599f7275219Cb2BfBA300C` | 10395629 | + +Circuit verifiers (C5, C7 BN/Mod, Fold) are deployed with the main flow. See `deployed_contracts.json` for addresses. + +| Verifier | Address | +| -------------------------------------------------------- | -------------------------------------------- | +| ThresholdPkAggregationVerifier (C5) | See `deployed_contracts.json` after deploy | +| ThresholdDecryptedSharesAggregationBnVerifier (C7 BN) | See `deployed_contracts.json` after deploy | +| ThresholdDecryptedSharesAggregationModVerifier (C7 Mod) | See `deployed_contracts.json` after deploy | +| RecursiveAggregationFoldVerifier (Fold) | See `deployed_contracts.json` after deploy | > Always verify addresses from `packages/enclave-contracts/deployed_contracts.json` or your > deployment output. Addresses differ per network. diff --git a/examples/CRISP/circuits/lib/src/constants.nr b/examples/CRISP/circuits/lib/src/constants.nr index 609e6243dd..93f8d74e71 100644 --- a/examples/CRISP/circuits/lib/src/constants.nr +++ b/examples/CRISP/circuits/lib/src/constants.nr @@ -11,4 +11,4 @@ pub global MAX_OPTIONS: u32 = 10; // This constant defines the size of the coefficient region allocated for encoding vote amounts /// in binary format within each half of the polynomial. Votes are encoded as binary numbers. -pub global MAX_VOTE_BITS: u32 = 50; +pub global MAX_VOTE_BITS: u32 = 50; diff --git a/examples/CRISP/crates/evm_helpers/src/lib.rs b/examples/CRISP/crates/evm_helpers/src/lib.rs index c8b2ef7a77..366570c3b2 100644 --- a/examples/CRISP/crates/evm_helpers/src/lib.rs +++ b/examples/CRISP/crates/evm_helpers/src/lib.rs @@ -8,11 +8,15 @@ use alloy::{ network::{Ethereum, EthereumWallet}, primitives::{Address, Bytes, I256, U256}, providers::{ - Identity, ProviderBuilder, RootProvider, fillers::{ + fillers::{ BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, WalletFiller, - } - }, rpc::types::TransactionReceipt, signers::local::PrivateKeySigner, sol + }, + Identity, ProviderBuilder, RootProvider, + }, + rpc::types::TransactionReceipt, + signers::local::PrivateKeySigner, + sol, }; use eyre::Result; use std::sync::Arc; @@ -100,11 +104,7 @@ impl CRISPContract { } // publish an input to the CRISPProgram contract - pub async fn publish_input( - &self, - e3_id: U256, - data: Bytes, - ) -> Result { + pub async fn publish_input(&self, e3_id: U256, data: Bytes) -> Result { let contract = CRISPProgram::new(self.contract_address, self.provider.as_ref()); let receipt = contract .publishInput(e3_id, data.into()) diff --git a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json index a60954fb1d..4a6c4eda23 100644 --- a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json +++ b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json @@ -126,10 +126,6 @@ "blockNumber": 10395628, "address": "0xBf0A32A7D546944561a8773628e6c0036C16354C" }, - "DkgPkVerifier": { - "blockNumber": 10395629, - "address": "0x6997eCF2926a94c439599f7275219Cb2BfBA300C" - }, "MockRISC0Verifier": { "address": "0x1DdB8dc5d48CE8598bcb92759A33ab4350e85Ef5", "blockNumber": 10395677 diff --git a/examples/CRISP/scripts/test_e2e.sh b/examples/CRISP/scripts/test_e2e.sh index 45f783683a..bcafeaca6e 100755 --- a/examples/CRISP/scripts/test_e2e.sh +++ b/examples/CRISP/scripts/test_e2e.sh @@ -14,5 +14,6 @@ else fi echo "TEST E2E SCRIPT STARTING..." -pnpm concurrently -krs first ./scripts/dev.sh "wait-on tcp:3000 && sleep 20 && ${PLAYWRIGHT_CMD} && sleep 3" +# Wait for client then give ciphernodes extra time to fully initialize DKG listeners +pnpm concurrently -krs first ./scripts/dev.sh "wait-on tcp:3000 && sleep 45 && ${PLAYWRIGHT_CMD} && sleep 3" \ No newline at end of file diff --git a/examples/CRISP/server/.env.example b/examples/CRISP/server/.env.example index fdf0065727..850466139e 100644 --- a/examples/CRISP/server/.env.example +++ b/examples/CRISP/server/.env.example @@ -23,7 +23,7 @@ FEE_TOKEN_ADDRESS="0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" # After this interval, the computation phase starts automatically # After activation + this interval, ciphernodes are then not responsing to # any more decryption requests -E3_DURATION=225 +E3_DURATION=900 # 0=Micro, 1=Small, 2=Medium, 3=Large E3_COMMITTEE_SIZE=0 diff --git a/examples/CRISP/server/src/cli/commands.rs b/examples/CRISP/server/src/cli/commands.rs index af341dd7dd..9aaf62da71 100644 --- a/examples/CRISP/server/src/cli/commands.rs +++ b/examples/CRISP/server/src/cli/commands.rs @@ -19,7 +19,9 @@ use alloy::providers::{Provider, ProviderBuilder}; use alloy::sol_types::SolValue; use crisp::config::CONFIG; use e3_fhe_params::{build_bfv_params_from_set_arc, encode_bfv_params}; -use e3_sdk::evm_helpers::contracts::{CommitteeSize, EnclaveContract, EnclaveRead, EnclaveWrite, E3}; +use e3_sdk::evm_helpers::contracts::{ + CommitteeSize, EnclaveContract, EnclaveRead, EnclaveWrite, E3, +}; use fhe::bfv::{BfvParameters, Ciphertext, Encoding, Plaintext, PublicKey, SecretKey}; use fhe_traits::{ DeserializeParametrized, FheDecoder, FheDecrypter, FheEncoder, FheEncrypter, @@ -196,7 +198,7 @@ pub async fn initialize_crisp_round( CONFIG.ciphernode_registry_address ); - // Recompute the current timestamp to ensure it's as up-to-date as possible before sending the transaction, + // Recompute the current timestamp to ensure it's as up-to-date as possible before sending the transaction, // since there are multiple steps (fee quote, token approval) that could take time. let mut current_timestamp = get_current_timestamp().await?; // Buffer so tx can mine before window opens; end = start + duration so voting window equals e3_duration diff --git a/examples/CRISP/server/src/cli/main.rs b/examples/CRISP/server/src/cli/main.rs index 01ce0958b1..4c6ba179b8 100644 --- a/examples/CRISP/server/src/cli/main.rs +++ b/examples/CRISP/server/src/cli/main.rs @@ -54,7 +54,7 @@ enum Commands { CheckE3Ready { #[arg(short, long)] e3id: u64, - } + }, } #[tokio::main] diff --git a/examples/CRISP/server/src/server/indexer.rs b/examples/CRISP/server/src/server/indexer.rs index 1d3af72e23..cd4e713f72 100644 --- a/examples/CRISP/server/src/server/indexer.rs +++ b/examples/CRISP/server/src/server/indexer.rs @@ -20,8 +20,7 @@ use e3_sdk::{ evm_helpers::{ contracts::{EnclaveRead, ReadWrite}, events::{ - CiphertextOutputPublished, CommitteePublished, E3Requested, - PlaintextOutputPublished, + CiphertextOutputPublished, CommitteePublished, E3Requested, PlaintextOutputPublished, }, retry::call_with_retry, }, @@ -320,7 +319,7 @@ pub async fn register_plaintext_output_published( // The plaintextOutput from the event contains the result of the FHE computation. // Decode the tally using the utility function. let vote_counts = decode_tally(&event.plaintextOutput, num_options)?; - + for (i, count) in vote_counts.iter().enumerate() { info!("[e3_id={}] Option index: {} votes: {:?}", e3_id, i, count); } diff --git a/examples/CRISP/server/src/server/repo.rs b/examples/CRISP/server/src/server/repo.rs index df1d031a66..2a89bcd578 100644 --- a/examples/CRISP/server/src/server/repo.rs +++ b/examples/CRISP/server/src/server/repo.rs @@ -228,7 +228,9 @@ impl CrispE3Repository { pub async fn set_votes(&mut self, votes: Vec) -> Result<()> { info!( "set_votes: [{}]", - votes.iter().enumerate() + votes + .iter() + .enumerate() .map(|(i, v)| format!("option_{}: {}", i, v)) .collect::>() .join(", ") @@ -236,14 +238,14 @@ impl CrispE3Repository { let key = self.crisp_key(); self.store - .modify(&key, |e3_obj: Option| { - e3_obj.map(|mut e| { - e.tally = votes.iter().map(|v| v.to_string()).collect(); - e + .modify(&key, |e3_obj: Option| { + e3_obj.map(|mut e| { + e.tally = votes.iter().map(|v| v.to_string()).collect(); + e + }) }) - }) - .await - .map_err(|_| eyre::eyre!("Could not set votes for '{key}'"))?; + .await + .map_err(|_| eyre::eyre!("Could not set votes for '{key}'"))?; Ok(()) } diff --git a/examples/CRISP/server/src/server/token_holders/etherscan.rs b/examples/CRISP/server/src/server/token_holders/etherscan.rs index 01dc516705..a4c6bdf7bb 100644 --- a/examples/CRISP/server/src/server/token_holders/etherscan.rs +++ b/examples/CRISP/server/src/server/token_holders/etherscan.rs @@ -4,7 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use crate::server::{models::TokenHolder}; +use crate::server::models::TokenHolder; use alloy::primitives::{Address, U256}; use alloy::providers::ProviderBuilder; use alloy::sol; @@ -327,7 +327,7 @@ impl EtherscanClient { let scale_factor = U256::from(10u128.pow(half_decimals as u32)); - for voter in potential_voters { + for voter in potential_voters { match Self::get_past_votes(token_address, voter.address, block_number, rpc_url).await { Ok(votes) => { if votes >= threshold { diff --git a/examples/CRISP/test/crisp.spec.ts b/examples/CRISP/test/crisp.spec.ts index 31fb8a19c5..9a0ac312c5 100644 --- a/examples/CRISP/test/crisp.spec.ts +++ b/examples/CRISP/test/crisp.spec.ts @@ -14,6 +14,9 @@ import path from 'path' config({ path: path.join(process.cwd(), 'server', '.env') }) +const E3_DURATION = parseInt(process.env.E3_DURATION as string, 10) * 1000 +const OUTPUT_DECRYPTION_WAIT = 80_000 // A small buffer for decryption + async function runCliInit(): Promise { try { // Execute the command and wait for it to complete @@ -43,17 +46,17 @@ async function checkE3Ready(e3id: number): Promise { const lastLine = lines[lines.length - 1].trim() return lastLine === 'true' } catch (error) { - console.error('Error checking e3 activation:', error) + log(`check-e3-ready failed for e3id=${e3id}: ${error}`) return false } } -async function waitForE3Ready(e3id: number, maxWaitMs: number = 300000): Promise { +async function waitForE3Ready(e3id: number, maxWaitMs: number = E3_DURATION): Promise { const startTime = Date.now() while (Date.now() - startTime < maxWaitMs) { const isActivated = await checkE3Ready(e3id) if (isActivated) { - console.log(`E3 ${e3id} is ready`) + log(`E3 ${e3id} is ready`) return } await new Promise((resolve) => setTimeout(resolve, 5000)) @@ -110,6 +113,7 @@ test('CRISP smoke test', async ({ context, page, metamaskPage, extensionId }) => log('============================================') log(' STARTING YOUR PLAYWRIGHT TEST! ') log('============================================') + const testStart = Date.now() log('Creating new Metamask...') const metamask = new MetaMask(context, metamaskPage, basicSetup.walletPassword, extensionId) @@ -133,6 +137,8 @@ test('CRISP smoke test', async ({ context, page, metamaskPage, extensionId }) => log(`waiting for E3 Committee being published...`) await waitForE3Ready(e3id) + const DKG_DURATION = Date.now() - testStart + log(`DKG duration: ${DKG_DURATION}ms`) log(`forcing page reload...`) await page.reload() @@ -142,7 +148,7 @@ test('CRISP smoke test', async ({ context, page, metamaskPage, extensionId }) => await page.locator('button:has-text("Cast Vote")').click() log(`confirming MetaMask signature request...`) await metamask.confirmSignature() - const WAIT = parseInt(process.env.E3_DURATION as string, 10) * 1000 + 25_000 // A small buffer for decryption + const WAIT = E3_DURATION - DKG_DURATION + OUTPUT_DECRYPTION_WAIT log(`waiting ${WAIT}ms...`) await page.waitForTimeout(WAIT) log(`clicking historic polls button...`) diff --git a/package.json b/package.json index ea36a7554a..3851ab9776 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "scripts": { "bump:versions": "tsx scripts/bump-versions.ts", "build:circuits": "tsx scripts/build-circuits.ts", - "generate:verifiers": "tsx scripts/generate-verifiers.ts", + "generate:verifiers": "tsx scripts/generate-verifiers.ts --circuits pk_aggregation,decrypted_shares_aggregation_bn,decrypted_shares_aggregation_mod,fold", "store:circuits": "tsx scripts/circuit-artifacts.ts", "clean": "tsx scripts/clean.ts", "compile": "pnpm build:ts && pnpm rust:build", diff --git a/packages/enclave-contracts/.gitignore b/packages/enclave-contracts/.gitignore index 5d288546ee..5e78485eb5 100644 --- a/packages/enclave-contracts/.gitignore +++ b/packages/enclave-contracts/.gitignore @@ -34,16 +34,18 @@ # Verifier contracts !/artifacts/contracts/verifier/ -!/artifacts/contracts/verifier/DkgPkVerifier.sol/ -!/artifacts/contracts/verifier/DkgPkVerifier.sol/DkgPkVerifier.json -!/artifacts/contracts/verifier/DkgPkVerifier.sol/ZKTranscriptLib.json - - -# Verifier contracts -!/artifacts/contracts/verifier/ -!/artifacts/contracts/verifier/DkgPkVerifier.sol/ -!/artifacts/contracts/verifier/DkgPkVerifier.sol/DkgPkVerifier.json -!/artifacts/contracts/verifier/DkgPkVerifier.sol/ZKTranscriptLib.json +!/artifacts/contracts/verifier/ThresholdPkAggregationVerifier.sol/ +!/artifacts/contracts/verifier/ThresholdPkAggregationVerifier.sol/ThresholdPkAggregationVerifier.json +!/artifacts/contracts/verifier/ThresholdPkAggregationVerifier.sol/ZKTranscriptLib.json +!/artifacts/contracts/verifier/ThresholdDecryptedSharesAggregationBnVerifier.sol/ +!/artifacts/contracts/verifier/ThresholdDecryptedSharesAggregationBnVerifier.sol/ThresholdDecryptedSharesAggregationBnVerifier.json +!/artifacts/contracts/verifier/ThresholdDecryptedSharesAggregationBnVerifier.sol/ZKTranscriptLib.json +!/artifacts/contracts/verifier/ThresholdDecryptedSharesAggregationModVerifier.sol/ +!/artifacts/contracts/verifier/ThresholdDecryptedSharesAggregationModVerifier.sol/ThresholdDecryptedSharesAggregationModVerifier.json +!/artifacts/contracts/verifier/ThresholdDecryptedSharesAggregationModVerifier.sol/ZKTranscriptLib.json +!/artifacts/contracts/verifier/RecursiveAggregationFoldVerifier.sol/ +!/artifacts/contracts/verifier/RecursiveAggregationFoldVerifier.sol/RecursiveAggregationFoldVerifier.json +!/artifacts/contracts/verifier/RecursiveAggregationFoldVerifier.sol/ZKTranscriptLib.json build cache diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json index f5615b6386..d41d7a2081 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json @@ -940,5 +940,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IBondingRegistry.sol", - "buildInfoId": "solc-0_8_28-d48586b5c515ad1b112178eeef9419e7799821ba" + "buildInfoId": "solc-0_8_28-bd46eae6f77be9a8538850385226c1ee4fc57e42" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json index f9388e7df0..749277e738 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json @@ -782,5 +782,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ICiphernodeRegistry.sol", - "buildInfoId": "solc-0_8_28-d48586b5c515ad1b112178eeef9419e7799821ba" + "buildInfoId": "solc-0_8_28-bd46eae6f77be9a8538850385226c1ee4fc57e42" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json index 6d07ea3d4e..497378e36e 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json @@ -1313,5 +1313,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IEnclave.sol", - "buildInfoId": "solc-0_8_28-d48586b5c515ad1b112178eeef9419e7799821ba" + "buildInfoId": "solc-0_8_28-bd46eae6f77be9a8538850385226c1ee4fc57e42" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json index 17073c1290..513041be4c 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json @@ -954,5 +954,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ISlashingManager.sol", - "buildInfoId": "solc-0_8_28-d48586b5c515ad1b112178eeef9419e7799821ba" + "buildInfoId": "solc-0_8_28-bd46eae6f77be9a8538850385226c1ee4fc57e42" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json b/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json index 09f0432af8..de3393366d 100644 --- a/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json +++ b/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json @@ -1223,5 +1223,5 @@ ] }, "inputSourceName": "project/contracts/token/EnclaveTicketToken.sol", - "buildInfoId": "solc-0_8_28-5311703a5d5fd8087d15f5c6957555cc4c2bf8c5" + "buildInfoId": "solc-0_8_28-bd46eae6f77be9a8538850385226c1ee4fc57e42" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/verifier/DkgPkVerifier.sol/DkgPkVerifier.json b/packages/enclave-contracts/artifacts/contracts/verifier/DkgPkVerifier.sol/DkgPkVerifier.json deleted file mode 100644 index d1bc14ae2e..0000000000 --- a/packages/enclave-contracts/artifacts/contracts/verifier/DkgPkVerifier.sol/DkgPkVerifier.json +++ /dev/null @@ -1,188 +0,0 @@ -{ - "_format": "hh3-artifact-1", - "contractName": "DkgPkVerifier", - "sourceName": "contracts/verifier/DkgPkVerifier.sol", - "abi": [ - { - "inputs": [], - "name": "ConsistencyCheckFailed", - "type": "error" - }, - { - "inputs": [], - "name": "GeminiChallengeInSubgroup", - "type": "error" - }, - { - "inputs": [], - "name": "ProofLengthWrong", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "logN", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "actualLength", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "expectedLength", - "type": "uint256" - } - ], - "name": "ProofLengthWrongWithLogN", - "type": "error" - }, - { - "inputs": [], - "name": "PublicInputsLengthWrong", - "type": "error" - }, - { - "inputs": [], - "name": "ShpleminiFailed", - "type": "error" - }, - { - "inputs": [], - "name": "SumcheckFailed", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "proof", - "type": "bytes" - }, - { - "internalType": "bytes32[]", - "name": "publicInputs", - "type": "bytes32[]" - } - ], - "name": "verify", - "outputs": [ - { - "internalType": "bool", - "name": "verified", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - } - ], - "bytecode": "0x610120604052348015610010575f5ffd5b506120006080819052600d60a08190527f2b9b4382ad1c1e2e1d085d25a74c59315a809667765be70770ddc185b70f780b60c0819052601160e081905260038361005c60016024610089565b6100669190610089565b6100709190610089565b61007b906002610089565b61010052506100ae92505050565b808201808211156100a857634e487b7160e01b5f52601160045260245ffd5b92915050565b60805160a05160c05160e05161010051615cb86101485f395f8181610c5401528181610cb90152612ec001525f81816101b001526108ff01525f61018e01525f8181605b01528181609401528181610101015281816101d201528181610a2d01528181610b8e01528181610c2b01528181611523015281816115dd015281816116100152818161181a0152612b9d01525f5050615cb85ff3fe608060405234801561000f575f5ffd5b5060043610610029575f3560e01c8063ea50d0e41461002d575b5f5ffd5b61004061003b3660046154ef565b610054565b604051901515815260200160405180910390f35b5f5f61007f7f00000000000000000000000000000000000000000000000000000000000000006102ee565b905061008c8160206155c7565b85146100ee577f0000000000000000000000000000000000000000000000000000000000000000856100bf8360206155c7565b6040516359895a5360e01b81526004810193909352602483019190915260448201526064015b60405180910390fd5b5f6100f76103fb565b90505f61012588887f0000000000000000000000000000000000000000000000000000000000000000610410565b90506010826040015161013891906155de565b85146101575760405163fa06659360e01b815260040160405180910390fd5b60405163995bf45760e01b81525f9073__$3f925933ac313a1c84f3f4c25b9ea43c90$__9063995bf457906101fa9085908b908b907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090600401615761565b610be060405180830381865af4158015610216573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061023a9190615a8d565b905061028a8787808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050855185516060810151608090910151919350915060016108a9565b815160a0015261029a8282610a10565b6102b7576040516313f8744360e31b815260040160405180910390fd5b6102c2828483610c17565b6102df576040516352ec174560e11b815260040160405180910390fd5b50600198975050505050505050565b5f8060026102fe60016008615b37565b61030891906155c7565b9050610316600260036155c7565b6103209082615b37565b9050600161032f6009856155c7565b61033991906155c7565b6103439082615b37565b90506001610352816029615b37565b61035c91906155c7565b6103669082615b37565b9050610374600160026155c7565b61037e9082615b37565b905061038b6001846155c7565b6103959082615b37565b90506103a3600160046155c7565b6103ad9082615b37565b905060026103bc6001856155de565b6103c691906155c7565b6103d09082615b37565b90506103dd6002806155c7565b6103e79082615b37565b90506103f4601082615b37565b9392505050565b610403615022565b61040b611c14565b905090565b6104186151ac565b5f805b601081101561047a57610447868387610435602083615b37565b9261044293929190615b4a565b61254c565b8351826010811061045a5761045a615b71565b6020020181815250506020826104709190615b37565b915060010161041b565b5061049e85828661048c604083615b37565b9261049993929190615b4a565b61255f565b60208301526104ae604082615b37565b90506104c185828661048c604083615b37565b6040808401919091526104d49082615b37565b90506104e785828661048c604083615b37565b60608301526104f7604082615b37565b905061050a85828661048c604083615b37565b608083015261051a604082615b37565b905061052d85828661048c604083615b37565b60c083015261053d604082615b37565b905061055085828661048c604083615b37565b60e0830152610560604082615b37565b905061057385828661048c604083615b37565b60a0830152610583604082615b37565b905061059685828661048c604083615b37565b6101008301526105a7604082615b37565b90506105ba85828661048c604083615b37565b6101208301526105cb604082615b37565b90506105de85828661048c604083615b37565b610140830151526105f0604082615b37565b9050610603858286610435602083615b37565b610160830152610614602082615b37565b90505f5b83811015610692575f5b60098110156106895761063c878488610435602083615b37565b84610180015183601c811061065357610653615b71565b6020020151826009811061066957610669615b71565b60200201818152505060208361067f9190615b37565b9250600101610622565b50600101610618565b505f5b6106a160016029615b37565b8110156106f0576106b9868387610435602083615b37565b836101c0015182602a81106106d0576106d0615b71565b6020020181815250506020826106e69190615b37565b9150600101610695565b50610702858286610435602083615b37565b6101a0830152610713602082615b37565b905061072685828661048c604083615b37565b6101408301516020015261073b604082615b37565b905061074e85828661048c604083615b37565b61014083015160026020020152610766604082615b37565b90505f5b6107756001856155de565b8110156107be5761078d86838761048c604083615b37565b836101e0015182601b81106107a4576107a4615b71565b60200201526107b4604083615b37565b915060010161076a565b505f5b83811015610811576107da868387610435602083615b37565b83610200015182601c81106107f1576107f1615b71565b6020020181815250506020826108079190615b37565b91506001016107c1565b505f5b60048110156108655761082e868387610435602083615b37565b836102200151826004811061084557610845615b71565b60200201818152505060208261085b9190615b37565b9150600101610814565b5061087785828661048c604083615b37565b610240830152610888604082615b37565b905061089b85828661048c604083615b37565b610260830152509392505050565b5f600180826108d6866108d1896108cc6108c78a6310000000615b37565b6125e1565b6125f9565b612614565b90505f6108f4876108ef8a6108cc6108c78b6001615b37565b61262d565b90505f5b61092360107f00000000000000000000000000000000000000000000000000000000000000006155de565b811015610990575f61094d8c838151811061094057610940615b71565b6020026020010151612655565b905061095d866108cc8684612614565b955061096d856108cc8584612614565b9450610979848b612614565b9350610985838b61262d565b9250506001016108f8565b505f5b60108110156109f7575f8a82601081106109af576109af615b71565b602002015190506109c4866108cc8684612614565b95506109d4856108cc8584612614565b94506109e0848b612614565b93506109ec838b61262d565b925050600101610993565b50610a02848461266a565b9a9950505050505050505050565b5f5f610a2583606001518561016001516125f9565b905060015f5b7f0000000000000000000000000000000000000000000000000000000000000000811015610b17575f86610180015182601c8110610a6b57610a6b615b71565b602002015180519091505f90610a89908360015b6020020151612614565b9050848114610aab576040516313f8744360e31b815260040160405180910390fd5b5f876080015184601c8110610ac257610ac2615b71565b60200201519050610ad38382612678565b9550610b07856108cc60016108d1856108cc8e604001518b601c8110610afb57610afb615b71565b6020020151600161262d565b9450505050806001019050610a2b565b50610b2061528f565b5f5b6029811015610b70576101c0870151610b3c600183615b37565b602a8110610b4c57610b4c615b71565b6020020151828260298110610b6357610b63615b71565b6020020152600101610b22565b505f610b8582875f0151886020015186612826565b9050600160025b7f0000000000000000000000000000000000000000000000000000000000000000811015610be257610bd882896080015183601c8110610bce57610bce615b71565b60200201516125f9565b9150600101610b8c565b50610c08610bf5836108cc60018561262d565b6108d18a6101a001518a606001516125f9565b94909414979650505050505050565b5f610c206152ae565b5f610c4f8460c001517f000000000000000000000000000000000000000000000000000000000000000061289f565b90505f7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03811115610c8a57610c8a61593e565b604051908082528060200260200182016040528015610cb3578160200160208202803683370190505b5090505f7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03811115610cef57610cef61593e565b604051908082528060200260200182016040528015610d2857816020015b610d1561531e565b815260200190600190039081610d0d5790505b509050610d5b610d56876101000151855f81518110610d4957610d49615b71565b602002602001015161262d565b612968565b84610120018181525050610d90610d56876101000151855f81518110610d8357610d83615b71565b6020026020010151612614565b610140850181905261012085015160e0880151610db1926108d191906125f9565b845260c0860151610de190610dc590612968565b6108cc8661012001516108ef8a60e001518961014001516125f9565b6020850152815160019083905f90610dfb57610dfb615b71565b602002602001018181525050876102400151815f81518110610e1f57610e1f615b71565b6020908102919091010152600160a08501525f60c08501528351610e42906129d8565b60408501526020840151610e55906129d8565b606085015260015b610e6960016024615b37565b8111610f0a57610e8185604001518660a001516125f9565b838281518110610e9357610e93615b71565b602002602001018181525050610edc8560c001516108d18b6101c00151600185610ebd91906155de565b602a8110610ecd57610ecd615b71565b60200201518860a001516125f9565b60c086015260a08086015190880151610ef591906125f9565b60a0860152610f0381615b85565b9050610e5d565b505f5b6005811015610fe3575f610f22601e83615b37565b90505f610f3160016024615b37565b610f3b9084615b37565b9050610f6c858381518110610f5257610f52615b71565b60200260200101516108d189606001518a60a001516125f9565b858381518110610f7e57610f7e615b71565b602002602001018181525050610fbb8760c001516108d18d6101c0015184602a8110610fac57610fac615b71565b60200201518a60a001516125f9565b60c088015260a080880151908a0151610fd491906125f9565b60a08801525050600101610f0d565b50876020015181600181518110610ffc57610ffc615b71565b602002602001018190525086606001518160028151811061101f5761101f615b71565b602002602001018190525086608001518160038151811061104257611042615b71565b60200260200101819052508660a001518160048151811061106557611065615b71565b60200260200101819052508660c001518160058151811061108857611088615b71565b60200260200101819052508660e00151816006815181106110ab576110ab615b71565b6020026020010181905250866101000151816007815181106110cf576110cf615b71565b6020026020010181905250866101200151816008815181106110f3576110f3615b71565b60200260200101819052508661014001518160098151811061111757611117615b71565b602002602001018190525086610160015181600a8151811061113b5761113b615b71565b6020026020010181905250866101c0015181600b8151811061115f5761115f615b71565b602002602001018190525086610180015181600c8151811061118357611183615b71565b6020026020010181905250866101a0015181600d815181106111a7576111a7615b71565b6020026020010181905250866101e0015181600e815181106111cb576111cb615b71565b602002602001018190525086610200015181600f815181106111ef576111ef615b71565b60200260200101819052508661022001518160108151811061121357611213615b71565b60200260200101819052508661024001518160118151811061123757611237615b71565b60200260200101819052508661026001518160128151811061125b5761125b615b71565b60200260200101819052508661028001518160138151811061127f5761127f615b71565b6020026020010181905250866102a00151816014815181106112a3576112a3615b71565b6020026020010181905250866102c00151816015815181106112c7576112c7615b71565b6020026020010181905250866102e00151816016815181106112eb576112eb615b71565b60200260200101819052508661030001518160178151811061130f5761130f615b71565b60200260200101819052508661032001518160188151811061133357611333615b71565b60200260200101819052508661034001518160198151811061135757611357615b71565b602002602001018190525086610360015181601a8151811061137b5761137b615b71565b602002602001018190525086610380015181601b8151811061139f5761139f615b71565b6020026020010181905250866103a0015181601c815181106113c3576113c3615b71565b6020026020010181905250866103c0015181601d815181106113e7576113e7615b71565b6020026020010181905250876040015181601e8151811061140a5761140a615b71565b6020026020010181905250876060015181601f8151811061142d5761142d615b71565b602002602001018190525087608001518160208151811061145057611450615b71565b60200260200101819052508760a001518160218151811061147357611473615b71565b60200260200101819052508761012001518160228151811061149757611497615b71565b6020026020010181905250876101000151816023815181106114bb576114bb615b71565b60200260200101819052508760c00151816024815181106114de576114de615b71565b60200260200101819052508760e001518160258151811061150157611501615b71565b60200260200101819052505f61154787608001518660c001518b6102000151877f00000000000000000000000000000000000000000000000000000000000000006129ea565b9050611571815f8151811061155e5761155e615b71565b60200260200101518661012001516125f9565b608086018190526102008a01515160e08901516115a292916108d19161159791906125f9565b8861014001516125f9565b608086015260e08701516115b590612b48565b60a08601525f6115c760016024615b37565b6115d2906001615b37565b90505f5b61160160017f00000000000000000000000000000000000000000000000000000000000000006155de565b811015611812575f61163460017f00000000000000000000000000000000000000000000000000000000000000006155de565b8210159050806117a357611668610d568b6101000151898560016116589190615b37565b81518110610d4957610d49615b71565b6101208901526101008a015161169890610d569089611688866001615b37565b81518110610d8357610d83615b71565b61014089015260a08801516101208901516116b391906125f9565b61016089015260a088015160e08b01516116db916116d0916125f9565b8961014001516125f9565b61018089018190526116fe906116f0906129d8565b6108d18a61016001516129d8565b866117098486615b37565b8151811061171957611719615b71565b6020026020010181815250505f6117528961018001518e61020001518560016117429190615b37565b601c8110610bce57610bce615b71565b905061178c816108d18b61016001518887600161176f9190615b37565b8151811061177f5761177f615b71565b60200260200101516125f9565b905061179c896080015182612614565b60808a0152505b6117c26117b88960a001518c60e001516125f9565b8b60e001516125f9565b60a08901526101e08c015182601b81106117de576117de615b71565b6020020151856117ee8486615b37565b815181106117fe576117fe615b71565b6020908102919091010152506001016115d6565b5061183e60017f00000000000000000000000000000000000000000000000000000000000000006155de565b6118489082615b37565b90506118686118608961010001518a60c0015161262d565b60019061266a565b60e08701515261010088015160c08901516118ac91611860916108ef907f07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76906125f9565b60e0878101805160200192909252815180516040909101529051805160609091015260a0870151908901516118ee916118e4916125f9565b8960e001516125f9565b60a08701525f5b600481101561199c575f6119278860e00151836004811061191857611918615b71565b60200201518960a001516125f9565b9050611932816129d8565b886101000151836004811061194957611949615b71565b602002015260a088015160e08b015161196291906125f9565b8860a001818152505061198e88608001516108d1838f61022001518660048110610bce57610bce615b71565b6080890152506001016118f5565b506101008601515184518590839081106119b8576119b8615b71565b602090810291909101810191909152610100870151908101516119dc916002610a7f565b846119e8836001615b37565b815181106119f8576119f8615b71565b60209081029190910101526101008601516060015184611a19836002615b37565b81518110611a2957611a29615b71565b60209081029190910101525f5b6003811015611a8b578a61014001518160038110611a5657611a56615b71565b60200201518483611a6681615b85565b945081518110611a7857611a78615b71565b6020908102919091010152600101611a36565b506040518060400160405280600181526020016002815250838281518110611ab557611ab5615b71565b60200260200101819052508560800151848280611ad190615b85565b935081518110611ae357611ae3615b71565b602002602001018181525050611b0d8a61022001518960c001518a608001518d6101a00151612b53565b611b2a5760405163a2a2ac8360e01b815260040160405180910390fd5b5f8a6102600151905080848381518110611b4657611b46615b71565b6020026020010181905250886101000151858381518110611b6957611b69615b71565b602002602001018181525050611b7d615336565b611b878587612eb6565b8152611b9282612fae565b602082018190528c5182515f92611baa929190612ff4565b90505f5f611bba8f5f0151613085565b91509150611bc782613146565b611bd081613146565b8351611bdd9083856131e5565b84526020840151611bef9082856131e5565b602085018190528451611c0191613214565b9f9e505050505050505050505050505050565b611c1c615022565b50604080516103e0810182526120008152600d602080830191909152601182840152825180840184527f027302a9f1897167f79b74a71dd1558e3a69cefa96c98e94c7c0f766bd1d2bcb81527f2515389a7ef0546fe7c56260129132c12a8eef371c3f22786af7f8d6ab7c60e5818301526060830152825180840184527f22462de1db5e1340e5bf6aec9c11bf7e7a6cc0c35f29f457684c6eec7cd5c5c681527f01dfc98a0c382ff395ae0236bf97e801452533f3caa87c7dced9f68bdc1ed210818301526080830152825180840184527f11e0f52b055371aea42a1b22947e7e9eee7e791ab9e947053f46e2782391370c81527f0f3c94cb00fedd2902b259d6cdc6105e578a7b5ddf1f0c153fd061deec17d12a8183015260a0830152825180840184527f0a106232b6d74de2311faac4c886951df58d1e19b22f33523b239ab765e164bc81527f07f5dfd7a9464341586ffe3f0d977ba991e3d5c988c7c0ea2b645b6c11dee2d98183015260c0830152825180840184527f041ff6d6168cd52a5201246e3da52e0cb260a2a5fa567e7bff13c5e3c744980781527f11b370611ff9c70c66e2da51cc8ffb647e7cf40c85762d7bfe64e2f18a6a0f4f8183015260e0830152825180840184527f12aa823df85fb765f0155977a70179b7aaf9aeac1155d778de46cb37f8faf2fa81527f2c3c8f16c5811640a0c3c503fe4918cf16de1383fbb359a2c063ace62d0189ef81830152610100830152825180840184527f0c4032c3079594eb75a8449d3d5ce8bc3661650d53f9b24d923d8f404cb0bbc981527f1084d709650356d40f0158fd6da81f54eb5fe796a0ca89441369b7c24301f85181830152610120830152825180840184527f0fb1ece2260000daef835e11c53b92f3816bbf66b6ebb43e364190027b77fe1081527f1893ea57a05c765b064019fd0abd1076edabf46d1e12c80d10a82cbeb3b59b3481830152610140830152825180840184527f2f2778e46cce7a1f87dde2bde498d36f7683f5671f1022e182515e191582f4f181527f0ddf6aec7c456c38be13fba3151ee3bae583c12438091bb70c403a22cfd8a8d781830152610160830152825180840184527f2f48eb759c28a68804dc0a9ca56b663e8a0d34e8cff4fca0dcf11d588a89738d81527f12bdd73bf2a510e729182fa7a97453d2819406c6d74d9cbc32b18a701f93026981830152610180830152825180840184527f08e14ddb419c267be8d5531e70141db8b637b9384198fe2da596908356b46a0181527f0bc9394fcfbb5cce0c123f14fea5469404e5b3c49766fa5484cd9bdcfddf2a49818301526101a0830152825180840184527f0e5dcd1acfe7ef2e6b46edb70fc2067187a1865939739730b14b54bb73a8bb1d81527f225366ddb76dc74766b564e123631cf98a813b7111f430502caa1070f42e8c89818301526101c0830152825180840184527f0d17a9a4387495fb2b691b245aa6f13e25d7af5b468c2503fd6ee7ffdb819eb281527f143b6e0733448ea59aeb2ef50e1b5e757a6720689ae9eb46ba1297d566da5651818301526101e0830152825180840184527f0ad0ed837fdb4a9a131b192a6184105210ce233ad85e80fc8f1ca7dfc028433081527f029148c0965a4ffa8b6884fc10677316afd2cd7d794a41124d197b5900a6552481830152610200830152825180840184527f07aa26298594e9aa3ed2e0cf1c372108f142acca0d94f0e3280ad8f6f7fea6f281527f045e7e9f923fcf98cbc38bfec20a81bc03e34588928a034a3d6cb9e1f7e9c22f81830152610220830152825180840184527f168740345edd76afa07da3ca2d75150bed7a67b12c47c528ee3ecdd7223ec92981527f1f211045a0fd5872f1c69ea0c8b2bf7a255743111fa34cf46cefd4900cd6f77a81830152610240830152825180840184527f2692234e86e35b65b0b2ca616655f2387f396f041d826cde54bd3133cfe00a1081527f26d963bc3e72700b141f37e9771cbf98e59674139d2ed5e581f6465c2edb85a081830152610260830152825180840184527e5cbcf3a063e8f7142c2a0c6499c584486f3c8c90453fb4d387e5fcd9fd298581527f07de59eeee14a344c8b030a6a6d12650a6c37706700bb1bf55b5595ce910b79981830152610280830152825180840184527f0769795fa9f0d34097d7299603c36221e3bf395161fc91a78f481804abf7e17081527f190150a49e86cb7cf15c464f3f741c800d9a9d944953023fe79a5be579ac274c818301526102a0830152825180840184527f08129f3cdaaafc492279ea99f91473af386bfbf92c55fd2a5efb9d3ad39c834181527f0e02894a268b25478a7e323f900b1c81e63227533d55a9269b6e083344ddc5c3818301526102c0830152825180840184527f223ba058ba70d89680a3236d02e81dea1fd139b9f9ec0074605a6073059b9e7d81527f04d18416fa482ce0c6eece742967f0862202d508046a5141f5fa43fd8f75fc6b818301526102e0830152825180840184527f25f874dc5eb164f0a82bd3c3f189e02089314799c7a555838569fe8499c30fe981527f256b5957d823a2daf400f685cd16a42182563f32442982d6236b359d4ee7809a81830152610300830152825180840184527f099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d2681527e15b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f81830152610320830152825180840184527f1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e81527f305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d1981830152610340830152825180840184527f061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d81527f1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e81830152610360830152825180840184527f043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce81527f261522c4089330646aff96736194949330952ae74c573d1686d9cb4a007338548183015261038083015282518084018452600181526002818301526103a083015282518084019093527f1104b2598e170ee71593833e2ea491147e60225ba814c1f8cd3ad557de0a98ee83527f2741fcea4e36b97464e7174e83dceaa89b2f2a49a3b4352193c11737f79d39a2908301526103c081019190915290565b5f6103f461255a8385615b9d565b612655565b61256761531e565b60408051808201909152805f516020615c6c5f395f51905f5261258d60205f8789615b4a565b61259691615b9d565b6125a09190615bba565b81526020908101905f516020615c6c5f395f51905f52906125c5906040908789615b4a565b6125ce91615b9d565b6125d89190615bba565b90529392505050565b5f5f516020615c8c5f395f51905f52825b0692915050565b5f5f516020615c8c5f395f51905f5282840990505b92915050565b5f5f516020615c8c5f395f51905f528284089392505050565b5f5f516020615c8c5f395f51905f52825f516020615c8c5f395f51905f520384089392505050565b5f5f516020615c8c5f395f51905f52826125f2565b5f6103f4836108cc84612968565b5f5f604051806101200160405280619d8081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec5181526020016105a081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31815260200161024081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd3181526020016105a081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec518152602001619d8081525090505f600190505f5f90505b600981101561277157612767826108cc878461262d565b9150600101612750565b5061277a615349565b5f5b60098110156127cb576127ac610d5685836009811061279d5761279d615b71565b60200201516108cc898561262d565b8282600981106127be576127be615b71565b602002015260010161277c565b505f5b600981101561281157612807856108d18984600981106127f0576127f0615b71565b6020020151858560098110610bce57610bce615b71565b94506001016127ce565b5061281c84836125f9565b9695505050505050565b5f61282f615368565b61283a868285613408565b612846868683866135b4565b612852868683866137a0565b61285d8682856139c5565b612868868285613bb9565b61287486868386613f02565b61287f8682856143ae565b61288a8682856147c0565b612895868285614b81565b61281c8185614e81565b60605f826001600160401b038111156128ba576128ba61593e565b6040519080825280602002602001820160405280156128e3578160200160208202803683370190505b50905083815f815181106128f9576128f9615b71565b602090810291909101015260015b838110156129605761293b8261291e6001846155de565b8151811061292e5761292e615b71565b6020026020010151612b48565b82828151811061294d5761294d615b71565b6020908102919091010152600101612907565b509392505050565b5f5f8290505f604051602081526020808201526020604082015282606082015260025f516020615c8c5f395f51905f520360808201525f516020615c8c5f395f51905f5260a082015260205f60c08360055afa806129c4575f5ffd5b505f51608091909101604052949350505050565b5f516020615c8c5f395f51905f520390565b60605f826001600160401b03811115612a0557612a0561593e565b604051908082528060200260200182016040528015612a2e578160200160208202803683370190505b509050825b8015612b3d575f85612a466001846155de565b81518110612a5657612a56615b71565b602002602001015190505f89600184612a6f91906155de565b601c8110612a7f57612a7f615b71565b602002015190505f612ada612a9e612a97858d6125f9565b60026125f9565b6108ef8b612aad6001896155de565b601c8110612abd57612abd615b71565b60200201516108cc612ad4886108cc60018a61262d565b8761262d565b9050612afb816108cc610d56612af5876108cc60018961262d565b86612614565b99508990508085612b0d6001876155de565b81518110612b1d57612b1d615b71565b60200260200101818152505050505080612b3690615bd9565b9050612a33565b509695505050505050565b5f61260e82836125f9565b5f600181612b6c612b6687610100614eda565b8361262d565b905080612b8c5760405163835eb8f760e01b815260040160405180910390fd5b612b94615387565b80518390525f5b7f0000000000000000000000000000000000000000000000000000000000000000811015612c7d575f612bcf8260096155c7565b612bda906001615b37565b905084835f0151826101008110612bf357612bf3615b71565b60200201525f612c04826001615b37565b90505b612c12600983615b37565b811015612c73578351612c5290612c2a6001846155de565b6101008110612c3b57612c3b615b71565b60200201518a85601c8110610bce57610bce615b71565b8451826101008110612c6657612c66615b71565b6020020152600101612c07565b5050600101612b9b565b50608081018390525f602082018190525b610100811015612d8e57612caf612ca983608001518a6125f9565b8561262d565b8260a00151826101008110612cc657612cc6615b71565b602002015260a0820151612cf090826101008110612ce657612ce6615b71565b6020020151612968565b8260a00151826101008110612d0757612d07615b71565b602002018181525050612d4d82602001516108d1845f0151846101008110612d3157612d31615b71565b60200201518560a00151856101008110610bce57610bce615b71565b60208301526080820151612d81907f204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d66125f9565b6080830152600101612c8e565b505f612d9f836108cc610100612968565b9050612daf8260200151826125f9565b602083015260a0820151612dca905f5b6020020151826125f9565b604083015260a0820151612df690612de560016101006155de565b6101008110612dbf57612dbf615b71565b60608301526040820151612e0c908a6002610bce565b60c08301819052612e6c906108d1612e448b7f204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d661262d565b60208d015160408e01516108cc91612e5b9161262d565b8e5160208901516108ef91906125f9565b60c083018190526060830151612ea191612e95916108d1906108cc8e600260200201518c61262d565b6108ef858c6003610bce565b60c08301819052159998505050505050505050565b612ebe61531e565b7f00000000000000000000000000000000000000000000000000000000000000005f5b81811015612f1357612f0b858281518110612efe57612efe615b71565b6020026020010151613146565b600101612ee1565b50604051600190815b60018401811015612f785760208102870160208202870181515160408501528151602001516060850152805160808501525050604080830160606040850160075afa8316925060408260808460065afa90921691600101612f1c565b5080518452602081015160208501525080612fa6576040516352ec174560e11b815260040160405180910390fd5b505092915050565b612fb661531e565b5f516020615c6c5f395f51905f5282602001515f516020615c6c5f395f51905f52612fe191906155de565b612feb9190615bba565b60208301525090565b5f5f5f61300086613085565b9150915061300c6153cb565b82518152602080840151818301528251604080840191909152838201516060840152875160808401528782015160a0840152865160c08401528682015160e08401525161307a9161305f91849101615bee565b60405160208183030381529060405280519060200120612655565b979650505050505050565b61308d61531e565b61309561531e565b82516020808501516040860151606087015160cc90811b608892831b604494851b90961795909517949094178652608087015160a088015160c089015160e08a0151871b90841b91851b9092171717868401526101008701516101208801516101408901516101608a0151871b90841b91851b909217171785526101808701516101a08801516101c08901516101e09099015190951b9790911b9390911b1791909117939093179281019290925291565b805160208201515f5f516020615c6c5f395f51905f528380095f516020615c6c5f395f51905f5260035f516020615c6c5f395f51905f52838709085f516020615c6c5f395f51905f5284850914915050806131df5760405162461bcd60e51b8152602060048201526019602482015278706f696e74206973206e6f74206f6e2074686520637572766560381b60448201526064016100e5565b50505050565b6131ed61531e565b6131f561531e565b6131ff8386614f3b565b905061320b8185614f91565b95945050505050565b81516020808401518351848301516040805194850195909552938301919091527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260608301527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed60808301527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b60a08301527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60c083015260e08201526101008101919091527f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c16101208201527f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b06101408201527f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe46101608201527f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e556101808201525f9081906101a00160405160208183030381529060405290505f5f60086001600160a01b0316836040516133aa9190615c22565b5f60405180830381855afa9150503d805f81146133e2576040519150601f19603f3d011682016040523d82523d5f602084013e6133e7565b606091505b509150915081801561281c57508080602001905181019061281c9190615c38565b5f613414846007614ff2565b90507f183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f80000005f61347561346f61344985600361262d565b6108cc6134646134598b5f614ff2565b6108cc8c601d614ff2565b6108cc8b601c614ff2565b836125f9565b90506134f66134eb6134d26134b96134a0856108d16134958d6002614ff2565b6108cc8e601c614ff2565b6108d16134ae8c6003614ff2565b6108cc8d601d614ff2565b6108d16134c78b6004614ff2565b6108cc8c601e614ff2565b6108d16134e08a6005614ff2565b6108cc8b601f614ff2565b6108d1886001614ff2565b9050613515816108d161350a86600161262d565b6108cc8a6027614ff2565b905061352181846125f9565b905061352d81856125f9565b8552505f905061356761355d61355261354788601c614ff2565b6108d189601f614ff2565b6108ef886024614ff2565b6108d1875f614ff2565b9050613578816108cc84600261262d565b9050613589816108cc84600161262d565b905061359581836125f9565b90506135a181846125f9565b9050808460015b60200201525050505050565b5f5f5f6135ea6135e06135c889601c614ff2565b6108d16135d68b6012614ff2565b8a606001516125f9565b8760800151612614565b9050613623816108cc6136196136018b601d614ff2565b6108d161360f8d6013614ff2565b8c606001516125f9565b8960800151612614565b9050613648816108cc61361961363a8b601e614ff2565b6108d161360f8d6014614ff2565b905061366d816108cc61361961365f8b601f614ff2565b6108d161360f8d6015614ff2565b92505f90506136916135e061368389601c614ff2565b6108d16135d68b600e614ff2565b90506136b6816108cc6136196136a88b601d614ff2565b6108d161360f8d600f614ff2565b90506136db816108cc6136196136cd8b601e614ff2565b6108d161360f8d6010614ff2565b9050613700816108cc6136196136f28b601f614ff2565b6108d161360f8d6011614ff2565b91505f9050613727613721613716896020614ff2565b6108d18a601a614ff2565b846125f9565b905061375c816108ef61375661373e8b6028614ff2565b6108d161374c8d601b614ff2565b8c60a001516125f9565b856125f9565b905061376881856125f9565b6040860152505f61378b61375661378089601b614ff2565b6108cc8a6028614ff2565b9050808560035b602002015250505050505050565b5f5f6137fe6137e66137ce6137b96136198a6016614ff2565b6108d16137c78b6017614ff2565b8a516125f9565b6108d16137dc8a6018614ff2565b89602001516125f9565b6108d16137f4896019614ff2565b88604001516125f9565b91505f61383561381c61381289601c614ff2565b8860800151612614565b6108d161382a8a6003614ff2565b6108cc8b6024614ff2565b90505f61385e61384689601d614ff2565b6108d16138538b5f614ff2565b6108cc8c6025614ff2565b90505f61388861386f8a601e614ff2565b6108d161387d8c6001614ff2565b6108cc8d6026614ff2565b90506138c76138af6138a1856108d1868d5f01516125f9565b6108d1848c602001516125f9565b6108d16138bd8c6004614ff2565b8b604001516125f9565b93505050505f6138db613721886021614ff2565b90505f6138ec613721896021614ff2565b90505f61392461390b6139008b6023614ff2565b6108d18c6006614ff2565b6108ef6139198c6023614ff2565b6108cc8d6006614ff2565b90505f613942612b6661393787896125f9565b6108cc8d6021614ff2565b905061394e81886125f9565b90505f6139766139686139628d6006614ff2565b876125f9565b6108ef6139628e6022614ff2565b90505f6139848c6023614ff2565b90505f613994612b6683846125f9565b60808c0185905260a08c0184905290506139ae818b6125f9565b8b6006602002015250505050505050505050505050565b5f6139d15f600161262d565b90505f6139df5f600261262d565b90505f6139ed5f600361262d565b90505f613a096139fe88601d614ff2565b6108ef89601c614ff2565b90505f613a25613a1a89601e614ff2565b6108ef8a601d614ff2565b90505f613a41613a368a601f614ff2565b6108ef8b601e614ff2565b90505f613a5d613a528b6024614ff2565b6108ef8c601f614ff2565b905083613a6e816108cc818b612614565b9050613a7e816108cc878a612614565b9050613a8e816108cc8789612614565b9050613a9f816108cc8d6008614ff2565b9050613aab818a6125f9565b60e08b01525082613ac0816108cc818b612614565b9050613ad0816108cc868a612614565b9050613ae0816108cc8689612614565b9050613af1816108cc8d6008614ff2565b9050613afd818a6125f9565b6101008b01525081613b13816108cc818b612614565b9050613b23816108cc858a612614565b9050613b33816108cc8589612614565b9050613b44816108cc8d6008614ff2565b9050613b50818a6125f9565b6101208b01525080613b66816108cc818b612614565b9050613b76816108cc848a612614565b9050613b86816108cc8489612614565b9050613b97816108cc8d6008614ff2565b9050613ba3818a6125f9565b610140909a019990995250505050505050505050565b613bf26040518060e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b613bfd84601d614ff2565b8152613c0a84601e614ff2565b6020820152613c1a846024614ff2565b6040820152613c2a846027614ff2565b6060820152613c3a846026614ff2565b6080820152613c4a846025614ff2565b60a08201525f613c5b856002614ff2565b90505f613c68865f614ff2565b90505f613c7c8460400151855f015161262d565b90505f613c91856020015186602001516125f9565b606086015190915086905f90613ca790806125f9565b90505f613cc5613cbf89602001518a606001516125f9565b886125f9565b90505f613ce4613cdd8a60a001518b60400151612614565b8a51612614565b9050613cf361396282886125f9565b9050613d1a613d14613d0e613d08848761262d565b8861262d565b84612614565b83612614565b9050613d42613d37613d2c83876125f9565b6108cc8f6009614ff2565b6108cc60018a61262d565b6101608c015250505050602085015160808601515f91613d6191612614565b90505f613d7f613d758860600151886125f9565b886020015161262d565b90505f613da3613d8f84876125f9565b6108d16137568b60a001518c5f015161262d565b9050613dcb613dc0613db5838c6125f9565b6108cc8e6009614ff2565b6108cc60018961262d565b6101808b0152505f9150613dec9050613de5836011612614565b87516125f9565b90505f613df98384612614565b9050613e058182612614565b90505f613e138360096125f9565b9050613e3c613e36613721613e2f8b60a001518c5f0151612614565b8b51612614565b8261262d565b60c089018190525f90613e5790613cbf90613d2c908d6125f9565b9050613e6a8b600b602002015182612614565b6101608c0152505086515f9250613e919150613de590613e8a9080612614565b8851612614565b90505f613ed1613eac836108cc8a5f01518b60a0015161262d565b60208901516108ef90613ebf9080612614565b6108cc8b602001518c60800151612614565b9050613eee89600c60200201516108d1613cbf613d2c858d6125f9565b89600c602002015250505050505050505050565b613f6c604051806101e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b613f84613f7a86601e614ff2565b85604001516125f9565b808252613fa3906108d1613f9988601d614ff2565b87602001516125f9565b808252613fb8906108d1613de588601c614ff2565b808252613fca906108d1876001614ff2565b80825260208201819052613fe3906108ef87601f614ff2565b8152613ffe613ff3866024614ff2565b6108ef87601c614ff2565b608082015261401c614011866027614ff2565b6108ef87601f614ff2565b60608201526080810151614035906108cc81600161262d565b6101c082015260808101516140759061406b90614064906108cc60015f516020615c8c5f395f51905f526155de565b6001612614565b82606001516125f9565b60a082018190526140ab9061409d906108cc614092896002614ff2565b6108cc8a6003614ff2565b6108cc61375688600a614ff2565b83600e60200201526101c08101516140cf9061409d906108cc614092896002614ff2565b6101e084015280516140f4906108cc6140e9886002614ff2565b6108cc896003614ff2565b6101208201525f61411361410987601f614ff2565b836020015161262d565b9050614124816108cc83600161262d565b60e0830152614141614137876026614ff2565b86604001516125f9565b60408301819052614164906108d161415a896025614ff2565b88602001516125f9565b60408301819052614184906108d161417d896024614ff2565b88516125f9565b60408301526141a1614197876027614ff2565b836040015161262d565b60408301525f6141c06141b5886026614ff2565b6108ef89601e614ff2565b90506142126141ed61346f614064866080015160015f516020615c8c5f395f51905f526108cc91906155de565b6108cc614064866040015160015f516020615c8c5f395f51905f526108cc91906155de565b60c084015260408301516142349061422a90806125f9565b846040015161262d565b61010084015260c083015161426090614252906108cc8a6004614ff2565b6108cc6139628a600a614ff2565b6102008601526101c083015161427f90614252906108cc8a6004614ff2565b61022086015261010083015161429e90614252906108cc8a6004614ff2565b61024086015260e08301516142b8906108cc896004614ff2565b6101408401526142d76142cc886025614ff2565b6108ef89601d614ff2565b6101608401526080830151614315906141b59061430a90614064906108cc60015f516020615c8c5f395f51905f526155de565b8561016001516125f9565b61018084018190526101208401516101a0850181905261434c916108d1906108cc6143418c6005614ff2565b6108cc8d6002614ff2565b6101a08401819052835161436c91906108d1906108cc6143418c5f614ff2565b6101a084018190526101408401516143849190612614565b6101a0840181905261439e906108cc6139628a600a614ff2565b6101a0840181905285600d613792565b6143e76040518060e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b61441c6144036143f886601c614ff2565b6108cc876025614ff2565b6108d1614411876024614ff2565b6108cc88601d614ff2565b815261446161445661443d61443287601c614ff2565b6108cc88601f614ff2565b6108d161444b88601d614ff2565b6108cc89601e614ff2565b6108ef866026614ff2565b6040820181905261447690600160441b6125f9565b6040820181905261448c906108ef866027614ff2565b60408201819052815161449f9190612614565b604082018190526144b5906108cc866005614ff2565b604082015280516144ca90600160441b6125f9565b8082526144ea906108d16144df876024614ff2565b6108cc886025614ff2565b80825260208201819052614511906108ef61450687601e614ff2565b6108d188601f614ff2565b60208201819052614527906108cc866004614ff2565b6020820152805160608201819052614544906108d186601f614ff2565b60608201819052614568906108ef61455d876026614ff2565b6108d1886027614ff2565b6060820181905261457d906108cc865f614ff2565b8160600181815250505f6145a661459c83602001518460400151612614565b8360600151612614565b90506145b7816108cc876003614ff2565b90506145cf6145c7866025614ff2565b6140006125f9565b608083018190526145e5906108d1876024614ff2565b608083018190526145f8906140006125f9565b6080830181905261460e906108d187601e614ff2565b60808301819052614621906140006125f9565b60808301819052614637906108d187601d614ff2565b6080830181905261464a906140006125f9565b60808301819052614660906108d187601c614ff2565b60808301819052614676906108ef87601f614ff2565b6080830181905261468c906108cc876005614ff2565b608083015261469f6145c7866026614ff2565b60a083018190526146b5906108d1876025614ff2565b60a083018190526146c8906140006125f9565b60a083018190526146de906108d1876024614ff2565b60a083018190526146f1906140006125f9565b60a08301819052614707906108d187601f614ff2565b60a0830181905261471a906140006125f9565b60a08301819052614730906108d187601e614ff2565b60a08301819052614746906108ef876027614ff2565b60a0830181905261475b906108cc875f614ff2565b60a0830181905260808301515f916147739190612614565b9050614784816108cc886004614ff2565b90506147908282612614565b60c084018190526147a9906108cc61396289600b614ff2565b60c084018190528560136020020152505050505050565b6148366040518061022001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b61484f61484485601c614ff2565b6108d1866002614ff2565b815261486a61485f85601d614ff2565b6108d1866003614ff2565b602082015261488861487d85601e614ff2565b6108d1866004614ff2565b60408201526148a661489b85601f614ff2565b6108d1866005614ff2565b606082015280516148d8906148d1906148ca906148c390806125f9565b84516125f9565b83516125f9565b82516125f9565b608082015260208101516149169061490c90614902906148f890806125f9565b84602001516125f9565b83602001516125f9565b82602001516125f9565b60a082015260408101516149549061494a906149409061493690806125f9565b84604001516125f9565b83604001516125f9565b82604001516125f9565b60c082015260608101516149889061406b9061497e9061497490806125f9565b84606001516125f9565b83606001516125f9565b60e0820152608081015160a08201516149a19190612614565b61010082015260c081015160e08201516149bb9190612614565b61012082015260a08101516149df906149d49080612614565b826101200151612614565b61014082015260e0810151614a03906149f89080612614565b826101000151612614565b610160820152610120810151614a199080612614565b6101e08201819052614a3a90614a2f9080612614565b826101600151612614565b6101e0820152610100810151614a509080612614565b6101a08201819052614a7190614a669080612614565b826101400151612614565b6101a08201819052610160820151614a8891612614565b6101808201526101408101516101e0820151614aa49190612614565b6101c0820152614ab861346f85600c614ff2565b6102008201819052610280840151610180830151614ae3926108d1916108cc906108ef8a6024614ff2565b8360146020020152614b1383601560200201516108d18361020001516108cc856101a001516108ef8a6025614ff2565b8360156020020152614b4383601660200201516108d18361020001516108cc856101c001516108ef8a6026614ff2565b8360166020020152614b7383601760200201516108d18361020001516108cc856101e001516108ef8a6027614ff2565b836017602002015250505050565b614bd36040518061016001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b5f6040518060800160405280614c087f10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e76125e1565b8152602001614c367f0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b6125e1565b8152602001614c637e544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac156125e1565b8152602001614c917f222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b6125e1565b90529050614cae614ca386601c614ff2565b6108d1876002614ff2565b6101208301819052614ced90614ce290614cd790614ccc90806125f9565b8561012001516125f9565b8461012001516125f9565b8361012001516125f9565b8252614cfa85601d614ff2565b6020830152614d0a85601e614ff2565b6040830152614d1a85601f614ff2565b606083015281516020830151614d429161459c91614d389190612614565b8460400151612614565b6080830152614d5561372186600d614ff2565b6101408301528151614d7690614d6c90835f610bce565b8360800151612614565b60a0830152614da284601860200201516108d18461014001516108cc8660a001516108ef8b6024614ff2565b6103008501526020820151614dbd90614d6c90836001610bce565b60c0830152614de984601960200201516108d18461014001516108cc8660c001516108ef8b6025614ff2565b6103208501526040820151614e0490614d6c90836002610bce565b60e0830152614e3084601a60200201516108d18461014001516108cc8660e001516108ef8b6026614ff2565b6103408501526060820151614e4b90614d6c90836003610bce565b610100830152614e7984601b60200201516108d18461014001516108cc8661010001516108ef8b6027614ff2565b84601b6135a8565b815160015b601c811015614ed357614ec9826108d18684601c8110614ea857614ea8615b71565b602002015186614eb96001876155de565b601b8110610bce57610bce615b71565b9150600101614e86565b5092915050565b5f5f8390505f60405160208152602080820152602060408201528260608201528460808201525f516020615c8c5f395f51905f5260a082015260205f60c08360055afa80614f26575f5ffd5b505f5160809190910160405295945050505050565b614f4361531e565b614f4b61531e565b604051835181526020840151602082015284604082015260408160608360075afa80614f75575f5ffd5b5080518252602080820151908301526060016040529392505050565b614f9961531e565b614fa161531e565b6040518451815260208501516020820152835160408201526020840151606082015260408160808360065afa80614fd6575f5ffd5b5080518252602080820151908301526080016040529392505050565b5f8282602881111561500657615006615c57565b6029811061501657615016615b71565b60200201519392505050565b604051806103e001604052805f81526020015f81526020015f815260200161504861531e565b815260200161505561531e565b815260200161506261531e565b815260200161506f61531e565b815260200161507c61531e565b815260200161508961531e565b815260200161509661531e565b81526020016150a361531e565b81526020016150b061531e565b81526020016150bd61531e565b81526020016150ca61531e565b81526020016150d761531e565b81526020016150e461531e565b81526020016150f161531e565b81526020016150fe61531e565b815260200161510b61531e565b815260200161511861531e565b815260200161512561531e565b815260200161513261531e565b815260200161513f61531e565b815260200161514c61531e565b815260200161515961531e565b815260200161516661531e565b815260200161517361531e565b815260200161518061531e565b815260200161518d61531e565b815260200161519a61531e565b81526020016151a761531e565b905290565b6040518061028001604052806151c06153ea565b81526020016151cd61531e565b81526020016151da61531e565b81526020016151e761531e565b81526020016151f461531e565b815260200161520161531e565b815260200161520e61531e565b815260200161521b61531e565b815260200161522861531e565b815260200161523561531e565b8152602001615242615409565b81526020015f8152602001615255615436565b81526020015f8152602001615268615464565b8152602001615275615483565b8152602001615282615368565b815260200161518d6154b1565b6040518061052001604052806029906020820280368337509192915050565b604051806101c001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020016152ec6154b1565b81526020016152f96154b1565b81526020015f81526020015f81526020015f81526020015f8152602001606081525090565b60405180604001604052805f81526020015f81525090565b604051806040016040528061519a61531e565b6040518061012001604052806009906020820280368337509192915050565b604051806103800160405280601c906020820280368337509192915050565b6040518060e0016040528061539a6154cf565b81526020015f81526020015f81526020015f81526020015f81526020016153bf6154cf565b81526020015f81525090565b6040518061010001604052806008906020820280368337509192915050565b6040518061020001604052806010906020820280368337509192915050565b60405180606001604052806003905b61542061531e565b8152602001906001900390816154185790505090565b604051806103800160405280601c905b61544e615349565b8152602001906001900390816154465790505090565b604051806105400160405280602a906020820280368337509192915050565b604051806103600160405280601b905b61549b61531e565b8152602001906001900390816154935790505090565b60405180608001604052806004906020820280368337509192915050565b604051806120000160405280610100906020820280368337509192915050565b5f5f5f5f60408587031215615502575f5ffd5b84356001600160401b03811115615517575f5ffd5b8501601f81018713615527575f5ffd5b80356001600160401b0381111561553c575f5ffd5b87602082840101111561554d575f5ffd5b6020918201955093508501356001600160401b0381111561556c575f5ffd5b8501601f8101871361557c575f5ffd5b80356001600160401b03811115615591575f5ffd5b8760208260051b84010111156155a5575f5ffd5b949793965060200194505050565b634e487b7160e01b5f52601160045260245ffd5b808202811582820484141761260e5761260e6155b3565b8181038181111561260e5761260e6155b3565b805f5b60108110156131df5781518452602093840193909101906001016155f4565b805f5b60038110156131df5761563484835180518252602090810151910152565b6040939093019260209190910190600101615616565b805f5b601c8110156131df578151845f5b600981101561567a57825182526020928301929091019060010161565b565b50505061012093909301926020919091019060010161564d565b805f5b602a8110156131df578151845260209384019390910190600101615697565b805f5b601b8110156131df576156d784835180518252602090810151910152565b60409390930192602091909101906001016156b9565b805f5b601c8110156131df5781518452602093840193909101906001016156f0565b805f5b60048110156131df578151845260209384019390910190600101615712565b8183525f6001600160fb1b03831115615748575f5ffd5b8260051b80836020870137939093016020019392505050565b61576c8188516155f1565b5f602088015161578a61020084018280518252602090810151910152565b5060408801518051610240840152602090810151610260840152606089015180516102808501528101516102a0840152608089015180516102c08501528101516102e084015260a0890151805161030085015281015161032084015260c0890151805161034085015281015161036084015260e089015180516103808501528101516103a084015261010089015180516103c08501528101516103e084015261012089015180516104008501520151610420830152610140880151615853610440840182615613565b5061016088015161050083015261018088015161587461052084018261564a565b506101a08801516124a08301526101c08801516158956124c0840182615694565b506101e08801516158aa612a008401826156b6565b506102008801516158bf6130c08401826156ed565b506102208801516158d461344084018261570f565b5061024088015180516134c08401526020908101516134e0840152610260890151805161350085015201516135208301526135c0613540830181905261591d9083018789615731565b613560830195909552506135808101929092526135a0909101529392505050565b634e487b7160e01b5f52604160045260245ffd5b60405161014081016001600160401b03811182821017156159755761597561593e565b60405290565b604051601f8201601f191681016001600160401b03811182821017156159a3576159a361593e565b604052919050565b5f60c082840312156159bb575f5ffd5b60405160c081016001600160401b03811182821017156159dd576159dd61593e565b604090815283518252602080850151908301528381015190820152606080840151908201526080808401519082015260a0928301519281019290925250919050565b5f82601f830112615a2e575f5ffd5b5f610360615a3b8161597b565b915083018185821115615a4c575f5ffd5b845b82811015615a66578051825260209182019101615a4e565b509195945050505050565b5f82601f830112615a80575f5ffd5b5f610380615a3b8161597b565b5f610be0828403128015615a9f575f5ffd5b50615aa8615952565b615ab284846159ab565b8152615ac18460c08501615a1f565b6020820152615ad4846104208501615a71565b60408201526107a08301516060820152615af2846107c08501615a71565b6080820152610b4083015160a0820152610b6083015160c0820152610b8083015160e0820152610ba0830151610100820152610bc09092015161012083015250919050565b8082018082111561260e5761260e6155b3565b5f5f85851115615b58575f5ffd5b83861115615b64575f5ffd5b5050820193919092039150565b634e487b7160e01b5f52603260045260245ffd5b5f60018201615b9657615b966155b3565b5060010190565b8035602083101561260e575f19602084900360031b1b1692915050565b5f82615bd457634e487b7160e01b5f52601260045260245ffd5b500690565b5f81615be757615be76155b3565b505f190190565b5f8183825b6008811015615c12578151835260209283019290910190600101615bf3565b5050506101008201905092915050565b5f82518060208501845e5f920191825250919050565b5f60208284031215615c48575f5ffd5b815180151581146103f4575f5ffd5b634e487b7160e01b5f52602160045260245ffdfe30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4730644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", - "deployedBytecode": "0x608060405234801561000f575f5ffd5b5060043610610029575f3560e01c8063ea50d0e41461002d575b5f5ffd5b61004061003b3660046154ef565b610054565b604051901515815260200160405180910390f35b5f5f61007f7f00000000000000000000000000000000000000000000000000000000000000006102ee565b905061008c8160206155c7565b85146100ee577f0000000000000000000000000000000000000000000000000000000000000000856100bf8360206155c7565b6040516359895a5360e01b81526004810193909352602483019190915260448201526064015b60405180910390fd5b5f6100f76103fb565b90505f61012588887f0000000000000000000000000000000000000000000000000000000000000000610410565b90506010826040015161013891906155de565b85146101575760405163fa06659360e01b815260040160405180910390fd5b60405163995bf45760e01b81525f9073__$3f925933ac313a1c84f3f4c25b9ea43c90$__9063995bf457906101fa9085908b908b907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090600401615761565b610be060405180830381865af4158015610216573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061023a9190615a8d565b905061028a8787808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050855185516060810151608090910151919350915060016108a9565b815160a0015261029a8282610a10565b6102b7576040516313f8744360e31b815260040160405180910390fd5b6102c2828483610c17565b6102df576040516352ec174560e11b815260040160405180910390fd5b50600198975050505050505050565b5f8060026102fe60016008615b37565b61030891906155c7565b9050610316600260036155c7565b6103209082615b37565b9050600161032f6009856155c7565b61033991906155c7565b6103439082615b37565b90506001610352816029615b37565b61035c91906155c7565b6103669082615b37565b9050610374600160026155c7565b61037e9082615b37565b905061038b6001846155c7565b6103959082615b37565b90506103a3600160046155c7565b6103ad9082615b37565b905060026103bc6001856155de565b6103c691906155c7565b6103d09082615b37565b90506103dd6002806155c7565b6103e79082615b37565b90506103f4601082615b37565b9392505050565b610403615022565b61040b611c14565b905090565b6104186151ac565b5f805b601081101561047a57610447868387610435602083615b37565b9261044293929190615b4a565b61254c565b8351826010811061045a5761045a615b71565b6020020181815250506020826104709190615b37565b915060010161041b565b5061049e85828661048c604083615b37565b9261049993929190615b4a565b61255f565b60208301526104ae604082615b37565b90506104c185828661048c604083615b37565b6040808401919091526104d49082615b37565b90506104e785828661048c604083615b37565b60608301526104f7604082615b37565b905061050a85828661048c604083615b37565b608083015261051a604082615b37565b905061052d85828661048c604083615b37565b60c083015261053d604082615b37565b905061055085828661048c604083615b37565b60e0830152610560604082615b37565b905061057385828661048c604083615b37565b60a0830152610583604082615b37565b905061059685828661048c604083615b37565b6101008301526105a7604082615b37565b90506105ba85828661048c604083615b37565b6101208301526105cb604082615b37565b90506105de85828661048c604083615b37565b610140830151526105f0604082615b37565b9050610603858286610435602083615b37565b610160830152610614602082615b37565b90505f5b83811015610692575f5b60098110156106895761063c878488610435602083615b37565b84610180015183601c811061065357610653615b71565b6020020151826009811061066957610669615b71565b60200201818152505060208361067f9190615b37565b9250600101610622565b50600101610618565b505f5b6106a160016029615b37565b8110156106f0576106b9868387610435602083615b37565b836101c0015182602a81106106d0576106d0615b71565b6020020181815250506020826106e69190615b37565b9150600101610695565b50610702858286610435602083615b37565b6101a0830152610713602082615b37565b905061072685828661048c604083615b37565b6101408301516020015261073b604082615b37565b905061074e85828661048c604083615b37565b61014083015160026020020152610766604082615b37565b90505f5b6107756001856155de565b8110156107be5761078d86838761048c604083615b37565b836101e0015182601b81106107a4576107a4615b71565b60200201526107b4604083615b37565b915060010161076a565b505f5b83811015610811576107da868387610435602083615b37565b83610200015182601c81106107f1576107f1615b71565b6020020181815250506020826108079190615b37565b91506001016107c1565b505f5b60048110156108655761082e868387610435602083615b37565b836102200151826004811061084557610845615b71565b60200201818152505060208261085b9190615b37565b9150600101610814565b5061087785828661048c604083615b37565b610240830152610888604082615b37565b905061089b85828661048c604083615b37565b610260830152509392505050565b5f600180826108d6866108d1896108cc6108c78a6310000000615b37565b6125e1565b6125f9565b612614565b90505f6108f4876108ef8a6108cc6108c78b6001615b37565b61262d565b90505f5b61092360107f00000000000000000000000000000000000000000000000000000000000000006155de565b811015610990575f61094d8c838151811061094057610940615b71565b6020026020010151612655565b905061095d866108cc8684612614565b955061096d856108cc8584612614565b9450610979848b612614565b9350610985838b61262d565b9250506001016108f8565b505f5b60108110156109f7575f8a82601081106109af576109af615b71565b602002015190506109c4866108cc8684612614565b95506109d4856108cc8584612614565b94506109e0848b612614565b93506109ec838b61262d565b925050600101610993565b50610a02848461266a565b9a9950505050505050505050565b5f5f610a2583606001518561016001516125f9565b905060015f5b7f0000000000000000000000000000000000000000000000000000000000000000811015610b17575f86610180015182601c8110610a6b57610a6b615b71565b602002015180519091505f90610a89908360015b6020020151612614565b9050848114610aab576040516313f8744360e31b815260040160405180910390fd5b5f876080015184601c8110610ac257610ac2615b71565b60200201519050610ad38382612678565b9550610b07856108cc60016108d1856108cc8e604001518b601c8110610afb57610afb615b71565b6020020151600161262d565b9450505050806001019050610a2b565b50610b2061528f565b5f5b6029811015610b70576101c0870151610b3c600183615b37565b602a8110610b4c57610b4c615b71565b6020020151828260298110610b6357610b63615b71565b6020020152600101610b22565b505f610b8582875f0151886020015186612826565b9050600160025b7f0000000000000000000000000000000000000000000000000000000000000000811015610be257610bd882896080015183601c8110610bce57610bce615b71565b60200201516125f9565b9150600101610b8c565b50610c08610bf5836108cc60018561262d565b6108d18a6101a001518a606001516125f9565b94909414979650505050505050565b5f610c206152ae565b5f610c4f8460c001517f000000000000000000000000000000000000000000000000000000000000000061289f565b90505f7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03811115610c8a57610c8a61593e565b604051908082528060200260200182016040528015610cb3578160200160208202803683370190505b5090505f7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03811115610cef57610cef61593e565b604051908082528060200260200182016040528015610d2857816020015b610d1561531e565b815260200190600190039081610d0d5790505b509050610d5b610d56876101000151855f81518110610d4957610d49615b71565b602002602001015161262d565b612968565b84610120018181525050610d90610d56876101000151855f81518110610d8357610d83615b71565b6020026020010151612614565b610140850181905261012085015160e0880151610db1926108d191906125f9565b845260c0860151610de190610dc590612968565b6108cc8661012001516108ef8a60e001518961014001516125f9565b6020850152815160019083905f90610dfb57610dfb615b71565b602002602001018181525050876102400151815f81518110610e1f57610e1f615b71565b6020908102919091010152600160a08501525f60c08501528351610e42906129d8565b60408501526020840151610e55906129d8565b606085015260015b610e6960016024615b37565b8111610f0a57610e8185604001518660a001516125f9565b838281518110610e9357610e93615b71565b602002602001018181525050610edc8560c001516108d18b6101c00151600185610ebd91906155de565b602a8110610ecd57610ecd615b71565b60200201518860a001516125f9565b60c086015260a08086015190880151610ef591906125f9565b60a0860152610f0381615b85565b9050610e5d565b505f5b6005811015610fe3575f610f22601e83615b37565b90505f610f3160016024615b37565b610f3b9084615b37565b9050610f6c858381518110610f5257610f52615b71565b60200260200101516108d189606001518a60a001516125f9565b858381518110610f7e57610f7e615b71565b602002602001018181525050610fbb8760c001516108d18d6101c0015184602a8110610fac57610fac615b71565b60200201518a60a001516125f9565b60c088015260a080880151908a0151610fd491906125f9565b60a08801525050600101610f0d565b50876020015181600181518110610ffc57610ffc615b71565b602002602001018190525086606001518160028151811061101f5761101f615b71565b602002602001018190525086608001518160038151811061104257611042615b71565b60200260200101819052508660a001518160048151811061106557611065615b71565b60200260200101819052508660c001518160058151811061108857611088615b71565b60200260200101819052508660e00151816006815181106110ab576110ab615b71565b6020026020010181905250866101000151816007815181106110cf576110cf615b71565b6020026020010181905250866101200151816008815181106110f3576110f3615b71565b60200260200101819052508661014001518160098151811061111757611117615b71565b602002602001018190525086610160015181600a8151811061113b5761113b615b71565b6020026020010181905250866101c0015181600b8151811061115f5761115f615b71565b602002602001018190525086610180015181600c8151811061118357611183615b71565b6020026020010181905250866101a0015181600d815181106111a7576111a7615b71565b6020026020010181905250866101e0015181600e815181106111cb576111cb615b71565b602002602001018190525086610200015181600f815181106111ef576111ef615b71565b60200260200101819052508661022001518160108151811061121357611213615b71565b60200260200101819052508661024001518160118151811061123757611237615b71565b60200260200101819052508661026001518160128151811061125b5761125b615b71565b60200260200101819052508661028001518160138151811061127f5761127f615b71565b6020026020010181905250866102a00151816014815181106112a3576112a3615b71565b6020026020010181905250866102c00151816015815181106112c7576112c7615b71565b6020026020010181905250866102e00151816016815181106112eb576112eb615b71565b60200260200101819052508661030001518160178151811061130f5761130f615b71565b60200260200101819052508661032001518160188151811061133357611333615b71565b60200260200101819052508661034001518160198151811061135757611357615b71565b602002602001018190525086610360015181601a8151811061137b5761137b615b71565b602002602001018190525086610380015181601b8151811061139f5761139f615b71565b6020026020010181905250866103a0015181601c815181106113c3576113c3615b71565b6020026020010181905250866103c0015181601d815181106113e7576113e7615b71565b6020026020010181905250876040015181601e8151811061140a5761140a615b71565b6020026020010181905250876060015181601f8151811061142d5761142d615b71565b602002602001018190525087608001518160208151811061145057611450615b71565b60200260200101819052508760a001518160218151811061147357611473615b71565b60200260200101819052508761012001518160228151811061149757611497615b71565b6020026020010181905250876101000151816023815181106114bb576114bb615b71565b60200260200101819052508760c00151816024815181106114de576114de615b71565b60200260200101819052508760e001518160258151811061150157611501615b71565b60200260200101819052505f61154787608001518660c001518b6102000151877f00000000000000000000000000000000000000000000000000000000000000006129ea565b9050611571815f8151811061155e5761155e615b71565b60200260200101518661012001516125f9565b608086018190526102008a01515160e08901516115a292916108d19161159791906125f9565b8861014001516125f9565b608086015260e08701516115b590612b48565b60a08601525f6115c760016024615b37565b6115d2906001615b37565b90505f5b61160160017f00000000000000000000000000000000000000000000000000000000000000006155de565b811015611812575f61163460017f00000000000000000000000000000000000000000000000000000000000000006155de565b8210159050806117a357611668610d568b6101000151898560016116589190615b37565b81518110610d4957610d49615b71565b6101208901526101008a015161169890610d569089611688866001615b37565b81518110610d8357610d83615b71565b61014089015260a08801516101208901516116b391906125f9565b61016089015260a088015160e08b01516116db916116d0916125f9565b8961014001516125f9565b61018089018190526116fe906116f0906129d8565b6108d18a61016001516129d8565b866117098486615b37565b8151811061171957611719615b71565b6020026020010181815250505f6117528961018001518e61020001518560016117429190615b37565b601c8110610bce57610bce615b71565b905061178c816108d18b61016001518887600161176f9190615b37565b8151811061177f5761177f615b71565b60200260200101516125f9565b905061179c896080015182612614565b60808a0152505b6117c26117b88960a001518c60e001516125f9565b8b60e001516125f9565b60a08901526101e08c015182601b81106117de576117de615b71565b6020020151856117ee8486615b37565b815181106117fe576117fe615b71565b6020908102919091010152506001016115d6565b5061183e60017f00000000000000000000000000000000000000000000000000000000000000006155de565b6118489082615b37565b90506118686118608961010001518a60c0015161262d565b60019061266a565b60e08701515261010088015160c08901516118ac91611860916108ef907f07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76906125f9565b60e0878101805160200192909252815180516040909101529051805160609091015260a0870151908901516118ee916118e4916125f9565b8960e001516125f9565b60a08701525f5b600481101561199c575f6119278860e00151836004811061191857611918615b71565b60200201518960a001516125f9565b9050611932816129d8565b886101000151836004811061194957611949615b71565b602002015260a088015160e08b015161196291906125f9565b8860a001818152505061198e88608001516108d1838f61022001518660048110610bce57610bce615b71565b6080890152506001016118f5565b506101008601515184518590839081106119b8576119b8615b71565b602090810291909101810191909152610100870151908101516119dc916002610a7f565b846119e8836001615b37565b815181106119f8576119f8615b71565b60209081029190910101526101008601516060015184611a19836002615b37565b81518110611a2957611a29615b71565b60209081029190910101525f5b6003811015611a8b578a61014001518160038110611a5657611a56615b71565b60200201518483611a6681615b85565b945081518110611a7857611a78615b71565b6020908102919091010152600101611a36565b506040518060400160405280600181526020016002815250838281518110611ab557611ab5615b71565b60200260200101819052508560800151848280611ad190615b85565b935081518110611ae357611ae3615b71565b602002602001018181525050611b0d8a61022001518960c001518a608001518d6101a00151612b53565b611b2a5760405163a2a2ac8360e01b815260040160405180910390fd5b5f8a6102600151905080848381518110611b4657611b46615b71565b6020026020010181905250886101000151858381518110611b6957611b69615b71565b602002602001018181525050611b7d615336565b611b878587612eb6565b8152611b9282612fae565b602082018190528c5182515f92611baa929190612ff4565b90505f5f611bba8f5f0151613085565b91509150611bc782613146565b611bd081613146565b8351611bdd9083856131e5565b84526020840151611bef9082856131e5565b602085018190528451611c0191613214565b9f9e505050505050505050505050505050565b611c1c615022565b50604080516103e0810182526120008152600d602080830191909152601182840152825180840184527f027302a9f1897167f79b74a71dd1558e3a69cefa96c98e94c7c0f766bd1d2bcb81527f2515389a7ef0546fe7c56260129132c12a8eef371c3f22786af7f8d6ab7c60e5818301526060830152825180840184527f22462de1db5e1340e5bf6aec9c11bf7e7a6cc0c35f29f457684c6eec7cd5c5c681527f01dfc98a0c382ff395ae0236bf97e801452533f3caa87c7dced9f68bdc1ed210818301526080830152825180840184527f11e0f52b055371aea42a1b22947e7e9eee7e791ab9e947053f46e2782391370c81527f0f3c94cb00fedd2902b259d6cdc6105e578a7b5ddf1f0c153fd061deec17d12a8183015260a0830152825180840184527f0a106232b6d74de2311faac4c886951df58d1e19b22f33523b239ab765e164bc81527f07f5dfd7a9464341586ffe3f0d977ba991e3d5c988c7c0ea2b645b6c11dee2d98183015260c0830152825180840184527f041ff6d6168cd52a5201246e3da52e0cb260a2a5fa567e7bff13c5e3c744980781527f11b370611ff9c70c66e2da51cc8ffb647e7cf40c85762d7bfe64e2f18a6a0f4f8183015260e0830152825180840184527f12aa823df85fb765f0155977a70179b7aaf9aeac1155d778de46cb37f8faf2fa81527f2c3c8f16c5811640a0c3c503fe4918cf16de1383fbb359a2c063ace62d0189ef81830152610100830152825180840184527f0c4032c3079594eb75a8449d3d5ce8bc3661650d53f9b24d923d8f404cb0bbc981527f1084d709650356d40f0158fd6da81f54eb5fe796a0ca89441369b7c24301f85181830152610120830152825180840184527f0fb1ece2260000daef835e11c53b92f3816bbf66b6ebb43e364190027b77fe1081527f1893ea57a05c765b064019fd0abd1076edabf46d1e12c80d10a82cbeb3b59b3481830152610140830152825180840184527f2f2778e46cce7a1f87dde2bde498d36f7683f5671f1022e182515e191582f4f181527f0ddf6aec7c456c38be13fba3151ee3bae583c12438091bb70c403a22cfd8a8d781830152610160830152825180840184527f2f48eb759c28a68804dc0a9ca56b663e8a0d34e8cff4fca0dcf11d588a89738d81527f12bdd73bf2a510e729182fa7a97453d2819406c6d74d9cbc32b18a701f93026981830152610180830152825180840184527f08e14ddb419c267be8d5531e70141db8b637b9384198fe2da596908356b46a0181527f0bc9394fcfbb5cce0c123f14fea5469404e5b3c49766fa5484cd9bdcfddf2a49818301526101a0830152825180840184527f0e5dcd1acfe7ef2e6b46edb70fc2067187a1865939739730b14b54bb73a8bb1d81527f225366ddb76dc74766b564e123631cf98a813b7111f430502caa1070f42e8c89818301526101c0830152825180840184527f0d17a9a4387495fb2b691b245aa6f13e25d7af5b468c2503fd6ee7ffdb819eb281527f143b6e0733448ea59aeb2ef50e1b5e757a6720689ae9eb46ba1297d566da5651818301526101e0830152825180840184527f0ad0ed837fdb4a9a131b192a6184105210ce233ad85e80fc8f1ca7dfc028433081527f029148c0965a4ffa8b6884fc10677316afd2cd7d794a41124d197b5900a6552481830152610200830152825180840184527f07aa26298594e9aa3ed2e0cf1c372108f142acca0d94f0e3280ad8f6f7fea6f281527f045e7e9f923fcf98cbc38bfec20a81bc03e34588928a034a3d6cb9e1f7e9c22f81830152610220830152825180840184527f168740345edd76afa07da3ca2d75150bed7a67b12c47c528ee3ecdd7223ec92981527f1f211045a0fd5872f1c69ea0c8b2bf7a255743111fa34cf46cefd4900cd6f77a81830152610240830152825180840184527f2692234e86e35b65b0b2ca616655f2387f396f041d826cde54bd3133cfe00a1081527f26d963bc3e72700b141f37e9771cbf98e59674139d2ed5e581f6465c2edb85a081830152610260830152825180840184527e5cbcf3a063e8f7142c2a0c6499c584486f3c8c90453fb4d387e5fcd9fd298581527f07de59eeee14a344c8b030a6a6d12650a6c37706700bb1bf55b5595ce910b79981830152610280830152825180840184527f0769795fa9f0d34097d7299603c36221e3bf395161fc91a78f481804abf7e17081527f190150a49e86cb7cf15c464f3f741c800d9a9d944953023fe79a5be579ac274c818301526102a0830152825180840184527f08129f3cdaaafc492279ea99f91473af386bfbf92c55fd2a5efb9d3ad39c834181527f0e02894a268b25478a7e323f900b1c81e63227533d55a9269b6e083344ddc5c3818301526102c0830152825180840184527f223ba058ba70d89680a3236d02e81dea1fd139b9f9ec0074605a6073059b9e7d81527f04d18416fa482ce0c6eece742967f0862202d508046a5141f5fa43fd8f75fc6b818301526102e0830152825180840184527f25f874dc5eb164f0a82bd3c3f189e02089314799c7a555838569fe8499c30fe981527f256b5957d823a2daf400f685cd16a42182563f32442982d6236b359d4ee7809a81830152610300830152825180840184527f099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d2681527e15b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f81830152610320830152825180840184527f1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e81527f305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d1981830152610340830152825180840184527f061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d81527f1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e81830152610360830152825180840184527f043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce81527f261522c4089330646aff96736194949330952ae74c573d1686d9cb4a007338548183015261038083015282518084018452600181526002818301526103a083015282518084019093527f1104b2598e170ee71593833e2ea491147e60225ba814c1f8cd3ad557de0a98ee83527f2741fcea4e36b97464e7174e83dceaa89b2f2a49a3b4352193c11737f79d39a2908301526103c081019190915290565b5f6103f461255a8385615b9d565b612655565b61256761531e565b60408051808201909152805f516020615c6c5f395f51905f5261258d60205f8789615b4a565b61259691615b9d565b6125a09190615bba565b81526020908101905f516020615c6c5f395f51905f52906125c5906040908789615b4a565b6125ce91615b9d565b6125d89190615bba565b90529392505050565b5f5f516020615c8c5f395f51905f52825b0692915050565b5f5f516020615c8c5f395f51905f5282840990505b92915050565b5f5f516020615c8c5f395f51905f528284089392505050565b5f5f516020615c8c5f395f51905f52825f516020615c8c5f395f51905f520384089392505050565b5f5f516020615c8c5f395f51905f52826125f2565b5f6103f4836108cc84612968565b5f5f604051806101200160405280619d8081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec5181526020016105a081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31815260200161024081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd3181526020016105a081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec518152602001619d8081525090505f600190505f5f90505b600981101561277157612767826108cc878461262d565b9150600101612750565b5061277a615349565b5f5b60098110156127cb576127ac610d5685836009811061279d5761279d615b71565b60200201516108cc898561262d565b8282600981106127be576127be615b71565b602002015260010161277c565b505f5b600981101561281157612807856108d18984600981106127f0576127f0615b71565b6020020151858560098110610bce57610bce615b71565b94506001016127ce565b5061281c84836125f9565b9695505050505050565b5f61282f615368565b61283a868285613408565b612846868683866135b4565b612852868683866137a0565b61285d8682856139c5565b612868868285613bb9565b61287486868386613f02565b61287f8682856143ae565b61288a8682856147c0565b612895868285614b81565b61281c8185614e81565b60605f826001600160401b038111156128ba576128ba61593e565b6040519080825280602002602001820160405280156128e3578160200160208202803683370190505b50905083815f815181106128f9576128f9615b71565b602090810291909101015260015b838110156129605761293b8261291e6001846155de565b8151811061292e5761292e615b71565b6020026020010151612b48565b82828151811061294d5761294d615b71565b6020908102919091010152600101612907565b509392505050565b5f5f8290505f604051602081526020808201526020604082015282606082015260025f516020615c8c5f395f51905f520360808201525f516020615c8c5f395f51905f5260a082015260205f60c08360055afa806129c4575f5ffd5b505f51608091909101604052949350505050565b5f516020615c8c5f395f51905f520390565b60605f826001600160401b03811115612a0557612a0561593e565b604051908082528060200260200182016040528015612a2e578160200160208202803683370190505b509050825b8015612b3d575f85612a466001846155de565b81518110612a5657612a56615b71565b602002602001015190505f89600184612a6f91906155de565b601c8110612a7f57612a7f615b71565b602002015190505f612ada612a9e612a97858d6125f9565b60026125f9565b6108ef8b612aad6001896155de565b601c8110612abd57612abd615b71565b60200201516108cc612ad4886108cc60018a61262d565b8761262d565b9050612afb816108cc610d56612af5876108cc60018961262d565b86612614565b99508990508085612b0d6001876155de565b81518110612b1d57612b1d615b71565b60200260200101818152505050505080612b3690615bd9565b9050612a33565b509695505050505050565b5f61260e82836125f9565b5f600181612b6c612b6687610100614eda565b8361262d565b905080612b8c5760405163835eb8f760e01b815260040160405180910390fd5b612b94615387565b80518390525f5b7f0000000000000000000000000000000000000000000000000000000000000000811015612c7d575f612bcf8260096155c7565b612bda906001615b37565b905084835f0151826101008110612bf357612bf3615b71565b60200201525f612c04826001615b37565b90505b612c12600983615b37565b811015612c73578351612c5290612c2a6001846155de565b6101008110612c3b57612c3b615b71565b60200201518a85601c8110610bce57610bce615b71565b8451826101008110612c6657612c66615b71565b6020020152600101612c07565b5050600101612b9b565b50608081018390525f602082018190525b610100811015612d8e57612caf612ca983608001518a6125f9565b8561262d565b8260a00151826101008110612cc657612cc6615b71565b602002015260a0820151612cf090826101008110612ce657612ce6615b71565b6020020151612968565b8260a00151826101008110612d0757612d07615b71565b602002018181525050612d4d82602001516108d1845f0151846101008110612d3157612d31615b71565b60200201518560a00151856101008110610bce57610bce615b71565b60208301526080820151612d81907f204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d66125f9565b6080830152600101612c8e565b505f612d9f836108cc610100612968565b9050612daf8260200151826125f9565b602083015260a0820151612dca905f5b6020020151826125f9565b604083015260a0820151612df690612de560016101006155de565b6101008110612dbf57612dbf615b71565b60608301526040820151612e0c908a6002610bce565b60c08301819052612e6c906108d1612e448b7f204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d661262d565b60208d015160408e01516108cc91612e5b9161262d565b8e5160208901516108ef91906125f9565b60c083018190526060830151612ea191612e95916108d1906108cc8e600260200201518c61262d565b6108ef858c6003610bce565b60c08301819052159998505050505050505050565b612ebe61531e565b7f00000000000000000000000000000000000000000000000000000000000000005f5b81811015612f1357612f0b858281518110612efe57612efe615b71565b6020026020010151613146565b600101612ee1565b50604051600190815b60018401811015612f785760208102870160208202870181515160408501528151602001516060850152805160808501525050604080830160606040850160075afa8316925060408260808460065afa90921691600101612f1c565b5080518452602081015160208501525080612fa6576040516352ec174560e11b815260040160405180910390fd5b505092915050565b612fb661531e565b5f516020615c6c5f395f51905f5282602001515f516020615c6c5f395f51905f52612fe191906155de565b612feb9190615bba565b60208301525090565b5f5f5f61300086613085565b9150915061300c6153cb565b82518152602080840151818301528251604080840191909152838201516060840152875160808401528782015160a0840152865160c08401528682015160e08401525161307a9161305f91849101615bee565b60405160208183030381529060405280519060200120612655565b979650505050505050565b61308d61531e565b61309561531e565b82516020808501516040860151606087015160cc90811b608892831b604494851b90961795909517949094178652608087015160a088015160c089015160e08a0151871b90841b91851b9092171717868401526101008701516101208801516101408901516101608a0151871b90841b91851b909217171785526101808701516101a08801516101c08901516101e09099015190951b9790911b9390911b1791909117939093179281019290925291565b805160208201515f5f516020615c6c5f395f51905f528380095f516020615c6c5f395f51905f5260035f516020615c6c5f395f51905f52838709085f516020615c6c5f395f51905f5284850914915050806131df5760405162461bcd60e51b8152602060048201526019602482015278706f696e74206973206e6f74206f6e2074686520637572766560381b60448201526064016100e5565b50505050565b6131ed61531e565b6131f561531e565b6131ff8386614f3b565b905061320b8185614f91565b95945050505050565b81516020808401518351848301516040805194850195909552938301919091527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260608301527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed60808301527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b60a08301527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60c083015260e08201526101008101919091527f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c16101208201527f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b06101408201527f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe46101608201527f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e556101808201525f9081906101a00160405160208183030381529060405290505f5f60086001600160a01b0316836040516133aa9190615c22565b5f60405180830381855afa9150503d805f81146133e2576040519150601f19603f3d011682016040523d82523d5f602084013e6133e7565b606091505b509150915081801561281c57508080602001905181019061281c9190615c38565b5f613414846007614ff2565b90507f183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f80000005f61347561346f61344985600361262d565b6108cc6134646134598b5f614ff2565b6108cc8c601d614ff2565b6108cc8b601c614ff2565b836125f9565b90506134f66134eb6134d26134b96134a0856108d16134958d6002614ff2565b6108cc8e601c614ff2565b6108d16134ae8c6003614ff2565b6108cc8d601d614ff2565b6108d16134c78b6004614ff2565b6108cc8c601e614ff2565b6108d16134e08a6005614ff2565b6108cc8b601f614ff2565b6108d1886001614ff2565b9050613515816108d161350a86600161262d565b6108cc8a6027614ff2565b905061352181846125f9565b905061352d81856125f9565b8552505f905061356761355d61355261354788601c614ff2565b6108d189601f614ff2565b6108ef886024614ff2565b6108d1875f614ff2565b9050613578816108cc84600261262d565b9050613589816108cc84600161262d565b905061359581836125f9565b90506135a181846125f9565b9050808460015b60200201525050505050565b5f5f5f6135ea6135e06135c889601c614ff2565b6108d16135d68b6012614ff2565b8a606001516125f9565b8760800151612614565b9050613623816108cc6136196136018b601d614ff2565b6108d161360f8d6013614ff2565b8c606001516125f9565b8960800151612614565b9050613648816108cc61361961363a8b601e614ff2565b6108d161360f8d6014614ff2565b905061366d816108cc61361961365f8b601f614ff2565b6108d161360f8d6015614ff2565b92505f90506136916135e061368389601c614ff2565b6108d16135d68b600e614ff2565b90506136b6816108cc6136196136a88b601d614ff2565b6108d161360f8d600f614ff2565b90506136db816108cc6136196136cd8b601e614ff2565b6108d161360f8d6010614ff2565b9050613700816108cc6136196136f28b601f614ff2565b6108d161360f8d6011614ff2565b91505f9050613727613721613716896020614ff2565b6108d18a601a614ff2565b846125f9565b905061375c816108ef61375661373e8b6028614ff2565b6108d161374c8d601b614ff2565b8c60a001516125f9565b856125f9565b905061376881856125f9565b6040860152505f61378b61375661378089601b614ff2565b6108cc8a6028614ff2565b9050808560035b602002015250505050505050565b5f5f6137fe6137e66137ce6137b96136198a6016614ff2565b6108d16137c78b6017614ff2565b8a516125f9565b6108d16137dc8a6018614ff2565b89602001516125f9565b6108d16137f4896019614ff2565b88604001516125f9565b91505f61383561381c61381289601c614ff2565b8860800151612614565b6108d161382a8a6003614ff2565b6108cc8b6024614ff2565b90505f61385e61384689601d614ff2565b6108d16138538b5f614ff2565b6108cc8c6025614ff2565b90505f61388861386f8a601e614ff2565b6108d161387d8c6001614ff2565b6108cc8d6026614ff2565b90506138c76138af6138a1856108d1868d5f01516125f9565b6108d1848c602001516125f9565b6108d16138bd8c6004614ff2565b8b604001516125f9565b93505050505f6138db613721886021614ff2565b90505f6138ec613721896021614ff2565b90505f61392461390b6139008b6023614ff2565b6108d18c6006614ff2565b6108ef6139198c6023614ff2565b6108cc8d6006614ff2565b90505f613942612b6661393787896125f9565b6108cc8d6021614ff2565b905061394e81886125f9565b90505f6139766139686139628d6006614ff2565b876125f9565b6108ef6139628e6022614ff2565b90505f6139848c6023614ff2565b90505f613994612b6683846125f9565b60808c0185905260a08c0184905290506139ae818b6125f9565b8b6006602002015250505050505050505050505050565b5f6139d15f600161262d565b90505f6139df5f600261262d565b90505f6139ed5f600361262d565b90505f613a096139fe88601d614ff2565b6108ef89601c614ff2565b90505f613a25613a1a89601e614ff2565b6108ef8a601d614ff2565b90505f613a41613a368a601f614ff2565b6108ef8b601e614ff2565b90505f613a5d613a528b6024614ff2565b6108ef8c601f614ff2565b905083613a6e816108cc818b612614565b9050613a7e816108cc878a612614565b9050613a8e816108cc8789612614565b9050613a9f816108cc8d6008614ff2565b9050613aab818a6125f9565b60e08b01525082613ac0816108cc818b612614565b9050613ad0816108cc868a612614565b9050613ae0816108cc8689612614565b9050613af1816108cc8d6008614ff2565b9050613afd818a6125f9565b6101008b01525081613b13816108cc818b612614565b9050613b23816108cc858a612614565b9050613b33816108cc8589612614565b9050613b44816108cc8d6008614ff2565b9050613b50818a6125f9565b6101208b01525080613b66816108cc818b612614565b9050613b76816108cc848a612614565b9050613b86816108cc8489612614565b9050613b97816108cc8d6008614ff2565b9050613ba3818a6125f9565b610140909a019990995250505050505050505050565b613bf26040518060e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b613bfd84601d614ff2565b8152613c0a84601e614ff2565b6020820152613c1a846024614ff2565b6040820152613c2a846027614ff2565b6060820152613c3a846026614ff2565b6080820152613c4a846025614ff2565b60a08201525f613c5b856002614ff2565b90505f613c68865f614ff2565b90505f613c7c8460400151855f015161262d565b90505f613c91856020015186602001516125f9565b606086015190915086905f90613ca790806125f9565b90505f613cc5613cbf89602001518a606001516125f9565b886125f9565b90505f613ce4613cdd8a60a001518b60400151612614565b8a51612614565b9050613cf361396282886125f9565b9050613d1a613d14613d0e613d08848761262d565b8861262d565b84612614565b83612614565b9050613d42613d37613d2c83876125f9565b6108cc8f6009614ff2565b6108cc60018a61262d565b6101608c015250505050602085015160808601515f91613d6191612614565b90505f613d7f613d758860600151886125f9565b886020015161262d565b90505f613da3613d8f84876125f9565b6108d16137568b60a001518c5f015161262d565b9050613dcb613dc0613db5838c6125f9565b6108cc8e6009614ff2565b6108cc60018961262d565b6101808b0152505f9150613dec9050613de5836011612614565b87516125f9565b90505f613df98384612614565b9050613e058182612614565b90505f613e138360096125f9565b9050613e3c613e36613721613e2f8b60a001518c5f0151612614565b8b51612614565b8261262d565b60c089018190525f90613e5790613cbf90613d2c908d6125f9565b9050613e6a8b600b602002015182612614565b6101608c0152505086515f9250613e919150613de590613e8a9080612614565b8851612614565b90505f613ed1613eac836108cc8a5f01518b60a0015161262d565b60208901516108ef90613ebf9080612614565b6108cc8b602001518c60800151612614565b9050613eee89600c60200201516108d1613cbf613d2c858d6125f9565b89600c602002015250505050505050505050565b613f6c604051806101e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b613f84613f7a86601e614ff2565b85604001516125f9565b808252613fa3906108d1613f9988601d614ff2565b87602001516125f9565b808252613fb8906108d1613de588601c614ff2565b808252613fca906108d1876001614ff2565b80825260208201819052613fe3906108ef87601f614ff2565b8152613ffe613ff3866024614ff2565b6108ef87601c614ff2565b608082015261401c614011866027614ff2565b6108ef87601f614ff2565b60608201526080810151614035906108cc81600161262d565b6101c082015260808101516140759061406b90614064906108cc60015f516020615c8c5f395f51905f526155de565b6001612614565b82606001516125f9565b60a082018190526140ab9061409d906108cc614092896002614ff2565b6108cc8a6003614ff2565b6108cc61375688600a614ff2565b83600e60200201526101c08101516140cf9061409d906108cc614092896002614ff2565b6101e084015280516140f4906108cc6140e9886002614ff2565b6108cc896003614ff2565b6101208201525f61411361410987601f614ff2565b836020015161262d565b9050614124816108cc83600161262d565b60e0830152614141614137876026614ff2565b86604001516125f9565b60408301819052614164906108d161415a896025614ff2565b88602001516125f9565b60408301819052614184906108d161417d896024614ff2565b88516125f9565b60408301526141a1614197876027614ff2565b836040015161262d565b60408301525f6141c06141b5886026614ff2565b6108ef89601e614ff2565b90506142126141ed61346f614064866080015160015f516020615c8c5f395f51905f526108cc91906155de565b6108cc614064866040015160015f516020615c8c5f395f51905f526108cc91906155de565b60c084015260408301516142349061422a90806125f9565b846040015161262d565b61010084015260c083015161426090614252906108cc8a6004614ff2565b6108cc6139628a600a614ff2565b6102008601526101c083015161427f90614252906108cc8a6004614ff2565b61022086015261010083015161429e90614252906108cc8a6004614ff2565b61024086015260e08301516142b8906108cc896004614ff2565b6101408401526142d76142cc886025614ff2565b6108ef89601d614ff2565b6101608401526080830151614315906141b59061430a90614064906108cc60015f516020615c8c5f395f51905f526155de565b8561016001516125f9565b61018084018190526101208401516101a0850181905261434c916108d1906108cc6143418c6005614ff2565b6108cc8d6002614ff2565b6101a08401819052835161436c91906108d1906108cc6143418c5f614ff2565b6101a084018190526101408401516143849190612614565b6101a0840181905261439e906108cc6139628a600a614ff2565b6101a0840181905285600d613792565b6143e76040518060e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b61441c6144036143f886601c614ff2565b6108cc876025614ff2565b6108d1614411876024614ff2565b6108cc88601d614ff2565b815261446161445661443d61443287601c614ff2565b6108cc88601f614ff2565b6108d161444b88601d614ff2565b6108cc89601e614ff2565b6108ef866026614ff2565b6040820181905261447690600160441b6125f9565b6040820181905261448c906108ef866027614ff2565b60408201819052815161449f9190612614565b604082018190526144b5906108cc866005614ff2565b604082015280516144ca90600160441b6125f9565b8082526144ea906108d16144df876024614ff2565b6108cc886025614ff2565b80825260208201819052614511906108ef61450687601e614ff2565b6108d188601f614ff2565b60208201819052614527906108cc866004614ff2565b6020820152805160608201819052614544906108d186601f614ff2565b60608201819052614568906108ef61455d876026614ff2565b6108d1886027614ff2565b6060820181905261457d906108cc865f614ff2565b8160600181815250505f6145a661459c83602001518460400151612614565b8360600151612614565b90506145b7816108cc876003614ff2565b90506145cf6145c7866025614ff2565b6140006125f9565b608083018190526145e5906108d1876024614ff2565b608083018190526145f8906140006125f9565b6080830181905261460e906108d187601e614ff2565b60808301819052614621906140006125f9565b60808301819052614637906108d187601d614ff2565b6080830181905261464a906140006125f9565b60808301819052614660906108d187601c614ff2565b60808301819052614676906108ef87601f614ff2565b6080830181905261468c906108cc876005614ff2565b608083015261469f6145c7866026614ff2565b60a083018190526146b5906108d1876025614ff2565b60a083018190526146c8906140006125f9565b60a083018190526146de906108d1876024614ff2565b60a083018190526146f1906140006125f9565b60a08301819052614707906108d187601f614ff2565b60a0830181905261471a906140006125f9565b60a08301819052614730906108d187601e614ff2565b60a08301819052614746906108ef876027614ff2565b60a0830181905261475b906108cc875f614ff2565b60a0830181905260808301515f916147739190612614565b9050614784816108cc886004614ff2565b90506147908282612614565b60c084018190526147a9906108cc61396289600b614ff2565b60c084018190528560136020020152505050505050565b6148366040518061022001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b61484f61484485601c614ff2565b6108d1866002614ff2565b815261486a61485f85601d614ff2565b6108d1866003614ff2565b602082015261488861487d85601e614ff2565b6108d1866004614ff2565b60408201526148a661489b85601f614ff2565b6108d1866005614ff2565b606082015280516148d8906148d1906148ca906148c390806125f9565b84516125f9565b83516125f9565b82516125f9565b608082015260208101516149169061490c90614902906148f890806125f9565b84602001516125f9565b83602001516125f9565b82602001516125f9565b60a082015260408101516149549061494a906149409061493690806125f9565b84604001516125f9565b83604001516125f9565b82604001516125f9565b60c082015260608101516149889061406b9061497e9061497490806125f9565b84606001516125f9565b83606001516125f9565b60e0820152608081015160a08201516149a19190612614565b61010082015260c081015160e08201516149bb9190612614565b61012082015260a08101516149df906149d49080612614565b826101200151612614565b61014082015260e0810151614a03906149f89080612614565b826101000151612614565b610160820152610120810151614a199080612614565b6101e08201819052614a3a90614a2f9080612614565b826101600151612614565b6101e0820152610100810151614a509080612614565b6101a08201819052614a7190614a669080612614565b826101400151612614565b6101a08201819052610160820151614a8891612614565b6101808201526101408101516101e0820151614aa49190612614565b6101c0820152614ab861346f85600c614ff2565b6102008201819052610280840151610180830151614ae3926108d1916108cc906108ef8a6024614ff2565b8360146020020152614b1383601560200201516108d18361020001516108cc856101a001516108ef8a6025614ff2565b8360156020020152614b4383601660200201516108d18361020001516108cc856101c001516108ef8a6026614ff2565b8360166020020152614b7383601760200201516108d18361020001516108cc856101e001516108ef8a6027614ff2565b836017602002015250505050565b614bd36040518061016001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b5f6040518060800160405280614c087f10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e76125e1565b8152602001614c367f0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b6125e1565b8152602001614c637e544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac156125e1565b8152602001614c917f222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b6125e1565b90529050614cae614ca386601c614ff2565b6108d1876002614ff2565b6101208301819052614ced90614ce290614cd790614ccc90806125f9565b8561012001516125f9565b8461012001516125f9565b8361012001516125f9565b8252614cfa85601d614ff2565b6020830152614d0a85601e614ff2565b6040830152614d1a85601f614ff2565b606083015281516020830151614d429161459c91614d389190612614565b8460400151612614565b6080830152614d5561372186600d614ff2565b6101408301528151614d7690614d6c90835f610bce565b8360800151612614565b60a0830152614da284601860200201516108d18461014001516108cc8660a001516108ef8b6024614ff2565b6103008501526020820151614dbd90614d6c90836001610bce565b60c0830152614de984601960200201516108d18461014001516108cc8660c001516108ef8b6025614ff2565b6103208501526040820151614e0490614d6c90836002610bce565b60e0830152614e3084601a60200201516108d18461014001516108cc8660e001516108ef8b6026614ff2565b6103408501526060820151614e4b90614d6c90836003610bce565b610100830152614e7984601b60200201516108d18461014001516108cc8661010001516108ef8b6027614ff2565b84601b6135a8565b815160015b601c811015614ed357614ec9826108d18684601c8110614ea857614ea8615b71565b602002015186614eb96001876155de565b601b8110610bce57610bce615b71565b9150600101614e86565b5092915050565b5f5f8390505f60405160208152602080820152602060408201528260608201528460808201525f516020615c8c5f395f51905f5260a082015260205f60c08360055afa80614f26575f5ffd5b505f5160809190910160405295945050505050565b614f4361531e565b614f4b61531e565b604051835181526020840151602082015284604082015260408160608360075afa80614f75575f5ffd5b5080518252602080820151908301526060016040529392505050565b614f9961531e565b614fa161531e565b6040518451815260208501516020820152835160408201526020840151606082015260408160808360065afa80614fd6575f5ffd5b5080518252602080820151908301526080016040529392505050565b5f8282602881111561500657615006615c57565b6029811061501657615016615b71565b60200201519392505050565b604051806103e001604052805f81526020015f81526020015f815260200161504861531e565b815260200161505561531e565b815260200161506261531e565b815260200161506f61531e565b815260200161507c61531e565b815260200161508961531e565b815260200161509661531e565b81526020016150a361531e565b81526020016150b061531e565b81526020016150bd61531e565b81526020016150ca61531e565b81526020016150d761531e565b81526020016150e461531e565b81526020016150f161531e565b81526020016150fe61531e565b815260200161510b61531e565b815260200161511861531e565b815260200161512561531e565b815260200161513261531e565b815260200161513f61531e565b815260200161514c61531e565b815260200161515961531e565b815260200161516661531e565b815260200161517361531e565b815260200161518061531e565b815260200161518d61531e565b815260200161519a61531e565b81526020016151a761531e565b905290565b6040518061028001604052806151c06153ea565b81526020016151cd61531e565b81526020016151da61531e565b81526020016151e761531e565b81526020016151f461531e565b815260200161520161531e565b815260200161520e61531e565b815260200161521b61531e565b815260200161522861531e565b815260200161523561531e565b8152602001615242615409565b81526020015f8152602001615255615436565b81526020015f8152602001615268615464565b8152602001615275615483565b8152602001615282615368565b815260200161518d6154b1565b6040518061052001604052806029906020820280368337509192915050565b604051806101c001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020016152ec6154b1565b81526020016152f96154b1565b81526020015f81526020015f81526020015f81526020015f8152602001606081525090565b60405180604001604052805f81526020015f81525090565b604051806040016040528061519a61531e565b6040518061012001604052806009906020820280368337509192915050565b604051806103800160405280601c906020820280368337509192915050565b6040518060e0016040528061539a6154cf565b81526020015f81526020015f81526020015f81526020015f81526020016153bf6154cf565b81526020015f81525090565b6040518061010001604052806008906020820280368337509192915050565b6040518061020001604052806010906020820280368337509192915050565b60405180606001604052806003905b61542061531e565b8152602001906001900390816154185790505090565b604051806103800160405280601c905b61544e615349565b8152602001906001900390816154465790505090565b604051806105400160405280602a906020820280368337509192915050565b604051806103600160405280601b905b61549b61531e565b8152602001906001900390816154935790505090565b60405180608001604052806004906020820280368337509192915050565b604051806120000160405280610100906020820280368337509192915050565b5f5f5f5f60408587031215615502575f5ffd5b84356001600160401b03811115615517575f5ffd5b8501601f81018713615527575f5ffd5b80356001600160401b0381111561553c575f5ffd5b87602082840101111561554d575f5ffd5b6020918201955093508501356001600160401b0381111561556c575f5ffd5b8501601f8101871361557c575f5ffd5b80356001600160401b03811115615591575f5ffd5b8760208260051b84010111156155a5575f5ffd5b949793965060200194505050565b634e487b7160e01b5f52601160045260245ffd5b808202811582820484141761260e5761260e6155b3565b8181038181111561260e5761260e6155b3565b805f5b60108110156131df5781518452602093840193909101906001016155f4565b805f5b60038110156131df5761563484835180518252602090810151910152565b6040939093019260209190910190600101615616565b805f5b601c8110156131df578151845f5b600981101561567a57825182526020928301929091019060010161565b565b50505061012093909301926020919091019060010161564d565b805f5b602a8110156131df578151845260209384019390910190600101615697565b805f5b601b8110156131df576156d784835180518252602090810151910152565b60409390930192602091909101906001016156b9565b805f5b601c8110156131df5781518452602093840193909101906001016156f0565b805f5b60048110156131df578151845260209384019390910190600101615712565b8183525f6001600160fb1b03831115615748575f5ffd5b8260051b80836020870137939093016020019392505050565b61576c8188516155f1565b5f602088015161578a61020084018280518252602090810151910152565b5060408801518051610240840152602090810151610260840152606089015180516102808501528101516102a0840152608089015180516102c08501528101516102e084015260a0890151805161030085015281015161032084015260c0890151805161034085015281015161036084015260e089015180516103808501528101516103a084015261010089015180516103c08501528101516103e084015261012089015180516104008501520151610420830152610140880151615853610440840182615613565b5061016088015161050083015261018088015161587461052084018261564a565b506101a08801516124a08301526101c08801516158956124c0840182615694565b506101e08801516158aa612a008401826156b6565b506102008801516158bf6130c08401826156ed565b506102208801516158d461344084018261570f565b5061024088015180516134c08401526020908101516134e0840152610260890151805161350085015201516135208301526135c0613540830181905261591d9083018789615731565b613560830195909552506135808101929092526135a0909101529392505050565b634e487b7160e01b5f52604160045260245ffd5b60405161014081016001600160401b03811182821017156159755761597561593e565b60405290565b604051601f8201601f191681016001600160401b03811182821017156159a3576159a361593e565b604052919050565b5f60c082840312156159bb575f5ffd5b60405160c081016001600160401b03811182821017156159dd576159dd61593e565b604090815283518252602080850151908301528381015190820152606080840151908201526080808401519082015260a0928301519281019290925250919050565b5f82601f830112615a2e575f5ffd5b5f610360615a3b8161597b565b915083018185821115615a4c575f5ffd5b845b82811015615a66578051825260209182019101615a4e565b509195945050505050565b5f82601f830112615a80575f5ffd5b5f610380615a3b8161597b565b5f610be0828403128015615a9f575f5ffd5b50615aa8615952565b615ab284846159ab565b8152615ac18460c08501615a1f565b6020820152615ad4846104208501615a71565b60408201526107a08301516060820152615af2846107c08501615a71565b6080820152610b4083015160a0820152610b6083015160c0820152610b8083015160e0820152610ba0830151610100820152610bc09092015161012083015250919050565b8082018082111561260e5761260e6155b3565b5f5f85851115615b58575f5ffd5b83861115615b64575f5ffd5b5050820193919092039150565b634e487b7160e01b5f52603260045260245ffd5b5f60018201615b9657615b966155b3565b5060010190565b8035602083101561260e575f19602084900360031b1b1692915050565b5f82615bd457634e487b7160e01b5f52601260045260245ffd5b500690565b5f81615be757615be76155b3565b505f190190565b5f8183825b6008811015615c12578151835260209283019290910190600101615bf3565b5050506101008201905092915050565b5f82518060208501845e5f920191825250919050565b5f60208284031215615c48575f5ffd5b815180151581146103f4575f5ffd5b634e487b7160e01b5f52602160045260245ffdfe30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4730644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", - "linkReferences": { - "project/contracts/verifier/DkgPkVerifier.sol": { - "ZKTranscriptLib": [ - { - "length": 20, - "start": 688 - } - ] - } - }, - "deployedLinkReferences": { - "project/contracts/verifier/DkgPkVerifier.sol": { - "ZKTranscriptLib": [ - { - "length": 20, - "start": 360 - } - ] - } - }, - "immutableReferences": { - "33496": [ - { - "length": 32, - "start": 91 - }, - { - "length": 32, - "start": 148 - }, - { - "length": 32, - "start": 257 - }, - { - "length": 32, - "start": 466 - }, - { - "length": 32, - "start": 2605 - }, - { - "length": 32, - "start": 2958 - }, - { - "length": 32, - "start": 3115 - }, - { - "length": 32, - "start": 5411 - }, - { - "length": 32, - "start": 5597 - }, - { - "length": 32, - "start": 5648 - }, - { - "length": 32, - "start": 6170 - }, - { - "length": 32, - "start": 11165 - } - ], - "33498": [ - { - "length": 32, - "start": 398 - } - ], - "33500": [ - { - "length": 32, - "start": 432 - }, - { - "length": 32, - "start": 2303 - } - ], - "33502": [ - { - "length": 32, - "start": 3156 - }, - { - "length": 32, - "start": 3257 - }, - { - "length": 32, - "start": 11968 - } - ] - }, - "inputSourceName": "project/contracts/verifier/DkgPkVerifier.sol", - "buildInfoId": "solc-0_8_28-d48586b5c515ad1b112178eeef9419e7799821ba" -} \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/verifier/RecursiveAggregationFoldVerifier.sol/RecursiveAggregationFoldVerifier.json b/packages/enclave-contracts/artifacts/contracts/verifier/RecursiveAggregationFoldVerifier.sol/RecursiveAggregationFoldVerifier.json new file mode 100644 index 0000000000..6d4cc8a2ff --- /dev/null +++ b/packages/enclave-contracts/artifacts/contracts/verifier/RecursiveAggregationFoldVerifier.sol/RecursiveAggregationFoldVerifier.json @@ -0,0 +1,188 @@ +{ + "_format": "hh3-artifact-1", + "contractName": "RecursiveAggregationFoldVerifier", + "sourceName": "contracts/verifier/RecursiveAggregationFoldVerifier.sol", + "abi": [ + { + "inputs": [], + "name": "ConsistencyCheckFailed", + "type": "error" + }, + { + "inputs": [], + "name": "GeminiChallengeInSubgroup", + "type": "error" + }, + { + "inputs": [], + "name": "ProofLengthWrong", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "logN", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "actualLength", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expectedLength", + "type": "uint256" + } + ], + "name": "ProofLengthWrongWithLogN", + "type": "error" + }, + { + "inputs": [], + "name": "PublicInputsLengthWrong", + "type": "error" + }, + { + "inputs": [], + "name": "ShpleminiFailed", + "type": "error" + }, + { + "inputs": [], + "name": "SumcheckFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "proof", + "type": "bytes" + }, + { + "internalType": "bytes32[]", + "name": "publicInputs", + "type": "bytes32[]" + } + ], + "name": "verify", + "outputs": [ + { + "internalType": "bool", + "name": "verified", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x610120604052348015610010575f5ffd5b50622000006080819052601560a08190527f0c0bf44aa8d9785b43e16005da8fc455d330c7ff959979a9c7cc87670345385960c0819052601260e081905260038361005d6001602461008a565b610067919061008a565b610071919061008a565b61007c90600261008a565b61010052506100af92505050565b808201808211156100a957634e487b7160e01b5f52601160045260245ffd5b92915050565b60805160a05160c05160e05161010051615cba6101495f395f8181610c5401528181610cb90152612ec201525f81816101b001526108ff01525f61018e01525f8181605b01528181609401528181610101015281816101d201528181610a2d01528181610b8e01528181610c2b01528181611523015281816115dd015281816116100152818161181a0152612b9f01525f5050615cba5ff3fe608060405234801561000f575f5ffd5b5060043610610029575f3560e01c8063ea50d0e41461002d575b5f5ffd5b61004061003b3660046154f1565b610054565b604051901515815260200160405180910390f35b5f5f61007f7f00000000000000000000000000000000000000000000000000000000000000006102ee565b905061008c8160206155c9565b85146100ee577f0000000000000000000000000000000000000000000000000000000000000000856100bf8360206155c9565b6040516359895a5360e01b81526004810193909352602483019190915260448201526064015b60405180910390fd5b5f6100f76103fb565b90505f61012588887f0000000000000000000000000000000000000000000000000000000000000000610410565b90506010826040015161013891906155e0565b85146101575760405163fa06659360e01b815260040160405180910390fd5b60405163995bf45760e01b81525f9073__$f25d01d3eaf7d03206a1c3f23eb1654acf$__9063995bf457906101fa9085908b908b907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090600401615763565b610be060405180830381865af4158015610216573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061023a9190615a8f565b905061028a8787808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050855185516060810151608090910151919350915060016108a9565b815160a0015261029a8282610a10565b6102b7576040516313f8744360e31b815260040160405180910390fd5b6102c2828483610c17565b6102df576040516352ec174560e11b815260040160405180910390fd5b50600198975050505050505050565b5f8060026102fe60016008615b39565b61030891906155c9565b9050610316600260036155c9565b6103209082615b39565b9050600161032f6009856155c9565b61033991906155c9565b6103439082615b39565b90506001610352816029615b39565b61035c91906155c9565b6103669082615b39565b9050610374600160026155c9565b61037e9082615b39565b905061038b6001846155c9565b6103959082615b39565b90506103a3600160046155c9565b6103ad9082615b39565b905060026103bc6001856155e0565b6103c691906155c9565b6103d09082615b39565b90506103dd6002806155c9565b6103e79082615b39565b90506103f4601082615b39565b9392505050565b610403615024565b61040b611c14565b905090565b6104186151ae565b5f805b601081101561047a57610447868387610435602083615b39565b9261044293929190615b4c565b61254e565b8351826010811061045a5761045a615b73565b6020020181815250506020826104709190615b39565b915060010161041b565b5061049e85828661048c604083615b39565b9261049993929190615b4c565b612561565b60208301526104ae604082615b39565b90506104c185828661048c604083615b39565b6040808401919091526104d49082615b39565b90506104e785828661048c604083615b39565b60608301526104f7604082615b39565b905061050a85828661048c604083615b39565b608083015261051a604082615b39565b905061052d85828661048c604083615b39565b60c083015261053d604082615b39565b905061055085828661048c604083615b39565b60e0830152610560604082615b39565b905061057385828661048c604083615b39565b60a0830152610583604082615b39565b905061059685828661048c604083615b39565b6101008301526105a7604082615b39565b90506105ba85828661048c604083615b39565b6101208301526105cb604082615b39565b90506105de85828661048c604083615b39565b610140830151526105f0604082615b39565b9050610603858286610435602083615b39565b610160830152610614602082615b39565b90505f5b83811015610692575f5b60098110156106895761063c878488610435602083615b39565b84610180015183601c811061065357610653615b73565b6020020151826009811061066957610669615b73565b60200201818152505060208361067f9190615b39565b9250600101610622565b50600101610618565b505f5b6106a160016029615b39565b8110156106f0576106b9868387610435602083615b39565b836101c0015182602a81106106d0576106d0615b73565b6020020181815250506020826106e69190615b39565b9150600101610695565b50610702858286610435602083615b39565b6101a0830152610713602082615b39565b905061072685828661048c604083615b39565b6101408301516020015261073b604082615b39565b905061074e85828661048c604083615b39565b61014083015160026020020152610766604082615b39565b90505f5b6107756001856155e0565b8110156107be5761078d86838761048c604083615b39565b836101e0015182601b81106107a4576107a4615b73565b60200201526107b4604083615b39565b915060010161076a565b505f5b83811015610811576107da868387610435602083615b39565b83610200015182601c81106107f1576107f1615b73565b6020020181815250506020826108079190615b39565b91506001016107c1565b505f5b60048110156108655761082e868387610435602083615b39565b836102200151826004811061084557610845615b73565b60200201818152505060208261085b9190615b39565b9150600101610814565b5061087785828661048c604083615b39565b610240830152610888604082615b39565b905061089b85828661048c604083615b39565b610260830152509392505050565b5f600180826108d6866108d1896108cc6108c78a6310000000615b39565b6125e3565b6125fb565b612616565b90505f6108f4876108ef8a6108cc6108c78b6001615b39565b61262f565b90505f5b61092360107f00000000000000000000000000000000000000000000000000000000000000006155e0565b811015610990575f61094d8c838151811061094057610940615b73565b6020026020010151612657565b905061095d866108cc8684612616565b955061096d856108cc8584612616565b9450610979848b612616565b9350610985838b61262f565b9250506001016108f8565b505f5b60108110156109f7575f8a82601081106109af576109af615b73565b602002015190506109c4866108cc8684612616565b95506109d4856108cc8584612616565b94506109e0848b612616565b93506109ec838b61262f565b925050600101610993565b50610a02848461266c565b9a9950505050505050505050565b5f5f610a2583606001518561016001516125fb565b905060015f5b7f0000000000000000000000000000000000000000000000000000000000000000811015610b17575f86610180015182601c8110610a6b57610a6b615b73565b602002015180519091505f90610a89908360015b6020020151612616565b9050848114610aab576040516313f8744360e31b815260040160405180910390fd5b5f876080015184601c8110610ac257610ac2615b73565b60200201519050610ad3838261267a565b9550610b07856108cc60016108d1856108cc8e604001518b601c8110610afb57610afb615b73565b6020020151600161262f565b9450505050806001019050610a2b565b50610b20615291565b5f5b6029811015610b70576101c0870151610b3c600183615b39565b602a8110610b4c57610b4c615b73565b6020020151828260298110610b6357610b63615b73565b6020020152600101610b22565b505f610b8582875f0151886020015186612828565b9050600160025b7f0000000000000000000000000000000000000000000000000000000000000000811015610be257610bd882896080015183601c8110610bce57610bce615b73565b60200201516125fb565b9150600101610b8c565b50610c08610bf5836108cc60018561262f565b6108d18a6101a001518a606001516125fb565b94909414979650505050505050565b5f610c206152b0565b5f610c4f8460c001517f00000000000000000000000000000000000000000000000000000000000000006128a1565b90505f7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03811115610c8a57610c8a615940565b604051908082528060200260200182016040528015610cb3578160200160208202803683370190505b5090505f7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03811115610cef57610cef615940565b604051908082528060200260200182016040528015610d2857816020015b610d15615320565b815260200190600190039081610d0d5790505b509050610d5b610d56876101000151855f81518110610d4957610d49615b73565b602002602001015161262f565b61296a565b84610120018181525050610d90610d56876101000151855f81518110610d8357610d83615b73565b6020026020010151612616565b610140850181905261012085015160e0880151610db1926108d191906125fb565b845260c0860151610de190610dc59061296a565b6108cc8661012001516108ef8a60e001518961014001516125fb565b6020850152815160019083905f90610dfb57610dfb615b73565b602002602001018181525050876102400151815f81518110610e1f57610e1f615b73565b6020908102919091010152600160a08501525f60c08501528351610e42906129da565b60408501526020840151610e55906129da565b606085015260015b610e6960016024615b39565b8111610f0a57610e8185604001518660a001516125fb565b838281518110610e9357610e93615b73565b602002602001018181525050610edc8560c001516108d18b6101c00151600185610ebd91906155e0565b602a8110610ecd57610ecd615b73565b60200201518860a001516125fb565b60c086015260a08086015190880151610ef591906125fb565b60a0860152610f0381615b87565b9050610e5d565b505f5b6005811015610fe3575f610f22601e83615b39565b90505f610f3160016024615b39565b610f3b9084615b39565b9050610f6c858381518110610f5257610f52615b73565b60200260200101516108d189606001518a60a001516125fb565b858381518110610f7e57610f7e615b73565b602002602001018181525050610fbb8760c001516108d18d6101c0015184602a8110610fac57610fac615b73565b60200201518a60a001516125fb565b60c088015260a080880151908a0151610fd491906125fb565b60a08801525050600101610f0d565b50876020015181600181518110610ffc57610ffc615b73565b602002602001018190525086606001518160028151811061101f5761101f615b73565b602002602001018190525086608001518160038151811061104257611042615b73565b60200260200101819052508660a001518160048151811061106557611065615b73565b60200260200101819052508660c001518160058151811061108857611088615b73565b60200260200101819052508660e00151816006815181106110ab576110ab615b73565b6020026020010181905250866101000151816007815181106110cf576110cf615b73565b6020026020010181905250866101200151816008815181106110f3576110f3615b73565b60200260200101819052508661014001518160098151811061111757611117615b73565b602002602001018190525086610160015181600a8151811061113b5761113b615b73565b6020026020010181905250866101c0015181600b8151811061115f5761115f615b73565b602002602001018190525086610180015181600c8151811061118357611183615b73565b6020026020010181905250866101a0015181600d815181106111a7576111a7615b73565b6020026020010181905250866101e0015181600e815181106111cb576111cb615b73565b602002602001018190525086610200015181600f815181106111ef576111ef615b73565b60200260200101819052508661022001518160108151811061121357611213615b73565b60200260200101819052508661024001518160118151811061123757611237615b73565b60200260200101819052508661026001518160128151811061125b5761125b615b73565b60200260200101819052508661028001518160138151811061127f5761127f615b73565b6020026020010181905250866102a00151816014815181106112a3576112a3615b73565b6020026020010181905250866102c00151816015815181106112c7576112c7615b73565b6020026020010181905250866102e00151816016815181106112eb576112eb615b73565b60200260200101819052508661030001518160178151811061130f5761130f615b73565b60200260200101819052508661032001518160188151811061133357611333615b73565b60200260200101819052508661034001518160198151811061135757611357615b73565b602002602001018190525086610360015181601a8151811061137b5761137b615b73565b602002602001018190525086610380015181601b8151811061139f5761139f615b73565b6020026020010181905250866103a0015181601c815181106113c3576113c3615b73565b6020026020010181905250866103c0015181601d815181106113e7576113e7615b73565b6020026020010181905250876040015181601e8151811061140a5761140a615b73565b6020026020010181905250876060015181601f8151811061142d5761142d615b73565b602002602001018190525087608001518160208151811061145057611450615b73565b60200260200101819052508760a001518160218151811061147357611473615b73565b60200260200101819052508761012001518160228151811061149757611497615b73565b6020026020010181905250876101000151816023815181106114bb576114bb615b73565b60200260200101819052508760c00151816024815181106114de576114de615b73565b60200260200101819052508760e001518160258151811061150157611501615b73565b60200260200101819052505f61154787608001518660c001518b6102000151877f00000000000000000000000000000000000000000000000000000000000000006129ec565b9050611571815f8151811061155e5761155e615b73565b60200260200101518661012001516125fb565b608086018190526102008a01515160e08901516115a292916108d19161159791906125fb565b8861014001516125fb565b608086015260e08701516115b590612b4a565b60a08601525f6115c760016024615b39565b6115d2906001615b39565b90505f5b61160160017f00000000000000000000000000000000000000000000000000000000000000006155e0565b811015611812575f61163460017f00000000000000000000000000000000000000000000000000000000000000006155e0565b8210159050806117a357611668610d568b6101000151898560016116589190615b39565b81518110610d4957610d49615b73565b6101208901526101008a015161169890610d569089611688866001615b39565b81518110610d8357610d83615b73565b61014089015260a08801516101208901516116b391906125fb565b61016089015260a088015160e08b01516116db916116d0916125fb565b8961014001516125fb565b61018089018190526116fe906116f0906129da565b6108d18a61016001516129da565b866117098486615b39565b8151811061171957611719615b73565b6020026020010181815250505f6117528961018001518e61020001518560016117429190615b39565b601c8110610bce57610bce615b73565b905061178c816108d18b61016001518887600161176f9190615b39565b8151811061177f5761177f615b73565b60200260200101516125fb565b905061179c896080015182612616565b60808a0152505b6117c26117b88960a001518c60e001516125fb565b8b60e001516125fb565b60a08901526101e08c015182601b81106117de576117de615b73565b6020020151856117ee8486615b39565b815181106117fe576117fe615b73565b6020908102919091010152506001016115d6565b5061183e60017f00000000000000000000000000000000000000000000000000000000000000006155e0565b6118489082615b39565b90506118686118608961010001518a60c0015161262f565b60019061266c565b60e08701515261010088015160c08901516118ac91611860916108ef907f07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76906125fb565b60e0878101805160200192909252815180516040909101529051805160609091015260a0870151908901516118ee916118e4916125fb565b8960e001516125fb565b60a08701525f5b600481101561199c575f6119278860e00151836004811061191857611918615b73565b60200201518960a001516125fb565b9050611932816129da565b886101000151836004811061194957611949615b73565b602002015260a088015160e08b015161196291906125fb565b8860a001818152505061198e88608001516108d1838f61022001518660048110610bce57610bce615b73565b6080890152506001016118f5565b506101008601515184518590839081106119b8576119b8615b73565b602090810291909101810191909152610100870151908101516119dc916002610a7f565b846119e8836001615b39565b815181106119f8576119f8615b73565b60209081029190910101526101008601516060015184611a19836002615b39565b81518110611a2957611a29615b73565b60209081029190910101525f5b6003811015611a8b578a61014001518160038110611a5657611a56615b73565b60200201518483611a6681615b87565b945081518110611a7857611a78615b73565b6020908102919091010152600101611a36565b506040518060400160405280600181526020016002815250838281518110611ab557611ab5615b73565b60200260200101819052508560800151848280611ad190615b87565b935081518110611ae357611ae3615b73565b602002602001018181525050611b0d8a61022001518960c001518a608001518d6101a00151612b55565b611b2a5760405163a2a2ac8360e01b815260040160405180910390fd5b5f8a6102600151905080848381518110611b4657611b46615b73565b6020026020010181905250886101000151858381518110611b6957611b69615b73565b602002602001018181525050611b7d615338565b611b878587612eb8565b8152611b9282612fb0565b602082018190528c5182515f92611baa929190612ff6565b90505f5f611bba8f5f0151613087565b91509150611bc782613148565b611bd081613148565b8351611bdd9083856131e7565b84526020840151611bef9082856131e7565b602085018190528451611c0191613216565b9f9e505050505050505050505050505050565b611c1c615024565b50604080516103e0810182526220000081526015602080830191909152601282840152825180840184527f286493b1bc5a9674ed3d063c79269512944b6dfb58b880361d36fce8a61c981481527f1ef073ead7f7fd307a1a357548e4d8c9f5d03d5a7191cea80b2ba0cf03d18d29818301526060830152825180840184527f216ae5e3bf2a41f792d13f7028ec70fb78ac8d5fa4834341acad58d3453bf12381527f2ef50ace63f2e23d4b2a184ce511e1b7781287457bcc135459c5c56de1877b93818301526080830152825180840184527f04874f63e091441575d7f87163ad2d04fa3e4b49cee4cde183081c90e13ff17f81527f1547db64a1675f5ab99f4dc4cf2af5cfd0ef1fbdb68977d8abc8f7112e88bf808183015260a0830152825180840184527f17094eb4ca2210abcb1b4d41cf1355b04dd66b014d42345e6eadf8407a74ed1a81527f2228aebc15e7852110c94a633f8fb944d71cb1a5cff1b5127d0e4eecf1a7726f8183015260c0830152825180840184527f15cfc9a869b9c86a839a67ab04b780b3049b2d5f10a901055f85d4717428f6dc81527f2aec2a8cee64e9da3981b9d6f9f00faffbe2ece25cd4f57d80b8552d81c716b58183015260e0830152825180840184527f232463dd05273276d135bcdfe18a8b299585d6e0fe220dfafe63a54293476dfe81527f17f763713bb623ce792152d0aabd32b83ca5a6abecb97ece41dc00a1bb6296ad81830152610100830152825180840184527f1ad0852ae1f831506b33a76a4968849abaf87e068b8e00ba462e190ff4dc0ff981527f2b782e70b28e331338317e348ecb3d3ee69c0cb7b1ca634b32395b5349605a9381830152610120830152825180840184527f1a03cf866e6a54045329039071909e3129fefe1a3f380a5db12e3156f6bed47e81527f1bdf1ec93f5b5962d4aac343eaa211a5b8021546f0dc225d03f39cb465546d5a81830152610140830152825180840184527f1940cce3f764969daf23bd6cf6d09ecd7e50392878ce2ba3a0a30c88f619cd3781527f06d183a757a5b2294f8e3b10d3304744bc4d7b68246b01563c48dfd3ab44384281830152610160830152825180840184527f11a96a0fb134b4ad2a3ac7b54fc7989a18ada93ac73e0e7bfb7134f334854c5581527f166911402df98ab7053779cb1f431384018e058cbe2ac79d1d5ff020ce5474d981830152610180830152825180840184527f11b342da18e6325f40bddb950e3ba058fe013e0a6757512f022a0d9961d3d46881527f04225227552dcaa71ff7c4571eebd04ca40091a33ff91d1b220af458dffca289818301526101a0830152825180840184527f1b5b7f1276e5867eef1d033df65d003c591c16bf64bd2d4cb89bccc1a98ab3dd81527f28f55668465e42d873d91d75906232c6f05bf37be890e4d4c0ef6a1d5df8c211818301526101c0830152825180840184527f18ae1c0c284757fd6a6bcf60413a8e2334fcb46562ea30b9cb9444a66349346381527f1cade59f171ec1d94348309a02e1642067d6e6b43eb254837e703c1488dff221818301526101e0830152825180840184527f2537fc03ae8cfc49498d0a04704d77e10d6477d6b7c5e515cf24dadab0dee87481527f0850d14d62e2c194a69110f3aefa10d6caea9bf6ff940331877c5f49e41e5fa781830152610200830152825180840184527f02f24bc3b670a81abc44133de34592b13bb3a5ea67cdaa1d73ad35965b8abe4981527f0c62df1ee51bfbaeba2a75c1cc8aa8a2ea3222f21bc0b8958bd43c3aa749e96381830152610220830152825180840184527f21ac9dc80bd7f54e6817d224b7650c4176407a1109f92567c05676ac1fe0973c81527f2d325ad91e20be879c716023bcde84d469c4f7867c12bb321c387e4a1036f86d81830152610240830152825180840184527f25b123800356ccc6d7a609989a0e4fc132bf757389ca25f0c1db7612bbec234981527f03832b0106e3581612f83d158277886cc9e5637790fccabde84b3218220f2a4881830152610260830152825180840184527f25962cb0996ebaeea7b01f8860fde2547f8bb738291e53c7f25a4ac80af4fd2e81527f22ec82955d2dc95fddc9cf9fd4f7aa555635abb02cf6eeb7ac3df225c8b62d5281830152610280830152825180840184527f1332d280da268502ba2d853e14e53baf604c7a6a41b7e70bdf2e861cc6a5982181527f0d14761f5281d5dc1209b25146d92d9e5692294f818c8c3468c9921f7a7cce79818301526102a0830152825180840184527f1b215ab72c8af91207dee6f8ac01f0e0d51895a9f670d7173c7b6d5b80a3908581527f04ee544edd280fa185c8d9886c9e19832ef71d67fab5093eadeec49ccc5a34b1818301526102c0830152825180840184527f11005f2ce25b6f954b89343fa059782ab8ecdc84d411c828cc7dfd54f796b6f681527f0c68694f3595aecaaffdff52c0fa5f1d05fa8c7ba651da1220634fa4b8867ba7818301526102e0830152825180840184527f1145c2f8face74c268cc68c772d99c59587659cbd594a1aef32a36ab556621e281527f1ffa60a61a074aa68c8412fd9f3b4ae61437cf3ff3f0497e1244ba56370d7b9181830152610300830152825180840184527f099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d2681527e15b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f81830152610320830152825180840184527f1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e81527f305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d1981830152610340830152825180840184527f061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d81527f1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e81830152610360830152825180840184527f043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce81527f261522c4089330646aff96736194949330952ae74c573d1686d9cb4a007338548183015261038083015282518084018452600181526002818301526103a083015282518084019093527f1f0071e7d3b9be4c8376ff230b3b8b242585d403d25463022d38e574cb0b575483527f19cb572dac34151573e3024878327a8ea7febb3d76c6171b10d1e3f2f2907584908301526103c081019190915290565b5f6103f461255c8385615b9f565b612657565b612569615320565b60408051808201909152805f516020615c6e5f395f51905f5261258f60205f8789615b4c565b61259891615b9f565b6125a29190615bbc565b81526020908101905f516020615c6e5f395f51905f52906125c7906040908789615b4c565b6125d091615b9f565b6125da9190615bbc565b90529392505050565b5f5f516020615c8e5f395f51905f52825b0692915050565b5f5f516020615c8e5f395f51905f5282840990505b92915050565b5f5f516020615c8e5f395f51905f528284089392505050565b5f5f516020615c8e5f395f51905f52825f516020615c8e5f395f51905f520384089392505050565b5f5f516020615c8e5f395f51905f52826125f4565b5f6103f4836108cc8461296a565b5f5f604051806101200160405280619d8081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec5181526020016105a081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31815260200161024081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd3181526020016105a081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec518152602001619d8081525090505f600190505f5f90505b600981101561277357612769826108cc878461262f565b9150600101612752565b5061277c61534b565b5f5b60098110156127cd576127ae610d5685836009811061279f5761279f615b73565b60200201516108cc898561262f565b8282600981106127c0576127c0615b73565b602002015260010161277e565b505f5b600981101561281357612809856108d18984600981106127f2576127f2615b73565b6020020151858560098110610bce57610bce615b73565b94506001016127d0565b5061281e84836125fb565b9695505050505050565b5f61283161536a565b61283c86828561340a565b612848868683866135b6565b612854868683866137a2565b61285f8682856139c7565b61286a868285613bbb565b61287686868386613f04565b6128818682856143b0565b61288c8682856147c2565b612897868285614b83565b61281e8185614e83565b60605f826001600160401b038111156128bc576128bc615940565b6040519080825280602002602001820160405280156128e5578160200160208202803683370190505b50905083815f815181106128fb576128fb615b73565b602090810291909101015260015b838110156129625761293d826129206001846155e0565b8151811061293057612930615b73565b6020026020010151612b4a565b82828151811061294f5761294f615b73565b6020908102919091010152600101612909565b509392505050565b5f5f8290505f604051602081526020808201526020604082015282606082015260025f516020615c8e5f395f51905f520360808201525f516020615c8e5f395f51905f5260a082015260205f60c08360055afa806129c6575f5ffd5b505f51608091909101604052949350505050565b5f516020615c8e5f395f51905f520390565b60605f826001600160401b03811115612a0757612a07615940565b604051908082528060200260200182016040528015612a30578160200160208202803683370190505b509050825b8015612b3f575f85612a486001846155e0565b81518110612a5857612a58615b73565b602002602001015190505f89600184612a7191906155e0565b601c8110612a8157612a81615b73565b602002015190505f612adc612aa0612a99858d6125fb565b60026125fb565b6108ef8b612aaf6001896155e0565b601c8110612abf57612abf615b73565b60200201516108cc612ad6886108cc60018a61262f565b8761262f565b9050612afd816108cc610d56612af7876108cc60018961262f565b86612616565b99508990508085612b0f6001876155e0565b81518110612b1f57612b1f615b73565b60200260200101818152505050505080612b3890615bdb565b9050612a35565b509695505050505050565b5f61261082836125fb565b5f600181612b6e612b6887610100614edc565b8361262f565b905080612b8e5760405163835eb8f760e01b815260040160405180910390fd5b612b96615389565b80518390525f5b7f0000000000000000000000000000000000000000000000000000000000000000811015612c7f575f612bd18260096155c9565b612bdc906001615b39565b905084835f0151826101008110612bf557612bf5615b73565b60200201525f612c06826001615b39565b90505b612c14600983615b39565b811015612c75578351612c5490612c2c6001846155e0565b6101008110612c3d57612c3d615b73565b60200201518a85601c8110610bce57610bce615b73565b8451826101008110612c6857612c68615b73565b6020020152600101612c09565b5050600101612b9d565b50608081018390525f602082018190525b610100811015612d9057612cb1612cab83608001518a6125fb565b8561262f565b8260a00151826101008110612cc857612cc8615b73565b602002015260a0820151612cf290826101008110612ce857612ce8615b73565b602002015161296a565b8260a00151826101008110612d0957612d09615b73565b602002018181525050612d4f82602001516108d1845f0151846101008110612d3357612d33615b73565b60200201518560a00151856101008110610bce57610bce615b73565b60208301526080820151612d83907f204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d66125fb565b6080830152600101612c90565b505f612da1836108cc61010061296a565b9050612db18260200151826125fb565b602083015260a0820151612dcc905f5b6020020151826125fb565b604083015260a0820151612df890612de760016101006155e0565b6101008110612dc157612dc1615b73565b60608301526040820151612e0e908a6002610bce565b60c08301819052612e6e906108d1612e468b7f204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d661262f565b60208d015160408e01516108cc91612e5d9161262f565b8e5160208901516108ef91906125fb565b60c083018190526060830151612ea391612e97916108d1906108cc8e600260200201518c61262f565b6108ef858c6003610bce565b60c08301819052159998505050505050505050565b612ec0615320565b7f00000000000000000000000000000000000000000000000000000000000000005f5b81811015612f1557612f0d858281518110612f0057612f00615b73565b6020026020010151613148565b600101612ee3565b50604051600190815b60018401811015612f7a5760208102870160208202870181515160408501528151602001516060850152805160808501525050604080830160606040850160075afa8316925060408260808460065afa90921691600101612f1e565b5080518452602081015160208501525080612fa8576040516352ec174560e11b815260040160405180910390fd5b505092915050565b612fb8615320565b5f516020615c6e5f395f51905f5282602001515f516020615c6e5f395f51905f52612fe391906155e0565b612fed9190615bbc565b60208301525090565b5f5f5f61300286613087565b9150915061300e6153cd565b82518152602080840151818301528251604080840191909152838201516060840152875160808401528782015160a0840152865160c08401528682015160e08401525161307c9161306191849101615bf0565b60405160208183030381529060405280519060200120612657565b979650505050505050565b61308f615320565b613097615320565b82516020808501516040860151606087015160cc90811b608892831b604494851b90961795909517949094178652608087015160a088015160c089015160e08a0151871b90841b91851b9092171717868401526101008701516101208801516101408901516101608a0151871b90841b91851b909217171785526101808701516101a08801516101c08901516101e09099015190951b9790911b9390911b1791909117939093179281019290925291565b805160208201515f5f516020615c6e5f395f51905f528380095f516020615c6e5f395f51905f5260035f516020615c6e5f395f51905f52838709085f516020615c6e5f395f51905f5284850914915050806131e15760405162461bcd60e51b8152602060048201526019602482015278706f696e74206973206e6f74206f6e2074686520637572766560381b60448201526064016100e5565b50505050565b6131ef615320565b6131f7615320565b6132018386614f3d565b905061320d8185614f93565b95945050505050565b81516020808401518351848301516040805194850195909552938301919091527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260608301527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed60808301527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b60a08301527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60c083015260e08201526101008101919091527f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c16101208201527f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b06101408201527f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe46101608201527f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e556101808201525f9081906101a00160405160208183030381529060405290505f5f60086001600160a01b0316836040516133ac9190615c24565b5f60405180830381855afa9150503d805f81146133e4576040519150601f19603f3d011682016040523d82523d5f602084013e6133e9565b606091505b509150915081801561281e57508080602001905181019061281e9190615c3a565b5f613416846007614ff4565b90507f183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f80000005f61347761347161344b85600361262f565b6108cc61346661345b8b5f614ff4565b6108cc8c601d614ff4565b6108cc8b601c614ff4565b836125fb565b90506134f86134ed6134d46134bb6134a2856108d16134978d6002614ff4565b6108cc8e601c614ff4565b6108d16134b08c6003614ff4565b6108cc8d601d614ff4565b6108d16134c98b6004614ff4565b6108cc8c601e614ff4565b6108d16134e28a6005614ff4565b6108cc8b601f614ff4565b6108d1886001614ff4565b9050613517816108d161350c86600161262f565b6108cc8a6027614ff4565b905061352381846125fb565b905061352f81856125fb565b8552505f905061356961355f61355461354988601c614ff4565b6108d189601f614ff4565b6108ef886024614ff4565b6108d1875f614ff4565b905061357a816108cc84600261262f565b905061358b816108cc84600161262f565b905061359781836125fb565b90506135a381846125fb565b9050808460015b60200201525050505050565b5f5f5f6135ec6135e26135ca89601c614ff4565b6108d16135d88b6012614ff4565b8a606001516125fb565b8760800151612616565b9050613625816108cc61361b6136038b601d614ff4565b6108d16136118d6013614ff4565b8c606001516125fb565b8960800151612616565b905061364a816108cc61361b61363c8b601e614ff4565b6108d16136118d6014614ff4565b905061366f816108cc61361b6136618b601f614ff4565b6108d16136118d6015614ff4565b92505f90506136936135e261368589601c614ff4565b6108d16135d88b600e614ff4565b90506136b8816108cc61361b6136aa8b601d614ff4565b6108d16136118d600f614ff4565b90506136dd816108cc61361b6136cf8b601e614ff4565b6108d16136118d6010614ff4565b9050613702816108cc61361b6136f48b601f614ff4565b6108d16136118d6011614ff4565b91505f9050613729613723613718896020614ff4565b6108d18a601a614ff4565b846125fb565b905061375e816108ef6137586137408b6028614ff4565b6108d161374e8d601b614ff4565b8c60a001516125fb565b856125fb565b905061376a81856125fb565b6040860152505f61378d61375861378289601b614ff4565b6108cc8a6028614ff4565b9050808560035b602002015250505050505050565b5f5f6138006137e86137d06137bb61361b8a6016614ff4565b6108d16137c98b6017614ff4565b8a516125fb565b6108d16137de8a6018614ff4565b89602001516125fb565b6108d16137f6896019614ff4565b88604001516125fb565b91505f61383761381e61381489601c614ff4565b8860800151612616565b6108d161382c8a6003614ff4565b6108cc8b6024614ff4565b90505f61386061384889601d614ff4565b6108d16138558b5f614ff4565b6108cc8c6025614ff4565b90505f61388a6138718a601e614ff4565b6108d161387f8c6001614ff4565b6108cc8d6026614ff4565b90506138c96138b16138a3856108d1868d5f01516125fb565b6108d1848c602001516125fb565b6108d16138bf8c6004614ff4565b8b604001516125fb565b93505050505f6138dd613723886021614ff4565b90505f6138ee613723896021614ff4565b90505f61392661390d6139028b6023614ff4565b6108d18c6006614ff4565b6108ef61391b8c6023614ff4565b6108cc8d6006614ff4565b90505f613944612b6861393987896125fb565b6108cc8d6021614ff4565b905061395081886125fb565b90505f61397861396a6139648d6006614ff4565b876125fb565b6108ef6139648e6022614ff4565b90505f6139868c6023614ff4565b90505f613996612b6883846125fb565b60808c0185905260a08c0184905290506139b0818b6125fb565b8b6006602002015250505050505050505050505050565b5f6139d35f600161262f565b90505f6139e15f600261262f565b90505f6139ef5f600361262f565b90505f613a0b613a0088601d614ff4565b6108ef89601c614ff4565b90505f613a27613a1c89601e614ff4565b6108ef8a601d614ff4565b90505f613a43613a388a601f614ff4565b6108ef8b601e614ff4565b90505f613a5f613a548b6024614ff4565b6108ef8c601f614ff4565b905083613a70816108cc818b612616565b9050613a80816108cc878a612616565b9050613a90816108cc8789612616565b9050613aa1816108cc8d6008614ff4565b9050613aad818a6125fb565b60e08b01525082613ac2816108cc818b612616565b9050613ad2816108cc868a612616565b9050613ae2816108cc8689612616565b9050613af3816108cc8d6008614ff4565b9050613aff818a6125fb565b6101008b01525081613b15816108cc818b612616565b9050613b25816108cc858a612616565b9050613b35816108cc8589612616565b9050613b46816108cc8d6008614ff4565b9050613b52818a6125fb565b6101208b01525080613b68816108cc818b612616565b9050613b78816108cc848a612616565b9050613b88816108cc8489612616565b9050613b99816108cc8d6008614ff4565b9050613ba5818a6125fb565b610140909a019990995250505050505050505050565b613bf46040518060e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b613bff84601d614ff4565b8152613c0c84601e614ff4565b6020820152613c1c846024614ff4565b6040820152613c2c846027614ff4565b6060820152613c3c846026614ff4565b6080820152613c4c846025614ff4565b60a08201525f613c5d856002614ff4565b90505f613c6a865f614ff4565b90505f613c7e8460400151855f015161262f565b90505f613c93856020015186602001516125fb565b606086015190915086905f90613ca990806125fb565b90505f613cc7613cc189602001518a606001516125fb565b886125fb565b90505f613ce6613cdf8a60a001518b60400151612616565b8a51612616565b9050613cf561396482886125fb565b9050613d1c613d16613d10613d0a848761262f565b8861262f565b84612616565b83612616565b9050613d44613d39613d2e83876125fb565b6108cc8f6009614ff4565b6108cc60018a61262f565b6101608c015250505050602085015160808601515f91613d6391612616565b90505f613d81613d778860600151886125fb565b886020015161262f565b90505f613da5613d9184876125fb565b6108d16137588b60a001518c5f015161262f565b9050613dcd613dc2613db7838c6125fb565b6108cc8e6009614ff4565b6108cc60018961262f565b6101808b0152505f9150613dee9050613de7836011612616565b87516125fb565b90505f613dfb8384612616565b9050613e078182612616565b90505f613e158360096125fb565b9050613e3e613e38613723613e318b60a001518c5f0151612616565b8b51612616565b8261262f565b60c089018190525f90613e5990613cc190613d2e908d6125fb565b9050613e6c8b600b602002015182612616565b6101608c0152505086515f9250613e939150613de790613e8c9080612616565b8851612616565b90505f613ed3613eae836108cc8a5f01518b60a0015161262f565b60208901516108ef90613ec19080612616565b6108cc8b602001518c60800151612616565b9050613ef089600c60200201516108d1613cc1613d2e858d6125fb565b89600c602002015250505050505050505050565b613f6e604051806101e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b613f86613f7c86601e614ff4565b85604001516125fb565b808252613fa5906108d1613f9b88601d614ff4565b87602001516125fb565b808252613fba906108d1613de788601c614ff4565b808252613fcc906108d1876001614ff4565b80825260208201819052613fe5906108ef87601f614ff4565b8152614000613ff5866024614ff4565b6108ef87601c614ff4565b608082015261401e614013866027614ff4565b6108ef87601f614ff4565b60608201526080810151614037906108cc81600161262f565b6101c082015260808101516140779061406d90614066906108cc60015f516020615c8e5f395f51905f526155e0565b6001612616565b82606001516125fb565b60a082018190526140ad9061409f906108cc614094896002614ff4565b6108cc8a6003614ff4565b6108cc61375888600a614ff4565b83600e60200201526101c08101516140d19061409f906108cc614094896002614ff4565b6101e084015280516140f6906108cc6140eb886002614ff4565b6108cc896003614ff4565b6101208201525f61411561410b87601f614ff4565b836020015161262f565b9050614126816108cc83600161262f565b60e0830152614143614139876026614ff4565b86604001516125fb565b60408301819052614166906108d161415c896025614ff4565b88602001516125fb565b60408301819052614186906108d161417f896024614ff4565b88516125fb565b60408301526141a3614199876027614ff4565b836040015161262f565b60408301525f6141c26141b7886026614ff4565b6108ef89601e614ff4565b90506142146141ef613471614066866080015160015f516020615c8e5f395f51905f526108cc91906155e0565b6108cc614066866040015160015f516020615c8e5f395f51905f526108cc91906155e0565b60c084015260408301516142369061422c90806125fb565b846040015161262f565b61010084015260c083015161426290614254906108cc8a6004614ff4565b6108cc6139648a600a614ff4565b6102008601526101c083015161428190614254906108cc8a6004614ff4565b6102208601526101008301516142a090614254906108cc8a6004614ff4565b61024086015260e08301516142ba906108cc896004614ff4565b6101408401526142d96142ce886025614ff4565b6108ef89601d614ff4565b6101608401526080830151614317906141b79061430c90614066906108cc60015f516020615c8e5f395f51905f526155e0565b8561016001516125fb565b61018084018190526101208401516101a0850181905261434e916108d1906108cc6143438c6005614ff4565b6108cc8d6002614ff4565b6101a08401819052835161436e91906108d1906108cc6143438c5f614ff4565b6101a084018190526101408401516143869190612616565b6101a084018190526143a0906108cc6139648a600a614ff4565b6101a0840181905285600d613794565b6143e96040518060e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b61441e6144056143fa86601c614ff4565b6108cc876025614ff4565b6108d1614413876024614ff4565b6108cc88601d614ff4565b815261446361445861443f61443487601c614ff4565b6108cc88601f614ff4565b6108d161444d88601d614ff4565b6108cc89601e614ff4565b6108ef866026614ff4565b6040820181905261447890600160441b6125fb565b6040820181905261448e906108ef866027614ff4565b6040820181905281516144a19190612616565b604082018190526144b7906108cc866005614ff4565b604082015280516144cc90600160441b6125fb565b8082526144ec906108d16144e1876024614ff4565b6108cc886025614ff4565b80825260208201819052614513906108ef61450887601e614ff4565b6108d188601f614ff4565b60208201819052614529906108cc866004614ff4565b6020820152805160608201819052614546906108d186601f614ff4565b6060820181905261456a906108ef61455f876026614ff4565b6108d1886027614ff4565b6060820181905261457f906108cc865f614ff4565b8160600181815250505f6145a861459e83602001518460400151612616565b8360600151612616565b90506145b9816108cc876003614ff4565b90506145d16145c9866025614ff4565b6140006125fb565b608083018190526145e7906108d1876024614ff4565b608083018190526145fa906140006125fb565b60808301819052614610906108d187601e614ff4565b60808301819052614623906140006125fb565b60808301819052614639906108d187601d614ff4565b6080830181905261464c906140006125fb565b60808301819052614662906108d187601c614ff4565b60808301819052614678906108ef87601f614ff4565b6080830181905261468e906108cc876005614ff4565b60808301526146a16145c9866026614ff4565b60a083018190526146b7906108d1876025614ff4565b60a083018190526146ca906140006125fb565b60a083018190526146e0906108d1876024614ff4565b60a083018190526146f3906140006125fb565b60a08301819052614709906108d187601f614ff4565b60a0830181905261471c906140006125fb565b60a08301819052614732906108d187601e614ff4565b60a08301819052614748906108ef876027614ff4565b60a0830181905261475d906108cc875f614ff4565b60a0830181905260808301515f916147759190612616565b9050614786816108cc886004614ff4565b90506147928282612616565b60c084018190526147ab906108cc61396489600b614ff4565b60c084018190528560136020020152505050505050565b6148386040518061022001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b61485161484685601c614ff4565b6108d1866002614ff4565b815261486c61486185601d614ff4565b6108d1866003614ff4565b602082015261488a61487f85601e614ff4565b6108d1866004614ff4565b60408201526148a861489d85601f614ff4565b6108d1866005614ff4565b606082015280516148da906148d3906148cc906148c590806125fb565b84516125fb565b83516125fb565b82516125fb565b608082015260208101516149189061490e90614904906148fa90806125fb565b84602001516125fb565b83602001516125fb565b82602001516125fb565b60a082015260408101516149569061494c906149429061493890806125fb565b84604001516125fb565b83604001516125fb565b82604001516125fb565b60c0820152606081015161498a9061406d906149809061497690806125fb565b84606001516125fb565b83606001516125fb565b60e0820152608081015160a08201516149a39190612616565b61010082015260c081015160e08201516149bd9190612616565b61012082015260a08101516149e1906149d69080612616565b826101200151612616565b61014082015260e0810151614a05906149fa9080612616565b826101000151612616565b610160820152610120810151614a1b9080612616565b6101e08201819052614a3c90614a319080612616565b826101600151612616565b6101e0820152610100810151614a529080612616565b6101a08201819052614a7390614a689080612616565b826101400151612616565b6101a08201819052610160820151614a8a91612616565b6101808201526101408101516101e0820151614aa69190612616565b6101c0820152614aba61347185600c614ff4565b6102008201819052610280840151610180830151614ae5926108d1916108cc906108ef8a6024614ff4565b8360146020020152614b1583601560200201516108d18361020001516108cc856101a001516108ef8a6025614ff4565b8360156020020152614b4583601660200201516108d18361020001516108cc856101c001516108ef8a6026614ff4565b8360166020020152614b7583601760200201516108d18361020001516108cc856101e001516108ef8a6027614ff4565b836017602002015250505050565b614bd56040518061016001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b5f6040518060800160405280614c0a7f10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e76125e3565b8152602001614c387f0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b6125e3565b8152602001614c657e544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac156125e3565b8152602001614c937f222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b6125e3565b90529050614cb0614ca586601c614ff4565b6108d1876002614ff4565b6101208301819052614cef90614ce490614cd990614cce90806125fb565b8561012001516125fb565b8461012001516125fb565b8361012001516125fb565b8252614cfc85601d614ff4565b6020830152614d0c85601e614ff4565b6040830152614d1c85601f614ff4565b606083015281516020830151614d449161459e91614d3a9190612616565b8460400151612616565b6080830152614d5761372386600d614ff4565b6101408301528151614d7890614d6e90835f610bce565b8360800151612616565b60a0830152614da484601860200201516108d18461014001516108cc8660a001516108ef8b6024614ff4565b6103008501526020820151614dbf90614d6e90836001610bce565b60c0830152614deb84601960200201516108d18461014001516108cc8660c001516108ef8b6025614ff4565b6103208501526040820151614e0690614d6e90836002610bce565b60e0830152614e3284601a60200201516108d18461014001516108cc8660e001516108ef8b6026614ff4565b6103408501526060820151614e4d90614d6e90836003610bce565b610100830152614e7b84601b60200201516108d18461014001516108cc8661010001516108ef8b6027614ff4565b84601b6135aa565b815160015b601c811015614ed557614ecb826108d18684601c8110614eaa57614eaa615b73565b602002015186614ebb6001876155e0565b601b8110610bce57610bce615b73565b9150600101614e88565b5092915050565b5f5f8390505f60405160208152602080820152602060408201528260608201528460808201525f516020615c8e5f395f51905f5260a082015260205f60c08360055afa80614f28575f5ffd5b505f5160809190910160405295945050505050565b614f45615320565b614f4d615320565b604051835181526020840151602082015284604082015260408160608360075afa80614f77575f5ffd5b5080518252602080820151908301526060016040529392505050565b614f9b615320565b614fa3615320565b6040518451815260208501516020820152835160408201526020840151606082015260408160808360065afa80614fd8575f5ffd5b5080518252602080820151908301526080016040529392505050565b5f8282602881111561500857615008615c59565b6029811061501857615018615b73565b60200201519392505050565b604051806103e001604052805f81526020015f81526020015f815260200161504a615320565b8152602001615057615320565b8152602001615064615320565b8152602001615071615320565b815260200161507e615320565b815260200161508b615320565b8152602001615098615320565b81526020016150a5615320565b81526020016150b2615320565b81526020016150bf615320565b81526020016150cc615320565b81526020016150d9615320565b81526020016150e6615320565b81526020016150f3615320565b8152602001615100615320565b815260200161510d615320565b815260200161511a615320565b8152602001615127615320565b8152602001615134615320565b8152602001615141615320565b815260200161514e615320565b815260200161515b615320565b8152602001615168615320565b8152602001615175615320565b8152602001615182615320565b815260200161518f615320565b815260200161519c615320565b81526020016151a9615320565b905290565b6040518061028001604052806151c26153ec565b81526020016151cf615320565b81526020016151dc615320565b81526020016151e9615320565b81526020016151f6615320565b8152602001615203615320565b8152602001615210615320565b815260200161521d615320565b815260200161522a615320565b8152602001615237615320565b815260200161524461540b565b81526020015f8152602001615257615438565b81526020015f815260200161526a615466565b8152602001615277615485565b815260200161528461536a565b815260200161518f6154b3565b6040518061052001604052806029906020820280368337509192915050565b604051806101c001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020016152ee6154b3565b81526020016152fb6154b3565b81526020015f81526020015f81526020015f81526020015f8152602001606081525090565b60405180604001604052805f81526020015f81525090565b604051806040016040528061519c615320565b6040518061012001604052806009906020820280368337509192915050565b604051806103800160405280601c906020820280368337509192915050565b6040518060e0016040528061539c6154d1565b81526020015f81526020015f81526020015f81526020015f81526020016153c16154d1565b81526020015f81525090565b6040518061010001604052806008906020820280368337509192915050565b6040518061020001604052806010906020820280368337509192915050565b60405180606001604052806003905b615422615320565b81526020019060019003908161541a5790505090565b604051806103800160405280601c905b61545061534b565b8152602001906001900390816154485790505090565b604051806105400160405280602a906020820280368337509192915050565b604051806103600160405280601b905b61549d615320565b8152602001906001900390816154955790505090565b60405180608001604052806004906020820280368337509192915050565b604051806120000160405280610100906020820280368337509192915050565b5f5f5f5f60408587031215615504575f5ffd5b84356001600160401b03811115615519575f5ffd5b8501601f81018713615529575f5ffd5b80356001600160401b0381111561553e575f5ffd5b87602082840101111561554f575f5ffd5b6020918201955093508501356001600160401b0381111561556e575f5ffd5b8501601f8101871361557e575f5ffd5b80356001600160401b03811115615593575f5ffd5b8760208260051b84010111156155a7575f5ffd5b949793965060200194505050565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417612610576126106155b5565b81810381811115612610576126106155b5565b805f5b60108110156131e15781518452602093840193909101906001016155f6565b805f5b60038110156131e15761563684835180518252602090810151910152565b6040939093019260209190910190600101615618565b805f5b601c8110156131e1578151845f5b600981101561567c57825182526020928301929091019060010161565d565b50505061012093909301926020919091019060010161564f565b805f5b602a8110156131e1578151845260209384019390910190600101615699565b805f5b601b8110156131e1576156d984835180518252602090810151910152565b60409390930192602091909101906001016156bb565b805f5b601c8110156131e15781518452602093840193909101906001016156f2565b805f5b60048110156131e1578151845260209384019390910190600101615714565b8183525f6001600160fb1b0383111561574a575f5ffd5b8260051b80836020870137939093016020019392505050565b61576e8188516155f3565b5f602088015161578c61020084018280518252602090810151910152565b5060408801518051610240840152602090810151610260840152606089015180516102808501528101516102a0840152608089015180516102c08501528101516102e084015260a0890151805161030085015281015161032084015260c0890151805161034085015281015161036084015260e089015180516103808501528101516103a084015261010089015180516103c08501528101516103e084015261012089015180516104008501520151610420830152610140880151615855610440840182615615565b5061016088015161050083015261018088015161587661052084018261564c565b506101a08801516124a08301526101c08801516158976124c0840182615696565b506101e08801516158ac612a008401826156b8565b506102008801516158c16130c08401826156ef565b506102208801516158d6613440840182615711565b5061024088015180516134c08401526020908101516134e0840152610260890151805161350085015201516135208301526135c0613540830181905261591f9083018789615733565b613560830195909552506135808101929092526135a0909101529392505050565b634e487b7160e01b5f52604160045260245ffd5b60405161014081016001600160401b038111828210171561597757615977615940565b60405290565b604051601f8201601f191681016001600160401b03811182821017156159a5576159a5615940565b604052919050565b5f60c082840312156159bd575f5ffd5b60405160c081016001600160401b03811182821017156159df576159df615940565b604090815283518252602080850151908301528381015190820152606080840151908201526080808401519082015260a0928301519281019290925250919050565b5f82601f830112615a30575f5ffd5b5f610360615a3d8161597d565b915083018185821115615a4e575f5ffd5b845b82811015615a68578051825260209182019101615a50565b509195945050505050565b5f82601f830112615a82575f5ffd5b5f610380615a3d8161597d565b5f610be0828403128015615aa1575f5ffd5b50615aaa615954565b615ab484846159ad565b8152615ac38460c08501615a21565b6020820152615ad6846104208501615a73565b60408201526107a08301516060820152615af4846107c08501615a73565b6080820152610b4083015160a0820152610b6083015160c0820152610b8083015160e0820152610ba0830151610100820152610bc09092015161012083015250919050565b80820180821115612610576126106155b5565b5f5f85851115615b5a575f5ffd5b83861115615b66575f5ffd5b5050820193919092039150565b634e487b7160e01b5f52603260045260245ffd5b5f60018201615b9857615b986155b5565b5060010190565b80356020831015612610575f19602084900360031b1b1692915050565b5f82615bd657634e487b7160e01b5f52601260045260245ffd5b500690565b5f81615be957615be96155b5565b505f190190565b5f8183825b6008811015615c14578151835260209283019290910190600101615bf5565b5050506101008201905092915050565b5f82518060208501845e5f920191825250919050565b5f60208284031215615c4a575f5ffd5b815180151581146103f4575f5ffd5b634e487b7160e01b5f52602160045260245ffdfe30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4730644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", + "deployedBytecode": "0x608060405234801561000f575f5ffd5b5060043610610029575f3560e01c8063ea50d0e41461002d575b5f5ffd5b61004061003b3660046154f1565b610054565b604051901515815260200160405180910390f35b5f5f61007f7f00000000000000000000000000000000000000000000000000000000000000006102ee565b905061008c8160206155c9565b85146100ee577f0000000000000000000000000000000000000000000000000000000000000000856100bf8360206155c9565b6040516359895a5360e01b81526004810193909352602483019190915260448201526064015b60405180910390fd5b5f6100f76103fb565b90505f61012588887f0000000000000000000000000000000000000000000000000000000000000000610410565b90506010826040015161013891906155e0565b85146101575760405163fa06659360e01b815260040160405180910390fd5b60405163995bf45760e01b81525f9073__$f25d01d3eaf7d03206a1c3f23eb1654acf$__9063995bf457906101fa9085908b908b907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090600401615763565b610be060405180830381865af4158015610216573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061023a9190615a8f565b905061028a8787808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050855185516060810151608090910151919350915060016108a9565b815160a0015261029a8282610a10565b6102b7576040516313f8744360e31b815260040160405180910390fd5b6102c2828483610c17565b6102df576040516352ec174560e11b815260040160405180910390fd5b50600198975050505050505050565b5f8060026102fe60016008615b39565b61030891906155c9565b9050610316600260036155c9565b6103209082615b39565b9050600161032f6009856155c9565b61033991906155c9565b6103439082615b39565b90506001610352816029615b39565b61035c91906155c9565b6103669082615b39565b9050610374600160026155c9565b61037e9082615b39565b905061038b6001846155c9565b6103959082615b39565b90506103a3600160046155c9565b6103ad9082615b39565b905060026103bc6001856155e0565b6103c691906155c9565b6103d09082615b39565b90506103dd6002806155c9565b6103e79082615b39565b90506103f4601082615b39565b9392505050565b610403615024565b61040b611c14565b905090565b6104186151ae565b5f805b601081101561047a57610447868387610435602083615b39565b9261044293929190615b4c565b61254e565b8351826010811061045a5761045a615b73565b6020020181815250506020826104709190615b39565b915060010161041b565b5061049e85828661048c604083615b39565b9261049993929190615b4c565b612561565b60208301526104ae604082615b39565b90506104c185828661048c604083615b39565b6040808401919091526104d49082615b39565b90506104e785828661048c604083615b39565b60608301526104f7604082615b39565b905061050a85828661048c604083615b39565b608083015261051a604082615b39565b905061052d85828661048c604083615b39565b60c083015261053d604082615b39565b905061055085828661048c604083615b39565b60e0830152610560604082615b39565b905061057385828661048c604083615b39565b60a0830152610583604082615b39565b905061059685828661048c604083615b39565b6101008301526105a7604082615b39565b90506105ba85828661048c604083615b39565b6101208301526105cb604082615b39565b90506105de85828661048c604083615b39565b610140830151526105f0604082615b39565b9050610603858286610435602083615b39565b610160830152610614602082615b39565b90505f5b83811015610692575f5b60098110156106895761063c878488610435602083615b39565b84610180015183601c811061065357610653615b73565b6020020151826009811061066957610669615b73565b60200201818152505060208361067f9190615b39565b9250600101610622565b50600101610618565b505f5b6106a160016029615b39565b8110156106f0576106b9868387610435602083615b39565b836101c0015182602a81106106d0576106d0615b73565b6020020181815250506020826106e69190615b39565b9150600101610695565b50610702858286610435602083615b39565b6101a0830152610713602082615b39565b905061072685828661048c604083615b39565b6101408301516020015261073b604082615b39565b905061074e85828661048c604083615b39565b61014083015160026020020152610766604082615b39565b90505f5b6107756001856155e0565b8110156107be5761078d86838761048c604083615b39565b836101e0015182601b81106107a4576107a4615b73565b60200201526107b4604083615b39565b915060010161076a565b505f5b83811015610811576107da868387610435602083615b39565b83610200015182601c81106107f1576107f1615b73565b6020020181815250506020826108079190615b39565b91506001016107c1565b505f5b60048110156108655761082e868387610435602083615b39565b836102200151826004811061084557610845615b73565b60200201818152505060208261085b9190615b39565b9150600101610814565b5061087785828661048c604083615b39565b610240830152610888604082615b39565b905061089b85828661048c604083615b39565b610260830152509392505050565b5f600180826108d6866108d1896108cc6108c78a6310000000615b39565b6125e3565b6125fb565b612616565b90505f6108f4876108ef8a6108cc6108c78b6001615b39565b61262f565b90505f5b61092360107f00000000000000000000000000000000000000000000000000000000000000006155e0565b811015610990575f61094d8c838151811061094057610940615b73565b6020026020010151612657565b905061095d866108cc8684612616565b955061096d856108cc8584612616565b9450610979848b612616565b9350610985838b61262f565b9250506001016108f8565b505f5b60108110156109f7575f8a82601081106109af576109af615b73565b602002015190506109c4866108cc8684612616565b95506109d4856108cc8584612616565b94506109e0848b612616565b93506109ec838b61262f565b925050600101610993565b50610a02848461266c565b9a9950505050505050505050565b5f5f610a2583606001518561016001516125fb565b905060015f5b7f0000000000000000000000000000000000000000000000000000000000000000811015610b17575f86610180015182601c8110610a6b57610a6b615b73565b602002015180519091505f90610a89908360015b6020020151612616565b9050848114610aab576040516313f8744360e31b815260040160405180910390fd5b5f876080015184601c8110610ac257610ac2615b73565b60200201519050610ad3838261267a565b9550610b07856108cc60016108d1856108cc8e604001518b601c8110610afb57610afb615b73565b6020020151600161262f565b9450505050806001019050610a2b565b50610b20615291565b5f5b6029811015610b70576101c0870151610b3c600183615b39565b602a8110610b4c57610b4c615b73565b6020020151828260298110610b6357610b63615b73565b6020020152600101610b22565b505f610b8582875f0151886020015186612828565b9050600160025b7f0000000000000000000000000000000000000000000000000000000000000000811015610be257610bd882896080015183601c8110610bce57610bce615b73565b60200201516125fb565b9150600101610b8c565b50610c08610bf5836108cc60018561262f565b6108d18a6101a001518a606001516125fb565b94909414979650505050505050565b5f610c206152b0565b5f610c4f8460c001517f00000000000000000000000000000000000000000000000000000000000000006128a1565b90505f7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03811115610c8a57610c8a615940565b604051908082528060200260200182016040528015610cb3578160200160208202803683370190505b5090505f7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03811115610cef57610cef615940565b604051908082528060200260200182016040528015610d2857816020015b610d15615320565b815260200190600190039081610d0d5790505b509050610d5b610d56876101000151855f81518110610d4957610d49615b73565b602002602001015161262f565b61296a565b84610120018181525050610d90610d56876101000151855f81518110610d8357610d83615b73565b6020026020010151612616565b610140850181905261012085015160e0880151610db1926108d191906125fb565b845260c0860151610de190610dc59061296a565b6108cc8661012001516108ef8a60e001518961014001516125fb565b6020850152815160019083905f90610dfb57610dfb615b73565b602002602001018181525050876102400151815f81518110610e1f57610e1f615b73565b6020908102919091010152600160a08501525f60c08501528351610e42906129da565b60408501526020840151610e55906129da565b606085015260015b610e6960016024615b39565b8111610f0a57610e8185604001518660a001516125fb565b838281518110610e9357610e93615b73565b602002602001018181525050610edc8560c001516108d18b6101c00151600185610ebd91906155e0565b602a8110610ecd57610ecd615b73565b60200201518860a001516125fb565b60c086015260a08086015190880151610ef591906125fb565b60a0860152610f0381615b87565b9050610e5d565b505f5b6005811015610fe3575f610f22601e83615b39565b90505f610f3160016024615b39565b610f3b9084615b39565b9050610f6c858381518110610f5257610f52615b73565b60200260200101516108d189606001518a60a001516125fb565b858381518110610f7e57610f7e615b73565b602002602001018181525050610fbb8760c001516108d18d6101c0015184602a8110610fac57610fac615b73565b60200201518a60a001516125fb565b60c088015260a080880151908a0151610fd491906125fb565b60a08801525050600101610f0d565b50876020015181600181518110610ffc57610ffc615b73565b602002602001018190525086606001518160028151811061101f5761101f615b73565b602002602001018190525086608001518160038151811061104257611042615b73565b60200260200101819052508660a001518160048151811061106557611065615b73565b60200260200101819052508660c001518160058151811061108857611088615b73565b60200260200101819052508660e00151816006815181106110ab576110ab615b73565b6020026020010181905250866101000151816007815181106110cf576110cf615b73565b6020026020010181905250866101200151816008815181106110f3576110f3615b73565b60200260200101819052508661014001518160098151811061111757611117615b73565b602002602001018190525086610160015181600a8151811061113b5761113b615b73565b6020026020010181905250866101c0015181600b8151811061115f5761115f615b73565b602002602001018190525086610180015181600c8151811061118357611183615b73565b6020026020010181905250866101a0015181600d815181106111a7576111a7615b73565b6020026020010181905250866101e0015181600e815181106111cb576111cb615b73565b602002602001018190525086610200015181600f815181106111ef576111ef615b73565b60200260200101819052508661022001518160108151811061121357611213615b73565b60200260200101819052508661024001518160118151811061123757611237615b73565b60200260200101819052508661026001518160128151811061125b5761125b615b73565b60200260200101819052508661028001518160138151811061127f5761127f615b73565b6020026020010181905250866102a00151816014815181106112a3576112a3615b73565b6020026020010181905250866102c00151816015815181106112c7576112c7615b73565b6020026020010181905250866102e00151816016815181106112eb576112eb615b73565b60200260200101819052508661030001518160178151811061130f5761130f615b73565b60200260200101819052508661032001518160188151811061133357611333615b73565b60200260200101819052508661034001518160198151811061135757611357615b73565b602002602001018190525086610360015181601a8151811061137b5761137b615b73565b602002602001018190525086610380015181601b8151811061139f5761139f615b73565b6020026020010181905250866103a0015181601c815181106113c3576113c3615b73565b6020026020010181905250866103c0015181601d815181106113e7576113e7615b73565b6020026020010181905250876040015181601e8151811061140a5761140a615b73565b6020026020010181905250876060015181601f8151811061142d5761142d615b73565b602002602001018190525087608001518160208151811061145057611450615b73565b60200260200101819052508760a001518160218151811061147357611473615b73565b60200260200101819052508761012001518160228151811061149757611497615b73565b6020026020010181905250876101000151816023815181106114bb576114bb615b73565b60200260200101819052508760c00151816024815181106114de576114de615b73565b60200260200101819052508760e001518160258151811061150157611501615b73565b60200260200101819052505f61154787608001518660c001518b6102000151877f00000000000000000000000000000000000000000000000000000000000000006129ec565b9050611571815f8151811061155e5761155e615b73565b60200260200101518661012001516125fb565b608086018190526102008a01515160e08901516115a292916108d19161159791906125fb565b8861014001516125fb565b608086015260e08701516115b590612b4a565b60a08601525f6115c760016024615b39565b6115d2906001615b39565b90505f5b61160160017f00000000000000000000000000000000000000000000000000000000000000006155e0565b811015611812575f61163460017f00000000000000000000000000000000000000000000000000000000000000006155e0565b8210159050806117a357611668610d568b6101000151898560016116589190615b39565b81518110610d4957610d49615b73565b6101208901526101008a015161169890610d569089611688866001615b39565b81518110610d8357610d83615b73565b61014089015260a08801516101208901516116b391906125fb565b61016089015260a088015160e08b01516116db916116d0916125fb565b8961014001516125fb565b61018089018190526116fe906116f0906129da565b6108d18a61016001516129da565b866117098486615b39565b8151811061171957611719615b73565b6020026020010181815250505f6117528961018001518e61020001518560016117429190615b39565b601c8110610bce57610bce615b73565b905061178c816108d18b61016001518887600161176f9190615b39565b8151811061177f5761177f615b73565b60200260200101516125fb565b905061179c896080015182612616565b60808a0152505b6117c26117b88960a001518c60e001516125fb565b8b60e001516125fb565b60a08901526101e08c015182601b81106117de576117de615b73565b6020020151856117ee8486615b39565b815181106117fe576117fe615b73565b6020908102919091010152506001016115d6565b5061183e60017f00000000000000000000000000000000000000000000000000000000000000006155e0565b6118489082615b39565b90506118686118608961010001518a60c0015161262f565b60019061266c565b60e08701515261010088015160c08901516118ac91611860916108ef907f07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76906125fb565b60e0878101805160200192909252815180516040909101529051805160609091015260a0870151908901516118ee916118e4916125fb565b8960e001516125fb565b60a08701525f5b600481101561199c575f6119278860e00151836004811061191857611918615b73565b60200201518960a001516125fb565b9050611932816129da565b886101000151836004811061194957611949615b73565b602002015260a088015160e08b015161196291906125fb565b8860a001818152505061198e88608001516108d1838f61022001518660048110610bce57610bce615b73565b6080890152506001016118f5565b506101008601515184518590839081106119b8576119b8615b73565b602090810291909101810191909152610100870151908101516119dc916002610a7f565b846119e8836001615b39565b815181106119f8576119f8615b73565b60209081029190910101526101008601516060015184611a19836002615b39565b81518110611a2957611a29615b73565b60209081029190910101525f5b6003811015611a8b578a61014001518160038110611a5657611a56615b73565b60200201518483611a6681615b87565b945081518110611a7857611a78615b73565b6020908102919091010152600101611a36565b506040518060400160405280600181526020016002815250838281518110611ab557611ab5615b73565b60200260200101819052508560800151848280611ad190615b87565b935081518110611ae357611ae3615b73565b602002602001018181525050611b0d8a61022001518960c001518a608001518d6101a00151612b55565b611b2a5760405163a2a2ac8360e01b815260040160405180910390fd5b5f8a6102600151905080848381518110611b4657611b46615b73565b6020026020010181905250886101000151858381518110611b6957611b69615b73565b602002602001018181525050611b7d615338565b611b878587612eb8565b8152611b9282612fb0565b602082018190528c5182515f92611baa929190612ff6565b90505f5f611bba8f5f0151613087565b91509150611bc782613148565b611bd081613148565b8351611bdd9083856131e7565b84526020840151611bef9082856131e7565b602085018190528451611c0191613216565b9f9e505050505050505050505050505050565b611c1c615024565b50604080516103e0810182526220000081526015602080830191909152601282840152825180840184527f286493b1bc5a9674ed3d063c79269512944b6dfb58b880361d36fce8a61c981481527f1ef073ead7f7fd307a1a357548e4d8c9f5d03d5a7191cea80b2ba0cf03d18d29818301526060830152825180840184527f216ae5e3bf2a41f792d13f7028ec70fb78ac8d5fa4834341acad58d3453bf12381527f2ef50ace63f2e23d4b2a184ce511e1b7781287457bcc135459c5c56de1877b93818301526080830152825180840184527f04874f63e091441575d7f87163ad2d04fa3e4b49cee4cde183081c90e13ff17f81527f1547db64a1675f5ab99f4dc4cf2af5cfd0ef1fbdb68977d8abc8f7112e88bf808183015260a0830152825180840184527f17094eb4ca2210abcb1b4d41cf1355b04dd66b014d42345e6eadf8407a74ed1a81527f2228aebc15e7852110c94a633f8fb944d71cb1a5cff1b5127d0e4eecf1a7726f8183015260c0830152825180840184527f15cfc9a869b9c86a839a67ab04b780b3049b2d5f10a901055f85d4717428f6dc81527f2aec2a8cee64e9da3981b9d6f9f00faffbe2ece25cd4f57d80b8552d81c716b58183015260e0830152825180840184527f232463dd05273276d135bcdfe18a8b299585d6e0fe220dfafe63a54293476dfe81527f17f763713bb623ce792152d0aabd32b83ca5a6abecb97ece41dc00a1bb6296ad81830152610100830152825180840184527f1ad0852ae1f831506b33a76a4968849abaf87e068b8e00ba462e190ff4dc0ff981527f2b782e70b28e331338317e348ecb3d3ee69c0cb7b1ca634b32395b5349605a9381830152610120830152825180840184527f1a03cf866e6a54045329039071909e3129fefe1a3f380a5db12e3156f6bed47e81527f1bdf1ec93f5b5962d4aac343eaa211a5b8021546f0dc225d03f39cb465546d5a81830152610140830152825180840184527f1940cce3f764969daf23bd6cf6d09ecd7e50392878ce2ba3a0a30c88f619cd3781527f06d183a757a5b2294f8e3b10d3304744bc4d7b68246b01563c48dfd3ab44384281830152610160830152825180840184527f11a96a0fb134b4ad2a3ac7b54fc7989a18ada93ac73e0e7bfb7134f334854c5581527f166911402df98ab7053779cb1f431384018e058cbe2ac79d1d5ff020ce5474d981830152610180830152825180840184527f11b342da18e6325f40bddb950e3ba058fe013e0a6757512f022a0d9961d3d46881527f04225227552dcaa71ff7c4571eebd04ca40091a33ff91d1b220af458dffca289818301526101a0830152825180840184527f1b5b7f1276e5867eef1d033df65d003c591c16bf64bd2d4cb89bccc1a98ab3dd81527f28f55668465e42d873d91d75906232c6f05bf37be890e4d4c0ef6a1d5df8c211818301526101c0830152825180840184527f18ae1c0c284757fd6a6bcf60413a8e2334fcb46562ea30b9cb9444a66349346381527f1cade59f171ec1d94348309a02e1642067d6e6b43eb254837e703c1488dff221818301526101e0830152825180840184527f2537fc03ae8cfc49498d0a04704d77e10d6477d6b7c5e515cf24dadab0dee87481527f0850d14d62e2c194a69110f3aefa10d6caea9bf6ff940331877c5f49e41e5fa781830152610200830152825180840184527f02f24bc3b670a81abc44133de34592b13bb3a5ea67cdaa1d73ad35965b8abe4981527f0c62df1ee51bfbaeba2a75c1cc8aa8a2ea3222f21bc0b8958bd43c3aa749e96381830152610220830152825180840184527f21ac9dc80bd7f54e6817d224b7650c4176407a1109f92567c05676ac1fe0973c81527f2d325ad91e20be879c716023bcde84d469c4f7867c12bb321c387e4a1036f86d81830152610240830152825180840184527f25b123800356ccc6d7a609989a0e4fc132bf757389ca25f0c1db7612bbec234981527f03832b0106e3581612f83d158277886cc9e5637790fccabde84b3218220f2a4881830152610260830152825180840184527f25962cb0996ebaeea7b01f8860fde2547f8bb738291e53c7f25a4ac80af4fd2e81527f22ec82955d2dc95fddc9cf9fd4f7aa555635abb02cf6eeb7ac3df225c8b62d5281830152610280830152825180840184527f1332d280da268502ba2d853e14e53baf604c7a6a41b7e70bdf2e861cc6a5982181527f0d14761f5281d5dc1209b25146d92d9e5692294f818c8c3468c9921f7a7cce79818301526102a0830152825180840184527f1b215ab72c8af91207dee6f8ac01f0e0d51895a9f670d7173c7b6d5b80a3908581527f04ee544edd280fa185c8d9886c9e19832ef71d67fab5093eadeec49ccc5a34b1818301526102c0830152825180840184527f11005f2ce25b6f954b89343fa059782ab8ecdc84d411c828cc7dfd54f796b6f681527f0c68694f3595aecaaffdff52c0fa5f1d05fa8c7ba651da1220634fa4b8867ba7818301526102e0830152825180840184527f1145c2f8face74c268cc68c772d99c59587659cbd594a1aef32a36ab556621e281527f1ffa60a61a074aa68c8412fd9f3b4ae61437cf3ff3f0497e1244ba56370d7b9181830152610300830152825180840184527f099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d2681527e15b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f81830152610320830152825180840184527f1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e81527f305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d1981830152610340830152825180840184527f061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d81527f1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e81830152610360830152825180840184527f043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce81527f261522c4089330646aff96736194949330952ae74c573d1686d9cb4a007338548183015261038083015282518084018452600181526002818301526103a083015282518084019093527f1f0071e7d3b9be4c8376ff230b3b8b242585d403d25463022d38e574cb0b575483527f19cb572dac34151573e3024878327a8ea7febb3d76c6171b10d1e3f2f2907584908301526103c081019190915290565b5f6103f461255c8385615b9f565b612657565b612569615320565b60408051808201909152805f516020615c6e5f395f51905f5261258f60205f8789615b4c565b61259891615b9f565b6125a29190615bbc565b81526020908101905f516020615c6e5f395f51905f52906125c7906040908789615b4c565b6125d091615b9f565b6125da9190615bbc565b90529392505050565b5f5f516020615c8e5f395f51905f52825b0692915050565b5f5f516020615c8e5f395f51905f5282840990505b92915050565b5f5f516020615c8e5f395f51905f528284089392505050565b5f5f516020615c8e5f395f51905f52825f516020615c8e5f395f51905f520384089392505050565b5f5f516020615c8e5f395f51905f52826125f4565b5f6103f4836108cc8461296a565b5f5f604051806101200160405280619d8081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec5181526020016105a081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31815260200161024081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd3181526020016105a081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec518152602001619d8081525090505f600190505f5f90505b600981101561277357612769826108cc878461262f565b9150600101612752565b5061277c61534b565b5f5b60098110156127cd576127ae610d5685836009811061279f5761279f615b73565b60200201516108cc898561262f565b8282600981106127c0576127c0615b73565b602002015260010161277e565b505f5b600981101561281357612809856108d18984600981106127f2576127f2615b73565b6020020151858560098110610bce57610bce615b73565b94506001016127d0565b5061281e84836125fb565b9695505050505050565b5f61283161536a565b61283c86828561340a565b612848868683866135b6565b612854868683866137a2565b61285f8682856139c7565b61286a868285613bbb565b61287686868386613f04565b6128818682856143b0565b61288c8682856147c2565b612897868285614b83565b61281e8185614e83565b60605f826001600160401b038111156128bc576128bc615940565b6040519080825280602002602001820160405280156128e5578160200160208202803683370190505b50905083815f815181106128fb576128fb615b73565b602090810291909101015260015b838110156129625761293d826129206001846155e0565b8151811061293057612930615b73565b6020026020010151612b4a565b82828151811061294f5761294f615b73565b6020908102919091010152600101612909565b509392505050565b5f5f8290505f604051602081526020808201526020604082015282606082015260025f516020615c8e5f395f51905f520360808201525f516020615c8e5f395f51905f5260a082015260205f60c08360055afa806129c6575f5ffd5b505f51608091909101604052949350505050565b5f516020615c8e5f395f51905f520390565b60605f826001600160401b03811115612a0757612a07615940565b604051908082528060200260200182016040528015612a30578160200160208202803683370190505b509050825b8015612b3f575f85612a486001846155e0565b81518110612a5857612a58615b73565b602002602001015190505f89600184612a7191906155e0565b601c8110612a8157612a81615b73565b602002015190505f612adc612aa0612a99858d6125fb565b60026125fb565b6108ef8b612aaf6001896155e0565b601c8110612abf57612abf615b73565b60200201516108cc612ad6886108cc60018a61262f565b8761262f565b9050612afd816108cc610d56612af7876108cc60018961262f565b86612616565b99508990508085612b0f6001876155e0565b81518110612b1f57612b1f615b73565b60200260200101818152505050505080612b3890615bdb565b9050612a35565b509695505050505050565b5f61261082836125fb565b5f600181612b6e612b6887610100614edc565b8361262f565b905080612b8e5760405163835eb8f760e01b815260040160405180910390fd5b612b96615389565b80518390525f5b7f0000000000000000000000000000000000000000000000000000000000000000811015612c7f575f612bd18260096155c9565b612bdc906001615b39565b905084835f0151826101008110612bf557612bf5615b73565b60200201525f612c06826001615b39565b90505b612c14600983615b39565b811015612c75578351612c5490612c2c6001846155e0565b6101008110612c3d57612c3d615b73565b60200201518a85601c8110610bce57610bce615b73565b8451826101008110612c6857612c68615b73565b6020020152600101612c09565b5050600101612b9d565b50608081018390525f602082018190525b610100811015612d9057612cb1612cab83608001518a6125fb565b8561262f565b8260a00151826101008110612cc857612cc8615b73565b602002015260a0820151612cf290826101008110612ce857612ce8615b73565b602002015161296a565b8260a00151826101008110612d0957612d09615b73565b602002018181525050612d4f82602001516108d1845f0151846101008110612d3357612d33615b73565b60200201518560a00151856101008110610bce57610bce615b73565b60208301526080820151612d83907f204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d66125fb565b6080830152600101612c90565b505f612da1836108cc61010061296a565b9050612db18260200151826125fb565b602083015260a0820151612dcc905f5b6020020151826125fb565b604083015260a0820151612df890612de760016101006155e0565b6101008110612dc157612dc1615b73565b60608301526040820151612e0e908a6002610bce565b60c08301819052612e6e906108d1612e468b7f204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d661262f565b60208d015160408e01516108cc91612e5d9161262f565b8e5160208901516108ef91906125fb565b60c083018190526060830151612ea391612e97916108d1906108cc8e600260200201518c61262f565b6108ef858c6003610bce565b60c08301819052159998505050505050505050565b612ec0615320565b7f00000000000000000000000000000000000000000000000000000000000000005f5b81811015612f1557612f0d858281518110612f0057612f00615b73565b6020026020010151613148565b600101612ee3565b50604051600190815b60018401811015612f7a5760208102870160208202870181515160408501528151602001516060850152805160808501525050604080830160606040850160075afa8316925060408260808460065afa90921691600101612f1e565b5080518452602081015160208501525080612fa8576040516352ec174560e11b815260040160405180910390fd5b505092915050565b612fb8615320565b5f516020615c6e5f395f51905f5282602001515f516020615c6e5f395f51905f52612fe391906155e0565b612fed9190615bbc565b60208301525090565b5f5f5f61300286613087565b9150915061300e6153cd565b82518152602080840151818301528251604080840191909152838201516060840152875160808401528782015160a0840152865160c08401528682015160e08401525161307c9161306191849101615bf0565b60405160208183030381529060405280519060200120612657565b979650505050505050565b61308f615320565b613097615320565b82516020808501516040860151606087015160cc90811b608892831b604494851b90961795909517949094178652608087015160a088015160c089015160e08a0151871b90841b91851b9092171717868401526101008701516101208801516101408901516101608a0151871b90841b91851b909217171785526101808701516101a08801516101c08901516101e09099015190951b9790911b9390911b1791909117939093179281019290925291565b805160208201515f5f516020615c6e5f395f51905f528380095f516020615c6e5f395f51905f5260035f516020615c6e5f395f51905f52838709085f516020615c6e5f395f51905f5284850914915050806131e15760405162461bcd60e51b8152602060048201526019602482015278706f696e74206973206e6f74206f6e2074686520637572766560381b60448201526064016100e5565b50505050565b6131ef615320565b6131f7615320565b6132018386614f3d565b905061320d8185614f93565b95945050505050565b81516020808401518351848301516040805194850195909552938301919091527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260608301527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed60808301527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b60a08301527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60c083015260e08201526101008101919091527f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c16101208201527f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b06101408201527f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe46101608201527f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e556101808201525f9081906101a00160405160208183030381529060405290505f5f60086001600160a01b0316836040516133ac9190615c24565b5f60405180830381855afa9150503d805f81146133e4576040519150601f19603f3d011682016040523d82523d5f602084013e6133e9565b606091505b509150915081801561281e57508080602001905181019061281e9190615c3a565b5f613416846007614ff4565b90507f183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f80000005f61347761347161344b85600361262f565b6108cc61346661345b8b5f614ff4565b6108cc8c601d614ff4565b6108cc8b601c614ff4565b836125fb565b90506134f86134ed6134d46134bb6134a2856108d16134978d6002614ff4565b6108cc8e601c614ff4565b6108d16134b08c6003614ff4565b6108cc8d601d614ff4565b6108d16134c98b6004614ff4565b6108cc8c601e614ff4565b6108d16134e28a6005614ff4565b6108cc8b601f614ff4565b6108d1886001614ff4565b9050613517816108d161350c86600161262f565b6108cc8a6027614ff4565b905061352381846125fb565b905061352f81856125fb565b8552505f905061356961355f61355461354988601c614ff4565b6108d189601f614ff4565b6108ef886024614ff4565b6108d1875f614ff4565b905061357a816108cc84600261262f565b905061358b816108cc84600161262f565b905061359781836125fb565b90506135a381846125fb565b9050808460015b60200201525050505050565b5f5f5f6135ec6135e26135ca89601c614ff4565b6108d16135d88b6012614ff4565b8a606001516125fb565b8760800151612616565b9050613625816108cc61361b6136038b601d614ff4565b6108d16136118d6013614ff4565b8c606001516125fb565b8960800151612616565b905061364a816108cc61361b61363c8b601e614ff4565b6108d16136118d6014614ff4565b905061366f816108cc61361b6136618b601f614ff4565b6108d16136118d6015614ff4565b92505f90506136936135e261368589601c614ff4565b6108d16135d88b600e614ff4565b90506136b8816108cc61361b6136aa8b601d614ff4565b6108d16136118d600f614ff4565b90506136dd816108cc61361b6136cf8b601e614ff4565b6108d16136118d6010614ff4565b9050613702816108cc61361b6136f48b601f614ff4565b6108d16136118d6011614ff4565b91505f9050613729613723613718896020614ff4565b6108d18a601a614ff4565b846125fb565b905061375e816108ef6137586137408b6028614ff4565b6108d161374e8d601b614ff4565b8c60a001516125fb565b856125fb565b905061376a81856125fb565b6040860152505f61378d61375861378289601b614ff4565b6108cc8a6028614ff4565b9050808560035b602002015250505050505050565b5f5f6138006137e86137d06137bb61361b8a6016614ff4565b6108d16137c98b6017614ff4565b8a516125fb565b6108d16137de8a6018614ff4565b89602001516125fb565b6108d16137f6896019614ff4565b88604001516125fb565b91505f61383761381e61381489601c614ff4565b8860800151612616565b6108d161382c8a6003614ff4565b6108cc8b6024614ff4565b90505f61386061384889601d614ff4565b6108d16138558b5f614ff4565b6108cc8c6025614ff4565b90505f61388a6138718a601e614ff4565b6108d161387f8c6001614ff4565b6108cc8d6026614ff4565b90506138c96138b16138a3856108d1868d5f01516125fb565b6108d1848c602001516125fb565b6108d16138bf8c6004614ff4565b8b604001516125fb565b93505050505f6138dd613723886021614ff4565b90505f6138ee613723896021614ff4565b90505f61392661390d6139028b6023614ff4565b6108d18c6006614ff4565b6108ef61391b8c6023614ff4565b6108cc8d6006614ff4565b90505f613944612b6861393987896125fb565b6108cc8d6021614ff4565b905061395081886125fb565b90505f61397861396a6139648d6006614ff4565b876125fb565b6108ef6139648e6022614ff4565b90505f6139868c6023614ff4565b90505f613996612b6883846125fb565b60808c0185905260a08c0184905290506139b0818b6125fb565b8b6006602002015250505050505050505050505050565b5f6139d35f600161262f565b90505f6139e15f600261262f565b90505f6139ef5f600361262f565b90505f613a0b613a0088601d614ff4565b6108ef89601c614ff4565b90505f613a27613a1c89601e614ff4565b6108ef8a601d614ff4565b90505f613a43613a388a601f614ff4565b6108ef8b601e614ff4565b90505f613a5f613a548b6024614ff4565b6108ef8c601f614ff4565b905083613a70816108cc818b612616565b9050613a80816108cc878a612616565b9050613a90816108cc8789612616565b9050613aa1816108cc8d6008614ff4565b9050613aad818a6125fb565b60e08b01525082613ac2816108cc818b612616565b9050613ad2816108cc868a612616565b9050613ae2816108cc8689612616565b9050613af3816108cc8d6008614ff4565b9050613aff818a6125fb565b6101008b01525081613b15816108cc818b612616565b9050613b25816108cc858a612616565b9050613b35816108cc8589612616565b9050613b46816108cc8d6008614ff4565b9050613b52818a6125fb565b6101208b01525080613b68816108cc818b612616565b9050613b78816108cc848a612616565b9050613b88816108cc8489612616565b9050613b99816108cc8d6008614ff4565b9050613ba5818a6125fb565b610140909a019990995250505050505050505050565b613bf46040518060e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b613bff84601d614ff4565b8152613c0c84601e614ff4565b6020820152613c1c846024614ff4565b6040820152613c2c846027614ff4565b6060820152613c3c846026614ff4565b6080820152613c4c846025614ff4565b60a08201525f613c5d856002614ff4565b90505f613c6a865f614ff4565b90505f613c7e8460400151855f015161262f565b90505f613c93856020015186602001516125fb565b606086015190915086905f90613ca990806125fb565b90505f613cc7613cc189602001518a606001516125fb565b886125fb565b90505f613ce6613cdf8a60a001518b60400151612616565b8a51612616565b9050613cf561396482886125fb565b9050613d1c613d16613d10613d0a848761262f565b8861262f565b84612616565b83612616565b9050613d44613d39613d2e83876125fb565b6108cc8f6009614ff4565b6108cc60018a61262f565b6101608c015250505050602085015160808601515f91613d6391612616565b90505f613d81613d778860600151886125fb565b886020015161262f565b90505f613da5613d9184876125fb565b6108d16137588b60a001518c5f015161262f565b9050613dcd613dc2613db7838c6125fb565b6108cc8e6009614ff4565b6108cc60018961262f565b6101808b0152505f9150613dee9050613de7836011612616565b87516125fb565b90505f613dfb8384612616565b9050613e078182612616565b90505f613e158360096125fb565b9050613e3e613e38613723613e318b60a001518c5f0151612616565b8b51612616565b8261262f565b60c089018190525f90613e5990613cc190613d2e908d6125fb565b9050613e6c8b600b602002015182612616565b6101608c0152505086515f9250613e939150613de790613e8c9080612616565b8851612616565b90505f613ed3613eae836108cc8a5f01518b60a0015161262f565b60208901516108ef90613ec19080612616565b6108cc8b602001518c60800151612616565b9050613ef089600c60200201516108d1613cc1613d2e858d6125fb565b89600c602002015250505050505050505050565b613f6e604051806101e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b613f86613f7c86601e614ff4565b85604001516125fb565b808252613fa5906108d1613f9b88601d614ff4565b87602001516125fb565b808252613fba906108d1613de788601c614ff4565b808252613fcc906108d1876001614ff4565b80825260208201819052613fe5906108ef87601f614ff4565b8152614000613ff5866024614ff4565b6108ef87601c614ff4565b608082015261401e614013866027614ff4565b6108ef87601f614ff4565b60608201526080810151614037906108cc81600161262f565b6101c082015260808101516140779061406d90614066906108cc60015f516020615c8e5f395f51905f526155e0565b6001612616565b82606001516125fb565b60a082018190526140ad9061409f906108cc614094896002614ff4565b6108cc8a6003614ff4565b6108cc61375888600a614ff4565b83600e60200201526101c08101516140d19061409f906108cc614094896002614ff4565b6101e084015280516140f6906108cc6140eb886002614ff4565b6108cc896003614ff4565b6101208201525f61411561410b87601f614ff4565b836020015161262f565b9050614126816108cc83600161262f565b60e0830152614143614139876026614ff4565b86604001516125fb565b60408301819052614166906108d161415c896025614ff4565b88602001516125fb565b60408301819052614186906108d161417f896024614ff4565b88516125fb565b60408301526141a3614199876027614ff4565b836040015161262f565b60408301525f6141c26141b7886026614ff4565b6108ef89601e614ff4565b90506142146141ef613471614066866080015160015f516020615c8e5f395f51905f526108cc91906155e0565b6108cc614066866040015160015f516020615c8e5f395f51905f526108cc91906155e0565b60c084015260408301516142369061422c90806125fb565b846040015161262f565b61010084015260c083015161426290614254906108cc8a6004614ff4565b6108cc6139648a600a614ff4565b6102008601526101c083015161428190614254906108cc8a6004614ff4565b6102208601526101008301516142a090614254906108cc8a6004614ff4565b61024086015260e08301516142ba906108cc896004614ff4565b6101408401526142d96142ce886025614ff4565b6108ef89601d614ff4565b6101608401526080830151614317906141b79061430c90614066906108cc60015f516020615c8e5f395f51905f526155e0565b8561016001516125fb565b61018084018190526101208401516101a0850181905261434e916108d1906108cc6143438c6005614ff4565b6108cc8d6002614ff4565b6101a08401819052835161436e91906108d1906108cc6143438c5f614ff4565b6101a084018190526101408401516143869190612616565b6101a084018190526143a0906108cc6139648a600a614ff4565b6101a0840181905285600d613794565b6143e96040518060e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b61441e6144056143fa86601c614ff4565b6108cc876025614ff4565b6108d1614413876024614ff4565b6108cc88601d614ff4565b815261446361445861443f61443487601c614ff4565b6108cc88601f614ff4565b6108d161444d88601d614ff4565b6108cc89601e614ff4565b6108ef866026614ff4565b6040820181905261447890600160441b6125fb565b6040820181905261448e906108ef866027614ff4565b6040820181905281516144a19190612616565b604082018190526144b7906108cc866005614ff4565b604082015280516144cc90600160441b6125fb565b8082526144ec906108d16144e1876024614ff4565b6108cc886025614ff4565b80825260208201819052614513906108ef61450887601e614ff4565b6108d188601f614ff4565b60208201819052614529906108cc866004614ff4565b6020820152805160608201819052614546906108d186601f614ff4565b6060820181905261456a906108ef61455f876026614ff4565b6108d1886027614ff4565b6060820181905261457f906108cc865f614ff4565b8160600181815250505f6145a861459e83602001518460400151612616565b8360600151612616565b90506145b9816108cc876003614ff4565b90506145d16145c9866025614ff4565b6140006125fb565b608083018190526145e7906108d1876024614ff4565b608083018190526145fa906140006125fb565b60808301819052614610906108d187601e614ff4565b60808301819052614623906140006125fb565b60808301819052614639906108d187601d614ff4565b6080830181905261464c906140006125fb565b60808301819052614662906108d187601c614ff4565b60808301819052614678906108ef87601f614ff4565b6080830181905261468e906108cc876005614ff4565b60808301526146a16145c9866026614ff4565b60a083018190526146b7906108d1876025614ff4565b60a083018190526146ca906140006125fb565b60a083018190526146e0906108d1876024614ff4565b60a083018190526146f3906140006125fb565b60a08301819052614709906108d187601f614ff4565b60a0830181905261471c906140006125fb565b60a08301819052614732906108d187601e614ff4565b60a08301819052614748906108ef876027614ff4565b60a0830181905261475d906108cc875f614ff4565b60a0830181905260808301515f916147759190612616565b9050614786816108cc886004614ff4565b90506147928282612616565b60c084018190526147ab906108cc61396489600b614ff4565b60c084018190528560136020020152505050505050565b6148386040518061022001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b61485161484685601c614ff4565b6108d1866002614ff4565b815261486c61486185601d614ff4565b6108d1866003614ff4565b602082015261488a61487f85601e614ff4565b6108d1866004614ff4565b60408201526148a861489d85601f614ff4565b6108d1866005614ff4565b606082015280516148da906148d3906148cc906148c590806125fb565b84516125fb565b83516125fb565b82516125fb565b608082015260208101516149189061490e90614904906148fa90806125fb565b84602001516125fb565b83602001516125fb565b82602001516125fb565b60a082015260408101516149569061494c906149429061493890806125fb565b84604001516125fb565b83604001516125fb565b82604001516125fb565b60c0820152606081015161498a9061406d906149809061497690806125fb565b84606001516125fb565b83606001516125fb565b60e0820152608081015160a08201516149a39190612616565b61010082015260c081015160e08201516149bd9190612616565b61012082015260a08101516149e1906149d69080612616565b826101200151612616565b61014082015260e0810151614a05906149fa9080612616565b826101000151612616565b610160820152610120810151614a1b9080612616565b6101e08201819052614a3c90614a319080612616565b826101600151612616565b6101e0820152610100810151614a529080612616565b6101a08201819052614a7390614a689080612616565b826101400151612616565b6101a08201819052610160820151614a8a91612616565b6101808201526101408101516101e0820151614aa69190612616565b6101c0820152614aba61347185600c614ff4565b6102008201819052610280840151610180830151614ae5926108d1916108cc906108ef8a6024614ff4565b8360146020020152614b1583601560200201516108d18361020001516108cc856101a001516108ef8a6025614ff4565b8360156020020152614b4583601660200201516108d18361020001516108cc856101c001516108ef8a6026614ff4565b8360166020020152614b7583601760200201516108d18361020001516108cc856101e001516108ef8a6027614ff4565b836017602002015250505050565b614bd56040518061016001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b5f6040518060800160405280614c0a7f10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e76125e3565b8152602001614c387f0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b6125e3565b8152602001614c657e544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac156125e3565b8152602001614c937f222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b6125e3565b90529050614cb0614ca586601c614ff4565b6108d1876002614ff4565b6101208301819052614cef90614ce490614cd990614cce90806125fb565b8561012001516125fb565b8461012001516125fb565b8361012001516125fb565b8252614cfc85601d614ff4565b6020830152614d0c85601e614ff4565b6040830152614d1c85601f614ff4565b606083015281516020830151614d449161459e91614d3a9190612616565b8460400151612616565b6080830152614d5761372386600d614ff4565b6101408301528151614d7890614d6e90835f610bce565b8360800151612616565b60a0830152614da484601860200201516108d18461014001516108cc8660a001516108ef8b6024614ff4565b6103008501526020820151614dbf90614d6e90836001610bce565b60c0830152614deb84601960200201516108d18461014001516108cc8660c001516108ef8b6025614ff4565b6103208501526040820151614e0690614d6e90836002610bce565b60e0830152614e3284601a60200201516108d18461014001516108cc8660e001516108ef8b6026614ff4565b6103408501526060820151614e4d90614d6e90836003610bce565b610100830152614e7b84601b60200201516108d18461014001516108cc8661010001516108ef8b6027614ff4565b84601b6135aa565b815160015b601c811015614ed557614ecb826108d18684601c8110614eaa57614eaa615b73565b602002015186614ebb6001876155e0565b601b8110610bce57610bce615b73565b9150600101614e88565b5092915050565b5f5f8390505f60405160208152602080820152602060408201528260608201528460808201525f516020615c8e5f395f51905f5260a082015260205f60c08360055afa80614f28575f5ffd5b505f5160809190910160405295945050505050565b614f45615320565b614f4d615320565b604051835181526020840151602082015284604082015260408160608360075afa80614f77575f5ffd5b5080518252602080820151908301526060016040529392505050565b614f9b615320565b614fa3615320565b6040518451815260208501516020820152835160408201526020840151606082015260408160808360065afa80614fd8575f5ffd5b5080518252602080820151908301526080016040529392505050565b5f8282602881111561500857615008615c59565b6029811061501857615018615b73565b60200201519392505050565b604051806103e001604052805f81526020015f81526020015f815260200161504a615320565b8152602001615057615320565b8152602001615064615320565b8152602001615071615320565b815260200161507e615320565b815260200161508b615320565b8152602001615098615320565b81526020016150a5615320565b81526020016150b2615320565b81526020016150bf615320565b81526020016150cc615320565b81526020016150d9615320565b81526020016150e6615320565b81526020016150f3615320565b8152602001615100615320565b815260200161510d615320565b815260200161511a615320565b8152602001615127615320565b8152602001615134615320565b8152602001615141615320565b815260200161514e615320565b815260200161515b615320565b8152602001615168615320565b8152602001615175615320565b8152602001615182615320565b815260200161518f615320565b815260200161519c615320565b81526020016151a9615320565b905290565b6040518061028001604052806151c26153ec565b81526020016151cf615320565b81526020016151dc615320565b81526020016151e9615320565b81526020016151f6615320565b8152602001615203615320565b8152602001615210615320565b815260200161521d615320565b815260200161522a615320565b8152602001615237615320565b815260200161524461540b565b81526020015f8152602001615257615438565b81526020015f815260200161526a615466565b8152602001615277615485565b815260200161528461536a565b815260200161518f6154b3565b6040518061052001604052806029906020820280368337509192915050565b604051806101c001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020016152ee6154b3565b81526020016152fb6154b3565b81526020015f81526020015f81526020015f81526020015f8152602001606081525090565b60405180604001604052805f81526020015f81525090565b604051806040016040528061519c615320565b6040518061012001604052806009906020820280368337509192915050565b604051806103800160405280601c906020820280368337509192915050565b6040518060e0016040528061539c6154d1565b81526020015f81526020015f81526020015f81526020015f81526020016153c16154d1565b81526020015f81525090565b6040518061010001604052806008906020820280368337509192915050565b6040518061020001604052806010906020820280368337509192915050565b60405180606001604052806003905b615422615320565b81526020019060019003908161541a5790505090565b604051806103800160405280601c905b61545061534b565b8152602001906001900390816154485790505090565b604051806105400160405280602a906020820280368337509192915050565b604051806103600160405280601b905b61549d615320565b8152602001906001900390816154955790505090565b60405180608001604052806004906020820280368337509192915050565b604051806120000160405280610100906020820280368337509192915050565b5f5f5f5f60408587031215615504575f5ffd5b84356001600160401b03811115615519575f5ffd5b8501601f81018713615529575f5ffd5b80356001600160401b0381111561553e575f5ffd5b87602082840101111561554f575f5ffd5b6020918201955093508501356001600160401b0381111561556e575f5ffd5b8501601f8101871361557e575f5ffd5b80356001600160401b03811115615593575f5ffd5b8760208260051b84010111156155a7575f5ffd5b949793965060200194505050565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417612610576126106155b5565b81810381811115612610576126106155b5565b805f5b60108110156131e15781518452602093840193909101906001016155f6565b805f5b60038110156131e15761563684835180518252602090810151910152565b6040939093019260209190910190600101615618565b805f5b601c8110156131e1578151845f5b600981101561567c57825182526020928301929091019060010161565d565b50505061012093909301926020919091019060010161564f565b805f5b602a8110156131e1578151845260209384019390910190600101615699565b805f5b601b8110156131e1576156d984835180518252602090810151910152565b60409390930192602091909101906001016156bb565b805f5b601c8110156131e15781518452602093840193909101906001016156f2565b805f5b60048110156131e1578151845260209384019390910190600101615714565b8183525f6001600160fb1b0383111561574a575f5ffd5b8260051b80836020870137939093016020019392505050565b61576e8188516155f3565b5f602088015161578c61020084018280518252602090810151910152565b5060408801518051610240840152602090810151610260840152606089015180516102808501528101516102a0840152608089015180516102c08501528101516102e084015260a0890151805161030085015281015161032084015260c0890151805161034085015281015161036084015260e089015180516103808501528101516103a084015261010089015180516103c08501528101516103e084015261012089015180516104008501520151610420830152610140880151615855610440840182615615565b5061016088015161050083015261018088015161587661052084018261564c565b506101a08801516124a08301526101c08801516158976124c0840182615696565b506101e08801516158ac612a008401826156b8565b506102008801516158c16130c08401826156ef565b506102208801516158d6613440840182615711565b5061024088015180516134c08401526020908101516134e0840152610260890151805161350085015201516135208301526135c0613540830181905261591f9083018789615733565b613560830195909552506135808101929092526135a0909101529392505050565b634e487b7160e01b5f52604160045260245ffd5b60405161014081016001600160401b038111828210171561597757615977615940565b60405290565b604051601f8201601f191681016001600160401b03811182821017156159a5576159a5615940565b604052919050565b5f60c082840312156159bd575f5ffd5b60405160c081016001600160401b03811182821017156159df576159df615940565b604090815283518252602080850151908301528381015190820152606080840151908201526080808401519082015260a0928301519281019290925250919050565b5f82601f830112615a30575f5ffd5b5f610360615a3d8161597d565b915083018185821115615a4e575f5ffd5b845b82811015615a68578051825260209182019101615a50565b509195945050505050565b5f82601f830112615a82575f5ffd5b5f610380615a3d8161597d565b5f610be0828403128015615aa1575f5ffd5b50615aaa615954565b615ab484846159ad565b8152615ac38460c08501615a21565b6020820152615ad6846104208501615a73565b60408201526107a08301516060820152615af4846107c08501615a73565b6080820152610b4083015160a0820152610b6083015160c0820152610b8083015160e0820152610ba0830151610100820152610bc09092015161012083015250919050565b80820180821115612610576126106155b5565b5f5f85851115615b5a575f5ffd5b83861115615b66575f5ffd5b5050820193919092039150565b634e487b7160e01b5f52603260045260245ffd5b5f60018201615b9857615b986155b5565b5060010190565b80356020831015612610575f19602084900360031b1b1692915050565b5f82615bd657634e487b7160e01b5f52601260045260245ffd5b500690565b5f81615be957615be96155b5565b505f190190565b5f8183825b6008811015615c14578151835260209283019290910190600101615bf5565b5050506101008201905092915050565b5f82518060208501845e5f920191825250919050565b5f60208284031215615c4a575f5ffd5b815180151581146103f4575f5ffd5b634e487b7160e01b5f52602160045260245ffdfe30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4730644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", + "linkReferences": { + "project/contracts/verifier/RecursiveAggregationFoldVerifier.sol": { + "ZKTranscriptLib": [ + { + "length": 20, + "start": 689 + } + ] + } + }, + "deployedLinkReferences": { + "project/contracts/verifier/RecursiveAggregationFoldVerifier.sol": { + "ZKTranscriptLib": [ + { + "length": 20, + "start": 360 + } + ] + } + }, + "immutableReferences": { + "35080": [ + { + "length": 32, + "start": 91 + }, + { + "length": 32, + "start": 148 + }, + { + "length": 32, + "start": 257 + }, + { + "length": 32, + "start": 466 + }, + { + "length": 32, + "start": 2605 + }, + { + "length": 32, + "start": 2958 + }, + { + "length": 32, + "start": 3115 + }, + { + "length": 32, + "start": 5411 + }, + { + "length": 32, + "start": 5597 + }, + { + "length": 32, + "start": 5648 + }, + { + "length": 32, + "start": 6170 + }, + { + "length": 32, + "start": 11167 + } + ], + "35082": [ + { + "length": 32, + "start": 398 + } + ], + "35084": [ + { + "length": 32, + "start": 432 + }, + { + "length": 32, + "start": 2303 + } + ], + "35086": [ + { + "length": 32, + "start": 3156 + }, + { + "length": 32, + "start": 3257 + }, + { + "length": 32, + "start": 11970 + } + ] + }, + "inputSourceName": "project/contracts/verifier/RecursiveAggregationFoldVerifier.sol", + "buildInfoId": "solc-0_8_28-bd46eae6f77be9a8538850385226c1ee4fc57e42" +} \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/verifier/RecursiveAggregationFoldVerifier.sol/ZKTranscriptLib.json b/packages/enclave-contracts/artifacts/contracts/verifier/RecursiveAggregationFoldVerifier.sol/ZKTranscriptLib.json new file mode 100644 index 0000000000..de153d1923 --- /dev/null +++ b/packages/enclave-contracts/artifacts/contracts/verifier/RecursiveAggregationFoldVerifier.sol/ZKTranscriptLib.json @@ -0,0 +1,395 @@ +{ + "_format": "hh3-artifact-1", + "contractName": "ZKTranscriptLib", + "sourceName": "contracts/verifier/RecursiveAggregationFoldVerifier.sol", + "abi": [ + { + "inputs": [ + { + "components": [ + { + "internalType": "Fr[16]", + "name": "pairingPointObject", + "type": "uint256[16]" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "geminiMaskingPoly", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "w1", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "w2", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "w3", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "w4", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "lookupReadCounts", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "lookupReadTags", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "lookupInverses", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "zPerm", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point[3]", + "name": "libraCommitments", + "type": "tuple[3]" + }, + { + "internalType": "Fr", + "name": "libraSum", + "type": "uint256" + }, + { + "internalType": "Fr[9][28]", + "name": "sumcheckUnivariates", + "type": "uint256[9][28]" + }, + { + "internalType": "Fr", + "name": "libraEvaluation", + "type": "uint256" + }, + { + "internalType": "Fr[42]", + "name": "sumcheckEvaluations", + "type": "uint256[42]" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point[27]", + "name": "geminiFoldComms", + "type": "tuple[27]" + }, + { + "internalType": "Fr[28]", + "name": "geminiAEvaluations", + "type": "uint256[28]" + }, + { + "internalType": "Fr[4]", + "name": "libraPolyEvals", + "type": "uint256[4]" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "shplonkQ", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "kzgQuotient", + "type": "tuple" + } + ], + "internalType": "struct Honk.ZKProof", + "name": "proof", + "type": "tuple" + }, + { + "internalType": "bytes32[]", + "name": "publicInputs", + "type": "bytes32[]" + }, + { + "internalType": "uint256", + "name": "vkHash", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "publicInputsSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "logN", + "type": "uint256" + } + ], + "name": "generateTranscript", + "outputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "Fr", + "name": "eta", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "etaTwo", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "etaThree", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "beta", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "gamma", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "publicInputsDelta", + "type": "uint256" + } + ], + "internalType": "struct Honk.RelationParameters", + "name": "relationParameters", + "type": "tuple" + }, + { + "internalType": "Fr[27]", + "name": "alphas", + "type": "uint256[27]" + }, + { + "internalType": "Fr[28]", + "name": "gateChallenges", + "type": "uint256[28]" + }, + { + "internalType": "Fr", + "name": "libraChallenge", + "type": "uint256" + }, + { + "internalType": "Fr[28]", + "name": "sumCheckUChallenges", + "type": "uint256[28]" + }, + { + "internalType": "Fr", + "name": "rho", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "geminiR", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "shplonkNu", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "shplonkZ", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "publicInputsDelta", + "type": "uint256" + } + ], + "internalType": "struct ZKTranscript", + "name": "t", + "type": "tuple" + } + ], + "stateMutability": "pure", + "type": "function" + } + ], + "bytecode": "0x6116f3610034600b8282823980515f1a607314602857634e487b7160e01b5f525f60045260245ffd5b305f52607381538281f3fe7300000000000000000000000000000000000000003014608060405260043610610034575f3560e01c8063995bf45714610038575b5f5ffd5b61004b610046366004611214565b610061565b604051610058919061144a565b60405180910390f35b610069610d76565b5f610078888888888886610128565b9083529050610087818961017c565b6020840191909152905061009b818461026c565b604084019190915290506100af8189610315565b606084019190915290506100c4888285610376565b608084019190915290506100d88882610445565b60a084019190915290506100ed8882856105cb565b60c0840191909152905061010288828561073d565b60e0840191909152905061011688826108bf565b50610100830152509695505050505050565b610130610ddb565b5f61013e88888888886108fc565b60408601919091526020850191909152908352925061015d8389610c6a565b6080850191909152606084019190915291989197509095505050505050565b610184610e0b565b5f61018d610e2a565b8481526101008401805151602080840191909152905181015160408084019190915261012086018051516060850152518201516080840152516101f1916101d69184910161151f565b60405160208183030381529060405280519060200120610cf2565b91505f6101fd83610d18565b50808552905060015b6102126001601c611566565b8110156102625761024385610228600184611566565b601b81106102385761023861150b565b602002015183610d48565b8582601b81106102555761025561150b565b6020020152600101610206565b5050509250929050565b610274610e48565b5f61028b846040516020016101d691815260200190565b935061029684610d18565b50825260015b8381101561030d576102ee836102b3600184611566565b601c81106102c3576102c361150b565b6020020151846102d4600185611566565b601c81106102e4576102e461150b565b6020020151610d48565b8382601c81106103005761030061150b565b602002015260010161029c565b509093915050565b5f5f61031f610e67565b84815261014084018051515160208084019190915290515181015160408084019190915261016086015160608401525161035f916101d691849101611579565b915061036a82610d18565b50959194509092505050565b61037e610e48565b5f805b8381101561043b57610391610e85565b8581525f5b60098110156103fa5787610180015183601c81106103b6576103b661150b565b602002015181600981106103cc576103cc61150b565b6020020151826103dd8360016115ac565b600a81106103ed576103ed61150b565b6020020152600101610396565b5061040f816040516020016101d691906115bf565b955061041a86610d18565b508483601c811061042d5761042d61150b565b602002015250600101610381565b5090949293505050565b5f5f61044f610ea4565b83815260015b610461600160296115ac565b81116104b6576101c0860151610478600183611566565b602a81106104885761048861150b565b602002015182826030811061049f5761049f61150b565b6020020152806104ae816115f3565b915050610455565b856101a001518282603081106104ce576104ce61150b565b60200201526104de6001826115ac565b61014087015160200151519091508282603081106104fe576104fe61150b565b602002015261014086015160016020020151602001518282600161052291906115ac565b603081106105325761053261150b565b60200201526105426002826115ac565b61014087015160400151519091508282603081106105625761056261150b565b602002015261014086015160026020020151602001518282600161058691906115ac565b603081106105965761059661150b565b6020020181815250506105b3826040516020016101d6919061160b565b92506105be83610d18565b5096929550919350505050565b5f80806105d9600185611566565b6105e490600261163f565b6105ef9060016115ac565b6001600160401b0381111561060657610606610eff565b60405190808252806020026020018201604052801561062f578160200160208202803683370190505b50905084815f815181106106455761064561150b565b60209081029190910101525f5b61065d600186611566565b81101561071057866101e0015181601b811061067b5761067b61150b565b6020020151518261068d83600261163f565b6106989060016115ac565b815181106106a8576106a861150b565b602002602001018181525050866101e0015181601b81106106cb576106cb61150b565b602002015160200151828260026106e2919061163f565b6106ed9060026115ac565b815181106106fd576106fd61150b565b6020908102919091010152600101610652565b50610725816040516020016101d69190611656565b915061073082610d18565b5096919550909350505050565b5f808061074b8460016115ac565b6107569060046115ac565b6001600160401b0381111561076d5761076d610eff565b604051908082528060200260200182016040528015610796578160200160208202803683370190505b50905084815f815181106107ac576107ac61150b565b602090810291909101015260015b848111610816576102008701516107d2600183611566565b601c81106107e2576107e261150b565b60200201518282815181106107f9576107f961150b565b60209081029190910101528061080e816115f3565b9150506107ba565b505f806108248660016115ac565b90505b6108328660046115ac565b811161089157876102200151826004811061084f5761084f61150b565b60200201518382815181106108665761086661150b565b60209081029190910101528161087b816115f3565b9250508080610889906115f3565b915050610827565b506108a6826040516020016101d69190611656565b92506108b183610d18565b509792965091945050505050565b5f5f6108c9610ec3565b838152610240850180515160208084019190915290518101516040808401919091525161035f916101d691849101611680565b5f8080808061090c8660016115ac565b6109179060086115ac565b6001600160401b0381111561092e5761092e610eff565b604051908082528060200260200182016040528015610957578160200160208202803683370190505b509050865f1b815f8151811061096f5761096f61150b565b60209081029190910101525f5b610987601088611566565b8110156109d8578989828181106109a0576109a061150b565b90506020020135828260016109b591906115ac565b815181106109c5576109c561150b565b602090810291909101015260010161097c565b505f5b6010811015610a49578a51610a039082601081106109fb576109fb61150b565b602002015190565b82826010610a128b60016115ac565b610a1c9190611566565b610a2691906115ac565b81518110610a3657610a3661150b565b60209081029190910101526001016109db565b5060208a01515181610a5c8860016115ac565b81518110610a6c57610a6c61150b565b6020908102919091018101919091528a810151015181610a8d8860016115ac565b610a989060016115ac565b81518110610aa857610aa861150b565b602090810291909101015260408a01515181610ac58860016115ac565b610ad09060026115ac565b81518110610ae057610ae061150b565b60209081029190910181019190915260408b0151015181610b028860016115ac565b610b0d9060036115ac565b81518110610b1d57610b1d61150b565b602090810291909101015260608a01515181610b3a8860016115ac565b610b459060046115ac565b81518110610b5557610b5561150b565b60209081029190910181019190915260608b0151015181610b778860016115ac565b610b829060056115ac565b81518110610b9257610b9261150b565b602090810291909101015260808a01515181610baf8860016115ac565b610bba9060066115ac565b81518110610bca57610bca61150b565b60209081029190910181019190915260808b0151015181610bec8860016115ac565b610bf79060076115ac565b81518110610c0757610c0761150b565b602002602001018181525050610c27816040516020016101d69190611656565b9150610c3282610d18565b6040805160208101869052929750909550610c4d91016101d6565b9150610c5882610d18565b50809350505095509550955095915050565b5f5f5f610c75610ee1565b85815260c0858101805151602080850191909152905181015160408085019190915260e08801805151606086015251820151608085015260a08089018051519186019190915251820151928401929092529051610cd8916101d6918491016116b3565b9150610ce382610d18565b90979096509194509092505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001900690565b5f808260016001607f1b038116607f82901c610d3382610cf2565b9450610d3e81610cf2565b9350505050915091565b5f7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182840990505b92915050565b604051806101400160405280610d8a610ddb565b8152602001610d97610e0b565b8152602001610da4610e48565b81526020015f8152602001610db7610e48565b81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b6040518060c001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b604051806103600160405280601b906020820280368337509192915050565b6040518060a001604052806005906020820280368337509192915050565b604051806103800160405280601c906020820280368337509192915050565b60405180608001604052806004906020820280368337509192915050565b604051806101400160405280600a906020820280368337509192915050565b6040518061060001604052806030906020820280368337509192915050565b60405180606001604052806003906020820280368337509192915050565b6040518060e001604052806007906020820280368337509192915050565b634e487b7160e01b5f52604160045260245ffd5b60405161028081016001600160401b0381118282101715610f3657610f36610eff565b60405290565b604051601f8201601f191681016001600160401b0381118282101715610f6457610f64610eff565b604052919050565b5f82601f830112610f7b575f5ffd5b5f610200610f8881610f3c565b915083018185821115610f99575f5ffd5b845b82811015610fb3578035825260209182019101610f9b565b509195945050505050565b5f60408284031215610fce575f5ffd5b604080519081016001600160401b0381118282101715610ff057610ff0610eff565b604052823581526020928301359281019290925250919050565b5f82601f830112611019575f5ffd5b5f6110246060610f3c565b90508060c0840185811115611037575f5ffd5b845b81811015610fb35761104b8782610fbe565b8352602090920191604001611039565b5f82601f83011261106a575f5ffd5b61038061107681610f3c565b905080611f8084018581111561108a575f5ffd5b845b818110156110ee5786601f8201126110a2575f5ffd5b5f6101206110af81610f3c565b9150820181898211156110c0575f5ffd5b835b828110156110da5780358252602091820191016110c2565b50505084526020909301926101200161108c565b509095945050505050565b5f82601f830112611108575f5ffd5b5f610540610f8881610f3c565b5f82601f830112611124575f5ffd5b5f61036061113181610f3c565b915050806106c0840185811115611146575f5ffd5b845b81811015610fb35761115a8782610fbe565b8352602090920191604001611148565b5f82601f830112611179575f5ffd5b5f610380610f8881610f3c565b5f82601f830112611195575f5ffd5b5f6111a06080610f3c565b90508060808401858111156111b3575f5ffd5b845b81811015610fb35780358352602092830192016111b5565b5f5f83601f8401126111dd575f5ffd5b5081356001600160401b038111156111f3575f5ffd5b6020830191508360208260051b850101111561120d575f5ffd5b9250929050565b5f5f5f5f5f5f8688036135c081121561122b575f5ffd5b613540811215611239575f5ffd5b50611242610f13565b61124c8989610f6c565b815261125c896102008a01610fbe565b602082015261126f896102408a01610fbe565b6040820152611282896102808a01610fbe565b6060820152611295896102c08a01610fbe565b60808201526112a8896103008a01610fbe565b60a08201526112bb896103408a01610fbe565b60c08201526112ce896103808a01610fbe565b60e08201526112e1896103c08a01610fbe565b6101008201526112f5896104008a01610fbe565b610120820152611309896104408a0161100a565b610140820152610500880135610160820152611329896105208a0161105b565b6101808201526124a08801356101a0820152611349896124c08a016110f9565b6101c082015261135d89612a008a01611115565b6101e0820152611371896130c08a0161116a565b610200820152611385896134408a01611186565b610220820152611399896134c08a01610fbe565b6102408201526113ad896135008a01610fbe565b61026082015295506135408701356001600160401b038111156113ce575f5ffd5b6113da89828a016111cd565b979a90995096976135608101359761358082013597506135a09091013595509350505050565b805f5b601b811015611422578151845260209384019390910190600101611403565b50505050565b805f5b601c81101561142257815184526020938401939091019060010161142b565b8151805182526020808201519083015260408082015190830152606080820151908301526080808201519083015260a09081015190820152610be08101602083015161149960c0840182611400565b5060408301516114ad610420840182611428565b5060608301516107a083015260808301516114cc6107c0840182611428565b5060a0830151610b4083015260c0830151610b6083015260e0830151610b80830152610100830151610ba083015261012090920151610bc09091015290565b634e487b7160e01b5f52603260045260245ffd5b5f8183825b6005811015611543578151835260209283019290910190600101611524565b50505060a08201905092915050565b634e487b7160e01b5f52601160045260245ffd5b81810381811115610d7057610d70611552565b5f8183825b600481101561159d57815183526020928301929091019060010161157e565b50505060808201905092915050565b80820180821115610d7057610d70611552565b5f8183825b600a8110156115e35781518352602092830192909101906001016115c4565b5050506101408201905092915050565b5f6001820161160457611604611552565b5060010190565b5f8183825b603081101561162f578151835260209283019290910190600101611610565b5050506106008201905092915050565b8082028115828204841417610d7057610d70611552565b81515f90829060208501835b82811015610fb3578151845260209384019390910190600101611662565b5f8183825b60038110156116a4578151835260209283019290910190600101611685565b50505060608201905092915050565b5f8183825b60078110156116d75781518352602092830192909101906001016116b8565b50505060e0820190509291505056fea164736f6c634300081c000a", + "deployedBytecode": "0x7300000000000000000000000000000000000000003014608060405260043610610034575f3560e01c8063995bf45714610038575b5f5ffd5b61004b610046366004611214565b610061565b604051610058919061144a565b60405180910390f35b610069610d76565b5f610078888888888886610128565b9083529050610087818961017c565b6020840191909152905061009b818461026c565b604084019190915290506100af8189610315565b606084019190915290506100c4888285610376565b608084019190915290506100d88882610445565b60a084019190915290506100ed8882856105cb565b60c0840191909152905061010288828561073d565b60e0840191909152905061011688826108bf565b50610100830152509695505050505050565b610130610ddb565b5f61013e88888888886108fc565b60408601919091526020850191909152908352925061015d8389610c6a565b6080850191909152606084019190915291989197509095505050505050565b610184610e0b565b5f61018d610e2a565b8481526101008401805151602080840191909152905181015160408084019190915261012086018051516060850152518201516080840152516101f1916101d69184910161151f565b60405160208183030381529060405280519060200120610cf2565b91505f6101fd83610d18565b50808552905060015b6102126001601c611566565b8110156102625761024385610228600184611566565b601b81106102385761023861150b565b602002015183610d48565b8582601b81106102555761025561150b565b6020020152600101610206565b5050509250929050565b610274610e48565b5f61028b846040516020016101d691815260200190565b935061029684610d18565b50825260015b8381101561030d576102ee836102b3600184611566565b601c81106102c3576102c361150b565b6020020151846102d4600185611566565b601c81106102e4576102e461150b565b6020020151610d48565b8382601c81106103005761030061150b565b602002015260010161029c565b509093915050565b5f5f61031f610e67565b84815261014084018051515160208084019190915290515181015160408084019190915261016086015160608401525161035f916101d691849101611579565b915061036a82610d18565b50959194509092505050565b61037e610e48565b5f805b8381101561043b57610391610e85565b8581525f5b60098110156103fa5787610180015183601c81106103b6576103b661150b565b602002015181600981106103cc576103cc61150b565b6020020151826103dd8360016115ac565b600a81106103ed576103ed61150b565b6020020152600101610396565b5061040f816040516020016101d691906115bf565b955061041a86610d18565b508483601c811061042d5761042d61150b565b602002015250600101610381565b5090949293505050565b5f5f61044f610ea4565b83815260015b610461600160296115ac565b81116104b6576101c0860151610478600183611566565b602a81106104885761048861150b565b602002015182826030811061049f5761049f61150b565b6020020152806104ae816115f3565b915050610455565b856101a001518282603081106104ce576104ce61150b565b60200201526104de6001826115ac565b61014087015160200151519091508282603081106104fe576104fe61150b565b602002015261014086015160016020020151602001518282600161052291906115ac565b603081106105325761053261150b565b60200201526105426002826115ac565b61014087015160400151519091508282603081106105625761056261150b565b602002015261014086015160026020020151602001518282600161058691906115ac565b603081106105965761059661150b565b6020020181815250506105b3826040516020016101d6919061160b565b92506105be83610d18565b5096929550919350505050565b5f80806105d9600185611566565b6105e490600261163f565b6105ef9060016115ac565b6001600160401b0381111561060657610606610eff565b60405190808252806020026020018201604052801561062f578160200160208202803683370190505b50905084815f815181106106455761064561150b565b60209081029190910101525f5b61065d600186611566565b81101561071057866101e0015181601b811061067b5761067b61150b565b6020020151518261068d83600261163f565b6106989060016115ac565b815181106106a8576106a861150b565b602002602001018181525050866101e0015181601b81106106cb576106cb61150b565b602002015160200151828260026106e2919061163f565b6106ed9060026115ac565b815181106106fd576106fd61150b565b6020908102919091010152600101610652565b50610725816040516020016101d69190611656565b915061073082610d18565b5096919550909350505050565b5f808061074b8460016115ac565b6107569060046115ac565b6001600160401b0381111561076d5761076d610eff565b604051908082528060200260200182016040528015610796578160200160208202803683370190505b50905084815f815181106107ac576107ac61150b565b602090810291909101015260015b848111610816576102008701516107d2600183611566565b601c81106107e2576107e261150b565b60200201518282815181106107f9576107f961150b565b60209081029190910101528061080e816115f3565b9150506107ba565b505f806108248660016115ac565b90505b6108328660046115ac565b811161089157876102200151826004811061084f5761084f61150b565b60200201518382815181106108665761086661150b565b60209081029190910101528161087b816115f3565b9250508080610889906115f3565b915050610827565b506108a6826040516020016101d69190611656565b92506108b183610d18565b509792965091945050505050565b5f5f6108c9610ec3565b838152610240850180515160208084019190915290518101516040808401919091525161035f916101d691849101611680565b5f8080808061090c8660016115ac565b6109179060086115ac565b6001600160401b0381111561092e5761092e610eff565b604051908082528060200260200182016040528015610957578160200160208202803683370190505b509050865f1b815f8151811061096f5761096f61150b565b60209081029190910101525f5b610987601088611566565b8110156109d8578989828181106109a0576109a061150b565b90506020020135828260016109b591906115ac565b815181106109c5576109c561150b565b602090810291909101015260010161097c565b505f5b6010811015610a49578a51610a039082601081106109fb576109fb61150b565b602002015190565b82826010610a128b60016115ac565b610a1c9190611566565b610a2691906115ac565b81518110610a3657610a3661150b565b60209081029190910101526001016109db565b5060208a01515181610a5c8860016115ac565b81518110610a6c57610a6c61150b565b6020908102919091018101919091528a810151015181610a8d8860016115ac565b610a989060016115ac565b81518110610aa857610aa861150b565b602090810291909101015260408a01515181610ac58860016115ac565b610ad09060026115ac565b81518110610ae057610ae061150b565b60209081029190910181019190915260408b0151015181610b028860016115ac565b610b0d9060036115ac565b81518110610b1d57610b1d61150b565b602090810291909101015260608a01515181610b3a8860016115ac565b610b459060046115ac565b81518110610b5557610b5561150b565b60209081029190910181019190915260608b0151015181610b778860016115ac565b610b829060056115ac565b81518110610b9257610b9261150b565b602090810291909101015260808a01515181610baf8860016115ac565b610bba9060066115ac565b81518110610bca57610bca61150b565b60209081029190910181019190915260808b0151015181610bec8860016115ac565b610bf79060076115ac565b81518110610c0757610c0761150b565b602002602001018181525050610c27816040516020016101d69190611656565b9150610c3282610d18565b6040805160208101869052929750909550610c4d91016101d6565b9150610c5882610d18565b50809350505095509550955095915050565b5f5f5f610c75610ee1565b85815260c0858101805151602080850191909152905181015160408085019190915260e08801805151606086015251820151608085015260a08089018051519186019190915251820151928401929092529051610cd8916101d6918491016116b3565b9150610ce382610d18565b90979096509194509092505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001900690565b5f808260016001607f1b038116607f82901c610d3382610cf2565b9450610d3e81610cf2565b9350505050915091565b5f7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182840990505b92915050565b604051806101400160405280610d8a610ddb565b8152602001610d97610e0b565b8152602001610da4610e48565b81526020015f8152602001610db7610e48565b81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b6040518060c001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b604051806103600160405280601b906020820280368337509192915050565b6040518060a001604052806005906020820280368337509192915050565b604051806103800160405280601c906020820280368337509192915050565b60405180608001604052806004906020820280368337509192915050565b604051806101400160405280600a906020820280368337509192915050565b6040518061060001604052806030906020820280368337509192915050565b60405180606001604052806003906020820280368337509192915050565b6040518060e001604052806007906020820280368337509192915050565b634e487b7160e01b5f52604160045260245ffd5b60405161028081016001600160401b0381118282101715610f3657610f36610eff565b60405290565b604051601f8201601f191681016001600160401b0381118282101715610f6457610f64610eff565b604052919050565b5f82601f830112610f7b575f5ffd5b5f610200610f8881610f3c565b915083018185821115610f99575f5ffd5b845b82811015610fb3578035825260209182019101610f9b565b509195945050505050565b5f60408284031215610fce575f5ffd5b604080519081016001600160401b0381118282101715610ff057610ff0610eff565b604052823581526020928301359281019290925250919050565b5f82601f830112611019575f5ffd5b5f6110246060610f3c565b90508060c0840185811115611037575f5ffd5b845b81811015610fb35761104b8782610fbe565b8352602090920191604001611039565b5f82601f83011261106a575f5ffd5b61038061107681610f3c565b905080611f8084018581111561108a575f5ffd5b845b818110156110ee5786601f8201126110a2575f5ffd5b5f6101206110af81610f3c565b9150820181898211156110c0575f5ffd5b835b828110156110da5780358252602091820191016110c2565b50505084526020909301926101200161108c565b509095945050505050565b5f82601f830112611108575f5ffd5b5f610540610f8881610f3c565b5f82601f830112611124575f5ffd5b5f61036061113181610f3c565b915050806106c0840185811115611146575f5ffd5b845b81811015610fb35761115a8782610fbe565b8352602090920191604001611148565b5f82601f830112611179575f5ffd5b5f610380610f8881610f3c565b5f82601f830112611195575f5ffd5b5f6111a06080610f3c565b90508060808401858111156111b3575f5ffd5b845b81811015610fb35780358352602092830192016111b5565b5f5f83601f8401126111dd575f5ffd5b5081356001600160401b038111156111f3575f5ffd5b6020830191508360208260051b850101111561120d575f5ffd5b9250929050565b5f5f5f5f5f5f8688036135c081121561122b575f5ffd5b613540811215611239575f5ffd5b50611242610f13565b61124c8989610f6c565b815261125c896102008a01610fbe565b602082015261126f896102408a01610fbe565b6040820152611282896102808a01610fbe565b6060820152611295896102c08a01610fbe565b60808201526112a8896103008a01610fbe565b60a08201526112bb896103408a01610fbe565b60c08201526112ce896103808a01610fbe565b60e08201526112e1896103c08a01610fbe565b6101008201526112f5896104008a01610fbe565b610120820152611309896104408a0161100a565b610140820152610500880135610160820152611329896105208a0161105b565b6101808201526124a08801356101a0820152611349896124c08a016110f9565b6101c082015261135d89612a008a01611115565b6101e0820152611371896130c08a0161116a565b610200820152611385896134408a01611186565b610220820152611399896134c08a01610fbe565b6102408201526113ad896135008a01610fbe565b61026082015295506135408701356001600160401b038111156113ce575f5ffd5b6113da89828a016111cd565b979a90995096976135608101359761358082013597506135a09091013595509350505050565b805f5b601b811015611422578151845260209384019390910190600101611403565b50505050565b805f5b601c81101561142257815184526020938401939091019060010161142b565b8151805182526020808201519083015260408082015190830152606080820151908301526080808201519083015260a09081015190820152610be08101602083015161149960c0840182611400565b5060408301516114ad610420840182611428565b5060608301516107a083015260808301516114cc6107c0840182611428565b5060a0830151610b4083015260c0830151610b6083015260e0830151610b80830152610100830151610ba083015261012090920151610bc09091015290565b634e487b7160e01b5f52603260045260245ffd5b5f8183825b6005811015611543578151835260209283019290910190600101611524565b50505060a08201905092915050565b634e487b7160e01b5f52601160045260245ffd5b81810381811115610d7057610d70611552565b5f8183825b600481101561159d57815183526020928301929091019060010161157e565b50505060808201905092915050565b80820180821115610d7057610d70611552565b5f8183825b600a8110156115e35781518352602092830192909101906001016115c4565b5050506101408201905092915050565b5f6001820161160457611604611552565b5060010190565b5f8183825b603081101561162f578151835260209283019290910190600101611610565b5050506106008201905092915050565b8082028115828204841417610d7057610d70611552565b81515f90829060208501835b82811015610fb3578151845260209384019390910190600101611662565b5f8183825b60038110156116a4578151835260209283019290910190600101611685565b50505060608201905092915050565b5f8183825b60078110156116d75781518352602092830192909101906001016116b8565b50505060e0820190509291505056fea164736f6c634300081c000a", + "linkReferences": {}, + "deployedLinkReferences": {}, + "immutableReferences": {}, + "inputSourceName": "project/contracts/verifier/RecursiveAggregationFoldVerifier.sol", + "buildInfoId": "solc-0_8_28-bd46eae6f77be9a8538850385226c1ee4fc57e42" +} \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/verifier/ThresholdDecryptedSharesAggregationBnVerifier.sol/ThresholdDecryptedSharesAggregationBnVerifier.json b/packages/enclave-contracts/artifacts/contracts/verifier/ThresholdDecryptedSharesAggregationBnVerifier.sol/ThresholdDecryptedSharesAggregationBnVerifier.json new file mode 100644 index 0000000000..d98f07bf79 --- /dev/null +++ b/packages/enclave-contracts/artifacts/contracts/verifier/ThresholdDecryptedSharesAggregationBnVerifier.sol/ThresholdDecryptedSharesAggregationBnVerifier.json @@ -0,0 +1,188 @@ +{ + "_format": "hh3-artifact-1", + "contractName": "ThresholdDecryptedSharesAggregationBnVerifier", + "sourceName": "contracts/verifier/ThresholdDecryptedSharesAggregationBnVerifier.sol", + "abi": [ + { + "inputs": [], + "name": "ConsistencyCheckFailed", + "type": "error" + }, + { + "inputs": [], + "name": "GeminiChallengeInSubgroup", + "type": "error" + }, + { + "inputs": [], + "name": "ProofLengthWrong", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "logN", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "actualLength", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expectedLength", + "type": "uint256" + } + ], + "name": "ProofLengthWrongWithLogN", + "type": "error" + }, + { + "inputs": [], + "name": "PublicInputsLengthWrong", + "type": "error" + }, + { + "inputs": [], + "name": "ShpleminiFailed", + "type": "error" + }, + { + "inputs": [], + "name": "SumcheckFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "proof", + "type": "bytes" + }, + { + "internalType": "bytes32[]", + "name": "publicInputs", + "type": "bytes32[]" + } + ], + "name": "verify", + "outputs": [ + { + "internalType": "bool", + "name": "verified", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x610120604052348015610010575f5ffd5b50620200006080819052601160a08190527f1eb29cc0a26390fad32af3a6275716d347fdcbdcc5f7c7c54c194a0cce5854e060c081905261024360e081905260038361005e6001602461008b565b610068919061008b565b610072919061008b565b61007d90600261008b565b61010052506100b092505050565b808201808211156100aa57634e487b7160e01b5f52601160045260245ffd5b92915050565b60805160a05160c05160e05161010051615cbb61014a5f395f8181610c5401528181610cb90152612ec301525f81816101b001526108ff01525f61018e01525f8181605b01528181609401528181610101015281816101d201528181610a2d01528181610b8e01528181610c2b01528181611523015281816115dd015281816116100152818161181a0152612ba001525f5050615cbb5ff3fe608060405234801561000f575f5ffd5b5060043610610029575f3560e01c8063ea50d0e41461002d575b5f5ffd5b61004061003b3660046154f2565b610054565b604051901515815260200160405180910390f35b5f5f61007f7f00000000000000000000000000000000000000000000000000000000000000006102ee565b905061008c8160206155ca565b85146100ee577f0000000000000000000000000000000000000000000000000000000000000000856100bf8360206155ca565b6040516359895a5360e01b81526004810193909352602483019190915260448201526064015b60405180910390fd5b5f6100f76103fb565b90505f61012588887f0000000000000000000000000000000000000000000000000000000000000000610410565b90506010826040015161013891906155e1565b85146101575760405163fa06659360e01b815260040160405180910390fd5b60405163995bf45760e01b81525f9073__$766c1d4ff1635319bed72d911807c12b70$__9063995bf457906101fa9085908b908b907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090600401615764565b610be060405180830381865af4158015610216573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061023a9190615a90565b905061028a8787808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050855185516060810151608090910151919350915060016108a9565b815160a0015261029a8282610a10565b6102b7576040516313f8744360e31b815260040160405180910390fd5b6102c2828483610c17565b6102df576040516352ec174560e11b815260040160405180910390fd5b50600198975050505050505050565b5f8060026102fe60016008615b3a565b61030891906155ca565b9050610316600260036155ca565b6103209082615b3a565b9050600161032f6009856155ca565b61033991906155ca565b6103439082615b3a565b90506001610352816029615b3a565b61035c91906155ca565b6103669082615b3a565b9050610374600160026155ca565b61037e9082615b3a565b905061038b6001846155ca565b6103959082615b3a565b90506103a3600160046155ca565b6103ad9082615b3a565b905060026103bc6001856155e1565b6103c691906155ca565b6103d09082615b3a565b90506103dd6002806155ca565b6103e79082615b3a565b90506103f4601082615b3a565b9392505050565b610403615025565b61040b611c14565b905090565b6104186151af565b5f805b601081101561047a57610447868387610435602083615b3a565b9261044293929190615b4d565b61254f565b8351826010811061045a5761045a615b74565b6020020181815250506020826104709190615b3a565b915060010161041b565b5061049e85828661048c604083615b3a565b9261049993929190615b4d565b612562565b60208301526104ae604082615b3a565b90506104c185828661048c604083615b3a565b6040808401919091526104d49082615b3a565b90506104e785828661048c604083615b3a565b60608301526104f7604082615b3a565b905061050a85828661048c604083615b3a565b608083015261051a604082615b3a565b905061052d85828661048c604083615b3a565b60c083015261053d604082615b3a565b905061055085828661048c604083615b3a565b60e0830152610560604082615b3a565b905061057385828661048c604083615b3a565b60a0830152610583604082615b3a565b905061059685828661048c604083615b3a565b6101008301526105a7604082615b3a565b90506105ba85828661048c604083615b3a565b6101208301526105cb604082615b3a565b90506105de85828661048c604083615b3a565b610140830151526105f0604082615b3a565b9050610603858286610435602083615b3a565b610160830152610614602082615b3a565b90505f5b83811015610692575f5b60098110156106895761063c878488610435602083615b3a565b84610180015183601c811061065357610653615b74565b6020020151826009811061066957610669615b74565b60200201818152505060208361067f9190615b3a565b9250600101610622565b50600101610618565b505f5b6106a160016029615b3a565b8110156106f0576106b9868387610435602083615b3a565b836101c0015182602a81106106d0576106d0615b74565b6020020181815250506020826106e69190615b3a565b9150600101610695565b50610702858286610435602083615b3a565b6101a0830152610713602082615b3a565b905061072685828661048c604083615b3a565b6101408301516020015261073b604082615b3a565b905061074e85828661048c604083615b3a565b61014083015160026020020152610766604082615b3a565b90505f5b6107756001856155e1565b8110156107be5761078d86838761048c604083615b3a565b836101e0015182601b81106107a4576107a4615b74565b60200201526107b4604083615b3a565b915060010161076a565b505f5b83811015610811576107da868387610435602083615b3a565b83610200015182601c81106107f1576107f1615b74565b6020020181815250506020826108079190615b3a565b91506001016107c1565b505f5b60048110156108655761082e868387610435602083615b3a565b836102200151826004811061084557610845615b74565b60200201818152505060208261085b9190615b3a565b9150600101610814565b5061087785828661048c604083615b3a565b610240830152610888604082615b3a565b905061089b85828661048c604083615b3a565b610260830152509392505050565b5f600180826108d6866108d1896108cc6108c78a6310000000615b3a565b6125e4565b6125fc565b612617565b90505f6108f4876108ef8a6108cc6108c78b6001615b3a565b612630565b90505f5b61092360107f00000000000000000000000000000000000000000000000000000000000000006155e1565b811015610990575f61094d8c838151811061094057610940615b74565b6020026020010151612658565b905061095d866108cc8684612617565b955061096d856108cc8584612617565b9450610979848b612617565b9350610985838b612630565b9250506001016108f8565b505f5b60108110156109f7575f8a82601081106109af576109af615b74565b602002015190506109c4866108cc8684612617565b95506109d4856108cc8584612617565b94506109e0848b612617565b93506109ec838b612630565b925050600101610993565b50610a02848461266d565b9a9950505050505050505050565b5f5f610a2583606001518561016001516125fc565b905060015f5b7f0000000000000000000000000000000000000000000000000000000000000000811015610b17575f86610180015182601c8110610a6b57610a6b615b74565b602002015180519091505f90610a89908360015b6020020151612617565b9050848114610aab576040516313f8744360e31b815260040160405180910390fd5b5f876080015184601c8110610ac257610ac2615b74565b60200201519050610ad3838261267b565b9550610b07856108cc60016108d1856108cc8e604001518b601c8110610afb57610afb615b74565b60200201516001612630565b9450505050806001019050610a2b565b50610b20615292565b5f5b6029811015610b70576101c0870151610b3c600183615b3a565b602a8110610b4c57610b4c615b74565b6020020151828260298110610b6357610b63615b74565b6020020152600101610b22565b505f610b8582875f0151886020015186612829565b9050600160025b7f0000000000000000000000000000000000000000000000000000000000000000811015610be257610bd882896080015183601c8110610bce57610bce615b74565b60200201516125fc565b9150600101610b8c565b50610c08610bf5836108cc600185612630565b6108d18a6101a001518a606001516125fc565b94909414979650505050505050565b5f610c206152b1565b5f610c4f8460c001517f00000000000000000000000000000000000000000000000000000000000000006128a2565b90505f7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03811115610c8a57610c8a615941565b604051908082528060200260200182016040528015610cb3578160200160208202803683370190505b5090505f7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03811115610cef57610cef615941565b604051908082528060200260200182016040528015610d2857816020015b610d15615321565b815260200190600190039081610d0d5790505b509050610d5b610d56876101000151855f81518110610d4957610d49615b74565b6020026020010151612630565b61296b565b84610120018181525050610d90610d56876101000151855f81518110610d8357610d83615b74565b6020026020010151612617565b610140850181905261012085015160e0880151610db1926108d191906125fc565b845260c0860151610de190610dc59061296b565b6108cc8661012001516108ef8a60e001518961014001516125fc565b6020850152815160019083905f90610dfb57610dfb615b74565b602002602001018181525050876102400151815f81518110610e1f57610e1f615b74565b6020908102919091010152600160a08501525f60c08501528351610e42906129db565b60408501526020840151610e55906129db565b606085015260015b610e6960016024615b3a565b8111610f0a57610e8185604001518660a001516125fc565b838281518110610e9357610e93615b74565b602002602001018181525050610edc8560c001516108d18b6101c00151600185610ebd91906155e1565b602a8110610ecd57610ecd615b74565b60200201518860a001516125fc565b60c086015260a08086015190880151610ef591906125fc565b60a0860152610f0381615b88565b9050610e5d565b505f5b6005811015610fe3575f610f22601e83615b3a565b90505f610f3160016024615b3a565b610f3b9084615b3a565b9050610f6c858381518110610f5257610f52615b74565b60200260200101516108d189606001518a60a001516125fc565b858381518110610f7e57610f7e615b74565b602002602001018181525050610fbb8760c001516108d18d6101c0015184602a8110610fac57610fac615b74565b60200201518a60a001516125fc565b60c088015260a080880151908a0151610fd491906125fc565b60a08801525050600101610f0d565b50876020015181600181518110610ffc57610ffc615b74565b602002602001018190525086606001518160028151811061101f5761101f615b74565b602002602001018190525086608001518160038151811061104257611042615b74565b60200260200101819052508660a001518160048151811061106557611065615b74565b60200260200101819052508660c001518160058151811061108857611088615b74565b60200260200101819052508660e00151816006815181106110ab576110ab615b74565b6020026020010181905250866101000151816007815181106110cf576110cf615b74565b6020026020010181905250866101200151816008815181106110f3576110f3615b74565b60200260200101819052508661014001518160098151811061111757611117615b74565b602002602001018190525086610160015181600a8151811061113b5761113b615b74565b6020026020010181905250866101c0015181600b8151811061115f5761115f615b74565b602002602001018190525086610180015181600c8151811061118357611183615b74565b6020026020010181905250866101a0015181600d815181106111a7576111a7615b74565b6020026020010181905250866101e0015181600e815181106111cb576111cb615b74565b602002602001018190525086610200015181600f815181106111ef576111ef615b74565b60200260200101819052508661022001518160108151811061121357611213615b74565b60200260200101819052508661024001518160118151811061123757611237615b74565b60200260200101819052508661026001518160128151811061125b5761125b615b74565b60200260200101819052508661028001518160138151811061127f5761127f615b74565b6020026020010181905250866102a00151816014815181106112a3576112a3615b74565b6020026020010181905250866102c00151816015815181106112c7576112c7615b74565b6020026020010181905250866102e00151816016815181106112eb576112eb615b74565b60200260200101819052508661030001518160178151811061130f5761130f615b74565b60200260200101819052508661032001518160188151811061133357611333615b74565b60200260200101819052508661034001518160198151811061135757611357615b74565b602002602001018190525086610360015181601a8151811061137b5761137b615b74565b602002602001018190525086610380015181601b8151811061139f5761139f615b74565b6020026020010181905250866103a0015181601c815181106113c3576113c3615b74565b6020026020010181905250866103c0015181601d815181106113e7576113e7615b74565b6020026020010181905250876040015181601e8151811061140a5761140a615b74565b6020026020010181905250876060015181601f8151811061142d5761142d615b74565b602002602001018190525087608001518160208151811061145057611450615b74565b60200260200101819052508760a001518160218151811061147357611473615b74565b60200260200101819052508761012001518160228151811061149757611497615b74565b6020026020010181905250876101000151816023815181106114bb576114bb615b74565b60200260200101819052508760c00151816024815181106114de576114de615b74565b60200260200101819052508760e001518160258151811061150157611501615b74565b60200260200101819052505f61154787608001518660c001518b6102000151877f00000000000000000000000000000000000000000000000000000000000000006129ed565b9050611571815f8151811061155e5761155e615b74565b60200260200101518661012001516125fc565b608086018190526102008a01515160e08901516115a292916108d19161159791906125fc565b8861014001516125fc565b608086015260e08701516115b590612b4b565b60a08601525f6115c760016024615b3a565b6115d2906001615b3a565b90505f5b61160160017f00000000000000000000000000000000000000000000000000000000000000006155e1565b811015611812575f61163460017f00000000000000000000000000000000000000000000000000000000000000006155e1565b8210159050806117a357611668610d568b6101000151898560016116589190615b3a565b81518110610d4957610d49615b74565b6101208901526101008a015161169890610d569089611688866001615b3a565b81518110610d8357610d83615b74565b61014089015260a08801516101208901516116b391906125fc565b61016089015260a088015160e08b01516116db916116d0916125fc565b8961014001516125fc565b61018089018190526116fe906116f0906129db565b6108d18a61016001516129db565b866117098486615b3a565b8151811061171957611719615b74565b6020026020010181815250505f6117528961018001518e61020001518560016117429190615b3a565b601c8110610bce57610bce615b74565b905061178c816108d18b61016001518887600161176f9190615b3a565b8151811061177f5761177f615b74565b60200260200101516125fc565b905061179c896080015182612617565b60808a0152505b6117c26117b88960a001518c60e001516125fc565b8b60e001516125fc565b60a08901526101e08c015182601b81106117de576117de615b74565b6020020151856117ee8486615b3a565b815181106117fe576117fe615b74565b6020908102919091010152506001016115d6565b5061183e60017f00000000000000000000000000000000000000000000000000000000000000006155e1565b6118489082615b3a565b90506118686118608961010001518a60c00151612630565b60019061266d565b60e08701515261010088015160c08901516118ac91611860916108ef907f07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76906125fc565b60e0878101805160200192909252815180516040909101529051805160609091015260a0870151908901516118ee916118e4916125fc565b8960e001516125fc565b60a08701525f5b600481101561199c575f6119278860e00151836004811061191857611918615b74565b60200201518960a001516125fc565b9050611932816129db565b886101000151836004811061194957611949615b74565b602002015260a088015160e08b015161196291906125fc565b8860a001818152505061198e88608001516108d1838f61022001518660048110610bce57610bce615b74565b6080890152506001016118f5565b506101008601515184518590839081106119b8576119b8615b74565b602090810291909101810191909152610100870151908101516119dc916002610a7f565b846119e8836001615b3a565b815181106119f8576119f8615b74565b60209081029190910101526101008601516060015184611a19836002615b3a565b81518110611a2957611a29615b74565b60209081029190910101525f5b6003811015611a8b578a61014001518160038110611a5657611a56615b74565b60200201518483611a6681615b88565b945081518110611a7857611a78615b74565b6020908102919091010152600101611a36565b506040518060400160405280600181526020016002815250838281518110611ab557611ab5615b74565b60200260200101819052508560800151848280611ad190615b88565b935081518110611ae357611ae3615b74565b602002602001018181525050611b0d8a61022001518960c001518a608001518d6101a00151612b56565b611b2a5760405163a2a2ac8360e01b815260040160405180910390fd5b5f8a6102600151905080848381518110611b4657611b46615b74565b6020026020010181905250886101000151858381518110611b6957611b69615b74565b602002602001018181525050611b7d615339565b611b878587612eb9565b8152611b9282612fb1565b602082018190528c5182515f92611baa929190612ff7565b90505f5f611bba8f5f0151613088565b91509150611bc782613149565b611bd081613149565b8351611bdd9083856131e8565b84526020840151611bef9082856131e8565b602085018190528451611c0191613217565b9f9e505050505050505050505050505050565b611c1c615025565b50604080516103e081018252620200008152601160208083019190915261024382840152825180840184527f039702f4c2b2b49fd29d82b077cbf9d1df2871e0fbade17241573ca5021603fc81527f03270b3b2673844695842df11cdbabf54acafe03642adfdfbf69834247ed0720818301526060830152825180840184527f1ccb0308d0359b8835480fa7dd829fdbf16b136d57e85c1621efb1679fe1a94c81527f207076fed3c60bbeb2df5f48c86b12f4027c523f9b8e69456ca51643fea3de1d818301526080830152825180840184527f067b40a80be55b95cc17070cf5dc78441e4868acea0ac1e46b5708705151590581527f0792e6c3d76aeeed6ee7d7e443138c813b74dbcd8ce92990e6f6747adc0af0548183015260a0830152825180840184527f148a04387a9e0a8cec6a4a0d90706f5907e8bb257fb04f8a3cfe07c18a7c101481527f225866d9efd4a5be4eb9a00016f902f844e3d81e2768b24dc92ecbd6b34176bd8183015260c0830152825180840184527f0dacf57ed82c686ca051fca3f1820016cb49e91b5d3c4bee7f9fa19800d9ed0681527f22ba5fda41aa14aeb7a3a2731fbfe3a673cf7db1d2217d32db491f44344309058183015260e0830152825180840184527f0f783f69a36c2bac47b9c59ff33ccc583a284a46b282591b9e9bbb85d8b5e5dc81527f029b91609a06e94924f0d83530fc01f3427f5ab729a0959903108c2ccc86825c81830152610100830152825180840184527f11beb2731ab8cf46c0a52666bb457fa74d4b635043bb5689a35a2aee24990b8081527f2f1fba989036c44aedb609d0d3341ccb842c2d1cd7c80cea4a95d0eb4530577081830152610120830152825180840184527f28ae1f7459c3c5d9ca2f80f8e5c28bb301d0245228bade0e2bb962b4bfbe301b81527f0622f7db14381bbecf862e186cfa570588160466ae7d4fed4b35a70d99af914f81830152610140830152825180840184527f233983f621ab48bdce4f7216bdbc54c49463e90d88301b9ab224fca61c93dba081527f265c3834d63be0bc710c9b4a48015e2442a3b9bf63caec9db0064dfb1bd569c481830152610160830152825180840184527f06d21b82f49e04e078d4ad30cd6828eaa66c1bf984bc52b79de8c786fe4dfe7a81527f301aa91e167bbc765996ac6d2889e7d49f0734bc7abb3a4b85900143556a1b3f81830152610180830152825180840184527f1222d34f53e6b36138627277789ad131ea24f79caca2d5f59193c5e3a7dfdde781527f245a380726977d41c9f0a9a0ba33608994eb3660de6a8efba38009df93da679a818301526101a0830152825180840184527f2dfc248e28ad889b5b692077f338d28b2f687c6e909b9693f8e8fd25cefc352881527f16e2cbc305166ea90a426987efff2020827d8c548ea4155c659df296f14f00fd818301526101c0830152825180840184527f0d815a04effc6e57ec95f3ac112f76ab4a9d993d044626ceb54f967c8d77d8e181527f1df4c065a1129d5cbdf244cb9cc2eaf406c2f5fbab4c393347d57ae5e2ab881f818301526101e0830152825180840184527f118733dc1db84685f4160119cb0cd9a51b831cf0643e914daf1c63fc7967297981527f27b63107b52edf239acb1bd47e24c97094422cc2bba51ee0f54b2707946be55681830152610200830152825180840184527f2e7a11cec89df0ad6b730cf80be4246e56e1667f55e35a22c9b39b196226135b81527f0891267297e339bedeef69881477b4e5c19e761e127e3d3772714f92ade80fd381830152610220830152825180840184527f16f90d7d5c3fd7ab9f490dd5d2b94afea698039e2456c1d5f0e22159348d1e0c81527f29ad721b49b02192f5323326fd803b102f7a09b0371c537db998014fa14c307981830152610240830152825180840184527f0a5e4e29375b31024f09dd662b460eb25930835255c62dc74d62d0c983981e4281527f069adf56537452ecad2fcd8cdea1291fbf273d7e752d9ba0812900e7215c8fc081830152610260830152825180840184527f1bd554f207de2e42cbc94e8579154a07e37f3ba9d80832f28e38dffaaccd1ade81527f22261d045539dbaabf26f23773b365b2ddd30af639a8b15a56e777f5607bf43581830152610280830152825180840184527f11769064ef79db1f35b23b8b1e5c118e769e1a41b912f49dfdbffbacf23d056481527f16f17c1721186bbd19d09f1645f968677cb1ad0c6b656c3849a4b7f27d5279d9818301526102a0830152825180840184527f0cad91097e151471556985b94a0188b759472f9bb3295fe6de6846f2d4fe27e181527f042f4a199546b40704a9c4822bfd20a91bc478055dc5676c7347f64545b47f4d818301526102c0830152825180840184527f1286a8beb1993863980820c27dfb00f93f8949f60933042e5aa8275dd48e55f781527f2037878d67d1009eb6dac43c5f6f076f884d456e057b000912d40a2ec440df1f818301526102e0830152825180840184527f1c0413fa3552ba4738b423829d55fcc03c912bf931336778a458e1c25cd4f55881527f190904e6db04c862bd9d7406029e03a1103a513203eb6d8faa010021eedf18d481830152610300830152825180840184527f099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d2681527e15b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f81830152610320830152825180840184527f1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e81527f305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d1981830152610340830152825180840184527f061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d81527f1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e81830152610360830152825180840184527f043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce81527f261522c4089330646aff96736194949330952ae74c573d1686d9cb4a007338548183015261038083015282518084018452600181526002818301526103a083015282518084019093527f0ad44c49c91e4374983e023f10e6256c39d27b2857eb2d2ce82a82b51e02dc3883527f23ad26f46d7b04b00d6ebe50273765d49c59d9db9ae22dd33909bc3c9efc5308908301526103c081019190915290565b5f6103f461255d8385615ba0565b612658565b61256a615321565b60408051808201909152805f516020615c6f5f395f51905f5261259060205f8789615b4d565b61259991615ba0565b6125a39190615bbd565b81526020908101905f516020615c6f5f395f51905f52906125c8906040908789615b4d565b6125d191615ba0565b6125db9190615bbd565b90529392505050565b5f5f516020615c8f5f395f51905f52825b0692915050565b5f5f516020615c8f5f395f51905f5282840990505b92915050565b5f5f516020615c8f5f395f51905f528284089392505050565b5f5f516020615c8f5f395f51905f52825f516020615c8f5f395f51905f520384089392505050565b5f5f516020615c8f5f395f51905f52826125f5565b5f6103f4836108cc8461296b565b5f5f604051806101200160405280619d8081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec5181526020016105a081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31815260200161024081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd3181526020016105a081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec518152602001619d8081525090505f600190505f5f90505b60098110156127745761276a826108cc8784612630565b9150600101612753565b5061277d61534c565b5f5b60098110156127ce576127af610d568583600981106127a0576127a0615b74565b60200201516108cc8985612630565b8282600981106127c1576127c1615b74565b602002015260010161277f565b505f5b60098110156128145761280a856108d18984600981106127f3576127f3615b74565b6020020151858560098110610bce57610bce615b74565b94506001016127d1565b5061281f84836125fc565b9695505050505050565b5f61283261536b565b61283d86828561340b565b612849868683866135b7565b612855868683866137a3565b6128608682856139c8565b61286b868285613bbc565b61287786868386613f05565b6128828682856143b1565b61288d8682856147c3565b612898868285614b84565b61281f8185614e84565b60605f826001600160401b038111156128bd576128bd615941565b6040519080825280602002602001820160405280156128e6578160200160208202803683370190505b50905083815f815181106128fc576128fc615b74565b602090810291909101015260015b838110156129635761293e826129216001846155e1565b8151811061293157612931615b74565b6020026020010151612b4b565b82828151811061295057612950615b74565b602090810291909101015260010161290a565b509392505050565b5f5f8290505f604051602081526020808201526020604082015282606082015260025f516020615c8f5f395f51905f520360808201525f516020615c8f5f395f51905f5260a082015260205f60c08360055afa806129c7575f5ffd5b505f51608091909101604052949350505050565b5f516020615c8f5f395f51905f520390565b60605f826001600160401b03811115612a0857612a08615941565b604051908082528060200260200182016040528015612a31578160200160208202803683370190505b509050825b8015612b40575f85612a496001846155e1565b81518110612a5957612a59615b74565b602002602001015190505f89600184612a7291906155e1565b601c8110612a8257612a82615b74565b602002015190505f612add612aa1612a9a858d6125fc565b60026125fc565b6108ef8b612ab06001896155e1565b601c8110612ac057612ac0615b74565b60200201516108cc612ad7886108cc60018a612630565b87612630565b9050612afe816108cc610d56612af8876108cc600189612630565b86612617565b99508990508085612b106001876155e1565b81518110612b2057612b20615b74565b60200260200101818152505050505080612b3990615bdc565b9050612a36565b509695505050505050565b5f61261182836125fc565b5f600181612b6f612b6987610100614edd565b83612630565b905080612b8f5760405163835eb8f760e01b815260040160405180910390fd5b612b9761538a565b80518390525f5b7f0000000000000000000000000000000000000000000000000000000000000000811015612c80575f612bd28260096155ca565b612bdd906001615b3a565b905084835f0151826101008110612bf657612bf6615b74565b60200201525f612c07826001615b3a565b90505b612c15600983615b3a565b811015612c76578351612c5590612c2d6001846155e1565b6101008110612c3e57612c3e615b74565b60200201518a85601c8110610bce57610bce615b74565b8451826101008110612c6957612c69615b74565b6020020152600101612c0a565b5050600101612b9e565b50608081018390525f602082018190525b610100811015612d9157612cb2612cac83608001518a6125fc565b85612630565b8260a00151826101008110612cc957612cc9615b74565b602002015260a0820151612cf390826101008110612ce957612ce9615b74565b602002015161296b565b8260a00151826101008110612d0a57612d0a615b74565b602002018181525050612d5082602001516108d1845f0151846101008110612d3457612d34615b74565b60200201518560a00151856101008110610bce57610bce615b74565b60208301526080820151612d84907f204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d66125fc565b6080830152600101612c91565b505f612da2836108cc61010061296b565b9050612db28260200151826125fc565b602083015260a0820151612dcd905f5b6020020151826125fc565b604083015260a0820151612df990612de860016101006155e1565b6101008110612dc257612dc2615b74565b60608301526040820151612e0f908a6002610bce565b60c08301819052612e6f906108d1612e478b7f204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6612630565b60208d015160408e01516108cc91612e5e91612630565b8e5160208901516108ef91906125fc565b60c083018190526060830151612ea491612e98916108d1906108cc8e600260200201518c612630565b6108ef858c6003610bce565b60c08301819052159998505050505050505050565b612ec1615321565b7f00000000000000000000000000000000000000000000000000000000000000005f5b81811015612f1657612f0e858281518110612f0157612f01615b74565b6020026020010151613149565b600101612ee4565b50604051600190815b60018401811015612f7b5760208102870160208202870181515160408501528151602001516060850152805160808501525050604080830160606040850160075afa8316925060408260808460065afa90921691600101612f1f565b5080518452602081015160208501525080612fa9576040516352ec174560e11b815260040160405180910390fd5b505092915050565b612fb9615321565b5f516020615c6f5f395f51905f5282602001515f516020615c6f5f395f51905f52612fe491906155e1565b612fee9190615bbd565b60208301525090565b5f5f5f61300386613088565b9150915061300f6153ce565b82518152602080840151818301528251604080840191909152838201516060840152875160808401528782015160a0840152865160c08401528682015160e08401525161307d9161306291849101615bf1565b60405160208183030381529060405280519060200120612658565b979650505050505050565b613090615321565b613098615321565b82516020808501516040860151606087015160cc90811b608892831b604494851b90961795909517949094178652608087015160a088015160c089015160e08a0151871b90841b91851b9092171717868401526101008701516101208801516101408901516101608a0151871b90841b91851b909217171785526101808701516101a08801516101c08901516101e09099015190951b9790911b9390911b1791909117939093179281019290925291565b805160208201515f5f516020615c6f5f395f51905f528380095f516020615c6f5f395f51905f5260035f516020615c6f5f395f51905f52838709085f516020615c6f5f395f51905f5284850914915050806131e25760405162461bcd60e51b8152602060048201526019602482015278706f696e74206973206e6f74206f6e2074686520637572766560381b60448201526064016100e5565b50505050565b6131f0615321565b6131f8615321565b6132028386614f3e565b905061320e8185614f94565b95945050505050565b81516020808401518351848301516040805194850195909552938301919091527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260608301527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed60808301527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b60a08301527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60c083015260e08201526101008101919091527f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c16101208201527f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b06101408201527f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe46101608201527f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e556101808201525f9081906101a00160405160208183030381529060405290505f5f60086001600160a01b0316836040516133ad9190615c25565b5f60405180830381855afa9150503d805f81146133e5576040519150601f19603f3d011682016040523d82523d5f602084013e6133ea565b606091505b509150915081801561281f57508080602001905181019061281f9190615c3b565b5f613417846007614ff5565b90507f183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f80000005f61347861347261344c856003612630565b6108cc61346761345c8b5f614ff5565b6108cc8c601d614ff5565b6108cc8b601c614ff5565b836125fc565b90506134f96134ee6134d56134bc6134a3856108d16134988d6002614ff5565b6108cc8e601c614ff5565b6108d16134b18c6003614ff5565b6108cc8d601d614ff5565b6108d16134ca8b6004614ff5565b6108cc8c601e614ff5565b6108d16134e38a6005614ff5565b6108cc8b601f614ff5565b6108d1886001614ff5565b9050613518816108d161350d866001612630565b6108cc8a6027614ff5565b905061352481846125fc565b905061353081856125fc565b8552505f905061356a61356061355561354a88601c614ff5565b6108d189601f614ff5565b6108ef886024614ff5565b6108d1875f614ff5565b905061357b816108cc846002612630565b905061358c816108cc846001612630565b905061359881836125fc565b90506135a481846125fc565b9050808460015b60200201525050505050565b5f5f5f6135ed6135e36135cb89601c614ff5565b6108d16135d98b6012614ff5565b8a606001516125fc565b8760800151612617565b9050613626816108cc61361c6136048b601d614ff5565b6108d16136128d6013614ff5565b8c606001516125fc565b8960800151612617565b905061364b816108cc61361c61363d8b601e614ff5565b6108d16136128d6014614ff5565b9050613670816108cc61361c6136628b601f614ff5565b6108d16136128d6015614ff5565b92505f90506136946135e361368689601c614ff5565b6108d16135d98b600e614ff5565b90506136b9816108cc61361c6136ab8b601d614ff5565b6108d16136128d600f614ff5565b90506136de816108cc61361c6136d08b601e614ff5565b6108d16136128d6010614ff5565b9050613703816108cc61361c6136f58b601f614ff5565b6108d16136128d6011614ff5565b91505f905061372a613724613719896020614ff5565b6108d18a601a614ff5565b846125fc565b905061375f816108ef6137596137418b6028614ff5565b6108d161374f8d601b614ff5565b8c60a001516125fc565b856125fc565b905061376b81856125fc565b6040860152505f61378e61375961378389601b614ff5565b6108cc8a6028614ff5565b9050808560035b602002015250505050505050565b5f5f6138016137e96137d16137bc61361c8a6016614ff5565b6108d16137ca8b6017614ff5565b8a516125fc565b6108d16137df8a6018614ff5565b89602001516125fc565b6108d16137f7896019614ff5565b88604001516125fc565b91505f61383861381f61381589601c614ff5565b8860800151612617565b6108d161382d8a6003614ff5565b6108cc8b6024614ff5565b90505f61386161384989601d614ff5565b6108d16138568b5f614ff5565b6108cc8c6025614ff5565b90505f61388b6138728a601e614ff5565b6108d16138808c6001614ff5565b6108cc8d6026614ff5565b90506138ca6138b26138a4856108d1868d5f01516125fc565b6108d1848c602001516125fc565b6108d16138c08c6004614ff5565b8b604001516125fc565b93505050505f6138de613724886021614ff5565b90505f6138ef613724896021614ff5565b90505f61392761390e6139038b6023614ff5565b6108d18c6006614ff5565b6108ef61391c8c6023614ff5565b6108cc8d6006614ff5565b90505f613945612b6961393a87896125fc565b6108cc8d6021614ff5565b905061395181886125fc565b90505f61397961396b6139658d6006614ff5565b876125fc565b6108ef6139658e6022614ff5565b90505f6139878c6023614ff5565b90505f613997612b6983846125fc565b60808c0185905260a08c0184905290506139b1818b6125fc565b8b6006602002015250505050505050505050505050565b5f6139d45f6001612630565b90505f6139e25f6002612630565b90505f6139f05f6003612630565b90505f613a0c613a0188601d614ff5565b6108ef89601c614ff5565b90505f613a28613a1d89601e614ff5565b6108ef8a601d614ff5565b90505f613a44613a398a601f614ff5565b6108ef8b601e614ff5565b90505f613a60613a558b6024614ff5565b6108ef8c601f614ff5565b905083613a71816108cc818b612617565b9050613a81816108cc878a612617565b9050613a91816108cc8789612617565b9050613aa2816108cc8d6008614ff5565b9050613aae818a6125fc565b60e08b01525082613ac3816108cc818b612617565b9050613ad3816108cc868a612617565b9050613ae3816108cc8689612617565b9050613af4816108cc8d6008614ff5565b9050613b00818a6125fc565b6101008b01525081613b16816108cc818b612617565b9050613b26816108cc858a612617565b9050613b36816108cc8589612617565b9050613b47816108cc8d6008614ff5565b9050613b53818a6125fc565b6101208b01525080613b69816108cc818b612617565b9050613b79816108cc848a612617565b9050613b89816108cc8489612617565b9050613b9a816108cc8d6008614ff5565b9050613ba6818a6125fc565b610140909a019990995250505050505050505050565b613bf56040518060e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b613c0084601d614ff5565b8152613c0d84601e614ff5565b6020820152613c1d846024614ff5565b6040820152613c2d846027614ff5565b6060820152613c3d846026614ff5565b6080820152613c4d846025614ff5565b60a08201525f613c5e856002614ff5565b90505f613c6b865f614ff5565b90505f613c7f8460400151855f0151612630565b90505f613c94856020015186602001516125fc565b606086015190915086905f90613caa90806125fc565b90505f613cc8613cc289602001518a606001516125fc565b886125fc565b90505f613ce7613ce08a60a001518b60400151612617565b8a51612617565b9050613cf661396582886125fc565b9050613d1d613d17613d11613d0b8487612630565b88612630565b84612617565b83612617565b9050613d45613d3a613d2f83876125fc565b6108cc8f6009614ff5565b6108cc60018a612630565b6101608c015250505050602085015160808601515f91613d6491612617565b90505f613d82613d788860600151886125fc565b8860200151612630565b90505f613da6613d9284876125fc565b6108d16137598b60a001518c5f0151612630565b9050613dce613dc3613db8838c6125fc565b6108cc8e6009614ff5565b6108cc600189612630565b6101808b0152505f9150613def9050613de8836011612617565b87516125fc565b90505f613dfc8384612617565b9050613e088182612617565b90505f613e168360096125fc565b9050613e3f613e39613724613e328b60a001518c5f0151612617565b8b51612617565b82612630565b60c089018190525f90613e5a90613cc290613d2f908d6125fc565b9050613e6d8b600b602002015182612617565b6101608c0152505086515f9250613e949150613de890613e8d9080612617565b8851612617565b90505f613ed4613eaf836108cc8a5f01518b60a00151612630565b60208901516108ef90613ec29080612617565b6108cc8b602001518c60800151612617565b9050613ef189600c60200201516108d1613cc2613d2f858d6125fc565b89600c602002015250505050505050505050565b613f6f604051806101e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b613f87613f7d86601e614ff5565b85604001516125fc565b808252613fa6906108d1613f9c88601d614ff5565b87602001516125fc565b808252613fbb906108d1613de888601c614ff5565b808252613fcd906108d1876001614ff5565b80825260208201819052613fe6906108ef87601f614ff5565b8152614001613ff6866024614ff5565b6108ef87601c614ff5565b608082015261401f614014866027614ff5565b6108ef87601f614ff5565b60608201526080810151614038906108cc816001612630565b6101c082015260808101516140789061406e90614067906108cc60015f516020615c8f5f395f51905f526155e1565b6001612617565b82606001516125fc565b60a082018190526140ae906140a0906108cc614095896002614ff5565b6108cc8a6003614ff5565b6108cc61375988600a614ff5565b83600e60200201526101c08101516140d2906140a0906108cc614095896002614ff5565b6101e084015280516140f7906108cc6140ec886002614ff5565b6108cc896003614ff5565b6101208201525f61411661410c87601f614ff5565b8360200151612630565b9050614127816108cc836001612630565b60e083015261414461413a876026614ff5565b86604001516125fc565b60408301819052614167906108d161415d896025614ff5565b88602001516125fc565b60408301819052614187906108d1614180896024614ff5565b88516125fc565b60408301526141a461419a876027614ff5565b8360400151612630565b60408301525f6141c36141b8886026614ff5565b6108ef89601e614ff5565b90506142156141f0613472614067866080015160015f516020615c8f5f395f51905f526108cc91906155e1565b6108cc614067866040015160015f516020615c8f5f395f51905f526108cc91906155e1565b60c084015260408301516142379061422d90806125fc565b8460400151612630565b61010084015260c083015161426390614255906108cc8a6004614ff5565b6108cc6139658a600a614ff5565b6102008601526101c083015161428290614255906108cc8a6004614ff5565b6102208601526101008301516142a190614255906108cc8a6004614ff5565b61024086015260e08301516142bb906108cc896004614ff5565b6101408401526142da6142cf886025614ff5565b6108ef89601d614ff5565b6101608401526080830151614318906141b89061430d90614067906108cc60015f516020615c8f5f395f51905f526155e1565b8561016001516125fc565b61018084018190526101208401516101a0850181905261434f916108d1906108cc6143448c6005614ff5565b6108cc8d6002614ff5565b6101a08401819052835161436f91906108d1906108cc6143448c5f614ff5565b6101a084018190526101408401516143879190612617565b6101a084018190526143a1906108cc6139658a600a614ff5565b6101a0840181905285600d613795565b6143ea6040518060e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b61441f6144066143fb86601c614ff5565b6108cc876025614ff5565b6108d1614414876024614ff5565b6108cc88601d614ff5565b815261446461445961444061443587601c614ff5565b6108cc88601f614ff5565b6108d161444e88601d614ff5565b6108cc89601e614ff5565b6108ef866026614ff5565b6040820181905261447990600160441b6125fc565b6040820181905261448f906108ef866027614ff5565b6040820181905281516144a29190612617565b604082018190526144b8906108cc866005614ff5565b604082015280516144cd90600160441b6125fc565b8082526144ed906108d16144e2876024614ff5565b6108cc886025614ff5565b80825260208201819052614514906108ef61450987601e614ff5565b6108d188601f614ff5565b6020820181905261452a906108cc866004614ff5565b6020820152805160608201819052614547906108d186601f614ff5565b6060820181905261456b906108ef614560876026614ff5565b6108d1886027614ff5565b60608201819052614580906108cc865f614ff5565b8160600181815250505f6145a961459f83602001518460400151612617565b8360600151612617565b90506145ba816108cc876003614ff5565b90506145d26145ca866025614ff5565b6140006125fc565b608083018190526145e8906108d1876024614ff5565b608083018190526145fb906140006125fc565b60808301819052614611906108d187601e614ff5565b60808301819052614624906140006125fc565b6080830181905261463a906108d187601d614ff5565b6080830181905261464d906140006125fc565b60808301819052614663906108d187601c614ff5565b60808301819052614679906108ef87601f614ff5565b6080830181905261468f906108cc876005614ff5565b60808301526146a26145ca866026614ff5565b60a083018190526146b8906108d1876025614ff5565b60a083018190526146cb906140006125fc565b60a083018190526146e1906108d1876024614ff5565b60a083018190526146f4906140006125fc565b60a0830181905261470a906108d187601f614ff5565b60a0830181905261471d906140006125fc565b60a08301819052614733906108d187601e614ff5565b60a08301819052614749906108ef876027614ff5565b60a0830181905261475e906108cc875f614ff5565b60a0830181905260808301515f916147769190612617565b9050614787816108cc886004614ff5565b90506147938282612617565b60c084018190526147ac906108cc61396589600b614ff5565b60c084018190528560136020020152505050505050565b6148396040518061022001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b61485261484785601c614ff5565b6108d1866002614ff5565b815261486d61486285601d614ff5565b6108d1866003614ff5565b602082015261488b61488085601e614ff5565b6108d1866004614ff5565b60408201526148a961489e85601f614ff5565b6108d1866005614ff5565b606082015280516148db906148d4906148cd906148c690806125fc565b84516125fc565b83516125fc565b82516125fc565b608082015260208101516149199061490f90614905906148fb90806125fc565b84602001516125fc565b83602001516125fc565b82602001516125fc565b60a082015260408101516149579061494d906149439061493990806125fc565b84604001516125fc565b83604001516125fc565b82604001516125fc565b60c0820152606081015161498b9061406e906149819061497790806125fc565b84606001516125fc565b83606001516125fc565b60e0820152608081015160a08201516149a49190612617565b61010082015260c081015160e08201516149be9190612617565b61012082015260a08101516149e2906149d79080612617565b826101200151612617565b61014082015260e0810151614a06906149fb9080612617565b826101000151612617565b610160820152610120810151614a1c9080612617565b6101e08201819052614a3d90614a329080612617565b826101600151612617565b6101e0820152610100810151614a539080612617565b6101a08201819052614a7490614a699080612617565b826101400151612617565b6101a08201819052610160820151614a8b91612617565b6101808201526101408101516101e0820151614aa79190612617565b6101c0820152614abb61347285600c614ff5565b6102008201819052610280840151610180830151614ae6926108d1916108cc906108ef8a6024614ff5565b8360146020020152614b1683601560200201516108d18361020001516108cc856101a001516108ef8a6025614ff5565b8360156020020152614b4683601660200201516108d18361020001516108cc856101c001516108ef8a6026614ff5565b8360166020020152614b7683601760200201516108d18361020001516108cc856101e001516108ef8a6027614ff5565b836017602002015250505050565b614bd66040518061016001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b5f6040518060800160405280614c0b7f10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e76125e4565b8152602001614c397f0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b6125e4565b8152602001614c667e544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac156125e4565b8152602001614c947f222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b6125e4565b90529050614cb1614ca686601c614ff5565b6108d1876002614ff5565b6101208301819052614cf090614ce590614cda90614ccf90806125fc565b8561012001516125fc565b8461012001516125fc565b8361012001516125fc565b8252614cfd85601d614ff5565b6020830152614d0d85601e614ff5565b6040830152614d1d85601f614ff5565b606083015281516020830151614d459161459f91614d3b9190612617565b8460400151612617565b6080830152614d5861372486600d614ff5565b6101408301528151614d7990614d6f90835f610bce565b8360800151612617565b60a0830152614da584601860200201516108d18461014001516108cc8660a001516108ef8b6024614ff5565b6103008501526020820151614dc090614d6f90836001610bce565b60c0830152614dec84601960200201516108d18461014001516108cc8660c001516108ef8b6025614ff5565b6103208501526040820151614e0790614d6f90836002610bce565b60e0830152614e3384601a60200201516108d18461014001516108cc8660e001516108ef8b6026614ff5565b6103408501526060820151614e4e90614d6f90836003610bce565b610100830152614e7c84601b60200201516108d18461014001516108cc8661010001516108ef8b6027614ff5565b84601b6135ab565b815160015b601c811015614ed657614ecc826108d18684601c8110614eab57614eab615b74565b602002015186614ebc6001876155e1565b601b8110610bce57610bce615b74565b9150600101614e89565b5092915050565b5f5f8390505f60405160208152602080820152602060408201528260608201528460808201525f516020615c8f5f395f51905f5260a082015260205f60c08360055afa80614f29575f5ffd5b505f5160809190910160405295945050505050565b614f46615321565b614f4e615321565b604051835181526020840151602082015284604082015260408160608360075afa80614f78575f5ffd5b5080518252602080820151908301526060016040529392505050565b614f9c615321565b614fa4615321565b6040518451815260208501516020820152835160408201526020840151606082015260408160808360065afa80614fd9575f5ffd5b5080518252602080820151908301526080016040529392505050565b5f8282602881111561500957615009615c5a565b6029811061501957615019615b74565b60200201519392505050565b604051806103e001604052805f81526020015f81526020015f815260200161504b615321565b8152602001615058615321565b8152602001615065615321565b8152602001615072615321565b815260200161507f615321565b815260200161508c615321565b8152602001615099615321565b81526020016150a6615321565b81526020016150b3615321565b81526020016150c0615321565b81526020016150cd615321565b81526020016150da615321565b81526020016150e7615321565b81526020016150f4615321565b8152602001615101615321565b815260200161510e615321565b815260200161511b615321565b8152602001615128615321565b8152602001615135615321565b8152602001615142615321565b815260200161514f615321565b815260200161515c615321565b8152602001615169615321565b8152602001615176615321565b8152602001615183615321565b8152602001615190615321565b815260200161519d615321565b81526020016151aa615321565b905290565b6040518061028001604052806151c36153ed565b81526020016151d0615321565b81526020016151dd615321565b81526020016151ea615321565b81526020016151f7615321565b8152602001615204615321565b8152602001615211615321565b815260200161521e615321565b815260200161522b615321565b8152602001615238615321565b815260200161524561540c565b81526020015f8152602001615258615439565b81526020015f815260200161526b615467565b8152602001615278615486565b815260200161528561536b565b81526020016151906154b4565b6040518061052001604052806029906020820280368337509192915050565b604051806101c001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020016152ef6154b4565b81526020016152fc6154b4565b81526020015f81526020015f81526020015f81526020015f8152602001606081525090565b60405180604001604052805f81526020015f81525090565b604051806040016040528061519d615321565b6040518061012001604052806009906020820280368337509192915050565b604051806103800160405280601c906020820280368337509192915050565b6040518060e0016040528061539d6154d2565b81526020015f81526020015f81526020015f81526020015f81526020016153c26154d2565b81526020015f81525090565b6040518061010001604052806008906020820280368337509192915050565b6040518061020001604052806010906020820280368337509192915050565b60405180606001604052806003905b615423615321565b81526020019060019003908161541b5790505090565b604051806103800160405280601c905b61545161534c565b8152602001906001900390816154495790505090565b604051806105400160405280602a906020820280368337509192915050565b604051806103600160405280601b905b61549e615321565b8152602001906001900390816154965790505090565b60405180608001604052806004906020820280368337509192915050565b604051806120000160405280610100906020820280368337509192915050565b5f5f5f5f60408587031215615505575f5ffd5b84356001600160401b0381111561551a575f5ffd5b8501601f8101871361552a575f5ffd5b80356001600160401b0381111561553f575f5ffd5b876020828401011115615550575f5ffd5b6020918201955093508501356001600160401b0381111561556f575f5ffd5b8501601f8101871361557f575f5ffd5b80356001600160401b03811115615594575f5ffd5b8760208260051b84010111156155a8575f5ffd5b949793965060200194505050565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417612611576126116155b6565b81810381811115612611576126116155b6565b805f5b60108110156131e25781518452602093840193909101906001016155f7565b805f5b60038110156131e25761563784835180518252602090810151910152565b6040939093019260209190910190600101615619565b805f5b601c8110156131e2578151845f5b600981101561567d57825182526020928301929091019060010161565e565b505050610120939093019260209190910190600101615650565b805f5b602a8110156131e257815184526020938401939091019060010161569a565b805f5b601b8110156131e2576156da84835180518252602090810151910152565b60409390930192602091909101906001016156bc565b805f5b601c8110156131e25781518452602093840193909101906001016156f3565b805f5b60048110156131e2578151845260209384019390910190600101615715565b8183525f6001600160fb1b0383111561574b575f5ffd5b8260051b80836020870137939093016020019392505050565b61576f8188516155f4565b5f602088015161578d61020084018280518252602090810151910152565b5060408801518051610240840152602090810151610260840152606089015180516102808501528101516102a0840152608089015180516102c08501528101516102e084015260a0890151805161030085015281015161032084015260c0890151805161034085015281015161036084015260e089015180516103808501528101516103a084015261010089015180516103c08501528101516103e084015261012089015180516104008501520151610420830152610140880151615856610440840182615616565b5061016088015161050083015261018088015161587761052084018261564d565b506101a08801516124a08301526101c08801516158986124c0840182615697565b506101e08801516158ad612a008401826156b9565b506102008801516158c26130c08401826156f0565b506102208801516158d7613440840182615712565b5061024088015180516134c08401526020908101516134e0840152610260890151805161350085015201516135208301526135c061354083018190526159209083018789615734565b613560830195909552506135808101929092526135a0909101529392505050565b634e487b7160e01b5f52604160045260245ffd5b60405161014081016001600160401b038111828210171561597857615978615941565b60405290565b604051601f8201601f191681016001600160401b03811182821017156159a6576159a6615941565b604052919050565b5f60c082840312156159be575f5ffd5b60405160c081016001600160401b03811182821017156159e0576159e0615941565b604090815283518252602080850151908301528381015190820152606080840151908201526080808401519082015260a0928301519281019290925250919050565b5f82601f830112615a31575f5ffd5b5f610360615a3e8161597e565b915083018185821115615a4f575f5ffd5b845b82811015615a69578051825260209182019101615a51565b509195945050505050565b5f82601f830112615a83575f5ffd5b5f610380615a3e8161597e565b5f610be0828403128015615aa2575f5ffd5b50615aab615955565b615ab584846159ae565b8152615ac48460c08501615a22565b6020820152615ad7846104208501615a74565b60408201526107a08301516060820152615af5846107c08501615a74565b6080820152610b4083015160a0820152610b6083015160c0820152610b8083015160e0820152610ba0830151610100820152610bc09092015161012083015250919050565b80820180821115612611576126116155b6565b5f5f85851115615b5b575f5ffd5b83861115615b67575f5ffd5b5050820193919092039150565b634e487b7160e01b5f52603260045260245ffd5b5f60018201615b9957615b996155b6565b5060010190565b80356020831015612611575f19602084900360031b1b1692915050565b5f82615bd757634e487b7160e01b5f52601260045260245ffd5b500690565b5f81615bea57615bea6155b6565b505f190190565b5f8183825b6008811015615c15578151835260209283019290910190600101615bf6565b5050506101008201905092915050565b5f82518060208501845e5f920191825250919050565b5f60208284031215615c4b575f5ffd5b815180151581146103f4575f5ffd5b634e487b7160e01b5f52602160045260245ffdfe30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4730644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", + "deployedBytecode": "0x608060405234801561000f575f5ffd5b5060043610610029575f3560e01c8063ea50d0e41461002d575b5f5ffd5b61004061003b3660046154f2565b610054565b604051901515815260200160405180910390f35b5f5f61007f7f00000000000000000000000000000000000000000000000000000000000000006102ee565b905061008c8160206155ca565b85146100ee577f0000000000000000000000000000000000000000000000000000000000000000856100bf8360206155ca565b6040516359895a5360e01b81526004810193909352602483019190915260448201526064015b60405180910390fd5b5f6100f76103fb565b90505f61012588887f0000000000000000000000000000000000000000000000000000000000000000610410565b90506010826040015161013891906155e1565b85146101575760405163fa06659360e01b815260040160405180910390fd5b60405163995bf45760e01b81525f9073__$766c1d4ff1635319bed72d911807c12b70$__9063995bf457906101fa9085908b908b907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090600401615764565b610be060405180830381865af4158015610216573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061023a9190615a90565b905061028a8787808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050855185516060810151608090910151919350915060016108a9565b815160a0015261029a8282610a10565b6102b7576040516313f8744360e31b815260040160405180910390fd5b6102c2828483610c17565b6102df576040516352ec174560e11b815260040160405180910390fd5b50600198975050505050505050565b5f8060026102fe60016008615b3a565b61030891906155ca565b9050610316600260036155ca565b6103209082615b3a565b9050600161032f6009856155ca565b61033991906155ca565b6103439082615b3a565b90506001610352816029615b3a565b61035c91906155ca565b6103669082615b3a565b9050610374600160026155ca565b61037e9082615b3a565b905061038b6001846155ca565b6103959082615b3a565b90506103a3600160046155ca565b6103ad9082615b3a565b905060026103bc6001856155e1565b6103c691906155ca565b6103d09082615b3a565b90506103dd6002806155ca565b6103e79082615b3a565b90506103f4601082615b3a565b9392505050565b610403615025565b61040b611c14565b905090565b6104186151af565b5f805b601081101561047a57610447868387610435602083615b3a565b9261044293929190615b4d565b61254f565b8351826010811061045a5761045a615b74565b6020020181815250506020826104709190615b3a565b915060010161041b565b5061049e85828661048c604083615b3a565b9261049993929190615b4d565b612562565b60208301526104ae604082615b3a565b90506104c185828661048c604083615b3a565b6040808401919091526104d49082615b3a565b90506104e785828661048c604083615b3a565b60608301526104f7604082615b3a565b905061050a85828661048c604083615b3a565b608083015261051a604082615b3a565b905061052d85828661048c604083615b3a565b60c083015261053d604082615b3a565b905061055085828661048c604083615b3a565b60e0830152610560604082615b3a565b905061057385828661048c604083615b3a565b60a0830152610583604082615b3a565b905061059685828661048c604083615b3a565b6101008301526105a7604082615b3a565b90506105ba85828661048c604083615b3a565b6101208301526105cb604082615b3a565b90506105de85828661048c604083615b3a565b610140830151526105f0604082615b3a565b9050610603858286610435602083615b3a565b610160830152610614602082615b3a565b90505f5b83811015610692575f5b60098110156106895761063c878488610435602083615b3a565b84610180015183601c811061065357610653615b74565b6020020151826009811061066957610669615b74565b60200201818152505060208361067f9190615b3a565b9250600101610622565b50600101610618565b505f5b6106a160016029615b3a565b8110156106f0576106b9868387610435602083615b3a565b836101c0015182602a81106106d0576106d0615b74565b6020020181815250506020826106e69190615b3a565b9150600101610695565b50610702858286610435602083615b3a565b6101a0830152610713602082615b3a565b905061072685828661048c604083615b3a565b6101408301516020015261073b604082615b3a565b905061074e85828661048c604083615b3a565b61014083015160026020020152610766604082615b3a565b90505f5b6107756001856155e1565b8110156107be5761078d86838761048c604083615b3a565b836101e0015182601b81106107a4576107a4615b74565b60200201526107b4604083615b3a565b915060010161076a565b505f5b83811015610811576107da868387610435602083615b3a565b83610200015182601c81106107f1576107f1615b74565b6020020181815250506020826108079190615b3a565b91506001016107c1565b505f5b60048110156108655761082e868387610435602083615b3a565b836102200151826004811061084557610845615b74565b60200201818152505060208261085b9190615b3a565b9150600101610814565b5061087785828661048c604083615b3a565b610240830152610888604082615b3a565b905061089b85828661048c604083615b3a565b610260830152509392505050565b5f600180826108d6866108d1896108cc6108c78a6310000000615b3a565b6125e4565b6125fc565b612617565b90505f6108f4876108ef8a6108cc6108c78b6001615b3a565b612630565b90505f5b61092360107f00000000000000000000000000000000000000000000000000000000000000006155e1565b811015610990575f61094d8c838151811061094057610940615b74565b6020026020010151612658565b905061095d866108cc8684612617565b955061096d856108cc8584612617565b9450610979848b612617565b9350610985838b612630565b9250506001016108f8565b505f5b60108110156109f7575f8a82601081106109af576109af615b74565b602002015190506109c4866108cc8684612617565b95506109d4856108cc8584612617565b94506109e0848b612617565b93506109ec838b612630565b925050600101610993565b50610a02848461266d565b9a9950505050505050505050565b5f5f610a2583606001518561016001516125fc565b905060015f5b7f0000000000000000000000000000000000000000000000000000000000000000811015610b17575f86610180015182601c8110610a6b57610a6b615b74565b602002015180519091505f90610a89908360015b6020020151612617565b9050848114610aab576040516313f8744360e31b815260040160405180910390fd5b5f876080015184601c8110610ac257610ac2615b74565b60200201519050610ad3838261267b565b9550610b07856108cc60016108d1856108cc8e604001518b601c8110610afb57610afb615b74565b60200201516001612630565b9450505050806001019050610a2b565b50610b20615292565b5f5b6029811015610b70576101c0870151610b3c600183615b3a565b602a8110610b4c57610b4c615b74565b6020020151828260298110610b6357610b63615b74565b6020020152600101610b22565b505f610b8582875f0151886020015186612829565b9050600160025b7f0000000000000000000000000000000000000000000000000000000000000000811015610be257610bd882896080015183601c8110610bce57610bce615b74565b60200201516125fc565b9150600101610b8c565b50610c08610bf5836108cc600185612630565b6108d18a6101a001518a606001516125fc565b94909414979650505050505050565b5f610c206152b1565b5f610c4f8460c001517f00000000000000000000000000000000000000000000000000000000000000006128a2565b90505f7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03811115610c8a57610c8a615941565b604051908082528060200260200182016040528015610cb3578160200160208202803683370190505b5090505f7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03811115610cef57610cef615941565b604051908082528060200260200182016040528015610d2857816020015b610d15615321565b815260200190600190039081610d0d5790505b509050610d5b610d56876101000151855f81518110610d4957610d49615b74565b6020026020010151612630565b61296b565b84610120018181525050610d90610d56876101000151855f81518110610d8357610d83615b74565b6020026020010151612617565b610140850181905261012085015160e0880151610db1926108d191906125fc565b845260c0860151610de190610dc59061296b565b6108cc8661012001516108ef8a60e001518961014001516125fc565b6020850152815160019083905f90610dfb57610dfb615b74565b602002602001018181525050876102400151815f81518110610e1f57610e1f615b74565b6020908102919091010152600160a08501525f60c08501528351610e42906129db565b60408501526020840151610e55906129db565b606085015260015b610e6960016024615b3a565b8111610f0a57610e8185604001518660a001516125fc565b838281518110610e9357610e93615b74565b602002602001018181525050610edc8560c001516108d18b6101c00151600185610ebd91906155e1565b602a8110610ecd57610ecd615b74565b60200201518860a001516125fc565b60c086015260a08086015190880151610ef591906125fc565b60a0860152610f0381615b88565b9050610e5d565b505f5b6005811015610fe3575f610f22601e83615b3a565b90505f610f3160016024615b3a565b610f3b9084615b3a565b9050610f6c858381518110610f5257610f52615b74565b60200260200101516108d189606001518a60a001516125fc565b858381518110610f7e57610f7e615b74565b602002602001018181525050610fbb8760c001516108d18d6101c0015184602a8110610fac57610fac615b74565b60200201518a60a001516125fc565b60c088015260a080880151908a0151610fd491906125fc565b60a08801525050600101610f0d565b50876020015181600181518110610ffc57610ffc615b74565b602002602001018190525086606001518160028151811061101f5761101f615b74565b602002602001018190525086608001518160038151811061104257611042615b74565b60200260200101819052508660a001518160048151811061106557611065615b74565b60200260200101819052508660c001518160058151811061108857611088615b74565b60200260200101819052508660e00151816006815181106110ab576110ab615b74565b6020026020010181905250866101000151816007815181106110cf576110cf615b74565b6020026020010181905250866101200151816008815181106110f3576110f3615b74565b60200260200101819052508661014001518160098151811061111757611117615b74565b602002602001018190525086610160015181600a8151811061113b5761113b615b74565b6020026020010181905250866101c0015181600b8151811061115f5761115f615b74565b602002602001018190525086610180015181600c8151811061118357611183615b74565b6020026020010181905250866101a0015181600d815181106111a7576111a7615b74565b6020026020010181905250866101e0015181600e815181106111cb576111cb615b74565b602002602001018190525086610200015181600f815181106111ef576111ef615b74565b60200260200101819052508661022001518160108151811061121357611213615b74565b60200260200101819052508661024001518160118151811061123757611237615b74565b60200260200101819052508661026001518160128151811061125b5761125b615b74565b60200260200101819052508661028001518160138151811061127f5761127f615b74565b6020026020010181905250866102a00151816014815181106112a3576112a3615b74565b6020026020010181905250866102c00151816015815181106112c7576112c7615b74565b6020026020010181905250866102e00151816016815181106112eb576112eb615b74565b60200260200101819052508661030001518160178151811061130f5761130f615b74565b60200260200101819052508661032001518160188151811061133357611333615b74565b60200260200101819052508661034001518160198151811061135757611357615b74565b602002602001018190525086610360015181601a8151811061137b5761137b615b74565b602002602001018190525086610380015181601b8151811061139f5761139f615b74565b6020026020010181905250866103a0015181601c815181106113c3576113c3615b74565b6020026020010181905250866103c0015181601d815181106113e7576113e7615b74565b6020026020010181905250876040015181601e8151811061140a5761140a615b74565b6020026020010181905250876060015181601f8151811061142d5761142d615b74565b602002602001018190525087608001518160208151811061145057611450615b74565b60200260200101819052508760a001518160218151811061147357611473615b74565b60200260200101819052508761012001518160228151811061149757611497615b74565b6020026020010181905250876101000151816023815181106114bb576114bb615b74565b60200260200101819052508760c00151816024815181106114de576114de615b74565b60200260200101819052508760e001518160258151811061150157611501615b74565b60200260200101819052505f61154787608001518660c001518b6102000151877f00000000000000000000000000000000000000000000000000000000000000006129ed565b9050611571815f8151811061155e5761155e615b74565b60200260200101518661012001516125fc565b608086018190526102008a01515160e08901516115a292916108d19161159791906125fc565b8861014001516125fc565b608086015260e08701516115b590612b4b565b60a08601525f6115c760016024615b3a565b6115d2906001615b3a565b90505f5b61160160017f00000000000000000000000000000000000000000000000000000000000000006155e1565b811015611812575f61163460017f00000000000000000000000000000000000000000000000000000000000000006155e1565b8210159050806117a357611668610d568b6101000151898560016116589190615b3a565b81518110610d4957610d49615b74565b6101208901526101008a015161169890610d569089611688866001615b3a565b81518110610d8357610d83615b74565b61014089015260a08801516101208901516116b391906125fc565b61016089015260a088015160e08b01516116db916116d0916125fc565b8961014001516125fc565b61018089018190526116fe906116f0906129db565b6108d18a61016001516129db565b866117098486615b3a565b8151811061171957611719615b74565b6020026020010181815250505f6117528961018001518e61020001518560016117429190615b3a565b601c8110610bce57610bce615b74565b905061178c816108d18b61016001518887600161176f9190615b3a565b8151811061177f5761177f615b74565b60200260200101516125fc565b905061179c896080015182612617565b60808a0152505b6117c26117b88960a001518c60e001516125fc565b8b60e001516125fc565b60a08901526101e08c015182601b81106117de576117de615b74565b6020020151856117ee8486615b3a565b815181106117fe576117fe615b74565b6020908102919091010152506001016115d6565b5061183e60017f00000000000000000000000000000000000000000000000000000000000000006155e1565b6118489082615b3a565b90506118686118608961010001518a60c00151612630565b60019061266d565b60e08701515261010088015160c08901516118ac91611860916108ef907f07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76906125fc565b60e0878101805160200192909252815180516040909101529051805160609091015260a0870151908901516118ee916118e4916125fc565b8960e001516125fc565b60a08701525f5b600481101561199c575f6119278860e00151836004811061191857611918615b74565b60200201518960a001516125fc565b9050611932816129db565b886101000151836004811061194957611949615b74565b602002015260a088015160e08b015161196291906125fc565b8860a001818152505061198e88608001516108d1838f61022001518660048110610bce57610bce615b74565b6080890152506001016118f5565b506101008601515184518590839081106119b8576119b8615b74565b602090810291909101810191909152610100870151908101516119dc916002610a7f565b846119e8836001615b3a565b815181106119f8576119f8615b74565b60209081029190910101526101008601516060015184611a19836002615b3a565b81518110611a2957611a29615b74565b60209081029190910101525f5b6003811015611a8b578a61014001518160038110611a5657611a56615b74565b60200201518483611a6681615b88565b945081518110611a7857611a78615b74565b6020908102919091010152600101611a36565b506040518060400160405280600181526020016002815250838281518110611ab557611ab5615b74565b60200260200101819052508560800151848280611ad190615b88565b935081518110611ae357611ae3615b74565b602002602001018181525050611b0d8a61022001518960c001518a608001518d6101a00151612b56565b611b2a5760405163a2a2ac8360e01b815260040160405180910390fd5b5f8a6102600151905080848381518110611b4657611b46615b74565b6020026020010181905250886101000151858381518110611b6957611b69615b74565b602002602001018181525050611b7d615339565b611b878587612eb9565b8152611b9282612fb1565b602082018190528c5182515f92611baa929190612ff7565b90505f5f611bba8f5f0151613088565b91509150611bc782613149565b611bd081613149565b8351611bdd9083856131e8565b84526020840151611bef9082856131e8565b602085018190528451611c0191613217565b9f9e505050505050505050505050505050565b611c1c615025565b50604080516103e081018252620200008152601160208083019190915261024382840152825180840184527f039702f4c2b2b49fd29d82b077cbf9d1df2871e0fbade17241573ca5021603fc81527f03270b3b2673844695842df11cdbabf54acafe03642adfdfbf69834247ed0720818301526060830152825180840184527f1ccb0308d0359b8835480fa7dd829fdbf16b136d57e85c1621efb1679fe1a94c81527f207076fed3c60bbeb2df5f48c86b12f4027c523f9b8e69456ca51643fea3de1d818301526080830152825180840184527f067b40a80be55b95cc17070cf5dc78441e4868acea0ac1e46b5708705151590581527f0792e6c3d76aeeed6ee7d7e443138c813b74dbcd8ce92990e6f6747adc0af0548183015260a0830152825180840184527f148a04387a9e0a8cec6a4a0d90706f5907e8bb257fb04f8a3cfe07c18a7c101481527f225866d9efd4a5be4eb9a00016f902f844e3d81e2768b24dc92ecbd6b34176bd8183015260c0830152825180840184527f0dacf57ed82c686ca051fca3f1820016cb49e91b5d3c4bee7f9fa19800d9ed0681527f22ba5fda41aa14aeb7a3a2731fbfe3a673cf7db1d2217d32db491f44344309058183015260e0830152825180840184527f0f783f69a36c2bac47b9c59ff33ccc583a284a46b282591b9e9bbb85d8b5e5dc81527f029b91609a06e94924f0d83530fc01f3427f5ab729a0959903108c2ccc86825c81830152610100830152825180840184527f11beb2731ab8cf46c0a52666bb457fa74d4b635043bb5689a35a2aee24990b8081527f2f1fba989036c44aedb609d0d3341ccb842c2d1cd7c80cea4a95d0eb4530577081830152610120830152825180840184527f28ae1f7459c3c5d9ca2f80f8e5c28bb301d0245228bade0e2bb962b4bfbe301b81527f0622f7db14381bbecf862e186cfa570588160466ae7d4fed4b35a70d99af914f81830152610140830152825180840184527f233983f621ab48bdce4f7216bdbc54c49463e90d88301b9ab224fca61c93dba081527f265c3834d63be0bc710c9b4a48015e2442a3b9bf63caec9db0064dfb1bd569c481830152610160830152825180840184527f06d21b82f49e04e078d4ad30cd6828eaa66c1bf984bc52b79de8c786fe4dfe7a81527f301aa91e167bbc765996ac6d2889e7d49f0734bc7abb3a4b85900143556a1b3f81830152610180830152825180840184527f1222d34f53e6b36138627277789ad131ea24f79caca2d5f59193c5e3a7dfdde781527f245a380726977d41c9f0a9a0ba33608994eb3660de6a8efba38009df93da679a818301526101a0830152825180840184527f2dfc248e28ad889b5b692077f338d28b2f687c6e909b9693f8e8fd25cefc352881527f16e2cbc305166ea90a426987efff2020827d8c548ea4155c659df296f14f00fd818301526101c0830152825180840184527f0d815a04effc6e57ec95f3ac112f76ab4a9d993d044626ceb54f967c8d77d8e181527f1df4c065a1129d5cbdf244cb9cc2eaf406c2f5fbab4c393347d57ae5e2ab881f818301526101e0830152825180840184527f118733dc1db84685f4160119cb0cd9a51b831cf0643e914daf1c63fc7967297981527f27b63107b52edf239acb1bd47e24c97094422cc2bba51ee0f54b2707946be55681830152610200830152825180840184527f2e7a11cec89df0ad6b730cf80be4246e56e1667f55e35a22c9b39b196226135b81527f0891267297e339bedeef69881477b4e5c19e761e127e3d3772714f92ade80fd381830152610220830152825180840184527f16f90d7d5c3fd7ab9f490dd5d2b94afea698039e2456c1d5f0e22159348d1e0c81527f29ad721b49b02192f5323326fd803b102f7a09b0371c537db998014fa14c307981830152610240830152825180840184527f0a5e4e29375b31024f09dd662b460eb25930835255c62dc74d62d0c983981e4281527f069adf56537452ecad2fcd8cdea1291fbf273d7e752d9ba0812900e7215c8fc081830152610260830152825180840184527f1bd554f207de2e42cbc94e8579154a07e37f3ba9d80832f28e38dffaaccd1ade81527f22261d045539dbaabf26f23773b365b2ddd30af639a8b15a56e777f5607bf43581830152610280830152825180840184527f11769064ef79db1f35b23b8b1e5c118e769e1a41b912f49dfdbffbacf23d056481527f16f17c1721186bbd19d09f1645f968677cb1ad0c6b656c3849a4b7f27d5279d9818301526102a0830152825180840184527f0cad91097e151471556985b94a0188b759472f9bb3295fe6de6846f2d4fe27e181527f042f4a199546b40704a9c4822bfd20a91bc478055dc5676c7347f64545b47f4d818301526102c0830152825180840184527f1286a8beb1993863980820c27dfb00f93f8949f60933042e5aa8275dd48e55f781527f2037878d67d1009eb6dac43c5f6f076f884d456e057b000912d40a2ec440df1f818301526102e0830152825180840184527f1c0413fa3552ba4738b423829d55fcc03c912bf931336778a458e1c25cd4f55881527f190904e6db04c862bd9d7406029e03a1103a513203eb6d8faa010021eedf18d481830152610300830152825180840184527f099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d2681527e15b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f81830152610320830152825180840184527f1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e81527f305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d1981830152610340830152825180840184527f061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d81527f1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e81830152610360830152825180840184527f043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce81527f261522c4089330646aff96736194949330952ae74c573d1686d9cb4a007338548183015261038083015282518084018452600181526002818301526103a083015282518084019093527f0ad44c49c91e4374983e023f10e6256c39d27b2857eb2d2ce82a82b51e02dc3883527f23ad26f46d7b04b00d6ebe50273765d49c59d9db9ae22dd33909bc3c9efc5308908301526103c081019190915290565b5f6103f461255d8385615ba0565b612658565b61256a615321565b60408051808201909152805f516020615c6f5f395f51905f5261259060205f8789615b4d565b61259991615ba0565b6125a39190615bbd565b81526020908101905f516020615c6f5f395f51905f52906125c8906040908789615b4d565b6125d191615ba0565b6125db9190615bbd565b90529392505050565b5f5f516020615c8f5f395f51905f52825b0692915050565b5f5f516020615c8f5f395f51905f5282840990505b92915050565b5f5f516020615c8f5f395f51905f528284089392505050565b5f5f516020615c8f5f395f51905f52825f516020615c8f5f395f51905f520384089392505050565b5f5f516020615c8f5f395f51905f52826125f5565b5f6103f4836108cc8461296b565b5f5f604051806101200160405280619d8081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec5181526020016105a081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31815260200161024081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd3181526020016105a081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec518152602001619d8081525090505f600190505f5f90505b60098110156127745761276a826108cc8784612630565b9150600101612753565b5061277d61534c565b5f5b60098110156127ce576127af610d568583600981106127a0576127a0615b74565b60200201516108cc8985612630565b8282600981106127c1576127c1615b74565b602002015260010161277f565b505f5b60098110156128145761280a856108d18984600981106127f3576127f3615b74565b6020020151858560098110610bce57610bce615b74565b94506001016127d1565b5061281f84836125fc565b9695505050505050565b5f61283261536b565b61283d86828561340b565b612849868683866135b7565b612855868683866137a3565b6128608682856139c8565b61286b868285613bbc565b61287786868386613f05565b6128828682856143b1565b61288d8682856147c3565b612898868285614b84565b61281f8185614e84565b60605f826001600160401b038111156128bd576128bd615941565b6040519080825280602002602001820160405280156128e6578160200160208202803683370190505b50905083815f815181106128fc576128fc615b74565b602090810291909101015260015b838110156129635761293e826129216001846155e1565b8151811061293157612931615b74565b6020026020010151612b4b565b82828151811061295057612950615b74565b602090810291909101015260010161290a565b509392505050565b5f5f8290505f604051602081526020808201526020604082015282606082015260025f516020615c8f5f395f51905f520360808201525f516020615c8f5f395f51905f5260a082015260205f60c08360055afa806129c7575f5ffd5b505f51608091909101604052949350505050565b5f516020615c8f5f395f51905f520390565b60605f826001600160401b03811115612a0857612a08615941565b604051908082528060200260200182016040528015612a31578160200160208202803683370190505b509050825b8015612b40575f85612a496001846155e1565b81518110612a5957612a59615b74565b602002602001015190505f89600184612a7291906155e1565b601c8110612a8257612a82615b74565b602002015190505f612add612aa1612a9a858d6125fc565b60026125fc565b6108ef8b612ab06001896155e1565b601c8110612ac057612ac0615b74565b60200201516108cc612ad7886108cc60018a612630565b87612630565b9050612afe816108cc610d56612af8876108cc600189612630565b86612617565b99508990508085612b106001876155e1565b81518110612b2057612b20615b74565b60200260200101818152505050505080612b3990615bdc565b9050612a36565b509695505050505050565b5f61261182836125fc565b5f600181612b6f612b6987610100614edd565b83612630565b905080612b8f5760405163835eb8f760e01b815260040160405180910390fd5b612b9761538a565b80518390525f5b7f0000000000000000000000000000000000000000000000000000000000000000811015612c80575f612bd28260096155ca565b612bdd906001615b3a565b905084835f0151826101008110612bf657612bf6615b74565b60200201525f612c07826001615b3a565b90505b612c15600983615b3a565b811015612c76578351612c5590612c2d6001846155e1565b6101008110612c3e57612c3e615b74565b60200201518a85601c8110610bce57610bce615b74565b8451826101008110612c6957612c69615b74565b6020020152600101612c0a565b5050600101612b9e565b50608081018390525f602082018190525b610100811015612d9157612cb2612cac83608001518a6125fc565b85612630565b8260a00151826101008110612cc957612cc9615b74565b602002015260a0820151612cf390826101008110612ce957612ce9615b74565b602002015161296b565b8260a00151826101008110612d0a57612d0a615b74565b602002018181525050612d5082602001516108d1845f0151846101008110612d3457612d34615b74565b60200201518560a00151856101008110610bce57610bce615b74565b60208301526080820151612d84907f204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d66125fc565b6080830152600101612c91565b505f612da2836108cc61010061296b565b9050612db28260200151826125fc565b602083015260a0820151612dcd905f5b6020020151826125fc565b604083015260a0820151612df990612de860016101006155e1565b6101008110612dc257612dc2615b74565b60608301526040820151612e0f908a6002610bce565b60c08301819052612e6f906108d1612e478b7f204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6612630565b60208d015160408e01516108cc91612e5e91612630565b8e5160208901516108ef91906125fc565b60c083018190526060830151612ea491612e98916108d1906108cc8e600260200201518c612630565b6108ef858c6003610bce565b60c08301819052159998505050505050505050565b612ec1615321565b7f00000000000000000000000000000000000000000000000000000000000000005f5b81811015612f1657612f0e858281518110612f0157612f01615b74565b6020026020010151613149565b600101612ee4565b50604051600190815b60018401811015612f7b5760208102870160208202870181515160408501528151602001516060850152805160808501525050604080830160606040850160075afa8316925060408260808460065afa90921691600101612f1f565b5080518452602081015160208501525080612fa9576040516352ec174560e11b815260040160405180910390fd5b505092915050565b612fb9615321565b5f516020615c6f5f395f51905f5282602001515f516020615c6f5f395f51905f52612fe491906155e1565b612fee9190615bbd565b60208301525090565b5f5f5f61300386613088565b9150915061300f6153ce565b82518152602080840151818301528251604080840191909152838201516060840152875160808401528782015160a0840152865160c08401528682015160e08401525161307d9161306291849101615bf1565b60405160208183030381529060405280519060200120612658565b979650505050505050565b613090615321565b613098615321565b82516020808501516040860151606087015160cc90811b608892831b604494851b90961795909517949094178652608087015160a088015160c089015160e08a0151871b90841b91851b9092171717868401526101008701516101208801516101408901516101608a0151871b90841b91851b909217171785526101808701516101a08801516101c08901516101e09099015190951b9790911b9390911b1791909117939093179281019290925291565b805160208201515f5f516020615c6f5f395f51905f528380095f516020615c6f5f395f51905f5260035f516020615c6f5f395f51905f52838709085f516020615c6f5f395f51905f5284850914915050806131e25760405162461bcd60e51b8152602060048201526019602482015278706f696e74206973206e6f74206f6e2074686520637572766560381b60448201526064016100e5565b50505050565b6131f0615321565b6131f8615321565b6132028386614f3e565b905061320e8185614f94565b95945050505050565b81516020808401518351848301516040805194850195909552938301919091527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260608301527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed60808301527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b60a08301527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60c083015260e08201526101008101919091527f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c16101208201527f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b06101408201527f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe46101608201527f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e556101808201525f9081906101a00160405160208183030381529060405290505f5f60086001600160a01b0316836040516133ad9190615c25565b5f60405180830381855afa9150503d805f81146133e5576040519150601f19603f3d011682016040523d82523d5f602084013e6133ea565b606091505b509150915081801561281f57508080602001905181019061281f9190615c3b565b5f613417846007614ff5565b90507f183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f80000005f61347861347261344c856003612630565b6108cc61346761345c8b5f614ff5565b6108cc8c601d614ff5565b6108cc8b601c614ff5565b836125fc565b90506134f96134ee6134d56134bc6134a3856108d16134988d6002614ff5565b6108cc8e601c614ff5565b6108d16134b18c6003614ff5565b6108cc8d601d614ff5565b6108d16134ca8b6004614ff5565b6108cc8c601e614ff5565b6108d16134e38a6005614ff5565b6108cc8b601f614ff5565b6108d1886001614ff5565b9050613518816108d161350d866001612630565b6108cc8a6027614ff5565b905061352481846125fc565b905061353081856125fc565b8552505f905061356a61356061355561354a88601c614ff5565b6108d189601f614ff5565b6108ef886024614ff5565b6108d1875f614ff5565b905061357b816108cc846002612630565b905061358c816108cc846001612630565b905061359881836125fc565b90506135a481846125fc565b9050808460015b60200201525050505050565b5f5f5f6135ed6135e36135cb89601c614ff5565b6108d16135d98b6012614ff5565b8a606001516125fc565b8760800151612617565b9050613626816108cc61361c6136048b601d614ff5565b6108d16136128d6013614ff5565b8c606001516125fc565b8960800151612617565b905061364b816108cc61361c61363d8b601e614ff5565b6108d16136128d6014614ff5565b9050613670816108cc61361c6136628b601f614ff5565b6108d16136128d6015614ff5565b92505f90506136946135e361368689601c614ff5565b6108d16135d98b600e614ff5565b90506136b9816108cc61361c6136ab8b601d614ff5565b6108d16136128d600f614ff5565b90506136de816108cc61361c6136d08b601e614ff5565b6108d16136128d6010614ff5565b9050613703816108cc61361c6136f58b601f614ff5565b6108d16136128d6011614ff5565b91505f905061372a613724613719896020614ff5565b6108d18a601a614ff5565b846125fc565b905061375f816108ef6137596137418b6028614ff5565b6108d161374f8d601b614ff5565b8c60a001516125fc565b856125fc565b905061376b81856125fc565b6040860152505f61378e61375961378389601b614ff5565b6108cc8a6028614ff5565b9050808560035b602002015250505050505050565b5f5f6138016137e96137d16137bc61361c8a6016614ff5565b6108d16137ca8b6017614ff5565b8a516125fc565b6108d16137df8a6018614ff5565b89602001516125fc565b6108d16137f7896019614ff5565b88604001516125fc565b91505f61383861381f61381589601c614ff5565b8860800151612617565b6108d161382d8a6003614ff5565b6108cc8b6024614ff5565b90505f61386161384989601d614ff5565b6108d16138568b5f614ff5565b6108cc8c6025614ff5565b90505f61388b6138728a601e614ff5565b6108d16138808c6001614ff5565b6108cc8d6026614ff5565b90506138ca6138b26138a4856108d1868d5f01516125fc565b6108d1848c602001516125fc565b6108d16138c08c6004614ff5565b8b604001516125fc565b93505050505f6138de613724886021614ff5565b90505f6138ef613724896021614ff5565b90505f61392761390e6139038b6023614ff5565b6108d18c6006614ff5565b6108ef61391c8c6023614ff5565b6108cc8d6006614ff5565b90505f613945612b6961393a87896125fc565b6108cc8d6021614ff5565b905061395181886125fc565b90505f61397961396b6139658d6006614ff5565b876125fc565b6108ef6139658e6022614ff5565b90505f6139878c6023614ff5565b90505f613997612b6983846125fc565b60808c0185905260a08c0184905290506139b1818b6125fc565b8b6006602002015250505050505050505050505050565b5f6139d45f6001612630565b90505f6139e25f6002612630565b90505f6139f05f6003612630565b90505f613a0c613a0188601d614ff5565b6108ef89601c614ff5565b90505f613a28613a1d89601e614ff5565b6108ef8a601d614ff5565b90505f613a44613a398a601f614ff5565b6108ef8b601e614ff5565b90505f613a60613a558b6024614ff5565b6108ef8c601f614ff5565b905083613a71816108cc818b612617565b9050613a81816108cc878a612617565b9050613a91816108cc8789612617565b9050613aa2816108cc8d6008614ff5565b9050613aae818a6125fc565b60e08b01525082613ac3816108cc818b612617565b9050613ad3816108cc868a612617565b9050613ae3816108cc8689612617565b9050613af4816108cc8d6008614ff5565b9050613b00818a6125fc565b6101008b01525081613b16816108cc818b612617565b9050613b26816108cc858a612617565b9050613b36816108cc8589612617565b9050613b47816108cc8d6008614ff5565b9050613b53818a6125fc565b6101208b01525080613b69816108cc818b612617565b9050613b79816108cc848a612617565b9050613b89816108cc8489612617565b9050613b9a816108cc8d6008614ff5565b9050613ba6818a6125fc565b610140909a019990995250505050505050505050565b613bf56040518060e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b613c0084601d614ff5565b8152613c0d84601e614ff5565b6020820152613c1d846024614ff5565b6040820152613c2d846027614ff5565b6060820152613c3d846026614ff5565b6080820152613c4d846025614ff5565b60a08201525f613c5e856002614ff5565b90505f613c6b865f614ff5565b90505f613c7f8460400151855f0151612630565b90505f613c94856020015186602001516125fc565b606086015190915086905f90613caa90806125fc565b90505f613cc8613cc289602001518a606001516125fc565b886125fc565b90505f613ce7613ce08a60a001518b60400151612617565b8a51612617565b9050613cf661396582886125fc565b9050613d1d613d17613d11613d0b8487612630565b88612630565b84612617565b83612617565b9050613d45613d3a613d2f83876125fc565b6108cc8f6009614ff5565b6108cc60018a612630565b6101608c015250505050602085015160808601515f91613d6491612617565b90505f613d82613d788860600151886125fc565b8860200151612630565b90505f613da6613d9284876125fc565b6108d16137598b60a001518c5f0151612630565b9050613dce613dc3613db8838c6125fc565b6108cc8e6009614ff5565b6108cc600189612630565b6101808b0152505f9150613def9050613de8836011612617565b87516125fc565b90505f613dfc8384612617565b9050613e088182612617565b90505f613e168360096125fc565b9050613e3f613e39613724613e328b60a001518c5f0151612617565b8b51612617565b82612630565b60c089018190525f90613e5a90613cc290613d2f908d6125fc565b9050613e6d8b600b602002015182612617565b6101608c0152505086515f9250613e949150613de890613e8d9080612617565b8851612617565b90505f613ed4613eaf836108cc8a5f01518b60a00151612630565b60208901516108ef90613ec29080612617565b6108cc8b602001518c60800151612617565b9050613ef189600c60200201516108d1613cc2613d2f858d6125fc565b89600c602002015250505050505050505050565b613f6f604051806101e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b613f87613f7d86601e614ff5565b85604001516125fc565b808252613fa6906108d1613f9c88601d614ff5565b87602001516125fc565b808252613fbb906108d1613de888601c614ff5565b808252613fcd906108d1876001614ff5565b80825260208201819052613fe6906108ef87601f614ff5565b8152614001613ff6866024614ff5565b6108ef87601c614ff5565b608082015261401f614014866027614ff5565b6108ef87601f614ff5565b60608201526080810151614038906108cc816001612630565b6101c082015260808101516140789061406e90614067906108cc60015f516020615c8f5f395f51905f526155e1565b6001612617565b82606001516125fc565b60a082018190526140ae906140a0906108cc614095896002614ff5565b6108cc8a6003614ff5565b6108cc61375988600a614ff5565b83600e60200201526101c08101516140d2906140a0906108cc614095896002614ff5565b6101e084015280516140f7906108cc6140ec886002614ff5565b6108cc896003614ff5565b6101208201525f61411661410c87601f614ff5565b8360200151612630565b9050614127816108cc836001612630565b60e083015261414461413a876026614ff5565b86604001516125fc565b60408301819052614167906108d161415d896025614ff5565b88602001516125fc565b60408301819052614187906108d1614180896024614ff5565b88516125fc565b60408301526141a461419a876027614ff5565b8360400151612630565b60408301525f6141c36141b8886026614ff5565b6108ef89601e614ff5565b90506142156141f0613472614067866080015160015f516020615c8f5f395f51905f526108cc91906155e1565b6108cc614067866040015160015f516020615c8f5f395f51905f526108cc91906155e1565b60c084015260408301516142379061422d90806125fc565b8460400151612630565b61010084015260c083015161426390614255906108cc8a6004614ff5565b6108cc6139658a600a614ff5565b6102008601526101c083015161428290614255906108cc8a6004614ff5565b6102208601526101008301516142a190614255906108cc8a6004614ff5565b61024086015260e08301516142bb906108cc896004614ff5565b6101408401526142da6142cf886025614ff5565b6108ef89601d614ff5565b6101608401526080830151614318906141b89061430d90614067906108cc60015f516020615c8f5f395f51905f526155e1565b8561016001516125fc565b61018084018190526101208401516101a0850181905261434f916108d1906108cc6143448c6005614ff5565b6108cc8d6002614ff5565b6101a08401819052835161436f91906108d1906108cc6143448c5f614ff5565b6101a084018190526101408401516143879190612617565b6101a084018190526143a1906108cc6139658a600a614ff5565b6101a0840181905285600d613795565b6143ea6040518060e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b61441f6144066143fb86601c614ff5565b6108cc876025614ff5565b6108d1614414876024614ff5565b6108cc88601d614ff5565b815261446461445961444061443587601c614ff5565b6108cc88601f614ff5565b6108d161444e88601d614ff5565b6108cc89601e614ff5565b6108ef866026614ff5565b6040820181905261447990600160441b6125fc565b6040820181905261448f906108ef866027614ff5565b6040820181905281516144a29190612617565b604082018190526144b8906108cc866005614ff5565b604082015280516144cd90600160441b6125fc565b8082526144ed906108d16144e2876024614ff5565b6108cc886025614ff5565b80825260208201819052614514906108ef61450987601e614ff5565b6108d188601f614ff5565b6020820181905261452a906108cc866004614ff5565b6020820152805160608201819052614547906108d186601f614ff5565b6060820181905261456b906108ef614560876026614ff5565b6108d1886027614ff5565b60608201819052614580906108cc865f614ff5565b8160600181815250505f6145a961459f83602001518460400151612617565b8360600151612617565b90506145ba816108cc876003614ff5565b90506145d26145ca866025614ff5565b6140006125fc565b608083018190526145e8906108d1876024614ff5565b608083018190526145fb906140006125fc565b60808301819052614611906108d187601e614ff5565b60808301819052614624906140006125fc565b6080830181905261463a906108d187601d614ff5565b6080830181905261464d906140006125fc565b60808301819052614663906108d187601c614ff5565b60808301819052614679906108ef87601f614ff5565b6080830181905261468f906108cc876005614ff5565b60808301526146a26145ca866026614ff5565b60a083018190526146b8906108d1876025614ff5565b60a083018190526146cb906140006125fc565b60a083018190526146e1906108d1876024614ff5565b60a083018190526146f4906140006125fc565b60a0830181905261470a906108d187601f614ff5565b60a0830181905261471d906140006125fc565b60a08301819052614733906108d187601e614ff5565b60a08301819052614749906108ef876027614ff5565b60a0830181905261475e906108cc875f614ff5565b60a0830181905260808301515f916147769190612617565b9050614787816108cc886004614ff5565b90506147938282612617565b60c084018190526147ac906108cc61396589600b614ff5565b60c084018190528560136020020152505050505050565b6148396040518061022001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b61485261484785601c614ff5565b6108d1866002614ff5565b815261486d61486285601d614ff5565b6108d1866003614ff5565b602082015261488b61488085601e614ff5565b6108d1866004614ff5565b60408201526148a961489e85601f614ff5565b6108d1866005614ff5565b606082015280516148db906148d4906148cd906148c690806125fc565b84516125fc565b83516125fc565b82516125fc565b608082015260208101516149199061490f90614905906148fb90806125fc565b84602001516125fc565b83602001516125fc565b82602001516125fc565b60a082015260408101516149579061494d906149439061493990806125fc565b84604001516125fc565b83604001516125fc565b82604001516125fc565b60c0820152606081015161498b9061406e906149819061497790806125fc565b84606001516125fc565b83606001516125fc565b60e0820152608081015160a08201516149a49190612617565b61010082015260c081015160e08201516149be9190612617565b61012082015260a08101516149e2906149d79080612617565b826101200151612617565b61014082015260e0810151614a06906149fb9080612617565b826101000151612617565b610160820152610120810151614a1c9080612617565b6101e08201819052614a3d90614a329080612617565b826101600151612617565b6101e0820152610100810151614a539080612617565b6101a08201819052614a7490614a699080612617565b826101400151612617565b6101a08201819052610160820151614a8b91612617565b6101808201526101408101516101e0820151614aa79190612617565b6101c0820152614abb61347285600c614ff5565b6102008201819052610280840151610180830151614ae6926108d1916108cc906108ef8a6024614ff5565b8360146020020152614b1683601560200201516108d18361020001516108cc856101a001516108ef8a6025614ff5565b8360156020020152614b4683601660200201516108d18361020001516108cc856101c001516108ef8a6026614ff5565b8360166020020152614b7683601760200201516108d18361020001516108cc856101e001516108ef8a6027614ff5565b836017602002015250505050565b614bd66040518061016001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b5f6040518060800160405280614c0b7f10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e76125e4565b8152602001614c397f0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b6125e4565b8152602001614c667e544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac156125e4565b8152602001614c947f222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b6125e4565b90529050614cb1614ca686601c614ff5565b6108d1876002614ff5565b6101208301819052614cf090614ce590614cda90614ccf90806125fc565b8561012001516125fc565b8461012001516125fc565b8361012001516125fc565b8252614cfd85601d614ff5565b6020830152614d0d85601e614ff5565b6040830152614d1d85601f614ff5565b606083015281516020830151614d459161459f91614d3b9190612617565b8460400151612617565b6080830152614d5861372486600d614ff5565b6101408301528151614d7990614d6f90835f610bce565b8360800151612617565b60a0830152614da584601860200201516108d18461014001516108cc8660a001516108ef8b6024614ff5565b6103008501526020820151614dc090614d6f90836001610bce565b60c0830152614dec84601960200201516108d18461014001516108cc8660c001516108ef8b6025614ff5565b6103208501526040820151614e0790614d6f90836002610bce565b60e0830152614e3384601a60200201516108d18461014001516108cc8660e001516108ef8b6026614ff5565b6103408501526060820151614e4e90614d6f90836003610bce565b610100830152614e7c84601b60200201516108d18461014001516108cc8661010001516108ef8b6027614ff5565b84601b6135ab565b815160015b601c811015614ed657614ecc826108d18684601c8110614eab57614eab615b74565b602002015186614ebc6001876155e1565b601b8110610bce57610bce615b74565b9150600101614e89565b5092915050565b5f5f8390505f60405160208152602080820152602060408201528260608201528460808201525f516020615c8f5f395f51905f5260a082015260205f60c08360055afa80614f29575f5ffd5b505f5160809190910160405295945050505050565b614f46615321565b614f4e615321565b604051835181526020840151602082015284604082015260408160608360075afa80614f78575f5ffd5b5080518252602080820151908301526060016040529392505050565b614f9c615321565b614fa4615321565b6040518451815260208501516020820152835160408201526020840151606082015260408160808360065afa80614fd9575f5ffd5b5080518252602080820151908301526080016040529392505050565b5f8282602881111561500957615009615c5a565b6029811061501957615019615b74565b60200201519392505050565b604051806103e001604052805f81526020015f81526020015f815260200161504b615321565b8152602001615058615321565b8152602001615065615321565b8152602001615072615321565b815260200161507f615321565b815260200161508c615321565b8152602001615099615321565b81526020016150a6615321565b81526020016150b3615321565b81526020016150c0615321565b81526020016150cd615321565b81526020016150da615321565b81526020016150e7615321565b81526020016150f4615321565b8152602001615101615321565b815260200161510e615321565b815260200161511b615321565b8152602001615128615321565b8152602001615135615321565b8152602001615142615321565b815260200161514f615321565b815260200161515c615321565b8152602001615169615321565b8152602001615176615321565b8152602001615183615321565b8152602001615190615321565b815260200161519d615321565b81526020016151aa615321565b905290565b6040518061028001604052806151c36153ed565b81526020016151d0615321565b81526020016151dd615321565b81526020016151ea615321565b81526020016151f7615321565b8152602001615204615321565b8152602001615211615321565b815260200161521e615321565b815260200161522b615321565b8152602001615238615321565b815260200161524561540c565b81526020015f8152602001615258615439565b81526020015f815260200161526b615467565b8152602001615278615486565b815260200161528561536b565b81526020016151906154b4565b6040518061052001604052806029906020820280368337509192915050565b604051806101c001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020016152ef6154b4565b81526020016152fc6154b4565b81526020015f81526020015f81526020015f81526020015f8152602001606081525090565b60405180604001604052805f81526020015f81525090565b604051806040016040528061519d615321565b6040518061012001604052806009906020820280368337509192915050565b604051806103800160405280601c906020820280368337509192915050565b6040518060e0016040528061539d6154d2565b81526020015f81526020015f81526020015f81526020015f81526020016153c26154d2565b81526020015f81525090565b6040518061010001604052806008906020820280368337509192915050565b6040518061020001604052806010906020820280368337509192915050565b60405180606001604052806003905b615423615321565b81526020019060019003908161541b5790505090565b604051806103800160405280601c905b61545161534c565b8152602001906001900390816154495790505090565b604051806105400160405280602a906020820280368337509192915050565b604051806103600160405280601b905b61549e615321565b8152602001906001900390816154965790505090565b60405180608001604052806004906020820280368337509192915050565b604051806120000160405280610100906020820280368337509192915050565b5f5f5f5f60408587031215615505575f5ffd5b84356001600160401b0381111561551a575f5ffd5b8501601f8101871361552a575f5ffd5b80356001600160401b0381111561553f575f5ffd5b876020828401011115615550575f5ffd5b6020918201955093508501356001600160401b0381111561556f575f5ffd5b8501601f8101871361557f575f5ffd5b80356001600160401b03811115615594575f5ffd5b8760208260051b84010111156155a8575f5ffd5b949793965060200194505050565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417612611576126116155b6565b81810381811115612611576126116155b6565b805f5b60108110156131e25781518452602093840193909101906001016155f7565b805f5b60038110156131e25761563784835180518252602090810151910152565b6040939093019260209190910190600101615619565b805f5b601c8110156131e2578151845f5b600981101561567d57825182526020928301929091019060010161565e565b505050610120939093019260209190910190600101615650565b805f5b602a8110156131e257815184526020938401939091019060010161569a565b805f5b601b8110156131e2576156da84835180518252602090810151910152565b60409390930192602091909101906001016156bc565b805f5b601c8110156131e25781518452602093840193909101906001016156f3565b805f5b60048110156131e2578151845260209384019390910190600101615715565b8183525f6001600160fb1b0383111561574b575f5ffd5b8260051b80836020870137939093016020019392505050565b61576f8188516155f4565b5f602088015161578d61020084018280518252602090810151910152565b5060408801518051610240840152602090810151610260840152606089015180516102808501528101516102a0840152608089015180516102c08501528101516102e084015260a0890151805161030085015281015161032084015260c0890151805161034085015281015161036084015260e089015180516103808501528101516103a084015261010089015180516103c08501528101516103e084015261012089015180516104008501520151610420830152610140880151615856610440840182615616565b5061016088015161050083015261018088015161587761052084018261564d565b506101a08801516124a08301526101c08801516158986124c0840182615697565b506101e08801516158ad612a008401826156b9565b506102008801516158c26130c08401826156f0565b506102208801516158d7613440840182615712565b5061024088015180516134c08401526020908101516134e0840152610260890151805161350085015201516135208301526135c061354083018190526159209083018789615734565b613560830195909552506135808101929092526135a0909101529392505050565b634e487b7160e01b5f52604160045260245ffd5b60405161014081016001600160401b038111828210171561597857615978615941565b60405290565b604051601f8201601f191681016001600160401b03811182821017156159a6576159a6615941565b604052919050565b5f60c082840312156159be575f5ffd5b60405160c081016001600160401b03811182821017156159e0576159e0615941565b604090815283518252602080850151908301528381015190820152606080840151908201526080808401519082015260a0928301519281019290925250919050565b5f82601f830112615a31575f5ffd5b5f610360615a3e8161597e565b915083018185821115615a4f575f5ffd5b845b82811015615a69578051825260209182019101615a51565b509195945050505050565b5f82601f830112615a83575f5ffd5b5f610380615a3e8161597e565b5f610be0828403128015615aa2575f5ffd5b50615aab615955565b615ab584846159ae565b8152615ac48460c08501615a22565b6020820152615ad7846104208501615a74565b60408201526107a08301516060820152615af5846107c08501615a74565b6080820152610b4083015160a0820152610b6083015160c0820152610b8083015160e0820152610ba0830151610100820152610bc09092015161012083015250919050565b80820180821115612611576126116155b6565b5f5f85851115615b5b575f5ffd5b83861115615b67575f5ffd5b5050820193919092039150565b634e487b7160e01b5f52603260045260245ffd5b5f60018201615b9957615b996155b6565b5060010190565b80356020831015612611575f19602084900360031b1b1692915050565b5f82615bd757634e487b7160e01b5f52601260045260245ffd5b500690565b5f81615bea57615bea6155b6565b505f190190565b5f8183825b6008811015615c15578151835260209283019290910190600101615bf6565b5050506101008201905092915050565b5f82518060208501845e5f920191825250919050565b5f60208284031215615c4b575f5ffd5b815180151581146103f4575f5ffd5b634e487b7160e01b5f52602160045260245ffdfe30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4730644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", + "linkReferences": { + "project/contracts/verifier/ThresholdDecryptedSharesAggregationBnVerifier.sol": { + "ZKTranscriptLib": [ + { + "length": 20, + "start": 690 + } + ] + } + }, + "deployedLinkReferences": { + "project/contracts/verifier/ThresholdDecryptedSharesAggregationBnVerifier.sol": { + "ZKTranscriptLib": [ + { + "length": 20, + "start": 360 + } + ] + } + }, + "immutableReferences": { + "45189": [ + { + "length": 32, + "start": 91 + }, + { + "length": 32, + "start": 148 + }, + { + "length": 32, + "start": 257 + }, + { + "length": 32, + "start": 466 + }, + { + "length": 32, + "start": 2605 + }, + { + "length": 32, + "start": 2958 + }, + { + "length": 32, + "start": 3115 + }, + { + "length": 32, + "start": 5411 + }, + { + "length": 32, + "start": 5597 + }, + { + "length": 32, + "start": 5648 + }, + { + "length": 32, + "start": 6170 + }, + { + "length": 32, + "start": 11168 + } + ], + "45191": [ + { + "length": 32, + "start": 398 + } + ], + "45193": [ + { + "length": 32, + "start": 432 + }, + { + "length": 32, + "start": 2303 + } + ], + "45195": [ + { + "length": 32, + "start": 3156 + }, + { + "length": 32, + "start": 3257 + }, + { + "length": 32, + "start": 11971 + } + ] + }, + "inputSourceName": "project/contracts/verifier/ThresholdDecryptedSharesAggregationBnVerifier.sol", + "buildInfoId": "solc-0_8_28-bd46eae6f77be9a8538850385226c1ee4fc57e42" +} \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/verifier/ThresholdDecryptedSharesAggregationBnVerifier.sol/ZKTranscriptLib.json b/packages/enclave-contracts/artifacts/contracts/verifier/ThresholdDecryptedSharesAggregationBnVerifier.sol/ZKTranscriptLib.json new file mode 100644 index 0000000000..07540b1e68 --- /dev/null +++ b/packages/enclave-contracts/artifacts/contracts/verifier/ThresholdDecryptedSharesAggregationBnVerifier.sol/ZKTranscriptLib.json @@ -0,0 +1,395 @@ +{ + "_format": "hh3-artifact-1", + "contractName": "ZKTranscriptLib", + "sourceName": "contracts/verifier/ThresholdDecryptedSharesAggregationBnVerifier.sol", + "abi": [ + { + "inputs": [ + { + "components": [ + { + "internalType": "Fr[16]", + "name": "pairingPointObject", + "type": "uint256[16]" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "geminiMaskingPoly", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "w1", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "w2", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "w3", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "w4", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "lookupReadCounts", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "lookupReadTags", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "lookupInverses", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "zPerm", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point[3]", + "name": "libraCommitments", + "type": "tuple[3]" + }, + { + "internalType": "Fr", + "name": "libraSum", + "type": "uint256" + }, + { + "internalType": "Fr[9][28]", + "name": "sumcheckUnivariates", + "type": "uint256[9][28]" + }, + { + "internalType": "Fr", + "name": "libraEvaluation", + "type": "uint256" + }, + { + "internalType": "Fr[42]", + "name": "sumcheckEvaluations", + "type": "uint256[42]" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point[27]", + "name": "geminiFoldComms", + "type": "tuple[27]" + }, + { + "internalType": "Fr[28]", + "name": "geminiAEvaluations", + "type": "uint256[28]" + }, + { + "internalType": "Fr[4]", + "name": "libraPolyEvals", + "type": "uint256[4]" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "shplonkQ", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "kzgQuotient", + "type": "tuple" + } + ], + "internalType": "struct Honk.ZKProof", + "name": "proof", + "type": "tuple" + }, + { + "internalType": "bytes32[]", + "name": "publicInputs", + "type": "bytes32[]" + }, + { + "internalType": "uint256", + "name": "vkHash", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "publicInputsSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "logN", + "type": "uint256" + } + ], + "name": "generateTranscript", + "outputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "Fr", + "name": "eta", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "etaTwo", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "etaThree", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "beta", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "gamma", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "publicInputsDelta", + "type": "uint256" + } + ], + "internalType": "struct Honk.RelationParameters", + "name": "relationParameters", + "type": "tuple" + }, + { + "internalType": "Fr[27]", + "name": "alphas", + "type": "uint256[27]" + }, + { + "internalType": "Fr[28]", + "name": "gateChallenges", + "type": "uint256[28]" + }, + { + "internalType": "Fr", + "name": "libraChallenge", + "type": "uint256" + }, + { + "internalType": "Fr[28]", + "name": "sumCheckUChallenges", + "type": "uint256[28]" + }, + { + "internalType": "Fr", + "name": "rho", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "geminiR", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "shplonkNu", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "shplonkZ", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "publicInputsDelta", + "type": "uint256" + } + ], + "internalType": "struct ZKTranscript", + "name": "t", + "type": "tuple" + } + ], + "stateMutability": "pure", + "type": "function" + } + ], + "bytecode": "0x6116f3610034600b8282823980515f1a607314602857634e487b7160e01b5f525f60045260245ffd5b305f52607381538281f3fe7300000000000000000000000000000000000000003014608060405260043610610034575f3560e01c8063995bf45714610038575b5f5ffd5b61004b610046366004611214565b610061565b604051610058919061144a565b60405180910390f35b610069610d76565b5f610078888888888886610128565b9083529050610087818961017c565b6020840191909152905061009b818461026c565b604084019190915290506100af8189610315565b606084019190915290506100c4888285610376565b608084019190915290506100d88882610445565b60a084019190915290506100ed8882856105cb565b60c0840191909152905061010288828561073d565b60e0840191909152905061011688826108bf565b50610100830152509695505050505050565b610130610ddb565b5f61013e88888888886108fc565b60408601919091526020850191909152908352925061015d8389610c6a565b6080850191909152606084019190915291989197509095505050505050565b610184610e0b565b5f61018d610e2a565b8481526101008401805151602080840191909152905181015160408084019190915261012086018051516060850152518201516080840152516101f1916101d69184910161151f565b60405160208183030381529060405280519060200120610cf2565b91505f6101fd83610d18565b50808552905060015b6102126001601c611566565b8110156102625761024385610228600184611566565b601b81106102385761023861150b565b602002015183610d48565b8582601b81106102555761025561150b565b6020020152600101610206565b5050509250929050565b610274610e48565b5f61028b846040516020016101d691815260200190565b935061029684610d18565b50825260015b8381101561030d576102ee836102b3600184611566565b601c81106102c3576102c361150b565b6020020151846102d4600185611566565b601c81106102e4576102e461150b565b6020020151610d48565b8382601c81106103005761030061150b565b602002015260010161029c565b509093915050565b5f5f61031f610e67565b84815261014084018051515160208084019190915290515181015160408084019190915261016086015160608401525161035f916101d691849101611579565b915061036a82610d18565b50959194509092505050565b61037e610e48565b5f805b8381101561043b57610391610e85565b8581525f5b60098110156103fa5787610180015183601c81106103b6576103b661150b565b602002015181600981106103cc576103cc61150b565b6020020151826103dd8360016115ac565b600a81106103ed576103ed61150b565b6020020152600101610396565b5061040f816040516020016101d691906115bf565b955061041a86610d18565b508483601c811061042d5761042d61150b565b602002015250600101610381565b5090949293505050565b5f5f61044f610ea4565b83815260015b610461600160296115ac565b81116104b6576101c0860151610478600183611566565b602a81106104885761048861150b565b602002015182826030811061049f5761049f61150b565b6020020152806104ae816115f3565b915050610455565b856101a001518282603081106104ce576104ce61150b565b60200201526104de6001826115ac565b61014087015160200151519091508282603081106104fe576104fe61150b565b602002015261014086015160016020020151602001518282600161052291906115ac565b603081106105325761053261150b565b60200201526105426002826115ac565b61014087015160400151519091508282603081106105625761056261150b565b602002015261014086015160026020020151602001518282600161058691906115ac565b603081106105965761059661150b565b6020020181815250506105b3826040516020016101d6919061160b565b92506105be83610d18565b5096929550919350505050565b5f80806105d9600185611566565b6105e490600261163f565b6105ef9060016115ac565b6001600160401b0381111561060657610606610eff565b60405190808252806020026020018201604052801561062f578160200160208202803683370190505b50905084815f815181106106455761064561150b565b60209081029190910101525f5b61065d600186611566565b81101561071057866101e0015181601b811061067b5761067b61150b565b6020020151518261068d83600261163f565b6106989060016115ac565b815181106106a8576106a861150b565b602002602001018181525050866101e0015181601b81106106cb576106cb61150b565b602002015160200151828260026106e2919061163f565b6106ed9060026115ac565b815181106106fd576106fd61150b565b6020908102919091010152600101610652565b50610725816040516020016101d69190611656565b915061073082610d18565b5096919550909350505050565b5f808061074b8460016115ac565b6107569060046115ac565b6001600160401b0381111561076d5761076d610eff565b604051908082528060200260200182016040528015610796578160200160208202803683370190505b50905084815f815181106107ac576107ac61150b565b602090810291909101015260015b848111610816576102008701516107d2600183611566565b601c81106107e2576107e261150b565b60200201518282815181106107f9576107f961150b565b60209081029190910101528061080e816115f3565b9150506107ba565b505f806108248660016115ac565b90505b6108328660046115ac565b811161089157876102200151826004811061084f5761084f61150b565b60200201518382815181106108665761086661150b565b60209081029190910101528161087b816115f3565b9250508080610889906115f3565b915050610827565b506108a6826040516020016101d69190611656565b92506108b183610d18565b509792965091945050505050565b5f5f6108c9610ec3565b838152610240850180515160208084019190915290518101516040808401919091525161035f916101d691849101611680565b5f8080808061090c8660016115ac565b6109179060086115ac565b6001600160401b0381111561092e5761092e610eff565b604051908082528060200260200182016040528015610957578160200160208202803683370190505b509050865f1b815f8151811061096f5761096f61150b565b60209081029190910101525f5b610987601088611566565b8110156109d8578989828181106109a0576109a061150b565b90506020020135828260016109b591906115ac565b815181106109c5576109c561150b565b602090810291909101015260010161097c565b505f5b6010811015610a49578a51610a039082601081106109fb576109fb61150b565b602002015190565b82826010610a128b60016115ac565b610a1c9190611566565b610a2691906115ac565b81518110610a3657610a3661150b565b60209081029190910101526001016109db565b5060208a01515181610a5c8860016115ac565b81518110610a6c57610a6c61150b565b6020908102919091018101919091528a810151015181610a8d8860016115ac565b610a989060016115ac565b81518110610aa857610aa861150b565b602090810291909101015260408a01515181610ac58860016115ac565b610ad09060026115ac565b81518110610ae057610ae061150b565b60209081029190910181019190915260408b0151015181610b028860016115ac565b610b0d9060036115ac565b81518110610b1d57610b1d61150b565b602090810291909101015260608a01515181610b3a8860016115ac565b610b459060046115ac565b81518110610b5557610b5561150b565b60209081029190910181019190915260608b0151015181610b778860016115ac565b610b829060056115ac565b81518110610b9257610b9261150b565b602090810291909101015260808a01515181610baf8860016115ac565b610bba9060066115ac565b81518110610bca57610bca61150b565b60209081029190910181019190915260808b0151015181610bec8860016115ac565b610bf79060076115ac565b81518110610c0757610c0761150b565b602002602001018181525050610c27816040516020016101d69190611656565b9150610c3282610d18565b6040805160208101869052929750909550610c4d91016101d6565b9150610c5882610d18565b50809350505095509550955095915050565b5f5f5f610c75610ee1565b85815260c0858101805151602080850191909152905181015160408085019190915260e08801805151606086015251820151608085015260a08089018051519186019190915251820151928401929092529051610cd8916101d6918491016116b3565b9150610ce382610d18565b90979096509194509092505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001900690565b5f808260016001607f1b038116607f82901c610d3382610cf2565b9450610d3e81610cf2565b9350505050915091565b5f7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182840990505b92915050565b604051806101400160405280610d8a610ddb565b8152602001610d97610e0b565b8152602001610da4610e48565b81526020015f8152602001610db7610e48565b81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b6040518060c001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b604051806103600160405280601b906020820280368337509192915050565b6040518060a001604052806005906020820280368337509192915050565b604051806103800160405280601c906020820280368337509192915050565b60405180608001604052806004906020820280368337509192915050565b604051806101400160405280600a906020820280368337509192915050565b6040518061060001604052806030906020820280368337509192915050565b60405180606001604052806003906020820280368337509192915050565b6040518060e001604052806007906020820280368337509192915050565b634e487b7160e01b5f52604160045260245ffd5b60405161028081016001600160401b0381118282101715610f3657610f36610eff565b60405290565b604051601f8201601f191681016001600160401b0381118282101715610f6457610f64610eff565b604052919050565b5f82601f830112610f7b575f5ffd5b5f610200610f8881610f3c565b915083018185821115610f99575f5ffd5b845b82811015610fb3578035825260209182019101610f9b565b509195945050505050565b5f60408284031215610fce575f5ffd5b604080519081016001600160401b0381118282101715610ff057610ff0610eff565b604052823581526020928301359281019290925250919050565b5f82601f830112611019575f5ffd5b5f6110246060610f3c565b90508060c0840185811115611037575f5ffd5b845b81811015610fb35761104b8782610fbe565b8352602090920191604001611039565b5f82601f83011261106a575f5ffd5b61038061107681610f3c565b905080611f8084018581111561108a575f5ffd5b845b818110156110ee5786601f8201126110a2575f5ffd5b5f6101206110af81610f3c565b9150820181898211156110c0575f5ffd5b835b828110156110da5780358252602091820191016110c2565b50505084526020909301926101200161108c565b509095945050505050565b5f82601f830112611108575f5ffd5b5f610540610f8881610f3c565b5f82601f830112611124575f5ffd5b5f61036061113181610f3c565b915050806106c0840185811115611146575f5ffd5b845b81811015610fb35761115a8782610fbe565b8352602090920191604001611148565b5f82601f830112611179575f5ffd5b5f610380610f8881610f3c565b5f82601f830112611195575f5ffd5b5f6111a06080610f3c565b90508060808401858111156111b3575f5ffd5b845b81811015610fb35780358352602092830192016111b5565b5f5f83601f8401126111dd575f5ffd5b5081356001600160401b038111156111f3575f5ffd5b6020830191508360208260051b850101111561120d575f5ffd5b9250929050565b5f5f5f5f5f5f8688036135c081121561122b575f5ffd5b613540811215611239575f5ffd5b50611242610f13565b61124c8989610f6c565b815261125c896102008a01610fbe565b602082015261126f896102408a01610fbe565b6040820152611282896102808a01610fbe565b6060820152611295896102c08a01610fbe565b60808201526112a8896103008a01610fbe565b60a08201526112bb896103408a01610fbe565b60c08201526112ce896103808a01610fbe565b60e08201526112e1896103c08a01610fbe565b6101008201526112f5896104008a01610fbe565b610120820152611309896104408a0161100a565b610140820152610500880135610160820152611329896105208a0161105b565b6101808201526124a08801356101a0820152611349896124c08a016110f9565b6101c082015261135d89612a008a01611115565b6101e0820152611371896130c08a0161116a565b610200820152611385896134408a01611186565b610220820152611399896134c08a01610fbe565b6102408201526113ad896135008a01610fbe565b61026082015295506135408701356001600160401b038111156113ce575f5ffd5b6113da89828a016111cd565b979a90995096976135608101359761358082013597506135a09091013595509350505050565b805f5b601b811015611422578151845260209384019390910190600101611403565b50505050565b805f5b601c81101561142257815184526020938401939091019060010161142b565b8151805182526020808201519083015260408082015190830152606080820151908301526080808201519083015260a09081015190820152610be08101602083015161149960c0840182611400565b5060408301516114ad610420840182611428565b5060608301516107a083015260808301516114cc6107c0840182611428565b5060a0830151610b4083015260c0830151610b6083015260e0830151610b80830152610100830151610ba083015261012090920151610bc09091015290565b634e487b7160e01b5f52603260045260245ffd5b5f8183825b6005811015611543578151835260209283019290910190600101611524565b50505060a08201905092915050565b634e487b7160e01b5f52601160045260245ffd5b81810381811115610d7057610d70611552565b5f8183825b600481101561159d57815183526020928301929091019060010161157e565b50505060808201905092915050565b80820180821115610d7057610d70611552565b5f8183825b600a8110156115e35781518352602092830192909101906001016115c4565b5050506101408201905092915050565b5f6001820161160457611604611552565b5060010190565b5f8183825b603081101561162f578151835260209283019290910190600101611610565b5050506106008201905092915050565b8082028115828204841417610d7057610d70611552565b81515f90829060208501835b82811015610fb3578151845260209384019390910190600101611662565b5f8183825b60038110156116a4578151835260209283019290910190600101611685565b50505060608201905092915050565b5f8183825b60078110156116d75781518352602092830192909101906001016116b8565b50505060e0820190509291505056fea164736f6c634300081c000a", + "deployedBytecode": "0x7300000000000000000000000000000000000000003014608060405260043610610034575f3560e01c8063995bf45714610038575b5f5ffd5b61004b610046366004611214565b610061565b604051610058919061144a565b60405180910390f35b610069610d76565b5f610078888888888886610128565b9083529050610087818961017c565b6020840191909152905061009b818461026c565b604084019190915290506100af8189610315565b606084019190915290506100c4888285610376565b608084019190915290506100d88882610445565b60a084019190915290506100ed8882856105cb565b60c0840191909152905061010288828561073d565b60e0840191909152905061011688826108bf565b50610100830152509695505050505050565b610130610ddb565b5f61013e88888888886108fc565b60408601919091526020850191909152908352925061015d8389610c6a565b6080850191909152606084019190915291989197509095505050505050565b610184610e0b565b5f61018d610e2a565b8481526101008401805151602080840191909152905181015160408084019190915261012086018051516060850152518201516080840152516101f1916101d69184910161151f565b60405160208183030381529060405280519060200120610cf2565b91505f6101fd83610d18565b50808552905060015b6102126001601c611566565b8110156102625761024385610228600184611566565b601b81106102385761023861150b565b602002015183610d48565b8582601b81106102555761025561150b565b6020020152600101610206565b5050509250929050565b610274610e48565b5f61028b846040516020016101d691815260200190565b935061029684610d18565b50825260015b8381101561030d576102ee836102b3600184611566565b601c81106102c3576102c361150b565b6020020151846102d4600185611566565b601c81106102e4576102e461150b565b6020020151610d48565b8382601c81106103005761030061150b565b602002015260010161029c565b509093915050565b5f5f61031f610e67565b84815261014084018051515160208084019190915290515181015160408084019190915261016086015160608401525161035f916101d691849101611579565b915061036a82610d18565b50959194509092505050565b61037e610e48565b5f805b8381101561043b57610391610e85565b8581525f5b60098110156103fa5787610180015183601c81106103b6576103b661150b565b602002015181600981106103cc576103cc61150b565b6020020151826103dd8360016115ac565b600a81106103ed576103ed61150b565b6020020152600101610396565b5061040f816040516020016101d691906115bf565b955061041a86610d18565b508483601c811061042d5761042d61150b565b602002015250600101610381565b5090949293505050565b5f5f61044f610ea4565b83815260015b610461600160296115ac565b81116104b6576101c0860151610478600183611566565b602a81106104885761048861150b565b602002015182826030811061049f5761049f61150b565b6020020152806104ae816115f3565b915050610455565b856101a001518282603081106104ce576104ce61150b565b60200201526104de6001826115ac565b61014087015160200151519091508282603081106104fe576104fe61150b565b602002015261014086015160016020020151602001518282600161052291906115ac565b603081106105325761053261150b565b60200201526105426002826115ac565b61014087015160400151519091508282603081106105625761056261150b565b602002015261014086015160026020020151602001518282600161058691906115ac565b603081106105965761059661150b565b6020020181815250506105b3826040516020016101d6919061160b565b92506105be83610d18565b5096929550919350505050565b5f80806105d9600185611566565b6105e490600261163f565b6105ef9060016115ac565b6001600160401b0381111561060657610606610eff565b60405190808252806020026020018201604052801561062f578160200160208202803683370190505b50905084815f815181106106455761064561150b565b60209081029190910101525f5b61065d600186611566565b81101561071057866101e0015181601b811061067b5761067b61150b565b6020020151518261068d83600261163f565b6106989060016115ac565b815181106106a8576106a861150b565b602002602001018181525050866101e0015181601b81106106cb576106cb61150b565b602002015160200151828260026106e2919061163f565b6106ed9060026115ac565b815181106106fd576106fd61150b565b6020908102919091010152600101610652565b50610725816040516020016101d69190611656565b915061073082610d18565b5096919550909350505050565b5f808061074b8460016115ac565b6107569060046115ac565b6001600160401b0381111561076d5761076d610eff565b604051908082528060200260200182016040528015610796578160200160208202803683370190505b50905084815f815181106107ac576107ac61150b565b602090810291909101015260015b848111610816576102008701516107d2600183611566565b601c81106107e2576107e261150b565b60200201518282815181106107f9576107f961150b565b60209081029190910101528061080e816115f3565b9150506107ba565b505f806108248660016115ac565b90505b6108328660046115ac565b811161089157876102200151826004811061084f5761084f61150b565b60200201518382815181106108665761086661150b565b60209081029190910101528161087b816115f3565b9250508080610889906115f3565b915050610827565b506108a6826040516020016101d69190611656565b92506108b183610d18565b509792965091945050505050565b5f5f6108c9610ec3565b838152610240850180515160208084019190915290518101516040808401919091525161035f916101d691849101611680565b5f8080808061090c8660016115ac565b6109179060086115ac565b6001600160401b0381111561092e5761092e610eff565b604051908082528060200260200182016040528015610957578160200160208202803683370190505b509050865f1b815f8151811061096f5761096f61150b565b60209081029190910101525f5b610987601088611566565b8110156109d8578989828181106109a0576109a061150b565b90506020020135828260016109b591906115ac565b815181106109c5576109c561150b565b602090810291909101015260010161097c565b505f5b6010811015610a49578a51610a039082601081106109fb576109fb61150b565b602002015190565b82826010610a128b60016115ac565b610a1c9190611566565b610a2691906115ac565b81518110610a3657610a3661150b565b60209081029190910101526001016109db565b5060208a01515181610a5c8860016115ac565b81518110610a6c57610a6c61150b565b6020908102919091018101919091528a810151015181610a8d8860016115ac565b610a989060016115ac565b81518110610aa857610aa861150b565b602090810291909101015260408a01515181610ac58860016115ac565b610ad09060026115ac565b81518110610ae057610ae061150b565b60209081029190910181019190915260408b0151015181610b028860016115ac565b610b0d9060036115ac565b81518110610b1d57610b1d61150b565b602090810291909101015260608a01515181610b3a8860016115ac565b610b459060046115ac565b81518110610b5557610b5561150b565b60209081029190910181019190915260608b0151015181610b778860016115ac565b610b829060056115ac565b81518110610b9257610b9261150b565b602090810291909101015260808a01515181610baf8860016115ac565b610bba9060066115ac565b81518110610bca57610bca61150b565b60209081029190910181019190915260808b0151015181610bec8860016115ac565b610bf79060076115ac565b81518110610c0757610c0761150b565b602002602001018181525050610c27816040516020016101d69190611656565b9150610c3282610d18565b6040805160208101869052929750909550610c4d91016101d6565b9150610c5882610d18565b50809350505095509550955095915050565b5f5f5f610c75610ee1565b85815260c0858101805151602080850191909152905181015160408085019190915260e08801805151606086015251820151608085015260a08089018051519186019190915251820151928401929092529051610cd8916101d6918491016116b3565b9150610ce382610d18565b90979096509194509092505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001900690565b5f808260016001607f1b038116607f82901c610d3382610cf2565b9450610d3e81610cf2565b9350505050915091565b5f7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182840990505b92915050565b604051806101400160405280610d8a610ddb565b8152602001610d97610e0b565b8152602001610da4610e48565b81526020015f8152602001610db7610e48565b81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b6040518060c001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b604051806103600160405280601b906020820280368337509192915050565b6040518060a001604052806005906020820280368337509192915050565b604051806103800160405280601c906020820280368337509192915050565b60405180608001604052806004906020820280368337509192915050565b604051806101400160405280600a906020820280368337509192915050565b6040518061060001604052806030906020820280368337509192915050565b60405180606001604052806003906020820280368337509192915050565b6040518060e001604052806007906020820280368337509192915050565b634e487b7160e01b5f52604160045260245ffd5b60405161028081016001600160401b0381118282101715610f3657610f36610eff565b60405290565b604051601f8201601f191681016001600160401b0381118282101715610f6457610f64610eff565b604052919050565b5f82601f830112610f7b575f5ffd5b5f610200610f8881610f3c565b915083018185821115610f99575f5ffd5b845b82811015610fb3578035825260209182019101610f9b565b509195945050505050565b5f60408284031215610fce575f5ffd5b604080519081016001600160401b0381118282101715610ff057610ff0610eff565b604052823581526020928301359281019290925250919050565b5f82601f830112611019575f5ffd5b5f6110246060610f3c565b90508060c0840185811115611037575f5ffd5b845b81811015610fb35761104b8782610fbe565b8352602090920191604001611039565b5f82601f83011261106a575f5ffd5b61038061107681610f3c565b905080611f8084018581111561108a575f5ffd5b845b818110156110ee5786601f8201126110a2575f5ffd5b5f6101206110af81610f3c565b9150820181898211156110c0575f5ffd5b835b828110156110da5780358252602091820191016110c2565b50505084526020909301926101200161108c565b509095945050505050565b5f82601f830112611108575f5ffd5b5f610540610f8881610f3c565b5f82601f830112611124575f5ffd5b5f61036061113181610f3c565b915050806106c0840185811115611146575f5ffd5b845b81811015610fb35761115a8782610fbe565b8352602090920191604001611148565b5f82601f830112611179575f5ffd5b5f610380610f8881610f3c565b5f82601f830112611195575f5ffd5b5f6111a06080610f3c565b90508060808401858111156111b3575f5ffd5b845b81811015610fb35780358352602092830192016111b5565b5f5f83601f8401126111dd575f5ffd5b5081356001600160401b038111156111f3575f5ffd5b6020830191508360208260051b850101111561120d575f5ffd5b9250929050565b5f5f5f5f5f5f8688036135c081121561122b575f5ffd5b613540811215611239575f5ffd5b50611242610f13565b61124c8989610f6c565b815261125c896102008a01610fbe565b602082015261126f896102408a01610fbe565b6040820152611282896102808a01610fbe565b6060820152611295896102c08a01610fbe565b60808201526112a8896103008a01610fbe565b60a08201526112bb896103408a01610fbe565b60c08201526112ce896103808a01610fbe565b60e08201526112e1896103c08a01610fbe565b6101008201526112f5896104008a01610fbe565b610120820152611309896104408a0161100a565b610140820152610500880135610160820152611329896105208a0161105b565b6101808201526124a08801356101a0820152611349896124c08a016110f9565b6101c082015261135d89612a008a01611115565b6101e0820152611371896130c08a0161116a565b610200820152611385896134408a01611186565b610220820152611399896134c08a01610fbe565b6102408201526113ad896135008a01610fbe565b61026082015295506135408701356001600160401b038111156113ce575f5ffd5b6113da89828a016111cd565b979a90995096976135608101359761358082013597506135a09091013595509350505050565b805f5b601b811015611422578151845260209384019390910190600101611403565b50505050565b805f5b601c81101561142257815184526020938401939091019060010161142b565b8151805182526020808201519083015260408082015190830152606080820151908301526080808201519083015260a09081015190820152610be08101602083015161149960c0840182611400565b5060408301516114ad610420840182611428565b5060608301516107a083015260808301516114cc6107c0840182611428565b5060a0830151610b4083015260c0830151610b6083015260e0830151610b80830152610100830151610ba083015261012090920151610bc09091015290565b634e487b7160e01b5f52603260045260245ffd5b5f8183825b6005811015611543578151835260209283019290910190600101611524565b50505060a08201905092915050565b634e487b7160e01b5f52601160045260245ffd5b81810381811115610d7057610d70611552565b5f8183825b600481101561159d57815183526020928301929091019060010161157e565b50505060808201905092915050565b80820180821115610d7057610d70611552565b5f8183825b600a8110156115e35781518352602092830192909101906001016115c4565b5050506101408201905092915050565b5f6001820161160457611604611552565b5060010190565b5f8183825b603081101561162f578151835260209283019290910190600101611610565b5050506106008201905092915050565b8082028115828204841417610d7057610d70611552565b81515f90829060208501835b82811015610fb3578151845260209384019390910190600101611662565b5f8183825b60038110156116a4578151835260209283019290910190600101611685565b50505060608201905092915050565b5f8183825b60078110156116d75781518352602092830192909101906001016116b8565b50505060e0820190509291505056fea164736f6c634300081c000a", + "linkReferences": {}, + "deployedLinkReferences": {}, + "immutableReferences": {}, + "inputSourceName": "project/contracts/verifier/ThresholdDecryptedSharesAggregationBnVerifier.sol", + "buildInfoId": "solc-0_8_28-bd46eae6f77be9a8538850385226c1ee4fc57e42" +} \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/verifier/ThresholdDecryptedSharesAggregationModVerifier.sol/ThresholdDecryptedSharesAggregationModVerifier.json b/packages/enclave-contracts/artifacts/contracts/verifier/ThresholdDecryptedSharesAggregationModVerifier.sol/ThresholdDecryptedSharesAggregationModVerifier.json new file mode 100644 index 0000000000..31f8f7e917 --- /dev/null +++ b/packages/enclave-contracts/artifacts/contracts/verifier/ThresholdDecryptedSharesAggregationModVerifier.sol/ThresholdDecryptedSharesAggregationModVerifier.json @@ -0,0 +1,188 @@ +{ + "_format": "hh3-artifact-1", + "contractName": "ThresholdDecryptedSharesAggregationModVerifier", + "sourceName": "contracts/verifier/ThresholdDecryptedSharesAggregationModVerifier.sol", + "abi": [ + { + "inputs": [], + "name": "ConsistencyCheckFailed", + "type": "error" + }, + { + "inputs": [], + "name": "GeminiChallengeInSubgroup", + "type": "error" + }, + { + "inputs": [], + "name": "ProofLengthWrong", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "logN", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "actualLength", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expectedLength", + "type": "uint256" + } + ], + "name": "ProofLengthWrongWithLogN", + "type": "error" + }, + { + "inputs": [], + "name": "PublicInputsLengthWrong", + "type": "error" + }, + { + "inputs": [], + "name": "ShpleminiFailed", + "type": "error" + }, + { + "inputs": [], + "name": "SumcheckFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "proof", + "type": "bytes" + }, + { + "internalType": "bytes32[]", + "name": "publicInputs", + "type": "bytes32[]" + } + ], + "name": "verify", + "outputs": [ + { + "internalType": "bool", + "name": "verified", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x610120604052348015610010575f5ffd5b50620200006080819052601160a08190527f0772b20ab0892c5f70111dd5e3e9737a04e02129d3f48fbd7a1079f68d41c3de60c081905261024360e081905260038361005e6001602461008b565b610068919061008b565b610072919061008b565b61007d90600261008b565b61010052506100b092505050565b808201808211156100aa57634e487b7160e01b5f52601160045260245ffd5b92915050565b60805160a05160c05160e05161010051615cba61014a5f395f8181610c5401528181610cb90152612ec201525f81816101b001526108ff01525f61018e01525f8181605b01528181609401528181610101015281816101d201528181610a2d01528181610b8e01528181610c2b01528181611523015281816115dd015281816116100152818161181a0152612b9f01525f5050615cba5ff3fe608060405234801561000f575f5ffd5b5060043610610029575f3560e01c8063ea50d0e41461002d575b5f5ffd5b61004061003b3660046154f1565b610054565b604051901515815260200160405180910390f35b5f5f61007f7f00000000000000000000000000000000000000000000000000000000000000006102ee565b905061008c8160206155c9565b85146100ee577f0000000000000000000000000000000000000000000000000000000000000000856100bf8360206155c9565b6040516359895a5360e01b81526004810193909352602483019190915260448201526064015b60405180910390fd5b5f6100f76103fb565b90505f61012588887f0000000000000000000000000000000000000000000000000000000000000000610410565b90506010826040015161013891906155e0565b85146101575760405163fa06659360e01b815260040160405180910390fd5b60405163995bf45760e01b81525f9073__$2b3697cbc38bc9b2f889ee4ee8a0f41368$__9063995bf457906101fa9085908b908b907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090600401615763565b610be060405180830381865af4158015610216573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061023a9190615a8f565b905061028a8787808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050855185516060810151608090910151919350915060016108a9565b815160a0015261029a8282610a10565b6102b7576040516313f8744360e31b815260040160405180910390fd5b6102c2828483610c17565b6102df576040516352ec174560e11b815260040160405180910390fd5b50600198975050505050505050565b5f8060026102fe60016008615b39565b61030891906155c9565b9050610316600260036155c9565b6103209082615b39565b9050600161032f6009856155c9565b61033991906155c9565b6103439082615b39565b90506001610352816029615b39565b61035c91906155c9565b6103669082615b39565b9050610374600160026155c9565b61037e9082615b39565b905061038b6001846155c9565b6103959082615b39565b90506103a3600160046155c9565b6103ad9082615b39565b905060026103bc6001856155e0565b6103c691906155c9565b6103d09082615b39565b90506103dd6002806155c9565b6103e79082615b39565b90506103f4601082615b39565b9392505050565b610403615024565b61040b611c14565b905090565b6104186151ae565b5f805b601081101561047a57610447868387610435602083615b39565b9261044293929190615b4c565b61254e565b8351826010811061045a5761045a615b73565b6020020181815250506020826104709190615b39565b915060010161041b565b5061049e85828661048c604083615b39565b9261049993929190615b4c565b612561565b60208301526104ae604082615b39565b90506104c185828661048c604083615b39565b6040808401919091526104d49082615b39565b90506104e785828661048c604083615b39565b60608301526104f7604082615b39565b905061050a85828661048c604083615b39565b608083015261051a604082615b39565b905061052d85828661048c604083615b39565b60c083015261053d604082615b39565b905061055085828661048c604083615b39565b60e0830152610560604082615b39565b905061057385828661048c604083615b39565b60a0830152610583604082615b39565b905061059685828661048c604083615b39565b6101008301526105a7604082615b39565b90506105ba85828661048c604083615b39565b6101208301526105cb604082615b39565b90506105de85828661048c604083615b39565b610140830151526105f0604082615b39565b9050610603858286610435602083615b39565b610160830152610614602082615b39565b90505f5b83811015610692575f5b60098110156106895761063c878488610435602083615b39565b84610180015183601c811061065357610653615b73565b6020020151826009811061066957610669615b73565b60200201818152505060208361067f9190615b39565b9250600101610622565b50600101610618565b505f5b6106a160016029615b39565b8110156106f0576106b9868387610435602083615b39565b836101c0015182602a81106106d0576106d0615b73565b6020020181815250506020826106e69190615b39565b9150600101610695565b50610702858286610435602083615b39565b6101a0830152610713602082615b39565b905061072685828661048c604083615b39565b6101408301516020015261073b604082615b39565b905061074e85828661048c604083615b39565b61014083015160026020020152610766604082615b39565b90505f5b6107756001856155e0565b8110156107be5761078d86838761048c604083615b39565b836101e0015182601b81106107a4576107a4615b73565b60200201526107b4604083615b39565b915060010161076a565b505f5b83811015610811576107da868387610435602083615b39565b83610200015182601c81106107f1576107f1615b73565b6020020181815250506020826108079190615b39565b91506001016107c1565b505f5b60048110156108655761082e868387610435602083615b39565b836102200151826004811061084557610845615b73565b60200201818152505060208261085b9190615b39565b9150600101610814565b5061087785828661048c604083615b39565b610240830152610888604082615b39565b905061089b85828661048c604083615b39565b610260830152509392505050565b5f600180826108d6866108d1896108cc6108c78a6310000000615b39565b6125e3565b6125fb565b612616565b90505f6108f4876108ef8a6108cc6108c78b6001615b39565b61262f565b90505f5b61092360107f00000000000000000000000000000000000000000000000000000000000000006155e0565b811015610990575f61094d8c838151811061094057610940615b73565b6020026020010151612657565b905061095d866108cc8684612616565b955061096d856108cc8584612616565b9450610979848b612616565b9350610985838b61262f565b9250506001016108f8565b505f5b60108110156109f7575f8a82601081106109af576109af615b73565b602002015190506109c4866108cc8684612616565b95506109d4856108cc8584612616565b94506109e0848b612616565b93506109ec838b61262f565b925050600101610993565b50610a02848461266c565b9a9950505050505050505050565b5f5f610a2583606001518561016001516125fb565b905060015f5b7f0000000000000000000000000000000000000000000000000000000000000000811015610b17575f86610180015182601c8110610a6b57610a6b615b73565b602002015180519091505f90610a89908360015b6020020151612616565b9050848114610aab576040516313f8744360e31b815260040160405180910390fd5b5f876080015184601c8110610ac257610ac2615b73565b60200201519050610ad3838261267a565b9550610b07856108cc60016108d1856108cc8e604001518b601c8110610afb57610afb615b73565b6020020151600161262f565b9450505050806001019050610a2b565b50610b20615291565b5f5b6029811015610b70576101c0870151610b3c600183615b39565b602a8110610b4c57610b4c615b73565b6020020151828260298110610b6357610b63615b73565b6020020152600101610b22565b505f610b8582875f0151886020015186612828565b9050600160025b7f0000000000000000000000000000000000000000000000000000000000000000811015610be257610bd882896080015183601c8110610bce57610bce615b73565b60200201516125fb565b9150600101610b8c565b50610c08610bf5836108cc60018561262f565b6108d18a6101a001518a606001516125fb565b94909414979650505050505050565b5f610c206152b0565b5f610c4f8460c001517f00000000000000000000000000000000000000000000000000000000000000006128a1565b90505f7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03811115610c8a57610c8a615940565b604051908082528060200260200182016040528015610cb3578160200160208202803683370190505b5090505f7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03811115610cef57610cef615940565b604051908082528060200260200182016040528015610d2857816020015b610d15615320565b815260200190600190039081610d0d5790505b509050610d5b610d56876101000151855f81518110610d4957610d49615b73565b602002602001015161262f565b61296a565b84610120018181525050610d90610d56876101000151855f81518110610d8357610d83615b73565b6020026020010151612616565b610140850181905261012085015160e0880151610db1926108d191906125fb565b845260c0860151610de190610dc59061296a565b6108cc8661012001516108ef8a60e001518961014001516125fb565b6020850152815160019083905f90610dfb57610dfb615b73565b602002602001018181525050876102400151815f81518110610e1f57610e1f615b73565b6020908102919091010152600160a08501525f60c08501528351610e42906129da565b60408501526020840151610e55906129da565b606085015260015b610e6960016024615b39565b8111610f0a57610e8185604001518660a001516125fb565b838281518110610e9357610e93615b73565b602002602001018181525050610edc8560c001516108d18b6101c00151600185610ebd91906155e0565b602a8110610ecd57610ecd615b73565b60200201518860a001516125fb565b60c086015260a08086015190880151610ef591906125fb565b60a0860152610f0381615b87565b9050610e5d565b505f5b6005811015610fe3575f610f22601e83615b39565b90505f610f3160016024615b39565b610f3b9084615b39565b9050610f6c858381518110610f5257610f52615b73565b60200260200101516108d189606001518a60a001516125fb565b858381518110610f7e57610f7e615b73565b602002602001018181525050610fbb8760c001516108d18d6101c0015184602a8110610fac57610fac615b73565b60200201518a60a001516125fb565b60c088015260a080880151908a0151610fd491906125fb565b60a08801525050600101610f0d565b50876020015181600181518110610ffc57610ffc615b73565b602002602001018190525086606001518160028151811061101f5761101f615b73565b602002602001018190525086608001518160038151811061104257611042615b73565b60200260200101819052508660a001518160048151811061106557611065615b73565b60200260200101819052508660c001518160058151811061108857611088615b73565b60200260200101819052508660e00151816006815181106110ab576110ab615b73565b6020026020010181905250866101000151816007815181106110cf576110cf615b73565b6020026020010181905250866101200151816008815181106110f3576110f3615b73565b60200260200101819052508661014001518160098151811061111757611117615b73565b602002602001018190525086610160015181600a8151811061113b5761113b615b73565b6020026020010181905250866101c0015181600b8151811061115f5761115f615b73565b602002602001018190525086610180015181600c8151811061118357611183615b73565b6020026020010181905250866101a0015181600d815181106111a7576111a7615b73565b6020026020010181905250866101e0015181600e815181106111cb576111cb615b73565b602002602001018190525086610200015181600f815181106111ef576111ef615b73565b60200260200101819052508661022001518160108151811061121357611213615b73565b60200260200101819052508661024001518160118151811061123757611237615b73565b60200260200101819052508661026001518160128151811061125b5761125b615b73565b60200260200101819052508661028001518160138151811061127f5761127f615b73565b6020026020010181905250866102a00151816014815181106112a3576112a3615b73565b6020026020010181905250866102c00151816015815181106112c7576112c7615b73565b6020026020010181905250866102e00151816016815181106112eb576112eb615b73565b60200260200101819052508661030001518160178151811061130f5761130f615b73565b60200260200101819052508661032001518160188151811061133357611333615b73565b60200260200101819052508661034001518160198151811061135757611357615b73565b602002602001018190525086610360015181601a8151811061137b5761137b615b73565b602002602001018190525086610380015181601b8151811061139f5761139f615b73565b6020026020010181905250866103a0015181601c815181106113c3576113c3615b73565b6020026020010181905250866103c0015181601d815181106113e7576113e7615b73565b6020026020010181905250876040015181601e8151811061140a5761140a615b73565b6020026020010181905250876060015181601f8151811061142d5761142d615b73565b602002602001018190525087608001518160208151811061145057611450615b73565b60200260200101819052508760a001518160218151811061147357611473615b73565b60200260200101819052508761012001518160228151811061149757611497615b73565b6020026020010181905250876101000151816023815181106114bb576114bb615b73565b60200260200101819052508760c00151816024815181106114de576114de615b73565b60200260200101819052508760e001518160258151811061150157611501615b73565b60200260200101819052505f61154787608001518660c001518b6102000151877f00000000000000000000000000000000000000000000000000000000000000006129ec565b9050611571815f8151811061155e5761155e615b73565b60200260200101518661012001516125fb565b608086018190526102008a01515160e08901516115a292916108d19161159791906125fb565b8861014001516125fb565b608086015260e08701516115b590612b4a565b60a08601525f6115c760016024615b39565b6115d2906001615b39565b90505f5b61160160017f00000000000000000000000000000000000000000000000000000000000000006155e0565b811015611812575f61163460017f00000000000000000000000000000000000000000000000000000000000000006155e0565b8210159050806117a357611668610d568b6101000151898560016116589190615b39565b81518110610d4957610d49615b73565b6101208901526101008a015161169890610d569089611688866001615b39565b81518110610d8357610d83615b73565b61014089015260a08801516101208901516116b391906125fb565b61016089015260a088015160e08b01516116db916116d0916125fb565b8961014001516125fb565b61018089018190526116fe906116f0906129da565b6108d18a61016001516129da565b866117098486615b39565b8151811061171957611719615b73565b6020026020010181815250505f6117528961018001518e61020001518560016117429190615b39565b601c8110610bce57610bce615b73565b905061178c816108d18b61016001518887600161176f9190615b39565b8151811061177f5761177f615b73565b60200260200101516125fb565b905061179c896080015182612616565b60808a0152505b6117c26117b88960a001518c60e001516125fb565b8b60e001516125fb565b60a08901526101e08c015182601b81106117de576117de615b73565b6020020151856117ee8486615b39565b815181106117fe576117fe615b73565b6020908102919091010152506001016115d6565b5061183e60017f00000000000000000000000000000000000000000000000000000000000000006155e0565b6118489082615b39565b90506118686118608961010001518a60c0015161262f565b60019061266c565b60e08701515261010088015160c08901516118ac91611860916108ef907f07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76906125fb565b60e0878101805160200192909252815180516040909101529051805160609091015260a0870151908901516118ee916118e4916125fb565b8960e001516125fb565b60a08701525f5b600481101561199c575f6119278860e00151836004811061191857611918615b73565b60200201518960a001516125fb565b9050611932816129da565b886101000151836004811061194957611949615b73565b602002015260a088015160e08b015161196291906125fb565b8860a001818152505061198e88608001516108d1838f61022001518660048110610bce57610bce615b73565b6080890152506001016118f5565b506101008601515184518590839081106119b8576119b8615b73565b602090810291909101810191909152610100870151908101516119dc916002610a7f565b846119e8836001615b39565b815181106119f8576119f8615b73565b60209081029190910101526101008601516060015184611a19836002615b39565b81518110611a2957611a29615b73565b60209081029190910101525f5b6003811015611a8b578a61014001518160038110611a5657611a56615b73565b60200201518483611a6681615b87565b945081518110611a7857611a78615b73565b6020908102919091010152600101611a36565b506040518060400160405280600181526020016002815250838281518110611ab557611ab5615b73565b60200260200101819052508560800151848280611ad190615b87565b935081518110611ae357611ae3615b73565b602002602001018181525050611b0d8a61022001518960c001518a608001518d6101a00151612b55565b611b2a5760405163a2a2ac8360e01b815260040160405180910390fd5b5f8a6102600151905080848381518110611b4657611b46615b73565b6020026020010181905250886101000151858381518110611b6957611b69615b73565b602002602001018181525050611b7d615338565b611b878587612eb8565b8152611b9282612fb0565b602082018190528c5182515f92611baa929190612ff6565b90505f5f611bba8f5f0151613087565b91509150611bc782613148565b611bd081613148565b8351611bdd9083856131e7565b84526020840151611bef9082856131e7565b602085018190528451611c0191613216565b9f9e505050505050505050505050505050565b611c1c615024565b50604080516103e081018252620200008152601160208083019190915261024382840152825180840184527f3046224394ac0664c5df0bbff473db8485b523993c3378b647785875fbda56e581527f075b8449cf262130cd0d53747c76da002a74053958060ee7c8e930b7f729af4e818301526060830152825180840184527f23f894967a8d0ad87a02976c74accf68659348a280e6c1ab3f99dad7b8cf4bc481527f21acfe5f2f788aee6ca1b87420abfb1b567605edbfc9de6cbff67aa99eb633b8818301526080830152825180840184527f2d8eb0ef0769af9b60944c9c1f9aef88f4458599d8d53d5700e57c463d3d248181527f067d884aca949978f4410a65ef7addb04f9e8a13536c428fbe9d71beb988a3218183015260a0830152825180840184527f04f478772f21c19f23074f73f4cafc722ade3b71fd3e674504df4d488e70844f81527f256ede28a442453cee32d59a1ae23e10ad9765b8cd5aa917f613437e401d03df8183015260c0830152825180840184527f296c0719e6414ccc762949a1eb297b40c46b886179cf2ba5d3660b218aa9a72881527f219d51db4d209022eaa607152a2ddc23b94fb99be6ed4b2f158ed46b21faceb88183015260e0830152825180840184527f0cf55eebb61a0e90c6f9fd68c4839012c6433b88fafff66701ab99c6975e6da381527f1f44b9a9447265fad2e8c326a3cfd26d434ed9eece6c2d5b5586f8232572d76581830152610100830152825180840184527f11beb2731ab8cf46c0a52666bb457fa74d4b635043bb5689a35a2aee24990b8081527f2f1fba989036c44aedb609d0d3341ccb842c2d1cd7c80cea4a95d0eb4530577081830152610120830152825180840184527f2d70068023998c277c75458b7b6eb1fe3c479eb044dec7aa3384d7b5049542c781527f10af4b4ffb3c03f9c906e4dd1cf3c21dc74139881dfa7ad73c25370424f3978181830152610140830152825180840184527f0d07902b3266cd4af955bac93e3579b0bc1bcd754c7b70035cc95877d641376c81527f195d808eeb35965b8df7ea80461dcc033e5d8d0ef25b028a1d429e52b33f3f0d81830152610160830152825180840184527f24ec1ac19b9f052e9e2c4270db9b317502859b163514073401512c675318823881527f28869fd3c9b2ad1ce8a966e9225bed7eac600c656be6891317404dc44726736781830152610180830152825180840184527f201e0e19e72484d21e8136546fe6aaeea2d5b6413019276eada6638ffeb2ce8a81527eba5238e406d8d46f68dc3b00a201ad10d2dc97beed32f5e83d051e742b2642818301526101a0830152825180840184527f2656809e98f3dee7945c4f48c6fff3c0833412aeeae84dada472462112fa8fe981527f2454353d981df4ae6c3fd37c4a7b5114bcb1416f43efc627d55e128449c7c14d818301526101c0830152825180840184527f1fb5e9c3ae41ba815444512a4e882619ac69f0cbb639657adeadb93ed50895b181527f201d42c30be1a478f6c6a28159a335c02fa3dc3e6c6843089c648bfd4dc8c250818301526101e0830152825180840184527f1375207e60c7d651a62aed70d2496e2324a7dc718de86e5887f805f907d4964081527f22a964f903d1faa46e20c6f8902415d61985c86bbeef23a01b6ca1718dfe80c581830152610200830152825180840184527f2a38162537d04323884347c1f31f76b03a04ec2f6f9c4b125d5d322932ff65ba81527f2465da5aac067658f2f98944a8ba1bc9fd81c2220d22f07995746a7b7deccd2981830152610220830152825180840184527f2a12695e6fa7a23c3519eee6d96433bb00d51cd274de90288914e3fb336334b681527f235e728947aff729c4ade5a63ca501273a958075ae1281bb612012f03219601681830152610240830152825180840184527f09e5efa8f84174e74fe0429a76ec404c053a4f8c3e5524d68e3cd4e338a2b4b881527f057873d3cf1d8589310604f6f203386d9a9928d980432f5316522d84e6eecb6181830152610260830152825180840184527f096a6fb164d4a53c869c12d0369ef747bbbd3f40e5dd997c01e32495b64b07cb81527f01198ac63b80d1d160ab5882da3dfc3a8864b70b72c31790e55fec5014c4436281830152610280830152825180840184527f085cbd5bb5a6a7a0407ad624e78b82be37c9c90a9c5de9302004bc406bc1778d81527f2a4cbd271054bf508c73ffeab6e321bba811248126e552f55701993aed192e4b818301526102a0830152825180840184527f1a89bfbff2ee55715a55cf0de0d6a1c62183bbaae9d200a5247b58f170cbbc7c81527f0ae77530f6cad79672428f376996a91ce1358fea5f49180218f18a7918f80bb3818301526102c0830152825180840184527f1faa95912a219c90e3cdbe1e78b25d6f79d4ca8ef1cd9fd3a8a6b87f94d138f281527f22a4b4a860838e96fcc07839e0c9742c2dbe4aa634664da1e24570a1dc4156fd818301526102e0830152825180840184527f1d11efcd01d1170fc229d305654298122dd323cc9ae7e16ad2f0bfa56a0f9e2e81527f225cd914fa8180a343e93ee8a0e87e2d850b8ca6e03267e5281f7ace3dd82ab581830152610300830152825180840184527f099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d2681527e15b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f81830152610320830152825180840184527f1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e81527f305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d1981830152610340830152825180840184527f061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d81527f1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e81830152610360830152825180840184527f043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce81527f261522c4089330646aff96736194949330952ae74c573d1686d9cb4a007338548183015261038083015282518084018452600181526002818301526103a083015282518084019093527f1af84073758283d4c848fed9bc6f055831baf81a643050fceafadf8da1791eec83527f1be165956a9c6a29edc2a4d21793a6fb1f00a6967720f870ebb2f2c6b267e9dd908301526103c081019190915290565b5f6103f461255c8385615b9f565b612657565b612569615320565b60408051808201909152805f516020615c6e5f395f51905f5261258f60205f8789615b4c565b61259891615b9f565b6125a29190615bbc565b81526020908101905f516020615c6e5f395f51905f52906125c7906040908789615b4c565b6125d091615b9f565b6125da9190615bbc565b90529392505050565b5f5f516020615c8e5f395f51905f52825b0692915050565b5f5f516020615c8e5f395f51905f5282840990505b92915050565b5f5f516020615c8e5f395f51905f528284089392505050565b5f5f516020615c8e5f395f51905f52825f516020615c8e5f395f51905f520384089392505050565b5f5f516020615c8e5f395f51905f52826125f4565b5f6103f4836108cc8461296a565b5f5f604051806101200160405280619d8081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec5181526020016105a081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31815260200161024081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd3181526020016105a081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec518152602001619d8081525090505f600190505f5f90505b600981101561277357612769826108cc878461262f565b9150600101612752565b5061277c61534b565b5f5b60098110156127cd576127ae610d5685836009811061279f5761279f615b73565b60200201516108cc898561262f565b8282600981106127c0576127c0615b73565b602002015260010161277e565b505f5b600981101561281357612809856108d18984600981106127f2576127f2615b73565b6020020151858560098110610bce57610bce615b73565b94506001016127d0565b5061281e84836125fb565b9695505050505050565b5f61283161536a565b61283c86828561340a565b612848868683866135b6565b612854868683866137a2565b61285f8682856139c7565b61286a868285613bbb565b61287686868386613f04565b6128818682856143b0565b61288c8682856147c2565b612897868285614b83565b61281e8185614e83565b60605f826001600160401b038111156128bc576128bc615940565b6040519080825280602002602001820160405280156128e5578160200160208202803683370190505b50905083815f815181106128fb576128fb615b73565b602090810291909101015260015b838110156129625761293d826129206001846155e0565b8151811061293057612930615b73565b6020026020010151612b4a565b82828151811061294f5761294f615b73565b6020908102919091010152600101612909565b509392505050565b5f5f8290505f604051602081526020808201526020604082015282606082015260025f516020615c8e5f395f51905f520360808201525f516020615c8e5f395f51905f5260a082015260205f60c08360055afa806129c6575f5ffd5b505f51608091909101604052949350505050565b5f516020615c8e5f395f51905f520390565b60605f826001600160401b03811115612a0757612a07615940565b604051908082528060200260200182016040528015612a30578160200160208202803683370190505b509050825b8015612b3f575f85612a486001846155e0565b81518110612a5857612a58615b73565b602002602001015190505f89600184612a7191906155e0565b601c8110612a8157612a81615b73565b602002015190505f612adc612aa0612a99858d6125fb565b60026125fb565b6108ef8b612aaf6001896155e0565b601c8110612abf57612abf615b73565b60200201516108cc612ad6886108cc60018a61262f565b8761262f565b9050612afd816108cc610d56612af7876108cc60018961262f565b86612616565b99508990508085612b0f6001876155e0565b81518110612b1f57612b1f615b73565b60200260200101818152505050505080612b3890615bdb565b9050612a35565b509695505050505050565b5f61261082836125fb565b5f600181612b6e612b6887610100614edc565b8361262f565b905080612b8e5760405163835eb8f760e01b815260040160405180910390fd5b612b96615389565b80518390525f5b7f0000000000000000000000000000000000000000000000000000000000000000811015612c7f575f612bd18260096155c9565b612bdc906001615b39565b905084835f0151826101008110612bf557612bf5615b73565b60200201525f612c06826001615b39565b90505b612c14600983615b39565b811015612c75578351612c5490612c2c6001846155e0565b6101008110612c3d57612c3d615b73565b60200201518a85601c8110610bce57610bce615b73565b8451826101008110612c6857612c68615b73565b6020020152600101612c09565b5050600101612b9d565b50608081018390525f602082018190525b610100811015612d9057612cb1612cab83608001518a6125fb565b8561262f565b8260a00151826101008110612cc857612cc8615b73565b602002015260a0820151612cf290826101008110612ce857612ce8615b73565b602002015161296a565b8260a00151826101008110612d0957612d09615b73565b602002018181525050612d4f82602001516108d1845f0151846101008110612d3357612d33615b73565b60200201518560a00151856101008110610bce57610bce615b73565b60208301526080820151612d83907f204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d66125fb565b6080830152600101612c90565b505f612da1836108cc61010061296a565b9050612db18260200151826125fb565b602083015260a0820151612dcc905f5b6020020151826125fb565b604083015260a0820151612df890612de760016101006155e0565b6101008110612dc157612dc1615b73565b60608301526040820151612e0e908a6002610bce565b60c08301819052612e6e906108d1612e468b7f204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d661262f565b60208d015160408e01516108cc91612e5d9161262f565b8e5160208901516108ef91906125fb565b60c083018190526060830151612ea391612e97916108d1906108cc8e600260200201518c61262f565b6108ef858c6003610bce565b60c08301819052159998505050505050505050565b612ec0615320565b7f00000000000000000000000000000000000000000000000000000000000000005f5b81811015612f1557612f0d858281518110612f0057612f00615b73565b6020026020010151613148565b600101612ee3565b50604051600190815b60018401811015612f7a5760208102870160208202870181515160408501528151602001516060850152805160808501525050604080830160606040850160075afa8316925060408260808460065afa90921691600101612f1e565b5080518452602081015160208501525080612fa8576040516352ec174560e11b815260040160405180910390fd5b505092915050565b612fb8615320565b5f516020615c6e5f395f51905f5282602001515f516020615c6e5f395f51905f52612fe391906155e0565b612fed9190615bbc565b60208301525090565b5f5f5f61300286613087565b9150915061300e6153cd565b82518152602080840151818301528251604080840191909152838201516060840152875160808401528782015160a0840152865160c08401528682015160e08401525161307c9161306191849101615bf0565b60405160208183030381529060405280519060200120612657565b979650505050505050565b61308f615320565b613097615320565b82516020808501516040860151606087015160cc90811b608892831b604494851b90961795909517949094178652608087015160a088015160c089015160e08a0151871b90841b91851b9092171717868401526101008701516101208801516101408901516101608a0151871b90841b91851b909217171785526101808701516101a08801516101c08901516101e09099015190951b9790911b9390911b1791909117939093179281019290925291565b805160208201515f5f516020615c6e5f395f51905f528380095f516020615c6e5f395f51905f5260035f516020615c6e5f395f51905f52838709085f516020615c6e5f395f51905f5284850914915050806131e15760405162461bcd60e51b8152602060048201526019602482015278706f696e74206973206e6f74206f6e2074686520637572766560381b60448201526064016100e5565b50505050565b6131ef615320565b6131f7615320565b6132018386614f3d565b905061320d8185614f93565b95945050505050565b81516020808401518351848301516040805194850195909552938301919091527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260608301527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed60808301527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b60a08301527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60c083015260e08201526101008101919091527f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c16101208201527f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b06101408201527f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe46101608201527f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e556101808201525f9081906101a00160405160208183030381529060405290505f5f60086001600160a01b0316836040516133ac9190615c24565b5f60405180830381855afa9150503d805f81146133e4576040519150601f19603f3d011682016040523d82523d5f602084013e6133e9565b606091505b509150915081801561281e57508080602001905181019061281e9190615c3a565b5f613416846007614ff4565b90507f183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f80000005f61347761347161344b85600361262f565b6108cc61346661345b8b5f614ff4565b6108cc8c601d614ff4565b6108cc8b601c614ff4565b836125fb565b90506134f86134ed6134d46134bb6134a2856108d16134978d6002614ff4565b6108cc8e601c614ff4565b6108d16134b08c6003614ff4565b6108cc8d601d614ff4565b6108d16134c98b6004614ff4565b6108cc8c601e614ff4565b6108d16134e28a6005614ff4565b6108cc8b601f614ff4565b6108d1886001614ff4565b9050613517816108d161350c86600161262f565b6108cc8a6027614ff4565b905061352381846125fb565b905061352f81856125fb565b8552505f905061356961355f61355461354988601c614ff4565b6108d189601f614ff4565b6108ef886024614ff4565b6108d1875f614ff4565b905061357a816108cc84600261262f565b905061358b816108cc84600161262f565b905061359781836125fb565b90506135a381846125fb565b9050808460015b60200201525050505050565b5f5f5f6135ec6135e26135ca89601c614ff4565b6108d16135d88b6012614ff4565b8a606001516125fb565b8760800151612616565b9050613625816108cc61361b6136038b601d614ff4565b6108d16136118d6013614ff4565b8c606001516125fb565b8960800151612616565b905061364a816108cc61361b61363c8b601e614ff4565b6108d16136118d6014614ff4565b905061366f816108cc61361b6136618b601f614ff4565b6108d16136118d6015614ff4565b92505f90506136936135e261368589601c614ff4565b6108d16135d88b600e614ff4565b90506136b8816108cc61361b6136aa8b601d614ff4565b6108d16136118d600f614ff4565b90506136dd816108cc61361b6136cf8b601e614ff4565b6108d16136118d6010614ff4565b9050613702816108cc61361b6136f48b601f614ff4565b6108d16136118d6011614ff4565b91505f9050613729613723613718896020614ff4565b6108d18a601a614ff4565b846125fb565b905061375e816108ef6137586137408b6028614ff4565b6108d161374e8d601b614ff4565b8c60a001516125fb565b856125fb565b905061376a81856125fb565b6040860152505f61378d61375861378289601b614ff4565b6108cc8a6028614ff4565b9050808560035b602002015250505050505050565b5f5f6138006137e86137d06137bb61361b8a6016614ff4565b6108d16137c98b6017614ff4565b8a516125fb565b6108d16137de8a6018614ff4565b89602001516125fb565b6108d16137f6896019614ff4565b88604001516125fb565b91505f61383761381e61381489601c614ff4565b8860800151612616565b6108d161382c8a6003614ff4565b6108cc8b6024614ff4565b90505f61386061384889601d614ff4565b6108d16138558b5f614ff4565b6108cc8c6025614ff4565b90505f61388a6138718a601e614ff4565b6108d161387f8c6001614ff4565b6108cc8d6026614ff4565b90506138c96138b16138a3856108d1868d5f01516125fb565b6108d1848c602001516125fb565b6108d16138bf8c6004614ff4565b8b604001516125fb565b93505050505f6138dd613723886021614ff4565b90505f6138ee613723896021614ff4565b90505f61392661390d6139028b6023614ff4565b6108d18c6006614ff4565b6108ef61391b8c6023614ff4565b6108cc8d6006614ff4565b90505f613944612b6861393987896125fb565b6108cc8d6021614ff4565b905061395081886125fb565b90505f61397861396a6139648d6006614ff4565b876125fb565b6108ef6139648e6022614ff4565b90505f6139868c6023614ff4565b90505f613996612b6883846125fb565b60808c0185905260a08c0184905290506139b0818b6125fb565b8b6006602002015250505050505050505050505050565b5f6139d35f600161262f565b90505f6139e15f600261262f565b90505f6139ef5f600361262f565b90505f613a0b613a0088601d614ff4565b6108ef89601c614ff4565b90505f613a27613a1c89601e614ff4565b6108ef8a601d614ff4565b90505f613a43613a388a601f614ff4565b6108ef8b601e614ff4565b90505f613a5f613a548b6024614ff4565b6108ef8c601f614ff4565b905083613a70816108cc818b612616565b9050613a80816108cc878a612616565b9050613a90816108cc8789612616565b9050613aa1816108cc8d6008614ff4565b9050613aad818a6125fb565b60e08b01525082613ac2816108cc818b612616565b9050613ad2816108cc868a612616565b9050613ae2816108cc8689612616565b9050613af3816108cc8d6008614ff4565b9050613aff818a6125fb565b6101008b01525081613b15816108cc818b612616565b9050613b25816108cc858a612616565b9050613b35816108cc8589612616565b9050613b46816108cc8d6008614ff4565b9050613b52818a6125fb565b6101208b01525080613b68816108cc818b612616565b9050613b78816108cc848a612616565b9050613b88816108cc8489612616565b9050613b99816108cc8d6008614ff4565b9050613ba5818a6125fb565b610140909a019990995250505050505050505050565b613bf46040518060e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b613bff84601d614ff4565b8152613c0c84601e614ff4565b6020820152613c1c846024614ff4565b6040820152613c2c846027614ff4565b6060820152613c3c846026614ff4565b6080820152613c4c846025614ff4565b60a08201525f613c5d856002614ff4565b90505f613c6a865f614ff4565b90505f613c7e8460400151855f015161262f565b90505f613c93856020015186602001516125fb565b606086015190915086905f90613ca990806125fb565b90505f613cc7613cc189602001518a606001516125fb565b886125fb565b90505f613ce6613cdf8a60a001518b60400151612616565b8a51612616565b9050613cf561396482886125fb565b9050613d1c613d16613d10613d0a848761262f565b8861262f565b84612616565b83612616565b9050613d44613d39613d2e83876125fb565b6108cc8f6009614ff4565b6108cc60018a61262f565b6101608c015250505050602085015160808601515f91613d6391612616565b90505f613d81613d778860600151886125fb565b886020015161262f565b90505f613da5613d9184876125fb565b6108d16137588b60a001518c5f015161262f565b9050613dcd613dc2613db7838c6125fb565b6108cc8e6009614ff4565b6108cc60018961262f565b6101808b0152505f9150613dee9050613de7836011612616565b87516125fb565b90505f613dfb8384612616565b9050613e078182612616565b90505f613e158360096125fb565b9050613e3e613e38613723613e318b60a001518c5f0151612616565b8b51612616565b8261262f565b60c089018190525f90613e5990613cc190613d2e908d6125fb565b9050613e6c8b600b602002015182612616565b6101608c0152505086515f9250613e939150613de790613e8c9080612616565b8851612616565b90505f613ed3613eae836108cc8a5f01518b60a0015161262f565b60208901516108ef90613ec19080612616565b6108cc8b602001518c60800151612616565b9050613ef089600c60200201516108d1613cc1613d2e858d6125fb565b89600c602002015250505050505050505050565b613f6e604051806101e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b613f86613f7c86601e614ff4565b85604001516125fb565b808252613fa5906108d1613f9b88601d614ff4565b87602001516125fb565b808252613fba906108d1613de788601c614ff4565b808252613fcc906108d1876001614ff4565b80825260208201819052613fe5906108ef87601f614ff4565b8152614000613ff5866024614ff4565b6108ef87601c614ff4565b608082015261401e614013866027614ff4565b6108ef87601f614ff4565b60608201526080810151614037906108cc81600161262f565b6101c082015260808101516140779061406d90614066906108cc60015f516020615c8e5f395f51905f526155e0565b6001612616565b82606001516125fb565b60a082018190526140ad9061409f906108cc614094896002614ff4565b6108cc8a6003614ff4565b6108cc61375888600a614ff4565b83600e60200201526101c08101516140d19061409f906108cc614094896002614ff4565b6101e084015280516140f6906108cc6140eb886002614ff4565b6108cc896003614ff4565b6101208201525f61411561410b87601f614ff4565b836020015161262f565b9050614126816108cc83600161262f565b60e0830152614143614139876026614ff4565b86604001516125fb565b60408301819052614166906108d161415c896025614ff4565b88602001516125fb565b60408301819052614186906108d161417f896024614ff4565b88516125fb565b60408301526141a3614199876027614ff4565b836040015161262f565b60408301525f6141c26141b7886026614ff4565b6108ef89601e614ff4565b90506142146141ef613471614066866080015160015f516020615c8e5f395f51905f526108cc91906155e0565b6108cc614066866040015160015f516020615c8e5f395f51905f526108cc91906155e0565b60c084015260408301516142369061422c90806125fb565b846040015161262f565b61010084015260c083015161426290614254906108cc8a6004614ff4565b6108cc6139648a600a614ff4565b6102008601526101c083015161428190614254906108cc8a6004614ff4565b6102208601526101008301516142a090614254906108cc8a6004614ff4565b61024086015260e08301516142ba906108cc896004614ff4565b6101408401526142d96142ce886025614ff4565b6108ef89601d614ff4565b6101608401526080830151614317906141b79061430c90614066906108cc60015f516020615c8e5f395f51905f526155e0565b8561016001516125fb565b61018084018190526101208401516101a0850181905261434e916108d1906108cc6143438c6005614ff4565b6108cc8d6002614ff4565b6101a08401819052835161436e91906108d1906108cc6143438c5f614ff4565b6101a084018190526101408401516143869190612616565b6101a084018190526143a0906108cc6139648a600a614ff4565b6101a0840181905285600d613794565b6143e96040518060e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b61441e6144056143fa86601c614ff4565b6108cc876025614ff4565b6108d1614413876024614ff4565b6108cc88601d614ff4565b815261446361445861443f61443487601c614ff4565b6108cc88601f614ff4565b6108d161444d88601d614ff4565b6108cc89601e614ff4565b6108ef866026614ff4565b6040820181905261447890600160441b6125fb565b6040820181905261448e906108ef866027614ff4565b6040820181905281516144a19190612616565b604082018190526144b7906108cc866005614ff4565b604082015280516144cc90600160441b6125fb565b8082526144ec906108d16144e1876024614ff4565b6108cc886025614ff4565b80825260208201819052614513906108ef61450887601e614ff4565b6108d188601f614ff4565b60208201819052614529906108cc866004614ff4565b6020820152805160608201819052614546906108d186601f614ff4565b6060820181905261456a906108ef61455f876026614ff4565b6108d1886027614ff4565b6060820181905261457f906108cc865f614ff4565b8160600181815250505f6145a861459e83602001518460400151612616565b8360600151612616565b90506145b9816108cc876003614ff4565b90506145d16145c9866025614ff4565b6140006125fb565b608083018190526145e7906108d1876024614ff4565b608083018190526145fa906140006125fb565b60808301819052614610906108d187601e614ff4565b60808301819052614623906140006125fb565b60808301819052614639906108d187601d614ff4565b6080830181905261464c906140006125fb565b60808301819052614662906108d187601c614ff4565b60808301819052614678906108ef87601f614ff4565b6080830181905261468e906108cc876005614ff4565b60808301526146a16145c9866026614ff4565b60a083018190526146b7906108d1876025614ff4565b60a083018190526146ca906140006125fb565b60a083018190526146e0906108d1876024614ff4565b60a083018190526146f3906140006125fb565b60a08301819052614709906108d187601f614ff4565b60a0830181905261471c906140006125fb565b60a08301819052614732906108d187601e614ff4565b60a08301819052614748906108ef876027614ff4565b60a0830181905261475d906108cc875f614ff4565b60a0830181905260808301515f916147759190612616565b9050614786816108cc886004614ff4565b90506147928282612616565b60c084018190526147ab906108cc61396489600b614ff4565b60c084018190528560136020020152505050505050565b6148386040518061022001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b61485161484685601c614ff4565b6108d1866002614ff4565b815261486c61486185601d614ff4565b6108d1866003614ff4565b602082015261488a61487f85601e614ff4565b6108d1866004614ff4565b60408201526148a861489d85601f614ff4565b6108d1866005614ff4565b606082015280516148da906148d3906148cc906148c590806125fb565b84516125fb565b83516125fb565b82516125fb565b608082015260208101516149189061490e90614904906148fa90806125fb565b84602001516125fb565b83602001516125fb565b82602001516125fb565b60a082015260408101516149569061494c906149429061493890806125fb565b84604001516125fb565b83604001516125fb565b82604001516125fb565b60c0820152606081015161498a9061406d906149809061497690806125fb565b84606001516125fb565b83606001516125fb565b60e0820152608081015160a08201516149a39190612616565b61010082015260c081015160e08201516149bd9190612616565b61012082015260a08101516149e1906149d69080612616565b826101200151612616565b61014082015260e0810151614a05906149fa9080612616565b826101000151612616565b610160820152610120810151614a1b9080612616565b6101e08201819052614a3c90614a319080612616565b826101600151612616565b6101e0820152610100810151614a529080612616565b6101a08201819052614a7390614a689080612616565b826101400151612616565b6101a08201819052610160820151614a8a91612616565b6101808201526101408101516101e0820151614aa69190612616565b6101c0820152614aba61347185600c614ff4565b6102008201819052610280840151610180830151614ae5926108d1916108cc906108ef8a6024614ff4565b8360146020020152614b1583601560200201516108d18361020001516108cc856101a001516108ef8a6025614ff4565b8360156020020152614b4583601660200201516108d18361020001516108cc856101c001516108ef8a6026614ff4565b8360166020020152614b7583601760200201516108d18361020001516108cc856101e001516108ef8a6027614ff4565b836017602002015250505050565b614bd56040518061016001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b5f6040518060800160405280614c0a7f10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e76125e3565b8152602001614c387f0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b6125e3565b8152602001614c657e544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac156125e3565b8152602001614c937f222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b6125e3565b90529050614cb0614ca586601c614ff4565b6108d1876002614ff4565b6101208301819052614cef90614ce490614cd990614cce90806125fb565b8561012001516125fb565b8461012001516125fb565b8361012001516125fb565b8252614cfc85601d614ff4565b6020830152614d0c85601e614ff4565b6040830152614d1c85601f614ff4565b606083015281516020830151614d449161459e91614d3a9190612616565b8460400151612616565b6080830152614d5761372386600d614ff4565b6101408301528151614d7890614d6e90835f610bce565b8360800151612616565b60a0830152614da484601860200201516108d18461014001516108cc8660a001516108ef8b6024614ff4565b6103008501526020820151614dbf90614d6e90836001610bce565b60c0830152614deb84601960200201516108d18461014001516108cc8660c001516108ef8b6025614ff4565b6103208501526040820151614e0690614d6e90836002610bce565b60e0830152614e3284601a60200201516108d18461014001516108cc8660e001516108ef8b6026614ff4565b6103408501526060820151614e4d90614d6e90836003610bce565b610100830152614e7b84601b60200201516108d18461014001516108cc8661010001516108ef8b6027614ff4565b84601b6135aa565b815160015b601c811015614ed557614ecb826108d18684601c8110614eaa57614eaa615b73565b602002015186614ebb6001876155e0565b601b8110610bce57610bce615b73565b9150600101614e88565b5092915050565b5f5f8390505f60405160208152602080820152602060408201528260608201528460808201525f516020615c8e5f395f51905f5260a082015260205f60c08360055afa80614f28575f5ffd5b505f5160809190910160405295945050505050565b614f45615320565b614f4d615320565b604051835181526020840151602082015284604082015260408160608360075afa80614f77575f5ffd5b5080518252602080820151908301526060016040529392505050565b614f9b615320565b614fa3615320565b6040518451815260208501516020820152835160408201526020840151606082015260408160808360065afa80614fd8575f5ffd5b5080518252602080820151908301526080016040529392505050565b5f8282602881111561500857615008615c59565b6029811061501857615018615b73565b60200201519392505050565b604051806103e001604052805f81526020015f81526020015f815260200161504a615320565b8152602001615057615320565b8152602001615064615320565b8152602001615071615320565b815260200161507e615320565b815260200161508b615320565b8152602001615098615320565b81526020016150a5615320565b81526020016150b2615320565b81526020016150bf615320565b81526020016150cc615320565b81526020016150d9615320565b81526020016150e6615320565b81526020016150f3615320565b8152602001615100615320565b815260200161510d615320565b815260200161511a615320565b8152602001615127615320565b8152602001615134615320565b8152602001615141615320565b815260200161514e615320565b815260200161515b615320565b8152602001615168615320565b8152602001615175615320565b8152602001615182615320565b815260200161518f615320565b815260200161519c615320565b81526020016151a9615320565b905290565b6040518061028001604052806151c26153ec565b81526020016151cf615320565b81526020016151dc615320565b81526020016151e9615320565b81526020016151f6615320565b8152602001615203615320565b8152602001615210615320565b815260200161521d615320565b815260200161522a615320565b8152602001615237615320565b815260200161524461540b565b81526020015f8152602001615257615438565b81526020015f815260200161526a615466565b8152602001615277615485565b815260200161528461536a565b815260200161518f6154b3565b6040518061052001604052806029906020820280368337509192915050565b604051806101c001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020016152ee6154b3565b81526020016152fb6154b3565b81526020015f81526020015f81526020015f81526020015f8152602001606081525090565b60405180604001604052805f81526020015f81525090565b604051806040016040528061519c615320565b6040518061012001604052806009906020820280368337509192915050565b604051806103800160405280601c906020820280368337509192915050565b6040518060e0016040528061539c6154d1565b81526020015f81526020015f81526020015f81526020015f81526020016153c16154d1565b81526020015f81525090565b6040518061010001604052806008906020820280368337509192915050565b6040518061020001604052806010906020820280368337509192915050565b60405180606001604052806003905b615422615320565b81526020019060019003908161541a5790505090565b604051806103800160405280601c905b61545061534b565b8152602001906001900390816154485790505090565b604051806105400160405280602a906020820280368337509192915050565b604051806103600160405280601b905b61549d615320565b8152602001906001900390816154955790505090565b60405180608001604052806004906020820280368337509192915050565b604051806120000160405280610100906020820280368337509192915050565b5f5f5f5f60408587031215615504575f5ffd5b84356001600160401b03811115615519575f5ffd5b8501601f81018713615529575f5ffd5b80356001600160401b0381111561553e575f5ffd5b87602082840101111561554f575f5ffd5b6020918201955093508501356001600160401b0381111561556e575f5ffd5b8501601f8101871361557e575f5ffd5b80356001600160401b03811115615593575f5ffd5b8760208260051b84010111156155a7575f5ffd5b949793965060200194505050565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417612610576126106155b5565b81810381811115612610576126106155b5565b805f5b60108110156131e15781518452602093840193909101906001016155f6565b805f5b60038110156131e15761563684835180518252602090810151910152565b6040939093019260209190910190600101615618565b805f5b601c8110156131e1578151845f5b600981101561567c57825182526020928301929091019060010161565d565b50505061012093909301926020919091019060010161564f565b805f5b602a8110156131e1578151845260209384019390910190600101615699565b805f5b601b8110156131e1576156d984835180518252602090810151910152565b60409390930192602091909101906001016156bb565b805f5b601c8110156131e15781518452602093840193909101906001016156f2565b805f5b60048110156131e1578151845260209384019390910190600101615714565b8183525f6001600160fb1b0383111561574a575f5ffd5b8260051b80836020870137939093016020019392505050565b61576e8188516155f3565b5f602088015161578c61020084018280518252602090810151910152565b5060408801518051610240840152602090810151610260840152606089015180516102808501528101516102a0840152608089015180516102c08501528101516102e084015260a0890151805161030085015281015161032084015260c0890151805161034085015281015161036084015260e089015180516103808501528101516103a084015261010089015180516103c08501528101516103e084015261012089015180516104008501520151610420830152610140880151615855610440840182615615565b5061016088015161050083015261018088015161587661052084018261564c565b506101a08801516124a08301526101c08801516158976124c0840182615696565b506101e08801516158ac612a008401826156b8565b506102008801516158c16130c08401826156ef565b506102208801516158d6613440840182615711565b5061024088015180516134c08401526020908101516134e0840152610260890151805161350085015201516135208301526135c0613540830181905261591f9083018789615733565b613560830195909552506135808101929092526135a0909101529392505050565b634e487b7160e01b5f52604160045260245ffd5b60405161014081016001600160401b038111828210171561597757615977615940565b60405290565b604051601f8201601f191681016001600160401b03811182821017156159a5576159a5615940565b604052919050565b5f60c082840312156159bd575f5ffd5b60405160c081016001600160401b03811182821017156159df576159df615940565b604090815283518252602080850151908301528381015190820152606080840151908201526080808401519082015260a0928301519281019290925250919050565b5f82601f830112615a30575f5ffd5b5f610360615a3d8161597d565b915083018185821115615a4e575f5ffd5b845b82811015615a68578051825260209182019101615a50565b509195945050505050565b5f82601f830112615a82575f5ffd5b5f610380615a3d8161597d565b5f610be0828403128015615aa1575f5ffd5b50615aaa615954565b615ab484846159ad565b8152615ac38460c08501615a21565b6020820152615ad6846104208501615a73565b60408201526107a08301516060820152615af4846107c08501615a73565b6080820152610b4083015160a0820152610b6083015160c0820152610b8083015160e0820152610ba0830151610100820152610bc09092015161012083015250919050565b80820180821115612610576126106155b5565b5f5f85851115615b5a575f5ffd5b83861115615b66575f5ffd5b5050820193919092039150565b634e487b7160e01b5f52603260045260245ffd5b5f60018201615b9857615b986155b5565b5060010190565b80356020831015612610575f19602084900360031b1b1692915050565b5f82615bd657634e487b7160e01b5f52601260045260245ffd5b500690565b5f81615be957615be96155b5565b505f190190565b5f8183825b6008811015615c14578151835260209283019290910190600101615bf5565b5050506101008201905092915050565b5f82518060208501845e5f920191825250919050565b5f60208284031215615c4a575f5ffd5b815180151581146103f4575f5ffd5b634e487b7160e01b5f52602160045260245ffdfe30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4730644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", + "deployedBytecode": "0x608060405234801561000f575f5ffd5b5060043610610029575f3560e01c8063ea50d0e41461002d575b5f5ffd5b61004061003b3660046154f1565b610054565b604051901515815260200160405180910390f35b5f5f61007f7f00000000000000000000000000000000000000000000000000000000000000006102ee565b905061008c8160206155c9565b85146100ee577f0000000000000000000000000000000000000000000000000000000000000000856100bf8360206155c9565b6040516359895a5360e01b81526004810193909352602483019190915260448201526064015b60405180910390fd5b5f6100f76103fb565b90505f61012588887f0000000000000000000000000000000000000000000000000000000000000000610410565b90506010826040015161013891906155e0565b85146101575760405163fa06659360e01b815260040160405180910390fd5b60405163995bf45760e01b81525f9073__$2b3697cbc38bc9b2f889ee4ee8a0f41368$__9063995bf457906101fa9085908b908b907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090600401615763565b610be060405180830381865af4158015610216573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061023a9190615a8f565b905061028a8787808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050855185516060810151608090910151919350915060016108a9565b815160a0015261029a8282610a10565b6102b7576040516313f8744360e31b815260040160405180910390fd5b6102c2828483610c17565b6102df576040516352ec174560e11b815260040160405180910390fd5b50600198975050505050505050565b5f8060026102fe60016008615b39565b61030891906155c9565b9050610316600260036155c9565b6103209082615b39565b9050600161032f6009856155c9565b61033991906155c9565b6103439082615b39565b90506001610352816029615b39565b61035c91906155c9565b6103669082615b39565b9050610374600160026155c9565b61037e9082615b39565b905061038b6001846155c9565b6103959082615b39565b90506103a3600160046155c9565b6103ad9082615b39565b905060026103bc6001856155e0565b6103c691906155c9565b6103d09082615b39565b90506103dd6002806155c9565b6103e79082615b39565b90506103f4601082615b39565b9392505050565b610403615024565b61040b611c14565b905090565b6104186151ae565b5f805b601081101561047a57610447868387610435602083615b39565b9261044293929190615b4c565b61254e565b8351826010811061045a5761045a615b73565b6020020181815250506020826104709190615b39565b915060010161041b565b5061049e85828661048c604083615b39565b9261049993929190615b4c565b612561565b60208301526104ae604082615b39565b90506104c185828661048c604083615b39565b6040808401919091526104d49082615b39565b90506104e785828661048c604083615b39565b60608301526104f7604082615b39565b905061050a85828661048c604083615b39565b608083015261051a604082615b39565b905061052d85828661048c604083615b39565b60c083015261053d604082615b39565b905061055085828661048c604083615b39565b60e0830152610560604082615b39565b905061057385828661048c604083615b39565b60a0830152610583604082615b39565b905061059685828661048c604083615b39565b6101008301526105a7604082615b39565b90506105ba85828661048c604083615b39565b6101208301526105cb604082615b39565b90506105de85828661048c604083615b39565b610140830151526105f0604082615b39565b9050610603858286610435602083615b39565b610160830152610614602082615b39565b90505f5b83811015610692575f5b60098110156106895761063c878488610435602083615b39565b84610180015183601c811061065357610653615b73565b6020020151826009811061066957610669615b73565b60200201818152505060208361067f9190615b39565b9250600101610622565b50600101610618565b505f5b6106a160016029615b39565b8110156106f0576106b9868387610435602083615b39565b836101c0015182602a81106106d0576106d0615b73565b6020020181815250506020826106e69190615b39565b9150600101610695565b50610702858286610435602083615b39565b6101a0830152610713602082615b39565b905061072685828661048c604083615b39565b6101408301516020015261073b604082615b39565b905061074e85828661048c604083615b39565b61014083015160026020020152610766604082615b39565b90505f5b6107756001856155e0565b8110156107be5761078d86838761048c604083615b39565b836101e0015182601b81106107a4576107a4615b73565b60200201526107b4604083615b39565b915060010161076a565b505f5b83811015610811576107da868387610435602083615b39565b83610200015182601c81106107f1576107f1615b73565b6020020181815250506020826108079190615b39565b91506001016107c1565b505f5b60048110156108655761082e868387610435602083615b39565b836102200151826004811061084557610845615b73565b60200201818152505060208261085b9190615b39565b9150600101610814565b5061087785828661048c604083615b39565b610240830152610888604082615b39565b905061089b85828661048c604083615b39565b610260830152509392505050565b5f600180826108d6866108d1896108cc6108c78a6310000000615b39565b6125e3565b6125fb565b612616565b90505f6108f4876108ef8a6108cc6108c78b6001615b39565b61262f565b90505f5b61092360107f00000000000000000000000000000000000000000000000000000000000000006155e0565b811015610990575f61094d8c838151811061094057610940615b73565b6020026020010151612657565b905061095d866108cc8684612616565b955061096d856108cc8584612616565b9450610979848b612616565b9350610985838b61262f565b9250506001016108f8565b505f5b60108110156109f7575f8a82601081106109af576109af615b73565b602002015190506109c4866108cc8684612616565b95506109d4856108cc8584612616565b94506109e0848b612616565b93506109ec838b61262f565b925050600101610993565b50610a02848461266c565b9a9950505050505050505050565b5f5f610a2583606001518561016001516125fb565b905060015f5b7f0000000000000000000000000000000000000000000000000000000000000000811015610b17575f86610180015182601c8110610a6b57610a6b615b73565b602002015180519091505f90610a89908360015b6020020151612616565b9050848114610aab576040516313f8744360e31b815260040160405180910390fd5b5f876080015184601c8110610ac257610ac2615b73565b60200201519050610ad3838261267a565b9550610b07856108cc60016108d1856108cc8e604001518b601c8110610afb57610afb615b73565b6020020151600161262f565b9450505050806001019050610a2b565b50610b20615291565b5f5b6029811015610b70576101c0870151610b3c600183615b39565b602a8110610b4c57610b4c615b73565b6020020151828260298110610b6357610b63615b73565b6020020152600101610b22565b505f610b8582875f0151886020015186612828565b9050600160025b7f0000000000000000000000000000000000000000000000000000000000000000811015610be257610bd882896080015183601c8110610bce57610bce615b73565b60200201516125fb565b9150600101610b8c565b50610c08610bf5836108cc60018561262f565b6108d18a6101a001518a606001516125fb565b94909414979650505050505050565b5f610c206152b0565b5f610c4f8460c001517f00000000000000000000000000000000000000000000000000000000000000006128a1565b90505f7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03811115610c8a57610c8a615940565b604051908082528060200260200182016040528015610cb3578160200160208202803683370190505b5090505f7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03811115610cef57610cef615940565b604051908082528060200260200182016040528015610d2857816020015b610d15615320565b815260200190600190039081610d0d5790505b509050610d5b610d56876101000151855f81518110610d4957610d49615b73565b602002602001015161262f565b61296a565b84610120018181525050610d90610d56876101000151855f81518110610d8357610d83615b73565b6020026020010151612616565b610140850181905261012085015160e0880151610db1926108d191906125fb565b845260c0860151610de190610dc59061296a565b6108cc8661012001516108ef8a60e001518961014001516125fb565b6020850152815160019083905f90610dfb57610dfb615b73565b602002602001018181525050876102400151815f81518110610e1f57610e1f615b73565b6020908102919091010152600160a08501525f60c08501528351610e42906129da565b60408501526020840151610e55906129da565b606085015260015b610e6960016024615b39565b8111610f0a57610e8185604001518660a001516125fb565b838281518110610e9357610e93615b73565b602002602001018181525050610edc8560c001516108d18b6101c00151600185610ebd91906155e0565b602a8110610ecd57610ecd615b73565b60200201518860a001516125fb565b60c086015260a08086015190880151610ef591906125fb565b60a0860152610f0381615b87565b9050610e5d565b505f5b6005811015610fe3575f610f22601e83615b39565b90505f610f3160016024615b39565b610f3b9084615b39565b9050610f6c858381518110610f5257610f52615b73565b60200260200101516108d189606001518a60a001516125fb565b858381518110610f7e57610f7e615b73565b602002602001018181525050610fbb8760c001516108d18d6101c0015184602a8110610fac57610fac615b73565b60200201518a60a001516125fb565b60c088015260a080880151908a0151610fd491906125fb565b60a08801525050600101610f0d565b50876020015181600181518110610ffc57610ffc615b73565b602002602001018190525086606001518160028151811061101f5761101f615b73565b602002602001018190525086608001518160038151811061104257611042615b73565b60200260200101819052508660a001518160048151811061106557611065615b73565b60200260200101819052508660c001518160058151811061108857611088615b73565b60200260200101819052508660e00151816006815181106110ab576110ab615b73565b6020026020010181905250866101000151816007815181106110cf576110cf615b73565b6020026020010181905250866101200151816008815181106110f3576110f3615b73565b60200260200101819052508661014001518160098151811061111757611117615b73565b602002602001018190525086610160015181600a8151811061113b5761113b615b73565b6020026020010181905250866101c0015181600b8151811061115f5761115f615b73565b602002602001018190525086610180015181600c8151811061118357611183615b73565b6020026020010181905250866101a0015181600d815181106111a7576111a7615b73565b6020026020010181905250866101e0015181600e815181106111cb576111cb615b73565b602002602001018190525086610200015181600f815181106111ef576111ef615b73565b60200260200101819052508661022001518160108151811061121357611213615b73565b60200260200101819052508661024001518160118151811061123757611237615b73565b60200260200101819052508661026001518160128151811061125b5761125b615b73565b60200260200101819052508661028001518160138151811061127f5761127f615b73565b6020026020010181905250866102a00151816014815181106112a3576112a3615b73565b6020026020010181905250866102c00151816015815181106112c7576112c7615b73565b6020026020010181905250866102e00151816016815181106112eb576112eb615b73565b60200260200101819052508661030001518160178151811061130f5761130f615b73565b60200260200101819052508661032001518160188151811061133357611333615b73565b60200260200101819052508661034001518160198151811061135757611357615b73565b602002602001018190525086610360015181601a8151811061137b5761137b615b73565b602002602001018190525086610380015181601b8151811061139f5761139f615b73565b6020026020010181905250866103a0015181601c815181106113c3576113c3615b73565b6020026020010181905250866103c0015181601d815181106113e7576113e7615b73565b6020026020010181905250876040015181601e8151811061140a5761140a615b73565b6020026020010181905250876060015181601f8151811061142d5761142d615b73565b602002602001018190525087608001518160208151811061145057611450615b73565b60200260200101819052508760a001518160218151811061147357611473615b73565b60200260200101819052508761012001518160228151811061149757611497615b73565b6020026020010181905250876101000151816023815181106114bb576114bb615b73565b60200260200101819052508760c00151816024815181106114de576114de615b73565b60200260200101819052508760e001518160258151811061150157611501615b73565b60200260200101819052505f61154787608001518660c001518b6102000151877f00000000000000000000000000000000000000000000000000000000000000006129ec565b9050611571815f8151811061155e5761155e615b73565b60200260200101518661012001516125fb565b608086018190526102008a01515160e08901516115a292916108d19161159791906125fb565b8861014001516125fb565b608086015260e08701516115b590612b4a565b60a08601525f6115c760016024615b39565b6115d2906001615b39565b90505f5b61160160017f00000000000000000000000000000000000000000000000000000000000000006155e0565b811015611812575f61163460017f00000000000000000000000000000000000000000000000000000000000000006155e0565b8210159050806117a357611668610d568b6101000151898560016116589190615b39565b81518110610d4957610d49615b73565b6101208901526101008a015161169890610d569089611688866001615b39565b81518110610d8357610d83615b73565b61014089015260a08801516101208901516116b391906125fb565b61016089015260a088015160e08b01516116db916116d0916125fb565b8961014001516125fb565b61018089018190526116fe906116f0906129da565b6108d18a61016001516129da565b866117098486615b39565b8151811061171957611719615b73565b6020026020010181815250505f6117528961018001518e61020001518560016117429190615b39565b601c8110610bce57610bce615b73565b905061178c816108d18b61016001518887600161176f9190615b39565b8151811061177f5761177f615b73565b60200260200101516125fb565b905061179c896080015182612616565b60808a0152505b6117c26117b88960a001518c60e001516125fb565b8b60e001516125fb565b60a08901526101e08c015182601b81106117de576117de615b73565b6020020151856117ee8486615b39565b815181106117fe576117fe615b73565b6020908102919091010152506001016115d6565b5061183e60017f00000000000000000000000000000000000000000000000000000000000000006155e0565b6118489082615b39565b90506118686118608961010001518a60c0015161262f565b60019061266c565b60e08701515261010088015160c08901516118ac91611860916108ef907f07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76906125fb565b60e0878101805160200192909252815180516040909101529051805160609091015260a0870151908901516118ee916118e4916125fb565b8960e001516125fb565b60a08701525f5b600481101561199c575f6119278860e00151836004811061191857611918615b73565b60200201518960a001516125fb565b9050611932816129da565b886101000151836004811061194957611949615b73565b602002015260a088015160e08b015161196291906125fb565b8860a001818152505061198e88608001516108d1838f61022001518660048110610bce57610bce615b73565b6080890152506001016118f5565b506101008601515184518590839081106119b8576119b8615b73565b602090810291909101810191909152610100870151908101516119dc916002610a7f565b846119e8836001615b39565b815181106119f8576119f8615b73565b60209081029190910101526101008601516060015184611a19836002615b39565b81518110611a2957611a29615b73565b60209081029190910101525f5b6003811015611a8b578a61014001518160038110611a5657611a56615b73565b60200201518483611a6681615b87565b945081518110611a7857611a78615b73565b6020908102919091010152600101611a36565b506040518060400160405280600181526020016002815250838281518110611ab557611ab5615b73565b60200260200101819052508560800151848280611ad190615b87565b935081518110611ae357611ae3615b73565b602002602001018181525050611b0d8a61022001518960c001518a608001518d6101a00151612b55565b611b2a5760405163a2a2ac8360e01b815260040160405180910390fd5b5f8a6102600151905080848381518110611b4657611b46615b73565b6020026020010181905250886101000151858381518110611b6957611b69615b73565b602002602001018181525050611b7d615338565b611b878587612eb8565b8152611b9282612fb0565b602082018190528c5182515f92611baa929190612ff6565b90505f5f611bba8f5f0151613087565b91509150611bc782613148565b611bd081613148565b8351611bdd9083856131e7565b84526020840151611bef9082856131e7565b602085018190528451611c0191613216565b9f9e505050505050505050505050505050565b611c1c615024565b50604080516103e081018252620200008152601160208083019190915261024382840152825180840184527f3046224394ac0664c5df0bbff473db8485b523993c3378b647785875fbda56e581527f075b8449cf262130cd0d53747c76da002a74053958060ee7c8e930b7f729af4e818301526060830152825180840184527f23f894967a8d0ad87a02976c74accf68659348a280e6c1ab3f99dad7b8cf4bc481527f21acfe5f2f788aee6ca1b87420abfb1b567605edbfc9de6cbff67aa99eb633b8818301526080830152825180840184527f2d8eb0ef0769af9b60944c9c1f9aef88f4458599d8d53d5700e57c463d3d248181527f067d884aca949978f4410a65ef7addb04f9e8a13536c428fbe9d71beb988a3218183015260a0830152825180840184527f04f478772f21c19f23074f73f4cafc722ade3b71fd3e674504df4d488e70844f81527f256ede28a442453cee32d59a1ae23e10ad9765b8cd5aa917f613437e401d03df8183015260c0830152825180840184527f296c0719e6414ccc762949a1eb297b40c46b886179cf2ba5d3660b218aa9a72881527f219d51db4d209022eaa607152a2ddc23b94fb99be6ed4b2f158ed46b21faceb88183015260e0830152825180840184527f0cf55eebb61a0e90c6f9fd68c4839012c6433b88fafff66701ab99c6975e6da381527f1f44b9a9447265fad2e8c326a3cfd26d434ed9eece6c2d5b5586f8232572d76581830152610100830152825180840184527f11beb2731ab8cf46c0a52666bb457fa74d4b635043bb5689a35a2aee24990b8081527f2f1fba989036c44aedb609d0d3341ccb842c2d1cd7c80cea4a95d0eb4530577081830152610120830152825180840184527f2d70068023998c277c75458b7b6eb1fe3c479eb044dec7aa3384d7b5049542c781527f10af4b4ffb3c03f9c906e4dd1cf3c21dc74139881dfa7ad73c25370424f3978181830152610140830152825180840184527f0d07902b3266cd4af955bac93e3579b0bc1bcd754c7b70035cc95877d641376c81527f195d808eeb35965b8df7ea80461dcc033e5d8d0ef25b028a1d429e52b33f3f0d81830152610160830152825180840184527f24ec1ac19b9f052e9e2c4270db9b317502859b163514073401512c675318823881527f28869fd3c9b2ad1ce8a966e9225bed7eac600c656be6891317404dc44726736781830152610180830152825180840184527f201e0e19e72484d21e8136546fe6aaeea2d5b6413019276eada6638ffeb2ce8a81527eba5238e406d8d46f68dc3b00a201ad10d2dc97beed32f5e83d051e742b2642818301526101a0830152825180840184527f2656809e98f3dee7945c4f48c6fff3c0833412aeeae84dada472462112fa8fe981527f2454353d981df4ae6c3fd37c4a7b5114bcb1416f43efc627d55e128449c7c14d818301526101c0830152825180840184527f1fb5e9c3ae41ba815444512a4e882619ac69f0cbb639657adeadb93ed50895b181527f201d42c30be1a478f6c6a28159a335c02fa3dc3e6c6843089c648bfd4dc8c250818301526101e0830152825180840184527f1375207e60c7d651a62aed70d2496e2324a7dc718de86e5887f805f907d4964081527f22a964f903d1faa46e20c6f8902415d61985c86bbeef23a01b6ca1718dfe80c581830152610200830152825180840184527f2a38162537d04323884347c1f31f76b03a04ec2f6f9c4b125d5d322932ff65ba81527f2465da5aac067658f2f98944a8ba1bc9fd81c2220d22f07995746a7b7deccd2981830152610220830152825180840184527f2a12695e6fa7a23c3519eee6d96433bb00d51cd274de90288914e3fb336334b681527f235e728947aff729c4ade5a63ca501273a958075ae1281bb612012f03219601681830152610240830152825180840184527f09e5efa8f84174e74fe0429a76ec404c053a4f8c3e5524d68e3cd4e338a2b4b881527f057873d3cf1d8589310604f6f203386d9a9928d980432f5316522d84e6eecb6181830152610260830152825180840184527f096a6fb164d4a53c869c12d0369ef747bbbd3f40e5dd997c01e32495b64b07cb81527f01198ac63b80d1d160ab5882da3dfc3a8864b70b72c31790e55fec5014c4436281830152610280830152825180840184527f085cbd5bb5a6a7a0407ad624e78b82be37c9c90a9c5de9302004bc406bc1778d81527f2a4cbd271054bf508c73ffeab6e321bba811248126e552f55701993aed192e4b818301526102a0830152825180840184527f1a89bfbff2ee55715a55cf0de0d6a1c62183bbaae9d200a5247b58f170cbbc7c81527f0ae77530f6cad79672428f376996a91ce1358fea5f49180218f18a7918f80bb3818301526102c0830152825180840184527f1faa95912a219c90e3cdbe1e78b25d6f79d4ca8ef1cd9fd3a8a6b87f94d138f281527f22a4b4a860838e96fcc07839e0c9742c2dbe4aa634664da1e24570a1dc4156fd818301526102e0830152825180840184527f1d11efcd01d1170fc229d305654298122dd323cc9ae7e16ad2f0bfa56a0f9e2e81527f225cd914fa8180a343e93ee8a0e87e2d850b8ca6e03267e5281f7ace3dd82ab581830152610300830152825180840184527f099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d2681527e15b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f81830152610320830152825180840184527f1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e81527f305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d1981830152610340830152825180840184527f061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d81527f1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e81830152610360830152825180840184527f043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce81527f261522c4089330646aff96736194949330952ae74c573d1686d9cb4a007338548183015261038083015282518084018452600181526002818301526103a083015282518084019093527f1af84073758283d4c848fed9bc6f055831baf81a643050fceafadf8da1791eec83527f1be165956a9c6a29edc2a4d21793a6fb1f00a6967720f870ebb2f2c6b267e9dd908301526103c081019190915290565b5f6103f461255c8385615b9f565b612657565b612569615320565b60408051808201909152805f516020615c6e5f395f51905f5261258f60205f8789615b4c565b61259891615b9f565b6125a29190615bbc565b81526020908101905f516020615c6e5f395f51905f52906125c7906040908789615b4c565b6125d091615b9f565b6125da9190615bbc565b90529392505050565b5f5f516020615c8e5f395f51905f52825b0692915050565b5f5f516020615c8e5f395f51905f5282840990505b92915050565b5f5f516020615c8e5f395f51905f528284089392505050565b5f5f516020615c8e5f395f51905f52825f516020615c8e5f395f51905f520384089392505050565b5f5f516020615c8e5f395f51905f52826125f4565b5f6103f4836108cc8461296a565b5f5f604051806101200160405280619d8081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec5181526020016105a081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31815260200161024081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd3181526020016105a081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec518152602001619d8081525090505f600190505f5f90505b600981101561277357612769826108cc878461262f565b9150600101612752565b5061277c61534b565b5f5b60098110156127cd576127ae610d5685836009811061279f5761279f615b73565b60200201516108cc898561262f565b8282600981106127c0576127c0615b73565b602002015260010161277e565b505f5b600981101561281357612809856108d18984600981106127f2576127f2615b73565b6020020151858560098110610bce57610bce615b73565b94506001016127d0565b5061281e84836125fb565b9695505050505050565b5f61283161536a565b61283c86828561340a565b612848868683866135b6565b612854868683866137a2565b61285f8682856139c7565b61286a868285613bbb565b61287686868386613f04565b6128818682856143b0565b61288c8682856147c2565b612897868285614b83565b61281e8185614e83565b60605f826001600160401b038111156128bc576128bc615940565b6040519080825280602002602001820160405280156128e5578160200160208202803683370190505b50905083815f815181106128fb576128fb615b73565b602090810291909101015260015b838110156129625761293d826129206001846155e0565b8151811061293057612930615b73565b6020026020010151612b4a565b82828151811061294f5761294f615b73565b6020908102919091010152600101612909565b509392505050565b5f5f8290505f604051602081526020808201526020604082015282606082015260025f516020615c8e5f395f51905f520360808201525f516020615c8e5f395f51905f5260a082015260205f60c08360055afa806129c6575f5ffd5b505f51608091909101604052949350505050565b5f516020615c8e5f395f51905f520390565b60605f826001600160401b03811115612a0757612a07615940565b604051908082528060200260200182016040528015612a30578160200160208202803683370190505b509050825b8015612b3f575f85612a486001846155e0565b81518110612a5857612a58615b73565b602002602001015190505f89600184612a7191906155e0565b601c8110612a8157612a81615b73565b602002015190505f612adc612aa0612a99858d6125fb565b60026125fb565b6108ef8b612aaf6001896155e0565b601c8110612abf57612abf615b73565b60200201516108cc612ad6886108cc60018a61262f565b8761262f565b9050612afd816108cc610d56612af7876108cc60018961262f565b86612616565b99508990508085612b0f6001876155e0565b81518110612b1f57612b1f615b73565b60200260200101818152505050505080612b3890615bdb565b9050612a35565b509695505050505050565b5f61261082836125fb565b5f600181612b6e612b6887610100614edc565b8361262f565b905080612b8e5760405163835eb8f760e01b815260040160405180910390fd5b612b96615389565b80518390525f5b7f0000000000000000000000000000000000000000000000000000000000000000811015612c7f575f612bd18260096155c9565b612bdc906001615b39565b905084835f0151826101008110612bf557612bf5615b73565b60200201525f612c06826001615b39565b90505b612c14600983615b39565b811015612c75578351612c5490612c2c6001846155e0565b6101008110612c3d57612c3d615b73565b60200201518a85601c8110610bce57610bce615b73565b8451826101008110612c6857612c68615b73565b6020020152600101612c09565b5050600101612b9d565b50608081018390525f602082018190525b610100811015612d9057612cb1612cab83608001518a6125fb565b8561262f565b8260a00151826101008110612cc857612cc8615b73565b602002015260a0820151612cf290826101008110612ce857612ce8615b73565b602002015161296a565b8260a00151826101008110612d0957612d09615b73565b602002018181525050612d4f82602001516108d1845f0151846101008110612d3357612d33615b73565b60200201518560a00151856101008110610bce57610bce615b73565b60208301526080820151612d83907f204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d66125fb565b6080830152600101612c90565b505f612da1836108cc61010061296a565b9050612db18260200151826125fb565b602083015260a0820151612dcc905f5b6020020151826125fb565b604083015260a0820151612df890612de760016101006155e0565b6101008110612dc157612dc1615b73565b60608301526040820151612e0e908a6002610bce565b60c08301819052612e6e906108d1612e468b7f204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d661262f565b60208d015160408e01516108cc91612e5d9161262f565b8e5160208901516108ef91906125fb565b60c083018190526060830151612ea391612e97916108d1906108cc8e600260200201518c61262f565b6108ef858c6003610bce565b60c08301819052159998505050505050505050565b612ec0615320565b7f00000000000000000000000000000000000000000000000000000000000000005f5b81811015612f1557612f0d858281518110612f0057612f00615b73565b6020026020010151613148565b600101612ee3565b50604051600190815b60018401811015612f7a5760208102870160208202870181515160408501528151602001516060850152805160808501525050604080830160606040850160075afa8316925060408260808460065afa90921691600101612f1e565b5080518452602081015160208501525080612fa8576040516352ec174560e11b815260040160405180910390fd5b505092915050565b612fb8615320565b5f516020615c6e5f395f51905f5282602001515f516020615c6e5f395f51905f52612fe391906155e0565b612fed9190615bbc565b60208301525090565b5f5f5f61300286613087565b9150915061300e6153cd565b82518152602080840151818301528251604080840191909152838201516060840152875160808401528782015160a0840152865160c08401528682015160e08401525161307c9161306191849101615bf0565b60405160208183030381529060405280519060200120612657565b979650505050505050565b61308f615320565b613097615320565b82516020808501516040860151606087015160cc90811b608892831b604494851b90961795909517949094178652608087015160a088015160c089015160e08a0151871b90841b91851b9092171717868401526101008701516101208801516101408901516101608a0151871b90841b91851b909217171785526101808701516101a08801516101c08901516101e09099015190951b9790911b9390911b1791909117939093179281019290925291565b805160208201515f5f516020615c6e5f395f51905f528380095f516020615c6e5f395f51905f5260035f516020615c6e5f395f51905f52838709085f516020615c6e5f395f51905f5284850914915050806131e15760405162461bcd60e51b8152602060048201526019602482015278706f696e74206973206e6f74206f6e2074686520637572766560381b60448201526064016100e5565b50505050565b6131ef615320565b6131f7615320565b6132018386614f3d565b905061320d8185614f93565b95945050505050565b81516020808401518351848301516040805194850195909552938301919091527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260608301527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed60808301527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b60a08301527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60c083015260e08201526101008101919091527f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c16101208201527f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b06101408201527f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe46101608201527f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e556101808201525f9081906101a00160405160208183030381529060405290505f5f60086001600160a01b0316836040516133ac9190615c24565b5f60405180830381855afa9150503d805f81146133e4576040519150601f19603f3d011682016040523d82523d5f602084013e6133e9565b606091505b509150915081801561281e57508080602001905181019061281e9190615c3a565b5f613416846007614ff4565b90507f183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f80000005f61347761347161344b85600361262f565b6108cc61346661345b8b5f614ff4565b6108cc8c601d614ff4565b6108cc8b601c614ff4565b836125fb565b90506134f86134ed6134d46134bb6134a2856108d16134978d6002614ff4565b6108cc8e601c614ff4565b6108d16134b08c6003614ff4565b6108cc8d601d614ff4565b6108d16134c98b6004614ff4565b6108cc8c601e614ff4565b6108d16134e28a6005614ff4565b6108cc8b601f614ff4565b6108d1886001614ff4565b9050613517816108d161350c86600161262f565b6108cc8a6027614ff4565b905061352381846125fb565b905061352f81856125fb565b8552505f905061356961355f61355461354988601c614ff4565b6108d189601f614ff4565b6108ef886024614ff4565b6108d1875f614ff4565b905061357a816108cc84600261262f565b905061358b816108cc84600161262f565b905061359781836125fb565b90506135a381846125fb565b9050808460015b60200201525050505050565b5f5f5f6135ec6135e26135ca89601c614ff4565b6108d16135d88b6012614ff4565b8a606001516125fb565b8760800151612616565b9050613625816108cc61361b6136038b601d614ff4565b6108d16136118d6013614ff4565b8c606001516125fb565b8960800151612616565b905061364a816108cc61361b61363c8b601e614ff4565b6108d16136118d6014614ff4565b905061366f816108cc61361b6136618b601f614ff4565b6108d16136118d6015614ff4565b92505f90506136936135e261368589601c614ff4565b6108d16135d88b600e614ff4565b90506136b8816108cc61361b6136aa8b601d614ff4565b6108d16136118d600f614ff4565b90506136dd816108cc61361b6136cf8b601e614ff4565b6108d16136118d6010614ff4565b9050613702816108cc61361b6136f48b601f614ff4565b6108d16136118d6011614ff4565b91505f9050613729613723613718896020614ff4565b6108d18a601a614ff4565b846125fb565b905061375e816108ef6137586137408b6028614ff4565b6108d161374e8d601b614ff4565b8c60a001516125fb565b856125fb565b905061376a81856125fb565b6040860152505f61378d61375861378289601b614ff4565b6108cc8a6028614ff4565b9050808560035b602002015250505050505050565b5f5f6138006137e86137d06137bb61361b8a6016614ff4565b6108d16137c98b6017614ff4565b8a516125fb565b6108d16137de8a6018614ff4565b89602001516125fb565b6108d16137f6896019614ff4565b88604001516125fb565b91505f61383761381e61381489601c614ff4565b8860800151612616565b6108d161382c8a6003614ff4565b6108cc8b6024614ff4565b90505f61386061384889601d614ff4565b6108d16138558b5f614ff4565b6108cc8c6025614ff4565b90505f61388a6138718a601e614ff4565b6108d161387f8c6001614ff4565b6108cc8d6026614ff4565b90506138c96138b16138a3856108d1868d5f01516125fb565b6108d1848c602001516125fb565b6108d16138bf8c6004614ff4565b8b604001516125fb565b93505050505f6138dd613723886021614ff4565b90505f6138ee613723896021614ff4565b90505f61392661390d6139028b6023614ff4565b6108d18c6006614ff4565b6108ef61391b8c6023614ff4565b6108cc8d6006614ff4565b90505f613944612b6861393987896125fb565b6108cc8d6021614ff4565b905061395081886125fb565b90505f61397861396a6139648d6006614ff4565b876125fb565b6108ef6139648e6022614ff4565b90505f6139868c6023614ff4565b90505f613996612b6883846125fb565b60808c0185905260a08c0184905290506139b0818b6125fb565b8b6006602002015250505050505050505050505050565b5f6139d35f600161262f565b90505f6139e15f600261262f565b90505f6139ef5f600361262f565b90505f613a0b613a0088601d614ff4565b6108ef89601c614ff4565b90505f613a27613a1c89601e614ff4565b6108ef8a601d614ff4565b90505f613a43613a388a601f614ff4565b6108ef8b601e614ff4565b90505f613a5f613a548b6024614ff4565b6108ef8c601f614ff4565b905083613a70816108cc818b612616565b9050613a80816108cc878a612616565b9050613a90816108cc8789612616565b9050613aa1816108cc8d6008614ff4565b9050613aad818a6125fb565b60e08b01525082613ac2816108cc818b612616565b9050613ad2816108cc868a612616565b9050613ae2816108cc8689612616565b9050613af3816108cc8d6008614ff4565b9050613aff818a6125fb565b6101008b01525081613b15816108cc818b612616565b9050613b25816108cc858a612616565b9050613b35816108cc8589612616565b9050613b46816108cc8d6008614ff4565b9050613b52818a6125fb565b6101208b01525080613b68816108cc818b612616565b9050613b78816108cc848a612616565b9050613b88816108cc8489612616565b9050613b99816108cc8d6008614ff4565b9050613ba5818a6125fb565b610140909a019990995250505050505050505050565b613bf46040518060e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b613bff84601d614ff4565b8152613c0c84601e614ff4565b6020820152613c1c846024614ff4565b6040820152613c2c846027614ff4565b6060820152613c3c846026614ff4565b6080820152613c4c846025614ff4565b60a08201525f613c5d856002614ff4565b90505f613c6a865f614ff4565b90505f613c7e8460400151855f015161262f565b90505f613c93856020015186602001516125fb565b606086015190915086905f90613ca990806125fb565b90505f613cc7613cc189602001518a606001516125fb565b886125fb565b90505f613ce6613cdf8a60a001518b60400151612616565b8a51612616565b9050613cf561396482886125fb565b9050613d1c613d16613d10613d0a848761262f565b8861262f565b84612616565b83612616565b9050613d44613d39613d2e83876125fb565b6108cc8f6009614ff4565b6108cc60018a61262f565b6101608c015250505050602085015160808601515f91613d6391612616565b90505f613d81613d778860600151886125fb565b886020015161262f565b90505f613da5613d9184876125fb565b6108d16137588b60a001518c5f015161262f565b9050613dcd613dc2613db7838c6125fb565b6108cc8e6009614ff4565b6108cc60018961262f565b6101808b0152505f9150613dee9050613de7836011612616565b87516125fb565b90505f613dfb8384612616565b9050613e078182612616565b90505f613e158360096125fb565b9050613e3e613e38613723613e318b60a001518c5f0151612616565b8b51612616565b8261262f565b60c089018190525f90613e5990613cc190613d2e908d6125fb565b9050613e6c8b600b602002015182612616565b6101608c0152505086515f9250613e939150613de790613e8c9080612616565b8851612616565b90505f613ed3613eae836108cc8a5f01518b60a0015161262f565b60208901516108ef90613ec19080612616565b6108cc8b602001518c60800151612616565b9050613ef089600c60200201516108d1613cc1613d2e858d6125fb565b89600c602002015250505050505050505050565b613f6e604051806101e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b613f86613f7c86601e614ff4565b85604001516125fb565b808252613fa5906108d1613f9b88601d614ff4565b87602001516125fb565b808252613fba906108d1613de788601c614ff4565b808252613fcc906108d1876001614ff4565b80825260208201819052613fe5906108ef87601f614ff4565b8152614000613ff5866024614ff4565b6108ef87601c614ff4565b608082015261401e614013866027614ff4565b6108ef87601f614ff4565b60608201526080810151614037906108cc81600161262f565b6101c082015260808101516140779061406d90614066906108cc60015f516020615c8e5f395f51905f526155e0565b6001612616565b82606001516125fb565b60a082018190526140ad9061409f906108cc614094896002614ff4565b6108cc8a6003614ff4565b6108cc61375888600a614ff4565b83600e60200201526101c08101516140d19061409f906108cc614094896002614ff4565b6101e084015280516140f6906108cc6140eb886002614ff4565b6108cc896003614ff4565b6101208201525f61411561410b87601f614ff4565b836020015161262f565b9050614126816108cc83600161262f565b60e0830152614143614139876026614ff4565b86604001516125fb565b60408301819052614166906108d161415c896025614ff4565b88602001516125fb565b60408301819052614186906108d161417f896024614ff4565b88516125fb565b60408301526141a3614199876027614ff4565b836040015161262f565b60408301525f6141c26141b7886026614ff4565b6108ef89601e614ff4565b90506142146141ef613471614066866080015160015f516020615c8e5f395f51905f526108cc91906155e0565b6108cc614066866040015160015f516020615c8e5f395f51905f526108cc91906155e0565b60c084015260408301516142369061422c90806125fb565b846040015161262f565b61010084015260c083015161426290614254906108cc8a6004614ff4565b6108cc6139648a600a614ff4565b6102008601526101c083015161428190614254906108cc8a6004614ff4565b6102208601526101008301516142a090614254906108cc8a6004614ff4565b61024086015260e08301516142ba906108cc896004614ff4565b6101408401526142d96142ce886025614ff4565b6108ef89601d614ff4565b6101608401526080830151614317906141b79061430c90614066906108cc60015f516020615c8e5f395f51905f526155e0565b8561016001516125fb565b61018084018190526101208401516101a0850181905261434e916108d1906108cc6143438c6005614ff4565b6108cc8d6002614ff4565b6101a08401819052835161436e91906108d1906108cc6143438c5f614ff4565b6101a084018190526101408401516143869190612616565b6101a084018190526143a0906108cc6139648a600a614ff4565b6101a0840181905285600d613794565b6143e96040518060e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b61441e6144056143fa86601c614ff4565b6108cc876025614ff4565b6108d1614413876024614ff4565b6108cc88601d614ff4565b815261446361445861443f61443487601c614ff4565b6108cc88601f614ff4565b6108d161444d88601d614ff4565b6108cc89601e614ff4565b6108ef866026614ff4565b6040820181905261447890600160441b6125fb565b6040820181905261448e906108ef866027614ff4565b6040820181905281516144a19190612616565b604082018190526144b7906108cc866005614ff4565b604082015280516144cc90600160441b6125fb565b8082526144ec906108d16144e1876024614ff4565b6108cc886025614ff4565b80825260208201819052614513906108ef61450887601e614ff4565b6108d188601f614ff4565b60208201819052614529906108cc866004614ff4565b6020820152805160608201819052614546906108d186601f614ff4565b6060820181905261456a906108ef61455f876026614ff4565b6108d1886027614ff4565b6060820181905261457f906108cc865f614ff4565b8160600181815250505f6145a861459e83602001518460400151612616565b8360600151612616565b90506145b9816108cc876003614ff4565b90506145d16145c9866025614ff4565b6140006125fb565b608083018190526145e7906108d1876024614ff4565b608083018190526145fa906140006125fb565b60808301819052614610906108d187601e614ff4565b60808301819052614623906140006125fb565b60808301819052614639906108d187601d614ff4565b6080830181905261464c906140006125fb565b60808301819052614662906108d187601c614ff4565b60808301819052614678906108ef87601f614ff4565b6080830181905261468e906108cc876005614ff4565b60808301526146a16145c9866026614ff4565b60a083018190526146b7906108d1876025614ff4565b60a083018190526146ca906140006125fb565b60a083018190526146e0906108d1876024614ff4565b60a083018190526146f3906140006125fb565b60a08301819052614709906108d187601f614ff4565b60a0830181905261471c906140006125fb565b60a08301819052614732906108d187601e614ff4565b60a08301819052614748906108ef876027614ff4565b60a0830181905261475d906108cc875f614ff4565b60a0830181905260808301515f916147759190612616565b9050614786816108cc886004614ff4565b90506147928282612616565b60c084018190526147ab906108cc61396489600b614ff4565b60c084018190528560136020020152505050505050565b6148386040518061022001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b61485161484685601c614ff4565b6108d1866002614ff4565b815261486c61486185601d614ff4565b6108d1866003614ff4565b602082015261488a61487f85601e614ff4565b6108d1866004614ff4565b60408201526148a861489d85601f614ff4565b6108d1866005614ff4565b606082015280516148da906148d3906148cc906148c590806125fb565b84516125fb565b83516125fb565b82516125fb565b608082015260208101516149189061490e90614904906148fa90806125fb565b84602001516125fb565b83602001516125fb565b82602001516125fb565b60a082015260408101516149569061494c906149429061493890806125fb565b84604001516125fb565b83604001516125fb565b82604001516125fb565b60c0820152606081015161498a9061406d906149809061497690806125fb565b84606001516125fb565b83606001516125fb565b60e0820152608081015160a08201516149a39190612616565b61010082015260c081015160e08201516149bd9190612616565b61012082015260a08101516149e1906149d69080612616565b826101200151612616565b61014082015260e0810151614a05906149fa9080612616565b826101000151612616565b610160820152610120810151614a1b9080612616565b6101e08201819052614a3c90614a319080612616565b826101600151612616565b6101e0820152610100810151614a529080612616565b6101a08201819052614a7390614a689080612616565b826101400151612616565b6101a08201819052610160820151614a8a91612616565b6101808201526101408101516101e0820151614aa69190612616565b6101c0820152614aba61347185600c614ff4565b6102008201819052610280840151610180830151614ae5926108d1916108cc906108ef8a6024614ff4565b8360146020020152614b1583601560200201516108d18361020001516108cc856101a001516108ef8a6025614ff4565b8360156020020152614b4583601660200201516108d18361020001516108cc856101c001516108ef8a6026614ff4565b8360166020020152614b7583601760200201516108d18361020001516108cc856101e001516108ef8a6027614ff4565b836017602002015250505050565b614bd56040518061016001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b5f6040518060800160405280614c0a7f10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e76125e3565b8152602001614c387f0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b6125e3565b8152602001614c657e544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac156125e3565b8152602001614c937f222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b6125e3565b90529050614cb0614ca586601c614ff4565b6108d1876002614ff4565b6101208301819052614cef90614ce490614cd990614cce90806125fb565b8561012001516125fb565b8461012001516125fb565b8361012001516125fb565b8252614cfc85601d614ff4565b6020830152614d0c85601e614ff4565b6040830152614d1c85601f614ff4565b606083015281516020830151614d449161459e91614d3a9190612616565b8460400151612616565b6080830152614d5761372386600d614ff4565b6101408301528151614d7890614d6e90835f610bce565b8360800151612616565b60a0830152614da484601860200201516108d18461014001516108cc8660a001516108ef8b6024614ff4565b6103008501526020820151614dbf90614d6e90836001610bce565b60c0830152614deb84601960200201516108d18461014001516108cc8660c001516108ef8b6025614ff4565b6103208501526040820151614e0690614d6e90836002610bce565b60e0830152614e3284601a60200201516108d18461014001516108cc8660e001516108ef8b6026614ff4565b6103408501526060820151614e4d90614d6e90836003610bce565b610100830152614e7b84601b60200201516108d18461014001516108cc8661010001516108ef8b6027614ff4565b84601b6135aa565b815160015b601c811015614ed557614ecb826108d18684601c8110614eaa57614eaa615b73565b602002015186614ebb6001876155e0565b601b8110610bce57610bce615b73565b9150600101614e88565b5092915050565b5f5f8390505f60405160208152602080820152602060408201528260608201528460808201525f516020615c8e5f395f51905f5260a082015260205f60c08360055afa80614f28575f5ffd5b505f5160809190910160405295945050505050565b614f45615320565b614f4d615320565b604051835181526020840151602082015284604082015260408160608360075afa80614f77575f5ffd5b5080518252602080820151908301526060016040529392505050565b614f9b615320565b614fa3615320565b6040518451815260208501516020820152835160408201526020840151606082015260408160808360065afa80614fd8575f5ffd5b5080518252602080820151908301526080016040529392505050565b5f8282602881111561500857615008615c59565b6029811061501857615018615b73565b60200201519392505050565b604051806103e001604052805f81526020015f81526020015f815260200161504a615320565b8152602001615057615320565b8152602001615064615320565b8152602001615071615320565b815260200161507e615320565b815260200161508b615320565b8152602001615098615320565b81526020016150a5615320565b81526020016150b2615320565b81526020016150bf615320565b81526020016150cc615320565b81526020016150d9615320565b81526020016150e6615320565b81526020016150f3615320565b8152602001615100615320565b815260200161510d615320565b815260200161511a615320565b8152602001615127615320565b8152602001615134615320565b8152602001615141615320565b815260200161514e615320565b815260200161515b615320565b8152602001615168615320565b8152602001615175615320565b8152602001615182615320565b815260200161518f615320565b815260200161519c615320565b81526020016151a9615320565b905290565b6040518061028001604052806151c26153ec565b81526020016151cf615320565b81526020016151dc615320565b81526020016151e9615320565b81526020016151f6615320565b8152602001615203615320565b8152602001615210615320565b815260200161521d615320565b815260200161522a615320565b8152602001615237615320565b815260200161524461540b565b81526020015f8152602001615257615438565b81526020015f815260200161526a615466565b8152602001615277615485565b815260200161528461536a565b815260200161518f6154b3565b6040518061052001604052806029906020820280368337509192915050565b604051806101c001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020016152ee6154b3565b81526020016152fb6154b3565b81526020015f81526020015f81526020015f81526020015f8152602001606081525090565b60405180604001604052805f81526020015f81525090565b604051806040016040528061519c615320565b6040518061012001604052806009906020820280368337509192915050565b604051806103800160405280601c906020820280368337509192915050565b6040518060e0016040528061539c6154d1565b81526020015f81526020015f81526020015f81526020015f81526020016153c16154d1565b81526020015f81525090565b6040518061010001604052806008906020820280368337509192915050565b6040518061020001604052806010906020820280368337509192915050565b60405180606001604052806003905b615422615320565b81526020019060019003908161541a5790505090565b604051806103800160405280601c905b61545061534b565b8152602001906001900390816154485790505090565b604051806105400160405280602a906020820280368337509192915050565b604051806103600160405280601b905b61549d615320565b8152602001906001900390816154955790505090565b60405180608001604052806004906020820280368337509192915050565b604051806120000160405280610100906020820280368337509192915050565b5f5f5f5f60408587031215615504575f5ffd5b84356001600160401b03811115615519575f5ffd5b8501601f81018713615529575f5ffd5b80356001600160401b0381111561553e575f5ffd5b87602082840101111561554f575f5ffd5b6020918201955093508501356001600160401b0381111561556e575f5ffd5b8501601f8101871361557e575f5ffd5b80356001600160401b03811115615593575f5ffd5b8760208260051b84010111156155a7575f5ffd5b949793965060200194505050565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417612610576126106155b5565b81810381811115612610576126106155b5565b805f5b60108110156131e15781518452602093840193909101906001016155f6565b805f5b60038110156131e15761563684835180518252602090810151910152565b6040939093019260209190910190600101615618565b805f5b601c8110156131e1578151845f5b600981101561567c57825182526020928301929091019060010161565d565b50505061012093909301926020919091019060010161564f565b805f5b602a8110156131e1578151845260209384019390910190600101615699565b805f5b601b8110156131e1576156d984835180518252602090810151910152565b60409390930192602091909101906001016156bb565b805f5b601c8110156131e15781518452602093840193909101906001016156f2565b805f5b60048110156131e1578151845260209384019390910190600101615714565b8183525f6001600160fb1b0383111561574a575f5ffd5b8260051b80836020870137939093016020019392505050565b61576e8188516155f3565b5f602088015161578c61020084018280518252602090810151910152565b5060408801518051610240840152602090810151610260840152606089015180516102808501528101516102a0840152608089015180516102c08501528101516102e084015260a0890151805161030085015281015161032084015260c0890151805161034085015281015161036084015260e089015180516103808501528101516103a084015261010089015180516103c08501528101516103e084015261012089015180516104008501520151610420830152610140880151615855610440840182615615565b5061016088015161050083015261018088015161587661052084018261564c565b506101a08801516124a08301526101c08801516158976124c0840182615696565b506101e08801516158ac612a008401826156b8565b506102008801516158c16130c08401826156ef565b506102208801516158d6613440840182615711565b5061024088015180516134c08401526020908101516134e0840152610260890151805161350085015201516135208301526135c0613540830181905261591f9083018789615733565b613560830195909552506135808101929092526135a0909101529392505050565b634e487b7160e01b5f52604160045260245ffd5b60405161014081016001600160401b038111828210171561597757615977615940565b60405290565b604051601f8201601f191681016001600160401b03811182821017156159a5576159a5615940565b604052919050565b5f60c082840312156159bd575f5ffd5b60405160c081016001600160401b03811182821017156159df576159df615940565b604090815283518252602080850151908301528381015190820152606080840151908201526080808401519082015260a0928301519281019290925250919050565b5f82601f830112615a30575f5ffd5b5f610360615a3d8161597d565b915083018185821115615a4e575f5ffd5b845b82811015615a68578051825260209182019101615a50565b509195945050505050565b5f82601f830112615a82575f5ffd5b5f610380615a3d8161597d565b5f610be0828403128015615aa1575f5ffd5b50615aaa615954565b615ab484846159ad565b8152615ac38460c08501615a21565b6020820152615ad6846104208501615a73565b60408201526107a08301516060820152615af4846107c08501615a73565b6080820152610b4083015160a0820152610b6083015160c0820152610b8083015160e0820152610ba0830151610100820152610bc09092015161012083015250919050565b80820180821115612610576126106155b5565b5f5f85851115615b5a575f5ffd5b83861115615b66575f5ffd5b5050820193919092039150565b634e487b7160e01b5f52603260045260245ffd5b5f60018201615b9857615b986155b5565b5060010190565b80356020831015612610575f19602084900360031b1b1692915050565b5f82615bd657634e487b7160e01b5f52601260045260245ffd5b500690565b5f81615be957615be96155b5565b505f190190565b5f8183825b6008811015615c14578151835260209283019290910190600101615bf5565b5050506101008201905092915050565b5f82518060208501845e5f920191825250919050565b5f60208284031215615c4a575f5ffd5b815180151581146103f4575f5ffd5b634e487b7160e01b5f52602160045260245ffdfe30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4730644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", + "linkReferences": { + "project/contracts/verifier/ThresholdDecryptedSharesAggregationModVerifier.sol": { + "ZKTranscriptLib": [ + { + "length": 20, + "start": 690 + } + ] + } + }, + "deployedLinkReferences": { + "project/contracts/verifier/ThresholdDecryptedSharesAggregationModVerifier.sol": { + "ZKTranscriptLib": [ + { + "length": 20, + "start": 360 + } + ] + } + }, + "immutableReferences": { + "55298": [ + { + "length": 32, + "start": 91 + }, + { + "length": 32, + "start": 148 + }, + { + "length": 32, + "start": 257 + }, + { + "length": 32, + "start": 466 + }, + { + "length": 32, + "start": 2605 + }, + { + "length": 32, + "start": 2958 + }, + { + "length": 32, + "start": 3115 + }, + { + "length": 32, + "start": 5411 + }, + { + "length": 32, + "start": 5597 + }, + { + "length": 32, + "start": 5648 + }, + { + "length": 32, + "start": 6170 + }, + { + "length": 32, + "start": 11167 + } + ], + "55300": [ + { + "length": 32, + "start": 398 + } + ], + "55302": [ + { + "length": 32, + "start": 432 + }, + { + "length": 32, + "start": 2303 + } + ], + "55304": [ + { + "length": 32, + "start": 3156 + }, + { + "length": 32, + "start": 3257 + }, + { + "length": 32, + "start": 11970 + } + ] + }, + "inputSourceName": "project/contracts/verifier/ThresholdDecryptedSharesAggregationModVerifier.sol", + "buildInfoId": "solc-0_8_28-bd46eae6f77be9a8538850385226c1ee4fc57e42" +} \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/verifier/ThresholdDecryptedSharesAggregationModVerifier.sol/ZKTranscriptLib.json b/packages/enclave-contracts/artifacts/contracts/verifier/ThresholdDecryptedSharesAggregationModVerifier.sol/ZKTranscriptLib.json new file mode 100644 index 0000000000..d222bdae21 --- /dev/null +++ b/packages/enclave-contracts/artifacts/contracts/verifier/ThresholdDecryptedSharesAggregationModVerifier.sol/ZKTranscriptLib.json @@ -0,0 +1,395 @@ +{ + "_format": "hh3-artifact-1", + "contractName": "ZKTranscriptLib", + "sourceName": "contracts/verifier/ThresholdDecryptedSharesAggregationModVerifier.sol", + "abi": [ + { + "inputs": [ + { + "components": [ + { + "internalType": "Fr[16]", + "name": "pairingPointObject", + "type": "uint256[16]" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "geminiMaskingPoly", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "w1", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "w2", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "w3", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "w4", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "lookupReadCounts", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "lookupReadTags", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "lookupInverses", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "zPerm", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point[3]", + "name": "libraCommitments", + "type": "tuple[3]" + }, + { + "internalType": "Fr", + "name": "libraSum", + "type": "uint256" + }, + { + "internalType": "Fr[9][28]", + "name": "sumcheckUnivariates", + "type": "uint256[9][28]" + }, + { + "internalType": "Fr", + "name": "libraEvaluation", + "type": "uint256" + }, + { + "internalType": "Fr[42]", + "name": "sumcheckEvaluations", + "type": "uint256[42]" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point[27]", + "name": "geminiFoldComms", + "type": "tuple[27]" + }, + { + "internalType": "Fr[28]", + "name": "geminiAEvaluations", + "type": "uint256[28]" + }, + { + "internalType": "Fr[4]", + "name": "libraPolyEvals", + "type": "uint256[4]" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "shplonkQ", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct Honk.G1Point", + "name": "kzgQuotient", + "type": "tuple" + } + ], + "internalType": "struct Honk.ZKProof", + "name": "proof", + "type": "tuple" + }, + { + "internalType": "bytes32[]", + "name": "publicInputs", + "type": "bytes32[]" + }, + { + "internalType": "uint256", + "name": "vkHash", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "publicInputsSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "logN", + "type": "uint256" + } + ], + "name": "generateTranscript", + "outputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "Fr", + "name": "eta", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "etaTwo", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "etaThree", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "beta", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "gamma", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "publicInputsDelta", + "type": "uint256" + } + ], + "internalType": "struct Honk.RelationParameters", + "name": "relationParameters", + "type": "tuple" + }, + { + "internalType": "Fr[27]", + "name": "alphas", + "type": "uint256[27]" + }, + { + "internalType": "Fr[28]", + "name": "gateChallenges", + "type": "uint256[28]" + }, + { + "internalType": "Fr", + "name": "libraChallenge", + "type": "uint256" + }, + { + "internalType": "Fr[28]", + "name": "sumCheckUChallenges", + "type": "uint256[28]" + }, + { + "internalType": "Fr", + "name": "rho", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "geminiR", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "shplonkNu", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "shplonkZ", + "type": "uint256" + }, + { + "internalType": "Fr", + "name": "publicInputsDelta", + "type": "uint256" + } + ], + "internalType": "struct ZKTranscript", + "name": "t", + "type": "tuple" + } + ], + "stateMutability": "pure", + "type": "function" + } + ], + "bytecode": "0x6116f3610034600b8282823980515f1a607314602857634e487b7160e01b5f525f60045260245ffd5b305f52607381538281f3fe7300000000000000000000000000000000000000003014608060405260043610610034575f3560e01c8063995bf45714610038575b5f5ffd5b61004b610046366004611214565b610061565b604051610058919061144a565b60405180910390f35b610069610d76565b5f610078888888888886610128565b9083529050610087818961017c565b6020840191909152905061009b818461026c565b604084019190915290506100af8189610315565b606084019190915290506100c4888285610376565b608084019190915290506100d88882610445565b60a084019190915290506100ed8882856105cb565b60c0840191909152905061010288828561073d565b60e0840191909152905061011688826108bf565b50610100830152509695505050505050565b610130610ddb565b5f61013e88888888886108fc565b60408601919091526020850191909152908352925061015d8389610c6a565b6080850191909152606084019190915291989197509095505050505050565b610184610e0b565b5f61018d610e2a565b8481526101008401805151602080840191909152905181015160408084019190915261012086018051516060850152518201516080840152516101f1916101d69184910161151f565b60405160208183030381529060405280519060200120610cf2565b91505f6101fd83610d18565b50808552905060015b6102126001601c611566565b8110156102625761024385610228600184611566565b601b81106102385761023861150b565b602002015183610d48565b8582601b81106102555761025561150b565b6020020152600101610206565b5050509250929050565b610274610e48565b5f61028b846040516020016101d691815260200190565b935061029684610d18565b50825260015b8381101561030d576102ee836102b3600184611566565b601c81106102c3576102c361150b565b6020020151846102d4600185611566565b601c81106102e4576102e461150b565b6020020151610d48565b8382601c81106103005761030061150b565b602002015260010161029c565b509093915050565b5f5f61031f610e67565b84815261014084018051515160208084019190915290515181015160408084019190915261016086015160608401525161035f916101d691849101611579565b915061036a82610d18565b50959194509092505050565b61037e610e48565b5f805b8381101561043b57610391610e85565b8581525f5b60098110156103fa5787610180015183601c81106103b6576103b661150b565b602002015181600981106103cc576103cc61150b565b6020020151826103dd8360016115ac565b600a81106103ed576103ed61150b565b6020020152600101610396565b5061040f816040516020016101d691906115bf565b955061041a86610d18565b508483601c811061042d5761042d61150b565b602002015250600101610381565b5090949293505050565b5f5f61044f610ea4565b83815260015b610461600160296115ac565b81116104b6576101c0860151610478600183611566565b602a81106104885761048861150b565b602002015182826030811061049f5761049f61150b565b6020020152806104ae816115f3565b915050610455565b856101a001518282603081106104ce576104ce61150b565b60200201526104de6001826115ac565b61014087015160200151519091508282603081106104fe576104fe61150b565b602002015261014086015160016020020151602001518282600161052291906115ac565b603081106105325761053261150b565b60200201526105426002826115ac565b61014087015160400151519091508282603081106105625761056261150b565b602002015261014086015160026020020151602001518282600161058691906115ac565b603081106105965761059661150b565b6020020181815250506105b3826040516020016101d6919061160b565b92506105be83610d18565b5096929550919350505050565b5f80806105d9600185611566565b6105e490600261163f565b6105ef9060016115ac565b6001600160401b0381111561060657610606610eff565b60405190808252806020026020018201604052801561062f578160200160208202803683370190505b50905084815f815181106106455761064561150b565b60209081029190910101525f5b61065d600186611566565b81101561071057866101e0015181601b811061067b5761067b61150b565b6020020151518261068d83600261163f565b6106989060016115ac565b815181106106a8576106a861150b565b602002602001018181525050866101e0015181601b81106106cb576106cb61150b565b602002015160200151828260026106e2919061163f565b6106ed9060026115ac565b815181106106fd576106fd61150b565b6020908102919091010152600101610652565b50610725816040516020016101d69190611656565b915061073082610d18565b5096919550909350505050565b5f808061074b8460016115ac565b6107569060046115ac565b6001600160401b0381111561076d5761076d610eff565b604051908082528060200260200182016040528015610796578160200160208202803683370190505b50905084815f815181106107ac576107ac61150b565b602090810291909101015260015b848111610816576102008701516107d2600183611566565b601c81106107e2576107e261150b565b60200201518282815181106107f9576107f961150b565b60209081029190910101528061080e816115f3565b9150506107ba565b505f806108248660016115ac565b90505b6108328660046115ac565b811161089157876102200151826004811061084f5761084f61150b565b60200201518382815181106108665761086661150b565b60209081029190910101528161087b816115f3565b9250508080610889906115f3565b915050610827565b506108a6826040516020016101d69190611656565b92506108b183610d18565b509792965091945050505050565b5f5f6108c9610ec3565b838152610240850180515160208084019190915290518101516040808401919091525161035f916101d691849101611680565b5f8080808061090c8660016115ac565b6109179060086115ac565b6001600160401b0381111561092e5761092e610eff565b604051908082528060200260200182016040528015610957578160200160208202803683370190505b509050865f1b815f8151811061096f5761096f61150b565b60209081029190910101525f5b610987601088611566565b8110156109d8578989828181106109a0576109a061150b565b90506020020135828260016109b591906115ac565b815181106109c5576109c561150b565b602090810291909101015260010161097c565b505f5b6010811015610a49578a51610a039082601081106109fb576109fb61150b565b602002015190565b82826010610a128b60016115ac565b610a1c9190611566565b610a2691906115ac565b81518110610a3657610a3661150b565b60209081029190910101526001016109db565b5060208a01515181610a5c8860016115ac565b81518110610a6c57610a6c61150b565b6020908102919091018101919091528a810151015181610a8d8860016115ac565b610a989060016115ac565b81518110610aa857610aa861150b565b602090810291909101015260408a01515181610ac58860016115ac565b610ad09060026115ac565b81518110610ae057610ae061150b565b60209081029190910181019190915260408b0151015181610b028860016115ac565b610b0d9060036115ac565b81518110610b1d57610b1d61150b565b602090810291909101015260608a01515181610b3a8860016115ac565b610b459060046115ac565b81518110610b5557610b5561150b565b60209081029190910181019190915260608b0151015181610b778860016115ac565b610b829060056115ac565b81518110610b9257610b9261150b565b602090810291909101015260808a01515181610baf8860016115ac565b610bba9060066115ac565b81518110610bca57610bca61150b565b60209081029190910181019190915260808b0151015181610bec8860016115ac565b610bf79060076115ac565b81518110610c0757610c0761150b565b602002602001018181525050610c27816040516020016101d69190611656565b9150610c3282610d18565b6040805160208101869052929750909550610c4d91016101d6565b9150610c5882610d18565b50809350505095509550955095915050565b5f5f5f610c75610ee1565b85815260c0858101805151602080850191909152905181015160408085019190915260e08801805151606086015251820151608085015260a08089018051519186019190915251820151928401929092529051610cd8916101d6918491016116b3565b9150610ce382610d18565b90979096509194509092505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001900690565b5f808260016001607f1b038116607f82901c610d3382610cf2565b9450610d3e81610cf2565b9350505050915091565b5f7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182840990505b92915050565b604051806101400160405280610d8a610ddb565b8152602001610d97610e0b565b8152602001610da4610e48565b81526020015f8152602001610db7610e48565b81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b6040518060c001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b604051806103600160405280601b906020820280368337509192915050565b6040518060a001604052806005906020820280368337509192915050565b604051806103800160405280601c906020820280368337509192915050565b60405180608001604052806004906020820280368337509192915050565b604051806101400160405280600a906020820280368337509192915050565b6040518061060001604052806030906020820280368337509192915050565b60405180606001604052806003906020820280368337509192915050565b6040518060e001604052806007906020820280368337509192915050565b634e487b7160e01b5f52604160045260245ffd5b60405161028081016001600160401b0381118282101715610f3657610f36610eff565b60405290565b604051601f8201601f191681016001600160401b0381118282101715610f6457610f64610eff565b604052919050565b5f82601f830112610f7b575f5ffd5b5f610200610f8881610f3c565b915083018185821115610f99575f5ffd5b845b82811015610fb3578035825260209182019101610f9b565b509195945050505050565b5f60408284031215610fce575f5ffd5b604080519081016001600160401b0381118282101715610ff057610ff0610eff565b604052823581526020928301359281019290925250919050565b5f82601f830112611019575f5ffd5b5f6110246060610f3c565b90508060c0840185811115611037575f5ffd5b845b81811015610fb35761104b8782610fbe565b8352602090920191604001611039565b5f82601f83011261106a575f5ffd5b61038061107681610f3c565b905080611f8084018581111561108a575f5ffd5b845b818110156110ee5786601f8201126110a2575f5ffd5b5f6101206110af81610f3c565b9150820181898211156110c0575f5ffd5b835b828110156110da5780358252602091820191016110c2565b50505084526020909301926101200161108c565b509095945050505050565b5f82601f830112611108575f5ffd5b5f610540610f8881610f3c565b5f82601f830112611124575f5ffd5b5f61036061113181610f3c565b915050806106c0840185811115611146575f5ffd5b845b81811015610fb35761115a8782610fbe565b8352602090920191604001611148565b5f82601f830112611179575f5ffd5b5f610380610f8881610f3c565b5f82601f830112611195575f5ffd5b5f6111a06080610f3c565b90508060808401858111156111b3575f5ffd5b845b81811015610fb35780358352602092830192016111b5565b5f5f83601f8401126111dd575f5ffd5b5081356001600160401b038111156111f3575f5ffd5b6020830191508360208260051b850101111561120d575f5ffd5b9250929050565b5f5f5f5f5f5f8688036135c081121561122b575f5ffd5b613540811215611239575f5ffd5b50611242610f13565b61124c8989610f6c565b815261125c896102008a01610fbe565b602082015261126f896102408a01610fbe565b6040820152611282896102808a01610fbe565b6060820152611295896102c08a01610fbe565b60808201526112a8896103008a01610fbe565b60a08201526112bb896103408a01610fbe565b60c08201526112ce896103808a01610fbe565b60e08201526112e1896103c08a01610fbe565b6101008201526112f5896104008a01610fbe565b610120820152611309896104408a0161100a565b610140820152610500880135610160820152611329896105208a0161105b565b6101808201526124a08801356101a0820152611349896124c08a016110f9565b6101c082015261135d89612a008a01611115565b6101e0820152611371896130c08a0161116a565b610200820152611385896134408a01611186565b610220820152611399896134c08a01610fbe565b6102408201526113ad896135008a01610fbe565b61026082015295506135408701356001600160401b038111156113ce575f5ffd5b6113da89828a016111cd565b979a90995096976135608101359761358082013597506135a09091013595509350505050565b805f5b601b811015611422578151845260209384019390910190600101611403565b50505050565b805f5b601c81101561142257815184526020938401939091019060010161142b565b8151805182526020808201519083015260408082015190830152606080820151908301526080808201519083015260a09081015190820152610be08101602083015161149960c0840182611400565b5060408301516114ad610420840182611428565b5060608301516107a083015260808301516114cc6107c0840182611428565b5060a0830151610b4083015260c0830151610b6083015260e0830151610b80830152610100830151610ba083015261012090920151610bc09091015290565b634e487b7160e01b5f52603260045260245ffd5b5f8183825b6005811015611543578151835260209283019290910190600101611524565b50505060a08201905092915050565b634e487b7160e01b5f52601160045260245ffd5b81810381811115610d7057610d70611552565b5f8183825b600481101561159d57815183526020928301929091019060010161157e565b50505060808201905092915050565b80820180821115610d7057610d70611552565b5f8183825b600a8110156115e35781518352602092830192909101906001016115c4565b5050506101408201905092915050565b5f6001820161160457611604611552565b5060010190565b5f8183825b603081101561162f578151835260209283019290910190600101611610565b5050506106008201905092915050565b8082028115828204841417610d7057610d70611552565b81515f90829060208501835b82811015610fb3578151845260209384019390910190600101611662565b5f8183825b60038110156116a4578151835260209283019290910190600101611685565b50505060608201905092915050565b5f8183825b60078110156116d75781518352602092830192909101906001016116b8565b50505060e0820190509291505056fea164736f6c634300081c000a", + "deployedBytecode": "0x7300000000000000000000000000000000000000003014608060405260043610610034575f3560e01c8063995bf45714610038575b5f5ffd5b61004b610046366004611214565b610061565b604051610058919061144a565b60405180910390f35b610069610d76565b5f610078888888888886610128565b9083529050610087818961017c565b6020840191909152905061009b818461026c565b604084019190915290506100af8189610315565b606084019190915290506100c4888285610376565b608084019190915290506100d88882610445565b60a084019190915290506100ed8882856105cb565b60c0840191909152905061010288828561073d565b60e0840191909152905061011688826108bf565b50610100830152509695505050505050565b610130610ddb565b5f61013e88888888886108fc565b60408601919091526020850191909152908352925061015d8389610c6a565b6080850191909152606084019190915291989197509095505050505050565b610184610e0b565b5f61018d610e2a565b8481526101008401805151602080840191909152905181015160408084019190915261012086018051516060850152518201516080840152516101f1916101d69184910161151f565b60405160208183030381529060405280519060200120610cf2565b91505f6101fd83610d18565b50808552905060015b6102126001601c611566565b8110156102625761024385610228600184611566565b601b81106102385761023861150b565b602002015183610d48565b8582601b81106102555761025561150b565b6020020152600101610206565b5050509250929050565b610274610e48565b5f61028b846040516020016101d691815260200190565b935061029684610d18565b50825260015b8381101561030d576102ee836102b3600184611566565b601c81106102c3576102c361150b565b6020020151846102d4600185611566565b601c81106102e4576102e461150b565b6020020151610d48565b8382601c81106103005761030061150b565b602002015260010161029c565b509093915050565b5f5f61031f610e67565b84815261014084018051515160208084019190915290515181015160408084019190915261016086015160608401525161035f916101d691849101611579565b915061036a82610d18565b50959194509092505050565b61037e610e48565b5f805b8381101561043b57610391610e85565b8581525f5b60098110156103fa5787610180015183601c81106103b6576103b661150b565b602002015181600981106103cc576103cc61150b565b6020020151826103dd8360016115ac565b600a81106103ed576103ed61150b565b6020020152600101610396565b5061040f816040516020016101d691906115bf565b955061041a86610d18565b508483601c811061042d5761042d61150b565b602002015250600101610381565b5090949293505050565b5f5f61044f610ea4565b83815260015b610461600160296115ac565b81116104b6576101c0860151610478600183611566565b602a81106104885761048861150b565b602002015182826030811061049f5761049f61150b565b6020020152806104ae816115f3565b915050610455565b856101a001518282603081106104ce576104ce61150b565b60200201526104de6001826115ac565b61014087015160200151519091508282603081106104fe576104fe61150b565b602002015261014086015160016020020151602001518282600161052291906115ac565b603081106105325761053261150b565b60200201526105426002826115ac565b61014087015160400151519091508282603081106105625761056261150b565b602002015261014086015160026020020151602001518282600161058691906115ac565b603081106105965761059661150b565b6020020181815250506105b3826040516020016101d6919061160b565b92506105be83610d18565b5096929550919350505050565b5f80806105d9600185611566565b6105e490600261163f565b6105ef9060016115ac565b6001600160401b0381111561060657610606610eff565b60405190808252806020026020018201604052801561062f578160200160208202803683370190505b50905084815f815181106106455761064561150b565b60209081029190910101525f5b61065d600186611566565b81101561071057866101e0015181601b811061067b5761067b61150b565b6020020151518261068d83600261163f565b6106989060016115ac565b815181106106a8576106a861150b565b602002602001018181525050866101e0015181601b81106106cb576106cb61150b565b602002015160200151828260026106e2919061163f565b6106ed9060026115ac565b815181106106fd576106fd61150b565b6020908102919091010152600101610652565b50610725816040516020016101d69190611656565b915061073082610d18565b5096919550909350505050565b5f808061074b8460016115ac565b6107569060046115ac565b6001600160401b0381111561076d5761076d610eff565b604051908082528060200260200182016040528015610796578160200160208202803683370190505b50905084815f815181106107ac576107ac61150b565b602090810291909101015260015b848111610816576102008701516107d2600183611566565b601c81106107e2576107e261150b565b60200201518282815181106107f9576107f961150b565b60209081029190910101528061080e816115f3565b9150506107ba565b505f806108248660016115ac565b90505b6108328660046115ac565b811161089157876102200151826004811061084f5761084f61150b565b60200201518382815181106108665761086661150b565b60209081029190910101528161087b816115f3565b9250508080610889906115f3565b915050610827565b506108a6826040516020016101d69190611656565b92506108b183610d18565b509792965091945050505050565b5f5f6108c9610ec3565b838152610240850180515160208084019190915290518101516040808401919091525161035f916101d691849101611680565b5f8080808061090c8660016115ac565b6109179060086115ac565b6001600160401b0381111561092e5761092e610eff565b604051908082528060200260200182016040528015610957578160200160208202803683370190505b509050865f1b815f8151811061096f5761096f61150b565b60209081029190910101525f5b610987601088611566565b8110156109d8578989828181106109a0576109a061150b565b90506020020135828260016109b591906115ac565b815181106109c5576109c561150b565b602090810291909101015260010161097c565b505f5b6010811015610a49578a51610a039082601081106109fb576109fb61150b565b602002015190565b82826010610a128b60016115ac565b610a1c9190611566565b610a2691906115ac565b81518110610a3657610a3661150b565b60209081029190910101526001016109db565b5060208a01515181610a5c8860016115ac565b81518110610a6c57610a6c61150b565b6020908102919091018101919091528a810151015181610a8d8860016115ac565b610a989060016115ac565b81518110610aa857610aa861150b565b602090810291909101015260408a01515181610ac58860016115ac565b610ad09060026115ac565b81518110610ae057610ae061150b565b60209081029190910181019190915260408b0151015181610b028860016115ac565b610b0d9060036115ac565b81518110610b1d57610b1d61150b565b602090810291909101015260608a01515181610b3a8860016115ac565b610b459060046115ac565b81518110610b5557610b5561150b565b60209081029190910181019190915260608b0151015181610b778860016115ac565b610b829060056115ac565b81518110610b9257610b9261150b565b602090810291909101015260808a01515181610baf8860016115ac565b610bba9060066115ac565b81518110610bca57610bca61150b565b60209081029190910181019190915260808b0151015181610bec8860016115ac565b610bf79060076115ac565b81518110610c0757610c0761150b565b602002602001018181525050610c27816040516020016101d69190611656565b9150610c3282610d18565b6040805160208101869052929750909550610c4d91016101d6565b9150610c5882610d18565b50809350505095509550955095915050565b5f5f5f610c75610ee1565b85815260c0858101805151602080850191909152905181015160408085019190915260e08801805151606086015251820151608085015260a08089018051519186019190915251820151928401929092529051610cd8916101d6918491016116b3565b9150610ce382610d18565b90979096509194509092505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001900690565b5f808260016001607f1b038116607f82901c610d3382610cf2565b9450610d3e81610cf2565b9350505050915091565b5f7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182840990505b92915050565b604051806101400160405280610d8a610ddb565b8152602001610d97610e0b565b8152602001610da4610e48565b81526020015f8152602001610db7610e48565b81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b6040518060c001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b604051806103600160405280601b906020820280368337509192915050565b6040518060a001604052806005906020820280368337509192915050565b604051806103800160405280601c906020820280368337509192915050565b60405180608001604052806004906020820280368337509192915050565b604051806101400160405280600a906020820280368337509192915050565b6040518061060001604052806030906020820280368337509192915050565b60405180606001604052806003906020820280368337509192915050565b6040518060e001604052806007906020820280368337509192915050565b634e487b7160e01b5f52604160045260245ffd5b60405161028081016001600160401b0381118282101715610f3657610f36610eff565b60405290565b604051601f8201601f191681016001600160401b0381118282101715610f6457610f64610eff565b604052919050565b5f82601f830112610f7b575f5ffd5b5f610200610f8881610f3c565b915083018185821115610f99575f5ffd5b845b82811015610fb3578035825260209182019101610f9b565b509195945050505050565b5f60408284031215610fce575f5ffd5b604080519081016001600160401b0381118282101715610ff057610ff0610eff565b604052823581526020928301359281019290925250919050565b5f82601f830112611019575f5ffd5b5f6110246060610f3c565b90508060c0840185811115611037575f5ffd5b845b81811015610fb35761104b8782610fbe565b8352602090920191604001611039565b5f82601f83011261106a575f5ffd5b61038061107681610f3c565b905080611f8084018581111561108a575f5ffd5b845b818110156110ee5786601f8201126110a2575f5ffd5b5f6101206110af81610f3c565b9150820181898211156110c0575f5ffd5b835b828110156110da5780358252602091820191016110c2565b50505084526020909301926101200161108c565b509095945050505050565b5f82601f830112611108575f5ffd5b5f610540610f8881610f3c565b5f82601f830112611124575f5ffd5b5f61036061113181610f3c565b915050806106c0840185811115611146575f5ffd5b845b81811015610fb35761115a8782610fbe565b8352602090920191604001611148565b5f82601f830112611179575f5ffd5b5f610380610f8881610f3c565b5f82601f830112611195575f5ffd5b5f6111a06080610f3c565b90508060808401858111156111b3575f5ffd5b845b81811015610fb35780358352602092830192016111b5565b5f5f83601f8401126111dd575f5ffd5b5081356001600160401b038111156111f3575f5ffd5b6020830191508360208260051b850101111561120d575f5ffd5b9250929050565b5f5f5f5f5f5f8688036135c081121561122b575f5ffd5b613540811215611239575f5ffd5b50611242610f13565b61124c8989610f6c565b815261125c896102008a01610fbe565b602082015261126f896102408a01610fbe565b6040820152611282896102808a01610fbe565b6060820152611295896102c08a01610fbe565b60808201526112a8896103008a01610fbe565b60a08201526112bb896103408a01610fbe565b60c08201526112ce896103808a01610fbe565b60e08201526112e1896103c08a01610fbe565b6101008201526112f5896104008a01610fbe565b610120820152611309896104408a0161100a565b610140820152610500880135610160820152611329896105208a0161105b565b6101808201526124a08801356101a0820152611349896124c08a016110f9565b6101c082015261135d89612a008a01611115565b6101e0820152611371896130c08a0161116a565b610200820152611385896134408a01611186565b610220820152611399896134c08a01610fbe565b6102408201526113ad896135008a01610fbe565b61026082015295506135408701356001600160401b038111156113ce575f5ffd5b6113da89828a016111cd565b979a90995096976135608101359761358082013597506135a09091013595509350505050565b805f5b601b811015611422578151845260209384019390910190600101611403565b50505050565b805f5b601c81101561142257815184526020938401939091019060010161142b565b8151805182526020808201519083015260408082015190830152606080820151908301526080808201519083015260a09081015190820152610be08101602083015161149960c0840182611400565b5060408301516114ad610420840182611428565b5060608301516107a083015260808301516114cc6107c0840182611428565b5060a0830151610b4083015260c0830151610b6083015260e0830151610b80830152610100830151610ba083015261012090920151610bc09091015290565b634e487b7160e01b5f52603260045260245ffd5b5f8183825b6005811015611543578151835260209283019290910190600101611524565b50505060a08201905092915050565b634e487b7160e01b5f52601160045260245ffd5b81810381811115610d7057610d70611552565b5f8183825b600481101561159d57815183526020928301929091019060010161157e565b50505060808201905092915050565b80820180821115610d7057610d70611552565b5f8183825b600a8110156115e35781518352602092830192909101906001016115c4565b5050506101408201905092915050565b5f6001820161160457611604611552565b5060010190565b5f8183825b603081101561162f578151835260209283019290910190600101611610565b5050506106008201905092915050565b8082028115828204841417610d7057610d70611552565b81515f90829060208501835b82811015610fb3578151845260209384019390910190600101611662565b5f8183825b60038110156116a4578151835260209283019290910190600101611685565b50505060608201905092915050565b5f8183825b60078110156116d75781518352602092830192909101906001016116b8565b50505060e0820190509291505056fea164736f6c634300081c000a", + "linkReferences": {}, + "deployedLinkReferences": {}, + "immutableReferences": {}, + "inputSourceName": "project/contracts/verifier/ThresholdDecryptedSharesAggregationModVerifier.sol", + "buildInfoId": "solc-0_8_28-bd46eae6f77be9a8538850385226c1ee4fc57e42" +} \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/verifier/ThresholdPkAggregationVerifier.sol/ThresholdPkAggregationVerifier.json b/packages/enclave-contracts/artifacts/contracts/verifier/ThresholdPkAggregationVerifier.sol/ThresholdPkAggregationVerifier.json new file mode 100644 index 0000000000..d161c2ad7b --- /dev/null +++ b/packages/enclave-contracts/artifacts/contracts/verifier/ThresholdPkAggregationVerifier.sol/ThresholdPkAggregationVerifier.json @@ -0,0 +1,188 @@ +{ + "_format": "hh3-artifact-1", + "contractName": "ThresholdPkAggregationVerifier", + "sourceName": "contracts/verifier/ThresholdPkAggregationVerifier.sol", + "abi": [ + { + "inputs": [], + "name": "ConsistencyCheckFailed", + "type": "error" + }, + { + "inputs": [], + "name": "GeminiChallengeInSubgroup", + "type": "error" + }, + { + "inputs": [], + "name": "ProofLengthWrong", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "logN", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "actualLength", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expectedLength", + "type": "uint256" + } + ], + "name": "ProofLengthWrongWithLogN", + "type": "error" + }, + { + "inputs": [], + "name": "PublicInputsLengthWrong", + "type": "error" + }, + { + "inputs": [], + "name": "ShpleminiFailed", + "type": "error" + }, + { + "inputs": [], + "name": "SumcheckFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "proof", + "type": "bytes" + }, + { + "internalType": "bytes32[]", + "name": "publicInputs", + "type": "bytes32[]" + } + ], + "name": "verify", + "outputs": [ + { + "internalType": "bool", + "name": "verified", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x610120604052348015610010575f5ffd5b50620400006080819052601260a08190527f02306f0575881d3ddf6f0b9e8dac79af163672a144640cdf2a6ed6942ee4c6aa60c0819052601660e081905260038361005d6001602461008a565b610067919061008a565b610071919061008a565b61007c90600261008a565b61010052506100af92505050565b808201808211156100a957634e487b7160e01b5f52601160045260245ffd5b92915050565b60805160a05160c05160e05161010051615cba6101495f395f8181610c5401528181610cb90152612ec201525f81816101b001526108ff01525f61018e01525f8181605b01528181609401528181610101015281816101d201528181610a2d01528181610b8e01528181610c2b01528181611523015281816115dd015281816116100152818161181a0152612b9f01525f5050615cba5ff3fe608060405234801561000f575f5ffd5b5060043610610029575f3560e01c8063ea50d0e41461002d575b5f5ffd5b61004061003b3660046154f1565b610054565b604051901515815260200160405180910390f35b5f5f61007f7f00000000000000000000000000000000000000000000000000000000000000006102ee565b905061008c8160206155c9565b85146100ee577f0000000000000000000000000000000000000000000000000000000000000000856100bf8360206155c9565b6040516359895a5360e01b81526004810193909352602483019190915260448201526064015b60405180910390fd5b5f6100f76103fb565b90505f61012588887f0000000000000000000000000000000000000000000000000000000000000000610410565b90506010826040015161013891906155e0565b85146101575760405163fa06659360e01b815260040160405180910390fd5b60405163995bf45760e01b81525f9073__$63cdeab5e76d5ced0e84f3fc5e357ab766$__9063995bf457906101fa9085908b908b907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090600401615763565b610be060405180830381865af4158015610216573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061023a9190615a8f565b905061028a8787808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050855185516060810151608090910151919350915060016108a9565b815160a0015261029a8282610a10565b6102b7576040516313f8744360e31b815260040160405180910390fd5b6102c2828483610c17565b6102df576040516352ec174560e11b815260040160405180910390fd5b50600198975050505050505050565b5f8060026102fe60016008615b39565b61030891906155c9565b9050610316600260036155c9565b6103209082615b39565b9050600161032f6009856155c9565b61033991906155c9565b6103439082615b39565b90506001610352816029615b39565b61035c91906155c9565b6103669082615b39565b9050610374600160026155c9565b61037e9082615b39565b905061038b6001846155c9565b6103959082615b39565b90506103a3600160046155c9565b6103ad9082615b39565b905060026103bc6001856155e0565b6103c691906155c9565b6103d09082615b39565b90506103dd6002806155c9565b6103e79082615b39565b90506103f4601082615b39565b9392505050565b610403615024565b61040b611c14565b905090565b6104186151ae565b5f805b601081101561047a57610447868387610435602083615b39565b9261044293929190615b4c565b61254e565b8351826010811061045a5761045a615b73565b6020020181815250506020826104709190615b39565b915060010161041b565b5061049e85828661048c604083615b39565b9261049993929190615b4c565b612561565b60208301526104ae604082615b39565b90506104c185828661048c604083615b39565b6040808401919091526104d49082615b39565b90506104e785828661048c604083615b39565b60608301526104f7604082615b39565b905061050a85828661048c604083615b39565b608083015261051a604082615b39565b905061052d85828661048c604083615b39565b60c083015261053d604082615b39565b905061055085828661048c604083615b39565b60e0830152610560604082615b39565b905061057385828661048c604083615b39565b60a0830152610583604082615b39565b905061059685828661048c604083615b39565b6101008301526105a7604082615b39565b90506105ba85828661048c604083615b39565b6101208301526105cb604082615b39565b90506105de85828661048c604083615b39565b610140830151526105f0604082615b39565b9050610603858286610435602083615b39565b610160830152610614602082615b39565b90505f5b83811015610692575f5b60098110156106895761063c878488610435602083615b39565b84610180015183601c811061065357610653615b73565b6020020151826009811061066957610669615b73565b60200201818152505060208361067f9190615b39565b9250600101610622565b50600101610618565b505f5b6106a160016029615b39565b8110156106f0576106b9868387610435602083615b39565b836101c0015182602a81106106d0576106d0615b73565b6020020181815250506020826106e69190615b39565b9150600101610695565b50610702858286610435602083615b39565b6101a0830152610713602082615b39565b905061072685828661048c604083615b39565b6101408301516020015261073b604082615b39565b905061074e85828661048c604083615b39565b61014083015160026020020152610766604082615b39565b90505f5b6107756001856155e0565b8110156107be5761078d86838761048c604083615b39565b836101e0015182601b81106107a4576107a4615b73565b60200201526107b4604083615b39565b915060010161076a565b505f5b83811015610811576107da868387610435602083615b39565b83610200015182601c81106107f1576107f1615b73565b6020020181815250506020826108079190615b39565b91506001016107c1565b505f5b60048110156108655761082e868387610435602083615b39565b836102200151826004811061084557610845615b73565b60200201818152505060208261085b9190615b39565b9150600101610814565b5061087785828661048c604083615b39565b610240830152610888604082615b39565b905061089b85828661048c604083615b39565b610260830152509392505050565b5f600180826108d6866108d1896108cc6108c78a6310000000615b39565b6125e3565b6125fb565b612616565b90505f6108f4876108ef8a6108cc6108c78b6001615b39565b61262f565b90505f5b61092360107f00000000000000000000000000000000000000000000000000000000000000006155e0565b811015610990575f61094d8c838151811061094057610940615b73565b6020026020010151612657565b905061095d866108cc8684612616565b955061096d856108cc8584612616565b9450610979848b612616565b9350610985838b61262f565b9250506001016108f8565b505f5b60108110156109f7575f8a82601081106109af576109af615b73565b602002015190506109c4866108cc8684612616565b95506109d4856108cc8584612616565b94506109e0848b612616565b93506109ec838b61262f565b925050600101610993565b50610a02848461266c565b9a9950505050505050505050565b5f5f610a2583606001518561016001516125fb565b905060015f5b7f0000000000000000000000000000000000000000000000000000000000000000811015610b17575f86610180015182601c8110610a6b57610a6b615b73565b602002015180519091505f90610a89908360015b6020020151612616565b9050848114610aab576040516313f8744360e31b815260040160405180910390fd5b5f876080015184601c8110610ac257610ac2615b73565b60200201519050610ad3838261267a565b9550610b07856108cc60016108d1856108cc8e604001518b601c8110610afb57610afb615b73565b6020020151600161262f565b9450505050806001019050610a2b565b50610b20615291565b5f5b6029811015610b70576101c0870151610b3c600183615b39565b602a8110610b4c57610b4c615b73565b6020020151828260298110610b6357610b63615b73565b6020020152600101610b22565b505f610b8582875f0151886020015186612828565b9050600160025b7f0000000000000000000000000000000000000000000000000000000000000000811015610be257610bd882896080015183601c8110610bce57610bce615b73565b60200201516125fb565b9150600101610b8c565b50610c08610bf5836108cc60018561262f565b6108d18a6101a001518a606001516125fb565b94909414979650505050505050565b5f610c206152b0565b5f610c4f8460c001517f00000000000000000000000000000000000000000000000000000000000000006128a1565b90505f7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03811115610c8a57610c8a615940565b604051908082528060200260200182016040528015610cb3578160200160208202803683370190505b5090505f7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03811115610cef57610cef615940565b604051908082528060200260200182016040528015610d2857816020015b610d15615320565b815260200190600190039081610d0d5790505b509050610d5b610d56876101000151855f81518110610d4957610d49615b73565b602002602001015161262f565b61296a565b84610120018181525050610d90610d56876101000151855f81518110610d8357610d83615b73565b6020026020010151612616565b610140850181905261012085015160e0880151610db1926108d191906125fb565b845260c0860151610de190610dc59061296a565b6108cc8661012001516108ef8a60e001518961014001516125fb565b6020850152815160019083905f90610dfb57610dfb615b73565b602002602001018181525050876102400151815f81518110610e1f57610e1f615b73565b6020908102919091010152600160a08501525f60c08501528351610e42906129da565b60408501526020840151610e55906129da565b606085015260015b610e6960016024615b39565b8111610f0a57610e8185604001518660a001516125fb565b838281518110610e9357610e93615b73565b602002602001018181525050610edc8560c001516108d18b6101c00151600185610ebd91906155e0565b602a8110610ecd57610ecd615b73565b60200201518860a001516125fb565b60c086015260a08086015190880151610ef591906125fb565b60a0860152610f0381615b87565b9050610e5d565b505f5b6005811015610fe3575f610f22601e83615b39565b90505f610f3160016024615b39565b610f3b9084615b39565b9050610f6c858381518110610f5257610f52615b73565b60200260200101516108d189606001518a60a001516125fb565b858381518110610f7e57610f7e615b73565b602002602001018181525050610fbb8760c001516108d18d6101c0015184602a8110610fac57610fac615b73565b60200201518a60a001516125fb565b60c088015260a080880151908a0151610fd491906125fb565b60a08801525050600101610f0d565b50876020015181600181518110610ffc57610ffc615b73565b602002602001018190525086606001518160028151811061101f5761101f615b73565b602002602001018190525086608001518160038151811061104257611042615b73565b60200260200101819052508660a001518160048151811061106557611065615b73565b60200260200101819052508660c001518160058151811061108857611088615b73565b60200260200101819052508660e00151816006815181106110ab576110ab615b73565b6020026020010181905250866101000151816007815181106110cf576110cf615b73565b6020026020010181905250866101200151816008815181106110f3576110f3615b73565b60200260200101819052508661014001518160098151811061111757611117615b73565b602002602001018190525086610160015181600a8151811061113b5761113b615b73565b6020026020010181905250866101c0015181600b8151811061115f5761115f615b73565b602002602001018190525086610180015181600c8151811061118357611183615b73565b6020026020010181905250866101a0015181600d815181106111a7576111a7615b73565b6020026020010181905250866101e0015181600e815181106111cb576111cb615b73565b602002602001018190525086610200015181600f815181106111ef576111ef615b73565b60200260200101819052508661022001518160108151811061121357611213615b73565b60200260200101819052508661024001518160118151811061123757611237615b73565b60200260200101819052508661026001518160128151811061125b5761125b615b73565b60200260200101819052508661028001518160138151811061127f5761127f615b73565b6020026020010181905250866102a00151816014815181106112a3576112a3615b73565b6020026020010181905250866102c00151816015815181106112c7576112c7615b73565b6020026020010181905250866102e00151816016815181106112eb576112eb615b73565b60200260200101819052508661030001518160178151811061130f5761130f615b73565b60200260200101819052508661032001518160188151811061133357611333615b73565b60200260200101819052508661034001518160198151811061135757611357615b73565b602002602001018190525086610360015181601a8151811061137b5761137b615b73565b602002602001018190525086610380015181601b8151811061139f5761139f615b73565b6020026020010181905250866103a0015181601c815181106113c3576113c3615b73565b6020026020010181905250866103c0015181601d815181106113e7576113e7615b73565b6020026020010181905250876040015181601e8151811061140a5761140a615b73565b6020026020010181905250876060015181601f8151811061142d5761142d615b73565b602002602001018190525087608001518160208151811061145057611450615b73565b60200260200101819052508760a001518160218151811061147357611473615b73565b60200260200101819052508761012001518160228151811061149757611497615b73565b6020026020010181905250876101000151816023815181106114bb576114bb615b73565b60200260200101819052508760c00151816024815181106114de576114de615b73565b60200260200101819052508760e001518160258151811061150157611501615b73565b60200260200101819052505f61154787608001518660c001518b6102000151877f00000000000000000000000000000000000000000000000000000000000000006129ec565b9050611571815f8151811061155e5761155e615b73565b60200260200101518661012001516125fb565b608086018190526102008a01515160e08901516115a292916108d19161159791906125fb565b8861014001516125fb565b608086015260e08701516115b590612b4a565b60a08601525f6115c760016024615b39565b6115d2906001615b39565b90505f5b61160160017f00000000000000000000000000000000000000000000000000000000000000006155e0565b811015611812575f61163460017f00000000000000000000000000000000000000000000000000000000000000006155e0565b8210159050806117a357611668610d568b6101000151898560016116589190615b39565b81518110610d4957610d49615b73565b6101208901526101008a015161169890610d569089611688866001615b39565b81518110610d8357610d83615b73565b61014089015260a08801516101208901516116b391906125fb565b61016089015260a088015160e08b01516116db916116d0916125fb565b8961014001516125fb565b61018089018190526116fe906116f0906129da565b6108d18a61016001516129da565b866117098486615b39565b8151811061171957611719615b73565b6020026020010181815250505f6117528961018001518e61020001518560016117429190615b39565b601c8110610bce57610bce615b73565b905061178c816108d18b61016001518887600161176f9190615b39565b8151811061177f5761177f615b73565b60200260200101516125fb565b905061179c896080015182612616565b60808a0152505b6117c26117b88960a001518c60e001516125fb565b8b60e001516125fb565b60a08901526101e08c015182601b81106117de576117de615b73565b6020020151856117ee8486615b39565b815181106117fe576117fe615b73565b6020908102919091010152506001016115d6565b5061183e60017f00000000000000000000000000000000000000000000000000000000000000006155e0565b6118489082615b39565b90506118686118608961010001518a60c0015161262f565b60019061266c565b60e08701515261010088015160c08901516118ac91611860916108ef907f07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76906125fb565b60e0878101805160200192909252815180516040909101529051805160609091015260a0870151908901516118ee916118e4916125fb565b8960e001516125fb565b60a08701525f5b600481101561199c575f6119278860e00151836004811061191857611918615b73565b60200201518960a001516125fb565b9050611932816129da565b886101000151836004811061194957611949615b73565b602002015260a088015160e08b015161196291906125fb565b8860a001818152505061198e88608001516108d1838f61022001518660048110610bce57610bce615b73565b6080890152506001016118f5565b506101008601515184518590839081106119b8576119b8615b73565b602090810291909101810191909152610100870151908101516119dc916002610a7f565b846119e8836001615b39565b815181106119f8576119f8615b73565b60209081029190910101526101008601516060015184611a19836002615b39565b81518110611a2957611a29615b73565b60209081029190910101525f5b6003811015611a8b578a61014001518160038110611a5657611a56615b73565b60200201518483611a6681615b87565b945081518110611a7857611a78615b73565b6020908102919091010152600101611a36565b506040518060400160405280600181526020016002815250838281518110611ab557611ab5615b73565b60200260200101819052508560800151848280611ad190615b87565b935081518110611ae357611ae3615b73565b602002602001018181525050611b0d8a61022001518960c001518a608001518d6101a00151612b55565b611b2a5760405163a2a2ac8360e01b815260040160405180910390fd5b5f8a6102600151905080848381518110611b4657611b46615b73565b6020026020010181905250886101000151858381518110611b6957611b69615b73565b602002602001018181525050611b7d615338565b611b878587612eb8565b8152611b9282612fb0565b602082018190528c5182515f92611baa929190612ff6565b90505f5f611bba8f5f0151613087565b91509150611bc782613148565b611bd081613148565b8351611bdd9083856131e7565b84526020840151611bef9082856131e7565b602085018190528451611c0191613216565b9f9e505050505050505050505050505050565b611c1c615024565b50604080516103e0810182526204000081526012602080830191909152601682840152825180840184527f1c34241ed7bd1ea35de9fe54f11e3deca92247eda67d756489374539ed08082881527f1fa29ca2f59dfccf9e71342fe789ae8e2aa41079798daf79b8e02d06a510a4b7818301526060830152825180840184527f147ff414795e16dd9d8ad7758205875dcbb2feca77004005326a37295450902e81527f2ce19f89d76fc3a0e68df64342ecb603eb7743838eaab9cad94f597ff49c3d52818301526080830152825180840184527f131fd9af41c938c8b1b6e0086eb4f08ba4075a71288d83449d8d5eac6247099a81527f072066e82cbf467dd3c411fdaf6242d1ca23f1b37710849520bf2cd4b714e7958183015260a0830152825180840184527f02f04c8042c961b5c40c8617efb47c118b71d76c64259724eb802e8efdada48f81527f266704b76a579143a49d75c18ad41e4c8664833a345240b18787c26e832b2c2e8183015260c0830152825180840184527f0efe5ef9accef9728a562f5e9ea4fd60b0957f6e559689936896c9d7e94823ba81527f280a5dfe9d34183e42c5f275391576ae8e2891ab077920340756a44d9131ca708183015260e0830152825180840184527f2ecfda953e756b07faf8e3954fe5012494ee0237dea45538b88e34d949a516f981527f0677e9dcbd562e682c44632da1d4b5ffecdc6c9129adb89089c020384a34d28181830152610100830152825180840184527f15729599ef3888a89db8f52912b65cdfc12205eb78f54bd9f4742df51d35d9d281527f1890a0e4fb04b07215d2034d92fc6ad93cb3670761c8c93df420b3a7d89baefa81830152610120830152825180840184527f2aa5463c894b45d28a4b5ea9bed212fc3dd803ce12033d85358d4432a0655c6a81527f1397cec5342f4f86432b983400c0e56ceb029e78abeba47fc958779df917e1d781830152610140830152825180840184527f1617218df444678e9120d0de7f480a6d0bf6098c6e77009e8cc81a5bb0e820fc81527f1f03683cfa36530952dc9494533856af2e1487bed9e76f07b4afda82f3afb12981830152610160830152825180840184527f161d4f65fef69c1bbc2a671569c92da155e9374fabef1d260eb8776142c73bd881527f268e5909d8cab6c89c8064ee0fc25a7fc541a3238f93dea808d9fda94242113281830152610180830152825180840184527f26be72a59de37228c424b5ffc3149b2489578d0f09ba433097d9c8596473da5f81527f1e1d0abff71ff2bf7e0833e32af3baff87b41a17a2cf187367c04ced7bc5ff7a818301526101a0830152825180840184527f2229994889f103841fc6d5f7a4b209e045992468aec5a30800606e5c1918007c81527f016891f753e67c0d4349565eaeb8c02d828793eb835c20c532e771ec6918702f818301526101c0830152825180840184527f099ba64dd88f156dc06f527c2bac7b2c7c86d903e24ac09e47f833cfd6f903b881527f067f7914c30e0ca5dab695d46a6b6644f048d04e0938eea01c282d7ba0f043cc818301526101e0830152825180840184527f281fd5754fbb77ac2ef58f72fe142cf865a329c0611b38919dd1a36feb99436081527f15f7f262eba8a9b6df7a0928493a1399b33b1f765cb31c934d1cbde777c3bdfd81830152610200830152825180840184527f04ff10ec802daeb081f77ae5a048da6c5695f760c4b705fa2d0ae3cd5e76a9ae81527f1242b0b182171f1a737e13750ff2ff10762a6fcb1d4280ce14b5b3a43295610d81830152610220830152825180840184527f20bb81a1dc9b0c8d3cbcc2761b6f6751e08be09b384efd9b56bd5f81ca29c13681527f10d80cf403b8ff3de754a57331c5dd143cb0ae9e0382550efd8f20b353e274b581830152610240830152825180840184527f047c3bf9b1df8256aa780ba25fac792e3a8de5e460d9cb3e41bf8686653a2b8381527f1f13e4fa50f1ff9430c4f58316e6c3f1ccdcf87540d6a19add847606138d93bf81830152610260830152825180840184527f087819484fd402168d10636c0e1ff16095086cfebb16fa72ecd905faa77611e281527f061b92f2194d55921ff02e4362fe5b4f59eac905edb027e656700a56806a5e8981830152610280830152825180840184527f30302f92d6b476ab0407ba6570e2c869057a534fafc859946c315715fd37397d81527f2bccc49a1f124a8ea4933e1f65cd744c4ef91d17b429bc27f44ed533ff179ad9818301526102a0830152825180840184527f2fa778d2df483e2c7535aa5b015aba244987291fe5d0f78655332f35dbb983a281527f0eaaa6e5c290180d0979a51caf8592f4014402d09f8fdee08e2d2b98596b1743818301526102c0830152825180840184527f035d549bad95f400a2d94f6032216957b08b7ab07ab972d8403663454ecb46e481527f02512a1a401fd0832cea115ea34675373aa09faa59149b8ffd71fe76be90c306818301526102e0830152825180840184527f07377ddbcd535099c7600f69e539e1d41cdfd8705405334687c4f5b741226da881527f06608b7e3be5235cc0368ce844f8c822b118e54f993626431efa578a881b1e0181830152610300830152825180840184527f099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d2681527e15b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f81830152610320830152825180840184527f1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e81527f305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d1981830152610340830152825180840184527f061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d81527f1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e81830152610360830152825180840184527f043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce81527f261522c4089330646aff96736194949330952ae74c573d1686d9cb4a007338548183015261038083015282518084018452600181526002818301526103a083015282518084019093527f1280735205fd7e4a58ee8493d273e5afa2f13bfb7879873c4095a5cbfc646a4183527f155c3c102286dfdbfe78f876a3c74b9c36a02bb8197154c7d9fedb371f55b779908301526103c081019190915290565b5f6103f461255c8385615b9f565b612657565b612569615320565b60408051808201909152805f516020615c6e5f395f51905f5261258f60205f8789615b4c565b61259891615b9f565b6125a29190615bbc565b81526020908101905f516020615c6e5f395f51905f52906125c7906040908789615b4c565b6125d091615b9f565b6125da9190615bbc565b90529392505050565b5f5f516020615c8e5f395f51905f52825b0692915050565b5f5f516020615c8e5f395f51905f5282840990505b92915050565b5f5f516020615c8e5f395f51905f528284089392505050565b5f5f516020615c8e5f395f51905f52825f516020615c8e5f395f51905f520384089392505050565b5f5f516020615c8e5f395f51905f52826125f4565b5f6103f4836108cc8461296a565b5f5f604051806101200160405280619d8081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec5181526020016105a081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31815260200161024081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd3181526020016105a081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec518152602001619d8081525090505f600190505f5f90505b600981101561277357612769826108cc878461262f565b9150600101612752565b5061277c61534b565b5f5b60098110156127cd576127ae610d5685836009811061279f5761279f615b73565b60200201516108cc898561262f565b8282600981106127c0576127c0615b73565b602002015260010161277e565b505f5b600981101561281357612809856108d18984600981106127f2576127f2615b73565b6020020151858560098110610bce57610bce615b73565b94506001016127d0565b5061281e84836125fb565b9695505050505050565b5f61283161536a565b61283c86828561340a565b612848868683866135b6565b612854868683866137a2565b61285f8682856139c7565b61286a868285613bbb565b61287686868386613f04565b6128818682856143b0565b61288c8682856147c2565b612897868285614b83565b61281e8185614e83565b60605f826001600160401b038111156128bc576128bc615940565b6040519080825280602002602001820160405280156128e5578160200160208202803683370190505b50905083815f815181106128fb576128fb615b73565b602090810291909101015260015b838110156129625761293d826129206001846155e0565b8151811061293057612930615b73565b6020026020010151612b4a565b82828151811061294f5761294f615b73565b6020908102919091010152600101612909565b509392505050565b5f5f8290505f604051602081526020808201526020604082015282606082015260025f516020615c8e5f395f51905f520360808201525f516020615c8e5f395f51905f5260a082015260205f60c08360055afa806129c6575f5ffd5b505f51608091909101604052949350505050565b5f516020615c8e5f395f51905f520390565b60605f826001600160401b03811115612a0757612a07615940565b604051908082528060200260200182016040528015612a30578160200160208202803683370190505b509050825b8015612b3f575f85612a486001846155e0565b81518110612a5857612a58615b73565b602002602001015190505f89600184612a7191906155e0565b601c8110612a8157612a81615b73565b602002015190505f612adc612aa0612a99858d6125fb565b60026125fb565b6108ef8b612aaf6001896155e0565b601c8110612abf57612abf615b73565b60200201516108cc612ad6886108cc60018a61262f565b8761262f565b9050612afd816108cc610d56612af7876108cc60018961262f565b86612616565b99508990508085612b0f6001876155e0565b81518110612b1f57612b1f615b73565b60200260200101818152505050505080612b3890615bdb565b9050612a35565b509695505050505050565b5f61261082836125fb565b5f600181612b6e612b6887610100614edc565b8361262f565b905080612b8e5760405163835eb8f760e01b815260040160405180910390fd5b612b96615389565b80518390525f5b7f0000000000000000000000000000000000000000000000000000000000000000811015612c7f575f612bd18260096155c9565b612bdc906001615b39565b905084835f0151826101008110612bf557612bf5615b73565b60200201525f612c06826001615b39565b90505b612c14600983615b39565b811015612c75578351612c5490612c2c6001846155e0565b6101008110612c3d57612c3d615b73565b60200201518a85601c8110610bce57610bce615b73565b8451826101008110612c6857612c68615b73565b6020020152600101612c09565b5050600101612b9d565b50608081018390525f602082018190525b610100811015612d9057612cb1612cab83608001518a6125fb565b8561262f565b8260a00151826101008110612cc857612cc8615b73565b602002015260a0820151612cf290826101008110612ce857612ce8615b73565b602002015161296a565b8260a00151826101008110612d0957612d09615b73565b602002018181525050612d4f82602001516108d1845f0151846101008110612d3357612d33615b73565b60200201518560a00151856101008110610bce57610bce615b73565b60208301526080820151612d83907f204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d66125fb565b6080830152600101612c90565b505f612da1836108cc61010061296a565b9050612db18260200151826125fb565b602083015260a0820151612dcc905f5b6020020151826125fb565b604083015260a0820151612df890612de760016101006155e0565b6101008110612dc157612dc1615b73565b60608301526040820151612e0e908a6002610bce565b60c08301819052612e6e906108d1612e468b7f204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d661262f565b60208d015160408e01516108cc91612e5d9161262f565b8e5160208901516108ef91906125fb565b60c083018190526060830151612ea391612e97916108d1906108cc8e600260200201518c61262f565b6108ef858c6003610bce565b60c08301819052159998505050505050505050565b612ec0615320565b7f00000000000000000000000000000000000000000000000000000000000000005f5b81811015612f1557612f0d858281518110612f0057612f00615b73565b6020026020010151613148565b600101612ee3565b50604051600190815b60018401811015612f7a5760208102870160208202870181515160408501528151602001516060850152805160808501525050604080830160606040850160075afa8316925060408260808460065afa90921691600101612f1e565b5080518452602081015160208501525080612fa8576040516352ec174560e11b815260040160405180910390fd5b505092915050565b612fb8615320565b5f516020615c6e5f395f51905f5282602001515f516020615c6e5f395f51905f52612fe391906155e0565b612fed9190615bbc565b60208301525090565b5f5f5f61300286613087565b9150915061300e6153cd565b82518152602080840151818301528251604080840191909152838201516060840152875160808401528782015160a0840152865160c08401528682015160e08401525161307c9161306191849101615bf0565b60405160208183030381529060405280519060200120612657565b979650505050505050565b61308f615320565b613097615320565b82516020808501516040860151606087015160cc90811b608892831b604494851b90961795909517949094178652608087015160a088015160c089015160e08a0151871b90841b91851b9092171717868401526101008701516101208801516101408901516101608a0151871b90841b91851b909217171785526101808701516101a08801516101c08901516101e09099015190951b9790911b9390911b1791909117939093179281019290925291565b805160208201515f5f516020615c6e5f395f51905f528380095f516020615c6e5f395f51905f5260035f516020615c6e5f395f51905f52838709085f516020615c6e5f395f51905f5284850914915050806131e15760405162461bcd60e51b8152602060048201526019602482015278706f696e74206973206e6f74206f6e2074686520637572766560381b60448201526064016100e5565b50505050565b6131ef615320565b6131f7615320565b6132018386614f3d565b905061320d8185614f93565b95945050505050565b81516020808401518351848301516040805194850195909552938301919091527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260608301527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed60808301527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b60a08301527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60c083015260e08201526101008101919091527f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c16101208201527f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b06101408201527f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe46101608201527f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e556101808201525f9081906101a00160405160208183030381529060405290505f5f60086001600160a01b0316836040516133ac9190615c24565b5f60405180830381855afa9150503d805f81146133e4576040519150601f19603f3d011682016040523d82523d5f602084013e6133e9565b606091505b509150915081801561281e57508080602001905181019061281e9190615c3a565b5f613416846007614ff4565b90507f183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f80000005f61347761347161344b85600361262f565b6108cc61346661345b8b5f614ff4565b6108cc8c601d614ff4565b6108cc8b601c614ff4565b836125fb565b90506134f86134ed6134d46134bb6134a2856108d16134978d6002614ff4565b6108cc8e601c614ff4565b6108d16134b08c6003614ff4565b6108cc8d601d614ff4565b6108d16134c98b6004614ff4565b6108cc8c601e614ff4565b6108d16134e28a6005614ff4565b6108cc8b601f614ff4565b6108d1886001614ff4565b9050613517816108d161350c86600161262f565b6108cc8a6027614ff4565b905061352381846125fb565b905061352f81856125fb565b8552505f905061356961355f61355461354988601c614ff4565b6108d189601f614ff4565b6108ef886024614ff4565b6108d1875f614ff4565b905061357a816108cc84600261262f565b905061358b816108cc84600161262f565b905061359781836125fb565b90506135a381846125fb565b9050808460015b60200201525050505050565b5f5f5f6135ec6135e26135ca89601c614ff4565b6108d16135d88b6012614ff4565b8a606001516125fb565b8760800151612616565b9050613625816108cc61361b6136038b601d614ff4565b6108d16136118d6013614ff4565b8c606001516125fb565b8960800151612616565b905061364a816108cc61361b61363c8b601e614ff4565b6108d16136118d6014614ff4565b905061366f816108cc61361b6136618b601f614ff4565b6108d16136118d6015614ff4565b92505f90506136936135e261368589601c614ff4565b6108d16135d88b600e614ff4565b90506136b8816108cc61361b6136aa8b601d614ff4565b6108d16136118d600f614ff4565b90506136dd816108cc61361b6136cf8b601e614ff4565b6108d16136118d6010614ff4565b9050613702816108cc61361b6136f48b601f614ff4565b6108d16136118d6011614ff4565b91505f9050613729613723613718896020614ff4565b6108d18a601a614ff4565b846125fb565b905061375e816108ef6137586137408b6028614ff4565b6108d161374e8d601b614ff4565b8c60a001516125fb565b856125fb565b905061376a81856125fb565b6040860152505f61378d61375861378289601b614ff4565b6108cc8a6028614ff4565b9050808560035b602002015250505050505050565b5f5f6138006137e86137d06137bb61361b8a6016614ff4565b6108d16137c98b6017614ff4565b8a516125fb565b6108d16137de8a6018614ff4565b89602001516125fb565b6108d16137f6896019614ff4565b88604001516125fb565b91505f61383761381e61381489601c614ff4565b8860800151612616565b6108d161382c8a6003614ff4565b6108cc8b6024614ff4565b90505f61386061384889601d614ff4565b6108d16138558b5f614ff4565b6108cc8c6025614ff4565b90505f61388a6138718a601e614ff4565b6108d161387f8c6001614ff4565b6108cc8d6026614ff4565b90506138c96138b16138a3856108d1868d5f01516125fb565b6108d1848c602001516125fb565b6108d16138bf8c6004614ff4565b8b604001516125fb565b93505050505f6138dd613723886021614ff4565b90505f6138ee613723896021614ff4565b90505f61392661390d6139028b6023614ff4565b6108d18c6006614ff4565b6108ef61391b8c6023614ff4565b6108cc8d6006614ff4565b90505f613944612b6861393987896125fb565b6108cc8d6021614ff4565b905061395081886125fb565b90505f61397861396a6139648d6006614ff4565b876125fb565b6108ef6139648e6022614ff4565b90505f6139868c6023614ff4565b90505f613996612b6883846125fb565b60808c0185905260a08c0184905290506139b0818b6125fb565b8b6006602002015250505050505050505050505050565b5f6139d35f600161262f565b90505f6139e15f600261262f565b90505f6139ef5f600361262f565b90505f613a0b613a0088601d614ff4565b6108ef89601c614ff4565b90505f613a27613a1c89601e614ff4565b6108ef8a601d614ff4565b90505f613a43613a388a601f614ff4565b6108ef8b601e614ff4565b90505f613a5f613a548b6024614ff4565b6108ef8c601f614ff4565b905083613a70816108cc818b612616565b9050613a80816108cc878a612616565b9050613a90816108cc8789612616565b9050613aa1816108cc8d6008614ff4565b9050613aad818a6125fb565b60e08b01525082613ac2816108cc818b612616565b9050613ad2816108cc868a612616565b9050613ae2816108cc8689612616565b9050613af3816108cc8d6008614ff4565b9050613aff818a6125fb565b6101008b01525081613b15816108cc818b612616565b9050613b25816108cc858a612616565b9050613b35816108cc8589612616565b9050613b46816108cc8d6008614ff4565b9050613b52818a6125fb565b6101208b01525080613b68816108cc818b612616565b9050613b78816108cc848a612616565b9050613b88816108cc8489612616565b9050613b99816108cc8d6008614ff4565b9050613ba5818a6125fb565b610140909a019990995250505050505050505050565b613bf46040518060e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b613bff84601d614ff4565b8152613c0c84601e614ff4565b6020820152613c1c846024614ff4565b6040820152613c2c846027614ff4565b6060820152613c3c846026614ff4565b6080820152613c4c846025614ff4565b60a08201525f613c5d856002614ff4565b90505f613c6a865f614ff4565b90505f613c7e8460400151855f015161262f565b90505f613c93856020015186602001516125fb565b606086015190915086905f90613ca990806125fb565b90505f613cc7613cc189602001518a606001516125fb565b886125fb565b90505f613ce6613cdf8a60a001518b60400151612616565b8a51612616565b9050613cf561396482886125fb565b9050613d1c613d16613d10613d0a848761262f565b8861262f565b84612616565b83612616565b9050613d44613d39613d2e83876125fb565b6108cc8f6009614ff4565b6108cc60018a61262f565b6101608c015250505050602085015160808601515f91613d6391612616565b90505f613d81613d778860600151886125fb565b886020015161262f565b90505f613da5613d9184876125fb565b6108d16137588b60a001518c5f015161262f565b9050613dcd613dc2613db7838c6125fb565b6108cc8e6009614ff4565b6108cc60018961262f565b6101808b0152505f9150613dee9050613de7836011612616565b87516125fb565b90505f613dfb8384612616565b9050613e078182612616565b90505f613e158360096125fb565b9050613e3e613e38613723613e318b60a001518c5f0151612616565b8b51612616565b8261262f565b60c089018190525f90613e5990613cc190613d2e908d6125fb565b9050613e6c8b600b602002015182612616565b6101608c0152505086515f9250613e939150613de790613e8c9080612616565b8851612616565b90505f613ed3613eae836108cc8a5f01518b60a0015161262f565b60208901516108ef90613ec19080612616565b6108cc8b602001518c60800151612616565b9050613ef089600c60200201516108d1613cc1613d2e858d6125fb565b89600c602002015250505050505050505050565b613f6e604051806101e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b613f86613f7c86601e614ff4565b85604001516125fb565b808252613fa5906108d1613f9b88601d614ff4565b87602001516125fb565b808252613fba906108d1613de788601c614ff4565b808252613fcc906108d1876001614ff4565b80825260208201819052613fe5906108ef87601f614ff4565b8152614000613ff5866024614ff4565b6108ef87601c614ff4565b608082015261401e614013866027614ff4565b6108ef87601f614ff4565b60608201526080810151614037906108cc81600161262f565b6101c082015260808101516140779061406d90614066906108cc60015f516020615c8e5f395f51905f526155e0565b6001612616565b82606001516125fb565b60a082018190526140ad9061409f906108cc614094896002614ff4565b6108cc8a6003614ff4565b6108cc61375888600a614ff4565b83600e60200201526101c08101516140d19061409f906108cc614094896002614ff4565b6101e084015280516140f6906108cc6140eb886002614ff4565b6108cc896003614ff4565b6101208201525f61411561410b87601f614ff4565b836020015161262f565b9050614126816108cc83600161262f565b60e0830152614143614139876026614ff4565b86604001516125fb565b60408301819052614166906108d161415c896025614ff4565b88602001516125fb565b60408301819052614186906108d161417f896024614ff4565b88516125fb565b60408301526141a3614199876027614ff4565b836040015161262f565b60408301525f6141c26141b7886026614ff4565b6108ef89601e614ff4565b90506142146141ef613471614066866080015160015f516020615c8e5f395f51905f526108cc91906155e0565b6108cc614066866040015160015f516020615c8e5f395f51905f526108cc91906155e0565b60c084015260408301516142369061422c90806125fb565b846040015161262f565b61010084015260c083015161426290614254906108cc8a6004614ff4565b6108cc6139648a600a614ff4565b6102008601526101c083015161428190614254906108cc8a6004614ff4565b6102208601526101008301516142a090614254906108cc8a6004614ff4565b61024086015260e08301516142ba906108cc896004614ff4565b6101408401526142d96142ce886025614ff4565b6108ef89601d614ff4565b6101608401526080830151614317906141b79061430c90614066906108cc60015f516020615c8e5f395f51905f526155e0565b8561016001516125fb565b61018084018190526101208401516101a0850181905261434e916108d1906108cc6143438c6005614ff4565b6108cc8d6002614ff4565b6101a08401819052835161436e91906108d1906108cc6143438c5f614ff4565b6101a084018190526101408401516143869190612616565b6101a084018190526143a0906108cc6139648a600a614ff4565b6101a0840181905285600d613794565b6143e96040518060e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b61441e6144056143fa86601c614ff4565b6108cc876025614ff4565b6108d1614413876024614ff4565b6108cc88601d614ff4565b815261446361445861443f61443487601c614ff4565b6108cc88601f614ff4565b6108d161444d88601d614ff4565b6108cc89601e614ff4565b6108ef866026614ff4565b6040820181905261447890600160441b6125fb565b6040820181905261448e906108ef866027614ff4565b6040820181905281516144a19190612616565b604082018190526144b7906108cc866005614ff4565b604082015280516144cc90600160441b6125fb565b8082526144ec906108d16144e1876024614ff4565b6108cc886025614ff4565b80825260208201819052614513906108ef61450887601e614ff4565b6108d188601f614ff4565b60208201819052614529906108cc866004614ff4565b6020820152805160608201819052614546906108d186601f614ff4565b6060820181905261456a906108ef61455f876026614ff4565b6108d1886027614ff4565b6060820181905261457f906108cc865f614ff4565b8160600181815250505f6145a861459e83602001518460400151612616565b8360600151612616565b90506145b9816108cc876003614ff4565b90506145d16145c9866025614ff4565b6140006125fb565b608083018190526145e7906108d1876024614ff4565b608083018190526145fa906140006125fb565b60808301819052614610906108d187601e614ff4565b60808301819052614623906140006125fb565b60808301819052614639906108d187601d614ff4565b6080830181905261464c906140006125fb565b60808301819052614662906108d187601c614ff4565b60808301819052614678906108ef87601f614ff4565b6080830181905261468e906108cc876005614ff4565b60808301526146a16145c9866026614ff4565b60a083018190526146b7906108d1876025614ff4565b60a083018190526146ca906140006125fb565b60a083018190526146e0906108d1876024614ff4565b60a083018190526146f3906140006125fb565b60a08301819052614709906108d187601f614ff4565b60a0830181905261471c906140006125fb565b60a08301819052614732906108d187601e614ff4565b60a08301819052614748906108ef876027614ff4565b60a0830181905261475d906108cc875f614ff4565b60a0830181905260808301515f916147759190612616565b9050614786816108cc886004614ff4565b90506147928282612616565b60c084018190526147ab906108cc61396489600b614ff4565b60c084018190528560136020020152505050505050565b6148386040518061022001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b61485161484685601c614ff4565b6108d1866002614ff4565b815261486c61486185601d614ff4565b6108d1866003614ff4565b602082015261488a61487f85601e614ff4565b6108d1866004614ff4565b60408201526148a861489d85601f614ff4565b6108d1866005614ff4565b606082015280516148da906148d3906148cc906148c590806125fb565b84516125fb565b83516125fb565b82516125fb565b608082015260208101516149189061490e90614904906148fa90806125fb565b84602001516125fb565b83602001516125fb565b82602001516125fb565b60a082015260408101516149569061494c906149429061493890806125fb565b84604001516125fb565b83604001516125fb565b82604001516125fb565b60c0820152606081015161498a9061406d906149809061497690806125fb565b84606001516125fb565b83606001516125fb565b60e0820152608081015160a08201516149a39190612616565b61010082015260c081015160e08201516149bd9190612616565b61012082015260a08101516149e1906149d69080612616565b826101200151612616565b61014082015260e0810151614a05906149fa9080612616565b826101000151612616565b610160820152610120810151614a1b9080612616565b6101e08201819052614a3c90614a319080612616565b826101600151612616565b6101e0820152610100810151614a529080612616565b6101a08201819052614a7390614a689080612616565b826101400151612616565b6101a08201819052610160820151614a8a91612616565b6101808201526101408101516101e0820151614aa69190612616565b6101c0820152614aba61347185600c614ff4565b6102008201819052610280840151610180830151614ae5926108d1916108cc906108ef8a6024614ff4565b8360146020020152614b1583601560200201516108d18361020001516108cc856101a001516108ef8a6025614ff4565b8360156020020152614b4583601660200201516108d18361020001516108cc856101c001516108ef8a6026614ff4565b8360166020020152614b7583601760200201516108d18361020001516108cc856101e001516108ef8a6027614ff4565b836017602002015250505050565b614bd56040518061016001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b5f6040518060800160405280614c0a7f10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e76125e3565b8152602001614c387f0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b6125e3565b8152602001614c657e544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac156125e3565b8152602001614c937f222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b6125e3565b90529050614cb0614ca586601c614ff4565b6108d1876002614ff4565b6101208301819052614cef90614ce490614cd990614cce90806125fb565b8561012001516125fb565b8461012001516125fb565b8361012001516125fb565b8252614cfc85601d614ff4565b6020830152614d0c85601e614ff4565b6040830152614d1c85601f614ff4565b606083015281516020830151614d449161459e91614d3a9190612616565b8460400151612616565b6080830152614d5761372386600d614ff4565b6101408301528151614d7890614d6e90835f610bce565b8360800151612616565b60a0830152614da484601860200201516108d18461014001516108cc8660a001516108ef8b6024614ff4565b6103008501526020820151614dbf90614d6e90836001610bce565b60c0830152614deb84601960200201516108d18461014001516108cc8660c001516108ef8b6025614ff4565b6103208501526040820151614e0690614d6e90836002610bce565b60e0830152614e3284601a60200201516108d18461014001516108cc8660e001516108ef8b6026614ff4565b6103408501526060820151614e4d90614d6e90836003610bce565b610100830152614e7b84601b60200201516108d18461014001516108cc8661010001516108ef8b6027614ff4565b84601b6135aa565b815160015b601c811015614ed557614ecb826108d18684601c8110614eaa57614eaa615b73565b602002015186614ebb6001876155e0565b601b8110610bce57610bce615b73565b9150600101614e88565b5092915050565b5f5f8390505f60405160208152602080820152602060408201528260608201528460808201525f516020615c8e5f395f51905f5260a082015260205f60c08360055afa80614f28575f5ffd5b505f5160809190910160405295945050505050565b614f45615320565b614f4d615320565b604051835181526020840151602082015284604082015260408160608360075afa80614f77575f5ffd5b5080518252602080820151908301526060016040529392505050565b614f9b615320565b614fa3615320565b6040518451815260208501516020820152835160408201526020840151606082015260408160808360065afa80614fd8575f5ffd5b5080518252602080820151908301526080016040529392505050565b5f8282602881111561500857615008615c59565b6029811061501857615018615b73565b60200201519392505050565b604051806103e001604052805f81526020015f81526020015f815260200161504a615320565b8152602001615057615320565b8152602001615064615320565b8152602001615071615320565b815260200161507e615320565b815260200161508b615320565b8152602001615098615320565b81526020016150a5615320565b81526020016150b2615320565b81526020016150bf615320565b81526020016150cc615320565b81526020016150d9615320565b81526020016150e6615320565b81526020016150f3615320565b8152602001615100615320565b815260200161510d615320565b815260200161511a615320565b8152602001615127615320565b8152602001615134615320565b8152602001615141615320565b815260200161514e615320565b815260200161515b615320565b8152602001615168615320565b8152602001615175615320565b8152602001615182615320565b815260200161518f615320565b815260200161519c615320565b81526020016151a9615320565b905290565b6040518061028001604052806151c26153ec565b81526020016151cf615320565b81526020016151dc615320565b81526020016151e9615320565b81526020016151f6615320565b8152602001615203615320565b8152602001615210615320565b815260200161521d615320565b815260200161522a615320565b8152602001615237615320565b815260200161524461540b565b81526020015f8152602001615257615438565b81526020015f815260200161526a615466565b8152602001615277615485565b815260200161528461536a565b815260200161518f6154b3565b6040518061052001604052806029906020820280368337509192915050565b604051806101c001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020016152ee6154b3565b81526020016152fb6154b3565b81526020015f81526020015f81526020015f81526020015f8152602001606081525090565b60405180604001604052805f81526020015f81525090565b604051806040016040528061519c615320565b6040518061012001604052806009906020820280368337509192915050565b604051806103800160405280601c906020820280368337509192915050565b6040518060e0016040528061539c6154d1565b81526020015f81526020015f81526020015f81526020015f81526020016153c16154d1565b81526020015f81525090565b6040518061010001604052806008906020820280368337509192915050565b6040518061020001604052806010906020820280368337509192915050565b60405180606001604052806003905b615422615320565b81526020019060019003908161541a5790505090565b604051806103800160405280601c905b61545061534b565b8152602001906001900390816154485790505090565b604051806105400160405280602a906020820280368337509192915050565b604051806103600160405280601b905b61549d615320565b8152602001906001900390816154955790505090565b60405180608001604052806004906020820280368337509192915050565b604051806120000160405280610100906020820280368337509192915050565b5f5f5f5f60408587031215615504575f5ffd5b84356001600160401b03811115615519575f5ffd5b8501601f81018713615529575f5ffd5b80356001600160401b0381111561553e575f5ffd5b87602082840101111561554f575f5ffd5b6020918201955093508501356001600160401b0381111561556e575f5ffd5b8501601f8101871361557e575f5ffd5b80356001600160401b03811115615593575f5ffd5b8760208260051b84010111156155a7575f5ffd5b949793965060200194505050565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417612610576126106155b5565b81810381811115612610576126106155b5565b805f5b60108110156131e15781518452602093840193909101906001016155f6565b805f5b60038110156131e15761563684835180518252602090810151910152565b6040939093019260209190910190600101615618565b805f5b601c8110156131e1578151845f5b600981101561567c57825182526020928301929091019060010161565d565b50505061012093909301926020919091019060010161564f565b805f5b602a8110156131e1578151845260209384019390910190600101615699565b805f5b601b8110156131e1576156d984835180518252602090810151910152565b60409390930192602091909101906001016156bb565b805f5b601c8110156131e15781518452602093840193909101906001016156f2565b805f5b60048110156131e1578151845260209384019390910190600101615714565b8183525f6001600160fb1b0383111561574a575f5ffd5b8260051b80836020870137939093016020019392505050565b61576e8188516155f3565b5f602088015161578c61020084018280518252602090810151910152565b5060408801518051610240840152602090810151610260840152606089015180516102808501528101516102a0840152608089015180516102c08501528101516102e084015260a0890151805161030085015281015161032084015260c0890151805161034085015281015161036084015260e089015180516103808501528101516103a084015261010089015180516103c08501528101516103e084015261012089015180516104008501520151610420830152610140880151615855610440840182615615565b5061016088015161050083015261018088015161587661052084018261564c565b506101a08801516124a08301526101c08801516158976124c0840182615696565b506101e08801516158ac612a008401826156b8565b506102008801516158c16130c08401826156ef565b506102208801516158d6613440840182615711565b5061024088015180516134c08401526020908101516134e0840152610260890151805161350085015201516135208301526135c0613540830181905261591f9083018789615733565b613560830195909552506135808101929092526135a0909101529392505050565b634e487b7160e01b5f52604160045260245ffd5b60405161014081016001600160401b038111828210171561597757615977615940565b60405290565b604051601f8201601f191681016001600160401b03811182821017156159a5576159a5615940565b604052919050565b5f60c082840312156159bd575f5ffd5b60405160c081016001600160401b03811182821017156159df576159df615940565b604090815283518252602080850151908301528381015190820152606080840151908201526080808401519082015260a0928301519281019290925250919050565b5f82601f830112615a30575f5ffd5b5f610360615a3d8161597d565b915083018185821115615a4e575f5ffd5b845b82811015615a68578051825260209182019101615a50565b509195945050505050565b5f82601f830112615a82575f5ffd5b5f610380615a3d8161597d565b5f610be0828403128015615aa1575f5ffd5b50615aaa615954565b615ab484846159ad565b8152615ac38460c08501615a21565b6020820152615ad6846104208501615a73565b60408201526107a08301516060820152615af4846107c08501615a73565b6080820152610b4083015160a0820152610b6083015160c0820152610b8083015160e0820152610ba0830151610100820152610bc09092015161012083015250919050565b80820180821115612610576126106155b5565b5f5f85851115615b5a575f5ffd5b83861115615b66575f5ffd5b5050820193919092039150565b634e487b7160e01b5f52603260045260245ffd5b5f60018201615b9857615b986155b5565b5060010190565b80356020831015612610575f19602084900360031b1b1692915050565b5f82615bd657634e487b7160e01b5f52601260045260245ffd5b500690565b5f81615be957615be96155b5565b505f190190565b5f8183825b6008811015615c14578151835260209283019290910190600101615bf5565b5050506101008201905092915050565b5f82518060208501845e5f920191825250919050565b5f60208284031215615c4a575f5ffd5b815180151581146103f4575f5ffd5b634e487b7160e01b5f52602160045260245ffdfe30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4730644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", + "deployedBytecode": "0x608060405234801561000f575f5ffd5b5060043610610029575f3560e01c8063ea50d0e41461002d575b5f5ffd5b61004061003b3660046154f1565b610054565b604051901515815260200160405180910390f35b5f5f61007f7f00000000000000000000000000000000000000000000000000000000000000006102ee565b905061008c8160206155c9565b85146100ee577f0000000000000000000000000000000000000000000000000000000000000000856100bf8360206155c9565b6040516359895a5360e01b81526004810193909352602483019190915260448201526064015b60405180910390fd5b5f6100f76103fb565b90505f61012588887f0000000000000000000000000000000000000000000000000000000000000000610410565b90506010826040015161013891906155e0565b85146101575760405163fa06659360e01b815260040160405180910390fd5b60405163995bf45760e01b81525f9073__$63cdeab5e76d5ced0e84f3fc5e357ab766$__9063995bf457906101fa9085908b908b907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090600401615763565b610be060405180830381865af4158015610216573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061023a9190615a8f565b905061028a8787808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050855185516060810151608090910151919350915060016108a9565b815160a0015261029a8282610a10565b6102b7576040516313f8744360e31b815260040160405180910390fd5b6102c2828483610c17565b6102df576040516352ec174560e11b815260040160405180910390fd5b50600198975050505050505050565b5f8060026102fe60016008615b39565b61030891906155c9565b9050610316600260036155c9565b6103209082615b39565b9050600161032f6009856155c9565b61033991906155c9565b6103439082615b39565b90506001610352816029615b39565b61035c91906155c9565b6103669082615b39565b9050610374600160026155c9565b61037e9082615b39565b905061038b6001846155c9565b6103959082615b39565b90506103a3600160046155c9565b6103ad9082615b39565b905060026103bc6001856155e0565b6103c691906155c9565b6103d09082615b39565b90506103dd6002806155c9565b6103e79082615b39565b90506103f4601082615b39565b9392505050565b610403615024565b61040b611c14565b905090565b6104186151ae565b5f805b601081101561047a57610447868387610435602083615b39565b9261044293929190615b4c565b61254e565b8351826010811061045a5761045a615b73565b6020020181815250506020826104709190615b39565b915060010161041b565b5061049e85828661048c604083615b39565b9261049993929190615b4c565b612561565b60208301526104ae604082615b39565b90506104c185828661048c604083615b39565b6040808401919091526104d49082615b39565b90506104e785828661048c604083615b39565b60608301526104f7604082615b39565b905061050a85828661048c604083615b39565b608083015261051a604082615b39565b905061052d85828661048c604083615b39565b60c083015261053d604082615b39565b905061055085828661048c604083615b39565b60e0830152610560604082615b39565b905061057385828661048c604083615b39565b60a0830152610583604082615b39565b905061059685828661048c604083615b39565b6101008301526105a7604082615b39565b90506105ba85828661048c604083615b39565b6101208301526105cb604082615b39565b90506105de85828661048c604083615b39565b610140830151526105f0604082615b39565b9050610603858286610435602083615b39565b610160830152610614602082615b39565b90505f5b83811015610692575f5b60098110156106895761063c878488610435602083615b39565b84610180015183601c811061065357610653615b73565b6020020151826009811061066957610669615b73565b60200201818152505060208361067f9190615b39565b9250600101610622565b50600101610618565b505f5b6106a160016029615b39565b8110156106f0576106b9868387610435602083615b39565b836101c0015182602a81106106d0576106d0615b73565b6020020181815250506020826106e69190615b39565b9150600101610695565b50610702858286610435602083615b39565b6101a0830152610713602082615b39565b905061072685828661048c604083615b39565b6101408301516020015261073b604082615b39565b905061074e85828661048c604083615b39565b61014083015160026020020152610766604082615b39565b90505f5b6107756001856155e0565b8110156107be5761078d86838761048c604083615b39565b836101e0015182601b81106107a4576107a4615b73565b60200201526107b4604083615b39565b915060010161076a565b505f5b83811015610811576107da868387610435602083615b39565b83610200015182601c81106107f1576107f1615b73565b6020020181815250506020826108079190615b39565b91506001016107c1565b505f5b60048110156108655761082e868387610435602083615b39565b836102200151826004811061084557610845615b73565b60200201818152505060208261085b9190615b39565b9150600101610814565b5061087785828661048c604083615b39565b610240830152610888604082615b39565b905061089b85828661048c604083615b39565b610260830152509392505050565b5f600180826108d6866108d1896108cc6108c78a6310000000615b39565b6125e3565b6125fb565b612616565b90505f6108f4876108ef8a6108cc6108c78b6001615b39565b61262f565b90505f5b61092360107f00000000000000000000000000000000000000000000000000000000000000006155e0565b811015610990575f61094d8c838151811061094057610940615b73565b6020026020010151612657565b905061095d866108cc8684612616565b955061096d856108cc8584612616565b9450610979848b612616565b9350610985838b61262f565b9250506001016108f8565b505f5b60108110156109f7575f8a82601081106109af576109af615b73565b602002015190506109c4866108cc8684612616565b95506109d4856108cc8584612616565b94506109e0848b612616565b93506109ec838b61262f565b925050600101610993565b50610a02848461266c565b9a9950505050505050505050565b5f5f610a2583606001518561016001516125fb565b905060015f5b7f0000000000000000000000000000000000000000000000000000000000000000811015610b17575f86610180015182601c8110610a6b57610a6b615b73565b602002015180519091505f90610a89908360015b6020020151612616565b9050848114610aab576040516313f8744360e31b815260040160405180910390fd5b5f876080015184601c8110610ac257610ac2615b73565b60200201519050610ad3838261267a565b9550610b07856108cc60016108d1856108cc8e604001518b601c8110610afb57610afb615b73565b6020020151600161262f565b9450505050806001019050610a2b565b50610b20615291565b5f5b6029811015610b70576101c0870151610b3c600183615b39565b602a8110610b4c57610b4c615b73565b6020020151828260298110610b6357610b63615b73565b6020020152600101610b22565b505f610b8582875f0151886020015186612828565b9050600160025b7f0000000000000000000000000000000000000000000000000000000000000000811015610be257610bd882896080015183601c8110610bce57610bce615b73565b60200201516125fb565b9150600101610b8c565b50610c08610bf5836108cc60018561262f565b6108d18a6101a001518a606001516125fb565b94909414979650505050505050565b5f610c206152b0565b5f610c4f8460c001517f00000000000000000000000000000000000000000000000000000000000000006128a1565b90505f7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03811115610c8a57610c8a615940565b604051908082528060200260200182016040528015610cb3578160200160208202803683370190505b5090505f7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03811115610cef57610cef615940565b604051908082528060200260200182016040528015610d2857816020015b610d15615320565b815260200190600190039081610d0d5790505b509050610d5b610d56876101000151855f81518110610d4957610d49615b73565b602002602001015161262f565b61296a565b84610120018181525050610d90610d56876101000151855f81518110610d8357610d83615b73565b6020026020010151612616565b610140850181905261012085015160e0880151610db1926108d191906125fb565b845260c0860151610de190610dc59061296a565b6108cc8661012001516108ef8a60e001518961014001516125fb565b6020850152815160019083905f90610dfb57610dfb615b73565b602002602001018181525050876102400151815f81518110610e1f57610e1f615b73565b6020908102919091010152600160a08501525f60c08501528351610e42906129da565b60408501526020840151610e55906129da565b606085015260015b610e6960016024615b39565b8111610f0a57610e8185604001518660a001516125fb565b838281518110610e9357610e93615b73565b602002602001018181525050610edc8560c001516108d18b6101c00151600185610ebd91906155e0565b602a8110610ecd57610ecd615b73565b60200201518860a001516125fb565b60c086015260a08086015190880151610ef591906125fb565b60a0860152610f0381615b87565b9050610e5d565b505f5b6005811015610fe3575f610f22601e83615b39565b90505f610f3160016024615b39565b610f3b9084615b39565b9050610f6c858381518110610f5257610f52615b73565b60200260200101516108d189606001518a60a001516125fb565b858381518110610f7e57610f7e615b73565b602002602001018181525050610fbb8760c001516108d18d6101c0015184602a8110610fac57610fac615b73565b60200201518a60a001516125fb565b60c088015260a080880151908a0151610fd491906125fb565b60a08801525050600101610f0d565b50876020015181600181518110610ffc57610ffc615b73565b602002602001018190525086606001518160028151811061101f5761101f615b73565b602002602001018190525086608001518160038151811061104257611042615b73565b60200260200101819052508660a001518160048151811061106557611065615b73565b60200260200101819052508660c001518160058151811061108857611088615b73565b60200260200101819052508660e00151816006815181106110ab576110ab615b73565b6020026020010181905250866101000151816007815181106110cf576110cf615b73565b6020026020010181905250866101200151816008815181106110f3576110f3615b73565b60200260200101819052508661014001518160098151811061111757611117615b73565b602002602001018190525086610160015181600a8151811061113b5761113b615b73565b6020026020010181905250866101c0015181600b8151811061115f5761115f615b73565b602002602001018190525086610180015181600c8151811061118357611183615b73565b6020026020010181905250866101a0015181600d815181106111a7576111a7615b73565b6020026020010181905250866101e0015181600e815181106111cb576111cb615b73565b602002602001018190525086610200015181600f815181106111ef576111ef615b73565b60200260200101819052508661022001518160108151811061121357611213615b73565b60200260200101819052508661024001518160118151811061123757611237615b73565b60200260200101819052508661026001518160128151811061125b5761125b615b73565b60200260200101819052508661028001518160138151811061127f5761127f615b73565b6020026020010181905250866102a00151816014815181106112a3576112a3615b73565b6020026020010181905250866102c00151816015815181106112c7576112c7615b73565b6020026020010181905250866102e00151816016815181106112eb576112eb615b73565b60200260200101819052508661030001518160178151811061130f5761130f615b73565b60200260200101819052508661032001518160188151811061133357611333615b73565b60200260200101819052508661034001518160198151811061135757611357615b73565b602002602001018190525086610360015181601a8151811061137b5761137b615b73565b602002602001018190525086610380015181601b8151811061139f5761139f615b73565b6020026020010181905250866103a0015181601c815181106113c3576113c3615b73565b6020026020010181905250866103c0015181601d815181106113e7576113e7615b73565b6020026020010181905250876040015181601e8151811061140a5761140a615b73565b6020026020010181905250876060015181601f8151811061142d5761142d615b73565b602002602001018190525087608001518160208151811061145057611450615b73565b60200260200101819052508760a001518160218151811061147357611473615b73565b60200260200101819052508761012001518160228151811061149757611497615b73565b6020026020010181905250876101000151816023815181106114bb576114bb615b73565b60200260200101819052508760c00151816024815181106114de576114de615b73565b60200260200101819052508760e001518160258151811061150157611501615b73565b60200260200101819052505f61154787608001518660c001518b6102000151877f00000000000000000000000000000000000000000000000000000000000000006129ec565b9050611571815f8151811061155e5761155e615b73565b60200260200101518661012001516125fb565b608086018190526102008a01515160e08901516115a292916108d19161159791906125fb565b8861014001516125fb565b608086015260e08701516115b590612b4a565b60a08601525f6115c760016024615b39565b6115d2906001615b39565b90505f5b61160160017f00000000000000000000000000000000000000000000000000000000000000006155e0565b811015611812575f61163460017f00000000000000000000000000000000000000000000000000000000000000006155e0565b8210159050806117a357611668610d568b6101000151898560016116589190615b39565b81518110610d4957610d49615b73565b6101208901526101008a015161169890610d569089611688866001615b39565b81518110610d8357610d83615b73565b61014089015260a08801516101208901516116b391906125fb565b61016089015260a088015160e08b01516116db916116d0916125fb565b8961014001516125fb565b61018089018190526116fe906116f0906129da565b6108d18a61016001516129da565b866117098486615b39565b8151811061171957611719615b73565b6020026020010181815250505f6117528961018001518e61020001518560016117429190615b39565b601c8110610bce57610bce615b73565b905061178c816108d18b61016001518887600161176f9190615b39565b8151811061177f5761177f615b73565b60200260200101516125fb565b905061179c896080015182612616565b60808a0152505b6117c26117b88960a001518c60e001516125fb565b8b60e001516125fb565b60a08901526101e08c015182601b81106117de576117de615b73565b6020020151856117ee8486615b39565b815181106117fe576117fe615b73565b6020908102919091010152506001016115d6565b5061183e60017f00000000000000000000000000000000000000000000000000000000000000006155e0565b6118489082615b39565b90506118686118608961010001518a60c0015161262f565b60019061266c565b60e08701515261010088015160c08901516118ac91611860916108ef907f07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76906125fb565b60e0878101805160200192909252815180516040909101529051805160609091015260a0870151908901516118ee916118e4916125fb565b8960e001516125fb565b60a08701525f5b600481101561199c575f6119278860e00151836004811061191857611918615b73565b60200201518960a001516125fb565b9050611932816129da565b886101000151836004811061194957611949615b73565b602002015260a088015160e08b015161196291906125fb565b8860a001818152505061198e88608001516108d1838f61022001518660048110610bce57610bce615b73565b6080890152506001016118f5565b506101008601515184518590839081106119b8576119b8615b73565b602090810291909101810191909152610100870151908101516119dc916002610a7f565b846119e8836001615b39565b815181106119f8576119f8615b73565b60209081029190910101526101008601516060015184611a19836002615b39565b81518110611a2957611a29615b73565b60209081029190910101525f5b6003811015611a8b578a61014001518160038110611a5657611a56615b73565b60200201518483611a6681615b87565b945081518110611a7857611a78615b73565b6020908102919091010152600101611a36565b506040518060400160405280600181526020016002815250838281518110611ab557611ab5615b73565b60200260200101819052508560800151848280611ad190615b87565b935081518110611ae357611ae3615b73565b602002602001018181525050611b0d8a61022001518960c001518a608001518d6101a00151612b55565b611b2a5760405163a2a2ac8360e01b815260040160405180910390fd5b5f8a6102600151905080848381518110611b4657611b46615b73565b6020026020010181905250886101000151858381518110611b6957611b69615b73565b602002602001018181525050611b7d615338565b611b878587612eb8565b8152611b9282612fb0565b602082018190528c5182515f92611baa929190612ff6565b90505f5f611bba8f5f0151613087565b91509150611bc782613148565b611bd081613148565b8351611bdd9083856131e7565b84526020840151611bef9082856131e7565b602085018190528451611c0191613216565b9f9e505050505050505050505050505050565b611c1c615024565b50604080516103e0810182526204000081526012602080830191909152601682840152825180840184527f1c34241ed7bd1ea35de9fe54f11e3deca92247eda67d756489374539ed08082881527f1fa29ca2f59dfccf9e71342fe789ae8e2aa41079798daf79b8e02d06a510a4b7818301526060830152825180840184527f147ff414795e16dd9d8ad7758205875dcbb2feca77004005326a37295450902e81527f2ce19f89d76fc3a0e68df64342ecb603eb7743838eaab9cad94f597ff49c3d52818301526080830152825180840184527f131fd9af41c938c8b1b6e0086eb4f08ba4075a71288d83449d8d5eac6247099a81527f072066e82cbf467dd3c411fdaf6242d1ca23f1b37710849520bf2cd4b714e7958183015260a0830152825180840184527f02f04c8042c961b5c40c8617efb47c118b71d76c64259724eb802e8efdada48f81527f266704b76a579143a49d75c18ad41e4c8664833a345240b18787c26e832b2c2e8183015260c0830152825180840184527f0efe5ef9accef9728a562f5e9ea4fd60b0957f6e559689936896c9d7e94823ba81527f280a5dfe9d34183e42c5f275391576ae8e2891ab077920340756a44d9131ca708183015260e0830152825180840184527f2ecfda953e756b07faf8e3954fe5012494ee0237dea45538b88e34d949a516f981527f0677e9dcbd562e682c44632da1d4b5ffecdc6c9129adb89089c020384a34d28181830152610100830152825180840184527f15729599ef3888a89db8f52912b65cdfc12205eb78f54bd9f4742df51d35d9d281527f1890a0e4fb04b07215d2034d92fc6ad93cb3670761c8c93df420b3a7d89baefa81830152610120830152825180840184527f2aa5463c894b45d28a4b5ea9bed212fc3dd803ce12033d85358d4432a0655c6a81527f1397cec5342f4f86432b983400c0e56ceb029e78abeba47fc958779df917e1d781830152610140830152825180840184527f1617218df444678e9120d0de7f480a6d0bf6098c6e77009e8cc81a5bb0e820fc81527f1f03683cfa36530952dc9494533856af2e1487bed9e76f07b4afda82f3afb12981830152610160830152825180840184527f161d4f65fef69c1bbc2a671569c92da155e9374fabef1d260eb8776142c73bd881527f268e5909d8cab6c89c8064ee0fc25a7fc541a3238f93dea808d9fda94242113281830152610180830152825180840184527f26be72a59de37228c424b5ffc3149b2489578d0f09ba433097d9c8596473da5f81527f1e1d0abff71ff2bf7e0833e32af3baff87b41a17a2cf187367c04ced7bc5ff7a818301526101a0830152825180840184527f2229994889f103841fc6d5f7a4b209e045992468aec5a30800606e5c1918007c81527f016891f753e67c0d4349565eaeb8c02d828793eb835c20c532e771ec6918702f818301526101c0830152825180840184527f099ba64dd88f156dc06f527c2bac7b2c7c86d903e24ac09e47f833cfd6f903b881527f067f7914c30e0ca5dab695d46a6b6644f048d04e0938eea01c282d7ba0f043cc818301526101e0830152825180840184527f281fd5754fbb77ac2ef58f72fe142cf865a329c0611b38919dd1a36feb99436081527f15f7f262eba8a9b6df7a0928493a1399b33b1f765cb31c934d1cbde777c3bdfd81830152610200830152825180840184527f04ff10ec802daeb081f77ae5a048da6c5695f760c4b705fa2d0ae3cd5e76a9ae81527f1242b0b182171f1a737e13750ff2ff10762a6fcb1d4280ce14b5b3a43295610d81830152610220830152825180840184527f20bb81a1dc9b0c8d3cbcc2761b6f6751e08be09b384efd9b56bd5f81ca29c13681527f10d80cf403b8ff3de754a57331c5dd143cb0ae9e0382550efd8f20b353e274b581830152610240830152825180840184527f047c3bf9b1df8256aa780ba25fac792e3a8de5e460d9cb3e41bf8686653a2b8381527f1f13e4fa50f1ff9430c4f58316e6c3f1ccdcf87540d6a19add847606138d93bf81830152610260830152825180840184527f087819484fd402168d10636c0e1ff16095086cfebb16fa72ecd905faa77611e281527f061b92f2194d55921ff02e4362fe5b4f59eac905edb027e656700a56806a5e8981830152610280830152825180840184527f30302f92d6b476ab0407ba6570e2c869057a534fafc859946c315715fd37397d81527f2bccc49a1f124a8ea4933e1f65cd744c4ef91d17b429bc27f44ed533ff179ad9818301526102a0830152825180840184527f2fa778d2df483e2c7535aa5b015aba244987291fe5d0f78655332f35dbb983a281527f0eaaa6e5c290180d0979a51caf8592f4014402d09f8fdee08e2d2b98596b1743818301526102c0830152825180840184527f035d549bad95f400a2d94f6032216957b08b7ab07ab972d8403663454ecb46e481527f02512a1a401fd0832cea115ea34675373aa09faa59149b8ffd71fe76be90c306818301526102e0830152825180840184527f07377ddbcd535099c7600f69e539e1d41cdfd8705405334687c4f5b741226da881527f06608b7e3be5235cc0368ce844f8c822b118e54f993626431efa578a881b1e0181830152610300830152825180840184527f099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d2681527e15b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f81830152610320830152825180840184527f1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e81527f305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d1981830152610340830152825180840184527f061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d81527f1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e81830152610360830152825180840184527f043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce81527f261522c4089330646aff96736194949330952ae74c573d1686d9cb4a007338548183015261038083015282518084018452600181526002818301526103a083015282518084019093527f1280735205fd7e4a58ee8493d273e5afa2f13bfb7879873c4095a5cbfc646a4183527f155c3c102286dfdbfe78f876a3c74b9c36a02bb8197154c7d9fedb371f55b779908301526103c081019190915290565b5f6103f461255c8385615b9f565b612657565b612569615320565b60408051808201909152805f516020615c6e5f395f51905f5261258f60205f8789615b4c565b61259891615b9f565b6125a29190615bbc565b81526020908101905f516020615c6e5f395f51905f52906125c7906040908789615b4c565b6125d091615b9f565b6125da9190615bbc565b90529392505050565b5f5f516020615c8e5f395f51905f52825b0692915050565b5f5f516020615c8e5f395f51905f5282840990505b92915050565b5f5f516020615c8e5f395f51905f528284089392505050565b5f5f516020615c8e5f395f51905f52825f516020615c8e5f395f51905f520384089392505050565b5f5f516020615c8e5f395f51905f52826125f4565b5f6103f4836108cc8461296a565b5f5f604051806101200160405280619d8081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec5181526020016105a081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31815260200161024081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd3181526020016105a081526020017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec518152602001619d8081525090505f600190505f5f90505b600981101561277357612769826108cc878461262f565b9150600101612752565b5061277c61534b565b5f5b60098110156127cd576127ae610d5685836009811061279f5761279f615b73565b60200201516108cc898561262f565b8282600981106127c0576127c0615b73565b602002015260010161277e565b505f5b600981101561281357612809856108d18984600981106127f2576127f2615b73565b6020020151858560098110610bce57610bce615b73565b94506001016127d0565b5061281e84836125fb565b9695505050505050565b5f61283161536a565b61283c86828561340a565b612848868683866135b6565b612854868683866137a2565b61285f8682856139c7565b61286a868285613bbb565b61287686868386613f04565b6128818682856143b0565b61288c8682856147c2565b612897868285614b83565b61281e8185614e83565b60605f826001600160401b038111156128bc576128bc615940565b6040519080825280602002602001820160405280156128e5578160200160208202803683370190505b50905083815f815181106128fb576128fb615b73565b602090810291909101015260015b838110156129625761293d826129206001846155e0565b8151811061293057612930615b73565b6020026020010151612b4a565b82828151811061294f5761294f615b73565b6020908102919091010152600101612909565b509392505050565b5f5f8290505f604051602081526020808201526020604082015282606082015260025f516020615c8e5f395f51905f520360808201525f516020615c8e5f395f51905f5260a082015260205f60c08360055afa806129c6575f5ffd5b505f51608091909101604052949350505050565b5f516020615c8e5f395f51905f520390565b60605f826001600160401b03811115612a0757612a07615940565b604051908082528060200260200182016040528015612a30578160200160208202803683370190505b509050825b8015612b3f575f85612a486001846155e0565b81518110612a5857612a58615b73565b602002602001015190505f89600184612a7191906155e0565b601c8110612a8157612a81615b73565b602002015190505f612adc612aa0612a99858d6125fb565b60026125fb565b6108ef8b612aaf6001896155e0565b601c8110612abf57612abf615b73565b60200201516108cc612ad6886108cc60018a61262f565b8761262f565b9050612afd816108cc610d56612af7876108cc60018961262f565b86612616565b99508990508085612b0f6001876155e0565b81518110612b1f57612b1f615b73565b60200260200101818152505050505080612b3890615bdb565b9050612a35565b509695505050505050565b5f61261082836125fb565b5f600181612b6e612b6887610100614edc565b8361262f565b905080612b8e5760405163835eb8f760e01b815260040160405180910390fd5b612b96615389565b80518390525f5b7f0000000000000000000000000000000000000000000000000000000000000000811015612c7f575f612bd18260096155c9565b612bdc906001615b39565b905084835f0151826101008110612bf557612bf5615b73565b60200201525f612c06826001615b39565b90505b612c14600983615b39565b811015612c75578351612c5490612c2c6001846155e0565b6101008110612c3d57612c3d615b73565b60200201518a85601c8110610bce57610bce615b73565b8451826101008110612c6857612c68615b73565b6020020152600101612c09565b5050600101612b9d565b50608081018390525f602082018190525b610100811015612d9057612cb1612cab83608001518a6125fb565b8561262f565b8260a00151826101008110612cc857612cc8615b73565b602002015260a0820151612cf290826101008110612ce857612ce8615b73565b602002015161296a565b8260a00151826101008110612d0957612d09615b73565b602002018181525050612d4f82602001516108d1845f0151846101008110612d3357612d33615b73565b60200201518560a00151856101008110610bce57610bce615b73565b60208301526080820151612d83907f204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d66125fb565b6080830152600101612c90565b505f612da1836108cc61010061296a565b9050612db18260200151826125fb565b602083015260a0820151612dcc905f5b6020020151826125fb565b604083015260a0820151612df890612de760016101006155e0565b6101008110612dc157612dc1615b73565b60608301526040820151612e0e908a6002610bce565b60c08301819052612e6e906108d1612e468b7f204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d661262f565b60208d015160408e01516108cc91612e5d9161262f565b8e5160208901516108ef91906125fb565b60c083018190526060830151612ea391612e97916108d1906108cc8e600260200201518c61262f565b6108ef858c6003610bce565b60c08301819052159998505050505050505050565b612ec0615320565b7f00000000000000000000000000000000000000000000000000000000000000005f5b81811015612f1557612f0d858281518110612f0057612f00615b73565b6020026020010151613148565b600101612ee3565b50604051600190815b60018401811015612f7a5760208102870160208202870181515160408501528151602001516060850152805160808501525050604080830160606040850160075afa8316925060408260808460065afa90921691600101612f1e565b5080518452602081015160208501525080612fa8576040516352ec174560e11b815260040160405180910390fd5b505092915050565b612fb8615320565b5f516020615c6e5f395f51905f5282602001515f516020615c6e5f395f51905f52612fe391906155e0565b612fed9190615bbc565b60208301525090565b5f5f5f61300286613087565b9150915061300e6153cd565b82518152602080840151818301528251604080840191909152838201516060840152875160808401528782015160a0840152865160c08401528682015160e08401525161307c9161306191849101615bf0565b60405160208183030381529060405280519060200120612657565b979650505050505050565b61308f615320565b613097615320565b82516020808501516040860151606087015160cc90811b608892831b604494851b90961795909517949094178652608087015160a088015160c089015160e08a0151871b90841b91851b9092171717868401526101008701516101208801516101408901516101608a0151871b90841b91851b909217171785526101808701516101a08801516101c08901516101e09099015190951b9790911b9390911b1791909117939093179281019290925291565b805160208201515f5f516020615c6e5f395f51905f528380095f516020615c6e5f395f51905f5260035f516020615c6e5f395f51905f52838709085f516020615c6e5f395f51905f5284850914915050806131e15760405162461bcd60e51b8152602060048201526019602482015278706f696e74206973206e6f74206f6e2074686520637572766560381b60448201526064016100e5565b50505050565b6131ef615320565b6131f7615320565b6132018386614f3d565b905061320d8185614f93565b95945050505050565b81516020808401518351848301516040805194850195909552938301919091527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260608301527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed60808301527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b60a08301527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60c083015260e08201526101008101919091527f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c16101208201527f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b06101408201527f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe46101608201527f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e556101808201525f9081906101a00160405160208183030381529060405290505f5f60086001600160a01b0316836040516133ac9190615c24565b5f60405180830381855afa9150503d805f81146133e4576040519150601f19603f3d011682016040523d82523d5f602084013e6133e9565b606091505b509150915081801561281e57508080602001905181019061281e9190615c3a565b5f613416846007614ff4565b90507f183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f80000005f61347761347161344b85600361262f565b6108cc61346661345b8b5f614ff4565b6108cc8c601d614ff4565b6108cc8b601c614ff4565b836125fb565b90506134f86134ed6134d46134bb6134a2856108d16134978d6002614ff4565b6108cc8e601c614ff4565b6108d16134b08c6003614ff4565b6108cc8d601d614ff4565b6108d16134c98b6004614ff4565b6108cc8c601e614ff4565b6108d16134e28a6005614ff4565b6108cc8b601f614ff4565b6108d1886001614ff4565b9050613517816108d161350c86600161262f565b6108cc8a6027614ff4565b905061352381846125fb565b905061352f81856125fb565b8552505f905061356961355f61355461354988601c614ff4565b6108d189601f614ff4565b6108ef886024614ff4565b6108d1875f614ff4565b905061357a816108cc84600261262f565b905061358b816108cc84600161262f565b905061359781836125fb565b90506135a381846125fb565b9050808460015b60200201525050505050565b5f5f5f6135ec6135e26135ca89601c614ff4565b6108d16135d88b6012614ff4565b8a606001516125fb565b8760800151612616565b9050613625816108cc61361b6136038b601d614ff4565b6108d16136118d6013614ff4565b8c606001516125fb565b8960800151612616565b905061364a816108cc61361b61363c8b601e614ff4565b6108d16136118d6014614ff4565b905061366f816108cc61361b6136618b601f614ff4565b6108d16136118d6015614ff4565b92505f90506136936135e261368589601c614ff4565b6108d16135d88b600e614ff4565b90506136b8816108cc61361b6136aa8b601d614ff4565b6108d16136118d600f614ff4565b90506136dd816108cc61361b6136cf8b601e614ff4565b6108d16136118d6010614ff4565b9050613702816108cc61361b6136f48b601f614ff4565b6108d16136118d6011614ff4565b91505f9050613729613723613718896020614ff4565b6108d18a601a614ff4565b846125fb565b905061375e816108ef6137586137408b6028614ff4565b6108d161374e8d601b614ff4565b8c60a001516125fb565b856125fb565b905061376a81856125fb565b6040860152505f61378d61375861378289601b614ff4565b6108cc8a6028614ff4565b9050808560035b602002015250505050505050565b5f5f6138006137e86137d06137bb61361b8a6016614ff4565b6108d16137c98b6017614ff4565b8a516125fb565b6108d16137de8a6018614ff4565b89602001516125fb565b6108d16137f6896019614ff4565b88604001516125fb565b91505f61383761381e61381489601c614ff4565b8860800151612616565b6108d161382c8a6003614ff4565b6108cc8b6024614ff4565b90505f61386061384889601d614ff4565b6108d16138558b5f614ff4565b6108cc8c6025614ff4565b90505f61388a6138718a601e614ff4565b6108d161387f8c6001614ff4565b6108cc8d6026614ff4565b90506138c96138b16138a3856108d1868d5f01516125fb565b6108d1848c602001516125fb565b6108d16138bf8c6004614ff4565b8b604001516125fb565b93505050505f6138dd613723886021614ff4565b90505f6138ee613723896021614ff4565b90505f61392661390d6139028b6023614ff4565b6108d18c6006614ff4565b6108ef61391b8c6023614ff4565b6108cc8d6006614ff4565b90505f613944612b6861393987896125fb565b6108cc8d6021614ff4565b905061395081886125fb565b90505f61397861396a6139648d6006614ff4565b876125fb565b6108ef6139648e6022614ff4565b90505f6139868c6023614ff4565b90505f613996612b6883846125fb565b60808c0185905260a08c0184905290506139b0818b6125fb565b8b6006602002015250505050505050505050505050565b5f6139d35f600161262f565b90505f6139e15f600261262f565b90505f6139ef5f600361262f565b90505f613a0b613a0088601d614ff4565b6108ef89601c614ff4565b90505f613a27613a1c89601e614ff4565b6108ef8a601d614ff4565b90505f613a43613a388a601f614ff4565b6108ef8b601e614ff4565b90505f613a5f613a548b6024614ff4565b6108ef8c601f614ff4565b905083613a70816108cc818b612616565b9050613a80816108cc878a612616565b9050613a90816108cc8789612616565b9050613aa1816108cc8d6008614ff4565b9050613aad818a6125fb565b60e08b01525082613ac2816108cc818b612616565b9050613ad2816108cc868a612616565b9050613ae2816108cc8689612616565b9050613af3816108cc8d6008614ff4565b9050613aff818a6125fb565b6101008b01525081613b15816108cc818b612616565b9050613b25816108cc858a612616565b9050613b35816108cc8589612616565b9050613b46816108cc8d6008614ff4565b9050613b52818a6125fb565b6101208b01525080613b68816108cc818b612616565b9050613b78816108cc848a612616565b9050613b88816108cc8489612616565b9050613b99816108cc8d6008614ff4565b9050613ba5818a6125fb565b610140909a019990995250505050505050505050565b613bf46040518060e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b613bff84601d614ff4565b8152613c0c84601e614ff4565b6020820152613c1c846024614ff4565b6040820152613c2c846027614ff4565b6060820152613c3c846026614ff4565b6080820152613c4c846025614ff4565b60a08201525f613c5d856002614ff4565b90505f613c6a865f614ff4565b90505f613c7e8460400151855f015161262f565b90505f613c93856020015186602001516125fb565b606086015190915086905f90613ca990806125fb565b90505f613cc7613cc189602001518a606001516125fb565b886125fb565b90505f613ce6613cdf8a60a001518b60400151612616565b8a51612616565b9050613cf561396482886125fb565b9050613d1c613d16613d10613d0a848761262f565b8861262f565b84612616565b83612616565b9050613d44613d39613d2e83876125fb565b6108cc8f6009614ff4565b6108cc60018a61262f565b6101608c015250505050602085015160808601515f91613d6391612616565b90505f613d81613d778860600151886125fb565b886020015161262f565b90505f613da5613d9184876125fb565b6108d16137588b60a001518c5f015161262f565b9050613dcd613dc2613db7838c6125fb565b6108cc8e6009614ff4565b6108cc60018961262f565b6101808b0152505f9150613dee9050613de7836011612616565b87516125fb565b90505f613dfb8384612616565b9050613e078182612616565b90505f613e158360096125fb565b9050613e3e613e38613723613e318b60a001518c5f0151612616565b8b51612616565b8261262f565b60c089018190525f90613e5990613cc190613d2e908d6125fb565b9050613e6c8b600b602002015182612616565b6101608c0152505086515f9250613e939150613de790613e8c9080612616565b8851612616565b90505f613ed3613eae836108cc8a5f01518b60a0015161262f565b60208901516108ef90613ec19080612616565b6108cc8b602001518c60800151612616565b9050613ef089600c60200201516108d1613cc1613d2e858d6125fb565b89600c602002015250505050505050505050565b613f6e604051806101e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b613f86613f7c86601e614ff4565b85604001516125fb565b808252613fa5906108d1613f9b88601d614ff4565b87602001516125fb565b808252613fba906108d1613de788601c614ff4565b808252613fcc906108d1876001614ff4565b80825260208201819052613fe5906108ef87601f614ff4565b8152614000613ff5866024614ff4565b6108ef87601c614ff4565b608082015261401e614013866027614ff4565b6108ef87601f614ff4565b60608201526080810151614037906108cc81600161262f565b6101c082015260808101516140779061406d90614066906108cc60015f516020615c8e5f395f51905f526155e0565b6001612616565b82606001516125fb565b60a082018190526140ad9061409f906108cc614094896002614ff4565b6108cc8a6003614ff4565b6108cc61375888600a614ff4565b83600e60200201526101c08101516140d19061409f906108cc614094896002614ff4565b6101e084015280516140f6906108cc6140eb886002614ff4565b6108cc896003614ff4565b6101208201525f61411561410b87601f614ff4565b836020015161262f565b9050614126816108cc83600161262f565b60e0830152614143614139876026614ff4565b86604001516125fb565b60408301819052614166906108d161415c896025614ff4565b88602001516125fb565b60408301819052614186906108d161417f896024614ff4565b88516125fb565b60408301526141a3614199876027614ff4565b836040015161262f565b60408301525f6141c26141b7886026614ff4565b6108ef89601e614ff4565b90506142146141ef613471614066866080015160015f516020615c8e5f395f51905f526108cc91906155e0565b6108cc614066866040015160015f516020615c8e5f395f51905f526108cc91906155e0565b60c084015260408301516142369061422c90806125fb565b846040015161262f565b61010084015260c083015161426290614254906108cc8a6004614ff4565b6108cc6139648a600a614ff4565b6102008601526101c083015161428190614254906108cc8a6004614ff4565b6102208601526101008301516142a090614254906108cc8a6004614ff4565b61024086015260e08301516142ba906108cc896004614ff4565b6101408401526142d96142ce886025614ff4565b6108ef89601d614ff4565b6101608401526080830151614317906141b79061430c90614066906108cc60015f516020615c8e5f395f51905f526155e0565b8561016001516125fb565b61018084018190526101208401516101a0850181905261434e916108d1906108cc6143438c6005614ff4565b6108cc8d6002614ff4565b6101a08401819052835161436e91906108d1906108cc6143438c5f614ff4565b6101a084018190526101408401516143869190612616565b6101a084018190526143a0906108cc6139648a600a614ff4565b6101a0840181905285600d613794565b6143e96040518060e001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b61441e6144056143fa86601c614ff4565b6108cc876025614ff4565b6108d1614413876024614ff4565b6108cc88601d614ff4565b815261446361445861443f61443487601c614ff4565b6108cc88601f614ff4565b6108d161444d88601d614ff4565b6108cc89601e614ff4565b6108ef866026614ff4565b6040820181905261447890600160441b6125fb565b6040820181905261448e906108ef866027614ff4565b6040820181905281516144a19190612616565b604082018190526144b7906108cc866005614ff4565b604082015280516144cc90600160441b6125fb565b8082526144ec906108d16144e1876024614ff4565b6108cc886025614ff4565b80825260208201819052614513906108ef61450887601e614ff4565b6108d188601f614ff4565b60208201819052614529906108cc866004614ff4565b6020820152805160608201819052614546906108d186601f614ff4565b6060820181905261456a906108ef61455f876026614ff4565b6108d1886027614ff4565b6060820181905261457f906108cc865f614ff4565b8160600181815250505f6145a861459e83602001518460400151612616565b8360600151612616565b90506145b9816108cc876003614ff4565b90506145d16145c9866025614ff4565b6140006125fb565b608083018190526145e7906108d1876024614ff4565b608083018190526145fa906140006125fb565b60808301819052614610906108d187601e614ff4565b60808301819052614623906140006125fb565b60808301819052614639906108d187601d614ff4565b6080830181905261464c906140006125fb565b60808301819052614662906108d187601c614ff4565b60808301819052614678906108ef87601f614ff4565b6080830181905261468e906108cc876005614ff4565b60808301526146a16145c9866026614ff4565b60a083018190526146b7906108d1876025614ff4565b60a083018190526146ca906140006125fb565b60a083018190526146e0906108d1876024614ff4565b60a083018190526146f3906140006125fb565b60a08301819052614709906108d187601f614ff4565b60a0830181905261471c906140006125fb565b60a08301819052614732906108d187601e614ff4565b60a08301819052614748906108ef876027614ff4565b60a0830181905261475d906108cc875f614ff4565b60a0830181905260808301515f916147759190612616565b9050614786816108cc886004614ff4565b90506147928282612616565b60c084018190526147ab906108cc61396489600b614ff4565b60c084018190528560136020020152505050505050565b6148386040518061022001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b61485161484685601c614ff4565b6108d1866002614ff4565b815261486c61486185601d614ff4565b6108d1866003614ff4565b602082015261488a61487f85601e614ff4565b6108d1866004614ff4565b60408201526148a861489d85601f614ff4565b6108d1866005614ff4565b606082015280516148da906148d3906148cc906148c590806125fb565b84516125fb565b83516125fb565b82516125fb565b608082015260208101516149189061490e90614904906148fa90806125fb565b84602001516125fb565b83602001516125fb565b82602001516125fb565b60a082015260408101516149569061494c906149429061493890806125fb565b84604001516125fb565b83604001516125fb565b82604001516125fb565b60c0820152606081015161498a9061406d906149809061497690806125fb565b84606001516125fb565b83606001516125fb565b60e0820152608081015160a08201516149a39190612616565b61010082015260c081015160e08201516149bd9190612616565b61012082015260a08101516149e1906149d69080612616565b826101200151612616565b61014082015260e0810151614a05906149fa9080612616565b826101000151612616565b610160820152610120810151614a1b9080612616565b6101e08201819052614a3c90614a319080612616565b826101600151612616565b6101e0820152610100810151614a529080612616565b6101a08201819052614a7390614a689080612616565b826101400151612616565b6101a08201819052610160820151614a8a91612616565b6101808201526101408101516101e0820151614aa69190612616565b6101c0820152614aba61347185600c614ff4565b6102008201819052610280840151610180830151614ae5926108d1916108cc906108ef8a6024614ff4565b8360146020020152614b1583601560200201516108d18361020001516108cc856101a001516108ef8a6025614ff4565b8360156020020152614b4583601660200201516108d18361020001516108cc856101c001516108ef8a6026614ff4565b8360166020020152614b7583601760200201516108d18361020001516108cc856101e001516108ef8a6027614ff4565b836017602002015250505050565b614bd56040518061016001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b5f6040518060800160405280614c0a7f10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e76125e3565b8152602001614c387f0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b6125e3565b8152602001614c657e544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac156125e3565b8152602001614c937f222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b6125e3565b90529050614cb0614ca586601c614ff4565b6108d1876002614ff4565b6101208301819052614cef90614ce490614cd990614cce90806125fb565b8561012001516125fb565b8461012001516125fb565b8361012001516125fb565b8252614cfc85601d614ff4565b6020830152614d0c85601e614ff4565b6040830152614d1c85601f614ff4565b606083015281516020830151614d449161459e91614d3a9190612616565b8460400151612616565b6080830152614d5761372386600d614ff4565b6101408301528151614d7890614d6e90835f610bce565b8360800151612616565b60a0830152614da484601860200201516108d18461014001516108cc8660a001516108ef8b6024614ff4565b6103008501526020820151614dbf90614d6e90836001610bce565b60c0830152614deb84601960200201516108d18461014001516108cc8660c001516108ef8b6025614ff4565b6103208501526040820151614e0690614d6e90836002610bce565b60e0830152614e3284601a60200201516108d18461014001516108cc8660e001516108ef8b6026614ff4565b6103408501526060820151614e4d90614d6e90836003610bce565b610100830152614e7b84601b60200201516108d18461014001516108cc8661010001516108ef8b6027614ff4565b84601b6135aa565b815160015b601c811015614ed557614ecb826108d18684601c8110614eaa57614eaa615b73565b602002015186614ebb6001876155e0565b601b8110610bce57610bce615b73565b9150600101614e88565b5092915050565b5f5f8390505f60405160208152602080820152602060408201528260608201528460808201525f516020615c8e5f395f51905f5260a082015260205f60c08360055afa80614f28575f5ffd5b505f5160809190910160405295945050505050565b614f45615320565b614f4d615320565b604051835181526020840151602082015284604082015260408160608360075afa80614f77575f5ffd5b5080518252602080820151908301526060016040529392505050565b614f9b615320565b614fa3615320565b6040518451815260208501516020820152835160408201526020840151606082015260408160808360065afa80614fd8575f5ffd5b5080518252602080820151908301526080016040529392505050565b5f8282602881111561500857615008615c59565b6029811061501857615018615b73565b60200201519392505050565b604051806103e001604052805f81526020015f81526020015f815260200161504a615320565b8152602001615057615320565b8152602001615064615320565b8152602001615071615320565b815260200161507e615320565b815260200161508b615320565b8152602001615098615320565b81526020016150a5615320565b81526020016150b2615320565b81526020016150bf615320565b81526020016150cc615320565b81526020016150d9615320565b81526020016150e6615320565b81526020016150f3615320565b8152602001615100615320565b815260200161510d615320565b815260200161511a615320565b8152602001615127615320565b8152602001615134615320565b8152602001615141615320565b815260200161514e615320565b815260200161515b615320565b8152602001615168615320565b8152602001615175615320565b8152602001615182615320565b815260200161518f615320565b815260200161519c615320565b81526020016151a9615320565b905290565b6040518061028001604052806151c26153ec565b81526020016151cf615320565b81526020016151dc615320565b81526020016151e9615320565b81526020016151f6615320565b8152602001615203615320565b8152602001615210615320565b815260200161521d615320565b815260200161522a615320565b8152602001615237615320565b815260200161524461540b565b81526020015f8152602001615257615438565b81526020015f815260200161526a615466565b8152602001615277615485565b815260200161528461536a565b815260200161518f6154b3565b6040518061052001604052806029906020820280368337509192915050565b604051806101c001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020016152ee6154b3565b81526020016152fb6154b3565b81526020015f81526020015f81526020015f81526020015f8152602001606081525090565b60405180604001604052805f81526020015f81525090565b604051806040016040528061519c615320565b6040518061012001604052806009906020820280368337509192915050565b604051806103800160405280601c906020820280368337509192915050565b6040518060e0016040528061539c6154d1565b81526020015f81526020015f81526020015f81526020015f81526020016153c16154d1565b81526020015f81525090565b6040518061010001604052806008906020820280368337509192915050565b6040518061020001604052806010906020820280368337509192915050565b60405180606001604052806003905b615422615320565b81526020019060019003908161541a5790505090565b604051806103800160405280601c905b61545061534b565b8152602001906001900390816154485790505090565b604051806105400160405280602a906020820280368337509192915050565b604051806103600160405280601b905b61549d615320565b8152602001906001900390816154955790505090565b60405180608001604052806004906020820280368337509192915050565b604051806120000160405280610100906020820280368337509192915050565b5f5f5f5f60408587031215615504575f5ffd5b84356001600160401b03811115615519575f5ffd5b8501601f81018713615529575f5ffd5b80356001600160401b0381111561553e575f5ffd5b87602082840101111561554f575f5ffd5b6020918201955093508501356001600160401b0381111561556e575f5ffd5b8501601f8101871361557e575f5ffd5b80356001600160401b03811115615593575f5ffd5b8760208260051b84010111156155a7575f5ffd5b949793965060200194505050565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417612610576126106155b5565b81810381811115612610576126106155b5565b805f5b60108110156131e15781518452602093840193909101906001016155f6565b805f5b60038110156131e15761563684835180518252602090810151910152565b6040939093019260209190910190600101615618565b805f5b601c8110156131e1578151845f5b600981101561567c57825182526020928301929091019060010161565d565b50505061012093909301926020919091019060010161564f565b805f5b602a8110156131e1578151845260209384019390910190600101615699565b805f5b601b8110156131e1576156d984835180518252602090810151910152565b60409390930192602091909101906001016156bb565b805f5b601c8110156131e15781518452602093840193909101906001016156f2565b805f5b60048110156131e1578151845260209384019390910190600101615714565b8183525f6001600160fb1b0383111561574a575f5ffd5b8260051b80836020870137939093016020019392505050565b61576e8188516155f3565b5f602088015161578c61020084018280518252602090810151910152565b5060408801518051610240840152602090810151610260840152606089015180516102808501528101516102a0840152608089015180516102c08501528101516102e084015260a0890151805161030085015281015161032084015260c0890151805161034085015281015161036084015260e089015180516103808501528101516103a084015261010089015180516103c08501528101516103e084015261012089015180516104008501520151610420830152610140880151615855610440840182615615565b5061016088015161050083015261018088015161587661052084018261564c565b506101a08801516124a08301526101c08801516158976124c0840182615696565b506101e08801516158ac612a008401826156b8565b506102008801516158c16130c08401826156ef565b506102208801516158d6613440840182615711565b5061024088015180516134c08401526020908101516134e0840152610260890151805161350085015201516135208301526135c0613540830181905261591f9083018789615733565b613560830195909552506135808101929092526135a0909101529392505050565b634e487b7160e01b5f52604160045260245ffd5b60405161014081016001600160401b038111828210171561597757615977615940565b60405290565b604051601f8201601f191681016001600160401b03811182821017156159a5576159a5615940565b604052919050565b5f60c082840312156159bd575f5ffd5b60405160c081016001600160401b03811182821017156159df576159df615940565b604090815283518252602080850151908301528381015190820152606080840151908201526080808401519082015260a0928301519281019290925250919050565b5f82601f830112615a30575f5ffd5b5f610360615a3d8161597d565b915083018185821115615a4e575f5ffd5b845b82811015615a68578051825260209182019101615a50565b509195945050505050565b5f82601f830112615a82575f5ffd5b5f610380615a3d8161597d565b5f610be0828403128015615aa1575f5ffd5b50615aaa615954565b615ab484846159ad565b8152615ac38460c08501615a21565b6020820152615ad6846104208501615a73565b60408201526107a08301516060820152615af4846107c08501615a73565b6080820152610b4083015160a0820152610b6083015160c0820152610b8083015160e0820152610ba0830151610100820152610bc09092015161012083015250919050565b80820180821115612610576126106155b5565b5f5f85851115615b5a575f5ffd5b83861115615b66575f5ffd5b5050820193919092039150565b634e487b7160e01b5f52603260045260245ffd5b5f60018201615b9857615b986155b5565b5060010190565b80356020831015612610575f19602084900360031b1b1692915050565b5f82615bd657634e487b7160e01b5f52601260045260245ffd5b500690565b5f81615be957615be96155b5565b505f190190565b5f8183825b6008811015615c14578151835260209283019290910190600101615bf5565b5050506101008201905092915050565b5f82518060208501845e5f920191825250919050565b5f60208284031215615c4a575f5ffd5b815180151581146103f4575f5ffd5b634e487b7160e01b5f52602160045260245ffdfe30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4730644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", + "linkReferences": { + "project/contracts/verifier/ThresholdPkAggregationVerifier.sol": { + "ZKTranscriptLib": [ + { + "length": 20, + "start": 689 + } + ] + } + }, + "deployedLinkReferences": { + "project/contracts/verifier/ThresholdPkAggregationVerifier.sol": { + "ZKTranscriptLib": [ + { + "length": 20, + "start": 360 + } + ] + } + }, + "immutableReferences": { + "65407": [ + { + "length": 32, + "start": 91 + }, + { + "length": 32, + "start": 148 + }, + { + "length": 32, + "start": 257 + }, + { + "length": 32, + "start": 466 + }, + { + "length": 32, + "start": 2605 + }, + { + "length": 32, + "start": 2958 + }, + { + "length": 32, + "start": 3115 + }, + { + "length": 32, + "start": 5411 + }, + { + "length": 32, + "start": 5597 + }, + { + "length": 32, + "start": 5648 + }, + { + "length": 32, + "start": 6170 + }, + { + "length": 32, + "start": 11167 + } + ], + "65409": [ + { + "length": 32, + "start": 398 + } + ], + "65411": [ + { + "length": 32, + "start": 432 + }, + { + "length": 32, + "start": 2303 + } + ], + "65413": [ + { + "length": 32, + "start": 3156 + }, + { + "length": 32, + "start": 3257 + }, + { + "length": 32, + "start": 11970 + } + ] + }, + "inputSourceName": "project/contracts/verifier/ThresholdPkAggregationVerifier.sol", + "buildInfoId": "solc-0_8_28-bd46eae6f77be9a8538850385226c1ee4fc57e42" +} \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/verifier/DkgPkVerifier.sol/ZKTranscriptLib.json b/packages/enclave-contracts/artifacts/contracts/verifier/ThresholdPkAggregationVerifier.sol/ZKTranscriptLib.json similarity index 99% rename from packages/enclave-contracts/artifacts/contracts/verifier/DkgPkVerifier.sol/ZKTranscriptLib.json rename to packages/enclave-contracts/artifacts/contracts/verifier/ThresholdPkAggregationVerifier.sol/ZKTranscriptLib.json index e066bbdadb..b6bbee1be1 100644 --- a/packages/enclave-contracts/artifacts/contracts/verifier/DkgPkVerifier.sol/ZKTranscriptLib.json +++ b/packages/enclave-contracts/artifacts/contracts/verifier/ThresholdPkAggregationVerifier.sol/ZKTranscriptLib.json @@ -1,7 +1,7 @@ { "_format": "hh3-artifact-1", "contractName": "ZKTranscriptLib", - "sourceName": "contracts/verifier/DkgPkVerifier.sol", + "sourceName": "contracts/verifier/ThresholdPkAggregationVerifier.sol", "abi": [ { "inputs": [ @@ -390,6 +390,6 @@ "linkReferences": {}, "deployedLinkReferences": {}, "immutableReferences": {}, - "inputSourceName": "project/contracts/verifier/DkgPkVerifier.sol", - "buildInfoId": "solc-0_8_28-d48586b5c515ad1b112178eeef9419e7799821ba" + "inputSourceName": "project/contracts/verifier/ThresholdPkAggregationVerifier.sol", + "buildInfoId": "solc-0_8_28-bd46eae6f77be9a8538850385226c1ee4fc57e42" } \ No newline at end of file diff --git a/packages/enclave-contracts/contracts/interfaces/ICircuitVerifier.sol b/packages/enclave-contracts/contracts/interfaces/ICircuitVerifier.sol index 0ff4008516..d4551f4289 100644 --- a/packages/enclave-contracts/contracts/interfaces/ICircuitVerifier.sol +++ b/packages/enclave-contracts/contracts/interfaces/ICircuitVerifier.sol @@ -7,7 +7,7 @@ pragma solidity >=0.8.27; /** * @title ICircuitVerifier - * @notice Interface for on-chain ZK circuit verifiers (e.g., DkgPkVerifier, Honk verifiers) + * @notice Interface for on-chain ZK circuit verifiers (e.g., ThresholdPkAggregationVerifier, Honk verifiers) * @dev Standard interface matching the verification pattern used by Honk-generated verifiers. * Set the circuit verifier address directly as the proofVerifier in a SlashPolicy. */ diff --git a/packages/enclave-contracts/contracts/verifier/RecursiveAggregationFoldVerifier.sol b/packages/enclave-contracts/contracts/verifier/RecursiveAggregationFoldVerifier.sol new file mode 100644 index 0000000000..dad00f9ba6 --- /dev/null +++ b/packages/enclave-contracts/contracts/verifier/RecursiveAggregationFoldVerifier.sol @@ -0,0 +1,2452 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +pragma solidity >=0.8.21; + +uint256 constant N = 2097152; +uint256 constant LOG_N = 21; +uint256 constant NUMBER_OF_PUBLIC_INPUTS = 18; +uint256 constant VK_HASH = 0x0c0bf44aa8d9785b43e16005da8fc455d330c7ff959979a9c7cc876703453859; +library HonkVerificationKey { + function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { + Honk.VerificationKey memory vk = Honk.VerificationKey({ + circuitSize: uint256(2097152), + logCircuitSize: uint256(21), + publicInputsSize: uint256(18), + ql: Honk.G1Point({ + x: uint256(0x04874f63e091441575d7f87163ad2d04fa3e4b49cee4cde183081c90e13ff17f), + y: uint256(0x1547db64a1675f5ab99f4dc4cf2af5cfd0ef1fbdb68977d8abc8f7112e88bf80) + }), + qr: Honk.G1Point({ + x: uint256(0x17094eb4ca2210abcb1b4d41cf1355b04dd66b014d42345e6eadf8407a74ed1a), + y: uint256(0x2228aebc15e7852110c94a633f8fb944d71cb1a5cff1b5127d0e4eecf1a7726f) + }), + qo: Honk.G1Point({ + x: uint256(0x15cfc9a869b9c86a839a67ab04b780b3049b2d5f10a901055f85d4717428f6dc), + y: uint256(0x2aec2a8cee64e9da3981b9d6f9f00faffbe2ece25cd4f57d80b8552d81c716b5) + }), + q4: Honk.G1Point({ + x: uint256(0x232463dd05273276d135bcdfe18a8b299585d6e0fe220dfafe63a54293476dfe), + y: uint256(0x17f763713bb623ce792152d0aabd32b83ca5a6abecb97ece41dc00a1bb6296ad) + }), + qm: Honk.G1Point({ + x: uint256(0x286493b1bc5a9674ed3d063c79269512944b6dfb58b880361d36fce8a61c9814), + y: uint256(0x1ef073ead7f7fd307a1a357548e4d8c9f5d03d5a7191cea80b2ba0cf03d18d29) + }), + qc: Honk.G1Point({ + x: uint256(0x216ae5e3bf2a41f792d13f7028ec70fb78ac8d5fa4834341acad58d3453bf123), + y: uint256(0x2ef50ace63f2e23d4b2a184ce511e1b7781287457bcc135459c5c56de1877b93) + }), + qLookup: Honk.G1Point({ + x: uint256(0x1ad0852ae1f831506b33a76a4968849abaf87e068b8e00ba462e190ff4dc0ff9), + y: uint256(0x2b782e70b28e331338317e348ecb3d3ee69c0cb7b1ca634b32395b5349605a93) + }), + qArith: Honk.G1Point({ + x: uint256(0x1a03cf866e6a54045329039071909e3129fefe1a3f380a5db12e3156f6bed47e), + y: uint256(0x1bdf1ec93f5b5962d4aac343eaa211a5b8021546f0dc225d03f39cb465546d5a) + }), + qDeltaRange: Honk.G1Point({ + x: uint256(0x1940cce3f764969daf23bd6cf6d09ecd7e50392878ce2ba3a0a30c88f619cd37), + y: uint256(0x06d183a757a5b2294f8e3b10d3304744bc4d7b68246b01563c48dfd3ab443842) + }), + qElliptic: Honk.G1Point({ + x: uint256(0x1b5b7f1276e5867eef1d033df65d003c591c16bf64bd2d4cb89bccc1a98ab3dd), + y: uint256(0x28f55668465e42d873d91d75906232c6f05bf37be890e4d4c0ef6a1d5df8c211) + }), + qMemory: Honk.G1Point({ + x: uint256(0x11a96a0fb134b4ad2a3ac7b54fc7989a18ada93ac73e0e7bfb7134f334854c55), + y: uint256(0x166911402df98ab7053779cb1f431384018e058cbe2ac79d1d5ff020ce5474d9) + }), + qNnf: Honk.G1Point({ + x: uint256(0x11b342da18e6325f40bddb950e3ba058fe013e0a6757512f022a0d9961d3d468), + y: uint256(0x04225227552dcaa71ff7c4571eebd04ca40091a33ff91d1b220af458dffca289) + }), + qPoseidon2External: Honk.G1Point({ + x: uint256(0x18ae1c0c284757fd6a6bcf60413a8e2334fcb46562ea30b9cb9444a663493463), + y: uint256(0x1cade59f171ec1d94348309a02e1642067d6e6b43eb254837e703c1488dff221) + }), + qPoseidon2Internal: Honk.G1Point({ + x: uint256(0x2537fc03ae8cfc49498d0a04704d77e10d6477d6b7c5e515cf24dadab0dee874), + y: uint256(0x0850d14d62e2c194a69110f3aefa10d6caea9bf6ff940331877c5f49e41e5fa7) + }), + s1: Honk.G1Point({ + x: uint256(0x02f24bc3b670a81abc44133de34592b13bb3a5ea67cdaa1d73ad35965b8abe49), + y: uint256(0x0c62df1ee51bfbaeba2a75c1cc8aa8a2ea3222f21bc0b8958bd43c3aa749e963) + }), + s2: Honk.G1Point({ + x: uint256(0x21ac9dc80bd7f54e6817d224b7650c4176407a1109f92567c05676ac1fe0973c), + y: uint256(0x2d325ad91e20be879c716023bcde84d469c4f7867c12bb321c387e4a1036f86d) + }), + s3: Honk.G1Point({ + x: uint256(0x25b123800356ccc6d7a609989a0e4fc132bf757389ca25f0c1db7612bbec2349), + y: uint256(0x03832b0106e3581612f83d158277886cc9e5637790fccabde84b3218220f2a48) + }), + s4: Honk.G1Point({ + x: uint256(0x25962cb0996ebaeea7b01f8860fde2547f8bb738291e53c7f25a4ac80af4fd2e), + y: uint256(0x22ec82955d2dc95fddc9cf9fd4f7aa555635abb02cf6eeb7ac3df225c8b62d52) + }), + t1: Honk.G1Point({ + x: uint256(0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26), + y: uint256(0x0015b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f) + }), + t2: Honk.G1Point({ + x: uint256(0x1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e), + y: uint256(0x305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d19) + }), + t3: Honk.G1Point({ + x: uint256(0x061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d), + y: uint256(0x1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e) + }), + t4: Honk.G1Point({ + x: uint256(0x043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce), + y: uint256(0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854) + }), + id1: Honk.G1Point({ + x: uint256(0x1332d280da268502ba2d853e14e53baf604c7a6a41b7e70bdf2e861cc6a59821), + y: uint256(0x0d14761f5281d5dc1209b25146d92d9e5692294f818c8c3468c9921f7a7cce79) + }), + id2: Honk.G1Point({ + x: uint256(0x1b215ab72c8af91207dee6f8ac01f0e0d51895a9f670d7173c7b6d5b80a39085), + y: uint256(0x04ee544edd280fa185c8d9886c9e19832ef71d67fab5093eadeec49ccc5a34b1) + }), + id3: Honk.G1Point({ + x: uint256(0x11005f2ce25b6f954b89343fa059782ab8ecdc84d411c828cc7dfd54f796b6f6), + y: uint256(0x0c68694f3595aecaaffdff52c0fa5f1d05fa8c7ba651da1220634fa4b8867ba7) + }), + id4: Honk.G1Point({ + x: uint256(0x1145c2f8face74c268cc68c772d99c59587659cbd594a1aef32a36ab556621e2), + y: uint256(0x1ffa60a61a074aa68c8412fd9f3b4ae61437cf3ff3f0497e1244ba56370d7b91) + }), + lagrangeFirst: Honk.G1Point({ + x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), + y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) + }), + lagrangeLast: Honk.G1Point({ + x: uint256(0x1f0071e7d3b9be4c8376ff230b3b8b242585d403d25463022d38e574cb0b5754), + y: uint256(0x19cb572dac34151573e3024878327a8ea7febb3d76c6171b10d1e3f2f2907584) + }) + }); + return vk; + } +} + +pragma solidity ^0.8.27; + +interface IVerifier { + function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); +} + +type Fr is uint256; + +using {add as +} for Fr global; +using {sub as -} for Fr global; +using {mul as *} for Fr global; + +using {exp as ^} for Fr global; +using {notEqual as !=} for Fr global; +using {equal as ==} for Fr global; + +uint256 constant SUBGROUP_SIZE = 256; +uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order +uint256 constant P = MODULUS; +Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); +Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); +Fr constant ONE = Fr.wrap(1); +Fr constant ZERO = Fr.wrap(0); +// Instantiation + +library FrLib { + function from(uint256 value) internal pure returns (Fr) { + unchecked { + return Fr.wrap(value % MODULUS); + } + } + + function fromBytes32(bytes32 value) internal pure returns (Fr) { + unchecked { + return Fr.wrap(uint256(value) % MODULUS); + } + } + + function toBytes32(Fr value) internal pure returns (bytes32) { + unchecked { + return bytes32(Fr.unwrap(value)); + } + } + + function invert(Fr value) internal view returns (Fr) { + uint256 v = Fr.unwrap(value); + uint256 result; + + // Call the modexp precompile to invert in the field + assembly { + let free := mload(0x40) + mstore(free, 0x20) + mstore(add(free, 0x20), 0x20) + mstore(add(free, 0x40), 0x20) + mstore(add(free, 0x60), v) + mstore(add(free, 0x80), sub(MODULUS, 2)) + mstore(add(free, 0xa0), MODULUS) + let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) + if iszero(success) { + revert(0, 0) + } + result := mload(0x00) + mstore(0x40, add(free, 0x80)) + } + + return Fr.wrap(result); + } + + function pow(Fr base, uint256 v) internal view returns (Fr) { + uint256 b = Fr.unwrap(base); + uint256 result; + + // Call the modexp precompile to invert in the field + assembly { + let free := mload(0x40) + mstore(free, 0x20) + mstore(add(free, 0x20), 0x20) + mstore(add(free, 0x40), 0x20) + mstore(add(free, 0x60), b) + mstore(add(free, 0x80), v) + mstore(add(free, 0xa0), MODULUS) + let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) + if iszero(success) { + revert(0, 0) + } + result := mload(0x00) + mstore(0x40, add(free, 0x80)) + } + + return Fr.wrap(result); + } + + function div(Fr numerator, Fr denominator) internal view returns (Fr) { + unchecked { + return numerator * invert(denominator); + } + } + + function sqr(Fr value) internal pure returns (Fr) { + unchecked { + return value * value; + } + } + + function unwrap(Fr value) internal pure returns (uint256) { + unchecked { + return Fr.unwrap(value); + } + } + + function neg(Fr value) internal pure returns (Fr) { + unchecked { + return Fr.wrap(MODULUS - Fr.unwrap(value)); + } + } +} + +// Free functions +function add(Fr a, Fr b) pure returns (Fr) { + unchecked { + return Fr.wrap(addmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS)); + } +} + +function mul(Fr a, Fr b) pure returns (Fr) { + unchecked { + return Fr.wrap(mulmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS)); + } +} + +function sub(Fr a, Fr b) pure returns (Fr) { + unchecked { + return Fr.wrap(addmod(Fr.unwrap(a), MODULUS - Fr.unwrap(b), MODULUS)); + } +} + +function exp(Fr base, Fr exponent) pure returns (Fr) { + if (Fr.unwrap(exponent) == 0) return Fr.wrap(1); + // Implement exponent with a loop as we will overflow otherwise + for (uint256 i = 1; i < Fr.unwrap(exponent); i += i) { + base = base * base; + } + return base; +} + +function notEqual(Fr a, Fr b) pure returns (bool) { + unchecked { + return Fr.unwrap(a) != Fr.unwrap(b); + } +} + +function equal(Fr a, Fr b) pure returns (bool) { + unchecked { + return Fr.unwrap(a) == Fr.unwrap(b); + } +} + +uint256 constant CONST_PROOF_SIZE_LOG_N = 28; + +uint256 constant NUMBER_OF_SUBRELATIONS = 28; +uint256 constant BATCHED_RELATION_PARTIAL_LENGTH = 8; +uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; +uint256 constant NUMBER_OF_ENTITIES = 41; +// The number of entities added for ZK (gemini_masking_poly) +uint256 constant NUM_MASKING_POLYNOMIALS = 1; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED = 36; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_TO_BE_SHIFTED = 5; +uint256 constant PAIRING_POINTS_SIZE = 16; + +uint256 constant FIELD_ELEMENT_SIZE = 0x20; +uint256 constant GROUP_ELEMENT_SIZE = 0x40; + +// Powers of alpha used to batch subrelations (alpha, alpha^2, ..., alpha^(NUM_SUBRELATIONS-1)) +uint256 constant NUMBER_OF_ALPHAS = NUMBER_OF_SUBRELATIONS - 1; + +// ENUM FOR WIRES +enum WIRE { + Q_M, + Q_C, + Q_L, + Q_R, + Q_O, + Q_4, + Q_LOOKUP, + Q_ARITH, + Q_RANGE, + Q_ELLIPTIC, + Q_MEMORY, + Q_NNF, + Q_POSEIDON2_EXTERNAL, + Q_POSEIDON2_INTERNAL, + SIGMA_1, + SIGMA_2, + SIGMA_3, + SIGMA_4, + ID_1, + ID_2, + ID_3, + ID_4, + TABLE_1, + TABLE_2, + TABLE_3, + TABLE_4, + LAGRANGE_FIRST, + LAGRANGE_LAST, + W_L, + W_R, + W_O, + W_4, + Z_PERM, + LOOKUP_INVERSES, + LOOKUP_READ_COUNTS, + LOOKUP_READ_TAGS, + W_L_SHIFT, + W_R_SHIFT, + W_O_SHIFT, + W_4_SHIFT, + Z_PERM_SHIFT +} + +library Honk { + struct G1Point { + uint256 x; + uint256 y; + } + + struct VerificationKey { + // Misc Params + uint256 circuitSize; + uint256 logCircuitSize; + uint256 publicInputsSize; + // Selectors + G1Point qm; + G1Point qc; + G1Point ql; + G1Point qr; + G1Point qo; + G1Point q4; + G1Point qLookup; // Lookup + G1Point qArith; // Arithmetic widget + G1Point qDeltaRange; // Delta Range sort + G1Point qMemory; // Memory + G1Point qNnf; // Non-native Field + G1Point qElliptic; // Auxillary + G1Point qPoseidon2External; + G1Point qPoseidon2Internal; + // Copy constraints + G1Point s1; + G1Point s2; + G1Point s3; + G1Point s4; + // Copy identity + G1Point id1; + G1Point id2; + G1Point id3; + G1Point id4; + // Precomputed lookup table + G1Point t1; + G1Point t2; + G1Point t3; + G1Point t4; + // Fixed first and last + G1Point lagrangeFirst; + G1Point lagrangeLast; + } + + struct RelationParameters { + // challenges + Fr eta; + Fr etaTwo; + Fr etaThree; + Fr beta; + Fr gamma; + // derived + Fr publicInputsDelta; + } + + struct Proof { + // Pairing point object + Fr[PAIRING_POINTS_SIZE] pairingPointObject; + // Free wires + G1Point w1; + G1Point w2; + G1Point w3; + G1Point w4; + // Lookup helpers - Permutations + G1Point zPerm; + // Lookup helpers - logup + G1Point lookupReadCounts; + G1Point lookupReadTags; + G1Point lookupInverses; + // Sumcheck + Fr[BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates; + Fr[NUMBER_OF_ENTITIES] sumcheckEvaluations; + // Shplemini + G1Point[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms; + Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations; + G1Point shplonkQ; + G1Point kzgQuotient; + } + + /// forge-lint: disable-next-item(pascal-case-struct) + struct ZKProof { + // Pairing point object + Fr[PAIRING_POINTS_SIZE] pairingPointObject; + // ZK: Gemini masking polynomial commitment (sent first, right after public inputs) + G1Point geminiMaskingPoly; + // Commitments to wire polynomials + G1Point w1; + G1Point w2; + G1Point w3; + G1Point w4; + // Commitments to logup witness polynomials + G1Point lookupReadCounts; + G1Point lookupReadTags; + G1Point lookupInverses; + // Commitment to grand permutation polynomial + G1Point zPerm; + G1Point[3] libraCommitments; + // Sumcheck + Fr libraSum; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates; + Fr libraEvaluation; + Fr[NUMBER_OF_ENTITIES_ZK] sumcheckEvaluations; // Includes gemini_masking_poly eval at index 0 (first position) + // Shplemini + G1Point[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms; + Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations; + Fr[4] libraPolyEvals; + G1Point shplonkQ; + G1Point kzgQuotient; + } +} + +// ZKTranscript library to generate fiat shamir challenges, the ZK transcript only differest +/// forge-lint: disable-next-item(pascal-case-struct) +struct ZKTranscript { + // Oink + Honk.RelationParameters relationParameters; + Fr[NUMBER_OF_ALPHAS] alphas; // Powers of alpha: [alpha, alpha^2, ..., alpha^(NUM_SUBRELATIONS-1)] + Fr[CONST_PROOF_SIZE_LOG_N] gateChallenges; + // Sumcheck + Fr libraChallenge; + Fr[CONST_PROOF_SIZE_LOG_N] sumCheckUChallenges; + // Shplemini + Fr rho; + Fr geminiR; + Fr shplonkNu; + Fr shplonkZ; + // Derived + Fr publicInputsDelta; +} + +library ZKTranscriptLib { + function generateTranscript( + Honk.ZKProof memory proof, + bytes32[] calldata publicInputs, + uint256 vkHash, + uint256 publicInputsSize, + uint256 logN + ) external pure returns (ZKTranscript memory t) { + Fr previousChallenge; + (t.relationParameters, previousChallenge) = + generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); + + (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); + + (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); + (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); + + (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); + + (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); + + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); + + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); + return t; + } + + function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { + uint256 challengeU256 = uint256(Fr.unwrap(challenge)); + // Split into two equal 127-bit chunks (254/2) + uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits + uint256 hi = challengeU256 >> 127; + first = FrLib.fromBytes32(bytes32(lo)); + second = FrLib.fromBytes32(bytes32(hi)); + } + + function generateRelationParametersChallenges( + Honk.ZKProof memory proof, + bytes32[] calldata publicInputs, + uint256 vkHash, + uint256 publicInputsSize, + Fr previousChallenge + ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { + (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = + generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + } + + function generateEtaChallenge( + Honk.ZKProof memory proof, + bytes32[] calldata publicInputs, + uint256 vkHash, + uint256 publicInputsSize + ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { + // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) + bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); + round0[0] = bytes32(vkHash); + + for (uint256 i = 0; i < publicInputsSize - PAIRING_POINTS_SIZE; i++) { + round0[1 + i] = bytes32(publicInputs[i]); + } + for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); + } + + // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) + round0[1 + publicInputsSize] = bytes32(proof.geminiMaskingPoly.x); + round0[1 + publicInputsSize + 1] = bytes32(proof.geminiMaskingPoly.y); + + // Create the first challenge + // Note: w4 is added to the challenge later on + round0[1 + publicInputsSize + 2] = bytes32(proof.w1.x); + round0[1 + publicInputsSize + 3] = bytes32(proof.w1.y); + round0[1 + publicInputsSize + 4] = bytes32(proof.w2.x); + round0[1 + publicInputsSize + 5] = bytes32(proof.w2.y); + round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); + round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); + + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); + (eta, etaTwo) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + + (etaThree,) = splitChallenge(previousChallenge); + } + + function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) + { + bytes32[7] memory round1; + round1[0] = FrLib.toBytes32(previousChallenge); + round1[1] = bytes32(proof.lookupReadCounts.x); + round1[2] = bytes32(proof.lookupReadCounts.y); + round1[3] = bytes32(proof.lookupReadTags.x); + round1[4] = bytes32(proof.lookupReadTags.y); + round1[5] = bytes32(proof.w4.x); + round1[6] = bytes32(proof.w4.y); + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); + (beta, gamma) = splitChallenge(nextPreviousChallenge); + } + + // Alpha challenges non-linearise the gate contributions + function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) + { + // Generate the original sumcheck alpha 0 by hashing zPerm and zLookup + uint256[5] memory alpha0; + alpha0[0] = Fr.unwrap(previousChallenge); + alpha0[1] = proof.lookupInverses.x; + alpha0[2] = proof.lookupInverses.y; + alpha0[3] = proof.zPerm.x; + alpha0[4] = proof.zPerm.y; + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); + Fr alpha; + (alpha,) = splitChallenge(nextPreviousChallenge); + + // Compute powers of alpha for batching subrelations + alphas[0] = alpha; + for (uint256 i = 1; i < NUMBER_OF_ALPHAS; i++) { + alphas[i] = alphas[i - 1] * alpha; + } + } + + function generateGateChallenges(Fr previousChallenge, uint256 logN) + internal + pure + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) + { + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + (gateChallenges[0],) = splitChallenge(previousChallenge); + for (uint256 i = 1; i < logN; i++) { + gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; + } + nextPreviousChallenge = previousChallenge; + } + + function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr libraChallenge, Fr nextPreviousChallenge) + { + // 2 comm, 1 sum, 1 challenge + uint256[4] memory challengeData; + challengeData[0] = Fr.unwrap(previousChallenge); + challengeData[1] = proof.libraCommitments[0].x; + challengeData[2] = proof.libraCommitments[0].y; + challengeData[3] = Fr.unwrap(proof.libraSum); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); + (libraChallenge,) = splitChallenge(nextPreviousChallenge); + } + + function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) + { + for (uint256 i = 0; i < logN; i++) { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; + univariateChal[0] = prevChallenge; + + for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { + univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; + } + prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); + + (sumcheckChallenges[i],) = splitChallenge(prevChallenge); + } + nextPreviousChallenge = prevChallenge; + } + + // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) + function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr rho, Fr nextPreviousChallenge) + { + uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; + rhoChallengeElements[0] = Fr.unwrap(prevChallenge); + uint256 i; + for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { + rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); + } + rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); + i += 1; + rhoChallengeElements[i] = proof.libraCommitments[1].x; + rhoChallengeElements[i + 1] = proof.libraCommitments[1].y; + i += 2; + rhoChallengeElements[i] = proof.libraCommitments[2].x; + rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); + (rho,) = splitChallenge(nextPreviousChallenge); + } + + function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr geminiR, Fr nextPreviousChallenge) + { + uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); + gR[0] = Fr.unwrap(prevChallenge); + + for (uint256 i = 0; i < logN - 1; i++) { + gR[1 + i * 2] = proof.geminiFoldComms[i].x; + gR[2 + i * 2] = proof.geminiFoldComms[i].y; + } + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); + + (geminiR,) = splitChallenge(nextPreviousChallenge); + } + + function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr shplonkNu, Fr nextPreviousChallenge) + { + uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); + shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); + + for (uint256 i = 1; i <= logN; i++) { + shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); + } + + uint256 libraIdx = 0; + for (uint256 i = logN + 1; i <= logN + 4; i++) { + shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); + libraIdx++; + } + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); + (shplonkNu,) = splitChallenge(nextPreviousChallenge); + } + + function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr shplonkZ, Fr nextPreviousChallenge) + { + uint256[3] memory shplonkZChallengeElements; + shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); + + shplonkZChallengeElements[1] = proof.shplonkQ.x; + shplonkZChallengeElements[2] = proof.shplonkQ.y; + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); + (shplonkZ,) = splitChallenge(nextPreviousChallenge); + } + + function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { + uint256 boundary = 0x0; + + // Pairing point object + for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { + p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + } + + // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) + p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + + // Commitments + p.w1 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.w2 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.w3 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + + // Lookup / Permutation Helper Commitments + p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + + p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + // Sumcheck univariates + for (uint256 i = 0; i < logN; i++) { + for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { + p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + } + } + + // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) + for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { + p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + } + + p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + + p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + + // Gemini + // Read gemini fold univariates + for (uint256 i = 0; i < logN - 1; i++) { + p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + } + + // Read gemini a evaluations + for (uint256 i = 0; i < logN; i++) { + p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + } + + for (uint256 i = 0; i < 4; i++) { + p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + } + + // Shplonk + p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + // KZG + p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + } +} + +// Field arithmetic libraries + +library RelationsLib { + Fr internal constant GRUMPKIN_CURVE_B_PARAMETER_NEGATED = Fr.wrap(17); // -(-17) + + function accumulateRelationEvaluations( + Fr[NUMBER_OF_ENTITIES] memory purportedEvaluations, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges, + Fr powPartialEval + ) internal pure returns (Fr accumulator) { + Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; + + // Accumulate all relations in Ultra Honk - each with varying number of subrelations + accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); + + // batch the subrelations with the precomputed alpha powers to obtain the full honk relation + accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); + } + + /** + * Aesthetic helper function that is used to index by enum into proof.sumcheckEvaluations, it avoids + * the relation checking code being cluttered with uint256 type casting, which is often a different colour in code + * editors, and thus is noisy. + */ + function wire(Fr[NUMBER_OF_ENTITIES] memory p, WIRE _wire) internal pure returns (Fr) { + return p[uint256(_wire)]; + } + + uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; + /** + * Ultra Arithmetic Relation + * + */ + + function accumulateArithmeticRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + // Relation 0 + Fr q_arith = wire(p, WIRE.Q_ARITH); + { + Fr neg_half = Fr.wrap(NEG_HALF_MODULO_P); + + Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half; + accum = accum + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + wire(p, WIRE.Q_C); + accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); + accum = accum * q_arith; + accum = accum * domainSep; + evals[0] = accum; + } + + // Relation 1 + { + Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); + accum = accum * (q_arith - Fr.wrap(2)); + accum = accum * (q_arith - ONE); + accum = accum * q_arith; + accum = accum * domainSep; + evals[1] = accum; + } + } + + function accumulatePermutationRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + Fr grand_product_numerator; + Fr grand_product_denominator; + + { + Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; + num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + + grand_product_numerator = num; + } + { + Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; + den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); + + grand_product_denominator = den; + } + + // Contribution 2 + { + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; + + acc = acc + - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) + * grand_product_denominator); + acc = acc * domainSep; + evals[2] = acc; + } + + // Contribution 3 + { + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + evals[3] = acc; + } + } + + function accumulateLogDerivativeLookupRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + Fr write_term; + Fr read_term; + + // Calculate the write term (the table accumulation) + { + write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); + } + + // Calculate the write term + { + Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); + + read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); + } + + Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; + Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; + + Fr inverse_exists_xor = + wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) + - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + + // Inverse calculated correctly relation + Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; + accumulatorNone = accumulatorNone * domainSep; + + // Inverse + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; + + Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); + + Fr read_tag_boolean_relation = read_tag * read_tag - read_tag; + + evals[4] = accumulatorNone; + evals[5] = accumulatorOne; + evals[6] = read_tag_boolean_relation * domainSep; + } + + function accumulateDeltaRangeRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + Fr minus_one = ZERO - ONE; + Fr minus_two = ZERO - Fr.wrap(2); + Fr minus_three = ZERO - Fr.wrap(3); + + // Compute wire differences + Fr delta_1 = wire(p, WIRE.W_R) - wire(p, WIRE.W_L); + Fr delta_2 = wire(p, WIRE.W_O) - wire(p, WIRE.W_R); + Fr delta_3 = wire(p, WIRE.W_4) - wire(p, WIRE.W_O); + Fr delta_4 = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_4); + + // Contribution 6 + { + Fr acc = delta_1; + acc = acc * (delta_1 + minus_one); + acc = acc * (delta_1 + minus_two); + acc = acc * (delta_1 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[7] = acc; + } + + // Contribution 7 + { + Fr acc = delta_2; + acc = acc * (delta_2 + minus_one); + acc = acc * (delta_2 + minus_two); + acc = acc * (delta_2 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[8] = acc; + } + + // Contribution 8 + { + Fr acc = delta_3; + acc = acc * (delta_3 + minus_one); + acc = acc * (delta_3 + minus_two); + acc = acc * (delta_3 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[9] = acc; + } + + // Contribution 9 + { + Fr acc = delta_4; + acc = acc * (delta_4 + minus_one); + acc = acc * (delta_4 + minus_two); + acc = acc * (delta_4 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[10] = acc; + } + } + + struct EllipticParams { + // Points + Fr x_1; + Fr y_1; + Fr x_2; + Fr y_2; + Fr y_3; + Fr x_3; + // push accumulators into memory + Fr x_double_identity; + } + + function accumulateEllipticRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + EllipticParams memory ep; + ep.x_1 = wire(p, WIRE.W_R); + ep.y_1 = wire(p, WIRE.W_O); + + ep.x_2 = wire(p, WIRE.W_L_SHIFT); + ep.y_2 = wire(p, WIRE.W_4_SHIFT); + ep.y_3 = wire(p, WIRE.W_O_SHIFT); + ep.x_3 = wire(p, WIRE.W_R_SHIFT); + + Fr q_sign = wire(p, WIRE.Q_L); + Fr q_is_double = wire(p, WIRE.Q_M); + + // Contribution 10 point addition, x-coordinate check + // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0 + Fr x_diff = (ep.x_2 - ep.x_1); + Fr y1_sqr = (ep.y_1 * ep.y_1); + { + // Move to top + Fr partialEval = domainSep; + + Fr y2_sqr = (ep.y_2 * ep.y_2); + Fr y1y2 = ep.y_1 * ep.y_2 * q_sign; + Fr x_add_identity = (ep.x_3 + ep.x_2 + ep.x_1); + x_add_identity = x_add_identity * x_diff * x_diff; + x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; + + evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + } + + // Contribution 11 point addition, x-coordinate check + // q_elliptic * (q_sign * y1 + y3)(x2 - x1) + (x3 - x1)(y2 - q_sign * y1) = 0 + { + Fr y1_plus_y3 = ep.y_1 + ep.y_3; + Fr y_diff = ep.y_2 * q_sign - ep.y_1; + Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; + evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + } + + // Contribution 10 point doubling, x-coordinate check + // (x3 + x1 + x1) (4y1*y1) - 9 * x1 * x1 * x1 * x1 = 0 + // N.B. we're using the equivalence x1*x1*x1 === y1*y1 - curve_b to reduce degree by 1 + { + Fr x_pow_4 = (y1_sqr + GRUMPKIN_CURVE_B_PARAMETER_NEGATED) * ep.x_1; + Fr y1_sqr_mul_4 = y1_sqr + y1_sqr; + y1_sqr_mul_4 = y1_sqr_mul_4 + y1_sqr_mul_4; + Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); + + // NOTE: pushed into memory (stack >:'( ) + ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + evals[11] = evals[11] + acc; + } + + // Contribution 11 point doubling, y-coordinate check + // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 + { + Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; + Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); + evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + } + } + + // Parameters used within the Memory Relation + // A struct is used to work around stack too deep. This relation has alot of variables + struct MemParams { + Fr memory_record_check; + Fr partial_record_check; + Fr next_gate_access_type; + Fr record_delta; + Fr index_delta; + Fr adjacent_values_match_if_adjacent_indices_match; + Fr adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation; + Fr access_check; + Fr next_gate_access_type_is_boolean; + Fr ROM_consistency_check_identity; + Fr RAM_consistency_check_identity; + Fr timestamp_delta; + Fr RAM_timestamp_check_identity; + Fr memory_identity; + Fr index_is_monotonically_increasing; + } + + function accumulateMemoryRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + MemParams memory ap; + + /** + * MEMORY + * + * A RAM memory record contains a tuple of the following fields: + * * i: `index` of memory cell being accessed + * * t: `timestamp` of memory cell being accessed (used for RAM, set to 0 for ROM) + * * v: `value` of memory cell being accessed + * * a: `access` type of record. read: 0 = read, 1 = write + * * r: `record` of memory cell. record = access + index * eta + timestamp * eta_two + value * eta_three + * + * A ROM memory record contains a tuple of the following fields: + * * i: `index` of memory cell being accessed + * * v: `value1` of memory cell being accessed (ROM tables can store up to 2 values per index) + * * v2:`value2` of memory cell being accessed (ROM tables can store up to 2 values per index) + * * r: `record` of memory cell. record = index * eta + value2 * eta_two + value1 * eta_three + * + * When performing a read/write access, the values of i, t, v, v2, a, r are stored in the following wires + + * selectors, depending on whether the gate is a RAM read/write or a ROM read + * + * | gate type | i | v2/t | v | a | r | + * | --------- | -- | ----- | -- | -- | -- | + * | ROM | w1 | w2 | w3 | -- | w4 | + * | RAM | w1 | w2 | w3 | qc | w4 | + * + * (for accesses where `index` is a circuit constant, it is assumed the circuit will apply a copy constraint on + * `w2` to fix its value) + * + * + */ + + /** + * Memory Record Check + * Partial degree: 1 + * Total degree: 4 + * + * A ROM/ROM access gate can be evaluated with the identity: + * + * qc + w1 \eta + w2 \eta_two + w3 \eta_three - w4 = 0 + * + * For ROM gates, qc = 0 + */ + ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); + ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 + ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); + + /** + * Contribution 13 & 14 + * ROM Consistency Check + * Partial degree: 1 + * Total degree: 4 + * + * For every ROM read, a set equivalence check is applied between the record witnesses, and a second set of + * records that are sorted. + * + * We apply the following checks for the sorted records: + * + * 1. w1, w2, w3 correctly map to 'index', 'v1, 'v2' for a given record value at w4 + * 2. index values for adjacent records are monotonically increasing + * 3. if, at gate i, index_i == index_{i + 1}, then value1_i == value1_{i + 1} and value2_i == value2_{i + 1} + * + */ + ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); + ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); + + ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 + + ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 + + evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + + ap.ROM_consistency_check_identity = ap.memory_record_check * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)); // deg 3 or 7 + + /** + * Contributions 15,16,17 + * RAM Consistency Check + * + * The 'access' type of the record is extracted with the expression `w_4 - ap.partial_record_check` + * (i.e. for an honest Prover `w1 * eta + w2 * eta^2 + w3 * eta^3 - w4 = access`. + * This is validated by requiring `access` to be boolean + * + * For two adjacent entries in the sorted list if _both_ + * A) index values match + * B) adjacent access value is 0 (i.e. next gate is a READ) + * then + * C) both values must match. + * The gate boolean check is + * (A && B) => C === !(A && B) || C === !A || !B || C + * + * N.B. it is the responsibility of the circuit writer to ensure that every RAM cell is initialized + * with a WRITE operation. + */ + Fr access_type = (wire(p, WIRE.W_4) - ap.partial_record_check); // will be 0 or 1 for honest Prover; deg 1 or 4 + ap.access_check = access_type * (access_type - Fr.wrap(1)); // check value is 0 or 1; deg 2 or 8 + + // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta + // deg 1 or 4 + ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; + + Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); + ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + + // We can't apply the RAM consistency check identity on the final entry in the sorted list (the wires in the + // next gate would make the identity fail). We need to validate that its 'access type' bool is correct. Can't + // do with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access + // type is correct, to cover this edge case + // deg 2 or 4 + ap.next_gate_access_type_is_boolean = + ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; + + // Putting it all together... + evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation + * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 + + /** + * RAM Timestamp Consistency Check + * + * | w1 | w2 | w3 | w4 | + * | index | timestamp | timestamp_check | -- | + * + * Let delta_index = index_{i + 1} - index_{i} + * + * Iff delta_index == 0, timestamp_check = timestamp_{i + 1} - timestamp_i + * Else timestamp_check = 0 + */ + ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); + ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 + + /** + * Complete Contribution 12 + * The complete RAM/ROM memory identity + * Partial degree: + */ + ap.memory_identity = ap.ROM_consistency_check_identity; // deg 3 or 6 + ap.memory_identity = + ap.memory_identity + ap.RAM_timestamp_check_identity * (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9 + + // (deg 3 or 9) + (deg 4) + (deg 3) + ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + evals[13] = ap.memory_identity; + } + + // Constants for the Non-native Field relation + Fr constant LIMB_SIZE = Fr.wrap(uint256(1) << 68); + Fr constant SUBLIMB_SHIFT = Fr.wrap(uint256(1) << 14); + + // Parameters used within the Non-Native Field Relation + // A struct is used to work around stack too deep. This relation has alot of variables + struct NnfParams { + Fr limb_subproduct; + Fr non_native_field_gate_1; + Fr non_native_field_gate_2; + Fr non_native_field_gate_3; + Fr limb_accumulator_1; + Fr limb_accumulator_2; + Fr nnf_identity; + } + + function accumulateNnfRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + NnfParams memory ap; + + /** + * Contribution 12 + * Non native field arithmetic gate 2 + * deg 4 + * + * _ _ + * / _ _ _ 14 \ + * q_2 . q_4 | (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2 - w_3 - w_4 | + * \_ _/ + * + * + */ + ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); + ap.non_native_field_gate_2 = + (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); + + ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; + ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.non_native_field_gate_1 = ap.limb_subproduct; + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); + + ap.non_native_field_gate_3 = ap.limb_subproduct; + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); + + Fr non_native_field_identity = + ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; + non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); + + // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm + // deg 2 + ap.limb_accumulator_1 = wire(p, WIRE.W_R_SHIFT) * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L_SHIFT); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_O); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_R); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L); + ap.limb_accumulator_1 = ap.limb_accumulator_1 - wire(p, WIRE.W_4); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * wire(p, WIRE.Q_4); + + // ((((w3' * 2^14 + w2') * 2^14 + w1') * 2^14 + w4) * 2^14 + w3 - w4') * qm + // deg 2 + ap.limb_accumulator_2 = wire(p, WIRE.W_O_SHIFT) * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_R_SHIFT); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_L_SHIFT); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_4); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_O); + ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); + + Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; + limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 + + ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; + ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); + evals[19] = ap.nnf_identity; + } + + struct PoseidonExternalParams { + Fr s1; + Fr s2; + Fr s3; + Fr s4; + Fr u1; + Fr u2; + Fr u3; + Fr u4; + Fr t0; + Fr t1; + Fr t2; + Fr t3; + Fr v1; + Fr v2; + Fr v3; + Fr v4; + Fr q_pos_by_scaling; + } + + function accumulatePoseidonExternalRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + PoseidonExternalParams memory ep; + + ep.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L); + ep.s2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_R); + ep.s3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_O); + ep.s4 = wire(p, WIRE.W_4) + wire(p, WIRE.Q_4); + + ep.u1 = ep.s1 * ep.s1 * ep.s1 * ep.s1 * ep.s1; + ep.u2 = ep.s2 * ep.s2 * ep.s2 * ep.s2 * ep.s2; + ep.u3 = ep.s3 * ep.s3 * ep.s3 * ep.s3 * ep.s3; + ep.u4 = ep.s4 * ep.s4 * ep.s4 * ep.s4 * ep.s4; + // matrix mul v = M_E * u with 14 additions + ep.t0 = ep.u1 + ep.u2; // u_1 + u_2 + ep.t1 = ep.u3 + ep.u4; // u_3 + u_4 + ep.t2 = ep.u2 + ep.u2 + ep.t1; // 2u_2 + // ep.t2 += ep.t1; // 2u_2 + u_3 + u_4 + ep.t3 = ep.u4 + ep.u4 + ep.t0; // 2u_4 + // ep.t3 += ep.t0; // u_1 + u_2 + 2u_4 + ep.v4 = ep.t1 + ep.t1; + ep.v4 = ep.v4 + ep.v4 + ep.t3; + // ep.v4 += ep.t3; // u_1 + u_2 + 4u_3 + 6u_4 + ep.v2 = ep.t0 + ep.t0; + ep.v2 = ep.v2 + ep.v2 + ep.t2; + // ep.v2 += ep.t2; // 4u_1 + 6u_2 + u_3 + u_4 + ep.v1 = ep.t3 + ep.v2; // 5u_1 + 7u_2 + u_3 + 3u_4 + ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 + + ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; + evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + + evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + + evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + + evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + } + + struct PoseidonInternalParams { + Fr u1; + Fr u2; + Fr u3; + Fr u4; + Fr u_sum; + Fr v1; + Fr v2; + Fr v3; + Fr v4; + Fr s1; + Fr q_pos_by_scaling; + } + + function accumulatePoseidonInternalRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + PoseidonInternalParams memory ip; + + Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ + FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), + FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), + FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), + FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) + ]; + + // add round constants + ip.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L); + + // apply s-box round + ip.u1 = ip.s1 * ip.s1 * ip.s1 * ip.s1 * ip.s1; + ip.u2 = wire(p, WIRE.W_R); + ip.u3 = wire(p, WIRE.W_O); + ip.u4 = wire(p, WIRE.W_4); + + // matrix mul with v = M_I * u 4 muls and 7 additions + ip.u_sum = ip.u1 + ip.u2 + ip.u3 + ip.u4; + + ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; + + ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; + evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + + ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; + evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + + ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; + evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + + ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; + evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + } + + // Batch subrelation evaluations using precomputed powers of alpha + // First subrelation is implicitly scaled by 1, subsequent ones use powers from the subrelationChallenges array + function scaleAndBatchSubrelations( + Fr[NUMBER_OF_SUBRELATIONS] memory evaluations, + Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges + ) internal pure returns (Fr accumulator) { + accumulator = evaluations[0]; + + for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { + accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; + } + } +} + +// Field arithmetic libraries - prevent littering the code with modmul / addmul + +library CommitmentSchemeLib { + using FrLib for Fr; + + // Avoid stack too deep + struct ShpleminiIntermediates { + Fr unshiftedScalar; + Fr shiftedScalar; + Fr unshiftedScalarNeg; + Fr shiftedScalarNeg; + // Scalar to be multiplied by [1]₁ + Fr constantTermAccumulator; + // Accumulator for powers of rho + Fr batchingChallenge; + // Linear combination of multilinear (sumcheck) evaluations and powers of rho + Fr batchedEvaluation; + Fr[4] denominators; + Fr[4] batchingScalars; + // 1/(z - r^{2^i}) for i = 0, ..., logSize, dynamically updated + Fr posInvertedDenominator; + // 1/(z + r^{2^i}) for i = 0, ..., logSize, dynamically updated + Fr negInvertedDenominator; + // ν^{2i} * 1/(z - r^{2^i}) + Fr scalingFactorPos; + // ν^{2i+1} * 1/(z + r^{2^i}) + Fr scalingFactorNeg; + // Fold_i(r^{2^i}) reconstructed by Verifier + Fr[] foldPosEvaluations; + } + + function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { + Fr[] memory squares = new Fr[](logN); + squares[0] = r; + for (uint256 i = 1; i < logN; ++i) { + squares[i] = squares[i - 1].sqr(); + } + return squares; + } + // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., m-1 + + function computeFoldPosEvaluations( + Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckUChallenges, + Fr batchedEvalAccumulator, + Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvaluations, + Fr[] memory geminiEvalChallengePowers, + uint256 logSize + ) internal view returns (Fr[] memory) { + Fr[] memory foldPosEvaluations = new Fr[](logSize); + for (uint256 i = logSize; i > 0; --i) { + Fr challengePower = geminiEvalChallengePowers[i - 1]; + Fr u = sumcheckUChallenges[i - 1]; + + Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] + * (challengePower * (ONE - u) - u)); + // Divide by the denominator + batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); + + batchedEvalAccumulator = batchedEvalRoundAcc; + foldPosEvaluations[i - 1] = batchedEvalRoundAcc; + } + return foldPosEvaluations; + } +} + +uint256 constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; // EC group order. F_q + +function bytes32ToString(bytes32 value) pure returns (string memory result) { + bytes memory alphabet = "0123456789abcdef"; + + bytes memory str = new bytes(66); + str[0] = "0"; + str[1] = "x"; + for (uint256 i = 0; i < 32; i++) { + str[2 + i * 2] = alphabet[uint8(value[i] >> 4)]; + str[3 + i * 2] = alphabet[uint8(value[i] & 0x0f)]; + } + result = string(str); +} + +// Fr utility + +function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { + scalar = FrLib.fromBytes32(bytes32(proofSection)); +} + +// EC Point utilities +function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { + point = Honk.G1Point({ + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q + }); +} + +function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { + point.y = (Q - point.y) % Q; + return point; +} + +/** + * Convert the pairing points to G1 points. + * + * The pairing points are serialised as an array of 68 bit limbs representing two points + * The lhs of a pairing operation and the rhs of a pairing operation + * + * There are 4 fields for each group element, leaving 8 fields for each side of the pairing. + * + * @param pairingPoints The pairing points to convert. + * @return lhs + * @return rhs + */ +function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) + pure + returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) +{ + uint256 lhsX = Fr.unwrap(pairingPoints[0]); + lhsX |= Fr.unwrap(pairingPoints[1]) << 68; + lhsX |= Fr.unwrap(pairingPoints[2]) << 136; + lhsX |= Fr.unwrap(pairingPoints[3]) << 204; + lhs.x = lhsX; + + uint256 lhsY = Fr.unwrap(pairingPoints[4]); + lhsY |= Fr.unwrap(pairingPoints[5]) << 68; + lhsY |= Fr.unwrap(pairingPoints[6]) << 136; + lhsY |= Fr.unwrap(pairingPoints[7]) << 204; + lhs.y = lhsY; + + uint256 rhsX = Fr.unwrap(pairingPoints[8]); + rhsX |= Fr.unwrap(pairingPoints[9]) << 68; + rhsX |= Fr.unwrap(pairingPoints[10]) << 136; + rhsX |= Fr.unwrap(pairingPoints[11]) << 204; + rhs.x = rhsX; + + uint256 rhsY = Fr.unwrap(pairingPoints[12]); + rhsY |= Fr.unwrap(pairingPoints[13]) << 68; + rhsY |= Fr.unwrap(pairingPoints[14]) << 136; + rhsY |= Fr.unwrap(pairingPoints[15]) << 204; + rhs.y = rhsY; +} + +/** + * Hash the pairing inputs from the present verification context with those extracted from the public inputs. + * + * @param proofPairingPoints Pairing points from the proof - (public inputs). + * @param accLhs Accumulator point for the left side - result of shplemini. + * @param accRhs Accumulator point for the right side - result of shplemini. + * @return recursionSeparator The recursion separator - generated from hashing the above. + */ +function generateRecursionSeparator( + Fr[PAIRING_POINTS_SIZE] memory proofPairingPoints, + Honk.G1Point memory accLhs, + Honk.G1Point memory accRhs +) pure returns (Fr recursionSeparator) { + // hash the proof aggregated X + // hash the proof aggregated Y + // hash the accum X + // hash the accum Y + + (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); + + uint256[8] memory recursionSeparatorElements; + + // Proof points + recursionSeparatorElements[0] = proofLhs.x; + recursionSeparatorElements[1] = proofLhs.y; + recursionSeparatorElements[2] = proofRhs.x; + recursionSeparatorElements[3] = proofRhs.y; + + // Accumulator points + recursionSeparatorElements[4] = accLhs.x; + recursionSeparatorElements[5] = accLhs.y; + recursionSeparatorElements[6] = accRhs.x; + recursionSeparatorElements[7] = accRhs.y; + + recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); +} + +/** + * G1 Mul with Separator + * Using the ecAdd and ecMul precompiles + * + * @param basePoint The point to multiply. + * @param other The other point to add. + * @param recursionSeperator The separator to use for the multiplication. + * @return `(recursionSeperator * basePoint) + other`. + */ +function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) + view + returns (Honk.G1Point memory) +{ + Honk.G1Point memory result; + + result = ecMul(recursionSeperator, basePoint); + result = ecAdd(result, other); + + return result; +} + +/** + * G1 Mul + * Takes a Fr value and a G1 point and uses the ecMul precompile to return the result. + * + * @param value The value to multiply the point by. + * @param point The point to multiply. + * @return result The result of the multiplication. + */ +function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { + Honk.G1Point memory result; + + assembly { + let free := mload(0x40) + // Write the point into memory (two 32 byte words) + // Memory layout: + // Address | value + // free | point.x + // free + 0x20| point.y + mstore(free, mload(point)) + mstore(add(free, 0x20), mload(add(point, 0x20))) + // Write the scalar into memory (one 32 byte word) + // Memory layout: + // Address | value + // free + 0x40| value + mstore(add(free, 0x40), value) + + // Call the ecMul precompile, it takes in the following + // [point.x, point.y, scalar], and returns the result back into the free memory location. + let success := staticcall(gas(), 0x07, free, 0x60, free, 0x40) + if iszero(success) { + revert(0, 0) + } + // Copy the result of the multiplication back into the result memory location. + // Memory layout: + // Address | value + // result | result.x + // result + 0x20| result.y + mstore(result, mload(free)) + mstore(add(result, 0x20), mload(add(free, 0x20))) + + mstore(0x40, add(free, 0x60)) + } + + return result; +} + +/** + * G1 Add + * Takes two G1 points and uses the ecAdd precompile to return the result. + * + * @param lhs The left hand side of the addition. + * @param rhs The right hand side of the addition. + * @return result The result of the addition. + */ +function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { + Honk.G1Point memory result; + + assembly { + let free := mload(0x40) + // Write lhs into memory (two 32 byte words) + // Memory layout: + // Address | value + // free | lhs.x + // free + 0x20| lhs.y + mstore(free, mload(lhs)) + mstore(add(free, 0x20), mload(add(lhs, 0x20))) + + // Write rhs into memory (two 32 byte words) + // Memory layout: + // Address | value + // free + 0x40| rhs.x + // free + 0x60| rhs.y + mstore(add(free, 0x40), mload(rhs)) + mstore(add(free, 0x60), mload(add(rhs, 0x20))) + + // Call the ecAdd precompile, it takes in the following + // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. + let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) + if iszero(success) { revert(0, 0) } + + // Copy the result of the addition back into the result memory location. + // Memory layout: + // Address | value + // result | result.x + // result + 0x20| result.y + mstore(result, mload(free)) + mstore(add(result, 0x20), mload(add(free, 0x20))) + + mstore(0x40, add(free, 0x80)) + } + + return result; +} + +function validateOnCurve(Honk.G1Point memory point) pure { + uint256 x = point.x; + uint256 y = point.y; + + bool success = false; + assembly { + let xx := mulmod(x, x, Q) + success := eq(mulmod(y, y, Q), addmod(mulmod(x, xx, Q), 3, Q)) + } + + require(success, "point is not on the curve"); +} + +function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { + bytes memory input = abi.encodePacked( + rhs.x, + rhs.y, + // Fixed G2 point + uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), + uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), + uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), + uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), + lhs.x, + lhs.y, + // G2 point from VK + uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), + uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), + uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), + uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) + ); + + (bool success, bytes memory result) = address(0x08).staticcall(input); + decodedResult = success && abi.decode(result, (bool)); +} + +// Field arithmetic libraries - prevent littering the code with modmul / addmul + + + + +abstract contract BaseZKHonkVerifier is IVerifier { + using FrLib for Fr; + + uint256 immutable $N; + uint256 immutable $LOG_N; + uint256 immutable $VK_HASH; + uint256 immutable $NUM_PUBLIC_INPUTS; + uint256 immutable $MSMSize; + + constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { + $N = _N; + $LOG_N = _logN; + $VK_HASH = _vkHash; + $NUM_PUBLIC_INPUTS = _numPublicInputs; + $MSMSize = NUMBER_UNSHIFTED_ZK + _logN + LIBRA_COMMITMENTS + 2; + } + + // Errors + error ProofLengthWrong(); + error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); + error PublicInputsLengthWrong(); + error SumcheckFailed(); + error ShpleminiFailed(); + error GeminiChallengeInSubgroup(); + error ConsistencyCheckFailed(); + + // Constants for proof length calculation (matching UltraKeccakZKFlavor) + uint256 constant NUM_WITNESS_ENTITIES = 8 + NUM_MASKING_POLYNOMIALS; + uint256 constant NUM_ELEMENTS_COMM = 2; // uint256 elements for curve points + uint256 constant NUM_ELEMENTS_FR = 1; // uint256 elements for field elements + uint256 constant NUM_LIBRA_EVALUATIONS = 4; // libra evaluations + + // Calculate proof size based on log_n (matching UltraKeccakZKFlavor formula) + function calculateProofSize(uint256 logN) internal pure returns (uint256) { + // Witness and Libra commitments + uint256 proofLength = NUM_WITNESS_ENTITIES * NUM_ELEMENTS_COMM; // witness commitments + proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking + + // Sumcheck + proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations + + // Libra and Gemini + proofLength += NUM_ELEMENTS_FR * 2; // Libra sum, claimed eval + proofLength += logN * NUM_ELEMENTS_FR; // Gemini a evaluations + proofLength += NUM_LIBRA_EVALUATIONS * NUM_ELEMENTS_FR; // libra evaluations + + // PCS commitments + proofLength += (logN - 1) * NUM_ELEMENTS_COMM; // Gemini Fold commitments + proofLength += NUM_ELEMENTS_COMM * 2; // Shplonk Q and KZG W commitments + + // Pairing points + proofLength += PAIRING_POINTS_SIZE; // pairing inputs carried on public inputs + + return proofLength; + } + + uint256 constant SHIFTED_COMMITMENTS_START = 30; + + function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); + + function verify(bytes calldata proof, bytes32[] calldata publicInputs) + public + view + override + returns (bool verified) + { + // Calculate expected proof size based on $LOG_N + uint256 expectedProofSize = calculateProofSize($LOG_N); + + // Check the received proof is the expected size where each field element is 32 bytes + if (proof.length != expectedProofSize * 32) { + revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); + } + + Honk.VerificationKey memory vk = loadVerificationKey(); + Honk.ZKProof memory p = ZKTranscriptLib.loadProof(proof, $LOG_N); + + if (publicInputs.length != vk.publicInputsSize - PAIRING_POINTS_SIZE) { + revert PublicInputsLengthWrong(); + } + + // Generate the fiat shamir challenges for the whole protocol + ZKTranscript memory t = + ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); + + // Derive public input delta + t.relationParameters.publicInputsDelta = computePublicInputDelta( + publicInputs, + p.pairingPointObject, + t.relationParameters.beta, + t.relationParameters.gamma, /*pubInputsOffset=*/ + 1 + ); + + // Sumcheck + if (!verifySumcheck(p, t)) revert SumcheckFailed(); + + if (!verifyShplemini(p, vk, t)) revert ShpleminiFailed(); + + verified = true; + } + + uint256 constant PERMUTATION_ARGUMENT_VALUE_SEPARATOR = 1 << 28; + + function computePublicInputDelta( + bytes32[] memory publicInputs, + Fr[PAIRING_POINTS_SIZE] memory pairingPointObject, + Fr beta, + Fr gamma, + uint256 offset + ) internal view returns (Fr publicInputDelta) { + Fr numerator = Fr.wrap(1); + Fr denominator = Fr.wrap(1); + + Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); + + { + for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { + Fr pubInput = FrLib.fromBytes32(publicInputs[i]); + + numerator = numerator * (numeratorAcc + pubInput); + denominator = denominator * (denominatorAcc + pubInput); + + numeratorAcc = numeratorAcc + beta; + denominatorAcc = denominatorAcc - beta; + } + + for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { + Fr pubInput = pairingPointObject[i]; + + numerator = numerator * (numeratorAcc + pubInput); + denominator = denominator * (denominatorAcc + pubInput); + + numeratorAcc = numeratorAcc + beta; + denominatorAcc = denominatorAcc - beta; + } + } + + // Fr delta = numerator / denominator; // TOOO: batch invert later? + publicInputDelta = FrLib.div(numerator, denominator); + } + + function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { + Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 + Fr powPartialEvaluation = Fr.wrap(1); + + // We perform sumcheck reductions over log n rounds ( the multivariate degree ) + for (uint256 round; round < $LOG_N; ++round) { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; + if (totalSum != roundTargetSum) revert SumcheckFailed(); + + Fr roundChallenge = tp.sumCheckUChallenges[round]; + + // Update the round target for the next rounf + roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); + powPartialEvaluation = + powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); + } + + // Last round + // For ZK flavors: sumcheckEvaluations has 42 elements + // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations + Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; + for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { + relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 + } + Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( + relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation + ); + + Fr evaluation = Fr.wrap(1); + for (uint256 i = 2; i < $LOG_N; i++) { + evaluation = evaluation * tp.sumCheckUChallenges[i]; + } + + grandHonkRelationSum = + grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; + verified = (grandHonkRelationSum == roundTargetSum); + } + + // Return the new target sum for the next sumcheck round + function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) + internal + view + returns (Fr targetSum) + { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) + ]; + + // To compute the next target sum, we evaluate the given univariate at a point u (challenge). + + // Performing Barycentric evaluations + // Compute B(x) + Fr numeratorValue = Fr.wrap(1); + for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { + numeratorValue = numeratorValue * (roundChallenge - Fr.wrap(i)); + } + + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; + for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { + denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); + } + + for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { + targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; + } + + // Scale the sum by the value of B(x) + targetSum = targetSum * numeratorValue; + } + + uint256 constant LIBRA_COMMITMENTS = 3; + uint256 constant LIBRA_EVALUATIONS = 4; + uint256 constant LIBRA_UNIVARIATES_LENGTH = 9; + + struct PairingInputs { + Honk.G1Point P_0; + Honk.G1Point P_1; + } + + function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) + internal + view + returns (bool verified) + { + CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack + + // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); + // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings + Fr[] memory scalars = new Fr[]($MSMSize); + Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); + + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); + + mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); + mem.shiftedScalar = + tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); + + scalars[0] = Fr.wrap(1); + commitments[0] = proof.shplonkQ; + + /* Batch multivariate opening claims, shifted and unshifted + * The vector of scalars is populated as follows: + * \f[ + * \left( + * - \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right), + * \ldots, + * - \rho^{i+k-1} \times \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right), + * - \rho^{i+k} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right), + * \ldots, + * - \rho^{k+m-1} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right) + * \right) + * \f] + * + * The following vector is concatenated to the vector of commitments: + * \f[ + * f_0, \ldots, f_{m-1}, f_{\text{shift}, 0}, \ldots, f_{\text{shift}, k-1} + * \f] + * + * Simultaneously, the evaluation of the multilinear polynomial + * \f[ + * \sum \rho^i \cdot f_i + \sum \rho^{i+k} \cdot f_{\text{shift}, i} + * \f] + * at the challenge point \f$ (u_0,\ldots, u_{n-1}) \f$ is computed. + * + * This approach minimizes the number of iterations over the commitments to multilinear polynomials + * and eliminates the need to store the powers of \f$ \rho \f$. + */ + // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] + // Start batching challenge at 1, not rho, to match non-ZK pattern + mem.batchingChallenge = Fr.wrap(1); + mem.batchedEvaluation = Fr.wrap(0); + + mem.unshiftedScalarNeg = mem.unshiftedScalar.neg(); + mem.shiftedScalarNeg = mem.shiftedScalar.neg(); + + // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) + for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { + scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; + mem.batchedEvaluation = mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); + mem.batchingChallenge = mem.batchingChallenge * tp.rho; + } + // g commitments are accumulated at r + // For each of the to be shifted commitments perform the shift in place by + // adding to the unshifted value. + // We do so, as the values are to be used in batchMul later, and as + // `a * c + b * c = (a + b) * c` this will allow us to reduce memory and compute. + // Applied to w1, w2, w3, w4 and zPerm + for (uint256 i = 0; i < NUMBER_TO_BE_SHIFTED; ++i) { + uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; + uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; + + scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); + mem.batchedEvaluation = + mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); + mem.batchingChallenge = mem.batchingChallenge * tp.rho; + } + + commitments[1] = proof.geminiMaskingPoly; + + commitments[2] = vk.qm; + commitments[3] = vk.qc; + commitments[4] = vk.ql; + commitments[5] = vk.qr; + commitments[6] = vk.qo; + commitments[7] = vk.q4; + commitments[8] = vk.qLookup; + commitments[9] = vk.qArith; + commitments[10] = vk.qDeltaRange; + commitments[11] = vk.qElliptic; + commitments[12] = vk.qMemory; + commitments[13] = vk.qNnf; + commitments[14] = vk.qPoseidon2External; + commitments[15] = vk.qPoseidon2Internal; + commitments[16] = vk.s1; + commitments[17] = vk.s2; + commitments[18] = vk.s3; + commitments[19] = vk.s4; + commitments[20] = vk.id1; + commitments[21] = vk.id2; + commitments[22] = vk.id3; + commitments[23] = vk.id4; + commitments[24] = vk.t1; + commitments[25] = vk.t2; + commitments[26] = vk.t3; + commitments[27] = vk.t4; + commitments[28] = vk.lagrangeFirst; + commitments[29] = vk.lagrangeLast; + + // Accumulate proof points + commitments[30] = proof.w1; + commitments[31] = proof.w2; + commitments[32] = proof.w3; + commitments[33] = proof.w4; + commitments[34] = proof.zPerm; + commitments[35] = proof.lookupInverses; + commitments[36] = proof.lookupReadCounts; + commitments[37] = proof.lookupReadTags; + + /* Batch gemini claims from the prover + * place the commitments to gemini aᵢ to the vector of commitments, compute the contributions from + * aᵢ(−r²ⁱ) for i=1, … , n−1 to the constant term accumulator, add corresponding scalars + * + * 1. Moves the vector + * \f[ + * \left( \text{com}(A_1), \text{com}(A_2), \ldots, \text{com}(A_{n-1}) \right) + * \f] + * to the 'commitments' vector. + * + * 2. Computes the scalars: + * \f[ + * \frac{\nu^{2}}{z + r^2}, \frac{\nu^3}{z + r^4}, \ldots, \frac{\nu^{n-1}}{z + r^{2^{n-1}}} + * \f] + * and places them into the 'scalars' vector. + * + * 3. Accumulates the summands of the constant term: + * \f[ + * \sum_{i=2}^{n-1} \frac{\nu^{i} \cdot A_i(-r^{2^i})}{z + r^{2^i}} + * \f] + * and adds them to the 'constant_term_accumulator'. + */ + + // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: + // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 + Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); + + mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; + mem.constantTermAccumulator = + mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); + + mem.batchingChallenge = tp.shplonkNu.sqr(); + uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; + + // Compute Shplonk constant term contributions from Aₗ(± r^{2ˡ}) for l = 1, ..., m-1; + // Compute scalar multipliers for each fold commitment + for (uint256 i = 0; i < $LOG_N - 1; ++i) { + bool dummy_round = i >= ($LOG_N - 1); + + if (!dummy_round) { + // Update inverted denominators + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); + + // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] + mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; + mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; + scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); + + // Accumulate the const term contribution given by + // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) + Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; + accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; + } + // Update the running power of v + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + + commitments[boundary + i] = proof.geminiFoldComms[i]; + } + + boundary += $LOG_N - 1; + + // Finalize the batch opening claim + mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); + mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); + mem.denominators[2] = mem.denominators[0]; + mem.denominators[3] = mem.denominators[0]; + + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { + Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; + mem.batchingScalars[i] = scalingFactor.neg(); + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; + mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; + } + scalars[boundary] = mem.batchingScalars[0]; + scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; + scalars[boundary + 2] = mem.batchingScalars[3]; + + for (uint256 i = 0; i < LIBRA_COMMITMENTS; i++) { + commitments[boundary++] = proof.libraCommitments[i]; + } + + commitments[boundary] = Honk.G1Point({x: 1, y: 2}); + scalars[boundary++] = mem.constantTermAccumulator; + + if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { + revert ConsistencyCheckFailed(); + } + + Honk.G1Point memory quotient_commitment = proof.kzgQuotient; + + commitments[boundary] = quotient_commitment; + scalars[boundary] = tp.shplonkZ; // evaluation challenge + + PairingInputs memory pair; + pair.P_0 = batchMul(commitments, scalars); + pair.P_1 = negateInplace(quotient_commitment); + + // Aggregate pairing points + Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); + (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = + convertPairingPointsToG1(proof.pairingPointObject); + + // Validate the points from the proof are on the curve + validateOnCurve(P_0_other); + validateOnCurve(P_1_other); + + // accumulate with aggregate points in proof + pair.P_0 = mulWithSeperator(pair.P_0, P_0_other, recursionSeparator); + pair.P_1 = mulWithSeperator(pair.P_1, P_1_other, recursionSeparator); + + return pairing(pair.P_0, pair.P_1); + } + + struct SmallSubgroupIpaIntermediates { + Fr[SUBGROUP_SIZE] challengePolyLagrange; + Fr challengePolyEval; + Fr lagrangeFirst; + Fr lagrangeLast; + Fr rootPower; + Fr[SUBGROUP_SIZE] denominators; // this has to disappear + Fr diff; + } + + function checkEvalsConsistency( + Fr[LIBRA_EVALUATIONS] memory libraPolyEvals, + Fr geminiR, + Fr[CONST_PROOF_SIZE_LOG_N] memory uChallenges, + Fr libraEval + ) internal view returns (bool check) { + Fr one = Fr.wrap(1); + Fr vanishingPolyEval = geminiR.pow(SUBGROUP_SIZE) - one; + if (vanishingPolyEval == Fr.wrap(0)) { + revert GeminiChallengeInSubgroup(); + } + + SmallSubgroupIpaIntermediates memory mem; + mem.challengePolyLagrange[0] = one; + for (uint256 round = 0; round < $LOG_N; round++) { + uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; + mem.challengePolyLagrange[currIdx] = one; + for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { + mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; + } + } + + mem.rootPower = one; + mem.challengePolyEval = Fr.wrap(0); + for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { + mem.denominators[idx] = mem.rootPower * geminiR - one; + mem.denominators[idx] = mem.denominators[idx].invert(); + mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; + mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; + } + + Fr numerator = vanishingPolyEval * Fr.wrap(SUBGROUP_SIZE).invert(); + mem.challengePolyEval = mem.challengePolyEval * numerator; + mem.lagrangeFirst = mem.denominators[0] * numerator; + mem.lagrangeLast = mem.denominators[SUBGROUP_SIZE - 1] * numerator; + + mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; + + mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) + * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); + mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; + + check = mem.diff == Fr.wrap(0); + } + + // This implementation is the same as above with different constants + function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) + internal + view + returns (Honk.G1Point memory result) + { + uint256 limit = $MSMSize; + + // Validate all points are on the curve + for (uint256 i = 0; i < limit; ++i) { + validateOnCurve(base[i]); + } + + bool success = true; + assembly { + let free := mload(0x40) + + let count := 0x01 + for {} lt(count, add(limit, 1)) { count := add(count, 1) } { + // Get loop offsets + let base_base := add(base, mul(count, 0x20)) + let scalar_base := add(scalars, mul(count, 0x20)) + + mstore(add(free, 0x40), mload(mload(base_base))) + mstore(add(free, 0x60), mload(add(0x20, mload(base_base)))) + // Add scalar + mstore(add(free, 0x80), mload(scalar_base)) + + success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) + } + + // Return the result + mstore(result, mload(free)) + mstore(add(result, 0x20), mload(add(free, 0x20))) + } + + require(success, ShpleminiFailed()); + } +} + +contract RecursiveAggregationFoldVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { + function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { + return HonkVerificationKey.loadVerificationKey(); + } +} diff --git a/packages/enclave-contracts/contracts/verifier/ThresholdDecryptedSharesAggregationBnVerifier.sol b/packages/enclave-contracts/contracts/verifier/ThresholdDecryptedSharesAggregationBnVerifier.sol new file mode 100644 index 0000000000..d7bd687db4 --- /dev/null +++ b/packages/enclave-contracts/contracts/verifier/ThresholdDecryptedSharesAggregationBnVerifier.sol @@ -0,0 +1,2452 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +pragma solidity >=0.8.21; + +uint256 constant N = 131072; +uint256 constant LOG_N = 17; +uint256 constant NUMBER_OF_PUBLIC_INPUTS = 579; +uint256 constant VK_HASH = 0x1eb29cc0a26390fad32af3a6275716d347fdcbdcc5f7c7c54c194a0cce5854e0; +library HonkVerificationKey { + function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { + Honk.VerificationKey memory vk = Honk.VerificationKey({ + circuitSize: uint256(131072), + logCircuitSize: uint256(17), + publicInputsSize: uint256(579), + ql: Honk.G1Point({ + x: uint256(0x067b40a80be55b95cc17070cf5dc78441e4868acea0ac1e46b57087051515905), + y: uint256(0x0792e6c3d76aeeed6ee7d7e443138c813b74dbcd8ce92990e6f6747adc0af054) + }), + qr: Honk.G1Point({ + x: uint256(0x148a04387a9e0a8cec6a4a0d90706f5907e8bb257fb04f8a3cfe07c18a7c1014), + y: uint256(0x225866d9efd4a5be4eb9a00016f902f844e3d81e2768b24dc92ecbd6b34176bd) + }), + qo: Honk.G1Point({ + x: uint256(0x0dacf57ed82c686ca051fca3f1820016cb49e91b5d3c4bee7f9fa19800d9ed06), + y: uint256(0x22ba5fda41aa14aeb7a3a2731fbfe3a673cf7db1d2217d32db491f4434430905) + }), + q4: Honk.G1Point({ + x: uint256(0x0f783f69a36c2bac47b9c59ff33ccc583a284a46b282591b9e9bbb85d8b5e5dc), + y: uint256(0x029b91609a06e94924f0d83530fc01f3427f5ab729a0959903108c2ccc86825c) + }), + qm: Honk.G1Point({ + x: uint256(0x039702f4c2b2b49fd29d82b077cbf9d1df2871e0fbade17241573ca5021603fc), + y: uint256(0x03270b3b2673844695842df11cdbabf54acafe03642adfdfbf69834247ed0720) + }), + qc: Honk.G1Point({ + x: uint256(0x1ccb0308d0359b8835480fa7dd829fdbf16b136d57e85c1621efb1679fe1a94c), + y: uint256(0x207076fed3c60bbeb2df5f48c86b12f4027c523f9b8e69456ca51643fea3de1d) + }), + qLookup: Honk.G1Point({ + x: uint256(0x11beb2731ab8cf46c0a52666bb457fa74d4b635043bb5689a35a2aee24990b80), + y: uint256(0x2f1fba989036c44aedb609d0d3341ccb842c2d1cd7c80cea4a95d0eb45305770) + }), + qArith: Honk.G1Point({ + x: uint256(0x28ae1f7459c3c5d9ca2f80f8e5c28bb301d0245228bade0e2bb962b4bfbe301b), + y: uint256(0x0622f7db14381bbecf862e186cfa570588160466ae7d4fed4b35a70d99af914f) + }), + qDeltaRange: Honk.G1Point({ + x: uint256(0x233983f621ab48bdce4f7216bdbc54c49463e90d88301b9ab224fca61c93dba0), + y: uint256(0x265c3834d63be0bc710c9b4a48015e2442a3b9bf63caec9db0064dfb1bd569c4) + }), + qElliptic: Honk.G1Point({ + x: uint256(0x2dfc248e28ad889b5b692077f338d28b2f687c6e909b9693f8e8fd25cefc3528), + y: uint256(0x16e2cbc305166ea90a426987efff2020827d8c548ea4155c659df296f14f00fd) + }), + qMemory: Honk.G1Point({ + x: uint256(0x06d21b82f49e04e078d4ad30cd6828eaa66c1bf984bc52b79de8c786fe4dfe7a), + y: uint256(0x301aa91e167bbc765996ac6d2889e7d49f0734bc7abb3a4b85900143556a1b3f) + }), + qNnf: Honk.G1Point({ + x: uint256(0x1222d34f53e6b36138627277789ad131ea24f79caca2d5f59193c5e3a7dfdde7), + y: uint256(0x245a380726977d41c9f0a9a0ba33608994eb3660de6a8efba38009df93da679a) + }), + qPoseidon2External: Honk.G1Point({ + x: uint256(0x0d815a04effc6e57ec95f3ac112f76ab4a9d993d044626ceb54f967c8d77d8e1), + y: uint256(0x1df4c065a1129d5cbdf244cb9cc2eaf406c2f5fbab4c393347d57ae5e2ab881f) + }), + qPoseidon2Internal: Honk.G1Point({ + x: uint256(0x118733dc1db84685f4160119cb0cd9a51b831cf0643e914daf1c63fc79672979), + y: uint256(0x27b63107b52edf239acb1bd47e24c97094422cc2bba51ee0f54b2707946be556) + }), + s1: Honk.G1Point({ + x: uint256(0x2e7a11cec89df0ad6b730cf80be4246e56e1667f55e35a22c9b39b196226135b), + y: uint256(0x0891267297e339bedeef69881477b4e5c19e761e127e3d3772714f92ade80fd3) + }), + s2: Honk.G1Point({ + x: uint256(0x16f90d7d5c3fd7ab9f490dd5d2b94afea698039e2456c1d5f0e22159348d1e0c), + y: uint256(0x29ad721b49b02192f5323326fd803b102f7a09b0371c537db998014fa14c3079) + }), + s3: Honk.G1Point({ + x: uint256(0x0a5e4e29375b31024f09dd662b460eb25930835255c62dc74d62d0c983981e42), + y: uint256(0x069adf56537452ecad2fcd8cdea1291fbf273d7e752d9ba0812900e7215c8fc0) + }), + s4: Honk.G1Point({ + x: uint256(0x1bd554f207de2e42cbc94e8579154a07e37f3ba9d80832f28e38dffaaccd1ade), + y: uint256(0x22261d045539dbaabf26f23773b365b2ddd30af639a8b15a56e777f5607bf435) + }), + t1: Honk.G1Point({ + x: uint256(0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26), + y: uint256(0x0015b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f) + }), + t2: Honk.G1Point({ + x: uint256(0x1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e), + y: uint256(0x305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d19) + }), + t3: Honk.G1Point({ + x: uint256(0x061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d), + y: uint256(0x1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e) + }), + t4: Honk.G1Point({ + x: uint256(0x043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce), + y: uint256(0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854) + }), + id1: Honk.G1Point({ + x: uint256(0x11769064ef79db1f35b23b8b1e5c118e769e1a41b912f49dfdbffbacf23d0564), + y: uint256(0x16f17c1721186bbd19d09f1645f968677cb1ad0c6b656c3849a4b7f27d5279d9) + }), + id2: Honk.G1Point({ + x: uint256(0x0cad91097e151471556985b94a0188b759472f9bb3295fe6de6846f2d4fe27e1), + y: uint256(0x042f4a199546b40704a9c4822bfd20a91bc478055dc5676c7347f64545b47f4d) + }), + id3: Honk.G1Point({ + x: uint256(0x1286a8beb1993863980820c27dfb00f93f8949f60933042e5aa8275dd48e55f7), + y: uint256(0x2037878d67d1009eb6dac43c5f6f076f884d456e057b000912d40a2ec440df1f) + }), + id4: Honk.G1Point({ + x: uint256(0x1c0413fa3552ba4738b423829d55fcc03c912bf931336778a458e1c25cd4f558), + y: uint256(0x190904e6db04c862bd9d7406029e03a1103a513203eb6d8faa010021eedf18d4) + }), + lagrangeFirst: Honk.G1Point({ + x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), + y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) + }), + lagrangeLast: Honk.G1Point({ + x: uint256(0x0ad44c49c91e4374983e023f10e6256c39d27b2857eb2d2ce82a82b51e02dc38), + y: uint256(0x23ad26f46d7b04b00d6ebe50273765d49c59d9db9ae22dd33909bc3c9efc5308) + }) + }); + return vk; + } +} + +pragma solidity ^0.8.27; + +interface IVerifier { + function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); +} + +type Fr is uint256; + +using {add as +} for Fr global; +using {sub as -} for Fr global; +using {mul as *} for Fr global; + +using {exp as ^} for Fr global; +using {notEqual as !=} for Fr global; +using {equal as ==} for Fr global; + +uint256 constant SUBGROUP_SIZE = 256; +uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order +uint256 constant P = MODULUS; +Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); +Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); +Fr constant ONE = Fr.wrap(1); +Fr constant ZERO = Fr.wrap(0); +// Instantiation + +library FrLib { + function from(uint256 value) internal pure returns (Fr) { + unchecked { + return Fr.wrap(value % MODULUS); + } + } + + function fromBytes32(bytes32 value) internal pure returns (Fr) { + unchecked { + return Fr.wrap(uint256(value) % MODULUS); + } + } + + function toBytes32(Fr value) internal pure returns (bytes32) { + unchecked { + return bytes32(Fr.unwrap(value)); + } + } + + function invert(Fr value) internal view returns (Fr) { + uint256 v = Fr.unwrap(value); + uint256 result; + + // Call the modexp precompile to invert in the field + assembly { + let free := mload(0x40) + mstore(free, 0x20) + mstore(add(free, 0x20), 0x20) + mstore(add(free, 0x40), 0x20) + mstore(add(free, 0x60), v) + mstore(add(free, 0x80), sub(MODULUS, 2)) + mstore(add(free, 0xa0), MODULUS) + let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) + if iszero(success) { + revert(0, 0) + } + result := mload(0x00) + mstore(0x40, add(free, 0x80)) + } + + return Fr.wrap(result); + } + + function pow(Fr base, uint256 v) internal view returns (Fr) { + uint256 b = Fr.unwrap(base); + uint256 result; + + // Call the modexp precompile to invert in the field + assembly { + let free := mload(0x40) + mstore(free, 0x20) + mstore(add(free, 0x20), 0x20) + mstore(add(free, 0x40), 0x20) + mstore(add(free, 0x60), b) + mstore(add(free, 0x80), v) + mstore(add(free, 0xa0), MODULUS) + let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) + if iszero(success) { + revert(0, 0) + } + result := mload(0x00) + mstore(0x40, add(free, 0x80)) + } + + return Fr.wrap(result); + } + + function div(Fr numerator, Fr denominator) internal view returns (Fr) { + unchecked { + return numerator * invert(denominator); + } + } + + function sqr(Fr value) internal pure returns (Fr) { + unchecked { + return value * value; + } + } + + function unwrap(Fr value) internal pure returns (uint256) { + unchecked { + return Fr.unwrap(value); + } + } + + function neg(Fr value) internal pure returns (Fr) { + unchecked { + return Fr.wrap(MODULUS - Fr.unwrap(value)); + } + } +} + +// Free functions +function add(Fr a, Fr b) pure returns (Fr) { + unchecked { + return Fr.wrap(addmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS)); + } +} + +function mul(Fr a, Fr b) pure returns (Fr) { + unchecked { + return Fr.wrap(mulmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS)); + } +} + +function sub(Fr a, Fr b) pure returns (Fr) { + unchecked { + return Fr.wrap(addmod(Fr.unwrap(a), MODULUS - Fr.unwrap(b), MODULUS)); + } +} + +function exp(Fr base, Fr exponent) pure returns (Fr) { + if (Fr.unwrap(exponent) == 0) return Fr.wrap(1); + // Implement exponent with a loop as we will overflow otherwise + for (uint256 i = 1; i < Fr.unwrap(exponent); i += i) { + base = base * base; + } + return base; +} + +function notEqual(Fr a, Fr b) pure returns (bool) { + unchecked { + return Fr.unwrap(a) != Fr.unwrap(b); + } +} + +function equal(Fr a, Fr b) pure returns (bool) { + unchecked { + return Fr.unwrap(a) == Fr.unwrap(b); + } +} + +uint256 constant CONST_PROOF_SIZE_LOG_N = 28; + +uint256 constant NUMBER_OF_SUBRELATIONS = 28; +uint256 constant BATCHED_RELATION_PARTIAL_LENGTH = 8; +uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; +uint256 constant NUMBER_OF_ENTITIES = 41; +// The number of entities added for ZK (gemini_masking_poly) +uint256 constant NUM_MASKING_POLYNOMIALS = 1; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED = 36; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_TO_BE_SHIFTED = 5; +uint256 constant PAIRING_POINTS_SIZE = 16; + +uint256 constant FIELD_ELEMENT_SIZE = 0x20; +uint256 constant GROUP_ELEMENT_SIZE = 0x40; + +// Powers of alpha used to batch subrelations (alpha, alpha^2, ..., alpha^(NUM_SUBRELATIONS-1)) +uint256 constant NUMBER_OF_ALPHAS = NUMBER_OF_SUBRELATIONS - 1; + +// ENUM FOR WIRES +enum WIRE { + Q_M, + Q_C, + Q_L, + Q_R, + Q_O, + Q_4, + Q_LOOKUP, + Q_ARITH, + Q_RANGE, + Q_ELLIPTIC, + Q_MEMORY, + Q_NNF, + Q_POSEIDON2_EXTERNAL, + Q_POSEIDON2_INTERNAL, + SIGMA_1, + SIGMA_2, + SIGMA_3, + SIGMA_4, + ID_1, + ID_2, + ID_3, + ID_4, + TABLE_1, + TABLE_2, + TABLE_3, + TABLE_4, + LAGRANGE_FIRST, + LAGRANGE_LAST, + W_L, + W_R, + W_O, + W_4, + Z_PERM, + LOOKUP_INVERSES, + LOOKUP_READ_COUNTS, + LOOKUP_READ_TAGS, + W_L_SHIFT, + W_R_SHIFT, + W_O_SHIFT, + W_4_SHIFT, + Z_PERM_SHIFT +} + +library Honk { + struct G1Point { + uint256 x; + uint256 y; + } + + struct VerificationKey { + // Misc Params + uint256 circuitSize; + uint256 logCircuitSize; + uint256 publicInputsSize; + // Selectors + G1Point qm; + G1Point qc; + G1Point ql; + G1Point qr; + G1Point qo; + G1Point q4; + G1Point qLookup; // Lookup + G1Point qArith; // Arithmetic widget + G1Point qDeltaRange; // Delta Range sort + G1Point qMemory; // Memory + G1Point qNnf; // Non-native Field + G1Point qElliptic; // Auxillary + G1Point qPoseidon2External; + G1Point qPoseidon2Internal; + // Copy constraints + G1Point s1; + G1Point s2; + G1Point s3; + G1Point s4; + // Copy identity + G1Point id1; + G1Point id2; + G1Point id3; + G1Point id4; + // Precomputed lookup table + G1Point t1; + G1Point t2; + G1Point t3; + G1Point t4; + // Fixed first and last + G1Point lagrangeFirst; + G1Point lagrangeLast; + } + + struct RelationParameters { + // challenges + Fr eta; + Fr etaTwo; + Fr etaThree; + Fr beta; + Fr gamma; + // derived + Fr publicInputsDelta; + } + + struct Proof { + // Pairing point object + Fr[PAIRING_POINTS_SIZE] pairingPointObject; + // Free wires + G1Point w1; + G1Point w2; + G1Point w3; + G1Point w4; + // Lookup helpers - Permutations + G1Point zPerm; + // Lookup helpers - logup + G1Point lookupReadCounts; + G1Point lookupReadTags; + G1Point lookupInverses; + // Sumcheck + Fr[BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates; + Fr[NUMBER_OF_ENTITIES] sumcheckEvaluations; + // Shplemini + G1Point[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms; + Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations; + G1Point shplonkQ; + G1Point kzgQuotient; + } + + /// forge-lint: disable-next-item(pascal-case-struct) + struct ZKProof { + // Pairing point object + Fr[PAIRING_POINTS_SIZE] pairingPointObject; + // ZK: Gemini masking polynomial commitment (sent first, right after public inputs) + G1Point geminiMaskingPoly; + // Commitments to wire polynomials + G1Point w1; + G1Point w2; + G1Point w3; + G1Point w4; + // Commitments to logup witness polynomials + G1Point lookupReadCounts; + G1Point lookupReadTags; + G1Point lookupInverses; + // Commitment to grand permutation polynomial + G1Point zPerm; + G1Point[3] libraCommitments; + // Sumcheck + Fr libraSum; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates; + Fr libraEvaluation; + Fr[NUMBER_OF_ENTITIES_ZK] sumcheckEvaluations; // Includes gemini_masking_poly eval at index 0 (first position) + // Shplemini + G1Point[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms; + Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations; + Fr[4] libraPolyEvals; + G1Point shplonkQ; + G1Point kzgQuotient; + } +} + +// ZKTranscript library to generate fiat shamir challenges, the ZK transcript only differest +/// forge-lint: disable-next-item(pascal-case-struct) +struct ZKTranscript { + // Oink + Honk.RelationParameters relationParameters; + Fr[NUMBER_OF_ALPHAS] alphas; // Powers of alpha: [alpha, alpha^2, ..., alpha^(NUM_SUBRELATIONS-1)] + Fr[CONST_PROOF_SIZE_LOG_N] gateChallenges; + // Sumcheck + Fr libraChallenge; + Fr[CONST_PROOF_SIZE_LOG_N] sumCheckUChallenges; + // Shplemini + Fr rho; + Fr geminiR; + Fr shplonkNu; + Fr shplonkZ; + // Derived + Fr publicInputsDelta; +} + +library ZKTranscriptLib { + function generateTranscript( + Honk.ZKProof memory proof, + bytes32[] calldata publicInputs, + uint256 vkHash, + uint256 publicInputsSize, + uint256 logN + ) external pure returns (ZKTranscript memory t) { + Fr previousChallenge; + (t.relationParameters, previousChallenge) = + generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); + + (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); + + (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); + (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); + + (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); + + (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); + + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); + + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); + return t; + } + + function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { + uint256 challengeU256 = uint256(Fr.unwrap(challenge)); + // Split into two equal 127-bit chunks (254/2) + uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits + uint256 hi = challengeU256 >> 127; + first = FrLib.fromBytes32(bytes32(lo)); + second = FrLib.fromBytes32(bytes32(hi)); + } + + function generateRelationParametersChallenges( + Honk.ZKProof memory proof, + bytes32[] calldata publicInputs, + uint256 vkHash, + uint256 publicInputsSize, + Fr previousChallenge + ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { + (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = + generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + } + + function generateEtaChallenge( + Honk.ZKProof memory proof, + bytes32[] calldata publicInputs, + uint256 vkHash, + uint256 publicInputsSize + ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { + // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) + bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); + round0[0] = bytes32(vkHash); + + for (uint256 i = 0; i < publicInputsSize - PAIRING_POINTS_SIZE; i++) { + round0[1 + i] = bytes32(publicInputs[i]); + } + for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); + } + + // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) + round0[1 + publicInputsSize] = bytes32(proof.geminiMaskingPoly.x); + round0[1 + publicInputsSize + 1] = bytes32(proof.geminiMaskingPoly.y); + + // Create the first challenge + // Note: w4 is added to the challenge later on + round0[1 + publicInputsSize + 2] = bytes32(proof.w1.x); + round0[1 + publicInputsSize + 3] = bytes32(proof.w1.y); + round0[1 + publicInputsSize + 4] = bytes32(proof.w2.x); + round0[1 + publicInputsSize + 5] = bytes32(proof.w2.y); + round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); + round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); + + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); + (eta, etaTwo) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + + (etaThree,) = splitChallenge(previousChallenge); + } + + function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) + { + bytes32[7] memory round1; + round1[0] = FrLib.toBytes32(previousChallenge); + round1[1] = bytes32(proof.lookupReadCounts.x); + round1[2] = bytes32(proof.lookupReadCounts.y); + round1[3] = bytes32(proof.lookupReadTags.x); + round1[4] = bytes32(proof.lookupReadTags.y); + round1[5] = bytes32(proof.w4.x); + round1[6] = bytes32(proof.w4.y); + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); + (beta, gamma) = splitChallenge(nextPreviousChallenge); + } + + // Alpha challenges non-linearise the gate contributions + function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) + { + // Generate the original sumcheck alpha 0 by hashing zPerm and zLookup + uint256[5] memory alpha0; + alpha0[0] = Fr.unwrap(previousChallenge); + alpha0[1] = proof.lookupInverses.x; + alpha0[2] = proof.lookupInverses.y; + alpha0[3] = proof.zPerm.x; + alpha0[4] = proof.zPerm.y; + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); + Fr alpha; + (alpha,) = splitChallenge(nextPreviousChallenge); + + // Compute powers of alpha for batching subrelations + alphas[0] = alpha; + for (uint256 i = 1; i < NUMBER_OF_ALPHAS; i++) { + alphas[i] = alphas[i - 1] * alpha; + } + } + + function generateGateChallenges(Fr previousChallenge, uint256 logN) + internal + pure + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) + { + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + (gateChallenges[0],) = splitChallenge(previousChallenge); + for (uint256 i = 1; i < logN; i++) { + gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; + } + nextPreviousChallenge = previousChallenge; + } + + function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr libraChallenge, Fr nextPreviousChallenge) + { + // 2 comm, 1 sum, 1 challenge + uint256[4] memory challengeData; + challengeData[0] = Fr.unwrap(previousChallenge); + challengeData[1] = proof.libraCommitments[0].x; + challengeData[2] = proof.libraCommitments[0].y; + challengeData[3] = Fr.unwrap(proof.libraSum); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); + (libraChallenge,) = splitChallenge(nextPreviousChallenge); + } + + function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) + { + for (uint256 i = 0; i < logN; i++) { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; + univariateChal[0] = prevChallenge; + + for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { + univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; + } + prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); + + (sumcheckChallenges[i],) = splitChallenge(prevChallenge); + } + nextPreviousChallenge = prevChallenge; + } + + // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) + function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr rho, Fr nextPreviousChallenge) + { + uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; + rhoChallengeElements[0] = Fr.unwrap(prevChallenge); + uint256 i; + for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { + rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); + } + rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); + i += 1; + rhoChallengeElements[i] = proof.libraCommitments[1].x; + rhoChallengeElements[i + 1] = proof.libraCommitments[1].y; + i += 2; + rhoChallengeElements[i] = proof.libraCommitments[2].x; + rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); + (rho,) = splitChallenge(nextPreviousChallenge); + } + + function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr geminiR, Fr nextPreviousChallenge) + { + uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); + gR[0] = Fr.unwrap(prevChallenge); + + for (uint256 i = 0; i < logN - 1; i++) { + gR[1 + i * 2] = proof.geminiFoldComms[i].x; + gR[2 + i * 2] = proof.geminiFoldComms[i].y; + } + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); + + (geminiR,) = splitChallenge(nextPreviousChallenge); + } + + function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr shplonkNu, Fr nextPreviousChallenge) + { + uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); + shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); + + for (uint256 i = 1; i <= logN; i++) { + shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); + } + + uint256 libraIdx = 0; + for (uint256 i = logN + 1; i <= logN + 4; i++) { + shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); + libraIdx++; + } + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); + (shplonkNu,) = splitChallenge(nextPreviousChallenge); + } + + function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr shplonkZ, Fr nextPreviousChallenge) + { + uint256[3] memory shplonkZChallengeElements; + shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); + + shplonkZChallengeElements[1] = proof.shplonkQ.x; + shplonkZChallengeElements[2] = proof.shplonkQ.y; + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); + (shplonkZ,) = splitChallenge(nextPreviousChallenge); + } + + function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { + uint256 boundary = 0x0; + + // Pairing point object + for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { + p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + } + + // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) + p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + + // Commitments + p.w1 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.w2 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.w3 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + + // Lookup / Permutation Helper Commitments + p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + + p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + // Sumcheck univariates + for (uint256 i = 0; i < logN; i++) { + for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { + p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + } + } + + // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) + for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { + p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + } + + p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + + p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + + // Gemini + // Read gemini fold univariates + for (uint256 i = 0; i < logN - 1; i++) { + p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + } + + // Read gemini a evaluations + for (uint256 i = 0; i < logN; i++) { + p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + } + + for (uint256 i = 0; i < 4; i++) { + p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + } + + // Shplonk + p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + // KZG + p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + } +} + +// Field arithmetic libraries + +library RelationsLib { + Fr internal constant GRUMPKIN_CURVE_B_PARAMETER_NEGATED = Fr.wrap(17); // -(-17) + + function accumulateRelationEvaluations( + Fr[NUMBER_OF_ENTITIES] memory purportedEvaluations, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges, + Fr powPartialEval + ) internal pure returns (Fr accumulator) { + Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; + + // Accumulate all relations in Ultra Honk - each with varying number of subrelations + accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); + + // batch the subrelations with the precomputed alpha powers to obtain the full honk relation + accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); + } + + /** + * Aesthetic helper function that is used to index by enum into proof.sumcheckEvaluations, it avoids + * the relation checking code being cluttered with uint256 type casting, which is often a different colour in code + * editors, and thus is noisy. + */ + function wire(Fr[NUMBER_OF_ENTITIES] memory p, WIRE _wire) internal pure returns (Fr) { + return p[uint256(_wire)]; + } + + uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; + /** + * Ultra Arithmetic Relation + * + */ + + function accumulateArithmeticRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + // Relation 0 + Fr q_arith = wire(p, WIRE.Q_ARITH); + { + Fr neg_half = Fr.wrap(NEG_HALF_MODULO_P); + + Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half; + accum = accum + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + wire(p, WIRE.Q_C); + accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); + accum = accum * q_arith; + accum = accum * domainSep; + evals[0] = accum; + } + + // Relation 1 + { + Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); + accum = accum * (q_arith - Fr.wrap(2)); + accum = accum * (q_arith - ONE); + accum = accum * q_arith; + accum = accum * domainSep; + evals[1] = accum; + } + } + + function accumulatePermutationRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + Fr grand_product_numerator; + Fr grand_product_denominator; + + { + Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; + num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + + grand_product_numerator = num; + } + { + Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; + den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); + + grand_product_denominator = den; + } + + // Contribution 2 + { + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; + + acc = acc + - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) + * grand_product_denominator); + acc = acc * domainSep; + evals[2] = acc; + } + + // Contribution 3 + { + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + evals[3] = acc; + } + } + + function accumulateLogDerivativeLookupRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + Fr write_term; + Fr read_term; + + // Calculate the write term (the table accumulation) + { + write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); + } + + // Calculate the write term + { + Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); + + read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); + } + + Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; + Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; + + Fr inverse_exists_xor = + wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) + - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + + // Inverse calculated correctly relation + Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; + accumulatorNone = accumulatorNone * domainSep; + + // Inverse + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; + + Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); + + Fr read_tag_boolean_relation = read_tag * read_tag - read_tag; + + evals[4] = accumulatorNone; + evals[5] = accumulatorOne; + evals[6] = read_tag_boolean_relation * domainSep; + } + + function accumulateDeltaRangeRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + Fr minus_one = ZERO - ONE; + Fr minus_two = ZERO - Fr.wrap(2); + Fr minus_three = ZERO - Fr.wrap(3); + + // Compute wire differences + Fr delta_1 = wire(p, WIRE.W_R) - wire(p, WIRE.W_L); + Fr delta_2 = wire(p, WIRE.W_O) - wire(p, WIRE.W_R); + Fr delta_3 = wire(p, WIRE.W_4) - wire(p, WIRE.W_O); + Fr delta_4 = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_4); + + // Contribution 6 + { + Fr acc = delta_1; + acc = acc * (delta_1 + minus_one); + acc = acc * (delta_1 + minus_two); + acc = acc * (delta_1 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[7] = acc; + } + + // Contribution 7 + { + Fr acc = delta_2; + acc = acc * (delta_2 + minus_one); + acc = acc * (delta_2 + minus_two); + acc = acc * (delta_2 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[8] = acc; + } + + // Contribution 8 + { + Fr acc = delta_3; + acc = acc * (delta_3 + minus_one); + acc = acc * (delta_3 + minus_two); + acc = acc * (delta_3 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[9] = acc; + } + + // Contribution 9 + { + Fr acc = delta_4; + acc = acc * (delta_4 + minus_one); + acc = acc * (delta_4 + minus_two); + acc = acc * (delta_4 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[10] = acc; + } + } + + struct EllipticParams { + // Points + Fr x_1; + Fr y_1; + Fr x_2; + Fr y_2; + Fr y_3; + Fr x_3; + // push accumulators into memory + Fr x_double_identity; + } + + function accumulateEllipticRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + EllipticParams memory ep; + ep.x_1 = wire(p, WIRE.W_R); + ep.y_1 = wire(p, WIRE.W_O); + + ep.x_2 = wire(p, WIRE.W_L_SHIFT); + ep.y_2 = wire(p, WIRE.W_4_SHIFT); + ep.y_3 = wire(p, WIRE.W_O_SHIFT); + ep.x_3 = wire(p, WIRE.W_R_SHIFT); + + Fr q_sign = wire(p, WIRE.Q_L); + Fr q_is_double = wire(p, WIRE.Q_M); + + // Contribution 10 point addition, x-coordinate check + // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0 + Fr x_diff = (ep.x_2 - ep.x_1); + Fr y1_sqr = (ep.y_1 * ep.y_1); + { + // Move to top + Fr partialEval = domainSep; + + Fr y2_sqr = (ep.y_2 * ep.y_2); + Fr y1y2 = ep.y_1 * ep.y_2 * q_sign; + Fr x_add_identity = (ep.x_3 + ep.x_2 + ep.x_1); + x_add_identity = x_add_identity * x_diff * x_diff; + x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; + + evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + } + + // Contribution 11 point addition, x-coordinate check + // q_elliptic * (q_sign * y1 + y3)(x2 - x1) + (x3 - x1)(y2 - q_sign * y1) = 0 + { + Fr y1_plus_y3 = ep.y_1 + ep.y_3; + Fr y_diff = ep.y_2 * q_sign - ep.y_1; + Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; + evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + } + + // Contribution 10 point doubling, x-coordinate check + // (x3 + x1 + x1) (4y1*y1) - 9 * x1 * x1 * x1 * x1 = 0 + // N.B. we're using the equivalence x1*x1*x1 === y1*y1 - curve_b to reduce degree by 1 + { + Fr x_pow_4 = (y1_sqr + GRUMPKIN_CURVE_B_PARAMETER_NEGATED) * ep.x_1; + Fr y1_sqr_mul_4 = y1_sqr + y1_sqr; + y1_sqr_mul_4 = y1_sqr_mul_4 + y1_sqr_mul_4; + Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); + + // NOTE: pushed into memory (stack >:'( ) + ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + evals[11] = evals[11] + acc; + } + + // Contribution 11 point doubling, y-coordinate check + // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 + { + Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; + Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); + evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + } + } + + // Parameters used within the Memory Relation + // A struct is used to work around stack too deep. This relation has alot of variables + struct MemParams { + Fr memory_record_check; + Fr partial_record_check; + Fr next_gate_access_type; + Fr record_delta; + Fr index_delta; + Fr adjacent_values_match_if_adjacent_indices_match; + Fr adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation; + Fr access_check; + Fr next_gate_access_type_is_boolean; + Fr ROM_consistency_check_identity; + Fr RAM_consistency_check_identity; + Fr timestamp_delta; + Fr RAM_timestamp_check_identity; + Fr memory_identity; + Fr index_is_monotonically_increasing; + } + + function accumulateMemoryRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + MemParams memory ap; + + /** + * MEMORY + * + * A RAM memory record contains a tuple of the following fields: + * * i: `index` of memory cell being accessed + * * t: `timestamp` of memory cell being accessed (used for RAM, set to 0 for ROM) + * * v: `value` of memory cell being accessed + * * a: `access` type of record. read: 0 = read, 1 = write + * * r: `record` of memory cell. record = access + index * eta + timestamp * eta_two + value * eta_three + * + * A ROM memory record contains a tuple of the following fields: + * * i: `index` of memory cell being accessed + * * v: `value1` of memory cell being accessed (ROM tables can store up to 2 values per index) + * * v2:`value2` of memory cell being accessed (ROM tables can store up to 2 values per index) + * * r: `record` of memory cell. record = index * eta + value2 * eta_two + value1 * eta_three + * + * When performing a read/write access, the values of i, t, v, v2, a, r are stored in the following wires + + * selectors, depending on whether the gate is a RAM read/write or a ROM read + * + * | gate type | i | v2/t | v | a | r | + * | --------- | -- | ----- | -- | -- | -- | + * | ROM | w1 | w2 | w3 | -- | w4 | + * | RAM | w1 | w2 | w3 | qc | w4 | + * + * (for accesses where `index` is a circuit constant, it is assumed the circuit will apply a copy constraint on + * `w2` to fix its value) + * + * + */ + + /** + * Memory Record Check + * Partial degree: 1 + * Total degree: 4 + * + * A ROM/ROM access gate can be evaluated with the identity: + * + * qc + w1 \eta + w2 \eta_two + w3 \eta_three - w4 = 0 + * + * For ROM gates, qc = 0 + */ + ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); + ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 + ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); + + /** + * Contribution 13 & 14 + * ROM Consistency Check + * Partial degree: 1 + * Total degree: 4 + * + * For every ROM read, a set equivalence check is applied between the record witnesses, and a second set of + * records that are sorted. + * + * We apply the following checks for the sorted records: + * + * 1. w1, w2, w3 correctly map to 'index', 'v1, 'v2' for a given record value at w4 + * 2. index values for adjacent records are monotonically increasing + * 3. if, at gate i, index_i == index_{i + 1}, then value1_i == value1_{i + 1} and value2_i == value2_{i + 1} + * + */ + ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); + ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); + + ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 + + ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 + + evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + + ap.ROM_consistency_check_identity = ap.memory_record_check * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)); // deg 3 or 7 + + /** + * Contributions 15,16,17 + * RAM Consistency Check + * + * The 'access' type of the record is extracted with the expression `w_4 - ap.partial_record_check` + * (i.e. for an honest Prover `w1 * eta + w2 * eta^2 + w3 * eta^3 - w4 = access`. + * This is validated by requiring `access` to be boolean + * + * For two adjacent entries in the sorted list if _both_ + * A) index values match + * B) adjacent access value is 0 (i.e. next gate is a READ) + * then + * C) both values must match. + * The gate boolean check is + * (A && B) => C === !(A && B) || C === !A || !B || C + * + * N.B. it is the responsibility of the circuit writer to ensure that every RAM cell is initialized + * with a WRITE operation. + */ + Fr access_type = (wire(p, WIRE.W_4) - ap.partial_record_check); // will be 0 or 1 for honest Prover; deg 1 or 4 + ap.access_check = access_type * (access_type - Fr.wrap(1)); // check value is 0 or 1; deg 2 or 8 + + // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta + // deg 1 or 4 + ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; + + Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); + ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + + // We can't apply the RAM consistency check identity on the final entry in the sorted list (the wires in the + // next gate would make the identity fail). We need to validate that its 'access type' bool is correct. Can't + // do with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access + // type is correct, to cover this edge case + // deg 2 or 4 + ap.next_gate_access_type_is_boolean = + ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; + + // Putting it all together... + evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation + * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 + + /** + * RAM Timestamp Consistency Check + * + * | w1 | w2 | w3 | w4 | + * | index | timestamp | timestamp_check | -- | + * + * Let delta_index = index_{i + 1} - index_{i} + * + * Iff delta_index == 0, timestamp_check = timestamp_{i + 1} - timestamp_i + * Else timestamp_check = 0 + */ + ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); + ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 + + /** + * Complete Contribution 12 + * The complete RAM/ROM memory identity + * Partial degree: + */ + ap.memory_identity = ap.ROM_consistency_check_identity; // deg 3 or 6 + ap.memory_identity = + ap.memory_identity + ap.RAM_timestamp_check_identity * (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9 + + // (deg 3 or 9) + (deg 4) + (deg 3) + ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + evals[13] = ap.memory_identity; + } + + // Constants for the Non-native Field relation + Fr constant LIMB_SIZE = Fr.wrap(uint256(1) << 68); + Fr constant SUBLIMB_SHIFT = Fr.wrap(uint256(1) << 14); + + // Parameters used within the Non-Native Field Relation + // A struct is used to work around stack too deep. This relation has alot of variables + struct NnfParams { + Fr limb_subproduct; + Fr non_native_field_gate_1; + Fr non_native_field_gate_2; + Fr non_native_field_gate_3; + Fr limb_accumulator_1; + Fr limb_accumulator_2; + Fr nnf_identity; + } + + function accumulateNnfRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + NnfParams memory ap; + + /** + * Contribution 12 + * Non native field arithmetic gate 2 + * deg 4 + * + * _ _ + * / _ _ _ 14 \ + * q_2 . q_4 | (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2 - w_3 - w_4 | + * \_ _/ + * + * + */ + ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); + ap.non_native_field_gate_2 = + (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); + + ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; + ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.non_native_field_gate_1 = ap.limb_subproduct; + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); + + ap.non_native_field_gate_3 = ap.limb_subproduct; + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); + + Fr non_native_field_identity = + ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; + non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); + + // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm + // deg 2 + ap.limb_accumulator_1 = wire(p, WIRE.W_R_SHIFT) * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L_SHIFT); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_O); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_R); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L); + ap.limb_accumulator_1 = ap.limb_accumulator_1 - wire(p, WIRE.W_4); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * wire(p, WIRE.Q_4); + + // ((((w3' * 2^14 + w2') * 2^14 + w1') * 2^14 + w4) * 2^14 + w3 - w4') * qm + // deg 2 + ap.limb_accumulator_2 = wire(p, WIRE.W_O_SHIFT) * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_R_SHIFT); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_L_SHIFT); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_4); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_O); + ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); + + Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; + limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 + + ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; + ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); + evals[19] = ap.nnf_identity; + } + + struct PoseidonExternalParams { + Fr s1; + Fr s2; + Fr s3; + Fr s4; + Fr u1; + Fr u2; + Fr u3; + Fr u4; + Fr t0; + Fr t1; + Fr t2; + Fr t3; + Fr v1; + Fr v2; + Fr v3; + Fr v4; + Fr q_pos_by_scaling; + } + + function accumulatePoseidonExternalRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + PoseidonExternalParams memory ep; + + ep.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L); + ep.s2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_R); + ep.s3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_O); + ep.s4 = wire(p, WIRE.W_4) + wire(p, WIRE.Q_4); + + ep.u1 = ep.s1 * ep.s1 * ep.s1 * ep.s1 * ep.s1; + ep.u2 = ep.s2 * ep.s2 * ep.s2 * ep.s2 * ep.s2; + ep.u3 = ep.s3 * ep.s3 * ep.s3 * ep.s3 * ep.s3; + ep.u4 = ep.s4 * ep.s4 * ep.s4 * ep.s4 * ep.s4; + // matrix mul v = M_E * u with 14 additions + ep.t0 = ep.u1 + ep.u2; // u_1 + u_2 + ep.t1 = ep.u3 + ep.u4; // u_3 + u_4 + ep.t2 = ep.u2 + ep.u2 + ep.t1; // 2u_2 + // ep.t2 += ep.t1; // 2u_2 + u_3 + u_4 + ep.t3 = ep.u4 + ep.u4 + ep.t0; // 2u_4 + // ep.t3 += ep.t0; // u_1 + u_2 + 2u_4 + ep.v4 = ep.t1 + ep.t1; + ep.v4 = ep.v4 + ep.v4 + ep.t3; + // ep.v4 += ep.t3; // u_1 + u_2 + 4u_3 + 6u_4 + ep.v2 = ep.t0 + ep.t0; + ep.v2 = ep.v2 + ep.v2 + ep.t2; + // ep.v2 += ep.t2; // 4u_1 + 6u_2 + u_3 + u_4 + ep.v1 = ep.t3 + ep.v2; // 5u_1 + 7u_2 + u_3 + 3u_4 + ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 + + ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; + evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + + evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + + evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + + evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + } + + struct PoseidonInternalParams { + Fr u1; + Fr u2; + Fr u3; + Fr u4; + Fr u_sum; + Fr v1; + Fr v2; + Fr v3; + Fr v4; + Fr s1; + Fr q_pos_by_scaling; + } + + function accumulatePoseidonInternalRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + PoseidonInternalParams memory ip; + + Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ + FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), + FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), + FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), + FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) + ]; + + // add round constants + ip.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L); + + // apply s-box round + ip.u1 = ip.s1 * ip.s1 * ip.s1 * ip.s1 * ip.s1; + ip.u2 = wire(p, WIRE.W_R); + ip.u3 = wire(p, WIRE.W_O); + ip.u4 = wire(p, WIRE.W_4); + + // matrix mul with v = M_I * u 4 muls and 7 additions + ip.u_sum = ip.u1 + ip.u2 + ip.u3 + ip.u4; + + ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; + + ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; + evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + + ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; + evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + + ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; + evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + + ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; + evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + } + + // Batch subrelation evaluations using precomputed powers of alpha + // First subrelation is implicitly scaled by 1, subsequent ones use powers from the subrelationChallenges array + function scaleAndBatchSubrelations( + Fr[NUMBER_OF_SUBRELATIONS] memory evaluations, + Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges + ) internal pure returns (Fr accumulator) { + accumulator = evaluations[0]; + + for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { + accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; + } + } +} + +// Field arithmetic libraries - prevent littering the code with modmul / addmul + +library CommitmentSchemeLib { + using FrLib for Fr; + + // Avoid stack too deep + struct ShpleminiIntermediates { + Fr unshiftedScalar; + Fr shiftedScalar; + Fr unshiftedScalarNeg; + Fr shiftedScalarNeg; + // Scalar to be multiplied by [1]₁ + Fr constantTermAccumulator; + // Accumulator for powers of rho + Fr batchingChallenge; + // Linear combination of multilinear (sumcheck) evaluations and powers of rho + Fr batchedEvaluation; + Fr[4] denominators; + Fr[4] batchingScalars; + // 1/(z - r^{2^i}) for i = 0, ..., logSize, dynamically updated + Fr posInvertedDenominator; + // 1/(z + r^{2^i}) for i = 0, ..., logSize, dynamically updated + Fr negInvertedDenominator; + // ν^{2i} * 1/(z - r^{2^i}) + Fr scalingFactorPos; + // ν^{2i+1} * 1/(z + r^{2^i}) + Fr scalingFactorNeg; + // Fold_i(r^{2^i}) reconstructed by Verifier + Fr[] foldPosEvaluations; + } + + function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { + Fr[] memory squares = new Fr[](logN); + squares[0] = r; + for (uint256 i = 1; i < logN; ++i) { + squares[i] = squares[i - 1].sqr(); + } + return squares; + } + // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., m-1 + + function computeFoldPosEvaluations( + Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckUChallenges, + Fr batchedEvalAccumulator, + Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvaluations, + Fr[] memory geminiEvalChallengePowers, + uint256 logSize + ) internal view returns (Fr[] memory) { + Fr[] memory foldPosEvaluations = new Fr[](logSize); + for (uint256 i = logSize; i > 0; --i) { + Fr challengePower = geminiEvalChallengePowers[i - 1]; + Fr u = sumcheckUChallenges[i - 1]; + + Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] + * (challengePower * (ONE - u) - u)); + // Divide by the denominator + batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); + + batchedEvalAccumulator = batchedEvalRoundAcc; + foldPosEvaluations[i - 1] = batchedEvalRoundAcc; + } + return foldPosEvaluations; + } +} + +uint256 constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; // EC group order. F_q + +function bytes32ToString(bytes32 value) pure returns (string memory result) { + bytes memory alphabet = "0123456789abcdef"; + + bytes memory str = new bytes(66); + str[0] = "0"; + str[1] = "x"; + for (uint256 i = 0; i < 32; i++) { + str[2 + i * 2] = alphabet[uint8(value[i] >> 4)]; + str[3 + i * 2] = alphabet[uint8(value[i] & 0x0f)]; + } + result = string(str); +} + +// Fr utility + +function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { + scalar = FrLib.fromBytes32(bytes32(proofSection)); +} + +// EC Point utilities +function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { + point = Honk.G1Point({ + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q + }); +} + +function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { + point.y = (Q - point.y) % Q; + return point; +} + +/** + * Convert the pairing points to G1 points. + * + * The pairing points are serialised as an array of 68 bit limbs representing two points + * The lhs of a pairing operation and the rhs of a pairing operation + * + * There are 4 fields for each group element, leaving 8 fields for each side of the pairing. + * + * @param pairingPoints The pairing points to convert. + * @return lhs + * @return rhs + */ +function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) + pure + returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) +{ + uint256 lhsX = Fr.unwrap(pairingPoints[0]); + lhsX |= Fr.unwrap(pairingPoints[1]) << 68; + lhsX |= Fr.unwrap(pairingPoints[2]) << 136; + lhsX |= Fr.unwrap(pairingPoints[3]) << 204; + lhs.x = lhsX; + + uint256 lhsY = Fr.unwrap(pairingPoints[4]); + lhsY |= Fr.unwrap(pairingPoints[5]) << 68; + lhsY |= Fr.unwrap(pairingPoints[6]) << 136; + lhsY |= Fr.unwrap(pairingPoints[7]) << 204; + lhs.y = lhsY; + + uint256 rhsX = Fr.unwrap(pairingPoints[8]); + rhsX |= Fr.unwrap(pairingPoints[9]) << 68; + rhsX |= Fr.unwrap(pairingPoints[10]) << 136; + rhsX |= Fr.unwrap(pairingPoints[11]) << 204; + rhs.x = rhsX; + + uint256 rhsY = Fr.unwrap(pairingPoints[12]); + rhsY |= Fr.unwrap(pairingPoints[13]) << 68; + rhsY |= Fr.unwrap(pairingPoints[14]) << 136; + rhsY |= Fr.unwrap(pairingPoints[15]) << 204; + rhs.y = rhsY; +} + +/** + * Hash the pairing inputs from the present verification context with those extracted from the public inputs. + * + * @param proofPairingPoints Pairing points from the proof - (public inputs). + * @param accLhs Accumulator point for the left side - result of shplemini. + * @param accRhs Accumulator point for the right side - result of shplemini. + * @return recursionSeparator The recursion separator - generated from hashing the above. + */ +function generateRecursionSeparator( + Fr[PAIRING_POINTS_SIZE] memory proofPairingPoints, + Honk.G1Point memory accLhs, + Honk.G1Point memory accRhs +) pure returns (Fr recursionSeparator) { + // hash the proof aggregated X + // hash the proof aggregated Y + // hash the accum X + // hash the accum Y + + (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); + + uint256[8] memory recursionSeparatorElements; + + // Proof points + recursionSeparatorElements[0] = proofLhs.x; + recursionSeparatorElements[1] = proofLhs.y; + recursionSeparatorElements[2] = proofRhs.x; + recursionSeparatorElements[3] = proofRhs.y; + + // Accumulator points + recursionSeparatorElements[4] = accLhs.x; + recursionSeparatorElements[5] = accLhs.y; + recursionSeparatorElements[6] = accRhs.x; + recursionSeparatorElements[7] = accRhs.y; + + recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); +} + +/** + * G1 Mul with Separator + * Using the ecAdd and ecMul precompiles + * + * @param basePoint The point to multiply. + * @param other The other point to add. + * @param recursionSeperator The separator to use for the multiplication. + * @return `(recursionSeperator * basePoint) + other`. + */ +function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) + view + returns (Honk.G1Point memory) +{ + Honk.G1Point memory result; + + result = ecMul(recursionSeperator, basePoint); + result = ecAdd(result, other); + + return result; +} + +/** + * G1 Mul + * Takes a Fr value and a G1 point and uses the ecMul precompile to return the result. + * + * @param value The value to multiply the point by. + * @param point The point to multiply. + * @return result The result of the multiplication. + */ +function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { + Honk.G1Point memory result; + + assembly { + let free := mload(0x40) + // Write the point into memory (two 32 byte words) + // Memory layout: + // Address | value + // free | point.x + // free + 0x20| point.y + mstore(free, mload(point)) + mstore(add(free, 0x20), mload(add(point, 0x20))) + // Write the scalar into memory (one 32 byte word) + // Memory layout: + // Address | value + // free + 0x40| value + mstore(add(free, 0x40), value) + + // Call the ecMul precompile, it takes in the following + // [point.x, point.y, scalar], and returns the result back into the free memory location. + let success := staticcall(gas(), 0x07, free, 0x60, free, 0x40) + if iszero(success) { + revert(0, 0) + } + // Copy the result of the multiplication back into the result memory location. + // Memory layout: + // Address | value + // result | result.x + // result + 0x20| result.y + mstore(result, mload(free)) + mstore(add(result, 0x20), mload(add(free, 0x20))) + + mstore(0x40, add(free, 0x60)) + } + + return result; +} + +/** + * G1 Add + * Takes two G1 points and uses the ecAdd precompile to return the result. + * + * @param lhs The left hand side of the addition. + * @param rhs The right hand side of the addition. + * @return result The result of the addition. + */ +function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { + Honk.G1Point memory result; + + assembly { + let free := mload(0x40) + // Write lhs into memory (two 32 byte words) + // Memory layout: + // Address | value + // free | lhs.x + // free + 0x20| lhs.y + mstore(free, mload(lhs)) + mstore(add(free, 0x20), mload(add(lhs, 0x20))) + + // Write rhs into memory (two 32 byte words) + // Memory layout: + // Address | value + // free + 0x40| rhs.x + // free + 0x60| rhs.y + mstore(add(free, 0x40), mload(rhs)) + mstore(add(free, 0x60), mload(add(rhs, 0x20))) + + // Call the ecAdd precompile, it takes in the following + // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. + let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) + if iszero(success) { revert(0, 0) } + + // Copy the result of the addition back into the result memory location. + // Memory layout: + // Address | value + // result | result.x + // result + 0x20| result.y + mstore(result, mload(free)) + mstore(add(result, 0x20), mload(add(free, 0x20))) + + mstore(0x40, add(free, 0x80)) + } + + return result; +} + +function validateOnCurve(Honk.G1Point memory point) pure { + uint256 x = point.x; + uint256 y = point.y; + + bool success = false; + assembly { + let xx := mulmod(x, x, Q) + success := eq(mulmod(y, y, Q), addmod(mulmod(x, xx, Q), 3, Q)) + } + + require(success, "point is not on the curve"); +} + +function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { + bytes memory input = abi.encodePacked( + rhs.x, + rhs.y, + // Fixed G2 point + uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), + uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), + uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), + uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), + lhs.x, + lhs.y, + // G2 point from VK + uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), + uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), + uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), + uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) + ); + + (bool success, bytes memory result) = address(0x08).staticcall(input); + decodedResult = success && abi.decode(result, (bool)); +} + +// Field arithmetic libraries - prevent littering the code with modmul / addmul + + + + +abstract contract BaseZKHonkVerifier is IVerifier { + using FrLib for Fr; + + uint256 immutable $N; + uint256 immutable $LOG_N; + uint256 immutable $VK_HASH; + uint256 immutable $NUM_PUBLIC_INPUTS; + uint256 immutable $MSMSize; + + constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { + $N = _N; + $LOG_N = _logN; + $VK_HASH = _vkHash; + $NUM_PUBLIC_INPUTS = _numPublicInputs; + $MSMSize = NUMBER_UNSHIFTED_ZK + _logN + LIBRA_COMMITMENTS + 2; + } + + // Errors + error ProofLengthWrong(); + error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); + error PublicInputsLengthWrong(); + error SumcheckFailed(); + error ShpleminiFailed(); + error GeminiChallengeInSubgroup(); + error ConsistencyCheckFailed(); + + // Constants for proof length calculation (matching UltraKeccakZKFlavor) + uint256 constant NUM_WITNESS_ENTITIES = 8 + NUM_MASKING_POLYNOMIALS; + uint256 constant NUM_ELEMENTS_COMM = 2; // uint256 elements for curve points + uint256 constant NUM_ELEMENTS_FR = 1; // uint256 elements for field elements + uint256 constant NUM_LIBRA_EVALUATIONS = 4; // libra evaluations + + // Calculate proof size based on log_n (matching UltraKeccakZKFlavor formula) + function calculateProofSize(uint256 logN) internal pure returns (uint256) { + // Witness and Libra commitments + uint256 proofLength = NUM_WITNESS_ENTITIES * NUM_ELEMENTS_COMM; // witness commitments + proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking + + // Sumcheck + proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations + + // Libra and Gemini + proofLength += NUM_ELEMENTS_FR * 2; // Libra sum, claimed eval + proofLength += logN * NUM_ELEMENTS_FR; // Gemini a evaluations + proofLength += NUM_LIBRA_EVALUATIONS * NUM_ELEMENTS_FR; // libra evaluations + + // PCS commitments + proofLength += (logN - 1) * NUM_ELEMENTS_COMM; // Gemini Fold commitments + proofLength += NUM_ELEMENTS_COMM * 2; // Shplonk Q and KZG W commitments + + // Pairing points + proofLength += PAIRING_POINTS_SIZE; // pairing inputs carried on public inputs + + return proofLength; + } + + uint256 constant SHIFTED_COMMITMENTS_START = 30; + + function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); + + function verify(bytes calldata proof, bytes32[] calldata publicInputs) + public + view + override + returns (bool verified) + { + // Calculate expected proof size based on $LOG_N + uint256 expectedProofSize = calculateProofSize($LOG_N); + + // Check the received proof is the expected size where each field element is 32 bytes + if (proof.length != expectedProofSize * 32) { + revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); + } + + Honk.VerificationKey memory vk = loadVerificationKey(); + Honk.ZKProof memory p = ZKTranscriptLib.loadProof(proof, $LOG_N); + + if (publicInputs.length != vk.publicInputsSize - PAIRING_POINTS_SIZE) { + revert PublicInputsLengthWrong(); + } + + // Generate the fiat shamir challenges for the whole protocol + ZKTranscript memory t = + ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); + + // Derive public input delta + t.relationParameters.publicInputsDelta = computePublicInputDelta( + publicInputs, + p.pairingPointObject, + t.relationParameters.beta, + t.relationParameters.gamma, /*pubInputsOffset=*/ + 1 + ); + + // Sumcheck + if (!verifySumcheck(p, t)) revert SumcheckFailed(); + + if (!verifyShplemini(p, vk, t)) revert ShpleminiFailed(); + + verified = true; + } + + uint256 constant PERMUTATION_ARGUMENT_VALUE_SEPARATOR = 1 << 28; + + function computePublicInputDelta( + bytes32[] memory publicInputs, + Fr[PAIRING_POINTS_SIZE] memory pairingPointObject, + Fr beta, + Fr gamma, + uint256 offset + ) internal view returns (Fr publicInputDelta) { + Fr numerator = Fr.wrap(1); + Fr denominator = Fr.wrap(1); + + Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); + + { + for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { + Fr pubInput = FrLib.fromBytes32(publicInputs[i]); + + numerator = numerator * (numeratorAcc + pubInput); + denominator = denominator * (denominatorAcc + pubInput); + + numeratorAcc = numeratorAcc + beta; + denominatorAcc = denominatorAcc - beta; + } + + for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { + Fr pubInput = pairingPointObject[i]; + + numerator = numerator * (numeratorAcc + pubInput); + denominator = denominator * (denominatorAcc + pubInput); + + numeratorAcc = numeratorAcc + beta; + denominatorAcc = denominatorAcc - beta; + } + } + + // Fr delta = numerator / denominator; // TOOO: batch invert later? + publicInputDelta = FrLib.div(numerator, denominator); + } + + function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { + Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 + Fr powPartialEvaluation = Fr.wrap(1); + + // We perform sumcheck reductions over log n rounds ( the multivariate degree ) + for (uint256 round; round < $LOG_N; ++round) { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; + if (totalSum != roundTargetSum) revert SumcheckFailed(); + + Fr roundChallenge = tp.sumCheckUChallenges[round]; + + // Update the round target for the next rounf + roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); + powPartialEvaluation = + powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); + } + + // Last round + // For ZK flavors: sumcheckEvaluations has 42 elements + // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations + Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; + for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { + relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 + } + Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( + relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation + ); + + Fr evaluation = Fr.wrap(1); + for (uint256 i = 2; i < $LOG_N; i++) { + evaluation = evaluation * tp.sumCheckUChallenges[i]; + } + + grandHonkRelationSum = + grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; + verified = (grandHonkRelationSum == roundTargetSum); + } + + // Return the new target sum for the next sumcheck round + function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) + internal + view + returns (Fr targetSum) + { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) + ]; + + // To compute the next target sum, we evaluate the given univariate at a point u (challenge). + + // Performing Barycentric evaluations + // Compute B(x) + Fr numeratorValue = Fr.wrap(1); + for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { + numeratorValue = numeratorValue * (roundChallenge - Fr.wrap(i)); + } + + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; + for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { + denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); + } + + for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { + targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; + } + + // Scale the sum by the value of B(x) + targetSum = targetSum * numeratorValue; + } + + uint256 constant LIBRA_COMMITMENTS = 3; + uint256 constant LIBRA_EVALUATIONS = 4; + uint256 constant LIBRA_UNIVARIATES_LENGTH = 9; + + struct PairingInputs { + Honk.G1Point P_0; + Honk.G1Point P_1; + } + + function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) + internal + view + returns (bool verified) + { + CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack + + // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); + // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings + Fr[] memory scalars = new Fr[]($MSMSize); + Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); + + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); + + mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); + mem.shiftedScalar = + tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); + + scalars[0] = Fr.wrap(1); + commitments[0] = proof.shplonkQ; + + /* Batch multivariate opening claims, shifted and unshifted + * The vector of scalars is populated as follows: + * \f[ + * \left( + * - \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right), + * \ldots, + * - \rho^{i+k-1} \times \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right), + * - \rho^{i+k} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right), + * \ldots, + * - \rho^{k+m-1} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right) + * \right) + * \f] + * + * The following vector is concatenated to the vector of commitments: + * \f[ + * f_0, \ldots, f_{m-1}, f_{\text{shift}, 0}, \ldots, f_{\text{shift}, k-1} + * \f] + * + * Simultaneously, the evaluation of the multilinear polynomial + * \f[ + * \sum \rho^i \cdot f_i + \sum \rho^{i+k} \cdot f_{\text{shift}, i} + * \f] + * at the challenge point \f$ (u_0,\ldots, u_{n-1}) \f$ is computed. + * + * This approach minimizes the number of iterations over the commitments to multilinear polynomials + * and eliminates the need to store the powers of \f$ \rho \f$. + */ + // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] + // Start batching challenge at 1, not rho, to match non-ZK pattern + mem.batchingChallenge = Fr.wrap(1); + mem.batchedEvaluation = Fr.wrap(0); + + mem.unshiftedScalarNeg = mem.unshiftedScalar.neg(); + mem.shiftedScalarNeg = mem.shiftedScalar.neg(); + + // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) + for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { + scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; + mem.batchedEvaluation = mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); + mem.batchingChallenge = mem.batchingChallenge * tp.rho; + } + // g commitments are accumulated at r + // For each of the to be shifted commitments perform the shift in place by + // adding to the unshifted value. + // We do so, as the values are to be used in batchMul later, and as + // `a * c + b * c = (a + b) * c` this will allow us to reduce memory and compute. + // Applied to w1, w2, w3, w4 and zPerm + for (uint256 i = 0; i < NUMBER_TO_BE_SHIFTED; ++i) { + uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; + uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; + + scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); + mem.batchedEvaluation = + mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); + mem.batchingChallenge = mem.batchingChallenge * tp.rho; + } + + commitments[1] = proof.geminiMaskingPoly; + + commitments[2] = vk.qm; + commitments[3] = vk.qc; + commitments[4] = vk.ql; + commitments[5] = vk.qr; + commitments[6] = vk.qo; + commitments[7] = vk.q4; + commitments[8] = vk.qLookup; + commitments[9] = vk.qArith; + commitments[10] = vk.qDeltaRange; + commitments[11] = vk.qElliptic; + commitments[12] = vk.qMemory; + commitments[13] = vk.qNnf; + commitments[14] = vk.qPoseidon2External; + commitments[15] = vk.qPoseidon2Internal; + commitments[16] = vk.s1; + commitments[17] = vk.s2; + commitments[18] = vk.s3; + commitments[19] = vk.s4; + commitments[20] = vk.id1; + commitments[21] = vk.id2; + commitments[22] = vk.id3; + commitments[23] = vk.id4; + commitments[24] = vk.t1; + commitments[25] = vk.t2; + commitments[26] = vk.t3; + commitments[27] = vk.t4; + commitments[28] = vk.lagrangeFirst; + commitments[29] = vk.lagrangeLast; + + // Accumulate proof points + commitments[30] = proof.w1; + commitments[31] = proof.w2; + commitments[32] = proof.w3; + commitments[33] = proof.w4; + commitments[34] = proof.zPerm; + commitments[35] = proof.lookupInverses; + commitments[36] = proof.lookupReadCounts; + commitments[37] = proof.lookupReadTags; + + /* Batch gemini claims from the prover + * place the commitments to gemini aᵢ to the vector of commitments, compute the contributions from + * aᵢ(−r²ⁱ) for i=1, … , n−1 to the constant term accumulator, add corresponding scalars + * + * 1. Moves the vector + * \f[ + * \left( \text{com}(A_1), \text{com}(A_2), \ldots, \text{com}(A_{n-1}) \right) + * \f] + * to the 'commitments' vector. + * + * 2. Computes the scalars: + * \f[ + * \frac{\nu^{2}}{z + r^2}, \frac{\nu^3}{z + r^4}, \ldots, \frac{\nu^{n-1}}{z + r^{2^{n-1}}} + * \f] + * and places them into the 'scalars' vector. + * + * 3. Accumulates the summands of the constant term: + * \f[ + * \sum_{i=2}^{n-1} \frac{\nu^{i} \cdot A_i(-r^{2^i})}{z + r^{2^i}} + * \f] + * and adds them to the 'constant_term_accumulator'. + */ + + // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: + // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 + Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); + + mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; + mem.constantTermAccumulator = + mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); + + mem.batchingChallenge = tp.shplonkNu.sqr(); + uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; + + // Compute Shplonk constant term contributions from Aₗ(± r^{2ˡ}) for l = 1, ..., m-1; + // Compute scalar multipliers for each fold commitment + for (uint256 i = 0; i < $LOG_N - 1; ++i) { + bool dummy_round = i >= ($LOG_N - 1); + + if (!dummy_round) { + // Update inverted denominators + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); + + // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] + mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; + mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; + scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); + + // Accumulate the const term contribution given by + // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) + Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; + accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; + } + // Update the running power of v + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + + commitments[boundary + i] = proof.geminiFoldComms[i]; + } + + boundary += $LOG_N - 1; + + // Finalize the batch opening claim + mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); + mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); + mem.denominators[2] = mem.denominators[0]; + mem.denominators[3] = mem.denominators[0]; + + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { + Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; + mem.batchingScalars[i] = scalingFactor.neg(); + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; + mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; + } + scalars[boundary] = mem.batchingScalars[0]; + scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; + scalars[boundary + 2] = mem.batchingScalars[3]; + + for (uint256 i = 0; i < LIBRA_COMMITMENTS; i++) { + commitments[boundary++] = proof.libraCommitments[i]; + } + + commitments[boundary] = Honk.G1Point({x: 1, y: 2}); + scalars[boundary++] = mem.constantTermAccumulator; + + if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { + revert ConsistencyCheckFailed(); + } + + Honk.G1Point memory quotient_commitment = proof.kzgQuotient; + + commitments[boundary] = quotient_commitment; + scalars[boundary] = tp.shplonkZ; // evaluation challenge + + PairingInputs memory pair; + pair.P_0 = batchMul(commitments, scalars); + pair.P_1 = negateInplace(quotient_commitment); + + // Aggregate pairing points + Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); + (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = + convertPairingPointsToG1(proof.pairingPointObject); + + // Validate the points from the proof are on the curve + validateOnCurve(P_0_other); + validateOnCurve(P_1_other); + + // accumulate with aggregate points in proof + pair.P_0 = mulWithSeperator(pair.P_0, P_0_other, recursionSeparator); + pair.P_1 = mulWithSeperator(pair.P_1, P_1_other, recursionSeparator); + + return pairing(pair.P_0, pair.P_1); + } + + struct SmallSubgroupIpaIntermediates { + Fr[SUBGROUP_SIZE] challengePolyLagrange; + Fr challengePolyEval; + Fr lagrangeFirst; + Fr lagrangeLast; + Fr rootPower; + Fr[SUBGROUP_SIZE] denominators; // this has to disappear + Fr diff; + } + + function checkEvalsConsistency( + Fr[LIBRA_EVALUATIONS] memory libraPolyEvals, + Fr geminiR, + Fr[CONST_PROOF_SIZE_LOG_N] memory uChallenges, + Fr libraEval + ) internal view returns (bool check) { + Fr one = Fr.wrap(1); + Fr vanishingPolyEval = geminiR.pow(SUBGROUP_SIZE) - one; + if (vanishingPolyEval == Fr.wrap(0)) { + revert GeminiChallengeInSubgroup(); + } + + SmallSubgroupIpaIntermediates memory mem; + mem.challengePolyLagrange[0] = one; + for (uint256 round = 0; round < $LOG_N; round++) { + uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; + mem.challengePolyLagrange[currIdx] = one; + for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { + mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; + } + } + + mem.rootPower = one; + mem.challengePolyEval = Fr.wrap(0); + for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { + mem.denominators[idx] = mem.rootPower * geminiR - one; + mem.denominators[idx] = mem.denominators[idx].invert(); + mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; + mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; + } + + Fr numerator = vanishingPolyEval * Fr.wrap(SUBGROUP_SIZE).invert(); + mem.challengePolyEval = mem.challengePolyEval * numerator; + mem.lagrangeFirst = mem.denominators[0] * numerator; + mem.lagrangeLast = mem.denominators[SUBGROUP_SIZE - 1] * numerator; + + mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; + + mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) + * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); + mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; + + check = mem.diff == Fr.wrap(0); + } + + // This implementation is the same as above with different constants + function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) + internal + view + returns (Honk.G1Point memory result) + { + uint256 limit = $MSMSize; + + // Validate all points are on the curve + for (uint256 i = 0; i < limit; ++i) { + validateOnCurve(base[i]); + } + + bool success = true; + assembly { + let free := mload(0x40) + + let count := 0x01 + for {} lt(count, add(limit, 1)) { count := add(count, 1) } { + // Get loop offsets + let base_base := add(base, mul(count, 0x20)) + let scalar_base := add(scalars, mul(count, 0x20)) + + mstore(add(free, 0x40), mload(mload(base_base))) + mstore(add(free, 0x60), mload(add(0x20, mload(base_base)))) + // Add scalar + mstore(add(free, 0x80), mload(scalar_base)) + + success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) + } + + // Return the result + mstore(result, mload(free)) + mstore(add(result, 0x20), mload(add(free, 0x20))) + } + + require(success, ShpleminiFailed()); + } +} + +contract ThresholdDecryptedSharesAggregationBnVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { + function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { + return HonkVerificationKey.loadVerificationKey(); + } +} diff --git a/packages/enclave-contracts/contracts/verifier/DkgPkVerifier.sol b/packages/enclave-contracts/contracts/verifier/ThresholdDecryptedSharesAggregationModVerifier.sol similarity index 60% rename from packages/enclave-contracts/contracts/verifier/DkgPkVerifier.sol rename to packages/enclave-contracts/contracts/verifier/ThresholdDecryptedSharesAggregationModVerifier.sol index b10314e942..a2b3bc0533 100644 --- a/packages/enclave-contracts/contracts/verifier/DkgPkVerifier.sol +++ b/packages/enclave-contracts/contracts/verifier/ThresholdDecryptedSharesAggregationModVerifier.sol @@ -5,243 +5,127 @@ // or FITNESS FOR A PARTICULAR PURPOSE. pragma solidity >=0.8.21; -uint256 constant N = 8192; -uint256 constant LOG_N = 13; -uint256 constant NUMBER_OF_PUBLIC_INPUTS = 17; -uint256 constant VK_HASH = 0x2b9b4382ad1c1e2e1d085d25a74c59315a809667765be70770ddc185b70f780b; +uint256 constant N = 131072; +uint256 constant LOG_N = 17; +uint256 constant NUMBER_OF_PUBLIC_INPUTS = 579; +uint256 constant VK_HASH = 0x0772b20ab0892c5f70111dd5e3e9737a04e02129d3f48fbd7a1079f68d41c3de; library HonkVerificationKey { - function loadVerificationKey() - internal - pure - returns (Honk.VerificationKey memory) - { + function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { Honk.VerificationKey memory vk = Honk.VerificationKey({ - circuitSize: uint256(8192), - logCircuitSize: uint256(13), - publicInputsSize: uint256(17), - ql: Honk.G1Point({ - x: uint256( - 0x11e0f52b055371aea42a1b22947e7e9eee7e791ab9e947053f46e2782391370c - ), - y: uint256( - 0x0f3c94cb00fedd2902b259d6cdc6105e578a7b5ddf1f0c153fd061deec17d12a - ) + circuitSize: uint256(131072), + logCircuitSize: uint256(17), + publicInputsSize: uint256(579), + ql: Honk.G1Point({ + x: uint256(0x2d8eb0ef0769af9b60944c9c1f9aef88f4458599d8d53d5700e57c463d3d2481), + y: uint256(0x067d884aca949978f4410a65ef7addb04f9e8a13536c428fbe9d71beb988a321) }), - qr: Honk.G1Point({ - x: uint256( - 0x0a106232b6d74de2311faac4c886951df58d1e19b22f33523b239ab765e164bc - ), - y: uint256( - 0x07f5dfd7a9464341586ffe3f0d977ba991e3d5c988c7c0ea2b645b6c11dee2d9 - ) + qr: Honk.G1Point({ + x: uint256(0x04f478772f21c19f23074f73f4cafc722ade3b71fd3e674504df4d488e70844f), + y: uint256(0x256ede28a442453cee32d59a1ae23e10ad9765b8cd5aa917f613437e401d03df) }), - qo: Honk.G1Point({ - x: uint256( - 0x041ff6d6168cd52a5201246e3da52e0cb260a2a5fa567e7bff13c5e3c7449807 - ), - y: uint256( - 0x11b370611ff9c70c66e2da51cc8ffb647e7cf40c85762d7bfe64e2f18a6a0f4f - ) + qo: Honk.G1Point({ + x: uint256(0x296c0719e6414ccc762949a1eb297b40c46b886179cf2ba5d3660b218aa9a728), + y: uint256(0x219d51db4d209022eaa607152a2ddc23b94fb99be6ed4b2f158ed46b21faceb8) }), - q4: Honk.G1Point({ - x: uint256( - 0x12aa823df85fb765f0155977a70179b7aaf9aeac1155d778de46cb37f8faf2fa - ), - y: uint256( - 0x2c3c8f16c5811640a0c3c503fe4918cf16de1383fbb359a2c063ace62d0189ef - ) + q4: Honk.G1Point({ + x: uint256(0x0cf55eebb61a0e90c6f9fd68c4839012c6433b88fafff66701ab99c6975e6da3), + y: uint256(0x1f44b9a9447265fad2e8c326a3cfd26d434ed9eece6c2d5b5586f8232572d765) }), - qm: Honk.G1Point({ - x: uint256( - 0x027302a9f1897167f79b74a71dd1558e3a69cefa96c98e94c7c0f766bd1d2bcb - ), - y: uint256( - 0x2515389a7ef0546fe7c56260129132c12a8eef371c3f22786af7f8d6ab7c60e5 - ) + qm: Honk.G1Point({ + x: uint256(0x3046224394ac0664c5df0bbff473db8485b523993c3378b647785875fbda56e5), + y: uint256(0x075b8449cf262130cd0d53747c76da002a74053958060ee7c8e930b7f729af4e) }), - qc: Honk.G1Point({ - x: uint256( - 0x22462de1db5e1340e5bf6aec9c11bf7e7a6cc0c35f29f457684c6eec7cd5c5c6 - ), - y: uint256( - 0x01dfc98a0c382ff395ae0236bf97e801452533f3caa87c7dced9f68bdc1ed210 - ) + qc: Honk.G1Point({ + x: uint256(0x23f894967a8d0ad87a02976c74accf68659348a280e6c1ab3f99dad7b8cf4bc4), + y: uint256(0x21acfe5f2f788aee6ca1b87420abfb1b567605edbfc9de6cbff67aa99eb633b8) }), - qLookup: Honk.G1Point({ - x: uint256( - 0x0c4032c3079594eb75a8449d3d5ce8bc3661650d53f9b24d923d8f404cb0bbc9 - ), - y: uint256( - 0x1084d709650356d40f0158fd6da81f54eb5fe796a0ca89441369b7c24301f851 - ) + qLookup: Honk.G1Point({ + x: uint256(0x11beb2731ab8cf46c0a52666bb457fa74d4b635043bb5689a35a2aee24990b80), + y: uint256(0x2f1fba989036c44aedb609d0d3341ccb842c2d1cd7c80cea4a95d0eb45305770) }), - qArith: Honk.G1Point({ - x: uint256( - 0x0fb1ece2260000daef835e11c53b92f3816bbf66b6ebb43e364190027b77fe10 - ), - y: uint256( - 0x1893ea57a05c765b064019fd0abd1076edabf46d1e12c80d10a82cbeb3b59b34 - ) + qArith: Honk.G1Point({ + x: uint256(0x2d70068023998c277c75458b7b6eb1fe3c479eb044dec7aa3384d7b5049542c7), + y: uint256(0x10af4b4ffb3c03f9c906e4dd1cf3c21dc74139881dfa7ad73c25370424f39781) }), - qDeltaRange: Honk.G1Point({ - x: uint256( - 0x2f2778e46cce7a1f87dde2bde498d36f7683f5671f1022e182515e191582f4f1 - ), - y: uint256( - 0x0ddf6aec7c456c38be13fba3151ee3bae583c12438091bb70c403a22cfd8a8d7 - ) + qDeltaRange: Honk.G1Point({ + x: uint256(0x0d07902b3266cd4af955bac93e3579b0bc1bcd754c7b70035cc95877d641376c), + y: uint256(0x195d808eeb35965b8df7ea80461dcc033e5d8d0ef25b028a1d429e52b33f3f0d) }), - qElliptic: Honk.G1Point({ - x: uint256( - 0x0e5dcd1acfe7ef2e6b46edb70fc2067187a1865939739730b14b54bb73a8bb1d - ), - y: uint256( - 0x225366ddb76dc74766b564e123631cf98a813b7111f430502caa1070f42e8c89 - ) + qElliptic: Honk.G1Point({ + x: uint256(0x2656809e98f3dee7945c4f48c6fff3c0833412aeeae84dada472462112fa8fe9), + y: uint256(0x2454353d981df4ae6c3fd37c4a7b5114bcb1416f43efc627d55e128449c7c14d) }), - qMemory: Honk.G1Point({ - x: uint256( - 0x2f48eb759c28a68804dc0a9ca56b663e8a0d34e8cff4fca0dcf11d588a89738d - ), - y: uint256( - 0x12bdd73bf2a510e729182fa7a97453d2819406c6d74d9cbc32b18a701f930269 - ) + qMemory: Honk.G1Point({ + x: uint256(0x24ec1ac19b9f052e9e2c4270db9b317502859b163514073401512c6753188238), + y: uint256(0x28869fd3c9b2ad1ce8a966e9225bed7eac600c656be6891317404dc447267367) }), - qNnf: Honk.G1Point({ - x: uint256( - 0x08e14ddb419c267be8d5531e70141db8b637b9384198fe2da596908356b46a01 - ), - y: uint256( - 0x0bc9394fcfbb5cce0c123f14fea5469404e5b3c49766fa5484cd9bdcfddf2a49 - ) + qNnf: Honk.G1Point({ + x: uint256(0x201e0e19e72484d21e8136546fe6aaeea2d5b6413019276eada6638ffeb2ce8a), + y: uint256(0x00ba5238e406d8d46f68dc3b00a201ad10d2dc97beed32f5e83d051e742b2642) }), - qPoseidon2External: Honk.G1Point({ - x: uint256( - 0x0d17a9a4387495fb2b691b245aa6f13e25d7af5b468c2503fd6ee7ffdb819eb2 - ), - y: uint256( - 0x143b6e0733448ea59aeb2ef50e1b5e757a6720689ae9eb46ba1297d566da5651 - ) + qPoseidon2External: Honk.G1Point({ + x: uint256(0x1fb5e9c3ae41ba815444512a4e882619ac69f0cbb639657adeadb93ed50895b1), + y: uint256(0x201d42c30be1a478f6c6a28159a335c02fa3dc3e6c6843089c648bfd4dc8c250) }), - qPoseidon2Internal: Honk.G1Point({ - x: uint256( - 0x0ad0ed837fdb4a9a131b192a6184105210ce233ad85e80fc8f1ca7dfc0284330 - ), - y: uint256( - 0x029148c0965a4ffa8b6884fc10677316afd2cd7d794a41124d197b5900a65524 - ) + qPoseidon2Internal: Honk.G1Point({ + x: uint256(0x1375207e60c7d651a62aed70d2496e2324a7dc718de86e5887f805f907d49640), + y: uint256(0x22a964f903d1faa46e20c6f8902415d61985c86bbeef23a01b6ca1718dfe80c5) }), - s1: Honk.G1Point({ - x: uint256( - 0x07aa26298594e9aa3ed2e0cf1c372108f142acca0d94f0e3280ad8f6f7fea6f2 - ), - y: uint256( - 0x045e7e9f923fcf98cbc38bfec20a81bc03e34588928a034a3d6cb9e1f7e9c22f - ) + s1: Honk.G1Point({ + x: uint256(0x2a38162537d04323884347c1f31f76b03a04ec2f6f9c4b125d5d322932ff65ba), + y: uint256(0x2465da5aac067658f2f98944a8ba1bc9fd81c2220d22f07995746a7b7deccd29) }), - s2: Honk.G1Point({ - x: uint256( - 0x168740345edd76afa07da3ca2d75150bed7a67b12c47c528ee3ecdd7223ec929 - ), - y: uint256( - 0x1f211045a0fd5872f1c69ea0c8b2bf7a255743111fa34cf46cefd4900cd6f77a - ) + s2: Honk.G1Point({ + x: uint256(0x2a12695e6fa7a23c3519eee6d96433bb00d51cd274de90288914e3fb336334b6), + y: uint256(0x235e728947aff729c4ade5a63ca501273a958075ae1281bb612012f032196016) }), - s3: Honk.G1Point({ - x: uint256( - 0x2692234e86e35b65b0b2ca616655f2387f396f041d826cde54bd3133cfe00a10 - ), - y: uint256( - 0x26d963bc3e72700b141f37e9771cbf98e59674139d2ed5e581f6465c2edb85a0 - ) + s3: Honk.G1Point({ + x: uint256(0x09e5efa8f84174e74fe0429a76ec404c053a4f8c3e5524d68e3cd4e338a2b4b8), + y: uint256(0x057873d3cf1d8589310604f6f203386d9a9928d980432f5316522d84e6eecb61) }), - s4: Honk.G1Point({ - x: uint256( - 0x005cbcf3a063e8f7142c2a0c6499c584486f3c8c90453fb4d387e5fcd9fd2985 - ), - y: uint256( - 0x07de59eeee14a344c8b030a6a6d12650a6c37706700bb1bf55b5595ce910b799 - ) + s4: Honk.G1Point({ + x: uint256(0x096a6fb164d4a53c869c12d0369ef747bbbd3f40e5dd997c01e32495b64b07cb), + y: uint256(0x01198ac63b80d1d160ab5882da3dfc3a8864b70b72c31790e55fec5014c44362) }), - t1: Honk.G1Point({ - x: uint256( - 0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26 - ), - y: uint256( - 0x0015b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f - ) + t1: Honk.G1Point({ + x: uint256(0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26), + y: uint256(0x0015b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f) }), - t2: Honk.G1Point({ - x: uint256( - 0x1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e - ), - y: uint256( - 0x305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d19 - ) + t2: Honk.G1Point({ + x: uint256(0x1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e), + y: uint256(0x305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d19) }), - t3: Honk.G1Point({ - x: uint256( - 0x061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d - ), - y: uint256( - 0x1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e - ) + t3: Honk.G1Point({ + x: uint256(0x061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d), + y: uint256(0x1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e) }), - t4: Honk.G1Point({ - x: uint256( - 0x043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce - ), - y: uint256( - 0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854 - ) + t4: Honk.G1Point({ + x: uint256(0x043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce), + y: uint256(0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854) }), - id1: Honk.G1Point({ - x: uint256( - 0x0769795fa9f0d34097d7299603c36221e3bf395161fc91a78f481804abf7e170 - ), - y: uint256( - 0x190150a49e86cb7cf15c464f3f741c800d9a9d944953023fe79a5be579ac274c - ) + id1: Honk.G1Point({ + x: uint256(0x085cbd5bb5a6a7a0407ad624e78b82be37c9c90a9c5de9302004bc406bc1778d), + y: uint256(0x2a4cbd271054bf508c73ffeab6e321bba811248126e552f55701993aed192e4b) }), - id2: Honk.G1Point({ - x: uint256( - 0x08129f3cdaaafc492279ea99f91473af386bfbf92c55fd2a5efb9d3ad39c8341 - ), - y: uint256( - 0x0e02894a268b25478a7e323f900b1c81e63227533d55a9269b6e083344ddc5c3 - ) + id2: Honk.G1Point({ + x: uint256(0x1a89bfbff2ee55715a55cf0de0d6a1c62183bbaae9d200a5247b58f170cbbc7c), + y: uint256(0x0ae77530f6cad79672428f376996a91ce1358fea5f49180218f18a7918f80bb3) }), - id3: Honk.G1Point({ - x: uint256( - 0x223ba058ba70d89680a3236d02e81dea1fd139b9f9ec0074605a6073059b9e7d - ), - y: uint256( - 0x04d18416fa482ce0c6eece742967f0862202d508046a5141f5fa43fd8f75fc6b - ) + id3: Honk.G1Point({ + x: uint256(0x1faa95912a219c90e3cdbe1e78b25d6f79d4ca8ef1cd9fd3a8a6b87f94d138f2), + y: uint256(0x22a4b4a860838e96fcc07839e0c9742c2dbe4aa634664da1e24570a1dc4156fd) }), - id4: Honk.G1Point({ - x: uint256( - 0x25f874dc5eb164f0a82bd3c3f189e02089314799c7a555838569fe8499c30fe9 - ), - y: uint256( - 0x256b5957d823a2daf400f685cd16a42182563f32442982d6236b359d4ee7809a - ) + id4: Honk.G1Point({ + x: uint256(0x1d11efcd01d1170fc229d305654298122dd323cc9ae7e16ad2f0bfa56a0f9e2e), + y: uint256(0x225cd914fa8180a343e93ee8a0e87e2d850b8ca6e03267e5281f7ace3dd82ab5) }), - lagrangeFirst: Honk.G1Point({ - x: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000001 - ), - y: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000002 - ) + lagrangeFirst: Honk.G1Point({ + x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), + y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) }), - lagrangeLast: Honk.G1Point({ - x: uint256( - 0x1104b2598e170ee71593833e2ea491147e60225ba814c1f8cd3ad557de0a98ee - ), - y: uint256( - 0x2741fcea4e36b97464e7174e83dceaa89b2f2a49a3b4352193c11737f79d39a2 - ) + lagrangeLast: Honk.G1Point({ + x: uint256(0x1af84073758283d4c848fed9bc6f055831baf81a643050fceafadf8da1791eec), + y: uint256(0x1be165956a9c6a29edc2a4d21793a6fb1f00a6967720f870ebb2f2c6b267e9dd) }) }); return vk; @@ -251,31 +135,24 @@ library HonkVerificationKey { pragma solidity ^0.8.27; interface IVerifier { - function verify( - bytes calldata _proof, - bytes32[] calldata _publicInputs - ) external returns (bool); + function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); } type Fr is uint256; -using { add as + } for Fr global; -using { sub as - } for Fr global; -using { mul as * } for Fr global; +using {add as +} for Fr global; +using {sub as -} for Fr global; +using {mul as *} for Fr global; -using { exp as ^ } for Fr global; -using { notEqual as != } for Fr global; -using { equal as == } for Fr global; +using {exp as ^} for Fr global; +using {notEqual as !=} for Fr global; +using {equal as ==} for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap( - 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 -); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( - 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 -); +Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); Fr constant ONE = Fr.wrap(1); Fr constant ZERO = Fr.wrap(0); @@ -421,11 +298,9 @@ uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; // The number of entities added for ZK (gemini_masking_poly) uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_TO_BE_SHIFTED = 5; uint256 constant PAIRING_POINTS_SIZE = 16; @@ -621,63 +496,26 @@ library ZKTranscriptLib { uint256 logN ) external pure returns (ZKTranscript memory t) { Fr previousChallenge; - ( - t.relationParameters, - previousChallenge - ) = generateRelationParametersChallenges( - proof, - publicInputs, - vkHash, - publicInputsSize, - previousChallenge - ); + (t.relationParameters, previousChallenge) = + generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); - (t.alphas, previousChallenge) = generateAlphaChallenges( - previousChallenge, - proof - ); + (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); - (t.gateChallenges, previousChallenge) = generateGateChallenges( - previousChallenge, - logN - ); - (t.libraChallenge, previousChallenge) = generateLibraChallenge( - previousChallenge, - proof - ); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( - proof, - previousChallenge, - logN - ); + (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); + (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); - (t.rho, previousChallenge) = generateRhoChallenge( - proof, - previousChallenge - ); + (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); - (t.geminiR, previousChallenge) = generateGeminiRChallenge( - proof, - previousChallenge, - logN - ); + (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( - proof, - previousChallenge, - logN - ); + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( - proof, - previousChallenge - ); + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); return t; } - function splitChallenge( - Fr challenge - ) internal pure returns (Fr first, Fr second) { + function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { uint256 challengeU256 = uint256(Fr.unwrap(challenge)); // Split into two equal 127-bit chunks (254/2) uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits @@ -692,23 +530,11 @@ library ZKTranscriptLib { uint256 vkHash, uint256 publicInputsSize, Fr previousChallenge - ) - internal - pure - returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) - { - ( - rp.eta, - rp.etaTwo, - rp.etaThree, - previousChallenge - ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { + (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = + generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - ( - rp.beta, - rp.gamma, - nextPreviousChallenge - ) = generateBetaAndGammaChallenges(previousChallenge, proof); + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -716,11 +542,7 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) - internal - pure - returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) - { + ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -729,8 +551,7 @@ library ZKTranscriptLib { round0[1 + i] = bytes32(publicInputs[i]); } for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib - .toBytes32(proof.pairingPointObject[i]); + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); } // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) @@ -746,21 +567,18 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round0)) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (etaThree, ) = splitChallenge(previousChallenge); + (etaThree,) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { + function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) + { bytes32[7] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); round1[1] = bytes32(proof.lookupReadCounts.x); @@ -770,17 +588,12 @@ library ZKTranscriptLib { round1[5] = bytes32(proof.w4.x); round1[6] = bytes32(proof.w4.y); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round1)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); (beta, gamma) = splitChallenge(nextPreviousChallenge); } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) + function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) @@ -793,11 +606,9 @@ library ZKTranscriptLib { alpha0[3] = proof.zPerm.x; alpha0[4] = proof.zPerm.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(alpha0)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); Fr alpha; - (alpha, ) = splitChallenge(nextPreviousChallenge); + (alpha,) = splitChallenge(nextPreviousChallenge); // Compute powers of alpha for batching subrelations alphas[0] = alpha; @@ -806,54 +617,38 @@ library ZKTranscriptLib { } } - function generateGateChallenges( - Fr previousChallenge, - uint256 logN - ) + function generateGateChallenges(Fr previousChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) { - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); - (gateChallenges[0], ) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + (gateChallenges[0],) = splitChallenge(previousChallenge); for (uint256 i = 1; i < logN; i++) { gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; } nextPreviousChallenge = previousChallenge; } - function generateLibraChallenge( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { + function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr libraChallenge, Fr nextPreviousChallenge) + { // 2 comm, 1 sum, 1 challenge uint256[4] memory challengeData; challengeData[0] = Fr.unwrap(previousChallenge); challengeData[1] = proof.libraCommitments[0].x; challengeData[2] = proof.libraCommitments[0].y; challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(challengeData)) - ); - (libraChallenge, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); + (libraChallenge,) = splitChallenge(nextPreviousChallenge); } - function generateSumcheckChallenges( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) + function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) { for (uint256 i = 0; i < logN; i++) { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; @@ -862,27 +657,24 @@ library ZKTranscriptLib { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } - prevChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(univariateChal)) - ); + prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); - (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); + (sumcheckChallenges[i],) = splitChallenge(prevChallenge); } nextPreviousChallenge = prevChallenge; } // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { + function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr rho, Fr nextPreviousChallenge) + { uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; rhoChallengeElements[0] = Fr.unwrap(prevChallenge); uint256 i; for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap( - proof.sumcheckEvaluations[i - 1] - ); + rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); } rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); i += 1; @@ -892,17 +684,15 @@ library ZKTranscriptLib { rhoChallengeElements[i] = proof.libraCommitments[2].x; rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(rhoChallengeElements)) - ); - (rho, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); + (rho,) = splitChallenge(nextPreviousChallenge); } - function generateGeminiRChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { + function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr geminiR, Fr nextPreviousChallenge) + { uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); gR[0] = Fr.unwrap(prevChallenge); @@ -911,77 +701,59 @@ library ZKTranscriptLib { gR[2 + i * 2] = proof.geminiFoldComms[i].y; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(gR)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); - (geminiR, ) = splitChallenge(nextPreviousChallenge); + (geminiR,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkNuChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { - uint256[] memory shplonkNuChallengeElements = new uint256[]( - logN + 1 + 4 - ); + function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr shplonkNu, Fr nextPreviousChallenge) + { + uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.geminiAEvaluations[i - 1] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); } uint256 libraIdx = 0; for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.libraPolyEvals[libraIdx] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); libraIdx++; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkNuChallengeElements)) - ); - (shplonkNu, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); + (shplonkNu,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkZChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { + function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr shplonkZ, Fr nextPreviousChallenge) + { uint256[3] memory shplonkZChallengeElements; shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); shplonkZChallengeElements[1] = proof.shplonkQ.x; shplonkZChallengeElements[2] = proof.shplonkQ.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkZChallengeElements)) - ); - (shplonkZ, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); + (shplonkZ,) = splitChallenge(nextPreviousChallenge); } - function loadProof( - bytes calldata proof, - uint256 logN - ) internal pure returns (Honk.ZKProof memory p) { + function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { uint256 boundary = 0x0; // Pairing point object for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Commitments @@ -993,25 +765,17 @@ library ZKTranscriptLib { boundary += GROUP_ELEMENT_SIZE; // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); @@ -1019,68 +783,48 @@ library ZKTranscriptLib { // Sumcheck univariates for (uint256 i = 0; i < logN; i++) { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } } // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } - p.libraEvaluation = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; - p.libraCommitments[1] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Gemini // Read gemini fold univariates for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; } // Read gemini a evaluations for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Shplonk - p.shplonkQ = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // KZG - p.kzgQuotient = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); } } @@ -1098,60 +842,18 @@ library RelationsLib { Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePermutationRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateLogDerivativeLookupRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateDeltaRangeRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateEllipticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateMemoryRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateNnfRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonExternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonInternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); + accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations( - evaluations, - subrelationChallenges - ); + accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); } /** @@ -1159,15 +861,11 @@ library RelationsLib { * the relation checking code being cluttered with uint256 type casting, which is often a different colour in code * editors, and thus is noisy. */ - function wire( - Fr[NUMBER_OF_ENTITIES] memory p, - WIRE _wire - ) internal pure returns (Fr) { + function wire(Fr[NUMBER_OF_ENTITIES] memory p, WIRE _wire) internal pure returns (Fr) { return p[uint256(_wire)]; } - uint256 internal constant NEG_HALF_MODULO_P = - 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; + uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -1183,16 +881,9 @@ library RelationsLib { { Fr neg_half = Fr.wrap(NEG_HALF_MODULO_P); - Fr accum = (q_arith - Fr.wrap(3)) * - (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * - neg_half; - accum = - accum + - (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + - (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + - (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + - (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + - wire(p, WIRE.Q_C); + Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half; + accum = accum + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + wire(p, WIRE.Q_C); accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -1201,10 +892,7 @@ library RelationsLib { // Relation 1 { - Fr accum = wire(p, WIRE.W_L) + - wire(p, WIRE.W_4) - - wire(p, WIRE.W_L_SHIFT) + - wire(p, WIRE.Q_M); + Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); accum = accum * (q_arith - Fr.wrap(2)); accum = accum * (q_arith - ONE); accum = accum * q_arith; @@ -1223,67 +911,36 @@ library RelationsLib { Fr grand_product_denominator; { - Fr num = wire(p, WIRE.W_L) + - wire(p, WIRE.ID_1) * - rp.beta + - rp.gamma; - num = - num * - (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; + num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); grand_product_numerator = num; } { - Fr den = wire(p, WIRE.W_L) + - wire(p, WIRE.SIGMA_1) * - rp.beta + - rp.gamma; - den = - den * - (wire(p, WIRE.W_R) + - wire(p, WIRE.SIGMA_2) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_O) + - wire(p, WIRE.SIGMA_3) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_4) + - wire(p, WIRE.SIGMA_4) * - rp.beta + - rp.gamma); + Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; + den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); grand_product_denominator = den; } // Contribution 2 { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * - grand_product_numerator; - - acc = - acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + - (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * - grand_product_denominator); + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; + + acc = acc + - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) + * grand_product_denominator); acc = acc * domainSep; evals[2] = acc; } // Contribution 3 { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * - wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } } @@ -1299,52 +956,33 @@ library RelationsLib { // Calculate the write term (the table accumulation) { - write_term = - wire(p, WIRE.TABLE_1) + - rp.gamma + - (wire(p, WIRE.TABLE_2) * rp.eta) + - (wire(p, WIRE.TABLE_3) * rp.etaTwo) + - (wire(p, WIRE.TABLE_4) * rp.etaThree); + write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); } // Calculate the write term { - Fr derived_entry_1 = wire(p, WIRE.W_L) + - rp.gamma + - (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + - wire(p, WIRE.Q_M) * - wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + - wire(p, WIRE.Q_C) * - wire(p, WIRE.W_O_SHIFT); - - read_term = - derived_entry_1 + - (derived_entry_2 * rp.eta) + - (derived_entry_3 * rp.etaTwo) + - (wire(p, WIRE.Q_O) * rp.etaThree); + Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); + + read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + - wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = + wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) + - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation - Fr accumulatorNone = read_term * - write_term * - wire(p, WIRE.LOOKUP_INVERSES) - - inverse_exists_xor; + Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; accumulatorNone = accumulatorNone * domainSep; // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * - read_inverse - - wire(p, WIRE.LOOKUP_READ_COUNTS) * - write_inverse; + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); @@ -1458,11 +1096,7 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = - x_add_identity * - partialEval * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1470,15 +1104,8 @@ library RelationsLib { { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * - x_diff + - (ep.x_3 - ep.x_1) * - y_diff; - evals[12] = - y_add_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; + evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1491,15 +1118,9 @@ library RelationsLib { Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = - (ep.x_3 + ep.x_1 + ep.x_1) * - y1_sqr_mul_4 - - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; evals[11] = evals[11] + acc; } @@ -1507,16 +1128,8 @@ library RelationsLib { // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * - (ep.x_1 - ep.x_3) - - (ep.y_1 + ep.y_1) * - (ep.y_1 + ep.y_3); - evals[12] = - evals[12] + - y_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); + evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; } } @@ -1590,12 +1203,8 @@ library RelationsLib { * For ROM gates, qc = 0 */ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); @@ -1619,26 +1228,16 @@ library RelationsLib { ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - ap.index_is_monotonically_increasing = - ap.index_delta * - (ap.index_delta - Fr.wrap(1)); // deg 2 + ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = - (ap.index_delta * MINUS_ONE + ONE) * - ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 - evals[14] = - ap.adjacent_values_match_if_adjacent_indices_match * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - ap.ROM_consistency_check_identity = - ap.memory_record_check * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)); // deg 3 or 7 + ap.ROM_consistency_check_identity = ap.memory_record_check * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)); // deg 3 or 7 /** * Contributions 15,16,17 @@ -1665,22 +1264,13 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = - wire(p, WIRE.W_4_SHIFT) - - ap.next_gate_access_type; + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * - value_delta * - (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 // We can't apply the RAM consistency check identity on the final entry in the sorted list (the wires in the // next gate would make the identity fail). We need to validate that its 'access type' bool is correct. Can't @@ -1688,28 +1278,15 @@ library RelationsLib { // type is correct, to cover this edge case // deg 2 or 4 ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * - ap.next_gate_access_type - - ap.next_gate_access_type; + ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; // Putting it all together... - evals[16] = - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = - ap.next_gate_access_type_is_boolean * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = - ap.access_check * - (wire(p, WIRE.Q_O)); // deg 3 or 9 + evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation + * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 /** * RAM Timestamp Consistency Check @@ -1723,10 +1300,7 @@ library RelationsLib { * Else timestamp_check = 0 */ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = - (ap.index_delta * MINUS_ONE + ONE) * - ap.timestamp_delta - - wire(p, WIRE.W_O); // deg 3 + ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 /** * Complete Contribution 12 @@ -1735,21 +1309,12 @@ library RelationsLib { */ ap.memory_identity = ap.ROM_consistency_check_identity; // deg 3 or 6 ap.memory_identity = - ap.memory_identity + - ap.RAM_timestamp_check_identity * - (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 - ap.memory_identity = - ap.memory_identity + - ap.memory_record_check * - (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 - ap.memory_identity = - ap.memory_identity + - ap.RAM_consistency_check_identity; // deg 3 or 9 + ap.memory_identity + ap.RAM_timestamp_check_identity * (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = - ap.memory_identity * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 evals[13] = ap.memory_identity; } @@ -1788,56 +1353,28 @@ library RelationsLib { * * */ - ap.limb_subproduct = - wire(p, WIRE.W_L) * - wire(p, WIRE.W_R_SHIFT) + - wire(p, WIRE.W_L_SHIFT) * - wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * - wire(p, WIRE.W_4) + - wire(p, WIRE.W_R) * - wire(p, WIRE.W_O) - - wire(p, WIRE.W_O_SHIFT)); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 - - wire(p, WIRE.W_4_SHIFT); + ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 + - ap.limb_subproduct; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 * - wire(p, WIRE.Q_4); + (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = - ap.limb_subproduct + - (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 - - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 * - wire(p, WIRE.Q_O); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 + - wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 - - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 * - wire(p, WIRE.Q_M); - - Fr non_native_field_identity = ap.non_native_field_gate_1 + - ap.non_native_field_gate_2 + - ap.non_native_field_gate_3; - non_native_field_identity = - non_native_field_identity * - wire(p, WIRE.Q_R); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); + + Fr non_native_field_identity = + ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; + non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm // deg 2 @@ -1865,11 +1402,8 @@ library RelationsLib { ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - Fr limb_accumulator_identity = ap.limb_accumulator_1 + - ap.limb_accumulator_2; - limb_accumulator_identity = - limb_accumulator_identity * - wire(p, WIRE.Q_O); // deg 3 + Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; + limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); @@ -1929,25 +1463,13 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = - evals[20] + - ep.q_pos_by_scaling * - (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = - evals[21] + - ep.q_pos_by_scaling * - (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = - evals[22] + - ep.q_pos_by_scaling * - (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = - evals[23] + - ep.q_pos_by_scaling * - (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } struct PoseidonInternalParams { @@ -1972,18 +1494,10 @@ library RelationsLib { PoseidonInternalParams memory ip; Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from( - 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 - ), - FrLib.from( - 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b - ), - FrLib.from( - 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 - ), - FrLib.from( - 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b - ) + FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), + FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), + FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), + FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) ]; // add round constants @@ -2001,28 +1515,16 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = - evals[24] + - ip.q_pos_by_scaling * - (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = - evals[25] + - ip.q_pos_by_scaling * - (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = - evals[26] + - ip.q_pos_by_scaling * - (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = - evals[27] + - ip.q_pos_by_scaling * - (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha @@ -2034,10 +1536,7 @@ library RelationsLib { accumulator = evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = - accumulator + - evaluations[i] * - subrelationChallenges[i - 1]; + accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; } } } @@ -2073,10 +1572,7 @@ library CommitmentSchemeLib { Fr[] foldPosEvaluations; } - function computeSquares( - Fr r, - uint256 logN - ) internal pure returns (Fr[] memory) { + function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { Fr[] memory squares = new Fr[](logN); squares[0] = r; for (uint256 i = 1; i < logN; ++i) { @@ -2098,15 +1594,10 @@ library CommitmentSchemeLib { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr batchedEvalRoundAcc = ((challengePower * - batchedEvalAccumulator * - Fr.wrap(2)) - - geminiEvaluations[i - 1] * - (challengePower * (ONE - u) - u)); + Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] + * (challengePower * (ONE - u) - u)); // Divide by the denominator - batchedEvalRoundAcc = - batchedEvalRoundAcc * - (challengePower * (ONE - u) + u).invert(); + batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); batchedEvalAccumulator = batchedEvalRoundAcc; foldPosEvaluations[i - 1] = batchedEvalRoundAcc; @@ -2137,18 +1628,13 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { } // EC Point utilities -function bytesToG1Point( - bytes calldata proofSection -) pure returns (Honk.G1Point memory point) { +function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, - y: uint256(bytes32(proofSection[0x20:0x40])) % Q + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); } -function negateInplace( - Honk.G1Point memory point -) pure returns (Honk.G1Point memory) { +function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; } @@ -2165,9 +1651,10 @@ function negateInplace( * @return lhs * @return rhs */ -function convertPairingPointsToG1( - Fr[PAIRING_POINTS_SIZE] memory pairingPoints -) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { +function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) + pure + returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) +{ uint256 lhsX = Fr.unwrap(pairingPoints[0]); lhsX |= Fr.unwrap(pairingPoints[1]) << 68; lhsX |= Fr.unwrap(pairingPoints[2]) << 136; @@ -2211,10 +1698,7 @@ function generateRecursionSeparator( // hash the accum X // hash the accum Y - ( - Honk.G1Point memory proofLhs, - Honk.G1Point memory proofRhs - ) = convertPairingPointsToG1(proofPairingPoints); + (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); uint256[8] memory recursionSeparatorElements; @@ -2230,9 +1714,7 @@ function generateRecursionSeparator( recursionSeparatorElements[6] = accRhs.x; recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32( - keccak256(abi.encodePacked(recursionSeparatorElements)) - ); + recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); } /** @@ -2244,11 +1726,10 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator( - Honk.G1Point memory basePoint, - Honk.G1Point memory other, - Fr recursionSeperator -) view returns (Honk.G1Point memory) { +function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) + view + returns (Honk.G1Point memory) +{ Honk.G1Point memory result; result = ecMul(recursionSeperator, basePoint); @@ -2265,10 +1746,7 @@ function mulWithSeperator( * @param point The point to multiply. * @return result The result of the multiplication. */ -function ecMul( - Fr value, - Honk.G1Point memory point -) view returns (Honk.G1Point memory) { +function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2314,10 +1792,7 @@ function ecMul( * @param rhs The right hand side of the addition. * @return result The result of the addition. */ -function ecAdd( - Honk.G1Point memory lhs, - Honk.G1Point memory rhs -) view returns (Honk.G1Point memory) { +function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2341,9 +1816,7 @@ function ecAdd( // Call the ecAdd precompile, it takes in the following // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { - revert(0, 0) - } + if iszero(success) { revert(0, 0) } // Copy the result of the addition back into the result memory location. // Memory layout: @@ -2372,41 +1845,22 @@ function validateOnCurve(Honk.G1Point memory point) pure { require(success, "point is not on the curve"); } -function pairing( - Honk.G1Point memory rhs, - Honk.G1Point memory lhs -) view returns (bool decodedResult) { +function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { bytes memory input = abi.encodePacked( rhs.x, rhs.y, // Fixed G2 point - uint256( - 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 - ), - uint256( - 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed - ), - uint256( - 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b - ), - uint256( - 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - ), + uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), + uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), + uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), + uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), lhs.x, lhs.y, // G2 point from VK - uint256( - 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 - ), - uint256( - 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 - ), - uint256( - 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 - ), - uint256( - 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 - ) + uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), + uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), + uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), + uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) ); (bool success, bytes memory result) = address(0x08).staticcall(input); @@ -2415,6 +1869,9 @@ function pairing( // Field arithmetic libraries - prevent littering the code with modmul / addmul + + + abstract contract BaseZKHonkVerifier is IVerifier { using FrLib for Fr; @@ -2424,12 +1881,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 immutable $NUM_PUBLIC_INPUTS; uint256 immutable $MSMSize; - constructor( - uint256 _N, - uint256 _logN, - uint256 _vkHash, - uint256 _numPublicInputs - ) { + constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { $N = _N; $LOG_N = _logN; $VK_HASH = _vkHash; @@ -2439,11 +1891,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Errors error ProofLengthWrong(); - error ProofLengthWrongWithLogN( - uint256 logN, - uint256 actualLength, - uint256 expectedLength - ); + error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); error PublicInputsLengthWrong(); error SumcheckFailed(); error ShpleminiFailed(); @@ -2463,10 +1911,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking // Sumcheck - proofLength += - logN * - ZK_BATCHED_RELATION_PARTIAL_LENGTH * - NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations // Libra and Gemini @@ -2486,26 +1931,20 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 constant SHIFTED_COMMITMENTS_START = 30; - function loadVerificationKey() - internal - pure - virtual - returns (Honk.VerificationKey memory); + function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); - function verify( - bytes calldata proof, - bytes32[] calldata publicInputs - ) public view override returns (bool verified) { + function verify(bytes calldata proof, bytes32[] calldata publicInputs) + public + view + override + returns (bool verified) + { // Calculate expected proof size based on $LOG_N uint256 expectedProofSize = calculateProofSize($LOG_N); // Check the received proof is the expected size where each field element is 32 bytes if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN( - $LOG_N, - proof.length, - expectedProofSize * 32 - ); + revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); } Honk.VerificationKey memory vk = loadVerificationKey(); @@ -2516,20 +1955,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { } // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = ZKTranscriptLib.generateTranscript( - p, - publicInputs, - $VK_HASH, - $NUM_PUBLIC_INPUTS, - $LOG_N - ); + ZKTranscript memory t = + ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); // Derive public input delta t.relationParameters.publicInputsDelta = computePublicInputDelta( publicInputs, p.pairingPointObject, t.relationParameters.beta, - t.relationParameters.gamma /*pubInputsOffset=*/, + t.relationParameters.gamma, /*pubInputsOffset=*/ 1 ); @@ -2553,16 +1987,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr numerator = Fr.wrap(1); Fr denominator = Fr.wrap(1); - Fr numeratorAcc = gamma + - (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { - for ( - uint256 i = 0; - i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; - i++ - ) { + for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { Fr pubInput = FrLib.fromBytes32(publicInputs[i]); numerator = numerator * (numeratorAcc + pubInput); @@ -2587,32 +2016,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { publicInputDelta = FrLib.div(numerator, denominator); } - function verifySumcheck( - Honk.ZKProof memory proof, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 Fr powPartialEvaluation = Fr.wrap(1); // We perform sumcheck reductions over log n rounds ( the multivariate degree ) for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; if (totalSum != roundTargetSum) revert SumcheckFailed(); Fr roundChallenge = tp.sumCheckUChallenges[round]; // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum( - roundUnivariate, - roundChallenge - ); + roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); powPartialEvaluation = - powPartialEvaluation * - (Fr.wrap(1) + - roundChallenge * - (tp.gateChallenges[round] - Fr.wrap(1))); + powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); } // Last round @@ -2620,15 +2039,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[ - i + NUM_MASKING_POLYNOMIALS - ]; // Skip gemini_masking_poly at index 0 + relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 } Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, - tp.relationParameters, - tp.alphas, - powPartialEvaluation + relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation ); Fr evaluation = Fr.wrap(1); @@ -2637,48 +2051,27 @@ abstract contract BaseZKHonkVerifier is IVerifier { } grandHonkRelationSum = - grandHonkRelationSum * - (Fr.wrap(1) - evaluation) + - proof.libraEvaluation * - tp.libraChallenge; + grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; verified = (grandHonkRelationSum == roundTargetSum); } // Return the new target sum for the next sumcheck round - function computeNextTargetSum( - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, - Fr roundChallenge - ) internal view returns (Fr targetSum) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000000240 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ) - ]; + function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) + internal + view + returns (Fr targetSum) + { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) + ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). @@ -2691,17 +2084,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert( - BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * - (roundChallenge - Fr.wrap(i)) - ); + denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); } for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = - targetSum + - roundUnivariates[i] * - denominatorInverses[i]; + targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; } // Scale the sum by the value of B(x) @@ -2717,63 +2104,56 @@ abstract contract BaseZKHonkVerifier is IVerifier { Honk.G1Point P_1; } - function verifyShplemini( - Honk.ZKProof memory proof, - Honk.VerificationKey memory vk, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) + internal + view + returns (bool verified) + { CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib - .computeSquares(tp.geminiR, $LOG_N); + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings Fr[] memory scalars = new Fr[]($MSMSize); Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[0]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = - mem.posInvertedDenominator + - (tp.shplonkNu * mem.negInvertedDenominator); + mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * - (mem.posInvertedDenominator - - (tp.shplonkNu * mem.negInvertedDenominator)); + tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = proof.shplonkQ; /* Batch multivariate opening claims, shifted and unshifted - * The vector of scalars is populated as follows: - * \f[ - * \left( - * - \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right), - * \ldots, - * - \rho^{i+k-1} \times \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right), - * - \rho^{i+k} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right), - * \ldots, - * - \rho^{k+m-1} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right) - * \right) - * \f] - * - * The following vector is concatenated to the vector of commitments: - * \f[ - * f_0, \ldots, f_{m-1}, f_{\text{shift}, 0}, \ldots, f_{\text{shift}, k-1} - * \f] - * - * Simultaneously, the evaluation of the multilinear polynomial - * \f[ - * \sum \rho^i \cdot f_i + \sum \rho^{i+k} \cdot f_{\text{shift}, i} - * \f] - * at the challenge point \f$ (u_0,\ldots, u_{n-1}) \f$ is computed. - * - * This approach minimizes the number of iterations over the commitments to multilinear polynomials - * and eliminates the need to store the powers of \f$ \rho \f$. - */ + * The vector of scalars is populated as follows: + * \f[ + * \left( + * - \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right), + * \ldots, + * - \rho^{i+k-1} \times \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right), + * - \rho^{i+k} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right), + * \ldots, + * - \rho^{k+m-1} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right) + * \right) + * \f] + * + * The following vector is concatenated to the vector of commitments: + * \f[ + * f_0, \ldots, f_{m-1}, f_{\text{shift}, 0}, \ldots, f_{\text{shift}, k-1} + * \f] + * + * Simultaneously, the evaluation of the multilinear polynomial + * \f[ + * \sum \rho^i \cdot f_i + \sum \rho^{i+k} \cdot f_{\text{shift}, i} + * \f] + * at the challenge point \f$ (u_0,\ldots, u_{n-1}) \f$ is computed. + * + * This approach minimizes the number of iterations over the commitments to multilinear polynomials + * and eliminates the need to store the powers of \f$ \rho \f$. + */ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] // Start batching challenge at 1, not rho, to match non-ZK pattern mem.batchingChallenge = Fr.wrap(1); @@ -2785,10 +2165,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * - mem.batchingChallenge); + mem.batchedEvaluation = mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } // g commitments are accumulated at r @@ -2801,13 +2179,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - scalars[scalarOff] = - scalars[scalarOff] + - (mem.shiftedScalarNeg * mem.batchingChallenge); + scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[evaluationOff] * - mem.batchingChallenge); + mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } @@ -2860,15 +2234,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \f[ * \left( \text{com}(A_1), \text{com}(A_2), \ldots, \text{com}(A_{n-1}) \right) * \f] - * to the 'commitments' vector. - * - * 2. Computes the scalars: - * \f[ - * \frac{\nu^{2}}{z + r^2}, \frac{\nu^3}{z + r^4}, \ldots, \frac{\nu^{n-1}}{z + r^{2^{n-1}}} - * \f] - * and places them into the 'scalars' vector. - * - * 3. Accumulates the summands of the constant term: + * to the 'commitments' vector. + * + * 2. Computes the scalars: + * \f[ + * \frac{\nu^{2}}{z + r^2}, \frac{\nu^3}{z + r^4}, \ldots, \frac{\nu^{n-1}}{z + r^{2^{n-1}}} + * \f] + * and places them into the 'scalars' vector. + * + * 3. Accumulates the summands of the constant term: * \f[ * \sum_{i=2}^{n-1} \frac{\nu^{i} \cdot A_i(-r^{2^i})}{z + r^{2^i}} * \f] @@ -2877,23 +2251,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib - .computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); + mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; mem.constantTermAccumulator = - foldPosEvaluations[0] * - mem.posInvertedDenominator; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - (proof.geminiAEvaluations[0] * - tp.shplonkNu * - mem.negInvertedDenominator); + mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; @@ -2905,40 +2273,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { if (!dummy_round) { // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[i + 1]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = - mem.batchingChallenge * - mem.posInvertedDenominator; - mem.scalingFactorNeg = - mem.batchingChallenge * - tp.shplonkNu * - mem.negInvertedDenominator; - scalars[boundary + i] = - mem.scalingFactorNeg.neg() + - mem.scalingFactorPos.neg(); + mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; + mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; + scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); // Accumulate the const term contribution given by // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * - proof.geminiAEvaluations[i + 1]; - accumContribution = - accumContribution + - mem.scalingFactorPos * - foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - accumContribution; + Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; + accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; } // Update the running power of v - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; commitments[boundary + i] = proof.geminiFoldComms[i]; } @@ -2947,24 +2297,16 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Finalize the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div( - tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR - ); + mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - scalingFactor * - proof.libraPolyEvals[i]; + mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; } scalars[boundary] = mem.batchingScalars[0]; scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; @@ -2974,17 +2316,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { commitments[boundary++] = proof.libraCommitments[i]; } - commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); + commitments[boundary] = Honk.G1Point({x: 1, y: 2}); scalars[boundary++] = mem.constantTermAccumulator; - if ( - !checkEvalsConsistency( - proof.libraPolyEvals, - tp.geminiR, - tp.sumCheckUChallenges, - proof.libraEvaluation - ) - ) { + if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { revert ConsistencyCheckFailed(); } @@ -2998,15 +2333,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { pair.P_1 = negateInplace(quotient_commitment); // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator( - proof.pairingPointObject, - pair.P_0, - pair.P_1 - ); - ( - Honk.G1Point memory P_0_other, - Honk.G1Point memory P_1_other - ) = convertPairingPointsToG1(proof.pairingPointObject); + Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); + (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = + convertPairingPointsToG1(proof.pairingPointObject); // Validate the points from the proof are on the curve validateOnCurve(P_0_other); @@ -3046,14 +2375,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 round = 0; round < $LOG_N; round++) { uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; mem.challengePolyLagrange[currIdx] = one; - for ( - uint256 idx = currIdx + 1; - idx < currIdx + LIBRA_UNIVARIATES_LENGTH; - idx++ - ) { - mem.challengePolyLagrange[idx] = - mem.challengePolyLagrange[idx - 1] * - uChallenges[round]; + for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { + mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; } } @@ -3062,10 +2385,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { mem.denominators[idx] = mem.rootPower * geminiR - one; mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = - mem.challengePolyEval + - mem.challengePolyLagrange[idx] * - mem.denominators[idx]; + mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; } @@ -3076,28 +2396,19 @@ abstract contract BaseZKHonkVerifier is IVerifier { mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - mem.diff = - mem.diff + - (geminiR - SUBGROUP_GENERATOR_INVERSE) * - (libraPolyEvals[1] - - libraPolyEvals[2] - - libraPolyEvals[0] * - mem.challengePolyEval); - mem.diff = - mem.diff + - mem.lagrangeLast * - (libraPolyEvals[2] - libraEval) - - vanishingPolyEval * - libraPolyEvals[3]; + mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) + * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); + mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; check = mem.diff == Fr.wrap(0); } // This implementation is the same as above with different constants - function batchMul( - Honk.G1Point[] memory base, - Fr[] memory scalars - ) internal view returns (Honk.G1Point memory result) { + function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) + internal + view + returns (Honk.G1Point memory result) + { uint256 limit = $MSMSize; // Validate all points are on the curve @@ -3110,9 +2421,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { let free := mload(0x40) let count := 0x01 - for {} lt(count, add(limit, 1)) { - count := add(count, 1) - } { + for {} lt(count, add(limit, 1)) { count := add(count, 1) } { // Get loop offsets let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) @@ -3122,22 +2431,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add scalar mstore(add(free, 0x80), mload(scalar_base)) - success := and( - success, - staticcall( - gas(), - 7, - add(free, 0x40), - 0x60, - add(free, 0x40), - 0x40 - ) - ) + success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) // accumulator = accumulator + accumulator_2 - success := and( - success, - staticcall(gas(), 6, free, 0x80, free, 0x40) - ) + success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) } // Return the result @@ -3149,15 +2445,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract DkgPkVerifier is - BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) -{ - function loadVerificationKey() - internal - pure - override - returns (Honk.VerificationKey memory) - { - return HonkVerificationKey.loadVerificationKey(); +contract ThresholdDecryptedSharesAggregationModVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { + function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { + return HonkVerificationKey.loadVerificationKey(); } } diff --git a/packages/enclave-contracts/contracts/verifier/ThresholdPkAggregationVerifier.sol b/packages/enclave-contracts/contracts/verifier/ThresholdPkAggregationVerifier.sol new file mode 100644 index 0000000000..4448f656df --- /dev/null +++ b/packages/enclave-contracts/contracts/verifier/ThresholdPkAggregationVerifier.sol @@ -0,0 +1,2452 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +pragma solidity >=0.8.21; + +uint256 constant N = 262144; +uint256 constant LOG_N = 18; +uint256 constant NUMBER_OF_PUBLIC_INPUTS = 22; +uint256 constant VK_HASH = 0x02306f0575881d3ddf6f0b9e8dac79af163672a144640cdf2a6ed6942ee4c6aa; +library HonkVerificationKey { + function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { + Honk.VerificationKey memory vk = Honk.VerificationKey({ + circuitSize: uint256(262144), + logCircuitSize: uint256(18), + publicInputsSize: uint256(22), + ql: Honk.G1Point({ + x: uint256(0x131fd9af41c938c8b1b6e0086eb4f08ba4075a71288d83449d8d5eac6247099a), + y: uint256(0x072066e82cbf467dd3c411fdaf6242d1ca23f1b37710849520bf2cd4b714e795) + }), + qr: Honk.G1Point({ + x: uint256(0x02f04c8042c961b5c40c8617efb47c118b71d76c64259724eb802e8efdada48f), + y: uint256(0x266704b76a579143a49d75c18ad41e4c8664833a345240b18787c26e832b2c2e) + }), + qo: Honk.G1Point({ + x: uint256(0x0efe5ef9accef9728a562f5e9ea4fd60b0957f6e559689936896c9d7e94823ba), + y: uint256(0x280a5dfe9d34183e42c5f275391576ae8e2891ab077920340756a44d9131ca70) + }), + q4: Honk.G1Point({ + x: uint256(0x2ecfda953e756b07faf8e3954fe5012494ee0237dea45538b88e34d949a516f9), + y: uint256(0x0677e9dcbd562e682c44632da1d4b5ffecdc6c9129adb89089c020384a34d281) + }), + qm: Honk.G1Point({ + x: uint256(0x1c34241ed7bd1ea35de9fe54f11e3deca92247eda67d756489374539ed080828), + y: uint256(0x1fa29ca2f59dfccf9e71342fe789ae8e2aa41079798daf79b8e02d06a510a4b7) + }), + qc: Honk.G1Point({ + x: uint256(0x147ff414795e16dd9d8ad7758205875dcbb2feca77004005326a37295450902e), + y: uint256(0x2ce19f89d76fc3a0e68df64342ecb603eb7743838eaab9cad94f597ff49c3d52) + }), + qLookup: Honk.G1Point({ + x: uint256(0x15729599ef3888a89db8f52912b65cdfc12205eb78f54bd9f4742df51d35d9d2), + y: uint256(0x1890a0e4fb04b07215d2034d92fc6ad93cb3670761c8c93df420b3a7d89baefa) + }), + qArith: Honk.G1Point({ + x: uint256(0x2aa5463c894b45d28a4b5ea9bed212fc3dd803ce12033d85358d4432a0655c6a), + y: uint256(0x1397cec5342f4f86432b983400c0e56ceb029e78abeba47fc958779df917e1d7) + }), + qDeltaRange: Honk.G1Point({ + x: uint256(0x1617218df444678e9120d0de7f480a6d0bf6098c6e77009e8cc81a5bb0e820fc), + y: uint256(0x1f03683cfa36530952dc9494533856af2e1487bed9e76f07b4afda82f3afb129) + }), + qElliptic: Honk.G1Point({ + x: uint256(0x2229994889f103841fc6d5f7a4b209e045992468aec5a30800606e5c1918007c), + y: uint256(0x016891f753e67c0d4349565eaeb8c02d828793eb835c20c532e771ec6918702f) + }), + qMemory: Honk.G1Point({ + x: uint256(0x161d4f65fef69c1bbc2a671569c92da155e9374fabef1d260eb8776142c73bd8), + y: uint256(0x268e5909d8cab6c89c8064ee0fc25a7fc541a3238f93dea808d9fda942421132) + }), + qNnf: Honk.G1Point({ + x: uint256(0x26be72a59de37228c424b5ffc3149b2489578d0f09ba433097d9c8596473da5f), + y: uint256(0x1e1d0abff71ff2bf7e0833e32af3baff87b41a17a2cf187367c04ced7bc5ff7a) + }), + qPoseidon2External: Honk.G1Point({ + x: uint256(0x099ba64dd88f156dc06f527c2bac7b2c7c86d903e24ac09e47f833cfd6f903b8), + y: uint256(0x067f7914c30e0ca5dab695d46a6b6644f048d04e0938eea01c282d7ba0f043cc) + }), + qPoseidon2Internal: Honk.G1Point({ + x: uint256(0x281fd5754fbb77ac2ef58f72fe142cf865a329c0611b38919dd1a36feb994360), + y: uint256(0x15f7f262eba8a9b6df7a0928493a1399b33b1f765cb31c934d1cbde777c3bdfd) + }), + s1: Honk.G1Point({ + x: uint256(0x04ff10ec802daeb081f77ae5a048da6c5695f760c4b705fa2d0ae3cd5e76a9ae), + y: uint256(0x1242b0b182171f1a737e13750ff2ff10762a6fcb1d4280ce14b5b3a43295610d) + }), + s2: Honk.G1Point({ + x: uint256(0x20bb81a1dc9b0c8d3cbcc2761b6f6751e08be09b384efd9b56bd5f81ca29c136), + y: uint256(0x10d80cf403b8ff3de754a57331c5dd143cb0ae9e0382550efd8f20b353e274b5) + }), + s3: Honk.G1Point({ + x: uint256(0x047c3bf9b1df8256aa780ba25fac792e3a8de5e460d9cb3e41bf8686653a2b83), + y: uint256(0x1f13e4fa50f1ff9430c4f58316e6c3f1ccdcf87540d6a19add847606138d93bf) + }), + s4: Honk.G1Point({ + x: uint256(0x087819484fd402168d10636c0e1ff16095086cfebb16fa72ecd905faa77611e2), + y: uint256(0x061b92f2194d55921ff02e4362fe5b4f59eac905edb027e656700a56806a5e89) + }), + t1: Honk.G1Point({ + x: uint256(0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26), + y: uint256(0x0015b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f) + }), + t2: Honk.G1Point({ + x: uint256(0x1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e), + y: uint256(0x305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d19) + }), + t3: Honk.G1Point({ + x: uint256(0x061f64497996e8915722501e9e367938ed8da2375186b518c7345c60b1134b2d), + y: uint256(0x1b84d38339321f405ebaf6a2f830842ad3d7cb59792e11c0d2691f317fd50e6e) + }), + t4: Honk.G1Point({ + x: uint256(0x043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce), + y: uint256(0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854) + }), + id1: Honk.G1Point({ + x: uint256(0x30302f92d6b476ab0407ba6570e2c869057a534fafc859946c315715fd37397d), + y: uint256(0x2bccc49a1f124a8ea4933e1f65cd744c4ef91d17b429bc27f44ed533ff179ad9) + }), + id2: Honk.G1Point({ + x: uint256(0x2fa778d2df483e2c7535aa5b015aba244987291fe5d0f78655332f35dbb983a2), + y: uint256(0x0eaaa6e5c290180d0979a51caf8592f4014402d09f8fdee08e2d2b98596b1743) + }), + id3: Honk.G1Point({ + x: uint256(0x035d549bad95f400a2d94f6032216957b08b7ab07ab972d8403663454ecb46e4), + y: uint256(0x02512a1a401fd0832cea115ea34675373aa09faa59149b8ffd71fe76be90c306) + }), + id4: Honk.G1Point({ + x: uint256(0x07377ddbcd535099c7600f69e539e1d41cdfd8705405334687c4f5b741226da8), + y: uint256(0x06608b7e3be5235cc0368ce844f8c822b118e54f993626431efa578a881b1e01) + }), + lagrangeFirst: Honk.G1Point({ + x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), + y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) + }), + lagrangeLast: Honk.G1Point({ + x: uint256(0x1280735205fd7e4a58ee8493d273e5afa2f13bfb7879873c4095a5cbfc646a41), + y: uint256(0x155c3c102286dfdbfe78f876a3c74b9c36a02bb8197154c7d9fedb371f55b779) + }) + }); + return vk; + } +} + +pragma solidity ^0.8.27; + +interface IVerifier { + function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); +} + +type Fr is uint256; + +using {add as +} for Fr global; +using {sub as -} for Fr global; +using {mul as *} for Fr global; + +using {exp as ^} for Fr global; +using {notEqual as !=} for Fr global; +using {equal as ==} for Fr global; + +uint256 constant SUBGROUP_SIZE = 256; +uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order +uint256 constant P = MODULUS; +Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); +Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); +Fr constant ONE = Fr.wrap(1); +Fr constant ZERO = Fr.wrap(0); +// Instantiation + +library FrLib { + function from(uint256 value) internal pure returns (Fr) { + unchecked { + return Fr.wrap(value % MODULUS); + } + } + + function fromBytes32(bytes32 value) internal pure returns (Fr) { + unchecked { + return Fr.wrap(uint256(value) % MODULUS); + } + } + + function toBytes32(Fr value) internal pure returns (bytes32) { + unchecked { + return bytes32(Fr.unwrap(value)); + } + } + + function invert(Fr value) internal view returns (Fr) { + uint256 v = Fr.unwrap(value); + uint256 result; + + // Call the modexp precompile to invert in the field + assembly { + let free := mload(0x40) + mstore(free, 0x20) + mstore(add(free, 0x20), 0x20) + mstore(add(free, 0x40), 0x20) + mstore(add(free, 0x60), v) + mstore(add(free, 0x80), sub(MODULUS, 2)) + mstore(add(free, 0xa0), MODULUS) + let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) + if iszero(success) { + revert(0, 0) + } + result := mload(0x00) + mstore(0x40, add(free, 0x80)) + } + + return Fr.wrap(result); + } + + function pow(Fr base, uint256 v) internal view returns (Fr) { + uint256 b = Fr.unwrap(base); + uint256 result; + + // Call the modexp precompile to invert in the field + assembly { + let free := mload(0x40) + mstore(free, 0x20) + mstore(add(free, 0x20), 0x20) + mstore(add(free, 0x40), 0x20) + mstore(add(free, 0x60), b) + mstore(add(free, 0x80), v) + mstore(add(free, 0xa0), MODULUS) + let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) + if iszero(success) { + revert(0, 0) + } + result := mload(0x00) + mstore(0x40, add(free, 0x80)) + } + + return Fr.wrap(result); + } + + function div(Fr numerator, Fr denominator) internal view returns (Fr) { + unchecked { + return numerator * invert(denominator); + } + } + + function sqr(Fr value) internal pure returns (Fr) { + unchecked { + return value * value; + } + } + + function unwrap(Fr value) internal pure returns (uint256) { + unchecked { + return Fr.unwrap(value); + } + } + + function neg(Fr value) internal pure returns (Fr) { + unchecked { + return Fr.wrap(MODULUS - Fr.unwrap(value)); + } + } +} + +// Free functions +function add(Fr a, Fr b) pure returns (Fr) { + unchecked { + return Fr.wrap(addmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS)); + } +} + +function mul(Fr a, Fr b) pure returns (Fr) { + unchecked { + return Fr.wrap(mulmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS)); + } +} + +function sub(Fr a, Fr b) pure returns (Fr) { + unchecked { + return Fr.wrap(addmod(Fr.unwrap(a), MODULUS - Fr.unwrap(b), MODULUS)); + } +} + +function exp(Fr base, Fr exponent) pure returns (Fr) { + if (Fr.unwrap(exponent) == 0) return Fr.wrap(1); + // Implement exponent with a loop as we will overflow otherwise + for (uint256 i = 1; i < Fr.unwrap(exponent); i += i) { + base = base * base; + } + return base; +} + +function notEqual(Fr a, Fr b) pure returns (bool) { + unchecked { + return Fr.unwrap(a) != Fr.unwrap(b); + } +} + +function equal(Fr a, Fr b) pure returns (bool) { + unchecked { + return Fr.unwrap(a) == Fr.unwrap(b); + } +} + +uint256 constant CONST_PROOF_SIZE_LOG_N = 28; + +uint256 constant NUMBER_OF_SUBRELATIONS = 28; +uint256 constant BATCHED_RELATION_PARTIAL_LENGTH = 8; +uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; +uint256 constant NUMBER_OF_ENTITIES = 41; +// The number of entities added for ZK (gemini_masking_poly) +uint256 constant NUM_MASKING_POLYNOMIALS = 1; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED = 36; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_TO_BE_SHIFTED = 5; +uint256 constant PAIRING_POINTS_SIZE = 16; + +uint256 constant FIELD_ELEMENT_SIZE = 0x20; +uint256 constant GROUP_ELEMENT_SIZE = 0x40; + +// Powers of alpha used to batch subrelations (alpha, alpha^2, ..., alpha^(NUM_SUBRELATIONS-1)) +uint256 constant NUMBER_OF_ALPHAS = NUMBER_OF_SUBRELATIONS - 1; + +// ENUM FOR WIRES +enum WIRE { + Q_M, + Q_C, + Q_L, + Q_R, + Q_O, + Q_4, + Q_LOOKUP, + Q_ARITH, + Q_RANGE, + Q_ELLIPTIC, + Q_MEMORY, + Q_NNF, + Q_POSEIDON2_EXTERNAL, + Q_POSEIDON2_INTERNAL, + SIGMA_1, + SIGMA_2, + SIGMA_3, + SIGMA_4, + ID_1, + ID_2, + ID_3, + ID_4, + TABLE_1, + TABLE_2, + TABLE_3, + TABLE_4, + LAGRANGE_FIRST, + LAGRANGE_LAST, + W_L, + W_R, + W_O, + W_4, + Z_PERM, + LOOKUP_INVERSES, + LOOKUP_READ_COUNTS, + LOOKUP_READ_TAGS, + W_L_SHIFT, + W_R_SHIFT, + W_O_SHIFT, + W_4_SHIFT, + Z_PERM_SHIFT +} + +library Honk { + struct G1Point { + uint256 x; + uint256 y; + } + + struct VerificationKey { + // Misc Params + uint256 circuitSize; + uint256 logCircuitSize; + uint256 publicInputsSize; + // Selectors + G1Point qm; + G1Point qc; + G1Point ql; + G1Point qr; + G1Point qo; + G1Point q4; + G1Point qLookup; // Lookup + G1Point qArith; // Arithmetic widget + G1Point qDeltaRange; // Delta Range sort + G1Point qMemory; // Memory + G1Point qNnf; // Non-native Field + G1Point qElliptic; // Auxillary + G1Point qPoseidon2External; + G1Point qPoseidon2Internal; + // Copy constraints + G1Point s1; + G1Point s2; + G1Point s3; + G1Point s4; + // Copy identity + G1Point id1; + G1Point id2; + G1Point id3; + G1Point id4; + // Precomputed lookup table + G1Point t1; + G1Point t2; + G1Point t3; + G1Point t4; + // Fixed first and last + G1Point lagrangeFirst; + G1Point lagrangeLast; + } + + struct RelationParameters { + // challenges + Fr eta; + Fr etaTwo; + Fr etaThree; + Fr beta; + Fr gamma; + // derived + Fr publicInputsDelta; + } + + struct Proof { + // Pairing point object + Fr[PAIRING_POINTS_SIZE] pairingPointObject; + // Free wires + G1Point w1; + G1Point w2; + G1Point w3; + G1Point w4; + // Lookup helpers - Permutations + G1Point zPerm; + // Lookup helpers - logup + G1Point lookupReadCounts; + G1Point lookupReadTags; + G1Point lookupInverses; + // Sumcheck + Fr[BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates; + Fr[NUMBER_OF_ENTITIES] sumcheckEvaluations; + // Shplemini + G1Point[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms; + Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations; + G1Point shplonkQ; + G1Point kzgQuotient; + } + + /// forge-lint: disable-next-item(pascal-case-struct) + struct ZKProof { + // Pairing point object + Fr[PAIRING_POINTS_SIZE] pairingPointObject; + // ZK: Gemini masking polynomial commitment (sent first, right after public inputs) + G1Point geminiMaskingPoly; + // Commitments to wire polynomials + G1Point w1; + G1Point w2; + G1Point w3; + G1Point w4; + // Commitments to logup witness polynomials + G1Point lookupReadCounts; + G1Point lookupReadTags; + G1Point lookupInverses; + // Commitment to grand permutation polynomial + G1Point zPerm; + G1Point[3] libraCommitments; + // Sumcheck + Fr libraSum; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates; + Fr libraEvaluation; + Fr[NUMBER_OF_ENTITIES_ZK] sumcheckEvaluations; // Includes gemini_masking_poly eval at index 0 (first position) + // Shplemini + G1Point[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms; + Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations; + Fr[4] libraPolyEvals; + G1Point shplonkQ; + G1Point kzgQuotient; + } +} + +// ZKTranscript library to generate fiat shamir challenges, the ZK transcript only differest +/// forge-lint: disable-next-item(pascal-case-struct) +struct ZKTranscript { + // Oink + Honk.RelationParameters relationParameters; + Fr[NUMBER_OF_ALPHAS] alphas; // Powers of alpha: [alpha, alpha^2, ..., alpha^(NUM_SUBRELATIONS-1)] + Fr[CONST_PROOF_SIZE_LOG_N] gateChallenges; + // Sumcheck + Fr libraChallenge; + Fr[CONST_PROOF_SIZE_LOG_N] sumCheckUChallenges; + // Shplemini + Fr rho; + Fr geminiR; + Fr shplonkNu; + Fr shplonkZ; + // Derived + Fr publicInputsDelta; +} + +library ZKTranscriptLib { + function generateTranscript( + Honk.ZKProof memory proof, + bytes32[] calldata publicInputs, + uint256 vkHash, + uint256 publicInputsSize, + uint256 logN + ) external pure returns (ZKTranscript memory t) { + Fr previousChallenge; + (t.relationParameters, previousChallenge) = + generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); + + (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); + + (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); + (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); + + (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); + + (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); + + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); + + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); + return t; + } + + function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { + uint256 challengeU256 = uint256(Fr.unwrap(challenge)); + // Split into two equal 127-bit chunks (254/2) + uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits + uint256 hi = challengeU256 >> 127; + first = FrLib.fromBytes32(bytes32(lo)); + second = FrLib.fromBytes32(bytes32(hi)); + } + + function generateRelationParametersChallenges( + Honk.ZKProof memory proof, + bytes32[] calldata publicInputs, + uint256 vkHash, + uint256 publicInputsSize, + Fr previousChallenge + ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { + (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = + generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + } + + function generateEtaChallenge( + Honk.ZKProof memory proof, + bytes32[] calldata publicInputs, + uint256 vkHash, + uint256 publicInputsSize + ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { + // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) + bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); + round0[0] = bytes32(vkHash); + + for (uint256 i = 0; i < publicInputsSize - PAIRING_POINTS_SIZE; i++) { + round0[1 + i] = bytes32(publicInputs[i]); + } + for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); + } + + // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) + round0[1 + publicInputsSize] = bytes32(proof.geminiMaskingPoly.x); + round0[1 + publicInputsSize + 1] = bytes32(proof.geminiMaskingPoly.y); + + // Create the first challenge + // Note: w4 is added to the challenge later on + round0[1 + publicInputsSize + 2] = bytes32(proof.w1.x); + round0[1 + publicInputsSize + 3] = bytes32(proof.w1.y); + round0[1 + publicInputsSize + 4] = bytes32(proof.w2.x); + round0[1 + publicInputsSize + 5] = bytes32(proof.w2.y); + round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); + round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); + + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); + (eta, etaTwo) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + + (etaThree,) = splitChallenge(previousChallenge); + } + + function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) + { + bytes32[7] memory round1; + round1[0] = FrLib.toBytes32(previousChallenge); + round1[1] = bytes32(proof.lookupReadCounts.x); + round1[2] = bytes32(proof.lookupReadCounts.y); + round1[3] = bytes32(proof.lookupReadTags.x); + round1[4] = bytes32(proof.lookupReadTags.y); + round1[5] = bytes32(proof.w4.x); + round1[6] = bytes32(proof.w4.y); + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); + (beta, gamma) = splitChallenge(nextPreviousChallenge); + } + + // Alpha challenges non-linearise the gate contributions + function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) + { + // Generate the original sumcheck alpha 0 by hashing zPerm and zLookup + uint256[5] memory alpha0; + alpha0[0] = Fr.unwrap(previousChallenge); + alpha0[1] = proof.lookupInverses.x; + alpha0[2] = proof.lookupInverses.y; + alpha0[3] = proof.zPerm.x; + alpha0[4] = proof.zPerm.y; + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); + Fr alpha; + (alpha,) = splitChallenge(nextPreviousChallenge); + + // Compute powers of alpha for batching subrelations + alphas[0] = alpha; + for (uint256 i = 1; i < NUMBER_OF_ALPHAS; i++) { + alphas[i] = alphas[i - 1] * alpha; + } + } + + function generateGateChallenges(Fr previousChallenge, uint256 logN) + internal + pure + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) + { + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + (gateChallenges[0],) = splitChallenge(previousChallenge); + for (uint256 i = 1; i < logN; i++) { + gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; + } + nextPreviousChallenge = previousChallenge; + } + + function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr libraChallenge, Fr nextPreviousChallenge) + { + // 2 comm, 1 sum, 1 challenge + uint256[4] memory challengeData; + challengeData[0] = Fr.unwrap(previousChallenge); + challengeData[1] = proof.libraCommitments[0].x; + challengeData[2] = proof.libraCommitments[0].y; + challengeData[3] = Fr.unwrap(proof.libraSum); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); + (libraChallenge,) = splitChallenge(nextPreviousChallenge); + } + + function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) + { + for (uint256 i = 0; i < logN; i++) { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; + univariateChal[0] = prevChallenge; + + for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { + univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; + } + prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); + + (sumcheckChallenges[i],) = splitChallenge(prevChallenge); + } + nextPreviousChallenge = prevChallenge; + } + + // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) + function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr rho, Fr nextPreviousChallenge) + { + uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; + rhoChallengeElements[0] = Fr.unwrap(prevChallenge); + uint256 i; + for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { + rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); + } + rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); + i += 1; + rhoChallengeElements[i] = proof.libraCommitments[1].x; + rhoChallengeElements[i + 1] = proof.libraCommitments[1].y; + i += 2; + rhoChallengeElements[i] = proof.libraCommitments[2].x; + rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); + (rho,) = splitChallenge(nextPreviousChallenge); + } + + function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr geminiR, Fr nextPreviousChallenge) + { + uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); + gR[0] = Fr.unwrap(prevChallenge); + + for (uint256 i = 0; i < logN - 1; i++) { + gR[1 + i * 2] = proof.geminiFoldComms[i].x; + gR[2 + i * 2] = proof.geminiFoldComms[i].y; + } + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); + + (geminiR,) = splitChallenge(nextPreviousChallenge); + } + + function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr shplonkNu, Fr nextPreviousChallenge) + { + uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); + shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); + + for (uint256 i = 1; i <= logN; i++) { + shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); + } + + uint256 libraIdx = 0; + for (uint256 i = logN + 1; i <= logN + 4; i++) { + shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); + libraIdx++; + } + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); + (shplonkNu,) = splitChallenge(nextPreviousChallenge); + } + + function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr shplonkZ, Fr nextPreviousChallenge) + { + uint256[3] memory shplonkZChallengeElements; + shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); + + shplonkZChallengeElements[1] = proof.shplonkQ.x; + shplonkZChallengeElements[2] = proof.shplonkQ.y; + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); + (shplonkZ,) = splitChallenge(nextPreviousChallenge); + } + + function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { + uint256 boundary = 0x0; + + // Pairing point object + for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { + p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + } + + // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) + p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + + // Commitments + p.w1 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.w2 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.w3 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + + // Lookup / Permutation Helper Commitments + p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + + p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + // Sumcheck univariates + for (uint256 i = 0; i < logN; i++) { + for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { + p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + } + } + + // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) + for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { + p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + } + + p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + + p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + + // Gemini + // Read gemini fold univariates + for (uint256 i = 0; i < logN - 1; i++) { + p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + } + + // Read gemini a evaluations + for (uint256 i = 0; i < logN; i++) { + p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + } + + for (uint256 i = 0; i < 4; i++) { + p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + } + + // Shplonk + p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + // KZG + p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + } +} + +// Field arithmetic libraries + +library RelationsLib { + Fr internal constant GRUMPKIN_CURVE_B_PARAMETER_NEGATED = Fr.wrap(17); // -(-17) + + function accumulateRelationEvaluations( + Fr[NUMBER_OF_ENTITIES] memory purportedEvaluations, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges, + Fr powPartialEval + ) internal pure returns (Fr accumulator) { + Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; + + // Accumulate all relations in Ultra Honk - each with varying number of subrelations + accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); + + // batch the subrelations with the precomputed alpha powers to obtain the full honk relation + accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); + } + + /** + * Aesthetic helper function that is used to index by enum into proof.sumcheckEvaluations, it avoids + * the relation checking code being cluttered with uint256 type casting, which is often a different colour in code + * editors, and thus is noisy. + */ + function wire(Fr[NUMBER_OF_ENTITIES] memory p, WIRE _wire) internal pure returns (Fr) { + return p[uint256(_wire)]; + } + + uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; + /** + * Ultra Arithmetic Relation + * + */ + + function accumulateArithmeticRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + // Relation 0 + Fr q_arith = wire(p, WIRE.Q_ARITH); + { + Fr neg_half = Fr.wrap(NEG_HALF_MODULO_P); + + Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half; + accum = accum + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + wire(p, WIRE.Q_C); + accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); + accum = accum * q_arith; + accum = accum * domainSep; + evals[0] = accum; + } + + // Relation 1 + { + Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); + accum = accum * (q_arith - Fr.wrap(2)); + accum = accum * (q_arith - ONE); + accum = accum * q_arith; + accum = accum * domainSep; + evals[1] = accum; + } + } + + function accumulatePermutationRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + Fr grand_product_numerator; + Fr grand_product_denominator; + + { + Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; + num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + + grand_product_numerator = num; + } + { + Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; + den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); + + grand_product_denominator = den; + } + + // Contribution 2 + { + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; + + acc = acc + - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) + * grand_product_denominator); + acc = acc * domainSep; + evals[2] = acc; + } + + // Contribution 3 + { + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + evals[3] = acc; + } + } + + function accumulateLogDerivativeLookupRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + Fr write_term; + Fr read_term; + + // Calculate the write term (the table accumulation) + { + write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); + } + + // Calculate the write term + { + Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); + + read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); + } + + Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; + Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; + + Fr inverse_exists_xor = + wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) + - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + + // Inverse calculated correctly relation + Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; + accumulatorNone = accumulatorNone * domainSep; + + // Inverse + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; + + Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); + + Fr read_tag_boolean_relation = read_tag * read_tag - read_tag; + + evals[4] = accumulatorNone; + evals[5] = accumulatorOne; + evals[6] = read_tag_boolean_relation * domainSep; + } + + function accumulateDeltaRangeRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + Fr minus_one = ZERO - ONE; + Fr minus_two = ZERO - Fr.wrap(2); + Fr minus_three = ZERO - Fr.wrap(3); + + // Compute wire differences + Fr delta_1 = wire(p, WIRE.W_R) - wire(p, WIRE.W_L); + Fr delta_2 = wire(p, WIRE.W_O) - wire(p, WIRE.W_R); + Fr delta_3 = wire(p, WIRE.W_4) - wire(p, WIRE.W_O); + Fr delta_4 = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_4); + + // Contribution 6 + { + Fr acc = delta_1; + acc = acc * (delta_1 + minus_one); + acc = acc * (delta_1 + minus_two); + acc = acc * (delta_1 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[7] = acc; + } + + // Contribution 7 + { + Fr acc = delta_2; + acc = acc * (delta_2 + minus_one); + acc = acc * (delta_2 + minus_two); + acc = acc * (delta_2 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[8] = acc; + } + + // Contribution 8 + { + Fr acc = delta_3; + acc = acc * (delta_3 + minus_one); + acc = acc * (delta_3 + minus_two); + acc = acc * (delta_3 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[9] = acc; + } + + // Contribution 9 + { + Fr acc = delta_4; + acc = acc * (delta_4 + minus_one); + acc = acc * (delta_4 + minus_two); + acc = acc * (delta_4 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[10] = acc; + } + } + + struct EllipticParams { + // Points + Fr x_1; + Fr y_1; + Fr x_2; + Fr y_2; + Fr y_3; + Fr x_3; + // push accumulators into memory + Fr x_double_identity; + } + + function accumulateEllipticRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + EllipticParams memory ep; + ep.x_1 = wire(p, WIRE.W_R); + ep.y_1 = wire(p, WIRE.W_O); + + ep.x_2 = wire(p, WIRE.W_L_SHIFT); + ep.y_2 = wire(p, WIRE.W_4_SHIFT); + ep.y_3 = wire(p, WIRE.W_O_SHIFT); + ep.x_3 = wire(p, WIRE.W_R_SHIFT); + + Fr q_sign = wire(p, WIRE.Q_L); + Fr q_is_double = wire(p, WIRE.Q_M); + + // Contribution 10 point addition, x-coordinate check + // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0 + Fr x_diff = (ep.x_2 - ep.x_1); + Fr y1_sqr = (ep.y_1 * ep.y_1); + { + // Move to top + Fr partialEval = domainSep; + + Fr y2_sqr = (ep.y_2 * ep.y_2); + Fr y1y2 = ep.y_1 * ep.y_2 * q_sign; + Fr x_add_identity = (ep.x_3 + ep.x_2 + ep.x_1); + x_add_identity = x_add_identity * x_diff * x_diff; + x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; + + evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + } + + // Contribution 11 point addition, x-coordinate check + // q_elliptic * (q_sign * y1 + y3)(x2 - x1) + (x3 - x1)(y2 - q_sign * y1) = 0 + { + Fr y1_plus_y3 = ep.y_1 + ep.y_3; + Fr y_diff = ep.y_2 * q_sign - ep.y_1; + Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; + evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + } + + // Contribution 10 point doubling, x-coordinate check + // (x3 + x1 + x1) (4y1*y1) - 9 * x1 * x1 * x1 * x1 = 0 + // N.B. we're using the equivalence x1*x1*x1 === y1*y1 - curve_b to reduce degree by 1 + { + Fr x_pow_4 = (y1_sqr + GRUMPKIN_CURVE_B_PARAMETER_NEGATED) * ep.x_1; + Fr y1_sqr_mul_4 = y1_sqr + y1_sqr; + y1_sqr_mul_4 = y1_sqr_mul_4 + y1_sqr_mul_4; + Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); + + // NOTE: pushed into memory (stack >:'( ) + ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + evals[11] = evals[11] + acc; + } + + // Contribution 11 point doubling, y-coordinate check + // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 + { + Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; + Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); + evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + } + } + + // Parameters used within the Memory Relation + // A struct is used to work around stack too deep. This relation has alot of variables + struct MemParams { + Fr memory_record_check; + Fr partial_record_check; + Fr next_gate_access_type; + Fr record_delta; + Fr index_delta; + Fr adjacent_values_match_if_adjacent_indices_match; + Fr adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation; + Fr access_check; + Fr next_gate_access_type_is_boolean; + Fr ROM_consistency_check_identity; + Fr RAM_consistency_check_identity; + Fr timestamp_delta; + Fr RAM_timestamp_check_identity; + Fr memory_identity; + Fr index_is_monotonically_increasing; + } + + function accumulateMemoryRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + MemParams memory ap; + + /** + * MEMORY + * + * A RAM memory record contains a tuple of the following fields: + * * i: `index` of memory cell being accessed + * * t: `timestamp` of memory cell being accessed (used for RAM, set to 0 for ROM) + * * v: `value` of memory cell being accessed + * * a: `access` type of record. read: 0 = read, 1 = write + * * r: `record` of memory cell. record = access + index * eta + timestamp * eta_two + value * eta_three + * + * A ROM memory record contains a tuple of the following fields: + * * i: `index` of memory cell being accessed + * * v: `value1` of memory cell being accessed (ROM tables can store up to 2 values per index) + * * v2:`value2` of memory cell being accessed (ROM tables can store up to 2 values per index) + * * r: `record` of memory cell. record = index * eta + value2 * eta_two + value1 * eta_three + * + * When performing a read/write access, the values of i, t, v, v2, a, r are stored in the following wires + + * selectors, depending on whether the gate is a RAM read/write or a ROM read + * + * | gate type | i | v2/t | v | a | r | + * | --------- | -- | ----- | -- | -- | -- | + * | ROM | w1 | w2 | w3 | -- | w4 | + * | RAM | w1 | w2 | w3 | qc | w4 | + * + * (for accesses where `index` is a circuit constant, it is assumed the circuit will apply a copy constraint on + * `w2` to fix its value) + * + * + */ + + /** + * Memory Record Check + * Partial degree: 1 + * Total degree: 4 + * + * A ROM/ROM access gate can be evaluated with the identity: + * + * qc + w1 \eta + w2 \eta_two + w3 \eta_three - w4 = 0 + * + * For ROM gates, qc = 0 + */ + ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); + ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 + ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); + + /** + * Contribution 13 & 14 + * ROM Consistency Check + * Partial degree: 1 + * Total degree: 4 + * + * For every ROM read, a set equivalence check is applied between the record witnesses, and a second set of + * records that are sorted. + * + * We apply the following checks for the sorted records: + * + * 1. w1, w2, w3 correctly map to 'index', 'v1, 'v2' for a given record value at w4 + * 2. index values for adjacent records are monotonically increasing + * 3. if, at gate i, index_i == index_{i + 1}, then value1_i == value1_{i + 1} and value2_i == value2_{i + 1} + * + */ + ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); + ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); + + ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 + + ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 + + evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + + ap.ROM_consistency_check_identity = ap.memory_record_check * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)); // deg 3 or 7 + + /** + * Contributions 15,16,17 + * RAM Consistency Check + * + * The 'access' type of the record is extracted with the expression `w_4 - ap.partial_record_check` + * (i.e. for an honest Prover `w1 * eta + w2 * eta^2 + w3 * eta^3 - w4 = access`. + * This is validated by requiring `access` to be boolean + * + * For two adjacent entries in the sorted list if _both_ + * A) index values match + * B) adjacent access value is 0 (i.e. next gate is a READ) + * then + * C) both values must match. + * The gate boolean check is + * (A && B) => C === !(A && B) || C === !A || !B || C + * + * N.B. it is the responsibility of the circuit writer to ensure that every RAM cell is initialized + * with a WRITE operation. + */ + Fr access_type = (wire(p, WIRE.W_4) - ap.partial_record_check); // will be 0 or 1 for honest Prover; deg 1 or 4 + ap.access_check = access_type * (access_type - Fr.wrap(1)); // check value is 0 or 1; deg 2 or 8 + + // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta + // deg 1 or 4 + ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; + + Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); + ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + + // We can't apply the RAM consistency check identity on the final entry in the sorted list (the wires in the + // next gate would make the identity fail). We need to validate that its 'access type' bool is correct. Can't + // do with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access + // type is correct, to cover this edge case + // deg 2 or 4 + ap.next_gate_access_type_is_boolean = + ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; + + // Putting it all together... + evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation + * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 + + /** + * RAM Timestamp Consistency Check + * + * | w1 | w2 | w3 | w4 | + * | index | timestamp | timestamp_check | -- | + * + * Let delta_index = index_{i + 1} - index_{i} + * + * Iff delta_index == 0, timestamp_check = timestamp_{i + 1} - timestamp_i + * Else timestamp_check = 0 + */ + ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); + ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 + + /** + * Complete Contribution 12 + * The complete RAM/ROM memory identity + * Partial degree: + */ + ap.memory_identity = ap.ROM_consistency_check_identity; // deg 3 or 6 + ap.memory_identity = + ap.memory_identity + ap.RAM_timestamp_check_identity * (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9 + + // (deg 3 or 9) + (deg 4) + (deg 3) + ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + evals[13] = ap.memory_identity; + } + + // Constants for the Non-native Field relation + Fr constant LIMB_SIZE = Fr.wrap(uint256(1) << 68); + Fr constant SUBLIMB_SHIFT = Fr.wrap(uint256(1) << 14); + + // Parameters used within the Non-Native Field Relation + // A struct is used to work around stack too deep. This relation has alot of variables + struct NnfParams { + Fr limb_subproduct; + Fr non_native_field_gate_1; + Fr non_native_field_gate_2; + Fr non_native_field_gate_3; + Fr limb_accumulator_1; + Fr limb_accumulator_2; + Fr nnf_identity; + } + + function accumulateNnfRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + NnfParams memory ap; + + /** + * Contribution 12 + * Non native field arithmetic gate 2 + * deg 4 + * + * _ _ + * / _ _ _ 14 \ + * q_2 . q_4 | (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2 - w_3 - w_4 | + * \_ _/ + * + * + */ + ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); + ap.non_native_field_gate_2 = + (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); + + ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; + ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.non_native_field_gate_1 = ap.limb_subproduct; + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); + + ap.non_native_field_gate_3 = ap.limb_subproduct; + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); + + Fr non_native_field_identity = + ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; + non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); + + // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm + // deg 2 + ap.limb_accumulator_1 = wire(p, WIRE.W_R_SHIFT) * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L_SHIFT); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_O); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_R); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L); + ap.limb_accumulator_1 = ap.limb_accumulator_1 - wire(p, WIRE.W_4); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * wire(p, WIRE.Q_4); + + // ((((w3' * 2^14 + w2') * 2^14 + w1') * 2^14 + w4) * 2^14 + w3 - w4') * qm + // deg 2 + ap.limb_accumulator_2 = wire(p, WIRE.W_O_SHIFT) * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_R_SHIFT); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_L_SHIFT); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_4); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_O); + ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); + + Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; + limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 + + ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; + ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); + evals[19] = ap.nnf_identity; + } + + struct PoseidonExternalParams { + Fr s1; + Fr s2; + Fr s3; + Fr s4; + Fr u1; + Fr u2; + Fr u3; + Fr u4; + Fr t0; + Fr t1; + Fr t2; + Fr t3; + Fr v1; + Fr v2; + Fr v3; + Fr v4; + Fr q_pos_by_scaling; + } + + function accumulatePoseidonExternalRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + PoseidonExternalParams memory ep; + + ep.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L); + ep.s2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_R); + ep.s3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_O); + ep.s4 = wire(p, WIRE.W_4) + wire(p, WIRE.Q_4); + + ep.u1 = ep.s1 * ep.s1 * ep.s1 * ep.s1 * ep.s1; + ep.u2 = ep.s2 * ep.s2 * ep.s2 * ep.s2 * ep.s2; + ep.u3 = ep.s3 * ep.s3 * ep.s3 * ep.s3 * ep.s3; + ep.u4 = ep.s4 * ep.s4 * ep.s4 * ep.s4 * ep.s4; + // matrix mul v = M_E * u with 14 additions + ep.t0 = ep.u1 + ep.u2; // u_1 + u_2 + ep.t1 = ep.u3 + ep.u4; // u_3 + u_4 + ep.t2 = ep.u2 + ep.u2 + ep.t1; // 2u_2 + // ep.t2 += ep.t1; // 2u_2 + u_3 + u_4 + ep.t3 = ep.u4 + ep.u4 + ep.t0; // 2u_4 + // ep.t3 += ep.t0; // u_1 + u_2 + 2u_4 + ep.v4 = ep.t1 + ep.t1; + ep.v4 = ep.v4 + ep.v4 + ep.t3; + // ep.v4 += ep.t3; // u_1 + u_2 + 4u_3 + 6u_4 + ep.v2 = ep.t0 + ep.t0; + ep.v2 = ep.v2 + ep.v2 + ep.t2; + // ep.v2 += ep.t2; // 4u_1 + 6u_2 + u_3 + u_4 + ep.v1 = ep.t3 + ep.v2; // 5u_1 + 7u_2 + u_3 + 3u_4 + ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 + + ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; + evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + + evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + + evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + + evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + } + + struct PoseidonInternalParams { + Fr u1; + Fr u2; + Fr u3; + Fr u4; + Fr u_sum; + Fr v1; + Fr v2; + Fr v3; + Fr v4; + Fr s1; + Fr q_pos_by_scaling; + } + + function accumulatePoseidonInternalRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + PoseidonInternalParams memory ip; + + Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ + FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), + FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), + FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), + FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) + ]; + + // add round constants + ip.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L); + + // apply s-box round + ip.u1 = ip.s1 * ip.s1 * ip.s1 * ip.s1 * ip.s1; + ip.u2 = wire(p, WIRE.W_R); + ip.u3 = wire(p, WIRE.W_O); + ip.u4 = wire(p, WIRE.W_4); + + // matrix mul with v = M_I * u 4 muls and 7 additions + ip.u_sum = ip.u1 + ip.u2 + ip.u3 + ip.u4; + + ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; + + ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; + evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + + ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; + evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + + ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; + evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + + ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; + evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + } + + // Batch subrelation evaluations using precomputed powers of alpha + // First subrelation is implicitly scaled by 1, subsequent ones use powers from the subrelationChallenges array + function scaleAndBatchSubrelations( + Fr[NUMBER_OF_SUBRELATIONS] memory evaluations, + Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges + ) internal pure returns (Fr accumulator) { + accumulator = evaluations[0]; + + for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { + accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; + } + } +} + +// Field arithmetic libraries - prevent littering the code with modmul / addmul + +library CommitmentSchemeLib { + using FrLib for Fr; + + // Avoid stack too deep + struct ShpleminiIntermediates { + Fr unshiftedScalar; + Fr shiftedScalar; + Fr unshiftedScalarNeg; + Fr shiftedScalarNeg; + // Scalar to be multiplied by [1]₁ + Fr constantTermAccumulator; + // Accumulator for powers of rho + Fr batchingChallenge; + // Linear combination of multilinear (sumcheck) evaluations and powers of rho + Fr batchedEvaluation; + Fr[4] denominators; + Fr[4] batchingScalars; + // 1/(z - r^{2^i}) for i = 0, ..., logSize, dynamically updated + Fr posInvertedDenominator; + // 1/(z + r^{2^i}) for i = 0, ..., logSize, dynamically updated + Fr negInvertedDenominator; + // ν^{2i} * 1/(z - r^{2^i}) + Fr scalingFactorPos; + // ν^{2i+1} * 1/(z + r^{2^i}) + Fr scalingFactorNeg; + // Fold_i(r^{2^i}) reconstructed by Verifier + Fr[] foldPosEvaluations; + } + + function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { + Fr[] memory squares = new Fr[](logN); + squares[0] = r; + for (uint256 i = 1; i < logN; ++i) { + squares[i] = squares[i - 1].sqr(); + } + return squares; + } + // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., m-1 + + function computeFoldPosEvaluations( + Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckUChallenges, + Fr batchedEvalAccumulator, + Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvaluations, + Fr[] memory geminiEvalChallengePowers, + uint256 logSize + ) internal view returns (Fr[] memory) { + Fr[] memory foldPosEvaluations = new Fr[](logSize); + for (uint256 i = logSize; i > 0; --i) { + Fr challengePower = geminiEvalChallengePowers[i - 1]; + Fr u = sumcheckUChallenges[i - 1]; + + Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] + * (challengePower * (ONE - u) - u)); + // Divide by the denominator + batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); + + batchedEvalAccumulator = batchedEvalRoundAcc; + foldPosEvaluations[i - 1] = batchedEvalRoundAcc; + } + return foldPosEvaluations; + } +} + +uint256 constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; // EC group order. F_q + +function bytes32ToString(bytes32 value) pure returns (string memory result) { + bytes memory alphabet = "0123456789abcdef"; + + bytes memory str = new bytes(66); + str[0] = "0"; + str[1] = "x"; + for (uint256 i = 0; i < 32; i++) { + str[2 + i * 2] = alphabet[uint8(value[i] >> 4)]; + str[3 + i * 2] = alphabet[uint8(value[i] & 0x0f)]; + } + result = string(str); +} + +// Fr utility + +function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { + scalar = FrLib.fromBytes32(bytes32(proofSection)); +} + +// EC Point utilities +function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { + point = Honk.G1Point({ + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q + }); +} + +function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { + point.y = (Q - point.y) % Q; + return point; +} + +/** + * Convert the pairing points to G1 points. + * + * The pairing points are serialised as an array of 68 bit limbs representing two points + * The lhs of a pairing operation and the rhs of a pairing operation + * + * There are 4 fields for each group element, leaving 8 fields for each side of the pairing. + * + * @param pairingPoints The pairing points to convert. + * @return lhs + * @return rhs + */ +function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) + pure + returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) +{ + uint256 lhsX = Fr.unwrap(pairingPoints[0]); + lhsX |= Fr.unwrap(pairingPoints[1]) << 68; + lhsX |= Fr.unwrap(pairingPoints[2]) << 136; + lhsX |= Fr.unwrap(pairingPoints[3]) << 204; + lhs.x = lhsX; + + uint256 lhsY = Fr.unwrap(pairingPoints[4]); + lhsY |= Fr.unwrap(pairingPoints[5]) << 68; + lhsY |= Fr.unwrap(pairingPoints[6]) << 136; + lhsY |= Fr.unwrap(pairingPoints[7]) << 204; + lhs.y = lhsY; + + uint256 rhsX = Fr.unwrap(pairingPoints[8]); + rhsX |= Fr.unwrap(pairingPoints[9]) << 68; + rhsX |= Fr.unwrap(pairingPoints[10]) << 136; + rhsX |= Fr.unwrap(pairingPoints[11]) << 204; + rhs.x = rhsX; + + uint256 rhsY = Fr.unwrap(pairingPoints[12]); + rhsY |= Fr.unwrap(pairingPoints[13]) << 68; + rhsY |= Fr.unwrap(pairingPoints[14]) << 136; + rhsY |= Fr.unwrap(pairingPoints[15]) << 204; + rhs.y = rhsY; +} + +/** + * Hash the pairing inputs from the present verification context with those extracted from the public inputs. + * + * @param proofPairingPoints Pairing points from the proof - (public inputs). + * @param accLhs Accumulator point for the left side - result of shplemini. + * @param accRhs Accumulator point for the right side - result of shplemini. + * @return recursionSeparator The recursion separator - generated from hashing the above. + */ +function generateRecursionSeparator( + Fr[PAIRING_POINTS_SIZE] memory proofPairingPoints, + Honk.G1Point memory accLhs, + Honk.G1Point memory accRhs +) pure returns (Fr recursionSeparator) { + // hash the proof aggregated X + // hash the proof aggregated Y + // hash the accum X + // hash the accum Y + + (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); + + uint256[8] memory recursionSeparatorElements; + + // Proof points + recursionSeparatorElements[0] = proofLhs.x; + recursionSeparatorElements[1] = proofLhs.y; + recursionSeparatorElements[2] = proofRhs.x; + recursionSeparatorElements[3] = proofRhs.y; + + // Accumulator points + recursionSeparatorElements[4] = accLhs.x; + recursionSeparatorElements[5] = accLhs.y; + recursionSeparatorElements[6] = accRhs.x; + recursionSeparatorElements[7] = accRhs.y; + + recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); +} + +/** + * G1 Mul with Separator + * Using the ecAdd and ecMul precompiles + * + * @param basePoint The point to multiply. + * @param other The other point to add. + * @param recursionSeperator The separator to use for the multiplication. + * @return `(recursionSeperator * basePoint) + other`. + */ +function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) + view + returns (Honk.G1Point memory) +{ + Honk.G1Point memory result; + + result = ecMul(recursionSeperator, basePoint); + result = ecAdd(result, other); + + return result; +} + +/** + * G1 Mul + * Takes a Fr value and a G1 point and uses the ecMul precompile to return the result. + * + * @param value The value to multiply the point by. + * @param point The point to multiply. + * @return result The result of the multiplication. + */ +function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { + Honk.G1Point memory result; + + assembly { + let free := mload(0x40) + // Write the point into memory (two 32 byte words) + // Memory layout: + // Address | value + // free | point.x + // free + 0x20| point.y + mstore(free, mload(point)) + mstore(add(free, 0x20), mload(add(point, 0x20))) + // Write the scalar into memory (one 32 byte word) + // Memory layout: + // Address | value + // free + 0x40| value + mstore(add(free, 0x40), value) + + // Call the ecMul precompile, it takes in the following + // [point.x, point.y, scalar], and returns the result back into the free memory location. + let success := staticcall(gas(), 0x07, free, 0x60, free, 0x40) + if iszero(success) { + revert(0, 0) + } + // Copy the result of the multiplication back into the result memory location. + // Memory layout: + // Address | value + // result | result.x + // result + 0x20| result.y + mstore(result, mload(free)) + mstore(add(result, 0x20), mload(add(free, 0x20))) + + mstore(0x40, add(free, 0x60)) + } + + return result; +} + +/** + * G1 Add + * Takes two G1 points and uses the ecAdd precompile to return the result. + * + * @param lhs The left hand side of the addition. + * @param rhs The right hand side of the addition. + * @return result The result of the addition. + */ +function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { + Honk.G1Point memory result; + + assembly { + let free := mload(0x40) + // Write lhs into memory (two 32 byte words) + // Memory layout: + // Address | value + // free | lhs.x + // free + 0x20| lhs.y + mstore(free, mload(lhs)) + mstore(add(free, 0x20), mload(add(lhs, 0x20))) + + // Write rhs into memory (two 32 byte words) + // Memory layout: + // Address | value + // free + 0x40| rhs.x + // free + 0x60| rhs.y + mstore(add(free, 0x40), mload(rhs)) + mstore(add(free, 0x60), mload(add(rhs, 0x20))) + + // Call the ecAdd precompile, it takes in the following + // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. + let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) + if iszero(success) { revert(0, 0) } + + // Copy the result of the addition back into the result memory location. + // Memory layout: + // Address | value + // result | result.x + // result + 0x20| result.y + mstore(result, mload(free)) + mstore(add(result, 0x20), mload(add(free, 0x20))) + + mstore(0x40, add(free, 0x80)) + } + + return result; +} + +function validateOnCurve(Honk.G1Point memory point) pure { + uint256 x = point.x; + uint256 y = point.y; + + bool success = false; + assembly { + let xx := mulmod(x, x, Q) + success := eq(mulmod(y, y, Q), addmod(mulmod(x, xx, Q), 3, Q)) + } + + require(success, "point is not on the curve"); +} + +function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { + bytes memory input = abi.encodePacked( + rhs.x, + rhs.y, + // Fixed G2 point + uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), + uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), + uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), + uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), + lhs.x, + lhs.y, + // G2 point from VK + uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), + uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), + uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), + uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) + ); + + (bool success, bytes memory result) = address(0x08).staticcall(input); + decodedResult = success && abi.decode(result, (bool)); +} + +// Field arithmetic libraries - prevent littering the code with modmul / addmul + + + + +abstract contract BaseZKHonkVerifier is IVerifier { + using FrLib for Fr; + + uint256 immutable $N; + uint256 immutable $LOG_N; + uint256 immutable $VK_HASH; + uint256 immutable $NUM_PUBLIC_INPUTS; + uint256 immutable $MSMSize; + + constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { + $N = _N; + $LOG_N = _logN; + $VK_HASH = _vkHash; + $NUM_PUBLIC_INPUTS = _numPublicInputs; + $MSMSize = NUMBER_UNSHIFTED_ZK + _logN + LIBRA_COMMITMENTS + 2; + } + + // Errors + error ProofLengthWrong(); + error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); + error PublicInputsLengthWrong(); + error SumcheckFailed(); + error ShpleminiFailed(); + error GeminiChallengeInSubgroup(); + error ConsistencyCheckFailed(); + + // Constants for proof length calculation (matching UltraKeccakZKFlavor) + uint256 constant NUM_WITNESS_ENTITIES = 8 + NUM_MASKING_POLYNOMIALS; + uint256 constant NUM_ELEMENTS_COMM = 2; // uint256 elements for curve points + uint256 constant NUM_ELEMENTS_FR = 1; // uint256 elements for field elements + uint256 constant NUM_LIBRA_EVALUATIONS = 4; // libra evaluations + + // Calculate proof size based on log_n (matching UltraKeccakZKFlavor formula) + function calculateProofSize(uint256 logN) internal pure returns (uint256) { + // Witness and Libra commitments + uint256 proofLength = NUM_WITNESS_ENTITIES * NUM_ELEMENTS_COMM; // witness commitments + proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking + + // Sumcheck + proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations + + // Libra and Gemini + proofLength += NUM_ELEMENTS_FR * 2; // Libra sum, claimed eval + proofLength += logN * NUM_ELEMENTS_FR; // Gemini a evaluations + proofLength += NUM_LIBRA_EVALUATIONS * NUM_ELEMENTS_FR; // libra evaluations + + // PCS commitments + proofLength += (logN - 1) * NUM_ELEMENTS_COMM; // Gemini Fold commitments + proofLength += NUM_ELEMENTS_COMM * 2; // Shplonk Q and KZG W commitments + + // Pairing points + proofLength += PAIRING_POINTS_SIZE; // pairing inputs carried on public inputs + + return proofLength; + } + + uint256 constant SHIFTED_COMMITMENTS_START = 30; + + function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); + + function verify(bytes calldata proof, bytes32[] calldata publicInputs) + public + view + override + returns (bool verified) + { + // Calculate expected proof size based on $LOG_N + uint256 expectedProofSize = calculateProofSize($LOG_N); + + // Check the received proof is the expected size where each field element is 32 bytes + if (proof.length != expectedProofSize * 32) { + revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); + } + + Honk.VerificationKey memory vk = loadVerificationKey(); + Honk.ZKProof memory p = ZKTranscriptLib.loadProof(proof, $LOG_N); + + if (publicInputs.length != vk.publicInputsSize - PAIRING_POINTS_SIZE) { + revert PublicInputsLengthWrong(); + } + + // Generate the fiat shamir challenges for the whole protocol + ZKTranscript memory t = + ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); + + // Derive public input delta + t.relationParameters.publicInputsDelta = computePublicInputDelta( + publicInputs, + p.pairingPointObject, + t.relationParameters.beta, + t.relationParameters.gamma, /*pubInputsOffset=*/ + 1 + ); + + // Sumcheck + if (!verifySumcheck(p, t)) revert SumcheckFailed(); + + if (!verifyShplemini(p, vk, t)) revert ShpleminiFailed(); + + verified = true; + } + + uint256 constant PERMUTATION_ARGUMENT_VALUE_SEPARATOR = 1 << 28; + + function computePublicInputDelta( + bytes32[] memory publicInputs, + Fr[PAIRING_POINTS_SIZE] memory pairingPointObject, + Fr beta, + Fr gamma, + uint256 offset + ) internal view returns (Fr publicInputDelta) { + Fr numerator = Fr.wrap(1); + Fr denominator = Fr.wrap(1); + + Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); + + { + for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { + Fr pubInput = FrLib.fromBytes32(publicInputs[i]); + + numerator = numerator * (numeratorAcc + pubInput); + denominator = denominator * (denominatorAcc + pubInput); + + numeratorAcc = numeratorAcc + beta; + denominatorAcc = denominatorAcc - beta; + } + + for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { + Fr pubInput = pairingPointObject[i]; + + numerator = numerator * (numeratorAcc + pubInput); + denominator = denominator * (denominatorAcc + pubInput); + + numeratorAcc = numeratorAcc + beta; + denominatorAcc = denominatorAcc - beta; + } + } + + // Fr delta = numerator / denominator; // TOOO: batch invert later? + publicInputDelta = FrLib.div(numerator, denominator); + } + + function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { + Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 + Fr powPartialEvaluation = Fr.wrap(1); + + // We perform sumcheck reductions over log n rounds ( the multivariate degree ) + for (uint256 round; round < $LOG_N; ++round) { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; + if (totalSum != roundTargetSum) revert SumcheckFailed(); + + Fr roundChallenge = tp.sumCheckUChallenges[round]; + + // Update the round target for the next rounf + roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); + powPartialEvaluation = + powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); + } + + // Last round + // For ZK flavors: sumcheckEvaluations has 42 elements + // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations + Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; + for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { + relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 + } + Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( + relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation + ); + + Fr evaluation = Fr.wrap(1); + for (uint256 i = 2; i < $LOG_N; i++) { + evaluation = evaluation * tp.sumCheckUChallenges[i]; + } + + grandHonkRelationSum = + grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; + verified = (grandHonkRelationSum == roundTargetSum); + } + + // Return the new target sum for the next sumcheck round + function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) + internal + view + returns (Fr targetSum) + { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) + ]; + + // To compute the next target sum, we evaluate the given univariate at a point u (challenge). + + // Performing Barycentric evaluations + // Compute B(x) + Fr numeratorValue = Fr.wrap(1); + for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { + numeratorValue = numeratorValue * (roundChallenge - Fr.wrap(i)); + } + + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; + for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { + denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); + } + + for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { + targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; + } + + // Scale the sum by the value of B(x) + targetSum = targetSum * numeratorValue; + } + + uint256 constant LIBRA_COMMITMENTS = 3; + uint256 constant LIBRA_EVALUATIONS = 4; + uint256 constant LIBRA_UNIVARIATES_LENGTH = 9; + + struct PairingInputs { + Honk.G1Point P_0; + Honk.G1Point P_1; + } + + function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) + internal + view + returns (bool verified) + { + CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack + + // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); + // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings + Fr[] memory scalars = new Fr[]($MSMSize); + Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); + + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); + + mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); + mem.shiftedScalar = + tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); + + scalars[0] = Fr.wrap(1); + commitments[0] = proof.shplonkQ; + + /* Batch multivariate opening claims, shifted and unshifted + * The vector of scalars is populated as follows: + * \f[ + * \left( + * - \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right), + * \ldots, + * - \rho^{i+k-1} \times \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right), + * - \rho^{i+k} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right), + * \ldots, + * - \rho^{k+m-1} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right) + * \right) + * \f] + * + * The following vector is concatenated to the vector of commitments: + * \f[ + * f_0, \ldots, f_{m-1}, f_{\text{shift}, 0}, \ldots, f_{\text{shift}, k-1} + * \f] + * + * Simultaneously, the evaluation of the multilinear polynomial + * \f[ + * \sum \rho^i \cdot f_i + \sum \rho^{i+k} \cdot f_{\text{shift}, i} + * \f] + * at the challenge point \f$ (u_0,\ldots, u_{n-1}) \f$ is computed. + * + * This approach minimizes the number of iterations over the commitments to multilinear polynomials + * and eliminates the need to store the powers of \f$ \rho \f$. + */ + // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] + // Start batching challenge at 1, not rho, to match non-ZK pattern + mem.batchingChallenge = Fr.wrap(1); + mem.batchedEvaluation = Fr.wrap(0); + + mem.unshiftedScalarNeg = mem.unshiftedScalar.neg(); + mem.shiftedScalarNeg = mem.shiftedScalar.neg(); + + // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) + for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { + scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; + mem.batchedEvaluation = mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); + mem.batchingChallenge = mem.batchingChallenge * tp.rho; + } + // g commitments are accumulated at r + // For each of the to be shifted commitments perform the shift in place by + // adding to the unshifted value. + // We do so, as the values are to be used in batchMul later, and as + // `a * c + b * c = (a + b) * c` this will allow us to reduce memory and compute. + // Applied to w1, w2, w3, w4 and zPerm + for (uint256 i = 0; i < NUMBER_TO_BE_SHIFTED; ++i) { + uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; + uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; + + scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); + mem.batchedEvaluation = + mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); + mem.batchingChallenge = mem.batchingChallenge * tp.rho; + } + + commitments[1] = proof.geminiMaskingPoly; + + commitments[2] = vk.qm; + commitments[3] = vk.qc; + commitments[4] = vk.ql; + commitments[5] = vk.qr; + commitments[6] = vk.qo; + commitments[7] = vk.q4; + commitments[8] = vk.qLookup; + commitments[9] = vk.qArith; + commitments[10] = vk.qDeltaRange; + commitments[11] = vk.qElliptic; + commitments[12] = vk.qMemory; + commitments[13] = vk.qNnf; + commitments[14] = vk.qPoseidon2External; + commitments[15] = vk.qPoseidon2Internal; + commitments[16] = vk.s1; + commitments[17] = vk.s2; + commitments[18] = vk.s3; + commitments[19] = vk.s4; + commitments[20] = vk.id1; + commitments[21] = vk.id2; + commitments[22] = vk.id3; + commitments[23] = vk.id4; + commitments[24] = vk.t1; + commitments[25] = vk.t2; + commitments[26] = vk.t3; + commitments[27] = vk.t4; + commitments[28] = vk.lagrangeFirst; + commitments[29] = vk.lagrangeLast; + + // Accumulate proof points + commitments[30] = proof.w1; + commitments[31] = proof.w2; + commitments[32] = proof.w3; + commitments[33] = proof.w4; + commitments[34] = proof.zPerm; + commitments[35] = proof.lookupInverses; + commitments[36] = proof.lookupReadCounts; + commitments[37] = proof.lookupReadTags; + + /* Batch gemini claims from the prover + * place the commitments to gemini aᵢ to the vector of commitments, compute the contributions from + * aᵢ(−r²ⁱ) for i=1, … , n−1 to the constant term accumulator, add corresponding scalars + * + * 1. Moves the vector + * \f[ + * \left( \text{com}(A_1), \text{com}(A_2), \ldots, \text{com}(A_{n-1}) \right) + * \f] + * to the 'commitments' vector. + * + * 2. Computes the scalars: + * \f[ + * \frac{\nu^{2}}{z + r^2}, \frac{\nu^3}{z + r^4}, \ldots, \frac{\nu^{n-1}}{z + r^{2^{n-1}}} + * \f] + * and places them into the 'scalars' vector. + * + * 3. Accumulates the summands of the constant term: + * \f[ + * \sum_{i=2}^{n-1} \frac{\nu^{i} \cdot A_i(-r^{2^i})}{z + r^{2^i}} + * \f] + * and adds them to the 'constant_term_accumulator'. + */ + + // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: + // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 + Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); + + mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; + mem.constantTermAccumulator = + mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); + + mem.batchingChallenge = tp.shplonkNu.sqr(); + uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; + + // Compute Shplonk constant term contributions from Aₗ(± r^{2ˡ}) for l = 1, ..., m-1; + // Compute scalar multipliers for each fold commitment + for (uint256 i = 0; i < $LOG_N - 1; ++i) { + bool dummy_round = i >= ($LOG_N - 1); + + if (!dummy_round) { + // Update inverted denominators + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); + + // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] + mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; + mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; + scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); + + // Accumulate the const term contribution given by + // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) + Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; + accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; + } + // Update the running power of v + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + + commitments[boundary + i] = proof.geminiFoldComms[i]; + } + + boundary += $LOG_N - 1; + + // Finalize the batch opening claim + mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); + mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); + mem.denominators[2] = mem.denominators[0]; + mem.denominators[3] = mem.denominators[0]; + + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { + Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; + mem.batchingScalars[i] = scalingFactor.neg(); + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; + mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; + } + scalars[boundary] = mem.batchingScalars[0]; + scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; + scalars[boundary + 2] = mem.batchingScalars[3]; + + for (uint256 i = 0; i < LIBRA_COMMITMENTS; i++) { + commitments[boundary++] = proof.libraCommitments[i]; + } + + commitments[boundary] = Honk.G1Point({x: 1, y: 2}); + scalars[boundary++] = mem.constantTermAccumulator; + + if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { + revert ConsistencyCheckFailed(); + } + + Honk.G1Point memory quotient_commitment = proof.kzgQuotient; + + commitments[boundary] = quotient_commitment; + scalars[boundary] = tp.shplonkZ; // evaluation challenge + + PairingInputs memory pair; + pair.P_0 = batchMul(commitments, scalars); + pair.P_1 = negateInplace(quotient_commitment); + + // Aggregate pairing points + Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); + (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = + convertPairingPointsToG1(proof.pairingPointObject); + + // Validate the points from the proof are on the curve + validateOnCurve(P_0_other); + validateOnCurve(P_1_other); + + // accumulate with aggregate points in proof + pair.P_0 = mulWithSeperator(pair.P_0, P_0_other, recursionSeparator); + pair.P_1 = mulWithSeperator(pair.P_1, P_1_other, recursionSeparator); + + return pairing(pair.P_0, pair.P_1); + } + + struct SmallSubgroupIpaIntermediates { + Fr[SUBGROUP_SIZE] challengePolyLagrange; + Fr challengePolyEval; + Fr lagrangeFirst; + Fr lagrangeLast; + Fr rootPower; + Fr[SUBGROUP_SIZE] denominators; // this has to disappear + Fr diff; + } + + function checkEvalsConsistency( + Fr[LIBRA_EVALUATIONS] memory libraPolyEvals, + Fr geminiR, + Fr[CONST_PROOF_SIZE_LOG_N] memory uChallenges, + Fr libraEval + ) internal view returns (bool check) { + Fr one = Fr.wrap(1); + Fr vanishingPolyEval = geminiR.pow(SUBGROUP_SIZE) - one; + if (vanishingPolyEval == Fr.wrap(0)) { + revert GeminiChallengeInSubgroup(); + } + + SmallSubgroupIpaIntermediates memory mem; + mem.challengePolyLagrange[0] = one; + for (uint256 round = 0; round < $LOG_N; round++) { + uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; + mem.challengePolyLagrange[currIdx] = one; + for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { + mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; + } + } + + mem.rootPower = one; + mem.challengePolyEval = Fr.wrap(0); + for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { + mem.denominators[idx] = mem.rootPower * geminiR - one; + mem.denominators[idx] = mem.denominators[idx].invert(); + mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; + mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; + } + + Fr numerator = vanishingPolyEval * Fr.wrap(SUBGROUP_SIZE).invert(); + mem.challengePolyEval = mem.challengePolyEval * numerator; + mem.lagrangeFirst = mem.denominators[0] * numerator; + mem.lagrangeLast = mem.denominators[SUBGROUP_SIZE - 1] * numerator; + + mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; + + mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) + * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); + mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; + + check = mem.diff == Fr.wrap(0); + } + + // This implementation is the same as above with different constants + function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) + internal + view + returns (Honk.G1Point memory result) + { + uint256 limit = $MSMSize; + + // Validate all points are on the curve + for (uint256 i = 0; i < limit; ++i) { + validateOnCurve(base[i]); + } + + bool success = true; + assembly { + let free := mload(0x40) + + let count := 0x01 + for {} lt(count, add(limit, 1)) { count := add(count, 1) } { + // Get loop offsets + let base_base := add(base, mul(count, 0x20)) + let scalar_base := add(scalars, mul(count, 0x20)) + + mstore(add(free, 0x40), mload(mload(base_base))) + mstore(add(free, 0x60), mload(add(0x20, mload(base_base)))) + // Add scalar + mstore(add(free, 0x80), mload(scalar_base)) + + success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) + } + + // Return the result + mstore(result, mload(free)) + mstore(add(result, 0x20), mload(add(free, 0x20))) + } + + require(success, ShpleminiFailed()); + } +} + +contract ThresholdPkAggregationVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { + function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { + return HonkVerificationKey.loadVerificationKey(); + } +} diff --git a/packages/enclave-contracts/deployed_contracts.json b/packages/enclave-contracts/deployed_contracts.json index 48454f75b3..f54919ff16 100644 --- a/packages/enclave-contracts/deployed_contracts.json +++ b/packages/enclave-contracts/deployed_contracts.json @@ -125,10 +125,6 @@ "ZKTranscriptLib": { "blockNumber": 10395628, "address": "0xBf0A32A7D546944561a8773628e6c0036C16354C" - }, - "DkgPkVerifier": { - "blockNumber": 10395629, - "address": "0x6997eCF2926a94c439599f7275219Cb2BfBA300C" } }, "localhost": { diff --git a/packages/enclave-contracts/ignition/modules/recursiveAggregationFoldVerifier.ts b/packages/enclave-contracts/ignition/modules/recursiveAggregationFoldVerifier.ts new file mode 100644 index 0000000000..dafc719d49 --- /dev/null +++ b/packages/enclave-contracts/ignition/modules/recursiveAggregationFoldVerifier.ts @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; + +export default buildModule("RecursiveAggregationFoldVerifier", (m) => { + const recursiveAggregationFoldVerifier = m.contract("RecursiveAggregationFoldVerifier"); + + return { recursiveAggregationFoldVerifier }; +}) as any; diff --git a/packages/enclave-contracts/ignition/modules/thresholdDecryptedSharesAggregationBnVerifier.ts b/packages/enclave-contracts/ignition/modules/thresholdDecryptedSharesAggregationBnVerifier.ts new file mode 100644 index 0000000000..5ec1c542a8 --- /dev/null +++ b/packages/enclave-contracts/ignition/modules/thresholdDecryptedSharesAggregationBnVerifier.ts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; + +export default buildModule("ThresholdDecryptedSharesAggregationBnVerifier", (m) => { + const thresholdDecryptedSharesAggregationBnVerifier = m.contract( + "ThresholdDecryptedSharesAggregationBnVerifier", + ); + + return { thresholdDecryptedSharesAggregationBnVerifier }; +}) as any; diff --git a/packages/enclave-contracts/ignition/modules/thresholdDecryptedSharesAggregationModVerifier.ts b/packages/enclave-contracts/ignition/modules/thresholdDecryptedSharesAggregationModVerifier.ts new file mode 100644 index 0000000000..44e6cdfe77 --- /dev/null +++ b/packages/enclave-contracts/ignition/modules/thresholdDecryptedSharesAggregationModVerifier.ts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; + +export default buildModule("ThresholdDecryptedSharesAggregationModVerifier", (m) => { + const thresholdDecryptedSharesAggregationModVerifier = m.contract( + "ThresholdDecryptedSharesAggregationModVerifier", + ); + + return { thresholdDecryptedSharesAggregationModVerifier }; +}) as any; diff --git a/packages/enclave-contracts/ignition/modules/dkgPkVerifier.ts b/packages/enclave-contracts/ignition/modules/thresholdPkAggregationVerifier.ts similarity index 57% rename from packages/enclave-contracts/ignition/modules/dkgPkVerifier.ts rename to packages/enclave-contracts/ignition/modules/thresholdPkAggregationVerifier.ts index 504280e6b7..2ced20e7ca 100644 --- a/packages/enclave-contracts/ignition/modules/dkgPkVerifier.ts +++ b/packages/enclave-contracts/ignition/modules/thresholdPkAggregationVerifier.ts @@ -5,8 +5,8 @@ // or FITNESS FOR A PARTICULAR PURPOSE. import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; -export default buildModule("DkgPkVerifier", (m) => { - const dkgPkVerifier = m.contract("DkgPkVerifier"); +export default buildModule("ThresholdPkAggregationVerifier", (m) => { + const thresholdPkAggregationVerifier = m.contract("ThresholdPkAggregationVerifier"); - return { dkgPkVerifier }; + return { thresholdPkAggregationVerifier }; }) as any; diff --git a/packages/enclave-contracts/scripts/deployAndSave/verifiers.ts b/packages/enclave-contracts/scripts/deployAndSave/verifiers.ts index 97ebf320e6..acbbfcf8bf 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/verifiers.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/verifiers.ts @@ -28,10 +28,15 @@ export const discoverVerifierContracts = (): string[] => { /** * Deploys ZKTranscriptLib library required by BB-generated verifiers. * Reuses existing deployment if already deployed on the chain. + * + * Uses a fully-qualified name (FQN) because Hardhat has multiple ZKTranscriptLib + * artifacts (one per verifier .sol file). All are identical; we pick one. */ const deployZKTranscriptLib = async ( hre: HardhatRuntimeEnvironment, chain: string, + /** Verifier contract whose .sol file contains ZKTranscriptLib; used to form FQN */ + referenceContract: string, ): Promise => { const libName = "ZKTranscriptLib"; @@ -42,10 +47,11 @@ const deployZKTranscriptLib = async ( return existing.address; } - // Deploy the library + // Deploy the library — use FQN to disambiguate multiple ZKTranscriptLib artifacts + const libFQN = `contracts/verifier/${referenceContract}.sol:ZKTranscriptLib`; console.log(` Deploying ${libName}...`); const { ethers } = await hre.network.connect(); - const factory = await ethers.getContractFactory(libName); + const factory = await ethers.getContractFactory(libFQN); const contract = await factory.deploy(); await contract.waitForDeployment(); @@ -139,7 +145,11 @@ export const deployAndSaveAllVerifiers = async ( console.log(` Found ${contractNames.length} verifier contract(s)`); // Deploy ZKTranscriptLib once, reused by all verifiers - const zkTranscriptLibAddress = await deployZKTranscriptLib(hre, chain); + const zkTranscriptLibAddress = await deployZKTranscriptLib( + hre, + chain, + contractNames[0], + ); const deployments: VerifierDeployments = {}; diff --git a/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts b/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts index cbbf2aeaef..b2d246993d 100644 --- a/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts +++ b/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts @@ -783,7 +783,6 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { e3RefundManager, registry, slashingManager, - circuitVerifier, bondingRegistry, usdcToken, makeRequest, @@ -890,7 +889,6 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { e3RefundManager, registry, slashingManager, - circuitVerifier, usdcToken, makeRequest, operator1, @@ -1462,7 +1460,6 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { e3RefundManager, registry, slashingManager, - circuitVerifier, usdcToken, makeRequest, operator1, diff --git a/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts b/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts index acb34c50b1..6625eccdf2 100644 --- a/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts +++ b/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts @@ -1071,7 +1071,7 @@ describe("SlashingManager", function () { describe("proposeSlashEvidence() — Lane B (evidence-based, SLASHER_ROLE)", function () { it("should propose evidence-based slash with appeal window", async function () { - const { slashingManager, slasher, operatorAddress, mockVerifier } = + const { slashingManager, slasher, operatorAddress } = await loadFixture(setup); await setupPolicies(slashingManager); @@ -1100,7 +1100,7 @@ describe("SlashingManager", function () { }); it("should revert if caller is not slasher", async function () { - const { slashingManager, notTheOwner, operatorAddress, mockVerifier } = + const { slashingManager, notTheOwner, operatorAddress } = await loadFixture(setup); await setupPolicies(slashingManager); @@ -1120,8 +1120,7 @@ describe("SlashingManager", function () { }); it("should revert if operator is zero address", async function () { - const { slashingManager, slasher, mockVerifier } = - await loadFixture(setup); + const { slashingManager, slasher } = await loadFixture(setup); await setupPolicies(slashingManager); @@ -1140,7 +1139,7 @@ describe("SlashingManager", function () { describe("executeSlash() — Lane B execution", function () { it("should execute evidence-based slash after appeal window", async function () { - const { slashingManager, slasher, operatorAddress, mockVerifier } = + const { slashingManager, slasher, operatorAddress } = await loadFixture(setup); await setupPolicies(slashingManager); @@ -1216,7 +1215,7 @@ describe("SlashingManager", function () { }); it("should revert if already executed", async function () { - const { slashingManager, slasher, operatorAddress, mockVerifier } = + const { slashingManager, slasher, operatorAddress } = await loadFixture(setup); await setupPolicies(slashingManager); @@ -1241,13 +1240,8 @@ describe("SlashingManager", function () { describe("appeal system", function () { it("should allow operator to file appeal on evidence-based slash", async function () { - const { - slashingManager, - slasher, - operator, - operatorAddress, - mockVerifier, - } = await loadFixture(setup); + const { slashingManager, slasher, operator, operatorAddress } = + await loadFixture(setup); await setupPolicies(slashingManager); @@ -1271,13 +1265,8 @@ describe("SlashingManager", function () { }); it("should revert if non-operator tries to appeal", async function () { - const { - slashingManager, - slasher, - notTheOwner, - operatorAddress, - mockVerifier, - } = await loadFixture(setup); + const { slashingManager, slasher, notTheOwner, operatorAddress } = + await loadFixture(setup); await setupPolicies(slashingManager); @@ -1296,13 +1285,8 @@ describe("SlashingManager", function () { }); it("should revert if appeal window expired", async function () { - const { - slashingManager, - slasher, - operator, - operatorAddress, - mockVerifier, - } = await loadFixture(setup); + const { slashingManager, slasher, operator, operatorAddress } = + await loadFixture(setup); await setupPolicies(slashingManager); @@ -1323,13 +1307,8 @@ describe("SlashingManager", function () { }); it("should revert if already appealed", async function () { - const { - slashingManager, - slasher, - operator, - operatorAddress, - mockVerifier, - } = await loadFixture(setup); + const { slashingManager, slasher, operator, operatorAddress } = + await loadFixture(setup); await setupPolicies(slashingManager); @@ -1387,14 +1366,8 @@ describe("SlashingManager", function () { }); it("should allow governance to resolve appeal (approve)", async function () { - const { - slashingManager, - slasher, - operator, - owner, - operatorAddress, - mockVerifier, - } = await loadFixture(setup); + const { slashingManager, slasher, operator, owner, operatorAddress } = + await loadFixture(setup); await setupPolicies(slashingManager); @@ -1428,14 +1401,8 @@ describe("SlashingManager", function () { }); it("should allow governance to resolve appeal (deny)", async function () { - const { - slashingManager, - slasher, - operator, - owner, - operatorAddress, - mockVerifier, - } = await loadFixture(setup); + const { slashingManager, slasher, operator, owner, operatorAddress } = + await loadFixture(setup); await setupPolicies(slashingManager); @@ -1459,13 +1426,8 @@ describe("SlashingManager", function () { }); it("should block execution if appeal is pending", async function () { - const { - slashingManager, - slasher, - operator, - operatorAddress, - mockVerifier, - } = await loadFixture(setup); + const { slashingManager, slasher, operator, operatorAddress } = + await loadFixture(setup); await setupPolicies(slashingManager); @@ -1487,14 +1449,8 @@ describe("SlashingManager", function () { }); it("should block execution if appeal was approved", async function () { - const { - slashingManager, - slasher, - operator, - owner, - operatorAddress, - mockVerifier, - } = await loadFixture(setup); + const { slashingManager, slasher, operator, owner, operatorAddress } = + await loadFixture(setup); await setupPolicies(slashingManager); @@ -1517,14 +1473,8 @@ describe("SlashingManager", function () { }); it("should allow execution if appeal was denied", async function () { - const { - slashingManager, - slasher, - operator, - owner, - operatorAddress, - mockVerifier, - } = await loadFixture(setup); + const { slashingManager, slasher, operator, owner, operatorAddress } = + await loadFixture(setup); await setupPolicies(slashingManager); diff --git a/scripts/README.md b/scripts/README.md index 9e97e28c6d..60749563dd 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -278,15 +278,17 @@ Automates the full pipeline from Noir circuits to on-chain Solidity verifiers: 3. **Generates verification keys** using `bb write_vk -t evm` 4. **Generates Solidity verifiers** using `bb write_solidity_verifier` 5. **Post-processes** the generated Solidity: - - Renames contract from `HonkVerifier` to descriptive name (e.g., `DkgPkVerifier`, + - Renames contract from `HonkVerifier` to descriptive name (e.g., `ThresholdPkAggregationVerifier`, `ThresholdPkGenerationVerifier`) - Replaces Apache-2.0 license header with LGPL-3.0-only 6. **Outputs** to `packages/enclave-contracts/contracts/verifier/` ### Options +The `generate:verifiers` script in package.json passes `--circuits` with the on-chain used list. + +- `--circuits ` - Circuit names, comma-separated. Omit to generate all. - `--group ` - Circuit groups (comma-separated: dkg,threshold,recursive_aggregation) -- `--circuit ` - Generate verifier for specific circuit(s) (repeatable) - `--clean` - Remove existing verifier directory before generating - `--no-compile` - Don't compile circuits automatically (fail if not already compiled) - `--dry-run` - Show what would be generated without doing anything @@ -302,14 +304,14 @@ Automates the full pipeline from Noir circuits to on-chain Solidity verifiers: ``` 🔮 Generating Solidity verifiers from Noir circuits... - Found 13 circuit(s) + Found 4 circuit(s) - ✓ dkg/pk → DkgPkVerifier.sol - ✓ dkg/sk_share_computation → DkgSkShareComputationVerifier.sol - ✓ threshold/pk_generation → ThresholdPkGenerationVerifier.sol - ... + ✓ threshold/pk_aggregation → ThresholdPkAggregationVerifier.sol + ✓ threshold/decrypted_shares_aggregation_bn → ThresholdDecryptedSharesAggregationBnVerifier.sol + ✓ threshold/decrypted_shares_aggregation_mod → ThresholdDecryptedSharesAggregationModVerifier.sol + ✓ recursive_aggregation/fold → RecursiveAggregationFoldVerifier.sol -✅ Generated 13 Solidity verifier(s) in: +✅ Generated 4 Solidity verifier(s) in: packages/enclave-contracts/contracts/verifier/ ``` diff --git a/scripts/build-circuits.ts b/scripts/build-circuits.ts index 39c3017485..1de789ca76 100644 --- a/scripts/build-circuits.ts +++ b/scripts/build-circuits.ts @@ -11,6 +11,13 @@ import { appendFileSync, copyFileSync, existsSync, mkdirSync, readdirSync, readF import { basename, join, resolve } from 'path' import { ALL_GROUPS, CIRCUIT_GROUPS, CIRCUIT_VARIANTS, type CircuitGroup } from './circuit-constants' +/** share_computation wrapper is shared by sk_share_computation and e_sm_share_computation. */ +const SHARE_COMP_WRAPPER = { + path: ['recursive_aggregation', 'wrapper', 'dkg'] as const, + aliases: ['sk_share_computation', 'e_sm_share_computation'] as const, + variants: [CIRCUIT_VARIANTS.DEFAULT, CIRCUIT_VARIANTS.RECURSIVE] as const, +} + interface CircuitInfo { name: string group: CircuitGroup @@ -477,6 +484,24 @@ class NoirCircuitBuilder { } } + // share_computation wrapper aliases + const wrapperPath = SHARE_COMP_WRAPPER.path.join('/') + for (const variant of SHARE_COMP_WRAPPER.variants) { + const shareCompPrefix = `${variant}/${wrapperPath}/share_computation` + const basePrefix = `${variant}/${wrapperPath}` + for (const alias of SHARE_COMP_WRAPPER.aliases) { + for (const suffix of ['.json', '.vk', '.vk_hash']) { + const srcKey = `${shareCompPrefix}/share_computation${suffix}` + const srcHash = checksums[srcKey] + if (srcHash) { + const aliasKey = `${basePrefix}/${alias}/${alias}${suffix}` + checksums[aliasKey] = srcHash + lines.push(`${srcHash} ${aliasKey}`) + } + } + } + } + const outputDir = this.options.outputDir! writeFileSync(join(outputDir, 'SHA256SUMS'), lines.join('\n') + '\n') writeFileSync( @@ -523,6 +548,24 @@ class NoirCircuitBuilder { } } } + + // Share_computation wrapper aliases (source includes circuit group per SHARE_COMP_WRAPPER.path) + for (const variant of SHARE_COMP_WRAPPER.variants) { + const shareCompSrc = join(outputDir, variant, ...SHARE_COMP_WRAPPER.path, 'share_computation') + const hasJson = existsSync(join(shareCompSrc, 'share_computation.json')) + const hasVk = existsSync(join(shareCompSrc, 'share_computation.vk')) + const hasVkHash = existsSync(join(shareCompSrc, 'share_computation.vk_hash')) + if (hasJson || hasVk || hasVkHash) { + for (const alias of SHARE_COMP_WRAPPER.aliases) { + const destDir = join(outputDir, variant, ...SHARE_COMP_WRAPPER.path, alias) + mkdirSync(destDir, { recursive: true }) + if (hasJson) copyFileSync(join(shareCompSrc, 'share_computation.json'), join(destDir, `${alias}.json`)) + if (hasVk) copyFileSync(join(shareCompSrc, 'share_computation.vk'), join(destDir, `${alias}.vk`)) + if (hasVkHash) copyFileSync(join(shareCompSrc, 'share_computation.vk_hash'), join(destDir, `${alias}.vk_hash`)) + } + } + } + return outputDir } diff --git a/scripts/generate-verifiers.ts b/scripts/generate-verifiers.ts index bb90ec4d6d..4a2a8a8d7c 100644 --- a/scripts/generate-verifiers.ts +++ b/scripts/generate-verifiers.ts @@ -14,12 +14,10 @@ * will compile them automatically. * * Usage: - * pnpm generate:verifiers # Generate all verifiers - * pnpm generate:verifiers --group dkg # Only DKG circuits - * pnpm generate:verifiers --group threshold # Only Threshold circuits - * pnpm generate:verifiers --circuit pk # Only a specific circuit - * pnpm generate:verifiers --clean # Remove existing verifiers first - * pnpm generate:verifiers --dry-run # Show what would be generated + * pnpm generate:verifiers # All circuits (or --circuits from package.json) + * pnpm generate:verifiers --circuits pk,fold # Specific circuits + * pnpm generate:verifiers --clean # Remove existing verifiers first + * pnpm generate:verifiers --dry-run # Show what would be generated */ import { execSync } from 'child_process' @@ -140,6 +138,9 @@ class VerifierGenerator { const circuits: CircuitInfo[] = [] if (!existsSync(this.circuitsDir)) return circuits + // When --circuits not specified, include all circuits + const circuitFilter = this.options.circuits?.length ? this.options.circuits : undefined + for (const group of this.options.groups ?? ALL_GROUPS) { const groupDir = join(this.circuitsDir, group) if (!existsSync(groupDir)) continue @@ -147,7 +148,7 @@ class VerifierGenerator { for (const entry of readdirSync(groupDir)) { const circuitPath = join(groupDir, entry) if (statSync(circuitPath).isDirectory() && existsSync(join(circuitPath, 'Nargo.toml'))) { - if (!this.options.circuits || this.options.circuits.includes(entry)) { + if (!circuitFilter || circuitFilter.includes(entry)) { const packageName = this.getPackageName(circuitPath) circuits.push({ name: entry, group, path: circuitPath, packageName }) } @@ -292,7 +293,7 @@ class VerifierGenerator { /** * Convert group/name to a PascalCase Solidity contract name. - * e.g. (dkg, pk) → DkgPkVerifier + * e.g. (threshold, pk_aggregation) → ThresholdPkAggregationVerifier * (threshold, pk_generation) → ThresholdPkGenerationVerifier * (recursive_aggregation, fold) → RecursiveAggregationFoldVerifier */ @@ -368,13 +369,13 @@ async function main() { process.exit(1) } options.groups = value.split(',') as CircuitGroup[] - } else if (arg === '--circuit') { + } else if (arg === '--circuits') { const value = args[++i] if (!value || value.startsWith('--')) { - console.error('Error: --circuit requires a value') + console.error('Error: --circuits requires a value') process.exit(1) } - ;(options.circuits ??= []).push(value) + options.circuits = value.split(',').map((s) => s.trim()) } } @@ -390,18 +391,16 @@ Generates Solidity verifier contracts from compiled Noir circuits and places them in packages/enclave-contracts/contracts/verifier/. Options: + --circuits Circuit names (comma-separated). When omitted, generates all circuits. --group Circuit groups (comma-separated: dkg,threshold,recursive_aggregation) - --circuit Generate verifier for specific circuit(s) (repeatable) --clean Remove existing verifier directory before generating --no-compile Don't compile circuits automatically (fail if not already compiled) --dry-run Show what would be generated without doing anything -h, --help Show this help message Examples: - pnpm generate:verifiers # All circuits - pnpm generate:verifiers --group dkg # Only DKG circuits - pnpm generate:verifiers --group threshold --clean # Threshold only, clean first - pnpm generate:verifiers --circuit pk --circuit fold # Specific circuits + pnpm generate:verifiers --circuits pk,pk_aggregation,decrypted_shares_aggregation_bn,decrypted_shares_aggregation_mod,fold + pnpm generate:verifiers --circuits pk --clean `) } diff --git a/templates/default/tests/integration.spec.ts b/templates/default/tests/integration.spec.ts index ca75c31069..f6a2516eb2 100644 --- a/templates/default/tests/integration.spec.ts +++ b/templates/default/tests/integration.spec.ts @@ -189,7 +189,7 @@ describe('Integration', () => { const { waitForEvent } = await setupEventListeners(sdk, store) const committeeSize = CommitteeSize.Micro - const duration = 300 + const duration = 900 const inputWindow = await calculateInputWindow(publicClient, duration) const thresholdBfvParams = await sdk.getThresholdBfvParamsSet() const e3ProgramParams = encodeBfvParams(thresholdBfvParams) diff --git a/tests/integration/fns.sh b/tests/integration/fns.sh index ab647c47bf..765715ede4 100644 --- a/tests/integration/fns.sh +++ b/tests/integration/fns.sh @@ -65,7 +65,7 @@ strip_ansi() { waiton() { local file_path="$1" - local timeout="${2:-600}" # default 10 minutes + local timeout="${2:-1300}" # default ~22 minutes local start_time=$(date +%s) until [ -f "$file_path" ]; do if [ $(($(date +%s) - start_time)) -ge $timeout ]; then @@ -77,7 +77,7 @@ waiton() { } waiton-files() { - local timeout=600 # 10 minutes timeout + local timeout=1300 # ~22 minutes timeout local start_time=$(date +%s) while true; do all_exist=true