diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a5479fdcc..69002691f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - gateway: allow to voluntarily send statistical data about the number of active inboxes served by a gateway ([#1376]) - gateway & mixnode: move detailed build info back to `--version` from `--help`. - socks5 client/websocket client: upgrade to latest clap and switched to declarative commandline parsing. +- validator-api: fee payment for multisig operations comes from the gateway account instead of the validator APIs' accounts ([#1419]) [#1249]: https://github.com/nymtech/nym/pull/1249 [#1256]: https://github.com/nymtech/nym/pull/1256 @@ -66,6 +67,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// [#1376]: https://github.com/nymtech/nym/pull/1376 [#1393]: https://github.com/nymtech/nym/pull/1393 [#1404]: https://github.com/nymtech/nym/pull/1404 +[#1419]: https://github.com/nymtech/nym/pull/1419 [#1427]: https://github.com/nymtech/nym/pull/1427 ## [nym-wallet-v1.0.7](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.7) (2022-07-11) diff --git a/Cargo.lock b/Cargo.lock index 7821a14118..e190bb6131 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -782,9 +782,8 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "cosmos-sdk-proto" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f109fe191e73898d74b8020c50f86018364ad19bc30318aa074616c382b52856" +version = "0.12.3" +source = "git+https://github.com/neacsu/cosmos-rust?branch=neacsu/feegrant_support#484f3b8bd39e00c2db567e754d4b7337b7c635a6" dependencies = [ "prost 0.10.3", "prost-types 0.10.1", @@ -793,9 +792,8 @@ dependencies = [ [[package]] name = "cosmrs" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8413275b23cb5a0734d9d1e3e33f0b5b94547c1e94776dbc3149dbf46588a533" +version = "0.7.1" +source = "git+https://github.com/neacsu/cosmos-rust?branch=neacsu/feegrant_support#484f3b8bd39e00c2db567e754d4b7337b7c635a6" dependencies = [ "bip32", "cosmos-sdk-proto", diff --git a/common/client-libs/validator-client/Cargo.toml b/common/client-libs/validator-client/Cargo.toml index 4e0ca6be12..67c472710c 100644 --- a/common/client-libs/validator-client/Cargo.toml +++ b/common/client-libs/validator-client/Cargo.toml @@ -35,7 +35,7 @@ validator-api-requests = { path = "../../../validator-api/validator-api-requests async-trait = { version = "0.1.51", optional = true } bip39 = { version = "1", features = ["rand"], optional = true } config = { path = "../../config", optional = true } -cosmrs = { version = "0.7.0", features = ["rpc", "bip32", "cosmwasm"], optional = true} +cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", features = ["rpc", "bip32", "cosmwasm"], optional = true} prost = { version = "0.10", default-features = false, optional = true } flate2 = { version = "1.0.20", optional = true } sha2 = { version = "0.9.5", optional = true } diff --git a/common/client-libs/validator-client/src/client.rs b/common/client-libs/validator-client/src/client.rs index b8fe902d51..45f2ec5a91 100644 --- a/common/client-libs/validator-client/src/client.rs +++ b/common/client-libs/validator-client/src/client.rs @@ -5,7 +5,7 @@ use crate::{validator_api, ValidatorClientError}; use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond}; use url::Url; use validator_api_requests::coconut::{ - BlindSignRequestBody, BlindedSignatureResponse, ExecuteReleaseFundsRequestBody, + BlindSignRequestBody, BlindedSignatureResponse, CosmosAddressResponse, ProposeReleaseFundsRequestBody, ProposeReleaseFundsResponse, VerificationKeyResponse, VerifyCredentialBody, VerifyCredentialResponse, }; @@ -734,6 +734,10 @@ impl ApiClient { Ok(self.validator_api.get_coconut_verification_key().await?) } + pub async fn get_cosmos_address(&self) -> Result { + Ok(self.validator_api.get_cosmos_address().await?) + } + pub async fn verify_bandwidth_credential( &self, request_body: &VerifyCredentialBody, @@ -753,14 +757,4 @@ impl ApiClient { .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?) - } } diff --git a/common/client-libs/validator-client/src/nymd/cosmwasm_client/signing_client.rs b/common/client-libs/validator-client/src/nymd/cosmwasm_client/signing_client.rs index 2452683a15..cc6bc9ce17 100644 --- a/common/client-libs/validator-client/src/nymd/cosmwasm_client/signing_client.rs +++ b/common/client-libs/validator-client/src/nymd/cosmwasm_client/signing_client.rs @@ -12,6 +12,9 @@ use crate::nymd::{Coin, GasAdjustable, GasPrice, TxResponse}; use async_trait::async_trait; use cosmrs::bank::MsgSend; use cosmrs::distribution::MsgWithdrawDelegatorReward; +use cosmrs::feegrant::{ + AllowedMsgAllowance, BasicAllowance, MsgGrantAllowance, MsgRevokeAllowance, +}; use cosmrs::proto::cosmos::tx::signing::v1beta1::SignMode; use cosmrs::rpc::endpoint::broadcast; use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl, SimpleRequest}; @@ -23,7 +26,7 @@ use serde::Serialize; use sha2::Digest; use sha2::Sha256; use std::convert::TryInto; -use std::time::Duration; +use std::time::{Duration, SystemTime}; const DEFAULT_BROADCAST_POLLING_RATE: Duration = Duration::from_secs(4); const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60); @@ -420,6 +423,63 @@ pub trait SigningCosmWasmClient: CosmWasmClient { .check_response() } + #[allow(clippy::too_many_arguments)] + async fn grant_allowance( + &self, + granter: &AccountId, + grantee: &AccountId, + spend_limit: Vec, + expiration: Option, + allowed_messages: Vec, + fee: Fee, + memo: impl Into + Send + 'static, + ) -> Result { + let basic_allowance = BasicAllowance { + spend_limit: spend_limit.into_iter().map(Into::into).collect(), + expiration, + } + .to_any() + .map_err(|_| NymdError::SerializationError("BasicAllowance".to_owned()))?; + + let allowed_msg_allowance = AllowedMsgAllowance { + allowance: Some(basic_allowance), + allowed_messages, + } + .to_any() + .map_err(|_| NymdError::SerializationError("AllowedMsgAllowance".to_owned()))?; + + let grant_allowance_msg = MsgGrantAllowance { + granter: granter.to_owned(), + grantee: grantee.to_owned(), + allowance: Some(allowed_msg_allowance), + } + .to_any() + .map_err(|_| NymdError::SerializationError("MsgGrantAllowance".to_owned()))?; + + self.sign_and_broadcast(granter, vec![grant_allowance_msg], fee, memo) + .await? + .check_response() + } + + async fn revoke_allowance( + &self, + granter: &AccountId, + grantee: &AccountId, + fee: Fee, + memo: impl Into + Send + 'static, + ) -> Result { + let revoke_allowance_msg = MsgRevokeAllowance { + granter: granter.to_owned(), + grantee: grantee.to_owned(), + } + .to_any() + .map_err(|_| NymdError::SerializationError("MsgRevokeAllowance".to_owned()))?; + + self.sign_and_broadcast(granter, vec![revoke_allowance_msg], fee, memo) + .await? + .check_response() + } + async fn delegate_tokens( &self, delegator_address: &AccountId, diff --git a/common/client-libs/validator-client/src/nymd/mod.rs b/common/client-libs/validator-client/src/nymd/mod.rs index 5fb967c963..ceeabd5133 100644 --- a/common/client-libs/validator-client/src/nymd/mod.rs +++ b/common/client-libs/validator-client/src/nymd/mod.rs @@ -26,6 +26,7 @@ use mixnet_contract_common::{ }; use serde::Serialize; use std::convert::TryInto; +use std::time::SystemTime; use vesting_contract_common::ExecuteMsg as VestingExecuteMsg; use vesting_contract_common::QueryMsg as VestingQueryMsg; @@ -845,6 +846,49 @@ impl NymdClient { .await } + /// Grant a fee allowance from one address to another + pub async fn grant_allowance( + &self, + grantee: &AccountId, + spend_limit: Vec, + expiration: Option, + allowed_messages: Vec, + memo: impl Into + Send + 'static, + fee: Option, + ) -> Result + where + C: SigningCosmWasmClient + Sync, + { + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + self.client + .grant_allowance( + self.address(), + grantee, + spend_limit, + expiration, + allowed_messages, + fee, + memo, + ) + .await + } + + /// Revoke a fee allowance from one address to another + pub async fn revoke_allowance( + &self, + grantee: &AccountId, + memo: impl Into + Send + 'static, + fee: Option, + ) -> Result + where + C: SigningCosmWasmClient + Sync, + { + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + self.client + .revoke_allowance(self.address(), grantee, fee, memo) + .await + } + pub async fn execute( &self, contract_address: &AccountId, diff --git a/common/client-libs/validator-client/src/validator_api/mod.rs b/common/client-libs/validator-client/src/validator_api/mod.rs index f6c1d16e20..f326904fb4 100644 --- a/common/client-libs/validator-client/src/validator_api/mod.rs +++ b/common/client-libs/validator-client/src/validator_api/mod.rs @@ -9,8 +9,8 @@ use std::collections::HashMap; use url::Url; use validator_api_requests::coconut::{ BlindSignRequestBody, BlindedSignatureResponse, CosmosAddressResponse, - ExecuteReleaseFundsRequestBody, ProposeReleaseFundsRequestBody, ProposeReleaseFundsResponse, - VerificationKeyResponse, VerifyCredentialBody, VerifyCredentialResponse, + ProposeReleaseFundsRequestBody, ProposeReleaseFundsResponse, VerificationKeyResponse, + VerifyCredentialBody, VerifyCredentialResponse, }; use validator_api_requests::models::{ CoreNodeStatusResponse, InclusionProbabilityResponse, MixNodeBondAnnotated, @@ -422,23 +422,6 @@ impl Client { ) .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. diff --git a/common/client-libs/validator-client/src/validator_api/routes.rs b/common/client-libs/validator-client/src/validator_api/routes.rs index 4c156e06db..a0cd6011b9 100644 --- a/common/client-libs/validator-client/src/validator_api/routes.rs +++ b/common/client-libs/validator-client/src/validator_api/routes.rs @@ -20,7 +20,6 @@ pub const COCONUT_VERIFICATION_KEY: &str = "verification-key"; pub const COCONUT_COSMOS_ADDRESS: &str = "cosmos-address"; 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"; diff --git a/common/credentials/Cargo.toml b/common/credentials/Cargo.toml index 827e257e92..110d6ee224 100644 --- a/common/credentials/Cargo.toml +++ b/common/credentials/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] bls12_381 = { version = "0.5", default-features = false, features = ["pairings", "alloc", "experimental"] } -cosmrs = { version = "0.7.0", optional = true } +cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", optional = true } thiserror = "1.0" url = "2.2" diff --git a/common/types/Cargo.toml b/common/types/Cargo.toml index 7c692d86fd..8e9d7c3edf 100644 --- a/common/types/Cargo.toml +++ b/common/types/Cargo.toml @@ -20,7 +20,7 @@ url = "2.2" ts-rs = "6.1.2" cosmwasm-std = "1.0.0-beta8" -cosmrs = "0.7.0" +cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } validator-client = { path = "../../common/client-libs/validator-client", features = [ "nymd-client", diff --git a/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs b/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs index 4eaa4232e0..b372cbcc94 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs @@ -48,6 +48,9 @@ pub(crate) enum RequestHandlingError { #[error("This gateway is not running in the disabled credentials mode")] NotInDisabledCredentialsMode, + #[error("Nymd Error - {0}")] + NymdError(#[from] validator_client::nymd::error::NymdError), + #[cfg(not(feature = "coconut"))] #[error("Ethereum web3 error")] Web3Error(#[from] web3::Error), @@ -60,10 +63,6 @@ pub(crate) enum RequestHandlingError { #[error("Ethereum contract error")] EthContractError(#[from] web3::contract::Error), - #[cfg(not(feature = "coconut"))] - #[error("Nymd Error - {0}")] - NymdError(#[from] validator_client::nymd::error::NymdError), - #[cfg(feature = "coconut")] #[error("Validator API error")] APIError(#[from] validator_client::ValidatorClientError), @@ -71,6 +70,14 @@ pub(crate) enum RequestHandlingError { #[cfg(feature = "coconut")] #[error("Not enough validator API endpoints provided. Needed {needed}, received {received}")] NotEnoughValidatorAPIs { received: usize, needed: usize }, + + #[cfg(feature = "coconut")] + #[error("Validator API {url} misbehaved in the bandwidth redemption protocol: {reason}")] + MisbehavingAPI { url: String, reason: String }, + + #[cfg(feature = "coconut")] + #[error("Coconut interface error - {0}")] + CoconutInterfaceError(#[from] coconut_interface::error::CoconutInterfaceError), } impl RequestHandlingError { @@ -223,46 +230,9 @@ where )); } - let req = validator_api_requests::coconut::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 = validator_api_requests::coconut::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 = validator_api_requests::coconut::ExecuteReleaseFundsRequestBody::new(proposal_id); self.inner .coconut_verifier - .api_clients() - .get(0) - .ok_or(RequestHandlingError::NotEnoughValidatorAPIs { - needed: 1, - received: 0, - })? - .execute_release_funds(&req) + .release_funds(&credential) .await?; let bandwidth = Bandwidth::from(credential); 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 06fb8fd4eb..48106eda07 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs @@ -1,40 +1,134 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use coconut_interface::VerificationKey; +use std::time::{Duration, SystemTime}; + +use log::*; + +use coconut_interface::{Credential, VerificationKey}; +use network_defaults::MIX_DENOM; use validator_client::{ - nymd::{NymdClient, SigningNymdClient}, + nymd::{ + traits::{MultisigSigningClient, QueryClient}, + Coin, Fee, NymdClient, SigningNymdClient, + }, ApiClient, }; -pub struct CoconutVerifier { +use super::authenticated::RequestHandlingError; + +const ONE_HOUR_SEC: u64 = 3600; +const MAX_FEEGRANT_UNYM: u128 = 10000; + +pub(crate) struct CoconutVerifier { api_clients: Vec, - _nymd_client: NymdClient, + nymd_client: NymdClient, aggregated_verification_key: VerificationKey, } impl CoconutVerifier { pub fn new( api_clients: Vec, - _nymd_client: NymdClient, + nymd_client: NymdClient, aggregated_verification_key: VerificationKey, - ) -> Self { - CoconutVerifier { - api_clients, - _nymd_client, - aggregated_verification_key, + ) -> Result { + if api_clients.is_empty() { + return Err(RequestHandlingError::NotEnoughValidatorAPIs { + received: 0, + needed: 1, + }); } - } - - pub fn api_clients(&self) -> &Vec { - &self.api_clients - } - - pub fn _nymd_client(&self) -> &NymdClient { - &self._nymd_client + Ok(CoconutVerifier { + api_clients, + nymd_client, + aggregated_verification_key, + }) } pub fn aggregated_verification_key(&self) -> &VerificationKey { &self.aggregated_verification_key } + + pub async fn release_funds(&self, 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 first_api_client = self + .api_clients + .get(0) + .expect("This shouldn't happen, as we check for length in constructor"); + + let first_api_cosmos_addr = first_api_client.get_cosmos_address().await?.addr; + self.nymd_client + .grant_allowance( + &first_api_cosmos_addr, + vec![Coin::new(MAX_FEEGRANT_UNYM, MIX_DENOM.base)], + 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 propose the release of funds".to_string(), + None, + ) + .await?; + + let req = validator_api_requests::coconut::ProposeReleaseFundsRequestBody::new( + credential.clone(), + self.nymd_client.address().clone(), + ); + let ret = first_api_client.propose_release_funds(&req).await; + + self.nymd_client + .revoke_allowance( + &first_api_cosmos_addr, + "Cleanup the previous allowance for releasing funds".to_string(), + revoke_fee.clone(), + ) + .await?; + + let proposal_id = ret?.proposal_id; + + let proposal = self.nymd_client.get_proposal(proposal_id).await?; + if !credential.has_blinded_serial_number(&proposal.description)? { + return Err(RequestHandlingError::MisbehavingAPI { + url: first_api_client.validator_api.current_url().to_string(), + reason: String::from("Created proposal with different serial number"), + }); + } + + let req = validator_api_requests::coconut::VerifyCredentialBody::new( + credential.clone(), + proposal_id, + self.nymd_client.address().clone(), + ); + for client in self.api_clients.iter().skip(1) { + let api_cosmos_addr = client.get_cosmos_address().await?.addr; + self.nymd_client + .grant_allowance( + &api_cosmos_addr, + vec![Coin::new(MAX_FEEGRANT_UNYM, MIX_DENOM.base)], + 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.verify_bandwidth_credential(&req).await; + self.nymd_client + .revoke_allowance( + &api_cosmos_addr, + "Cleanup the previous allowance for releasing funds".to_string(), + revoke_fee.clone(), + ) + .await?; + if !ret?.verification_result { + debug!("Validator {} didn't accept the credential. It will probably vote No on the spending proposal", client.validator_api.current_url()); + } + } + + self.nymd_client.execute_proposal(proposal_id, None).await?; + + Ok(()) + } } diff --git a/gateway/src/node/mod.rs b/gateway/src/node/mod.rs index d539dafb1a..b2354e7a3b 100644 --- a/gateway/src/node/mod.rs +++ b/gateway/src/node/mod.rs @@ -317,7 +317,8 @@ where self.all_api_clients(), nymd_client, validators_verification_key, - ); + ) + .expect("Could not create coconut verifier"); #[cfg(not(feature = "coconut"))] let erc20_bridge = ERC20Bridge::new(self.config.get_eth_endpoint(), nymd_client); diff --git a/nym-wallet/nym-wallet-types/Cargo.toml b/nym-wallet/nym-wallet-types/Cargo.toml index 103d32b5e6..2a6fb8836b 100644 --- a/nym-wallet/nym-wallet-types/Cargo.toml +++ b/nym-wallet/nym-wallet-types/Cargo.toml @@ -11,7 +11,7 @@ strum = { version = "0.23", features = ["derive"] } ts-rs = "6.1.2" cosmwasm-std = "1.0.0-beta8" -cosmrs = "0.7.0" +cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } config = { path = "../../common/config" } mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" } diff --git a/nym-wallet/src-tauri/Cargo.toml b/nym-wallet/src-tauri/Cargo.toml index a86630c682..f6ea6170fe 100644 --- a/nym-wallet/src-tauri/Cargo.toml +++ b/nym-wallet/src-tauri/Cargo.toml @@ -49,7 +49,7 @@ base64 = "0.13" zeroize = "1.4.3" cosmwasm-std = "1.0.0" -cosmrs = "0.7.0" +cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } validator-client = { path = "../../common/client-libs/validator-client", features = [ "nymd-client", diff --git a/validator-api/src/coconut/client.rs b/validator-api/src/coconut/client.rs index a8b3646692..612efc20c1 100644 --- a/validator-api/src/coconut/client.rs +++ b/validator-api/src/coconut/client.rs @@ -19,5 +19,4 @@ pub trait Client { ) -> Result; async fn vote_proposal(&self, proposal_id: u64, vote_yes: bool, fee: Option) -> Result<()>; - async fn execute_proposal(&self, proposal_id: u64, fee: Option) -> Result<()>; } diff --git a/validator-api/src/coconut/mod.rs b/validator-api/src/coconut/mod.rs index f4c8203d2d..2eba8eed0d 100644 --- a/validator-api/src/coconut/mod.rs +++ b/validator-api/src/coconut/mod.rs @@ -25,9 +25,10 @@ use crypto::shared_key::new_ephemeral_shared_key; use crypto::symmetric::stream_cipher; use validator_api_requests::coconut::{ BlindSignRequestBody, BlindedSignatureResponse, CosmosAddressResponse, - ExecuteReleaseFundsRequestBody, ProposeReleaseFundsRequestBody, ProposeReleaseFundsResponse, - VerificationKeyResponse, VerifyCredentialBody, VerifyCredentialResponse, + ProposeReleaseFundsRequestBody, ProposeReleaseFundsResponse, VerificationKeyResponse, + VerifyCredentialBody, VerifyCredentialResponse, }; +use validator_client::nymd::Fee; use validator_client::validator_api::routes::{BANDWIDTH, COCONUT_ROUTES}; use getset::{CopyGetters, Getters}; @@ -175,8 +176,7 @@ impl InternalSignRequest { get_cosmos_address, post_partial_bandwidth_credential, verify_bandwidth_credential, - post_propose_release_funds, - post_execute_release_funds + post_propose_release_funds ], ) }) @@ -282,7 +282,15 @@ pub async fn verify_bandwidth_credential( // Vote yes or no on the proposal based on the verification result state .client - .vote_proposal(proposal_id, verification_result, None) + .vote_proposal( + proposal_id, + verification_result, + Some(Fee::PayerGranterAuto( + None, + None, + Some(verify_credential_body.0.gateway_cosmos_addr().to_owned()), + )), + ) .await?; Ok(Json(VerifyCredentialResponse::new(verification_result))) @@ -307,19 +315,17 @@ pub async fn post_propose_release_funds( 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) + .propose_release_funds( + title, + blinded_serial_number, + voucher_value, + Some(Fee::PayerGranterAuto( + None, + None, + Some(propose_release_funds.0.gateway_cosmos_addr().to_owned()), + )), + ) .await?; Ok(Json(ProposeReleaseFundsResponse::new(proposal_id))) } - -#[post("/execute-release-funds", data = "")] -pub async fn post_execute_release_funds( - execute_release_funds: Json, - state: &RocketState, -) -> Result> { - let proposal_id = *execute_release_funds.0.proposal_id(); - state.client.execute_proposal(proposal_id, None).await?; - - Ok(Json(())) -} diff --git a/validator-api/src/coconut/tests.rs b/validator-api/src/coconut/tests.rs index 61fdb6dc4c..1fd66b9160 100644 --- a/validator-api/src/coconut/tests.rs +++ b/validator-api/src/coconut/tests.rs @@ -86,14 +86,6 @@ impl super::client::Client for DummyClient { ) -> Result<()> { todo!() } - - async fn execute_proposal( - &self, - _proposal_id: u64, - _fee: Option, - ) -> crate::coconut::error::Result<()> { - todo!() - } } pub fn tx_entry_fixture(tx_hash: &str) -> TxResponse { diff --git a/validator-api/src/nymd_client.rs b/validator-api/src/nymd_client.rs index 21532f90be..6977e73c1b 100644 --- a/validator-api/src/nymd_client.rs +++ b/validator-api/src/nymd_client.rs @@ -497,18 +497,4 @@ where .await?; Ok(()) } - - async fn execute_proposal( - &self, - proposal_id: u64, - fee: Option, - ) -> Result<(), CoconutError> { - self.0 - .read() - .await - .nymd - .execute_proposal(proposal_id, fee) - .await?; - Ok(()) - } } diff --git a/validator-api/validator-api-requests/Cargo.toml b/validator-api/validator-api-requests/Cargo.toml index 1fbb6ab2ec..237be6e62a 100644 --- a/validator-api/validator-api-requests/Cargo.toml +++ b/validator-api/validator-api-requests/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] bs58 = "0.4.0" -cosmrs = { version = "0.7.0" } +cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } getset = "0.1.1" schemars = { version = "0.8", features = ["preserve_order"] } serde = "1.0" diff --git a/validator-api/validator-api-requests/src/coconut.rs b/validator-api/validator-api-requests/src/coconut.rs index f50fbc9fec..917934dd52 100644 --- a/validator-api/validator-api-requests/src/coconut.rs +++ b/validator-api/validator-api-requests/src/coconut.rs @@ -15,13 +15,20 @@ pub struct VerifyCredentialBody { credential: Credential, #[getset(get = "pub")] proposal_id: u64, + #[getset(get = "pub")] + gateway_cosmos_addr: AccountId, } impl VerifyCredentialBody { - pub fn new(credential: Credential, proposal_id: u64) -> VerifyCredentialBody { + pub fn new( + credential: Credential, + proposal_id: u64, + gateway_cosmos_addr: AccountId, + ) -> VerifyCredentialBody { VerifyCredentialBody { credential, proposal_id, + gateway_cosmos_addr, } } } @@ -154,11 +161,16 @@ impl CosmosAddressResponse { pub struct ProposeReleaseFundsRequestBody { #[getset(get = "pub")] credential: Credential, + #[getset(get = "pub")] + gateway_cosmos_addr: AccountId, } impl ProposeReleaseFundsRequestBody { - pub fn new(credential: Credential) -> Self { - ProposeReleaseFundsRequestBody { credential } + pub fn new(credential: Credential, gateway_cosmos_addr: AccountId) -> Self { + ProposeReleaseFundsRequestBody { + credential, + gateway_cosmos_addr, + } } } @@ -172,15 +184,3 @@ impl ProposeReleaseFundsResponse { 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 } - } -}