From 64c963e36e34abeb0254ca45b141af49a262013a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogdan-=C8=98tefan=20Neac=C5=9Fu?= Date: Thu, 19 Jan 2023 11:15:07 +0200 Subject: [PATCH] Feature/dkg rerun (#2839) * Reset contract state when dkg needs rerun * Reset nym-api for rerun * Gateway updates signer APIs at runtime * Fix clippy * Add epoch id * Use IndexedMap for shares * Query with epoch id * Add Clone to client traits * Pass nyxd client instead of api data * Get the specific epoch vk * Make wasm work * Remove wasm test runs As there are no wasm tests and the target_arch macros are not compatible with the cargo test environment, we can safely remove (for now) the wasm test target runs. * Put epoch_id in storage pk * Gateway uses old keys but current verifiers * Add group contract to env * Move group msg in common * Only run DKG if part of group * Clippy test * Rename wasm_storage to wasm_mockups * Update changelog --- .github/workflows/wasm_client_build.yml | 5 - CHANGELOG.md | 8 ++ Cargo.lock | 13 ++ Cargo.toml | 1 + Makefile | 5 +- .../client-core/src/client/base_client/mod.rs | 23 +-- clients/client-core/src/client/mix_traffic.rs | 17 ++- clients/client-core/src/init/helpers.rs | 6 +- clients/credential/src/commands.rs | 5 +- clients/native/src/client/mod.rs | 55 +++----- clients/socks5/src/client/mod.rs | 55 +++----- clients/webassembly/src/client/mod.rs | 3 +- common/client-libs/gateway-client/Cargo.toml | 4 +- .../gateway-client/src/bandwidth.rs | 48 ++++--- .../client-libs/gateway-client/src/client.rs | 19 ++- .../client-libs/gateway-client/src/error.rs | 2 +- common/client-libs/gateway-client/src/lib.rs | 2 +- .../src/{wasm_storage.rs => wasm_mockups.rs} | 14 ++ .../client-libs/validator-client/Cargo.toml | 3 + .../validator-client/src/client.rs | 29 +++- .../validator-client/src/nyxd/mod.rs | 18 ++- .../traits/coconut_bandwidth_query_client.rs | 2 +- .../coconut_bandwidth_signing_client.rs | 4 +- .../src/nyxd/traits/dkg_query_client.rs | 7 +- .../src/nyxd/traits/dkg_signing_client.rs | 2 +- .../src/nyxd/traits/group_query_client.rs | 28 ++++ .../src/nyxd/traits/mixnet_query_client.rs | 4 +- .../src/nyxd/traits/mixnet_signing_client.rs | 4 +- .../validator-client/src/nyxd/traits/mod.rs | 2 + .../src/nyxd/traits/multisig_query_client.rs | 2 +- .../nyxd/traits/multisig_signing_client.rs | 2 +- .../src/nyxd/traits/vesting_query_client.rs | 2 +- .../src/nyxd/traits/vesting_signing_client.rs | 2 +- common/coconut-interface/src/lib.rs | 13 +- .../coconut-dkg/src/msg.rs | 5 +- .../coconut-dkg/src/types.rs | 4 + .../coconut-dkg/src/verification_key.rs | 3 +- .../group-contract/Cargo.toml | 11 ++ .../group-contract/src/lib.rs | 1 + .../group-contract}/src/msg.rs | 3 + ...20230117120000_add_epoch_id_to_coconut.sql | 17 +++ common/credential-storage/src/coconut.rs | 5 +- common/credential-storage/src/lib.rs | 2 + common/credential-storage/src/models.rs | 1 + common/credential-storage/src/storage.rs | 6 + common/credentials/src/coconut/bandwidth.rs | 2 + common/credentials/src/coconut/utils.rs | 3 + common/network-defaults/src/lib.rs | 11 ++ common/network-defaults/src/mainnet.rs | 3 + common/network-defaults/src/var_names.rs | 1 + common/nymcoconut/src/tests/helpers.rs | 3 + contracts/coconut-dkg/Cargo.toml | 3 +- contracts/coconut-dkg/src/constants.rs | 3 + contracts/coconut-dkg/src/contract.rs | 14 +- .../src/epoch_state/transactions.rs | 132 ++++++++++++++---- .../coconut-dkg/src/epoch_state/utils.rs | 2 +- .../coconut-dkg/src/support/tests/fixtures.rs | 1 + .../src/verification_key_shares/queries.rs | 56 ++++---- .../src/verification_key_shares/storage.rs | 28 +++- .../verification_key_shares/transactions.rs | 15 +- contracts/coconut-test/Cargo.toml | 1 + .../src/spend_credential_creates_proposal.rs | 2 +- .../src/submit_vk_creates_proposal.rs | 4 +- .../multisig/cw3-flex-multisig/Cargo.toml | 1 + .../cw3-flex-multisig/src/contract.rs | 8 +- contracts/multisig/cw4-group/Cargo.toml | 2 + contracts/multisig/cw4-group/src/contract.rs | 2 +- contracts/multisig/cw4-group/src/helpers.rs | 2 +- contracts/multisig/cw4-group/src/lib.rs | 1 - envs/local.env | 5 +- envs/mainnet.env | 1 + envs/qa-qwerty.env | 1 + envs/qa.env | 1 + gateway/src/error.rs | 21 --- .../connection_handler/authenticated.rs | 34 ++++- .../websocket/connection_handler/coconut.rs | 45 +++--- .../websocket/connection_handler/mod.rs | 3 - gateway/src/node/mod.rs | 15 +- nym-api/Cargo.toml | 2 + nym-api/src/coconut/client.rs | 6 +- nym-api/src/coconut/comm.rs | 8 +- nym-api/src/coconut/dkg/client.rs | 14 +- nym-api/src/coconut/dkg/controller.rs | 20 ++- nym-api/src/coconut/dkg/public_key.rs | 18 ++- nym-api/src/coconut/dkg/state.rs | 27 +++- nym-api/src/coconut/dkg/verification_key.rs | 3 +- nym-api/src/coconut/keypair.rs | 4 +- nym-api/src/coconut/mod.rs | 11 +- nym-api/src/coconut/tests.rs | 28 ++-- nym-api/src/network_monitor/mod.rs | 16 +-- .../monitor/gateway_clients_cache.rs | 13 +- nym-api/src/network_monitor/monitor/sender.rs | 9 +- nym-api/src/support/cli/mod.rs | 2 +- nym-api/src/support/nyxd/mod.rs | 15 +- nym-wallet/Cargo.lock | 11 ++ nym-wallet/nym-wallet-types/src/network.rs | 4 + sdk/rust/nym-sdk/Cargo.toml | 1 + sdk/rust/nym-sdk/src/mixnet/client.rs | 3 +- 98 files changed, 754 insertions(+), 354 deletions(-) rename common/client-libs/gateway-client/src/{wasm_storage.rs => wasm_mockups.rs} (87%) create mode 100644 common/client-libs/validator-client/src/nyxd/traits/group_query_client.rs create mode 100644 common/cosmwasm-smart-contracts/group-contract/Cargo.toml create mode 100644 common/cosmwasm-smart-contracts/group-contract/src/lib.rs rename {contracts/multisig/cw4-group => common/cosmwasm-smart-contracts/group-contract}/src/msg.rs (93%) create mode 100644 common/credential-storage/migrations/20230117120000_add_epoch_id_to_coconut.sql diff --git a/.github/workflows/wasm_client_build.yml b/.github/workflows/wasm_client_build.yml index e1da3b04b0..4067e81939 100644 --- a/.github/workflows/wasm_client_build.yml +++ b/.github/workflows/wasm_client_build.yml @@ -29,11 +29,6 @@ jobs: command: build args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown --features=coconut - - uses: actions-rs/cargo@v1 - with: - command: test - args: --manifest-path clients/webassembly/Cargo.toml - - uses: actions-rs/cargo@v1 with: command: fmt diff --git a/CHANGELOG.md b/CHANGELOG.md index eb2be34ebd..c6b06fc4ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +- dkg rerun from scratch and dkg-specific epochs ([#2839]) + +[#2839]: https://github.com/nymtech/nym/pull/2839 + ## [v1.1.6] (2023-01-17) ### Added diff --git a/Cargo.lock b/Cargo.lock index fd88eec3be..0c896c97b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2368,6 +2368,15 @@ dependencies = [ "subtle 2.4.1", ] +[[package]] +name = "group-contract-common" +version = "0.1.0" +dependencies = [ + "cw4", + "schemars", + "serde", +] + [[package]] name = "h2" version = "0.3.15" @@ -3447,6 +3456,7 @@ dependencies = [ "crypto", "cw-utils", "cw3", + "cw4", "dirs", "dkg", "dotenv", @@ -3802,6 +3812,7 @@ dependencies = [ "tokio", "toml", "url", + "validator-client", ] [[package]] @@ -6860,9 +6871,11 @@ dependencies = [ "cosmrs", "cosmwasm-std", "cw3", + "cw4", "execute", "flate2", "futures", + "group-contract-common", "itertools", "log", "mixnet-contract-common", diff --git a/Cargo.toml b/Cargo.toml index ede4e57ca2..d0cd902eb0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ members = [ "common/cosmwasm-smart-contracts/coconut-bandwidth-contract", "common/cosmwasm-smart-contracts/coconut-dkg", "common/cosmwasm-smart-contracts/contracts-common", + "common/cosmwasm-smart-contracts/group-contract", "common/cosmwasm-smart-contracts/mixnet-contract", "common/cosmwasm-smart-contracts/multisig-contract", "common/cosmwasm-smart-contracts/vesting-contract", diff --git a/Makefile b/Makefile index 883be67453..558290cead 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ no-clippy: build cargo-test wasm fmt happy: fmt clippy-happy test clippy-all: clippy-main clippy-coconut clippy-all-contracts clippy-all-wallet clippy-all-connect clippy-all-wasm-client clippy-happy: clippy-happy-main clippy-happy-contracts clippy-happy-wallet clippy-happy-connect -cargo-test: test-main test-contracts test-wallet test-connect test-coconut test-wasm-client +cargo-test: test-main test-contracts test-wallet test-connect test-coconut cargo-test-expensive: test-main-expensive test-contracts-expensive test-wallet-expensive test-connect-expensive test-coconut-expensive build: build-contracts build-wallet build-main build-connect build-wasm-client fmt: fmt-main fmt-contracts fmt-wallet fmt-connect fmt-wasm-client @@ -68,9 +68,6 @@ test-wallet: test-wallet-expensive: cargo test --manifest-path nym-wallet/Cargo.toml --all-features -- --ignored -test-wasm-client: - cargo test --workspace --manifest-path clients/webassembly/Cargo.toml --all-features - test-connect: cargo test --manifest-path nym-connect/Cargo.toml --all-features diff --git a/clients/client-core/src/client/base_client/mod.rs b/clients/client-core/src/client/base_client/mod.rs index 25556c6240..6c02a1cbb8 100644 --- a/clients/client-core/src/client/base_client/mod.rs +++ b/clients/client-core/src/client/base_client/mod.rs @@ -25,6 +25,8 @@ use client_connections::{ConnectionCommandReceiver, ConnectionCommandSender, Lan use crypto::asymmetric::{encryption, identity}; use futures::channel::mpsc; use gateway_client::bandwidth::BandwidthController; +#[cfg(target_arch = "wasm32")] +use gateway_client::wasm_mockups::CosmWasmClient; use gateway_client::{ AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver, MixnetMessageSender, @@ -39,6 +41,8 @@ use std::time::Duration; use tap::TapFallible; use task::{TaskClient, TaskManager}; use url::Url; +#[cfg(not(target_arch = "wasm32"))] +use validator_client::nyxd::CosmWasmClient; use super::received_buffer::ReceivedBufferMessage; @@ -129,7 +133,7 @@ impl From for CredentialsToggle { } } -pub struct BaseClientBuilder<'a, B> { +pub struct BaseClientBuilder<'a, B, C: Clone> { // due to wasm limitations I had to split it like this : ( gateway_config: &'a GatewayEndpointConfig, debug_config: &'a DebugConfig, @@ -137,20 +141,21 @@ pub struct BaseClientBuilder<'a, B> { nym_api_endpoints: Vec, reply_storage_backend: B, - bandwidth_controller: Option, + bandwidth_controller: Option>, key_manager: KeyManager, } -impl<'a, B> BaseClientBuilder<'a, B> +impl<'a, B, C> BaseClientBuilder<'a, B, C> where B: ReplyStorageBackend + Send + Sync + 'static, + C: CosmWasmClient + Sync + Send + Clone + 'static, { pub fn new_from_base_config( base_config: &'a Config, key_manager: KeyManager, - bandwidth_controller: Option, + bandwidth_controller: Option>, reply_storage_backend: B, - ) -> BaseClientBuilder<'a, B> { + ) -> BaseClientBuilder<'a, B, C> { BaseClientBuilder { gateway_config: base_config.get_gateway_endpoint_config(), debug_config: base_config.get_debug_config(), @@ -166,11 +171,11 @@ where gateway_config: &'a GatewayEndpointConfig, debug_config: &'a DebugConfig, key_manager: KeyManager, - bandwidth_controller: Option, + bandwidth_controller: Option>, reply_storage_backend: B, credentials_toggle: CredentialsToggle, nym_api_endpoints: Vec, - ) -> BaseClientBuilder<'a, B> { + ) -> BaseClientBuilder<'a, B, C> { BaseClientBuilder { gateway_config, debug_config, @@ -279,7 +284,7 @@ where mixnet_message_sender: MixnetMessageSender, ack_sender: AcknowledgementSender, shutdown: TaskClient, - ) -> Result { + ) -> Result, ClientCoreError> { let gateway_id = self.gateway_config.gateway_id.clone(); if gateway_id.is_empty() { return Err(ClientCoreError::GatewayIdUnknown); @@ -365,7 +370,7 @@ where // over it. Perhaps GatewayClient needs to be thread-shareable or have some channel for // requests? fn start_mix_traffic_controller( - gateway_client: GatewayClient, + gateway_client: GatewayClient, shutdown: TaskClient, ) -> BatchMixMessageSender { info!("Starting mix traffic controller..."); diff --git a/clients/client-core/src/client/mix_traffic.rs b/clients/client-core/src/client/mix_traffic.rs index 114f04fb12..05d44824e7 100644 --- a/clients/client-core/src/client/mix_traffic.rs +++ b/clients/client-core/src/client/mix_traffic.rs @@ -2,9 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 use crate::spawn_future; +#[cfg(target_arch = "wasm32")] +use gateway_client::wasm_mockups::CosmWasmClient; use gateway_client::GatewayClient; use log::*; use nymsphinx::forwarding::packet::MixPacket; +#[cfg(not(target_arch = "wasm32"))] +use validator_client::nyxd::CosmWasmClient; pub type BatchMixMessageSender = tokio::sync::mpsc::Sender>; pub type BatchMixMessageReceiver = tokio::sync::mpsc::Receiver>; @@ -13,10 +17,10 @@ pub type BatchMixMessageReceiver = tokio::sync::mpsc::Receiver>; pub const MIX_MESSAGE_RECEIVER_BUFFER_SIZE: usize = 32; const MAX_FAILURE_COUNT: usize = 100; -pub struct MixTrafficController { +pub struct MixTrafficController { // TODO: most likely to be replaced by some higher level construct as // later on gateway_client will need to be accessible by other entities - gateway_client: GatewayClient, + gateway_client: GatewayClient, mix_rx: BatchMixMessageReceiver, // TODO: this is temporary work-around. @@ -24,8 +28,13 @@ pub struct MixTrafficController { consecutive_gateway_failure_count: usize, } -impl MixTrafficController { - pub fn new(gateway_client: GatewayClient) -> (MixTrafficController, BatchMixMessageSender) { +impl MixTrafficController +where + C: CosmWasmClient + Sync + Send + Clone + 'static, +{ + pub fn new( + gateway_client: GatewayClient, + ) -> (MixTrafficController, BatchMixMessageSender) { let (sphinx_message_sender, sphinx_message_receiver) = tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE); ( diff --git a/clients/client-core/src/init/helpers.rs b/clients/client-core/src/init/helpers.rs index cd22774ecb..cbc25000b6 100644 --- a/clients/client-core/src/init/helpers.rs +++ b/clients/client-core/src/init/helpers.rs @@ -8,6 +8,8 @@ use crate::{ }; use config::NymConfig; use crypto::asymmetric::identity; +#[cfg(target_arch = "wasm32")] +use gateway_client::wasm_mockups::SigningNyxdClient; use gateway_client::GatewayClient; use gateway_requests::registration::handshake::SharedKeys; use rand::{seq::SliceRandom, thread_rng}; @@ -15,6 +17,8 @@ use std::{sync::Arc, time::Duration}; use tap::TapFallible; use topology::{filter::VersionFilterable, gateway}; use url::Url; +#[cfg(not(target_arch = "wasm32"))] +use validator_client::nyxd::SigningNyxdClient; pub(super) async fn query_gateway_details( validator_servers: Vec, @@ -56,7 +60,7 @@ pub(super) async fn register_with_gateway( our_identity: Arc, ) -> Result, ClientCoreError> { let timeout = Duration::from_millis(1500); - let mut gateway_client = GatewayClient::new_init( + let mut gateway_client: GatewayClient = GatewayClient::new_init( gateway.clients_address(), gateway.identity_key, gateway.owner.clone(), diff --git a/clients/credential/src/commands.rs b/clients/credential/src/commands.rs index f697a98d7d..a007dbf766 100644 --- a/clients/credential/src/commands.rs +++ b/clients/credential/src/commands.rs @@ -13,6 +13,7 @@ use credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES}; use credentials::coconut::utils::obtain_aggregate_signature; use crypto::asymmetric::{encryption, identity}; use network_defaults::{NymNetworkDetails, VOUCHER_INFO}; +use validator_client::nyxd::traits::DkgQueryClient; use validator_client::nyxd::tx::Hash; use validator_client::{CoconutApiClient, Config}; @@ -80,7 +81,8 @@ pub(crate) async fn get_credential(state: &State, shared_storage: PersistentStor let network_details = NymNetworkDetails::new_from_env(); let config = Config::try_from_nym_network_details(&network_details)?; let client = validator_client::Client::new_query(config)?; - let coconut_api_clients = CoconutApiClient::all_coconut_api_clients(&client).await?; + let epoch_id = client.nyxd.get_current_epoch().await?.epoch_id; + let coconut_api_clients = CoconutApiClient::all_coconut_api_clients(&client, epoch_id).await?; let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap(); let bandwidth_credential_attributes = BandwidthVoucher::new( @@ -106,6 +108,7 @@ pub(crate) async fn get_credential(state: &State, shared_storage: PersistentStor bandwidth_credential_attributes.get_private_attributes()[0].to_bs58(), bandwidth_credential_attributes.get_private_attributes()[1].to_bs58(), signature.to_bs58(), + epoch_id.to_string(), ) .await?; diff --git a/clients/native/src/client/mod.rs b/clients/native/src/client/mod.rs index 4cf9e99e56..3c814fc3e7 100644 --- a/clients/native/src/client/mod.rs +++ b/clients/native/src/client/mod.rs @@ -21,6 +21,7 @@ use nymsphinx::addressing::clients::Recipient; use nymsphinx::anonymous_replies::requests::AnonymousSenderTag; use nymsphinx::receiver::ReconstructedMessage; use task::TaskManager; +use validator_client::nyxd::QueryNyxdClient; pub(crate) mod config; @@ -44,42 +45,28 @@ impl SocketClient { } } - async fn create_bandwidth_controller(config: &Config) -> BandwidthController { - #[cfg(feature = "coconut")] - let bandwidth_controller = { - let details = network_defaults::NymNetworkDetails::new_from_env(); - let mut client_config = - validator_client::Config::try_from_nym_network_details(&details) - .expect("failed to construct validator client config"); - let nyxd_url = config - .get_base() - .get_validator_endpoints() - .pop() - .expect("No nyxd validator endpoint provided"); - let api_url = config - .get_base() - .get_nym_api_endpoints() - .pop() - .expect("No validator api endpoint provided"); - // overwrite env configuration with config URLs - client_config = client_config.with_urls(nyxd_url, api_url); - let client = validator_client::Client::new_query(client_config) - .expect("Could not construct query client"); - let coconut_api_clients = - validator_client::CoconutApiClient::all_coconut_api_clients(&client) - .await - .expect("Could not query api clients"); - BandwidthController::new( - credential_storage::initialise_storage(config.get_base().get_database_path()).await, - coconut_api_clients, - ) - }; - #[cfg(not(feature = "coconut"))] - let bandwidth_controller = BandwidthController::new( + async fn create_bandwidth_controller(config: &Config) -> BandwidthController { + let details = network_defaults::NymNetworkDetails::new_from_env(); + let mut client_config = validator_client::Config::try_from_nym_network_details(&details) + .expect("failed to construct validator client config"); + let nyxd_url = config + .get_base() + .get_validator_endpoints() + .pop() + .expect("No nyxd validator endpoint provided"); + let api_url = config + .get_base() + .get_nym_api_endpoints() + .pop() + .expect("No validator api endpoint provided"); + // overwrite env configuration with config URLs + client_config = client_config.with_urls(nyxd_url, api_url); + let client = validator_client::Client::new_query(client_config) + .expect("Could not construct query client"); + BandwidthController::new( credential_storage::initialise_storage(config.get_base().get_database_path()).await, + client, ) - .expect("Could not create bandwidth controller"); - bandwidth_controller } fn start_websocket_listener( diff --git a/clients/socks5/src/client/mod.rs b/clients/socks5/src/client/mod.rs index aabc729d7e..88eba9e3ff 100644 --- a/clients/socks5/src/client/mod.rs +++ b/clients/socks5/src/client/mod.rs @@ -20,6 +20,7 @@ use log::*; use nymsphinx::addressing::clients::Recipient; use std::error::Error; use task::{TaskClient, TaskManager}; +use validator_client::nyxd::QueryNyxdClient; pub mod config; @@ -53,42 +54,28 @@ impl NymClient { } } - async fn create_bandwidth_controller(config: &Config) -> BandwidthController { - #[cfg(feature = "coconut")] - let bandwidth_controller = { - let details = network_defaults::NymNetworkDetails::new_from_env(); - let mut client_config = - validator_client::Config::try_from_nym_network_details(&details) - .expect("failed to construct validator client config"); - let nyxd_url = config - .get_base() - .get_validator_endpoints() - .pop() - .expect("No nyxd validator endpoint provided"); - let api_url = config - .get_base() - .get_nym_api_endpoints() - .pop() - .expect("No validator api endpoint provided"); - // overwrite env configuration with config URLs - client_config = client_config.with_urls(nyxd_url, api_url); - let client = validator_client::Client::new_query(client_config) - .expect("Could not construct query client"); - let coconut_api_clients = - validator_client::CoconutApiClient::all_coconut_api_clients(&client) - .await - .expect("Could not query api clients"); - BandwidthController::new( - credential_storage::initialise_storage(config.get_base().get_database_path()).await, - coconut_api_clients, - ) - }; - #[cfg(not(feature = "coconut"))] - let bandwidth_controller = BandwidthController::new( + async fn create_bandwidth_controller(config: &Config) -> BandwidthController { + let details = network_defaults::NymNetworkDetails::new_from_env(); + let mut client_config = validator_client::Config::try_from_nym_network_details(&details) + .expect("failed to construct validator client config"); + let nyxd_url = config + .get_base() + .get_validator_endpoints() + .pop() + .expect("No nyxd validator endpoint provided"); + let api_url = config + .get_base() + .get_nym_api_endpoints() + .pop() + .expect("No validator api endpoint provided"); + // overwrite env configuration with config URLs + client_config = client_config.with_urls(nyxd_url, api_url); + let client = validator_client::Client::new_query(client_config) + .expect("Could not construct query client"); + BandwidthController::new( credential_storage::initialise_storage(config.get_base().get_database_path()).await, + client, ) - .expect("Could not create bandwidth controller"); - bandwidth_controller } fn start_socks5_listener( diff --git a/clients/webassembly/src/client/mod.rs b/clients/webassembly/src/client/mod.rs index 01689febf0..306c2d85ed 100644 --- a/clients/webassembly/src/client/mod.rs +++ b/clients/webassembly/src/client/mod.rs @@ -11,6 +11,7 @@ use client_core::client::base_client::{ use client_core::client::replies::reply_storage::browser_backend; use client_core::client::{inbound_messages::InputMessage, key_manager::KeyManager}; use gateway_client::bandwidth::BandwidthController; +use gateway_client::wasm_mockups::SigningNyxdClient; use js_sys::Promise; use nymsphinx::addressing::clients::Recipient; use nymsphinx::anonymous_replies::requests::AnonymousSenderTag; @@ -47,7 +48,7 @@ pub struct NymClientBuilder { on_message: js_sys::Function, // unimplemented: - bandwidth_controller: Option, + bandwidth_controller: Option>, disabled_credentials: bool, } diff --git a/common/client-libs/gateway-client/Cargo.toml b/common/client-libs/gateway-client/Cargo.toml index 7dffddd6ad..ff65280643 100644 --- a/common/client-libs/gateway-client/Cargo.toml +++ b/common/client-libs/gateway-client/Cargo.toml @@ -25,7 +25,7 @@ gateway-requests = { path = "../../../gateway/gateway-requests" } network-defaults = { path = "../../network-defaults" } nymsphinx = { path = "../../nymsphinx" } pemstore = { path = "../../pemstore" } -validator-client = { path = "../validator-client", optional = true } +validator-client = { path = "../validator-client" } task = { path = "../../task" } serde = { version = "1.0", features = ["derive"]} @@ -79,5 +79,5 @@ features = ["js"] #url = "2.1" [features] -coconut = ["gateway-requests/coconut", "coconut-interface", "validator-client", "credentials/coconut"] +coconut = ["gateway-requests/coconut", "coconut-interface", "credentials/coconut"] wasm = [] diff --git a/common/client-libs/gateway-client/src/bandwidth.rs b/common/client-libs/gateway-client/src/bandwidth.rs index d6d1cb04f0..34fc308a48 100644 --- a/common/client-libs/gateway-client/src/bandwidth.rs +++ b/common/client-libs/gateway-client/src/bandwidth.rs @@ -1,23 +1,26 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +#[cfg(feature = "coconut")] use crate::error::GatewayClientError; #[cfg(target_arch = "wasm32")] -use crate::wasm_storage::Storage; +use crate::wasm_mockups::Storage; #[cfg(not(target_arch = "wasm32"))] use credential_storage::storage::Storage; #[cfg(all(target_arch = "wasm32", feature = "coconut"))] -use crate::wasm_storage::StorageError; +use crate::wasm_mockups::StorageError; #[cfg(all(not(target_arch = "wasm32"), feature = "coconut"))] use credential_storage::error::StorageError; +#[cfg(target_arch = "wasm32")] +use crate::wasm_mockups::{Client, CosmWasmClient}; #[cfg(feature = "coconut")] use std::str::FromStr; -#[cfg(feature = "coconut")] -use validator_client::client::CoconutApiClient; +#[cfg(not(target_arch = "wasm32"))] +use validator_client::{nyxd::CosmWasmClient, Client}; #[cfg(feature = "coconut")] use { coconut_interface::Base58, @@ -28,41 +31,34 @@ use { // TODO: make it nicer for wasm (I don't want to touch it for this experiment) #[cfg(target_arch = "wasm32")] -use crate::wasm_storage::PersistentStorage; +use crate::wasm_mockups::PersistentStorage; #[cfg(not(target_arch = "wasm32"))] use credential_storage::PersistentStorage; #[derive(Clone)] -pub struct BandwidthController { - #[allow(dead_code)] +#[allow(dead_code)] +pub struct BandwidthController { storage: St, - #[cfg(feature = "coconut")] - coconut_api_clients: Vec, + nyxd_client: Client, } -impl BandwidthController +impl BandwidthController where + C: CosmWasmClient + Sync + Send + Clone, St: Storage + Clone + 'static, { - #[cfg(feature = "coconut")] - pub fn new(storage: St, coconut_api_clients: Vec) -> Self { + pub fn new(storage: St, nyxd_client: Client) -> Self { BandwidthController { storage, - coconut_api_clients, + nyxd_client, } } - #[cfg(not(feature = "coconut"))] - pub fn new(storage: St) -> Result { - Ok(BandwidthController { storage }) - } - #[cfg(feature = "coconut")] pub async fn prepare_coconut_credential( &self, ) -> Result<(coconut_interface::Credential, i64), GatewayClientError> { - let verification_key = obtain_aggregate_verification_key(&self.coconut_api_clients).await?; let bandwidth_credential = self.storage.get_next_coconut_credential().await?; let voucher_value = u64::from_str(&bandwidth_credential.voucher_value) .map_err(|_| StorageError::InconsistentData)?; @@ -73,6 +69,19 @@ where coconut_interface::Attribute::try_from_bs58(bandwidth_credential.binding_number)?; let signature = coconut_interface::Signature::try_from_bs58(bandwidth_credential.signature)?; + let epoch_id = u64::from_str(&bandwidth_credential.epoch_id) + .map_err(|_| StorageError::InconsistentData)?; + + #[cfg(not(target_arch = "wasm32"))] + let coconut_api_clients = validator_client::CoconutApiClient::all_coconut_api_clients( + &self.nyxd_client, + epoch_id, + ) + .await + .expect("Could not query api clients"); + #[cfg(target_arch = "wasm32")] + let coconut_api_clients = vec![]; + let verification_key = obtain_aggregate_verification_key(&coconut_api_clients).await?; // the below would only be executed once we know where we want to spend it (i.e. which gateway and stuff) Ok(( @@ -81,6 +90,7 @@ where voucher_info, serial_number, binding_number, + epoch_id, &signature, &verification_key, )?, diff --git a/common/client-libs/gateway-client/src/client.rs b/common/client-libs/gateway-client/src/client.rs index 23cae7e4a6..b9e28a3852 100644 --- a/common/client-libs/gateway-client/src/client.rs +++ b/common/client-libs/gateway-client/src/client.rs @@ -32,9 +32,13 @@ use coconut_interface::Credential; use credential_storage::PersistentStorage; #[cfg(not(target_arch = "wasm32"))] use tokio_tungstenite::connect_async; +#[cfg(not(target_arch = "wasm32"))] +use validator_client::nyxd::CosmWasmClient; #[cfg(target_arch = "wasm32")] -use crate::wasm_storage::PersistentStorage; +use crate::wasm_mockups::CosmWasmClient; +#[cfg(target_arch = "wasm32")] +use crate::wasm_mockups::PersistentStorage; #[cfg(target_arch = "wasm32")] use wasm_timer; #[cfg(target_arch = "wasm32")] @@ -43,7 +47,7 @@ use wasm_utils::websocket::JSWebsocket; const DEFAULT_RECONNECTION_ATTEMPTS: usize = 10; const DEFAULT_RECONNECTION_BACKOFF: Duration = Duration::from_secs(5); -pub struct GatewayClient { +pub struct GatewayClient { authenticated: bool, disabled_credentials_mode: bool, bandwidth_remaining: i64, @@ -55,7 +59,7 @@ pub struct GatewayClient { connection: SocketState, packet_router: PacketRouter, response_timeout_duration: Duration, - bandwidth_controller: Option>, + bandwidth_controller: Option>, // reconnection related variables /// Specifies whether client should try to reconnect to gateway on connection failure. @@ -70,7 +74,10 @@ pub struct GatewayClient { shutdown: TaskClient, } -impl GatewayClient { +impl GatewayClient +where + C: CosmWasmClient + Sync + Send + Clone, +{ // TODO: put it all in a Config struct #[allow(clippy::too_many_arguments)] pub fn new( @@ -82,7 +89,7 @@ impl GatewayClient { mixnet_message_sender: MixnetMessageSender, ack_sender: AcknowledgementSender, response_timeout_duration: Duration, - bandwidth_controller: Option>, + bandwidth_controller: Option>, shutdown: TaskClient, ) -> Self { GatewayClient { @@ -138,7 +145,7 @@ impl GatewayClient { let shutdown = TaskClient::dummy(); let packet_router = PacketRouter::new(ack_tx, mix_tx, shutdown.clone()); - GatewayClient { + GatewayClient:: { authenticated: false, disabled_credentials_mode: true, bandwidth_remaining: 0, diff --git a/common/client-libs/gateway-client/src/error.rs b/common/client-libs/gateway-client/src/error.rs index 8a8104658b..61142d64be 100644 --- a/common/client-libs/gateway-client/src/error.rs +++ b/common/client-libs/gateway-client/src/error.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #[cfg(target_arch = "wasm32")] -use crate::wasm_storage::StorageError; +use crate::wasm_mockups::StorageError; #[cfg(not(target_arch = "wasm32"))] use credential_storage::error::StorageError; use gateway_requests::registration::handshake::error::HandshakeError; diff --git a/common/client-libs/gateway-client/src/lib.rs b/common/client-libs/gateway-client/src/lib.rs index ac209f0920..1dc95a71aa 100644 --- a/common/client-libs/gateway-client/src/lib.rs +++ b/common/client-libs/gateway-client/src/lib.rs @@ -17,7 +17,7 @@ pub mod error; pub mod packet_router; pub mod socket_state; #[cfg(target_arch = "wasm32")] -mod wasm_storage; +pub mod wasm_mockups; /// Helper method for reading from websocket stream. Helps to flatten the structure. pub(crate) fn cleanup_socket_message( diff --git a/common/client-libs/gateway-client/src/wasm_storage.rs b/common/client-libs/gateway-client/src/wasm_mockups.rs similarity index 87% rename from common/client-libs/gateway-client/src/wasm_storage.rs rename to common/client-libs/gateway-client/src/wasm_mockups.rs index 644560b9ca..55b07e367c 100644 --- a/common/client-libs/gateway-client/src/wasm_storage.rs +++ b/common/client-libs/gateway-client/src/wasm_mockups.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use async_trait::async_trait; +use std::marker::PhantomData; use thiserror::Error; #[derive(Error, Debug)] @@ -14,6 +15,18 @@ pub enum StorageError { InconsistentData, } +pub trait CosmWasmClient {} + +#[derive(Clone)] +pub struct SigningNyxdClient {} + +impl CosmWasmClient for SigningNyxdClient {} + +#[derive(Clone)] +pub struct Client { + _phantom: PhantomData, +} + #[derive(Clone)] pub struct PersistentStorage {} @@ -24,6 +37,7 @@ pub struct CoconutCredential { pub serial_number: String, pub binding_number: String, pub signature: String, + pub epoch_id: String, } #[async_trait] diff --git a/common/client-libs/validator-client/Cargo.toml b/common/client-libs/validator-client/Cargo.toml index 8b64ae3621..c6d467a0c4 100644 --- a/common/client-libs/validator-client/Cargo.toml +++ b/common/client-libs/validator-client/Cargo.toml @@ -17,6 +17,7 @@ mixnet-contract-common = { path= "../../cosmwasm-smart-contracts/mixnet-contract vesting-contract-common = { path= "../../cosmwasm-smart-contracts/vesting-contract" } coconut-bandwidth-contract-common = { path= "../../cosmwasm-smart-contracts/coconut-bandwidth-contract" } multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig-contract" } +group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contract" } vesting-contract = { path = "../../../contracts/vesting" } serde = { version = "1", features = ["derive"] } serde_json = "1" @@ -39,6 +40,7 @@ bip39 = { version = "1", features = ["rand"], optional = true } config = { path = "../../config", optional = true } cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", features = ["rpc", "bip32", "cosmwasm"], optional = true} cw3 = { version = "0.13.4", optional = true } +cw4 = { version = "0.13.4", optional = true } prost = { version = "0.10", default-features = false, optional = true } flate2 = { version = "1.0.20", optional = true } sha2 = { version = "0.9.5", optional = true } @@ -56,6 +58,7 @@ nyxd-client = [ "config", "cosmrs", "cw3", + "cw4", "prost", "flate2", "sha2", diff --git a/common/client-libs/validator-client/src/client.rs b/common/client-libs/validator-client/src/client.rs index 9ba5e6319b..d73d323876 100644 --- a/common/client-libs/validator-client/src/client.rs +++ b/common/client-libs/validator-client/src/client.rs @@ -21,7 +21,9 @@ use crate::nyxd::traits::{DkgQueryClient, MixnetQueryClient, MultisigQueryClient use crate::nyxd::{self, CosmWasmClient, NyxdClient, QueryNyxdClient, SigningNyxdClient}; #[cfg(feature = "nyxd-client")] use coconut_dkg_common::{ - dealer::ContractDealing, types::DealerDetails, verification_key::ContractVKShare, + dealer::ContractDealing, + types::{DealerDetails, EpochId}, + verification_key::ContractVKShare, }; #[cfg(feature = "nyxd-client")] use coconut_interface::Base58; @@ -128,7 +130,8 @@ impl Config { } #[cfg(feature = "nyxd-client")] -pub struct Client { +#[derive(Clone)] +pub struct Client { // TODO: we really shouldn't be storing a mnemonic here, but removing it would be // non-trivial amount of work and it's out of scope of the current branch mnemonic: Option, @@ -218,7 +221,10 @@ impl Client { // nyxd wrappers #[cfg(feature = "nyxd-client")] -impl Client { +impl Client +where + C: Clone, +{ // use case: somebody initialised client without a contract in order to upload and initialise one // and now they want to actually use it without making new client @@ -676,6 +682,7 @@ impl Client { pub async fn get_all_nyxd_verification_key_shares( &self, + epoch_id: EpochId, ) -> Result, ValidatorClientError> where C: CosmWasmClient + Sync + Send, @@ -685,7 +692,11 @@ impl Client { loop { let mut paged_response = self .nyxd - .get_vk_shares_paged(start_after.take(), self.verification_key_page_limit) + .get_vk_shares_paged( + epoch_id, + start_after.take(), + self.verification_key_page_limit, + ) .await?; shares.append(&mut paged_response.shares); @@ -730,7 +741,10 @@ impl Client { // validator-api wrappers #[cfg(feature = "nyxd-client")] -impl Client { +impl Client +where + C: Clone, +{ pub fn change_nym_api(&mut self, new_endpoint: Url) { self.nym_api.change_url(new_endpoint) } @@ -792,14 +806,15 @@ pub struct CoconutApiClient { #[cfg(feature = "nyxd-client")] impl CoconutApiClient { - pub async fn all_coconut_api_clients( + pub async fn all_coconut_api_clients( nyxd_client: &Client, + epoch_id: EpochId, ) -> Result, ValidatorClientError> where C: CosmWasmClient + Sync + Send, { Ok(nyxd_client - .get_all_nyxd_verification_key_shares() + .get_all_nyxd_verification_key_shares(epoch_id) .await? .into_iter() .filter_map(Self::try_from) diff --git a/common/client-libs/validator-client/src/nyxd/mod.rs b/common/client-libs/validator-client/src/nyxd/mod.rs index 6e19d7f3b3..fa92389e4c 100644 --- a/common/client-libs/validator-client/src/nyxd/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/mod.rs @@ -65,6 +65,7 @@ pub struct Config { pub(crate) vesting_contract_address: Option, pub(crate) bandwidth_claim_contract_address: Option, pub(crate) coconut_bandwidth_contract_address: Option, + pub(crate) group_contract_address: Option, pub(crate) multisig_contract_address: Option, pub(crate) coconut_dkg_contract_address: Option, // TODO: add this in later commits @@ -119,6 +120,10 @@ impl Config { .as_ref(), prefix, )?, + group_contract_address: Self::parse_optional_account( + details.contracts.group_contract_address.as_ref(), + prefix, + )?, multisig_contract_address: Self::parse_optional_account( details.contracts.multisig_contract_address.as_ref(), prefix, @@ -131,8 +136,8 @@ impl Config { } } -#[derive(Debug)] -pub struct NyxdClient { +#[derive(Clone, Debug)] +pub struct NyxdClient { client: C, config: Config, client_address: Option>, @@ -209,7 +214,10 @@ impl NyxdClient { } } -impl NyxdClient { +impl NyxdClient +where + C: Clone, +{ pub fn current_config(&self) -> &Config { &self.config } @@ -276,6 +284,10 @@ impl NyxdClient { .unwrap() } + pub fn group_contract_address(&self) -> &AccountId { + self.config.group_contract_address.as_ref().unwrap() + } + // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits // note: what unwrap is doing here is just moving a failure that would have normally // occurred in `connect` when attempting to parse an empty address, diff --git a/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_query_client.rs index 73c13d9d41..7cacd57801 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_query_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_query_client.rs @@ -18,7 +18,7 @@ pub trait CoconutBandwidthQueryClient { } #[async_trait] -impl CoconutBandwidthQueryClient for NyxdClient { +impl CoconutBandwidthQueryClient for NyxdClient { async fn get_spent_credential( &self, blinded_serial_number: String, diff --git a/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_signing_client.rs b/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_signing_client.rs index 0d0e11e9e8..a7d03c9625 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_signing_client.rs @@ -30,7 +30,9 @@ pub trait CoconutBandwidthSigningClient { } #[async_trait] -impl CoconutBandwidthSigningClient for NyxdClient { +impl CoconutBandwidthSigningClient + for NyxdClient +{ async fn deposit( &self, amount: Coin, diff --git a/common/client-libs/validator-client/src/nyxd/traits/dkg_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/dkg_query_client.rs index c9477fedd6..3b405caf9e 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/dkg_query_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/dkg_query_client.rs @@ -8,7 +8,7 @@ use coconut_dkg_common::dealer::{ DealerDetailsResponse, PagedDealerResponse, PagedDealingsResponse, }; use coconut_dkg_common::msg::QueryMsg as DkgQueryMsg; -use coconut_dkg_common::types::Epoch; +use coconut_dkg_common::types::{Epoch, EpochId}; use coconut_dkg_common::verification_key::PagedVKSharesResponse; use cosmrs::AccountId; @@ -39,6 +39,7 @@ pub trait DkgQueryClient { ) -> Result; async fn get_vk_shares_paged( &self, + epoch_id: EpochId, start_after: Option, page_limit: Option, ) -> Result; @@ -47,7 +48,7 @@ pub trait DkgQueryClient { #[async_trait] impl DkgQueryClient for NyxdClient where - C: CosmWasmClient + Send + Sync, + C: CosmWasmClient + Send + Sync + Clone, { async fn get_current_epoch(&self) -> Result { let request = DkgQueryMsg::GetCurrentEpochState {}; @@ -119,10 +120,12 @@ where async fn get_vk_shares_paged( &self, + epoch_id: EpochId, start_after: Option, page_limit: Option, ) -> Result { let request = DkgQueryMsg::GetVerificationKeys { + epoch_id, limit: page_limit, start_after, }; diff --git a/common/client-libs/validator-client/src/nyxd/traits/dkg_signing_client.rs b/common/client-libs/validator-client/src/nyxd/traits/dkg_signing_client.rs index f04730adb1..a025a84db0 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/dkg_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/dkg_signing_client.rs @@ -36,7 +36,7 @@ pub trait DkgSigningClient { #[async_trait] impl DkgSigningClient for NyxdClient where - C: SigningCosmWasmClient + Send + Sync, + C: SigningCosmWasmClient + Send + Sync + Clone, { async fn advance_dkg_epoch_state(&self, fee: Option) -> Result { let req = DkgExecuteMsg::AdvanceEpochState {}; diff --git a/common/client-libs/validator-client/src/nyxd/traits/group_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/group_query_client.rs new file mode 100644 index 0000000000..57a6f6a98f --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/traits/group_query_client.rs @@ -0,0 +1,28 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::nyxd::error::NyxdError; +use crate::nyxd::{CosmWasmClient, NyxdClient}; + +use group_contract_common::msg::QueryMsg; + +use async_trait::async_trait; +use cw4::MemberResponse; + +#[async_trait] +pub trait GroupQueryClient { + async fn member(&self, addr: String) -> Result; +} + +#[async_trait] +impl GroupQueryClient for NyxdClient { + async fn member(&self, addr: String) -> Result { + let request = QueryMsg::Member { + addr, + at_height: None, + }; + self.client + .query_contract_smart(self.group_contract_address(), &request) + .await + } +} diff --git a/common/client-libs/validator-client/src/nyxd/traits/mixnet_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/mixnet_query_client.rs index e589719fa3..97643bdabb 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/mixnet_query_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/mixnet_query_client.rs @@ -395,7 +395,7 @@ pub trait MixnetQueryClient { #[async_trait] impl MixnetQueryClient for NyxdClient where - C: CosmWasmClient + Sync + Send, + C: CosmWasmClient + Sync + Send + Clone, { async fn query_mixnet_contract(&self, query: MixnetQueryMsg) -> Result where @@ -410,7 +410,7 @@ where #[async_trait] impl MixnetQueryClient for crate::Client where - C: CosmWasmClient + Sync + Send, + C: CosmWasmClient + Sync + Send + Clone, { async fn query_mixnet_contract(&self, query: MixnetQueryMsg) -> Result where diff --git a/common/client-libs/validator-client/src/nyxd/traits/mixnet_signing_client.rs b/common/client-libs/validator-client/src/nyxd/traits/mixnet_signing_client.rs index f059674683..0288f73828 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/mixnet_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/mixnet_signing_client.rs @@ -627,7 +627,7 @@ pub trait MixnetSigningClient { #[async_trait] impl MixnetSigningClient for NyxdClient where - C: SigningCosmWasmClient + Sync + Send, + C: SigningCosmWasmClient + Sync + Send + Clone, { async fn execute_mixnet_contract( &self, @@ -653,7 +653,7 @@ where #[async_trait] impl MixnetSigningClient for crate::Client where - C: SigningCosmWasmClient + Sync + Send, + C: SigningCosmWasmClient + Sync + Send + Clone, { async fn execute_mixnet_contract( &self, diff --git a/common/client-libs/validator-client/src/nyxd/traits/mod.rs b/common/client-libs/validator-client/src/nyxd/traits/mod.rs index 11e70cd484..90e75adecd 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/mod.rs @@ -5,6 +5,7 @@ mod coconut_bandwidth_query_client; mod coconut_bandwidth_signing_client; mod dkg_query_client; mod dkg_signing_client; +mod group_query_client; mod mixnet_query_client; mod mixnet_signing_client; mod multisig_query_client; @@ -16,6 +17,7 @@ pub use coconut_bandwidth_query_client::CoconutBandwidthQueryClient; pub use coconut_bandwidth_signing_client::CoconutBandwidthSigningClient; pub use dkg_query_client::DkgQueryClient; pub use dkg_signing_client::DkgSigningClient; +pub use group_query_client::GroupQueryClient; pub use mixnet_query_client::MixnetQueryClient; pub use mixnet_signing_client::MixnetSigningClient; pub use multisig_query_client::MultisigQueryClient; diff --git a/common/client-libs/validator-client/src/nyxd/traits/multisig_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/multisig_query_client.rs index 51eac0532b..c396cecec4 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/multisig_query_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/multisig_query_client.rs @@ -20,7 +20,7 @@ pub trait MultisigQueryClient { } #[async_trait] -impl MultisigQueryClient for NyxdClient { +impl MultisigQueryClient for NyxdClient { async fn get_proposal(&self, proposal_id: u64) -> Result { let request = QueryMsg::Proposal { proposal_id }; self.client diff --git a/common/client-libs/validator-client/src/nyxd/traits/multisig_signing_client.rs b/common/client-libs/validator-client/src/nyxd/traits/multisig_signing_client.rs index 9051cfa79d..6e4069c4a2 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/multisig_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/multisig_signing_client.rs @@ -38,7 +38,7 @@ pub trait MultisigSigningClient { } #[async_trait] -impl MultisigSigningClient for NyxdClient { +impl MultisigSigningClient for NyxdClient { async fn propose_release_funds( &self, title: String, diff --git a/common/client-libs/validator-client/src/nyxd/traits/vesting_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/vesting_query_client.rs index 97eadce4bf..3d753e4bdc 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/vesting_query_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/vesting_query_client.rs @@ -269,7 +269,7 @@ pub trait VestingQueryClient { } #[async_trait] -impl VestingQueryClient for NyxdClient { +impl VestingQueryClient for NyxdClient { async fn query_vesting_contract(&self, query: VestingQueryMsg) -> Result where for<'a> T: Deserialize<'a>, diff --git a/common/client-libs/validator-client/src/nyxd/traits/vesting_signing_client.rs b/common/client-libs/validator-client/src/nyxd/traits/vesting_signing_client.rs index a4060943f8..0b34ae362b 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/vesting_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/vesting_signing_client.rs @@ -129,7 +129,7 @@ pub trait VestingSigningClient { } #[async_trait] -impl VestingSigningClient for NyxdClient { +impl VestingSigningClient for NyxdClient { async fn execute_vesting_contract( &self, fee: Option, diff --git a/common/coconut-interface/src/lib.rs b/common/coconut-interface/src/lib.rs index 7c4cd72213..e8ea53daed 100644 --- a/common/coconut-interface/src/lib.rs +++ b/common/coconut-interface/src/lib.rs @@ -18,6 +18,8 @@ pub struct Credential { theta: Theta, voucher_value: u64, voucher_info: String, + #[getset(get = "pub")] + epoch_id: u64, } impl Credential { pub fn new( @@ -25,12 +27,14 @@ impl Credential { theta: Theta, voucher_value: u64, voucher_info: String, + epoch_id: u64, ) -> Credential { Credential { n_params, theta, voucher_value, voucher_info, + epoch_id, } } @@ -68,6 +72,7 @@ impl Credential { let theta_bytes = self.theta.to_bytes(); let theta_bytes_len = theta_bytes.len(); let voucher_value_bytes = self.voucher_value.to_be_bytes(); + let epoch_id_bytes = self.epoch_id.to_be_bytes(); let voucher_info_bytes = self.voucher_info.as_bytes(); let voucher_info_len = voucher_info_bytes.len(); @@ -76,6 +81,7 @@ impl Credential { bytes.extend_from_slice(&(theta_bytes_len as u64).to_be_bytes()); bytes.extend_from_slice(&theta_bytes); bytes.extend_from_slice(&voucher_value_bytes); + bytes.extend_from_slice(&epoch_id_bytes); bytes.extend_from_slice(voucher_info_bytes); bytes @@ -103,7 +109,9 @@ impl Credential { .map_err(|e| CoconutError::Deserialization(e.to_string()))?; eight_byte.copy_from_slice(&bytes[12 + theta_len as usize..20 + theta_len as usize]); let voucher_value = u64::from_be_bytes(eight_byte); - let voucher_info = String::from_utf8(bytes[20 + theta_len as usize..].to_vec()) + eight_byte.copy_from_slice(&bytes[20 + theta_len as usize..28 + theta_len as usize]); + let epoch_id = u64::from_be_bytes(eight_byte); + let voucher_info = String::from_utf8(bytes[28 + theta_len as usize..].to_vec()) .map_err(|e| CoconutError::Deserialization(e.to_string()))?; Ok(Credential { @@ -111,6 +119,7 @@ impl Credential { theta, voucher_value, voucher_info, + epoch_id, }) } } @@ -166,7 +175,7 @@ mod tests { binding_number, ) .unwrap(); - let credential = Credential::new(4, theta, voucher_value, voucher_info); + let credential = Credential::new(4, theta, voucher_value, voucher_info, 42); let serialized_credential = credential.as_bytes(); let deserialized_credential = Credential::from_bytes(&serialized_credential).unwrap(); diff --git a/common/cosmwasm-smart-contracts/coconut-dkg/src/msg.rs b/common/cosmwasm-smart-contracts/coconut-dkg/src/msg.rs index 57582313b0..d3b59c9f1b 100644 --- a/common/cosmwasm-smart-contracts/coconut-dkg/src/msg.rs +++ b/common/cosmwasm-smart-contracts/coconut-dkg/src/msg.rs @@ -1,7 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::types::{ContractSafeBytes, EncodedBTEPublicKeyWithProof, TimeConfiguration}; +use crate::types::{ContractSafeBytes, EncodedBTEPublicKeyWithProof, EpochId, TimeConfiguration}; use crate::verification_key::VerificationKeyShare; use cosmwasm_std::Addr; use schemars::JsonSchema; @@ -35,6 +35,8 @@ pub enum ExecuteMsg { owner: Addr, }, + SurpassedThreshold {}, + AdvanceEpochState {}, } @@ -60,6 +62,7 @@ pub enum QueryMsg { start_after: Option, }, GetVerificationKeys { + epoch_id: EpochId, limit: Option, start_after: Option, }, diff --git a/common/cosmwasm-smart-contracts/coconut-dkg/src/types.rs b/common/cosmwasm-smart-contracts/coconut-dkg/src/types.rs index ad51688b6f..170a8d83f7 100644 --- a/common/cosmwasm-smart-contracts/coconut-dkg/src/types.rs +++ b/common/cosmwasm-smart-contracts/coconut-dkg/src/types.rs @@ -13,6 +13,7 @@ pub use cosmwasm_std::{Addr, Coin, Timestamp}; pub type EncodedBTEPublicKeyWithProof = String; pub type EncodedBTEPublicKeyWithProofRef<'a> = &'a str; pub type NodeIndex = u64; +pub type EpochId = u64; // 2 public attributes, 2 private attributes, 1 fixed for coconut credential pub const TOTAL_DEALINGS: usize = 2 + 2 + 1; @@ -72,6 +73,7 @@ impl Default for TimeConfiguration { #[serde(rename_all = "snake_case")] pub struct Epoch { pub state: EpochState, + pub epoch_id: EpochId, pub time_configuration: TimeConfiguration, pub finish_timestamp: Timestamp, } @@ -79,6 +81,7 @@ pub struct Epoch { impl Epoch { pub fn new( state: EpochState, + epoch_id: u64, time_configuration: TimeConfiguration, current_timestamp: Timestamp, ) -> Self { @@ -98,6 +101,7 @@ impl Epoch { }; Epoch { state, + epoch_id, time_configuration, finish_timestamp: current_timestamp.plus_seconds(duration), } diff --git a/common/cosmwasm-smart-contracts/coconut-dkg/src/verification_key.rs b/common/cosmwasm-smart-contracts/coconut-dkg/src/verification_key.rs index cfc3ffae03..2dcd222b91 100644 --- a/common/cosmwasm-smart-contracts/coconut-dkg/src/verification_key.rs +++ b/common/cosmwasm-smart-contracts/coconut-dkg/src/verification_key.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::msg::ExecuteMsg; -use crate::types::NodeIndex; +use crate::types::{EpochId, NodeIndex}; use cosmwasm_std::{from_binary, to_binary, Addr, CosmosMsg, StdResult, Timestamp, WasmMsg}; use cw_utils::Expiration; use multisig_contract_common::msg::ExecuteMsg as MultisigExecuteMsg; @@ -16,6 +16,7 @@ pub struct ContractVKShare { pub announce_address: String, pub node_index: NodeIndex, pub owner: Addr, + pub epoch_id: EpochId, pub verified: bool, } diff --git a/common/cosmwasm-smart-contracts/group-contract/Cargo.toml b/common/cosmwasm-smart-contracts/group-contract/Cargo.toml new file mode 100644 index 0000000000..6389f8fb3c --- /dev/null +++ b/common/cosmwasm-smart-contracts/group-contract/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "group-contract-common" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cw4 = { version = "0.13.4" } +schemars = "0.8" +serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/common/cosmwasm-smart-contracts/group-contract/src/lib.rs b/common/cosmwasm-smart-contracts/group-contract/src/lib.rs new file mode 100644 index 0000000000..d0e87a0d30 --- /dev/null +++ b/common/cosmwasm-smart-contracts/group-contract/src/lib.rs @@ -0,0 +1 @@ +pub mod msg; diff --git a/contracts/multisig/cw4-group/src/msg.rs b/common/cosmwasm-smart-contracts/group-contract/src/msg.rs similarity index 93% rename from contracts/multisig/cw4-group/src/msg.rs rename to common/cosmwasm-smart-contracts/group-contract/src/msg.rs index d3dd63388c..a5a473135d 100644 --- a/contracts/multisig/cw4-group/src/msg.rs +++ b/common/cosmwasm-smart-contracts/group-contract/src/msg.rs @@ -1,3 +1,6 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + use schemars::JsonSchema; use serde::{Deserialize, Serialize}; diff --git a/common/credential-storage/migrations/20230117120000_add_epoch_id_to_coconut.sql b/common/credential-storage/migrations/20230117120000_add_epoch_id_to_coconut.sql new file mode 100644 index 0000000000..2ecd81bcee --- /dev/null +++ b/common/credential-storage/migrations/20230117120000_add_epoch_id_to_coconut.sql @@ -0,0 +1,17 @@ +/* + * Copyright 2023 - Nym Technologies SA + * SPDX-License-Identifier: Apache-2.0 + */ + +DROP TABLE coconut_credentials; +CREATE TABLE coconut_credentials +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + voucher_value TEXT NOT NULL, + voucher_info TEXT NOT NULL, + serial_number TEXT NOT NULL, + binding_number TEXT NOT NULL, + signature TEXT NOT NULL UNIQUE, + epoch_id TEXT NOT NULL, + consumed BOOLEAN NOT NULL +); \ No newline at end of file diff --git a/common/credential-storage/src/coconut.rs b/common/credential-storage/src/coconut.rs index 103f8760d2..a6cd968c2e 100644 --- a/common/credential-storage/src/coconut.rs +++ b/common/credential-storage/src/coconut.rs @@ -34,10 +34,11 @@ impl CoconutCredentialManager { serial_number: String, binding_number: String, signature: String, + epoch_id: String, ) -> Result<(), sqlx::Error> { sqlx::query!( - "INSERT INTO coconut_credentials(voucher_value, voucher_info, serial_number, binding_number, signature, consumed) VALUES (?, ?, ?, ?, ?, ?)", - voucher_value, voucher_info, serial_number, binding_number, signature, false + "INSERT INTO coconut_credentials(voucher_value, voucher_info, serial_number, binding_number, signature, epoch_id, consumed) VALUES (?, ?, ?, ?, ?, ?, ?)", + voucher_value, voucher_info, serial_number, binding_number, signature, epoch_id, false ) .execute(&self.connection_pool) .await?; diff --git a/common/credential-storage/src/lib.rs b/common/credential-storage/src/lib.rs index d0a0992894..bf3af1b272 100644 --- a/common/credential-storage/src/lib.rs +++ b/common/credential-storage/src/lib.rs @@ -70,6 +70,7 @@ impl Storage for PersistentStorage { serial_number: String, binding_number: String, signature: String, + epoch_id: String, ) -> Result<(), StorageError> { self.coconut_credential_manager .insert_coconut_credential( @@ -78,6 +79,7 @@ impl Storage for PersistentStorage { serial_number, binding_number, signature, + epoch_id, ) .await?; diff --git a/common/credential-storage/src/models.rs b/common/credential-storage/src/models.rs index 6ce7a9dd4b..67f6e4790c 100644 --- a/common/credential-storage/src/models.rs +++ b/common/credential-storage/src/models.rs @@ -9,5 +9,6 @@ pub struct CoconutCredential { pub serial_number: String, pub binding_number: String, pub signature: String, + pub epoch_id: String, pub consumed: bool, } diff --git a/common/credential-storage/src/storage.rs b/common/credential-storage/src/storage.rs index 77df96f4c8..bf3285cae6 100644 --- a/common/credential-storage/src/storage.rs +++ b/common/credential-storage/src/storage.rs @@ -12,7 +12,12 @@ pub trait Storage: Send + Sync { /// /// # Arguments /// + /// * `voucher_value`: How much bandwidth is in the credential. + /// * `voucher_info`: What type of credential it is. + /// * `serial_number`: Serial number of the credential. + /// * `binding_number`: Binding number of the credential. /// * `signature`: Coconut credential in the form of a signature. + /// * `epoch_id`: The epoch when it was signed. async fn insert_coconut_credential( &self, voucher_value: String, @@ -20,6 +25,7 @@ pub trait Storage: Send + Sync { serial_number: String, binding_number: String, signature: String, + epoch_id: String, ) -> Result<(), StorageError>; /// Tries to retrieve one of the stored, unused credentials. diff --git a/common/credentials/src/coconut/bandwidth.rs b/common/credentials/src/coconut/bandwidth.rs index ed38fbb14c..53e9c5ed41 100644 --- a/common/credentials/src/coconut/bandwidth.rs +++ b/common/credentials/src/coconut/bandwidth.rs @@ -168,6 +168,7 @@ pub fn prepare_for_spending( voucher_info: String, serial_number: PrivateAttribute, binding_number: PrivateAttribute, + epoch_id: u64, signature: &Signature, verification_key: &VerificationKey, ) -> Result { @@ -179,6 +180,7 @@ pub fn prepare_for_spending( voucher_info, serial_number, binding_number, + epoch_id, signature, verification_key, ) diff --git a/common/credentials/src/coconut/utils.rs b/common/credentials/src/coconut/utils.rs index 53e01437af..12edebc3df 100644 --- a/common/credentials/src/coconut/utils.rs +++ b/common/credentials/src/coconut/utils.rs @@ -137,12 +137,14 @@ pub async fn obtain_aggregate_signature( } // TODO: better type flow +#[allow(clippy::too_many_arguments)] pub fn prepare_credential_for_spending( params: &Parameters, voucher_value: u64, voucher_info: String, serial_number: Attribute, binding_number: Attribute, + epoch_id: u64, signature: &Signature, verification_key: &VerificationKey, ) -> Result { @@ -159,5 +161,6 @@ pub fn prepare_credential_for_spending( theta, voucher_value, voucher_info, + epoch_id, )) } diff --git a/common/network-defaults/src/lib.rs b/common/network-defaults/src/lib.rs index dd16eb18d7..857177411e 100644 --- a/common/network-defaults/src/lib.rs +++ b/common/network-defaults/src/lib.rs @@ -36,6 +36,7 @@ pub struct NymContracts { pub vesting_contract_address: Option, pub bandwidth_claim_contract_address: Option, pub coconut_bandwidth_contract_address: Option, + pub group_contract_address: Option, pub multisig_contract_address: Option, pub coconut_dkg_contract_address: Option, } @@ -95,6 +96,9 @@ impl NymNetworkDetails { var(var_names::COCONUT_BANDWIDTH_CONTRACT_ADDRESS) .expect("coconut bandwidth contract not set"), )) + .with_group_contract(Some( + var(var_names::GROUP_CONTRACT_ADDRESS).expect("group contract not set"), + )) .with_multisig_contract(Some( var(var_names::MULTISIG_CONTRACT_ADDRESS).expect("multisig contract not set"), )) @@ -125,6 +129,7 @@ impl NymNetworkDetails { coconut_bandwidth_contract_address: parse_optional_str( mainnet::COCONUT_BANDWIDTH_CONTRACT_ADDRESS, ), + group_contract_address: parse_optional_str(mainnet::GROUP_CONTRACT_ADDRESS), multisig_contract_address: parse_optional_str(mainnet::MULTISIG_CONTRACT_ADDRESS), coconut_dkg_contract_address: parse_optional_str( mainnet::COCONUT_DKG_CONTRACT_ADDRESS, @@ -193,6 +198,12 @@ impl NymNetworkDetails { self } + #[must_use] + pub fn with_group_contract>(mut self, contract: Option) -> Self { + self.contracts.multisig_contract_address = contract.map(Into::into); + self + } + #[must_use] pub fn with_multisig_contract>(mut self, contract: Option) -> Self { self.contracts.multisig_contract_address = contract.map(Into::into); diff --git a/common/network-defaults/src/mainnet.rs b/common/network-defaults/src/mainnet.rs index 0fae7a4d78..3e37633b3f 100644 --- a/common/network-defaults/src/mainnet.rs +++ b/common/network-defaults/src/mainnet.rs @@ -17,6 +17,7 @@ pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; +pub(crate) const GROUP_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; pub(crate) const COCONUT_DKG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] = @@ -81,6 +82,7 @@ pub fn export_to_env() { var_names::COCONUT_BANDWIDTH_CONTRACT_ADDRESS, COCONUT_BANDWIDTH_CONTRACT_ADDRESS, ); + set_var_to_default(var_names::GROUP_CONTRACT_ADDRESS, GROUP_CONTRACT_ADDRESS); set_var_to_default( var_names::MULTISIG_CONTRACT_ADDRESS, MULTISIG_CONTRACT_ADDRESS, @@ -125,6 +127,7 @@ pub fn export_to_env_if_not_set() { var_names::COCONUT_BANDWIDTH_CONTRACT_ADDRESS, COCONUT_BANDWIDTH_CONTRACT_ADDRESS, ); + set_var_conditionally_to_default(var_names::GROUP_CONTRACT_ADDRESS, GROUP_CONTRACT_ADDRESS); set_var_conditionally_to_default( var_names::MULTISIG_CONTRACT_ADDRESS, MULTISIG_CONTRACT_ADDRESS, diff --git a/common/network-defaults/src/var_names.rs b/common/network-defaults/src/var_names.rs index f1b5c16055..0fd5506ddc 100644 --- a/common/network-defaults/src/var_names.rs +++ b/common/network-defaults/src/var_names.rs @@ -14,6 +14,7 @@ pub const MIXNET_CONTRACT_ADDRESS: &str = "MIXNET_CONTRACT_ADDRESS"; pub const VESTING_CONTRACT_ADDRESS: &str = "VESTING_CONTRACT_ADDRESS"; pub const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = "BANDWIDTH_CLAIM_CONTRACT_ADDRESS"; pub const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str = "COCONUT_BANDWIDTH_CONTRACT_ADDRESS"; +pub const GROUP_CONTRACT_ADDRESS: &str = "GROUP_CONTRACT_ADDRESS"; pub const MULTISIG_CONTRACT_ADDRESS: &str = "MULTISIG_CONTRACT_ADDRESS"; pub const COCONUT_DKG_CONTRACT_ADDRESS: &str = "COCONUT_DKG_CONTRACT_ADDRESS"; pub const REWARDING_VALIDATOR_ADDRESS: &str = "REWARDING_VALIDATOR_ADDRESS"; diff --git a/common/nymcoconut/src/tests/helpers.rs b/common/nymcoconut/src/tests/helpers.rs index ee7e418788..bae9efa36d 100644 --- a/common/nymcoconut/src/tests/helpers.rs +++ b/common/nymcoconut/src/tests/helpers.rs @@ -89,6 +89,9 @@ pub fn theta_from_keys_and_attributes( } pub fn transpose_matrix(matrix: Vec>) -> Vec> { + if matrix.is_empty() { + return vec![]; + } let len = matrix[0].len(); let mut iters: Vec<_> = matrix.into_iter().map(|d| d.into_iter()).collect(); (0..len) diff --git a/contracts/coconut-dkg/Cargo.toml b/contracts/coconut-dkg/Cargo.toml index fe4440cf2f..06a43598b1 100644 --- a/contracts/coconut-dkg/Cargo.toml +++ b/contracts/coconut-dkg/Cargo.toml @@ -23,4 +23,5 @@ thiserror = "1.0.23" [dev-dependencies] cw-multi-test = { version = "0.13.4" } -cw4-group = { path = "../multisig/cw4-group" } \ No newline at end of file +cw4-group = { path = "../multisig/cw4-group" } +group-contract-common = { path = "../../common/cosmwasm-smart-contracts/group-contract" } \ No newline at end of file diff --git a/contracts/coconut-dkg/src/constants.rs b/contracts/coconut-dkg/src/constants.rs index 249a96c696..ef7c79e13a 100644 --- a/contracts/coconut-dkg/src/constants.rs +++ b/contracts/coconut-dkg/src/constants.rs @@ -3,3 +3,6 @@ // Wait time for the verification to take place (currently 24h) pub(crate) const BLOCK_TIME_FOR_VERIFICATION_SECS: u64 = 86400; + +pub(crate) const VK_SHARES_PK_NAMESPACE: &str = "vksp"; +pub(crate) const VK_SHARES_EPOCH_ID_IDX_NAMESPACE: &str = "vkse"; diff --git a/contracts/coconut-dkg/src/contract.rs b/contracts/coconut-dkg/src/contract.rs index 101964304e..c2d16d51c6 100644 --- a/contracts/coconut-dkg/src/contract.rs +++ b/contracts/coconut-dkg/src/contract.rs @@ -9,7 +9,7 @@ use crate::dealings::queries::query_dealings_paged; use crate::dealings::transactions::try_commit_dealings; use crate::epoch_state::queries::{query_current_epoch, query_current_epoch_threshold}; use crate::epoch_state::storage::CURRENT_EPOCH; -use crate::epoch_state::transactions::advance_epoch_state; +use crate::epoch_state::transactions::{advance_epoch_state, try_surpassed_threshold}; use crate::error::ContractError; use crate::state::{State, MULTISIG, STATE}; use crate::verification_key_shares::queries::query_vk_shares_paged; @@ -54,6 +54,7 @@ pub fn instantiate( deps.storage, &Epoch::new( EpochState::default(), + 0, msg.time_configuration.unwrap_or_default(), env.block.time, ), @@ -84,6 +85,7 @@ pub fn execute( ExecuteMsg::VerifyVerificationKeyShare { owner } => { try_verify_verification_key_share(deps, info, owner) } + ExecuteMsg::SurpassedThreshold {} => try_surpassed_threshold(deps, env), ExecuteMsg::AdvanceEpochState {} => advance_epoch_state(deps, env), } } @@ -109,9 +111,11 @@ pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> Result to_binary(&query_dealings_paged(deps, idx, start_after, limit)?)?, - QueryMsg::GetVerificationKeys { limit, start_after } => { - to_binary(&query_vk_shares_paged(deps, start_after, limit)?)? - } + QueryMsg::GetVerificationKeys { + epoch_id, + limit, + start_after, + } => to_binary(&query_vk_shares_paged(deps, epoch_id, start_after, limit)?)?, }; Ok(response) @@ -132,8 +136,8 @@ mod tests { use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; use cosmwasm_std::{coins, Addr}; use cw4::Member; - use cw4_group::msg::InstantiateMsg as GroupInstantiateMsg; use cw_multi_test::{App, AppBuilder, AppResponse, ContractWrapper, Executor}; + use group_contract_common::msg::InstantiateMsg as GroupInstantiateMsg; fn instantiate_with_group(app: &mut App, members: &[Addr]) -> Addr { let group_code_id = app.store_code(Box::new(ContractWrapper::new( diff --git a/contracts/coconut-dkg/src/epoch_state/transactions.rs b/contracts/coconut-dkg/src/epoch_state/transactions.rs index f63327dd9e..62d600b272 100644 --- a/contracts/coconut-dkg/src/epoch_state/transactions.rs +++ b/contracts/coconut-dkg/src/epoch_state/transactions.rs @@ -1,11 +1,49 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::dealers::storage::current_dealers; +use crate::dealers::storage::{current_dealers, past_dealers}; +use crate::dealings::storage::DEALINGS_BYTES; use crate::epoch_state::storage::{CURRENT_EPOCH, THRESHOLD}; +use crate::epoch_state::utils::check_epoch_state; use crate::error::ContractError; +use crate::state::STATE; use coconut_dkg_common::types::{Epoch, EpochState}; -use cosmwasm_std::{DepsMut, Env, Order, Response}; +use cosmwasm_std::{DepsMut, Env, Order, Response, Storage}; + +fn reset_epoch_state(storage: &mut dyn Storage) -> Result<(), ContractError> { + THRESHOLD.remove(storage); + let dealers: Vec<_> = current_dealers() + .keys(storage, None, None, Order::Ascending) + .collect::>()?; + + for dealer_addr in dealers { + let details = current_dealers().load(storage, &dealer_addr)?; + for dealings in DEALINGS_BYTES { + dealings.remove(storage, &details.address); + } + current_dealers().remove(storage, &dealer_addr)?; + past_dealers().save(storage, &dealer_addr, &details)?; + } + Ok(()) +} + +fn dealers_still_active(deps: &DepsMut<'_>) -> Result { + let state = STATE.load(deps.storage)?; + let mut still_active = 0; + for dealer_addr in current_dealers() + .keys(deps.storage, None, None, Order::Ascending) + .flatten() + { + if state + .group_addr + .is_voting_member(&deps.querier, &dealer_addr, None)? + .is_some() + { + still_active += 1; + } + } + Ok(still_active) +} pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result { let epoch = CURRENT_EPOCH.load(deps.storage)?; @@ -18,24 +56,71 @@ pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result(deps.storage, |mut epoch| { - // TODO: When defaulting to the first state, some action will probably need to be taken on the - // rest of the contract, as we're starting with a new set of signers - epoch = Epoch::new( - epoch.state.next().unwrap_or_default(), - epoch.time_configuration, + let current_epoch = CURRENT_EPOCH.load(deps.storage)?; + let next_epoch = if let Some(state) = current_epoch.state.next() { + // We are during DKG process + if state == EpochState::DealingExchange { + let current_dealer_count = current_dealers() + .keys(deps.storage, None, None, Order::Ascending) + .count(); + // note: ceiling in integer division can be achieved via q = (x + y - 1) / y; + let threshold = (2 * current_dealer_count as u64 + 3 - 1) / 3; + THRESHOLD.save(deps.storage, &threshold)?; + } + Epoch::new( + state, + current_epoch.epoch_id, + current_epoch.time_configuration, env.block.time, - ); - Ok(epoch) - })?; - if current_epoch.state == EpochState::DealingExchange { - let current_dealer_count = current_dealers() - .keys(deps.storage, None, None, Order::Ascending) - .count(); - // note: ceiling in integer division can be achieved via q = (x + y - 1) / y; - let threshold = (2 * current_dealer_count as u64 + 3 - 1) / 3; - THRESHOLD.save(deps.storage, &threshold)?; + ) + } else if dealers_still_active(&deps)? + == STATE + .load(deps.storage)? + .group_addr + .list_members(&deps.querier, None, None)? + .len() + { + // The dealer set hasn't changed, so we only extend the finish timestamp + Epoch::new( + current_epoch.state, + current_epoch.epoch_id, + current_epoch.time_configuration, + env.block.time, + ) + } else { + // Dealer set changed, we need to redo DKG from scratch + reset_epoch_state(deps.storage)?; + Epoch::new( + EpochState::default(), + current_epoch.epoch_id + 1, + current_epoch.time_configuration, + env.block.time, + ) + }; + CURRENT_EPOCH.save(deps.storage, &next_epoch)?; + + Ok(Response::default()) +} + +pub(crate) fn try_surpassed_threshold( + deps: DepsMut<'_>, + env: Env, +) -> Result { + check_epoch_state(deps.storage, EpochState::InProgress)?; + + let threshold = THRESHOLD.load(deps.storage)?; + if dealers_still_active(&deps)? < threshold as usize { + reset_epoch_state(deps.storage)?; + CURRENT_EPOCH.update::<_, ContractError>(deps.storage, |epoch| { + Ok(Epoch::new( + EpochState::default(), + epoch.epoch_id + 1, + epoch.time_configuration, + env.block.time, + )) + })?; } + Ok(Response::default()) } @@ -186,17 +271,6 @@ pub(crate) mod tests { advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(), EarlyEpochStateAdvancement(50) ); - - env.block.time = env.block.time.plus_seconds(100); - advance_epoch_state(deps.as_mut(), env.clone()).unwrap(); - let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap(); - assert_eq!(epoch.state, EpochState::PublicKeySubmission); - assert_eq!( - epoch.finish_timestamp, - env.block - .time - .plus_seconds(epoch.time_configuration.public_key_submission_time_secs) - ); } #[test] diff --git a/contracts/coconut-dkg/src/epoch_state/utils.rs b/contracts/coconut-dkg/src/epoch_state/utils.rs index 65cca74797..78e91b6a14 100644 --- a/contracts/coconut-dkg/src/epoch_state/utils.rs +++ b/contracts/coconut-dkg/src/epoch_state/utils.rs @@ -37,7 +37,7 @@ pub(crate) mod test { CURRENT_EPOCH .save( deps.as_mut().storage, - &Epoch::new(fixed_state, TimeConfiguration::default(), env.block.time), + &Epoch::new(fixed_state, 0, TimeConfiguration::default(), env.block.time), ) .unwrap(); for against_state in EpochState::default().all_until(EpochState::InProgress) { diff --git a/contracts/coconut-dkg/src/support/tests/fixtures.rs b/contracts/coconut-dkg/src/support/tests/fixtures.rs index 19aad555d1..e2a3144a14 100644 --- a/contracts/coconut-dkg/src/support/tests/fixtures.rs +++ b/contracts/coconut-dkg/src/support/tests/fixtures.rs @@ -14,6 +14,7 @@ pub fn vk_share_fixture(index: u64) -> ContractVKShare { announce_address: format!("localhost:{}", index), node_index: index, owner: Addr::unchecked(format!("owner{}", index)), + epoch_id: 0, verified: index % 2 == 0, } } diff --git a/contracts/coconut-dkg/src/verification_key_shares/queries.rs b/contracts/coconut-dkg/src/verification_key_shares/queries.rs index 331c42e58a..a3a02a0743 100644 --- a/contracts/coconut-dkg/src/verification_key_shares/queries.rs +++ b/contracts/coconut-dkg/src/verification_key_shares/queries.rs @@ -2,13 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 use crate::verification_key_shares::storage; -use crate::verification_key_shares::storage::VK_SHARES; +use crate::verification_key_shares::storage::vk_shares; +use coconut_dkg_common::types::EpochId; use coconut_dkg_common::verification_key::PagedVKSharesResponse; use cosmwasm_std::{Deps, Order, StdResult}; use cw_storage_plus::Bound; pub fn query_vk_shares_paged( deps: Deps<'_>, + epoch_id: EpochId, start_after: Option, limit: Option, ) -> StdResult { @@ -20,9 +22,12 @@ pub fn query_vk_shares_paged( .map(|addr| deps.api.addr_validate(&addr)) .transpose()?; - let start = addr.as_ref().map(Bound::exclusive); + let start = addr.as_ref().map(|addr| Bound::exclusive((addr, epoch_id))); - let shares = VK_SHARES + let shares = vk_shares() + .idx + .epoch_id + .prefix(epoch_id) .range(deps.storage, start, None, Order::Ascending) .take(limit) .map(|res| res.map(|(_, share)| share)) @@ -50,7 +55,7 @@ pub(crate) mod tests { #[test] fn vk_shares_empty_on_init() { let deps = init_contract(); - let response = query_vk_shares_paged(deps.as_ref(), None, Option::from(2)).unwrap(); + let response = query_vk_shares_paged(deps.as_ref(), 0, None, Option::from(2)).unwrap(); assert_eq!(0, response.shares.len()); } @@ -61,12 +66,12 @@ pub(crate) mod tests { for n in 0..1000 { let vk_share = vk_share_fixture(n); let sender = Addr::unchecked(format!("owner{}", n)); - VK_SHARES - .save(&mut deps.storage, &sender, &vk_share) + vk_shares() + .save(&mut deps.storage, (&sender, 0), &vk_share) .unwrap(); } - let page1 = query_vk_shares_paged(deps.as_ref(), None, Option::from(limit)).unwrap(); + let page1 = query_vk_shares_paged(deps.as_ref(), 0, None, Option::from(limit)).unwrap(); assert_eq!(limit, page1.shares.len() as u32); } @@ -76,13 +81,13 @@ pub(crate) mod tests { for n in 0..1000 { let vk_share = vk_share_fixture(n); let sender = Addr::unchecked(format!("owner{}", n)); - VK_SHARES - .save(&mut deps.storage, &sender, &vk_share) + vk_shares() + .save(&mut deps.storage, (&sender, 0), &vk_share) .unwrap(); } // query without explicitly setting a limit - let page1 = query_vk_shares_paged(deps.as_ref(), None, None).unwrap(); + let page1 = query_vk_shares_paged(deps.as_ref(), 0, None, None).unwrap(); assert_eq!( VERIFICATION_KEY_SHARES_PAGE_DEFAULT_LIMIT, @@ -96,14 +101,15 @@ pub(crate) mod tests { for n in 0..1000 { let vk_share = vk_share_fixture(n); let sender = Addr::unchecked(format!("owner{}", n)); - VK_SHARES - .save(&mut deps.storage, &sender, &vk_share) + vk_shares() + .save(&mut deps.storage, (&sender, 0), &vk_share) .unwrap(); } // query with a crazily high limit in an attempt to use too many resources let crazy_limit = 1000 * VERIFICATION_KEY_SHARES_PAGE_MAX_LIMIT; - let page1 = query_vk_shares_paged(deps.as_ref(), None, Option::from(crazy_limit)).unwrap(); + let page1 = + query_vk_shares_paged(deps.as_ref(), 0, None, Option::from(crazy_limit)).unwrap(); // we default to a decent sized upper bound instead let expected_limit = VERIFICATION_KEY_SHARES_PAGE_MAX_LIMIT; @@ -116,12 +122,12 @@ pub(crate) mod tests { let vk_share = vk_share_fixture(1); let sender = Addr::unchecked(format!("owner{}", 1)); - VK_SHARES - .save(&mut deps.storage, &sender, &vk_share) + vk_shares() + .save(&mut deps.storage, (&sender, 0), &vk_share) .unwrap(); let per_page = 2; - let page1 = query_vk_shares_paged(deps.as_ref(), None, Option::from(per_page)).unwrap(); + let page1 = query_vk_shares_paged(deps.as_ref(), 0, None, Option::from(per_page)).unwrap(); // page should have 1 result on it assert_eq!(1, page1.shares.len()); @@ -129,28 +135,29 @@ pub(crate) mod tests { // save another let vk_share = vk_share_fixture(2); let sender = Addr::unchecked(format!("owner{}", 2)); - VK_SHARES - .save(&mut deps.storage, &sender, &vk_share) + vk_shares() + .save(&mut deps.storage, (&sender, 0), &vk_share) .unwrap(); // page1 should have 2 results on it - let page1 = query_vk_shares_paged(deps.as_ref(), None, Option::from(per_page)).unwrap(); + let page1 = query_vk_shares_paged(deps.as_ref(), 0, None, Option::from(per_page)).unwrap(); assert_eq!(2, page1.shares.len()); let vk_share = vk_share_fixture(3); let sender = Addr::unchecked(format!("owner{}", 3)); - VK_SHARES - .save(&mut deps.storage, &sender, &vk_share) + vk_shares() + .save(&mut deps.storage, (&sender, 0), &vk_share) .unwrap(); // page1 still has 2 results - let page1 = query_vk_shares_paged(deps.as_ref(), None, Option::from(per_page)).unwrap(); + let page1 = query_vk_shares_paged(deps.as_ref(), 0, None, Option::from(per_page)).unwrap(); assert_eq!(2, page1.shares.len()); // retrieving the next page should start after the last key on this page let start_after = page1.start_next_after.unwrap(); let page2 = query_vk_shares_paged( deps.as_ref(), + 0, Option::from(start_after.to_string()), Option::from(per_page), ) @@ -160,12 +167,13 @@ pub(crate) mod tests { let vk_share = vk_share_fixture(4); let sender = Addr::unchecked(format!("owner{}", 4)); - VK_SHARES - .save(&mut deps.storage, &sender, &vk_share) + vk_shares() + .save(&mut deps.storage, (&sender, 0), &vk_share) .unwrap(); let page2 = query_vk_shares_paged( deps.as_ref(), + 0, Option::from(start_after.to_string()), Option::from(per_page), ) diff --git a/contracts/coconut-dkg/src/verification_key_shares/storage.rs b/contracts/coconut-dkg/src/verification_key_shares/storage.rs index 761736367c..13ecd5370a 100644 --- a/contracts/coconut-dkg/src/verification_key_shares/storage.rs +++ b/contracts/coconut-dkg/src/verification_key_shares/storage.rs @@ -3,13 +3,35 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::constants::{VK_SHARES_EPOCH_ID_IDX_NAMESPACE, VK_SHARES_PK_NAMESPACE}; +use coconut_dkg_common::types::EpochId; use coconut_dkg_common::verification_key::ContractVKShare; use cosmwasm_std::Addr; -use cw_storage_plus::Map; +use cw_storage_plus::{Index, IndexList, IndexedMap, MultiIndex}; pub(crate) const VERIFICATION_KEY_SHARES_PAGE_MAX_LIMIT: u32 = 75; pub(crate) const VERIFICATION_KEY_SHARES_PAGE_DEFAULT_LIMIT: u32 = 50; -type VKShareKey<'a> = &'a Addr; +type VKShareKey<'a> = (&'a Addr, EpochId); -pub(crate) const VK_SHARES: Map<'_, VKShareKey<'_>, ContractVKShare> = Map::new("vks"); +pub(crate) struct VkShareIndex<'a> { + pub(crate) epoch_id: MultiIndex<'a, EpochId, ContractVKShare, VKShareKey<'a>>, +} + +impl<'a> IndexList for VkShareIndex<'a> { + fn get_indexes(&'_ self) -> Box> + '_> { + let v: Vec<&dyn Index> = vec![&self.epoch_id]; + Box::new(v.into_iter()) + } +} + +pub(crate) fn vk_shares<'a>() -> IndexedMap<'a, VKShareKey<'a>, ContractVKShare, VkShareIndex<'a>> { + let indexes = VkShareIndex { + epoch_id: MultiIndex::new( + |d| d.epoch_id, + VK_SHARES_PK_NAMESPACE, + VK_SHARES_EPOCH_ID_IDX_NAMESPACE, + ), + }; + IndexedMap::new(VK_SHARES_PK_NAMESPACE, indexes) +} diff --git a/contracts/coconut-dkg/src/verification_key_shares/transactions.rs b/contracts/coconut-dkg/src/verification_key_shares/transactions.rs index c2e8ae377a..ef33f0b1a1 100644 --- a/contracts/coconut-dkg/src/verification_key_shares/transactions.rs +++ b/contracts/coconut-dkg/src/verification_key_shares/transactions.rs @@ -3,10 +3,11 @@ use crate::constants::BLOCK_TIME_FOR_VERIFICATION_SECS; use crate::dealers::storage as dealers_storage; +use crate::epoch_state::storage::CURRENT_EPOCH; use crate::epoch_state::utils::check_epoch_state; use crate::error::ContractError; use crate::state::{MULTISIG, STATE}; -use crate::verification_key_shares::storage::VK_SHARES; +use crate::verification_key_shares::storage::vk_shares; use coconut_dkg_common::types::EpochState; use coconut_dkg_common::verification_key::{to_cosmos_msg, ContractVKShare, VerificationKeyShare}; use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response}; @@ -22,7 +23,11 @@ pub fn try_commit_verification_key_share( let details = dealers_storage::current_dealers() .load(deps.storage, &info.sender) .map_err(|_| ContractError::NotADealer)?; - if VK_SHARES.may_load(deps.storage, &info.sender)?.is_some() { + let epoch_id = CURRENT_EPOCH.load(deps.storage)?.epoch_id; + if vk_shares() + .may_load(deps.storage, (&info.sender, epoch_id))? + .is_some() + { return Err(ContractError::AlreadyCommitted { commitment: String::from("verification key share"), }); @@ -33,9 +38,10 @@ pub fn try_commit_verification_key_share( node_index: details.assigned_index, announce_address: details.announce_address, owner: info.sender.clone(), + epoch_id: CURRENT_EPOCH.load(deps.storage)?.epoch_id, verified: false, }; - VK_SHARES.save(deps.storage, &info.sender, &data)?; + vk_shares().save(deps.storage, (&info.sender, epoch_id), &data)?; let msg = to_cosmos_msg( info.sender, @@ -55,8 +61,9 @@ pub fn try_verify_verification_key_share( owner: Addr, ) -> Result { check_epoch_state(deps.storage, EpochState::VerificationKeyFinalization)?; + let epoch_id = CURRENT_EPOCH.load(deps.storage)?.epoch_id; MULTISIG.assert_admin(deps.as_ref(), &info.sender)?; - VK_SHARES.update(deps.storage, &owner, |vk_share| { + vk_shares().update(deps.storage, (&owner, epoch_id), |vk_share| { vk_share .map(|mut share| { share.verified = true; diff --git a/contracts/coconut-test/Cargo.toml b/contracts/coconut-test/Cargo.toml index f9dd795e4b..7eaa724fd0 100644 --- a/contracts/coconut-test/Cargo.toml +++ b/contracts/coconut-test/Cargo.toml @@ -10,6 +10,7 @@ bandwidth-claim-contract = { path = "../../common/bandwidth-claim-contract" } coconut-bandwidth-contract-common = { path = "../../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" } coconut-dkg-common = { path = "../../common/cosmwasm-smart-contracts/coconut-dkg" } multisig-contract-common = { path = "../../common/cosmwasm-smart-contracts/multisig-contract" } +group-contract-common = { path = "../../common/cosmwasm-smart-contracts/group-contract" } cosmwasm-std = "1.0.0" cosmwasm-storage = "1.0.0" diff --git a/contracts/coconut-test/src/spend_credential_creates_proposal.rs b/contracts/coconut-test/src/spend_credential_creates_proposal.rs index 3d382f493b..7d68073b40 100644 --- a/contracts/coconut-test/src/spend_credential_creates_proposal.rs +++ b/contracts/coconut-test/src/spend_credential_creates_proposal.rs @@ -7,9 +7,9 @@ use coconut_bandwidth_contract_common::{ spend_credential::SpendCredentialData, }; use cosmwasm_std::{coins, Addr, Coin, Decimal}; -use cw4_group::msg::InstantiateMsg as GroupInstantiateMsg; use cw_multi_test::Executor; use cw_utils::{Duration, Threshold}; +use group_contract_common::msg::InstantiateMsg as GroupInstantiateMsg; use multisig_contract_common::msg::InstantiateMsg as MultisigInstantiateMsg; pub const TEST_COIN_DENOM: &str = "unym"; diff --git a/contracts/coconut-test/src/submit_vk_creates_proposal.rs b/contracts/coconut-test/src/submit_vk_creates_proposal.rs index 62f6af15f0..c2f09d4bb2 100644 --- a/contracts/coconut-test/src/submit_vk_creates_proposal.rs +++ b/contracts/coconut-test/src/submit_vk_creates_proposal.rs @@ -15,9 +15,9 @@ use coconut_dkg_common::msg::QueryMsg::GetVerificationKeys; use coconut_dkg_common::verification_key::PagedVKSharesResponse; use cosmwasm_std::{coins, Addr, Decimal}; use cw4::Member; -use cw4_group::msg::InstantiateMsg as GroupInstantiateMsg; use cw_multi_test::Executor; use cw_utils::{Duration, Threshold}; +use group_contract_common::msg::InstantiateMsg as GroupInstantiateMsg; use multisig_contract_common::msg::ExecuteMsg::{Execute, Vote}; use multisig_contract_common::msg::InstantiateMsg as MultisigInstantiateMsg; @@ -152,6 +152,7 @@ fn dkg_proposal() { .query_wasm_smart( coconut_dkg_contract_addr.clone(), &GetVerificationKeys { + epoch_id: 0, limit: None, start_after: None, }, @@ -199,6 +200,7 @@ fn dkg_proposal() { .query_wasm_smart( coconut_dkg_contract_addr, &GetVerificationKeys { + epoch_id: 0, limit: None, start_after: None, }, diff --git a/contracts/multisig/cw3-flex-multisig/Cargo.toml b/contracts/multisig/cw3-flex-multisig/Cargo.toml index 8afa71f251..e014c728b7 100644 --- a/contracts/multisig/cw3-flex-multisig/Cargo.toml +++ b/contracts/multisig/cw3-flex-multisig/Cargo.toml @@ -28,6 +28,7 @@ schemars = "0.8.1" serde = { version = "1.0.103", default-features = false, features = ["derive"] } thiserror = { version = "1.0.23" } +group-contract-common = { path = "../../../common/cosmwasm-smart-contracts/group-contract" } multisig-contract-common = { path= "../../../common/cosmwasm-smart-contracts/multisig-contract" } [dev-dependencies] diff --git a/contracts/multisig/cw3-flex-multisig/src/contract.rs b/contracts/multisig/cw3-flex-multisig/src/contract.rs index 5a56ea9745..677006daf4 100644 --- a/contracts/multisig/cw3-flex-multisig/src/contract.rs +++ b/contracts/multisig/cw3-flex-multisig/src/contract.rs @@ -502,7 +502,7 @@ mod tests { // uploads code and returns address of group contract fn instantiate_group(app: &mut App, members: Vec) -> Addr { let group_id = app.store_code(contract_group()); - let msg = cw4_group::msg::InstantiateMsg { + let msg = group_contract_common::msg::InstantiateMsg { admin: Some(OWNER.into()), members, }; @@ -1410,7 +1410,7 @@ mod tests { // adds NEWBIE with 2 power -> with snapshot, invalid vote // removes VOTER3 -> with snapshot, can vote on proposal let newbie: &str = "newbie"; - let update_msg = cw4_group::msg::ExecuteMsg::UpdateMembers { + let update_msg = group_contract_common::msg::ExecuteMsg::UpdateMembers { remove: vec![VOTER3.into()], add: vec![member(VOTER2, 21), member(newbie, 2)], }; @@ -1623,7 +1623,7 @@ mod tests { // admin changes the group (3 -> 0, 2 -> 9, 0 -> 29) - total = 56, require 29 to pass let newbie: &str = "newbie"; - let update_msg = cw4_group::msg::ExecuteMsg::UpdateMembers { + let update_msg = group_contract_common::msg::ExecuteMsg::UpdateMembers { remove: vec![VOTER3.into()], add: vec![member(VOTER2, 9), member(newbie, 29)], }; @@ -1702,7 +1702,7 @@ mod tests { // admin changes the group (3 -> 0, 2 -> 9, 0 -> 28) - total = 55, require 28 to pass let newbie: &str = "newbie"; - let update_msg = cw4_group::msg::ExecuteMsg::UpdateMembers { + let update_msg = group_contract_common::msg::ExecuteMsg::UpdateMembers { remove: vec![VOTER3.into()], add: vec![member(VOTER2, 9), member(newbie, 29)], }; diff --git a/contracts/multisig/cw4-group/Cargo.toml b/contracts/multisig/cw4-group/Cargo.toml index 235cfeed70..7c98f4c991 100644 --- a/contracts/multisig/cw4-group/Cargo.toml +++ b/contracts/multisig/cw4-group/Cargo.toml @@ -24,6 +24,8 @@ crate-type = ["cdylib", "rlib"] library = [] [dependencies] +group-contract-common = { path = "../../../common/cosmwasm-smart-contracts/group-contract" } + cw-utils = { version = "0.13.4" } cw2 = { version = "0.13.4" } cw4 = { version = "0.13.4" } diff --git a/contracts/multisig/cw4-group/src/contract.rs b/contracts/multisig/cw4-group/src/contract.rs index 913d427f6a..cb9c4f2787 100644 --- a/contracts/multisig/cw4-group/src/contract.rs +++ b/contracts/multisig/cw4-group/src/contract.rs @@ -13,8 +13,8 @@ use cw_storage_plus::Bound; use cw_utils::maybe_addr; use crate::error::ContractError; -use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; use crate::state::{ADMIN, HOOKS, MEMBERS, TOTAL}; +use group_contract_common::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; // version info for migration info const CONTRACT_NAME: &str = "crates.io:cw4-group"; diff --git a/contracts/multisig/cw4-group/src/helpers.rs b/contracts/multisig/cw4-group/src/helpers.rs index add7451a43..ac0e56f42b 100644 --- a/contracts/multisig/cw4-group/src/helpers.rs +++ b/contracts/multisig/cw4-group/src/helpers.rs @@ -5,7 +5,7 @@ use std::ops::Deref; use cosmwasm_std::{to_binary, Addr, CosmosMsg, StdResult, WasmMsg}; use cw4::{Cw4Contract, Member}; -use crate::msg::ExecuteMsg; +use group_contract_common::msg::ExecuteMsg; /// Cw4GroupContract is a wrapper around Cw4Contract that provides a lot of helpers /// for working with cw4-group contracts. diff --git a/contracts/multisig/cw4-group/src/lib.rs b/contracts/multisig/cw4-group/src/lib.rs index 98208b782f..3870baf04b 100644 --- a/contracts/multisig/cw4-group/src/lib.rs +++ b/contracts/multisig/cw4-group/src/lib.rs @@ -1,7 +1,6 @@ pub mod contract; pub mod error; pub mod helpers; -pub mod msg; pub mod state; pub use crate::error::ContractError; diff --git a/envs/local.env b/envs/local.env index c9ffed0865..c7121762d0 100644 --- a/envs/local.env +++ b/envs/local.env @@ -13,9 +13,12 @@ MIXNET_CONTRACT_ADDRESS=n1xsml6tm6pnx8nsuz3a54cznh55g6nuwu9ug9t92ucjkq5ewp5w9syx VESTING_CONTRACT_ADDRESS=n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n1lp5zex6685kd22agzskhqsylpnssxnweyuvsz4edr4p4ta92qf3q3dhmsx +GROUP_CONTRACT_ADDRESS=n1rw8fw2mpcpzzq3jpa4e52ufawnmj5a4u68p35umvgskewuw0nlzsaa5w4m MULTISIG_CONTRACT_ADDRESS=n1rw8fw2mpcpzzq3jpa4e52ufawnmj5a4u68p35umvgskewuw0nlzsaa5w4m -COCONUT_DKG_CONTRACT_ADDRESS=n15f5e6ffz33khfzaadaug9axjt5r38xfe84v9jvlhfs0y7mv9sxvs2xt0u6 +COCONUT_DKG_CONTRACT_ADDRESS=n1vwtgazgpancsfel04y7syc95ausmat47cjtldqzkdmx6phyrwx2qvkv32p REWARDING_VALIDATOR_ADDRESS=n1tfzd4qz3a45u8p4mr5zmzv66457uwjgcl05jdq STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0" NYXD="http://127.0.0.1:26657" NYM_API="http://127.0.0.1:8000" + +DKG_TIME_CONFIGURATION="600,300,300,60,60,1209600" diff --git a/envs/mainnet.env b/envs/mainnet.env index 57cd2ad646..6d3d65f14f 100644 --- a/envs/mainnet.env +++ b/envs/mainnet.env @@ -13,6 +13,7 @@ MIXNET_CONTRACT_ADDRESS=n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0 VESTING_CONTRACT_ADDRESS=n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 +GROUP_CONTRACT_ADDRESS=n1rw8fw2mpcpzzq3jpa4e52ufawnmj5a4u68p35umvgskewuw0nlzsaa5w4m MULTISIG_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 COCONUT_DKG_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy diff --git a/envs/qa-qwerty.env b/envs/qa-qwerty.env index a2f54cbd79..30130fd6e9 100644 --- a/envs/qa-qwerty.env +++ b/envs/qa-qwerty.env @@ -13,6 +13,7 @@ MIXNET_CONTRACT_ADDRESS=n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjy VESTING_CONTRACT_ADDRESS=n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n12ckdkm3q7eytefs7rwu4ue3t9hxgvl9v08jddmtwgct2ve0pv50q0t8dlt +GROUP_CONTRACT_ADDRESS=n1rw8fw2mpcpzzq3jpa4e52ufawnmj5a4u68p35umvgskewuw0nlzsaa5w4m MULTISIG_CONTRACT_ADDRESS=n14krxe8ukzagwhvec0rmteexu62w8k9kp9sra9ww6em2hnmzcukqsa0utc8 COCONUT_DKG_CONTRACT_ADDRESS=n1rl5n6cxuz2hdy3f7d9hsnw8zn0zwwwr0r4dxfz7tktgpgkcnz9zshmvksc REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy diff --git a/envs/qa.env b/envs/qa.env index fae89c8922..a5442b7600 100644 --- a/envs/qa.env +++ b/envs/qa.env @@ -13,6 +13,7 @@ MIXNET_CONTRACT_ADDRESS=n1rjzps6qrmdqmf0xz4cn4x4rcmqeqzq6hnzqg4wcvd0r2lyasdq5sep VESTING_CONTRACT_ADDRESS=n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n1ghd753shjuwexxywmgs4xz7x2q732vcn7ty4yw +GROUP_CONTRACT_ADDRESS=n1rw8fw2mpcpzzq3jpa4e52ufawnmj5a4u68p35umvgskewuw0nlzsaa5w4m MULTISIG_CONTRACT_ADDRESS=n17p9rzwnnfxcjp32un9ug7yhhzgtkhvl988qccs COCONUT_DKG_CONTRACT_ADDRESS=n17p9rzwnnfxcjp32un9ug7yhhzgtkhvl988qccs REWARDING_VALIDATOR_ADDRESS=n1tfzd4qz3a45u8p4mr5zmzv66457uwjgcl05jdq diff --git a/gateway/src/error.rs b/gateway/src/error.rs index 6b8bc023a0..4692686cb9 100644 --- a/gateway/src/error.rs +++ b/gateway/src/error.rs @@ -44,25 +44,4 @@ pub(crate) enum GatewayError { expected_prefix: String, actual_prefix: String, }, - - #[cfg(feature = "coconut")] - #[error("could not obtain all coconut coconut verifiers details: {source}")] - CoconutVerifiersQueryFailure { - #[source] - source: ValidatorClientError, - }, - - #[cfg(feature = "coconut")] - #[error("failed to aggregate coconut verification keys: {source}")] - CoconutVerificationKeyAggregationFailure { - #[source] - source: credentials::error::Error, - }, - - #[cfg(feature = "coconut")] - #[error("failed to create coconut verifier: {source}")] - CoconutVerifierCreationFailure { - #[source] - source: crate::node::client_handling::websocket::connection_handler::RequestHandlingError, - }, } diff --git a/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs b/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs index 94d262004d..948a2187fd 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs @@ -67,6 +67,10 @@ pub(crate) enum RequestHandlingError { #[cfg(feature = "coconut")] #[error("Coconut interface error - {0}")] CoconutInterfaceError(#[from] coconut_interface::error::CoconutInterfaceError), + + #[cfg(feature = "coconut")] + #[error("Credential error - {0}")] + CredentialError(#[from] credentials::error::Error), } impl RequestHandlingError { @@ -208,12 +212,28 @@ where iv, )?; - if !credential.verify( - self.inner - .coconut_verifier - .as_ref() - .aggregated_verification_key(), - ) { + // Get the latest coconut signers and their VK + let credential_api_clients = self + .inner + .coconut_verifier + .all_coconut_api_clients(*credential.epoch_id()) + .await?; + let current_api_clients = self + .inner + .coconut_verifier + .all_current_coconut_api_clients() + .await?; + if credential_api_clients.is_empty() || current_api_clients.is_empty() { + return Err(RequestHandlingError::NotEnoughNymAPIs { + received: 0, + needed: 1, + }); + } + + let aggregated_verification_key = + credentials::obtain_aggregate_verification_key(&credential_api_clients).await?; + + if !credential.verify(&aggregated_verification_key) { return Err(RequestHandlingError::InvalidBandwidthCredential( String::from("credential failed to verify on gateway"), )); @@ -221,7 +241,7 @@ where self.inner .coconut_verifier - .release_funds(&credential) + .release_funds(current_api_clients, &credential) .await?; let bandwidth = Bandwidth::from(credential); diff --git a/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs b/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs index 6b06f2b17d..aa07567b0a 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs @@ -1,9 +1,10 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use coconut_interface::{Credential, VerificationKey}; +use coconut_interface::Credential; use log::*; use std::time::{Duration, SystemTime}; +use validator_client::nyxd::traits::DkgQueryClient; use validator_client::{ nyxd::{ cosmwasm_client::logs::{find_attribute, BANDWIDTH_PROPOSAL_ID}, @@ -19,24 +20,12 @@ const ONE_HOUR_SEC: u64 = 3600; const MAX_FEEGRANT_UNYM: u128 = 10000; pub(crate) struct CoconutVerifier { - nym_api_clients: Vec, nyxd_client: Client, mix_denom_base: String, - aggregated_verification_key: VerificationKey, } impl CoconutVerifier { - pub fn new( - api_clients: Vec, - nyxd_client: Client, - aggregated_verification_key: VerificationKey, - ) -> Result { - if api_clients.is_empty() { - return Err(RequestHandlingError::NotEnoughNymAPIs { - received: 0, - needed: 1, - }); - } + pub fn new(nyxd_client: Client) -> Self { let mix_denom_base = nyxd_client .nyxd .current_chain_details() @@ -44,19 +33,31 @@ impl CoconutVerifier { .base .clone(); - Ok(CoconutVerifier { - nym_api_clients: api_clients, + CoconutVerifier { nyxd_client, mix_denom_base, - aggregated_verification_key, - }) + } } - pub fn aggregated_verification_key(&self) -> &VerificationKey { - &self.aggregated_verification_key + pub async fn all_current_coconut_api_clients( + &self, + ) -> Result, RequestHandlingError> { + let epoch_id = self.nyxd_client.nyxd.get_current_epoch().await?.epoch_id; + self.all_coconut_api_clients(epoch_id).await } - pub async fn release_funds(&self, credential: &Credential) -> Result<(), RequestHandlingError> { + pub async fn all_coconut_api_clients( + &self, + epoch_id: u64, + ) -> Result, RequestHandlingError> { + Ok(CoconutApiClient::all_coconut_api_clients(&self.nyxd_client, epoch_id).await?) + } + + pub async fn release_funds( + &self, + api_clients: Vec, + credential: &Credential, + ) -> Result<(), RequestHandlingError> { // Use a custom multiplier for revoke, as the default one (1.3) // isn't enough let revoke_fee = Some(Fee::Auto(Some(1.5))); @@ -96,7 +97,7 @@ impl CoconutVerifier { proposal_id, self.nyxd_client.nyxd.address().clone(), ); - for client in self.nym_api_clients.iter() { + for client in api_clients { self.nyxd_client .nyxd .grant_allowance( diff --git a/gateway/src/node/client_handling/websocket/connection_handler/mod.rs b/gateway/src/node/client_handling/websocket/connection_handler/mod.rs index 2bab732ae7..6cdfeacb32 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/mod.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/mod.rs @@ -14,9 +14,6 @@ use tokio_tungstenite::WebSocketStream; pub(crate) use self::authenticated::AuthenticatedHandler; pub(crate) use self::fresh::FreshHandler; -#[cfg(feature = "coconut")] -pub(crate) use self::authenticated::RequestHandlingError; - mod authenticated; #[cfg(feature = "coconut")] pub(crate) mod coconut; diff --git a/gateway/src/node/mod.rs b/gateway/src/node/mod.rs index cfae4525f4..96309c082a 100644 --- a/gateway/src/node/mod.rs +++ b/gateway/src/node/mod.rs @@ -28,11 +28,9 @@ use task::{TaskClient, TaskManager}; #[cfg(feature = "coconut")] use crate::node::client_handling::websocket::connection_handler::coconut::CoconutVerifier; #[cfg(feature = "coconut")] -use credentials::coconut::utils::obtain_aggregate_verification_key; -#[cfg(feature = "coconut")] use network_defaults::NymNetworkDetails; #[cfg(feature = "coconut")] -use validator_client::{Client, CoconutApiClient}; +use validator_client::Client; pub(crate) mod client_handling; pub(crate) mod mixnet_handling; @@ -321,16 +319,7 @@ where #[cfg(feature = "coconut")] let coconut_verifier = { let nyxd_client = self.random_nyxd_client(); - let api_clients = CoconutApiClient::all_coconut_api_clients(&nyxd_client) - .await - .map_err(|source| GatewayError::CoconutVerifiersQueryFailure { source })?; - let validators_verification_key = obtain_aggregate_verification_key(&api_clients) - .await - .map_err( - |source| GatewayError::CoconutVerificationKeyAggregationFailure { source }, - )?; - CoconutVerifier::new(api_clients, nyxd_client, validators_verification_key) - .map_err(|source| GatewayError::CoconutVerifierCreationFailure { source })? + CoconutVerifier::new(nyxd_client) }; let mix_forwarding_channel = self.start_packet_forwarder(shutdown.subscribe()); diff --git a/nym-api/Cargo.toml b/nym-api/Cargo.toml index 931f536539..2bee3caa39 100644 --- a/nym-api/Cargo.toml +++ b/nym-api/Cargo.toml @@ -76,6 +76,7 @@ credentials = { path = "../common/credentials", optional = true } crypto = { path = "../common/crypto" } logging = { path = "../common/logging"} cw3 = { version = "0.13.4", optional = true } +cw4 = { version = "0.13.4", optional = true } dkg = { path = "../common/crypto/dkg", optional = true } gateway-client = { path = "../common/client-libs/gateway-client" } inclusion-probability = { path = "../common/inclusion-probability" } @@ -99,6 +100,7 @@ coconut = [ "coconut-interface", "credentials", "cw3", + "cw4", "gateway-client/coconut", "credentials/coconut", "nym-api-requests/coconut", diff --git a/nym-api/src/coconut/client.rs b/nym-api/src/coconut/client.rs index 48788a03be..a80c6716ba 100644 --- a/nym-api/src/coconut/client.rs +++ b/nym-api/src/coconut/client.rs @@ -4,10 +4,11 @@ use crate::coconut::error::Result; use coconut_bandwidth_contract_common::spend_credential::SpendCredentialResponse; use coconut_dkg_common::dealer::{ContractDealing, DealerDetails, DealerDetailsResponse}; -use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, Epoch}; +use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, Epoch, EpochId}; use coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare}; use contracts_common::dealings::ContractSafeBytes; use cw3::ProposalResponse; +use cw4::MemberResponse; use dkg::Threshold; use validator_client::nyxd::cosmwasm_client::types::ExecuteResult; use validator_client::nyxd::{AccountId, Fee, TxResponse}; @@ -23,11 +24,12 @@ pub trait Client { blinded_serial_number: String, ) -> Result; async fn get_current_epoch(&self) -> Result; + async fn group_member(&self, addr: String) -> Result; async fn get_current_epoch_threshold(&self) -> Result>; async fn get_self_registered_dealer_details(&self) -> Result; async fn get_current_dealers(&self) -> Result>; async fn get_dealings(&self, idx: usize) -> Result>; - async fn get_verification_key_shares(&self) -> Result>; + async fn get_verification_key_shares(&self, epoch_id: EpochId) -> Result>; async fn vote_proposal(&self, proposal_id: u64, vote_yes: bool, fee: Option) -> Result<()>; async fn execute_proposal(&self, proposal_id: u64) -> Result<()>; diff --git a/nym-api/src/coconut/comm.rs b/nym-api/src/coconut/comm.rs index d0694d78d6..bca6bfda12 100644 --- a/nym-api/src/coconut/comm.rs +++ b/nym-api/src/coconut/comm.rs @@ -3,13 +3,14 @@ use crate::coconut::error::Result; use crate::nyxd; +use coconut_dkg_common::types::EpochId; use coconut_interface::VerificationKey; use credentials::coconut::utils::obtain_aggregate_verification_key; use validator_client::CoconutApiClient; #[async_trait] pub trait APICommunicationChannel { - async fn aggregated_verification_key(&self) -> Result; + async fn aggregated_verification_key(&self, epoch_id: EpochId) -> Result; } pub(crate) struct QueryCommunicationChannel { @@ -24,9 +25,10 @@ impl QueryCommunicationChannel { #[async_trait] impl APICommunicationChannel for QueryCommunicationChannel { - async fn aggregated_verification_key(&self) -> Result { + async fn aggregated_verification_key(&self, epoch_id: EpochId) -> Result { let client = self.nyxd_client.0.read().await; - let coconut_api_clients = CoconutApiClient::all_coconut_api_clients(&client).await?; + let coconut_api_clients = + CoconutApiClient::all_coconut_api_clients(&client, epoch_id).await?; let vk = obtain_aggregate_verification_key(&coconut_api_clients).await?; Ok(vk) } diff --git a/nym-api/src/coconut/dkg/client.rs b/nym-api/src/coconut/dkg/client.rs index 6243b5ec2c..53f462c0de 100644 --- a/nym-api/src/coconut/dkg/client.rs +++ b/nym-api/src/coconut/dkg/client.rs @@ -4,10 +4,11 @@ use crate::coconut::client::Client; use crate::coconut::error::CoconutError; use coconut_dkg_common::dealer::{ContractDealing, DealerDetails, DealerDetailsResponse}; -use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, Epoch, NodeIndex}; +use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, Epoch, EpochId, NodeIndex}; use coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare}; use contracts_common::dealings::ContractSafeBytes; use cw3::ProposalResponse; +use cw4::MemberResponse; use dkg::Threshold; use validator_client::nyxd::cosmwasm_client::logs::{find_attribute, NODE_INDEX}; use validator_client::nyxd::cosmwasm_client::types::ExecuteResult; @@ -31,7 +32,7 @@ impl DkgClient { } } - pub(crate) async fn _get_address(&self) -> AccountId { + pub(crate) async fn get_address(&self) -> AccountId { self.inner.address().await } @@ -46,6 +47,12 @@ impl DkgClient { ret } + pub(crate) async fn group_member(&self) -> Result { + self.inner + .group_member(self.get_address().await.to_string()) + .await + } + pub(crate) async fn get_current_epoch_threshold( &self, ) -> Result, CoconutError> { @@ -78,8 +85,9 @@ impl DkgClient { pub(crate) async fn get_verification_key_shares( &self, + epoch_id: EpochId, ) -> Result, CoconutError> { - self.inner.get_verification_key_shares().await + self.inner.get_verification_key_shares(epoch_id).await } pub(crate) async fn list_proposals(&self) -> Result, CoconutError> { diff --git a/nym-api/src/coconut/dkg/controller.rs b/nym-api/src/coconut/dkg/controller.rs index 3c1c353668..7329a2b62c 100644 --- a/nym-api/src/coconut/dkg/controller.rs +++ b/nym-api/src/coconut/dkg/controller.rs @@ -61,7 +61,7 @@ impl DkgController { config.secret_key_path(), config.verification_key_path(), )) { - coconut_keypair.set(coconut_keypair_value).await; + coconut_keypair.set(Some(coconut_keypair_value)).await; } let persistent_state = PersistentState::load_from_file(config.persistent_state_path()).unwrap_or_default(); @@ -86,8 +86,19 @@ impl DkgController { match self.dkg_client.get_current_epoch().await { Err(err) => warn!("Could not get current epoch state {err}"), Ok(epoch) => { + if self + .dkg_client + .group_member() + .await + .map(|resp| resp.weight.is_none()) + .unwrap_or(true) + { + debug!("Not a member of the group, DKG won't be run"); + return; + } if let Err(err) = self.state.is_consistent(epoch.state).await { error!("Epoch state is corrupted - {err}, the process should be terminated"); + return; } let ret = match epoch.state { EpochState::PublicKeySubmission => { @@ -115,7 +126,10 @@ impl DkgController { verification_key_finalization(&self.dkg_client, &mut self.state).await } // Just wait, in case we need to redo dkg at some point - EpochState::InProgress => Ok(()), + EpochState::InProgress => { + self.state.set_was_in_progress(); + Ok(()) + } }; if let Err(err) = ret { warn!("Could not handle this iteration for the epoch state: {err}"); @@ -132,7 +146,7 @@ impl DkgController { { if current_timestamp.as_secs() >= epoch.finish_timestamp.seconds() { // We try advancing the epoch state, on a best-effort basis - info!("Trying to advance the epoch"); + info!("DKG: Trying to advance the epoch"); self.dkg_client.advance_epoch_state().await.ok(); } } diff --git a/nym-api/src/coconut/dkg/public_key.rs b/nym-api/src/coconut/dkg/public_key.rs index aa3fa72330..67fd0ccce2 100644 --- a/nym-api/src/coconut/dkg/public_key.rs +++ b/nym-api/src/coconut/dkg/public_key.rs @@ -4,23 +4,31 @@ use crate::coconut::dkg::client::DkgClient; use crate::coconut::dkg::state::State; use crate::coconut::error::CoconutError; +use coconut_dkg_common::dealer::DealerType; pub(crate) async fn public_key_submission( dkg_client: &DkgClient, state: &mut State, ) -> Result<(), CoconutError> { + if state.was_in_progress() { + state.reset_persistent().await; + } if state.node_index().is_some() { return Ok(()); } let bte_key = bs58::encode(&state.dkg_keypair().public_key().to_bytes()).into_string(); - let index = if let Some(details) = dkg_client - .get_self_registered_dealer_details() - .await? - .details - { + let dealer_details = dkg_client.get_self_registered_dealer_details().await?; + let index = if let Some(details) = dealer_details.details { + if dealer_details.dealer_type == DealerType::Past { + // If it was a dealer in a previous epoch, re-register it for this epoch + dkg_client + .register_dealer(bte_key, state.announce_address().to_string()) + .await?; + } details.assigned_index } else { + // First time registration dkg_client .register_dealer(bte_key, state.announce_address().to_string()) .await? diff --git a/nym-api/src/coconut/dkg/state.rs b/nym-api/src/coconut/dkg/state.rs index 9ba1dfaffd..7a2c46c526 100644 --- a/nym-api/src/coconut/dkg/state.rs +++ b/nym-api/src/coconut/dkg/state.rs @@ -170,6 +170,7 @@ pub(crate) struct PersistentState { proposal_id: Option, voted_vks: bool, executed_proposal: bool, + was_in_progress: bool, } impl From<&State> for PersistentState { @@ -183,6 +184,7 @@ impl From<&State> for PersistentState { proposal_id: s.proposal_id, voted_vks: s.voted_vks, executed_proposal: s.executed_proposal, + was_in_progress: s.was_in_progress, } } } @@ -211,6 +213,7 @@ pub(crate) struct State { proposal_id: Option, voted_vks: bool, executed_proposal: bool, + was_in_progress: bool, } impl State { @@ -234,9 +237,23 @@ impl State { proposal_id: persistent_state.proposal_id, voted_vks: persistent_state.voted_vks, executed_proposal: persistent_state.executed_proposal, + was_in_progress: persistent_state.was_in_progress, } } + pub async fn reset_persistent(&mut self) { + self.coconut_keypair.set(None).await; + 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 persistent_state_path(&self) -> PathBuf { self.persistent_state_path.clone() } @@ -299,12 +316,16 @@ impl State { self.executed_proposal } + pub fn was_in_progress(&self) -> bool { + self.was_in_progress + } + pub fn set_recovered_vks(&mut self, recovered_vks: Vec) { self.recovered_vks = recovered_vks; } pub async fn set_coconut_keypair(&mut self, coconut_keypair: coconut_interface::KeyPair) { - self.coconut_keypair.set(coconut_keypair).await + self.coconut_keypair.set(Some(coconut_keypair)).await } pub fn set_node_index(&mut self, node_index: Option) { @@ -349,6 +370,10 @@ impl State { self.executed_proposal = true; } + pub fn set_was_in_progress(&mut self) { + self.was_in_progress = true; + } + #[cfg(test)] pub fn all_dealers(&self) -> &BTreeMap> { &self.dealers diff --git a/nym-api/src/coconut/dkg/verification_key.rs b/nym-api/src/coconut/dkg/verification_key.rs index 91b135277a..15f0138a90 100644 --- a/nym-api/src/coconut/dkg/verification_key.rs +++ b/nym-api/src/coconut/dkg/verification_key.rs @@ -177,7 +177,8 @@ pub(crate) async fn verification_key_validation( return Ok(()); } - let vk_shares = dkg_client.get_verification_key_shares().await?; + let epoch_id = dkg_client.get_current_epoch().await?.epoch_id; + let vk_shares = dkg_client.get_verification_key_shares(epoch_id).await?; let proposal_ids = BTreeMap::from_iter( dkg_client .list_proposals() diff --git a/nym-api/src/coconut/keypair.rs b/nym-api/src/coconut/keypair.rs index 680f1c4d13..e97ca82000 100644 --- a/nym-api/src/coconut/keypair.rs +++ b/nym-api/src/coconut/keypair.rs @@ -20,8 +20,8 @@ impl KeyPair { self.inner.read().await } - pub async fn set(&self, keypair: coconut_interface::KeyPair) { + pub async fn set(&self, keypair: Option) { let mut w_lock = self.inner.write().await; - *w_lock = Some(keypair); + *w_lock = keypair; } } diff --git a/nym-api/src/coconut/mod.rs b/nym-api/src/coconut/mod.rs index 0bd7fc8477..a0aa042440 100644 --- a/nym-api/src/coconut/mod.rs +++ b/nym-api/src/coconut/mod.rs @@ -9,6 +9,7 @@ use crate::support::storage::NymApiStorage; use coconut_bandwidth_contract_common::spend_credential::{ funds_from_cosmos_msgs, SpendCredentialStatus, }; +use coconut_dkg_common::types::EpochId; use coconut_interface::KeyPair as CoconutKeyPair; use coconut_interface::{ Attribute, BlindSignRequest, BlindedSignature, Parameters, VerificationKey, @@ -132,8 +133,10 @@ impl State { } } - pub async fn verification_key(&self) -> Result { - self.comm_channel.aggregated_verification_key().await + pub async fn verification_key(&self, epoch_id: EpochId) -> Result { + self.comm_channel + .aggregated_verification_key(epoch_id) + .await } } @@ -284,7 +287,9 @@ pub async fn verify_bandwidth_credential( status: format!("{:?}", credential_status), }); } - let verification_key = state.verification_key().await?; + let verification_key = state + .verification_key(*verify_credential_body.credential().epoch_id()) + .await?; let mut vote_yes = verify_credential_body .credential() .verify(&verification_key); diff --git a/nym-api/src/coconut/tests.rs b/nym-api/src/coconut/tests.rs index db222270d5..09ea5789ea 100644 --- a/nym-api/src/coconut/tests.rs +++ b/nym-api/src/coconut/tests.rs @@ -40,11 +40,12 @@ use coconut_dkg_common::dealer::{ ContractDealing, DealerDetails, DealerDetailsResponse, DealerType, }; use coconut_dkg_common::event_attributes::{DKG_PROPOSAL_ID, NODE_INDEX}; -use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, Epoch, TOTAL_DEALINGS}; +use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, Epoch, EpochId, TOTAL_DEALINGS}; use coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare}; use contracts_common::dealings::ContractSafeBytes; use crypto::asymmetric::{encryption, identity}; use cw3::ProposalResponse; +use cw4::MemberResponse; use dkg::Threshold; use rand_07::rngs::OsRng; use rand_07::Rng; @@ -192,6 +193,10 @@ impl super::client::Client for DummyClient { Ok(*self.epoch.read().unwrap()) } + async fn group_member(&self, _addr: String) -> Result { + todo!() + } + async fn get_current_epoch_threshold(&self) -> Result> { Ok(*self.threshold.read().unwrap()) } @@ -231,7 +236,10 @@ impl super::client::Client for DummyClient { .collect()) } - async fn get_verification_key_shares(&self) -> Result> { + async fn get_verification_key_shares( + &self, + _epoch_id: EpochId, + ) -> Result> { Ok(self .verification_share .read() @@ -342,6 +350,7 @@ impl super::client::Client for DummyClient { 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, }, ); @@ -398,7 +407,7 @@ impl DummyCommunicationChannel { #[async_trait] impl super::comm::APICommunicationChannel for DummyCommunicationChannel { - async fn aggregated_verification_key(&self) -> Result { + async fn aggregated_verification_key(&self, _epoch_id: EpochId) -> Result { Ok(self.aggregated_verification_key.clone()) } } @@ -471,7 +480,7 @@ async fn signed_before() { .with_tx_db(&tx_db); let comm_channel = DummyCommunicationChannel::new(key_pair.verification_key()); let staged_key_pair = crate::coconut::KeyPair::new(); - staged_key_pair.set(key_pair).await; + staged_key_pair.set(Some(key_pair)).await; let rocket = rocket::build().attach(InternalSignRequest::stage( nyxd_client, @@ -539,7 +548,7 @@ async fn state_functions() { let storage = NymApiStorage::init(db_dir).await.unwrap(); let comm_channel = DummyCommunicationChannel::new(key_pair.verification_key()); let staged_key_pair = crate::coconut::KeyPair::new(); - staged_key_pair.set(key_pair).await; + staged_key_pair.set(Some(key_pair)).await; let state = State::new( nyxd_client, TEST_COIN_DENOM.to_string(), @@ -708,7 +717,7 @@ async fn blind_sign_correct() { .with_tx_db(&tx_db); let comm_channel = DummyCommunicationChannel::new(key_pair.verification_key()); let staged_key_pair = crate::coconut::KeyPair::new(); - staged_key_pair.set(key_pair).await; + staged_key_pair.set(Some(key_pair)).await; let rocket = rocket::build().attach(InternalSignRequest::stage( nyxd_client, @@ -785,7 +794,7 @@ async fn signature_test() { DummyClient::new(AccountId::from_str(TEST_REWARDING_VALIDATOR_ADDRESS).unwrap()); let comm_channel = DummyCommunicationChannel::new(key_pair.verification_key()); let staged_key_pair = crate::coconut::KeyPair::new(); - staged_key_pair.set(key_pair).await; + staged_key_pair.set(Some(key_pair)).await; let rocket = rocket::build().attach(InternalSignRequest::stage( nyxd_client, @@ -872,7 +881,7 @@ async fn verification_of_bandwidth_credential() { let storage1 = NymApiStorage::init(db_dir).await.unwrap(); let comm_channel = DummyCommunicationChannel::new(key_pair.verification_key()); let staged_key_pair = crate::coconut::KeyPair::new(); - staged_key_pair.set(key_pair).await; + staged_key_pair.set(Some(key_pair)).await; let rocket = rocket::build().attach(InternalSignRequest::stage( nyxd_client.clone(), TEST_COIN_DENOM.to_string(), @@ -885,7 +894,7 @@ async fn verification_of_bandwidth_credential() { .await .expect("valid rocket instance"); - let credential = Credential::new(4, theta.clone(), voucher_value, voucher_info.to_string()); + let credential = Credential::new(4, theta.clone(), voucher_value, voucher_info.to_string(), 0); let proposal_id = 42; // The address is not used, so we can use a duplicate let gateway_cosmos_addr = validator_address.clone(); @@ -1036,6 +1045,7 @@ async fn verification_of_bandwidth_credential() { theta.clone(), voucher_value, String::from("bad voucher info"), + 0, ); let bad_req = VerifyCredentialBody::new(bad_credential, proposal_id, gateway_cosmos_addr.clone()); diff --git a/nym-api/src/network_monitor/mod.rs b/nym-api/src/network_monitor/mod.rs index 2d85101465..f062679da4 100644 --- a/nym-api/src/network_monitor/mod.rs +++ b/nym-api/src/network_monitor/mod.rs @@ -22,6 +22,7 @@ use futures::channel::mpsc; use gateway_client::bandwidth::BandwidthController; use std::sync::Arc; use task::TaskManager; +use validator_client::nyxd::SigningNyxdClient; pub(crate) mod chunker; pub(crate) mod gateways_reader; @@ -93,25 +94,14 @@ impl<'a> NetworkMonitorBuilder<'a> { *encryption_keypair.public_key(), ); - #[cfg(feature = "coconut")] let bandwidth_controller = { let client = self._nyxd_client.0.read().await; - let coconut_api_clients = - validator_client::CoconutApiClient::all_coconut_api_clients(&client) - .await - .expect("Could not query api clients"); BandwidthController::new( credential_storage::initialise_storage(self.config.get_credentials_database_path()) .await, - coconut_api_clients, + client.clone(), ) }; - #[cfg(not(feature = "coconut"))] - let bandwidth_controller = BandwidthController::new( - credential_storage::initialise_storage(self.config.get_credentials_database_path()) - .await, - ) - .expect("Could not create bandwidth controller"); let packet_sender = new_packet_sender( self.config, @@ -188,7 +178,7 @@ fn new_packet_sender( gateways_status_updater: GatewayClientUpdateSender, local_identity: Arc, max_sending_rate: usize, - bandwidth_controller: BandwidthController, + bandwidth_controller: BandwidthController, disabled_credentials_mode: bool, ) -> PacketSender { PacketSender::new( diff --git a/nym-api/src/network_monitor/monitor/gateway_clients_cache.rs b/nym-api/src/network_monitor/monitor/gateway_clients_cache.rs index 6d7f415912..8d0551d608 100644 --- a/nym-api/src/network_monitor/monitor/gateway_clients_cache.rs +++ b/nym-api/src/network_monitor/monitor/gateway_clients_cache.rs @@ -6,18 +6,21 @@ use gateway_client::GatewayClient; use std::collections::HashMap; use std::sync::Arc; use tokio::sync::{Mutex, MutexGuard, TryLockError}; +use validator_client::nyxd::SigningNyxdClient; pub(crate) struct GatewayClientHandle(Arc); struct GatewayClientHandleInner { - client: Mutex>, + client: Mutex>>, raw_identity: [u8; PUBLIC_KEY_LENGTH], } -pub(crate) struct UnlockedGatewayClientHandle<'a>(MutexGuard<'a, Option>); +pub(crate) struct UnlockedGatewayClientHandle<'a>( + MutexGuard<'a, Option>>, +); impl GatewayClientHandle { - pub(crate) fn new(gateway_client: GatewayClient) -> Self { + pub(crate) fn new(gateway_client: GatewayClient) -> Self { GatewayClientHandle(Arc::new(GatewayClientHandleInner { raw_identity: gateway_client.gateway_identity().to_bytes(), client: Mutex::new(Some(gateway_client)), @@ -56,11 +59,11 @@ impl GatewayClientHandle { } impl<'a> UnlockedGatewayClientHandle<'a> { - pub(crate) fn get_mut_unchecked(&mut self) -> &mut GatewayClient { + pub(crate) fn get_mut_unchecked(&mut self) -> &mut GatewayClient { self.0.as_mut().unwrap() } - pub(crate) fn inner_mut(&mut self) -> Option<&mut GatewayClient> { + pub(crate) fn inner_mut(&mut self) -> Option<&mut GatewayClient> { self.0.as_mut() } diff --git a/nym-api/src/network_monitor/monitor/sender.rs b/nym-api/src/network_monitor/monitor/sender.rs index 5c6f3df22d..bcb099adf4 100644 --- a/nym-api/src/network_monitor/monitor/sender.rs +++ b/nym-api/src/network_monitor/monitor/sender.rs @@ -27,6 +27,7 @@ use std::time::Duration; use task::TaskClient; use gateway_client::bandwidth::BandwidthController; +use validator_client::nyxd::SigningNyxdClient; const TIME_CHUNK_SIZE: Duration = Duration::from_millis(50); @@ -90,7 +91,7 @@ struct FreshGatewayClientData { gateways_status_updater: GatewayClientUpdateSender, local_identity: Arc, gateway_response_timeout: Duration, - bandwidth_controller: BandwidthController, + bandwidth_controller: BandwidthController, disabled_credentials_mode: bool, } @@ -148,7 +149,7 @@ impl PacketSender { gateway_connection_timeout: Duration, max_concurrent_clients: usize, max_sending_rate: usize, - bandwidth_controller: BandwidthController, + bandwidth_controller: BandwidthController, disabled_credentials_mode: bool, ) -> Self { PacketSender { @@ -218,7 +219,7 @@ impl PacketSender { } async fn attempt_to_send_packets( - client: &mut GatewayClient, + client: &mut GatewayClient, mut mix_packets: Vec, max_sending_rate: usize, ) -> Result<(), GatewayClientError> { @@ -327,7 +328,7 @@ impl PacketSender { } async fn check_remaining_bandwidth( - client: &mut GatewayClient, + client: &mut GatewayClient, ) -> Result<(), GatewayClientError> { if client.remaining_bandwidth() < REMAINING_BANDWIDTH_THRESHOLD { Err(GatewayClientError::NotEnoughBandwidth( diff --git a/nym-api/src/support/cli/mod.rs b/nym-api/src/support/cli/mod.rs index 806e073287..264e9a66c4 100644 --- a/nym-api/src/support/cli/mod.rs +++ b/nym-api/src/support/cli/mod.rs @@ -99,7 +99,7 @@ pub(crate) struct CliArgs { /// Flag to indicate whether coconut signer authority is enabled on this API #[cfg(feature = "coconut")] - #[clap(long, requires = "mnemonic", requires = "announce-address")] + #[clap(long, requires = "mnemonic", requires = "announce_address")] pub(crate) enable_coconut: Option, } diff --git a/nym-api/src/support/nyxd/mod.rs b/nym-api/src/support/nyxd/mod.rs index 4492fbaa04..5efce0f0e9 100644 --- a/nym-api/src/support/nyxd/mod.rs +++ b/nym-api/src/support/nyxd/mod.rs @@ -32,7 +32,7 @@ use coconut_bandwidth_contract_common::spend_credential::SpendCredentialResponse #[cfg(feature = "coconut")] use coconut_dkg_common::{ dealer::{ContractDealing, DealerDetails, DealerDetailsResponse}, - types::{EncodedBTEPublicKeyWithProof, Epoch}, + types::{EncodedBTEPublicKeyWithProof, Epoch, EpochId}, verification_key::{ContractVKShare, VerificationKeyShare}, }; #[cfg(feature = "coconut")] @@ -40,11 +40,13 @@ use contracts_common::dealings::ContractSafeBytes; #[cfg(feature = "coconut")] use cw3::ProposalResponse; #[cfg(feature = "coconut")] +use cw4::MemberResponse; +#[cfg(feature = "coconut")] use validator_client::nyxd::{ cosmwasm_client::types::ExecuteResult, traits::{ - CoconutBandwidthQueryClient, DkgQueryClient, DkgSigningClient, MultisigQueryClient, - MultisigSigningClient, + CoconutBandwidthQueryClient, DkgQueryClient, DkgSigningClient, GroupQueryClient, + MultisigQueryClient, MultisigSigningClient, }, Fee, }; @@ -327,6 +329,10 @@ impl crate::coconut::client::Client for Client { Ok(self.0.read().await.nyxd.get_current_epoch().await?) } + async fn group_member(&self, addr: String) -> crate::coconut::error::Result { + Ok(self.0.read().await.nyxd.member(addr).await?) + } + async fn get_current_epoch_threshold( &self, ) -> crate::coconut::error::Result> { @@ -365,12 +371,13 @@ impl crate::coconut::client::Client for Client { async fn get_verification_key_shares( &self, + epoch_id: EpochId, ) -> crate::coconut::error::Result> { Ok(self .0 .read() .await - .get_all_nyxd_verification_key_shares() + .get_all_nyxd_verification_key_shares(epoch_id) .await?) } diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index 09241b5cfa..acce120ca4 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -1892,6 +1892,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "group-contract-common" +version = "0.1.0" +dependencies = [ + "cw4", + "schemars", + "serde", +] + [[package]] name = "gtk" version = "0.15.4" @@ -5403,9 +5412,11 @@ dependencies = [ "cosmrs", "cosmwasm-std", "cw3", + "cw4", "execute", "flate2", "futures", + "group-contract-common", "itertools", "log", "mixnet-contract-common", diff --git a/nym-wallet/nym-wallet-types/src/network.rs b/nym-wallet/nym-wallet-types/src/network.rs index 0506405baf..7a2275a092 100644 --- a/nym-wallet/nym-wallet-types/src/network.rs +++ b/nym-wallet/nym-wallet-types/src/network.rs @@ -109,6 +109,7 @@ mod sandbox { "nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv"; pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str = "nymt1nz0r0au8aj6dc00wmm3ufy4g4k86rjzlgq608r"; + pub(crate) const GROUP_CONTRACT_ADDRESS: &str = "nymt1k8re7jwz6rnnwrktnejdwkwnncte7ek7kk6fvg"; pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str = "nymt1k8re7jwz6rnnwrktnejdwkwnncte7ek7kk6fvg"; pub(crate) const COCONUT_DKG_CONTRACT_ADDRESS: &str = @@ -145,6 +146,7 @@ mod sandbox { coconut_bandwidth_contract_address: parse_optional_str( COCONUT_BANDWIDTH_CONTRACT_ADDRESS, ), + group_contract_address: parse_optional_str(GROUP_CONTRACT_ADDRESS), multisig_contract_address: parse_optional_str(MULTISIG_CONTRACT_ADDRESS), coconut_dkg_contract_address: parse_optional_str(COCONUT_DKG_CONTRACT_ADDRESS), }, @@ -170,6 +172,7 @@ mod qa { "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str = "n1ghd753shjuwexxywmgs4xz7x2q732vcn7ty4yw"; + pub(crate) const GROUP_CONTRACT_ADDRESS: &str = "n17p9rzwnnfxcjp32un9ug7yhhzgtkhvl988qccs"; pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str = "n17p9rzwnnfxcjp32un9ug7yhhzgtkhvl988qccs"; pub(crate) const COCONUT_DKG_CONTRACT_ADDRESS: &str = "n17p9rzwnnfxcjp32un9ug7yhhzgtkhvl988qccs"; @@ -204,6 +207,7 @@ mod qa { coconut_bandwidth_contract_address: parse_optional_str( COCONUT_BANDWIDTH_CONTRACT_ADDRESS, ), + group_contract_address: parse_optional_str(GROUP_CONTRACT_ADDRESS), multisig_contract_address: parse_optional_str(MULTISIG_CONTRACT_ADDRESS), coconut_dkg_contract_address: parse_optional_str(COCONUT_DKG_CONTRACT_ADDRESS), }, diff --git a/sdk/rust/nym-sdk/Cargo.toml b/sdk/rust/nym-sdk/Cargo.toml index ac7ce86ad1..a60e2dc447 100644 --- a/sdk/rust/nym-sdk/Cargo.toml +++ b/sdk/rust/nym-sdk/Cargo.toml @@ -14,6 +14,7 @@ gateway-requests = { path = "../../../gateway/gateway-requests" } network-defaults = { path = "../../../common/network-defaults" } nymsphinx = { path = "../../../common/nymsphinx" } task = { path = "../../../common/task" } +validator-client = { path = "../../../common/client-libs/validator-client" } futures = "0.3" log = { workspace = true } diff --git a/sdk/rust/nym-sdk/src/mixnet/client.rs b/sdk/rust/nym-sdk/src/mixnet/client.rs index 99fca9e424..13d14beca5 100644 --- a/sdk/rust/nym-sdk/src/mixnet/client.rs +++ b/sdk/rust/nym-sdk/src/mixnet/client.rs @@ -21,6 +21,7 @@ use nymsphinx::{ use task::TaskManager; use futures::StreamExt; +use validator_client::nyxd::SigningNyxdClient; use super::{connection_state::BuilderState, Config, GatewayKeyMode, Keys, KeysArc, StoragePaths}; use crate::error::{Error, Result}; @@ -224,7 +225,7 @@ impl ClientBuilder { let reply_storage_backend = non_wasm_helpers::setup_empty_reply_surb_backend(&self.config.debug_config); - let base_builder = BaseClientBuilder::new( + let base_builder: BaseClientBuilder<_, SigningNyxdClient> = BaseClientBuilder::new( &gateway_endpoint_config, &self.config.debug_config, self.key_manager.clone(),