From dfd7bd5889fdedd525a85a89230e5593b2f565ba Mon Sep 17 00:00:00 2001 From: benedettadavico Date: Fri, 10 Mar 2023 17:10:01 +0100 Subject: [PATCH 01/14] adding pledge more --- .../src/validator/mixnet/delegators/mod.rs | 6 ++++ .../mixnet/delegators/pledge_more.rs | 29 ++++++++++++++++++ .../mixnet/delegators/vesting_pledge_more.rs | 30 +++++++++++++++++++ .../src/validator/mixnet/delegators/mod.rs | 6 ++++ 4 files changed, 71 insertions(+) create mode 100644 common/commands/src/validator/mixnet/delegators/pledge_more.rs create mode 100644 common/commands/src/validator/mixnet/delegators/vesting_pledge_more.rs diff --git a/common/commands/src/validator/mixnet/delegators/mod.rs b/common/commands/src/validator/mixnet/delegators/mod.rs index 61e87f119d..6acdfd1962 100644 --- a/common/commands/src/validator/mixnet/delegators/mod.rs +++ b/common/commands/src/validator/mixnet/delegators/mod.rs @@ -10,6 +10,8 @@ pub mod query_for_delegations; pub mod undelegate_from_mixnode; pub mod vesting_delegate_to_mixnode; pub mod vesting_undelegate_from_mixnode; +pub mod pledge_more; +pub mod vesting_pledge_more; #[derive(Debug, Args)] #[clap(args_conflicts_with_subcommands = true, subcommand_required = true)] @@ -32,4 +34,8 @@ pub enum MixnetDelegatorsCommands { DelegateVesting(vesting_delegate_to_mixnode::Args), /// Undelegate from a mixnode (when originally using locked tokens) UndelegateVesting(vesting_undelegate_from_mixnode::Args), + /// Pledge more + PledgeMore(pledge_more::Args), + /// Pledge more with locked tokens + PledgeMoreVesting(vesting_pledge_more::Args), } diff --git a/common/commands/src/validator/mixnet/delegators/pledge_more.rs b/common/commands/src/validator/mixnet/delegators/pledge_more.rs new file mode 100644 index 0000000000..7270295019 --- /dev/null +++ b/common/commands/src/validator/mixnet/delegators/pledge_more.rs @@ -0,0 +1,29 @@ +// Copyright 2021 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::context::SigningClient; +use clap::Parser; +use log::info; +use nym_mixnet_contract_common::{Coin}; +use validator_client::nyxd::traits::{MixnetSigningClient}; + +#[derive(Debug, Parser)] +pub struct Args { + #[clap(long)] + pub amount: u128, +} + +pub async fn pledge_more(args: Args, client: SigningClient) { + let denom = client.current_chain_details().mix_denom.base.as_str(); + + info!("Starting to pledge more"); + + let coin = Coin::new(args.amount, denom); + + let res = client + .pledge_more( coin.into(), None) + .await + .expect("failed to pledge more!"); + + info!("pledging more: {:?}", res); +} diff --git a/common/commands/src/validator/mixnet/delegators/vesting_pledge_more.rs b/common/commands/src/validator/mixnet/delegators/vesting_pledge_more.rs new file mode 100644 index 0000000000..cf9c1042bd --- /dev/null +++ b/common/commands/src/validator/mixnet/delegators/vesting_pledge_more.rs @@ -0,0 +1,30 @@ +// Copyright 2021 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use clap::Parser; +use log::info; +use nym_mixnet_contract_common::{Coin}; +use validator_client::nyxd::VestingSigningClient; + +use crate::context::SigningClient; + +#[derive(Debug, Parser)] +pub struct Args { + #[clap(long)] + pub amount: u128, +} + +pub async fn vesting_pledge_more(args: Args, client: SigningClient) { + let denom = client.current_chain_details().mix_denom.base.as_str(); + + info!("Starting vesting pledge more"); + + let coin = Coin::new(args.amount, denom); + + let res = client + .vesting_pledge_more(coin.into(), None) + .await + .expect("failed to pledge more!"); + + info!("vesting pledge more: {:?}", res); +} diff --git a/tools/nym-cli/src/validator/mixnet/delegators/mod.rs b/tools/nym-cli/src/validator/mixnet/delegators/mod.rs index 76776b1146..30222de4c8 100644 --- a/tools/nym-cli/src/validator/mixnet/delegators/mod.rs +++ b/tools/nym-cli/src/validator/mixnet/delegators/mod.rs @@ -20,6 +20,12 @@ pub(crate) async fn execute( nym_cli_commands::validator::mixnet::delegators::MixnetDelegatorsCommands::Delegate(args) => { nym_cli_commands::validator::mixnet::delegators::delegate_to_mixnode::delegate_to_mixnode(args, create_signing_client(global_args, network_details)?).await } + nym_cli_commands::validator::mixnet::delegators::MixnetDelegatorsCommands::PledgeMore(args) => { + nym_cli_commands::validator::mixnet::delegators::pledge_more::pledge_more(args, create_signing_client(global_args, network_details)?).await + } + nym_cli_commands::validator::mixnet::delegators::MixnetDelegatorsCommands::PledgeMoreVesting(args) => { + nym_cli_commands::validator::mixnet::delegators::vesting_pledge_more::vesting_pledge_more(args, create_signing_client(global_args, network_details)?).await + } nym_cli_commands::validator::mixnet::delegators::MixnetDelegatorsCommands::Undelegate(args) => { nym_cli_commands::validator::mixnet::delegators::undelegate_from_mixnode::undelegate_from_mixnode(args, create_signing_client(global_args, network_details)?).await } From c2a871a1a7bb6d8f09c996ca9decb0450b724a34 Mon Sep 17 00:00:00 2001 From: benedettadavico Date: Fri, 10 Mar 2023 17:10:27 +0100 Subject: [PATCH 02/14] typo --- .../validator/mixnet/operators/gateway/mod.rs | 8 ++++---- .../validator/mixnet/operators/mixnode/mod.rs | 8 ++++---- .../validator/mixnet/operators/gateways/mod.rs | 2 +- .../validator/mixnet/operators/mixnodes/mod.rs | 2 +- tools/nym-cli/user-docs/fig-spec.ts | 16 ++++++++-------- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/common/commands/src/validator/mixnet/operators/gateway/mod.rs b/common/commands/src/validator/mixnet/operators/gateway/mod.rs index af658a7876..6eee68243a 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/mod.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/mod.rs @@ -19,10 +19,10 @@ pub struct MixnetOperatorsGateway { pub enum MixnetOperatorsGatewayCommands { /// Bond to a gateway Bond(bond_gateway::Args), - /// Unbound from a gateway - Unbound(unbond_gateway::Args), + /// Unbond from a gateway + Unbond(unbond_gateway::Args), /// Bond to a gateway with locked tokens VestingBond(vesting_bond_gateway::Args), - /// Unbound from a gateway (when originally using locked tokens) - VestingUnbound(vesting_unbond_gateway::Args), + /// Unbond from a gateway (when originally using locked tokens) + VestingUnbond(vesting_unbond_gateway::Args), } diff --git a/common/commands/src/validator/mixnet/operators/mixnode/mod.rs b/common/commands/src/validator/mixnet/operators/mixnode/mod.rs index e608c4c36f..ef988de113 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/mod.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/mod.rs @@ -28,10 +28,10 @@ pub enum MixnetOperatorsMixnodeCommands { Settings(settings::MixnetOperatorsMixnodeSettings), /// Bond to a mixnode Bond(bond_mixnode::Args), - /// Unbound from a mixnode - Unbound(unbond_mixnode::Args), + /// Unbond from a mixnode + Unbond(unbond_mixnode::Args), /// Bond to a mixnode with locked tokens BondVesting(vesting_bond_mixnode::Args), - /// Unbound from a mixnode (when originally using locked tokens) - UnboundVesting(vesting_unbond_mixnode::Args), + /// Unbond from a mixnode (when originally using locked tokens) + UnbondVesting(vesting_unbond_mixnode::Args), } diff --git a/tools/nym-cli/src/validator/mixnet/operators/gateways/mod.rs b/tools/nym-cli/src/validator/mixnet/operators/gateways/mod.rs index b7bde45cf3..f55dd56707 100644 --- a/tools/nym-cli/src/validator/mixnet/operators/gateways/mod.rs +++ b/tools/nym-cli/src/validator/mixnet/operators/gateways/mod.rs @@ -13,7 +13,7 @@ pub(crate) async fn execute( nym_cli_commands::validator::mixnet::operators::gateway::MixnetOperatorsGatewayCommands::Bond(args) => { nym_cli_commands::validator::mixnet::operators::gateway::bond_gateway::bond_gateway(args, create_signing_client(global_args, network_details)?).await }, - nym_cli_commands::validator::mixnet::operators::gateway::MixnetOperatorsGatewayCommands::Unbound(_args) => { + nym_cli_commands::validator::mixnet::operators::gateway::MixnetOperatorsGatewayCommands::Unbond(_args) => { nym_cli_commands::validator::mixnet::operators::gateway::unbond_gateway::unbond_gateway(create_signing_client(global_args, network_details)?).await }, _ => unreachable!(), diff --git a/tools/nym-cli/src/validator/mixnet/operators/mixnodes/mod.rs b/tools/nym-cli/src/validator/mixnet/operators/mixnodes/mod.rs index fbe07679fb..8d70aa4387 100644 --- a/tools/nym-cli/src/validator/mixnet/operators/mixnodes/mod.rs +++ b/tools/nym-cli/src/validator/mixnet/operators/mixnodes/mod.rs @@ -26,7 +26,7 @@ pub(crate) async fn execute( nym_cli_commands::validator::mixnet::operators::mixnode::MixnetOperatorsMixnodeCommands::Bond(args) => { nym_cli_commands::validator::mixnet::operators::mixnode::bond_mixnode::bond_mixnode(args, create_signing_client(global_args, network_details)?).await } - nym_cli_commands::validator::mixnet::operators::mixnode::MixnetOperatorsMixnodeCommands::Unbound(args) => { + nym_cli_commands::validator::mixnet::operators::mixnode::MixnetOperatorsMixnodeCommands::Unbond(args) => { nym_cli_commands::validator::mixnet::operators::mixnode::unbond_mixnode::unbond_mixnode(args, create_signing_client(global_args, network_details)?).await } _ => unreachable!(), diff --git a/tools/nym-cli/user-docs/fig-spec.ts b/tools/nym-cli/user-docs/fig-spec.ts index 3fbb1c213a..87bdd0eca1 100644 --- a/tools/nym-cli/user-docs/fig-spec.ts +++ b/tools/nym-cli/user-docs/fig-spec.ts @@ -3803,8 +3803,8 @@ const completion: Fig.Spec = { ], }, { - name: "unbound", - description: "Unbound from a mixnode", + name: "unbond", + description: "Unbond from a mixnode", options: [ { name: "--mnemonic", @@ -3994,8 +3994,8 @@ const completion: Fig.Spec = { ], }, { - name: "unbound-vesting", - description: "Unbound from a mixnode (when originally using locked tokens)", + name: "unbond-vesting", + description: "Unbond from a mixnode (when originally using locked tokens)", options: [ { name: "--gas", @@ -4296,8 +4296,8 @@ const completion: Fig.Spec = { ], }, { - name: "unbound", - description: "Unbound from a gateway", + name: "unbond", + description: "Unbond from a gateway", options: [ { name: "--mnemonic", @@ -4473,8 +4473,8 @@ const completion: Fig.Spec = { ], }, { - name: "vesting-unbound", - description: "Unbound from a gateway (when originally using locked tokens)", + name: "vesting-unbond", + description: "Unbond from a gateway (when originally using locked tokens)", options: [ { name: "--mnemonic", From 82c92501d988992ae074f0f4e5f8d72bfaaf201d Mon Sep 17 00:00:00 2001 From: benedettadavico Date: Mon, 13 Mar 2023 12:01:22 +0100 Subject: [PATCH 03/14] vesting stuff --- tools/nym-cli/src/validator/mixnet/delegators/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/nym-cli/src/validator/mixnet/delegators/mod.rs b/tools/nym-cli/src/validator/mixnet/delegators/mod.rs index 30222de4c8..af40520255 100644 --- a/tools/nym-cli/src/validator/mixnet/delegators/mod.rs +++ b/tools/nym-cli/src/validator/mixnet/delegators/mod.rs @@ -20,6 +20,9 @@ pub(crate) async fn execute( nym_cli_commands::validator::mixnet::delegators::MixnetDelegatorsCommands::Delegate(args) => { nym_cli_commands::validator::mixnet::delegators::delegate_to_mixnode::delegate_to_mixnode(args, create_signing_client(global_args, network_details)?).await } + nym_cli_commands::validator::mixnet::delegators::MixnetDelegatorsCommands::DelegateVesting(args) => { + nym_cli_commands::validator::mixnet::delegators::vesting_delegate_to_mixnode::vesting_delegate_to_mixnode(args, create_signing_client(global_args, network_details)?).await + } nym_cli_commands::validator::mixnet::delegators::MixnetDelegatorsCommands::PledgeMore(args) => { nym_cli_commands::validator::mixnet::delegators::pledge_more::pledge_more(args, create_signing_client(global_args, network_details)?).await } @@ -29,6 +32,9 @@ pub(crate) async fn execute( nym_cli_commands::validator::mixnet::delegators::MixnetDelegatorsCommands::Undelegate(args) => { nym_cli_commands::validator::mixnet::delegators::undelegate_from_mixnode::undelegate_from_mixnode(args, create_signing_client(global_args, network_details)?).await } + nym_cli_commands::validator::mixnet::delegators::MixnetDelegatorsCommands::UndelegateVesting(args) => { + nym_cli_commands::validator::mixnet::delegators::vesting_undelegate_from_mixnode::vesting_undelegate_from_mixnode(args, create_signing_client(global_args, network_details)?).await + } nym_cli_commands::validator::mixnet::delegators::MixnetDelegatorsCommands::List(args) => { nym_cli_commands::validator::mixnet::delegators::query_for_delegations::execute(args, create_signing_client_with_nym_api(global_args, network_details)?).await } From 30c07712e35a491c92de9338d5bcce6bd9d83201 Mon Sep 17 00:00:00 2001 From: benedettadavico Date: Mon, 13 Mar 2023 12:03:38 +0100 Subject: [PATCH 04/14] format --- common/commands/src/validator/mixnet/delegators/mod.rs | 4 ++-- .../commands/src/validator/mixnet/delegators/pledge_more.rs | 6 +++--- .../src/validator/mixnet/delegators/vesting_pledge_more.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/common/commands/src/validator/mixnet/delegators/mod.rs b/common/commands/src/validator/mixnet/delegators/mod.rs index 6acdfd1962..1fa955c5f7 100644 --- a/common/commands/src/validator/mixnet/delegators/mod.rs +++ b/common/commands/src/validator/mixnet/delegators/mod.rs @@ -6,12 +6,12 @@ use clap::{Args, Subcommand}; pub mod rewards; pub mod delegate_to_mixnode; +pub mod pledge_more; pub mod query_for_delegations; pub mod undelegate_from_mixnode; pub mod vesting_delegate_to_mixnode; -pub mod vesting_undelegate_from_mixnode; -pub mod pledge_more; pub mod vesting_pledge_more; +pub mod vesting_undelegate_from_mixnode; #[derive(Debug, Args)] #[clap(args_conflicts_with_subcommands = true, subcommand_required = true)] diff --git a/common/commands/src/validator/mixnet/delegators/pledge_more.rs b/common/commands/src/validator/mixnet/delegators/pledge_more.rs index 7270295019..b368938515 100644 --- a/common/commands/src/validator/mixnet/delegators/pledge_more.rs +++ b/common/commands/src/validator/mixnet/delegators/pledge_more.rs @@ -4,8 +4,8 @@ use crate::context::SigningClient; use clap::Parser; use log::info; -use nym_mixnet_contract_common::{Coin}; -use validator_client::nyxd::traits::{MixnetSigningClient}; +use nym_mixnet_contract_common::Coin; +use validator_client::nyxd::traits::MixnetSigningClient; #[derive(Debug, Parser)] pub struct Args { @@ -21,7 +21,7 @@ pub async fn pledge_more(args: Args, client: SigningClient) { let coin = Coin::new(args.amount, denom); let res = client - .pledge_more( coin.into(), None) + .pledge_more(coin.into(), None) .await .expect("failed to pledge more!"); diff --git a/common/commands/src/validator/mixnet/delegators/vesting_pledge_more.rs b/common/commands/src/validator/mixnet/delegators/vesting_pledge_more.rs index cf9c1042bd..8971053563 100644 --- a/common/commands/src/validator/mixnet/delegators/vesting_pledge_more.rs +++ b/common/commands/src/validator/mixnet/delegators/vesting_pledge_more.rs @@ -3,7 +3,7 @@ use clap::Parser; use log::info; -use nym_mixnet_contract_common::{Coin}; +use nym_mixnet_contract_common::Coin; use validator_client::nyxd::VestingSigningClient; use crate::context::SigningClient; From adb5ed7c30f016bab9edbb92e779fb9996b4b888 Mon Sep 17 00:00:00 2001 From: benedettadavico Date: Mon, 13 Mar 2023 12:14:14 +0100 Subject: [PATCH 05/14] typo fix --- common/commands/src/validator/mixnet/operators/gateway/mod.rs | 4 ++-- common/commands/src/validator/mixnet/operators/mixnode/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/commands/src/validator/mixnet/operators/gateway/mod.rs b/common/commands/src/validator/mixnet/operators/gateway/mod.rs index 4e7159d6c4..1cdd67520b 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/mod.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/mod.rs @@ -24,8 +24,8 @@ pub enum MixnetOperatorsGatewayCommands { Unbond(unbond_gateway::Args), /// Bond to a gateway with locked tokens VestingBond(vesting_bond_gateway::Args), - /// Unbound from a gateway (when originally using locked tokens) - VestingUnbound(vesting_unbond_gateway::Args), + /// Unbond from a gateway (when originally using locked tokens) + VestingUnbond(vesting_unbond_gateway::Args), /// Create base58-encoded payload required for producing valid bonding signature. CreateGatewayBondingSignPayload(gateway_bonding_sign_payload::Args), } diff --git a/common/commands/src/validator/mixnet/operators/mixnode/mod.rs b/common/commands/src/validator/mixnet/operators/mixnode/mod.rs index bd91623041..5ba93b4e29 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/mod.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/mod.rs @@ -33,8 +33,8 @@ pub enum MixnetOperatorsMixnodeCommands { Unbond(unbond_mixnode::Args), /// Bond to a mixnode with locked tokens BondVesting(vesting_bond_mixnode::Args), - /// Unbound from a mixnode (when originally using locked tokens) - UnboundVesting(vesting_unbond_mixnode::Args), + /// Unbond from a mixnode (when originally using locked tokens) + UnbondVesting(vesting_unbond_mixnode::Args), /// Create base58-encoded payload required for producing valid bonding signature. CreateMixnodeBondingSignPayload(mixnode_bonding_sign_payload::Args), } From 54287666e81a8a5ee6dd6d57b51306881ceee596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Mon, 13 Mar 2023 11:29:56 +0000 Subject: [PATCH 06/14] chore: simplify mnemonic zeroize story (#3165) * updated bip39 dependency to simplify our zeroize story * Replaced UserPassword wrapper with Zeroizing type alias * fixed wallet-types cosmwasm-std dependency version --- Cargo.lock | 61 ++-- Cargo.toml | 1 + clients/credential/Cargo.toml | 2 +- .../client-libs/validator-client/Cargo.toml | 4 +- .../validator-client/src/nyxd/wallet/mod.rs | 26 +- common/commands/Cargo.toml | 2 +- gateway/Cargo.toml | 2 +- nym-api/Cargo.toml | 2 +- nym-connect/desktop/src-tauri/Cargo.toml | 2 +- nym-connect/mobile/src-tauri/Cargo.toml | 2 +- nym-wallet/Cargo.lock | 256 ++++----------- nym-wallet/nym-wallet-recovery-cli/Cargo.toml | 2 +- nym-wallet/nym-wallet-types/Cargo.toml | 2 +- nym-wallet/src-tauri/Cargo.toml | 5 +- .../src/operations/mixnet/account.rs | 57 ++-- nym-wallet/src-tauri/src/utils.rs | 95 +----- .../src/wallet_storage/account_data.rs | 57 +--- .../src-tauri/src/wallet_storage/mod.rs | 298 +++++++----------- .../src-tauri/src/wallet_storage/password.rs | 31 +- tools/nym-cli/Cargo.toml | 2 +- 20 files changed, 255 insertions(+), 654 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e262a0b76a..7c15040c17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -302,22 +302,23 @@ dependencies = [ [[package]] name = "bip39" -version = "1.0.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e89470017230c38e52b82b3ee3f530db1856ba1d434e3a67a3456a8a8dec5f" +checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" dependencies = [ "bitcoin_hashes", - "rand 0.6.5", - "rand_core 0.4.2", + "rand 0.8.5", + "rand_core 0.6.4", "serde", "unicode-normalization", + "zeroize", ] [[package]] name = "bitcoin_hashes" -version = "0.9.7" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ce18265ec2324ad075345d5814fbeed4f41f0a660055dc78840b74d19b874b1" +checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" [[package]] name = "bitflags" @@ -2845,12 +2846,6 @@ dependencies = [ "serde", ] -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - [[package]] name = "memchr" version = "2.5.0" @@ -4171,7 +4166,7 @@ dependencies = [ "instant", "libc", "redox_syscall", - "smallvec 1.10.0", + "smallvec", "winapi", ] @@ -4184,7 +4179,7 @@ dependencies = [ "cfg-if", "libc", "redox_syscall", - "smallvec 1.10.0", + "smallvec", "windows-sys 0.45.0", ] @@ -5057,7 +5052,7 @@ dependencies = [ "pin-project-lite", "ref-cast", "serde", - "smallvec 1.10.0", + "smallvec", "stable-pattern", "state", "time 0.3.17", @@ -5552,15 +5547,6 @@ dependencies = [ "autocfg 1.1.0", ] -[[package]] -name = "smallvec" -version = "0.6.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" -dependencies = [ - "maybe-uninit", -] - [[package]] name = "smallvec" version = "1.10.0" @@ -5725,7 +5711,7 @@ dependencies = [ "percent-encoding", "rustls 0.19.1", "sha2 0.10.6", - "smallvec 1.10.0", + "smallvec", "sqlformat 0.1.8", "sqlx-rt 0.5.13", "stringprep", @@ -5773,7 +5759,7 @@ dependencies = [ "rustls 0.20.8", "rustls-pemfile", "sha2 0.10.6", - "smallvec 1.10.0", + "smallvec", "sqlformat 0.2.1", "sqlx-rt 0.6.2", "stringprep", @@ -6198,6 +6184,21 @@ dependencies = [ "serde_json", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.25.0" @@ -6495,7 +6496,7 @@ dependencies = [ "once_cell", "regex", "sharded-slab", - "smallvec 1.10.0", + "smallvec", "thread_local", "tracing", "tracing-core", @@ -6661,11 +6662,11 @@ checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-normalization" -version = "0.1.9" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09c8070a9942f5e7cfccd93f490fdebd230ee3c3c9f107cb25bad5351ef671cf" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ - "smallvec 0.6.14", + "tinyvec", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f3567b79b3..59b80615b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,6 +105,7 @@ license = "Apache-2.0" [workspace.dependencies] async-trait = "0.1.64" +bip39 = { version = "2.0.0", features = ["zeroize"] } cfg-if = "1.0.0" dotenvy = "0.15.6" lazy_static = "1.4.0" diff --git a/clients/credential/Cargo.toml b/clients/credential/Cargo.toml index ca836f3858..b82767a434 100644 --- a/clients/credential/Cargo.toml +++ b/clients/credential/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bip39 = "1.0.1" +bip39 = { workspace = true } clap = { version = "4.0", features = ["cargo", "derive"] } log = "0.4" rand = "0.7.3" diff --git a/common/client-libs/validator-client/Cargo.toml b/common/client-libs/validator-client/Cargo.toml index f218d7a6c7..312a23ccc2 100644 --- a/common/client-libs/validator-client/Cargo.toml +++ b/common/client-libs/validator-client/Cargo.toml @@ -37,7 +37,7 @@ nym-execute = { path = "../../execute" } # at some point it might be possible to make it wasm-compatible # perhaps after https://github.com/cosmos/cosmos-rust/pull/97 is resolved (and tendermint-rs is updated) async-trait = { workspace = true, optional = true } -bip39 = { version = "1", features = ["rand"], optional = true } +bip39 = { workspace = true, features = ["rand"], optional = true } nym-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 } @@ -47,7 +47,7 @@ flate2 = { version = "1.0.20", optional = true } sha2 = { version = "0.9.5", optional = true } itertools = { version = "0.10", optional = true } cosmwasm-std = { version = "1.0.0", optional = true } -zeroize = { version = "1.5.7", optional = true } +zeroize = { version = "1.5.7", optional = true, features = ["zeroize_derive"] } [dev-dependencies] ts-rs = "6.1.2" diff --git a/common/client-libs/validator-client/src/nyxd/wallet/mod.rs b/common/client-libs/validator-client/src/nyxd/wallet/mod.rs index c859b6fecc..a668446c9d 100644 --- a/common/client-libs/validator-client/src/nyxd/wallet/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/wallet/mod.rs @@ -8,7 +8,7 @@ use cosmrs::crypto::PublicKey; use cosmrs::tx::SignDoc; use cosmrs::{tx, AccountId}; use nym_config::defaults; -use zeroize::Zeroize; +use zeroize::{Zeroize, ZeroizeOnDrop}; /// Derivation information required to derive a keypair and an address from a mnemonic. #[derive(Debug, Clone)] @@ -41,7 +41,7 @@ impl AccountData { type Secp256k1Keypair = (SigningKey, PublicKey); -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Zeroize, ZeroizeOnDrop)] pub struct DirectSecp256k1HdWallet { /// Base secret secret: bip39::Mnemonic, @@ -54,30 +54,10 @@ pub struct DirectSecp256k1HdWallet { // that would include the secret key which is a dyn EcdsaSigner and hence not Sync making the wallet // not Sync and if used on the signing client in an async trait, it wouldn't be Send /// Derivation instructions + #[zeroize(skip)] accounts: Vec, } -impl Zeroize for DirectSecp256k1HdWallet { - fn zeroize(&mut self) { - // in ideal world, Mnemonic would have had zeroize defined on it (there's an almost year old PR that introduces it) - // and the memory would have been filled with zeroes. - // - // we really don't want to keep our real mnemonic in memory, so let's do the semi-nasty thing - // of overwriting it with a fresh mnemonic that was never used before - // - // note: this function can only fail on an invalid word count, which clearly is not the case here - self.secret = bip39::Mnemonic::generate(self.secret.word_count()).unwrap(); - self.seed.zeroize(); - // there's nothing secret about derivation paths - } -} - -impl Drop for DirectSecp256k1HdWallet { - fn drop(&mut self) { - self.zeroize() - } -} - impl DirectSecp256k1HdWallet { pub fn builder(prefix: &str) -> DirectSecp256k1HdWalletBuilder { DirectSecp256k1HdWalletBuilder::new(prefix) diff --git a/common/commands/Cargo.toml b/common/commands/Cargo.toml index 12ea1b5176..d20eb0c806 100644 --- a/common/commands/Cargo.toml +++ b/common/commands/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] base64 = "0.13.0" -bip39 = "1.0.1" +bip39 = { workspace = true } bs58 = "0.4" comfy-table = "6.0.0" cfg-if = "1.0.0" diff --git a/gateway/Cargo.toml b/gateway/Cargo.toml index c7f54810c6..6408849008 100644 --- a/gateway/Cargo.toml +++ b/gateway/Cargo.toml @@ -17,7 +17,7 @@ rust-version = "1.56" [dependencies] anyhow = "1.0.53" async-trait = { workspace = true } -bip39 = "1.0.1" +bip39 = { workspace = true } bs58 = "0.4.0" clap = { version = "4.0", features = ["cargo", "derive"] } colored = "2.0" diff --git a/nym-api/Cargo.toml b/nym-api/Cargo.toml index acbbc3c305..89ec2e3a25 100644 --- a/nym-api/Cargo.toml +++ b/nym-api/Cargo.toml @@ -17,7 +17,7 @@ rust-version = "1.56" [dependencies] async-trait = { workspace = true } bs58 = {version = "0.4.0" } -bip39 = "1" +bip39 = { workspace = true } cfg-if = "1.0" clap = { version = "4.0", features = ["cargo", "derive"] } console-subscriber = { version = "0.1.1", optional = true } # validator-api needs to be built with RUSTFLAGS="--cfg tokio_unstable" diff --git a/nym-connect/desktop/src-tauri/Cargo.toml b/nym-connect/desktop/src-tauri/Cargo.toml index c7e509b38d..f94185cb82 100644 --- a/nym-connect/desktop/src-tauri/Cargo.toml +++ b/nym-connect/desktop/src-tauri/Cargo.toml @@ -20,7 +20,7 @@ tauri-macros = "^1.2.1" [dependencies] anyhow = "1.0" -bip39 = "1.0" +bip39 = { version = "2.0.0", features = ["zeroize"] } dirs = "4.0" eyre = "0.6.5" fix-path-env = { git = "https://github.com/tauri-apps/fix-path-env-rs", branch = "release"} diff --git a/nym-connect/mobile/src-tauri/Cargo.toml b/nym-connect/mobile/src-tauri/Cargo.toml index 8614dd7f0c..628a96a7c4 100644 --- a/nym-connect/mobile/src-tauri/Cargo.toml +++ b/nym-connect/mobile/src-tauri/Cargo.toml @@ -21,7 +21,7 @@ tauri-build = { version = "2.0.0-alpha.1", features = [] } [dependencies] anyhow = "1.0" -bip39 = "1.0" +bip39 = { version = "2.0.0", features = ["zeroize"] } chrono = "0.4" dirs = "4.0" eyre = "0.6.5" diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index 2be6c2fb75..2c1cdd58d5 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -181,15 +181,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "autocfg" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" -dependencies = [ - "autocfg 1.1.0", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -240,22 +231,23 @@ dependencies = [ [[package]] name = "bip39" -version = "1.0.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e89470017230c38e52b82b3ee3f530db1856ba1d434e3a67a3456a8a8dec5f" +checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" dependencies = [ "bitcoin_hashes", - "rand 0.6.5", - "rand_core 0.4.2", + "rand 0.8.5", + "rand_core 0.6.4", "serde", "unicode-normalization", + "zeroize", ] [[package]] name = "bitcoin_hashes" -version = "0.9.7" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ce18265ec2324ad075345d5814fbeed4f41f0a660055dc78840b74d19b874b1" +checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" [[package]] name = "bitflags" @@ -476,7 +468,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3431df59f28accaf4cb4eed4a9acc66bea3f3c3753aa6cdc2f024174ef232af7" dependencies = [ - "smallvec 1.10.0", + "smallvec", ] [[package]] @@ -485,7 +477,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa" dependencies = [ - "smallvec 1.10.0", + "smallvec", ] [[package]] @@ -569,15 +561,6 @@ dependencies = [ "os_str_bytes", ] -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags", -] - [[package]] name = "cocoa" version = "0.24.1" @@ -869,7 +852,7 @@ dependencies = [ "phf 0.8.0", "proc-macro2", "quote", - "smallvec 1.10.0", + "smallvec", "syn", ] @@ -1431,12 +1414,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - [[package]] name = "funty" version = "2.0.0" @@ -1762,7 +1739,7 @@ dependencies = [ "gobject-sys", "libc", "once_cell", - "smallvec 1.10.0", + "smallvec", "thiserror", ] @@ -2248,7 +2225,7 @@ version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ - "autocfg 1.1.0", + "autocfg", "hashbrown", "serde", ] @@ -2508,7 +2485,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ - "autocfg 1.1.0", + "autocfg", "scopeguard", ] @@ -2581,12 +2558,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - [[package]] name = "memchr" version = "2.5.0" @@ -2599,7 +2570,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] @@ -2729,7 +2700,7 @@ version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-traits", ] @@ -2739,7 +2710,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-integer", "num-traits", ] @@ -2750,7 +2721,7 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ - "autocfg 1.1.0", + "autocfg", "libm", ] @@ -2878,6 +2849,20 @@ dependencies = [ "thiserror", ] +[[package]] +name = "nym-crypto" +version = "0.2.0" +dependencies = [ + "bs58", + "ed25519-dalek", + "nym-pemstore", + "nym-sphinx-types", + "rand 0.7.3", + "subtle-encoding", + "thiserror", + "x25519-dalek", +] + [[package]] name = "nym-dkg" version = "0.1.0" @@ -2899,20 +2884,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "nym-crypto" -version = "0.1.0" -dependencies = [ - "bs58", - "ed25519-dalek", - "nym-pemstore", - "nym-sphinx-types", - "rand 0.7.3", - "subtle-encoding", - "thiserror", - "x25519-dalek", -] - [[package]] name = "nym-execute" version = "0.1.0" @@ -2984,7 +2955,7 @@ dependencies = [ [[package]] name = "nym-sphinx-types" -version = "0.1.0" +version = "0.2.0" dependencies = [ "sphinx-packet", ] @@ -3110,7 +3081,6 @@ dependencies = [ "nym-wallet-types", "once_cell", "pretty_env_logger", - "rand 0.6.5", "rand_chacha 0.2.2", "reqwest", "serde", @@ -3258,7 +3228,7 @@ version = "0.9.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" dependencies = [ - "autocfg 1.1.0", + "autocfg", "cc", "libc", "pkg-config", @@ -3330,7 +3300,7 @@ dependencies = [ "cfg-if", "libc", "redox_syscall", - "smallvec 1.10.0", + "smallvec", "windows-sys 0.45.0", ] @@ -3790,25 +3760,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -dependencies = [ - "autocfg 0.1.8", - "libc", - "rand_chacha 0.1.1", - "rand_core 0.4.2", - "rand_hc 0.1.0", - "rand_isaac", - "rand_jitter", - "rand_os", - "rand_pcg 0.1.2", - "rand_xorshift", - "winapi", -] - [[package]] name = "rand" version = "0.7.3" @@ -3819,8 +3770,8 @@ dependencies = [ "libc", "rand_chacha 0.2.2", "rand_core 0.5.1", - "rand_hc 0.2.0", - "rand_pcg 0.2.1", + "rand_hc", + "rand_pcg", ] [[package]] @@ -3834,16 +3785,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.3.1", -] - [[package]] name = "rand_chacha" version = "0.2.2" @@ -3864,21 +3805,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - [[package]] name = "rand_core" version = "0.5.1" @@ -3907,15 +3833,6 @@ dependencies = [ "rand 0.7.3", ] -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "rand_hc" version = "0.2.0" @@ -3925,50 +3842,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -dependencies = [ - "libc", - "rand_core 0.4.2", - "winapi", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "winapi", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.4.2", -] - [[package]] name = "rand_pcg" version = "0.2.1" @@ -3978,15 +3851,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "raw-window-handle" version = "0.5.0" @@ -3996,15 +3860,6 @@ dependencies = [ "cty", ] -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "redox_syscall" version = "0.2.16" @@ -4350,7 +4205,7 @@ dependencies = [ "phf_codegen", "precomputed-hash", "servo_arc", - "smallvec 1.10.0", + "smallvec", "thin-slice", ] @@ -4605,16 +4460,7 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "smallvec" -version = "0.6.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" -dependencies = [ - "maybe-uninit", + "autocfg", ] [[package]] @@ -5281,13 +5127,28 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" dependencies = [ - "autocfg 1.1.0", + "autocfg", "bytes", "libc", "memchr", @@ -5434,7 +5295,7 @@ dependencies = [ "once_cell", "regex", "sharded-slab", - "smallvec 1.10.0", + "smallvec", "thread_local", "tracing", "tracing-core", @@ -5517,11 +5378,11 @@ checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-normalization" -version = "0.1.9" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09c8070a9942f5e7cfccd93f490fdebd230ee3c3c9f107cb25bad5351ef671cf" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ - "smallvec 0.6.14", + "tinyvec", ] [[package]] @@ -6228,6 +6089,7 @@ version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" dependencies = [ + "serde", "zeroize_derive", ] diff --git a/nym-wallet/nym-wallet-recovery-cli/Cargo.toml b/nym-wallet/nym-wallet-recovery-cli/Cargo.toml index 30d478d3b3..effd2d09ca 100644 --- a/nym-wallet/nym-wallet-recovery-cli/Cargo.toml +++ b/nym-wallet/nym-wallet-recovery-cli/Cargo.toml @@ -10,7 +10,7 @@ aes-gcm = "0.9" anyhow = "1.0" argon2 = "0.4" base64 = "0.13" -bip39 = "1.0" +bip39 = { version = "2.0.0", features = ["zeroize"] } clap = { version = "4.0", features = ["derive"] } log = "0.4" pretty_env_logger = "0.4" diff --git a/nym-wallet/nym-wallet-types/Cargo.toml b/nym-wallet/nym-wallet-types/Cargo.toml index f8ebba8263..ed577ea71a 100644 --- a/nym-wallet/nym-wallet-types/Cargo.toml +++ b/nym-wallet/nym-wallet-types/Cargo.toml @@ -11,7 +11,7 @@ serde_json = "1.0" strum = { version = "0.23", features = ["derive"] } ts-rs = "6.1.2" -cosmwasm-std = "1.0.0-beta8" +cosmwasm-std = "1.0.0" cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } nym-config = { path = "../../common/config" } diff --git a/nym-wallet/src-tauri/Cargo.toml b/nym-wallet/src-tauri/Cargo.toml index 00a6f7f63b..aff3b993e8 100644 --- a/nym-wallet/src-tauri/Cargo.toml +++ b/nym-wallet/src-tauri/Cargo.toml @@ -20,7 +20,7 @@ tauri-macros = "=1.2.1" [dependencies] async-trait = "0.1.64" -bip39 = "1.0" +bip39 = { version = "2.0.0", features = ["zeroize", "rand"] } cfg-if = "1.0.0" colored = "2.0" dirs = "4.0" @@ -32,7 +32,6 @@ itertools = "0.10" log = { version = "0.4", features = ["serde"] } once_cell = "1.7.2" pretty_env_logger = "0.4" -rand = "0.6.5" reqwest = {version = "0.11.9", features = ["json"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" @@ -50,7 +49,7 @@ k256 = { version = "0.10", features = ["ecdsa", "sha256"] } aes-gcm = "0.9.4" argon2 = { version = "0.3.2", features = ["std"] } base64 = "0.13" -zeroize = "1.4.3" +zeroize = { version = "1.5", features = ["zeroize_derive", "serde"] } cosmwasm-std = "1.0.0" cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } diff --git a/nym-wallet/src-tauri/src/operations/mixnet/account.rs b/nym-wallet/src-tauri/src/operations/mixnet/account.rs index fb145749ba..842acc9644 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/account.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/account.rs @@ -2,15 +2,14 @@ use crate::config::{Config, CUSTOM_SIMULATED_GAS_MULTIPLIER}; use crate::error::BackendError; use crate::network_config; use crate::state::{WalletAccountIds, WalletState}; -use crate::utils::{SensitiveStringWrapper, ZeroizeMnemonicWrapper}; use crate::wallet_storage::{self, UserPassword, DEFAULT_LOGIN_ID}; +use bip39::rand::{self, seq::SliceRandom}; use bip39::{Language, Mnemonic}; use cosmrs::bip32::DerivationPath; use itertools::Itertools; use nym_config::defaults::{NymNetworkDetails, COSMOS_DERIVATION_PATH}; use nym_types::account::{Account, AccountEntry, Balance}; use nym_wallet_types::network::Network as WalletNetwork; -use rand::seq::SliceRandom; use std::collections::HashMap; use strum::IntoEnumIterator; use url::Url; @@ -19,10 +18,9 @@ use validator_client::{nyxd::SigningNyxdClient, Client}; #[tauri::command] pub async fn connect_with_mnemonic( - mnemonic: SensitiveStringWrapper, + mnemonic: Mnemonic, state: tauri::State<'_, WalletState>, ) -> Result { - let mnemonic = ZeroizeMnemonicWrapper::try_from_string(mnemonic)?; _connect_with_mnemonic(mnemonic, state).await } @@ -48,13 +46,13 @@ pub async fn get_balance(state: tauri::State<'_, WalletState>) -> Result SensitiveStringWrapper { - random_mnemonic().into_string() +pub fn create_new_mnemonic() -> Mnemonic { + random_mnemonic() } #[tauri::command] -pub fn validate_mnemonic(mnemonic: SensitiveStringWrapper) -> bool { - ZeroizeMnemonicWrapper::try_from_string(mnemonic).is_ok() +pub fn validate_mnemonic(_mnemonic: Mnemonic) -> bool { + true } #[tauri::command] @@ -82,15 +80,13 @@ pub async fn logout(state: tauri::State<'_, WalletState>) -> Result<(), BackendE Ok(()) } -fn random_mnemonic() -> ZeroizeMnemonicWrapper { +fn random_mnemonic() -> Mnemonic { let mut rng = rand::thread_rng(); - Mnemonic::generate_in_with(&mut rng, Language::English, 24) - .unwrap() - .into() + Mnemonic::generate_in_with(&mut rng, Language::English, 24).unwrap() } async fn _connect_with_mnemonic( - mnemonic: ZeroizeMnemonicWrapper, + mnemonic: Mnemonic, state: tauri::State<'_, WalletState>, ) -> Result { { @@ -141,7 +137,7 @@ async fn _connect_with_mnemonic( &default_nyxd_urls, &default_api_urls, &config, - mnemonic.as_ref(), + &mnemonic, )?; // Set the default account @@ -298,16 +294,12 @@ pub fn does_password_file_exist() -> Result { } #[tauri::command] -pub fn create_password( - mnemonic: SensitiveStringWrapper, - password: UserPassword, -) -> Result<(), BackendError> { +pub fn create_password(mnemonic: Mnemonic, password: UserPassword) -> Result<(), BackendError> { if does_password_file_exist()? { return Err(BackendError::WalletFileAlreadyExists); } log::info!("Creating password"); - let mnemonic = ZeroizeMnemonicWrapper::try_from_string(mnemonic)?; let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); // Currently we only support a single, default, login id in the wallet let login_id = wallet_storage::LoginId::new(DEFAULT_LOGIN_ID.to_string()); @@ -330,7 +322,7 @@ pub async fn sign_in_with_password( set_state_with_all_accounts(stored_login, first_login_id_when_converting, state.clone()) .await?; - _connect_with_mnemonic(mnemonic.into(), state).await + _connect_with_mnemonic(mnemonic, state).await } fn extract_first_mnemonic( @@ -370,7 +362,7 @@ pub async fn sign_in_with_password_and_account_id( set_state_with_all_accounts(stored_login, first_login_id_when_converting, state.clone()) .await?; - _connect_with_mnemonic(mnemonic.into(), state).await + _connect_with_mnemonic(mnemonic, state).await } fn extract_mnemonic( @@ -404,13 +396,12 @@ pub fn archive_wallet_file() -> Result<(), BackendError> { #[tauri::command] pub async fn add_account_for_password( - mnemonic: SensitiveStringWrapper, + mnemonic: Mnemonic, password: UserPassword, account_id: &str, state: tauri::State<'_, WalletState>, ) -> Result { log::info!("Adding account for the current password: {account_id}"); - let mnemonic = ZeroizeMnemonicWrapper::try_from_string(mnemonic)?; let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); // Currently we only support a single, default, login id in the wallet let login_id = wallet_storage::LoginId::new(DEFAULT_LOGIN_ID.to_string()); @@ -427,13 +418,7 @@ pub async fn add_account_for_password( let address = { let state = state.read().await; let network: NymNetworkDetails = state.current_network().into(); - // safety: the call to `clone_inner` is fine here as the raw mnemonic will get immediately - // passed to `DirectSecp256k1HdWallet` which will zeroize it on drop - derive_address( - mnemonic.into_cloned_inner(), - &network.chain_details.bech32_account_prefix, - )? - .to_string() + derive_address(mnemonic, &network.chain_details.bech32_account_prefix)?.to_string() }; // Re-read all the acccounts from the wallet to reset the state, rather than updating it @@ -553,19 +538,19 @@ pub async fn list_accounts( pub fn show_mnemonic_for_account_in_password( account_id: String, password: UserPassword, -) -> Result { +) -> Result { log::info!("Getting mnemonic for: {account_id}"); let login_id = wallet_storage::LoginId::new(DEFAULT_LOGIN_ID.to_string()); let account_id = wallet_storage::AccountId::new(account_id); let mnemonic = _show_mnemonic_for_account_in_password(&login_id, &account_id, &password)?; - Ok(mnemonic.into_string()) + Ok(mnemonic) } fn _show_mnemonic_for_account_in_password( login_id: &wallet_storage::LoginId, account_id: &wallet_storage::AccountId, password: &wallet_storage::UserPassword, -) -> Result { +) -> Result { let stored_account = wallet_storage::load_existing_login(login_id, password)?; let mnemonic = match stored_account { wallet_storage::StoredLogin::Mnemonic(ref account) => account.mnemonic().clone(), @@ -575,7 +560,7 @@ fn _show_mnemonic_for_account_in_password( .mnemonic() .clone(), }; - Ok(mnemonic.into()) + Ok(mnemonic) } #[cfg(test)] @@ -590,8 +575,8 @@ mod tests { use super::*; - // This decryptes a stored wallet file using the same procedure as when signing in. Most tests - // related to the encryped wallet storage is in `wallet_storage`. + // This decrypts a stored wallet file using the same procedure as when signing in. Most tests + // related to the encrypted wallet storage is in `wallet_storage`. #[test] fn decrypt_stored_wallet_for_sign_in() { const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet.json"; diff --git a/nym-wallet/src-tauri/src/utils.rs b/nym-wallet/src-tauri/src/utils.rs index c2a22fcb2f..fee3a1b727 100644 --- a/nym-wallet/src-tauri/src/utils.rs +++ b/nym-wallet/src-tauri/src/utils.rs @@ -4,16 +4,14 @@ use crate::error::BackendError; use crate::nyxd_client; use crate::state::WalletState; -use bip39::Mnemonic; use cosmwasm_std::Decimal; use nym_mixnet_contract_common::{IdentityKey, MixId, Percent}; use nym_types::currency::DecCoin; use nym_types::mixnode::MixNodeCostParams; use nym_wallet_types::app::AppEnv; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde::{Deserialize, Serialize}; use validator_client::nyxd::traits::MixnetQueryClient; use validator_client::nyxd::{tx, Coin, CosmosCoin, Gas, GasPrice}; -use zeroize::Zeroize; fn get_env_as_option(key: &str) -> Option { match ::std::env::var(key) { @@ -194,94 +192,3 @@ pub async fn get_old_and_incorrect_hardcoded_fee( log::info!("hardcoded fee for {:?} is {:?}", operation, coin); guard.attempt_convert_to_display_dec_coin(coin) } - -#[derive(Zeroize)] -#[zeroize(drop)] -pub struct SensitiveStringWrapper(String); - -impl<'de> Deserialize<'de> for SensitiveStringWrapper { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - Ok(SensitiveStringWrapper(String::deserialize(deserializer)?)) - } -} - -impl Serialize for SensitiveStringWrapper { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - // unfortunately this serialized value will live on... - self.0.serialize(serializer) - } -} - -impl From for SensitiveStringWrapper { - fn from(value: String) -> Self { - SensitiveStringWrapper(value) - } -} - -impl AsRef for SensitiveStringWrapper { - fn as_ref(&self) -> &str { - self.0.as_ref() - } -} - -#[derive(Clone)] -// can't do it natively until https://github.com/rust-bitcoin/rust-bip39/pull/32 gets merged and released... -pub(crate) struct ZeroizeMnemonicWrapper(bip39::Mnemonic); - -impl From for ZeroizeMnemonicWrapper { - fn from(value: Mnemonic) -> Self { - ZeroizeMnemonicWrapper(value) - } -} - -impl Zeroize for ZeroizeMnemonicWrapper { - fn zeroize(&mut self) { - // overwrite the mnemonic value with a completely random one - // (a poor man's zeroize until bip39 crate does it properly...) - self.0 = Mnemonic::generate(self.0.word_count()).unwrap(); - } -} - -impl Drop for ZeroizeMnemonicWrapper { - fn drop(&mut self) { - self.zeroize() - } -} - -impl AsRef for ZeroizeMnemonicWrapper { - fn as_ref(&self) -> &Mnemonic { - &self.0 - } -} - -impl ZeroizeMnemonicWrapper { - pub(crate) fn into_string(self) -> SensitiveStringWrapper { - SensitiveStringWrapper(self.0.to_string()) - } - - pub(crate) fn try_from_string(string: SensitiveStringWrapper) -> Result { - let res = string.as_ref().parse()?; - Ok(ZeroizeMnemonicWrapper(res)) - } - - // special care must be taken when calling this method as the mnemonic will no longer get zeroized! - pub(crate) fn into_cloned_inner(self) -> bip39::Mnemonic { - self.0.clone() - } - - #[cfg(test)] - pub(crate) fn unchecked_clone_inner(&self) -> bip39::Mnemonic { - self.0.clone() - } - - #[cfg(test)] - pub(crate) fn generate_random() -> Self { - ZeroizeMnemonicWrapper(Mnemonic::generate(24).unwrap()) - } -} diff --git a/nym-wallet/src-tauri/src/wallet_storage/account_data.rs b/nym-wallet/src-tauri/src/wallet_storage/account_data.rs index 9451c065b7..33b64bec7a 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/account_data.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/account_data.rs @@ -14,16 +14,14 @@ // In the future we might want to simplify by dropping the support for a single account entry, // instead treating as muliple accounts with one entry. -use serde::{Deserialize, Serialize}; -use validator_client::nyxd::bip32::DerivationPath; -use zeroize::Zeroize; - -use crate::error::BackendError; -use crate::utils::ZeroizeMnemonicWrapper; - use super::encryption::EncryptedData; use super::password::{AccountId, LoginId}; use super::UserPassword; +use crate::error::BackendError; +use bip39::Mnemonic; +use serde::{Deserialize, Serialize}; +use validator_client::nyxd::bip32::DerivationPath; +use zeroize::{Zeroize, ZeroizeOnDrop}; const CURRENT_WALLET_FILE_VERSION: u32 = 1; @@ -153,9 +151,8 @@ impl EncryptedLogin { /// A stored login is either a account, such as a mnemonic, or a list of multiple accounts where /// each has an inner id. Future proofed for having private key backed accounts. -#[derive(Serialize, Deserialize, Debug, Zeroize)] +#[derive(Serialize, Deserialize, Debug, Zeroize, ZeroizeOnDrop)] #[serde(untagged)] -#[zeroize(drop)] pub(crate) enum StoredLogin { Mnemonic(MnemonicAccount), // PrivateKey(PrivateKeyAccount) @@ -193,8 +190,7 @@ impl StoredLogin { } /// Multiple stored accounts, each entry having an id and a data field. -#[derive(Serialize, Deserialize, Clone, Debug, Zeroize, PartialEq, Eq)] -#[zeroize(drop)] +#[derive(Serialize, Deserialize, Clone, Debug, Zeroize, ZeroizeOnDrop, PartialEq, Eq)] pub(crate) struct MultipleAccounts { accounts: Vec, } @@ -238,19 +234,17 @@ impl MultipleAccounts { pub(crate) fn add( &mut self, id: AccountId, - mnemonic: ZeroizeMnemonicWrapper, + mnemonic: Mnemonic, hd_path: DerivationPath, ) -> Result<(), BackendError> { if self.get_account(&id).is_some() { Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin) - } else if self.get_account_with_mnemonic(mnemonic.as_ref()).is_some() { + } else if self.get_account_with_mnemonic(&mnemonic).is_some() { Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin) } else { self.accounts.push(WalletAccount::new( id, - // safety: the call to `clone_inner` is fine here as the raw mnemonic will get immediately - // passed to `MnemonicAccount` which will zeroize it on drop - MnemonicAccount::new(mnemonic.into_cloned_inner(), hd_path), + MnemonicAccount::new(mnemonic, hd_path), )); Ok(()) } @@ -272,8 +266,7 @@ impl From> for MultipleAccounts { } /// An entry in the list of stored accounts -#[derive(Serialize, Deserialize, Clone, Debug, Zeroize, PartialEq, Eq)] -#[zeroize(drop)] +#[derive(Serialize, Deserialize, Clone, Debug, Zeroize, ZeroizeOnDrop, PartialEq, Eq)] pub(crate) struct WalletAccount { id: AccountId, account: AccountData, @@ -307,19 +300,20 @@ impl WalletAccount { /// An account usually is a mnemonic account, but in the future it might be backed by a private /// key. -#[derive(Serialize, Deserialize, Clone, Debug, Zeroize, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Clone, Debug, Zeroize, ZeroizeOnDrop, PartialEq, Eq)] #[serde(untagged)] -#[zeroize(drop)] enum AccountData { Mnemonic(MnemonicAccount), // PrivateKey(PrivateKeyAccount) } /// An account backed by a unique mnemonic. -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Clone, Debug, Zeroize, ZeroizeOnDrop, PartialEq, Eq)] pub(crate) struct MnemonicAccount { mnemonic: bip39::Mnemonic, #[serde(with = "display_hd_path")] + // there's nothing secret about our derivation path + #[zeroize(skip)] hd_path: DerivationPath, } @@ -338,27 +332,6 @@ impl MnemonicAccount { } } -impl Zeroize for MnemonicAccount { - fn zeroize(&mut self) { - // in ideal world, Mnemonic would have had zeroize defined on it (there's an almost year old PR that introduces it) - // and the memory would have been filled with zeroes. - // - // we really don't want to keep our real mnemonic in memory, so let's do the semi-nasty thing - // of overwriting it with a fresh mnemonic that was never used before - // - // note: this function can only fail on an invalid word count, which clearly is not the case here - self.mnemonic = bip39::Mnemonic::generate(self.mnemonic.word_count()).unwrap(); - - // further note: we don't really care about the hd_path, there's nothing secret about it. - } -} - -impl Drop for MnemonicAccount { - fn drop(&mut self) { - self.zeroize() - } -} - mod display_hd_path { use serde::{Deserialize, Deserializer, Serializer}; use validator_client::nyxd::bip32::DerivationPath; diff --git a/nym-wallet/src-tauri/src/wallet_storage/mod.rs b/nym-wallet/src-tauri/src/wallet_storage/mod.rs index 57464eda49..4c02a1b44c 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/mod.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/mod.rs @@ -14,7 +14,7 @@ pub(crate) use crate::wallet_storage::password::{AccountId, LoginId, UserPasswor use crate::error::BackendError; use crate::platform_constants::{STORAGE_DIR_NAME, WALLET_INFO_FILENAME}; -use crate::utils::ZeroizeMnemonicWrapper; +use bip39::Mnemonic; use std::ffi::OsString; use std::fs::{self, create_dir_all, OpenOptions}; use std::path::{Path, PathBuf}; @@ -139,7 +139,7 @@ fn store_login_at_file( } pub(crate) fn store_login_with_multiple_accounts( - mnemonic: ZeroizeMnemonicWrapper, + mnemonic: Mnemonic, hd_path: DerivationPath, id: LoginId, password: &UserPassword, @@ -154,7 +154,7 @@ pub(crate) fn store_login_with_multiple_accounts( fn store_login_with_multiple_accounts_at_file( filepath: &Path, - mnemonic: ZeroizeMnemonicWrapper, + mnemonic: Mnemonic, hd_path: DerivationPath, id: LoginId, password: &UserPassword, @@ -185,7 +185,7 @@ fn store_login_with_multiple_accounts_at_file( /// account in the list of accounts associated with the encrypted entry. The inner id for this /// entry will be set to the same as the outer, unencrypted, id. pub(crate) fn append_account_to_login( - mnemonic: ZeroizeMnemonicWrapper, + mnemonic: Mnemonic, hd_path: DerivationPath, id: LoginId, inner_id: AccountId, @@ -201,7 +201,7 @@ pub(crate) fn append_account_to_login( fn append_account_to_login_at_file( filepath: &Path, - mnemonic: ZeroizeMnemonicWrapper, + mnemonic: Mnemonic, hd_path: DerivationPath, id: LoginId, inner_id: AccountId, @@ -422,19 +422,12 @@ mod tests { fn store_single_login() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let id1 = LoginId::new("first".to_string()); - store_login_at_file( - &wallet_file, - account1.into_cloned_inner(), - hd_path, - id1.clone(), - &password, - ) - .unwrap(); + store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); assert_eq!(stored_wallet.len(), 1); @@ -450,7 +443,7 @@ mod tests { fn store_single_login_with_multi() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let id1 = LoginId::new("first".to_string()); @@ -478,7 +471,7 @@ mod tests { fn store_twice_for_the_same_id_fails() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let id1 = LoginId::new("first".to_string()); @@ -486,7 +479,7 @@ mod tests { // Store the first login store_login_at_file( &wallet_file, - account1.unchecked_clone_inner(), + account1.clone(), hd_path.clone(), id1.clone(), &password, @@ -495,13 +488,7 @@ mod tests { // and storing the same id again fails assert!(matches!( - store_login_at_file( - &wallet_file, - account1.into_cloned_inner(), - hd_path, - id1, - &password, - ), + store_login_at_file(&wallet_file, account1, hd_path, id1, &password,), Err(BackendError::WalletLoginIdAlreadyExists), )); } @@ -510,7 +497,7 @@ mod tests { fn store_twice_for_the_same_id_fails_with_multiple() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let id1 = LoginId::new("first".to_string()); @@ -542,20 +529,13 @@ mod tests { fn load_with_wrong_password_fails() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let bad_password = UserPassword::new("bad-password".to_string()); let id1 = LoginId::new("first".to_string()); - store_login_at_file( - &wallet_file, - account1.into_cloned_inner(), - hd_path, - id1.clone(), - &password, - ) - .unwrap(); + store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); // Trying to load it with wrong password now fails assert!(matches!( @@ -568,7 +548,7 @@ mod tests { fn load_with_wrong_password_fails_with_multi() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let bad_password = UserPassword::new("bad-password".to_string()); @@ -594,20 +574,13 @@ mod tests { fn load_with_wrong_id_fails() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let id1 = LoginId::new("first".to_string()); let id2 = LoginId::new("second".to_string()); - store_login_at_file( - &wallet_file, - account1.into_cloned_inner(), - hd_path, - id1, - &password, - ) - .unwrap(); + store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); // Trying to load with the wrong id assert!(matches!( @@ -620,7 +593,7 @@ mod tests { fn load_with_wrong_id_fails_with_multi() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let id1 = LoginId::new("first".to_string()); @@ -640,14 +613,14 @@ mod tests { fn store_and_load_a_single_login() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let id1 = LoginId::new("first".to_string()); store_login_at_file( &wallet_file, - account1.unchecked_clone_inner(), + account1.clone(), hd_path.clone(), id1.clone(), &password, @@ -656,7 +629,7 @@ mod tests { let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(account1.as_ref(), acc.mnemonic()); + assert_eq!(&account1, acc.mnemonic()); assert_eq!(&hd_path, acc.hd_path()); } @@ -664,7 +637,7 @@ mod tests { fn store_and_load_a_single_login_with_multi() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = ZeroizeMnemonicWrapper::generate_random(); + let acc1 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let id1 = LoginId::new("first".to_string()); @@ -685,7 +658,7 @@ mod tests { .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) .unwrap(); assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); - assert_eq!(account.mnemonic(), acc1.as_ref()); + assert_eq!(account.mnemonic(), &acc1); assert_eq!(account.hd_path(), &hd_path); } @@ -693,8 +666,8 @@ mod tests { fn store_a_second_login_with_a_different_password_fails() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); - let account2 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let bad_password = UserPassword::new("bad-password".to_string()); @@ -703,7 +676,7 @@ mod tests { store_login_at_file( &wallet_file, - account1.into_cloned_inner(), + account1, cosmos_hd_path.clone(), id1, &password, @@ -712,13 +685,7 @@ mod tests { // Can't store a second login if you use different password assert!(matches!( - store_login_at_file( - &wallet_file, - account2.into_cloned_inner(), - cosmos_hd_path, - id2, - &bad_password - ), + store_login_at_file(&wallet_file, account2, cosmos_hd_path, id2, &bad_password), Err(BackendError::WalletDifferentPasswordDetected), )); } @@ -727,8 +694,8 @@ mod tests { fn store_a_second_login_with_a_different_password_fails_with_multi() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); - let account2 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let bad_password = UserPassword::new("bad-password".to_string()); @@ -761,8 +728,8 @@ mod tests { fn store_two_mnemonic_accounts_gives_different_salts_and_iv() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); - let account2 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let different_hd_path: DerivationPath = "m".parse().unwrap(); let password = UserPassword::new("password".to_string()); @@ -770,14 +737,7 @@ mod tests { let id2 = LoginId::new("second".to_string()); // Store the first account - store_login_at_file( - &wallet_file, - account1.into_cloned_inner(), - hd_path, - id1, - &password, - ) - .unwrap(); + store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); let encrypted_blob = &stored_wallet @@ -790,14 +750,7 @@ mod tests { let original_salt = encrypted_blob.salt().to_vec(); // Add an extra account - store_login_at_file( - &wallet_file, - account2.into_cloned_inner(), - different_hd_path, - id2, - &password, - ) - .unwrap(); + store_login_at_file(&wallet_file, account2, different_hd_path, id2, &password).unwrap(); let loaded_accounts = load_existing_wallet_at_file(&wallet_file).unwrap(); assert_eq!(loaded_accounts.len(), 2); @@ -815,8 +768,8 @@ mod tests { fn store_two_mnemonic_accounts_using_two_logins() { let store_dir = tempdir().unwrap(); let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); - let account2 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let different_hd_path: DerivationPath = "m".parse().unwrap(); let password = UserPassword::new("password".to_string()); @@ -826,7 +779,7 @@ mod tests { // Store the first account store_login_at_file( &wallet, - account1.unchecked_clone_inner(), + account1.clone(), cosmos_hd_path.clone(), id1.clone(), &password, @@ -835,13 +788,13 @@ mod tests { let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); let acc = login.as_mnemonic_account().unwrap(); - assert_eq!(account1.as_ref(), acc.mnemonic()); + assert_eq!(&account1, acc.mnemonic()); assert_eq!(&cosmos_hd_path, acc.hd_path()); // Add an extra account store_login_at_file( &wallet, - account2.unchecked_clone_inner(), + account2.clone(), different_hd_path.clone(), id2.clone(), &password, @@ -851,12 +804,12 @@ mod tests { // first account should be unchanged let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(account1.as_ref(), acc1.mnemonic()); + assert_eq!(&account1, acc1.mnemonic()); assert_eq!(&cosmos_hd_path, acc1.hd_path()); let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(account2.as_ref(), acc2.mnemonic()); + assert_eq!(&account2, acc2.mnemonic()); assert_eq!(&different_hd_path, acc2.hd_path()); } @@ -864,8 +817,8 @@ mod tests { fn store_one_mnemonic_account_and_one_multi_account() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); - let account2 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let different_hd_path: DerivationPath = "m".parse().unwrap(); let password = UserPassword::new("password".to_string()); @@ -875,7 +828,7 @@ mod tests { // Store the first account store_login_at_file( &wallet_file, - account1.unchecked_clone_inner(), + account1.clone(), hd_path.clone(), id1.clone(), &password, @@ -884,7 +837,7 @@ mod tests { let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(account1.as_ref(), acc.mnemonic()); + assert_eq!(&account1, acc.mnemonic()); assert_eq!(&hd_path, acc.hd_path()); // Add an extra account @@ -900,7 +853,7 @@ mod tests { // first account should be unchanged let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(account1.as_ref(), acc1.mnemonic()); + assert_eq!(&account1, acc1.mnemonic()); assert_eq!(&hd_path, acc1.hd_path()); let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); @@ -910,7 +863,7 @@ mod tests { .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) .unwrap(); assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); - assert_eq!(account.mnemonic(), account2.as_ref()); + assert_eq!(account.mnemonic(), &account2); assert_eq!(account.hd_path(), &different_hd_path); } @@ -918,7 +871,7 @@ mod tests { fn remove_non_existent_id_fails() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let id1 = LoginId::new("first".to_string()); @@ -938,8 +891,8 @@ mod tests { fn store_and_remove_wallet_login_information() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); - let account2 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let different_hd_path: DerivationPath = "m".parse().unwrap(); let password = UserPassword::new("password".to_string()); @@ -949,7 +902,7 @@ mod tests { // Store two accounts with two different passwords store_login_at_file( &wallet_file, - account1.unchecked_clone_inner(), + account1.clone(), cosmos_hd_path.clone(), id1.clone(), &password, @@ -957,7 +910,7 @@ mod tests { .unwrap(); store_login_at_file( &wallet_file, - account2.unchecked_clone_inner(), + account2.clone(), different_hd_path.clone(), id2.clone(), &password, @@ -967,12 +920,12 @@ mod tests { // Load and compare let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(account1.as_ref(), acc1.mnemonic()); + assert_eq!(&account1, acc1.mnemonic()); assert_eq!(&cosmos_hd_path, acc1.hd_path()); let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(account2.as_ref(), acc2.mnemonic()); + assert_eq!(&account2, acc2.mnemonic()); assert_eq!(&different_hd_path, acc2.hd_path()); // Delete the second account @@ -981,7 +934,7 @@ mod tests { // The first account should be unchanged let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(account1.as_ref(), acc1.mnemonic()); + assert_eq!(&account1, acc1.mnemonic()); assert_eq!(&cosmos_hd_path, acc1.hd_path()); // And we can't load the second one anymore @@ -1008,8 +961,8 @@ mod tests { fn append_account_converts_the_type() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); - let account2 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let id1 = LoginId::new("first".to_string()); @@ -1017,7 +970,7 @@ mod tests { store_login_at_file( &wallet_file, - account1.unchecked_clone_inner(), + account1.clone(), hd_path.clone(), id1.clone(), &password, @@ -1027,7 +980,7 @@ mod tests { // Check that it's there as the correct non-multiple type let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(acc.mnemonic(), account1.as_ref()); + assert_eq!(acc.mnemonic(), &account1); assert_eq!(acc.hd_path(), &hd_path); append_account_to_login_at_file( @@ -1044,14 +997,8 @@ mod tests { let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); let expected = vec![ - WalletAccount::new( - id1.into(), - MnemonicAccount::new(account1.into_cloned_inner(), hd_path.clone()), - ), - WalletAccount::new( - id2, - MnemonicAccount::new(account2.into_cloned_inner(), hd_path), - ), + WalletAccount::new(id1.into(), MnemonicAccount::new(account1, hd_path.clone())), + WalletAccount::new(id2, MnemonicAccount::new(account2, hd_path)), ] .into(); assert_eq!(loaded_accounts, &expected); @@ -1061,10 +1008,10 @@ mod tests { fn append_accounts_to_existing_login() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); - let account2 = ZeroizeMnemonicWrapper::generate_random(); - let account3 = ZeroizeMnemonicWrapper::generate_random(); - let account4 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let account3 = Mnemonic::generate(24).unwrap(); + let account4 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let id1 = LoginId::new("first".to_string()); @@ -1074,7 +1021,7 @@ mod tests { store_login_at_file( &wallet_file, - account1.unchecked_clone_inner(), + account1.clone(), hd_path.clone(), id1.clone(), &password, @@ -1083,7 +1030,7 @@ mod tests { store_login_at_file( &wallet_file, - account2.unchecked_clone_inner(), + account2.clone(), hd_path.clone(), id2.clone(), &password, @@ -1093,7 +1040,7 @@ mod tests { // Check that it's there as the correct non-multiple type let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(acc2.mnemonic(), account2.as_ref()); + assert_eq!(acc2.mnemonic(), &account2); assert_eq!(acc2.hd_path(), &hd_path); // Add a third and fourth mnenonic grouped together with the second one @@ -1119,24 +1066,15 @@ mod tests { // Check that we can load all four let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(acc1.mnemonic(), account1.as_ref()); + assert_eq!(acc1.mnemonic(), &account1); assert_eq!(acc1.hd_path(), &hd_path); let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); let expected = vec![ - WalletAccount::new( - id2.into(), - MnemonicAccount::new(account2.into_cloned_inner(), hd_path.clone()), - ), - WalletAccount::new( - id3, - MnemonicAccount::new(account3.into_cloned_inner(), hd_path.clone()), - ), - WalletAccount::new( - id4, - MnemonicAccount::new(account4.into_cloned_inner(), hd_path), - ), + WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), + WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), + WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), ] .into(); assert_eq!(loaded_accounts, &expected); @@ -1146,10 +1084,10 @@ mod tests { fn append_accounts_to_existing_login_with_multi() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); - let account2 = ZeroizeMnemonicWrapper::generate_random(); - let account3 = ZeroizeMnemonicWrapper::generate_random(); - let account4 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let account3 = Mnemonic::generate(24).unwrap(); + let account4 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let id1 = LoginId::new("first".to_string()); @@ -1200,7 +1138,7 @@ mod tests { let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); let expected = vec![WalletAccount::new( DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1.into_cloned_inner(), hd_path.clone()), + MnemonicAccount::new(account1, hd_path.clone()), )] .into(); assert_eq!(loaded_accounts, &expected); @@ -1210,16 +1148,10 @@ mod tests { let expected = vec![ WalletAccount::new( DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account2.into_cloned_inner(), hd_path.clone()), - ), - WalletAccount::new( - id3, - MnemonicAccount::new(account3.into_cloned_inner(), hd_path.clone()), - ), - WalletAccount::new( - id4, - MnemonicAccount::new(account4.into_cloned_inner(), hd_path), + MnemonicAccount::new(account2, hd_path.clone()), ), + WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), + WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), ] .into(); assert_eq!(loaded_accounts, &expected); @@ -1229,7 +1161,7 @@ mod tests { fn append_the_same_mnemonic_twice_fails() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let id1 = LoginId::new("first".to_string()); @@ -1254,21 +1186,14 @@ mod tests { fn delete_the_same_account_twice_for_a_login_fails() { let store_dir = tempdir().unwrap(); let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); - let account2 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let id1 = LoginId::new("first".to_string()); let id2 = AccountId::new("second".to_string()); - store_login_at_file( - &wallet, - account1.into_cloned_inner(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); + store_login_at_file(&wallet, account1, hd_path.clone(), id1.clone(), &password).unwrap(); append_account_to_login_at_file( &wallet, @@ -1292,8 +1217,8 @@ mod tests { fn delete_the_same_account_twice_for_a_login_fails_with_multi() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); - let account2 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let id1 = LoginId::new("first".to_string()); @@ -1330,9 +1255,9 @@ mod tests { fn delete_appended_account_doesnt_affect_others() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); - let account2 = ZeroizeMnemonicWrapper::generate_random(); - let account3 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let account3 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let id1 = LoginId::new("first".to_string()); @@ -1341,7 +1266,7 @@ mod tests { store_login_at_file( &wallet_file, - account1.into_cloned_inner(), + account1, hd_path.clone(), id1.clone(), &password, @@ -1350,7 +1275,7 @@ mod tests { store_login_at_file( &wallet_file, - account2.unchecked_clone_inner(), + account2.clone(), hd_path.clone(), id2.clone(), &password, @@ -1373,14 +1298,8 @@ mod tests { let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); let expected = vec![ - WalletAccount::new( - id2.into(), - MnemonicAccount::new(account2.into_cloned_inner(), hd_path.clone()), - ), - WalletAccount::new( - id3, - MnemonicAccount::new(account3.into_cloned_inner(), hd_path), - ), + WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), + WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path)), ] .into(); assert_eq!(loaded_accounts, &expected); @@ -1390,8 +1309,8 @@ mod tests { fn remove_all_accounts_for_a_login_removes_the_file_when_empty() { let store_dir = tempdir().unwrap(); let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); - let account2 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let id1 = LoginId::new("first".to_string()); @@ -1439,9 +1358,9 @@ mod tests { fn remove_all_accounts_for_a_login_removes_that_login() { let store_dir = tempdir().unwrap(); let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = ZeroizeMnemonicWrapper::generate_random(); - let account2 = ZeroizeMnemonicWrapper::generate_random(); - let account3 = ZeroizeMnemonicWrapper::generate_random(); + let account1 = Mnemonic::generate(24).unwrap(); + let account2 = Mnemonic::generate(24).unwrap(); + let account3 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let id1 = LoginId::new("first".to_string()); @@ -1496,7 +1415,7 @@ mod tests { let acc3 = loaded_login.as_multiple_accounts().unwrap(); let expected = vec![WalletAccount::new( DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account3.into_cloned_inner(), hd_path), + MnemonicAccount::new(account3, hd_path), )] .into(); assert_eq!(acc3, &expected); @@ -1506,10 +1425,10 @@ mod tests { fn append_accounts_and_remove_appended_accounts() { let store_dir = tempdir().unwrap(); let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = ZeroizeMnemonicWrapper::generate_random(); - let acc2 = ZeroizeMnemonicWrapper::generate_random(); - let acc3 = ZeroizeMnemonicWrapper::generate_random(); - let acc4 = ZeroizeMnemonicWrapper::generate_random(); + let acc1 = Mnemonic::generate(24).unwrap(); + let acc2 = Mnemonic::generate(24).unwrap(); + let acc3 = Mnemonic::generate(24).unwrap(); + let acc4 = Mnemonic::generate(24).unwrap(); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); let id1 = LoginId::new("first".to_string()); @@ -1519,7 +1438,7 @@ mod tests { store_login_at_file( &wallet, - acc1.unchecked_clone_inner(), + acc1.clone(), hd_path.clone(), id1.clone(), &password, @@ -1528,7 +1447,7 @@ mod tests { store_login_at_file( &wallet, - acc2.unchecked_clone_inner(), + acc2.clone(), hd_path.clone(), id2.clone(), &password, @@ -1564,12 +1483,9 @@ mod tests { let expected = vec![ WalletAccount::new( id2.clone().into(), - MnemonicAccount::new(acc2.into_cloned_inner(), hd_path.clone()), - ), - WalletAccount::new( - id4.clone(), - MnemonicAccount::new(acc4.into_cloned_inner(), hd_path.clone()), + MnemonicAccount::new(acc2, hd_path.clone()), ), + WalletAccount::new(id4.clone(), MnemonicAccount::new(acc4, hd_path.clone())), ] .into(); assert_eq!(loaded_accounts, &expected); @@ -1585,7 +1501,7 @@ mod tests { // The first login is still available let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); let account = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(account.mnemonic(), acc1.as_ref()); + assert_eq!(account.mnemonic(), &acc1); assert_eq!(account.hd_path(), &hd_path); } diff --git a/nym-wallet/src-tauri/src/wallet_storage/password.rs b/nym-wallet/src-tauri/src/wallet_storage/password.rs index d523c87496..02c570b342 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/password.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/password.rs @@ -1,13 +1,12 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use serde::{Deserialize, Serialize}; use std::fmt; - -use serde::{Deserialize, Deserializer, Serialize}; -use zeroize::Zeroize; +use zeroize::{Zeroize, Zeroizing}; // The `LoginId` is the top level id in the wallet file, and is not stored encrypted -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Zeroize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub(crate) struct LoginId(String); impl LoginId { @@ -81,26 +80,4 @@ impl fmt::Display for AccountId { } // simple wrapper for String that will get zeroized on drop -#[derive(Zeroize)] -#[zeroize(drop)] -pub struct UserPassword(String); - -impl UserPassword { - #[cfg(test)] - pub(crate) fn new(inner: String) -> Self { - UserPassword(inner) - } - - pub(crate) fn as_bytes(&self) -> &[u8] { - self.0.as_bytes() - } -} - -impl<'de> Deserialize<'de> for UserPassword { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - Ok(UserPassword(String::deserialize(deserializer)?)) - } -} +pub type UserPassword = Zeroizing; diff --git a/tools/nym-cli/Cargo.toml b/tools/nym-cli/Cargo.toml index cf62275946..041ea9578b 100644 --- a/tools/nym-cli/Cargo.toml +++ b/tools/nym-cli/Cargo.toml @@ -16,7 +16,7 @@ pretty_env_logger = "0.4" serde = { version = "1", features = ["derive"] } serde_json = "1" tokio = { version = "1.11", features = [ "net", "rt-multi-thread", "macros", "signal"] } -bip39 = "1.0.1" +bip39 = { workspace = true } anyhow = "1" tap = "1" From 8e963184786661288d0f9df5d0b7478154e9c827 Mon Sep 17 00:00:00 2001 From: pierre Date: Wed, 15 Mar 2023 10:25:53 +0100 Subject: [PATCH 07/14] build(connect-android): try to fix fdroid build --- nym-connect/mobile/src-tauri/Cargo.lock | 231 +++++------------------- nym-connect/mobile/src-tauri/Cargo.toml | 4 + 2 files changed, 53 insertions(+), 182 deletions(-) diff --git a/nym-connect/mobile/src-tauri/Cargo.lock b/nym-connect/mobile/src-tauri/Cargo.lock index 696e24bcb6..e176ef1873 100644 --- a/nym-connect/mobile/src-tauri/Cargo.lock +++ b/nym-connect/mobile/src-tauri/Cargo.lock @@ -186,15 +186,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "autocfg" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" -dependencies = [ - "autocfg 1.1.0", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -245,15 +236,16 @@ dependencies = [ [[package]] name = "bip39" -version = "1.1.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5b9d9748b5770d1539657653dc5ac3cd9353549e74238dc0d96c22919128b94" +checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" dependencies = [ "bitcoin_hashes", - "rand 0.6.5", - "rand_core 0.4.2", + "rand 0.8.5", + "rand_core 0.6.4", "serde", "unicode-normalization", + "zeroize", ] [[package]] @@ -501,7 +493,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa" dependencies = [ - "smallvec 1.10.0", + "smallvec", ] [[package]] @@ -649,15 +641,6 @@ dependencies = [ "wasm-utils", ] -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags", -] - [[package]] name = "cocoa" version = "0.24.1" @@ -1005,7 +988,7 @@ dependencies = [ "phf 0.8.0", "proc-macro2", "quote", - "smallvec 1.10.0", + "smallvec", "syn", ] @@ -1687,12 +1670,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - [[package]] name = "funty" version = "2.0.0" @@ -2028,7 +2005,7 @@ dependencies = [ "libc", "once_cell", "pin-project-lite", - "smallvec 1.10.0", + "smallvec", "thiserror", ] @@ -2076,7 +2053,7 @@ dependencies = [ "gobject-sys", "libc", "once_cell", - "smallvec 1.10.0", + "smallvec", "thiserror", ] @@ -2659,7 +2636,7 @@ version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ - "autocfg 1.1.0", + "autocfg", "hashbrown 0.12.3", "serde", ] @@ -3009,7 +2986,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ - "autocfg 1.1.0", + "autocfg", "scopeguard", ] @@ -3095,12 +3072,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - [[package]] name = "memchr" version = "2.5.0" @@ -3113,7 +3084,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] @@ -3273,7 +3244,7 @@ version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-traits", ] @@ -3283,7 +3254,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-integer", "num-traits", ] @@ -3294,7 +3265,7 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ - "autocfg 1.1.0", + "autocfg", "libm", ] @@ -4002,7 +3973,7 @@ version = "0.9.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" dependencies = [ - "autocfg 1.1.0", + "autocfg", "cc", "libc", "openssl-src", @@ -4097,7 +4068,7 @@ dependencies = [ "instant", "libc", "redox_syscall", - "smallvec 1.10.0", + "smallvec", "winapi", ] @@ -4110,7 +4081,7 @@ dependencies = [ "cfg-if", "libc", "redox_syscall", - "smallvec 1.10.0", + "smallvec", "windows-sys 0.45.0", ] @@ -4545,25 +4516,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -dependencies = [ - "autocfg 0.1.8", - "libc", - "rand_chacha 0.1.1", - "rand_core 0.4.2", - "rand_hc 0.1.0", - "rand_isaac", - "rand_jitter", - "rand_os", - "rand_pcg 0.1.2", - "rand_xorshift", - "winapi", -] - [[package]] name = "rand" version = "0.7.3" @@ -4574,8 +4526,8 @@ dependencies = [ "libc", "rand_chacha 0.2.2", "rand_core 0.5.1", - "rand_hc 0.2.0", - "rand_pcg 0.2.1", + "rand_hc", + "rand_pcg", ] [[package]] @@ -4589,16 +4541,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.3.1", -] - [[package]] name = "rand_chacha" version = "0.2.2" @@ -4619,21 +4561,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - [[package]] name = "rand_core" version = "0.5.1" @@ -4662,15 +4589,6 @@ dependencies = [ "rand 0.7.3", ] -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "rand_hc" version = "0.2.0" @@ -4680,50 +4598,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -dependencies = [ - "libc", - "rand_core 0.4.2", - "winapi", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "winapi", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.4.2", -] - [[package]] name = "rand_pcg" version = "0.2.1" @@ -4733,15 +4607,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "raw-window-handle" version = "0.5.0" @@ -4751,15 +4616,6 @@ dependencies = [ "cty", ] -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "redox_syscall" version = "0.2.16" @@ -5177,7 +5033,7 @@ dependencies = [ "phf_codegen", "precomputed-hash", "servo_arc", - "smallvec 1.10.0", + "smallvec", "thin-slice", ] @@ -5445,16 +5301,7 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "smallvec" -version = "0.6.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" -dependencies = [ - "maybe-uninit", + "autocfg", ] [[package]] @@ -5626,7 +5473,7 @@ dependencies = [ "percent-encoding", "rustls 0.19.1", "sha2 0.10.6", - "smallvec 1.10.0", + "smallvec", "sqlformat 0.1.8", "sqlx-rt 0.5.13", "stringprep", @@ -5673,7 +5520,7 @@ dependencies = [ "rustls 0.20.8", "rustls-pemfile", "sha2 0.10.6", - "smallvec 1.10.0", + "smallvec", "sqlformat 0.2.1", "sqlx-rt 0.6.2", "stringprep", @@ -6363,13 +6210,28 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" dependencies = [ - "autocfg 1.1.0", + "autocfg", "bytes", "libc", "memchr", @@ -6564,7 +6426,7 @@ dependencies = [ "once_cell", "regex", "sharded-slab", - "smallvec 1.10.0", + "smallvec", "thread_local", "tracing", "tracing-core", @@ -6667,11 +6529,11 @@ checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-normalization" -version = "0.1.9" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09c8070a9942f5e7cfccd93f490fdebd230ee3c3c9f107cb25bad5351ef671cf" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ - "smallvec 0.6.14", + "tinyvec", ] [[package]] @@ -7438,3 +7300,8 @@ dependencies = [ "syn", "synstructure", ] + +[[patch.unused]] +name = "tauri-mobile" +version = "0.2.4" +source = "git+https://github.com/tauri-apps/tauri-mobile?branch=dev#442f0d2c7328930db61058a55706d22e6a401c16" diff --git a/nym-connect/mobile/src-tauri/Cargo.toml b/nym-connect/mobile/src-tauri/Cargo.toml index 628a96a7c4..6d79f5d0af 100644 --- a/nym-connect/mobile/src-tauri/Cargo.toml +++ b/nym-connect/mobile/src-tauri/Cargo.toml @@ -19,6 +19,10 @@ crate-type = ["staticlib", "cdylib", "rlib"] tauri-build = { version = "2.0.0-alpha.1", features = [] } # tauri-build = { git = "https://github.com/tauri-apps/tauri", branch = "next", features = [] } +# TODO untill new tauri version includes https://github.com/tauri-apps/tauri-mobile/pull/111 +[patch.crates-io] +tauri-mobile = { git = "https://github.com/tauri-apps/tauri-mobile", branch = "dev" } + [dependencies] anyhow = "1.0" bip39 = { version = "2.0.0", features = ["zeroize"] } From 9eaf9cf4917c7b362ffd53e159b62c6d9dd24b90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogdan-=C8=98tefan=20Neac=C5=9Fu?= Date: Wed, 15 Mar 2023 11:16:43 +0000 Subject: [PATCH 08/14] Feature/fix resharing (#3139) * Compare verified vks against current group instead of initial dealers * Fix various dkg logs * API auto-advance epoch even on corrupt states * Use verified vks as ultimate truth for dealers * Set initial dealers based of verified vk * Extend register period even more * Fix test * Use shares from current epoch * Save initial dealers only when triggering resharing * Fix tests * Backup the last InProgress state too * Reset previous signers that are not initial dealers * Add unit test for bug reproduction * More verbose debug logging * Handle edge case for coconut keypair removal * Update dkg api test * Remove dealings directly for each key * Replacement data is saved only on the first reshare start * More debug logging * On failed DKG, just reset * Clippy fix --- .../coconut-dkg/src/types.rs | 2 +- .../coconut-dkg/src/dealers/transactions.rs | 6 +- .../coconut-dkg/src/dealings/transactions.rs | 2 +- .../src/epoch_state/transactions.rs | 153 ++++++---- .../src/verification_key_shares/storage.rs | 22 +- nym-api/src/coconut/dkg/controller.rs | 117 ++++---- nym-api/src/coconut/dkg/dealing.rs | 35 ++- nym-api/src/coconut/dkg/public_key.rs | 17 +- nym-api/src/coconut/dkg/state.rs | 11 +- nym-api/src/coconut/dkg/verification_key.rs | 276 ++++++++++++++++-- nym-api/src/coconut/error.rs | 2 +- nym-api/src/coconut/tests.rs | 79 +++-- .../src/validator/mixnet/delegators/mod.rs | 1 - 13 files changed, 553 insertions(+), 170 deletions(-) diff --git a/common/cosmwasm-smart-contracts/coconut-dkg/src/types.rs b/common/cosmwasm-smart-contracts/coconut-dkg/src/types.rs index 4eca33cb55..ee0b854eb9 100644 --- a/common/cosmwasm-smart-contracts/coconut-dkg/src/types.rs +++ b/common/cosmwasm-smart-contracts/coconut-dkg/src/types.rs @@ -21,7 +21,7 @@ pub const TOTAL_DEALINGS: usize = 2 + 2 + 1; #[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq, Eq, Ord, PartialOrd)] pub struct InitialReplacementData { pub initial_dealers: Vec, - pub initial_height: Option, + pub initial_height: u64, } #[derive( diff --git a/contracts/coconut-dkg/src/dealers/transactions.rs b/contracts/coconut-dkg/src/dealers/transactions.rs index e7d019ee65..b59ccc0780 100644 --- a/contracts/coconut-dkg/src/dealers/transactions.rs +++ b/contracts/coconut-dkg/src/dealers/transactions.rs @@ -22,7 +22,7 @@ fn verify_dealer(deps: DepsMut<'_>, dealer: &Addr, resharing: bool) -> Result<() let state = STATE.load(deps.storage)?; let height = if resharing { - INITIAL_REPLACEMENT_DATA.load(deps.storage)?.initial_height + Some(INITIAL_REPLACEMENT_DATA.load(deps.storage)?.initial_height) } else { None }; @@ -105,7 +105,7 @@ pub(crate) mod tests { deps.as_mut().storage, &InitialReplacementData { initial_dealers: vec![details1.address, details2.address, details3.address], - initial_height: Some(1), + initial_height: 1, }, ) .unwrap(); @@ -126,7 +126,7 @@ pub(crate) mod tests { INITIAL_REPLACEMENT_DATA .update::<_, ContractError>(deps.as_mut().storage, |mut data| { - data.initial_height = Some(2); + data.initial_height = 2; Ok(data) }) .unwrap(); diff --git a/contracts/coconut-dkg/src/dealings/transactions.rs b/contracts/coconut-dkg/src/dealings/transactions.rs index e5a9d9bb1b..5924b54822 100644 --- a/contracts/coconut-dkg/src/dealings/transactions.rs +++ b/contracts/coconut-dkg/src/dealings/transactions.rs @@ -110,7 +110,7 @@ pub(crate) mod tests { deps.as_mut().storage, &InitialReplacementData { initial_dealers: vec![], - initial_height: None, + initial_height: 1, }, ) .unwrap(); diff --git a/contracts/coconut-dkg/src/epoch_state/transactions.rs b/contracts/coconut-dkg/src/epoch_state/transactions.rs index b2fb55125e..46f6ebc611 100644 --- a/contracts/coconut-dkg/src/epoch_state/transactions.rs +++ b/contracts/coconut-dkg/src/epoch_state/transactions.rs @@ -7,6 +7,7 @@ use crate::epoch_state::storage::{CURRENT_EPOCH, INITIAL_REPLACEMENT_DATA, THRES use crate::epoch_state::utils::check_epoch_state; use crate::error::ContractError; use crate::state::STATE; +use crate::verification_key_shares::storage::verified_dealers; use cosmwasm_std::{Addr, Deps, DepsMut, Env, Order, Response, Storage}; use nym_coconut_dkg_common::types::{Epoch, EpochState, InitialReplacementData}; @@ -19,7 +20,13 @@ fn reset_epoch_state(storage: &mut dyn Storage) -> Result<(), ContractError> { for dealer_addr in dealers { let details = current_dealers().load(storage, &dealer_addr)?; for dealings in DEALINGS_BYTES { - dealings.remove(storage, &details.address); + let dealing_keys: Vec<_> = dealings + .keys(storage, None, None, Order::Ascending) + .flatten() + .collect(); + for key in dealing_keys { + dealings.remove(storage, &key); + } } current_dealers().remove(storage, &dealer_addr)?; past_dealers().save(storage, &dealer_addr, &details)?; @@ -46,15 +53,9 @@ fn dealers_still_active( } fn dealers_eq_members(deps: &DepsMut<'_>) -> Result { - let dealers_still_active = dealers_still_active( - &deps.as_ref(), - current_dealers() - .keys(deps.storage, None, None, Order::Ascending) - .flatten(), - )?; - let all_dealers = current_dealers() - .keys(deps.storage, None, None, Order::Ascending) - .count(); + let verified_dealers = verified_dealers(deps.storage)?; + let all_dealers = verified_dealers.len(); + let dealers_still_active = dealers_still_active(&deps.as_ref(), verified_dealers.into_iter())?; let group_members = STATE .load(deps.storage)? .group_addr @@ -66,7 +67,11 @@ fn dealers_eq_members(deps: &DepsMut<'_>) -> Result { fn replacement_threshold_surpassed(deps: &DepsMut<'_>) -> Result { let threshold = THRESHOLD.load(deps.storage)? as usize; - let initial_dealers = INITIAL_REPLACEMENT_DATA.load(deps.storage)?.initial_dealers; + let initial_dealers = verified_dealers(deps.storage)?; + if initial_dealers.is_empty() { + // possibly failed DKG, just reset and start again + return Ok(true); + } let initial_dealer_count = initial_dealers.len(); let replacement_threshold = threshold - (initial_dealers.len() + 2 - 1) / 2 + 1; let removed_dealer_count = @@ -90,24 +95,23 @@ pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result, _>>()?; - if current_dealers.is_empty() { - // If no dealer registered yet, we just stay in the same state until there's at least one + let group_members = + STATE + .load(deps.storage)? + .group_addr + .list_members(&deps.querier, None, None)?; + if current_dealers.len() < group_members.len() { + // If not all group members registered yet, we just stay in the same state until + // they either register or they get kicked out of the group new_state = current_epoch.state; } else { // note: ceiling in integer division can be achieved via q = (x + y - 1) / y; let threshold = (2 * current_dealers.len() as u64 + 3 - 1) / 3; THRESHOLD.save(deps.storage, &threshold)?; - if !resharing { - let replacement_data = InitialReplacementData { - initial_dealers: current_dealers, - initial_height: None, - }; - INITIAL_REPLACEMENT_DATA.save(deps.storage, &replacement_data)?; - } } }; Epoch::new( @@ -129,13 +133,23 @@ pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result(deps.storage, |mut data| { - data.initial_height = Some(env.block.height); - Ok(data) - })?; + if INITIAL_REPLACEMENT_DATA.may_load(deps.storage)?.is_some() { + INITIAL_REPLACEMENT_DATA.update::<_, ContractError>(deps.storage, |mut data| { + data.initial_height = env.block.height; + Ok(data) + })?; + } else { + let replacement_data = InitialReplacementData { + initial_dealers: verified_dealers(deps.storage)?, + initial_height: env.block.height, + }; + INITIAL_REPLACEMENT_DATA.save(deps.storage, &replacement_data)?; + } + EpochState::PublicKeySubmission { resharing: true } }; reset_epoch_state(deps.storage)?; @@ -158,10 +172,8 @@ pub(crate) fn try_surpassed_threshold( check_epoch_state(deps.storage, EpochState::InProgress)?; let threshold = THRESHOLD.load(deps.storage)?; - let dealers = current_dealers() - .keys(deps.storage, None, None, Order::Ascending) - .flatten(); - if dealers_still_active(&deps.as_ref(), dealers)? < threshold as usize { + let dealers = verified_dealers(deps.storage)?; + if dealers_still_active(&deps.as_ref(), dealers.into_iter())? < threshold as usize { reset_epoch_state(deps.storage)?; CURRENT_EPOCH.update::<_, ContractError>(deps.storage, |epoch| { Ok(Epoch::new( @@ -180,8 +192,9 @@ pub(crate) fn try_surpassed_threshold( pub(crate) mod tests { use super::*; use crate::error::ContractError::EarlyEpochStateAdvancement; - use crate::support::tests::fixtures::dealer_details_fixture; + use crate::support::tests::fixtures::{dealer_details_fixture, vk_share_fixture}; use crate::support::tests::helpers::{init_contract, GROUP_MEMBERS}; + use crate::verification_key_shares::storage::vk_shares; use cosmwasm_std::testing::mock_env; use cosmwasm_std::Addr; use cw4::Member; @@ -204,11 +217,15 @@ pub(crate) mod tests { for n in [10, 25, 50, 100] { let dealers: Vec<_> = (0..n).map(dealer_details_fixture).collect(); + let shares: Vec<_> = (0..n).map(|idx| vk_share_fixture(&format!("owner{}", idx), 0)).collect(); let initial_dealers = dealers.iter().map(|d| d.address.clone()).collect(); let data = InitialReplacementData { initial_dealers, - initial_height: None, + initial_height: 1, }; + for share in shares { + vk_shares().save(deps.as_mut().storage, (&share.owner, 0), &share).unwrap(); + } for f in [two_thirds, three_fourths, ninty_pc] { let threshold = f(n); THRESHOLD.save(deps.as_mut().storage, &threshold).unwrap(); @@ -247,39 +264,39 @@ pub(crate) mod tests { assert!(dealers_eq_members(&deps.as_mut()).unwrap()); - let details = dealer_details_fixture(1); - let different_details = dealer_details_fixture(2); - current_dealers() - .save(deps.as_mut().storage, &details.address, &details) + let share = vk_share_fixture("owner2", 0); + let different_share = vk_share_fixture("owner4", 0); + vk_shares() + .save(deps.as_mut().storage, (&share.owner, 0), &share) .unwrap(); assert!(!dealers_eq_members(&deps.as_mut()).unwrap()); - current_dealers() - .remove(deps.as_mut().storage, &details.address) + vk_shares() + .remove(deps.as_mut().storage, (&share.owner, 0)) .unwrap(); GROUP_MEMBERS.lock().unwrap().push(( Member { - addr: "owner1".to_string(), + addr: "owner2".to_string(), weight: 10, }, 1, )); assert!(!dealers_eq_members(&deps.as_mut()).unwrap()); - current_dealers() + vk_shares() .save( deps.as_mut().storage, - &different_details.address, - &different_details, + (&different_share.owner, 0), + &different_share, ) .unwrap(); assert!(!dealers_eq_members(&deps.as_mut()).unwrap()); - current_dealers() - .remove(deps.as_mut().storage, &different_details.address) + vk_shares() + .remove(deps.as_mut().storage, (&different_share.owner, 0)) .unwrap(); - current_dealers() - .save(deps.as_mut().storage, &details.address, &details) + vk_shares() + .save(deps.as_mut().storage, (&share.owner, 0), &share) .unwrap(); assert!(dealers_eq_members(&deps.as_mut()).unwrap()); } @@ -407,6 +424,12 @@ pub(crate) mod tests { ); // setup dealer details + let all_shares: [_; 4] = std::array::from_fn(|i| vk_share_fixture(&format!("owner{}", i + 1), 0)); + for share in all_shares.iter() { + vk_shares() + .save(deps.as_mut().storage, (&share.owner, 0), share) + .unwrap(); + } let all_details: [_; 4] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 1)); for details in all_details.iter() { current_dealers() @@ -431,12 +454,6 @@ pub(crate) mod tests { .time .plus_seconds(epoch.time_configuration.dealing_exchange_time_secs) ); - let replacement_data = INITIAL_REPLACEMENT_DATA.load(&deps.storage).unwrap(); - let expected_replacement_data = InitialReplacementData { - initial_dealers: all_details.iter().map(|d| d.address.clone()).collect(), - initial_height: None, - }; - assert_eq!(replacement_data, expected_replacement_data); env.block.time = env .block @@ -588,8 +605,14 @@ pub(crate) mod tests { ); assert_eq!(curr_epoch, expected_epoch); assert!(THRESHOLD.may_load(&deps.storage).unwrap().is_none()); + let replacement_data = INITIAL_REPLACEMENT_DATA.load(&deps.storage).unwrap(); + let expected_replacement_data = InitialReplacementData { + initial_dealers: all_details.iter().map(|d| d.address.clone()).collect(), + initial_height: 12345, + }; + assert_eq!(replacement_data, expected_replacement_data); - let all_details: [_; 2] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 2)); + let all_details: [_; 4] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 2)); for details in all_details.iter() { past_dealers().remove(deps.as_mut().storage, &details.address).unwrap(); current_dealers() @@ -607,6 +630,17 @@ pub(crate) mod tests { advance_epoch_state(deps.as_mut(), env.clone()).unwrap(); } + let all_shares: [_; 4] = std::array::from_fn(|i| { + let mut share = vk_share_fixture(&format!("owner{}", i + 1), 1); + share.verified = i % 2 == 0; + share + }); + for share in all_shares.iter() { + vk_shares() + .save(deps.as_mut().storage, (&share.owner, 0), share) + .unwrap(); + } + // Group changed even more, surpassing threshold, so re-run dkg in reset mode *GROUP_MEMBERS.lock().unwrap().last_mut().unwrap() = ( Member { @@ -623,7 +657,7 @@ pub(crate) mod tests { advance_epoch_state(deps.as_mut(), env.clone()).unwrap(); let curr_epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap(); let expected_epoch = Epoch::new( - EpochState::PublicKeySubmission { resharing: false }, + EpochState::PublicKeySubmission { resharing: true }, prev_epoch.epoch_id + 1, prev_epoch.time_configuration, env.block.time, @@ -672,12 +706,25 @@ pub(crate) mod tests { } ); + + let all_shares: [_; 3] = std::array::from_fn(|i| vk_share_fixture(&format!("owner{}", i + 1), 0)); + for share in all_shares.iter() { + vk_shares() + .save(deps.as_mut().storage, (&share.owner, 0), share) + .unwrap(); + } let all_details: [_; 3] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 1)); for details in all_details.iter() { current_dealers() .save(deps.as_mut().storage, &details.address, details) .unwrap(); } + let all_shares: [_; 3] = std::array::from_fn(|i| vk_share_fixture(&format!("owner{}", i + 1), 0)); + for share in all_shares.iter() { + vk_shares() + .save(deps.as_mut().storage, (&share.owner, share.epoch_id), share) + .unwrap(); + } for times in [ time_configuration.public_key_submission_time_secs, diff --git a/contracts/coconut-dkg/src/verification_key_shares/storage.rs b/contracts/coconut-dkg/src/verification_key_shares/storage.rs index b27605322d..fae9a59d10 100644 --- a/contracts/coconut-dkg/src/verification_key_shares/storage.rs +++ b/contracts/coconut-dkg/src/verification_key_shares/storage.rs @@ -4,7 +4,9 @@ // SPDX-License-Identifier: Apache-2.0 use crate::constants::{VK_SHARES_EPOCH_ID_IDX_NAMESPACE, VK_SHARES_PK_NAMESPACE}; -use cosmwasm_std::Addr; +use crate::epoch_state::storage::CURRENT_EPOCH; +use crate::error::ContractError; +use cosmwasm_std::{Addr, Order, Storage}; use cw_storage_plus::{Index, IndexList, IndexedMap, MultiIndex}; use nym_coconut_dkg_common::types::EpochId; use nym_coconut_dkg_common::verification_key::ContractVKShare; @@ -35,3 +37,21 @@ pub(crate) fn vk_shares<'a>() -> IndexedMap<'a, VKShareKey<'a>, ContractVKShare, }; IndexedMap::new(VK_SHARES_PK_NAMESPACE, indexes) } + +pub(crate) fn verified_dealers(storage: &dyn Storage) -> Result, ContractError> { + let epoch_id = CURRENT_EPOCH.load(storage)?.epoch_id; + Ok(vk_shares() + .idx + .epoch_id + .prefix(epoch_id) + .range(storage, None, None, Order::Ascending) + .flatten() + .filter_map(|(_, share)| { + if share.verified { + Some(share.owner) + } else { + None + } + }) + .collect()) +} diff --git a/nym-api/src/coconut/dkg/controller.rs b/nym-api/src/coconut/dkg/controller.rs index b4fc713741..d22e77a683 100644 --- a/nym-api/src/coconut/dkg/controller.rs +++ b/nym-api/src/coconut/dkg/controller.rs @@ -84,6 +84,18 @@ impl DkgController { }) } + async fn dump_persistent_state(&self) { + if !self.state.coconut_keypair_is_some().await { + // Delete the files just in case the process is killed before the new keys are generated + std::fs::remove_file(&self.secret_key_path).ok(); + std::fs::remove_file(&self.verification_key_path).ok(); + } + let persistent_state = PersistentState::from(&self.state); + if let Err(err) = persistent_state.save_to_file(self.state.persistent_state_path()) { + warn!("Could not backup the state for this iteration: {err}"); + } + } + pub(crate) async fn handle_epoch_state(&mut self) { match self.dkg_client.get_current_epoch().await { Err(err) => warn!("Could not get current epoch state {err}"), @@ -99,57 +111,64 @@ impl DkgController { 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 { resharing } => { - public_key_submission(&self.dkg_client, &mut self.state, resharing).await - } - EpochState::DealingExchange { resharing } => { - dealing_exchange( - &self.dkg_client, - &mut self.state, - self.rng.clone(), - resharing, - ) - .await - } - EpochState::VerificationKeySubmission { resharing } => { - let keypair_path = nym_pemstore::KeyPairPath::new( - self.secret_key_path.clone(), - self.verification_key_path.clone(), - ); - verification_key_submission( - &self.dkg_client, - &mut self.state, - &keypair_path, - resharing, - ) - .await - } - EpochState::VerificationKeyValidation { resharing } => { - verification_key_validation(&self.dkg_client, &mut self.state, resharing) + debug!("Epoch state is corrupted - {err}. Awaiting for a DKG restart."); + } else { + let ret = match epoch.state { + EpochState::PublicKeySubmission { resharing } => { + public_key_submission(&self.dkg_client, &mut self.state, resharing) + .await + } + EpochState::DealingExchange { resharing } => { + dealing_exchange( + &self.dkg_client, + &mut self.state, + self.rng.clone(), + resharing, + ) .await - } - EpochState::VerificationKeyFinalization { resharing } => { - verification_key_finalization(&self.dkg_client, &mut self.state, resharing) + } + EpochState::VerificationKeySubmission { resharing } => { + let keypair_path = nym_pemstore::KeyPairPath::new( + self.secret_key_path.clone(), + self.verification_key_path.clone(), + ); + verification_key_submission( + &self.dkg_client, + &mut self.state, + &keypair_path, + resharing, + ) .await - } - // Just wait, in case we need to redo dkg at some point - 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}"); - } else if epoch.state != EpochState::InProgress { - let persistent_state = PersistentState::from(&self.state); - if let Err(err) = - persistent_state.save_to_file(self.state.persistent_state_path()) - { - warn!("Could not backup the state for this iteration: {err}"); + } + EpochState::VerificationKeyValidation { resharing } => { + verification_key_validation( + &self.dkg_client, + &mut self.state, + resharing, + ) + .await + } + EpochState::VerificationKeyFinalization { resharing } => { + verification_key_finalization( + &self.dkg_client, + &mut self.state, + resharing, + ) + .await + } + // Just wait, in case we need to redo dkg at some point + EpochState::InProgress => { + self.state.set_was_in_progress(); + // We're dumping state here so that we don't do it uselessly during the + // long InProgress state + self.dump_persistent_state().await; + Ok(()) + } + }; + if let Err(err) = ret { + warn!("Could not handle this iteration for the epoch state: {err}"); + } else if epoch.state != EpochState::InProgress { + self.dump_persistent_state().await; } } if let Ok(current_timestamp) = diff --git a/nym-api/src/coconut/dkg/dealing.rs b/nym-api/src/coconut/dkg/dealing.rs index 7c8424acf6..91e9d7ba78 100644 --- a/nym-api/src/coconut/dkg/dealing.rs +++ b/nym-api/src/coconut/dkg/dealing.rs @@ -4,6 +4,7 @@ use crate::coconut::dkg::client::DkgClient; use crate::coconut::dkg::state::{ConsistentState, State}; use crate::coconut::error::CoconutError; +use log::debug; use nym_coconut_dkg_common::types::TOTAL_DEALINGS; use nym_contracts_common::dealings::ContractSafeBytes; use nym_dkg::bte::setup; @@ -18,6 +19,7 @@ pub(crate) async fn dealing_exchange( resharing: bool, ) -> Result<(), CoconutError> { if state.receiver_index().is_some() { + debug!("Receiver index was set previously, nothing to do"); return Ok(()); } @@ -45,6 +47,7 @@ pub(crate) async fn dealing_exchange( return Err(CoconutError::CorruptedCoconutKeyPair); } // We can now erase the keypair from memory + debug!("Removing coconut keypair from memory"); state.set_coconut_keypair(None).await; scalars.push(x); scalars @@ -59,6 +62,11 @@ pub(crate) async fn dealing_exchange( if !resharing || initial_dealers.iter().any(|d| *d == own_address) { let params = setup(); for _ in 0..TOTAL_DEALINGS { + debug!( + "Submitting dealing for indexes {:?} with resharing: {}", + receivers.keys().collect::>(), + prior_resharing_secrets.front().is_some() + ); let (dealing, _) = Dealing::create( rng.clone(), ¶ms, @@ -71,9 +79,11 @@ pub(crate) async fn dealing_exchange( .submit_dealing(ContractSafeBytes::from(&dealing), resharing) .await?; } + } else { + debug!("Nothing to do, waiting for initial dealers to submit dealings"); } - info!("DKG: Finished submitting dealing"); + info!("DKG: Finished dealing exchange"); state.set_receiver_index(receiver_index); Ok(()) @@ -109,7 +119,7 @@ pub(crate) mod tests { fn insert_dealers( params: &Params, - dealer_details_db: &Arc>>, + dealer_details_db: &Arc>>, ) -> Vec { let mut keypairs = vec![]; for (idx, addr) in TEST_VALIDATORS_ADDRESS.iter().enumerate() { @@ -119,12 +129,15 @@ pub(crate) mod tests { keypairs.push(keypair); dealer_details_db.write().unwrap().insert( addr.to_string(), - DealerDetails { - address: Addr::unchecked(*addr), - bte_public_key_with_proof, - announce_address: format!("localhost:80{}", idx), - assigned_index: (idx + 1) as u64, - }, + ( + DealerDetails { + address: Addr::unchecked(*addr), + bte_public_key_with_proof, + announce_address: format!("localhost:80{}", idx), + assigned_index: (idx + 1) as u64, + }, + true, + ), ); } keypairs @@ -216,7 +229,7 @@ pub(crate) mod tests { .unwrap() .entry(TEST_VALIDATORS_ADDRESS[1].to_string()) .and_modify(|details| { - let mut bytes = bs58::decode(details.bte_public_key_with_proof.clone()) + let mut bytes = bs58::decode(details.0.bte_public_key_with_proof.clone()) .into_vec() .unwrap(); // Find another value for last byte that still deserializes to a public key with proof @@ -231,7 +244,7 @@ pub(crate) mod tests { break; } } - details.bte_public_key_with_proof = bs58::encode(&bytes).into_string(); + details.0.bte_public_key_with_proof = bs58::encode(&bytes).into_string(); }); dealing_exchange(&dkg_client, &mut state, OsRng, false) @@ -257,7 +270,7 @@ pub(crate) mod tests { let threshold_db = Arc::new(RwLock::new(Some(3))); let initial_dealers_db = Arc::new(RwLock::new(Some(InitialReplacementData { initial_dealers: vec![Addr::unchecked(TEST_VALIDATORS_ADDRESS[0])], - initial_height: Some(100), + initial_height: 100, }))); let dkg_client = DkgClient::new( DummyClient::new( diff --git a/nym-api/src/coconut/dkg/public_key.rs b/nym-api/src/coconut/dkg/public_key.rs index cf779cd8a1..5312cc0742 100644 --- a/nym-api/src/coconut/dkg/public_key.rs +++ b/nym-api/src/coconut/dkg/public_key.rs @@ -4,6 +4,7 @@ use crate::coconut::dkg::client::DkgClient; use crate::coconut::dkg::state::State; use crate::coconut::error::CoconutError; +use log::debug; use nym_coconut_dkg_common::dealer::DealerType; pub(crate) async fn public_key_submission( @@ -12,9 +13,21 @@ pub(crate) async fn public_key_submission( resharing: bool, ) -> Result<(), CoconutError> { if state.was_in_progress() { - state.reset_persistent(resharing).await; + let own_address = dkg_client.get_address().await.as_ref().to_string(); + let is_initial_dealer = dkg_client + .get_initial_dealers() + .await? + .map(|data| data.initial_dealers.iter().any(|d| *d == own_address)) + .unwrap_or(false); + let reset_coconut_keypair = !resharing || !is_initial_dealer; + debug!( + "Resetting state, with coconut keypair reset: {}", + reset_coconut_keypair + ); + state.reset_persistent(reset_coconut_keypair).await; } if state.node_index().is_some() { + debug!("Node index was set previously, nothing to do"); return Ok(()); } @@ -23,12 +36,14 @@ pub(crate) async fn public_key_submission( let index = if let Some(details) = dealer_details.details { if dealer_details.dealer_type == DealerType::Past { // If it was a dealer in a previous epoch, re-register it for this epoch + debug!("Registering for the current DKG round, with keys from a previous epoch"); dkg_client .register_dealer(bte_key, state.announce_address().to_string(), resharing) .await?; } details.assigned_index } else { + debug!("Registering for the first time to be a dealer"); // First time registration dkg_client .register_dealer(bte_key, state.announce_address().to_string(), resharing) diff --git a/nym-api/src/coconut/dkg/state.rs b/nym-api/src/coconut/dkg/state.rs index 6d37161572..58d7bf20fb 100644 --- a/nym-api/src/coconut/dkg/state.rs +++ b/nym-api/src/coconut/dkg/state.rs @@ -5,6 +5,7 @@ use crate::coconut::dkg::complaints::ComplaintReason; use crate::coconut::error::CoconutError; use crate::coconut::keypair::KeyPair as CoconutKeyPair; use cosmwasm_std::Addr; +use log::debug; use nym_coconut_dkg_common::dealer::DealerDetails; use nym_coconut_dkg_common::types::EpochState; use nym_dkg::bte::{keys::KeyPair as DkgKeyPair, PublicKey, PublicKeyWithProof}; @@ -133,7 +134,7 @@ impl ConsistentState for State { fn proposal_id_value(&self) -> Result { self.proposal_id.ok_or(CoconutError::UnrecoverableState { - reason: String::from("Proposal id should have benn set"), + reason: String::from("Proposal id should have been set"), }) } } @@ -242,8 +243,8 @@ impl State { } } - pub async fn reset_persistent(&mut self, resharing: bool) { - if !resharing { + pub async fn reset_persistent(&mut self, reset_coconut_keypair: bool) { + if reset_coconut_keypair { self.coconut_keypair.set(None).await; } self.node_index = Default::default(); @@ -360,6 +361,10 @@ impl State { .iter_mut() .find(|(addr, _)| *addr == dealer_addr) { + debug!( + "Dealer {} misbehaved: {:?}. It will be marked locally as bad dealer and ignored", + dealer_addr, reason + ); *value = Err(reason); } } diff --git a/nym-api/src/coconut/dkg/verification_key.rs b/nym-api/src/coconut/dkg/verification_key.rs index 402db402ee..a023cd8be6 100644 --- a/nym-api/src/coconut/dkg/verification_key.rs +++ b/nym-api/src/coconut/dkg/verification_key.rs @@ -8,6 +8,7 @@ use crate::coconut::error::CoconutError; use crate::coconut::helpers::accepted_vote_err; use cosmwasm_std::Addr; use cw3::{ProposalResponse, Status}; +use log::debug; use nym_coconut_dkg_common::event_attributes::DKG_PROPOSAL_ID; use nym_coconut_dkg_common::types::{NodeIndex, TOTAL_DEALINGS}; use nym_coconut_dkg_common::verification_key::owner_from_cosmos_msgs; @@ -116,6 +117,11 @@ fn derive_partial_keypair( } }) .unzip(); + debug!( + "Recovering verification keys from dealings of dealers {:?} with receivers {:?}", + filtered_dealers, + filtered_receivers_by_idx.keys().collect::>() + ); let recovered = try_recover_verification_keys( &filtered_dealings, threshold, @@ -123,10 +129,12 @@ fn derive_partial_keypair( )?; recovered_vks.push(recovered); + debug!("Decrypting shares"); let shares = filtered_dealings .iter() .map(|dealing| decrypt_share(dk, node_index_value, &dealing.ciphertexts, None)) .collect::>()?; + debug!("Combining shares into one secret"); let scalar = combine_shares(shares, &filtered_dealers)?; scalars.push(scalar); } @@ -152,13 +160,19 @@ pub(crate) async fn verification_key_submission( resharing: bool, ) -> Result<(), CoconutError> { if state.coconut_keypair_is_some().await { + debug!("Coconut keypair was set previously, nothing to do"); return Ok(()); } let threshold = state.threshold()?; let dealings_maps = deterministic_filter_dealers(dkg_client, state, threshold, resharing).await?; + debug!( + "Filtered dealers to {:?}", + dealings_maps[0].keys().collect::>() + ); let coconut_keypair = derive_partial_keypair(state, threshold, dealings_maps)?; + debug!("Derived own coconut keypair"); let vk_share = coconut_keypair.verification_key().to_bs58(); nym_pemstore::store_keypair(&coconut_keypair, keypair_path)?; let res = dkg_client @@ -173,6 +187,10 @@ pub(crate) async fn verification_key_submission( .map_err(|_| CoconutError::ProposalIdError { reason: String::from("proposal id could not be parsed to u64"), })?; + debug!( + "Submitted own verification key share, proposal id {} is attached to it", + proposal_id + ); state.set_proposal_id(proposal_id); state.set_coconut_keypair(Some(coconut_keypair)).await; info!("DKG: Submitted own verification key"); @@ -195,6 +213,7 @@ pub(crate) async fn verification_key_validation( _resharing: bool, ) -> Result<(), CoconutError> { if state.voted_vks() { + debug!("Already voted on the verification keys, nothing to do"); return Ok(()); } @@ -225,10 +244,15 @@ pub(crate) async fn verification_key_validation( .position(|node_index| contract_share.node_index == *node_index) { let ret = if !check_vk_pairing(¶ms, &recovered_partials[idx], &vk) { + debug!( + "Voting NO to proposal {} because of failed VK pairing", + proposal_id + ); dkg_client .vote_verification_key_share(proposal_id, false) .await } else { + debug!("Voting YES to proposal {}", proposal_id); dkg_client .vote_verification_key_share(proposal_id, true) .await @@ -237,6 +261,10 @@ pub(crate) async fn verification_key_validation( } } Err(_) => { + debug!( + "Voting NO to proposal {} because of failed base 58 deserialization", + proposal_id + ); let ret = dkg_client .vote_verification_key_share(proposal_id, false) .await; @@ -256,6 +284,7 @@ pub(crate) async fn verification_key_finalization( _resharing: bool, ) -> Result<(), CoconutError> { if state.executed_proposal() { + debug!("Already executed the proposal, nothing to do"); return Ok(()); } @@ -294,7 +323,7 @@ pub(crate) mod tests { use validator_client::nyxd::AccountId; struct MockContractDb { - dealer_details_db: Arc>>, + dealer_details_db: Arc>>, dealings_db: Arc>>>, proposal_db: Arc>>, verification_share_db: Arc>>, @@ -315,10 +344,11 @@ pub(crate) mod tests { } } - const TEST_VALIDATORS_ADDRESS: [&str; 3] = [ + const TEST_VALIDATORS_ADDRESS: [&str; 4] = [ "n1aq9kakfgwqcufr23lsv644apavcntrsqsk4yus", "n1s9l3xr4g0rglvk4yctktmck3h4eq0gp6z2e20v", "n19kl4py32vsk297dm93ezem992cdyzdy4zuc2x6", + "n1jfrs6cmw9t7dv0x8cgny6geunzjh56n2s89fkv", ]; async fn prepare_clients_and_states(db: &MockContractDb) -> Vec<(DkgClient, State)> { @@ -418,7 +448,7 @@ pub(crate) mod tests { .unwrap(); assert_eq!(filtered.len(), TOTAL_DEALINGS); for mapping in filtered.iter() { - assert_eq!(mapping.len(), 3); + assert_eq!(mapping.len(), 4); } } } @@ -471,7 +501,7 @@ pub(crate) mod tests { for (dkg_client, state) in clients_and_states.iter_mut().skip(1) { *db.initial_dealers_db.write().unwrap() = Some(InitialReplacementData { initial_dealers: vec![Addr::unchecked(TEST_VALIDATORS_ADDRESS[0])], - initial_height: None, + initial_height: 1, }); let filtered = deterministic_filter_dealers(dkg_client, state, 2, true) .await @@ -504,7 +534,7 @@ pub(crate) mod tests { for (dkg_client, state) in clients_and_states.iter_mut().skip(1) { *db.initial_dealers_db.write().unwrap() = Some(InitialReplacementData { initial_dealers: vec![], - initial_height: None, + initial_height: 1, }); let filtered = deterministic_filter_dealers(dkg_client, state, 2, true) .await @@ -542,7 +572,7 @@ pub(crate) mod tests { .unwrap(); assert_eq!(filtered.len(), TOTAL_DEALINGS); for mapping in filtered.iter() { - assert_eq!(mapping.len(), 2); + assert_eq!(mapping.len(), 3); } let corrupted_status = state .all_dealers() @@ -803,6 +833,9 @@ pub(crate) mod tests { async fn reshare_preserves_keys() { let db = MockContractDb::new(); let mut clients_and_states = prepare_clients_and_states_with_finalization(&db).await; + for (_, state) in clients_and_states.iter_mut() { + state.set_was_in_progress(); + } let params = Parameters::new(4).unwrap(); let mut vks = vec![]; @@ -839,23 +872,22 @@ pub(crate) mod tests { KeyPair::new(), ); - let removed_dealer = clients_and_states.first().unwrap().0.get_address().await; - db.dealer_details_db - .write() - .unwrap() - .remove(removed_dealer.as_ref()); + for (_, active) in db.dealer_details_db.write().unwrap().values_mut() { + *active = false; + } + *db.dealings_db.write().unwrap() = Default::default(); *db.verification_share_db.write().unwrap() = Default::default(); + let mut initial_dealers = vec![]; + for (dkg_client, _) in clients_and_states.iter() { + let client_address = Addr::unchecked(dkg_client.get_address().await.as_ref()); + initial_dealers.push(client_address); + } *db.initial_dealers_db.write().unwrap() = Some(InitialReplacementData { - initial_dealers: vec![ - Addr::unchecked(clients_and_states[1].0.get_address().await.as_ref()), - Addr::unchecked(clients_and_states[2].0.get_address().await.as_ref()), - ], - initial_height: None, + initial_dealers, + initial_height: 1, }); *clients_and_states.first_mut().unwrap() = (new_dkg_client, state); - clients_and_states[1].1.set_was_in_progress(); - clients_and_states[2].1.set_was_in_progress(); for (dkg_client, state) in clients_and_states.iter_mut() { public_key_submission(dkg_client, state, true) @@ -880,6 +912,214 @@ pub(crate) mod tests { std::fs::remove_file(private_key_path).unwrap(); std::fs::remove_file(public_key_path).unwrap(); } + for (dkg_client, state) in clients_and_states.iter_mut() { + verification_key_validation(dkg_client, state, true) + .await + .unwrap(); + } + for (dkg_client, state) in clients_and_states.iter_mut() { + verification_key_finalization(dkg_client, state, true) + .await + .unwrap(); + } + assert!(db + .proposal_db + .read() + .unwrap() + .values() + .all(|proposal| { proposal.status == Status::Executed })); + + let mut vks = vec![]; + let mut indices = vec![]; + for (_, state) in clients_and_states.iter() { + let vk = state + .coconut_secret_key() + .await + .unwrap() + .verification_key(¶ms); + let index = state.node_index().unwrap(); + vks.push(vk); + indices.push(index); + } + let reshared_master_vk = aggregate_verification_keys(&vks, Some(&indices)).unwrap(); + assert_eq!(initial_master_vk, reshared_master_vk); + } + + #[tokio::test] + #[ignore] // expensive test + async fn reshare_after_reset() { + let db = MockContractDb::new(); + let mut clients_and_states = prepare_clients_and_states_with_finalization(&db).await; + for (_, state) in clients_and_states.iter_mut() { + state.set_was_in_progress(); + } + + let new_dkg_client = DkgClient::new( + DummyClient::new( + AccountId::from_str("n1vxkywf9g4cg0k2dehanzwzz64jw782qm0kuynf").unwrap(), + ) + .with_dealer_details(&db.dealer_details_db) + .with_dealings(&db.dealings_db) + .with_proposal_db(&db.proposal_db) + .with_verification_share(&db.verification_share_db) + .with_threshold(&db.threshold_db) + .with_initial_dealers_db(&db.initial_dealers_db), + ); + let keypair = DkgKeyPair::new(&setup(), OsRng); + let state = State::new( + PathBuf::default(), + PersistentState::default(), + Url::parse("localhost:8000").unwrap(), + keypair, + KeyPair::new(), + ); + let new_dkg_client2 = DkgClient::new( + DummyClient::new( + AccountId::from_str("n1sqkxzh7nl6kgndr4ew9795t2nkwmd8tpql67q7").unwrap(), + ) + .with_dealer_details(&db.dealer_details_db) + .with_dealings(&db.dealings_db) + .with_proposal_db(&db.proposal_db) + .with_verification_share(&db.verification_share_db) + .with_threshold(&db.threshold_db) + .with_initial_dealers_db(&db.initial_dealers_db), + ); + let keypair = DkgKeyPair::new(&setup(), OsRng); + let state2 = State::new( + PathBuf::default(), + PersistentState::default(), + Url::parse("localhost:8000").unwrap(), + keypair, + KeyPair::new(), + ); + + for (_, active) in db.dealer_details_db.write().unwrap().values_mut() { + *active = false; + } + + *db.dealings_db.write().unwrap() = Default::default(); + *db.verification_share_db.write().unwrap() = Default::default(); + clients_and_states.pop().unwrap(); + let (initial_client2, initial_state2) = clients_and_states.pop().unwrap(); + clients_and_states.push((new_dkg_client, state)); + clients_and_states.push((new_dkg_client2, state2)); + + // DKG in reset mode + for (dkg_client, state) in clients_and_states.iter_mut() { + public_key_submission(dkg_client, state, false) + .await + .unwrap(); + } + for (dkg_client, state) in clients_and_states.iter_mut() { + dealing_exchange(dkg_client, state, OsRng, false) + .await + .unwrap(); + } + for (dkg_client, state) in clients_and_states.iter_mut() { + let random_file: usize = OsRng.gen(); + let private_key_path = temp_dir().join(format!("private{}.pem", random_file)); + let public_key_path = temp_dir().join(format!("public{}.pem", random_file)); + let keypair_path = KeyPairPath::new(private_key_path.clone(), public_key_path.clone()); + verification_key_submission(dkg_client, state, &keypair_path, false) + .await + .unwrap(); + std::fs::remove_file(private_key_path).unwrap(); + std::fs::remove_file(public_key_path).unwrap(); + } + for (dkg_client, state) in clients_and_states.iter_mut() { + verification_key_validation(dkg_client, state, false) + .await + .unwrap(); + } + for (dkg_client, state) in clients_and_states.iter_mut() { + verification_key_finalization(dkg_client, state, false) + .await + .unwrap(); + } + assert!(db + .proposal_db + .read() + .unwrap() + .values() + .all(|proposal| { proposal.status == Status::Executed })); + for (_, state) in clients_and_states.iter_mut() { + state.set_was_in_progress(); + } + + // DKG in reshare mode + let params = Parameters::new(4).unwrap(); + + let mut vks = vec![]; + let mut indices = vec![]; + for (_, state) in clients_and_states.iter() { + let vk = state + .coconut_secret_key() + .await + .unwrap() + .verification_key(¶ms); + let index = state.node_index().unwrap(); + vks.push(vk); + indices.push(index); + } + let initial_master_vk = aggregate_verification_keys(&vks, Some(&indices)).unwrap(); + + for (_, active) in db.dealer_details_db.write().unwrap().values_mut() { + *active = false; + } + *db.dealings_db.write().unwrap() = Default::default(); + *db.verification_share_db.write().unwrap() = Default::default(); + let mut initial_dealers = vec![]; + for (dkg_client, _) in clients_and_states.iter() { + let client_address = Addr::unchecked(dkg_client.get_address().await.as_ref()); + initial_dealers.push(client_address); + } + *db.initial_dealers_db.write().unwrap() = Some(InitialReplacementData { + initial_dealers, + initial_height: 1, + }); + *clients_and_states.last_mut().unwrap() = (initial_client2, initial_state2); + + for (dkg_client, state) in clients_and_states.iter_mut() { + public_key_submission(dkg_client, state, true) + .await + .unwrap(); + } + + for (dkg_client, state) in clients_and_states.iter_mut() { + dealing_exchange(dkg_client, state, OsRng, true) + .await + .unwrap(); + } + + for (dkg_client, state) in clients_and_states.iter_mut() { + let random_file: usize = OsRng.gen(); + let private_key_path = temp_dir().join(format!("private{}.pem", random_file)); + let public_key_path = temp_dir().join(format!("public{}.pem", random_file)); + let keypair_path = KeyPairPath::new(private_key_path.clone(), public_key_path.clone()); + verification_key_submission(dkg_client, state, &keypair_path, true) + .await + .unwrap(); + std::fs::remove_file(private_key_path).unwrap(); + std::fs::remove_file(public_key_path).unwrap(); + } + + for (dkg_client, state) in clients_and_states.iter_mut() { + verification_key_validation(dkg_client, state, true) + .await + .unwrap(); + } + for (dkg_client, state) in clients_and_states.iter_mut() { + verification_key_finalization(dkg_client, state, true) + .await + .unwrap(); + } + // assert!(db + // .proposal_db + // .read() + // .unwrap() + // .values() + // .all(|proposal| { proposal.status == Status::Executed })); + let mut vks = vec![]; let mut indices = vec![]; for (_, state) in clients_and_states.iter() { diff --git a/nym-api/src/coconut/error.rs b/nym-api/src/coconut/error.rs index 387f4219f0..210af7e1ee 100644 --- a/nym-api/src/coconut/error.rs +++ b/nym-api/src/coconut/error.rs @@ -91,7 +91,7 @@ pub enum CoconutError { #[error("Failed to recover assigned node index: {reason}")] NodeIndexRecoveryError { reason: String }, - #[error("Unrecoverable state: {reason}. Process should be restarted")] + #[error("Unrecoverable state: {reason}")] UnrecoverableState { reason: String }, #[error("DKG has not finished yet in order to derive the coconut key")] diff --git a/nym-api/src/coconut/tests.rs b/nym-api/src/coconut/tests.rs index 065c7ac5a5..bfba43ad75 100644 --- a/nym-api/src/coconut/tests.rs +++ b/nym-api/src/coconut/tests.rs @@ -69,7 +69,7 @@ pub(crate) struct DummyClient { spent_credential_db: Arc>>, epoch: Arc>, - dealer_details: Arc>>, + dealer_details: Arc>>, threshold: Arc>>, dealings: Arc>>>, verification_share: Arc>>, @@ -122,7 +122,7 @@ impl DummyClient { pub fn with_dealer_details( mut self, - dealer_details: &Arc>>, + dealer_details: &Arc>>, ) -> Self { self.dealer_details = Arc::clone(dealer_details); self @@ -233,14 +233,25 @@ impl super::client::Client for DummyClient { } async fn get_self_registered_dealer_details(&self) -> Result { + let (details, dealer_type) = if let Some((details, current)) = self + .dealer_details + .read() + .unwrap() + .get(self.validator_address.as_ref()) + .cloned() + { + let dealer_type = if current { + DealerType::Current + } else { + DealerType::Past + }; + (Some(details), dealer_type) + } else { + (None, DealerType::Unknown) + }; Ok(DealerDetailsResponse { - details: self - .dealer_details - .read() - .unwrap() - .get(self.validator_address.as_ref()) - .cloned(), - dealer_type: DealerType::Current, + details, + dealer_type, }) } @@ -251,6 +262,7 @@ impl super::client::Client for DummyClient { .unwrap() .values() .cloned() + .filter_map(|(d, current)| if current { Some(d) } else { None }) .collect()) } @@ -287,13 +299,11 @@ impl super::client::Client for DummyClient { _fee: Option, ) -> Result<()> { if let Some(proposal) = self.proposal_db.write().unwrap().get_mut(&proposal_id) { - // for now, just suppose that first vote is honest - if proposal.status == cw3::Status::Open { - if vote_yes { - proposal.status = cw3::Status::Passed; - } else { - proposal.status = cw3::Status::Rejected; - } + // for now, just suppose that every vote is honest + if !vote_yes { + proposal.status = cw3::Status::Rejected; + } else if vote_yes && proposal.status == cw3::Status::Open { + proposal.status = cw3::Status::Passed; } } Ok(()) @@ -323,22 +333,33 @@ impl super::client::Client for DummyClient { _resharing: bool, ) -> Result { let mut dealer_details = self.dealer_details.write().unwrap(); - let assigned_index = - if let Some(details) = dealer_details.get(self.validator_address.as_ref()) { - details.assigned_index - } else { - let assigned_index = OsRng.gen(); - dealer_details.insert( - self.validator_address.to_string(), + let assigned_index = if let Some((details, active)) = + dealer_details.get_mut(self.validator_address.as_ref()) + { + *active = true; + details.assigned_index + } else { + // let assigned_index = OsRng.gen(); + let assigned_index = dealer_details + .values() + .map(|(d, _)| d.assigned_index) + .max() + .unwrap_or(0) + + 1; + dealer_details.insert( + self.validator_address.to_string(), + ( DealerDetails { address: Addr::unchecked(self.validator_address.to_string()), bte_public_key_with_proof, announce_address, assigned_index, }, - ); - assigned_index - }; + true, + ), + ); + assigned_index + }; Ok(ExecuteResult { logs: vec![Log { msg_index: 0, @@ -380,13 +401,17 @@ impl super::client::Client for DummyClient { share: VerificationKeyShare, resharing: bool, ) -> Result { - let dealer_details = self + let (dealer_details, active) = self .dealer_details .read() .unwrap() .get(self.validator_address.as_ref()) .unwrap() .clone(); + if !active { + // Just throw some error, not really the correct one + return Err(CoconutError::DepositEncrKeyNotFound); + } self.verification_share.write().unwrap().insert( self.validator_address.to_string(), ContractVKShare { diff --git a/tools/nym-cli/src/validator/mixnet/delegators/mod.rs b/tools/nym-cli/src/validator/mixnet/delegators/mod.rs index af40520255..1a33949e5b 100644 --- a/tools/nym-cli/src/validator/mixnet/delegators/mod.rs +++ b/tools/nym-cli/src/validator/mixnet/delegators/mod.rs @@ -38,7 +38,6 @@ pub(crate) async fn execute( nym_cli_commands::validator::mixnet::delegators::MixnetDelegatorsCommands::List(args) => { nym_cli_commands::validator::mixnet::delegators::query_for_delegations::execute(args, create_signing_client_with_nym_api(global_args, network_details)?).await } - _ => unreachable!(), } Ok(()) } From 1010df1077b4e456b78f8b7d76bdbaf6d6365c83 Mon Sep 17 00:00:00 2001 From: Pierre Dommerc Date: Wed, 15 Mar 2023 12:22:00 +0100 Subject: [PATCH 09/14] refactor(wallet): ui adjustments (#3182) --- .../Accounts/MultiAccountWithPwdHowTo.tsx | 9 +- nym-wallet/src/components/Alert.tsx | 17 ++- nym-wallet/src/components/Bonding/Bond.tsx | 8 +- .../Bonding/forms/GatewayInitForm.tsx | 1 + .../Bonding/forms/MixnodeInitForm.tsx | 1 + nym-wallet/src/components/ClientAddress.tsx | 2 +- .../components/Delegation/DelegateModal.tsx | 10 +- .../components/NetworkSelector.stories.tsx | 20 ++++ nym-wallet/src/components/NetworkSelector.tsx | 31 ++++-- .../src/components/Receive/ReceiveModal.tsx | 1 - .../src/components/Send/SendInputModal.tsx | 4 +- nym-wallet/src/components/Send/SendModal.tsx | 2 +- .../components/TokenPoolSelector.stories.tsx | 24 +++++ .../src/components/TokenPoolSelector.tsx | 43 +++++--- .../general-settings/InfoSettings.tsx | 65 ++++++----- .../general-settings/ParametersSettings.tsx | 101 ++++++++++++------ .../settings-pages/general-settings/index.tsx | 2 +- nym-wallet/src/theme/theme.tsx | 18 ++++ .../components/currency/CurrencyFormField.tsx | 2 +- 19 files changed, 261 insertions(+), 100 deletions(-) create mode 100644 nym-wallet/src/components/NetworkSelector.stories.tsx create mode 100644 nym-wallet/src/components/TokenPoolSelector.stories.tsx diff --git a/nym-wallet/src/components/Accounts/MultiAccountWithPwdHowTo.tsx b/nym-wallet/src/components/Accounts/MultiAccountWithPwdHowTo.tsx index c5cda54150..29c1d84750 100644 --- a/nym-wallet/src/components/Accounts/MultiAccountWithPwdHowTo.tsx +++ b/nym-wallet/src/components/Accounts/MultiAccountWithPwdHowTo.tsx @@ -22,12 +22,12 @@ export const MultiAccountWithPwdHowTo = ({ show, handleClose }: { show: boolean; > - + This machine already has a password set on it - + In order to import or create account(s) you need to log in with your password or create a new one. Creating a - new password will overwrite any old one. Make sure your menonics are all wirtten down before creating a new + new password will overwrite any old one. Make sure your mnemonics are all written down before creating a new password. @@ -35,13 +35,14 @@ export const MultiAccountWithPwdHowTo = ({ show, handleClose }: { show: boolean; {passwordCreationSteps.map((step, index) => ( {`${index + 1}.`} - {`${step}`} + {`${step}`} ))} diff --git a/nym-wallet/src/components/Alert.tsx b/nym-wallet/src/components/Alert.tsx index 38bd2cb989..c583f50338 100644 --- a/nym-wallet/src/components/Alert.tsx +++ b/nym-wallet/src/components/Alert.tsx @@ -1,8 +1,18 @@ import React, { useState } from 'react'; -import { Alert as MuiAlert, IconButton } from '@mui/material'; +import { Alert as MuiAlert, IconButton, SxProps } from '@mui/material'; import { Close } from '@mui/icons-material'; -export const Alert = ({ title, dismissable }: { title: string | React.ReactNode; dismissable?: boolean }) => { +export const Alert = ({ + title, + dismissable, + sxAlert, + bgColor, +}: { + title: string | React.ReactNode; + dismissable?: boolean; + sxAlert?: SxProps; + bgColor?: string; +}) => { const [displayAlert, setDisplayAlert] = useState(true); const handleDismiss = () => setDisplayAlert(false); @@ -14,9 +24,10 @@ export const Alert = ({ title, dismissable }: { title: string | React.ReactNode; sx={{ width: '100%', borderRadius: 0, - bgcolor: 'background.default', + bgcolor: bgColor || 'background.default', color: (theme) => theme.palette.nym.nymWallet.text.blue, '& .MuiAlert-icon': { color: 'nym.nymWallet.text.blue', mr: 1 }, + ...sxAlert, }} action={ dismissable && ( diff --git a/nym-wallet/src/components/Bonding/Bond.tsx b/nym-wallet/src/components/Bonding/Bond.tsx index 1f96da875a..8cf745c6e5 100644 --- a/nym-wallet/src/components/Bonding/Bond.tsx +++ b/nym-wallet/src/components/Bonding/Bond.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { Link } from '@nymproject/react/link/Link'; import { Box, Button, Typography } from '@mui/material'; import { NymCard } from '../NymCard'; @@ -18,7 +19,12 @@ export const Bond = ({ justifyContent: 'space-between', }} > - Bond a mixnode or a gateway + + Bond a mix node or a gateway. Learn how to set up and run a node{' '} + + here + + setValue('identityKey', value)} + showTickOnValid={false} /> setValue('identityKey', value)} + showTickOnValid={false} /> )} - + {showEntireAddress ? address || '' : splice(6, address)} diff --git a/nym-wallet/src/components/Delegation/DelegateModal.tsx b/nym-wallet/src/components/Delegation/DelegateModal.tsx index ad549e206a..61762357de 100644 --- a/nym-wallet/src/components/Delegation/DelegateModal.tsx +++ b/nym-wallet/src/components/Delegation/DelegateModal.tsx @@ -257,6 +257,7 @@ export const DelegateModal: FCWithChildren<{ textFieldProps={{ autoFocus: !initialIdentityKey, }} + showTickOnValid={false} /> - - {errorAmount} - diff --git a/nym-wallet/src/components/NetworkSelector.stories.tsx b/nym-wallet/src/components/NetworkSelector.stories.tsx new file mode 100644 index 0000000000..52cd74c6cc --- /dev/null +++ b/nym-wallet/src/components/NetworkSelector.stories.tsx @@ -0,0 +1,20 @@ +import * as React from 'react'; +import { ComponentMeta, ComponentStory } from '@storybook/react'; +import { Box } from '@mui/material'; +import { MockMainContextProvider } from '../context/mocks/main'; +import { NetworkSelector } from './NetworkSelector'; + +export default { + title: 'Wallet / Network Selector', + component: NetworkSelector, +} as ComponentMeta; + +const Template: ComponentStory = () => ( + + + + + +); + +export const Default = Template.bind({}); diff --git a/nym-wallet/src/components/NetworkSelector.tsx b/nym-wallet/src/components/NetworkSelector.tsx index d17330f679..5df0d7ec74 100644 --- a/nym-wallet/src/components/NetworkSelector.tsx +++ b/nym-wallet/src/components/NetworkSelector.tsx @@ -1,6 +1,6 @@ import React, { useState, useContext } from 'react'; -import { Button, List, ListItem, ListItemIcon, ListItemText, ListSubheader, Popover } from '@mui/material'; -import { ArrowDropDown, CheckSharp } from '@mui/icons-material'; +import { Button, List, ListItemButton, ListItemIcon, ListItemText, ListSubheader, Popover, Stack } from '@mui/material'; +import { ArrowDropDown, Check } from '@mui/icons-material'; import { Network } from 'src/types'; import { AppContext } from '../context/main'; import { config } from '../config'; @@ -16,10 +16,29 @@ const NetworkItem: FCWithChildren<{ title: string; isSelected: boolean; onSelect isSelected, onSelect, }) => ( - - {isSelected && } - {title} - + + + + {isSelected && ( + + + + )} + + ); export const NetworkSelector = () => { diff --git a/nym-wallet/src/components/Receive/ReceiveModal.tsx b/nym-wallet/src/components/Receive/ReceiveModal.tsx index 951ce8ff25..3e4245208a 100644 --- a/nym-wallet/src/components/Receive/ReceiveModal.tsx +++ b/nym-wallet/src/components/Receive/ReceiveModal.tsx @@ -19,7 +19,6 @@ export const ReceiveModal = ({ return ( - Est. fee for this transaction will be show on the next page + Est. fee for this transaction will be shown on the next page onUserFeesChange(v)} initialValue={userFees?.amount} fullWidth diff --git a/nym-wallet/src/components/Send/SendModal.tsx b/nym-wallet/src/components/Send/SendModal.tsx index 650fa4124c..b6d391d0ef 100644 --- a/nym-wallet/src/components/Send/SendModal.tsx +++ b/nym-wallet/src/components/Send/SendModal.tsx @@ -77,7 +77,7 @@ export const SendModal = ({ onClose, hasStorybookStyles }: { onClose: () => void } catch (e) { Console.error(e as string); if (/Raw log: out of gas/.test(e as string)) { - setGasError('Out of gas, please increase the amount of fees'); + setGasError('Specified fee was too small. Please increase the amount and try again'); } else { setSendError(true); } diff --git a/nym-wallet/src/components/TokenPoolSelector.stories.tsx b/nym-wallet/src/components/TokenPoolSelector.stories.tsx new file mode 100644 index 0000000000..1247f5b00d --- /dev/null +++ b/nym-wallet/src/components/TokenPoolSelector.stories.tsx @@ -0,0 +1,24 @@ +import * as React from 'react'; +import { ComponentMeta, ComponentStory } from '@storybook/react'; +import { Box } from '@mui/material'; +import { TokenPoolSelector } from './TokenPoolSelector'; +import { MockMainContextProvider } from '../context/mocks/main'; + +export default { + title: 'Wallet / Token pool', + component: TokenPoolSelector, +} as ComponentMeta; + +const Template: ComponentStory = (args) => ( + + + + + +); + +export const Default = Template.bind({}); +Default.args = { + disabled: false, + onSelect: () => {}, +}; diff --git a/nym-wallet/src/components/TokenPoolSelector.tsx b/nym-wallet/src/components/TokenPoolSelector.tsx index 1ef768bbb3..0f8c5bd7f6 100644 --- a/nym-wallet/src/components/TokenPoolSelector.tsx +++ b/nym-wallet/src/components/TokenPoolSelector.tsx @@ -1,5 +1,15 @@ import React, { useContext, useEffect, useState } from 'react'; -import { FormControl, InputLabel, ListItemText, MenuItem, Select, SelectChangeEvent, Typography } from '@mui/material'; +import { + FormControl, + InputLabel, + ListItemText, + MenuItem, + Select, + SelectChangeEvent, + Stack, + Typography, +} from '@mui/material'; +import { Check as CheckIcon } from '@mui/icons-material'; import { AppContext } from '../context/main'; export type TPoolOption = 'balance' | 'locked'; @@ -40,20 +50,29 @@ export const TokenPoolSelector: FCWithChildren<{ disabled: boolean; onSelect: (p renderValue={(val) => {val}} > - + + + {value === 'balance' && } + {tokenAllocation && ( - + + + {value === 'locked' && } + )} diff --git a/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/InfoSettings.tsx b/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/InfoSettings.tsx index 00a409dab2..5dac5ce04f 100644 --- a/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/InfoSettings.tsx +++ b/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/InfoSettings.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; -import { Box, Button, Divider, Grid, TextField, Typography } from '@mui/material'; +import { Button, Divider, Grid, Stack, TextField, Typography } from '@mui/material'; import { useTheme } from '@mui/material/styles'; import { isMixnode } from 'src/types'; import { simulateUpdateMixnodeConfig, simulateVestingUpdateMixnodeConfig, updateMixnodeConfig } from 'src/requests'; @@ -62,7 +62,7 @@ export const InfoSettings = ({ bondedNode }: { bondedNode: TBondedMixnode | TBon }; return ( - + {fee && ( } - Changing these values will ONLY change the data about your node on the blockchain. Remember to change your - node’s config file with the same values too - + + + Changing these values will ONLY change the data about your node on the blockchain. + + Remember to change your node’s config file with the same values too. + } + bgColor={`${theme.palette.nym.nymWallet.text.blue}0D !important`} dismissable /> - + @@ -126,7 +129,7 @@ export const InfoSettings = ({ bondedNode }: { bondedNode: TBondedMixnode | TBon - + @@ -147,7 +150,7 @@ export const InfoSettings = ({ bondedNode }: { bondedNode: TBondedMixnode | TBon - + @@ -168,31 +171,35 @@ export const InfoSettings = ({ bondedNode }: { bondedNode: TBondedMixnode | TBon - - - + + + + + + { const [openConfirmationModal, setOpenConfirmationModal] = useState(false); @@ -114,10 +131,9 @@ export const ParametersSettings = ({ bondedNode }: { bondedNode: TBondedMixnode @@ -132,13 +148,29 @@ export const ParametersSettings = ({ bondedNode }: { bondedNode: TBondedMixnode /> )} {isSubmitting && } - {`Next interval: ${intervalTime}`}} /> - + {`Next interval: ${intervalTime}`}} + bgColor={`${theme.palette.nym.nymWallet.text.blue}0D !important`} + sxAlert={{ + icon: false as unknown as number, + '& .MuiAlert-message': { + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + }, + }} + /> + - - Profit Margin - + + + + + Profit Margin + + + )} - + - - Operating cost - + + + + + Operator cost + + + { setValue('operatorCost', newValue, { shouldValidate: true, shouldDirty: true }); }} @@ -221,23 +258,27 @@ export const ParametersSettings = ({ bondedNode }: { bondedNode: TBondedMixnode )} - - - + + + + + + { const [settingsCard, setSettingsCard] = useState(nodeGeneralNav[0]); diff --git a/nym-wallet/src/theme/theme.tsx b/nym-wallet/src/theme/theme.tsx index c473dacaf9..8e19eb856a 100644 --- a/nym-wallet/src/theme/theme.tsx +++ b/nym-wallet/src/theme/theme.tsx @@ -303,6 +303,24 @@ export const getDesignTokens = (mode: PaletteMode): ThemeOptions => { }, }, }, + MuiSelect: { + defaultProps: { + MenuProps: { + PaperProps: { + sx: { + '&& .Mui-selected': { + color: nymPalette.highlight, + backgroundColor: (t) => + t.palette.mode === 'dark' ? `${t.palette.background.default} !important` : '#FFFFFF !important', + }, + '&& .Mui-selected:hover': { + backgroundColor: 'rgba(251, 110, 78, 0.08) !important', + }, + }, + }, + }, + }, + }, MuiMenu: { styleOverrides: { list: ({ _, theme }) => ({ diff --git a/ts-packages/react-components/src/components/currency/CurrencyFormField.tsx b/ts-packages/react-components/src/components/currency/CurrencyFormField.tsx index 1adea08132..72646dc488 100644 --- a/ts-packages/react-components/src/components/currency/CurrencyFormField.tsx +++ b/ts-packages/react-components/src/components/currency/CurrencyFormField.tsx @@ -84,7 +84,7 @@ export const CurrencyFormField: FCWithChildren<{ // it can't be lower than one micro coin if (newNumber < MIN_VALUE) { - setValidationError('Amount cannot be less than 1 uNYM'); + setValidationError('Amount cannot be less than 0.000001'); return fireOnValidate(false); } From dd13073037e179411241782b29e614b19d48a089 Mon Sep 17 00:00:00 2001 From: pierre Date: Wed, 15 Mar 2023 12:29:23 +0100 Subject: [PATCH 10/14] docs(connect-android): add build note --- nym-connect/mobile/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nym-connect/mobile/README.md b/nym-connect/mobile/README.md index 1bbc685447..efb522dd81 100644 --- a/nym-connect/mobile/README.md +++ b/nym-connect/mobile/README.md @@ -54,6 +54,8 @@ yarn webpack:prod WRY_ANDROID_PACKAGE=net.nymtech.nym_connect WRY_ANDROID_LIBRARY=nym_connect cargo tauri android build --debug --apk ``` +**NOTE**: Production build without the `--debug` flag requires a signed build. + # Storybook Run storybook with: From 9ec36e49b7ee1d92356503ec051dfe17a3c60ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Thu, 16 Mar 2023 12:28:06 +0000 Subject: [PATCH 11/14] contracts: remove .gitignore with Cargo.lock in it While developing the service-provider-directory contract I ran into issues with the lock file being inconsistent for cosmwasm-std (1.0 vs 1.2) and was hidden due to ignoring the lock file --- contracts/.gitignore | 1 - 1 file changed, 1 deletion(-) delete mode 100644 contracts/.gitignore diff --git a/contracts/.gitignore b/contracts/.gitignore deleted file mode 100644 index ffa3bbd20e..0000000000 --- a/contracts/.gitignore +++ /dev/null @@ -1 +0,0 @@ -Cargo.lock \ No newline at end of file From 4e7ff53214fc500c31d7ecb7eecf4cf753d1f84a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Thu, 16 Mar 2023 13:43:56 +0100 Subject: [PATCH 12/14] Contract and client support for updating gateway config (#3166) * mixnet-contract: add update gateway config * mixnet-contract: tests for updating gateway config * vesting-contract: add update gateway config * validator-client: add update gateway config * wallet: add update_gateway_config * common/commands: add support for setting gateway config * Remove commented out line * Review fixes * Generate ts file for GatewayConfigUpdate type * Add generated GatewayConfigUpdate.ts file --- .../src/nyxd/traits/mixnet_signing_client.rs | 31 ++++ .../src/nyxd/traits/vesting_signing_client.rs | 20 +++ .../validator/mixnet/operators/gateway/mod.rs | 3 + .../mixnet/operators/gateway/settings/mod.rs | 22 +++ .../gateway/settings/update_config.rs | 60 +++++++ .../gateway/settings/vesting_update_config.rs | 61 +++++++ .../mixnet-contract/src/events.rs | 15 ++ .../mixnet-contract/src/gateway.rs | 20 +++ .../mixnet-contract/src/lib.rs | 3 +- .../mixnet-contract/src/msg.rs | 12 ++ .../vesting-contract/src/events.rs | 5 + .../vesting-contract/src/messages.rs | 5 + contracts/mixnet/src/contract.rs | 8 + contracts/mixnet/src/gateways/helpers.rs | 20 +++ contracts/mixnet/src/gateways/mod.rs | 1 + contracts/mixnet/src/gateways/transactions.rs | 150 +++++++++++++++++- contracts/mixnet/src/mixnodes/transactions.rs | 4 +- contracts/mixnet/src/support/tests/mod.rs | 2 +- contracts/vesting/src/contract.rs | 13 ++ .../vesting/src/traits/bonding_account.rs | 7 + .../account/gateway_bonding_account.rs | 23 ++- nym-wallet/src-tauri/src/main.rs | 1 + .../src-tauri/src/operations/mixnet/bond.rs | 26 +++ tools/ts-rs-cli/src/main.rs | 6 +- .../src/types/rust/GatewayConfigUpdate.ts | 9 ++ 25 files changed, 518 insertions(+), 9 deletions(-) create mode 100644 common/commands/src/validator/mixnet/operators/gateway/settings/mod.rs create mode 100644 common/commands/src/validator/mixnet/operators/gateway/settings/update_config.rs create mode 100644 common/commands/src/validator/mixnet/operators/gateway/settings/vesting_update_config.rs create mode 100644 contracts/mixnet/src/gateways/helpers.rs create mode 100644 ts-packages/types/src/types/rust/GatewayConfigUpdate.ts 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 0f49dcc622..159835f5a7 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 @@ -9,6 +9,7 @@ use crate::nyxd::{Fee, NyxdClient, SigningCosmWasmClient}; use async_trait::async_trait; use cosmrs::AccountId; use nym_contracts_common::signing::MessageSignature; +use nym_mixnet_contract_common::gateway::GatewayConfigUpdate; use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams}; use nym_mixnet_contract_common::reward_params::{IntervalRewardingParamsUpdate, Performance}; use nym_mixnet_contract_common::{ @@ -498,6 +499,36 @@ pub trait MixnetSigningClient { .await } + async fn update_gateway_config( + &self, + new_config: GatewayConfigUpdate, + fee: Option, + ) -> Result { + self.execute_mixnet_contract( + fee, + MixnetExecuteMsg::UpdateGatewayConfig { new_config }, + vec![], + ) + .await + } + + async fn update_gateway_config_on_behalf( + &self, + owner: AccountId, + new_config: GatewayConfigUpdate, + fee: Option, + ) -> Result { + self.execute_mixnet_contract( + fee, + MixnetExecuteMsg::UpdateGatewayConfigOnBehalf { + new_config, + owner: owner.to_string(), + }, + vec![], + ) + .await + } + // delegation-related: async fn delegate_to_mixnode( 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 fec9c19c04..296b3219dc 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 @@ -7,6 +7,7 @@ use crate::nyxd::error::NyxdError; use crate::nyxd::{Coin, Fee, NyxdClient}; use async_trait::async_trait; use nym_contracts_common::signing::MessageSignature; +use nym_mixnet_contract_common::gateway::GatewayConfigUpdate; use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams}; use nym_mixnet_contract_common::{Gateway, MixId, MixNode}; use nym_vesting_contract_common::messages::{ @@ -35,6 +36,12 @@ pub trait VestingSigningClient { fee: Option, ) -> Result; + async fn vesting_update_gateway_config( + &self, + new_config: GatewayConfigUpdate, + fee: Option, + ) -> Result; + async fn update_mixnet_address( &self, address: &str, @@ -185,6 +192,19 @@ impl VestingSigningClient for Ny .await } + async fn vesting_update_gateway_config( + &self, + new_config: GatewayConfigUpdate, + fee: Option, + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::UpdateGatewayConfig { new_config }, + vec![], + ) + .await + } + async fn update_mixnet_address( &self, address: &str, diff --git a/common/commands/src/validator/mixnet/operators/gateway/mod.rs b/common/commands/src/validator/mixnet/operators/gateway/mod.rs index 1cdd67520b..8c4b8753c3 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/mod.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/mod.rs @@ -5,6 +5,7 @@ use clap::{Args, Subcommand}; pub mod bond_gateway; pub mod gateway_bonding_sign_payload; +pub mod settings; pub mod unbond_gateway; pub mod vesting_bond_gateway; pub mod vesting_unbond_gateway; @@ -18,6 +19,8 @@ pub struct MixnetOperatorsGateway { #[derive(Debug, Subcommand)] pub enum MixnetOperatorsGatewayCommands { + /// Manage your gateway settings stored in the directory + Settings(settings::MixnetOperatorsGatewaySettings), /// Bond to a gateway Bond(bond_gateway::Args), /// Unbond from a gateway diff --git a/common/commands/src/validator/mixnet/operators/gateway/settings/mod.rs b/common/commands/src/validator/mixnet/operators/gateway/settings/mod.rs new file mode 100644 index 0000000000..d5b96c8ed2 --- /dev/null +++ b/common/commands/src/validator/mixnet/operators/gateway/settings/mod.rs @@ -0,0 +1,22 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use clap::{Args, Subcommand}; + +pub mod update_config; +pub mod vesting_update_config; + +#[derive(Debug, Args)] +#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)] +pub struct MixnetOperatorsGatewaySettings { + #[clap(subcommand)] + pub command: MixnetOperatorsGatewaySettingsCommands, +} + +#[derive(Debug, Subcommand)] +pub enum MixnetOperatorsGatewaySettingsCommands { + /// Update gateway configuration + UpdateConfig(update_config::Args), + /// Update gateway configuration for a gateway bonded with locked tokens + VestingUpdateConfig(vesting_update_config::Args), +} diff --git a/common/commands/src/validator/mixnet/operators/gateway/settings/update_config.rs b/common/commands/src/validator/mixnet/operators/gateway/settings/update_config.rs new file mode 100644 index 0000000000..b88d0c79a6 --- /dev/null +++ b/common/commands/src/validator/mixnet/operators/gateway/settings/update_config.rs @@ -0,0 +1,60 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::context::SigningClient; +use clap::Parser; +use log::info; +use nym_mixnet_contract_common::GatewayConfigUpdate; +use validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; + +#[derive(Debug, Parser)] +pub struct Args { + #[clap(long)] + pub host: Option, + + #[clap(long)] + pub mix_port: Option, + + #[clap(long)] + pub clients_port: Option, + + #[clap(long)] + pub location: Option, + + #[clap(long)] + pub version: Option, +} + +pub async fn update_config(args: Args, client: SigningClient) { + info!("Update gateway config!"); + + let current_details = match client + .get_owned_gateway(client.address()) + .await + .expect("failed to query the chain for gateway details") + .gateway + { + Some(details) => details, + None => { + log::warn!("this operator does not own a gateway to update"); + return; + } + }; + + let update = GatewayConfigUpdate { + host: args.host.unwrap_or(current_details.gateway.host), + mix_port: args.mix_port.unwrap_or(current_details.gateway.mix_port), + clients_port: args + .clients_port + .unwrap_or(current_details.gateway.clients_port), + location: args.location.unwrap_or(current_details.gateway.location), + version: args.version.unwrap_or(current_details.gateway.version), + }; + + let res = client + .update_gateway_config(update, None) + .await + .expect("updating gateway config"); + + info!("gateway config updated: {:?}", res) +} diff --git a/common/commands/src/validator/mixnet/operators/gateway/settings/vesting_update_config.rs b/common/commands/src/validator/mixnet/operators/gateway/settings/vesting_update_config.rs new file mode 100644 index 0000000000..50c25b301d --- /dev/null +++ b/common/commands/src/validator/mixnet/operators/gateway/settings/vesting_update_config.rs @@ -0,0 +1,61 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::context::SigningClient; +use clap::Parser; +use log::info; +use nym_mixnet_contract_common::GatewayConfigUpdate; +use validator_client::nyxd::traits::MixnetQueryClient; +use validator_client::nyxd::VestingSigningClient; + +#[derive(Debug, Parser)] +pub struct Args { + #[clap(long)] + pub host: Option, + + #[clap(long)] + pub mix_port: Option, + + #[clap(long)] + pub clients_port: Option, + + #[clap(long)] + pub location: Option, + + #[clap(long)] + pub version: Option, +} + +pub async fn vesting_update_config(client: SigningClient, args: Args) { + info!("Update vesting gateway config!"); + + let current_details = match client + .get_owned_gateway(client.address()) + .await + .expect("failed to query the chain for gateway details") + .gateway + { + Some(details) => details, + None => { + log::warn!("this operator does not own a gateway to update"); + return; + } + }; + + let update = GatewayConfigUpdate { + host: args.host.unwrap_or(current_details.gateway.host), + mix_port: args.mix_port.unwrap_or(current_details.gateway.mix_port), + clients_port: args + .clients_port + .unwrap_or(current_details.gateway.clients_port), + location: args.location.unwrap_or(current_details.gateway.location), + version: args.version.unwrap_or(current_details.gateway.version), + }; + + let res = client + .vesting_update_gateway_config(update, None) + .await + .expect("updating vesting gateway config"); + + info!("gateway config updated: {:?}", res) +} diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs index 98ecb8a1b7..e61e26adb2 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs @@ -1,6 +1,7 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::gateway::GatewayConfigUpdate; use crate::mixnode::{MixNodeConfigUpdate, MixNodeCostParams}; use crate::reward_params::{IntervalRewardParams, IntervalRewardingParamsUpdate}; use crate::rewarding::RewardDistribution; @@ -42,6 +43,7 @@ pub enum MixnetEventType { ReconcilePendingEvents, PendingIntervalConfigUpdate, IntervalConfigUpdate, + GatewayConfigUpdate, } impl From for String { @@ -86,6 +88,7 @@ impl ToString for MixnetEventType { MixnetEventType::PendingIntervalConfigUpdate => "pending_interval_config_update", MixnetEventType::IntervalConfigUpdate => "interval_config_update", MixnetEventType::DelegationOnUnbonding => "delegation_on_unbonding_node", + MixnetEventType::GatewayConfigUpdate => "gateway_config_update", }; format!("{EVENT_VERSION_PREFIX}{event_name}") @@ -122,6 +125,7 @@ pub const OLD_REWARDING_VALIDATOR_ADDRESS_KEY: &str = "old_rewarding_validator_a pub const NEW_REWARDING_VALIDATOR_ADDRESS_KEY: &str = "new_rewarding_validator_address"; pub const UPDATED_MIXNODE_CONFIG_KEY: &str = "updated_mixnode_config"; +pub const UPDATED_GATEWAY_CONFIG_KEY: &str = "updated_gateway_config"; pub const UPDATED_MIXNODE_COST_PARAMS_KEY: &str = "updated_mixnode_cost_params"; // rewarding @@ -382,6 +386,17 @@ pub fn new_mixnode_config_update_event( .add_attribute(UPDATED_MIXNODE_CONFIG_KEY, update.to_inline_json()) } +pub fn new_gateway_config_update_event( + owner: &Addr, + proxy: &Option, + update: &GatewayConfigUpdate, +) -> Event { + Event::new(MixnetEventType::GatewayConfigUpdate) + .add_attribute(OWNER_KEY, owner) + .add_optional_attribute(PROXY_KEY, proxy.as_ref()) + .add_attribute(UPDATED_GATEWAY_CONFIG_KEY, update.to_inline_json()) +} + pub fn new_mixnode_pending_cost_params_update_event( mix_id: MixId, owner: &Addr, diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs index ebb5464064..3ba030a5b3 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs @@ -112,6 +112,26 @@ impl Display for GatewayBond { } } +#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))] +#[cfg_attr( + feature = "generate-ts", + ts(export_to = "ts-packages/types/src/types/rust/GatewayConfigUpdate.ts") +)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +pub struct GatewayConfigUpdate { + pub host: String, + pub mix_port: u16, + pub clients_port: u16, + pub location: String, + pub version: String, +} + +impl GatewayConfigUpdate { + pub fn to_inline_json(&self) -> String { + serde_json::to_string(self).unwrap_or_else(|_| "serialisation failure".into()) + } +} + #[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] pub struct PagedGatewayResponse { pub nodes: Vec, diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs index 738aeb6daa..3079d03640 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs @@ -27,7 +27,8 @@ pub use delegation::{ PagedMixNodeDelegationsResponse, }; pub use gateway::{ - Gateway, GatewayBond, GatewayBondResponse, GatewayOwnershipResponse, PagedGatewayResponse, + Gateway, GatewayBond, GatewayBondResponse, GatewayConfigUpdate, GatewayOwnershipResponse, + PagedGatewayResponse, }; pub use interval::{ CurrentIntervalResponse, EpochState, EpochStatus, Interval, NumberOfPendingEventsResponse, diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs index 8376259bfc..83fa00be2f 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs @@ -3,6 +3,7 @@ use crate::delegation::OwnerProxySubKey; use crate::error::MixnetContractError; +use crate::gateway::GatewayConfigUpdate; use crate::helpers::IntoBaseDecimal; use crate::mixnode::{MixNodeConfigUpdate, MixNodeCostParams}; use crate::reward_params::{ @@ -199,6 +200,13 @@ pub enum ExecuteMsg { UnbondGatewayOnBehalf { owner: String, }, + UpdateGatewayConfig { + new_config: GatewayConfigUpdate, + }, + UpdateGatewayConfigOnBehalf { + new_config: GatewayConfigUpdate, + owner: String, + }, // delegation-related: DelegateToMixnode { @@ -313,6 +321,10 @@ impl ExecuteMsg { } ExecuteMsg::UnbondGateway { .. } => "unbonding gateway".into(), ExecuteMsg::UnbondGatewayOnBehalf { .. } => "unbonding gateway on behalf".into(), + ExecuteMsg::UpdateGatewayConfig { .. } => "updating gateway configuration".into(), + ExecuteMsg::UpdateGatewayConfigOnBehalf { .. } => { + "updating gateway configuration on behalf".into() + } ExecuteMsg::DelegateToMixnode { mix_id } => format!("delegating to mixnode {mix_id}"), ExecuteMsg::DelegateToMixnodeOnBehalf { mix_id, .. } => { format!("delegating to mixnode {mix_id} on behalf") diff --git a/common/cosmwasm-smart-contracts/vesting-contract/src/events.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/events.rs index 4e5809beee..e46cd67159 100644 --- a/common/cosmwasm-smart-contracts/vesting-contract/src/events.rs +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/events.rs @@ -17,6 +17,7 @@ pub const VESTING_MIXNODE_BONDING_EVENT_TYPE: &str = "vesting_mixnode_bonding"; pub const VESTING_PLEDGE_MORE_EVENT_TYPE: &str = "vesting_pledge_more"; pub const VESTING_MIXNODE_UNBONDING_EVENT_TYPE: &str = "vesting_mixnode_unbonding"; pub const VESTING_UPDATE_MIXNODE_CONFIG_EVENT_TYPE: &str = "vesting_update_mixnode_config"; +pub const VESTING_UPDATE_GATEWAY_CONFIG_EVENT_TYPE: &str = "vesting_update_gateway_config"; pub const VESTING_UPDATE_MIXNODE_COST_PARAMS_EVENT_TYPE: &str = "vesting_update_mixnode_cost_params"; @@ -121,6 +122,10 @@ pub fn new_vesting_update_mixnode_config_event() -> Event { Event::new(VESTING_UPDATE_MIXNODE_CONFIG_EVENT_TYPE) } +pub fn new_vesting_update_gateway_config_event() -> Event { + Event::new(VESTING_UPDATE_GATEWAY_CONFIG_EVENT_TYPE) +} + pub fn new_vesting_update_mixnode_cost_params_event() -> Event { Event::new(VESTING_UPDATE_MIXNODE_COST_PARAMS_EVENT_TYPE) } diff --git a/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs index 39b5e74187..d55e9a0fa7 100644 --- a/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs @@ -1,6 +1,7 @@ use contracts_common::signing::MessageSignature; use cosmwasm_std::{Coin, Timestamp}; use mixnet_contract_common::{ + gateway::GatewayConfigUpdate, mixnode::{MixNodeConfigUpdate, MixNodeCostParams}, Gateway, IdentityKey, MixId, MixNode, }; @@ -140,6 +141,9 @@ pub enum ExecuteMsg { owner: String, amount: Coin, }, + UpdateGatewayConfig { + new_config: GatewayConfigUpdate, + }, TransferOwnership { to_address: String, }, @@ -179,6 +183,7 @@ impl ExecuteMsg { ExecuteMsg::BondGateway { .. } => "VestingExecuteMsg::BondGateway", ExecuteMsg::UnbondGateway { .. } => "VestingExecuteMsg::UnbondGateway", ExecuteMsg::TrackUnbondGateway { .. } => "VestingExecuteMsg::TrackUnbondGateway", + ExecuteMsg::UpdateGatewayConfig { .. } => "VestingExecuteMsg::UpdateGatewayConfig", ExecuteMsg::TransferOwnership { .. } => "VestingExecuteMsg::TransferOwnership", ExecuteMsg::UpdateStakingAddress { .. } => "VestingExecuteMsg::UpdateStakingAddress", ExecuteMsg::UpdateLockedPledgeCap { .. } => "VestingExecuteMsg::UpdateLockedPledgeCap", diff --git a/contracts/mixnet/src/contract.rs b/contracts/mixnet/src/contract.rs index 623e9c0a1c..181f70a82f 100644 --- a/contracts/mixnet/src/contract.rs +++ b/contracts/mixnet/src/contract.rs @@ -318,6 +318,14 @@ pub fn execute( ExecuteMsg::UnbondGatewayOnBehalf { owner } => { crate::gateways::transactions::try_remove_gateway_on_behalf(deps, info, owner) } + ExecuteMsg::UpdateGatewayConfig { new_config } => { + crate::gateways::transactions::try_update_gateway_config(deps, info, new_config) + } + ExecuteMsg::UpdateGatewayConfigOnBehalf { new_config, owner } => { + crate::gateways::transactions::try_update_gateway_config_on_behalf( + deps, info, new_config, owner, + ) + } // delegation-related: ExecuteMsg::DelegateToMixnode { mix_id } => { diff --git a/contracts/mixnet/src/gateways/helpers.rs b/contracts/mixnet/src/gateways/helpers.rs new file mode 100644 index 0000000000..8a450bd3cd --- /dev/null +++ b/contracts/mixnet/src/gateways/helpers.rs @@ -0,0 +1,20 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use super::storage; +use cosmwasm_std::{Addr, Storage}; +use mixnet_contract_common::{error::MixnetContractError, GatewayBond}; + +pub(crate) fn must_get_gateway_bond_by_owner( + store: &dyn Storage, + owner: &Addr, +) -> Result { + Ok(storage::gateways() + .idx + .owner + .item(store, owner.clone())? + .ok_or(MixnetContractError::NoAssociatedGatewayBond { + owner: owner.clone(), + })? + .1) +} diff --git a/contracts/mixnet/src/gateways/mod.rs b/contracts/mixnet/src/gateways/mod.rs index d76ed62ced..ebff4a6ace 100644 --- a/contracts/mixnet/src/gateways/mod.rs +++ b/contracts/mixnet/src/gateways/mod.rs @@ -1,6 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +pub mod helpers; pub mod queries; pub mod signature_helpers; pub mod storage; diff --git a/contracts/mixnet/src/gateways/transactions.rs b/contracts/mixnet/src/gateways/transactions.rs index dc32d6ce4d..193fe99252 100644 --- a/contracts/mixnet/src/gateways/transactions.rs +++ b/contracts/mixnet/src/gateways/transactions.rs @@ -1,16 +1,20 @@ // Copyright 2021-2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use super::helpers::must_get_gateway_bond_by_owner; use super::storage; use crate::gateways::signature_helpers::verify_gateway_bonding_signature; use crate::mixnet_contract_settings::storage as mixnet_params_storage; use crate::signing::storage as signing_storage; use crate::support::helpers::{ - ensure_no_existing_bond, ensure_sent_by_vesting_contract, validate_pledge, + ensure_no_existing_bond, ensure_proxy_match, ensure_sent_by_vesting_contract, validate_pledge, }; use cosmwasm_std::{wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response}; use mixnet_contract_common::error::MixnetContractError; -use mixnet_contract_common::events::{new_gateway_bonding_event, new_gateway_unbonding_event}; +use mixnet_contract_common::events::{ + new_gateway_bonding_event, new_gateway_config_update_event, new_gateway_unbonding_event, +}; +use mixnet_contract_common::gateway::GatewayConfigUpdate; use mixnet_contract_common::{Gateway, GatewayBond}; use nym_contracts_common::signing::MessageSignature; use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg; @@ -189,6 +193,56 @@ pub(crate) fn _try_remove_gateway( ))) } +pub(crate) fn try_update_gateway_config( + deps: DepsMut<'_>, + info: MessageInfo, + new_config: GatewayConfigUpdate, +) -> Result { + let owner = info.sender; + _try_update_gateway_config(deps, new_config, owner, None) +} + +pub(crate) fn try_update_gateway_config_on_behalf( + deps: DepsMut, + info: MessageInfo, + new_config: GatewayConfigUpdate, + owner: String, +) -> Result { + ensure_sent_by_vesting_contract(&info, deps.storage)?; + + let owner = deps.api.addr_validate(&owner)?; + let proxy = info.sender; + _try_update_gateway_config(deps, new_config, owner, Some(proxy)) +} + +pub(crate) fn _try_update_gateway_config( + deps: DepsMut, + new_config: GatewayConfigUpdate, + owner: Addr, + proxy: Option, +) -> Result { + let existing_bond = must_get_gateway_bond_by_owner(deps.storage, &owner)?; + ensure_proxy_match(&proxy, &existing_bond.proxy)?; + + let cfg_update_event = new_gateway_config_update_event(&owner, &proxy, &new_config); + + let mut updated_bond = existing_bond.clone(); + updated_bond.gateway.host = new_config.host; + updated_bond.gateway.mix_port = new_config.mix_port; + updated_bond.gateway.clients_port = new_config.clients_port; + updated_bond.gateway.location = new_config.location; + updated_bond.gateway.version = new_config.version; + + storage::gateways().replace( + deps.storage, + existing_bond.identity(), + Some(&updated_bond), + Some(&existing_bond), + )?; + + Ok(Response::new().add_event(cfg_update_event)) +} + #[cfg(test)] pub mod tests { use super::*; @@ -196,6 +250,7 @@ pub mod tests { use crate::gateways::queries; use crate::gateways::transactions::{ try_add_gateway, try_add_gateway_on_behalf, try_remove_gateway_on_behalf, + try_update_gateway_config, try_update_gateway_config_on_behalf, }; use crate::interval::pending_events; use crate::mixnet_contract_settings::storage::minimum_gateway_pledge; @@ -207,6 +262,7 @@ pub mod tests { use cosmwasm_std::{Addr, BankMsg, Response, Uint128}; use mixnet_contract_common::error::MixnetContractError; use mixnet_contract_common::events::new_gateway_unbonding_event; + use mixnet_contract_common::gateway::GatewayConfigUpdate; use mixnet_contract_common::ExecuteMsg; #[test] @@ -485,4 +541,94 @@ pub mod tests { } ) } + + #[test] + fn update_gateway_config() { + let mut test = TestSetup::new(); + + let owner = "alice"; + let info = mock_info(owner, &[]); + let update = GatewayConfigUpdate { + host: "1.1.1.1:1234".to_string(), + mix_port: 1234, + clients_port: 1235, + location: "home".to_string(), + version: "v1.2.3".to_string(), + }; + + // try updating a non existing gateway bond + let res = try_update_gateway_config(test.deps_mut(), info.clone(), update.clone()); + assert_eq!( + res, + Err(MixnetContractError::NoAssociatedGatewayBond { + owner: Addr::unchecked(owner) + }) + ); + + test.add_dummy_gateway(owner, None); + let vesting_contract = test.vesting_contract(); + + // attempted to remove on behalf with invalid proxy (current is `None`) + let res = try_update_gateway_config_on_behalf( + test.deps_mut(), + mock_info(vesting_contract.as_ref(), &[]), + update.clone(), + owner.to_string(), + ); + assert_eq!( + res, + Err(MixnetContractError::ProxyMismatch { + existing: "None".to_string(), + incoming: vesting_contract.into_string() + }) + ); + + // "normal" update succeeds + let res = try_update_gateway_config(test.deps_mut(), info, update.clone()); + assert!(res.is_ok()); + + // and the config has actually been updated + let bond = + must_get_gateway_bond_by_owner(test.deps().storage, &Addr::unchecked(owner)).unwrap(); + assert_eq!(bond.gateway.host, update.host); + assert_eq!(bond.gateway.mix_port, update.mix_port); + assert_eq!(bond.gateway.clients_port, update.clients_port); + assert_eq!(bond.gateway.location, update.location); + assert_eq!(bond.gateway.version, update.version); + } + + #[test] + fn updating_gateway_config_with_illegal_proxy() { + let mut test = TestSetup::new(); + + let illegal_proxy = Addr::unchecked("not-vesting-contract"); + let vesting_contract = test.vesting_contract(); + + let owner = "alice"; + + test.add_dummy_gateway_with_illegal_proxy(owner, None, illegal_proxy.clone()); + let update = GatewayConfigUpdate { + host: "1.1.1.1:1234".to_string(), + mix_port: 1234, + clients_port: 1235, + location: "at home".to_string(), + version: "v1.2.3".to_string(), + }; + + let res = try_update_gateway_config_on_behalf( + test.deps_mut(), + mock_info(illegal_proxy.as_ref(), &[]), + update, + owner.to_string(), + ) + .unwrap_err(); + + assert_eq!( + res, + MixnetContractError::SenderIsNotVestingContract { + received: illegal_proxy, + vesting_contract + } + ) + } } diff --git a/contracts/mixnet/src/mixnodes/transactions.rs b/contracts/mixnet/src/mixnodes/transactions.rs index 6ce2416851..f2bfa89941 100644 --- a/contracts/mixnet/src/mixnodes/transactions.rs +++ b/contracts/mixnet/src/mixnodes/transactions.rs @@ -295,7 +295,7 @@ pub(crate) fn try_update_mixnode_config( } pub(crate) fn try_update_mixnode_config_on_behalf( - deps: DepsMut, + deps: DepsMut<'_>, info: MessageInfo, new_config: MixNodeConfigUpdate, owner: String, @@ -308,7 +308,7 @@ pub(crate) fn try_update_mixnode_config_on_behalf( } pub(crate) fn _try_update_mixnode_config( - deps: DepsMut, + deps: DepsMut<'_>, new_config: MixNodeConfigUpdate, owner: Addr, proxy: Option, diff --git a/contracts/mixnet/src/support/tests/mod.rs b/contracts/mixnet/src/support/tests/mod.rs index 71a2d96437..e693b6b1f4 100644 --- a/contracts/mixnet/src/support/tests/mod.rs +++ b/contracts/mixnet/src/support/tests/mod.rs @@ -20,7 +20,7 @@ pub mod test_helpers { perform_pending_epoch_actions, perform_pending_interval_actions, try_begin_epoch_transition, }; use crate::interval::{pending_events, storage as interval_storage}; - use crate::mixnet_contract_settings::storage as mixnet_params_storage; + use crate::mixnet_contract_settings::storage::{self as mixnet_params_storage}; use crate::mixnet_contract_settings::storage::{ minimum_gateway_pledge, minimum_mixnode_pledge, rewarding_denom, rewarding_validator_address, diff --git a/contracts/vesting/src/contract.rs b/contracts/vesting/src/contract.rs index cc6e59aa29..9454a2c34e 100644 --- a/contracts/vesting/src/contract.rs +++ b/contracts/vesting/src/contract.rs @@ -14,6 +14,7 @@ use cosmwasm_std::{ QueryResponse, Response, StdError, StdResult, Timestamp, Uint128, }; use cw_storage_plus::Bound; +use mixnet_contract_common::gateway::GatewayConfigUpdate; use mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams}; use mixnet_contract_common::{Gateway, MixId, MixNode}; use semver::Version; @@ -219,6 +220,9 @@ pub fn execute( ExecuteMsg::TrackUnbondGateway { owner, amount } => { try_track_unbond_gateway(&owner, amount, info, deps) } + ExecuteMsg::UpdateGatewayConfig { new_config } => { + try_update_gateway_config(new_config, info, deps) + } ExecuteMsg::TransferOwnership { to_address } => { try_transfer_ownership(to_address, info, deps) } @@ -300,6 +304,15 @@ pub fn try_update_mixnode_config( account.try_update_mixnode_config(new_config, deps.storage) } +pub fn try_update_gateway_config( + new_config: GatewayConfigUpdate, + info: MessageInfo, + deps: DepsMut, +) -> Result { + let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; + account.try_update_gateway_config(new_config, deps.storage) +} + pub fn try_update_mixnode_cost_params( new_costs: MixNodeCostParams, info: MessageInfo, diff --git a/contracts/vesting/src/traits/bonding_account.rs b/contracts/vesting/src/traits/bonding_account.rs index 242620a3be..402b156924 100644 --- a/contracts/vesting/src/traits/bonding_account.rs +++ b/contracts/vesting/src/traits/bonding_account.rs @@ -2,6 +2,7 @@ use crate::errors::ContractError; use contracts_common::signing::MessageSignature; use cosmwasm_std::{Coin, Env, Response, Storage}; use mixnet_contract_common::{ + gateway::GatewayConfigUpdate, mixnode::{MixNodeConfigUpdate, MixNodeCostParams}, Gateway, MixNode, }; @@ -64,4 +65,10 @@ pub trait GatewayBondingAccount { amount: Coin, storage: &mut dyn Storage, ) -> Result<(), ContractError>; + + fn try_update_gateway_config( + &self, + new_config: GatewayConfigUpdate, + storage: &mut dyn Storage, + ) -> Result; } diff --git a/contracts/vesting/src/vesting/account/gateway_bonding_account.rs b/contracts/vesting/src/vesting/account/gateway_bonding_account.rs index 72f83405b3..12910acbcc 100644 --- a/contracts/vesting/src/vesting/account/gateway_bonding_account.rs +++ b/contracts/vesting/src/vesting/account/gateway_bonding_account.rs @@ -4,9 +4,12 @@ use crate::storage::MIXNET_CONTRACT_ADDRESS; use crate::traits::GatewayBondingAccount; use contracts_common::signing::MessageSignature; use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128}; -use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, Gateway}; +use mixnet_contract_common::{ + gateway::GatewayConfigUpdate, ExecuteMsg as MixnetExecuteMsg, Gateway, +}; use vesting_contract_common::events::{ new_vesting_gateway_bonding_event, new_vesting_gateway_unbonding_event, + new_vesting_update_gateway_config_event, }; use super::Account; @@ -78,4 +81,22 @@ impl GatewayBondingAccount for Account { self.remove_gateway_pledge(storage)?; Ok(()) } + + fn try_update_gateway_config( + &self, + new_config: GatewayConfigUpdate, + storage: &mut dyn Storage, + ) -> Result { + let msg = MixnetExecuteMsg::UpdateGatewayConfigOnBehalf { + new_config, + owner: self.owner_address().into_string(), + }; + + let update_gateway_config_msg = + wasm_execute(MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, vec![])?; + + Ok(Response::new() + .add_message(update_gateway_config_msg) + .add_event(new_vesting_update_gateway_config_event())) + } } diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index cda784a8b7..0493bc1493 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -63,6 +63,7 @@ fn main() { mixnet::bond::unbond_mixnode, mixnet::bond::update_mixnode_cost_params, mixnet::bond::update_mixnode_config, + mixnet::bond::update_gateway_config, mixnet::bond::get_number_of_mixnode_delegators, mixnet::bond::get_mix_node_description, mixnet::bond::get_mixnode_avg_uptime, diff --git a/nym-wallet/src-tauri/src/operations/mixnet/bond.rs b/nym-wallet/src-tauri/src/operations/mixnet/bond.rs index 9884c88080..fcfdf9868d 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/bond.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/bond.rs @@ -8,6 +8,7 @@ use crate::operations::helpers::{ use crate::state::WalletState; use crate::{nyxd_client, Gateway, MixNode}; use nym_contracts_common::signing::MessageSignature; +use nym_mixnet_contract_common::gateway::GatewayConfigUpdate; use nym_mixnet_contract_common::{MixId, MixNodeConfigUpdate}; use nym_types::currency::DecCoin; use nym_types::gateway::GatewayBond; @@ -227,6 +228,31 @@ pub async fn update_mixnode_config( )?) } +#[tauri::command] +pub async fn update_gateway_config( + update: GatewayConfigUpdate, + fee: Option, + state: tauri::State<'_, WalletState>, +) -> Result { + let guard = state.read().await; + let fee_amount = guard.convert_tx_fee(fee.as_ref()); + log::info!( + ">>> Update gateway config: update = {}, fee {:?}", + update.to_inline_json(), + fee, + ); + let res = guard + .current_client()? + .nyxd + .update_gateway_config(update, fee) + .await?; + log::info!("<<< tx hash = {}", res.transaction_hash); + log::trace!("<<< {:?}", res); + Ok(TransactionExecuteResult::from_execute_result( + res, fee_amount, + )?) +} + #[tauri::command] pub async fn get_mixnode_avg_uptime( state: tauri::State<'_, WalletState>, diff --git a/tools/ts-rs-cli/src/main.rs b/tools/ts-rs-cli/src/main.rs index 9b6ea3f2c2..8bfdf074eb 100644 --- a/tools/ts-rs-cli/src/main.rs +++ b/tools/ts-rs-cli/src/main.rs @@ -5,8 +5,9 @@ use nym_api_requests::models::{ }; use nym_mixnet_contract_common::rewarding::RewardEstimate; use nym_mixnet_contract_common::{ - Interval as ContractInterval, IntervalRewardParams, IntervalRewardingParamsUpdate, MixNode, - MixNodeConfigUpdate, RewardedSetNodeStatus, RewardingParams, UnbondedMixnode, + GatewayConfigUpdate, Interval as ContractInterval, IntervalRewardParams, + IntervalRewardingParamsUpdate, MixNode, MixNodeConfigUpdate, RewardedSetNodeStatus, + RewardingParams, UnbondedMixnode, }; use nym_types::account::{Account, AccountEntry, AccountWithMnemonic, Balance}; use nym_types::currency::{CurrencyDenom, DecCoin}; @@ -90,6 +91,7 @@ fn main() { do_export!(Gas); do_export!(GasInfo); do_export!(Gateway); + do_export!(GatewayConfigUpdate); do_export!(GatewayBond); do_export!(CurrencyDenom); do_export!(DecCoin); diff --git a/ts-packages/types/src/types/rust/GatewayConfigUpdate.ts b/ts-packages/types/src/types/rust/GatewayConfigUpdate.ts new file mode 100644 index 0000000000..acd40d4421 --- /dev/null +++ b/ts-packages/types/src/types/rust/GatewayConfigUpdate.ts @@ -0,0 +1,9 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export interface GatewayConfigUpdate { + host: string; + mix_port: number; + clients_port: number; + location: string; + version: string; +} From 2b80e5d1c9cd8689b56bcff14bb42022306a5e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Thu, 16 Mar 2023 12:59:46 +0000 Subject: [PATCH 13/14] Remove leftover file from deleted crate --- common/build-information/Cargo.toml | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 common/build-information/Cargo.toml diff --git a/common/build-information/Cargo.toml b/common/build-information/Cargo.toml deleted file mode 100644 index e69de29bb2..0000000000 From 4c2967a733e914b3d3acc3742c19f8b5763d870c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Thu, 16 Mar 2023 15:27:45 +0000 Subject: [PATCH 14/14] Delete stray .gitignore file --- common/bin-common/.gitignore | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 common/bin-common/.gitignore diff --git a/common/bin-common/.gitignore b/common/bin-common/.gitignore deleted file mode 100644 index 693699042b..0000000000 --- a/common/bin-common/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -**/*.rs.bk -Cargo.lock