Compare commits

...

4 Commits

Author SHA1 Message Date
Bogdan-Ștefan Neacșu 59455d3f13 API auto-advance epoch even on corrupt states 2023-02-27 16:40:41 +02:00
Bogdan-Ștefan Neacșu 329346d952 Fix various dkg logs 2023-02-27 16:21:40 +02:00
Bogdan-Ștefan Neacșu 9495d9821b Compare verified vks against current group instead of initial dealers 2023-02-27 14:01:14 +02:00
Bogdan-Ștefan Neacșu 2b8f49f918 Extend public key submission in case no dealer registered 2023-02-24 17:24:29 +02:00
8 changed files with 134 additions and 71 deletions
@@ -80,7 +80,7 @@ pub(crate) mod tests {
use crate::epoch_state::transactions::advance_epoch_state; use crate::epoch_state::transactions::advance_epoch_state;
use crate::support::tests::fixtures::dealer_details_fixture; use crate::support::tests::fixtures::dealer_details_fixture;
use crate::support::tests::helpers; use crate::support::tests::helpers;
use crate::support::tests::helpers::GROUP_MEMBERS; use crate::support::tests::helpers::{add_fixture_dealer, GROUP_MEMBERS};
use coconut_dkg_common::types::{InitialReplacementData, TimeConfiguration}; use coconut_dkg_common::types::{InitialReplacementData, TimeConfiguration};
use cosmwasm_std::testing::{mock_env, mock_info}; use cosmwasm_std::testing::{mock_env, mock_info};
use cw4::Member; use cw4::Member;
@@ -147,6 +147,8 @@ pub(crate) mod tests {
.block .block
.time .time
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs); .plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
add_fixture_dealer(deps.as_mut());
advance_epoch_state(deps.as_mut(), env).unwrap(); advance_epoch_state(deps.as_mut(), env).unwrap();
let ret = try_add_dealer( let ret = try_add_dealer(
@@ -53,6 +53,7 @@ pub(crate) mod tests {
use crate::epoch_state::transactions::advance_epoch_state; use crate::epoch_state::transactions::advance_epoch_state;
use crate::support::tests::fixtures::{dealer_details_fixture, dealing_bytes_fixture}; use crate::support::tests::fixtures::{dealer_details_fixture, dealing_bytes_fixture};
use crate::support::tests::helpers; use crate::support::tests::helpers;
use crate::support::tests::helpers::add_fixture_dealer;
use coconut_dkg_common::dealer::DealerDetails; use coconut_dkg_common::dealer::DealerDetails;
use coconut_dkg_common::types::{InitialReplacementData, TimeConfiguration}; use coconut_dkg_common::types::{InitialReplacementData, TimeConfiguration};
use cosmwasm_std::testing::{mock_env, mock_info}; use cosmwasm_std::testing::{mock_env, mock_info};
@@ -80,6 +81,7 @@ pub(crate) mod tests {
.block .block
.time .time
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs); .plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
add_fixture_dealer(deps.as_mut());
advance_epoch_state(deps.as_mut(), env).unwrap(); advance_epoch_state(deps.as_mut(), env).unwrap();
let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone(), false) let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone(), false)
@@ -7,6 +7,7 @@ use crate::epoch_state::storage::{CURRENT_EPOCH, INITIAL_REPLACEMENT_DATA, THRES
use crate::epoch_state::utils::check_epoch_state; use crate::epoch_state::utils::check_epoch_state;
use crate::error::ContractError; use crate::error::ContractError;
use crate::state::STATE; use crate::state::STATE;
use crate::verification_key_shares::storage::vk_shares;
use coconut_dkg_common::types::{Epoch, EpochState, InitialReplacementData}; use coconut_dkg_common::types::{Epoch, EpochState, InitialReplacementData};
use cosmwasm_std::{Addr, Deps, DepsMut, Env, Order, Response, Storage}; use cosmwasm_std::{Addr, Deps, DepsMut, Env, Order, Response, Storage};
@@ -89,23 +90,29 @@ pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Respons
let current_epoch = CURRENT_EPOCH.load(deps.storage)?; let current_epoch = CURRENT_EPOCH.load(deps.storage)?;
let next_epoch = if let Some(state) = current_epoch.state.next() { let next_epoch = if let Some(state) = current_epoch.state.next() {
// We are during DKG process // We are during DKG process
let mut new_state = state;
if let EpochState::DealingExchange { resharing } = state { if let EpochState::DealingExchange { resharing } = state {
let current_dealers = current_dealers() let current_dealers = current_dealers()
.keys(deps.storage, None, None, Order::Ascending) .keys(deps.storage, None, None, Order::Ascending)
.collect::<Result<Vec<Addr>, _>>()?; .collect::<Result<Vec<Addr>, _>>()?;
// note: ceiling in integer division can be achieved via q = (x + y - 1) / y; if current_dealers.is_empty() {
let threshold = (2 * current_dealers.len() as u64 + 3 - 1) / 3; // If no dealer registered yet, we just stay in the same state until there's at least one
THRESHOLD.save(deps.storage, &threshold)?; new_state = current_epoch.state;
if !resharing { } else {
let replacement_data = InitialReplacementData { // note: ceiling in integer division can be achieved via q = (x + y - 1) / y;
initial_dealers: current_dealers, let threshold = (2 * current_dealers.len() as u64 + 3 - 1) / 3;
initial_height: None, THRESHOLD.save(deps.storage, &threshold)?;
}; if !resharing {
INITIAL_REPLACEMENT_DATA.save(deps.storage, &replacement_data)?; let replacement_data = InitialReplacementData {
initial_dealers: current_dealers,
initial_height: None,
};
INITIAL_REPLACEMENT_DATA.save(deps.storage, &replacement_data)?;
}
} }
} };
Epoch::new( Epoch::new(
state, new_state,
current_epoch.epoch_id, current_epoch.epoch_id,
current_epoch.time_configuration, current_epoch.time_configuration,
env.block.time, env.block.time,
@@ -152,9 +159,16 @@ pub(crate) fn try_surpassed_threshold(
check_epoch_state(deps.storage, EpochState::InProgress)?; check_epoch_state(deps.storage, EpochState::InProgress)?;
let threshold = THRESHOLD.load(deps.storage)?; let threshold = THRESHOLD.load(deps.storage)?;
let dealers = current_dealers() let dealers = vk_shares()
.keys(deps.storage, None, None, Order::Ascending) .range(deps.storage, None, None, Order::Ascending)
.flatten(); .flatten()
.filter_map(|(_, share)| {
if share.verified {
Some(share.owner)
} else {
None
}
});
if dealers_still_active(&deps.as_ref(), dealers)? < threshold as usize { if dealers_still_active(&deps.as_ref(), dealers)? < threshold as usize {
reset_epoch_state(deps.storage)?; reset_epoch_state(deps.storage)?;
CURRENT_EPOCH.update::<_, ContractError>(deps.storage, |epoch| { CURRENT_EPOCH.update::<_, ContractError>(deps.storage, |epoch| {
@@ -174,7 +188,7 @@ pub(crate) fn try_surpassed_threshold(
pub(crate) mod tests { pub(crate) mod tests {
use super::*; use super::*;
use crate::error::ContractError::EarlyEpochStateAdvancement; use crate::error::ContractError::EarlyEpochStateAdvancement;
use crate::support::tests::fixtures::dealer_details_fixture; use crate::support::tests::fixtures::{dealer_details_fixture, vk_share_fixture};
use crate::support::tests::helpers::{init_contract, GROUP_MEMBERS}; use crate::support::tests::helpers::{init_contract, GROUP_MEMBERS};
use coconut_dkg_common::types::{ use coconut_dkg_common::types::{
ContractSafeBytes, DealerDetails, EpochState, TimeConfiguration, ContractSafeBytes, DealerDetails, EpochState, TimeConfiguration,
@@ -392,6 +406,14 @@ pub(crate) mod tests {
EarlyEpochStateAdvancement(1) 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 // setup dealer details
let all_details: [_; 4] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 1)); let all_details: [_; 4] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 1));
for details in all_details.iter() { for details in all_details.iter() {
@@ -404,7 +426,7 @@ pub(crate) mod tests {
.may_load(&deps.storage) .may_load(&deps.storage)
.unwrap() .unwrap()
.is_none()); .is_none());
env.block.time = env.block.time.plus_seconds(1); 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(); advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap(); let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
assert_eq!( assert_eq!(
@@ -664,6 +686,12 @@ pub(crate) mod tests {
.save(deps.as_mut().storage, &details.address, details) .save(deps.as_mut().storage, &details.address, details)
.unwrap(); .unwrap();
} }
let all_shares: [_; 3] = std::array::from_fn(|i| vk_share_fixture(&format!("owner{}", i + 1), 0));
for share in all_shares.iter() {
vk_shares()
.save(deps.as_mut().storage, (&share.owner, share.epoch_id), share)
.unwrap();
}
for times in [ for times in [
time_configuration.public_key_submission_time_secs, time_configuration.public_key_submission_time_secs,
@@ -2,11 +2,13 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::contract::instantiate; use crate::contract::instantiate;
use crate::dealers::storage::current_dealers;
use coconut_dkg_common::msg::InstantiateMsg; use coconut_dkg_common::msg::InstantiateMsg;
use coconut_dkg_common::types::DealerDetails;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier}; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier};
use cosmwasm_std::{ use cosmwasm_std::{
from_binary, to_binary, ContractResult, Empty, MemoryStorage, OwnedDeps, QuerierResult, from_binary, to_binary, Addr, ContractResult, DepsMut, Empty, MemoryStorage, OwnedDeps,
SystemResult, WasmQuery, QuerierResult, SystemResult, WasmQuery,
}; };
use cw4::{Cw4QueryMsg, Member, MemberListResponse, MemberResponse}; use cw4::{Cw4QueryMsg, Member, MemberListResponse, MemberResponse};
use lazy_static::lazy_static; use lazy_static::lazy_static;
@@ -22,6 +24,22 @@ lazy_static! {
pub static ref GROUP_MEMBERS: Mutex<Vec<(Member, u64)>> = Mutex::new(vec![]); pub static ref GROUP_MEMBERS: Mutex<Vec<(Member, u64)>> = Mutex::new(vec![]);
} }
pub fn add_fixture_dealer(deps: DepsMut<'_>) {
let owner = Addr::unchecked("owner");
current_dealers()
.save(
deps.storage,
&owner,
&DealerDetails {
address: owner.clone(),
bte_public_key_with_proof: String::new(),
announce_address: String::new(),
assigned_index: 100,
},
)
.unwrap();
}
fn querier_handler(query: &WasmQuery) -> QuerierResult { fn querier_handler(query: &WasmQuery) -> QuerierResult {
let bin = match query { let bin = match query {
WasmQuery::Smart { contract_addr, msg } => { WasmQuery::Smart { contract_addr, msg } => {
@@ -91,7 +91,7 @@ mod tests {
use super::*; use super::*;
use crate::epoch_state::transactions::advance_epoch_state; use crate::epoch_state::transactions::advance_epoch_state;
use crate::support::tests::helpers; use crate::support::tests::helpers;
use crate::support::tests::helpers::MULTISIG_CONTRACT; use crate::support::tests::helpers::{add_fixture_dealer, MULTISIG_CONTRACT};
use coconut_dkg_common::dealer::DealerDetails; use coconut_dkg_common::dealer::DealerDetails;
use coconut_dkg_common::types::{EpochState, TimeConfiguration}; use coconut_dkg_common::types::{EpochState, TimeConfiguration};
use cosmwasm_std::testing::{mock_env, mock_info}; use cosmwasm_std::testing::{mock_env, mock_info};
@@ -104,6 +104,7 @@ mod tests {
let info = mock_info("requester", &[]); let info = mock_info("requester", &[]);
let share = "share".to_string(); let share = "share".to_string();
add_fixture_dealer(deps.as_mut());
env.block.time = env env.block.time = env
.block .block
.time .time
@@ -171,6 +172,7 @@ mod tests {
.to_string() .to_string()
} }
); );
add_fixture_dealer(deps.as_mut());
env.block.time = env env.block.time = env
.block .block
.time .time
@@ -247,6 +249,7 @@ mod tests {
} }
); );
add_fixture_dealer(deps.as_mut());
env.block.time = env env.block.time = env
.block .block
.time .time
@@ -292,6 +295,7 @@ mod tests {
let share = "share".to_string(); let share = "share".to_string();
let multisig_info = mock_info(MULTISIG_CONTRACT, &[]); let multisig_info = mock_info(MULTISIG_CONTRACT, &[]);
add_fixture_dealer(deps.as_mut());
env.block.time = env env.block.time = env
.block .block
.time .time
+58 -49
View File
@@ -99,57 +99,66 @@ impl<R: RngCore + Clone> DkgController<R> {
return; return;
} }
if let Err(err) = self.state.is_consistent(epoch.state).await { if let Err(err) = self.state.is_consistent(epoch.state).await {
error!("Epoch state is corrupted - {err}, the process should be terminated"); debug!("Epoch state is corrupted - {err}. Awaiting for a DKG restart.");
return; } else {
} let ret = match epoch.state {
let ret = match epoch.state { EpochState::PublicKeySubmission { resharing } => {
EpochState::PublicKeySubmission { resharing } => { public_key_submission(&self.dkg_client, &mut self.state, resharing)
public_key_submission(&self.dkg_client, &mut self.state, resharing).await .await
} }
EpochState::DealingExchange { resharing } => { EpochState::DealingExchange { resharing } => {
dealing_exchange( dealing_exchange(
&self.dkg_client, &self.dkg_client,
&mut self.state, &mut self.state,
self.rng.clone(), self.rng.clone(),
resharing, resharing,
) )
.await
}
EpochState::VerificationKeySubmission { resharing } => {
let keypair_path = nym_pemstore::KeyPairPath::new(
self.secret_key_path.clone(),
self.verification_key_path.clone(),
);
verification_key_submission(
&self.dkg_client,
&mut self.state,
&keypair_path,
resharing,
)
.await
}
EpochState::VerificationKeyValidation { resharing } => {
verification_key_validation(&self.dkg_client, &mut self.state, resharing)
.await .await
} }
EpochState::VerificationKeyFinalization { resharing } => { EpochState::VerificationKeySubmission { resharing } => {
verification_key_finalization(&self.dkg_client, &mut self.state, resharing) let keypair_path = nym_pemstore::KeyPairPath::new(
self.secret_key_path.clone(),
self.verification_key_path.clone(),
);
verification_key_submission(
&self.dkg_client,
&mut self.state,
&keypair_path,
resharing,
)
.await .await
} }
// Just wait, in case we need to redo dkg at some point EpochState::VerificationKeyValidation { resharing } => {
EpochState::InProgress => { verification_key_validation(
self.state.set_was_in_progress(); &self.dkg_client,
Ok(()) &mut self.state,
} resharing,
}; )
if let Err(err) = ret { .await
warn!("Could not handle this iteration for the epoch state: {err}"); }
} else if epoch.state != EpochState::InProgress { EpochState::VerificationKeyFinalization { resharing } => {
let persistent_state = PersistentState::from(&self.state); verification_key_finalization(
if let Err(err) = &self.dkg_client,
persistent_state.save_to_file(self.state.persistent_state_path()) &mut self.state,
{ resharing,
warn!("Could not backup the state for this iteration: {err}"); )
.await
}
// Just wait, in case we need to redo dkg at some point
EpochState::InProgress => {
self.state.set_was_in_progress();
Ok(())
}
};
if let Err(err) = ret {
warn!("Could not handle this iteration for the epoch state: {err}");
} else if epoch.state != EpochState::InProgress {
let persistent_state = PersistentState::from(&self.state);
if let Err(err) =
persistent_state.save_to_file(self.state.persistent_state_path())
{
warn!("Could not backup the state for this iteration: {err}");
}
} }
} }
if let Ok(current_timestamp) = if let Ok(current_timestamp) =
+1 -1
View File
@@ -133,7 +133,7 @@ impl ConsistentState for State {
fn proposal_id_value(&self) -> Result<u64, CoconutError> { fn proposal_id_value(&self) -> Result<u64, CoconutError> {
self.proposal_id.ok_or(CoconutError::UnrecoverableState { self.proposal_id.ok_or(CoconutError::UnrecoverableState {
reason: String::from("Proposal id should have benn set"), reason: String::from("Proposal id should have been set"),
}) })
} }
} }
+1 -1
View File
@@ -91,7 +91,7 @@ pub enum CoconutError {
#[error("Failed to recover assigned node index: {reason}")] #[error("Failed to recover assigned node index: {reason}")]
NodeIndexRecoveryError { reason: String }, NodeIndexRecoveryError { reason: String },
#[error("Unrecoverable state: {reason}. Process should be restarted")] #[error("Unrecoverable state: {reason}")]
UnrecoverableState { reason: String }, UnrecoverableState { reason: String },
#[error("DKG has not finished yet in order to derive the coconut key")] #[error("DKG has not finished yet in order to derive the coconut key")]