From 646f5221427660819df3d45102d4ee307351c316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Thu, 15 Feb 2024 20:19:46 +0000 Subject: [PATCH] fixed nym-api tests --- nym-api/src/coconut/dkg/dealing.rs | 34 +- nym-api/src/coconut/tests/fixtures.rs | 45 +- nym-api/src/coconut/tests/mod.rs | 619 ++++++++++++++------------ 3 files changed, 389 insertions(+), 309 deletions(-) diff --git a/nym-api/src/coconut/dkg/dealing.rs b/nym-api/src/coconut/dkg/dealing.rs index 0c5b8132a6..6c4074af72 100644 --- a/nym-api/src/coconut/dkg/dealing.rs +++ b/nym-api/src/coconut/dkg/dealing.rs @@ -286,7 +286,7 @@ impl DkgController { // it could be outdated and we can't use it for resharing let previous = epoch_id.saturating_sub(1); if old_keypair.issued_for_epoch != previous { - warn!("our existing coconut keypair has been generated for an distant epoch ({} vs expected {previous} for resharing)", old_keypair.issued_for_epoch); + warn!("our existing coconut keypair has been generated for a distant epoch ({} vs expected {previous} for resharing)", old_keypair.issued_for_epoch); // don't participate in resharing return Ok(()); } @@ -475,6 +475,7 @@ pub(crate) mod tests { }; use crate::coconut::tests::helpers::unchecked_decode_bte_key; use nym_coconut::{ttp_keygen, Parameters}; + use nym_coconut_dkg_common::types::DealerRegistrationDetails; use nym_dkg::bte::PublicKeyWithProof; #[tokio::test] @@ -615,7 +616,7 @@ pub(crate) mod tests { let dealers = dealers_fixtures(&mut rng, 4); let self_dealer = dealers[0].clone(); - let epoch = 0; + let epoch = 1; let mut keys = ttp_keygen(&Parameters::new(4).unwrap(), 3, 4).unwrap(); let coconut_keypair = KeyPair::new(); @@ -672,29 +673,42 @@ pub(crate) mod tests { let dealers = dealers_fixtures(&mut rng, 4); let self_dealer = dealers[0].clone(); - let epoch = 0; + let epoch = 1; let mut keys = ttp_keygen(&Parameters::new(4).unwrap(), 3, 4).unwrap(); let coconut_keypair = KeyPair::new(); coconut_keypair - .set(KeyPairWithEpoch::new(keys.pop().unwrap(), epoch)) + .set(KeyPairWithEpoch::new(keys.pop().unwrap(), epoch - 1)) .await; - let initial_dealers = InitialReplacementData { - initial_dealers: vec![self_dealer.address.clone()], - initial_height: 100, - }; - let mut controller = TestingDkgControllerBuilder::default() .with_threshold(3) .with_dealers(dealers.clone()) .with_as_dealer(self_dealer.clone()) .with_keypair(coconut_keypair) .with_initial_epoch_id(epoch) - .with_initial_dealers(initial_dealers) .build() .await; + let chain = controller.chain_state.clone(); + + // TODO: put that functionality in the builder + chain + .lock() + .unwrap() + .dkg_contract + .dealers + .entry(epoch - 1) + .or_default() + .insert( + self_dealer.address.to_string(), + DealerRegistrationDetails { + bte_public_key_with_proof: self_dealer.bte_public_key_with_proof.clone(), + ed25519_identity: self_dealer.ed25519_identity.clone(), + announce_address: self_dealer.announce_address.clone(), + }, + ); + let key_size = controller.dkg_client.get_contract_state().await?.key_size; let res = controller.dealing_exchange(epoch, true).await; diff --git a/nym-api/src/coconut/tests/fixtures.rs b/nym-api/src/coconut/tests/fixtures.rs index b7da76d805..36e416df19 100644 --- a/nym-api/src/coconut/tests/fixtures.rs +++ b/nym-api/src/coconut/tests/fixtures.rs @@ -10,7 +10,8 @@ use crate::coconut::keys::KeyPair; use crate::coconut::tests::{DummyClient, SharedFakeChain}; use cosmwasm_std::Addr; use nym_coconut::VerificationKey; -use nym_coconut_dkg_common::types::{DealerDetails, EpochId, InitialReplacementData}; +use nym_coconut_dkg_common::dealer::DealerRegistrationDetails; +use nym_coconut_dkg_common::types::{DealerDetails, EpochId}; use nym_crypto::asymmetric::identity; use nym_dkg::bte::keys::KeyPair as DkgKeyPair; use nym_dkg::{NodeIndex, Threshold}; @@ -81,7 +82,6 @@ pub struct TestingDkgControllerBuilder { threshold: Option, self_dealer: Option, dealers: Vec, - initial_dealers: Option, } impl TestingDkgControllerBuilder { @@ -127,11 +127,6 @@ impl TestingDkgControllerBuilder { self } - pub fn with_initial_dealers(mut self, initial_dealers: InitialReplacementData) -> Self { - self.initial_dealers = Some(initial_dealers); - self - } - #[allow(dead_code)] pub fn with_address(mut self, address: impl Into) -> Self { let addr = address.into(); @@ -181,20 +176,34 @@ impl TestingDkgControllerBuilder { // insert initial data into the chain state { let mut state_guard = chain_state.lock().unwrap(); - if let Some(threshold) = self.threshold { - state_guard.dkg_contract.threshold = Some(threshold) - } - for dealer in self.dealers { - state_guard - .dkg_contract - .dealers - .insert(dealer.assigned_index, dealer); - } if let Some(epoch_id) = self.epoch_id { state_guard.dkg_contract.epoch.epoch_id = epoch_id; } - if let Some(initial_dealers) = self.initial_dealers { - state_guard.dkg_contract.initial_dealers = Some(initial_dealers) + if let Some(threshold) = self.threshold { + state_guard.dkg_contract.threshold = Some(threshold) + } + let epoch_id = state_guard.dkg_contract.epoch.epoch_id; + + for dealer in self.dealers { + let epoch_dealers = state_guard + .dkg_contract + .dealers + .entry(epoch_id) + .or_default(); + + epoch_dealers.insert( + dealer.address.to_string(), + DealerRegistrationDetails { + bte_public_key_with_proof: dealer.bte_public_key_with_proof, + ed25519_identity: dealer.ed25519_identity, + announce_address: dealer.announce_address, + }, + ); + + state_guard + .dkg_contract + .dealer_indices + .insert(dealer.address.to_string(), dealer.assigned_index); } } diff --git a/nym-api/src/coconut/tests/mod.rs b/nym-api/src/coconut/tests/mod.rs index eb055f3cfa..c28bb78398 100644 --- a/nym-api/src/coconut/tests/mod.rs +++ b/nym-api/src/coconut/tests/mod.rs @@ -21,15 +21,17 @@ use nym_coconut_bandwidth_contract_common::events::{ DEPOSIT_VALUE, }; use nym_coconut_bandwidth_contract_common::spend_credential::SpendCredentialResponse; -use nym_coconut_dkg_common::dealer::{DealerDetails, DealerDetailsResponse, DealerType}; +use nym_coconut_dkg_common::dealer::{ + DealerDetails, DealerDetailsResponse, DealerType, RegisteredDealerDetails, +}; use nym_coconut_dkg_common::dealing::{ DealerDealingsStatusResponse, DealingChunkInfo, DealingMetadata, DealingStatus, DealingStatusResponse, PartialContractDealing, }; use nym_coconut_dkg_common::event_attributes::{DKG_PROPOSAL_ID, NODE_INDEX}; use nym_coconut_dkg_common::types::{ - ChunkIndex, DealingIndex, EncodedBTEPublicKeyWithProof, Epoch, EpochId, EpochState, - PartialContractDealingData, State as ContractState, + ChunkIndex, DealerRegistrationDetails, DealingIndex, EncodedBTEPublicKeyWithProof, Epoch, + EpochId, EpochState, PartialContractDealingData, State as ContractState, }; use nym_coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare}; use nym_coconut_interface::VerificationKey; @@ -38,7 +40,6 @@ use nym_contracts_common::IdentityKey; use nym_credentials::coconut::bandwidth::BandwidthVoucher; use nym_crypto::asymmetric::{encryption, identity}; use nym_dkg::{NodeIndex, Threshold}; -use nym_mixnet_contract_common::BlockHeight; use nym_validator_client::nym_api::routes::{ API_VERSION, BANDWIDTH, COCONUT_BLIND_SIGN, COCONUT_ROUTES, }; @@ -53,7 +54,6 @@ use rand_07::RngCore; use rocket::http::Status; use rocket::local::asynchronous::Client; use std::collections::{BTreeMap, HashMap}; -use std::mem; use std::ops::Deref; use std::str::FromStr; use std::sync::atomic::{AtomicU64, Ordering}; @@ -138,6 +138,10 @@ pub(crate) struct FakeDkgContractState { // pub(crate) dealers: HashMap, // pub(crate) past_dealers: HashMap, // pub(crate) initial_dealers: Option, + pub(crate) dealer_indices: HashMap, + + // map of epoch id -> dealer -> info + pub(crate) dealers: HashMap>, // map of epoch id -> dealer -> dealings pub(crate) dealings: HashMap>>, @@ -151,41 +155,84 @@ pub(crate) struct FakeDkgContractState { } impl FakeDkgContractState { - pub(crate) fn verified_dealers(&self) -> Vec { - let epoch_id = self.epoch.epoch_id; - let Some(shares) = self.verification_shares.get(&epoch_id) else { - return Vec::new(); - }; - - shares - .values() - .filter(|s| s.verified) - .map(|s| s.owner.clone()) - .collect() - } + // pub(crate) fn verified_dealers(&self) -> Vec { + // let epoch_id = self.epoch.epoch_id; + // let Some(shares) = self.verification_shares.get(&epoch_id) else { + // return Vec::new(); + // }; + // + // shares + // .values() + // .filter(|s| s.verified) + // .map(|s| s.owner.clone()) + // .collect() + // } fn reset_dkg_state(&mut self) { self.threshold = None; - let dealers = mem::take(&mut self.dealers); - for (index, details) in dealers { - self.past_dealers.insert(index, details); - } } - pub(crate) fn reset_epoch_in_reshare_mode(&mut self, block_height: BlockHeight) { - if let Some(initial_dealers) = self.initial_dealers.as_mut() { - initial_dealers.initial_height = block_height; - } else { - self.initial_dealers = Some(InitialReplacementData { - initial_dealers: self.verified_dealers(), - initial_height: block_height, - }) - } - + pub(crate) fn reset_epoch_in_reshare_mode(&mut self) { self.reset_dkg_state(); self.epoch.state = EpochState::PublicKeySubmission { resharing: true }; self.epoch.epoch_id += 1; } + + pub(crate) fn reset_dkg(&mut self) { + self.reset_dkg_state(); + self.epoch.state = EpochState::PublicKeySubmission { resharing: false }; + self.epoch.epoch_id += 1; + } + + pub(crate) fn get_registration_details( + &self, + addr: &str, + epoch_id: EpochId, + ) -> Option { + self.dealers.get(&epoch_id)?.get(addr).cloned() + } + + pub(crate) fn get_dealer_details( + &self, + addr: &str, + epoch_id: EpochId, + ) -> Option { + let registration_details = self.get_registration_details(addr, epoch_id)?; + let assigned_index = self.get_dealer_index(addr)?; + + Some(DealerDetails { + address: Addr::unchecked(addr), + bte_public_key_with_proof: registration_details.bte_public_key_with_proof, + ed25519_identity: registration_details.ed25519_identity, + announce_address: registration_details.announce_address, + assigned_index, + }) + } + + // implementation copied from our contract + pub(crate) fn query_dealer_details(&self, addr: &str) -> DealerDetailsResponse { + let current_epoch_id = self.epoch.epoch_id; + + // if the address has registration data for the current epoch, it means it's an active dealer + if let Some(dealer_details) = self.get_dealer_details(addr, current_epoch_id) { + let assigned_index = dealer_details.assigned_index; + return DealerDetailsResponse::new( + Some(dealer_details), + DealerType::Current { assigned_index }, + ); + } + + // and if has had an assigned index it must have been a dealer at some point in the past + if let Some(assigned_index) = self.get_dealer_index(addr) { + return DealerDetailsResponse::new(None, DealerType::Past { assigned_index }); + } + + DealerDetailsResponse::new(None, DealerType::Unknown) + } + + pub(crate) fn get_dealer_index(&self, addr: &str) -> Option { + self.dealer_indices.get(addr).copied() + } } #[derive(Debug)] @@ -274,8 +321,8 @@ impl Default for FakeChainState { dkg_contract: FakeDkgContractState { address: dkg_contract.as_ref().parse().unwrap(), + dealer_indices: Default::default(), dealers: HashMap::new(), - past_dealers: Default::default(), epoch: Epoch::default(), contract_state: ContractState { @@ -287,7 +334,6 @@ impl Default for FakeChainState { dealings: HashMap::new(), verification_shares: HashMap::new(), threshold: None, - initial_dealers: None, }, group_contract: FakeGroupContractState { address: group_contract, @@ -307,6 +353,18 @@ impl Default for FakeChainState { } impl FakeChainState { + pub(crate) fn get_or_assign_dealer(&mut self, addr: &str) -> NodeIndex { + if let Some(index) = self.dkg_contract.dealer_indices.get(addr) { + *index + } else { + let new = self._counters.next_node_index(); + self.dkg_contract + .dealer_indices + .insert(addr.to_string(), new); + new + } + } + pub(crate) fn total_group_weight(&self) -> u64 { self.group_contract.total_weight() } @@ -320,8 +378,12 @@ impl FakeChainState { } pub(crate) fn advance_epoch_in_reshare_mode(&mut self) { - self.dkg_contract - .reset_epoch_in_reshare_mode(self.block_info.height) + self.dkg_contract.reset_epoch_in_reshare_mode() + } + + #[allow(unused)] + pub(crate) fn advance_epoch_in_reset_mode(&mut self) { + self.dkg_contract.reset_dkg() } // TODO: make it return a result @@ -519,25 +581,25 @@ impl DummyClient { // // self // } - async fn get_dealer_by_address(&self, address: &str) -> Option { - let guard = self.state.lock().unwrap(); - for dealer in guard.dkg_contract.dealers.values() { - if dealer.address.as_str() == address { - return Some(dealer.clone()); - } - } - None - } - - async fn get_past_dealer_by_address(&self, address: &str) -> Option { - let guard = self.state.lock().unwrap(); - for dealer in guard.dkg_contract.past_dealers.values() { - if dealer.address.as_str() == address { - return Some(dealer.clone()); - } - } - None - } + // async fn get_dealer_by_address(&self, address: &str) -> Option { + // let guard = self.state.lock().unwrap(); + // for dealer in guard.dkg_contract.dealers.values() { + // if dealer.address.as_str() == address { + // return Some(dealer.clone()); + // } + // } + // None + // } + // + // async fn get_past_dealer_by_address(&self, address: &str) -> Option { + // let guard = self.state.lock().unwrap(); + // for dealer in guard.dkg_contract.past_dealers.values() { + // if dealer.address.as_str() == address { + // return Some(dealer.clone()); + // } + // } + // None + // } } #[async_trait] @@ -653,24 +715,74 @@ impl super::client::Client for DummyClient { async fn get_self_registered_dealer_details(&self) -> Result { let address = self.validator_address.as_ref(); + Ok(self + .state + .lock() + .unwrap() + .dkg_contract + .query_dealer_details(address)) + } - if let Some(details) = self.get_dealer_by_address(address).await { - return Ok(DealerDetailsResponse { - details: Some(details), - dealer_type: DealerType::Current, + async fn get_registered_dealer_details( + &self, + epoch_id: EpochId, + dealer: String, + ) -> Result { + let details = self + .state + .lock() + .unwrap() + .dkg_contract + .dealers + .get(&epoch_id) + .and_then(|dealers| dealers.get(&dealer)) + .cloned(); + Ok(RegisteredDealerDetails { details }) + } + + async fn get_dealer_dealings_status( + &self, + epoch_id: EpochId, + dealer: String, + ) -> Result { + let guard = self.state.lock().unwrap(); + let key_size = guard.dkg_contract.contract_state.key_size; + + let dealer_addr = Addr::unchecked(&dealer); + + let Some(epoch_dealings) = guard.dkg_contract.dealings.get(&epoch_id) else { + return Ok(DealerDealingsStatusResponse { + epoch_id, + dealer: dealer_addr, + all_dealings_fully_submitted: false, + dealing_submission_status: Default::default(), }); + }; + + let Some(dealer_dealings) = epoch_dealings.get(&dealer) else { + return Ok(DealerDealingsStatusResponse { + epoch_id, + dealer: dealer_addr, + all_dealings_fully_submitted: false, + dealing_submission_status: Default::default(), + }); + }; + + let mut dealing_submission_status: BTreeMap = BTreeMap::new(); + for dealing_index in 0..key_size { + let metadata = dealer_dealings + .get(&dealing_index) + .map(|d| d.metadata.clone()); + dealing_submission_status.insert(dealing_index, metadata.into()); } - if let Some(details) = self.get_past_dealer_by_address(address).await { - return Ok(DealerDetailsResponse { - details: Some(details), - dealer_type: DealerType::Past, - }); - } - - Ok(DealerDetailsResponse { - details: None, - dealer_type: DealerType::Unknown, + Ok(DealerDealingsStatusResponse { + epoch_id, + dealer: Addr::unchecked(&dealer), + all_dealings_fully_submitted: dealing_submission_status + .values() + .all(|d| d.fully_submitted), + dealing_submission_status, }) } @@ -699,17 +811,75 @@ impl super::client::Client for DummyClient { } async fn get_current_dealers(&self) -> Result> { - Ok(self - .state - .lock() - .unwrap() - .dkg_contract - .dealers - .values() - .cloned() + let chain = self.state.lock().unwrap(); + let current_epoch_id = chain.dkg_contract.epoch.epoch_id; + + let Some(epoch_dealers) = chain.dkg_contract.dealers.get(¤t_epoch_id) else { + return Ok(Vec::new()); + }; + + Ok(epoch_dealers + .iter() + .map(|(address, details)| { + let assigned_index = chain.dkg_contract.get_dealer_index(address).unwrap(); + DealerDetails { + address: Addr::unchecked(address), + bte_public_key_with_proof: details.bte_public_key_with_proof.clone(), + ed25519_identity: details.ed25519_identity.clone(), + announce_address: details.announce_address.clone(), + assigned_index, + } + }) .collect()) } + async fn get_dealing_metadata( + &self, + epoch_id: EpochId, + dealer: String, + dealing_index: DealingIndex, + ) -> Result> { + let guard = self.state.lock().unwrap(); + + let Some(epoch_dealings) = guard.dkg_contract.dealings.get(&epoch_id) else { + return Ok(None); + }; + + let Some(dealer_dealings) = epoch_dealings.get(&dealer) else { + return Ok(None); + }; + + let Some(dealing) = dealer_dealings.get(&dealing_index) else { + return Ok(None); + }; + + Ok(Some(dealing.metadata.clone())) + } + + async fn get_dealing_chunk( + &self, + epoch_id: EpochId, + dealer: &str, + dealing_index: DealingIndex, + chunk_index: ChunkIndex, + ) -> Result> { + let guard = self.state.lock().unwrap(); + + let Some(epoch_dealings) = guard.dkg_contract.dealings.get(&epoch_id) else { + return Ok(None); + }; + + let Some(dealer_dealings) = epoch_dealings.get(dealer) else { + return Ok(None); + }; + + let Some(dealing) = dealer_dealings.get(&dealing_index) else { + return Ok(None); + }; + + Ok(dealing.chunks.get(&chunk_index).cloned()) + } + async fn get_verification_key_share( &self, epoch_id: EpochId, @@ -733,7 +903,6 @@ impl super::client::Client for DummyClient { Some(epoch_shares) => Ok(epoch_shares.values().cloned().collect()), } } - async fn vote_proposal( &self, proposal_id: u64, @@ -819,43 +988,23 @@ impl super::client::Client for DummyClient { announce_address: String, _resharing: bool, ) -> Result { - let assigned_index = if let Some(already_registered) = self - .get_dealer_by_address(self.validator_address.as_ref()) - .await - { - // current dealer - already_registered.assigned_index - } else if let Some(registered_in_the_past) = self - .get_past_dealer_by_address(self.validator_address.as_ref()) - .await - { - // past dealer - let index = registered_in_the_past.assigned_index; - let mut guard = self.state.lock().unwrap(); - guard - .dkg_contract - .dealers - .insert(index, registered_in_the_past); - - index - } else { - // new dealer - let mut guard = self.state.lock().unwrap(); - let assigned_index = guard._counters.next_node_index(); - - guard.dkg_contract.dealers.insert( - assigned_index, - DealerDetails { - address: Addr::unchecked(self.validator_address.to_string()), - bte_public_key_with_proof, - ed25519_identity: identity_key, - announce_address, - assigned_index, - }, - ); - assigned_index - }; let mut guard = self.state.lock().unwrap(); + let assigned_index = guard.get_or_assign_dealer(self.validator_address.as_ref()); + let epoch = guard.dkg_contract.epoch.epoch_id; + + let dealer_details = DealerRegistrationDetails { + bte_public_key_with_proof, + ed25519_identity: identity_key, + announce_address, + }; + + let epoch_dealers = guard.dkg_contract.dealers.entry(epoch).or_default(); + if !epoch_dealers.contains_key(self.validator_address.as_ref()) { + epoch_dealers.insert(self.validator_address.to_string(), dealer_details); + } else { + unimplemented!("already registered") + } + let transaction_hash = guard._counters.next_tx_hash(); Ok(ExecuteResult { @@ -869,174 +1018,6 @@ impl super::client::Client for DummyClient { gas_info: Default::default(), }) } - async fn submit_verification_key_share( - &self, - share: VerificationKeyShare, - resharing: bool, - ) -> Result { - let address = self.validator_address.to_string(); - - let Some(dealer_details) = self.get_dealer_by_address(&address).await else { - // Just throw some error, not really the correct one - return Err(CoconutError::DepositEncrKeyNotFound); - }; - - let mut chain = self.state.lock().unwrap(); - let dkg_contract = chain.dkg_contract.address.clone(); - let epoch_id = chain.dkg_contract.epoch.epoch_id; - - chain - .dkg_contract - .verification_shares - .entry(epoch_id) - .or_default() - .insert( - self.validator_address.to_string(), - ContractVKShare { - share, - announce_address: dealer_details.announce_address.clone(), - node_index: dealer_details.assigned_index, - owner: Addr::unchecked(&address), - epoch_id, - verified: false, - }, - ); - - let proposal_id = chain._counters.next_proposal_id(); - let verify_vk_share_req = - nym_coconut_dkg_common::msg::ExecuteMsg::VerifyVerificationKeyShare { - owner: address, - resharing, - }; - let verify_vk_share_msg = CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: chain.dkg_contract.address.to_string(), - msg: to_binary(&verify_vk_share_req).unwrap(), - funds: vec![], - }); - let proposal = Proposal { - title: String::new(), - description: String::new(), - msgs: vec![verify_vk_share_msg], - status: cw3::Status::Open, - expires: cw_utils::Expiration::Never {}, - threshold: cw_utils::Threshold::AbsolutePercentage { - percentage: Decimal::from_ratio(2u32, 3u32), - }, - total_weight: chain.total_group_weight(), - votes: Votes::yes(0), - proposer: Addr::unchecked(dkg_contract.as_ref()), - deposit: None, - start_height: 0, - }; - chain - .multisig_contract - .proposals - .insert(proposal_id, proposal); - let transaction_hash = chain._counters.next_tx_hash(); - Ok(ExecuteResult { - logs: vec![Log { - msg_index: 0, - events: vec![cosmwasm_std::Event::new("wasm") - .add_attribute(DKG_PROPOSAL_ID, proposal_id.to_string())], - }], - data: Default::default(), - transaction_hash, - gas_info: Default::default(), - }) - } - - async fn get_dealer_dealings_status( - &self, - epoch_id: EpochId, - dealer: String, - ) -> Result { - let guard = self.state.lock().unwrap(); - let key_size = guard.dkg_contract.contract_state.key_size; - - let dealer_addr = Addr::unchecked(&dealer); - - let Some(epoch_dealings) = guard.dkg_contract.dealings.get(&epoch_id) else { - return Ok(DealerDealingsStatusResponse { - epoch_id, - dealer: dealer_addr, - all_dealings_fully_submitted: false, - dealing_submission_status: Default::default(), - }); - }; - - let Some(dealer_dealings) = epoch_dealings.get(&dealer) else { - return Ok(DealerDealingsStatusResponse { - epoch_id, - dealer: dealer_addr, - all_dealings_fully_submitted: false, - dealing_submission_status: Default::default(), - }); - }; - - let mut dealing_submission_status: BTreeMap = BTreeMap::new(); - for dealing_index in 0..key_size { - let metadata = dealer_dealings - .get(&dealing_index) - .map(|d| d.metadata.clone()); - dealing_submission_status.insert(dealing_index, metadata.into()); - } - - Ok(DealerDealingsStatusResponse { - epoch_id, - dealer: Addr::unchecked(&dealer), - all_dealings_fully_submitted: dealing_submission_status - .values() - .all(|d| d.fully_submitted), - dealing_submission_status, - }) - } - - async fn get_dealing_metadata( - &self, - epoch_id: EpochId, - dealer: String, - dealing_index: DealingIndex, - ) -> Result> { - let guard = self.state.lock().unwrap(); - - let Some(epoch_dealings) = guard.dkg_contract.dealings.get(&epoch_id) else { - return Ok(None); - }; - - let Some(dealer_dealings) = epoch_dealings.get(&dealer) else { - return Ok(None); - }; - - let Some(dealing) = dealer_dealings.get(&dealing_index) else { - return Ok(None); - }; - - Ok(Some(dealing.metadata.clone())) - } - - async fn get_dealing_chunk( - &self, - epoch_id: EpochId, - dealer: &str, - dealing_index: DealingIndex, - chunk_index: ChunkIndex, - ) -> Result> { - let guard = self.state.lock().unwrap(); - - let Some(epoch_dealings) = guard.dkg_contract.dealings.get(&epoch_id) else { - return Ok(None); - }; - - let Some(dealer_dealings) = epoch_dealings.get(dealer) else { - return Ok(None); - }; - - let Some(dealing) = dealer_dealings.get(&dealing_index) else { - return Ok(None); - }; - - Ok(dealing.chunks.get(&chunk_index).cloned()) - } async fn submit_dealing_metadata( &self, @@ -1104,6 +1085,82 @@ impl super::client::Client for DummyClient { gas_info: Default::default(), }) } + + async fn submit_verification_key_share( + &self, + share: VerificationKeyShare, + resharing: bool, + ) -> Result { + let mut chain = self.state.lock().unwrap(); + + let address = self.validator_address.to_string(); + let epoch_id = chain.dkg_contract.epoch.epoch_id; + let Some(dealer_details) = chain.dkg_contract.get_dealer_details(&address, epoch_id) else { + // Just throw some error, not really the correct one + return Err(CoconutError::DepositEncrKeyNotFound); + }; + + let dkg_contract = chain.dkg_contract.address.clone(); + + chain + .dkg_contract + .verification_shares + .entry(epoch_id) + .or_default() + .insert( + self.validator_address.to_string(), + ContractVKShare { + share, + announce_address: dealer_details.announce_address.clone(), + node_index: dealer_details.assigned_index, + owner: Addr::unchecked(&address), + epoch_id, + verified: false, + }, + ); + + let proposal_id = chain._counters.next_proposal_id(); + let verify_vk_share_req = + nym_coconut_dkg_common::msg::ExecuteMsg::VerifyVerificationKeyShare { + owner: address, + resharing, + }; + let verify_vk_share_msg = CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: chain.dkg_contract.address.to_string(), + msg: to_binary(&verify_vk_share_req).unwrap(), + funds: vec![], + }); + let proposal = Proposal { + title: String::new(), + description: String::new(), + msgs: vec![verify_vk_share_msg], + status: cw3::Status::Open, + expires: cw_utils::Expiration::Never {}, + threshold: cw_utils::Threshold::AbsolutePercentage { + percentage: Decimal::from_ratio(2u32, 3u32), + }, + total_weight: chain.total_group_weight(), + votes: Votes::yes(0), + proposer: Addr::unchecked(dkg_contract.as_ref()), + deposit: None, + start_height: 0, + }; + chain + .multisig_contract + .proposals + .insert(proposal_id, proposal); + let transaction_hash = chain._counters.next_tx_hash(); + Ok(ExecuteResult { + logs: vec![Log { + msg_index: 0, + events: vec![cosmwasm_std::Event::new("wasm") + .add_attribute(DKG_PROPOSAL_ID, proposal_id.to_string())], + }], + data: Default::default(), + transaction_hash, + gas_info: Default::default(), + }) + } } #[derive(Clone, Debug)]