diff --git a/common/client-libs/validator-client/src/nymd/mod.rs b/common/client-libs/validator-client/src/nymd/mod.rs index 8bb25136e9..7b153d77d8 100644 --- a/common/client-libs/validator-client/src/nymd/mod.rs +++ b/common/client-libs/validator-client/src/nymd/mod.rs @@ -1378,7 +1378,7 @@ impl NymdClient { // } } -fn cosmwasm_coin_to_cosmos_coin(coin: Coin) -> CosmosCoin { +pub(crate) fn cosmwasm_coin_to_cosmos_coin(coin: Coin) -> CosmosCoin { CosmosCoin { denom: coin.denom.parse().unwrap(), // this might be a bit iffy, cosmwasm coin stores value as u128, while cosmos does it as u64 diff --git a/common/client-libs/validator-client/src/nymd/traits/dkg_client.rs b/common/client-libs/validator-client/src/nymd/traits/dkg_client.rs index d1800c4fe1..440c666fbf 100644 --- a/common/client-libs/validator-client/src/nymd/traits/dkg_client.rs +++ b/common/client-libs/validator-client/src/nymd/traits/dkg_client.rs @@ -3,11 +3,14 @@ use crate::nymd::cosmwasm_client::types::ExecuteResult; use crate::nymd::error::NymdError; -use crate::nymd::{Fee, NymdClient, SigningCosmWasmClient}; +use crate::nymd::{cosmwasm_coin_to_cosmos_coin, Fee, NymdClient, SigningCosmWasmClient}; use async_trait::async_trait; use coconut_dkg_common::msg::ExecuteMsg as DkgExecuteMsg; use coconut_dkg_common::msg::QueryMsg as DkgQueryMsg; -use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, EncodedEd25519PublicKey, Epoch, BlacklistingResponse, PagedBlacklistingResponse, PagedDealerResponse}; +use coconut_dkg_common::types::{ + BlacklistingResponse, EncodedBTEPublicKeyWithProof, EncodedEd25519PublicKey, Epoch, + MinimumDepositResponse, PagedBlacklistingResponse, PagedDealerResponse, +}; #[async_trait] pub trait DkgClient { @@ -30,6 +33,7 @@ pub trait DkgClient { ) -> Result; async fn get_blacklisting(&self, dealer: String) -> Result; + async fn get_deposit_amount(&self) -> Result; async fn register_dealer( &self, @@ -107,6 +111,13 @@ where .await } + async fn get_deposit_amount(&self) -> Result { + let request = DkgQueryMsg::GetDepositAmount {}; + self.client + .query_contract_smart(self.coconut_dkg_contract_address()?, &request) + .await + } + async fn register_dealer( &self, identity: EncodedEd25519PublicKey, @@ -121,6 +132,7 @@ where owner_signature, host: listening_address, }; + let deposit = self.get_deposit_amount().await?; self.client .execute( @@ -129,7 +141,7 @@ where &req, fee.unwrap_or_default(), format!("registering {} as a dealer", self.address()), - Vec::new(), + vec![cosmwasm_coin_to_cosmos_coin(deposit.amount)], ) .await } diff --git a/common/cosmwasm-smart-contracts/coconut-dkg-contract/src/msg.rs b/common/cosmwasm-smart-contracts/coconut-dkg-contract/src/msg.rs index dfc36e046a..758451bf6d 100644 --- a/common/cosmwasm-smart-contracts/coconut-dkg-contract/src/msg.rs +++ b/common/cosmwasm-smart-contracts/coconut-dkg-contract/src/msg.rs @@ -53,6 +53,7 @@ pub enum QueryMsg { GetBlacklisting { dealer: String, }, + GetDepositAmount {}, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] diff --git a/common/cosmwasm-smart-contracts/coconut-dkg-contract/src/types.rs b/common/cosmwasm-smart-contracts/coconut-dkg-contract/src/types.rs index 41f79eed3c..8320e2ad0b 100644 --- a/common/cosmwasm-smart-contracts/coconut-dkg-contract/src/types.rs +++ b/common/cosmwasm-smart-contracts/coconut-dkg-contract/src/types.rs @@ -7,11 +7,13 @@ pub use crate::dealer::{ BlacklistedDealer, Blacklisting, BlacklistingReason, BlacklistingResponse, DealerDetails, PagedBlacklistingResponse, PagedDealerResponse, }; -pub use cosmwasm_std::Addr; +pub use cosmwasm_std::{Addr, Coin}; + pub type BlockHeight = u64; pub type EncodedEd25519PublicKey = String; pub type EncodedEd25519PublicKeyRef<'a> = &'a str; pub type EncodedBTEPublicKeyWithProof = String; +pub type EncodedBTEPublicKeyWithProofRef<'a> = &'a str; pub type NodeIndex = u64; pub type EpochId = u32; @@ -120,3 +122,15 @@ pub enum EpochState { finish_by: Option, }, } + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[serde(rename_all = "snake_case")] +pub struct MinimumDepositResponse { + pub amount: Coin, +} + +impl MinimumDepositResponse { + pub fn new(amount: Coin) -> Self { + MinimumDepositResponse { amount } + } +} diff --git a/contracts/coconut-dkg/src/dealers/queries.rs b/contracts/coconut-dkg/src/dealers/queries.rs index ec1c6390d9..1a09bcaec8 100644 --- a/contracts/coconut-dkg/src/dealers/queries.rs +++ b/contracts/coconut-dkg/src/dealers/queries.rs @@ -6,7 +6,6 @@ use crate::dealers::storage::{IndexedDealersMap, BLACKLISTED_DEALERS}; use coconut_dkg_common::dealer::{ BlacklistedDealer, BlacklistingResponse, PagedBlacklistingResponse, PagedDealerResponse, }; -use coconut_dkg_common::types::Blacklisting; use cosmwasm_std::{Deps, Order, StdResult}; use cw_storage_plus::Bound; diff --git a/contracts/coconut-dkg/src/lib.rs b/contracts/coconut-dkg/src/lib.rs index 695b4b6fcb..bfb7f733b2 100644 --- a/contracts/coconut-dkg/src/lib.rs +++ b/contracts/coconut-dkg/src/lib.rs @@ -1,6 +1,7 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::constants::MINIMUM_DEPOSIT; use crate::dealers::queries::{ query_blacklisted_dealers_paged, query_blacklisting, query_current_dealers_paged, query_past_dealers_paged, @@ -8,9 +9,10 @@ use crate::dealers::queries::{ use crate::epoch::queries::query_current_epoch; use crate::error::ContractError; use coconut_dkg_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; -use coconut_dkg_common::types::{Epoch, EpochState}; +use coconut_dkg_common::types::{Epoch, EpochState, MinimumDepositResponse}; +use config::defaults::STAKE_DENOM; use cosmwasm_std::{ - entry_point, to_binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, + entry_point, to_binary, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, }; use epoch::storage as epoch_storage; @@ -139,6 +141,10 @@ pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> Result to_binary(&query_blacklisting(deps, dealer)?)?, + QueryMsg::GetDepositAmount {} => to_binary(&MinimumDepositResponse::new(Coin::new( + MINIMUM_DEPOSIT.u128(), + STAKE_DENOM, + )))?, }; Ok(response) diff --git a/validator-api/src/dkg/main_loop/mod.rs b/validator-api/src/dkg/main_loop/mod.rs index d7f683255b..ea0d20e621 100644 --- a/validator-api/src/dkg/main_loop/mod.rs +++ b/validator-api/src/dkg/main_loop/mod.rs @@ -10,6 +10,8 @@ use coconut_dkg_common::types::{Addr, BlockHeight, DealerDetails}; use futures::channel::mpsc; use futures::StreamExt; use log::{debug, info, trace}; +use std::net::SocketAddr; +use validator_client::nymd::SigningCosmWasmClient; // essentially events originating from the contract watcher could only drive our state forward // (TODO: is it actually true? I guess we'll find out soon enough) @@ -26,20 +28,28 @@ pub(crate) struct ProcessingLoop { contract_publisher: Publisher, // network_sender: Sender + + // TODO: this HAS to go away from here as it doesn't fit. but I've put it here temporarily to validate dealer registration + network_host: SocketAddr, } -impl ProcessingLoop { +impl ProcessingLoop +where + C: SigningCosmWasmClient + Send + Sync, +{ pub(crate) fn new( dkg_state: DkgState, dispatcher_sender: DispatcherSender, contract_events_receiver: ContractEventsReceiver, contract_publisher: Publisher, + network_host: SocketAddr, ) -> Self { ProcessingLoop { dkg_state, dispatcher_sender, contract_events_receiver, contract_publisher, + network_host, } } @@ -106,7 +116,26 @@ impl ProcessingLoop { async fn process_new_key_submission(&self, height: BlockHeight) { debug!("attempting to register our own dealer keys for this round of dkg"); - info!(".... but that's not implemented yet...."); + + let chain_address = self.contract_publisher.get_address().await; + let registration = self + .dkg_state + .prepare_dealer_registration(chain_address, self.network_host.to_string()); + + if let Err(err) = self + .contract_publisher + .register_dealer( + registration.identity, + registration.bte_key, + registration.owner_signature, + registration.listening_address, + ) + .await + { + error!("failed to register our dealer - {}", err) + } else { + info!("registered our dealer for this DKG round") + } } async fn process_dealer_changes(&self, changes: Vec, height: BlockHeight) { diff --git a/validator-api/src/dkg/mod.rs b/validator-api/src/dkg/mod.rs index f8c0112a10..2f66e948a7 100644 --- a/validator-api/src/dkg/mod.rs +++ b/validator-api/src/dkg/mod.rs @@ -72,13 +72,14 @@ where config.contract_polling_interval, ); let publisher = smart_contract::Publisher::new(nyxd_client); - let mut net_listener = Listener::new(config.network_address, state_accessor); + let mut net_listener = Listener::new(config.network_address.clone(), state_accessor); let mut processing_loop = ProcessingLoop::new( state, dispatcher_sender, contracts_events_receiver, publisher, + config.network_address, ); tokio::spawn(async move { event_dispatcher.run().await }); diff --git a/validator-api/src/dkg/smart_contract/publisher/mod.rs b/validator-api/src/dkg/smart_contract/publisher/mod.rs index 0e764f36c0..b0a147f1cd 100644 --- a/validator-api/src/dkg/smart_contract/publisher/mod.rs +++ b/validator-api/src/dkg/smart_contract/publisher/mod.rs @@ -1,8 +1,10 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::dkg::error::DkgError; use crate::Client; -use validator_client::nymd::SigningCosmWasmClient; +use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, EncodedEd25519PublicKey}; +use validator_client::nymd::{AccountId, SigningCosmWasmClient}; pub(crate) struct Publisher { client: Client, @@ -16,6 +18,23 @@ where Publisher { client } } + pub(crate) async fn get_address(&self) -> AccountId { + self.client.address().await + } + + pub(crate) async fn register_dealer( + &self, + identity: EncodedEd25519PublicKey, + bte_key: EncodedBTEPublicKeyWithProof, + owner_signature: String, + listening_address: String, + ) -> Result<(), DkgError> { + self.client + .register_dealer(identity, bte_key, owner_signature, listening_address) + .await?; + Ok(()) + } + pub(crate) async fn submit_dealing_commitment(&self) { // self.client.submit_dealing_commitment().await; // diff --git a/validator-api/src/dkg/smart_contract/watcher/mod.rs b/validator-api/src/dkg/smart_contract/watcher/mod.rs index 07407e60b4..d723ac1216 100644 --- a/validator-api/src/dkg/smart_contract/watcher/mod.rs +++ b/validator-api/src/dkg/smart_contract/watcher/mod.rs @@ -133,6 +133,9 @@ where .await; } } + } else { + // TODO: change to trace + debug!("our dkg key is already registered in the dkg contract") } Ok(()) diff --git a/validator-api/src/dkg/state/mod.rs b/validator-api/src/dkg/state/mod.rs index 4df5e4fd93..34dac7233b 100644 --- a/validator-api/src/dkg/state/mod.rs +++ b/validator-api/src/dkg/state/mod.rs @@ -2,7 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 use crate::Client; -use coconut_dkg_common::types::{Addr, BlockHeight, DealerDetails, Epoch, NodeIndex}; +use coconut_dkg_common::types::{ + Addr, BlockHeight, DealerDetails, EncodedBTEPublicKeyWithProof, EncodedEd25519PublicKey, Epoch, + NodeIndex, +}; use crypto::asymmetric::identity; use dkg::{bte, Dealing}; use futures::lock::Mutex; @@ -17,7 +20,7 @@ mod accessor; use crate::dkg::error::DkgError; pub(crate) use accessor::StateAccessor; -use validator_client::nymd::SigningCosmWasmClient; +use validator_client::nymd::{AccountId, SigningCosmWasmClient}; type IdentityBytes = [u8; identity::PUBLIC_KEY_LENGTH]; @@ -99,6 +102,13 @@ pub struct ReceivedDealing { signature: identity::Signature, } +pub(crate) struct DealerRegistration { + pub(crate) identity: EncodedEd25519PublicKey, + pub(crate) bte_key: EncodedBTEPublicKeyWithProof, + pub(crate) owner_signature: String, + pub(crate) listening_address: String, +} + #[derive(Debug, Clone)] pub(crate) struct DkgState { inner_state: Arc>, @@ -286,4 +296,26 @@ impl DkgState { } } } + + pub(crate) fn prepare_dealer_registration( + &self, + chain_address: AccountId, + listening_address: String, + ) -> DealerRegistration { + let bte_key = bs58::encode(&self.keys.bte_public_key.to_bytes()).into_string(); + + // chain_address || host || bte_keys + let mut plaintext = chain_address.to_string(); + plaintext.push_str(&listening_address); + plaintext.push_str(&bte_key); + + let owner_signature = self.keys.identity.private_key().sign_text(&plaintext); + + DealerRegistration { + identity: self.keys.identity.public_key().to_base58_string(), + bte_key, + owner_signature, + listening_address, + } + } } diff --git a/validator-api/src/nymd_client.rs b/validator-api/src/nymd_client.rs index d9ad0f7261..6f78edda73 100644 --- a/validator-api/src/nymd_client.rs +++ b/validator-api/src/nymd_client.rs @@ -6,7 +6,8 @@ use crate::rewarded_set_updater::error::RewardingError; #[cfg(feature = "coconut")] use async_trait::async_trait; use coconut_dkg_common::types::{ - BlacklistedDealer, BlacklistingResponse, DealerDetails, Epoch as DkgEpoch, + BlacklistedDealer, BlacklistingResponse, Coin, DealerDetails, EncodedBTEPublicKeyWithProof, + EncodedEd25519PublicKey, Epoch as DkgEpoch, }; use config::defaults::{DEFAULT_NETWORK, DEFAULT_VALIDATOR_API_PORT}; use mixnet_contract_common::Interval; @@ -479,6 +480,31 @@ where self.0.read().await.nymd.get_blacklisting(address).await } + pub(crate) async fn get_minimum_deposit(&self) -> Result { + self.0 + .read() + .await + .nymd + .get_deposit_amount() + .await + .map(|res| res.amount) + } + + pub(crate) async fn register_dealer( + &self, + identity: EncodedEd25519PublicKey, + bte_key: EncodedBTEPublicKeyWithProof, + owner_signature: String, + listening_address: String, + ) -> Result { + self.0 + .write() + .await + .nymd + .register_dealer(identity, bte_key, owner_signature, listening_address, None) + .await + } + pub(crate) async fn submit_dealing_commitment( &self, epoch_id: u32,