diff --git a/Cargo.lock b/Cargo.lock index cd20c29c44..611108642d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -664,8 +664,8 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc347c19eb5b940f396ac155822caee6662f850d97306890ac3773ed76c90c5a" dependencies = [ - "prost", - "prost-types", + "prost 0.9.0", + "prost-types 0.9.0", "tonic", "tonic-build", "tracing-core", @@ -683,7 +683,7 @@ dependencies = [ "futures", "hdrhistogram", "humantime 2.1.0", - "prost-types", + "prost-types 0.9.0", "serde", "serde_json", "thread_local", @@ -761,60 +761,29 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "cosmos-sdk-proto" -version = "0.9.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0254ffee603f5301d6a66963d9e1cc5091479c22e2e925e1f7689c8027a0828" +checksum = "f109fe191e73898d74b8020c50f86018364ad19bc30318aa074616c382b52856" dependencies = [ - "prost", - "prost-types", - "tendermint-proto", -] - -[[package]] -name = "cosmos-sdk-proto" -version = "0.9.0" -source = "git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation#911fbe1236cfed591783ccef01018f7ccc97c496" -dependencies = [ - "prost", - "prost-types", + "prost 0.10.3", + "prost-types 0.10.1", "tendermint-proto", ] [[package]] name = "cosmrs" -version = "0.4.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "505ea048e9ff2f906d6b954f9f8157d903ca468bfb301d906b40ecc25ba6838d" +checksum = "8413275b23cb5a0734d9d1e3e33f0b5b94547c1e94776dbc3149dbf46588a533" dependencies = [ "bip32", - "cosmos-sdk-proto 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cosmos-sdk-proto", "ecdsa 0.13.4", "eyre", "getrandom 0.2.6", "k256 0.10.4", - "prost", - "prost-types", - "rand_core 0.6.3", - "serde", - "serde_json", - "subtle-encoding", - "tendermint", - "thiserror", -] - -[[package]] -name = "cosmrs" -version = "0.4.1" -source = "git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation#911fbe1236cfed591783ccef01018f7ccc97c496" -dependencies = [ - "bip32", - "cosmos-sdk-proto 0.9.0 (git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation)", - "ecdsa 0.13.4", - "eyre", - "getrandom 0.2.6", - "k256 0.10.4", - "prost", - "prost-types", + "prost 0.10.3", + "prost-types 0.10.1", "rand_core 0.6.3", "serde", "serde_json", @@ -938,7 +907,7 @@ version = "0.1.0" dependencies = [ "bls12_381 0.5.0", "coconut-interface", - "cosmrs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cosmrs", "crypto", "network-defaults", "rand 0.7.3", @@ -1181,6 +1150,7 @@ dependencies = [ "byteorder", "digest 0.9.0", "rand_core 0.5.1", + "serde", "subtle 2.4.1", "zeroize", ] @@ -2384,11 +2354,12 @@ dependencies = [ "headers", "http", "hyper", - "hyper-tls", - "native-tls", + "hyper-rustls", + "rustls-native-certs", "tokio", - "tokio-native-tls", + "tokio-rustls", "tower-service", + "webpki", ] [[package]] @@ -3937,7 +3908,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.9.0", +] + +[[package]] +name = "prost" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc03e116981ff7d8da8e5c220e374587b98d294af7ba7dd7fda761158f00086f" +dependencies = [ + "bytes", + "prost-derive 0.10.1", ] [[package]] @@ -3953,8 +3934,8 @@ dependencies = [ "log", "multimap", "petgraph", - "prost", - "prost-types", + "prost 0.9.0", + "prost-types 0.9.0", "regex", "tempfile", "which", @@ -3973,6 +3954,19 @@ dependencies = [ "syn", ] +[[package]] +name = "prost-derive" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b670f45da57fb8542ebdbb6105a925fe571b67f9e7ed9f47a06a84e72b4e7cc" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "prost-types" version = "0.9.0" @@ -3980,7 +3974,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a" dependencies = [ "bytes", - "prost", + "prost 0.9.0", +] + +[[package]] +name = "prost-types" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d0a014229361011dc8e69c8a1ec6c2e8d0f2af7c91e3ea3f5b2170298461e68" +dependencies = [ + "bytes", + "prost 0.10.3", ] [[package]] @@ -5464,9 +5468,9 @@ dependencies = [ [[package]] name = "tendermint" -version = "0.23.3" +version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9ef686b8ecd36550d0581f0989c9d8607090b23005df479d8e6ba348b5800b9" +checksum = "3ca881fa4dedd2b46334f13be7fbc8cc1549ba4be5a833fe4e73d1a1baaf7949" dependencies = [ "async-trait", "bytes", @@ -5477,8 +5481,8 @@ dependencies = [ "k256 0.10.4", "num-traits", "once_cell", - "prost", - "prost-types", + "prost 0.10.3", + "prost-types 0.10.1", "ripemd160", "serde", "serde_bytes", @@ -5495,9 +5499,9 @@ dependencies = [ [[package]] name = "tendermint-config" -version = "0.23.3" +version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc127f82e7a8c7337c1f293d65a821380d3407c4c44bc979ef4da0ebc3b31ed" +checksum = "f6c56ee93f4e9b7e7daba86d171f44572e91b741084384d0ae00df7991873dfd" dependencies = [ "flex-error", "serde", @@ -5509,16 +5513,16 @@ dependencies = [ [[package]] name = "tendermint-proto" -version = "0.23.3" +version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e0a0251fd81bed7420bea0f4d91c2a58f6c9fa34d5b2f70bed0ba8890de5aa" +checksum = "b71f925d74903f4abbdc4af0110635a307b3cb05b175fdff4a7247c14a4d0874" dependencies = [ "bytes", "flex-error", "num-derive", "num-traits", - "prost", - "prost-types", + "prost 0.10.3", + "prost-types 0.10.1", "serde", "serde_bytes", "subtle-encoding", @@ -5527,9 +5531,9 @@ dependencies = [ [[package]] name = "tendermint-rpc" -version = "0.23.3" +version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626c493e9ced3a9de37583bbd929f4b6dbd49aa513ab9b4776aa44a9332ce9b5" +checksum = "a13e63f57ee05a1e927887191c76d1b139de9fa40c180b9f8727ee44377242a6" dependencies = [ "async-trait", "bytes", @@ -5856,8 +5860,8 @@ dependencies = [ "hyper-timeout", "percent-encoding", "pin-project", - "prost", - "prost-derive", + "prost 0.9.0", + "prost-derive 0.9.0", "tokio", "tokio-stream", "tokio-util 0.6.9", @@ -6200,7 +6204,7 @@ dependencies = [ "coconut-interface", "colored", "config", - "cosmrs 0.4.1 (git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation)", + "cosmrs", "cosmwasm-std", "flate2", "futures", @@ -6208,7 +6212,7 @@ dependencies = [ "log", "mixnet-contract-common", "network-defaults", - "prost", + "prost 0.10.3", "reqwest", "serde", "serde_json", @@ -6628,6 +6632,7 @@ checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" dependencies = [ "curve25519-dalek", "rand_core 0.5.1", + "serde", "zeroize", ] diff --git a/common/client-libs/validator-client/Cargo.toml b/common/client-libs/validator-client/Cargo.toml index 32c5aa1b2a..3ea58f4212 100644 --- a/common/client-libs/validator-client/Cargo.toml +++ b/common/client-libs/validator-client/Cargo.toml @@ -32,8 +32,8 @@ validator-api-requests = { path = "../../../validator-api/validator-api-requests async-trait = { version = "0.1.51", optional = true } bip39 = { version = "1", features = ["rand"], optional = true } config = { path = "../../config", optional = true } -cosmrs = { git = "https://github.com/nymtech/cosmos-rust", branch = "bugfix/account-id-length-validation", features = ["rpc", "bip32", "cosmwasm"], optional = true} -prost = { version = "0.9", default-features = false, optional = true } +cosmrs = { version = "0.7.0", features = ["rpc", "bip32", "cosmwasm"], optional = true} +prost = { version = "0.10", default-features = false, optional = true } flate2 = { version = "1.0.20", optional = true } sha2 = { version = "0.9.5", optional = true } itertools = { version = "0.10", optional = true } diff --git a/common/client-libs/validator-client/src/nymd/cosmwasm_client/client.rs b/common/client-libs/validator-client/src/nymd/cosmwasm_client/client.rs index c2e4dadd39..594092bc86 100644 --- a/common/client-libs/validator-client/src/nymd/cosmwasm_client/client.rs +++ b/common/client-libs/validator-client/src/nymd/cosmwasm_client/client.rs @@ -483,6 +483,9 @@ pub trait CosmWasmClient: rpc::Client { // deprecation warning is due to the fact the protobuf files built were based on cosmos-sdk 0.44, // where they prefer using tx_bytes directly. However, in 0.42, which we are using at the time // of writing this, the option does not work + // TODO: we should really stop using the `tx` argument here and use `tx_bytes` exlusively, + // however, at the time of writing this update, while our QA and mainnet networks do support it, + // sandbox is still running old version of wasmd that lacks support for `tx_bytes` #[allow(deprecated)] async fn query_simulate( &self, diff --git a/common/client-libs/validator-client/src/nymd/cosmwasm_client/helpers.rs b/common/client-libs/validator-client/src/nymd/cosmwasm_client/helpers.rs index 44662d90a1..ce1e93dcdb 100644 --- a/common/client-libs/validator-client/src/nymd/cosmwasm_client/helpers.rs +++ b/common/client-libs/validator-client/src/nymd/cosmwasm_client/helpers.rs @@ -68,6 +68,7 @@ pub(crate) fn create_pagination(key: Vec) -> PageRequest { offset: 0, limit: 0, count_total: false, + reverse: false, } } diff --git a/common/client-libs/validator-client/src/nymd/cosmwasm_client/signing_client.rs b/common/client-libs/validator-client/src/nymd/cosmwasm_client/signing_client.rs index 50b0f9dfe6..58e10c8655 100644 --- a/common/client-libs/validator-client/src/nymd/cosmwasm_client/signing_client.rs +++ b/common/client-libs/validator-client/src/nymd/cosmwasm_client/signing_client.rs @@ -1,9 +1,14 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use std::convert::TryInto; -use std::time::Duration; - +use crate::nymd::cosmwasm_client::client::CosmWasmClient; +use crate::nymd::cosmwasm_client::helpers::{compress_wasm_code, CheckResponse}; +use crate::nymd::cosmwasm_client::logs::{self, parse_raw_logs}; +use crate::nymd::cosmwasm_client::types::*; +use crate::nymd::error::NymdError; +use crate::nymd::fee::{Fee, DEFAULT_SIMULATED_GAS_MULTIPLIER}; +use crate::nymd::wallet::DirectSecp256k1HdWallet; +use crate::nymd::{GasPrice, TxResponse}; use async_trait::async_trait; use cosmrs::bank::MsgSend; use cosmrs::distribution::MsgWithdrawDelegatorReward; @@ -17,28 +22,35 @@ use log::debug; use serde::Serialize; use sha2::Digest; use sha2::Sha256; - -use crate::nymd::cosmwasm_client::client::CosmWasmClient; -use crate::nymd::cosmwasm_client::helpers::{compress_wasm_code, CheckResponse}; -use crate::nymd::cosmwasm_client::logs::{self, parse_raw_logs}; -use crate::nymd::cosmwasm_client::types::*; -use crate::nymd::error::NymdError; -use crate::nymd::fee::{Fee, DEFAULT_SIMULATED_GAS_MULTIPLIER}; -use crate::nymd::wallet::DirectSecp256k1HdWallet; -use crate::nymd::{CosmosCoin, GasPrice, TxResponse}; - -// we need to have **a** valid secp256k1 signature for simulation purposes. -// it doesn't matter what it is as long as it parses correctly -const DUMMY_SECP256K1_SIGNATURE: &[u8] = &[ - 54, 167, 169, 61, 100, 173, 231, 87, 1, 113, 179, 49, 102, 141, 67, 22, 170, 153, 52, 88, 178, - 159, 200, 11, 37, 138, 76, 221, 187, 70, 104, 123, 98, 216, 190, 249, 149, 81, 1, 158, 0, 220, - 32, 147, 101, 60, 64, 77, 44, 83, 221, 119, 170, 124, 109, 177, 73, 116, 46, 57, 102, 181, 98, - 91, -]; +use std::convert::TryInto; +use std::time::Duration; const DEFAULT_BROADCAST_POLLING_RATE: Duration = Duration::from_secs(4); const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60); +fn empty_fee() -> tx::Fee { + tx::Fee { + amount: vec![], + gas_limit: Default::default(), + payer: None, + granter: None, + } +} + +fn single_unspecified_signer_auth( + public_key: Option, + sequence_number: tx::SequenceNumber, +) -> tx::AuthInfo { + tx::SignerInfo { + public_key, + mode_info: tx::ModeInfo::Single(tx::mode_info::Single { + mode: SignMode::Unspecified, + }), + sequence: sequence_number, + } + .auth_info(empty_fee()) +} + #[async_trait] pub trait SigningCosmWasmClient: CosmWasmClient { fn signer(&self) -> &DirectSecp256k1HdWallet; @@ -64,33 +76,21 @@ pub trait SigningCosmWasmClient: CosmWasmClient { let sequence_response = self.get_sequence(signer_address).await?; let partial_tx = Tx { - body: tx::Body { - messages, - memo: memo.into(), - timeout_height: 0u32.into(), - extension_options: vec![], - non_critical_extension_options: vec![], - }, - auth_info: tx::AuthInfo { - signer_infos: vec![tx::SignerInfo { - public_key, - mode_info: tx::ModeInfo::Single(tx::mode_info::Single { - mode: SignMode::Unspecified, - }), - sequence: sequence_response.sequence, - }], - fee: tx::Fee::from_amount_and_gas( - CosmosCoin { - denom: "".parse().unwrap(), - amount: 0u64.into(), - }, - 0, - ), - }, - signatures: vec![DUMMY_SECP256K1_SIGNATURE.try_into().unwrap()], + body: tx::Body::new(messages, memo, 0u32), + auth_info: single_unspecified_signer_auth(public_key, sequence_response.sequence), + signatures: vec![Vec::new()], }; - self.query_simulate(Some(partial_tx), Vec::new()).await + + // for completion sake, once we're able to transition into using `tx_bytes`, + // we might want to use something like this instead: + // let tx_raw: tx::Raw = cosmrs::proto::cosmos::tx::v1beta1::TxRaw { + // body_bytes: partial_tx.body.into_bytes().unwrap(), + // auth_info_bytes: partial_tx.auth_info.into_bytes().unwrap(), + // signatures: partial_tx.signatures, + // } + // .into(); + // self.query_simulate(None, tx_raw.to_bytes().unwrap()).await } async fn upload( @@ -452,7 +452,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { let undelegate_msg = MsgUndelegate { delegator_address: delegator_address.to_owned(), validator_address: validator_address.to_owned(), - amount: Some(amount), + amount, } .to_any() .map_err(|_| NymdError::SerializationError("MsgUndelegate".to_owned()))?; diff --git a/common/client-libs/validator-client/src/nymd/cosmwasm_client/types.rs b/common/client-libs/validator-client/src/nymd/cosmwasm_client/types.rs index f928857c7e..5d3994bd15 100644 --- a/common/client-libs/validator-client/src/nymd/cosmwasm_client/types.rs +++ b/common/client-libs/validator-client/src/nymd/cosmwasm_client/types.rs @@ -489,7 +489,7 @@ impl TryFrom for ContractCodeHistoryEntry { } } -#[derive(Debug)] +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] pub struct GasInfo { /// GasWanted is the maximum units of work we allow this tx to perform. pub gas_wanted: Gas, diff --git a/common/client-libs/validator-client/src/nymd/error.rs b/common/client-libs/validator-client/src/nymd/error.rs index 9aa13e949f..ea3620ec35 100644 --- a/common/client-libs/validator-client/src/nymd/error.rs +++ b/common/client-libs/validator-client/src/nymd/error.rs @@ -21,9 +21,12 @@ pub enum NymdError { #[error("There was an issue with bip32 - {0}")] Bip32Error(#[from] bip32::Error), - #[error("There was an issue with bip32 - {0}")] + #[error("There was an issue with bip39 - {0}")] Bip39Error(#[from] bip39::Error), + #[error("There was an issue on the cosmrs side - {0}")] + CosmrsError(#[from] cosmrs::Error), + #[error("Failed to derive account address")] AccountDerivationError, @@ -39,10 +42,10 @@ pub enum NymdError { #[error("There was an issue with a tendermint RPC request - {0}")] TendermintError(#[from] TendermintRpcError), - #[error("There was an issue when attempting to serialize data")] + #[error("There was an issue when attempting to serialize data ({0})")] SerializationError(String), - #[error("There was an issue when attempting to deserialize data")] + #[error("There was an issue when attempting to deserialize data ({0})")] DeserializationError(String), #[error("There was an issue when attempting to encode our protobuf data - {0}")] diff --git a/common/client-libs/validator-client/src/nymd/fee/gas_price.rs b/common/client-libs/validator-client/src/nymd/fee/gas_price.rs index 27276ee12d..e7e69a32a5 100644 --- a/common/client-libs/validator-client/src/nymd/fee/gas_price.rs +++ b/common/client-libs/validator-client/src/nymd/fee/gas_price.rs @@ -107,7 +107,6 @@ mod tests { assert!(".25upunk".parse::().is_err()); assert!("0.025 upunk".parse::().is_err()); - assert!("0.025UPUNK".parse::().is_err()); } #[test] diff --git a/common/client-libs/validator-client/src/nymd/fee/helpers.rs b/common/client-libs/validator-client/src/nymd/fee/helpers.rs deleted file mode 100644 index 661f23eb50..0000000000 --- a/common/client-libs/validator-client/src/nymd/fee/helpers.rs +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2021 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::nymd::GasPrice; -use cosmrs::tx::{Fee, Gas}; -use cosmrs::Coin; -use serde::{Deserialize, Serialize}; -use std::fmt; - -#[cfg_attr(test, derive(ts_rs::TS))] -#[cfg_attr( - test, - ts(export, export_to = "../../../nym-wallet/src/types/rust/operation.ts") -)] -#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize)] -pub enum Operation { - Upload, - Init, - Migrate, - ChangeAdmin, - Send, - - BondMixnode, - BondMixnodeOnBehalf, - UnbondMixnode, - UnbondMixnodeOnBehalf, - UpdateMixnodeConfig, - DelegateToMixnode, - DelegateToMixnodeOnBehalf, - UndelegateFromMixnode, - UndelegateFromMixnodeOnBehalf, - - BondGateway, - BondGatewayOnBehalf, - UnbondGateway, - UnbondGatewayOnBehalf, - - UpdateContractSettings, - - BeginMixnodeRewarding, - FinishMixnodeRewarding, - - TrackUnbondGateway, - TrackUnbondMixnode, - WithdrawVestedCoins, - TrackUndelegation, - CreatePeriodicVestingAccount, - - AdvanceCurrentInterval, - AdvanceCurrentEpoch, - WriteRewardedSet, - ClearRewardedSet, - UpdateMixnetAddress, - CheckpointMixnodes, - ReconcileDelegations, -} - -pub(crate) fn calculate_fee(gas_price: &GasPrice, gas_limit: Gas) -> Coin { - gas_price * gas_limit -} - -impl fmt::Display for Operation { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Operation::Upload => f.write_str("Upload"), - Operation::Init => f.write_str("Init"), - Operation::Migrate => f.write_str("Migrate"), - Operation::ChangeAdmin => f.write_str("ChangeAdmin"), - Operation::Send => f.write_str("Send"), - Operation::BondMixnode => f.write_str("BondMixnode"), - Operation::BondMixnodeOnBehalf => f.write_str("BondMixnodeOnBehalf"), - Operation::UnbondMixnode => f.write_str("UnbondMixnode"), - Operation::UpdateMixnodeConfig => f.write_str("UpdateMixnodeConfig"), - Operation::UnbondMixnodeOnBehalf => f.write_str("UnbondMixnodeOnBehalf"), - Operation::BondGateway => f.write_str("BondGateway"), - Operation::BondGatewayOnBehalf => f.write_str("BondGatewayOnBehalf"), - Operation::UnbondGateway => f.write_str("UnbondGateway"), - Operation::UnbondGatewayOnBehalf => f.write_str("UnbondGatewayOnBehalf"), - Operation::DelegateToMixnode => f.write_str("DelegateToMixnode"), - Operation::DelegateToMixnodeOnBehalf => f.write_str("DelegateToMixnodeOnBehalf"), - Operation::UndelegateFromMixnode => f.write_str("UndelegateFromMixnode"), - Operation::UndelegateFromMixnodeOnBehalf => { - f.write_str("UndelegateFromMixnodeOnBehalf") - } - Operation::UpdateContractSettings => f.write_str("UpdateContractSettings"), - Operation::BeginMixnodeRewarding => f.write_str("BeginMixnodeRewarding"), - Operation::FinishMixnodeRewarding => f.write_str("FinishMixnodeRewarding"), - Operation::TrackUnbondGateway => f.write_str("TrackUnbondGateway"), - Operation::TrackUnbondMixnode => f.write_str("TrackUnbondMixnode"), - Operation::WithdrawVestedCoins => f.write_str("WithdrawVestedCoins"), - Operation::TrackUndelegation => f.write_str("TrackUndelegation"), - Operation::CreatePeriodicVestingAccount => f.write_str("CreatePeriodicVestingAccount"), - Operation::AdvanceCurrentInterval => f.write_str("AdvanceCurrentInterval"), - Operation::WriteRewardedSet => f.write_str("WriteRewardedSet"), - Operation::ClearRewardedSet => f.write_str("ClearRewardedSet"), - Operation::UpdateMixnetAddress => f.write_str("UpdateMixnetAddress"), - Operation::CheckpointMixnodes => f.write_str("CheckpointMixnodes"), - Operation::ReconcileDelegations => f.write_str("ReconcileDelegations"), - Operation::AdvanceCurrentEpoch => f.write_str("AdvanceCurrentEpoch"), - } - } -} - -impl Operation { - // TODO: some value tweaking - pub fn default_gas_limit(&self) -> Gas { - match self { - Operation::Upload => 3_000_000u64.into(), - Operation::Init => 500_000u64.into(), - Operation::Migrate => 200_000u64.into(), - Operation::ChangeAdmin => 80_000u64.into(), - Operation::Send => 80_000u64.into(), - - Operation::BondMixnode => 175_000u64.into(), - Operation::BondMixnodeOnBehalf => 200_000u64.into(), - Operation::UnbondMixnode => 175_000u64.into(), - Operation::UnbondMixnodeOnBehalf => 175_000u64.into(), - Operation::UpdateMixnodeConfig => 175_000u64.into(), - Operation::DelegateToMixnode => 175_000u64.into(), - Operation::DelegateToMixnodeOnBehalf => 175_000u64.into(), - Operation::UndelegateFromMixnode => 175_000u64.into(), - Operation::UndelegateFromMixnodeOnBehalf => 175_000u64.into(), - - Operation::BondGateway => 175_000u64.into(), - Operation::BondGatewayOnBehalf => 200_000u64.into(), - Operation::UnbondGateway => 175_000u64.into(), - Operation::UnbondGatewayOnBehalf => 200_000u64.into(), - - Operation::UpdateContractSettings => 175_000u64.into(), - Operation::BeginMixnodeRewarding => 175_000u64.into(), - Operation::FinishMixnodeRewarding => 175_000u64.into(), - Operation::TrackUnbondGateway => 175_000u64.into(), - Operation::TrackUnbondMixnode => 175_000u64.into(), - Operation::WithdrawVestedCoins => 175_000u64.into(), - Operation::TrackUndelegation => 175_000u64.into(), - Operation::CreatePeriodicVestingAccount => 175_000u64.into(), - Operation::AdvanceCurrentInterval => 175_000u64.into(), - Operation::WriteRewardedSet => 175_000u64.into(), - Operation::ClearRewardedSet => 175_000u64.into(), - Operation::UpdateMixnetAddress => 80_000u64.into(), - Operation::CheckpointMixnodes => 175_000u64.into(), - Operation::ReconcileDelegations => 500_000u64.into(), - Operation::AdvanceCurrentEpoch => 175_000u64.into(), - } - } - - pub(crate) fn determine_custom_fee(gas_price: &GasPrice, gas_limit: Gas) -> Fee { - // we need to know 2 of the following 3 parameters (the third one is being implicit) in order to construct Fee: - // (source: https://docs.cosmos.network/v0.42/basics/gas-fees.html) - // - gas price - // - gas limit - // - fees - let fee = calculate_fee(gas_price, gas_limit); - Fee::from_amount_and_gas(fee, gas_limit) - } - - pub fn default_fee(&self, gas_price: &GasPrice) -> Fee { - Self::determine_custom_fee(gas_price, self.default_gas_limit()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn calculating_fee() { - let expected = Coin { - denom: "upunk".parse().unwrap(), - amount: 1000u64.into(), - }; - let gas_price = "1upunk".parse().unwrap(); - let gas_limit = 1000u64.into(); - - assert_eq!(expected, calculate_fee(&gas_price, gas_limit)); - - let expected = Coin { - denom: "upunk".parse().unwrap(), - amount: 50u64.into(), - }; - let gas_price = "0.05upunk".parse().unwrap(); - let gas_limit = 1000u64.into(); - - assert_eq!(expected, calculate_fee(&gas_price, gas_limit)); - - let expected = Coin { - denom: "upunk".parse().unwrap(), - amount: 100000u64.into(), - }; - let gas_price = "100upunk".parse().unwrap(); - let gas_limit = 1000u64.into(); - - assert_eq!(expected, calculate_fee(&gas_price, gas_limit)) - } -} diff --git a/common/client-libs/validator-client/src/nymd/fee/mod.rs b/common/client-libs/validator-client/src/nymd/fee/mod.rs index 5aa87b65a6..7044fc6f9d 100644 --- a/common/client-libs/validator-client/src/nymd/fee/mod.rs +++ b/common/client-libs/validator-client/src/nymd/fee/mod.rs @@ -2,15 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 use cosmrs::tx; +use serde::{Deserialize, Serialize}; pub mod gas_price; -pub mod helpers; pub const DEFAULT_SIMULATED_GAS_MULTIPLIER: f32 = 1.3; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum Fee { - Manual(tx::Fee), + Manual(#[serde(with = "sealed::TxFee")] tx::Fee), Auto(Option), } @@ -31,3 +31,91 @@ impl Default for Fee { Fee::Auto(Some(DEFAULT_SIMULATED_GAS_MULTIPLIER)) } } + +// a workaround to provide serde implementation for tx::Fee. We don't want to ever expose any of those +// types to the public and ideally they will get replaced by proper implementation inside comrs +mod sealed { + use cosmrs::tx::{self, Gas}; + use cosmrs::Coin as CosmosCoin; + use cosmrs::{AccountId, Decimal as CosmosDecimal, Denom as CosmosDenom}; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + fn cosmos_denom_inner_getter(val: &CosmosDenom) -> String { + val.as_ref().to_string() + } + + #[derive(Serialize, Deserialize)] + #[serde(remote = "CosmosDenom")] + struct Denom(#[serde(getter = "cosmos_denom_inner_getter")] String); + + impl From for CosmosDenom { + fn from(val: Denom) -> Self { + val.0.parse().unwrap() + } + } + + fn cosmos_decimal_inner_getter(val: &CosmosDecimal) -> u64 { + // haha, this code is so disgusting. I'll make a PR on cosmrs to slightly alleviate those issues... + // note: unwrap here is fine as the to_string is just returning a stringified u64 which, well, is a valid u64 + val.to_string().parse().unwrap() + } + + // at the time of writing it the current cosmrs' Decimal is extremely limited... + #[derive(Serialize, Deserialize)] + #[serde(remote = "CosmosDecimal")] + struct Decimal(#[serde(getter = "cosmos_decimal_inner_getter")] u64); + + impl From for CosmosDecimal { + fn from(val: Decimal) -> Self { + val.0.into() + } + } + + #[derive(Serialize, Deserialize, Clone)] + struct Coin { + #[serde(with = "Denom")] + denom: CosmosDenom, + #[serde(with = "Decimal")] + amount: CosmosDecimal, + } + + impl From for CosmosCoin { + fn from(val: Coin) -> Self { + CosmosCoin { + denom: val.denom, + amount: val.amount, + } + } + } + + impl From for Coin { + fn from(val: CosmosCoin) -> Self { + Coin { + denom: val.denom, + amount: val.amount, + } + } + } + + fn coin_vec_ser(val: &[CosmosCoin], serializer: S) -> Result { + let vec: Vec = val.iter().cloned().map(Into::into).collect(); + vec.serialize(serializer) + } + fn coin_vec_deser<'de, D: Deserializer<'de>>( + deserializer: D, + ) -> Result, D::Error> { + let vec: Vec = Deserialize::deserialize(deserializer)?; + Ok(vec.iter().cloned().map(Into::into).collect()) + } + + #[derive(Serialize, Deserialize)] + #[serde(remote = "tx::Fee")] + pub(super) struct TxFee { + #[serde(serialize_with = "coin_vec_ser")] + #[serde(deserialize_with = "coin_vec_deser")] + pub amount: Vec, + pub gas_limit: Gas, + pub payer: Option, + pub granter: Option, + } +} diff --git a/common/client-libs/validator-client/src/nymd/mod.rs b/common/client-libs/validator-client/src/nymd/mod.rs index a8c1f9d9c1..03adeb8e5b 100644 --- a/common/client-libs/validator-client/src/nymd/mod.rs +++ b/common/client-libs/validator-client/src/nymd/mod.rs @@ -4,15 +4,16 @@ use crate::nymd::cosmwasm_client::signing_client; use crate::nymd::cosmwasm_client::types::{ Account, ChangeAdminResult, ContractCodeId, ExecuteResult, InstantiateOptions, - InstantiateResult, MigrateResult, SequenceResponse, UploadResult, + InstantiateResult, MigrateResult, SequenceResponse, SimulateResponse, UploadResult, }; use crate::nymd::error::NymdError; +use crate::nymd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER; use crate::nymd::wallet::DirectSecp256k1HdWallet; use cosmrs::rpc::Error as TendermintRpcError; use cosmrs::rpc::HttpClientUrl; +use cosmrs::tx::Msg; use cosmwasm_std::{Coin, Uint128}; pub use fee::gas_price::GasPrice; -use fee::helpers::Operation; use mixnet_contract_common::mixnode::DelegationEvent; use mixnet_contract_common::{ ContractStateParams, Delegation, ExecuteMsg, Gateway, GatewayBond, GatewayOwnershipResponse, @@ -22,14 +23,13 @@ use mixnet_contract_common::{ PagedRewardedSetResponse, QueryMsg, RewardedSetUpdateDetails, }; use serde::Serialize; -use std::collections::HashMap; use std::convert::TryInto; pub use crate::nymd::cosmwasm_client::client::CosmWasmClient; pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient; pub use crate::nymd::fee::Fee; -use crate::nymd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER; pub use cosmrs::bank::MsgSend; +use cosmrs::cosmwasm; pub use cosmrs::rpc::endpoint::tx::Response as TxResponse; pub use cosmrs::rpc::endpoint::validators::Response as ValidatorResponse; pub use cosmrs::rpc::HttpClient as QueryNymdClient; @@ -42,7 +42,7 @@ pub use cosmrs::tendermint::validator::Info as TendermintValidatorInfo; pub use cosmrs::tendermint::Time as TendermintTime; pub use cosmrs::tx::{self, Gas}; pub use cosmrs::Coin as CosmosCoin; -pub use cosmrs::{AccountId, Decimal, Denom}; +pub use cosmrs::{bip32, AccountId, Decimal, Denom}; pub use signing_client::Client as SigningNymdClient; pub use traits::{VestingQueryClient, VestingSigningClient}; @@ -59,7 +59,6 @@ pub struct NymdClient { vesting_contract_address: Option, erc20_bridge_contract_address: Option, client_address: Option>, - custom_gas_limits: HashMap, simulated_gas_multiplier: f32, } @@ -79,7 +78,6 @@ impl NymdClient { vesting_contract_address, erc20_bridge_contract_address, client_address: None, - custom_gas_limits: HashMap::default(), simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER, }) } @@ -113,7 +111,6 @@ impl NymdClient { vesting_contract_address, erc20_bridge_contract_address, client_address: Some(client_address), - custom_gas_limits: HashMap::default(), simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER, }) } @@ -146,7 +143,6 @@ impl NymdClient { vesting_contract_address, erc20_bridge_contract_address, client_address: Some(client_address), - custom_gas_limits: HashMap::default(), simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER, }) } @@ -175,6 +171,24 @@ impl NymdClient { self.simulated_gas_multiplier = multiplier; } + pub fn wrap_contract_execute_message( + &self, + contract_address: &AccountId, + msg: &M, + funds: Vec, + ) -> Result + where + C: SigningCosmWasmClient, + M: ?Sized + Serialize, + { + Ok(cosmwasm::MsgExecuteContract { + sender: self.address().clone(), + contract: contract_address.clone(), + msg: serde_json::to_vec(msg)?, + funds, + }) + } + pub fn address(&self) -> &AccountId where C: SigningCosmWasmClient, @@ -190,39 +204,6 @@ impl NymdClient { self.client.gas_price() } - pub fn set_custom_gas_limit(&mut self, operation: Operation, limit: Gas) - where - C: SigningCosmWasmClient + Sync, - { - self.custom_gas_limits.insert(operation, limit); - } - - pub fn operation_fee(&self, operation: Operation) -> Fee - where - C: SigningCosmWasmClient + Sync, - { - if let Some(&gas_limit) = self.custom_gas_limits.get(&operation) { - Operation::determine_custom_fee(self.client.gas_price(), gas_limit).into() - } else { - Fee::Auto(Some(self.simulated_gas_multiplier)) - } - } - - pub fn repeated_operation_fee(&self, operation: Operation, count: u64) -> Fee - where - C: SigningCosmWasmClient + Sync, - { - if let Some(&gas_limit) = self.custom_gas_limits.get(&operation) { - Operation::determine_custom_fee( - self.client.gas_price(), - (gas_limit.value() * count).into(), - ) - .into() - } else { - Fee::Auto(Some(self.simulated_gas_multiplier)) - } - } - pub async fn account_sequence(&self) -> Result where C: SigningCosmWasmClient + Sync, @@ -634,17 +615,39 @@ impl NymdClient { .await } + pub async fn simulate(&self, messages: I) -> Result + where + C: SigningCosmWasmClient + Sync, + I: IntoIterator + Send, + M: Msg, + { + self.client + .simulate( + self.address(), + messages + .into_iter() + .map(|msg| msg.into_any()) + .collect::, _>>() + .map_err(|_| { + NymdError::SerializationError("custom simulate messages".to_owned()) + })?, + "simulating execution of transactions", + ) + .await + } + /// Send funds from one address to another pub async fn send( &self, recipient: &AccountId, amount: Vec, memo: impl Into + Send + 'static, + fee: Option, ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::Send); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); self.client .send_tokens(self.address(), recipient, amount, fee, memo) .await @@ -655,11 +658,12 @@ impl NymdClient { &self, msgs: Vec<(AccountId, Vec)>, memo: impl Into + Send + 'static, + fee: Option, ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.repeated_operation_fee(Operation::Send, msgs.len() as u64); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); self.client .send_tokens_multiple(self.address(), msgs, fee, memo) .await @@ -703,11 +707,12 @@ impl NymdClient { &self, wasm_code: Vec, memo: impl Into + Send + 'static, + fee: Option, ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::Upload); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); self.client .upload(self.address(), wasm_code, fee, memo) .await @@ -720,12 +725,13 @@ impl NymdClient { label: String, memo: impl Into + Send + 'static, options: Option, + fee: Option, ) -> Result where C: SigningCosmWasmClient + Sync, M: ?Sized + Serialize + Sync, { - let fee = self.operation_fee(Operation::Init); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); self.client .instantiate(self.address(), code_id, msg, label, fee, memo, options) .await @@ -736,11 +742,12 @@ impl NymdClient { contract_address: &AccountId, new_admin: &AccountId, memo: impl Into + Send + 'static, + fee: Option, ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::ChangeAdmin); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); self.client .update_admin(self.address(), contract_address, new_admin, fee, memo) .await @@ -750,11 +757,12 @@ impl NymdClient { &self, contract_address: &AccountId, memo: impl Into + Send + 'static, + fee: Option, ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::ChangeAdmin); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); self.client .clear_admin(self.address(), contract_address, fee, memo) .await @@ -766,12 +774,13 @@ impl NymdClient { code_id: ContractCodeId, msg: &M, memo: impl Into + Send + 'static, + fee: Option, ) -> Result where C: SigningCosmWasmClient + Sync, M: ?Sized + Serialize + Sync, { - let fee = self.operation_fee(Operation::Migrate); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); self.client .migrate(self.address(), contract_address, code_id, fee, msg, memo) .await @@ -783,11 +792,12 @@ impl NymdClient { mixnode: MixNode, owner_signature: String, pledge: Coin, + fee: Option, ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::BondMixnode); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = ExecuteMsg::BondMixnode { mix_node: mixnode, @@ -812,11 +822,12 @@ impl NymdClient { owner: String, owner_signature: String, pledge: Coin, + fee: Option, ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::BondMixnodeOnBehalf); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = ExecuteMsg::BondMixnodeOnBehalf { mix_node: mixnode, @@ -839,14 +850,12 @@ impl NymdClient { pub async fn bond_multiple_mixnodes_on_behalf( &self, mixnode_bonds_with_sigs: Vec<(MixNodeBond, String)>, + fee: Option, ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.repeated_operation_fee( - Operation::BondMixnodeOnBehalf, - mixnode_bonds_with_sigs.len() as u64, - ); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let reqs: Vec<(ExecuteMsg, Vec)> = mixnode_bonds_with_sigs .into_iter() @@ -874,11 +883,11 @@ impl NymdClient { } /// Unbond a mixnode, removing it from the network and reclaiming staked coins - pub async fn unbond_mixnode(&self) -> Result + pub async fn unbond_mixnode(&self, fee: Option) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::UnbondMixnode); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = ExecuteMsg::UnbondMixnode {}; self.client @@ -894,11 +903,15 @@ impl NymdClient { } /// Unbond a mixnode on behalf of the owner, removing it from the network and reclaiming staked coins - pub async fn unbond_mixnode_on_behalf(&self, owner: String) -> Result + pub async fn unbond_mixnode_on_behalf( + &self, + owner: String, + fee: Option, + ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::UnbondMixnodeOnBehalf); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = ExecuteMsg::UnbondMixnodeOnBehalf { owner }; self.client @@ -917,11 +930,12 @@ impl NymdClient { pub async fn update_mixnode_config( &self, profit_margin_percent: u8, + fee: Option, ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::UpdateMixnodeConfig); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = ExecuteMsg::UpdateMixnodeConfig { profit_margin_percent, @@ -943,11 +957,12 @@ impl NymdClient { &self, mix_identity: &str, amount: &Coin, + fee: Option, ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::DelegateToMixnode); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = ExecuteMsg::DelegateToMixnode { mix_identity: mix_identity.to_string(), @@ -971,11 +986,12 @@ impl NymdClient { mix_identity: &str, delegate: &str, amount: &Coin, + fee: Option, ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::DelegateToMixnodeOnBehalf); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = ExecuteMsg::DelegateToMixnodeOnBehalf { mix_identity: mix_identity.to_string(), @@ -997,14 +1013,12 @@ impl NymdClient { pub async fn delegate_to_multiple_mixnodes_on_behalf( &self, mixnode_delegations: Vec, + fee: Option, ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.repeated_operation_fee( - Operation::DelegateToMixnodeOnBehalf, - mixnode_delegations.len() as u64, - ); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let reqs: Vec<(ExecuteMsg, Vec)> = mixnode_delegations .into_iter() @@ -1034,11 +1048,12 @@ impl NymdClient { pub async fn remove_mixnode_delegation( &self, mix_identity: &str, + fee: Option, ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::UndelegateFromMixnode); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = ExecuteMsg::UndelegateFromMixnode { mix_identity: mix_identity.to_string(), @@ -1060,11 +1075,12 @@ impl NymdClient { &self, mix_identity: &str, delegate: &str, + fee: Option, ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::UndelegateFromMixnodeOnBehalf); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = ExecuteMsg::UndelegateFromMixnodeOnBehalf { mix_identity: mix_identity.to_string(), @@ -1088,11 +1104,12 @@ impl NymdClient { gateway: Gateway, owner_signature: String, pledge: Coin, + fee: Option, ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::BondGateway); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = ExecuteMsg::BondGateway { gateway, @@ -1117,11 +1134,12 @@ impl NymdClient { owner: String, owner_signature: String, pledge: Coin, + fee: Option, ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::BondGatewayOnBehalf); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = ExecuteMsg::BondGatewayOnBehalf { gateway, @@ -1144,14 +1162,12 @@ impl NymdClient { pub async fn bond_multiple_gateways_on_behalf( &self, gateway_bonds_with_sigs: Vec<(GatewayBond, String)>, + fee: Option, ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.repeated_operation_fee( - Operation::BondGatewayOnBehalf, - gateway_bonds_with_sigs.len() as u64, - ); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let reqs: Vec<(ExecuteMsg, Vec)> = gateway_bonds_with_sigs .into_iter() @@ -1179,11 +1195,11 @@ impl NymdClient { } /// Unbond a gateway, removing it from the network and reclaiming staked coins - pub async fn unbond_gateway(&self) -> Result + pub async fn unbond_gateway(&self, fee: Option) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::UnbondGateway); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = ExecuteMsg::UnbondGateway {}; self.client @@ -1200,11 +1216,15 @@ impl NymdClient { /// Unbond a gateway on behalf of the owner, removing it from the /// network and reclaiming staked coins - pub async fn unbond_gateway_on_behalf(&self, owner: String) -> Result + pub async fn unbond_gateway_on_behalf( + &self, + owner: String, + fee: Option, + ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::UnbondGatewayOnBehalf); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = ExecuteMsg::UnbondGatewayOnBehalf { owner }; self.client @@ -1222,11 +1242,12 @@ impl NymdClient { pub async fn update_contract_settings( &self, new_params: ContractStateParams, + fee: Option, ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::UpdateContractSettings); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = ExecuteMsg::UpdateContractStateParams(new_params); self.client @@ -1241,11 +1262,11 @@ impl NymdClient { .await } - pub async fn advance_current_epoch(&self) -> Result + pub async fn advance_current_epoch(&self, fee: Option) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::AdvanceCurrentEpoch); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = ExecuteMsg::AdvanceCurrentEpoch {}; self.client @@ -1260,11 +1281,11 @@ impl NymdClient { .await } - pub async fn reconcile_delegations(&self) -> Result + pub async fn reconcile_delegations(&self, fee: Option) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::ReconcileDelegations); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = ExecuteMsg::ReconcileDelegations {}; self.client @@ -1279,11 +1300,11 @@ impl NymdClient { .await } - pub async fn checkpoint_mixnodes(&self) -> Result + pub async fn checkpoint_mixnodes(&self, fee: Option) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::CheckpointMixnodes); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = ExecuteMsg::CheckpointMixnodes {}; self.client @@ -1302,11 +1323,12 @@ impl NymdClient { &self, rewarded_set: Vec, expected_active_set_size: u32, + fee: Option, ) -> Result where C: SigningCosmWasmClient + Sync, { - let fee = self.operation_fee(Operation::WriteRewardedSet); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = ExecuteMsg::WriteRewardedSet { rewarded_set, diff --git a/common/client-libs/validator-client/src/nymd/traits/vesting_signing_client.rs b/common/client-libs/validator-client/src/nymd/traits/vesting_signing_client.rs index 248ef9cdd7..073cfb7052 100644 --- a/common/client-libs/validator-client/src/nymd/traits/vesting_signing_client.rs +++ b/common/client-libs/validator-client/src/nymd/traits/vesting_signing_client.rs @@ -4,8 +4,7 @@ pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient; use crate::nymd::cosmwasm_client::types::ExecuteResult; use crate::nymd::error::NymdError; -use crate::nymd::fee::helpers::Operation; -use crate::nymd::{cosmwasm_coin_to_cosmos_coin, NymdClient}; +use crate::nymd::{cosmwasm_coin_to_cosmos_coin, Fee, NymdClient}; use async_trait::async_trait; use cosmwasm_std::Coin; use mixnet_contract_common::{Gateway, IdentityKey, IdentityKeyRef, MixNode}; @@ -16,23 +15,30 @@ pub trait VestingSigningClient { async fn vesting_update_mixnode_config( &self, profix_margin_percent: u8, + fee: Option, ) -> Result; - async fn update_mixnet_address(&self, address: &str) -> Result; + async fn update_mixnet_address( + &self, + address: &str, + fee: Option, + ) -> Result; async fn vesting_bond_gateway( &self, gateway: Gateway, owner_signature: &str, pledge: Coin, + fee: Option, ) -> Result; - async fn vesting_unbond_gateway(&self) -> Result; + async fn vesting_unbond_gateway(&self, fee: Option) -> Result; async fn vesting_track_unbond_gateway( &self, owner: &str, amount: Coin, + fee: Option, ) -> Result; async fn vesting_bond_mixnode( @@ -40,33 +46,42 @@ pub trait VestingSigningClient { mix_node: MixNode, owner_signature: &str, pledge: Coin, + fee: Option, ) -> Result; - async fn vesting_unbond_mixnode(&self) -> Result; + async fn vesting_unbond_mixnode(&self, fee: Option) -> Result; async fn vesting_track_unbond_mixnode( &self, owner: &str, amount: Coin, + fee: Option, ) -> Result; - async fn withdraw_vested_coins(&self, amount: Coin) -> Result; + async fn withdraw_vested_coins( + &self, + amount: Coin, + fee: Option, + ) -> Result; async fn vesting_track_undelegation( &self, address: &str, mix_identity: IdentityKey, amount: Coin, + fee: Option, ) -> Result; async fn vesting_delegate_to_mixnode<'a>( &self, mix_identity: IdentityKeyRef<'a>, amount: &Coin, + fee: Option, ) -> Result; async fn vesting_undelegate_from_mixnode<'a>( &self, mix_identity: IdentityKeyRef<'a>, + fee: Option, ) -> Result; async fn create_periodic_vesting_account( @@ -75,18 +90,62 @@ pub trait VestingSigningClient { staking_address: Option, vesting_spec: Option, amount: Coin, + fee: Option, ) -> Result; } #[async_trait] impl VestingSigningClient for NymdClient { + async fn vesting_update_mixnode_config( + &self, + profit_margin_percent: u8, + fee: Option, + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let req = VestingExecuteMsg::UpdateMixnodeConfig { + profit_margin_percent, + }; + self.client + .execute( + self.address(), + self.vesting_contract_address()?, + &req, + fee, + "VestingContract::UpdateMixnetConfig", + vec![], + ) + .await + } + + async fn update_mixnet_address( + &self, + address: &str, + fee: Option, + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let req = VestingExecuteMsg::UpdateMixnetAddress { + address: address.to_string(), + }; + self.client + .execute( + self.address(), + self.vesting_contract_address()?, + &req, + fee, + "VestingContract::UpdateMixnetAddress", + vec![], + ) + .await + } + async fn vesting_bond_gateway( &self, gateway: Gateway, owner_signature: &str, pledge: Coin, + fee: Option, ) -> Result { - let fee = self.operation_fee(Operation::BondGateway); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = VestingExecuteMsg::BondGateway { gateway, owner_signature: owner_signature.to_string(), @@ -104,8 +163,8 @@ impl VestingSigningClient for NymdClient .await } - async fn vesting_unbond_gateway(&self) -> Result { - let fee = self.operation_fee(Operation::UnbondGateway); + async fn vesting_unbond_gateway(&self, fee: Option) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = VestingExecuteMsg::UnbondGateway {}; self.client .execute( @@ -123,8 +182,9 @@ impl VestingSigningClient for NymdClient &self, owner: &str, amount: Coin, + fee: Option, ) -> Result { - let fee = self.operation_fee(Operation::TrackUnbondGateway); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = VestingExecuteMsg::TrackUnbondGateway { owner: owner.to_string(), amount, @@ -146,8 +206,9 @@ impl VestingSigningClient for NymdClient mix_node: MixNode, owner_signature: &str, pledge: Coin, + fee: Option, ) -> Result { - let fee = self.operation_fee(Operation::BondMixnode); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = VestingExecuteMsg::BondMixnode { mix_node, owner_signature: owner_signature.to_string(), @@ -165,8 +226,8 @@ impl VestingSigningClient for NymdClient .await } - async fn vesting_unbond_mixnode(&self) -> Result { - let fee = self.operation_fee(Operation::UnbondMixnode); + async fn vesting_unbond_mixnode(&self, fee: Option) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = VestingExecuteMsg::UnbondMixnode {}; self.client .execute( @@ -184,8 +245,9 @@ impl VestingSigningClient for NymdClient &self, owner: &str, amount: Coin, + fee: Option, ) -> Result { - let fee = self.operation_fee(Operation::TrackUnbondMixnode); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = VestingExecuteMsg::TrackUnbondMixnode { owner: owner.to_string(), amount, @@ -201,9 +263,12 @@ impl VestingSigningClient for NymdClient ) .await } - - async fn withdraw_vested_coins(&self, amount: Coin) -> Result { - let fee = self.operation_fee(Operation::WithdrawVestedCoins); + async fn withdraw_vested_coins( + &self, + amount: Coin, + fee: Option, + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = VestingExecuteMsg::WithdrawVestedCoins { amount }; self.client .execute( @@ -216,14 +281,14 @@ impl VestingSigningClient for NymdClient ) .await } - async fn vesting_track_undelegation( &self, address: &str, mix_identity: IdentityKey, amount: Coin, + fee: Option, ) -> Result { - let fee = self.operation_fee(Operation::TrackUndelegation); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = VestingExecuteMsg::TrackUndelegation { owner: address.to_string(), mix_identity, @@ -244,8 +309,9 @@ impl VestingSigningClient for NymdClient &self, mix_identity: IdentityKeyRef<'a>, amount: &Coin, + fee: Option, ) -> Result { - let fee = self.operation_fee(Operation::DelegateToMixnode); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = VestingExecuteMsg::DelegateToMixnode { mix_identity: mix_identity.into(), amount: amount.clone(), @@ -261,11 +327,13 @@ impl VestingSigningClient for NymdClient ) .await } + async fn vesting_undelegate_from_mixnode<'a>( &self, mix_identity: IdentityKeyRef<'a>, + fee: Option, ) -> Result { - let fee = self.operation_fee(Operation::UndelegateFromMixnode); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = VestingExecuteMsg::UndelegateFromMixnode { mix_identity: mix_identity.into(), }; @@ -280,14 +348,16 @@ impl VestingSigningClient for NymdClient ) .await } + async fn create_periodic_vesting_account( &self, owner_address: &str, staking_address: Option, vesting_spec: Option, amount: Coin, + fee: Option, ) -> Result { - let fee = self.operation_fee(Operation::CreatePeriodicVestingAccount); + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = VestingExecuteMsg::CreateAccount { owner_address: owner_address.to_string(), staking_address, @@ -304,41 +374,4 @@ impl VestingSigningClient for NymdClient ) .await } - - async fn update_mixnet_address(&self, address: &str) -> Result { - let fee = self.operation_fee(Operation::UpdateMixnetAddress); - let req = VestingExecuteMsg::UpdateMixnetAddress { - address: address.to_string(), - }; - self.client - .execute( - self.address(), - self.vesting_contract_address()?, - &req, - fee, - "VestingContract::UpdateMixnetAddress", - vec![], - ) - .await - } - - async fn vesting_update_mixnode_config( - &self, - profit_margin_percent: u8, - ) -> Result { - let fee = self.operation_fee(Operation::UpdateMixnodeConfig); - let req = VestingExecuteMsg::UpdateMixnodeConfig { - profit_margin_percent, - }; - self.client - .execute( - self.address(), - self.vesting_contract_address()?, - &req, - fee, - "VestingContract::UpdateMixnetConfig", - vec![], - ) - .await - } } diff --git a/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs index b7ac998655..4c5fd864e9 100644 --- a/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs @@ -5,6 +5,8 @@ use cosmwasm_std::{Coin, Timestamp}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +pub use messages::{ExecuteMsg, InitMsg, MigrateMsg, QueryMsg}; + pub mod events; pub mod messages; diff --git a/common/credentials/Cargo.toml b/common/credentials/Cargo.toml index b7266becfa..a579b69a6c 100644 --- a/common/credentials/Cargo.toml +++ b/common/credentials/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] bls12_381 = { version = "0.5", default-features = false, features = ["pairings", "alloc", "experimental"] } -cosmrs = { version = "0.4.1", optional = true } +cosmrs = { version = "0.7.0", optional = true } thiserror = "1.0" url = "2.2" diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index 8f29f2236b..0429c1a093 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -828,8 +828,9 @@ dependencies = [ [[package]] name = "cosmos-sdk-proto" -version = "0.9.0" -source = "git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation#911fbe1236cfed591783ccef01018f7ccc97c496" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f109fe191e73898d74b8020c50f86018364ad19bc30318aa074616c382b52856" dependencies = [ "prost", "prost-types", @@ -838,8 +839,9 @@ dependencies = [ [[package]] name = "cosmrs" -version = "0.4.1" -source = "git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation#911fbe1236cfed591783ccef01018f7ccc97c496" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8413275b23cb5a0734d9d1e3e33f0b5b94547c1e94776dbc3149dbf46588a533" dependencies = [ "bip32", "cosmos-sdk-proto", @@ -2355,11 +2357,12 @@ dependencies = [ "headers", "http", "hyper", - "hyper-tls", - "native-tls", + "hyper-rustls", + "rustls-native-certs", "tokio", - "tokio-native-tls", + "tokio-rustls", "tower-service", + "webpki", ] [[package]] @@ -3007,7 +3010,6 @@ dependencies = [ "coconut-interface", "colored", "config", - "cosmrs", "cosmwasm-std", "dirs", "dotenv", @@ -3642,9 +3644,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.9.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" +checksum = "bc03e116981ff7d8da8e5c220e374587b98d294af7ba7dd7fda761158f00086f" dependencies = [ "bytes", "prost-derive", @@ -3652,9 +3654,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.9.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" +checksum = "7b670f45da57fb8542ebdbb6105a925fe571b67f9e7ed9f47a06a84e72b4e7cc" dependencies = [ "anyhow", "itertools", @@ -3665,9 +3667,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.9.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a" +checksum = "2d0a014229361011dc8e69c8a1ec6c2e8d0f2af7c91e3ea3f5b2170298461e68" dependencies = [ "bytes", "prost", @@ -5004,9 +5006,9 @@ dependencies = [ [[package]] name = "tendermint" -version = "0.23.3" +version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9ef686b8ecd36550d0581f0989c9d8607090b23005df479d8e6ba348b5800b9" +checksum = "3ca881fa4dedd2b46334f13be7fbc8cc1549ba4be5a833fe4e73d1a1baaf7949" dependencies = [ "async-trait", "bytes", @@ -5035,9 +5037,9 @@ dependencies = [ [[package]] name = "tendermint-config" -version = "0.23.3" +version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc127f82e7a8c7337c1f293d65a821380d3407c4c44bc979ef4da0ebc3b31ed" +checksum = "f6c56ee93f4e9b7e7daba86d171f44572e91b741084384d0ae00df7991873dfd" dependencies = [ "flex-error", "serde", @@ -5049,9 +5051,9 @@ dependencies = [ [[package]] name = "tendermint-proto" -version = "0.23.3" +version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e0a0251fd81bed7420bea0f4d91c2a58f6c9fa34d5b2f70bed0ba8890de5aa" +checksum = "b71f925d74903f4abbdc4af0110635a307b3cb05b175fdff4a7247c14a4d0874" dependencies = [ "bytes", "flex-error", @@ -5067,9 +5069,9 @@ dependencies = [ [[package]] name = "tendermint-rpc" -version = "0.23.3" +version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626c493e9ced3a9de37583bbd929f4b6dbd49aa513ab9b4776aa44a9332ce9b5" +checksum = "a13e63f57ee05a1e927887191c76d1b139de9fa40c180b9f8727ee44377242a6" dependencies = [ "async-trait", "bytes", diff --git a/nym-wallet/package.json b/nym-wallet/package.json index 7cb5729725..b6044f64b0 100644 --- a/nym-wallet/package.json +++ b/nym-wallet/package.json @@ -11,7 +11,7 @@ "tauri:build": "yarn tauri build", "tsc": "tsc --noEmit true", "tsc:watch": "tsc --noEmit true --watch", - "dev": "yarn run webpack:dev & yarn run tauri:dev", + "dev": "run-p tauri:dev webpack:dev", "prebuild": "yarn --cwd .. build", "build": "run-s webpack:prod tauri:build", "lint": "eslint src", @@ -35,6 +35,7 @@ "clsx": "^1.1.1", "date-fns": "^2.28.0", "notistack": "^2.0.3", + "npm-run-all": "^4.1.5", "qrcode.react": "^1.0.1", "react": "^17.0.2", "react-dom": "^17.0.2", diff --git a/nym-wallet/src-tauri/Cargo.toml b/nym-wallet/src-tauri/Cargo.toml index 34554fa284..82f6e2d89b 100644 --- a/nym-wallet/src-tauri/Cargo.toml +++ b/nym-wallet/src-tauri/Cargo.toml @@ -47,8 +47,7 @@ argon2 = { version = "0.3.2", features = ["std"] } base64 = "0.13" zeroize = "1.4.3" -cosmrs = { git = "https://github.com/nymtech/cosmos-rust", branch = "bugfix/account-id-length-validation", features = ["rpc", "bip32", "cosmwasm"] } -cosmwasm-std = "1.0.0-beta3" +cosmwasm-std = "1.0.0-beta8" validator-client = { path = "../../common/client-libs/validator-client", features = [ "nymd-client", diff --git a/nym-wallet/src-tauri/src/coin.rs b/nym-wallet/src-tauri/src/coin.rs index 188cd10895..3e2078c069 100644 --- a/nym-wallet/src-tauri/src/coin.rs +++ b/nym-wallet/src-tauri/src/coin.rs @@ -2,8 +2,6 @@ use crate::error::BackendError; use crate::network::Network; -use cosmrs::Decimal; -use cosmrs::Denom as CosmosDenom; use cosmwasm_std::Coin as CosmWasmCoin; use cosmwasm_std::Uint128; use serde::{Deserialize, Serialize}; @@ -11,7 +9,9 @@ use std::convert::TryFrom; use std::ops::{Add, Sub}; use std::str::FromStr; use strum::IntoEnumIterator; -use validator_client::nymd::{CosmosCoin, GasPrice}; +use validator_client::nymd::CosmosCoin; +use validator_client::nymd::Decimal; +use validator_client::nymd::Denom as CosmosDenom; #[cfg_attr(test, derive(ts_rs::TS))] #[cfg_attr(test, ts(export, export, export_to = "../src/types/rust/denom.ts"))] @@ -56,17 +56,6 @@ pub struct Coin { denom: Denom, } -impl TryFrom for Coin { - type Error = BackendError; - - fn try_from(g: GasPrice) -> Result { - Ok(Coin { - amount: g.amount.to_string(), - denom: Denom::from_str(&g.denom.to_string())?, - }) - } -} - // Allows adding minor and major denominations, output will have the LHS denom. impl Add for Coin { type Output = Self; @@ -204,7 +193,7 @@ impl From for Coin { fn from(c: CosmosCoin) -> Coin { Coin { amount: c.amount.to_string(), - denom: Denom::from_str(&c.denom.to_string()).unwrap(), + denom: Denom::from_str(c.denom.as_ref()).unwrap(), } } } @@ -222,9 +211,6 @@ impl From for Coin { mod test { use super::*; use crate::error::BackendError; - use cosmrs::Coin as CosmosCoin; - use cosmrs::Decimal; - use cosmrs::Denom as CosmosDenom; use cosmwasm_std::Coin as CosmWasmCoin; use serde_json::json; use std::convert::TryFrom; @@ -430,23 +416,4 @@ mod test { assert_eq!(Coin::minor("1") - Coin::major("1"), Coin::minor("-999999")); assert_eq!(Coin::major("1") - Coin::minor("1"), Coin::major("0.999999")); } - - #[test] - fn coin_from_gas_price() { - assert_eq!( - Coin::try_from(GasPrice::from_str("42unym").unwrap()).unwrap(), - Coin { - amount: "42".to_string(), - denom: Denom::Minor, - } - ); - - assert_eq!( - Coin::try_from(GasPrice::from_str("42nym").unwrap()).unwrap(), - Coin { - amount: "42".to_string(), - denom: Denom::Major, - } - ); - } } diff --git a/nym-wallet/src-tauri/src/config/mod.rs b/nym-wallet/src-tauri/src/config/mod.rs index ee6a7e6223..f4dc16a9c5 100644 --- a/nym-wallet/src-tauri/src/config/mod.rs +++ b/nym-wallet/src-tauri/src/config/mod.rs @@ -15,6 +15,7 @@ use std::str::FromStr; use std::{fs, io, path::PathBuf}; use strum::IntoEnumIterator; use url::Url; +use validator_client::nymd::AccountId as CosmosAccountId; pub const REMOTE_SOURCE_OF_VALIDATOR_URLS: &str = "https://nymtech.net/.wellknown/wallet/validators.json"; @@ -211,7 +212,7 @@ impl Config { .flat_map(|c| c.validators().cloned()) } - pub fn get_mixnet_contract_address(&self, network: WalletNetwork) -> Option { + pub fn get_mixnet_contract_address(&self, network: WalletNetwork) -> Option { self .base .networks @@ -221,7 +222,7 @@ impl Config { .ok() } - pub fn get_vesting_contract_address(&self, network: WalletNetwork) -> Option { + pub fn get_vesting_contract_address(&self, network: WalletNetwork) -> Option { self .base .networks @@ -234,7 +235,7 @@ impl Config { pub fn get_bandwidth_claim_contract_address( &self, network: WalletNetwork, - ) -> Option { + ) -> Option { self .base .networks diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index cb49a3974a..2b59a437fa 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -3,11 +3,13 @@ windows_subsystem = "windows" )] +use crate::menu::AddDefaultSubmenus; +use crate::operations::{mixnet, simulate, validator_api, vesting}; +use crate::state::State; use mixnet_contract_common::{Gateway, MixNode}; use std::sync::Arc; use tauri::Menu; use tokio::sync::RwLock; -use validator_client::nymd::fee::helpers::Operation; mod coin; mod config; @@ -21,13 +23,6 @@ mod state; mod utils; mod wallet_storage; -use crate::menu::AddDefaultSubmenus; -use crate::operations::mixnet; -use crate::operations::validator_api; -use crate::operations::vesting; - -use crate::state::State; - fn main() { dotenv::dotenv().ok(); setup_logging(); @@ -79,7 +74,6 @@ fn main() { state::save_config_to_files, utils::major_to_minor, utils::minor_to_major, - utils::outdated_get_approximate_fee, utils::owns_gateway, utils::owns_mixnode, utils::get_env, @@ -111,6 +105,22 @@ fn main() { vesting::queries::vesting_get_gateway_pledge, vesting::queries::vesting_get_mixnode_pledge, vesting::queries::vesting_start_time, + // simulate endpoints + simulate::admin::simulate_update_contract_settings, + simulate::mixnet::simulate_bond_gateway, + simulate::mixnet::simulate_bond_mixnode, + simulate::mixnet::simulate_unbond_gateway, + simulate::mixnet::simulate_unbond_mixnode, + simulate::mixnet::simulate_update_mixnode, + simulate::mixnet::simulate_delegate_to_mixnode, + simulate::mixnet::simulate_undelegate_from_mixnode, + simulate::cosmos::simulate_send, + simulate::vesting::simulate_vesting_bond_gateway, + simulate::vesting::simulate_vesting_bond_mixnode, + simulate::vesting::simulate_vesting_unbond_gateway, + simulate::vesting::simulate_vesting_unbond_mixnode, + simulate::vesting::simulate_vesting_update_mixnode, + simulate::vesting::simulate_withdraw_vested_coins, ]) .menu(Menu::new().add_default_app_submenu_if_macos()) .run(tauri::generate_context!()) diff --git a/nym-wallet/src-tauri/src/network.rs b/nym-wallet/src-tauri/src/network.rs index 5827792cef..6ab4098427 100644 --- a/nym-wallet/src-tauri/src/network.rs +++ b/nym-wallet/src-tauri/src/network.rs @@ -1,7 +1,6 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use cosmrs::Denom; use serde::{Deserialize, Serialize}; use std::fmt; use std::str::FromStr; @@ -9,6 +8,7 @@ use strum::EnumIter; use config::defaults::all::Network as ConfigNetwork; use config::defaults::{mainnet, qa, sandbox}; +use validator_client::nymd::Denom; #[allow(clippy::upper_case_acronyms)] #[cfg_attr(test, derive(ts_rs::TS))] diff --git a/nym-wallet/src-tauri/src/operations/mixnet/account.rs b/nym-wallet/src-tauri/src/operations/mixnet/account.rs index becbf34dc0..3e70d82fbb 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/account.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/account.rs @@ -10,7 +10,6 @@ use crate::wallet_storage::{self, DEFAULT_LOGIN_ID}; use bip39::{Language, Mnemonic}; use config::defaults::all::Network; use config::defaults::COSMOS_DERIVATION_PATH; -use cosmrs::bip32::DerivationPath; use itertools::Itertools; use rand::seq::SliceRandom; use serde::{Deserialize, Serialize}; @@ -21,9 +20,10 @@ use std::sync::Arc; use strum::IntoEnumIterator; use tokio::sync::RwLock; use url::Url; +use validator_client::nymd::bip32::DerivationPath; use validator_client::nymd::wallet::{AccountData, DirectSecp256k1HdWallet}; - -use validator_client::{nymd::SigningNymdClient, Client}; +use validator_client::nymd::{AccountId as CosmosAccountId, SigningNymdClient}; +use validator_client::Client; #[cfg_attr(test, derive(ts_rs::TS))] #[cfg_attr(test, ts(export, export_to = "../src/types/rust/account.ts"))] @@ -525,7 +525,7 @@ async fn set_state_with_all_accounts( .iter() .map(|account| { let mnemonic = account.mnemonic(); - let addresses: HashMap = WalletNetwork::iter() + let addresses: HashMap = WalletNetwork::iter() .map(|network| { let config_network: Network = network.into(); ( @@ -570,7 +570,7 @@ pub async fn remove_account_for_password( fn derive_address( mnemonic: bip39::Mnemonic, prefix: &str, -) -> Result { +) -> Result { DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic)? .try_derive_accounts()? .first() diff --git a/nym-wallet/src-tauri/src/operations/mixnet/admin.rs b/nym-wallet/src-tauri/src/operations/mixnet/admin.rs index 2f5bba28bc..34f792b08e 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/admin.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/admin.rs @@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize}; use std::convert::{TryFrom, TryInto}; use std::sync::Arc; use tokio::sync::RwLock; +use validator_client::nymd::Fee; #[cfg_attr(test, derive(ts_rs::TS))] #[cfg_attr(test, ts(export, export_to = "../src/types/rust/stateparams.ts"))] @@ -52,11 +53,12 @@ pub async fn get_contract_settings( #[tauri::command] pub async fn update_contract_settings( params: TauriContractStateParams, + fee: Option, state: tauri::State<'_, Arc>>, ) -> Result { let mixnet_contract_settings_params: ContractStateParams = params.try_into()?; nymd_client!(state) - .update_contract_settings(mixnet_contract_settings_params.clone()) + .update_contract_settings(mixnet_contract_settings_params.clone(), fee) .await?; Ok(mixnet_contract_settings_params.into()) } diff --git a/nym-wallet/src-tauri/src/operations/mixnet/bond.rs b/nym-wallet/src-tauri/src/operations/mixnet/bond.rs index fc024075a9..e1a0c11e85 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/bond.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/bond.rs @@ -7,35 +7,39 @@ use cosmwasm_std::Uint128; use mixnet_contract_common::{GatewayBond, MixNodeBond}; use std::sync::Arc; use tokio::sync::RwLock; +use validator_client::nymd::Fee; #[tauri::command] pub async fn bond_gateway( gateway: Gateway, pledge: Coin, owner_signature: String, + fee: Option, state: tauri::State<'_, Arc>>, ) -> Result<(), BackendError> { let denom = state.read().await.current_network().denom(); let pledge = pledge.into_cosmwasm_coin(&denom)?; nymd_client!(state) - .bond_gateway(gateway, owner_signature, pledge) + .bond_gateway(gateway, owner_signature, pledge, fee) .await?; Ok(()) } #[tauri::command] pub async fn unbond_gateway( + fee: Option, state: tauri::State<'_, Arc>>, ) -> Result<(), BackendError> { - nymd_client!(state).unbond_gateway().await?; + nymd_client!(state).unbond_gateway(fee).await?; Ok(()) } #[tauri::command] pub async fn unbond_mixnode( + fee: Option, state: tauri::State<'_, Arc>>, ) -> Result<(), BackendError> { - nymd_client!(state).unbond_mixnode().await?; + nymd_client!(state).unbond_mixnode(fee).await?; Ok(()) } @@ -44,12 +48,13 @@ pub async fn bond_mixnode( mixnode: MixNode, owner_signature: String, pledge: Coin, + fee: Option, state: tauri::State<'_, Arc>>, ) -> Result<(), BackendError> { let denom = state.read().await.current_network().denom(); let pledge = pledge.into_cosmwasm_coin(&denom)?; nymd_client!(state) - .bond_mixnode(mixnode, owner_signature, pledge) + .bond_mixnode(mixnode, owner_signature, pledge, fee) .await?; Ok(()) } @@ -57,10 +62,11 @@ pub async fn bond_mixnode( #[tauri::command] pub async fn update_mixnode( profit_margin_percent: u8, + fee: Option, state: tauri::State<'_, Arc>>, ) -> Result<(), BackendError> { nymd_client!(state) - .update_mixnode_config(profit_margin_percent) + .update_mixnode_config(profit_margin_percent, fee) .await?; Ok(()) } diff --git a/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs b/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs index 6f5a7dee92..a3becdfdfe 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs @@ -8,6 +8,7 @@ use cosmwasm_std::{Coin as CosmWasmCoin, Uint128}; use mixnet_contract_common::{IdentityKey, PagedDelegatorDelegationsResponse}; use std::sync::Arc; use tokio::sync::RwLock; +use validator_client::nymd::Fee; #[tauri::command] pub async fn get_pending_delegation_events( @@ -27,12 +28,13 @@ pub async fn get_pending_delegation_events( pub async fn delegate_to_mixnode( identity: &str, amount: Coin, + fee: Option, state: tauri::State<'_, Arc>>, ) -> Result { let denom = state.read().await.current_network().denom(); let delegation: CosmWasmCoin = amount.into_cosmwasm_coin(&denom)?; nymd_client!(state) - .delegate_to_mixnode(identity, &delegation) + .delegate_to_mixnode(identity, &delegation, fee) .await?; Ok(DelegationResult::new( nymd_client!(state).address().as_ref(), @@ -44,10 +46,11 @@ pub async fn delegate_to_mixnode( #[tauri::command] pub async fn undelegate_from_mixnode( identity: &str, + fee: Option, state: tauri::State<'_, Arc>>, ) -> Result { nymd_client!(state) - .remove_mixnode_delegation(identity) + .remove_mixnode_delegation(identity, fee) .await?; Ok(DelegationResult::new( nymd_client!(state).address().as_ref(), diff --git a/nym-wallet/src-tauri/src/operations/mixnet/send.rs b/nym-wallet/src-tauri/src/operations/mixnet/send.rs index 8855fa0ba6..5725f0ca55 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/send.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/send.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use std::str::FromStr; use std::sync::Arc; use tokio::sync::RwLock; -use validator_client::nymd::{AccountId, CosmosCoin, TxResponse}; +use validator_client::nymd::{AccountId, CosmosCoin, Fee, TxResponse}; #[cfg_attr(test, derive(ts_rs::TS))] #[cfg_attr(test, ts(export, export_to = "../src/types/rust/tauritxresult.ts"))] @@ -50,13 +50,14 @@ pub async fn send( address: &str, amount: Coin, memo: String, + fee: Option, state: tauri::State<'_, Arc>>, ) -> Result { let address = AccountId::from_str(address)?; let network_denom = state.read().await.current_network().denom(); let cosmos_amount: CosmosCoin = amount.clone().into_cosmos_coin(&network_denom)?; let result = nymd_client!(state) - .send(&address, vec![cosmos_amount], memo) + .send(&address, vec![cosmos_amount], memo, fee) .await?; Ok(TauriTxResult::new( result, diff --git a/nym-wallet/src-tauri/src/operations/mod.rs b/nym-wallet/src-tauri/src/operations/mod.rs index c49e27d4d2..a0720b85b0 100644 --- a/nym-wallet/src-tauri/src/operations/mod.rs +++ b/nym-wallet/src-tauri/src/operations/mod.rs @@ -1,3 +1,4 @@ pub mod mixnet; +pub mod simulate; pub mod validator_api; pub mod vesting; diff --git a/nym-wallet/src-tauri/src/operations/simulate/admin.rs b/nym-wallet/src-tauri/src/operations/simulate/admin.rs new file mode 100644 index 0000000000..52f3b4b074 --- /dev/null +++ b/nym-wallet/src-tauri/src/operations/simulate/admin.rs @@ -0,0 +1,32 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::error::BackendError; +use crate::mixnet::admin::TauriContractStateParams; +use crate::simulate::{FeeDetails, SimulateResult}; +use crate::State; +use mixnet_contract_common::{ContractStateParams, ExecuteMsg}; +use std::sync::Arc; +use tokio::sync::RwLock; + +#[tauri::command] +pub async fn simulate_update_contract_settings( + params: TauriContractStateParams, + state: tauri::State<'_, Arc>>, +) -> Result { + let guard = state.read().await; + let mixnet_contract_settings_params: ContractStateParams = params.try_into()?; + + let client = guard.current_client()?; + let mixnet_contract = client.nymd.mixnet_contract_address()?; + let gas_price = client.nymd.gas_price().clone(); + + let msg = client.nymd.wrap_contract_execute_message( + mixnet_contract, + &ExecuteMsg::UpdateContractStateParams(mixnet_contract_settings_params), + vec![], + )?; + + let result = client.nymd.simulate(vec![msg]).await?; + Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()) +} diff --git a/nym-wallet/src-tauri/src/operations/simulate/cosmos.rs b/nym-wallet/src-tauri/src/operations/simulate/cosmos.rs new file mode 100644 index 0000000000..2aaa8bda1d --- /dev/null +++ b/nym-wallet/src-tauri/src/operations/simulate/cosmos.rs @@ -0,0 +1,38 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::coin::Coin; +use crate::error::BackendError; +use crate::operations::simulate::{FeeDetails, SimulateResult}; +use crate::state::State; +use std::str::FromStr; +use std::sync::Arc; +use tokio::sync::RwLock; +use validator_client::nymd::{AccountId, MsgSend}; + +#[tauri::command] +pub async fn simulate_send( + address: &str, + amount: Coin, + state: tauri::State<'_, Arc>>, +) -> Result { + let guard = state.read().await; + + let to_address = AccountId::from_str(address)?; + let network_denom = guard.current_network().denom(); + let amount = vec![amount.clone().into_cosmos_coin(&network_denom)?]; + + let client = guard.current_client()?; + let from_address = client.nymd.address().clone(); + let gas_price = client.nymd.gas_price().clone(); + + // TODO: I'm still not 100% convinced whether this should be exposed here or handled somewhere else in the client code + let msg = MsgSend { + from_address, + to_address, + amount, + }; + + let result = client.nymd.simulate(vec![msg]).await?; + Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()) +} diff --git a/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs b/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs new file mode 100644 index 0000000000..0fe99ad11c --- /dev/null +++ b/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs @@ -0,0 +1,179 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::coin::Coin; +use crate::error::BackendError; +use crate::simulate::{FeeDetails, SimulateResult}; +use crate::State; +use mixnet_contract_common::{ExecuteMsg, Gateway, MixNode}; +use std::sync::Arc; +use tokio::sync::RwLock; + +#[tauri::command] +pub async fn simulate_bond_gateway( + gateway: Gateway, + pledge: Coin, + owner_signature: String, + state: tauri::State<'_, Arc>>, +) -> Result { + let guard = state.read().await; + let network_denom = guard.current_network().denom(); + let pledge = pledge.into_cosmos_coin(&network_denom)?; + + let client = guard.current_client()?; + let mixnet_contract = client.nymd.mixnet_contract_address()?; + let gas_price = client.nymd.gas_price().clone(); + + // TODO: I'm still not 100% convinced whether this should be exposed here or handled somewhere else in the client code + let msg = client.nymd.wrap_contract_execute_message( + mixnet_contract, + &ExecuteMsg::BondGateway { + gateway, + owner_signature, + }, + vec![pledge], + )?; + + let result = client.nymd.simulate(vec![msg]).await?; + Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()) +} + +#[tauri::command] +pub async fn simulate_unbond_gateway( + state: tauri::State<'_, Arc>>, +) -> Result { + let guard = state.read().await; + + let client = guard.current_client()?; + let mixnet_contract = client.nymd.mixnet_contract_address()?; + let gas_price = client.nymd.gas_price().clone(); + + let msg = client.nymd.wrap_contract_execute_message( + mixnet_contract, + &ExecuteMsg::UnbondGateway {}, + vec![], + )?; + + let result = client.nymd.simulate(vec![msg]).await?; + Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()) +} + +#[tauri::command] +pub async fn simulate_bond_mixnode( + mixnode: MixNode, + owner_signature: String, + pledge: Coin, + state: tauri::State<'_, Arc>>, +) -> Result { + let guard = state.read().await; + let network_denom = guard.current_network().denom(); + let pledge = pledge.into_cosmos_coin(&network_denom)?; + + let client = guard.current_client()?; + let mixnet_contract = client.nymd.mixnet_contract_address()?; + let gas_price = client.nymd.gas_price().clone(); + + let msg = client.nymd.wrap_contract_execute_message( + mixnet_contract, + &ExecuteMsg::BondMixnode { + mix_node: mixnode, + owner_signature, + }, + vec![pledge], + )?; + + let result = client.nymd.simulate(vec![msg]).await?; + Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()) +} + +#[tauri::command] +pub async fn simulate_unbond_mixnode( + state: tauri::State<'_, Arc>>, +) -> Result { + let guard = state.read().await; + + let client = guard.current_client()?; + let mixnet_contract = client.nymd.mixnet_contract_address()?; + let gas_price = client.nymd.gas_price().clone(); + + let msg = client.nymd.wrap_contract_execute_message( + mixnet_contract, + &ExecuteMsg::UnbondMixnode {}, + vec![], + )?; + + let result = client.nymd.simulate(vec![msg]).await?; + Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()) +} + +#[tauri::command] +pub async fn simulate_update_mixnode( + profit_margin_percent: u8, + state: tauri::State<'_, Arc>>, +) -> Result { + let guard = state.read().await; + + let client = guard.current_client()?; + let mixnet_contract = client.nymd.mixnet_contract_address()?; + let gas_price = client.nymd.gas_price().clone(); + + let msg = client.nymd.wrap_contract_execute_message( + mixnet_contract, + &ExecuteMsg::UpdateMixnodeConfig { + profit_margin_percent, + }, + vec![], + )?; + + let result = client.nymd.simulate(vec![msg]).await?; + Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()) +} + +#[tauri::command] +pub async fn simulate_delegate_to_mixnode( + identity: &str, + amount: Coin, + state: tauri::State<'_, Arc>>, +) -> Result { + let guard = state.read().await; + let network_denom = guard.current_network().denom(); + let delegation = amount.into_cosmos_coin(&network_denom)?; + + let client = guard.current_client()?; + let mixnet_contract = client.nymd.mixnet_contract_address()?; + let gas_price = client.nymd.gas_price().clone(); + + let msg = client.nymd.wrap_contract_execute_message( + mixnet_contract, + &ExecuteMsg::DelegateToMixnode { + mix_identity: identity.to_string(), + }, + vec![delegation], + )?; + + let result = client.nymd.simulate(vec![msg]).await?; + Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()) +} + +#[tauri::command] +pub async fn simulate_undelegate_from_mixnode( + identity: &str, + state: tauri::State<'_, Arc>>, +) -> Result { + let guard = state.read().await; + + let client = guard.current_client()?; + let mixnet_contract = client.nymd.mixnet_contract_address()?; + let gas_price = client.nymd.gas_price().clone(); + + let msg = client.nymd.wrap_contract_execute_message( + mixnet_contract, + &ExecuteMsg::UndelegateFromMixnode { + mix_identity: identity.to_string(), + }, + vec![], + )?; + + let result = client.nymd.simulate(vec![msg]).await?; + Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()) +} diff --git a/nym-wallet/src-tauri/src/operations/simulate/mod.rs b/nym-wallet/src-tauri/src/operations/simulate/mod.rs new file mode 100644 index 0000000000..d0f333ca4e --- /dev/null +++ b/nym-wallet/src-tauri/src/operations/simulate/mod.rs @@ -0,0 +1,65 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::coin::Coin; +use serde::{Deserialize, Serialize}; +use validator_client::nymd::cosmwasm_client::types::GasInfo; +use validator_client::nymd::{tx, CosmosCoin, Fee, GasPrice}; + +pub mod admin; +pub mod cosmos; +pub mod mixnet; +pub mod vesting; + +// technically we could have also exposed a result: Option field from the SimulateResponse, +// but in the context of the wallet it's really irrelevant and useless for the time being +pub(crate) struct SimulateResult { + // As I mentioned somewhere before, from what I've seen in manual testing, + // gas estimation does not exist if transaction itself fails to get executed. + // for example if you attempt to send a 'BondMixnode' with invalid signature + pub gas_info: Option, + pub gas_price: GasPrice, +} + +impl SimulateResult { + pub fn new(gas_info: Option, gas_price: GasPrice) -> Self { + SimulateResult { + gas_info, + gas_price, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct FeeDetails { + // expected to be used by the wallet in order to display detailed fee information to the user + pub amount: Option, + pub fee: Fee, +} + +impl SimulateResult { + pub fn detailed_fee(&self) -> FeeDetails { + FeeDetails { + amount: self.to_fee_amount().map(Into::into), + fee: self.to_fee(), + } + } + + fn to_fee_amount(&self) -> Option { + self + .gas_info + .map(|gas_info| &self.gas_price * gas_info.gas_used) + } + + fn to_fee(&self) -> Fee { + self + .to_fee_amount() + .and_then(|fee_amount| { + self.gas_info.map(|gas_info| { + let gas_limit = gas_info.gas_used; + tx::Fee::from_amount_and_gas(fee_amount, gas_limit).into() + }) + }) + .unwrap_or_default() + } +} diff --git a/nym-wallet/src-tauri/src/operations/simulate/vesting.rs b/nym-wallet/src-tauri/src/operations/simulate/vesting.rs new file mode 100644 index 0000000000..df969cf300 --- /dev/null +++ b/nym-wallet/src-tauri/src/operations/simulate/vesting.rs @@ -0,0 +1,155 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::coin::Coin; +use crate::error::BackendError; +use crate::simulate::{FeeDetails, SimulateResult}; +use crate::State; +use mixnet_contract_common::{Gateway, MixNode}; +use std::sync::Arc; +use tokio::sync::RwLock; +use vesting_contract_common::ExecuteMsg; + +#[tauri::command] +pub async fn simulate_vesting_bond_gateway( + gateway: Gateway, + pledge: Coin, + owner_signature: String, + state: tauri::State<'_, Arc>>, +) -> Result { + let guard = state.read().await; + let network_denom = guard.current_network().denom(); + let pledge = pledge.into_cosmwasm_coin(&network_denom)?; + + let client = guard.current_client()?; + let vesting_contract = client.nymd.vesting_contract_address()?; + let gas_price = client.nymd.gas_price().clone(); + + let msg = client.nymd.wrap_contract_execute_message( + vesting_contract, + &ExecuteMsg::BondGateway { + gateway, + owner_signature, + amount: pledge, + }, + vec![], + )?; + + let result = client.nymd.simulate(vec![msg]).await?; + Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()) +} + +#[tauri::command] +pub async fn simulate_vesting_unbond_gateway( + state: tauri::State<'_, Arc>>, +) -> Result { + let guard = state.read().await; + + let client = guard.current_client()?; + let vesting_contract = client.nymd.vesting_contract_address()?; + let gas_price = client.nymd.gas_price().clone(); + + let msg = client.nymd.wrap_contract_execute_message( + vesting_contract, + &ExecuteMsg::UnbondGateway {}, + vec![], + )?; + + let result = client.nymd.simulate(vec![msg]).await?; + Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()) +} + +#[tauri::command] +pub async fn simulate_vesting_bond_mixnode( + mixnode: MixNode, + owner_signature: String, + pledge: Coin, + state: tauri::State<'_, Arc>>, +) -> Result { + let guard = state.read().await; + let network_denom = guard.current_network().denom(); + let pledge = pledge.into_cosmwasm_coin(&network_denom)?; + + let client = guard.current_client()?; + let vesting_contract = client.nymd.vesting_contract_address()?; + let gas_price = client.nymd.gas_price().clone(); + + let msg = client.nymd.wrap_contract_execute_message( + vesting_contract, + &ExecuteMsg::BondMixnode { + mix_node: mixnode, + owner_signature, + amount: pledge, + }, + vec![], + )?; + + let result = client.nymd.simulate(vec![msg]).await?; + Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()) +} + +#[tauri::command] +pub async fn simulate_vesting_unbond_mixnode( + state: tauri::State<'_, Arc>>, +) -> Result { + let guard = state.read().await; + + let client = guard.current_client()?; + let vesting_contract = client.nymd.vesting_contract_address()?; + let gas_price = client.nymd.gas_price().clone(); + + let msg = client.nymd.wrap_contract_execute_message( + vesting_contract, + &ExecuteMsg::UnbondMixnode {}, + vec![], + )?; + + let result = client.nymd.simulate(vec![msg]).await?; + Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()) +} + +#[tauri::command] +pub async fn simulate_vesting_update_mixnode( + profit_margin_percent: u8, + state: tauri::State<'_, Arc>>, +) -> Result { + let guard = state.read().await; + + let client = guard.current_client()?; + let vesting_contract = client.nymd.vesting_contract_address()?; + let gas_price = client.nymd.gas_price().clone(); + + let msg = client.nymd.wrap_contract_execute_message( + vesting_contract, + &ExecuteMsg::UpdateMixnodeConfig { + profit_margin_percent, + }, + vec![], + )?; + + let result = client.nymd.simulate(vec![msg]).await?; + Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()) +} + +#[tauri::command] +pub async fn simulate_withdraw_vested_coins( + amount: Coin, + state: tauri::State<'_, Arc>>, +) -> Result { + let guard = state.read().await; + let network_denom = guard.current_network().denom(); + let amount = amount.into_cosmwasm_coin(&network_denom)?; + + let client = guard.current_client()?; + let vesting_contract = client.nymd.vesting_contract_address()?; + let gas_price = client.nymd.gas_price().clone(); + + let msg = client.nymd.wrap_contract_execute_message( + vesting_contract, + &ExecuteMsg::WithdrawVestedCoins { amount }, + vec![], + )?; + + let result = client.nymd.simulate(vec![msg]).await?; + Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()) +} diff --git a/nym-wallet/src-tauri/src/operations/vesting/bond.rs b/nym-wallet/src-tauri/src/operations/vesting/bond.rs index 1739f2e98a..0cfdb654d9 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/bond.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/bond.rs @@ -3,39 +3,41 @@ use crate::error::BackendError; use crate::nymd_client; use crate::state::State; use crate::{Gateway, MixNode}; - use std::sync::Arc; use tokio::sync::RwLock; -use validator_client::nymd::VestingSigningClient; +use validator_client::nymd::{Fee, VestingSigningClient}; #[tauri::command] pub async fn vesting_bond_gateway( gateway: Gateway, pledge: Coin, owner_signature: String, + fee: Option, state: tauri::State<'_, Arc>>, ) -> Result<(), BackendError> { let denom = state.read().await.current_network().denom(); let pledge = pledge.into_cosmwasm_coin(&denom)?; nymd_client!(state) - .vesting_bond_gateway(gateway, &owner_signature, pledge) + .vesting_bond_gateway(gateway, &owner_signature, pledge, fee) .await?; Ok(()) } #[tauri::command] pub async fn vesting_unbond_gateway( + fee: Option, state: tauri::State<'_, Arc>>, ) -> Result<(), BackendError> { - nymd_client!(state).vesting_unbond_gateway().await?; + nymd_client!(state).vesting_unbond_gateway(fee).await?; Ok(()) } #[tauri::command] pub async fn vesting_unbond_mixnode( + fee: Option, state: tauri::State<'_, Arc>>, ) -> Result<(), BackendError> { - nymd_client!(state).vesting_unbond_mixnode().await?; + nymd_client!(state).vesting_unbond_mixnode(fee).await?; Ok(()) } @@ -44,12 +46,13 @@ pub async fn vesting_bond_mixnode( mixnode: MixNode, owner_signature: String, pledge: Coin, + fee: Option, state: tauri::State<'_, Arc>>, ) -> Result<(), BackendError> { let denom = state.read().await.current_network().denom(); let pledge = pledge.into_cosmwasm_coin(&denom)?; nymd_client!(state) - .vesting_bond_mixnode(mixnode, &owner_signature, pledge) + .vesting_bond_mixnode(mixnode, &owner_signature, pledge, fee) .await?; Ok(()) } @@ -57,21 +60,25 @@ pub async fn vesting_bond_mixnode( #[tauri::command] pub async fn withdraw_vested_coins( amount: Coin, + fee: Option, state: tauri::State<'_, Arc>>, ) -> Result<(), BackendError> { let denom = state.read().await.current_network().denom(); let amount = amount.into_cosmwasm_coin(&denom)?; - nymd_client!(state).withdraw_vested_coins(amount).await?; + nymd_client!(state) + .withdraw_vested_coins(amount, fee) + .await?; Ok(()) } #[tauri::command] pub async fn vesting_update_mixnode( profit_margin_percent: u8, + fee: Option, state: tauri::State<'_, Arc>>, ) -> Result<(), BackendError> { nymd_client!(state) - .vesting_update_mixnode_config(profit_margin_percent) + .vesting_update_mixnode_config(profit_margin_percent, fee) .await?; Ok(()) } diff --git a/nym-wallet/src-tauri/src/operations/vesting/delegate.rs b/nym-wallet/src-tauri/src/operations/vesting/delegate.rs index 266573e3dc..854b418609 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/delegate.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/delegate.rs @@ -6,7 +6,7 @@ use crate::utils::DelegationEvent; use crate::utils::DelegationResult; use std::sync::Arc; use tokio::sync::RwLock; -use validator_client::nymd::VestingSigningClient; +use validator_client::nymd::{Fee, VestingSigningClient}; #[tauri::command] pub async fn get_pending_vesting_delegation_events( @@ -33,12 +33,13 @@ pub async fn get_pending_vesting_delegation_events( pub async fn vesting_delegate_to_mixnode( identity: &str, amount: Coin, + fee: Option, state: tauri::State<'_, Arc>>, ) -> Result { let denom = state.read().await.current_network().denom(); let delegation = amount.into_cosmwasm_coin(&denom)?; nymd_client!(state) - .vesting_delegate_to_mixnode(identity, &delegation) + .vesting_delegate_to_mixnode(identity, &delegation, fee) .await?; Ok(DelegationResult::new( nymd_client!(state).address().as_ref(), @@ -50,10 +51,11 @@ pub async fn vesting_delegate_to_mixnode( #[tauri::command] pub async fn vesting_undelegate_from_mixnode( identity: &str, + fee: Option, state: tauri::State<'_, Arc>>, ) -> Result { nymd_client!(state) - .vesting_undelegate_from_mixnode(identity) + .vesting_undelegate_from_mixnode(identity, fee) .await?; Ok(DelegationResult::new( nymd_client!(state).address().as_ref(), diff --git a/nym-wallet/src-tauri/src/state.rs b/nym-wallet/src-tauri/src/state.rs index a94a599a89..cf889718e8 100644 --- a/nym-wallet/src-tauri/src/state.rs +++ b/nym-wallet/src-tauri/src/state.rs @@ -3,7 +3,7 @@ use crate::network::Network; use crate::{config, network_config}; use strum::IntoEnumIterator; -use validator_client::nymd::SigningNymdClient; +use validator_client::nymd::{AccountId as CosmosAccountId, SigningNymdClient}; use validator_client::Client; use itertools::Itertools; @@ -61,7 +61,7 @@ pub(crate) struct WalletAccountIds { // The wallet account id pub id: crate::wallet_storage::AccountId, // The set of corresponding network identities derived from the mnemonic - pub addresses: HashMap, + pub addresses: HashMap, } impl State { diff --git a/nym-wallet/src-tauri/src/utils.rs b/nym-wallet/src-tauri/src/utils.rs index b56fd67805..9a28277e3e 100644 --- a/nym-wallet/src-tauri/src/utils.rs +++ b/nym-wallet/src-tauri/src/utils.rs @@ -2,7 +2,6 @@ use crate::coin::{Coin, Denom}; use crate::error::BackendError; use crate::nymd_client; use crate::state::State; -use crate::Operation; use mixnet_contract_common::mixnode::DelegationEvent as ContractDelegationEvent; use mixnet_contract_common::mixnode::PendingUndelegate as ContractPendingUndelegate; use mixnet_contract_common::Delegation; @@ -70,21 +69,6 @@ pub async fn owns_gateway( ) } -// NOTE: this uses OUTDATED defaults that might have no resemblance with the reality -// as for the actual transaction, the gas cost is being simulated beforehand -#[tauri::command] -pub async fn outdated_get_approximate_fee( - operation: Operation, - state: tauri::State<'_, Arc>>, -) -> Result { - let approximate_fee = operation.default_fee(nymd_client!(state).gas_price()); - let mut coin = Coin::new("0", &Denom::Major); - for f in approximate_fee.amount { - coin = coin + f.into(); - } - Ok(coin) -} - #[cfg_attr(test, derive(ts_rs::TS))] #[cfg_attr(test, ts(export, export_to = "../src/types/rust/delegationresult.ts"))] #[derive(Serialize, Deserialize)] 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 a43a7f5e5c..10149948e9 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/account_data.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/account_data.rs @@ -14,8 +14,8 @@ // 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 cosmrs::bip32::DerivationPath; use serde::{Deserialize, Serialize}; +use validator_client::nymd::bip32::DerivationPath; use zeroize::Zeroize; use crate::error::BackendError; @@ -348,8 +348,8 @@ impl Drop for MnemonicAccount { } mod display_hd_path { - use cosmrs::bip32::DerivationPath; use serde::{Deserialize, Deserializer, Serializer}; + use validator_client::nymd::bip32::DerivationPath; pub fn serialize( hd_path: &DerivationPath, diff --git a/nym-wallet/src-tauri/src/wallet_storage/mod.rs b/nym-wallet/src-tauri/src/wallet_storage/mod.rs index cc886e9f2b..9427471ff9 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/mod.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/mod.rs @@ -14,9 +14,9 @@ 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 cosmrs::bip32::DerivationPath; use std::fs::{self, create_dir_all, OpenOptions}; use std::path::{Path, PathBuf}; +use validator_client::nymd::bip32::DerivationPath; #[cfg(test)] use self::account_data::MnemonicAccount; diff --git a/nym-wallet/src/requests/queries.ts b/nym-wallet/src/requests/queries.ts index b653d88297..cd3b03da46 100644 --- a/nym-wallet/src/requests/queries.ts +++ b/nym-wallet/src/requests/queries.ts @@ -60,8 +60,10 @@ export const checkGatewayOwnership = async (): Promise => { // NOTE: this uses OUTDATED defaults that might have no resemblance with the reality // as for the actual transaction, the gas cost is being simulated beforehand export const getGasFee = async (operation: Operation): Promise => { - const res: Coin = await invoke('outdated_get_approximate_fee', { operation }); - return res; + // THIS NO LONGER EXISTS : ) + // const res: Coin = await invoke('outdated_get_approximate_fee', { operation }); + // return res; + return new Promise(((resolve, reject) => reject())) }; export const getInclusionProbability = async (identity: string): Promise => { diff --git a/service-providers/network-requester/src/core.rs b/service-providers/network-requester/src/core.rs index dd17a7cf38..315854a9da 100644 --- a/service-providers/network-requester/src/core.rs +++ b/service-providers/network-requester/src/core.rs @@ -33,6 +33,7 @@ pub struct ServiceProvider { db_path: PathBuf, outbound_request_filter: OutboundRequestFilter, open_proxy: bool, + enable_statistics: bool, } impl ServiceProvider { @@ -40,6 +41,7 @@ impl ServiceProvider { listening_address: String, description: String, open_proxy: bool, + enable_statistics: bool, ) -> ServiceProvider { let allowed_hosts = HostsStore::new( HostsStore::default_base_dir(), @@ -65,6 +67,7 @@ impl ServiceProvider { db_path, outbound_request_filter, open_proxy, + enable_statistics, } } @@ -73,14 +76,16 @@ impl ServiceProvider { async fn mixnet_response_listener( mut websocket_writer: SplitSink, mut mix_reader: mpsc::UnboundedReceiver<(Response, Recipient)>, - response_stats_data: &Arc>, + response_stats_data: &Option>>, ) { // TODO: wire SURBs in here once they're available while let Some((response, return_address)) = mix_reader.next().await { - response_stats_data - .write() - .await - .processed(return_address.identity(), response.data.len() as u32); + if let Some(response_stats_data) = response_stats_data { + response_stats_data + .write() + .await + .processed(return_address.identity(), response.data.len() as u32); + } // make 'request' to native-websocket client let response_message = ClientRequest::Send { recipient: return_address, @@ -226,7 +231,7 @@ impl ServiceProvider { raw_request: &[u8], controller_sender: &mut ControllerSender, mix_input_sender: &mpsc::UnboundedSender<(Response, Recipient)>, - request_stats_data: &Arc>, + request_stats_data: &Option>>, connected_clients: &mut HashMap, ) { let deserialized_msg = match Socks5Message::try_from_bytes(raw_request) { @@ -239,7 +244,9 @@ impl ServiceProvider { match deserialized_msg { Socks5Message::Request(deserialized_request) => match deserialized_request { Request::Connect(req) => { - connected_clients.insert(req.conn_id, *req.return_address.identity()); + if self.enable_statistics { + connected_clients.insert(req.conn_id, *req.return_address.identity()); + } self.handle_proxy_connect( controller_sender, mix_input_sender, @@ -250,11 +257,13 @@ impl ServiceProvider { } Request::Send(conn_id, data, closed) => { - if let Some(client_identity) = connected_clients.get(&conn_id) { - request_stats_data - .write() - .await - .processed(client_identity, data.len() as u32); + if let Some(request_stats_data) = request_stats_data { + if let Some(client_identity) = connected_clients.get(&conn_id) { + request_stats_data + .write() + .await + .processed(client_identity, data.len() as u32); + } } self.handle_proxy_send(controller_sender, conn_id, data, closed) } @@ -297,15 +306,20 @@ impl ServiceProvider { active_connections_controller.run().await; }); - let mut stats = Statistics::new(self.description.clone(), interval, timer_receiver) - .await - .expect("Statistics controller could not be bootstrapped"); - let request_stats_data = Arc::clone(stats.request_data()); - let response_stats_data = Arc::clone(stats.response_data()); - let mix_input_sender_clone = mix_input_sender.clone(); - tokio::spawn(async move { - stats.run(&mix_input_sender_clone).await; - }); + let mut request_stats_data = None; + let mut response_stats_data = None; + + if self.enable_statistics { + let mut stats = Statistics::new(self.description.clone(), interval, timer_receiver) + .await + .expect("Statistics controller could not be bootstrapped"); + request_stats_data = Some(Arc::clone(stats.request_data())); + response_stats_data = Some(Arc::clone(stats.response_data())); + let mix_input_sender_clone = mix_input_sender.clone(); + tokio::spawn(async move { + stats.run(&mix_input_sender_clone).await; + }); + } #[cfg(feature = "stats-service")] let storage = crate::storage::NetworkRequesterStorage::init(self.db_path.as_path()) diff --git a/service-providers/network-requester/src/main.rs b/service-providers/network-requester/src/main.rs index 935f37b6c1..a7a988ebc2 100644 --- a/service-providers/network-requester/src/main.rs +++ b/service-providers/network-requester/src/main.rs @@ -16,6 +16,7 @@ mod websocket; const OPEN_PROXY_ARG: &str = "open-proxy"; const WS_PORT: &str = "websocket-port"; const DESCRIPTION: &str = "description"; +const ENABLE_STATISTICS: &str = "enable-statistics"; fn parse_args<'a>() -> ArgMatches<'a> { App::new("Nym Network Requester") @@ -34,13 +35,18 @@ fn parse_args<'a>() -> ArgMatches<'a> { .short("p") .takes_value(true), ) + .arg( + Arg::with_name(ENABLE_STATISTICS) + .help("enable mixnet statistics that get sent to a Nym server") + .long(ENABLE_STATISTICS) + .requires(DESCRIPTION), + ) .arg( Arg::with_name(DESCRIPTION) .help("service description") .long(DESCRIPTION) .short("d") - .takes_value(true) - .default_value("undefined"), + .takes_value(true), ) .get_matches() } @@ -55,6 +61,11 @@ async fn main() { println!("\n\nYOU HAVE STARTED IN 'OPEN PROXY' MODE. ANYONE WITH YOUR CLIENT ADDRESS CAN MAKE REQUESTS FROM YOUR MACHINE. PLEASE QUIT IF YOU DON'T UNDERSTAND WHAT YOU'RE DOING.\n\n"); } + let enable_statistics = matches.is_present(ENABLE_STATISTICS); + if enable_statistics { + println!("\n\nTHE NETWORK REQUESTER STATISTICS ARE ENABLED. IT WILL COLLECT AND SEND STATISTICS TO A NYM SERVER. PLEASE QUIT IF YOU DON'T WANT THIS TO HAPPEN AND START WITHOUT THE {} FLAG .\n\n", ENABLE_STATISTICS); + } + let uri = format!( "ws://localhost:{}", matches @@ -62,9 +73,12 @@ async fn main() { .unwrap_or(&DEFAULT_WEBSOCKET_LISTENING_PORT.to_string()) ); - let description = matches.value_of(DESCRIPTION).unwrap().to_string(); + let description = matches + .value_of(DESCRIPTION) + .unwrap_or("undefined") + .to_string(); println!("Starting socks5 service provider:"); - let mut server = core::ServiceProvider::new(uri, description, open_proxy); + let mut server = core::ServiceProvider::new(uri, description, open_proxy, enable_statistics); server.run().await; } diff --git a/validator-api/src/nymd_client.rs b/validator-api/src/nymd_client.rs index 8ece9ec6aa..7de1c49be6 100644 --- a/validator-api/src/nymd_client.rs +++ b/validator-api/src/nymd_client.rs @@ -272,7 +272,12 @@ impl Client { where C: SigningCosmWasmClient + Sync, { - self.0.write().await.nymd.advance_current_epoch().await?; + self.0 + .write() + .await + .nymd + .advance_current_epoch(None) + .await?; Ok(()) } @@ -281,7 +286,7 @@ impl Client { where C: SigningCosmWasmClient + Sync, { - self.0.write().await.nymd.checkpoint_mixnodes().await?; + self.0.write().await.nymd.checkpoint_mixnodes(None).await?; Ok(()) } @@ -290,7 +295,12 @@ impl Client { where C: SigningCosmWasmClient + Sync, { - self.0.write().await.nymd.reconcile_delegations().await?; + self.0 + .write() + .await + .nymd + .reconcile_delegations(None) + .await?; Ok(()) } @@ -307,7 +317,7 @@ impl Client { .write() .await .nymd - .write_rewarded_set(rewarded_set, expected_active_set_size) + .write_rewarded_set(rewarded_set, expected_active_set_size, None) .await?; Ok(()) }