Feature/fee grant (#1419)
* Temporarily point cosmrs to PR branch * Expose grant allowance in client * Move functions in coconut verifier * Do execute from gateway * Explicit fee payment by granter * Include revoke grant after finishing with the op * Gateway checks the proposal content before proceeding * Clippy fixes * CHANGELOG update
This commit is contained in:
committed by
GitHub
parent
61c0092f27
commit
ad547e516a
@@ -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)
|
||||
|
||||
Generated
+4
-6
@@ -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",
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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<CosmosAddressResponse, ValidatorClientError> {
|
||||
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?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Coin>,
|
||||
expiration: Option<SystemTime>,
|
||||
allowed_messages: Vec<String>,
|
||||
fee: Fee,
|
||||
memo: impl Into<String> + Send + 'static,
|
||||
) -> Result<TxResponse, NymdError> {
|
||||
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<String> + Send + 'static,
|
||||
) -> Result<TxResponse, NymdError> {
|
||||
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,
|
||||
|
||||
@@ -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<C> NymdClient<C> {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Grant a fee allowance from one address to another
|
||||
pub async fn grant_allowance(
|
||||
&self,
|
||||
grantee: &AccountId,
|
||||
spend_limit: Vec<Coin>,
|
||||
expiration: Option<SystemTime>,
|
||||
allowed_messages: Vec<String>,
|
||||
memo: impl Into<String> + Send + 'static,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<TxResponse, NymdError>
|
||||
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<String> + Send + 'static,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<TxResponse, NymdError>
|
||||
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<M>(
|
||||
&self,
|
||||
contract_address: &AccountId,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,40 +1,134 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<ApiClient>,
|
||||
_nymd_client: NymdClient<SigningNymdClient>,
|
||||
nymd_client: NymdClient<SigningNymdClient>,
|
||||
aggregated_verification_key: VerificationKey,
|
||||
}
|
||||
|
||||
impl CoconutVerifier {
|
||||
pub fn new(
|
||||
api_clients: Vec<ApiClient>,
|
||||
_nymd_client: NymdClient<SigningNymdClient>,
|
||||
nymd_client: NymdClient<SigningNymdClient>,
|
||||
aggregated_verification_key: VerificationKey,
|
||||
) -> Self {
|
||||
CoconutVerifier {
|
||||
api_clients,
|
||||
_nymd_client,
|
||||
aggregated_verification_key,
|
||||
) -> Result<Self, RequestHandlingError> {
|
||||
if api_clients.is_empty() {
|
||||
return Err(RequestHandlingError::NotEnoughValidatorAPIs {
|
||||
received: 0,
|
||||
needed: 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn api_clients(&self) -> &Vec<ApiClient> {
|
||||
&self.api_clients
|
||||
}
|
||||
|
||||
pub fn _nymd_client(&self) -> &NymdClient<SigningNymdClient> {
|
||||
&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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -19,5 +19,4 @@ pub trait Client {
|
||||
) -> 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<()>;
|
||||
}
|
||||
|
||||
@@ -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 = "<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(()))
|
||||
}
|
||||
|
||||
@@ -86,14 +86,6 @@ impl super::client::Client for DummyClient {
|
||||
) -> 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 {
|
||||
|
||||
@@ -497,18 +497,4 @@ where
|
||||
.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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user