nym_credentials/ecash/bandwidth/
issued.rs1use crate::ecash::bandwidth::importable::ImportableTicketBook;
5use crate::ecash::bandwidth::serialiser::VersionedSerialise;
6use crate::ecash::bandwidth::CredentialSpendingData;
7use crate::ecash::utils::ecash_today;
8use crate::error::Error;
9use nym_credentials_interface::{
10 CoinIndexSignature, ExpirationDateSignature, NymPayInfo, PayInfo, SecretKeyUser, TicketType,
11 VerificationKeyAuth, Wallet, WalletSignatures,
12};
13use nym_ecash_time::EcashTime;
14use nym_validator_client::nym_api::EpochId;
15use serde::{Deserialize, Serialize};
16use std::borrow::Borrow;
17use time::Date;
18use zeroize::{Zeroize, ZeroizeOnDrop};
19
20pub const CURRENT_SERIALIZATION_REVISION: u8 = 1;
21
22#[derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
24pub struct IssuedTicketBook {
25 signatures_wallet: WalletSignatures,
27
28 spent_tickets: u64,
30
31 epoch_id: EpochId,
33
34 ecash_secret_key: SecretKeyUser,
36
37 #[zeroize(skip)]
39 expiration_date: Date,
40
41 #[zeroize(skip)]
43 ticketbook_type: TicketType,
44}
45
46impl IssuedTicketBook {
47 pub fn new(
48 wallet: WalletSignatures,
49 epoch_id: EpochId,
50 ecash_secret_key: SecretKeyUser,
51 ticketbook_type: TicketType,
52 expiration_date: Date,
53 ) -> Self {
54 IssuedTicketBook {
55 signatures_wallet: wallet,
56 spent_tickets: 0,
57 epoch_id,
58 ecash_secret_key,
59 expiration_date,
60 ticketbook_type,
61 }
62 }
63
64 pub fn from_parts(
65 signatures_wallet: WalletSignatures,
66 epoch_id: EpochId,
67 ecash_secret_key: SecretKeyUser,
68 ticketbook_type: TicketType,
69 expiration_date: Date,
70 spent_tickets: u64,
71 ) -> Self {
72 IssuedTicketBook {
73 signatures_wallet,
74 spent_tickets,
75 epoch_id,
76 ecash_secret_key,
77 expiration_date,
78 ticketbook_type,
79 }
80 }
81
82 pub fn update_spent_tickets(&mut self, spent_tickets: u64) {
83 self.spent_tickets = spent_tickets
84 }
85
86 pub fn epoch_id(&self) -> EpochId {
87 self.epoch_id
88 }
89
90 pub fn ticketbook_type(&self) -> TicketType {
91 self.ticketbook_type
92 }
93
94 pub fn current_serialization_revision(&self) -> u8 {
95 CURRENT_SERIALIZATION_REVISION
96 }
97
98 pub fn expiration_date(&self) -> Date {
99 self.expiration_date
100 }
101
102 pub fn expired(&self) -> bool {
103 self.expiration_date < ecash_today().date()
104 }
105
106 pub fn global_total_tickets() -> u64 {
107 nym_credentials_interface::ecash_parameters().get_total_coins()
108 }
109
110 pub fn params_total_tickets(&self) -> u64 {
111 Self::global_total_tickets()
112 }
113
114 pub fn spent_tickets(&self) -> u64 {
115 self.spent_tickets
116 }
117
118 pub fn wallet(&self) -> &WalletSignatures {
119 &self.signatures_wallet
120 }
121
122 pub fn generate_pay_info(&self, provider_pk: [u8; 32]) -> NymPayInfo {
123 NymPayInfo::generate(provider_pk)
124 }
125
126 pub fn prepare_for_spending<BI, BE>(
127 &mut self,
128 verification_key: &VerificationKeyAuth,
129 pay_info: PayInfo,
130 coin_indices_signatures: &[BI],
131 expiration_date_signatures: &[BE],
132 tickets_to_spend: u64,
133 ) -> Result<CredentialSpendingData, Error>
134 where
135 BI: Borrow<CoinIndexSignature>,
136 BE: Borrow<ExpirationDateSignature>,
137 {
138 let params = nym_credentials_interface::ecash_parameters();
139 let spend_date = ecash_today();
140
141 Wallet::ensure_allowance(params, self.spent_tickets, tickets_to_spend)?;
143
144 let payment = self.signatures_wallet.spend(
145 params,
146 verification_key,
147 &self.ecash_secret_key,
148 &pay_info,
149 self.spent_tickets,
150 tickets_to_spend,
151 expiration_date_signatures,
152 coin_indices_signatures,
153 spend_date.ecash_unix_timestamp(),
154 )?;
155
156 self.spent_tickets += tickets_to_spend;
157
158 Ok(CredentialSpendingData {
159 payment,
160 pay_info,
161 spend_date: spend_date.ecash_date(),
162 epoch_id: self.epoch_id,
163 })
164 }
165
166 pub fn begin_export(self) -> ImportableTicketBook {
167 self.into()
168 }
169}
170
171impl VersionedSerialise for IssuedTicketBook {
172 const CURRENT_SERIALISATION_REVISION: u8 = 1;
173
174 fn try_unpack(b: &[u8], revision: impl Into<Option<u8>>) -> Result<Self, Error> {
175 let revision = revision
176 .into()
177 .unwrap_or(<Self as VersionedSerialise>::CURRENT_SERIALISATION_REVISION);
178
179 match revision {
180 1 => Self::try_unpack_current(b),
181 _ => Err(Error::UnknownSerializationRevision { revision }),
182 }
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 fn assert_zeroize_on_drop<T: ZeroizeOnDrop>() {}
191
192 fn assert_zeroize<T: Zeroize>() {}
193
194 #[test]
195 fn credential_is_zeroized() {
196 assert_zeroize::<IssuedTicketBook>();
197 assert_zeroize_on_drop::<IssuedTicketBook>();
198 }
199}