nym_credential_proxy_lib/shared_state/
mod.rs1use crate::deposits_buffer::{BufferedDeposit, DepositsBuffer};
5use crate::error::CredentialProxyError;
6use crate::shared_state::ecash_state::EcashState;
7use crate::shared_state::nyxd_client::ChainClient;
8use crate::storage::CredentialProxyStorage;
9use nym_compact_ecash::{Base58, PublicKeyUser, VerificationKeyAuth};
10use nym_credential_proxy_requests::api::v1::ticketbook::models::{
11 AggregatedCoinIndicesSignaturesResponse, AggregatedExpirationDateSignaturesResponse,
12 GlobalDataParams, MasterVerificationKeyResponse,
13};
14use nym_credentials::{AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures};
15use nym_ecash_contract_common::deposit::DepositId;
16use nym_validator_client::EcashApiClient;
17use nym_validator_client::nym_api::EpochId;
18use nym_validator_client::nyxd::Coin;
19use nym_validator_client::nyxd::contract_traits::dkg_query_client::Epoch;
20use std::sync::Arc;
21use std::time::Duration;
22use time::{Date, OffsetDateTime};
23use tokio::sync::RwLockReadGuard;
24use tokio::time::Instant;
25use tracing::{debug, error, warn};
26use uuid::Uuid;
27
28pub mod ecash_state;
29pub mod nyxd_client;
30pub mod required_deposit_cache;
31
32#[derive(Clone)]
33pub struct CredentialProxyState {
34 inner: Arc<CredentialProxyStateInner>,
35}
36
37impl CredentialProxyState {
38 pub fn new(
39 storage: CredentialProxyStorage,
40 client: ChainClient,
41 deposits_buffer: DepositsBuffer,
42 ecash_state: EcashState,
43 ) -> Self {
44 CredentialProxyState {
45 inner: Arc::new(CredentialProxyStateInner {
46 storage,
47 client,
48 deposits_buffer,
49 ecash_state,
50 }),
51 }
52 }
53
54 pub fn storage(&self) -> &CredentialProxyStorage {
55 &self.inner.storage
56 }
57
58 pub async fn deposit_amount(&self) -> Result<Coin, CredentialProxyError> {
59 self.ecash_state().deposit_amount(self.client()).await
60 }
61
62 pub fn client(&self) -> &ChainClient {
63 &self.inner.client
64 }
65
66 pub fn deposits_buffer(&self) -> &DepositsBuffer {
67 &self.inner.deposits_buffer
68 }
69
70 pub fn ecash_state(&self) -> &EcashState {
71 &self.inner.ecash_state
72 }
73
74 pub async fn ensure_credentials_issuable(&self) -> Result<(), CredentialProxyError> {
75 self.ecash_state()
76 .ensure_credentials_issuable(self.client())
77 .await
78 }
79
80 pub async fn get_deposit(
81 &self,
82 request_uuid: Uuid,
83 requested_on: OffsetDateTime,
84 client_pubkey: PublicKeyUser,
85 ) -> Result<BufferedDeposit, CredentialProxyError> {
86 let start = Instant::now();
87 let deposit = self
88 .deposits_buffer()
89 .get_valid_deposit(request_uuid, requested_on, client_pubkey)
90 .await;
91
92 let time_taken = start.elapsed();
93 let formatted = humantime::format_duration(time_taken);
94 if time_taken > Duration::from_secs(10) {
95 warn!(
96 "attempting to get buffered deposit took {formatted}. perhaps the buffer is too small or the process/chain is overloaded?"
97 )
98 } else {
99 debug!("attempting to get buffered deposit took {formatted}")
100 };
101
102 deposit
103 }
104
105 pub async fn insert_deposit_usage_error(&self, deposit_id: DepositId, error: String) {
106 if let Err(err) = self
107 .storage()
108 .insert_deposit_usage_error(deposit_id, error)
109 .await
110 {
111 error!(
112 "failed to insert information about deposit (id: {deposit_id}) usage failure: {err}"
113 )
114 }
115 }
116
117 pub async fn current_epoch_id(&self) -> Result<EpochId, CredentialProxyError> {
118 self.ecash_state().current_epoch_id(self.client()).await
119 }
120
121 pub async fn current_epoch(&self) -> Result<Epoch, CredentialProxyError> {
122 self.ecash_state().current_epoch(self.client()).await
123 }
124
125 pub async fn global_data(
126 &self,
127 global_data: GlobalDataParams,
128 epoch_id: EpochId,
129 expiration_date: Date,
130 ) -> Result<
131 (
132 Option<MasterVerificationKeyResponse>,
133 Option<AggregatedExpirationDateSignaturesResponse>,
134 Option<AggregatedCoinIndicesSignaturesResponse>,
135 ),
136 CredentialProxyError,
137 > {
138 let master_verification_key = if global_data.include_master_verification_key {
139 debug!("including master verification key in the response");
140 Some(
141 self.master_verification_key(Some(epoch_id))
142 .await
143 .map(|key| MasterVerificationKeyResponse {
144 epoch_id,
145 bs58_encoded_key: key.to_bs58(),
146 })
147 .inspect_err(|err| warn!("request failure: {err}"))?,
148 )
149 } else {
150 None
151 };
152
153 let aggregated_expiration_date_signatures =
154 if global_data.include_expiration_date_signatures {
155 debug!("including expiration date signatures in the response");
156 Some(
157 self.master_expiration_date_signatures(epoch_id, expiration_date)
158 .await
159 .map(|signatures| AggregatedExpirationDateSignaturesResponse {
160 signatures: signatures.clone(),
161 })
162 .inspect_err(|err| warn!("request failure: {err}"))?,
163 )
164 } else {
165 None
166 };
167
168 let aggregated_coin_index_signatures = if global_data.include_coin_index_signatures {
169 debug!("including coin index signatures in the response");
170 Some(
171 self.master_coin_index_signatures(Some(epoch_id))
172 .await
173 .map(|signatures| AggregatedCoinIndicesSignaturesResponse {
174 signatures: signatures.clone(),
175 })
176 .inspect_err(|err| warn!("request failure: {err}"))?,
177 )
178 } else {
179 None
180 };
181
182 Ok((
183 master_verification_key,
184 aggregated_expiration_date_signatures,
185 aggregated_coin_index_signatures,
186 ))
187 }
188
189 pub async fn master_verification_key(
190 &self,
191 epoch_id: Option<EpochId>,
192 ) -> Result<RwLockReadGuard<'_, VerificationKeyAuth>, CredentialProxyError> {
193 self.ecash_state()
194 .master_verification_key(self.client(), self.storage(), epoch_id)
195 .await
196 }
197
198 pub async fn master_coin_index_signatures(
199 &self,
200 epoch_id: Option<EpochId>,
201 ) -> Result<RwLockReadGuard<'_, AggregatedCoinIndicesSignatures>, CredentialProxyError> {
202 self.ecash_state()
203 .master_coin_index_signatures(self.client(), self.storage(), epoch_id)
204 .await
205 }
206
207 pub async fn master_expiration_date_signatures(
208 &self,
209 epoch_id: EpochId,
210 expiration_date: Date,
211 ) -> Result<RwLockReadGuard<'_, AggregatedExpirationDateSignatures>, CredentialProxyError> {
212 self.ecash_state()
213 .master_expiration_date_signatures(
214 self.client(),
215 self.storage(),
216 epoch_id,
217 expiration_date,
218 )
219 .await
220 }
221
222 pub async fn ecash_clients(
223 &self,
224 epoch_id: EpochId,
225 ) -> Result<RwLockReadGuard<'_, Vec<EcashApiClient>>, CredentialProxyError> {
226 self.ecash_state()
227 .ecash_clients(self.client(), epoch_id)
228 .await
229 }
230
231 pub async fn ecash_threshold(&self, epoch_id: EpochId) -> Result<u64, CredentialProxyError> {
232 self.ecash_state()
233 .ecash_threshold(self.client(), epoch_id)
234 .await
235 }
236}
237
238struct CredentialProxyStateInner {
239 storage: CredentialProxyStorage,
240
241 client: ChainClient,
242
243 deposits_buffer: DepositsBuffer,
244
245 ecash_state: EcashState,
246}