This commit is contained in:
Jędrzej Stuczyński
2024-01-25 16:11:07 +00:00
parent c9814a1c6e
commit a66f63e34d
22 changed files with 335 additions and 578 deletions
-34
View File
@@ -1,34 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub(crate) enum ComplaintReason {
MalformedBTEPublicKey,
InvalidBTEPublicKey,
MissingDealing,
MalformedDealing,
DealingVerificationError,
}
impl Display for ComplaintReason {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ComplaintReason::MalformedBTEPublicKey => {
write!(f, "provided BTE Public key is malformed")
}
ComplaintReason::InvalidBTEPublicKey => {
write!(f, "provided BTE public key does not verify correctly")
}
ComplaintReason::MissingDealing => write!(f, "one of the dealings is missing"),
ComplaintReason::MalformedDealing => {
write!(f, "one of the provided dealings is malformed")
}
ComplaintReason::DealingVerificationError => {
write!(f, "failed to verify one of the dealings")
}
}
}
}
+4 -2
View File
@@ -1,9 +1,11 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::coconut::dkg::dealing::DealingGenerationError;
use crate::coconut::dkg::key_derivation::KeyDerivationError;
use crate::coconut::dkg::key_finalization::KeyFinalizationError;
use crate::coconut::dkg::key_validation::KeyValidationError;
use crate::coconut::dkg::public_key::PublicKeySubmissionError;
use crate::coconut::error::CoconutError;
use std::path::PathBuf;
use thiserror::Error;
@@ -35,13 +37,13 @@ pub enum DkgError {
#[error("failed to submit public keys to the DKG contract: {source}")]
PublicKeySubmissionFailure {
#[source]
source: CoconutError,
source: PublicKeySubmissionError,
},
#[error("failed to submit DKG dealings to the DKG contract: {source}")]
DealingExchangeFailure {
#[source]
source: CoconutError,
source: DealingGenerationError,
},
#[error("failed to submit verification keys to the DKG contract: {source}")]
+26 -2
View File
@@ -1,10 +1,11 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::coconut::client::Client;
use crate::coconut::keys::KeyPairWithEpoch;
use crate::support::config;
use crate::support::{config, nyxd};
use anyhow::{anyhow, bail, Context};
use nym_coconut_dkg_common::types::EpochId;
use nym_coconut_dkg_common::types::{EpochId, EpochState};
use nym_dkg::bte::keys::KeyPair as DkgKeyPair;
use rand::{CryptoRng, RngCore};
use std::path::Path;
@@ -45,6 +46,29 @@ pub(crate) fn load_coconut_keypair_if_exists(
.map(Some)
}
// the keys can be considered valid if they were generated for the current dkg epoch
// and we're either in the "in progress" or "key finalization" states of the DKG
pub(crate) async fn can_validate_coconut_keys(
nyxd_client: &nyxd::Client,
issued_for: EpochId,
) -> anyhow::Result<bool> {
// validate the keys if they were generated for the current dkg epoch
// and we're either in the "in progress" or "key finalization" states of the DKG
let current_dkg_epoch = nyxd_client.get_current_epoch().await?;
if issued_for != current_dkg_epoch.epoch_id {
warn!("managed to load coconut keys, but they were generated for epoch {issued_for}. The current epoch is {}. the keys won't be used for credential issuance", current_dkg_epoch.epoch_id);
Ok(false)
} else if !matches!(
current_dkg_epoch.state,
EpochState::InProgress | EpochState::VerificationKeyFinalization { .. }
) {
warn!("managed to load coconut keys, but the current DKG epoch is at {}. the keys won't (yet) be used for credential issuance", current_dkg_epoch.state);
Ok(false)
} else {
Ok(true)
}
}
pub(crate) fn persist_coconut_keypair<P: AsRef<Path>>(
keys: &KeyPairWithEpoch,
store_path: P,
+63 -39
View File
@@ -3,7 +3,7 @@
use crate::coconut::dkg::client::DkgClient;
use crate::coconut::dkg::controller::error::DkgError;
use crate::coconut::dkg::state::{ConsistentState, PersistentState, State};
use crate::coconut::dkg::state::{PersistentState, State};
use crate::coconut::keys::KeyPair as CoconutKeyPair;
use crate::nyxd;
use crate::support::config;
@@ -13,7 +13,7 @@ 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 rand::{CryptoRng, Rng, RngCore};
use std::path::PathBuf;
use std::time::Duration;
use time::OffsetDateTime;
@@ -43,9 +43,12 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
bail!("can't start a DKG controller without specifying an announce address!")
};
let persistent_state =
PersistentState::load_from_file(&config.storage_paths.dkg_persistent_state_path)
.unwrap_or_default();
let persistent_state = PersistentState::load_from_file(
&config.storage_paths.dkg_persistent_state_path,
).unwrap_or_else(|err| {
warn!("could not load an existing persistent state from the file. a fresh state will be used: {err}");
Default::default()
});
Ok(DkgController {
dkg_client: DkgClient::new(nyxd_client),
@@ -64,11 +67,6 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
}
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
// std::fs::remove_file(&self.secret_key_path).ok();
// std::fs::remove_file(&self.verification_key_path).ok();
// }
let persistent_state = PersistentState::from(&self.state);
let save_path = self.state.persistent_state_path();
persistent_state.save_to_file(save_path).map_err(|source| {
@@ -107,14 +105,6 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
Ok(())
}
async fn ensure_state_consistency(&self) -> Result<(), DkgError> {
todo!()
// if let Err(err) = self.state.is_consistent(epoch.state).await {
// warn!("Epoch state is corrupted - {err}. Awaiting for a DKG restart.");
// return;
// }
}
async fn handle_awaiting_initialisation(&mut self) -> Result<(), DkgError> {
info!("DKG hasn't been initialised yet - nothing to do");
Ok(())
@@ -128,7 +118,8 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
debug!("DKG: public key submission (resharing: {resharing})");
self.public_key_submission(epoch_id, resharing)
.await
.map_err(|source| DkgError::PublicKeySubmissionFailure { source })
.map_err(|source| DkgError::PublicKeySubmissionFailure { source })?;
self.persist_state()
}
async fn handle_dealing_exchange(
@@ -139,7 +130,8 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
debug!("DKG: dealing exchange (resharing: {resharing})");
self.dealing_exchange(epoch_id, resharing)
.await
.map_err(|source| DkgError::DealingExchangeFailure { source })
.map_err(|source| DkgError::DealingExchangeFailure { source })?;
self.persist_state()
}
async fn handle_verification_key_submission(
@@ -150,7 +142,8 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
debug!("DKG: verification key submission (resharing: {resharing})");
self.verification_key_submission(epoch_id, resharing)
.await
.map_err(|source| DkgError::VerificationKeySubmissionFailure { source })
.map_err(|source| DkgError::VerificationKeySubmissionFailure { source })?;
self.persist_state()
}
async fn handle_verification_key_validation(
@@ -162,7 +155,8 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
self.verification_key_validation(epoch_id)
.await
.map_err(|source| DkgError::VerificationKeyValidationFailure { source })
.map_err(|source| DkgError::VerificationKeyValidationFailure { source })?;
self.persist_state()
}
async fn handle_verification_key_finalization(
@@ -174,13 +168,29 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
self.verification_key_finalization(epoch_id)
.await
.map_err(|source| DkgError::VerificationKeyFinalizationFailure { source })
.map_err(|source| DkgError::VerificationKeyFinalizationFailure { source })?;
self.persist_state()
}
async fn handle_in_progress(&mut self) -> Result<(), DkgError> {
async fn handle_in_progress(&mut self, epoch_id: EpochId) -> Result<(), DkgError> {
debug!("DKG: epoch in progress");
self.state.set_was_in_progress();
let Ok(state) = self.state.in_progress_state(epoch_id) else {
// we probably just started up the api while the DKG has already finished and we're waiting for new round to join
debug!("the DKG has finished without our participation");
return Ok(());
};
if !state.entered {
info!("this is the first time this node is in the in progress state - going to clear state from the PREVIOUS epoch...");
// if we finished dkg for epoch 123, we no longer care about anything from epoch 122
// (but keep track of data from 123 for the future reference)
self.state.clear_previous_epoch(epoch_id);
// SAFETY: we just accessed this item in an immutable way, thus it MUST exist so the unwrap is fine
self.state.in_progress_state_mut(epoch_id).unwrap().entered = true;
}
Ok(())
}
@@ -194,12 +204,8 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
}
pub(crate) async fn handle_epoch_state(&mut self) -> Result<(), DkgError> {
self.ensure_state_consistency().await?;
self.ensure_group_member().await?;
// make sure to always persist our state before continuing in case of failures
self.persist_state()?;
let epoch = self.current_epoch().await?;
match epoch.state {
@@ -225,17 +231,14 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
.await?
}
// Just wait, in case we need to redo dkg at some point
EpochState::InProgress => self.handle_in_progress().await?,
EpochState::InProgress => self.handle_in_progress(epoch.epoch_id).await?,
};
// persist the state after the successful update
// (sure, we might be doing this unnecessarily for each "InProgress",
// but that's just one write every polling interval, which in the grand scheme of things is nothing
self.persist_state()?;
// 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 {
let now = OffsetDateTime::now_utc();
if now.unix_timestamp() > epoch_finish.seconds() as i64 {
if now.unix_timestamp() > epoch_finish.seconds() as i64 + variance {
// TODO: make sure to not overload validator in case its running slow
// i.e. send it once at most every X seconds
self.try_advance_dkg_state().await?
@@ -247,9 +250,26 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
pub(crate) async fn run(mut self, mut shutdown: TaskClient) {
let mut interval = interval(self.polling_rate);
// sometimes when the process is running behind, the ticker resolves multiple times in quick succession
// so explicitly track those instances and make sure we don't overload the validator with contract calls
let mut last_polled = OffsetDateTime::now_utc();
let mut last_tick_duration = Default::default();
while !shutdown.is_shutdown() {
tokio::select! {
_ = interval.tick() => {
let now = OffsetDateTime::now_utc();
let tick_duration = now - last_polled;
last_polled = now;
if tick_duration < self.polling_rate {
warn!("it seems the process is running behind. The current tick rate is lower than the polling rate. rate: {:?}, current tick: {}, previous tick: {}", self.polling_rate, tick_duration, last_tick_duration);
last_tick_duration = tick_duration;
continue
}
last_tick_duration = tick_duration;
if let Err(err) = self.handle_epoch_state().await {
error!("failed to update the DKG state: {err}")
}
@@ -289,17 +309,21 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
#[cfg(test)]
impl DkgController {
pub(crate) fn test_mock(dkg_client: DkgClient, state: State) -> DkgController {
#[allow(dead_code)]
pub(crate) fn default_test_mock(
dkg_client: DkgClient,
state: State,
) -> DkgController<rand_chacha::ChaCha20Rng> {
DkgController {
dkg_client,
coconut_key_path: Default::default(),
state,
rng: OsRng,
rng: crate::coconut::tests::fixtures::test_rng([1u8; 32]),
polling_rate: Default::default(),
}
}
pub(crate) fn test_mock_new(
pub(crate) fn test_mock(
rng: rand_chacha::ChaCha20Rng,
dkg_client: DkgClient,
state: State,
+43 -13
View File
@@ -4,7 +4,6 @@
use crate::coconut::dkg;
use crate::coconut::dkg::controller::keys::archive_coconut_keypair;
use crate::coconut::dkg::controller::DkgController;
use crate::coconut::dkg::state::ParticipantState;
use crate::coconut::error::CoconutError;
use crate::coconut::keys::KeyPairWithEpoch;
use log::debug;
@@ -13,8 +12,10 @@ use nym_coconut_dkg_common::types::{
};
use nym_dkg::{Dealing, Scalar};
use rand::{CryptoRng, RngCore};
use std::collections::{BTreeMap, HashMap};
use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::path::PathBuf;
use thiserror::Error;
enum DealingGeneration {
Fresh { number: u32 },
@@ -36,12 +37,37 @@ impl Debug for DealingGeneration {
}
}
#[derive(Debug, Error)]
pub enum DealingGenerationError {
#[error(transparent)]
CoconutError(#[from] CoconutError),
#[error("can't complete dealing exchange without registering public keys")]
IncompletePublicKeyRegistration,
#[error("contract state failure - the DKG threshold is unavailable even though dealing exchange has been initiated")]
UnavailableContractThreshold,
#[error("could not establish receiver index for epoch {epoch_id} even though we're a dealer!")]
UnavailableReceiverIndex { epoch_id: EpochId },
#[error("failed to archive coconut key for epoch {epoch_id} using path {}: {source}", path.display())]
KeyArchiveFailure {
epoch_id: EpochId,
path: PathBuf,
// I hate that we're using anyhow error source here, but changing that would require bigger refactoring
#[source]
source: anyhow::Error,
},
}
impl<R: RngCore + CryptoRng> DkgController<R> {
async fn generate_dealings(
&mut self,
epoch_id: EpochId,
spec: DealingGeneration,
) -> Result<HashMap<DealingIndex, Dealing>, CoconutError> {
) -> Result<HashMap<DealingIndex, Dealing>, DealingGenerationError> {
let threshold = self.dkg_client.get_current_epoch_threshold().await?.ok_or(
CoconutError::UnrecoverableState {
reason: String::from("Threshold should have been set"),
@@ -107,7 +133,7 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
&mut self,
epoch_id: EpochId,
number: u32,
) -> Result<HashMap<DealingIndex, Dealing>, CoconutError> {
) -> Result<HashMap<DealingIndex, Dealing>, DealingGenerationError> {
self.generate_dealings(epoch_id, DealingGeneration::Fresh { number })
.await
}
@@ -116,7 +142,7 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
&mut self,
epoch_id: EpochId,
prior_secrets: Vec<Scalar>,
) -> Result<HashMap<DealingIndex, Dealing>, CoconutError> {
) -> Result<HashMap<DealingIndex, Dealing>, DealingGenerationError> {
self.generate_dealings(epoch_id, DealingGeneration::Resharing { prior_secrets })
.await
}
@@ -125,7 +151,7 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
&self,
epoch_id: EpochId,
resharing: bool,
) -> Result<(), CoconutError> {
) -> Result<(), DealingGenerationError> {
let dealing_state = self.state.dealing_exchange_state(epoch_id)?;
for (dealing_index, dealing) in &dealing_state.generated_dealings {
@@ -154,7 +180,7 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
/// Check whether this dealer can participate in the resharing
/// by looking into the contract and ensuring it's in the list of initial dealers for this epoch
async fn can_reshare(&self) -> Result<bool, CoconutError> {
async fn can_reshare(&self) -> Result<bool, DealingGenerationError> {
let Some(initial_data) = self.dkg_client.get_initial_dealers().await? else {
return Ok(false);
};
@@ -173,7 +199,7 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
epoch_id: EpochId,
expected_key_size: u32,
old_keypair: KeyPairWithEpoch,
) -> Result<(), CoconutError> {
) -> Result<(), DealingGenerationError> {
// make sure we're allowed to participate in resharing
if !self.can_reshare().await? {
// we have to wait for other dealers to give us the dealings (hopefully)
@@ -222,7 +248,7 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
&mut self,
epoch_id: EpochId,
resharing: bool,
) -> Result<(), CoconutError> {
) -> Result<(), DealingGenerationError> {
let dealing_state = self.state.dealing_exchange_state(epoch_id)?;
// check if we have already submitted the dealings
@@ -233,6 +259,10 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
return Ok(());
}
if !self.state.registration_state(epoch_id)?.completed() {
return Err(DealingGenerationError::IncompletePublicKeyRegistration);
}
// FAILURE CASE:
// check if we have already generated the dealings, but they failed to get sent to the contract for whatever reason
if !dealing_state.generated_dealings.is_empty() {
@@ -267,8 +297,8 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
// update internally used threshold value which should have been available after all dealers registered
let Some(threshold) = self.dkg_client.get_current_epoch_threshold().await? else {
// TODO: if we're in the dealing exchange phase, the threshold must have been already established
todo!("yell at me clippy")
// if we're in the dealing exchange phase, the threshold must have been already established
return Err(DealingGenerationError::UnavailableContractThreshold);
};
self.state
.key_derivation_state_mut(epoch_id)?
@@ -280,7 +310,7 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
else {
// this branch should be impossible as `dealing_exchange` should never be called unless we're actually a dealer
error!("could not establish receiver index for epoch {epoch_id} even though we're a dealer!");
return Err(CoconutError::UnavailableReceiverIndex { epoch_id });
return Err(DealingGenerationError::UnavailableReceiverIndex { epoch_id });
};
self.state
.dealing_exchange_state_mut(epoch_id)?
@@ -314,7 +344,7 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
self.state.persist()?;
// archive the keypair
if let Err(source) = archive_coconut_keypair(&self.coconut_key_path, keypair_epoch) {
return Err(CoconutError::KeyArchiveFailure {
return Err(DealingGenerationError::KeyArchiveFailure {
epoch_id,
path: self.coconut_key_path.clone(),
source,
+6 -3
View File
@@ -31,12 +31,12 @@ pub enum KeyDerivationError {
#[error(transparent)]
CoconutError(#[from] CoconutError),
#[error("the initial, zeroth, epoch is set to be in resharing mode - this is illegal and should have been impossible!")]
ZerothEpochResharing,
#[error("can't complete key derivation without dealing exchange")]
IncompleteDealingExchange,
#[error("the initial, zeroth, epoch is set to be in resharing mode - this is illegal and should have been impossible!")]
ZerothEpochResharing,
#[error("could not recover our own proposal id from the submitted share")]
UnrecoverableProposalId,
@@ -555,6 +555,9 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
"we have already generated key for this epoch and submitted validation proposal"
);
return Ok(());
} else if let Some(failure) = key_generation_state.completion_failure() {
error!("key derivation failed with unrecoverable failure: {failure}");
return Ok(());
}
if !self.state.dealing_exchange_state(epoch_id)?.completed {
+1 -1
View File
@@ -5,7 +5,7 @@ use crate::coconut::dkg::controller::DkgController;
use crate::coconut::error::CoconutError;
use crate::coconut::state::bandwidth_voucher_params;
use cosmwasm_std::Addr;
use cw3::{Status, Vote};
use cw3::Vote;
use nym_coconut::{check_vk_pairing, Base58, VerificationKey};
use nym_coconut_dkg_common::types::EpochId;
use nym_coconut_dkg_common::verification_key::ContractVKShare;
-1
View File
@@ -9,7 +9,6 @@ pub(crate) fn params() -> &'static nym_dkg::bte::Params {
}
pub(crate) mod client;
pub(crate) mod complaints;
pub(crate) mod controller;
pub(crate) mod dealing;
pub(crate) mod key_derivation;
+8 -1
View File
@@ -6,6 +6,13 @@ use crate::coconut::error::CoconutError;
use log::debug;
use nym_coconut_dkg_common::types::EpochId;
use rand::{CryptoRng, RngCore};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum PublicKeySubmissionError {
#[error(transparent)]
CoconutError(#[from] CoconutError),
}
impl<R: RngCore + CryptoRng> DkgController<R> {
/// First step of the DKG process during which the nym api will register for the key exchange
@@ -24,7 +31,7 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
&mut self,
epoch_id: EpochId,
resharing: bool,
) -> Result<(), CoconutError> {
) -> Result<(), PublicKeySubmissionError> {
self.state.maybe_init_dkg_state(epoch_id);
let registration_state = self.state.registration_state(epoch_id)?;
@@ -2,13 +2,14 @@
// SPDX-License-Identifier: GPL-3.0-only
use super::serde_helpers::generated_dealings;
use crate::coconut::dkg::state::DkgParticipant;
use nym_coconut_dkg_common::types::DealingIndex;
use nym_dkg::{Dealing, NodeIndex};
use serde_derive::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
use crate::coconut::dkg::state::DkgParticipant;
#[derive(Debug, Default, Serialize, Deserialize)]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub struct DealingExchangeState {
pub(crate) dealers: BTreeMap<NodeIndex, DkgParticipant>,
@@ -0,0 +1,11 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use serde_derive::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub struct InProgressState {
// indicate whether this node has been in this state before and performed any one-off tasks
pub(crate) entered: bool,
}
@@ -4,7 +4,6 @@
use super::serde_helpers::recovered_keys;
use cosmwasm_std::Addr;
use nym_coconut_dkg_common::types::{DealingIndex, EpochId};
use nym_dkg::error::DkgError;
use nym_dkg::{G2Projective, NodeIndex, RecoveredVerificationKeys, Threshold};
use serde_derive::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
@@ -69,7 +68,8 @@ pub enum DealerRejectionReason {
},
}
#[derive(Debug, Default, Serialize, Deserialize)]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub struct KeyDerivationState {
pub(crate) expected_threshold: Option<Threshold>,
@@ -97,10 +97,6 @@ impl KeyDerivationState {
Some(recovered)
}
pub fn completed(&self) -> bool {
self.completed.is_some()
}
pub fn completed_with_success(&self) -> bool {
matches!(self.completed, Some(Ok(_)))
}
@@ -3,7 +3,8 @@
use serde_derive::{Deserialize, Serialize};
#[derive(Debug, Default, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub struct FinalizationState {
pub(crate) completed: bool,
}
@@ -6,7 +6,8 @@ use std::collections::HashMap;
type ProposalId = u64;
#[derive(Debug, Default, Serialize, Deserialize)]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub struct ValidationState {
pub votes: HashMap<ProposalId, bool>,
+46 -369
View File
@@ -1,214 +1,50 @@
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::coconut::dkg::complaints::ComplaintReason;
use crate::coconut::dkg::controller::keys::archive_coconut_keypair;
use crate::coconut::dkg::state::dealing_exchange::DealingExchangeState;
use crate::coconut::dkg::state::in_progress::InProgressState;
use crate::coconut::dkg::state::key_derivation::KeyDerivationState;
use crate::coconut::dkg::state::key_finalization::FinalizationState;
use crate::coconut::dkg::state::key_validation::ValidationState;
use crate::coconut::dkg::state::registration::RegistrationState;
use crate::coconut::dkg::state::registration::{
DkgParticipant, ParticipantState, RegistrationState,
};
use crate::coconut::error::CoconutError;
use crate::coconut::keys::{KeyPair as CoconutKeyPair, KeyPairWithEpoch};
use cosmwasm_std::Addr;
use log::debug;
use nym_coconut_dkg_common::dealer::DealerDetails;
use nym_coconut_dkg_common::types::{
DealingIndex, EncodedBTEPublicKeyWithProof, EpochId, EpochState,
};
use nym_coconut_dkg_common::types::EpochId;
use nym_crypto::asymmetric::identity;
use nym_dkg::bte::{keys::KeyPair as DkgKeyPair, PublicKey, PublicKeyWithProof};
use nym_dkg::{bte, Dealing, NodeIndex, RecoveredVerificationKeys, Threshold};
use nym_validator_client::nyxd::{tx, Hash};
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_helpers::{bte_pk_serde, generated_dealings_old, vks_serde};
use nym_dkg::bte::keys::KeyPair as DkgKeyPair;
use nym_dkg::{bte, NodeIndex, Threshold};
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
use std::path::{Path, PathBuf};
use time::OffsetDateTime;
use tokio::sync::RwLockReadGuard;
use url::Url;
pub(crate) mod dealing_exchange;
pub(crate) mod in_progress;
pub(crate) mod key_derivation;
pub(crate) mod key_finalization;
pub(crate) mod key_validation;
pub(crate) mod registration;
pub(crate) mod serde_helpers;
#[derive(Clone, Deserialize, Debug, Serialize)]
pub(crate) enum ParticipantState {
Invalid(ComplaintReason),
VerifiedKey(#[serde(with = "bte_pk_serde")] PublicKeyWithProof),
}
impl ParticipantState {
pub fn is_valid(&self) -> bool {
matches!(self, ParticipantState::VerifiedKey(..))
}
pub fn public_key(&self) -> Option<bte::PublicKey> {
match self {
ParticipantState::Invalid(_) => None,
ParticipantState::VerifiedKey(key_with_proof) => Some(*key_with_proof.public_key()),
}
}
fn from_raw_encoded_key(raw: EncodedBTEPublicKeyWithProof) -> Self {
// TODO: include more error information
let Ok(bytes) = bs58::decode(raw).into_vec() else {
return ParticipantState::Invalid(ComplaintReason::MalformedBTEPublicKey);
};
let Ok(key) = PublicKeyWithProof::try_from_bytes(&bytes) else {
return ParticipantState::Invalid(ComplaintReason::MalformedBTEPublicKey);
};
if !key.verify() {
return ParticipantState::Invalid(ComplaintReason::InvalidBTEPublicKey);
}
ParticipantState::VerifiedKey(key)
}
}
// note: each dealer is also a receiver which simplifies some logic significantly
#[derive(Clone, Deserialize, Debug, Serialize)]
pub(crate) struct DkgParticipant {
pub(crate) address: Addr,
pub(crate) assigned_index: NodeIndex,
pub(crate) state: ParticipantState,
}
impl DkgParticipant {
#[cfg(test)]
pub(crate) fn unwrap_key(&self) -> PublicKeyWithProof {
if let ParticipantState::VerifiedKey(key) = &self.state {
return key.clone();
}
panic!("no key")
}
}
impl From<DealerDetails> for DkgParticipant {
fn from(dealer: DealerDetails) -> Self {
DkgParticipant {
address: dealer.address,
state: ParticipantState::from_raw_encoded_key(dealer.bte_public_key_with_proof),
assigned_index: dealer.assigned_index,
}
}
}
#[async_trait]
pub(crate) trait ConsistentState {
fn node_index_value(&self) -> Result<NodeIndex, CoconutError>;
fn receiver_index_value(&self) -> Result<usize, CoconutError>;
fn threshold(&self) -> Result<Threshold, CoconutError>;
async fn coconut_keypair_is_some(&self) -> Result<(), CoconutError>;
fn proposal_id_value(&self) -> Result<u64, CoconutError>;
async fn is_consistent(&self, epoch_state: EpochState) -> Result<(), CoconutError> {
match epoch_state {
EpochState::WaitingInitialisation => {}
EpochState::PublicKeySubmission { .. } => {}
EpochState::DealingExchange { .. } => {
self.node_index_value()?;
}
EpochState::VerificationKeySubmission { .. } => {
self.receiver_index_value()?;
self.threshold()?;
}
EpochState::VerificationKeyValidation { .. } => {
self.coconut_keypair_is_some().await?;
}
EpochState::VerificationKeyFinalization { .. } => {
self.proposal_id_value()?;
}
EpochState::InProgress => {}
}
Ok(())
}
}
#[async_trait]
impl ConsistentState for State {
fn node_index_value(&self) -> Result<NodeIndex, CoconutError> {
self.node_index.ok_or(CoconutError::UnrecoverableState {
reason: String::from("Node index should have been set"),
})
}
fn receiver_index_value(&self) -> Result<usize, CoconutError> {
self.receiver_index.ok_or(CoconutError::UnrecoverableState {
reason: String::from("Receiver index should have been set"),
})
}
fn threshold(&self) -> Result<Threshold, CoconutError> {
let threshold = self.threshold.ok_or(CoconutError::UnrecoverableState {
reason: String::from("Threshold should have been set"),
})?;
if self.current_dealers_by_idx().len() < threshold as usize {
Err(CoconutError::UnrecoverableState {
reason: String::from(
"Not enough good dealers in the signer set to achieve threshold",
),
})
} else {
Ok(threshold)
}
}
async fn coconut_keypair_is_some(&self) -> Result<(), CoconutError> {
if self.coconut_keypair_is_some().await {
Ok(())
} else {
Err(CoconutError::UnrecoverableState {
reason: String::from("Coconut keypair should have been set"),
})
}
}
fn proposal_id_value(&self) -> Result<u64, CoconutError> {
self.proposal_id.ok_or(CoconutError::UnrecoverableState {
reason: String::from("Proposal id should have been set"),
})
}
}
#[derive(Deserialize, Serialize)]
pub(crate) struct PersistentState {
timestamp: OffsetDateTime,
//
node_index: Option<NodeIndex>,
dealers: BTreeMap<Addr, Result<DkgParticipant, ComplaintReason>>,
#[serde(with = "generated_dealings_old")]
generated_dealings: HashMap<EpochId, HashMap<DealingIndex, Dealing>>,
receiver_index: Option<usize>,
threshold: Option<Threshold>,
#[serde(with = "vks_serde")]
recovered_vks: Vec<RecoveredVerificationKeys>,
proposal_id: Option<u64>,
voted_vks: bool,
executed_proposal: bool,
was_in_progress: bool,
dkg_instances: HashMap<EpochId, DkgState>,
}
impl Default for PersistentState {
fn default() -> Self {
PersistentState {
timestamp: OffsetDateTime::now_utc(),
node_index: None,
dealers: Default::default(),
generated_dealings: Default::default(),
receiver_index: None,
threshold: None,
recovered_vks: vec![],
proposal_id: None,
voted_vks: false,
executed_proposal: false,
was_in_progress: false,
dkg_instances: Default::default(),
}
}
}
@@ -217,16 +53,8 @@ impl From<&State> for PersistentState {
fn from(s: &State) -> Self {
PersistentState {
timestamp: OffsetDateTime::now_utc(),
node_index: s.node_index,
dealers: s.dealers.clone(),
generated_dealings: s.generated_dealings.clone(),
receiver_index: s.receiver_index,
threshold: s.threshold,
recovered_vks: s.recovered_vks.clone(),
proposal_id: s.proposal_id,
voted_vks: s.voted_vks,
executed_proposal: s.executed_proposal,
was_in_progress: s.was_in_progress,
dkg_instances: s.dkg_instances.clone(),
}
}
}
@@ -243,7 +71,7 @@ impl PersistentState {
}
}
#[derive(Default)]
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub(crate) struct DkgState {
pub(crate) registration: RegistrationState,
@@ -254,6 +82,8 @@ pub(crate) struct DkgState {
pub(crate) key_validation: ValidationState,
pub(crate) key_finalization: FinalizationState,
pub(crate) in_progress: InProgressState,
}
impl DkgState {
@@ -261,11 +91,9 @@ impl DkgState {
assert!(self.dealing_exchange.dealers.is_empty());
for raw_dealer in raw_dealers {
let dkg_participant = DkgParticipant::from(raw_dealer);
if let ParticipantState::Invalid(complaint) = &dkg_participant.state {
warn!(
"{} dealer is malformed: {complaint}",
dkg_participant.address
)
let address = &dkg_participant.address;
if let ParticipantState::Invalid(rejection) = &dkg_participant.state {
warn!("{address} dealer is malformed: {rejection}",)
}
self.dealing_exchange
.dealers
@@ -280,35 +108,13 @@ pub(crate) struct State {
dkg_instances: HashMap<EpochId, DkgState>,
//
#[deprecated]
announce_address: Url,
#[deprecated]
identity_key: identity::PublicKey,
#[deprecated]
dkg_keypair: DkgKeyPair,
#[deprecated]
coconut_keypair: CoconutKeyPair,
#[deprecated]
node_index: Option<NodeIndex>,
#[deprecated]
dealers: BTreeMap<Addr, Result<DkgParticipant, ComplaintReason>>,
#[deprecated]
generated_dealings: HashMap<EpochId, HashMap<DealingIndex, Dealing>>,
#[deprecated]
receiver_index: Option<usize>,
#[deprecated]
threshold: Option<Threshold>,
#[deprecated]
recovered_vks: Vec<RecoveredVerificationKeys>,
#[deprecated]
proposal_id: Option<u64>,
#[deprecated]
voted_vks: bool,
#[deprecated]
executed_proposal: bool,
#[deprecated]
was_in_progress: bool,
}
impl State {
@@ -322,21 +128,11 @@ impl State {
) -> Self {
State {
persistent_state_path,
dkg_instances: Default::default(),
dkg_instances: persistent_state.dkg_instances,
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,
}
}
@@ -344,22 +140,15 @@ impl State {
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.invalidate();
pub fn clear_previous_epoch(&mut self, current_epoch: EpochId) {
if let Some(previous) = current_epoch.checked_sub(1) {
self.dkg_instances.remove(&previous);
}
self.node_index = Default::default();
self.dealers = Default::default();
self.receiver_index = Default::default();
self.threshold = Default::default();
self.recovered_vks = Default::default();
self.proposal_id = Default::default();
self.voted_vks = Default::default();
self.executed_proposal = Default::default();
self.was_in_progress = Default::default();
}
pub fn maybe_init_dkg_state(&mut self, epoch_id: EpochId) {
// given we're not using that entry here, I think the explicit check and insert is more readable
#[allow(clippy::map_entry)]
if !self.dkg_instances.contains_key(&epoch_id) {
self.dkg_instances.insert(epoch_id, Default::default());
}
@@ -428,6 +217,23 @@ impl State {
.ok_or(CoconutError::MissingDkgState { epoch_id })
}
pub fn in_progress_state(&self, epoch_id: EpochId) -> Result<&InProgressState, CoconutError> {
self.dkg_instances
.get(&epoch_id)
.map(|state| &state.in_progress)
.ok_or(CoconutError::MissingDkgState { epoch_id })
}
pub fn in_progress_state_mut(
&mut self,
epoch_id: EpochId,
) -> Result<&mut InProgressState, CoconutError> {
self.dkg_instances
.get_mut(&epoch_id)
.map(|state| &mut state.in_progress)
.ok_or(CoconutError::MissingDkgState { epoch_id })
}
pub fn dealing_exchange_state(
&self,
epoch_id: EpochId,
@@ -564,142 +370,13 @@ impl State {
self.coconut_keypair.validate()
}
pub fn get_dealing(&self, epoch_id: EpochId, dealing_index: DealingIndex) -> Option<&Dealing> {
self.generated_dealings
.get(&epoch_id)
.and_then(|epoch_dealings| epoch_dealings.get(&dealing_index))
}
pub fn store_dealing(
&mut self,
epoch_id: EpochId,
dealing_index: DealingIndex,
dealing: Dealing,
) {
self.generated_dealings
.entry(epoch_id)
.or_default()
.insert(dealing_index, dealing);
}
pub async fn coconut_keypair(
&self,
) -> Option<tokio::sync::RwLockReadGuard<'_, Option<KeyPairWithEpoch>>> {
self.coconut_keypair.get().await
}
pub async fn unchecked_coconut_keypair(
&self,
) -> tokio::sync::RwLockReadGuard<'_, Option<KeyPairWithEpoch>> {
self.coconut_keypair.read_keys().await
}
pub fn node_index(&self) -> Option<NodeIndex> {
self.node_index
}
pub fn current_dealers_by_addr(&self) -> BTreeMap<Addr, NodeIndex> {
self.dealers
.iter()
.filter_map(|(addr, dealer)| {
dealer
.as_ref()
.ok()
.map(|participant| (addr.clone(), participant.assigned_index))
})
.collect()
}
// FIXME: BUG: if we remove dealers, we won't be able to verify shares of other parties...
pub fn current_dealers_by_idx(&self) -> BTreeMap<NodeIndex, PublicKey> {
todo!()
// self.dealers
// .iter()
// .filter_map(|(_, dealer)| {
// dealer.as_ref().ok().map(|participant| {
// (
// participant.assigned_index,
// *participant.bte_public_key_with_proof.public_key(),
// )
// })
// })
// .collect()
}
pub fn recovered_vks(&self) -> &Vec<RecoveredVerificationKeys> {
&self.recovered_vks
}
pub fn voted_vks(&self) -> bool {
self.voted_vks
}
pub fn executed_proposal(&self) -> bool {
self.executed_proposal
}
pub fn was_in_progress(&self) -> bool {
self.was_in_progress
}
pub fn set_recovered_vks(&mut self, recovered_vks: Vec<RecoveredVerificationKeys>) {
self.recovered_vks = recovered_vks;
}
pub async fn set_coconut_keypair(&mut self, coconut_keypair: KeyPairWithEpoch) {
self.coconut_keypair.set(coconut_keypair).await
}
pub fn set_node_index(&mut self, node_index: Option<NodeIndex>) {
self.node_index = node_index;
}
// pub fn set_dealers(&mut self, dealers: Vec<DealerDetails>) {
// self.dealers = BTreeMap::from_iter(
// dealers
// .into_iter()
// .map(|details| (details.address.clone(), DkgParticipant::try_from(details))),
// )
// }
pub fn mark_bad_dealer(&mut self, dealer_addr: &Addr, reason: ComplaintReason) {
if let Some((_, value)) = self
.dealers
.iter_mut()
.find(|(addr, _)| *addr == dealer_addr)
{
debug!(
"Dealer {dealer_addr} misbehaved: {reason:?}. It will be marked locally as bad dealer and ignored",
);
*value = Err(reason);
}
}
pub fn set_receiver_index(&mut self, receiver_index: Option<usize>) {
self.receiver_index = receiver_index;
}
pub fn set_threshold(&mut self, threshold: Option<Threshold>) {
self.threshold = threshold;
}
pub fn set_proposal_id(&mut self, proposal_id: u64) {
self.proposal_id = Some(proposal_id);
}
pub fn set_voted_vks(&mut self) {
self.voted_vks = true;
}
pub fn set_executed_proposal(&mut self) {
self.executed_proposal = true;
}
pub fn set_was_in_progress(&mut self) {
self.was_in_progress = true;
}
pub fn all_dealers(&self) -> &BTreeMap<Addr, Result<DkgParticipant, ComplaintReason>> {
&self.dealers
}
}
+97 -2
View File
@@ -1,10 +1,105 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use nym_dkg::NodeIndex;
use crate::coconut::dkg::state::serde_helpers::bte_pk_serde;
use cosmwasm_std::Addr;
use nym_coconut_dkg_common::dealer::DealerDetails;
use nym_coconut_dkg_common::types::EncodedBTEPublicKeyWithProof;
use nym_dkg::bte::PublicKeyWithProof;
use nym_dkg::{bte, NodeIndex};
use serde_derive::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Debug, Default, Serialize, Deserialize)]
#[derive(Clone, Deserialize, Debug, Serialize)]
pub(crate) enum ParticipantState {
Invalid(KeyRejectionReason),
VerifiedKey(#[serde(with = "bte_pk_serde")] Box<PublicKeyWithProof>),
}
impl ParticipantState {
pub fn is_valid(&self) -> bool {
matches!(self, ParticipantState::VerifiedKey(..))
}
pub fn public_key(&self) -> Option<bte::PublicKey> {
match self {
ParticipantState::Invalid(_) => None,
ParticipantState::VerifiedKey(key_with_proof) => Some(*key_with_proof.public_key()),
}
}
fn from_raw_encoded_key(raw: EncodedBTEPublicKeyWithProof) -> Self {
let bytes = match bs58::decode(raw).into_vec() {
Ok(bytes) => bytes,
Err(err) => {
return ParticipantState::Invalid(
KeyRejectionReason::MalformedBTEPublicKeyEncoding {
err_msg: err.to_string(),
},
);
}
};
let key = match PublicKeyWithProof::try_from_bytes(&bytes) {
Ok(key) => key,
Err(err) => {
return ParticipantState::Invalid(KeyRejectionReason::MalformedBTEPublicKey {
err_msg: err.to_string(),
});
}
};
if !key.verify() {
return ParticipantState::Invalid(KeyRejectionReason::InvalidBTEPublicKey);
}
ParticipantState::VerifiedKey(Box::new(key))
}
}
// note: each dealer is also a receiver which simplifies some logic significantly
#[derive(Clone, Deserialize, Debug, Serialize)]
pub(crate) struct DkgParticipant {
pub(crate) address: Addr,
pub(crate) assigned_index: NodeIndex,
pub(crate) state: ParticipantState,
}
impl From<DealerDetails> for DkgParticipant {
fn from(dealer: DealerDetails) -> Self {
DkgParticipant {
address: dealer.address,
state: ParticipantState::from_raw_encoded_key(dealer.bte_public_key_with_proof),
assigned_index: dealer.assigned_index,
}
}
}
impl DkgParticipant {
#[cfg(test)]
pub(crate) fn unwrap_key(&self) -> PublicKeyWithProof {
if let ParticipantState::VerifiedKey(key) = &self.state {
return *key.clone();
}
panic!("no key")
}
}
#[derive(Clone, Error, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "lowercase")]
pub(crate) enum KeyRejectionReason {
#[error("provided BTE Public key encoding is malformed: {err_msg}")]
MalformedBTEPublicKeyEncoding { err_msg: String },
#[error("provided BTE Public key has invalid byte representation: {err_msg}")]
MalformedBTEPublicKey { err_msg: String },
#[error("provided BTE public key does not verify correctly")]
InvalidBTEPublicKey,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub struct RegistrationState {
pub(crate) assigned_index: Option<NodeIndex>,
}
+3 -71
View File
@@ -13,40 +13,14 @@ pub(super) mod bte_pk_serde {
val.to_bytes().serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<PublicKeyWithProof, D::Error>
pub fn deserialize<'de, D>(deserializer: D) -> Result<Box<PublicKeyWithProof>, D::Error>
where
D: Deserializer<'de>,
{
let vec: Vec<u8> = Deserialize::deserialize(deserializer)?;
PublicKeyWithProof::try_from_bytes(&vec)
.map_err(|err| Error::custom(format_args!("{:?}", err)))
}
}
pub(super) mod vks_serde {
use nym_dkg::RecoveredVerificationKeys;
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S: Serializer>(
val: &[RecoveredVerificationKeys],
serializer: S,
) -> Result<S::Ok, S::Error> {
let vec: Vec<Vec<u8>> = val.iter().map(|vk| vk.to_bytes()).collect();
vec.serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<RecoveredVerificationKeys>, D::Error>
where
D: Deserializer<'de>,
{
let vec: Vec<Vec<u8>> = Deserialize::deserialize(deserializer)?;
vec.into_iter()
.map(|b| {
RecoveredVerificationKeys::try_from_bytes(&b)
.map_err(|err| D::Error::custom(format_args!("{:?}", err)))
})
.collect()
.map(Box::new)
}
}
@@ -55,7 +29,7 @@ pub(super) mod recovered_keys {
use nym_dkg::RecoveredVerificationKeys;
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::collections::{BTreeMap, HashMap};
use std::collections::BTreeMap;
type Helper = BTreeMap<DealingIndex, Vec<u8>>;
@@ -120,45 +94,3 @@ pub(super) mod generated_dealings {
.collect()
}
}
pub(super) mod generated_dealings_old {
use nym_coconut_dkg_common::types::{DealingIndex, EpochId};
use nym_dkg::Dealing;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::collections::HashMap;
type Helper = HashMap<EpochId, HashMap<DealingIndex, Vec<u8>>>;
pub fn serialize<S: Serializer>(
dealings: &HashMap<EpochId, HashMap<DealingIndex, Dealing>>,
serializer: S,
) -> Result<S::Ok, S::Error> {
let mut helper = HashMap::new();
for (epoch, dealings) in dealings {
let mut inner = HashMap::new();
for (dealing_index, dealing) in dealings {
inner.insert(*dealing_index, dealing.to_bytes());
}
helper.insert(*epoch, inner);
}
helper.serialize(serializer)
}
pub fn deserialize<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<HashMap<EpochId, HashMap<DealingIndex, Dealing>>, D::Error> {
let helper = <Helper>::deserialize(deserializer)?;
let mut epoch_dealings = HashMap::with_capacity(helper.len());
for (epoch, dealings) in helper {
let mut inner = HashMap::with_capacity(dealings.len());
for (dealing_index, raw_dealing) in dealings {
let dealing =
Dealing::try_from_bytes(&raw_dealing).map_err(serde::de::Error::custom)?;
inner.insert(dealing_index, dealing);
}
epoch_dealings.insert(epoch, inner);
}
Ok(epoch_dealings)
}
}
+6 -20
View File
@@ -1,24 +1,20 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::node_status_api::models::NymApiStorageError;
use nym_coconut_dkg_common::types::EpochId;
use rocket::http::{ContentType, Status};
use rocket::response::Responder;
use rocket::{response, Request, Response};
use std::io::Cursor;
use std::path::PathBuf;
use thiserror::Error;
use nym_crypto::asymmetric::{
encryption::KeyRecoveryError,
identity::{Ed25519RecoveryError, SignatureError},
};
use nym_dkg::error::DkgError;
use nym_pemstore::KeyPairPath;
use nym_validator_client::coconut::CoconutApiError;
use nym_validator_client::nyxd::error::{NyxdError, TendermintError};
use crate::node_status_api::models::NymApiStorageError;
use rocket::http::{ContentType, Status};
use rocket::response::Responder;
use rocket::{response, Request, Response};
use std::io::Cursor;
use thiserror::Error;
pub type Result<T> = std::result::Result<T, CoconutError>;
@@ -121,16 +117,6 @@ pub enum CoconutError {
#[error("the coconut keypair is corrupted")]
CorruptedCoconutKeyPair,
#[error("failed to archive coconut key for epoch {epoch_id} using path {}: {source}", path.display())]
KeyArchiveFailure {
epoch_id: EpochId,
path: PathBuf,
// I hate that we're using anyhow error source here, but changing that would require bigger refactoring
#[source]
source: anyhow::Error,
},
#[error("there was a problem with the proposal id: {reason}")]
ProposalIdError { reason: String },
+2 -4
View File
@@ -1,16 +1,14 @@
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
mod persistence;
use nym_coconut_dkg_common::types::EpochId;
use nym_dkg::Scalar;
use std::collections::HashMap;
use std::ops::Deref;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use tokio::sync::{RwLock, RwLockReadGuard};
mod persistence;
#[derive(Clone, Debug)]
pub struct KeyPair {
// keys: Arc<RwLock<HashMap<EpochId, nym_coconut_interface::KeyPair>>>,
-1
View File
@@ -8,7 +8,6 @@ use crate::coconut::error::Result;
use crate::coconut::keys::KeyPair;
use crate::coconut::storage::CoconutStorageExt;
use crate::support::storage::NymApiStorage;
use lazy_static::lazy_static;
use nym_api_requests::coconut::helpers::issued_credential_plaintext;
use nym_api_requests::coconut::BlindSignRequestBody;
use nym_coconut::Parameters;
+1 -1
View File
@@ -188,7 +188,7 @@ impl TestingDkgControllerBuilder {
}
TestingDkgController {
controller: DkgController::test_mock_new(
controller: DkgController::test_mock(
rng,
dummy_client,
state,
+9 -4
View File
@@ -4,7 +4,9 @@
#[macro_use]
extern crate rocket;
use crate::coconut::dkg::controller::keys::{load_bte_keypair, load_coconut_keypair_if_exists};
use crate::coconut::dkg::controller::keys::{
can_validate_coconut_keys, load_bte_keypair, load_coconut_keypair_if_exists,
};
use crate::epoch_operations::RewardedSetUpdater;
use crate::network::models::NetworkDetails;
use crate::node_describe_cache::DescribedNodes;
@@ -16,7 +18,6 @@ use crate::support::storage;
use crate::support::storage::NymApiStorage;
use ::ephemera::configuration::Configuration as EphemeraConfiguration;
use ::nym_config::defaults::setup_env;
use anyhow::Result;
use circulating_supply_api::cache::CirculatingSupplyCache;
use clap::Parser;
use coconut::dkg::controller::DkgController;
@@ -73,8 +74,12 @@ 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)? {
todo!()
// coconut_keypair_wrapper.set(loaded_keys).await
let issued_for = loaded_keys.issued_for_epoch;
coconut_keypair_wrapper.set(loaded_keys).await;
if can_validate_coconut_keys(&nyxd_client, issued_for).await? {
coconut_keypair_wrapper.validate()
}
}
let identity_keypair = config.base.storage_paths.load_identity()?;