diff --git a/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs b/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs index a2c000875f..57f77fd7f7 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs @@ -12,14 +12,10 @@ use nym_validator_client::{ MultisigSigningClient, }, cosmwasm_client::logs::{find_attribute, BANDWIDTH_PROPOSAL_ID}, - Coin, Fee, + Coin, }, CoconutApiClient, DirectSigningHttpRpcNyxdClient, }; -use std::time::{Duration, SystemTime}; - -const ONE_HOUR_SEC: u64 = 3600; -const MAX_FEEGRANT_UNYM: u128 = 10000; pub(crate) struct CoconutVerifier { nyxd_client: DirectSigningHttpRpcNyxdClient, @@ -55,10 +51,6 @@ impl CoconutVerifier { api_clients: Vec, credential: &Credential, ) -> Result<(), RequestHandlingError> { - // Use a custom multiplier for revoke, as the default one (1.3) - // isn't enough - let revoke_fee = Some(Fee::Auto(Some(1.5))); - let res = self .nyxd_client .spend_credential( @@ -94,25 +86,7 @@ impl CoconutVerifier { self.nyxd_client.address(), ); for client in api_clients { - self.nyxd_client - .grant_allowance( - &client.cosmos_address, - vec![Coin::new(MAX_FEEGRANT_UNYM, self.mix_denom_base.clone())], - SystemTime::now().checked_add(Duration::from_secs(ONE_HOUR_SEC)), - // It would be nice to be able to filter deeper, but for now only the msg type filter is avaialable - vec![String::from("/cosmwasm.wasm.v1.MsgExecuteContract")], - "Create allowance to vote the release of funds".to_string(), - None, - ) - .await?; let ret = client.api_client.verify_bandwidth_credential(&req).await; - self.nyxd_client - .revoke_allowance( - &client.cosmos_address, - "Cleanup the previous allowance for releasing funds".to_string(), - revoke_fee.clone(), - ) - .await?; match ret { Ok(res) => { if !res.verification_result { diff --git a/nym-api/src/coconut/api_routes/mod.rs b/nym-api/src/coconut/api_routes/mod.rs index 9110487ade..6e505d0479 100644 --- a/nym-api/src/coconut/api_routes/mod.rs +++ b/nym-api/src/coconut/api_routes/mod.rs @@ -21,7 +21,7 @@ use nym_coconut_bandwidth_contract_common::spend_credential::{ }; use nym_coconut_dkg_common::types::EpochId; use nym_credentials::coconut::bandwidth::BandwidthVoucher; -use nym_validator_client::nyxd::{Coin, Fee}; +use nym_validator_client::nyxd::Coin; use rocket::serde::json::Json; use rocket::State as RocketState; @@ -94,6 +94,9 @@ pub async fn verify_bandwidth_credential( ) -> Result> { let proposal_id = verify_credential_body.proposal_id; let proposal = state.client.get_proposal(proposal_id).await?; + + // TODO: introduce a check to make sure we haven't already voted for this proposal to prevent DDOS + // Proposal description is the blinded serial number if !verify_credential_body .credential @@ -136,15 +139,7 @@ pub async fn verify_bandwidth_credential( // Vote yes or no on the proposal based on the verification result let ret = state .client - .vote_proposal( - proposal_id, - vote_yes, - Some(Fee::new_payer_granter_auto( - None, - None, - Some(verify_credential_body.gateway_cosmos_addr.clone()), - )), - ) + .vote_proposal(proposal_id, vote_yes, None) .await; accepted_vote_err(ret)?; diff --git a/nym-api/src/coconut/mod.rs b/nym-api/src/coconut/mod.rs index 51c4199018..3f25cba0e1 100644 --- a/nym-api/src/coconut/mod.rs +++ b/nym-api/src/coconut/mod.rs @@ -24,6 +24,9 @@ pub(crate) mod storage; #[cfg(test)] pub(crate) mod tests; +// equivalent of 10nym +pub(crate) const MINIMUM_BALANCE: u128 = 10_000000; + pub fn stage( client: C, mix_denom: String, diff --git a/nym-api/src/support/http/mod.rs b/nym-api/src/support/http/mod.rs index 88ded2ca45..7f54a35f8e 100644 --- a/nym-api/src/support/http/mod.rs +++ b/nym-api/src/support/http/mod.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::circulating_supply_api::cache::CirculatingSupplyCache; +use crate::coconut::client::Client; use crate::coconut::{self, comm::QueryCommunicationChannel}; use crate::network::models::NetworkDetails; use crate::network::network_routes; @@ -12,8 +13,9 @@ use crate::support::caching::cache::SharedCache; use crate::support::config::Config; use crate::support::{nyxd, storage}; use crate::{circulating_supply_api, nym_contract_cache, nym_nodes::nym_node_routes}; -use anyhow::Result; +use anyhow::{bail, Result}; use nym_crypto::asymmetric::identity; +use nym_validator_client::nyxd::Coin; use rocket::http::Method; use rocket::{Ignite, Rocket}; use rocket_cors::{AllowedHeaders, AllowedOrigins, Cors}; @@ -67,6 +69,14 @@ pub(crate) async fn setup_rocket( }; let rocket = if config.coconut_signer.enabled { + // make sure we have some tokens to cover multisig fees + let balance = _nyxd_client.balance(&mix_denom).await?; + if balance.amount < coconut::MINIMUM_BALANCE { + let address = _nyxd_client.address().await; + let min = Coin::new(coconut::MINIMUM_BALANCE, mix_denom); + bail!("the account ({address}) doesn't have enough funds to cover verification fees. it has {balance} while it needs at least {min}") + } + let comm_channel = QueryCommunicationChannel::new(_nyxd_client.clone()); rocket.attach(coconut::stage( _nyxd_client.clone(), diff --git a/nym-api/src/support/nyxd/mod.rs b/nym-api/src/support/nyxd/mod.rs index 3c04803226..8a99a0ff0a 100644 --- a/nym-api/src/support/nyxd/mod.rs +++ b/nym-api/src/support/nyxd/mod.rs @@ -88,6 +88,22 @@ impl Client { self.0.read().await.address() } + pub(crate) async fn balance>(&self, denom: S) -> Result { + let guard = self.0.read().await; + let denom = denom.into(); + let address = guard.address(); + match self + .0 + .read() + .await + .get_balance(&address, denom.clone()) + .await? + { + None => Ok(Coin::new(0, denom)), + Some(coin) => Ok(coin), + } + } + pub(crate) async fn chain_details(&self) -> ChainDetails { self.0.read().await.current_chain_details().clone() }