[wip]: improving error recovery during key submission phase
This commit is contained in:
@@ -34,6 +34,8 @@ fn verify_dealer(deps: DepsMut<'_>, dealer: &Addr, resharing: bool) -> Result<()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// future optimisation:
|
||||
// for a recurring dealer just let it refresh the keys without having to do all the storage operations
|
||||
pub fn try_add_dealer(
|
||||
mut deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::support::config;
|
||||
use anyhow::Context;
|
||||
use anyhow::{anyhow, bail, Context};
|
||||
use nym_coconut_dkg_common::types::EpochId;
|
||||
use nym_dkg::bte::keys::KeyPair as DkgKeyPair;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use thiserror::__private::AsDisplay;
|
||||
|
||||
pub(crate) fn init_bte_keypair<R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
@@ -18,8 +20,8 @@ pub(crate) fn init_bte_keypair<R: RngCore + CryptoRng>(
|
||||
&config.storage_paths.decryption_key_path,
|
||||
&config.storage_paths.public_key_with_proof_path,
|
||||
),
|
||||
)?;
|
||||
Ok(())
|
||||
)
|
||||
.context("DKG BTE keypair store failure")
|
||||
}
|
||||
|
||||
pub(crate) fn load_bte_keypair(config: &config::CoconutSigner) -> anyhow::Result<DkgKeyPair> {
|
||||
@@ -43,3 +45,50 @@ pub(crate) fn load_coconut_keypair_if_exists(
|
||||
.context("coconut keypair load failure")
|
||||
.map(Some)
|
||||
}
|
||||
|
||||
pub(crate) fn persist_coconut_keypair(
|
||||
keys: &nym_coconut_interface::KeyPair,
|
||||
store_paths: &nym_pemstore::KeyPairPath,
|
||||
) -> anyhow::Result<()> {
|
||||
nym_pemstore::store_keypair(keys, store_paths).context("coconut keypair store failure")
|
||||
}
|
||||
|
||||
pub(crate) fn archive_coconut_keypair(
|
||||
store_paths: &nym_pemstore::KeyPairPath,
|
||||
epoch_id: EpochId,
|
||||
) -> anyhow::Result<()> {
|
||||
if !store_paths.private_key_path.exists() {
|
||||
bail!(
|
||||
"private key does not exist at {}",
|
||||
store_paths.private_key_path.as_display()
|
||||
)
|
||||
}
|
||||
|
||||
let dir = store_paths
|
||||
.private_key_path
|
||||
.parent()
|
||||
.ok_or(anyhow!("the private key does not have a valid parent"))?;
|
||||
let filename = store_paths
|
||||
.private_key_path
|
||||
.file_name()
|
||||
.ok_or(anyhow!("the private key does not have a filename"))?
|
||||
.to_str()
|
||||
.ok_or(anyhow!("the key filename is not valid UTF8"))?;
|
||||
let archive_path = dir.join(format!("epoch-{epoch_id}-{filename}.archived"));
|
||||
std::fs::rename(&store_paths.private_key_path, archive_path)?;
|
||||
|
||||
let dir = store_paths
|
||||
.public_key_path
|
||||
.parent()
|
||||
.ok_or(anyhow!("the public key does not have a valid parent"))?;
|
||||
let filename = store_paths
|
||||
.public_key_path
|
||||
.file_name()
|
||||
.ok_or(anyhow!("the public key does not have a filename"))?
|
||||
.to_str()
|
||||
.ok_or(anyhow!("the key filename is not valid UTF8"))?;
|
||||
let archive_path = dir.join(format!("epoch-{epoch_id}-{filename}.archived"));
|
||||
std::fs::rename(&store_paths.public_key_path, archive_path)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -8,8 +8,7 @@ use crate::coconut::dkg::verification_key::{
|
||||
verification_key_finalization, verification_key_validation,
|
||||
};
|
||||
use crate::coconut::dkg::{
|
||||
dealing::dealing_exchange, public_key::public_key_submission,
|
||||
verification_key::verification_key_submission,
|
||||
dealing::dealing_exchange, verification_key::verification_key_submission,
|
||||
};
|
||||
use crate::coconut::keypair::KeyPair as CoconutKeyPair;
|
||||
use crate::nyxd;
|
||||
@@ -19,6 +18,7 @@ use nym_coconut_dkg_common::types::{Epoch, EpochId, EpochState};
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_dkg::bte::keys::KeyPair as DkgKeyPair;
|
||||
use nym_task::{TaskClient, TaskManager};
|
||||
use rand::rngs::OsRng;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
@@ -28,11 +28,11 @@ use tokio::time::interval;
|
||||
mod error;
|
||||
pub(crate) mod keys;
|
||||
|
||||
pub(crate) struct DkgController<R> {
|
||||
dkg_client: DkgClient,
|
||||
pub(crate) struct DkgController<R = OsRng> {
|
||||
pub(crate) dkg_client: DkgClient,
|
||||
secret_key_path: PathBuf,
|
||||
verification_key_path: PathBuf,
|
||||
state: State,
|
||||
pub(crate) state: State,
|
||||
rng: R,
|
||||
polling_rate: Duration,
|
||||
}
|
||||
@@ -71,6 +71,10 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn coconut_keypaths(&self) -> nym_pemstore::KeyPairPath {
|
||||
nym_pemstore::KeyPairPath::new(&self.secret_key_path, &self.verification_key_path)
|
||||
}
|
||||
|
||||
fn persist_state(&self) -> Result<(), DkgError> {
|
||||
// if !self.state.coconut_keypair_is_some().await {
|
||||
// // Delete the files just in case the process is killed before the new keys are generated
|
||||
@@ -124,13 +128,17 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
|
||||
}
|
||||
|
||||
async fn handle_awaiting_initialisation(&mut self) -> Result<(), DkgError> {
|
||||
info!("DKG hasn't been initialised yet");
|
||||
return Ok(());
|
||||
info!("DKG hasn't been initialised yet - nothing to do");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_key_submission(&mut self, resharing: bool) -> Result<(), DkgError> {
|
||||
async fn handle_key_submission(
|
||||
&mut self,
|
||||
epoch_id: EpochId,
|
||||
resharing: bool,
|
||||
) -> Result<(), DkgError> {
|
||||
debug!("DKG: public key submission (resharing: {resharing})");
|
||||
public_key_submission(&self.dkg_client, &mut self.state, resharing)
|
||||
self.public_key_submission(epoch_id, resharing)
|
||||
.await
|
||||
.map_err(|source| DkgError::PublicKeySubmissionFailure { source })
|
||||
}
|
||||
@@ -152,12 +160,10 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
|
||||
epoch_id: EpochId,
|
||||
resharing: bool,
|
||||
) -> Result<(), DkgError> {
|
||||
debug!("DKG: verification key sumbission (resharing: {resharing})");
|
||||
debug!("DKG: verification key submission (resharing: {resharing})");
|
||||
|
||||
let keypair_path = nym_pemstore::KeyPairPath::new(
|
||||
self.secret_key_path.clone(),
|
||||
self.verification_key_path.clone(),
|
||||
);
|
||||
let keypair_path =
|
||||
nym_pemstore::KeyPairPath::new(&self.secret_key_path, &self.verification_key_path);
|
||||
verification_key_submission(
|
||||
&self.dkg_client,
|
||||
&mut self.state,
|
||||
@@ -219,7 +225,8 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
|
||||
match epoch.state {
|
||||
EpochState::WaitingInitialisation => self.handle_awaiting_initialisation().await?,
|
||||
EpochState::PublicKeySubmission { resharing } => {
|
||||
self.handle_key_submission(resharing).await?
|
||||
self.handle_key_submission(epoch.epoch_id, resharing)
|
||||
.await?
|
||||
}
|
||||
EpochState::DealingExchange { resharing } => {
|
||||
self.handle_dealing_exchange(resharing).await?
|
||||
@@ -294,3 +301,17 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl DkgController {
|
||||
pub(crate) fn test_mock(dkg_client: DkgClient, state: State) -> DkgController {
|
||||
DkgController {
|
||||
dkg_client,
|
||||
secret_key_path: Default::default(),
|
||||
verification_key_path: Default::default(),
|
||||
state,
|
||||
rng: OsRng,
|
||||
polling_rate: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,18 +47,19 @@ pub(crate) async fn dealing_exchange(
|
||||
let prior_resharing_secrets = if let Some(mut keypair) = state.take_coconut_keypair().await {
|
||||
// Double check that we are in resharing mode
|
||||
if resharing {
|
||||
let sk = keypair.secret_key();
|
||||
if sk.size() + 1 != expected_key_size as usize {
|
||||
return Err(CoconutError::CorruptedCoconutKeyPair);
|
||||
}
|
||||
|
||||
let (x, mut scalars) = sk.into_raw();
|
||||
|
||||
// We can now erase the keypair from memory
|
||||
debug!("Removing coconut keypair from memory");
|
||||
keypair.zeroize();
|
||||
scalars.push(x);
|
||||
scalars
|
||||
todo!()
|
||||
// let sk = keypair.secret_key();
|
||||
// if sk.size() + 1 != expected_key_size as usize {
|
||||
// return Err(CoconutError::CorruptedCoconutKeyPair);
|
||||
// }
|
||||
//
|
||||
// let (x, mut scalars) = sk.into_raw();
|
||||
//
|
||||
// // We can now erase the keypair from memory
|
||||
// debug!("Removing coconut keypair from memory");
|
||||
// keypair.zeroize();
|
||||
// scalars.push(x);
|
||||
// scalars
|
||||
} else {
|
||||
log::warn!("Coconut key hasn't been reset in memory. The state might be corrupt");
|
||||
vec![]
|
||||
@@ -340,7 +341,7 @@ pub(crate) mod tests {
|
||||
let params = dkg::params();
|
||||
let mut keys = ttp_keygen(&Parameters::new(4).unwrap(), 3, 4).unwrap();
|
||||
let coconut_keypair = KeyPair::new();
|
||||
coconut_keypair.set(Some(keys.pop().unwrap())).await;
|
||||
coconut_keypair.set(0, keys.pop().unwrap()).await;
|
||||
let identity_keypair = identity::KeyPair::new(&mut thread_rng());
|
||||
|
||||
let mut state = State::new(
|
||||
|
||||
@@ -1,74 +1,73 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::coconut::dkg::client::DkgClient;
|
||||
use crate::coconut::dkg::state::State;
|
||||
use crate::coconut::dkg::controller::DkgController;
|
||||
use crate::coconut::error::CoconutError;
|
||||
use log::debug;
|
||||
use nym_coconut_dkg_common::dealer::DealerType;
|
||||
use nym_coconut_dkg_common::types::EpochId;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
pub(crate) async fn public_key_submission(
|
||||
dkg_client: &DkgClient,
|
||||
state: &mut State,
|
||||
resharing: bool,
|
||||
) -> Result<(), CoconutError> {
|
||||
if state.was_in_progress() {
|
||||
let own_address = dkg_client.get_address().await.as_ref().to_string();
|
||||
let is_initial_dealer = dkg_client
|
||||
.get_initial_dealers()
|
||||
.await?
|
||||
.map(|data| data.initial_dealers.iter().any(|d| *d == own_address))
|
||||
.unwrap_or(false);
|
||||
let reset_coconut_keypair = !resharing || !is_initial_dealer;
|
||||
debug!(
|
||||
"Resetting state, with coconut keypair reset: {}",
|
||||
reset_coconut_keypair
|
||||
);
|
||||
state.reset_persistent(reset_coconut_keypair).await;
|
||||
}
|
||||
if state.node_index().is_some() {
|
||||
debug!("Node index was set previously, nothing to do");
|
||||
return Ok(());
|
||||
}
|
||||
impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
|
||||
pub(crate) async fn public_key_submission(
|
||||
&mut self,
|
||||
epoch_id: EpochId,
|
||||
resharing: bool,
|
||||
) -> Result<(), CoconutError> {
|
||||
self.state.init_dkg_state(epoch_id);
|
||||
let registration_state = self.state.registration_state(epoch_id);
|
||||
|
||||
let bte_key = bs58::encode(&state.dkg_keypair().public_key().to_bytes()).into_string();
|
||||
let dealer_details = dkg_client.get_self_registered_dealer_details().await?;
|
||||
let index = if let Some(details) = dealer_details.details {
|
||||
if dealer_details.dealer_type == DealerType::Past {
|
||||
// If it was a dealer in a previous epoch, re-register it for this epoch
|
||||
debug!("Registering for the current DKG round, with keys from a previous epoch");
|
||||
dkg_client
|
||||
.register_dealer(
|
||||
bte_key,
|
||||
state.identity_key().to_base58_string(),
|
||||
state.announce_address().to_string(),
|
||||
resharing,
|
||||
)
|
||||
.await?;
|
||||
// check if we have already submitted the key
|
||||
if registration_state.completed() {
|
||||
// the only way this could be a false positive is if the chain forked and blocks got reverted,
|
||||
// but I don't think we have to worry about that
|
||||
debug!("we have already submitted the keys for this epoch");
|
||||
return Ok(());
|
||||
}
|
||||
details.assigned_index
|
||||
} else {
|
||||
debug!("Registering for the first time to be a dealer");
|
||||
// First time registration
|
||||
dkg_client
|
||||
.register_dealer(
|
||||
bte_key,
|
||||
state.identity_key().to_base58_string(),
|
||||
state.announce_address().to_string(),
|
||||
resharing,
|
||||
)
|
||||
.await?
|
||||
};
|
||||
state.set_node_index(Some(index));
|
||||
info!("DKG: Using node index {}", index);
|
||||
|
||||
Ok(())
|
||||
// if we have coconut keys available, it means we have already completed the DKG before (in previous epoch)
|
||||
// in which case, archive and reset those keys
|
||||
if let Some((old_epoch, _)) = self.state.take_coconut_keypair().await {
|
||||
debug!("resetting and archiving old coconut keypair");
|
||||
let store_path = self.coconut_keypaths();
|
||||
// archive_coconut_keypair(&store_path, old_epoch)?
|
||||
//
|
||||
}
|
||||
|
||||
// FAILURE CASE:
|
||||
// check if we have already sent the registration transaction, but it timed out or got stuck in the mempool and
|
||||
// eventually got executed without us knowing about it
|
||||
// in that case we MUST recover the assigned index since we won't be allowed to register again
|
||||
let dealer_details = self.dkg_client.get_self_registered_dealer_details().await?;
|
||||
if dealer_details.dealer_type.is_current() {
|
||||
if let Some(details) = dealer_details.details {
|
||||
// the tx did actually go through
|
||||
self.state.registration_state_mut(epoch_id).assigned_index =
|
||||
Some(details.assigned_index);
|
||||
info!("DKG: recovered node index: {}", details.assigned_index);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let bte_key = bs58::encode(&self.state.dkg_keypair().public_key().to_bytes()).into_string();
|
||||
let identity_key = self.state.identity_key().to_base58_string();
|
||||
let announce_address = self.state.announce_address().to_string();
|
||||
|
||||
let assigned_index = self
|
||||
.dkg_client
|
||||
.register_dealer(bte_key, identity_key, announce_address, resharing)
|
||||
.await?;
|
||||
self.state.registration_state_mut(epoch_id).assigned_index = Some(assigned_index);
|
||||
info!("DKG: Using node index {assigned_index}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::coconut::dkg::state::PersistentState;
|
||||
use crate::coconut::dkg::client::DkgClient;
|
||||
use crate::coconut::dkg::state::{PersistentState, State};
|
||||
use crate::coconut::tests::DummyClient;
|
||||
use crate::coconut::KeyPair;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
@@ -97,30 +96,29 @@ pub(crate) mod tests {
|
||||
*identity_keypair.public_key(),
|
||||
KeyPair::new(),
|
||||
);
|
||||
let mut controller = DkgController::test_mock(dkg_client, state);
|
||||
|
||||
assert!(dkg_client
|
||||
assert!(controller
|
||||
.dkg_client
|
||||
.get_self_registered_dealer_details()
|
||||
.await
|
||||
.unwrap()
|
||||
.details
|
||||
.is_none());
|
||||
public_key_submission(&dkg_client, &mut state, false)
|
||||
.await
|
||||
.unwrap();
|
||||
let client_idx = dkg_client
|
||||
controller.public_key_submission(0, false).await.unwrap();
|
||||
let client_idx = controller
|
||||
.dkg_client
|
||||
.get_self_registered_dealer_details()
|
||||
.await
|
||||
.unwrap()
|
||||
.details
|
||||
.unwrap()
|
||||
.assigned_index;
|
||||
assert_eq!(state.node_index().unwrap(), client_idx);
|
||||
assert_eq!(controller.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, false)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(state.node_index().unwrap(), client_idx);
|
||||
controller.state.set_node_index(None);
|
||||
controller.public_key_submission(0, false).await.unwrap();
|
||||
assert_eq!(controller.state.node_index().unwrap(), client_idx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
mod registration;
|
||||
|
||||
use crate::coconut::dkg::complaints::ComplaintReason;
|
||||
use crate::coconut::dkg::state::registration::RegistrationState;
|
||||
use crate::coconut::error::CoconutError;
|
||||
use crate::coconut::keypair::KeyPair as CoconutKeyPair;
|
||||
use cosmwasm_std::Addr;
|
||||
@@ -11,6 +14,8 @@ use nym_coconut_dkg_common::types::{DealingIndex, EpochId, EpochState};
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_dkg::bte::{keys::KeyPair as DkgKeyPair, PublicKey, PublicKeyWithProof};
|
||||
use nym_dkg::{Dealing, NodeIndex, RecoveredVerificationKeys, Threshold};
|
||||
use nym_validator_client::nyxd::{tx, Hash};
|
||||
use rocket::form::validate::Contains;
|
||||
use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
@@ -271,8 +276,18 @@ impl PersistentState {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct DkgState {
|
||||
pub(crate) registration: RegistrationState,
|
||||
}
|
||||
|
||||
pub(crate) struct State {
|
||||
/// Path to the file containing the persistent state
|
||||
persistent_state_path: PathBuf,
|
||||
|
||||
dkg_instances: HashMap<EpochId, DkgState>,
|
||||
|
||||
//
|
||||
announce_address: Url,
|
||||
identity_key: identity::PublicKey,
|
||||
dkg_keypair: DkgKeyPair,
|
||||
@@ -298,28 +313,33 @@ impl State {
|
||||
identity_key: identity::PublicKey,
|
||||
coconut_keypair: CoconutKeyPair,
|
||||
) -> Self {
|
||||
State {
|
||||
persistent_state_path,
|
||||
announce_address,
|
||||
identity_key,
|
||||
dkg_keypair,
|
||||
coconut_keypair,
|
||||
node_index: persistent_state.node_index,
|
||||
dealers: persistent_state.dealers,
|
||||
generated_dealings: persistent_state.generated_dealings,
|
||||
receiver_index: persistent_state.receiver_index,
|
||||
threshold: persistent_state.threshold,
|
||||
recovered_vks: persistent_state.recovered_vks,
|
||||
proposal_id: persistent_state.proposal_id,
|
||||
voted_vks: persistent_state.voted_vks,
|
||||
executed_proposal: persistent_state.executed_proposal,
|
||||
was_in_progress: persistent_state.was_in_progress,
|
||||
}
|
||||
todo!()
|
||||
// State {
|
||||
// persistent_state_path,
|
||||
// announce_address,
|
||||
// identity_key,
|
||||
// dkg_keypair,
|
||||
// coconut_keypair,
|
||||
// node_index: persistent_state.node_index,
|
||||
// dealers: persistent_state.dealers,
|
||||
// generated_dealings: persistent_state.generated_dealings,
|
||||
// receiver_index: persistent_state.receiver_index,
|
||||
// threshold: persistent_state.threshold,
|
||||
// recovered_vks: persistent_state.recovered_vks,
|
||||
// proposal_id: persistent_state.proposal_id,
|
||||
// voted_vks: persistent_state.voted_vks,
|
||||
// executed_proposal: persistent_state.executed_proposal,
|
||||
// was_in_progress: persistent_state.was_in_progress,
|
||||
// }
|
||||
}
|
||||
|
||||
pub fn persist(&self) -> Result<(), CoconutError> {
|
||||
PersistentState::from(self).save_to_file(self.persistent_state_path())
|
||||
}
|
||||
|
||||
pub async fn reset_persistent(&mut self, reset_coconut_keypair: bool) {
|
||||
if reset_coconut_keypair {
|
||||
self.coconut_keypair.set(None).await;
|
||||
self.coconut_keypair.invalidate().await;
|
||||
}
|
||||
self.node_index = Default::default();
|
||||
self.dealers = Default::default();
|
||||
@@ -332,6 +352,26 @@ impl State {
|
||||
self.was_in_progress = Default::default();
|
||||
}
|
||||
|
||||
pub fn init_dkg_state(&mut self, epoch_id: EpochId) {
|
||||
if !self.dkg_instances.contains_key(&epoch_id) {
|
||||
self.dkg_instances.insert(epoch_id, Default::default());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn registration_state(&self, epoch_id: EpochId) -> &RegistrationState {
|
||||
// safety: before any accessors are called, `init_dkg_state` is used at the beginning of submission handler
|
||||
&self.dkg_instances[&epoch_id].registration
|
||||
}
|
||||
|
||||
pub fn registration_state_mut(&mut self, epoch_id: EpochId) -> &mut RegistrationState {
|
||||
// safety: before any accessors are called, `init_dkg_state` is used at the beginning of submission handler
|
||||
&mut self.dkg_instances.get_mut(&epoch_id).unwrap().registration
|
||||
}
|
||||
|
||||
pub fn already_registered(&self, epoch_id: EpochId) -> bool {
|
||||
self.registration_state(epoch_id).completed()
|
||||
}
|
||||
|
||||
pub fn persistent_state_path(&self) -> &Path {
|
||||
self.persistent_state_path.as_path()
|
||||
}
|
||||
@@ -352,7 +392,7 @@ impl State {
|
||||
self.coconut_keypair.get().await.is_some()
|
||||
}
|
||||
|
||||
pub async fn take_coconut_keypair(&self) -> Option<nym_coconut::KeyPair> {
|
||||
pub async fn take_coconut_keypair(&self) -> Option<(EpochId, nym_coconut::KeyPair)> {
|
||||
self.coconut_keypair.take().await
|
||||
}
|
||||
|
||||
@@ -438,9 +478,10 @@ impl State {
|
||||
|
||||
pub async fn set_coconut_keypair(
|
||||
&mut self,
|
||||
coconut_keypair: Option<nym_coconut_interface::KeyPair>,
|
||||
epoch_id: EpochId,
|
||||
coconut_keypair: nym_coconut_interface::KeyPair,
|
||||
) {
|
||||
self.coconut_keypair.set(coconut_keypair).await
|
||||
self.coconut_keypair.set(epoch_id, coconut_keypair).await
|
||||
}
|
||||
|
||||
pub fn set_node_index(&mut self, node_index: Option<NodeIndex>) {
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use nym_dkg::NodeIndex;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
pub struct RegistrationState {
|
||||
pub(crate) assigned_index: Option<NodeIndex>,
|
||||
}
|
||||
|
||||
impl RegistrationState {
|
||||
/// Specifies whether this dealer has already registered in the particular DKG epoch
|
||||
pub fn completed(&self) -> bool {
|
||||
self.assigned_index.is_some()
|
||||
}
|
||||
}
|
||||
@@ -243,7 +243,7 @@ pub(crate) async fn verification_key_submission(
|
||||
proposal_id
|
||||
);
|
||||
state.set_proposal_id(proposal_id);
|
||||
state.set_coconut_keypair(Some(coconut_keypair)).await;
|
||||
state.set_coconut_keypair(epoch_id, coconut_keypair).await;
|
||||
info!("DKG: Submitted own verification key");
|
||||
|
||||
Ok(())
|
||||
@@ -352,8 +352,8 @@ pub(crate) async fn verification_key_finalization(
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::coconut::dkg::controller::DkgController;
|
||||
use crate::coconut::dkg::dealing::dealing_exchange;
|
||||
use crate::coconut::dkg::public_key::public_key_submission;
|
||||
use crate::coconut::dkg::state::PersistentState;
|
||||
use crate::coconut::tests::DummyClient;
|
||||
use crate::coconut::KeyPair;
|
||||
@@ -405,7 +405,7 @@ pub(crate) mod tests {
|
||||
"n1jfrs6cmw9t7dv0x8cgny6geunzjh56n2s89fkv",
|
||||
];
|
||||
|
||||
async fn prepare_clients_and_states(db: &MockContractDb) -> Vec<(DkgClient, State)> {
|
||||
async fn prepare_clients_and_states(db: &MockContractDb) -> Vec<DkgController> {
|
||||
let params = dkg::params();
|
||||
let mut clients_and_states = vec![];
|
||||
let identity_keypair = identity::KeyPair::new(&mut thread_rng());
|
||||
@@ -429,52 +429,50 @@ pub(crate) mod tests {
|
||||
*identity_keypair.public_key(),
|
||||
KeyPair::new(),
|
||||
);
|
||||
clients_and_states.push((dkg_client, state));
|
||||
clients_and_states.push(DkgController::test_mock(dkg_client, state));
|
||||
}
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
public_key_submission(dkg_client, state, false)
|
||||
.await
|
||||
.unwrap();
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
controller.public_key_submission(0, false).await.unwrap();
|
||||
}
|
||||
clients_and_states
|
||||
}
|
||||
|
||||
async fn prepare_clients_and_states_with_dealing(
|
||||
db: &MockContractDb,
|
||||
) -> Vec<(DkgClient, State)> {
|
||||
async fn prepare_clients_and_states_with_dealing(db: &MockContractDb) -> Vec<DkgController> {
|
||||
let mut clients_and_states = prepare_clients_and_states(db).await;
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
dealing_exchange(dkg_client, state, OsRng, false)
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
dealing_exchange(&controller.dkg_client, &mut controller.state, OsRng, false)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
clients_and_states
|
||||
}
|
||||
|
||||
async fn prepare_clients_and_states_with_submission(
|
||||
db: &MockContractDb,
|
||||
) -> Vec<(DkgClient, State)> {
|
||||
async fn prepare_clients_and_states_with_submission(db: &MockContractDb) -> Vec<DkgController> {
|
||||
let mut clients_and_states = prepare_clients_and_states_with_dealing(db).await;
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
for controller 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, 0, &keypair_path, false)
|
||||
.await
|
||||
.unwrap();
|
||||
verification_key_submission(
|
||||
&controller.dkg_client,
|
||||
&mut controller.state,
|
||||
0,
|
||||
&keypair_path,
|
||||
false,
|
||||
)
|
||||
.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(
|
||||
db: &MockContractDb,
|
||||
) -> Vec<(DkgClient, State)> {
|
||||
async fn prepare_clients_and_states_with_validation(db: &MockContractDb) -> Vec<DkgController> {
|
||||
let mut clients_and_states = prepare_clients_and_states_with_submission(db).await;
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
verification_key_validation(dkg_client, state, false)
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
verification_key_validation(&controller.dkg_client, &mut controller.state, false)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
@@ -483,10 +481,10 @@ pub(crate) mod tests {
|
||||
|
||||
async fn prepare_clients_and_states_with_finalization(
|
||||
db: &MockContractDb,
|
||||
) -> Vec<(DkgClient, State)> {
|
||||
) -> Vec<DkgController> {
|
||||
let mut clients_and_states = prepare_clients_and_states_with_validation(db).await;
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
verification_key_finalization(dkg_client, state, false)
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
verification_key_finalization(&controller.dkg_client, &mut controller.state, false)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
@@ -498,12 +496,22 @@ pub(crate) mod tests {
|
||||
async fn check_dealers_filter_all_good() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_dealing(&db).await;
|
||||
let contract_state = clients_and_states[0].0.get_contract_state().await.unwrap();
|
||||
let contract_state = clients_and_states[0]
|
||||
.dkg_client
|
||||
.get_contract_state()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 0, 2, false)
|
||||
.await
|
||||
.unwrap();
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
let filtered = deterministic_filter_dealers(
|
||||
&controller.dkg_client,
|
||||
&mut controller.state,
|
||||
0,
|
||||
2,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), contract_state.key_size as usize);
|
||||
for mapping in filtered.iter() {
|
||||
assert_eq!(mapping.len(), 4);
|
||||
@@ -516,7 +524,11 @@ pub(crate) mod tests {
|
||||
async fn check_dealers_filter_one_bad_dealing() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_dealing(&db).await;
|
||||
let contract_state = clients_and_states[0].0.get_contract_state().await.unwrap();
|
||||
let contract_state = clients_and_states[0]
|
||||
.dkg_client
|
||||
.get_contract_state()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// corrupt just one dealing
|
||||
db.dealings_db
|
||||
@@ -532,12 +544,19 @@ pub(crate) mod tests {
|
||||
validator_dealings.push(last);
|
||||
});
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 0, 2, false)
|
||||
.await
|
||||
.unwrap();
|
||||
for controller in clients_and_states.iter_mut().skip(1) {
|
||||
let filtered = deterministic_filter_dealers(
|
||||
&controller.dkg_client,
|
||||
&mut controller.state,
|
||||
0,
|
||||
2,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), contract_state.key_size as usize);
|
||||
let corrupted_status = state
|
||||
let corrupted_status = controller
|
||||
.state
|
||||
.all_dealers()
|
||||
.get(&Addr::unchecked(TEST_VALIDATORS_ADDRESS[0]))
|
||||
.unwrap()
|
||||
@@ -552,25 +571,36 @@ pub(crate) mod tests {
|
||||
async fn check_dealers_resharing_filter_one_missing_dealing() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states(&db).await;
|
||||
let contract_state = clients_and_states[0].0.get_contract_state().await.unwrap();
|
||||
let contract_state = clients_and_states[0]
|
||||
.dkg_client
|
||||
.get_contract_state()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// add all but the first dealing
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
dealing_exchange(dkg_client, state, OsRng, true)
|
||||
for controller in clients_and_states.iter_mut().skip(1) {
|
||||
dealing_exchange(&controller.dkg_client, &mut controller.state, OsRng, true)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
for controller in clients_and_states.iter_mut().skip(1) {
|
||||
*db.initial_dealers_db.write().unwrap() = Some(InitialReplacementData {
|
||||
initial_dealers: vec![Addr::unchecked(TEST_VALIDATORS_ADDRESS[0])],
|
||||
initial_height: 1,
|
||||
});
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 0, 2, true)
|
||||
.await
|
||||
.unwrap();
|
||||
let filtered = deterministic_filter_dealers(
|
||||
&controller.dkg_client,
|
||||
&mut controller.state,
|
||||
0,
|
||||
2,
|
||||
true,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), contract_state.key_size as usize);
|
||||
let corrupted_status = state
|
||||
let corrupted_status = controller
|
||||
.state
|
||||
.all_dealers()
|
||||
.get(&Addr::unchecked(TEST_VALIDATORS_ADDRESS[0]))
|
||||
.unwrap()
|
||||
@@ -586,25 +616,36 @@ pub(crate) mod tests {
|
||||
async fn check_dealers_resharing_filter_one_noninitial_missing_dealing() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states(&db).await;
|
||||
let contract_state = clients_and_states[0].0.get_contract_state().await.unwrap();
|
||||
let contract_state = clients_and_states[0]
|
||||
.dkg_client
|
||||
.get_contract_state()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// add all but the first dealing
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
dealing_exchange(dkg_client, state, OsRng, true)
|
||||
for controller in clients_and_states.iter_mut().skip(1) {
|
||||
dealing_exchange(&controller.dkg_client, &mut controller.state, OsRng, true)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
for controller in clients_and_states.iter_mut().skip(1) {
|
||||
*db.initial_dealers_db.write().unwrap() = Some(InitialReplacementData {
|
||||
initial_dealers: vec![],
|
||||
initial_height: 1,
|
||||
});
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 0, 2, true)
|
||||
.await
|
||||
.unwrap();
|
||||
let filtered = deterministic_filter_dealers(
|
||||
&controller.dkg_client,
|
||||
&mut controller.state,
|
||||
0,
|
||||
2,
|
||||
true,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), contract_state.key_size as usize);
|
||||
assert!(state
|
||||
assert!(controller
|
||||
.state
|
||||
.all_dealers()
|
||||
.get(&Addr::unchecked(TEST_VALIDATORS_ADDRESS[0]))
|
||||
.unwrap()
|
||||
@@ -618,7 +659,11 @@ pub(crate) mod tests {
|
||||
async fn check_dealers_filter_all_bad_dealings() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_dealing(&db).await;
|
||||
let contract_state = clients_and_states[0].0.get_contract_state().await.unwrap();
|
||||
let contract_state = clients_and_states[0]
|
||||
.dkg_client
|
||||
.get_contract_state()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// corrupt all dealings of one address
|
||||
db.dealings_db
|
||||
@@ -634,15 +679,22 @@ pub(crate) mod tests {
|
||||
});
|
||||
});
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 0, 2, false)
|
||||
.await
|
||||
.unwrap();
|
||||
for controller in clients_and_states.iter_mut().skip(1) {
|
||||
let filtered = deterministic_filter_dealers(
|
||||
&controller.dkg_client,
|
||||
&mut controller.state,
|
||||
0,
|
||||
2,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), contract_state.key_size as usize);
|
||||
for mapping in filtered.iter() {
|
||||
assert_eq!(mapping.len(), 3);
|
||||
}
|
||||
let corrupted_status = state
|
||||
let corrupted_status = controller
|
||||
.state
|
||||
.all_dealers()
|
||||
.get(&Addr::unchecked(TEST_VALIDATORS_ADDRESS[0]))
|
||||
.unwrap()
|
||||
@@ -657,7 +709,11 @@ pub(crate) mod tests {
|
||||
async fn check_dealers_filter_malformed_dealing() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_dealing(&db).await;
|
||||
let contract_state = clients_and_states[0].0.get_contract_state().await.unwrap();
|
||||
let contract_state = clients_and_states[0]
|
||||
.dkg_client
|
||||
.get_contract_state()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// corrupt just one dealing
|
||||
db.dealings_db
|
||||
@@ -673,17 +729,30 @@ pub(crate) mod tests {
|
||||
validator_dealings.push(last);
|
||||
});
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
deterministic_filter_dealers(dkg_client, state, 0, 2, false)
|
||||
.await
|
||||
.unwrap();
|
||||
for controller in clients_and_states.iter_mut().skip(1) {
|
||||
deterministic_filter_dealers(
|
||||
&controller.dkg_client,
|
||||
&mut controller.state,
|
||||
0,
|
||||
2,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
// second filter will leave behind the bad dealer and surface why it was left out
|
||||
// in the first place
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 0, 2, false)
|
||||
.await
|
||||
.unwrap();
|
||||
let filtered = deterministic_filter_dealers(
|
||||
&controller.dkg_client,
|
||||
&mut controller.state,
|
||||
0,
|
||||
2,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), contract_state.key_size as usize);
|
||||
let corrupted_status = state
|
||||
let corrupted_status = controller
|
||||
.state
|
||||
.all_dealers()
|
||||
.get(&Addr::unchecked(TEST_VALIDATORS_ADDRESS[0]))
|
||||
.unwrap()
|
||||
@@ -698,7 +767,11 @@ pub(crate) mod tests {
|
||||
async fn check_dealers_filter_dealing_verification_error() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_dealing(&db).await;
|
||||
let contract_state = clients_and_states[0].0.get_contract_state().await.unwrap();
|
||||
let contract_state = clients_and_states[0]
|
||||
.dkg_client
|
||||
.get_contract_state()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// corrupt just one dealing
|
||||
db.dealings_db
|
||||
@@ -719,17 +792,30 @@ pub(crate) mod tests {
|
||||
validator_dealings.push(last);
|
||||
});
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
deterministic_filter_dealers(dkg_client, state, 0, 2, false)
|
||||
.await
|
||||
.unwrap();
|
||||
for controller in clients_and_states.iter_mut().skip(1) {
|
||||
deterministic_filter_dealers(
|
||||
&controller.dkg_client,
|
||||
&mut controller.state,
|
||||
0,
|
||||
2,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
// second filter will leave behind the bad dealer and surface why it was left out
|
||||
// in the first place
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 0, 2, false)
|
||||
.await
|
||||
.unwrap();
|
||||
let filtered = deterministic_filter_dealers(
|
||||
&controller.dkg_client,
|
||||
&mut controller.state,
|
||||
0,
|
||||
2,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), contract_state.key_size as usize);
|
||||
let corrupted_status = state
|
||||
let corrupted_status = controller
|
||||
.state
|
||||
.all_dealers()
|
||||
.get(&Addr::unchecked(TEST_VALIDATORS_ADDRESS[0]))
|
||||
.unwrap()
|
||||
@@ -744,11 +830,17 @@ pub(crate) mod tests {
|
||||
async fn partial_keypair_derivation() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_dealing(&db).await;
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 0, 2, false)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(derive_partial_keypair(state, 2, filtered).is_ok());
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
let filtered = deterministic_filter_dealers(
|
||||
&controller.dkg_client,
|
||||
&mut controller.state,
|
||||
0,
|
||||
2,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(derive_partial_keypair(&mut controller.state, 2, filtered).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -772,11 +864,17 @@ pub(crate) mod tests {
|
||||
validator_dealings.push(last);
|
||||
});
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 0, 2, false)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(derive_partial_keypair(state, 2, filtered).is_ok());
|
||||
for controller in clients_and_states.iter_mut().skip(1) {
|
||||
let filtered = deterministic_filter_dealers(
|
||||
&controller.dkg_client,
|
||||
&mut controller.state,
|
||||
0,
|
||||
2,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(derive_partial_keypair(&mut controller.state, 2, filtered).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -786,13 +884,13 @@ pub(crate) mod tests {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_submission(&db).await;
|
||||
|
||||
for (_, state) in clients_and_states.iter_mut() {
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
assert!(db
|
||||
.proposal_db
|
||||
.read()
|
||||
.unwrap()
|
||||
.contains_key(&state.proposal_id_value().unwrap()));
|
||||
assert!(state.coconut_keypair_is_some().await);
|
||||
.contains_key(&controller.state.proposal_id_value().unwrap()));
|
||||
assert!(controller.state.coconut_keypair_is_some().await);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -801,12 +899,12 @@ pub(crate) mod tests {
|
||||
async fn validate_verification_key() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_validation(&db).await;
|
||||
for (_, state) in clients_and_states.iter_mut() {
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
let proposal = db
|
||||
.proposal_db
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&state.proposal_id_value().unwrap())
|
||||
.get(&controller.state.proposal_id_value().unwrap())
|
||||
.unwrap()
|
||||
.clone();
|
||||
assert_eq!(proposal.status, Status::Passed);
|
||||
@@ -825,18 +923,18 @@ pub(crate) mod tests {
|
||||
.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, false)
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
verification_key_validation(&controller.dkg_client, &mut controller.state, false)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for (idx, (_, state)) in clients_and_states.iter().enumerate() {
|
||||
for (idx, controller) in clients_and_states.iter().enumerate() {
|
||||
let proposal = db
|
||||
.proposal_db
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&state.proposal_id_value().unwrap())
|
||||
.get(&controller.state.proposal_id_value().unwrap())
|
||||
.unwrap()
|
||||
.clone();
|
||||
if idx == 0 {
|
||||
@@ -867,18 +965,18 @@ pub(crate) mod tests {
|
||||
.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, false)
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
verification_key_validation(&controller.dkg_client, &mut controller.state, false)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for (idx, (_, state)) in clients_and_states.iter().enumerate() {
|
||||
for (idx, controller) in clients_and_states.iter().enumerate() {
|
||||
let proposal = db
|
||||
.proposal_db
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&state.proposal_id_value().unwrap())
|
||||
.get(&controller.state.proposal_id_value().unwrap())
|
||||
.unwrap()
|
||||
.clone();
|
||||
if idx == 0 {
|
||||
@@ -895,12 +993,12 @@ pub(crate) mod tests {
|
||||
let db = MockContractDb::new();
|
||||
let clients_and_states = prepare_clients_and_states_with_finalization(&db).await;
|
||||
|
||||
for (_, state) in clients_and_states.iter() {
|
||||
for controller in clients_and_states.iter() {
|
||||
let proposal = db
|
||||
.proposal_db
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&state.proposal_id_value().unwrap())
|
||||
.get(&controller.state.proposal_id_value().unwrap())
|
||||
.unwrap()
|
||||
.clone();
|
||||
assert_eq!(proposal.status, Status::Executed);
|
||||
@@ -912,21 +1010,22 @@ pub(crate) mod tests {
|
||||
async fn reshare_preserves_keys() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_finalization(&db).await;
|
||||
for (_, state) in clients_and_states.iter_mut() {
|
||||
state.set_was_in_progress();
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
controller.state.set_was_in_progress();
|
||||
}
|
||||
|
||||
let mut vks = vec![];
|
||||
let mut indices = vec![];
|
||||
for (_, state) in clients_and_states.iter() {
|
||||
let vk = state
|
||||
for controller in clients_and_states.iter() {
|
||||
let vk = controller
|
||||
.state
|
||||
.coconut_keypair()
|
||||
.await
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.verification_key()
|
||||
.clone();
|
||||
let index = state.node_index().unwrap();
|
||||
let index = controller.state.node_index().unwrap();
|
||||
vks.push(vk);
|
||||
indices.push(index);
|
||||
}
|
||||
@@ -961,46 +1060,51 @@ pub(crate) mod tests {
|
||||
*db.dealings_db.write().unwrap() = Default::default();
|
||||
*db.verification_share_db.write().unwrap() = Default::default();
|
||||
let mut initial_dealers = vec![];
|
||||
for (dkg_client, _) in clients_and_states.iter() {
|
||||
let client_address = Addr::unchecked(dkg_client.get_address().await.as_ref());
|
||||
for controller in clients_and_states.iter() {
|
||||
let client_address =
|
||||
Addr::unchecked(controller.dkg_client.get_address().await.as_ref());
|
||||
initial_dealers.push(client_address);
|
||||
}
|
||||
*db.initial_dealers_db.write().unwrap() = Some(InitialReplacementData {
|
||||
initial_dealers,
|
||||
initial_height: 1,
|
||||
});
|
||||
*clients_and_states.first_mut().unwrap() = (new_dkg_client, state);
|
||||
*clients_and_states.first_mut().unwrap() = DkgController::test_mock(new_dkg_client, state);
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
public_key_submission(dkg_client, state, true)
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
controller.public_key_submission(0, true).await.unwrap();
|
||||
}
|
||||
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
dealing_exchange(&controller.dkg_client, &mut controller.state, OsRng, true)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
dealing_exchange(dkg_client, state, OsRng, true)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
for controller 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, 0, &keypair_path, true)
|
||||
.await
|
||||
.unwrap();
|
||||
verification_key_submission(
|
||||
&controller.dkg_client,
|
||||
&mut controller.state,
|
||||
0,
|
||||
&keypair_path,
|
||||
true,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
std::fs::remove_file(private_key_path).unwrap();
|
||||
std::fs::remove_file(public_key_path).unwrap();
|
||||
}
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
verification_key_validation(dkg_client, state, true)
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
verification_key_validation(&controller.dkg_client, &mut controller.state, true)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
verification_key_finalization(dkg_client, state, true)
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
verification_key_finalization(&controller.dkg_client, &mut controller.state, true)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
@@ -1013,15 +1117,16 @@ pub(crate) mod tests {
|
||||
|
||||
let mut vks = vec![];
|
||||
let mut indices = vec![];
|
||||
for (_, state) in clients_and_states.iter() {
|
||||
let vk = state
|
||||
for controller in clients_and_states.iter() {
|
||||
let vk = controller
|
||||
.state
|
||||
.coconut_keypair()
|
||||
.await
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.verification_key()
|
||||
.clone();
|
||||
let index = state.node_index().unwrap();
|
||||
let index = controller.state.node_index().unwrap();
|
||||
vks.push(vk);
|
||||
indices.push(index);
|
||||
}
|
||||
@@ -1034,8 +1139,8 @@ pub(crate) mod tests {
|
||||
async fn reshare_after_reset() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_finalization(&db).await;
|
||||
for (_, state) in clients_and_states.iter_mut() {
|
||||
state.set_was_in_progress();
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
controller.state.set_was_in_progress();
|
||||
}
|
||||
|
||||
let new_dkg_client = DkgClient::new(
|
||||
@@ -1088,39 +1193,43 @@ pub(crate) mod tests {
|
||||
*db.dealings_db.write().unwrap() = Default::default();
|
||||
*db.verification_share_db.write().unwrap() = Default::default();
|
||||
clients_and_states.pop().unwrap();
|
||||
let (initial_client2, initial_state2) = clients_and_states.pop().unwrap();
|
||||
clients_and_states.push((new_dkg_client, state));
|
||||
clients_and_states.push((new_dkg_client2, state2));
|
||||
let controller2 = clients_and_states.pop().unwrap();
|
||||
clients_and_states.push(DkgController::test_mock(new_dkg_client, state));
|
||||
clients_and_states.push(DkgController::test_mock(new_dkg_client2, state2));
|
||||
|
||||
// DKG in reset mode
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
public_key_submission(dkg_client, state, false)
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
controller.public_key_submission(0, false).await.unwrap();
|
||||
}
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
dealing_exchange(&controller.dkg_client, &mut controller.state, OsRng, false)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
dealing_exchange(dkg_client, state, OsRng, false)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
for controller 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, 0, &keypair_path, false)
|
||||
.await
|
||||
.unwrap();
|
||||
verification_key_submission(
|
||||
&controller.dkg_client,
|
||||
&mut controller.state,
|
||||
0,
|
||||
&keypair_path,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
std::fs::remove_file(private_key_path).unwrap();
|
||||
std::fs::remove_file(public_key_path).unwrap();
|
||||
}
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
verification_key_validation(dkg_client, state, false)
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
verification_key_validation(&controller.dkg_client, &mut controller.state, false)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
verification_key_finalization(dkg_client, state, false)
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
verification_key_finalization(&controller.dkg_client, &mut controller.state, false)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
@@ -1130,22 +1239,23 @@ pub(crate) mod tests {
|
||||
.unwrap()
|
||||
.values()
|
||||
.all(|proposal| { proposal.status == Status::Executed }));
|
||||
for (_, state) in clients_and_states.iter_mut() {
|
||||
state.set_was_in_progress();
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
controller.state.set_was_in_progress();
|
||||
}
|
||||
|
||||
// DKG in reshare mode
|
||||
let mut vks = vec![];
|
||||
let mut indices = vec![];
|
||||
for (_, state) in clients_and_states.iter() {
|
||||
let vk = state
|
||||
for controller in clients_and_states.iter() {
|
||||
let vk = controller
|
||||
.state
|
||||
.coconut_keypair()
|
||||
.await
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.verification_key()
|
||||
.clone();
|
||||
let index = state.node_index().unwrap();
|
||||
let index = controller.state.node_index().unwrap();
|
||||
vks.push(vk);
|
||||
indices.push(index);
|
||||
}
|
||||
@@ -1157,47 +1267,52 @@ pub(crate) mod tests {
|
||||
*db.dealings_db.write().unwrap() = Default::default();
|
||||
*db.verification_share_db.write().unwrap() = Default::default();
|
||||
let mut initial_dealers = vec![];
|
||||
for (dkg_client, _) in clients_and_states.iter() {
|
||||
let client_address = Addr::unchecked(dkg_client.get_address().await.as_ref());
|
||||
for controller in clients_and_states.iter() {
|
||||
let client_address =
|
||||
Addr::unchecked(controller.dkg_client.get_address().await.as_ref());
|
||||
initial_dealers.push(client_address);
|
||||
}
|
||||
*db.initial_dealers_db.write().unwrap() = Some(InitialReplacementData {
|
||||
initial_dealers,
|
||||
initial_height: 1,
|
||||
});
|
||||
*clients_and_states.last_mut().unwrap() = (initial_client2, initial_state2);
|
||||
*clients_and_states.last_mut().unwrap() = controller2;
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
public_key_submission(dkg_client, state, true)
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
controller.public_key_submission(0, true).await.unwrap();
|
||||
}
|
||||
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
dealing_exchange(&controller.dkg_client, &mut controller.state, OsRng, true)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
dealing_exchange(dkg_client, state, OsRng, true)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
for controller 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, 0, &keypair_path, true)
|
||||
.await
|
||||
.unwrap();
|
||||
verification_key_submission(
|
||||
&controller.dkg_client,
|
||||
&mut controller.state,
|
||||
0,
|
||||
&keypair_path,
|
||||
true,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
std::fs::remove_file(private_key_path).unwrap();
|
||||
std::fs::remove_file(public_key_path).unwrap();
|
||||
}
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
verification_key_validation(dkg_client, state, true)
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
verification_key_validation(&controller.dkg_client, &mut controller.state, true)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
verification_key_finalization(dkg_client, state, true)
|
||||
for controller in clients_and_states.iter_mut() {
|
||||
verification_key_finalization(&controller.dkg_client, &mut controller.state, true)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
@@ -1210,15 +1325,16 @@ pub(crate) mod tests {
|
||||
|
||||
let mut vks = vec![];
|
||||
let mut indices = vec![];
|
||||
for (_, state) in clients_and_states.iter() {
|
||||
let vk = state
|
||||
for controller in clients_and_states.iter() {
|
||||
let vk = controller
|
||||
.state
|
||||
.coconut_keypair()
|
||||
.await
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.verification_key()
|
||||
.clone();
|
||||
let index = state.node_index().unwrap();
|
||||
let index = controller.state.node_index().unwrap();
|
||||
vks.push(vk);
|
||||
indices.push(index);
|
||||
}
|
||||
|
||||
@@ -1,31 +1,41 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use nym_coconut_dkg_common::types::EpochId;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{RwLock, RwLockReadGuard};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct KeyPair {
|
||||
inner: Arc<RwLock<Option<nym_coconut_interface::KeyPair>>>,
|
||||
// keys: Arc<RwLock<HashMap<EpochId, nym_coconut_interface::KeyPair>>>,
|
||||
keys: Arc<RwLock<Option<(EpochId, nym_coconut_interface::KeyPair)>>>,
|
||||
// issued_for_epoch: Arc<AtomicU64>,
|
||||
}
|
||||
|
||||
impl KeyPair {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new(None)),
|
||||
keys: Arc::new(RwLock::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn take(&self) -> Option<nym_coconut::KeyPair> {
|
||||
self.inner.write().await.take()
|
||||
pub async fn take(&self) -> Option<(EpochId, nym_coconut::KeyPair)> {
|
||||
self.keys.write().await.take()
|
||||
}
|
||||
|
||||
pub async fn get(&self) -> RwLockReadGuard<'_, Option<nym_coconut::KeyPair>> {
|
||||
self.inner.read().await
|
||||
todo!()
|
||||
// self.keys.read().await
|
||||
}
|
||||
|
||||
pub async fn set(&self, keypair: Option<nym_coconut_interface::KeyPair>) {
|
||||
let mut w_lock = self.inner.write().await;
|
||||
*w_lock = keypair;
|
||||
pub async fn set(&self, epoch_id: EpochId, keypair: nym_coconut_interface::KeyPair) {
|
||||
todo!()
|
||||
// let mut w_lock = self.keys.write().await;
|
||||
// *w_lock = Some(keypair);
|
||||
}
|
||||
|
||||
pub async fn invalidate(&self) {
|
||||
*self.keys.write().await = None
|
||||
}
|
||||
}
|
||||
|
||||
+538
-534
File diff suppressed because it is too large
Load Diff
+2
-1
@@ -73,7 +73,8 @@ async fn start_nym_api_tasks(config: Config) -> anyhow::Result<ShutdownHandles>
|
||||
|
||||
// if the keypair doesnt exist (because say this API is running in the caching mode), nothing will happen
|
||||
if let Some(loaded_keys) = load_coconut_keypair_if_exists(&config.coconut_signer)? {
|
||||
coconut_keypair_wrapper.set(Some(loaded_keys)).await
|
||||
todo!()
|
||||
// coconut_keypair_wrapper.set(loaded_keys).await
|
||||
}
|
||||
|
||||
let identity_keypair = config.base.storage_paths.load_identity()?;
|
||||
|
||||
Reference in New Issue
Block a user