Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 169f8f2c1c | |||
| 8782fd7bb8 | |||
| 146c3bd358 | |||
| e68ebdc2b8 | |||
| 397b03267a | |||
| 2a021b46ac | |||
| c43cb657c6 | |||
| 6f66b377e2 | |||
| 5ea67a9376 | |||
| f566dffc5b | |||
| 05f8beedad | |||
| 2fff051e28 | |||
| 44bd70c546 | |||
| 702354d127 | |||
| 56b1010d16 | |||
| 0632517f5d | |||
| bfcc5e9b41 | |||
| 337aacd442 | |||
| da5b7302b5 | |||
| 7a53e86b40 | |||
| 654dd07d19 | |||
| ef0765face | |||
| 3685b4681c | |||
| 47e2af2caa | |||
| 5be555d79f | |||
| 8cc2b3167e | |||
| 4d95955961 | |||
| e36ae4091f | |||
| b566147f2f | |||
| 12242bb3c6 | |||
| 0a0b0e80f4 |
Generated
+2
@@ -6309,6 +6309,8 @@ dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
"cw2",
|
||||
"cw4",
|
||||
"nym-contracts-common",
|
||||
"nym-multisig-contract-common",
|
||||
]
|
||||
|
||||
@@ -7,13 +7,20 @@ use crate::nyxd::error::NyxdError;
|
||||
use crate::nyxd::CosmWasmClient;
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::AccountId;
|
||||
use nym_coconut_dkg_common::{
|
||||
dealer::{ContractDealing, DealerDetailsResponse, PagedDealerResponse, PagedDealingsResponse},
|
||||
use serde::Deserialize;
|
||||
|
||||
pub use nym_coconut_dkg_common::{
|
||||
dealer::{
|
||||
DealerDetailsResponse, DealingResponse, DealingStatusResponse, PagedDealerResponse,
|
||||
PagedDealingsResponse,
|
||||
},
|
||||
msg::QueryMsg as DkgQueryMsg,
|
||||
types::{DealerDetails, Epoch, EpochId, InitialReplacementData},
|
||||
types::{
|
||||
DealerDetails, DealingIndex, Epoch, EpochId, InitialReplacementData,
|
||||
PartialContractDealing, State,
|
||||
},
|
||||
verification_key::{ContractVKShare, PagedVKSharesResponse},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
@@ -22,10 +29,16 @@ pub trait DkgQueryClient {
|
||||
where
|
||||
for<'a> T: Deserialize<'a>;
|
||||
|
||||
async fn get_state(&self) -> Result<State, NyxdError> {
|
||||
let request = DkgQueryMsg::GetState {};
|
||||
self.query_dkg_contract(request).await
|
||||
}
|
||||
|
||||
async fn get_current_epoch(&self) -> Result<Epoch, NyxdError> {
|
||||
let request = DkgQueryMsg::GetCurrentEpochState {};
|
||||
self.query_dkg_contract(request).await
|
||||
}
|
||||
|
||||
async fn get_current_epoch_threshold(&self) -> Result<Option<u64>, NyxdError> {
|
||||
let request = DkgQueryMsg::GetCurrentEpochThreshold {};
|
||||
self.query_dkg_contract(request).await
|
||||
@@ -64,14 +77,46 @@ pub trait DkgQueryClient {
|
||||
self.query_dkg_contract(request).await
|
||||
}
|
||||
|
||||
async fn get_dealings_paged(
|
||||
async fn get_dealing_status(
|
||||
&self,
|
||||
idx: u64,
|
||||
start_after: Option<String>,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
) -> Result<DealingStatusResponse, NyxdError> {
|
||||
let request = DkgQueryMsg::GetDealingStatus {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
};
|
||||
|
||||
self.query_dkg_contract(request).await
|
||||
}
|
||||
|
||||
async fn get_dealing(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
) -> Result<DealingResponse, NyxdError> {
|
||||
let request = DkgQueryMsg::GetDealing {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
};
|
||||
|
||||
self.query_dkg_contract(request).await
|
||||
}
|
||||
|
||||
async fn get_dealer_dealings_paged(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: &str,
|
||||
start_after: Option<DealingIndex>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedDealingsResponse, NyxdError> {
|
||||
let request = DkgQueryMsg::GetDealing {
|
||||
idx,
|
||||
let request = DkgQueryMsg::GetDealings {
|
||||
epoch_id,
|
||||
dealer: dealer.to_string(),
|
||||
limit,
|
||||
start_after,
|
||||
};
|
||||
@@ -91,6 +136,11 @@ pub trait DkgQueryClient {
|
||||
};
|
||||
self.query_dkg_contract(request).await
|
||||
}
|
||||
|
||||
async fn get_contract_cw2_version(&self) -> Result<cw2::ContractVersion, NyxdError> {
|
||||
self.query_dkg_contract(DkgQueryMsg::GetCW2ContractVersion {})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
// extension trait to the query client to deal with the paged queries
|
||||
@@ -106,8 +156,12 @@ pub trait PagedDkgQueryClient: DkgQueryClient {
|
||||
collect_paged!(self, get_past_dealers_paged, dealers)
|
||||
}
|
||||
|
||||
async fn get_all_epoch_dealings(&self, idx: u64) -> Result<Vec<ContractDealing>, NyxdError> {
|
||||
collect_paged!(self, get_dealings_paged, dealings, idx)
|
||||
async fn get_all_dealer_dealings(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: &str,
|
||||
) -> Result<Vec<PartialContractDealing>, NyxdError> {
|
||||
collect_paged!(self, get_dealer_dealings_paged, dealings, epoch_id, dealer)
|
||||
}
|
||||
|
||||
async fn get_all_verification_key_shares(
|
||||
@@ -151,6 +205,7 @@ mod tests {
|
||||
msg: DkgQueryMsg,
|
||||
) {
|
||||
match msg {
|
||||
DkgQueryMsg::GetState {} => client.get_state().ignore(),
|
||||
DkgQueryMsg::GetCurrentEpochState {} => client.get_current_epoch().ignore(),
|
||||
DkgQueryMsg::GetCurrentEpochThreshold {} => {
|
||||
client.get_current_epoch_threshold().ignore()
|
||||
@@ -165,11 +220,26 @@ mod tests {
|
||||
DkgQueryMsg::GetPastDealers { limit, start_after } => {
|
||||
client.get_past_dealers_paged(start_after, limit).ignore()
|
||||
}
|
||||
DkgQueryMsg::GetDealingStatus {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
} => client
|
||||
.get_dealing_status(epoch_id, dealer, dealing_index)
|
||||
.ignore(),
|
||||
DkgQueryMsg::GetDealing {
|
||||
idx,
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
} => client.get_dealing(epoch_id, dealer, dealing_index).ignore(),
|
||||
DkgQueryMsg::GetDealings {
|
||||
epoch_id,
|
||||
dealer,
|
||||
limit,
|
||||
start_after,
|
||||
} => client.get_dealings_paged(idx, start_after, limit).ignore(),
|
||||
} => client
|
||||
.get_dealer_dealings_paged(epoch_id, &dealer, limit, start_after)
|
||||
.ignore(),
|
||||
DkgQueryMsg::GetVerificationKeys {
|
||||
epoch_id,
|
||||
limit,
|
||||
@@ -177,6 +247,7 @@ mod tests {
|
||||
} => client
|
||||
.get_vk_shares_paged(epoch_id, start_after, limit)
|
||||
.ignore(),
|
||||
DkgQueryMsg::GetCW2ContractVersion {} => client.get_contract_cw2_version().ignore(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+24
-13
@@ -10,9 +10,9 @@ use async_trait::async_trait;
|
||||
use cosmrs::AccountId;
|
||||
use cosmwasm_std::Addr;
|
||||
use nym_coconut_dkg_common::msg::ExecuteMsg as DkgExecuteMsg;
|
||||
use nym_coconut_dkg_common::types::EncodedBTEPublicKeyWithProof;
|
||||
use nym_coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, PartialContractDealing};
|
||||
use nym_coconut_dkg_common::verification_key::VerificationKeyShare;
|
||||
use nym_contracts_common::dealings::ContractSafeBytes;
|
||||
use nym_contracts_common::IdentityKey;
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
@@ -25,6 +25,13 @@ pub trait DkgSigningClient {
|
||||
funds: Vec<Coin>,
|
||||
) -> Result<ExecuteResult, NyxdError>;
|
||||
|
||||
async fn initiate_dkg(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
|
||||
let req = DkgExecuteMsg::InitiateDkg {};
|
||||
|
||||
self.execute_dkg_contract(fee, req, "initiating the DKG".to_string(), vec![])
|
||||
.await
|
||||
}
|
||||
|
||||
async fn advance_dkg_epoch_state(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
|
||||
let req = DkgExecuteMsg::AdvanceEpochState {};
|
||||
|
||||
@@ -42,12 +49,14 @@ pub trait DkgSigningClient {
|
||||
async fn register_dealer(
|
||||
&self,
|
||||
bte_key: EncodedBTEPublicKeyWithProof,
|
||||
identity_key: IdentityKey,
|
||||
announce_address: String,
|
||||
resharing: bool,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
let req = DkgExecuteMsg::RegisterDealer {
|
||||
bte_key_with_proof: bte_key,
|
||||
identity_key,
|
||||
announce_address,
|
||||
resharing,
|
||||
};
|
||||
@@ -58,14 +67,11 @@ pub trait DkgSigningClient {
|
||||
|
||||
async fn submit_dealing_bytes(
|
||||
&self,
|
||||
dealing_bytes: ContractSafeBytes,
|
||||
dealing: PartialContractDealing,
|
||||
resharing: bool,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
let req = DkgExecuteMsg::CommitDealing {
|
||||
dealing_bytes,
|
||||
resharing,
|
||||
};
|
||||
let req = DkgExecuteMsg::CommitDealing { dealing, resharing };
|
||||
|
||||
self.execute_dkg_contract(fee, req, "dealing commitment".to_string(), vec![])
|
||||
.await
|
||||
@@ -146,18 +152,23 @@ mod tests {
|
||||
msg: DkgExecuteMsg,
|
||||
) {
|
||||
match msg {
|
||||
DkgExecuteMsg::InitiateDkg {} => client.initiate_dkg(None).ignore(),
|
||||
DkgExecuteMsg::RegisterDealer {
|
||||
bte_key_with_proof,
|
||||
identity_key,
|
||||
announce_address,
|
||||
resharing,
|
||||
} => client
|
||||
.register_dealer(bte_key_with_proof, announce_address, resharing, None)
|
||||
.register_dealer(
|
||||
bte_key_with_proof,
|
||||
identity_key,
|
||||
announce_address,
|
||||
resharing,
|
||||
None,
|
||||
)
|
||||
.ignore(),
|
||||
DkgExecuteMsg::CommitDealing {
|
||||
dealing_bytes,
|
||||
resharing,
|
||||
} => client
|
||||
.submit_dealing_bytes(dealing_bytes, resharing, None)
|
||||
DkgExecuteMsg::CommitDealing { dealing, resharing } => client
|
||||
.submit_dealing_bytes(dealing, resharing, None)
|
||||
.ignore(),
|
||||
DkgExecuteMsg::CommitVerificationKeyShare { share, resharing } => client
|
||||
.submit_verification_key_share(share, resharing, None)
|
||||
|
||||
@@ -8,26 +8,26 @@ use std::str::FromStr;
|
||||
// TODO: all of those could/should be derived via a macro
|
||||
|
||||
// query clients
|
||||
mod coconut_bandwidth_query_client;
|
||||
mod dkg_query_client;
|
||||
mod ephemera_query_client;
|
||||
mod group_query_client;
|
||||
mod mixnet_query_client;
|
||||
mod multisig_query_client;
|
||||
mod name_service_query_client;
|
||||
mod sp_directory_query_client;
|
||||
mod vesting_query_client;
|
||||
pub mod coconut_bandwidth_query_client;
|
||||
pub mod dkg_query_client;
|
||||
pub mod ephemera_query_client;
|
||||
pub mod group_query_client;
|
||||
pub mod mixnet_query_client;
|
||||
pub mod multisig_query_client;
|
||||
pub mod name_service_query_client;
|
||||
pub mod sp_directory_query_client;
|
||||
pub mod vesting_query_client;
|
||||
|
||||
// signing clients
|
||||
mod coconut_bandwidth_signing_client;
|
||||
mod dkg_signing_client;
|
||||
mod ephemera_signing_client;
|
||||
mod group_signing_client;
|
||||
mod mixnet_signing_client;
|
||||
mod multisig_signing_client;
|
||||
mod name_service_signing_client;
|
||||
mod sp_directory_signing_client;
|
||||
mod vesting_signing_client;
|
||||
pub mod coconut_bandwidth_signing_client;
|
||||
pub mod dkg_signing_client;
|
||||
pub mod ephemera_signing_client;
|
||||
pub mod group_signing_client;
|
||||
pub mod mixnet_signing_client;
|
||||
pub mod multisig_signing_client;
|
||||
pub mod name_service_signing_client;
|
||||
pub mod sp_directory_signing_client;
|
||||
pub mod vesting_signing_client;
|
||||
|
||||
// re-export query traits
|
||||
pub use coconut_bandwidth_query_client::{
|
||||
|
||||
@@ -6,7 +6,7 @@ use log::{debug, info};
|
||||
use std::str::FromStr;
|
||||
|
||||
use nym_coconut_dkg_common::msg::InstantiateMsg;
|
||||
use nym_coconut_dkg_common::types::TimeConfiguration;
|
||||
use nym_coconut_dkg_common::types::{TimeConfiguration, DEFAULT_DEALINGS};
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
@@ -93,6 +93,7 @@ pub async fn generate(args: Args) {
|
||||
multisig_addr: multisig_addr.to_string(),
|
||||
time_configuration: Some(time_configuration),
|
||||
mix_denom,
|
||||
key_size: DEFAULT_DEALINGS as u32,
|
||||
};
|
||||
|
||||
debug!("instantiate_msg: {:?}", instantiate_msg);
|
||||
|
||||
@@ -10,6 +10,8 @@ license.workspace = true
|
||||
cosmwasm-schema = { workspace = true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
cw-utils = { workspace = true }
|
||||
cw2 = { workspace = true }
|
||||
cw4 = { workspace = true }
|
||||
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common" }
|
||||
nym-multisig-contract-common = { path = "../multisig-contract" }
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::types::{ContractSafeBytes, EncodedBTEPublicKeyWithProof, NodeIndex};
|
||||
use crate::types::{
|
||||
ContractDealing, DealingIndex, EncodedBTEPublicKeyWithProof, EpochId, NodeIndex,
|
||||
PartialContractDealing,
|
||||
};
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::Addr;
|
||||
|
||||
@@ -9,6 +12,7 @@ use cosmwasm_std::Addr;
|
||||
pub struct DealerDetails {
|
||||
pub address: Addr,
|
||||
pub bte_public_key_with_proof: EncodedBTEPublicKeyWithProof,
|
||||
pub ed25519_identity: String,
|
||||
pub announce_address: String,
|
||||
pub assigned_index: NodeIndex,
|
||||
}
|
||||
@@ -66,35 +70,50 @@ impl PagedDealerResponse {
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct ContractDealing {
|
||||
pub dealing: ContractSafeBytes,
|
||||
pub struct DealingResponse {
|
||||
pub epoch_id: EpochId,
|
||||
|
||||
pub dealer: Addr,
|
||||
|
||||
pub dealing_index: DealingIndex,
|
||||
|
||||
pub dealing: Option<ContractDealing>,
|
||||
}
|
||||
|
||||
impl ContractDealing {
|
||||
pub fn new(dealing: ContractSafeBytes, dealer: Addr) -> Self {
|
||||
ContractDealing { dealing, dealer }
|
||||
}
|
||||
#[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 dealings: Vec<ContractDealing>,
|
||||
pub per_page: usize,
|
||||
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<Addr>,
|
||||
pub start_next_after: Option<DealingIndex>,
|
||||
}
|
||||
|
||||
impl PagedDealingsResponse {
|
||||
pub fn new(
|
||||
dealings: Vec<ContractDealing>,
|
||||
per_page: usize,
|
||||
start_next_after: Option<Addr>,
|
||||
epoch_id: EpochId,
|
||||
dealer: Addr,
|
||||
dealings: Vec<PartialContractDealing>,
|
||||
start_next_after: Option<DealingIndex>,
|
||||
) -> Self {
|
||||
PagedDealingsResponse {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealings,
|
||||
per_page,
|
||||
start_next_after,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::types::{ContractSafeBytes, EncodedBTEPublicKeyWithProof, EpochId, TimeConfiguration};
|
||||
use crate::types::{
|
||||
DealingIndex, EncodedBTEPublicKeyWithProof, EpochId, PartialContractDealing, TimeConfiguration,
|
||||
};
|
||||
use crate::verification_key::VerificationKeyShare;
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::Addr;
|
||||
|
||||
#[cfg(feature = "schema")]
|
||||
use crate::{
|
||||
dealer::{DealerDetailsResponse, PagedDealerResponse, PagedDealingsResponse},
|
||||
types::{Epoch, InitialReplacementData},
|
||||
dealer::{
|
||||
DealerDetailsResponse, DealingResponse, DealingStatusResponse, PagedDealerResponse,
|
||||
PagedDealingsResponse,
|
||||
},
|
||||
types::{Epoch, InitialReplacementData, State},
|
||||
verification_key::PagedVKSharesResponse,
|
||||
};
|
||||
use contracts_common::IdentityKey;
|
||||
#[cfg(feature = "schema")]
|
||||
use cosmwasm_schema::QueryResponses;
|
||||
|
||||
@@ -21,18 +27,25 @@ pub struct InstantiateMsg {
|
||||
pub multisig_addr: String,
|
||||
pub time_configuration: Option<TimeConfiguration>,
|
||||
pub mix_denom: String,
|
||||
|
||||
/// Specifies the number of elements in the derived keys
|
||||
pub key_size: u32,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub enum ExecuteMsg {
|
||||
// we could have just re-used AdvanceEpochState, but imo an explicit message is better
|
||||
InitiateDkg {},
|
||||
|
||||
RegisterDealer {
|
||||
bte_key_with_proof: EncodedBTEPublicKeyWithProof,
|
||||
identity_key: IdentityKey,
|
||||
announce_address: String,
|
||||
resharing: bool,
|
||||
},
|
||||
|
||||
CommitDealing {
|
||||
dealing_bytes: ContractSafeBytes,
|
||||
dealing: PartialContractDealing,
|
||||
resharing: bool,
|
||||
},
|
||||
|
||||
@@ -55,6 +68,9 @@ pub enum ExecuteMsg {
|
||||
#[cw_serde]
|
||||
#[cfg_attr(feature = "schema", derive(QueryResponses))]
|
||||
pub enum QueryMsg {
|
||||
#[cfg_attr(feature = "schema", returns(State))]
|
||||
GetState {},
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(Epoch))]
|
||||
GetCurrentEpochState {},
|
||||
|
||||
@@ -79,11 +95,26 @@ pub enum QueryMsg {
|
||||
start_after: Option<String>,
|
||||
},
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(PagedDealingsResponse))]
|
||||
#[cfg_attr(feature = "schema", returns(DealingStatusResponse))]
|
||||
GetDealingStatus {
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
},
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(DealingResponse))]
|
||||
GetDealing {
|
||||
idx: u64,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
},
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(PagedDealingsResponse))]
|
||||
GetDealings {
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
limit: Option<u32>,
|
||||
start_after: Option<String>,
|
||||
start_after: Option<DealingIndex>,
|
||||
},
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(PagedVKSharesResponse))]
|
||||
@@ -92,6 +123,11 @@ pub enum QueryMsg {
|
||||
limit: Option<u32>,
|
||||
start_after: Option<String>,
|
||||
},
|
||||
|
||||
/// Gets the stored contract version information that's required by the CW2 spec interface for migrations.
|
||||
#[serde(rename = "get_cw2_contract_version")]
|
||||
#[cfg_attr(feature = "schema", returns(cw2::ContractVersion))]
|
||||
GetCW2ContractVersion {},
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
|
||||
@@ -8,14 +8,38 @@ use std::str::FromStr;
|
||||
pub use crate::dealer::{DealerDetails, PagedDealerResponse};
|
||||
pub use contracts_common::dealings::ContractSafeBytes;
|
||||
pub use cosmwasm_std::{Addr, Coin, Timestamp};
|
||||
pub use cw4::Cw4Contract;
|
||||
|
||||
pub type EncodedBTEPublicKeyWithProof = String;
|
||||
pub type EncodedBTEPublicKeyWithProofRef<'a> = &'a str;
|
||||
pub type NodeIndex = u64;
|
||||
pub type EpochId = u64;
|
||||
pub type DealingIndex = u32;
|
||||
pub type ContractDealing = ContractSafeBytes;
|
||||
|
||||
// 2 public attributes, 2 private attributes, 1 fixed for coconut credential
|
||||
pub const TOTAL_DEALINGS: usize = 2 + 2 + 1;
|
||||
pub const DEFAULT_DEALINGS: usize = 2 + 2 + 1;
|
||||
|
||||
#[cw_serde]
|
||||
pub struct PartialContractDealing {
|
||||
pub index: DealingIndex,
|
||||
pub data: ContractDealing,
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct InitialReplacementData {
|
||||
@@ -73,13 +97,23 @@ impl Default for TimeConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct State {
|
||||
pub mix_denom: String,
|
||||
pub multisig_addr: Addr,
|
||||
pub group_addr: Cw4Contract,
|
||||
|
||||
/// Specifies the number of elements in the derived keys
|
||||
pub key_size: u32,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
#[derive(Copy, Default)]
|
||||
pub struct Epoch {
|
||||
pub state: EpochState,
|
||||
pub epoch_id: EpochId,
|
||||
pub time_configuration: TimeConfiguration,
|
||||
pub finish_timestamp: Timestamp,
|
||||
pub finish_timestamp: Option<Timestamp>,
|
||||
}
|
||||
|
||||
impl Epoch {
|
||||
@@ -90,36 +124,40 @@ impl Epoch {
|
||||
current_timestamp: Timestamp,
|
||||
) -> Self {
|
||||
let duration = match state {
|
||||
EpochState::WaitingInitialisation => None,
|
||||
EpochState::PublicKeySubmission { .. } => {
|
||||
time_configuration.public_key_submission_time_secs
|
||||
Some(time_configuration.public_key_submission_time_secs)
|
||||
}
|
||||
EpochState::DealingExchange { .. } => {
|
||||
Some(time_configuration.dealing_exchange_time_secs)
|
||||
}
|
||||
EpochState::DealingExchange { .. } => time_configuration.dealing_exchange_time_secs,
|
||||
EpochState::VerificationKeySubmission { .. } => {
|
||||
time_configuration.verification_key_submission_time_secs
|
||||
Some(time_configuration.verification_key_submission_time_secs)
|
||||
}
|
||||
EpochState::VerificationKeyValidation { .. } => {
|
||||
time_configuration.verification_key_validation_time_secs
|
||||
Some(time_configuration.verification_key_validation_time_secs)
|
||||
}
|
||||
EpochState::VerificationKeyFinalization { .. } => {
|
||||
time_configuration.verification_key_finalization_time_secs
|
||||
Some(time_configuration.verification_key_finalization_time_secs)
|
||||
}
|
||||
EpochState::InProgress => time_configuration.in_progress_time_secs,
|
||||
EpochState::InProgress => Some(time_configuration.in_progress_time_secs),
|
||||
};
|
||||
Epoch {
|
||||
state,
|
||||
epoch_id,
|
||||
time_configuration,
|
||||
finish_timestamp: current_timestamp.plus_seconds(duration),
|
||||
finish_timestamp: duration.map(|d| current_timestamp.plus_seconds(d)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn final_timestamp_secs(&self) -> u64 {
|
||||
let mut finish = self.finish_timestamp.seconds();
|
||||
pub fn final_timestamp_secs(&self) -> Option<u64> {
|
||||
let mut finish = self.finish_timestamp?.seconds();
|
||||
let time_configuration = self.time_configuration;
|
||||
let mut curr_epoch_state = self.state;
|
||||
while let Some(state) = curr_epoch_state.next() {
|
||||
curr_epoch_state = state;
|
||||
let adding = match curr_epoch_state {
|
||||
EpochState::WaitingInitialisation => return None,
|
||||
EpochState::PublicKeySubmission { .. } => {
|
||||
time_configuration.public_key_submission_time_secs
|
||||
}
|
||||
@@ -137,12 +175,13 @@ impl Epoch {
|
||||
};
|
||||
finish += adding;
|
||||
}
|
||||
finish
|
||||
Some(finish)
|
||||
}
|
||||
}
|
||||
|
||||
// currently (it is still extremely likely to change, we might be able to get rid of verification key-related complaints),
|
||||
// the epoch can be in the following states (in order):
|
||||
// 0. WaitingInitialisation -> the contract has been instantiated, but awaits for the admin to kick off the process (group members might still be getting added)
|
||||
// 1. PublicKeySubmission -> potential dealers are submitting their BTE and ed25519 public keys to participate in dealing exchange
|
||||
// 2. DealingExchange -> the actual (off-chain) dealing exchange is happening
|
||||
// 3. ComplaintSubmission -> receivers submitting evidence of other dealers sending malformed data
|
||||
@@ -156,6 +195,7 @@ impl Epoch {
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
pub enum EpochState {
|
||||
WaitingInitialisation,
|
||||
PublicKeySubmission { resharing: bool },
|
||||
DealingExchange { resharing: bool },
|
||||
VerificationKeySubmission { resharing: bool },
|
||||
@@ -166,13 +206,14 @@ pub enum EpochState {
|
||||
|
||||
impl Default for EpochState {
|
||||
fn default() -> Self {
|
||||
Self::PublicKeySubmission { resharing: false }
|
||||
Self::WaitingInitialisation
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for EpochState {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
EpochState::WaitingInitialisation => write!(f, "Waiting for initialisation"),
|
||||
EpochState::PublicKeySubmission { resharing } => {
|
||||
write!(f, "PublicKeySubmission (resharing: {resharing})")
|
||||
}
|
||||
@@ -194,8 +235,13 @@ impl Display for EpochState {
|
||||
}
|
||||
|
||||
impl EpochState {
|
||||
pub fn first() -> Self {
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
}
|
||||
|
||||
pub fn next(self) -> Option<Self> {
|
||||
match self {
|
||||
EpochState::WaitingInitialisation => None,
|
||||
EpochState::PublicKeySubmission { resharing } => {
|
||||
Some(EpochState::DealingExchange { resharing })
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ use nym_bandwidth_controller::acquire::state::State;
|
||||
use nym_client_core::config::disk_persistence::CommonClientPaths;
|
||||
use nym_config::DEFAULT_DATA_DIR;
|
||||
use nym_credential_storage::persistent_storage::PersistentStorage;
|
||||
use nym_validator_client::nyxd::contract_traits::{CoconutBandwidthSigningClient, DkgQueryClient};
|
||||
use nym_validator_client::nyxd::contract_traits::{
|
||||
dkg_query_client::EpochState, CoconutBandwidthSigningClient, DkgQueryClient,
|
||||
};
|
||||
use nym_validator_client::nyxd::Coin;
|
||||
use std::path::PathBuf;
|
||||
use std::process::exit;
|
||||
@@ -87,21 +89,29 @@ where
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.expect("the system clock is set to 01/01/1970 (or earlier)")
|
||||
.as_secs();
|
||||
|
||||
if epoch.state.is_final() {
|
||||
if current_timestamp_secs + SAFETY_BUFFER_SECS >= epoch.finish_timestamp.seconds() {
|
||||
info!("In the next {} minute(s), a transition will take place in the coconut system. Deposits should be halted in this time for safety reasons.", SAFETY_BUFFER_SECS / 60);
|
||||
exit(0);
|
||||
if let Some(finish_timestamp) = epoch.finish_timestamp {
|
||||
if current_timestamp_secs + SAFETY_BUFFER_SECS >= finish_timestamp.seconds() {
|
||||
info!("In the next {} minute(s), a transition will take place in the coconut system. Deposits should be halted in this time for safety reasons.", SAFETY_BUFFER_SECS / 60);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
} else {
|
||||
} else if let Some(final_timestamp) = epoch.final_timestamp_secs() {
|
||||
// Use 1 additional second to not start the next iteration immediately and spam get_current_epoch queries
|
||||
let secs_until_final = epoch
|
||||
.final_timestamp_secs()
|
||||
.saturating_sub(current_timestamp_secs)
|
||||
+ 1;
|
||||
let secs_until_final = final_timestamp.saturating_sub(current_timestamp_secs) + 1;
|
||||
info!("Approximately {} seconds until coconut is available. Sleeping until then. You can safely kill the process at any moment.", secs_until_final);
|
||||
tokio::time::sleep(Duration::from_secs(secs_until_final)).await;
|
||||
} else if matches!(epoch.state, EpochState::WaitingInitialisation) {
|
||||
info!("dkg hasn't been initialised yet and it is not known when it will be. Going to check again later");
|
||||
tokio::time::sleep(Duration::from_secs(60 * 5)).await;
|
||||
} else {
|
||||
// this should never be the case since the only case where final timestamp is unknown is when it's waiting for initialisation,
|
||||
// but let's guard ourselves against future changes
|
||||
info!("it is unknown when coconut will be come available. Going to check again later");
|
||||
tokio::time::sleep(Duration::from_secs(60 * 5)).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ use std::collections::HashMap;
|
||||
use std::ops::Neg;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(Clone, PartialEq, Eq))]
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||
pub struct Ciphertexts {
|
||||
pub rr: [G1Projective; NUM_CHUNKS],
|
||||
pub ss: [G1Projective; NUM_CHUNKS],
|
||||
|
||||
@@ -67,8 +67,8 @@ impl<'a> Instance<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(Clone, PartialEq, Eq))]
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||
pub struct ProofOfChunking {
|
||||
y0: G1Projective,
|
||||
bb: Vec<G1Projective>,
|
||||
|
||||
@@ -76,8 +76,8 @@ impl<'a> Instance<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(Clone, PartialEq, Eq))]
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||
pub struct ProofOfSecretSharing {
|
||||
ff: G1Projective,
|
||||
aa: G2Projective,
|
||||
|
||||
@@ -82,7 +82,7 @@ impl RecoveredVerificationKeys {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||
pub struct Dealing {
|
||||
pub public_coefficients: PublicCoefficients,
|
||||
|
||||
Generated
+6
-3
@@ -1220,12 +1220,13 @@ dependencies = [
|
||||
"cw-controllers",
|
||||
"cw-multi-test",
|
||||
"cw-storage-plus",
|
||||
"cw2",
|
||||
"cw4",
|
||||
"cw4-group",
|
||||
"lazy_static",
|
||||
"nym-coconut-dkg-common",
|
||||
"nym-group-contract-common",
|
||||
"rusty-fork",
|
||||
"semver",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
@@ -1237,6 +1238,8 @@ dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
"cw2",
|
||||
"cw4",
|
||||
"nym-contracts-common",
|
||||
"nym-multisig-contract-common",
|
||||
]
|
||||
@@ -1879,9 +1882,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.17"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
|
||||
checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
|
||||
@@ -48,5 +48,6 @@ cw3 = "=1.1.0"
|
||||
cw3-fixed-multisig = "=1.1.0"
|
||||
cw4 = "=1.1.0"
|
||||
cw20 = "=1.1.0"
|
||||
semver = "1.0.21"
|
||||
|
||||
thiserror = "1.0.48"
|
||||
|
||||
@@ -20,15 +20,16 @@ cosmwasm-std = { workspace = true }
|
||||
cosmwasm-storage = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
cw-controllers = { workspace = true }
|
||||
cw2 = { workspace = true }
|
||||
cw4 = { workspace = true }
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
semver = { workspace = true, default-features = false }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
cw-multi-test = { workspace = true }
|
||||
cw4-group = { path = "../multisig/cw4-group" }
|
||||
nym-group-contract-common = { path = "../../common/cosmwasm-smart-contracts/group-contract" }
|
||||
lazy_static = "1.4"
|
||||
rusty-fork = "0.3"
|
||||
|
||||
[features]
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"group_addr",
|
||||
"key_size",
|
||||
"mix_denom",
|
||||
"multisig_addr"
|
||||
],
|
||||
@@ -15,6 +16,12 @@
|
||||
"group_addr": {
|
||||
"type": "string"
|
||||
},
|
||||
"key_size": {
|
||||
"description": "Specifies the number of elements in the derived keys",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"mix_denom": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -84,6 +91,19 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "ExecuteMsg",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"initiate_dkg"
|
||||
],
|
||||
"properties": {
|
||||
"initiate_dkg": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -95,6 +115,7 @@
|
||||
"required": [
|
||||
"announce_address",
|
||||
"bte_key_with_proof",
|
||||
"identity_key",
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
@@ -104,6 +125,9 @@
|
||||
"bte_key_with_proof": {
|
||||
"type": "string"
|
||||
},
|
||||
"identity_key": {
|
||||
"type": "string"
|
||||
},
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
}
|
||||
@@ -122,12 +146,12 @@
|
||||
"commit_dealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealing_bytes",
|
||||
"dealing",
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
"dealing_bytes": {
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
"dealing": {
|
||||
"$ref": "#/definitions/PartialContractDealing"
|
||||
},
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
@@ -227,6 +251,24 @@
|
||||
"format": "uint8",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"PartialContractDealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"data",
|
||||
"index"
|
||||
],
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
},
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -234,6 +276,19 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "QueryMsg",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_state"
|
||||
],
|
||||
"properties": {
|
||||
"get_state": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -352,6 +407,39 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_dealing_status"
|
||||
],
|
||||
"properties": {
|
||||
"get_dealing_status": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"type": "string"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -361,10 +449,47 @@
|
||||
"get_dealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"idx"
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"idx": {
|
||||
"dealer": {
|
||||
"type": "string"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_dealings"
|
||||
],
|
||||
"properties": {
|
||||
"get_dealings": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"type": "string"
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
@@ -379,9 +504,11 @@
|
||||
},
|
||||
"start_after": {
|
||||
"type": [
|
||||
"string",
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -425,6 +552,20 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Gets the stored contract version information that's required by the CW2 spec interface for migrations.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_cw2_contract_version"
|
||||
],
|
||||
"properties": {
|
||||
"get_cw2_contract_version": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -436,6 +577,26 @@
|
||||
},
|
||||
"sudo": null,
|
||||
"responses": {
|
||||
"get_c_w2_contract_version": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "ContractVersion",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"contract",
|
||||
"version"
|
||||
],
|
||||
"properties": {
|
||||
"contract": {
|
||||
"description": "contract is the crate name of the implementing contract, eg. `crate:cw20-base` we will use other prefixes for other languages, and their standard global namespacing",
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"description": "version is any string that this implementation knows. It may be simple counter \"1\", \"2\". or semantic version on release tags \"v0.7.0\", or some custom feature flag list. the only code that needs to understand the version parsing is code that knows how to migrate from the given contract (and is tied to it's implementation somehow)",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"get_current_dealers": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "PagedDealerResponse",
|
||||
@@ -480,7 +641,8 @@
|
||||
"address",
|
||||
"announce_address",
|
||||
"assigned_index",
|
||||
"bte_public_key_with_proof"
|
||||
"bte_public_key_with_proof",
|
||||
"ed25519_identity"
|
||||
],
|
||||
"properties": {
|
||||
"address": {
|
||||
@@ -496,6 +658,9 @@
|
||||
},
|
||||
"bte_public_key_with_proof": {
|
||||
"type": "string"
|
||||
},
|
||||
"ed25519_identity": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -508,7 +673,6 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"epoch_id",
|
||||
"finish_timestamp",
|
||||
"state",
|
||||
"time_configuration"
|
||||
],
|
||||
@@ -519,7 +683,14 @@
|
||||
"minimum": 0.0
|
||||
},
|
||||
"finish_timestamp": {
|
||||
"$ref": "#/definitions/Timestamp"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Timestamp"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"state": {
|
||||
"$ref": "#/definitions/EpochState"
|
||||
@@ -535,6 +706,7 @@
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"waiting_initialisation",
|
||||
"in_progress"
|
||||
]
|
||||
},
|
||||
@@ -744,7 +916,8 @@
|
||||
"address",
|
||||
"announce_address",
|
||||
"assigned_index",
|
||||
"bte_public_key_with_proof"
|
||||
"bte_public_key_with_proof",
|
||||
"ed25519_identity"
|
||||
],
|
||||
"properties": {
|
||||
"address": {
|
||||
@@ -760,6 +933,9 @@
|
||||
},
|
||||
"bte_public_key_with_proof": {
|
||||
"type": "string"
|
||||
},
|
||||
"ed25519_identity": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -776,34 +952,36 @@
|
||||
},
|
||||
"get_dealing": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "PagedDealingsResponse",
|
||||
"title": "DealingResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealings",
|
||||
"per_page"
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ContractDealing"
|
||||
}
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"per_page": {
|
||||
"type": "integer",
|
||||
"format": "uint",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"start_next_after": {
|
||||
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
|
||||
"dealing": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Addr"
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
@@ -812,21 +990,91 @@
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"ContractDealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealing"
|
||||
"ContractSafeBytes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer",
|
||||
"format": "uint8",
|
||||
"minimum": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_dealing_status": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "DealingStatusResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"dealing_submitted",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"dealing_submitted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_dealings": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "PagedDealingsResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealings",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/PartialContractDealing"
|
||||
}
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"start_next_after": {
|
||||
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealing": {
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"ContractSafeBytes": {
|
||||
"type": "array",
|
||||
@@ -835,6 +1083,24 @@
|
||||
"format": "uint8",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"PartialContractDealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"data",
|
||||
"index"
|
||||
],
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
},
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -921,7 +1187,8 @@
|
||||
"address",
|
||||
"announce_address",
|
||||
"assigned_index",
|
||||
"bte_public_key_with_proof"
|
||||
"bte_public_key_with_proof",
|
||||
"ed25519_identity"
|
||||
],
|
||||
"properties": {
|
||||
"address": {
|
||||
@@ -937,12 +1204,58 @@
|
||||
},
|
||||
"bte_public_key_with_proof": {
|
||||
"type": "string"
|
||||
},
|
||||
"ed25519_identity": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_state": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "State",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"group_addr",
|
||||
"key_size",
|
||||
"mix_denom",
|
||||
"multisig_addr"
|
||||
],
|
||||
"properties": {
|
||||
"group_addr": {
|
||||
"$ref": "#/definitions/Cw4Contract"
|
||||
},
|
||||
"key_size": {
|
||||
"description": "Specifies the number of elements in the derived keys",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"mix_denom": {
|
||||
"type": "string"
|
||||
},
|
||||
"multisig_addr": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"Cw4Contract": {
|
||||
"description": "Cw4Contract is a wrapper around Addr that provides a lot of helpers for working with cw4 contracts\n\nIf you wish to persist this, convert to Cw4CanonicalContract via .canonical()",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Addr"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_verification_keys": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "PagedVKSharesResponse",
|
||||
|
||||
@@ -2,6 +2,19 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "ExecuteMsg",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"initiate_dkg"
|
||||
],
|
||||
"properties": {
|
||||
"initiate_dkg": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -13,6 +26,7 @@
|
||||
"required": [
|
||||
"announce_address",
|
||||
"bte_key_with_proof",
|
||||
"identity_key",
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
@@ -22,6 +36,9 @@
|
||||
"bte_key_with_proof": {
|
||||
"type": "string"
|
||||
},
|
||||
"identity_key": {
|
||||
"type": "string"
|
||||
},
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
}
|
||||
@@ -40,12 +57,12 @@
|
||||
"commit_dealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealing_bytes",
|
||||
"dealing",
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
"dealing_bytes": {
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
"dealing": {
|
||||
"$ref": "#/definitions/PartialContractDealing"
|
||||
},
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
@@ -145,6 +162,24 @@
|
||||
"format": "uint8",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"PartialContractDealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"data",
|
||||
"index"
|
||||
],
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
},
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"group_addr",
|
||||
"key_size",
|
||||
"mix_denom",
|
||||
"multisig_addr"
|
||||
],
|
||||
@@ -11,6 +12,12 @@
|
||||
"group_addr": {
|
||||
"type": "string"
|
||||
},
|
||||
"key_size": {
|
||||
"description": "Specifies the number of elements in the derived keys",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"mix_denom": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -2,6 +2,19 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "QueryMsg",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_state"
|
||||
],
|
||||
"properties": {
|
||||
"get_state": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -120,6 +133,39 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_dealing_status"
|
||||
],
|
||||
"properties": {
|
||||
"get_dealing_status": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"type": "string"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -129,10 +175,47 @@
|
||||
"get_dealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"idx"
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"idx": {
|
||||
"dealer": {
|
||||
"type": "string"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_dealings"
|
||||
],
|
||||
"properties": {
|
||||
"get_dealings": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"type": "string"
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
@@ -147,9 +230,11 @@
|
||||
},
|
||||
"start_after": {
|
||||
"type": [
|
||||
"string",
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -193,6 +278,20 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Gets the stored contract version information that's required by the CW2 spec interface for migrations.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_cw2_contract_version"
|
||||
],
|
||||
"properties": {
|
||||
"get_cw2_contract_version": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "ContractVersion",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"contract",
|
||||
"version"
|
||||
],
|
||||
"properties": {
|
||||
"contract": {
|
||||
"description": "contract is the crate name of the implementing contract, eg. `crate:cw20-base` we will use other prefixes for other languages, and their standard global namespacing",
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"description": "version is any string that this implementation knows. It may be simple counter \"1\", \"2\". or semantic version on release tags \"v0.7.0\", or some custom feature flag list. the only code that needs to understand the version parsing is code that knows how to migrate from the given contract (and is tied to it's implementation somehow)",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -42,7 +42,8 @@
|
||||
"address",
|
||||
"announce_address",
|
||||
"assigned_index",
|
||||
"bte_public_key_with_proof"
|
||||
"bte_public_key_with_proof",
|
||||
"ed25519_identity"
|
||||
],
|
||||
"properties": {
|
||||
"address": {
|
||||
@@ -58,6 +59,9 @@
|
||||
},
|
||||
"bte_public_key_with_proof": {
|
||||
"type": "string"
|
||||
},
|
||||
"ed25519_identity": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"epoch_id",
|
||||
"finish_timestamp",
|
||||
"state",
|
||||
"time_configuration"
|
||||
],
|
||||
@@ -15,7 +14,14 @@
|
||||
"minimum": 0.0
|
||||
},
|
||||
"finish_timestamp": {
|
||||
"$ref": "#/definitions/Timestamp"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Timestamp"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"state": {
|
||||
"$ref": "#/definitions/EpochState"
|
||||
@@ -31,6 +37,7 @@
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"waiting_initialisation",
|
||||
"in_progress"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -32,7 +32,8 @@
|
||||
"address",
|
||||
"announce_address",
|
||||
"assigned_index",
|
||||
"bte_public_key_with_proof"
|
||||
"bte_public_key_with_proof",
|
||||
"ed25519_identity"
|
||||
],
|
||||
"properties": {
|
||||
"address": {
|
||||
@@ -48,6 +49,9 @@
|
||||
},
|
||||
"bte_public_key_with_proof": {
|
||||
"type": "string"
|
||||
},
|
||||
"ed25519_identity": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -1,33 +1,35 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "PagedDealingsResponse",
|
||||
"title": "DealingResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealings",
|
||||
"per_page"
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ContractDealing"
|
||||
}
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"per_page": {
|
||||
"type": "integer",
|
||||
"format": "uint",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"start_next_after": {
|
||||
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
|
||||
"dealing": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Addr"
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
@@ -36,22 +38,6 @@
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"ContractDealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealing"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealing": {
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ContractSafeBytes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "DealingStatusResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"dealing_submitted",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"dealing_submitted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "PagedDealingsResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealings",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/PartialContractDealing"
|
||||
}
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"start_next_after": {
|
||||
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"ContractSafeBytes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer",
|
||||
"format": "uint8",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"PartialContractDealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"data",
|
||||
"index"
|
||||
],
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
},
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,8 @@
|
||||
"address",
|
||||
"announce_address",
|
||||
"assigned_index",
|
||||
"bte_public_key_with_proof"
|
||||
"bte_public_key_with_proof",
|
||||
"ed25519_identity"
|
||||
],
|
||||
"properties": {
|
||||
"address": {
|
||||
@@ -58,6 +59,9 @@
|
||||
},
|
||||
"bte_public_key_with_proof": {
|
||||
"type": "string"
|
||||
},
|
||||
"ed25519_identity": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "State",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"group_addr",
|
||||
"key_size",
|
||||
"mix_denom",
|
||||
"multisig_addr"
|
||||
],
|
||||
"properties": {
|
||||
"group_addr": {
|
||||
"$ref": "#/definitions/Cw4Contract"
|
||||
},
|
||||
"key_size": {
|
||||
"description": "Specifies the number of elements in the derived keys",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"mix_denom": {
|
||||
"type": "string"
|
||||
},
|
||||
"multisig_addr": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"Cw4Contract": {
|
||||
"description": "Cw4Contract is a wrapper around Addr that provides a lot of helpers for working with cw4 contracts\n\nIf you wish to persist this, convert to Cw4CanonicalContract via .canonical()",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Addr"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,22 @@
|
||||
// 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::queries::{
|
||||
query_current_dealers_paged, query_dealer_details, query_past_dealers_paged,
|
||||
};
|
||||
use crate::dealers::transactions::try_add_dealer;
|
||||
use crate::dealings::queries::query_dealings_paged;
|
||||
use crate::dealings::queries::{query_dealing, query_dealing_status, query_dealings_paged};
|
||||
use crate::dealings::transactions::try_commit_dealings;
|
||||
use crate::epoch_state::queries::{
|
||||
query_current_epoch, query_current_epoch_threshold, query_initial_dealers,
|
||||
};
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
use crate::epoch_state::transactions::{advance_epoch_state, try_surpassed_threshold};
|
||||
use crate::epoch_state::transactions::{
|
||||
advance_epoch_state, try_initiate_dkg, try_surpassed_threshold,
|
||||
};
|
||||
use crate::error::ContractError;
|
||||
use crate::state::{State, MULTISIG, STATE};
|
||||
use crate::state::queries::query_state;
|
||||
use crate::state::storage::{DKG_ADMIN, MULTISIG, STATE};
|
||||
use crate::verification_key_shares::queries::query_vk_shares_paged;
|
||||
use crate::verification_key_shares::transactions::try_commit_verification_key_share;
|
||||
use crate::verification_key_shares::transactions::try_verify_verification_key_share;
|
||||
@@ -22,7 +25,11 @@ use cosmwasm_std::{
|
||||
};
|
||||
use cw4::Cw4Contract;
|
||||
use nym_coconut_dkg_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
|
||||
use nym_coconut_dkg_common::types::{Epoch, EpochState};
|
||||
use nym_coconut_dkg_common::types::{Epoch, EpochState, State};
|
||||
use semver::Version;
|
||||
|
||||
const CONTRACT_NAME: &str = "crate:nym-coconut-dkg";
|
||||
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
/// Instantiate the contract.
|
||||
///
|
||||
@@ -33,13 +40,15 @@ use nym_coconut_dkg_common::types::{Epoch, EpochState};
|
||||
pub fn instantiate(
|
||||
mut deps: DepsMut<'_>,
|
||||
env: Env,
|
||||
_info: MessageInfo,
|
||||
info: MessageInfo,
|
||||
msg: InstantiateMsg,
|
||||
) -> Result<Response, ContractError> {
|
||||
let multisig_addr = deps.api.addr_validate(&msg.multisig_addr)?;
|
||||
MULTISIG.set(deps.branch(), Some(multisig_addr.clone()))?;
|
||||
|
||||
let group_addr = Cw4Contract(deps.api.addr_validate(&msg.group_addr).map_err(|_| {
|
||||
DKG_ADMIN.set(deps.branch(), Some(info.sender))?;
|
||||
|
||||
let group_addr = Cw4Contract::new(deps.api.addr_validate(&msg.group_addr).map_err(|_| {
|
||||
ContractError::InvalidGroup {
|
||||
addr: msg.group_addr.clone(),
|
||||
}
|
||||
@@ -49,19 +58,22 @@ pub fn instantiate(
|
||||
group_addr,
|
||||
multisig_addr,
|
||||
mix_denom: msg.mix_denom,
|
||||
key_size: msg.key_size,
|
||||
};
|
||||
STATE.save(deps.storage, &state)?;
|
||||
|
||||
CURRENT_EPOCH.save(
|
||||
deps.storage,
|
||||
&Epoch::new(
|
||||
EpochState::default(),
|
||||
EpochState::WaitingInitialisation,
|
||||
0,
|
||||
msg.time_configuration.unwrap_or_default(),
|
||||
env.block.time,
|
||||
),
|
||||
)?;
|
||||
|
||||
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
|
||||
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
@@ -74,15 +86,23 @@ pub fn execute(
|
||||
msg: ExecuteMsg,
|
||||
) -> Result<Response, ContractError> {
|
||||
match msg {
|
||||
ExecuteMsg::InitiateDkg {} => try_initiate_dkg(deps, env, info),
|
||||
ExecuteMsg::RegisterDealer {
|
||||
bte_key_with_proof,
|
||||
identity_key,
|
||||
announce_address,
|
||||
resharing,
|
||||
} => try_add_dealer(deps, info, bte_key_with_proof, announce_address, resharing),
|
||||
ExecuteMsg::CommitDealing {
|
||||
dealing_bytes,
|
||||
} => try_add_dealer(
|
||||
deps,
|
||||
info,
|
||||
bte_key_with_proof,
|
||||
identity_key,
|
||||
announce_address,
|
||||
resharing,
|
||||
} => try_commit_dealings(deps, info, dealing_bytes, resharing),
|
||||
),
|
||||
ExecuteMsg::CommitDealing { dealing, resharing } => {
|
||||
try_commit_dealings(deps, info, dealing, resharing)
|
||||
}
|
||||
ExecuteMsg::CommitVerificationKeyShare { share, resharing } => {
|
||||
try_commit_verification_key_share(deps, env, info, share, resharing)
|
||||
}
|
||||
@@ -97,6 +117,7 @@ pub fn execute(
|
||||
#[entry_point]
|
||||
pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> Result<QueryResponse, ContractError> {
|
||||
let response = match msg {
|
||||
QueryMsg::GetState {} => to_binary(&query_state(deps.storage)?)?,
|
||||
QueryMsg::GetCurrentEpochState {} => to_binary(&query_current_epoch(deps.storage)?)?,
|
||||
QueryMsg::GetCurrentEpochThreshold {} => {
|
||||
to_binary(&query_current_epoch_threshold(deps.storage)?)?
|
||||
@@ -111,24 +132,67 @@ 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::GetDealingStatus {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
} => to_binary(&query_dealing_status(
|
||||
deps,
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
)?)?,
|
||||
QueryMsg::GetDealing {
|
||||
idx,
|
||||
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(deps, idx, start_after, limit)?)?,
|
||||
} => to_binary(&query_dealings_paged(
|
||||
deps,
|
||||
epoch_id,
|
||||
dealer,
|
||||
start_after,
|
||||
limit,
|
||||
)?)?,
|
||||
QueryMsg::GetVerificationKeys {
|
||||
epoch_id,
|
||||
limit,
|
||||
start_after,
|
||||
} => to_binary(&query_vk_shares_paged(deps, epoch_id, start_after, limit)?)?,
|
||||
QueryMsg::GetCW2ContractVersion {} => to_binary(&cw2::get_contract_version(deps.storage)?)?,
|
||||
};
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
Ok(Default::default())
|
||||
pub fn migrate(deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
fn parse_semver(raw: &str) -> Result<Version, ContractError> {
|
||||
raw.parse()
|
||||
.map_err(|error: semver::Error| ContractError::SemVerFailure {
|
||||
value: CONTRACT_VERSION.to_string(),
|
||||
error_message: error.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
// Note: don't remove this particular bit of code as we have to ALWAYS check whether we have to
|
||||
// update the stored version
|
||||
let build_version: Version = parse_semver(CONTRACT_VERSION)?;
|
||||
let stored_version: Version = parse_semver(&cw2::get_contract_version(deps.storage)?.version)?;
|
||||
|
||||
if stored_version < build_version {
|
||||
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
|
||||
|
||||
// If state structure changed in any contract version in the way migration is needed, it
|
||||
// should occur here, for example anything from `crate::queued_migrations::`
|
||||
}
|
||||
|
||||
Ok(Response::new())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -140,8 +204,8 @@ mod tests {
|
||||
use cosmwasm_std::{coins, Addr};
|
||||
use cw4::Member;
|
||||
use cw_multi_test::{App, AppBuilder, AppResponse, ContractWrapper, Executor};
|
||||
use nym_coconut_dkg_common::msg::ExecuteMsg::RegisterDealer;
|
||||
use nym_coconut_dkg_common::types::NodeIndex;
|
||||
use nym_coconut_dkg_common::msg::ExecuteMsg::{InitiateDkg, RegisterDealer};
|
||||
use nym_coconut_dkg_common::types::{NodeIndex, DEFAULT_DEALINGS};
|
||||
use nym_group_contract_common::msg::InstantiateMsg as GroupInstantiateMsg;
|
||||
|
||||
fn instantiate_with_group(app: &mut App, members: &[Addr]) -> Addr {
|
||||
@@ -178,6 +242,7 @@ mod tests {
|
||||
multisig_addr: MULTISIG_CONTRACT.to_string(),
|
||||
time_configuration: None,
|
||||
mix_denom: TEST_MIX_DENOM.to_string(),
|
||||
key_size: DEFAULT_DEALINGS as u32,
|
||||
};
|
||||
app.instantiate_contract(
|
||||
coconut_dkg_code_id,
|
||||
@@ -213,6 +278,7 @@ mod tests {
|
||||
multisig_addr: "multisig_addr".to_string(),
|
||||
time_configuration: None,
|
||||
mix_denom: "nym".to_string(),
|
||||
key_size: 5,
|
||||
};
|
||||
let info = mock_info("creator", &[]);
|
||||
|
||||
@@ -235,6 +301,14 @@ mod tests {
|
||||
});
|
||||
let coconut_dkg_contract_addr = instantiate_with_group(&mut app, &members);
|
||||
|
||||
app.execute_contract(
|
||||
Addr::unchecked(ADMIN_ADDRESS),
|
||||
coconut_dkg_contract_addr.clone(),
|
||||
&InitiateDkg {},
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
for (idx, member) in members.iter().enumerate() {
|
||||
let res = app
|
||||
.execute_contract(
|
||||
@@ -242,6 +316,7 @@ mod tests {
|
||||
coconut_dkg_contract_addr.clone(),
|
||||
&RegisterDealer {
|
||||
bte_key_with_proof: "bte_key_with_proof".to_string(),
|
||||
identity_key: "identity".to_string(),
|
||||
announce_address: "127.0.0.1:8000".to_string(),
|
||||
resharing: false,
|
||||
},
|
||||
@@ -256,6 +331,7 @@ mod tests {
|
||||
coconut_dkg_contract_addr.clone(),
|
||||
&RegisterDealer {
|
||||
bte_key_with_proof: "bte_key_with_proof".to_string(),
|
||||
identity_key: "identity".to_string(),
|
||||
announce_address: "127.0.0.1:8000".to_string(),
|
||||
resharing: false,
|
||||
},
|
||||
@@ -272,6 +348,7 @@ mod tests {
|
||||
coconut_dkg_contract_addr,
|
||||
&RegisterDealer {
|
||||
bte_key_with_proof: "bte_key_with_proof".to_string(),
|
||||
identity_key: "identity".to_string(),
|
||||
announce_address: "127.0.0.1:8000".to_string(),
|
||||
resharing: false,
|
||||
},
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::dealers::storage as dealers_storage;
|
||||
use crate::epoch_state::storage::INITIAL_REPLACEMENT_DATA;
|
||||
use crate::epoch_state::utils::check_epoch_state;
|
||||
use crate::error::ContractError;
|
||||
use crate::state::STATE;
|
||||
use crate::state::storage::STATE;
|
||||
use cosmwasm_std::{Addr, DepsMut, MessageInfo, Response};
|
||||
use nym_coconut_dkg_common::types::{DealerDetails, EncodedBTEPublicKeyWithProof, EpochState};
|
||||
|
||||
@@ -38,6 +38,7 @@ pub fn try_add_dealer(
|
||||
mut deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
bte_key_with_proof: EncodedBTEPublicKeyWithProof,
|
||||
identity_key: String,
|
||||
announce_address: String,
|
||||
resharing: bool,
|
||||
) -> Result<Response, ContractError> {
|
||||
@@ -65,6 +66,7 @@ pub fn try_add_dealer(
|
||||
let dealer_details = DealerDetails {
|
||||
address: info.sender.clone(),
|
||||
bte_public_key_with_proof: bte_key_with_proof,
|
||||
ed25519_identity: identity_key,
|
||||
announce_address,
|
||||
assigned_index: node_index,
|
||||
};
|
||||
@@ -77,10 +79,10 @@ pub fn try_add_dealer(
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::dealers::storage::current_dealers;
|
||||
use crate::epoch_state::transactions::advance_epoch_state;
|
||||
use crate::epoch_state::transactions::{advance_epoch_state, try_initiate_dkg};
|
||||
use crate::support::tests::fixtures::dealer_details_fixture;
|
||||
use crate::support::tests::helpers;
|
||||
use crate::support::tests::helpers::{add_fixture_dealer, GROUP_MEMBERS};
|
||||
use crate::support::tests::helpers::{add_fixture_dealer, ADMIN_ADDRESS, GROUP_MEMBERS};
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cw4::Member;
|
||||
use nym_coconut_dkg_common::types::{InitialReplacementData, TimeConfiguration};
|
||||
@@ -137,10 +139,13 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn invalid_state() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let owner = Addr::unchecked("owner");
|
||||
let mut env = mock_env();
|
||||
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
|
||||
let owner = Addr::unchecked("owner");
|
||||
let info = mock_info(owner.as_str(), &[]);
|
||||
let bte_key_with_proof = String::from("bte_key_with_proof");
|
||||
let identity = String::from("identity");
|
||||
let announce_address = String::from("localhost:8000");
|
||||
|
||||
env.block.time = env
|
||||
@@ -155,6 +160,7 @@ pub(crate) mod tests {
|
||||
deps.as_mut(),
|
||||
info,
|
||||
bte_key_with_proof,
|
||||
identity,
|
||||
announce_address,
|
||||
false,
|
||||
)
|
||||
@@ -163,7 +169,7 @@ pub(crate) mod tests {
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
current_state: EpochState::DealingExchange { resharing: false }.to_string(),
|
||||
expected_state: EpochState::default().to_string(),
|
||||
expected_state: EpochState::PublicKeySubmission { resharing: false }.to_string(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,44 +2,74 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::dealings::storage;
|
||||
use crate::dealings::storage::DEALINGS_BYTES;
|
||||
use cosmwasm_std::{Deps, Order, StdResult};
|
||||
use crate::dealings::storage::StoredDealing;
|
||||
use cosmwasm_std::{Deps, StdResult};
|
||||
use cw_storage_plus::Bound;
|
||||
use nym_coconut_dkg_common::dealer::{ContractDealing, PagedDealingsResponse};
|
||||
use nym_coconut_dkg_common::types::TOTAL_DEALINGS;
|
||||
use nym_coconut_dkg_common::dealer::{
|
||||
DealingResponse, DealingStatusResponse, PagedDealingsResponse,
|
||||
};
|
||||
use nym_coconut_dkg_common::types::{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
|
||||
pub fn query_dealing_status(
|
||||
deps: Deps<'_>,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
) -> StdResult<DealingStatusResponse> {
|
||||
let dealer = deps.api.addr_validate(&dealer)?;
|
||||
let dealing_submitted = StoredDealing::exists(deps.storage, epoch_id, &dealer, dealing_index);
|
||||
|
||||
Ok(DealingStatusResponse {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
dealing_submitted,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn query_dealing(
|
||||
deps: Deps<'_>,
|
||||
epoch_id: EpochId,
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn query_dealings_paged(
|
||||
deps: Deps<'_>,
|
||||
idx: u64,
|
||||
start_after: Option<String>,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
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) as usize;
|
||||
.min(storage::DEALINGS_PAGE_MAX_LIMIT);
|
||||
|
||||
let idx = idx as usize;
|
||||
if idx >= TOTAL_DEALINGS {
|
||||
return Ok(PagedDealingsResponse::new(vec![], limit, None));
|
||||
}
|
||||
let dealer = deps.api.addr_validate(&dealer)?;
|
||||
let start = start_after.map(Bound::exclusive);
|
||||
|
||||
let addr = start_after
|
||||
.map(|addr| deps.api.addr_validate(&addr))
|
||||
.transpose()?;
|
||||
|
||||
let start = addr.as_ref().map(Bound::exclusive);
|
||||
|
||||
let dealings = DEALINGS_BYTES[idx]
|
||||
.range(deps.storage, start, None, Order::Ascending)
|
||||
.take(limit)
|
||||
.map(|res| res.map(|(dealer, dealing)| ContractDealing::new(dealing, dealer)))
|
||||
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.dealer.clone());
|
||||
let start_next_after = dealings.last().map(|dealing| dealing.index);
|
||||
|
||||
Ok(PagedDealingsResponse::new(
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealings,
|
||||
limit,
|
||||
start_next_after,
|
||||
))
|
||||
}
|
||||
@@ -48,148 +78,229 @@ pub fn query_dealings_paged(
|
||||
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;
|
||||
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<'_>, size: usize) {
|
||||
for n in 0..size {
|
||||
let dealing_share = dealing_bytes_fixture();
|
||||
let sender = Addr::unchecked(format!("owner{}", n));
|
||||
(0..TOTAL_DEALINGS).for_each(|idx| {
|
||||
DEALINGS_BYTES[idx]
|
||||
.save(deps.storage, &sender, &dealing_share)
|
||||
.unwrap();
|
||||
});
|
||||
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 empty_on_bad_idx() {
|
||||
let mut deps = init_contract();
|
||||
fill_dealings(deps.as_mut(), 1000);
|
||||
|
||||
for idx in TOTAL_DEALINGS as u64..100 * TOTAL_DEALINGS as u64 {
|
||||
let page1 = query_dealings_paged(deps.as_ref(), idx, None, None).unwrap();
|
||||
assert_eq!(0, page1.dealings.len() as u32);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dealings_empty_on_init() {
|
||||
let deps = init_contract();
|
||||
for idx in 0..TOTAL_DEALINGS as u64 {
|
||||
let response = query_dealings_paged(deps.as_ref(), idx, None, Option::from(2)).unwrap();
|
||||
assert_eq!(0, response.dealings.len());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dealings_paged_retrieval_obeys_limits() {
|
||||
let mut deps = init_contract();
|
||||
let limit = 2;
|
||||
fill_dealings(deps.as_mut(), 1000);
|
||||
|
||||
for idx in 0..TOTAL_DEALINGS as u64 {
|
||||
let page1 =
|
||||
query_dealings_paged(deps.as_ref(), idx, 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(), 1000);
|
||||
|
||||
for idx in 0..TOTAL_DEALINGS as u64 {
|
||||
// query without explicitly setting a limit
|
||||
let page1 = query_dealings_paged(deps.as_ref(), idx, 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(), 1000);
|
||||
|
||||
// query with a crazily high limit in an attempt to use too many resources
|
||||
let crazy_limit = 1000 * DEALINGS_PAGE_MAX_LIMIT;
|
||||
for idx in 0..TOTAL_DEALINGS as u64 {
|
||||
let page1 =
|
||||
query_dealings_paged(deps.as_ref(), idx, 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() {
|
||||
fn test_query_dealing() {
|
||||
let mut deps = init_contract();
|
||||
|
||||
fill_dealings(deps.as_mut(), 1);
|
||||
let bad_address = "FOOMP".to_string();
|
||||
assert!(query_dealing(deps.as_ref(), 0, bad_address, 0).is_err());
|
||||
|
||||
let per_page = 2;
|
||||
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());
|
||||
|
||||
for idx in 0..TOTAL_DEALINGS as u64 {
|
||||
let page1 =
|
||||
query_dealings_paged(deps.as_ref(), idx, None, Option::from(per_page)).unwrap();
|
||||
// insert the dealing
|
||||
let dealing = partial_dealing_fixture();
|
||||
StoredDealing::save(
|
||||
deps.as_mut().storage,
|
||||
0,
|
||||
&Addr::unchecked("foo"),
|
||||
dealing.clone(),
|
||||
);
|
||||
|
||||
// page should have 1 result on it
|
||||
assert_eq!(1, page1.dealings.len());
|
||||
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())
|
||||
}
|
||||
|
||||
// save another
|
||||
fill_dealings(deps.as_mut(), 2);
|
||||
#[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 idx in 0..TOTAL_DEALINGS as u64 {
|
||||
// page1 should have 2 results on it
|
||||
let page1 =
|
||||
query_dealings_paged(deps.as_ref(), idx, None, Option::from(per_page)).unwrap();
|
||||
assert_eq!(2, page1.dealings.len());
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
fill_dealings(deps.as_mut(), 3);
|
||||
#[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 idx in 0..TOTAL_DEALINGS as u64 {
|
||||
// page1 still has 2 results
|
||||
let page1 =
|
||||
query_dealings_paged(deps.as_ref(), idx, None, Option::from(per_page)).unwrap();
|
||||
assert_eq!(2, page1.dealings.len());
|
||||
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();
|
||||
|
||||
// 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(),
|
||||
idx,
|
||||
Option::from(start_after.to_string()),
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(1, page2.dealings.len());
|
||||
assert_eq!(DEALINGS_PAGE_DEFAULT_LIMIT, page1.dealings.len() as u32);
|
||||
}
|
||||
}
|
||||
|
||||
fill_dealings(deps.as_mut(), 4);
|
||||
#[test]
|
||||
fn dealings_paged_retrieval_has_max_limit() {
|
||||
let mut deps = init_contract();
|
||||
fill_dealings(deps.as_mut(), 0, 10, DEFAULT_DEALINGS as u32);
|
||||
|
||||
for idx in 0..TOTAL_DEALINGS as u64 {
|
||||
let page1 =
|
||||
query_dealings_paged(deps.as_ref(), idx, None, Option::from(per_page)).unwrap();
|
||||
let start_after = page1.start_next_after.unwrap();
|
||||
let page2 = query_dealings_paged(
|
||||
deps.as_ref(),
|
||||
idx,
|
||||
Option::from(start_after.to_string()),
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
// 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();
|
||||
|
||||
// now we have 2 pages, with 2 results on the second page
|
||||
assert_eq!(2, page2.dealings.len());
|
||||
// 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,33 +1,293 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::Addr;
|
||||
use cw_storage_plus::Map;
|
||||
use nym_coconut_dkg_common::types::{ContractSafeBytes, TOTAL_DEALINGS};
|
||||
use cosmwasm_std::{Addr, Order, Record, StdResult, Storage};
|
||||
use cw_storage_plus::{Bound, Key, KeyDeserialize, Path, Prefix, Prefixer, PrimaryKey};
|
||||
use nym_coconut_dkg_common::types::{
|
||||
ContractDealing, ContractSafeBytes, DealingIndex, EpochId, PartialContractDealing,
|
||||
};
|
||||
|
||||
pub(crate) const DEALINGS_PAGE_MAX_LIMIT: u32 = 2;
|
||||
pub(crate) const DEALINGS_PAGE_DEFAULT_LIMIT: u32 = 1;
|
||||
|
||||
type DealingKey<'a> = &'a Addr;
|
||||
type Dealer<'a> = &'a Addr;
|
||||
|
||||
// Note to whoever is looking at this implementation and is thinking of using something similar
|
||||
// for storing small commitments/hashes of data on chain:
|
||||
// If there's a lot of entries you want to store thinking, "oh, this digest is only 32 bytes, it's not that much",
|
||||
// the default cosmwasm' serializer will bloat it to around ~100B. So you really don't want to be using
|
||||
// Buckets/Maps, etc. for that purpose. Instead you want to use `storage` directly (look into the actual implementation of
|
||||
// `Map` or `Bucket` to see what I mean. Instead of using the `to_vec` method on serde_json_wasm, you'd
|
||||
// provide your data directly yourself.
|
||||
// but you must be extremely careful when doing so, as you might end up overwriting some existing data
|
||||
// if you don't choose your prefixes wisely.
|
||||
// I didn't have to do it here as I'm storing relatively little data and after just base58-encoding
|
||||
// my bytes, I was fine with the json overhead.
|
||||
// dealings are stored in a multilevel map with the following hierarchy:
|
||||
// - epoch-id:
|
||||
// - issuer-address:
|
||||
// - dealing 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!
|
||||
|
||||
// if TOTAL_DEALINGS is modified to anything other then current value (5), this part will also need
|
||||
// to be modified
|
||||
pub(crate) const DEALINGS_BYTES: [Map<'_, DealingKey<'_>, ContractSafeBytes>; TOTAL_DEALINGS] = [
|
||||
Map::new("dbyt1"),
|
||||
Map::new("dbyt2"),
|
||||
Map::new("dbyt3"),
|
||||
Map::new("dbyt4"),
|
||||
Map::new("dbyt5"),
|
||||
];
|
||||
pub(crate) struct StoredDealing;
|
||||
|
||||
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 storage_key(
|
||||
epoch_id: EpochId,
|
||||
dealer: Dealer,
|
||||
dealing_index: DealingIndex,
|
||||
) -> Path<Vec<u8>> {
|
||||
// just replicate the behaviour from `Map::key`
|
||||
let key = (epoch_id, dealer, dealing_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"),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn exists(
|
||||
storage: &dyn Storage,
|
||||
epoch_id: EpochId,
|
||||
dealer: &Addr,
|
||||
dealing_index: DealingIndex,
|
||||
) -> bool {
|
||||
StoredDealing::storage_key(epoch_id, dealer, dealing_index).has(storage)
|
||||
}
|
||||
|
||||
pub(crate) fn save(
|
||||
storage: &mut dyn Storage,
|
||||
epoch_id: EpochId,
|
||||
dealer: Dealer,
|
||||
dealing: PartialContractDealing,
|
||||
) {
|
||||
// NOTE: we're storing bytes directly here!
|
||||
let storage_key = StoredDealing::storage_key(epoch_id, dealer, dealing.index);
|
||||
storage.set(&storage_key, dealing.data.as_slice());
|
||||
}
|
||||
|
||||
pub(crate) fn read(
|
||||
storage: &dyn Storage,
|
||||
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)
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,18 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::dealers::storage as dealers_storage;
|
||||
use crate::dealings::storage::DEALINGS_BYTES;
|
||||
use crate::epoch_state::storage::INITIAL_REPLACEMENT_DATA;
|
||||
use crate::dealings::storage::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::{ContractSafeBytes, EpochState};
|
||||
use nym_coconut_dkg_common::types::{EpochState, PartialContractDealing};
|
||||
|
||||
pub fn try_commit_dealings(
|
||||
deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
dealing_bytes: ContractSafeBytes,
|
||||
dealing: PartialContractDealing,
|
||||
resharing: bool,
|
||||
) -> Result<Response, ContractError> {
|
||||
check_epoch_state(deps.storage, EpochState::DealingExchange { resharing })?;
|
||||
@@ -32,47 +33,65 @@ pub fn try_commit_dealings(
|
||||
return Err(ContractError::NotAnInitialDealer);
|
||||
}
|
||||
|
||||
// check if this dealer has already committed to all dealings
|
||||
// (we don't want to allow overwriting anything)
|
||||
for dealings in DEALINGS_BYTES {
|
||||
if !dealings.has(deps.storage, &info.sender) {
|
||||
dealings.save(deps.storage, &info.sender, &dealing_bytes)?;
|
||||
return Ok(Response::default());
|
||||
}
|
||||
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
|
||||
// note: dealing indexing starts from 0
|
||||
if dealing.index >= state.key_size {
|
||||
return Err(ContractError::DealingOutOfRange {
|
||||
epoch_id: epoch.epoch_id,
|
||||
dealer: info.sender,
|
||||
index: dealing.index,
|
||||
key_size: state.key_size,
|
||||
});
|
||||
}
|
||||
|
||||
Err(ContractError::AlreadyCommitted {
|
||||
commitment: String::from("dealing"),
|
||||
})
|
||||
// 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 {
|
||||
epoch_id: epoch.epoch_id,
|
||||
dealer: info.sender,
|
||||
index: dealing.index,
|
||||
});
|
||||
}
|
||||
|
||||
StoredDealing::save(deps.storage, epoch.epoch_id, &info.sender, dealing);
|
||||
|
||||
Ok(Response::new())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
use crate::epoch_state::transactions::advance_epoch_state;
|
||||
use crate::support::tests::fixtures::{dealer_details_fixture, dealing_bytes_fixture};
|
||||
use crate::epoch_state::transactions::{advance_epoch_state, try_initiate_dkg};
|
||||
use crate::support::tests::fixtures::{dealer_details_fixture, partial_dealing_fixture};
|
||||
use crate::support::tests::helpers;
|
||||
use crate::support::tests::helpers::add_fixture_dealer;
|
||||
use crate::support::tests::helpers::{add_fixture_dealer, ADMIN_ADDRESS};
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cosmwasm_std::Addr;
|
||||
use nym_coconut_dkg_common::dealer::DealerDetails;
|
||||
use nym_coconut_dkg_common::types::{InitialReplacementData, TimeConfiguration};
|
||||
use nym_coconut_dkg_common::types::{
|
||||
ContractSafeBytes, InitialReplacementData, TimeConfiguration, DEFAULT_DEALINGS,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn invalid_commit_dealing() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let owner = Addr::unchecked("owner1");
|
||||
let mut env = mock_env();
|
||||
let info = mock_info(owner.as_str(), &[]);
|
||||
let dealing_bytes = dealing_bytes_fixture();
|
||||
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
|
||||
let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone(), false)
|
||||
.unwrap_err();
|
||||
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::default().to_string(),
|
||||
current_state: EpochState::PublicKeySubmission { resharing: false }.to_string(),
|
||||
expected_state: EpochState::DealingExchange { resharing: false }.to_string()
|
||||
}
|
||||
);
|
||||
@@ -84,13 +103,14 @@ pub(crate) mod tests {
|
||||
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_bytes.clone(), false)
|
||||
.unwrap_err();
|
||||
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,
|
||||
};
|
||||
@@ -114,8 +134,8 @@ pub(crate) mod tests {
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone(), true)
|
||||
.unwrap_err();
|
||||
let ret =
|
||||
try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), true).unwrap_err();
|
||||
assert_eq!(ret, ContractError::NotAnInitialDealer);
|
||||
|
||||
INITIAL_REPLACEMENT_DATA
|
||||
@@ -125,18 +145,60 @@ pub(crate) mod tests {
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
for dealings in DEALINGS_BYTES {
|
||||
assert!(!dealings.has(deps.as_mut().storage, &owner));
|
||||
let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone(), true);
|
||||
assert!(ret.is_ok());
|
||||
assert!(dealings.has(deps.as_mut().storage, &owner));
|
||||
}
|
||||
let ret = try_commit_dealings(deps.as_mut(), info, dealing_bytes, true).unwrap_err();
|
||||
// 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::AlreadyCommitted {
|
||||
commitment: String::from("dealing"),
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,22 +27,29 @@ pub(crate) fn query_initial_dealers(
|
||||
#[cfg(test)]
|
||||
pub(crate) mod test {
|
||||
use super::*;
|
||||
use crate::support::tests::helpers::init_contract;
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
use crate::epoch_state::transactions::try_initiate_dkg;
|
||||
use crate::support::tests::helpers::{init_contract, ADMIN_ADDRESS};
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use nym_coconut_dkg_common::types::{EpochState, TimeConfiguration};
|
||||
|
||||
#[test]
|
||||
fn query_state() {
|
||||
let mut deps = init_contract();
|
||||
let epoch = query_current_epoch(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(epoch.state, EpochState::WaitingInitialisation);
|
||||
assert_eq!(epoch.finish_timestamp, None);
|
||||
|
||||
let env = mock_env();
|
||||
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
|
||||
let epoch = query_current_epoch(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
mock_env()
|
||||
.block
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs)
|
||||
);
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
// 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::{current_dealers, past_dealers};
|
||||
use crate::dealings::storage::DEALINGS_BYTES;
|
||||
use crate::epoch_state::storage::{CURRENT_EPOCH, INITIAL_REPLACEMENT_DATA, THRESHOLD};
|
||||
use crate::epoch_state::utils::check_epoch_state;
|
||||
use crate::error::ContractError;
|
||||
use crate::state::STATE;
|
||||
use crate::state::storage::{DKG_ADMIN, STATE};
|
||||
use crate::verification_key_shares::storage::verified_dealers;
|
||||
use cosmwasm_std::{Addr, Deps, DepsMut, Env, Order, Response, Storage};
|
||||
use cosmwasm_std::{Addr, Deps, DepsMut, Env, MessageInfo, Order, Response, Storage};
|
||||
use nym_coconut_dkg_common::types::{Epoch, EpochState, InitialReplacementData};
|
||||
|
||||
fn reset_epoch_state(storage: &mut dyn Storage) -> Result<(), ContractError> {
|
||||
fn reset_dkg_state(storage: &mut dyn Storage) -> Result<(), ContractError> {
|
||||
THRESHOLD.remove(storage);
|
||||
let dealers: Vec<_> = current_dealers()
|
||||
.keys(storage, None, None, Order::Ascending)
|
||||
@@ -19,15 +18,6 @@ fn reset_epoch_state(storage: &mut dyn Storage) -> Result<(), ContractError> {
|
||||
|
||||
for dealer_addr in dealers {
|
||||
let details = current_dealers().load(storage, &dealer_addr)?;
|
||||
for dealings in DEALINGS_BYTES {
|
||||
let dealing_keys: Vec<_> = dealings
|
||||
.keys(storage, None, None, Order::Ascending)
|
||||
.flatten()
|
||||
.collect();
|
||||
for key in dealing_keys {
|
||||
dealings.remove(storage, &key);
|
||||
}
|
||||
}
|
||||
current_dealers().remove(storage, &dealer_addr)?;
|
||||
past_dealers().save(storage, &dealer_addr, &details)?;
|
||||
}
|
||||
@@ -80,18 +70,43 @@ fn replacement_threshold_surpassed(deps: &DepsMut<'_>) -> Result<bool, ContractE
|
||||
Ok(removed_dealer_count >= replacement_threshold)
|
||||
}
|
||||
|
||||
pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Response, ContractError> {
|
||||
pub(crate) fn try_initiate_dkg(
|
||||
deps: DepsMut<'_>,
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
) -> Result<Response, ContractError> {
|
||||
// only the admin is allowed to kick start the process
|
||||
DKG_ADMIN.assert_admin(deps.as_ref(), &info.sender)?;
|
||||
|
||||
let epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
if epoch.finish_timestamp > env.block.time {
|
||||
return Err(ContractError::EarlyEpochStateAdvancement(
|
||||
epoch
|
||||
.finish_timestamp
|
||||
.minus_seconds(env.block.time.seconds())
|
||||
.seconds(),
|
||||
));
|
||||
if !matches!(epoch.state, EpochState::WaitingInitialisation) {
|
||||
return Err(ContractError::AlreadyInitialised);
|
||||
}
|
||||
|
||||
// the first exchange won't involve resharing
|
||||
let initial_state = EpochState::PublicKeySubmission { resharing: false };
|
||||
let initial_epoch = Epoch::new(initial_state, 0, epoch.time_configuration, env.block.time);
|
||||
CURRENT_EPOCH.save(deps.storage, &initial_epoch)?;
|
||||
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Response, ContractError> {
|
||||
let current_epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
if current_epoch.state == EpochState::WaitingInitialisation {
|
||||
return Err(ContractError::WaitingInitialisation);
|
||||
}
|
||||
|
||||
if let Some(finish_timestamp) = current_epoch.finish_timestamp {
|
||||
if finish_timestamp > env.block.time {
|
||||
return Err(ContractError::EarlyEpochStateAdvancement(
|
||||
finish_timestamp
|
||||
.minus_seconds(env.block.time.seconds())
|
||||
.seconds(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let next_epoch = if let Some(state) = current_epoch.state.next() {
|
||||
// We are during DKG process
|
||||
let mut new_state = state;
|
||||
@@ -123,6 +138,8 @@ pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Respons
|
||||
} else if dealers_eq_members(&deps)? {
|
||||
// The dealer set hasn't changed, so we only extend the finish timestamp
|
||||
// The epoch remains the same, as we use it as key for storing VKs
|
||||
|
||||
// TODO: change that behaviour in the following PR
|
||||
Epoch::new(
|
||||
current_epoch.state,
|
||||
current_epoch.epoch_id,
|
||||
@@ -152,7 +169,7 @@ pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Respons
|
||||
|
||||
EpochState::PublicKeySubmission { resharing: true }
|
||||
};
|
||||
reset_epoch_state(deps.storage)?;
|
||||
reset_dkg_state(deps.storage)?;
|
||||
Epoch::new(
|
||||
state,
|
||||
current_epoch.epoch_id + 1,
|
||||
@@ -174,7 +191,7 @@ pub(crate) fn try_surpassed_threshold(
|
||||
let threshold = THRESHOLD.load(deps.storage)?;
|
||||
let dealers = verified_dealers(deps.storage)?;
|
||||
if dealers_still_active(&deps.as_ref(), dealers.into_iter())? < threshold as usize {
|
||||
reset_epoch_state(deps.storage)?;
|
||||
reset_dkg_state(deps.storage)?;
|
||||
CURRENT_EPOCH.update::<_, ContractError>(deps.storage, |epoch| {
|
||||
Ok(Epoch::new(
|
||||
EpochState::default(),
|
||||
@@ -193,20 +210,19 @@ pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::error::ContractError::EarlyEpochStateAdvancement;
|
||||
use crate::support::tests::fixtures::{dealer_details_fixture, vk_share_fixture};
|
||||
use crate::support::tests::helpers::{init_contract, GROUP_MEMBERS};
|
||||
use crate::support::tests::helpers::{init_contract, ADMIN_ADDRESS, GROUP_MEMBERS};
|
||||
use crate::verification_key_shares::storage::vk_shares;
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cosmwasm_std::Addr;
|
||||
use cw4::Member;
|
||||
use nym_coconut_dkg_common::types::{
|
||||
ContractSafeBytes, DealerDetails, EpochState, TimeConfiguration,
|
||||
};
|
||||
use cw_controllers::AdminError;
|
||||
use nym_coconut_dkg_common::types::{DealerDetails, EpochState, TimeConfiguration};
|
||||
use rusty_fork::rusty_fork_test;
|
||||
|
||||
// Because of the global variable handling group, we need individual process for each test
|
||||
|
||||
rusty_fork_test! {
|
||||
// Using values from the DKG document
|
||||
// Using values from the DKG document
|
||||
#[test]
|
||||
fn threshold_surpassed() {
|
||||
let mut deps = init_contract();
|
||||
@@ -217,14 +233,18 @@ pub(crate) mod tests {
|
||||
|
||||
for n in [10, 25, 50, 100] {
|
||||
let dealers: Vec<_> = (0..n).map(dealer_details_fixture).collect();
|
||||
let shares: Vec<_> = (0..n).map(|idx| vk_share_fixture(&format!("owner{}", idx), 0)).collect();
|
||||
let shares: Vec<_> = (0..n)
|
||||
.map(|idx| vk_share_fixture(&format!("owner{}", idx), 0))
|
||||
.collect();
|
||||
let initial_dealers = dealers.iter().map(|d| d.address.clone()).collect();
|
||||
let data = InitialReplacementData {
|
||||
initial_dealers,
|
||||
initial_height: 1,
|
||||
};
|
||||
for share in shares {
|
||||
vk_shares().save(deps.as_mut().storage, (&share.owner, 0), &share).unwrap();
|
||||
vk_shares()
|
||||
.save(deps.as_mut().storage, (&share.owner, 0), &share)
|
||||
.unwrap();
|
||||
}
|
||||
for f in [two_thirds, three_fourths, ninty_pc] {
|
||||
let threshold = f(n);
|
||||
@@ -361,7 +381,8 @@ pub(crate) mod tests {
|
||||
fn advance_state() {
|
||||
let mut deps = init_contract();
|
||||
let mut env = mock_env();
|
||||
{
|
||||
|
||||
{
|
||||
let mut group = GROUP_MEMBERS.lock().unwrap();
|
||||
|
||||
group.push((
|
||||
@@ -394,13 +415,21 @@ pub(crate) mod tests {
|
||||
));
|
||||
}
|
||||
|
||||
// can't advance the state if dkg hasn't been initiated
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
ContractError::WaitingInitialisation
|
||||
);
|
||||
|
||||
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.public_key_submission_time_secs)
|
||||
@@ -424,7 +453,8 @@ pub(crate) mod tests {
|
||||
);
|
||||
|
||||
// setup dealer details
|
||||
let all_shares: [_; 4] = std::array::from_fn(|i| vk_share_fixture(&format!("owner{}", i + 1), 0));
|
||||
let all_shares: [_; 4] =
|
||||
std::array::from_fn(|i| vk_share_fixture(&format!("owner{}", i + 1), 0));
|
||||
for share in all_shares.iter() {
|
||||
vk_shares()
|
||||
.save(deps.as_mut().storage, (&share.owner, 0), share)
|
||||
@@ -441,7 +471,10 @@ pub(crate) mod tests {
|
||||
.may_load(&deps.storage)
|
||||
.unwrap()
|
||||
.is_none());
|
||||
env.block.time = env.block.time.plus_seconds(epoch.time_configuration.public_key_submission_time_secs);
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.public_key_submission_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
@@ -449,7 +482,7 @@ pub(crate) mod tests {
|
||||
EpochState::DealingExchange { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.dealing_exchange_time_secs)
|
||||
@@ -472,7 +505,7 @@ pub(crate) mod tests {
|
||||
EpochState::VerificationKeySubmission { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block.time.plus_seconds(
|
||||
epoch
|
||||
.time_configuration
|
||||
@@ -499,7 +532,7 @@ pub(crate) mod tests {
|
||||
EpochState::VerificationKeyValidation { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block.time.plus_seconds(
|
||||
epoch
|
||||
.time_configuration
|
||||
@@ -526,7 +559,7 @@ pub(crate) mod tests {
|
||||
EpochState::VerificationKeyFinalization { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block.time.plus_seconds(
|
||||
epoch
|
||||
.time_configuration
|
||||
@@ -548,7 +581,7 @@ pub(crate) mod tests {
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(epoch.state, EpochState::InProgress);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.in_progress_time_secs)
|
||||
@@ -614,7 +647,9 @@ pub(crate) mod tests {
|
||||
|
||||
let all_details: [_; 4] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 2));
|
||||
for details in all_details.iter() {
|
||||
past_dealers().remove(deps.as_mut().storage, &details.address).unwrap();
|
||||
past_dealers()
|
||||
.remove(deps.as_mut().storage, &details.address)
|
||||
.unwrap();
|
||||
current_dealers()
|
||||
.save(deps.as_mut().storage, &details.address, details)
|
||||
.unwrap();
|
||||
@@ -622,9 +657,15 @@ pub(crate) mod tests {
|
||||
for times in [
|
||||
epoch.time_configuration.public_key_submission_time_secs,
|
||||
epoch.time_configuration.dealing_exchange_time_secs,
|
||||
epoch.time_configuration.verification_key_submission_time_secs,
|
||||
epoch.time_configuration.verification_key_validation_time_secs,
|
||||
epoch.time_configuration.verification_key_finalization_time_secs,
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_submission_time_secs,
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_validation_time_secs,
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_finalization_time_secs,
|
||||
] {
|
||||
env.block.time = env.block.time.plus_seconds(times);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
@@ -634,7 +675,7 @@ pub(crate) mod tests {
|
||||
let mut share = vk_share_fixture(&format!("owner{}", i + 1), 1);
|
||||
share.verified = i % 2 == 0;
|
||||
share
|
||||
});
|
||||
});
|
||||
for share in all_shares.iter() {
|
||||
vk_shares()
|
||||
.save(deps.as_mut().storage, (&share.owner, 0), share)
|
||||
@@ -670,6 +711,8 @@ pub(crate) mod tests {
|
||||
fn surpass_threshold() {
|
||||
let mut deps = init_contract();
|
||||
let mut env = mock_env();
|
||||
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
|
||||
let time_configuration = TimeConfiguration::default();
|
||||
{
|
||||
let mut group = GROUP_MEMBERS.lock().unwrap();
|
||||
@@ -701,13 +744,13 @@ pub(crate) mod tests {
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
current_state: EpochState::default().to_string(),
|
||||
current_state: EpochState::PublicKeySubmission { resharing: false }.to_string(),
|
||||
expected_state: EpochState::InProgress.to_string()
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
let all_shares: [_; 3] = std::array::from_fn(|i| vk_share_fixture(&format!("owner{}", i + 1), 0));
|
||||
let all_shares: [_; 3] =
|
||||
std::array::from_fn(|i| vk_share_fixture(&format!("owner{}", i + 1), 0));
|
||||
for share in all_shares.iter() {
|
||||
vk_shares()
|
||||
.save(deps.as_mut().storage, (&share.owner, 0), share)
|
||||
@@ -719,7 +762,8 @@ pub(crate) mod tests {
|
||||
.save(deps.as_mut().storage, &details.address, details)
|
||||
.unwrap();
|
||||
}
|
||||
let all_shares: [_; 3] = std::array::from_fn(|i| vk_share_fixture(&format!("owner{}", i + 1), 0));
|
||||
let all_shares: [_; 3] =
|
||||
std::array::from_fn(|i| vk_share_fixture(&format!("owner{}", i + 1), 0));
|
||||
for share in all_shares.iter() {
|
||||
vk_shares()
|
||||
.save(deps.as_mut().storage, (&share.owner, share.epoch_id), share)
|
||||
@@ -778,6 +822,46 @@ pub(crate) mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn initialising_dkg() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
|
||||
let initial_epoch_info = CURRENT_EPOCH.load(&deps.storage).unwrap();
|
||||
assert!(initial_epoch_info.finish_timestamp.is_none());
|
||||
|
||||
// can only be executed by the admin
|
||||
let res = try_initiate_dkg(deps.as_mut(), env.clone(), mock_info("not an admin", &[]))
|
||||
.unwrap_err();
|
||||
assert_eq!(ContractError::Admin(AdminError::NotAdmin {}), res);
|
||||
|
||||
let res = try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[]));
|
||||
assert!(res.is_ok());
|
||||
|
||||
// can't be initialised more than once
|
||||
let res = try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[]))
|
||||
.unwrap_err();
|
||||
assert_eq!(ContractError::AlreadyInitialised, res);
|
||||
|
||||
// sets the correct epoch data
|
||||
let epoch = CURRENT_EPOCH.load(&deps.storage).unwrap();
|
||||
assert_eq!(epoch.epoch_id, 0);
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.time_configuration,
|
||||
initial_epoch_info.time_configuration
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.public_key_submission_time_secs)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reset_state() {
|
||||
let mut deps = init_contract();
|
||||
@@ -788,27 +872,12 @@ pub(crate) mod tests {
|
||||
current_dealers()
|
||||
.save(deps.as_mut().storage, &details.address, details)
|
||||
.unwrap();
|
||||
for dealings in DEALINGS_BYTES {
|
||||
dealings
|
||||
.save(
|
||||
deps.as_mut().storage,
|
||||
&details.address,
|
||||
&ContractSafeBytes(vec![1, 2, 3]),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
reset_epoch_state(deps.as_mut().storage).unwrap();
|
||||
reset_dkg_state(deps.as_mut().storage).unwrap();
|
||||
|
||||
assert!(THRESHOLD.may_load(&deps.storage).unwrap().is_none());
|
||||
for details in all_details {
|
||||
for dealings in DEALINGS_BYTES {
|
||||
assert!(dealings
|
||||
.may_load(&deps.storage, &details.address)
|
||||
.unwrap()
|
||||
.is_none());
|
||||
}
|
||||
assert!(current_dealers()
|
||||
.may_load(deps.as_mut().storage, &details.address)
|
||||
.unwrap()
|
||||
@@ -826,6 +895,7 @@ pub(crate) mod tests {
|
||||
fn verify_threshold() {
|
||||
let mut deps = init_contract();
|
||||
let mut env = mock_env();
|
||||
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
|
||||
assert!(THRESHOLD.may_load(deps.as_mut().storage).unwrap().is_none());
|
||||
|
||||
@@ -838,6 +908,7 @@ pub(crate) mod tests {
|
||||
&DealerDetails {
|
||||
address: address.clone(),
|
||||
bte_public_key_with_proof: "bte_public_key_with_proof".to_string(),
|
||||
ed25519_identity: "identity".to_string(),
|
||||
announce_address: "127.0.0.1".to_string(),
|
||||
assigned_index: i,
|
||||
},
|
||||
|
||||
@@ -33,14 +33,14 @@ pub(crate) mod test {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
|
||||
for fixed_state in EpochState::default().all_until(EpochState::InProgress) {
|
||||
for fixed_state in EpochState::first().all_until(EpochState::InProgress) {
|
||||
CURRENT_EPOCH
|
||||
.save(
|
||||
deps.as_mut().storage,
|
||||
&Epoch::new(fixed_state, 0, TimeConfiguration::default(), env.block.time),
|
||||
)
|
||||
.unwrap();
|
||||
for against_state in EpochState::default().all_until(EpochState::InProgress) {
|
||||
for against_state in EpochState::first().all_until(EpochState::InProgress) {
|
||||
let ret = check_epoch_state(deps.as_mut().storage, against_state);
|
||||
if fixed_state == against_state {
|
||||
assert!(ret.is_ok());
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::StdError;
|
||||
use cosmwasm_std::{Addr, StdError};
|
||||
use cw_controllers::AdminError;
|
||||
use nym_coconut_dkg_common::types::{DealingIndex, EpochId};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Custom errors for contract failure conditions.
|
||||
@@ -14,6 +15,12 @@ pub enum ContractError {
|
||||
#[error(transparent)]
|
||||
Admin(#[from] AdminError),
|
||||
|
||||
#[error("Dkg hasn't been initialised yet")]
|
||||
WaitingInitialisation,
|
||||
|
||||
#[error("Dkg has already been initialised")]
|
||||
AlreadyInitialised,
|
||||
|
||||
#[error("Group contract invalid address '{addr}'")]
|
||||
InvalidGroup { addr: String },
|
||||
|
||||
@@ -43,9 +50,34 @@ 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 {
|
||||
epoch_id: EpochId,
|
||||
dealer: Addr,
|
||||
index: DealingIndex,
|
||||
},
|
||||
|
||||
#[error(
|
||||
"Dealer {dealer} has attempted to commit dealing for epoch {epoch_id} with index {index} while the key size is set to {key_size}"
|
||||
)]
|
||||
DealingOutOfRange {
|
||||
epoch_id: EpochId,
|
||||
dealer: Addr,
|
||||
index: DealingIndex,
|
||||
key_size: u32,
|
||||
},
|
||||
|
||||
#[error("This dealer has already committed {commitment}")]
|
||||
AlreadyCommitted { commitment: String },
|
||||
|
||||
#[error("No verification key committed for owner {owner}")]
|
||||
NoCommitForOwner { owner: String },
|
||||
|
||||
#[error("failed to parse {value} into a valid SemVer version: {error_message}")]
|
||||
SemVerFailure {
|
||||
value: String,
|
||||
error_message: String,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
// 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 cw4::Cw4Contract;
|
||||
use cw_controllers::Admin;
|
||||
use cw_storage_plus::Item;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// unique items
|
||||
pub const STATE: Item<State> = Item::new("state");
|
||||
pub const MULTISIG: Admin = Admin::new("multisig");
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
|
||||
pub struct State {
|
||||
pub mix_denom: String,
|
||||
pub multisig_addr: Addr,
|
||||
pub group_addr: Cw4Contract,
|
||||
}
|
||||
pub mod queries;
|
||||
pub mod storage;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::state::storage::STATE;
|
||||
use cosmwasm_std::{StdResult, Storage};
|
||||
use nym_coconut_dkg_common::types::State;
|
||||
|
||||
pub(crate) fn query_state(storage: &dyn Storage) -> StdResult<State> {
|
||||
STATE.load(storage)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cw_controllers::Admin;
|
||||
use cw_storage_plus::Item;
|
||||
use nym_coconut_dkg_common::types::State;
|
||||
|
||||
// unique items
|
||||
pub const DKG_ADMIN: Admin = Admin::new("dkg-admin");
|
||||
|
||||
pub const STATE: Item<State> = Item::new("state");
|
||||
|
||||
pub const MULTISIG: Admin = Admin::new("multisig");
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use cosmwasm_std::Addr;
|
||||
use nym_coconut_dkg_common::dealer::DealerDetails;
|
||||
use nym_coconut_dkg_common::types::ContractSafeBytes;
|
||||
use nym_coconut_dkg_common::types::{ContractSafeBytes, PartialContractDealing};
|
||||
use nym_coconut_dkg_common::verification_key::ContractVKShare;
|
||||
|
||||
pub const TEST_MIX_DENOM: &str = "unym";
|
||||
@@ -20,13 +20,21 @@ pub fn vk_share_fixture(owner: &str, index: u64) -> ContractVKShare {
|
||||
}
|
||||
|
||||
pub fn dealing_bytes_fixture() -> ContractSafeBytes {
|
||||
ContractSafeBytes(vec![])
|
||||
ContractSafeBytes(vec![1, 2, 3])
|
||||
}
|
||||
|
||||
pub fn partial_dealing_fixture() -> PartialContractDealing {
|
||||
PartialContractDealing {
|
||||
index: 0,
|
||||
data: ContractSafeBytes(vec![1, 2, 3]),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dealer_details_fixture(assigned_index: u64) -> DealerDetails {
|
||||
DealerDetails {
|
||||
address: Addr::unchecked(format!("owner{}", assigned_index)),
|
||||
bte_public_key_with_proof: "".to_string(),
|
||||
ed25519_identity: "".to_string(),
|
||||
announce_address: "".to_string(),
|
||||
assigned_index,
|
||||
}
|
||||
|
||||
@@ -9,9 +9,8 @@ use cosmwasm_std::{
|
||||
QuerierResult, SystemResult, WasmQuery,
|
||||
};
|
||||
use cw4::{Cw4QueryMsg, Member, MemberListResponse, MemberResponse};
|
||||
use lazy_static::lazy_static;
|
||||
use nym_coconut_dkg_common::msg::InstantiateMsg;
|
||||
use nym_coconut_dkg_common::types::DealerDetails;
|
||||
use nym_coconut_dkg_common::types::{DealerDetails, DEFAULT_DEALINGS};
|
||||
use std::sync::Mutex;
|
||||
|
||||
use super::fixtures::TEST_MIX_DENOM;
|
||||
@@ -20,9 +19,7 @@ pub const ADMIN_ADDRESS: &str = "admin address";
|
||||
pub const GROUP_CONTRACT: &str = "group contract address";
|
||||
pub const MULTISIG_CONTRACT: &str = "multisig contract address";
|
||||
|
||||
lazy_static! {
|
||||
pub static ref GROUP_MEMBERS: Mutex<Vec<(Member, u64)>> = Mutex::new(vec![]);
|
||||
}
|
||||
pub(crate) static GROUP_MEMBERS: Mutex<Vec<(Member, u64)>> = Mutex::new(Vec::new());
|
||||
|
||||
pub fn add_fixture_dealer(deps: DepsMut<'_>) {
|
||||
let owner = Addr::unchecked("owner");
|
||||
@@ -33,6 +30,7 @@ pub fn add_fixture_dealer(deps: DepsMut<'_>) {
|
||||
&DealerDetails {
|
||||
address: owner.clone(),
|
||||
bte_public_key_with_proof: String::new(),
|
||||
ed25519_identity: String::new(),
|
||||
announce_address: String::new(),
|
||||
assigned_index: 100,
|
||||
},
|
||||
@@ -87,6 +85,7 @@ pub fn init_contract() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>>
|
||||
multisig_addr: String::from(MULTISIG_CONTRACT),
|
||||
time_configuration: None,
|
||||
mix_denom: TEST_MIX_DENOM.to_string(),
|
||||
key_size: DEFAULT_DEALINGS as u32,
|
||||
};
|
||||
let env = mock_env();
|
||||
let info = mock_info(ADMIN_ADDRESS, &[]);
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::dealers::storage as dealers_storage;
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
use crate::epoch_state::utils::check_epoch_state;
|
||||
use crate::error::ContractError;
|
||||
use crate::state::{MULTISIG, STATE};
|
||||
use crate::state::storage::{MULTISIG, STATE};
|
||||
use crate::verification_key_shares::storage::vk_shares;
|
||||
use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response};
|
||||
use nym_coconut_dkg_common::types::EpochState;
|
||||
@@ -91,9 +91,9 @@ pub fn try_verify_verification_key_share(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::epoch_state::transactions::advance_epoch_state;
|
||||
use crate::epoch_state::transactions::{advance_epoch_state, try_initiate_dkg};
|
||||
use crate::support::tests::helpers;
|
||||
use crate::support::tests::helpers::{add_fixture_dealer, MULTISIG_CONTRACT};
|
||||
use crate::support::tests::helpers::{add_fixture_dealer, ADMIN_ADDRESS, MULTISIG_CONTRACT};
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cw_controllers::AdminError;
|
||||
use nym_coconut_dkg_common::dealer::DealerDetails;
|
||||
@@ -103,6 +103,8 @@ mod tests {
|
||||
fn current_epoch_id() {
|
||||
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 info = mock_info("requester", &[]);
|
||||
let share = "share".to_string();
|
||||
|
||||
@@ -122,6 +124,7 @@ mod tests {
|
||||
let dealer_details = DealerDetails {
|
||||
address: dealer.clone(),
|
||||
bte_public_key_with_proof: String::new(),
|
||||
ed25519_identity: String::new(),
|
||||
announce_address: announce_address.clone(),
|
||||
assigned_index: 1,
|
||||
};
|
||||
@@ -149,6 +152,8 @@ mod tests {
|
||||
fn commit_vk_share() {
|
||||
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 info = mock_info("requester", &[]);
|
||||
let share = "share".to_string();
|
||||
|
||||
@@ -163,7 +168,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
current_state: EpochState::default().to_string(),
|
||||
current_state: EpochState::PublicKeySubmission { resharing: false }.to_string(),
|
||||
expected_state: EpochState::VerificationKeySubmission { resharing: false }
|
||||
.to_string()
|
||||
}
|
||||
@@ -193,6 +198,7 @@ mod tests {
|
||||
let dealer_details = DealerDetails {
|
||||
address: dealer.clone(),
|
||||
bte_public_key_with_proof: String::new(),
|
||||
ed25519_identity: String::new(),
|
||||
announce_address: String::new(),
|
||||
assigned_index: 1,
|
||||
};
|
||||
@@ -223,6 +229,8 @@ mod tests {
|
||||
fn invalid_verify_vk_share() {
|
||||
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 info = mock_info("requester", &[]);
|
||||
let owner = Addr::unchecked("owner");
|
||||
let multisig_info = mock_info(MULTISIG_CONTRACT, &[]);
|
||||
@@ -233,7 +241,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
current_state: EpochState::default().to_string(),
|
||||
current_state: EpochState::PublicKeySubmission { resharing: false }.to_string(),
|
||||
expected_state: EpochState::VerificationKeyFinalization { resharing: false }
|
||||
.to_string()
|
||||
}
|
||||
@@ -280,6 +288,8 @@ mod tests {
|
||||
fn verify_vk_share() {
|
||||
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("owner");
|
||||
let info = mock_info(owner.as_ref(), &[]);
|
||||
let share = "share".to_string();
|
||||
@@ -300,6 +310,7 @@ mod tests {
|
||||
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,
|
||||
};
|
||||
|
||||
@@ -75,6 +75,7 @@ fn dkg_proposal() {
|
||||
multisig_addr: multisig_contract_addr.to_string(),
|
||||
time_configuration: None,
|
||||
mix_denom: TEST_COIN_DENOM.to_string(),
|
||||
key_size: 5,
|
||||
};
|
||||
let coconut_dkg_contract_addr = app
|
||||
.instantiate_contract(
|
||||
@@ -104,6 +105,7 @@ fn dkg_proposal() {
|
||||
coconut_dkg_contract_addr.clone(),
|
||||
&RegisterDealer {
|
||||
bte_key_with_proof: "bte_key_with_proof".to_string(),
|
||||
identity_key: "identity".to_string(),
|
||||
announce_address: "127.0.0.1:8000".to_string(),
|
||||
resharing: false,
|
||||
},
|
||||
|
||||
@@ -41,7 +41,7 @@ bs58 = "0.4.0"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
time = { version = "0.3", features = ["macros"] }
|
||||
semver = { version = "1.0.16", default-features = false }
|
||||
semver = { workspace = true, default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
rand_chacha = "0.2"
|
||||
|
||||
@@ -20,7 +20,7 @@ cw-utils = { workspace = true }
|
||||
cw2 = { workspace = true }
|
||||
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", version = "0.5.0" }
|
||||
nym-name-service-common = { path = "../../common/cosmwasm-smart-contracts/name-service" }
|
||||
semver = { version = "1.0.16", default-features = false }
|
||||
semver = { workspace = true, default-features = false }
|
||||
serde = { version = "1.0.155", default-features = false, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ cw-utils = { workspace = true }
|
||||
cw2 = { workspace = true }
|
||||
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", version = "0.5.0" }
|
||||
nym-service-provider-directory-common = { path = "../../common/cosmwasm-smart-contracts/service-provider-directory" }
|
||||
semver = { version = "1.0.16", default-features = false }
|
||||
semver = { workspace = true, default-features = false }
|
||||
serde = { version = "1.0.155", default-features = false, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ cw-storage-plus = { workspace = true, features = ["iterator"] }
|
||||
|
||||
serde = { version = "1.0", default-features = false, features = ["derive"] }
|
||||
thiserror ={ workspace = true }
|
||||
semver = { version = "1.0.16", default-features = false }
|
||||
semver = { workspace = true, default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
rand_chacha = "0.3.1"
|
||||
|
||||
@@ -5,12 +5,13 @@ use crate::coconut::error::Result;
|
||||
use cw3::ProposalResponse;
|
||||
use cw4::MemberResponse;
|
||||
use nym_coconut_bandwidth_contract_common::spend_credential::SpendCredentialResponse;
|
||||
use nym_coconut_dkg_common::dealer::{ContractDealing, DealerDetails, DealerDetailsResponse};
|
||||
use nym_coconut_dkg_common::dealer::{DealerDetails, DealerDetailsResponse, DealingStatusResponse};
|
||||
use nym_coconut_dkg_common::types::{
|
||||
EncodedBTEPublicKeyWithProof, Epoch, EpochId, InitialReplacementData,
|
||||
DealingIndex, EncodedBTEPublicKeyWithProof, Epoch, EpochId, InitialReplacementData,
|
||||
PartialContractDealing, State,
|
||||
};
|
||||
use nym_coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare};
|
||||
use nym_contracts_common::dealings::ContractSafeBytes;
|
||||
use nym_contracts_common::IdentityKey;
|
||||
use nym_dkg::Threshold;
|
||||
use nym_validator_client::nyxd::cosmwasm_client::types::ExecuteResult;
|
||||
use nym_validator_client::nyxd::{AccountId, Fee, Hash, TxResponse};
|
||||
@@ -25,13 +26,25 @@ pub trait Client {
|
||||
&self,
|
||||
blinded_serial_number: String,
|
||||
) -> Result<SpendCredentialResponse>;
|
||||
|
||||
async fn contract_state(&self) -> Result<State>;
|
||||
async fn get_current_epoch(&self) -> Result<Epoch>;
|
||||
async fn group_member(&self, addr: String) -> Result<MemberResponse>;
|
||||
async fn get_current_epoch_threshold(&self) -> Result<Option<Threshold>>;
|
||||
async fn get_initial_dealers(&self) -> Result<Option<InitialReplacementData>>;
|
||||
async fn get_self_registered_dealer_details(&self) -> Result<DealerDetailsResponse>;
|
||||
async fn get_dealing_status(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
) -> Result<DealingStatusResponse>;
|
||||
async fn get_current_dealers(&self) -> Result<Vec<DealerDetails>>;
|
||||
async fn get_dealings(&self, idx: usize) -> Result<Vec<ContractDealing>>;
|
||||
async fn get_dealings(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: &str,
|
||||
) -> Result<Vec<PartialContractDealing>>;
|
||||
async fn get_verification_key_shares(&self, epoch_id: EpochId) -> Result<Vec<ContractVKShare>>;
|
||||
async fn vote_proposal(&self, proposal_id: u64, vote_yes: bool, fee: Option<Fee>)
|
||||
-> Result<()>;
|
||||
@@ -40,12 +53,13 @@ pub trait Client {
|
||||
async fn register_dealer(
|
||||
&self,
|
||||
bte_key: EncodedBTEPublicKeyWithProof,
|
||||
identity_key: IdentityKey,
|
||||
announce_address: String,
|
||||
resharing: bool,
|
||||
) -> Result<ExecuteResult>;
|
||||
async fn submit_dealing(
|
||||
&self,
|
||||
dealing_bytes: ContractSafeBytes,
|
||||
dealing: PartialContractDealing,
|
||||
resharing: bool,
|
||||
) -> Result<ExecuteResult>;
|
||||
async fn submit_verification_key_share(
|
||||
|
||||
@@ -42,12 +42,18 @@ impl CachedEpoch {
|
||||
|
||||
async fn update(&mut self, epoch: Epoch) -> Result<()> {
|
||||
let now = OffsetDateTime::now_utc();
|
||||
let state_end =
|
||||
OffsetDateTime::from_unix_timestamp(epoch.finish_timestamp.seconds() as i64).unwrap();
|
||||
let until_epoch_state_end = state_end - now;
|
||||
|
||||
// make it valid until the next epoch transition or next 5min, whichever is smaller
|
||||
self.valid_until = now + min(until_epoch_state_end, 5 * time::Duration::MINUTE);
|
||||
let validity_duration = if let Some(epoch_finish) = epoch.finish_timestamp {
|
||||
let state_end =
|
||||
OffsetDateTime::from_unix_timestamp(epoch_finish.seconds() as i64).unwrap();
|
||||
let until_epoch_state_end = state_end - now;
|
||||
// make it valid until the next epoch transition or next 5min, whichever is smaller
|
||||
min(until_epoch_state_end, 5 * time::Duration::MINUTE)
|
||||
} else {
|
||||
5 * time::Duration::MINUTE
|
||||
};
|
||||
|
||||
self.valid_until = now + validity_duration;
|
||||
self.current_epoch_id = epoch.epoch_id;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -5,22 +5,25 @@ use crate::coconut::client::Client;
|
||||
use crate::coconut::error::CoconutError;
|
||||
use cw3::ProposalResponse;
|
||||
use cw4::MemberResponse;
|
||||
use nym_coconut_dkg_common::dealer::{ContractDealing, DealerDetails, DealerDetailsResponse};
|
||||
use nym_coconut_dkg_common::dealer::{DealerDetails, DealerDetailsResponse};
|
||||
use nym_coconut_dkg_common::types::{
|
||||
EncodedBTEPublicKeyWithProof, Epoch, EpochId, InitialReplacementData, NodeIndex,
|
||||
DealingIndex, EncodedBTEPublicKeyWithProof, Epoch, EpochId, InitialReplacementData, NodeIndex,
|
||||
PartialContractDealing, State as ContractState,
|
||||
};
|
||||
use nym_coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare};
|
||||
use nym_contracts_common::dealings::ContractSafeBytes;
|
||||
use nym_contracts_common::IdentityKey;
|
||||
use nym_dkg::Threshold;
|
||||
use nym_validator_client::nyxd::cosmwasm_client::logs::{find_attribute, NODE_INDEX};
|
||||
use nym_validator_client::nyxd::cosmwasm_client::types::ExecuteResult;
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
use std::time::Duration;
|
||||
|
||||
pub(crate) struct DkgClient {
|
||||
inner: Box<dyn Client + Send + Sync>,
|
||||
}
|
||||
|
||||
impl DkgClient {
|
||||
// FIXME:
|
||||
// Some queries simply don't work the first time
|
||||
// Until we determine why that is, retry the query a few more times
|
||||
const RETRIES: usize = 3;
|
||||
@@ -44,11 +47,24 @@ impl DkgClient {
|
||||
if ret.is_ok() {
|
||||
return ret;
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(200)).await;
|
||||
ret = self.inner.get_current_epoch().await;
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
pub(crate) async fn get_contract_state(&self) -> Result<ContractState, CoconutError> {
|
||||
let mut ret = self.inner.contract_state().await;
|
||||
for _ in 0..Self::RETRIES {
|
||||
if ret.is_ok() {
|
||||
return ret;
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(200)).await;
|
||||
ret = self.inner.contract_state().await;
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
pub(crate) async fn group_member(&self) -> Result<MemberResponse, CoconutError> {
|
||||
self.inner
|
||||
.group_member(self.get_address().await.to_string())
|
||||
@@ -77,16 +93,44 @@ impl DkgClient {
|
||||
self.inner.get_current_dealers().await
|
||||
}
|
||||
|
||||
pub(crate) async fn get_dealings(
|
||||
pub(crate) async fn get_dealing_status(
|
||||
&self,
|
||||
idx: usize,
|
||||
) -> Result<Vec<ContractDealing>, CoconutError> {
|
||||
let mut ret = self.inner.get_dealings(idx).await;
|
||||
epoch_id: EpochId,
|
||||
dealing_index: DealingIndex,
|
||||
) -> Result<bool, CoconutError> {
|
||||
let address = self.inner.address().await.to_string();
|
||||
|
||||
let mut ret = self
|
||||
.inner
|
||||
.get_dealing_status(epoch_id, address.clone(), dealing_index)
|
||||
.await
|
||||
.map(|r| r.dealing_submitted);
|
||||
for _ in 0..Self::RETRIES {
|
||||
if ret.is_ok() {
|
||||
return ret;
|
||||
}
|
||||
ret = self.inner.get_dealings(idx).await;
|
||||
tokio::time::sleep(Duration::from_millis(200)).await;
|
||||
ret = self
|
||||
.inner
|
||||
.get_dealing_status(epoch_id, address.clone(), dealing_index)
|
||||
.await
|
||||
.map(|r| r.dealing_submitted);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
pub(crate) async fn get_dealings(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
) -> Result<Vec<PartialContractDealing>, CoconutError> {
|
||||
let mut ret = self.inner.get_dealings(epoch_id, &dealer).await;
|
||||
for _ in 0..Self::RETRIES {
|
||||
if ret.is_ok() {
|
||||
return ret;
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(200)).await;
|
||||
ret = self.inner.get_dealings(epoch_id, &dealer).await;
|
||||
}
|
||||
ret
|
||||
}
|
||||
@@ -109,12 +153,13 @@ impl DkgClient {
|
||||
pub(crate) async fn register_dealer(
|
||||
&self,
|
||||
bte_key: EncodedBTEPublicKeyWithProof,
|
||||
identity_key: IdentityKey,
|
||||
announce_address: String,
|
||||
resharing: bool,
|
||||
) -> Result<NodeIndex, CoconutError> {
|
||||
let res = self
|
||||
.inner
|
||||
.register_dealer(bte_key, announce_address, resharing)
|
||||
.register_dealer(bte_key, identity_key, announce_address, resharing)
|
||||
.await?;
|
||||
let node_index = find_attribute(&res.logs, "wasm", NODE_INDEX)
|
||||
.ok_or(CoconutError::NodeIndexRecoveryError {
|
||||
@@ -131,10 +176,10 @@ impl DkgClient {
|
||||
|
||||
pub(crate) async fn submit_dealing(
|
||||
&self,
|
||||
dealing_bytes: ContractSafeBytes,
|
||||
dealing: PartialContractDealing,
|
||||
resharing: bool,
|
||||
) -> Result<(), CoconutError> {
|
||||
self.inner.submit_dealing(dealing_bytes, resharing).await?;
|
||||
self.inner.submit_dealing(dealing, resharing).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -151,6 +196,7 @@ impl DkgClient {
|
||||
if let Ok(res) = ret {
|
||||
return Ok(res);
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(200)).await;
|
||||
ret = self
|
||||
.inner
|
||||
.submit_verification_key_share(share.clone(), resharing)
|
||||
|
||||
@@ -15,6 +15,7 @@ use crate::nyxd;
|
||||
use crate::support::config;
|
||||
use anyhow::{bail, Result};
|
||||
use nym_coconut_dkg_common::types::EpochState;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_dkg::bte::keys::KeyPair as DkgKeyPair;
|
||||
use nym_task::{TaskClient, TaskManager};
|
||||
use rand::rngs::OsRng;
|
||||
@@ -51,6 +52,7 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
|
||||
config: &config::CoconutSigner,
|
||||
nyxd_client: nyxd::Client,
|
||||
coconut_keypair: CoconutKeyPair,
|
||||
identity_key: identity::PublicKey,
|
||||
rng: R,
|
||||
) -> Result<Self> {
|
||||
let Some(announce_address) = &config.announce_address else {
|
||||
@@ -82,6 +84,7 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
|
||||
persistent_state,
|
||||
announce_address.clone(),
|
||||
dkg_keypair,
|
||||
identity_key,
|
||||
coconut_keypair,
|
||||
),
|
||||
rng,
|
||||
@@ -102,88 +105,89 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
|
||||
}
|
||||
|
||||
pub(crate) async fn handle_epoch_state(&mut self) {
|
||||
match self.dkg_client.get_current_epoch().await {
|
||||
Err(err) => warn!("Could not get current epoch state {err}"),
|
||||
Ok(epoch) => {
|
||||
if self
|
||||
.dkg_client
|
||||
.group_member()
|
||||
.await
|
||||
.map(|resp| resp.weight.is_none())
|
||||
.unwrap_or(true)
|
||||
{
|
||||
debug!("Not a member of the group, DKG won't be run");
|
||||
return;
|
||||
}
|
||||
if let Err(err) = self.state.is_consistent(epoch.state).await {
|
||||
debug!("Epoch state is corrupted - {err}. Awaiting for a DKG restart.");
|
||||
} else {
|
||||
let ret = match epoch.state {
|
||||
EpochState::PublicKeySubmission { resharing } => {
|
||||
public_key_submission(&self.dkg_client, &mut self.state, resharing)
|
||||
.await
|
||||
}
|
||||
EpochState::DealingExchange { resharing } => {
|
||||
dealing_exchange(
|
||||
&self.dkg_client,
|
||||
&mut self.state,
|
||||
self.rng.clone(),
|
||||
resharing,
|
||||
)
|
||||
.await
|
||||
}
|
||||
EpochState::VerificationKeySubmission { resharing } => {
|
||||
let keypair_path = nym_pemstore::KeyPairPath::new(
|
||||
self.secret_key_path.clone(),
|
||||
self.verification_key_path.clone(),
|
||||
);
|
||||
verification_key_submission(
|
||||
&self.dkg_client,
|
||||
&mut self.state,
|
||||
&keypair_path,
|
||||
resharing,
|
||||
)
|
||||
.await
|
||||
}
|
||||
EpochState::VerificationKeyValidation { resharing } => {
|
||||
verification_key_validation(
|
||||
&self.dkg_client,
|
||||
&mut self.state,
|
||||
resharing,
|
||||
)
|
||||
.await
|
||||
}
|
||||
EpochState::VerificationKeyFinalization { resharing } => {
|
||||
verification_key_finalization(
|
||||
&self.dkg_client,
|
||||
&mut self.state,
|
||||
resharing,
|
||||
)
|
||||
.await
|
||||
}
|
||||
// Just wait, in case we need to redo dkg at some point
|
||||
EpochState::InProgress => {
|
||||
self.state.set_was_in_progress();
|
||||
// We're dumping state here so that we don't do it uselessly during the
|
||||
// long InProgress state
|
||||
self.dump_persistent_state().await;
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
if let Err(err) = ret {
|
||||
warn!("Could not handle this iteration for the epoch state: {err}");
|
||||
} else if epoch.state != EpochState::InProgress {
|
||||
self.dump_persistent_state().await;
|
||||
}
|
||||
}
|
||||
if let Ok(current_timestamp) =
|
||||
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)
|
||||
{
|
||||
if current_timestamp.as_secs() >= epoch.finish_timestamp.seconds() {
|
||||
// We try advancing the epoch state, on a best-effort basis
|
||||
info!("DKG: Trying to advance the epoch");
|
||||
self.dkg_client.advance_epoch_state().await.ok();
|
||||
}
|
||||
let epoch = match self.dkg_client.get_current_epoch().await {
|
||||
Err(err) => {
|
||||
warn!("Could not get current epoch state {err}");
|
||||
return;
|
||||
}
|
||||
Ok(epoch) => epoch,
|
||||
};
|
||||
|
||||
if self
|
||||
.dkg_client
|
||||
.group_member()
|
||||
.await
|
||||
.map(|resp| resp.weight.is_none())
|
||||
.unwrap_or(true)
|
||||
{
|
||||
debug!("Not a member of the group, DKG won't be run");
|
||||
return;
|
||||
}
|
||||
if let Err(err) = self.state.is_consistent(epoch.state).await {
|
||||
warn!("Epoch state is corrupted - {err}. Awaiting for a DKG restart.");
|
||||
return;
|
||||
}
|
||||
|
||||
let ret = match epoch.state {
|
||||
EpochState::WaitingInitialisation => {
|
||||
info!("DKG hasn't been initialised yet");
|
||||
return;
|
||||
}
|
||||
EpochState::PublicKeySubmission { resharing } => {
|
||||
public_key_submission(&self.dkg_client, &mut self.state, resharing).await
|
||||
}
|
||||
EpochState::DealingExchange { resharing } => {
|
||||
dealing_exchange(
|
||||
&self.dkg_client,
|
||||
&mut self.state,
|
||||
self.rng.clone(),
|
||||
resharing,
|
||||
)
|
||||
.await
|
||||
}
|
||||
EpochState::VerificationKeySubmission { resharing } => {
|
||||
let keypair_path = nym_pemstore::KeyPairPath::new(
|
||||
self.secret_key_path.clone(),
|
||||
self.verification_key_path.clone(),
|
||||
);
|
||||
verification_key_submission(
|
||||
&self.dkg_client,
|
||||
&mut self.state,
|
||||
epoch.epoch_id,
|
||||
&keypair_path,
|
||||
resharing,
|
||||
)
|
||||
.await
|
||||
}
|
||||
EpochState::VerificationKeyValidation { resharing } => {
|
||||
verification_key_validation(&self.dkg_client, &mut self.state, resharing).await
|
||||
}
|
||||
EpochState::VerificationKeyFinalization { resharing } => {
|
||||
verification_key_finalization(&self.dkg_client, &mut self.state, resharing).await
|
||||
}
|
||||
// Just wait, in case we need to redo dkg at some point
|
||||
EpochState::InProgress => {
|
||||
self.state.set_was_in_progress();
|
||||
// We're dumping state here so that we don't do it uselessly during the
|
||||
// long InProgress state
|
||||
self.dump_persistent_state().await;
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
if let Err(err) = ret {
|
||||
warn!("Could not handle this iteration for the epoch state: {err}");
|
||||
} else if epoch.state != EpochState::InProgress
|
||||
&& epoch.state != EpochState::WaitingInitialisation
|
||||
{
|
||||
self.dump_persistent_state().await;
|
||||
}
|
||||
|
||||
if let Ok(current_timestamp) = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
|
||||
if let Some(epoch_finish) = epoch.finish_timestamp {
|
||||
if current_timestamp.as_secs() >= epoch_finish.seconds() {
|
||||
// We try advancing the epoch state, on a best-effort basis
|
||||
info!("DKG: Trying to advance the epoch");
|
||||
self.dkg_client.advance_epoch_state().await.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,6 +211,7 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
|
||||
config: &config::CoconutSigner,
|
||||
nyxd_client: nyxd::Client,
|
||||
coconut_keypair: CoconutKeyPair,
|
||||
identity_key: identity::PublicKey,
|
||||
rng: R,
|
||||
shutdown: &TaskManager,
|
||||
) -> Result<()>
|
||||
@@ -214,7 +219,8 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
|
||||
R: Sync + Send + 'static,
|
||||
{
|
||||
let shutdown_listener = shutdown.subscribe();
|
||||
let dkg_controller = DkgController::new(config, nyxd_client, coconut_keypair, rng).await?;
|
||||
let dkg_controller =
|
||||
DkgController::new(config, nyxd_client, coconut_keypair, identity_key, rng).await?;
|
||||
tokio::spawn(async move { dkg_controller.run(shutdown_listener).await });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::coconut::dkg;
|
||||
use crate::coconut::dkg::client::DkgClient;
|
||||
use crate::coconut::dkg::state::{ConsistentState, State};
|
||||
use crate::coconut::error::CoconutError;
|
||||
use log::debug;
|
||||
use nym_coconut_dkg_common::types::TOTAL_DEALINGS;
|
||||
use nym_contracts_common::dealings::ContractSafeBytes;
|
||||
use nym_dkg::bte::setup;
|
||||
use nym_coconut_dkg_common::types::{ContractDealing, PartialContractDealing};
|
||||
use nym_dkg::Dealing;
|
||||
use rand::RngCore;
|
||||
use std::collections::VecDeque;
|
||||
@@ -24,6 +23,11 @@ pub(crate) async fn dealing_exchange(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let contract_state = dkg_client.get_contract_state().await?;
|
||||
let expected_key_size = contract_state.key_size;
|
||||
|
||||
let epoch_id = dkg_client.get_current_epoch().await?.epoch_id;
|
||||
|
||||
let dealers = dkg_client.get_current_dealers().await?;
|
||||
let threshold = dkg_client.get_current_epoch_threshold().await?;
|
||||
let initial_dealers = dkg_client
|
||||
@@ -44,7 +48,7 @@ pub(crate) async fn dealing_exchange(
|
||||
// Double check that we are in resharing mode
|
||||
if resharing {
|
||||
let sk = keypair.secret_key();
|
||||
if sk.size() + 1 != TOTAL_DEALINGS {
|
||||
if sk.size() + 1 != expected_key_size as usize {
|
||||
return Err(CoconutError::CorruptedCoconutKeyPair);
|
||||
}
|
||||
|
||||
@@ -64,23 +68,54 @@ pub(crate) async fn dealing_exchange(
|
||||
};
|
||||
let mut prior_resharing_secrets = VecDeque::from(prior_resharing_secrets);
|
||||
if !resharing || initial_dealers.iter().any(|d| *d == own_address) {
|
||||
let params = setup();
|
||||
for _ in 0..TOTAL_DEALINGS {
|
||||
let params = dkg::params();
|
||||
for dealing_index in 0..expected_key_size {
|
||||
debug!(
|
||||
"dealing {dealing_index} ({} out of {expected_key_size})",
|
||||
dealing_index + 1
|
||||
);
|
||||
|
||||
// see if we have already submitted this one (we might have crashed)
|
||||
if dkg_client
|
||||
.get_dealing_status(epoch_id, dealing_index)
|
||||
.await?
|
||||
{
|
||||
warn!("we have already submitted dealing {dealing_index} before - we probably crashed!");
|
||||
continue;
|
||||
}
|
||||
|
||||
// see if we have already generated, but not submitted this one before (we might have crashed or validator might have had a problem)
|
||||
let contract_dealing = if let Some(prior_dealing) =
|
||||
state.get_dealing(epoch_id, dealing_index)
|
||||
{
|
||||
warn!("we have already generated dealing {dealing_index} before, but failed to submit it");
|
||||
PartialContractDealing::new(dealing_index, ContractDealing::from(prior_dealing))
|
||||
} else {
|
||||
// generate fresh dealing
|
||||
let (dealing, _) = Dealing::create(
|
||||
rng.clone(),
|
||||
params,
|
||||
dealer_index,
|
||||
state.threshold()?,
|
||||
&receivers,
|
||||
prior_resharing_secrets.pop_front(),
|
||||
);
|
||||
|
||||
let contract_dealing =
|
||||
PartialContractDealing::new(dealing_index, ContractDealing::from(&dealing));
|
||||
state.store_dealing(epoch_id, dealing_index, dealing);
|
||||
|
||||
contract_dealing
|
||||
};
|
||||
|
||||
debug!(
|
||||
"Submitting dealing for indexes {:?} with resharing: {}",
|
||||
receivers.keys().collect::<Vec<_>>(),
|
||||
prior_resharing_secrets.front().is_some()
|
||||
);
|
||||
let (dealing, _) = Dealing::create(
|
||||
rng.clone(),
|
||||
¶ms,
|
||||
dealer_index,
|
||||
state.threshold()?,
|
||||
&receivers,
|
||||
prior_resharing_secrets.pop_front(),
|
||||
);
|
||||
|
||||
dkg_client
|
||||
.submit_dealing(ContractSafeBytes::from(&dealing), resharing)
|
||||
.submit_dealing(contract_dealing, resharing)
|
||||
.await?;
|
||||
}
|
||||
} else {
|
||||
@@ -104,10 +139,12 @@ pub(crate) mod tests {
|
||||
use nym_coconut::{ttp_keygen, Parameters};
|
||||
use nym_coconut_dkg_common::dealer::DealerDetails;
|
||||
use nym_coconut_dkg_common::types::InitialReplacementData;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_dkg::bte::keys::KeyPair as DkgKeyPair;
|
||||
use nym_dkg::bte::{Params, PublicKeyWithProof};
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
use rand::rngs::OsRng;
|
||||
use rand_07::thread_rng;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
@@ -128,6 +165,8 @@ pub(crate) mod tests {
|
||||
let mut keypairs = vec![];
|
||||
for (idx, addr) in TEST_VALIDATORS_ADDRESS.iter().enumerate() {
|
||||
let keypair = DkgKeyPair::new(params, OsRng);
|
||||
let identity_keypair = identity::KeyPair::new(&mut thread_rng());
|
||||
|
||||
let bte_public_key_with_proof =
|
||||
bs58::encode(&keypair.public_key().to_bytes()).into_string();
|
||||
keypairs.push(keypair);
|
||||
@@ -137,6 +176,7 @@ pub(crate) mod tests {
|
||||
DealerDetails {
|
||||
address: Addr::unchecked(*addr),
|
||||
bte_public_key_with_proof,
|
||||
ed25519_identity: identity_keypair.public_key().to_base58_string(),
|
||||
announce_address: format!("localhost:80{}", idx),
|
||||
assigned_index: (idx + 1) as u64,
|
||||
},
|
||||
@@ -160,16 +200,20 @@ pub(crate) mod tests {
|
||||
.with_dealings(&dealings_db)
|
||||
.with_threshold(&threshold_db),
|
||||
);
|
||||
let params = setup();
|
||||
let params = dkg::params();
|
||||
let identity_keypair = identity::KeyPair::new(&mut thread_rng());
|
||||
let mut state = State::new(
|
||||
PathBuf::default(),
|
||||
PersistentState::default(),
|
||||
Url::parse("localhost:8000").unwrap(),
|
||||
DkgKeyPair::new(¶ms, OsRng),
|
||||
DkgKeyPair::new(params, OsRng),
|
||||
*identity_keypair.public_key(),
|
||||
KeyPair::new(),
|
||||
);
|
||||
state.set_node_index(Some(self_index));
|
||||
let keypairs = insert_dealers(¶ms, &dealer_details_db);
|
||||
let keypairs = insert_dealers(params, &dealer_details_db);
|
||||
|
||||
let contract_state = dkg_client.get_contract_state().await.unwrap();
|
||||
|
||||
dealing_exchange(&dkg_client, &mut state, OsRng, false)
|
||||
.await
|
||||
@@ -187,10 +231,12 @@ pub(crate) mod tests {
|
||||
let dealings = dealings_db
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&0)
|
||||
.unwrap()
|
||||
.get(TEST_VALIDATORS_ADDRESS[0])
|
||||
.unwrap()
|
||||
.clone();
|
||||
assert_eq!(dealings.len(), TOTAL_DEALINGS);
|
||||
assert_eq!(dealings.len(), contract_state.key_size as usize);
|
||||
|
||||
dealing_exchange(&dkg_client, &mut state, OsRng, false)
|
||||
.await
|
||||
@@ -198,6 +244,8 @@ pub(crate) mod tests {
|
||||
let new_dealings = dealings_db
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&0)
|
||||
.unwrap()
|
||||
.get(TEST_VALIDATORS_ADDRESS[0])
|
||||
.unwrap()
|
||||
.clone();
|
||||
@@ -217,16 +265,18 @@ pub(crate) mod tests {
|
||||
.with_dealings(&dealings_db)
|
||||
.with_threshold(&threshold_db),
|
||||
);
|
||||
let params = setup();
|
||||
let params = dkg::params();
|
||||
let identity_keypair = identity::KeyPair::new(&mut thread_rng());
|
||||
let mut state = State::new(
|
||||
PathBuf::default(),
|
||||
PersistentState::default(),
|
||||
Url::parse("localhost:8000").unwrap(),
|
||||
DkgKeyPair::new(¶ms, OsRng),
|
||||
DkgKeyPair::new(params, OsRng),
|
||||
*identity_keypair.public_key(),
|
||||
KeyPair::new(),
|
||||
);
|
||||
state.set_node_index(Some(self_index));
|
||||
insert_dealers(¶ms, &dealer_details_db);
|
||||
insert_dealers(params, &dealer_details_db);
|
||||
|
||||
dealer_details_db
|
||||
.write()
|
||||
@@ -285,20 +335,24 @@ pub(crate) mod tests {
|
||||
.with_threshold(&threshold_db)
|
||||
.with_initial_dealers_db(&initial_dealers_db),
|
||||
);
|
||||
let params = setup();
|
||||
let contract_state = dkg_client.get_contract_state().await.unwrap();
|
||||
|
||||
let params = dkg::params();
|
||||
let mut keys = ttp_keygen(&Parameters::new(4).unwrap(), 3, 4).unwrap();
|
||||
let coconut_keypair = KeyPair::new();
|
||||
coconut_keypair.set(Some(keys.pop().unwrap())).await;
|
||||
let identity_keypair = identity::KeyPair::new(&mut thread_rng());
|
||||
|
||||
let mut state = State::new(
|
||||
PathBuf::default(),
|
||||
PersistentState::default(),
|
||||
Url::parse("localhost:8000").unwrap(),
|
||||
DkgKeyPair::new(¶ms, OsRng),
|
||||
DkgKeyPair::new(params, OsRng),
|
||||
*identity_keypair.public_key(),
|
||||
coconut_keypair.clone(),
|
||||
);
|
||||
state.set_node_index(Some(self_index));
|
||||
let keypairs = insert_dealers(¶ms, &dealer_details_db);
|
||||
let keypairs = insert_dealers(params, &dealer_details_db);
|
||||
|
||||
dealing_exchange(&dkg_client, &mut state, OsRng, true)
|
||||
.await
|
||||
@@ -313,14 +367,18 @@ pub(crate) mod tests {
|
||||
);
|
||||
assert_eq!(state.threshold().unwrap(), 3);
|
||||
assert_eq!(state.receiver_index().unwrap(), 1);
|
||||
let addr = dkg_client.get_address().await;
|
||||
assert!(dealings_db.read().unwrap().get(addr.as_ref()).is_none());
|
||||
// let addr = dkg_client.get_address().await;
|
||||
|
||||
// no dealings submitted for the first (zeroth) epoch
|
||||
assert!(dealings_db.read().unwrap().get(&0).is_none());
|
||||
|
||||
let identity_keypair = identity::KeyPair::new(&mut thread_rng());
|
||||
let mut state = State::new(
|
||||
PathBuf::default(),
|
||||
PersistentState::default(),
|
||||
Url::parse("localhost:8000").unwrap(),
|
||||
DkgKeyPair::new(¶ms, OsRng),
|
||||
DkgKeyPair::new(params, OsRng),
|
||||
*identity_keypair.public_key(),
|
||||
coconut_keypair,
|
||||
);
|
||||
state.set_node_index(Some(self_index));
|
||||
@@ -340,9 +398,11 @@ pub(crate) mod tests {
|
||||
let dealings = dealings_db
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&0)
|
||||
.unwrap()
|
||||
.get(TEST_VALIDATORS_ADDRESS[0])
|
||||
.unwrap()
|
||||
.clone();
|
||||
assert_eq!(dealings.len(), TOTAL_DEALINGS);
|
||||
assert_eq!(dealings.len(), contract_state.key_size as usize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::sync::OnceLock;
|
||||
|
||||
pub(crate) fn params() -> &'static nym_dkg::bte::Params {
|
||||
static PARAMS: OnceLock<nym_dkg::bte::Params> = OnceLock::new();
|
||||
PARAMS.get_or_init(nym_dkg::bte::setup)
|
||||
}
|
||||
|
||||
pub(crate) mod client;
|
||||
pub(crate) mod complaints;
|
||||
pub(crate) mod controller;
|
||||
|
||||
@@ -38,7 +38,12 @@ pub(crate) async fn public_key_submission(
|
||||
// If it was a dealer in a previous epoch, re-register it for this epoch
|
||||
debug!("Registering for the current DKG round, with keys from a previous epoch");
|
||||
dkg_client
|
||||
.register_dealer(bte_key, state.announce_address().to_string(), resharing)
|
||||
.register_dealer(
|
||||
bte_key,
|
||||
state.identity_key().to_base58_string(),
|
||||
state.announce_address().to_string(),
|
||||
resharing,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
details.assigned_index
|
||||
@@ -46,7 +51,12 @@ pub(crate) async fn public_key_submission(
|
||||
debug!("Registering for the first time to be a dealer");
|
||||
// First time registration
|
||||
dkg_client
|
||||
.register_dealer(bte_key, state.announce_address().to_string(), resharing)
|
||||
.register_dealer(
|
||||
bte_key,
|
||||
state.identity_key().to_base58_string(),
|
||||
state.announce_address().to_string(),
|
||||
resharing,
|
||||
)
|
||||
.await?
|
||||
};
|
||||
state.set_node_index(Some(index));
|
||||
@@ -61,9 +71,11 @@ pub(crate) mod tests {
|
||||
use crate::coconut::dkg::state::PersistentState;
|
||||
use crate::coconut::tests::DummyClient;
|
||||
use crate::coconut::KeyPair;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_dkg::bte::keys::KeyPair as DkgKeyPair;
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
use rand::rngs::OsRng;
|
||||
use rand_07::thread_rng;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use url::Url;
|
||||
@@ -76,11 +88,13 @@ pub(crate) mod tests {
|
||||
let dkg_client = DkgClient::new(DummyClient::new(
|
||||
AccountId::from_str(TEST_VALIDATOR_ADDRESS).unwrap(),
|
||||
));
|
||||
let identity_keypair = identity::KeyPair::new(&mut thread_rng());
|
||||
let mut state = State::new(
|
||||
PathBuf::default(),
|
||||
PersistentState::default(),
|
||||
Url::parse("localhost:8000").unwrap(),
|
||||
DkgKeyPair::new(&nym_dkg::bte::setup(), OsRng),
|
||||
*identity_keypair.public_key(),
|
||||
KeyPair::new(),
|
||||
);
|
||||
|
||||
|
||||
@@ -7,12 +7,13 @@ use crate::coconut::keypair::KeyPair as CoconutKeyPair;
|
||||
use cosmwasm_std::Addr;
|
||||
use log::debug;
|
||||
use nym_coconut_dkg_common::dealer::DealerDetails;
|
||||
use nym_coconut_dkg_common::types::EpochState;
|
||||
use nym_coconut_dkg_common::types::{DealingIndex, EpochId, EpochState};
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_dkg::bte::{keys::KeyPair as DkgKeyPair, PublicKey, PublicKeyWithProof};
|
||||
use nym_dkg::{NodeIndex, RecoveredVerificationKeys, Threshold};
|
||||
use nym_dkg::{Dealing, NodeIndex, RecoveredVerificationKeys, Threshold};
|
||||
use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::path::{Path, PathBuf};
|
||||
use url::Url;
|
||||
|
||||
@@ -72,6 +73,7 @@ pub(crate) trait ConsistentState {
|
||||
fn proposal_id_value(&self) -> Result<u64, CoconutError>;
|
||||
async fn is_consistent(&self, epoch_state: EpochState) -> Result<(), CoconutError> {
|
||||
match epoch_state {
|
||||
EpochState::WaitingInitialisation => {}
|
||||
EpochState::PublicKeySubmission { .. } => {}
|
||||
EpochState::DealingExchange { .. } => {
|
||||
self.node_index_value()?;
|
||||
@@ -159,10 +161,54 @@ where
|
||||
.collect()
|
||||
}
|
||||
|
||||
mod generated_dealings {
|
||||
use nym_coconut_dkg_common::types::{DealingIndex, EpochId};
|
||||
use nym_dkg::Dealing;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::collections::HashMap;
|
||||
|
||||
type Helper = HashMap<EpochId, HashMap<DealingIndex, Vec<u8>>>;
|
||||
|
||||
pub fn serialize<S: Serializer>(
|
||||
dealings: &HashMap<EpochId, HashMap<DealingIndex, Dealing>>,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error> {
|
||||
let mut helper = HashMap::new();
|
||||
for (epoch, dealings) in dealings {
|
||||
let mut inner = HashMap::new();
|
||||
for (dealing_index, dealing) in dealings {
|
||||
inner.insert(*dealing_index, dealing.to_bytes());
|
||||
}
|
||||
helper.insert(*epoch, inner);
|
||||
}
|
||||
helper.serialize(serializer)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D: Deserializer<'de>>(
|
||||
deserializer: D,
|
||||
) -> Result<HashMap<EpochId, HashMap<DealingIndex, Dealing>>, D::Error> {
|
||||
let helper = <Helper>::deserialize(deserializer)?;
|
||||
|
||||
let mut epoch_dealings = HashMap::with_capacity(helper.len());
|
||||
for (epoch, dealings) in helper {
|
||||
let mut inner = HashMap::with_capacity(dealings.len());
|
||||
for (dealing_index, raw_dealing) in dealings {
|
||||
let dealing =
|
||||
Dealing::try_from_bytes(&raw_dealing).map_err(serde::de::Error::custom)?;
|
||||
inner.insert(dealing_index, dealing);
|
||||
}
|
||||
epoch_dealings.insert(epoch, inner);
|
||||
}
|
||||
Ok(epoch_dealings)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Serialize)]
|
||||
pub(crate) struct PersistentState {
|
||||
node_index: Option<NodeIndex>,
|
||||
dealers: BTreeMap<Addr, Result<DkgParticipant, ComplaintReason>>,
|
||||
#[serde(with = "generated_dealings")]
|
||||
generated_dealings: HashMap<EpochId, HashMap<DealingIndex, Dealing>>,
|
||||
receiver_index: Option<usize>,
|
||||
threshold: Option<Threshold>,
|
||||
#[serde(serialize_with = "vks_serialize")]
|
||||
@@ -179,6 +225,7 @@ impl From<&State> for PersistentState {
|
||||
PersistentState {
|
||||
node_index: s.node_index,
|
||||
dealers: s.dealers.clone(),
|
||||
generated_dealings: s.generated_dealings.clone(),
|
||||
receiver_index: s.receiver_index,
|
||||
threshold: s.threshold,
|
||||
recovered_vks: s.recovered_vks.clone(),
|
||||
@@ -204,10 +251,12 @@ impl PersistentState {
|
||||
pub(crate) struct State {
|
||||
persistent_state_path: PathBuf,
|
||||
announce_address: Url,
|
||||
identity_key: identity::PublicKey,
|
||||
dkg_keypair: DkgKeyPair,
|
||||
coconut_keypair: CoconutKeyPair,
|
||||
node_index: Option<NodeIndex>,
|
||||
dealers: BTreeMap<Addr, Result<DkgParticipant, ComplaintReason>>,
|
||||
generated_dealings: HashMap<EpochId, HashMap<DealingIndex, Dealing>>,
|
||||
receiver_index: Option<usize>,
|
||||
threshold: Option<Threshold>,
|
||||
recovered_vks: Vec<RecoveredVerificationKeys>,
|
||||
@@ -223,15 +272,18 @@ impl State {
|
||||
persistent_state: PersistentState,
|
||||
announce_address: Url,
|
||||
dkg_keypair: DkgKeyPair,
|
||||
identity_key: identity::PublicKey,
|
||||
coconut_keypair: CoconutKeyPair,
|
||||
) -> Self {
|
||||
State {
|
||||
persistent_state_path,
|
||||
announce_address,
|
||||
identity_key,
|
||||
dkg_keypair,
|
||||
coconut_keypair,
|
||||
node_index: persistent_state.node_index,
|
||||
dealers: persistent_state.dealers,
|
||||
generated_dealings: persistent_state.generated_dealings,
|
||||
receiver_index: persistent_state.receiver_index,
|
||||
threshold: persistent_state.threshold,
|
||||
recovered_vks: persistent_state.recovered_vks,
|
||||
@@ -265,6 +317,10 @@ impl State {
|
||||
&self.announce_address
|
||||
}
|
||||
|
||||
pub fn identity_key(&self) -> identity::PublicKey {
|
||||
self.identity_key
|
||||
}
|
||||
|
||||
pub fn dkg_keypair(&self) -> &DkgKeyPair {
|
||||
&self.dkg_keypair
|
||||
}
|
||||
@@ -277,6 +333,24 @@ impl State {
|
||||
self.coconut_keypair.take().await
|
||||
}
|
||||
|
||||
pub fn get_dealing(&self, epoch_id: EpochId, dealing_index: DealingIndex) -> Option<&Dealing> {
|
||||
self.generated_dealings
|
||||
.get(&epoch_id)
|
||||
.and_then(|epoch_dealings| epoch_dealings.get(&dealing_index))
|
||||
}
|
||||
|
||||
pub fn store_dealing(
|
||||
&mut self,
|
||||
epoch_id: EpochId,
|
||||
dealing_index: DealingIndex,
|
||||
dealing: Dealing,
|
||||
) {
|
||||
self.generated_dealings
|
||||
.entry(epoch_id)
|
||||
.or_default()
|
||||
.insert(dealing_index, dealing);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub async fn coconut_keypair(
|
||||
&self,
|
||||
@@ -304,6 +378,7 @@ impl State {
|
||||
.collect()
|
||||
}
|
||||
|
||||
// FIXME: BUG: if we remove dealers, we won't be able to verify shares of other parties...
|
||||
pub fn current_dealers_by_idx(&self) -> BTreeMap<NodeIndex, PublicKey> {
|
||||
self.dealers
|
||||
.iter()
|
||||
@@ -364,8 +439,7 @@ impl State {
|
||||
.find(|(addr, _)| *addr == dealer_addr)
|
||||
{
|
||||
debug!(
|
||||
"Dealer {} misbehaved: {:?}. It will be marked locally as bad dealer and ignored",
|
||||
dealer_addr, reason
|
||||
"Dealer {dealer_addr} misbehaved: {reason:?}. It will be marked locally as bad dealer and ignored",
|
||||
);
|
||||
*value = Err(reason);
|
||||
}
|
||||
@@ -395,7 +469,6 @@ impl State {
|
||||
self.was_in_progress = true;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn all_dealers(&self) -> &BTreeMap<Addr, Result<DkgParticipant, ComplaintReason>> {
|
||||
&self.dealers
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::coconut::dkg;
|
||||
use crate::coconut::dkg::client::DkgClient;
|
||||
use crate::coconut::dkg::complaints::ComplaintReason;
|
||||
use crate::coconut::dkg::state::{ConsistentState, State};
|
||||
@@ -13,24 +14,30 @@ use log::debug;
|
||||
use nym_coconut::tests::helpers::transpose_matrix;
|
||||
use nym_coconut::{check_vk_pairing, Base58, KeyPair, SecretKey, VerificationKey};
|
||||
use nym_coconut_dkg_common::event_attributes::DKG_PROPOSAL_ID;
|
||||
use nym_coconut_dkg_common::types::{NodeIndex, TOTAL_DEALINGS};
|
||||
use nym_coconut_dkg_common::types::{EpochId, NodeIndex};
|
||||
use nym_coconut_dkg_common::verification_key::owner_from_cosmos_msgs;
|
||||
use nym_coconut_interface::KeyPair as CoconutKeyPair;
|
||||
use nym_dkg::bte::{decrypt_share, setup};
|
||||
use nym_dkg::bte::decrypt_share;
|
||||
use nym_dkg::error::DkgError;
|
||||
use nym_dkg::{combine_shares, try_recover_verification_keys, Dealing, Threshold};
|
||||
use nym_pemstore::KeyPairPath;
|
||||
use nym_validator_client::nyxd::cosmwasm_client::logs::find_attribute;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
// Filter the dealers based on what dealing they posted (or not) in the contract
|
||||
|
||||
// TODO: change the return type to make sure that:
|
||||
// - each entry has the same number of dealings
|
||||
// - dealer data is not duplicated
|
||||
// - each dealer has submitted all or nothing
|
||||
async fn deterministic_filter_dealers(
|
||||
dkg_client: &DkgClient,
|
||||
state: &mut State,
|
||||
epoch_id: EpochId,
|
||||
threshold: Threshold,
|
||||
resharing: bool,
|
||||
) -> Result<Vec<BTreeMap<NodeIndex, (Addr, Dealing)>>, CoconutError> {
|
||||
let mut dealings_maps = vec![];
|
||||
let mut dealings_maps = Vec::new();
|
||||
let initial_dealers_by_addr = state.current_dealers_by_addr();
|
||||
let initial_receivers = state.current_dealers_by_idx();
|
||||
let initial_resharing_dealers = if resharing {
|
||||
@@ -43,36 +50,46 @@ async fn deterministic_filter_dealers(
|
||||
vec![]
|
||||
};
|
||||
|
||||
let params = setup();
|
||||
let params = dkg::params();
|
||||
|
||||
for idx in 0..TOTAL_DEALINGS {
|
||||
let dealings = dkg_client.get_dealings(idx).await?;
|
||||
// note: this is a temporary solution to replicate the behaviour of the old code so that I wouldn't need to
|
||||
// fix the filtering in this PR, because the old code is quite buggy and misses few edge cases
|
||||
let mut raw_dealings = HashMap::new();
|
||||
for dealer in state.all_dealers().keys() {
|
||||
let dealer_dealings = dkg_client
|
||||
.get_dealings(epoch_id, dealer.to_string())
|
||||
.await?;
|
||||
for dealing in dealer_dealings {
|
||||
let old_contract_dealing = raw_dealings.entry(dealing.index).or_insert(Vec::new());
|
||||
old_contract_dealing.push((dealer.clone(), dealing.data))
|
||||
}
|
||||
}
|
||||
|
||||
// this is a temporary thing to reintroduce the bug to make sure tests still pass : )
|
||||
// i will fix it properly in next PR
|
||||
for dealing_index in 0..5 {
|
||||
let dealings = raw_dealings.remove(&dealing_index).unwrap_or_default();
|
||||
let dealings_map =
|
||||
BTreeMap::from_iter(dealings.into_iter().filter_map(|contract_dealing| {
|
||||
match Dealing::try_from(&contract_dealing.dealing) {
|
||||
BTreeMap::from_iter(dealings.into_iter().filter_map(|(dealer, dealing)| {
|
||||
match Dealing::try_from(&dealing) {
|
||||
Ok(dealing) => {
|
||||
if dealing
|
||||
.verify(¶ms, threshold, &initial_receivers, None)
|
||||
.verify(params, threshold, &initial_receivers, None)
|
||||
.is_err()
|
||||
{
|
||||
state.mark_bad_dealer(
|
||||
&contract_dealing.dealer,
|
||||
&dealer,
|
||||
ComplaintReason::DealingVerificationError,
|
||||
);
|
||||
None
|
||||
} else if let Some(idx) =
|
||||
initial_dealers_by_addr.get(&contract_dealing.dealer)
|
||||
{
|
||||
Some((*idx, (contract_dealing.dealer, dealing)))
|
||||
} else {
|
||||
None
|
||||
initial_dealers_by_addr
|
||||
.get(&dealer)
|
||||
.map(|idx| (*idx, (dealer, dealing)))
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
state.mark_bad_dealer(
|
||||
&contract_dealing.dealer,
|
||||
ComplaintReason::MalformedDealing,
|
||||
);
|
||||
state.mark_bad_dealer(&dealer, ComplaintReason::MalformedDealing);
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -80,6 +97,40 @@ async fn deterministic_filter_dealers(
|
||||
dealings_maps.push(dealings_map);
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// for dealer in initial_dealers_by_addr.keys() {
|
||||
// let Some(dealer_index) = initial_dealers_by_addr.get(dealer) else {
|
||||
// warn!("could not obtain dealer index of {dealer}");
|
||||
// continue;
|
||||
// };
|
||||
//
|
||||
// let dealer_dealings = dkg_client
|
||||
// .get_dealings(epoch_id, dealer.to_string())
|
||||
// .await?;
|
||||
//
|
||||
// for contract_dealing in dealer_dealings {
|
||||
// match Dealing::try_from(&contract_dealing.data) {
|
||||
// // FIXME: bug: this doesn't check resharing
|
||||
// Ok(dealing) => {
|
||||
// if let Err(err) = dealing.verify(params, threshold, &initial_receivers, None) {
|
||||
// println!("dealing verification failure from {dealer}: {err}");
|
||||
// state.mark_bad_dealer(dealer, ComplaintReason::DealingVerificationError);
|
||||
// } else {
|
||||
// let entry = dealings_maps
|
||||
// .entry(contract_dealing.index)
|
||||
// .or_insert(BTreeMap::new());
|
||||
// entry.insert(*dealer_index, (dealer.clone(), dealing));
|
||||
// }
|
||||
// }
|
||||
// Err(err) => {
|
||||
// warn!("malformed dealing from {dealer}: {err}");
|
||||
// state.mark_bad_dealer(dealer, ComplaintReason::MalformedDealing);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
for (addr, _) in initial_dealers_by_addr.iter() {
|
||||
// in resharing mode, we don't commit dealings from dealers outside the initial set
|
||||
if !resharing || initial_resharing_dealers.contains(addr) {
|
||||
@@ -155,6 +206,7 @@ fn derive_partial_keypair(
|
||||
pub(crate) async fn verification_key_submission(
|
||||
dkg_client: &DkgClient,
|
||||
state: &mut State,
|
||||
epoch_id: EpochId,
|
||||
keypair_path: &KeyPairPath,
|
||||
resharing: bool,
|
||||
) -> Result<(), CoconutError> {
|
||||
@@ -165,7 +217,7 @@ pub(crate) async fn verification_key_submission(
|
||||
|
||||
let threshold = state.threshold()?;
|
||||
let dealings_maps =
|
||||
deterministic_filter_dealers(dkg_client, state, threshold, resharing).await?;
|
||||
deterministic_filter_dealers(dkg_client, state, epoch_id, threshold, resharing).await?;
|
||||
debug!(
|
||||
"Filtered dealers to {:?}",
|
||||
dealings_maps[0].keys().collect::<Vec<_>>()
|
||||
@@ -307,13 +359,14 @@ pub(crate) mod tests {
|
||||
use crate::coconut::KeyPair;
|
||||
use nym_coconut::aggregate_verification_keys;
|
||||
use nym_coconut_dkg_common::dealer::DealerDetails;
|
||||
use nym_coconut_dkg_common::types::InitialReplacementData;
|
||||
use nym_coconut_dkg_common::types::{EpochId, InitialReplacementData, PartialContractDealing};
|
||||
use nym_coconut_dkg_common::verification_key::ContractVKShare;
|
||||
use nym_contracts_common::dealings::ContractSafeBytes;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_dkg::bte::keys::KeyPair as DkgKeyPair;
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
use rand::rngs::OsRng;
|
||||
use rand::Rng;
|
||||
use rand_07::thread_rng;
|
||||
use std::collections::HashMap;
|
||||
use std::env::temp_dir;
|
||||
use std::path::PathBuf;
|
||||
@@ -323,7 +376,9 @@ pub(crate) mod tests {
|
||||
|
||||
struct MockContractDb {
|
||||
dealer_details_db: Arc<RwLock<HashMap<String, (DealerDetails, bool)>>>,
|
||||
dealings_db: Arc<RwLock<HashMap<String, Vec<ContractSafeBytes>>>>,
|
||||
// it's a really bad practice, but I'm not going to be changing it now...
|
||||
#[allow(clippy::type_complexity)]
|
||||
dealings_db: Arc<RwLock<HashMap<EpochId, HashMap<String, Vec<PartialContractDealing>>>>>,
|
||||
proposal_db: Arc<RwLock<HashMap<u64, ProposalResponse>>>,
|
||||
verification_share_db: Arc<RwLock<HashMap<String, ContractVKShare>>>,
|
||||
threshold_db: Arc<RwLock<Option<Threshold>>>,
|
||||
@@ -351,8 +406,9 @@ pub(crate) mod tests {
|
||||
];
|
||||
|
||||
async fn prepare_clients_and_states(db: &MockContractDb) -> Vec<(DkgClient, State)> {
|
||||
let params = setup();
|
||||
let params = dkg::params();
|
||||
let mut clients_and_states = vec![];
|
||||
let identity_keypair = identity::KeyPair::new(&mut thread_rng());
|
||||
|
||||
for addr in TEST_VALIDATORS_ADDRESS {
|
||||
let dkg_client = DkgClient::new(
|
||||
@@ -364,12 +420,13 @@ pub(crate) mod tests {
|
||||
.with_threshold(&db.threshold_db)
|
||||
.with_initial_dealers_db(&db.initial_dealers_db),
|
||||
);
|
||||
let keypair = DkgKeyPair::new(¶ms, OsRng);
|
||||
let keypair = DkgKeyPair::new(params, OsRng);
|
||||
let state = State::new(
|
||||
PathBuf::default(),
|
||||
PersistentState::default(),
|
||||
Url::parse("localhost:8000").unwrap(),
|
||||
keypair,
|
||||
*identity_keypair.public_key(),
|
||||
KeyPair::new(),
|
||||
);
|
||||
clients_and_states.push((dkg_client, state));
|
||||
@@ -403,7 +460,7 @@ pub(crate) mod tests {
|
||||
let private_key_path = temp_dir().join(format!("private{}.pem", random_file));
|
||||
let public_key_path = temp_dir().join(format!("public{}.pem", random_file));
|
||||
let keypair_path = KeyPairPath::new(private_key_path.clone(), public_key_path.clone());
|
||||
verification_key_submission(dkg_client, state, &keypair_path, false)
|
||||
verification_key_submission(dkg_client, state, 0, &keypair_path, false)
|
||||
.await
|
||||
.unwrap();
|
||||
std::fs::remove_file(private_key_path).unwrap();
|
||||
@@ -441,11 +498,13 @@ pub(crate) mod tests {
|
||||
async fn check_dealers_filter_all_good() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_dealing(&db).await;
|
||||
let contract_state = clients_and_states[0].0.get_contract_state().await.unwrap();
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2, false)
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 0, 2, false)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), TOTAL_DEALINGS);
|
||||
assert_eq!(filtered.len(), contract_state.key_size as usize);
|
||||
for mapping in filtered.iter() {
|
||||
assert_eq!(mapping.len(), 4);
|
||||
}
|
||||
@@ -457,23 +516,27 @@ pub(crate) mod tests {
|
||||
async fn check_dealers_filter_one_bad_dealing() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_dealing(&db).await;
|
||||
let contract_state = clients_and_states[0].0.get_contract_state().await.unwrap();
|
||||
|
||||
// corrupt just one dealing
|
||||
db.dealings_db
|
||||
.write()
|
||||
.unwrap()
|
||||
.entry(TEST_VALIDATORS_ADDRESS[0].to_string())
|
||||
.and_modify(|dealings| {
|
||||
let mut last = dealings.pop().unwrap();
|
||||
last.0.pop();
|
||||
dealings.push(last);
|
||||
.entry(0)
|
||||
.and_modify(|epoch_dealings| {
|
||||
let validator_dealings = epoch_dealings
|
||||
.entry(TEST_VALIDATORS_ADDRESS[0].to_string())
|
||||
.or_default();
|
||||
let mut last = validator_dealings.pop().unwrap();
|
||||
last.data.0.pop();
|
||||
validator_dealings.push(last);
|
||||
});
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2, false)
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 0, 2, false)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), TOTAL_DEALINGS);
|
||||
assert_eq!(filtered.len(), contract_state.key_size as usize);
|
||||
let corrupted_status = state
|
||||
.all_dealers()
|
||||
.get(&Addr::unchecked(TEST_VALIDATORS_ADDRESS[0]))
|
||||
@@ -489,6 +552,7 @@ pub(crate) mod tests {
|
||||
async fn check_dealers_resharing_filter_one_missing_dealing() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states(&db).await;
|
||||
let contract_state = clients_and_states[0].0.get_contract_state().await.unwrap();
|
||||
|
||||
// add all but the first dealing
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
@@ -502,10 +566,10 @@ pub(crate) mod tests {
|
||||
initial_dealers: vec![Addr::unchecked(TEST_VALIDATORS_ADDRESS[0])],
|
||||
initial_height: 1,
|
||||
});
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2, true)
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 0, 2, true)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), TOTAL_DEALINGS);
|
||||
assert_eq!(filtered.len(), contract_state.key_size as usize);
|
||||
let corrupted_status = state
|
||||
.all_dealers()
|
||||
.get(&Addr::unchecked(TEST_VALIDATORS_ADDRESS[0]))
|
||||
@@ -522,6 +586,7 @@ pub(crate) mod tests {
|
||||
async fn check_dealers_resharing_filter_one_noninitial_missing_dealing() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states(&db).await;
|
||||
let contract_state = clients_and_states[0].0.get_contract_state().await.unwrap();
|
||||
|
||||
// add all but the first dealing
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
@@ -535,10 +600,10 @@ pub(crate) mod tests {
|
||||
initial_dealers: vec![],
|
||||
initial_height: 1,
|
||||
});
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2, true)
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 0, 2, true)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), TOTAL_DEALINGS);
|
||||
assert_eq!(filtered.len(), contract_state.key_size as usize);
|
||||
assert!(state
|
||||
.all_dealers()
|
||||
.get(&Addr::unchecked(TEST_VALIDATORS_ADDRESS[0]))
|
||||
@@ -553,23 +618,27 @@ pub(crate) mod tests {
|
||||
async fn check_dealers_filter_all_bad_dealings() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_dealing(&db).await;
|
||||
let contract_state = clients_and_states[0].0.get_contract_state().await.unwrap();
|
||||
|
||||
// corrupt all dealings of one address
|
||||
db.dealings_db
|
||||
.write()
|
||||
.unwrap()
|
||||
.entry(TEST_VALIDATORS_ADDRESS[0].to_string())
|
||||
.and_modify(|dealings| {
|
||||
dealings.iter_mut().for_each(|dealing| {
|
||||
dealing.0.pop();
|
||||
.entry(0)
|
||||
.and_modify(|epoch_dealings| {
|
||||
let validator_dealings = epoch_dealings
|
||||
.entry(TEST_VALIDATORS_ADDRESS[0].to_string())
|
||||
.or_default();
|
||||
validator_dealings.iter_mut().for_each(|dealing| {
|
||||
dealing.data.0.pop();
|
||||
});
|
||||
});
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2, false)
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 0, 2, false)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), TOTAL_DEALINGS);
|
||||
assert_eq!(filtered.len(), contract_state.key_size as usize);
|
||||
for mapping in filtered.iter() {
|
||||
assert_eq!(mapping.len(), 3);
|
||||
}
|
||||
@@ -588,28 +657,32 @@ pub(crate) mod tests {
|
||||
async fn check_dealers_filter_malformed_dealing() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_dealing(&db).await;
|
||||
let contract_state = clients_and_states[0].0.get_contract_state().await.unwrap();
|
||||
|
||||
// corrupt just one dealing
|
||||
db.dealings_db
|
||||
.write()
|
||||
.unwrap()
|
||||
.entry(TEST_VALIDATORS_ADDRESS[0].to_string())
|
||||
.and_modify(|dealings| {
|
||||
let mut last = dealings.pop().unwrap();
|
||||
last.0.pop();
|
||||
dealings.push(last);
|
||||
.entry(0)
|
||||
.and_modify(|epoch_dealings| {
|
||||
let validator_dealings = epoch_dealings
|
||||
.get_mut(TEST_VALIDATORS_ADDRESS[0])
|
||||
.expect("no dealing");
|
||||
let mut last = validator_dealings.pop().unwrap();
|
||||
last.data.0.pop();
|
||||
validator_dealings.push(last);
|
||||
});
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
deterministic_filter_dealers(dkg_client, state, 2, false)
|
||||
deterministic_filter_dealers(dkg_client, state, 0, 2, false)
|
||||
.await
|
||||
.unwrap();
|
||||
// second filter will leave behind the bad dealer and surface why it was left out
|
||||
// in the first place
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2, false)
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 0, 2, false)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), TOTAL_DEALINGS);
|
||||
assert_eq!(filtered.len(), contract_state.key_size as usize);
|
||||
let corrupted_status = state
|
||||
.all_dealers()
|
||||
.get(&Addr::unchecked(TEST_VALIDATORS_ADDRESS[0]))
|
||||
@@ -625,33 +698,37 @@ pub(crate) mod tests {
|
||||
async fn check_dealers_filter_dealing_verification_error() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_dealing(&db).await;
|
||||
let contract_state = clients_and_states[0].0.get_contract_state().await.unwrap();
|
||||
|
||||
// corrupt just one dealing
|
||||
db.dealings_db
|
||||
.write()
|
||||
.unwrap()
|
||||
.entry(TEST_VALIDATORS_ADDRESS[0].to_string())
|
||||
.and_modify(|dealings| {
|
||||
let mut last = dealings.pop().unwrap();
|
||||
let value = last.0.pop().unwrap();
|
||||
.entry(0)
|
||||
.and_modify(|epoch_dealings| {
|
||||
let validator_dealings = epoch_dealings
|
||||
.entry(TEST_VALIDATORS_ADDRESS[0].to_string())
|
||||
.or_default();
|
||||
let mut last = validator_dealings.pop().unwrap();
|
||||
let value = last.data.0.pop().unwrap();
|
||||
if value == 42 {
|
||||
last.0.push(43);
|
||||
last.data.0.push(43);
|
||||
} else {
|
||||
last.0.push(42);
|
||||
last.data.0.push(42);
|
||||
}
|
||||
dealings.push(last);
|
||||
validator_dealings.push(last);
|
||||
});
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
deterministic_filter_dealers(dkg_client, state, 2, false)
|
||||
deterministic_filter_dealers(dkg_client, state, 0, 2, false)
|
||||
.await
|
||||
.unwrap();
|
||||
// second filter will leave behind the bad dealer and surface why it was left out
|
||||
// in the first place
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2, false)
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 0, 2, false)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), TOTAL_DEALINGS);
|
||||
assert_eq!(filtered.len(), contract_state.key_size as usize);
|
||||
let corrupted_status = state
|
||||
.all_dealers()
|
||||
.get(&Addr::unchecked(TEST_VALIDATORS_ADDRESS[0]))
|
||||
@@ -668,7 +745,7 @@ pub(crate) mod tests {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_dealing(&db).await;
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2, false)
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 0, 2, false)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(derive_partial_keypair(state, 2, filtered).is_ok());
|
||||
@@ -685,15 +762,18 @@ pub(crate) mod tests {
|
||||
db.dealings_db
|
||||
.write()
|
||||
.unwrap()
|
||||
.entry(TEST_VALIDATORS_ADDRESS[0].to_string())
|
||||
.and_modify(|dealings| {
|
||||
let mut last = dealings.pop().unwrap();
|
||||
last.0.pop();
|
||||
dealings.push(last);
|
||||
.entry(0)
|
||||
.and_modify(|epoch_dealings| {
|
||||
let validator_dealings = epoch_dealings
|
||||
.entry(TEST_VALIDATORS_ADDRESS[0].to_string())
|
||||
.or_default();
|
||||
let mut last = validator_dealings.pop().unwrap();
|
||||
last.data.0.pop();
|
||||
validator_dealings.push(last);
|
||||
});
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2, false)
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 0, 2, false)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(derive_partial_keypair(state, 2, filtered).is_ok());
|
||||
@@ -863,12 +943,14 @@ pub(crate) mod tests {
|
||||
.with_threshold(&db.threshold_db)
|
||||
.with_initial_dealers_db(&db.initial_dealers_db),
|
||||
);
|
||||
let keypair = DkgKeyPair::new(&setup(), OsRng);
|
||||
let keypair = DkgKeyPair::new(dkg::params(), OsRng);
|
||||
let identity_keypair = identity::KeyPair::new(&mut thread_rng());
|
||||
let state = State::new(
|
||||
PathBuf::default(),
|
||||
PersistentState::default(),
|
||||
Url::parse("localhost:8000").unwrap(),
|
||||
keypair,
|
||||
*identity_keypair.public_key(),
|
||||
KeyPair::new(),
|
||||
);
|
||||
|
||||
@@ -906,7 +988,7 @@ pub(crate) mod tests {
|
||||
let private_key_path = temp_dir().join(format!("private{}.pem", random_file));
|
||||
let public_key_path = temp_dir().join(format!("public{}.pem", random_file));
|
||||
let keypair_path = KeyPairPath::new(private_key_path.clone(), public_key_path.clone());
|
||||
verification_key_submission(dkg_client, state, &keypair_path, true)
|
||||
verification_key_submission(dkg_client, state, 0, &keypair_path, true)
|
||||
.await
|
||||
.unwrap();
|
||||
std::fs::remove_file(private_key_path).unwrap();
|
||||
@@ -967,12 +1049,14 @@ pub(crate) mod tests {
|
||||
.with_threshold(&db.threshold_db)
|
||||
.with_initial_dealers_db(&db.initial_dealers_db),
|
||||
);
|
||||
let keypair = DkgKeyPair::new(&setup(), OsRng);
|
||||
let keypair = DkgKeyPair::new(dkg::params(), OsRng);
|
||||
let identity_keypair = identity::KeyPair::new(&mut thread_rng());
|
||||
let state = State::new(
|
||||
PathBuf::default(),
|
||||
PersistentState::default(),
|
||||
Url::parse("localhost:8000").unwrap(),
|
||||
keypair,
|
||||
*identity_keypair.public_key(),
|
||||
KeyPair::new(),
|
||||
);
|
||||
let new_dkg_client2 = DkgClient::new(
|
||||
@@ -986,12 +1070,14 @@ pub(crate) mod tests {
|
||||
.with_threshold(&db.threshold_db)
|
||||
.with_initial_dealers_db(&db.initial_dealers_db),
|
||||
);
|
||||
let keypair = DkgKeyPair::new(&setup(), OsRng);
|
||||
let keypair = DkgKeyPair::new(dkg::params(), OsRng);
|
||||
let identity_keypair = identity::KeyPair::new(&mut thread_rng());
|
||||
let state2 = State::new(
|
||||
PathBuf::default(),
|
||||
PersistentState::default(),
|
||||
Url::parse("localhost:8000").unwrap(),
|
||||
keypair,
|
||||
*identity_keypair.public_key(),
|
||||
KeyPair::new(),
|
||||
);
|
||||
|
||||
@@ -1022,7 +1108,7 @@ pub(crate) mod tests {
|
||||
let private_key_path = temp_dir().join(format!("private{}.pem", random_file));
|
||||
let public_key_path = temp_dir().join(format!("public{}.pem", random_file));
|
||||
let keypair_path = KeyPairPath::new(private_key_path.clone(), public_key_path.clone());
|
||||
verification_key_submission(dkg_client, state, &keypair_path, false)
|
||||
verification_key_submission(dkg_client, state, 0, &keypair_path, false)
|
||||
.await
|
||||
.unwrap();
|
||||
std::fs::remove_file(private_key_path).unwrap();
|
||||
@@ -1098,7 +1184,7 @@ pub(crate) mod tests {
|
||||
let private_key_path = temp_dir().join(format!("private{}.pem", random_file));
|
||||
let public_key_path = temp_dir().join(format!("public{}.pem", random_file));
|
||||
let keypair_path = KeyPairPath::new(private_key_path.clone(), public_key_path.clone());
|
||||
verification_key_submission(dkg_client, state, &keypair_path, true)
|
||||
verification_key_submission(dkg_client, state, 0, &keypair_path, true)
|
||||
.await
|
||||
.unwrap();
|
||||
std::fs::remove_file(private_key_path).unwrap();
|
||||
|
||||
@@ -124,6 +124,11 @@ pub enum CoconutError {
|
||||
// I guess we should make this one a bit more detailed
|
||||
#[error("the provided query arguments were invalid")]
|
||||
InvalidQueryArguments,
|
||||
|
||||
#[error("insufficient number of dealings provided to derive the key")]
|
||||
InsufficientDealings {
|
||||
// TODO: details
|
||||
},
|
||||
}
|
||||
|
||||
impl<'r, 'o: 'r> Responder<'r, 'o> for CoconutError {
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::support::storage::NymApiStorage;
|
||||
use async_trait::async_trait;
|
||||
use cosmwasm_std::{coin, to_binary, Addr, CosmosMsg, Decimal, WasmMsg};
|
||||
use cw3::ProposalResponse;
|
||||
use cw4::MemberResponse;
|
||||
use cw4::{Cw4Contract, MemberResponse};
|
||||
use nym_api_requests::coconut::models::{IssuedCredentialBody, IssuedCredentialResponse};
|
||||
use nym_api_requests::coconut::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
|
||||
@@ -23,16 +23,17 @@ use nym_coconut_bandwidth_contract_common::spend_credential::{
|
||||
SpendCredential, SpendCredentialResponse,
|
||||
};
|
||||
use nym_coconut_dkg_common::dealer::{
|
||||
ContractDealing, DealerDetails, DealerDetailsResponse, DealerType,
|
||||
DealerDetails, DealerDetailsResponse, DealerType, DealingStatusResponse,
|
||||
};
|
||||
use nym_coconut_dkg_common::event_attributes::{DKG_PROPOSAL_ID, NODE_INDEX};
|
||||
use nym_coconut_dkg_common::types::{
|
||||
EncodedBTEPublicKeyWithProof, Epoch, EpochId, InitialReplacementData, TOTAL_DEALINGS,
|
||||
DealingIndex, EncodedBTEPublicKeyWithProof, Epoch, EpochId, InitialReplacementData,
|
||||
PartialContractDealing, State as ContractState,
|
||||
};
|
||||
use nym_coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare};
|
||||
use nym_coconut_interface::{hash_to_scalar, Credential, VerificationKey};
|
||||
use nym_config::defaults::VOUCHER_INFO;
|
||||
use nym_contracts_common::dealings::ContractSafeBytes;
|
||||
use nym_contracts_common::IdentityKey;
|
||||
use nym_credentials::coconut::bandwidth::BandwidthVoucher;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_dkg::Threshold;
|
||||
@@ -67,9 +68,12 @@ pub(crate) struct DummyClient {
|
||||
spent_credential_db: Arc<RwLock<HashMap<String, SpendCredentialResponse>>>,
|
||||
|
||||
epoch: Arc<RwLock<Epoch>>,
|
||||
contract_state: Arc<RwLock<ContractState>>,
|
||||
dealer_details: Arc<RwLock<HashMap<String, (DealerDetails, bool)>>>,
|
||||
threshold: Arc<RwLock<Option<Threshold>>>,
|
||||
dealings: Arc<RwLock<HashMap<String, Vec<ContractSafeBytes>>>>,
|
||||
// it's a really bad practice, but I'm not going to be changing it now...
|
||||
#[allow(clippy::type_complexity)]
|
||||
dealings: Arc<RwLock<HashMap<EpochId, HashMap<String, Vec<PartialContractDealing>>>>>,
|
||||
verification_share: Arc<RwLock<HashMap<String, ContractVKShare>>>,
|
||||
group_db: Arc<RwLock<HashMap<String, MemberResponse>>>,
|
||||
initial_dealers_db: Arc<RwLock<Option<InitialReplacementData>>>,
|
||||
@@ -83,6 +87,12 @@ impl DummyClient {
|
||||
proposal_db: Arc::new(RwLock::new(HashMap::new())),
|
||||
spent_credential_db: Arc::new(RwLock::new(HashMap::new())),
|
||||
epoch: Arc::new(RwLock::new(Epoch::default())),
|
||||
contract_state: Arc::new(RwLock::new(ContractState {
|
||||
mix_denom: TEST_COIN_DENOM.to_string(),
|
||||
multisig_addr: Addr::unchecked("dummy address"),
|
||||
group_addr: Cw4Contract::new(Addr::unchecked("dummy cw4")),
|
||||
key_size: 5,
|
||||
})),
|
||||
dealer_details: Arc::new(RwLock::new(HashMap::new())),
|
||||
threshold: Arc::new(RwLock::new(None)),
|
||||
dealings: Arc::new(RwLock::new(HashMap::new())),
|
||||
@@ -131,9 +141,11 @@ impl DummyClient {
|
||||
self
|
||||
}
|
||||
|
||||
// it's a really bad practice, but I'm not going to be changing it now...
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn with_dealings(
|
||||
mut self,
|
||||
dealings: &Arc<RwLock<HashMap<String, Vec<ContractSafeBytes>>>>,
|
||||
dealings: &Arc<RwLock<HashMap<EpochId, HashMap<String, Vec<PartialContractDealing>>>>>,
|
||||
) -> Self {
|
||||
self.dealings = Arc::clone(dealings);
|
||||
self
|
||||
@@ -203,6 +215,10 @@ impl super::client::Client for DummyClient {
|
||||
})
|
||||
}
|
||||
|
||||
async fn contract_state(&self) -> Result<ContractState> {
|
||||
Ok(self.contract_state.read().unwrap().clone())
|
||||
}
|
||||
|
||||
async fn get_current_epoch(&self) -> Result<Epoch> {
|
||||
Ok(*self.epoch.read().unwrap())
|
||||
}
|
||||
@@ -248,6 +264,21 @@ impl super::client::Client for DummyClient {
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_dealing_status(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
) -> crate::coconut::error::Result<DealingStatusResponse> {
|
||||
let dealings = self.get_dealings(epoch_id, &dealer).await?;
|
||||
Ok(DealingStatusResponse {
|
||||
epoch_id,
|
||||
dealer: Addr::unchecked(dealer),
|
||||
dealing_index,
|
||||
dealing_submitted: dealings.get(dealing_index as usize).is_some(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_current_dealers(&self) -> Result<Vec<DealerDetails>> {
|
||||
Ok(self
|
||||
.dealer_details
|
||||
@@ -259,17 +290,21 @@ impl super::client::Client for DummyClient {
|
||||
.collect())
|
||||
}
|
||||
|
||||
async fn get_dealings(&self, idx: usize) -> Result<Vec<ContractDealing>> {
|
||||
async fn get_dealings(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: &str,
|
||||
) -> Result<Vec<PartialContractDealing>> {
|
||||
Ok(self
|
||||
.dealings
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|(dealer, dealings)| ContractDealing {
|
||||
dealing: dealings.get(idx).unwrap().clone(),
|
||||
dealer: Addr::unchecked(dealer),
|
||||
})
|
||||
.collect())
|
||||
.get(&epoch_id)
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
.get(dealer)
|
||||
.cloned()
|
||||
.unwrap_or_default())
|
||||
}
|
||||
|
||||
async fn get_verification_key_shares(
|
||||
@@ -322,6 +357,7 @@ impl super::client::Client for DummyClient {
|
||||
async fn register_dealer(
|
||||
&self,
|
||||
bte_public_key_with_proof: EncodedBTEPublicKeyWithProof,
|
||||
identity_key: IdentityKey,
|
||||
announce_address: String,
|
||||
_resharing: bool,
|
||||
) -> Result<ExecuteResult> {
|
||||
@@ -345,6 +381,7 @@ impl super::client::Client for DummyClient {
|
||||
DealerDetails {
|
||||
address: Addr::unchecked(self.validator_address.to_string()),
|
||||
bte_public_key_with_proof,
|
||||
ed25519_identity: identity_key,
|
||||
announce_address,
|
||||
assigned_index,
|
||||
},
|
||||
@@ -367,19 +404,16 @@ impl super::client::Client for DummyClient {
|
||||
|
||||
async fn submit_dealing(
|
||||
&self,
|
||||
dealing_bytes: ContractSafeBytes,
|
||||
dealing: PartialContractDealing,
|
||||
_resharing: bool,
|
||||
) -> Result<ExecuteResult> {
|
||||
self.dealings
|
||||
.write()
|
||||
.unwrap()
|
||||
let current_epoch = self.epoch.read().unwrap().epoch_id;
|
||||
let mut guard = self.dealings.write().unwrap();
|
||||
let epoch_dealings = guard.entry(current_epoch).or_default();
|
||||
let existing_dealings = epoch_dealings
|
||||
.entry(self.validator_address.to_string())
|
||||
.and_modify(|v| {
|
||||
if v.len() < TOTAL_DEALINGS {
|
||||
v.push(dealing_bytes.clone())
|
||||
}
|
||||
})
|
||||
.or_insert_with(|| vec![dealing_bytes]);
|
||||
.or_default();
|
||||
existing_dealings.push(dealing);
|
||||
|
||||
Ok(ExecuteResult {
|
||||
logs: vec![],
|
||||
|
||||
@@ -70,6 +70,7 @@ async fn start_nym_api_tasks(config: Config) -> anyhow::Result<ShutdownHandles>
|
||||
|
||||
let coconut_keypair = coconut::keypair::KeyPair::new();
|
||||
let identity_keypair = config.base.storage_paths.load_identity()?;
|
||||
let identity_public_key = *identity_keypair.public_key();
|
||||
|
||||
// let's build our rocket!
|
||||
let rocket = http::setup_rocket(
|
||||
@@ -137,6 +138,7 @@ async fn start_nym_api_tasks(config: Config) -> anyhow::Result<ShutdownHandles>
|
||||
&config.coconut_signer,
|
||||
nyxd_client.clone(),
|
||||
coconut_keypair,
|
||||
identity_public_key,
|
||||
OsRng,
|
||||
&shutdown,
|
||||
)
|
||||
|
||||
@@ -9,15 +9,17 @@ use async_trait::async_trait;
|
||||
use cw3::ProposalResponse;
|
||||
use cw4::MemberResponse;
|
||||
use nym_coconut_bandwidth_contract_common::spend_credential::SpendCredentialResponse;
|
||||
use nym_coconut_dkg_common::dealer::DealingStatusResponse;
|
||||
use nym_coconut_dkg_common::msg::QueryMsg as DkgQueryMsg;
|
||||
use nym_coconut_dkg_common::types::InitialReplacementData;
|
||||
use nym_coconut_dkg_common::types::{
|
||||
DealingIndex, InitialReplacementData, PartialContractDealing, State,
|
||||
};
|
||||
use nym_coconut_dkg_common::{
|
||||
dealer::{ContractDealing, DealerDetails, DealerDetailsResponse},
|
||||
dealer::{DealerDetails, DealerDetailsResponse},
|
||||
types::{EncodedBTEPublicKeyWithProof, Epoch, EpochId},
|
||||
verification_key::{ContractVKShare, VerificationKeyShare},
|
||||
};
|
||||
use nym_config::defaults::{ChainDetails, NymNetworkDetails};
|
||||
use nym_contracts_common::dealings::ContractSafeBytes;
|
||||
use nym_ephemera_common::msg::QueryMsg as EphemeraQueryMsg;
|
||||
use nym_ephemera_common::types::JsonPeerInfo;
|
||||
use nym_mixnet_contract_common::families::FamilyHead;
|
||||
@@ -374,6 +376,10 @@ impl crate::coconut::client::Client for Client {
|
||||
))
|
||||
}
|
||||
|
||||
async fn contract_state(&self) -> crate::coconut::error::Result<State> {
|
||||
Ok(nyxd_query!(self, get_state().await?))
|
||||
}
|
||||
|
||||
async fn get_current_epoch(&self) -> crate::coconut::error::Result<Epoch> {
|
||||
Ok(nyxd_query!(self, get_current_epoch().await?))
|
||||
}
|
||||
@@ -401,15 +407,31 @@ impl crate::coconut::client::Client for Client {
|
||||
Ok(nyxd_query!(self, get_dealer_details(self_address).await?))
|
||||
}
|
||||
|
||||
async fn get_dealing_status(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
) -> crate::coconut::error::Result<DealingStatusResponse> {
|
||||
Ok(nyxd_query!(
|
||||
self,
|
||||
get_dealing_status(epoch_id, dealer, dealing_index).await?
|
||||
))
|
||||
}
|
||||
|
||||
async fn get_current_dealers(&self) -> crate::coconut::error::Result<Vec<DealerDetails>> {
|
||||
Ok(nyxd_query!(self, get_all_current_dealers().await?))
|
||||
}
|
||||
|
||||
async fn get_dealings(
|
||||
&self,
|
||||
idx: usize,
|
||||
) -> crate::coconut::error::Result<Vec<ContractDealing>> {
|
||||
Ok(nyxd_query!(self, get_all_epoch_dealings(idx as u64).await?))
|
||||
epoch_id: EpochId,
|
||||
dealer: &str,
|
||||
) -> crate::coconut::error::Result<Vec<PartialContractDealing>> {
|
||||
Ok(nyxd_query!(
|
||||
self,
|
||||
get_all_dealer_dealings(epoch_id, dealer).await?
|
||||
))
|
||||
}
|
||||
|
||||
async fn get_verification_key_shares(
|
||||
@@ -445,23 +467,24 @@ impl crate::coconut::client::Client for Client {
|
||||
async fn register_dealer(
|
||||
&self,
|
||||
bte_key: EncodedBTEPublicKeyWithProof,
|
||||
identity_key: IdentityKey,
|
||||
announce_address: String,
|
||||
resharing: bool,
|
||||
) -> Result<ExecuteResult, CoconutError> {
|
||||
Ok(nyxd_signing!(
|
||||
self,
|
||||
register_dealer(bte_key, announce_address, resharing, None).await?
|
||||
register_dealer(bte_key, announce_address, identity_key, resharing, None).await?
|
||||
))
|
||||
}
|
||||
|
||||
async fn submit_dealing(
|
||||
&self,
|
||||
dealing_bytes: ContractSafeBytes,
|
||||
dealing: PartialContractDealing,
|
||||
resharing: bool,
|
||||
) -> Result<ExecuteResult, CoconutError> {
|
||||
Ok(nyxd_signing!(
|
||||
self,
|
||||
submit_dealing_bytes(dealing_bytes, resharing, None).await?
|
||||
submit_dealing_bytes(dealing, resharing, None).await?
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
Generated
+2
@@ -3814,6 +3814,8 @@ dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
"cw2",
|
||||
"cw4",
|
||||
"nym-contracts-common",
|
||||
"nym-multisig-contract-common",
|
||||
]
|
||||
|
||||
Generated
+2
@@ -3220,6 +3220,8 @@ dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
"cw2",
|
||||
"cw4",
|
||||
"nym-contracts-common",
|
||||
"nym-multisig-contract-common",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user