Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/target

CLAUDE.md
AGENTS.md
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions client-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,6 @@ futures = "0.3.31"
csv = "1.3.1"
gloo-timers = { version = "0.3.0", features = ["futures"] }
coins-bip39 = "0.12.0"

[target.'cfg(target_arch = "wasm32")'.dependencies]
instant = { version = "0.1.13", features = ["wasm-bindgen"] }
87 changes: 45 additions & 42 deletions client-sdk/src/external_api/s3_store_vault.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::utils::query::post_request;
use crate::external_api::utils::{
query::{build_client, REQWEST_TIMEOUT},
rate_limit::{limiter_from_env, RequestRateLimiter},
retry::with_retry,
};
use async_trait::async_trait;
Expand All @@ -26,23 +27,50 @@ use intmax2_interfaces::{
};
use intmax2_zkp::ethereum_types::bytes32::Bytes32;
use reqwest::Client;
use serde::{de::DeserializeOwned, Serialize};
use std::sync::Arc;

const TIME_TO_EXPIRY: u64 = 60; // 1 minute for normal requests
const TIME_TO_EXPIRY_READONLY: u64 = 60 * 60 * 24; // 24 hours for readonly
const STORE_VAULT_DEFAULT_RPS: f64 = 1.0;
const STORE_VAULT_DEFAULT_BURST_MULTIPLIER: u32 = 2;

#[derive(Debug, Clone)]
pub struct S3StoreVaultClient {
client: Client,
base_url: String,
rate_limiter: Arc<RequestRateLimiter>,
}

impl S3StoreVaultClient {
pub fn new(base_url: &str) -> Self {
S3StoreVaultClient {
client: build_client(),
base_url: base_url.to_string(),
rate_limiter: limiter_from_env(
"STORE_VAULT_MAX_RPS",
"STORE_VAULT_MAX_BURST",
STORE_VAULT_DEFAULT_RPS,
STORE_VAULT_DEFAULT_BURST_MULTIPLIER,
),
}
}

async fn post_with_limit<B, R>(
&self,
endpoint: &str,
body: Option<&B>,
) -> Result<R, ServerError>
where
B: Serialize,
R: DeserializeOwned,
{
self.rate_limiter
.run(1, || async {
post_request(&self.client, &self.base_url, endpoint, body).await
})
.await
}
}

#[async_trait(?Send)]
Expand All @@ -62,13 +90,9 @@ impl StoreVaultClientInterface for S3StoreVaultClient {
digest,
};
let request_with_auth = request.sign(view_key, TIME_TO_EXPIRY);
let response: S3PreSaveSnapshotResponse = post_request(
&self.client,
&self.base_url,
"/pre-save-snapshot",
Some(&request_with_auth),
)
.await?;
let response: S3PreSaveSnapshotResponse = self
.post_with_limit("/pre-save-snapshot", Some(&request_with_auth))
.await?;

// upload data to s3
upload_s3(&self.client, &response.presigned_url, data).await?;
Expand All @@ -81,13 +105,8 @@ impl StoreVaultClientInterface for S3StoreVaultClient {
digest,
};
let request_with_auth = request.sign(view_key, TIME_TO_EXPIRY);
let () = post_request(
&self.client,
&self.base_url,
"/save-snapshot",
Some(&request_with_auth),
)
.await?;
self.post_with_limit::<_, ()>("/save-snapshot", Some(&request_with_auth))
.await?;

Ok(())
}
Expand All @@ -103,13 +122,9 @@ impl StoreVaultClientInterface for S3StoreVaultClient {
pubkey: key.pubkey,
};
let request_with_auth = request.sign(view_key, TIME_TO_EXPIRY);
let response: S3GetSnapshotResponse = post_request(
&self.client,
&self.base_url,
"/get-snapshot",
Some(&request_with_auth),
)
.await?;
let response: S3GetSnapshotResponse = self
.post_with_limit("/get-snapshot", Some(&request_with_auth))
.await?;

match response.presigned_url {
Some(url) => {
Expand Down Expand Up @@ -139,13 +154,9 @@ impl StoreVaultClientInterface for S3StoreVaultClient {
let digests = data.iter().map(|entry| entry.digest).collect::<Vec<_>>();
let request = S3SaveDataBatchRequest { data };
let request_with_auth = request.sign(view_key, TIME_TO_EXPIRY);
let response: S3SaveDataBatchResponse = post_request(
&self.client,
&self.base_url,
"/save-data-batch",
Some(&request_with_auth),
)
.await?;
let response: S3SaveDataBatchResponse = self
.post_with_limit("/save-data-batch", Some(&request_with_auth))
.await?;

let data = chunk
.iter()
Expand Down Expand Up @@ -173,13 +184,9 @@ impl StoreVaultClientInterface for S3StoreVaultClient {
pubkey: key.pubkey,
};
let request_with_auth = request.sign(view_key, TIME_TO_EXPIRY);
let response: S3GetDataBatchResponse = post_request(
&self.client,
&self.base_url,
"/get-data-batch",
Some(&request_with_auth),
)
.await?;
let response: S3GetDataBatchResponse = self
.post_with_limit("/get-data-batch", Some(&request_with_auth))
.await?;
let urls = response
.presigned_urls_with_meta
.iter()
Expand Down Expand Up @@ -239,13 +246,9 @@ impl StoreVaultClientInterface for S3StoreVaultClient {
},
auth: auth.clone(),
};
let response: S3GetDataSequenceResponse = post_request(
&self.client,
&self.base_url,
"/get-data-sequence",
Some(&request_with_auth),
)
.await?;
let response: S3GetDataSequenceResponse = self
.post_with_limit("/get-data-sequence", Some(&request_with_auth))
.await?;

let urls = response
.presigned_urls_with_meta
Expand Down
81 changes: 45 additions & 36 deletions client-sdk/src/external_api/store_vault_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,57 @@ use intmax2_interfaces::{
};
use intmax2_zkp::ethereum_types::bytes32::Bytes32;
use reqwest::Client;
use serde::{de::DeserializeOwned, Serialize};
use std::sync::Arc;

use crate::external_api::utils::query::build_client;
use crate::external_api::utils::{
query::build_client,
rate_limit::{limiter_from_env, RequestRateLimiter},
};

use super::utils::query::post_request;

const TIME_TO_EXPIRY: u64 = 60; // 1 minute for normal requests
const TIME_TO_EXPIRY_READONLY: u64 = 60 * 60 * 24; // 24 hours for readonly
const STORE_VAULT_DEFAULT_RPS: f64 = 1.0;
const STORE_VAULT_DEFAULT_BURST_MULTIPLIER: u32 = 2;

#[derive(Debug, Clone)]
pub struct StoreVaultServerClient {
client: Client,
base_url: String,
rate_limiter: Arc<RequestRateLimiter>,
}

impl StoreVaultServerClient {
pub fn new(base_url: &str) -> Self {
StoreVaultServerClient {
client: build_client(),
base_url: base_url.to_string(),
rate_limiter: limiter_from_env(
"STORE_VAULT_MAX_RPS",
"STORE_VAULT_MAX_BURST",
STORE_VAULT_DEFAULT_RPS,
STORE_VAULT_DEFAULT_BURST_MULTIPLIER,
),
}
}

async fn post_with_limit<B, R>(
&self,
endpoint: &str,
body: Option<&B>,
) -> Result<R, ServerError>
where
B: Serialize,
R: DeserializeOwned,
{
self.rate_limiter
.run(1, || async {
post_request(&self.client, &self.base_url, endpoint, body).await
})
.await
}
}

#[async_trait(?Send)]
Expand All @@ -59,13 +89,8 @@ impl StoreVaultClientInterface for StoreVaultServerClient {
prev_digest,
};
let request_with_auth = request.sign(view_key, TIME_TO_EXPIRY);
post_request::<_, ()>(
&self.client,
&self.base_url,
"/save-snapshot",
Some(&request_with_auth),
)
.await?;
self.post_with_limit::<_, ()>("/save-snapshot", Some(&request_with_auth))
.await?;
Ok(())
}

Expand All @@ -80,13 +105,9 @@ impl StoreVaultClientInterface for StoreVaultServerClient {
pubkey: key.pubkey,
};
let request_with_auth = request.sign(view_key, TIME_TO_EXPIRY);
let response: GetSnapshotResponse = post_request(
&self.client,
&self.base_url,
"/get-snapshot",
Some(&request_with_auth),
)
.await?;
let response: GetSnapshotResponse = self
.post_with_limit("/get-snapshot", Some(&request_with_auth))
.await?;
Ok(response.data)
}

Expand All @@ -102,13 +123,9 @@ impl StoreVaultClientInterface for StoreVaultServerClient {
data: chunk.to_vec(),
};
let request_with_auth = request.sign(view_key, TIME_TO_EXPIRY);
let response: SaveDataBatchResponse = post_request(
&self.client,
&self.base_url,
"/save-data-batch",
Some(&request_with_auth),
)
.await?;
let response: SaveDataBatchResponse = self
.post_with_limit("/save-data-batch", Some(&request_with_auth))
.await?;
all_digests.extend(response.digests);
}
Ok(all_digests)
Expand All @@ -129,13 +146,9 @@ impl StoreVaultClientInterface for StoreVaultServerClient {
pubkey: key.pubkey,
};
let request_with_auth = request.sign(view_key, TIME_TO_EXPIRY);
let response: GetDataBatchResponse = post_request(
&self.client,
&self.base_url,
"/get-data-batch",
Some(&request_with_auth),
)
.await?;
let response: GetDataBatchResponse = self
.post_with_limit("/get-data-batch", Some(&request_with_auth))
.await?;
all_data.extend(response.data);
}
Ok(all_data)
Expand Down Expand Up @@ -177,13 +190,9 @@ impl StoreVaultClientInterface for StoreVaultServerClient {
},
auth: auth.clone(),
};
let response: GetDataSequenceResponse = post_request(
&self.client,
&self.base_url,
"/get-data-sequence",
Some(&request_with_auth),
)
.await?;
let response: GetDataSequenceResponse = self
.post_with_limit("/get-data-sequence", Some(&request_with_auth))
.await?;
Ok((response.data, response.cursor_response))
}
}
Expand Down
1 change: 1 addition & 0 deletions client-sdk/src/external_api/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod query;
pub mod rate_limit;
pub mod retry;
pub mod time;
Loading