Merge with wallet-claim-rewards

This commit is contained in:
Jędrzej Stuczyński
2022-06-06 12:04:15 +01:00
83 changed files with 2013 additions and 722 deletions
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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",
+2
View File
@@ -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",
-1
View File
@@ -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
+20 -31
View File
@@ -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())
+1 -1
View File
@@ -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",
@@ -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";
+3 -5
View File
@@ -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),
}
+191 -46
View File
@@ -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(&params, 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(
&params,
&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;
@@ -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};
+3 -16
View File
@@ -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,
))
}
+1 -5
View File
@@ -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),
+11
View File
@@ -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"
+110
View File
@@ -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![async](execute_f.sig.ident.span()));
simulate_f.sig.asyncness = Some(Token![async](simulate_f.sig.ident.span()));
execute_f.vis = Visibility::Public(VisPublic {
pub_token: Token![pub](execute_f.sig.ident.span()),
});
simulate_f.vis = Visibility::Public(VisPublic {
pub_token: Token![pub](simulate_f.sig.ident.span()),
});
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()
}
+8
View File
@@ -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
}
+10 -2
View File
@@ -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";
+3
View File
@@ -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] =
+3
View File
@@ -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] =
+3
View File
@@ -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] =
+1 -2
View File
@@ -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 {}
+2 -2
View File
@@ -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;
+15 -2
View File
@@ -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 {
+13
View File
@@ -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;
+1 -10
View File
@@ -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!"),
-1
View File
@@ -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,
},
}
+9 -13
View File
@@ -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"))]
+7 -11
View File
@@ -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
View File
@@ -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,
);
+7 -3
View File
@@ -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) => {
+69
View File
@@ -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",
+6 -9
View File
@@ -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) {
+16
View File
@@ -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(())
}
+2 -9
View File
@@ -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, }
+2 -10
View File
@@ -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";
+1
View File
@@ -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" }
+13 -1
View File
@@ -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<()>;
}
+14
View File
@@ -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 {
+98 -11
View File
@@ -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(()))
}
+43 -3
View File
@@ -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)
+52 -23
View File
@@ -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
+23 -12
View File
@@ -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
View File
@@ -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() });
+91 -32
View File
@@ -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}")]