diff --git a/Cargo.lock b/Cargo.lock index 378fa1d859..0dcd73e7b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3286,6 +3286,7 @@ dependencies = [ "e3-test-helpers", "e3-trbfv", "e3-utils", + "e3-zk-helpers", "futures-util", "hex", "once_cell", @@ -3457,6 +3458,7 @@ dependencies = [ "e3-request", "e3-trbfv", "e3-utils", + "e3-zk-helpers", "fhe", "fhe-traits", "ndarray", @@ -3487,6 +3489,7 @@ dependencies = [ "e3-data", "e3-events", "e3-fhe-params", + "e3-polynomial", "e3-trbfv", "e3-utils", "e3-zk-helpers", diff --git a/crates/events/Cargo.toml b/crates/events/Cargo.toml index 716e437a08..10f67bf2bc 100644 --- a/crates/events/Cargo.toml +++ b/crates/events/Cargo.toml @@ -31,6 +31,7 @@ e3-crypto = { workspace = true } e3-trbfv = { workspace = true } e3-utils = { workspace = true } e3-fhe-params = { workspace = true } +e3-zk-helpers = { workspace = true } [features] test-helpers = [] # ensure test-helpers is available for integration tests diff --git a/crates/events/src/enclave_event/compute_request/mod.rs b/crates/events/src/enclave_event/compute_request/mod.rs index d28efab3ca..e339ed9a14 100644 --- a/crates/events/src/enclave_event/compute_request/mod.rs +++ b/crates/events/src/enclave_event/compute_request/mod.rs @@ -82,6 +82,7 @@ impl ToString for ComputeRequest { }, ComputeRequestKind::Zk(req) => match req { ZkRequest::PkBfv(_) => "ZkPkBfv", + ZkRequest::PkGeneration(_) => "ZkPkGeneration", }, } .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 ec3fa1cd26..7af29ac4c8 100644 --- a/crates/events/src/enclave_event/compute_request/zk.rs +++ b/crates/events/src/enclave_event/compute_request/zk.rs @@ -6,8 +6,10 @@ use crate::Proof; use derivative::Derivative; +use e3_crypto::SensitiveBytes; use e3_fhe_params::BfvPreset; use e3_utils::utility_types::ArcBytes; +use e3_zk_helpers::CiphernodesCommitteeSize; use serde::{Deserialize, Serialize}; /// ZK proof generation request variants. @@ -15,6 +17,8 @@ use serde::{Deserialize, Serialize}; pub enum ZkRequest { /// Generate proof for BFV public key (T0). PkBfv(PkBfvProofRequest), + /// Generate proof for PK generation (T1a). + PkGeneration(PkGenerationProofRequest), } /// Request to generate a proof for BFV public key generation (T0). @@ -27,6 +31,28 @@ pub struct PkBfvProofRequest { pub params_preset: BfvPreset, } +/// Request to generate a proof for PK share generation (T1a). +#[derive(Derivative, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derivative(Debug)] +pub struct PkGenerationProofRequest { + /// Raw pk0 share polynomial bytes (public statement). + #[derivative(Debug(format_with = "e3_utils::formatters::hexf"))] + pub pk0_share: ArcBytes, + /// Raw common random polynomial bytes (public statement). + #[derivative(Debug(format_with = "e3_utils::formatters::hexf"))] + pub a: ArcBytes, + /// Raw secret key polynomial bytes (witness — encrypted at rest). + pub sk: SensitiveBytes, + /// Raw error polynomial bytes (witness — encrypted at rest). + pub eek: SensitiveBytes, + /// Raw smudging noise polynomial bytes (witness — encrypted at rest). + pub e_sm: SensitiveBytes, + /// BFV preset for parameter resolution. + pub params_preset: BfvPreset, + /// The size of the committee + pub committee_size: CiphernodesCommitteeSize, +} + impl PkBfvProofRequest { pub fn new(pk_bfv: impl Into, params_preset: BfvPreset) -> Self { Self { @@ -36,11 +62,35 @@ impl PkBfvProofRequest { } } +impl PkGenerationProofRequest { + pub fn new( + pk0_share: impl Into, + a: impl Into, + sk: SensitiveBytes, + eek: SensitiveBytes, + e_sm: SensitiveBytes, + params_preset: BfvPreset, + committee_size: CiphernodesCommitteeSize, + ) -> Self { + Self { + pk0_share: pk0_share.into(), + a: a.into(), + sk, + eek, + params_preset, + e_sm, + committee_size, + } + } +} + /// ZK proof generation response variants. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum ZkResponse { /// Proof for BFV public key (T0). PkBfv(PkBfvProofResponse), + /// Proof for PK generation (T1a). + PkGeneration(PkGenerationProofResponse), } /// Response containing a generated BFV public key proof. @@ -49,12 +99,24 @@ pub struct PkBfvProofResponse { pub proof: Proof, } +/// Response containing a generated PK generation proof. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct PkGenerationProofResponse { + pub proof: Proof, +} + impl PkBfvProofResponse { pub fn new(proof: Proof) -> Self { Self { proof } } } +impl PkGenerationProofResponse { + pub fn new(proof: Proof) -> Self { + Self { proof } + } +} + /// ZK-specific error variants. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum ZkError { diff --git a/crates/events/src/enclave_event/keyshare_created.rs b/crates/events/src/enclave_event/keyshare_created.rs index 78b2d1e80d..2c2d614c06 100644 --- a/crates/events/src/enclave_event/keyshare_created.rs +++ b/crates/events/src/enclave_event/keyshare_created.rs @@ -4,7 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use crate::E3id; +use crate::{E3id, SignedProofPayload}; use actix::Message; use derivative::Derivative; use e3_utils::ArcBytes; @@ -19,6 +19,7 @@ pub struct KeyshareCreated { pub pubkey: ArcBytes, pub e3_id: E3id, pub node: String, + pub signed_pk_generation_proof: Option, } impl Display for KeyshareCreated { diff --git a/crates/events/src/enclave_event/mod.rs b/crates/events/src/enclave_event/mod.rs index 1f660a7faa..fba6dce82c 100644 --- a/crates/events/src/enclave_event/mod.rs +++ b/crates/events/src/enclave_event/mod.rs @@ -30,6 +30,7 @@ mod keyshare_created; mod net_sync_events_received; mod operator_activation_changed; mod outgoing_sync_requested; +mod pk_generation_proof_signed; mod plaintext_aggregated; mod plaintext_output_published; mod proof; @@ -43,6 +44,7 @@ mod sync_start; mod test_event; mod threshold_share_collection_failed; mod threshold_share_created; +mod threshold_share_pending; mod ticket_balance_updated; mod ticket_generated; mod ticket_submitted; @@ -75,6 +77,7 @@ pub use keyshare_created::*; pub use net_sync_events_received::*; pub use operator_activation_changed::*; pub use outgoing_sync_requested::*; +pub use pk_generation_proof_signed::*; pub use plaintext_aggregated::*; pub use plaintext_output_published::*; pub use proof::*; @@ -89,6 +92,7 @@ pub use sync_start::*; pub use test_event::*; pub use threshold_share_collection_failed::*; pub use threshold_share_created::*; +pub use threshold_share_pending::*; pub use ticket_balance_updated::*; pub use ticket_generated::*; pub use ticket_submitted::*; @@ -210,6 +214,7 @@ pub enum EnclaveEventData { TicketGenerated(TicketGenerated), TicketSubmitted(TicketSubmitted), PlaintextOutputPublished(PlaintextOutputPublished), + PkGenerationProofSigned(PkGenerationProofSigned), EnclaveError(EnclaveError), E3RequestComplete(E3RequestComplete), E3Failed(E3Failed), @@ -217,6 +222,7 @@ pub enum EnclaveEventData { Shutdown(Shutdown), DocumentReceived(DocumentReceived), ThresholdShareCreated(ThresholdShareCreated), + ThresholdSharePending(ThresholdSharePending), EncryptionKeyPending(EncryptionKeyPending), EncryptionKeyReceived(EncryptionKeyReceived), EncryptionKeyCreated(EncryptionKeyCreated), @@ -461,8 +467,10 @@ impl EnclaveEventData { EnclaveEventData::CiphertextOutputPublished(ref data) => Some(data.e3_id.clone()), EnclaveEventData::DecryptionshareCreated(ref data) => Some(data.e3_id.clone()), EnclaveEventData::PlaintextAggregated(ref data) => Some(data.e3_id.clone()), + EnclaveEventData::PkGenerationProofSigned(ref data) => Some(data.e3_id.clone()), EnclaveEventData::CiphernodeSelected(ref data) => Some(data.e3_id.clone()), EnclaveEventData::ThresholdShareCreated(ref data) => Some(data.e3_id.clone()), + EnclaveEventData::ThresholdSharePending(ref data) => Some(data.e3_id.clone()), EnclaveEventData::EncryptionKeyPending(ref data) => Some(data.e3_id.clone()), EnclaveEventData::EncryptionKeyReceived(ref data) => Some(data.e3_id.clone()), EnclaveEventData::CommitteePublished(ref data) => Some(data.e3_id.clone()), @@ -510,6 +518,7 @@ impl_event_types!( DecryptionshareCreated, PlaintextAggregated, PublishDocumentRequested, + PkGenerationProofSigned, E3RequestComplete, E3Failed, E3StageChanged, @@ -531,6 +540,7 @@ impl_event_types!( TestEvent, DocumentReceived, ThresholdShareCreated, + ThresholdSharePending, EncryptionKeyPending, EncryptionKeyReceived, EncryptionKeyCreated, diff --git a/crates/events/src/enclave_event/pk_generation_proof_signed.rs b/crates/events/src/enclave_event/pk_generation_proof_signed.rs new file mode 100644 index 0000000000..281ff8adaa --- /dev/null +++ b/crates/events/src/enclave_event/pk_generation_proof_signed.rs @@ -0,0 +1,24 @@ +// 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 crate::{E3id, SignedProofPayload}; +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct PkGenerationProofSigned { + pub e3_id: E3id, + pub party_id: u64, + pub signed_proof: SignedProofPayload, +} + +impl Display for PkGenerationProofSigned { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} diff --git a/crates/events/src/enclave_event/threshold_share_pending.rs b/crates/events/src/enclave_event/threshold_share_pending.rs new file mode 100644 index 0000000000..be37599881 --- /dev/null +++ b/crates/events/src/enclave_event/threshold_share_pending.rs @@ -0,0 +1,27 @@ +// 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 crate::{E3id, PkGenerationProofRequest, ThresholdShare}; +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; +use std::sync::Arc; + +#[derive(Message, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct ThresholdSharePending { + pub e3_id: E3id, + /// Full threshold share containing all encrypted shares for all parties + pub full_share: Arc, + /// The proof request data for the zk actor + pub proof_request: PkGenerationProofRequest, +} + +impl Display for ThresholdSharePending { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} diff --git a/crates/keyshare/Cargo.toml b/crates/keyshare/Cargo.toml index fcd908ced0..becd731686 100644 --- a/crates/keyshare/Cargo.toml +++ b/crates/keyshare/Cargo.toml @@ -21,6 +21,7 @@ e3-multithread = { workspace = true } e3-request = { workspace = true } e3-trbfv = { workspace = true } e3-utils = { workspace = true } +e3-zk-helpers = { workspace = true } fhe = { workspace = true } fhe-traits = { workspace = true } rand = { workspace = true } diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index cca0bc00e3..f893ca7db1 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -13,8 +13,9 @@ use e3_events::{ ComputeResponse, ComputeResponseKind, CorrelationId, DecryptionshareCreated, Die, E3RequestComplete, E3id, EType, EnclaveEvent, EnclaveEventData, EncryptionKey, EncryptionKeyCollectionFailed, EncryptionKeyCreated, EncryptionKeyPending, EventContext, - KeyshareCreated, PartyId, Sequenced, ThresholdShare, ThresholdShareCollectionFailed, - ThresholdShareCreated, TypedEvent, + KeyshareCreated, PartyId, PkGenerationProofRequest, PkGenerationProofSigned, Sequenced, + SignedProofPayload, ThresholdShare, ThresholdShareCollectionFailed, ThresholdShareCreated, + ThresholdSharePending, TypedEvent, }; use e3_fhe::create_crp; use e3_fhe_params::{BfvParamSet, BfvPreset}; @@ -31,6 +32,7 @@ use e3_trbfv::{ }; use e3_utils::{to_ordered_vec, utility_types::ArcBytes}; use e3_utils::{NotifySync, MAILBOX_LIMIT}; +use e3_zk_helpers::CiphernodesCommitteeSize; use fhe::bfv::{PublicKey, SecretKey}; use fhe_traits::{DeserializeParametrized, Serialize}; use rand::{rngs::OsRng, SeedableRng}; @@ -51,7 +53,10 @@ pub struct GenPkShareAndSkSss(CiphernodeSelected); #[derive(Message, Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] #[rtype(result = "()")] -pub struct GenEsiSss(CiphernodeSelected); +pub struct GenEsiSss { + pub ciphernode_selected: CiphernodeSelected, + pub e_sm_raw: SensitiveBytes, +} #[derive(Message)] #[rtype(result = "()")] @@ -74,23 +79,32 @@ pub struct CollectingEncryptionKeysData { ciphernode_selected: CiphernodeSelected, } +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct ProofRequestData { + pub pk0_share_raw: ArcBytes, + pub a_raw: ArcBytes, + pub sk_raw: SensitiveBytes, + pub eek_raw: SensitiveBytes, +} + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct GeneratingThresholdShareData { pk_share: Option, sk_sss: Option>, esi_sss: Option>>, + e_sm_raw: Option, sk_bfv: SensitiveBytes, pk_bfv: ArcBytes, collected_encryption_keys: Vec>, + ciphernode_selected: Option, + proof_request_data: Option, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct AggregatingDecryptionKey { pk_share: ArcBytes, - sk_sss: Encrypted, - esi_sss: Vec>, sk_bfv: SensitiveBytes, - collected_encryption_keys: Vec>, + signed_pk_generation_proof: Option, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -98,6 +112,7 @@ pub struct ReadyForDecryption { pk_share: ArcBytes, sk_poly_sum: SensitiveBytes, es_poly_sum: Vec, + signed_pk_generation_proof: Option, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -105,6 +120,7 @@ pub struct Decrypting { pk_share: ArcBytes, sk_poly_sum: SensitiveBytes, es_poly_sum: Vec, + signed_pk_generation_proof: Option, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -403,6 +419,38 @@ impl ThresholdKeyshare { Ok(()) } + /// Handle PkGenerationProofSigned - stores the signed T1 proof in state + pub fn handle_pk_generation_proof_signed( + &mut self, + msg: TypedEvent, + ) -> Result<()> { + let (msg, ec) = msg.into_components(); + let state = self.state.try_get()?; + + // Only accept proof for our own party + if msg.party_id != state.party_id { + return Ok(()); + } + + info!( + "Received PkGenerationProofSigned for party {} E3 {}", + msg.party_id, msg.e3_id + ); + + // Store the signed proof in AggregatingDecryptionKey state + self.state.try_mutate(&ec, |s| { + let current: AggregatingDecryptionKey = s.clone().try_into()?; + s.new_state(KeyshareState::AggregatingDecryptionKey( + AggregatingDecryptionKey { + signed_pk_generation_proof: Some(msg.signed_proof), + ..current + }, + )) + })?; + + Ok(()) + } + pub fn handle_compute_response(&mut self, msg: TypedEvent) -> Result<()> { match &msg.response { ComputeResponseKind::TrBFV(trbfv) => match trbfv { @@ -418,7 +466,9 @@ impl ThresholdKeyshare { } _ => Ok(()), }, - ComputeResponseKind::Zk(_) => Ok(()), + ComputeResponseKind::Zk(zk) => match zk { + _ => Ok(()), + }, } } @@ -456,18 +506,6 @@ impl ThresholdKeyshare { )) })?; - // let state = self.state.try_get()?; - // self.bus.publish( - // EncryptionKeyCreated { - // e3_id: state.e3_id.clone(), - // key: Arc::new(EncryptionKey { - // party_id: state.party_id, - // pk_bfv: pk_bfv_bytes, - // }), - // external: false, - // }, - // ec, - // )?; self.bus.publish( EncryptionKeyPending { e3_id, @@ -499,16 +537,16 @@ impl ThresholdKeyshare { sk_sss: None, pk_share: None, esi_sss: None, + e_sm_raw: None, sk_bfv: current.sk_bfv, pk_bfv: current.pk_bfv, collected_encryption_keys: msg.keys, + ciphernode_selected: Some(current.ciphernode_selected.clone()), + proof_request_data: None, }, )) })?; - self.handle_gen_esi_sss_requested(TypedEvent::new( - GenEsiSss(current.ciphernode_selected.clone()), - ec.clone(), - ))?; + self.handle_gen_pk_share_and_sk_sss_requested(TypedEvent::new( GenPkShareAndSkSss(current.ciphernode_selected), ec, @@ -517,34 +555,44 @@ impl ThresholdKeyshare { Ok(()) } - /// 2. GenEsiSss - pub fn handle_gen_esi_sss_requested(&self, msg: TypedEvent) -> Result<()> { + /// 2. GenPkShareAndSkSss + pub fn handle_gen_pk_share_and_sk_sss_requested( + &self, + msg: TypedEvent, + ) -> Result<()> { let (msg, ec) = msg.into_components(); - info!("GenEsiSss on ThresholdKeyshare"); - - let evt = msg.0; - let CiphernodeSelected { - // TODO: should these be on meta? These seem TrBFV specific. perhaps it is best to - // bundle them in with the params - error_size, - esi_per_ct, - e3_id, - .. - } = evt.clone(); - + info!("GenPkShareAndSkSss on ThresholdKeyshare"); + let CiphernodeSelected { seed, e3_id, .. } = msg.0; let state = self .state .get() .ok_or(anyhow!("State not found on ThrehsoldKeyshare"))?; - let trbfv_config = state.get_trbfv_config(); + let trbfv_config: TrBFVConfig = state.get_trbfv_config(); + + let crp = ArcBytes::from_bytes( + &create_crp( + trbfv_config.params(), + Arc::new(Mutex::new(ChaCha20Rng::from_seed(seed.into()))), + ) + .to_bytes(), + ); + + let threshold_preset = self + .share_enc_preset + .threshold_counterpart() + .ok_or_else(|| anyhow!("No threshold counterpart for {:?}", self.share_enc_preset))?; + let defaults = threshold_preset + .search_defaults() + .ok_or_else(|| anyhow!("No search defaults for {:?}", threshold_preset))?; let event = ComputeRequest::trbfv( - TrBFVRequest::GenEsiSss( - GenEsiSssRequest { + TrBFVRequest::GenPkShareAndSkSss( + GenPkShareAndSkSssRequest { trbfv_config, - error_size, - esi_per_ct: esi_per_ct as u64, + crp, + lambda: defaults.lambda as usize, + num_ciphertexts: defaults.z as usize, } .into(), ), @@ -556,81 +604,85 @@ impl ThresholdKeyshare { Ok(()) } - /// 2a. GenEsiSss result - pub fn handle_gen_esi_sss_response(&mut self, res: TypedEvent) -> Result<()> { + /// 2a. GenPkShareAndSkSss result + pub fn handle_gen_pk_share_and_sk_sss_response( + &mut self, + res: TypedEvent, + ) -> Result<()> { let (res, ec) = res.into_components(); - let output: GenEsiSssResponse = res.try_into()?; - let esi_sss = output.esi_sss; + let output: GenPkShareAndSkSssResponse = res + .try_into() + .context("Error extracting data from compute process")?; - self.state.try_mutate(&ec, |s| { - use KeyshareState as K; + let (pk_share, sk_sss, e_sm_raw) = ( + output.pk_share.clone(), + output.sk_sss, + output.e_sm_raw.clone(), + ); - info!("try_store_esi_sss"); + // Store proof request data for later use by ProofRequestActor + let proof_request_data = ProofRequestData { + pk0_share_raw: output.pk0_share_raw, + a_raw: output.a_raw, + sk_raw: output.sk_raw, + eek_raw: output.eek_raw, + }; + self.state.try_mutate(&ec, |s| { + info!("try_store_pk_share_and_sk_sss"); let current: GeneratingThresholdShareData = s.clone().try_into()?; - let next = match (current.pk_share, current.sk_sss) { - // If the other shares are here then transition to aggregation - (Some(pk_share), Some(sk_sss)) => { - K::AggregatingDecryptionKey(AggregatingDecryptionKey { - esi_sss, - pk_share, - sk_sss, - sk_bfv: current.sk_bfv, - collected_encryption_keys: current.collected_encryption_keys, - }) - } - // If the other shares are not here yet then don't transition - (None, None) => K::GeneratingThresholdShare(GeneratingThresholdShareData { - esi_sss: Some(esi_sss), - pk_share: None, - sk_sss: None, - sk_bfv: current.sk_bfv, - pk_bfv: current.pk_bfv, - collected_encryption_keys: current.collected_encryption_keys, - }), - _ => bail!("Inconsistent state!"), - }; - - s.new_state(next) + s.new_state(KeyshareState::GeneratingThresholdShare( + GeneratingThresholdShareData { + pk_share: Some(pk_share), + sk_sss: Some(sk_sss), + e_sm_raw: Some(e_sm_raw.clone()), + proof_request_data: Some(proof_request_data), + ..current + }, + )) })?; - info!("esi stored"); - if let Some(ThresholdKeyshareState { - state: KeyshareState::AggregatingDecryptionKey { .. }, - .. - }) = self.state.get() - { - self.handle_shares_generated(ec)?; + // Fire gen_esi_sss with the e_sm_raw + let current_state: GeneratingThresholdShareData = self.state.try_get()?.try_into()?; + if let Some(ciphernode_selected) = current_state.ciphernode_selected { + self.handle_gen_esi_sss_requested(TypedEvent::new( + GenEsiSss { + ciphernode_selected, + e_sm_raw: current_state + .e_sm_raw + .expect("e_sm_raw should be set at this point"), + }, + ec.clone(), + ))?; } + Ok(()) } - /// 3. GenPkShareAndSkSss - pub fn handle_gen_pk_share_and_sk_sss_requested( - &self, - msg: TypedEvent, - ) -> Result<()> { + /// 3. GenEsiSss + pub fn handle_gen_esi_sss_requested(&self, msg: TypedEvent) -> Result<()> { let (msg, ec) = msg.into_components(); - info!("GenPkShareAndSkSss on ThresholdKeyshare"); - let CiphernodeSelected { seed, e3_id, .. } = msg.0; + info!("GenEsiSss on ThresholdKeyshare"); + + let evt = msg.ciphernode_selected; + let e_sm_raw_decrypted = ArcBytes::from_bytes(&msg.e_sm_raw.access_raw(&self.cipher)?); + let CiphernodeSelected { e3_id, .. } = evt.clone(); + let state = self .state .get() .ok_or(anyhow!("State not found on ThrehsoldKeyshare"))?; - let trbfv_config: TrBFVConfig = state.get_trbfv_config(); + let trbfv_config = state.get_trbfv_config(); - let crp = ArcBytes::from_bytes( - &create_crp( - trbfv_config.params(), - Arc::new(Mutex::new(ChaCha20Rng::from_seed(seed.into()))), - ) - .to_bytes(), - ); let event = ComputeRequest::trbfv( - TrBFVRequest::GenPkShareAndSkSss( - GenPkShareAndSkSssRequest { trbfv_config, crp }.into(), + TrBFVRequest::GenEsiSss( + GenEsiSssRequest { + trbfv_config, + e_sm_raw: e_sm_raw_decrypted, + } + .into(), ), CorrelationId::new(), e3_id, @@ -640,52 +692,50 @@ impl ThresholdKeyshare { Ok(()) } - /// 3a. GenPkShareAndSkSss result - pub fn handle_gen_pk_share_and_sk_sss_response( - &mut self, - res: TypedEvent, - ) -> Result<()> { + /// 3a. GenEsiSss result + pub fn handle_gen_esi_sss_response(&mut self, res: TypedEvent) -> Result<()> { let (res, ec) = res.into_components(); + let output: GenEsiSssResponse = res.try_into()?; - let output: GenPkShareAndSkSssResponse = res - .try_into() - .context("Error extracting data from compute process")?; - - let (pk_share, sk_sss) = (output.pk_share, output.sk_sss); + let esi_sss = output.esi_sss; + // First store esi_sss in GeneratingThresholdShareData self.state.try_mutate(&ec, |s| { - info!("try_store_pk_share_and_sk_sss"); + info!("try_store_esi_sss"); let current: GeneratingThresholdShareData = s.clone().try_into()?; - let next = match current.esi_sss { - // If the esi shares are here then transition to aggregation - Some(esi_sss) => { - KeyshareState::AggregatingDecryptionKey(AggregatingDecryptionKey { - esi_sss, - pk_share, - sk_sss, - sk_bfv: current.sk_bfv, - collected_encryption_keys: current.collected_encryption_keys, - }) - } - // If esi shares are not here yet then don't transition - None => KeyshareState::GeneratingThresholdShare(GeneratingThresholdShareData { - esi_sss: None, - pk_share: Some(pk_share), - sk_sss: Some(sk_sss), - sk_bfv: current.sk_bfv, - pk_bfv: current.pk_bfv, - collected_encryption_keys: current.collected_encryption_keys, - }), - }; - s.new_state(next) + s.new_state(KeyshareState::GeneratingThresholdShare( + GeneratingThresholdShareData { + esi_sss: Some(esi_sss), + ..current + }, + )) })?; - if let Some(ThresholdKeyshareState { - state: KeyshareState::AggregatingDecryptionKey { .. }, - .. - }) = self.state.get() - { - self.handle_shares_generated(ec)?; + info!("esi stored"); + + // Check if all data is ready, if so call handle_shares_generated BEFORE transitioning + let current: GeneratingThresholdShareData = self.state.try_get()?.try_into()?; + let ready = current.pk_share.is_some() + && current.sk_sss.is_some() + && current.esi_sss.is_some() + && current.e_sm_raw.is_some() + && current.proof_request_data.is_some(); + + if ready { + // Call handle_shares_generated while still in GeneratingThresholdShare state + self.handle_shares_generated(ec.clone())?; + + // Now transition to AggregatingDecryptionKey with minimal state + self.state.try_mutate(&ec, |s| { + let current: GeneratingThresholdShareData = s.clone().try_into()?; + s.new_state(KeyshareState::AggregatingDecryptionKey( + AggregatingDecryptionKey { + pk_share: current.pk_share.expect("pk_share checked above"), + sk_bfv: current.sk_bfv, + signed_pk_generation_proof: None, + }, + )) + })?; } Ok(()) } @@ -694,10 +744,12 @@ impl ThresholdKeyshare { pub fn handle_shares_generated(&mut self, ec: EventContext) -> Result<()> { let Some(ThresholdKeyshareState { state: - KeyshareState::AggregatingDecryptionKey(AggregatingDecryptionKey { - pk_share, - sk_sss, - esi_sss, + KeyshareState::GeneratingThresholdShare(GeneratingThresholdShareData { + pk_share: Some(pk_share), + sk_sss: Some(sk_sss), + esi_sss: Some(esi_sss), + e_sm_raw: Some(e_sm_raw), + proof_request_data: Some(proof_request_data), collected_encryption_keys, .. }), @@ -706,10 +758,10 @@ impl ThresholdKeyshare { .. }) = self.state.get() else { - bail!("Invalid state!"); + bail!("Invalid state - expected GeneratingThresholdShare with all data"); }; - // Get collected BFV public keys from all parties (now from persisted state) + // Get collected BFV public keys from all parties (from persisted state) let encryption_keys = &collected_encryption_keys; // Convert to BFV public keys @@ -747,31 +799,37 @@ impl ThresholdKeyshare { esi_sss: encrypted_esi_sss, }; - // Domain-level splitting: publish one ThresholdShareCreated per recipient party - // Each party only receives the share data meant for them - let num_parties = full_share.num_parties(); + // Build the proof request + let threshold_preset = self + .share_enc_preset + .threshold_counterpart() + .ok_or_else(|| anyhow!("No threshold counterpart for {:?}", self.share_enc_preset))?; + + let proof_request = PkGenerationProofRequest::new( + proof_request_data.pk0_share_raw.clone(), + proof_request_data.a_raw.clone(), + proof_request_data.sk_raw.clone(), + proof_request_data.eek_raw.clone(), + e_sm_raw.clone(), + threshold_preset, + CiphernodesCommitteeSize::Small, // TODO: derive from config + ); + info!( - "Publishing ThresholdShare for E3 {} to {} parties", - e3_id, num_parties + "Publishing ThresholdSharePending for E3 {} (proof will be generated and signed by ProofRequestActor)", + e3_id ); - for recipient_party_id in 0..num_parties { - let party_share = full_share - .extract_for_party(recipient_party_id) - .ok_or_else(|| { - anyhow!("Failed to extract share for party {}", recipient_party_id) - })?; - - self.bus.publish( - ThresholdShareCreated { - e3_id: e3_id.clone(), - share: Arc::new(party_share), - target_party_id: recipient_party_id as u64, - external: false, - }, - ec.clone(), - )?; - } + // Publish ThresholdSharePending - ProofRequestActor will generate proof, sign, and publish ThresholdShareCreated + self.bus.publish( + ThresholdSharePending { + e3_id: e3_id.clone(), + full_share: Arc::new(full_share), + proof_request, + }, + ec.clone(), + )?; + Ok(()) } @@ -865,14 +923,15 @@ impl ThresholdKeyshare { use KeyshareState as K; info!("Try store decryption key"); - // Attempt to get pk_share from current state + // Get pk_share and signed proof from current state let current: AggregatingDecryptionKey = s.clone().try_into()?; - // Transition to ReadyForDecryption + // Transition to ReadyForDecryption, carrying the signed proof let next = K::ReadyForDecryption(ReadyForDecryption { pk_share: current.pk_share, sk_poly_sum, es_poly_sum, + signed_pk_generation_proof: current.signed_pk_generation_proof, }); s.new_state(next) @@ -888,8 +947,9 @@ impl ThresholdKeyshare { pubkey: current.pk_share, e3_id: e3_id.clone(), node: address, + signed_pk_generation_proof: current.signed_pk_generation_proof, }, - ec, + ec.clone(), )?; Ok(()) @@ -911,6 +971,7 @@ impl ThresholdKeyshare { pk_share: current.pk_share, sk_poly_sum: current.sk_poly_sum, es_poly_sum: current.es_poly_sum, + signed_pk_generation_proof: current.signed_pk_generation_proof, }); s.new_state(next) @@ -994,6 +1055,9 @@ impl Handler for ThresholdKeyshare { let _ = self.handle_encryption_key_created(TypedEvent::new(data, ec), ctx.address()); } + EnclaveEventData::PkGenerationProofSigned(data) => { + let _ = self.handle_pk_generation_proof_signed(TypedEvent::new(data, ec)); + } EnclaveEventData::E3RequestComplete(data) => self.notify_sync(ctx, data), EnclaveEventData::E3Failed(data) => { warn!( diff --git a/crates/multithread/Cargo.toml b/crates/multithread/Cargo.toml index b231789b93..a8a33b975e 100644 --- a/crates/multithread/Cargo.toml +++ b/crates/multithread/Cargo.toml @@ -14,6 +14,7 @@ e3-fhe-params = { workspace = true } e3-trbfv = { workspace = true } e3-crypto = { workspace = true } e3-events = { workspace = true } +e3-polynomial = { workspace = true } e3-zk-helpers = { workspace = true } e3-utils = { workspace = true } e3-zk-prover = { workspace = true } diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index 97f985bdcf..a62106fbc3 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -19,30 +19,29 @@ use anyhow::Result; use e3_crypto::Cipher; use e3_events::run_once; use e3_events::trap_fut; -use e3_events::BusHandle; -use e3_events::ComputeRequestErrorKind; use e3_events::EType; use e3_events::EffectsEnabled; -use e3_events::EnclaveEvent; -use e3_events::EnclaveEventData; -use e3_events::EventPublisher; -use e3_events::EventSubscriber; -use e3_events::TypedEvent; -use e3_events::{ComputeRequest, ComputeRequestError, ComputeResponse, EventType}; use e3_events::{ - ComputeRequestKind, PkBfvProofRequest, PkBfvProofResponse, ZkError as ZkEventError, ZkRequest, - ZkResponse, + BusHandle, ComputeRequest, ComputeRequestError, ComputeRequestErrorKind, ComputeRequestKind, + ComputeResponse, EnclaveEvent, EnclaveEventData, EventPublisher, EventSubscriber, EventType, + PkBfvProofRequest, PkBfvProofResponse, PkGenerationProofRequest, PkGenerationProofResponse, + TypedEvent, ZkError as ZkEventError, ZkRequest, ZkResponse, }; use e3_fhe_params::{BfvParamSet, BfvPreset}; +use e3_polynomial::CrtPolynomial; use e3_trbfv::calculate_decryption_key::calculate_decryption_key; use e3_trbfv::calculate_decryption_share::calculate_decryption_share; use e3_trbfv::calculate_threshold_decryption::calculate_threshold_decryption; use e3_trbfv::gen_esi_sss::gen_esi_sss; use e3_trbfv::gen_pk_share_and_sk_sss::gen_pk_share_and_sk_sss; +use e3_trbfv::helpers::try_poly_from_bytes; use e3_trbfv::{TrBFVError, TrBFVRequest, TrBFVResponse}; use e3_utils::SharedRng; use e3_utils::MAILBOX_LIMIT; use e3_zk_helpers::circuits::dkg::pk::circuit::{PkCircuit, PkCircuitData}; +use e3_zk_helpers::circuits::threshold::pk_generation::circuit::{ + PkGenerationCircuit, PkGenerationCircuitData, +}; use e3_zk_prover::{Provable, ZkBackend, ZkProver}; use fhe::bfv::PublicKey; use fhe_traits::DeserializeParametrized; @@ -240,7 +239,7 @@ fn handle_compute_request( ComputeRequestKind::TrBFV(trbfv_req) => { handle_trbfv_request(rng, cipher, trbfv_req, request, id) } - ComputeRequestKind::Zk(zk_req) => handle_zk_request(zk_prover, zk_req, request, id), + ComputeRequestKind::Zk(zk_req) => handle_zk_request(cipher, zk_prover, zk_req, request, id), } } @@ -342,6 +341,7 @@ fn handle_trbfv_request( } fn handle_zk_request( + cipher: Arc, zk_prover: Option>, zk_req: ZkRequest, request: ComputeRequest, @@ -363,9 +363,98 @@ fn handle_zk_request( ZkRequest::PkBfv(req) => timefunc("zk_pk_bfv", id, || { handle_pk_bfv_proof(&prover, req, request.clone()) }), + ZkRequest::PkGeneration(req) => timefunc("zk_pk_generation", id, || { + handle_pk_generation_proof(&prover, &cipher, req, request.clone()) + }), } } +/// Helper to reduce boilerplate for ZK errors +fn make_zk_error(request: &ComputeRequest, msg: String) -> ComputeRequestError { + ComputeRequestError::new( + ComputeRequestErrorKind::Zk(ZkEventError::InvalidParams(msg)), + request.clone(), + ) +} + +fn handle_pk_generation_proof( + prover: &ZkProver, + cipher: &Cipher, + req: PkGenerationProofRequest, + request: ComputeRequest, +) -> Result { + // 1. Build BFV parameters from the threshold preset + let params = BfvParamSet::from(req.params_preset.clone()).build_arc(); + + // 2. Decrypt sensitive witness fields + let sk_bytes = req + .sk + .access_raw(cipher) + .map_err(|e| make_zk_error(&request, format!("sk decrypt: {}", e)))?; + let eek_bytes = req + .eek + .access_raw(cipher) + .map_err(|e| make_zk_error(&request, format!("eek decrypt: {}", e)))?; + let e_sm_bytes = req + .e_sm + .access_raw(cipher) + .map_err(|e| make_zk_error(&request, format!("e_sm decrypt: {}", e)))?; + + // 3. Deserialize raw polynomial bytes → Poly + let pk0_share_poly = try_poly_from_bytes(&req.pk0_share, ¶ms) + .map_err(|e| make_zk_error(&request, format!("pk0_share: {}", e)))?; + + let a_poly = try_poly_from_bytes(&req.a, ¶ms) + .map_err(|e| make_zk_error(&request, format!("a: {}", e)))?; + + let sk_poly = try_poly_from_bytes(&sk_bytes, ¶ms) + .map_err(|e| make_zk_error(&request, format!("sk: {}", e)))?; + + let eek_poly = try_poly_from_bytes(&eek_bytes, ¶ms) + .map_err(|e| make_zk_error(&request, format!("eek: {}", e)))?; + + let e_sm_poly = try_poly_from_bytes(&e_sm_bytes, ¶ms) + .map_err(|e| make_zk_error(&request, format!("e_sm: {}", e)))?; + + // 3. Convert Poly → CrtPolynomial + let pk0_share = CrtPolynomial::from_fhe_polynomial(&pk0_share_poly); + let a = CrtPolynomial::from_fhe_polynomial(&a_poly); + let sk = CrtPolynomial::from_fhe_polynomial(&sk_poly); + let eek = CrtPolynomial::from_fhe_polynomial(&eek_poly); + let e_sm = CrtPolynomial::from_fhe_polynomial(&e_sm_poly); + + // 4. Build circuit data + let committee = req.committee_size.values(); + let circuit_data = PkGenerationCircuitData { + committee, + pk0_share, + a, + eek, + e_sm, + sk, + }; + + // 5. Generate proof via Provable trait + let circuit = PkGenerationCircuit; + 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| { + ComputeRequestError::new( + ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), + request.clone(), + ) + })?; + + // 6. Return response + Ok(ComputeResponse::zk( + ZkResponse::PkGeneration(PkGenerationProofResponse::new(proof)), + request.correlation_id, + request.e3_id, + )) +} + fn handle_pk_bfv_proof( prover: &ZkProver, req: PkBfvProofRequest, diff --git a/crates/test-helpers/src/usecase_helpers.rs b/crates/test-helpers/src/usecase_helpers.rs index 6f1460b384..54b3039311 100644 --- a/crates/test-helpers/src/usecase_helpers.rs +++ b/crates/test-helpers/src/usecase_helpers.rs @@ -39,8 +39,6 @@ pub struct GeneratedShares { pub fn generate_shares_hash_map( trbfv_config: &TrBFVConfig, - esi_per_ct: u64, - error_size: &ArcBytes, crp: &CommonRandomPoly, rng: &SharedRng, cipher: &Cipher, @@ -62,22 +60,28 @@ pub fn generate_shares_hash_map( let mut shares_hash_map = HashMap::new(); for party_id in 0u64..threshold_n as u64 { - let GenEsiSssResponse { esi_sss } = gen_esi_sss( + let GenPkShareAndSkSssResponse { + sk_sss, + pk_share, + e_sm_raw, + .. + } = gen_pk_share_and_sk_sss( &rng, &cipher, - GenEsiSssRequest { - esi_per_ct, - error_size: error_size.clone(), + GenPkShareAndSkSssRequest { trbfv_config: trbfv_config.clone(), + crp: ArcBytes::from_bytes(&crp.to_bytes()), + lambda: 40, + num_ciphertexts: 1, }, )?; - let GenPkShareAndSkSssResponse { sk_sss, pk_share } = gen_pk_share_and_sk_sss( + let GenEsiSssResponse { esi_sss } = gen_esi_sss( &rng, &cipher, - GenPkShareAndSkSssRequest { + GenEsiSssRequest { trbfv_config: trbfv_config.clone(), - crp: ArcBytes::from_bytes(&crp.to_bytes()), + e_sm_raw: ArcBytes::from_bytes(&e_sm_raw.access_raw(&cipher)?), }, )?; diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index d6d6da1eb9..f3078e5154 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -100,6 +100,8 @@ async fn setup_test_zk_backend() -> (ZkBackend, tempfile::TempDir) { .join("zk-prover") .join("tests") .join("fixtures"); + + // Copy T0 (pk) circuit let pk_circuit_dir = circuits_dir.join("dkg").join("pk"); tokio::fs::create_dir_all(&pk_circuit_dir).await.unwrap(); tokio::fs::copy(fixtures_dir.join("pk.json"), pk_circuit_dir.join("pk.json")) @@ -109,6 +111,24 @@ async fn setup_test_zk_backend() -> (ZkBackend, tempfile::TempDir) { .await .unwrap(); + // Copy T1 (pk_generation) circuit + let pk_gen_circuit_dir = circuits_dir.join("threshold").join("pk_generation"); + tokio::fs::create_dir_all(&pk_gen_circuit_dir) + .await + .unwrap(); + tokio::fs::copy( + fixtures_dir.join("pk_generation.json"), + pk_gen_circuit_dir.join("pk_generation.json"), + ) + .await + .unwrap(); + tokio::fs::copy( + fixtures_dir.join("pk_generation.vk"), + pk_gen_circuit_dir.join("pk_generation.vk"), + ) + .await + .unwrap(); + let backend = ZkBackend::new( BBPath::Default(bb_binary), circuits_dir, diff --git a/crates/trbfv/src/gen_esi_sss.rs b/crates/trbfv/src/gen_esi_sss.rs index 26c14deddc..7eb473f9f4 100644 --- a/crates/trbfv/src/gen_esi_sss.rs +++ b/crates/trbfv/src/gen_esi_sss.rs @@ -5,14 +5,14 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use crate::{ + helpers::try_poly_from_bytes, shares::{Encrypted, SharedSecret}, TrBFVConfig, }; use anyhow::{Context, Result}; use e3_crypto::Cipher; use e3_utils::{utility_types::ArcBytes, SharedRng}; -use fhe::trbfv::{smudging::SmudgingNoiseGenerator, ShareManager}; -use num_bigint::BigUint; +use fhe::trbfv::ShareManager; use serde::{Deserialize, Serialize}; use tracing::info; @@ -20,16 +20,13 @@ use tracing::info; pub struct GenEsiSssRequest { /// TrBFV configuration pub trbfv_config: TrBFVConfig, - /// Error Size extracted from the E3 Program Parameters - pub error_size: ArcBytes, - /// Smudging noise per ciphertext - pub esi_per_ct: u64, + /// This is pre-generated smudging noise. + pub e_sm_raw: ArcBytes, } struct InnerRequest { pub trbfv_config: TrBFVConfig, - pub error_size: BigUint, - pub esi_per_ct: u64, + pub e_sm_raw: ArcBytes, } impl TryFrom for InnerRequest { @@ -37,8 +34,7 @@ impl TryFrom for InnerRequest { fn try_from(value: GenEsiSssRequest) -> std::result::Result { Ok(InnerRequest { trbfv_config: value.trbfv_config, - error_size: BigUint::from_bytes_be(&value.error_size), - esi_per_ct: value.esi_per_ct, + e_sm_raw: value.e_sm_raw, }) } } @@ -68,6 +64,10 @@ struct InnerResponse { pub esi_sss: Vec, } +/// This function generates secret shares for the smudging noise (esi_sss) using the provided pre-generated smudging noise polynomial (e_sm_raw). +/// When implementing multiple ciphertext outputs decryptions, we are going to need multiple smudging noise polynomials, +/// so we are generating a vector of smudging noise secret shares (esi_sss) instead of just one in anticipation of that change. +/// We will also need to ensure that all of them are committed to the pk_generation circuit. pub fn gen_esi_sss( rng: &SharedRng, cipher: &Cipher, @@ -79,28 +79,19 @@ pub fn gen_esi_sss( let params = req.trbfv_config.params(); let threshold = req.trbfv_config.threshold() as usize; let num_ciphernodes = req.trbfv_config.num_parties() as usize; - let error_size = req.error_size; - let esi_per_ct = req.esi_per_ct as usize; - let esi_sss: Vec = (0..esi_per_ct) - .map(|_| -> Result<_> { - info!("gen_esi_sss:mapping..."); - let generator = SmudgingNoiseGenerator::new(params.clone(), error_size.clone()); - info!("gen_esi_sss:generate_smudging_error..."); - let esi_coeffs = { - generator - .generate_smudging_error(&mut *rng.lock().unwrap()) - .context("Failed to generate smudging error")? - }; - let mut share_manager = ShareManager::new(num_ciphernodes, threshold, params.clone()); - let esi_poly = share_manager.bigints_to_poly(&esi_coeffs)?; - info!("gen_esi_sss:generate_secret_shares_from_poly..."); - Ok(SharedSecret::from({ - share_manager - .generate_secret_shares_from_poly(esi_poly, &mut *rng.lock().unwrap()) - .context("Failed to generate secret shares from poly")? - })) - }) - .collect::>()?; + let e_sm_raw = req.e_sm_raw; + + info!("gen_esi_sss:mapping..."); + let e_sm_poly = try_poly_from_bytes(&e_sm_raw, ¶ms)?; + let mut share_manager = ShareManager::new(num_ciphernodes, threshold, params.clone()); + + info!("gen_esi_sss:generate_smudging_error..."); + + let esi_sss = vec![SharedSecret::from( + share_manager + .generate_secret_shares_from_poly(e_sm_poly.into(), &mut *rng.lock().unwrap()) + .context("Failed to generate secret shares from poly")?, + )]; info!("gen_esi_sss:returning..."); diff --git a/crates/trbfv/src/gen_pk_share_and_sk_sss.rs b/crates/trbfv/src/gen_pk_share_and_sk_sss.rs index 0e4c9856eb..8877f86ec3 100644 --- a/crates/trbfv/src/gen_pk_share_and_sk_sss.rs +++ b/crates/trbfv/src/gen_pk_share_and_sk_sss.rs @@ -11,15 +11,16 @@ use crate::{ TrBFVConfig, }; use anyhow::Result; -use e3_crypto::Cipher; +use e3_crypto::{Cipher, SensitiveBytes}; use e3_utils::{utility_types::ArcBytes, SharedRng}; use fhe::{ bfv::SecretKey, mbfv::{CommonRandomPoly, PublicKeyShare}, - trbfv::ShareManager, + trbfv::{ShareManager, TRBFV}, }; use fhe_traits::Serialize as FheSerialize; use serde::{Deserialize, Serialize}; +use std::ops::Deref; use tracing::info; #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] @@ -28,11 +29,17 @@ pub struct GenPkShareAndSkSssRequest { pub trbfv_config: TrBFVConfig, /// Crp pub crp: ArcBytes, + /// Statistical security parameter λ for smudging noise generation. + pub lambda: usize, + /// Number of ciphertexts (z) for smudging noise generation. + pub num_ciphertexts: usize, } struct InnerRequest { pub trbfv_config: TrBFVConfig, pub crp: CommonRandomPoly, + pub lambda: usize, + pub num_ciphertexts: usize, } impl TryFrom for InnerRequest { @@ -43,6 +50,8 @@ impl TryFrom for InnerRequest { Ok(InnerRequest { trbfv_config: value.trbfv_config, crp, + lambda: value.lambda, + num_ciphertexts: value.num_ciphertexts, }) } } @@ -53,6 +62,16 @@ pub struct GenPkShareAndSkSssResponse { pub pk_share: ArcBytes, /// SecretKey Shamir Shares for other parties pub sk_sss: Encrypted, + /// Raw pk0 share polynomial (RNS form) for ZK proof generation (T1a). + pub pk0_share_raw: ArcBytes, + /// Raw common random polynomial (RNS form) for ZK proof generation (T1a). + pub a_raw: ArcBytes, + /// Raw secret key polynomial (RNS form) for ZK proof generation (T1a) — encrypted at rest. + pub sk_raw: SensitiveBytes, + /// Raw error polynomial from key generation (RNS form) for ZK proof generation (T1a) — encrypted at rest. + pub eek_raw: SensitiveBytes, + /// Raw smudging noise polynomial (RNS form) for ZK proof generation (C1) — encrypted at rest. + pub e_sm_raw: SensitiveBytes, } impl TryFrom<(InnerResponse, &Cipher)> for GenPkShareAndSkSssResponse { @@ -62,15 +81,33 @@ impl TryFrom<(InnerResponse, &Cipher)> for GenPkShareAndSkSssResponse { ) -> std::result::Result { let pk_share = ArcBytes::from_bytes(&value.pk_share.to_bytes()); let sk_sss = Encrypted::new(value.sk_sss, cipher)?; - Ok(GenPkShareAndSkSssResponse { pk_share, sk_sss }) + Ok(GenPkShareAndSkSssResponse { + pk_share, + sk_sss, + pk0_share_raw: value.pk0_share_raw, + a_raw: value.a_raw, + sk_raw: SensitiveBytes::new(value.sk_raw.to_vec(), cipher)?, + eek_raw: SensitiveBytes::new(value.eek_raw.to_vec(), cipher)?, + e_sm_raw: SensitiveBytes::new(value.e_sm_raw.to_vec(), cipher)?, + }) } } struct InnerResponse { - /// PublicKey share for this node + /// Aggregation-compatible PublicKeyShare for this node. pub pk_share: PublicKeyShare, - /// SecretKey Shamir Shares for other parties + /// Secret key Shamir shares for other parties. pub sk_sss: SharedSecret, + /// Raw pk0 share polynomial bytes for ZK proof. + pub pk0_share_raw: ArcBytes, + /// Raw CRP polynomial bytes for ZK proof. + pub a_raw: ArcBytes, + /// Raw secret key polynomial bytes for ZK proof. + pub sk_raw: ArcBytes, + /// Raw error polynomial bytes for ZK proof. + pub eek_raw: ArcBytes, + /// Raw smudging noise polynomial bytes for ZK proof. + pub e_sm_raw: ArcBytes, } pub fn gen_pk_share_and_sk_sss( @@ -91,17 +128,49 @@ pub fn gen_pk_share_and_sk_sss( num_ciphernodes, threshold ); let sk_share = { SecretKey::random(¶ms, &mut *rng.lock().unwrap()) }; - let pk_share = { PublicKeyShare::new(&sk_share, crp.clone(), &mut *rng.lock().unwrap())? }; + let (pk0_share, a, sk_poly, eek) = + { PublicKeyShare::new_extended(&sk_share, crp.clone(), &mut *rng.lock().unwrap())? }; + + let pk_share = PublicKeyShare::deserialize(&pk0_share.to_bytes(), ¶ms, crp.clone())?; + + // Generate smudging noise + let trbfv = TRBFV::new(num_ciphernodes as usize, threshold as usize, params.clone())?; + let share_manager_for_esm = + ShareManager::new(num_ciphernodes as usize, threshold as usize, params.clone()); + let esi_coeffs = trbfv.generate_smudging_error( + req.num_ciphertexts, + req.lambda, + &mut *rng.lock().unwrap(), + )?; + let e_sm_rns = share_manager_for_esm.bigints_to_poly(&esi_coeffs)?; + let e_sm_raw = ArcBytes::from_bytes(&e_sm_rns.deref().to_bytes()); + + let pk0_share_raw = ArcBytes::from_bytes(&pk0_share.to_bytes()); + let a_raw = ArcBytes::from_bytes(&a.to_bytes()); + let eek_raw = ArcBytes::from_bytes(&eek.to_bytes()); let mut share_manager = ShareManager::new(num_ciphernodes as usize, threshold as usize, params.clone()); let sk_poly = share_manager.coeffs_to_poly_level0(sk_share.coeffs.clone().as_ref())?; + let sk_raw = ArcBytes::from_bytes(&sk_poly.to_bytes()); info!("gen_pk_share_and_sk_sss:generate_secret_shares_from_poly..."); let sk_sss = SharedSecret::from({ share_manager.generate_secret_shares_from_poly(sk_poly, &mut *rng.lock().unwrap())? }); - (InnerResponse { pk_share, sk_sss }, cipher).try_into() + ( + InnerResponse { + pk_share, + sk_sss, + pk0_share_raw, + a_raw, + sk_raw, + eek_raw, + e_sm_raw, + }, + cipher, + ) + .try_into() } diff --git a/crates/trbfv/tests/integration.rs b/crates/trbfv/tests/integration.rs index 72fb33d71f..ea25d937b7 100644 --- a/crates/trbfv/tests/integration.rs +++ b/crates/trbfv/tests/integration.rs @@ -24,12 +24,10 @@ use e3_trbfv::{ calculate_threshold_decryption, CalculateThresholdDecryptionRequest, CalculateThresholdDecryptionResponse, }, - helpers::calculate_error_size, TrBFVConfig, }; use e3_utils::{to_ordered_vec, ArcBytes}; use fhe_traits::Serialize; -use num_bigint::BigUint; use rand::SeedableRng; use rand_chacha::ChaCha20Rng; @@ -51,21 +49,10 @@ async fn test_trbfv_isolation() -> Result<()> { // E3Parameters let threshold_m = 2; let threshold_n = 5; - let esi_per_ct = 3; - // WARNING: INSECURE SECURITY PARAMETER LAMBDA. - // This is just for INSECURE parameter set. - // This is not secure and should not be used in production. - // For production use lambda = 80. - let lambda = 2; + let seed = create_seed_from_u64(123); let cipher = Arc::new(Cipher::from_password("I am the music man.").await?); - let error_size = ArcBytes::from_bytes(&BigUint::to_bytes_be(&calculate_error_size( - params_raw.clone(), - threshold_n, - esi_per_ct, - lambda, - )?)); let trbfv_config = TrBFVConfig::new(params, threshold_n as u64, threshold_m as u64); let crp_raw = create_crp( @@ -74,14 +61,8 @@ async fn test_trbfv_isolation() -> Result<()> { ); // let crp = ArcBytes::from_bytes(crp_raw.to_bytes()); - let generated = usecase_helpers::generate_shares_hash_map( - &trbfv_config, - esi_per_ct as u64, - &error_size, - &crp_raw, - &rng, - &cipher, - )?; + let generated = + usecase_helpers::generate_shares_hash_map(&trbfv_config, &crp_raw, &rng, &cipher)?; let pubkey = usecase_helpers::get_public_key(&generated.shares, trbfv_config.params(), &crp_raw)?; diff --git a/crates/zk-helpers/src/ciphernodes_committee.rs b/crates/zk-helpers/src/ciphernodes_committee.rs index b1c921e36f..da5a706e55 100644 --- a/crates/zk-helpers/src/ciphernodes_committee.rs +++ b/crates/zk-helpers/src/ciphernodes_committee.rs @@ -4,10 +4,12 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +use serde::{Deserialize, Serialize}; + /// @todo this must be integrated inside Ciphernodes & Smart Contract /// instead of being a separate type in here. The pvss crate should import this and /// the default values that must be used and shared among the whole enclave repository. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum CiphernodesCommitteeSize { /// Small committee size (fast local/testing). Small, diff --git a/crates/zk-prover/src/actors/proof_request.rs b/crates/zk-prover/src/actors/proof_request.rs index af815b23b8..2cf1d8d35f 100644 --- a/crates/zk-prover/src/actors/proof_request.rs +++ b/crates/zk-prover/src/actors/proof_request.rs @@ -12,9 +12,10 @@ use alloy::signers::local::PrivateKeySigner; use e3_events::{ BusHandle, ComputeRequest, ComputeRequestError, ComputeRequestErrorKind, ComputeResponse, ComputeResponseKind, CorrelationId, E3id, EnclaveEvent, EnclaveEventData, EncryptionKey, - EncryptionKeyCreated, EncryptionKeyPending, EventPublisher, EventSubscriber, EventType, - PkBfvProofRequest, ProofPayload, ProofType, SignedProofPayload, TypedEvent, ZkRequest, - ZkResponse, + EncryptionKeyCreated, EncryptionKeyPending, EventContext, EventPublisher, EventSubscriber, + EventType, PkBfvProofRequest, PkGenerationProofSigned, Proof, ProofPayload, ProofType, + Sequenced, SignedProofPayload, ThresholdShare, ThresholdShareCreated, ThresholdSharePending, + TypedEvent, ZkRequest, ZkResponse, }; use e3_utils::NotifySync; use tracing::{error, info}; @@ -25,6 +26,12 @@ struct PendingProofRequest { key: Arc, } +#[derive(Clone, Debug)] +struct PendingThresholdShareProof { + e3_id: E3id, + full_share: Arc, +} + /// Core actor that handles encryption key proof requests. /// /// Proofs are always wrapped in a [`SignedProofPayload`] before being published, @@ -34,6 +41,7 @@ pub struct ProofRequestActor { bus: BusHandle, signer: PrivateKeySigner, pending: HashMap, + pending_threshold: HashMap, } impl ProofRequestActor { @@ -42,6 +50,7 @@ impl ProofRequestActor { bus: bus.clone(), signer, pending: HashMap::new(), + pending_threshold: HashMap::new(), } } @@ -50,6 +59,7 @@ impl ProofRequestActor { bus.subscribe(EventType::EncryptionKeyPending, addr.clone().into()); bus.subscribe(EventType::ComputeResponse, addr.clone().into()); bus.subscribe(EventType::ComputeRequestError, addr.clone().into()); + bus.subscribe(EventType::ThresholdSharePending, addr.clone().into()); addr } @@ -80,24 +90,141 @@ impl ProofRequestActor { } } + fn handle_threshold_share_pending(&mut self, msg: TypedEvent) { + let (msg, ec) = msg.into_components(); + let correlation_id = CorrelationId::new(); + self.pending_threshold.insert( + correlation_id, + PendingThresholdShareProof { + e3_id: msg.e3_id.clone(), + full_share: msg.full_share.clone(), + }, + ); + + let request = ComputeRequest::zk( + ZkRequest::PkGeneration(msg.proof_request), + correlation_id, + msg.e3_id, + ); + + info!("Requesting T1 PkGeneration proof generation"); + if let Err(err) = self.bus.publish(request, ec) { + error!("Failed to publish ZK proof request: {err}"); + self.pending_threshold.remove(&correlation_id); + } + } + fn handle_compute_response(&mut self, msg: TypedEvent) { let (msg, ec) = msg.into_components(); - let ComputeResponseKind::Zk(ZkResponse::PkBfv(resp)) = msg.response else { + match &msg.response { + ComputeResponseKind::Zk(ZkResponse::PkBfv(resp)) => { + self.handle_pk_bfv_response(&msg.correlation_id, resp.proof.clone(), &ec); + } + ComputeResponseKind::Zk(ZkResponse::PkGeneration(resp)) => { + self.handle_pk_generation_response(&msg.correlation_id, resp.proof.clone(), &ec); + } + _ => {} + } + } + + fn handle_pk_generation_response( + &mut self, + correlation_id: &CorrelationId, + proof: Proof, + ec: &EventContext, + ) { + let Some(pending) = self.pending_threshold.remove(correlation_id) else { return; }; - let Some(pending) = self.pending.remove(&msg.correlation_id) else { + let payload = ProofPayload { + e3_id: pending.e3_id.clone(), + proof_type: ProofType::T1PkGeneration, + proof: proof.clone(), + }; + + let signed = match SignedProofPayload::sign(payload, &self.signer) { + Ok(s) => { + info!( + "Signed T1 PkGeneration proof for party {} (signer: {})", + pending.full_share.party_id, + self.signer.address() + ); + s + } + Err(err) => { + error!("Failed to sign T1 PkGeneration proof payload: {err} — shares will not be published"); + return; + } + }; + + let party_id = pending.full_share.party_id; + let e3_id = pending.e3_id.clone(); + + info!( + "Publishing PkGenerationProofSigned for E3 {} party {}", + pending.e3_id, party_id + ); + if let Err(err) = self.bus.publish( + PkGenerationProofSigned { + e3_id: pending.e3_id, + party_id, + signed_proof: signed, + }, + ec.clone(), + ) { + error!("Failed to publish PkGenerationProofSigned: {err}"); + } + + let share = &pending.full_share; + + // Publish per-party shares + let num_parties = share.num_parties(); + info!( + "Publishing ThresholdShareCreated for E3 {} to {} parties", + e3_id, num_parties + ); + + for recipient_party_id in 0..num_parties { + if let Some(party_share) = share.extract_for_party(recipient_party_id) { + if let Err(err) = self.bus.publish( + ThresholdShareCreated { + e3_id: e3_id.clone(), + share: Arc::new(party_share), + target_party_id: recipient_party_id as u64, + external: false, + }, + ec.clone(), + ) { + error!( + "Failed to publish ThresholdShareCreated for party {}: {err}", + recipient_party_id + ); + } + } else { + error!("Failed to extract share for party {}", recipient_party_id); + } + } + } + + fn handle_pk_bfv_response( + &mut self, + correlation_id: &CorrelationId, + proof: Proof, + ec: &EventContext, + ) { + let Some(pending) = self.pending.remove(&correlation_id) else { return; }; let mut key = (*pending.key).clone(); - key.proof = Some(resp.proof.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(), proof_type: ProofType::T0PkBfv, - proof: resp.proof.clone(), + proof: proof.clone(), }; match SignedProofPayload::sign(payload, &self.signer) { @@ -121,7 +248,7 @@ impl ProofRequestActor { key: Arc::new(key), external: false, }, - ec, + ec.clone(), ) { error!("Failed to publish EncryptionKeyCreated: {err}"); } @@ -134,10 +261,16 @@ impl ProofRequestActor { if let Some(pending) = self.pending.remove(msg.correlation_id()) { error!( - "ZK proof request failed for E3 {}: {err} — key will not be published without proof", + "T0 proof request failed for E3 {}: {err} — key will not be published without proof", pending.e3_id ); } + + if let Some(pending) = self.pending_threshold.remove(msg.correlation_id()) { + error!( + "T1 PkShareGeneration proof request failed for E3 {}: {err} — threshold share will not be published without proof", pending.e3_id + ) + } } } @@ -150,10 +283,14 @@ impl Handler for ProofRequestActor { fn handle(&mut self, msg: EnclaveEvent, ctx: &mut Self::Context) -> Self::Result { let (msg, ec) = msg.into_components(); + match msg { EnclaveEventData::EncryptionKeyPending(data) => { self.notify_sync(ctx, TypedEvent::new(data, ec)) } + EnclaveEventData::ThresholdSharePending(data) => { + self.notify_sync(ctx, TypedEvent::new(data, ec)) + } EnclaveEventData::ComputeResponse(data) => { self.notify_sync(ctx, TypedEvent::new(data, ec)) } @@ -177,6 +314,18 @@ impl Handler> for ProofRequestActor { } } +impl Handler> for ProofRequestActor { + type Result = (); + + fn handle( + &mut self, + msg: TypedEvent, + _ctx: &mut Self::Context, + ) -> Self::Result { + self.handle_threshold_share_pending(msg); + } +} + impl Handler> for ProofRequestActor { type Result = (); diff --git a/crates/zk-prover/src/traits.rs b/crates/zk-prover/src/traits.rs index 5a6c7d3c86..f7f03fcb30 100644 --- a/crates/zk-prover/src/traits.rs +++ b/crates/zk-prover/src/traits.rs @@ -98,7 +98,6 @@ pub trait Provable: Send + Sync { "Verifying proof for circuit {} with e3_id {} and party_id {}", proof.circuit, e3_id, party_id ); - println!("Proof details: {:?}", proof); prover.verify(proof, e3_id, party_id) } } diff --git a/examples/CRISP/server/.env.example b/examples/CRISP/server/.env.example index 934e446b43..0d81df449f 100644 --- a/examples/CRISP/server/.env.example +++ b/examples/CRISP/server/.env.example @@ -23,7 +23,7 @@ FEE_TOKEN_ADDRESS="0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" # After this interval, the computation phase starts automatically # After activation + this interval, ciphernodes are then not responsing to # any more decryption requests -E3_DURATION=100 +E3_DURATION=130 E3_THRESHOLD_MIN=2 E3_THRESHOLD_MAX=5 diff --git a/examples/CRISP/test/crisp.spec.ts b/examples/CRISP/test/crisp.spec.ts index f5e288b520..27758a508e 100644 --- a/examples/CRISP/test/crisp.spec.ts +++ b/examples/CRISP/test/crisp.spec.ts @@ -110,7 +110,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 = 80_000 + const WAIT = 130_000 log(`waiting for ${WAIT}ms...`) await page.waitForTimeout(WAIT) log(`clicking historic polls button...`) 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 bfcb8a4f4c..a9daa05c33 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json @@ -890,5 +890,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IBondingRegistry.sol", - "buildInfoId": "solc-0_8_28-d7b7e823e5eb177cd42a96206dc5c5f8387609a9" + "buildInfoId": "solc-0_8_28-e60a5d7c133605edcf61acdd5ba43ab44ee0928e" } \ 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 a953710b9f..7c6ad1ce87 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json @@ -590,5 +590,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ICiphernodeRegistry.sol", - "buildInfoId": "solc-0_8_28-d7b7e823e5eb177cd42a96206dc5c5f8387609a9" + "buildInfoId": "solc-0_8_28-e60a5d7c133605edcf61acdd5ba43ab44ee0928e" } \ 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 4578c7e43d..28c6fdb611 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json @@ -1202,5 +1202,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IEnclave.sol", - "buildInfoId": "solc-0_8_28-d7b7e823e5eb177cd42a96206dc5c5f8387609a9" + "buildInfoId": "solc-0_8_28-e60a5d7c133605edcf61acdd5ba43ab44ee0928e" } \ 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 1cfee6a53d..c995f68ebf 100644 --- a/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json +++ b/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json @@ -1143,80 +1143,72 @@ "type": "function" } ], - "bytecode": "0x610180604052348015610010575f5ffd5b506040516129d73803806129d783398101604081905261002f9161037d565b82816040518060400160405280601481526020017f456e636c617665205469636b657420546f6b656e00000000000000000000000081525080604051806040016040528060018152602001603160f81b8152506040518060400160405280601481526020017f456e636c617665205469636b657420546f6b656e0000000000000000000000008152506040518060400160405280600381526020016245544b60e81b81525081600390816100e3919061045f565b5060046100f0828261045f565b5061010091508390506005610226565b6101205261010f816006610226565b61014052815160208084019190912060e052815190820120610100524660a05261019b60e05161010051604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201529081019290925260608201524660808201523060a08201525f9060c00160405160208183030381529060405280519060200120905090565b60805250503060c052506001600160a01b0381166101d357604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b6101dc81610258565b50306001600160a01b038216036102085760405163438d6fe360e01b81523060048201526024016101ca565b6001600160a01b03166101605261021e826102a9565b505050610571565b5f6020835110156102415761023a836102fa565b9050610252565b8161024c848261045f565b5060ff90505b92915050565b600b80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b6102b1610337565b6001600160a01b0381166102d85760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b5f5f829050601f81511115610324578260405163305a27a960e01b81526004016101ca9190610519565b805161032f8261054e565b179392505050565b600b546001600160a01b031633146103645760405163118cdaa760e01b81523360048201526024016101ca565b565b6001600160a01b038116811461037a575f5ffd5b50565b5f5f5f6060848603121561038f575f5ffd5b835161039a81610366565b60208501519093506103ab81610366565b60408501519092506103bc81610366565b809150509250925092565b634e487b7160e01b5f52604160045260245ffd5b600181811c908216806103ef57607f821691505b60208210810361040d57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561045a57805f5260205f20601f840160051c810160208510156104385750805b601f840160051c820191505b81811015610457575f8155600101610444565b50505b505050565b81516001600160401b03811115610478576104786103c7565b61048c8161048684546103db565b84610413565b6020601f8211600181146104be575f83156104a75750848201515b5f19600385901b1c1916600184901b178455610457565b5f84815260208120601f198516915b828110156104ed57878501518255602094850194600190920191016104cd565b508482101561050a57868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b8051602080830151919081101561040d575f1960209190910360031b1b16919050565b60805160a05160c05160e051610100516101205161014051610160516123e86105ef5f395f8181610368015281816106400152818161084101528181610c6801528181610dcd0152610e7f01525f6112a401525f61127701525f610fdf01525f610fb701525f610f1201525f610f3c01525f610f6601526123e85ff3fe608060405234801561000f575f5ffd5b506004361061021b575f3560e01c8063715018a61161012357806395d89b41116100b8578063c3cda52011610088578063dd62ed3e1161006e578063dd62ed3e146104dd578063f1127ed814610515578063f2fde38b14610554575f5ffd5b8063c3cda520146104bc578063d505accf146104ca575f5ffd5b806395d89b411461047b5780639ab24eb014610483578063a9059cbb14610496578063a91ee0dc146104a9575f5ffd5b806385bc898c116100f357806385bc898c146104255780638da5cb5b146104385780638e539e8c1461044957806391ddadf41461045c575f5ffd5b8063715018a6146103dc5780637b103999146103e45780637ecebe00146103f757806384b0196e1461040a575f5ffd5b80633644e515116101b35780635c19a95c116101835780636f307dc3116101695780636f307dc3146103665780636fcfff451461038c57806370a08231146103b4575f5ffd5b80635c19a95c1461034057806368a9674d14610353575f5ffd5b80633644e515146102da5780633a46b1a8146102e25780634bf5d7e9146102f5578063587cde1e146102fd575f5ffd5b8063205c2878116101ee578063205c28781461028757806323b872dd1461029a5780632f4f21e2146102ad578063313ce567146102c0575f5ffd5b806306fdde031461021f578063095ea7b31461023d578063117de2fd1461026057806318160ddd14610275575b5f5ffd5b610227610567565b604051610234919061202b565b60405180910390f35b61025061024b366004612053565b6105f7565b6040519015158152602001610234565b61027361026e366004612053565b610610565b005b6002545b604051908152602001610234565b610250610295366004612053565b61066a565b6102506102a836600461207b565b6106a8565b6102506102bb366004612053565b6106cb565b6102c861072d565b60405160ff9091168152602001610234565b61027961073b565b6102796102f0366004612053565b610744565b61022761077e565b61032861030b3660046120b5565b6001600160a01b039081165f908152600860205260409020541690565b6040516001600160a01b039091168152602001610234565b61027361034e3660046120b5565b6107f6565b61025061036136600461207b565b61080f565b7f0000000000000000000000000000000000000000000000000000000000000000610328565b61039f61039a3660046120b5565b6108a4565b60405163ffffffff9091168152602001610234565b6102796103c23660046120b5565b6001600160a01b03165f9081526020819052604090205490565b6102736108ae565b600c54610328906001600160a01b031681565b6102796104053660046120b5565b6108c1565b6104126108cb565b60405161023497969594939291906120ce565b610273610433366004612053565b61090d565b600b546001600160a01b0316610328565b610279610457366004612164565b610942565b610464610966565b60405165ffffffffffff9091168152602001610234565b61022761096f565b6102796104913660046120b5565b61097e565b6102506104a4366004612053565b61099e565b6102736104b73660046120b5565b6109ab565b61027361034e366004612189565b6102736104d83660046121df565b610a09565b6102796104eb366004612247565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b610528610523366004612278565b610b44565b60408051825165ffffffffffff1681526020928301516001600160d01b03169281019290925201610234565b6102736105623660046120b5565b610b61565b606060038054610576906122b5565b80601f01602080910402602001604051908101604052809291908181526020018280546105a2906122b5565b80156105ed5780601f106105c4576101008083540402835291602001916105ed565b820191905f5260205f20905b8154815290600101906020018083116105d057829003601f168201915b5050505050905090565b5f33610604818585610b9e565b60019150505b92915050565b600c546001600160a01b0316331461063b57604051633217675b60e21b815260040160405180910390fd5b6106667f00000000000000000000000000000000000000000000000000000000000000008383610bb0565b5050565b600c545f906001600160a01b0316331461069757604051633217675b60e21b815260040160405180910390fd5b6106a18383610c24565b9392505050565b5f336106b5858285610c97565b6106c0858585610d13565b506001949350505050565b600c545f906001600160a01b031633146106f857604051633217675b60e21b815260040160405180910390fd5b6107028383610d70565b6001600160a01b038481165f908152600860205260409020549192501661060a5761060a8384610dfe565b5f610736610e7c565b905090565b5f610736610f06565b5f61076e6107518361102f565b6001600160a01b0385165f9081526009602052604090209061107d565b6001600160d01b03169392505050565b6060610788611130565b65ffffffffffff16610798610966565b65ffffffffffff16146107be576040516301bfc1c560e61b815260040160405180910390fd5b5060408051808201909152601d81527f6d6f64653d626c6f636b6e756d6265722666726f6d3d64656661756c74000000602082015290565b604051635e81118160e11b815260040160405180910390fd5b600c545f906001600160a01b0316331461083c57604051633217675b60e21b815260040160405180910390fd5b6108687f000000000000000000000000000000000000000000000000000000000000000085308561113a565b6108728383611173565b6001600160a01b038381165f908152600860205260409020541661089a5761089a8384610dfe565b5060019392505050565b5f61060a826111a7565b6108b66111c8565b6108bf5f6111f5565b565b5f61060a82611253565b5f6060805f5f5f60606108dc611270565b6108e461129d565b604080515f80825260208201909252600f60f81b9b939a50919850469750309650945092509050565b600c546001600160a01b0316331461093857604051633217675b60e21b815260040160405180910390fd5b61066682826112ca565b5f61095761094f8361102f565b600a9061107d565b6001600160d01b031692915050565b5f610736611130565b606060048054610576906122b5565b6001600160a01b0381165f908152600960205260408120610957906112fe565b5f33610604818585610d13565b6109b36111c8565b6001600160a01b0381166109da5760405163d92e233d60e01b815260040160405180910390fd5b600c805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b83421115610a325760405163313c898160e11b8152600481018590526024015b60405180910390fd5b5f7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9888888610a7d8c6001600160a01b03165f90815260076020526040902080546001810190915590565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090505f610ad782611338565b90505f610ae682878787611364565b9050896001600160a01b0316816001600160a01b031614610b2d576040516325c0072360e11b81526001600160a01b0380831660048301528b166024820152604401610a29565b610b388a8a8a610b9e565b50505050505050505050565b604080518082019091525f80825260208201526106a18383611390565b610b696111c8565b6001600160a01b038116610b9257604051631e4fbdf760e01b81525f6004820152602401610a29565b610b9b816111f5565b50565b610bab83838360016113c4565b505050565b6040516001600160a01b03838116602483015260448201839052610bab91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050611496565b5f306001600160a01b03841603610c595760405163ec442f0560e01b81526001600160a01b0384166004820152602401610a29565b610c6333836112ca565b610c8e7f00000000000000000000000000000000000000000000000000000000000000008484610bb0565b50600192915050565b6001600160a01b038381165f908152600160209081526040808320938616835292905220545f19811015610d0d5781811015610cff57604051637dc7a0d960e11b81526001600160a01b03841660048201526024810182905260448101839052606401610a29565b610d0d84848484035f6113c4565b50505050565b6001600160a01b038316610d3c57604051634b637e8f60e11b81525f6004820152602401610a29565b6001600160a01b038216610d655760405163ec442f0560e01b81525f6004820152602401610a29565b610bab838383611502565b5f33308103610d9457604051634b637e8f60e11b8152306004820152602401610a29565b306001600160a01b03851603610dc85760405163ec442f0560e01b81526001600160a01b0385166004820152602401610a29565b610df47f000000000000000000000000000000000000000000000000000000000000000082308661113a565b6106048484611173565b6001600160a01b038281165f81815260086020526040808220805486861673ffffffffffffffffffffffffffffffffffffffff19821681179092559151919094169392849290917f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f9190a4610bab8183610e778661154b565b611568565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015610ef7575060408051601f3d908101601f19168201909252610ef4918101906122ed565b60015b610f015750601290565b919050565b5f306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016148015610f5e57507f000000000000000000000000000000000000000000000000000000000000000046145b15610f8857507f000000000000000000000000000000000000000000000000000000000000000090565b610736604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f0000000000000000000000000000000000000000000000000000000000000000918101919091527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a08201525f9060c00160405160208183030381529060405280519060200120905090565b5f5f611039610966565b90508065ffffffffffff16831061107457604051637669fc0f60e11b81526004810184905265ffffffffffff82166024820152604401610a29565b6106a1836116d1565b81545f90818160058111156110d9575f61109684611707565b6110a0908561231c565b5f8881526020902090915081015465ffffffffffff90811690871610156110c9578091506110d7565b6110d481600161232f565b92505b505b5f6110e68787858561186c565b905080156111235761110a876110fd60018461231c565b5f91825260209091200190565b54660100000000000090046001600160d01b0316611125565b5f5b979650505050505050565b5f610736436116d1565b6040516001600160a01b038481166024830152838116604483015260648201839052610d0d9186918216906323b872dd90608401610bdd565b6001600160a01b03821661119c5760405163ec442f0560e01b81525f6004820152602401610a29565b6106665f8383611502565b6001600160a01b0381165f9081526009602052604081205461060a906118cb565b600b546001600160a01b031633146108bf5760405163118cdaa760e01b8152336004820152602401610a29565b600b80546001600160a01b0383811673ffffffffffffffffffffffffffffffffffffffff19831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b6001600160a01b0381165f9081526007602052604081205461060a565b60606107367f000000000000000000000000000000000000000000000000000000000000000060056118fb565b60606107367f000000000000000000000000000000000000000000000000000000000000000060066118fb565b6001600160a01b0382166112f357604051634b637e8f60e11b81525f6004820152602401610a29565b610666825f83611502565b80545f90801561133057611317836110fd60018461231c565b54660100000000000090046001600160d01b03166106a1565b5f9392505050565b5f61060a611344610f06565b8360405161190160f01b8152600281019290925260228201526042902090565b5f5f5f5f611374888888886119a4565b9250925092506113848282611a6c565b50909695505050505050565b604080518082019091525f80825260208201526001600160a01b0383165f9081526009602052604090206106a19083611b24565b6001600160a01b0384166113ed5760405163e602df0560e01b81525f6004820152602401610a29565b6001600160a01b03831661141657604051634a1406b160e11b81525f6004820152602401610a29565b6001600160a01b038085165f9081526001602090815260408083209387168352929052208290558015610d0d57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161148891815260200190565b60405180910390a350505050565b5f5f60205f8451602086015f885af1806114b5576040513d5f823e3d81fd5b50505f513d915081156114cc5780600114156114d9565b6001600160a01b0384163b155b15610d0d57604051635274afe760e01b81526001600160a01b0385166004820152602401610a29565b6001600160a01b0383161580159061152257506001600160a01b03821615155b1561154057604051638cd22d1960e01b815260040160405180910390fd5b610bab838383611b94565b6001600160a01b0381165f9081526020819052604081205461060a565b816001600160a01b0316836001600160a01b03161415801561158957505f81115b15610bab576001600160a01b03831615611630576001600160a01b0383165f90815260096020526040812081906115cb90611bfa6115c686611c05565b611c38565b6001600160d01b031691506001600160d01b03169150846001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a7248383604051611625929190918252602082015260400190565b60405180910390a250505b6001600160a01b03821615610bab576001600160a01b0382165f908152600960205260408120819061166890611c706115c686611c05565b6001600160d01b031691506001600160d01b03169150836001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a72483836040516116c2929190918252602082015260400190565b60405180910390a25050505050565b5f65ffffffffffff821115611703576040516306dfcc6560e41b81526030600482015260248101839052604401610a29565b5090565b5f60018211611714575090565b816001700100000000000000000000000000000000821061173a5760809190911c9060401b5b6801000000000000000082106117555760409190911c9060201b5b640100000000821061176c5760209190911c9060101b5b6201000082106117815760109190911c9060081b5b61010082106117955760089190911c9060041b5b601082106117a85760049190911c9060021b5b600482106117b45760011b5b600302600190811c908185816117cc576117cc612342565b048201901c905060018185816117e4576117e4612342565b048201901c905060018185816117fc576117fc612342565b048201901c9050600181858161181457611814612342565b048201901c9050600181858161182c5761182c612342565b048201901c9050600181858161184457611844612342565b048201901c905061186381858161185d5761185d612342565b04821190565b90039392505050565b5f5b818310156118c3575f6118818484611c7b565b5f8781526020902090915065ffffffffffff86169082015465ffffffffffff1611156118af578092506118bd565b6118ba81600161232f565b93505b5061186e565b509392505050565b5f63ffffffff821115611703576040516306dfcc6560e41b81526020600482015260248101839052604401610a29565b606060ff83146119155761190e83611c95565b905061060a565b818054611921906122b5565b80601f016020809104026020016040519081016040528092919081815260200182805461194d906122b5565b80156119985780601f1061196f57610100808354040283529160200191611998565b820191905f5260205f20905b81548152906001019060200180831161197b57829003601f168201915b5050505050905061060a565b5f80807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411156119dd57505f91506003905082611a62565b604080515f808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015611a2e573d5f5f3e3d5ffd5b5050604051601f1901519150506001600160a01b038116611a5957505f925060019150829050611a62565b92505f91508190505b9450945094915050565b5f826003811115611a7f57611a7f612356565b03611a88575050565b6001826003811115611a9c57611a9c612356565b03611aba5760405163f645eedf60e01b815260040160405180910390fd5b6002826003811115611ace57611ace612356565b03611aef5760405163fce698f760e01b815260048101829052602401610a29565b6003826003811115611b0357611b03612356565b03610666576040516335e2f38360e21b815260048101829052602401610a29565b604080518082019091525f8082526020820152825f018263ffffffff1681548110611b5157611b5161236a565b5f9182526020918290206040805180820190915291015465ffffffffffff81168252660100000000000090046001600160d01b0316918101919091529392505050565b611b9f838383611cd2565b6001600160a01b038316611bef575f611bb760025490565b90506001600160d01b0380821115611bec57604051630e58ae9360e11b81526004810183905260248101829052604401610a29565b50505b610bab838383611df8565b5f6106a1828461237e565b5f6001600160d01b03821115611703576040516306dfcc6560e41b815260d0600482015260248101839052604401610a29565b5f5f611c63611c45610966565b611c5b611c51886112fe565b868863ffffffff16565b879190611e6d565b915091505b935093915050565b5f6106a1828461239d565b5f611c8960028484186123bc565b6106a19084841661232f565b60605f611ca183611e7a565b6040805160208082528183019092529192505f91906020820181803683375050509182525060208101929092525090565b6001600160a01b038316611cfc578060025f828254611cf1919061232f565b90915550611d6c9050565b6001600160a01b0383165f9081526020819052604090205481811015611d4e5760405163391434e360e21b81526001600160a01b03851660048201526024810182905260448101839052606401610a29565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b038216611d8857600280548290039055611da6565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051611deb91815260200190565b60405180910390a3505050565b6001600160a01b038316611e1a57611e17600a611c706115c684611c05565b50505b6001600160a01b038216611e3c57611e39600a611bfa6115c684611c05565b50505b6001600160a01b038381165f90815260086020526040808220548584168352912054610bab92918216911683611568565b5f80611c63858585611ea1565b5f60ff8216601f81111561060a57604051632cd44ac360e21b815260040160405180910390fd5b82545f9081908015611fa0575f611ebd876110fd60018561231c565b805490915065ffffffffffff80821691660100000000000090046001600160d01b0316908816821115611f0357604051632520601d60e01b815260040160405180910390fd5b8765ffffffffffff168265ffffffffffff1603611f3f57825465ffffffffffff1666010000000000006001600160d01b03891602178355611f92565b6040805180820190915265ffffffffffff808a1682526001600160d01b03808a1660208085019182528d54600181018f555f8f815291909120945191519092166601000000000000029216919091179101555b9450859350611c6892505050565b50506040805180820190915265ffffffffffff80851682526001600160d01b0380851660208085019182528854600181018a555f8a8152918220955192519093166601000000000000029190931617920191909155905081611c68565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f6106a16020830184611ffd565b80356001600160a01b0381168114610f01575f5ffd5b5f5f60408385031215612064575f5ffd5b61206d8361203d565b946020939093013593505050565b5f5f5f6060848603121561208d575f5ffd5b6120968461203d565b92506120a46020850161203d565b929592945050506040919091013590565b5f602082840312156120c5575f5ffd5b6106a18261203d565b60ff60f81b8816815260e060208201525f6120ec60e0830189611ffd565b82810360408401526120fe8189611ffd565b606084018890526001600160a01b038716608085015260a0840186905283810360c0850152845180825260208087019350909101905f5b81811015612153578351835260209384019390920191600101612135565b50909b9a5050505050505050505050565b5f60208284031215612174575f5ffd5b5035919050565b60ff81168114610b9b575f5ffd5b5f5f5f5f5f5f60c0878903121561219e575f5ffd5b6121a78761203d565b9550602087013594506040870135935060608701356121c58161217b565b9598949750929560808101359460a0909101359350915050565b5f5f5f5f5f5f5f60e0888a0312156121f5575f5ffd5b6121fe8861203d565b965061220c6020890161203d565b95506040880135945060608801359350608088013561222a8161217b565b9699959850939692959460a0840135945060c09093013592915050565b5f5f60408385031215612258575f5ffd5b6122618361203d565b915061226f6020840161203d565b90509250929050565b5f5f60408385031215612289575f5ffd5b6122928361203d565b9150602083013563ffffffff811681146122aa575f5ffd5b809150509250929050565b600181811c908216806122c957607f821691505b6020821081036122e757634e487b7160e01b5f52602260045260245ffd5b50919050565b5f602082840312156122fd575f5ffd5b81516106a18161217b565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561060a5761060a612308565b8082018082111561060a5761060a612308565b634e487b7160e01b5f52601260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b6001600160d01b03828116828216039081111561060a5761060a612308565b6001600160d01b03818116838216019081111561060a5761060a612308565b5f826123d657634e487b7160e01b5f52601260045260245ffd5b50049056fea164736f6c634300081c000a", - "deployedBytecode": "0x608060405234801561000f575f5ffd5b506004361061021b575f3560e01c8063715018a61161012357806395d89b41116100b8578063c3cda52011610088578063dd62ed3e1161006e578063dd62ed3e146104dd578063f1127ed814610515578063f2fde38b14610554575f5ffd5b8063c3cda520146104bc578063d505accf146104ca575f5ffd5b806395d89b411461047b5780639ab24eb014610483578063a9059cbb14610496578063a91ee0dc146104a9575f5ffd5b806385bc898c116100f357806385bc898c146104255780638da5cb5b146104385780638e539e8c1461044957806391ddadf41461045c575f5ffd5b8063715018a6146103dc5780637b103999146103e45780637ecebe00146103f757806384b0196e1461040a575f5ffd5b80633644e515116101b35780635c19a95c116101835780636f307dc3116101695780636f307dc3146103665780636fcfff451461038c57806370a08231146103b4575f5ffd5b80635c19a95c1461034057806368a9674d14610353575f5ffd5b80633644e515146102da5780633a46b1a8146102e25780634bf5d7e9146102f5578063587cde1e146102fd575f5ffd5b8063205c2878116101ee578063205c28781461028757806323b872dd1461029a5780632f4f21e2146102ad578063313ce567146102c0575f5ffd5b806306fdde031461021f578063095ea7b31461023d578063117de2fd1461026057806318160ddd14610275575b5f5ffd5b610227610567565b604051610234919061202b565b60405180910390f35b61025061024b366004612053565b6105f7565b6040519015158152602001610234565b61027361026e366004612053565b610610565b005b6002545b604051908152602001610234565b610250610295366004612053565b61066a565b6102506102a836600461207b565b6106a8565b6102506102bb366004612053565b6106cb565b6102c861072d565b60405160ff9091168152602001610234565b61027961073b565b6102796102f0366004612053565b610744565b61022761077e565b61032861030b3660046120b5565b6001600160a01b039081165f908152600860205260409020541690565b6040516001600160a01b039091168152602001610234565b61027361034e3660046120b5565b6107f6565b61025061036136600461207b565b61080f565b7f0000000000000000000000000000000000000000000000000000000000000000610328565b61039f61039a3660046120b5565b6108a4565b60405163ffffffff9091168152602001610234565b6102796103c23660046120b5565b6001600160a01b03165f9081526020819052604090205490565b6102736108ae565b600c54610328906001600160a01b031681565b6102796104053660046120b5565b6108c1565b6104126108cb565b60405161023497969594939291906120ce565b610273610433366004612053565b61090d565b600b546001600160a01b0316610328565b610279610457366004612164565b610942565b610464610966565b60405165ffffffffffff9091168152602001610234565b61022761096f565b6102796104913660046120b5565b61097e565b6102506104a4366004612053565b61099e565b6102736104b73660046120b5565b6109ab565b61027361034e366004612189565b6102736104d83660046121df565b610a09565b6102796104eb366004612247565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b610528610523366004612278565b610b44565b60408051825165ffffffffffff1681526020928301516001600160d01b03169281019290925201610234565b6102736105623660046120b5565b610b61565b606060038054610576906122b5565b80601f01602080910402602001604051908101604052809291908181526020018280546105a2906122b5565b80156105ed5780601f106105c4576101008083540402835291602001916105ed565b820191905f5260205f20905b8154815290600101906020018083116105d057829003601f168201915b5050505050905090565b5f33610604818585610b9e565b60019150505b92915050565b600c546001600160a01b0316331461063b57604051633217675b60e21b815260040160405180910390fd5b6106667f00000000000000000000000000000000000000000000000000000000000000008383610bb0565b5050565b600c545f906001600160a01b0316331461069757604051633217675b60e21b815260040160405180910390fd5b6106a18383610c24565b9392505050565b5f336106b5858285610c97565b6106c0858585610d13565b506001949350505050565b600c545f906001600160a01b031633146106f857604051633217675b60e21b815260040160405180910390fd5b6107028383610d70565b6001600160a01b038481165f908152600860205260409020549192501661060a5761060a8384610dfe565b5f610736610e7c565b905090565b5f610736610f06565b5f61076e6107518361102f565b6001600160a01b0385165f9081526009602052604090209061107d565b6001600160d01b03169392505050565b6060610788611130565b65ffffffffffff16610798610966565b65ffffffffffff16146107be576040516301bfc1c560e61b815260040160405180910390fd5b5060408051808201909152601d81527f6d6f64653d626c6f636b6e756d6265722666726f6d3d64656661756c74000000602082015290565b604051635e81118160e11b815260040160405180910390fd5b600c545f906001600160a01b0316331461083c57604051633217675b60e21b815260040160405180910390fd5b6108687f000000000000000000000000000000000000000000000000000000000000000085308561113a565b6108728383611173565b6001600160a01b038381165f908152600860205260409020541661089a5761089a8384610dfe565b5060019392505050565b5f61060a826111a7565b6108b66111c8565b6108bf5f6111f5565b565b5f61060a82611253565b5f6060805f5f5f60606108dc611270565b6108e461129d565b604080515f80825260208201909252600f60f81b9b939a50919850469750309650945092509050565b600c546001600160a01b0316331461093857604051633217675b60e21b815260040160405180910390fd5b61066682826112ca565b5f61095761094f8361102f565b600a9061107d565b6001600160d01b031692915050565b5f610736611130565b606060048054610576906122b5565b6001600160a01b0381165f908152600960205260408120610957906112fe565b5f33610604818585610d13565b6109b36111c8565b6001600160a01b0381166109da5760405163d92e233d60e01b815260040160405180910390fd5b600c805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b83421115610a325760405163313c898160e11b8152600481018590526024015b60405180910390fd5b5f7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9888888610a7d8c6001600160a01b03165f90815260076020526040902080546001810190915590565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090505f610ad782611338565b90505f610ae682878787611364565b9050896001600160a01b0316816001600160a01b031614610b2d576040516325c0072360e11b81526001600160a01b0380831660048301528b166024820152604401610a29565b610b388a8a8a610b9e565b50505050505050505050565b604080518082019091525f80825260208201526106a18383611390565b610b696111c8565b6001600160a01b038116610b9257604051631e4fbdf760e01b81525f6004820152602401610a29565b610b9b816111f5565b50565b610bab83838360016113c4565b505050565b6040516001600160a01b03838116602483015260448201839052610bab91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050611496565b5f306001600160a01b03841603610c595760405163ec442f0560e01b81526001600160a01b0384166004820152602401610a29565b610c6333836112ca565b610c8e7f00000000000000000000000000000000000000000000000000000000000000008484610bb0565b50600192915050565b6001600160a01b038381165f908152600160209081526040808320938616835292905220545f19811015610d0d5781811015610cff57604051637dc7a0d960e11b81526001600160a01b03841660048201526024810182905260448101839052606401610a29565b610d0d84848484035f6113c4565b50505050565b6001600160a01b038316610d3c57604051634b637e8f60e11b81525f6004820152602401610a29565b6001600160a01b038216610d655760405163ec442f0560e01b81525f6004820152602401610a29565b610bab838383611502565b5f33308103610d9457604051634b637e8f60e11b8152306004820152602401610a29565b306001600160a01b03851603610dc85760405163ec442f0560e01b81526001600160a01b0385166004820152602401610a29565b610df47f000000000000000000000000000000000000000000000000000000000000000082308661113a565b6106048484611173565b6001600160a01b038281165f81815260086020526040808220805486861673ffffffffffffffffffffffffffffffffffffffff19821681179092559151919094169392849290917f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f9190a4610bab8183610e778661154b565b611568565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015610ef7575060408051601f3d908101601f19168201909252610ef4918101906122ed565b60015b610f015750601290565b919050565b5f306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016148015610f5e57507f000000000000000000000000000000000000000000000000000000000000000046145b15610f8857507f000000000000000000000000000000000000000000000000000000000000000090565b610736604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f0000000000000000000000000000000000000000000000000000000000000000918101919091527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a08201525f9060c00160405160208183030381529060405280519060200120905090565b5f5f611039610966565b90508065ffffffffffff16831061107457604051637669fc0f60e11b81526004810184905265ffffffffffff82166024820152604401610a29565b6106a1836116d1565b81545f90818160058111156110d9575f61109684611707565b6110a0908561231c565b5f8881526020902090915081015465ffffffffffff90811690871610156110c9578091506110d7565b6110d481600161232f565b92505b505b5f6110e68787858561186c565b905080156111235761110a876110fd60018461231c565b5f91825260209091200190565b54660100000000000090046001600160d01b0316611125565b5f5b979650505050505050565b5f610736436116d1565b6040516001600160a01b038481166024830152838116604483015260648201839052610d0d9186918216906323b872dd90608401610bdd565b6001600160a01b03821661119c5760405163ec442f0560e01b81525f6004820152602401610a29565b6106665f8383611502565b6001600160a01b0381165f9081526009602052604081205461060a906118cb565b600b546001600160a01b031633146108bf5760405163118cdaa760e01b8152336004820152602401610a29565b600b80546001600160a01b0383811673ffffffffffffffffffffffffffffffffffffffff19831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b6001600160a01b0381165f9081526007602052604081205461060a565b60606107367f000000000000000000000000000000000000000000000000000000000000000060056118fb565b60606107367f000000000000000000000000000000000000000000000000000000000000000060066118fb565b6001600160a01b0382166112f357604051634b637e8f60e11b81525f6004820152602401610a29565b610666825f83611502565b80545f90801561133057611317836110fd60018461231c565b54660100000000000090046001600160d01b03166106a1565b5f9392505050565b5f61060a611344610f06565b8360405161190160f01b8152600281019290925260228201526042902090565b5f5f5f5f611374888888886119a4565b9250925092506113848282611a6c565b50909695505050505050565b604080518082019091525f80825260208201526001600160a01b0383165f9081526009602052604090206106a19083611b24565b6001600160a01b0384166113ed5760405163e602df0560e01b81525f6004820152602401610a29565b6001600160a01b03831661141657604051634a1406b160e11b81525f6004820152602401610a29565b6001600160a01b038085165f9081526001602090815260408083209387168352929052208290558015610d0d57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161148891815260200190565b60405180910390a350505050565b5f5f60205f8451602086015f885af1806114b5576040513d5f823e3d81fd5b50505f513d915081156114cc5780600114156114d9565b6001600160a01b0384163b155b15610d0d57604051635274afe760e01b81526001600160a01b0385166004820152602401610a29565b6001600160a01b0383161580159061152257506001600160a01b03821615155b1561154057604051638cd22d1960e01b815260040160405180910390fd5b610bab838383611b94565b6001600160a01b0381165f9081526020819052604081205461060a565b816001600160a01b0316836001600160a01b03161415801561158957505f81115b15610bab576001600160a01b03831615611630576001600160a01b0383165f90815260096020526040812081906115cb90611bfa6115c686611c05565b611c38565b6001600160d01b031691506001600160d01b03169150846001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a7248383604051611625929190918252602082015260400190565b60405180910390a250505b6001600160a01b03821615610bab576001600160a01b0382165f908152600960205260408120819061166890611c706115c686611c05565b6001600160d01b031691506001600160d01b03169150836001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a72483836040516116c2929190918252602082015260400190565b60405180910390a25050505050565b5f65ffffffffffff821115611703576040516306dfcc6560e41b81526030600482015260248101839052604401610a29565b5090565b5f60018211611714575090565b816001700100000000000000000000000000000000821061173a5760809190911c9060401b5b6801000000000000000082106117555760409190911c9060201b5b640100000000821061176c5760209190911c9060101b5b6201000082106117815760109190911c9060081b5b61010082106117955760089190911c9060041b5b601082106117a85760049190911c9060021b5b600482106117b45760011b5b600302600190811c908185816117cc576117cc612342565b048201901c905060018185816117e4576117e4612342565b048201901c905060018185816117fc576117fc612342565b048201901c9050600181858161181457611814612342565b048201901c9050600181858161182c5761182c612342565b048201901c9050600181858161184457611844612342565b048201901c905061186381858161185d5761185d612342565b04821190565b90039392505050565b5f5b818310156118c3575f6118818484611c7b565b5f8781526020902090915065ffffffffffff86169082015465ffffffffffff1611156118af578092506118bd565b6118ba81600161232f565b93505b5061186e565b509392505050565b5f63ffffffff821115611703576040516306dfcc6560e41b81526020600482015260248101839052604401610a29565b606060ff83146119155761190e83611c95565b905061060a565b818054611921906122b5565b80601f016020809104026020016040519081016040528092919081815260200182805461194d906122b5565b80156119985780601f1061196f57610100808354040283529160200191611998565b820191905f5260205f20905b81548152906001019060200180831161197b57829003601f168201915b5050505050905061060a565b5f80807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411156119dd57505f91506003905082611a62565b604080515f808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015611a2e573d5f5f3e3d5ffd5b5050604051601f1901519150506001600160a01b038116611a5957505f925060019150829050611a62565b92505f91508190505b9450945094915050565b5f826003811115611a7f57611a7f612356565b03611a88575050565b6001826003811115611a9c57611a9c612356565b03611aba5760405163f645eedf60e01b815260040160405180910390fd5b6002826003811115611ace57611ace612356565b03611aef5760405163fce698f760e01b815260048101829052602401610a29565b6003826003811115611b0357611b03612356565b03610666576040516335e2f38360e21b815260048101829052602401610a29565b604080518082019091525f8082526020820152825f018263ffffffff1681548110611b5157611b5161236a565b5f9182526020918290206040805180820190915291015465ffffffffffff81168252660100000000000090046001600160d01b0316918101919091529392505050565b611b9f838383611cd2565b6001600160a01b038316611bef575f611bb760025490565b90506001600160d01b0380821115611bec57604051630e58ae9360e11b81526004810183905260248101829052604401610a29565b50505b610bab838383611df8565b5f6106a1828461237e565b5f6001600160d01b03821115611703576040516306dfcc6560e41b815260d0600482015260248101839052604401610a29565b5f5f611c63611c45610966565b611c5b611c51886112fe565b868863ffffffff16565b879190611e6d565b915091505b935093915050565b5f6106a1828461239d565b5f611c8960028484186123bc565b6106a19084841661232f565b60605f611ca183611e7a565b6040805160208082528183019092529192505f91906020820181803683375050509182525060208101929092525090565b6001600160a01b038316611cfc578060025f828254611cf1919061232f565b90915550611d6c9050565b6001600160a01b0383165f9081526020819052604090205481811015611d4e5760405163391434e360e21b81526001600160a01b03851660048201526024810182905260448101839052606401610a29565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b038216611d8857600280548290039055611da6565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051611deb91815260200190565b60405180910390a3505050565b6001600160a01b038316611e1a57611e17600a611c706115c684611c05565b50505b6001600160a01b038216611e3c57611e39600a611bfa6115c684611c05565b50505b6001600160a01b038381165f90815260086020526040808220548584168352912054610bab92918216911683611568565b5f80611c63858585611ea1565b5f60ff8216601f81111561060a57604051632cd44ac360e21b815260040160405180910390fd5b82545f9081908015611fa0575f611ebd876110fd60018561231c565b805490915065ffffffffffff80821691660100000000000090046001600160d01b0316908816821115611f0357604051632520601d60e01b815260040160405180910390fd5b8765ffffffffffff168265ffffffffffff1603611f3f57825465ffffffffffff1666010000000000006001600160d01b03891602178355611f92565b6040805180820190915265ffffffffffff808a1682526001600160d01b03808a1660208085019182528d54600181018f555f8f815291909120945191519092166601000000000000029216919091179101555b9450859350611c6892505050565b50506040805180820190915265ffffffffffff80851682526001600160d01b0380851660208085019182528854600181018a555f8a8152918220955192519093166601000000000000029190931617920191909155905081611c68565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f6106a16020830184611ffd565b80356001600160a01b0381168114610f01575f5ffd5b5f5f60408385031215612064575f5ffd5b61206d8361203d565b946020939093013593505050565b5f5f5f6060848603121561208d575f5ffd5b6120968461203d565b92506120a46020850161203d565b929592945050506040919091013590565b5f602082840312156120c5575f5ffd5b6106a18261203d565b60ff60f81b8816815260e060208201525f6120ec60e0830189611ffd565b82810360408401526120fe8189611ffd565b606084018890526001600160a01b038716608085015260a0840186905283810360c0850152845180825260208087019350909101905f5b81811015612153578351835260209384019390920191600101612135565b50909b9a5050505050505050505050565b5f60208284031215612174575f5ffd5b5035919050565b60ff81168114610b9b575f5ffd5b5f5f5f5f5f5f60c0878903121561219e575f5ffd5b6121a78761203d565b9550602087013594506040870135935060608701356121c58161217b565b9598949750929560808101359460a0909101359350915050565b5f5f5f5f5f5f5f60e0888a0312156121f5575f5ffd5b6121fe8861203d565b965061220c6020890161203d565b95506040880135945060608801359350608088013561222a8161217b565b9699959850939692959460a0840135945060c09093013592915050565b5f5f60408385031215612258575f5ffd5b6122618361203d565b915061226f6020840161203d565b90509250929050565b5f5f60408385031215612289575f5ffd5b6122928361203d565b9150602083013563ffffffff811681146122aa575f5ffd5b809150509250929050565b600181811c908216806122c957607f821691505b6020821081036122e757634e487b7160e01b5f52602260045260245ffd5b50919050565b5f602082840312156122fd575f5ffd5b81516106a18161217b565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561060a5761060a612308565b8082018082111561060a5761060a612308565b634e487b7160e01b5f52601260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b6001600160d01b03828116828216039081111561060a5761060a612308565b6001600160d01b03818116838216019081111561060a5761060a612308565b5f826123d657634e487b7160e01b5f52601260045260245ffd5b50049056fea164736f6c634300081c000a", + "bytecode": "0x610180604052348015610010575f5ffd5b5060405161289638038061289683398101604081905261002f9161037d565b82816040518060400160405280601481526020017f456e636c617665205469636b657420546f6b656e00000000000000000000000081525080604051806040016040528060018152602001603160f81b8152506040518060400160405280601481526020017f456e636c617665205469636b657420546f6b656e0000000000000000000000008152506040518060400160405280600381526020016245544b60e81b81525081600390816100e3919061045f565b5060046100f0828261045f565b5061010091508390506005610226565b6101205261010f816006610226565b61014052815160208084019190912060e052815190820120610100524660a05261019b60e05161010051604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201529081019290925260608201524660808201523060a08201525f9060c00160405160208183030381529060405280519060200120905090565b60805250503060c052506001600160a01b0381166101d357604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b6101dc81610258565b50306001600160a01b038216036102085760405163438d6fe360e01b81523060048201526024016101ca565b6001600160a01b03166101605261021e826102a9565b505050610571565b5f6020835110156102415761023a836102fa565b9050610252565b8161024c848261045f565b5060ff90505b92915050565b600b80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b6102b1610337565b6001600160a01b0381166102d85760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b5f5f829050601f81511115610324578260405163305a27a960e01b81526004016101ca9190610519565b805161032f8261054e565b179392505050565b600b546001600160a01b031633146103645760405163118cdaa760e01b81523360048201526024016101ca565b565b6001600160a01b038116811461037a575f5ffd5b50565b5f5f5f6060848603121561038f575f5ffd5b835161039a81610366565b60208501519093506103ab81610366565b60408501519092506103bc81610366565b809150509250925092565b634e487b7160e01b5f52604160045260245ffd5b600181811c908216806103ef57607f821691505b60208210810361040d57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561045a57805f5260205f20601f840160051c810160208510156104385750805b601f840160051c820191505b81811015610457575f8155600101610444565b50505b505050565b81516001600160401b03811115610478576104786103c7565b61048c8161048684546103db565b84610413565b6020601f8211600181146104be575f83156104a75750848201515b5f19600385901b1c1916600184901b178455610457565b5f84815260208120601f198516915b828110156104ed57878501518255602094850194600190920191016104cd565b508482101561050a57868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b8051602080830151919081101561040d575f1960209190910360031b1b16919050565b60805160a05160c05160e051610100516101205161014051610160516122b56105e15f395f81816107c401528181610ba901528181610cda0152610d8701525f61119c01525f61116f01525f610ee701525f610ebf01525f610e1a01525f610e4401525f610e6e01526122b55ff3fe608060405234801561000f575f5ffd5b50600436106101c1575f3560e01c8063715018a6116100f657806395d89b411161009a57806395d89b41146103e05780639ab24eb0146103e8578063a9059cbb146103fb578063a91ee0dc1461040e578063c3cda52014610421578063d505accf1461042f578063dd62ed3e14610442578063f1127ed814610455578063f2fde38b14610494575f5ffd5b8063715018a6146103415780637b103999146103495780637ecebe001461035c57806384b0196e1461036f57806385bc898c1461038a5780638da5cb5b1461039d5780638e539e8c146103ae57806391ddadf4146103c1575f5ffd5b80633644e515116101685780633644e515146102805780633a46b1a8146102885780634bf5d7e91461029b578063587cde1e146102a35780635c19a95c146102c357806368a9674d146102d65780636f307dc3146102e95780636fcfff45146102f157806370a0823114610319575f5ffd5b806306fdde03146101c5578063095ea7b3146101e3578063117de2fd1461020657806318160ddd1461021b578063205c28781461022d57806323b872dd146102405780632f4f21e214610253578063313ce56714610266575b5f5ffd5b6101cd6104a7565b6040516101da9190611ec3565b60405180910390f35b6101f66101f1366004611eeb565b610537565b60405190151581526020016101da565b610219610214366004611eeb565b610550565b005b6002545b6040519081526020016101da565b6101f661023b366004611eeb565b610591565b6101f661024e366004611f13565b6105cf565b6101f6610261366004611eeb565b6105f2565b61026e61064d565b60405160ff90911681526020016101da565b61021f61065b565b61021f610296366004611eeb565b610664565b6101cd61069e565b6102b66102b1366004611f4d565b610716565b6040516101da9190611f66565b6102196102d1366004611f4d565b610733565b6101f66102e4366004611f13565b61074c565b6102b66107c2565b6103046102ff366004611f4d565b6107e6565b60405163ffffffff90911681526020016101da565b61021f610327366004611f4d565b6001600160a01b03165f9081526020819052604090205490565b6102196107f0565b600c546102b6906001600160a01b031681565b61021f61036a366004611f4d565b610803565b61037761080d565b6040516101da9796959493929190611f7a565b610219610398366004611eeb565b61084f565b600b546001600160a01b03166102b6565b61021f6103bc366004612010565b610884565b6103c96108a8565b60405165ffffffffffff90911681526020016101da565b6101cd6108b1565b61021f6103f6366004611f4d565b6108c0565b6101f6610409366004611eeb565b6108e0565b61021961041c366004611f4d565b6108ed565b6102196102d1366004612035565b61021961043d36600461208b565b61093e565b61021f6104503660046120f3565b610a79565b610468610463366004612124565b610aa3565b60408051825165ffffffffffff1681526020928301516001600160d01b031692810192909252016101da565b6102196104a2366004611f4d565b610ac0565b6060600380546104b690612161565b80601f01602080910402602001604051908101604052809291908181526020018280546104e290612161565b801561052d5780601f106105045761010080835404028352916020019161052d565b820191905f5260205f20905b81548152906001019060200180831161051057829003601f168201915b5050505050905090565b5f33610544818585610afd565b60019150505b92915050565b600c546001600160a01b0316331461057b57604051633217675b60e21b815260040160405180910390fd5b61058d6105866107c2565b8383610b0f565b5050565b600c545f906001600160a01b031633146105be57604051633217675b60e21b815260040160405180910390fd5b6105c88383610b6e565b9392505050565b5f336105dc858285610bd8565b6105e7858585610c29565b506001949350505050565b600c545f906001600160a01b0316331461061f57604051633217675b60e21b815260040160405180910390fd5b6106298383610c86565b90505f61063584610716565b6001600160a01b03160361054a5761054a8384610d0b565b5f610656610d84565b905090565b5f610656610e0e565b5f61068e61067183610f37565b6001600160a01b0385165f90815260096020526040902090610f85565b6001600160d01b03169392505050565b60606106a8611035565b65ffffffffffff166106b86108a8565b65ffffffffffff16146106de576040516301bfc1c560e61b815260040160405180910390fd5b5060408051808201909152601d81527f6d6f64653d626c6f636b6e756d6265722666726f6d3d64656661756c74000000602082015290565b6001600160a01b039081165f908152600860205260409020541690565b604051635e81118160e11b815260040160405180910390fd5b600c545f906001600160a01b0316331461077957604051633217675b60e21b815260040160405180910390fd5b61078c6107846107c2565b85308561103f565b6107968383611078565b5f6107a084610716565b6001600160a01b0316036107b8576107b88384610d0b565b5060019392505050565b7f000000000000000000000000000000000000000000000000000000000000000090565b5f61054a826110ac565b6107f86110cd565b6108015f6110fa565b565b5f61054a8261114b565b5f6060805f5f5f606061081e611168565b610826611195565b604080515f80825260208201909252600f60f81b9b939a50919850469750309650945092509050565b600c546001600160a01b0316331461087a57604051633217675b60e21b815260040160405180910390fd5b61058d82826111c2565b5f61089961089183610f37565b600a90610f85565b6001600160d01b031692915050565b5f610656611035565b6060600480546104b690612161565b6001600160a01b0381165f908152600960205260408120610899906111f6565b5f33610544818585610c29565b6108f56110cd565b6001600160a01b03811661091c5760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b834211156109675760405163313c898160e11b8152600481018590526024015b60405180910390fd5b5f7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98888886109b28c6001600160a01b03165f90815260076020526040902080546001810190915590565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090505f610a0c8261122d565b90505f610a1b82878787611259565b9050896001600160a01b0316816001600160a01b031614610a62576040516325c0072360e11b81526001600160a01b0380831660048301528b16602482015260440161095e565b610a6d8a8a8a610afd565b50505050505050505050565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b604080518082019091525f80825260208201526105c88383611285565b610ac86110cd565b6001600160a01b038116610af1575f604051631e4fbdf760e01b815260040161095e9190611f66565b610afa816110fa565b50565b610b0a83838360016112b9565b505050565b6040516001600160a01b03838116602483015260448201839052610b0a91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505061138b565b5f306001600160a01b03841603610b9a578260405163ec442f0560e01b815260040161095e9190611f66565b610ba433836111c2565b610bcf7f00000000000000000000000000000000000000000000000000000000000000008484610b0f565b50600192915050565b5f610be38484610a79565b90505f19811015610c235781811015610c1557828183604051637dc7a0d960e11b815260040161095e93929190612199565b610c2384848484035f6112b9565b50505050565b6001600160a01b038316610c52575f604051634b637e8f60e11b815260040161095e9190611f66565b6001600160a01b038216610c7b575f60405163ec442f0560e01b815260040161095e9190611f66565b610b0a8383836113ee565b5f33308103610caa5730604051634b637e8f60e11b815260040161095e9190611f66565b306001600160a01b03851603610cd5578360405163ec442f0560e01b815260040161095e9190611f66565b610d017f000000000000000000000000000000000000000000000000000000000000000082308661103f565b6105448484611078565b5f610d1583610716565b6001600160a01b038481165f8181526008602052604080822080546001600160a01b031916888616908117909155905194955093928516927f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f9190a4610b0a8183610d7f86611437565b611454565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015610dff575060408051601f3d908101601f19168201909252610dfc918101906121ba565b60015b610e095750601290565b919050565b5f306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016148015610e6657507f000000000000000000000000000000000000000000000000000000000000000046145b15610e9057507f000000000000000000000000000000000000000000000000000000000000000090565b610656604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f0000000000000000000000000000000000000000000000000000000000000000918101919091527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a08201525f9060c00160405160208183030381529060405280519060200120905090565b5f5f610f416108a8565b90508065ffffffffffff168310610f7c57604051637669fc0f60e11b81526004810184905265ffffffffffff8216602482015260440161095e565b6105c8836115bd565b81545f9081816005811115610fe1575f610f9e846115f3565b610fa890856121e9565b5f8881526020902090915081015465ffffffffffff9081169087161015610fd157809150610fdf565b610fdc8160016121fc565b92505b505b5f610fee87878585611746565b9050801561102857611012876110056001846121e9565b5f91825260209091200190565b54600160301b90046001600160d01b031661102a565b5f5b979650505050505050565b5f610656436115bd565b6040516001600160a01b038481166024830152838116604483015260648201839052610c239186918216906323b872dd90608401610b3c565b6001600160a01b0382166110a1575f60405163ec442f0560e01b815260040161095e9190611f66565b61058d5f83836113ee565b6001600160a01b0381165f9081526009602052604081205461054a906117a5565b600b546001600160a01b03163314610801573360405163118cdaa760e01b815260040161095e9190611f66565b600b80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b6001600160a01b0381165f9081526007602052604081205461054a565b60606106567f000000000000000000000000000000000000000000000000000000000000000060056117d5565b60606106567f000000000000000000000000000000000000000000000000000000000000000060066117d5565b6001600160a01b0382166111eb575f604051634b637e8f60e11b815260040161095e9190611f66565b61058d825f836113ee565b80545f9080156112255761120f836110056001846121e9565b54600160301b90046001600160d01b03166105c8565b5f9392505050565b5f61054a611239610e0e565b8360405161190160f01b8152600281019290925260228201526042902090565b5f5f5f5f6112698888888861187e565b925092509250611279828261193c565b50909695505050505050565b604080518082019091525f80825260208201526001600160a01b0383165f9081526009602052604090206105c890836119f4565b6001600160a01b0384166112e2575f60405163e602df0560e01b815260040161095e9190611f66565b6001600160a01b03831661130b575f604051634a1406b160e11b815260040161095e9190611f66565b6001600160a01b038085165f9081526001602090815260408083209387168352929052208290558015610c2357826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161137d91815260200190565b60405180910390a350505050565b5f5f60205f8451602086015f885af1806113aa576040513d5f823e3d81fd5b50505f513d915081156113c15780600114156113ce565b6001600160a01b0384163b155b15610c235783604051635274afe760e01b815260040161095e9190611f66565b6001600160a01b0383161580159061140e57506001600160a01b03821615155b1561142c57604051638cd22d1960e01b815260040160405180910390fd5b610b0a838383611a61565b6001600160a01b0381165f9081526020819052604081205461054a565b816001600160a01b0316836001600160a01b03161415801561147557505f81115b15610b0a576001600160a01b0383161561151c576001600160a01b0383165f90815260096020526040812081906114b790611ac76114b286611ad2565b611b05565b6001600160d01b031691506001600160d01b03169150846001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a7248383604051611511929190918252602082015260400190565b60405180910390a250505b6001600160a01b03821615610b0a576001600160a01b0382165f908152600960205260408120819061155490611b3d6114b286611ad2565b6001600160d01b031691506001600160d01b03169150836001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a72483836040516115ae929190918252602082015260400190565b60405180910390a25050505050565b5f65ffffffffffff8211156115ef576040516306dfcc6560e41b8152603060048201526024810183905260440161095e565b5090565b5f60018211611600575090565b816001600160801b82106116195760809190911c9060401b5b600160401b821061162f5760409190911c9060201b5b64010000000082106116465760209190911c9060101b5b62010000821061165b5760109190911c9060081b5b610100821061166f5760089190911c9060041b5b601082106116825760049190911c9060021b5b6004821061168e5760011b5b600302600190811c908185816116a6576116a661220f565b048201901c905060018185816116be576116be61220f565b048201901c905060018185816116d6576116d661220f565b048201901c905060018185816116ee576116ee61220f565b048201901c905060018185816117065761170661220f565b048201901c9050600181858161171e5761171e61220f565b048201901c905061173d8185816117375761173761220f565b04821190565b90039392505050565b5f5b8183101561179d575f61175b8484611b48565b5f8781526020902090915065ffffffffffff86169082015465ffffffffffff16111561178957809250611797565b6117948160016121fc565b93505b50611748565b509392505050565b5f63ffffffff8211156115ef576040516306dfcc6560e41b8152602060048201526024810183905260440161095e565b606060ff83146117ef576117e883611b62565b905061054a565b8180546117fb90612161565b80601f016020809104026020016040519081016040528092919081815260200182805461182790612161565b80156118725780601f1061184957610100808354040283529160200191611872565b820191905f5260205f20905b81548152906001019060200180831161185557829003601f168201915b5050505050905061054a565b5f80806fa2a8918ca85bafe22016d0b997e4df60600160ff1b038411156118ad57505f91506003905082611932565b604080515f808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa1580156118fe573d5f5f3e3d5ffd5b5050604051601f1901519150506001600160a01b03811661192957505f925060019150829050611932565b92505f91508190505b9450945094915050565b5f82600381111561194f5761194f612223565b03611958575050565b600182600381111561196c5761196c612223565b0361198a5760405163f645eedf60e01b815260040160405180910390fd5b600282600381111561199e5761199e612223565b036119bf5760405163fce698f760e01b81526004810182905260240161095e565b60038260038111156119d3576119d3612223565b0361058d576040516335e2f38360e21b81526004810182905260240161095e565b604080518082019091525f8082526020820152825f018263ffffffff1681548110611a2157611a21612237565b5f9182526020918290206040805180820190915291015465ffffffffffff81168252600160301b90046001600160d01b0316918101919091529392505050565b611a6c838383611b9f565b6001600160a01b038316611abc575f611a8460025490565b90506001600160d01b0380821115611ab957604051630e58ae9360e11b8152600481018390526024810182905260440161095e565b50505b610b0a838383611cb2565b5f6105c8828461224b565b5f6001600160d01b038211156115ef576040516306dfcc6560e41b815260d060048201526024810183905260440161095e565b5f5f611b30611b126108a8565b611b28611b1e886111f6565b868863ffffffff16565b879190611d11565b915091505b935093915050565b5f6105c8828461226a565b5f611b566002848418612289565b6105c8908484166121fc565b60605f611b6e83611d1e565b6040805160208082528183019092529192505f91906020820181803683375050509182525060208101929092525090565b6001600160a01b038316611bc9578060025f828254611bbe91906121fc565b90915550611c269050565b6001600160a01b0383165f9081526020819052604090205481811015611c085783818360405163391434e360e21b815260040161095e93929190612199565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b038216611c4257600280548290039055611c60565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051611ca591815260200190565b60405180910390a3505050565b6001600160a01b038316611cd457611cd1600a611b3d6114b284611ad2565b50505b6001600160a01b038216611cf657611cf3600a611ac76114b284611ad2565b50505b610b0a611d0284610716565b611d0b84610716565b83611454565b5f80611b30858585611d45565b5f60ff8216601f81111561054a57604051632cd44ac360e21b815260040160405180910390fd5b82545f9081908015611e3b575f611d61876110056001856121e9565b805490915065ffffffffffff80821691600160301b90046001600160d01b0316908816821115611da457604051632520601d60e01b815260040160405180910390fd5b8765ffffffffffff168265ffffffffffff1603611ddd57825465ffffffffffff16600160301b6001600160d01b03891602178355611e2d565b6040805180820190915265ffffffffffff808a1682526001600160d01b03808a1660208085019182528d54600181018f555f8f81529190912094519151909216600160301b029216919091179101555b9450859350611b3592505050565b50506040805180820190915265ffffffffffff80851682526001600160d01b0380851660208085019182528854600181018a555f8a815291822095519251909316600160301b029190931617920191909155905081611b35565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f6105c86020830184611e95565b80356001600160a01b0381168114610e09575f5ffd5b5f5f60408385031215611efc575f5ffd5b611f0583611ed5565b946020939093013593505050565b5f5f5f60608486031215611f25575f5ffd5b611f2e84611ed5565b9250611f3c60208501611ed5565b929592945050506040919091013590565b5f60208284031215611f5d575f5ffd5b6105c882611ed5565b6001600160a01b0391909116815260200190565b60ff60f81b8816815260e060208201525f611f9860e0830189611e95565b8281036040840152611faa8189611e95565b606084018890526001600160a01b038716608085015260a0840186905283810360c0850152845180825260208087019350909101905f5b81811015611fff578351835260209384019390920191600101611fe1565b50909b9a5050505050505050505050565b5f60208284031215612020575f5ffd5b5035919050565b60ff81168114610afa575f5ffd5b5f5f5f5f5f5f60c0878903121561204a575f5ffd5b61205387611ed5565b95506020870135945060408701359350606087013561207181612027565b9598949750929560808101359460a0909101359350915050565b5f5f5f5f5f5f5f60e0888a0312156120a1575f5ffd5b6120aa88611ed5565b96506120b860208901611ed5565b9550604088013594506060880135935060808801356120d681612027565b9699959850939692959460a0840135945060c09093013592915050565b5f5f60408385031215612104575f5ffd5b61210d83611ed5565b915061211b60208401611ed5565b90509250929050565b5f5f60408385031215612135575f5ffd5b61213e83611ed5565b9150602083013563ffffffff81168114612156575f5ffd5b809150509250929050565b600181811c9082168061217557607f821691505b60208210810361219357634e487b7160e01b5f52602260045260245ffd5b50919050565b6001600160a01b039390931683526020830191909152604082015260600190565b5f602082840312156121ca575f5ffd5b81516105c881612027565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561054a5761054a6121d5565b8082018082111561054a5761054a6121d5565b634e487b7160e01b5f52601260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b6001600160d01b03828116828216039081111561054a5761054a6121d5565b6001600160d01b03818116838216019081111561054a5761054a6121d5565b5f826122a357634e487b7160e01b5f52601260045260245ffd5b50049056fea164736f6c634300081c000a", + "deployedBytecode": "0x608060405234801561000f575f5ffd5b50600436106101c1575f3560e01c8063715018a6116100f657806395d89b411161009a57806395d89b41146103e05780639ab24eb0146103e8578063a9059cbb146103fb578063a91ee0dc1461040e578063c3cda52014610421578063d505accf1461042f578063dd62ed3e14610442578063f1127ed814610455578063f2fde38b14610494575f5ffd5b8063715018a6146103415780637b103999146103495780637ecebe001461035c57806384b0196e1461036f57806385bc898c1461038a5780638da5cb5b1461039d5780638e539e8c146103ae57806391ddadf4146103c1575f5ffd5b80633644e515116101685780633644e515146102805780633a46b1a8146102885780634bf5d7e91461029b578063587cde1e146102a35780635c19a95c146102c357806368a9674d146102d65780636f307dc3146102e95780636fcfff45146102f157806370a0823114610319575f5ffd5b806306fdde03146101c5578063095ea7b3146101e3578063117de2fd1461020657806318160ddd1461021b578063205c28781461022d57806323b872dd146102405780632f4f21e214610253578063313ce56714610266575b5f5ffd5b6101cd6104a7565b6040516101da9190611ec3565b60405180910390f35b6101f66101f1366004611eeb565b610537565b60405190151581526020016101da565b610219610214366004611eeb565b610550565b005b6002545b6040519081526020016101da565b6101f661023b366004611eeb565b610591565b6101f661024e366004611f13565b6105cf565b6101f6610261366004611eeb565b6105f2565b61026e61064d565b60405160ff90911681526020016101da565b61021f61065b565b61021f610296366004611eeb565b610664565b6101cd61069e565b6102b66102b1366004611f4d565b610716565b6040516101da9190611f66565b6102196102d1366004611f4d565b610733565b6101f66102e4366004611f13565b61074c565b6102b66107c2565b6103046102ff366004611f4d565b6107e6565b60405163ffffffff90911681526020016101da565b61021f610327366004611f4d565b6001600160a01b03165f9081526020819052604090205490565b6102196107f0565b600c546102b6906001600160a01b031681565b61021f61036a366004611f4d565b610803565b61037761080d565b6040516101da9796959493929190611f7a565b610219610398366004611eeb565b61084f565b600b546001600160a01b03166102b6565b61021f6103bc366004612010565b610884565b6103c96108a8565b60405165ffffffffffff90911681526020016101da565b6101cd6108b1565b61021f6103f6366004611f4d565b6108c0565b6101f6610409366004611eeb565b6108e0565b61021961041c366004611f4d565b6108ed565b6102196102d1366004612035565b61021961043d36600461208b565b61093e565b61021f6104503660046120f3565b610a79565b610468610463366004612124565b610aa3565b60408051825165ffffffffffff1681526020928301516001600160d01b031692810192909252016101da565b6102196104a2366004611f4d565b610ac0565b6060600380546104b690612161565b80601f01602080910402602001604051908101604052809291908181526020018280546104e290612161565b801561052d5780601f106105045761010080835404028352916020019161052d565b820191905f5260205f20905b81548152906001019060200180831161051057829003601f168201915b5050505050905090565b5f33610544818585610afd565b60019150505b92915050565b600c546001600160a01b0316331461057b57604051633217675b60e21b815260040160405180910390fd5b61058d6105866107c2565b8383610b0f565b5050565b600c545f906001600160a01b031633146105be57604051633217675b60e21b815260040160405180910390fd5b6105c88383610b6e565b9392505050565b5f336105dc858285610bd8565b6105e7858585610c29565b506001949350505050565b600c545f906001600160a01b0316331461061f57604051633217675b60e21b815260040160405180910390fd5b6106298383610c86565b90505f61063584610716565b6001600160a01b03160361054a5761054a8384610d0b565b5f610656610d84565b905090565b5f610656610e0e565b5f61068e61067183610f37565b6001600160a01b0385165f90815260096020526040902090610f85565b6001600160d01b03169392505050565b60606106a8611035565b65ffffffffffff166106b86108a8565b65ffffffffffff16146106de576040516301bfc1c560e61b815260040160405180910390fd5b5060408051808201909152601d81527f6d6f64653d626c6f636b6e756d6265722666726f6d3d64656661756c74000000602082015290565b6001600160a01b039081165f908152600860205260409020541690565b604051635e81118160e11b815260040160405180910390fd5b600c545f906001600160a01b0316331461077957604051633217675b60e21b815260040160405180910390fd5b61078c6107846107c2565b85308561103f565b6107968383611078565b5f6107a084610716565b6001600160a01b0316036107b8576107b88384610d0b565b5060019392505050565b7f000000000000000000000000000000000000000000000000000000000000000090565b5f61054a826110ac565b6107f86110cd565b6108015f6110fa565b565b5f61054a8261114b565b5f6060805f5f5f606061081e611168565b610826611195565b604080515f80825260208201909252600f60f81b9b939a50919850469750309650945092509050565b600c546001600160a01b0316331461087a57604051633217675b60e21b815260040160405180910390fd5b61058d82826111c2565b5f61089961089183610f37565b600a90610f85565b6001600160d01b031692915050565b5f610656611035565b6060600480546104b690612161565b6001600160a01b0381165f908152600960205260408120610899906111f6565b5f33610544818585610c29565b6108f56110cd565b6001600160a01b03811661091c5760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b834211156109675760405163313c898160e11b8152600481018590526024015b60405180910390fd5b5f7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98888886109b28c6001600160a01b03165f90815260076020526040902080546001810190915590565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090505f610a0c8261122d565b90505f610a1b82878787611259565b9050896001600160a01b0316816001600160a01b031614610a62576040516325c0072360e11b81526001600160a01b0380831660048301528b16602482015260440161095e565b610a6d8a8a8a610afd565b50505050505050505050565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b604080518082019091525f80825260208201526105c88383611285565b610ac86110cd565b6001600160a01b038116610af1575f604051631e4fbdf760e01b815260040161095e9190611f66565b610afa816110fa565b50565b610b0a83838360016112b9565b505050565b6040516001600160a01b03838116602483015260448201839052610b0a91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505061138b565b5f306001600160a01b03841603610b9a578260405163ec442f0560e01b815260040161095e9190611f66565b610ba433836111c2565b610bcf7f00000000000000000000000000000000000000000000000000000000000000008484610b0f565b50600192915050565b5f610be38484610a79565b90505f19811015610c235781811015610c1557828183604051637dc7a0d960e11b815260040161095e93929190612199565b610c2384848484035f6112b9565b50505050565b6001600160a01b038316610c52575f604051634b637e8f60e11b815260040161095e9190611f66565b6001600160a01b038216610c7b575f60405163ec442f0560e01b815260040161095e9190611f66565b610b0a8383836113ee565b5f33308103610caa5730604051634b637e8f60e11b815260040161095e9190611f66565b306001600160a01b03851603610cd5578360405163ec442f0560e01b815260040161095e9190611f66565b610d017f000000000000000000000000000000000000000000000000000000000000000082308661103f565b6105448484611078565b5f610d1583610716565b6001600160a01b038481165f8181526008602052604080822080546001600160a01b031916888616908117909155905194955093928516927f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f9190a4610b0a8183610d7f86611437565b611454565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015610dff575060408051601f3d908101601f19168201909252610dfc918101906121ba565b60015b610e095750601290565b919050565b5f306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016148015610e6657507f000000000000000000000000000000000000000000000000000000000000000046145b15610e9057507f000000000000000000000000000000000000000000000000000000000000000090565b610656604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f0000000000000000000000000000000000000000000000000000000000000000918101919091527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a08201525f9060c00160405160208183030381529060405280519060200120905090565b5f5f610f416108a8565b90508065ffffffffffff168310610f7c57604051637669fc0f60e11b81526004810184905265ffffffffffff8216602482015260440161095e565b6105c8836115bd565b81545f9081816005811115610fe1575f610f9e846115f3565b610fa890856121e9565b5f8881526020902090915081015465ffffffffffff9081169087161015610fd157809150610fdf565b610fdc8160016121fc565b92505b505b5f610fee87878585611746565b9050801561102857611012876110056001846121e9565b5f91825260209091200190565b54600160301b90046001600160d01b031661102a565b5f5b979650505050505050565b5f610656436115bd565b6040516001600160a01b038481166024830152838116604483015260648201839052610c239186918216906323b872dd90608401610b3c565b6001600160a01b0382166110a1575f60405163ec442f0560e01b815260040161095e9190611f66565b61058d5f83836113ee565b6001600160a01b0381165f9081526009602052604081205461054a906117a5565b600b546001600160a01b03163314610801573360405163118cdaa760e01b815260040161095e9190611f66565b600b80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b6001600160a01b0381165f9081526007602052604081205461054a565b60606106567f000000000000000000000000000000000000000000000000000000000000000060056117d5565b60606106567f000000000000000000000000000000000000000000000000000000000000000060066117d5565b6001600160a01b0382166111eb575f604051634b637e8f60e11b815260040161095e9190611f66565b61058d825f836113ee565b80545f9080156112255761120f836110056001846121e9565b54600160301b90046001600160d01b03166105c8565b5f9392505050565b5f61054a611239610e0e565b8360405161190160f01b8152600281019290925260228201526042902090565b5f5f5f5f6112698888888861187e565b925092509250611279828261193c565b50909695505050505050565b604080518082019091525f80825260208201526001600160a01b0383165f9081526009602052604090206105c890836119f4565b6001600160a01b0384166112e2575f60405163e602df0560e01b815260040161095e9190611f66565b6001600160a01b03831661130b575f604051634a1406b160e11b815260040161095e9190611f66565b6001600160a01b038085165f9081526001602090815260408083209387168352929052208290558015610c2357826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161137d91815260200190565b60405180910390a350505050565b5f5f60205f8451602086015f885af1806113aa576040513d5f823e3d81fd5b50505f513d915081156113c15780600114156113ce565b6001600160a01b0384163b155b15610c235783604051635274afe760e01b815260040161095e9190611f66565b6001600160a01b0383161580159061140e57506001600160a01b03821615155b1561142c57604051638cd22d1960e01b815260040160405180910390fd5b610b0a838383611a61565b6001600160a01b0381165f9081526020819052604081205461054a565b816001600160a01b0316836001600160a01b03161415801561147557505f81115b15610b0a576001600160a01b0383161561151c576001600160a01b0383165f90815260096020526040812081906114b790611ac76114b286611ad2565b611b05565b6001600160d01b031691506001600160d01b03169150846001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a7248383604051611511929190918252602082015260400190565b60405180910390a250505b6001600160a01b03821615610b0a576001600160a01b0382165f908152600960205260408120819061155490611b3d6114b286611ad2565b6001600160d01b031691506001600160d01b03169150836001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a72483836040516115ae929190918252602082015260400190565b60405180910390a25050505050565b5f65ffffffffffff8211156115ef576040516306dfcc6560e41b8152603060048201526024810183905260440161095e565b5090565b5f60018211611600575090565b816001600160801b82106116195760809190911c9060401b5b600160401b821061162f5760409190911c9060201b5b64010000000082106116465760209190911c9060101b5b62010000821061165b5760109190911c9060081b5b610100821061166f5760089190911c9060041b5b601082106116825760049190911c9060021b5b6004821061168e5760011b5b600302600190811c908185816116a6576116a661220f565b048201901c905060018185816116be576116be61220f565b048201901c905060018185816116d6576116d661220f565b048201901c905060018185816116ee576116ee61220f565b048201901c905060018185816117065761170661220f565b048201901c9050600181858161171e5761171e61220f565b048201901c905061173d8185816117375761173761220f565b04821190565b90039392505050565b5f5b8183101561179d575f61175b8484611b48565b5f8781526020902090915065ffffffffffff86169082015465ffffffffffff16111561178957809250611797565b6117948160016121fc565b93505b50611748565b509392505050565b5f63ffffffff8211156115ef576040516306dfcc6560e41b8152602060048201526024810183905260440161095e565b606060ff83146117ef576117e883611b62565b905061054a565b8180546117fb90612161565b80601f016020809104026020016040519081016040528092919081815260200182805461182790612161565b80156118725780601f1061184957610100808354040283529160200191611872565b820191905f5260205f20905b81548152906001019060200180831161185557829003601f168201915b5050505050905061054a565b5f80806fa2a8918ca85bafe22016d0b997e4df60600160ff1b038411156118ad57505f91506003905082611932565b604080515f808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa1580156118fe573d5f5f3e3d5ffd5b5050604051601f1901519150506001600160a01b03811661192957505f925060019150829050611932565b92505f91508190505b9450945094915050565b5f82600381111561194f5761194f612223565b03611958575050565b600182600381111561196c5761196c612223565b0361198a5760405163f645eedf60e01b815260040160405180910390fd5b600282600381111561199e5761199e612223565b036119bf5760405163fce698f760e01b81526004810182905260240161095e565b60038260038111156119d3576119d3612223565b0361058d576040516335e2f38360e21b81526004810182905260240161095e565b604080518082019091525f8082526020820152825f018263ffffffff1681548110611a2157611a21612237565b5f9182526020918290206040805180820190915291015465ffffffffffff81168252600160301b90046001600160d01b0316918101919091529392505050565b611a6c838383611b9f565b6001600160a01b038316611abc575f611a8460025490565b90506001600160d01b0380821115611ab957604051630e58ae9360e11b8152600481018390526024810182905260440161095e565b50505b610b0a838383611cb2565b5f6105c8828461224b565b5f6001600160d01b038211156115ef576040516306dfcc6560e41b815260d060048201526024810183905260440161095e565b5f5f611b30611b126108a8565b611b28611b1e886111f6565b868863ffffffff16565b879190611d11565b915091505b935093915050565b5f6105c8828461226a565b5f611b566002848418612289565b6105c8908484166121fc565b60605f611b6e83611d1e565b6040805160208082528183019092529192505f91906020820181803683375050509182525060208101929092525090565b6001600160a01b038316611bc9578060025f828254611bbe91906121fc565b90915550611c269050565b6001600160a01b0383165f9081526020819052604090205481811015611c085783818360405163391434e360e21b815260040161095e93929190612199565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b038216611c4257600280548290039055611c60565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051611ca591815260200190565b60405180910390a3505050565b6001600160a01b038316611cd457611cd1600a611b3d6114b284611ad2565b50505b6001600160a01b038216611cf657611cf3600a611ac76114b284611ad2565b50505b610b0a611d0284610716565b611d0b84610716565b83611454565b5f80611b30858585611d45565b5f60ff8216601f81111561054a57604051632cd44ac360e21b815260040160405180910390fd5b82545f9081908015611e3b575f611d61876110056001856121e9565b805490915065ffffffffffff80821691600160301b90046001600160d01b0316908816821115611da457604051632520601d60e01b815260040160405180910390fd5b8765ffffffffffff168265ffffffffffff1603611ddd57825465ffffffffffff16600160301b6001600160d01b03891602178355611e2d565b6040805180820190915265ffffffffffff808a1682526001600160d01b03808a1660208085019182528d54600181018f555f8f81529190912094519151909216600160301b029216919091179101555b9450859350611b3592505050565b50506040805180820190915265ffffffffffff80851682526001600160d01b0380851660208085019182528854600181018a555f8a815291822095519251909316600160301b029190931617920191909155905081611b35565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f6105c86020830184611e95565b80356001600160a01b0381168114610e09575f5ffd5b5f5f60408385031215611efc575f5ffd5b611f0583611ed5565b946020939093013593505050565b5f5f5f60608486031215611f25575f5ffd5b611f2e84611ed5565b9250611f3c60208501611ed5565b929592945050506040919091013590565b5f60208284031215611f5d575f5ffd5b6105c882611ed5565b6001600160a01b0391909116815260200190565b60ff60f81b8816815260e060208201525f611f9860e0830189611e95565b8281036040840152611faa8189611e95565b606084018890526001600160a01b038716608085015260a0840186905283810360c0850152845180825260208087019350909101905f5b81811015611fff578351835260209384019390920191600101611fe1565b50909b9a5050505050505050505050565b5f60208284031215612020575f5ffd5b5035919050565b60ff81168114610afa575f5ffd5b5f5f5f5f5f5f60c0878903121561204a575f5ffd5b61205387611ed5565b95506020870135945060408701359350606087013561207181612027565b9598949750929560808101359460a0909101359350915050565b5f5f5f5f5f5f5f60e0888a0312156120a1575f5ffd5b6120aa88611ed5565b96506120b860208901611ed5565b9550604088013594506060880135935060808801356120d681612027565b9699959850939692959460a0840135945060c09093013592915050565b5f5f60408385031215612104575f5ffd5b61210d83611ed5565b915061211b60208401611ed5565b90509250929050565b5f5f60408385031215612135575f5ffd5b61213e83611ed5565b9150602083013563ffffffff81168114612156575f5ffd5b809150509250929050565b600181811c9082168061217557607f821691505b60208210810361219357634e487b7160e01b5f52602260045260245ffd5b50919050565b6001600160a01b039390931683526020830191909152604082015260600190565b5f602082840312156121ca575f5ffd5b81516105c881612027565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561054a5761054a6121d5565b8082018082111561054a5761054a6121d5565b634e487b7160e01b5f52601260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b6001600160d01b03828116828216039081111561054a5761054a6121d5565b6001600160d01b03818116838216019081111561054a5761054a6121d5565b5f826122a357634e487b7160e01b5f52601260045260245ffd5b50049056fea164736f6c634300081c000a", "linkReferences": {}, "deployedLinkReferences": {}, "immutableReferences": { "3415": [ { "length": 32, - "start": 872 + "start": 1988 }, { "length": 32, - "start": 1600 + "start": 2985 }, { "length": 32, - "start": 2113 + "start": 3290 }, { "length": 32, - "start": 3176 - }, - { - "length": 32, - "start": 3533 - }, - { - "length": 32, - "start": 3711 + "start": 3463 } ], "6684": [ { "length": 32, - "start": 3942 + "start": 3694 } ], "6686": [ { "length": 32, - "start": 3900 + "start": 3652 } ], "6688": [ { "length": 32, - "start": 3858 + "start": 3610 } ], "6690": [ { "length": 32, - "start": 4023 + "start": 3775 } ], "6692": [ { "length": 32, - "start": 4063 + "start": 3815 } ], "6695": [ { "length": 32, - "start": 4727 + "start": 4463 } ], "6698": [ { "length": 32, - "start": 4772 + "start": 4508 } ] }, "inputSourceName": "project/contracts/token/EnclaveTicketToken.sol", - "buildInfoId": "solc-0_8_28-15112a5a277d7846347c8e3a91ba0ec7bb3521bd" + "buildInfoId": "solc-0_8_28-e60a5d7c133605edcf61acdd5ba43ab44ee0928e" } \ No newline at end of file diff --git a/packages/enclave-contracts/scripts/deployEnclave.ts b/packages/enclave-contracts/scripts/deployEnclave.ts index 6dfb6c5240..5c9670f0be 100644 --- a/packages/enclave-contracts/scripts/deployEnclave.ts +++ b/packages/enclave-contracts/scripts/deployEnclave.ts @@ -44,7 +44,7 @@ export const deployEnclave = async (withMocks?: boolean) => { const ownerAddress = await owner.getAddress(); const polynomial_degree = ethers.toBigInt(512); - const plaintext_modulus = ethers.toBigInt(10); + const plaintext_modulus = ethers.toBigInt(100); const moduli = [ ethers.toBigInt("0xffffee001"), ethers.toBigInt("0xffffc4001"), diff --git a/templates/default/.gitignore b/templates/default/.gitignore index 4704916269..aa919565f1 100644 --- a/templates/default/.gitignore +++ b/templates/default/.gitignore @@ -16,6 +16,7 @@ node_modules .enclave/data/ .enclave/config/ .enclave/caches/ +.enclave/noir/ /target