propagated new ticket type through the whole stack

This commit is contained in:
Jędrzej Stuczyński
2024-07-25 17:32:03 +01:00
parent aea962b546
commit 4c10cebf1b
52 changed files with 547 additions and 255 deletions
Generated
+7 -1
View File
@@ -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",
+1 -1
View File
@@ -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" }
+1 -1
View File
@@ -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,
))
}
+2
View File
@@ -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;
+3
View File
@@ -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
View File
@@ -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
"#,
)
+3
View File
@@ -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,
+1 -1
View File
@@ -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" }
+12 -2
View File
@@ -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(())
}
+3 -1
View File
@@ -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" }
+80 -4
View File
@@ -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
}
+1 -17
View File
@@ -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
+12 -10
View File
@@ -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;
+1 -1
View File
@@ -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()
}
+21 -12
View File
@@ -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,
)
}
}
+4 -4
View File
@@ -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
+2
View File
@@ -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 {
+4
View File
@@ -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 {
+9 -3
View File
@@ -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(),
)?)
}
+4 -2
View File
@@ -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?;
+65 -39
View File
@@ -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> {
+22 -18
View File
@@ -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?)
}
+40 -35
View File
@@ -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(),
+9 -3
View File
@@ -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 {
+5
View File
@@ -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"] }
+1 -2
View File
@@ -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
+2
View File
@@ -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",
]
+2 -1
View File
@@ -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" }
+3 -1
View File
@@ -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?;
+1 -1
View File
@@ -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();
+11 -1
View File
@@ -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(())
}
}
+4 -1
View File
@@ -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,
)
}