Skip to content

Commit a2887f9

Browse files
Merge #89
89: feat: add function to fetch subscription status in nilauth r=mfontanini a=mfontanini This creates a nilauth client function for the new subscription status endpoint NillionNetwork/nilauth#27. I refactored this a bit since it reuses the same request structure as token minting. Co-authored-by: Matias Fontanini <matias.fontanini@gmail.com>
2 parents 6b2918e + 7d8aa9c commit a2887f9

File tree

1 file changed

+94
-24
lines changed

1 file changed

+94
-24
lines changed

libs/nilauth-client/src/client.rs

Lines changed: 94 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ pub trait NilauthClient {
4747
key: &PublicKey,
4848
) -> Result<TxHash, PaySubscriptionError>;
4949

50+
/// Get our subscription status.
51+
async fn subscription_status(&self, key: &SecretKey) -> Result<Subscription, SubscriptionStatusError>;
52+
5053
/// Get the cost of a subscription.
5154
async fn subscription_cost(&self) -> Result<TokenAmount, SubscriptionCostError>;
5255

@@ -66,11 +69,8 @@ pub enum RequestTokenError {
6669
#[error("fetching server's about: {0}")]
6770
About(#[from] AboutError),
6871

69-
#[error("serde: {0}")]
70-
Serde(#[from] serde_json::Error),
71-
72-
#[error("invalid public key")]
73-
InvalidPublicKey,
72+
#[error("signing request: {0}")]
73+
Signing(#[from] SigningError),
7474

7575
#[error("http: {0}")]
7676
Http(#[from] reqwest::Error),
@@ -107,6 +107,22 @@ pub enum PaySubscriptionError {
107107
Request(RequestError),
108108
}
109109

110+
/// An error when fetching the subscription status.
111+
#[derive(Debug, thiserror::Error)]
112+
pub enum SubscriptionStatusError {
113+
#[error("fetching server's about: {0}")]
114+
About(#[from] AboutError),
115+
116+
#[error("http: {0}")]
117+
Http(#[from] reqwest::Error),
118+
119+
#[error("signing request: {0}")]
120+
Signing(#[from] SigningError),
121+
122+
#[error("request: {0:?}")]
123+
Request(RequestError),
124+
}
125+
110126
/// An error when fetching the subscription cost.
111127
#[derive(Debug, thiserror::Error)]
112128
pub enum SubscriptionCostError {
@@ -183,6 +199,7 @@ macro_rules! impl_from_request_error {
183199
impl_from_request_error!(
184200
RequestTokenError,
185201
PaySubscriptionError,
202+
SubscriptionStatusError,
186203
SubscriptionCostError,
187204
RevokeTokenError,
188205
AboutError,
@@ -253,13 +270,7 @@ impl NilauthClient for DefaultNilauthClient {
253270
expires_at: Utc::now() + TOKEN_REQUEST_EXPIRATION,
254271
target_public_key: about.public_key,
255272
};
256-
let payload = serde_json::to_string(&payload)?;
257-
let signature: Signature = SigningKey::from(key).sign(payload.as_bytes());
258-
259-
let public_key =
260-
key.public_key().to_sec1_bytes().as_ref().try_into().map_err(|_| RequestTokenError::InvalidPublicKey)?;
261-
let request =
262-
CreateNucRequest { public_key, signature: signature.to_bytes().into(), payload: payload.into_bytes() };
273+
let request = SignedRequest::new(key, &payload)?;
263274
let url = self.make_url("/api/v1/nucs/create");
264275
let response: Result<CreateNucResponse, RequestTokenError> = self.post(&url, &request).await;
265276
Ok(response?.token)
@@ -299,6 +310,14 @@ impl NilauthClient for DefaultNilauthClient {
299310
Err(PaySubscriptionError::PaymentValidation { tx_hash, payload })
300311
}
301312

313+
async fn subscription_status(&self, key: &SecretKey) -> Result<Subscription, SubscriptionStatusError> {
314+
let payload =
315+
SubscriptionStatusRequest { nonce: rand::random(), expires_at: Utc::now() + TOKEN_REQUEST_EXPIRATION };
316+
let request = SignedRequest::new(key, &payload)?;
317+
let url = self.make_url("/api/v1/subscriptions/status");
318+
self.post(&url, &request).await
319+
}
320+
302321
async fn subscription_cost(&self) -> Result<TokenAmount, SubscriptionCostError> {
303322
let url = self.make_url("/api/v1/payments/cost");
304323
let response: Result<GetCostResponse, SubscriptionCostError> = self.get(&url).await;
@@ -349,35 +368,60 @@ impl Display for TxHash {
349368
#[derive(Clone, Deserialize)]
350369
pub struct About {
351370
/// The server's public key.
352-
#[serde(deserialize_with = "hex::serde::deserialize")]
371+
#[serde(with = "hex::serde")]
353372
pub public_key: [u8; 33],
354373
}
355374

356375
#[derive(Serialize)]
357-
struct CreateNucRequest {
358-
#[serde(serialize_with = "hex::serde::serialize")]
376+
struct SignedRequest {
377+
#[serde(with = "hex::serde")]
359378
public_key: [u8; 33],
360379

361-
#[serde(serialize_with = "hex::serde::serialize")]
380+
#[serde(with = "hex::serde")]
362381
signature: [u8; 64],
363382

364-
#[serde(serialize_with = "hex::serde::serialize")]
383+
#[serde(with = "hex::serde")]
365384
payload: Vec<u8>,
366385
}
367386

387+
impl SignedRequest {
388+
fn new<T>(key: &SecretKey, payload: &T) -> Result<Self, SigningError>
389+
where
390+
T: Serialize,
391+
{
392+
let payload = serde_json::to_string(&payload)?;
393+
let signature: Signature = SigningKey::from(key).sign(payload.as_bytes());
394+
395+
let public_key =
396+
key.public_key().to_sec1_bytes().as_ref().try_into().map_err(|_| SigningError::InvalidPublicKey)?;
397+
let request = Self { public_key, signature: signature.to_bytes().into(), payload: payload.into_bytes() };
398+
Ok(request)
399+
}
400+
}
401+
402+
/// An error when signing a request.
403+
#[derive(Debug, thiserror::Error)]
404+
pub enum SigningError {
405+
#[error("payload serialization: {0}")]
406+
PayloadSerde(#[from] serde_json::Error),
407+
408+
#[error("invalid public key")]
409+
InvalidPublicKey,
410+
}
411+
368412
#[derive(Serialize)]
369413
struct CreateNucRequestPayload {
370414
// A nonce, to add entropy.
371-
#[serde(serialize_with = "hex::serde::serialize")]
415+
#[serde(with = "hex::serde")]
372416
nonce: [u8; 16],
373417

374418
// When this payload is no longer considered valid, to prevent reusing this forever if it
375419
// leaks.
376-
#[serde(serialize_with = "chrono::serde::ts_seconds::serialize")]
420+
#[serde(with = "chrono::serde::ts_seconds")]
377421
expires_at: DateTime<Utc>,
378422

379423
// Our public key, to ensure this request can't be redirected to another authority service.
380-
#[serde(serialize_with = "hex::serde::serialize")]
424+
#[serde(with = "hex::serde")]
381425
target_public_key: [u8; 33],
382426
}
383427

@@ -390,20 +434,20 @@ struct CreateNucResponse {
390434
struct ValidatePaymentRequest {
391435
tx_hash: String,
392436

393-
#[serde(serialize_with = "hex::serde::serialize")]
437+
#[serde(with = "hex::serde")]
394438
payload: Vec<u8>,
395439

396-
#[serde(serialize_with = "hex::serde::serialize")]
440+
#[serde(with = "hex::serde")]
397441
public_key: [u8; 33],
398442
}
399443

400444
#[derive(Serialize)]
401445
struct ValidatePaymentRequestPayload {
402446
#[allow(dead_code)]
403-
#[serde(serialize_with = "hex::serde::serialize")]
447+
#[serde(with = "hex::serde")]
404448
nonce: [u8; 16],
405449

406-
#[serde(serialize_with = "hex::serde::serialize")]
450+
#[serde(with = "hex::serde")]
407451
service_public_key: [u8; 33],
408452
}
409453

@@ -442,3 +486,29 @@ pub struct RequestError {
442486
/// The error code.
443487
pub error_code: String,
444488
}
489+
490+
#[derive(Serialize)]
491+
struct SubscriptionStatusRequest {
492+
#[serde(with = "hex::serde")]
493+
nonce: [u8; 16],
494+
495+
#[serde(with = "chrono::serde::ts_seconds")]
496+
expires_at: DateTime<Utc>,
497+
}
498+
499+
#[derive(Deserialize)]
500+
pub struct Subscription {
501+
/// Whether the user is actively subscribed.
502+
pub subscribed: bool,
503+
504+
/// The details about the subscription.
505+
pub details: Option<SubscriptionDetails>,
506+
}
507+
508+
/// The subscription information.
509+
#[derive(Deserialize)]
510+
pub struct SubscriptionDetails {
511+
/// The timestamp at which the subscription expires.
512+
#[serde(with = "chrono::serde::ts_seconds")]
513+
pub expires_at: DateTime<Utc>,
514+
}

0 commit comments

Comments
 (0)