contract changes

This commit is contained in:
Jędrzej Stuczyński
2024-02-01 12:14:43 +00:00
parent b393405db8
commit 35d7525d1e
14 changed files with 722 additions and 564 deletions
@@ -1,10 +1,7 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::types::{
DealingIndex, EncodedBTEPublicKeyWithProof, EpochId, NodeIndex, PartialContractDealing,
PartialContractDealingData,
};
use crate::types::{EncodedBTEPublicKeyWithProof, NodeIndex};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Addr;
@@ -68,53 +65,3 @@ impl PagedDealerResponse {
}
}
}
#[cw_serde]
pub struct DealingResponse {
pub epoch_id: EpochId,
pub dealer: Addr,
pub dealing_index: DealingIndex,
pub dealing: Option<PartialContractDealingData>,
}
#[cw_serde]
pub struct DealingStatusResponse {
pub epoch_id: EpochId,
pub dealer: Addr,
pub dealing_index: DealingIndex,
pub dealing_submitted: bool,
}
#[cw_serde]
pub struct PagedDealingsResponse {
pub epoch_id: EpochId,
pub dealer: Addr,
pub dealings: Vec<PartialContractDealing>,
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
pub start_next_after: Option<DealingIndex>,
}
impl PagedDealingsResponse {
pub fn new(
epoch_id: EpochId,
dealer: Addr,
dealings: Vec<PartialContractDealing>,
start_next_after: Option<DealingIndex>,
) -> Self {
PagedDealingsResponse {
epoch_id,
dealer,
dealings,
start_next_after,
}
}
}
@@ -0,0 +1,198 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::types::{ChunkIndex, DealingIndex, EpochId, PartialContractDealingData};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Addr;
use std::collections::BTreeMap;
/// Defines the maximum size of a dealing chunk. Currently set to 2kB
pub const MAX_DEALING_CHUNK_SIZE: usize = 2048;
/// Defines the maximum size of a full dealing.
/// Currently set to 100kB (which is enough for a dealing created for 100 parties)
pub const MAX_DEALING_SIZE: usize = 102400;
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;
#[cw_serde]
pub struct DealingChunkInfo {
pub size: usize,
}
impl DealingChunkInfo {
pub fn new(size: usize) -> Self {
DealingChunkInfo { size }
}
pub fn construct(dealing_len: usize, chunk_size: usize) -> Vec<Self> {
let (full_chunks, overflow) = (dealing_len / chunk_size, dealing_len % chunk_size);
let mut chunks = Vec::new();
for _ in 0..full_chunks {
chunks.push(DealingChunkInfo::new(chunk_size));
}
if overflow != 0 {
chunks.push(DealingChunkInfo::new(overflow));
}
chunks
}
}
#[cw_serde]
pub struct SubmittedChunk {
pub info: DealingChunkInfo,
// this field is updated by the contract itself to indicate when this particular chunk has been received
pub submission_height: Option<u64>,
}
impl From<DealingChunkInfo> for SubmittedChunk {
fn from(value: DealingChunkInfo) -> Self {
SubmittedChunk::new(value)
}
}
impl SubmittedChunk {
pub fn new(info: DealingChunkInfo) -> Self {
SubmittedChunk {
info,
submission_height: None,
}
}
}
#[cw_serde]
pub struct DealingMetadata {
pub dealing_index: DealingIndex,
pub submitted_chunks: BTreeMap<ChunkIndex, SubmittedChunk>,
}
impl DealingMetadata {
pub fn new(dealing_index: DealingIndex, chunks: Vec<DealingChunkInfo>) -> Self {
DealingMetadata {
dealing_index,
submitted_chunks: chunks
.into_iter()
.enumerate()
.map(|(id, chunk)| (id as ChunkIndex, chunk.into()))
.collect(),
}
}
pub fn is_complete(&self) -> bool {
self.submitted_chunks
.values()
.all(|c| c.submission_height.is_some())
}
pub fn total_size(&self) -> usize {
self.submitted_chunks.values().map(|c| c.info.size).sum()
}
}
#[cw_serde]
pub struct PartialContractDealing {
pub dealing_index: DealingIndex,
pub chunk_index: ChunkIndex,
pub data: PartialContractDealingData,
}
impl PartialContractDealing {
pub fn new(
dealing_index: DealingIndex,
chunk_index: ChunkIndex,
data: PartialContractDealingData,
) -> Self {
PartialContractDealing {
dealing_index,
chunk_index,
data,
}
}
}
#[cw_serde]
pub struct DealingMetadataResponse {
pub epoch_id: EpochId,
pub dealer: Addr,
pub dealing_index: DealingIndex,
pub metadata: Option<DealingMetadata>,
}
#[cw_serde]
pub struct DealingChunkResponse {
pub epoch_id: EpochId,
pub dealer: Addr,
pub dealing_index: DealingIndex,
pub chunk_index: ChunkIndex,
pub chunk: Option<PartialContractDealingData>,
}
#[cw_serde]
pub struct DealingChunkStatusResponse {
pub epoch_id: EpochId,
pub dealer: Addr,
pub dealing_index: DealingIndex,
pub chunk_index: ChunkIndex,
pub submission_height: Option<u64>,
}
#[cw_serde]
pub struct DealingStatusResponse {
pub epoch_id: EpochId,
pub dealer: Addr,
pub dealing_index: DealingIndex,
pub full_dealing_submitted: bool,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn chunking_dealings() {
const CHUNK_SIZE: usize = 512;
let test_cases = [
(CHUNK_SIZE - 10, CHUNK_SIZE, 1),
(CHUNK_SIZE, CHUNK_SIZE, 1),
(CHUNK_SIZE + 10, CHUNK_SIZE, 2),
(CHUNK_SIZE * 2, CHUNK_SIZE, 2),
(CHUNK_SIZE * 2 + 1, CHUNK_SIZE, 3),
(CHUNK_SIZE * 10 + 42, CHUNK_SIZE, 11),
];
for (dealing_len, chunk_size, expected_chunks) in test_cases {
let chunks = DealingChunkInfo::construct(dealing_len, chunk_size);
assert_eq!(expected_chunks, chunks.len());
assert_eq!(dealing_len, chunks.iter().map(|c| c.size).sum::<usize>());
let mut expected_last = dealing_len % chunk_size;
if expected_last == 0 {
expected_last = chunk_size;
}
assert_eq!(chunks.last().unwrap().size, expected_last);
}
}
}
@@ -1,4 +1,8 @@
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod dealer;
pub mod dealing;
pub mod event_attributes;
pub mod msg;
pub mod types;
@@ -1,23 +1,24 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::dealing::{DealingChunkInfo, PartialContractDealing};
use crate::types::{
DealingIndex, EncodedBTEPublicKeyWithProof, EpochId, PartialContractDealing, TimeConfiguration,
ChunkIndex, DealingIndex, EncodedBTEPublicKeyWithProof, EpochId, TimeConfiguration,
};
use crate::verification_key::VerificationKeyShare;
use contracts_common::IdentityKey;
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Addr;
#[cfg(feature = "schema")]
use crate::{
dealer::{
DealerDetailsResponse, DealingResponse, DealingStatusResponse, PagedDealerResponse,
PagedDealingsResponse,
dealer::{DealerDetailsResponse, PagedDealerResponse},
dealing::{
DealingChunkResponse, DealingChunkStatusResponse, DealingMetadataResponse,
DealingStatusResponse,
},
types::{Epoch, InitialReplacementData, State},
verification_key::{PagedVKSharesResponse, VkShareResponse},
};
use contracts_common::IdentityKey;
#[cfg(feature = "schema")]
use cosmwasm_schema::QueryResponses;
@@ -44,8 +45,14 @@ pub enum ExecuteMsg {
resharing: bool,
},
CommitDealing {
dealing: PartialContractDealing,
CommitDealingsMetadata {
dealing_index: DealingIndex,
chunks: Vec<DealingChunkInfo>,
resharing: bool,
},
CommitDealingsChunk {
chunk: PartialContractDealing,
resharing: bool,
},
@@ -55,8 +62,7 @@ pub enum ExecuteMsg {
},
VerifyVerificationKeyShare {
// TODO: this should be using a String...
owner: Addr,
owner: String,
resharing: bool,
},
@@ -95,6 +101,13 @@ pub enum QueryMsg {
start_after: Option<String>,
},
#[cfg_attr(feature = "schema", returns(DealingMetadataResponse))]
GetDealingsMetadata {
epoch_id: EpochId,
dealer: String,
dealing_index: DealingIndex,
},
#[cfg_attr(feature = "schema", returns(DealingStatusResponse))]
GetDealingStatus {
epoch_id: EpochId,
@@ -102,19 +115,20 @@ pub enum QueryMsg {
dealing_index: DealingIndex,
},
#[cfg_attr(feature = "schema", returns(DealingResponse))]
GetDealing {
#[cfg_attr(feature = "schema", returns(DealingChunkStatusResponse))]
GetDealingChunkStatus {
epoch_id: EpochId,
dealer: String,
dealing_index: DealingIndex,
chunk_index: ChunkIndex,
},
#[cfg_attr(feature = "schema", returns(PagedDealingsResponse))]
GetDealings {
#[cfg_attr(feature = "schema", returns(DealingChunkResponse))]
GetDealingChunk {
epoch_id: EpochId,
dealer: String,
limit: Option<u32>,
start_after: Option<DealingIndex>,
dealing_index: DealingIndex,
chunk_index: ChunkIndex,
},
#[cfg_attr(feature = "schema", returns(VkShareResponse))]
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_schema::cw_serde;
use std::collections::BTreeMap;
use std::fmt::{Display, Formatter};
use std::str::FromStr;
@@ -22,102 +21,6 @@ pub type DealingIndex = u32;
pub type ChunkIndex = u16;
pub type PartialContractDealingData = ContractSafeBytes;
/// Defines the maximum size of a dealing chunk. Currently set to 2kB
pub const MAX_DEALING_CHUNK_SIZE: usize = 2048;
/// Defines the maximum size of a full dealing.
/// Currently set to 100kB (which is enough for a dealing created for 100 parties)
pub const MAX_DEALING_SIZE: usize = 102400;
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;
#[cw_serde]
pub struct SubmittedChunk {
pub size: usize,
// this field is updated by the contract itself to indicate when this particular chunk has been received
pub submission_height: Option<u64>,
}
impl SubmittedChunk {
pub fn new(size: usize) -> Self {
SubmittedChunk {
size,
submission_height: None,
}
}
}
#[cw_serde]
pub struct DealingMetadata {
pub dealing_index: DealingIndex,
pub submitted_chunks: BTreeMap<ChunkIndex, SubmittedChunk>,
}
impl DealingMetadata {
pub fn new(dealing_index: DealingIndex, dealing_len: usize, chunk_size: usize) -> Self {
let (full_chunks, overflow) = (dealing_len / chunk_size, dealing_len % chunk_size);
let mut submitted_chunks = BTreeMap::new();
for id in 0..full_chunks {
submitted_chunks.insert(id as ChunkIndex, SubmittedChunk::new(chunk_size));
}
if overflow != 0 {
submitted_chunks.insert(full_chunks as ChunkIndex, SubmittedChunk::new(overflow));
}
DealingMetadata {
dealing_index,
submitted_chunks,
}
}
pub fn is_complete(&self) -> bool {
self.submitted_chunks
.values()
.all(|c| c.submission_height.is_some())
}
pub fn total_size(&self) -> usize {
self.submitted_chunks.values().map(|c| c.size).sum()
}
}
#[cw_serde]
pub struct PartialContractDealing {
pub dealing_index: DealingIndex,
pub chunk_index: ChunkIndex,
pub data: PartialContractDealingData,
}
impl PartialContractDealing {
pub fn new(
dealing_index: DealingIndex,
chunk_index: ChunkIndex,
data: PartialContractDealingData,
) -> Self {
PartialContractDealing {
dealing_index,
chunk_index,
data,
}
}
}
// impl From<(DealingIndex, ContractDealing)> for PartialContractDealing {
// fn from(value: (DealingIndex, ContractDealing)) -> Self {
// PartialContractDealing {
// dealing_index: value.0,
// data: value.1,
// }
// }
// }
#[cw_serde]
pub struct InitialReplacementData {
pub initial_dealers: Vec<Addr>,
@@ -350,37 +253,3 @@ impl EpochState {
*self == EpochState::InProgress
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dealing_metadata() {
const CHUNK_SIZE: usize = 512;
let test_cases = [
(CHUNK_SIZE - 10, CHUNK_SIZE, 1),
(CHUNK_SIZE, CHUNK_SIZE, 1),
(CHUNK_SIZE + 10, CHUNK_SIZE, 2),
(CHUNK_SIZE * 2, CHUNK_SIZE, 2),
(CHUNK_SIZE * 2 + 1, CHUNK_SIZE, 3),
(CHUNK_SIZE * 10 + 42, CHUNK_SIZE, 11),
];
for (dealing_len, chunk_size, expected_chunks) in test_cases {
let meta = DealingMetadata::new(42, dealing_len, chunk_size);
assert_eq!(expected_chunks, meta.submitted_chunks.len());
assert_eq!(dealing_len, meta.total_size());
let mut expected_last = dealing_len % chunk_size;
if expected_last == 0 {
expected_last = chunk_size;
}
assert_eq!(
meta.submitted_chunks.last_key_value().unwrap().1.size,
expected_last
);
}
}
}
@@ -43,7 +43,10 @@ pub fn to_cosmos_msg(
multisig_addr: String,
expiration_time: Timestamp,
) -> StdResult<CosmosMsg> {
let verify_vk_share_req = ExecuteMsg::VerifyVerificationKeyShare { owner, resharing };
let verify_vk_share_req = ExecuteMsg::VerifyVerificationKeyShare {
owner: owner.to_string(),
resharing,
};
let verify_vk_share_msg = CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: coconut_dkg_addr,
msg: to_binary(&verify_vk_share_req)?,
@@ -67,7 +70,7 @@ pub fn to_cosmos_msg(
// DKG SAFETY:
// each legit verification proposal will only contain a single execute msg,
// if they have more than one, we can safely ignore it
pub fn owner_from_cosmos_msgs(msgs: &[CosmosMsg]) -> Option<Addr> {
pub fn owner_from_cosmos_msgs(msgs: &[CosmosMsg]) -> Option<String> {
if msgs.len() != 1 {
return None;
}
+40 -15
View File
@@ -5,8 +5,10 @@ use crate::dealers::queries::{
query_current_dealers_paged, query_dealer_details, query_past_dealers_paged,
};
use crate::dealers::transactions::try_add_dealer;
use crate::dealings::queries::{query_dealing, query_dealing_status, query_dealings_paged};
use crate::dealings::transactions::try_commit_dealings_chunk;
use crate::dealings::queries::{
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::{
query_current_epoch, query_current_epoch_threshold, query_initial_dealers,
};
@@ -100,8 +102,13 @@ pub fn execute(
announce_address,
resharing,
),
ExecuteMsg::CommitDealing { dealing, resharing } => {
try_commit_dealings_chunk(deps, info, dealing, resharing)
ExecuteMsg::CommitDealingsMetadata {
dealing_index,
chunks,
resharing,
} => try_submit_dealings_metadata(deps, info, dealing_index, chunks, resharing),
ExecuteMsg::CommitDealingsChunk { chunk, resharing } => {
try_commit_dealings_chunk(deps, env, info, chunk, resharing)
}
ExecuteMsg::CommitVerificationKeyShare { share, resharing } => {
try_commit_verification_key_share(deps, env, info, share, resharing)
@@ -132,6 +139,16 @@ pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> Result<QueryResponse,
QueryMsg::GetPastDealers { limit, start_after } => {
to_binary(&query_past_dealers_paged(deps, start_after, limit)?)?
}
QueryMsg::GetDealingsMetadata {
epoch_id,
dealer,
dealing_index,
} => to_binary(&query_dealing_metadata(
deps,
epoch_id,
dealer,
dealing_index,
)?)?,
QueryMsg::GetDealingStatus {
epoch_id,
dealer,
@@ -142,22 +159,29 @@ pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> Result<QueryResponse,
dealer,
dealing_index,
)?)?,
QueryMsg::GetDealing {
QueryMsg::GetDealingChunkStatus {
epoch_id,
dealer,
dealing_index,
} => to_binary(&query_dealing(deps, epoch_id, dealer, dealing_index)?)?,
QueryMsg::GetDealings {
epoch_id,
dealer,
limit,
start_after,
} => to_binary(&query_dealings_paged(
chunk_index,
} => to_binary(&query_dealing_chunk_status(
deps,
epoch_id,
dealer,
start_after,
limit,
dealing_index,
chunk_index,
)?)?,
QueryMsg::GetDealingChunk {
epoch_id,
dealer,
dealing_index,
chunk_index,
} => to_binary(&query_dealing_chunk(
deps,
epoch_id,
dealer,
dealing_index,
chunk_index,
)?)?,
QueryMsg::GetVerificationKey { owner, epoch_id } => {
to_binary(&query_vk_share(deps, owner, epoch_id)?)?
@@ -207,8 +231,9 @@ mod tests {
use cosmwasm_std::{coins, Addr};
use cw4::Member;
use cw_multi_test::{App, AppBuilder, AppResponse, ContractWrapper, Executor};
use nym_coconut_dkg_common::dealing::DEFAULT_DEALINGS;
use nym_coconut_dkg_common::msg::ExecuteMsg::{InitiateDkg, RegisterDealer};
use nym_coconut_dkg_common::types::{NodeIndex, DEFAULT_DEALINGS};
use nym_coconut_dkg_common::types::NodeIndex;
use nym_group_contract_common::msg::InstantiateMsg as GroupInstantiateMsg;
fn instantiate_with_group(app: &mut App, members: &[Addr]) -> Addr {
+72 -47
View File
@@ -1,17 +1,33 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::dealings::storage;
use crate::dealings::storage::StoredDealing;
use crate::dealings::storage::{StoredDealing, DEALINGS_METADATA};
use cosmwasm_std::{Deps, StdResult};
use cw_storage_plus::Bound;
use nym_coconut_dkg_common::dealer::{
DealingResponse, DealingStatusResponse, PagedDealingsResponse,
use nym_coconut_dkg_common::dealing::{
DealingChunkResponse, DealingChunkStatusResponse, DealingMetadataResponse,
DealingStatusResponse,
};
use nym_coconut_dkg_common::types::{DealingIndex, EpochId};
use nym_coconut_dkg_common::types::{ChunkIndex, DealingIndex, EpochId};
// this does almost the same as query_dealing but doesn't return the actual dealing to make it easier on the validator
// so it wouldn't need to deal with the deserialization
/// Get the metadata associated with the particular dealing
pub fn query_dealing_metadata(
deps: Deps<'_>,
epoch_id: EpochId,
dealer: String,
dealing_index: DealingIndex,
) -> StdResult<DealingMetadataResponse> {
let dealer = deps.api.addr_validate(&dealer)?;
let metadata = DEALINGS_METADATA.may_load(deps.storage, (epoch_id, &dealer, dealing_index))?;
Ok(DealingMetadataResponse {
epoch_id,
dealer,
dealing_index,
metadata,
})
}
/// Get the status of particular dealing, i.e. whether it has been fully submitted.
pub fn query_dealing_status(
deps: Deps<'_>,
epoch_id: EpochId,
@@ -19,61 +35,70 @@ pub fn query_dealing_status(
dealing_index: DealingIndex,
) -> StdResult<DealingStatusResponse> {
let dealer = deps.api.addr_validate(&dealer)?;
let dealing_submitted = StoredDealing::exists(deps.storage, epoch_id, &dealer, dealing_index);
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,
dealing_submitted,
full_dealing_submitted,
})
}
pub fn query_dealing(
/// Get the status of particular chunk, i.e. whether (and when) it has been fully submitted.
pub fn query_dealing_chunk_status(
deps: Deps<'_>,
epoch_id: EpochId,
dealer: String,
dealing_index: DealingIndex,
) -> StdResult<DealingResponse> {
todo!()
// let dealer = deps.api.addr_validate(&dealer)?;
// let dealing = StoredDealing::read(deps.storage, epoch_id, &dealer, dealing_index);
//
// Ok(DealingResponse {
// epoch_id,
// dealer,
// dealing_index,
// dealing,
// })
chunk_index: ChunkIndex,
) -> StdResult<DealingChunkStatusResponse> {
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
};
Ok(DealingChunkStatusResponse {
epoch_id,
dealer,
dealing_index,
chunk_index,
submission_height,
})
}
pub fn query_dealings_paged(
/// Get the particular chunk of the dealing.
pub fn query_dealing_chunk(
deps: Deps<'_>,
epoch_id: EpochId,
dealer: String,
start_after: Option<DealingIndex>,
limit: Option<u32>,
) -> StdResult<PagedDealingsResponse> {
todo!()
// let limit = limit
// .unwrap_or(storage::DEALINGS_PAGE_DEFAULT_LIMIT)
// .min(storage::DEALINGS_PAGE_MAX_LIMIT);
//
// let dealer = deps.api.addr_validate(&dealer)?;
// let start = start_after.map(Bound::exclusive);
//
// let dealings = StoredDealing::prefix_range(deps.storage, (epoch_id, &dealer), start)
// .take(limit as usize)
// .collect::<StdResult<Vec<_>>>()?;
//
// let start_next_after = dealings.last().map(|dealing| dealing.dealing_index);
//
// Ok(PagedDealingsResponse::new(
// epoch_id,
// dealer,
// dealings,
// start_next_after,
// ))
dealing_index: DealingIndex,
chunk_index: ChunkIndex,
) -> StdResult<DealingChunkResponse> {
let dealer = deps.api.addr_validate(&dealer)?;
let chunk = StoredDealing::read(deps.storage, epoch_id, &dealer, dealing_index, chunk_index);
Ok(DealingChunkResponse {
epoch_id,
dealer,
dealing_index,
chunk_index,
chunk,
})
}
// #[cfg(test)]
+297 -231
View File
@@ -1,21 +1,18 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::ContractError;
use cosmwasm_std::{Addr, Order, Record, StdResult, Storage};
use cw_storage_plus::{Bound, Key, KeyDeserialize, Map, Path, Prefix, Prefixer, PrimaryKey};
use nym_coconut_dkg_common::dealing::{DealingMetadata, PartialContractDealing};
use nym_coconut_dkg_common::types::{
ChunkIndex, ContractSafeBytes, DealingIndex, DealingMetadata, EpochId, PartialContractDealing,
PartialContractDealingData,
ChunkIndex, ContractSafeBytes, DealingIndex, EpochId, PartialContractDealingData,
};
pub(crate) const DEALINGS_PAGE_MAX_LIMIT: u32 = 2;
pub(crate) const DEALINGS_PAGE_DEFAULT_LIMIT: u32 = 1;
type Dealer<'a> = &'a Addr;
/// Metadata for a dealing for given `EpochId`, submitted by particular `Dealer` for given `DealingIndex`.
const DEALINGS_METADATA: Map<(EpochId, Dealer, DealingIndex), DealingMetadata> =
pub(crate) const DEALINGS_METADATA: Map<(EpochId, Dealer, DealingIndex), DealingMetadata> =
Map::new("dealings_metadata");
pub(crate) fn metadata_exists(
@@ -61,7 +58,7 @@ pub(crate) fn store_metadata(
Ok(DEALINGS_METADATA.save(storage, (epoch_id, dealer, dealing_index), metadata)?)
}
// dealings are stored in a multilevel map with the following hierarchy:
// dealings data is stored in a multilevel map with the following hierarchy:
// - epoch-id:
// - issuer-address:
// - dealing id:
@@ -69,7 +66,6 @@ pub(crate) fn store_metadata(
// - dealing content
// NOTE: we're storing raw bytes bypassing serialization, so we can't use the `Map` type,
// thus make sure you always use the below methods for using the storage!
pub(crate) struct StoredDealing;
impl StoredDealing {
@@ -77,13 +73,12 @@ impl StoredDealing {
fn deserialize_dealing_record(
kv: Record,
) -> StdResult<(DealingIndex, PartialContractDealingData)> {
todo!()
// let (k, v) = kv;
// let index = <DealingIndex as KeyDeserialize>::from_vec(k)?;
// let data = ContractSafeBytes(v);
//
// Ok((index, data))
) -> StdResult<(ChunkIndex, PartialContractDealingData)> {
let (k, v) = kv;
let index = <ChunkIndex as KeyDeserialize>::from_vec(k)?;
let data = ContractSafeBytes(v);
Ok((index, data))
}
fn storage_key(
@@ -102,25 +97,37 @@ impl StoredDealing {
)
}
// fn prefix(prefix: (EpochId, Dealer)) -> Prefix<DealingIndex, ContractSafeBytes, DealingIndex> {
// todo!()
// // Prefix::with_deserialization_functions(
// // Self::NAMESPACE,
// // &prefix.prefix(),
// // &[],
// // // explicitly panic to make sure we're never attempting to call an unexpected deserializer on our data
// // |_, _, kv| Self::deserialize_dealing_record(kv),
// // |_, _, _| panic!("attempted to call custom de_fn_v"),
// // )
// }
fn prefix(
prefix: (EpochId, Dealer, DealingIndex),
) -> Prefix<ChunkIndex, PartialContractDealingData, ChunkIndex> {
Prefix::with_deserialization_functions(
Self::NAMESPACE,
&prefix.prefix(),
&[],
// explicitly panic to make sure we're never attempting to call an unexpected deserializer on our data
|_, _, kv| Self::deserialize_dealing_record(kv),
|_, _, _| panic!("attempted to call custom de_fn_v"),
)
}
pub(crate) fn exists(
storage: &dyn Storage,
epoch_id: EpochId,
dealer: &Addr,
dealing_index: DealingIndex,
) -> bool {
todo!()
chunk_index: ChunkIndex,
) -> StdResult<bool> {
// whenever the dealing is saved, the metadata is appropriately updated
// reading metadata is way cheaper than the dealing chunk itself
let Some(metadata) =
DEALINGS_METADATA.may_load(storage, (epoch_id, dealer, dealing_index))?
else {
return Ok(false);
};
let Some(chunk_info) = metadata.submitted_chunks.get(&chunk_index) else {
return Ok(false);
};
Ok(chunk_info.submission_height.is_some())
// StoredDealing::storage_key(epoch_id, dealer, dealing_index).has(storage)
}
@@ -128,12 +135,16 @@ impl StoredDealing {
storage: &mut dyn Storage,
epoch_id: EpochId,
dealer: Dealer,
dealing: PartialContractDealing,
dealng_chunk: PartialContractDealing,
) {
// NOTE: we're storing bytes directly here!
let storage_key =
Self::storage_key(epoch_id, dealer, dealing.dealing_index, dealing.chunk_index);
storage.set(&storage_key, dealing.data.as_slice());
let storage_key = Self::storage_key(
epoch_id,
dealer,
dealng_chunk.dealing_index,
dealng_chunk.chunk_index,
);
storage.set(&storage_key, dealng_chunk.data.as_slice());
}
pub(crate) fn read(
@@ -147,209 +158,264 @@ impl StoredDealing {
storage.get(&storage_key).map(ContractSafeBytes)
}
// pub(crate) fn prefix_range<'a>(
// storage: &'a dyn Storage,
// prefix: (EpochId, Dealer),
// start: Option<Bound<DealingIndex>>,
// ) -> impl Iterator<Item = StdResult<PartialContractDealing>> + 'a {
// vec![].into_iter()
// // todo!()
// // Self::prefix(prefix)
// // .range(storage, start, None, Order::Ascending)
// // .map(|maybe_record| maybe_record.map(Into::into))
// }
pub(crate) fn prefix_range<'a>(
storage: &'a dyn Storage,
prefix: (EpochId, Dealer, DealingIndex),
start: Option<Bound<ChunkIndex>>,
) -> impl Iterator<Item = StdResult<PartialContractDealing>> + 'a {
let dealing_index = prefix.2;
Self::prefix(prefix)
.range(storage, start, None, Order::Ascending)
.map(move |maybe_record| {
maybe_record.map(|(chunk_index, data)| PartialContractDealing {
dealing_index,
chunk_index,
data,
})
})
}
// iterate over all values, only to be used in tests due to the amount of data being returned
#[cfg(test)]
#[allow(clippy::type_complexity)]
pub(crate) fn unchecked_all_entries(
storage: &dyn Storage,
) -> Vec<((EpochId, Addr, DealingIndex), PartialContractDealingData)> {
todo!()
// type StorageKey<'a> = (EpochId, Dealer<'a>, DealingIndex);
//
// let empty_prefix: Prefix<StorageKey, ContractDealing, StorageKey> =
// Prefix::with_deserialization_functions(
// Self::NAMESPACE,
// &[],
// &[],
// |_, _, kv| StorageKey::from_vec(kv.0).map(|kt| (kt, ContractSafeBytes(kv.1))),
// |_, _, _| unimplemented!(),
// );
//
// empty_prefix
// .range(storage, None, None, Order::Ascending)
// .collect::<StdResult<_>>()
// .unwrap()
) -> Vec<(
(EpochId, Addr, (DealingIndex, ChunkIndex)),
PartialContractDealingData,
)> {
type StorageKey<'a> = (EpochId, Dealer<'a>, (DealingIndex, ChunkIndex));
let empty_prefix: Prefix<StorageKey, PartialContractDealingData, StorageKey> =
Prefix::with_deserialization_functions(
Self::NAMESPACE,
&[],
&[],
|_, _, kv| StorageKey::from_vec(kv.0).map(|kt| (kt, ContractSafeBytes(kv.1))),
|_, _, _| unimplemented!(),
);
empty_prefix
.range(storage, None, None, Order::Ascending)
.collect::<StdResult<_>>()
.unwrap()
}
}
// #[cfg(test)]
// mod tests {
// use super::*;
// use crate::support::tests::helpers::init_contract;
// use std::collections::HashMap;
//
// fn dealing_data(
// epoch_id: EpochId,
// dealer: Dealer,
// dealing_index: DealingIndex,
// ) -> PartialContractDealingData {
// ContractSafeBytes(
// format!("{epoch_id},{dealer},{dealing_index}")
// .as_bytes()
// .to_vec(),
// )
// }
//
// #[test]
// fn saving_dealing() {
// let mut deps = init_contract();
//
// // make sure to check all combinations of epoch id, dealer address and dealing index to ensure nothing overlaps
// let epochs = [54, 423, 754];
// let dealers = [
// Addr::unchecked("dealer1"),
// Addr::unchecked("dealer2"),
// Addr::unchecked("dealer3"),
// Addr::unchecked("dealer4"),
// Addr::unchecked("dealer5"),
// ];
// let dealing_indices = [0, 1, 2, 3, 4, 5, 6, 7];
//
// for epoch_id in &epochs {
// for dealer in &dealers {
// for dealing_index in &dealing_indices {
// assert!(!StoredDealing::exists(
// &deps.storage,
// *epoch_id,
// dealer,
// *dealing_index
// ));
//
// StoredDealing::save(
// deps.as_mut().storage,
// *epoch_id,
// dealer,
// PartialContractDealing {
// dealing_index: *dealing_index,
// data: dealing_data(*epoch_id, dealer, *dealing_index),
// },
// )
// }
// }
// }
//
// let all: HashMap<_, _> = StoredDealing::unchecked_all_entries(&deps.storage)
// .into_iter()
// .collect();
// assert_eq!(
// all.len(),
// epochs.len() * dealers.len() * dealing_indices.len()
// );
//
// for epoch_id in &epochs {
// for dealer in &dealers {
// for dealing_index in &dealing_indices {
// assert!(StoredDealing::exists(
// &deps.storage,
// *epoch_id,
// dealer,
// *dealing_index
// ));
//
// let content =
// StoredDealing::read(&deps.storage, *epoch_id, dealer, *dealing_index)
// .unwrap();
// let expected = dealing_data(*epoch_id, dealer, *dealing_index);
// assert_eq!(expected, content);
// assert_eq!(
// &expected,
// all.get(&(*epoch_id, dealer.clone(), *dealing_index))
// .unwrap()
// );
// }
// }
// }
// }
//
// #[test]
// fn iterating_over_dealings() {
// let mut deps = init_contract();
//
// let epochs = [54, 423, 754];
// let dealers = [
// Addr::unchecked("dealer1"),
// Addr::unchecked("dealer2"),
// Addr::unchecked("dealer3"),
// Addr::unchecked("dealer4"),
// Addr::unchecked("dealer5"),
// ];
// let dealing_indices = [0, 1, 2, 3, 4, 5, 6, 7];
//
// for epoch_id in &epochs {
// for dealer in &dealers {
// for dealing_index in &dealing_indices {
// StoredDealing::save(
// deps.as_mut().storage,
// *epoch_id,
// dealer,
// PartialContractDealing {
// dealing_index: *dealing_index,
// data: dealing_data(*epoch_id, dealer, *dealing_index),
// },
// )
// }
// }
// }
//
// // remember, we're not testing the iterator implementation
//
// // nothing under epoch 0
// let dealings =
// StoredDealing::prefix_range(&deps.storage, (0, &dealers[0]), None).collect::<Vec<_>>();
// assert!(dealings.is_empty());
//
// // nothing for dealer "foo"
// let foo = Addr::unchecked("foo");
// let dealings =
// StoredDealing::prefix_range(&deps.storage, (epochs[0], &foo), None).collect::<Vec<_>>();
// assert!(dealings.is_empty());
//
// let all = StoredDealing::prefix_range(&deps.storage, (epochs[0], &dealers[0]), None)
// .collect::<Vec<_>>();
// assert_eq!(all.len(), dealing_indices.len());
//
// for (i, dealing) in all.iter().enumerate() {
// let expected = dealing_data(epochs[0], &dealers[0], dealing_indices[i]);
// assert_eq!(expected, dealing.as_ref().unwrap().data);
// assert_eq!(dealing_indices[i], dealing.as_ref().unwrap().dealing_index);
// }
//
// // for sanity sake, check another dealer with different epoch
// let all_other = StoredDealing::prefix_range(&deps.storage, (epochs[2], &dealers[3]), None)
// .collect::<Vec<_>>();
// assert_eq!(all_other.len(), dealing_indices.len());
//
// for (i, dealing) in all_other.iter().enumerate() {
// let expected = dealing_data(epochs[2], &dealers[3], dealing_indices[i]);
// assert_eq!(expected, dealing.as_ref().unwrap().data);
// assert_eq!(dealing_indices[i], dealing.as_ref().unwrap().dealing_index);
// }
//
// let without_first = StoredDealing::prefix_range(
// &deps.storage,
// (epochs[0], &dealers[0]),
// Some(Bound::exclusive(dealing_indices[0])),
// )
// .collect::<Vec<_>>();
// assert_eq!(&all[1..], without_first);
//
// let mid = StoredDealing::prefix_range(
// &deps.storage,
// (epochs[0], &dealers[0]),
// Some(Bound::inclusive(dealing_indices[3])),
// )
// .collect::<Vec<_>>();
// assert_eq!(&all[3..], mid);
// }
// }
#[cfg(test)]
mod tests {
use super::*;
use crate::support::tests::helpers::init_contract;
use std::collections::HashMap;
fn dealing_data(
epoch_id: EpochId,
dealer: Dealer,
dealing_index: DealingIndex,
chunk_index: ChunkIndex,
) -> PartialContractDealingData {
ContractSafeBytes(
format!("{epoch_id},{dealer},{dealing_index},{chunk_index}")
.as_bytes()
.to_vec(),
)
}
#[test]
fn saving_dealing_chunks() {
let mut deps = init_contract();
fn exists_in_storage(
storage: &dyn Storage,
epoch_id: EpochId,
dealer: Dealer,
dealing_index: DealingIndex,
chunk_index: ChunkIndex,
) -> bool {
StoredDealing::storage_key(epoch_id, dealer, dealing_index, chunk_index).has(storage)
}
// make sure to check all combinations of epoch id, dealer address and dealing index to ensure nothing overlaps
let epochs = [54, 423, 754];
let dealers = [
Addr::unchecked("dealer1"),
Addr::unchecked("dealer2"),
Addr::unchecked("dealer3"),
Addr::unchecked("dealer4"),
Addr::unchecked("dealer5"),
];
let dealing_indices = [0, 1, 2, 3, 4, 5, 6, 7];
let chunk_indices = [0, 1, 2, 3, 4];
for epoch_id in &epochs {
for dealer in &dealers {
for dealing_index in &dealing_indices {
for chunk_index in &chunk_indices {
assert!(!exists_in_storage(
&deps.storage,
*epoch_id,
dealer,
*dealing_index,
*chunk_index
));
StoredDealing::save(
deps.as_mut().storage,
*epoch_id,
dealer,
PartialContractDealing {
dealing_index: *dealing_index,
chunk_index: *chunk_index,
data: dealing_data(*epoch_id, dealer, *dealing_index, *chunk_index),
},
)
}
}
}
}
let all: HashMap<_, _> = StoredDealing::unchecked_all_entries(&deps.storage)
.into_iter()
.collect();
assert_eq!(
all.len(),
epochs.len() * dealers.len() * dealing_indices.len() * chunk_indices.len()
);
for epoch_id in &epochs {
for dealer in &dealers {
for dealing_index in &dealing_indices {
for chunk_index in &chunk_indices {
assert!(exists_in_storage(
&deps.storage,
*epoch_id,
dealer,
*dealing_index,
*chunk_index
));
let content = StoredDealing::read(
&deps.storage,
*epoch_id,
dealer,
*dealing_index,
*chunk_index,
)
.unwrap();
let expected =
dealing_data(*epoch_id, dealer, *dealing_index, *chunk_index);
assert_eq!(expected, content);
assert_eq!(
&expected,
all.get(&(*epoch_id, dealer.clone(), (*dealing_index, *chunk_index)))
.unwrap()
);
}
}
}
}
}
#[test]
fn iterating_over_dealing_chunks() {
let mut deps = init_contract();
let epochs = [54, 423, 754];
let dealers = [
Addr::unchecked("dealer1"),
Addr::unchecked("dealer2"),
Addr::unchecked("dealer3"),
Addr::unchecked("dealer4"),
Addr::unchecked("dealer5"),
];
let dealing_indices = [0, 1, 2, 3, 4, 5, 6, 7];
let chunk_indices = [0, 1, 2, 3, 4];
for epoch_id in &epochs {
for dealer in &dealers {
for dealing_index in &dealing_indices {
for chunk_index in &chunk_indices {
StoredDealing::save(
deps.as_mut().storage,
*epoch_id,
dealer,
PartialContractDealing {
dealing_index: *dealing_index,
chunk_index: *chunk_index,
data: dealing_data(*epoch_id, dealer, *dealing_index, *chunk_index),
},
)
}
}
}
}
// remember, we're not testing the iterator implementation
// nothing under epoch 0
let dealings =
StoredDealing::prefix_range(&deps.storage, (0, &dealers[0], dealing_indices[0]), None)
.collect::<Vec<_>>();
assert!(dealings.is_empty());
// nothing for dealer "foo"
let foo = Addr::unchecked("foo");
let dealings =
StoredDealing::prefix_range(&deps.storage, (epochs[0], &foo, dealing_indices[0]), None)
.collect::<Vec<_>>();
assert!(dealings.is_empty());
// nothing for dealing index 99
let dealings =
StoredDealing::prefix_range(&deps.storage, (epochs[0], &dealers[0], 99), None)
.collect::<Vec<_>>();
assert!(dealings.is_empty());
let all = StoredDealing::prefix_range(
&deps.storage,
(epochs[0], &dealers[0], dealing_indices[0]),
None,
)
.collect::<Vec<_>>();
assert_eq!(all.len(), chunk_indices.len());
for (i, dealing) in all.iter().enumerate() {
let expected =
dealing_data(epochs[0], &dealers[0], dealing_indices[0], chunk_indices[i]);
assert_eq!(expected, dealing.as_ref().unwrap().data);
assert_eq!(chunk_indices[i], dealing.as_ref().unwrap().chunk_index);
}
// for sanity sake, check another dealer with different epoch and different dealing index
let all_other = StoredDealing::prefix_range(
&deps.storage,
(epochs[2], &dealers[3], dealing_indices[4]),
None,
)
.collect::<Vec<_>>();
assert_eq!(all_other.len(), chunk_indices.len());
for (i, dealing) in all_other.iter().enumerate() {
let expected =
dealing_data(epochs[2], &dealers[3], dealing_indices[4], chunk_indices[i]);
assert_eq!(expected, dealing.as_ref().unwrap().data);
assert_eq!(chunk_indices[i], dealing.as_ref().unwrap().chunk_index);
}
let without_first = StoredDealing::prefix_range(
&deps.storage,
(epochs[0], &dealers[0], dealing_indices[0]),
Some(Bound::exclusive(chunk_indices[0])),
)
.collect::<Vec<_>>();
assert_eq!(&all[1..], without_first);
let mid = StoredDealing::prefix_range(
&deps.storage,
(epochs[0], &dealers[0], dealing_indices[0]),
Some(Bound::inclusive(chunk_indices[3])),
)
.collect::<Vec<_>>();
assert_eq!(&all[3..], mid);
}
}
@@ -1,4 +1,4 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::dealers::storage as dealers_storage;
@@ -10,9 +10,10 @@ use crate::epoch_state::utils::check_epoch_state;
use crate::error::ContractError;
use crate::state::storage::STATE;
use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response, Storage};
use nym_coconut_dkg_common::types::{
DealingMetadata, EpochState, PartialContractDealing, MAX_DEALING_CHUNKS,
use nym_coconut_dkg_common::dealing::{
DealingChunkInfo, DealingMetadata, PartialContractDealing, MAX_DEALING_CHUNKS,
};
use nym_coconut_dkg_common::types::{ChunkIndex, DealingIndex, EpochState};
// make sure the epoch is in the dealing exchange and the message sender is a valid dealer for this epoch
fn ensure_permission(
@@ -43,7 +44,8 @@ fn ensure_permission(
pub fn try_submit_dealings_metadata(
deps: DepsMut,
info: MessageInfo,
metadata: DealingMetadata,
dealing_index: DealingIndex,
chunks: Vec<DealingChunkInfo>,
resharing: bool,
) -> Result<Response, ContractError> {
ensure_permission(deps.storage, &info.sender, resharing)?;
@@ -52,85 +54,80 @@ pub fn try_submit_dealings_metadata(
let epoch = CURRENT_EPOCH.load(deps.storage)?;
// don't allow overwriting existing metadata
if metadata_exists(
deps.storage,
epoch.epoch_id,
&info.sender,
metadata.dealing_index,
) {
if metadata_exists(deps.storage, epoch.epoch_id, &info.sender, dealing_index) {
return Err(ContractError::MetadataAlreadyExists {
epoch_id: epoch.epoch_id,
dealer: info.sender,
dealing_index: metadata.dealing_index,
});
}
// make sure the metadata is not empty
if metadata.submitted_chunks.is_empty() {
return Err(ContractError::EmptyMetadata {
epoch_id: epoch.epoch_id,
dealer: info.sender,
dealing_index: metadata.dealing_index,
});
}
// make sure the dealing has non-zero size
if metadata.total_size() == 0 {
return Err(ContractError::EmptyMetadata {
epoch_id: epoch.epoch_id,
dealer: info.sender,
dealing_index: metadata.dealing_index,
dealing_index,
});
}
// make sure the dealing index is in the allowed range
// note: dealing indexing starts from 0
if metadata.dealing_index >= state.key_size {
if dealing_index >= state.key_size {
return Err(ContractError::DealingOutOfRange {
epoch_id: epoch.epoch_id,
dealer: info.sender,
index: metadata.dealing_index,
index: dealing_index,
key_size: state.key_size,
});
}
let chunks = metadata.submitted_chunks.len();
// make sure the metadata is not empty
if chunks.is_empty() {
return Err(ContractError::EmptyMetadata {
epoch_id: epoch.epoch_id,
dealer: info.sender,
dealing_index,
});
}
// make sure the chunks are non empty
if chunks.iter().any(|c| c.size == 0) {
return Err(ContractError::EmptyMetadata {
epoch_id: epoch.epoch_id,
dealer: info.sender,
dealing_index,
});
}
// make sure the number of dealing chunks is in the allowed range
// to prevent somebody splitting their dealings into 10B chunks
if chunks > MAX_DEALING_CHUNKS {
if chunks.len() > MAX_DEALING_CHUNKS {
return Err(ContractError::TooFragmentedMetadata {
epoch_id: epoch.epoch_id,
dealer: info.sender,
dealing_index: metadata.dealing_index,
chunks,
dealing_index,
chunks: chunks.len(),
});
}
// make sure all chunks, but the last one, have the same size
// SAFETY: we checked for whether `metadata.submitted_chunks` is empty and returned an error in that case
// SAFETY: we checked for whether `chunks` is empty and returned an error in that case
#[allow(clippy::unwrap_used)]
let first_chunk_size = metadata.submitted_chunks.first_key_value().unwrap().1.size;
let first_chunk_size = chunks.first().unwrap().size;
for (&chunk_index, chunk_info) in metadata.submitted_chunks.iter().take(chunks - 1) {
for (chunk_index, chunk_info) in chunks.iter().enumerate().take(chunks.len() - 1) {
if chunk_info.size != first_chunk_size {
return Err(ContractError::UnevenChunkSplit {
epoch_id: epoch.epoch_id,
dealer: info.sender,
dealing_index: metadata.dealing_index,
chunk_index,
dealing_index,
chunk_index: chunk_index as ChunkIndex,
first_chunk_size,
size: chunk_info.size,
});
}
}
// finally, store the metadata
// finally, construct and store the metadata
let metadata = DealingMetadata::new(dealing_index, chunks);
store_metadata(
deps.storage,
epoch.epoch_id,
&info.sender,
metadata.dealing_index,
dealing_index,
&metadata,
)?;
@@ -141,7 +138,7 @@ pub fn try_commit_dealings_chunk(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
dealing: PartialContractDealing,
chunk: PartialContractDealing,
resharing: bool,
) -> Result<Response, ContractError> {
ensure_permission(deps.storage, &info.sender, resharing)?;
@@ -153,16 +150,16 @@ pub fn try_commit_dealings_chunk(
deps.storage,
epoch.epoch_id,
&info.sender,
dealing.dealing_index,
chunk.dealing_index,
)?;
// check if the received chunk is within the declared range
let Some(submission_status) = metadata.submitted_chunks.get_mut(&dealing.chunk_index) else {
let Some(submission_status) = metadata.submitted_chunks.get_mut(&chunk.chunk_index) else {
return Err(ContractError::DealingChunkNotInMetadata {
epoch_id: epoch.epoch_id,
dealer: info.sender,
dealing_index: dealing.dealing_index,
chunk_index: dealing.chunk_index,
dealing_index: chunk.dealing_index,
chunk_index: chunk.chunk_index,
});
};
@@ -171,8 +168,8 @@ pub fn try_commit_dealings_chunk(
return Err(ContractError::DealingChunkAlreadyCommitted {
epoch_id: epoch.epoch_id,
dealer: info.sender,
dealing_index: dealing.dealing_index,
chunk_index: dealing.chunk_index,
dealing_index: chunk.dealing_index,
chunk_index: chunk.chunk_index,
block_height: submission_height,
});
}
@@ -183,12 +180,12 @@ pub fn try_commit_dealings_chunk(
deps.storage,
epoch.epoch_id,
&info.sender,
dealing.dealing_index,
chunk.dealing_index,
&metadata,
)?;
// store the dealing
StoredDealing::save(deps.storage, epoch.epoch_id, &info.sender, dealing);
StoredDealing::save(deps.storage, epoch.epoch_id, &info.sender, chunk);
Ok(Response::new())
}
@@ -205,7 +202,7 @@ pub(crate) mod tests {
use cosmwasm_std::Addr;
use nym_coconut_dkg_common::dealer::DealerDetails;
use nym_coconut_dkg_common::types::{
ContractSafeBytes, InitialReplacementData, TimeConfiguration, DEFAULT_DEALINGS,
ContractSafeBytes, InitialReplacementData, TimeConfiguration,
};
#[test]
+2 -1
View File
@@ -3,7 +3,8 @@
use cosmwasm_std::{Addr, StdError};
use cw_controllers::AdminError;
use nym_coconut_dkg_common::types::{ChunkIndex, DealingIndex, EpochId, MAX_DEALING_CHUNKS};
use nym_coconut_dkg_common::dealing::MAX_DEALING_CHUNKS;
use nym_coconut_dkg_common::types::{ChunkIndex, DealingIndex, EpochId};
use thiserror::Error;
/// Custom errors for contract failure conditions.
@@ -1,9 +1,10 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::Addr;
use nym_coconut_dkg_common::dealer::DealerDetails;
use nym_coconut_dkg_common::types::{ContractSafeBytes, PartialContractDealing};
use nym_coconut_dkg_common::dealing::PartialContractDealing;
use nym_coconut_dkg_common::types::ContractSafeBytes;
use nym_coconut_dkg_common::verification_key::ContractVKShare;
pub const TEST_MIX_DENOM: &str = "unym";
@@ -24,11 +25,11 @@ pub fn dealing_bytes_fixture() -> ContractSafeBytes {
}
pub fn partial_dealing_fixture() -> PartialContractDealing {
todo!()
// PartialContractDealing {
// dealing_index: 0,
// data: ContractSafeBytes(vec![1, 2, 3]),
// }
PartialContractDealing {
chunk_index: 0,
dealing_index: 0,
data: ContractSafeBytes(vec![1, 2, 3]),
}
}
pub fn dealer_details_fixture(assigned_index: u64) -> DealerDetails {
@@ -9,8 +9,9 @@ use cosmwasm_std::{
QuerierResult, SystemResult, WasmQuery,
};
use cw4::{Cw4QueryMsg, Member, MemberListResponse, MemberResponse};
use nym_coconut_dkg_common::dealing::DEFAULT_DEALINGS;
use nym_coconut_dkg_common::msg::InstantiateMsg;
use nym_coconut_dkg_common::types::{DealerDetails, DEFAULT_DEALINGS};
use nym_coconut_dkg_common::types::DealerDetails;
use std::sync::Mutex;
use super::fixtures::TEST_MIX_DENOM;
@@ -8,7 +8,7 @@ use crate::epoch_state::utils::check_epoch_state;
use crate::error::ContractError;
use crate::state::storage::{MULTISIG, STATE};
use crate::verification_key_shares::storage::vk_shares;
use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response};
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response};
use nym_coconut_dkg_common::types::EpochState;
use nym_coconut_dkg_common::verification_key::{
to_cosmos_msg, ContractVKShare, VerificationKeyShare,
@@ -66,9 +66,11 @@ pub fn try_commit_verification_key_share(
pub fn try_verify_verification_key_share(
deps: DepsMut<'_>,
info: MessageInfo,
owner: Addr,
owner: String,
resharing: bool,
) -> Result<Response, ContractError> {
let owner = deps.api.addr_validate(&owner)?;
check_epoch_state(
deps.storage,
EpochState::VerificationKeyFinalization { resharing },
@@ -96,6 +98,7 @@ mod tests {
use crate::support::tests::helpers;
use crate::support::tests::helpers::{add_fixture_dealer, ADMIN_ADDRESS, MULTISIG_CONTRACT};
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::Addr;
use cw_controllers::AdminError;
use nym_coconut_dkg_common::dealer::DealerDetails;
use nym_coconut_dkg_common::types::{EpochState, TimeConfiguration};
@@ -233,7 +236,7 @@ mod tests {
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
let info = mock_info("requester", &[]);
let owner = Addr::unchecked("owner");
let owner = "owner".to_string();
let multisig_info = mock_info(MULTISIG_CONTRACT, &[]);
let ret =
@@ -291,7 +294,7 @@ mod tests {
let mut env = mock_env();
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
let owner = Addr::unchecked("owner");
let owner = "owner".to_string();
let info = mock_info(owner.as_ref(), &[]);
let share = "share".to_string();
let multisig_info = mock_info(MULTISIG_CONTRACT, &[]);
@@ -309,14 +312,18 @@ mod tests {
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
let dealer_details = DealerDetails {
address: owner.clone(),
address: Addr::unchecked(&owner),
bte_public_key_with_proof: String::new(),
ed25519_identity: String::new(),
announce_address: String::new(),
assigned_index: 1,
};
dealers_storage::current_dealers()
.save(deps.as_mut().storage, &owner, &dealer_details)
.save(
deps.as_mut().storage,
&Addr::unchecked(&owner),
&dealer_details,
)
.unwrap();
try_commit_verification_key_share(deps.as_mut(), env.clone(), info, share, false).unwrap();