handle chunking on nym-api side
This commit is contained in:
@@ -13,8 +13,8 @@ use serde::Deserialize;
|
||||
pub use nym_coconut_dkg_common::{
|
||||
dealer::{DealerDetailsResponse, PagedDealerResponse},
|
||||
dealing::{
|
||||
DealingChunkResponse, DealingChunkStatusResponse, DealingMetadataResponse,
|
||||
DealingStatusResponse,
|
||||
DealerDealingsStatusResponse, DealingChunkResponse, DealingChunkStatusResponse,
|
||||
DealingMetadataResponse, DealingStatusResponse,
|
||||
},
|
||||
msg::QueryMsg as DkgQueryMsg,
|
||||
types::{
|
||||
@@ -93,6 +93,16 @@ pub trait DkgQueryClient {
|
||||
self.query_dkg_contract(request).await
|
||||
}
|
||||
|
||||
async fn get_dealer_dealings_status(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
) -> Result<DealerDealingsStatusResponse, NyxdError> {
|
||||
let request = DkgQueryMsg::GetDealerDealingsStatus { epoch_id, dealer };
|
||||
|
||||
self.query_dkg_contract(request).await
|
||||
}
|
||||
|
||||
async fn get_dealing_status(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
@@ -114,7 +124,7 @@ pub trait DkgQueryClient {
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
) -> Result<DealingStatusResponse, NyxdError> {
|
||||
) -> Result<DealingChunkStatusResponse, NyxdError> {
|
||||
let request = DkgQueryMsg::GetDealingChunkStatus {
|
||||
epoch_id,
|
||||
dealer,
|
||||
@@ -217,6 +227,7 @@ where
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::nyxd::contract_traits::tests::IgnoreValue;
|
||||
use nym_coconut_dkg_common::msg::QueryMsg;
|
||||
|
||||
// it's enough that this compiles and clippy is happy about it
|
||||
#[allow(dead_code)]
|
||||
@@ -254,6 +265,9 @@ mod tests {
|
||||
} => client
|
||||
.get_dealings_metadata(epoch_id, dealer, dealing_index)
|
||||
.ignore(),
|
||||
QueryMsg::GetDealerDealingsStatus { epoch_id, dealer } => {
|
||||
client.get_dealer_dealings_status(epoch_id, dealer).ignore()
|
||||
}
|
||||
DkgQueryMsg::GetDealingChunkStatus {
|
||||
epoch_id,
|
||||
dealer,
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::types::{ChunkIndex, DealingIndex, EpochId, PartialContractDealingData};
|
||||
use contracts_common::dealings::ContractSafeBytes;
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::Addr;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
/// Defines the maximum size of a dealing chunk. Currently set to 2kB
|
||||
pub const MAX_DEALING_CHUNK_SIZE: usize = 2048;
|
||||
@@ -18,7 +19,26 @@ pub const MAX_DEALING_CHUNKS: usize = MAX_DEALING_SIZE / MAX_DEALING_CHUNK_SIZE;
|
||||
// 2 public attributes, 2 private attributes, 1 fixed for coconut credential
|
||||
pub const DEFAULT_DEALINGS: usize = 2 + 2 + 1;
|
||||
|
||||
pub fn chunk_dealing(
|
||||
dealing_index: DealingIndex,
|
||||
dealing_bytes: Vec<u8>,
|
||||
chunk_size: usize,
|
||||
) -> HashMap<ChunkIndex, PartialContractDealing> {
|
||||
let mut chunks = HashMap::new();
|
||||
for (chunk_index, chunk) in dealing_bytes.chunks(chunk_size).enumerate() {
|
||||
let chunk = PartialContractDealing {
|
||||
dealing_index,
|
||||
chunk_index: chunk_index as ChunkIndex,
|
||||
data: ContractSafeBytes(chunk.to_vec()),
|
||||
};
|
||||
chunks.insert(chunk_index as ChunkIndex, chunk);
|
||||
}
|
||||
|
||||
chunks
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
pub struct DealingChunkInfo {
|
||||
pub size: usize,
|
||||
}
|
||||
@@ -45,13 +65,26 @@ impl DealingChunkInfo {
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
pub struct SubmittedChunk {
|
||||
pub info: DealingChunkInfo,
|
||||
|
||||
pub status: ChunkSubmissionStatus,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
#[derive(Default, Copy)]
|
||||
pub struct ChunkSubmissionStatus {
|
||||
// this field is updated by the contract itself to indicate when this particular chunk has been received
|
||||
pub submission_height: Option<u64>,
|
||||
}
|
||||
|
||||
impl ChunkSubmissionStatus {
|
||||
pub fn submitted(&self) -> bool {
|
||||
self.submission_height.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DealingChunkInfo> for SubmittedChunk {
|
||||
fn from(value: DealingChunkInfo) -> Self {
|
||||
SubmittedChunk::new(value)
|
||||
@@ -62,9 +95,13 @@ impl SubmittedChunk {
|
||||
pub fn new(info: DealingChunkInfo) -> Self {
|
||||
SubmittedChunk {
|
||||
info,
|
||||
submission_height: None,
|
||||
status: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn submitted(&self) -> bool {
|
||||
self.status.submitted()
|
||||
}
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
@@ -87,14 +124,19 @@ impl DealingMetadata {
|
||||
}
|
||||
|
||||
pub fn is_complete(&self) -> bool {
|
||||
self.submitted_chunks
|
||||
.values()
|
||||
.all(|c| c.submission_height.is_some())
|
||||
self.submitted_chunks.values().all(|c| c.submitted())
|
||||
}
|
||||
|
||||
pub fn total_size(&self) -> usize {
|
||||
self.submitted_chunks.values().map(|c| c.info.size).sum()
|
||||
}
|
||||
|
||||
pub fn submission_statuses(&self) -> BTreeMap<ChunkIndex, ChunkSubmissionStatus> {
|
||||
self.submitted_chunks
|
||||
.iter()
|
||||
.map(|(id, c)| (*id, c.status))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
@@ -152,7 +194,7 @@ pub struct DealingChunkStatusResponse {
|
||||
|
||||
pub chunk_index: ChunkIndex,
|
||||
|
||||
pub submission_height: Option<u64>,
|
||||
pub status: ChunkSubmissionStatus,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
@@ -163,7 +205,51 @@ pub struct DealingStatusResponse {
|
||||
|
||||
pub dealing_index: DealingIndex,
|
||||
|
||||
pub full_dealing_submitted: bool,
|
||||
pub status: DealingStatus,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct DealingStatus {
|
||||
pub has_metadata: bool,
|
||||
|
||||
pub fully_submitted: bool,
|
||||
|
||||
pub chunk_submission_status: BTreeMap<ChunkIndex, ChunkSubmissionStatus>,
|
||||
}
|
||||
|
||||
impl From<Option<DealingMetadata>> for DealingStatus {
|
||||
fn from(metadata: Option<DealingMetadata>) -> Self {
|
||||
DealingStatus {
|
||||
has_metadata: metadata.is_some(),
|
||||
fully_submitted: metadata
|
||||
.as_ref()
|
||||
.map(|m| m.is_complete())
|
||||
.unwrap_or_default(),
|
||||
chunk_submission_status: metadata
|
||||
.map(|m| m.submission_statuses())
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct DealerDealingsStatusResponse {
|
||||
pub epoch_id: EpochId,
|
||||
|
||||
pub dealer: Addr,
|
||||
|
||||
pub all_dealings_fully_submitted: bool,
|
||||
|
||||
pub dealing_submission_status: BTreeMap<DealingIndex, DealingStatus>,
|
||||
}
|
||||
|
||||
impl DealerDealingsStatusResponse {
|
||||
pub fn full_dealings(&self) -> usize {
|
||||
self.dealing_submission_status
|
||||
.values()
|
||||
.filter(|s| s.fully_submitted)
|
||||
.count()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -13,8 +13,8 @@ use cosmwasm_schema::cw_serde;
|
||||
use crate::{
|
||||
dealer::{DealerDetailsResponse, PagedDealerResponse},
|
||||
dealing::{
|
||||
DealingChunkResponse, DealingChunkStatusResponse, DealingMetadataResponse,
|
||||
DealingStatusResponse,
|
||||
DealerDealingsStatusResponse, DealingChunkResponse, DealingChunkStatusResponse,
|
||||
DealingMetadataResponse, DealingStatusResponse,
|
||||
},
|
||||
types::{Epoch, InitialReplacementData, State},
|
||||
verification_key::{PagedVKSharesResponse, VkShareResponse},
|
||||
@@ -108,6 +108,9 @@ pub enum QueryMsg {
|
||||
dealing_index: DealingIndex,
|
||||
},
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(DealerDealingsStatusResponse))]
|
||||
GetDealerDealingsStatus { epoch_id: EpochId, dealer: String },
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(DealingStatusResponse))]
|
||||
GetDealingStatus {
|
||||
epoch_id: EpochId,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::ops::Deref;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
// some sane upper-bound size on byte sizes
|
||||
// currently set to 128 bytes
|
||||
@@ -23,6 +23,12 @@ impl Deref for ContractSafeBytes {
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for ContractSafeBytes {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for ContractSafeBytes {
|
||||
fn from(value: Vec<u8>) -> Self {
|
||||
ContractSafeBytes(value)
|
||||
|
||||
@@ -6,7 +6,8 @@ use crate::dealers::queries::{
|
||||
};
|
||||
use crate::dealers::transactions::try_add_dealer;
|
||||
use crate::dealings::queries::{
|
||||
query_dealing_chunk, query_dealing_chunk_status, query_dealing_metadata, query_dealing_status,
|
||||
query_dealer_dealings_status, query_dealing_chunk, query_dealing_chunk_status,
|
||||
query_dealing_metadata, query_dealing_status,
|
||||
};
|
||||
use crate::dealings::transactions::{try_commit_dealings_chunk, try_submit_dealings_metadata};
|
||||
use crate::epoch_state::queries::{
|
||||
@@ -149,6 +150,9 @@ pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> Result<QueryResponse,
|
||||
dealer,
|
||||
dealing_index,
|
||||
)?)?,
|
||||
QueryMsg::GetDealerDealingsStatus { epoch_id, dealer } => {
|
||||
to_binary(&query_dealer_dealings_status(deps, epoch_id, dealer)?)?
|
||||
}
|
||||
QueryMsg::GetDealingStatus {
|
||||
epoch_id,
|
||||
dealer,
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::dealings::storage::{StoredDealing, DEALINGS_METADATA};
|
||||
use crate::state::storage::STATE;
|
||||
use cosmwasm_std::{Deps, StdResult};
|
||||
use nym_coconut_dkg_common::dealing::{
|
||||
DealingChunkResponse, DealingChunkStatusResponse, DealingMetadataResponse,
|
||||
DealingStatusResponse,
|
||||
DealerDealingsStatusResponse, DealingChunkResponse, DealingChunkStatusResponse,
|
||||
DealingMetadataResponse, DealingStatus, DealingStatusResponse,
|
||||
};
|
||||
use nym_coconut_dkg_common::types::{ChunkIndex, DealingIndex, EpochId};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
/// Get the metadata associated with the particular dealing
|
||||
pub fn query_dealing_metadata(
|
||||
@@ -27,6 +29,34 @@ pub fn query_dealing_metadata(
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the status of all dealings of particular dealer for given epoch.
|
||||
pub fn query_dealer_dealings_status(
|
||||
deps: Deps<'_>,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
) -> StdResult<DealerDealingsStatusResponse> {
|
||||
let dealer = deps.api.addr_validate(&dealer)?;
|
||||
let state = STATE.load(deps.storage)?;
|
||||
|
||||
let mut dealing_submission_status: BTreeMap<DealingIndex, DealingStatus> = BTreeMap::new();
|
||||
|
||||
// Since our key size is in single digit range, querying all of this at once on chain is fine
|
||||
for dealing_index in 0..state.key_size {
|
||||
let metadata =
|
||||
DEALINGS_METADATA.may_load(deps.storage, (epoch_id, &dealer, dealing_index))?;
|
||||
dealing_submission_status.insert(dealing_index, metadata.into());
|
||||
}
|
||||
|
||||
Ok(DealerDealingsStatusResponse {
|
||||
epoch_id,
|
||||
dealer,
|
||||
all_dealings_fully_submitted: dealing_submission_status
|
||||
.values()
|
||||
.all(|d| d.fully_submitted),
|
||||
dealing_submission_status,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the status of particular dealing, i.e. whether it has been fully submitted.
|
||||
pub fn query_dealing_status(
|
||||
deps: Deps<'_>,
|
||||
@@ -37,17 +67,11 @@ pub fn query_dealing_status(
|
||||
let dealer = deps.api.addr_validate(&dealer)?;
|
||||
let metadata = DEALINGS_METADATA.may_load(deps.storage, (epoch_id, &dealer, dealing_index))?;
|
||||
|
||||
let full_dealing_submitted = if let Some(metadata) = metadata {
|
||||
metadata.is_complete()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
Ok(DealingStatusResponse {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
full_dealing_submitted,
|
||||
status: metadata.into(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -62,22 +86,18 @@ pub fn query_dealing_chunk_status(
|
||||
let dealer = deps.api.addr_validate(&dealer)?;
|
||||
let metadata = DEALINGS_METADATA.may_load(deps.storage, (epoch_id, &dealer, dealing_index))?;
|
||||
|
||||
let submission_height = if let Some(metadata) = metadata {
|
||||
if let Some(chunk) = metadata.submitted_chunks.get(&chunk_index) {
|
||||
chunk.submission_height
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let status = metadata
|
||||
.as_ref()
|
||||
.and_then(|m| m.submitted_chunks.get(&chunk_index))
|
||||
.map(|&c| c.status)
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(DealingChunkStatusResponse {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
chunk_index,
|
||||
submission_height,
|
||||
status,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ impl StoredDealing {
|
||||
let Some(chunk_info) = metadata.submitted_chunks.get(&chunk_index) else {
|
||||
return Ok(false);
|
||||
};
|
||||
Ok(chunk_info.submission_height.is_some())
|
||||
Ok(chunk_info.status.submitted())
|
||||
// StoredDealing::storage_key(epoch_id, dealer, dealing_index).has(storage)
|
||||
}
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ pub fn try_commit_dealings_chunk(
|
||||
};
|
||||
|
||||
// check if this dealer has already committed this particular dealing chunk
|
||||
if let Some(submission_height) = submission_status.submission_height {
|
||||
if let Some(submission_height) = submission_status.status.submission_height {
|
||||
return Err(ContractError::DealingChunkAlreadyCommitted {
|
||||
epoch_id: epoch.epoch_id,
|
||||
dealer: info.sender,
|
||||
@@ -174,8 +174,20 @@ pub fn try_commit_dealings_chunk(
|
||||
});
|
||||
}
|
||||
|
||||
// check if the received chunk has the specified size
|
||||
if submission_status.info.size != chunk.data.len() {
|
||||
return Err(ContractError::InconsistentChunkLength {
|
||||
epoch_id: epoch.epoch_id,
|
||||
dealer: info.sender,
|
||||
dealing_index: chunk.dealing_index,
|
||||
chunk_index: chunk.chunk_index,
|
||||
metadata_length: submission_status.info.size,
|
||||
received: chunk.data.len(),
|
||||
});
|
||||
}
|
||||
|
||||
// update the metadata
|
||||
submission_status.submission_height = Some(env.block.height);
|
||||
submission_status.status.submission_height = Some(env.block.height);
|
||||
store_metadata(
|
||||
deps.storage,
|
||||
epoch.epoch_id,
|
||||
|
||||
@@ -84,7 +84,7 @@ pub enum ContractError {
|
||||
chunks: usize,
|
||||
},
|
||||
|
||||
#[error("")]
|
||||
#[error("the declared chunk split for epoch {epoch_id} from dealer {dealer} for dealing index {dealing_index} is uneven. first chunk has size of {first_chunk_size} while chunk at index {chunk_index} has {size}")]
|
||||
UnevenChunkSplit {
|
||||
epoch_id: EpochId,
|
||||
dealer: Addr,
|
||||
@@ -94,6 +94,16 @@ pub enum ContractError {
|
||||
size: usize,
|
||||
},
|
||||
|
||||
#[error("the received chunk for epoch {epoch_id} from dealer {dealer} at dealing index {dealing_index} at chunk index {chunk_index} has inconsistent length. the metadata contains length of {metadata_length} while the received data is {received} bytes long")]
|
||||
InconsistentChunkLength {
|
||||
epoch_id: EpochId,
|
||||
dealer: Addr,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
metadata_length: usize,
|
||||
received: usize,
|
||||
},
|
||||
|
||||
#[error("dealer {dealer} has attempted to commit dealing metadata for epoch {epoch_id} for dealing index {dealing_index} zero chunks")]
|
||||
EmptyMetadata {
|
||||
epoch_id: EpochId,
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::coconut::error::Result;
|
||||
use cw3::{ProposalResponse, VoteResponse};
|
||||
use cw4::MemberResponse;
|
||||
use nym_coconut_bandwidth_contract_common::spend_credential::SpendCredentialResponse;
|
||||
use nym_coconut_dkg_common::dealer::{DealerDetails, DealerDetailsResponse, DealingStatusResponse};
|
||||
use nym_coconut_dkg_common::dealer::{DealerDetails, DealerDetailsResponse};
|
||||
use nym_coconut_dkg_common::dealing::{
|
||||
DealerDealingsStatusResponse, DealingChunkInfo, DealingMetadata, DealingStatusResponse,
|
||||
PartialContractDealing,
|
||||
};
|
||||
use nym_coconut_dkg_common::types::{
|
||||
DealingIndex, EncodedBTEPublicKeyWithProof, Epoch, EpochId, InitialReplacementData,
|
||||
PartialContractDealing, State,
|
||||
ChunkIndex, DealingIndex, EncodedBTEPublicKeyWithProof, Epoch, EpochId, InitialReplacementData,
|
||||
PartialContractDealingData, State,
|
||||
};
|
||||
use nym_coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare};
|
||||
use nym_contracts_common::IdentityKey;
|
||||
@@ -21,44 +25,77 @@ pub trait Client {
|
||||
async fn address(&self) -> AccountId;
|
||||
|
||||
async fn dkg_contract_address(&self) -> Result<AccountId>;
|
||||
|
||||
async fn get_tx(&self, tx_hash: Hash) -> Result<TxResponse>;
|
||||
|
||||
async fn get_proposal(&self, proposal_id: u64) -> Result<ProposalResponse>;
|
||||
|
||||
async fn list_proposals(&self) -> Result<Vec<ProposalResponse>>;
|
||||
|
||||
async fn get_vote(&self, proposal_id: u64, voter: String) -> Result<VoteResponse>;
|
||||
|
||||
async fn get_spent_credential(
|
||||
&self,
|
||||
blinded_serial_number: String,
|
||||
) -> Result<SpendCredentialResponse>;
|
||||
|
||||
async fn contract_state(&self) -> Result<State>;
|
||||
|
||||
async fn get_current_epoch(&self) -> Result<Epoch>;
|
||||
|
||||
async fn group_member(&self, addr: String) -> Result<MemberResponse>;
|
||||
|
||||
async fn get_current_epoch_threshold(&self) -> Result<Option<Threshold>>;
|
||||
|
||||
async fn get_initial_dealers(&self) -> Result<Option<InitialReplacementData>>;
|
||||
|
||||
async fn get_self_registered_dealer_details(&self) -> Result<DealerDetailsResponse>;
|
||||
|
||||
async fn get_dealer_dealings_status(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
) -> Result<DealerDealingsStatusResponse>;
|
||||
|
||||
async fn get_dealing_status(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
) -> Result<DealingStatusResponse>;
|
||||
|
||||
async fn get_current_dealers(&self) -> Result<Vec<DealerDetails>>;
|
||||
async fn get_dealings(
|
||||
|
||||
async fn get_dealing_metadata(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
) -> Result<Option<DealingMetadata>>;
|
||||
|
||||
async fn get_dealing_chunk(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: &str,
|
||||
) -> Result<Vec<PartialContractDealing>>;
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
) -> Result<Option<PartialContractDealingData>>;
|
||||
|
||||
async fn get_verification_key_share(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
) -> Result<Option<ContractVKShare>>;
|
||||
|
||||
async fn get_verification_key_shares(&self, epoch_id: EpochId) -> Result<Vec<ContractVKShare>>;
|
||||
|
||||
async fn vote_proposal(&self, proposal_id: u64, vote_yes: bool, fee: Option<Fee>)
|
||||
-> Result<()>;
|
||||
|
||||
async fn execute_proposal(&self, proposal_id: u64) -> Result<()>;
|
||||
|
||||
async fn advance_epoch_state(&self) -> Result<()>;
|
||||
|
||||
async fn register_dealer(
|
||||
&self,
|
||||
bte_key: EncodedBTEPublicKeyWithProof,
|
||||
@@ -66,11 +103,20 @@ pub trait Client {
|
||||
announce_address: String,
|
||||
resharing: bool,
|
||||
) -> Result<ExecuteResult>;
|
||||
async fn submit_dealing(
|
||||
|
||||
async fn submit_dealing_metadata(
|
||||
&self,
|
||||
dealing: PartialContractDealing,
|
||||
dealing_index: DealingIndex,
|
||||
chunks: Vec<DealingChunkInfo>,
|
||||
resharing: bool,
|
||||
) -> Result<ExecuteResult>;
|
||||
|
||||
async fn submit_dealing_chunk(
|
||||
&self,
|
||||
chunk: PartialContractDealing,
|
||||
resharing: bool,
|
||||
) -> Result<ExecuteResult>;
|
||||
|
||||
async fn submit_verification_key_share(
|
||||
&self,
|
||||
share: VerificationKeyShare,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::coconut::client::Client;
|
||||
@@ -6,9 +6,12 @@ use crate::coconut::error::CoconutError;
|
||||
use cw3::{ProposalResponse, Status, VoteResponse};
|
||||
use cw4::MemberResponse;
|
||||
use nym_coconut_dkg_common::dealer::{DealerDetails, DealerDetailsResponse};
|
||||
use nym_coconut_dkg_common::dealing::{
|
||||
DealerDealingsStatusResponse, DealingChunkInfo, PartialContractDealing,
|
||||
};
|
||||
use nym_coconut_dkg_common::types::{
|
||||
DealingIndex, EncodedBTEPublicKeyWithProof, Epoch, EpochId, InitialReplacementData, NodeIndex,
|
||||
PartialContractDealing, State as ContractState,
|
||||
ChunkIndex, DealingIndex, EncodedBTEPublicKeyWithProof, Epoch, EpochId, InitialReplacementData,
|
||||
NodeIndex, PartialContractDealingData, State as ContractState,
|
||||
};
|
||||
use nym_coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare};
|
||||
use nym_contracts_common::IdentityKey;
|
||||
@@ -75,25 +78,32 @@ impl DkgClient {
|
||||
self.inner.get_current_dealers().await
|
||||
}
|
||||
|
||||
pub(crate) async fn get_dealing_status(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealing_index: DealingIndex,
|
||||
) -> Result<bool, CoconutError> {
|
||||
let address = self.inner.address().await.to_string();
|
||||
|
||||
self.inner
|
||||
.get_dealing_status(epoch_id, address, dealing_index)
|
||||
.await
|
||||
.map(|r| r.dealing_submitted)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_dealings(
|
||||
pub(crate) async fn get_dealings_statuses(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
) -> Result<Vec<PartialContractDealing>, CoconutError> {
|
||||
self.inner.get_dealings(epoch_id, &dealer).await
|
||||
) -> Result<DealerDealingsStatusResponse, CoconutError> {
|
||||
self.inner
|
||||
.get_dealer_dealings_status(epoch_id, dealer)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn get_dealing_chunk(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: &str,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
) -> Result<PartialContractDealingData, CoconutError> {
|
||||
self.inner
|
||||
.get_dealing_chunk(epoch_id, dealer, dealing_index, chunk_index)
|
||||
.await?
|
||||
.ok_or(CoconutError::MissingDealingChunk {
|
||||
epoch_id,
|
||||
dealer: dealer.to_string(),
|
||||
dealing_index,
|
||||
chunk_index,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn get_verification_key_share<S: Into<String>>(
|
||||
@@ -165,12 +175,24 @@ impl DkgClient {
|
||||
Ok(node_index)
|
||||
}
|
||||
|
||||
pub(crate) async fn submit_dealing(
|
||||
pub(crate) async fn submit_dealing_metadata(
|
||||
&self,
|
||||
dealing: PartialContractDealing,
|
||||
dealing_index: DealingIndex,
|
||||
chunks: Vec<DealingChunkInfo>,
|
||||
resharing: bool,
|
||||
) -> Result<(), CoconutError> {
|
||||
self.inner.submit_dealing(dealing, resharing).await?;
|
||||
self.inner
|
||||
.submit_dealing_metadata(dealing_index, chunks, resharing)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn submit_dealing_chunk(
|
||||
&self,
|
||||
chunk: PartialContractDealing,
|
||||
resharing: bool,
|
||||
) -> Result<(), CoconutError> {
|
||||
self.inner.submit_dealing_chunk(chunk, resharing).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -7,12 +7,11 @@ use crate::coconut::dkg::controller::DkgController;
|
||||
use crate::coconut::error::CoconutError;
|
||||
use crate::coconut::keys::KeyPairWithEpoch;
|
||||
use log::debug;
|
||||
use nym_coconut_dkg_common::types::{
|
||||
ContractDealing, DealingIndex, EpochId, PartialContractDealing,
|
||||
};
|
||||
use nym_coconut_dkg_common::dealing::{chunk_dealing, DealingChunkInfo, MAX_DEALING_CHUNK_SIZE};
|
||||
use nym_coconut_dkg_common::types::{DealingIndex, EpochId};
|
||||
use nym_dkg::{Dealing, Scalar};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::path::PathBuf;
|
||||
use thiserror::Error;
|
||||
@@ -63,6 +62,8 @@ pub enum DealingGenerationError {
|
||||
}
|
||||
|
||||
impl<R: RngCore + CryptoRng> DkgController<R> {
|
||||
const DEALING_CHUNK_SIZE: usize = MAX_DEALING_CHUNK_SIZE;
|
||||
|
||||
async fn generate_dealings(
|
||||
&mut self,
|
||||
epoch_id: EpochId,
|
||||
@@ -153,28 +154,100 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
|
||||
resharing: bool,
|
||||
) -> Result<(), DealingGenerationError> {
|
||||
let dealing_state = self.state.dealing_exchange_state(epoch_id)?;
|
||||
let address = self.dkg_client.get_address().await.to_string();
|
||||
|
||||
let status = self
|
||||
.dkg_client
|
||||
.get_dealings_statuses(epoch_id, address)
|
||||
.await?;
|
||||
|
||||
if status.all_dealings_fully_submitted {
|
||||
warn!("we have actually submitted all dealings for epoch {epoch_id}, but somehow haven't finalized the state!");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// check which dealing is actually present on the chain (some might have gotten stuck in the mempool for quite a while)
|
||||
for (dealing_index, dealing) in &dealing_state.generated_dealings {
|
||||
// check which dealing is actually present on the chain (some might have gotten stuck in the mempool for quite a while)
|
||||
let dealing_submitted = self
|
||||
.dkg_client
|
||||
.get_dealing_status(epoch_id, *dealing_index)
|
||||
.await?;
|
||||
// check the dealing
|
||||
let Some(dealing_status) = status.dealing_submission_status.get(dealing_index) else {
|
||||
// we should NEVER see this error
|
||||
error!("we have generated a dealing for index {dealing_index} but the contract does not require its submission!");
|
||||
continue;
|
||||
};
|
||||
|
||||
if dealing_submitted {
|
||||
warn!("we have already submitted dealing {dealing_index} before - we probably crashed or the chain timed out!");
|
||||
if !dealing_status.has_metadata {
|
||||
// if the metadata doesn't exist on the chain, we haven't submitted anything - treat it as fresh submission
|
||||
self.submit_fresh_dealing(*dealing_index, dealing, resharing)
|
||||
.await?;
|
||||
continue;
|
||||
}
|
||||
warn!(
|
||||
"we have already generated dealing {dealing_index} before, but failed to submit it"
|
||||
);
|
||||
let contract_dealing =
|
||||
PartialContractDealing::new(*dealing_index, ContractDealing::from(dealing));
|
||||
|
||||
if dealing_status.fully_submitted {
|
||||
warn!("we have already submitted the full dealing {dealing_index} before - we probably crashed or the chain timed out!");
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut needs_resubmission = HashSet::new();
|
||||
for (chunk_id, chunk_status) in &dealing_status.chunk_submission_status {
|
||||
if !chunk_status.submitted() {
|
||||
needs_resubmission.insert(chunk_id);
|
||||
} else {
|
||||
warn!("[dealing {dealing_index}]: we have already submitted chunk at index {chunk_id} before - we probably crashed or the chain timed out!");
|
||||
}
|
||||
}
|
||||
|
||||
warn!("[dealing {dealing_index}]: the following chunks need to be resubmitted: {needs_resubmission:?}");
|
||||
|
||||
// perform the chunking (again)
|
||||
let mut chunks =
|
||||
chunk_dealing(*dealing_index, dealing.to_bytes(), Self::DEALING_CHUNK_SIZE);
|
||||
for chunk_index in needs_resubmission {
|
||||
// this is a hard failure, panic level, actually.
|
||||
// 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)
|
||||
.expect("chunking specification has changed mid-exchange!");
|
||||
debug!("[dealing {dealing_index}]: resubmitting chunk index {chunk_index}");
|
||||
self.dkg_client
|
||||
.submit_dealing_chunk(chunk, resharing)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn submit_fresh_dealing(
|
||||
&self,
|
||||
dealing_index: DealingIndex,
|
||||
dealing: &Dealing,
|
||||
resharing: bool,
|
||||
) -> Result<(), DealingGenerationError> {
|
||||
let bytes = dealing.to_bytes();
|
||||
|
||||
// construct metadata
|
||||
let chunk_info = DealingChunkInfo::construct(bytes.len(), Self::DEALING_CHUNK_SIZE);
|
||||
|
||||
let total_chunks = chunk_info.len();
|
||||
debug!("dealing at index {dealing_index} has been chunked into {total_chunks} pieces",);
|
||||
|
||||
// submit the metadata
|
||||
self.dkg_client
|
||||
.submit_dealing_metadata(dealing_index, chunk_info, resharing)
|
||||
.await?;
|
||||
|
||||
// actually chunk the dealing and submit the chunks
|
||||
let chunks = chunk_dealing(dealing_index, bytes, Self::DEALING_CHUNK_SIZE);
|
||||
|
||||
for (chunk_index, chunk) in chunks {
|
||||
let human_index = chunk_index + 1;
|
||||
debug!("[dealing {dealing_index}]: submitting chunk index {chunk_index} ({human_index}/{total_chunks})");
|
||||
|
||||
self.dkg_client
|
||||
.submit_dealing(contract_dealing, resharing)
|
||||
.submit_dealing_chunk(chunk, resharing)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -376,15 +449,11 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
|
||||
self.state.persist()?;
|
||||
}
|
||||
|
||||
for (i, (dealing_index, dealing)) in dealings.iter().enumerate() {
|
||||
for (i, (&dealing_index, dealing)) in dealings.iter().enumerate() {
|
||||
let i = i + 1;
|
||||
debug!("submitting dealing index {dealing_index} ({i}/{total})");
|
||||
|
||||
let contract_dealing =
|
||||
PartialContractDealing::new(*dealing_index, ContractDealing::from(dealing));
|
||||
|
||||
self.dkg_client
|
||||
.submit_dealing(contract_dealing, resharing)
|
||||
self.submit_fresh_dealing(dealing_index, dealing, resharing)
|
||||
.await?;
|
||||
}
|
||||
|
||||
@@ -481,7 +550,9 @@ pub(crate) mod tests {
|
||||
for submitted_dealing in submitted_dealings {
|
||||
let dealing = Dealing::try_from_bytes(submitted_dealing.data.as_slice())?;
|
||||
assert_eq!(
|
||||
generated_dealings.get(&submitted_dealing.index).unwrap(),
|
||||
generated_dealings
|
||||
.get(&submitted_dealing.dealing_index)
|
||||
.unwrap(),
|
||||
&dealing
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
use crate::coconut::dkg::controller::DkgController;
|
||||
use crate::coconut::error::CoconutError;
|
||||
use cosmwasm_std::Addr;
|
||||
use cw3::{ProposalResponse, Status};
|
||||
use nym_coconut_dkg_common::verification_key::owner_from_cosmos_msgs;
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
@@ -15,7 +14,7 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
|
||||
&self,
|
||||
dkg_contract: &AccountId,
|
||||
proposal: &ProposalResponse,
|
||||
) -> Option<(Addr, u64)> {
|
||||
) -> Option<(String, u64)> {
|
||||
// make sure the proposal we're checking is:
|
||||
// - still open (not point in voting for anything that has already expired)
|
||||
// - was proposed by the DKG contract - so that we'd ignore anything from malicious dealers
|
||||
@@ -30,7 +29,7 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
|
||||
|
||||
pub(crate) async fn get_validation_proposals(
|
||||
&self,
|
||||
) -> Result<HashMap<Addr, u64>, CoconutError> {
|
||||
) -> Result<HashMap<String, u64>, CoconutError> {
|
||||
let dkg_contract = self.dkg_client.dkg_contract_address().await?;
|
||||
|
||||
// FUTURE OPTIMIZATION: don't query for ALL proposals. say if we're in epoch 50,
|
||||
|
||||
@@ -12,7 +12,7 @@ use cosmwasm_std::Addr;
|
||||
use log::debug;
|
||||
use nym_coconut::{check_vk_pairing, Base58, SecretKey, VerificationKey};
|
||||
use nym_coconut_dkg_common::event_attributes::DKG_PROPOSAL_ID;
|
||||
use nym_coconut_dkg_common::types::{DealingIndex, EpochId, NodeIndex, PartialContractDealing};
|
||||
use nym_coconut_dkg_common::types::{DealingIndex, EpochId, NodeIndex};
|
||||
use nym_coconut_interface::KeyPair as CoconutKeyPair;
|
||||
use nym_dkg::{
|
||||
bte::{self, decrypt_share},
|
||||
@@ -21,7 +21,6 @@ use nym_dkg::{
|
||||
use nym_validator_client::nyxd::cosmwasm_client::logs::{find_attribute, Log};
|
||||
use nym_validator_client::nyxd::Hash;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use rocket::form::validate::Contains;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::ops::Deref;
|
||||
use thiserror::Error;
|
||||
@@ -75,7 +74,7 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
|
||||
epoch_id: EpochId,
|
||||
dealer: &Addr,
|
||||
epoch_receivers: &BTreeMap<NodeIndex, bte::PublicKey>,
|
||||
raw_dealings: Vec<PartialContractDealing>,
|
||||
raw_dealings: HashMap<DealingIndex, Vec<u8>>,
|
||||
prior_public_key: Option<VerificationKey>,
|
||||
) -> Result<Result<Vec<(DealingIndex, Dealing)>, DealerRejectionReason>, KeyDerivationError>
|
||||
{
|
||||
@@ -105,11 +104,9 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
|
||||
|
||||
let mut temp_verified = Vec::with_capacity(raw_dealings.len());
|
||||
// make sure ALL of them verify correctly, we can't have a situation where dealing 2 is valid but dealing 3 is not
|
||||
for raw_dealing in raw_dealings {
|
||||
let index = raw_dealing.index;
|
||||
|
||||
for (index, data) in raw_dealings {
|
||||
// recover the actual dealing from its submitted bytes representation
|
||||
let dealing = match Dealing::try_from_bytes(&raw_dealing.data) {
|
||||
let dealing = match Dealing::try_from_bytes(&data) {
|
||||
Ok(dealing) => dealing,
|
||||
Err(err) => {
|
||||
warn!("failed to recover dealing {index} from {dealer}: {err}");
|
||||
@@ -184,6 +181,63 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
|
||||
))
|
||||
}
|
||||
|
||||
async fn get_raw_dealings(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: &Addr,
|
||||
resharing: bool,
|
||||
initial_dealers: &[Addr],
|
||||
) -> Result<Result<HashMap<DealingIndex, Vec<u8>>, DealerRejectionReason>, KeyDerivationError>
|
||||
{
|
||||
let dealing_statuses = self
|
||||
.dkg_client
|
||||
.get_dealings_statuses(epoch_id, dealer.to_string())
|
||||
.await?;
|
||||
|
||||
let submitted = dealing_statuses.full_dealings();
|
||||
|
||||
// no point in making any queries if the dealer hasn't submitted ALL expected dealings
|
||||
if !dealing_statuses.all_dealings_fully_submitted {
|
||||
// the dealer only submitted some subset of dealings
|
||||
if submitted > 0 {
|
||||
return Ok(Err(
|
||||
DealerRejectionReason::InsufficientNumberOfDealingsProvided {
|
||||
got: submitted,
|
||||
expected: dealing_statuses.dealing_submission_status.len(),
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
// we might be in resharing mode and this dealer was not in "initial" set.
|
||||
// in that case we don't expect any dealings
|
||||
if resharing && !initial_dealers.contains(&dealer) {
|
||||
return Ok(Ok(HashMap::new()));
|
||||
}
|
||||
return Ok(Err(DealerRejectionReason::NoDealingsProvided));
|
||||
}
|
||||
|
||||
// TODO: introduce caching here in case we crash or chain times out because those queries are EXPENSIVE
|
||||
|
||||
// rebuild the dealings
|
||||
let mut raw_dealings = HashMap::new();
|
||||
for (dealing_index, info) in dealing_statuses.dealing_submission_status {
|
||||
let mut raw_dealing = Vec::new();
|
||||
|
||||
// note: we're iterating over a BTreeMap and so all chunk indices are guaranteed to be ordered
|
||||
for chunk_index in info.chunk_submission_status.into_keys() {
|
||||
let mut chunk_data = self
|
||||
.dkg_client
|
||||
.get_dealing_chunk(epoch_id, dealer.as_str(), dealing_index, chunk_index)
|
||||
.await?;
|
||||
raw_dealing.append(&mut chunk_data);
|
||||
}
|
||||
|
||||
raw_dealings.insert(dealing_index, raw_dealing);
|
||||
}
|
||||
|
||||
Ok(Ok(raw_dealings))
|
||||
}
|
||||
|
||||
/// Attempt to retrieve valid dealings submitted this epoch.
|
||||
///
|
||||
/// For each dealer that submitted a valid public key, query its dealings.
|
||||
@@ -194,8 +248,6 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
|
||||
epoch_id: EpochId,
|
||||
resharing: bool,
|
||||
) -> Result<BTreeMap<DealingIndex, BTreeMap<NodeIndex, Dealing>>, KeyDerivationError> {
|
||||
let expected_key_size = self.dkg_client.get_contract_state().await?.key_size;
|
||||
|
||||
let mut valid_dealings: BTreeMap<_, BTreeMap<_, _>> = BTreeMap::new();
|
||||
|
||||
// given at MOST we'll have like 50 entries here, iterating over entire vector for lookup is fine
|
||||
@@ -211,32 +263,19 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
|
||||
// note: if we're in resharing mode, the contract itself will forbid submission of dealings from
|
||||
// parties that were not "initial" dealers, so we don't have to worry about it
|
||||
|
||||
// TODO: introduce caching here in case we crash because those queries are EXPENSIVE
|
||||
let raw_dealings = self
|
||||
.dkg_client
|
||||
.get_dealings(epoch_id, dealer.to_string())
|
||||
.await?;
|
||||
|
||||
if raw_dealings.is_empty() {
|
||||
// we might be in resharing mode and this dealer was not in "initial" set.
|
||||
// in that case we don't expect any dealings
|
||||
if resharing && !initial_dealers.contains(&dealer) {
|
||||
let raw_dealings = match self
|
||||
.get_raw_dealings(epoch_id, &dealer, resharing, &initial_dealers)
|
||||
.await?
|
||||
{
|
||||
Ok(dealings) => dealings,
|
||||
Err(rejection) => {
|
||||
self.blacklist_dealer(epoch_id, dealer, rejection)?;
|
||||
continue;
|
||||
}
|
||||
self.blacklist_dealer(epoch_id, dealer, DealerRejectionReason::NoDealingsProvided)?;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// no point in verifying any dealings if we don't have all of them
|
||||
if raw_dealings.len() != expected_key_size as usize {
|
||||
self.blacklist_dealer(
|
||||
epoch_id,
|
||||
dealer,
|
||||
DealerRejectionReason::InsufficientNumberOfDealingsProvided {
|
||||
got: raw_dealings.len(),
|
||||
expected: expected_key_size as usize,
|
||||
},
|
||||
)?;
|
||||
// nothing to do
|
||||
if raw_dealings.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -449,9 +488,7 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
|
||||
// submitted proposals and find the one with our address
|
||||
self.get_validation_proposals()
|
||||
.await?
|
||||
.get(&Addr::unchecked(
|
||||
self.dkg_client.get_address().await.as_ref(),
|
||||
))
|
||||
.get(self.dkg_client.get_address().await.as_ref())
|
||||
.copied()
|
||||
.ok_or(KeyDerivationError::UnrecoverableProposalId)
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
|
||||
debug!("verifying vk share from {owner}");
|
||||
|
||||
// there's no point in checking anything if there doesn't exist an associated multisig proposal
|
||||
let Some(proposal_id) = proposals.get(&owner) else {
|
||||
let Some(proposal_id) = proposals.get(owner.as_ref()) else {
|
||||
warn!("there does not seem to exist proposal for share validation from {owner}");
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::node_status_api::models::NymApiStorageError;
|
||||
use nym_coconut_dkg_common::types::EpochId;
|
||||
use nym_coconut_dkg_common::types::{ChunkIndex, DealingIndex, EpochId};
|
||||
use nym_crypto::asymmetric::{
|
||||
encryption::KeyRecoveryError,
|
||||
identity::{Ed25519RecoveryError, SignatureError},
|
||||
@@ -141,9 +141,12 @@ pub enum CoconutError {
|
||||
#[error("the proposal id value for epoch {epoch_id} is not available")]
|
||||
UnavailableProposalId { epoch_id: EpochId },
|
||||
|
||||
#[error("insufficient number of dealings provided to derive the key")]
|
||||
InsufficientDealings {
|
||||
// TODO: details
|
||||
#[error("could not find dealing chunk {chunk_index} for dealing {dealing_index} from dealer {dealer} for epoch {epoch_id} on the chain!")]
|
||||
MissingDealingChunk {
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -653,7 +653,7 @@ impl super::client::Client for DummyClient {
|
||||
epoch_id,
|
||||
dealer: Addr::unchecked(dealer),
|
||||
dealing_index,
|
||||
dealing_submitted: dealings.get(dealing_index as usize).is_some(),
|
||||
full_dealing_submitted: dealings.get(dealing_index as usize).is_some(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,13 @@ use async_trait::async_trait;
|
||||
use cw3::{ProposalResponse, VoteResponse};
|
||||
use cw4::MemberResponse;
|
||||
use nym_coconut_bandwidth_contract_common::spend_credential::SpendCredentialResponse;
|
||||
use nym_coconut_dkg_common::dealer::DealingStatusResponse;
|
||||
use nym_coconut_dkg_common::dealing::{
|
||||
DealerDealingsStatusResponse, DealingChunkInfo, DealingMetadata, DealingStatusResponse,
|
||||
PartialContractDealing,
|
||||
};
|
||||
use nym_coconut_dkg_common::msg::QueryMsg as DkgQueryMsg;
|
||||
use nym_coconut_dkg_common::types::{
|
||||
DealingIndex, InitialReplacementData, PartialContractDealing, State,
|
||||
ChunkIndex, DealingIndex, InitialReplacementData, PartialContractDealingData, State,
|
||||
};
|
||||
use nym_coconut_dkg_common::{
|
||||
dealer::{DealerDetails, DealerDetailsResponse},
|
||||
@@ -424,6 +427,17 @@ impl crate::coconut::client::Client for Client {
|
||||
Ok(nyxd_query!(self, get_dealer_details(self_address).await?))
|
||||
}
|
||||
|
||||
async fn get_dealer_dealings_status(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
) -> crate::coconut::error::Result<DealerDealingsStatusResponse> {
|
||||
Ok(nyxd_query!(
|
||||
self,
|
||||
get_dealer_dealings_status(epoch_id, dealer).await?
|
||||
))
|
||||
}
|
||||
|
||||
async fn get_dealing_status(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
@@ -440,14 +454,32 @@ impl crate::coconut::client::Client for Client {
|
||||
Ok(nyxd_query!(self, get_all_current_dealers().await?))
|
||||
}
|
||||
|
||||
async fn get_dealings(
|
||||
async fn get_dealing_metadata(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
) -> crate::coconut::error::Result<Option<DealingMetadata>> {
|
||||
Ok(nyxd_query!(
|
||||
self,
|
||||
get_dealings_metadata(epoch_id, dealer, dealing_index)
|
||||
.await?
|
||||
.metadata
|
||||
))
|
||||
}
|
||||
|
||||
async fn get_dealing_chunk(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: &str,
|
||||
) -> crate::coconut::error::Result<Vec<PartialContractDealing>> {
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
) -> crate::coconut::error::Result<Option<PartialContractDealingData>> {
|
||||
Ok(nyxd_query!(
|
||||
self,
|
||||
get_all_dealer_dealings(epoch_id, dealer).await?
|
||||
get_dealing_chunk(epoch_id, dealer.to_string(), dealing_index, chunk_index)
|
||||
.await?
|
||||
.chunk
|
||||
))
|
||||
}
|
||||
|
||||
@@ -502,14 +534,26 @@ impl crate::coconut::client::Client for Client {
|
||||
))
|
||||
}
|
||||
|
||||
async fn submit_dealing(
|
||||
async fn submit_dealing_metadata(
|
||||
&self,
|
||||
dealing: PartialContractDealing,
|
||||
dealing_index: DealingIndex,
|
||||
chunks: Vec<DealingChunkInfo>,
|
||||
resharing: bool,
|
||||
) -> crate::coconut::error::Result<ExecuteResult> {
|
||||
Ok(nyxd_signing!(
|
||||
self,
|
||||
submit_dealing_metadata(dealing_index, chunks, resharing, None).await?
|
||||
))
|
||||
}
|
||||
|
||||
async fn submit_dealing_chunk(
|
||||
&self,
|
||||
chunk: PartialContractDealing,
|
||||
resharing: bool,
|
||||
) -> Result<ExecuteResult, CoconutError> {
|
||||
Ok(nyxd_signing!(
|
||||
self,
|
||||
submit_dealing_bytes(dealing, resharing, None).await?
|
||||
submit_dealing_chunk(chunk, resharing, None).await?
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user