moved epoch advancement logic into separate file
This commit is contained in:
@@ -93,7 +93,9 @@ pub struct Epoch {
|
||||
pub state: EpochState,
|
||||
pub epoch_id: EpochId,
|
||||
pub time_configuration: TimeConfiguration,
|
||||
pub finish_timestamp: Option<Timestamp>,
|
||||
|
||||
#[serde(alias = "finish_timestamp")]
|
||||
pub deadline: Option<Timestamp>,
|
||||
}
|
||||
|
||||
impl Epoch {
|
||||
@@ -126,12 +128,12 @@ impl Epoch {
|
||||
state,
|
||||
epoch_id,
|
||||
time_configuration,
|
||||
finish_timestamp: duration.map(|d| current_timestamp.plus_seconds(d)),
|
||||
deadline: duration.map(|d| current_timestamp.plus_seconds(d)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn final_timestamp_secs(&self) -> Option<u64> {
|
||||
let mut finish = self.finish_timestamp?.seconds();
|
||||
let mut finish = self.deadline?.seconds();
|
||||
let time_configuration = self.time_configuration;
|
||||
let mut curr_epoch_state = self.state;
|
||||
while let Some(state) = curr_epoch_state.next() {
|
||||
|
||||
@@ -91,7 +91,7 @@ where
|
||||
.as_secs();
|
||||
|
||||
if epoch.state.is_final() {
|
||||
if let Some(finish_timestamp) = epoch.finish_timestamp {
|
||||
if let Some(finish_timestamp) = epoch.deadline {
|
||||
if current_timestamp_secs + SAFETY_BUFFER_SECS >= finish_timestamp.seconds() {
|
||||
info!("In the next {} minute(s), a transition will take place in the coconut system. Deposits should be halted in this time for safety reasons.", SAFETY_BUFFER_SECS / 60);
|
||||
exit(0);
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::epoch_state::queries::{
|
||||
};
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
use crate::epoch_state::transactions::{
|
||||
advance_epoch_state, try_initiate_dkg, try_surpassed_threshold,
|
||||
try_advance_epoch_state, try_initiate_dkg, try_surpassed_threshold,
|
||||
};
|
||||
use crate::error::ContractError;
|
||||
use crate::state::queries::query_state;
|
||||
@@ -118,7 +118,7 @@ pub fn execute(
|
||||
try_verify_verification_key_share(deps, info, owner, resharing)
|
||||
}
|
||||
ExecuteMsg::SurpassedThreshold {} => try_surpassed_threshold(deps, env),
|
||||
ExecuteMsg::AdvanceEpochState {} => advance_epoch_state(deps, env),
|
||||
ExecuteMsg::AdvanceEpochState {} => try_advance_epoch_state(deps, env),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ pub fn try_add_dealer(
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::dealers::storage::current_dealers;
|
||||
use crate::epoch_state::transactions::{advance_epoch_state, try_initiate_dkg};
|
||||
use crate::epoch_state::transactions::{try_advance_epoch_state, try_initiate_dkg};
|
||||
use crate::support::tests::fixtures::dealer_details_fixture;
|
||||
use crate::support::tests::helpers;
|
||||
use crate::support::tests::helpers::{add_fixture_dealer, ADMIN_ADDRESS, GROUP_MEMBERS};
|
||||
@@ -156,7 +156,7 @@ pub(crate) mod tests {
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
|
||||
|
||||
add_fixture_dealer(deps.as_mut());
|
||||
advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
|
||||
let ret = try_add_dealer(
|
||||
deps.as_mut(),
|
||||
|
||||
@@ -206,7 +206,7 @@ pub fn try_commit_dealings_chunk(
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
use crate::epoch_state::transactions::{advance_epoch_state, try_initiate_dkg};
|
||||
use crate::epoch_state::transactions::{try_advance_epoch_state, try_initiate_dkg};
|
||||
use crate::support::tests::fixtures::{
|
||||
dealer_details_fixture, dealing_metadata_fixture, partial_dealing_fixture,
|
||||
};
|
||||
@@ -250,7 +250,7 @@ pub(crate) mod tests {
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
|
||||
add_fixture_dealer(deps.as_mut());
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
|
||||
let ret = try_commit_dealings_chunk(
|
||||
deps.as_mut(),
|
||||
|
||||
@@ -37,7 +37,7 @@ pub(crate) mod test {
|
||||
let mut deps = init_contract();
|
||||
let epoch = query_current_epoch(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(epoch.state, EpochState::WaitingInitialisation);
|
||||
assert_eq!(epoch.finish_timestamp, None);
|
||||
assert_eq!(epoch.deadline, None);
|
||||
|
||||
let env = mock_env();
|
||||
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
@@ -48,7 +48,7 @@ pub(crate) mod test {
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
epoch.deadline.unwrap(),
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs)
|
||||
|
||||
@@ -0,0 +1,449 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::dealers::storage::current_dealers;
|
||||
use crate::epoch_state::storage::{CURRENT_EPOCH, INITIAL_REPLACEMENT_DATA, THRESHOLD};
|
||||
use crate::epoch_state::transactions::{
|
||||
dealers_eq_members, replacement_threshold_surpassed, reset_dkg_state,
|
||||
};
|
||||
use crate::error::ContractError;
|
||||
use crate::state::storage::STATE;
|
||||
use crate::verification_key_shares::storage::verified_dealers;
|
||||
use cosmwasm_std::{Addr, DepsMut, Env, Order, Response};
|
||||
use nym_coconut_dkg_common::types::{Epoch, EpochState, InitialReplacementData};
|
||||
|
||||
pub fn try_advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Response, ContractError> {
|
||||
let current_epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
if current_epoch.state == EpochState::WaitingInitialisation {
|
||||
return Err(ContractError::WaitingInitialisation);
|
||||
}
|
||||
|
||||
if let Some(finish_timestamp) = current_epoch.deadline {
|
||||
if finish_timestamp > env.block.time {
|
||||
return Err(ContractError::EarlyEpochStateAdvancement(
|
||||
finish_timestamp
|
||||
.minus_seconds(env.block.time.seconds())
|
||||
.seconds(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let next_epoch = if let Some(state) = current_epoch.state.next() {
|
||||
// We are during DKG process
|
||||
let mut new_state = state;
|
||||
if let EpochState::DealingExchange { .. } = state {
|
||||
let current_dealers = current_dealers()
|
||||
.keys(deps.storage, None, None, Order::Ascending)
|
||||
.collect::<Result<Vec<Addr>, _>>()?;
|
||||
let group_members =
|
||||
STATE
|
||||
.load(deps.storage)?
|
||||
.group_addr
|
||||
.list_members(&deps.querier, None, None)?;
|
||||
if current_dealers.len() < group_members.len() {
|
||||
// If not all group members registered yet, we just stay in the same state until
|
||||
// they either register or they get kicked out of the group
|
||||
new_state = current_epoch.state;
|
||||
} else {
|
||||
// note: ceiling in integer division can be achieved via q = (x + y - 1) / y;
|
||||
let threshold = (2 * current_dealers.len() as u64 + 3 - 1) / 3;
|
||||
THRESHOLD.save(deps.storage, &threshold)?;
|
||||
}
|
||||
};
|
||||
Epoch::new(
|
||||
new_state,
|
||||
current_epoch.epoch_id,
|
||||
current_epoch.time_configuration,
|
||||
env.block.time,
|
||||
)
|
||||
} else if dealers_eq_members(&deps)? {
|
||||
// The dealer set hasn't changed, so we only extend the finish timestamp
|
||||
// The epoch remains the same, as we use it as key for storing VKs
|
||||
|
||||
// TODO: change that behaviour in the following PR
|
||||
Epoch::new(
|
||||
current_epoch.state,
|
||||
current_epoch.epoch_id,
|
||||
current_epoch.time_configuration,
|
||||
env.block.time,
|
||||
)
|
||||
} else {
|
||||
// Dealer set changed, we need to redo DKG...
|
||||
let state = if replacement_threshold_surpassed(&deps)? {
|
||||
// ... in reset mode
|
||||
INITIAL_REPLACEMENT_DATA.remove(deps.storage);
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
} else {
|
||||
// ... in reshare mode
|
||||
if INITIAL_REPLACEMENT_DATA.may_load(deps.storage)?.is_some() {
|
||||
INITIAL_REPLACEMENT_DATA.update::<_, ContractError>(deps.storage, |mut data| {
|
||||
// TODO: FIXME: for second reshare the added set of dealers won't be allowed to participate
|
||||
data.initial_height = env.block.height;
|
||||
Ok(data)
|
||||
})?;
|
||||
} else {
|
||||
let replacement_data = InitialReplacementData {
|
||||
initial_dealers: verified_dealers(deps.storage)?,
|
||||
initial_height: env.block.height,
|
||||
};
|
||||
INITIAL_REPLACEMENT_DATA.save(deps.storage, &replacement_data)?;
|
||||
}
|
||||
|
||||
EpochState::PublicKeySubmission { resharing: true }
|
||||
};
|
||||
reset_dkg_state(deps.storage)?;
|
||||
Epoch::new(
|
||||
state,
|
||||
current_epoch.epoch_id + 1,
|
||||
current_epoch.time_configuration,
|
||||
env.block.time,
|
||||
)
|
||||
};
|
||||
CURRENT_EPOCH.save(deps.storage, &next_epoch)?;
|
||||
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::dealers::storage::past_dealers;
|
||||
use crate::epoch_state::transactions::try_initiate_dkg;
|
||||
use crate::error::ContractError::EarlyEpochStateAdvancement;
|
||||
use crate::support::tests::fixtures::{dealer_details_fixture, vk_share_fixture};
|
||||
use crate::support::tests::helpers::{init_contract, ADMIN_ADDRESS, GROUP_MEMBERS};
|
||||
use crate::verification_key_shares::storage::vk_shares;
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cw4::Member;
|
||||
use nym_coconut_dkg_common::types::TimeConfiguration;
|
||||
|
||||
#[test]
|
||||
fn advance_state() {
|
||||
let mut deps = init_contract();
|
||||
let mut env = mock_env();
|
||||
|
||||
{
|
||||
let mut group = GROUP_MEMBERS.lock().unwrap();
|
||||
|
||||
group.push((
|
||||
Member {
|
||||
addr: "owner1".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
));
|
||||
group.push((
|
||||
Member {
|
||||
addr: "owner2".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
));
|
||||
group.push((
|
||||
Member {
|
||||
addr: "owner3".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
));
|
||||
group.push((
|
||||
Member {
|
||||
addr: "owner4".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
));
|
||||
}
|
||||
|
||||
// can't advance the state if dkg hasn't been initiated
|
||||
assert_eq!(
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
ContractError::WaitingInitialisation
|
||||
);
|
||||
|
||||
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.deadline.unwrap(),
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.public_key_submission_time_secs)
|
||||
);
|
||||
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.public_key_submission_time_secs - 1);
|
||||
assert_eq!(
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(1)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(1);
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
);
|
||||
|
||||
// setup dealer details
|
||||
let all_shares: [_; 4] =
|
||||
std::array::from_fn(|i| vk_share_fixture(&format!("owner{}", i + 1), 0));
|
||||
for share in all_shares.iter() {
|
||||
vk_shares()
|
||||
.save(deps.as_mut().storage, (&share.owner, 0), share)
|
||||
.unwrap();
|
||||
}
|
||||
let all_details: [_; 4] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 1));
|
||||
for details in all_details.iter() {
|
||||
current_dealers()
|
||||
.save(deps.as_mut().storage, &details.address, details)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
assert!(INITIAL_REPLACEMENT_DATA
|
||||
.may_load(&deps.storage)
|
||||
.unwrap()
|
||||
.is_none());
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.public_key_submission_time_secs);
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::DealingExchange { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.deadline.unwrap(),
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.dealing_exchange_time_secs)
|
||||
);
|
||||
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.dealing_exchange_time_secs - 2);
|
||||
assert_eq!(
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(2)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(3);
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::VerificationKeySubmission { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.deadline.unwrap(),
|
||||
env.block.time.plus_seconds(
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_submission_time_secs
|
||||
)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_submission_time_secs
|
||||
- 2,
|
||||
);
|
||||
assert_eq!(
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(2)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(3);
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::VerificationKeyValidation { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.deadline.unwrap(),
|
||||
env.block.time.plus_seconds(
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_validation_time_secs
|
||||
)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_validation_time_secs
|
||||
- 3,
|
||||
);
|
||||
assert_eq!(
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(3)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(3);
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::VerificationKeyFinalization { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.deadline.unwrap(),
|
||||
env.block.time.plus_seconds(
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_finalization_time_secs
|
||||
)
|
||||
);
|
||||
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().verification_key_finalization_time_secs - 1);
|
||||
assert_eq!(
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(1)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(1);
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(epoch.state, EpochState::InProgress);
|
||||
assert_eq!(
|
||||
epoch.deadline.unwrap(),
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.in_progress_time_secs)
|
||||
);
|
||||
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.in_progress_time_secs - 100);
|
||||
assert_eq!(
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(100)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(50);
|
||||
assert_eq!(
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(50)
|
||||
);
|
||||
|
||||
// Group hasn't changed, so we remain in the same epoch, with updated finish timestamp
|
||||
env.block.time = env.block.time.plus_seconds(100);
|
||||
let prev_epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let curr_epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
let expected_epoch = Epoch::new(
|
||||
EpochState::InProgress,
|
||||
prev_epoch.epoch_id,
|
||||
prev_epoch.time_configuration,
|
||||
env.block.time,
|
||||
);
|
||||
assert_eq!(curr_epoch, expected_epoch);
|
||||
|
||||
// Group changed slightly, so re-run dkg in reshare mode
|
||||
*GROUP_MEMBERS.lock().unwrap().first_mut().unwrap() = (
|
||||
Member {
|
||||
addr: "owner5".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
);
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.in_progress_time_secs);
|
||||
let prev_epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let curr_epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
let expected_epoch = Epoch::new(
|
||||
EpochState::PublicKeySubmission { resharing: true },
|
||||
prev_epoch.epoch_id + 1,
|
||||
prev_epoch.time_configuration,
|
||||
env.block.time,
|
||||
);
|
||||
assert_eq!(curr_epoch, expected_epoch);
|
||||
assert!(THRESHOLD.may_load(&deps.storage).unwrap().is_none());
|
||||
let replacement_data = INITIAL_REPLACEMENT_DATA.load(&deps.storage).unwrap();
|
||||
let expected_replacement_data = InitialReplacementData {
|
||||
initial_dealers: all_details.iter().map(|d| d.address.clone()).collect(),
|
||||
initial_height: 12345,
|
||||
};
|
||||
assert_eq!(replacement_data, expected_replacement_data);
|
||||
|
||||
let all_details: [_; 4] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 2));
|
||||
for details in all_details.iter() {
|
||||
past_dealers()
|
||||
.remove(deps.as_mut().storage, &details.address)
|
||||
.unwrap();
|
||||
current_dealers()
|
||||
.save(deps.as_mut().storage, &details.address, details)
|
||||
.unwrap();
|
||||
}
|
||||
for times in [
|
||||
epoch.time_configuration.public_key_submission_time_secs,
|
||||
epoch.time_configuration.dealing_exchange_time_secs,
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_submission_time_secs,
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_validation_time_secs,
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_finalization_time_secs,
|
||||
] {
|
||||
env.block.time = env.block.time.plus_seconds(times);
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
}
|
||||
|
||||
let all_shares: [_; 4] = std::array::from_fn(|i| {
|
||||
let mut share = vk_share_fixture(&format!("owner{}", i + 1), 1);
|
||||
share.verified = i % 2 == 0;
|
||||
share
|
||||
});
|
||||
for share in all_shares.iter() {
|
||||
vk_shares()
|
||||
.save(deps.as_mut().storage, (&share.owner, 0), share)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// Group changed even more, surpassing threshold, so re-run dkg in reset mode
|
||||
*GROUP_MEMBERS.lock().unwrap().last_mut().unwrap() = (
|
||||
Member {
|
||||
addr: "owner6".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
);
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.in_progress_time_secs);
|
||||
let prev_epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let curr_epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
let expected_epoch = Epoch::new(
|
||||
EpochState::PublicKeySubmission { resharing: true },
|
||||
prev_epoch.epoch_id + 1,
|
||||
prev_epoch.time_configuration,
|
||||
env.block.time,
|
||||
);
|
||||
assert_eq!(curr_epoch, expected_epoch);
|
||||
assert!(THRESHOLD.may_load(&deps.storage).unwrap().is_none());
|
||||
}
|
||||
}
|
||||
+15
-428
@@ -2,13 +2,17 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::dealers::storage::{current_dealers, past_dealers};
|
||||
use crate::epoch_state::storage::{CURRENT_EPOCH, INITIAL_REPLACEMENT_DATA, THRESHOLD};
|
||||
use crate::epoch_state::storage::{CURRENT_EPOCH, THRESHOLD};
|
||||
use crate::epoch_state::utils::check_epoch_state;
|
||||
use crate::error::ContractError;
|
||||
use crate::state::storage::{DKG_ADMIN, STATE};
|
||||
use crate::verification_key_shares::storage::verified_dealers;
|
||||
use cosmwasm_std::{Addr, Deps, DepsMut, Env, MessageInfo, Order, Response, Storage};
|
||||
use nym_coconut_dkg_common::types::{Epoch, EpochState, InitialReplacementData};
|
||||
use nym_coconut_dkg_common::types::{Epoch, EpochState};
|
||||
|
||||
pub use advance_epoch_state::try_advance_epoch_state;
|
||||
|
||||
pub mod advance_epoch_state;
|
||||
|
||||
fn reset_dkg_state(storage: &mut dyn Storage) -> Result<(), ContractError> {
|
||||
THRESHOLD.remove(storage);
|
||||
@@ -91,98 +95,6 @@ pub(crate) fn try_initiate_dkg(
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Response, ContractError> {
|
||||
let current_epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
if current_epoch.state == EpochState::WaitingInitialisation {
|
||||
return Err(ContractError::WaitingInitialisation);
|
||||
}
|
||||
|
||||
if let Some(finish_timestamp) = current_epoch.finish_timestamp {
|
||||
if finish_timestamp > env.block.time {
|
||||
return Err(ContractError::EarlyEpochStateAdvancement(
|
||||
finish_timestamp
|
||||
.minus_seconds(env.block.time.seconds())
|
||||
.seconds(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let next_epoch = if let Some(state) = current_epoch.state.next() {
|
||||
// We are during DKG process
|
||||
let mut new_state = state;
|
||||
if let EpochState::DealingExchange { .. } = state {
|
||||
let current_dealers = current_dealers()
|
||||
.keys(deps.storage, None, None, Order::Ascending)
|
||||
.collect::<Result<Vec<Addr>, _>>()?;
|
||||
let group_members =
|
||||
STATE
|
||||
.load(deps.storage)?
|
||||
.group_addr
|
||||
.list_members(&deps.querier, None, None)?;
|
||||
if current_dealers.len() < group_members.len() {
|
||||
// If not all group members registered yet, we just stay in the same state until
|
||||
// they either register or they get kicked out of the group
|
||||
new_state = current_epoch.state;
|
||||
} else {
|
||||
// note: ceiling in integer division can be achieved via q = (x + y - 1) / y;
|
||||
let threshold = (2 * current_dealers.len() as u64 + 3 - 1) / 3;
|
||||
THRESHOLD.save(deps.storage, &threshold)?;
|
||||
}
|
||||
};
|
||||
Epoch::new(
|
||||
new_state,
|
||||
current_epoch.epoch_id,
|
||||
current_epoch.time_configuration,
|
||||
env.block.time,
|
||||
)
|
||||
} else if dealers_eq_members(&deps)? {
|
||||
// The dealer set hasn't changed, so we only extend the finish timestamp
|
||||
// The epoch remains the same, as we use it as key for storing VKs
|
||||
|
||||
// TODO: change that behaviour in the following PR
|
||||
Epoch::new(
|
||||
current_epoch.state,
|
||||
current_epoch.epoch_id,
|
||||
current_epoch.time_configuration,
|
||||
env.block.time,
|
||||
)
|
||||
} else {
|
||||
// Dealer set changed, we need to redo DKG...
|
||||
let state = if replacement_threshold_surpassed(&deps)? {
|
||||
// ... in reset mode
|
||||
INITIAL_REPLACEMENT_DATA.remove(deps.storage);
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
} else {
|
||||
// ... in reshare mode
|
||||
if INITIAL_REPLACEMENT_DATA.may_load(deps.storage)?.is_some() {
|
||||
INITIAL_REPLACEMENT_DATA.update::<_, ContractError>(deps.storage, |mut data| {
|
||||
// TODO: FIXME: for second reshare the added set of dealers won't be allowed to participate
|
||||
data.initial_height = env.block.height;
|
||||
Ok(data)
|
||||
})?;
|
||||
} else {
|
||||
let replacement_data = InitialReplacementData {
|
||||
initial_dealers: verified_dealers(deps.storage)?,
|
||||
initial_height: env.block.height,
|
||||
};
|
||||
INITIAL_REPLACEMENT_DATA.save(deps.storage, &replacement_data)?;
|
||||
}
|
||||
|
||||
EpochState::PublicKeySubmission { resharing: true }
|
||||
};
|
||||
reset_dkg_state(deps.storage)?;
|
||||
Epoch::new(
|
||||
state,
|
||||
current_epoch.epoch_id + 1,
|
||||
current_epoch.time_configuration,
|
||||
env.block.time,
|
||||
)
|
||||
};
|
||||
CURRENT_EPOCH.save(deps.storage, &next_epoch)?;
|
||||
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
pub(crate) fn try_surpassed_threshold(
|
||||
deps: DepsMut<'_>,
|
||||
env: Env,
|
||||
@@ -209,7 +121,8 @@ pub(crate) fn try_surpassed_threshold(
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::error::ContractError::EarlyEpochStateAdvancement;
|
||||
use crate::epoch_state::storage::INITIAL_REPLACEMENT_DATA;
|
||||
use crate::epoch_state::transactions::advance_epoch_state::try_advance_epoch_state;
|
||||
use crate::support::tests::fixtures::{dealer_details_fixture, vk_share_fixture};
|
||||
use crate::support::tests::helpers::{init_contract, ADMIN_ADDRESS, GROUP_MEMBERS};
|
||||
use crate::verification_key_shares::storage::vk_shares;
|
||||
@@ -217,7 +130,9 @@ pub(crate) mod tests {
|
||||
use cosmwasm_std::Addr;
|
||||
use cw4::Member;
|
||||
use cw_controllers::AdminError;
|
||||
use nym_coconut_dkg_common::types::{DealerDetails, EpochState, TimeConfiguration};
|
||||
use nym_coconut_dkg_common::types::{
|
||||
DealerDetails, EpochState, InitialReplacementData, TimeConfiguration,
|
||||
};
|
||||
use rusty_fork::rusty_fork_test;
|
||||
|
||||
// Because of the global variable handling group, we need individual process for each test
|
||||
@@ -378,335 +293,7 @@ pub(crate) mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn advance_state() {
|
||||
let mut deps = init_contract();
|
||||
let mut env = mock_env();
|
||||
|
||||
{
|
||||
let mut group = GROUP_MEMBERS.lock().unwrap();
|
||||
|
||||
group.push((
|
||||
Member {
|
||||
addr: "owner1".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
));
|
||||
group.push((
|
||||
Member {
|
||||
addr: "owner2".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
));
|
||||
group.push((
|
||||
Member {
|
||||
addr: "owner3".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
));
|
||||
group.push((
|
||||
Member {
|
||||
addr: "owner4".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
));
|
||||
}
|
||||
|
||||
// can't advance the state if dkg hasn't been initiated
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
ContractError::WaitingInitialisation
|
||||
);
|
||||
|
||||
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.public_key_submission_time_secs)
|
||||
);
|
||||
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.public_key_submission_time_secs - 1);
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(1)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(1);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
);
|
||||
|
||||
// setup dealer details
|
||||
let all_shares: [_; 4] =
|
||||
std::array::from_fn(|i| vk_share_fixture(&format!("owner{}", i + 1), 0));
|
||||
for share in all_shares.iter() {
|
||||
vk_shares()
|
||||
.save(deps.as_mut().storage, (&share.owner, 0), share)
|
||||
.unwrap();
|
||||
}
|
||||
let all_details: [_; 4] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 1));
|
||||
for details in all_details.iter() {
|
||||
current_dealers()
|
||||
.save(deps.as_mut().storage, &details.address, details)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
assert!(INITIAL_REPLACEMENT_DATA
|
||||
.may_load(&deps.storage)
|
||||
.unwrap()
|
||||
.is_none());
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.public_key_submission_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::DealingExchange { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.dealing_exchange_time_secs)
|
||||
);
|
||||
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.dealing_exchange_time_secs - 2);
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(2)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(3);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::VerificationKeySubmission { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block.time.plus_seconds(
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_submission_time_secs
|
||||
)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_submission_time_secs
|
||||
- 2,
|
||||
);
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(2)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(3);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::VerificationKeyValidation { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block.time.plus_seconds(
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_validation_time_secs
|
||||
)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_validation_time_secs
|
||||
- 3,
|
||||
);
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(3)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(3);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::VerificationKeyFinalization { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block.time.plus_seconds(
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_finalization_time_secs
|
||||
)
|
||||
);
|
||||
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().verification_key_finalization_time_secs - 1);
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(1)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(1);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(epoch.state, EpochState::InProgress);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.in_progress_time_secs)
|
||||
);
|
||||
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.in_progress_time_secs - 100);
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(100)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(50);
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(50)
|
||||
);
|
||||
|
||||
// Group hasn't changed, so we remain in the same epoch, with updated finish timestamp
|
||||
env.block.time = env.block.time.plus_seconds(100);
|
||||
let prev_epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let curr_epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
let expected_epoch = Epoch::new(
|
||||
EpochState::InProgress,
|
||||
prev_epoch.epoch_id,
|
||||
prev_epoch.time_configuration,
|
||||
env.block.time,
|
||||
);
|
||||
assert_eq!(curr_epoch, expected_epoch);
|
||||
|
||||
// Group changed slightly, so re-run dkg in reshare mode
|
||||
*GROUP_MEMBERS.lock().unwrap().first_mut().unwrap() = (
|
||||
Member {
|
||||
addr: "owner5".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
);
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.in_progress_time_secs);
|
||||
let prev_epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let curr_epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
let expected_epoch = Epoch::new(
|
||||
EpochState::PublicKeySubmission { resharing: true },
|
||||
prev_epoch.epoch_id + 1,
|
||||
prev_epoch.time_configuration,
|
||||
env.block.time,
|
||||
);
|
||||
assert_eq!(curr_epoch, expected_epoch);
|
||||
assert!(THRESHOLD.may_load(&deps.storage).unwrap().is_none());
|
||||
let replacement_data = INITIAL_REPLACEMENT_DATA.load(&deps.storage).unwrap();
|
||||
let expected_replacement_data = InitialReplacementData {
|
||||
initial_dealers: all_details.iter().map(|d| d.address.clone()).collect(),
|
||||
initial_height: 12345,
|
||||
};
|
||||
assert_eq!(replacement_data, expected_replacement_data);
|
||||
|
||||
let all_details: [_; 4] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 2));
|
||||
for details in all_details.iter() {
|
||||
past_dealers()
|
||||
.remove(deps.as_mut().storage, &details.address)
|
||||
.unwrap();
|
||||
current_dealers()
|
||||
.save(deps.as_mut().storage, &details.address, details)
|
||||
.unwrap();
|
||||
}
|
||||
for times in [
|
||||
epoch.time_configuration.public_key_submission_time_secs,
|
||||
epoch.time_configuration.dealing_exchange_time_secs,
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_submission_time_secs,
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_validation_time_secs,
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_finalization_time_secs,
|
||||
] {
|
||||
env.block.time = env.block.time.plus_seconds(times);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
}
|
||||
|
||||
let all_shares: [_; 4] = std::array::from_fn(|i| {
|
||||
let mut share = vk_share_fixture(&format!("owner{}", i + 1), 1);
|
||||
share.verified = i % 2 == 0;
|
||||
share
|
||||
});
|
||||
for share in all_shares.iter() {
|
||||
vk_shares()
|
||||
.save(deps.as_mut().storage, (&share.owner, 0), share)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// Group changed even more, surpassing threshold, so re-run dkg in reset mode
|
||||
*GROUP_MEMBERS.lock().unwrap().last_mut().unwrap() = (
|
||||
Member {
|
||||
addr: "owner6".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
);
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.in_progress_time_secs);
|
||||
let prev_epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let curr_epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
let expected_epoch = Epoch::new(
|
||||
EpochState::PublicKeySubmission { resharing: true },
|
||||
prev_epoch.epoch_id + 1,
|
||||
prev_epoch.time_configuration,
|
||||
env.block.time,
|
||||
);
|
||||
assert_eq!(curr_epoch, expected_epoch);
|
||||
assert!(THRESHOLD.may_load(&deps.storage).unwrap().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn surpass_threshold() {
|
||||
@@ -779,7 +366,7 @@ pub(crate) mod tests {
|
||||
time_configuration.verification_key_finalization_time_secs,
|
||||
] {
|
||||
env.block.time = env.block.time.plus_seconds(times);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
}
|
||||
let curr_epoch = CURRENT_EPOCH.load(&deps.storage).unwrap();
|
||||
assert_eq!(THRESHOLD.load(&deps.storage).unwrap(), 2);
|
||||
@@ -829,7 +416,7 @@ pub(crate) mod tests {
|
||||
let env = mock_env();
|
||||
|
||||
let initial_epoch_info = CURRENT_EPOCH.load(&deps.storage).unwrap();
|
||||
assert!(initial_epoch_info.finish_timestamp.is_none());
|
||||
assert!(initial_epoch_info.deadline.is_none());
|
||||
|
||||
// can only be executed by the admin
|
||||
let res = try_initiate_dkg(deps.as_mut(), env.clone(), mock_info("not an admin", &[]))
|
||||
@@ -856,7 +443,7 @@ pub(crate) mod tests {
|
||||
initial_epoch_info.time_configuration
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
epoch.deadline.unwrap(),
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.public_key_submission_time_secs)
|
||||
@@ -921,7 +508,7 @@ pub(crate) mod tests {
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
assert_eq!(
|
||||
THRESHOLD.may_load(deps.as_mut().storage).unwrap().unwrap(),
|
||||
67
|
||||
@@ -20,6 +20,7 @@ pub const ADMIN_ADDRESS: &str = "admin address";
|
||||
pub const GROUP_CONTRACT: &str = "group contract address";
|
||||
pub const MULTISIG_CONTRACT: &str = "multisig contract address";
|
||||
|
||||
// wtf, why is this a thing?
|
||||
pub(crate) static GROUP_MEMBERS: Mutex<Vec<(Member, u64)>> = Mutex::new(Vec::new());
|
||||
|
||||
pub fn add_fixture_dealer(deps: DepsMut<'_>) {
|
||||
|
||||
@@ -94,7 +94,7 @@ pub fn try_verify_verification_key_share(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::epoch_state::transactions::{advance_epoch_state, try_initiate_dkg};
|
||||
use crate::epoch_state::transactions::{try_advance_epoch_state, try_initiate_dkg};
|
||||
use crate::support::tests::helpers;
|
||||
use crate::support::tests::helpers::{add_fixture_dealer, ADMIN_ADDRESS, MULTISIG_CONTRACT};
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
@@ -117,12 +117,12 @@ mod tests {
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().dealing_exchange_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let dealer = Addr::unchecked("requester");
|
||||
let announce_address = String::from("localhost");
|
||||
let dealer_details = DealerDetails {
|
||||
@@ -182,12 +182,12 @@ mod tests {
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().dealing_exchange_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let ret = try_commit_verification_key_share(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
@@ -256,22 +256,22 @@ mod tests {
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().dealing_exchange_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().verification_key_submission_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().verification_key_validation_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
|
||||
let ret = try_verify_verification_key_share(deps.as_mut(), info, owner.clone(), false)
|
||||
.unwrap_err();
|
||||
@@ -304,12 +304,12 @@ mod tests {
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().dealing_exchange_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
|
||||
let dealer_details = DealerDetails {
|
||||
address: Addr::unchecked(&owner),
|
||||
@@ -331,12 +331,12 @@ mod tests {
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().verification_key_submission_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().verification_key_validation_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
|
||||
try_verify_verification_key_share(deps.as_mut(), multisig_info, owner, false).unwrap();
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::test_wrapper::TestSetup;
|
||||
use nym_coconut_dkg_common::types::EpochState;
|
||||
|
||||
#[test]
|
||||
fn adding_enough_members_causes_dkg_redoing() {
|
||||
let mut test = TestSetup::new();
|
||||
|
||||
let member1 = test.add_mock_group_member(None);
|
||||
let member2 = test.add_mock_group_member(None);
|
||||
|
||||
test.begin_dkg();
|
||||
test.full_dummy_dkg(vec![member1, member2], false);
|
||||
|
||||
let member3 = test.add_mock_group_member(None);
|
||||
let member4 = test.add_mock_group_member(None);
|
||||
let member5 = test.add_mock_group_member(None);
|
||||
|
||||
test.skip_to_dkg_state_end();
|
||||
test.unchecked_advance_dkg_epoch();
|
||||
|
||||
let epoch = test.epoch();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::test_wrapper::TestSetup;
|
||||
use nym_coconut_dkg_common::types::EpochState;
|
||||
|
||||
#[test]
|
||||
fn removing_enough_members_causes_dkg_reset() {
|
||||
let mut test = TestSetup::new();
|
||||
|
||||
let member1 = test.add_mock_group_member(None);
|
||||
let member2 = test.add_mock_group_member(None);
|
||||
let member3 = test.add_mock_group_member(None);
|
||||
let member4 = test.add_mock_group_member(None);
|
||||
let member5 = test.add_mock_group_member(None);
|
||||
|
||||
test.begin_dkg();
|
||||
test.full_dummy_dkg(
|
||||
vec![
|
||||
member1,
|
||||
member2,
|
||||
member3.clone(),
|
||||
member4.clone(),
|
||||
member5.clone(),
|
||||
],
|
||||
false,
|
||||
);
|
||||
|
||||
test.remove_group_member(&member3);
|
||||
test.remove_group_member(&member4);
|
||||
test.remove_group_member(&member5);
|
||||
|
||||
test.skip_to_dkg_state_end();
|
||||
test.unchecked_advance_dkg_epoch();
|
||||
|
||||
let epoch = test.epoch();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adding_enough_members_causes_dkg_reset() {
|
||||
let mut test = TestSetup::new();
|
||||
|
||||
let member1 = test.add_mock_group_member(None);
|
||||
let member2 = test.add_mock_group_member(None);
|
||||
|
||||
test.begin_dkg();
|
||||
test.full_dummy_dkg(vec![member1, member2], false);
|
||||
|
||||
let _member3 = test.add_mock_group_member(None);
|
||||
let _member4 = test.add_mock_group_member(None);
|
||||
let _member5 = test.add_mock_group_member(None);
|
||||
|
||||
test.skip_to_dkg_state_end();
|
||||
test.unchecked_advance_dkg_epoch();
|
||||
|
||||
let epoch = test.epoch();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
);
|
||||
}
|
||||
@@ -207,6 +207,20 @@ impl TestSetup {
|
||||
self.global_admin.clone()
|
||||
}
|
||||
|
||||
pub fn remove_group_member(&mut self, addr: &Addr) {
|
||||
self.app
|
||||
.execute_contract(
|
||||
self.admin(),
|
||||
self.group_contract.addr(),
|
||||
&nym_group_contract_common::msg::ExecuteMsg::UpdateMembers {
|
||||
remove: vec![addr.to_string()],
|
||||
add: vec![],
|
||||
},
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn add_mock_group_member(&mut self, weight: Option<u64>) -> Addr {
|
||||
let member_addr = self.random_address();
|
||||
let weight = weight.unwrap_or(10);
|
||||
@@ -245,7 +259,7 @@ impl TestSetup {
|
||||
|
||||
self.app.update_block(|block| {
|
||||
block.height += 42;
|
||||
if let Some(finish_timestamp) = epoch.finish_timestamp {
|
||||
if let Some(finish_timestamp) = epoch.deadline {
|
||||
block.time = finish_timestamp.plus_seconds(BLOCK_TIME_SECS);
|
||||
} else {
|
||||
block.time = block.time.plus_seconds(BLOCK_TIME_SECS)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
mod deposit_and_release;
|
||||
mod helpers;
|
||||
mod redoing_upon_threshold;
|
||||
mod reset_upon_threshold;
|
||||
mod spend_credential_creates_proposal;
|
||||
mod submit_vk_creates_proposal;
|
||||
mod test_wrapper;
|
||||
|
||||
@@ -43,7 +43,7 @@ impl CachedEpoch {
|
||||
async fn update(&mut self, epoch: Epoch) -> Result<()> {
|
||||
let now = OffsetDateTime::now_utc();
|
||||
|
||||
let validity_duration = if let Some(epoch_finish) = epoch.finish_timestamp {
|
||||
let validity_duration = if let Some(epoch_finish) = epoch.deadline {
|
||||
let state_end =
|
||||
OffsetDateTime::from_unix_timestamp(epoch_finish.seconds() as i64).unwrap();
|
||||
let until_epoch_state_end = state_end - now;
|
||||
|
||||
@@ -236,7 +236,7 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
|
||||
|
||||
// add a bit of variance so that all apis wouldn't attempt to trigger it at the same time
|
||||
let variance = self.rng.gen_range(0..=60);
|
||||
if let Some(epoch_finish) = epoch.finish_timestamp {
|
||||
if let Some(epoch_finish) = epoch.deadline {
|
||||
let now = OffsetDateTime::now_utc();
|
||||
if now.unix_timestamp() > epoch_finish.seconds() as i64 + variance {
|
||||
// TODO: make sure to not overload validator in case its running slow
|
||||
|
||||
Reference in New Issue
Block a user