Broken, but compilable first stage of post-merging
This commit is contained in:
@@ -9,17 +9,27 @@
|
||||
- wallet: added support for multiple accounts ([#1265])
|
||||
- wallet: the wallet backend learned how to keep track of validator name, either hardcoded or by querying the status endpoint.
|
||||
- mixnet-contract: Replace all naked `-` with `saturating_sub`.
|
||||
- mixnet-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292])
|
||||
- vesting-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292])
|
||||
- validator-api: add Swagger to document the REST API ([#1249]).
|
||||
- validator-api: add `estimated_node_profit` and `estimated_operator_cost` to `reward-estimate` endpoint ([#1284])
|
||||
- all: added network compilation target to `--help` (or `--version`) commands ([#1256]).
|
||||
- network-requester: send traffic statistics from all network requesters and receive it in a special network-requester that aggregates the data and exposes it via a rest API ([#1267], [#1278]).
|
||||
|
||||
### Fixed
|
||||
|
||||
- mixnet-contract: replaced integer division with fixed for performance calculations ([#1284])
|
||||
- mixnet-contract: delegator and operator rewards use lambda and sigma instead of lambda_ticked and sigma_ticked ([#1284])
|
||||
- mixnet-contract: `estimated_delegator_reward` calculation ([#1284])
|
||||
- vesting-contract: replaced `checked_sub` with `saturating_sub` to fix the underflow in `get_vesting_tokens` ([#1275])
|
||||
- mixnet-contract: removed `expect` in `query_delegator_reward` and queries containing invalid proxy address should now return a more human-readable error ([#1257])
|
||||
- mixnet-contract: Under certain circumstances nodes could not be unbonded ([#1255](https://github.com/nymtech/nym/issues/1255)) ([#1258])
|
||||
- mixnode, gateway: attempting to determine reconnection backoff to persistently failing mixnode could result in a crash ([#1260])
|
||||
|
||||
### Changed
|
||||
|
||||
- validator-client: created internal `Coin` type that replaces coins from `cosmrs` and `cosmwasm` for API entrypoints [[#1295]]
|
||||
|
||||
[#1258]: https://github.com/nymtech/nym/pull/1258
|
||||
[#1249]: https://github.com/nymtech/nym/pull/1249
|
||||
[#1256]: https://github.com/nymtech/nym/pull/1256
|
||||
@@ -29,6 +39,9 @@
|
||||
[#1267]: https://github.com/nymtech/nym/pull/1267
|
||||
[#1275]: https://github.com/nymtech/nym/pull/1275
|
||||
[#1278]: https://github.com/nymtech/nym/pull/1278
|
||||
[#1284]: https://github.com/nymtech/nym/pull/1284
|
||||
[#1292]: https://github.com/nymtech/nym/pull/1292
|
||||
[#1295]: https://github.com/nymtech/nym/pull/1295
|
||||
|
||||
## [nym-wallet-v1.0.4](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.4) (2022-05-04)
|
||||
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bip39::Mnemonic;
|
||||
use coconut_bandwidth_contract_common::deposit::DepositData;
|
||||
use std::str::FromStr;
|
||||
use url::Url;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::{CONTRACT_ADDRESS, MNEMONIC, NYMD_URL};
|
||||
|
||||
use bip39::Mnemonic;
|
||||
use coconut_bandwidth_contract_common::deposit::DepositData;
|
||||
use coconut_bandwidth_contract_common::msg::ExecuteMsg;
|
||||
use network_defaults::DEFAULT_NETWORK;
|
||||
use validator_client::nymd::{
|
||||
AccountId, CosmosCoin, Decimal, Denom, NymdClient, SigningNymdClient,
|
||||
};
|
||||
use std::str::FromStr;
|
||||
use url::Url;
|
||||
use validator_client::nymd::{AccountId, Coin, Denom, NymdClient, SigningNymdClient};
|
||||
|
||||
pub(crate) struct Client {
|
||||
nymd_client: NymdClient<SigningNymdClient>,
|
||||
@@ -55,10 +51,7 @@ impl Client {
|
||||
let req = ExecuteMsg::DepositFunds {
|
||||
data: DepositData::new(info.to_string(), verification_key, encryption_key),
|
||||
};
|
||||
let funds = vec![CosmosCoin {
|
||||
denom: self.denom.clone(),
|
||||
amount: Decimal::from(amount),
|
||||
}];
|
||||
let funds = vec![Coin::new(amount as u128, self.denom.to_string())];
|
||||
Ok(self
|
||||
.nymd_client
|
||||
.execute(&self.contract_address, &req, Default::default(), "", funds)
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
pub use cosmrs::Coin as CosmosCoin;
|
||||
pub use cosmwasm_std::Coin as CosmWasmCoin;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Default, Debug, PartialEq)]
|
||||
pub struct MismatchedDenoms;
|
||||
|
||||
// the reason the coin is created here as opposed to different place in the codebase is that
|
||||
// eventually we want to either publish the cosmwasm client separately or commit it to
|
||||
// some other project, like cosmrs. Either way, in that case we can't really have
|
||||
// a dependency on an internal type
|
||||
#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq)]
|
||||
pub struct Coin {
|
||||
pub amount: u128,
|
||||
pub denom: String,
|
||||
}
|
||||
|
||||
impl Coin {
|
||||
pub fn new<S: Into<String>>(amount: u128, denom: S) -> Self {
|
||||
Coin {
|
||||
amount,
|
||||
denom: denom.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_add(&self, other: &Self) -> Result<Self, MismatchedDenoms> {
|
||||
if self.denom != other.denom {
|
||||
Err(MismatchedDenoms)
|
||||
} else {
|
||||
Ok(Coin {
|
||||
amount: self.amount + other.amount,
|
||||
denom: self.denom.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Coin {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}{}", self.amount, self.denom)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Coin> for CosmosCoin {
|
||||
fn from(coin: Coin) -> Self {
|
||||
assert!(
|
||||
coin.amount <= u64::MAX as u128,
|
||||
"the coin amount is higher than the maximum supported by cosmrs"
|
||||
);
|
||||
|
||||
CosmosCoin {
|
||||
denom: coin
|
||||
.denom
|
||||
.parse()
|
||||
.expect("the coin should have had a valid denom!"),
|
||||
amount: (coin.amount as u64).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CosmosCoin> for Coin {
|
||||
fn from(coin: CosmosCoin) -> Self {
|
||||
Coin {
|
||||
amount: coin
|
||||
.amount
|
||||
.to_string()
|
||||
.parse()
|
||||
.expect("somehow failed to parse string representation of u64"),
|
||||
denom: coin.denom.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Coin> for CosmWasmCoin {
|
||||
fn from(coin: Coin) -> Self {
|
||||
CosmWasmCoin::new(coin.amount, coin.denom)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CosmWasmCoin> for Coin {
|
||||
fn from(coin: CosmWasmCoin) -> Self {
|
||||
Coin {
|
||||
amount: coin.amount.u128(),
|
||||
denom: coin.denom,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CoinConverter {
|
||||
type Target;
|
||||
|
||||
fn convert_coin(&self) -> Self::Target;
|
||||
}
|
||||
|
||||
impl CoinConverter for CosmosCoin {
|
||||
type Target = CosmWasmCoin;
|
||||
|
||||
fn convert_coin(&self) -> Self::Target {
|
||||
CosmWasmCoin::new(
|
||||
self.amount
|
||||
.to_string()
|
||||
.parse()
|
||||
.expect("cosmos coin had an invalid amount assigned"),
|
||||
self.denom.to_string(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl CoinConverter for CosmWasmCoin {
|
||||
type Target = CosmosCoin;
|
||||
|
||||
fn convert_coin(&self) -> Self::Target {
|
||||
assert!(
|
||||
self.amount.u128() <= u64::MAX as u128,
|
||||
"the coin amount is higher than the maximum supported by cosmrs"
|
||||
);
|
||||
|
||||
CosmosCoin {
|
||||
denom: self
|
||||
.denom
|
||||
.parse()
|
||||
.expect("cosmwasm coin had an invalid amount assigned"),
|
||||
amount: (self.amount.u128() as u64).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::nymd::coin::Coin;
|
||||
use crate::nymd::cosmwasm_client::helpers::{create_pagination, next_page_key};
|
||||
use crate::nymd::cosmwasm_client::types::{
|
||||
Account, Code, CodeDetails, Contract, ContractCodeHistoryEntry, ContractCodeId,
|
||||
@@ -25,8 +26,7 @@ use cosmrs::rpc::{self, HttpClient, Order};
|
||||
use cosmrs::tendermint::abci::Code as AbciCode;
|
||||
use cosmrs::tendermint::abci::Transaction;
|
||||
use cosmrs::tendermint::{abci, block, chain};
|
||||
use cosmrs::{tx, AccountId, Coin, Denom, Tx};
|
||||
use cosmwasm_std::Coin as CosmWasmCoin;
|
||||
use cosmrs::{tx, AccountId, Coin as CosmosCoin, Denom, Tx};
|
||||
use prost::Message;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
@@ -135,7 +135,7 @@ pub trait CosmWasmClient: rpc::Client {
|
||||
.await?;
|
||||
|
||||
res.balance
|
||||
.map(TryFrom::try_from)
|
||||
.map(|proto| CosmosCoin::try_from(proto).map(Into::into))
|
||||
.transpose()
|
||||
.map_err(|_| NymdError::SerializationError("Coin".to_owned()))
|
||||
}
|
||||
@@ -166,16 +166,12 @@ pub trait CosmWasmClient: rpc::Client {
|
||||
|
||||
raw_balances
|
||||
.into_iter()
|
||||
.map(TryFrom::try_from)
|
||||
.map(|proto| CosmosCoin::try_from(proto).map(Into::into))
|
||||
.collect::<Result<_, _>>()
|
||||
.map_err(|_| NymdError::SerializationError("Coins".to_owned()))
|
||||
}
|
||||
|
||||
// this is annoyingly and inconsistently returning `Vec<CosmWasmCoin>` rather than
|
||||
// Vec<Coin>, since cosmrs::Coin can't deal with IBC denoms.
|
||||
// Presumably after https://github.com/cosmos/cosmos-rust/issues/173 is resolved,
|
||||
// the code could be adjusted
|
||||
async fn get_total_supply(&self) -> Result<Vec<CosmWasmCoin>, NymdError> {
|
||||
async fn get_total_supply(&self) -> Result<Vec<Coin>, NymdError> {
|
||||
let path = Some("/cosmos.bank.v1beta1.Query/TotalSupply".parse().unwrap());
|
||||
|
||||
let mut supply = Vec::new();
|
||||
@@ -198,12 +194,7 @@ pub trait CosmWasmClient: rpc::Client {
|
||||
|
||||
supply
|
||||
.into_iter()
|
||||
.map(|coin| {
|
||||
coin.amount.parse().map(|amount| CosmWasmCoin {
|
||||
denom: coin.denom,
|
||||
amount,
|
||||
})
|
||||
})
|
||||
.map(|proto| CosmosCoin::try_from(proto).map(Into::into))
|
||||
.collect::<Result<_, _>>()
|
||||
.map_err(|_| NymdError::SerializationError("Coins".to_owned()))
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ 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 crate::nymd::{Coin, GasPrice, TxResponse};
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::bank::MsgSend;
|
||||
use cosmrs::distribution::MsgWithdrawDelegatorReward;
|
||||
@@ -17,7 +17,7 @@ use cosmrs::rpc::endpoint::broadcast;
|
||||
use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl, SimpleRequest};
|
||||
use cosmrs::staking::{MsgDelegate, MsgUndelegate};
|
||||
use cosmrs::tx::{self, Msg, SignDoc, SignerInfo};
|
||||
use cosmrs::{cosmwasm, rpc, AccountId, Any, Coin, Tx};
|
||||
use cosmrs::{cosmwasm, rpc, AccountId, Any, Tx};
|
||||
use log::debug;
|
||||
use serde::Serialize;
|
||||
use sha2::Digest;
|
||||
@@ -310,7 +310,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
||||
sender: sender_address.clone(),
|
||||
contract: contract_address.clone(),
|
||||
msg: serde_json::to_vec(msg)?,
|
||||
funds,
|
||||
funds: funds.into_iter().map(Into::into).collect(),
|
||||
}
|
||||
.to_any()
|
||||
.map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned()))?;
|
||||
@@ -349,7 +349,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
||||
sender: sender_address.clone(),
|
||||
contract: contract_address.clone(),
|
||||
msg: serde_json::to_vec(&msg)?,
|
||||
funds,
|
||||
funds: funds.into_iter().map(Into::into).collect(),
|
||||
}
|
||||
.to_any()
|
||||
.map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned()))
|
||||
@@ -382,7 +382,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
||||
let send_msg = MsgSend {
|
||||
from_address: sender_address.clone(),
|
||||
to_address: recipient_address.clone(),
|
||||
amount,
|
||||
amount: amount.into_iter().map(Into::into).collect(),
|
||||
}
|
||||
.to_any()
|
||||
.map_err(|_| NymdError::SerializationError("MsgSend".to_owned()))?;
|
||||
@@ -408,7 +408,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
||||
MsgSend {
|
||||
from_address: sender_address.clone(),
|
||||
to_address,
|
||||
amount,
|
||||
amount: amount.into_iter().map(Into::into).collect(),
|
||||
}
|
||||
.to_any()
|
||||
.map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned()))
|
||||
@@ -431,7 +431,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
||||
let delegate_msg = MsgDelegate {
|
||||
delegator_address: delegator_address.to_owned(),
|
||||
validator_address: validator_address.to_owned(),
|
||||
amount,
|
||||
amount: amount.into(),
|
||||
}
|
||||
.to_any()
|
||||
.map_err(|_| NymdError::SerializationError("MsgDelegate".to_owned()))?;
|
||||
@@ -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,
|
||||
amount: amount.into(),
|
||||
}
|
||||
.to_any()
|
||||
.map_err(|_| NymdError::SerializationError("MsgUndelegate".to_owned()))?;
|
||||
|
||||
@@ -28,7 +28,7 @@ use cosmrs::proto::cosmwasm::wasm::v1::{
|
||||
use cosmrs::tendermint::abci::Data;
|
||||
use cosmrs::tendermint::{abci, chain};
|
||||
use cosmrs::tx::{AccountNumber, Gas, SequenceNumber};
|
||||
use cosmrs::{tx, AccountId, Any, Coin};
|
||||
use cosmrs::{tx, AccountId, Any, Coin as CosmosCoin};
|
||||
use prost::Message;
|
||||
use serde::Serialize;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
@@ -107,9 +107,9 @@ impl TryFrom<ProtoModuleAccount> for ModuleAccount {
|
||||
#[derive(Debug)]
|
||||
pub struct BaseVestingAccount {
|
||||
pub base_account: Option<BaseAccount>,
|
||||
pub original_vesting: Vec<Coin>,
|
||||
pub delegated_free: Vec<Coin>,
|
||||
pub delegated_vesting: Vec<Coin>,
|
||||
pub original_vesting: Vec<CosmosCoin>,
|
||||
pub delegated_free: Vec<CosmosCoin>,
|
||||
pub delegated_vesting: Vec<CosmosCoin>,
|
||||
pub end_time: i64,
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ impl TryFrom<ProtoDelayedVestingAccount> for DelayedVestingAccount {
|
||||
#[derive(Debug)]
|
||||
pub struct Period {
|
||||
pub length: i64,
|
||||
pub amount: Vec<Coin>,
|
||||
pub amount: Vec<CosmosCoin>,
|
||||
}
|
||||
|
||||
impl TryFrom<ProtoPeriod> for Period {
|
||||
@@ -628,7 +628,7 @@ pub struct InstantiateOptions {
|
||||
/// created and before the instantiation message is executed by the contract.
|
||||
///
|
||||
/// Only native tokens are supported.
|
||||
pub funds: Vec<Coin>,
|
||||
pub funds: Vec<CosmosCoin>,
|
||||
|
||||
/// A bech32 encoded address of an admin account.
|
||||
/// Caution: an admin has the privilege to upgrade a contract.
|
||||
@@ -636,6 +636,15 @@ pub struct InstantiateOptions {
|
||||
pub admin: Option<AccountId>,
|
||||
}
|
||||
|
||||
impl InstantiateOptions {
|
||||
pub fn new<T: Into<CosmosCoin>>(funds: Vec<T>, admin: Option<AccountId>) -> Self {
|
||||
InstantiateOptions {
|
||||
funds: funds.into_iter().map(Into::into).collect(),
|
||||
admin,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InstantiateResult {
|
||||
/// The address of the newly instantiated contract
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use crate::nymd::error::NymdError;
|
||||
use config::defaults;
|
||||
use cosmrs::tx::Gas;
|
||||
use cosmrs::{Coin, Denom};
|
||||
use cosmrs::Coin;
|
||||
use cosmwasm_std::{Decimal, Fraction, Uint128};
|
||||
use std::ops::Mul;
|
||||
use std::str::FromStr;
|
||||
@@ -13,11 +13,12 @@ use std::str::FromStr;
|
||||
/// the smallest fee token unit, such as 0.012utoken.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct GasPrice {
|
||||
// I really hate the combination of cosmwasm Decimal with cosmos-sdk Denom,
|
||||
// but cosmos-sdk's Decimal is too basic for our needs
|
||||
// I really dislike the usage of cosmwasm Decimal, but I didn't feel like implementing
|
||||
// our own maths subcrate just for the purposes of calculating gas requirements
|
||||
// this should definitely be rectified later on
|
||||
pub amount: Decimal,
|
||||
|
||||
pub denom: Denom,
|
||||
pub denom: String,
|
||||
}
|
||||
|
||||
impl<'a> Mul<Gas> for &'a GasPrice {
|
||||
@@ -44,7 +45,10 @@ impl<'a> Mul<Gas> for &'a GasPrice {
|
||||
|
||||
assert!(amount.u128() <= u64::MAX as u128);
|
||||
Coin {
|
||||
denom: self.denom.clone(),
|
||||
denom: self
|
||||
.denom
|
||||
.parse()
|
||||
.expect("the gas price has been created with invalid denom"),
|
||||
amount: (amount.u128() as u64).into(),
|
||||
}
|
||||
}
|
||||
@@ -63,9 +67,7 @@ impl FromStr for GasPrice {
|
||||
.parse()
|
||||
.map_err(|_| NymdError::MalformedGasPrice)?;
|
||||
let possible_denom = s.chars().skip(amount_len).collect::<String>();
|
||||
let denom = possible_denom
|
||||
.parse()
|
||||
.map_err(|_| NymdError::MalformedGasPrice)?;
|
||||
let denom = possible_denom.trim().to_string();
|
||||
|
||||
Ok(GasPrice { amount, denom })
|
||||
}
|
||||
@@ -106,7 +108,14 @@ mod tests {
|
||||
);
|
||||
|
||||
assert!(".25upunk".parse::<GasPrice>().is_err());
|
||||
assert!("0.025 upunk".parse::<GasPrice>().is_err());
|
||||
|
||||
assert_eq!(
|
||||
"0.025upunk".parse::<GasPrice>().unwrap(),
|
||||
"0.025 upunk".parse().unwrap()
|
||||
);
|
||||
|
||||
let gas: GasPrice = "0.025 upunk ".parse().unwrap();
|
||||
assert_eq!("upunk", gas.denom);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -9,10 +9,11 @@ use crate::nymd::cosmwasm_client::types::{
|
||||
use crate::nymd::error::NymdError;
|
||||
use crate::nymd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER;
|
||||
use crate::nymd::wallet::DirectSecp256k1HdWallet;
|
||||
use cosmrs::cosmwasm;
|
||||
use cosmrs::rpc::Error as TendermintRpcError;
|
||||
use cosmrs::rpc::HttpClientUrl;
|
||||
use cosmrs::tx::Msg;
|
||||
use cosmwasm_std::{Coin, Uint128};
|
||||
use cosmwasm_std::Uint128;
|
||||
pub use fee::gas_price::GasPrice;
|
||||
use mixnet_contract_common::mixnode::DelegationEvent;
|
||||
use mixnet_contract_common::{
|
||||
@@ -28,8 +29,8 @@ 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;
|
||||
pub use coin::Coin;
|
||||
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;
|
||||
@@ -43,9 +44,11 @@ pub use cosmrs::tendermint::Time as TendermintTime;
|
||||
pub use cosmrs::tx::{self, Gas};
|
||||
pub use cosmrs::Coin as CosmosCoin;
|
||||
pub use cosmrs::{bip32, AccountId, Decimal, Denom};
|
||||
pub use cosmwasm_std::Coin as CosmWasmCoin;
|
||||
pub use signing_client::Client as SigningNymdClient;
|
||||
pub use traits::{VestingQueryClient, VestingSigningClient};
|
||||
|
||||
pub mod coin;
|
||||
pub mod cosmwasm_client;
|
||||
pub mod error;
|
||||
pub mod fee;
|
||||
@@ -175,7 +178,7 @@ impl<C> NymdClient<C> {
|
||||
&self,
|
||||
contract_address: &AccountId,
|
||||
msg: &M,
|
||||
funds: Vec<CosmosCoin>,
|
||||
funds: Vec<Coin>,
|
||||
) -> Result<cosmwasm::MsgExecuteContract, NymdError>
|
||||
where
|
||||
C: SigningCosmWasmClient,
|
||||
@@ -185,7 +188,7 @@ impl<C> NymdClient<C> {
|
||||
sender: self.address().clone(),
|
||||
contract: contract_address.clone(),
|
||||
msg: serde_json::to_vec(msg)?,
|
||||
funds,
|
||||
funds: funds.into_iter().map(Into::into).collect(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -275,7 +278,7 @@ impl<C> NymdClient<C> {
|
||||
&self,
|
||||
address: &AccountId,
|
||||
denom: Denom,
|
||||
) -> Result<Option<CosmosCoin>, NymdError>
|
||||
) -> Result<Option<Coin>, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
@@ -650,7 +653,7 @@ impl<C> NymdClient<C> {
|
||||
pub async fn send(
|
||||
&self,
|
||||
recipient: &AccountId,
|
||||
amount: Vec<CosmosCoin>,
|
||||
amount: Vec<Coin>,
|
||||
memo: impl Into<String> + Send + 'static,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<TxResponse, NymdError>
|
||||
@@ -666,7 +669,7 @@ impl<C> NymdClient<C> {
|
||||
/// Send funds from one address to multiple others
|
||||
pub async fn send_multiple(
|
||||
&self,
|
||||
msgs: Vec<(AccountId, Vec<CosmosCoin>)>,
|
||||
msgs: Vec<(AccountId, Vec<Coin>)>,
|
||||
memo: impl Into<String> + Send + 'static,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<TxResponse, NymdError>
|
||||
@@ -685,7 +688,7 @@ impl<C> NymdClient<C> {
|
||||
msg: &M,
|
||||
fee: Fee,
|
||||
memo: impl Into<String> + Send + 'static,
|
||||
funds: Vec<CosmosCoin>,
|
||||
funds: Vec<Coin>,
|
||||
) -> Result<ExecuteResult, NymdError>
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
@@ -705,7 +708,7 @@ impl<C> NymdClient<C> {
|
||||
) -> Result<ExecuteResult, NymdError>
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
I: IntoIterator<Item = (M, Vec<CosmosCoin>)> + Send,
|
||||
I: IntoIterator<Item = (M, Vec<Coin>)> + Send,
|
||||
M: Serialize,
|
||||
{
|
||||
self.client
|
||||
@@ -796,6 +799,89 @@ impl<C> NymdClient<C> {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn compound_operator_reward(
|
||||
&self,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
{
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let req = ExecuteMsg::CompoundOperatorReward {};
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
&req,
|
||||
fee,
|
||||
"MixnetContract::CompoundOperatorReward",
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn claim_operator_reward(&self, fee: Option<Fee>) -> Result<ExecuteResult, NymdError>
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
{
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let req = ExecuteMsg::ClaimOperatorReward {};
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
&req,
|
||||
fee,
|
||||
"MixnetContract::ClaimOperatorReward",
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn compound_delegator_reward(
|
||||
&self,
|
||||
mix_identity: IdentityKey,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
{
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let req = ExecuteMsg::CompoundDelegatorReward { mix_identity };
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
&req,
|
||||
fee,
|
||||
"MixnetContract::CompoundDelegatorReward",
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn claim_delegator_reward(
|
||||
&self,
|
||||
mix_identity: IdentityKey,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
{
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let req = ExecuteMsg::ClaimDelegatorReward { mix_identity };
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
&req,
|
||||
fee,
|
||||
"MixnetContract::ClaimDelegatorReward",
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Announce a mixnode, paying a fee.
|
||||
pub async fn bond_mixnode(
|
||||
&self,
|
||||
@@ -820,7 +906,7 @@ impl<C> NymdClient<C> {
|
||||
&req,
|
||||
fee,
|
||||
"Bonding mixnode from rust!",
|
||||
vec![cosmwasm_coin_to_cosmos_coin(pledge)],
|
||||
vec![pledge],
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -851,7 +937,7 @@ impl<C> NymdClient<C> {
|
||||
&req,
|
||||
fee,
|
||||
"Bonding mixnode on behalf from rust!",
|
||||
vec![cosmwasm_coin_to_cosmos_coin(pledge)],
|
||||
vec![pledge],
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -867,7 +953,7 @@ impl<C> NymdClient<C> {
|
||||
{
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
|
||||
let reqs: Vec<(ExecuteMsg, Vec<CosmosCoin>)> = mixnode_bonds_with_sigs
|
||||
let reqs: Vec<(ExecuteMsg, Vec<Coin>)> = mixnode_bonds_with_sigs
|
||||
.into_iter()
|
||||
.map(|(bond, owner_signature)| {
|
||||
(
|
||||
@@ -876,7 +962,7 @@ impl<C> NymdClient<C> {
|
||||
owner: bond.owner.to_string(),
|
||||
owner_signature,
|
||||
},
|
||||
vec![cosmwasm_coin_to_cosmos_coin(bond.pledge_amount)],
|
||||
vec![bond.pledge_amount.into()],
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
@@ -907,7 +993,7 @@ impl<C> NymdClient<C> {
|
||||
&req,
|
||||
fee,
|
||||
"Unbonding mixnode from rust!",
|
||||
Vec::new(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -931,7 +1017,7 @@ impl<C> NymdClient<C> {
|
||||
&req,
|
||||
fee,
|
||||
"Unbonding mixnode on behalf from rust!",
|
||||
Vec::new(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -957,7 +1043,7 @@ impl<C> NymdClient<C> {
|
||||
&req,
|
||||
fee,
|
||||
"Updating mixnode configuration from rust!",
|
||||
Vec::new(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -966,7 +1052,7 @@ impl<C> NymdClient<C> {
|
||||
pub async fn delegate_to_mixnode(
|
||||
&self,
|
||||
mix_identity: &str,
|
||||
amount: &Coin,
|
||||
amount: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>
|
||||
where
|
||||
@@ -984,7 +1070,7 @@ impl<C> NymdClient<C> {
|
||||
&req,
|
||||
fee,
|
||||
"Delegating to mixnode from rust!",
|
||||
vec![cosmwasm_coin_ptr_to_cosmos_coin(amount)],
|
||||
vec![amount],
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -995,7 +1081,7 @@ impl<C> NymdClient<C> {
|
||||
&self,
|
||||
mix_identity: &str,
|
||||
delegate: &str,
|
||||
amount: &Coin,
|
||||
amount: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>
|
||||
where
|
||||
@@ -1014,7 +1100,7 @@ impl<C> NymdClient<C> {
|
||||
&req,
|
||||
fee,
|
||||
"Delegating to mixnode on behalf from rust!",
|
||||
vec![cosmwasm_coin_ptr_to_cosmos_coin(amount)],
|
||||
vec![amount],
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -1030,7 +1116,7 @@ impl<C> NymdClient<C> {
|
||||
{
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
|
||||
let reqs: Vec<(ExecuteMsg, Vec<CosmosCoin>)> = mixnode_delegations
|
||||
let reqs: Vec<(ExecuteMsg, Vec<Coin>)> = mixnode_delegations
|
||||
.into_iter()
|
||||
.map(|delegation| {
|
||||
(
|
||||
@@ -1038,7 +1124,7 @@ impl<C> NymdClient<C> {
|
||||
mix_identity: delegation.node_identity(),
|
||||
delegate: delegation.owner().to_string(),
|
||||
},
|
||||
vec![cosmwasm_coin_to_cosmos_coin(delegation.amount().clone())],
|
||||
vec![delegation.amount().clone().into()],
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
@@ -1075,7 +1161,7 @@ impl<C> NymdClient<C> {
|
||||
&req,
|
||||
fee,
|
||||
"Removing mixnode delegation from rust!",
|
||||
Vec::new(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -1103,7 +1189,7 @@ impl<C> NymdClient<C> {
|
||||
&req,
|
||||
fee,
|
||||
"Removing mixnode delegation on behalf from rust!",
|
||||
Vec::new(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -1132,7 +1218,7 @@ impl<C> NymdClient<C> {
|
||||
&req,
|
||||
fee,
|
||||
"Bonding gateway from rust!",
|
||||
vec![cosmwasm_coin_to_cosmos_coin(pledge)],
|
||||
vec![pledge],
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -1163,7 +1249,7 @@ impl<C> NymdClient<C> {
|
||||
&req,
|
||||
fee,
|
||||
"Bonding gateway on behalf from rust!",
|
||||
vec![cosmwasm_coin_to_cosmos_coin(pledge)],
|
||||
vec![pledge],
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -1179,7 +1265,7 @@ impl<C> NymdClient<C> {
|
||||
{
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
|
||||
let reqs: Vec<(ExecuteMsg, Vec<CosmosCoin>)> = gateway_bonds_with_sigs
|
||||
let reqs: Vec<(ExecuteMsg, Vec<Coin>)> = gateway_bonds_with_sigs
|
||||
.into_iter()
|
||||
.map(|(bond, owner_signature)| {
|
||||
(
|
||||
@@ -1188,7 +1274,7 @@ impl<C> NymdClient<C> {
|
||||
owner: bond.owner.to_string(),
|
||||
owner_signature,
|
||||
},
|
||||
vec![cosmwasm_coin_to_cosmos_coin(bond.pledge_amount)],
|
||||
vec![bond.pledge_amount.into()],
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
@@ -1219,7 +1305,7 @@ impl<C> NymdClient<C> {
|
||||
&req,
|
||||
fee,
|
||||
"Unbonding gateway from rust!",
|
||||
Vec::new(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -1244,7 +1330,7 @@ impl<C> NymdClient<C> {
|
||||
&req,
|
||||
fee,
|
||||
"Unbonding gateway on behalf from rust!",
|
||||
Vec::new(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -1267,7 +1353,7 @@ impl<C> NymdClient<C> {
|
||||
&req,
|
||||
fee,
|
||||
"Updating contract state from rust!",
|
||||
Vec::new(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -1286,7 +1372,7 @@ impl<C> NymdClient<C> {
|
||||
&req,
|
||||
fee,
|
||||
"Advance current epoch",
|
||||
Vec::new(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -1305,7 +1391,7 @@ impl<C> NymdClient<C> {
|
||||
&req,
|
||||
fee,
|
||||
"Reconciling delegation events",
|
||||
Vec::new(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -1324,7 +1410,7 @@ impl<C> NymdClient<C> {
|
||||
&req,
|
||||
fee,
|
||||
"Snapshotting mixnodes",
|
||||
Vec::new(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -1351,24 +1437,8 @@ impl<C> NymdClient<C> {
|
||||
&req,
|
||||
fee,
|
||||
"Writing rewarded set",
|
||||
Vec::new(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
fn cosmwasm_coin_to_cosmos_coin(coin: Coin) -> CosmosCoin {
|
||||
CosmosCoin {
|
||||
denom: coin.denom.parse().unwrap(),
|
||||
// this might be a bit iffy, cosmwasm coin stores value as u128, while cosmos does it as u64
|
||||
amount: (coin.amount.u128() as u64).into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cosmwasm_coin_ptr_to_cosmos_coin(coin: &Coin) -> CosmosCoin {
|
||||
CosmosCoin {
|
||||
denom: coin.denom.parse().unwrap(),
|
||||
// this might be a bit iffy, cosmwasm coin stores value as u128, while cosmos does it as u64
|
||||
amount: (coin.amount.u128() as u64).into(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::nymd::coin::Coin;
|
||||
pub use crate::nymd::cosmwasm_client::client::CosmWasmClient;
|
||||
use crate::nymd::error::NymdError;
|
||||
use crate::nymd::NymdClient;
|
||||
use async_trait::async_trait;
|
||||
use cosmwasm_std::{Coin, Timestamp};
|
||||
use cosmwasm_std::{Coin as CosmWasmCoin, Timestamp};
|
||||
use vesting_contract::vesting::Account;
|
||||
use vesting_contract_common::{
|
||||
messages::QueryMsg as VestingQueryMsg, OriginalVestingResponse, Period, PledgeData,
|
||||
@@ -83,8 +84,9 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
block_time,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address()?, &request)
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address()?, &request)
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
async fn spendable_coins(
|
||||
@@ -97,8 +99,9 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
block_time,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address()?, &request)
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address()?, &request)
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
async fn vested_coins(
|
||||
&self,
|
||||
@@ -110,8 +113,9 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
block_time,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address()?, &request)
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address()?, &request)
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
async fn vesting_coins(
|
||||
&self,
|
||||
@@ -123,8 +127,9 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
block_time,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address()?, &request)
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address()?, &request)
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
async fn vesting_start_time(
|
||||
@@ -173,8 +178,9 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
block_time,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address()?, &request)
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address()?, &request)
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
/// Returns the total amount of delegated tokens that have vested
|
||||
@@ -188,8 +194,9 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
block_time,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address()?, &request)
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address()?, &request)
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
async fn get_account(&self, address: &str) -> Result<Account, NymdError> {
|
||||
|
||||
@@ -4,14 +4,35 @@
|
||||
pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient;
|
||||
use crate::nymd::cosmwasm_client::types::ExecuteResult;
|
||||
use crate::nymd::error::NymdError;
|
||||
use crate::nymd::{cosmwasm_coin_to_cosmos_coin, Fee, NymdClient};
|
||||
use crate::nymd::{Coin, Fee, NymdClient};
|
||||
use async_trait::async_trait;
|
||||
use cosmwasm_std::Coin;
|
||||
use mixnet_contract_common::{Gateway, IdentityKey, IdentityKeyRef, MixNode};
|
||||
use vesting_contract_common::messages::{ExecuteMsg as VestingExecuteMsg, VestingSpecification};
|
||||
|
||||
#[async_trait]
|
||||
pub trait VestingSigningClient {
|
||||
async fn vesting_claim_operator_reward(
|
||||
&self,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>;
|
||||
|
||||
async fn vesting_claim_delegator_reward(
|
||||
&self,
|
||||
mix_identity: IdentityKey,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>;
|
||||
|
||||
async fn vesting_compound_operator_reward(
|
||||
&self,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>;
|
||||
|
||||
async fn vesting_compound_delegator_reward(
|
||||
&self,
|
||||
mix_identity: IdentityKey,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>;
|
||||
|
||||
async fn vesting_update_mixnode_config(
|
||||
&self,
|
||||
profix_margin_percent: u8,
|
||||
@@ -74,7 +95,7 @@ pub trait VestingSigningClient {
|
||||
async fn vesting_delegate_to_mixnode<'a>(
|
||||
&self,
|
||||
mix_identity: IdentityKeyRef<'a>,
|
||||
amount: &Coin,
|
||||
amount: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>;
|
||||
|
||||
@@ -149,7 +170,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
let req = VestingExecuteMsg::BondGateway {
|
||||
gateway,
|
||||
owner_signature: owner_signature.to_string(),
|
||||
amount: pledge,
|
||||
amount: pledge.into(),
|
||||
};
|
||||
self.client
|
||||
.execute(
|
||||
@@ -187,7 +208,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let req = VestingExecuteMsg::TrackUnbondGateway {
|
||||
owner: owner.to_string(),
|
||||
amount,
|
||||
amount: amount.into(),
|
||||
};
|
||||
self.client
|
||||
.execute(
|
||||
@@ -212,7 +233,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
let req = VestingExecuteMsg::BondMixnode {
|
||||
mix_node,
|
||||
owner_signature: owner_signature.to_string(),
|
||||
amount: pledge,
|
||||
amount: pledge.into(),
|
||||
};
|
||||
self.client
|
||||
.execute(
|
||||
@@ -250,7 +271,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let req = VestingExecuteMsg::TrackUnbondMixnode {
|
||||
owner: owner.to_string(),
|
||||
amount,
|
||||
amount: amount.into(),
|
||||
};
|
||||
self.client
|
||||
.execute(
|
||||
@@ -269,7 +290,9 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let req = VestingExecuteMsg::WithdrawVestedCoins { amount };
|
||||
let req = VestingExecuteMsg::WithdrawVestedCoins {
|
||||
amount: amount.into(),
|
||||
};
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
@@ -292,7 +315,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
let req = VestingExecuteMsg::TrackUndelegation {
|
||||
owner: address.to_string(),
|
||||
mix_identity,
|
||||
amount,
|
||||
amount: amount.into(),
|
||||
};
|
||||
self.client
|
||||
.execute(
|
||||
@@ -308,13 +331,13 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
async fn vesting_delegate_to_mixnode<'a>(
|
||||
&self,
|
||||
mix_identity: IdentityKeyRef<'a>,
|
||||
amount: &Coin,
|
||||
amount: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let req = VestingExecuteMsg::DelegateToMixnode {
|
||||
mix_identity: mix_identity.into(),
|
||||
amount: amount.clone(),
|
||||
amount: amount.into(),
|
||||
};
|
||||
self.client
|
||||
.execute(
|
||||
@@ -370,7 +393,81 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::CreatePeriodicVestingAccount",
|
||||
vec![cosmwasm_coin_to_cosmos_coin(amount)],
|
||||
vec![amount],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn vesting_claim_operator_reward(
|
||||
&self,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let req = VestingExecuteMsg::ClaimOperatorReward {};
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address()?,
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::ClaimOperatorReward",
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn vesting_compound_operator_reward(
|
||||
&self,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let req = VestingExecuteMsg::CompoundOperatorReward {};
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address()?,
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::CompoundOperatorReward",
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn vesting_claim_delegator_reward(
|
||||
&self,
|
||||
mix_identity: IdentityKey,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let req = VestingExecuteMsg::ClaimDelegatorReward { mix_identity };
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address()?,
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::ClaimDelegatorReward",
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn vesting_compound_delegator_reward(
|
||||
&self,
|
||||
mix_identity: IdentityKey,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let req = VestingExecuteMsg::CompoundDelegatorReward { mix_identity };
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address()?,
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::CompoundDelegatorReward",
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -23,7 +23,9 @@ pub const CHANGE_REWARDED_SET_EVENT_TYPE: &str = "change_rewarded_set";
|
||||
pub const ADVANCE_INTERVAL_EVENT_TYPE: &str = "advance_interval";
|
||||
pub const ADVANCE_EPOCH_EVENT_TYPE: &str = "advance_epoch";
|
||||
pub const COMPOUND_DELEGATOR_REWARD_EVENT_TYPE: &str = "compound_delegator_reward";
|
||||
pub const CLAIM_DELEGATOR_REWARD_EVENT_TYPE: &str = "claim_delegator_reward";
|
||||
pub const COMPOUND_OPERATOR_REWARD_EVENT_TYPE: &str = "compound_operator_reward";
|
||||
pub const CLAIM_OPERATOR_REWARD_EVENT_TYPE: &str = "claim_operator_reward";
|
||||
pub const SNAPSHOT_MIXNODES_EVENT: &str = "snapshot_mixnodes";
|
||||
|
||||
// attributes that are used in multiple places
|
||||
@@ -151,6 +153,11 @@ pub fn new_compound_operator_reward_event(owner: &Addr, amount: Uint128) -> Even
|
||||
event.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_claim_operator_reward_event(owner: &Addr, amount: Uint128) -> Event {
|
||||
let event = Event::new(CLAIM_OPERATOR_REWARD_EVENT_TYPE).add_attribute(OWNER_KEY, owner);
|
||||
event.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_compound_delegator_reward_event(
|
||||
delegator: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
@@ -171,6 +178,26 @@ pub fn new_compound_delegator_reward_event(
|
||||
.add_attribute(DELEGATOR_KEY, delegator)
|
||||
}
|
||||
|
||||
pub fn new_claim_delegator_reward_event(
|
||||
delegator: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
amount: Uint128,
|
||||
mix_identity: IdentityKeyRef<'_>,
|
||||
) -> Event {
|
||||
let mut event =
|
||||
Event::new(CLAIM_DELEGATOR_REWARD_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator);
|
||||
|
||||
if let Some(proxy) = proxy {
|
||||
event = event.add_attribute(PROXY_KEY, proxy)
|
||||
}
|
||||
|
||||
// coin implements Display trait and we use that implementation here
|
||||
event
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
.add_attribute(DELEGATION_TARGET_KEY, mix_identity)
|
||||
.add_attribute(DELEGATOR_KEY, delegator)
|
||||
}
|
||||
|
||||
pub fn new_undelegation_event(
|
||||
delegator: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
|
||||
@@ -310,6 +310,14 @@ impl NodeRewardResult {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RewardEstimate {
|
||||
pub total_node_reward: u64,
|
||||
pub operator_reward: u64,
|
||||
pub delegators_reward: u64,
|
||||
pub node_profit: u64,
|
||||
pub operator_cost: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct MixNodeBond {
|
||||
pub pledge_amount: Coin,
|
||||
@@ -402,63 +410,80 @@ impl MixNodeBond {
|
||||
/ U128::from_num(circulating_supply)
|
||||
}
|
||||
|
||||
pub fn lambda_ticked(&self, params: &RewardParams) -> U128 {
|
||||
// Ratio of a bond to the token circulating supply
|
||||
self.lambda(params).min(params.one_over_k())
|
||||
}
|
||||
|
||||
pub fn lambda(&self, params: &RewardParams) -> U128 {
|
||||
// Ratio of a bond to the token circulating supply
|
||||
let pledge_to_circulating_supply_ratio =
|
||||
self.pledge_to_circulating_supply(params.circulating_supply());
|
||||
pledge_to_circulating_supply_ratio.min(params.one_over_k())
|
||||
self.pledge_to_circulating_supply(params.circulating_supply())
|
||||
}
|
||||
|
||||
pub fn sigma_ticked(&self, params: &RewardParams) -> U128 {
|
||||
// Ratio of a delegation to the the token circulating supply
|
||||
self.sigma(params).min(params.one_over_k())
|
||||
}
|
||||
|
||||
pub fn sigma(&self, params: &RewardParams) -> U128 {
|
||||
// Ratio of a delegation to the the token circulating supply
|
||||
let total_bond_to_circulating_supply_ratio =
|
||||
self.total_bond_to_circulating_supply(params.circulating_supply());
|
||||
total_bond_to_circulating_supply_ratio.min(params.one_over_k())
|
||||
self.total_bond_to_circulating_supply(params.circulating_supply())
|
||||
}
|
||||
|
||||
pub fn estimate_reward(
|
||||
&self,
|
||||
params: &RewardParams,
|
||||
) -> Result<(u64, u64, u64), MixnetContractError> {
|
||||
) -> Result<RewardEstimate, MixnetContractError> {
|
||||
let total_node_reward = self
|
||||
.reward(params)
|
||||
.reward()
|
||||
.checked_to_num::<u128>()
|
||||
.unwrap_or_default();
|
||||
let node_profit = self
|
||||
.node_profit(params)
|
||||
.checked_to_num::<u128>()
|
||||
.unwrap_or_default();
|
||||
let operator_cost = params
|
||||
.node
|
||||
.operator_cost()
|
||||
.checked_to_num::<u128>()
|
||||
.unwrap_or_default();
|
||||
let operator_reward = self.operator_reward(params);
|
||||
// Total reward has to be the sum of operator and delegator rewards
|
||||
let delegators_reward = total_node_reward - operator_reward;
|
||||
let delegators_reward = node_profit - operator_reward;
|
||||
|
||||
Ok((
|
||||
total_node_reward.try_into()?,
|
||||
operator_reward.try_into()?,
|
||||
delegators_reward.try_into()?,
|
||||
))
|
||||
Ok(RewardEstimate {
|
||||
total_node_reward: total_node_reward.try_into()?,
|
||||
operator_reward: operator_reward.try_into()?,
|
||||
delegators_reward: delegators_reward.try_into()?,
|
||||
node_profit: node_profit.try_into()?,
|
||||
operator_cost: operator_cost.try_into()?,
|
||||
})
|
||||
}
|
||||
|
||||
// keybase://chat/nymtech#dev-core/14473
|
||||
pub fn reward(&self, params: &RewardParams) -> NodeRewardResult {
|
||||
let lambda = self.lambda(params);
|
||||
let sigma = self.sigma(params);
|
||||
let lambda_ticked = self.lambda_ticked(params);
|
||||
let sigma_ticked = self.sigma_ticked(params);
|
||||
|
||||
let reward = params.performance()
|
||||
* params.epoch_reward_pool()
|
||||
* (sigma * params.omega()
|
||||
+ params.alpha() * lambda * sigma * params.rewarded_set_size())
|
||||
* (sigma_ticked * params.omega()
|
||||
+ params.alpha() * lambda_ticked * sigma_ticked * params.rewarded_set_size())
|
||||
/ (ONE + params.alpha());
|
||||
|
||||
// we only need regular lambda and sigma to calculate operator and delegator rewards
|
||||
NodeRewardResult {
|
||||
reward,
|
||||
lambda,
|
||||
sigma,
|
||||
lambda: self.lambda(params),
|
||||
sigma: self.sigma(params),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn node_profit(&self, params: &RewardParams) -> U128 {
|
||||
if self.reward(params).reward() < params.node.operator_cost() {
|
||||
U128::from_num(0u128)
|
||||
} else {
|
||||
self.reward(params).reward() - params.node.operator_cost()
|
||||
}
|
||||
self.reward(params)
|
||||
.reward()
|
||||
.saturating_sub(params.node.operator_cost())
|
||||
}
|
||||
|
||||
pub fn operator_reward(&self, params: &RewardParams) -> u128 {
|
||||
@@ -466,11 +491,9 @@ impl MixNodeBond {
|
||||
if reward.sigma == 0 {
|
||||
return 0;
|
||||
}
|
||||
let profit = if reward.reward < params.node.operator_cost() {
|
||||
U128::from_num(0u128)
|
||||
} else {
|
||||
reward.reward - params.node.operator_cost()
|
||||
};
|
||||
|
||||
let profit = reward.reward.saturating_sub(params.node.operator_cost());
|
||||
|
||||
let operator_base_reward = reward.reward.min(params.node.operator_cost());
|
||||
// Div by zero checked above
|
||||
let operator_reward = (self.profit_margin()
|
||||
|
||||
@@ -99,6 +99,17 @@ pub enum ExecuteMsg {
|
||||
},
|
||||
// AdvanceCurrentInterval {},
|
||||
AdvanceCurrentEpoch {},
|
||||
ClaimOperatorReward {},
|
||||
ClaimOperatorRewardOnBehalf {
|
||||
owner: String,
|
||||
},
|
||||
ClaimDelegatorReward {
|
||||
mix_identity: IdentityKey,
|
||||
},
|
||||
ClaimDelegatorRewardOnBehalf {
|
||||
mix_identity: IdentityKey,
|
||||
owner: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
|
||||
@@ -42,7 +42,7 @@ impl NodeEpochRewards {
|
||||
}
|
||||
|
||||
pub fn operator_cost(&self) -> U128 {
|
||||
U128::from_num(self.params.uptime.u128() / 100u128 * DEFAULT_OPERATOR_INTERVAL_COST as u128)
|
||||
self.params.operator_cost()
|
||||
}
|
||||
|
||||
pub fn node_profit(&self) -> U128 {
|
||||
@@ -178,11 +178,15 @@ impl NodeRewardParams {
|
||||
}
|
||||
|
||||
pub fn operator_cost(&self) -> U128 {
|
||||
U128::from_num(self.uptime.u128() / 100u128 * DEFAULT_OPERATOR_INTERVAL_COST as u128)
|
||||
self.performance() * U128::from_num(DEFAULT_OPERATOR_INTERVAL_COST)
|
||||
}
|
||||
|
||||
pub fn uptime(&self) -> u128 {
|
||||
self.uptime.u128()
|
||||
pub fn uptime(&self) -> Uint128 {
|
||||
self.uptime
|
||||
}
|
||||
|
||||
pub fn performance(&self) -> U128 {
|
||||
U128::from_num(self.uptime.u128()) / U128::from_num(100)
|
||||
}
|
||||
|
||||
pub fn set_reward_blockstamp(&mut self, blockstamp: u64) {
|
||||
@@ -233,7 +237,7 @@ impl RewardParams {
|
||||
}
|
||||
|
||||
pub fn performance(&self) -> U128 {
|
||||
U128::from_num(self.node.uptime.u128()) / U128::from_num(100)
|
||||
self.node.performance()
|
||||
}
|
||||
|
||||
pub fn set_reward_blockstamp(&mut self, blockstamp: u64) {
|
||||
@@ -256,8 +260,8 @@ impl RewardParams {
|
||||
self.node.reward_blockstamp
|
||||
}
|
||||
|
||||
pub fn uptime(&self) -> u128 {
|
||||
self.node.uptime.u128()
|
||||
pub fn uptime(&self) -> Uint128 {
|
||||
self.node.uptime()
|
||||
}
|
||||
|
||||
pub fn one_over_k(&self) -> U128 {
|
||||
|
||||
@@ -20,6 +20,7 @@ pub const VESTING_UPDATE_MIXNODE_CONFIG_EVENT_TYPE: &str = "vesting_update_mixno
|
||||
pub const TRACK_MIXNODE_UNBOND_EVENT_TYPE: &str = "track_mixnode_unbond";
|
||||
pub const TRACK_GATEWAY_UNBOND_EVENT_TYPE: &str = "track_gateway_unbond";
|
||||
pub const TRACK_UNDELEGATION_EVENT_TYPE: &str = "track_undelegation";
|
||||
pub const TRACK_REWARD_EVENT_TYPE: &str = "track_reaward";
|
||||
|
||||
// attributes that are used in multiple places
|
||||
pub const OWNER_KEY: &str = "owner";
|
||||
@@ -136,3 +137,7 @@ pub fn new_track_gateway_unbond_event() -> Event {
|
||||
pub fn new_track_undelegation_event() -> Event {
|
||||
Event::new(TRACK_UNDELEGATION_EVENT_TYPE)
|
||||
}
|
||||
|
||||
pub fn new_track_reward_event() -> Event {
|
||||
Event::new(TRACK_REWARD_EVENT_TYPE)
|
||||
}
|
||||
|
||||
@@ -49,6 +49,14 @@ impl VestingSpecification {
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ExecuteMsg {
|
||||
TrackReward {
|
||||
amount: Coin,
|
||||
address: String,
|
||||
},
|
||||
ClaimOperatorReward {},
|
||||
ClaimDelegatorReward {
|
||||
mix_identity: String,
|
||||
},
|
||||
CompoundDelegatorReward {
|
||||
mix_identity: String,
|
||||
},
|
||||
|
||||
+489
-509
File diff suppressed because it is too large
Load Diff
@@ -35,7 +35,7 @@ impl TryFrom<MixnetContractDelegation> for Delegation {
|
||||
proxy,
|
||||
} = value;
|
||||
|
||||
let amount: MajorCurrencyAmount = amount.try_into()?;
|
||||
let amount: MajorCurrencyAmount = amount.into();
|
||||
|
||||
Ok(Delegation {
|
||||
owner: owner.into_string(),
|
||||
@@ -112,7 +112,7 @@ impl TryFrom<MixnetContractDelegation> for DelegationResult {
|
||||
type Error = TypesError;
|
||||
|
||||
fn try_from(delegation: MixnetContractDelegation) -> Result<Self, Self::Error> {
|
||||
let amount: MajorCurrencyAmount = delegation.amount.clone().try_into()?;
|
||||
let amount: MajorCurrencyAmount = delegation.amount.clone().into();
|
||||
Ok(DelegationResult {
|
||||
source_address: delegation.owner().to_string(),
|
||||
target_address: delegation.node_identity(),
|
||||
@@ -152,7 +152,7 @@ impl TryFrom<ContractDelegationEvent> for DelegationEvent {
|
||||
fn try_from(event: ContractDelegationEvent) -> Result<Self, Self::Error> {
|
||||
match event {
|
||||
ContractDelegationEvent::Delegate(delegation) => {
|
||||
let amount: MajorCurrencyAmount = delegation.amount.try_into()?;
|
||||
let amount: MajorCurrencyAmount = delegation.amount.into();
|
||||
Ok(DelegationEvent {
|
||||
kind: DelegationEventKind::Delegate,
|
||||
block_height: delegation.block_height,
|
||||
|
||||
+29
-25
@@ -26,35 +26,39 @@ pub struct Gas {
|
||||
|
||||
impl Gas {
|
||||
pub fn from_cosmrs_gas(value: CosmrsGas, denom_minor: &str) -> Result<Gas, TypesError> {
|
||||
// TODO: use simulator struct to do conversion to fee
|
||||
let value_u128 = Uint128::from(value.value());
|
||||
let amount = Decimal::new(value_u128) * Decimal::from_str("0.0025")?;
|
||||
Ok(Gas {
|
||||
gas_units: value.value(),
|
||||
amount: MajorCurrencyAmount::from_minor_decimal_and_denom(amount, denom_minor)?,
|
||||
})
|
||||
todo!()
|
||||
|
||||
// // TODO: use simulator struct to do conversion to fee
|
||||
// let value_u128 = Uint128::from(value.value());
|
||||
// let amount = Decimal::new(value_u128) * Decimal::from_str("0.0025")?;
|
||||
// Ok(Gas {
|
||||
// gas_units: value.value(),
|
||||
// amount: MajorCurrencyAmount::from_minor_decimal_and_denom(amount, denom_minor)?,
|
||||
// })
|
||||
}
|
||||
pub fn from_u64(value: u64, denom_minor: &str) -> Result<Gas, TypesError> {
|
||||
// TODO: use simulator struct to do conversion to fee
|
||||
let value_u128 = Uint128::from(value);
|
||||
let amount = Decimal::new(value_u128) * Decimal::from_str("0.0025")?;
|
||||
Ok(Gas {
|
||||
gas_units: value,
|
||||
amount: MajorCurrencyAmount::from_minor_decimal_and_denom(amount, denom_minor)?,
|
||||
})
|
||||
todo!()
|
||||
// // TODO: use simulator struct to do conversion to fee
|
||||
// let value_u128 = Uint128::from(value);
|
||||
// let amount = Decimal::new(value_u128) * Decimal::from_str("0.0025")?;
|
||||
// Ok(Gas {
|
||||
// gas_units: value,
|
||||
// amount: MajorCurrencyAmount::from_minor_decimal_and_denom(amount, denom_minor)?,
|
||||
// })
|
||||
}
|
||||
pub fn from_gas_price(value: ValidatorClientGasPrice) -> Result<Gas, TypesError> {
|
||||
// TODO: use simulator struct to do conversion to fee
|
||||
let gas_units_str = (value.amount / Uint128::from_str("0.0025")?).to_string();
|
||||
let decimal_seperator_pos = gas_units_str.find('.').unwrap_or(gas_units_str.len());
|
||||
let gas_units = gas_units_str[..decimal_seperator_pos]
|
||||
.parse()
|
||||
.unwrap_or(0_u64);
|
||||
let ValidatorClientGasPrice { amount, denom } = value;
|
||||
Ok(Gas {
|
||||
gas_units,
|
||||
amount: MajorCurrencyAmount::from_minor_decimal_and_denom(amount, denom.as_ref())?,
|
||||
})
|
||||
todo!()
|
||||
// // TODO: use simulator struct to do conversion to fee
|
||||
// let gas_units_str = (value.amount / Uint128::from_str("0.0025")?).to_string();
|
||||
// let decimal_seperator_pos = gas_units_str.find('.').unwrap_or(gas_units_str.len());
|
||||
// let gas_units = gas_units_str[..decimal_seperator_pos]
|
||||
// .parse()
|
||||
// .unwrap_or(0_u64);
|
||||
// let ValidatorClientGasPrice { amount, denom } = value;
|
||||
// Ok(Gas {
|
||||
// gas_units,
|
||||
// amount: MajorCurrencyAmount::from_minor_decimal_and_denom(amount, denom.as_ref())?,
|
||||
// })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ impl TryFrom<MixnetContractGatewayBond> for GatewayBond {
|
||||
proxy,
|
||||
} = value;
|
||||
|
||||
let pledge_amount: MajorCurrencyAmount = pledge_amount.try_into()?;
|
||||
let pledge_amount: MajorCurrencyAmount = pledge_amount.into();
|
||||
|
||||
Ok(GatewayBond {
|
||||
pledge_amount,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use crate::currency::MajorCurrencyAmount;
|
||||
use crate::error::TypesError;
|
||||
use mixnet_contract_common::{
|
||||
MixNode as MixnetContractMixNode, MixNodeBond as MixnetContractMixNodeBond,
|
||||
Coin as CosmWasmCoin, MixNode as MixnetContractMixNode,
|
||||
MixNodeBond as MixnetContractMixNodeBond,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -102,12 +103,12 @@ impl TryFrom<MixnetContractMixNodeBond> for MixNodeBond {
|
||||
));
|
||||
}
|
||||
|
||||
let pledge_amount: MajorCurrencyAmount = pledge_amount.try_into()?;
|
||||
let total_delegation: MajorCurrencyAmount = total_delegation.try_into()?;
|
||||
let accumulated_rewards: Option<MajorCurrencyAmount> = accumulated_rewards.and_then(|r| {
|
||||
MajorCurrencyAmount::from_minor_uint128_and_denom(r, &pledge_amount.denom.to_string())
|
||||
.ok()
|
||||
});
|
||||
let denom = total_delegation.denom.clone();
|
||||
|
||||
let pledge_amount: MajorCurrencyAmount = pledge_amount.into();
|
||||
let total_delegation: MajorCurrencyAmount = total_delegation.into();
|
||||
let accumulated_rewards: Option<MajorCurrencyAmount> =
|
||||
accumulated_rewards.map(|r| CosmWasmCoin::new(r.u128(), denom).into());
|
||||
|
||||
Ok(MixNodeBond {
|
||||
pledge_amount,
|
||||
|
||||
@@ -20,8 +20,8 @@ pub struct SendTxResult {
|
||||
pub details: TransactionDetails,
|
||||
pub gas_used: u64,
|
||||
pub gas_wanted: u64,
|
||||
pub fee: MajorCurrencyAmount,
|
||||
pub tx_hash: String,
|
||||
// pub fee: MajorCurrencyAmount,
|
||||
}
|
||||
|
||||
impl SendTxResult {
|
||||
@@ -37,10 +37,11 @@ impl SendTxResult {
|
||||
gas_used: t.tx_result.gas_used.value(),
|
||||
gas_wanted: t.tx_result.gas_wanted.value(),
|
||||
tx_hash: t.hash.to_string(),
|
||||
fee: MajorCurrencyAmount::from_decimal_and_denom(
|
||||
Decimal::new(Uint128::from(t.tx_result.gas_used.value())),
|
||||
denom_minor.to_string(),
|
||||
)?,
|
||||
// that is completely wrong: fee is what you told the validator to use beforehandn
|
||||
// fee: MajorCurrencyAmount::from_decimal_and_denom(
|
||||
// Decimal::new(Uint128::from(t.tx_result.gas_used.value())),
|
||||
// denom_minor.to_string(),
|
||||
// )?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -100,7 +101,7 @@ pub struct RpcTransactionResponse {
|
||||
pub block_height: u64,
|
||||
pub transaction_hash: String,
|
||||
pub gas_info: GasInfo,
|
||||
pub fee: MajorCurrencyAmount,
|
||||
// pub fee: MajorCurrencyAmount,
|
||||
}
|
||||
|
||||
impl RpcTransactionResponse {
|
||||
@@ -118,10 +119,11 @@ impl RpcTransactionResponse {
|
||||
transaction_hash: value.hash.to_string(),
|
||||
tx_result_json: ::serde_json::to_string_pretty(&value.tx_result)?,
|
||||
block_height: value.height.value(),
|
||||
fee: MajorCurrencyAmount::from_decimal_and_denom(
|
||||
Decimal::new(Uint128::from(value.tx_result.gas_used.value())),
|
||||
denom_minor.to_string(),
|
||||
)?,
|
||||
// wrong
|
||||
// fee: MajorCurrencyAmount::from_decimal_and_denom(
|
||||
// Decimal::new(Uint128::from(value.tx_result.gas_used.value())),
|
||||
// denom_minor.to_string(),
|
||||
// )?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ impl TryFrom<VestingPledgeData> for PledgeData {
|
||||
type Error = TypesError;
|
||||
|
||||
fn try_from(data: VestingPledgeData) -> Result<Self, Self::Error> {
|
||||
let amount: MajorCurrencyAmount = data.amount().try_into()?;
|
||||
let amount: MajorCurrencyAmount = data.amount().into();
|
||||
Ok(Self {
|
||||
amount,
|
||||
block_time: data.block_time().seconds(),
|
||||
@@ -51,7 +51,7 @@ impl TryFrom<VestingOriginalVestingResponse> for OriginalVestingResponse {
|
||||
type Error = TypesError;
|
||||
|
||||
fn try_from(data: VestingOriginalVestingResponse) -> Result<Self, Self::Error> {
|
||||
let amount = data.amount().try_into()?;
|
||||
let amount = data.amount().into();
|
||||
Ok(Self {
|
||||
amount,
|
||||
number_of_periods: data.number_of_periods(),
|
||||
@@ -82,7 +82,7 @@ impl TryFrom<VestingAccount> for VestingAccountInfo {
|
||||
for period in account.periods() {
|
||||
periods.push(period.into());
|
||||
}
|
||||
let amount: MajorCurrencyAmount = account.coin().try_into()?;
|
||||
let amount: MajorCurrencyAmount = account.coin().into();
|
||||
Ok(Self {
|
||||
owner_address: account.owner_address().to_string(),
|
||||
staking_address: account.staking_address().map(|a| a.to_string()),
|
||||
|
||||
Generated
+49
@@ -2,6 +2,12 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "Inflector"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.7.5"
|
||||
@@ -1040,6 +1046,7 @@ dependencies = [
|
||||
"serde_repr",
|
||||
"thiserror",
|
||||
"time 0.3.6",
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1573,6 +1580,15 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.30"
|
||||
@@ -1645,6 +1661,29 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ts-rs"
|
||||
version = "6.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc59f479df54269b400dd95bc3b7e81623b3e4b9c70c8ca7125ab8341eafa64e"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"ts-rs-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ts-rs-macros"
|
||||
version = "6.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f807fdb3151fee75df7485b901a89624358cd07a67a8fb1a5831bf5a07681ff"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.15.0"
|
||||
@@ -1755,6 +1794,7 @@ dependencies = [
|
||||
"mixnet-contract-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1839,6 +1879,15 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
|
||||
@@ -283,6 +283,32 @@ pub fn execute(
|
||||
info,
|
||||
)
|
||||
}
|
||||
ExecuteMsg::ClaimOperatorReward {} => {
|
||||
crate::rewards::transactions::try_claim_operator_reward(deps, &env, &info)
|
||||
}
|
||||
ExecuteMsg::ClaimOperatorRewardOnBehalf { owner } => {
|
||||
crate::rewards::transactions::try_claim_operator_reward_on_behalf(
|
||||
deps, &env, &info, owner,
|
||||
)
|
||||
}
|
||||
ExecuteMsg::ClaimDelegatorReward { mix_identity } => {
|
||||
crate::rewards::transactions::try_claim_delegator_reward(
|
||||
deps,
|
||||
&env,
|
||||
&info,
|
||||
&mix_identity,
|
||||
)
|
||||
}
|
||||
ExecuteMsg::ClaimDelegatorRewardOnBehalf {
|
||||
mix_identity,
|
||||
owner,
|
||||
} => crate::rewards::transactions::try_claim_delegator_reward_on_behalf(
|
||||
deps,
|
||||
&env,
|
||||
&info,
|
||||
owner,
|
||||
&mix_identity,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -153,8 +153,8 @@ pub enum ContractError {
|
||||
#[from]
|
||||
source: MixnetContractError,
|
||||
},
|
||||
#[error("No rewards to claim for mixnode {identity} for delegate {delegate}")]
|
||||
NoRewardsToClaim { identity: String, delegate: String },
|
||||
#[error("No rewards to claim for mixnode {identity} for {address}")]
|
||||
NoRewardsToClaim { identity: String, address: String },
|
||||
|
||||
#[error("Epoch not initialized yet!")]
|
||||
EpochNotInitialized,
|
||||
|
||||
@@ -15,9 +15,13 @@ use crate::mixnodes::storage::{self as mixnodes_storage, StoredMixnodeBond};
|
||||
use crate::rewards::helpers;
|
||||
use crate::support::helpers::is_authorized;
|
||||
use config::defaults::DENOM;
|
||||
use cosmwasm_std::{Addr, Api, Coin, DepsMut, Env, MessageInfo, Order, Response, Storage, Uint128};
|
||||
use cosmwasm_std::{
|
||||
coins, wasm_execute, Addr, Api, BankMsg, Coin, DepsMut, Env, MessageInfo, Order, Response,
|
||||
Storage, Uint128,
|
||||
};
|
||||
use cw_storage_plus::Bound;
|
||||
use mixnet_contract_common::events::{
|
||||
new_claim_delegator_reward_event, new_claim_operator_reward_event,
|
||||
new_compound_delegator_reward_event, new_compound_operator_reward_event,
|
||||
new_mix_operator_rewarding_event, new_not_found_mix_operator_rewarding_event,
|
||||
new_too_fresh_bond_mix_operator_rewarding_event, new_zero_uptime_mix_operator_rewarding_event,
|
||||
@@ -27,6 +31,186 @@ use mixnet_contract_common::reward_params::{NodeEpochRewards, NodeRewardParams,
|
||||
use mixnet_contract_common::{Delegation, IdentityKey, RewardingStatus};
|
||||
|
||||
use mixnet_contract_common::RewardingResult;
|
||||
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
|
||||
use vesting_contract_common::one_ucoin;
|
||||
|
||||
// All four of the below methods need to do the following things:
|
||||
// 1. Calculate currently available rewards
|
||||
// 2. Send the rewards back to whoever claimed them
|
||||
// 3. Set the LAST_CLAIMED_HEIGHT to the current height
|
||||
pub fn try_claim_operator_reward(
|
||||
deps: DepsMut<'_>,
|
||||
env: &Env,
|
||||
info: &MessageInfo,
|
||||
) -> Result<Response, ContractError> {
|
||||
_try_claim_operator_reward(deps.storage, deps.api, env, &info.sender.to_string(), None)
|
||||
}
|
||||
|
||||
pub fn try_claim_operator_reward_on_behalf(
|
||||
deps: DepsMut<'_>,
|
||||
env: &Env,
|
||||
info: &MessageInfo,
|
||||
owner: String,
|
||||
) -> Result<Response, ContractError> {
|
||||
_try_claim_operator_reward(
|
||||
deps.storage,
|
||||
deps.api,
|
||||
env,
|
||||
&owner,
|
||||
Some(info.sender.clone()),
|
||||
)
|
||||
}
|
||||
|
||||
fn _try_claim_operator_reward(
|
||||
storage: &mut dyn Storage,
|
||||
api: &dyn Api,
|
||||
env: &Env,
|
||||
owner: &str,
|
||||
proxy: Option<Addr>,
|
||||
) -> Result<Response, ContractError> {
|
||||
let owner = api.addr_validate(owner)?;
|
||||
|
||||
let bond = match crate::mixnodes::storage::mixnodes()
|
||||
.idx
|
||||
.owner
|
||||
.item(storage, owner.clone())?
|
||||
{
|
||||
Some(record) => record.1,
|
||||
None => return Err(ContractError::NoAssociatedMixNodeBond { owner }),
|
||||
};
|
||||
|
||||
if proxy != bond.proxy {
|
||||
return Err(ContractError::ProxyMismatch {
|
||||
existing: bond
|
||||
.proxy
|
||||
.map_or_else(|| "None".to_string(), |a| a.as_str().to_string()),
|
||||
incoming: proxy.map_or_else(|| "None".to_string(), |a| a.as_str().to_string()),
|
||||
});
|
||||
}
|
||||
|
||||
let reward = calculate_operator_reward(storage, api, &owner, &bond)?;
|
||||
|
||||
OPERATOR_REWARD_CLAIMED_HEIGHT.save(
|
||||
storage,
|
||||
(owner.to_string(), bond.identity().to_string()),
|
||||
&env.block.height,
|
||||
)?;
|
||||
|
||||
if reward.is_zero() {
|
||||
return Err(ContractError::NoRewardsToClaim {
|
||||
identity: bond.identity().to_string(),
|
||||
address: owner.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let return_tokens = BankMsg::Send {
|
||||
to_address: proxy.as_ref().unwrap_or(&owner).to_string(),
|
||||
amount: coins(reward.u128(), DENOM),
|
||||
};
|
||||
|
||||
let mut response = Response::default()
|
||||
.add_message(return_tokens)
|
||||
.add_event(new_claim_operator_reward_event(&owner, reward));
|
||||
|
||||
if let Some(proxy) = proxy {
|
||||
let msg = Some(VestingContractExecuteMsg::TrackReward {
|
||||
address: owner.to_string(),
|
||||
amount: Coin::new(reward.u128(), DENOM),
|
||||
});
|
||||
|
||||
let wasm_msg = wasm_execute(proxy, &msg, vec![one_ucoin()])?;
|
||||
response = response.add_message(wasm_msg);
|
||||
}
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
pub fn _try_claim_delegator_reward(
|
||||
storage: &mut dyn Storage,
|
||||
api: &dyn Api,
|
||||
env: &Env,
|
||||
owner: &str,
|
||||
mix_identity: &str,
|
||||
proxy: Option<Addr>,
|
||||
) -> Result<Response, ContractError> {
|
||||
let owner = api.addr_validate(owner)?;
|
||||
|
||||
let key = mixnet_contract_common::delegation::generate_storage_key(&owner, proxy.as_ref());
|
||||
let reward = calculate_delegator_reward(storage, api, key.clone(), mix_identity)?;
|
||||
|
||||
DELEGATOR_REWARD_CLAIMED_HEIGHT.save(
|
||||
storage,
|
||||
(key, mix_identity.to_string()),
|
||||
&env.block.height,
|
||||
)?;
|
||||
|
||||
if reward.is_zero() {
|
||||
return Err(ContractError::NoRewardsToClaim {
|
||||
identity: mix_identity.to_string(),
|
||||
address: owner.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let return_tokens = BankMsg::Send {
|
||||
to_address: proxy.as_ref().unwrap_or(&owner).to_string(),
|
||||
amount: coins(reward.u128(), DENOM),
|
||||
};
|
||||
|
||||
let mut response =
|
||||
Response::default()
|
||||
.add_message(return_tokens)
|
||||
.add_event(new_claim_delegator_reward_event(
|
||||
&owner,
|
||||
&proxy,
|
||||
reward,
|
||||
mix_identity,
|
||||
));
|
||||
|
||||
if let Some(proxy) = proxy {
|
||||
let msg = Some(VestingContractExecuteMsg::TrackReward {
|
||||
address: owner.to_string(),
|
||||
amount: Coin::new(reward.u128(), DENOM),
|
||||
});
|
||||
|
||||
let wasm_msg = wasm_execute(proxy, &msg, vec![one_ucoin()])?;
|
||||
response = response.add_message(wasm_msg);
|
||||
}
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
pub fn try_claim_delegator_reward_on_behalf(
|
||||
deps: DepsMut<'_>,
|
||||
env: &Env,
|
||||
info: &MessageInfo,
|
||||
owner: String,
|
||||
mix_identity: &str,
|
||||
) -> Result<Response, ContractError> {
|
||||
_try_claim_delegator_reward(
|
||||
deps.storage,
|
||||
deps.api,
|
||||
env,
|
||||
&owner,
|
||||
mix_identity,
|
||||
Some(info.sender.clone()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn try_claim_delegator_reward(
|
||||
deps: DepsMut<'_>,
|
||||
env: &Env,
|
||||
info: &MessageInfo,
|
||||
mix_identity: &str,
|
||||
) -> Result<Response, ContractError> {
|
||||
_try_claim_delegator_reward(
|
||||
deps.storage,
|
||||
deps.api,
|
||||
env,
|
||||
&info.sender.to_string(),
|
||||
mix_identity,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn try_compound_operator_reward_on_behalf(
|
||||
deps: DepsMut,
|
||||
@@ -449,7 +633,7 @@ pub(crate) fn try_reward_mixnode(
|
||||
let node_delegation = current_bond.total_delegation.amount;
|
||||
|
||||
// check if it has non-zero uptime
|
||||
if params.uptime() == 0 {
|
||||
if params.uptime() == Uint128::zero() {
|
||||
storage::REWARDING_STATUS.save(
|
||||
deps.storage,
|
||||
(epoch.id(), mix_identity.clone()),
|
||||
@@ -1381,7 +1565,7 @@ pub mod tests {
|
||||
let mix_1 = mixnodes_storage::read_full_mixnode_bond(&deps.storage, &node_identity)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let mix_1_uptime = 100;
|
||||
let mix_1_uptime = 90;
|
||||
|
||||
let epoch = Interval::init_epoch(env.clone());
|
||||
save_epoch(&mut deps.storage, &epoch).unwrap();
|
||||
@@ -1395,7 +1579,7 @@ pub mod tests {
|
||||
|
||||
params.set_reward_blockstamp(env.block.height);
|
||||
|
||||
assert_eq!(params.performance(), U128::from_num(1u32));
|
||||
assert_eq!(params.performance(), U128::from_num(0.8999999999999999));
|
||||
|
||||
let mix_1_reward_result = mix_1.reward(¶ms);
|
||||
|
||||
@@ -1407,9 +1591,14 @@ pub mod tests {
|
||||
mix_1_reward_result.lambda(),
|
||||
U128::from_num(0.0000133333333333f64)
|
||||
);
|
||||
assert_eq!(mix_1_reward_result.reward().int(), 259114u128);
|
||||
assert_eq!(mix_1_reward_result.reward().int(), 233202u128);
|
||||
|
||||
assert_eq!(mix_1.node_profit(¶ms).int(), 203558u128);
|
||||
assert_eq!(mix_1.node_profit(¶ms).int(), 183202u128);
|
||||
|
||||
assert_ne!(
|
||||
mix_1_reward_result.reward(),
|
||||
mix_1.node_profit(¶ms).int()
|
||||
);
|
||||
|
||||
let mix1_operator_reward = mix_1.operator_reward(¶ms);
|
||||
|
||||
@@ -1417,9 +1606,9 @@ pub mod tests {
|
||||
|
||||
let mix1_delegator2_reward = mix_1.reward_delegation(Uint128::new(2000_000000), ¶ms);
|
||||
|
||||
assert_eq!(mix1_operator_reward, 167513);
|
||||
assert_eq!(mix1_delegator1_reward, 73280);
|
||||
assert_eq!(mix1_delegator2_reward, 18320);
|
||||
assert_eq!(mix1_operator_reward, 150761);
|
||||
assert_eq!(mix1_delegator1_reward, 65952);
|
||||
assert_eq!(mix1_delegator2_reward, 16488);
|
||||
|
||||
assert_eq!(
|
||||
mix_1_reward_result.reward().int(),
|
||||
|
||||
@@ -13,7 +13,8 @@ use mixnet_contract_common::{Gateway, IdentityKey, MixNode};
|
||||
use vesting_contract_common::events::{
|
||||
new_ownership_transfer_event, new_periodic_vesting_account_event,
|
||||
new_staking_address_update_event, new_track_gateway_unbond_event,
|
||||
new_track_mixnode_unbond_event, new_track_undelegation_event, new_vested_coins_withdraw_event,
|
||||
new_track_mixnode_unbond_event, new_track_reward_event, new_track_undelegation_event,
|
||||
new_vested_coins_withdraw_event,
|
||||
};
|
||||
use vesting_contract_common::messages::{
|
||||
ExecuteMsg, InitMsg, MigrateMsg, QueryMsg, VestingSpecification,
|
||||
@@ -46,6 +47,13 @@ pub fn execute(
|
||||
msg: ExecuteMsg,
|
||||
) -> Result<Response, ContractError> {
|
||||
match msg {
|
||||
ExecuteMsg::TrackReward { amount, address } => {
|
||||
try_track_reward(deps, info, amount, &address)
|
||||
}
|
||||
ExecuteMsg::ClaimOperatorReward {} => try_claim_operator_reward(deps, info),
|
||||
ExecuteMsg::ClaimDelegatorReward { mix_identity } => {
|
||||
try_claim_delegator_reward(deps, info, mix_identity)
|
||||
}
|
||||
ExecuteMsg::CompoundDelegatorReward { mix_identity } => {
|
||||
try_compound_delegator_reward(mix_identity, info, deps)
|
||||
}
|
||||
@@ -278,6 +286,20 @@ pub fn try_track_unbond_mixnode(
|
||||
Ok(Response::new().add_event(new_track_mixnode_unbond_event()))
|
||||
}
|
||||
|
||||
fn try_track_reward(
|
||||
deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
amount: Coin,
|
||||
address: &str,
|
||||
) -> Result<Response, ContractError> {
|
||||
if info.sender != MIXNET_CONTRACT_ADDRESS.load(deps.storage)? {
|
||||
return Err(ContractError::NotMixnetContract(info.sender));
|
||||
}
|
||||
let account = account_from_address(address, deps.storage, deps.api)?;
|
||||
account.track_reward(amount, deps.storage)?;
|
||||
Ok(Response::new().add_event(new_track_reward_event()))
|
||||
}
|
||||
|
||||
fn try_track_undelegation(
|
||||
address: &str,
|
||||
mix_identity: IdentityKey,
|
||||
@@ -314,6 +336,23 @@ fn try_compound_delegator_reward(
|
||||
account.try_compound_delegator_reward(mix_identity, deps.storage)
|
||||
}
|
||||
|
||||
fn try_claim_operator_reward(
|
||||
deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
) -> Result<Response, ContractError> {
|
||||
let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
|
||||
account.try_claim_operator_reward(deps.storage)
|
||||
}
|
||||
|
||||
fn try_claim_delegator_reward(
|
||||
deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
mix_identity: String,
|
||||
) -> Result<Response, ContractError> {
|
||||
let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
|
||||
account.try_claim_delegator_reward(mix_identity, deps.storage)
|
||||
}
|
||||
|
||||
fn try_undelegate_from_mixnode(
|
||||
mix_identity: IdentityKey,
|
||||
info: MessageInfo,
|
||||
|
||||
@@ -8,6 +8,8 @@ pub trait MixnodeBondingAccount {
|
||||
storage: &dyn Storage,
|
||||
) -> Result<Response, ContractError>;
|
||||
|
||||
fn try_claim_operator_reward(&self, storage: &dyn Storage) -> Result<Response, ContractError>;
|
||||
|
||||
fn try_bond_mixnode(
|
||||
&self,
|
||||
mix_node: MixNode,
|
||||
|
||||
@@ -3,6 +3,12 @@ use cosmwasm_std::{Coin, Env, Response, Storage, Uint128};
|
||||
use mixnet_contract_common::IdentityKey;
|
||||
|
||||
pub trait DelegatingAccount {
|
||||
fn try_claim_delegator_reward(
|
||||
&self,
|
||||
mix_identity: IdentityKey,
|
||||
storage: &dyn Storage,
|
||||
) -> Result<Response, ContractError>;
|
||||
|
||||
fn try_compound_delegator_reward(
|
||||
&self,
|
||||
mix_identity: IdentityKey,
|
||||
|
||||
@@ -73,4 +73,5 @@ pub trait VestingAccount {
|
||||
to_address: Option<Addr>,
|
||||
storage: &mut dyn Storage,
|
||||
) -> Result<(), ContractError>;
|
||||
fn track_reward(&self, amount: Coin, storage: &mut dyn Storage) -> Result<(), ContractError>;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,25 @@ use vesting_contract_common::one_ucoin;
|
||||
use super::Account;
|
||||
|
||||
impl DelegatingAccount for Account {
|
||||
fn try_claim_delegator_reward(
|
||||
&self,
|
||||
mix_identity: IdentityKey,
|
||||
storage: &dyn Storage,
|
||||
) -> Result<Response, ContractError> {
|
||||
let msg = MixnetExecuteMsg::ClaimDelegatorRewardOnBehalf {
|
||||
owner: self.owner_address().to_string(),
|
||||
mix_identity,
|
||||
};
|
||||
|
||||
let compound_delegator_reward_msg = wasm_execute(
|
||||
MIXNET_CONTRACT_ADDRESS.load(storage)?,
|
||||
&msg,
|
||||
vec![one_ucoin()],
|
||||
)?;
|
||||
|
||||
Ok(Response::new().add_message(compound_delegator_reward_msg))
|
||||
}
|
||||
|
||||
fn try_compound_delegator_reward(
|
||||
&self,
|
||||
mix_identity: IdentityKey,
|
||||
|
||||
@@ -14,6 +14,20 @@ use vesting_contract_common::PledgeData;
|
||||
use super::Account;
|
||||
|
||||
impl MixnodeBondingAccount for Account {
|
||||
fn try_claim_operator_reward(&self, storage: &dyn Storage) -> Result<Response, ContractError> {
|
||||
let msg = MixnetExecuteMsg::ClaimOperatorRewardOnBehalf {
|
||||
owner: self.owner_address().into_string(),
|
||||
};
|
||||
|
||||
let compound_operator_reward_msg = wasm_execute(
|
||||
MIXNET_CONTRACT_ADDRESS.load(storage)?,
|
||||
&msg,
|
||||
vec![one_ucoin()],
|
||||
)?;
|
||||
|
||||
Ok(Response::new().add_message(compound_operator_reward_msg))
|
||||
}
|
||||
|
||||
fn try_compound_operator_reward(
|
||||
&self,
|
||||
storage: &dyn Storage,
|
||||
|
||||
@@ -8,6 +8,13 @@ use vesting_contract_common::{OriginalVestingResponse, Period};
|
||||
use super::Account;
|
||||
|
||||
impl VestingAccount for Account {
|
||||
fn track_reward(&self, amount: Coin, storage: &mut dyn Storage) -> Result<(), ContractError> {
|
||||
let current_balance = self.load_balance(storage)?;
|
||||
let new_balance = current_balance + amount.amount;
|
||||
self.save_balance(new_balance, storage)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn locked_coins(
|
||||
&self,
|
||||
block_time: Option<Timestamp>,
|
||||
|
||||
@@ -36,6 +36,6 @@ pub(crate) async fn retrieve_mixnode_econ_stats(
|
||||
estimated_total_node_reward: reward_estimation.estimated_total_node_reward,
|
||||
estimated_operator_reward: reward_estimation.estimated_operator_reward,
|
||||
estimated_delegators_reward: reward_estimation.estimated_delegators_reward,
|
||||
current_interval_uptime: reward_estimation.reward_params.node.uptime() as u8,
|
||||
current_interval_uptime: reward_estimation.reward_params.node.uptime().u128() as u8,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
max_width = 100
|
||||
hard_tabs = false
|
||||
tab_spaces = 2
|
||||
newline_style = "Auto"
|
||||
use_small_heuristics = "Default"
|
||||
reorder_imports = true
|
||||
reorder_modules = true
|
||||
remove_nested_parens = true
|
||||
edition = "2018"
|
||||
merge_derives = true
|
||||
use_try_shorthand = false
|
||||
use_field_init_shorthand = false
|
||||
force_explicit_abi = true
|
||||
@@ -1,3 +1,3 @@
|
||||
fn main() {
|
||||
tauri_build::build()
|
||||
tauri_build::build()
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ use crate::error::BackendError;
|
||||
use crate::platform_constants::{CONFIG_DIR_NAME, CONFIG_FILENAME};
|
||||
|
||||
pub const REMOTE_SOURCE_OF_VALIDATOR_URLS: &str =
|
||||
"https://nymtech.net/.wellknown/wallet/validators.json";
|
||||
"https://nymtech.net/.wellknown/wallet/validators.json";
|
||||
|
||||
const CURRENT_GLOBAL_CONFIG_VERSION: u32 = 1;
|
||||
const CURRENT_NETWORK_CONFIG_VERSION: u32 = 1;
|
||||
@@ -30,448 +30,445 @@ pub(crate) const CUSTOM_SIMULATED_GAS_MULTIPLIER: f32 = 1.4;
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub struct Config {
|
||||
// Base configuration is not part of the configuration file as it's not intended to be changed.
|
||||
base: Base,
|
||||
// Base configuration is not part of the configuration file as it's not intended to be changed.
|
||||
base: Base,
|
||||
|
||||
// Global configuration file
|
||||
global: Option<GlobalConfig>,
|
||||
// Global configuration file
|
||||
global: Option<GlobalConfig>,
|
||||
|
||||
// One configuration file per network
|
||||
networks: HashMap<String, NetworkConfig>,
|
||||
// One configuration file per network
|
||||
networks: HashMap<String, NetworkConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||
struct Base {
|
||||
/// Information on all the networks that the wallet connects to.
|
||||
networks: SupportedNetworks,
|
||||
/// Information on all the networks that the wallet connects to.
|
||||
networks: SupportedNetworks,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||
pub struct GlobalConfig {
|
||||
version: Option<u32>,
|
||||
// TODO: there are no global settings (yet)
|
||||
version: Option<u32>,
|
||||
// TODO: there are no global settings (yet)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||
pub struct NetworkConfig {
|
||||
version: Option<u32>,
|
||||
version: Option<u32>,
|
||||
|
||||
// User selected urls
|
||||
selected_nymd_url: Option<Url>,
|
||||
selected_api_url: Option<Url>,
|
||||
// User selected urls
|
||||
selected_nymd_url: Option<Url>,
|
||||
selected_api_url: Option<Url>,
|
||||
|
||||
// Additional user provided validators.
|
||||
// It is an option for the purpuse of file serialization.
|
||||
validator_urls: Option<Vec<ValidatorConfigEntry>>,
|
||||
// Additional user provided validators.
|
||||
// It is an option for the purpuse of file serialization.
|
||||
validator_urls: Option<Vec<ValidatorConfigEntry>>,
|
||||
}
|
||||
|
||||
impl Default for Base {
|
||||
fn default() -> Self {
|
||||
let networks = WalletNetwork::iter().map(Into::into).collect();
|
||||
Base {
|
||||
networks: SupportedNetworks::new(networks),
|
||||
fn default() -> Self {
|
||||
let networks = WalletNetwork::iter().map(Into::into).collect();
|
||||
Base {
|
||||
networks: SupportedNetworks::new(networks),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GlobalConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
version: Some(CURRENT_GLOBAL_CONFIG_VERSION),
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
version: Some(CURRENT_GLOBAL_CONFIG_VERSION),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for NetworkConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
version: Some(CURRENT_NETWORK_CONFIG_VERSION),
|
||||
selected_nymd_url: None,
|
||||
selected_api_url: None,
|
||||
validator_urls: None,
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
version: Some(CURRENT_NETWORK_CONFIG_VERSION),
|
||||
selected_nymd_url: None,
|
||||
selected_api_url: None,
|
||||
validator_urls: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkConfig {
|
||||
fn validators(&self) -> impl Iterator<Item = &ValidatorConfigEntry> {
|
||||
self.validator_urls.iter().flat_map(|v| v.iter())
|
||||
}
|
||||
fn validators(&self) -> impl Iterator<Item = &ValidatorConfigEntry> {
|
||||
self.validator_urls.iter().flat_map(|v| v.iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
fn root_directory() -> PathBuf {
|
||||
tauri::api::path::config_dir().expect("Failed to get config directory")
|
||||
}
|
||||
|
||||
fn config_directory() -> PathBuf {
|
||||
Self::root_directory().join(CONFIG_DIR_NAME)
|
||||
}
|
||||
|
||||
fn config_file_path(network: Option<WalletNetwork>) -> PathBuf {
|
||||
if let Some(network) = network {
|
||||
let network_filename = format!("{}.toml", network.as_key());
|
||||
Self::config_directory().join(network_filename)
|
||||
} else {
|
||||
Self::config_directory().join(CONFIG_FILENAME)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save_to_files(&self) -> io::Result<()> {
|
||||
log::trace!("Config::save_to_file");
|
||||
|
||||
// Make sure the whole directory structure actually exists
|
||||
fs::create_dir_all(Self::config_directory())?;
|
||||
|
||||
// Global config
|
||||
if let Some(global) = &self.global {
|
||||
let location = Self::config_file_path(None);
|
||||
|
||||
match toml::to_string_pretty(&global)
|
||||
.map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
|
||||
.map(|toml| fs::write(location.clone(), toml))
|
||||
{
|
||||
Ok(_) => log::debug!("Writing to: {:#?}", location),
|
||||
Err(err) => log::warn!("Failed to write to {:#?}: {err}", location),
|
||||
}
|
||||
fn root_directory() -> PathBuf {
|
||||
tauri::api::path::config_dir().expect("Failed to get config directory")
|
||||
}
|
||||
|
||||
// One file per network
|
||||
for (network, config) in &self.networks {
|
||||
let network = match Network::from_str(network).map(Into::into) {
|
||||
Ok(network) => network,
|
||||
Err(err) => {
|
||||
log::warn!("Unexpected name for network configuration, not saving: {err}");
|
||||
break;
|
||||
fn config_directory() -> PathBuf {
|
||||
Self::root_directory().join(CONFIG_DIR_NAME)
|
||||
}
|
||||
|
||||
fn config_file_path(network: Option<WalletNetwork>) -> PathBuf {
|
||||
if let Some(network) = network {
|
||||
let network_filename = format!("{}.toml", network.as_key());
|
||||
Self::config_directory().join(network_filename)
|
||||
} else {
|
||||
Self::config_directory().join(CONFIG_FILENAME)
|
||||
}
|
||||
};
|
||||
|
||||
let location = Self::config_file_path(Some(network));
|
||||
match toml::to_string_pretty(config)
|
||||
.map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
|
||||
.map(|toml| fs::write(location.clone(), toml))
|
||||
{
|
||||
Ok(_) => log::debug!("Writing to: {:#?}", location),
|
||||
Err(err) => log::warn!("Failed to write to {:#?}: {err}", location),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_from_files() -> Self {
|
||||
// Global
|
||||
let global = {
|
||||
let file = Self::config_file_path(None);
|
||||
match load_from_file::<GlobalConfig>(file.clone()) {
|
||||
Ok(global) => {
|
||||
log::debug!("Loaded from file {:#?}", file);
|
||||
Some(global)
|
||||
pub fn save_to_files(&self) -> io::Result<()> {
|
||||
log::trace!("Config::save_to_file");
|
||||
|
||||
// Make sure the whole directory structure actually exists
|
||||
fs::create_dir_all(Self::config_directory())?;
|
||||
|
||||
// Global config
|
||||
if let Some(global) = &self.global {
|
||||
let location = Self::config_file_path(None);
|
||||
|
||||
match toml::to_string_pretty(&global)
|
||||
.map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
|
||||
.map(|toml| fs::write(location.clone(), toml))
|
||||
{
|
||||
Ok(_) => log::debug!("Writing to: {:#?}", location),
|
||||
Err(err) => log::warn!("Failed to write to {:#?}: {err}", location),
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
log::trace!("Not loading {:#?}: {}", file, err);
|
||||
None
|
||||
|
||||
// One file per network
|
||||
for (network, config) in &self.networks {
|
||||
let network = match Network::from_str(network).map(Into::into) {
|
||||
Ok(network) => network,
|
||||
Err(err) => {
|
||||
log::warn!("Unexpected name for network configuration, not saving: {err}");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
let location = Self::config_file_path(Some(network));
|
||||
match toml::to_string_pretty(config)
|
||||
.map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
|
||||
.map(|toml| fs::write(location.clone(), toml))
|
||||
{
|
||||
Ok(_) => log::debug!("Writing to: {:#?}", location),
|
||||
Err(err) => log::warn!("Failed to write to {:#?}: {err}", location),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// One file per network
|
||||
let mut networks = HashMap::new();
|
||||
for network in WalletNetwork::iter() {
|
||||
let file = Self::config_file_path(Some(network));
|
||||
match load_from_file::<NetworkConfig>(file.clone()) {
|
||||
Ok(config) => {
|
||||
log::trace!("Loaded from file {:#?}", file);
|
||||
networks.insert(network.as_key(), config);
|
||||
pub fn load_from_files() -> Self {
|
||||
// Global
|
||||
let global = {
|
||||
let file = Self::config_file_path(None);
|
||||
match load_from_file::<GlobalConfig>(file.clone()) {
|
||||
Ok(global) => {
|
||||
log::debug!("Loaded from file {:#?}", file);
|
||||
Some(global)
|
||||
}
|
||||
Err(err) => {
|
||||
log::trace!("Not loading {:#?}: {}", file, err);
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// One file per network
|
||||
let mut networks = HashMap::new();
|
||||
for network in WalletNetwork::iter() {
|
||||
let file = Self::config_file_path(Some(network));
|
||||
match load_from_file::<NetworkConfig>(file.clone()) {
|
||||
Ok(config) => {
|
||||
log::trace!("Loaded from file {:#?}", file);
|
||||
networks.insert(network.as_key(), config);
|
||||
}
|
||||
Err(err) => log::trace!("Not loading {:#?}: {}", file, err),
|
||||
};
|
||||
}
|
||||
|
||||
Self {
|
||||
base: Base::default(),
|
||||
global,
|
||||
networks,
|
||||
}
|
||||
Err(err) => log::trace!("Not loading {:#?}: {}", file, err),
|
||||
};
|
||||
}
|
||||
|
||||
Self {
|
||||
base: Base::default(),
|
||||
global,
|
||||
networks,
|
||||
pub fn get_base_validators(
|
||||
&self,
|
||||
network: WalletNetwork,
|
||||
) -> impl Iterator<Item = ValidatorConfigEntry> + '_ {
|
||||
self.base.networks.validators(network.into()).map(|v| {
|
||||
v.clone()
|
||||
.try_into()
|
||||
.expect("The hardcoded validators are assumed to be valid urls")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_base_validators(
|
||||
&self,
|
||||
network: WalletNetwork,
|
||||
) -> impl Iterator<Item = ValidatorConfigEntry> + '_ {
|
||||
self.base.networks.validators(network.into()).map(|v| {
|
||||
v.clone()
|
||||
.try_into()
|
||||
.expect("The hardcoded validators are assumed to be valid urls")
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_configured_validators(
|
||||
&self,
|
||||
network: WalletNetwork,
|
||||
) -> impl Iterator<Item = ValidatorConfigEntry> + '_ {
|
||||
self
|
||||
.networks
|
||||
.get(&network.as_key())
|
||||
.into_iter()
|
||||
.flat_map(|c| c.validators().cloned())
|
||||
}
|
||||
|
||||
pub fn get_mixnet_contract_address(&self, network: WalletNetwork) -> Option<CosmosAccountId> {
|
||||
self
|
||||
.base
|
||||
.networks
|
||||
.mixnet_contract_address(network.into())
|
||||
.expect("No mixnet contract address found in config")
|
||||
.parse()
|
||||
.ok()
|
||||
}
|
||||
|
||||
pub fn get_vesting_contract_address(&self, network: WalletNetwork) -> Option<CosmosAccountId> {
|
||||
self
|
||||
.base
|
||||
.networks
|
||||
.vesting_contract_address(network.into())
|
||||
.expect("No vesting contract address found in config")
|
||||
.parse()
|
||||
.ok()
|
||||
}
|
||||
|
||||
pub fn get_bandwidth_claim_contract_address(
|
||||
&self,
|
||||
network: WalletNetwork,
|
||||
) -> Option<CosmosAccountId> {
|
||||
self
|
||||
.base
|
||||
.networks
|
||||
.bandwidth_claim_contract_address(network.into())
|
||||
.expect("No bandwidth claim contract address found in config")
|
||||
.parse()
|
||||
.ok()
|
||||
}
|
||||
|
||||
pub fn select_validator_nymd_url(&mut self, nymd_url: Url, network: WalletNetwork) {
|
||||
if let Some(net) = self.networks.get_mut(&network.as_key()) {
|
||||
net.selected_nymd_url = Some(nymd_url);
|
||||
} else {
|
||||
self.networks.insert(
|
||||
network.as_key(),
|
||||
NetworkConfig {
|
||||
selected_nymd_url: Some(nymd_url),
|
||||
..NetworkConfig::default()
|
||||
},
|
||||
);
|
||||
pub fn get_configured_validators(
|
||||
&self,
|
||||
network: WalletNetwork,
|
||||
) -> impl Iterator<Item = ValidatorConfigEntry> + '_ {
|
||||
self.networks
|
||||
.get(&network.as_key())
|
||||
.into_iter()
|
||||
.flat_map(|c| c.validators().cloned())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select_validator_api_url(&mut self, api_url: Url, network: WalletNetwork) {
|
||||
if let Some(net) = self.networks.get_mut(&network.as_key()) {
|
||||
net.selected_api_url = Some(api_url);
|
||||
} else {
|
||||
self.networks.insert(
|
||||
network.as_key(),
|
||||
NetworkConfig {
|
||||
selected_nymd_url: Some(api_url),
|
||||
..NetworkConfig::default()
|
||||
},
|
||||
);
|
||||
pub fn get_mixnet_contract_address(&self, network: WalletNetwork) -> Option<CosmosAccountId> {
|
||||
self.base
|
||||
.networks
|
||||
.mixnet_contract_address(network.into())
|
||||
.expect("No mixnet contract address found in config")
|
||||
.parse()
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_selected_validator_nymd_url(&self, network: WalletNetwork) -> Option<Url> {
|
||||
self
|
||||
.networks
|
||||
.get(&network.as_key())
|
||||
.and_then(|config| config.selected_nymd_url.clone())
|
||||
}
|
||||
|
||||
pub fn get_selected_validator_api_url(&self, network: &WalletNetwork) -> Option<Url> {
|
||||
self
|
||||
.networks
|
||||
.get(&network.as_key())
|
||||
.and_then(|config| config.selected_api_url.clone())
|
||||
}
|
||||
|
||||
pub fn add_validator_url(&mut self, url: ValidatorConfigEntry, network: WalletNetwork) {
|
||||
if let Some(network_config) = self.networks.get_mut(&network.as_key()) {
|
||||
if let Some(ref mut urls) = network_config.validator_urls {
|
||||
urls.push(url);
|
||||
} else {
|
||||
network_config.validator_urls = Some(vec![url]);
|
||||
}
|
||||
} else {
|
||||
self.networks.insert(
|
||||
network.as_key(),
|
||||
NetworkConfig {
|
||||
validator_urls: Some(vec![url]),
|
||||
..NetworkConfig::default()
|
||||
},
|
||||
);
|
||||
pub fn get_vesting_contract_address(&self, network: WalletNetwork) -> Option<CosmosAccountId> {
|
||||
self.base
|
||||
.networks
|
||||
.vesting_contract_address(network.into())
|
||||
.expect("No vesting contract address found in config")
|
||||
.parse()
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_validator_url(&mut self, url: ValidatorConfigEntry, network: WalletNetwork) {
|
||||
if let Some(network_config) = self.networks.get_mut(&network.as_key()) {
|
||||
if let Some(ref mut urls) = network_config.validator_urls {
|
||||
// Removes duplicates too if there are any
|
||||
urls.retain(|existing_url| existing_url != &url);
|
||||
}
|
||||
pub fn get_bandwidth_claim_contract_address(
|
||||
&self,
|
||||
network: WalletNetwork,
|
||||
) -> Option<CosmosAccountId> {
|
||||
self.base
|
||||
.networks
|
||||
.bandwidth_claim_contract_address(network.into())
|
||||
.expect("No bandwidth claim contract address found in config")
|
||||
.parse()
|
||||
.ok()
|
||||
}
|
||||
|
||||
pub fn select_validator_nymd_url(&mut self, nymd_url: Url, network: WalletNetwork) {
|
||||
if let Some(net) = self.networks.get_mut(&network.as_key()) {
|
||||
net.selected_nymd_url = Some(nymd_url);
|
||||
} else {
|
||||
self.networks.insert(
|
||||
network.as_key(),
|
||||
NetworkConfig {
|
||||
selected_nymd_url: Some(nymd_url),
|
||||
..NetworkConfig::default()
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select_validator_api_url(&mut self, api_url: Url, network: WalletNetwork) {
|
||||
if let Some(net) = self.networks.get_mut(&network.as_key()) {
|
||||
net.selected_api_url = Some(api_url);
|
||||
} else {
|
||||
self.networks.insert(
|
||||
network.as_key(),
|
||||
NetworkConfig {
|
||||
selected_nymd_url: Some(api_url),
|
||||
..NetworkConfig::default()
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_selected_validator_nymd_url(&self, network: WalletNetwork) -> Option<Url> {
|
||||
self.networks
|
||||
.get(&network.as_key())
|
||||
.and_then(|config| config.selected_nymd_url.clone())
|
||||
}
|
||||
|
||||
pub fn get_selected_validator_api_url(&self, network: &WalletNetwork) -> Option<Url> {
|
||||
self.networks
|
||||
.get(&network.as_key())
|
||||
.and_then(|config| config.selected_api_url.clone())
|
||||
}
|
||||
|
||||
pub fn add_validator_url(&mut self, url: ValidatorConfigEntry, network: WalletNetwork) {
|
||||
if let Some(network_config) = self.networks.get_mut(&network.as_key()) {
|
||||
if let Some(ref mut urls) = network_config.validator_urls {
|
||||
urls.push(url);
|
||||
} else {
|
||||
network_config.validator_urls = Some(vec![url]);
|
||||
}
|
||||
} else {
|
||||
self.networks.insert(
|
||||
network.as_key(),
|
||||
NetworkConfig {
|
||||
validator_urls: Some(vec![url]),
|
||||
..NetworkConfig::default()
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_validator_url(&mut self, url: ValidatorConfigEntry, network: WalletNetwork) {
|
||||
if let Some(network_config) = self.networks.get_mut(&network.as_key()) {
|
||||
if let Some(ref mut urls) = network_config.validator_urls {
|
||||
// Removes duplicates too if there are any
|
||||
urls.retain(|existing_url| existing_url != &url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_from_file<T>(file: PathBuf) -> Result<T, io::Error>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
fs::read_to_string(file).and_then(|contents| {
|
||||
toml::from_str::<T>(&contents)
|
||||
.map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
|
||||
})
|
||||
fs::read_to_string(file).and_then(|contents| {
|
||||
toml::from_str::<T>(&contents)
|
||||
.map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
pub struct ValidatorConfigEntry {
|
||||
pub nymd_url: Url,
|
||||
pub nymd_name: Option<String>,
|
||||
pub api_url: Option<Url>,
|
||||
pub nymd_url: Url,
|
||||
pub nymd_name: Option<String>,
|
||||
pub api_url: Option<Url>,
|
||||
}
|
||||
|
||||
impl TryFrom<ValidatorDetails> for ValidatorConfigEntry {
|
||||
type Error = BackendError;
|
||||
type Error = BackendError;
|
||||
|
||||
fn try_from(validator: ValidatorDetails) -> Result<Self, Self::Error> {
|
||||
Ok(ValidatorConfigEntry {
|
||||
nymd_url: validator.nymd_url.parse()?,
|
||||
nymd_name: None,
|
||||
api_url: match &validator.api_url {
|
||||
Some(url) => Some(url.parse()?),
|
||||
None => None,
|
||||
},
|
||||
})
|
||||
}
|
||||
fn try_from(validator: ValidatorDetails) -> Result<Self, Self::Error> {
|
||||
Ok(ValidatorConfigEntry {
|
||||
nymd_url: validator.nymd_url.parse()?,
|
||||
nymd_name: None,
|
||||
api_url: match &validator.api_url {
|
||||
Some(url) => Some(url.parse()?),
|
||||
None => None,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<network_config::Validator> for ValidatorConfigEntry {
|
||||
type Error = BackendError;
|
||||
type Error = BackendError;
|
||||
|
||||
fn try_from(validator: network_config::Validator) -> Result<Self, Self::Error> {
|
||||
Ok(ValidatorConfigEntry {
|
||||
nymd_url: validator.nymd_url.parse()?,
|
||||
nymd_name: validator.nymd_name,
|
||||
api_url: match &validator.api_url {
|
||||
Some(url) => Some(url.parse()?),
|
||||
None => None,
|
||||
},
|
||||
})
|
||||
}
|
||||
fn try_from(validator: network_config::Validator) -> Result<Self, Self::Error> {
|
||||
Ok(ValidatorConfigEntry {
|
||||
nymd_url: validator.nymd_url.parse()?,
|
||||
nymd_name: validator.nymd_name,
|
||||
api_url: match &validator.api_url {
|
||||
Some(url) => Some(url.parse()?),
|
||||
None => None,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ValidatorConfigEntry {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let s1 = format!("nymd_url: {}", self.nymd_url);
|
||||
let name = self.nymd_name.as_ref().map(|name| format!(" ({})", name));
|
||||
let s2 = self
|
||||
.api_url
|
||||
.as_ref()
|
||||
.map(|url| format!(", api_url: {}", url));
|
||||
write!(
|
||||
f,
|
||||
" {}{}{},",
|
||||
s1,
|
||||
name.unwrap_or_default(),
|
||||
s2.unwrap_or_default()
|
||||
)
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let s1 = format!("nymd_url: {}", self.nymd_url);
|
||||
let name = self.nymd_name.as_ref().map(|name| format!(" ({})", name));
|
||||
let s2 = self
|
||||
.api_url
|
||||
.as_ref()
|
||||
.map(|url| format!(", api_url: {}", url));
|
||||
write!(
|
||||
f,
|
||||
" {}{}{},",
|
||||
s1,
|
||||
name.unwrap_or_default(),
|
||||
s2.unwrap_or_default()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct OptionalValidators {
|
||||
// User supplied additional validator urls in addition to the hardcoded ones.
|
||||
// These are separate fields, rather than a map, to force the serialization order.
|
||||
mainnet: Option<Vec<ValidatorConfigEntry>>,
|
||||
sandbox: Option<Vec<ValidatorConfigEntry>>,
|
||||
qa: Option<Vec<ValidatorConfigEntry>>,
|
||||
// User supplied additional validator urls in addition to the hardcoded ones.
|
||||
// These are separate fields, rather than a map, to force the serialization order.
|
||||
mainnet: Option<Vec<ValidatorConfigEntry>>,
|
||||
sandbox: Option<Vec<ValidatorConfigEntry>>,
|
||||
qa: Option<Vec<ValidatorConfigEntry>>,
|
||||
}
|
||||
|
||||
impl OptionalValidators {
|
||||
pub fn validators(&self, network: WalletNetwork) -> impl Iterator<Item = &ValidatorConfigEntry> {
|
||||
match network {
|
||||
WalletNetwork::MAINNET => self.mainnet.as_ref(),
|
||||
WalletNetwork::SANDBOX => self.sandbox.as_ref(),
|
||||
WalletNetwork::QA => self.qa.as_ref(),
|
||||
pub fn validators(
|
||||
&self,
|
||||
network: WalletNetwork,
|
||||
) -> impl Iterator<Item = &ValidatorConfigEntry> {
|
||||
match network {
|
||||
WalletNetwork::MAINNET => self.mainnet.as_ref(),
|
||||
WalletNetwork::SANDBOX => self.sandbox.as_ref(),
|
||||
WalletNetwork::QA => self.qa.as_ref(),
|
||||
}
|
||||
.into_iter()
|
||||
.flatten()
|
||||
}
|
||||
.into_iter()
|
||||
.flatten()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for OptionalValidators {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let s1 = self
|
||||
.mainnet
|
||||
.as_ref()
|
||||
.map(|validators| format!("mainnet: [\n{}\n]", validators.iter().format("\n")))
|
||||
.unwrap_or_default();
|
||||
let s2 = self
|
||||
.sandbox
|
||||
.as_ref()
|
||||
.map(|validators| format!(",\nsandbox: [\n{}\n]", validators.iter().format("\n")))
|
||||
.unwrap_or_default();
|
||||
let s3 = self
|
||||
.qa
|
||||
.as_ref()
|
||||
.map(|validators| format!(",\nqa: [\n{}\n]", validators.iter().format("\n")))
|
||||
.unwrap_or_default();
|
||||
write!(f, "{}{}{}", s1, s2, s3)
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let s1 = self
|
||||
.mainnet
|
||||
.as_ref()
|
||||
.map(|validators| format!("mainnet: [\n{}\n]", validators.iter().format("\n")))
|
||||
.unwrap_or_default();
|
||||
let s2 = self
|
||||
.sandbox
|
||||
.as_ref()
|
||||
.map(|validators| format!(",\nsandbox: [\n{}\n]", validators.iter().format("\n")))
|
||||
.unwrap_or_default();
|
||||
let s3 = self
|
||||
.qa
|
||||
.as_ref()
|
||||
.map(|validators| format!(",\nqa: [\n{}\n]", validators.iter().format("\n")))
|
||||
.unwrap_or_default();
|
||||
write!(f, "{}{}{}", s1, s2, s3)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
fn test_config() -> Config {
|
||||
let netconfig = NetworkConfig {
|
||||
selected_nymd_url: None,
|
||||
selected_api_url: Some("https://my_api_url.com".parse().unwrap()),
|
||||
fn test_config() -> Config {
|
||||
let netconfig = NetworkConfig {
|
||||
selected_nymd_url: None,
|
||||
selected_api_url: Some("https://my_api_url.com".parse().unwrap()),
|
||||
|
||||
validator_urls: Some(vec![
|
||||
ValidatorConfigEntry {
|
||||
nymd_url: "https://foo".parse().unwrap(),
|
||||
nymd_name: Some("FooName".to_string()),
|
||||
api_url: None,
|
||||
},
|
||||
ValidatorConfigEntry {
|
||||
nymd_url: "https://bar".parse().unwrap(),
|
||||
nymd_name: None,
|
||||
api_url: Some("https://bar/api".parse().unwrap()),
|
||||
},
|
||||
ValidatorConfigEntry {
|
||||
nymd_url: "https://baz".parse().unwrap(),
|
||||
nymd_name: None,
|
||||
api_url: Some("https://baz/api".parse().unwrap()),
|
||||
},
|
||||
]),
|
||||
..NetworkConfig::default()
|
||||
};
|
||||
validator_urls: Some(vec![
|
||||
ValidatorConfigEntry {
|
||||
nymd_url: "https://foo".parse().unwrap(),
|
||||
nymd_name: Some("FooName".to_string()),
|
||||
api_url: None,
|
||||
},
|
||||
ValidatorConfigEntry {
|
||||
nymd_url: "https://bar".parse().unwrap(),
|
||||
nymd_name: None,
|
||||
api_url: Some("https://bar/api".parse().unwrap()),
|
||||
},
|
||||
ValidatorConfigEntry {
|
||||
nymd_url: "https://baz".parse().unwrap(),
|
||||
nymd_name: None,
|
||||
api_url: Some("https://baz/api".parse().unwrap()),
|
||||
},
|
||||
]),
|
||||
..NetworkConfig::default()
|
||||
};
|
||||
|
||||
Config {
|
||||
base: Base::default(),
|
||||
global: Some(GlobalConfig::default()),
|
||||
networks: [(WalletNetwork::MAINNET.as_key(), netconfig)]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
Config {
|
||||
base: Base::default(),
|
||||
global: Some(GlobalConfig::default()),
|
||||
networks: [(WalletNetwork::MAINNET.as_key(), netconfig)]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_to_toml() {
|
||||
let config = test_config();
|
||||
let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()];
|
||||
assert_eq!(
|
||||
toml::to_string_pretty(netconfig).unwrap(),
|
||||
r#"version = 1
|
||||
#[test]
|
||||
fn serialize_to_toml() {
|
||||
let config = test_config();
|
||||
let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()];
|
||||
assert_eq!(
|
||||
toml::to_string_pretty(netconfig).unwrap(),
|
||||
r#"version = 1
|
||||
selected_api_url = 'https://my_api_url.com/'
|
||||
|
||||
[[validator_urls]]
|
||||
@@ -486,17 +483,17 @@ api_url = 'https://bar/api'
|
||||
nymd_url = 'https://baz/'
|
||||
api_url = 'https://baz/api'
|
||||
"#
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_to_json() {
|
||||
let config = test_config();
|
||||
let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()];
|
||||
println!("{}", serde_json::to_string_pretty(netconfig).unwrap());
|
||||
assert_eq!(
|
||||
serde_json::to_string_pretty(netconfig).unwrap(),
|
||||
r#"{
|
||||
#[test]
|
||||
fn serialize_to_json() {
|
||||
let config = test_config();
|
||||
let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()];
|
||||
println!("{}", serde_json::to_string_pretty(netconfig).unwrap());
|
||||
assert_eq!(
|
||||
serde_json::to_string_pretty(netconfig).unwrap(),
|
||||
r#"{
|
||||
"version": 1,
|
||||
"selected_nymd_url": null,
|
||||
"selected_api_url": "https://my_api_url.com/",
|
||||
@@ -518,53 +515,53 @@ api_url = 'https://baz/api'
|
||||
}
|
||||
]
|
||||
}"#
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_and_deserialize_to_toml() {
|
||||
let config = test_config();
|
||||
let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()];
|
||||
let config_str = toml::to_string_pretty(netconfig).unwrap();
|
||||
let config_from_toml: NetworkConfig = toml::from_str(&config_str).unwrap();
|
||||
assert_eq!(netconfig, &config_from_toml);
|
||||
}
|
||||
#[test]
|
||||
fn serialize_and_deserialize_to_toml() {
|
||||
let config = test_config();
|
||||
let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()];
|
||||
let config_str = toml::to_string_pretty(netconfig).unwrap();
|
||||
let config_from_toml: NetworkConfig = toml::from_str(&config_str).unwrap();
|
||||
assert_eq!(netconfig, &config_from_toml);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_urls_parsed_from_config() {
|
||||
let config = test_config();
|
||||
#[test]
|
||||
fn get_urls_parsed_from_config() {
|
||||
let config = test_config();
|
||||
|
||||
let nymd_url = config
|
||||
.get_configured_validators(WalletNetwork::MAINNET)
|
||||
.next()
|
||||
.map(|v| v.nymd_url)
|
||||
.unwrap();
|
||||
assert_eq!(nymd_url.as_ref(), "https://foo/");
|
||||
let nymd_url = config
|
||||
.get_configured_validators(WalletNetwork::MAINNET)
|
||||
.next()
|
||||
.map(|v| v.nymd_url)
|
||||
.unwrap();
|
||||
assert_eq!(nymd_url.as_ref(), "https://foo/");
|
||||
|
||||
// The first entry is missing an API URL
|
||||
let api_url = config
|
||||
.get_configured_validators(WalletNetwork::MAINNET)
|
||||
.next()
|
||||
.and_then(|v| v.api_url);
|
||||
assert_eq!(api_url, None);
|
||||
}
|
||||
// The first entry is missing an API URL
|
||||
let api_url = config
|
||||
.get_configured_validators(WalletNetwork::MAINNET)
|
||||
.next()
|
||||
.and_then(|v| v.api_url);
|
||||
assert_eq!(api_url, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_urls_from_defaults() {
|
||||
let config = Config::default();
|
||||
#[test]
|
||||
fn get_urls_from_defaults() {
|
||||
let config = Config::default();
|
||||
|
||||
let nymd_url = config
|
||||
.get_base_validators(WalletNetwork::MAINNET)
|
||||
.next()
|
||||
.map(|v| v.nymd_url)
|
||||
.unwrap();
|
||||
assert_eq!(nymd_url.as_ref(), "https://rpc.nyx.nodes.guru/");
|
||||
let nymd_url = config
|
||||
.get_base_validators(WalletNetwork::MAINNET)
|
||||
.next()
|
||||
.map(|v| v.nymd_url)
|
||||
.unwrap();
|
||||
assert_eq!(nymd_url.as_ref(), "https://rpc.nyx.nodes.guru/");
|
||||
|
||||
let api_url = config
|
||||
.get_base_validators(WalletNetwork::MAINNET)
|
||||
.next()
|
||||
.and_then(|v| v.api_url)
|
||||
.unwrap();
|
||||
assert_eq!(api_url.as_ref(), "https://validator.nymtech.net/api/",);
|
||||
}
|
||||
let api_url = config
|
||||
.get_base_validators(WalletNetwork::MAINNET)
|
||||
.next()
|
||||
.and_then(|v| v.api_url)
|
||||
.unwrap();
|
||||
assert_eq!(api_url.as_ref(), "https://validator.nymtech.net/api/",);
|
||||
}
|
||||
}
|
||||
|
||||
+106
-106
@@ -7,117 +7,117 @@ use validator_client::{nymd::error::NymdError, ValidatorClientError};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum BackendError {
|
||||
#[error("{source}")]
|
||||
TypesError {
|
||||
#[from]
|
||||
source: TypesError,
|
||||
},
|
||||
#[error("{source}")]
|
||||
Bip39Error {
|
||||
#[from]
|
||||
source: bip39::Error,
|
||||
},
|
||||
#[error("{source}")]
|
||||
TendermintError {
|
||||
#[from]
|
||||
source: tendermint_rpc::Error,
|
||||
},
|
||||
#[error("{source}")]
|
||||
NymdError {
|
||||
#[from]
|
||||
source: NymdError,
|
||||
},
|
||||
#[error("{source}")]
|
||||
CosmwasmStd {
|
||||
#[from]
|
||||
source: cosmwasm_std::StdError,
|
||||
},
|
||||
#[error("{source}")]
|
||||
ErrorReport {
|
||||
#[from]
|
||||
source: eyre::Report,
|
||||
},
|
||||
#[error("{source}")]
|
||||
ValidatorApiError {
|
||||
#[from]
|
||||
source: ValidatorAPIError,
|
||||
},
|
||||
#[error("{source}")]
|
||||
KeyDerivationError {
|
||||
#[from]
|
||||
source: argon2::Error,
|
||||
},
|
||||
#[error("{source}")]
|
||||
IOError {
|
||||
#[from]
|
||||
source: io::Error,
|
||||
},
|
||||
#[error("{source}")]
|
||||
SerdeJsonError {
|
||||
#[from]
|
||||
source: serde_json::Error,
|
||||
},
|
||||
#[error("{source}")]
|
||||
MalformedUrlProvided {
|
||||
#[from]
|
||||
source: url::ParseError,
|
||||
},
|
||||
#[error("{source}")]
|
||||
ReqwestError {
|
||||
#[from]
|
||||
source: reqwest::Error,
|
||||
},
|
||||
#[error("failed to encrypt the given data with the provided password")]
|
||||
EncryptionError,
|
||||
#[error("failed to decrypt the given data with the provided password")]
|
||||
DecryptionError,
|
||||
#[error("Client has not been initialized yet, connect with mnemonic to initialize")]
|
||||
ClientNotInitialized,
|
||||
#[error("No balance available for address {0}")]
|
||||
NoBalance(String),
|
||||
#[error("The provided network is not supported (yet)")]
|
||||
NetworkNotSupported(config::defaults::all::Network),
|
||||
#[error("Could not access the local data storage directory")]
|
||||
UnknownStorageDirectory,
|
||||
#[error("The wallet file already exists")]
|
||||
WalletFileAlreadyExists,
|
||||
#[error("The wallet file is not found")]
|
||||
WalletFileNotFound,
|
||||
#[error("Login ID not found in wallet")]
|
||||
WalletNoSuchLoginId,
|
||||
#[error("Account ID not found in wallet login")]
|
||||
WalletNoSuchAccountIdInWalletLogin,
|
||||
#[error("Login ID already found in wallet")]
|
||||
WalletLoginIdAlreadyExists,
|
||||
#[error("Account ID already found in wallet login")]
|
||||
WalletAccountIdAlreadyExistsInWalletLogin,
|
||||
#[error("Mnemonic already found in wallet login, was it already imported?")]
|
||||
WalletMnemonicAlreadyExistsInWalletLogin,
|
||||
#[error("Adding a different password to the wallet not currently supported")]
|
||||
WalletDifferentPasswordDetected,
|
||||
#[error("Unexpected mnemonic account for login")]
|
||||
WalletUnexpectedMnemonicAccount,
|
||||
// #[error("Unexpected multiple account entry for login")]
|
||||
// WalletUnexpectedMultipleAccounts,
|
||||
#[error("Failed to derive address from mnemonic")]
|
||||
FailedToDeriveAddress,
|
||||
#[error("{source}")]
|
||||
TypesError {
|
||||
#[from]
|
||||
source: TypesError,
|
||||
},
|
||||
#[error("{source}")]
|
||||
Bip39Error {
|
||||
#[from]
|
||||
source: bip39::Error,
|
||||
},
|
||||
#[error("{source}")]
|
||||
TendermintError {
|
||||
#[from]
|
||||
source: tendermint_rpc::Error,
|
||||
},
|
||||
#[error("{source}")]
|
||||
NymdError {
|
||||
#[from]
|
||||
source: NymdError,
|
||||
},
|
||||
#[error("{source}")]
|
||||
CosmwasmStd {
|
||||
#[from]
|
||||
source: cosmwasm_std::StdError,
|
||||
},
|
||||
#[error("{source}")]
|
||||
ErrorReport {
|
||||
#[from]
|
||||
source: eyre::Report,
|
||||
},
|
||||
#[error("{source}")]
|
||||
ValidatorApiError {
|
||||
#[from]
|
||||
source: ValidatorAPIError,
|
||||
},
|
||||
#[error("{source}")]
|
||||
KeyDerivationError {
|
||||
#[from]
|
||||
source: argon2::Error,
|
||||
},
|
||||
#[error("{source}")]
|
||||
IOError {
|
||||
#[from]
|
||||
source: io::Error,
|
||||
},
|
||||
#[error("{source}")]
|
||||
SerdeJsonError {
|
||||
#[from]
|
||||
source: serde_json::Error,
|
||||
},
|
||||
#[error("{source}")]
|
||||
MalformedUrlProvided {
|
||||
#[from]
|
||||
source: url::ParseError,
|
||||
},
|
||||
#[error("{source}")]
|
||||
ReqwestError {
|
||||
#[from]
|
||||
source: reqwest::Error,
|
||||
},
|
||||
#[error("failed to encrypt the given data with the provided password")]
|
||||
EncryptionError,
|
||||
#[error("failed to decrypt the given data with the provided password")]
|
||||
DecryptionError,
|
||||
#[error("Client has not been initialized yet, connect with mnemonic to initialize")]
|
||||
ClientNotInitialized,
|
||||
#[error("No balance available for address {0}")]
|
||||
NoBalance(String),
|
||||
#[error("The provided network is not supported (yet)")]
|
||||
NetworkNotSupported(config::defaults::all::Network),
|
||||
#[error("Could not access the local data storage directory")]
|
||||
UnknownStorageDirectory,
|
||||
#[error("The wallet file already exists")]
|
||||
WalletFileAlreadyExists,
|
||||
#[error("The wallet file is not found")]
|
||||
WalletFileNotFound,
|
||||
#[error("Login ID not found in wallet")]
|
||||
WalletNoSuchLoginId,
|
||||
#[error("Account ID not found in wallet login")]
|
||||
WalletNoSuchAccountIdInWalletLogin,
|
||||
#[error("Login ID already found in wallet")]
|
||||
WalletLoginIdAlreadyExists,
|
||||
#[error("Account ID already found in wallet login")]
|
||||
WalletAccountIdAlreadyExistsInWalletLogin,
|
||||
#[error("Mnemonic already found in wallet login, was it already imported?")]
|
||||
WalletMnemonicAlreadyExistsInWalletLogin,
|
||||
#[error("Adding a different password to the wallet not currently supported")]
|
||||
WalletDifferentPasswordDetected,
|
||||
#[error("Unexpected mnemonic account for login")]
|
||||
WalletUnexpectedMnemonicAccount,
|
||||
// #[error("Unexpected multiple account entry for login")]
|
||||
// WalletUnexpectedMultipleAccounts,
|
||||
#[error("Failed to derive address from mnemonic")]
|
||||
FailedToDeriveAddress,
|
||||
}
|
||||
|
||||
impl Serialize for BackendError {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.collect_str(self)
|
||||
}
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.collect_str(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ValidatorClientError> for BackendError {
|
||||
fn from(e: ValidatorClientError) -> Self {
|
||||
match e {
|
||||
ValidatorClientError::ValidatorAPIError { source } => source.into(),
|
||||
ValidatorClientError::MalformedUrlProvided(e) => e.into(),
|
||||
ValidatorClientError::NymdError(e) => e.into(),
|
||||
fn from(e: ValidatorClientError) -> Self {
|
||||
match e {
|
||||
ValidatorClientError::ValidatorAPIError { source } => source.into(),
|
||||
ValidatorClientError::MalformedUrlProvided(e) => e.into(),
|
||||
ValidatorClientError::NymdError(e) => e.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+122
-122
@@ -1,6 +1,6 @@
|
||||
#![cfg_attr(
|
||||
all(not(debug_assertions), target_os = "windows"),
|
||||
windows_subsystem = "windows"
|
||||
all(not(debug_assertions), target_os = "windows"),
|
||||
windows_subsystem = "windows"
|
||||
)]
|
||||
|
||||
use mixnet_contract_common::{Gateway, MixNode};
|
||||
@@ -27,130 +27,130 @@ use crate::operations::vesting;
|
||||
use crate::state::State;
|
||||
|
||||
fn main() {
|
||||
dotenv::dotenv().ok();
|
||||
setup_logging();
|
||||
dotenv::dotenv().ok();
|
||||
setup_logging();
|
||||
|
||||
tauri::Builder::default()
|
||||
.manage(Arc::new(RwLock::new(State::default())))
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
mixnet::account::add_account_for_password,
|
||||
mixnet::account::connect_with_mnemonic,
|
||||
mixnet::account::create_new_mnemonic,
|
||||
mixnet::account::create_password,
|
||||
mixnet::account::does_password_file_exist,
|
||||
mixnet::account::get_balance,
|
||||
mixnet::account::list_accounts,
|
||||
mixnet::account::logout,
|
||||
mixnet::account::remove_account_for_password,
|
||||
mixnet::account::remove_password,
|
||||
mixnet::account::show_mnemonic_for_account_in_password,
|
||||
mixnet::account::sign_in_with_password,
|
||||
mixnet::account::sign_in_with_password_and_account_id,
|
||||
mixnet::account::switch_network,
|
||||
mixnet::account::validate_mnemonic,
|
||||
mixnet::admin::get_contract_settings,
|
||||
mixnet::admin::update_contract_settings,
|
||||
mixnet::bond::bond_gateway,
|
||||
mixnet::bond::bond_mixnode,
|
||||
mixnet::bond::gateway_bond_details,
|
||||
mixnet::bond::get_operator_rewards,
|
||||
mixnet::bond::mixnode_bond_details,
|
||||
mixnet::bond::unbond_gateway,
|
||||
mixnet::bond::unbond_mixnode,
|
||||
mixnet::bond::update_mixnode,
|
||||
mixnet::delegate::delegate_to_mixnode,
|
||||
mixnet::delegate::get_delegator_rewards,
|
||||
mixnet::delegate::get_pending_delegation_events,
|
||||
mixnet::delegate::get_delegation_summary,
|
||||
mixnet::delegate::get_all_pending_delegation_events,
|
||||
mixnet::delegate::get_all_mix_delegations,
|
||||
mixnet::delegate::undelegate_from_mixnode,
|
||||
mixnet::epoch::get_current_epoch,
|
||||
mixnet::send::send,
|
||||
network_config::add_validator,
|
||||
network_config::get_validator_api_urls,
|
||||
network_config::get_validator_nymd_urls,
|
||||
network_config::remove_validator,
|
||||
network_config::select_validator_api_url,
|
||||
network_config::select_validator_nymd_url,
|
||||
network_config::update_validator_urls,
|
||||
state::load_config_from_files,
|
||||
state::save_config_to_files,
|
||||
utils::owns_gateway,
|
||||
utils::owns_mixnode,
|
||||
utils::get_env,
|
||||
validator_api::status::gateway_core_node_status,
|
||||
validator_api::status::mixnode_core_node_status,
|
||||
validator_api::status::mixnode_inclusion_probability,
|
||||
validator_api::status::mixnode_reward_estimation,
|
||||
validator_api::status::mixnode_stake_saturation,
|
||||
validator_api::status::mixnode_status,
|
||||
vesting::bond::vesting_bond_gateway,
|
||||
vesting::bond::vesting_bond_mixnode,
|
||||
vesting::bond::vesting_unbond_gateway,
|
||||
vesting::bond::vesting_unbond_mixnode,
|
||||
vesting::bond::vesting_update_mixnode,
|
||||
vesting::bond::withdraw_vested_coins,
|
||||
vesting::delegate::get_pending_vesting_delegation_events,
|
||||
vesting::delegate::vesting_delegate_to_mixnode,
|
||||
vesting::delegate::vesting_undelegate_from_mixnode,
|
||||
vesting::queries::delegated_free,
|
||||
vesting::queries::delegated_vesting,
|
||||
vesting::queries::get_account_info,
|
||||
vesting::queries::get_current_vesting_period,
|
||||
vesting::queries::locked_coins,
|
||||
vesting::queries::original_vesting,
|
||||
vesting::queries::spendable_coins,
|
||||
vesting::queries::vested_coins,
|
||||
vesting::queries::vesting_coins,
|
||||
vesting::queries::vesting_end_time,
|
||||
vesting::queries::vesting_get_gateway_pledge,
|
||||
vesting::queries::vesting_get_mixnode_pledge,
|
||||
vesting::queries::vesting_start_time,
|
||||
simulate::admin::simulate_update_contract_settings,
|
||||
simulate::cosmos::simulate_send,
|
||||
simulate::mixnet::simulate_bond_gateway,
|
||||
simulate::mixnet::simulate_unbond_gateway,
|
||||
simulate::mixnet::simulate_bond_mixnode,
|
||||
simulate::mixnet::simulate_unbond_mixnode,
|
||||
simulate::mixnet::simulate_update_mixnode,
|
||||
simulate::mixnet::simulate_delegate_to_mixnode,
|
||||
simulate::mixnet::simulate_undelegate_from_mixnode,
|
||||
simulate::vesting::simulate_vesting_bond_gateway,
|
||||
simulate::vesting::simulate_vesting_unbond_gateway,
|
||||
simulate::vesting::simulate_vesting_bond_mixnode,
|
||||
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!())
|
||||
.expect("error while running tauri application");
|
||||
tauri::Builder::default()
|
||||
.manage(Arc::new(RwLock::new(State::default())))
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
mixnet::account::add_account_for_password,
|
||||
mixnet::account::connect_with_mnemonic,
|
||||
mixnet::account::create_new_mnemonic,
|
||||
mixnet::account::create_password,
|
||||
mixnet::account::does_password_file_exist,
|
||||
mixnet::account::get_balance,
|
||||
mixnet::account::list_accounts,
|
||||
mixnet::account::logout,
|
||||
mixnet::account::remove_account_for_password,
|
||||
mixnet::account::remove_password,
|
||||
mixnet::account::show_mnemonic_for_account_in_password,
|
||||
mixnet::account::sign_in_with_password,
|
||||
mixnet::account::sign_in_with_password_and_account_id,
|
||||
mixnet::account::switch_network,
|
||||
mixnet::account::validate_mnemonic,
|
||||
mixnet::admin::get_contract_settings,
|
||||
mixnet::admin::update_contract_settings,
|
||||
mixnet::bond::bond_gateway,
|
||||
mixnet::bond::bond_mixnode,
|
||||
mixnet::bond::gateway_bond_details,
|
||||
mixnet::bond::get_operator_rewards,
|
||||
mixnet::bond::mixnode_bond_details,
|
||||
mixnet::bond::unbond_gateway,
|
||||
mixnet::bond::unbond_mixnode,
|
||||
mixnet::bond::update_mixnode,
|
||||
mixnet::delegate::delegate_to_mixnode,
|
||||
mixnet::delegate::get_delegator_rewards,
|
||||
mixnet::delegate::get_pending_delegation_events,
|
||||
mixnet::delegate::get_delegation_summary,
|
||||
mixnet::delegate::get_all_pending_delegation_events,
|
||||
mixnet::delegate::get_all_mix_delegations,
|
||||
mixnet::delegate::undelegate_from_mixnode,
|
||||
mixnet::epoch::get_current_epoch,
|
||||
mixnet::send::send,
|
||||
network_config::add_validator,
|
||||
network_config::get_validator_api_urls,
|
||||
network_config::get_validator_nymd_urls,
|
||||
network_config::remove_validator,
|
||||
network_config::select_validator_api_url,
|
||||
network_config::select_validator_nymd_url,
|
||||
network_config::update_validator_urls,
|
||||
state::load_config_from_files,
|
||||
state::save_config_to_files,
|
||||
utils::owns_gateway,
|
||||
utils::owns_mixnode,
|
||||
utils::get_env,
|
||||
validator_api::status::gateway_core_node_status,
|
||||
validator_api::status::mixnode_core_node_status,
|
||||
validator_api::status::mixnode_inclusion_probability,
|
||||
validator_api::status::mixnode_reward_estimation,
|
||||
validator_api::status::mixnode_stake_saturation,
|
||||
validator_api::status::mixnode_status,
|
||||
vesting::bond::vesting_bond_gateway,
|
||||
vesting::bond::vesting_bond_mixnode,
|
||||
vesting::bond::vesting_unbond_gateway,
|
||||
vesting::bond::vesting_unbond_mixnode,
|
||||
vesting::bond::vesting_update_mixnode,
|
||||
vesting::bond::withdraw_vested_coins,
|
||||
vesting::delegate::get_pending_vesting_delegation_events,
|
||||
vesting::delegate::vesting_delegate_to_mixnode,
|
||||
vesting::delegate::vesting_undelegate_from_mixnode,
|
||||
vesting::queries::delegated_free,
|
||||
vesting::queries::delegated_vesting,
|
||||
vesting::queries::get_account_info,
|
||||
vesting::queries::get_current_vesting_period,
|
||||
vesting::queries::locked_coins,
|
||||
vesting::queries::original_vesting,
|
||||
vesting::queries::spendable_coins,
|
||||
vesting::queries::vested_coins,
|
||||
vesting::queries::vesting_coins,
|
||||
vesting::queries::vesting_end_time,
|
||||
vesting::queries::vesting_get_gateway_pledge,
|
||||
vesting::queries::vesting_get_mixnode_pledge,
|
||||
vesting::queries::vesting_start_time,
|
||||
simulate::admin::simulate_update_contract_settings,
|
||||
simulate::cosmos::simulate_send,
|
||||
simulate::mixnet::simulate_bond_gateway,
|
||||
simulate::mixnet::simulate_unbond_gateway,
|
||||
simulate::mixnet::simulate_bond_mixnode,
|
||||
simulate::mixnet::simulate_unbond_mixnode,
|
||||
simulate::mixnet::simulate_update_mixnode,
|
||||
simulate::mixnet::simulate_delegate_to_mixnode,
|
||||
simulate::mixnet::simulate_undelegate_from_mixnode,
|
||||
simulate::vesting::simulate_vesting_bond_gateway,
|
||||
simulate::vesting::simulate_vesting_unbond_gateway,
|
||||
simulate::vesting::simulate_vesting_bond_mixnode,
|
||||
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!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
||||
fn setup_logging() {
|
||||
let mut log_builder = pretty_env_logger::formatted_timed_builder();
|
||||
if let Ok(s) = ::std::env::var("RUST_LOG") {
|
||||
log_builder.parse_filters(&s);
|
||||
} else {
|
||||
// default to 'Info'
|
||||
log_builder.filter(None, log::LevelFilter::Info);
|
||||
}
|
||||
let mut log_builder = pretty_env_logger::formatted_timed_builder();
|
||||
if let Ok(s) = ::std::env::var("RUST_LOG") {
|
||||
log_builder.parse_filters(&s);
|
||||
} else {
|
||||
// default to 'Info'
|
||||
log_builder.filter(None, log::LevelFilter::Info);
|
||||
}
|
||||
|
||||
if ::std::env::var("RUST_TRACE_OPERATIONS").is_ok() {
|
||||
log_builder.filter_module("nym_wallet::operations", log::LevelFilter::Trace);
|
||||
}
|
||||
if ::std::env::var("RUST_TRACE_OPERATIONS").is_ok() {
|
||||
log_builder.filter_module("nym_wallet::operations", log::LevelFilter::Trace);
|
||||
}
|
||||
|
||||
log_builder
|
||||
.filter_module("hyper", log::LevelFilter::Warn)
|
||||
.filter_module("tokio_reactor", log::LevelFilter::Warn)
|
||||
.filter_module("reqwest", log::LevelFilter::Warn)
|
||||
.filter_module("mio", log::LevelFilter::Warn)
|
||||
.filter_module("want", log::LevelFilter::Warn)
|
||||
.filter_module("sled", log::LevelFilter::Warn)
|
||||
.filter_module("tungstenite", log::LevelFilter::Warn)
|
||||
.filter_module("tokio_tungstenite", log::LevelFilter::Warn)
|
||||
.filter_module("rustls", log::LevelFilter::Warn)
|
||||
.filter_module("tokio_util", log::LevelFilter::Warn)
|
||||
.init();
|
||||
log_builder
|
||||
.filter_module("hyper", log::LevelFilter::Warn)
|
||||
.filter_module("tokio_reactor", log::LevelFilter::Warn)
|
||||
.filter_module("reqwest", log::LevelFilter::Warn)
|
||||
.filter_module("mio", log::LevelFilter::Warn)
|
||||
.filter_module("want", log::LevelFilter::Warn)
|
||||
.filter_module("sled", log::LevelFilter::Warn)
|
||||
.filter_module("tungstenite", log::LevelFilter::Warn)
|
||||
.filter_module("tokio_tungstenite", log::LevelFilter::Warn)
|
||||
.filter_module("rustls", log::LevelFilter::Warn)
|
||||
.filter_module("tokio_util", log::LevelFilter::Warn)
|
||||
.init();
|
||||
}
|
||||
|
||||
@@ -3,25 +3,25 @@ use tauri::Menu;
|
||||
use tauri::{MenuItem, Submenu};
|
||||
|
||||
pub trait AddDefaultSubmenus {
|
||||
fn add_default_app_submenu_if_macos(self) -> Self;
|
||||
fn add_default_app_submenu_if_macos(self) -> Self;
|
||||
}
|
||||
|
||||
impl AddDefaultSubmenus for Menu {
|
||||
fn add_default_app_submenu_if_macos(self) -> Menu {
|
||||
#[cfg(target_os = "macos")]
|
||||
return self.add_submenu(Submenu::new(
|
||||
"Menu",
|
||||
Menu::new()
|
||||
.add_native_item(MenuItem::Copy)
|
||||
.add_native_item(MenuItem::Cut)
|
||||
.add_native_item(MenuItem::Paste)
|
||||
.add_native_item(MenuItem::Hide)
|
||||
.add_native_item(MenuItem::HideOthers)
|
||||
.add_native_item(MenuItem::SelectAll)
|
||||
.add_native_item(MenuItem::ShowAll)
|
||||
.add_native_item(MenuItem::Quit),
|
||||
));
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
return self;
|
||||
}
|
||||
fn add_default_app_submenu_if_macos(self) -> Menu {
|
||||
#[cfg(target_os = "macos")]
|
||||
return self.add_submenu(Submenu::new(
|
||||
"Menu",
|
||||
Menu::new()
|
||||
.add_native_item(MenuItem::Copy)
|
||||
.add_native_item(MenuItem::Cut)
|
||||
.add_native_item(MenuItem::Paste)
|
||||
.add_native_item(MenuItem::Hide)
|
||||
.add_native_item(MenuItem::HideOthers)
|
||||
.add_native_item(MenuItem::SelectAll)
|
||||
.add_native_item(MenuItem::ShowAll)
|
||||
.add_native_item(MenuItem::Quit),
|
||||
));
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
return self;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,79 +13,79 @@ use crate::state::State;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_validator_nymd_urls(
|
||||
network: WalletNetwork,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
network: WalletNetwork,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<ValidatorUrls, BackendError> {
|
||||
let state = state.read().await;
|
||||
let urls: Vec<ValidatorUrl> = state.get_nymd_urls(network).collect();
|
||||
Ok(ValidatorUrls { urls })
|
||||
let state = state.read().await;
|
||||
let urls: Vec<ValidatorUrl> = state.get_nymd_urls(network).collect();
|
||||
Ok(ValidatorUrls { urls })
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_validator_api_urls(
|
||||
network: WalletNetwork,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
network: WalletNetwork,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<ValidatorUrls, BackendError> {
|
||||
let state = state.read().await;
|
||||
let urls: Vec<ValidatorUrl> = state.get_api_urls(network).collect();
|
||||
Ok(ValidatorUrls { urls })
|
||||
let state = state.read().await;
|
||||
let urls: Vec<ValidatorUrl> = state.get_api_urls(network).collect();
|
||||
Ok(ValidatorUrls { urls })
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn select_validator_nymd_url(
|
||||
url: &str,
|
||||
network: WalletNetwork,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
url: &str,
|
||||
network: WalletNetwork,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
log::debug!("Selecting new validator nymd_url for {network}: {url}");
|
||||
state
|
||||
.write()
|
||||
.await
|
||||
.select_validator_nymd_url(url, network)?;
|
||||
Ok(())
|
||||
log::debug!("Selecting new validator nymd_url for {network}: {url}");
|
||||
state
|
||||
.write()
|
||||
.await
|
||||
.select_validator_nymd_url(url, network)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn select_validator_api_url(
|
||||
url: &str,
|
||||
network: WalletNetwork,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
url: &str,
|
||||
network: WalletNetwork,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
log::debug!("Selecting new validator api_url for {network}: {url}");
|
||||
state.write().await.select_validator_api_url(url, network)?;
|
||||
Ok(())
|
||||
log::debug!("Selecting new validator api_url for {network}: {url}");
|
||||
state.write().await.select_validator_api_url(url, network)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn add_validator(
|
||||
validator: Validator,
|
||||
network: WalletNetwork,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
validator: Validator,
|
||||
network: WalletNetwork,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
log::debug!("Add validator for {network}: {validator}");
|
||||
let url = validator.try_into()?;
|
||||
state.write().await.add_validator_url(url, network);
|
||||
Ok(())
|
||||
log::debug!("Add validator for {network}: {validator}");
|
||||
let url = validator.try_into()?;
|
||||
state.write().await.add_validator_url(url, network);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn remove_validator(
|
||||
validator: Validator,
|
||||
network: WalletNetwork,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
validator: Validator,
|
||||
network: WalletNetwork,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
log::debug!("Remove validator for {network}: {validator}");
|
||||
let url = validator.try_into()?;
|
||||
state.write().await.remove_validator_url(url, network);
|
||||
Ok(())
|
||||
log::debug!("Remove validator for {network}: {validator}");
|
||||
let url = validator.try_into()?;
|
||||
state.write().await.remove_validator_url(url, network);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Update the list of validators by fecthing additional ones remotely. If it fails, just ignore.
|
||||
#[tauri::command]
|
||||
pub async fn update_validator_urls(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
let mut w_state = state.write().await;
|
||||
let _r = w_state.fetch_updated_validator_urls().await;
|
||||
Ok(())
|
||||
let mut w_state = state.write().await;
|
||||
let _r = w_state.fetch_updated_validator_urls().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,29 +13,29 @@ use crate::state::State;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_contract_settings(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<TauriContractStateParams, BackendError> {
|
||||
log::info!(">>> Getting contract settings");
|
||||
let res = nymd_client!(state).get_contract_settings().await?.into();
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(res)
|
||||
log::info!(">>> Getting contract settings");
|
||||
let res = nymd_client!(state).get_contract_settings().await?.into();
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn update_contract_settings(
|
||||
params: TauriContractStateParams,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
params: TauriContractStateParams,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<TauriContractStateParams, BackendError> {
|
||||
let mixnet_contract_settings_params: ContractStateParams = params.try_into()?;
|
||||
log::info!(
|
||||
">>> Updating contract settings: {:?}",
|
||||
mixnet_contract_settings_params
|
||||
);
|
||||
nymd_client!(state)
|
||||
.update_contract_settings(mixnet_contract_settings_params.clone(), fee)
|
||||
.await?;
|
||||
let res = mixnet_contract_settings_params.into();
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(res)
|
||||
let mixnet_contract_settings_params: ContractStateParams = params.try_into()?;
|
||||
log::info!(
|
||||
">>> Updating contract settings: {:?}",
|
||||
mixnet_contract_settings_params
|
||||
);
|
||||
nymd_client!(state)
|
||||
.update_contract_settings(mixnet_contract_settings_params.clone(), fee)
|
||||
.await?;
|
||||
let res = mixnet_contract_settings_params.into();
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
@@ -8,167 +8,167 @@ use nym_types::mixnode::MixNodeBond;
|
||||
use nym_types::transaction::TransactionExecuteResult;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use validator_client::nymd::Fee;
|
||||
use validator_client::nymd::{CosmWasmCoin, Fee};
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn bond_gateway(
|
||||
gateway: Gateway,
|
||||
pledge: MajorCurrencyAmount,
|
||||
owner_signature: String,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
gateway: Gateway,
|
||||
pledge: MajorCurrencyAmount,
|
||||
owner_signature: String,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<TransactionExecuteResult, BackendError> {
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
let pledge_minor = pledge.clone().into_minor_cosmwasm_coin()?;
|
||||
log::info!(
|
||||
">>> Bond gateway: identity_key = {}, pledge = {}, pledge_minor = {}, fee = {:?}",
|
||||
&gateway.identity_key,
|
||||
pledge,
|
||||
&pledge_minor,
|
||||
fee,
|
||||
);
|
||||
let res = nymd_client!(state)
|
||||
.bond_gateway(gateway, owner_signature, pledge_minor, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
let pledge_minor = pledge.clone().into();
|
||||
log::info!(
|
||||
">>> Bond gateway: identity_key = {}, pledge = {}, pledge_minor = {}, fee = {:?}",
|
||||
&gateway.identity_key,
|
||||
pledge,
|
||||
&pledge_minor,
|
||||
fee,
|
||||
);
|
||||
let res = nymd_client!(state)
|
||||
.bond_gateway(gateway, owner_signature, pledge_minor, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn unbond_gateway(
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<TransactionExecuteResult, BackendError> {
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
log::info!(">>> Unbond gateway, fee = {:?}", fee);
|
||||
let res = nymd_client!(state).unbond_gateway(fee).await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
log::info!(">>> Unbond gateway, fee = {:?}", fee);
|
||||
let res = nymd_client!(state).unbond_gateway(fee).await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn bond_mixnode(
|
||||
mixnode: MixNode,
|
||||
owner_signature: String,
|
||||
pledge: MajorCurrencyAmount,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
mixnode: MixNode,
|
||||
owner_signature: String,
|
||||
pledge: MajorCurrencyAmount,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<TransactionExecuteResult, BackendError> {
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
let pledge_minor = pledge.clone().into_minor_cosmwasm_coin()?;
|
||||
log::info!(
|
||||
">>> Bond mixnode: identity_key = {}, pledge = {}, pledge_minor = {}, fee = {:?}",
|
||||
mixnode.identity_key,
|
||||
pledge,
|
||||
pledge_minor,
|
||||
fee,
|
||||
);
|
||||
let res = nymd_client!(state)
|
||||
.bond_mixnode(mixnode, owner_signature, pledge_minor, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
let pledge_minor = pledge.clone().into();
|
||||
log::info!(
|
||||
">>> Bond mixnode: identity_key = {}, pledge = {}, pledge_minor = {}, fee = {:?}",
|
||||
mixnode.identity_key,
|
||||
pledge,
|
||||
pledge_minor,
|
||||
fee,
|
||||
);
|
||||
let res = nymd_client!(state)
|
||||
.bond_mixnode(mixnode, owner_signature, pledge_minor, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn unbond_mixnode(
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<TransactionExecuteResult, BackendError> {
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
log::info!(">>> Unbond mixnode, fee = {:?}", fee);
|
||||
let res = nymd_client!(state).unbond_mixnode(fee).await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
log::info!(">>> Unbond mixnode, fee = {:?}", fee);
|
||||
let res = nymd_client!(state).unbond_mixnode(fee).await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn update_mixnode(
|
||||
profit_margin_percent: u8,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
profit_margin_percent: u8,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<TransactionExecuteResult, BackendError> {
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
log::info!(
|
||||
">>> Update mixnode: profit_margin_percent = {}, fee {:?}",
|
||||
profit_margin_percent,
|
||||
fee,
|
||||
);
|
||||
let res = nymd_client!(state)
|
||||
.update_mixnode_config(profit_margin_percent, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
log::info!(
|
||||
">>> Update mixnode: profit_margin_percent = {}, fee {:?}",
|
||||
profit_margin_percent,
|
||||
fee,
|
||||
);
|
||||
let res = nymd_client!(state)
|
||||
.update_mixnode_config(profit_margin_percent, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn mixnode_bond_details(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<Option<MixNodeBond>, BackendError> {
|
||||
log::info!(">>> Get mixnode bond details");
|
||||
let guard = state.read().await;
|
||||
let client = guard.current_client()?;
|
||||
let bond = client.nymd.owns_mixnode(client.nymd.address()).await?;
|
||||
let res = MixNodeBond::from_mixnet_contract_mixnode_bond(bond)?;
|
||||
log::info!(
|
||||
"<<< identity_key = {:?}",
|
||||
res.as_ref().map(|r| r.mix_node.identity_key.to_string())
|
||||
);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(res)
|
||||
log::info!(">>> Get mixnode bond details");
|
||||
let guard = state.read().await;
|
||||
let client = guard.current_client()?;
|
||||
let bond = client.nymd.owns_mixnode(client.nymd.address()).await?;
|
||||
let res = MixNodeBond::from_mixnet_contract_mixnode_bond(bond)?;
|
||||
log::info!(
|
||||
"<<< identity_key = {:?}",
|
||||
res.as_ref().map(|r| r.mix_node.identity_key.to_string())
|
||||
);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn gateway_bond_details(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<Option<GatewayBond>, BackendError> {
|
||||
log::info!(">>> Get gateway bond details");
|
||||
let guard = state.read().await;
|
||||
let client = guard.current_client()?;
|
||||
let bond = client.nymd.owns_gateway(client.nymd.address()).await?;
|
||||
let res = GatewayBond::from_mixnet_contract_gateway_bond(bond)?;
|
||||
log::info!(
|
||||
"<<< identity_key = {:?}",
|
||||
res.as_ref().map(|r| r.gateway.identity_key.to_string())
|
||||
);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(res)
|
||||
log::info!(">>> Get gateway bond details");
|
||||
let guard = state.read().await;
|
||||
let client = guard.current_client()?;
|
||||
let bond = client.nymd.owns_gateway(client.nymd.address()).await?;
|
||||
let res = GatewayBond::from_mixnet_contract_gateway_bond(bond)?;
|
||||
log::info!(
|
||||
"<<< identity_key = {:?}",
|
||||
res.as_ref().map(|r| r.gateway.identity_key.to_string())
|
||||
);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_operator_rewards(
|
||||
address: String,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
address: String,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<MajorCurrencyAmount, BackendError> {
|
||||
log::info!(">>> Get operator rewards for {}", address);
|
||||
let denom = state.read().await.current_network().denom();
|
||||
let rewards_as_minor = nymd_client!(state).get_operator_rewards(address).await?;
|
||||
let amount: MajorCurrencyAmount =
|
||||
MajorCurrencyAmount::from_minor_uint128_and_denom(rewards_as_minor, &denom.to_string())?;
|
||||
log::info!(
|
||||
"<<< rewards_as_minor = {}, amount = {}",
|
||||
rewards_as_minor,
|
||||
amount
|
||||
);
|
||||
Ok(amount)
|
||||
log::info!(">>> Get operator rewards for {}", address);
|
||||
let denom = state.read().await.current_network().denom();
|
||||
let rewards_as_minor = nymd_client!(state).get_operator_rewards(address).await?;
|
||||
let coin = CosmWasmCoin::new(rewards_as_minor.u128(), denom.as_ref());
|
||||
let amount: MajorCurrencyAmount = coin.into();
|
||||
log::info!(
|
||||
"<<< rewards_as_minor = {}, amount = {}",
|
||||
rewards_as_minor,
|
||||
amount
|
||||
);
|
||||
Ok(amount)
|
||||
}
|
||||
|
||||
@@ -1,373 +1,378 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use cosmwasm_std::Coin as CosmWasmCoin;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use mixnet_contract_common::IdentityKey;
|
||||
use nym_types::currency::{CurrencyDenom, MajorCurrencyAmount};
|
||||
use nym_types::delegation::{
|
||||
from_contract_delegation_events, Delegation, DelegationEvent, DelegationRecord,
|
||||
DelegationWithEverything, DelegationsSummaryResponse,
|
||||
};
|
||||
use nym_types::transaction::TransactionExecuteResult;
|
||||
use validator_client::nymd::Fee;
|
||||
|
||||
use crate::error::BackendError;
|
||||
use crate::state::State;
|
||||
use crate::vesting::delegate::get_pending_vesting_delegation_events;
|
||||
use crate::{api_client, nymd_client};
|
||||
use cosmwasm_std::Coin as CosmWasmCoin;
|
||||
use mixnet_contract_common::IdentityKey;
|
||||
use nym_types::currency::{CurrencyDenom, MajorCurrencyAmount};
|
||||
use nym_types::delegation::{
|
||||
from_contract_delegation_events, Delegation, DelegationEvent, DelegationRecord,
|
||||
DelegationWithEverything, DelegationsSummaryResponse,
|
||||
};
|
||||
use nym_types::transaction::TransactionExecuteResult;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use validator_client::nymd::Fee;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_pending_delegation_events(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<Vec<DelegationEvent>, BackendError> {
|
||||
log::info!(">>> Get pending delegation events");
|
||||
let events = nymd_client!(state)
|
||||
.get_pending_delegation_events(nymd_client!(state).address().to_string(), None)
|
||||
.await?;
|
||||
log::info!("<<< {} pending delegation events", events.len());
|
||||
log::trace!("<<< pending delegation events = {:?}", events);
|
||||
log::info!(">>> Get pending delegation events");
|
||||
let events = nymd_client!(state)
|
||||
.get_pending_delegation_events(nymd_client!(state).address().to_string(), None)
|
||||
.await?;
|
||||
log::info!("<<< {} pending delegation events", events.len());
|
||||
log::trace!("<<< pending delegation events = {:?}", events);
|
||||
|
||||
match from_contract_delegation_events(events) {
|
||||
Ok(res) => Ok(res),
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
match from_contract_delegation_events(events) {
|
||||
Ok(res) => Ok(res),
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn delegate_to_mixnode(
|
||||
identity: &str,
|
||||
amount: MajorCurrencyAmount,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
identity: &str,
|
||||
amount: MajorCurrencyAmount,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<TransactionExecuteResult, BackendError> {
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
let delegation: CosmWasmCoin = amount.clone().into_minor_cosmwasm_coin()?;
|
||||
log::info!(
|
||||
">>> Delegate to mixnode: identity_key = {}, amount = {}, minor_amount = {}, fee = {:?}",
|
||||
identity,
|
||||
amount,
|
||||
delegation,
|
||||
fee,
|
||||
);
|
||||
let res = nymd_client!(state)
|
||||
.delegate_to_mixnode(identity, &delegation, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
let delegation = amount.clone().into();
|
||||
log::info!(
|
||||
">>> Delegate to mixnode: identity_key = {}, amount = {}, minor_amount = {}, fee = {:?}",
|
||||
identity,
|
||||
amount,
|
||||
delegation,
|
||||
fee,
|
||||
);
|
||||
let res = nymd_client!(state)
|
||||
.delegate_to_mixnode(identity, delegation, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn undelegate_from_mixnode(
|
||||
identity: &str,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
identity: &str,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<TransactionExecuteResult, BackendError> {
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
log::info!(
|
||||
">>> Undelegate from mixnode: identity_key = {}, fee = {:?}",
|
||||
identity,
|
||||
fee
|
||||
);
|
||||
let res = nymd_client!(state)
|
||||
.remove_mixnode_delegation(identity, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
log::info!(
|
||||
">>> Undelegate from mixnode: identity_key = {}, fee = {:?}",
|
||||
identity,
|
||||
fee
|
||||
);
|
||||
let res = nymd_client!(state)
|
||||
.remove_mixnode_delegation(identity, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
}
|
||||
|
||||
struct DelegationWithHistory {
|
||||
pub delegation: Delegation,
|
||||
pub amount_sum: MajorCurrencyAmount,
|
||||
pub history: Vec<DelegationRecord>,
|
||||
pub delegation: Delegation,
|
||||
pub amount_sum: MajorCurrencyAmount,
|
||||
pub history: Vec<DelegationRecord>,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_all_mix_delegations(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<Vec<DelegationWithEverything>, BackendError> {
|
||||
log::info!(">>> Get all mixnode delegations");
|
||||
todo!("figure out this one after merging")
|
||||
|
||||
// TODO: add endpoint to validator API to get a single mix node bond
|
||||
let mixnodes = api_client!(state).get_mixnodes().await?;
|
||||
|
||||
let address = nymd_client!(state).address().to_string();
|
||||
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
let denom: CurrencyDenom = denom_minor.clone().try_into()?;
|
||||
|
||||
log::info!(" >>> Get delegations");
|
||||
let delegations = nymd_client!(state)
|
||||
.get_delegator_delegations_paged(address.clone(), None, None) // get all delegations, ignoring paging
|
||||
.await?
|
||||
.delegations;
|
||||
log::info!(" <<< {} delegations", delegations.len());
|
||||
|
||||
// first get pending events from the mixnet contract (operations made with unlocked tokens)
|
||||
let mut pending_events_for_account = get_pending_delegation_events(state.clone()).await?;
|
||||
|
||||
// then get pending events from the vesting contract (operations made with locked tokens)
|
||||
let pending_vesting_events = get_pending_vesting_delegation_events(state.clone()).await?;
|
||||
for event in pending_vesting_events {
|
||||
pending_events_for_account.push(event);
|
||||
}
|
||||
|
||||
log::info!(
|
||||
" <<< {} pending delegation events for account",
|
||||
pending_events_for_account.len()
|
||||
);
|
||||
|
||||
let mut map: HashMap<String, DelegationWithHistory> = HashMap::new();
|
||||
|
||||
for pending_event in pending_events_for_account.clone() {
|
||||
if delegations
|
||||
.iter()
|
||||
.any(|d| d.node_identity == pending_event.node_identity)
|
||||
{
|
||||
let amount = pending_event
|
||||
.amount
|
||||
.unwrap_or_else(|| MajorCurrencyAmount::zero(&denom));
|
||||
let delegation = DelegationWithHistory {
|
||||
delegation: Delegation {
|
||||
amount: amount.clone(),
|
||||
node_identity: pending_event.node_identity,
|
||||
proxy: None,
|
||||
owner: pending_event.address,
|
||||
block_height: pending_event.block_height,
|
||||
},
|
||||
amount_sum: amount,
|
||||
history: vec![],
|
||||
};
|
||||
map.insert(delegation.delegation.node_identity.clone(), delegation);
|
||||
}
|
||||
}
|
||||
|
||||
for d in delegations {
|
||||
// create record of delegation
|
||||
let delegated_on_iso_datetime = nymd_client!(state)
|
||||
.get_block_timestamp(Some(d.block_height as u32))
|
||||
.await?
|
||||
.to_rfc3339();
|
||||
let amount: MajorCurrencyAmount = d.amount.clone().try_into()?;
|
||||
let record = DelegationRecord {
|
||||
amount: amount.clone(),
|
||||
block_height: d.block_height,
|
||||
delegated_on_iso_datetime,
|
||||
};
|
||||
|
||||
let entry = map
|
||||
.entry(d.node_identity.clone())
|
||||
.or_insert(DelegationWithHistory {
|
||||
delegation: d.try_into()?,
|
||||
history: vec![],
|
||||
amount_sum: MajorCurrencyAmount::zero(&amount.denom),
|
||||
});
|
||||
|
||||
entry.history.push(record);
|
||||
entry.amount_sum = entry.amount_sum.clone() + amount;
|
||||
}
|
||||
|
||||
let mut with_everything: Vec<DelegationWithEverything> = vec![];
|
||||
|
||||
for item in map {
|
||||
let d = item.1.delegation;
|
||||
let history = item.1.history;
|
||||
let Delegation {
|
||||
owner,
|
||||
node_identity,
|
||||
amount,
|
||||
block_height,
|
||||
proxy,
|
||||
} = d;
|
||||
|
||||
log::trace!(
|
||||
" --- Delegation: node_identity = {}, amount = {}",
|
||||
node_identity,
|
||||
amount
|
||||
);
|
||||
|
||||
let mixnode = mixnodes
|
||||
.iter()
|
||||
.find(|m| m.mix_node.identity_key == node_identity);
|
||||
|
||||
let pledge_amount: Option<MajorCurrencyAmount> =
|
||||
mixnode.and_then(|m| m.pledge_amount.clone().try_into().ok());
|
||||
|
||||
let total_delegation: Option<MajorCurrencyAmount> =
|
||||
mixnode.and_then(|m| m.total_delegation.clone().try_into().ok());
|
||||
|
||||
let profit_margin_percent: Option<u8> = mixnode.map(|m| m.mix_node.profit_margin_percent);
|
||||
|
||||
log::trace!(" >>> Get accumulated rewards: address = {}", address);
|
||||
let accumulated_rewards = match nymd_client!(state)
|
||||
.get_delegator_rewards(address.clone(), node_identity.clone(), proxy.clone())
|
||||
.await
|
||||
{
|
||||
Ok(rewards) => {
|
||||
let amount =
|
||||
MajorCurrencyAmount::from_minor_uint128_and_denom(rewards, denom_minor.as_ref())?;
|
||||
log::trace!(" <<< rewards = {}, amount = {}", rewards, amount);
|
||||
Some(amount)
|
||||
}
|
||||
Err(_) => {
|
||||
log::trace!(" <<< no rewards waiting");
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let pending_events = filter_pending_events(&node_identity, pending_events_for_account.clone());
|
||||
log::trace!(
|
||||
" --- pending events for mixnode = {}",
|
||||
pending_events.len()
|
||||
);
|
||||
|
||||
log::trace!(
|
||||
" >>> Get stake saturation: node_identity = {}",
|
||||
node_identity
|
||||
);
|
||||
let stake_saturation = api_client!(state)
|
||||
.get_mixnode_stake_saturation(&node_identity)
|
||||
.await
|
||||
.ok()
|
||||
.map(|r| r.saturation);
|
||||
log::trace!(" <<< {:?}", stake_saturation);
|
||||
|
||||
log::trace!(
|
||||
" >>> Get average uptime percentage: node_identity = {}",
|
||||
node_identity
|
||||
);
|
||||
let avg_uptime_percent = api_client!(state)
|
||||
.get_mixnode_avg_uptime(&node_identity)
|
||||
.await
|
||||
.ok()
|
||||
.map(|r| r.avg_uptime);
|
||||
log::trace!(" <<< {:?}", avg_uptime_percent);
|
||||
|
||||
log::trace!(
|
||||
" >>> Convert delegated on block height to timestamp: block_height = {}",
|
||||
d.block_height
|
||||
);
|
||||
let timestamp = nymd_client!(state)
|
||||
.get_block_timestamp(Some(d.block_height as u32))
|
||||
.await?;
|
||||
let delegated_on_iso_datetime = timestamp.to_rfc3339();
|
||||
log::trace!(
|
||||
" <<< timestamp = {:?}, delegated_on_iso_datetime = {:?}",
|
||||
timestamp,
|
||||
delegated_on_iso_datetime
|
||||
);
|
||||
|
||||
with_everything.push(DelegationWithEverything {
|
||||
owner: owner.to_string(),
|
||||
node_identity: node_identity.to_string(),
|
||||
amount: item.1.amount_sum,
|
||||
block_height,
|
||||
proxy: proxy.clone(),
|
||||
delegated_on_iso_datetime,
|
||||
stake_saturation,
|
||||
accumulated_rewards,
|
||||
profit_margin_percent,
|
||||
pledge_amount,
|
||||
avg_uptime_percent,
|
||||
total_delegation,
|
||||
pending_events,
|
||||
history,
|
||||
})
|
||||
}
|
||||
log::trace!("<<< {:?}", with_everything);
|
||||
|
||||
Ok(with_everything)
|
||||
// log::info!(">>> Get all mixnode delegations");
|
||||
//
|
||||
// // TODO: add endpoint to validator API to get a single mix node bond
|
||||
// let mixnodes = api_client!(state).get_mixnodes().await?;
|
||||
//
|
||||
// let address = nymd_client!(state).address().to_string();
|
||||
//
|
||||
// let denom_minor = state.read().await.current_network().denom();
|
||||
// let denom: CurrencyDenom = denom_minor.clone().try_into()?;
|
||||
//
|
||||
// log::info!(" >>> Get delegations");
|
||||
// let delegations = nymd_client!(state)
|
||||
// .get_delegator_delegations_paged(address.clone(), None, None) // get all delegations, ignoring paging
|
||||
// .await?
|
||||
// .delegations;
|
||||
// log::info!(" <<< {} delegations", delegations.len());
|
||||
//
|
||||
// // first get pending events from the mixnet contract (operations made with unlocked tokens)
|
||||
// let mut pending_events_for_account = get_pending_delegation_events(state.clone()).await?;
|
||||
//
|
||||
// // then get pending events from the vesting contract (operations made with locked tokens)
|
||||
// let pending_vesting_events = get_pending_vesting_delegation_events(state.clone()).await?;
|
||||
// for event in pending_vesting_events {
|
||||
// pending_events_for_account.push(event);
|
||||
// }
|
||||
//
|
||||
// log::info!(
|
||||
// " <<< {} pending delegation events for account",
|
||||
// pending_events_for_account.len()
|
||||
// );
|
||||
//
|
||||
// let mut map: HashMap<String, DelegationWithHistory> = HashMap::new();
|
||||
//
|
||||
// for pending_event in pending_events_for_account.clone() {
|
||||
// if delegations
|
||||
// .iter()
|
||||
// .any(|d| d.node_identity == pending_event.node_identity)
|
||||
// {
|
||||
// let amount = pending_event
|
||||
// .amount
|
||||
// .unwrap_or_else(|| MajorCurrencyAmount::zero(&denom));
|
||||
// let delegation = DelegationWithHistory {
|
||||
// delegation: Delegation {
|
||||
// amount: amount.clone(),
|
||||
// node_identity: pending_event.node_identity,
|
||||
// proxy: None,
|
||||
// owner: pending_event.address,
|
||||
// block_height: pending_event.block_height,
|
||||
// },
|
||||
// amount_sum: amount,
|
||||
// history: vec![],
|
||||
// };
|
||||
// map.insert(delegation.delegation.node_identity.clone(), delegation);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// for d in delegations {
|
||||
// // create record of delegation
|
||||
// let delegated_on_iso_datetime = nymd_client!(state)
|
||||
// .get_block_timestamp(Some(d.block_height as u32))
|
||||
// .await?
|
||||
// .to_rfc3339();
|
||||
// let amount: MajorCurrencyAmount = d.amount.clone().try_into()?;
|
||||
// let record = DelegationRecord {
|
||||
// amount: amount.clone(),
|
||||
// block_height: d.block_height,
|
||||
// delegated_on_iso_datetime,
|
||||
// };
|
||||
//
|
||||
// let entry = map
|
||||
// .entry(d.node_identity.clone())
|
||||
// .or_insert(DelegationWithHistory {
|
||||
// delegation: d.try_into()?,
|
||||
// history: vec![],
|
||||
// amount_sum: MajorCurrencyAmount::zero(&amount.denom),
|
||||
// });
|
||||
//
|
||||
// entry.history.push(record);
|
||||
// entry.amount_sum = entry.amount_sum.clone() + amount;
|
||||
// }
|
||||
//
|
||||
// let mut with_everything: Vec<DelegationWithEverything> = vec![];
|
||||
//
|
||||
// for item in map {
|
||||
// let d = item.1.delegation;
|
||||
// let history = item.1.history;
|
||||
// let Delegation {
|
||||
// owner,
|
||||
// node_identity,
|
||||
// amount,
|
||||
// block_height,
|
||||
// proxy,
|
||||
// } = d;
|
||||
//
|
||||
// log::trace!(
|
||||
// " --- Delegation: node_identity = {}, amount = {}",
|
||||
// node_identity,
|
||||
// amount
|
||||
// );
|
||||
//
|
||||
// let mixnode = mixnodes
|
||||
// .iter()
|
||||
// .find(|m| m.mix_node.identity_key == node_identity);
|
||||
//
|
||||
// let pledge_amount: Option<MajorCurrencyAmount> =
|
||||
// mixnode.and_then(|m| m.pledge_amount.clone().try_into().ok());
|
||||
//
|
||||
// let total_delegation: Option<MajorCurrencyAmount> =
|
||||
// mixnode.and_then(|m| m.total_delegation.clone().try_into().ok());
|
||||
//
|
||||
// let profit_margin_percent: Option<u8> = mixnode.map(|m| m.mix_node.profit_margin_percent);
|
||||
//
|
||||
// log::trace!(" >>> Get accumulated rewards: address = {}", address);
|
||||
// let accumulated_rewards = match nymd_client!(state)
|
||||
// .get_delegator_rewards(address.clone(), node_identity.clone(), proxy.clone())
|
||||
// .await
|
||||
// {
|
||||
// Ok(rewards) => {
|
||||
// let amount = MajorCurrencyAmount::from_minor_uint128_and_denom(
|
||||
// rewards,
|
||||
// denom_minor.as_ref(),
|
||||
// )?;
|
||||
// log::trace!(" <<< rewards = {}, amount = {}", rewards, amount);
|
||||
// Some(amount)
|
||||
// }
|
||||
// Err(_) => {
|
||||
// log::trace!(" <<< no rewards waiting");
|
||||
// None
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// let pending_events =
|
||||
// filter_pending_events(&node_identity, pending_events_for_account.clone());
|
||||
// log::trace!(
|
||||
// " --- pending events for mixnode = {}",
|
||||
// pending_events.len()
|
||||
// );
|
||||
//
|
||||
// log::trace!(
|
||||
// " >>> Get stake saturation: node_identity = {}",
|
||||
// node_identity
|
||||
// );
|
||||
// let stake_saturation = api_client!(state)
|
||||
// .get_mixnode_stake_saturation(&node_identity)
|
||||
// .await
|
||||
// .ok()
|
||||
// .map(|r| r.saturation);
|
||||
// log::trace!(" <<< {:?}", stake_saturation);
|
||||
//
|
||||
// log::trace!(
|
||||
// " >>> Get average uptime percentage: node_identity = {}",
|
||||
// node_identity
|
||||
// );
|
||||
// let avg_uptime_percent = api_client!(state)
|
||||
// .get_mixnode_avg_uptime(&node_identity)
|
||||
// .await
|
||||
// .ok()
|
||||
// .map(|r| r.avg_uptime);
|
||||
// log::trace!(" <<< {:?}", avg_uptime_percent);
|
||||
//
|
||||
// log::trace!(
|
||||
// " >>> Convert delegated on block height to timestamp: block_height = {}",
|
||||
// d.block_height
|
||||
// );
|
||||
// let timestamp = nymd_client!(state)
|
||||
// .get_block_timestamp(Some(d.block_height as u32))
|
||||
// .await?;
|
||||
// let delegated_on_iso_datetime = timestamp.to_rfc3339();
|
||||
// log::trace!(
|
||||
// " <<< timestamp = {:?}, delegated_on_iso_datetime = {:?}",
|
||||
// timestamp,
|
||||
// delegated_on_iso_datetime
|
||||
// );
|
||||
//
|
||||
// with_everything.push(DelegationWithEverything {
|
||||
// owner: owner.to_string(),
|
||||
// node_identity: node_identity.to_string(),
|
||||
// amount: item.1.amount_sum,
|
||||
// block_height,
|
||||
// proxy: proxy.clone(),
|
||||
// delegated_on_iso_datetime,
|
||||
// stake_saturation,
|
||||
// accumulated_rewards,
|
||||
// profit_margin_percent,
|
||||
// pledge_amount,
|
||||
// avg_uptime_percent,
|
||||
// total_delegation,
|
||||
// pending_events,
|
||||
// history,
|
||||
// })
|
||||
// }
|
||||
// log::trace!("<<< {:?}", with_everything);
|
||||
//
|
||||
// Ok(with_everything)
|
||||
}
|
||||
|
||||
fn filter_pending_events(
|
||||
node_identity: &str,
|
||||
pending_events: Vec<DelegationEvent>,
|
||||
node_identity: &str,
|
||||
pending_events: Vec<DelegationEvent>,
|
||||
) -> Vec<DelegationEvent> {
|
||||
pending_events
|
||||
.iter()
|
||||
.filter(|e| e.node_identity == node_identity)
|
||||
.cloned()
|
||||
.collect::<Vec<DelegationEvent>>()
|
||||
pending_events
|
||||
.iter()
|
||||
.filter(|e| e.node_identity == node_identity)
|
||||
.cloned()
|
||||
.collect::<Vec<DelegationEvent>>()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_delegator_rewards(
|
||||
address: String,
|
||||
mix_identity: IdentityKey,
|
||||
proxy: Option<String>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
address: String,
|
||||
mix_identity: IdentityKey,
|
||||
proxy: Option<String>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<MajorCurrencyAmount, BackendError> {
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
log::info!(
|
||||
">>> Get delegator rewards: mix_identity = {}, proxy = {:?}",
|
||||
mix_identity,
|
||||
proxy
|
||||
);
|
||||
let res = nymd_client!(state)
|
||||
.get_delegator_rewards(address, mix_identity, proxy)
|
||||
.await?;
|
||||
let amount = MajorCurrencyAmount::from_minor_uint128_and_denom(res, denom_minor.as_ref())?;
|
||||
log::info!(">>> res = {}, amount = {}", res, amount);
|
||||
Ok(amount)
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
log::info!(
|
||||
">>> Get delegator rewards: mix_identity = {}, proxy = {:?}",
|
||||
mix_identity,
|
||||
proxy
|
||||
);
|
||||
let res = nymd_client!(state)
|
||||
.get_delegator_rewards(address, mix_identity, proxy)
|
||||
.await?;
|
||||
let coin = CosmWasmCoin::new(res.u128(), denom_minor.as_ref());
|
||||
let amount = coin.into();
|
||||
log::info!(">>> res = {}, amount = {}", res, amount);
|
||||
Ok(amount)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_delegation_summary(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<DelegationsSummaryResponse, BackendError> {
|
||||
log::info!(">>> Get delegation summary");
|
||||
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
let denom: CurrencyDenom = denom_minor.clone().try_into()?;
|
||||
|
||||
let delegations = get_all_mix_delegations(state.clone()).await?;
|
||||
let mut total_delegations = MajorCurrencyAmount::zero(&denom);
|
||||
let mut total_rewards = MajorCurrencyAmount::zero(&denom);
|
||||
|
||||
for d in delegations.clone() {
|
||||
total_delegations = total_delegations + d.amount;
|
||||
if let Some(rewards) = d.accumulated_rewards {
|
||||
total_rewards = total_rewards + rewards;
|
||||
}
|
||||
}
|
||||
log::info!(
|
||||
"<<< {} delegations, total_delegations = {}, total_rewards = {}",
|
||||
delegations.len(),
|
||||
total_delegations,
|
||||
total_rewards
|
||||
);
|
||||
log::trace!("<<< {:?}", delegations);
|
||||
|
||||
Ok(DelegationsSummaryResponse {
|
||||
delegations,
|
||||
total_delegations,
|
||||
total_rewards,
|
||||
})
|
||||
todo!("figure out this one after merging")
|
||||
//
|
||||
// log::info!(">>> Get delegation summary");
|
||||
//
|
||||
// let denom_minor = state.read().await.current_network().denom();
|
||||
// let denom: CurrencyDenom = denom_minor.clone().try_into()?;
|
||||
//
|
||||
// let delegations = get_all_mix_delegations(state.clone()).await?;
|
||||
// let mut total_delegations = MajorCurrencyAmount::zero(&denom);
|
||||
// let mut total_rewards = MajorCurrencyAmount::zero(&denom);
|
||||
//
|
||||
// for d in delegations.clone() {
|
||||
// total_delegations = total_delegations + d.amount;
|
||||
// if let Some(rewards) = d.accumulated_rewards {
|
||||
// total_rewards = total_rewards + rewards;
|
||||
// }
|
||||
// }
|
||||
// log::info!(
|
||||
// "<<< {} delegations, total_delegations = {}, total_rewards = {}",
|
||||
// delegations.len(),
|
||||
// total_delegations,
|
||||
// total_rewards
|
||||
// );
|
||||
// log::trace!("<<< {:?}", delegations);
|
||||
//
|
||||
// Ok(DelegationsSummaryResponse {
|
||||
// delegations,
|
||||
// total_delegations,
|
||||
// total_rewards,
|
||||
// })
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_all_pending_delegation_events(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<Vec<DelegationEvent>, BackendError> {
|
||||
log::info!(">>> Get all pending delegation events");
|
||||
log::info!(">>> Get all pending delegation events");
|
||||
|
||||
// get pending events from mixnet and vesting contract
|
||||
let mut pending_events_for_account = get_pending_delegation_events(state.clone()).await?;
|
||||
let pending_vesting_events = get_pending_vesting_delegation_events(state.clone()).await?;
|
||||
// get pending events from mixnet and vesting contract
|
||||
let mut pending_events_for_account = get_pending_delegation_events(state.clone()).await?;
|
||||
let pending_vesting_events = get_pending_vesting_delegation_events(state.clone()).await?;
|
||||
|
||||
// combine them
|
||||
for event in pending_vesting_events {
|
||||
pending_events_for_account.push(event);
|
||||
}
|
||||
// combine them
|
||||
for event in pending_vesting_events {
|
||||
pending_events_for_account.push(event);
|
||||
}
|
||||
|
||||
Ok(pending_events_for_account)
|
||||
Ok(pending_events_for_account)
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ use tokio::sync::RwLock;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_current_epoch(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<Epoch, BackendError> {
|
||||
log::info!(">>> Get curren epoch");
|
||||
let interval = nymd_client!(state).get_current_epoch().await?;
|
||||
log::info!("<<< curren epoch = {}", interval);
|
||||
Ok(interval.into())
|
||||
log::info!(">>> Get curren epoch");
|
||||
let interval = nymd_client!(state).get_current_epoch().await?;
|
||||
log::info!("<<< curren epoch = {}", interval);
|
||||
Ok(interval.into())
|
||||
}
|
||||
|
||||
@@ -6,41 +6,41 @@ use nym_types::transaction::{SendTxResult, TransactionDetails};
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use validator_client::nymd::{AccountId, CosmosCoin, Fee};
|
||||
use validator_client::nymd::{AccountId, Fee};
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn send(
|
||||
address: &str,
|
||||
amount: MajorCurrencyAmount,
|
||||
memo: String,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
address: &str,
|
||||
amount: MajorCurrencyAmount,
|
||||
memo: String,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<SendTxResult, BackendError> {
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
let address = AccountId::from_str(address)?;
|
||||
let from_address = nymd_client!(state).address().to_string();
|
||||
let cosmos_amount: CosmosCoin = amount.clone().into_minor_cosmos_coin()?;
|
||||
log::info!(
|
||||
">>> Send: amount = {}, minor_amount = {:?}, from = {}, to = {}, fee = {:?}",
|
||||
amount,
|
||||
cosmos_amount,
|
||||
from_address,
|
||||
address.as_ref(),
|
||||
fee,
|
||||
);
|
||||
let raw_res = nymd_client!(state)
|
||||
.send(&address, vec![cosmos_amount], memo, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", raw_res.hash.to_string());
|
||||
let res = SendTxResult::new(
|
||||
raw_res,
|
||||
TransactionDetails {
|
||||
from_address,
|
||||
to_address: address.to_string(),
|
||||
amount,
|
||||
},
|
||||
denom_minor.as_ref(),
|
||||
)?;
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(res)
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
let address = AccountId::from_str(address)?;
|
||||
let from_address = nymd_client!(state).address().to_string();
|
||||
let amount2 = amount.clone().into();
|
||||
log::info!(
|
||||
">>> Send: amount = {}, minor_amount = {:?}, from = {}, to = {}, fee = {:?}",
|
||||
amount,
|
||||
amount2,
|
||||
from_address,
|
||||
address.as_ref(),
|
||||
fee,
|
||||
);
|
||||
let raw_res = nymd_client!(state)
|
||||
.send(&address, vec![amount2], memo, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", raw_res.hash.to_string());
|
||||
let res = SendTxResult::new(
|
||||
raw_res,
|
||||
TransactionDetails {
|
||||
from_address,
|
||||
to_address: address.to_string(),
|
||||
amount,
|
||||
},
|
||||
denom_minor.as_ref(),
|
||||
)?;
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
@@ -11,22 +11,22 @@ use tokio::sync::RwLock;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn simulate_update_contract_settings(
|
||||
params: TauriContractStateParams,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
params: TauriContractStateParams,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
let mixnet_contract_settings_params: ContractStateParams = params.try_into()?;
|
||||
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 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 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()?)
|
||||
let result = client.nymd.simulate(vec![msg]).await?;
|
||||
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
|
||||
}
|
||||
|
||||
@@ -12,26 +12,26 @@ use validator_client::nymd::{AccountId, MsgSend};
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn simulate_send(
|
||||
address: &str,
|
||||
amount: MajorCurrencyAmount,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
address: &str,
|
||||
amount: MajorCurrencyAmount,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
let guard = state.read().await;
|
||||
|
||||
let to_address = AccountId::from_str(address)?;
|
||||
let amount = vec![amount.into_cosmos_coin()?];
|
||||
let to_address = AccountId::from_str(address)?;
|
||||
let amount = vec![amount.into()];
|
||||
|
||||
let client = guard.current_client()?;
|
||||
let from_address = client.nymd.address().clone();
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
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,
|
||||
};
|
||||
// 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()?)
|
||||
let result = client.nymd.simulate(vec![msg]).await?;
|
||||
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
|
||||
}
|
||||
|
||||
@@ -11,166 +11,166 @@ use tokio::sync::RwLock;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn simulate_bond_gateway(
|
||||
gateway: Gateway,
|
||||
pledge: MajorCurrencyAmount,
|
||||
owner_signature: String,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
gateway: Gateway,
|
||||
pledge: MajorCurrencyAmount,
|
||||
owner_signature: String,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
let pledge = pledge.into_cosmos_coin()?;
|
||||
let guard = state.read().await;
|
||||
let pledge = pledge.into();
|
||||
|
||||
let client = guard.current_client()?;
|
||||
let mixnet_contract = client.nymd.mixnet_contract_address()?;
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
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],
|
||||
)?;
|
||||
// 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()?)
|
||||
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<RwLock<State>>>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
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 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 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()?)
|
||||
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: MajorCurrencyAmount,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
mixnode: MixNode,
|
||||
owner_signature: String,
|
||||
pledge: MajorCurrencyAmount,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
let pledge = pledge.into_cosmos_coin()?;
|
||||
let guard = state.read().await;
|
||||
let pledge = pledge.into();
|
||||
|
||||
let client = guard.current_client()?;
|
||||
let mixnet_contract = client.nymd.mixnet_contract_address()?;
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
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 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()?)
|
||||
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<RwLock<State>>>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
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 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 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()?)
|
||||
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<RwLock<State>>>,
|
||||
profit_margin_percent: u8,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
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 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 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()?)
|
||||
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: MajorCurrencyAmount,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
identity: &str,
|
||||
amount: MajorCurrencyAmount,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
let delegation = amount.into_cosmos_coin()?;
|
||||
let guard = state.read().await;
|
||||
let delegation = amount.into();
|
||||
|
||||
let client = guard.current_client()?;
|
||||
let mixnet_contract = client.nymd.mixnet_contract_address()?;
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
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 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()?)
|
||||
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<RwLock<State>>>,
|
||||
identity: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
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 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 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()?)
|
||||
let result = client.nymd.simulate(vec![msg]).await?;
|
||||
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
|
||||
}
|
||||
|
||||
@@ -15,47 +15,45 @@ pub mod vesting;
|
||||
// technically we could have also exposed a result: Option<AbciResult> 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<GasInfo>,
|
||||
pub gas_price: GasPrice,
|
||||
// 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<GasInfo>,
|
||||
pub gas_price: GasPrice,
|
||||
}
|
||||
|
||||
impl SimulateResult {
|
||||
pub fn new(gas_info: Option<GasInfo>, gas_price: GasPrice) -> Self {
|
||||
SimulateResult {
|
||||
gas_info,
|
||||
gas_price,
|
||||
pub fn new(gas_info: Option<GasInfo>, gas_price: GasPrice) -> Self {
|
||||
SimulateResult {
|
||||
gas_info,
|
||||
gas_price,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn detailed_fee(&self) -> Result<FeeDetails, TypesError> {
|
||||
let amount: Option<MajorCurrencyAmount> = match self.to_fee_amount() {
|
||||
Some(a) => Some(a.try_into()?),
|
||||
None => None,
|
||||
};
|
||||
Ok(FeeDetails {
|
||||
amount,
|
||||
fee: self.to_fee(),
|
||||
})
|
||||
}
|
||||
|
||||
fn to_fee_amount(&self) -> Option<CosmosCoin> {
|
||||
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()
|
||||
pub fn detailed_fee(&self) -> Result<FeeDetails, TypesError> {
|
||||
let amount: Option<MajorCurrencyAmount> = match self.to_fee_amount() {
|
||||
Some(a) => Some(a.into()),
|
||||
None => None,
|
||||
};
|
||||
Ok(FeeDetails {
|
||||
amount,
|
||||
fee: self.to_fee(),
|
||||
})
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
fn to_fee_amount(&self) -> Option<CosmosCoin> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,141 +12,141 @@ use vesting_contract_common::ExecuteMsg;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn simulate_vesting_bond_gateway(
|
||||
gateway: Gateway,
|
||||
pledge: MajorCurrencyAmount,
|
||||
owner_signature: String,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
gateway: Gateway,
|
||||
pledge: MajorCurrencyAmount,
|
||||
owner_signature: String,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
let pledge = pledge.into_cosmwasm_coin()?;
|
||||
let guard = state.read().await;
|
||||
let pledge = pledge.into();
|
||||
|
||||
let client = guard.current_client()?;
|
||||
let vesting_contract = client.nymd.vesting_contract_address()?;
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
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 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()?)
|
||||
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<RwLock<State>>>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
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 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 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()?)
|
||||
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: MajorCurrencyAmount,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
mixnode: MixNode,
|
||||
owner_signature: String,
|
||||
pledge: MajorCurrencyAmount,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
let pledge = pledge.into_cosmwasm_coin()?;
|
||||
let guard = state.read().await;
|
||||
let pledge = pledge.into();
|
||||
|
||||
let client = guard.current_client()?;
|
||||
let vesting_contract = client.nymd.vesting_contract_address()?;
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
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 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()?)
|
||||
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<RwLock<State>>>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
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 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 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()?)
|
||||
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<RwLock<State>>>,
|
||||
profit_margin_percent: u8,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
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 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 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()?)
|
||||
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: MajorCurrencyAmount,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
amount: MajorCurrencyAmount,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
let amount = amount.into_cosmwasm_coin()?;
|
||||
let guard = state.read().await;
|
||||
let amount = amount.into();
|
||||
|
||||
let client = guard.current_client()?;
|
||||
let vesting_contract = client.nymd.vesting_contract_address()?;
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
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 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()?)
|
||||
let result = client.nymd.simulate(vec![msg]).await?;
|
||||
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
|
||||
}
|
||||
|
||||
@@ -7,76 +7,66 @@ use crate::state::State;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use validator_client::models::{
|
||||
CoreNodeStatusResponse, InclusionProbabilityResponse, MixnodeStatusResponse,
|
||||
RewardEstimationResponse, StakeSaturationResponse,
|
||||
CoreNodeStatusResponse, InclusionProbabilityResponse, MixnodeStatusResponse,
|
||||
RewardEstimationResponse, StakeSaturationResponse,
|
||||
};
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn mixnode_core_node_status(
|
||||
identity: &str,
|
||||
since: Option<i64>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
identity: &str,
|
||||
since: Option<i64>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<CoreNodeStatusResponse, BackendError> {
|
||||
Ok(
|
||||
api_client!(state)
|
||||
.get_mixnode_core_status_count(identity, since)
|
||||
.await?,
|
||||
)
|
||||
Ok(api_client!(state)
|
||||
.get_mixnode_core_status_count(identity, since)
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn gateway_core_node_status(
|
||||
identity: &str,
|
||||
since: Option<i64>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
identity: &str,
|
||||
since: Option<i64>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<CoreNodeStatusResponse, BackendError> {
|
||||
Ok(
|
||||
api_client!(state)
|
||||
.get_gateway_core_status_count(identity, since)
|
||||
.await?,
|
||||
)
|
||||
Ok(api_client!(state)
|
||||
.get_gateway_core_status_count(identity, since)
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn mixnode_status(
|
||||
identity: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
identity: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<MixnodeStatusResponse, BackendError> {
|
||||
Ok(api_client!(state).get_mixnode_status(identity).await?)
|
||||
Ok(api_client!(state).get_mixnode_status(identity).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn mixnode_reward_estimation(
|
||||
identity: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
identity: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<RewardEstimationResponse, BackendError> {
|
||||
Ok(
|
||||
api_client!(state)
|
||||
.get_mixnode_reward_estimation(identity)
|
||||
.await?,
|
||||
)
|
||||
Ok(api_client!(state)
|
||||
.get_mixnode_reward_estimation(identity)
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn mixnode_stake_saturation(
|
||||
identity: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
identity: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<StakeSaturationResponse, BackendError> {
|
||||
Ok(
|
||||
api_client!(state)
|
||||
.get_mixnode_stake_saturation(identity)
|
||||
.await?,
|
||||
)
|
||||
Ok(api_client!(state)
|
||||
.get_mixnode_stake_saturation(identity)
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn mixnode_inclusion_probability(
|
||||
identity: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
identity: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<InclusionProbabilityResponse, BackendError> {
|
||||
Ok(
|
||||
api_client!(state)
|
||||
.get_mixnode_inclusion_probability(identity)
|
||||
.await?,
|
||||
)
|
||||
Ok(api_client!(state)
|
||||
.get_mixnode_inclusion_probability(identity)
|
||||
.await?)
|
||||
}
|
||||
|
||||
@@ -11,142 +11,142 @@ use validator_client::nymd::{Fee, VestingSigningClient};
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn vesting_bond_gateway(
|
||||
gateway: Gateway,
|
||||
pledge: MajorCurrencyAmount,
|
||||
owner_signature: String,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
gateway: Gateway,
|
||||
pledge: MajorCurrencyAmount,
|
||||
owner_signature: String,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<TransactionExecuteResult, BackendError> {
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
let pledge_minor = pledge.clone().into_minor_cosmwasm_coin()?;
|
||||
log::info!(
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
let pledge_minor = pledge.clone().into();
|
||||
log::info!(
|
||||
">>> Bond gateway with locked tokens: identity_key = {}, pledge = {}, pledge_minor = {}, fee = {:?}",
|
||||
gateway.identity_key,
|
||||
pledge,
|
||||
pledge_minor,
|
||||
fee,
|
||||
);
|
||||
let res = nymd_client!(state)
|
||||
.vesting_bond_gateway(gateway, &owner_signature, pledge_minor, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
let res = nymd_client!(state)
|
||||
.vesting_bond_gateway(gateway, &owner_signature, pledge_minor, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn vesting_unbond_gateway(
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<TransactionExecuteResult, BackendError> {
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
log::info!(
|
||||
">>> Unbond gateway bonded with locked tokens, fee = {:?}",
|
||||
fee
|
||||
);
|
||||
let res = nymd_client!(state).vesting_unbond_gateway(fee).await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
log::info!(
|
||||
">>> Unbond gateway bonded with locked tokens, fee = {:?}",
|
||||
fee
|
||||
);
|
||||
let res = nymd_client!(state).vesting_unbond_gateway(fee).await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn vesting_bond_mixnode(
|
||||
mixnode: MixNode,
|
||||
owner_signature: String,
|
||||
pledge: MajorCurrencyAmount,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
mixnode: MixNode,
|
||||
owner_signature: String,
|
||||
pledge: MajorCurrencyAmount,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<TransactionExecuteResult, BackendError> {
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
let pledge_minor = pledge.clone().into_minor_cosmwasm_coin()?;
|
||||
log::info!(
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
let pledge_minor = pledge.clone().into();
|
||||
log::info!(
|
||||
">>> Bond mixnode with locked tokens: identity_key = {}, pledge = {}, pledge_minor = {}, fee = {:?}",
|
||||
mixnode.identity_key,
|
||||
pledge,
|
||||
pledge_minor,
|
||||
fee
|
||||
);
|
||||
let res = nymd_client!(state)
|
||||
.vesting_bond_mixnode(mixnode, &owner_signature, pledge_minor, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
let res = nymd_client!(state)
|
||||
.vesting_bond_mixnode(mixnode, &owner_signature, pledge_minor, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn vesting_unbond_mixnode(
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<TransactionExecuteResult, BackendError> {
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
log::info!(
|
||||
">>> Unbond mixnode bonded with locked tokens, fee = {:?}",
|
||||
fee
|
||||
);
|
||||
let res = nymd_client!(state).vesting_unbond_mixnode(fee).await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
log::info!(
|
||||
">>> Unbond mixnode bonded with locked tokens, fee = {:?}",
|
||||
fee
|
||||
);
|
||||
let res = nymd_client!(state).vesting_unbond_mixnode(fee).await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn withdraw_vested_coins(
|
||||
amount: MajorCurrencyAmount,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
amount: MajorCurrencyAmount,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<TransactionExecuteResult, BackendError> {
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
let amount_minor = amount.clone().into_minor_cosmwasm_coin()?;
|
||||
log::info!(
|
||||
">>> Withdraw vested liquid coins: amount = {}, amount_minor = {}, fee = {:?}",
|
||||
amount,
|
||||
amount_minor,
|
||||
fee
|
||||
);
|
||||
let res = nymd_client!(state)
|
||||
.withdraw_vested_coins(amount_minor, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
let amount_minor = amount.clone().into();
|
||||
log::info!(
|
||||
">>> Withdraw vested liquid coins: amount = {}, amount_minor = {}, fee = {:?}",
|
||||
amount,
|
||||
amount_minor,
|
||||
fee
|
||||
);
|
||||
let res = nymd_client!(state)
|
||||
.withdraw_vested_coins(amount_minor, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn vesting_update_mixnode(
|
||||
profit_margin_percent: u8,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
profit_margin_percent: u8,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<TransactionExecuteResult, BackendError> {
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
log::info!(
|
||||
">>> Update mixnode bonded with locked tokens: profit_margin_percent = {}, fee = {:?}",
|
||||
profit_margin_percent,
|
||||
fee,
|
||||
);
|
||||
let res = nymd_client!(state)
|
||||
.vesting_update_mixnode_config(profit_margin_percent, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
log::info!(
|
||||
">>> Update mixnode bonded with locked tokens: profit_margin_percent = {}, fee = {:?}",
|
||||
profit_margin_percent,
|
||||
fee,
|
||||
);
|
||||
let res = nymd_client!(state)
|
||||
.vesting_update_mixnode_config(profit_margin_percent, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
}
|
||||
|
||||
@@ -13,76 +13,76 @@ use crate::state::State;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_pending_vesting_delegation_events(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<Vec<DelegationEvent>, BackendError> {
|
||||
log::info!(">>> Get pending delegations from vesting contract");
|
||||
log::info!(">>> Get pending delegations from vesting contract");
|
||||
|
||||
let guard = state.read().await;
|
||||
let client = &guard.current_client()?.nymd;
|
||||
let vesting_contract = client.vesting_contract_address()?;
|
||||
let guard = state.read().await;
|
||||
let client = &guard.current_client()?.nymd;
|
||||
let vesting_contract = client.vesting_contract_address()?;
|
||||
|
||||
let events = client
|
||||
.get_pending_delegation_events(
|
||||
client.address().to_string(),
|
||||
Some(vesting_contract.to_string()),
|
||||
)
|
||||
.await?;
|
||||
let events = client
|
||||
.get_pending_delegation_events(
|
||||
client.address().to_string(),
|
||||
Some(vesting_contract.to_string()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
log::info!("<<< {} events", events.len());
|
||||
log::trace!("<<< {:?}", events);
|
||||
log::info!("<<< {} events", events.len());
|
||||
log::trace!("<<< {:?}", events);
|
||||
|
||||
match from_contract_delegation_events(events) {
|
||||
Ok(res) => Ok(res),
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
match from_contract_delegation_events(events) {
|
||||
Ok(res) => Ok(res),
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn vesting_delegate_to_mixnode(
|
||||
identity: &str,
|
||||
amount: MajorCurrencyAmount,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
identity: &str,
|
||||
amount: MajorCurrencyAmount,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<TransactionExecuteResult, BackendError> {
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
let delegation = amount.clone().into_minor_cosmwasm_coin()?;
|
||||
log::info!(
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
let delegation = amount.clone().into();
|
||||
log::info!(
|
||||
">>> Delegate to mixnode with locked tokens: identity_key = {}, amount = {}, minor_amount = {}, fee = {:?}",
|
||||
identity,
|
||||
amount,
|
||||
delegation,
|
||||
fee
|
||||
);
|
||||
let res = nymd_client!(state)
|
||||
.vesting_delegate_to_mixnode(identity, &delegation, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
let res = nymd_client!(state)
|
||||
.vesting_delegate_to_mixnode(identity, delegation, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn vesting_undelegate_from_mixnode(
|
||||
identity: &str,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
identity: &str,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<TransactionExecuteResult, BackendError> {
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
log::info!(
|
||||
">>> Undelegate from mixnode delegated with locked tokens: identity_key = {}, fee = {:?}",
|
||||
identity,
|
||||
fee,
|
||||
);
|
||||
let res = nymd_client!(state)
|
||||
.vesting_undelegate_from_mixnode(identity, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
let denom_minor = state.read().await.current_network().denom();
|
||||
log::info!(
|
||||
">>> Undelegate from mixnode delegated with locked tokens: identity_key = {}, fee = {:?}",
|
||||
identity,
|
||||
fee,
|
||||
);
|
||||
let res = nymd_client!(state)
|
||||
.vesting_undelegate_from_mixnode(identity, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
Ok(TransactionExecuteResult::from_execute_result(
|
||||
res,
|
||||
denom_minor.as_ref(),
|
||||
)?)
|
||||
}
|
||||
|
||||
@@ -15,201 +15,201 @@ use crate::state::State;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn locked_coins(
|
||||
block_time: Option<u64>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
block_time: Option<u64>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<MajorCurrencyAmount, BackendError> {
|
||||
log::info!(">>> Query locked coins");
|
||||
let res = nymd_client!(state)
|
||||
.locked_coins(
|
||||
nymd_client!(state).address().as_ref(),
|
||||
block_time.map(Timestamp::from_seconds),
|
||||
)
|
||||
.await?
|
||||
.try_into()?;
|
||||
log::info!("<<< locked coins = {}", res);
|
||||
Ok(res)
|
||||
log::info!(">>> Query locked coins");
|
||||
let res = nymd_client!(state)
|
||||
.locked_coins(
|
||||
nymd_client!(state).address().as_ref(),
|
||||
block_time.map(Timestamp::from_seconds),
|
||||
)
|
||||
.await?
|
||||
.into();
|
||||
log::info!("<<< locked coins = {}", res);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn spendable_coins(
|
||||
block_time: Option<u64>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
block_time: Option<u64>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<MajorCurrencyAmount, BackendError> {
|
||||
log::info!(">>> Query spendable coins");
|
||||
let res = nymd_client!(state)
|
||||
.spendable_coins(
|
||||
nymd_client!(state).address().as_ref(),
|
||||
block_time.map(Timestamp::from_seconds),
|
||||
)
|
||||
.await?
|
||||
.try_into()?;
|
||||
log::info!("<<< spendable coins = {}", res);
|
||||
Ok(res)
|
||||
log::info!(">>> Query spendable coins");
|
||||
let res = nymd_client!(state)
|
||||
.spendable_coins(
|
||||
nymd_client!(state).address().as_ref(),
|
||||
block_time.map(Timestamp::from_seconds),
|
||||
)
|
||||
.await?
|
||||
.into();
|
||||
log::info!("<<< spendable coins = {}", res);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn vested_coins(
|
||||
vesting_account_address: &str,
|
||||
block_time: Option<u64>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
vesting_account_address: &str,
|
||||
block_time: Option<u64>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<MajorCurrencyAmount, BackendError> {
|
||||
log::info!(">>> Query vested coins");
|
||||
let res = nymd_client!(state)
|
||||
.vested_coins(
|
||||
vesting_account_address,
|
||||
block_time.map(Timestamp::from_seconds),
|
||||
)
|
||||
.await?
|
||||
.try_into()?;
|
||||
log::info!("<<< vested coins = {}", res);
|
||||
Ok(res)
|
||||
log::info!(">>> Query vested coins");
|
||||
let res = nymd_client!(state)
|
||||
.vested_coins(
|
||||
vesting_account_address,
|
||||
block_time.map(Timestamp::from_seconds),
|
||||
)
|
||||
.await?
|
||||
.into();
|
||||
log::info!("<<< vested coins = {}", res);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn vesting_coins(
|
||||
vesting_account_address: &str,
|
||||
block_time: Option<u64>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
vesting_account_address: &str,
|
||||
block_time: Option<u64>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<MajorCurrencyAmount, BackendError> {
|
||||
log::info!(">>> Query vesting coins");
|
||||
let res = nymd_client!(state)
|
||||
.vesting_coins(
|
||||
vesting_account_address,
|
||||
block_time.map(Timestamp::from_seconds),
|
||||
)
|
||||
.await?
|
||||
.try_into()?;
|
||||
log::info!("<<< vesting coins = {}", res);
|
||||
Ok(res)
|
||||
log::info!(">>> Query vesting coins");
|
||||
let res = nymd_client!(state)
|
||||
.vesting_coins(
|
||||
vesting_account_address,
|
||||
block_time.map(Timestamp::from_seconds),
|
||||
)
|
||||
.await?
|
||||
.into();
|
||||
log::info!("<<< vesting coins = {}", res);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn vesting_start_time(
|
||||
vesting_account_address: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
vesting_account_address: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<u64, BackendError> {
|
||||
log::info!(">>> Query vesting start time");
|
||||
let res = nymd_client!(state)
|
||||
.vesting_start_time(vesting_account_address)
|
||||
.await?
|
||||
.seconds();
|
||||
log::info!("<<< vesting start time = {}", res);
|
||||
Ok(res)
|
||||
log::info!(">>> Query vesting start time");
|
||||
let res = nymd_client!(state)
|
||||
.vesting_start_time(vesting_account_address)
|
||||
.await?
|
||||
.seconds();
|
||||
log::info!("<<< vesting start time = {}", res);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn vesting_end_time(
|
||||
vesting_account_address: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
vesting_account_address: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<u64, BackendError> {
|
||||
log::info!(">>> Query vesting end time");
|
||||
let res = nymd_client!(state)
|
||||
.vesting_end_time(vesting_account_address)
|
||||
.await?
|
||||
.seconds();
|
||||
log::info!("<<< vesting end time = {}", res);
|
||||
Ok(res)
|
||||
log::info!(">>> Query vesting end time");
|
||||
let res = nymd_client!(state)
|
||||
.vesting_end_time(vesting_account_address)
|
||||
.await?
|
||||
.seconds();
|
||||
log::info!("<<< vesting end time = {}", res);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn original_vesting(
|
||||
vesting_account_address: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
vesting_account_address: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<OriginalVestingResponse, BackendError> {
|
||||
log::info!(">>> Query original vesting");
|
||||
let res = nymd_client!(state)
|
||||
.original_vesting(vesting_account_address)
|
||||
.await?
|
||||
.try_into()?;
|
||||
log::info!("<<< {:?}", res);
|
||||
Ok(res)
|
||||
log::info!(">>> Query original vesting");
|
||||
let res = nymd_client!(state)
|
||||
.original_vesting(vesting_account_address)
|
||||
.await?
|
||||
.try_into()?;
|
||||
log::info!("<<< {:?}", res);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn delegated_free(
|
||||
vesting_account_address: &str,
|
||||
block_time: Option<u64>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
vesting_account_address: &str,
|
||||
block_time: Option<u64>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<MajorCurrencyAmount, BackendError> {
|
||||
log::info!(">>> Query delegated free");
|
||||
let res = nymd_client!(state)
|
||||
.delegated_free(
|
||||
vesting_account_address,
|
||||
block_time.map(Timestamp::from_seconds),
|
||||
)
|
||||
.await?
|
||||
.try_into()?;
|
||||
log::info!("<<< delegated free = {}", res);
|
||||
Ok(res)
|
||||
log::info!(">>> Query delegated free");
|
||||
let res = nymd_client!(state)
|
||||
.delegated_free(
|
||||
vesting_account_address,
|
||||
block_time.map(Timestamp::from_seconds),
|
||||
)
|
||||
.await?
|
||||
.into();
|
||||
log::info!("<<< delegated free = {}", res);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Returns the total amount of delegated tokens that have vested
|
||||
#[tauri::command]
|
||||
pub async fn delegated_vesting(
|
||||
block_time: Option<u64>,
|
||||
vesting_account_address: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
block_time: Option<u64>,
|
||||
vesting_account_address: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<MajorCurrencyAmount, BackendError> {
|
||||
log::info!(">>> Query delegated vesting");
|
||||
let res = nymd_client!(state)
|
||||
.delegated_vesting(
|
||||
vesting_account_address,
|
||||
block_time.map(Timestamp::from_seconds),
|
||||
)
|
||||
.await?
|
||||
.try_into()?;
|
||||
log::info!("<<< delegated_vesting = {}", res);
|
||||
Ok(res)
|
||||
log::info!(">>> Query delegated vesting");
|
||||
let res = nymd_client!(state)
|
||||
.delegated_vesting(
|
||||
vesting_account_address,
|
||||
block_time.map(Timestamp::from_seconds),
|
||||
)
|
||||
.await?
|
||||
.into();
|
||||
log::info!("<<< delegated_vesting = {}", res);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn vesting_get_mixnode_pledge(
|
||||
address: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
address: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<Option<PledgeData>, BackendError> {
|
||||
log::info!(">>> Query vesting get mixnode pledge");
|
||||
let res = nymd_client!(state)
|
||||
.get_mixnode_pledge(address)
|
||||
.await?
|
||||
.and_then(PledgeData::and_then);
|
||||
log::info!("<<< {:?}", res);
|
||||
Ok(res)
|
||||
log::info!(">>> Query vesting get mixnode pledge");
|
||||
let res = nymd_client!(state)
|
||||
.get_mixnode_pledge(address)
|
||||
.await?
|
||||
.and_then(PledgeData::and_then);
|
||||
log::info!("<<< {:?}", res);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn vesting_get_gateway_pledge(
|
||||
address: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
address: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<Option<PledgeData>, BackendError> {
|
||||
log::info!(">>> Query vesting get gateway pledge");
|
||||
let res = nymd_client!(state)
|
||||
.get_gateway_pledge(address)
|
||||
.await?
|
||||
.and_then(PledgeData::and_then);
|
||||
log::info!("<<< {:?}", res);
|
||||
Ok(res)
|
||||
log::info!(">>> Query vesting get gateway pledge");
|
||||
let res = nymd_client!(state)
|
||||
.get_gateway_pledge(address)
|
||||
.await?
|
||||
.and_then(PledgeData::and_then);
|
||||
log::info!("<<< {:?}", res);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_current_vesting_period(
|
||||
address: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
address: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<Period, BackendError> {
|
||||
log::info!(">>> Query current vesting period");
|
||||
let res = nymd_client!(state)
|
||||
.get_current_vesting_period(address)
|
||||
.await?;
|
||||
log::info!("<<< {:?}", res);
|
||||
Ok(res)
|
||||
log::info!(">>> Query current vesting period");
|
||||
let res = nymd_client!(state)
|
||||
.get_current_vesting_period(address)
|
||||
.await?;
|
||||
log::info!("<<< {:?}", res);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_account_info(
|
||||
address: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
address: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<VestingAccountInfo, BackendError> {
|
||||
log::info!(">>> Query account info");
|
||||
let res = nymd_client!(state).get_account(address).await?.try_into()?;
|
||||
log::info!("<<< {:?}", res);
|
||||
Ok(res)
|
||||
log::info!(">>> Query account info");
|
||||
let res = nymd_client!(state).get_account(address).await?.try_into()?;
|
||||
log::info!("<<< {:?}", res);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
+348
-358
@@ -18,450 +18,440 @@ use std::time::Duration;
|
||||
|
||||
// Some hardcoded metadata overrides
|
||||
static METADATA_OVERRIDES: Lazy<Vec<(Url, ValidatorMetadata)>> = Lazy::new(|| {
|
||||
vec![(
|
||||
"https://rpc.nyx.nodes.guru/".parse().unwrap(),
|
||||
ValidatorMetadata {
|
||||
name: Some("Nodes.Guru".to_string()),
|
||||
},
|
||||
)]
|
||||
vec![(
|
||||
"https://rpc.nyx.nodes.guru/".parse().unwrap(),
|
||||
ValidatorMetadata {
|
||||
name: Some("Nodes.Guru".to_string()),
|
||||
},
|
||||
)]
|
||||
});
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn load_config_from_files(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
state.write().await.load_config_files();
|
||||
Ok(())
|
||||
state.write().await.load_config_files();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn save_config_to_files(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
state.read().await.save_config_files()
|
||||
state.read().await.save_config_files()
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct State {
|
||||
config: config::Config,
|
||||
signing_clients: HashMap<Network, Client<SigningNymdClient>>,
|
||||
current_network: Network,
|
||||
config: config::Config,
|
||||
signing_clients: HashMap<Network, Client<SigningNymdClient>>,
|
||||
current_network: Network,
|
||||
|
||||
// All the accounts the we get from decrypting the wallet. We hold on to these for being able to
|
||||
// switch accounts on-the-fly
|
||||
all_accounts: Vec<WalletAccountIds>,
|
||||
// All the accounts the we get from decrypting the wallet. We hold on to these for being able to
|
||||
// switch accounts on-the-fly
|
||||
all_accounts: Vec<WalletAccountIds>,
|
||||
|
||||
/// Validators that have been fetched dynamically, probably during startup.
|
||||
fetched_validators: config::OptionalValidators,
|
||||
/// Validators that have been fetched dynamically, probably during startup.
|
||||
fetched_validators: config::OptionalValidators,
|
||||
|
||||
/// We fetch (and cache) some metadata, such as names, when available
|
||||
validator_metadata: HashMap<Url, ValidatorMetadata>,
|
||||
/// We fetch (and cache) some metadata, such as names, when available
|
||||
validator_metadata: HashMap<Url, ValidatorMetadata>,
|
||||
}
|
||||
|
||||
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<Network, CosmosAccountId>,
|
||||
// The wallet account id
|
||||
pub id: crate::wallet_storage::AccountId,
|
||||
// The set of corresponding network identities derived from the mnemonic
|
||||
pub addresses: HashMap<Network, CosmosAccountId>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn client(&self, network: Network) -> Result<&Client<SigningNymdClient>, BackendError> {
|
||||
self
|
||||
.signing_clients
|
||||
.get(&network)
|
||||
.ok_or(BackendError::ClientNotInitialized)
|
||||
}
|
||||
pub fn client(&self, network: Network) -> Result<&Client<SigningNymdClient>, BackendError> {
|
||||
self.signing_clients
|
||||
.get(&network)
|
||||
.ok_or(BackendError::ClientNotInitialized)
|
||||
}
|
||||
|
||||
pub fn client_mut(
|
||||
&mut self,
|
||||
network: Network,
|
||||
) -> Result<&mut Client<SigningNymdClient>, BackendError> {
|
||||
self
|
||||
.signing_clients
|
||||
.get_mut(&network)
|
||||
.ok_or(BackendError::ClientNotInitialized)
|
||||
}
|
||||
pub fn client_mut(
|
||||
&mut self,
|
||||
network: Network,
|
||||
) -> Result<&mut Client<SigningNymdClient>, BackendError> {
|
||||
self.signing_clients
|
||||
.get_mut(&network)
|
||||
.ok_or(BackendError::ClientNotInitialized)
|
||||
}
|
||||
|
||||
pub fn current_client(&self) -> Result<&Client<SigningNymdClient>, BackendError> {
|
||||
self
|
||||
.signing_clients
|
||||
.get(&self.current_network)
|
||||
.ok_or(BackendError::ClientNotInitialized)
|
||||
}
|
||||
pub fn current_client(&self) -> Result<&Client<SigningNymdClient>, BackendError> {
|
||||
self.signing_clients
|
||||
.get(&self.current_network)
|
||||
.ok_or(BackendError::ClientNotInitialized)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn current_client_mut(&mut self) -> Result<&mut Client<SigningNymdClient>, BackendError> {
|
||||
self
|
||||
.signing_clients
|
||||
.get_mut(&self.current_network)
|
||||
.ok_or(BackendError::ClientNotInitialized)
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub fn current_client_mut(&mut self) -> Result<&mut Client<SigningNymdClient>, BackendError> {
|
||||
self.signing_clients
|
||||
.get_mut(&self.current_network)
|
||||
.ok_or(BackendError::ClientNotInitialized)
|
||||
}
|
||||
|
||||
pub fn config(&self) -> &config::Config {
|
||||
&self.config
|
||||
}
|
||||
pub fn config(&self) -> &config::Config {
|
||||
&self.config
|
||||
}
|
||||
|
||||
/// Load configuration from files. If unsuccessful we just log it and move on.
|
||||
pub fn load_config_files(&mut self) {
|
||||
self.config = config::Config::load_from_files();
|
||||
}
|
||||
/// Load configuration from files. If unsuccessful we just log it and move on.
|
||||
pub fn load_config_files(&mut self) {
|
||||
self.config = config::Config::load_from_files();
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn save_config_files(&self) -> Result<(), BackendError> {
|
||||
Ok(self.config.save_to_files()?)
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub fn save_config_files(&self) -> Result<(), BackendError> {
|
||||
Ok(self.config.save_to_files()?)
|
||||
}
|
||||
|
||||
pub fn add_client(&mut self, network: Network, client: Client<SigningNymdClient>) {
|
||||
self.signing_clients.insert(network, client);
|
||||
}
|
||||
pub fn add_client(&mut self, network: Network, client: Client<SigningNymdClient>) {
|
||||
self.signing_clients.insert(network, client);
|
||||
}
|
||||
|
||||
pub fn set_network(&mut self, network: Network) {
|
||||
self.current_network = network;
|
||||
}
|
||||
pub fn set_network(&mut self, network: Network) {
|
||||
self.current_network = network;
|
||||
}
|
||||
|
||||
pub fn current_network(&self) -> Network {
|
||||
self.current_network
|
||||
}
|
||||
pub fn current_network(&self) -> Network {
|
||||
self.current_network
|
||||
}
|
||||
|
||||
pub(crate) fn set_all_accounts(&mut self, all_accounts: Vec<WalletAccountIds>) {
|
||||
self.all_accounts = all_accounts
|
||||
}
|
||||
pub(crate) fn set_all_accounts(&mut self, all_accounts: Vec<WalletAccountIds>) {
|
||||
self.all_accounts = all_accounts
|
||||
}
|
||||
|
||||
pub(crate) fn get_all_accounts(&self) -> impl Iterator<Item = &WalletAccountIds> {
|
||||
self.all_accounts.iter()
|
||||
}
|
||||
pub(crate) fn get_all_accounts(&self) -> impl Iterator<Item = &WalletAccountIds> {
|
||||
self.all_accounts.iter()
|
||||
}
|
||||
|
||||
pub fn logout(&mut self) {
|
||||
self.signing_clients = HashMap::new();
|
||||
}
|
||||
pub fn logout(&mut self) {
|
||||
self.signing_clients = HashMap::new();
|
||||
}
|
||||
|
||||
/// Get the available validators in the order
|
||||
/// 1. from the configuration file
|
||||
/// 2. provided remotely
|
||||
/// 3. hardcoded fallback
|
||||
/// The format is the config backend format, which is flat due to serialization preference.
|
||||
pub fn get_config_validator_entries(
|
||||
&self,
|
||||
network: Network,
|
||||
) -> impl Iterator<Item = config::ValidatorConfigEntry> + '_ {
|
||||
let validators_in_config = self.config.get_configured_validators(network);
|
||||
let fetched_validators = self.fetched_validators.validators(network).cloned();
|
||||
let default_validators = self.config.get_base_validators(network);
|
||||
/// Get the available validators in the order
|
||||
/// 1. from the configuration file
|
||||
/// 2. provided remotely
|
||||
/// 3. hardcoded fallback
|
||||
/// The format is the config backend format, which is flat due to serialization preference.
|
||||
pub fn get_config_validator_entries(
|
||||
&self,
|
||||
network: Network,
|
||||
) -> impl Iterator<Item = config::ValidatorConfigEntry> + '_ {
|
||||
let validators_in_config = self.config.get_configured_validators(network);
|
||||
let fetched_validators = self.fetched_validators.validators(network).cloned();
|
||||
let default_validators = self.config.get_base_validators(network);
|
||||
|
||||
// All the validators, in decending list of priority
|
||||
let validators = validators_in_config
|
||||
.chain(fetched_validators)
|
||||
.chain(default_validators)
|
||||
.unique_by(|v| (v.nymd_url.clone(), v.api_url.clone()));
|
||||
// All the validators, in decending list of priority
|
||||
let validators = validators_in_config
|
||||
.chain(fetched_validators)
|
||||
.chain(default_validators)
|
||||
.unique_by(|v| (v.nymd_url.clone(), v.api_url.clone()));
|
||||
|
||||
// Annotate with dynamic metadata
|
||||
validators.map(|v| {
|
||||
let metadata = self.validator_metadata.get(&v.nymd_url);
|
||||
let name = v
|
||||
.nymd_name
|
||||
.or_else(|| metadata.and_then(|m| m.name.clone()));
|
||||
config::ValidatorConfigEntry {
|
||||
nymd_url: v.nymd_url,
|
||||
nymd_name: name,
|
||||
api_url: v.api_url,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_nymd_urls_only(&self, network: Network) -> impl Iterator<Item = Url> + '_ {
|
||||
self
|
||||
.get_config_validator_entries(network)
|
||||
.into_iter()
|
||||
.map(|v| v.nymd_url)
|
||||
}
|
||||
|
||||
pub fn get_api_urls_only(&self, network: Network) -> impl Iterator<Item = Url> + '_ {
|
||||
self
|
||||
.get_config_validator_entries(network)
|
||||
.into_iter()
|
||||
.filter_map(|v| v.api_url)
|
||||
}
|
||||
|
||||
/// Get the list of validator nymd urls in the network config format, suitable for passing on to
|
||||
/// the UI
|
||||
pub fn get_nymd_urls(
|
||||
&self,
|
||||
network: Network,
|
||||
) -> impl Iterator<Item = network_config::ValidatorUrl> + '_ {
|
||||
self
|
||||
.get_config_validator_entries(network)
|
||||
.into_iter()
|
||||
.map(|v| network_config::ValidatorUrl {
|
||||
url: v.nymd_url.to_string(),
|
||||
name: v.nymd_name,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the list of validator-api urls in the network config format, suitable for passing on to
|
||||
/// the UI
|
||||
pub fn get_api_urls(
|
||||
&self,
|
||||
network: Network,
|
||||
) -> impl Iterator<Item = network_config::ValidatorUrl> + '_ {
|
||||
self
|
||||
.get_config_validator_entries(network)
|
||||
.into_iter()
|
||||
.filter_map(|v| {
|
||||
v.api_url.map(|u| network_config::ValidatorUrl {
|
||||
url: u.to_string(),
|
||||
name: None,
|
||||
// Annotate with dynamic metadata
|
||||
validators.map(|v| {
|
||||
let metadata = self.validator_metadata.get(&v.nymd_url);
|
||||
let name = v
|
||||
.nymd_name
|
||||
.or_else(|| metadata.and_then(|m| m.name.clone()));
|
||||
config::ValidatorConfigEntry {
|
||||
nymd_url: v.nymd_url,
|
||||
nymd_name: name,
|
||||
api_url: v.api_url,
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_all_nymd_urls(&self) -> HashMap<Network, Vec<Url>> {
|
||||
Network::iter()
|
||||
.flat_map(|network| {
|
||||
self
|
||||
.get_nymd_urls_only(network)
|
||||
.map(move |url| (network, url))
|
||||
})
|
||||
.into_group_map()
|
||||
}
|
||||
pub fn get_nymd_urls_only(&self, network: Network) -> impl Iterator<Item = Url> + '_ {
|
||||
self.get_config_validator_entries(network)
|
||||
.into_iter()
|
||||
.map(|v| v.nymd_url)
|
||||
}
|
||||
|
||||
pub fn get_all_api_urls(&self) -> HashMap<Network, Vec<Url>> {
|
||||
Network::iter()
|
||||
.flat_map(|network| {
|
||||
self
|
||||
.get_api_urls_only(network)
|
||||
.map(move |url| (network, url))
|
||||
})
|
||||
.into_group_map()
|
||||
}
|
||||
pub fn get_api_urls_only(&self, network: Network) -> impl Iterator<Item = Url> + '_ {
|
||||
self.get_config_validator_entries(network)
|
||||
.into_iter()
|
||||
.filter_map(|v| v.api_url)
|
||||
}
|
||||
|
||||
/// Fetch validator urls remotely. These are used to in addition to the base ones, and the user
|
||||
/// configured ones.
|
||||
pub async fn fetch_updated_validator_urls(&mut self) -> Result<(), BackendError> {
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(Duration::from_secs(3))
|
||||
.build()?;
|
||||
log::debug!(
|
||||
"Fetching validator urls from: {}",
|
||||
crate::config::REMOTE_SOURCE_OF_VALIDATOR_URLS
|
||||
);
|
||||
let response = client
|
||||
.get(crate::config::REMOTE_SOURCE_OF_VALIDATOR_URLS.to_string())
|
||||
.send()
|
||||
.await?;
|
||||
/// Get the list of validator nymd urls in the network config format, suitable for passing on to
|
||||
/// the UI
|
||||
pub fn get_nymd_urls(
|
||||
&self,
|
||||
network: Network,
|
||||
) -> impl Iterator<Item = network_config::ValidatorUrl> + '_ {
|
||||
self.get_config_validator_entries(network)
|
||||
.into_iter()
|
||||
.map(|v| network_config::ValidatorUrl {
|
||||
url: v.nymd_url.to_string(),
|
||||
name: v.nymd_name,
|
||||
})
|
||||
}
|
||||
|
||||
self.fetched_validators = serde_json::from_str(&response.text().await?)?;
|
||||
log::debug!("Received validator urls: \n{}", self.fetched_validators);
|
||||
/// Get the list of validator-api urls in the network config format, suitable for passing on to
|
||||
/// the UI
|
||||
pub fn get_api_urls(
|
||||
&self,
|
||||
network: Network,
|
||||
) -> impl Iterator<Item = network_config::ValidatorUrl> + '_ {
|
||||
self.get_config_validator_entries(network)
|
||||
.into_iter()
|
||||
.filter_map(|v| {
|
||||
v.api_url.map(|u| network_config::ValidatorUrl {
|
||||
url: u.to_string(),
|
||||
name: None,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
self.refresh_validator_status().await?;
|
||||
pub fn get_all_nymd_urls(&self) -> HashMap<Network, Vec<Url>> {
|
||||
Network::iter()
|
||||
.flat_map(|network| {
|
||||
self.get_nymd_urls_only(network)
|
||||
.map(move |url| (network, url))
|
||||
})
|
||||
.into_group_map()
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub fn get_all_api_urls(&self) -> HashMap<Network, Vec<Url>> {
|
||||
Network::iter()
|
||||
.flat_map(|network| {
|
||||
self.get_api_urls_only(network)
|
||||
.map(move |url| (network, url))
|
||||
})
|
||||
.into_group_map()
|
||||
}
|
||||
|
||||
pub async fn refresh_validator_status(&mut self) -> Result<(), BackendError> {
|
||||
log::debug!("Refreshing validator status");
|
||||
|
||||
// All urls for all networks
|
||||
let nymd_urls = self
|
||||
.get_all_nymd_urls()
|
||||
.into_iter()
|
||||
.flat_map(|(_, urls)| urls.into_iter());
|
||||
|
||||
// Fetch status for all urls
|
||||
let responses = fetch_status_for_urls(nymd_urls).await?;
|
||||
|
||||
// Update the stored metadata
|
||||
self.apply_responses(responses)?;
|
||||
|
||||
// Override some overrides for usability
|
||||
self.apply_metadata_override(METADATA_OVERRIDES.to_vec());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn apply_responses(
|
||||
&mut self,
|
||||
responses: Vec<Result<(Url, String), reqwest::Error>>,
|
||||
) -> Result<(), BackendError> {
|
||||
for response in responses.into_iter().flatten() {
|
||||
let json: serde_json::Value = serde_json::from_str(&response.1)?;
|
||||
let moniker = &json["result"]["node_info"]["moniker"];
|
||||
log::debug!("Fetched moniker for: {}: {}", response.0, moniker);
|
||||
|
||||
// Insert into metadata map
|
||||
if let Some(ref mut m) = self.validator_metadata.get_mut(&response.0) {
|
||||
m.name = Some(moniker.to_string());
|
||||
} else {
|
||||
self.validator_metadata.insert(
|
||||
response.0,
|
||||
ValidatorMetadata {
|
||||
name: Some(moniker.to_string()),
|
||||
},
|
||||
/// Fetch validator urls remotely. These are used to in addition to the base ones, and the user
|
||||
/// configured ones.
|
||||
pub async fn fetch_updated_validator_urls(&mut self) -> Result<(), BackendError> {
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(Duration::from_secs(3))
|
||||
.build()?;
|
||||
log::debug!(
|
||||
"Fetching validator urls from: {}",
|
||||
crate::config::REMOTE_SOURCE_OF_VALIDATOR_URLS
|
||||
);
|
||||
}
|
||||
let response = client
|
||||
.get(crate::config::REMOTE_SOURCE_OF_VALIDATOR_URLS.to_string())
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
self.fetched_validators = serde_json::from_str(&response.text().await?)?;
|
||||
log::debug!("Received validator urls: \n{}", self.fetched_validators);
|
||||
|
||||
self.refresh_validator_status().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn apply_metadata_override(&mut self, metadata_overrides: Vec<(Url, ValidatorMetadata)>) {
|
||||
for (url, metadata) in metadata_overrides {
|
||||
log::debug!("Overriding (some) metadata for: {url}");
|
||||
if let Some(m) = self.validator_metadata.get_mut(&url) {
|
||||
m.name = metadata.name;
|
||||
} else {
|
||||
self.validator_metadata.insert(url, metadata);
|
||||
}
|
||||
pub async fn refresh_validator_status(&mut self) -> Result<(), BackendError> {
|
||||
log::debug!("Refreshing validator status");
|
||||
|
||||
// All urls for all networks
|
||||
let nymd_urls = self
|
||||
.get_all_nymd_urls()
|
||||
.into_iter()
|
||||
.flat_map(|(_, urls)| urls.into_iter());
|
||||
|
||||
// Fetch status for all urls
|
||||
let responses = fetch_status_for_urls(nymd_urls).await?;
|
||||
|
||||
// Update the stored metadata
|
||||
self.apply_responses(responses)?;
|
||||
|
||||
// Override some overrides for usability
|
||||
self.apply_metadata_override(METADATA_OVERRIDES.to_vec());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select_validator_nymd_url(
|
||||
&mut self,
|
||||
url: &str,
|
||||
network: Network,
|
||||
) -> Result<(), BackendError> {
|
||||
self.config.select_validator_nymd_url(url.parse()?, network);
|
||||
if let Ok(client) = self.client_mut(network) {
|
||||
client.change_nymd(url.parse()?)?;
|
||||
fn apply_responses(
|
||||
&mut self,
|
||||
responses: Vec<Result<(Url, String), reqwest::Error>>,
|
||||
) -> Result<(), BackendError> {
|
||||
for response in responses.into_iter().flatten() {
|
||||
let json: serde_json::Value = serde_json::from_str(&response.1)?;
|
||||
let moniker = &json["result"]["node_info"]["moniker"];
|
||||
log::debug!("Fetched moniker for: {}: {}", response.0, moniker);
|
||||
|
||||
// Insert into metadata map
|
||||
if let Some(ref mut m) = self.validator_metadata.get_mut(&response.0) {
|
||||
m.name = Some(moniker.to_string());
|
||||
} else {
|
||||
self.validator_metadata.insert(
|
||||
response.0,
|
||||
ValidatorMetadata {
|
||||
name: Some(moniker.to_string()),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn select_validator_api_url(
|
||||
&mut self,
|
||||
url: &str,
|
||||
network: Network,
|
||||
) -> Result<(), BackendError> {
|
||||
self.config.select_validator_api_url(url.parse()?, network);
|
||||
if let Ok(client) = self.client_mut(network) {
|
||||
client.change_validator_api(url.parse()?);
|
||||
fn apply_metadata_override(&mut self, metadata_overrides: Vec<(Url, ValidatorMetadata)>) {
|
||||
for (url, metadata) in metadata_overrides {
|
||||
log::debug!("Overriding (some) metadata for: {url}");
|
||||
if let Some(m) = self.validator_metadata.get_mut(&url) {
|
||||
m.name = metadata.name;
|
||||
} else {
|
||||
self.validator_metadata.insert(url, metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_validator_url(&mut self, url: config::ValidatorConfigEntry, network: Network) {
|
||||
self.config.add_validator_url(url, network);
|
||||
}
|
||||
pub fn select_validator_nymd_url(
|
||||
&mut self,
|
||||
url: &str,
|
||||
network: Network,
|
||||
) -> Result<(), BackendError> {
|
||||
self.config.select_validator_nymd_url(url.parse()?, network);
|
||||
if let Ok(client) = self.client_mut(network) {
|
||||
client.change_nymd(url.parse()?)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_validator_url(&mut self, url: config::ValidatorConfigEntry, network: Network) {
|
||||
self.config.remove_validator_url(url, network)
|
||||
}
|
||||
pub fn select_validator_api_url(
|
||||
&mut self,
|
||||
url: &str,
|
||||
network: Network,
|
||||
) -> Result<(), BackendError> {
|
||||
self.config.select_validator_api_url(url.parse()?, network);
|
||||
if let Ok(client) = self.client_mut(network) {
|
||||
client.change_validator_api(url.parse()?);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_validator_url(&mut self, url: config::ValidatorConfigEntry, network: Network) {
|
||||
self.config.add_validator_url(url, network);
|
||||
}
|
||||
|
||||
pub fn remove_validator_url(&mut self, url: config::ValidatorConfigEntry, network: Network) {
|
||||
self.config.remove_validator_url(url, network)
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_status_for_urls(
|
||||
nymd_urls: impl Iterator<Item = Url>,
|
||||
nymd_urls: impl Iterator<Item = Url>,
|
||||
) -> Result<Vec<Result<(Url, String), reqwest::Error>>, BackendError> {
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(Duration::from_secs(3))
|
||||
.build()?;
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(Duration::from_secs(3))
|
||||
.build()?;
|
||||
|
||||
let responses = futures::future::join_all(nymd_urls.into_iter().map(|url| {
|
||||
let client = &client;
|
||||
let status_url = url.join("status").unwrap_or_else(|_| url.clone());
|
||||
async move {
|
||||
let resp = client.get(status_url).send().await?;
|
||||
resp.text().await.map(|text| (url, text))
|
||||
}
|
||||
}))
|
||||
.await;
|
||||
let responses = futures::future::join_all(nymd_urls.into_iter().map(|url| {
|
||||
let client = &client;
|
||||
let status_url = url.join("status").unwrap_or_else(|_| url.clone());
|
||||
async move {
|
||||
let resp = client.get(status_url).send().await?;
|
||||
resp.text().await.map(|text| (url, text))
|
||||
}
|
||||
}))
|
||||
.await;
|
||||
|
||||
Ok(responses)
|
||||
Ok(responses)
|
||||
}
|
||||
|
||||
// Validator metadata that can by dynamically populated
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ValidatorMetadata {
|
||||
pub name: Option<String>,
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! client {
|
||||
($state:ident) => {
|
||||
$state.read().await.current_client()?
|
||||
};
|
||||
($state:ident) => {
|
||||
$state.read().await.current_client()?
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! nymd_client {
|
||||
($state:ident) => {
|
||||
$state.read().await.current_client()?.nymd
|
||||
};
|
||||
($state:ident) => {
|
||||
$state.read().await.current_client()?.nymd
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! api_client {
|
||||
($state:ident) => {
|
||||
$state.read().await.current_client()?.validator_api
|
||||
};
|
||||
($state:ident) => {
|
||||
$state.read().await.current_client()?.validator_api
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn adding_validators_urls_prepends() {
|
||||
let mut state = State::default();
|
||||
let _api_urls = state.get_api_urls(Network::MAINNET).collect::<Vec<_>>();
|
||||
#[test]
|
||||
fn adding_validators_urls_prepends() {
|
||||
let mut state = State::default();
|
||||
let _api_urls = state.get_api_urls(Network::MAINNET).collect::<Vec<_>>();
|
||||
|
||||
state.add_validator_url(
|
||||
config::ValidatorConfigEntry {
|
||||
nymd_url: "http://nymd_url.com".parse().unwrap(),
|
||||
nymd_name: Some("NymdUrl".to_string()),
|
||||
api_url: Some("http://nymd_url.com/api".parse().unwrap()),
|
||||
},
|
||||
Network::MAINNET,
|
||||
);
|
||||
state.add_validator_url(
|
||||
config::ValidatorConfigEntry {
|
||||
nymd_url: "http://nymd_url.com".parse().unwrap(),
|
||||
nymd_name: Some("NymdUrl".to_string()),
|
||||
api_url: Some("http://nymd_url.com/api".parse().unwrap()),
|
||||
},
|
||||
Network::MAINNET,
|
||||
);
|
||||
|
||||
state.add_validator_url(
|
||||
config::ValidatorConfigEntry {
|
||||
nymd_url: "http://foo.com".parse().unwrap(),
|
||||
nymd_name: None,
|
||||
api_url: None,
|
||||
},
|
||||
Network::MAINNET,
|
||||
);
|
||||
state.add_validator_url(
|
||||
config::ValidatorConfigEntry {
|
||||
nymd_url: "http://foo.com".parse().unwrap(),
|
||||
nymd_name: None,
|
||||
api_url: None,
|
||||
},
|
||||
Network::MAINNET,
|
||||
);
|
||||
|
||||
state.add_validator_url(
|
||||
config::ValidatorConfigEntry {
|
||||
nymd_url: "http://bar.com".parse().unwrap(),
|
||||
nymd_name: None,
|
||||
api_url: None,
|
||||
},
|
||||
Network::MAINNET,
|
||||
);
|
||||
state.add_validator_url(
|
||||
config::ValidatorConfigEntry {
|
||||
nymd_url: "http://bar.com".parse().unwrap(),
|
||||
nymd_name: None,
|
||||
api_url: None,
|
||||
},
|
||||
Network::MAINNET,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
state
|
||||
.get_nymd_urls_only(Network::MAINNET)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
"http://nymd_url.com/".parse().unwrap(),
|
||||
"http://foo.com".parse().unwrap(),
|
||||
"http://bar.com".parse().unwrap(),
|
||||
"https://rpc.nyx.nodes.guru".parse().unwrap(),
|
||||
],
|
||||
);
|
||||
assert_eq!(
|
||||
state
|
||||
.get_api_urls_only(Network::MAINNET)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
"http://nymd_url.com/api".parse().unwrap(),
|
||||
"https://validator.nymtech.net/api/".parse().unwrap(),
|
||||
],
|
||||
);
|
||||
assert_eq!(
|
||||
state
|
||||
.get_all_nymd_urls()
|
||||
.get(&Network::MAINNET)
|
||||
.unwrap()
|
||||
.clone(),
|
||||
vec![
|
||||
"http://nymd_url.com/".parse().unwrap(),
|
||||
"http://foo.com".parse().unwrap(),
|
||||
"http://bar.com".parse().unwrap(),
|
||||
"https://rpc.nyx.nodes.guru".parse().unwrap(),
|
||||
],
|
||||
)
|
||||
}
|
||||
assert_eq!(
|
||||
state
|
||||
.get_nymd_urls_only(Network::MAINNET)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
"http://nymd_url.com/".parse().unwrap(),
|
||||
"http://foo.com".parse().unwrap(),
|
||||
"http://bar.com".parse().unwrap(),
|
||||
"https://rpc.nyx.nodes.guru".parse().unwrap(),
|
||||
],
|
||||
);
|
||||
assert_eq!(
|
||||
state
|
||||
.get_api_urls_only(Network::MAINNET)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
"http://nymd_url.com/api".parse().unwrap(),
|
||||
"https://validator.nymtech.net/api/".parse().unwrap(),
|
||||
],
|
||||
);
|
||||
assert_eq!(
|
||||
state
|
||||
.get_all_nymd_urls()
|
||||
.get(&Network::MAINNET)
|
||||
.unwrap()
|
||||
.clone(),
|
||||
vec![
|
||||
"http://nymd_url.com/".parse().unwrap(),
|
||||
"http://foo.com".parse().unwrap(),
|
||||
"http://bar.com".parse().unwrap(),
|
||||
"https://rpc.nyx.nodes.guru".parse().unwrap(),
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,40 +6,36 @@ use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
fn get_env_as_option(key: &str) -> Option<String> {
|
||||
match ::std::env::var(key) {
|
||||
Ok(res) => Some(res),
|
||||
Err(_e) => None,
|
||||
}
|
||||
match ::std::env::var(key) {
|
||||
Ok(res) => Some(res),
|
||||
Err(_e) => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_env() -> AppEnv {
|
||||
AppEnv {
|
||||
ADMIN_ADDRESS: get_env_as_option("ADMIN_ADDRESS"),
|
||||
SHOW_TERMINAL: get_env_as_option("SHOW_TERMINAL"),
|
||||
}
|
||||
AppEnv {
|
||||
ADMIN_ADDRESS: get_env_as_option("ADMIN_ADDRESS"),
|
||||
SHOW_TERMINAL: get_env_as_option("SHOW_TERMINAL"),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn owns_mixnode(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<bool, BackendError> {
|
||||
Ok(
|
||||
nymd_client!(state)
|
||||
.owns_mixnode(nymd_client!(state).address())
|
||||
.await?
|
||||
.is_some(),
|
||||
)
|
||||
Ok(nymd_client!(state)
|
||||
.owns_mixnode(nymd_client!(state).address())
|
||||
.await?
|
||||
.is_some())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn owns_gateway(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<bool, BackendError> {
|
||||
Ok(
|
||||
nymd_client!(state)
|
||||
.owns_gateway(nymd_client!(state).address())
|
||||
.await?
|
||||
.is_some(),
|
||||
)
|
||||
Ok(nymd_client!(state)
|
||||
.owns_gateway(nymd_client!(state).address())
|
||||
.await?
|
||||
.is_some())
|
||||
}
|
||||
|
||||
@@ -29,119 +29,125 @@ const CURRENT_WALLET_FILE_VERSION: u32 = 1;
|
||||
/// The wallet, stored as a serialized json file.
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub(crate) struct StoredWallet {
|
||||
version: u32,
|
||||
accounts: Vec<EncryptedLogin>,
|
||||
version: u32,
|
||||
accounts: Vec<EncryptedLogin>,
|
||||
}
|
||||
|
||||
impl StoredWallet {
|
||||
#[allow(unused)]
|
||||
pub fn version(&self) -> u32 {
|
||||
self.version
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.accounts.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.accounts.is_empty()
|
||||
}
|
||||
|
||||
pub fn add_encrypted_login(&mut self, new_login: EncryptedLogin) -> Result<(), BackendError> {
|
||||
if self.get_encrypted_login(&new_login.id).is_ok() {
|
||||
return Err(BackendError::WalletLoginIdAlreadyExists);
|
||||
#[allow(unused)]
|
||||
pub fn version(&self) -> u32 {
|
||||
self.version
|
||||
}
|
||||
self.accounts.push(new_login);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_encrypted_login(&self, id: &LoginId) -> Result<&EncryptedData<StoredLogin>, BackendError> {
|
||||
self
|
||||
.accounts
|
||||
.iter()
|
||||
.find(|account| &account.id == id)
|
||||
.map(|account| &account.account)
|
||||
.ok_or(BackendError::WalletNoSuchLoginId)
|
||||
}
|
||||
|
||||
fn get_encrypted_login_mut(&mut self, id: &LoginId) -> Result<&mut EncryptedLogin, BackendError> {
|
||||
self
|
||||
.accounts
|
||||
.iter_mut()
|
||||
.find(|account| &account.id == id)
|
||||
.ok_or(BackendError::WalletNoSuchLoginId)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn get_encrypted_login_by_index(&self, index: usize) -> Option<&EncryptedLogin> {
|
||||
self.accounts.get(index)
|
||||
}
|
||||
|
||||
pub fn replace_encrypted_login(&mut self, new_login: EncryptedLogin) -> Result<(), BackendError> {
|
||||
let login = self.get_encrypted_login_mut(&new_login.id)?;
|
||||
*login = new_login;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_encrypted_login(&mut self, id: &LoginId) -> Option<EncryptedLogin> {
|
||||
if let Some(index) = self.accounts.iter().position(|account| &account.id == id) {
|
||||
log::info!("Removing from wallet file: {id}");
|
||||
Some(self.accounts.remove(index))
|
||||
} else {
|
||||
log::debug!("Tried to remove non-existent id from wallet: {id}");
|
||||
None
|
||||
#[allow(unused)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.accounts.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrypt_login(
|
||||
&self,
|
||||
id: &LoginId,
|
||||
password: &UserPassword,
|
||||
) -> Result<StoredLogin, BackendError> {
|
||||
self.get_encrypted_login(id)?.decrypt_struct(password)
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.accounts.is_empty()
|
||||
}
|
||||
|
||||
pub fn decrypt_all(&self, password: &UserPassword) -> Result<Vec<StoredLogin>, BackendError> {
|
||||
self
|
||||
.accounts
|
||||
.iter()
|
||||
.map(|account| account.account.decrypt_struct(password))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
}
|
||||
pub fn add_encrypted_login(&mut self, new_login: EncryptedLogin) -> Result<(), BackendError> {
|
||||
if self.get_encrypted_login(&new_login.id).is_ok() {
|
||||
return Err(BackendError::WalletLoginIdAlreadyExists);
|
||||
}
|
||||
self.accounts.push(new_login);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn password_can_decrypt_all(&self, password: &UserPassword) -> bool {
|
||||
self.decrypt_all(password).is_ok()
|
||||
}
|
||||
fn get_encrypted_login(
|
||||
&self,
|
||||
id: &LoginId,
|
||||
) -> Result<&EncryptedData<StoredLogin>, BackendError> {
|
||||
self.accounts
|
||||
.iter()
|
||||
.find(|account| &account.id == id)
|
||||
.map(|account| &account.account)
|
||||
.ok_or(BackendError::WalletNoSuchLoginId)
|
||||
}
|
||||
|
||||
fn get_encrypted_login_mut(
|
||||
&mut self,
|
||||
id: &LoginId,
|
||||
) -> Result<&mut EncryptedLogin, BackendError> {
|
||||
self.accounts
|
||||
.iter_mut()
|
||||
.find(|account| &account.id == id)
|
||||
.ok_or(BackendError::WalletNoSuchLoginId)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn get_encrypted_login_by_index(&self, index: usize) -> Option<&EncryptedLogin> {
|
||||
self.accounts.get(index)
|
||||
}
|
||||
|
||||
pub fn replace_encrypted_login(
|
||||
&mut self,
|
||||
new_login: EncryptedLogin,
|
||||
) -> Result<(), BackendError> {
|
||||
let login = self.get_encrypted_login_mut(&new_login.id)?;
|
||||
*login = new_login;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_encrypted_login(&mut self, id: &LoginId) -> Option<EncryptedLogin> {
|
||||
if let Some(index) = self.accounts.iter().position(|account| &account.id == id) {
|
||||
log::info!("Removing from wallet file: {id}");
|
||||
Some(self.accounts.remove(index))
|
||||
} else {
|
||||
log::debug!("Tried to remove non-existent id from wallet: {id}");
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrypt_login(
|
||||
&self,
|
||||
id: &LoginId,
|
||||
password: &UserPassword,
|
||||
) -> Result<StoredLogin, BackendError> {
|
||||
self.get_encrypted_login(id)?.decrypt_struct(password)
|
||||
}
|
||||
|
||||
pub fn decrypt_all(&self, password: &UserPassword) -> Result<Vec<StoredLogin>, BackendError> {
|
||||
self.accounts
|
||||
.iter()
|
||||
.map(|account| account.account.decrypt_struct(password))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
}
|
||||
|
||||
pub fn password_can_decrypt_all(&self, password: &UserPassword) -> bool {
|
||||
self.decrypt_all(password).is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for StoredWallet {
|
||||
fn default() -> Self {
|
||||
StoredWallet {
|
||||
version: CURRENT_WALLET_FILE_VERSION,
|
||||
accounts: Vec::new(),
|
||||
fn default() -> Self {
|
||||
StoredWallet {
|
||||
version: CURRENT_WALLET_FILE_VERSION,
|
||||
accounts: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Each entry in the stored wallet file. An id field in plaintext and an encrypted stored login.
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub(crate) struct EncryptedLogin {
|
||||
pub id: LoginId,
|
||||
pub account: EncryptedData<StoredLogin>,
|
||||
pub id: LoginId,
|
||||
pub account: EncryptedData<StoredLogin>,
|
||||
}
|
||||
|
||||
impl EncryptedLogin {
|
||||
pub(crate) fn encrypt(
|
||||
id: LoginId,
|
||||
login: &StoredLogin,
|
||||
password: &UserPassword,
|
||||
) -> Result<Self, BackendError> {
|
||||
Ok(EncryptedLogin {
|
||||
id,
|
||||
account: super::encryption::encrypt_struct(login, password)?,
|
||||
})
|
||||
}
|
||||
pub(crate) fn encrypt(
|
||||
id: LoginId,
|
||||
login: &StoredLogin,
|
||||
password: &UserPassword,
|
||||
) -> Result<Self, BackendError> {
|
||||
Ok(EncryptedLogin {
|
||||
id,
|
||||
account: super::encryption::encrypt_struct(login, password)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A stored login is either a account, such as a mnemonic, or a list of multiple accounts where
|
||||
@@ -150,147 +156,148 @@ impl EncryptedLogin {
|
||||
#[serde(untagged)]
|
||||
#[zeroize(drop)]
|
||||
pub(crate) enum StoredLogin {
|
||||
Mnemonic(MnemonicAccount),
|
||||
// PrivateKey(PrivateKeyAccount)
|
||||
Multiple(MultipleAccounts),
|
||||
Mnemonic(MnemonicAccount),
|
||||
// PrivateKey(PrivateKeyAccount)
|
||||
Multiple(MultipleAccounts),
|
||||
}
|
||||
|
||||
impl StoredLogin {
|
||||
#[cfg(test)]
|
||||
pub(crate) fn as_mnemonic_account(&self) -> Option<&MnemonicAccount> {
|
||||
match self {
|
||||
StoredLogin::Mnemonic(mn) => Some(mn),
|
||||
StoredLogin::Multiple(_) => None,
|
||||
#[cfg(test)]
|
||||
pub(crate) fn as_mnemonic_account(&self) -> Option<&MnemonicAccount> {
|
||||
match self {
|
||||
StoredLogin::Mnemonic(mn) => Some(mn),
|
||||
StoredLogin::Multiple(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn as_multiple_accounts(&self) -> Option<&MultipleAccounts> {
|
||||
match self {
|
||||
StoredLogin::Mnemonic(_) => None,
|
||||
StoredLogin::Multiple(accounts) => Some(accounts),
|
||||
#[cfg(test)]
|
||||
pub(crate) fn as_multiple_accounts(&self) -> Option<&MultipleAccounts> {
|
||||
match self {
|
||||
StoredLogin::Mnemonic(_) => None,
|
||||
StoredLogin::Multiple(accounts) => Some(accounts),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the login as multiple accounts, and if there is only a single mnemonic backed account,
|
||||
// return a set containing only the single account paired with the account id passed as function
|
||||
// argument.
|
||||
pub(crate) fn unwrap_into_multiple_accounts(self, id: AccountId) -> MultipleAccounts {
|
||||
match self {
|
||||
StoredLogin::Mnemonic(ref account) => vec![WalletAccount::new(id, account.clone())].into(),
|
||||
StoredLogin::Multiple(ref accounts) => accounts.clone(),
|
||||
// Return the login as multiple accounts, and if there is only a single mnemonic backed account,
|
||||
// return a set containing only the single account paired with the account id passed as function
|
||||
// argument.
|
||||
pub(crate) fn unwrap_into_multiple_accounts(self, id: AccountId) -> MultipleAccounts {
|
||||
match self {
|
||||
StoredLogin::Mnemonic(ref account) => {
|
||||
vec![WalletAccount::new(id, account.clone())].into()
|
||||
}
|
||||
StoredLogin::Multiple(ref accounts) => accounts.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiple stored accounts, each entry having an id and a data field.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Zeroize, PartialEq, Eq)]
|
||||
pub(crate) struct MultipleAccounts {
|
||||
accounts: Vec<WalletAccount>,
|
||||
accounts: Vec<WalletAccount>,
|
||||
}
|
||||
|
||||
impl MultipleAccounts {
|
||||
pub(crate) fn new() -> Self {
|
||||
MultipleAccounts {
|
||||
accounts: Vec::new(),
|
||||
pub(crate) fn new() -> Self {
|
||||
MultipleAccounts {
|
||||
accounts: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_accounts(&self) -> impl Iterator<Item = &WalletAccount> {
|
||||
self.accounts.iter()
|
||||
}
|
||||
|
||||
pub(crate) fn get_account(&self, id: &AccountId) -> Option<&WalletAccount> {
|
||||
self.accounts.iter().find(|account| &account.id == id)
|
||||
}
|
||||
|
||||
pub(crate) fn get_account_with_mnemonic(
|
||||
&self,
|
||||
mnemonic: &bip39::Mnemonic,
|
||||
) -> Option<&WalletAccount> {
|
||||
self
|
||||
.get_accounts()
|
||||
.find(|account| account.mnemonic() == mnemonic)
|
||||
}
|
||||
|
||||
pub(crate) fn into_accounts(self) -> impl Iterator<Item = WalletAccount> {
|
||||
self.accounts.into_iter()
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
self.accounts.len()
|
||||
}
|
||||
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
self.accounts.is_empty()
|
||||
}
|
||||
|
||||
pub(crate) fn add(
|
||||
&mut self,
|
||||
id: AccountId,
|
||||
mnemonic: bip39::Mnemonic,
|
||||
hd_path: DerivationPath,
|
||||
) -> Result<(), BackendError> {
|
||||
if self.get_account(&id).is_some() {
|
||||
Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin)
|
||||
} else if self.get_account_with_mnemonic(&mnemonic).is_some() {
|
||||
Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin)
|
||||
} else {
|
||||
self.accounts.push(WalletAccount::new(
|
||||
id,
|
||||
MnemonicAccount::new(mnemonic, hd_path),
|
||||
));
|
||||
Ok(())
|
||||
pub(crate) fn get_accounts(&self) -> impl Iterator<Item = &WalletAccount> {
|
||||
self.accounts.iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn remove(&mut self, id: &AccountId) -> Result<(), BackendError> {
|
||||
if self.get_account(id).is_none() {
|
||||
return Err(BackendError::WalletNoSuchAccountIdInWalletLogin);
|
||||
pub(crate) fn get_account(&self, id: &AccountId) -> Option<&WalletAccount> {
|
||||
self.accounts.iter().find(|account| &account.id == id)
|
||||
}
|
||||
|
||||
pub(crate) fn get_account_with_mnemonic(
|
||||
&self,
|
||||
mnemonic: &bip39::Mnemonic,
|
||||
) -> Option<&WalletAccount> {
|
||||
self.get_accounts()
|
||||
.find(|account| account.mnemonic() == mnemonic)
|
||||
}
|
||||
|
||||
pub(crate) fn into_accounts(self) -> impl Iterator<Item = WalletAccount> {
|
||||
self.accounts.into_iter()
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
self.accounts.len()
|
||||
}
|
||||
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
self.accounts.is_empty()
|
||||
}
|
||||
|
||||
pub(crate) fn add(
|
||||
&mut self,
|
||||
id: AccountId,
|
||||
mnemonic: bip39::Mnemonic,
|
||||
hd_path: DerivationPath,
|
||||
) -> Result<(), BackendError> {
|
||||
if self.get_account(&id).is_some() {
|
||||
Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin)
|
||||
} else if self.get_account_with_mnemonic(&mnemonic).is_some() {
|
||||
Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin)
|
||||
} else {
|
||||
self.accounts.push(WalletAccount::new(
|
||||
id,
|
||||
MnemonicAccount::new(mnemonic, hd_path),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn remove(&mut self, id: &AccountId) -> Result<(), BackendError> {
|
||||
if self.get_account(id).is_none() {
|
||||
return Err(BackendError::WalletNoSuchAccountIdInWalletLogin);
|
||||
}
|
||||
self.accounts.retain(|accounts| &accounts.id != id);
|
||||
Ok(())
|
||||
}
|
||||
self.accounts.retain(|accounts| &accounts.id != id);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<WalletAccount>> for MultipleAccounts {
|
||||
fn from(accounts: Vec<WalletAccount>) -> MultipleAccounts {
|
||||
Self { accounts }
|
||||
}
|
||||
fn from(accounts: Vec<WalletAccount>) -> MultipleAccounts {
|
||||
Self { accounts }
|
||||
}
|
||||
}
|
||||
|
||||
/// An entry in the list of stored accounts
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Zeroize, PartialEq, Eq)]
|
||||
pub(crate) struct WalletAccount {
|
||||
id: AccountId,
|
||||
account: AccountData,
|
||||
id: AccountId,
|
||||
account: AccountData,
|
||||
}
|
||||
|
||||
impl WalletAccount {
|
||||
pub(crate) fn new(id: AccountId, mnemonic_account: MnemonicAccount) -> Self {
|
||||
Self {
|
||||
id,
|
||||
account: AccountData::Mnemonic(mnemonic_account),
|
||||
pub(crate) fn new(id: AccountId, mnemonic_account: MnemonicAccount) -> Self {
|
||||
Self {
|
||||
id,
|
||||
account: AccountData::Mnemonic(mnemonic_account),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn id(&self) -> &AccountId {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub(crate) fn mnemonic(&self) -> &bip39::Mnemonic {
|
||||
match self.account {
|
||||
AccountData::Mnemonic(ref account) => account.mnemonic(),
|
||||
pub(crate) fn id(&self) -> &AccountId {
|
||||
&self.id
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn hd_path(&self) -> &DerivationPath {
|
||||
match self.account {
|
||||
AccountData::Mnemonic(ref account) => account.hd_path(),
|
||||
pub(crate) fn mnemonic(&self) -> &bip39::Mnemonic {
|
||||
match self.account {
|
||||
AccountData::Mnemonic(ref account) => account.mnemonic(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn hd_path(&self) -> &DerivationPath {
|
||||
match self.account {
|
||||
AccountData::Mnemonic(ref account) => account.hd_path(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An account usually is a mnemonic account, but in the future it might be backed by a private
|
||||
@@ -299,69 +306,69 @@ impl WalletAccount {
|
||||
#[serde(untagged)]
|
||||
#[zeroize(drop)]
|
||||
enum AccountData {
|
||||
Mnemonic(MnemonicAccount),
|
||||
// PrivateKey(PrivateKeyAccount)
|
||||
Mnemonic(MnemonicAccount),
|
||||
// PrivateKey(PrivateKeyAccount)
|
||||
}
|
||||
|
||||
/// An account backed by a unique mnemonic.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) struct MnemonicAccount {
|
||||
mnemonic: bip39::Mnemonic,
|
||||
#[serde(with = "display_hd_path")]
|
||||
hd_path: DerivationPath,
|
||||
mnemonic: bip39::Mnemonic,
|
||||
#[serde(with = "display_hd_path")]
|
||||
hd_path: DerivationPath,
|
||||
}
|
||||
|
||||
impl MnemonicAccount {
|
||||
pub(crate) fn new(mnemonic: bip39::Mnemonic, hd_path: DerivationPath) -> Self {
|
||||
Self { mnemonic, hd_path }
|
||||
}
|
||||
pub(crate) fn new(mnemonic: bip39::Mnemonic, hd_path: DerivationPath) -> Self {
|
||||
Self { mnemonic, hd_path }
|
||||
}
|
||||
|
||||
pub(crate) fn mnemonic(&self) -> &bip39::Mnemonic {
|
||||
&self.mnemonic
|
||||
}
|
||||
pub(crate) fn mnemonic(&self) -> &bip39::Mnemonic {
|
||||
&self.mnemonic
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn hd_path(&self) -> &DerivationPath {
|
||||
&self.hd_path
|
||||
}
|
||||
#[cfg(test)]
|
||||
pub(crate) fn hd_path(&self) -> &DerivationPath {
|
||||
&self.hd_path
|
||||
}
|
||||
}
|
||||
|
||||
impl Zeroize for MnemonicAccount {
|
||||
fn zeroize(&mut self) {
|
||||
// in ideal world, Mnemonic would have had zeroize defined on it (there's an almost year old PR that introduces it)
|
||||
// and the memory would have been filled with zeroes.
|
||||
//
|
||||
// we really don't want to keep our real mnemonic in memory, so let's do the semi-nasty thing
|
||||
// of overwriting it with a fresh mnemonic that was never used before
|
||||
//
|
||||
// note: this function can only fail on an invalid word count, which clearly is not the case here
|
||||
self.mnemonic = bip39::Mnemonic::generate(self.mnemonic.word_count()).unwrap();
|
||||
fn zeroize(&mut self) {
|
||||
// in ideal world, Mnemonic would have had zeroize defined on it (there's an almost year old PR that introduces it)
|
||||
// and the memory would have been filled with zeroes.
|
||||
//
|
||||
// we really don't want to keep our real mnemonic in memory, so let's do the semi-nasty thing
|
||||
// of overwriting it with a fresh mnemonic that was never used before
|
||||
//
|
||||
// note: this function can only fail on an invalid word count, which clearly is not the case here
|
||||
self.mnemonic = bip39::Mnemonic::generate(self.mnemonic.word_count()).unwrap();
|
||||
|
||||
// further note: we don't really care about the hd_path, there's nothing secret about it.
|
||||
}
|
||||
// further note: we don't really care about the hd_path, there's nothing secret about it.
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MnemonicAccount {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize()
|
||||
}
|
||||
fn drop(&mut self) {
|
||||
self.zeroize()
|
||||
}
|
||||
}
|
||||
|
||||
mod display_hd_path {
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
use validator_client::nymd::bip32::DerivationPath;
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
use validator_client::nymd::bip32::DerivationPath;
|
||||
|
||||
pub fn serialize<S: Serializer>(
|
||||
hd_path: &DerivationPath,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error> {
|
||||
serializer.collect_str(hd_path)
|
||||
}
|
||||
pub fn serialize<S: Serializer>(
|
||||
hd_path: &DerivationPath,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error> {
|
||||
serializer.collect_str(hd_path)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D: Deserializer<'de>>(
|
||||
deserializer: D,
|
||||
) -> Result<DerivationPath, D::Error> {
|
||||
let s = <&str>::deserialize(deserializer)?;
|
||||
s.parse().map_err(serde::de::Error::custom)
|
||||
}
|
||||
pub fn deserialize<'de, D: Deserializer<'de>>(
|
||||
deserializer: D,
|
||||
) -> Result<DerivationPath, D::Error> {
|
||||
let s = <&str>::deserialize(deserializer)?;
|
||||
s.parse().map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ use aes_gcm::aead::generic_array::ArrayLength;
|
||||
use aes_gcm::aead::{Aead, NewAead};
|
||||
use aes_gcm::{Aes256Gcm, Key, Nonce};
|
||||
use argon2::{
|
||||
password_hash::rand_core::{OsRng, RngCore},
|
||||
Algorithm, Argon2, Params, Version,
|
||||
password_hash::rand_core::{OsRng, RngCore},
|
||||
Algorithm, Argon2, Params, Version,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::marker::PhantomData;
|
||||
@@ -27,249 +27,249 @@ const IV_LEN: usize = 12;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Zeroize)]
|
||||
pub(crate) struct EncryptedData<T> {
|
||||
#[serde(with = "base64")]
|
||||
ciphertext: Vec<u8>,
|
||||
#[serde(with = "base64")]
|
||||
salt: Vec<u8>,
|
||||
#[serde(with = "base64")]
|
||||
iv: Vec<u8>,
|
||||
#[serde(with = "base64")]
|
||||
ciphertext: Vec<u8>,
|
||||
#[serde(with = "base64")]
|
||||
salt: Vec<u8>,
|
||||
#[serde(with = "base64")]
|
||||
iv: Vec<u8>,
|
||||
|
||||
#[serde(skip)]
|
||||
#[zeroize(skip)]
|
||||
_marker: PhantomData<T>,
|
||||
#[serde(skip)]
|
||||
#[zeroize(skip)]
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Drop for EncryptedData<T> {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
}
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
// we only ever want to expose those getters in the test code
|
||||
#[cfg(test)]
|
||||
impl<T> EncryptedData<T> {
|
||||
pub(crate) fn ciphertext(&self) -> &[u8] {
|
||||
&self.ciphertext
|
||||
}
|
||||
pub(crate) fn ciphertext(&self) -> &[u8] {
|
||||
&self.ciphertext
|
||||
}
|
||||
|
||||
pub(crate) fn salt(&self) -> &[u8] {
|
||||
&self.salt
|
||||
}
|
||||
pub(crate) fn salt(&self) -> &[u8] {
|
||||
&self.salt
|
||||
}
|
||||
|
||||
pub(crate) fn iv(&self) -> &[u8] {
|
||||
&self.iv
|
||||
}
|
||||
pub(crate) fn iv(&self) -> &[u8] {
|
||||
&self.iv
|
||||
}
|
||||
}
|
||||
|
||||
// helper to make Vec<u8> serialization use base64 representation to make it human readable
|
||||
// so that it would be easier for users to copy contents from the disk if they wanted to use it elsewhere
|
||||
mod base64 {
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
|
||||
pub fn serialize<S: Serializer>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_str(&base64::encode(bytes))
|
||||
}
|
||||
pub fn serialize<S: Serializer>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_str(&base64::encode(bytes))
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<u8>, D::Error> {
|
||||
let s = <String>::deserialize(deserializer)?;
|
||||
base64::decode(&s).map_err(serde::de::Error::custom)
|
||||
}
|
||||
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<u8>, D::Error> {
|
||||
let s = <String>::deserialize(deserializer)?;
|
||||
base64::decode(&s).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> EncryptedData<T> {
|
||||
#[allow(unused)]
|
||||
pub(crate) fn encrypt_struct(data: &T, password: &UserPassword) -> Result<Self, BackendError>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
encrypt_struct(data, password)
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub(crate) fn encrypt_struct(data: &T, password: &UserPassword) -> Result<Self, BackendError>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
encrypt_struct(data, password)
|
||||
}
|
||||
|
||||
pub(crate) fn decrypt_struct(&self, password: &UserPassword) -> Result<T, BackendError>
|
||||
where
|
||||
T: for<'a> Deserialize<'a>,
|
||||
{
|
||||
decrypt_struct(self, password)
|
||||
}
|
||||
pub(crate) fn decrypt_struct(&self, password: &UserPassword) -> Result<T, BackendError>
|
||||
where
|
||||
T: for<'a> Deserialize<'a>,
|
||||
{
|
||||
decrypt_struct(self, password)
|
||||
}
|
||||
}
|
||||
|
||||
impl EncryptedData<Vec<u8>> {
|
||||
#[allow(unused)]
|
||||
pub(crate) fn encrypt_data(data: &[u8], password: &UserPassword) -> Result<Self, BackendError> {
|
||||
encrypt_data(data, password)
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub(crate) fn encrypt_data(data: &[u8], password: &UserPassword) -> Result<Self, BackendError> {
|
||||
encrypt_data(data, password)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn decrypt_data(&self, password: &UserPassword) -> Result<Vec<u8>, BackendError> {
|
||||
decrypt_data(self, password)
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub(crate) fn decrypt_data(&self, password: &UserPassword) -> Result<Vec<u8>, BackendError> {
|
||||
decrypt_data(self, password)
|
||||
}
|
||||
}
|
||||
|
||||
fn derive_cipher_key<KeySize>(
|
||||
password: &UserPassword,
|
||||
salt: &[u8],
|
||||
password: &UserPassword,
|
||||
salt: &[u8],
|
||||
) -> Result<Key<KeySize>, BackendError>
|
||||
where
|
||||
KeySize: ArrayLength<u8>,
|
||||
KeySize: ArrayLength<u8>,
|
||||
{
|
||||
// this can only fail if output length is either smaller than 4 or larger than 2^32 - 1 which is not the case here
|
||||
let params = Params::new(MEMORY_COST, ITERATIONS, PARALLELISM, Some(OUTPUT_LENGTH)).unwrap();
|
||||
// this can only fail if output length is either smaller than 4 or larger than 2^32 - 1 which is not the case here
|
||||
let params = Params::new(MEMORY_COST, ITERATIONS, PARALLELISM, Some(OUTPUT_LENGTH)).unwrap();
|
||||
|
||||
let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
|
||||
let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
|
||||
|
||||
let mut key = Key::default();
|
||||
argon2.hash_password_into(password.as_bytes(), salt, &mut key)?;
|
||||
let mut key = Key::default();
|
||||
argon2.hash_password_into(password.as_bytes(), salt, &mut key)?;
|
||||
|
||||
Ok(key)
|
||||
Ok(key)
|
||||
}
|
||||
|
||||
fn random_salt_and_iv() -> (Vec<u8>, Vec<u8>) {
|
||||
let mut rng = OsRng;
|
||||
let mut rng = OsRng;
|
||||
|
||||
let mut salt = vec![0u8; SALT_LEN];
|
||||
rng.fill_bytes(&mut salt);
|
||||
let mut salt = vec![0u8; SALT_LEN];
|
||||
rng.fill_bytes(&mut salt);
|
||||
|
||||
let mut iv = vec![0u8; IV_LEN];
|
||||
rng.fill_bytes(&mut iv);
|
||||
let mut iv = vec![0u8; IV_LEN];
|
||||
rng.fill_bytes(&mut iv);
|
||||
|
||||
(salt, iv)
|
||||
(salt, iv)
|
||||
}
|
||||
|
||||
fn encrypt(
|
||||
data: &[u8],
|
||||
password: &UserPassword,
|
||||
salt: &[u8],
|
||||
iv: &[u8],
|
||||
data: &[u8],
|
||||
password: &UserPassword,
|
||||
salt: &[u8],
|
||||
iv: &[u8],
|
||||
) -> Result<Vec<u8>, BackendError> {
|
||||
let key = derive_cipher_key(password, salt)?;
|
||||
let cipher = Aes256Gcm::new(&key);
|
||||
cipher
|
||||
.encrypt(Nonce::from_slice(iv), data)
|
||||
.map_err(|_| BackendError::EncryptionError)
|
||||
let key = derive_cipher_key(password, salt)?;
|
||||
let cipher = Aes256Gcm::new(&key);
|
||||
cipher
|
||||
.encrypt(Nonce::from_slice(iv), data)
|
||||
.map_err(|_| BackendError::EncryptionError)
|
||||
}
|
||||
|
||||
fn decrypt(
|
||||
ciphertext: &[u8],
|
||||
password: &UserPassword,
|
||||
salt: &[u8],
|
||||
iv: &[u8],
|
||||
ciphertext: &[u8],
|
||||
password: &UserPassword,
|
||||
salt: &[u8],
|
||||
iv: &[u8],
|
||||
) -> Result<Vec<u8>, BackendError> {
|
||||
let key = derive_cipher_key(password, salt)?;
|
||||
let cipher = Aes256Gcm::new(&key);
|
||||
cipher
|
||||
.decrypt(Nonce::from_slice(iv), ciphertext)
|
||||
.map_err(|_| BackendError::DecryptionError)
|
||||
let key = derive_cipher_key(password, salt)?;
|
||||
let cipher = Aes256Gcm::new(&key);
|
||||
cipher
|
||||
.decrypt(Nonce::from_slice(iv), ciphertext)
|
||||
.map_err(|_| BackendError::DecryptionError)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn encrypt_data(
|
||||
data: &[u8],
|
||||
password: &UserPassword,
|
||||
data: &[u8],
|
||||
password: &UserPassword,
|
||||
) -> Result<EncryptedData<Vec<u8>>, BackendError> {
|
||||
let (salt, iv) = random_salt_and_iv();
|
||||
let ciphertext = encrypt(data, password, &salt, &iv)?;
|
||||
let (salt, iv) = random_salt_and_iv();
|
||||
let ciphertext = encrypt(data, password, &salt, &iv)?;
|
||||
|
||||
Ok(EncryptedData {
|
||||
ciphertext,
|
||||
salt,
|
||||
iv,
|
||||
_marker: Default::default(),
|
||||
})
|
||||
Ok(EncryptedData {
|
||||
ciphertext,
|
||||
salt,
|
||||
iv,
|
||||
_marker: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn encrypt_struct<T>(
|
||||
data: &T,
|
||||
password: &UserPassword,
|
||||
data: &T,
|
||||
password: &UserPassword,
|
||||
) -> Result<EncryptedData<T>, BackendError>
|
||||
where
|
||||
T: Serialize,
|
||||
T: Serialize,
|
||||
{
|
||||
let bytes = serde_json::to_vec(data).map_err(|_| BackendError::EncryptionError)?;
|
||||
let bytes = serde_json::to_vec(data).map_err(|_| BackendError::EncryptionError)?;
|
||||
|
||||
let (salt, iv) = random_salt_and_iv();
|
||||
let ciphertext = encrypt(&bytes, password, &salt, &iv)?;
|
||||
let (salt, iv) = random_salt_and_iv();
|
||||
let ciphertext = encrypt(&bytes, password, &salt, &iv)?;
|
||||
|
||||
Ok(EncryptedData {
|
||||
ciphertext,
|
||||
salt,
|
||||
iv,
|
||||
_marker: Default::default(),
|
||||
})
|
||||
Ok(EncryptedData {
|
||||
ciphertext,
|
||||
salt,
|
||||
iv,
|
||||
_marker: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn decrypt_data(
|
||||
encrypted_data: &EncryptedData<Vec<u8>>,
|
||||
password: &UserPassword,
|
||||
encrypted_data: &EncryptedData<Vec<u8>>,
|
||||
password: &UserPassword,
|
||||
) -> Result<Vec<u8>, BackendError> {
|
||||
decrypt(
|
||||
&encrypted_data.ciphertext,
|
||||
password,
|
||||
&encrypted_data.salt,
|
||||
&encrypted_data.iv,
|
||||
)
|
||||
decrypt(
|
||||
&encrypted_data.ciphertext,
|
||||
password,
|
||||
&encrypted_data.salt,
|
||||
&encrypted_data.iv,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn decrypt_struct<T>(
|
||||
encrypted_data: &EncryptedData<T>,
|
||||
password: &UserPassword,
|
||||
encrypted_data: &EncryptedData<T>,
|
||||
password: &UserPassword,
|
||||
) -> Result<T, BackendError>
|
||||
where
|
||||
T: for<'a> Deserialize<'a>,
|
||||
T: for<'a> Deserialize<'a>,
|
||||
{
|
||||
let bytes = decrypt(
|
||||
&encrypted_data.ciphertext,
|
||||
password,
|
||||
&encrypted_data.salt,
|
||||
&encrypted_data.iv,
|
||||
)?;
|
||||
let bytes = decrypt(
|
||||
&encrypted_data.ciphertext,
|
||||
password,
|
||||
&encrypted_data.salt,
|
||||
&encrypted_data.iv,
|
||||
)?;
|
||||
|
||||
serde_json::from_slice(&bytes).map_err(|_| BackendError::DecryptionError)
|
||||
serde_json::from_slice(&bytes).map_err(|_| BackendError::DecryptionError)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
struct DummyData {
|
||||
foo: String,
|
||||
bar: String,
|
||||
}
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
struct DummyData {
|
||||
foo: String,
|
||||
bar: String,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn struct_encryption() {
|
||||
let password = UserPassword::new("my-super-secret-password".to_string());
|
||||
let data = DummyData {
|
||||
foo: "my secret mnemonic".to_string(),
|
||||
bar: "totally-valid-hd-path".to_string(),
|
||||
};
|
||||
#[test]
|
||||
fn struct_encryption() {
|
||||
let password = UserPassword::new("my-super-secret-password".to_string());
|
||||
let data = DummyData {
|
||||
foo: "my secret mnemonic".to_string(),
|
||||
bar: "totally-valid-hd-path".to_string(),
|
||||
};
|
||||
|
||||
let wrong_password = UserPassword::new("brute-force-attempt-1".to_string());
|
||||
let wrong_password = UserPassword::new("brute-force-attempt-1".to_string());
|
||||
|
||||
let mut encrypted_data = encrypt_struct(&data, &password).unwrap();
|
||||
let recovered = decrypt_struct(&encrypted_data, &password).unwrap();
|
||||
assert_eq!(data, recovered);
|
||||
let mut encrypted_data = encrypt_struct(&data, &password).unwrap();
|
||||
let recovered = decrypt_struct(&encrypted_data, &password).unwrap();
|
||||
assert_eq!(data, recovered);
|
||||
|
||||
// decryption with wrong password fails
|
||||
assert!(decrypt_struct(&encrypted_data, &wrong_password).is_err());
|
||||
// decryption with wrong password fails
|
||||
assert!(decrypt_struct(&encrypted_data, &wrong_password).is_err());
|
||||
|
||||
// decryption fails if ciphertext got malformed
|
||||
encrypted_data.ciphertext[3] ^= 123;
|
||||
assert!(decrypt_struct(&encrypted_data, &wrong_password).is_err());
|
||||
// decryption fails if ciphertext got malformed
|
||||
encrypted_data.ciphertext[3] ^= 123;
|
||||
assert!(decrypt_struct(&encrypted_data, &wrong_password).is_err());
|
||||
|
||||
// restore the ciphertext (for test purposes)
|
||||
encrypted_data.ciphertext[3] ^= 123;
|
||||
// restore the ciphertext (for test purposes)
|
||||
encrypted_data.ciphertext[3] ^= 123;
|
||||
|
||||
// decryption fails if salt got malformed (it would result in incorrect key being derived)
|
||||
encrypted_data.salt[3] ^= 123;
|
||||
assert!(decrypt_struct(&encrypted_data, &password).is_err());
|
||||
// decryption fails if salt got malformed (it would result in incorrect key being derived)
|
||||
encrypted_data.salt[3] ^= 123;
|
||||
assert!(decrypt_struct(&encrypted_data, &password).is_err());
|
||||
|
||||
// restore the salt (for test purposes)
|
||||
encrypted_data.salt[3] ^= 123;
|
||||
// restore the salt (for test purposes)
|
||||
encrypted_data.salt[3] ^= 123;
|
||||
|
||||
// decryption fails if iv got malformed
|
||||
encrypted_data.iv[3] ^= 123;
|
||||
assert!(decrypt_struct(&encrypted_data, &password).is_err());
|
||||
}
|
||||
// decryption fails if iv got malformed
|
||||
encrypted_data.iv[3] ^= 123;
|
||||
assert!(decrypt_struct(&encrypted_data, &password).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,33 +11,33 @@ use zeroize::Zeroize;
|
||||
pub(crate) struct LoginId(String);
|
||||
|
||||
impl LoginId {
|
||||
pub(crate) fn new(id: String) -> LoginId {
|
||||
LoginId(id)
|
||||
}
|
||||
pub(crate) fn new(id: String) -> LoginId {
|
||||
LoginId(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for LoginId {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.0.as_ref()
|
||||
}
|
||||
fn as_ref(&self) -> &str {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for LoginId {
|
||||
fn from(id: String) -> Self {
|
||||
Self::new(id)
|
||||
}
|
||||
fn from(id: String) -> Self {
|
||||
Self::new(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for LoginId {
|
||||
fn from(id: &str) -> Self {
|
||||
Self::new(id.to_string())
|
||||
}
|
||||
fn from(id: &str) -> Self {
|
||||
Self::new(id.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for LoginId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
// For each encrypted login, we can have multiple encrypted accounts.
|
||||
@@ -45,39 +45,39 @@ impl fmt::Display for LoginId {
|
||||
pub(crate) struct AccountId(String);
|
||||
|
||||
impl AccountId {
|
||||
pub(crate) fn new(id: String) -> AccountId {
|
||||
AccountId(id)
|
||||
}
|
||||
pub(crate) fn new(id: String) -> AccountId {
|
||||
AccountId(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for AccountId {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.0.as_ref()
|
||||
}
|
||||
fn as_ref(&self) -> &str {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for AccountId {
|
||||
fn from(id: String) -> Self {
|
||||
Self::new(id)
|
||||
}
|
||||
fn from(id: String) -> Self {
|
||||
Self::new(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for AccountId {
|
||||
fn from(id: &str) -> Self {
|
||||
Self::new(id.to_string())
|
||||
}
|
||||
fn from(id: &str) -> Self {
|
||||
Self::new(id.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LoginId> for AccountId {
|
||||
fn from(login_id: LoginId) -> Self {
|
||||
Self::new(login_id.0)
|
||||
}
|
||||
fn from(login_id: LoginId) -> Self {
|
||||
Self::new(login_id.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for AccountId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
// simple wrapper for String that will get zeroized on drop
|
||||
@@ -86,17 +86,17 @@ impl fmt::Display for AccountId {
|
||||
pub(crate) struct UserPassword(String);
|
||||
|
||||
impl UserPassword {
|
||||
pub(crate) fn new(pass: String) -> UserPassword {
|
||||
UserPassword(pass)
|
||||
}
|
||||
pub(crate) fn new(pass: String) -> UserPassword {
|
||||
UserPassword(pass)
|
||||
}
|
||||
|
||||
pub(crate) fn as_bytes(&self) -> &[u8] {
|
||||
self.0.as_bytes()
|
||||
}
|
||||
pub(crate) fn as_bytes(&self) -> &[u8] {
|
||||
self.0.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for UserPassword {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.0.as_ref()
|
||||
}
|
||||
fn as_ref(&self) -> &str {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,15 +148,13 @@ pub(crate) async fn get_mixnode_reward_estimation(
|
||||
let reward_params = RewardParams::new(reward_params, node_reward_params);
|
||||
|
||||
match bond.estimate_reward(&reward_params) {
|
||||
Ok((
|
||||
estimated_total_node_reward,
|
||||
estimated_operator_reward,
|
||||
estimated_delegators_reward,
|
||||
)) => {
|
||||
Ok(reward_estimate) => {
|
||||
let reponse = RewardEstimationResponse {
|
||||
estimated_total_node_reward,
|
||||
estimated_operator_reward,
|
||||
estimated_delegators_reward,
|
||||
estimated_total_node_reward: reward_estimate.total_node_reward,
|
||||
estimated_operator_reward: reward_estimate.operator_reward,
|
||||
estimated_delegators_reward: reward_estimate.delegators_reward,
|
||||
estimated_node_profit: reward_estimate.node_profit,
|
||||
estimated_operator_cost: reward_estimate.operator_cost,
|
||||
reward_params,
|
||||
as_at,
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ use tokio::sync::RwLock;
|
||||
use tokio::time::sleep;
|
||||
use validator_client::nymd::{
|
||||
hash::{Hash, SHA256_HASH_SIZE},
|
||||
CosmWasmClient, CosmosCoin, Fee, QueryNymdClient, SigningCosmWasmClient, SigningNymdClient,
|
||||
Coin, CosmWasmClient, Fee, QueryNymdClient, SigningCosmWasmClient, SigningNymdClient,
|
||||
TendermintTime,
|
||||
};
|
||||
use validator_client::ValidatorClientError;
|
||||
@@ -326,7 +326,7 @@ impl<C> Client<C> {
|
||||
&self,
|
||||
rewarded_set: Vec<IdentityKey>,
|
||||
expected_active_set_size: u32,
|
||||
reward_msgs: Vec<(ExecuteMsg, Vec<CosmosCoin>)>,
|
||||
reward_msgs: Vec<(ExecuteMsg, Vec<Coin>)>,
|
||||
) -> Result<(), RewardingError>
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
@@ -358,7 +358,7 @@ impl<C> Client<C> {
|
||||
|
||||
async fn execute_multiple_with_retry<M>(
|
||||
&self,
|
||||
msgs: Vec<(M, Vec<CosmosCoin>)>,
|
||||
msgs: Vec<(M, Vec<Coin>)>,
|
||||
fee: Fee,
|
||||
memo: String,
|
||||
) -> Result<(), RewardingError>
|
||||
|
||||
@@ -25,7 +25,7 @@ use std::collections::HashSet;
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
use tokio::time::sleep;
|
||||
use validator_client::nymd::{CosmosCoin, SigningNymdClient};
|
||||
use validator_client::nymd::{Coin, SigningNymdClient};
|
||||
|
||||
pub(crate) mod error;
|
||||
|
||||
@@ -120,7 +120,7 @@ impl RewardedSetUpdater {
|
||||
|
||||
async fn reward_current_rewarded_set(
|
||||
&self,
|
||||
) -> Result<Vec<(ExecuteMsg, Vec<CosmosCoin>)>, RewardingError> {
|
||||
) -> Result<Vec<(ExecuteMsg, Vec<Coin>)>, RewardingError> {
|
||||
let to_reward = self.nodes_to_reward().await?;
|
||||
let epoch = self.epoch().await?;
|
||||
|
||||
@@ -143,7 +143,7 @@ impl RewardedSetUpdater {
|
||||
async fn generate_reward_messages(
|
||||
&self,
|
||||
eligible_mixnodes: &[MixnodeToReward],
|
||||
) -> Result<Vec<(ExecuteMsg, Vec<CosmosCoin>)>, RewardingError> {
|
||||
) -> Result<Vec<(ExecuteMsg, Vec<Coin>)>, RewardingError> {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "no-reward")] {
|
||||
Ok(vec![])
|
||||
|
||||
@@ -52,6 +52,8 @@ pub struct RewardEstimationResponse {
|
||||
pub estimated_total_node_reward: u64,
|
||||
pub estimated_operator_reward: u64,
|
||||
pub estimated_delegators_reward: u64,
|
||||
pub estimated_node_profit: u64,
|
||||
pub estimated_operator_cost: u64,
|
||||
|
||||
pub reward_params: RewardParams,
|
||||
pub as_at: i64,
|
||||
|
||||
Reference in New Issue
Block a user