create-sia-app 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.js +179 -0
- package/package.json +29 -0
- package/template/CLAUDE.md +160 -0
- package/template/README.md +102 -0
- package/template/_gitignore +5 -0
- package/template/biome.json +40 -0
- package/template/index.html +13 -0
- package/template/package.json +30 -0
- package/template/rust/README.md +16 -0
- package/template/rust/sia-sdk-rs/.changeset/added_cancel_function_to_cancel_inflight_packed_uploads.md +6 -0
- package/template/rust/sia-sdk-rs/.changeset/check_if_we_have_enough_hosts_prior_to_encoding_in_upload_slabs.md +16 -0
- package/template/rust/sia-sdk-rs/.changeset/fix_slab_length_in_packed_object.md +5 -0
- package/template/rust/sia-sdk-rs/.changeset/fix_upload_racing_race_conditon.md +13 -0
- package/template/rust/sia-sdk-rs/.changeset/improved_parallelism_of_packed_uploads.md +5 -0
- package/template/rust/sia-sdk-rs/.changeset/progress_callback_will_now_be_called_as_expected_for_packed_uploads.md +5 -0
- package/template/rust/sia-sdk-rs/.github/dependabot.yml +10 -0
- package/template/rust/sia-sdk-rs/.github/workflows/main.yml +36 -0
- package/template/rust/sia-sdk-rs/.github/workflows/prepare-release.yml +34 -0
- package/template/rust/sia-sdk-rs/.github/workflows/release.yml +30 -0
- package/template/rust/sia-sdk-rs/.rustfmt.toml +4 -0
- package/template/rust/sia-sdk-rs/Cargo.lock +4127 -0
- package/template/rust/sia-sdk-rs/Cargo.toml +3 -0
- package/template/rust/sia-sdk-rs/LICENSE +21 -0
- package/template/rust/sia-sdk-rs/README.md +30 -0
- package/template/rust/sia-sdk-rs/indexd/CHANGELOG.md +79 -0
- package/template/rust/sia-sdk-rs/indexd/Cargo.toml +79 -0
- package/template/rust/sia-sdk-rs/indexd/benches/upload.rs +258 -0
- package/template/rust/sia-sdk-rs/indexd/src/app_client.rs +1710 -0
- package/template/rust/sia-sdk-rs/indexd/src/builder.rs +354 -0
- package/template/rust/sia-sdk-rs/indexd/src/download.rs +379 -0
- package/template/rust/sia-sdk-rs/indexd/src/hosts.rs +659 -0
- package/template/rust/sia-sdk-rs/indexd/src/lib.rs +827 -0
- package/template/rust/sia-sdk-rs/indexd/src/mock.rs +162 -0
- package/template/rust/sia-sdk-rs/indexd/src/object_encryption.rs +125 -0
- package/template/rust/sia-sdk-rs/indexd/src/quic.rs +575 -0
- package/template/rust/sia-sdk-rs/indexd/src/rhp4.rs +52 -0
- package/template/rust/sia-sdk-rs/indexd/src/slabs.rs +497 -0
- package/template/rust/sia-sdk-rs/indexd/src/upload.rs +629 -0
- package/template/rust/sia-sdk-rs/indexd/src/wasm_time.rs +41 -0
- package/template/rust/sia-sdk-rs/indexd/src/web_transport.rs +398 -0
- package/template/rust/sia-sdk-rs/indexd_ffi/CHANGELOG.md +76 -0
- package/template/rust/sia-sdk-rs/indexd_ffi/Cargo.toml +47 -0
- package/template/rust/sia-sdk-rs/indexd_ffi/examples/python/README.md +10 -0
- package/template/rust/sia-sdk-rs/indexd_ffi/examples/python/example.py +130 -0
- package/template/rust/sia-sdk-rs/indexd_ffi/src/bin/uniffi-bindgen.rs +3 -0
- package/template/rust/sia-sdk-rs/indexd_ffi/src/builder.rs +377 -0
- package/template/rust/sia-sdk-rs/indexd_ffi/src/io.rs +155 -0
- package/template/rust/sia-sdk-rs/indexd_ffi/src/lib.rs +1039 -0
- package/template/rust/sia-sdk-rs/indexd_ffi/src/logging.rs +58 -0
- package/template/rust/sia-sdk-rs/indexd_ffi/src/tls.rs +23 -0
- package/template/rust/sia-sdk-rs/indexd_wasm/Cargo.toml +33 -0
- package/template/rust/sia-sdk-rs/indexd_wasm/src/lib.rs +818 -0
- package/template/rust/sia-sdk-rs/knope.toml +54 -0
- package/template/rust/sia-sdk-rs/sia_derive/CHANGELOG.md +38 -0
- package/template/rust/sia-sdk-rs/sia_derive/Cargo.toml +19 -0
- package/template/rust/sia-sdk-rs/sia_derive/src/lib.rs +278 -0
- package/template/rust/sia-sdk-rs/sia_sdk/CHANGELOG.md +91 -0
- package/template/rust/sia-sdk-rs/sia_sdk/Cargo.toml +59 -0
- package/template/rust/sia-sdk-rs/sia_sdk/benches/merkle_root.rs +12 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/blake2.rs +22 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/consensus.rs +767 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/encoding/v1.rs +257 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/encoding/v2.rs +291 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/encoding.rs +26 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/encoding_async/v2.rs +367 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/encoding_async.rs +6 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/encryption.rs +303 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/erasure_coding.rs +347 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/lib.rs +15 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/macros.rs +435 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/merkle.rs +112 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/rhp/merkle.rs +357 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/rhp/rpc.rs +1507 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/rhp/types.rs +146 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/rhp.rs +7 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/seed.rs +278 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/signing.rs +236 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/common.rs +677 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/currency.rs +450 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/specifier.rs +110 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/spendpolicy.rs +778 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/utils.rs +117 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/v1.rs +1737 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/v2.rs +1726 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/work.rs +59 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/types.rs +16 -0
- package/template/scripts/setup-rust.js +29 -0
- package/template/src/App.tsx +13 -0
- package/template/src/components/DevNote.tsx +21 -0
- package/template/src/components/auth/ApproveScreen.tsx +84 -0
- package/template/src/components/auth/AuthFlow.tsx +77 -0
- package/template/src/components/auth/ConnectScreen.tsx +214 -0
- package/template/src/components/auth/LoadingScreen.tsx +8 -0
- package/template/src/components/auth/RecoveryScreen.tsx +182 -0
- package/template/src/components/upload/UploadZone.tsx +314 -0
- package/template/src/index.css +9 -0
- package/template/src/lib/constants.ts +8 -0
- package/template/src/lib/format.ts +35 -0
- package/template/src/lib/hex.ts +13 -0
- package/template/src/lib/sdk.ts +25 -0
- package/template/src/lib/wasm-env.ts +5 -0
- package/template/src/main.tsx +12 -0
- package/template/src/stores/auth.ts +86 -0
- package/template/tsconfig.app.json +31 -0
- package/template/tsconfig.json +7 -0
- package/template/tsconfig.node.json +26 -0
- package/template/vite.config.ts +18 -0
- package/template/wasm/indexd_wasm/indexd_wasm.d.ts +309 -0
- package/template/wasm/indexd_wasm/indexd_wasm.js +1507 -0
- package/template/wasm/indexd_wasm/indexd_wasm_bg.wasm +0 -0
- package/template/wasm/indexd_wasm/package.json +31 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
use std::sync::Arc;
|
|
2
|
+
use std::time::Duration;
|
|
3
|
+
|
|
4
|
+
use chrono::{DateTime, Utc};
|
|
5
|
+
use log::debug;
|
|
6
|
+
use reqwest::IntoUrl;
|
|
7
|
+
use sia::seed::{self, Seed};
|
|
8
|
+
use sia::signing::PrivateKey;
|
|
9
|
+
use sia::types::Hash256;
|
|
10
|
+
use thiserror::Error;
|
|
11
|
+
use url::Url;
|
|
12
|
+
|
|
13
|
+
use crate::app_client::{self, AppClient, Client, RegisterAppRequest, RegisterAppResponse};
|
|
14
|
+
use crate::object_encryption::derive;
|
|
15
|
+
use crate::SDK;
|
|
16
|
+
|
|
17
|
+
#[cfg(not(target_arch = "wasm32"))]
|
|
18
|
+
use crate::quic;
|
|
19
|
+
|
|
20
|
+
/// Configuration for concurrency limits (WASM only).
|
|
21
|
+
#[cfg(target_arch = "wasm32")]
|
|
22
|
+
#[derive(Debug, Clone, Copy)]
|
|
23
|
+
pub struct ConcurrencyConfig {
|
|
24
|
+
pub max_price_fetches: usize,
|
|
25
|
+
pub max_downloads: usize,
|
|
26
|
+
pub max_uploads: usize,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
#[cfg(target_arch = "wasm32")]
|
|
30
|
+
impl Default for ConcurrencyConfig {
|
|
31
|
+
fn default() -> Self {
|
|
32
|
+
Self {
|
|
33
|
+
max_price_fetches: 10,
|
|
34
|
+
max_downloads: 20,
|
|
35
|
+
max_uploads: 16,
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
#[cfg(not(target_arch = "wasm32"))]
|
|
41
|
+
use tokio::time::sleep;
|
|
42
|
+
|
|
43
|
+
#[cfg(target_arch = "wasm32")]
|
|
44
|
+
use crate::wasm_time::sleep;
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
/// The initial state of the SDK builder, before connecting to the indexd service.
|
|
48
|
+
pub struct DisconnectedState;
|
|
49
|
+
|
|
50
|
+
/// The state of the SDK builder after requesting approval for the application.
|
|
51
|
+
pub struct RequestingApprovalState {
|
|
52
|
+
app_id: Hash256,
|
|
53
|
+
response_url: Url,
|
|
54
|
+
register_url: Url,
|
|
55
|
+
status_url: Url,
|
|
56
|
+
expiration: DateTime<Utc>,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/// The state of the SDK builder after the application has been approved.
|
|
60
|
+
pub struct ApprovedState {
|
|
61
|
+
app_id: Hash256,
|
|
62
|
+
register_url: Url,
|
|
63
|
+
user_secret: Hash256,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// A builder for creating an SDK instance.
|
|
67
|
+
pub struct Builder<S> {
|
|
68
|
+
state: S,
|
|
69
|
+
client: Arc<dyn AppClient>,
|
|
70
|
+
// FIXME this can be removed if neccesary
|
|
71
|
+
// this is needed to allow the webapp user to set their own
|
|
72
|
+
// concurrency settings. Now being used to determine reasonable settings.
|
|
73
|
+
#[cfg(target_arch = "wasm32")]
|
|
74
|
+
concurrency: ConcurrencyConfig,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/// Errors that can occur during the SDK building process.
|
|
78
|
+
#[derive(Error, Debug)]
|
|
79
|
+
pub enum BuilderError {
|
|
80
|
+
#[error("url error: {0}")]
|
|
81
|
+
Url(#[from] url::ParseError),
|
|
82
|
+
|
|
83
|
+
#[error("client error: {0}")]
|
|
84
|
+
Client(#[from] app_client::Error),
|
|
85
|
+
|
|
86
|
+
#[cfg(not(target_arch = "wasm32"))]
|
|
87
|
+
#[error("quic error: {0}")]
|
|
88
|
+
QUIC(#[from] quic::ConnectError),
|
|
89
|
+
|
|
90
|
+
#[error("mnemonic error: {0}")]
|
|
91
|
+
Mnemonic(#[from] seed::SeedError),
|
|
92
|
+
|
|
93
|
+
#[error("request expired")]
|
|
94
|
+
RequestExpired,
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
impl Builder<DisconnectedState> {
|
|
98
|
+
/// Creates a new SDK builder with the provided indexer URL.
|
|
99
|
+
///
|
|
100
|
+
/// After creating the builder, call [Builder::connected] to attempt
|
|
101
|
+
/// to connect using an existing app key, or [Builder::request_connection]
|
|
102
|
+
/// to request a new connection.
|
|
103
|
+
pub fn new<U: IntoUrl>(indexer_url: U) -> Result<Self, BuilderError> {
|
|
104
|
+
debug!(
|
|
105
|
+
"Creating SDK builder for indexer at {}",
|
|
106
|
+
indexer_url.as_str()
|
|
107
|
+
);
|
|
108
|
+
let client = Client::new(indexer_url)?;
|
|
109
|
+
Ok(Self {
|
|
110
|
+
state: DisconnectedState,
|
|
111
|
+
client: Arc::new(client),
|
|
112
|
+
#[cfg(target_arch = "wasm32")]
|
|
113
|
+
concurrency: ConcurrencyConfig::default(),
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/// Sets the concurrency configuration (WASM only).
|
|
118
|
+
#[cfg(target_arch = "wasm32")]
|
|
119
|
+
pub fn with_concurrency(mut self, config: ConcurrencyConfig) -> Self {
|
|
120
|
+
self.concurrency = config;
|
|
121
|
+
self
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/// Sets the maximum number of concurrent price fetches (WASM only).
|
|
125
|
+
#[cfg(target_arch = "wasm32")]
|
|
126
|
+
pub fn with_max_price_fetches(mut self, max: usize) -> Self {
|
|
127
|
+
self.concurrency.max_price_fetches = max;
|
|
128
|
+
self
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/// Sets the maximum number of concurrent downloads (WASM only).
|
|
132
|
+
#[cfg(target_arch = "wasm32")]
|
|
133
|
+
pub fn with_max_downloads(mut self, max: usize) -> Self {
|
|
134
|
+
self.concurrency.max_downloads = max;
|
|
135
|
+
self
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/// Sets the maximum number of concurrent uploads (WASM only).
|
|
139
|
+
#[cfg(target_arch = "wasm32")]
|
|
140
|
+
pub fn with_max_uploads(mut self, max: usize) -> Self {
|
|
141
|
+
self.concurrency.max_uploads = max;
|
|
142
|
+
self
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/// Attempts to connect using the provided app key and TLS configuration.
|
|
146
|
+
/// If the app key is valid, returns Some([SDK]), otherwise returns None.
|
|
147
|
+
///
|
|
148
|
+
/// If you receive None, call [Builder::request_connection] to request a new connection.
|
|
149
|
+
///
|
|
150
|
+
/// # Arguments
|
|
151
|
+
/// * `app_key` - The application key used for authentication.
|
|
152
|
+
/// * `tls_config` - The TLS configuration for secure communication.
|
|
153
|
+
#[cfg(not(target_arch = "wasm32"))]
|
|
154
|
+
pub async fn connected(
|
|
155
|
+
&self,
|
|
156
|
+
app_key: &PrivateKey,
|
|
157
|
+
tls_config: rustls::ClientConfig,
|
|
158
|
+
) -> Result<Option<SDK>, BuilderError> {
|
|
159
|
+
let connected = self.client.check_app_authenticated(app_key).await?;
|
|
160
|
+
if !connected {
|
|
161
|
+
return Ok(None);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
let sdk = SDK::new(self.client.clone(), Arc::new(app_key.clone()), tls_config).await?;
|
|
165
|
+
Ok(Some(sdk))
|
|
166
|
+
}
|
|
167
|
+
#[cfg(target_arch = "wasm32")]
|
|
168
|
+
pub async fn connected(
|
|
169
|
+
&self,
|
|
170
|
+
app_key: &PrivateKey,
|
|
171
|
+
) -> Result<Option<SDK>, BuilderError> {
|
|
172
|
+
let connected = self.client.check_app_authenticated(app_key).await?;
|
|
173
|
+
if !connected {
|
|
174
|
+
return Ok(None);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
let sdk = SDK::new(
|
|
178
|
+
self.client.clone(),
|
|
179
|
+
Arc::new(app_key.clone()),
|
|
180
|
+
self.concurrency,
|
|
181
|
+
)
|
|
182
|
+
.await?;
|
|
183
|
+
Ok(Some(sdk))
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/// Requests a new connection for the application.
|
|
187
|
+
///
|
|
188
|
+
/// # Arguments
|
|
189
|
+
/// * `app` - Details of the application requesting connection.
|
|
190
|
+
pub async fn request_connection(
|
|
191
|
+
self,
|
|
192
|
+
app: &RegisterAppRequest,
|
|
193
|
+
) -> Result<Builder<RequestingApprovalState>, BuilderError> {
|
|
194
|
+
let resp = self.client.request_app_connection(app).await?;
|
|
195
|
+
self.with_connection_response(app.app_id, resp)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/// Transitions to [RequestingApprovalState] using a pre-fetched
|
|
199
|
+
/// connection response. This is useful when the HTTP call to
|
|
200
|
+
/// `POST /auth/connect` was made out-of-band (e.g. via curl).
|
|
201
|
+
pub fn with_connection_response(
|
|
202
|
+
self,
|
|
203
|
+
app_id: Hash256,
|
|
204
|
+
response: RegisterAppResponse,
|
|
205
|
+
) -> Result<Builder<RequestingApprovalState>, BuilderError> {
|
|
206
|
+
Ok(Builder {
|
|
207
|
+
state: RequestingApprovalState {
|
|
208
|
+
app_id,
|
|
209
|
+
response_url: Url::parse(&response.response_url)?,
|
|
210
|
+
register_url: Url::parse(&response.register_url)?,
|
|
211
|
+
status_url: Url::parse(&response.status_url)?,
|
|
212
|
+
expiration: response.expiration,
|
|
213
|
+
},
|
|
214
|
+
client: self.client,
|
|
215
|
+
#[cfg(target_arch = "wasm32")]
|
|
216
|
+
concurrency: self.concurrency,
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
impl Builder<RequestingApprovalState> {
|
|
222
|
+
/// Returns the response URL for the registration process. This
|
|
223
|
+
/// should be displayed to the user so they can authorize the
|
|
224
|
+
/// application.
|
|
225
|
+
pub fn response_url(&self) -> &str {
|
|
226
|
+
self.state.response_url.as_str()
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/// Waits for the application registration to be approved. This
|
|
230
|
+
/// polls the status URL until the registration is approved or
|
|
231
|
+
/// rejected. This can take several minutes depending on user action.
|
|
232
|
+
///
|
|
233
|
+
/// [Builder::response_url] should be displayed to the user
|
|
234
|
+
/// before calling this method.
|
|
235
|
+
pub async fn wait_for_approval(self) -> Result<Builder<ApprovedState>, BuilderError> {
|
|
236
|
+
loop {
|
|
237
|
+
if Utc::now() >= self.state.expiration {
|
|
238
|
+
return Err(BuilderError::RequestExpired);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if let Some(user_secret) = self
|
|
242
|
+
.client
|
|
243
|
+
.check_request_status(self.state.status_url.clone())
|
|
244
|
+
.await?
|
|
245
|
+
{
|
|
246
|
+
return Ok(Builder {
|
|
247
|
+
state: ApprovedState {
|
|
248
|
+
app_id: self.state.app_id,
|
|
249
|
+
register_url: self.state.register_url.clone(),
|
|
250
|
+
user_secret,
|
|
251
|
+
},
|
|
252
|
+
client: self.client,
|
|
253
|
+
#[cfg(target_arch = "wasm32")]
|
|
254
|
+
concurrency: self.concurrency,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
sleep(Duration::from_secs(5)).await;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
impl Builder<ApprovedState> {
|
|
263
|
+
/// Completes the registration process and returns an SDK instance.
|
|
264
|
+
///
|
|
265
|
+
/// # Arguments
|
|
266
|
+
/// * `mnemonic` - The user's mnemonic phrase used to derive the application key.
|
|
267
|
+
/// * `tls_config` - The TLS configuration for secure communication.
|
|
268
|
+
///
|
|
269
|
+
/// # Errors
|
|
270
|
+
/// Returns [BuilderError] if the registration fails or the SDK cannot be created.
|
|
271
|
+
#[cfg(not(target_arch = "wasm32"))]
|
|
272
|
+
pub async fn register(
|
|
273
|
+
self,
|
|
274
|
+
mnemonic: &str,
|
|
275
|
+
tls_config: rustls::ClientConfig,
|
|
276
|
+
) -> Result<SDK, BuilderError> {
|
|
277
|
+
let app_key = derive_app_key(mnemonic, &self.state.app_id, &self.state.user_secret)?;
|
|
278
|
+
self.client
|
|
279
|
+
.register_app(&app_key, self.state.register_url.clone())
|
|
280
|
+
.await?;
|
|
281
|
+
SDK::new(self.client, Arc::new(app_key), tls_config).await
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/// Completes the registration process and returns an SDK instance.
|
|
285
|
+
///
|
|
286
|
+
/// # Arguments
|
|
287
|
+
/// * `mnemonic` - The user's mnemonic phrase used to derive the application key.
|
|
288
|
+
/// * `tls_config` - The TLS configuration for secure communication.
|
|
289
|
+
///
|
|
290
|
+
/// # Errors
|
|
291
|
+
/// Returns [BuilderError] if the registration fails or the SDK cannot be created.
|
|
292
|
+
#[cfg(target_arch = "wasm32")]
|
|
293
|
+
pub async fn register(
|
|
294
|
+
self,
|
|
295
|
+
mnemonic: &str,
|
|
296
|
+
) -> Result<SDK, BuilderError> {
|
|
297
|
+
let app_key = derive_app_key(mnemonic, &self.state.app_id, &self.state.user_secret)?;
|
|
298
|
+
self.client
|
|
299
|
+
.register_app(&app_key, self.state.register_url.clone())
|
|
300
|
+
.await?;
|
|
301
|
+
SDK::new(
|
|
302
|
+
self.client,
|
|
303
|
+
Arc::new(app_key),
|
|
304
|
+
self.concurrency,
|
|
305
|
+
)
|
|
306
|
+
.await
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/// A helper function to derive an application key from a
|
|
311
|
+
/// mnemonic, app ID, and shared secret.
|
|
312
|
+
///
|
|
313
|
+
/// It is exposed to be able to test the app key derivation logic.
|
|
314
|
+
fn derive_app_key(
|
|
315
|
+
mnemonic: &str,
|
|
316
|
+
app_id: &Hash256,
|
|
317
|
+
shared_secret: &Hash256,
|
|
318
|
+
) -> Result<PrivateKey, BuilderError> {
|
|
319
|
+
const KEY_DOMAIN: &[u8] = b"indexd app key derivation";
|
|
320
|
+
let seed = Seed::new(mnemonic)?;
|
|
321
|
+
let mut key = [0u8; 64];
|
|
322
|
+
key[..32].copy_from_slice(seed.entropy());
|
|
323
|
+
key[32..].copy_from_slice(shared_secret.as_ref());
|
|
324
|
+
let mut okm = [0u8; 32];
|
|
325
|
+
derive(&key, app_id.as_ref(), KEY_DOMAIN, &mut okm);
|
|
326
|
+
Ok(PrivateKey::from_seed(&okm))
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
#[cfg(test)]
|
|
330
|
+
mod test {
|
|
331
|
+
use super::*;
|
|
332
|
+
use sia::hash_256;
|
|
333
|
+
use sia::types::Hash256;
|
|
334
|
+
|
|
335
|
+
#[test]
|
|
336
|
+
fn test_app_key_derivation_golden() {
|
|
337
|
+
const MNEMONIC: &str =
|
|
338
|
+
"glare own entire dish exact open theme family harsh room scrap rose";
|
|
339
|
+
const APP_ID: Hash256 =
|
|
340
|
+
hash_256!("0e90d697f5045a6593f1c43ebf79a369e2bc72cc5c7b6282f3b5aeb0de6e4005");
|
|
341
|
+
const SHARED_SECRET: Hash256 =
|
|
342
|
+
hash_256!("cf02d945fe4bfe614d823dc13c19aa8501699e656d0f7915490c3056d5c97dc6");
|
|
343
|
+
const EXPECTED_APP_KEY: &str =
|
|
344
|
+
"b75061f34bb3aeab232b0671da2d0347c547343a0026bb5535c291d964fd09a1";
|
|
345
|
+
|
|
346
|
+
let mut seed = [0u8; 32];
|
|
347
|
+
hex::decode_to_slice(EXPECTED_APP_KEY, &mut seed).expect("decoding failed");
|
|
348
|
+
let expected_app_key = PrivateKey::from_seed(&seed);
|
|
349
|
+
|
|
350
|
+
let derived_app_key =
|
|
351
|
+
derive_app_key(MNEMONIC, &APP_ID, &SHARED_SECRET).expect("derivation failed");
|
|
352
|
+
assert_eq!(derived_app_key, expected_app_key);
|
|
353
|
+
}
|
|
354
|
+
}
|