actually working happy path with a unit test
This commit is contained in:
@@ -17,7 +17,7 @@ use crate::epoch_state::transactions::{
|
||||
use crate::error::ContractError;
|
||||
use crate::state::queries::query_state;
|
||||
use crate::state::storage::{DKG_ADMIN, MULTISIG, STATE};
|
||||
use crate::verification_key_shares::queries::query_vk_shares_paged;
|
||||
use crate::verification_key_shares::queries::{query_vk_share, query_vk_shares_paged};
|
||||
use crate::verification_key_shares::transactions::try_commit_verification_key_share;
|
||||
use crate::verification_key_shares::transactions::try_verify_verification_key_share;
|
||||
use cosmwasm_std::{
|
||||
@@ -159,6 +159,9 @@ pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> Result<QueryResponse,
|
||||
start_after,
|
||||
limit,
|
||||
)?)?,
|
||||
QueryMsg::GetVerificationKey { owner, epoch_id } => {
|
||||
to_binary(&query_vk_share(deps, owner, epoch_id)?)?
|
||||
}
|
||||
QueryMsg::GetVerificationKeys {
|
||||
epoch_id,
|
||||
limit,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::verification_key_shares::storage;
|
||||
@@ -6,7 +6,23 @@ use crate::verification_key_shares::storage::vk_shares;
|
||||
use cosmwasm_std::{Deps, Order, StdResult};
|
||||
use cw_storage_plus::Bound;
|
||||
use nym_coconut_dkg_common::types::EpochId;
|
||||
use nym_coconut_dkg_common::verification_key::PagedVKSharesResponse;
|
||||
use nym_coconut_dkg_common::verification_key::{PagedVKSharesResponse, VkShareResponse};
|
||||
|
||||
// TODO: unit tests
|
||||
pub fn query_vk_share(
|
||||
deps: Deps<'_>,
|
||||
owner: String,
|
||||
epoch_id: EpochId,
|
||||
) -> StdResult<VkShareResponse> {
|
||||
let owner = deps.api.addr_validate(&owner)?;
|
||||
let share = vk_shares().may_load(deps.storage, (&owner, epoch_id))?;
|
||||
|
||||
Ok(VkShareResponse {
|
||||
owner,
|
||||
epoch_id,
|
||||
share,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn query_vk_shares_paged(
|
||||
deps: Deps<'_>,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::constants::{VK_SHARES_EPOCH_ID_IDX_NAMESPACE, VK_SHARES_PK_NAMESPACE};
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
|
||||
@@ -58,7 +58,10 @@ pub async fn post_blind_sign(
|
||||
|
||||
// check if we have the signing key available
|
||||
debug!("checking if we actually have coconut keys derived...");
|
||||
let keypair_guard = state.coconut_keypair.get().await;
|
||||
let maybe_keypair_guard = state.coconut_keypair.get().await;
|
||||
let Some(keypair_guard) = maybe_keypair_guard.as_ref() else {
|
||||
return Err(CoconutError::KeyPairNotDerivedYet);
|
||||
};
|
||||
let Some(signing_key) = keypair_guard.as_ref() else {
|
||||
return Err(CoconutError::KeyPairNotDerivedYet);
|
||||
};
|
||||
|
||||
@@ -302,10 +302,11 @@ impl DkgController {
|
||||
rng: rand_chacha::ChaCha20Rng,
|
||||
dkg_client: DkgClient,
|
||||
state: State,
|
||||
coconut_key_path: PathBuf,
|
||||
) -> DkgController<rand_chacha::ChaCha20Rng> {
|
||||
DkgController {
|
||||
dkg_client,
|
||||
coconut_key_path: Default::default(),
|
||||
coconut_key_path,
|
||||
state,
|
||||
rng,
|
||||
polling_rate: Default::default(),
|
||||
|
||||
@@ -234,13 +234,6 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
|
||||
) -> Result<(), CoconutError> {
|
||||
let dealing_state = self.state.dealing_exchange_state(epoch_id)?;
|
||||
|
||||
// TODO:
|
||||
/*
|
||||
let receiver_index = receivers
|
||||
.keys()
|
||||
.position(|node_index| *node_index == dealer_index);
|
||||
*/
|
||||
|
||||
// check if we have already submitted the dealings
|
||||
if dealing_state.completed() {
|
||||
// the only way this could be a false positive is if the chain forked and blocks got reverted,
|
||||
@@ -277,6 +270,30 @@ impl<R: RngCore + CryptoRng> DkgController<R> {
|
||||
|
||||
self.state.dkg_state_mut(epoch_id)?.set_dealers(dealers);
|
||||
|
||||
// obtain our dealer index to correctly set receiver index (used for share decryption)
|
||||
let dealer_index = self.state.assigned_index(epoch_id)?;
|
||||
|
||||
// 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")
|
||||
};
|
||||
self.state
|
||||
.key_derivation_state_mut(epoch_id)?
|
||||
.expected_threshold = Some(threshold);
|
||||
|
||||
// establish our receiver index
|
||||
let sorted_dealers = &self.state.dkg_state(epoch_id)?.dealers;
|
||||
let Some(receiver_index) = sorted_dealers.keys().position(|idx| idx == &dealer_index)
|
||||
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 });
|
||||
};
|
||||
self.state
|
||||
.dealing_exchange_state_mut(epoch_id)?
|
||||
.receiver_index = Some(receiver_index);
|
||||
|
||||
// get the expected key size which will determine the number of dealings we need to construct
|
||||
let contract_state = self.dkg_client.get_contract_state().await?;
|
||||
let expected_key_size = contract_state.key_size;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,7 @@ use nym_coconut_dkg_common::types::{
|
||||
};
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_dkg::bte::{keys::KeyPair as DkgKeyPair, PublicKey, PublicKeyWithProof};
|
||||
use nym_dkg::{Dealing, NodeIndex, RecoveredVerificationKeys, Threshold};
|
||||
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};
|
||||
@@ -43,6 +43,13 @@ impl ParticipantState {
|
||||
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 {
|
||||
@@ -353,7 +360,7 @@ impl State {
|
||||
}
|
||||
|
||||
/// Obtain the list of dealers for the provided epoch that have submitted valid public keys.
|
||||
pub fn valid_epoch_dealers(
|
||||
pub fn valid_epoch_receivers(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
) -> Result<Vec<(Addr, NodeIndex)>, CoconutError> {
|
||||
@@ -371,6 +378,18 @@ impl State {
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub fn valid_epoch_receivers_keys(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
) -> Result<BTreeMap<NodeIndex, bte::PublicKey>, CoconutError> {
|
||||
Ok(self
|
||||
.dkg_state(epoch_id)?
|
||||
.dealers
|
||||
.values()
|
||||
.filter_map(|d| d.state.public_key().map(|k| (d.assigned_index, k)))
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub fn dkg_state(&self, epoch_id: EpochId) -> Result<&DkgState, CoconutError> {
|
||||
self.dkg_instances
|
||||
.get(&epoch_id)
|
||||
@@ -449,11 +468,11 @@ impl State {
|
||||
.ok_or(CoconutError::UnavailableThreshold { epoch_id })
|
||||
}
|
||||
|
||||
// pub fn assigned_index(&self, epoch_id: EpochId) -> Result<NodeIndex, CoconutError> {
|
||||
// self.registration_state(epoch_id)?
|
||||
// .assigned_index
|
||||
// .ok_or(CoconutError::UnavailableAssignedIndex { epoch_id })
|
||||
// }
|
||||
pub fn assigned_index(&self, epoch_id: EpochId) -> Result<NodeIndex, CoconutError> {
|
||||
self.registration_state(epoch_id)?
|
||||
.assigned_index
|
||||
.ok_or(CoconutError::UnavailableAssignedIndex { epoch_id })
|
||||
}
|
||||
|
||||
pub fn receiver_index(&self, epoch_id: EpochId) -> Result<usize, CoconutError> {
|
||||
self.dealing_exchange_state(epoch_id)?
|
||||
@@ -509,7 +528,7 @@ impl State {
|
||||
|
||||
pub async fn coconut_keypair(
|
||||
&self,
|
||||
) -> tokio::sync::RwLockReadGuard<'_, Option<KeyPairWithEpoch>> {
|
||||
) -> Option<tokio::sync::RwLockReadGuard<'_, Option<KeyPairWithEpoch>>> {
|
||||
self.coconut_keypair.get().await
|
||||
}
|
||||
|
||||
|
||||
@@ -142,14 +142,14 @@ pub enum CoconutError {
|
||||
MissingDkgState { epoch_id: EpochId },
|
||||
|
||||
#[error(
|
||||
"the node index value for {epoch_id} is not available - are you sure we are a dealer?"
|
||||
"the node index value for epoch {epoch_id} is not available - are you sure we are a dealer?"
|
||||
)]
|
||||
UnavailableAssignedIndex { epoch_id: EpochId },
|
||||
|
||||
#[error("the receiver index value for {epoch_id} is not available - are you sure we are a receiver?")]
|
||||
#[error("the receiver index value for epoch {epoch_id} is not available - are you sure we are a receiver?")]
|
||||
UnavailableReceiverIndex { epoch_id: EpochId },
|
||||
|
||||
#[error("the threshold value for {epoch_id} is not available")]
|
||||
#[error("the threshold value for epoch {epoch_id} is not available")]
|
||||
UnavailableThreshold { epoch_id: EpochId },
|
||||
|
||||
#[error("insufficient number of dealings provided to derive the key")]
|
||||
|
||||
@@ -50,22 +50,28 @@ impl KeyPair {
|
||||
self.keys.write().await.take()
|
||||
}
|
||||
|
||||
pub async fn get(&self) -> RwLockReadGuard<'_, Option<KeyPairWithEpoch>> {
|
||||
todo!()
|
||||
// self.keys.read().await
|
||||
pub async fn get(&self) -> Option<RwLockReadGuard<'_, Option<KeyPairWithEpoch>>> {
|
||||
if self.is_valid() {
|
||||
Some(self.keys.read().await)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn set(&self, keypair: KeyPairWithEpoch) {
|
||||
todo!()
|
||||
// let mut w_lock = self.keys.write().await;
|
||||
// *w_lock = Some(keypair);
|
||||
let mut w_lock = self.keys.write().await;
|
||||
*w_lock = Some(keypair);
|
||||
}
|
||||
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.valid.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
pub fn invalidate(&self) {
|
||||
pub fn enable(&self) {
|
||||
self.valid.store(true, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
pub fn invalidate(&self) {
|
||||
self.valid.store(false, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ use rand_chacha::{
|
||||
};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tempfile::{tempdir, TempDir};
|
||||
|
||||
pub fn test_rng(seed: [u8; 32]) -> ChaCha20Rng {
|
||||
ChaCha20Rng::from_seed(seed)
|
||||
@@ -73,6 +74,8 @@ pub struct TestingDkgControllerBuilder {
|
||||
address: Option<AccountId>,
|
||||
threshold: Option<Threshold>,
|
||||
|
||||
chain_state: Option<Arc<Mutex<FakeChainState>>>,
|
||||
|
||||
self_dealer: Option<DealerDetails>,
|
||||
dealers: Vec<DealerDetails>,
|
||||
}
|
||||
@@ -88,6 +91,11 @@ impl TestingDkgControllerBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_shared_chain_state(mut self, fake_chain: Arc<Mutex<FakeChainState>>) -> Self {
|
||||
self.chain_state = Some(fake_chain);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_as_dealer(mut self, dealer_details: DealerDetails) -> Self {
|
||||
self.self_dealer = Some(dealer_details);
|
||||
self
|
||||
@@ -141,8 +149,11 @@ impl TestingDkgControllerBuilder {
|
||||
}
|
||||
});
|
||||
|
||||
let dummy_client = DummyClient::new(self_dealer.address.to_string().parse().unwrap());
|
||||
let chain_state = dummy_client.chain_state();
|
||||
let chain_state = self.chain_state.unwrap_or_default();
|
||||
let dummy_client = DummyClient::new(
|
||||
self_dealer.address.to_string().parse().unwrap(),
|
||||
chain_state.clone(),
|
||||
);
|
||||
// insert initial data into the chain state
|
||||
|
||||
let mut state_guard = chain_state.lock().unwrap();
|
||||
@@ -153,13 +164,14 @@ impl TestingDkgControllerBuilder {
|
||||
state_guard.dealers.insert(dealer.assigned_index, dealer);
|
||||
}
|
||||
|
||||
let epoch = state_guard.epoch.epoch_id;
|
||||
let epoch = state_guard.dkg_epoch.epoch_id;
|
||||
drop(state_guard);
|
||||
|
||||
let dummy_client = DkgClient::new(dummy_client);
|
||||
let tmp_dir = tempdir().unwrap();
|
||||
|
||||
let mut state = State::new(
|
||||
Default::default(),
|
||||
tmp_dir.path().join("persistent_state.json"),
|
||||
Default::default(),
|
||||
self_dealer.announce_address.parse().unwrap(),
|
||||
// TODO: we might need to fix up the key here
|
||||
@@ -176,8 +188,14 @@ impl TestingDkgControllerBuilder {
|
||||
}
|
||||
|
||||
TestingDkgController {
|
||||
controller: DkgController::test_mock_new(rng, dummy_client, state),
|
||||
controller: DkgController::test_mock_new(
|
||||
rng,
|
||||
dummy_client,
|
||||
state,
|
||||
tmp_dir.path().join("coconut_keypair.pem"),
|
||||
),
|
||||
chain_state,
|
||||
_tmp_dir: tmp_dir,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,6 +208,8 @@ pub(crate) struct TestingDkgController {
|
||||
pub(crate) controller: DkgController<ChaCha20Rng>,
|
||||
|
||||
pub(crate) chain_state: Arc<Mutex<FakeChainState>>,
|
||||
|
||||
_tmp_dir: TempDir,
|
||||
}
|
||||
|
||||
impl Deref for TestingDkgController {
|
||||
|
||||
@@ -1,10 +1,132 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::coconut::dkg::controller::DkgController;
|
||||
use crate::coconut::dkg::key_derivation::{
|
||||
verification_key_finalization, verification_key_validation,
|
||||
};
|
||||
use crate::coconut::tests::fixtures::{TestingDkgController, TestingDkgControllerBuilder};
|
||||
use crate::coconut::tests::FakeChainState;
|
||||
use nym_coconut_dkg_common::types::EpochState;
|
||||
use nym_crypto::aes::cipher::crypto_common::rand_core::OsRng;
|
||||
use nym_dkg::bte::PublicKeyWithProof;
|
||||
use nym_dkg::Dealing;
|
||||
use std::env::temp_dir;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
pub(crate) fn unchecked_decode_bte_key(raw: &str) -> PublicKeyWithProof {
|
||||
let bytes = bs58::decode(raw).into_vec().unwrap();
|
||||
PublicKeyWithProof::try_from_bytes(&bytes).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) type SharedChainState = Arc<Mutex<FakeChainState>>;
|
||||
|
||||
pub(crate) fn init_chain() -> SharedChainState {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub(crate) fn initialise_controllers(amount: usize) -> Vec<TestingDkgController> {
|
||||
let chain = init_chain();
|
||||
|
||||
let mut controllers = Vec::with_capacity(amount);
|
||||
assert!(amount <= u8::MAX as usize);
|
||||
for rng_seed in 0..amount {
|
||||
let controller = TestingDkgControllerBuilder::default()
|
||||
.with_shared_chain_state(chain.clone())
|
||||
.with_magic_seed_val(rng_seed as u8)
|
||||
.build();
|
||||
|
||||
controllers.push(controller)
|
||||
}
|
||||
|
||||
controllers
|
||||
}
|
||||
|
||||
pub(crate) fn initialise_dkg(controllers: &mut [TestingDkgController], resharing: bool) {
|
||||
assert_eq!(
|
||||
controllers[0].chain_state.lock().unwrap().dkg_epoch.state,
|
||||
EpochState::WaitingInitialisation
|
||||
);
|
||||
|
||||
controllers[0].chain_state.lock().unwrap().dkg_epoch.state =
|
||||
EpochState::PublicKeySubmission { resharing }
|
||||
}
|
||||
|
||||
pub(crate) async fn submit_public_keys(controllers: &mut [TestingDkgController], resharing: bool) {
|
||||
let epoch = controllers[0]
|
||||
.chain_state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.dkg_epoch
|
||||
.epoch_id;
|
||||
|
||||
for controller in controllers.iter_mut() {
|
||||
controller
|
||||
.public_key_submission(epoch, resharing)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let threshold = (2 * controllers.len() as u64 + 3 - 1) / 3;
|
||||
|
||||
let mut guard = controllers[0].chain_state.lock().unwrap();
|
||||
guard.dkg_epoch.state = EpochState::DealingExchange { resharing };
|
||||
guard.threshold = Some(threshold)
|
||||
}
|
||||
|
||||
pub(crate) async fn exchange_dealings(controllers: &mut [TestingDkgController], resharing: bool) {
|
||||
let epoch = controllers[0]
|
||||
.chain_state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.dkg_epoch
|
||||
.epoch_id;
|
||||
|
||||
for controller in controllers.iter_mut() {
|
||||
controller.dealing_exchange(epoch, resharing).await.unwrap();
|
||||
}
|
||||
|
||||
let mut guard = controllers[0].chain_state.lock().unwrap();
|
||||
guard.dkg_epoch.state = EpochState::VerificationKeySubmission { resharing };
|
||||
}
|
||||
|
||||
pub(crate) async fn derive_keypairs(controllers: &mut [TestingDkgController], resharing: bool) {
|
||||
let epoch = controllers[0]
|
||||
.chain_state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.dkg_epoch
|
||||
.epoch_id;
|
||||
|
||||
for controller in controllers.iter_mut() {
|
||||
controller
|
||||
.verification_key_submission(epoch, resharing)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let mut guard = controllers[0].chain_state.lock().unwrap();
|
||||
guard.dkg_epoch.state = EpochState::VerificationKeyValidation { resharing }
|
||||
}
|
||||
|
||||
pub(crate) async fn validate_keys(controllers: &mut [TestingDkgController], resharing: bool) {
|
||||
todo!()
|
||||
// let mut clients_and_states = derive_keypairs(db).await;
|
||||
// for controller in clients_and_states.iter_mut() {
|
||||
// verification_key_validation(&controller.dkg_client, &mut controller.state, false)
|
||||
// .await
|
||||
// .unwrap();
|
||||
// }
|
||||
// clients_and_states
|
||||
}
|
||||
|
||||
pub(crate) async fn finalize(controllers: &mut [TestingDkgController], resharing: bool) {
|
||||
todo!()
|
||||
// let mut clients_and_states = validate_keys(db).await;
|
||||
// for controller in clients_and_states.iter_mut() {
|
||||
// verification_key_finalization(&controller.dkg_client, &mut controller.state, false)
|
||||
// .await
|
||||
// .unwrap();
|
||||
// }
|
||||
// clients_and_states
|
||||
}
|
||||
|
||||
+215
-172
@@ -64,46 +64,47 @@ const TEST_REWARDING_VALIDATOR_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8g
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct FakeChainState {
|
||||
// new
|
||||
pub(crate) dealers: HashMap<NodeIndex, DealerDetails>,
|
||||
pub(crate) past_dealers: HashMap<NodeIndex, DealerDetails>,
|
||||
|
||||
pub(crate) dealings: HashMap<EpochId, HashMap<String, Vec<PartialContractDealing>>>,
|
||||
pub(crate) verification_shares: HashMap<EpochId, HashMap<String, ContractVKShare>>,
|
||||
|
||||
pub(crate) dkg_epoch: Epoch,
|
||||
pub(crate) dkg_contract_state: ContractState,
|
||||
pub(crate) threshold: Option<Threshold>,
|
||||
|
||||
pub(crate) node_index_counter: NodeIndex,
|
||||
|
||||
pub(crate) proposals: HashMap<u64, ProposalResponse>,
|
||||
// old
|
||||
txs: HashMap<Hash, TxResponse>,
|
||||
proposals: HashMap<u64, ProposalResponse>,
|
||||
spent_credentials: HashMap<String, SpendCredentialResponse>,
|
||||
|
||||
epoch: Epoch,
|
||||
contract_state: ContractState,
|
||||
old_dealers: HashMap<String, (DealerDetails, bool)>,
|
||||
threshold: Option<Threshold>,
|
||||
|
||||
verification_shares: HashMap<String, ContractVKShare>,
|
||||
group: HashMap<String, MemberResponse>,
|
||||
initial_dealers: Option<InitialReplacementData>,
|
||||
// txs: HashMap<Hash, TxResponse>,
|
||||
// spent_credentials: HashMap<String, SpendCredentialResponse>,
|
||||
//
|
||||
// old_dealers: HashMap<String, (DealerDetails, bool)>,
|
||||
//
|
||||
// group: HashMap<String, MemberResponse>,
|
||||
// initial_dealers: Option<InitialReplacementData>,
|
||||
}
|
||||
|
||||
impl Default for FakeChainState {
|
||||
fn default() -> Self {
|
||||
FakeChainState {
|
||||
dealers: HashMap::new(),
|
||||
past_dealers: Default::default(),
|
||||
|
||||
txs: HashMap::new(),
|
||||
proposals: HashMap::new(),
|
||||
spent_credentials: HashMap::new(),
|
||||
epoch: Epoch::default(),
|
||||
contract_state: ContractState {
|
||||
dkg_epoch: Epoch::default(),
|
||||
dkg_contract_state: ContractState {
|
||||
mix_denom: TEST_COIN_DENOM.to_string(),
|
||||
multisig_addr: Addr::unchecked("dummy address"),
|
||||
group_addr: Cw4Contract::new(Addr::unchecked("dummy cw4")),
|
||||
key_size: 5,
|
||||
},
|
||||
old_dealers: HashMap::new(),
|
||||
threshold: None,
|
||||
dealings: HashMap::new(),
|
||||
verification_shares: HashMap::new(),
|
||||
group: HashMap::new(),
|
||||
initial_dealers: None,
|
||||
threshold: None,
|
||||
node_index_counter: 0,
|
||||
proposals: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,10 +117,10 @@ pub(crate) struct DummyClient {
|
||||
}
|
||||
|
||||
impl DummyClient {
|
||||
pub fn new(validator_address: AccountId) -> Self {
|
||||
pub fn new(validator_address: AccountId, state: Arc<Mutex<FakeChainState>>) -> Self {
|
||||
Self {
|
||||
validator_address,
|
||||
state: Arc::new(Mutex::new(FakeChainState::default())),
|
||||
state,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,6 +220,26 @@ impl DummyClient {
|
||||
// self.initial_dealers_db = Arc::clone(initial_dealers);
|
||||
// self
|
||||
}
|
||||
|
||||
async fn get_dealer_by_address(&self, address: &str) -> Option<DealerDetails> {
|
||||
let guard = self.state.lock().unwrap();
|
||||
for dealer in guard.dealers.values() {
|
||||
if dealer.address.as_str() == address {
|
||||
return Some(dealer.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
async fn get_past_dealer_by_address(&self, address: &str) -> Option<DealerDetails> {
|
||||
let guard = self.state.lock().unwrap();
|
||||
for dealer in guard.past_dealers.values() {
|
||||
if dealer.address.as_str() == address {
|
||||
return Some(dealer.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -228,71 +249,76 @@ impl super::client::Client for DummyClient {
|
||||
}
|
||||
|
||||
async fn get_tx(&self, tx_hash: Hash) -> Result<TxResponse> {
|
||||
Ok(self
|
||||
.state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.txs
|
||||
.get(&tx_hash)
|
||||
.cloned()
|
||||
.unwrap())
|
||||
todo!()
|
||||
// Ok(self
|
||||
// .state
|
||||
// .lock()
|
||||
// .unwrap()
|
||||
// .txs
|
||||
// .get(&tx_hash)
|
||||
// .cloned()
|
||||
// .unwrap())
|
||||
}
|
||||
|
||||
async fn get_proposal(&self, proposal_id: u64) -> Result<ProposalResponse> {
|
||||
self.state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.proposals
|
||||
.get(&proposal_id)
|
||||
.cloned()
|
||||
.ok_or(CoconutError::IncorrectProposal {
|
||||
reason: String::from("proposal not found"),
|
||||
})
|
||||
todo!()
|
||||
// self.state
|
||||
// .lock()
|
||||
// .unwrap()
|
||||
// .proposals
|
||||
// .get(&proposal_id)
|
||||
// .cloned()
|
||||
// .ok_or(CoconutError::IncorrectProposal {
|
||||
// reason: String::from("proposal not found"),
|
||||
// })
|
||||
}
|
||||
|
||||
async fn list_proposals(&self) -> Result<Vec<ProposalResponse>> {
|
||||
Ok(self
|
||||
.state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.proposals
|
||||
.values()
|
||||
.cloned()
|
||||
.collect())
|
||||
todo!()
|
||||
// Ok(self
|
||||
// .state
|
||||
// .lock()
|
||||
// .unwrap()
|
||||
// .proposals
|
||||
// .values()
|
||||
// .cloned()
|
||||
// .collect())
|
||||
}
|
||||
|
||||
async fn get_spent_credential(
|
||||
&self,
|
||||
blinded_serial_number: String,
|
||||
) -> Result<SpendCredentialResponse> {
|
||||
self.state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.spent_credentials
|
||||
.get(&blinded_serial_number)
|
||||
.cloned()
|
||||
.ok_or(CoconutError::InvalidCredentialStatus {
|
||||
status: String::from("spent credential not found"),
|
||||
})
|
||||
todo!()
|
||||
// self.state
|
||||
// .lock()
|
||||
// .unwrap()
|
||||
// .spent_credentials
|
||||
// .get(&blinded_serial_number)
|
||||
// .cloned()
|
||||
// .ok_or(CoconutError::InvalidCredentialStatus {
|
||||
// status: String::from("spent credential not found"),
|
||||
// })
|
||||
}
|
||||
|
||||
async fn contract_state(&self) -> Result<ContractState> {
|
||||
Ok(self.state.lock().unwrap().contract_state.clone())
|
||||
Ok(self.state.lock().unwrap().dkg_contract_state.clone())
|
||||
}
|
||||
|
||||
async fn get_current_epoch(&self) -> Result<Epoch> {
|
||||
Ok(self.state.lock().unwrap().epoch)
|
||||
Ok(self.state.lock().unwrap().dkg_epoch)
|
||||
}
|
||||
|
||||
async fn group_member(&self, addr: String) -> Result<MemberResponse> {
|
||||
Ok(self
|
||||
.state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.group
|
||||
.get(&addr)
|
||||
.cloned()
|
||||
.unwrap_or(MemberResponse { weight: None }))
|
||||
todo!()
|
||||
// Ok(self
|
||||
// .state
|
||||
// .lock()
|
||||
// .unwrap()
|
||||
// .group
|
||||
// .get(&addr)
|
||||
// .cloned()
|
||||
// .unwrap_or(MemberResponse { weight: None }))
|
||||
}
|
||||
|
||||
async fn get_current_epoch_threshold(&self) -> Result<Option<Threshold>> {
|
||||
@@ -300,30 +326,30 @@ impl super::client::Client for DummyClient {
|
||||
}
|
||||
|
||||
async fn get_initial_dealers(&self) -> Result<Option<InitialReplacementData>> {
|
||||
Ok(self.state.lock().unwrap().initial_dealers.clone())
|
||||
todo!()
|
||||
// Ok(self.state.lock().unwrap().initial_dealers.clone())
|
||||
}
|
||||
|
||||
async fn get_self_registered_dealer_details(&self) -> Result<DealerDetailsResponse> {
|
||||
let (details, dealer_type) = if let Some((details, current)) = self
|
||||
.state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.old_dealers
|
||||
.get(self.validator_address.as_ref())
|
||||
.cloned()
|
||||
{
|
||||
let dealer_type = if current {
|
||||
DealerType::Current
|
||||
} else {
|
||||
DealerType::Past
|
||||
};
|
||||
(Some(details), dealer_type)
|
||||
} else {
|
||||
(None, DealerType::Unknown)
|
||||
};
|
||||
let address = self.validator_address.as_ref();
|
||||
|
||||
if let Some(details) = self.get_dealer_by_address(address).await {
|
||||
return Ok(DealerDetailsResponse {
|
||||
details: Some(details),
|
||||
dealer_type: DealerType::Current,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(details) = self.get_past_dealer_by_address(address).await {
|
||||
return Ok(DealerDetailsResponse {
|
||||
details: Some(details),
|
||||
dealer_type: DealerType::Past,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(DealerDetailsResponse {
|
||||
details,
|
||||
dealer_type,
|
||||
details: None,
|
||||
dealer_type: DealerType::Unknown,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -332,7 +358,7 @@ impl super::client::Client for DummyClient {
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
) -> crate::coconut::error::Result<DealingStatusResponse> {
|
||||
) -> Result<DealingStatusResponse> {
|
||||
let dealings = self.get_dealings(epoch_id, &dealer).await?;
|
||||
Ok(DealingStatusResponse {
|
||||
epoch_id,
|
||||
@@ -371,18 +397,28 @@ impl super::client::Client for DummyClient {
|
||||
.unwrap_or_default())
|
||||
}
|
||||
|
||||
async fn get_verification_key_shares(
|
||||
async fn get_verification_key_share(
|
||||
&self,
|
||||
_epoch_id: EpochId,
|
||||
) -> Result<Vec<ContractVKShare>> {
|
||||
Ok(self
|
||||
.state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.verification_shares
|
||||
.values()
|
||||
.cloned()
|
||||
.collect())
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
) -> Result<Option<ContractVKShare>> {
|
||||
let guard = self.state.lock().unwrap();
|
||||
let epoch_shares = guard.verification_shares.get(&epoch_id);
|
||||
|
||||
match epoch_shares {
|
||||
None => Ok(None),
|
||||
Some(epoch_shares) => Ok(epoch_shares.get(&dealer).cloned()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_verification_key_shares(&self, epoch_id: EpochId) -> Result<Vec<ContractVKShare>> {
|
||||
let guard = self.state.lock().unwrap();
|
||||
let epoch_shares = guard.verification_shares.get(&epoch_id);
|
||||
|
||||
match epoch_shares {
|
||||
None => Ok(Vec::new()),
|
||||
Some(epoch_shares) => Ok(epoch_shares.values().cloned().collect()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn vote_proposal(
|
||||
@@ -391,29 +427,31 @@ impl super::client::Client for DummyClient {
|
||||
vote_yes: bool,
|
||||
_fee: Option<Fee>,
|
||||
) -> Result<()> {
|
||||
if let Some(proposal) = self.state.lock().unwrap().proposals.get_mut(&proposal_id) {
|
||||
// for now, just suppose that every vote is honest
|
||||
if !vote_yes {
|
||||
proposal.status = cw3::Status::Rejected;
|
||||
} else if vote_yes && proposal.status == cw3::Status::Open {
|
||||
proposal.status = cw3::Status::Passed;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
todo!()
|
||||
// if let Some(proposal) = self.state.lock().unwrap().proposals.get_mut(&proposal_id) {
|
||||
// // for now, just suppose that every vote is honest
|
||||
// if !vote_yes {
|
||||
// proposal.status = cw3::Status::Rejected;
|
||||
// } else if vote_yes && proposal.status == cw3::Status::Open {
|
||||
// proposal.status = cw3::Status::Passed;
|
||||
// }
|
||||
// }
|
||||
// Ok(())
|
||||
}
|
||||
|
||||
async fn execute_proposal(&self, proposal_id: u64) -> Result<()> {
|
||||
self.state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.proposals
|
||||
.entry(proposal_id)
|
||||
.and_modify(|prop| {
|
||||
if prop.status == cw3::Status::Passed {
|
||||
prop.status = cw3::Status::Executed
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
todo!()
|
||||
// self.state
|
||||
// .lock()
|
||||
// .unwrap()
|
||||
// .proposals
|
||||
// .entry(proposal_id)
|
||||
// .and_modify(|prop| {
|
||||
// if prop.status == cw3::Status::Passed {
|
||||
// prop.status = cw3::Status::Executed
|
||||
// }
|
||||
// });
|
||||
// Ok(())
|
||||
}
|
||||
|
||||
async fn advance_epoch_state(&self) -> Result<()> {
|
||||
@@ -427,33 +465,39 @@ impl super::client::Client for DummyClient {
|
||||
announce_address: String,
|
||||
_resharing: bool,
|
||||
) -> Result<ExecuteResult> {
|
||||
let mut guard = self.state.lock().unwrap();
|
||||
let assigned_index = if let Some((details, active)) =
|
||||
guard.old_dealers.get_mut(self.validator_address.as_ref())
|
||||
let assigned_index = if let Some(already_registered) = self
|
||||
.get_dealer_by_address(self.validator_address.as_ref())
|
||||
.await
|
||||
{
|
||||
*active = true;
|
||||
details.assigned_index
|
||||
// current dealer
|
||||
already_registered.assigned_index
|
||||
} else if let Some(registered_in_the_past) = self
|
||||
.get_past_dealer_by_address(self.validator_address.as_ref())
|
||||
.await
|
||||
{
|
||||
// past dealer
|
||||
let index = registered_in_the_past.assigned_index;
|
||||
let mut guard = self.state.lock().unwrap();
|
||||
guard.dealers.insert(index, registered_in_the_past);
|
||||
|
||||
index
|
||||
} else {
|
||||
// new dealer
|
||||
let mut guard = self.state.lock().unwrap();
|
||||
|
||||
// let assigned_index = OsRng.gen();
|
||||
let assigned_index = guard
|
||||
.old_dealers
|
||||
.values()
|
||||
.map(|(d, _)| d.assigned_index)
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
+ 1;
|
||||
guard.old_dealers.insert(
|
||||
self.validator_address.to_string(),
|
||||
(
|
||||
DealerDetails {
|
||||
address: Addr::unchecked(self.validator_address.to_string()),
|
||||
bte_public_key_with_proof,
|
||||
ed25519_identity: identity_key,
|
||||
announce_address,
|
||||
assigned_index,
|
||||
},
|
||||
true,
|
||||
),
|
||||
guard.node_index_counter += 1;
|
||||
let assigned_index = guard.node_index_counter;
|
||||
|
||||
guard.dealers.insert(
|
||||
assigned_index,
|
||||
DealerDetails {
|
||||
address: Addr::unchecked(self.validator_address.to_string()),
|
||||
bte_public_key_with_proof,
|
||||
ed25519_identity: identity_key,
|
||||
announce_address,
|
||||
assigned_index,
|
||||
},
|
||||
);
|
||||
assigned_index
|
||||
};
|
||||
@@ -475,7 +519,7 @@ impl super::client::Client for DummyClient {
|
||||
_resharing: bool,
|
||||
) -> Result<ExecuteResult> {
|
||||
let mut guard = self.state.lock().unwrap();
|
||||
let current_epoch = guard.epoch.epoch_id;
|
||||
let current_epoch = guard.dkg_epoch.epoch_id;
|
||||
|
||||
let epoch_dealings = guard.dealings.entry(current_epoch).or_default();
|
||||
let existing_dealings = epoch_dealings
|
||||
@@ -496,33 +540,36 @@ impl super::client::Client for DummyClient {
|
||||
share: VerificationKeyShare,
|
||||
resharing: bool,
|
||||
) -> Result<ExecuteResult> {
|
||||
let (dealer_details, active) = self
|
||||
.state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.old_dealers
|
||||
.get(self.validator_address.as_ref())
|
||||
.unwrap()
|
||||
.clone();
|
||||
if !active {
|
||||
let address = self.validator_address.to_string();
|
||||
|
||||
let Some(dealer_details) = self.get_dealer_by_address(&address).await else {
|
||||
// Just throw some error, not really the correct one
|
||||
return Err(CoconutError::DepositEncrKeyNotFound);
|
||||
}
|
||||
self.state.lock().unwrap().verification_shares.insert(
|
||||
self.validator_address.to_string(),
|
||||
ContractVKShare {
|
||||
share,
|
||||
announce_address: dealer_details.announce_address.clone(),
|
||||
node_index: dealer_details.assigned_index,
|
||||
owner: Addr::unchecked(self.validator_address.to_string()),
|
||||
epoch_id: 0,
|
||||
verified: false,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
let mut guard = self.state.lock().unwrap();
|
||||
let epoch_id = guard.dkg_epoch.epoch_id;
|
||||
|
||||
guard
|
||||
.verification_shares
|
||||
.entry(epoch_id)
|
||||
.or_default()
|
||||
.insert(
|
||||
self.validator_address.to_string(),
|
||||
ContractVKShare {
|
||||
share,
|
||||
announce_address: dealer_details.announce_address.clone(),
|
||||
node_index: dealer_details.assigned_index,
|
||||
owner: Addr::unchecked(&address),
|
||||
epoch_id,
|
||||
verified: false,
|
||||
},
|
||||
);
|
||||
|
||||
let proposal_id = OsRng.gen();
|
||||
let verify_vk_share_req =
|
||||
nym_coconut_dkg_common::msg::ExecuteMsg::VerifyVerificationKeyShare {
|
||||
owner: Addr::unchecked(self.validator_address.as_ref()),
|
||||
owner: Addr::unchecked(&address),
|
||||
resharing,
|
||||
};
|
||||
let verify_vk_share_msg = CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
@@ -544,11 +591,7 @@ impl super::client::Client for DummyClient {
|
||||
proposer: Addr::unchecked(self.validator_address.as_ref()),
|
||||
deposit: None,
|
||||
};
|
||||
self.state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.proposals
|
||||
.insert(proposal_id, proposal);
|
||||
guard.proposals.insert(proposal_id, proposal);
|
||||
Ok(ExecuteResult {
|
||||
logs: vec![Log {
|
||||
msg_index: 0,
|
||||
|
||||
Reference in New Issue
Block a user