Merge with wallet-claim-rewards
This commit is contained in:
@@ -11,6 +11,8 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
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
|
||||
|
||||
+17
-12
@@ -4,39 +4,43 @@
|
||||
|
||||
### Added
|
||||
|
||||
- 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.
|
||||
- wallet: require password to switch accounts
|
||||
- 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
|
||||
@@ -44,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
+61
-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"
|
||||
@@ -1951,7 +1994,6 @@ dependencies = [
|
||||
name = "gateway-requests"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bs58",
|
||||
"coconut-interface",
|
||||
"credentials",
|
||||
@@ -2910,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"
|
||||
@@ -3252,6 +3306,7 @@ dependencies = [
|
||||
"humantime-serde",
|
||||
"log",
|
||||
"mixnet-contract-common",
|
||||
"multisig-contract-common",
|
||||
"nymcoconut",
|
||||
"nymsphinx",
|
||||
"okapi",
|
||||
@@ -6285,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::DENOM).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,7 +2,11 @@
|
||||
// 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;
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -733,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.denom()),
|
||||
};
|
||||
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,7 +3,11 @@
|
||||
|
||||
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;
|
||||
@@ -371,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.
|
||||
|
||||
@@ -17,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()
|
||||
}
|
||||
@@ -52,6 +52,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
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@ pub struct DefaultNetworkDetails<'a> {
|
||||
mixnet_contract_address: &'a str,
|
||||
vesting_contract_address: &'a str,
|
||||
bandwidth_claim_contract_address: &'a str,
|
||||
coconut_bandwidth_contract_address: &'a str,
|
||||
multisig_contract_address: &'a str,
|
||||
rewarding_validator_address: &'a str,
|
||||
validators: Vec<ValidatorDetails>,
|
||||
}
|
||||
@@ -62,6 +64,8 @@ static MAINNET_DEFAULTS: Lazy<DefaultNetworkDetails<'static>> =
|
||||
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(),
|
||||
});
|
||||
@@ -73,6 +77,8 @@ static SANDBOX_DEFAULTS: Lazy<DefaultNetworkDetails<'static>> =
|
||||
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(),
|
||||
});
|
||||
@@ -83,6 +89,8 @@ static QA_DEFAULTS: Lazy<DefaultNetworkDetails<'static>> = Lazy::new(|| DefaultN
|
||||
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(),
|
||||
});
|
||||
@@ -146,7 +154,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;
|
||||
@@ -155,7 +163,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";
|
||||
|
||||
|
||||
@@ -13,6 +13,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] =
|
||||
|
||||
@@ -13,6 +13,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] =
|
||||
|
||||
@@ -11,6 +11,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;
|
||||
|
||||
@@ -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!"),
|
||||
|
||||
@@ -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!())
|
||||
|
||||
@@ -77,7 +77,7 @@ pub async fn switch_network(
|
||||
let denom = network.denom();
|
||||
|
||||
Account::new(
|
||||
client.nymd.mixnet_contract_address()?.to_string(),
|
||||
client.nymd.mixnet_contract_address().to_string(),
|
||||
client.nymd.address().to_string(),
|
||||
denom.try_into()?,
|
||||
)
|
||||
@@ -160,7 +160,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.denom().try_into()?,
|
||||
)),
|
||||
@@ -253,14 +253,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(
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::BackendError;
|
||||
use crate::nymd_client;
|
||||
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::MajorCurrencyAmount;
|
||||
use std::sync::Arc;
|
||||
@@ -20,7 +22,7 @@ pub async fn simulate_bond_gateway(
|
||||
let pledge = pledge.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();
|
||||
|
||||
// TODO: I'm still not 100% convinced whether this should be exposed here or handled somewhere else in the client code
|
||||
@@ -44,7 +46,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 +70,7 @@ pub async fn simulate_bond_mixnode(
|
||||
let pledge = pledge.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(
|
||||
@@ -91,7 +93,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 +114,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 +139,7 @@ pub async fn simulate_delegate_to_mixnode(
|
||||
let delegation = amount.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(
|
||||
@@ -160,7 +162,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 +176,49 @@ pub async fn simulate_undelegate_from_mixnode(
|
||||
let result = client.nymd.simulate(vec![msg]).await?;
|
||||
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn simulate_claim_operator_reward(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let result = nymd_client!(state)
|
||||
.simulate_claim_operator_reward(None)
|
||||
.await?;
|
||||
let gas_price = nymd_client!(state).gas_price().clone();
|
||||
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn simulate_compound_operator_reward(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let result = nymd_client!(state)
|
||||
.simulate_compound_operator_reward(None)
|
||||
.await?;
|
||||
let gas_price = nymd_client!(state).gas_price().clone();
|
||||
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn simulate_claim_delegator_reward(
|
||||
mix_identity: IdentityKey,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let result = nymd_client!(state)
|
||||
.simulate_claim_delegator_reward(mix_identity, None)
|
||||
.await?;
|
||||
let gas_price = nymd_client!(state).gas_price().clone();
|
||||
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn simulate_compound_delegator_reward(
|
||||
mix_identity: IdentityKey,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let result = nymd_client!(state)
|
||||
.simulate_compound_delegator_reward(mix_identity, None)
|
||||
.await?;
|
||||
let gas_price = nymd_client!(state).gas_price().clone();
|
||||
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::BackendError;
|
||||
use crate::nymd_client;
|
||||
use crate::operations::simulate::{FeeDetails, SimulateResult};
|
||||
use crate::State;
|
||||
use mixnet_contract_common::IdentityKey;
|
||||
use mixnet_contract_common::{Gateway, MixNode};
|
||||
use nym_types::currency::MajorCurrencyAmount;
|
||||
use std::sync::Arc;
|
||||
@@ -21,7 +23,7 @@ pub async fn simulate_vesting_bond_gateway(
|
||||
let pledge = pledge.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(
|
||||
@@ -45,7 +47,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 +71,7 @@ pub async fn simulate_vesting_bond_mixnode(
|
||||
let pledge = pledge.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(
|
||||
@@ -93,7 +95,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 +116,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 +140,7 @@ pub async fn simulate_withdraw_vested_coins(
|
||||
let amount = 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 +152,49 @@ pub async fn simulate_withdraw_vested_coins(
|
||||
let result = client.nymd.simulate(vec![msg]).await?;
|
||||
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn simulate_vesting_claim_operator_reward(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let result = nymd_client!(state)
|
||||
.simulate_vesting_claim_operator_reward(None)
|
||||
.await?;
|
||||
let gas_price = nymd_client!(state).gas_price().clone();
|
||||
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn simulate_vesting_compound_operator_reward(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let result = nymd_client!(state)
|
||||
.simulate_vesting_compound_operator_reward(None)
|
||||
.await?;
|
||||
let gas_price = nymd_client!(state).gas_price().clone();
|
||||
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn simulate_vesting_claim_delegator_reward(
|
||||
mix_identity: IdentityKey,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let result = nymd_client!(state)
|
||||
.simulate_vesting_claim_delegator_reward(mix_identity, None)
|
||||
.await?;
|
||||
let gas_price = nymd_client!(state).gas_price().clone();
|
||||
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn simulate_vesting_compound_delegator_reward(
|
||||
mix_identity: IdentityKey,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
let result = nymd_client!(state)
|
||||
.simulate_vesting_compound_delegator_reward(mix_identity, None)
|
||||
.await?;
|
||||
let gas_price = nymd_client!(state).gas_price().clone();
|
||||
Ok(SimulateResult::new(result.gas_info, gas_price).detailed_fee()?)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ pub async fn get_pending_vesting_delegation_events(
|
||||
|
||||
let guard = state.read().await;
|
||||
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";
|
||||
@@ -56,6 +56,7 @@ 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}}
|
||||
]
|
||||
|
||||
"#
|
||||
}
|
||||
|
||||
+45
-32
@@ -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() });
|
||||
|
||||
@@ -1,21 +1,28 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::rewarded_set_updater::error::RewardingError;
|
||||
#[cfg(feature = "coconut")]
|
||||
use async_trait::async_trait;
|
||||
use config::defaults::{DEFAULT_NETWORK, DEFAULT_VALIDATOR_API_PORT};
|
||||
use mixnet_contract_common::Interval;
|
||||
use mixnet_contract_common::{
|
||||
reward_params::EpochRewardParams, ContractStateParams, Delegation, ExecuteMsg, GatewayBond,
|
||||
IdentityKey, MixNodeBond, MixnodeRewardingStatusResponse, RewardedSetNodeStatus,
|
||||
};
|
||||
use serde::Serialize;
|
||||
#[cfg(feature = "coconut")]
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::time::sleep;
|
||||
|
||||
use config::defaults::{DEFAULT_NETWORK, DEFAULT_VALIDATOR_API_PORT};
|
||||
use mixnet_contract_common::{
|
||||
reward_params::EpochRewardParams, ContractStateParams, Delegation, ExecuteMsg, GatewayBond,
|
||||
IdentityKey, Interval, MixNodeBond, MixnodeRewardingStatusResponse, RewardedSetNodeStatus,
|
||||
};
|
||||
#[cfg(feature = "coconut")]
|
||||
use multisig_contract_common::msg::ProposalResponse;
|
||||
#[cfg(feature = "coconut")]
|
||||
use validator_client::nymd::{
|
||||
cosmwasm_client::logs::find_attribute,
|
||||
traits::{MultisigSigningClient, QueryClient},
|
||||
};
|
||||
use validator_client::nymd::{
|
||||
hash::{Hash, SHA256_HASH_SIZE},
|
||||
Coin, CosmWasmClient, Fee, QueryNymdClient, SigningCosmWasmClient, SigningNymdClient,
|
||||
@@ -23,6 +30,11 @@ use validator_client::nymd::{
|
||||
};
|
||||
use validator_client::ValidatorClientError;
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
use crate::coconut::error::CoconutError;
|
||||
use crate::config::Config;
|
||||
use crate::rewarded_set_updater::error::RewardingError;
|
||||
|
||||
pub(crate) struct Client<C>(pub(crate) Arc<RwLock<validator_client::Client<C>>>);
|
||||
|
||||
impl<C> Clone for Client<C> {
|
||||
@@ -46,14 +58,8 @@ impl Client<QueryNymdClient> {
|
||||
.parse()
|
||||
.expect("the mixnet contract address is invalid!");
|
||||
|
||||
let client_config = validator_client::Config::new(
|
||||
network,
|
||||
nymd_url,
|
||||
api_url,
|
||||
Some(mixnet_contract),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let client_config = validator_client::Config::new(network, nymd_url, api_url)
|
||||
.with_mixnode_contract_address(mixnet_contract);
|
||||
let inner =
|
||||
validator_client::Client::new_query(client_config).expect("Failed to connect to nymd!");
|
||||
|
||||
@@ -80,14 +86,8 @@ impl Client<SigningNymdClient> {
|
||||
.parse()
|
||||
.expect("the mnemonic is invalid!");
|
||||
|
||||
let client_config = validator_client::Config::new(
|
||||
network,
|
||||
nymd_url,
|
||||
api_url,
|
||||
Some(mixnet_contract),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let client_config = validator_client::Config::new(network, nymd_url, api_url)
|
||||
.with_mixnode_contract_address(mixnet_contract);
|
||||
let inner = validator_client::Client::new_signing(client_config, mnemonic)
|
||||
.expect("Failed to connect to nymd!");
|
||||
|
||||
@@ -366,12 +366,7 @@ impl<C> Client<C> {
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
M: Serialize + Clone + Send,
|
||||
{
|
||||
let contract = self
|
||||
.0
|
||||
.read()
|
||||
.await
|
||||
.get_mixnet_contract_address()
|
||||
.ok_or(RewardingError::UnspecifiedContractAddress)?;
|
||||
let contract = self.0.read().await.get_mixnet_contract_address();
|
||||
|
||||
// grab the write lock here so we're sure nothing else is executing anything on the contract
|
||||
// in the meantime
|
||||
@@ -420,7 +415,7 @@ impl<C> Client<C> {
|
||||
#[cfg(feature = "coconut")]
|
||||
impl<C> crate::coconut::client::Client for Client<C>
|
||||
where
|
||||
C: CosmWasmClient + Sync + Send,
|
||||
C: SigningCosmWasmClient + Sync + Send,
|
||||
{
|
||||
async fn get_tx(
|
||||
&self,
|
||||
@@ -428,7 +423,71 @@ where
|
||||
) -> crate::coconut::error::Result<validator_client::nymd::TxResponse> {
|
||||
let tx_hash = tx_hash
|
||||
.parse::<validator_client::nymd::tx::Hash>()
|
||||
.map_err(|_| crate::coconut::error::CoconutError::TxHashParseError)?;
|
||||
.map_err(|_| CoconutError::TxHashParseError)?;
|
||||
Ok(self.0.read().await.nymd.get_tx(tx_hash).await?)
|
||||
}
|
||||
|
||||
async fn get_proposal(
|
||||
&self,
|
||||
proposal_id: u64,
|
||||
) -> crate::coconut::error::Result<ProposalResponse> {
|
||||
Ok(self.0.read().await.nymd.get_proposal(proposal_id).await?)
|
||||
}
|
||||
|
||||
async fn propose_release_funds(
|
||||
&self,
|
||||
title: String,
|
||||
blinded_serial_number: String,
|
||||
voucher_value: u128,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<u64, CoconutError> {
|
||||
let res = self
|
||||
.0
|
||||
.read()
|
||||
.await
|
||||
.nymd
|
||||
.propose_release_funds(title, blinded_serial_number, voucher_value, fee)
|
||||
.await?;
|
||||
let proposal_id = u64::from_str(
|
||||
&find_attribute(&res.logs, "wasm", "proposal_id")
|
||||
.ok_or_else(|| {
|
||||
CoconutError::InternalError("No attribute with proposal_id as key".to_string())
|
||||
})?
|
||||
.value,
|
||||
)
|
||||
.map_err(|_| {
|
||||
CoconutError::InternalError("proposal_id could not be parsed to u64".to_string())
|
||||
})?;
|
||||
|
||||
Ok(proposal_id)
|
||||
}
|
||||
|
||||
async fn vote_proposal(
|
||||
&self,
|
||||
proposal_id: u64,
|
||||
vote_yes: bool,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<(), CoconutError> {
|
||||
self.0
|
||||
.read()
|
||||
.await
|
||||
.nymd
|
||||
.vote_proposal(proposal_id, vote_yes, fee)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn execute_proposal(
|
||||
&self,
|
||||
proposal_id: u64,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<(), CoconutError> {
|
||||
self.0
|
||||
.read()
|
||||
.await
|
||||
.nymd
|
||||
.execute_proposal(proposal_id, fee)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,6 @@ use validator_client::ValidatorClientError;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum RewardingError {
|
||||
#[error("Could not distribute rewards as the contract address was unspecified")]
|
||||
UnspecifiedContractAddress,
|
||||
|
||||
// #[error("There were no mixnodes to reward (network is dead)")]
|
||||
// NoMixnodesToReward,
|
||||
#[error("Failed to execute the smart contract - {0}")]
|
||||
|
||||
Reference in New Issue
Block a user