propagated new ticket type through the whole stack
This commit is contained in:
Generated
+7
-1
@@ -4562,6 +4562,7 @@ dependencies = [
|
||||
"nym-config",
|
||||
"nym-country-group",
|
||||
"nym-credential-storage",
|
||||
"nym-credentials-interface",
|
||||
"nym-crypto",
|
||||
"nym-ecash-time",
|
||||
"nym-explorer-client",
|
||||
@@ -4808,10 +4809,10 @@ dependencies = [
|
||||
"log",
|
||||
"nym-bandwidth-controller",
|
||||
"nym-client-core",
|
||||
"nym-compact-ecash",
|
||||
"nym-config",
|
||||
"nym-credential-storage",
|
||||
"nym-credentials",
|
||||
"nym-credentials-interface",
|
||||
"nym-ecash-time",
|
||||
"nym-validator-client",
|
||||
"thiserror",
|
||||
@@ -4848,8 +4849,10 @@ dependencies = [
|
||||
"bls12_381",
|
||||
"nym-compact-ecash",
|
||||
"nym-ecash-time",
|
||||
"nym-network-defaults",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"strum 0.25.0",
|
||||
"thiserror",
|
||||
"time",
|
||||
]
|
||||
@@ -5660,6 +5663,7 @@ dependencies = [
|
||||
"nym-credential-storage",
|
||||
"nym-credential-utils",
|
||||
"nym-credentials",
|
||||
"nym-credentials-interface",
|
||||
"nym-crypto",
|
||||
"nym-gateway-requests",
|
||||
"nym-network-defaults",
|
||||
@@ -6129,12 +6133,14 @@ dependencies = [
|
||||
"nym-compact-ecash",
|
||||
"nym-config",
|
||||
"nym-credentials",
|
||||
"nym-credentials-interface",
|
||||
"nym-crypto",
|
||||
"nym-ecash-time",
|
||||
"nym-network-defaults",
|
||||
"nym-task",
|
||||
"nym-validator-client",
|
||||
"nyxd-scraper",
|
||||
"rand_chacha 0.3.1",
|
||||
"serde",
|
||||
"serde_with",
|
||||
"sha2 0.10.8",
|
||||
|
||||
@@ -38,7 +38,7 @@ zeroize = { workspace = true }
|
||||
## internal
|
||||
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
|
||||
nym-bin-common = { path = "../../common/bin-common", features = ["output_format", "clap"] }
|
||||
nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage", "fs-gateways-storage", "cli"] }
|
||||
nym-client-core = { path = "../../common/client-core", features = ["fs-credentials-storage", "fs-surb-storage", "fs-gateways-storage", "cli"] }
|
||||
nym-config = { path = "../../common/config" }
|
||||
nym-credential-storage = { path = "../../common/credential-storage" }
|
||||
nym-credentials = { path = "../../common/credentials" }
|
||||
|
||||
@@ -23,7 +23,7 @@ zeroize = { workspace = true }
|
||||
|
||||
# internal
|
||||
nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] }
|
||||
nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage", "fs-gateways-storage", "cli"] }
|
||||
nym-client-core = { path = "../../common/client-core", features = ["fs-credentials-storage", "fs-surb-storage", "fs-gateways-storage", "cli"] }
|
||||
nym-config = { path = "../../common/config" }
|
||||
nym-credential-storage = { path = "../../common/credential-storage" }
|
||||
nym-credentials = { path = "../../common/credentials" }
|
||||
|
||||
@@ -8,6 +8,7 @@ use nym_credential_storage::storage::Storage;
|
||||
use nym_credentials::ecash::bandwidth::IssuanceTicketBook;
|
||||
use nym_credentials::ecash::utils::obtain_aggregate_wallet;
|
||||
use nym_credentials::IssuedTicketBook;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_ecash_time::{ecash_default_expiration_date, Date};
|
||||
use nym_validator_client::coconut::all_ecash_api_clients;
|
||||
@@ -22,6 +23,7 @@ pub async fn make_deposit<C>(
|
||||
client: &C,
|
||||
client_id: &[u8],
|
||||
expiration: Option<Date>,
|
||||
ticketbook_type: TicketType,
|
||||
) -> Result<IssuanceTicketBook, BandwidthControllerError>
|
||||
where
|
||||
C: EcashSigningClient + EcashQueryClient + Sync,
|
||||
@@ -48,6 +50,7 @@ where
|
||||
deposit_id,
|
||||
client_id,
|
||||
signing_key,
|
||||
ticketbook_type,
|
||||
expiration,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ nym-pemstore = { path = "../pemstore" }
|
||||
nym-topology = { path = "../topology", features = ["serializable"] }
|
||||
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
|
||||
nym-task = { path = "../task" }
|
||||
nym-credentials-interface = { path = "../credentials-interface" }
|
||||
nym-credential-storage = { path = "../credential-storage" }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
nym-client-core-config-types = { path = "./config-types", features = ["disk-persistence"] }
|
||||
@@ -115,6 +116,7 @@ tempfile = { workspace = true }
|
||||
[features]
|
||||
default = []
|
||||
cli = ["clap", "comfy-table"]
|
||||
fs-credentials-storage = ["nym-credential-storage/persistent-storage"]
|
||||
fs-surb-storage = ["nym-client-core-surb-storage/fs-surb-storage"]
|
||||
fs-gateways-storage = ["nym-client-core-gateways-storage/fs-gateways-storage"]
|
||||
wasm = ["nym-gateway-client/wasm"]
|
||||
|
||||
@@ -5,14 +5,15 @@ use crate::cli_helpers::{CliClient, CliClientConfig};
|
||||
use crate::error::ClientCoreError;
|
||||
use nym_credential_storage::models::BasicTicketbookInformation;
|
||||
use nym_credential_storage::storage::Storage;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_ecash_time::ecash_today;
|
||||
use nym_network_defaults::TicketbookType::MixnetEntry;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::Date;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct AvailableTicketbook {
|
||||
pub id: i64,
|
||||
pub typ: TicketType,
|
||||
pub expiration: Date,
|
||||
pub issued_tickets: u32,
|
||||
pub claimed_tickets: u32,
|
||||
@@ -45,6 +46,7 @@ impl AvailableTicketbook {
|
||||
|
||||
vec![
|
||||
comfy_table::Cell::new(self.id.to_string()),
|
||||
comfy_table::Cell::new(self.typ),
|
||||
expiration,
|
||||
comfy_table::Cell::new(format!("{issued} ({si_issued})")),
|
||||
comfy_table::Cell::new(format!("{claimed} ({si_claimed})")),
|
||||
@@ -55,17 +57,22 @@ impl AvailableTicketbook {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BasicTicketbookInformation> for AvailableTicketbook {
|
||||
fn from(value: BasicTicketbookInformation) -> Self {
|
||||
AvailableTicketbook {
|
||||
impl TryFrom<BasicTicketbookInformation> for AvailableTicketbook {
|
||||
type Error = ClientCoreError;
|
||||
|
||||
fn try_from(value: BasicTicketbookInformation) -> Result<Self, Self::Error> {
|
||||
let typ = value
|
||||
.ticketbook_type
|
||||
.parse()
|
||||
.map_err(|_| ClientCoreError::UnknownTicketType)?;
|
||||
Ok(AvailableTicketbook {
|
||||
id: value.id,
|
||||
typ,
|
||||
expiration: value.expiration_date,
|
||||
issued_tickets: value.total_tickets,
|
||||
claimed_tickets: value.used_tickets,
|
||||
|
||||
// TODO: this will change when 'type' field is introduced; for now doesn't matter what we put there
|
||||
ticket_size: MixnetEntry.bandwidth_value(),
|
||||
}
|
||||
ticket_size: typ.to_repr().bandwidth_value(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +86,7 @@ impl std::fmt::Display for AvailableTicketbooks {
|
||||
let mut table = comfy_table::Table::new();
|
||||
table.set_header(vec![
|
||||
"id",
|
||||
"type",
|
||||
"expiration",
|
||||
"issued tickets (bandwidth)",
|
||||
"claimed tickets (bandwidth)",
|
||||
@@ -124,6 +132,9 @@ where
|
||||
})?;
|
||||
|
||||
Ok(AvailableTicketbooks(
|
||||
ticketbooks.into_iter().map(Into::into).collect(),
|
||||
ticketbooks
|
||||
.into_iter()
|
||||
.map(TryInto::<AvailableTicketbook>::try_into)
|
||||
.collect::<Result<_, _>>()?,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ use crate::{
|
||||
config::{self, disk_persistence::CommonClientPaths},
|
||||
error::ClientCoreError,
|
||||
};
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-credentials-storage"))]
|
||||
use nym_credential_storage::persistent_storage::PersistentStorage as PersistentCredentialStorage;
|
||||
|
||||
pub use nym_client_core_gateways_storage as gateways_storage;
|
||||
|
||||
@@ -68,6 +68,9 @@ pub enum ClientCoreError {
|
||||
source: Box<dyn Error + Send + Sync>,
|
||||
},
|
||||
|
||||
#[error("the provided ticket type is invalid")]
|
||||
UnknownTicketType,
|
||||
|
||||
#[error("the gateway id is invalid - {0}")]
|
||||
UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError),
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@ use std::future::Future;
|
||||
|
||||
#[cfg(all(
|
||||
not(target_arch = "wasm32"),
|
||||
feature = "cli",
|
||||
feature = "fs-surb-storage",
|
||||
feature = "fs-credentials-storage",
|
||||
feature = "fs-gateways-storage"
|
||||
))]
|
||||
pub mod cli_helpers;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::GatewayClientError;
|
||||
use nym_network_defaults::TicketbookType::MixnetEntry;
|
||||
use nym_network_defaults::TicketTypeRepr::V1MixnetEntry;
|
||||
use si_scale::helpers::bibytes2;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -103,7 +103,7 @@ impl BandwidthTickets {
|
||||
|
||||
// 20% of entry ticket value
|
||||
pub const DEFAULT_REMAINING_BANDWIDTH_THRESHOLD: i64 =
|
||||
(MixnetEntry.bandwidth_value() / 5) as i64;
|
||||
(V1MixnetEntry.bandwidth_value() / 5) as i64;
|
||||
|
||||
pub const DEFAULT_CUTOFF_REMAINING_BANDWIDTH_THRESHOLD: Option<i64> = None;
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ use nym_api_requests::ecash::VerificationKeyResponse;
|
||||
pub use nym_api_requests::{
|
||||
ecash::{
|
||||
models::{
|
||||
EpochCredentialsResponse, IssuedCredential, IssuedCredentialBody,
|
||||
IssuedCredentialResponse, IssuedCredentialsResponse, SpentCredentialsResponse,
|
||||
EpochCredentialsResponse, IssuedCredentialResponse, IssuedCredentialsResponse,
|
||||
IssuedTicketbook, IssuedTicketbookBody, SpentCredentialsResponse,
|
||||
},
|
||||
BlindSignRequestBody, BlindedSignatureResponse, CredentialsRequestBody,
|
||||
PartialCoinIndicesSignatureResponse, PartialExpirationDateSignatureResponse,
|
||||
|
||||
@@ -7,11 +7,16 @@ use anyhow::bail;
|
||||
use clap::Parser;
|
||||
use nym_credential_storage::initialise_persistent_storage;
|
||||
use nym_credential_utils::utils;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
/// Specify which type of ticketbook should be issued
|
||||
#[clap(long, default_value_t = TicketType::default())]
|
||||
pub(crate) ticketbook_type: TicketType,
|
||||
|
||||
/// Config file of the client that is supposed to use the credential.
|
||||
#[clap(long)]
|
||||
pub(crate) client_config: PathBuf,
|
||||
@@ -39,7 +44,13 @@ pub async fn execute(args: Args, client: SigningClient) -> anyhow::Result<()> {
|
||||
|
||||
let persistent_storage = initialise_persistent_storage(credentials_store).await;
|
||||
let private_id_key: identity::PrivateKey = nym_pemstore::load_key(private_id_key)?;
|
||||
utils::issue_credential(&client, &persistent_storage, &private_id_key.to_bytes()).await?;
|
||||
utils::issue_credential(
|
||||
&client,
|
||||
&persistent_storage,
|
||||
&private_id_key.to_bytes(),
|
||||
args.ticketbook_type,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ CREATE TABLE ecash_ticketbook
|
||||
-- introduce a way for us to introduce breaking changes in serialization of data
|
||||
serialization_revision INTEGER NOT NULL,
|
||||
|
||||
-- the type of the associated ticketbook
|
||||
ticketbook_type TEXT NOT NULL,
|
||||
|
||||
-- the actual crypto data of the ticketbook (wallet, keys, etc.)
|
||||
ticketbook_data BLOB NOT NULL UNIQUE,
|
||||
|
||||
|
||||
@@ -175,6 +175,7 @@ impl MemoryEcachTicketbookManager {
|
||||
.map(|t| BasicTicketbookInformation {
|
||||
id: t.ticketbook_id,
|
||||
expiration_date: t.ticketbook.expiration_date(),
|
||||
ticketbook_type: t.ticketbook.ticketbook_type().to_string(),
|
||||
epoch_id: t.ticketbook.epoch_id() as u32,
|
||||
total_tickets: t.ticketbook.spent_tickets() as u32,
|
||||
used_tickets: t.ticketbook.params_total_tickets() as u32,
|
||||
|
||||
@@ -61,11 +61,13 @@ impl SqliteEcashTicketbookManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) async fn insert_new_ticketbook(
|
||||
&self,
|
||||
serialisation_revision: u8,
|
||||
data: &[u8],
|
||||
expiration_date: Date,
|
||||
typ: &str,
|
||||
epoch_id: u32,
|
||||
total_tickets: u32,
|
||||
used_tickets: u32,
|
||||
@@ -73,12 +75,13 @@ impl SqliteEcashTicketbookManager {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO ecash_ticketbook
|
||||
(serialization_revision, ticketbook_data, expiration_date, epoch_id, total_tickets, used_tickets)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
(serialization_revision, ticketbook_data, expiration_date, ticketbook_type, epoch_id, total_tickets, used_tickets)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
"#,
|
||||
serialisation_revision,
|
||||
data,
|
||||
expiration_date,
|
||||
typ,
|
||||
epoch_id,
|
||||
total_tickets,
|
||||
used_tickets,
|
||||
@@ -92,7 +95,7 @@ impl SqliteEcashTicketbookManager {
|
||||
) -> Result<Vec<BasicTicketbookInformation>, sqlx::Error> {
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
SELECT id, expiration_date, epoch_id, total_tickets, used_tickets
|
||||
SELECT id, expiration_date, ticketbook_type, epoch_id, total_tickets, used_tickets
|
||||
FROM ecash_ticketbook
|
||||
"#,
|
||||
)
|
||||
|
||||
@@ -19,6 +19,7 @@ pub struct RetrievedPendingTicketbook {
|
||||
pub struct BasicTicketbookInformation {
|
||||
pub id: i64,
|
||||
pub expiration_date: Date,
|
||||
pub ticketbook_type: String,
|
||||
pub epoch_id: u32,
|
||||
pub total_tickets: u32,
|
||||
pub used_tickets: u32,
|
||||
@@ -31,6 +32,8 @@ pub struct StoredIssuedTicketbook {
|
||||
|
||||
pub serialization_revision: u8,
|
||||
|
||||
pub ticketbook_type: String,
|
||||
|
||||
pub ticketbook_data: Vec<u8>,
|
||||
|
||||
#[zeroize(skip)]
|
||||
|
||||
@@ -114,6 +114,7 @@ impl Storage for PersistentStorage {
|
||||
serialisation_revision,
|
||||
&data,
|
||||
ticketbook.expiration_date(),
|
||||
&ticketbook.ticketbook_type().to_string(),
|
||||
ticketbook.epoch_id() as u32,
|
||||
ticketbook.params_total_tickets() as u32,
|
||||
ticketbook.spent_tickets() as u32,
|
||||
|
||||
@@ -14,9 +14,9 @@ time.workspace = true
|
||||
|
||||
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
|
||||
nym-credentials = { path = "../../common/credentials" }
|
||||
nym-credentials-interface = { path = "../../common/credentials-interface" }
|
||||
nym-credential-storage = { path = "../../common/credential-storage", features = ["persistent-storage"] }
|
||||
nym-validator-client = { path = "../../common/client-libs/validator-client" }
|
||||
nym-config = { path = "../../common/config" }
|
||||
nym-client-core = { path = "../../common/client-core" }
|
||||
nym-compact-ecash = { path = "../../common/nym_offline_compact_ecash" }
|
||||
nym-ecash-time = { path = "../../common/ecash-time" }
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::errors::{Error, Result};
|
||||
use log::*;
|
||||
use nym_bandwidth_controller::acquire::{
|
||||
@@ -7,6 +10,7 @@ use nym_client_core::config::disk_persistence::CommonClientPaths;
|
||||
use nym_config::DEFAULT_DATA_DIR;
|
||||
use nym_credential_storage::persistent_storage::PersistentStorage;
|
||||
use nym_credential_storage::storage::Storage;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_ecash_time::ecash_default_expiration_date;
|
||||
use nym_validator_client::coconut::all_ecash_api_clients;
|
||||
use nym_validator_client::nyxd::contract_traits::{
|
||||
@@ -16,7 +20,12 @@ use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
pub async fn issue_credential<C, S>(client: &C, storage: &S, client_id: &[u8]) -> Result<()>
|
||||
pub async fn issue_credential<C, S>(
|
||||
client: &C,
|
||||
storage: &S,
|
||||
client_id: &[u8],
|
||||
typ: TicketType,
|
||||
) -> Result<()>
|
||||
where
|
||||
C: DkgQueryClient + EcashSigningClient + EcashQueryClient + Send + Sync,
|
||||
S: Storage,
|
||||
@@ -49,6 +58,7 @@ where
|
||||
client,
|
||||
client_id,
|
||||
Some(ticketbook_expiration),
|
||||
typ,
|
||||
)
|
||||
.await?;
|
||||
info!("Deposit done");
|
||||
@@ -65,7 +75,7 @@ where
|
||||
}).map_err(Error::storage_error)?
|
||||
}
|
||||
|
||||
info!("Succeeded adding a ticketbook");
|
||||
info!("Succeeded adding a ticketbook of type '{typ}'");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -14,8 +14,10 @@ license.workspace = true
|
||||
bls12_381 = { workspace = true, default-features = false }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
strum = { workspace = true, features = ["derive"] }
|
||||
time = { workspace = true, features = ["serde"] }
|
||||
rand = { workspace = true }
|
||||
|
||||
nym-compact-ecash = { path = "../nym_offline_compact_ecash" }
|
||||
nym-ecash-time = { path = "../ecash-time" }
|
||||
nym-ecash-time = { path = "../ecash-time" }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_network_defaults::TicketTypeRepr;
|
||||
use rand::Rng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use time::{Date, OffsetDateTime};
|
||||
|
||||
pub use nym_compact_ecash::{
|
||||
@@ -15,7 +17,6 @@ pub use nym_compact_ecash::{
|
||||
PartialCoinIndexSignature,
|
||||
},
|
||||
scheme::expiration_date_signatures::aggregate_expiration_signatures,
|
||||
scheme::expiration_date_signatures::date_scalar,
|
||||
scheme::expiration_date_signatures::{
|
||||
AnnotatedExpirationDateSignature, ExpirationDateSignature, ExpirationDateSignatureShare,
|
||||
PartialExpirationDateSignature,
|
||||
@@ -24,8 +25,8 @@ pub use nym_compact_ecash::{
|
||||
scheme::withdrawal::RequestInfo,
|
||||
scheme::Payment,
|
||||
scheme::{Wallet, WalletSignatures},
|
||||
withdrawal_request, Base58, BlindedSignature, Bytable, PartialWallet, PayInfo, PublicKeyUser,
|
||||
SecretKeyUser, VerificationKeyAuth, WithdrawalRequest,
|
||||
withdrawal_request, Base58, BlindedSignature, Bytable, EncodedDate, EncodedTicketType,
|
||||
PartialWallet, PayInfo, PublicKeyUser, SecretKeyUser, VerificationKeyAuth, WithdrawalRequest,
|
||||
};
|
||||
use nym_ecash_time::EcashTime;
|
||||
|
||||
@@ -38,6 +39,8 @@ pub struct CredentialSigningData {
|
||||
pub ecash_pub_key: PublicKeyUser,
|
||||
|
||||
pub expiration_date: Date,
|
||||
|
||||
pub ticketbook_type: TicketType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
@@ -58,7 +61,7 @@ impl CredentialSpendingData {
|
||||
self.payment.spend_verify(
|
||||
verification_key,
|
||||
&self.pay_info,
|
||||
date_scalar(self.spend_date.ecash_unix_timestamp()),
|
||||
self.spend_date.ecash_unix_timestamp(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -216,3 +219,76 @@ impl From<PayInfo> for NymPayInfo {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Default,
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
strum::Display,
|
||||
strum::EnumString,
|
||||
)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[strum(serialize_all = "kebab-case")]
|
||||
pub enum TicketType {
|
||||
#[default]
|
||||
V1MixnetEntry,
|
||||
V1MixnetExit,
|
||||
V1WireguardEntry,
|
||||
V1WireguardExit,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Error)]
|
||||
#[error("provided unknown ticketbook type")]
|
||||
pub struct UnknownTicketType;
|
||||
|
||||
impl TicketType {
|
||||
pub fn to_repr(&self) -> TicketTypeRepr {
|
||||
(*self).into()
|
||||
}
|
||||
|
||||
pub fn encode(&self) -> EncodedTicketType {
|
||||
self.to_repr() as EncodedTicketType
|
||||
}
|
||||
|
||||
pub fn try_from_encoded(val: EncodedTicketType) -> Result<Self, UnknownTicketType> {
|
||||
match val {
|
||||
n if n == TicketTypeRepr::V1MixnetEntry as u8 => {
|
||||
Ok(TicketTypeRepr::V1MixnetEntry.into())
|
||||
}
|
||||
n if n == TicketTypeRepr::V1MixnetExit as u8 => Ok(TicketTypeRepr::V1MixnetExit.into()),
|
||||
n if n == TicketTypeRepr::V1WireguardEntry as u8 => {
|
||||
Ok(TicketTypeRepr::V1WireguardEntry.into())
|
||||
}
|
||||
n if n == TicketTypeRepr::V1WireguardExit as u8 => {
|
||||
Ok(TicketTypeRepr::V1WireguardExit.into())
|
||||
}
|
||||
_ => Err(UnknownTicketType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TicketType> for TicketTypeRepr {
|
||||
fn from(value: TicketType) -> Self {
|
||||
match value {
|
||||
TicketType::V1MixnetEntry => TicketTypeRepr::V1MixnetEntry,
|
||||
TicketType::V1MixnetExit => TicketTypeRepr::V1MixnetExit,
|
||||
TicketType::V1WireguardEntry => TicketTypeRepr::V1WireguardEntry,
|
||||
TicketType::V1WireguardExit => TicketTypeRepr::V1WireguardExit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TicketTypeRepr> for TicketType {
|
||||
fn from(value: TicketTypeRepr) -> Self {
|
||||
match value {
|
||||
TicketTypeRepr::V1MixnetEntry => TicketType::V1MixnetEntry,
|
||||
TicketTypeRepr::V1MixnetExit => TicketType::V1MixnetExit,
|
||||
TicketTypeRepr::V1WireguardEntry => TicketType::V1WireguardEntry,
|
||||
TicketTypeRepr::V1WireguardExit => TicketType::V1WireguardExit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,15 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::ecash::bandwidth::issued::IssuedTicketBook;
|
||||
use crate::ecash::bandwidth::serialiser::VersionedSerialise;
|
||||
use crate::ecash::bandwidth::CredentialSigningData;
|
||||
use crate::ecash::utils::cred_exp_date;
|
||||
use crate::error::Error;
|
||||
use nym_api_requests::ecash::BlindSignRequestBody;
|
||||
use nym_credentials_interface::{
|
||||
aggregate_wallets, generate_keypair_user_from_seed, issue_verify, withdrawal_request,
|
||||
BlindedSignature, KeyPairUser, PartialWallet, VerificationKeyAuth, WalletSignatures,
|
||||
WithdrawalRequest,
|
||||
BlindedSignature, KeyPairUser, PartialWallet, TicketType, VerificationKeyAuth,
|
||||
WalletSignatures, WithdrawalRequest,
|
||||
};
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_ecash_contract_common::deposit::DepositId;
|
||||
@@ -18,7 +19,6 @@ use nym_validator_client::nym_api::EpochId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::Date;
|
||||
|
||||
use crate::ecash::bandwidth::serialiser::VersionedSerialise;
|
||||
pub use nym_validator_client::nyxd::{Coin, Hash};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@@ -32,6 +32,9 @@ pub struct IssuanceTicketBook {
|
||||
/// ecash keypair related to the credential
|
||||
ecash_keypair: KeyPairUser,
|
||||
|
||||
/// the type of the ticketbook to be issued
|
||||
ticketbook_type: TicketType,
|
||||
|
||||
/// expiration_date of that credential
|
||||
expiration_date: Date,
|
||||
}
|
||||
@@ -41,12 +44,14 @@ impl IssuanceTicketBook {
|
||||
deposit_id: DepositId,
|
||||
identifier: M,
|
||||
signing_key: identity::PrivateKey,
|
||||
ticketbook_type: TicketType,
|
||||
) -> Self {
|
||||
//this expiration date will get fed to the ecash library, force midnight to be set
|
||||
Self::new_with_expiration(
|
||||
deposit_id,
|
||||
identifier,
|
||||
signing_key,
|
||||
ticketbook_type,
|
||||
ecash_default_expiration_date(),
|
||||
)
|
||||
}
|
||||
@@ -55,6 +60,7 @@ impl IssuanceTicketBook {
|
||||
deposit_id: DepositId,
|
||||
identifier: M,
|
||||
signing_key: identity::PrivateKey,
|
||||
ticketbook_type: TicketType,
|
||||
expiration_date: Date,
|
||||
) -> Self {
|
||||
let ecash_keypair = generate_keypair_user_from_seed(identifier);
|
||||
@@ -62,6 +68,7 @@ impl IssuanceTicketBook {
|
||||
deposit_id,
|
||||
signing_key,
|
||||
ecash_keypair,
|
||||
ticketbook_type,
|
||||
expiration_date,
|
||||
}
|
||||
}
|
||||
@@ -76,6 +83,10 @@ impl IssuanceTicketBook {
|
||||
self.expiration_date
|
||||
}
|
||||
|
||||
pub fn ticketbook_type(&self) -> TicketType {
|
||||
self.ticketbook_type
|
||||
}
|
||||
|
||||
pub fn request_plaintext(request: &WithdrawalRequest, deposit_id: DepositId) -> Vec<u8> {
|
||||
let mut message = request.to_bytes();
|
||||
message.extend_from_slice(&deposit_id.to_be_bytes());
|
||||
@@ -99,6 +110,7 @@ impl IssuanceTicketBook {
|
||||
request_signature,
|
||||
signing_request.ecash_pub_key.clone(),
|
||||
signing_request.expiration_date,
|
||||
signing_request.ticketbook_type,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -133,6 +145,7 @@ impl IssuanceTicketBook {
|
||||
let (withdrawal_request, request_info) = withdrawal_request(
|
||||
self.ecash_keypair.secret_key(),
|
||||
self.expiration_date.ecash_unix_timestamp(),
|
||||
self.ticketbook_type.encode(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -141,6 +154,7 @@ impl IssuanceTicketBook {
|
||||
request_info,
|
||||
ecash_pub_key: self.ecash_keypair.public_key(),
|
||||
expiration_date: self.expiration_date,
|
||||
ticketbook_type: self.ticketbook_type,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,6 +232,7 @@ impl IssuanceTicketBook {
|
||||
wallet,
|
||||
epoch_id,
|
||||
self.ecash_keypair.secret_key().clone(),
|
||||
self.ticketbook_type,
|
||||
self.expiration_date,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ use crate::ecash::bandwidth::CredentialSpendingData;
|
||||
use crate::ecash::utils::ecash_today;
|
||||
use crate::error::Error;
|
||||
use nym_credentials_interface::{
|
||||
CoinIndexSignature, ExpirationDateSignature, PayInfo, SecretKeyUser, VerificationKeyAuth,
|
||||
Wallet, WalletSignatures,
|
||||
CoinIndexSignature, ExpirationDateSignature, PayInfo, SecretKeyUser, TicketType,
|
||||
VerificationKeyAuth, Wallet, WalletSignatures,
|
||||
};
|
||||
use nym_ecash_time::EcashTime;
|
||||
use nym_validator_client::nym_api::EpochId;
|
||||
@@ -36,6 +36,10 @@ pub struct IssuedTicketBook {
|
||||
/// expiration_date for easier discarding
|
||||
#[zeroize(skip)]
|
||||
expiration_date: Date,
|
||||
|
||||
/// the type of the ticketbook to got issued
|
||||
#[zeroize(skip)]
|
||||
ticketbook_type: TicketType,
|
||||
}
|
||||
|
||||
impl IssuedTicketBook {
|
||||
@@ -43,6 +47,7 @@ impl IssuedTicketBook {
|
||||
wallet: WalletSignatures,
|
||||
epoch_id: EpochId,
|
||||
ecash_secret_key: SecretKeyUser,
|
||||
ticketbook_type: TicketType,
|
||||
expiration_date: Date,
|
||||
) -> Self {
|
||||
IssuedTicketBook {
|
||||
@@ -51,6 +56,7 @@ impl IssuedTicketBook {
|
||||
epoch_id,
|
||||
ecash_secret_key,
|
||||
expiration_date,
|
||||
ticketbook_type,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +64,7 @@ impl IssuedTicketBook {
|
||||
signatures_wallet: WalletSignatures,
|
||||
epoch_id: EpochId,
|
||||
ecash_secret_key: SecretKeyUser,
|
||||
ticketbook_type: TicketType,
|
||||
expiration_date: Date,
|
||||
spent_tickets: u64,
|
||||
) -> Self {
|
||||
@@ -67,6 +74,7 @@ impl IssuedTicketBook {
|
||||
epoch_id,
|
||||
ecash_secret_key,
|
||||
expiration_date,
|
||||
ticketbook_type,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +86,10 @@ impl IssuedTicketBook {
|
||||
self.epoch_id
|
||||
}
|
||||
|
||||
pub fn ticketbook_type(&self) -> TicketType {
|
||||
self.ticketbook_type
|
||||
}
|
||||
|
||||
pub fn current_serialization_revision(&self) -> u8 {
|
||||
CURRENT_SERIALIZATION_REVISION
|
||||
}
|
||||
|
||||
@@ -36,22 +36,6 @@ pub fn aggregate_verification_keys(
|
||||
)?)
|
||||
}
|
||||
|
||||
pub fn obtain_aggregated_verification_key(
|
||||
_api_clients: &[EcashApiClient],
|
||||
) -> Result<VerificationKeyAuth, Error> {
|
||||
// TODO:
|
||||
// let total = api_clients.len();
|
||||
// let mut rng = thread_rng();
|
||||
// let indices = sample(&mut rng, total, total);
|
||||
// for index in indices {
|
||||
// // randomly try apis until we succeed
|
||||
// // if let Ok(res) = api_clients[index].api_client.get_aggregated_verification_key().await {
|
||||
// // //
|
||||
// // }
|
||||
// }
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn obtain_expiration_date_signatures(
|
||||
ecash_api_clients: &[EcashApiClient],
|
||||
verification_key: &VerificationKeyAuth,
|
||||
@@ -63,7 +47,7 @@ pub async fn obtain_expiration_date_signatures(
|
||||
|
||||
let mut signatures_shares: Vec<_> = Vec::with_capacity(ecash_api_clients.len());
|
||||
|
||||
let expiration_date = cred_exp_date().unix_timestamp() as u64;
|
||||
let expiration_date = cred_exp_date().ecash_unix_timestamp();
|
||||
for ecash_api_client in ecash_api_clients.iter() {
|
||||
match ecash_api_client
|
||||
.api_client
|
||||
|
||||
@@ -7,17 +7,19 @@ pub const TICKETBOOK_VALIDITY_DAYS: u32 = 7;
|
||||
/// Specifies the number of tickets in each issued ticketbook.
|
||||
pub const TICKETBOOK_SIZE: u64 = 50;
|
||||
|
||||
/// This type is defined mostly for the purposes of having constants (like sizes) associated with given variants
|
||||
/// It's not meant to be serialised or have any fancy traits defined on it (in this crate)
|
||||
#[derive(Default, Copy, Clone, Debug, PartialEq)]
|
||||
#[repr(u8)]
|
||||
pub enum TicketbookType {
|
||||
pub enum TicketTypeRepr {
|
||||
#[default]
|
||||
MixnetEntry = 0,
|
||||
MixnetExit = 1,
|
||||
WireguardEntry = 2,
|
||||
WireguardExit = 3,
|
||||
V1MixnetEntry = 0,
|
||||
V1MixnetExit = 1,
|
||||
V1WireguardEntry = 2,
|
||||
V1WireguardExit = 3,
|
||||
}
|
||||
|
||||
impl TicketbookType {
|
||||
impl TicketTypeRepr {
|
||||
pub const WIREGUARD_ENTRY_TICKET_SIZE: u64 = 500 * 1024 * 1024; // 500 MB
|
||||
|
||||
// TBD:
|
||||
@@ -28,10 +30,10 @@ impl TicketbookType {
|
||||
/// How much bandwidth (in bytes) one ticket can grant
|
||||
pub const fn bandwidth_value(&self) -> u64 {
|
||||
match self {
|
||||
TicketbookType::MixnetEntry => Self::MIXNET_ENTRY_TICKET_SIZE,
|
||||
TicketbookType::MixnetExit => Self::MIXNET_EXIT_TICKET_SIZE,
|
||||
TicketbookType::WireguardEntry => Self::WIREGUARD_ENTRY_TICKET_SIZE,
|
||||
TicketbookType::WireguardExit => Self::WIREGUARD_EXIT_TICKET_SIZE,
|
||||
TicketTypeRepr::V1MixnetEntry => Self::MIXNET_ENTRY_TICKET_SIZE,
|
||||
TicketTypeRepr::V1MixnetExit => Self::MIXNET_EXIT_TICKET_SIZE,
|
||||
TicketTypeRepr::V1WireguardEntry => Self::WIREGUARD_ENTRY_TICKET_SIZE,
|
||||
TicketTypeRepr::V1WireguardExit => Self::WIREGUARD_EXIT_TICKET_SIZE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ pub(crate) fn recover_g1_tuple<T: Any>(
|
||||
Ok((first, second))
|
||||
}
|
||||
|
||||
pub(crate) fn date_scalar(date: EncodedDate) -> Scalar {
|
||||
pub fn date_scalar(date: EncodedDate) -> Scalar {
|
||||
Scalar::from(date as u64)
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ pub(crate) fn scalar_date(scalar: &Scalar) -> EncodedDate {
|
||||
u64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]) as EncodedDate
|
||||
}
|
||||
|
||||
pub(crate) fn type_scalar(t_type: EncodedTicketType) -> Scalar {
|
||||
pub fn type_scalar(t_type: EncodedTicketType) -> Scalar {
|
||||
Scalar::from(t_type as u64)
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ pub use crate::error::CompactEcashError;
|
||||
pub use crate::traits::Bytable;
|
||||
pub use bls12_381::G1Projective;
|
||||
pub use common_types::{BlindedSignature, Signature};
|
||||
pub use helpers::{date_scalar, type_scalar};
|
||||
pub use scheme::aggregation::aggregate_verification_keys;
|
||||
pub use scheme::aggregation::aggregate_wallets;
|
||||
pub use scheme::identify;
|
||||
|
||||
@@ -22,7 +22,7 @@ tokio = { workspace = true, features = ["rt-multi-thread", "net", "signal"] }
|
||||
url = { workspace = true }
|
||||
|
||||
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
|
||||
nym-client-core = { path = "../client-core", features = ["fs-surb-storage", "fs-gateways-storage"] }
|
||||
nym-client-core = { path = "../client-core", features = ["fs-credentials-storage", "fs-surb-storage", "fs-gateways-storage"] }
|
||||
nym-config = { path = "../config" }
|
||||
nym-contracts-common = { path = "../cosmwasm-smart-contracts/contracts-common" }
|
||||
nym-credential-storage = { path = "../credential-storage" }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use nym_network_defaults::TicketbookType;
|
||||
use nym_network_defaults::TicketTypeRepr;
|
||||
use std::num::ParseIntError;
|
||||
use thiserror::Error;
|
||||
use time::error::ComponentRange;
|
||||
@@ -42,7 +42,7 @@ impl Bandwidth {
|
||||
Bandwidth { value }
|
||||
}
|
||||
|
||||
pub fn ticket_amount(typ: TicketbookType) -> Self {
|
||||
pub fn ticket_amount(typ: TicketTypeRepr) -> Self {
|
||||
Bandwidth {
|
||||
value: typ.bandwidth_value(),
|
||||
}
|
||||
|
||||
@@ -64,17 +64,17 @@ CREATE TABLE partial_expiration_date_signatures (
|
||||
|
||||
DROP TABLE issued_credential;
|
||||
|
||||
-- particular **partial** ecash credential issued in this epoch
|
||||
CREATE TABLE issued_credential
|
||||
-- particular **partial** ecash ticketbook issued in this epoch
|
||||
CREATE TABLE issued_ticketbook
|
||||
(
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
epoch_id INTEGER NOT NULL,
|
||||
deposit_id INTEGER NOT NULL UNIQUE,
|
||||
-- at some point those should be blobified
|
||||
bs58_partial_credential VARCHAR NOT NULL,
|
||||
bs58_signature VARCHAR NOT NULL,
|
||||
joined_private_commitments VARCHAR NOT NULL,
|
||||
expiration_date DATE NOT NULL
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
epoch_id INTEGER NOT NULL,
|
||||
deposit_id INTEGER NOT NULL UNIQUE,
|
||||
partial_credential BLOB NOT NULL,
|
||||
signature BLOB NOT NULL,
|
||||
joined_private_commitments BLOB NOT NULL,
|
||||
expiration_date DATE NOT NULL,
|
||||
ticketbook_type_repr INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE ticket_providers
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_compact_ecash::BlindedSignature;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_ecash_time::EcashTime;
|
||||
use std::iter::once;
|
||||
use time::Date;
|
||||
|
||||
// recomputes plaintext on the credential nym-api has used for signing
|
||||
@@ -12,8 +14,9 @@ pub fn issued_credential_plaintext(
|
||||
epoch_id: u32,
|
||||
deposit_id: u32,
|
||||
blinded_partial_credential: &BlindedSignature,
|
||||
bs58_encoded_private_attributes_commitments: &[String],
|
||||
encoded_private_attributes_commitments: &[Vec<u8>],
|
||||
expiration_date: Date,
|
||||
ticketbook_type: TicketType,
|
||||
) -> Vec<u8> {
|
||||
epoch_id
|
||||
.to_be_bytes()
|
||||
@@ -21,10 +24,11 @@ pub fn issued_credential_plaintext(
|
||||
.chain(deposit_id.to_be_bytes())
|
||||
.chain(blinded_partial_credential.to_bytes())
|
||||
.chain(
|
||||
bs58_encoded_private_attributes_commitments
|
||||
encoded_private_attributes_commitments
|
||||
.iter()
|
||||
.flat_map(|attr| attr.as_bytes().iter().copied()),
|
||||
.flat_map(|attr| attr.iter().copied()),
|
||||
)
|
||||
.chain(expiration_date.ecash_unix_timestamp().to_be_bytes())
|
||||
.chain(once(ticketbook_type.to_repr() as u8))
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ use crate::helpers::PlaceholderJsonSchemaImpl;
|
||||
use cosmrs::AccountId;
|
||||
use nym_compact_ecash::scheme::coin_indices_signatures::AnnotatedCoinIndexSignature;
|
||||
use nym_compact_ecash::scheme::expiration_date_signatures::AnnotatedExpirationDateSignature;
|
||||
use nym_compact_ecash::Bytable;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_credentials_interface::{
|
||||
BlindedSignature, CompactEcashError, CredentialSpendingData, PublicKeyUser,
|
||||
VerificationKeyAuth, WithdrawalRequest,
|
||||
@@ -115,6 +117,9 @@ pub struct BlindSignRequestBody {
|
||||
#[schemars(with = "String")]
|
||||
#[serde(with = "crate::helpers::date_serde")]
|
||||
pub expiration_date: Date,
|
||||
|
||||
#[schemars(with = "String")]
|
||||
pub ticketbook_type: TicketType,
|
||||
}
|
||||
|
||||
impl BlindSignRequestBody {
|
||||
@@ -124,6 +129,7 @@ impl BlindSignRequestBody {
|
||||
signature: identity::Signature,
|
||||
ecash_pubkey: PublicKeyUser,
|
||||
expiration_date: Date,
|
||||
ticketbook_type: TicketType,
|
||||
) -> BlindSignRequestBody {
|
||||
BlindSignRequestBody {
|
||||
inner_sign_request,
|
||||
@@ -131,16 +137,15 @@ impl BlindSignRequestBody {
|
||||
signature,
|
||||
ecash_pubkey,
|
||||
expiration_date,
|
||||
ticketbook_type,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode_commitments(&self) -> Vec<String> {
|
||||
use nym_compact_ecash::Base58;
|
||||
|
||||
pub fn encode_commitments(&self) -> Vec<Vec<u8>> {
|
||||
self.inner_sign_request
|
||||
.get_private_attributes_commitments()
|
||||
.iter()
|
||||
.map(|c| c.to_bs58())
|
||||
.map(|c| c.to_byte_vec())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
@@ -371,26 +376,26 @@ pub struct EpochCredentialsResponse {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct IssuedCredentialsResponse {
|
||||
// note: BTreeMap returns ordered results so it's fine to use it with pagination
|
||||
pub credentials: BTreeMap<i64, IssuedCredentialBody>,
|
||||
pub credentials: BTreeMap<i64, IssuedTicketbookBody>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct IssuedCredentialResponse {
|
||||
pub credential: Option<IssuedCredentialBody>,
|
||||
pub credential: Option<IssuedTicketbookBody>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct IssuedCredentialBody {
|
||||
pub credential: IssuedCredential,
|
||||
pub struct IssuedTicketbookBody {
|
||||
pub credential: IssuedTicketbook,
|
||||
#[schemars(with = "PlaceholderJsonSchemaImpl")]
|
||||
pub signature: identity::Signature,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct IssuedCredential {
|
||||
pub struct IssuedTicketbook {
|
||||
pub id: i64,
|
||||
pub epoch_id: u32,
|
||||
pub deposit_id: u32,
|
||||
@@ -400,22 +405,26 @@ pub struct IssuedCredential {
|
||||
// so that nym-api wouldn't need to parse the value out of its storage
|
||||
#[schemars(with = "PlaceholderJsonSchemaImpl")]
|
||||
pub blinded_partial_credential: BlindedSignature,
|
||||
pub bs58_encoded_private_attributes_commitments: Vec<String>,
|
||||
pub encoded_private_attributes_commitments: Vec<Vec<u8>>,
|
||||
|
||||
#[schemars(with = "String")]
|
||||
#[serde(with = "crate::helpers::date_serde")]
|
||||
pub expiration_date: Date,
|
||||
|
||||
#[schemars(with = "String")]
|
||||
pub ticketbook_type: TicketType,
|
||||
}
|
||||
|
||||
impl IssuedCredential {
|
||||
impl IssuedTicketbook {
|
||||
// this method doesn't have to be reversible so just naively concatenate everything
|
||||
pub fn signable_plaintext(&self) -> Vec<u8> {
|
||||
issued_credential_plaintext(
|
||||
self.epoch_id,
|
||||
self.deposit_id,
|
||||
&self.blinded_partial_credential,
|
||||
&self.bs58_encoded_private_attributes_commitments,
|
||||
&self.encoded_private_attributes_commitments,
|
||||
self.expiration_date,
|
||||
self.ticketbook_type,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::ecash::error::Result;
|
||||
use crate::ecash::storage::models::IssuedCredential;
|
||||
use nym_api_requests::ecash::models::IssuedCredentialBody;
|
||||
use crate::ecash::storage::models::IssuedTicketbook;
|
||||
use nym_api_requests::ecash::models::IssuedCredentialsResponse;
|
||||
use nym_api_requests::ecash::models::IssuedTicketbookBody;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub(crate) fn build_credentials_response(
|
||||
raw: Vec<IssuedCredential>,
|
||||
raw: Vec<IssuedTicketbook>,
|
||||
) -> Result<IssuedCredentialsResponse> {
|
||||
let mut credentials = BTreeMap::new();
|
||||
|
||||
for raw_credential in raw {
|
||||
let id = raw_credential.id;
|
||||
let api_issued = IssuedCredentialBody::try_from(raw_credential)?;
|
||||
let api_issued = IssuedTicketbookBody::try_from(raw_credential)?;
|
||||
let old = credentials.insert(id, api_issued);
|
||||
if old.is_some() {
|
||||
// why do we panic here rather than return an error? because it's a critical failure because
|
||||
|
||||
@@ -23,6 +23,7 @@ mod test {
|
||||
use crate::ecash::error::EcashError;
|
||||
use crate::ecash::tests::voucher_fixture;
|
||||
use nym_compact_ecash::{generate_keypair_user, scheme::withdrawal::WithdrawalRequest};
|
||||
use nym_credentials_interface::TicketType;
|
||||
use rand::rngs::OsRng;
|
||||
use time::macros::date;
|
||||
|
||||
@@ -95,6 +96,7 @@ mod test {
|
||||
"3MpHDLYMCmuMvZ9zkZXPkTK6nKArvQW3dJA1notoPPxnbBW2ommkR2dkpRWoeWSkUjQSLv1nRyiRzMWbobGLw1eh".parse().unwrap(),
|
||||
ecash_keypair.public_key(),
|
||||
expiration_date,
|
||||
TicketType::V1MixnetEntry,
|
||||
);
|
||||
|
||||
let good_deposit = Deposit {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use crate::node_status_api::models::NymApiStorageError;
|
||||
use nym_coconut_dkg_common::types::{ChunkIndex, DealingIndex, EpochId};
|
||||
use nym_credentials_interface::UnknownTicketType;
|
||||
use nym_crypto::asymmetric::{
|
||||
encryption::KeyRecoveryError,
|
||||
identity::{Ed25519RecoveryError, SignatureError},
|
||||
@@ -213,6 +214,9 @@ pub enum EcashError {
|
||||
|
||||
#[error("could not obtain enough shares for aggregation. got {shares} shares whilst the threshold is {threshold}")]
|
||||
InsufficientNumberOfShares { threshold: Threshold, shares: usize },
|
||||
|
||||
#[error(transparent)]
|
||||
UnknownTicketBookType(#[from] UnknownTicketType),
|
||||
}
|
||||
|
||||
impl<'r, 'o: 'r> Responder<'r, 'o> for EcashError {
|
||||
|
||||
@@ -7,7 +7,7 @@ use nym_coconut_dkg_common::types::EpochId;
|
||||
use nym_compact_ecash::scheme::coin_indices_signatures::AnnotatedCoinIndexSignature;
|
||||
use nym_compact_ecash::scheme::expiration_date_signatures::AnnotatedExpirationDateSignature;
|
||||
use nym_compact_ecash::scheme::keygen::SecretKeyAuth;
|
||||
use nym_compact_ecash::BlindedSignature;
|
||||
use nym_compact_ecash::{BlindedSignature, EncodedDate, EncodedTicketType};
|
||||
use nym_compact_ecash::{PublicKeyUser, WithdrawalRequest};
|
||||
use nym_ecash_time::EcashTime;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -31,7 +31,8 @@ pub(crate) struct IssuedCoinIndicesSignatures {
|
||||
|
||||
pub(crate) trait CredentialRequest {
|
||||
fn withdrawal_request(&self) -> &WithdrawalRequest;
|
||||
fn expiration_date_timestamp(&self) -> u64;
|
||||
fn expiration_date_timestamp(&self) -> EncodedDate;
|
||||
fn ticketbook_type(&self) -> EncodedTicketType;
|
||||
fn ecash_pubkey(&self) -> PublicKeyUser;
|
||||
}
|
||||
|
||||
@@ -40,10 +41,14 @@ impl CredentialRequest for BlindSignRequestBody {
|
||||
&self.inner_sign_request
|
||||
}
|
||||
|
||||
fn expiration_date_timestamp(&self) -> u64 {
|
||||
fn expiration_date_timestamp(&self) -> EncodedDate {
|
||||
self.expiration_date.ecash_unix_timestamp()
|
||||
}
|
||||
|
||||
fn ticketbook_type(&self) -> EncodedTicketType {
|
||||
self.ticketbook_type.encode()
|
||||
}
|
||||
|
||||
fn ecash_pubkey(&self) -> PublicKeyUser {
|
||||
self.ecash_pubkey.clone()
|
||||
}
|
||||
@@ -58,6 +63,7 @@ pub(crate) fn blind_sign<C: CredentialRequest>(
|
||||
request.ecash_pubkey().clone(),
|
||||
request.withdrawal_request(),
|
||||
request.expiration_date_timestamp(),
|
||||
request.ticketbook_type(),
|
||||
)?)
|
||||
}
|
||||
|
||||
|
||||
@@ -599,12 +599,13 @@ impl EcashState {
|
||||
blinded_signature,
|
||||
&encoded_commitments,
|
||||
request_body.expiration_date,
|
||||
request_body.ticketbook_type,
|
||||
);
|
||||
|
||||
let signature = self.local.identity_keypair.private_key().sign(plaintext);
|
||||
|
||||
// note: we have a UNIQUE constraint on the tx_hash column of the credential
|
||||
// and so if the api is processing request for the same hash at the same time,
|
||||
// note: we have a UNIQUE constraint on the deposit_id column of the credential
|
||||
// and so if the api is processing request for the same deposit at the same time,
|
||||
// only one of them will be successfully inserted to the database
|
||||
let credential_id = self
|
||||
.aux
|
||||
@@ -616,6 +617,7 @@ impl EcashState {
|
||||
signature,
|
||||
encoded_commitments,
|
||||
request_body.expiration_date,
|
||||
request_body.ticketbook_type,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::ecash::storage::models::{
|
||||
EpochCredentials, IssuedCredential, RawExpirationDateSignatures, SerialNumberWrapper,
|
||||
EpochCredentials, IssuedTicketbook, RawExpirationDateSignatures, SerialNumberWrapper,
|
||||
StoredBloomfilterParams, TicketProvider, VerifiedTicket,
|
||||
};
|
||||
use crate::support::storage::manager::StorageManager;
|
||||
@@ -49,7 +49,7 @@ pub trait EcashStorageManagerExt {
|
||||
async fn get_issued_credential(
|
||||
&self,
|
||||
credential_id: i64,
|
||||
) -> Result<Option<IssuedCredential>, sqlx::Error>;
|
||||
) -> Result<Option<IssuedTicketbook>, sqlx::Error>;
|
||||
|
||||
/// Attempts to retrieve an issued credential from the data store.
|
||||
///
|
||||
@@ -59,17 +59,18 @@ pub trait EcashStorageManagerExt {
|
||||
async fn get_issued_bandwidth_credential_by_deposit_id(
|
||||
&self,
|
||||
deposit_id: DepositId,
|
||||
) -> Result<Option<IssuedCredential>, sqlx::Error>;
|
||||
) -> Result<Option<IssuedTicketbook>, sqlx::Error>;
|
||||
|
||||
/// Store the provided issued credential information and return its (database) id.
|
||||
async fn store_issued_credential(
|
||||
async fn store_issued_ticketbook(
|
||||
&self,
|
||||
epoch_id: u32,
|
||||
deposit_id: DepositId,
|
||||
bs58_partial_credential: String,
|
||||
bs58_signature: String,
|
||||
joined_private_commitments: String,
|
||||
partial_credential: &[u8],
|
||||
signature: &[u8],
|
||||
joined_private_commitments: &[u8],
|
||||
expiration_date: Date,
|
||||
ticketbook_type_repr: u8,
|
||||
) -> Result<i64, sqlx::Error>;
|
||||
|
||||
/// Attempts to retrieve issued credentials from the data store using provided ids.
|
||||
@@ -77,10 +78,10 @@ pub trait EcashStorageManagerExt {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `credential_ids`: (database) ids of the issued credentials
|
||||
async fn get_issued_credentials(
|
||||
async fn get_issued_ticketbooks(
|
||||
&self,
|
||||
credential_ids: Vec<i64>,
|
||||
) -> Result<Vec<IssuedCredential>, sqlx::Error>;
|
||||
) -> Result<Vec<IssuedTicketbook>, sqlx::Error>;
|
||||
|
||||
/// Attempts to retrieve issued credentials from the data store using pagination specification.
|
||||
///
|
||||
@@ -88,11 +89,11 @@ pub trait EcashStorageManagerExt {
|
||||
///
|
||||
/// * `start_after`: the value preceding the first retrieved result
|
||||
/// * `limit`: the maximum number of entries to retrieve
|
||||
async fn get_issued_credentials_paged(
|
||||
async fn get_issued_ticketbooks_paged(
|
||||
&self,
|
||||
start_after: i64,
|
||||
limit: u32,
|
||||
) -> Result<Vec<IssuedCredential>, sqlx::Error>;
|
||||
) -> Result<Vec<IssuedTicketbook>, sqlx::Error>;
|
||||
|
||||
async fn insert_ticket_provider(&self, gateway_address: &str) -> Result<i64, sqlx::Error>;
|
||||
|
||||
@@ -360,12 +361,20 @@ impl EcashStorageManagerExt for StorageManager {
|
||||
async fn get_issued_credential(
|
||||
&self,
|
||||
credential_id: i64,
|
||||
) -> Result<Option<IssuedCredential>, sqlx::Error> {
|
||||
) -> Result<Option<IssuedTicketbook>, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
IssuedCredential,
|
||||
IssuedTicketbook,
|
||||
r#"
|
||||
SELECT id, epoch_id as "epoch_id: u32", deposit_id as "deposit_id: DepositId", bs58_partial_credential, bs58_signature,joined_private_commitments, expiration_date as "expiration_date: Date"
|
||||
FROM issued_credential
|
||||
SELECT
|
||||
id,
|
||||
epoch_id as "epoch_id: u32",
|
||||
deposit_id as "deposit_id: DepositId",
|
||||
partial_credential,
|
||||
signature,
|
||||
joined_private_commitments,
|
||||
expiration_date as "expiration_date: Date",
|
||||
ticketbook_type_repr as "ticketbook_type_repr: u8"
|
||||
FROM issued_ticketbook
|
||||
WHERE id = ?
|
||||
"#,
|
||||
credential_id
|
||||
@@ -382,38 +391,47 @@ impl EcashStorageManagerExt for StorageManager {
|
||||
async fn get_issued_bandwidth_credential_by_deposit_id(
|
||||
&self,
|
||||
deposit_id: DepositId,
|
||||
) -> Result<Option<IssuedCredential>, sqlx::Error> {
|
||||
) -> Result<Option<IssuedTicketbook>, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
IssuedCredential,
|
||||
IssuedTicketbook,
|
||||
r#"
|
||||
SELECT id, epoch_id as "epoch_id: u32", deposit_id as "deposit_id: u32", bs58_partial_credential, bs58_signature,joined_private_commitments, expiration_date as "expiration_date: Date"
|
||||
FROM issued_credential
|
||||
SELECT
|
||||
id,
|
||||
epoch_id as "epoch_id: u32",
|
||||
deposit_id as "deposit_id: DepositId",
|
||||
partial_credential,
|
||||
signature,
|
||||
joined_private_commitments,
|
||||
expiration_date as "expiration_date: Date",
|
||||
ticketbook_type_repr as "ticketbook_type_repr: u8"
|
||||
FROM issued_ticketbook
|
||||
WHERE deposit_id = ?
|
||||
"#,
|
||||
deposit_id
|
||||
)
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Store the provided issued credential information and return its (database) id.
|
||||
async fn store_issued_credential(
|
||||
async fn store_issued_ticketbook(
|
||||
&self,
|
||||
epoch_id: u32,
|
||||
deposit_id: DepositId,
|
||||
bs58_partial_credential: String,
|
||||
bs58_signature: String,
|
||||
joined_private_commitments: String,
|
||||
partial_credential: &[u8],
|
||||
signature: &[u8],
|
||||
joined_private_commitments: &[u8],
|
||||
expiration_date: Date,
|
||||
ticketbook_type_repr: u8,
|
||||
) -> Result<i64, sqlx::Error> {
|
||||
let row_id = sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO issued_credential
|
||||
(epoch_id, deposit_id, bs58_partial_credential, bs58_signature, joined_private_commitments, expiration_date)
|
||||
INSERT INTO issued_ticketbook
|
||||
(epoch_id, deposit_id, partial_credential, signature, joined_private_commitments, expiration_date, ticketbook_type_repr)
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?, ?)
|
||||
(?, ?, ?, ?, ?, ?, ?)
|
||||
"#,
|
||||
epoch_id, deposit_id, bs58_partial_credential, bs58_signature, joined_private_commitments, expiration_date
|
||||
epoch_id, deposit_id, partial_credential, signature, joined_private_commitments, expiration_date, ticketbook_type_repr
|
||||
).execute(&self.connection_pool).await?.last_insert_rowid();
|
||||
|
||||
Ok(row_id)
|
||||
@@ -424,14 +442,14 @@ impl EcashStorageManagerExt for StorageManager {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `credential_ids`: (database) ids of the issued credentials
|
||||
async fn get_issued_credentials(
|
||||
async fn get_issued_ticketbooks(
|
||||
&self,
|
||||
credential_ids: Vec<i64>,
|
||||
) -> Result<Vec<IssuedCredential>, sqlx::Error> {
|
||||
) -> Result<Vec<IssuedTicketbook>, sqlx::Error> {
|
||||
// that sucks : (
|
||||
// https://stackoverflow.com/a/70032524
|
||||
let params = format!("?{}", ", ?".repeat(credential_ids.len() - 1));
|
||||
let query_str = format!("SELECT * FROM issued_credential WHERE id IN ( {params} )");
|
||||
let query_str = format!("SELECT * FROM issued_ticketbook WHERE id IN ( {params} )");
|
||||
let mut query = sqlx::query_as(&query_str);
|
||||
for id in credential_ids {
|
||||
query = query.bind(id)
|
||||
@@ -446,16 +464,24 @@ impl EcashStorageManagerExt for StorageManager {
|
||||
///
|
||||
/// * `start_after`: the value preceding the first retrieved result
|
||||
/// * `limit`: the maximum number of entries to retrieve
|
||||
async fn get_issued_credentials_paged(
|
||||
async fn get_issued_ticketbooks_paged(
|
||||
&self,
|
||||
start_after: i64,
|
||||
limit: u32,
|
||||
) -> Result<Vec<IssuedCredential>, sqlx::Error> {
|
||||
) -> Result<Vec<IssuedTicketbook>, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
IssuedCredential,
|
||||
IssuedTicketbook,
|
||||
r#"
|
||||
SELECT id, epoch_id as "epoch_id: u32", deposit_id as "deposit_id: u32", bs58_partial_credential, bs58_signature,joined_private_commitments, expiration_date as "expiration_date: Date"
|
||||
FROM issued_credential
|
||||
SELECT
|
||||
id,
|
||||
epoch_id as "epoch_id: u32",
|
||||
deposit_id as "deposit_id: DepositId",
|
||||
partial_credential,
|
||||
signature,
|
||||
joined_private_commitments,
|
||||
expiration_date as "expiration_date: Date",
|
||||
ticketbook_type_repr as "ticketbook_type_repr: u8"
|
||||
FROM issued_ticketbook
|
||||
WHERE id > ?
|
||||
ORDER BY id
|
||||
LIMIT ?
|
||||
@@ -463,8 +489,8 @@ impl EcashStorageManagerExt for StorageManager {
|
||||
start_after,
|
||||
limit
|
||||
)
|
||||
.fetch_all(&self.connection_pool)
|
||||
.await
|
||||
.fetch_all(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn insert_ticket_provider(&self, gateway_address: &str) -> Result<i64, sqlx::Error> {
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::ecash::storage::helpers::{
|
||||
};
|
||||
use crate::ecash::storage::manager::EcashStorageManagerExt;
|
||||
use crate::ecash::storage::models::{
|
||||
join_attributes, EpochCredentials, IssuedCredential, SerialNumberWrapper, TicketProvider,
|
||||
join_attributes, EpochCredentials, IssuedTicketbook, SerialNumberWrapper, TicketProvider,
|
||||
};
|
||||
use crate::node_status_api::models::NymApiStorageError;
|
||||
use crate::support::storage::NymApiStorage;
|
||||
@@ -16,9 +16,10 @@ use nym_api_requests::ecash::models::Pagination;
|
||||
use nym_coconut_dkg_common::types::EpochId;
|
||||
use nym_compact_ecash::scheme::coin_indices_signatures::AnnotatedCoinIndexSignature;
|
||||
use nym_compact_ecash::BlindedSignature;
|
||||
use nym_compact_ecash::{Base58, VerificationKeyAuth};
|
||||
use nym_compact_ecash::VerificationKeyAuth;
|
||||
use nym_config::defaults::BloomfilterParameters;
|
||||
use nym_credentials::CredentialSpendingData;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_ecash_contract_common::deposit::DepositId;
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
@@ -81,12 +82,12 @@ pub trait EcashStorageExt {
|
||||
async fn get_issued_credential(
|
||||
&self,
|
||||
credential_id: i64,
|
||||
) -> Result<Option<IssuedCredential>, NymApiStorageError>;
|
||||
) -> Result<Option<IssuedTicketbook>, NymApiStorageError>;
|
||||
|
||||
async fn get_issued_bandwidth_credential_by_deposit_id(
|
||||
&self,
|
||||
deposit_id: DepositId,
|
||||
) -> Result<Option<IssuedCredential>, NymApiStorageError>;
|
||||
) -> Result<Option<IssuedTicketbook>, NymApiStorageError>;
|
||||
|
||||
async fn store_issued_credential(
|
||||
&self,
|
||||
@@ -94,19 +95,20 @@ pub trait EcashStorageExt {
|
||||
deposit_id: DepositId,
|
||||
partial_credential: &BlindedSignature,
|
||||
signature: identity::Signature,
|
||||
private_commitments: Vec<String>,
|
||||
private_commitments: Vec<Vec<u8>>,
|
||||
expiration_date: Date,
|
||||
ticketbook_type: TicketType,
|
||||
) -> Result<i64, NymApiStorageError>;
|
||||
|
||||
async fn get_issued_credentials(
|
||||
&self,
|
||||
credential_ids: Vec<i64>,
|
||||
) -> Result<Vec<IssuedCredential>, NymApiStorageError>;
|
||||
) -> Result<Vec<IssuedTicketbook>, NymApiStorageError>;
|
||||
|
||||
async fn get_issued_credentials_paged(
|
||||
&self,
|
||||
pagination: Pagination<i64>,
|
||||
) -> Result<Vec<IssuedCredential>, NymApiStorageError>;
|
||||
) -> Result<Vec<IssuedTicketbook>, NymApiStorageError>;
|
||||
//
|
||||
// async fn insert_credential(
|
||||
// &self,
|
||||
@@ -308,14 +310,14 @@ impl EcashStorageExt for NymApiStorage {
|
||||
async fn get_issued_credential(
|
||||
&self,
|
||||
credential_id: i64,
|
||||
) -> Result<Option<IssuedCredential>, NymApiStorageError> {
|
||||
) -> Result<Option<IssuedTicketbook>, NymApiStorageError> {
|
||||
Ok(self.manager.get_issued_credential(credential_id).await?)
|
||||
}
|
||||
|
||||
async fn get_issued_bandwidth_credential_by_deposit_id(
|
||||
&self,
|
||||
deposit_id: DepositId,
|
||||
) -> Result<Option<IssuedCredential>, NymApiStorageError> {
|
||||
) -> Result<Option<IssuedTicketbook>, NymApiStorageError> {
|
||||
Ok(self
|
||||
.manager
|
||||
.get_issued_bandwidth_credential_by_deposit_id(deposit_id)
|
||||
@@ -328,18 +330,20 @@ impl EcashStorageExt for NymApiStorage {
|
||||
deposit_id: DepositId,
|
||||
partial_credential: &BlindedSignature,
|
||||
signature: identity::Signature,
|
||||
private_commitments: Vec<String>,
|
||||
private_commitments: Vec<Vec<u8>>,
|
||||
expiration_date: Date,
|
||||
ticketbook_type: TicketType,
|
||||
) -> Result<i64, NymApiStorageError> {
|
||||
Ok(self
|
||||
.manager
|
||||
.store_issued_credential(
|
||||
.store_issued_ticketbook(
|
||||
epoch_id,
|
||||
deposit_id,
|
||||
partial_credential.to_bs58(),
|
||||
signature.to_base58_string(),
|
||||
join_attributes(private_commitments),
|
||||
&partial_credential.to_bytes(),
|
||||
&signature.to_bytes(),
|
||||
&join_attributes(private_commitments),
|
||||
expiration_date,
|
||||
ticketbook_type.encode(),
|
||||
)
|
||||
.await?)
|
||||
}
|
||||
@@ -347,14 +351,14 @@ impl EcashStorageExt for NymApiStorage {
|
||||
async fn get_issued_credentials(
|
||||
&self,
|
||||
credential_ids: Vec<i64>,
|
||||
) -> Result<Vec<IssuedCredential>, NymApiStorageError> {
|
||||
Ok(self.manager.get_issued_credentials(credential_ids).await?)
|
||||
) -> Result<Vec<IssuedTicketbook>, NymApiStorageError> {
|
||||
Ok(self.manager.get_issued_ticketbooks(credential_ids).await?)
|
||||
}
|
||||
|
||||
async fn get_issued_credentials_paged(
|
||||
&self,
|
||||
pagination: Pagination<i64>,
|
||||
) -> Result<Vec<IssuedCredential>, NymApiStorageError> {
|
||||
) -> Result<Vec<IssuedTicketbook>, NymApiStorageError> {
|
||||
// rows start at 1
|
||||
let start_after = pagination.last_key.unwrap_or(0);
|
||||
let limit = match pagination.limit {
|
||||
@@ -370,7 +374,7 @@ impl EcashStorageExt for NymApiStorage {
|
||||
|
||||
Ok(self
|
||||
.manager
|
||||
.get_issued_credentials_paged(start_after, limit)
|
||||
.get_issued_ticketbooks_paged(start_after, limit)
|
||||
.await?)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,15 +4,16 @@
|
||||
use crate::ecash::error::EcashError;
|
||||
use crate::node_status_api::models::NymApiStorageError;
|
||||
use nym_api_requests::ecash::models::{
|
||||
EpochCredentialsResponse, IssuedCredential as ApiIssuedCredential,
|
||||
IssuedCredentialBody as ApiIssuedCredentialInner,
|
||||
EpochCredentialsResponse, IssuedTicketbook as ApiIssuedCredential,
|
||||
IssuedTicketbookBody as ApiIssuedCredentialInner,
|
||||
};
|
||||
use nym_api_requests::ecash::BlindedSignatureResponse;
|
||||
use nym_compact_ecash::{Base58, BlindedSignature};
|
||||
use nym_compact_ecash::BlindedSignature;
|
||||
use nym_config::defaults::BloomfilterParameters;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_ecash_contract_common::deposit::DepositId;
|
||||
use sqlx::FromRow;
|
||||
use std::fmt::Display;
|
||||
use std::ops::Deref;
|
||||
use time::{Date, OffsetDateTime};
|
||||
|
||||
@@ -71,21 +72,22 @@ pub struct VerifiedTicket {
|
||||
}
|
||||
|
||||
#[derive(FromRow)]
|
||||
pub struct IssuedCredential {
|
||||
pub struct IssuedTicketbook {
|
||||
pub id: i64,
|
||||
pub epoch_id: u32,
|
||||
pub deposit_id: DepositId,
|
||||
|
||||
/// base58-encoded issued credential
|
||||
pub bs58_partial_credential: String,
|
||||
pub partial_credential: Vec<u8>,
|
||||
|
||||
/// base58-encoded signature on the issued credential (and the attributes)
|
||||
pub bs58_signature: String,
|
||||
/// signature on the issued credential (and the attributes)
|
||||
pub signature: Vec<u8>,
|
||||
|
||||
// i.e. "'attr1','attr2',..."
|
||||
pub joined_private_commitments: String,
|
||||
pub joined_private_commitments: Vec<u8>,
|
||||
|
||||
pub expiration_date: Date,
|
||||
|
||||
pub ticketbook_type_repr: u8,
|
||||
}
|
||||
|
||||
#[derive(FromRow)]
|
||||
@@ -141,58 +143,61 @@ impl<'a> TryFrom<&'a StoredBloomfilterParams> for BloomfilterParameters {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<IssuedCredential> for ApiIssuedCredentialInner {
|
||||
impl TryFrom<IssuedTicketbook> for ApiIssuedCredentialInner {
|
||||
type Error = EcashError;
|
||||
|
||||
fn try_from(value: IssuedCredential) -> Result<Self, Self::Error> {
|
||||
fn try_from(value: IssuedTicketbook) -> Result<Self, Self::Error> {
|
||||
Ok(ApiIssuedCredentialInner {
|
||||
credential: ApiIssuedCredential {
|
||||
id: value.id,
|
||||
epoch_id: value.epoch_id,
|
||||
deposit_id: value.deposit_id,
|
||||
blinded_partial_credential: BlindedSignature::try_from_bs58(
|
||||
value.bs58_partial_credential,
|
||||
blinded_partial_credential: BlindedSignature::from_bytes(
|
||||
&value.partial_credential,
|
||||
)?,
|
||||
bs58_encoded_private_attributes_commitments: split_attributes(
|
||||
&value.joined_private_commitments,
|
||||
encoded_private_attributes_commitments: split_attributes(
|
||||
value.joined_private_commitments,
|
||||
),
|
||||
expiration_date: value.expiration_date,
|
||||
ticketbook_type: TicketType::try_from_encoded(value.ticketbook_type_repr)?,
|
||||
},
|
||||
signature: value.bs58_signature.parse()?,
|
||||
signature: ed25519::Signature::from_bytes(&value.signature)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<IssuedCredential> for BlindedSignatureResponse {
|
||||
impl TryFrom<IssuedTicketbook> for BlindedSignatureResponse {
|
||||
type Error = EcashError;
|
||||
|
||||
fn try_from(value: IssuedCredential) -> Result<Self, Self::Error> {
|
||||
fn try_from(value: IssuedTicketbook) -> Result<Self, Self::Error> {
|
||||
Ok(BlindedSignatureResponse {
|
||||
blinded_signature: BlindedSignature::try_from_bs58(value.bs58_partial_credential)?,
|
||||
blinded_signature: BlindedSignature::from_bytes(&value.partial_credential)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<IssuedCredential> for BlindedSignature {
|
||||
impl TryFrom<IssuedTicketbook> for BlindedSignature {
|
||||
type Error = EcashError;
|
||||
|
||||
fn try_from(value: IssuedCredential) -> Result<Self, Self::Error> {
|
||||
Ok(BlindedSignature::try_from_bs58(
|
||||
value.bs58_partial_credential,
|
||||
)?)
|
||||
fn try_from(value: IssuedTicketbook) -> Result<Self, Self::Error> {
|
||||
Ok(BlindedSignature::from_bytes(&value.partial_credential)?)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn join_attributes<I, M>(attrs: I) -> String
|
||||
where
|
||||
I: IntoIterator<Item = M>,
|
||||
M: Display,
|
||||
{
|
||||
// I could have used `attrs.into_iter().join(",")`,
|
||||
// but my IDE didn't like it (compiler was fine)
|
||||
itertools::Itertools::join(&mut attrs.into_iter(), ",")
|
||||
pub(crate) fn join_attributes(attrs: Vec<Vec<u8>>) -> Vec<u8> {
|
||||
// note: 48 is length of encoded G1 element
|
||||
let mut out = Vec::with_capacity(48 * attrs.len());
|
||||
for mut attr in attrs {
|
||||
// since this is called internally only, we expect valid attributes here!
|
||||
assert_eq!(attr.len(), 48);
|
||||
|
||||
out.append(&mut attr)
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
pub fn split_attributes(attrs: &str) -> Vec<String> {
|
||||
attrs.split(',').map(|s| s.to_string()).collect()
|
||||
pub(crate) fn split_attributes(attrs: Vec<u8>) -> Vec<Vec<u8>> {
|
||||
assert_eq!(attrs.len() % 48, 0, "database corruption");
|
||||
attrs.chunks_exact(48).map(|c| c.to_vec()).collect()
|
||||
}
|
||||
|
||||
@@ -148,9 +148,7 @@ async fn issued_credential() {
|
||||
|
||||
assert_eq!(
|
||||
request1.encode_commitments(),
|
||||
issued1
|
||||
.credential
|
||||
.bs58_encoded_private_attributes_commitments
|
||||
issued1.credential.encoded_private_attributes_commitments
|
||||
);
|
||||
assert_eq!(
|
||||
voucher1.expiration_date(),
|
||||
@@ -167,9 +165,7 @@ async fn issued_credential() {
|
||||
|
||||
assert_eq!(
|
||||
request2.encode_commitments(),
|
||||
issued2
|
||||
.credential
|
||||
.bs58_encoded_private_attributes_commitments
|
||||
issued2.credential.encoded_private_attributes_commitments
|
||||
);
|
||||
assert_eq!(
|
||||
voucher2.expiration_date(),
|
||||
|
||||
@@ -14,7 +14,7 @@ use cosmwasm_std::{
|
||||
};
|
||||
use cw3::{Proposal, ProposalResponse, Vote, VoteInfo, VoteResponse, Votes};
|
||||
use cw4::{Cw4Contract, MemberResponse};
|
||||
use nym_api_requests::ecash::models::{IssuedCredentialBody, IssuedCredentialResponse};
|
||||
use nym_api_requests::ecash::models::{IssuedCredentialResponse, IssuedTicketbookBody};
|
||||
use nym_api_requests::ecash::{BlindSignRequestBody, BlindedSignatureResponse};
|
||||
use nym_coconut_dkg_common::dealer::{
|
||||
DealerDetails, DealerDetailsResponse, DealerType, RegisteredDealerDetails,
|
||||
@@ -33,6 +33,7 @@ use nym_compact_ecash::BlindedSignature;
|
||||
use nym_compact_ecash::{ttp_keygen, VerificationKeyAuth};
|
||||
use nym_contracts_common::IdentityKey;
|
||||
use nym_credentials::IssuanceTicketBook;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_dkg::{NodeIndex, Threshold};
|
||||
use nym_ecash_contract_common::blacklist::{BlacklistedAccountResponse, Blacklisting};
|
||||
@@ -1223,7 +1224,7 @@ pub fn voucher_fixture(deposit_id: Option<DepositId>) -> IssuanceTicketBook {
|
||||
identity::PrivateKey::from_bytes(&identity_keypair.private_key().to_bytes()).unwrap();
|
||||
let identifier = [44u8; 32];
|
||||
// (voucher, request)
|
||||
IssuanceTicketBook::new(deposit_id, identifier, id_priv)
|
||||
IssuanceTicketBook::new(deposit_id, identifier, id_priv, TicketType::V1MixnetEntry)
|
||||
}
|
||||
|
||||
fn dummy_signature() -> identity::Signature {
|
||||
@@ -1372,7 +1373,7 @@ impl TestFixture {
|
||||
serde_json::from_str(&response.into_string().await.unwrap()).unwrap()
|
||||
}
|
||||
|
||||
async fn issued_unchecked(&self, id: i64) -> IssuedCredentialBody {
|
||||
async fn issued_unchecked(&self, id: i64) -> IssuedTicketbookBody {
|
||||
self.issued_credential(id)
|
||||
.await
|
||||
.unwrap()
|
||||
@@ -1409,6 +1410,7 @@ mod credential_tests {
|
||||
dummy_signature(),
|
||||
commitments,
|
||||
expiration_date,
|
||||
voucher.ticketbook_type(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -1494,6 +1496,7 @@ mod credential_tests {
|
||||
dummy_signature(),
|
||||
commitments.clone(),
|
||||
expiration_date,
|
||||
voucher.ticketbook_type(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -1527,6 +1530,7 @@ mod credential_tests {
|
||||
dummy_signature(),
|
||||
commitments.clone(),
|
||||
expiration_date,
|
||||
voucher.ticketbook_type(),
|
||||
)
|
||||
.await;
|
||||
assert!(storage_err.is_err());
|
||||
@@ -1542,6 +1546,7 @@ mod credential_tests {
|
||||
dummy_signature(),
|
||||
commitments.clone(),
|
||||
expiration_date,
|
||||
voucher.ticketbook_type(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -1572,6 +1577,7 @@ mod credential_tests {
|
||||
identity_keypair.private_key().to_base58_string(),
|
||||
)
|
||||
.unwrap(),
|
||||
TicketType::V1MixnetEntry,
|
||||
);
|
||||
|
||||
let deposit = Deposit {
|
||||
|
||||
@@ -47,3 +47,8 @@ nyxd-scraper = { path = "../common/nyxd-scraper" }
|
||||
[build-dependencies]
|
||||
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||
|
||||
[dev-dependencies]
|
||||
rand_chacha = { workspace = true }
|
||||
nym-credentials-interface = { path = "../common/credentials-interface" }
|
||||
nym-crypto = { path = "../common/crypto", features = ["rand"] }
|
||||
|
||||
@@ -115,9 +115,8 @@ pub enum NymRewarderError {
|
||||
received: usize,
|
||||
},
|
||||
|
||||
#[error("the following private attribute commitment is malformed: {raw}: {source}")]
|
||||
#[error("one of the provided private attribute commitment is malformed: {source}")]
|
||||
MalformedCredentialCommitment {
|
||||
raw: String,
|
||||
#[source]
|
||||
source: CompactEcashError,
|
||||
},
|
||||
|
||||
@@ -12,13 +12,12 @@ use crate::rewarder::storage::RewarderStorage;
|
||||
use bip39::rand::prelude::SliceRandom;
|
||||
use bip39::rand::thread_rng;
|
||||
use nym_coconut_dkg_common::types::EpochId;
|
||||
use nym_compact_ecash::scheme::expiration_date_signatures::date_scalar;
|
||||
use nym_compact_ecash::scheme::withdrawal::verify_partial_blind_signature;
|
||||
use nym_compact_ecash::{Base58, G1Projective, VerificationKeyAuth};
|
||||
use nym_compact_ecash::{date_scalar, type_scalar, Bytable, G1Projective, VerificationKeyAuth};
|
||||
use nym_credentials::ecash::utils::EcashTime;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_task::TaskClient;
|
||||
use nym_validator_client::nym_api::{IssuedCredential, IssuedCredentialBody, NymApiClientExt};
|
||||
use nym_validator_client::nym_api::{IssuedTicketbook, IssuedTicketbookBody, NymApiClientExt};
|
||||
use std::cmp::max;
|
||||
use tokio::time::interval;
|
||||
use tracing::{debug, error, info, instrument, trace};
|
||||
@@ -47,7 +46,7 @@ impl CredentialIssuanceMonitor {
|
||||
|
||||
fn validate_credential_signature(
|
||||
&mut self,
|
||||
issued_credential: &IssuedCredentialBody,
|
||||
issued_credential: &IssuedTicketbookBody,
|
||||
identity_key: &ed25519::PublicKey,
|
||||
) -> Result<(), NymRewarderError> {
|
||||
let plaintext = issued_credential.credential.signable_plaintext();
|
||||
@@ -66,7 +65,7 @@ impl CredentialIssuanceMonitor {
|
||||
async fn check_deposit_reuse(
|
||||
&mut self,
|
||||
issuer_identity: &str,
|
||||
credential_info: &IssuedCredentialBody,
|
||||
credential_info: &IssuedTicketbookBody,
|
||||
) -> Result<bool, NymRewarderError> {
|
||||
let credential_id = credential_info.credential.id;
|
||||
let deposit_id = credential_info.credential.deposit_id;
|
||||
@@ -102,7 +101,7 @@ impl CredentialIssuanceMonitor {
|
||||
|
||||
async fn validate_deposit(
|
||||
&mut self,
|
||||
issued_credential: &IssuedCredentialBody,
|
||||
issued_credential: &IssuedTicketbookBody,
|
||||
) -> Result<(), NymRewarderError> {
|
||||
// check if this deposit even exists
|
||||
let deposit_id = issued_credential.credential.deposit_id;
|
||||
@@ -115,51 +114,18 @@ impl CredentialIssuanceMonitor {
|
||||
}
|
||||
|
||||
fn verify_credential(
|
||||
&mut self,
|
||||
&self,
|
||||
vk: &VerificationKeyAuth,
|
||||
credential: &IssuedCredential,
|
||||
credential: &IssuedTicketbook,
|
||||
) -> Result<(), NymRewarderError> {
|
||||
let public_attributes = [date_scalar(
|
||||
credential.expiration_date.ecash_unix_timestamp(),
|
||||
)];
|
||||
|
||||
#[allow(clippy::map_identity)]
|
||||
let attributes_refs = public_attributes.iter().collect::<Vec<_>>();
|
||||
|
||||
let mut public_attribute_commitments =
|
||||
Vec::with_capacity(credential.bs58_encoded_private_attributes_commitments.len());
|
||||
|
||||
for raw_cm in &credential.bs58_encoded_private_attributes_commitments {
|
||||
match G1Projective::try_from_bs58(raw_cm) {
|
||||
Ok(cm) => public_attribute_commitments.push(cm),
|
||||
Err(source) => {
|
||||
return Err(NymRewarderError::MalformedCredentialCommitment {
|
||||
raw: raw_cm.clone(),
|
||||
source,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// actually do verify the credential now
|
||||
if !verify_partial_blind_signature(
|
||||
&public_attribute_commitments,
|
||||
&attributes_refs,
|
||||
&credential.blinded_partial_credential,
|
||||
vk,
|
||||
) {
|
||||
return Err(NymRewarderError::BlindVerificationFailure);
|
||||
}
|
||||
trace!("credential correctly verifies");
|
||||
|
||||
Ok(())
|
||||
verify_credential(vk, credential)
|
||||
}
|
||||
|
||||
#[instrument(skip_all, fields(credential_id = %issued_credential.credential.id, deposit_id = %issued_credential.credential.deposit_id))]
|
||||
async fn validate_issued_credential(
|
||||
&mut self,
|
||||
issuer: &CredentialIssuer,
|
||||
issued_credential: &IssuedCredentialBody,
|
||||
issued_credential: &IssuedTicketbookBody,
|
||||
) -> Result<(), NymRewarderError> {
|
||||
// check if the issuer has actually signed that issued credential information
|
||||
self.validate_credential_signature(issued_credential, &issuer.public_key)?;
|
||||
@@ -323,3 +289,92 @@ impl CredentialIssuanceMonitor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_credential(
|
||||
vk: &VerificationKeyAuth,
|
||||
credential: &IssuedTicketbook,
|
||||
) -> Result<(), NymRewarderError> {
|
||||
let public_attributes = [
|
||||
date_scalar(credential.expiration_date.ecash_unix_timestamp()),
|
||||
type_scalar(credential.ticketbook_type.encode()),
|
||||
];
|
||||
|
||||
#[allow(clippy::map_identity)]
|
||||
let attributes_refs = public_attributes.iter().collect::<Vec<_>>();
|
||||
|
||||
let mut public_attribute_commitments =
|
||||
Vec::with_capacity(credential.encoded_private_attributes_commitments.len());
|
||||
|
||||
for raw_cm in &credential.encoded_private_attributes_commitments {
|
||||
match G1Projective::try_from_byte_slice(raw_cm) {
|
||||
Ok(cm) => public_attribute_commitments.push(cm),
|
||||
Err(source) => return Err(NymRewarderError::MalformedCredentialCommitment { source }),
|
||||
}
|
||||
}
|
||||
|
||||
// actually do verify the credential now
|
||||
if !verify_partial_blind_signature(
|
||||
&public_attribute_commitments,
|
||||
&attributes_refs,
|
||||
&credential.blinded_partial_credential,
|
||||
vk,
|
||||
) {
|
||||
return Err(NymRewarderError::BlindVerificationFailure);
|
||||
}
|
||||
trace!("credential correctly verifies");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nym_compact_ecash::ttp_keygen;
|
||||
use nym_credentials::IssuanceTicketBook;
|
||||
use nym_crypto::asymmetric::{ed25519, identity};
|
||||
use rand_chacha::rand_core::{RngCore, SeedableRng};
|
||||
|
||||
#[test]
|
||||
fn verify_issued_partial_credential() -> anyhow::Result<()> {
|
||||
let dummy_seed = [42u8; 32];
|
||||
let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed);
|
||||
|
||||
let ecash_keypair = ttp_keygen(1, 1)?.pop().unwrap();
|
||||
|
||||
let deposit_id = rng.next_u32();
|
||||
|
||||
// create dummy ticketbook
|
||||
let identity_keypair = ed25519::KeyPair::new(&mut rng);
|
||||
let id_priv =
|
||||
identity::PrivateKey::from_bytes(&identity_keypair.private_key().to_bytes()).unwrap();
|
||||
let identifier = [44u8; 32];
|
||||
|
||||
let issuance = IssuanceTicketBook::new(deposit_id, identifier, id_priv, Default::default());
|
||||
let signing_data = issuance.prepare_for_signing();
|
||||
let request = issuance.create_blind_sign_request_body(&signing_data);
|
||||
|
||||
let partial = nym_compact_ecash::scheme::withdrawal::issue(
|
||||
ecash_keypair.secret_key(),
|
||||
request.ecash_pubkey.clone(),
|
||||
&request.inner_sign_request,
|
||||
request.expiration_date.ecash_unix_timestamp(),
|
||||
request.ticketbook_type.encode(),
|
||||
)?;
|
||||
|
||||
let commitments = request.encode_commitments();
|
||||
|
||||
let issued = IssuedTicketbook {
|
||||
id: 0,
|
||||
epoch_id: 0,
|
||||
deposit_id,
|
||||
blinded_partial_credential: partial,
|
||||
encoded_private_attributes_commitments: commitments,
|
||||
expiration_date: issuance.expiration_date(),
|
||||
ticketbook_type: issuance.ticketbook_type(),
|
||||
};
|
||||
|
||||
assert!(verify_credential(ecash_keypair.verification_key_ref(), &issued).is_ok());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::rewarder::credential_issuance::types::CredentialIssuer;
|
||||
use crate::rewarder::epoch::Epoch;
|
||||
use crate::rewarder::storage::manager::StorageManager;
|
||||
use crate::rewarder::{EpochRewards, RewardingResult};
|
||||
use nym_validator_client::nym_api::IssuedCredentialBody;
|
||||
use nym_validator_client::nym_api::IssuedTicketbookBody;
|
||||
use nym_validator_client::nyxd::contract_traits::ecash_query_client::DepositId;
|
||||
use nym_validator_client::nyxd::Coin;
|
||||
use sqlx::ConnectOptions;
|
||||
@@ -95,7 +95,7 @@ impl RewarderStorage {
|
||||
pub(crate) async fn insert_validated_deposit(
|
||||
&self,
|
||||
operator_identity_bs58: String,
|
||||
credential_info: &IssuedCredentialBody,
|
||||
credential_info: &IssuedTicketbookBody,
|
||||
) -> Result<(), NymRewarderError> {
|
||||
self.manager
|
||||
.insert_validated_deposit(
|
||||
@@ -113,7 +113,7 @@ impl RewarderStorage {
|
||||
&self,
|
||||
operator_identity_bs58: String,
|
||||
original_credential_id: i64,
|
||||
credential_info: &IssuedCredentialBody,
|
||||
credential_info: &IssuedTicketbookBody,
|
||||
) -> Result<(), NymRewarderError> {
|
||||
self.manager
|
||||
.insert_double_signing_evidence(
|
||||
@@ -131,7 +131,7 @@ impl RewarderStorage {
|
||||
pub(crate) async fn insert_issuance_foul_play_evidence(
|
||||
&self,
|
||||
issuer: &CredentialIssuer,
|
||||
credential_info: &IssuedCredentialBody,
|
||||
credential_info: &IssuedTicketbookBody,
|
||||
error_message: String,
|
||||
) -> Result<(), NymRewarderError> {
|
||||
self.manager
|
||||
|
||||
Generated
+2
@@ -3193,8 +3193,10 @@ dependencies = [
|
||||
"bls12_381",
|
||||
"nym-compact-ecash",
|
||||
"nym-ecash-time",
|
||||
"nym-network-defaults",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"strum 0.25.0",
|
||||
"thiserror",
|
||||
"time",
|
||||
]
|
||||
|
||||
@@ -9,11 +9,12 @@ license.workspace = true
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
bip39 = { workspace = true }
|
||||
nym-client-core = { path = "../../../common/client-core", features = ["fs-surb-storage", "fs-gateways-storage"] }
|
||||
nym-client-core = { path = "../../../common/client-core", features = ["fs-credentials-storage", "fs-surb-storage", "fs-gateways-storage"] }
|
||||
nym-crypto = { path = "../../../common/crypto" }
|
||||
nym-gateway-requests = { path = "../../../gateway/gateway-requests" }
|
||||
nym-bandwidth-controller = { path = "../../../common/bandwidth-controller" }
|
||||
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" }
|
||||
nym-network-defaults = { path = "../../../common/network-defaults" }
|
||||
|
||||
@@ -18,7 +18,9 @@ async fn main() -> anyhow::Result<()> {
|
||||
.enable_credentials_mode()
|
||||
.build()?;
|
||||
|
||||
let bandwidth_client = mixnet_client.create_bandwidth_client(mnemonic).await?;
|
||||
let bandwidth_client = mixnet_client
|
||||
.create_bandwidth_client(mnemonic, Default::default())
|
||||
.await?;
|
||||
|
||||
// Get a bandwidth credential for the mixnet_client
|
||||
bandwidth_client.acquire().await?;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
//! .build()
|
||||
//! .unwrap();
|
||||
//!
|
||||
//! let bandwidth_client = mixnet_client.create_bandwidth_client(String::from("my super secret mnemonic")).await.unwrap();
|
||||
//! let bandwidth_client = mixnet_client.create_bandwidth_client(String::from("my super secret mnemonic"), Default::default()).await.unwrap();
|
||||
//!
|
||||
//! // Get a bandwidth credential for the mixnet_client
|
||||
//! bandwidth_client.acquire().await.unwrap();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
use crate::error::Result;
|
||||
use nym_credential_storage::storage::Storage;
|
||||
use nym_credential_utils::utils::issue_credential;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_network_defaults::NymNetworkDetails;
|
||||
use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient};
|
||||
use zeroize::Zeroizing;
|
||||
@@ -17,6 +18,7 @@ pub struct BandwidthAcquireClient<'a, St: Storage> {
|
||||
client: DirectSigningHttpRpcNyxdClient,
|
||||
storage: &'a St,
|
||||
client_id: Zeroizing<String>,
|
||||
ticketbook_type: TicketType,
|
||||
}
|
||||
|
||||
impl<'a, St> BandwidthAcquireClient<'a, St>
|
||||
@@ -29,6 +31,7 @@ where
|
||||
mnemonic: String,
|
||||
storage: &'a St,
|
||||
client_id: String,
|
||||
ticketbook_type: TicketType,
|
||||
) -> Result<Self> {
|
||||
let nyxd_url = network_details.endpoints[0].nyxd_url.as_str();
|
||||
let config = nyxd::Config::try_from_nym_network_details(&network_details)?;
|
||||
@@ -42,11 +45,18 @@ where
|
||||
client,
|
||||
storage,
|
||||
client_id: client_id.into(),
|
||||
ticketbook_type,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn acquire(&self) -> Result<()> {
|
||||
issue_credential(&self.client, self.storage, self.client_id.as_bytes()).await?;
|
||||
issue_credential(
|
||||
&self.client,
|
||||
self.storage,
|
||||
self.client_id.as_bytes(),
|
||||
self.ticketbook_type,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ use nym_client_core::error::ClientCoreError;
|
||||
use nym_client_core::init::helpers::current_gateways;
|
||||
use nym_client_core::init::setup_gateway;
|
||||
use nym_client_core::init::types::{GatewaySelectionSpecification, GatewaySetup};
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_network_defaults::WG_TUN_DEVICE_IP_ADDRESS;
|
||||
use nym_socks5_client_core::config::Socks5;
|
||||
use nym_task::manager::TaskStatus;
|
||||
@@ -550,10 +551,11 @@ where
|
||||
}
|
||||
|
||||
/// Creates an associated [`BandwidthAcquireClient`] that can be used to acquire bandwidth
|
||||
/// credentials for this client to consume.
|
||||
/// credentials of particular type for this client to consume.
|
||||
pub async fn create_bandwidth_client(
|
||||
&self,
|
||||
mnemonic: String,
|
||||
ticketbook_type: TicketType,
|
||||
) -> Result<BandwidthAcquireClient<S::CredentialStore>> {
|
||||
if !self.config.enabled_credentials_mode {
|
||||
return Err(Error::DisabledCredentialsMode);
|
||||
@@ -574,6 +576,7 @@ where
|
||||
mnemonic,
|
||||
self.storage.credential_store(),
|
||||
client_id,
|
||||
ticketbook_type,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user