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:
Bogdan-Ștefan Neacşu
2022-07-14 12:24:46 +03:00
committed by GitHub
parent 61c0092f27
commit ad547e516a
21 changed files with 288 additions and 160 deletions
+2
View File
@@ -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
View File
@@ -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";
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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(())
}
}
+2 -1
View File
@@ -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);
+1 -1
View File
@@ -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" }
+1 -1
View File
@@ -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",
-1
View File
@@ -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<()>;
}
+23 -17
View File
@@ -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(()))
}
-8
View File
@@ -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 {
-14
View File
@@ -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 }
}
}