From 5a0911ca441f07efe73b6993c301716336e70835 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Thu, 12 Feb 2026 08:23:22 +0000 Subject: [PATCH 01/15] feat: integrate share proofs in ciphernode --- crates/events/Cargo.toml | 1 + .../src/enclave_event/compute_request/mod.rs | 1 + .../src/enclave_event/compute_request/zk.rs | 64 +++++++++++++ crates/fhe/src/fhe.rs | 1 + crates/keyshare/src/threshold_keyshare.rs | 93 +++++++++++++------ crates/multithread/src/multithread.rs | 11 +++ crates/trbfv/src/gen_pk_share_and_sk_sss.rs | 76 +++++++++++++-- 7 files changed, 215 insertions(+), 32 deletions(-) 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..9ee96580e4 100644 --- a/crates/events/src/enclave_event/compute_request/zk.rs +++ b/crates/events/src/enclave_event/compute_request/zk.rs @@ -8,6 +8,7 @@ use crate::Proof; use derivative::Derivative; 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 +16,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 +30,31 @@ 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. + #[derivative(Debug(format_with = "e3_utils::formatters::hexf"))] + pub pk0_share: ArcBytes, + /// Raw common random polynomial bytes. + #[derivative(Debug(format_with = "e3_utils::formatters::hexf"))] + pub a: ArcBytes, + /// Raw secret key polynomial bytes (witness). + #[derivative(Debug(format_with = "e3_utils::formatters::hexf"))] + pub sk: ArcBytes, + /// Raw error polynomial bytes (witness). + #[derivative(Debug(format_with = "e3_utils::formatters::hexf"))] + pub eek: ArcBytes, + /// Raw smudging noise polynomial bytes (witness). + #[derivative(Debug(format_with = "e3_utils::formatters::hexf"))] + pub e_sm: ArcBytes, + /// 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 +64,35 @@ impl PkBfvProofRequest { } } +impl PkGenerationProofRequest { + pub fn new( + pk0_share: impl Into, + a: impl Into, + sk: impl Into, + eek: impl Into, + e_sm: impl Into, + params_preset: BfvPreset, + committee_size: CiphernodesCommitteeSize, + ) -> Self { + Self { + pk0_share: pk0_share.into(), + a: a.into(), + sk: sk.into(), + eek: eek.into(), + params_preset, + e_sm: e_sm.into(), + 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 +101,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/fhe/src/fhe.rs b/crates/fhe/src/fhe.rs index becf28c126..477d02cdc7 100644 --- a/crates/fhe/src/fhe.rs +++ b/crates/fhe/src/fhe.rs @@ -189,3 +189,4 @@ impl SecretKeySerializer { }) } } + diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index cca0bc00e3..950d6a1e81 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -79,6 +79,7 @@ pub struct GeneratingThresholdShareData { pk_share: Option, sk_sss: Option>, esi_sss: Option>>, + pk_generation_proof: Option, sk_bfv: SensitiveBytes, pk_bfv: ArcBytes, collected_encryption_keys: Vec>, @@ -418,7 +419,12 @@ impl ThresholdKeyshare { } _ => Ok(()), }, - ComputeResponseKind::Zk(_) => Ok(()), + ComputeResponseKind::Zk(zk) => match zk { + ZkResponse::PkGeneration(res) => { + self.handle_pk_generation_proof_response(res.proof.clone()) + } + _ => Ok(()), + }, } } @@ -499,6 +505,7 @@ impl ThresholdKeyshare { sk_sss: None, pk_share: None, esi_sss: None, + pk_generation_proof: None, sk_bfv: current.sk_bfv, pk_bfv: current.pk_bfv, collected_encryption_keys: msg.keys, @@ -569,9 +576,9 @@ impl ThresholdKeyshare { info!("try_store_esi_sss"); let current: GeneratingThresholdShareData = s.clone().try_into()?; - let next = match (current.pk_share, current.sk_sss) { + let next = match (current.pk_share, current.sk_sss, current.pk_generation_proof) { // If the other shares are here then transition to aggregation - (Some(pk_share), Some(sk_sss)) => { + (Some(pk_share), Some(sk_sss), Some(_proof)) => { K::AggregatingDecryptionKey(AggregatingDecryptionKey { esi_sss, pk_share, @@ -581,15 +588,10 @@ impl ThresholdKeyshare { }) } // If the other shares are not here yet then don't transition - (None, None) => K::GeneratingThresholdShare(GeneratingThresholdShareData { + _ => 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, + ..current }), - _ => bail!("Inconsistent state!"), }; s.new_state(next) @@ -628,9 +630,19 @@ impl ThresholdKeyshare { ) .to_bytes(), ); + + let threshold_preset = self + .share_enc_preset + .threshold_counterpart() + .ok_or_else(|| anyhow!("No threshold counterpart for {:?}", self.share_enc_preset))?; + let metadata = threshold_preset.metadata(); + let defaults = threshold_preset + .search_defaults() + .ok_or_else(|| anyhow!("No search defaults for {:?}", threshold_preset))?; + let event = ComputeRequest::trbfv( TrBFVRequest::GenPkShareAndSkSss( - GenPkShareAndSkSssRequest { trbfv_config, crp }.into(), + GenPkShareAndSkSssRequest { trbfv_config, crp, lambda: defaults.lambda as usize, num_ciphertexts: defaults.z as usize }.into(), ), CorrelationId::new(), e3_id, @@ -656,30 +668,59 @@ impl ThresholdKeyshare { 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.esi_sss { - // If the esi shares are here then transition to aggregation - Some(esi_sss) => { + s.new_state(KeyshareState::GeneratingThresholdShare( + GeneratingThresholdShareData { + pk_share: Some(pk_share), + sk_sss: Some(sk_sss), + ..current + }, + )) + })?; + + // Fire ZK proof request for PK generation (T1a) + let state = self.state.try_get()?; + let e3_id = state.e3_id.clone(); + + self.bus.publish(ComputeRequest::zk( + ZkRequest::PkGeneration(PkGenerationProofRequest::new( + output.pk0_share_raw, + output.a_raw, + output.sk_raw, + output.eek_raw, + self.share_enc_preset, + self.comm + )), + CorrelationId::new(), + e3_id, + ))?; + + Ok(()) + } + + pub fn handle_pk_generation_proof_response(&mut self, proof: Proof) -> Result<()> { + self.state.try_mutate(|s| { + info!("try_store_pk_generation_proof"); + let current: GeneratingThresholdShareData = s.clone().try_into()?; + + let next = match (¤t.pk_share, ¤t.sk_sss, ¤t.esi_sss) { + (Some(pk_share), Some(sk_sss), Some(esi_sss)) => { KeyshareState::AggregatingDecryptionKey(AggregatingDecryptionKey { - esi_sss, - pk_share, - sk_sss, + pk_share: pk_share.clone(), + sk_sss: sk_sss.clone(), + esi_sss: esi_sss.clone(), 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, + _ => KeyshareState::GeneratingThresholdShare(GeneratingThresholdShareData { + pk_generation_proof: Some(proof), + ..current }), }; + s.new_state(next) })?; - + if let Some(ThresholdKeyshareState { state: KeyshareState::AggregatingDecryptionKey { .. }, .. diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index 97f985bdcf..e80251e651 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -363,9 +363,20 @@ 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, req, request.clone()) + }), } } +fn handle_pk_generation_proof( + prover: &ZkProver, + req: PkBfvProofRequest, + request: ComputeRequest, +) -> Result { + +} + fn handle_pk_bfv_proof( prover: &ZkProver, req: PkBfvProofRequest, 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..abc41ceea1 100644 --- a/crates/trbfv/src/gen_pk_share_and_sk_sss.rs +++ b/crates/trbfv/src/gen_pk_share_and_sk_sss.rs @@ -16,10 +16,11 @@ 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). + pub sk_raw: ArcBytes, + /// Raw error polynomial from key generation (RNS form) for ZK proof generation (T1a). + pub eek_raw: ArcBytes, + /// Raw smudging noise polynomial (RNS form) for ZK proof generation (C1). + pub e_sm_raw: ArcBytes, } 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: value.sk_raw, + eek_raw: value.eek_raw, + e_sm_raw: value.e_sm_raw, + }) } } 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,7 +128,26 @@ 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 sk_raw = ArcBytes::from_bytes(&sk_poly.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()); @@ -103,5 +159,13 @@ pub fn gen_pk_share_and_sk_sss( 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() } From ddd60c779f4c4c77afdcec8cd47c5442d54c7ad2 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Thu, 12 Feb 2026 16:49:18 +0000 Subject: [PATCH 02/15] feat: add t1 proof gen --- Cargo.lock | 3 + .../src/enclave_event/keyshare_created.rs | 3 +- crates/events/src/enclave_event/mod.rs | 5 + .../enclave_event/threshold_share_created.rs | 5 +- .../enclave_event/threshold_share_pending.rs | 27 +++ crates/fhe/src/fhe.rs | 1 - crates/keyshare/Cargo.toml | 1 + crates/keyshare/src/threshold_keyshare.rs | 215 +++++++++--------- crates/multithread/Cargo.toml | 1 + crates/multithread/src/multithread.rs | 78 ++++++- crates/test-helpers/src/usecase_helpers.rs | 8 +- crates/trbfv/src/gen_esi_sss.rs | 56 +++-- crates/trbfv/src/gen_pk_share_and_sk_sss.rs | 29 ++- .../zk-helpers/src/ciphernodes_committee.rs | 4 +- crates/zk-prover/src/actors/proof_request.rs | 143 ++++++++++-- .../IBondingRegistry.json | 2 +- .../ICiphernodeRegistry.json | 2 +- .../interfaces/IEnclave.sol/IEnclave.json | 2 +- .../EnclaveTicketToken.json | 36 ++- 19 files changed, 427 insertions(+), 194 deletions(-) create mode 100644 crates/events/src/enclave_event/threshold_share_pending.rs 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/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..032852f01a 100644 --- a/crates/events/src/enclave_event/mod.rs +++ b/crates/events/src/enclave_event/mod.rs @@ -43,6 +43,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; @@ -89,6 +90,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::*; @@ -217,6 +219,7 @@ pub enum EnclaveEventData { Shutdown(Shutdown), DocumentReceived(DocumentReceived), ThresholdShareCreated(ThresholdShareCreated), + ThresholdSharePending(ThresholdSharePending), EncryptionKeyPending(EncryptionKeyPending), EncryptionKeyReceived(EncryptionKeyReceived), EncryptionKeyCreated(EncryptionKeyCreated), @@ -463,6 +466,7 @@ impl EnclaveEventData { EnclaveEventData::PlaintextAggregated(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()), @@ -531,6 +535,7 @@ impl_event_types!( TestEvent, DocumentReceived, ThresholdShareCreated, + ThresholdSharePending, EncryptionKeyPending, EncryptionKeyReceived, EncryptionKeyCreated, diff --git a/crates/events/src/enclave_event/threshold_share_created.rs b/crates/events/src/enclave_event/threshold_share_created.rs index e9794a0397..69e84f76d7 100644 --- a/crates/events/src/enclave_event/threshold_share_created.rs +++ b/crates/events/src/enclave_event/threshold_share_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_trbfv::shares::BfvEncryptedShares; @@ -32,6 +32,8 @@ pub struct ThresholdShare { pub sk_sss: BfvEncryptedShares, /// BFV-encrypted esi_sss - one per secret key (sk), each recipient can decrypt their share pub esi_sss: Vec, + /// The signed proof payload for fault attribution + pub signed_pk_generation_proof: Option, } impl ThresholdShare { @@ -49,6 +51,7 @@ impl ThresholdShare { pk_share: self.pk_share.clone(), sk_sss, esi_sss, + signed_pk_generation_proof: self.signed_pk_generation_proof.clone(), }) } 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/fhe/src/fhe.rs b/crates/fhe/src/fhe.rs index 477d02cdc7..becf28c126 100644 --- a/crates/fhe/src/fhe.rs +++ b/crates/fhe/src/fhe.rs @@ -189,4 +189,3 @@ impl SecretKeySerializer { }) } } - 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 950d6a1e81..d553ae5283 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -12,9 +12,9 @@ use e3_events::{ prelude::*, trap, BusHandle, CiphernodeSelected, CiphertextOutputPublished, ComputeRequest, ComputeResponse, ComputeResponseKind, CorrelationId, DecryptionshareCreated, Die, E3RequestComplete, E3id, EType, EnclaveEvent, EnclaveEventData, EncryptionKey, - EncryptionKeyCollectionFailed, EncryptionKeyCreated, EncryptionKeyPending, EventContext, - KeyshareCreated, PartyId, Sequenced, ThresholdShare, ThresholdShareCollectionFailed, - ThresholdShareCreated, TypedEvent, + EncryptionKeyCollectionFailed, EncryptionKeyCreated, EncryptionKeyPending, EventContext, KeyshareCreated, + PartyId, PkGenerationProofRequest, ThresholdShare, ThresholdShareCollectionFailed, + ThresholdShareCreated, ThresholdSharePending, TypedEvent, }; use e3_fhe::create_crp; use e3_fhe_params::{BfvParamSet, BfvPreset}; @@ -31,12 +31,10 @@ 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}; -use rand_chacha::ChaCha20Rng; use std::{ - collections::HashMap, mem, sync::{Arc, Mutex}, }; @@ -51,7 +49,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: Option, +} #[derive(Message)] #[rtype(result = "()")] @@ -74,15 +75,25 @@ 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: ArcBytes, + pub eek_raw: ArcBytes, +} + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct GeneratingThresholdShareData { pk_share: Option, sk_sss: Option>, esi_sss: Option>>, - pk_generation_proof: 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)] @@ -90,8 +101,10 @@ pub struct AggregatingDecryptionKey { pk_share: ArcBytes, sk_sss: Encrypted, esi_sss: Vec>, + e_sm_raw: ArcBytes, sk_bfv: SensitiveBytes, collected_encryption_keys: Vec>, + proof_request_data: ProofRequestData, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -420,9 +433,6 @@ impl ThresholdKeyshare { _ => Ok(()), }, ComputeResponseKind::Zk(zk) => match zk { - ZkResponse::PkGeneration(res) => { - self.handle_pk_generation_proof_response(res.proof.clone()) - } _ => Ok(()), }, } @@ -505,21 +515,19 @@ impl ThresholdKeyshare { sk_sss: None, pk_share: None, esi_sss: None, - pk_generation_proof: 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, - ))?; + + self.handle_gen_pk_share_and_sk_sss_requested(TypedEvent::new(GenPkShareAndSkSss( + current.ciphernode_selected, + ), ec))?; Ok(()) } @@ -529,7 +537,8 @@ impl ThresholdKeyshare { let (msg, ec) = msg.into_components(); info!("GenEsiSss on ThresholdKeyshare"); - let evt = msg.0; + let evt = msg.ciphernode_selected; + let e_sm_raw = msg.e_sm_raw; let CiphernodeSelected { // TODO: should these be on meta? These seem TrBFV specific. perhaps it is best to // bundle them in with the params @@ -552,6 +561,7 @@ impl ThresholdKeyshare { trbfv_config, error_size, esi_per_ct: esi_per_ct as u64, + e_sm_raw, } .into(), ), @@ -576,15 +586,22 @@ impl ThresholdKeyshare { info!("try_store_esi_sss"); let current: GeneratingThresholdShareData = s.clone().try_into()?; - let next = match (current.pk_share, current.sk_sss, current.pk_generation_proof) { + let next = match ( + ¤t.pk_share, + ¤t.sk_sss, + ¤t.e_sm_raw, + ¤t.proof_request_data, + ) { // If the other shares are here then transition to aggregation - (Some(pk_share), Some(sk_sss), Some(_proof)) => { + (Some(pk_share), Some(sk_sss), Some(e_sm_raw), Some(proof_request_data)) => { K::AggregatingDecryptionKey(AggregatingDecryptionKey { esi_sss, - pk_share, - sk_sss, + pk_share: pk_share.clone(), + sk_sss: sk_sss.clone(), + e_sm_raw: e_sm_raw.clone(), sk_bfv: current.sk_bfv, collected_encryption_keys: current.collected_encryption_keys, + proof_request_data: proof_request_data.clone(), }) } // If the other shares are not here yet then don't transition @@ -635,14 +652,19 @@ impl ThresholdKeyshare { .share_enc_preset .threshold_counterpart() .ok_or_else(|| anyhow!("No threshold counterpart for {:?}", self.share_enc_preset))?; - let metadata = threshold_preset.metadata(); let defaults = threshold_preset .search_defaults() .ok_or_else(|| anyhow!("No search defaults for {:?}", threshold_preset))?; let event = ComputeRequest::trbfv( TrBFVRequest::GenPkShareAndSkSss( - GenPkShareAndSkSssRequest { trbfv_config, crp, lambda: defaults.lambda as usize, num_ciphertexts: defaults.z as usize }.into(), + GenPkShareAndSkSssRequest { + trbfv_config, + crp, + lambda: defaults.lambda as usize, + num_ciphertexts: defaults.z as usize, + } + .into(), ), CorrelationId::new(), e3_id, @@ -663,7 +685,19 @@ impl ThresholdKeyshare { .try_into() .context("Error extracting data from compute process")?; - let (pk_share, sk_sss) = (output.pk_share, output.sk_sss); + let (pk_share, sk_sss, e_sm_raw) = ( + output.pk_share.clone(), + output.sk_sss, + output.e_sm_raw.clone(), + ); + + // 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"); @@ -672,62 +706,22 @@ impl ThresholdKeyshare { 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 }, )) })?; - // Fire ZK proof request for PK generation (T1a) - let state = self.state.try_get()?; - let e3_id = state.e3_id.clone(); - - self.bus.publish(ComputeRequest::zk( - ZkRequest::PkGeneration(PkGenerationProofRequest::new( - output.pk0_share_raw, - output.a_raw, - output.sk_raw, - output.eek_raw, - self.share_enc_preset, - self.comm - )), - CorrelationId::new(), - e3_id, - ))?; - - Ok(()) - } - - pub fn handle_pk_generation_proof_response(&mut self, proof: Proof) -> Result<()> { - self.state.try_mutate(|s| { - info!("try_store_pk_generation_proof"); - let current: GeneratingThresholdShareData = s.clone().try_into()?; - - let next = match (¤t.pk_share, ¤t.sk_sss, ¤t.esi_sss) { - (Some(pk_share), Some(sk_sss), Some(esi_sss)) => { - KeyshareState::AggregatingDecryptionKey(AggregatingDecryptionKey { - pk_share: pk_share.clone(), - sk_sss: sk_sss.clone(), - esi_sss: esi_sss.clone(), - sk_bfv: current.sk_bfv, - collected_encryption_keys: current.collected_encryption_keys, - }) - } - _ => KeyshareState::GeneratingThresholdShare(GeneratingThresholdShareData { - pk_generation_proof: Some(proof), - ..current - }), - }; - - s.new_state(next) - })?; - - 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(GenEsiSss { + ciphernode_selected, + e_sm_raw: current_state.e_sm_raw, + })?; } + Ok(()) } @@ -739,6 +733,8 @@ impl ThresholdKeyshare { pk_share, sk_sss, esi_sss, + e_sm_raw, + proof_request_data, collected_encryption_keys, .. }), @@ -786,33 +782,37 @@ impl ThresholdKeyshare { pk_share, sk_sss: encrypted_sk_sss, esi_sss: encrypted_esi_sss, + signed_pk_generation_proof: None, }; - // 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, + })?; + Ok(()) } @@ -924,14 +924,13 @@ impl ThresholdKeyshare { let address = state.get_address().to_owned(); let current: ReadyForDecryption = state.clone().try_into()?; - self.bus.publish( - KeyshareCreated { - pubkey: current.pk_share, - e3_id: e3_id.clone(), - node: address, - }, - ec, - )?; + self.bus.publish(KeyshareCreated { + pubkey: current.pk_share, + e3_id: e3_id.clone(), + node: address, + // TODO add proof + signed_pk_generation_proof: None, + })?; Ok(()) } 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 e80251e651..ea972ff1d8 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -30,19 +30,26 @@ 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, Event, EventPublisher, EventSubscriber, + EventType, PkBfvProofRequest, PkBfvProofResponse, PkGenerationProofRequest, + PkGenerationProofResponse, 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; @@ -369,12 +376,75 @@ fn handle_zk_request( } } +/// 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, - req: PkBfvProofRequest, + req: PkGenerationProofRequest, request: ComputeRequest, ) -> Result { - + // 1. Build BFV parameters from the threshold preset + let params = BfvParamSet::from(req.params_preset.clone()).build_arc(); + + // 2. 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(&req.sk, ¶ms) + .map_err(|e| make_zk_error(&request, format!("sk: {}", e)))?; + + let eek_poly = try_poly_from_bytes(&req.eek, ¶ms) + .map_err(|e| make_zk_error(&request, format!("eek: {}", e)))?; + + let e_sm_poly = try_poly_from_bytes(&req.e_sm, ¶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( diff --git a/crates/test-helpers/src/usecase_helpers.rs b/crates/test-helpers/src/usecase_helpers.rs index 6f1460b384..25dee111f1 100644 --- a/crates/test-helpers/src/usecase_helpers.rs +++ b/crates/test-helpers/src/usecase_helpers.rs @@ -69,15 +69,20 @@ pub fn generate_shares_hash_map( esi_per_ct, error_size: error_size.clone(), trbfv_config: trbfv_config.clone(), + e_sm_raw: None, }, )?; - let GenPkShareAndSkSssResponse { sk_sss, pk_share } = gen_pk_share_and_sk_sss( + let GenPkShareAndSkSssResponse { + sk_sss, pk_share, .. + } = gen_pk_share_and_sk_sss( &rng, &cipher, GenPkShareAndSkSssRequest { trbfv_config: trbfv_config.clone(), crp: ArcBytes::from_bytes(&crp.to_bytes()), + lambda: 40, + num_ciphertexts: 1, }, )?; @@ -110,6 +115,7 @@ pub fn generate_shares_hash_map( esi_sss: encrypted_esi_sss, sk_sss: encrypted_sk_sss, pk_share, + signed_pk_generation_proof: None, }, ); } diff --git a/crates/trbfv/src/gen_esi_sss.rs b/crates/trbfv/src/gen_esi_sss.rs index 26c14deddc..72ef56d2e9 100644 --- a/crates/trbfv/src/gen_esi_sss.rs +++ b/crates/trbfv/src/gen_esi_sss.rs @@ -5,6 +5,7 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use crate::{ + helpers::try_poly_from_bytes, shares::{Encrypted, SharedSecret}, TrBFVConfig, }; @@ -24,12 +25,15 @@ pub struct GenEsiSssRequest { pub error_size: ArcBytes, /// Smudging noise per ciphertext pub esi_per_ct: u64, + /// This is pre-generated smudging noise. If provided, use this instead of generating new. + pub e_sm_raw: Option, } struct InnerRequest { pub trbfv_config: TrBFVConfig, pub error_size: BigUint, pub esi_per_ct: u64, + pub e_sm_raw: Option, } impl TryFrom for InnerRequest { @@ -39,6 +43,7 @@ impl TryFrom for 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, }) } } @@ -81,26 +86,37 @@ pub fn gen_esi_sss( 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 esi_sss: Vec = if let Some(e_sm_raw) = req.e_sm_raw { + // we are going to be using pre generated smudging noise + let e_sm_poly = try_poly_from_bytes(&e_sm_raw, ¶ms)?; + let mut share_manager = ShareManager::new(num_ciphernodes, threshold, params.clone()); + vec![SharedSecret::from( + share_manager + .generate_secret_shares_from_poly(e_sm_poly.into(), &mut *rng.lock().unwrap())?, + )] + } else { + (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::>()? + }; 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 abc41ceea1..367d99be09 100644 --- a/crates/trbfv/src/gen_pk_share_and_sk_sss.rs +++ b/crates/trbfv/src/gen_pk_share_and_sk_sss.rs @@ -81,8 +81,8 @@ 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, + Ok(GenPkShareAndSkSssResponse { + pk_share, sk_sss, pk0_share_raw: value.pk0_share_raw, a_raw: value.a_raw, @@ -128,7 +128,8 @@ pub fn gen_pk_share_and_sk_sss( num_ciphernodes, threshold ); let sk_share = { SecretKey::random(¶ms, &mut *rng.lock().unwrap()) }; - let (pk0_share, a, sk_poly, eek) = { PublicKeyShare::new_extended(&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())?; @@ -159,13 +160,17 @@ pub fn gen_pk_share_and_sk_sss( share_manager.generate_secret_shares_from_poly(sk_poly, &mut *rng.lock().unwrap())? }); - (InnerResponse { - pk_share, - sk_sss, - pk0_share_raw, - a_raw, - sk_raw, - eek_raw, - e_sm_raw, - }, 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/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..e5db8cf609 100644 --- a/crates/zk-prover/src/actors/proof_request.rs +++ b/crates/zk-prover/src/actors/proof_request.rs @@ -10,11 +10,7 @@ use std::sync::Arc; use actix::{Actor, Addr, Context, Handler}; 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, + BusHandle, ComputeRequest, ComputeRequestError, ComputeRequestErrorKind, ComputeResponse, ComputeResponseKind, CorrelationId, E3id, EnclaveEvent, EnclaveEventData, EncryptionKey, EncryptionKeyCreated, EncryptionKeyPending, EventContext, EventPublisher, EventSubscriber, EventType, PkBfvProofRequest, Proof, ProofPayload, ProofType, Sequenced, SignedProofPayload, ThresholdShare, ThresholdShareCreated, ThresholdSharePending, TypedEvent, ZkRequest, ZkResponse }; use e3_utils::NotifySync; use tracing::{error, info}; @@ -25,6 +21,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 +36,7 @@ pub struct ProofRequestActor { bus: BusHandle, signer: PrivateKeySigner, pending: HashMap, + pending_threshold: HashMap, } impl ProofRequestActor { @@ -42,6 +45,7 @@ impl ProofRequestActor { bus: bus.clone(), signer, pending: HashMap::new(), + pending_threshold: HashMap::new(), } } @@ -50,6 +54,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 +85,112 @@ 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.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; + } + }; + + // Create share with signed proof + let mut share = (*pending.full_share).clone(); + share.signed_pk_generation_proof = Some(signed); + + // Publish per-party shares + let num_parties = share.num_parties(); + info!( + "Publishing ThresholdShareCreated for E3 {} to {} parties", + pending.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: pending.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 +214,7 @@ impl ProofRequestActor { key: Arc::new(key), external: false, }, - ec, + ec.clone(), ) { error!("Failed to publish EncryptionKeyCreated: {err}"); } @@ -134,10 +227,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,16 +249,12 @@ 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::ComputeResponse(data) => { - self.notify_sync(ctx, TypedEvent::new(data, ec)) - } - EnclaveEventData::ComputeRequestError(data) => { - self.notify_sync(ctx, TypedEvent::new(data, ec)) - } + 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)), + EnclaveEventData::ComputeRequestError(data) => self.notify_sync(ctx, TypedEvent::new(data, ec)), _ => (), } } @@ -177,6 +272,14 @@ 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/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 From 16e46904f70f5721ba422ab4870d711b4947f902 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Thu, 12 Feb 2026 18:41:48 +0000 Subject: [PATCH 03/15] chore: refactoring --- crates/events/src/enclave_event/mod.rs | 5 + .../pk_generation_proof_signed.rs | 24 +++ .../enclave_event/threshold_share_created.rs | 5 +- crates/keyshare/src/threshold_keyshare.rs | 163 ++++++++++-------- crates/multithread/src/multithread.rs | 16 +- crates/test-helpers/src/usecase_helpers.rs | 1 - crates/tests/tests/integration.rs | 143 +++++++++------ crates/zk-prover/src/actors/proof_request.rs | 20 ++- crates/zk-prover/src/traits.rs | 1 - 9 files changed, 232 insertions(+), 146 deletions(-) create mode 100644 crates/events/src/enclave_event/pk_generation_proof_signed.rs diff --git a/crates/events/src/enclave_event/mod.rs b/crates/events/src/enclave_event/mod.rs index 032852f01a..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; @@ -76,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::*; @@ -212,6 +214,7 @@ pub enum EnclaveEventData { TicketGenerated(TicketGenerated), TicketSubmitted(TicketSubmitted), PlaintextOutputPublished(PlaintextOutputPublished), + PkGenerationProofSigned(PkGenerationProofSigned), EnclaveError(EnclaveError), E3RequestComplete(E3RequestComplete), E3Failed(E3Failed), @@ -464,6 +467,7 @@ 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()), @@ -514,6 +518,7 @@ impl_event_types!( DecryptionshareCreated, PlaintextAggregated, PublishDocumentRequested, + PkGenerationProofSigned, E3RequestComplete, E3Failed, E3StageChanged, 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_created.rs b/crates/events/src/enclave_event/threshold_share_created.rs index 69e84f76d7..e9794a0397 100644 --- a/crates/events/src/enclave_event/threshold_share_created.rs +++ b/crates/events/src/enclave_event/threshold_share_created.rs @@ -4,7 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use crate::{E3id, SignedProofPayload}; +use crate::E3id; use actix::Message; use derivative::Derivative; use e3_trbfv::shares::BfvEncryptedShares; @@ -32,8 +32,6 @@ pub struct ThresholdShare { pub sk_sss: BfvEncryptedShares, /// BFV-encrypted esi_sss - one per secret key (sk), each recipient can decrypt their share pub esi_sss: Vec, - /// The signed proof payload for fault attribution - pub signed_pk_generation_proof: Option, } impl ThresholdShare { @@ -51,7 +49,6 @@ impl ThresholdShare { pk_share: self.pk_share.clone(), sk_sss, esi_sss, - signed_pk_generation_proof: self.signed_pk_generation_proof.clone(), }) } diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index d553ae5283..762a753a90 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -9,12 +9,7 @@ use anyhow::{anyhow, bail, Context, Result}; use e3_crypto::{Cipher, SensitiveBytes}; use e3_data::Persistable; use e3_events::{ - prelude::*, trap, BusHandle, CiphernodeSelected, CiphertextOutputPublished, ComputeRequest, - ComputeResponse, ComputeResponseKind, CorrelationId, DecryptionshareCreated, Die, - E3RequestComplete, E3id, EType, EnclaveEvent, EnclaveEventData, EncryptionKey, - EncryptionKeyCollectionFailed, EncryptionKeyCreated, EncryptionKeyPending, EventContext, KeyshareCreated, - PartyId, PkGenerationProofRequest, ThresholdShare, ThresholdShareCollectionFailed, - ThresholdShareCreated, ThresholdSharePending, TypedEvent, + BusHandle, CiphernodeSelected, CiphertextOutputPublished, ComputeRequest, ComputeResponse, ComputeResponseKind, CorrelationId, DecryptionshareCreated, Die, E3RequestComplete, E3id, EType, EnclaveEvent, EnclaveEventData, EncryptionKey, EncryptionKeyCollectionFailed, EncryptionKeyCreated, EncryptionKeyPending, EventContext, KeyshareCreated, PartyId, PkGenerationProofRequest, PkGenerationProofSigned, Sequenced, SignedProofPayload, ThresholdShare, ThresholdShareCollectionFailed, ThresholdShareCreated, ThresholdSharePending, TypedEvent, prelude::*, trap }; use e3_fhe::create_crp; use e3_fhe_params::{BfvParamSet, BfvPreset}; @@ -33,10 +28,11 @@ 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}; +use rand_chacha::ChaCha20Rng; use std::{ - mem, - sync::{Arc, Mutex}, + collections::HashMap, mem, sync::{Arc, Mutex} }; use tracing::{info, trace, warn}; @@ -99,12 +95,8 @@ pub struct GeneratingThresholdShareData { #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct AggregatingDecryptionKey { pk_share: ArcBytes, - sk_sss: Encrypted, - esi_sss: Vec>, - e_sm_raw: ArcBytes, sk_bfv: SensitiveBytes, - collected_encryption_keys: Vec>, - proof_request_data: ProofRequestData, + signed_pk_generation_proof: Option, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -112,6 +104,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)] @@ -119,6 +112,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)] @@ -417,6 +411,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 { @@ -472,18 +498,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, @@ -580,47 +594,43 @@ impl ThresholdKeyshare { let esi_sss = output.esi_sss; + // First store esi_sss in GeneratingThresholdShareData self.state.try_mutate(&ec, |s| { - use KeyshareState as K; - info!("try_store_esi_sss"); - let current: GeneratingThresholdShareData = s.clone().try_into()?; - let next = match ( - ¤t.pk_share, - ¤t.sk_sss, - ¤t.e_sm_raw, - ¤t.proof_request_data, - ) { - // If the other shares are here then transition to aggregation - (Some(pk_share), Some(sk_sss), Some(e_sm_raw), Some(proof_request_data)) => { - K::AggregatingDecryptionKey(AggregatingDecryptionKey { - esi_sss, - pk_share: pk_share.clone(), - sk_sss: sk_sss.clone(), - e_sm_raw: e_sm_raw.clone(), - sk_bfv: current.sk_bfv, - collected_encryption_keys: current.collected_encryption_keys, - proof_request_data: proof_request_data.clone(), - }) - } - // If the other shares are not here yet then don't transition - _ => K::GeneratingThresholdShare(GeneratingThresholdShareData { + s.new_state(KeyshareState::GeneratingThresholdShare( + GeneratingThresholdShareData { esi_sss: Some(esi_sss), ..current - }), - }; - - s.new_state(next) + }, + )) })?; info!("esi stored"); - if let Some(ThresholdKeyshareState { - state: KeyshareState::AggregatingDecryptionKey { .. }, - .. - }) = self.state.get() - { - self.handle_shares_generated(ec)?; + + // 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(()) } @@ -716,10 +726,10 @@ impl ThresholdKeyshare { // 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(GenEsiSss { + self.handle_gen_esi_sss_requested(TypedEvent::new(GenEsiSss { ciphernode_selected, e_sm_raw: current_state.e_sm_raw, - })?; + }, ec.clone()))?; } Ok(()) @@ -729,12 +739,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, - e_sm_raw, - proof_request_data, + 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, .. }), @@ -743,10 +753,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 @@ -782,7 +792,6 @@ impl ThresholdKeyshare { pk_share, sk_sss: encrypted_sk_sss, esi_sss: encrypted_esi_sss, - signed_pk_generation_proof: None, }; // Build the proof request @@ -811,7 +820,7 @@ impl ThresholdKeyshare { e3_id: e3_id.clone(), full_share: Arc::new(full_share), proof_request, - })?; + }, ec.clone())?; Ok(()) } @@ -906,14 +915,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) @@ -928,9 +938,8 @@ impl ThresholdKeyshare { pubkey: current.pk_share, e3_id: e3_id.clone(), node: address, - // TODO add proof - signed_pk_generation_proof: None, - })?; + signed_pk_generation_proof: current.signed_pk_generation_proof, + }, ec.clone())?; Ok(()) } @@ -951,6 +960,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) @@ -1034,6 +1044,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/src/multithread.rs b/crates/multithread/src/multithread.rs index ea972ff1d8..8f1c4eb48a 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -17,23 +17,15 @@ use actix::prelude::*; use actix::{Actor, Handler}; 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::run_once; +use e3_events::trap_fut; use e3_events::{ BusHandle, ComputeRequest, ComputeRequestError, ComputeRequestErrorKind, ComputeRequestKind, - ComputeResponse, EnclaveEvent, EnclaveEventData, Event, EventPublisher, EventSubscriber, + ComputeResponse, EnclaveEvent, EnclaveEventData, EventPublisher, EventSubscriber, EventType, PkBfvProofRequest, PkBfvProofResponse, PkGenerationProofRequest, - PkGenerationProofResponse, ZkError as ZkEventError, ZkRequest, ZkResponse, + PkGenerationProofResponse, TypedEvent, ZkError as ZkEventError, ZkRequest, ZkResponse, }; use e3_fhe_params::{BfvParamSet, BfvPreset}; use e3_polynomial::CrtPolynomial; diff --git a/crates/test-helpers/src/usecase_helpers.rs b/crates/test-helpers/src/usecase_helpers.rs index 25dee111f1..6d0b292272 100644 --- a/crates/test-helpers/src/usecase_helpers.rs +++ b/crates/test-helpers/src/usecase_helpers.rs @@ -115,7 +115,6 @@ pub fn generate_shares_hash_map( esi_sss: encrypted_esi_sss, sk_sss: encrypted_sk_sss, pk_share, - signed_pk_generation_proof: None, }, ); } diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index d6d6da1eb9..6261ded470 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,12 +111,25 @@ async fn setup_test_zk_backend() -> (ZkBackend, tempfile::TempDir) { .await .unwrap(); - let backend = ZkBackend::new( - BBPath::Default(bb_binary), - circuits_dir, - work_dir, - ZkConfig::default(), - ); + // 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, work_dir, ZkConfig::default()); (backend, temp) } else { println!("bb binary not found locally, downloading via ensure_installed()..."); @@ -478,44 +493,68 @@ async fn test_trbfv_actor() -> Result<()> { )); // First, wait for all EncryptionKeyCreated events (BFV key exchange) + // Each of the 5 parties: + // - EncryptionKeyPending = 5 + // - ComputeRequest (T0 ZK proof) = 5 + // - ComputeResponse (T0 ZK proof) = 5 + // - EncryptionKeyCreated = 5 + // Total: 20 events let encryption_keys_timer = Instant::now(); - let expected = vec![ - "EncryptionKeyCreated", - "EncryptionKeyCreated", - "EncryptionKeyCreated", - "EncryptionKeyCreated", - "EncryptionKeyCreated", - ]; - let _ = nodes - .take_history_with_timeout(0, expected.len(), Duration::from_secs(1000)) + let expected_count = 5 + 5 + 5 + 5; // EncKeyPending + T0 ComputeReq + T0 ComputeResp + EncKeyCreated + println!( + "DEBUG: Waiting for {} encryption key events...", + expected_count + ); + let h = nodes + .take_history_with_timeout(0, expected_count, Duration::from_secs(1000)) .await?; + println!("DEBUG: EncryptionKey phase events: {:?}", h.event_types()); report.push(( "All EncryptionKeyCreated events", encryption_keys_timer.elapsed(), )); - // Then wait for all ThresholdShareCreated events - // With domain-level splitting, each of the 5 parties publishes 5 events (one per target party) - // Total: 5 parties × 5 targets = 25 events + // Then wait for share generation compute events + ThresholdShareCreated + PkGenerationProofSigned + // Each of the 5 parties: + // - ComputeRequest + ComputeResponse for GenPkShareAndSkSss = 10 + // - ComputeRequest + ComputeResponse for GenEsiSss = 10 + // - ThresholdSharePending = 5 + // - ComputeRequest + ComputeResponse for T1 ZK proof = 10 + // - 5 ThresholdShareCreated events (one per target party) = 25 total + // - 1 PkGenerationProofSigned event = 5 total + // Total: 10 + 10 + 5 + 10 + 25 + 5 = 65 events let shares_timer = Instant::now(); - let expected: Vec<&str> = (0..25).map(|_| "ThresholdShareCreated").collect(); - let _ = nodes - .take_history_with_timeout(0, expected.len(), Duration::from_secs(1000)) + let expected_count = 10 + 10 + 5 + 10 + 25 + 5; // GenPk + GenEsi + TSPending + T1 ZK + TSCreated + PkGenProof + println!( + "DEBUG: Waiting for {} share generation events...", + expected_count + ); + let h = nodes + .take_history_with_timeout(0, expected_count, Duration::from_secs(1000)) .await?; - report.push(("All ThresholdShareCreated events", shares_timer.elapsed())); - + println!( + "DEBUG: Share generation phase events: {:?}", + h.event_types() + ); + report.push(("All share generation events", shares_timer.elapsed())); + + // Wait for CalculateDecryptionKey compute events + KeyshareCreated + PublicKeyAggregated + // Each of the 5 parties: + // - ComputeRequest for CalculateDecryptionKey + // - ComputeResponse for CalculateDecryptionKey + // - KeyshareCreated + // Plus 1 PublicKeyAggregated at the end + // Total: 5 + 5 + 5 + 1 = 16 events let shares_to_pubkey_agg_timer = Instant::now(); - let expected = vec![ - "KeyshareCreated", - "KeyshareCreated", - "KeyshareCreated", - "KeyshareCreated", - "KeyshareCreated", - "PublicKeyAggregated", - ]; + let expected_count = 5 + 5 + 5 + 1; // ComputeRequest + ComputeResponse + KeyshareCreated + PublicKeyAggregated + println!( + "DEBUG: Waiting for {} decryption key events...", + expected_count + ); let h = nodes - .take_history_with_timeout(0, expected.len(), Duration::from_secs(1000)) + .take_history_with_timeout(0, expected_count, Duration::from_secs(1000)) .await?; + println!("DEBUG: Decryption key phase events: {:?}", h.event_types()); report.push(( "ThresholdShares -> PublicKeyAggregated", @@ -527,14 +566,16 @@ async fn test_trbfv_actor() -> Result<()> { e3_requested_timer.elapsed(), )); let app_gen_timer = Instant::now(); - assert_eq!(h.event_types(), expected); - // Aggregate decryption + // Verify we got the expected events (last should be PublicKeyAggregated) // First we get the public key println!("Getting public key"); let Some(EnclaveEventData::PublicKeyAggregated(pubkey_event)) = h.last().map(|e| e.get_data()) else { - panic!("Was expecting event to be PublicKeyAggregated"); + panic!( + "Was expecting last event to be PublicKeyAggregated, got: {:?}", + h.event_types() + ); }; let pubkey_bytes = pubkey_event.pubkey.clone(); @@ -582,23 +623,24 @@ async fn test_trbfv_actor() -> Result<()> { println!("CiphertextOutputPublished event has been dispatched!"); // Lets grab decryption share events - let expected = vec![ - "CiphertextOutputPublished", - "DecryptionshareCreated", - "DecryptionshareCreated", - "DecryptionshareCreated", - "ComputeRequest", - "DecryptionshareCreated", - "DecryptionshareCreated", - "ComputeResponse", - "PlaintextAggregated", - ]; + // Each of the 5 parties: + // - ComputeRequest for CalculateDecryptionShare + // - ComputeResponse for CalculateDecryptionShare + // - DecryptionshareCreated + // Plus aggregation: + // - 1 CiphertextOutputPublished + // - 1 ComputeRequest (PlaintextAggregation) + // - 1 ComputeResponse (PlaintextAggregation) + // - 1 PlaintextAggregated + // Total: 1 + 5*3 + 3 = 19 events + let expected_count = 1 + (5 * 3) + 3; + println!("DEBUG: Waiting for {} decryption events...", expected_count); let h = nodes - .take_history_with_timeout(0, expected.len(), Duration::from_secs(1000)) + .take_history_with_timeout(0, expected_count, Duration::from_secs(1000)) .await?; + println!("DEBUG: Decryption phase events: {:?}", h.event_types()); - assert_eq!(h.event_types(), expected); report.push(( "Ciphertext published -> PlaintextAggregated", publishing_ct_timer.elapsed(), @@ -609,7 +651,10 @@ async fn test_trbfv_actor() -> Result<()> { .. })) = h.last().map(|e| e.get_data()) else { - bail!("bad event") + bail!( + "Expected last event to be PlaintextAggregated, got: {:?}", + h.event_types() + ) }; let results = plaintext diff --git a/crates/zk-prover/src/actors/proof_request.rs b/crates/zk-prover/src/actors/proof_request.rs index e5db8cf609..94403a00ea 100644 --- a/crates/zk-prover/src/actors/proof_request.rs +++ b/crates/zk-prover/src/actors/proof_request.rs @@ -10,7 +10,8 @@ use std::sync::Arc; use actix::{Actor, Addr, Context, Handler}; use alloy::signers::local::PrivateKeySigner; use e3_events::{ - BusHandle, ComputeRequest, ComputeRequestError, ComputeRequestErrorKind, ComputeResponse, ComputeResponseKind, CorrelationId, E3id, EnclaveEvent, EnclaveEventData, EncryptionKey, EncryptionKeyCreated, EncryptionKeyPending, EventContext, EventPublisher, EventSubscriber, EventType, PkBfvProofRequest, Proof, ProofPayload, ProofType, Sequenced, SignedProofPayload, ThresholdShare, ThresholdShareCreated, ThresholdSharePending, TypedEvent, ZkRequest, ZkResponse + BusHandle, ComputeRequest, ComputeRequestError, ComputeRequestErrorKind, ComputeResponse, ComputeResponseKind, CorrelationId, E3id, EnclaveEvent, EnclaveEventData, EncryptionKey, EncryptionKeyCreated, EncryptionKeyPending, EventContext, EventPublisher, EventSubscriber, EventType, PkBfvProofRequest, Proof, ProofPayload, ProofType, Sequenced, SignedProofPayload, ThresholdShare, ThresholdShareCreated, ThresholdSharePending, TypedEvent, ZkRequest, ZkResponse, + PkGenerationProofSigned, }; use e3_utils::NotifySync; use tracing::{error, info}; @@ -148,9 +149,8 @@ impl ProofRequestActor { } }; - // Create share with signed proof - let mut share = (*pending.full_share).clone(); - share.signed_pk_generation_proof = Some(signed); + let party_id = pending.full_share.party_id; + let share = &pending.full_share; // Publish per-party shares let num_parties = share.num_parties(); @@ -176,6 +176,18 @@ impl ProofRequestActor { error!("Failed to extract share for party {}", recipient_party_id); } } + + 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}"); + } } fn handle_pk_bfv_response(&mut self, correlation_id: &CorrelationId, proof: Proof, ec: &EventContext) { 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) } } From 1227d57a1eece9f294e19b9dc06623a0f7dbb93c Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Thu, 12 Feb 2026 21:30:29 +0000 Subject: [PATCH 04/15] chore: integration test --- crates/tests/tests/integration.rs | 116 +++++++++++------------------- 1 file changed, 43 insertions(+), 73 deletions(-) diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index 6261ded470..16b507475d 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -493,68 +493,44 @@ async fn test_trbfv_actor() -> Result<()> { )); // First, wait for all EncryptionKeyCreated events (BFV key exchange) - // Each of the 5 parties: - // - EncryptionKeyPending = 5 - // - ComputeRequest (T0 ZK proof) = 5 - // - ComputeResponse (T0 ZK proof) = 5 - // - EncryptionKeyCreated = 5 - // Total: 20 events let encryption_keys_timer = Instant::now(); - let expected_count = 5 + 5 + 5 + 5; // EncKeyPending + T0 ComputeReq + T0 ComputeResp + EncKeyCreated - println!( - "DEBUG: Waiting for {} encryption key events...", - expected_count - ); - let h = nodes - .take_history_with_timeout(0, expected_count, Duration::from_secs(1000)) + let expected = vec![ + "EncryptionKeyCreated", + "EncryptionKeyCreated", + "EncryptionKeyCreated", + "EncryptionKeyCreated", + "EncryptionKeyCreated", + ]; + let _ = nodes + .take_history_with_timeout(0, expected.len(), Duration::from_secs(1000)) .await?; - println!("DEBUG: EncryptionKey phase events: {:?}", h.event_types()); report.push(( "All EncryptionKeyCreated events", encryption_keys_timer.elapsed(), )); - // Then wait for share generation compute events + ThresholdShareCreated + PkGenerationProofSigned - // Each of the 5 parties: - // - ComputeRequest + ComputeResponse for GenPkShareAndSkSss = 10 - // - ComputeRequest + ComputeResponse for GenEsiSss = 10 - // - ThresholdSharePending = 5 - // - ComputeRequest + ComputeResponse for T1 ZK proof = 10 - // - 5 ThresholdShareCreated events (one per target party) = 25 total - // - 1 PkGenerationProofSigned event = 5 total - // Total: 10 + 10 + 5 + 10 + 25 + 5 = 65 events + // Then wait for all ThresholdShareCreated events + // With domain-level splitting, each of the 5 parties publishes 5 events (one per target party) + // Total: 5 parties × 5 targets = 25 events let shares_timer = Instant::now(); - let expected_count = 10 + 10 + 5 + 10 + 25 + 5; // GenPk + GenEsi + TSPending + T1 ZK + TSCreated + PkGenProof - println!( - "DEBUG: Waiting for {} share generation events...", - expected_count - ); - let h = nodes - .take_history_with_timeout(0, expected_count, Duration::from_secs(1000)) + let expected: Vec<&str> = (0..25).map(|_| "ThresholdShareCreated").collect(); + let _ = nodes + .take_history_with_timeout(0, expected.len(), Duration::from_secs(1000)) .await?; - println!( - "DEBUG: Share generation phase events: {:?}", - h.event_types() - ); - report.push(("All share generation events", shares_timer.elapsed())); - - // Wait for CalculateDecryptionKey compute events + KeyshareCreated + PublicKeyAggregated - // Each of the 5 parties: - // - ComputeRequest for CalculateDecryptionKey - // - ComputeResponse for CalculateDecryptionKey - // - KeyshareCreated - // Plus 1 PublicKeyAggregated at the end - // Total: 5 + 5 + 5 + 1 = 16 events + report.push(("All ThresholdShareCreated events", shares_timer.elapsed())); + let shares_to_pubkey_agg_timer = Instant::now(); - let expected_count = 5 + 5 + 5 + 1; // ComputeRequest + ComputeResponse + KeyshareCreated + PublicKeyAggregated - println!( - "DEBUG: Waiting for {} decryption key events...", - expected_count - ); + let expected = vec![ + "KeyshareCreated", + "KeyshareCreated", + "KeyshareCreated", + "KeyshareCreated", + "KeyshareCreated", + "PublicKeyAggregated", + ]; let h = nodes - .take_history_with_timeout(0, expected_count, Duration::from_secs(1000)) + .take_history_with_timeout(0, expected.len(), Duration::from_secs(1000)) .await?; - println!("DEBUG: Decryption key phase events: {:?}", h.event_types()); report.push(( "ThresholdShares -> PublicKeyAggregated", @@ -566,16 +542,14 @@ async fn test_trbfv_actor() -> Result<()> { e3_requested_timer.elapsed(), )); let app_gen_timer = Instant::now(); + assert_eq!(h.event_types(), expected); + // Aggregate decryption - // Verify we got the expected events (last should be PublicKeyAggregated) // First we get the public key println!("Getting public key"); let Some(EnclaveEventData::PublicKeyAggregated(pubkey_event)) = h.last().map(|e| e.get_data()) else { - panic!( - "Was expecting last event to be PublicKeyAggregated, got: {:?}", - h.event_types() - ); + panic!("Was expecting event to be PublicKeyAggregated"); }; let pubkey_bytes = pubkey_event.pubkey.clone(); @@ -623,24 +597,23 @@ async fn test_trbfv_actor() -> Result<()> { println!("CiphertextOutputPublished event has been dispatched!"); // Lets grab decryption share events - // Each of the 5 parties: - // - ComputeRequest for CalculateDecryptionShare - // - ComputeResponse for CalculateDecryptionShare - // - DecryptionshareCreated - // Plus aggregation: - // - 1 CiphertextOutputPublished - // - 1 ComputeRequest (PlaintextAggregation) - // - 1 ComputeResponse (PlaintextAggregation) - // - 1 PlaintextAggregated - // Total: 1 + 5*3 + 3 = 19 events - let expected_count = 1 + (5 * 3) + 3; - println!("DEBUG: Waiting for {} decryption events...", expected_count); + let expected = vec![ + "CiphertextOutputPublished", + "DecryptionshareCreated", + "DecryptionshareCreated", + "DecryptionshareCreated", + "ComputeRequest", + "DecryptionshareCreated", + "DecryptionshareCreated", + "ComputeResponse", + "PlaintextAggregated", + ]; let h = nodes - .take_history_with_timeout(0, expected_count, Duration::from_secs(1000)) + .take_history_with_timeout(0, expected.len(), Duration::from_secs(1000)) .await?; - println!("DEBUG: Decryption phase events: {:?}", h.event_types()); + assert_eq!(h.event_types(), expected); report.push(( "Ciphertext published -> PlaintextAggregated", publishing_ct_timer.elapsed(), @@ -651,10 +624,7 @@ async fn test_trbfv_actor() -> Result<()> { .. })) = h.last().map(|e| e.get_data()) else { - bail!( - "Expected last event to be PlaintextAggregated, got: {:?}", - h.event_types() - ) + bail!("bad event") }; let results = plaintext From 9e722e77992dbc1fd18c0911a0043c2b74af9931 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Thu, 12 Feb 2026 22:26:22 +0000 Subject: [PATCH 05/15] chore: update params in contract deployment --- packages/enclave-contracts/scripts/deployEnclave.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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"), From 6a8bf462a4534375ea051818fa7f7ae7147a56ab Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 13 Feb 2026 10:03:05 +0000 Subject: [PATCH 06/15] chore: pr comment --- crates/zk-prover/src/actors/proof_request.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/zk-prover/src/actors/proof_request.rs b/crates/zk-prover/src/actors/proof_request.rs index 94403a00ea..3321dbffbd 100644 --- a/crates/zk-prover/src/actors/proof_request.rs +++ b/crates/zk-prover/src/actors/proof_request.rs @@ -106,7 +106,7 @@ impl ProofRequestActor { 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.remove(&correlation_id); + self.pending_threshold.remove(&correlation_id); } } From b8f15539f6eb20dd6eb315c3b9c6c719d0863c8c Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 13 Feb 2026 10:14:41 +0000 Subject: [PATCH 07/15] chore: update gitignore --- templates/default/.gitignore | 1 + 1 file changed, 1 insertion(+) 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 From 391215c5ffc8908ecdb5f912dc89534a7d188efe Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 13 Feb 2026 15:32:32 +0000 Subject: [PATCH 08/15] chore: update versions.json --- crates/zk-prover/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/zk-prover/src/config.rs b/crates/zk-prover/src/config.rs index 46a0e299b8..1a8e7bdd8b 100644 --- a/crates/zk-prover/src/config.rs +++ b/crates/zk-prover/src/config.rs @@ -14,7 +14,7 @@ use tokio::fs; use tracing::{debug, warn}; const VERSIONS_MANIFEST_URL: &str = - "https://raw.githubusercontent.com/gnosisguild/enclave/main/crates/zk-prover/versions.json"; + "https://raw.githubusercontent.com/gnosisguild/enclave/feat/integrate-t1-ciphernode/crates/zk-prover/versions.json"; const BB_VERSION: &str = "3.0.0-nightly.20251104"; const CIRCUITS_VERSION: &str = "0.1.14"; From 201c3f298db0ac2a2b21d5a13b32852658a2a072 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 13 Feb 2026 16:30:48 +0000 Subject: [PATCH 09/15] chore: revert change to manifest url --- crates/zk-prover/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/zk-prover/src/config.rs b/crates/zk-prover/src/config.rs index 1a8e7bdd8b..46a0e299b8 100644 --- a/crates/zk-prover/src/config.rs +++ b/crates/zk-prover/src/config.rs @@ -14,7 +14,7 @@ use tokio::fs; use tracing::{debug, warn}; const VERSIONS_MANIFEST_URL: &str = - "https://raw.githubusercontent.com/gnosisguild/enclave/feat/integrate-t1-ciphernode/crates/zk-prover/versions.json"; + "https://raw.githubusercontent.com/gnosisguild/enclave/main/crates/zk-prover/versions.json"; const BB_VERSION: &str = "3.0.0-nightly.20251104"; const CIRCUITS_VERSION: &str = "0.1.14"; From 59c227ef9d079979740f6058f9842efe6e1654f0 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Sat, 14 Feb 2026 10:26:25 +0000 Subject: [PATCH 10/15] chore: simplify flow --- crates/keyshare/src/threshold_keyshare.rs | 181 ++++++++++----------- crates/test-helpers/src/usecase_helpers.rs | 27 ++- crates/trbfv/src/gen_esi_sss.rs | 67 +++----- crates/trbfv/tests/integration.rs | 25 +-- 4 files changed, 125 insertions(+), 175 deletions(-) diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index 762a753a90..a2ffec2e54 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -47,7 +47,7 @@ pub struct GenPkShareAndSkSss(CiphernodeSelected); #[rtype(result = "()")] pub struct GenEsiSss { pub ciphernode_selected: CiphernodeSelected, - pub e_sm_raw: Option, + pub e_sm_raw: ArcBytes, } #[derive(Message)] @@ -546,96 +546,7 @@ impl ThresholdKeyshare { Ok(()) } - /// 2. GenEsiSss - pub fn handle_gen_esi_sss_requested(&self, msg: TypedEvent) -> Result<()> { - let (msg, ec) = msg.into_components(); - info!("GenEsiSss on ThresholdKeyshare"); - - let evt = msg.ciphernode_selected; - let e_sm_raw = msg.e_sm_raw; - 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(); - - let state = self - .state - .get() - .ok_or(anyhow!("State not found on ThrehsoldKeyshare"))?; - - let trbfv_config = state.get_trbfv_config(); - - let event = ComputeRequest::trbfv( - TrBFVRequest::GenEsiSss( - GenEsiSssRequest { - trbfv_config, - error_size, - esi_per_ct: esi_per_ct as u64, - e_sm_raw, - } - .into(), - ), - CorrelationId::new(), - e3_id, - ); - - self.bus.publish(event, ec)?; - Ok(()) - } - - /// 2a. 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 esi_sss = output.esi_sss; - - // First store esi_sss in GeneratingThresholdShareData - self.state.try_mutate(&ec, |s| { - info!("try_store_esi_sss"); - let current: GeneratingThresholdShareData = s.clone().try_into()?; - s.new_state(KeyshareState::GeneratingThresholdShare( - GeneratingThresholdShareData { - esi_sss: Some(esi_sss), - ..current - }, - )) - })?; - - 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(()) - } - - /// 3. GenPkShareAndSkSss + /// 2. GenPkShareAndSkSss pub fn handle_gen_pk_share_and_sk_sss_requested( &self, msg: TypedEvent, @@ -684,7 +595,7 @@ impl ThresholdKeyshare { Ok(()) } - /// 3a. GenPkShareAndSkSss result + /// 2a. GenPkShareAndSkSss result pub fn handle_gen_pk_share_and_sk_sss_response( &mut self, res: TypedEvent, @@ -728,13 +639,97 @@ impl ThresholdKeyshare { 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, + e_sm_raw: current_state.e_sm_raw.expect("e_sm_raw should be set at this point"), }, ec.clone()))?; } Ok(()) } + /// 3. GenEsiSss + pub fn handle_gen_esi_sss_requested(&self, msg: TypedEvent) -> Result<()> { + let (msg, ec) = msg.into_components(); + info!("GenEsiSss on ThresholdKeyshare"); + + let evt = msg.ciphernode_selected; + let e_sm_raw = msg.e_sm_raw; + let CiphernodeSelected { + e3_id, + .. + } = evt.clone(); + + let state = self + .state + .get() + .ok_or(anyhow!("State not found on ThrehsoldKeyshare"))?; + + let trbfv_config = state.get_trbfv_config(); + + let event = ComputeRequest::trbfv( + TrBFVRequest::GenEsiSss( + GenEsiSssRequest { + trbfv_config, + e_sm_raw, + } + .into(), + ), + CorrelationId::new(), + e3_id, + ); + + self.bus.publish(event, ec)?; + Ok(()) + } + + /// 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 esi_sss = output.esi_sss; + + // First store esi_sss in GeneratingThresholdShareData + self.state.try_mutate(&ec, |s| { + info!("try_store_esi_sss"); + let current: GeneratingThresholdShareData = s.clone().try_into()?; + s.new_state(KeyshareState::GeneratingThresholdShare( + GeneratingThresholdShareData { + esi_sss: Some(esi_sss), + ..current + }, + )) + })?; + + 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(()) + } + + /// 4. SharesGenerated - Encrypt shares with BFV and publish pub fn handle_shares_generated(&mut self, ec: EventContext) -> Result<()> { let Some(ThresholdKeyshareState { diff --git a/crates/test-helpers/src/usecase_helpers.rs b/crates/test-helpers/src/usecase_helpers.rs index 6d0b292272..cf0ea5224e 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,19 +60,11 @@ 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( - &rng, - &cipher, - GenEsiSssRequest { - esi_per_ct, - error_size: error_size.clone(), - trbfv_config: trbfv_config.clone(), - e_sm_raw: None, - }, - )?; - let GenPkShareAndSkSssResponse { - sk_sss, pk_share, .. + sk_sss, + pk_share, + e_sm_raw, + .. } = gen_pk_share_and_sk_sss( &rng, &cipher, @@ -86,6 +76,15 @@ pub fn generate_shares_hash_map( }, )?; + let GenEsiSssResponse { esi_sss } = gen_esi_sss( + &rng, + &cipher, + GenEsiSssRequest { + trbfv_config: trbfv_config.clone(), + e_sm_raw, + }, + )?; + // Decrypt locally stored secrets let decrypted_sk_sss: SharedSecret = sk_sss.decrypt(&cipher)?; let decrypted_esi_sss: Vec = esi_sss diff --git a/crates/trbfv/src/gen_esi_sss.rs b/crates/trbfv/src/gen_esi_sss.rs index 72ef56d2e9..7eb473f9f4 100644 --- a/crates/trbfv/src/gen_esi_sss.rs +++ b/crates/trbfv/src/gen_esi_sss.rs @@ -12,8 +12,7 @@ use crate::{ 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; @@ -21,19 +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. If provided, use this instead of generating new. - pub e_sm_raw: Option, + /// 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: Option, + pub e_sm_raw: ArcBytes, } impl TryFrom for InnerRequest { @@ -41,8 +34,6 @@ 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, }) } @@ -73,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, @@ -84,39 +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 = if let Some(e_sm_raw) = req.e_sm_raw { - // we are going to be using pre generated smudging noise - let e_sm_poly = try_poly_from_bytes(&e_sm_raw, ¶ms)?; - let mut share_manager = ShareManager::new(num_ciphernodes, threshold, params.clone()); - vec![SharedSecret::from( - share_manager - .generate_secret_shares_from_poly(e_sm_poly.into(), &mut *rng.lock().unwrap())?, - )] - } else { - (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/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)?; From 45cb74542b32b98a3feda067ade89c8afbd81814 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Sun, 15 Feb 2026 17:50:39 +0000 Subject: [PATCH 11/15] chore: conflicts --- crates/keyshare/src/threshold_keyshare.rs | 70 +++++++++++-------- crates/multithread/src/multithread.rs | 10 +-- crates/tests/tests/integration.rs | 7 +- crates/zk-prover/src/actors/proof_request.rs | 72 ++++++++++++++------ 4 files changed, 106 insertions(+), 53 deletions(-) diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index a2ffec2e54..5c7a79cc26 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -9,7 +9,13 @@ use anyhow::{anyhow, bail, Context, Result}; use e3_crypto::{Cipher, SensitiveBytes}; use e3_data::Persistable; use e3_events::{ - BusHandle, CiphernodeSelected, CiphertextOutputPublished, ComputeRequest, ComputeResponse, ComputeResponseKind, CorrelationId, DecryptionshareCreated, Die, E3RequestComplete, E3id, EType, EnclaveEvent, EnclaveEventData, EncryptionKey, EncryptionKeyCollectionFailed, EncryptionKeyCreated, EncryptionKeyPending, EventContext, KeyshareCreated, PartyId, PkGenerationProofRequest, PkGenerationProofSigned, Sequenced, SignedProofPayload, ThresholdShare, ThresholdShareCollectionFailed, ThresholdShareCreated, ThresholdSharePending, TypedEvent, prelude::*, trap + prelude::*, trap, BusHandle, CiphernodeSelected, CiphertextOutputPublished, ComputeRequest, + ComputeResponse, ComputeResponseKind, CorrelationId, DecryptionshareCreated, Die, + E3RequestComplete, E3id, EType, EnclaveEvent, EnclaveEventData, EncryptionKey, + EncryptionKeyCollectionFailed, EncryptionKeyCreated, EncryptionKeyPending, EventContext, + KeyshareCreated, PartyId, PkGenerationProofRequest, PkGenerationProofSigned, Sequenced, + SignedProofPayload, ThresholdShare, ThresholdShareCollectionFailed, ThresholdShareCreated, + ThresholdSharePending, TypedEvent, }; use e3_fhe::create_crp; use e3_fhe_params::{BfvParamSet, BfvPreset}; @@ -32,7 +38,9 @@ use fhe_traits::{DeserializeParametrized, Serialize}; use rand::{rngs::OsRng, SeedableRng}; use rand_chacha::ChaCha20Rng; use std::{ - collections::HashMap, mem, sync::{Arc, Mutex} + collections::HashMap, + mem, + sync::{Arc, Mutex}, }; use tracing::{info, trace, warn}; @@ -539,9 +547,10 @@ impl ThresholdKeyshare { )) })?; - self.handle_gen_pk_share_and_sk_sss_requested(TypedEvent::new(GenPkShareAndSkSss( - current.ciphernode_selected, - ), ec))?; + self.handle_gen_pk_share_and_sk_sss_requested(TypedEvent::new( + GenPkShareAndSkSss(current.ciphernode_selected), + ec, + ))?; Ok(()) } @@ -637,26 +646,28 @@ impl ThresholdKeyshare { // 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()))?; + 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. GenEsiSss - pub fn handle_gen_esi_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!("GenEsiSss on ThresholdKeyshare"); let evt = msg.ciphernode_selected; let e_sm_raw = msg.e_sm_raw; - let CiphernodeSelected { - e3_id, - .. - } = evt.clone(); + let CiphernodeSelected { e3_id, .. } = evt.clone(); let state = self .state @@ -729,7 +740,6 @@ impl ThresholdKeyshare { Ok(()) } - /// 4. SharesGenerated - Encrypt shares with BFV and publish pub fn handle_shares_generated(&mut self, ec: EventContext) -> Result<()> { let Some(ThresholdKeyshareState { @@ -811,11 +821,14 @@ impl ThresholdKeyshare { ); // 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())?; + self.bus.publish( + ThresholdSharePending { + e3_id: e3_id.clone(), + full_share: Arc::new(full_share), + proof_request, + }, + ec.clone(), + )?; Ok(()) } @@ -929,12 +942,15 @@ impl ThresholdKeyshare { let address = state.get_address().to_owned(); let current: ReadyForDecryption = state.clone().try_into()?; - self.bus.publish(KeyshareCreated { - pubkey: current.pk_share, - e3_id: e3_id.clone(), - node: address, - signed_pk_generation_proof: current.signed_pk_generation_proof, - }, ec.clone())?; + self.bus.publish( + KeyshareCreated { + pubkey: current.pk_share, + e3_id: e3_id.clone(), + node: address, + signed_pk_generation_proof: current.signed_pk_generation_proof, + }, + ec.clone(), + )?; Ok(()) } diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index 8f1c4eb48a..5c15b5bed9 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -17,15 +17,15 @@ use actix::prelude::*; use actix::{Actor, Handler}; use anyhow::Result; use e3_crypto::Cipher; -use e3_events::EType; -use e3_events::EffectsEnabled; use e3_events::run_once; use e3_events::trap_fut; +use e3_events::EType; +use e3_events::EffectsEnabled; use e3_events::{ BusHandle, ComputeRequest, ComputeRequestError, ComputeRequestErrorKind, ComputeRequestKind, - ComputeResponse, EnclaveEvent, EnclaveEventData, EventPublisher, EventSubscriber, - EventType, PkBfvProofRequest, PkBfvProofResponse, PkGenerationProofRequest, - PkGenerationProofResponse, TypedEvent, ZkError as ZkEventError, ZkRequest, ZkResponse, + 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; diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index 16b507475d..f3078e5154 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -129,7 +129,12 @@ async fn setup_test_zk_backend() -> (ZkBackend, tempfile::TempDir) { .await .unwrap(); - let backend = ZkBackend::new(BBPath::Default(bb_binary), circuits_dir, work_dir, ZkConfig::default()); + let backend = ZkBackend::new( + BBPath::Default(bb_binary), + circuits_dir, + work_dir, + ZkConfig::default(), + ); (backend, temp) } else { println!("bb binary not found locally, downloading via ensure_installed()..."); diff --git a/crates/zk-prover/src/actors/proof_request.rs b/crates/zk-prover/src/actors/proof_request.rs index 3321dbffbd..5460e36fc0 100644 --- a/crates/zk-prover/src/actors/proof_request.rs +++ b/crates/zk-prover/src/actors/proof_request.rs @@ -10,8 +10,12 @@ use std::sync::Arc; use actix::{Actor, Addr, Context, Handler}; use alloy::signers::local::PrivateKeySigner; use e3_events::{ - BusHandle, ComputeRequest, ComputeRequestError, ComputeRequestErrorKind, ComputeResponse, ComputeResponseKind, CorrelationId, E3id, EnclaveEvent, EnclaveEventData, EncryptionKey, EncryptionKeyCreated, EncryptionKeyPending, EventContext, EventPublisher, EventSubscriber, EventType, PkBfvProofRequest, Proof, ProofPayload, ProofType, Sequenced, SignedProofPayload, ThresholdShare, ThresholdShareCreated, ThresholdSharePending, TypedEvent, ZkRequest, ZkResponse, - PkGenerationProofSigned, + BusHandle, ComputeRequest, ComputeRequestError, ComputeRequestErrorKind, ComputeResponse, + ComputeResponseKind, CorrelationId, E3id, EnclaveEvent, EnclaveEventData, EncryptionKey, + 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}; @@ -123,7 +127,12 @@ impl ProofRequestActor { } } - fn handle_pk_generation_response(&mut self, correlation_id: &CorrelationId, proof: Proof, ec: &EventContext) { + 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; }; @@ -161,12 +170,15 @@ impl ProofRequestActor { 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: pending.e3_id.clone(), - share: Arc::new(party_share), - target_party_id: recipient_party_id as u64, - external: false, - }, ec.clone()) { + if let Err(err) = self.bus.publish( + ThresholdShareCreated { + e3_id: pending.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 @@ -181,16 +193,24 @@ impl ProofRequestActor { "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()) { + 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}"); } } - fn handle_pk_bfv_response(&mut self, correlation_id: &CorrelationId, proof: Proof, ec: &EventContext) { + fn handle_pk_bfv_response( + &mut self, + correlation_id: &CorrelationId, + proof: Proof, + ec: &EventContext, + ) { let Some(pending) = self.pending.remove(&correlation_id) else { return; }; @@ -263,10 +283,18 @@ impl Handler for ProofRequestActor { 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)), - EnclaveEventData::ComputeRequestError(data) => self.notify_sync(ctx, TypedEvent::new(data, ec)), + 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)) + } + EnclaveEventData::ComputeRequestError(data) => { + self.notify_sync(ctx, TypedEvent::new(data, ec)) + } _ => (), } } @@ -287,7 +315,11 @@ impl Handler> for ProofRequestActor { impl Handler> for ProofRequestActor { type Result = (); - fn handle(&mut self, msg: TypedEvent, _ctx: &mut Self::Context) -> Self::Result { + fn handle( + &mut self, + msg: TypedEvent, + _ctx: &mut Self::Context, + ) -> Self::Result { self.handle_threshold_share_pending(msg); } } From a2cac591652ec37dd547568bf635ae55e3f5e3b8 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Mon, 16 Feb 2026 10:23:29 +0000 Subject: [PATCH 12/15] chore: longer duration to account for extra proof gen --- examples/CRISP/server/.env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From fd657e6f737f1ef1546cb0b8da3b760f36cd394e Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Mon, 16 Feb 2026 14:28:37 +0000 Subject: [PATCH 13/15] chore: little extra timeout on crisp tests --- examples/CRISP/test/crisp.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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...`) From 1bb023a0aa07eb931c9c96ad0eb62a3a297748d9 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Mon, 16 Feb 2026 14:46:38 +0000 Subject: [PATCH 14/15] chore: re-order sk_raw generation --- crates/trbfv/src/gen_pk_share_and_sk_sss.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 367d99be09..1635b33dca 100644 --- a/crates/trbfv/src/gen_pk_share_and_sk_sss.rs +++ b/crates/trbfv/src/gen_pk_share_and_sk_sss.rs @@ -147,13 +147,13 @@ pub fn gen_pk_share_and_sk_sss( let pk0_share_raw = ArcBytes::from_bytes(&pk0_share.to_bytes()); let a_raw = ArcBytes::from_bytes(&a.to_bytes()); - let sk_raw = ArcBytes::from_bytes(&sk_poly.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({ From 7c644900ba9ee4aff245bc57bfe3ec8438559b4d Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Mon, 16 Feb 2026 20:29:22 +0000 Subject: [PATCH 15/15] chore: use sensitive bytes --- .../src/enclave_event/compute_request/zk.rs | 32 ++++++++--------- crates/keyshare/src/threshold_keyshare.rs | 12 +++---- crates/multithread/src/multithread.rs | 28 +++++++++++---- crates/test-helpers/src/usecase_helpers.rs | 2 +- crates/trbfv/src/gen_pk_share_and_sk_sss.rs | 20 +++++------ crates/zk-prover/src/actors/proof_request.rs | 36 ++++++++++--------- 6 files changed, 73 insertions(+), 57 deletions(-) diff --git a/crates/events/src/enclave_event/compute_request/zk.rs b/crates/events/src/enclave_event/compute_request/zk.rs index 9ee96580e4..7af29ac4c8 100644 --- a/crates/events/src/enclave_event/compute_request/zk.rs +++ b/crates/events/src/enclave_event/compute_request/zk.rs @@ -6,6 +6,7 @@ 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; @@ -34,21 +35,18 @@ pub struct PkBfvProofRequest { #[derive(Derivative, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derivative(Debug)] pub struct PkGenerationProofRequest { - /// Raw pk0 share polynomial bytes. + /// Raw pk0 share polynomial bytes (public statement). #[derivative(Debug(format_with = "e3_utils::formatters::hexf"))] pub pk0_share: ArcBytes, - /// Raw common random polynomial bytes. + /// Raw common random polynomial bytes (public statement). #[derivative(Debug(format_with = "e3_utils::formatters::hexf"))] pub a: ArcBytes, - /// Raw secret key polynomial bytes (witness). - #[derivative(Debug(format_with = "e3_utils::formatters::hexf"))] - pub sk: ArcBytes, - /// Raw error polynomial bytes (witness). - #[derivative(Debug(format_with = "e3_utils::formatters::hexf"))] - pub eek: ArcBytes, - /// Raw smudging noise polynomial bytes (witness). - #[derivative(Debug(format_with = "e3_utils::formatters::hexf"))] - pub e_sm: 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 @@ -68,19 +66,19 @@ impl PkGenerationProofRequest { pub fn new( pk0_share: impl Into, a: impl Into, - sk: impl Into, - eek: impl Into, - e_sm: 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: sk.into(), - eek: eek.into(), + sk, + eek, params_preset, - e_sm: e_sm.into(), + e_sm, committee_size, } } diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index 5c7a79cc26..f893ca7db1 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -55,7 +55,7 @@ pub struct GenPkShareAndSkSss(CiphernodeSelected); #[rtype(result = "()")] pub struct GenEsiSss { pub ciphernode_selected: CiphernodeSelected, - pub e_sm_raw: ArcBytes, + pub e_sm_raw: SensitiveBytes, } #[derive(Message)] @@ -83,8 +83,8 @@ pub struct CollectingEncryptionKeysData { pub struct ProofRequestData { pub pk0_share_raw: ArcBytes, pub a_raw: ArcBytes, - pub sk_raw: ArcBytes, - pub eek_raw: ArcBytes, + pub sk_raw: SensitiveBytes, + pub eek_raw: SensitiveBytes, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -92,7 +92,7 @@ pub struct GeneratingThresholdShareData { pk_share: Option, sk_sss: Option>, esi_sss: Option>>, - e_sm_raw: Option, + e_sm_raw: Option, sk_bfv: SensitiveBytes, pk_bfv: ArcBytes, collected_encryption_keys: Vec>, @@ -666,7 +666,7 @@ impl ThresholdKeyshare { info!("GenEsiSss on ThresholdKeyshare"); let evt = msg.ciphernode_selected; - let e_sm_raw = msg.e_sm_raw; + 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 @@ -680,7 +680,7 @@ impl ThresholdKeyshare { TrBFVRequest::GenEsiSss( GenEsiSssRequest { trbfv_config, - e_sm_raw, + e_sm_raw: e_sm_raw_decrypted, } .into(), ), diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index 5c15b5bed9..a62106fbc3 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -239,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), } } @@ -341,6 +341,7 @@ fn handle_trbfv_request( } fn handle_zk_request( + cipher: Arc, zk_prover: Option>, zk_req: ZkRequest, request: ComputeRequest, @@ -363,7 +364,7 @@ fn handle_zk_request( handle_pk_bfv_proof(&prover, req, request.clone()) }), ZkRequest::PkGeneration(req) => timefunc("zk_pk_generation", id, || { - handle_pk_generation_proof(&prover, req, request.clone()) + handle_pk_generation_proof(&prover, &cipher, req, request.clone()) }), } } @@ -378,26 +379,41 @@ fn make_zk_error(request: &ComputeRequest, msg: String) -> ComputeRequestError { 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. Deserialize raw polynomial bytes → Poly + // 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(&req.sk, ¶ms) + 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(&req.eek, ¶ms) + 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(&req.e_sm, ¶ms) + 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 diff --git a/crates/test-helpers/src/usecase_helpers.rs b/crates/test-helpers/src/usecase_helpers.rs index cf0ea5224e..54b3039311 100644 --- a/crates/test-helpers/src/usecase_helpers.rs +++ b/crates/test-helpers/src/usecase_helpers.rs @@ -81,7 +81,7 @@ pub fn generate_shares_hash_map( &cipher, GenEsiSssRequest { trbfv_config: trbfv_config.clone(), - e_sm_raw, + e_sm_raw: ArcBytes::from_bytes(&e_sm_raw.access_raw(&cipher)?), }, )?; 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 1635b33dca..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,7 +11,7 @@ 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, @@ -66,12 +66,12 @@ pub struct GenPkShareAndSkSssResponse { 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). - pub sk_raw: ArcBytes, - /// Raw error polynomial from key generation (RNS form) for ZK proof generation (T1a). - pub eek_raw: ArcBytes, - /// Raw smudging noise polynomial (RNS form) for ZK proof generation (C1). - pub e_sm_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 { @@ -86,9 +86,9 @@ impl TryFrom<(InnerResponse, &Cipher)> for GenPkShareAndSkSssResponse { sk_sss, pk0_share_raw: value.pk0_share_raw, a_raw: value.a_raw, - sk_raw: value.sk_raw, - eek_raw: value.eek_raw, - e_sm_raw: value.e_sm_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)?, }) } } diff --git a/crates/zk-prover/src/actors/proof_request.rs b/crates/zk-prover/src/actors/proof_request.rs index 5460e36fc0..2cf1d8d35f 100644 --- a/crates/zk-prover/src/actors/proof_request.rs +++ b/crates/zk-prover/src/actors/proof_request.rs @@ -159,20 +159,37 @@ impl ProofRequestActor { }; 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", - pending.e3_id, num_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: pending.e3_id.clone(), + e3_id: e3_id.clone(), share: Arc::new(party_share), target_party_id: recipient_party_id as u64, external: false, @@ -188,21 +205,6 @@ impl ProofRequestActor { error!("Failed to extract share for party {}", recipient_party_id); } } - - 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}"); - } } fn handle_pk_bfv_response(