merge with wallet-delegation-ui-squashed
This commit is contained in:
@@ -10,7 +10,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [ self-hosted, custom-linux-exoscale ]
|
||||
runs-on: [ self-hosted, custom-linux ]
|
||||
# Enable sccache via environment variable
|
||||
env:
|
||||
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
||||
|
||||
@@ -4,7 +4,7 @@ on: workflow_dispatch
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [ self-hosted, custom-linux-exoscale ]
|
||||
runs-on: [ self-hosted, custom-linux ]
|
||||
# Enable sccache via environment variable
|
||||
env:
|
||||
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
||||
|
||||
@@ -10,7 +10,9 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [ self-hosted, custom-linux-exoscale ]
|
||||
runs-on: [ self-hosted, custom-linux ]
|
||||
env:
|
||||
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
||||
steps:
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
|
||||
|
||||
+19
-12
@@ -4,37 +4,43 @@
|
||||
|
||||
### Added
|
||||
|
||||
- wallet: require password to switch accounts
|
||||
- all: added network compilation target to `--help` (or `--version`) commands ([#1256]).
|
||||
- explorer-api: learned how to sum the delegations by owner in a new endpoint.
|
||||
- gateway: Added gateway coconut verifications and validator-api communication for double spending protection ([#1261])
|
||||
- mixnet-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292])
|
||||
- mixnet-contract: Replace all naked `-` with `saturating_sub`.
|
||||
- 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]).
|
||||
- validator-api: add `estimated_node_profit` and `estimated_operator_cost` to `reward-estimate` endpoint ([#1284])
|
||||
- validator-api: add detailed mixnode bond endpoints, and explorer-api makes use of that data to append stake saturation.
|
||||
- validator-api: add Swagger to document the REST API ([#1249]).
|
||||
- validator-api: Added new endpoints for coconut spending flow and communications with coconut & multisig contracts ([#1261])
|
||||
- vesting-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292])
|
||||
- wallet: add simple CLI tool for decrypting and recovering the wallet file.
|
||||
- wallet: added support for multiple accounts ([#1265])
|
||||
- wallet: compound and claim reward endpoints for operators and delegators ([#1302])
|
||||
- wallet: require password to switch accounts
|
||||
- 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: delegator and operator rewards use lambda and sigma instead of lambda_ticked and sigma_ticked ([#1284])
|
||||
- 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: replaced integer division with fixed for performance calculations ([#1284])
|
||||
- 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])
|
||||
- vesting-contract: replaced `checked_sub` with `saturating_sub` to fix the underflow in `get_vesting_tokens` ([#1275])
|
||||
|
||||
### 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
|
||||
[#1257]: https://github.com/nymtech/nym/pull/1257
|
||||
[#1258]: https://github.com/nymtech/nym/pull/1258
|
||||
[#1260]: https://github.com/nymtech/nym/pull/1260
|
||||
[#1261]: https://github.com/nymtech/nym/pull/1261
|
||||
[#1265]: https://github.com/nymtech/nym/pull/1265
|
||||
[#1267]: https://github.com/nymtech/nym/pull/1267
|
||||
[#1275]: https://github.com/nymtech/nym/pull/1275
|
||||
@@ -42,6 +48,7 @@
|
||||
[#1284]: https://github.com/nymtech/nym/pull/1284
|
||||
[#1292]: https://github.com/nymtech/nym/pull/1292
|
||||
[#1295]: https://github.com/nymtech/nym/pull/1295
|
||||
[#1302]: https://github.com/nymtech/nym/pull/1302
|
||||
|
||||
## [nym-wallet-v1.0.4](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.4) (2022-05-04)
|
||||
|
||||
|
||||
Generated
+63
-2
@@ -873,7 +873,6 @@ dependencies = [
|
||||
"bip39",
|
||||
"cfg-if 0.1.10",
|
||||
"clap 3.1.8",
|
||||
"coconut-bandwidth-contract-common",
|
||||
"coconut-interface",
|
||||
"credential-storage",
|
||||
"credentials",
|
||||
@@ -1166,6 +1165,42 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw-utils"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "babd2c090f39d07ce5bf2556962305e795daa048ce20a93709eb591476e4a29e"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw3"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f871854338a54c7bb094d16ffe17212b93b146d9659dbce4c9402a9b77e240ef"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw4"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4476d6a7c13c46ed9ff260bd0e1cf648dc37b13f483822e1ff2a431f0f6ee52"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.13.4"
|
||||
@@ -1567,6 +1602,14 @@ dependencies = [
|
||||
"uint",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "execute"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "explorer-api"
|
||||
version = "1.0.1"
|
||||
@@ -1574,6 +1617,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"humantime-serde",
|
||||
"isocountry",
|
||||
"itertools",
|
||||
"log",
|
||||
"mixnet-contract-common",
|
||||
"network-defaults",
|
||||
@@ -1950,7 +1994,6 @@ dependencies = [
|
||||
name = "gateway-requests"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bs58",
|
||||
"coconut-interface",
|
||||
"credentials",
|
||||
@@ -2909,6 +2952,18 @@ version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
|
||||
|
||||
[[package]]
|
||||
name = "multisig-contract-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
"cw3",
|
||||
"cw4",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.10"
|
||||
@@ -3239,6 +3294,7 @@ dependencies = [
|
||||
"coconut-interface",
|
||||
"config",
|
||||
"console-subscriber",
|
||||
"cosmwasm-std",
|
||||
"credential-storage",
|
||||
"credentials",
|
||||
"crypto",
|
||||
@@ -3250,6 +3306,7 @@ dependencies = [
|
||||
"humantime-serde",
|
||||
"log",
|
||||
"mixnet-contract-common",
|
||||
"multisig-contract-common",
|
||||
"nymcoconut",
|
||||
"nymsphinx",
|
||||
"okapi",
|
||||
@@ -6283,16 +6340,20 @@ dependencies = [
|
||||
"async-trait",
|
||||
"base64",
|
||||
"bip39",
|
||||
"coconut-bandwidth-contract-common",
|
||||
"coconut-interface",
|
||||
"colored",
|
||||
"config",
|
||||
"cosmrs",
|
||||
"cosmwasm-std",
|
||||
"cw3",
|
||||
"execute",
|
||||
"flate2",
|
||||
"futures",
|
||||
"itertools",
|
||||
"log",
|
||||
"mixnet-contract-common",
|
||||
"multisig-contract-common",
|
||||
"network-defaults",
|
||||
"prost 0.10.3",
|
||||
"reqwest",
|
||||
|
||||
@@ -31,10 +31,12 @@ members = [
|
||||
"common/credentials",
|
||||
"common/crypto",
|
||||
"common/crypto/dkg",
|
||||
"common/execute",
|
||||
"common/bandwidth-claim-contract",
|
||||
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
|
||||
"common/cosmwasm-smart-contracts/contracts-common",
|
||||
"common/cosmwasm-smart-contracts/mixnet-contract",
|
||||
"common/cosmwasm-smart-contracts/multisig-contract",
|
||||
"common/cosmwasm-smart-contracts/vesting-contract",
|
||||
"common/mixnode-common",
|
||||
"common/network-defaults",
|
||||
|
||||
@@ -17,7 +17,6 @@ thiserror = "1.0"
|
||||
url = "2.2"
|
||||
tokio = { version = "1.4", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime
|
||||
|
||||
coconut-bandwidth-contract-common = { path = "../../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" }
|
||||
coconut-interface = { path = "../../common/coconut-interface" }
|
||||
credentials = { path = "../../common/credentials" }
|
||||
credential-storage = { path = "../../common/credential-storage" }
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,60 +1,49 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 std::str::FromStr;
|
||||
use url::Url;
|
||||
use validator_client::nymd::{AccountId, Coin, Denom, NymdClient, SigningNymdClient};
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::{MNEMONIC, NYMD_URL};
|
||||
|
||||
use network_defaults::{DEFAULT_NETWORK, DENOM, VOUCHER_INFO};
|
||||
use validator_client::nymd::traits::CoconutBandwidthSigningClient;
|
||||
use validator_client::nymd::{Coin, Fee, NymdClient, SigningNymdClient};
|
||||
|
||||
pub(crate) struct Client {
|
||||
nymd_client: NymdClient<SigningNymdClient>,
|
||||
denom: Denom,
|
||||
contract_address: AccountId,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn new() -> Self {
|
||||
let nymd_url = Url::from_str(NYMD_URL).unwrap();
|
||||
let mnemonic = Mnemonic::from_str(MNEMONIC).unwrap();
|
||||
let nymd_client = NymdClient::connect_with_mnemonic(
|
||||
DEFAULT_NETWORK,
|
||||
nymd_url.as_ref(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
mnemonic,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
let denom = Denom::from_str(network_defaults::MIX_DENOM.base).unwrap();
|
||||
let contract_address = AccountId::from_str(CONTRACT_ADDRESS).unwrap();
|
||||
let nymd_client =
|
||||
NymdClient::connect_with_mnemonic(DEFAULT_NETWORK, nymd_url.as_ref(), mnemonic, None)
|
||||
.unwrap();
|
||||
|
||||
Client {
|
||||
nymd_client,
|
||||
denom,
|
||||
contract_address,
|
||||
}
|
||||
Client { nymd_client }
|
||||
}
|
||||
|
||||
pub async fn deposit(
|
||||
&self,
|
||||
amount: u64,
|
||||
info: &str,
|
||||
verification_key: String,
|
||||
encryption_key: String,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<String> {
|
||||
let req = ExecuteMsg::DepositFunds {
|
||||
data: DepositData::new(info.to_string(), verification_key, encryption_key),
|
||||
};
|
||||
let funds = vec![Coin::new(amount as u128, self.denom.to_string())];
|
||||
let amount = Coin::new(amount as u128, DENOM.to_string());
|
||||
Ok(self
|
||||
.nymd_client
|
||||
.execute(&self.contract_address, &req, Default::default(), "", funds)
|
||||
.deposit(
|
||||
amount,
|
||||
String::from(VOUCHER_INFO),
|
||||
verification_key,
|
||||
encryption_key,
|
||||
fee,
|
||||
)
|
||||
.await?
|
||||
.transaction_hash
|
||||
.to_string())
|
||||
|
||||
@@ -55,9 +55,9 @@ impl Execute for Deposit {
|
||||
let tx_hash = client
|
||||
.deposit(
|
||||
self.amount,
|
||||
VOUCHER_INFO,
|
||||
signing_keypair.public_key.clone(),
|
||||
encryption_keypair.public_key.clone(),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ impl GatewayClient {
|
||||
) -> Self {
|
||||
GatewayClient {
|
||||
authenticated: false,
|
||||
disabled_credentials_mode: true,
|
||||
disabled_credentials_mode: false,
|
||||
bandwidth_remaining: 0,
|
||||
gateway_address,
|
||||
gateway_identity,
|
||||
@@ -100,8 +100,8 @@ impl GatewayClient {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_disabled_credentials_mode(&mut self, disabled_credentials_mode: bool) {
|
||||
self.disabled_credentials_mode = disabled_credentials_mode
|
||||
pub fn set_disabled_credentials_mode(&mut self, _disabled_credentials_mode: bool) {
|
||||
self.disabled_credentials_mode = false;
|
||||
}
|
||||
|
||||
// TODO: later convert into proper builder methods
|
||||
@@ -496,7 +496,6 @@ impl GatewayClient {
|
||||
self.shared_key.as_ref().unwrap(),
|
||||
iv,
|
||||
)
|
||||
.ok_or(GatewayClientError::SerializeCredential)?
|
||||
.into();
|
||||
self.bandwidth_remaining = match self.send_websocket_message(msg).await? {
|
||||
ServerResponse::Bandwidth { available_total } => Ok(available_total),
|
||||
|
||||
@@ -10,8 +10,11 @@ rust-version = "1.56"
|
||||
[dependencies]
|
||||
base64 = "0.13"
|
||||
colored = "2.0"
|
||||
cw3 = "0.13.1"
|
||||
mixnet-contract-common = { path= "../../cosmwasm-smart-contracts/mixnet-contract" }
|
||||
vesting-contract-common = { path= "../../cosmwasm-smart-contracts/vesting-contract" }
|
||||
coconut-bandwidth-contract-common = { path= "../../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
|
||||
multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig-contract" }
|
||||
vesting-contract = { path = "../../../contracts/vesting" }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
@@ -38,6 +41,7 @@ flate2 = { version = "1.0.20", optional = true }
|
||||
sha2 = { version = "0.9.5", optional = true }
|
||||
itertools = { version = "0.10", optional = true }
|
||||
cosmwasm-std = { version = "1.0.0-beta8", optional = true }
|
||||
execute = { path = "../../execute" }
|
||||
|
||||
ts-rs = "6.1.2"
|
||||
|
||||
|
||||
@@ -2,16 +2,20 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{validator_api, ValidatorClientError};
|
||||
use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse};
|
||||
use coconut_interface::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, ExecuteReleaseFundsRequestBody,
|
||||
ProposeReleaseFundsRequestBody, ProposeReleaseFundsResponse, VerificationKeyResponse,
|
||||
VerifyCredentialBody, VerifyCredentialResponse,
|
||||
};
|
||||
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond};
|
||||
use url::Url;
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use validator_api_requests::models::UptimeResponse;
|
||||
use validator_api_requests::models::{
|
||||
CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse,
|
||||
StakeSaturationResponse,
|
||||
};
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use validator_api_requests::models::{MixNodeBondAnnotated, UptimeResponse};
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use network_defaults::DEFAULT_NETWORK;
|
||||
@@ -29,8 +33,6 @@ use mixnet_contract_common::{
|
||||
};
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use std::collections::{HashMap, HashSet};
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use std::str::FromStr;
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
#[must_use]
|
||||
@@ -39,9 +41,9 @@ pub struct Config {
|
||||
network: network_defaults::all::Network,
|
||||
api_url: Url,
|
||||
nymd_url: Url,
|
||||
mixnet_contract_address: Option<cosmrs::AccountId>,
|
||||
vesting_contract_address: Option<cosmrs::AccountId>,
|
||||
erc20_bridge_contract_address: Option<cosmrs::AccountId>,
|
||||
mixnet_contract_address: cosmrs::AccountId,
|
||||
vesting_contract_address: cosmrs::AccountId,
|
||||
bandwidth_claim_contract_address: cosmrs::AccountId,
|
||||
|
||||
mixnode_page_limit: Option<u32>,
|
||||
gateway_page_limit: Option<u32>,
|
||||
@@ -51,20 +53,22 @@ pub struct Config {
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
impl Config {
|
||||
pub fn new(
|
||||
network: network_defaults::all::Network,
|
||||
nymd_url: Url,
|
||||
api_url: Url,
|
||||
mixnet_contract_address: Option<cosmrs::AccountId>,
|
||||
vesting_contract_address: Option<cosmrs::AccountId>,
|
||||
erc20_bridge_contract_address: Option<cosmrs::AccountId>,
|
||||
) -> Self {
|
||||
pub fn new(network: network_defaults::all::Network, nymd_url: Url, api_url: Url) -> Self {
|
||||
Config {
|
||||
network,
|
||||
nymd_url,
|
||||
mixnet_contract_address,
|
||||
vesting_contract_address,
|
||||
erc20_bridge_contract_address,
|
||||
mixnet_contract_address: DEFAULT_NETWORK
|
||||
.mixnet_contract_address()
|
||||
.parse()
|
||||
.expect("Error parsing mixnet contract address"),
|
||||
vesting_contract_address: DEFAULT_NETWORK
|
||||
.vesting_contract_address()
|
||||
.parse()
|
||||
.expect("Error parsing vesting contract address"),
|
||||
bandwidth_claim_contract_address: DEFAULT_NETWORK
|
||||
.bandwidth_claim_contract_address()
|
||||
.parse()
|
||||
.expect("Error parsing bandwidth claim contract address"),
|
||||
api_url,
|
||||
mixnode_page_limit: None,
|
||||
gateway_page_limit: None,
|
||||
@@ -73,6 +77,21 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_mixnode_contract_address(mut self, address: cosmrs::AccountId) -> Self {
|
||||
self.mixnet_contract_address = address;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_vesting_contract_address(mut self, address: cosmrs::AccountId) -> Self {
|
||||
self.vesting_contract_address = address;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_bandwidth_claim_contract_address(mut self, address: cosmrs::AccountId) -> Self {
|
||||
self.bandwidth_claim_contract_address = address;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_mixnode_page_limit(mut self, limit: Option<u32>) -> Config {
|
||||
self.mixnode_page_limit = limit;
|
||||
self
|
||||
@@ -97,9 +116,9 @@ impl Config {
|
||||
#[cfg(feature = "nymd-client")]
|
||||
pub struct Client<C> {
|
||||
pub network: network_defaults::all::Network,
|
||||
mixnet_contract_address: Option<cosmrs::AccountId>,
|
||||
vesting_contract_address: Option<cosmrs::AccountId>,
|
||||
erc20_bridge_contract_address: Option<cosmrs::AccountId>,
|
||||
mixnet_contract_address: cosmrs::AccountId,
|
||||
vesting_contract_address: cosmrs::AccountId,
|
||||
bandwidth_claim_contract_address: cosmrs::AccountId,
|
||||
mnemonic: Option<bip39::Mnemonic>,
|
||||
|
||||
mixnode_page_limit: Option<u32>,
|
||||
@@ -122,18 +141,18 @@ impl Client<SigningNymdClient> {
|
||||
let nymd_client = NymdClient::connect_with_mnemonic(
|
||||
config.network,
|
||||
config.nymd_url.as_str(),
|
||||
config.mixnet_contract_address.clone(),
|
||||
config.vesting_contract_address.clone(),
|
||||
config.erc20_bridge_contract_address.clone(),
|
||||
mnemonic.clone(),
|
||||
None,
|
||||
)?;
|
||||
)?
|
||||
.with_mixnet_contract_address(config.mixnet_contract_address.clone())
|
||||
.with_vesting_contract_address(config.vesting_contract_address.clone())
|
||||
.with_bandwidth_claim_contract_address(config.bandwidth_claim_contract_address.clone());
|
||||
|
||||
Ok(Client {
|
||||
network: config.network,
|
||||
mixnet_contract_address: config.mixnet_contract_address,
|
||||
vesting_contract_address: config.vesting_contract_address,
|
||||
erc20_bridge_contract_address: config.erc20_bridge_contract_address,
|
||||
bandwidth_claim_contract_address: config.bandwidth_claim_contract_address,
|
||||
mnemonic: Some(mnemonic),
|
||||
mixnode_page_limit: config.mixnode_page_limit,
|
||||
gateway_page_limit: config.gateway_page_limit,
|
||||
@@ -148,12 +167,12 @@ impl Client<SigningNymdClient> {
|
||||
self.nymd = NymdClient::connect_with_mnemonic(
|
||||
self.network,
|
||||
new_endpoint.as_ref(),
|
||||
self.mixnet_contract_address.clone(),
|
||||
self.vesting_contract_address.clone(),
|
||||
self.erc20_bridge_contract_address.clone(),
|
||||
self.mnemonic.clone().unwrap(),
|
||||
None,
|
||||
)?;
|
||||
)?
|
||||
.with_mixnet_contract_address(self.mixnet_contract_address.clone())
|
||||
.with_vesting_contract_address(self.vesting_contract_address.clone())
|
||||
.with_bandwidth_claim_contract_address(self.bandwidth_claim_contract_address.clone());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -166,32 +185,15 @@ impl Client<SigningNymdClient> {
|
||||
impl Client<QueryNymdClient> {
|
||||
pub fn new_query(config: Config) -> Result<Client<QueryNymdClient>, ValidatorClientError> {
|
||||
let validator_api_client = validator_api::Client::new(config.api_url.clone());
|
||||
let nymd_client = NymdClient::connect(
|
||||
config.nymd_url.as_str(),
|
||||
Some(config.mixnet_contract_address.clone().unwrap_or_else(|| {
|
||||
cosmrs::AccountId::from_str(DEFAULT_NETWORK.mixnet_contract_address()).unwrap()
|
||||
})),
|
||||
Some(config.vesting_contract_address.clone().unwrap_or_else(|| {
|
||||
cosmrs::AccountId::from_str(DEFAULT_NETWORK.vesting_contract_address()).unwrap()
|
||||
})),
|
||||
Some(
|
||||
config
|
||||
.erc20_bridge_contract_address
|
||||
.clone()
|
||||
.unwrap_or_else(|| {
|
||||
cosmrs::AccountId::from_str(
|
||||
DEFAULT_NETWORK.bandwidth_claim_contract_address(),
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
),
|
||||
)?;
|
||||
let nymd_client = NymdClient::connect(config.nymd_url.as_str())?
|
||||
.with_mixnet_contract_address(config.mixnet_contract_address.clone())
|
||||
.with_vesting_contract_address(config.vesting_contract_address.clone());
|
||||
|
||||
Ok(Client {
|
||||
network: config.network,
|
||||
mixnet_contract_address: config.mixnet_contract_address,
|
||||
vesting_contract_address: config.vesting_contract_address,
|
||||
erc20_bridge_contract_address: config.erc20_bridge_contract_address,
|
||||
bandwidth_claim_contract_address: config.bandwidth_claim_contract_address,
|
||||
mnemonic: None,
|
||||
mixnode_page_limit: config.mixnode_page_limit,
|
||||
gateway_page_limit: config.gateway_page_limit,
|
||||
@@ -203,12 +205,10 @@ impl Client<QueryNymdClient> {
|
||||
}
|
||||
|
||||
pub fn change_nymd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> {
|
||||
self.nymd = NymdClient::connect(
|
||||
new_endpoint.as_ref(),
|
||||
self.mixnet_contract_address.clone(),
|
||||
self.vesting_contract_address.clone(),
|
||||
self.erc20_bridge_contract_address.clone(),
|
||||
)?;
|
||||
self.nymd = NymdClient::connect(new_endpoint.as_ref())?
|
||||
.with_mixnet_contract_address(self.mixnet_contract_address.clone())
|
||||
.with_vesting_contract_address(self.vesting_contract_address.clone())
|
||||
.with_bandwidth_claim_contract_address(self.bandwidth_claim_contract_address.clone());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -222,10 +222,10 @@ impl<C> Client<C> {
|
||||
// use case: somebody initialised client without a contract in order to upload and initialise one
|
||||
// and now they want to actually use it without making new client
|
||||
pub fn set_mixnet_contract_address(&mut self, mixnet_contract_address: cosmrs::AccountId) {
|
||||
self.mixnet_contract_address = Some(mixnet_contract_address)
|
||||
self.mixnet_contract_address = mixnet_contract_address
|
||||
}
|
||||
|
||||
pub fn get_mixnet_contract_address(&self) -> Option<cosmrs::AccountId> {
|
||||
pub fn get_mixnet_contract_address(&self) -> cosmrs::AccountId {
|
||||
self.mixnet_contract_address.clone()
|
||||
}
|
||||
|
||||
@@ -233,18 +233,36 @@ impl<C> Client<C> {
|
||||
Ok(self.validator_api.get_mixnodes().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_mixnodes_detailed(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_mixnodes_detailed().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_rewarded_mixnodes(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_rewarded_mixnodes().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_rewarded_mixnodes_detailed(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_rewarded_mixnodes_detailed().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_active_mixnodes(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_active_mixnodes().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_active_mixnodes_detailed(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_active_mixnodes_detailed().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_gateways().await?)
|
||||
}
|
||||
@@ -715,4 +733,34 @@ impl ApiClient {
|
||||
) -> Result<VerificationKeyResponse, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_coconut_verification_key().await?)
|
||||
}
|
||||
|
||||
pub async fn verify_bandwidth_credential(
|
||||
&self,
|
||||
request_body: &VerifyCredentialBody,
|
||||
) -> Result<VerifyCredentialResponse, ValidatorClientError> {
|
||||
Ok(self
|
||||
.validator_api
|
||||
.verify_bandwidth_credential(request_body)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn propose_release_funds(
|
||||
&self,
|
||||
request_body: &ProposeReleaseFundsRequestBody,
|
||||
) -> Result<ProposeReleaseFundsResponse, ValidatorClientError> {
|
||||
Ok(self
|
||||
.validator_api
|
||||
.propose_release_funds(request_body)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn execute_release_funds(
|
||||
&self,
|
||||
request_body: &ExecuteReleaseFundsRequestBody,
|
||||
) -> Result<(), ValidatorClientError> {
|
||||
Ok(self
|
||||
.validator_api
|
||||
.execute_release_funds(request_body)
|
||||
.await?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ const CONNECTION_TEST_TIMEOUT_SEC: u64 = 2;
|
||||
pub async fn run_validator_connection_test<H: BuildHasher + 'static>(
|
||||
nymd_urls: impl Iterator<Item = (Network, Url)>,
|
||||
api_urls: impl Iterator<Item = (Network, Url)>,
|
||||
mixnet_contract_address: HashMap<Network, Option<cosmrs::AccountId>, H>,
|
||||
mixnet_contract_address: HashMap<Network, cosmrs::AccountId, H>,
|
||||
) -> (
|
||||
HashMap<Network, Vec<(Url, bool)>>,
|
||||
HashMap<Network, Vec<(Url, bool)>>,
|
||||
@@ -47,14 +47,15 @@ pub async fn run_validator_connection_test<H: BuildHasher + 'static>(
|
||||
fn setup_connection_tests<H: BuildHasher + 'static>(
|
||||
nymd_urls: impl Iterator<Item = (Network, Url)>,
|
||||
api_urls: impl Iterator<Item = (Network, Url)>,
|
||||
mixnet_contract_address: HashMap<Network, Option<cosmrs::AccountId>, H>,
|
||||
mixnet_contract_address: HashMap<Network, cosmrs::AccountId, H>,
|
||||
) -> impl Iterator<Item = ClientForConnectionTest> {
|
||||
let nymd_connection_test_clients = nymd_urls.filter_map(move |(network, url)| {
|
||||
let address = mixnet_contract_address
|
||||
.get(&network)
|
||||
.expect("No configured contract address")
|
||||
.clone();
|
||||
NymdClient::<QueryNymdClient>::connect(url.as_str(), address, None, None)
|
||||
NymdClient::<QueryNymdClient>::connect(url.as_str())
|
||||
.map(|client| client.with_mixnet_contract_address(address))
|
||||
.map(move |client| ClientForConnectionTest::Nymd(network, url, Box::new(client)))
|
||||
.ok()
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@ pub struct Log {
|
||||
|
||||
/// Searches in logs for the first event of the given event type and in that event
|
||||
/// for the first attribute with the given attribute key.
|
||||
pub(crate) fn find_attribute<'a>(
|
||||
pub fn find_attribute<'a>(
|
||||
logs: &'a [Log],
|
||||
event_type: &str,
|
||||
attribute_key: &str,
|
||||
|
||||
@@ -124,6 +124,12 @@ pub enum NymdError {
|
||||
|
||||
#[error("Transaction with ID {hash} has been submitted but not yet found on the chain. You might want to check for it later. There was a total wait of {} seconds", .timeout.as_secs())]
|
||||
BroadcastTimeout { hash: tx::Hash, timeout: Duration },
|
||||
|
||||
#[error("Cosmwasm std error: {0}")]
|
||||
CosmwasmStdError(#[from] cosmwasm_std::StdError),
|
||||
|
||||
#[error("Coconut interface error: {0}")]
|
||||
CoconutInterfaceError(#[from] coconut_interface::error::CoconutInterfaceError),
|
||||
}
|
||||
|
||||
impl NymdError {
|
||||
|
||||
@@ -14,6 +14,7 @@ use cosmrs::rpc::Error as TendermintRpcError;
|
||||
use cosmrs::rpc::HttpClientUrl;
|
||||
use cosmrs::tx::Msg;
|
||||
use cosmwasm_std::Uint128;
|
||||
use execute::execute;
|
||||
pub use fee::gas_price::GasPrice;
|
||||
use mixnet_contract_common::mixnode::DelegationEvent;
|
||||
use mixnet_contract_common::{
|
||||
@@ -23,8 +24,10 @@ use mixnet_contract_common::{
|
||||
PagedGatewayResponse, PagedMixDelegationsResponse, PagedMixnodeResponse,
|
||||
PagedRewardedSetResponse, QueryMsg, RewardedSetUpdateDetails,
|
||||
};
|
||||
use network_defaults::DEFAULT_NETWORK;
|
||||
use serde::Serialize;
|
||||
use std::convert::TryInto;
|
||||
use vesting_contract_common::ExecuteMsg as VestingExecuteMsg;
|
||||
|
||||
pub use crate::nymd::cosmwasm_client::client::CosmWasmClient;
|
||||
pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient;
|
||||
@@ -58,30 +61,64 @@ pub mod wallet;
|
||||
#[derive(Debug)]
|
||||
pub struct NymdClient<C> {
|
||||
client: C,
|
||||
mixnet_contract_address: Option<AccountId>,
|
||||
vesting_contract_address: Option<AccountId>,
|
||||
erc20_bridge_contract_address: Option<AccountId>,
|
||||
mixnet_contract_address: AccountId,
|
||||
vesting_contract_address: AccountId,
|
||||
bandwidth_claim_contract_address: AccountId,
|
||||
coconut_bandwidth_contract_address: AccountId,
|
||||
multisig_contract_address: AccountId,
|
||||
client_address: Option<Vec<AccountId>>,
|
||||
simulated_gas_multiplier: f32,
|
||||
}
|
||||
|
||||
impl<C> NymdClient<C> {
|
||||
pub fn with_mixnet_contract_address(mut self, address: AccountId) -> Self {
|
||||
self.mixnet_contract_address = address;
|
||||
self
|
||||
}
|
||||
pub fn with_vesting_contract_address(mut self, address: AccountId) -> Self {
|
||||
self.vesting_contract_address = address;
|
||||
self
|
||||
}
|
||||
pub fn with_bandwidth_claim_contract_address(mut self, address: AccountId) -> Self {
|
||||
self.bandwidth_claim_contract_address = address;
|
||||
self
|
||||
}
|
||||
pub fn with_coconut_bandwidth_contract_address(mut self, address: AccountId) -> Self {
|
||||
self.coconut_bandwidth_contract_address = address;
|
||||
self
|
||||
}
|
||||
pub fn with_multisig_contract_address(mut self, address: AccountId) -> Self {
|
||||
self.multisig_contract_address = address;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NymdClient<QueryNymdClient> {
|
||||
pub fn connect<U>(
|
||||
endpoint: U,
|
||||
mixnet_contract_address: Option<AccountId>,
|
||||
vesting_contract_address: Option<AccountId>,
|
||||
erc20_bridge_contract_address: Option<AccountId>,
|
||||
) -> Result<NymdClient<QueryNymdClient>, NymdError>
|
||||
pub fn connect<U>(endpoint: U) -> Result<NymdClient<QueryNymdClient>, NymdError>
|
||||
where
|
||||
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
|
||||
{
|
||||
Ok(NymdClient {
|
||||
client: QueryNymdClient::new(endpoint)?,
|
||||
mixnet_contract_address,
|
||||
vesting_contract_address,
|
||||
erc20_bridge_contract_address,
|
||||
client_address: None,
|
||||
simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER,
|
||||
mixnet_contract_address: DEFAULT_NETWORK
|
||||
.mixnet_contract_address()
|
||||
.parse()
|
||||
.expect("Error parsing mixnet contract address"),
|
||||
vesting_contract_address: DEFAULT_NETWORK
|
||||
.vesting_contract_address()
|
||||
.parse()
|
||||
.expect("Error parsing vesting contract address"),
|
||||
bandwidth_claim_contract_address: DEFAULT_NETWORK
|
||||
.bandwidth_claim_contract_address()
|
||||
.parse()
|
||||
.expect("Error parsing bandwidth claim contract address"),
|
||||
coconut_bandwidth_contract_address: DEFAULT_NETWORK
|
||||
.coconut_bandwidth_contract_address()
|
||||
.parse()
|
||||
.unwrap(),
|
||||
multisig_contract_address: DEFAULT_NETWORK.multisig_contract_address().parse().unwrap(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -91,9 +128,6 @@ impl NymdClient<SigningNymdClient> {
|
||||
pub fn connect_with_signer<U: Clone>(
|
||||
network: config::defaults::all::Network,
|
||||
endpoint: U,
|
||||
mixnet_contract_address: Option<AccountId>,
|
||||
vesting_contract_address: Option<AccountId>,
|
||||
erc20_bridge_contract_address: Option<AccountId>,
|
||||
signer: DirectSecp256k1HdWallet,
|
||||
gas_price: Option<GasPrice>,
|
||||
) -> Result<NymdClient<SigningNymdClient>, NymdError>
|
||||
@@ -110,20 +144,31 @@ impl NymdClient<SigningNymdClient> {
|
||||
|
||||
Ok(NymdClient {
|
||||
client: SigningNymdClient::connect_with_signer(endpoint, signer, gas_price)?,
|
||||
mixnet_contract_address,
|
||||
vesting_contract_address,
|
||||
erc20_bridge_contract_address,
|
||||
client_address: Some(client_address),
|
||||
simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER,
|
||||
mixnet_contract_address: DEFAULT_NETWORK
|
||||
.mixnet_contract_address()
|
||||
.parse()
|
||||
.expect("Error parsing mixnet contract address"),
|
||||
vesting_contract_address: DEFAULT_NETWORK
|
||||
.vesting_contract_address()
|
||||
.parse()
|
||||
.expect("Error parsing vesting contract address"),
|
||||
bandwidth_claim_contract_address: DEFAULT_NETWORK
|
||||
.bandwidth_claim_contract_address()
|
||||
.parse()
|
||||
.expect("Error parsing bandwidth claim contract address"),
|
||||
coconut_bandwidth_contract_address: DEFAULT_NETWORK
|
||||
.coconut_bandwidth_contract_address()
|
||||
.parse()
|
||||
.unwrap(),
|
||||
multisig_contract_address: DEFAULT_NETWORK.multisig_contract_address().parse().unwrap(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn connect_with_mnemonic<U: Clone>(
|
||||
network: config::defaults::all::Network,
|
||||
endpoint: U,
|
||||
mixnet_contract_address: Option<AccountId>,
|
||||
vesting_contract_address: Option<AccountId>,
|
||||
erc20_bridge_contract_address: Option<AccountId>,
|
||||
mnemonic: bip39::Mnemonic,
|
||||
gas_price: Option<GasPrice>,
|
||||
) -> Result<NymdClient<SigningNymdClient>, NymdError>
|
||||
@@ -142,32 +187,48 @@ impl NymdClient<SigningNymdClient> {
|
||||
|
||||
Ok(NymdClient {
|
||||
client: SigningNymdClient::connect_with_signer(endpoint, wallet, gas_price)?,
|
||||
mixnet_contract_address,
|
||||
vesting_contract_address,
|
||||
erc20_bridge_contract_address,
|
||||
client_address: Some(client_address),
|
||||
simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER,
|
||||
mixnet_contract_address: DEFAULT_NETWORK
|
||||
.mixnet_contract_address()
|
||||
.parse()
|
||||
.expect("Error parsing mixnet contract address"),
|
||||
vesting_contract_address: DEFAULT_NETWORK
|
||||
.vesting_contract_address()
|
||||
.parse()
|
||||
.expect("Error parsing vesting contract address"),
|
||||
bandwidth_claim_contract_address: DEFAULT_NETWORK
|
||||
.bandwidth_claim_contract_address()
|
||||
.parse()
|
||||
.expect("Error parsing bandwidth claim contract address"),
|
||||
coconut_bandwidth_contract_address: DEFAULT_NETWORK
|
||||
.coconut_bandwidth_contract_address()
|
||||
.parse()
|
||||
.unwrap(),
|
||||
multisig_contract_address: DEFAULT_NETWORK.multisig_contract_address().parse().unwrap(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> NymdClient<C> {
|
||||
pub fn mixnet_contract_address(&self) -> Result<&AccountId, NymdError> {
|
||||
self.mixnet_contract_address
|
||||
.as_ref()
|
||||
.ok_or(NymdError::NoContractAddressAvailable)
|
||||
pub fn mixnet_contract_address(&self) -> &AccountId {
|
||||
&self.mixnet_contract_address
|
||||
}
|
||||
|
||||
pub fn vesting_contract_address(&self) -> Result<&AccountId, NymdError> {
|
||||
self.vesting_contract_address
|
||||
.as_ref()
|
||||
.ok_or(NymdError::NoContractAddressAvailable)
|
||||
pub fn vesting_contract_address(&self) -> &AccountId {
|
||||
&self.vesting_contract_address
|
||||
}
|
||||
|
||||
pub fn erc20_bridge_contract_address(&self) -> Result<&AccountId, NymdError> {
|
||||
self.erc20_bridge_contract_address
|
||||
.as_ref()
|
||||
.ok_or(NymdError::NoContractAddressAvailable)
|
||||
pub fn bandwidth_claim_contract_address(&self) -> &AccountId {
|
||||
&self.bandwidth_claim_contract_address
|
||||
}
|
||||
|
||||
pub fn coconut_bandwidth_contract_address(&self) -> &AccountId {
|
||||
&self.coconut_bandwidth_contract_address
|
||||
}
|
||||
|
||||
pub fn multisig_contract_address(&self) -> &AccountId {
|
||||
&self.multisig_contract_address
|
||||
}
|
||||
|
||||
pub fn set_simulated_gas_multiplier(&mut self, multiplier: f32) {
|
||||
@@ -305,7 +366,7 @@ impl<C> NymdClient<C> {
|
||||
{
|
||||
let request = QueryMsg::StateParams {};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -315,7 +376,7 @@ impl<C> NymdClient<C> {
|
||||
{
|
||||
let request = QueryMsg::QueryOperatorReward { address };
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -334,7 +395,7 @@ impl<C> NymdClient<C> {
|
||||
proxy,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -351,7 +412,7 @@ impl<C> NymdClient<C> {
|
||||
proxy_address,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -361,7 +422,7 @@ impl<C> NymdClient<C> {
|
||||
{
|
||||
let request = QueryMsg::GetCurrentEpoch {};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -371,7 +432,7 @@ impl<C> NymdClient<C> {
|
||||
{
|
||||
let request = QueryMsg::GetContractVersion {};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -388,7 +449,7 @@ impl<C> NymdClient<C> {
|
||||
interval_id,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -398,7 +459,7 @@ impl<C> NymdClient<C> {
|
||||
{
|
||||
let request = QueryMsg::GetCurrentRewardedSetHeight {};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -410,7 +471,7 @@ impl<C> NymdClient<C> {
|
||||
{
|
||||
let request = QueryMsg::GetRewardedSetUpdateDetails {};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -430,7 +491,7 @@ impl<C> NymdClient<C> {
|
||||
};
|
||||
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -440,7 +501,7 @@ impl<C> NymdClient<C> {
|
||||
{
|
||||
let request = QueryMsg::LayerDistribution {};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -450,7 +511,7 @@ impl<C> NymdClient<C> {
|
||||
{
|
||||
let request = QueryMsg::GetRewardPool {};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -460,7 +521,7 @@ impl<C> NymdClient<C> {
|
||||
{
|
||||
let request = QueryMsg::GetCirculatingSupply {};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -470,7 +531,7 @@ impl<C> NymdClient<C> {
|
||||
{
|
||||
let request = QueryMsg::GetSybilResistancePercent {};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -480,7 +541,7 @@ impl<C> NymdClient<C> {
|
||||
{
|
||||
let request = QueryMsg::GetActiveSetWorkFactor {};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -490,7 +551,7 @@ impl<C> NymdClient<C> {
|
||||
{
|
||||
let request = QueryMsg::GetIntervalRewardPercent {};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -500,7 +561,7 @@ impl<C> NymdClient<C> {
|
||||
{
|
||||
let request = QueryMsg::GetEpochsInInterval {};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -514,7 +575,7 @@ impl<C> NymdClient<C> {
|
||||
};
|
||||
let response: MixOwnershipResponse = self
|
||||
.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await?;
|
||||
Ok(response.mixnode)
|
||||
}
|
||||
@@ -529,7 +590,7 @@ impl<C> NymdClient<C> {
|
||||
};
|
||||
let response: GatewayOwnershipResponse = self
|
||||
.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await?;
|
||||
Ok(response.gateway)
|
||||
}
|
||||
@@ -547,7 +608,7 @@ impl<C> NymdClient<C> {
|
||||
limit: page_limit,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -564,7 +625,7 @@ impl<C> NymdClient<C> {
|
||||
limit: page_limit,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -584,7 +645,7 @@ impl<C> NymdClient<C> {
|
||||
limit: page_limit,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -604,7 +665,7 @@ impl<C> NymdClient<C> {
|
||||
limit: page_limit,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -624,7 +685,7 @@ impl<C> NymdClient<C> {
|
||||
proxy,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.query_contract_smart(self.mixnet_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -799,87 +860,93 @@ impl<C> NymdClient<C> {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn compound_operator_reward(
|
||||
&self,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>
|
||||
#[execute("mixnet")]
|
||||
fn _compound_operator_reward(&self, fee: Option<Fee>) -> (ExecuteMsg, Option<Fee>)
|
||||
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
|
||||
(ExecuteMsg::CompoundOperatorReward {}, fee)
|
||||
}
|
||||
|
||||
pub async fn claim_operator_reward(&self, fee: Option<Fee>) -> Result<ExecuteResult, NymdError>
|
||||
#[execute("mixnet")]
|
||||
fn _claim_operator_reward(&self, fee: Option<Fee>) -> (ExecuteMsg, Option<Fee>)
|
||||
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
|
||||
(ExecuteMsg::ClaimOperatorReward {}, fee)
|
||||
}
|
||||
|
||||
pub async fn compound_delegator_reward(
|
||||
#[execute("mixnet")]
|
||||
fn _compound_delegator_reward(
|
||||
&self,
|
||||
mix_identity: IdentityKey,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>
|
||||
) -> (ExecuteMsg, Option<Fee>)
|
||||
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
|
||||
(ExecuteMsg::CompoundDelegatorReward { mix_identity }, fee)
|
||||
}
|
||||
|
||||
pub async fn claim_delegator_reward(
|
||||
#[execute("mixnet")]
|
||||
fn _claim_delegator_reward(
|
||||
&self,
|
||||
mix_identity: IdentityKey,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>
|
||||
) -> (ExecuteMsg, Option<Fee>)
|
||||
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
|
||||
(ExecuteMsg::ClaimDelegatorReward { mix_identity }, fee)
|
||||
}
|
||||
|
||||
#[execute("vesting")]
|
||||
fn _vesting_compound_delegator_reward(
|
||||
&self,
|
||||
mix_identity: IdentityKey,
|
||||
fee: Option<Fee>,
|
||||
) -> (VestingExecuteMsg, Option<Fee>)
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
{
|
||||
(
|
||||
VestingExecuteMsg::CompoundDelegatorReward { mix_identity },
|
||||
fee,
|
||||
)
|
||||
}
|
||||
|
||||
#[execute("vesting")]
|
||||
fn _vesting_claim_operator_reward(&self, fee: Option<Fee>) -> (VestingExecuteMsg, Option<Fee>)
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
{
|
||||
(VestingExecuteMsg::ClaimOperatorReward {}, fee)
|
||||
}
|
||||
|
||||
#[execute("vesting")]
|
||||
fn _vesting_compound_operator_reward(
|
||||
&self,
|
||||
fee: Option<Fee>,
|
||||
) -> (VestingExecuteMsg, Option<Fee>)
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
{
|
||||
(VestingExecuteMsg::CompoundOperatorReward {}, fee)
|
||||
}
|
||||
|
||||
#[execute("vesting")]
|
||||
fn _vesting_claim_delegator_reward(
|
||||
&self,
|
||||
mix_identity: IdentityKey,
|
||||
fee: Option<Fee>,
|
||||
) -> (VestingExecuteMsg, Option<Fee>)
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
{
|
||||
(
|
||||
VestingExecuteMsg::ClaimDelegatorReward { mix_identity },
|
||||
fee,
|
||||
)
|
||||
}
|
||||
|
||||
/// Announce a mixnode, paying a fee.
|
||||
@@ -902,7 +969,7 @@ impl<C> NymdClient<C> {
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
self.mixnet_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"Bonding mixnode from rust!",
|
||||
@@ -933,7 +1000,7 @@ impl<C> NymdClient<C> {
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
self.mixnet_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"Bonding mixnode on behalf from rust!",
|
||||
@@ -970,7 +1037,7 @@ impl<C> NymdClient<C> {
|
||||
self.client
|
||||
.execute_multiple(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
self.mixnet_contract_address(),
|
||||
reqs,
|
||||
fee,
|
||||
"Bonding multiple mixnodes on behalf from rust!",
|
||||
@@ -989,7 +1056,7 @@ impl<C> NymdClient<C> {
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
self.mixnet_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"Unbonding mixnode from rust!",
|
||||
@@ -1013,7 +1080,7 @@ impl<C> NymdClient<C> {
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
self.mixnet_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"Unbonding mixnode on behalf from rust!",
|
||||
@@ -1039,7 +1106,7 @@ impl<C> NymdClient<C> {
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
self.mixnet_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"Updating mixnode configuration from rust!",
|
||||
@@ -1066,7 +1133,7 @@ impl<C> NymdClient<C> {
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
self.mixnet_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"Delegating to mixnode from rust!",
|
||||
@@ -1096,7 +1163,7 @@ impl<C> NymdClient<C> {
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
self.mixnet_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"Delegating to mixnode on behalf from rust!",
|
||||
@@ -1132,7 +1199,7 @@ impl<C> NymdClient<C> {
|
||||
self.client
|
||||
.execute_multiple(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
self.mixnet_contract_address(),
|
||||
reqs,
|
||||
fee,
|
||||
"Delegating to multiple mixnodes on behalf from rust!",
|
||||
@@ -1157,7 +1224,7 @@ impl<C> NymdClient<C> {
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
self.mixnet_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"Removing mixnode delegation from rust!",
|
||||
@@ -1185,7 +1252,7 @@ impl<C> NymdClient<C> {
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
self.mixnet_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"Removing mixnode delegation on behalf from rust!",
|
||||
@@ -1214,7 +1281,7 @@ impl<C> NymdClient<C> {
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
self.mixnet_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"Bonding gateway from rust!",
|
||||
@@ -1245,7 +1312,7 @@ impl<C> NymdClient<C> {
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
self.mixnet_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"Bonding gateway on behalf from rust!",
|
||||
@@ -1282,7 +1349,7 @@ impl<C> NymdClient<C> {
|
||||
self.client
|
||||
.execute_multiple(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
self.mixnet_contract_address(),
|
||||
reqs,
|
||||
fee,
|
||||
"Bonding multiple gateways on behalf from rust!",
|
||||
@@ -1301,7 +1368,7 @@ impl<C> NymdClient<C> {
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
self.mixnet_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"Unbonding gateway from rust!",
|
||||
@@ -1326,7 +1393,7 @@ impl<C> NymdClient<C> {
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
self.mixnet_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"Unbonding gateway on behalf from rust!",
|
||||
@@ -1349,7 +1416,7 @@ impl<C> NymdClient<C> {
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
self.mixnet_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"Updating contract state from rust!",
|
||||
@@ -1368,7 +1435,7 @@ impl<C> NymdClient<C> {
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
self.mixnet_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"Advance current epoch",
|
||||
@@ -1387,7 +1454,7 @@ impl<C> NymdClient<C> {
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
self.mixnet_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"Reconciling delegation events",
|
||||
@@ -1406,7 +1473,7 @@ impl<C> NymdClient<C> {
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
self.mixnet_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"Snapshotting mixnodes",
|
||||
@@ -1433,7 +1500,7 @@ impl<C> NymdClient<C> {
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
self.mixnet_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"Writing rewarded set",
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient;
|
||||
use crate::nymd::cosmwasm_client::types::ExecuteResult;
|
||||
use crate::nymd::error::NymdError;
|
||||
use crate::nymd::{Coin, Fee, NymdClient};
|
||||
use coconut_bandwidth_contract_common::{deposit::DepositData, msg::ExecuteMsg};
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
#[async_trait]
|
||||
pub trait CoconutBandwidthSigningClient {
|
||||
async fn deposit(
|
||||
&self,
|
||||
amount: Coin,
|
||||
info: String,
|
||||
verification_key: String,
|
||||
encryption_key: String,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<C: SigningCosmWasmClient + Sync + Send> CoconutBandwidthSigningClient for NymdClient<C> {
|
||||
async fn deposit(
|
||||
&self,
|
||||
amount: Coin,
|
||||
info: String,
|
||||
verification_key: String,
|
||||
encryption_key: String,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let req = ExecuteMsg::DepositFunds {
|
||||
data: DepositData::new(info.to_string(), verification_key, encryption_key),
|
||||
};
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.coconut_bandwidth_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"CoconutBandwidth::Deposit",
|
||||
vec![amount],
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
mod coconut_bandwidth_signing_client;
|
||||
mod multisig_query_client;
|
||||
mod multisig_signing_client;
|
||||
mod vesting_query_client;
|
||||
mod vesting_signing_client;
|
||||
|
||||
pub use coconut_bandwidth_signing_client::CoconutBandwidthSigningClient;
|
||||
pub use multisig_query_client::QueryClient;
|
||||
pub use multisig_signing_client::MultisigSigningClient;
|
||||
pub use vesting_query_client::VestingQueryClient;
|
||||
pub use vesting_signing_client::VestingSigningClient;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::nymd::error::NymdError;
|
||||
use crate::nymd::{CosmWasmClient, NymdClient};
|
||||
|
||||
use multisig_contract_common::msg::{ProposalResponse, QueryMsg};
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
#[async_trait]
|
||||
pub trait QueryClient {
|
||||
async fn get_proposal(&self, proposal_id: u64) -> Result<ProposalResponse, NymdError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<C: CosmWasmClient + Sync + Send> QueryClient for NymdClient<C> {
|
||||
async fn get_proposal(&self, proposal_id: u64) -> Result<ProposalResponse, NymdError> {
|
||||
let request = QueryMsg::Proposal { proposal_id };
|
||||
self.client
|
||||
.query_contract_smart(self.multisig_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient;
|
||||
use crate::nymd::cosmwasm_client::types::ExecuteResult;
|
||||
use crate::nymd::error::NymdError;
|
||||
use crate::nymd::{Fee, NymdClient};
|
||||
|
||||
use coconut_bandwidth_contract_common::msg::ExecuteMsg as CoconutBandwidthExecuteMsg;
|
||||
use multisig_contract_common::msg::ExecuteMsg;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use cosmwasm_std::{to_binary, Coin, CosmosMsg, WasmMsg};
|
||||
use cw3::Vote;
|
||||
use network_defaults::DEFAULT_NETWORK;
|
||||
|
||||
#[async_trait]
|
||||
pub trait MultisigSigningClient {
|
||||
async fn propose_release_funds(
|
||||
&self,
|
||||
title: String,
|
||||
blinded_serial_number: String,
|
||||
voucher_value: u128,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>;
|
||||
|
||||
async fn vote_proposal(
|
||||
&self,
|
||||
proposal_id: u64,
|
||||
yes: bool,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>;
|
||||
|
||||
async fn execute_proposal(
|
||||
&self,
|
||||
proposal_id: u64,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<C: SigningCosmWasmClient + Sync + Send> MultisigSigningClient for NymdClient<C> {
|
||||
async fn propose_release_funds(
|
||||
&self,
|
||||
title: String,
|
||||
blinded_serial_number: String,
|
||||
voucher_value: u128,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let release_funds_req = CoconutBandwidthExecuteMsg::ReleaseFunds {
|
||||
funds: Coin::new(voucher_value, DEFAULT_NETWORK.mix_denom().base),
|
||||
};
|
||||
let release_funds_msg = CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: self.coconut_bandwidth_contract_address().to_string(),
|
||||
msg: to_binary(&release_funds_req)?,
|
||||
funds: vec![],
|
||||
});
|
||||
let req = ExecuteMsg::Propose {
|
||||
title,
|
||||
description: blinded_serial_number,
|
||||
msgs: vec![release_funds_msg],
|
||||
latest: None,
|
||||
};
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.multisig_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"Multisig::Propose::Execute::ReleaseFunds",
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn vote_proposal(
|
||||
&self,
|
||||
proposal_id: u64,
|
||||
vote_yes: bool,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let vote = if vote_yes { Vote::Yes } else { Vote::No };
|
||||
let req = ExecuteMsg::Vote { proposal_id, vote };
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.multisig_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"Multisig::Vote",
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn execute_proposal(
|
||||
&self,
|
||||
proposal_id: u64,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let req = ExecuteMsg::Execute { proposal_id };
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.multisig_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"Multisig::Execute",
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,7 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
block_time,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address()?, &request)
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
@@ -99,7 +99,7 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
block_time,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address()?, &request)
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
@@ -113,7 +113,7 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
block_time,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address()?, &request)
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
@@ -127,7 +127,7 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
block_time,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address()?, &request)
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
@@ -140,7 +140,7 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address()?, &request)
|
||||
.query_contract_smart(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address()?, &request)
|
||||
.query_contract_smart(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address()?, &request)
|
||||
.query_contract_smart(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
block_time,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address()?, &request)
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
@@ -194,7 +194,7 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
block_time,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address()?, &request)
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
@@ -204,7 +204,7 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
address: address.to_string(),
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address()?, &request)
|
||||
.query_contract_smart(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
async fn get_mixnode_pledge(&self, address: &str) -> Result<Option<PledgeData>, NymdError> {
|
||||
@@ -212,7 +212,7 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
address: address.to_string(),
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address()?, &request)
|
||||
.query_contract_smart(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
async fn get_gateway_pledge(&self, address: &str) -> Result<Option<PledgeData>, NymdError> {
|
||||
@@ -220,7 +220,7 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
address: address.to_string(),
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address()?, &request)
|
||||
.query_contract_smart(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
address: address.to_string(),
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address()?, &request)
|
||||
.query_contract_smart(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,28 +11,6 @@ use vesting_contract_common::messages::{ExecuteMsg as VestingExecuteMsg, Vesting
|
||||
|
||||
#[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,
|
||||
@@ -129,7 +107,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address()?,
|
||||
self.vesting_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::UpdateMixnetConfig",
|
||||
@@ -150,7 +128,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address()?,
|
||||
self.vesting_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::UpdateMixnetAddress",
|
||||
@@ -175,7 +153,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address()?,
|
||||
self.vesting_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::BondGateway",
|
||||
@@ -190,7 +168,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address()?,
|
||||
self.vesting_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::UnbondGateway",
|
||||
@@ -213,7 +191,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address()?,
|
||||
self.vesting_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::TrackUnbondGateway",
|
||||
@@ -238,7 +216,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address()?,
|
||||
self.vesting_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::BondMixnode",
|
||||
@@ -253,7 +231,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address()?,
|
||||
self.vesting_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::UnbondMixnode",
|
||||
@@ -276,7 +254,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address()?,
|
||||
self.vesting_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::TrackUnbondMixnode",
|
||||
@@ -296,7 +274,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address()?,
|
||||
self.vesting_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::WithdrawVested",
|
||||
@@ -320,7 +298,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address()?,
|
||||
self.vesting_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::TrackUndelegation",
|
||||
@@ -342,7 +320,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address()?,
|
||||
self.vesting_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::DelegateToMixnode",
|
||||
@@ -363,7 +341,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address()?,
|
||||
self.vesting_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::UndelegateFromMixnode",
|
||||
@@ -389,7 +367,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address()?,
|
||||
self.vesting_contract_address(),
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::CreatePeriodicVestingAccount",
|
||||
@@ -397,78 +375,4 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
)
|
||||
.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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,18 @@
|
||||
|
||||
use crate::validator_api::error::ValidatorAPIError;
|
||||
use crate::validator_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
|
||||
use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse};
|
||||
use coconut_interface::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, ExecuteReleaseFundsRequestBody,
|
||||
ProposeReleaseFundsRequestBody, ProposeReleaseFundsResponse, VerificationKeyResponse,
|
||||
VerifyCredentialBody, VerifyCredentialResponse,
|
||||
};
|
||||
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use url::Url;
|
||||
use validator_api_requests::models::{
|
||||
CoreNodeStatusResponse, InclusionProbabilityResponse, MixnodeStatusResponse,
|
||||
RewardEstimationResponse, StakeSaturationResponse, UptimeResponse,
|
||||
CoreNodeStatusResponse, InclusionProbabilityResponse, MixNodeBondAnnotated,
|
||||
MixnodeStatusResponse, RewardEstimationResponse, StakeSaturationResponse, UptimeResponse,
|
||||
};
|
||||
|
||||
pub mod error;
|
||||
@@ -85,6 +89,16 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnodes_detailed(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[routes::API_VERSION, routes::MIXNODES, routes::DETAILED],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorAPIError> {
|
||||
self.query_validator_api(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS)
|
||||
.await
|
||||
@@ -98,6 +112,21 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_active_mixnodes_detailed(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::MIXNODES,
|
||||
routes::ACTIVE,
|
||||
routes::DETAILED,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[routes::API_VERSION, routes::MIXNODES, routes::REWARDED],
|
||||
@@ -106,6 +135,21 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_rewarded_mixnodes_detailed(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::MIXNODES,
|
||||
routes::REWARDED,
|
||||
routes::DETAILED,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_probs_mixnode_rewarded(
|
||||
&self,
|
||||
mixnode_id: &str,
|
||||
@@ -331,6 +375,57 @@ impl Client {
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn verify_bandwidth_credential(
|
||||
&self,
|
||||
request_body: &VerifyCredentialBody,
|
||||
) -> Result<VerifyCredentialResponse, ValidatorAPIError> {
|
||||
self.post_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::COCONUT_ROUTES,
|
||||
routes::BANDWIDTH,
|
||||
routes::COCONUT_VERIFY_BANDWIDTH_CREDENTIAL,
|
||||
],
|
||||
NO_PARAMS,
|
||||
request_body,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn propose_release_funds(
|
||||
&self,
|
||||
request_body: &ProposeReleaseFundsRequestBody,
|
||||
) -> Result<ProposeReleaseFundsResponse, ValidatorAPIError> {
|
||||
self.post_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::COCONUT_ROUTES,
|
||||
routes::BANDWIDTH,
|
||||
routes::COCONUT_PROPOSE_RELEASE_FUNDS,
|
||||
],
|
||||
NO_PARAMS,
|
||||
request_body,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn execute_release_funds(
|
||||
&self,
|
||||
request_body: &ExecuteReleaseFundsRequestBody,
|
||||
) -> Result<(), ValidatorAPIError> {
|
||||
self.post_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::COCONUT_ROUTES,
|
||||
routes::BANDWIDTH,
|
||||
routes::COCONUT_EXECUTE_RELEASE_FUNDS,
|
||||
],
|
||||
NO_PARAMS,
|
||||
request_body,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
// utility function that should solve the double slash problem in validator API forever.
|
||||
|
||||
@@ -7,6 +7,7 @@ pub const API_VERSION: &str = VALIDATOR_API_VERSION;
|
||||
pub const MIXNODES: &str = "mixnodes";
|
||||
pub const GATEWAYS: &str = "gateways";
|
||||
|
||||
pub const DETAILED: &str = "detailed";
|
||||
pub const ACTIVE: &str = "active";
|
||||
pub const REWARDED: &str = "rewarded";
|
||||
|
||||
@@ -16,6 +17,9 @@ pub const BANDWIDTH: &str = "bandwidth";
|
||||
pub const COCONUT_BLIND_SIGN: &str = "blind-sign";
|
||||
pub const COCONUT_PARTIAL_BANDWIDTH_CREDENTIAL: &str = "partial-bandwidth-credential";
|
||||
pub const COCONUT_VERIFICATION_KEY: &str = "verification-key";
|
||||
pub const COCONUT_VERIFY_BANDWIDTH_CREDENTIAL: &str = "verify-bandwidth-credential";
|
||||
pub const COCONUT_PROPOSE_RELEASE_FUNDS: &str = "propose-release-funds";
|
||||
pub const COCONUT_EXECUTE_RELEASE_FUNDS: &str = "execute-release-funds";
|
||||
|
||||
pub const STATUS_ROUTES: &str = "status";
|
||||
pub const MIXNODE: &str = "mixnode";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nymcoconut::CoconutError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
@@ -11,9 +12,6 @@ pub enum CoconutInterfaceError {
|
||||
#[error("Could not decode base 58 string - {0}")]
|
||||
MalformedString(#[from] bs58::decode::Error),
|
||||
|
||||
#[error("Not enough public attributes were specified")]
|
||||
NotEnoughPublicAttributes,
|
||||
|
||||
#[error("Could not recover bandwidth value")]
|
||||
InvalidBandwidth,
|
||||
#[error("Coconut error - {0}")]
|
||||
CoconutError(#[from] CoconutError),
|
||||
}
|
||||
|
||||
@@ -5,96 +5,158 @@ pub mod error;
|
||||
|
||||
use getset::{CopyGetters, Getters};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
|
||||
use error::CoconutInterfaceError;
|
||||
|
||||
pub use nymcoconut::*;
|
||||
|
||||
#[derive(Serialize, Deserialize, Getters, CopyGetters, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, Getters, CopyGetters, Clone, PartialEq)]
|
||||
pub struct Credential {
|
||||
#[getset(get = "pub")]
|
||||
n_params: u32,
|
||||
#[getset(get = "pub")]
|
||||
theta: Theta,
|
||||
public_attributes: Vec<Vec<u8>>,
|
||||
#[getset(get = "pub")]
|
||||
signature: Signature,
|
||||
voucher_value: u64,
|
||||
voucher_info: String,
|
||||
}
|
||||
impl Credential {
|
||||
pub fn new(
|
||||
n_params: u32,
|
||||
theta: Theta,
|
||||
voucher_value: String,
|
||||
voucher_value: u64,
|
||||
voucher_info: String,
|
||||
signature: &Signature,
|
||||
) -> Credential {
|
||||
let public_attributes = vec![voucher_value.into_bytes(), voucher_info.into_bytes()];
|
||||
Credential {
|
||||
n_params,
|
||||
theta,
|
||||
public_attributes,
|
||||
signature: *signature,
|
||||
voucher_value,
|
||||
voucher_info,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn voucher_value(&self) -> Result<u64, CoconutInterfaceError> {
|
||||
let bandwidth_vec = self
|
||||
.public_attributes
|
||||
.get(0)
|
||||
.ok_or(CoconutInterfaceError::NotEnoughPublicAttributes)?
|
||||
.to_owned();
|
||||
let bandwidth_str = String::from_utf8(bandwidth_vec)
|
||||
.map_err(|_| CoconutInterfaceError::InvalidBandwidth)?;
|
||||
let value =
|
||||
u64::from_str(&bandwidth_str).map_err(|_| CoconutInterfaceError::InvalidBandwidth)?;
|
||||
pub fn blinded_serial_number(&self) -> String {
|
||||
self.theta.blinded_serial_number_bs58()
|
||||
}
|
||||
|
||||
Ok(value)
|
||||
pub fn has_blinded_serial_number(
|
||||
&self,
|
||||
blinded_serial_number_bs58: &str,
|
||||
) -> Result<bool, CoconutInterfaceError> {
|
||||
Ok(self
|
||||
.theta
|
||||
.has_blinded_serial_number(blinded_serial_number_bs58)?)
|
||||
}
|
||||
|
||||
pub fn voucher_value(&self) -> u64 {
|
||||
self.voucher_value
|
||||
}
|
||||
|
||||
pub fn verify(&self, verification_key: &VerificationKey) -> bool {
|
||||
let params = Parameters::new(self.n_params).unwrap();
|
||||
let public_attributes = self
|
||||
.public_attributes
|
||||
.iter()
|
||||
.map(hash_to_scalar)
|
||||
.collect::<Vec<Attribute>>();
|
||||
let public_attributes = vec![
|
||||
self.voucher_value.to_string().as_bytes(),
|
||||
self.voucher_info.as_bytes(),
|
||||
]
|
||||
.iter()
|
||||
.map(hash_to_scalar)
|
||||
.collect::<Vec<Attribute>>();
|
||||
nymcoconut::verify_credential(¶ms, verification_key, &self.theta, &public_attributes)
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> Vec<u8> {
|
||||
let n_params_bytes = self.n_params.to_be_bytes();
|
||||
let theta_bytes = self.theta.to_bytes();
|
||||
let theta_bytes_len = theta_bytes.len();
|
||||
let voucher_value_bytes = self.voucher_value.to_be_bytes();
|
||||
let voucher_info_bytes = self.voucher_info.as_bytes();
|
||||
let voucher_info_len = voucher_info_bytes.len();
|
||||
|
||||
let mut bytes = Vec::with_capacity(28 + theta_bytes_len + voucher_info_len);
|
||||
bytes.extend_from_slice(&n_params_bytes);
|
||||
bytes.extend_from_slice(&(theta_bytes_len as u64).to_be_bytes());
|
||||
bytes.extend_from_slice(&theta_bytes);
|
||||
bytes.extend_from_slice(&voucher_value_bytes);
|
||||
bytes.extend_from_slice(voucher_info_bytes);
|
||||
|
||||
bytes
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, CoconutError> {
|
||||
if bytes.len() < 28 {
|
||||
return Err(CoconutError::Deserialization(String::from(
|
||||
"To few bytes in credential",
|
||||
)));
|
||||
}
|
||||
let mut four_byte = [0u8; 4];
|
||||
let mut eight_byte = [0u8; 8];
|
||||
|
||||
four_byte.copy_from_slice(&bytes[..4]);
|
||||
let n_params = u32::from_be_bytes(four_byte);
|
||||
eight_byte.copy_from_slice(&bytes[4..12]);
|
||||
let theta_len = u64::from_be_bytes(eight_byte);
|
||||
if bytes.len() < 28 + theta_len as usize {
|
||||
return Err(CoconutError::Deserialization(String::from(
|
||||
"To few bytes in credential",
|
||||
)));
|
||||
}
|
||||
let theta = Theta::from_bytes(&bytes[12..12 + theta_len as usize])
|
||||
.map_err(|e| CoconutError::Deserialization(e.to_string()))?;
|
||||
eight_byte.copy_from_slice(&bytes[12 + theta_len as usize..20 + theta_len as usize]);
|
||||
let voucher_value = u64::from_be_bytes(eight_byte);
|
||||
let voucher_info = String::from_utf8(bytes[20 + theta_len as usize..].to_vec())
|
||||
.map_err(|e| CoconutError::Deserialization(e.to_string()))?;
|
||||
|
||||
Ok(Credential {
|
||||
n_params,
|
||||
theta,
|
||||
voucher_value,
|
||||
voucher_info,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Getters, CopyGetters)]
|
||||
impl Bytable for Credential {
|
||||
fn to_byte_vec(&self) -> Vec<u8> {
|
||||
self.as_bytes()
|
||||
}
|
||||
|
||||
fn try_from_byte_slice(slice: &[u8]) -> Result<Self, CoconutError> {
|
||||
Credential::from_bytes(slice)
|
||||
}
|
||||
}
|
||||
|
||||
impl Base58 for Credential {}
|
||||
|
||||
#[derive(Serialize, Deserialize, Getters, CopyGetters)]
|
||||
pub struct VerifyCredentialBody {
|
||||
#[getset(get = "pub")]
|
||||
n_params: u32,
|
||||
credential: Credential,
|
||||
#[getset(get = "pub")]
|
||||
theta: Theta,
|
||||
public_attributes: Vec<String>,
|
||||
proposal_id: u64,
|
||||
}
|
||||
|
||||
impl VerifyCredentialBody {
|
||||
pub fn new(
|
||||
n_params: u32,
|
||||
theta: &Theta,
|
||||
public_attributes: &[Attribute],
|
||||
) -> VerifyCredentialBody {
|
||||
pub fn new(credential: Credential, proposal_id: u64) -> VerifyCredentialBody {
|
||||
VerifyCredentialBody {
|
||||
n_params,
|
||||
theta: theta.clone(),
|
||||
public_attributes: public_attributes
|
||||
.iter()
|
||||
.map(|attr| attr.to_bs58())
|
||||
.collect(),
|
||||
credential,
|
||||
proposal_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn public_attributes(&self) -> Vec<Attribute> {
|
||||
self.public_attributes
|
||||
.iter()
|
||||
.map(|x| Attribute::try_from_bs58(x).unwrap())
|
||||
.collect()
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct VerifyCredentialResponse {
|
||||
pub verification_result: bool,
|
||||
}
|
||||
|
||||
impl VerifyCredentialResponse {
|
||||
pub fn new(verification_result: bool) -> Self {
|
||||
VerifyCredentialResponse {
|
||||
verification_result,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All strings are base58 encoded representations of structs
|
||||
#[derive(Clone, Serialize, Deserialize, Debug, Getters, CopyGetters)]
|
||||
pub struct BlindSignRequestBody {
|
||||
@@ -194,3 +256,86 @@ impl VerificationKeyResponse {
|
||||
VerificationKeyResponse { key }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Getters, CopyGetters)]
|
||||
pub struct ProposeReleaseFundsRequestBody {
|
||||
#[getset(get = "pub")]
|
||||
credential: Credential,
|
||||
}
|
||||
|
||||
impl ProposeReleaseFundsRequestBody {
|
||||
pub fn new(credential: Credential) -> Self {
|
||||
ProposeReleaseFundsRequestBody { credential }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ProposeReleaseFundsResponse {
|
||||
pub proposal_id: u64,
|
||||
}
|
||||
|
||||
impl ProposeReleaseFundsResponse {
|
||||
pub fn new(proposal_id: u64) -> Self {
|
||||
ProposeReleaseFundsResponse { proposal_id }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Getters, CopyGetters)]
|
||||
pub struct ExecuteReleaseFundsRequestBody {
|
||||
#[getset(get = "pub")]
|
||||
proposal_id: u64,
|
||||
}
|
||||
|
||||
impl ExecuteReleaseFundsRequestBody {
|
||||
pub fn new(proposal_id: u64) -> Self {
|
||||
ExecuteReleaseFundsRequestBody { proposal_id }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serde_coconut_credential() {
|
||||
let voucher_value = 1000000u64;
|
||||
let voucher_info = String::from("BandwidthVoucher");
|
||||
let serial_number =
|
||||
Attribute::try_from_bs58("7Rp3imcuNX3w9se9wm5th8gSvc2czsnMrGsdt5HsrycA").unwrap();
|
||||
let binding_number =
|
||||
Attribute::try_from_bs58("Auf8yVEgyEAWNHaXUZmimS4n9g5YiYnNYqp6F9BtBe9E").unwrap();
|
||||
let signature = Signature::try_from_bs58(
|
||||
"ta3pM9ffj5T6YGbwjSBp2W118rcwyP9PXStc\
|
||||
7ssb91g5GQYMQHhuTNajbdZcjxUFBFL5rhED8EHpRzE8r432ss3qbPBfpNev4CdkfMkQ3wepyM7hy7q1W6Rn9WmFoZL\
|
||||
ZR9j",
|
||||
)
|
||||
.unwrap();
|
||||
let params = Parameters::new(4).unwrap();
|
||||
let verification_key = VerificationKey::try_from_bs58("8CFtVVXdwLy4WHMQPE4\
|
||||
woe89q3DRHoNxBSchftrEjSBPWA4r4xZv4Y9qSvS5x5bMmFtp7BX6ikECAnuXr5EjXWSsgjirZJmpS5XDUynVfht1cD\
|
||||
FWGDvy2XFrRCuoCMotNXi3PoF6wYqdTR9Rqcfoj3i2H5Nid422WBaLtVoC9QNobvpvaqq6vX5PbsSyPayvU8HCXFxM6\
|
||||
JjScYpbRTxQtdwefWLrk3LmXyJQBWi7c2VAhSxu9msp7VTBycqdwQNgxHETStZuwXsozxaGQ2KssVUCaaoYPR4g2RqK\
|
||||
UAvtWwA7pMiAQNcbkXcbsjCgVjWaCpMWC37XA31cLcFf3zbjHD9e5tXjAcqa4M89fbFhuvvSXxowSAZ5NoWrN32kd5d\
|
||||
wxJm1JW3Tt2h6yDDBe84oMy71462dZn7N78DVk2mFNGwBCibrZWA7oUzRBMfYxiQrksoFcou7QfLLd58zoNYmPQPt84\
|
||||
1VpQopEBfdQ7Nf9zoXxBt3zMy7g5NsFGvzh7KTbDUyeeXrdkKJPQBs6dqaizr9sS8CPPmR4uk96vDTRh8CJ5FbSsmb8\
|
||||
nP71dRvvwRZJHGzwYirMo6SXS3ZYxFuiA3mkxYuqDHCwkTWDuRCcAaztrDYRZg7VCMo4Q446AaEso5eqpeWpHZQt53E\
|
||||
ZRpqmNYKASGwMhTeEHPSLgSmtoAAUcaRWpGRzYfd6kzEma8tdGLwyP4rLXgvSvtDLP37dU7YgF3LEXbGAz57U9ATy46\
|
||||
6sroLpHPdaCWB8RF11wvB6Tu196JnJd2KyQBP1iUWP3rtZs3GhAF1QVcxquh8BqDZzAcpQ6wCS1P9c5GxKgww77FVF5\
|
||||
Kp83XtoxSrw3GaYVyKTGxNh3vcKPR31txCjTxPaN2fg7TaPLhoQJX4YaAroFSXqrqbbRsisuHhhCeUP2YwDjHedes9y")
|
||||
.unwrap();
|
||||
let theta = prove_bandwidth_credential(
|
||||
¶ms,
|
||||
&verification_key,
|
||||
&signature,
|
||||
serial_number,
|
||||
binding_number,
|
||||
)
|
||||
.unwrap();
|
||||
let credential = Credential::new(4, theta, voucher_value, voucher_info);
|
||||
|
||||
let serialized_credential = credential.as_bytes();
|
||||
let deserialized_credential = Credential::from_bytes(&serialized_credential).unwrap();
|
||||
|
||||
assert_eq!(credential, deserialized_credential);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "multisig-contract-common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cw-utils = { version = "0.13.1" }
|
||||
cw3 = { version = "0.13.1" }
|
||||
cw4 = { version = "0.13.1" }
|
||||
cosmwasm-std = "1.0.0-beta6"
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
@@ -0,0 +1 @@
|
||||
pub mod msg;
|
||||
+4
@@ -1,7 +1,11 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use cosmwasm_std::{CosmosMsg, Empty};
|
||||
pub use cw3::ProposalResponse;
|
||||
use cw3::Vote;
|
||||
use cw4::MemberChangedHookMsg;
|
||||
use cw_utils::{Duration, Expiration, Threshold};
|
||||
@@ -51,12 +51,7 @@ pub async fn obtain_aggregate_verification_key(
|
||||
let mut shares = Vec::with_capacity(validators.len());
|
||||
|
||||
let mut client = validator_client::ApiClient::new(validators[0].clone());
|
||||
let response = client.get_coconut_verification_key().await?;
|
||||
|
||||
indices.push(1);
|
||||
shares.push(response.key);
|
||||
|
||||
for (id, validator_url) in validators.iter().enumerate().skip(1) {
|
||||
for (id, validator_url) in validators.iter().enumerate() {
|
||||
client.change_validator_api(validator_url.clone());
|
||||
let response = client.get_coconut_verification_key().await?;
|
||||
indices.push((id + 1) as u64);
|
||||
@@ -135,14 +130,7 @@ pub async fn obtain_aggregate_signature(
|
||||
let mut validators_partial_vks: Vec<VerificationKey> = Vec::with_capacity(validators.len());
|
||||
|
||||
let mut client = validator_client::ApiClient::new(validators[0].clone());
|
||||
let validator_partial_vk = client.get_coconut_verification_key().await?;
|
||||
validators_partial_vks.push(validator_partial_vk.key.clone());
|
||||
|
||||
let first =
|
||||
obtain_partial_credential(params, attributes, &client, &validator_partial_vk.key).await?;
|
||||
shares.push(SignatureShare::new(first, 1));
|
||||
|
||||
for (id, validator_url) in validators.iter().enumerate().skip(1) {
|
||||
for (id, validator_url) in validators.iter().enumerate() {
|
||||
client.change_validator_api(validator_url.clone());
|
||||
let validator_partial_vk = client.get_coconut_verification_key().await?;
|
||||
validators_partial_vks.push(validator_partial_vk.key.clone());
|
||||
@@ -193,8 +181,7 @@ pub fn prepare_credential_for_spending(
|
||||
Ok(Credential::new(
|
||||
PUBLIC_ATTRIBUTES + PRIVATE_ATTRIBUTES,
|
||||
theta,
|
||||
voucher_value.to_string(),
|
||||
voucher_value,
|
||||
voucher_info,
|
||||
signature,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
use coconut_interface::{error::CoconutInterfaceError, CoconutError};
|
||||
use coconut_interface::CoconutError;
|
||||
use crypto::asymmetric::encryption::KeyRecoveryError;
|
||||
use validator_client::ValidatorClientError;
|
||||
|
||||
@@ -20,10 +20,6 @@ pub enum Error {
|
||||
#[error("Ran into a coconut error - {0}")]
|
||||
CoconutError(#[from] CoconutError),
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
#[error("Ran into a coconut interface error - {0}")]
|
||||
CoconutInterfaceError(#[from] CoconutInterfaceError),
|
||||
|
||||
#[error("Ran into a validator client error - {0}")]
|
||||
ValidatorClientError(#[from] ValidatorClientError),
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "execute"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "1", features = ["full"] }
|
||||
quote = "1"
|
||||
@@ -0,0 +1,110 @@
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
parse_macro_input, Block, ExprMethodCall, FnArg, Ident, ItemFn, LitStr, ReturnType, Token,
|
||||
VisPublic, Visibility,
|
||||
};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn execute(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let f = parse_macro_input!(item as ItemFn);
|
||||
let target = parse_macro_input!(attr as LitStr).value();
|
||||
|
||||
let cl = if target == "mixnet" {
|
||||
quote! {self.mixnet_contract_address()}
|
||||
} else if target == "vesting" {
|
||||
quote! {self.vesting_contract_address()}
|
||||
} else {
|
||||
panic!("Only `mixnet` and `vesting` targets are supported!")
|
||||
};
|
||||
let cl = proc_macro::TokenStream::from(cl);
|
||||
let cl = parse_macro_input!(cl as ExprMethodCall);
|
||||
|
||||
let orig_f = f.clone();
|
||||
let mut execute_f = f.clone();
|
||||
let mut simulate_f = f.clone();
|
||||
let name = f.sig.ident;
|
||||
let name_str = name.to_string();
|
||||
let call_args = f.sig.inputs.into_iter().filter_map(|arg| match arg {
|
||||
FnArg::Receiver(_) => None,
|
||||
FnArg::Typed(arg) => Some(arg.pat),
|
||||
});
|
||||
let execute_args = call_args.clone();
|
||||
let simulate_args = call_args;
|
||||
|
||||
execute_f.sig.asyncness = Some(Token));
|
||||
simulate_f.sig.asyncness = Some(Token));
|
||||
|
||||
execute_f.vis = Visibility::Public(VisPublic {
|
||||
pub_token: Token),
|
||||
});
|
||||
simulate_f.vis = Visibility::Public(VisPublic {
|
||||
pub_token: Token),
|
||||
});
|
||||
|
||||
execute_f.sig.ident = Ident::new(
|
||||
&format!("execute{}", execute_f.sig.ident),
|
||||
execute_f.sig.ident.span(),
|
||||
);
|
||||
|
||||
simulate_f.sig.ident = Ident::new(
|
||||
&format!("simulate{}", simulate_f.sig.ident),
|
||||
simulate_f.sig.ident.span(),
|
||||
);
|
||||
|
||||
let execute_output = quote! {
|
||||
-> Result<ExecuteResult, NymdError>
|
||||
};
|
||||
let o_ts = proc_macro::TokenStream::from(execute_output);
|
||||
execute_f.sig.output = parse_macro_input!(o_ts as ReturnType);
|
||||
|
||||
let simulate_output = quote! {
|
||||
-> Result<SimulateResponse, NymdError>
|
||||
};
|
||||
let o_ts = proc_macro::TokenStream::from(simulate_output);
|
||||
simulate_f.sig.output = parse_macro_input!(o_ts as ReturnType);
|
||||
|
||||
let simulate_block = quote! {
|
||||
{
|
||||
let (msg, _fee) = self.#name(#(#simulate_args),*);
|
||||
let msg = self.wrap_contract_execute_message(
|
||||
#cl,
|
||||
&msg,
|
||||
vec![],
|
||||
)?;
|
||||
|
||||
self.simulate(vec![msg]).await
|
||||
}
|
||||
};
|
||||
|
||||
let ts = proc_macro::TokenStream::from(simulate_block);
|
||||
simulate_f.block = Box::new(parse_macro_input!(ts as Block));
|
||||
|
||||
let execute_block = quote! {
|
||||
{
|
||||
let (req, fee) = self.#name(#(#execute_args),*);
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
#cl,
|
||||
&req,
|
||||
fee,
|
||||
#name_str,
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
};
|
||||
|
||||
let ts = proc_macro::TokenStream::from(execute_block);
|
||||
execute_f.block = Box::new(parse_macro_input!(ts as Block));
|
||||
|
||||
let out = quote! {
|
||||
#orig_f
|
||||
#execute_f
|
||||
#simulate_f
|
||||
};
|
||||
|
||||
out.into()
|
||||
}
|
||||
@@ -57,6 +57,14 @@ impl Network {
|
||||
self.details().bandwidth_claim_contract_address
|
||||
}
|
||||
|
||||
pub fn coconut_bandwidth_contract_address(&self) -> &str {
|
||||
self.details().coconut_bandwidth_contract_address
|
||||
}
|
||||
|
||||
pub fn multisig_contract_address(&self) -> &str {
|
||||
self.details().multisig_contract_address
|
||||
}
|
||||
|
||||
pub fn rewarding_validator_address(&self) -> &str {
|
||||
self.details().rewarding_validator_address
|
||||
}
|
||||
|
||||
@@ -52,6 +52,8 @@ pub struct DefaultNetworkDetails {
|
||||
mixnet_contract_address: &'static str,
|
||||
vesting_contract_address: &'static str,
|
||||
bandwidth_claim_contract_address: &'static str,
|
||||
coconut_bandwidth_contract_address: &'static str,
|
||||
multisig_contract_address: &'static str,
|
||||
rewarding_validator_address: &'static str,
|
||||
validators: Vec<ValidatorDetails>,
|
||||
}
|
||||
@@ -63,6 +65,8 @@ static MAINNET_DEFAULTS: Lazy<DefaultNetworkDetails> = Lazy::new(|| DefaultNetwo
|
||||
mixnet_contract_address: mainnet::MIXNET_CONTRACT_ADDRESS,
|
||||
vesting_contract_address: mainnet::VESTING_CONTRACT_ADDRESS,
|
||||
bandwidth_claim_contract_address: mainnet::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
|
||||
coconut_bandwidth_contract_address: mainnet::COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
|
||||
multisig_contract_address: mainnet::MULTISIG_CONTRACT_ADDRESS,
|
||||
rewarding_validator_address: mainnet::REWARDING_VALIDATOR_ADDRESS,
|
||||
validators: mainnet::validators(),
|
||||
});
|
||||
@@ -74,6 +78,8 @@ static SANDBOX_DEFAULTS: Lazy<DefaultNetworkDetails> = Lazy::new(|| DefaultNetwo
|
||||
mixnet_contract_address: sandbox::MIXNET_CONTRACT_ADDRESS,
|
||||
vesting_contract_address: sandbox::VESTING_CONTRACT_ADDRESS,
|
||||
bandwidth_claim_contract_address: sandbox::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
|
||||
coconut_bandwidth_contract_address: sandbox::COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
|
||||
multisig_contract_address: sandbox::MULTISIG_CONTRACT_ADDRESS,
|
||||
rewarding_validator_address: sandbox::REWARDING_VALIDATOR_ADDRESS,
|
||||
validators: sandbox::validators(),
|
||||
});
|
||||
@@ -85,6 +91,8 @@ static QA_DEFAULTS: Lazy<DefaultNetworkDetails> = Lazy::new(|| DefaultNetworkDet
|
||||
mixnet_contract_address: qa::MIXNET_CONTRACT_ADDRESS,
|
||||
vesting_contract_address: qa::VESTING_CONTRACT_ADDRESS,
|
||||
bandwidth_claim_contract_address: qa::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
|
||||
coconut_bandwidth_contract_address: qa::COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
|
||||
multisig_contract_address: qa::MULTISIG_CONTRACT_ADDRESS,
|
||||
rewarding_validator_address: qa::REWARDING_VALIDATOR_ADDRESS,
|
||||
validators: qa::validators(),
|
||||
});
|
||||
@@ -184,7 +192,7 @@ pub const ETH_ERC20_APPROVE_FUNCTION_NAME: &str = "approve";
|
||||
|
||||
// Ethereum constants used for token bridge
|
||||
/// How much bandwidth (in bytes) one token can buy
|
||||
const BYTES_PER_TOKEN: u64 = 1024 * 1024 * 1024;
|
||||
pub const BYTES_PER_UTOKEN: u64 = 1024;
|
||||
|
||||
/// Threshold for claiming more bandwidth: 1 MB
|
||||
pub const REMAINING_BANDWIDTH_THRESHOLD: i64 = 1024 * 1024;
|
||||
@@ -193,7 +201,7 @@ pub const TOKENS_TO_BURN: u64 = 1;
|
||||
/// How many ERC20 utokens should be burned to buy bandwidth
|
||||
pub const UTOKENS_TO_BURN: u64 = TOKENS_TO_BURN * 1000000;
|
||||
/// Default bandwidth (in bytes) that we try to buy
|
||||
pub const BANDWIDTH_VALUE: u64 = TOKENS_TO_BURN * BYTES_PER_TOKEN;
|
||||
pub const BANDWIDTH_VALUE: u64 = UTOKENS_TO_BURN * BYTES_PER_UTOKEN;
|
||||
|
||||
pub const VOUCHER_INFO: &str = "BandwidthVoucher";
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@ pub(crate) const VESTING_CONTRACT_ADDRESS: &str =
|
||||
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw";
|
||||
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
|
||||
"n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
|
||||
pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str =
|
||||
"n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
|
||||
pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
|
||||
pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("0000000000000000000000000000000000000000");
|
||||
pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
|
||||
|
||||
@@ -15,6 +15,9 @@ pub(crate) const VESTING_CONTRACT_ADDRESS: &str =
|
||||
"n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav";
|
||||
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
|
||||
"n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
|
||||
pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str =
|
||||
"n1ghd753shjuwexxywmgs4xz7x2q732vcn7ty4yw";
|
||||
pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str = "n17p9rzwnnfxcjp32un9ug7yhhzgtkhvl988qccs";
|
||||
pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("0000000000000000000000000000000000000000");
|
||||
pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
|
||||
|
||||
@@ -13,6 +13,9 @@ pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "nymt1ghd753shjuwexxywmgs4xz7x2
|
||||
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt14ejqjyq8um4p3xfqj74yld5waqljf88fn549lh";
|
||||
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
|
||||
"nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
|
||||
pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str =
|
||||
"nymt1nz0r0au8aj6dc00wmm3ufy4g4k86rjzlgq608r";
|
||||
pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str = "nymt1k8re7jwz6rnnwrktnejdwkwnncte7ek7kk6fvg";
|
||||
pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("8e0DcFF7F3085235C32E845f3667aEB3f1e83133");
|
||||
pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
|
||||
|
||||
@@ -327,8 +327,7 @@ impl ProofCmCs {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ProofKappaZeta {
|
||||
// c
|
||||
challenge: Scalar,
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bls12_381::G2Projective;
|
||||
use group::Curve;
|
||||
use std::convert::TryFrom;
|
||||
use std::convert::TryInto;
|
||||
|
||||
use crate::error::{CoconutError, Result};
|
||||
use crate::traits::{Base58, Bytable};
|
||||
use crate::utils::try_deserialize_g2_projective;
|
||||
|
||||
pub struct BlindedSerialNumber {
|
||||
pub(crate) inner: G2Projective,
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for BlindedSerialNumber {
|
||||
type Error = CoconutError;
|
||||
|
||||
fn try_from(bytes: &[u8]) -> Result<Self> {
|
||||
if bytes.len() != 96 {
|
||||
return Err(
|
||||
CoconutError::Deserialization(
|
||||
format!("Tried to deserialize blinded serial number with incorrect number of bytes, expected 96, got {}", bytes.len()),
|
||||
));
|
||||
}
|
||||
|
||||
let inner = try_deserialize_g2_projective(
|
||||
&bytes.try_into().unwrap(),
|
||||
CoconutError::Deserialization(
|
||||
"failed to deserialize the blinded serial number (zeta)".to_string(),
|
||||
),
|
||||
)?;
|
||||
|
||||
Ok(BlindedSerialNumber { inner })
|
||||
}
|
||||
}
|
||||
|
||||
impl Bytable for BlindedSerialNumber {
|
||||
fn to_byte_vec(&self) -> Vec<u8> {
|
||||
self.inner.to_affine().to_compressed().to_vec()
|
||||
}
|
||||
|
||||
fn try_from_byte_slice(slice: &[u8]) -> Result<Self> {
|
||||
Self::try_from(slice)
|
||||
}
|
||||
}
|
||||
|
||||
impl Base58 for BlindedSerialNumber {}
|
||||
@@ -19,6 +19,7 @@ use crate::utils::try_deserialize_g1_projective;
|
||||
use crate::Attribute;
|
||||
|
||||
pub mod aggregation;
|
||||
pub mod double_use;
|
||||
pub mod issuance;
|
||||
pub mod keygen;
|
||||
pub mod setup;
|
||||
@@ -27,8 +28,7 @@ pub mod verification;
|
||||
pub type SignerIndex = u64;
|
||||
|
||||
// (h, s)
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Signature(pub(crate) G1Projective, pub(crate) G1Projective);
|
||||
|
||||
pub type PartialSignature = Signature;
|
||||
|
||||
@@ -10,6 +10,7 @@ use group::{Curve, Group};
|
||||
|
||||
use crate::error::{CoconutError, Result};
|
||||
use crate::proofs::ProofKappaZeta;
|
||||
use crate::scheme::double_use::BlindedSerialNumber;
|
||||
use crate::scheme::setup::Parameters;
|
||||
use crate::scheme::Signature;
|
||||
use crate::scheme::VerificationKey;
|
||||
@@ -19,8 +20,7 @@ use crate::Attribute;
|
||||
|
||||
// TODO NAMING: this whole thing
|
||||
// Theta
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Theta {
|
||||
// blinded_message (kappa)
|
||||
pub blinded_message: G2Projective,
|
||||
@@ -81,6 +81,12 @@ impl Theta {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn has_blinded_serial_number(&self, blinded_serial_number_bs58: &str) -> Result<bool> {
|
||||
let blinded_serial_number = BlindedSerialNumber::try_from_bs58(blinded_serial_number_bs58)?;
|
||||
let ret = self.blinded_serial_number.eq(&blinded_serial_number.inner);
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
// blinded message (kappa) || blinded serial number (zeta) || credential || pi_v
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
let blinded_message_bytes = self.blinded_message.to_affine().to_compressed();
|
||||
@@ -100,6 +106,13 @@ impl Theta {
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Theta> {
|
||||
Theta::try_from(bytes)
|
||||
}
|
||||
|
||||
pub fn blinded_serial_number_bs58(&self) -> String {
|
||||
let blinded_serial_nuumber = BlindedSerialNumber {
|
||||
inner: self.blinded_serial_number,
|
||||
};
|
||||
blinded_serial_nuumber.to_bs58()
|
||||
}
|
||||
}
|
||||
|
||||
impl Bytable for Theta {
|
||||
|
||||
Generated
+13
@@ -508,6 +508,7 @@ dependencies = [
|
||||
"cw3-fixed-multisig",
|
||||
"cw4",
|
||||
"cw4-group",
|
||||
"multisig-contract-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
@@ -1049,6 +1050,18 @@ dependencies = [
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multisig-contract-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
"cw3",
|
||||
"cw4",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "network-defaults"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -28,6 +28,8 @@ schemars = "0.8.1"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = { version = "1.0.23" }
|
||||
|
||||
multisig-contract-common = { path= "../../../common/cosmwasm-smart-contracts/multisig-contract" }
|
||||
|
||||
[dev-dependencies]
|
||||
cosmwasm-schema = { version = "1.0.0-beta6" }
|
||||
cw4-group = { path = "../cw4-group", version = "0.13.1" }
|
||||
|
||||
@@ -18,8 +18,8 @@ use cw_storage_plus::Bound;
|
||||
use cw_utils::{maybe_addr, Expiration, ThresholdResponse};
|
||||
|
||||
use crate::error::ContractError;
|
||||
use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};
|
||||
use crate::state::{Config, CONFIG};
|
||||
use multisig_contract_common::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};
|
||||
|
||||
// version info for migration info
|
||||
const CONTRACT_NAME: &str = "crates.io:cw3-flex-multisig";
|
||||
@@ -499,7 +499,7 @@ mod tests {
|
||||
max_voting_period: Duration,
|
||||
) -> Addr {
|
||||
let flex_id = app.store_code(contract_flex());
|
||||
let msg = crate::msg::InstantiateMsg {
|
||||
let msg = InstantiateMsg {
|
||||
group_addr: group.to_string(),
|
||||
threshold,
|
||||
max_voting_period,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
pub mod contract;
|
||||
pub mod error;
|
||||
pub mod msg;
|
||||
pub mod state;
|
||||
|
||||
pub use crate::error::ContractError;
|
||||
|
||||
+10
-9
@@ -6,21 +6,22 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
humantime-serde = "1.0"
|
||||
isocountry = "0.3.2"
|
||||
itertools = "0.10.3"
|
||||
log = "0.4.0"
|
||||
okapi = { version = "0.7.0-rc.1", features = ["impl_json_schema"] }
|
||||
pretty_env_logger = "0.4.0"
|
||||
reqwest = "0.11.4"
|
||||
rocket = {version = "0.5.0-rc.1", features=["json"] }
|
||||
rocket_cors = { git="https://github.com/lawliet89/rocket_cors", rev="dfd3662c49e2f6fc37df35091cb94d82f7fb5915" }
|
||||
serde = "1.0.126"
|
||||
humantime-serde = "1.0"
|
||||
serde_json = "1.0.66"
|
||||
tokio = {version = "1.9.0", features = ["full"] }
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
schemars = { version = "0.8", features = ["preserve_order"] }
|
||||
okapi = { version = "0.7.0-rc.1", features = ["impl_json_schema"] }
|
||||
rocket_okapi = { version = "0.8.0-rc.1", features = ["swagger"] }
|
||||
log = "0.4.0"
|
||||
pretty_env_logger = "0.4.0"
|
||||
schemars = { version = "0.8", features = ["preserve_order"] }
|
||||
serde = "1.0.126"
|
||||
serde_json = "1.0.66"
|
||||
thiserror = "1.0.29"
|
||||
tokio = {version = "1.9.0", features = ["full"] }
|
||||
|
||||
mixnet-contract-common = { path = "../common/cosmwasm-smart-contracts/mixnet-contract" }
|
||||
network-defaults = { path = "../common/network-defaults" }
|
||||
|
||||
@@ -5,13 +5,13 @@ An API that provides data for the [Network Explorer](../explorer).
|
||||
|
||||
Features:
|
||||
|
||||
- geolocates mixnodes using https://freegeoip.app/
|
||||
- geolocates mixnodes using https://app.ipbase.com/
|
||||
- calculates how many nodes are in each country
|
||||
- proxies mixnode API requests to add HTTPS
|
||||
|
||||
## Running
|
||||
|
||||
Supply the environment variable `GEO_IP_SERVICE_API_KEY` with a key from https://freegeoip.app/.
|
||||
Supply the environment variable `GEO_IP_SERVICE_API_KEY` with a key from https://app.ipbase.com/.
|
||||
|
||||
Run as a service and reverse proxy with `nginx` to add `https` with Lets Encrypt.
|
||||
|
||||
|
||||
@@ -23,19 +23,10 @@ impl ThreadsafeValidatorClient {
|
||||
}
|
||||
|
||||
pub(crate) fn new_validator_client() -> ThreadsafeValidatorClient {
|
||||
let network = DEFAULT_NETWORK;
|
||||
let mixnet_contract = network.mixnet_contract_address().to_string();
|
||||
let nymd_url = default_nymd_endpoints()[0].clone();
|
||||
let api_url = default_api_endpoints()[0].clone();
|
||||
|
||||
let client_config = validator_client::Config::new(
|
||||
network,
|
||||
nymd_url,
|
||||
api_url,
|
||||
Some(mixnet_contract.parse().unwrap()),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let client_config = validator_client::Config::new(DEFAULT_NETWORK, nymd_url, api_url);
|
||||
|
||||
ThreadsafeValidatorClient(Arc::new(
|
||||
validator_client::Client::new_query(client_config).expect("Failed to connect to nymd!"),
|
||||
|
||||
@@ -51,7 +51,7 @@ impl GeoLocateTask {
|
||||
.state
|
||||
.inner
|
||||
.mixnodes
|
||||
.is_location_valid(&cache_item.mix_node.identity_key)
|
||||
.is_location_valid(&cache_item.mix_node().identity_key)
|
||||
.await
|
||||
{
|
||||
// when the cached location is valid, don't locate and continue to next mix node
|
||||
@@ -59,14 +59,14 @@ impl GeoLocateTask {
|
||||
}
|
||||
|
||||
// the mix node has not been located or is the cache time has expired
|
||||
match locate(&cache_item.mix_node.host).await {
|
||||
match locate(&cache_item.mix_node().host).await {
|
||||
Ok(geo_location) => {
|
||||
let location = Location::new(geo_location);
|
||||
|
||||
trace!(
|
||||
"{} mix nodes already located. Ip {} is located in {:#?}",
|
||||
i,
|
||||
cache_item.mix_node.host,
|
||||
cache_item.mix_node().host,
|
||||
location.three_letter_iso_country_code,
|
||||
);
|
||||
|
||||
@@ -80,7 +80,7 @@ impl GeoLocateTask {
|
||||
self.state
|
||||
.inner
|
||||
.mixnodes
|
||||
.set_location(&cache_item.mix_node.identity_key, Some(location))
|
||||
.set_location(&cache_item.mix_node().identity_key, Some(location))
|
||||
.await;
|
||||
|
||||
// one node has been located, so return out of the loop
|
||||
@@ -89,22 +89,22 @@ impl GeoLocateTask {
|
||||
Err(e) => match e {
|
||||
LocateError::ReqwestError(e) => warn!(
|
||||
"❌ Oh no! Location for {} failed {}",
|
||||
cache_item.mix_node.host, e
|
||||
cache_item.mix_node().host, e
|
||||
),
|
||||
LocateError::NotFound(e) => {
|
||||
warn!(
|
||||
"❌ Location for {} not found. Response body: {}",
|
||||
cache_item.mix_node.host, e
|
||||
cache_item.mix_node().host, e
|
||||
);
|
||||
self.state
|
||||
.inner
|
||||
.mixnodes
|
||||
.set_location(&cache_item.mix_node.identity_key, None)
|
||||
.set_location(&cache_item.mix_node().identity_key, None)
|
||||
.await;
|
||||
},
|
||||
LocateError::RateLimited(e) => warn!(
|
||||
"❌ Oh no, we've been rate limited! Location for {} failed. Response body: {}",
|
||||
cache_item.mix_node.host, e
|
||||
cache_item.mix_node().host, e
|
||||
),
|
||||
},
|
||||
}
|
||||
@@ -129,7 +129,7 @@ enum LocateError {
|
||||
async fn locate(ip: &str) -> Result<GeoLocation, LocateError> {
|
||||
let api_key = ::std::env::var("GEO_IP_SERVICE_API_KEY")
|
||||
.expect("Env var GEO_IP_SERVICE_API_KEY is not set");
|
||||
let uri = format!("{}/{}?apikey={}", crate::GEO_IP_SERVICE, ip, api_key);
|
||||
let uri = format!("{}/?apikey={}&ip={}", crate::GEO_IP_SERVICE, api_key, ip);
|
||||
match reqwest::get(uri.clone()).await {
|
||||
Ok(response) => {
|
||||
if response.status() == 429 {
|
||||
|
||||
@@ -18,7 +18,7 @@ mod state;
|
||||
mod tasks;
|
||||
mod validators;
|
||||
|
||||
const GEO_IP_SERVICE: &str = "https://api.freegeoip.app/json";
|
||||
const GEO_IP_SERVICE: &str = "https://api.ipbase.com/json";
|
||||
const COUNTRY_DATA_REFRESH_INTERVAL: u64 = 60 * 15; // every 15 minutes
|
||||
|
||||
#[tokio::main]
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::client::ThreadsafeValidatorClient;
|
||||
use mixnet_contract_common::Delegation;
|
||||
|
||||
use super::models::SummedDelegations;
|
||||
|
||||
pub(crate) async fn get_single_mixnode_delegations(
|
||||
client: &ThreadsafeValidatorClient,
|
||||
pubkey: &str,
|
||||
@@ -21,3 +25,18 @@ pub(crate) async fn get_single_mixnode_delegations(
|
||||
};
|
||||
delegates
|
||||
}
|
||||
|
||||
pub(crate) async fn get_single_mixnode_delegations_summed(
|
||||
client: &ThreadsafeValidatorClient,
|
||||
pubkey: &str,
|
||||
) -> Vec<SummedDelegations> {
|
||||
let delegations_by_owner = get_single_mixnode_delegations(client, pubkey)
|
||||
.await
|
||||
.into_iter()
|
||||
.into_group_map_by(|delegation| delegation.owner.clone());
|
||||
|
||||
delegations_by_owner
|
||||
.iter()
|
||||
.filter_map(|(_, delegations)| SummedDelegations::from(delegations))
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -11,16 +11,19 @@ use rocket_okapi::settings::OpenApiSettings;
|
||||
|
||||
use mixnet_contract_common::Delegation;
|
||||
|
||||
use crate::mix_node::delegations::get_single_mixnode_delegations;
|
||||
use crate::mix_node::delegations::{
|
||||
get_single_mixnode_delegations, get_single_mixnode_delegations_summed,
|
||||
};
|
||||
use crate::mix_node::econ_stats::retrieve_mixnode_econ_stats;
|
||||
use crate::mix_node::models::{
|
||||
EconomicDynamicsStats, NodeDescription, NodeStats, PrettyDetailedMixNodeBond,
|
||||
EconomicDynamicsStats, NodeDescription, NodeStats, PrettyDetailedMixNodeBond, SummedDelegations,
|
||||
};
|
||||
use crate::state::ExplorerApiStateContext;
|
||||
|
||||
pub fn mix_node_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
|
||||
openapi_get_routes_spec![
|
||||
settings: get_delegations,
|
||||
get_delegations_summed,
|
||||
get_by_id,
|
||||
get_description,
|
||||
get_stats,
|
||||
@@ -54,6 +57,15 @@ pub(crate) async fn get_delegations(
|
||||
Json(get_single_mixnode_delegations(&state.inner.validator_client, pubkey).await)
|
||||
}
|
||||
|
||||
#[openapi(tag = "mix_node")]
|
||||
#[get("/<pubkey>/delegations/summed")]
|
||||
pub(crate) async fn get_delegations_summed(
|
||||
pubkey: &str,
|
||||
state: &State<ExplorerApiStateContext>,
|
||||
) -> Json<Vec<SummedDelegations>> {
|
||||
Json(get_single_mixnode_delegations_summed(&state.inner.validator_client, pubkey).await)
|
||||
}
|
||||
|
||||
#[openapi(tag = "mix_node")]
|
||||
#[get("/<pubkey>/description")]
|
||||
pub(crate) async fn get_description(
|
||||
@@ -70,8 +82,8 @@ pub(crate) async fn get_description(
|
||||
match state.inner.get_mix_node(pubkey).await {
|
||||
Some(bond) => {
|
||||
match get_mix_node_description(
|
||||
&bond.mix_node.host,
|
||||
&bond.mix_node.http_api_port,
|
||||
&bond.mix_node().host,
|
||||
&bond.mix_node().http_api_port,
|
||||
)
|
||||
.await
|
||||
{
|
||||
@@ -87,7 +99,10 @@ pub(crate) async fn get_description(
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Unable to get description for {} on {}:{} -> {}",
|
||||
pubkey, bond.mix_node.host, bond.mix_node.http_api_port, e
|
||||
pubkey,
|
||||
bond.mix_node().host,
|
||||
bond.mix_node().http_api_port,
|
||||
e
|
||||
);
|
||||
Option::None
|
||||
}
|
||||
@@ -114,7 +129,7 @@ pub(crate) async fn get_stats(
|
||||
trace!("No valid cache value for {}", pubkey);
|
||||
match state.inner.get_mix_node(pubkey).await {
|
||||
Some(bond) => {
|
||||
match get_mix_node_stats(&bond.mix_node.host, &bond.mix_node.http_api_port)
|
||||
match get_mix_node_stats(&bond.mix_node().host, &bond.mix_node().http_api_port)
|
||||
.await
|
||||
{
|
||||
Ok(response) => {
|
||||
@@ -129,7 +144,10 @@ pub(crate) async fn get_stats(
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Unable to get description for {} on {}:{} -> {}",
|
||||
pubkey, bond.mix_node.host, bond.mix_node.http_api_port, e
|
||||
pubkey,
|
||||
bond.mix_node().host,
|
||||
bond.mix_node().http_api_port,
|
||||
e
|
||||
);
|
||||
Option::None
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
use crate::cache::Cache;
|
||||
use crate::mix_nodes::location::Location;
|
||||
use mixnet_contract_common::{Addr, Coin, Layer, MixNode};
|
||||
use mixnet_contract_common::{Delegation, IdentityKey};
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::sync::Arc;
|
||||
@@ -29,6 +30,35 @@ pub(crate) struct PrettyDetailedMixNodeBond {
|
||||
pub layer: Layer,
|
||||
pub mix_node: MixNode,
|
||||
pub avg_uptime: Option<u8>,
|
||||
pub stake_saturation: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)]
|
||||
pub struct SummedDelegations {
|
||||
pub owner: Addr,
|
||||
pub node_identity: IdentityKey,
|
||||
pub amount: Coin,
|
||||
}
|
||||
|
||||
impl SummedDelegations {
|
||||
pub fn from(delegations: &[Delegation]) -> Option<Self> {
|
||||
let owner = get_common_owner(delegations)?;
|
||||
let node_identity = get_common_node_identity(delegations)?;
|
||||
let denom = get_common_denom(delegations)?;
|
||||
|
||||
let sum = delegations
|
||||
.iter()
|
||||
.map(|delegation| delegation.amount.amount)
|
||||
.sum();
|
||||
|
||||
let amount = Coin { denom, amount: sum };
|
||||
|
||||
Some(SummedDelegations {
|
||||
owner,
|
||||
node_identity,
|
||||
amount,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct MixNodeCache {
|
||||
@@ -137,3 +167,39 @@ pub(crate) struct EconomicDynamicsStats {
|
||||
|
||||
pub(crate) current_interval_uptime: u8,
|
||||
}
|
||||
|
||||
fn get_common_owner(delegations: &[Delegation]) -> Option<Addr> {
|
||||
let owner = delegations.iter().next()?.owner();
|
||||
if delegations
|
||||
.iter()
|
||||
.any(|delegation| delegation.owner() != owner)
|
||||
{
|
||||
log::warn!("Unexpected different owners when summing delegations");
|
||||
return None;
|
||||
}
|
||||
Some(owner)
|
||||
}
|
||||
|
||||
fn get_common_node_identity(delegations: &[Delegation]) -> Option<String> {
|
||||
let node_identity = delegations.iter().next()?.node_identity();
|
||||
if delegations
|
||||
.iter()
|
||||
.any(|delegation| delegation.node_identity() != node_identity)
|
||||
{
|
||||
log::warn!("Unexpected different node identities when summing delegations");
|
||||
return None;
|
||||
}
|
||||
Some(node_identity)
|
||||
}
|
||||
|
||||
fn get_common_denom(delegations: &[Delegation]) -> Option<String> {
|
||||
let denom = delegations.iter().next()?.amount.denom.clone();
|
||||
if delegations
|
||||
.iter()
|
||||
.any(|delegation| delegation.amount.denom != denom)
|
||||
{
|
||||
log::warn!("Unexpected different coin denom when summing delegations");
|
||||
return None;
|
||||
}
|
||||
Some(denom)
|
||||
}
|
||||
|
||||
@@ -8,8 +8,7 @@ use std::time::{Duration, SystemTime};
|
||||
use serde::Serialize;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use mixnet_contract_common::MixNodeBond;
|
||||
use validator_client::models::UptimeResponse;
|
||||
use validator_client::models::{MixNodeBondAnnotated, UptimeResponse};
|
||||
|
||||
use crate::cache::Cache;
|
||||
use crate::mix_node::models::{MixnodeStatus, PrettyDetailedMixNodeBond};
|
||||
@@ -32,7 +31,7 @@ pub(crate) struct MixNodeSummary {
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct MixNodesResult {
|
||||
pub(crate) valid_until: SystemTime,
|
||||
pub(crate) all_mixnodes: HashMap<String, MixNodeBond>,
|
||||
pub(crate) all_mixnodes: HashMap<String, MixNodeBondAnnotated>,
|
||||
active_mixnodes: HashSet<String>,
|
||||
rewarded_mixnodes: HashSet<String>,
|
||||
}
|
||||
@@ -61,7 +60,7 @@ impl MixNodesResult {
|
||||
self.valid_until >= SystemTime::now()
|
||||
}
|
||||
|
||||
fn get_mixnode(&self, pubkey: &str) -> Option<MixNodeBond> {
|
||||
fn get_mixnode(&self, pubkey: &str) -> Option<MixNodeBondAnnotated> {
|
||||
if self.is_valid() {
|
||||
self.all_mixnodes.get(pubkey).cloned()
|
||||
} else {
|
||||
@@ -69,7 +68,7 @@ impl MixNodesResult {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_mixnodes(&self) -> Option<HashMap<String, MixNodeBond>> {
|
||||
fn get_mixnodes(&self) -> Option<HashMap<String, MixNodeBondAnnotated>> {
|
||||
if self.is_valid() {
|
||||
Some(self.all_mixnodes.clone())
|
||||
} else {
|
||||
@@ -128,11 +127,11 @@ impl ThreadsafeMixNodesCache {
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) async fn get_mixnode(&self, pubkey: &str) -> Option<MixNodeBond> {
|
||||
pub(crate) async fn get_mixnode(&self, pubkey: &str) -> Option<MixNodeBondAnnotated> {
|
||||
self.mixnodes.read().await.get_mixnode(pubkey)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_mixnodes(&self) -> Option<HashMap<String, MixNodeBond>> {
|
||||
pub(crate) async fn get_mixnodes(&self) -> Option<HashMap<String, MixNodeBondAnnotated>> {
|
||||
self.mixnodes.read().await.get_mixnodes()
|
||||
}
|
||||
|
||||
@@ -151,13 +150,14 @@ impl ThreadsafeMixNodesCache {
|
||||
match bond {
|
||||
Some(bond) => Some(PrettyDetailedMixNodeBond {
|
||||
location: location.and_then(|l| l.location.clone()),
|
||||
status: mixnodes_guard.determine_node_status(&bond.mix_node.identity_key),
|
||||
pledge_amount: bond.pledge_amount,
|
||||
total_delegation: bond.total_delegation,
|
||||
owner: bond.owner,
|
||||
layer: bond.layer,
|
||||
mix_node: bond.mix_node,
|
||||
status: mixnodes_guard.determine_node_status(&bond.mix_node().identity_key),
|
||||
pledge_amount: bond.mixnode_bond.pledge_amount,
|
||||
total_delegation: bond.mixnode_bond.total_delegation,
|
||||
owner: bond.mixnode_bond.owner,
|
||||
layer: bond.mixnode_bond.layer,
|
||||
mix_node: bond.mixnode_bond.mix_node,
|
||||
avg_uptime: health.map(|m| m.avg_uptime),
|
||||
stake_saturation: bond.stake_saturation,
|
||||
}),
|
||||
None => None,
|
||||
}
|
||||
@@ -172,18 +172,19 @@ impl ThreadsafeMixNodesCache {
|
||||
.all_mixnodes
|
||||
.values()
|
||||
.map(|bond| {
|
||||
let location = location_guard.get(&bond.mix_node.identity_key);
|
||||
let copy = bond.clone();
|
||||
let health = mixnode_health_guard.get(&bond.mix_node.identity_key);
|
||||
let location = location_guard.get(&bond.mix_node().identity_key);
|
||||
let copy = bond.mixnode_bond.clone();
|
||||
let health = mixnode_health_guard.get(&bond.mix_node().identity_key);
|
||||
PrettyDetailedMixNodeBond {
|
||||
location: location.and_then(|l| l.location.clone()),
|
||||
status: mixnodes_guard.determine_node_status(&bond.mix_node.identity_key),
|
||||
status: mixnodes_guard.determine_node_status(&bond.mix_node().identity_key),
|
||||
pledge_amount: copy.pledge_amount,
|
||||
total_delegation: copy.total_delegation,
|
||||
owner: copy.owner,
|
||||
layer: copy.layer,
|
||||
mix_node: copy.mix_node,
|
||||
avg_uptime: health.map(|m| m.avg_uptime),
|
||||
stake_saturation: bond.stake_saturation,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
@@ -191,14 +192,14 @@ impl ThreadsafeMixNodesCache {
|
||||
|
||||
pub(crate) async fn update_cache(
|
||||
&self,
|
||||
all_bonds: Vec<MixNodeBond>,
|
||||
all_bonds: Vec<MixNodeBondAnnotated>,
|
||||
rewarded_nodes: HashSet<String>,
|
||||
active_nodes: HashSet<String>,
|
||||
) {
|
||||
let mut guard = self.mixnodes.write().await;
|
||||
guard.all_mixnodes = all_bonds
|
||||
.into_iter()
|
||||
.map(|bond| (bond.mix_node.identity_key.to_string(), bond))
|
||||
.map(|bond| (bond.mix_node().identity_key.to_string(), bond))
|
||||
.collect();
|
||||
guard.rewarded_mixnodes = rewarded_nodes;
|
||||
guard.active_mixnodes = active_nodes;
|
||||
|
||||
@@ -42,7 +42,7 @@ pub(crate) async fn index(
|
||||
state.inner.ping.set_pending(pubkey).await;
|
||||
|
||||
// do the check
|
||||
let ports = Some(port_check(&bond).await);
|
||||
let ports = Some(port_check(&bond.mixnode_bond).await);
|
||||
trace!("Tested mix node {}: {:?}", pubkey, ports);
|
||||
let response = PingResponse {
|
||||
ports,
|
||||
|
||||
@@ -6,7 +6,7 @@ use log::info;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::client::ThreadsafeValidatorClient;
|
||||
use mixnet_contract_common::MixNodeBond;
|
||||
use validator_client::models::MixNodeBondAnnotated;
|
||||
|
||||
use crate::country_statistics::country_nodes_distribution::{
|
||||
CountryNodesDistribution, ThreadsafeCountryNodesDistribution,
|
||||
@@ -35,7 +35,7 @@ pub struct ExplorerApiState {
|
||||
}
|
||||
|
||||
impl ExplorerApiState {
|
||||
pub(crate) async fn get_mix_node(&self, pubkey: &str) -> Option<MixNodeBond> {
|
||||
pub(crate) async fn get_mix_node(&self, pubkey: &str) -> Option<MixNodeBondAnnotated> {
|
||||
self.mixnodes.get_mixnode(pubkey).await
|
||||
}
|
||||
}
|
||||
|
||||
+12
-12
@@ -3,8 +3,8 @@
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
use mixnet_contract_common::{GatewayBond, MixNodeBond};
|
||||
use validator_client::models::UptimeResponse;
|
||||
use mixnet_contract_common::GatewayBond;
|
||||
use validator_client::models::{MixNodeBondAnnotated, UptimeResponse};
|
||||
use validator_client::nymd::error::NymdError;
|
||||
use validator_client::nymd::{Paging, QueryNymdClient, ValidatorResponse};
|
||||
use validator_client::ValidatorClientError;
|
||||
@@ -22,10 +22,10 @@ impl ExplorerApiTasks {
|
||||
}
|
||||
|
||||
// a helper to remove duplicate code when grabbing active/rewarded/all mixnodes
|
||||
async fn retrieve_mixnodes<'a, F, Fut>(&'a self, f: F) -> Vec<MixNodeBond>
|
||||
async fn retrieve_mixnodes<'a, F, Fut>(&'a self, f: F) -> Vec<MixNodeBondAnnotated>
|
||||
where
|
||||
F: FnOnce(&'a validator_client::Client<QueryNymdClient>) -> Fut,
|
||||
Fut: Future<Output = Result<Vec<MixNodeBond>, ValidatorClientError>>,
|
||||
Fut: Future<Output = Result<Vec<MixNodeBondAnnotated>, ValidatorClientError>>,
|
||||
{
|
||||
let bonds = match f(&self.state.inner.validator_client.0).await {
|
||||
Ok(result) => result,
|
||||
@@ -39,9 +39,9 @@ impl ExplorerApiTasks {
|
||||
bonds
|
||||
}
|
||||
|
||||
async fn retrieve_all_mixnodes(&self) -> Vec<MixNodeBond> {
|
||||
async fn retrieve_all_mixnodes(&self) -> Vec<MixNodeBondAnnotated> {
|
||||
info!("About to retrieve all mixnode bonds...");
|
||||
self.retrieve_mixnodes(validator_client::Client::get_cached_mixnodes)
|
||||
self.retrieve_mixnodes(validator_client::Client::get_cached_mixnodes_detailed)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -77,15 +77,15 @@ impl ExplorerApiTasks {
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
async fn retrieve_rewarded_mixnodes(&self) -> Vec<MixNodeBond> {
|
||||
async fn retrieve_rewarded_mixnodes(&self) -> Vec<MixNodeBondAnnotated> {
|
||||
info!("About to retrieve rewarded mixnode bonds...");
|
||||
self.retrieve_mixnodes(validator_client::Client::get_cached_rewarded_mixnodes)
|
||||
self.retrieve_mixnodes(validator_client::Client::get_cached_rewarded_mixnodes_detailed)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn retrieve_active_mixnodes(&self) -> Vec<MixNodeBond> {
|
||||
async fn retrieve_active_mixnodes(&self) -> Vec<MixNodeBondAnnotated> {
|
||||
info!("About to retrieve active mixnode bonds...");
|
||||
self.retrieve_mixnodes(validator_client::Client::get_cached_active_mixnodes)
|
||||
self.retrieve_mixnodes(validator_client::Client::get_cached_active_mixnodes_detailed)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -106,13 +106,13 @@ impl ExplorerApiTasks {
|
||||
.retrieve_rewarded_mixnodes()
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|bond| bond.mix_node.identity_key)
|
||||
.map(|bond| bond.mix_node().identity_key.clone())
|
||||
.collect();
|
||||
let active_nodes = self
|
||||
.retrieve_active_mixnodes()
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|bond| bond.mix_node.identity_key)
|
||||
.map(|bond| bond.mix_node().identity_key.clone())
|
||||
.collect();
|
||||
self.state
|
||||
.inner
|
||||
|
||||
@@ -13,6 +13,7 @@ export type MixnodeRowType = {
|
||||
layer: string;
|
||||
profit_percentage: string;
|
||||
avg_uptime: string;
|
||||
stake_saturation: number;
|
||||
};
|
||||
|
||||
export function mixnodeToGridRow(arrayOfMixnodes?: MixNodeResponse): MixnodeRowType[] {
|
||||
@@ -25,6 +26,7 @@ export function mixNodeResponseItemToMixnodeRowType(item: MixNodeResponseItem):
|
||||
const totalBond = pledge + delegations;
|
||||
const selfPercentage = ((pledge * 100) / totalBond).toFixed(2);
|
||||
const profitPercentage = item.mix_node.profit_margin_percent || 0;
|
||||
const stakeSaturation = typeof item.stake_saturation === 'number' ? item.stake_saturation * 100 : 0;
|
||||
return {
|
||||
id: item.owner,
|
||||
status: item.status,
|
||||
@@ -37,5 +39,6 @@ export function mixNodeResponseItemToMixnodeRowType(item: MixNodeResponseItem):
|
||||
layer: item?.layer || '',
|
||||
profit_percentage: `${profitPercentage}%`,
|
||||
avg_uptime: `${item.avg_uptime}%` || '-',
|
||||
stake_saturation: stakeSaturation,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -248,6 +248,27 @@ export const PageMixnodes: React.FC = () => {
|
||||
</MuiLink>
|
||||
),
|
||||
},
|
||||
{
|
||||
field: 'stake_saturation',
|
||||
headerName: 'Stake Saturation',
|
||||
renderHeader: () => <CustomColumnHeading headingTitle="Stake Saturation" />,
|
||||
headerClassName: 'MuiDataGrid-header-override',
|
||||
width: 175,
|
||||
headerAlign: 'left',
|
||||
renderCell: (params: GridRenderCellParams) => (
|
||||
<MuiLink
|
||||
sx={{
|
||||
textAlign: 'left',
|
||||
color: params.value > 100 ? theme.palette.warning.main : 'inherit',
|
||||
...getCellStyles(theme, params.row),
|
||||
}}
|
||||
component={RRDLink}
|
||||
to={`/network-components/mixnode/${params.row.identity_key}`}
|
||||
>
|
||||
{`${params.value.toFixed(2)} %`}
|
||||
</MuiLink>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const handlePageSize = (event: SelectChangeEvent<string>) => {
|
||||
|
||||
@@ -84,6 +84,7 @@ export interface MixNodeResponseItem {
|
||||
};
|
||||
mix_node: MixNode;
|
||||
avg_uptime: number;
|
||||
stake_saturation: number;
|
||||
}
|
||||
|
||||
export type MixNodeResponse = MixNodeResponseItem[];
|
||||
|
||||
@@ -18,7 +18,6 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
thiserror = "1.0"
|
||||
bincode = "1.3"
|
||||
|
||||
crypto = { path = "../../common/crypto" }
|
||||
pemstore = { path = "../../common/pemstore" }
|
||||
|
||||
@@ -25,9 +25,4 @@ pub enum HandshakeError {
|
||||
MalformedRequest,
|
||||
#[error("sent request was malformed")]
|
||||
HandshakeFailure,
|
||||
#[error("could not deserialize from slice: {source}")]
|
||||
DeserializationError {
|
||||
#[from]
|
||||
source: bincode::Error,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -146,18 +146,13 @@ impl ClientControlRequest {
|
||||
credential: &Credential,
|
||||
shared_key: &SharedKeys,
|
||||
iv: IV,
|
||||
) -> Option<Self> {
|
||||
match bincode::serialize(credential) {
|
||||
Ok(serialized_credential) => {
|
||||
let enc_credential =
|
||||
shared_key.encrypt_and_tag(&serialized_credential, Some(iv.inner()));
|
||||
) -> Self {
|
||||
let serialized_credential = credential.as_bytes();
|
||||
let enc_credential = shared_key.encrypt_and_tag(&serialized_credential, Some(iv.inner()));
|
||||
|
||||
Some(ClientControlRequest::BandwidthCredential {
|
||||
enc_credential,
|
||||
iv: iv.to_bytes(),
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
ClientControlRequest::BandwidthCredential {
|
||||
enc_credential,
|
||||
iv: iv.to_bytes(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,8 +162,9 @@ impl ClientControlRequest {
|
||||
shared_key: &SharedKeys,
|
||||
iv: IV,
|
||||
) -> Result<Credential, GatewayRequestsError> {
|
||||
let credential = shared_key.decrypt_tagged(&enc_credential, Some(iv.inner()))?;
|
||||
bincode::deserialize(&credential).map_err(|_| GatewayRequestsError::MalformedEncryption)
|
||||
let credential_bytes = shared_key.decrypt_tagged(&enc_credential, Some(iv.inner()))?;
|
||||
Credential::from_bytes(&credential_bytes)
|
||||
.map_err(|_| GatewayRequestsError::MalformedEncryption)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
use coconut_interface::Credential;
|
||||
#[cfg(feature = "coconut")]
|
||||
use credentials::error::Error;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use credentials::token::bandwidth::TokenCredential;
|
||||
|
||||
@@ -22,12 +17,13 @@ impl Bandwidth {
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
impl TryFrom<Credential> for Bandwidth {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(credential: Credential) -> Result<Self, Self::Error> {
|
||||
let value = credential.voucher_value()?;
|
||||
Ok(Self { value })
|
||||
impl From<Credential> for Bandwidth {
|
||||
fn from(credential: Credential) -> Self {
|
||||
let token_value = credential.voucher_value();
|
||||
let bandwidth_bytes = token_value * network_defaults::BYTES_PER_UTOKEN;
|
||||
Bandwidth {
|
||||
value: bandwidth_bytes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,8 +42,8 @@ pub(crate) enum RequestHandlingError {
|
||||
#[error("Provided bandwidth credential asks for more bandwidth than it is supported to add at once (credential value: {0}, supported: {}). Try to split it before attempting again", i64::MAX)]
|
||||
UnsupportedBandwidthValue(u64),
|
||||
|
||||
#[error("Provided bandwidth credential did not verify correctly")]
|
||||
InvalidBandwidthCredential,
|
||||
#[error("Provided bandwidth credential did not verify correctly on {0}")]
|
||||
InvalidBandwidthCredential(String),
|
||||
|
||||
#[error("This gateway is not running in the disabled credentials mode")]
|
||||
NotInDisabledCredentialsMode,
|
||||
@@ -65,8 +65,12 @@ pub(crate) enum RequestHandlingError {
|
||||
NymdError(#[from] validator_client::nymd::error::NymdError),
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
#[error("Provided coconut bandwidth credential did not have expected structure - {0}")]
|
||||
CoconutBandwidthCredentialError(#[from] credentials::error::Error),
|
||||
#[error("Validator API error")]
|
||||
APIError(#[from] validator_client::ValidatorClientError),
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
#[error("Not enough validator API endpoints provided. Needed {needed}, received {received}")]
|
||||
NotEnoughValidatorAPIs { received: usize, needed: usize },
|
||||
}
|
||||
|
||||
impl RequestHandlingError {
|
||||
@@ -208,11 +212,55 @@ where
|
||||
iv,
|
||||
)?;
|
||||
|
||||
if !credential.verify(&self.inner.aggregated_verification_key) {
|
||||
return Err(RequestHandlingError::InvalidBandwidthCredential);
|
||||
if !credential.verify(
|
||||
self.inner
|
||||
.coconut_verifier
|
||||
.as_ref()
|
||||
.aggregated_verification_key(),
|
||||
) {
|
||||
return Err(RequestHandlingError::InvalidBandwidthCredential(
|
||||
String::from("credential failed to verify on gateway"),
|
||||
));
|
||||
}
|
||||
|
||||
let bandwidth = Bandwidth::try_from(credential)?;
|
||||
let req = coconut_interface::ProposeReleaseFundsRequestBody::new(credential.clone());
|
||||
let proposal_id = self
|
||||
.inner
|
||||
.coconut_verifier
|
||||
.api_clients()
|
||||
.get(0)
|
||||
.ok_or(RequestHandlingError::NotEnoughValidatorAPIs {
|
||||
needed: 1,
|
||||
received: 0,
|
||||
})?
|
||||
.propose_release_funds(&req)
|
||||
.await?
|
||||
.proposal_id;
|
||||
|
||||
let req = coconut_interface::VerifyCredentialBody::new(credential.clone(), proposal_id);
|
||||
for client in self.inner.coconut_verifier.api_clients().iter().skip(1) {
|
||||
if !client
|
||||
.verify_bandwidth_credential(&req)
|
||||
.await?
|
||||
.verification_result
|
||||
{
|
||||
debug!("Validator {} didn't accept the credential. It will probably vote No on the spending proposal", client.validator_api.current_url());
|
||||
}
|
||||
}
|
||||
|
||||
let req = coconut_interface::ExecuteReleaseFundsRequestBody::new(proposal_id);
|
||||
self.inner
|
||||
.coconut_verifier
|
||||
.api_clients()
|
||||
.get(0)
|
||||
.ok_or(RequestHandlingError::NotEnoughValidatorAPIs {
|
||||
needed: 1,
|
||||
received: 0,
|
||||
})?
|
||||
.execute_release_funds(&req)
|
||||
.await?;
|
||||
|
||||
let bandwidth = Bandwidth::from(credential);
|
||||
let bandwidth_value = bandwidth.value();
|
||||
|
||||
if bandwidth_value > i64::MAX as u64 {
|
||||
@@ -254,11 +302,15 @@ where
|
||||
.inner
|
||||
.check_local_identity(&credential.gateway_identity())
|
||||
{
|
||||
return Err(RequestHandlingError::InvalidBandwidthCredential);
|
||||
return Err(RequestHandlingError::InvalidBandwidthCredential(
|
||||
String::from("gateway"),
|
||||
));
|
||||
}
|
||||
|
||||
if !credential.verify_signature() {
|
||||
return Err(RequestHandlingError::InvalidBandwidthCredential);
|
||||
return Err(RequestHandlingError::InvalidBandwidthCredential(
|
||||
String::from("gateway"),
|
||||
));
|
||||
}
|
||||
debug!("Verifying Ethereum for token burn...");
|
||||
let gateway_owner = self
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use coconut_interface::VerificationKey;
|
||||
use validator_client::ApiClient;
|
||||
|
||||
pub struct CoconutVerifier {
|
||||
api_clients: Vec<ApiClient>,
|
||||
aggregated_verification_key: VerificationKey,
|
||||
}
|
||||
|
||||
impl CoconutVerifier {
|
||||
pub fn new(api_clients: Vec<ApiClient>, aggregated_verification_key: VerificationKey) -> Self {
|
||||
CoconutVerifier {
|
||||
api_clients,
|
||||
aggregated_verification_key,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn api_clients(&self) -> &Vec<ApiClient> {
|
||||
&self.api_clients
|
||||
}
|
||||
|
||||
pub fn aggregated_verification_key(&self) -> &VerificationKey {
|
||||
&self.aggregated_verification_key
|
||||
}
|
||||
}
|
||||
@@ -39,16 +39,9 @@ impl ERC20Bridge {
|
||||
.expect("The list of validators is empty");
|
||||
let mnemonic =
|
||||
Mnemonic::from_str(&cosmos_mnemonic).expect("Invalid Cosmos mnemonic provided");
|
||||
let nymd_client = NymdClient::connect_with_mnemonic(
|
||||
DEFAULT_NETWORK,
|
||||
nymd_url.as_ref(),
|
||||
AccountId::from_str(DEFAULT_NETWORK.mixnet_contract_address()).ok(),
|
||||
None,
|
||||
AccountId::from_str(DEFAULT_NETWORK.bandwidth_claim_contract_address()).ok(),
|
||||
mnemonic,
|
||||
None,
|
||||
)
|
||||
.expect("Could not create nymd client");
|
||||
let nymd_client =
|
||||
NymdClient::connect_with_mnemonic(DEFAULT_NETWORK, nymd_url.as_ref(), mnemonic, None)
|
||||
.expect("Could not create nymd client");
|
||||
|
||||
ERC20Bridge {
|
||||
contract: eth_contract(web3.clone()),
|
||||
@@ -95,7 +88,9 @@ impl ERC20Bridge {
|
||||
}
|
||||
}
|
||||
|
||||
Err(RequestHandlingError::InvalidBandwidthCredential)
|
||||
Err(RequestHandlingError::InvalidBandwidthCredential(
|
||||
String::from("gateway"),
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) async fn verify_gateway_owner(
|
||||
@@ -103,17 +98,22 @@ impl ERC20Bridge {
|
||||
gateway_owner: String,
|
||||
gateway_identity: &PublicKey,
|
||||
) -> Result<(), RequestHandlingError> {
|
||||
let owner_address = AccountId::from_str(&gateway_owner)
|
||||
.map_err(|_| RequestHandlingError::InvalidBandwidthCredential)?;
|
||||
let owner_address = AccountId::from_str(&gateway_owner).map_err(|_| {
|
||||
RequestHandlingError::InvalidBandwidthCredential(String::from("gateway"))
|
||||
})?;
|
||||
let gateway_bond = self
|
||||
.nymd_client
|
||||
.owns_gateway(&owner_address)
|
||||
.await?
|
||||
.ok_or(RequestHandlingError::InvalidBandwidthCredential)?;
|
||||
.ok_or_else(|| {
|
||||
RequestHandlingError::InvalidBandwidthCredential(String::from("gateway"))
|
||||
})?;
|
||||
if gateway_bond.gateway.identity_key == gateway_identity.to_base58_string() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(RequestHandlingError::InvalidBandwidthCredential)
|
||||
Err(RequestHandlingError::InvalidBandwidthCredential(
|
||||
String::from("gateway"),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,9 +121,7 @@ impl ERC20Bridge {
|
||||
&self,
|
||||
credential: &TokenCredential,
|
||||
) -> Result<(), RequestHandlingError> {
|
||||
// It's ok to unwrap here, as the cosmos contract is set correctly
|
||||
let erc20_bridge_contract_address =
|
||||
self.nymd_client.erc20_bridge_contract_address().unwrap();
|
||||
let bandwidth_claim_contract_address = self.nymd_client.bandwidth_claim_contract_address();
|
||||
let req = ExecuteMsg::LinkPayment {
|
||||
data: LinkPaymentData::new(
|
||||
credential.verification_key().to_bytes(),
|
||||
@@ -134,7 +132,7 @@ impl ERC20Bridge {
|
||||
};
|
||||
self.nymd_client
|
||||
.execute(
|
||||
erc20_bridge_contract_address,
|
||||
bandwidth_claim_contract_address,
|
||||
&req,
|
||||
Default::default(),
|
||||
"Linking payment",
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::node::client_handling::active_clients::ActiveClientsStore;
|
||||
#[cfg(feature = "coconut")]
|
||||
use crate::node::client_handling::websocket::connection_handler::coconut::CoconutVerifier;
|
||||
use crate::node::client_handling::websocket::connection_handler::{
|
||||
AuthenticatedHandler, ClientDetails, InitialAuthResult, SocketStream,
|
||||
};
|
||||
@@ -27,9 +29,6 @@ use thiserror::Error;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use tokio_tungstenite::tungstenite::{protocol::Message, Error as WsError};
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
use coconut_interface::VerificationKey;
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use crate::node::client_handling::websocket::connection_handler::eth_events::ERC20Bridge;
|
||||
|
||||
@@ -77,11 +76,10 @@ pub(crate) struct FreshHandler<R, S, St> {
|
||||
pub(crate) socket_connection: SocketStream<S>,
|
||||
pub(crate) storage: St,
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
pub(crate) aggregated_verification_key: VerificationKey,
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub(crate) erc20_bridge: Arc<ERC20Bridge>,
|
||||
#[cfg(feature = "coconut")]
|
||||
pub(crate) coconut_verifier: Arc<CoconutVerifier>,
|
||||
}
|
||||
|
||||
impl<R, S, St> FreshHandler<R, S, St>
|
||||
@@ -102,7 +100,7 @@ where
|
||||
local_identity: Arc<identity::KeyPair>,
|
||||
storage: St,
|
||||
active_clients_store: ActiveClientsStore,
|
||||
#[cfg(feature = "coconut")] aggregated_verification_key: VerificationKey,
|
||||
#[cfg(feature = "coconut")] coconut_verifier: Arc<CoconutVerifier>,
|
||||
#[cfg(not(feature = "coconut"))] erc20_bridge: Arc<ERC20Bridge>,
|
||||
) -> Self {
|
||||
FreshHandler {
|
||||
@@ -114,7 +112,7 @@ where
|
||||
local_identity,
|
||||
storage,
|
||||
#[cfg(feature = "coconut")]
|
||||
aggregated_verification_key,
|
||||
coconut_verifier,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
erc20_bridge,
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ pub(crate) use self::authenticated::AuthenticatedHandler;
|
||||
pub(crate) use self::fresh::FreshHandler;
|
||||
|
||||
mod authenticated;
|
||||
#[cfg(feature = "coconut")]
|
||||
pub(crate) mod coconut;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub(crate) mod eth_events;
|
||||
mod fresh;
|
||||
|
||||
@@ -14,7 +14,7 @@ use std::sync::Arc;
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
use coconut_interface::VerificationKey;
|
||||
use crate::node::client_handling::websocket::connection_handler::coconut::CoconutVerifier;
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use crate::node::client_handling::websocket::connection_handler::eth_events::ERC20Bridge;
|
||||
@@ -25,8 +25,7 @@ pub(crate) struct Listener {
|
||||
disabled_credentials_mode: bool,
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
aggregated_verification_key: VerificationKey,
|
||||
|
||||
pub(crate) coconut_verifier: Arc<CoconutVerifier>,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
erc20_bridge: Arc<ERC20Bridge>,
|
||||
}
|
||||
@@ -36,7 +35,7 @@ impl Listener {
|
||||
address: SocketAddr,
|
||||
local_identity: Arc<identity::KeyPair>,
|
||||
disabled_credentials_mode: bool,
|
||||
#[cfg(feature = "coconut")] aggregated_verification_key: VerificationKey,
|
||||
#[cfg(feature = "coconut")] coconut_verifier: Arc<CoconutVerifier>,
|
||||
#[cfg(not(feature = "coconut"))] erc20_bridge: ERC20Bridge,
|
||||
) -> Self {
|
||||
Listener {
|
||||
@@ -44,7 +43,7 @@ impl Listener {
|
||||
local_identity,
|
||||
disabled_credentials_mode,
|
||||
#[cfg(feature = "coconut")]
|
||||
aggregated_verification_key,
|
||||
coconut_verifier,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
erc20_bridge: Arc::new(erc20_bridge),
|
||||
}
|
||||
@@ -84,7 +83,7 @@ impl Listener {
|
||||
storage.clone(),
|
||||
active_clients_store.clone(),
|
||||
#[cfg(feature = "coconut")]
|
||||
self.aggregated_verification_key.clone(),
|
||||
Arc::clone(&self.coconut_verifier),
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
Arc::clone(&self.erc20_bridge),
|
||||
);
|
||||
|
||||
+25
-8
@@ -18,11 +18,11 @@ use std::process;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::config::persistence::pathfinder::GatewayPathfinder;
|
||||
#[cfg(feature = "coconut")]
|
||||
use crate::node::client_handling::websocket::connection_handler::coconut::CoconutVerifier;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use crate::node::client_handling::websocket::connection_handler::eth_events::ERC20Bridge;
|
||||
#[cfg(feature = "coconut")]
|
||||
use coconut_interface::VerificationKey;
|
||||
#[cfg(feature = "coconut")]
|
||||
use credentials::obtain_aggregate_verification_key;
|
||||
|
||||
use self::storage::PersistentStorage;
|
||||
@@ -175,7 +175,7 @@ where
|
||||
&self,
|
||||
forwarding_channel: MixForwardingSender,
|
||||
active_clients_store: ActiveClientsStore,
|
||||
#[cfg(feature = "coconut")] verification_key: VerificationKey,
|
||||
#[cfg(feature = "coconut")] coconut_verifier: Arc<CoconutVerifier>,
|
||||
#[cfg(not(feature = "coconut"))] erc20_bridge: ERC20Bridge,
|
||||
) {
|
||||
info!("Starting client [web]socket listener...");
|
||||
@@ -190,7 +190,7 @@ where
|
||||
Arc::clone(&self.identity_keypair),
|
||||
self.config.get_disabled_credentials_mode(),
|
||||
#[cfg(feature = "coconut")]
|
||||
verification_key,
|
||||
coconut_verifier,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
erc20_bridge,
|
||||
)
|
||||
@@ -227,13 +227,27 @@ where
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: ask DH whether this function still makes sense in ^0.10
|
||||
async fn check_if_same_ip_gateway_exists(&self) -> Option<String> {
|
||||
fn random_api_client(&self) -> validator_client::ApiClient {
|
||||
let endpoints = self.config.get_validator_api_endpoints();
|
||||
let validator_api = endpoints
|
||||
.choose(&mut thread_rng())
|
||||
.expect("The list of validator apis is empty");
|
||||
let validator_client = validator_client::ApiClient::new(validator_api.clone());
|
||||
|
||||
validator_client::ApiClient::new(validator_api.clone())
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
fn all_api_clients(&self) -> Vec<validator_client::ApiClient> {
|
||||
self.config
|
||||
.get_validator_api_endpoints()
|
||||
.into_iter()
|
||||
.map(validator_client::ApiClient::new)
|
||||
.collect()
|
||||
}
|
||||
|
||||
// TODO: ask DH whether this function still makes sense in ^0.10
|
||||
async fn check_if_same_ip_gateway_exists(&self) -> Option<String> {
|
||||
let validator_client = self.random_api_client();
|
||||
|
||||
let existing_gateways = match validator_client.get_cached_gateways().await {
|
||||
Ok(gateways) => gateways,
|
||||
@@ -271,6 +285,9 @@ where
|
||||
obtain_aggregate_verification_key(&self.config.get_validator_api_endpoints())
|
||||
.await
|
||||
.expect("failed to contact validators to obtain their verification keys");
|
||||
#[cfg(feature = "coconut")]
|
||||
let coconut_verifier =
|
||||
CoconutVerifier::new(self.all_api_clients(), validators_verification_key);
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
let erc20_bridge = ERC20Bridge::new(
|
||||
@@ -291,7 +308,7 @@ where
|
||||
mix_forwarding_channel,
|
||||
active_clients_store,
|
||||
#[cfg(feature = "coconut")]
|
||||
validators_verification_key,
|
||||
Arc::new(coconut_verifier),
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
erc20_bridge,
|
||||
);
|
||||
|
||||
@@ -244,14 +244,18 @@ impl MixNode {
|
||||
atomic_verloc_results
|
||||
}
|
||||
|
||||
// TODO: ask DH whether this function still makes sense in ^0.10
|
||||
async fn check_if_same_ip_node_exists(&mut self) -> Option<String> {
|
||||
fn random_api_client(&self) -> validator_client::ApiClient {
|
||||
let endpoints = self.config.get_validator_api_endpoints();
|
||||
let validator_api = endpoints
|
||||
.choose(&mut thread_rng())
|
||||
.expect("The list of validator apis is empty");
|
||||
let validator_client = validator_client::ApiClient::new(validator_api.clone());
|
||||
|
||||
validator_client::ApiClient::new(validator_api.clone())
|
||||
}
|
||||
|
||||
// TODO: ask DH whether this function still makes sense in ^0.10
|
||||
async fn check_if_same_ip_node_exists(&mut self) -> Option<String> {
|
||||
let validator_client = self.random_api_client();
|
||||
let existing_nodes = match validator_client.get_cached_mixnodes().await {
|
||||
Ok(nodes) => nodes,
|
||||
Err(err) => {
|
||||
|
||||
Generated
+69
@@ -709,6 +709,15 @@ dependencies = [
|
||||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "coconut-bandwidth-contract-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "coconut-interface"
|
||||
version = "0.1.0"
|
||||
@@ -1096,6 +1105,42 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw-utils"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "babd2c090f39d07ce5bf2556962305e795daa048ce20a93709eb591476e4a29e"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw3"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f871854338a54c7bb094d16ffe17212b93b146d9659dbce4c9402a9b77e240ef"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw4"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4476d6a7c13c46ed9ff260bd0e1cf648dc37b13f483822e1ff2a431f0f6ee52"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.10.2"
|
||||
@@ -1492,6 +1537,14 @@ version = "2.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71"
|
||||
|
||||
[[package]]
|
||||
name = "execute"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
version = "0.6.7"
|
||||
@@ -2801,6 +2854,18 @@ dependencies = [
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multisig-contract-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
"cw3",
|
||||
"cw4",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.8"
|
||||
@@ -5526,16 +5591,20 @@ dependencies = [
|
||||
"async-trait",
|
||||
"base64",
|
||||
"bip39",
|
||||
"coconut-bandwidth-contract-common",
|
||||
"coconut-interface",
|
||||
"colored",
|
||||
"config",
|
||||
"cosmrs",
|
||||
"cosmwasm-std",
|
||||
"cw3",
|
||||
"execute",
|
||||
"flate2",
|
||||
"futures",
|
||||
"itertools",
|
||||
"log",
|
||||
"mixnet-contract-common",
|
||||
"multisig-contract-common",
|
||||
"network-defaults",
|
||||
"prost",
|
||||
"reqwest",
|
||||
|
||||
@@ -215,34 +215,31 @@ impl Config {
|
||||
.flat_map(|c| c.validators().cloned())
|
||||
}
|
||||
|
||||
pub fn get_mixnet_contract_address(&self, network: WalletNetwork) -> Option<CosmosAccountId> {
|
||||
pub fn get_mixnet_contract_address(&self, network: WalletNetwork) -> CosmosAccountId {
|
||||
self.base
|
||||
.networks
|
||||
.mixnet_contract_address(network.into())
|
||||
.expect("No mixnet contract address found in config")
|
||||
.parse()
|
||||
.ok()
|
||||
.expect("Wrong format for mixnet contract address")
|
||||
}
|
||||
|
||||
pub fn get_vesting_contract_address(&self, network: WalletNetwork) -> Option<CosmosAccountId> {
|
||||
pub fn get_vesting_contract_address(&self, network: WalletNetwork) -> CosmosAccountId {
|
||||
self.base
|
||||
.networks
|
||||
.vesting_contract_address(network.into())
|
||||
.expect("No vesting contract address found in config")
|
||||
.parse()
|
||||
.ok()
|
||||
.expect("Wrong format for vesting contract address")
|
||||
}
|
||||
|
||||
pub fn get_bandwidth_claim_contract_address(
|
||||
&self,
|
||||
network: WalletNetwork,
|
||||
) -> Option<CosmosAccountId> {
|
||||
pub fn get_bandwidth_claim_contract_address(&self, network: WalletNetwork) -> CosmosAccountId {
|
||||
self.base
|
||||
.networks
|
||||
.bandwidth_claim_contract_address(network.into())
|
||||
.expect("No bandwidth claim contract address found in config")
|
||||
.parse()
|
||||
.ok()
|
||||
.expect("Wrong format for bandwidth claim contract address")
|
||||
}
|
||||
|
||||
pub fn select_validator_nymd_url(&mut self, nymd_url: Url, network: WalletNetwork) {
|
||||
|
||||
@@ -66,6 +66,10 @@ fn main() {
|
||||
mixnet::delegate::get_all_mix_delegations,
|
||||
mixnet::delegate::undelegate_from_mixnode,
|
||||
mixnet::epoch::get_current_epoch,
|
||||
mixnet::rewards::claim_delegator_reward,
|
||||
mixnet::rewards::claim_operator_reward,
|
||||
mixnet::rewards::compound_operator_reward,
|
||||
mixnet::rewards::compound_delegator_reward,
|
||||
mixnet::send::send,
|
||||
network_config::add_validator,
|
||||
network_config::get_validator_api_urls,
|
||||
@@ -86,6 +90,10 @@ fn main() {
|
||||
validator_api::status::mixnode_reward_estimation,
|
||||
validator_api::status::mixnode_stake_saturation,
|
||||
validator_api::status::mixnode_status,
|
||||
vesting::rewards::vesting_claim_delegator_reward,
|
||||
vesting::rewards::vesting_claim_operator_reward,
|
||||
vesting::rewards::vesting_compound_operator_reward,
|
||||
vesting::rewards::vesting_compound_delegator_reward,
|
||||
vesting::bond::vesting_bond_gateway,
|
||||
vesting::bond::vesting_bond_mixnode,
|
||||
vesting::bond::vesting_unbond_gateway,
|
||||
@@ -123,6 +131,14 @@ fn main() {
|
||||
simulate::vesting::simulate_vesting_unbond_mixnode,
|
||||
simulate::vesting::simulate_vesting_update_mixnode,
|
||||
simulate::vesting::simulate_withdraw_vested_coins,
|
||||
simulate::vesting::simulate_vesting_claim_delegator_reward,
|
||||
simulate::vesting::simulate_vesting_claim_operator_reward,
|
||||
simulate::vesting::simulate_vesting_compound_operator_reward,
|
||||
simulate::vesting::simulate_vesting_compound_delegator_reward,
|
||||
simulate::mixnet::simulate_claim_delegator_reward,
|
||||
simulate::mixnet::simulate_claim_operator_reward,
|
||||
simulate::mixnet::simulate_compound_operator_reward,
|
||||
simulate::mixnet::simulate_compound_delegator_reward,
|
||||
])
|
||||
.menu(Menu::new().add_default_app_submenu_if_macos())
|
||||
.run(tauri::generate_context!())
|
||||
|
||||
@@ -73,7 +73,7 @@ pub async fn switch_network(
|
||||
let denom = network.base_mix_denom();
|
||||
|
||||
Account::new(
|
||||
client.nymd.mixnet_contract_address()?.to_string(),
|
||||
client.nymd.mixnet_contract_address().to_string(),
|
||||
client.nymd.address().to_string(),
|
||||
denom.into(),
|
||||
)
|
||||
@@ -156,7 +156,7 @@ async fn _connect_with_mnemonic(
|
||||
.find(|client| WalletNetwork::from(client.network) == default_network);
|
||||
let account_for_default_network = match client_for_default_network {
|
||||
Some(client) => Ok(Account::new(
|
||||
client.nymd.mixnet_contract_address()?.to_string(),
|
||||
client.nymd.mixnet_contract_address().to_string(),
|
||||
client.nymd.address().to_string(),
|
||||
default_network.base_mix_denom().into(),
|
||||
)),
|
||||
@@ -250,14 +250,12 @@ fn create_clients(
|
||||
log::info!("Connecting to: api_url: {api_url} for {network}");
|
||||
|
||||
let mut client = validator_client::Client::new_signing(
|
||||
validator_client::Config::new(
|
||||
network.into(),
|
||||
nymd_url,
|
||||
api_url,
|
||||
config.get_mixnet_contract_address(network),
|
||||
config.get_vesting_contract_address(network),
|
||||
config.get_bandwidth_claim_contract_address(network),
|
||||
),
|
||||
validator_client::Config::new(network.into(), nymd_url, api_url)
|
||||
.with_mixnode_contract_address(config.get_mixnet_contract_address(network))
|
||||
.with_vesting_contract_address(config.get_vesting_contract_address(network))
|
||||
.with_bandwidth_claim_contract_address(
|
||||
config.get_bandwidth_claim_contract_address(network),
|
||||
),
|
||||
mnemonic.clone(),
|
||||
)?;
|
||||
client.set_nymd_simulated_gas_multiplier(CUSTOM_SIMULATED_GAS_MULTIPLIER);
|
||||
|
||||
@@ -3,4 +3,5 @@ pub mod admin;
|
||||
pub mod bond;
|
||||
pub mod delegate;
|
||||
pub mod epoch;
|
||||
pub mod rewards;
|
||||
pub mod send;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
use crate::error::BackendError;
|
||||
use crate::nymd_client;
|
||||
use crate::state::State;
|
||||
use mixnet_contract_common::IdentityKey;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use validator_client::nymd::Fee;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn claim_operator_reward(
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
nymd_client!(state)
|
||||
.execute_claim_operator_reward(fee)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn compound_operator_reward(
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
nymd_client!(state)
|
||||
.execute_compound_operator_reward(fee)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn claim_delegator_reward(
|
||||
mix_identity: IdentityKey,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
nymd_client!(state)
|
||||
.execute_claim_delegator_reward(mix_identity, fee)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn compound_delegator_reward(
|
||||
mix_identity: IdentityKey,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
nymd_client!(state)
|
||||
.execute_compound_delegator_reward(mix_identity, fee)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -18,7 +18,7 @@ pub async fn simulate_update_contract_settings(
|
||||
let mixnet_contract_settings_params: ContractStateParams = params.try_into()?;
|
||||
|
||||
let client = guard.current_client()?;
|
||||
let mixnet_contract = client.nymd.mixnet_contract_address()?;
|
||||
let mixnet_contract = client.nymd.mixnet_contract_address();
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
|
||||
let msg = client.nymd.wrap_contract_execute_message(
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
use crate::error::BackendError;
|
||||
use crate::operations::simulate::{FeeDetails, SimulateResult};
|
||||
use crate::State;
|
||||
use mixnet_contract_common::IdentityKey;
|
||||
use mixnet_contract_common::{ExecuteMsg, Gateway, MixNode};
|
||||
use nym_types::currency::DecCoin;
|
||||
use std::sync::Arc;
|
||||
@@ -20,7 +21,7 @@ pub async fn simulate_bond_gateway(
|
||||
let pledge = guard.attempt_convert_to_base_coin(pledge)?;
|
||||
|
||||
let client = guard.current_client()?;
|
||||
let mixnet_contract = client.nymd.mixnet_contract_address()?;
|
||||
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
|
||||
@@ -44,7 +45,7 @@ pub async fn simulate_unbond_gateway(
|
||||
let guard = state.read().await;
|
||||
|
||||
let client = guard.current_client()?;
|
||||
let mixnet_contract = client.nymd.mixnet_contract_address()?;
|
||||
let mixnet_contract = client.nymd.mixnet_contract_address();
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
|
||||
let msg = client.nymd.wrap_contract_execute_message(
|
||||
@@ -68,7 +69,7 @@ pub async fn simulate_bond_mixnode(
|
||||
let pledge = guard.attempt_convert_to_base_coin(pledge)?;
|
||||
|
||||
let client = guard.current_client()?;
|
||||
let mixnet_contract = client.nymd.mixnet_contract_address()?;
|
||||
let mixnet_contract = client.nymd.mixnet_contract_address();
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
|
||||
let msg = client.nymd.wrap_contract_execute_message(
|
||||
@@ -91,7 +92,7 @@ pub async fn simulate_unbond_mixnode(
|
||||
let guard = state.read().await;
|
||||
|
||||
let client = guard.current_client()?;
|
||||
let mixnet_contract = client.nymd.mixnet_contract_address()?;
|
||||
let mixnet_contract = client.nymd.mixnet_contract_address();
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
|
||||
let msg = client.nymd.wrap_contract_execute_message(
|
||||
@@ -112,7 +113,7 @@ pub async fn simulate_update_mixnode(
|
||||
let guard = state.read().await;
|
||||
|
||||
let client = guard.current_client()?;
|
||||
let mixnet_contract = client.nymd.mixnet_contract_address()?;
|
||||
let mixnet_contract = client.nymd.mixnet_contract_address();
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
|
||||
let msg = client.nymd.wrap_contract_execute_message(
|
||||
@@ -137,7 +138,7 @@ pub async fn simulate_delegate_to_mixnode(
|
||||
let delegation = guard.attempt_convert_to_base_coin(amount)?;
|
||||
|
||||
let client = guard.current_client()?;
|
||||
let mixnet_contract = client.nymd.mixnet_contract_address()?;
|
||||
let mixnet_contract = client.nymd.mixnet_contract_address();
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
|
||||
let msg = client.nymd.wrap_contract_execute_message(
|
||||
@@ -160,7 +161,7 @@ pub async fn simulate_undelegate_from_mixnode(
|
||||
let guard = state.read().await;
|
||||
|
||||
let client = guard.current_client()?;
|
||||
let mixnet_contract = client.nymd.mixnet_contract_address()?;
|
||||
let mixnet_contract = client.nymd.mixnet_contract_address();
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
|
||||
let msg = client.nymd.wrap_contract_execute_message(
|
||||
@@ -174,3 +175,59 @@ pub async fn simulate_undelegate_from_mixnode(
|
||||
let result = client.nymd.simulate(vec![msg]).await?;
|
||||
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn simulate_claim_operator_reward(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
let client = guard.current_client()?;
|
||||
|
||||
let result = client.nymd.simulate_claim_operator_reward(None).await?;
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn simulate_compound_operator_reward(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
let client = guard.current_client()?;
|
||||
|
||||
let result = client.nymd.simulate_compound_operator_reward(None).await?;
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn simulate_claim_delegator_reward(
|
||||
mix_identity: IdentityKey,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
let client = guard.current_client()?;
|
||||
|
||||
let result = client
|
||||
.nymd
|
||||
.simulate_claim_delegator_reward(mix_identity, None)
|
||||
.await?;
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn simulate_compound_delegator_reward(
|
||||
mix_identity: IdentityKey,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
let client = guard.current_client()?;
|
||||
|
||||
let result = client
|
||||
.nymd
|
||||
.simulate_compound_delegator_reward(mix_identity, None)
|
||||
.await?;
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
use crate::error::BackendError;
|
||||
use crate::operations::simulate::{FeeDetails, SimulateResult};
|
||||
use crate::State;
|
||||
use mixnet_contract_common::IdentityKey;
|
||||
use mixnet_contract_common::{Gateway, MixNode};
|
||||
use nym_types::currency::DecCoin;
|
||||
use std::sync::Arc;
|
||||
@@ -21,7 +22,7 @@ pub async fn simulate_vesting_bond_gateway(
|
||||
let pledge = guard.attempt_convert_to_base_coin(pledge)?;
|
||||
|
||||
let client = guard.current_client()?;
|
||||
let vesting_contract = client.nymd.vesting_contract_address()?;
|
||||
let vesting_contract = client.nymd.vesting_contract_address();
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
|
||||
let msg = client.nymd.wrap_contract_execute_message(
|
||||
@@ -45,7 +46,7 @@ pub async fn simulate_vesting_unbond_gateway(
|
||||
let guard = state.read().await;
|
||||
|
||||
let client = guard.current_client()?;
|
||||
let vesting_contract = client.nymd.vesting_contract_address()?;
|
||||
let vesting_contract = client.nymd.vesting_contract_address();
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
|
||||
let msg = client.nymd.wrap_contract_execute_message(
|
||||
@@ -69,7 +70,7 @@ pub async fn simulate_vesting_bond_mixnode(
|
||||
let pledge = guard.attempt_convert_to_base_coin(pledge)?;
|
||||
|
||||
let client = guard.current_client()?;
|
||||
let vesting_contract = client.nymd.vesting_contract_address()?;
|
||||
let vesting_contract = client.nymd.vesting_contract_address();
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
|
||||
let msg = client.nymd.wrap_contract_execute_message(
|
||||
@@ -93,7 +94,7 @@ pub async fn simulate_vesting_unbond_mixnode(
|
||||
let guard = state.read().await;
|
||||
|
||||
let client = guard.current_client()?;
|
||||
let vesting_contract = client.nymd.vesting_contract_address()?;
|
||||
let vesting_contract = client.nymd.vesting_contract_address();
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
|
||||
let msg = client.nymd.wrap_contract_execute_message(
|
||||
@@ -114,7 +115,7 @@ pub async fn simulate_vesting_update_mixnode(
|
||||
let guard = state.read().await;
|
||||
|
||||
let client = guard.current_client()?;
|
||||
let vesting_contract = client.nymd.vesting_contract_address()?;
|
||||
let vesting_contract = client.nymd.vesting_contract_address();
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
|
||||
let msg = client.nymd.wrap_contract_execute_message(
|
||||
@@ -138,7 +139,7 @@ pub async fn simulate_withdraw_vested_coins(
|
||||
let amount = guard.attempt_convert_to_base_coin(amount)?.into();
|
||||
|
||||
let client = guard.current_client()?;
|
||||
let vesting_contract = client.nymd.vesting_contract_address()?;
|
||||
let vesting_contract = client.nymd.vesting_contract_address();
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
|
||||
let msg = client.nymd.wrap_contract_execute_message(
|
||||
@@ -150,3 +151,65 @@ pub async fn simulate_withdraw_vested_coins(
|
||||
let result = client.nymd.simulate(vec![msg]).await?;
|
||||
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn simulate_vesting_claim_operator_reward(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
let client = guard.current_client()?;
|
||||
|
||||
let result = client
|
||||
.nymd
|
||||
.simulate_vesting_claim_operator_reward(None)
|
||||
.await?;
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn simulate_vesting_compound_operator_reward(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
let client = guard.current_client()?;
|
||||
|
||||
let result = client
|
||||
.nymd
|
||||
.simulate_vesting_compound_operator_reward(None)
|
||||
.await?;
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn simulate_vesting_claim_delegator_reward(
|
||||
mix_identity: IdentityKey,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
let client = guard.current_client()?;
|
||||
|
||||
let result = client
|
||||
.nymd
|
||||
.simulate_vesting_claim_delegator_reward(mix_identity, None)
|
||||
.await?;
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn simulate_vesting_compound_delegator_reward(
|
||||
mix_identity: IdentityKey,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let guard = state.read().await;
|
||||
let client = guard.current_client()?;
|
||||
|
||||
let result = client
|
||||
.nymd
|
||||
.simulate_vesting_compound_delegator_reward(mix_identity, None)
|
||||
.await?;
|
||||
let gas_price = client.nymd.gas_price().clone();
|
||||
guard.create_detailed_fee(SimulateResult::new(result.gas_info, gas_price))
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ pub async fn get_pending_vesting_delegation_events(
|
||||
let guard = state.read().await;
|
||||
let reg = guard.registered_coins()?;
|
||||
let client = &guard.current_client()?.nymd;
|
||||
let vesting_contract = client.vesting_contract_address()?;
|
||||
let vesting_contract = client.vesting_contract_address();
|
||||
|
||||
let events = client
|
||||
.get_pending_delegation_events(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod bond;
|
||||
pub mod delegate;
|
||||
pub mod queries;
|
||||
pub mod rewards;
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
use crate::error::BackendError;
|
||||
use crate::nymd_client;
|
||||
use crate::state::State;
|
||||
use mixnet_contract_common::IdentityKey;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use validator_client::nymd::Fee;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn vesting_claim_operator_reward(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
nymd_client!(state)
|
||||
.execute_vesting_claim_operator_reward(None)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn vesting_compound_operator_reward(
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
nymd_client!(state)
|
||||
.execute_vesting_compound_operator_reward(fee)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn vesting_claim_delegator_reward(
|
||||
mix_identity: IdentityKey,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
nymd_client!(state)
|
||||
.execute_vesting_claim_delegator_reward(mix_identity, fee)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn vesting_compound_delegator_reward(
|
||||
mix_identity: IdentityKey,
|
||||
fee: Option<Fee>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
nymd_client!(state)
|
||||
.execute_vesting_compound_delegator_reward(mix_identity, fee)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,9 +1,2 @@
|
||||
export interface Gateway {
|
||||
host: string;
|
||||
mix_port: number;
|
||||
clients_port: number;
|
||||
location: string;
|
||||
sphinx_key: string;
|
||||
identity_key: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
export interface Gateway { host: string, mix_port: number, clients_port: number, location: string, sphinx_key: string, identity_key: string, version: string, }
|
||||
@@ -1,10 +1,2 @@
|
||||
export interface MixNode {
|
||||
host: string;
|
||||
mix_port: number;
|
||||
verloc_port: number;
|
||||
http_api_port: number;
|
||||
sphinx_key: string;
|
||||
identity_key: string;
|
||||
version: string;
|
||||
profit_margin_percent: number;
|
||||
}
|
||||
|
||||
export interface MixNode { host: string, mix_port: number, verloc_port: number, http_api_port: number, sphinx_key: string, identity_key: string, version: string, profit_margin_percent: number, }
|
||||
@@ -1 +1,2 @@
|
||||
export type RewardedSetNodeStatus = 'Active' | 'Standby';
|
||||
|
||||
export type RewardedSetNodeStatus = "Active" | "Standby";
|
||||
@@ -52,9 +52,11 @@ schemars = { version = "0.8", features = ["preserve_order"] }
|
||||
## internal
|
||||
coconut-bandwidth-contract-common = { path = "../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" }
|
||||
config = { path = "../common/config" }
|
||||
cosmwasm-std = "1.0.0-beta8"
|
||||
crypto = { path="../common/crypto" }
|
||||
gateway-client = { path="../common/client-libs/gateway-client" }
|
||||
mixnet-contract-common = { path= "../common/cosmwasm-smart-contracts/mixnet-contract" }
|
||||
multisig-contract-common = { path = "../common/cosmwasm-smart-contracts/multisig-contract" }
|
||||
nymsphinx = { path="../common/nymsphinx" }
|
||||
topology = { path="../common/topology" }
|
||||
validator-api-requests = { path = "validator-api-requests" }
|
||||
|
||||
@@ -2,9 +2,21 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::coconut::error::Result;
|
||||
use validator_client::nymd::TxResponse;
|
||||
use multisig_contract_common::msg::ProposalResponse;
|
||||
use validator_client::nymd::{Fee, TxResponse};
|
||||
|
||||
#[async_trait]
|
||||
pub trait Client {
|
||||
async fn get_tx(&self, tx_hash: &str) -> Result<TxResponse>;
|
||||
async fn get_proposal(&self, proposal_id: u64) -> Result<ProposalResponse>;
|
||||
async fn propose_release_funds(
|
||||
&self,
|
||||
title: String,
|
||||
blinded_serial_number: String,
|
||||
voucher_value: u128,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<u64>;
|
||||
async fn vote_proposal(&self, proposal_id: u64, vote_yes: bool, fee: Option<Fee>)
|
||||
-> Result<()>;
|
||||
async fn execute_proposal(&self, proposal_id: u64, fee: Option<Fee>) -> Result<()>;
|
||||
}
|
||||
|
||||
@@ -63,8 +63,22 @@ pub enum CoconutError {
|
||||
#[error("Error in coconut interface - {0}")]
|
||||
CoconutInterfaceError(#[from] coconut_interface::error::CoconutInterfaceError),
|
||||
|
||||
#[error("Could not create proposal for spending credential")]
|
||||
CreateProposalError,
|
||||
|
||||
#[error("Storage error - {0}")]
|
||||
StorageError(#[from] ValidatorApiStorageError),
|
||||
|
||||
#[error("Credentials error - {0}")]
|
||||
CredentialsError(#[from] credentials::error::Error),
|
||||
|
||||
#[error(
|
||||
"Incorrect credential proposal description. Expected blinded serial number in base 58"
|
||||
)]
|
||||
IncorrectProposal,
|
||||
|
||||
#[error("Internal error: {0}")]
|
||||
InternalError(String),
|
||||
}
|
||||
|
||||
impl<'r, 'o: 'r> Responder<'r, 'o> for CoconutError {
|
||||
|
||||
@@ -14,15 +14,19 @@ use crate::ValidatorApiStorage;
|
||||
|
||||
use coconut_interface::{
|
||||
Attribute, BlindSignRequest, BlindSignRequestBody, BlindedSignature, BlindedSignatureResponse,
|
||||
KeyPair, Parameters, VerificationKeyResponse,
|
||||
ExecuteReleaseFundsRequestBody, KeyPair, Parameters, ProposeReleaseFundsRequestBody,
|
||||
ProposeReleaseFundsResponse, VerificationKey, VerificationKeyResponse, VerifyCredentialBody,
|
||||
VerifyCredentialResponse,
|
||||
};
|
||||
use config::defaults::VALIDATOR_API_VERSION;
|
||||
use credentials::coconut::params::{
|
||||
ValidatorApiCredentialEncryptionAlgorithm, ValidatorApiCredentialHkdfAlgorithm,
|
||||
};
|
||||
use credentials::obtain_aggregate_verification_key;
|
||||
use crypto::asymmetric::encryption;
|
||||
use crypto::shared_key::new_ephemeral_shared_key;
|
||||
use crypto::symmetric::stream_cipher;
|
||||
use validator_client::validator_api::routes::{BANDWIDTH, COCONUT_ROUTES};
|
||||
|
||||
use getset::{CopyGetters, Getters};
|
||||
use rand_07::rngs::OsRng;
|
||||
@@ -30,26 +34,33 @@ use rocket::fairing::AdHoc;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::State as RocketState;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
use validator_client::validator_api::routes::{BANDWIDTH, COCONUT_ROUTES};
|
||||
use tokio::sync::Mutex;
|
||||
use url::Url;
|
||||
|
||||
pub struct State {
|
||||
client: Arc<RwLock<dyn LocalClient + Send + Sync>>,
|
||||
client: Arc<dyn LocalClient + Send + Sync>,
|
||||
key_pair: KeyPair,
|
||||
validator_apis: Vec<Url>,
|
||||
storage: ValidatorApiStorage,
|
||||
rng: Arc<Mutex<OsRng>>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub(crate) fn new<C>(client: C, key_pair: KeyPair, storage: ValidatorApiStorage) -> Self
|
||||
pub(crate) fn new<C>(
|
||||
client: C,
|
||||
key_pair: KeyPair,
|
||||
validator_apis: Vec<Url>,
|
||||
storage: ValidatorApiStorage,
|
||||
) -> Self
|
||||
where
|
||||
C: LocalClient + Send + Sync + 'static,
|
||||
{
|
||||
let client = Arc::new(RwLock::new(client));
|
||||
let client = Arc::new(client);
|
||||
let rng = Arc::new(Mutex::new(OsRng));
|
||||
Self {
|
||||
client,
|
||||
key_pair,
|
||||
validator_apis,
|
||||
storage,
|
||||
rng,
|
||||
}
|
||||
@@ -109,6 +120,10 @@ impl State {
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn verification_key(&self) -> Result<VerificationKey> {
|
||||
Ok(obtain_aggregate_verification_key(&self.validator_apis).await?)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Getters, CopyGetters, Debug)]
|
||||
@@ -135,11 +150,16 @@ impl InternalSignRequest {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stage<C>(client: C, key_pair: KeyPair, storage: ValidatorApiStorage) -> AdHoc
|
||||
pub fn stage<C>(
|
||||
client: C,
|
||||
key_pair: KeyPair,
|
||||
validator_apis: Vec<Url>,
|
||||
storage: ValidatorApiStorage,
|
||||
) -> AdHoc
|
||||
where
|
||||
C: LocalClient + Send + Sync + 'static,
|
||||
{
|
||||
let state = State::new(client, key_pair, storage);
|
||||
let state = State::new(client, key_pair, validator_apis, storage);
|
||||
AdHoc::on_ignite("Internal Sign Request Stage", |rocket| async {
|
||||
rocket.manage(state).mount(
|
||||
// this format! is so ugly...
|
||||
@@ -150,7 +170,10 @@ impl InternalSignRequest {
|
||||
routes![
|
||||
post_blind_sign,
|
||||
get_verification_key,
|
||||
post_partial_bandwidth_credential
|
||||
post_partial_bandwidth_credential,
|
||||
verify_bandwidth_credential,
|
||||
post_propose_release_funds,
|
||||
post_execute_release_funds
|
||||
],
|
||||
)
|
||||
})
|
||||
@@ -183,8 +206,6 @@ pub async fn post_blind_sign(
|
||||
}
|
||||
let tx = state
|
||||
.client
|
||||
.read()
|
||||
.await
|
||||
.get_tx(blind_sign_request_body.tx_hash())
|
||||
.await?;
|
||||
let encryption_key = extract_encryption_key(&blind_sign_request_body, tx).await?;
|
||||
@@ -226,3 +247,69 @@ pub async fn get_verification_key(
|
||||
state.key_pair.verification_key(),
|
||||
)))
|
||||
}
|
||||
|
||||
#[post("/verify-bandwidth-credential", data = "<verify_credential_body>")]
|
||||
pub async fn verify_bandwidth_credential(
|
||||
verify_credential_body: Json<VerifyCredentialBody>,
|
||||
state: &RocketState<State>,
|
||||
) -> Result<Json<VerifyCredentialResponse>> {
|
||||
let proposal_id = *verify_credential_body.0.proposal_id();
|
||||
let proposal = state.client.get_proposal(proposal_id).await?;
|
||||
// Proposal description is the blinded serial number
|
||||
if !verify_credential_body
|
||||
.0
|
||||
.credential()
|
||||
.has_blinded_serial_number(&proposal.description)?
|
||||
{
|
||||
return Err(CoconutError::IncorrectProposal);
|
||||
}
|
||||
let verification_key = state.verification_key().await?;
|
||||
let verification_result = verify_credential_body
|
||||
.0
|
||||
.credential()
|
||||
.verify(&verification_key);
|
||||
|
||||
// Vote yes or no on the proposal based on the verification result
|
||||
state
|
||||
.client
|
||||
.vote_proposal(proposal_id, verification_result, None)
|
||||
.await?;
|
||||
|
||||
Ok(Json(VerifyCredentialResponse::new(verification_result)))
|
||||
}
|
||||
|
||||
#[post("/propose-release-funds", data = "<propose_release_funds>")]
|
||||
pub async fn post_propose_release_funds(
|
||||
propose_release_funds: Json<ProposeReleaseFundsRequestBody>,
|
||||
state: &RocketState<State>,
|
||||
) -> Result<Json<ProposeReleaseFundsResponse>> {
|
||||
let verification_key = state.verification_key().await?;
|
||||
if !propose_release_funds
|
||||
.0
|
||||
.credential()
|
||||
.verify(&verification_key)
|
||||
{
|
||||
return Err(CoconutError::CreateProposalError);
|
||||
}
|
||||
|
||||
let title = String::from("Create proposal to spend a coconut credential");
|
||||
let blinded_serial_number = propose_release_funds.0.credential().blinded_serial_number();
|
||||
let voucher_value = propose_release_funds.0.credential().voucher_value() as u128;
|
||||
let proposal_id = state
|
||||
.client
|
||||
.propose_release_funds(title, blinded_serial_number, voucher_value, None)
|
||||
.await?;
|
||||
|
||||
Ok(Json(ProposeReleaseFundsResponse::new(proposal_id)))
|
||||
}
|
||||
|
||||
#[post("/execute-release-funds", data = "<execute_release_funds>")]
|
||||
pub async fn post_execute_release_funds(
|
||||
execute_release_funds: Json<ExecuteReleaseFundsRequestBody>,
|
||||
state: &RocketState<State>,
|
||||
) -> Result<Json<()>> {
|
||||
let proposal_id = *execute_release_funds.0.proposal_id();
|
||||
state.client.execute_proposal(proposal_id, None).await?;
|
||||
|
||||
Ok(Json(()))
|
||||
}
|
||||
|
||||
@@ -15,10 +15,11 @@ use credentials::coconut::params::{
|
||||
};
|
||||
use crypto::shared_key::recompute_shared_key;
|
||||
use crypto::symmetric::stream_cipher;
|
||||
use multisig_contract_common::msg::ProposalResponse;
|
||||
use nymcoconut::{
|
||||
prepare_blind_sign, ttp_keygen, Base58, BlindSignRequest, BlindedSignature, KeyPair, Parameters,
|
||||
};
|
||||
use validator_client::nymd::{tx::Hash, DeliverTx, Event, Tag, TxResponse};
|
||||
use validator_client::nymd::{tx::Hash, DeliverTx, Event, Fee, Tag, TxResponse};
|
||||
use validator_client::validator_api::routes::{
|
||||
API_VERSION, BANDWIDTH, COCONUT_BLIND_SIGN, COCONUT_PARTIAL_BANDWIDTH_CREDENTIAL,
|
||||
COCONUT_ROUTES, COCONUT_VERIFICATION_KEY,
|
||||
@@ -56,6 +57,37 @@ impl super::client::Client for DummyClient {
|
||||
.cloned()
|
||||
.ok_or(CoconutError::TxHashParseError)
|
||||
}
|
||||
|
||||
async fn get_proposal(&self, _proposal_id: u64) -> Result<ProposalResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn propose_release_funds(
|
||||
&self,
|
||||
_title: String,
|
||||
_blinded_serial_number: String,
|
||||
_voucher_value: u128,
|
||||
_fee: Option<Fee>,
|
||||
) -> Result<u64> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn vote_proposal(
|
||||
&self,
|
||||
_proposal_id: u64,
|
||||
_vote_yes: bool,
|
||||
_fee: Option<Fee>,
|
||||
) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn execute_proposal(
|
||||
&self,
|
||||
proposal_id: u64,
|
||||
fee: Option<Fee>,
|
||||
) -> crate::coconut::error::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tx_entry_fixture(tx_hash: &str) -> TxResponse {
|
||||
@@ -87,7 +119,12 @@ async fn check_signer_verif_key(key_pair: KeyPair) {
|
||||
let nymd_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let nymd_client = DummyClient::new(&nymd_db);
|
||||
|
||||
let rocket = rocket::build().attach(InternalSignRequest::stage(nymd_client, key_pair, storage));
|
||||
let rocket = rocket::build().attach(InternalSignRequest::stage(
|
||||
nymd_client,
|
||||
key_pair,
|
||||
vec![],
|
||||
storage,
|
||||
));
|
||||
|
||||
let client = Client::tracked(rocket)
|
||||
.await
|
||||
@@ -172,6 +209,7 @@ async fn signed_before() {
|
||||
let rocket = rocket::build().attach(InternalSignRequest::stage(
|
||||
nymd_client,
|
||||
key_pair,
|
||||
vec![],
|
||||
storage.clone(),
|
||||
));
|
||||
let client = Client::tracked(rocket)
|
||||
@@ -231,7 +269,7 @@ async fn state_functions() {
|
||||
let mut db_dir = std::env::temp_dir();
|
||||
db_dir.push(&key_pair.verification_key().to_bs58()[..8]);
|
||||
let storage = ValidatorApiStorage::init(db_dir).await.unwrap();
|
||||
let state = State::new(nymd_client, key_pair, storage.clone());
|
||||
let state = State::new(nymd_client, key_pair, vec![], storage.clone());
|
||||
|
||||
let tx_hash = String::from("6B27412050B823E58BB38447D7870BBC8CBE3C51C905BEA89D459ACCDA80A00E");
|
||||
assert!(state.signed_before(&tx_hash).await.unwrap().is_none());
|
||||
@@ -393,6 +431,7 @@ async fn blind_sign_correct() {
|
||||
let rocket = rocket::build().attach(InternalSignRequest::stage(
|
||||
nymd_client,
|
||||
key_pair,
|
||||
vec![],
|
||||
storage.clone(),
|
||||
));
|
||||
let client = Client::tracked(rocket)
|
||||
@@ -465,6 +504,7 @@ async fn signature_test() {
|
||||
let rocket = rocket::build().attach(InternalSignRequest::stage(
|
||||
nymd_client,
|
||||
key_pair,
|
||||
vec![],
|
||||
storage.clone(),
|
||||
));
|
||||
let client = Client::tracked(rocket)
|
||||
|
||||
@@ -2,16 +2,13 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::template::config_template;
|
||||
use config::defaults::{default_api_endpoints, DEFAULT_NETWORK};
|
||||
use config::defaults::DEFAULT_NETWORK;
|
||||
use config::NymConfig;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
use coconut_interface::{Base58, KeyPair};
|
||||
|
||||
mod template;
|
||||
|
||||
pub const DEFAULT_LOCAL_VALIDATOR: &str = "http://localhost:26657";
|
||||
@@ -82,17 +79,20 @@ impl NymConfig for Config {
|
||||
}
|
||||
|
||||
fn config_directory(&self) -> PathBuf {
|
||||
self.root_directory().join("config")
|
||||
self.root_directory().join(self.get_id()).join("config")
|
||||
}
|
||||
|
||||
fn data_directory(&self) -> PathBuf {
|
||||
self.root_directory().join("data")
|
||||
self.root_directory().join(self.get_id()).join("data")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct Base {
|
||||
/// ID specifies the human readable ID of this particular validator-api.
|
||||
id: String,
|
||||
|
||||
local_validator: Url,
|
||||
|
||||
/// Address of the validator contract managing the network
|
||||
@@ -105,6 +105,7 @@ pub struct Base {
|
||||
impl Default for Base {
|
||||
fn default() -> Self {
|
||||
Base {
|
||||
id: String::default(),
|
||||
local_validator: DEFAULT_LOCAL_VALIDATOR
|
||||
.parse()
|
||||
.expect("default local validator is malformed!"),
|
||||
@@ -128,11 +129,6 @@ pub struct NetworkMonitor {
|
||||
#[serde(default)]
|
||||
disabled_credentials_mode: bool,
|
||||
|
||||
/// Specifies list of all validators on the network issuing coconut credentials.
|
||||
/// A special care must be taken to ensure they are in correct order.
|
||||
/// The list must also contain THIS validator that is running the test
|
||||
all_validator_apis: Vec<Url>,
|
||||
|
||||
/// Specifies the interval at which the network monitor sends the test packets.
|
||||
#[serde(with = "humantime_serde")]
|
||||
run_interval: Duration,
|
||||
@@ -189,8 +185,10 @@ pub struct NetworkMonitor {
|
||||
}
|
||||
|
||||
impl NetworkMonitor {
|
||||
pub const DB_FILE: &'static str = "credentials_database.db";
|
||||
|
||||
fn default_credentials_database_path() -> PathBuf {
|
||||
Config::default_data_directory(None).join("credentials_database.db")
|
||||
Config::default_data_directory(None).join(Self::DB_FILE)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +199,6 @@ impl Default for NetworkMonitor {
|
||||
min_gateway_reliability: DEFAULT_MIN_GATEWAY_RELIABILITY,
|
||||
enabled: false,
|
||||
disabled_credentials_mode: true,
|
||||
all_validator_apis: default_api_endpoints(),
|
||||
run_interval: DEFAULT_MONITOR_RUN_INTERVAL,
|
||||
gateway_ping_interval: DEFAULT_GATEWAY_PING_INTERVAL,
|
||||
gateway_sending_rate: DEFAULT_GATEWAY_SENDING_RATE,
|
||||
@@ -230,8 +227,10 @@ pub struct NodeStatusAPI {
|
||||
}
|
||||
|
||||
impl NodeStatusAPI {
|
||||
pub const DB_FILE: &'static str = "db.sqlite";
|
||||
|
||||
fn default_database_path() -> PathBuf {
|
||||
Config::default_data_directory(None).join("db.sqlite")
|
||||
Config::default_data_directory(None).join(Self::DB_FILE)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,15 +278,31 @@ impl Default for Rewarding {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default)]
|
||||
#[cfg(feature = "coconut")]
|
||||
pub struct CoconutSigner {
|
||||
/// Specifies whether rewarding service is enabled in this process.
|
||||
enabled: bool,
|
||||
|
||||
/// Base58 encoded signing keypair
|
||||
keypair_bs58: String,
|
||||
/// Path to the signing keypair
|
||||
keypair_path: PathBuf,
|
||||
|
||||
/// Specifies list of all validators on the network issuing coconut credentials.
|
||||
/// A special care must be taken to ensure they are in correct order.
|
||||
/// The list must also contain THIS validator that is running the test
|
||||
all_validator_apis: Vec<Url>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
impl Default for CoconutSigner {
|
||||
fn default() -> Self {
|
||||
CoconutSigner {
|
||||
enabled: false,
|
||||
keypair_path: PathBuf::default(),
|
||||
all_validator_apis: config::defaults::default_api_endpoints(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -295,9 +310,18 @@ impl Config {
|
||||
Config::default()
|
||||
}
|
||||
|
||||
pub fn with_id(mut self, id: &str) -> Self {
|
||||
self.base.id = id.to_string();
|
||||
self.node_status_api.database_path =
|
||||
Config::default_data_directory(Some(id)).join(NodeStatusAPI::DB_FILE);
|
||||
self.network_monitor.credentials_database_path =
|
||||
Config::default_data_directory(Some(id)).join(NetworkMonitor::DB_FILE);
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
pub fn keypair(&self) -> KeyPair {
|
||||
KeyPair::try_from_bs58(self.coconut_signer.keypair_bs58.clone()).unwrap()
|
||||
pub fn keypair_path(&self) -> PathBuf {
|
||||
self.coconut_signer.keypair_path.clone()
|
||||
}
|
||||
|
||||
pub fn with_network_monitor_enabled(mut self, enabled: bool) -> Self {
|
||||
@@ -337,13 +361,14 @@ impl Config {
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
pub fn with_keypair<S: Into<String>>(mut self, keypair_bs58: S) -> Self {
|
||||
self.coconut_signer.keypair_bs58 = keypair_bs58.into();
|
||||
pub fn with_keypair_path(mut self, keypair_path: PathBuf) -> Self {
|
||||
self.coconut_signer.keypair_path = keypair_path;
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
pub fn with_custom_validator_apis(mut self, validator_api_urls: Vec<Url>) -> Self {
|
||||
self.network_monitor.all_validator_apis = validator_api_urls;
|
||||
self.coconut_signer.all_validator_apis = validator_api_urls;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -374,6 +399,10 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn get_id(&self) -> String {
|
||||
self.base.id.clone()
|
||||
}
|
||||
|
||||
pub fn get_network_monitor_enabled(&self) -> bool {
|
||||
self.network_monitor.enabled
|
||||
}
|
||||
@@ -474,7 +503,7 @@ impl Config {
|
||||
// fix dead code warnings as this method is only ever used with coconut feature
|
||||
#[cfg(feature = "coconut")]
|
||||
pub fn get_all_validator_api_endpoints(&self) -> Vec<Url> {
|
||||
self.network_monitor.all_validator_apis.clone()
|
||||
self.coconut_signer.all_validator_apis.clone()
|
||||
}
|
||||
|
||||
// TODO: Remove if still unused
|
||||
|
||||
@@ -10,12 +10,18 @@ pub(crate) fn config_template() -> &'static str {
|
||||
|
||||
[base]
|
||||
|
||||
# ID specifies the human readable ID of this particular validator-api.
|
||||
id = '{{ base.id }}'
|
||||
|
||||
# Validator server to which the API will be getting information about the network.
|
||||
local_validator = '{{ base.local_validator }}'
|
||||
|
||||
# Address of the validator contract managing the network.
|
||||
mixnet_contract_address = '{{ base.mixnet_contract_address }}'
|
||||
|
||||
# Mnemonic used for rewarding and validator interaction
|
||||
mnemonic = '{{ base.mnemonic }}'
|
||||
|
||||
##### network monitor config options #####
|
||||
|
||||
[network_monitor]
|
||||
@@ -26,15 +32,6 @@ enabled = {{ network_monitor.enabled }}
|
||||
# to claim bandwidth without presenting bandwidth credentials.
|
||||
disabled_credentials_mode = {{ network_monitor.disabled_credentials_mode }}
|
||||
|
||||
# Specifies list of all validators on the network issuing coconut credentials.
|
||||
# A special care must be taken to ensure they are in correct order.
|
||||
# The list must also contain THIS validator that is running the test
|
||||
all_validator_apis = [
|
||||
{{#each network_monitor.all_validator_apis }}
|
||||
'{{this}}',
|
||||
{{/each}}
|
||||
]
|
||||
|
||||
# Specifies the interval at which the network monitor sends the test packets.
|
||||
run_interval = '{{ network_monitor.run_interval }}'
|
||||
|
||||
@@ -92,13 +89,27 @@ database_path = '{{ node_status_api.database_path }}'
|
||||
# Specifies whether rewarding service is enabled in this process.
|
||||
enabled = {{ rewarding.enabled }}
|
||||
|
||||
# Mnemonic (currently of the network monitor) used for rewarding
|
||||
mnemonic = '{{ rewarding.mnemonic }}'
|
||||
|
||||
# Specifies the minimum percentage of monitor test run data present in order to
|
||||
# distribute rewards for given interval.
|
||||
# Note, only values in range 0-100 are valid
|
||||
minimum_interval_monitor_threshold = {{ rewarding.minimum_interval_monitor_threshold }}
|
||||
|
||||
[coconut_signer]
|
||||
|
||||
# Specifies whether rewarding service is enabled in this process.
|
||||
enabled = {{ coconut_signer.enabled }}
|
||||
|
||||
# Path to the signing keypair
|
||||
keypair_path = '{{ coconut_signer.keypair_path }}'
|
||||
|
||||
# Specifies list of all validators on the network issuing coconut credentials.
|
||||
# A special care must be taken to ensure they are in correct order.
|
||||
# The list must also contain THIS validator that is running the test
|
||||
all_validator_apis = [
|
||||
{{#each coconut_signer.all_validator_apis }}
|
||||
'{{this}}',
|
||||
{{/each}}
|
||||
]
|
||||
|
||||
"#
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::time;
|
||||
use validator_api_requests::models::MixnodeStatus;
|
||||
use validator_api_requests::models::{MixNodeBondAnnotated, MixnodeStatus};
|
||||
use validator_client::nymd::CosmWasmClient;
|
||||
|
||||
pub(crate) mod routes;
|
||||
@@ -40,14 +40,14 @@ pub struct ValidatorCache {
|
||||
}
|
||||
|
||||
struct ValidatorCacheInner {
|
||||
mixnodes: Cache<Vec<MixNodeBond>>,
|
||||
mixnodes: Cache<Vec<MixNodeBondAnnotated>>,
|
||||
gateways: Cache<Vec<GatewayBond>>,
|
||||
|
||||
mixnodes_blacklist: Cache<HashSet<IdentityKey>>,
|
||||
gateways_blacklist: Cache<HashSet<IdentityKey>>,
|
||||
|
||||
rewarded_set: Cache<Vec<MixNodeBond>>,
|
||||
active_set: Cache<Vec<MixNodeBond>>,
|
||||
rewarded_set: Cache<Vec<MixNodeBondAnnotated>>,
|
||||
active_set: Cache<Vec<MixNodeBondAnnotated>>,
|
||||
|
||||
current_reward_params: Cache<EpochRewardParams>,
|
||||
current_epoch: Cache<Option<Interval>>,
|
||||
@@ -99,11 +99,32 @@ impl<C> ValidatorCacheRefresher<C> {
|
||||
}
|
||||
}
|
||||
|
||||
fn annotate_bond_with_details(
|
||||
mixnodes: Vec<MixNodeBond>,
|
||||
interval_reward_params: EpochRewardParams,
|
||||
) -> Vec<MixNodeBondAnnotated> {
|
||||
mixnodes
|
||||
.into_iter()
|
||||
.map(|mixnode_bond| {
|
||||
let stake_saturation = mixnode_bond
|
||||
.stake_saturation(
|
||||
interval_reward_params.circulating_supply(),
|
||||
interval_reward_params.rewarded_set_size() as u32,
|
||||
)
|
||||
.to_num();
|
||||
MixNodeBondAnnotated {
|
||||
mixnode_bond,
|
||||
stake_saturation,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn collect_rewarded_and_active_set_details(
|
||||
&self,
|
||||
all_mixnodes: &[MixNodeBond],
|
||||
all_mixnodes: &[MixNodeBondAnnotated],
|
||||
rewarded_set_identities: Vec<(IdentityKey, RewardedSetNodeStatus)>,
|
||||
) -> (Vec<MixNodeBond>, Vec<MixNodeBond>) {
|
||||
) -> (Vec<MixNodeBondAnnotated>, Vec<MixNodeBondAnnotated>) {
|
||||
let mut active_set = Vec::new();
|
||||
let mut rewarded_set = Vec::new();
|
||||
let rewarded_set_identities = rewarded_set_identities
|
||||
@@ -111,7 +132,7 @@ impl<C> ValidatorCacheRefresher<C> {
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
for mix in all_mixnodes {
|
||||
if let Some(status) = rewarded_set_identities.get(mix.identity()) {
|
||||
if let Some(status) = rewarded_set_identities.get(mix.mixnode_bond.identity()) {
|
||||
rewarded_set.push(mix.clone());
|
||||
if status.is_active() {
|
||||
active_set.push(mix.clone())
|
||||
@@ -126,11 +147,16 @@ impl<C> ValidatorCacheRefresher<C> {
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let epoch_rewarding_params = self.nymd_client.get_current_epoch_reward_params().await?;
|
||||
let current_epoch = self.nymd_client.get_current_epoch().await?;
|
||||
|
||||
let (mixnodes, gateways) = tokio::try_join!(
|
||||
self.nymd_client.get_mixnodes(),
|
||||
self.nymd_client.get_gateways(),
|
||||
)?;
|
||||
|
||||
let mixnodes = Self::annotate_bond_with_details(mixnodes, epoch_rewarding_params);
|
||||
|
||||
let (rewarded_set, active_set) = if let Ok(rewarded_set_identities) =
|
||||
self.nymd_client.get_rewarded_set_identities().await
|
||||
{
|
||||
@@ -139,9 +165,6 @@ impl<C> ValidatorCacheRefresher<C> {
|
||||
(Vec::new(), Vec::new())
|
||||
};
|
||||
|
||||
let epoch_rewarding_params = self.nymd_client.get_current_epoch_reward_params().await?;
|
||||
let current_epoch = self.nymd_client.get_current_epoch().await?;
|
||||
|
||||
info!(
|
||||
"Updating validator cache. There are {} mixnodes and {} gateways",
|
||||
mixnodes.len(),
|
||||
@@ -184,9 +207,12 @@ impl<C> ValidatorCacheRefresher<C> {
|
||||
pub(crate) fn validator_cache_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
|
||||
openapi_get_routes_spec![
|
||||
settings: routes::get_mixnodes,
|
||||
routes::get_mixnodes_detailed,
|
||||
routes::get_gateways,
|
||||
routes::get_active_set,
|
||||
routes::get_active_set_detailed,
|
||||
routes::get_rewarded_set,
|
||||
routes::get_rewarded_set_detailed,
|
||||
routes::get_blacklisted_mixnodes,
|
||||
routes::get_blacklisted_gateways,
|
||||
routes::get_epoch_reward_params,
|
||||
@@ -210,10 +236,10 @@ impl ValidatorCache {
|
||||
|
||||
async fn update_cache(
|
||||
&self,
|
||||
mixnodes: Vec<MixNodeBond>,
|
||||
mixnodes: Vec<MixNodeBondAnnotated>,
|
||||
gateways: Vec<GatewayBond>,
|
||||
rewarded_set: Vec<MixNodeBond>,
|
||||
active_set: Vec<MixNodeBond>,
|
||||
rewarded_set: Vec<MixNodeBondAnnotated>,
|
||||
active_set: Vec<MixNodeBondAnnotated>,
|
||||
epoch_rewarding_params: EpochRewardParams,
|
||||
current_epoch: Interval,
|
||||
) {
|
||||
@@ -312,7 +338,7 @@ impl ValidatorCache {
|
||||
error!("Failed to update gateways blacklist");
|
||||
}
|
||||
|
||||
pub async fn mixnodes(&self) -> Vec<MixNodeBond> {
|
||||
pub async fn mixnodes_detailed(&self) -> Vec<MixNodeBondAnnotated> {
|
||||
let blacklist = self.mixnodes_blacklist().await;
|
||||
let mixnodes = match time::timeout(Duration::from_millis(100), self.inner.read()).await {
|
||||
Ok(cache) => cache.mixnodes.clone(),
|
||||
@@ -326,7 +352,7 @@ impl ValidatorCache {
|
||||
mixnodes
|
||||
.value
|
||||
.iter()
|
||||
.filter(|mix| !blacklist.value.contains(mix.identity()))
|
||||
.filter(|mix| !blacklist.value.contains(mix.mixnode_bond.identity()))
|
||||
.cloned()
|
||||
.collect()
|
||||
} else {
|
||||
@@ -334,9 +360,23 @@ impl ValidatorCache {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn mixnodes(&self) -> Vec<MixNodeBond> {
|
||||
self.mixnodes_detailed()
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|bond| bond.mixnode_bond)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub async fn mixnodes_all(&self) -> Vec<MixNodeBond> {
|
||||
match time::timeout(Duration::from_millis(100), self.inner.read()).await {
|
||||
Ok(cache) => cache.mixnodes.clone().into_inner(),
|
||||
Ok(cache) => cache
|
||||
.mixnodes
|
||||
.clone()
|
||||
.into_inner()
|
||||
.into_iter()
|
||||
.map(|bond| bond.mixnode_bond)
|
||||
.collect(),
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
Vec::new()
|
||||
@@ -376,7 +416,7 @@ impl ValidatorCache {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn rewarded_set(&self) -> Cache<Vec<MixNodeBond>> {
|
||||
pub async fn rewarded_set_detailed(&self) -> Cache<Vec<MixNodeBondAnnotated>> {
|
||||
match time::timeout(Duration::from_millis(100), self.inner.read()).await {
|
||||
Ok(cache) => cache.rewarded_set.clone(),
|
||||
Err(e) => {
|
||||
@@ -386,7 +426,16 @@ impl ValidatorCache {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn active_set(&self) -> Cache<Vec<MixNodeBond>> {
|
||||
pub async fn rewarded_set(&self) -> Vec<MixNodeBond> {
|
||||
self.rewarded_set_detailed()
|
||||
.await
|
||||
.value
|
||||
.into_iter()
|
||||
.map(|bond| bond.mixnode_bond)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub async fn active_set_detailed(&self) -> Cache<Vec<MixNodeBondAnnotated>> {
|
||||
match time::timeout(Duration::from_millis(100), self.inner.read()).await {
|
||||
Ok(cache) => cache.active_set.clone(),
|
||||
Err(e) => {
|
||||
@@ -396,6 +445,15 @@ impl ValidatorCache {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn active_set(&self) -> Vec<MixNodeBond> {
|
||||
self.active_set_detailed()
|
||||
.await
|
||||
.value
|
||||
.into_iter()
|
||||
.map(|bond| bond.mixnode_bond)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) async fn epoch_reward_params(&self) -> Cache<EpochRewardParams> {
|
||||
match time::timeout(Duration::from_millis(100), self.inner.read()).await {
|
||||
Ok(cache) => cache.current_reward_params.clone(),
|
||||
@@ -419,30 +477,30 @@ impl ValidatorCache {
|
||||
pub async fn mixnode_details(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> (Option<MixNodeBond>, MixnodeStatus) {
|
||||
) -> (Option<MixNodeBondAnnotated>, MixnodeStatus) {
|
||||
// it might not be the most optimal to possibly iterate the entire vector to find (or not)
|
||||
// the relevant value. However, the vectors are relatively small (< 10_000 elements, < 1000 for active set)
|
||||
|
||||
let active_set = &self.active_set().await.value;
|
||||
let active_set = &self.active_set_detailed().await.value;
|
||||
if let Some(bond) = active_set
|
||||
.iter()
|
||||
.find(|mix| mix.mix_node.identity_key == identity)
|
||||
.find(|mix| mix.mix_node().identity_key == identity)
|
||||
{
|
||||
return (Some(bond.clone()), MixnodeStatus::Active);
|
||||
}
|
||||
|
||||
let rewarded_set = &self.rewarded_set().await.value;
|
||||
let rewarded_set = &self.rewarded_set_detailed().await.value;
|
||||
if let Some(bond) = rewarded_set
|
||||
.iter()
|
||||
.find(|mix| mix.mix_node.identity_key == identity)
|
||||
.find(|mix| mix.mix_node().identity_key == identity)
|
||||
{
|
||||
return (Some(bond.clone()), MixnodeStatus::Standby);
|
||||
}
|
||||
|
||||
let all_bonded = &self.mixnodes().await;
|
||||
let all_bonded = &self.mixnodes_detailed().await;
|
||||
if let Some(bond) = all_bonded
|
||||
.iter()
|
||||
.find(|mix| mix.mix_node.identity_key == identity)
|
||||
.find(|mix| mix.mix_node().identity_key == identity)
|
||||
{
|
||||
(Some(bond.clone()), MixnodeStatus::Inactive)
|
||||
} else {
|
||||
|
||||
@@ -8,6 +8,7 @@ use rocket::serde::json::Json;
|
||||
use rocket::State;
|
||||
use rocket_okapi::openapi;
|
||||
use std::collections::HashSet;
|
||||
use validator_api_requests::models::MixNodeBondAnnotated;
|
||||
|
||||
#[openapi(tag = "contract-cache")]
|
||||
#[get("/mixnodes")]
|
||||
@@ -15,6 +16,14 @@ pub async fn get_mixnodes(cache: &State<ValidatorCache>) -> Json<Vec<MixNodeBond
|
||||
Json(cache.mixnodes().await)
|
||||
}
|
||||
|
||||
#[openapi(tag = "contract-cache")]
|
||||
#[get("/mixnodes/detailed")]
|
||||
pub async fn get_mixnodes_detailed(
|
||||
cache: &State<ValidatorCache>,
|
||||
) -> Json<Vec<MixNodeBondAnnotated>> {
|
||||
Json(cache.mixnodes_detailed().await)
|
||||
}
|
||||
|
||||
#[openapi(tag = "contract-cache")]
|
||||
#[get("/gateways")]
|
||||
pub async fn get_gateways(cache: &State<ValidatorCache>) -> Json<Vec<GatewayBond>> {
|
||||
@@ -24,13 +33,29 @@ pub async fn get_gateways(cache: &State<ValidatorCache>) -> Json<Vec<GatewayBond
|
||||
#[openapi(tag = "contract-cache")]
|
||||
#[get("/mixnodes/rewarded")]
|
||||
pub async fn get_rewarded_set(cache: &State<ValidatorCache>) -> Json<Vec<MixNodeBond>> {
|
||||
Json(cache.rewarded_set().await.value)
|
||||
Json(cache.rewarded_set().await)
|
||||
}
|
||||
|
||||
#[openapi(tag = "contract-cache")]
|
||||
#[get("/mixnodes/rewarded/detailed")]
|
||||
pub async fn get_rewarded_set_detailed(
|
||||
cache: &State<ValidatorCache>,
|
||||
) -> Json<Vec<MixNodeBondAnnotated>> {
|
||||
Json(cache.rewarded_set_detailed().await.value)
|
||||
}
|
||||
|
||||
#[openapi(tag = "contract-cache")]
|
||||
#[get("/mixnodes/active")]
|
||||
pub async fn get_active_set(cache: &State<ValidatorCache>) -> Json<Vec<MixNodeBond>> {
|
||||
Json(cache.active_set().await.value)
|
||||
Json(cache.active_set().await)
|
||||
}
|
||||
|
||||
#[openapi(tag = "contract-cache")]
|
||||
#[get("/mixnodes/active/detailed")]
|
||||
pub async fn get_active_set_detailed(
|
||||
cache: &State<ValidatorCache>,
|
||||
) -> Json<Vec<MixNodeBondAnnotated>> {
|
||||
Json(cache.active_set_detailed().await.value)
|
||||
}
|
||||
|
||||
#[openapi(tag = "contract-cache")]
|
||||
|
||||
+46
-33
@@ -23,17 +23,18 @@ use rocket::{Ignite, Rocket};
|
||||
use rocket_cors::{AllowedHeaders, AllowedOrigins, Cors};
|
||||
use rocket_okapi::mount_endpoints_and_merged_docs;
|
||||
use rocket_okapi::swagger_ui::make_swagger_ui;
|
||||
use std::process;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::{fs, process};
|
||||
use tokio::sync::Notify;
|
||||
use url::Url;
|
||||
// use validator_client::nymd::SigningNymdClient;
|
||||
// use validator_client::ValidatorClientError;
|
||||
|
||||
use crate::rewarded_set_updater::RewardedSetUpdater;
|
||||
#[cfg(feature = "coconut")]
|
||||
use coconut::InternalSignRequest;
|
||||
#[cfg(feature = "coconut")]
|
||||
use coconut_interface::{Base58, KeyPair};
|
||||
use validator_client::nymd::SigningNymdClient;
|
||||
|
||||
pub(crate) mod config;
|
||||
@@ -48,18 +49,19 @@ mod swagger;
|
||||
#[cfg(feature = "coconut")]
|
||||
mod coconut;
|
||||
|
||||
const ID: &str = "id";
|
||||
const MONITORING_ENABLED: &str = "enable-monitor";
|
||||
const REWARDING_ENABLED: &str = "enable-rewarding";
|
||||
const MIXNET_CONTRACT_ARG: &str = "mixnet-contract";
|
||||
const MNEMONIC_ARG: &str = "mnemonic";
|
||||
const WRITE_CONFIG_ARG: &str = "save-config";
|
||||
const NYMD_VALIDATOR_ARG: &str = "nymd-validator";
|
||||
const API_VALIDATORS_ARG: &str = "api-validators";
|
||||
const ENABLED_CREDENTIALS_MODE_ARG_NAME: &str = "enabled-credentials-mode";
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
const API_VALIDATORS_ARG: &str = "api-validators";
|
||||
#[cfg(feature = "coconut")]
|
||||
const KEYPAIR_ARG: &str = "keypair";
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
const COCONUT_ENABLED: &str = "enable-coconut";
|
||||
|
||||
@@ -73,7 +75,8 @@ const REWARDING_MONITOR_THRESHOLD_ARG: &str = "monitor-threshold";
|
||||
const MIN_MIXNODE_RELIABILITY_ARG: &str = "min_mixnode_reliability";
|
||||
const MIN_GATEWAY_RELIABILITY_ARG: &str = "min_gateway_reliability";
|
||||
|
||||
fn parse_validators(raw: &str) -> Vec<Url> {
|
||||
#[cfg(feature = "coconut")]
|
||||
fn parse_validators(raw: &str) -> Vec<url::Url> {
|
||||
raw.split(',')
|
||||
.map(|raw_validator| {
|
||||
raw_validator
|
||||
@@ -124,6 +127,12 @@ fn parse_args<'a>() -> ArgMatches<'a> {
|
||||
.version(crate_version!())
|
||||
.long_version(&*build_details)
|
||||
.author("Nymtech")
|
||||
.arg(
|
||||
Arg::with_name(ID)
|
||||
.help("Id of the validator-api we want to run")
|
||||
.long(ID)
|
||||
.takes_value(true)
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(MONITORING_ENABLED)
|
||||
.help("specifies whether a network monitoring is enabled on this API")
|
||||
@@ -159,12 +168,6 @@ fn parse_args<'a>() -> ArgMatches<'a> {
|
||||
.long(WRITE_CONFIG_ARG)
|
||||
.short("w")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(API_VALIDATORS_ARG)
|
||||
.help("specifies list of all validators on the network issuing coconut credentials. Ensure they are properly ordered")
|
||||
.long(API_VALIDATORS_ARG)
|
||||
.takes_value(true)
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(REWARDING_MONITOR_THRESHOLD_ARG)
|
||||
.help("Specifies the minimum percentage of monitor test run data present in order to distribute rewards for given interval.")
|
||||
@@ -185,10 +188,16 @@ fn parse_args<'a>() -> ArgMatches<'a> {
|
||||
.takes_value(true)
|
||||
.long(KEYPAIR_ARG),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(API_VALIDATORS_ARG)
|
||||
.help("specifies list of all validators on the network issuing coconut credentials. Ensure they are properly ordered")
|
||||
.long(API_VALIDATORS_ARG)
|
||||
.takes_value(true)
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(COCONUT_ENABLED)
|
||||
.help("Flag to indicate whether coconut signer authority is enabled on this API")
|
||||
.requires_all(&[KEYPAIR_ARG, MNEMONIC_ARG])
|
||||
.requires_all(&[KEYPAIR_ARG, MNEMONIC_ARG, API_VALIDATORS_ARG])
|
||||
.long(COCONUT_ENABLED),
|
||||
);
|
||||
|
||||
@@ -236,12 +245,18 @@ fn setup_logging() {
|
||||
.filter_module("sled", log::LevelFilter::Warn)
|
||||
.filter_module("tungstenite", log::LevelFilter::Warn)
|
||||
.filter_module("tokio_tungstenite", log::LevelFilter::Warn)
|
||||
.filter_module("_", log::LevelFilter::Warn)
|
||||
.filter_module("rocket::server", log::LevelFilter::Warn)
|
||||
.init();
|
||||
}
|
||||
|
||||
fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> Config {
|
||||
if let Some(id) = matches.value_of(ID) {
|
||||
fs::create_dir_all(Config::default_config_directory(Some(id)))
|
||||
.expect("Could not create config directory");
|
||||
fs::create_dir_all(Config::default_data_directory(Some(id)))
|
||||
.expect("Could not create data directory");
|
||||
config = config.with_id(id);
|
||||
}
|
||||
|
||||
if matches.is_present(MONITORING_ENABLED) {
|
||||
config = config.with_network_monitor_enabled(true)
|
||||
}
|
||||
@@ -255,6 +270,7 @@ fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> Config {
|
||||
config = config.with_coconut_signer_enabled(true)
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
if let Some(raw_validators) = matches.value_of(API_VALIDATORS_ARG) {
|
||||
config = config.with_custom_validator_apis(parse_validators(raw_validators));
|
||||
}
|
||||
@@ -311,11 +327,7 @@ fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> Config {
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
if let Some(keypair_path) = matches.value_of(KEYPAIR_ARG) {
|
||||
let keypair_bs58 = std::fs::read_to_string(keypair_path)
|
||||
.unwrap()
|
||||
.trim()
|
||||
.to_string();
|
||||
config = config.with_keypair(keypair_bs58)
|
||||
config = config.with_keypair_path(keypair_path.into())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
@@ -402,7 +414,7 @@ fn expected_monitor_test_runs(config: &Config, interval_length: Duration) -> usi
|
||||
async fn setup_rocket(
|
||||
config: &Config,
|
||||
liftoff_notify: Arc<Notify>,
|
||||
_nymd_client: Option<Client<SigningNymdClient>>,
|
||||
_nymd_client: Client<SigningNymdClient>,
|
||||
) -> Result<Rocket<Ignite>> {
|
||||
let openapi_settings = rocket_okapi::settings::OpenApiSettings::default();
|
||||
let mut rocket = rocket::build();
|
||||
@@ -434,9 +446,14 @@ async fn setup_rocket(
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
let rocket = if config.get_coconut_signer_enabled() {
|
||||
let keypair_bs58 = fs::read_to_string(config.keypair_path())?
|
||||
.trim()
|
||||
.to_string();
|
||||
let keypair = KeyPair::try_from_bs58(keypair_bs58)?;
|
||||
rocket.attach(InternalSignRequest::stage(
|
||||
_nymd_client.expect("Should have a signing client here"),
|
||||
config.keypair(),
|
||||
_nymd_client,
|
||||
keypair,
|
||||
config.get_all_validator_api_endpoints(),
|
||||
storage.clone().unwrap(),
|
||||
))
|
||||
} else {
|
||||
@@ -486,10 +503,11 @@ async fn run_validator_api(matches: ArgMatches<'static>) -> Result<()> {
|
||||
let system_version = env!("CARGO_PKG_VERSION");
|
||||
|
||||
// try to load config from the file, if it doesn't exist, use default values
|
||||
let config = match Config::load_from_file(None) {
|
||||
let id = matches.value_of(ID);
|
||||
let config = match Config::load_from_file(id) {
|
||||
Ok(cfg) => cfg,
|
||||
Err(_) => {
|
||||
let config_path = Config::default_config_file_path(None)
|
||||
let config_path = Config::default_config_file_path(id)
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.unwrap();
|
||||
@@ -507,11 +525,7 @@ async fn run_validator_api(matches: ArgMatches<'static>) -> Result<()> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let signing_nymd_client = if matches.is_present(MNEMONIC_ARG) {
|
||||
Some(Client::new_signing(&config))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let signing_nymd_client = Client::new_signing(&config);
|
||||
|
||||
let liftoff_notify = Arc::new(Notify::new());
|
||||
|
||||
@@ -529,9 +543,8 @@ async fn run_validator_api(matches: ArgMatches<'static>) -> Result<()> {
|
||||
// if network monitor is disabled, we're not going to be sending any rewarding hence
|
||||
// we're not starting signing client
|
||||
if config.get_network_monitor_enabled() {
|
||||
let nymd_client = signing_nymd_client.expect("We should have a signing client here");
|
||||
let validator_cache_refresher = ValidatorCacheRefresher::new(
|
||||
nymd_client.clone(),
|
||||
signing_nymd_client.clone(),
|
||||
config.get_caching_interval(),
|
||||
validator_cache.clone(),
|
||||
);
|
||||
@@ -546,7 +559,7 @@ async fn run_validator_api(matches: ArgMatches<'static>) -> Result<()> {
|
||||
tokio::spawn(async move { uptime_updater.run().await });
|
||||
|
||||
let mut rewarded_set_updater =
|
||||
RewardedSetUpdater::new(nymd_client, validator_cache.clone(), storage).await?;
|
||||
RewardedSetUpdater::new(signing_nymd_client, validator_cache.clone(), storage).await?;
|
||||
|
||||
// spawn rewarded set updater
|
||||
tokio::spawn(async move { rewarded_set_updater.run().await.unwrap() });
|
||||
@@ -590,7 +603,7 @@ async fn run_validator_api(matches: ArgMatches<'static>) -> Result<()> {
|
||||
async fn main() -> Result<()> {
|
||||
println!("Starting validator api...");
|
||||
|
||||
dotenv::dotenv()?;
|
||||
let _ = dotenv::dotenv();
|
||||
|
||||
cfg_if::cfg_if! {if #[cfg(feature = "console-subscriber")] {
|
||||
// instriment tokio console subscriber needs RUSTFLAGS="--cfg tokio_unstable" at build time
|
||||
|
||||
@@ -147,7 +147,7 @@ pub(crate) async fn get_mixnode_reward_estimation(
|
||||
let node_reward_params = NodeRewardParams::new(0, uptime.u8() as u128, status.is_active());
|
||||
let reward_params = RewardParams::new(reward_params, node_reward_params);
|
||||
|
||||
match bond.estimate_reward(&reward_params) {
|
||||
match bond.mixnode_bond.estimate_reward(&reward_params) {
|
||||
Ok(reward_estimate) => {
|
||||
let reponse = RewardEstimationResponse {
|
||||
estimated_total_node_reward: reward_estimate.total_node_reward,
|
||||
@@ -181,11 +181,13 @@ pub(crate) async fn get_mixnode_stake_saturation(
|
||||
) -> Result<Json<StakeSaturationResponse>, ErrorResponse> {
|
||||
let (bond, _) = cache.mixnode_details(&identity).await;
|
||||
if let Some(bond) = bond {
|
||||
// Recompute the stake saturation just so that we can confidentaly state that the `as_at`
|
||||
// field is consistent and correct. Luckily this is very cheap.
|
||||
let interval_reward_params = cache.epoch_reward_params().await;
|
||||
let as_at = interval_reward_params.timestamp();
|
||||
let interval_reward_params = interval_reward_params.into_inner();
|
||||
|
||||
let saturation = bond.stake_saturation(
|
||||
let saturation = bond.mixnode_bond.stake_saturation(
|
||||
interval_reward_params.circulating_supply(),
|
||||
interval_reward_params.rewarded_set_size() as u32,
|
||||
);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user