From a44339433e6b6a12291f9f2e0bfaedd956abc7f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Fri, 2 Feb 2024 13:56:21 +0000 Subject: [PATCH] fixed nym-api tests --- nym-api/src/coconut/dkg/dealing.rs | 14 +- nym-api/src/coconut/dkg/key_derivation.rs | 31 +- .../src/coconut/dkg/state/key_derivation.rs | 2 +- nym-api/src/coconut/tests/mod.rs | 281 ++++++++++++++---- 4 files changed, 248 insertions(+), 80 deletions(-) diff --git a/nym-api/src/coconut/dkg/dealing.rs b/nym-api/src/coconut/dkg/dealing.rs index ab2b850f99..77a6f3310e 100644 --- a/nym-api/src/coconut/dkg/dealing.rs +++ b/nym-api/src/coconut/dkg/dealing.rs @@ -206,7 +206,7 @@ impl DkgController { // because we have already committed to dealings of particular size // yet we don't have relevant chunks after chunking let chunk = chunks - .remove(&chunk_index) + .remove(chunk_index) .expect("chunking specification has changed mid-exchange!"); debug!("[dealing {dealing_index}]: resubmitting chunk index {chunk_index}"); self.dkg_client @@ -547,14 +547,10 @@ pub(crate) mod tests { .get(self_dealer.address.as_str()) .unwrap(); - for submitted_dealing in submitted_dealings { - let dealing = Dealing::try_from_bytes(submitted_dealing.data.as_slice())?; - assert_eq!( - generated_dealings - .get(&submitted_dealing.dealing_index) - .unwrap(), - &dealing - ) + for (dealing_index, submitted_info) in submitted_dealings { + let dealing = Dealing::try_from_bytes(&submitted_info.unchecked_rebuild())?; + + assert_eq!(generated_dealings.get(dealing_index).unwrap(), &dealing) } Ok(()) diff --git a/nym-api/src/coconut/dkg/key_derivation.rs b/nym-api/src/coconut/dkg/key_derivation.rs index 16dbc5ae81..64291f24c9 100644 --- a/nym-api/src/coconut/dkg/key_derivation.rs +++ b/nym-api/src/coconut/dkg/key_derivation.rs @@ -730,9 +730,10 @@ pub(crate) mod tests { .entry(epoch) .and_modify(|epoch_dealings| { let validator_dealings = epoch_dealings.get_mut(&address.to_string()).unwrap(); - let mut last = validator_dealings.pop().unwrap(); - last.data.0.pop(); - validator_dealings.push(last); + let mut first = validator_dealings.remove(&0).unwrap(); + let first_chunk = first.chunks.get_mut(&0).unwrap(); + first_chunk.0.pop().unwrap(); + validator_dealings.insert(0, first); }); for controller in controllers.iter_mut() { @@ -822,8 +823,10 @@ pub(crate) mod tests { .entry(epoch) .and_modify(|epoch_dealings| { let validator_dealings = epoch_dealings.get_mut(&address.to_string()).unwrap(); - validator_dealings.iter_mut().for_each(|dealing| { - dealing.data.0.pop(); + validator_dealings.values_mut().for_each(|dealing| { + dealing.chunks.values_mut().for_each(|chunk| { + chunk.0.pop(); + }) }); }); @@ -879,14 +882,15 @@ pub(crate) mod tests { .entry(epoch) .and_modify(|epoch_dealings| { let validator_dealings = epoch_dealings.get_mut(&address.to_string()).unwrap(); - let mut last = validator_dealings.pop().unwrap(); - let value = last.data.0.pop().unwrap(); + let chunks = &mut validator_dealings.get_mut(&0).unwrap().chunks; + let mut last_entry = chunks.last_entry().unwrap(); + let last = last_entry.get_mut(); + let value = last.0.pop().unwrap(); if value == 42 { - last.data.0.push(43); + last.0.push(43); } else { - last.data.0.push(42); + last.0.push(42); } - validator_dealings.push(last); }); for controller in controllers.iter_mut() { @@ -964,9 +968,10 @@ pub(crate) mod tests { .entry(epoch) .and_modify(|epoch_dealings| { let validator_dealings = epoch_dealings.get_mut(&address.to_string()).unwrap(); - let mut last = validator_dealings.pop().unwrap(); - last.data.0.pop(); - validator_dealings.push(last); + let mut first = validator_dealings.remove(&0).unwrap(); + let first_chunk = first.chunks.get_mut(&0).unwrap(); + first_chunk.0.pop().unwrap(); + validator_dealings.insert(0, first); }); for controller in controllers.iter_mut().skip(1) { diff --git a/nym-api/src/coconut/dkg/state/key_derivation.rs b/nym-api/src/coconut/dkg/state/key_derivation.rs index f0442e829a..43eb72f61c 100644 --- a/nym-api/src/coconut/dkg/state/key_derivation.rs +++ b/nym-api/src/coconut/dkg/state/key_derivation.rs @@ -61,7 +61,7 @@ pub enum DealerRejectionReason { err_msg: String, }, - #[error("the dealing at index {index} is [cryptographically] valid: {err_msg}")] + #[error("the dealing at index {index} is [cryptographically] invalid: {err_msg}")] InvalidDealing { index: DealingIndex, err_msg: String, diff --git a/nym-api/src/coconut/tests/mod.rs b/nym-api/src/coconut/tests/mod.rs index 772552af65..5690c7c46e 100644 --- a/nym-api/src/coconut/tests/mod.rs +++ b/nym-api/src/coconut/tests/mod.rs @@ -21,13 +21,15 @@ 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, DealingStatusResponse, +use nym_coconut_dkg_common::dealer::{DealerDetails, DealerDetailsResponse, DealerType}; +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::{ - DealingIndex, EncodedBTEPublicKeyWithProof, Epoch, EpochId, EpochState, InitialReplacementData, - PartialContractDealing, State as ContractState, + ChunkIndex, DealingIndex, EncodedBTEPublicKeyWithProof, Epoch, EpochId, EpochState, + InitialReplacementData, PartialContractDealingData, State as ContractState, }; use nym_coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare}; use nym_coconut_interface::VerificationKey; @@ -50,7 +52,7 @@ use rand_07::rngs::OsRng; use rand_07::RngCore; use rocket::http::Status; use rocket::local::asynchronous::Client; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::mem; use std::ops::Deref; use std::str::FromStr; @@ -92,6 +94,43 @@ impl InternalCounters { } } +#[derive(Debug)] +pub(crate) struct Dealing { + // fake entry is created whenever the metadata is submitted + pub(crate) metadata: DealingMetadata, + pub(crate) chunks: BTreeMap, +} + +impl Dealing { + pub(crate) fn new_metadata_submission( + dealing_index: DealingIndex, + chunks: Vec, + ) -> Self { + Dealing { + metadata: DealingMetadata::new(dealing_index, chunks), + chunks: Default::default(), + } + } + + pub(crate) fn unchecked_rebuild(&self) -> Vec { + let mut data = Vec::new(); + + for (chunk_index, partial) in self.chunks.iter() { + assert!(self + .metadata + .submitted_chunks + .get(chunk_index) + .unwrap() + .status + .submitted()); + + data.append(&mut partial.clone()) + } + + data + } +} + #[derive(Debug)] pub(crate) struct FakeDkgContractState { pub(crate) address: AccountId, @@ -100,7 +139,10 @@ pub(crate) struct FakeDkgContractState { pub(crate) past_dealers: HashMap, pub(crate) initial_dealers: Option, - pub(crate) dealings: HashMap>>, + // map of epoch id -> dealer -> dealings + pub(crate) dealings: HashMap>>, + + // map of epoch id -> dealer -> vk share pub(crate) verification_shares: HashMap>, pub(crate) epoch: Epoch, @@ -648,12 +690,21 @@ impl super::client::Client for DummyClient { dealer: String, dealing_index: DealingIndex, ) -> Result { - let dealings = self.get_dealings(epoch_id, &dealer).await?; + let guard = self.state.lock().unwrap(); + + let metadata = guard + .dkg_contract + .dealings + .get(&epoch_id) + .and_then(|epoch_dealings| epoch_dealings.get(&dealer)) + .and_then(|dealer_dealings| dealer_dealings.get(&dealing_index)) + .map(|info| info.metadata.clone()); + Ok(DealingStatusResponse { epoch_id, dealer: Addr::unchecked(dealer), dealing_index, - full_dealing_submitted: dealings.get(dealing_index as usize).is_some(), + status: metadata.into(), }) } @@ -669,25 +720,6 @@ impl super::client::Client for DummyClient { .collect()) } - async fn get_dealings( - &self, - epoch_id: EpochId, - dealer: &str, - ) -> Result> { - Ok(self - .state - .lock() - .unwrap() - .dkg_contract - .dealings - .get(&epoch_id) - .cloned() - .unwrap_or_default() - .get(dealer) - .cloned() - .unwrap_or_default()) - } - async fn get_verification_key_share( &self, epoch_id: EpochId, @@ -847,35 +879,6 @@ impl super::client::Client for DummyClient { gas_info: Default::default(), }) } - - async fn submit_dealing( - &self, - dealing: PartialContractDealing, - _resharing: bool, - ) -> Result { - let mut guard = self.state.lock().unwrap(); - let current_epoch = guard.dkg_contract.epoch.epoch_id; - - let epoch_dealings = guard - .dkg_contract - .dealings - .entry(current_epoch) - .or_default(); - let existing_dealings = epoch_dealings - .entry(self.validator_address.to_string()) - .or_default(); - existing_dealings.push(dealing); - - let transaction_hash = guard._counters.next_tx_hash(); - - Ok(ExecuteResult { - logs: vec![], - data: Default::default(), - transaction_hash, - gas_info: Default::default(), - }) - } - async fn submit_verification_key_share( &self, share: VerificationKeyShare, @@ -912,7 +915,7 @@ impl super::client::Client for DummyClient { let proposal_id = chain._counters.next_proposal_id(); let verify_vk_share_req = nym_coconut_dkg_common::msg::ExecuteMsg::VerifyVerificationKeyShare { - owner: Addr::unchecked(&address), + owner: address, resharing, }; let verify_vk_share_msg = CosmosMsg::Wasm(WasmMsg::Execute { @@ -951,6 +954,170 @@ impl super::client::Client for DummyClient { 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, + dealing_index: DealingIndex, + chunks: Vec, + _resharing: bool, + ) -> Result { + let mut guard = self.state.lock().unwrap(); + let current_epoch = guard.dkg_contract.epoch.epoch_id; + + let epoch_dealings = guard + .dkg_contract + .dealings + .entry(current_epoch) + .or_default(); + + let dealer_dealings = epoch_dealings + .entry(self.validator_address.to_string()) + .or_default(); + dealer_dealings.insert( + dealing_index, + Dealing::new_metadata_submission(dealing_index, chunks), + ); + + let transaction_hash = guard._counters.next_tx_hash(); + + Ok(ExecuteResult { + logs: vec![], + data: Default::default(), + transaction_hash, + gas_info: Default::default(), + }) + } + + async fn submit_dealing_chunk( + &self, + chunk: PartialContractDealing, + _resharing: bool, + ) -> Result { + let mut guard = self.state.lock().unwrap(); + let current_epoch = guard.dkg_contract.epoch.epoch_id; + let current_height = guard.block_info.height; + + // normally we should do checks for existence, etc. + // but since this is a testing code, we assume everything is sent in order and the appropriate entries exist + let epoch_dealings = guard.dkg_contract.dealings.get_mut(¤t_epoch).unwrap(); + + let dealer_dealings = epoch_dealings + .get_mut(self.validator_address.as_ref()) + .unwrap(); + + let dealing_chunks = dealer_dealings.get_mut(&chunk.dealing_index).unwrap(); + dealing_chunks.chunks.insert(chunk.chunk_index, chunk.data); + + dealing_chunks + .metadata + .submitted_chunks + .get_mut(&chunk.chunk_index) + .unwrap() + .status + .submission_height = Some(current_height); + + let transaction_hash = guard._counters.next_tx_hash(); + + Ok(ExecuteResult { + logs: vec![], + data: Default::default(), + transaction_hash, + gas_info: Default::default(), + }) + } } #[derive(Clone, Debug)]