nym-cli commands for issuing free passes

This commit is contained in:
Jędrzej Stuczyński
2024-02-12 16:26:19 +00:00
parent c2517ac63b
commit 3f0194a9aa
24 changed files with 439 additions and 34 deletions
Generated
+4
View File
@@ -5107,6 +5107,7 @@ dependencies = [
"cosmwasm-std",
"csv",
"cw-utils",
"futures",
"handlebars",
"humantime-serde",
"inquire",
@@ -5122,6 +5123,7 @@ dependencies = [
"nym-credential-storage",
"nym-credential-utils",
"nym-credentials",
"nym-credentials-interface",
"nym-crypto",
"nym-mixnet-contract-common",
"nym-multisig-contract-common",
@@ -5139,8 +5141,10 @@ dependencies = [
"tap",
"thiserror",
"time",
"tokio",
"toml 0.5.11",
"url",
"zeroize",
]
[[package]]
@@ -67,7 +67,7 @@ where
let signature =
obtain_aggregate_signature(&state.voucher, &coconut_api_clients, threshold).await?;
let issued = state.voucher.to_issued_credential(signature);
let issued = state.voucher.to_issued_credential(signature, epoch_id);
// make sure the data gets zeroized after persisting it
let credential_data = Zeroizing::new(issued.pack_v1());
+1 -1
View File
@@ -51,7 +51,7 @@ impl<C, St: Storage> BandwidthController<C, St> {
<St as Storage>::StorageError: Send + Sync + 'static,
{
let coconut_api_clients = all_coconut_api_clients(&self.client, epoch_id).await?;
Ok(obtain_aggregate_verification_key(&coconut_api_clients).await?)
Ok(obtain_aggregate_verification_key(&coconut_api_clients)?)
}
pub async fn prepare_bandwidth_credential(
@@ -8,8 +8,10 @@ use crate::{
nym_api, DirectSigningReqwestRpcValidatorClient, QueryReqwestRpcValidatorClient,
ReqwestRpcClient, ValidatorClientError,
};
use nym_api_requests::coconut::models::FreePassNonceResponse;
use nym_api_requests::coconut::{
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
BlindSignRequestBody, BlindedSignatureResponse, FreePassRequest, VerifyCredentialBody,
VerifyCredentialResponse,
};
use nym_api_requests::models::{DescribedGateway, MixNodeBondAnnotated};
use nym_api_requests::models::{
@@ -348,4 +350,15 @@ impl NymApiClient {
.verify_bandwidth_credential(request_body)
.await?)
}
pub async fn free_pass_nonce(&self) -> Result<FreePassNonceResponse, ValidatorClientError> {
Ok(self.nym_api.free_pass_nonce().await?)
}
pub async fn issue_free_pass_credential(
&self,
request: &FreePassRequest,
) -> Result<BlindedSignatureResponse, ValidatorClientError> {
Ok(self.nym_api.free_pass(request).await?)
}
}
@@ -32,6 +32,8 @@ pub mod error;
pub mod routes;
pub use http_api_client::Client;
use nym_api_requests::coconut::models::FreePassNonceResponse;
use nym_api_requests::coconut::FreePassRequest;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
@@ -373,6 +375,36 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn free_pass_nonce(&self) -> Result<FreePassNonceResponse, NymAPIError> {
self.get_json(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
routes::BANDWIDTH,
routes::COCONUT_FREE_PASS_NONCE,
],
NO_PARAMS,
)
.await
}
async fn free_pass(
&self,
request: &FreePassRequest,
) -> Result<BlindedSignatureResponse, NymAPIError> {
self.post_json(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
routes::BANDWIDTH,
routes::COCONUT_FREE_PASS_NONCE,
],
NO_PARAMS,
request,
)
.await
}
async fn blind_sign(
&self,
request_body: &BlindSignRequestBody,
@@ -16,6 +16,7 @@ pub const COCONUT_ROUTES: &str = "coconut";
pub const BANDWIDTH: &str = "bandwidth";
pub const COCONUT_FREE_PASS: &str = "free-pass";
pub const COCONUT_FREE_PASS_NONCE: &str = "free-pass-nonce";
pub const COCONUT_BLIND_SIGN: &str = "blind-sign";
pub const COCONUT_VERIFY_BANDWIDTH_CREDENTIAL: &str = "verify-bandwidth-credential";
pub const COCONUT_EPOCH_CREDENTIALS: &str = "epoch-credentials";
@@ -358,6 +358,10 @@ where
S: OfflineSigner + Send + Sync,
NyxdError: From<<S as OfflineSigner>::Error>,
{
pub fn signing_account(&self) -> Result<AccountData, NyxdError> {
Ok(self.find_account(&self.address())?)
}
pub fn address(&self) -> AccountId {
match self.client.signer_addresses() {
Ok(addresses) => addresses[0].clone(),
+4
View File
@@ -15,6 +15,7 @@ cfg-if = "1.0.0"
clap = { workspace = true, features = ["derive"] }
csv = "1.3.0"
cw-utils = { workspace = true }
futures = { workspace = true }
handlebars = "3.0.1"
humantime-serde = "1.0"
inquire = "0.6.2"
@@ -25,9 +26,11 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = { workspace = true }
thiserror = { workspace = true }
time = { workspace = true, features = ["parsing", "formatting"] }
tokio = { workspace = true, features = ["sync"]}
toml = "0.5.6"
url = { workspace = true }
tap = "1"
zeroize = { workspace = true }
cosmrs = { workspace = true }
cosmwasm-std = { workspace = true }
@@ -49,6 +52,7 @@ nym-sphinx = { path = "../../common/nymsphinx" }
nym-client-core = { path = "../../common/client-core" }
nym-config = { path = "../../common/config" }
nym-credentials = { path = "../../common/credentials" }
nym-credentials-interface = { path = "../../common/credentials-interface" }
nym-credential-storage = { path = "../../common/credential-storage" }
nym-credential-utils = { path = "../../common/credential-utils" }
@@ -0,0 +1,188 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use anyhow::{anyhow, bail};
use clap::ArgGroup;
use clap::Parser;
use futures::StreamExt;
use log::{error, info};
use nym_coconut_dkg_common::types::EpochId;
use nym_credential_utils::utils::block_until_coconut_is_available;
use nym_credentials::coconut::bandwidth::freepass::MAX_FREE_PASS_VALIDITY;
use nym_credentials::{
obtain_aggregate_verification_key, IssuanceBandwidthCredential, IssuedBandwidthCredential,
};
use nym_credentials_interface::VerificationKey;
use nym_validator_client::coconut::all_coconut_api_clients;
use nym_validator_client::nyxd::contract_traits::{DkgQueryClient, NymContractsProvider};
use nym_validator_client::nyxd::CosmWasmClient;
use nym_validator_client::signing::AccountData;
use nym_validator_client::CoconutApiClient;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::sync::Arc;
use time::format_description::well_known::Rfc3339;
use time::OffsetDateTime;
use zeroize::Zeroizing;
fn parse_rfc3339_expiration_date(raw: &str) -> Result<OffsetDateTime, time::error::Parse> {
OffsetDateTime::parse(raw, &Rfc3339)
}
#[derive(Debug, Parser)]
#[clap(group(ArgGroup::new("expiration").required(true)))]
pub struct Args {
/// Specifies the expiration date of the free pass(es)
/// Requires
#[clap(long, group = "expiration", value_parser = parse_rfc3339_expiration_date)]
pub(crate) expiration_date: Option<OffsetDateTime>,
#[clap(long, group = "expiration")]
pub(crate) expiration_timestamp: Option<i64>,
/// The number of free passes to issue
#[clap(long, default_value = "1")]
pub(crate) amount: u64,
/// Path to the output directory for generated free passes.
#[clap(long)]
pub(crate) output_dir: PathBuf,
}
async fn get_freepass(
api_clients: Vec<CoconutApiClient>,
aggregate_vk: &VerificationKey,
threshold: u64,
epoch_id: EpochId,
signing_account: &AccountData,
expiration_date: OffsetDateTime,
) -> anyhow::Result<IssuedBandwidthCredential> {
let issuance_pass = IssuanceBandwidthCredential::new_freepass(Some(expiration_date));
let signing_data = issuance_pass.prepare_for_signing();
let credential_shares = Arc::new(tokio::sync::Mutex::new(Vec::new()));
futures::stream::iter(api_clients)
.for_each_concurrent(None, |client| async {
// move the client into the block
let client = client;
let api_url = client.api_client.api_url();
info!("contacting {api_url} for blinded free pass");
match issuance_pass
.obtain_partial_freepass_credential(
&client.api_client,
&signing_account,
&client.verification_key,
signing_data.clone(),
)
.await
{
Ok(partial_credential) => {
credential_shares
.lock()
.await
.push((partial_credential, client.node_id).into());
}
Err(err) => {
error!("failed to obtain partial free pass from {api_url}: {err}")
}
}
})
.await;
// SAFETY: the futures have completed, so we MUST have the only arc reference
#[allow(clippy::unwrap_used)]
let credential_shares = Arc::into_inner(credential_shares).unwrap().into_inner();
if credential_shares.len() < threshold as usize {
bail!("we managed to obtain only {} partial credentials while the minimum threshold is {threshold}", credential_shares.len());
}
let signature = issuance_pass.aggregate_signature_shares(aggregate_vk, &credential_shares)?;
Ok(issuance_pass.into_issued_credential(signature, epoch_id))
}
pub async fn execute(args: Args, client: SigningClient) -> anyhow::Result<()> {
let address = client.address();
if !args.output_dir.is_dir() {
bail!("the provided output directory is not a directory!");
}
if args.output_dir.read_dir()?.next().is_some() {
bail!("the provided output directory is not empty!");
}
let Some(bandwidth_contract) = client.coconut_bandwidth_contract_address() else {
bail!("the bandwidth contract address is not set")
};
let Some(bandwidth_admin) = client
.get_contract(bandwidth_contract)
.await
.map(|c| c.contract_info.admin)?
else {
bail!("the bandwidth contract doesn't have any admin set")
};
// sanity checks since nym-apis will reject invalid requests anyway
if address != bandwidth_admin {
bail!("the provided mnemonic does not correspond to the current admin of the bandwidth contract")
}
let expiration_date = match args.expiration_date {
Some(date) => date,
// SAFETY: one of those arguments must have been set
None => OffsetDateTime::from_unix_timestamp(args.expiration_timestamp.unwrap())?,
};
let now = OffsetDateTime::now_utc();
if expiration_date > now + MAX_FREE_PASS_VALIDITY {
bail!("the provided free pass request has too long expiry (expiry is set to on {expiration_date})")
}
// issuance start
block_until_coconut_is_available(&client).await?;
let signing_account = client.signing_account()?;
let epoch_id = client.get_current_epoch().await?.epoch_id;
let threshold = client
.get_current_epoch_threshold()
.await?
.ok_or(anyhow!("no threshold available"))?;
let api_clients = all_coconut_api_clients(&client, epoch_id).await?;
if api_clients.len() < threshold as usize {
bail!(
"we have only {} api clients available while the minimum threshold is {threshold}",
api_clients.len()
)
}
let aggregate_vk = obtain_aggregate_verification_key(&api_clients)?;
for i in 0..args.amount {
let human_index = i + 1;
info!("trying to obtain free pass {human_index}/{}", args.amount);
let free_pass = get_freepass(
api_clients.clone(),
&aggregate_vk,
threshold,
epoch_id,
&signing_account,
expiration_date,
)
.await?;
let credential_data = Zeroizing::new(free_pass.pack_v1());
let output = args.output_dir.join(format!("freepass_{i}.nym"));
info!("saving the freepass to '{}'", output.display());
File::create(output)?.write_all(&credential_data)?;
}
Ok(())
}
+2
View File
@@ -3,6 +3,7 @@
use clap::{Args, Subcommand};
pub mod generate_freepass;
pub mod issue_credentials;
pub mod recover_credentials;
@@ -15,6 +16,7 @@ pub struct Coconut {
#[derive(Debug, Subcommand)]
pub enum CoconutCommands {
GenerateFreepass(generate_freepass::Args),
IssueCredentials(issue_credentials::Args),
RecoverCredentials(recover_credentials::Args),
}
@@ -2,7 +2,12 @@
// SPDX-License-Identifier: Apache-2.0
use crate::coconut::utils::scalar_serde_helper;
use nym_credentials_interface::{hash_to_scalar, Attribute, PublicAttribute};
use crate::error::Error;
use nym_api_requests::coconut::FreePassRequest;
use nym_credentials_interface::{
hash_to_scalar, Attribute, BlindedSignature, CredentialSigningData, PublicAttribute,
};
use nym_validator_client::signing::AccountData;
use serde::{Deserialize, Serialize};
use time::{Duration, OffsetDateTime, Time};
use zeroize::{Zeroize, ZeroizeOnDrop};
@@ -76,4 +81,54 @@ impl FreePassIssuanceData {
pub fn expiry_date_plain(&self) -> String {
self.expiry_date.unix_timestamp().to_string()
}
pub async fn obtain_free_pass_nonce(
&self,
client: &nym_validator_client::client::NymApiClient,
) -> Result<u32, Error> {
let server_response = client.free_pass_nonce().await?;
Ok(server_response.current_nonce)
}
pub fn create_free_pass_request(
&self,
signing_request: &CredentialSigningData,
account_data: &AccountData,
issuer_nonce: u32,
) -> Result<FreePassRequest, Error> {
let plaintext = issuer_nonce.to_be_bytes();
let nonce_signature = account_data
.private_key()
.sign(&plaintext)
.map_err(|_| Error::Secp256k1SignFailure)?;
Ok(FreePassRequest {
cosmos_pubkey: account_data.public_key(),
inner_sign_request: signing_request.blind_sign_request.clone(),
used_nonce: issuer_nonce,
nonce_signature,
public_attributes_plain: signing_request.public_attributes_plain.clone(),
})
}
pub async fn obtain_blinded_credential(
&self,
client: &nym_validator_client::client::NymApiClient,
request: &FreePassRequest,
) -> Result<BlindedSignature, Error> {
let server_response = client.issue_free_pass_credential(request).await?;
Ok(server_response.blinded_signature)
}
pub async fn request_blinded_credential(
&self,
signing_request: &CredentialSigningData,
account_data: &AccountData,
client: &nym_validator_client::client::NymApiClient,
) -> Result<BlindedSignature, Error> {
let signing_nonce = self.obtain_free_pass_nonce(client).await?;
let request =
self.create_free_pass_request(signing_request, account_data, signing_nonce)?;
self.obtain_blinded_credential(client, &request).await
}
}
@@ -11,11 +11,13 @@ use crate::coconut::utils::scalar_serde_helper;
use crate::error::Error;
use bls12_381::G1Projective;
use nym_credentials_interface::{
aggregate_signature_shares, hash_to_scalar, prepare_blind_sign, Attribute, Parameters,
PrivateAttribute, PublicAttribute, Signature, SignatureShare, VerificationKey,
aggregate_signature_shares, hash_to_scalar, prepare_blind_sign, Attribute, BlindedSignature,
Parameters, PrivateAttribute, PublicAttribute, Signature, SignatureShare, VerificationKey,
};
use nym_crypto::asymmetric::{encryption, identity};
use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::{Coin, Hash};
use nym_validator_client::signing::AccountData;
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use zeroize::{Zeroize, ZeroizeOnDrop};
@@ -196,26 +198,12 @@ impl IssuanceBandwidthCredential {
}
}
pub async fn obtain_partial_credential(
pub fn unblind_signature(
&self,
client: &nym_validator_client::client::NymApiClient,
validator_vk: &VerificationKey,
signing_data: impl Into<Option<CredentialSigningData>>,
signing_data: &CredentialSigningData,
blinded_signature: BlindedSignature,
) -> Result<Signature, Error> {
// if we provided signing data, do use them, otherwise generate fresh data
let signing_data = signing_data
.into()
.unwrap_or_else(|| self.prepare_for_signing());
let blinded_signature = match &self.variant_data {
BandwidthCredentialIssuanceDataVariant::FreePass(_freepass) => unimplemented!(),
BandwidthCredentialIssuanceDataVariant::Voucher(voucher) => {
// TODO: the request can be re-used between different apis
let request = voucher.create_blind_sign_request_body(&signing_data);
voucher.obtain_blinded_credential(client, &request).await?
}
};
let public_attributes = self.get_public_attributes();
let private_attributes = self.get_private_attributes();
@@ -232,6 +220,52 @@ impl IssuanceBandwidthCredential {
Ok(unblinded_signature)
}
pub async fn obtain_partial_freepass_credential(
&self,
client: &nym_validator_client::client::NymApiClient,
account_data: &AccountData,
validator_vk: &VerificationKey,
signing_data: impl Into<Option<CredentialSigningData>>,
) -> Result<Signature, Error> {
// if we provided signing data, do use them, otherwise generate fresh data
let signing_data = signing_data
.into()
.unwrap_or_else(|| self.prepare_for_signing());
let blinded_signature = match &self.variant_data {
BandwidthCredentialIssuanceDataVariant::FreePass(freepass) => {
freepass
.request_blinded_credential(&signing_data, account_data, client)
.await?
}
_ => return Err(Error::NotAFreePass),
};
self.unblind_signature(validator_vk, &signing_data, blinded_signature)
}
// ideally this would have been generic over credential type, but we really don't need secp256k1 keys for bandwidth vouchers
pub async fn obtain_partial_bandwidth_voucher_credential(
&self,
client: &nym_validator_client::client::NymApiClient,
validator_vk: &VerificationKey,
signing_data: impl Into<Option<CredentialSigningData>>,
) -> Result<Signature, Error> {
// if we provided signing data, do use them, otherwise generate fresh data
let signing_data = signing_data
.into()
.unwrap_or_else(|| self.prepare_for_signing());
let blinded_signature = match &self.variant_data {
BandwidthCredentialIssuanceDataVariant::Voucher(voucher) => {
// TODO: the request can be re-used between different apis
let request = voucher.create_blind_sign_request_body(&signing_data);
voucher.obtain_blinded_credential(client, &request).await?
}
_ => return Err(Error::NotABandwdithVoucher),
};
self.unblind_signature(validator_vk, &signing_data, blinded_signature)
}
pub fn aggregate_signature_shares(
&self,
verification_key: &VerificationKey,
@@ -254,13 +288,15 @@ impl IssuanceBandwidthCredential {
pub fn into_issued_credential(
self,
aggregate_signature: Signature,
epoch_id: EpochId,
) -> IssuedBandwidthCredential {
self.to_issued_credential(aggregate_signature)
self.to_issued_credential(aggregate_signature, epoch_id)
}
pub fn to_issued_credential(
&self,
aggregate_signature: Signature,
epoch_id: EpochId,
) -> IssuedBandwidthCredential {
IssuedBandwidthCredential::new(
self.serial_number,
@@ -268,6 +304,7 @@ impl IssuanceBandwidthCredential {
aggregate_signature,
(&self.variant_data).into(),
self.type_prehashed,
epoch_id,
)
}
@@ -16,6 +16,7 @@ use nym_credentials_interface::{
};
use serde::{Deserialize, Serialize};
use zeroize::{Zeroize, ZeroizeOnDrop};
use nym_validator_client::nym_api::EpochId;
pub const CURRENT_SERIALIZATION_REVISION: u8 = 1;
@@ -91,6 +92,9 @@ pub struct IssuedBandwidthCredential {
/// type of the bandwdith credential hashed onto a scalar
#[serde(with = "scalar_serde_helper")]
type_prehashed: PublicAttribute,
/// Specifies the (DKG) epoch id when this credential has been issued
epoch_id: EpochId
}
impl IssuedBandwidthCredential {
@@ -100,6 +104,7 @@ impl IssuedBandwidthCredential {
signature: Signature,
variant_data: BandwidthCredentialIssuedDataVariant,
type_prehashed: PublicAttribute,
epoch_id: EpochId
) -> Self {
IssuedBandwidthCredential {
serial_number,
@@ -107,6 +112,7 @@ impl IssuedBandwidthCredential {
signature,
variant_data,
type_prehashed,
epoch_id,
}
}
+3 -3
View File
@@ -9,7 +9,7 @@ use nym_credentials_interface::{
};
use nym_validator_client::client::CoconutApiClient;
pub async fn obtain_aggregate_verification_key(
pub fn obtain_aggregate_verification_key(
api_clients: &[CoconutApiClient],
) -> Result<VerificationKey, Error> {
if api_clients.is_empty() {
@@ -37,7 +37,7 @@ pub async fn obtain_aggregate_signature(
return Err(Error::NoValidatorsAvailable);
}
let mut shares = Vec::with_capacity(coconut_api_clients.len());
let verification_key = obtain_aggregate_verification_key(coconut_api_clients).await?;
let verification_key = obtain_aggregate_verification_key(coconut_api_clients)?;
let request = voucher.prepare_for_signing();
@@ -48,7 +48,7 @@ pub async fn obtain_aggregate_signature(
);
match voucher
.obtain_partial_credential(
.obtain_partial_bandwidth_voucher_credential(
&coconut_api_client.api_client,
&coconut_api_client.verification_key,
Some(request.clone()),
+9
View File
@@ -44,4 +44,13 @@ pub enum Error {
#[error("Could not deserialize bandwidth voucher - {0}")]
BandwidthVoucherDeserializationError(String),
#[error("the provided issuance data wasn't prepared for a bandwidth voucher")]
NotABandwdithVoucher,
#[error("the provided issuance data wasn't prepared for a free pass")]
NotAFreePass,
#[error("failed to create a secp256k1 signature")]
Secp256k1SignFailure
}
+9
View File
@@ -248,6 +248,15 @@ pub struct SignatureShare {
index: SignerIndex,
}
impl From<(Signature, SignerIndex)> for SignatureShare {
fn from(value: (Signature, SignerIndex)) -> Self {
SignatureShare {
signature: value.0,
index: value.1,
}
}
}
impl SignatureShare {
pub fn new(signature: Signature, index: SignerIndex) -> Self {
SignatureShare { signature, index }
+1 -1
View File
@@ -327,7 +327,7 @@ mod tests {
)
.unwrap();
let issued = issuance.into_issued_credential(sig);
let issued = issuance.into_issued_credential(sig, 42);
let spending = issued
.prepare_for_spending(keypair.verification_key())
.unwrap();
@@ -89,7 +89,7 @@ impl CoconutVerifier {
});
}
let aggregated_verification_key =
nym_credentials::obtain_aggregate_verification_key(&epoch_api_clients).await?;
nym_credentials::obtain_aggregate_verification_key(&epoch_api_clients)?;
api_clients.insert(current_epoch.epoch_id, epoch_api_clients);
master_keys.insert(current_epoch.epoch_id, aggregated_verification_key);
@@ -155,7 +155,7 @@ impl CoconutVerifier {
let api_clients = self.api_clients(epoch_id).await?;
let aggregated_verification_key =
nym_credentials::obtain_aggregate_verification_key(&api_clients).await?;
nym_credentials::obtain_aggregate_verification_key(&api_clients)?;
let mut guard = self.master_keys.write().await;
guard.insert(epoch_id, aggregated_verification_key);
+1 -1
View File
@@ -5,6 +5,6 @@ pub mod helpers;
pub mod models;
pub use models::{
BlindSignRequestBody, BlindedSignatureResponse, CredentialsRequestBody,
BlindSignRequestBody, BlindedSignatureResponse, CredentialsRequestBody, FreePassRequest,
VerificationKeyResponse, VerifyCredentialBody, VerifyCredentialResponse,
};
@@ -110,6 +110,11 @@ impl BlindSignRequestBody {
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct FreePassNonceResponse {
pub current_nonce: u32,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct BlindedSignatureResponse {
pub blinded_signature: BlindedSignature,
@@ -159,6 +164,22 @@ pub struct FreePassRequest {
}
impl FreePassRequest {
pub fn new(
cosmos_pubkey: cosmrs::crypto::PublicKey,
inner_sign_request: BlindSignRequest,
used_nonce: u32,
nonce_signature: cosmrs::crypto::secp256k1::Signature,
public_attributes_plain: Vec<String>,
) -> Self {
FreePassRequest {
cosmos_pubkey,
inner_sign_request,
used_nonce,
nonce_signature,
public_attributes_plain,
}
}
pub fn tendermint_pubkey(&self) -> tendermint::PublicKey {
self.cosmos_pubkey.into()
}
+14 -2
View File
@@ -8,8 +8,8 @@ use crate::coconut::state::State;
use crate::coconut::storage::CoconutStorageExt;
use k256::ecdsa::signature::Verifier;
use nym_api_requests::coconut::models::{
CredentialsRequestBody, EpochCredentialsResponse, FreePassRequest, IssuedCredentialResponse,
IssuedCredentialsResponse,
CredentialsRequestBody, EpochCredentialsResponse, FreePassNonceResponse, FreePassRequest,
IssuedCredentialResponse, IssuedCredentialsResponse,
};
use nym_api_requests::coconut::{
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
@@ -68,6 +68,18 @@ fn validate_freepass_public_attributes(res: &FreePassRequest) -> Result<()> {
Ok(())
}
#[get("/free-pass-nonce")]
pub async fn get_current_free_pass_nonce(
state: &RocketState<State>,
) -> Result<Json<FreePassNonceResponse>> {
debug!("Received free pass nonce request");
let current_nonce = state.storage.get_current_freepass_nonce().await?;
debug!("the current expected nonce is {current_nonce}");
Ok(Json(FreePassNonceResponse { current_nonce }))
}
#[post("/free-pass", data = "<freepass_request_body>")]
pub async fn post_free_pass(
freepass_request_body: Json<FreePassRequest>,
+1 -1
View File
@@ -110,7 +110,7 @@ impl APICommunicationChannel for QueryCommunicationChannel {
ClientInner::Signing(client) => all_coconut_api_clients(client, epoch_id).await?,
};
let vk = obtain_aggregate_verification_key(&coconut_api_clients).await?;
let vk = obtain_aggregate_verification_key(&coconut_api_clients)?;
guard.insert(epoch_id, vk.clone());
+1
View File
@@ -52,6 +52,7 @@ where
// this format! is so ugly...
format!("/{NYM_API_VERSION}/{COCONUT_ROUTES}/{BANDWIDTH}"),
routes![
api_routes::get_current_free_pass_nonce,
api_routes::post_free_pass,
api_routes::post_blind_sign,
api_routes::verify_bandwidth_credential,
+7
View File
@@ -7,6 +7,13 @@ pub(crate) async fn execute(
network_details: &NymNetworkDetails,
) -> anyhow::Result<()> {
match coconut.command {
nym_cli_commands::coconut::CoconutCommands::GenerateFreepass(args) => {
nym_cli_commands::coconut::generate_freepass::execute(
args,
create_signing_client(global_args, network_details)?,
)
.await?
}
nym_cli_commands::coconut::CoconutCommands::IssueCredentials(args) => {
nym_cli_commands::coconut::issue_credentials::execute(
args,