dealing metadata storage logic
This commit is contained in:
@@ -2,8 +2,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::types::{
|
||||
ContractDealing, DealingIndex, EncodedBTEPublicKeyWithProof, EpochId, NodeIndex,
|
||||
PartialContractDealing,
|
||||
DealingIndex, EncodedBTEPublicKeyWithProof, EpochId, NodeIndex, PartialContractDealing,
|
||||
PartialContractDealingData,
|
||||
};
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::Addr;
|
||||
@@ -77,7 +77,7 @@ pub struct DealingResponse {
|
||||
|
||||
pub dealing_index: DealingIndex,
|
||||
|
||||
pub dealing: Option<ContractDealing>,
|
||||
pub dealing: Option<PartialContractDealingData>,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
|
||||
@@ -15,32 +16,108 @@ pub type EncodedBTEPublicKeyWithProofRef<'a> = &'a str;
|
||||
pub type NodeIndex = u64;
|
||||
pub type EpochId = u64;
|
||||
pub type DealingIndex = u32;
|
||||
pub type ContractDealing = ContractSafeBytes;
|
||||
// we really don't need to hold more data than that (even u8 would have been enough),
|
||||
// but explicitly make it different type than `DealingIndex` so type system would detect any
|
||||
// accidental misuses
|
||||
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 PartialContractDealing {
|
||||
pub index: DealingIndex,
|
||||
pub data: ContractDealing,
|
||||
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 PartialContractDealing {
|
||||
pub fn new(index: DealingIndex, data: ContractDealing) -> Self {
|
||||
PartialContractDealing { index, data }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(DealingIndex, ContractDealing)> for PartialContractDealing {
|
||||
fn from(value: (DealingIndex, ContractDealing)) -> Self {
|
||||
PartialContractDealing {
|
||||
index: value.0,
|
||||
data: value.1,
|
||||
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>,
|
||||
@@ -277,3 +354,37 @@ impl EpochState {
|
||||
matches!(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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ use std::ops::Deref;
|
||||
// currently set to 128 bytes
|
||||
pub const MAX_DISPLAY_SIZE: usize = 128;
|
||||
|
||||
// TODO: if we are to use this for different types, it might make sense to introduce something like
|
||||
// CommitmentTypeId field on the below for distinguishing different ones. it would somehow become part of the trait
|
||||
// helps to transfer bytes between contract boundary to decrease amount of data sent accross
|
||||
// after it's put to `Binary`
|
||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, JsonSchema)]
|
||||
pub struct ContractSafeBytes(pub Vec<u8>);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::dealers::queries::{
|
||||
};
|
||||
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;
|
||||
use crate::dealings::transactions::try_commit_dealings_chunk;
|
||||
use crate::epoch_state::queries::{
|
||||
query_current_epoch, query_current_epoch_threshold, query_initial_dealers,
|
||||
};
|
||||
@@ -101,7 +101,7 @@ pub fn execute(
|
||||
resharing,
|
||||
),
|
||||
ExecuteMsg::CommitDealing { dealing, resharing } => {
|
||||
try_commit_dealings(deps, info, dealing, resharing)
|
||||
try_commit_dealings_chunk(deps, info, dealing, resharing)
|
||||
}
|
||||
ExecuteMsg::CommitVerificationKeyShare { share, resharing } => {
|
||||
try_commit_verification_key_share(deps, env, info, share, resharing)
|
||||
|
||||
@@ -35,15 +35,16 @@ pub fn query_dealing(
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
) -> StdResult<DealingResponse> {
|
||||
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,
|
||||
})
|
||||
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,
|
||||
// })
|
||||
}
|
||||
|
||||
pub fn query_dealings_paged(
|
||||
@@ -53,254 +54,255 @@ pub fn query_dealings_paged(
|
||||
start_after: Option<DealingIndex>,
|
||||
limit: Option<u32>,
|
||||
) -> StdResult<PagedDealingsResponse> {
|
||||
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.index);
|
||||
|
||||
Ok(PagedDealingsResponse::new(
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealings,
|
||||
start_next_after,
|
||||
))
|
||||
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,
|
||||
// ))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::dealings::storage::{DEALINGS_PAGE_DEFAULT_LIMIT, DEALINGS_PAGE_MAX_LIMIT};
|
||||
use crate::support::tests::fixtures::{dealing_bytes_fixture, partial_dealing_fixture};
|
||||
use crate::support::tests::helpers::init_contract;
|
||||
use cosmwasm_std::{Addr, DepsMut};
|
||||
use nym_coconut_dkg_common::types::PartialContractDealing;
|
||||
|
||||
fn fill_dealings(deps: DepsMut<'_>, epoch: EpochId, dealers: usize, key_size: u32) {
|
||||
for i in 0..dealers {
|
||||
let dealer = Addr::unchecked(format!("dealer{i}"));
|
||||
for dealing_index in 0..key_size {
|
||||
StoredDealing::save(
|
||||
deps.storage,
|
||||
epoch,
|
||||
&dealer,
|
||||
PartialContractDealing {
|
||||
index: dealing_index,
|
||||
data: dealing_bytes_fixture(),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_query_dealing() {
|
||||
let mut deps = init_contract();
|
||||
|
||||
let bad_address = "FOOMP".to_string();
|
||||
assert!(query_dealing(deps.as_ref(), 0, bad_address, 0).is_err());
|
||||
|
||||
let empty = query_dealing(deps.as_ref(), 0, "foo".to_string(), 0).unwrap();
|
||||
assert_eq!(empty.epoch_id, 0);
|
||||
assert_eq!(empty.dealing_index, 0);
|
||||
assert_eq!(empty.dealer, Addr::unchecked("foo"));
|
||||
assert!(empty.dealing.is_none());
|
||||
|
||||
// insert the dealing
|
||||
let dealing = partial_dealing_fixture();
|
||||
StoredDealing::save(
|
||||
deps.as_mut().storage,
|
||||
0,
|
||||
&Addr::unchecked("foo"),
|
||||
dealing.clone(),
|
||||
);
|
||||
|
||||
let retrieved = query_dealing(deps.as_ref(), 0, "foo".to_string(), 0).unwrap();
|
||||
assert_eq!(retrieved.epoch_id, 0);
|
||||
assert_eq!(retrieved.dealing_index, dealing.index);
|
||||
assert_eq!(retrieved.dealer, Addr::unchecked("foo"));
|
||||
assert_eq!(retrieved.dealing.unwrap(), dealing.data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_query_dealing_status() {
|
||||
let mut deps = init_contract();
|
||||
|
||||
let bad_address = "FOOMP".to_string();
|
||||
assert!(query_dealing_status(deps.as_ref(), 0, bad_address, 0).is_err());
|
||||
|
||||
let empty = query_dealing_status(deps.as_ref(), 0, "foo".to_string(), 0).unwrap();
|
||||
assert_eq!(empty.epoch_id, 0);
|
||||
assert_eq!(empty.dealing_index, 0);
|
||||
assert_eq!(empty.dealer, Addr::unchecked("foo"));
|
||||
assert!(!empty.dealing_submitted);
|
||||
|
||||
// insert the dealing
|
||||
let dealing = partial_dealing_fixture();
|
||||
StoredDealing::save(
|
||||
deps.as_mut().storage,
|
||||
0,
|
||||
&Addr::unchecked("foo"),
|
||||
dealing.clone(),
|
||||
);
|
||||
|
||||
let retrieved = query_dealing_status(deps.as_ref(), 0, "foo".to_string(), 0).unwrap();
|
||||
assert_eq!(retrieved.epoch_id, 0);
|
||||
assert_eq!(retrieved.dealing_index, dealing.index);
|
||||
assert_eq!(retrieved.dealer, Addr::unchecked("foo"));
|
||||
assert!(retrieved.dealing_submitted)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod query_dealings {
|
||||
use super::*;
|
||||
use nym_coconut_dkg_common::types::DEFAULT_DEALINGS;
|
||||
|
||||
#[test]
|
||||
fn dealings_empty_on_init() {
|
||||
let deps = init_contract();
|
||||
let all_dealings = StoredDealing::unchecked_all_entries(&deps.storage);
|
||||
assert!(all_dealings.is_empty())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dealings_paged_retrieval_obeys_limits() {
|
||||
let mut deps = init_contract();
|
||||
let limit = 2;
|
||||
fill_dealings(deps.as_mut(), 0, 10, DEFAULT_DEALINGS as u32);
|
||||
|
||||
for dealer in 0..10 {
|
||||
let dealer = format!("dealer{dealer}");
|
||||
let page1 =
|
||||
query_dealings_paged(deps.as_ref(), 0, dealer, None, Option::from(limit))
|
||||
.unwrap();
|
||||
assert_eq!(limit, page1.dealings.len() as u32);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dealings_paged_retrieval_has_default_limit() {
|
||||
let mut deps = init_contract();
|
||||
fill_dealings(deps.as_mut(), 0, 10, DEFAULT_DEALINGS as u32);
|
||||
|
||||
for dealer in 0..10 {
|
||||
let dealer = format!("dealer{dealer}");
|
||||
// query without explicitly setting a limit
|
||||
let page1 = query_dealings_paged(deps.as_ref(), 0, dealer, None, None).unwrap();
|
||||
|
||||
assert_eq!(DEALINGS_PAGE_DEFAULT_LIMIT, page1.dealings.len() as u32);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dealings_paged_retrieval_has_max_limit() {
|
||||
let mut deps = init_contract();
|
||||
fill_dealings(deps.as_mut(), 0, 10, DEFAULT_DEALINGS as u32);
|
||||
|
||||
// query with a crazily high limit in an attempt to use too many resources
|
||||
let crazy_limit = 1000 * DEALINGS_PAGE_MAX_LIMIT;
|
||||
for dealer in 0..10 {
|
||||
let dealer = format!("dealer{dealer}");
|
||||
let page1 =
|
||||
query_dealings_paged(deps.as_ref(), 0, dealer, None, Option::from(crazy_limit))
|
||||
.unwrap();
|
||||
|
||||
// we default to a decent sized upper bound instead
|
||||
let expected_limit = DEALINGS_PAGE_MAX_LIMIT;
|
||||
assert_eq!(expected_limit, page1.dealings.len() as u32);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dealings_pagination_works() {
|
||||
let mut deps = init_contract();
|
||||
|
||||
fill_dealings(deps.as_mut(), 0, 10, 1);
|
||||
let per_page = 2;
|
||||
|
||||
for dealer in 0..10 {
|
||||
let dealer = format!("dealer{dealer}");
|
||||
let page1 =
|
||||
query_dealings_paged(deps.as_ref(), 0, dealer, None, Option::from(per_page))
|
||||
.unwrap();
|
||||
|
||||
// page should have 1 result on it
|
||||
assert_eq!(1, page1.dealings.len());
|
||||
}
|
||||
|
||||
// save another
|
||||
fill_dealings(deps.as_mut(), 1, 10, 2);
|
||||
|
||||
for dealer in 0..10 {
|
||||
let dealer = format!("dealer{dealer}");
|
||||
// page1 should have 2 results on it
|
||||
let page1 =
|
||||
query_dealings_paged(deps.as_ref(), 1, dealer, None, Option::from(per_page))
|
||||
.unwrap();
|
||||
assert_eq!(2, page1.dealings.len());
|
||||
}
|
||||
|
||||
fill_dealings(deps.as_mut(), 3, 10, 3);
|
||||
|
||||
for dealer in 0..10 {
|
||||
let dealer = format!("dealer{dealer}");
|
||||
// page1 still has 2 results
|
||||
let page1 = query_dealings_paged(
|
||||
deps.as_ref(),
|
||||
3,
|
||||
dealer.clone(),
|
||||
None,
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(2, page1.dealings.len());
|
||||
|
||||
// retrieving the next page should start after the last key on this page
|
||||
let start_after = page1.start_next_after.unwrap();
|
||||
let page2 = query_dealings_paged(
|
||||
deps.as_ref(),
|
||||
3,
|
||||
dealer,
|
||||
Option::from(start_after),
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(1, page2.dealings.len());
|
||||
}
|
||||
|
||||
fill_dealings(deps.as_mut(), 4, 10, 4);
|
||||
|
||||
for dealer in 0..10 {
|
||||
let dealer = format!("dealer{dealer}");
|
||||
let page1 = query_dealings_paged(
|
||||
deps.as_ref(),
|
||||
4,
|
||||
dealer.clone(),
|
||||
None,
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
let start_after = page1.start_next_after.unwrap();
|
||||
let page2 = query_dealings_paged(
|
||||
deps.as_ref(),
|
||||
4,
|
||||
dealer,
|
||||
Option::from(start_after),
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// now we have 2 pages, with 2 results on the second page
|
||||
assert_eq!(2, page2.dealings.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// #[cfg(test)]
|
||||
// pub(crate) mod tests {
|
||||
// use super::*;
|
||||
// use crate::dealings::storage::{DEALINGS_PAGE_DEFAULT_LIMIT, DEALINGS_PAGE_MAX_LIMIT};
|
||||
// use crate::support::tests::fixtures::{dealing_bytes_fixture, partial_dealing_fixture};
|
||||
// use crate::support::tests::helpers::init_contract;
|
||||
// use cosmwasm_std::{Addr, DepsMut};
|
||||
// use nym_coconut_dkg_common::types::PartialContractDealing;
|
||||
//
|
||||
// fn fill_dealings(deps: DepsMut<'_>, epoch: EpochId, dealers: usize, key_size: u32) {
|
||||
// for i in 0..dealers {
|
||||
// let dealer = Addr::unchecked(format!("dealer{i}"));
|
||||
// for dealing_index in 0..key_size {
|
||||
// StoredDealing::save(
|
||||
// deps.storage,
|
||||
// epoch,
|
||||
// &dealer,
|
||||
// PartialContractDealing {
|
||||
// dealing_index: dealing_index,
|
||||
// data: dealing_bytes_fixture(),
|
||||
// },
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn test_query_dealing() {
|
||||
// let mut deps = init_contract();
|
||||
//
|
||||
// let bad_address = "FOOMP".to_string();
|
||||
// assert!(query_dealing(deps.as_ref(), 0, bad_address, 0).is_err());
|
||||
//
|
||||
// let empty = query_dealing(deps.as_ref(), 0, "foo".to_string(), 0).unwrap();
|
||||
// assert_eq!(empty.epoch_id, 0);
|
||||
// assert_eq!(empty.dealing_index, 0);
|
||||
// assert_eq!(empty.dealer, Addr::unchecked("foo"));
|
||||
// assert!(empty.dealing.is_none());
|
||||
//
|
||||
// // insert the dealing
|
||||
// let dealing = partial_dealing_fixture();
|
||||
// StoredDealing::save(
|
||||
// deps.as_mut().storage,
|
||||
// 0,
|
||||
// &Addr::unchecked("foo"),
|
||||
// dealing.clone(),
|
||||
// );
|
||||
//
|
||||
// let retrieved = query_dealing(deps.as_ref(), 0, "foo".to_string(), 0).unwrap();
|
||||
// assert_eq!(retrieved.epoch_id, 0);
|
||||
// assert_eq!(retrieved.dealing_index, dealing.dealing_index);
|
||||
// assert_eq!(retrieved.dealer, Addr::unchecked("foo"));
|
||||
// assert_eq!(retrieved.dealing.unwrap(), dealing.data);
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn test_query_dealing_status() {
|
||||
// let mut deps = init_contract();
|
||||
//
|
||||
// let bad_address = "FOOMP".to_string();
|
||||
// assert!(query_dealing_status(deps.as_ref(), 0, bad_address, 0).is_err());
|
||||
//
|
||||
// let empty = query_dealing_status(deps.as_ref(), 0, "foo".to_string(), 0).unwrap();
|
||||
// assert_eq!(empty.epoch_id, 0);
|
||||
// assert_eq!(empty.dealing_index, 0);
|
||||
// assert_eq!(empty.dealer, Addr::unchecked("foo"));
|
||||
// assert!(!empty.dealing_submitted);
|
||||
//
|
||||
// // insert the dealing
|
||||
// let dealing = partial_dealing_fixture();
|
||||
// StoredDealing::save(
|
||||
// deps.as_mut().storage,
|
||||
// 0,
|
||||
// &Addr::unchecked("foo"),
|
||||
// dealing.clone(),
|
||||
// );
|
||||
//
|
||||
// let retrieved = query_dealing_status(deps.as_ref(), 0, "foo".to_string(), 0).unwrap();
|
||||
// assert_eq!(retrieved.epoch_id, 0);
|
||||
// assert_eq!(retrieved.dealing_index, dealing.dealing_index);
|
||||
// assert_eq!(retrieved.dealer, Addr::unchecked("foo"));
|
||||
// assert!(retrieved.dealing_submitted)
|
||||
// }
|
||||
//
|
||||
// #[cfg(test)]
|
||||
// mod query_dealings {
|
||||
// use super::*;
|
||||
// use nym_coconut_dkg_common::types::DEFAULT_DEALINGS;
|
||||
//
|
||||
// #[test]
|
||||
// fn dealings_empty_on_init() {
|
||||
// let deps = init_contract();
|
||||
// let all_dealings = StoredDealing::unchecked_all_entries(&deps.storage);
|
||||
// assert!(all_dealings.is_empty())
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn dealings_paged_retrieval_obeys_limits() {
|
||||
// let mut deps = init_contract();
|
||||
// let limit = 2;
|
||||
// fill_dealings(deps.as_mut(), 0, 10, DEFAULT_DEALINGS as u32);
|
||||
//
|
||||
// for dealer in 0..10 {
|
||||
// let dealer = format!("dealer{dealer}");
|
||||
// let page1 =
|
||||
// query_dealings_paged(deps.as_ref(), 0, dealer, None, Option::from(limit))
|
||||
// .unwrap();
|
||||
// assert_eq!(limit, page1.dealings.len() as u32);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn dealings_paged_retrieval_has_default_limit() {
|
||||
// let mut deps = init_contract();
|
||||
// fill_dealings(deps.as_mut(), 0, 10, DEFAULT_DEALINGS as u32);
|
||||
//
|
||||
// for dealer in 0..10 {
|
||||
// let dealer = format!("dealer{dealer}");
|
||||
// // query without explicitly setting a limit
|
||||
// let page1 = query_dealings_paged(deps.as_ref(), 0, dealer, None, None).unwrap();
|
||||
//
|
||||
// assert_eq!(DEALINGS_PAGE_DEFAULT_LIMIT, page1.dealings.len() as u32);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn dealings_paged_retrieval_has_max_limit() {
|
||||
// let mut deps = init_contract();
|
||||
// fill_dealings(deps.as_mut(), 0, 10, DEFAULT_DEALINGS as u32);
|
||||
//
|
||||
// // query with a crazily high limit in an attempt to use too many resources
|
||||
// let crazy_limit = 1000 * DEALINGS_PAGE_MAX_LIMIT;
|
||||
// for dealer in 0..10 {
|
||||
// let dealer = format!("dealer{dealer}");
|
||||
// let page1 =
|
||||
// query_dealings_paged(deps.as_ref(), 0, dealer, None, Option::from(crazy_limit))
|
||||
// .unwrap();
|
||||
//
|
||||
// // we default to a decent sized upper bound instead
|
||||
// let expected_limit = DEALINGS_PAGE_MAX_LIMIT;
|
||||
// assert_eq!(expected_limit, page1.dealings.len() as u32);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn dealings_pagination_works() {
|
||||
// let mut deps = init_contract();
|
||||
//
|
||||
// fill_dealings(deps.as_mut(), 0, 10, 1);
|
||||
// let per_page = 2;
|
||||
//
|
||||
// for dealer in 0..10 {
|
||||
// let dealer = format!("dealer{dealer}");
|
||||
// let page1 =
|
||||
// query_dealings_paged(deps.as_ref(), 0, dealer, None, Option::from(per_page))
|
||||
// .unwrap();
|
||||
//
|
||||
// // page should have 1 result on it
|
||||
// assert_eq!(1, page1.dealings.len());
|
||||
// }
|
||||
//
|
||||
// // save another
|
||||
// fill_dealings(deps.as_mut(), 1, 10, 2);
|
||||
//
|
||||
// for dealer in 0..10 {
|
||||
// let dealer = format!("dealer{dealer}");
|
||||
// // page1 should have 2 results on it
|
||||
// let page1 =
|
||||
// query_dealings_paged(deps.as_ref(), 1, dealer, None, Option::from(per_page))
|
||||
// .unwrap();
|
||||
// assert_eq!(2, page1.dealings.len());
|
||||
// }
|
||||
//
|
||||
// fill_dealings(deps.as_mut(), 3, 10, 3);
|
||||
//
|
||||
// for dealer in 0..10 {
|
||||
// let dealer = format!("dealer{dealer}");
|
||||
// // page1 still has 2 results
|
||||
// let page1 = query_dealings_paged(
|
||||
// deps.as_ref(),
|
||||
// 3,
|
||||
// dealer.clone(),
|
||||
// None,
|
||||
// Option::from(per_page),
|
||||
// )
|
||||
// .unwrap();
|
||||
// assert_eq!(2, page1.dealings.len());
|
||||
//
|
||||
// // retrieving the next page should start after the last key on this page
|
||||
// let start_after = page1.start_next_after.unwrap();
|
||||
// let page2 = query_dealings_paged(
|
||||
// deps.as_ref(),
|
||||
// 3,
|
||||
// dealer,
|
||||
// Option::from(start_after),
|
||||
// Option::from(per_page),
|
||||
// )
|
||||
// .unwrap();
|
||||
//
|
||||
// assert_eq!(1, page2.dealings.len());
|
||||
// }
|
||||
//
|
||||
// fill_dealings(deps.as_mut(), 4, 10, 4);
|
||||
//
|
||||
// for dealer in 0..10 {
|
||||
// let dealer = format!("dealer{dealer}");
|
||||
// let page1 = query_dealings_paged(
|
||||
// deps.as_ref(),
|
||||
// 4,
|
||||
// dealer.clone(),
|
||||
// None,
|
||||
// Option::from(per_page),
|
||||
// )
|
||||
// .unwrap();
|
||||
// let start_after = page1.start_next_after.unwrap();
|
||||
// let page2 = query_dealings_paged(
|
||||
// deps.as_ref(),
|
||||
// 4,
|
||||
// dealer,
|
||||
// Option::from(start_after),
|
||||
// Option::from(per_page),
|
||||
// )
|
||||
// .unwrap();
|
||||
//
|
||||
// // now we have 2 pages, with 2 results on the second page
|
||||
// assert_eq!(2, page2.dealings.len());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
// Copyright 2022 - 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, Path, Prefix, Prefixer, PrimaryKey};
|
||||
use cw_storage_plus::{Bound, Key, KeyDeserialize, Map, Path, Prefix, Prefixer, PrimaryKey};
|
||||
use nym_coconut_dkg_common::types::{
|
||||
ContractDealing, ContractSafeBytes, DealingIndex, EpochId, PartialContractDealing,
|
||||
ChunkIndex, ContractSafeBytes, DealingIndex, DealingMetadata, EpochId, PartialContractDealing,
|
||||
PartialContractDealingData,
|
||||
};
|
||||
|
||||
pub(crate) const DEALINGS_PAGE_MAX_LIMIT: u32 = 2;
|
||||
@@ -12,54 +14,105 @@ 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> =
|
||||
Map::new("dealings_metadata");
|
||||
|
||||
pub(crate) fn metadata_exists(
|
||||
storage: &dyn Storage,
|
||||
epoch_id: EpochId,
|
||||
dealer: Dealer,
|
||||
dealing_index: DealingIndex,
|
||||
) -> bool {
|
||||
DEALINGS_METADATA.has(storage, (epoch_id, dealer, dealing_index))
|
||||
}
|
||||
|
||||
pub(crate) fn may_read_metadata(
|
||||
storage: &dyn Storage,
|
||||
epoch_id: EpochId,
|
||||
dealer: Dealer,
|
||||
dealing_index: DealingIndex,
|
||||
) -> Result<Option<DealingMetadata>, ContractError> {
|
||||
Ok(DEALINGS_METADATA.may_load(storage, (epoch_id, dealer, dealing_index))?)
|
||||
}
|
||||
|
||||
pub(crate) fn must_read_metadata(
|
||||
storage: &dyn Storage,
|
||||
epoch_id: EpochId,
|
||||
dealer: Dealer,
|
||||
dealing_index: DealingIndex,
|
||||
) -> Result<DealingMetadata, ContractError> {
|
||||
DEALINGS_METADATA
|
||||
.may_load(storage, (epoch_id, dealer, dealing_index))?
|
||||
.ok_or_else(|| ContractError::UnavailableDealingMetadata {
|
||||
epoch_id,
|
||||
dealer: dealer.to_owned(),
|
||||
dealing_index,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn store_metadata(
|
||||
storage: &mut dyn Storage,
|
||||
epoch_id: EpochId,
|
||||
dealer: Dealer,
|
||||
dealing_index: DealingIndex,
|
||||
metadata: &DealingMetadata,
|
||||
) -> Result<(), ContractError> {
|
||||
Ok(DEALINGS_METADATA.save(storage, (epoch_id, dealer, dealing_index), metadata)?)
|
||||
}
|
||||
|
||||
// dealings are stored in a multilevel map with the following hierarchy:
|
||||
// - epoch-id:
|
||||
// - issuer-address:
|
||||
// - dealing id:
|
||||
// - dealing content
|
||||
// - chunk_id:
|
||||
// - 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;
|
||||
|
||||
// part of `StoredDealing` to make existence lookup cheaper
|
||||
// TODO: do it later since we need to chunk the dealings anyway
|
||||
// pub(crate) struct UNIMPLEMENTED_DealingLookup;
|
||||
|
||||
impl StoredDealing {
|
||||
const NAMESPACE: &'static [u8] = b"dealing";
|
||||
|
||||
fn deserialize_dealing_record(kv: Record) -> StdResult<(DealingIndex, ContractDealing)> {
|
||||
let (k, v) = kv;
|
||||
let index = <DealingIndex as KeyDeserialize>::from_vec(k)?;
|
||||
let data = ContractSafeBytes(v);
|
||||
|
||||
Ok((index, data))
|
||||
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))
|
||||
}
|
||||
|
||||
fn storage_key(
|
||||
epoch_id: EpochId,
|
||||
dealer: Dealer,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
) -> Path<Vec<u8>> {
|
||||
// just replicate the behaviour from `Map::key`
|
||||
let key = (epoch_id, dealer, dealing_index);
|
||||
// note: `PrimaryKey` trait is not implemented for tuple (T, U, V, W), only for up to (T, U, V)
|
||||
// that's why we create a (T, U, (V, W)) tuple(s) instead
|
||||
let key = (epoch_id, dealer, (dealing_index, chunk_index));
|
||||
Path::new(
|
||||
Self::NAMESPACE,
|
||||
&key.key().iter().map(Key::as_ref).collect::<Vec<_>>(),
|
||||
)
|
||||
}
|
||||
|
||||
fn prefix(prefix: (EpochId, Dealer)) -> Prefix<DealingIndex, ContractSafeBytes, DealingIndex> {
|
||||
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)) -> 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"),
|
||||
// // )
|
||||
// }
|
||||
|
||||
pub(crate) fn exists(
|
||||
storage: &dyn Storage,
|
||||
@@ -67,7 +120,8 @@ impl StoredDealing {
|
||||
dealer: &Addr,
|
||||
dealing_index: DealingIndex,
|
||||
) -> bool {
|
||||
StoredDealing::storage_key(epoch_id, dealer, dealing_index).has(storage)
|
||||
todo!()
|
||||
// StoredDealing::storage_key(epoch_id, dealer, dealing_index).has(storage)
|
||||
}
|
||||
|
||||
pub(crate) fn save(
|
||||
@@ -77,7 +131,8 @@ impl StoredDealing {
|
||||
dealing: PartialContractDealing,
|
||||
) {
|
||||
// NOTE: we're storing bytes directly here!
|
||||
let storage_key = StoredDealing::storage_key(epoch_id, dealer, dealing.index);
|
||||
let storage_key =
|
||||
Self::storage_key(epoch_id, dealer, dealing.dealing_index, dealing.chunk_index);
|
||||
storage.set(&storage_key, dealing.data.as_slice());
|
||||
}
|
||||
|
||||
@@ -86,212 +141,215 @@ impl StoredDealing {
|
||||
epoch_id: EpochId,
|
||||
dealer: Dealer,
|
||||
dealing_index: DealingIndex,
|
||||
) -> Option<ContractDealing> {
|
||||
let storage_key = StoredDealing::storage_key(epoch_id, dealer, dealing_index);
|
||||
let raw_dealing = storage.get(&storage_key);
|
||||
raw_dealing.map(ContractSafeBytes)
|
||||
chunk_index: ChunkIndex,
|
||||
) -> Option<PartialContractDealingData> {
|
||||
let storage_key = Self::storage_key(epoch_id, dealer, dealing_index, chunk_index);
|
||||
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 {
|
||||
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),
|
||||
// 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))
|
||||
// }
|
||||
|
||||
// iterate over all values, only to be used in tests due to the amount of data being returned
|
||||
#[cfg(test)]
|
||||
pub(crate) fn unchecked_all_entries(
|
||||
storage: &dyn Storage,
|
||||
) -> Vec<((EpochId, Addr, DealingIndex), ContractDealing)> {
|
||||
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), 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()
|
||||
}
|
||||
}
|
||||
|
||||
#[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,
|
||||
) -> ContractDealing {
|
||||
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 {
|
||||
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 {
|
||||
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().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().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,
|
||||
// ) -> 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);
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -2,60 +2,192 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::dealers::storage as dealers_storage;
|
||||
use crate::dealings::storage::StoredDealing;
|
||||
use crate::dealings::storage::{
|
||||
metadata_exists, must_read_metadata, store_metadata, StoredDealing,
|
||||
};
|
||||
use crate::epoch_state::storage::{CURRENT_EPOCH, INITIAL_REPLACEMENT_DATA};
|
||||
use crate::epoch_state::utils::check_epoch_state;
|
||||
use crate::error::ContractError;
|
||||
use crate::state::storage::STATE;
|
||||
use cosmwasm_std::{DepsMut, MessageInfo, Response};
|
||||
use nym_coconut_dkg_common::types::{EpochState, PartialContractDealing};
|
||||
use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response, Storage};
|
||||
use nym_coconut_dkg_common::types::{
|
||||
DealingMetadata, EpochState, PartialContractDealing, MAX_DEALING_CHUNKS,
|
||||
};
|
||||
|
||||
pub fn try_commit_dealings(
|
||||
deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
dealing: PartialContractDealing,
|
||||
// make sure the epoch is in the dealing exchange and the message sender is a valid dealer for this epoch
|
||||
fn ensure_permission(
|
||||
storage: &dyn Storage,
|
||||
sender: &Addr,
|
||||
resharing: bool,
|
||||
) -> Result<Response, ContractError> {
|
||||
check_epoch_state(deps.storage, EpochState::DealingExchange { resharing })?;
|
||||
) -> Result<(), ContractError> {
|
||||
check_epoch_state(storage, EpochState::DealingExchange { resharing })?;
|
||||
|
||||
// ensure the sender is a dealer
|
||||
if dealers_storage::current_dealers()
|
||||
.may_load(deps.storage, &info.sender)?
|
||||
.may_load(storage, sender)?
|
||||
.is_none()
|
||||
{
|
||||
return Err(ContractError::NotADealer);
|
||||
}
|
||||
if resharing
|
||||
&& !INITIAL_REPLACEMENT_DATA
|
||||
.load(deps.storage)?
|
||||
.load(storage)?
|
||||
.initial_dealers
|
||||
.contains(&info.sender)
|
||||
.contains(sender)
|
||||
{
|
||||
return Err(ContractError::NotAnInitialDealer);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn try_submit_dealings_metadata(
|
||||
deps: DepsMut,
|
||||
info: MessageInfo,
|
||||
metadata: DealingMetadata,
|
||||
resharing: bool,
|
||||
) -> Result<Response, ContractError> {
|
||||
ensure_permission(deps.storage, &info.sender, resharing)?;
|
||||
|
||||
let state = STATE.load(deps.storage)?;
|
||||
let epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
|
||||
// check if the index is in range without doing expensive storage reads
|
||||
// don't allow overwriting existing metadata
|
||||
if metadata_exists(
|
||||
deps.storage,
|
||||
epoch.epoch_id,
|
||||
&info.sender,
|
||||
metadata.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,
|
||||
});
|
||||
}
|
||||
|
||||
// make sure the dealing index is in the allowed range
|
||||
// note: dealing indexing starts from 0
|
||||
if dealing.index >= state.key_size {
|
||||
if metadata.dealing_index >= state.key_size {
|
||||
return Err(ContractError::DealingOutOfRange {
|
||||
epoch_id: epoch.epoch_id,
|
||||
dealer: info.sender,
|
||||
index: dealing.index,
|
||||
index: metadata.dealing_index,
|
||||
key_size: state.key_size,
|
||||
});
|
||||
}
|
||||
|
||||
// check if this dealer has already committed this particular dealing
|
||||
if StoredDealing::exists(deps.storage, epoch.epoch_id, &info.sender, dealing.index) {
|
||||
return Err(ContractError::DealingAlreadyCommitted {
|
||||
let chunks = metadata.submitted_chunks.len();
|
||||
|
||||
// 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 {
|
||||
return Err(ContractError::TooFragmentedMetadata {
|
||||
epoch_id: epoch.epoch_id,
|
||||
dealer: info.sender,
|
||||
index: dealing.index,
|
||||
dealing_index: metadata.dealing_index,
|
||||
chunks,
|
||||
});
|
||||
}
|
||||
|
||||
// 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
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let first_chunk_size = metadata.submitted_chunks.first_key_value().unwrap().1.size;
|
||||
|
||||
for (&chunk_index, chunk_info) in metadata.submitted_chunks.iter().take(chunks - 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,
|
||||
first_chunk_size,
|
||||
size: chunk_info.size,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// finally, store the metadata
|
||||
store_metadata(
|
||||
deps.storage,
|
||||
epoch.epoch_id,
|
||||
&info.sender,
|
||||
metadata.dealing_index,
|
||||
&metadata,
|
||||
)?;
|
||||
|
||||
Ok(Response::new())
|
||||
}
|
||||
|
||||
pub fn try_commit_dealings_chunk(
|
||||
deps: DepsMut<'_>,
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
dealing: PartialContractDealing,
|
||||
resharing: bool,
|
||||
) -> Result<Response, ContractError> {
|
||||
ensure_permission(deps.storage, &info.sender, resharing)?;
|
||||
|
||||
let epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
|
||||
// read meta
|
||||
let mut metadata = must_read_metadata(
|
||||
deps.storage,
|
||||
epoch.epoch_id,
|
||||
&info.sender,
|
||||
dealing.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 {
|
||||
return Err(ContractError::DealingChunkNotInMetadata {
|
||||
epoch_id: epoch.epoch_id,
|
||||
dealer: info.sender,
|
||||
dealing_index: dealing.dealing_index,
|
||||
chunk_index: dealing.chunk_index,
|
||||
});
|
||||
};
|
||||
|
||||
// check if this dealer has already committed this particular dealing chunk
|
||||
if let Some(submission_height) = submission_status.submission_height {
|
||||
return Err(ContractError::DealingChunkAlreadyCommitted {
|
||||
epoch_id: epoch.epoch_id,
|
||||
dealer: info.sender,
|
||||
dealing_index: dealing.dealing_index,
|
||||
chunk_index: dealing.chunk_index,
|
||||
block_height: submission_height,
|
||||
});
|
||||
}
|
||||
|
||||
// update the metadata
|
||||
submission_status.submission_height = Some(env.block.height);
|
||||
store_metadata(
|
||||
deps.storage,
|
||||
epoch.epoch_id,
|
||||
&info.sender,
|
||||
dealing.dealing_index,
|
||||
&metadata,
|
||||
)?;
|
||||
|
||||
// store the dealing
|
||||
StoredDealing::save(deps.storage, epoch.epoch_id, &info.sender, dealing);
|
||||
|
||||
Ok(Response::new())
|
||||
@@ -78,127 +210,128 @@ pub(crate) mod tests {
|
||||
|
||||
#[test]
|
||||
fn invalid_commit_dealing() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let mut env = mock_env();
|
||||
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
|
||||
let owner = Addr::unchecked("owner1");
|
||||
let info = mock_info(owner.as_str(), &[]);
|
||||
let dealing = partial_dealing_fixture();
|
||||
|
||||
let ret =
|
||||
try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), false).unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
current_state: EpochState::PublicKeySubmission { resharing: false }.to_string(),
|
||||
expected_state: EpochState::DealingExchange { resharing: false }.to_string()
|
||||
}
|
||||
);
|
||||
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
|
||||
add_fixture_dealer(deps.as_mut());
|
||||
advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
|
||||
let ret =
|
||||
try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), false).unwrap_err();
|
||||
assert_eq!(ret, ContractError::NotADealer);
|
||||
|
||||
let dealer_details = DealerDetails {
|
||||
address: owner.clone(),
|
||||
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)
|
||||
.unwrap();
|
||||
|
||||
// assume we're in resharing mode
|
||||
CURRENT_EPOCH
|
||||
.update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
|
||||
epoch.state = EpochState::DealingExchange { resharing: true };
|
||||
Ok(epoch)
|
||||
})
|
||||
.unwrap();
|
||||
INITIAL_REPLACEMENT_DATA
|
||||
.save(
|
||||
deps.as_mut().storage,
|
||||
&InitialReplacementData {
|
||||
initial_dealers: vec![],
|
||||
initial_height: 1,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let ret =
|
||||
try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), true).unwrap_err();
|
||||
assert_eq!(ret, ContractError::NotAnInitialDealer);
|
||||
|
||||
INITIAL_REPLACEMENT_DATA
|
||||
.update::<_, ContractError>(deps.as_mut().storage, |mut data| {
|
||||
data.initial_dealers = vec![dealer_details_fixture(1).address];
|
||||
Ok(data)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// back to 'normal' mode
|
||||
CURRENT_EPOCH
|
||||
.update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
|
||||
epoch.state = EpochState::DealingExchange { resharing: false };
|
||||
Ok(epoch)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// dealing out of range
|
||||
let ret = try_commit_dealings(
|
||||
deps.as_mut(),
|
||||
info.clone(),
|
||||
PartialContractDealing {
|
||||
index: 42,
|
||||
data: ContractSafeBytes(vec![1, 2, 3]),
|
||||
},
|
||||
false,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::DealingOutOfRange {
|
||||
epoch_id: 0,
|
||||
dealer: info.sender.clone(),
|
||||
index: 42,
|
||||
key_size: DEFAULT_DEALINGS as u32,
|
||||
}
|
||||
);
|
||||
|
||||
// 'good' dealing
|
||||
let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), false);
|
||||
assert!(ret.is_ok());
|
||||
|
||||
// duplicate dealing
|
||||
let ret =
|
||||
try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), false).unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::DealingAlreadyCommitted {
|
||||
epoch_id: 0,
|
||||
dealer: info.sender.clone(),
|
||||
index: 0,
|
||||
}
|
||||
);
|
||||
|
||||
// same index, but next epoch
|
||||
CURRENT_EPOCH
|
||||
.update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
|
||||
epoch.epoch_id += 1;
|
||||
Ok(epoch)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), false);
|
||||
assert!(ret.is_ok());
|
||||
todo!()
|
||||
// let mut deps = helpers::init_contract();
|
||||
// let mut env = mock_env();
|
||||
// try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
//
|
||||
// let owner = Addr::unchecked("owner1");
|
||||
// let info = mock_info(owner.as_str(), &[]);
|
||||
// let dealing = partial_dealing_fixture();
|
||||
//
|
||||
// let ret =
|
||||
// try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), false).unwrap_err();
|
||||
// assert_eq!(
|
||||
// ret,
|
||||
// ContractError::IncorrectEpochState {
|
||||
// current_state: EpochState::PublicKeySubmission { resharing: false }.to_string(),
|
||||
// expected_state: EpochState::DealingExchange { resharing: false }.to_string()
|
||||
// }
|
||||
// );
|
||||
//
|
||||
// env.block.time = env
|
||||
// .block
|
||||
// .time
|
||||
// .plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
|
||||
// add_fixture_dealer(deps.as_mut());
|
||||
// advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
//
|
||||
// let ret =
|
||||
// try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), false).unwrap_err();
|
||||
// assert_eq!(ret, ContractError::NotADealer);
|
||||
//
|
||||
// let dealer_details = DealerDetails {
|
||||
// address: owner.clone(),
|
||||
// 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)
|
||||
// .unwrap();
|
||||
//
|
||||
// // assume we're in resharing mode
|
||||
// CURRENT_EPOCH
|
||||
// .update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
|
||||
// epoch.state = EpochState::DealingExchange { resharing: true };
|
||||
// Ok(epoch)
|
||||
// })
|
||||
// .unwrap();
|
||||
// INITIAL_REPLACEMENT_DATA
|
||||
// .save(
|
||||
// deps.as_mut().storage,
|
||||
// &InitialReplacementData {
|
||||
// initial_dealers: vec![],
|
||||
// initial_height: 1,
|
||||
// },
|
||||
// )
|
||||
// .unwrap();
|
||||
// let ret =
|
||||
// try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), true).unwrap_err();
|
||||
// assert_eq!(ret, ContractError::NotAnInitialDealer);
|
||||
//
|
||||
// INITIAL_REPLACEMENT_DATA
|
||||
// .update::<_, ContractError>(deps.as_mut().storage, |mut data| {
|
||||
// data.initial_dealers = vec![dealer_details_fixture(1).address];
|
||||
// Ok(data)
|
||||
// })
|
||||
// .unwrap();
|
||||
//
|
||||
// // back to 'normal' mode
|
||||
// CURRENT_EPOCH
|
||||
// .update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
|
||||
// epoch.state = EpochState::DealingExchange { resharing: false };
|
||||
// Ok(epoch)
|
||||
// })
|
||||
// .unwrap();
|
||||
//
|
||||
// // dealing out of range
|
||||
// let ret = try_commit_dealings(
|
||||
// deps.as_mut(),
|
||||
// info.clone(),
|
||||
// PartialContractDealing {
|
||||
// dealing_index: 42,
|
||||
// data: ContractSafeBytes(vec![1, 2, 3]),
|
||||
// },
|
||||
// false,
|
||||
// )
|
||||
// .unwrap_err();
|
||||
// assert_eq!(
|
||||
// ret,
|
||||
// ContractError::DealingOutOfRange {
|
||||
// epoch_id: 0,
|
||||
// dealer: info.sender.clone(),
|
||||
// index: 42,
|
||||
// key_size: DEFAULT_DEALINGS as u32,
|
||||
// }
|
||||
// );
|
||||
//
|
||||
// // 'good' dealing
|
||||
// let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), false);
|
||||
// assert!(ret.is_ok());
|
||||
//
|
||||
// // duplicate dealing
|
||||
// let ret =
|
||||
// try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), false).unwrap_err();
|
||||
// assert_eq!(
|
||||
// ret,
|
||||
// ContractError::DealingAlreadyCommitted {
|
||||
// epoch_id: 0,
|
||||
// dealer: info.sender.clone(),
|
||||
// index: 0,
|
||||
// }
|
||||
// );
|
||||
//
|
||||
// // same index, but next epoch
|
||||
// CURRENT_EPOCH
|
||||
// .update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
|
||||
// epoch.epoch_id += 1;
|
||||
// Ok(epoch)
|
||||
// })
|
||||
// .unwrap();
|
||||
//
|
||||
// let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), false);
|
||||
// assert!(ret.is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use cosmwasm_std::{Addr, StdError};
|
||||
use cw_controllers::AdminError;
|
||||
use nym_coconut_dkg_common::types::{DealingIndex, EpochId};
|
||||
use nym_coconut_dkg_common::types::{ChunkIndex, DealingIndex, EpochId, MAX_DEALING_CHUNKS};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Custom errors for contract failure conditions.
|
||||
@@ -37,7 +37,7 @@ pub enum ContractError {
|
||||
EpochNotInitialised,
|
||||
|
||||
#[error(
|
||||
"Requested action needs state to be {expected_state}, currently in state {current_state}, "
|
||||
"Requested action needs state to be {expected_state}, currently in state {current_state}"
|
||||
)]
|
||||
IncorrectEpochState {
|
||||
current_state: String,
|
||||
@@ -50,18 +50,24 @@ pub enum ContractError {
|
||||
#[error("This sender is not a dealer for the current resharing epoch")]
|
||||
NotAnInitialDealer,
|
||||
|
||||
#[error(
|
||||
"Dealer {dealer} has already committed dealing for epoch {epoch_id} with index {index}"
|
||||
)]
|
||||
DealingAlreadyCommitted {
|
||||
#[error("Dealer {dealer} has already committed dealing chunk for epoch {epoch_id} with dealing index {dealing_index} and chunk index {chunk_index} at height {block_height}")]
|
||||
DealingChunkAlreadyCommitted {
|
||||
epoch_id: EpochId,
|
||||
dealer: Addr,
|
||||
index: DealingIndex,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
block_height: u64,
|
||||
},
|
||||
|
||||
#[error(
|
||||
"Dealer {dealer} has attempted to commit dealing for epoch {epoch_id} with index {index} while the key size is set to {key_size}"
|
||||
)]
|
||||
#[error("dealer {dealer} tried to commit chunk {chunk_index} of dealing {dealing_index} for epoch {epoch_id}, but it hasn't been declared in the prior metadata")]
|
||||
DealingChunkNotInMetadata {
|
||||
epoch_id: EpochId,
|
||||
dealer: Addr,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
},
|
||||
|
||||
#[error("dealer {dealer} has attempted to commit dealing chunk for epoch {epoch_id} with dealing index {index} while the key size is set to {key_size}")]
|
||||
DealingOutOfRange {
|
||||
epoch_id: EpochId,
|
||||
dealer: Addr,
|
||||
@@ -69,6 +75,45 @@ pub enum ContractError {
|
||||
key_size: u32,
|
||||
},
|
||||
|
||||
#[error("dealer {dealer} has attempted to commit dealing metadata for epoch {epoch_id} for dealing index {dealing_index} with {chunks} chunks while at most {} chunks are allowed", MAX_DEALING_CHUNKS)]
|
||||
TooFragmentedMetadata {
|
||||
epoch_id: EpochId,
|
||||
dealer: Addr,
|
||||
dealing_index: DealingIndex,
|
||||
chunks: usize,
|
||||
},
|
||||
|
||||
#[error("")]
|
||||
UnevenChunkSplit {
|
||||
epoch_id: EpochId,
|
||||
dealer: Addr,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
first_chunk_size: usize,
|
||||
size: 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,
|
||||
dealer: Addr,
|
||||
dealing_index: DealingIndex,
|
||||
},
|
||||
|
||||
#[error("metadata for dealing for epoch {epoch_id} from {dealer} at index {dealing_index} does not exist")]
|
||||
UnavailableDealingMetadata {
|
||||
epoch_id: EpochId,
|
||||
dealer: Addr,
|
||||
dealing_index: DealingIndex,
|
||||
},
|
||||
|
||||
#[error("metadata for dealing for epoch {epoch_id} from {dealer} at index {dealing_index} already exists")]
|
||||
MetadataAlreadyExists {
|
||||
epoch_id: EpochId,
|
||||
dealer: Addr,
|
||||
dealing_index: DealingIndex,
|
||||
},
|
||||
|
||||
#[error("This dealer has already committed {commitment}")]
|
||||
AlreadyCommitted { commitment: String },
|
||||
|
||||
|
||||
@@ -24,10 +24,11 @@ pub fn dealing_bytes_fixture() -> ContractSafeBytes {
|
||||
}
|
||||
|
||||
pub fn partial_dealing_fixture() -> PartialContractDealing {
|
||||
PartialContractDealing {
|
||||
index: 0,
|
||||
data: ContractSafeBytes(vec![1, 2, 3]),
|
||||
}
|
||||
todo!()
|
||||
// PartialContractDealing {
|
||||
// dealing_index: 0,
|
||||
// data: ContractSafeBytes(vec![1, 2, 3]),
|
||||
// }
|
||||
}
|
||||
|
||||
pub fn dealer_details_fixture(assigned_index: u64) -> DealerDetails {
|
||||
|
||||
Reference in New Issue
Block a user