prepare new api endpoint
This commit is contained in:
@@ -9,8 +9,8 @@ use crate::{
|
||||
ReqwestRpcClient, ValidatorClientError,
|
||||
};
|
||||
use nym_api_requests::coconut::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, EcashParametersResponse, VerifyCredentialBody,
|
||||
VerifyCredentialResponse,
|
||||
BlindSignRequestBody, BlindedSignatureResponse, EcashParametersResponse,
|
||||
OfflineVerifyCredentialBody, OnlineVerifyCredentialBody, VerifyCredentialResponse,
|
||||
};
|
||||
use nym_api_requests::models::{DescribedGateway, MixNodeBondAnnotated};
|
||||
use nym_api_requests::models::{
|
||||
@@ -334,11 +334,18 @@ impl NymApiClient {
|
||||
|
||||
pub async fn verify_offline_credential(
|
||||
&self,
|
||||
request_body: &VerifyCredentialBody,
|
||||
request_body: &OfflineVerifyCredentialBody,
|
||||
) -> Result<VerifyCredentialResponse, ValidatorClientError> {
|
||||
Ok(self.nym_api.verify_offline_credential(request_body).await?)
|
||||
}
|
||||
|
||||
pub async fn verify_online_credential(
|
||||
&self,
|
||||
request_body: &OnlineVerifyCredentialBody,
|
||||
) -> Result<VerifyCredentialResponse, ValidatorClientError> {
|
||||
Ok(self.nym_api.verify_online_credential(request_body).await?)
|
||||
}
|
||||
|
||||
pub async fn ecash_parameters(&self) -> Result<EcashParametersResponse, ValidatorClientError> {
|
||||
Ok(self.nym_api.ecash_parameters().await?)
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ use crate::nym_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
|
||||
use async_trait::async_trait;
|
||||
use http_api_client::{ApiClient, NO_PARAMS};
|
||||
use nym_api_requests::coconut::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, EcashParametersResponse, VerifyCredentialBody,
|
||||
VerifyCredentialResponse,
|
||||
BlindSignRequestBody, BlindedSignatureResponse, EcashParametersResponse,
|
||||
OfflineVerifyCredentialBody, OnlineVerifyCredentialBody, VerifyCredentialResponse,
|
||||
};
|
||||
use nym_api_requests::models::{
|
||||
ComputeRewardEstParam, DescribedGateway, GatewayBondAnnotated, GatewayCoreStatusResponse,
|
||||
@@ -385,7 +385,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
|
||||
async fn verify_offline_credential(
|
||||
&self,
|
||||
request_body: &VerifyCredentialBody,
|
||||
request_body: &OfflineVerifyCredentialBody,
|
||||
) -> Result<VerifyCredentialResponse, NymAPIError> {
|
||||
self.post_json(
|
||||
&[
|
||||
@@ -400,6 +400,23 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn verify_online_credential(
|
||||
&self,
|
||||
request_body: &OnlineVerifyCredentialBody,
|
||||
) -> Result<VerifyCredentialResponse, NymAPIError> {
|
||||
self.post_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::COCONUT_ROUTES,
|
||||
routes::BANDWIDTH,
|
||||
routes::ECASH_VERIFY_ONLINE_CREDENTIAL,
|
||||
],
|
||||
NO_PARAMS,
|
||||
request_body,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn ecash_parameters(&self) -> Result<EcashParametersResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
|
||||
@@ -17,6 +17,7 @@ pub const BANDWIDTH: &str = "bandwidth";
|
||||
|
||||
pub const COCONUT_BLIND_SIGN: &str = "blind-sign";
|
||||
pub const ECASH_VERIFY_OFFLINE_CREDENTIAL: &str = "verify-offline-credential";
|
||||
pub const ECASH_VERIFY_ONLINE_CREDENTIAL: &str = "verify-online-credential";
|
||||
pub const ECASH_PARAMETERS: &str = "ecash-parameters";
|
||||
|
||||
pub const STATUS_ROUTES: &str = "status";
|
||||
|
||||
@@ -12,24 +12,44 @@ use nym_compact_ecash::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Getters, CopyGetters, Clone)]
|
||||
pub struct VerifyCredentialBody {
|
||||
pub struct OfflineVerifyCredentialBody {
|
||||
#[getset(get = "pub")]
|
||||
credential: EcashCredential,
|
||||
//#[getset(get = "pub")]
|
||||
//proposal_id: u64,
|
||||
#[getset(get = "pub")]
|
||||
gateway_cosmos_addr: AccountId,
|
||||
}
|
||||
|
||||
impl VerifyCredentialBody {
|
||||
impl OfflineVerifyCredentialBody {
|
||||
pub fn new(
|
||||
credential: EcashCredential,
|
||||
//proposal_id: u64,
|
||||
gateway_cosmos_addr: AccountId,
|
||||
) -> VerifyCredentialBody {
|
||||
VerifyCredentialBody {
|
||||
) -> OfflineVerifyCredentialBody {
|
||||
OfflineVerifyCredentialBody {
|
||||
credential,
|
||||
//proposal_id,
|
||||
gateway_cosmos_addr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Getters, CopyGetters, Clone)]
|
||||
pub struct OnlineVerifyCredentialBody {
|
||||
#[getset(get = "pub")]
|
||||
credential: EcashCredential,
|
||||
#[getset(get = "pub")]
|
||||
proposal_id: u64,
|
||||
#[getset(get = "pub")]
|
||||
gateway_cosmos_addr: AccountId,
|
||||
}
|
||||
|
||||
impl OnlineVerifyCredentialBody {
|
||||
pub fn new(
|
||||
credential: EcashCredential,
|
||||
proposal_id: u64,
|
||||
gateway_cosmos_addr: AccountId,
|
||||
) -> OnlineVerifyCredentialBody {
|
||||
OnlineVerifyCredentialBody {
|
||||
credential,
|
||||
proposal_id,
|
||||
gateway_cosmos_addr,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +83,12 @@ pub enum CoconutError {
|
||||
#[error("Credentials error - {0}")]
|
||||
CredentialsError(#[from] nym_credentials::error::Error),
|
||||
|
||||
#[error("Incorrect credential proposal description: {reason}")]
|
||||
IncorrectProposal { reason: String },
|
||||
|
||||
#[error("Invalid status of credential: {status}")]
|
||||
InvalidCredentialStatus { status: String },
|
||||
|
||||
#[error("DKG error: {0}")]
|
||||
DkgError(#[from] DkgError),
|
||||
|
||||
|
||||
@@ -9,11 +9,15 @@ use crate::support::storage::NymApiStorage;
|
||||
use getset::{CopyGetters, Getters};
|
||||
use keypair::KeyPair;
|
||||
use nym_api_requests::coconut::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, EcashParametersResponse, VerifyCredentialBody,
|
||||
VerifyCredentialResponse,
|
||||
BlindSignRequestBody, BlindedSignatureResponse, EcashParametersResponse,
|
||||
OfflineVerifyCredentialBody, OnlineVerifyCredentialBody, VerifyCredentialResponse,
|
||||
};
|
||||
use nym_coconut_bandwidth_contract_common::spend_credential::{
|
||||
funds_from_cosmos_msgs, SpendCredentialStatus,
|
||||
};
|
||||
use nym_coconut_dkg_common::types::EpochId;
|
||||
|
||||
use crate::coconut::helpers::accepted_vote_err;
|
||||
use nym_compact_ecash::error::CompactEcashError;
|
||||
use nym_compact_ecash::scheme::keygen::KeyPairAuth;
|
||||
use nym_compact_ecash::scheme::withdrawal::WithdrawalRequest;
|
||||
@@ -30,6 +34,7 @@ use nym_crypto::shared_key::new_ephemeral_shared_key;
|
||||
use nym_crypto::symmetric::stream_cipher;
|
||||
use nym_validator_client::nym_api::routes::{BANDWIDTH, COCONUT_ROUTES};
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
use nym_validator_client::nyxd::{Coin, Fee};
|
||||
use rand_07::rngs::OsRng;
|
||||
use rocket::fairing::AdHoc;
|
||||
use rocket::serde::json::Json;
|
||||
@@ -50,6 +55,7 @@ pub(crate) mod tests;
|
||||
|
||||
pub struct State {
|
||||
client: Arc<dyn LocalClient + Send + Sync>,
|
||||
mix_denom: String,
|
||||
key_pair: KeyPair,
|
||||
ecash_params: Parameters,
|
||||
comm_channel: Arc<dyn APICommunicationChannel + Send + Sync>,
|
||||
@@ -60,6 +66,7 @@ pub struct State {
|
||||
impl State {
|
||||
pub(crate) fn new<C, D>(
|
||||
client: C,
|
||||
mix_denom: String,
|
||||
key_pair: KeyPair,
|
||||
comm_channel: D,
|
||||
storage: NymApiStorage,
|
||||
@@ -76,6 +83,7 @@ impl State {
|
||||
let ecash_params = Parameters::try_from_bs58(params_base58).unwrap(); //SW Waiting for an actual parameters generation scheme.
|
||||
Self {
|
||||
client,
|
||||
mix_denom,
|
||||
key_pair,
|
||||
ecash_params,
|
||||
comm_channel,
|
||||
@@ -178,6 +186,7 @@ impl InternalSignRequest {
|
||||
|
||||
pub fn stage<C, D>(
|
||||
client: C,
|
||||
mix_denom: String,
|
||||
key_pair: KeyPair,
|
||||
comm_channel: D,
|
||||
storage: NymApiStorage,
|
||||
@@ -186,12 +195,16 @@ impl InternalSignRequest {
|
||||
C: LocalClient + Send + Sync + 'static,
|
||||
D: APICommunicationChannel + Send + Sync + 'static,
|
||||
{
|
||||
let state = State::new(client, key_pair, comm_channel, storage);
|
||||
let state = State::new(client, mix_denom, key_pair, comm_channel, storage);
|
||||
AdHoc::on_ignite("Internal Sign Request Stage", |rocket| async {
|
||||
rocket.manage(state).mount(
|
||||
// this format! is so ugly...
|
||||
format!("/{}/{}/{}", NYM_API_VERSION, COCONUT_ROUTES, BANDWIDTH),
|
||||
routes![post_blind_sign, verify_offline_credential],
|
||||
routes![
|
||||
post_blind_sign,
|
||||
verify_offline_credential,
|
||||
verify_online_credential
|
||||
],
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -248,7 +261,7 @@ pub async fn post_blind_sign(
|
||||
|
||||
#[post("/verify-offline-credential", data = "<verify_credential_body>")]
|
||||
pub async fn verify_offline_credential(
|
||||
verify_credential_body: Json<VerifyCredentialBody>,
|
||||
verify_credential_body: Json<OfflineVerifyCredentialBody>,
|
||||
state: &RocketState<State>,
|
||||
) -> Result<Json<VerifyCredentialResponse>> {
|
||||
let verification_key = state
|
||||
@@ -281,6 +294,81 @@ pub async fn verify_offline_credential(
|
||||
Ok(Json(VerifyCredentialResponse::new(true)))
|
||||
}
|
||||
|
||||
#[post("/verify-online-credential", data = "<verify_credential_body>")]
|
||||
pub async fn verify_online_credential(
|
||||
verify_credential_body: Json<OnlineVerifyCredentialBody>,
|
||||
state: &RocketState<State>,
|
||||
) -> Result<Json<VerifyCredentialResponse>> {
|
||||
let proposal_id = *verify_credential_body.proposal_id();
|
||||
let proposal = state.client.get_proposal(proposal_id).await?;
|
||||
// Proposal description is the blinded serial number
|
||||
if !verify_credential_body
|
||||
.credential()
|
||||
.has_blinded_serial_number(&proposal.description)?
|
||||
{
|
||||
return Err(CoconutError::IncorrectProposal {
|
||||
reason: String::from("incorrect blinded serial number in description"),
|
||||
});
|
||||
}
|
||||
let proposed_release_funds =
|
||||
funds_from_cosmos_msgs(proposal.msgs).ok_or(CoconutError::IncorrectProposal {
|
||||
reason: String::from("action is not to release funds"),
|
||||
})?;
|
||||
// Credential has not been spent before, and is on its way of being spent
|
||||
let credential_status = state
|
||||
.client
|
||||
.get_spent_credential(verify_credential_body.credential().blinded_serial_number())
|
||||
.await?
|
||||
.spend_credential
|
||||
.ok_or(CoconutError::InvalidCredentialStatus {
|
||||
status: String::from("Inexistent"),
|
||||
})?
|
||||
.status();
|
||||
if credential_status != SpendCredentialStatus::InProgress {
|
||||
return Err(CoconutError::InvalidCredentialStatus {
|
||||
status: format!("{:?}", credential_status),
|
||||
});
|
||||
}
|
||||
let verification_key = state
|
||||
.verification_key(*verify_credential_body.credential().epoch_id())
|
||||
.await?;
|
||||
let mut vote_yes = verify_credential_body
|
||||
.credential()
|
||||
.payment()
|
||||
.spend_verify(
|
||||
&state.ecash_params,
|
||||
&verification_key,
|
||||
verify_credential_body.credential().pay_info(),
|
||||
)
|
||||
.map_err(|_| {
|
||||
CoconutError::CompactEcashInternalError(CompactEcashError::PaymentVerification)
|
||||
})?;
|
||||
|
||||
//SW THIS WILL HAVE TO CHANGE AS WELL
|
||||
vote_yes &= Coin::from(proposed_release_funds)
|
||||
== Coin::new(
|
||||
verify_credential_body.credential().voucher_value() as u128,
|
||||
state.mix_denom.clone(),
|
||||
);
|
||||
|
||||
// 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().to_owned()),
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
accepted_vote_err(ret)?;
|
||||
|
||||
Ok(Json(VerifyCredentialResponse::new(vote_yes)))
|
||||
}
|
||||
|
||||
#[derive(Getters, CopyGetters)]
|
||||
pub struct EcashParameters {
|
||||
#[getset(get = "pub")]
|
||||
|
||||
@@ -5,7 +5,8 @@ use super::InternalSignRequest;
|
||||
use crate::coconut::error::{CoconutError, Result};
|
||||
use cosmwasm_std::{to_binary, Addr, CosmosMsg, Decimal, WasmMsg};
|
||||
use nym_api_requests::coconut::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
|
||||
BlindSignRequestBody, BlindedSignatureResponse, OnlineVerifyCredentialBody,
|
||||
VerifyCredentialResponse,
|
||||
};
|
||||
use nym_coconut::tests::helpers::theta_from_keys_and_attributes;
|
||||
use nym_coconut::{prepare_blind_sign, ttp_keygen, Base58, BlindedSignature, Parameters};
|
||||
@@ -880,8 +881,11 @@ async fn verification_of_bandwidth_credential() {
|
||||
let proposal_id = 42;
|
||||
// The address is not used, so we can use a duplicate
|
||||
let gateway_cosmos_addr = validator_address.clone();
|
||||
let req =
|
||||
VerifyCredentialBody::new(credential.clone(), proposal_id, gateway_cosmos_addr.clone());
|
||||
let req = OnlineVerifyCredentialBody::new(
|
||||
credential.clone(),
|
||||
proposal_id,
|
||||
gateway_cosmos_addr.clone(),
|
||||
);
|
||||
|
||||
// Test endpoint with not proposal for the proposal id
|
||||
let response = client
|
||||
@@ -1032,7 +1036,7 @@ async fn verification_of_bandwidth_credential() {
|
||||
0,
|
||||
);
|
||||
let bad_req =
|
||||
VerifyCredentialBody::new(bad_credential, proposal_id, gateway_cosmos_addr.clone());
|
||||
OnlineVerifyCredentialBody::new(bad_credential, proposal_id, gateway_cosmos_addr.clone());
|
||||
let response = client
|
||||
.post(format!(
|
||||
"/{}/{}/{}/{}",
|
||||
|
||||
@@ -70,6 +70,7 @@ pub(crate) async fn setup_rocket(
|
||||
let comm_channel = QueryCommunicationChannel::new(_nyxd_client.clone());
|
||||
rocket.attach(InternalSignRequest::stage(
|
||||
_nyxd_client.clone(),
|
||||
mix_denom,
|
||||
coconut_keypair,
|
||||
comm_channel,
|
||||
storage.clone().unwrap(),
|
||||
|
||||
Reference in New Issue
Block a user