Feature/dkg integration tests (#1815)
* DKG contract e2e test * Refactor to the same format as other contracts * Vk share tests * State tests * Dealings tests * Dealer tests * Api dkg tests * Fix path to contract after refactor * Fix test target clippy
This commit is contained in:
committed by
durch
parent
9a8218905e
commit
5c841de6ae
@@ -15,12 +15,12 @@ pub use coconut_dkg_common::event_attributes::*;
|
||||
pub struct Log {
|
||||
#[serde(default)]
|
||||
// weird thing is that the first msg_index seems to always be undefined on the raw logs
|
||||
msg_index: usize,
|
||||
pub msg_index: usize,
|
||||
// unless I'm missing something obvious, the "log" type in cosmjs is always an empty string
|
||||
// and launchpad cosmos validator was setting it to what essentially is just the raw version of what
|
||||
// we received (and we don't care about launchpad, we, as the time of writing this, work on the stargate)
|
||||
// log: String,
|
||||
events: Vec<cosmwasm_std::Event>,
|
||||
pub events: Vec<cosmwasm_std::Event>,
|
||||
}
|
||||
|
||||
/// Searches in logs for the first event of the given event type and in that event
|
||||
|
||||
@@ -15,7 +15,7 @@ pub const MAX_DISPLAY_SIZE: usize = 128;
|
||||
// TODO: if we are to use this for different types, it might make sense to introduce something like
|
||||
// CommitmentTypeId field on the below for distinguishing different ones. it would somehow become part of the trait
|
||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, JsonSchema)]
|
||||
pub struct ContractSafeBytes(Vec<u8>);
|
||||
pub struct ContractSafeBytes(pub Vec<u8>);
|
||||
|
||||
impl Deref for ContractSafeBytes {
|
||||
type Target = Vec<u8>;
|
||||
|
||||
Generated
+3
@@ -197,8 +197,10 @@ dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cosmwasm-storage",
|
||||
"cw-controllers",
|
||||
"cw-multi-test",
|
||||
"cw-storage-plus",
|
||||
"cw4",
|
||||
"cw4-group",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
@@ -231,6 +233,7 @@ dependencies = [
|
||||
"cw-multi-test",
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"cw3",
|
||||
"cw3-flex-multisig",
|
||||
"cw4",
|
||||
"cw4-group",
|
||||
|
||||
@@ -25,7 +25,7 @@ impl<'a> IndexList<SpendCredential> for SpendCredentialIndex<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// gateways() is the storage access function.
|
||||
// spent_credentials() is the storage access function.
|
||||
pub(crate) fn spent_credentials<'a>(
|
||||
) -> IndexedMap<'a, &'a str, SpendCredential, SpendCredentialIndex<'a>> {
|
||||
let indexes = SpendCredentialIndex {
|
||||
|
||||
@@ -19,4 +19,8 @@ cw4 = { version = "0.13.4" }
|
||||
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = "1.0.23"
|
||||
thiserror = "1.0.23"
|
||||
|
||||
[dev-dependencies]
|
||||
cw-multi-test = { version = "0.13.4" }
|
||||
cw4-group = { path = "../multisig/cw4-group" }
|
||||
@@ -0,0 +1,264 @@
|
||||
// Copyright 2022 - 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::transactions::try_commit_dealings;
|
||||
use crate::epoch_state::queries::query_current_epoch_state;
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH_STATE;
|
||||
use crate::epoch_state::transactions::advance_epoch_state;
|
||||
use crate::error::ContractError;
|
||||
use crate::state::{State, 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;
|
||||
use coconut_dkg_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
|
||||
use coconut_dkg_common::types::EpochState;
|
||||
use cosmwasm_std::{
|
||||
entry_point, to_binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response,
|
||||
};
|
||||
use cw4::Cw4Contract;
|
||||
|
||||
/// Instantiate the contract.
|
||||
///
|
||||
/// `deps` contains Storage, API and Querier
|
||||
/// `env` contains block, message and contract info
|
||||
/// `msg` is the contract initialization message, sort of like a constructor call.
|
||||
#[entry_point]
|
||||
pub fn instantiate(
|
||||
mut deps: DepsMut<'_>,
|
||||
_env: Env,
|
||||
_info: MessageInfo,
|
||||
msg: InstantiateMsg,
|
||||
) -> Result<Response, ContractError> {
|
||||
let admin_addr = deps.api.addr_validate(&msg.admin)?;
|
||||
let multisig_addr = deps.api.addr_validate(&msg.multisig_addr)?;
|
||||
ADMIN.set(deps.branch(), Some(admin_addr))?;
|
||||
MULTISIG.set(deps.branch(), Some(multisig_addr.clone()))?;
|
||||
|
||||
let group_addr = Cw4Contract(deps.api.addr_validate(&msg.group_addr).map_err(|_| {
|
||||
ContractError::InvalidGroup {
|
||||
addr: msg.group_addr.clone(),
|
||||
}
|
||||
})?);
|
||||
|
||||
let state = State {
|
||||
group_addr,
|
||||
multisig_addr,
|
||||
mix_denom: msg.mix_denom,
|
||||
};
|
||||
STATE.save(deps.storage, &state)?;
|
||||
|
||||
CURRENT_EPOCH_STATE.save(deps.storage, &EpochState::default())?;
|
||||
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
/// Handle an incoming message
|
||||
#[entry_point]
|
||||
pub fn execute(
|
||||
deps: DepsMut<'_>,
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
msg: ExecuteMsg,
|
||||
) -> Result<Response, ContractError> {
|
||||
match msg {
|
||||
ExecuteMsg::RegisterDealer {
|
||||
bte_key_with_proof,
|
||||
announce_address,
|
||||
} => try_add_dealer(deps, info, bte_key_with_proof, announce_address),
|
||||
ExecuteMsg::CommitDealing { dealing_bytes } => {
|
||||
try_commit_dealings(deps, info, dealing_bytes)
|
||||
}
|
||||
ExecuteMsg::CommitVerificationKeyShare { share } => {
|
||||
try_commit_verification_key_share(deps, env, info, share)
|
||||
}
|
||||
ExecuteMsg::VerifyVerificationKeyShare { owner } => {
|
||||
try_verify_verification_key_share(deps, info, owner)
|
||||
}
|
||||
ExecuteMsg::AdvanceEpochState {} => advance_epoch_state(deps, info),
|
||||
}
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> Result<QueryResponse, ContractError> {
|
||||
let response = match msg {
|
||||
QueryMsg::GetCurrentEpochState {} => to_binary(&query_current_epoch_state(deps.storage)?)?,
|
||||
QueryMsg::GetDealerDetails { dealer_address } => {
|
||||
to_binary(&query_dealer_details(deps, dealer_address)?)?
|
||||
}
|
||||
QueryMsg::GetCurrentDealers { limit, start_after } => {
|
||||
to_binary(&query_current_dealers_paged(deps, start_after, limit)?)?
|
||||
}
|
||||
QueryMsg::GetPastDealers { limit, start_after } => {
|
||||
to_binary(&query_past_dealers_paged(deps, start_after, limit)?)?
|
||||
}
|
||||
QueryMsg::GetDealing {
|
||||
idx,
|
||||
limit,
|
||||
start_after,
|
||||
} => to_binary(&query_dealings_paged(deps, idx, start_after, limit)?)?,
|
||||
QueryMsg::GetVerificationKeys { limit, start_after } => {
|
||||
to_binary(&query_vk_shares_paged(deps, start_after, limit)?)?
|
||||
}
|
||||
};
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
Ok(Default::default())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::support::tests::fixtures::{dealer_details_fixture, TEST_MIX_DENOM};
|
||||
use crate::support::tests::helpers::{ADMIN_ADDRESS, MULTISIG_CONTRACT};
|
||||
use coconut_dkg_common::dealer::DealerDetails;
|
||||
use coconut_dkg_common::msg::ExecuteMsg::RegisterDealer;
|
||||
use coconut_dkg_common::types::NodeIndex;
|
||||
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
|
||||
use cosmwasm_std::{coins, Addr};
|
||||
use cw4::Member;
|
||||
use cw4_group::msg::InstantiateMsg as GroupInstantiateMsg;
|
||||
use cw_multi_test::{App, AppBuilder, AppResponse, ContractWrapper, Executor};
|
||||
|
||||
fn instantiate_with_group(app: &mut App, members: &[Addr]) -> Addr {
|
||||
let group_code_id = app.store_code(Box::new(ContractWrapper::new(
|
||||
cw4_group::contract::execute,
|
||||
cw4_group::contract::instantiate,
|
||||
cw4_group::contract::query,
|
||||
)));
|
||||
let msg = GroupInstantiateMsg {
|
||||
admin: Some(ADMIN_ADDRESS.to_string()),
|
||||
members: members
|
||||
.iter()
|
||||
.map(|member| Member {
|
||||
addr: member.to_string(),
|
||||
weight: 10,
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
let group_contract_addr = app
|
||||
.instantiate_contract(
|
||||
group_code_id,
|
||||
Addr::unchecked(ADMIN_ADDRESS),
|
||||
&msg,
|
||||
&[],
|
||||
"group",
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let coconut_dkg_code_id =
|
||||
app.store_code(Box::new(ContractWrapper::new(execute, instantiate, query)));
|
||||
let msg = InstantiateMsg {
|
||||
group_addr: group_contract_addr.to_string(),
|
||||
multisig_addr: MULTISIG_CONTRACT.to_string(),
|
||||
admin: Addr::unchecked(ADMIN_ADDRESS).to_string(),
|
||||
mix_denom: TEST_MIX_DENOM.to_string(),
|
||||
};
|
||||
app.instantiate_contract(
|
||||
coconut_dkg_code_id,
|
||||
Addr::unchecked(ADMIN_ADDRESS),
|
||||
&msg,
|
||||
&[],
|
||||
"coconut dkg",
|
||||
None,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn parse_node_index(res: AppResponse) -> NodeIndex {
|
||||
res.events
|
||||
.into_iter()
|
||||
.find(|e| &e.ty == "wasm")
|
||||
.unwrap()
|
||||
.attributes
|
||||
.into_iter()
|
||||
.find(|attr| &attr.key == "node_index")
|
||||
.unwrap()
|
||||
.value
|
||||
.parse::<u64>()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn initialize_contract() {
|
||||
let mut deps = mock_dependencies();
|
||||
let env = mock_env();
|
||||
let msg = InstantiateMsg {
|
||||
group_addr: "group_addr".to_string(),
|
||||
multisig_addr: "multisig_addr".to_string(),
|
||||
admin: "admin".to_string(),
|
||||
mix_denom: "nym".to_string(),
|
||||
};
|
||||
let info = mock_info("creator", &[]);
|
||||
|
||||
let res = instantiate(deps.as_mut(), env.clone(), info, msg);
|
||||
assert!(res.is_ok())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_add_dealer() {
|
||||
let init_funds = coins(100, TEST_MIX_DENOM);
|
||||
const MEMBER_SIZE: usize = 100;
|
||||
let members: [Addr; MEMBER_SIZE] =
|
||||
std::array::from_fn(|idx| Addr::unchecked(format!("member{}", idx)));
|
||||
|
||||
let mut app = AppBuilder::new().build(|router, _, storage| {
|
||||
router
|
||||
.bank
|
||||
.init_balance(storage, &Addr::unchecked(ADMIN_ADDRESS), init_funds)
|
||||
.unwrap();
|
||||
});
|
||||
let coconut_dkg_contract_addr = instantiate_with_group(&mut app, &members);
|
||||
|
||||
for (idx, member) in members.iter().enumerate() {
|
||||
let res = app
|
||||
.execute_contract(
|
||||
member.clone(),
|
||||
coconut_dkg_contract_addr.clone(),
|
||||
&RegisterDealer {
|
||||
bte_key_with_proof: "bte_key_with_proof".to_string(),
|
||||
announce_address: "127.0.0.1:8000".to_string(),
|
||||
},
|
||||
&vec![],
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(parse_node_index(res), (idx + 1) as u64);
|
||||
|
||||
let err = app
|
||||
.execute_contract(
|
||||
member.clone(),
|
||||
coconut_dkg_contract_addr.clone(),
|
||||
&RegisterDealer {
|
||||
bte_key_with_proof: "bte_key_with_proof".to_string(),
|
||||
announce_address: "127.0.0.1:8000".to_string(),
|
||||
},
|
||||
&vec![],
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(ContractError::AlreadyADealer, err.downcast().unwrap());
|
||||
}
|
||||
|
||||
let unauthorized_member = Addr::unchecked("not_a_member");
|
||||
let err = app
|
||||
.execute_contract(
|
||||
unauthorized_member,
|
||||
coconut_dkg_contract_addr.clone(),
|
||||
&RegisterDealer {
|
||||
bte_key_with_proof: "bte_key_with_proof".to_string(),
|
||||
announce_address: "127.0.0.1:8000".to_string(),
|
||||
},
|
||||
&vec![],
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(ContractError::Unauthorized, err.downcast().unwrap());
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ fn query_dealers(
|
||||
deps: Deps<'_>,
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
underlying_map: IndexedDealersMap<'_>,
|
||||
underlying_map: &IndexedDealersMap<'_>,
|
||||
) -> StdResult<PagedDealerResponse> {
|
||||
let limit = limit
|
||||
.unwrap_or(storage::DEALERS_PAGE_DEFAULT_LIMIT)
|
||||
@@ -55,7 +55,7 @@ pub fn query_current_dealers_paged(
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
) -> StdResult<PagedDealerResponse> {
|
||||
query_dealers(deps, start_after, limit, storage::current_dealers())
|
||||
query_dealers(deps, start_after, limit, &storage::current_dealers())
|
||||
}
|
||||
|
||||
pub fn query_past_dealers_paged(
|
||||
@@ -63,5 +63,166 @@ pub fn query_past_dealers_paged(
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
) -> StdResult<PagedDealerResponse> {
|
||||
query_dealers(deps, start_after, limit, storage::past_dealers())
|
||||
query_dealers(deps, start_after, limit, &storage::past_dealers())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::dealers::storage::{DEALERS_PAGE_DEFAULT_LIMIT, DEALERS_PAGE_MAX_LIMIT};
|
||||
use crate::support::tests::fixtures::dealer_details_fixture;
|
||||
use crate::support::tests::helpers::init_contract;
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
use cosmwasm_std::{Addr, DepsMut};
|
||||
|
||||
fn fill_dealers(deps: DepsMut<'_>, mapping: &IndexedDealersMap<'_>, size: usize) {
|
||||
for n in 0..size {
|
||||
let dealer_details = dealer_details_fixture(n as u64);
|
||||
mapping
|
||||
.save(deps.storage, &dealer_details.address, &dealer_details)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_dealers(deps: DepsMut<'_>, mapping: &IndexedDealersMap<'_>, size: usize) {
|
||||
for n in 0..size {
|
||||
let dealer_details = dealer_details_fixture(n as u64);
|
||||
mapping
|
||||
.remove(deps.storage, &dealer_details.address)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dealers_empty_on_init() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
|
||||
for mapping in [storage::current_dealers(), storage::past_dealers()] {
|
||||
let page1 = query_dealers(deps.as_ref(), None, None, &mapping).unwrap();
|
||||
assert_eq!(0, page1.dealers.len() as u32);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dealers_paged_retrieval_obeys_limits() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
let owner = Addr::unchecked("owner");
|
||||
let limit = 2;
|
||||
|
||||
for mapping in [storage::current_dealers(), storage::past_dealers()] {
|
||||
fill_dealers(deps.as_mut(), &mapping, 1000);
|
||||
|
||||
let page1 = query_dealers(deps.as_ref(), None, Option::from(limit), &mapping).unwrap();
|
||||
assert_eq!(limit, page1.dealers.len() as u32);
|
||||
|
||||
remove_dealers(deps.as_mut(), &mapping, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dealers_paged_retrieval_has_default_limit() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
|
||||
for mapping in [storage::current_dealers(), storage::past_dealers()] {
|
||||
fill_dealers(deps.as_mut(), &mapping, 1000);
|
||||
|
||||
// query without explicitly setting a limit
|
||||
let page1 = query_dealers(deps.as_ref(), None, None, &mapping).unwrap();
|
||||
|
||||
assert_eq!(DEALERS_PAGE_DEFAULT_LIMIT, page1.dealers.len() as u32);
|
||||
|
||||
remove_dealers(deps.as_mut(), &mapping, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dealers_paged_retrieval_has_max_limit() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
|
||||
// query with a crazily high limit in an attempt to use too many resources
|
||||
let crazy_limit = 1000 * DEALERS_PAGE_MAX_LIMIT;
|
||||
|
||||
for mapping in [storage::current_dealers(), storage::past_dealers()] {
|
||||
fill_dealers(deps.as_mut(), &mapping, 1000);
|
||||
|
||||
let page1 =
|
||||
query_dealers(deps.as_ref(), None, Option::from(crazy_limit), &mapping).unwrap();
|
||||
|
||||
// we default to a decent sized upper bound instead
|
||||
let expected_limit = DEALERS_PAGE_MAX_LIMIT;
|
||||
assert_eq!(expected_limit, page1.dealers.len() as u32);
|
||||
|
||||
remove_dealers(deps.as_mut(), &mapping, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dealers_pagination_works() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
|
||||
let per_page = 2;
|
||||
|
||||
for mapping in [storage::current_dealers(), storage::past_dealers()] {
|
||||
fill_dealers(deps.as_mut(), &mapping, 1);
|
||||
let page1 =
|
||||
query_dealers(deps.as_ref(), None, Option::from(per_page), &mapping).unwrap();
|
||||
|
||||
// page should have 1 result on it
|
||||
assert_eq!(1, page1.dealers.len());
|
||||
remove_dealers(deps.as_mut(), &mapping, 1);
|
||||
}
|
||||
|
||||
for mapping in [storage::current_dealers(), storage::past_dealers()] {
|
||||
fill_dealers(deps.as_mut(), &mapping, 2);
|
||||
// page1 should have 2 results on it
|
||||
let page1 =
|
||||
query_dealers(deps.as_ref(), None, Option::from(per_page), &mapping).unwrap();
|
||||
assert_eq!(2, page1.dealers.len());
|
||||
remove_dealers(deps.as_mut(), &mapping, 2);
|
||||
}
|
||||
|
||||
for mapping in [storage::current_dealers(), storage::past_dealers()] {
|
||||
fill_dealers(deps.as_mut(), &mapping, 3);
|
||||
// page1 still has 2 results
|
||||
let page1 =
|
||||
query_dealers(deps.as_ref(), None, Option::from(per_page), &mapping).unwrap();
|
||||
assert_eq!(2, page1.dealers.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_dealers(
|
||||
deps.as_ref(),
|
||||
Option::from(start_after.to_string()),
|
||||
Option::from(per_page),
|
||||
&mapping,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(1, page2.dealers.len());
|
||||
remove_dealers(deps.as_mut(), &mapping, 3);
|
||||
}
|
||||
|
||||
for mapping in [storage::current_dealers(), storage::past_dealers()] {
|
||||
fill_dealers(deps.as_mut(), &mapping, 4);
|
||||
let page1 =
|
||||
query_dealers(deps.as_ref(), None, Option::from(per_page), &mapping).unwrap();
|
||||
let start_after = page1.start_next_after.unwrap();
|
||||
let page2 = query_dealers(
|
||||
deps.as_ref(),
|
||||
Option::from(start_after.to_string()),
|
||||
Option::from(per_page),
|
||||
&mapping,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// now we have 2 pages, with 2 results on the second page
|
||||
assert_eq!(2, page2.dealers.len());
|
||||
remove_dealers(deps.as_mut(), &mapping, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
use crate::dealers::storage as dealers_storage;
|
||||
use crate::epoch_state::utils::check_epoch_state;
|
||||
use crate::{ContractError, State, STATE};
|
||||
use crate::error::ContractError;
|
||||
use crate::state::{State, STATE};
|
||||
use coconut_dkg_common::types::{DealerDetails, EncodedBTEPublicKeyWithProof, EpochState};
|
||||
use cosmwasm_std::{Addr, DepsMut, MessageInfo, Response};
|
||||
|
||||
@@ -64,3 +65,40 @@ pub fn try_add_dealer(
|
||||
|
||||
Ok(Response::new().add_attribute("node_index", node_index.to_string()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::epoch_state::transactions::advance_epoch_state;
|
||||
use crate::support::tests::fixtures::dealer_details_fixture;
|
||||
use crate::support::tests::helpers;
|
||||
use crate::support::tests::helpers::ADMIN_ADDRESS;
|
||||
use cosmwasm_std::testing::mock_info;
|
||||
|
||||
#[test]
|
||||
fn invalid_state() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let owner = Addr::unchecked("owner");
|
||||
let info = mock_info(owner.as_str(), &[]);
|
||||
let dealer_details = dealer_details_fixture(1);
|
||||
let bte_key_with_proof = String::from("bte_key_with_proof");
|
||||
let announce_address = String::from("localhost:8000");
|
||||
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
|
||||
let ret = try_add_dealer(
|
||||
deps.as_mut(),
|
||||
info.clone(),
|
||||
bte_key_with_proof.clone(),
|
||||
announce_address.clone(),
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
current_state: EpochState::DealingExchange.to_string(),
|
||||
expected_state: EpochState::default().to_string(),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,3 +43,159 @@ pub fn query_dealings_paged(
|
||||
start_next_after,
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::dealings::storage::{DEALINGS_PAGE_DEFAULT_LIMIT, DEALINGS_PAGE_MAX_LIMIT};
|
||||
use crate::support::tests::fixtures::dealing_bytes_fixture;
|
||||
use crate::support::tests::helpers::init_contract;
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
use cosmwasm_std::{Addr, DepsMut};
|
||||
|
||||
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));
|
||||
for idx in 0..TOTAL_DEALINGS {
|
||||
DEALINGS_BYTES[idx]
|
||||
.save(deps.storage, &sender, &dealing_share)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_on_bad_idx() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
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 env = mock_env();
|
||||
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();
|
||||
let env = mock_env();
|
||||
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();
|
||||
let env = mock_env();
|
||||
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() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
|
||||
fill_dealings(deps.as_mut(), 1);
|
||||
|
||||
let per_page = 2;
|
||||
|
||||
for idx in 0..TOTAL_DEALINGS as u64 {
|
||||
let page1 =
|
||||
query_dealings_paged(deps.as_ref(), idx, 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(), 2);
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
fill_dealings(deps.as_mut(), 3);
|
||||
|
||||
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());
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
fill_dealings(deps.as_mut(), 4);
|
||||
|
||||
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();
|
||||
|
||||
// now we have 2 pages, with 2 results on the second page
|
||||
assert_eq!(2, page2.dealings.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use crate::dealers::storage as dealers_storage;
|
||||
use crate::dealings::storage::DEALINGS_BYTES;
|
||||
use crate::epoch_state::utils::check_epoch_state;
|
||||
use crate::ContractError;
|
||||
use crate::error::ContractError;
|
||||
use coconut_dkg_common::types::{ContractSafeBytes, EpochState};
|
||||
use cosmwasm_std::{DepsMut, MessageInfo, Response};
|
||||
|
||||
@@ -35,3 +35,65 @@ pub fn try_commit_dealings(
|
||||
commitment: String::from("dealing"),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::epoch_state::transactions::advance_epoch_state;
|
||||
use crate::support::tests::fixtures::dealing_bytes_fixture;
|
||||
use crate::support::tests::helpers;
|
||||
use crate::support::tests::helpers::ADMIN_ADDRESS;
|
||||
use coconut_dkg_common::dealer::DealerDetails;
|
||||
use coconut_dkg_common::types::TOTAL_DEALINGS;
|
||||
use cosmwasm_std::testing::mock_info;
|
||||
use cosmwasm_std::Addr;
|
||||
|
||||
#[test]
|
||||
fn invalid_commit_dealing() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let owner = Addr::unchecked("owner");
|
||||
let info = mock_info(owner.as_str(), &[]);
|
||||
let dealing_bytes = dealing_bytes_fixture();
|
||||
|
||||
let ret =
|
||||
try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone()).unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
current_state: EpochState::default().to_string(),
|
||||
expected_state: EpochState::DealingExchange.to_string()
|
||||
}
|
||||
);
|
||||
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
|
||||
let ret =
|
||||
try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone()).unwrap_err();
|
||||
assert_eq!(ret, ContractError::NotADealer);
|
||||
|
||||
let dealer_details = DealerDetails {
|
||||
address: owner.clone(),
|
||||
bte_public_key_with_proof: String::new(),
|
||||
announce_address: String::new(),
|
||||
assigned_index: 1,
|
||||
};
|
||||
dealers_storage::current_dealers()
|
||||
.save(deps.as_mut().storage, &owner, &dealer_details)
|
||||
.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());
|
||||
assert!(ret.is_ok());
|
||||
assert!(dealings.has(deps.as_mut().storage, &owner));
|
||||
}
|
||||
let ret =
|
||||
try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone()).unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::AlreadyCommitted {
|
||||
commitment: String::from("dealing"),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,4 +3,5 @@
|
||||
|
||||
pub mod queries;
|
||||
pub mod storage;
|
||||
pub mod transactions;
|
||||
pub mod utils;
|
||||
|
||||
@@ -1,13 +1,28 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::storage;
|
||||
use crate::ContractError;
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH_STATE;
|
||||
use crate::error::ContractError;
|
||||
use coconut_dkg_common::types::EpochState;
|
||||
use cosmwasm_std::Storage;
|
||||
|
||||
pub(crate) fn query_current_epoch_state(
|
||||
storage: &dyn Storage,
|
||||
) -> Result<EpochState, ContractError> {
|
||||
storage::current_epoch_state(storage)
|
||||
CURRENT_EPOCH_STATE
|
||||
.load(storage)
|
||||
.map_err(|_| ContractError::EpochNotInitialised)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod test {
|
||||
use super::*;
|
||||
use crate::support::tests::helpers::init_contract;
|
||||
|
||||
#[test]
|
||||
fn query_state() {
|
||||
let mut deps = init_contract();
|
||||
let state = query_current_epoch_state(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(state, EpochState::PublicKeySubmission);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,7 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{ContractError, ADMIN};
|
||||
use coconut_dkg_common::types::EpochState;
|
||||
use cosmwasm_std::{DepsMut, MessageInfo, Response, Storage};
|
||||
use cw_storage_plus::Item;
|
||||
|
||||
pub(crate) const CURRENT_EPOCH_STATE: Item<'_, EpochState> = Item::new("current_epoch_state");
|
||||
|
||||
pub(crate) fn current_epoch_state(storage: &dyn Storage) -> Result<EpochState, ContractError> {
|
||||
CURRENT_EPOCH_STATE
|
||||
.load(storage)
|
||||
.map_err(|_| ContractError::EpochNotInitialised)
|
||||
}
|
||||
|
||||
pub(crate) fn advance_epoch_state(
|
||||
deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
) -> Result<Response, ContractError> {
|
||||
ADMIN.assert_admin(deps.as_ref(), &info.sender)?;
|
||||
CURRENT_EPOCH_STATE.update::<_, ContractError>(deps.storage, |mut epoch_state| {
|
||||
// TODO: When defaulting to the first state, some action will probably need to be taken on the
|
||||
// rest of the contract, as we're starting with a new set of signers
|
||||
epoch_state = epoch_state.next().unwrap_or_default();
|
||||
Ok(epoch_state)
|
||||
})?;
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH_STATE;
|
||||
use crate::error::ContractError;
|
||||
use crate::state::ADMIN;
|
||||
use cosmwasm_std::{DepsMut, MessageInfo, Response};
|
||||
|
||||
pub(crate) fn advance_epoch_state(
|
||||
deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
) -> Result<Response, ContractError> {
|
||||
ADMIN.assert_admin(deps.as_ref(), &info.sender)?;
|
||||
CURRENT_EPOCH_STATE.update::<_, ContractError>(deps.storage, |mut epoch_state| {
|
||||
// TODO: When defaulting to the first state, some action will probably need to be taken on the
|
||||
// rest of the contract, as we're starting with a new set of signers
|
||||
epoch_state = epoch_state.next().unwrap_or_default();
|
||||
Ok(epoch_state)
|
||||
})?;
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::support::tests::helpers::{init_contract, ADMIN_ADDRESS};
|
||||
use coconut_dkg_common::types::EpochState;
|
||||
use cosmwasm_std::testing::mock_info;
|
||||
use cw_controllers::AdminError;
|
||||
|
||||
#[test]
|
||||
fn advance_state() {
|
||||
let mut deps = init_contract();
|
||||
let info = mock_info("requester", &[]);
|
||||
let admin_info = mock_info(ADMIN_ADDRESS, &[]);
|
||||
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), info).unwrap_err(),
|
||||
ContractError::Admin(AdminError::NotAdmin {})
|
||||
);
|
||||
|
||||
advance_epoch_state(deps.as_mut(), admin_info.clone()).unwrap();
|
||||
assert_eq!(
|
||||
CURRENT_EPOCH_STATE.load(deps.as_mut().storage).unwrap(),
|
||||
EpochState::DealingExchange
|
||||
);
|
||||
|
||||
advance_epoch_state(deps.as_mut(), admin_info.clone()).unwrap();
|
||||
assert_eq!(
|
||||
CURRENT_EPOCH_STATE.load(deps.as_mut().storage).unwrap(),
|
||||
EpochState::VerificationKeySubmission
|
||||
);
|
||||
|
||||
advance_epoch_state(deps.as_mut(), admin_info.clone()).unwrap();
|
||||
assert_eq!(
|
||||
CURRENT_EPOCH_STATE.load(deps.as_mut().storage).unwrap(),
|
||||
EpochState::VerificationKeyValidation
|
||||
);
|
||||
|
||||
advance_epoch_state(deps.as_mut(), admin_info.clone()).unwrap();
|
||||
assert_eq!(
|
||||
CURRENT_EPOCH_STATE.load(deps.as_mut().storage).unwrap(),
|
||||
EpochState::VerificationKeyFinalization
|
||||
);
|
||||
|
||||
advance_epoch_state(deps.as_mut(), admin_info.clone()).unwrap();
|
||||
assert_eq!(
|
||||
CURRENT_EPOCH_STATE.load(deps.as_mut().storage).unwrap(),
|
||||
EpochState::InProgress
|
||||
);
|
||||
|
||||
advance_epoch_state(deps.as_mut(), admin_info.clone()).unwrap();
|
||||
assert_eq!(
|
||||
CURRENT_EPOCH_STATE.load(deps.as_mut().storage).unwrap(),
|
||||
EpochState::PublicKeySubmission
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{ContractError, CURRENT_EPOCH_STATE};
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH_STATE;
|
||||
use crate::error::ContractError;
|
||||
use coconut_dkg_common::types::EpochState;
|
||||
use cosmwasm_std::Storage;
|
||||
|
||||
@@ -19,3 +20,28 @@ pub(crate) fn check_epoch_state(
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod test {
|
||||
use super::*;
|
||||
use crate::support::tests::helpers::init_contract;
|
||||
|
||||
#[test]
|
||||
pub fn check_state() {
|
||||
let mut deps = init_contract();
|
||||
|
||||
for fixed_state in EpochState::default().all_until(EpochState::InProgress) {
|
||||
CURRENT_EPOCH_STATE
|
||||
.save(deps.as_mut().storage, &fixed_state)
|
||||
.unwrap();
|
||||
for against_state in EpochState::default().all_until(EpochState::InProgress) {
|
||||
let ret = check_epoch_state(deps.as_mut().storage, against_state);
|
||||
if fixed_state == against_state {
|
||||
assert!(ret.is_ok());
|
||||
} else {
|
||||
assert!(ret.is_err());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::{Addr, StdError, VerificationError};
|
||||
use cosmwasm_std::StdError;
|
||||
use cw_controllers::AdminError;
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -17,24 +17,6 @@ pub enum ContractError {
|
||||
#[error("Group contract invalid address '{addr}'")]
|
||||
InvalidGroup { addr: String },
|
||||
|
||||
#[error("No coin was sent for the deposit, you must send {denom}")]
|
||||
NoDepositFound { denom: String },
|
||||
|
||||
#[error("Received multiple coin types")]
|
||||
MultipleDenoms,
|
||||
|
||||
#[error("Wrong coin denomination, you must send {denom}")]
|
||||
WrongDenom { denom: String },
|
||||
|
||||
#[error("Not enough funds sent for deposit. (received {received}, minimum {minimum})")]
|
||||
InsufficientDeposit { received: u128, minimum: u128 },
|
||||
|
||||
#[error("Failed to perform ed25519 signature verification - {0}. This dealer will be temporarily blacklisted now.")]
|
||||
Ed25519VerificationError(#[from] VerificationError),
|
||||
|
||||
#[error("Provided ed25519 signature did not verify correctly. This dealer will be temporarily blacklisted now.")]
|
||||
InvalidEd25519Signature,
|
||||
|
||||
#[error("This potential dealer is not in the coconut signer group")]
|
||||
Unauthorized,
|
||||
|
||||
@@ -52,11 +34,6 @@ pub enum ContractError {
|
||||
expected_state: String,
|
||||
},
|
||||
|
||||
// we should never ever see this error (famous last words in programming), therefore, I'd want to
|
||||
// explicitly declare it so that when we ultimate do see it, it's gonna be more informative over "normal" panic
|
||||
#[error("Somehow our validated address {address} is not using correct bech32 encoding")]
|
||||
InvalidValidatedAddress { address: Addr },
|
||||
|
||||
#[error("This sender is not a dealer for the current epoch")]
|
||||
NotADealer,
|
||||
|
||||
|
||||
@@ -1,146 +1,12 @@
|
||||
// Copyright 2022 - 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::dealings::queries::query_dealings_paged;
|
||||
use crate::epoch_state::queries::query_current_epoch_state;
|
||||
use crate::error::ContractError;
|
||||
use crate::state::{State, ADMIN, MULTISIG, STATE};
|
||||
use crate::verification_key_shares::queries::query_vk_shares_paged;
|
||||
use coconut_dkg_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
|
||||
use coconut_dkg_common::types::EpochState;
|
||||
use cosmwasm_std::{
|
||||
entry_point, to_binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response,
|
||||
};
|
||||
use cw4::Cw4Contract;
|
||||
use epoch_state::storage::{advance_epoch_state, CURRENT_EPOCH_STATE};
|
||||
|
||||
mod constants;
|
||||
pub mod contract;
|
||||
mod dealers;
|
||||
mod dealings;
|
||||
mod epoch_state;
|
||||
mod error;
|
||||
pub mod error;
|
||||
mod state;
|
||||
mod support;
|
||||
mod verification_key_shares;
|
||||
|
||||
/// Instantiate the contract.
|
||||
///
|
||||
/// `deps` contains Storage, API and Querier
|
||||
/// `env` contains block, message and contract info
|
||||
/// `msg` is the contract initialization message, sort of like a constructor call.
|
||||
#[entry_point]
|
||||
pub fn instantiate(
|
||||
mut deps: DepsMut<'_>,
|
||||
_env: Env,
|
||||
_info: MessageInfo,
|
||||
msg: InstantiateMsg,
|
||||
) -> Result<Response, ContractError> {
|
||||
let admin_addr = deps.api.addr_validate(&msg.admin)?;
|
||||
let multisig_addr = deps.api.addr_validate(&msg.multisig_addr)?;
|
||||
ADMIN.set(deps.branch(), Some(admin_addr))?;
|
||||
MULTISIG.set(deps.branch(), Some(multisig_addr.clone()))?;
|
||||
|
||||
let group_addr = Cw4Contract(deps.api.addr_validate(&msg.group_addr).map_err(|_| {
|
||||
ContractError::InvalidGroup {
|
||||
addr: msg.group_addr.clone(),
|
||||
}
|
||||
})?);
|
||||
|
||||
let state = State {
|
||||
group_addr,
|
||||
multisig_addr,
|
||||
mix_denom: msg.mix_denom,
|
||||
};
|
||||
STATE.save(deps.storage, &state)?;
|
||||
|
||||
CURRENT_EPOCH_STATE.save(deps.storage, &EpochState::default())?;
|
||||
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
/// Handle an incoming message
|
||||
#[entry_point]
|
||||
pub fn execute(
|
||||
deps: DepsMut<'_>,
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
msg: ExecuteMsg,
|
||||
) -> Result<Response, ContractError> {
|
||||
match msg {
|
||||
ExecuteMsg::RegisterDealer {
|
||||
bte_key_with_proof,
|
||||
announce_address,
|
||||
} => {
|
||||
dealers::transactions::try_add_dealer(deps, info, bte_key_with_proof, announce_address)
|
||||
}
|
||||
ExecuteMsg::CommitDealing { dealing_bytes } => {
|
||||
dealings::transactions::try_commit_dealings(deps, info, dealing_bytes)
|
||||
}
|
||||
ExecuteMsg::CommitVerificationKeyShare { share } => {
|
||||
verification_key_shares::transactions::try_commit_verification_key_share(
|
||||
deps, env, info, share,
|
||||
)
|
||||
}
|
||||
ExecuteMsg::VerifyVerificationKeyShare { owner } => {
|
||||
verification_key_shares::transactions::try_verify_verification_key_share(
|
||||
deps, info, owner,
|
||||
)
|
||||
}
|
||||
ExecuteMsg::AdvanceEpochState {} => advance_epoch_state(deps, info),
|
||||
}
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> Result<QueryResponse, ContractError> {
|
||||
let response = match msg {
|
||||
QueryMsg::GetCurrentEpochState {} => to_binary(&query_current_epoch_state(deps.storage)?)?,
|
||||
QueryMsg::GetDealerDetails { dealer_address } => {
|
||||
to_binary(&query_dealer_details(deps, dealer_address)?)?
|
||||
}
|
||||
QueryMsg::GetCurrentDealers { limit, start_after } => {
|
||||
to_binary(&query_current_dealers_paged(deps, start_after, limit)?)?
|
||||
}
|
||||
QueryMsg::GetPastDealers { limit, start_after } => {
|
||||
to_binary(&query_past_dealers_paged(deps, start_after, limit)?)?
|
||||
}
|
||||
QueryMsg::GetDealing {
|
||||
idx,
|
||||
limit,
|
||||
start_after,
|
||||
} => to_binary(&query_dealings_paged(deps, idx, start_after, limit)?)?,
|
||||
QueryMsg::GetVerificationKeys { limit, start_after } => {
|
||||
to_binary(&query_vk_shares_paged(deps, start_after, limit)?)?
|
||||
}
|
||||
};
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
Ok(Default::default())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
|
||||
|
||||
#[test]
|
||||
fn initialize_contract() {
|
||||
let mut deps = mock_dependencies();
|
||||
let env = mock_env();
|
||||
let msg = InstantiateMsg {
|
||||
group_addr: "group_addr".to_string(),
|
||||
multisig_addr: "multisig_addr".to_string(),
|
||||
admin: "admin".to_string(),
|
||||
mix_denom: "nym".to_string(),
|
||||
};
|
||||
let info = mock_info("creator", &[]);
|
||||
|
||||
let res = instantiate(deps.as_mut(), env.clone(), info, msg);
|
||||
assert!(res.is_ok())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests;
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use coconut_dkg_common::dealer::{ContractDealing, DealerDetails};
|
||||
use coconut_dkg_common::types::ContractSafeBytes;
|
||||
use coconut_dkg_common::verification_key::ContractVKShare;
|
||||
use cosmwasm_std::Addr;
|
||||
|
||||
pub const TEST_MIX_DENOM: &str = "unym";
|
||||
|
||||
pub fn vk_share_fixture(index: u64) -> ContractVKShare {
|
||||
ContractVKShare {
|
||||
share: format!("share{}", index),
|
||||
announce_address: format!("localhost:{}", index),
|
||||
node_index: index,
|
||||
owner: Addr::unchecked(format!("owner{}", index)),
|
||||
verified: index % 2 == 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dealing_bytes_fixture() -> ContractSafeBytes {
|
||||
ContractSafeBytes(vec![])
|
||||
}
|
||||
|
||||
pub fn dealer_details_fixture(assigned_index: u64) -> DealerDetails {
|
||||
DealerDetails {
|
||||
address: Addr::unchecked(format!("owner{}", assigned_index)),
|
||||
bte_public_key_with_proof: "".to_string(),
|
||||
announce_address: "".to_string(),
|
||||
assigned_index,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::contract::instantiate;
|
||||
use coconut_dkg_common::msg::InstantiateMsg;
|
||||
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier};
|
||||
use cosmwasm_std::{Empty, MemoryStorage, OwnedDeps};
|
||||
|
||||
use super::fixtures::TEST_MIX_DENOM;
|
||||
|
||||
pub const ADMIN_ADDRESS: &str = "admin address";
|
||||
pub const GROUP_CONTRACT: &str = "group contract address";
|
||||
pub const MULTISIG_CONTRACT: &str = "multisig contract address";
|
||||
|
||||
pub fn init_contract() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>> {
|
||||
let mut deps = mock_dependencies();
|
||||
let msg = InstantiateMsg {
|
||||
group_addr: String::from(GROUP_CONTRACT),
|
||||
multisig_addr: String::from(MULTISIG_CONTRACT),
|
||||
admin: String::from(ADMIN_ADDRESS),
|
||||
mix_denom: TEST_MIX_DENOM.to_string(),
|
||||
};
|
||||
let env = mock_env();
|
||||
let info = mock_info(ADMIN_ADDRESS, &[]);
|
||||
instantiate(deps.as_mut(), env.clone(), info, msg).unwrap();
|
||||
deps
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod fixtures;
|
||||
pub mod helpers;
|
||||
@@ -36,3 +36,147 @@ pub fn query_vk_shares_paged(
|
||||
start_next_after,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::support::tests::fixtures::vk_share_fixture;
|
||||
use crate::support::tests::helpers::init_contract;
|
||||
use crate::verification_key_shares::storage::{
|
||||
VERIFICATION_KEY_SHARES_PAGE_DEFAULT_LIMIT, VERIFICATION_KEY_SHARES_PAGE_MAX_LIMIT,
|
||||
};
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
use cosmwasm_std::Addr;
|
||||
|
||||
#[test]
|
||||
fn vk_shares_empty_on_init() {
|
||||
let deps = init_contract();
|
||||
let response = query_vk_shares_paged(deps.as_ref(), None, Option::from(2)).unwrap();
|
||||
assert_eq!(0, response.shares.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vk_shares_paged_retrieval_obeys_limits() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
let limit = 2;
|
||||
for n in 0..1000 {
|
||||
let vk_share = vk_share_fixture(n);
|
||||
let sender = Addr::unchecked(format!("owner{}", n));
|
||||
VK_SHARES
|
||||
.save(&mut deps.storage, &sender, &vk_share)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let page1 = query_vk_shares_paged(deps.as_ref(), None, Option::from(limit)).unwrap();
|
||||
assert_eq!(limit, page1.shares.len() as u32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vk_shares_paged_retrieval_has_default_limit() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
for n in 0..1000 {
|
||||
let vk_share = vk_share_fixture(n);
|
||||
let sender = Addr::unchecked(format!("owner{}", n));
|
||||
VK_SHARES
|
||||
.save(&mut deps.storage, &sender, &vk_share)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// query without explicitly setting a limit
|
||||
let page1 = query_vk_shares_paged(deps.as_ref(), None, None).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
VERIFICATION_KEY_SHARES_PAGE_DEFAULT_LIMIT,
|
||||
page1.shares.len() as u32
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vk_shares_paged_retrieval_has_max_limit() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
for n in 0..1000 {
|
||||
let vk_share = vk_share_fixture(n);
|
||||
let sender = Addr::unchecked(format!("owner{}", n));
|
||||
VK_SHARES
|
||||
.save(&mut deps.storage, &sender, &vk_share)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// query with a crazily high limit in an attempt to use too many resources
|
||||
let crazy_limit = 1000 * VERIFICATION_KEY_SHARES_PAGE_MAX_LIMIT;
|
||||
let page1 = query_vk_shares_paged(deps.as_ref(), None, Option::from(crazy_limit)).unwrap();
|
||||
|
||||
// we default to a decent sized upper bound instead
|
||||
let expected_limit = VERIFICATION_KEY_SHARES_PAGE_MAX_LIMIT;
|
||||
assert_eq!(expected_limit, page1.shares.len() as u32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vk_shares_pagination_works() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
|
||||
let vk_share = vk_share_fixture(1);
|
||||
let sender = Addr::unchecked(format!("owner{}", 1));
|
||||
VK_SHARES
|
||||
.save(&mut deps.storage, &sender, &vk_share)
|
||||
.unwrap();
|
||||
|
||||
let per_page = 2;
|
||||
let page1 = query_vk_shares_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
|
||||
|
||||
// page should have 1 result on it
|
||||
assert_eq!(1, page1.shares.len());
|
||||
|
||||
// save another
|
||||
let vk_share = vk_share_fixture(2);
|
||||
let sender = Addr::unchecked(format!("owner{}", 2));
|
||||
VK_SHARES
|
||||
.save(&mut deps.storage, &sender, &vk_share)
|
||||
.unwrap();
|
||||
|
||||
// page1 should have 2 results on it
|
||||
let page1 = query_vk_shares_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
|
||||
assert_eq!(2, page1.shares.len());
|
||||
|
||||
let vk_share = vk_share_fixture(3);
|
||||
let sender = Addr::unchecked(format!("owner{}", 3));
|
||||
VK_SHARES
|
||||
.save(&mut deps.storage, &sender, &vk_share)
|
||||
.unwrap();
|
||||
|
||||
// page1 still has 2 results
|
||||
let page1 = query_vk_shares_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
|
||||
assert_eq!(2, page1.shares.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_vk_shares_paged(
|
||||
deps.as_ref(),
|
||||
Option::from(start_after.to_string()),
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(1, page2.shares.len());
|
||||
|
||||
let vk_share = vk_share_fixture(4);
|
||||
let sender = Addr::unchecked(format!("owner{}", 4));
|
||||
VK_SHARES
|
||||
.save(&mut deps.storage, &sender, &vk_share)
|
||||
.unwrap();
|
||||
|
||||
let page2 = query_vk_shares_paged(
|
||||
deps.as_ref(),
|
||||
Option::from(start_after.to_string()),
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// now we have 2 pages, with 2 results on the second page
|
||||
assert_eq!(2, page2.shares.len());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,3 +69,144 @@ pub fn try_verify_verification_key_share(
|
||||
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH_STATE;
|
||||
use crate::epoch_state::transactions::advance_epoch_state;
|
||||
use crate::support::tests::helpers;
|
||||
use crate::support::tests::helpers::{ADMIN_ADDRESS, MULTISIG_CONTRACT};
|
||||
use coconut_dkg_common::dealer::DealerDetails;
|
||||
use coconut_dkg_common::types::EpochState;
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cosmwasm_std::Storage;
|
||||
use cw_controllers::AdminError;
|
||||
|
||||
#[test]
|
||||
fn commit_vk_share() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let info = mock_info("requester", &[]);
|
||||
let share = "share".to_string();
|
||||
|
||||
let ret = try_commit_verification_key_share(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
share.clone(),
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
current_state: EpochState::default().to_string(),
|
||||
expected_state: EpochState::VerificationKeySubmission.to_string()
|
||||
}
|
||||
);
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
let ret = try_commit_verification_key_share(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
share.clone(),
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(ret, ContractError::NotADealer);
|
||||
|
||||
let dealer = Addr::unchecked("requester");
|
||||
let dealer_details = DealerDetails {
|
||||
address: dealer.clone(),
|
||||
bte_public_key_with_proof: String::new(),
|
||||
announce_address: String::new(),
|
||||
assigned_index: 1,
|
||||
};
|
||||
dealers_storage::current_dealers()
|
||||
.save(deps.as_mut().storage, &dealer, &dealer_details)
|
||||
.unwrap();
|
||||
|
||||
try_commit_verification_key_share(deps.as_mut(), env.clone(), info.clone(), share.clone())
|
||||
.unwrap();
|
||||
|
||||
let ret = try_commit_verification_key_share(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
share.clone(),
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::AlreadyCommitted {
|
||||
commitment: String::from("verification key share")
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_verify_vk_share() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let info = mock_info("requester", &[]);
|
||||
let owner = Addr::unchecked("owner");
|
||||
let multisig_info = mock_info(MULTISIG_CONTRACT, &[]);
|
||||
|
||||
let ret = try_verify_verification_key_share(deps.as_mut(), info.clone(), owner.clone())
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
current_state: EpochState::default().to_string(),
|
||||
expected_state: EpochState::VerificationKeyFinalization.to_string()
|
||||
}
|
||||
);
|
||||
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
|
||||
let ret =
|
||||
try_verify_verification_key_share(deps.as_mut(), info, owner.clone()).unwrap_err();
|
||||
assert_eq!(ret, ContractError::Admin(AdminError::NotAdmin {}));
|
||||
|
||||
let ret = try_verify_verification_key_share(deps.as_mut(), multisig_info, owner.clone())
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::NoCommitForOwner {
|
||||
owner: owner.to_string()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_vk_share() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let owner = Addr::unchecked("owner");
|
||||
let info = mock_info(owner.as_ref(), &[]);
|
||||
let share = "share".to_string();
|
||||
let multisig_info = mock_info(MULTISIG_CONTRACT, &[]);
|
||||
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
|
||||
let dealer_details = DealerDetails {
|
||||
address: owner.clone(),
|
||||
bte_public_key_with_proof: String::new(),
|
||||
announce_address: String::new(),
|
||||
assigned_index: 1,
|
||||
};
|
||||
dealers_storage::current_dealers()
|
||||
.save(deps.as_mut().storage, &owner, &dealer_details)
|
||||
.unwrap();
|
||||
try_commit_verification_key_share(deps.as_mut(), env.clone(), info.clone(), share.clone())
|
||||
.unwrap();
|
||||
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
|
||||
try_verify_verification_key_share(deps.as_mut(), multisig_info, owner.clone()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ multisig-contract-common = { path = "../../common/cosmwasm-smart-contracts/multi
|
||||
|
||||
cosmwasm-std = "1.0.0"
|
||||
cosmwasm-storage = "1.0.0"
|
||||
cw3 = "0.13.4"
|
||||
cw4 = "0.13.4"
|
||||
cw-storage-plus = "0.13.4"
|
||||
cw-controllers = "0.13.4"
|
||||
|
||||
@@ -42,9 +42,9 @@ pub fn mock_app(init_funds: &[Coin]) -> App {
|
||||
}
|
||||
pub fn contract_dkg() -> Box<dyn Contract<Empty>> {
|
||||
let contract = ContractWrapper::new(
|
||||
coconut_dkg::execute,
|
||||
coconut_dkg::instantiate,
|
||||
coconut_dkg::query,
|
||||
coconut_dkg::contract::execute,
|
||||
coconut_dkg::contract::instantiate,
|
||||
coconut_dkg::contract::query,
|
||||
);
|
||||
Box::new(contract)
|
||||
}
|
||||
|
||||
@@ -11,15 +11,18 @@ use coconut_dkg_common::msg::ExecuteMsg::{
|
||||
AdvanceEpochState, CommitVerificationKeyShare, RegisterDealer,
|
||||
};
|
||||
use coconut_dkg_common::msg::InstantiateMsg as DkgInstantiateMsg;
|
||||
use coconut_dkg_common::msg::QueryMsg::GetVerificationKeys;
|
||||
use coconut_dkg_common::verification_key::PagedVKSharesResponse;
|
||||
use cosmwasm_std::{coins, Addr, Decimal};
|
||||
use cw4::Member;
|
||||
use cw4_group::msg::InstantiateMsg as GroupInstantiateMsg;
|
||||
use cw_multi_test::Executor;
|
||||
use cw_utils::{Duration, Threshold};
|
||||
use multisig_contract_common::msg::ExecuteMsg::{Execute, Vote};
|
||||
use multisig_contract_common::msg::InstantiateMsg as MultisigInstantiateMsg;
|
||||
|
||||
#[test]
|
||||
fn dkg_create_proposal() {
|
||||
fn dkg_proposal() {
|
||||
let init_funds = coins(10000000000, TEST_COIN_DENOM);
|
||||
let mut app = mock_app(&init_funds);
|
||||
let member1 = Member {
|
||||
@@ -47,7 +50,7 @@ fn dkg_create_proposal() {
|
||||
let msg = MultisigInstantiateMsg {
|
||||
group_addr: group_contract_addr.to_string(),
|
||||
threshold: Threshold::AbsolutePercentage {
|
||||
percentage: Decimal::from_ratio(2u128, 3u128),
|
||||
percentage: Decimal::from_ratio(1u128, 1u128),
|
||||
},
|
||||
max_voting_period: Duration::Time(1000),
|
||||
coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(),
|
||||
@@ -88,7 +91,7 @@ fn dkg_create_proposal() {
|
||||
};
|
||||
app.migrate_contract(
|
||||
Addr::unchecked(OWNER),
|
||||
multisig_contract_addr,
|
||||
multisig_contract_addr.clone(),
|
||||
&msg,
|
||||
multisig_code_id,
|
||||
)
|
||||
@@ -115,6 +118,9 @@ fn dkg_create_proposal() {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// Proposal needs to be later then the member became part of the group
|
||||
app.update_block(|block| block.height += 1);
|
||||
|
||||
let msg = CommitVerificationKeyShare {
|
||||
share: "share".to_string(),
|
||||
};
|
||||
@@ -126,6 +132,7 @@ fn dkg_create_proposal() {
|
||||
&vec![],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let proposal_id = res
|
||||
.events
|
||||
.into_iter()
|
||||
@@ -138,5 +145,62 @@ fn dkg_create_proposal() {
|
||||
.value
|
||||
.parse::<u64>()
|
||||
.unwrap();
|
||||
assert_eq!(1, proposal_id);
|
||||
|
||||
let mut res: PagedVKSharesResponse = app
|
||||
.wrap()
|
||||
.query_wasm_smart(
|
||||
coconut_dkg_contract_addr.clone(),
|
||||
&GetVerificationKeys {
|
||||
limit: None,
|
||||
start_after: None,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let share = res.shares.pop().unwrap();
|
||||
assert_eq!(share.share, "share".to_string());
|
||||
assert_eq!(share.announce_address, "127.0.0.1:8000".to_string());
|
||||
assert_eq!(share.node_index, 1);
|
||||
assert_eq!(share.owner, Addr::unchecked(MEMBER1));
|
||||
assert!(!share.verified);
|
||||
|
||||
app.execute_contract(
|
||||
Addr::unchecked(MEMBER1),
|
||||
multisig_contract_addr.clone(),
|
||||
&Vote {
|
||||
proposal_id,
|
||||
vote: cw3::Vote::Yes,
|
||||
},
|
||||
&vec![],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
for _ in 0..2 {
|
||||
app.execute_contract(
|
||||
Addr::unchecked(OWNER),
|
||||
coconut_dkg_contract_addr.clone(),
|
||||
&AdvanceEpochState {},
|
||||
&vec![],
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
app.execute_contract(
|
||||
Addr::unchecked(MEMBER1),
|
||||
multisig_contract_addr.clone(),
|
||||
&Execute { proposal_id },
|
||||
&vec![],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let res: PagedVKSharesResponse = app
|
||||
.wrap()
|
||||
.query_wasm_smart(
|
||||
coconut_dkg_contract_addr,
|
||||
&GetVerificationKeys {
|
||||
limit: None,
|
||||
start_after: None,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
assert!(res.shares[0].verified);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use dkg::error::DkgError;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub(crate) enum ComplaintReason {
|
||||
MalformedBTEPublicKey,
|
||||
MissingDealing,
|
||||
MalformedDealing(DkgError),
|
||||
DealingVerificationError(DkgError),
|
||||
MalformedDealing,
|
||||
DealingVerificationError,
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ impl<R: RngCore + Clone> DkgController<R> {
|
||||
})
|
||||
}
|
||||
|
||||
async fn handle_epoch_state(&mut self) {
|
||||
pub(crate) async fn handle_epoch_state(&mut self) {
|
||||
match self.dkg_client.get_current_epoch_state().await {
|
||||
Err(e) => warn!("Could not get current epoch state {}", e),
|
||||
Ok(epoch_state) => {
|
||||
|
||||
@@ -50,3 +50,101 @@ pub(crate) async fn dealing_exchange(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::coconut::tests::DummyClient;
|
||||
use crate::coconut::KeyPair;
|
||||
use coconut_dkg_common::dealer::DealerDetails;
|
||||
use cosmwasm_std::Addr;
|
||||
use dkg::bte::keys::KeyPair as DkgKeyPair;
|
||||
use dkg::bte::Params;
|
||||
use rand::rngs::OsRng;
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use url::Url;
|
||||
use validator_client::nymd::AccountId;
|
||||
|
||||
const TEST_VALIDATORS_ADDRESS: [&str; 3] = [
|
||||
"n1aq9kakfgwqcufr23lsv644apavcntrsqsk4yus",
|
||||
"n1s9l3xr4g0rglvk4yctktmck3h4eq0gp6z2e20v",
|
||||
"n19kl4py32vsk297dm93ezem992cdyzdy4zuc2x6",
|
||||
];
|
||||
|
||||
fn insert_dealers(
|
||||
params: &Params,
|
||||
dealer_details_db: &Arc<RwLock<HashMap<String, DealerDetails>>>,
|
||||
) -> Vec<DkgKeyPair> {
|
||||
let mut keypairs = vec![];
|
||||
for (idx, addr) in TEST_VALIDATORS_ADDRESS.iter().enumerate() {
|
||||
let keypair = DkgKeyPair::new(params, OsRng);
|
||||
let bte_public_key_with_proof =
|
||||
bs58::encode(&keypair.public_key().to_bytes()).into_string();
|
||||
keypairs.push(keypair);
|
||||
dealer_details_db.write().unwrap().insert(
|
||||
addr.to_string(),
|
||||
DealerDetails {
|
||||
address: Addr::unchecked(*addr),
|
||||
bte_public_key_with_proof,
|
||||
announce_address: format!("localhost:80{}", idx),
|
||||
assigned_index: (idx + 1) as u64,
|
||||
},
|
||||
);
|
||||
}
|
||||
keypairs
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn exchange_dealing() {
|
||||
let self_index = 2;
|
||||
let dealer_details_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let dealings_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let dkg_client = DkgClient::new(
|
||||
DummyClient::new(AccountId::from_str(TEST_VALIDATORS_ADDRESS[0]).unwrap())
|
||||
.with_dealer_details(&dealer_details_db)
|
||||
.with_dealings(&dealings_db),
|
||||
);
|
||||
let params = setup();
|
||||
let mut state = State::new(
|
||||
Url::parse("localhost:8000").unwrap(),
|
||||
DkgKeyPair::new(¶ms, OsRng),
|
||||
KeyPair::new(),
|
||||
);
|
||||
state.set_node_index(Some(self_index));
|
||||
let keypairs = insert_dealers(¶ms, &dealer_details_db);
|
||||
|
||||
dealing_exchange(&dkg_client, &mut state, OsRng)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
state.current_dealers_by_idx().values().collect::<Vec<_>>(),
|
||||
keypairs
|
||||
.iter()
|
||||
.map(|k| k.public_key().public_key())
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
assert_eq!(state.threshold().unwrap(), 2);
|
||||
assert_eq!(state.receiver_index().unwrap(), 1);
|
||||
let dealings = dealings_db
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(TEST_VALIDATORS_ADDRESS[0])
|
||||
.unwrap()
|
||||
.clone();
|
||||
assert_eq!(dealings.len(), TOTAL_DEALINGS);
|
||||
|
||||
dealing_exchange(&dkg_client, &mut state, OsRng)
|
||||
.await
|
||||
.unwrap();
|
||||
let new_dealings = dealings_db
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(TEST_VALIDATORS_ADDRESS[0])
|
||||
.unwrap()
|
||||
.clone();
|
||||
assert_eq!(dealings, new_dealings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,59 @@ pub(crate) async fn public_key_submission(
|
||||
.register_dealer(bte_key, state.announce_address().to_string())
|
||||
.await?
|
||||
};
|
||||
state.set_node_index(index);
|
||||
state.set_node_index(Some(index));
|
||||
info!("DKG: Using node index {}", index);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::coconut::tests::DummyClient;
|
||||
use crate::coconut::KeyPair;
|
||||
use dkg::bte::keys::KeyPair as DkgKeyPair;
|
||||
use rand::rngs::OsRng;
|
||||
use std::str::FromStr;
|
||||
use url::Url;
|
||||
use validator_client::nymd::AccountId;
|
||||
|
||||
const TEST_VALIDATOR_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
|
||||
|
||||
#[tokio::test]
|
||||
async fn submit_public_key() {
|
||||
let dkg_client = DkgClient::new(DummyClient::new(
|
||||
AccountId::from_str(TEST_VALIDATOR_ADDRESS).unwrap(),
|
||||
));
|
||||
let mut state = State::new(
|
||||
Url::parse("localhost:8000").unwrap(),
|
||||
DkgKeyPair::new(&dkg::bte::setup(), OsRng),
|
||||
KeyPair::new(),
|
||||
);
|
||||
|
||||
assert!(dkg_client
|
||||
.get_self_registered_dealer_details()
|
||||
.await
|
||||
.unwrap()
|
||||
.details
|
||||
.is_none());
|
||||
public_key_submission(&dkg_client, &mut state)
|
||||
.await
|
||||
.unwrap();
|
||||
let client_idx = dkg_client
|
||||
.get_self_registered_dealer_details()
|
||||
.await
|
||||
.unwrap()
|
||||
.details
|
||||
.unwrap()
|
||||
.assigned_index;
|
||||
assert_eq!(state.node_index().unwrap(), client_idx);
|
||||
|
||||
// keeps the same index from chain, not calling register_dealer again
|
||||
state.set_node_index(None);
|
||||
public_key_submission(&dkg_client, &mut state)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(state.node_index().unwrap(), client_idx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,8 +214,8 @@ impl State {
|
||||
self.coconut_keypair.set(coconut_keypair).await
|
||||
}
|
||||
|
||||
pub fn set_node_index(&mut self, node_index: NodeIndex) {
|
||||
self.node_index = Some(node_index);
|
||||
pub fn set_node_index(&mut self, node_index: Option<NodeIndex>) {
|
||||
self.node_index = node_index;
|
||||
}
|
||||
|
||||
pub fn set_dealers(&mut self, dealers: Vec<DealerDetails>) {
|
||||
@@ -255,4 +255,9 @@ impl State {
|
||||
pub fn set_executed_proposal(&mut self) {
|
||||
self.executed_proposal = true;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn all_dealers(&self) -> &BTreeMap<Addr, Result<DkgParticipant, ComplaintReason>> {
|
||||
&self.dealers
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,12 +37,13 @@ async fn deterministic_filter_dealers(
|
||||
BTreeMap::from_iter(dealings.into_iter().filter_map(|contract_dealing| {
|
||||
match Dealing::try_from(&contract_dealing.dealing) {
|
||||
Ok(dealing) => {
|
||||
if let Err(err) =
|
||||
dealing.verify(¶ms, threshold, &initial_receivers, None)
|
||||
if dealing
|
||||
.verify(¶ms, threshold, &initial_receivers, None)
|
||||
.is_err()
|
||||
{
|
||||
state.mark_bad_dealer(
|
||||
&contract_dealing.dealer,
|
||||
ComplaintReason::DealingVerificationError(err),
|
||||
ComplaintReason::DealingVerificationError,
|
||||
);
|
||||
None
|
||||
} else if let Some(idx) =
|
||||
@@ -53,10 +54,10 @@ async fn deterministic_filter_dealers(
|
||||
None
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
Err(_) => {
|
||||
state.mark_bad_dealer(
|
||||
&contract_dealing.dealer,
|
||||
ComplaintReason::MalformedDealing(err),
|
||||
ComplaintReason::MalformedDealing,
|
||||
);
|
||||
None
|
||||
}
|
||||
@@ -242,3 +243,546 @@ pub(crate) async fn verification_key_finalization(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::coconut::dkg::dealing::dealing_exchange;
|
||||
use crate::coconut::dkg::public_key::public_key_submission;
|
||||
use crate::coconut::tests::DummyClient;
|
||||
use crate::coconut::KeyPair;
|
||||
use coconut_dkg_common::dealer::DealerDetails;
|
||||
use coconut_dkg_common::verification_key::ContractVKShare;
|
||||
use contracts_common::dealings::ContractSafeBytes;
|
||||
use dkg::bte::keys::KeyPair as DkgKeyPair;
|
||||
use rand::rngs::OsRng;
|
||||
use rand::Rng;
|
||||
use std::collections::HashMap;
|
||||
use std::env::temp_dir;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use url::Url;
|
||||
use validator_client::nymd::AccountId;
|
||||
|
||||
const TEST_VALIDATORS_ADDRESS: [&str; 3] = [
|
||||
"n1aq9kakfgwqcufr23lsv644apavcntrsqsk4yus",
|
||||
"n1s9l3xr4g0rglvk4yctktmck3h4eq0gp6z2e20v",
|
||||
"n19kl4py32vsk297dm93ezem992cdyzdy4zuc2x6",
|
||||
];
|
||||
|
||||
async fn prepare_clients_and_states(
|
||||
dealer_details_db: &Arc<RwLock<HashMap<String, DealerDetails>>>,
|
||||
dealings_db: &Arc<RwLock<HashMap<String, Vec<ContractSafeBytes>>>>,
|
||||
proposal_db: &Arc<RwLock<HashMap<u64, ProposalResponse>>>,
|
||||
verification_share_db: &Arc<RwLock<HashMap<String, ContractVKShare>>>,
|
||||
) -> Vec<(DkgClient, State)> {
|
||||
let params = setup();
|
||||
let mut clients_and_states = vec![];
|
||||
|
||||
for addr in TEST_VALIDATORS_ADDRESS {
|
||||
let dkg_client = DkgClient::new(
|
||||
DummyClient::new(AccountId::from_str(addr).unwrap())
|
||||
.with_dealer_details(dealer_details_db)
|
||||
.with_dealings(dealings_db)
|
||||
.with_proposal_db(proposal_db)
|
||||
.with_verification_share(verification_share_db),
|
||||
);
|
||||
let keypair = DkgKeyPair::new(¶ms, OsRng);
|
||||
let state = State::new(
|
||||
Url::parse("localhost:8000").unwrap(),
|
||||
keypair,
|
||||
KeyPair::new(),
|
||||
);
|
||||
clients_and_states.push((dkg_client, state));
|
||||
}
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
public_key_submission(dkg_client, state).await.unwrap();
|
||||
}
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
dealing_exchange(dkg_client, state, OsRng).await.unwrap();
|
||||
}
|
||||
clients_and_states
|
||||
}
|
||||
|
||||
async fn prepare_clients_and_states_with_submission(
|
||||
dealer_details_db: &Arc<RwLock<HashMap<String, DealerDetails>>>,
|
||||
dealings_db: &Arc<RwLock<HashMap<String, Vec<ContractSafeBytes>>>>,
|
||||
proposal_db: &Arc<RwLock<HashMap<u64, ProposalResponse>>>,
|
||||
verification_share_db: &Arc<RwLock<HashMap<String, ContractVKShare>>>,
|
||||
) -> Vec<(DkgClient, State)> {
|
||||
let mut clients_and_states = prepare_clients_and_states(
|
||||
dealer_details_db,
|
||||
dealings_db,
|
||||
proposal_db,
|
||||
verification_share_db,
|
||||
)
|
||||
.await;
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
let random_file: usize = OsRng.gen();
|
||||
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)
|
||||
.await
|
||||
.unwrap();
|
||||
std::fs::remove_file(private_key_path).unwrap();
|
||||
std::fs::remove_file(public_key_path).unwrap();
|
||||
}
|
||||
clients_and_states
|
||||
}
|
||||
|
||||
async fn prepare_clients_and_states_with_validation(
|
||||
dealer_details_db: &Arc<RwLock<HashMap<String, DealerDetails>>>,
|
||||
dealings_db: &Arc<RwLock<HashMap<String, Vec<ContractSafeBytes>>>>,
|
||||
proposal_db: &Arc<RwLock<HashMap<u64, ProposalResponse>>>,
|
||||
verification_share_db: &Arc<RwLock<HashMap<String, ContractVKShare>>>,
|
||||
) -> Vec<(DkgClient, State)> {
|
||||
let mut clients_and_states = prepare_clients_and_states_with_submission(
|
||||
dealer_details_db,
|
||||
dealings_db,
|
||||
proposal_db,
|
||||
verification_share_db,
|
||||
)
|
||||
.await;
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
verification_key_validation(dkg_client, state)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
clients_and_states
|
||||
}
|
||||
|
||||
async fn prepare_clients_and_states_with_finalization(
|
||||
dealer_details_db: &Arc<RwLock<HashMap<String, DealerDetails>>>,
|
||||
dealings_db: &Arc<RwLock<HashMap<String, Vec<ContractSafeBytes>>>>,
|
||||
proposal_db: &Arc<RwLock<HashMap<u64, ProposalResponse>>>,
|
||||
verification_share_db: &Arc<RwLock<HashMap<String, ContractVKShare>>>,
|
||||
) -> Vec<(DkgClient, State)> {
|
||||
let mut clients_and_states = prepare_clients_and_states_with_validation(
|
||||
dealer_details_db,
|
||||
dealings_db,
|
||||
proposal_db,
|
||||
verification_share_db,
|
||||
)
|
||||
.await;
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
verification_key_finalization(dkg_client, state)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
clients_and_states
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn check_dealers_filter_all_good() {
|
||||
let dealer_details_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let dealings_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let proposal_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let verification_share_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let mut clients_and_states = prepare_clients_and_states(
|
||||
&dealer_details_db,
|
||||
&dealings_db,
|
||||
&proposal_db,
|
||||
&verification_share_db,
|
||||
)
|
||||
.await;
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), TOTAL_DEALINGS);
|
||||
for mapping in filtered.iter() {
|
||||
assert_eq!(mapping.len(), 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn check_dealers_filter_one_bad_dealing() {
|
||||
let dealer_details_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let dealings_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let proposal_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let verification_share_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let mut clients_and_states = prepare_clients_and_states(
|
||||
&dealer_details_db,
|
||||
&dealings_db,
|
||||
&proposal_db,
|
||||
&verification_share_db,
|
||||
)
|
||||
.await;
|
||||
|
||||
// corrupt just one dealing
|
||||
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);
|
||||
});
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), TOTAL_DEALINGS);
|
||||
let corrupted_status = state
|
||||
.all_dealers()
|
||||
.get(&Addr::unchecked(TEST_VALIDATORS_ADDRESS[0]))
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.unwrap_err();
|
||||
assert_eq!(*corrupted_status, ComplaintReason::MissingDealing);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn check_dealers_filter_all_bad_dealings() {
|
||||
let dealer_details_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let dealings_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let proposal_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let verification_share_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let mut clients_and_states = prepare_clients_and_states(
|
||||
&dealer_details_db,
|
||||
&dealings_db,
|
||||
&proposal_db,
|
||||
&verification_share_db,
|
||||
)
|
||||
.await;
|
||||
|
||||
// corrupt all dealings of one address
|
||||
dealings_db
|
||||
.write()
|
||||
.unwrap()
|
||||
.entry(TEST_VALIDATORS_ADDRESS[0].to_string())
|
||||
.and_modify(|dealings| {
|
||||
dealings.iter_mut().for_each(|dealing| {
|
||||
dealing.0.pop();
|
||||
});
|
||||
});
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), TOTAL_DEALINGS);
|
||||
for mapping in filtered.iter() {
|
||||
assert_eq!(mapping.len(), 2);
|
||||
}
|
||||
let corrupted_status = state
|
||||
.all_dealers()
|
||||
.get(&Addr::unchecked(TEST_VALIDATORS_ADDRESS[0]))
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.unwrap_err();
|
||||
assert_eq!(*corrupted_status, ComplaintReason::MissingDealing);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn check_dealers_filter_malformed_dealing() {
|
||||
let dealer_details_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let dealings_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let proposal_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let verification_share_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let mut clients_and_states = prepare_clients_and_states(
|
||||
&dealer_details_db,
|
||||
&dealings_db,
|
||||
&proposal_db,
|
||||
&verification_share_db,
|
||||
)
|
||||
.await;
|
||||
|
||||
// corrupt just one dealing
|
||||
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);
|
||||
});
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
deterministic_filter_dealers(dkg_client, state, 2)
|
||||
.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)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), TOTAL_DEALINGS);
|
||||
let corrupted_status = state
|
||||
.all_dealers()
|
||||
.get(&Addr::unchecked(TEST_VALIDATORS_ADDRESS[0]))
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.unwrap_err();
|
||||
assert_eq!(*corrupted_status, ComplaintReason::MalformedDealing);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn check_dealers_filter_dealing_verification_error() {
|
||||
let dealer_details_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let dealings_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let proposal_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let verification_share_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let mut clients_and_states = prepare_clients_and_states(
|
||||
&dealer_details_db,
|
||||
&dealings_db,
|
||||
&proposal_db,
|
||||
&verification_share_db,
|
||||
)
|
||||
.await;
|
||||
|
||||
// corrupt just one dealing
|
||||
dealings_db
|
||||
.write()
|
||||
.unwrap()
|
||||
.entry(TEST_VALIDATORS_ADDRESS[0].to_string())
|
||||
.and_modify(|dealings| {
|
||||
let mut last = dealings.pop().unwrap();
|
||||
last.0.pop();
|
||||
last.0.push(42);
|
||||
dealings.push(last);
|
||||
});
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
deterministic_filter_dealers(dkg_client, state, 2)
|
||||
.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)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), TOTAL_DEALINGS);
|
||||
let corrupted_status = state
|
||||
.all_dealers()
|
||||
.get(&Addr::unchecked(TEST_VALIDATORS_ADDRESS[0]))
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.unwrap_err();
|
||||
assert_eq!(*corrupted_status, ComplaintReason::DealingVerificationError);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn partial_keypair_derivation() {
|
||||
let dealer_details_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let dealings_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let proposal_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let verification_share_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let mut clients_and_states = prepare_clients_and_states(
|
||||
&dealer_details_db,
|
||||
&dealings_db,
|
||||
&proposal_db,
|
||||
&verification_share_db,
|
||||
)
|
||||
.await;
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(derive_partial_keypair(state, 2, filtered).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn partial_keypair_derivation_with_threshold() {
|
||||
let dealer_details_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let dealings_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let proposal_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let verification_share_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let mut clients_and_states = prepare_clients_and_states(
|
||||
&dealer_details_db,
|
||||
&dealings_db,
|
||||
&proposal_db,
|
||||
&verification_share_db,
|
||||
)
|
||||
.await;
|
||||
|
||||
// corrupt just one dealing
|
||||
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);
|
||||
});
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(derive_partial_keypair(state, 2, filtered).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn submit_verification_key() {
|
||||
let dealer_details_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let dealings_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let proposal_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let verification_share_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let mut clients_and_states = prepare_clients_and_states_with_submission(
|
||||
&dealer_details_db,
|
||||
&dealings_db,
|
||||
&proposal_db,
|
||||
&verification_share_db,
|
||||
)
|
||||
.await;
|
||||
|
||||
for (_, state) in clients_and_states.iter_mut() {
|
||||
assert!(proposal_db
|
||||
.read()
|
||||
.unwrap()
|
||||
.contains_key(&state.proposal_id_value().unwrap()));
|
||||
assert!(state.coconut_keypair_is_some().await);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn validate_verification_key() {
|
||||
let dealer_details_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let dealings_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let proposal_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let verification_share_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let mut clients_and_states = prepare_clients_and_states_with_validation(
|
||||
&dealer_details_db,
|
||||
&dealings_db,
|
||||
&proposal_db,
|
||||
&verification_share_db,
|
||||
)
|
||||
.await;
|
||||
for (_, state) in clients_and_states.iter_mut() {
|
||||
let proposal = proposal_db
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&state.proposal_id_value().unwrap())
|
||||
.unwrap()
|
||||
.clone();
|
||||
assert_eq!(proposal.status, Status::Passed);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn validate_verification_key_malformed_share() {
|
||||
let dealer_details_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let dealings_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let proposal_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let verification_share_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let mut clients_and_states = prepare_clients_and_states_with_submission(
|
||||
&dealer_details_db,
|
||||
&dealings_db,
|
||||
&proposal_db,
|
||||
&verification_share_db,
|
||||
)
|
||||
.await;
|
||||
|
||||
verification_share_db
|
||||
.write()
|
||||
.unwrap()
|
||||
.entry(TEST_VALIDATORS_ADDRESS[0].to_string())
|
||||
.and_modify(|share| share.share.push('x'));
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
verification_key_validation(dkg_client, state)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for (idx, (_, state)) in clients_and_states.iter().enumerate() {
|
||||
let proposal = proposal_db
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&state.proposal_id_value().unwrap())
|
||||
.unwrap()
|
||||
.clone();
|
||||
if idx == 0 {
|
||||
assert_eq!(proposal.status, Status::Rejected);
|
||||
} else {
|
||||
assert_eq!(proposal.status, Status::Passed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn validate_verification_key_unpaired_share() {
|
||||
let dealer_details_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let dealings_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let proposal_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let verification_share_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let mut clients_and_states = prepare_clients_and_states_with_submission(
|
||||
&dealer_details_db,
|
||||
&dealings_db,
|
||||
&proposal_db,
|
||||
&verification_share_db,
|
||||
)
|
||||
.await;
|
||||
|
||||
let second_share = verification_share_db
|
||||
.write()
|
||||
.unwrap()
|
||||
.get(TEST_VALIDATORS_ADDRESS[1])
|
||||
.unwrap()
|
||||
.share
|
||||
.clone();
|
||||
verification_share_db
|
||||
.write()
|
||||
.unwrap()
|
||||
.entry(TEST_VALIDATORS_ADDRESS[0].to_string())
|
||||
.and_modify(|share| share.share = second_share);
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
verification_key_validation(dkg_client, state)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for (idx, (_, state)) in clients_and_states.iter().enumerate() {
|
||||
let proposal = proposal_db
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&state.proposal_id_value().unwrap())
|
||||
.unwrap()
|
||||
.clone();
|
||||
if idx == 0 {
|
||||
assert_eq!(proposal.status, Status::Rejected);
|
||||
} else {
|
||||
assert_eq!(proposal.status, Status::Passed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn finalize_verification_key() {
|
||||
let dealer_details_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let dealings_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let proposal_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let verification_share_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let clients_and_states = prepare_clients_and_states_with_finalization(
|
||||
&dealer_details_db,
|
||||
&dealings_db,
|
||||
&proposal_db,
|
||||
&verification_share_db,
|
||||
)
|
||||
.await;
|
||||
|
||||
for (_, state) in clients_and_states.iter() {
|
||||
let proposal = proposal_db
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&state.proposal_id_value().unwrap())
|
||||
.unwrap()
|
||||
.clone();
|
||||
assert_eq!(proposal.status, Status::Executed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ pub(crate) mod dkg;
|
||||
pub(crate) mod error;
|
||||
pub(crate) mod keypair;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
pub(crate) mod tests;
|
||||
|
||||
use crate::coconut::client::Client as LocalClient;
|
||||
use crate::coconut::deposit::extract_encryption_key;
|
||||
|
||||
@@ -36,48 +36,104 @@ use validator_client::validator_api::routes::{
|
||||
use crate::coconut::State;
|
||||
use crate::ValidatorApiStorage;
|
||||
use async_trait::async_trait;
|
||||
use coconut_dkg_common::dealer::{ContractDealing, DealerDetails, DealerDetailsResponse};
|
||||
use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, EpochState};
|
||||
use coconut_dkg_common::dealer::{
|
||||
ContractDealing, DealerDetails, DealerDetailsResponse, DealerType,
|
||||
};
|
||||
use coconut_dkg_common::event_attributes::{DKG_PROPOSAL_ID, NODE_INDEX};
|
||||
use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, EpochState, TOTAL_DEALINGS};
|
||||
use coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare};
|
||||
use contracts_common::dealings::ContractSafeBytes;
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
use cw3::ProposalResponse;
|
||||
use rand_07::rngs::OsRng;
|
||||
use rand_07::Rng;
|
||||
use rocket::http::Status;
|
||||
use rocket::local::asynchronous::Client;
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use validator_client::nymd::cosmwasm_client::logs::Log;
|
||||
use validator_client::nymd::cosmwasm_client::types::ExecuteResult;
|
||||
|
||||
const TEST_COIN_DENOM: &str = "unym";
|
||||
const TEST_REWARDING_VALIDATOR_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct DummyClient {
|
||||
pub(crate) struct DummyClient {
|
||||
validator_address: AccountId,
|
||||
tx_db: Arc<RwLock<HashMap<String, TxResponse>>>,
|
||||
proposal_db: Arc<RwLock<HashMap<u64, ProposalResponse>>>,
|
||||
spent_credential_db: Arc<RwLock<HashMap<String, SpendCredentialResponse>>>,
|
||||
|
||||
epoch_state: Arc<RwLock<EpochState>>,
|
||||
dealer_details: Arc<RwLock<HashMap<String, DealerDetails>>>,
|
||||
dealings: Arc<RwLock<HashMap<String, Vec<ContractSafeBytes>>>>,
|
||||
verification_share: Arc<RwLock<HashMap<String, ContractVKShare>>>,
|
||||
}
|
||||
|
||||
impl DummyClient {
|
||||
pub fn new(
|
||||
validator_address: AccountId,
|
||||
tx_db: &Arc<RwLock<HashMap<String, TxResponse>>>,
|
||||
proposal_db: &Arc<RwLock<HashMap<u64, ProposalResponse>>>,
|
||||
spent_credential_db: &Arc<RwLock<HashMap<String, SpendCredentialResponse>>>,
|
||||
) -> Self {
|
||||
let tx_db = Arc::clone(tx_db);
|
||||
let proposal_db = Arc::clone(proposal_db);
|
||||
let spent_credential_db = Arc::clone(spent_credential_db);
|
||||
pub fn new(validator_address: AccountId) -> Self {
|
||||
Self {
|
||||
validator_address,
|
||||
tx_db,
|
||||
proposal_db,
|
||||
spent_credential_db,
|
||||
tx_db: Arc::new(RwLock::new(HashMap::new())),
|
||||
proposal_db: Arc::new(RwLock::new(HashMap::new())),
|
||||
spent_credential_db: Arc::new(RwLock::new(HashMap::new())),
|
||||
epoch_state: Arc::new(RwLock::new(EpochState::default())),
|
||||
dealer_details: Arc::new(RwLock::new(HashMap::new())),
|
||||
dealings: Arc::new(RwLock::new(HashMap::new())),
|
||||
verification_share: Arc::new(RwLock::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_tx_db(mut self, tx_db: &Arc<RwLock<HashMap<String, TxResponse>>>) -> Self {
|
||||
self.tx_db = Arc::clone(tx_db);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_proposal_db(
|
||||
mut self,
|
||||
proposal_db: &Arc<RwLock<HashMap<u64, ProposalResponse>>>,
|
||||
) -> Self {
|
||||
self.proposal_db = Arc::clone(proposal_db);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_spent_credential_db(
|
||||
mut self,
|
||||
spent_credential_db: &Arc<RwLock<HashMap<String, SpendCredentialResponse>>>,
|
||||
) -> Self {
|
||||
self.spent_credential_db = Arc::clone(spent_credential_db);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn _with_epoch_state(mut self, epoch_state: &Arc<RwLock<EpochState>>) -> Self {
|
||||
self.epoch_state = Arc::clone(epoch_state);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_dealer_details(
|
||||
mut self,
|
||||
dealer_details: &Arc<RwLock<HashMap<String, DealerDetails>>>,
|
||||
) -> Self {
|
||||
self.dealer_details = Arc::clone(dealer_details);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_dealings(
|
||||
mut self,
|
||||
dealings: &Arc<RwLock<HashMap<String, Vec<ContractSafeBytes>>>>,
|
||||
) -> Self {
|
||||
self.dealings = Arc::clone(dealings);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_verification_share(
|
||||
mut self,
|
||||
verification_share: &Arc<RwLock<HashMap<String, ContractVKShare>>>,
|
||||
) -> Self {
|
||||
self.verification_share = Arc::clone(verification_share);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -107,7 +163,7 @@ impl super::client::Client for DummyClient {
|
||||
}
|
||||
|
||||
async fn list_proposals(&self) -> Result<Vec<ProposalResponse>> {
|
||||
todo!()
|
||||
Ok(self.proposal_db.read().unwrap().values().cloned().collect())
|
||||
}
|
||||
|
||||
async fn get_spent_credential(
|
||||
@@ -125,23 +181,52 @@ impl super::client::Client for DummyClient {
|
||||
}
|
||||
|
||||
async fn get_current_epoch_state(&self) -> Result<EpochState> {
|
||||
todo!()
|
||||
Ok(*self.epoch_state.read().unwrap())
|
||||
}
|
||||
|
||||
async fn get_self_registered_dealer_details(&self) -> Result<DealerDetailsResponse> {
|
||||
todo!()
|
||||
Ok(DealerDetailsResponse {
|
||||
details: self
|
||||
.dealer_details
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(self.validator_address.as_ref())
|
||||
.cloned(),
|
||||
dealer_type: DealerType::Current,
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_current_dealers(&self) -> Result<Vec<DealerDetails>> {
|
||||
todo!()
|
||||
Ok(self
|
||||
.dealer_details
|
||||
.read()
|
||||
.unwrap()
|
||||
.values()
|
||||
.cloned()
|
||||
.collect())
|
||||
}
|
||||
|
||||
async fn get_dealings(&self, _idx: usize) -> Result<Vec<ContractDealing>> {
|
||||
todo!()
|
||||
async fn get_dealings(&self, idx: usize) -> Result<Vec<ContractDealing>> {
|
||||
Ok(self
|
||||
.dealings
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|(dealer, dealings)| ContractDealing {
|
||||
dealing: dealings.get(idx).unwrap().clone(),
|
||||
dealer: Addr::unchecked(dealer),
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
async fn get_verification_key_shares(&self) -> Result<Vec<ContractVKShare>> {
|
||||
todo!()
|
||||
Ok(self
|
||||
.verification_share
|
||||
.read()
|
||||
.unwrap()
|
||||
.values()
|
||||
.cloned()
|
||||
.collect())
|
||||
}
|
||||
|
||||
async fn vote_proposal(
|
||||
@@ -151,36 +236,134 @@ impl super::client::Client for DummyClient {
|
||||
_fee: Option<Fee>,
|
||||
) -> Result<()> {
|
||||
if let Some(proposal) = self.proposal_db.write().unwrap().get_mut(&proposal_id) {
|
||||
if vote_yes {
|
||||
proposal.status = cw3::Status::Passed;
|
||||
} else {
|
||||
proposal.status = cw3::Status::Rejected;
|
||||
// for now, just suppose that first vote is honest
|
||||
if proposal.status == cw3::Status::Open {
|
||||
if vote_yes {
|
||||
proposal.status = cw3::Status::Passed;
|
||||
} else {
|
||||
proposal.status = cw3::Status::Rejected;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn execute_proposal(&self, _proposal_id: u64) -> Result<()> {
|
||||
todo!()
|
||||
async fn execute_proposal(&self, proposal_id: u64) -> Result<()> {
|
||||
self.proposal_db
|
||||
.write()
|
||||
.unwrap()
|
||||
.entry(proposal_id)
|
||||
.and_modify(|prop| {
|
||||
if prop.status == cw3::Status::Passed {
|
||||
prop.status = cw3::Status::Executed
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn register_dealer(
|
||||
&self,
|
||||
_bte_key: EncodedBTEPublicKeyWithProof,
|
||||
_announce_address: String,
|
||||
bte_public_key_with_proof: EncodedBTEPublicKeyWithProof,
|
||||
announce_address: String,
|
||||
) -> Result<ExecuteResult> {
|
||||
todo!()
|
||||
let assigned_index = OsRng.gen();
|
||||
self.dealer_details.write().unwrap().insert(
|
||||
self.validator_address.to_string(),
|
||||
DealerDetails {
|
||||
address: Addr::unchecked(&self.validator_address.to_string()),
|
||||
bte_public_key_with_proof,
|
||||
announce_address,
|
||||
assigned_index,
|
||||
},
|
||||
);
|
||||
Ok(ExecuteResult {
|
||||
logs: vec![Log {
|
||||
msg_index: 0,
|
||||
events: vec![cosmwasm_std::Event::new("wasm")
|
||||
.add_attribute(NODE_INDEX, assigned_index.to_string())],
|
||||
}],
|
||||
data: Default::default(),
|
||||
transaction_hash: Hash::new([0; 32]),
|
||||
gas_info: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn submit_dealing(&self, _dealing_bytes: ContractSafeBytes) -> Result<ExecuteResult> {
|
||||
todo!()
|
||||
async fn submit_dealing(&self, dealing_bytes: ContractSafeBytes) -> Result<ExecuteResult> {
|
||||
self.dealings
|
||||
.write()
|
||||
.unwrap()
|
||||
.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]);
|
||||
|
||||
Ok(ExecuteResult {
|
||||
logs: vec![],
|
||||
data: Default::default(),
|
||||
transaction_hash: Hash::new([0; 32]),
|
||||
gas_info: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn submit_verification_key_share(
|
||||
&self,
|
||||
_share: VerificationKeyShare,
|
||||
share: VerificationKeyShare,
|
||||
) -> Result<ExecuteResult> {
|
||||
todo!()
|
||||
let dealer_details = self
|
||||
.dealer_details
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(self.validator_address.as_ref())
|
||||
.unwrap()
|
||||
.clone();
|
||||
self.verification_share.write().unwrap().insert(
|
||||
self.validator_address.to_string(),
|
||||
ContractVKShare {
|
||||
share,
|
||||
announce_address: dealer_details.announce_address.clone(),
|
||||
node_index: dealer_details.assigned_index,
|
||||
owner: Addr::unchecked(self.validator_address.to_string()),
|
||||
verified: false,
|
||||
},
|
||||
);
|
||||
let proposal_id = OsRng.gen();
|
||||
let verify_vk_share_req = coconut_dkg_common::msg::ExecuteMsg::VerifyVerificationKeyShare {
|
||||
owner: Addr::unchecked(self.validator_address.as_ref()),
|
||||
};
|
||||
let verify_vk_share_msg = CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: String::new(),
|
||||
msg: to_binary(&verify_vk_share_req).unwrap(),
|
||||
funds: vec![],
|
||||
});
|
||||
let proposal = ProposalResponse {
|
||||
id: proposal_id,
|
||||
title: String::new(),
|
||||
description: String::new(),
|
||||
msgs: vec![verify_vk_share_msg],
|
||||
status: cw3::Status::Open,
|
||||
expires: cw_utils::Expiration::Never {},
|
||||
threshold: cw_utils::ThresholdResponse::AbsolutePercentage {
|
||||
percentage: Decimal::from_ratio(2u32, 3u32),
|
||||
total_weight: 100,
|
||||
},
|
||||
};
|
||||
self.proposal_db
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(proposal_id, proposal);
|
||||
Ok(ExecuteResult {
|
||||
logs: vec![Log {
|
||||
msg_index: 0,
|
||||
events: vec![cosmwasm_std::Event::new("wasm")
|
||||
.add_attribute(DKG_PROPOSAL_ID, proposal_id.to_string())],
|
||||
}],
|
||||
data: Default::default(),
|
||||
transaction_hash: Hash::new([0; 32]),
|
||||
gas_info: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,12 +450,9 @@ async fn signed_before() {
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(tx_hash.to_string(), tx_entry.clone());
|
||||
let nymd_client = DummyClient::new(
|
||||
AccountId::from_str(TEST_REWARDING_VALIDATOR_ADDRESS).unwrap(),
|
||||
&tx_db,
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
);
|
||||
let nymd_client =
|
||||
DummyClient::new(AccountId::from_str(TEST_REWARDING_VALIDATOR_ADDRESS).unwrap())
|
||||
.with_tx_db(&tx_db);
|
||||
let comm_channel = DummyCommunicationChannel::new(key_pair.verification_key());
|
||||
let staged_key_pair = crate::coconut::KeyPair::new();
|
||||
staged_key_pair.set(key_pair).await;
|
||||
@@ -334,12 +514,8 @@ async fn signed_before() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn state_functions() {
|
||||
let nymd_client = DummyClient::new(
|
||||
AccountId::from_str(TEST_REWARDING_VALIDATOR_ADDRESS).unwrap(),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
);
|
||||
let nymd_client =
|
||||
DummyClient::new(AccountId::from_str(TEST_REWARDING_VALIDATOR_ADDRESS).unwrap());
|
||||
let params = Parameters::new(4).unwrap();
|
||||
let key_pair = ttp_keygen(¶ms, 1, 1).unwrap().remove(0);
|
||||
let mut db_dir = std::env::temp_dir();
|
||||
@@ -511,12 +687,9 @@ async fn blind_sign_correct() {
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(tx_hash.to_string(), tx_entry.clone());
|
||||
let nymd_client = DummyClient::new(
|
||||
AccountId::from_str(TEST_REWARDING_VALIDATOR_ADDRESS).unwrap(),
|
||||
&tx_db,
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
);
|
||||
let nymd_client =
|
||||
DummyClient::new(AccountId::from_str(TEST_REWARDING_VALIDATOR_ADDRESS).unwrap())
|
||||
.with_tx_db(&tx_db);
|
||||
let comm_channel = DummyCommunicationChannel::new(key_pair.verification_key());
|
||||
let staged_key_pair = crate::coconut::KeyPair::new();
|
||||
staged_key_pair.set(key_pair).await;
|
||||
@@ -592,12 +765,8 @@ async fn signature_test() {
|
||||
let mut db_dir = std::env::temp_dir();
|
||||
db_dir.push(&key_pair.verification_key().to_bs58()[..8]);
|
||||
let storage = ValidatorApiStorage::init(db_dir).await.unwrap();
|
||||
let nymd_client = DummyClient::new(
|
||||
AccountId::from_str(TEST_REWARDING_VALIDATOR_ADDRESS).unwrap(),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
);
|
||||
let nymd_client =
|
||||
DummyClient::new(AccountId::from_str(TEST_REWARDING_VALIDATOR_ADDRESS).unwrap());
|
||||
let comm_channel = DummyCommunicationChannel::new(key_pair.verification_key());
|
||||
let staged_key_pair = crate::coconut::KeyPair::new();
|
||||
staged_key_pair.set(key_pair).await;
|
||||
@@ -663,12 +832,9 @@ async fn verification_of_bandwidth_credential() {
|
||||
let validator_address = AccountId::from_str(TEST_REWARDING_VALIDATOR_ADDRESS).unwrap();
|
||||
let proposal_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let spent_credential_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let nymd_client = DummyClient::new(
|
||||
validator_address.clone(),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
&proposal_db,
|
||||
&spent_credential_db,
|
||||
);
|
||||
let nymd_client = DummyClient::new(validator_address.clone())
|
||||
.with_proposal_db(&proposal_db)
|
||||
.with_spent_credential_db(&spent_credential_db);
|
||||
let mut db_dir = std::env::temp_dir();
|
||||
let params = Parameters::new(4).unwrap();
|
||||
let mut key_pairs = ttp_keygen(¶ms, 1, 1).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user