create-sia-app 0.1.7 → 0.1.9

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.
Files changed (114) hide show
  1. package/package.json +2 -6
  2. package/template/AGENTS.md +143 -0
  3. package/template/CLAUDE.md +25 -46
  4. package/template/README.md +6 -12
  5. package/template/_gitignore +0 -1
  6. package/template/dist/assets/index-BEylY2j7.css +1 -0
  7. package/template/dist/assets/index-CnYqArKN.js +8741 -0
  8. package/template/dist/assets/sia_bg-BTOHUC1A.wasm +0 -0
  9. package/template/dist/assets/slab-download-worker-DhW6ZBJs.js +2 -0
  10. package/template/dist/assets/slab-upload-worker-B2uSB2iY.js +2 -0
  11. package/template/dist/index.html +13 -0
  12. package/template/e2e/smoke.spec.ts +20 -0
  13. package/template/index.html +0 -1
  14. package/template/package.json +2 -2
  15. package/template/playwright.config.ts +13 -0
  16. package/template/src/components/Navbar.tsx +3 -3
  17. package/template/src/components/auth/ApproveScreen.tsx +10 -13
  18. package/template/src/components/auth/AuthFlow.tsx +13 -10
  19. package/template/src/components/auth/ConnectScreen.tsx +2 -3
  20. package/template/src/components/auth/RecoveryScreen.tsx +7 -7
  21. package/template/src/components/upload/UploadZone.tsx +192 -74
  22. package/template/src/index.css +14 -4
  23. package/template/src/stores/auth.ts +6 -12
  24. package/template/test-results/.last-run.json +4 -0
  25. package/template/tsconfig.app.json +1 -1
  26. package/template/tsconfig.node.json +0 -1
  27. package/template/vite.config.ts +2 -3
  28. package/template/rust/README.md +0 -16
  29. package/template/rust/sia-sdk-rs/.changeset/added_cancel_function_to_cancel_inflight_packed_uploads.md +0 -6
  30. package/template/rust/sia-sdk-rs/.changeset/check_if_we_have_enough_hosts_prior_to_encoding_in_upload_slabs.md +0 -16
  31. package/template/rust/sia-sdk-rs/.changeset/fix_slab_length_in_packed_object.md +0 -5
  32. package/template/rust/sia-sdk-rs/.changeset/fix_upload_racing_race_conditon.md +0 -13
  33. package/template/rust/sia-sdk-rs/.changeset/improved_parallelism_of_packed_uploads.md +0 -5
  34. package/template/rust/sia-sdk-rs/.changeset/progress_callback_will_now_be_called_as_expected_for_packed_uploads.md +0 -5
  35. package/template/rust/sia-sdk-rs/.github/dependabot.yml +0 -10
  36. package/template/rust/sia-sdk-rs/.github/workflows/main.yml +0 -36
  37. package/template/rust/sia-sdk-rs/.github/workflows/prepare-release.yml +0 -34
  38. package/template/rust/sia-sdk-rs/.github/workflows/release.yml +0 -30
  39. package/template/rust/sia-sdk-rs/.rustfmt.toml +0 -4
  40. package/template/rust/sia-sdk-rs/Cargo.lock +0 -4127
  41. package/template/rust/sia-sdk-rs/Cargo.toml +0 -3
  42. package/template/rust/sia-sdk-rs/LICENSE +0 -21
  43. package/template/rust/sia-sdk-rs/README.md +0 -30
  44. package/template/rust/sia-sdk-rs/indexd/CHANGELOG.md +0 -79
  45. package/template/rust/sia-sdk-rs/indexd/Cargo.toml +0 -79
  46. package/template/rust/sia-sdk-rs/indexd/benches/upload.rs +0 -258
  47. package/template/rust/sia-sdk-rs/indexd/src/app_client.rs +0 -1710
  48. package/template/rust/sia-sdk-rs/indexd/src/builder.rs +0 -354
  49. package/template/rust/sia-sdk-rs/indexd/src/download.rs +0 -379
  50. package/template/rust/sia-sdk-rs/indexd/src/hosts.rs +0 -659
  51. package/template/rust/sia-sdk-rs/indexd/src/lib.rs +0 -827
  52. package/template/rust/sia-sdk-rs/indexd/src/mock.rs +0 -162
  53. package/template/rust/sia-sdk-rs/indexd/src/object_encryption.rs +0 -125
  54. package/template/rust/sia-sdk-rs/indexd/src/quic.rs +0 -575
  55. package/template/rust/sia-sdk-rs/indexd/src/rhp4.rs +0 -52
  56. package/template/rust/sia-sdk-rs/indexd/src/slabs.rs +0 -497
  57. package/template/rust/sia-sdk-rs/indexd/src/upload.rs +0 -629
  58. package/template/rust/sia-sdk-rs/indexd/src/wasm_time.rs +0 -41
  59. package/template/rust/sia-sdk-rs/indexd/src/web_transport.rs +0 -398
  60. package/template/rust/sia-sdk-rs/indexd_ffi/CHANGELOG.md +0 -76
  61. package/template/rust/sia-sdk-rs/indexd_ffi/Cargo.toml +0 -47
  62. package/template/rust/sia-sdk-rs/indexd_ffi/examples/python/README.md +0 -10
  63. package/template/rust/sia-sdk-rs/indexd_ffi/examples/python/example.py +0 -130
  64. package/template/rust/sia-sdk-rs/indexd_ffi/src/bin/uniffi-bindgen.rs +0 -3
  65. package/template/rust/sia-sdk-rs/indexd_ffi/src/builder.rs +0 -377
  66. package/template/rust/sia-sdk-rs/indexd_ffi/src/io.rs +0 -155
  67. package/template/rust/sia-sdk-rs/indexd_ffi/src/lib.rs +0 -1039
  68. package/template/rust/sia-sdk-rs/indexd_ffi/src/logging.rs +0 -58
  69. package/template/rust/sia-sdk-rs/indexd_ffi/src/tls.rs +0 -23
  70. package/template/rust/sia-sdk-rs/indexd_wasm/Cargo.toml +0 -33
  71. package/template/rust/sia-sdk-rs/indexd_wasm/src/lib.rs +0 -818
  72. package/template/rust/sia-sdk-rs/knope.toml +0 -54
  73. package/template/rust/sia-sdk-rs/sia_derive/CHANGELOG.md +0 -38
  74. package/template/rust/sia-sdk-rs/sia_derive/Cargo.toml +0 -19
  75. package/template/rust/sia-sdk-rs/sia_derive/src/lib.rs +0 -278
  76. package/template/rust/sia-sdk-rs/sia_sdk/CHANGELOG.md +0 -91
  77. package/template/rust/sia-sdk-rs/sia_sdk/Cargo.toml +0 -59
  78. package/template/rust/sia-sdk-rs/sia_sdk/benches/merkle_root.rs +0 -12
  79. package/template/rust/sia-sdk-rs/sia_sdk/src/blake2.rs +0 -22
  80. package/template/rust/sia-sdk-rs/sia_sdk/src/consensus.rs +0 -767
  81. package/template/rust/sia-sdk-rs/sia_sdk/src/encoding/v1.rs +0 -257
  82. package/template/rust/sia-sdk-rs/sia_sdk/src/encoding/v2.rs +0 -291
  83. package/template/rust/sia-sdk-rs/sia_sdk/src/encoding.rs +0 -26
  84. package/template/rust/sia-sdk-rs/sia_sdk/src/encoding_async/v2.rs +0 -367
  85. package/template/rust/sia-sdk-rs/sia_sdk/src/encoding_async.rs +0 -6
  86. package/template/rust/sia-sdk-rs/sia_sdk/src/encryption.rs +0 -303
  87. package/template/rust/sia-sdk-rs/sia_sdk/src/erasure_coding.rs +0 -347
  88. package/template/rust/sia-sdk-rs/sia_sdk/src/lib.rs +0 -15
  89. package/template/rust/sia-sdk-rs/sia_sdk/src/macros.rs +0 -435
  90. package/template/rust/sia-sdk-rs/sia_sdk/src/merkle.rs +0 -112
  91. package/template/rust/sia-sdk-rs/sia_sdk/src/rhp/merkle.rs +0 -357
  92. package/template/rust/sia-sdk-rs/sia_sdk/src/rhp/rpc.rs +0 -1507
  93. package/template/rust/sia-sdk-rs/sia_sdk/src/rhp/types.rs +0 -146
  94. package/template/rust/sia-sdk-rs/sia_sdk/src/rhp.rs +0 -7
  95. package/template/rust/sia-sdk-rs/sia_sdk/src/seed.rs +0 -278
  96. package/template/rust/sia-sdk-rs/sia_sdk/src/signing.rs +0 -236
  97. package/template/rust/sia-sdk-rs/sia_sdk/src/types/common.rs +0 -677
  98. package/template/rust/sia-sdk-rs/sia_sdk/src/types/currency.rs +0 -450
  99. package/template/rust/sia-sdk-rs/sia_sdk/src/types/specifier.rs +0 -110
  100. package/template/rust/sia-sdk-rs/sia_sdk/src/types/spendpolicy.rs +0 -778
  101. package/template/rust/sia-sdk-rs/sia_sdk/src/types/utils.rs +0 -117
  102. package/template/rust/sia-sdk-rs/sia_sdk/src/types/v1.rs +0 -1737
  103. package/template/rust/sia-sdk-rs/sia_sdk/src/types/v2.rs +0 -1726
  104. package/template/rust/sia-sdk-rs/sia_sdk/src/types/work.rs +0 -59
  105. package/template/rust/sia-sdk-rs/sia_sdk/src/types.rs +0 -16
  106. package/template/scripts/setup-rust.js +0 -29
  107. package/template/src/lib/format.ts +0 -35
  108. package/template/src/lib/hex.ts +0 -13
  109. package/template/src/lib/sdk.ts +0 -25
  110. package/template/src/lib/wasm-env.ts +0 -5
  111. package/template/wasm/indexd_wasm/indexd_wasm.d.ts +0 -309
  112. package/template/wasm/indexd_wasm/indexd_wasm.js +0 -1507
  113. package/template/wasm/indexd_wasm/indexd_wasm_bg.wasm +0 -0
  114. package/template/wasm/indexd_wasm/package.json +0 -31
@@ -1,1507 +0,0 @@
1
- use crate::rhp::merkle;
2
- use bytes::Bytes;
3
- use serde::{Deserialize, Serialize};
4
- use std::fmt::Display;
5
- use std::marker::PhantomData;
6
- use thiserror::Error;
7
- use tokio::sync::oneshot;
8
-
9
- use super::types::*;
10
- use crate::consensus::ChainState;
11
- use crate::encoding::{Error as EncodingError, SiaEncodable};
12
- use crate::encoding_async::{
13
- AsyncDecoder, AsyncEncoder, AsyncSiaDecodable, AsyncSiaDecode, AsyncSiaEncodable,
14
- AsyncSiaEncode,
15
- };
16
- use crate::rhp::merkle::ProofValidationError;
17
- use crate::rhp::{self, SECTOR_SIZE};
18
- use blake2b_simd::Params;
19
-
20
- use crate::signing::{PrivateKey, PublicKey, Signature};
21
- use crate::types::v2::{FileContract, SatisfiedPolicy, SiacoinElement, SiacoinInput, Transaction};
22
- use crate::types::{
23
- Address, ChainIndex, Currency, FileContractID, Hash256, Leaf, SiacoinOutput, specifier,
24
- };
25
-
26
- macro_rules! impl_rpc_request {
27
- ($name:ident, $text:literal) => {
28
- impl RPCRequest for $name {
29
- async fn encode_request<E: AsyncEncoder>(&self, w: &mut E) -> Result<(), E::Error> {
30
- specifier!($text).encode_async(w).await?;
31
- self.encode_async(w).await?;
32
- Ok(())
33
- }
34
- }
35
- impl sealed::Sealed for $name {}
36
- };
37
- }
38
-
39
- macro_rules! impl_rpc_response {
40
- ($name:ident) => {
41
- impl RPCResponse for $name {
42
- async fn encode_response<E: AsyncEncoder>(&self, w: &mut E) -> Result<(), E::Error> {
43
- false.encode_async(w).await?; // nil error
44
- self.encode_async(w).await?;
45
- Ok(())
46
- }
47
-
48
- async fn decode_response<D: AsyncDecoder>(r: &mut D) -> Result<Self, D::Error>
49
- where
50
- D::Error: From<rhp::Error>,
51
- {
52
- let has_error = bool::decode_async(r).await?;
53
- match has_error {
54
- false => {
55
- let resp = Self::decode_async(r).await?;
56
- Ok(resp)
57
- }
58
- true => {
59
- let error = RPCError::decode_async(r).await?;
60
- Err(Error::from(error).into())
61
- }
62
- }
63
- }
64
- }
65
-
66
- impl sealed::Sealed for $name {}
67
- };
68
- }
69
-
70
- pub trait RenterContractSigner {
71
- fn public_key(&self) -> PublicKey;
72
- fn sign(&self, msg: &[u8]) -> Signature;
73
- fn sign_revision(&self, state: &ChainState, contract: &mut FileContract);
74
- }
75
-
76
- impl RenterContractSigner for PrivateKey {
77
- fn public_key(&self) -> PublicKey {
78
- self.public_key()
79
- }
80
-
81
- fn sign(&self, msg: &[u8]) -> Signature {
82
- self.sign(msg)
83
- }
84
-
85
- fn sign_revision(&self, state: &ChainState, contract: &mut FileContract) {
86
- let sig_hash = contract.sig_hash(state);
87
- contract.renter_signature = self.sign(sig_hash.as_ref());
88
- }
89
- }
90
-
91
- pub trait TransactionBuilder {
92
- fn miner_fee(&self) -> Currency;
93
- fn fund_transaction(
94
- &self,
95
- transaction: &mut Transaction,
96
- amount: Currency,
97
- ) -> Result<ChainIndex, RPCError>;
98
- fn sign_transaction(&self, transaction: &mut Transaction) -> Result<(), RPCError>;
99
- }
100
-
101
- /// HostInputsResponse contains the host's Siacoin inputs for funding a
102
- /// formation or resolution transaction.
103
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
104
- struct HostInputsResponse {
105
- pub host_inputs: Vec<SiacoinInput>,
106
- }
107
- impl_rpc_response!(HostInputsResponse);
108
-
109
- /// RPCFormContractThirdResponse contains the finalized formation
110
- /// transaction set.
111
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
112
- struct TransactionSetResponse {
113
- pub basis: ChainIndex,
114
- pub transaction_set: Vec<Transaction>,
115
- }
116
- impl_rpc_response!(TransactionSetResponse);
117
-
118
- /// HostSignatureResponse contains the host's signature for a
119
- /// contract revision.
120
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
121
- #[allow(dead_code)] // TODO: use RPC
122
- struct HostSignatureResponse {
123
- pub host_signature: Signature,
124
- }
125
-
126
- /// RenterSignatureResponse contains the renter's signature for a
127
- /// contract revision.
128
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
129
- #[allow(dead_code)] // TODO: use RPC
130
- struct RenterSignatureResponse {
131
- pub renter_signature: Signature,
132
- }
133
-
134
- /// RenterResolutionSignaturesResponse contains the renter's signatures for the
135
- /// contract resolution transaction.
136
- ///
137
- /// At this point, the host has enough information to broadcast the refresh.
138
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
139
- #[allow(dead_code)] // TODO: use RPC
140
- struct RenterResolutionSignaturesResponse {
141
- pub renter_renewal_signature: Signature,
142
- pub renter_contract_signature: Signature,
143
- pub renter_satisfied_policies: Vec<SatisfiedPolicy>,
144
- }
145
-
146
- /// RPCRefreshContractParams contains the parameters for refreshing a contract.
147
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
148
- pub struct RefreshContractParams {
149
- pub contract_id: FileContractID,
150
- pub allowance: Currency,
151
- pub collateral: Currency,
152
- }
153
-
154
- pub struct RPCRefreshContractRequestParams {
155
- pub prices: HostPrices,
156
- pub refresh: RefreshContractParams,
157
- pub miner_fee: Currency,
158
- pub basis: ChainIndex,
159
- pub renter_inputs: Vec<SiacoinElement>,
160
- pub renter_parents: Vec<Transaction>,
161
- }
162
-
163
- /// RPCRefreshContractRequest is the request type for RPCRefreshContract.
164
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
165
- struct RPCRefreshContractRequest {
166
- pub prices: HostPrices,
167
- pub refresh: RefreshContractParams,
168
- pub miner_fee: Currency,
169
- pub basis: ChainIndex,
170
- pub renter_inputs: Vec<SiacoinElement>,
171
- pub renter_parents: Vec<Transaction>,
172
-
173
- pub challenge_signature: Signature,
174
- }
175
-
176
- impl RPCRefreshContractRequest {
177
- #[allow(dead_code)] // TODO: remove
178
- pub fn new<S: RenterContractSigner>(
179
- signer: S,
180
- params: RPCRefreshContractRequestParams,
181
- revision_number: u64,
182
- ) -> Self {
183
- let mut state = Params::new().hash_length(32).to_state();
184
- params.refresh.contract_id.encode(&mut state).unwrap();
185
- revision_number.encode(&mut state).unwrap();
186
-
187
- Self {
188
- prices: params.prices,
189
- refresh: params.refresh,
190
- miner_fee: params.miner_fee,
191
- basis: params.basis,
192
- renter_inputs: params.renter_inputs,
193
- renter_parents: params.renter_parents,
194
- challenge_signature: signer.sign(state.finalize().as_ref()),
195
- }
196
- }
197
- }
198
-
199
- /// RPCRenewContractParams contains the parameters for renewing a contract.
200
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
201
- struct RenewContractParams {
202
- pub contract_id: FileContractID,
203
- pub allowance: Currency,
204
- pub collateral: Currency,
205
- pub proof_height: u64,
206
- }
207
-
208
- struct RPCRenewContractRequestParams {
209
- pub prices: HostPrices,
210
- pub renewal: RenewContractParams,
211
- pub miner_fee: Currency,
212
- pub basis: ChainIndex,
213
- pub renter_inputs: Vec<SiacoinElement>,
214
- pub renter_parents: Vec<Transaction>,
215
- }
216
-
217
- /// RPCRenewContractRequest is the request type for RPCRenewContract.
218
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
219
- struct RPCRenewContractRequest {
220
- pub prices: HostPrices,
221
- pub renewal: RenewContractParams,
222
- pub miner_fee: Currency,
223
- pub basis: ChainIndex,
224
- pub renter_inputs: Vec<SiacoinElement>,
225
- pub renter_parents: Vec<Transaction>,
226
-
227
- pub challenge_signature: Signature,
228
- }
229
-
230
- impl RPCRenewContractRequest {
231
- #[allow(dead_code)] // TODO: remove
232
- pub fn new<S: RenterContractSigner>(
233
- signer: S,
234
- params: RPCRenewContractRequestParams,
235
- revision_number: u64,
236
- ) -> Self {
237
- let mut state = Params::new().hash_length(32).to_state();
238
- params.renewal.contract_id.encode(&mut state).unwrap();
239
- revision_number.encode(&mut state).unwrap();
240
-
241
- Self {
242
- prices: params.prices,
243
- renewal: params.renewal,
244
- miner_fee: params.miner_fee,
245
- basis: params.basis,
246
- renter_inputs: params.renter_inputs,
247
- renter_parents: params.renter_parents,
248
- challenge_signature: signer.sign(state.finalize().as_ref()),
249
- }
250
- }
251
- }
252
-
253
- pub struct RPCFreeSectorsRequestParams {
254
- pub contract_id: FileContractID,
255
- pub prices: HostPrices,
256
- pub indices: Vec<u64>,
257
- }
258
-
259
- /// RPCFreeSectorsRequest is the request type for removing sectors from a contract.
260
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
261
- struct RPCFreeSectorsRequest {
262
- pub contract_id: FileContractID,
263
- pub prices: HostPrices,
264
- pub indices: Vec<u64>,
265
-
266
- pub challenge_signature: Signature,
267
- }
268
-
269
- impl RPCFreeSectorsRequest {
270
- #[allow(dead_code)] // TODO: remove
271
- pub fn new<S: RenterContractSigner>(
272
- signer: S,
273
- params: RPCFreeSectorsRequestParams,
274
- revision_number: u64,
275
- ) -> Self {
276
- let mut state = Params::new().hash_length(32).to_state();
277
- params.contract_id.encode(&mut state).unwrap();
278
- revision_number.encode(&mut state).unwrap();
279
-
280
- RPCFreeSectorsRequest {
281
- contract_id: params.contract_id,
282
- prices: params.prices,
283
- indices: params.indices,
284
- challenge_signature: signer.sign(state.finalize().as_ref()),
285
- }
286
- }
287
- }
288
-
289
- /// RPCFreeSectorsResponse contains the host's old subtree hashes, old leaf hashes,
290
- /// and the new merkle root after freeing sectors.
291
- ///
292
- /// The renter must validate the response
293
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
294
- #[allow(dead_code)] // TODO: use RPC
295
- struct RPCFreeSectorsResponse {
296
- pub old_subtree_hashes: Vec<Hash256>,
297
- pub old_leaf_hashes: Vec<Hash256>,
298
- pub new_merkle_root: Hash256,
299
- }
300
- impl_rpc_response!(RPCFreeSectorsResponse);
301
-
302
- /// RPCLatestRevisionRequest is the request type for getting the latest
303
- /// revision of a file contract.
304
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
305
- #[allow(dead_code)] // TODO: use RPC
306
- struct RPCLatestRevisionRequest {
307
- pub contract_id: FileContractID,
308
- }
309
-
310
- /// RPCLatestRevisionResponse contains the latest revision of a file contract,
311
- /// whether it is revisable, and whether it has been renewed.
312
- ///
313
- /// If either `revisable` or `renewed` is false, the host will not accept
314
- /// further revisions or renewals of the contract.
315
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
316
- #[allow(dead_code)] // TODO: use RPC
317
- struct RPCLatestRevisionResponse {
318
- pub contract: FileContract,
319
- pub revisable: bool,
320
- pub renewed: bool,
321
- }
322
-
323
- struct RPCAppendSectorsRequestParams {
324
- pub prices: HostPrices,
325
- pub sectors: Vec<Hash256>,
326
- pub contract_id: FileContractID,
327
- }
328
-
329
- /// RPCAppendSectorsRequest is the request type for appending sectors to a contract.
330
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
331
- struct RPCAppendSectorsRequest {
332
- pub prices: HostPrices,
333
- pub sectors: Vec<Hash256>,
334
- pub contract_id: FileContractID,
335
-
336
- pub challenge_signature: Signature,
337
- }
338
-
339
- impl RPCAppendSectorsRequest {
340
- #[allow(dead_code)] // TODO: remove
341
- pub fn new<S: RenterContractSigner>(
342
- signer: S,
343
- params: RPCAppendSectorsRequestParams,
344
- revision_number: u64,
345
- ) -> Self {
346
- let mut state = Params::new().hash_length(32).to_state();
347
- params.contract_id.encode(&mut state).unwrap();
348
- revision_number.encode(&mut state).unwrap();
349
- Self {
350
- prices: params.prices,
351
- sectors: params.sectors,
352
- contract_id: params.contract_id,
353
- challenge_signature: signer.sign(state.finalize().as_ref()),
354
- }
355
- }
356
- }
357
-
358
- /// RPCAppendSectorsResponse contains the host's response to an append request.
359
- ///
360
- /// It includes the sectors that were accepted, the subtree roots, and the new
361
- /// merkle root after the append operation. The renter must validate the proof
362
- /// against the accepted roots.
363
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
364
- #[allow(dead_code)] // TODO: use RPC
365
- struct RPCAppendSectorsResponse {
366
- pub accepted: Vec<bool>,
367
- pub subtree_roots: Vec<Hash256>,
368
- pub new_merkle_root: Hash256,
369
- }
370
-
371
- /// RPCSectorRootsRequest is the request type for getting the sector roots
372
- /// for a contract.
373
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
374
- #[allow(dead_code)] // TODO: use RPC
375
- struct RPCSectorRootsRequest {
376
- pub prices: HostPrices,
377
- pub contract_id: FileContractID,
378
- pub renter_signature: Signature,
379
- pub offset: u64,
380
- pub length: u64,
381
- }
382
-
383
- /// RPCSectorRootsResponse contains the sector roots and a proof for a contract.
384
- /// The renter must validate the proof against the roots.
385
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
386
- #[allow(dead_code)] // TODO: use RPC
387
- struct RPCSectorRootsResponse {
388
- pub proof: Vec<Hash256>,
389
- pub roots: Vec<Hash256>,
390
- pub host_signature: Signature,
391
- }
392
-
393
- struct RPCReplenishAccountsParams {
394
- pub accounts: Vec<PublicKey>,
395
- pub target: Currency,
396
- pub contract_id: FileContractID,
397
- }
398
-
399
- /// RPCReplenishAccountsRequest is the request type for replenishing accounts
400
- /// with Siacoin deposits.
401
- ///
402
- /// The host will fund the account to the target amount and send
403
- /// a revision to the renter for verification.
404
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
405
- struct RPCReplenishAccountsRequest {
406
- pub accounts: Vec<PublicKey>,
407
- pub target: Currency,
408
- pub contract_id: FileContractID,
409
-
410
- pub challenge_signature: Signature,
411
- }
412
-
413
- impl RPCReplenishAccountsRequest {
414
- #[allow(dead_code)] // TODO: remove
415
- pub fn new<S: RenterContractSigner>(
416
- signer: S,
417
- params: RPCReplenishAccountsParams,
418
- revision_number: u64,
419
- ) -> Self {
420
- let mut state = Params::new().hash_length(32).to_state();
421
- params.accounts.encode(&mut state).unwrap();
422
- params.target.encode(&mut state).unwrap();
423
- params.contract_id.encode(&mut state).unwrap();
424
- revision_number.encode(&mut state).unwrap();
425
-
426
- Self {
427
- accounts: params.accounts,
428
- target: params.target,
429
- contract_id: params.contract_id,
430
- challenge_signature: signer.sign(state.finalize().as_ref()),
431
- }
432
- }
433
- }
434
-
435
- /// RPCReplenishAccountsResponse contains the host's response to the replenish
436
- /// request.
437
- ///
438
- /// The renter should verify the deposits and construct a revision
439
- /// transferring the funds.
440
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
441
- #[allow(dead_code)] // TODO: use RPC
442
- struct RPCReplenishAccountsResponse {
443
- pub deposits: Vec<AccountDeposit>,
444
- }
445
-
446
- /// RPCVerifySectorRequest is the request type for verifying the host
447
- /// is storing a sector.
448
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
449
- #[allow(dead_code)] // TODO: use RPC
450
- struct RPCVerifySectorRequest {
451
- pub prices: HostPrices,
452
- pub token: AccountToken,
453
- pub root: Hash256,
454
- pub leaf_index: u64,
455
- }
456
-
457
- /// RPCVerifySectorResponse contains a proof that the host is storing a
458
- /// sector.
459
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
460
- #[allow(dead_code)] // TODO: use RPC
461
- struct RPCVerifySectorResponse {
462
- pub proof: Vec<Hash256>,
463
- pub leaf: Leaf,
464
- }
465
-
466
- /// RPCFundAccountsRequest is the request type for funding accounts
467
- /// with Siacoin deposits.
468
- ///
469
- /// RPCReplenishAccounts should be preferred
470
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
471
- #[allow(dead_code)] // TODO: use RPC
472
- struct RPCFundAccountsRequest {
473
- pub contract_id: FileContractID,
474
- pub deposits: Vec<AccountDeposit>,
475
- pub renter_signature: Signature,
476
- }
477
-
478
- /// RPCFundAccountsResponse contains the host's signature and new
479
- /// balance after funding the accounts.
480
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
481
- #[allow(dead_code)] // TODO: use RPC
482
- struct RPCFundAccountsResponse {
483
- pub balances: Vec<Currency>,
484
- pub host_signature: Signature,
485
- }
486
-
487
- /// RPCError is the error type returned by the RPC server.
488
- #[derive(Debug, PartialEq, AsyncSiaDecode, AsyncSiaEncode)]
489
- pub struct RPCError {
490
- pub code: u8,
491
- pub description: String,
492
- }
493
-
494
- impl Display for RPCError {
495
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
496
- write!(f, "{} ({})", self.description, self.code)
497
- }
498
- }
499
-
500
- impl std::error::Error for RPCError {}
501
-
502
- #[derive(Debug, Error)]
503
- pub enum Error {
504
- #[error("IO error: {0}")]
505
- Io(#[from] std::io::Error),
506
- #[error("Encoding error: {0}")]
507
- Encoding(#[from] EncodingError),
508
-
509
- #[error("RPC error: {0}")]
510
- RPC(#[from] RPCError),
511
-
512
- #[error("not enough host funds {0} < {1}")]
513
- NotEnoughHostFunds(Currency, Currency),
514
-
515
- #[error("invalid response: {0}")]
516
- InvalidResponse(String),
517
-
518
- #[error("invalid signature")]
519
- InvalidSignature,
520
-
521
- #[error("expected single file contract in response, found {0}")]
522
- ExpectedContractTransaction(usize),
523
-
524
- #[error("expected transaction set in response")]
525
- ExpectedTransactionSet,
526
-
527
- #[error("proof validation failed")]
528
- ProofValidation(#[from] ProofValidationError),
529
-
530
- #[error(
531
- "root of uploaded data doesn't match root returned by host: expected {expected}, got {got}"
532
- )]
533
- SectorRootMismatch { expected: Hash256, got: Hash256 },
534
- }
535
-
536
- #[derive(Debug, Default, PartialEq, Clone, Copy, Serialize, Deserialize)]
537
- #[serde(rename_all = "camelCase")]
538
- pub struct Usage {
539
- pub rpc: Currency,
540
- pub storage: Currency,
541
- pub egress: Currency,
542
- pub ingress: Currency,
543
- pub account_funding: Currency,
544
- pub risked_collateral: Currency,
545
- }
546
-
547
- impl Usage {
548
- const fn round_4kib(size: u64) -> u64 {
549
- (size + 4095) & !4095
550
- }
551
-
552
- pub fn renter_cost(&self) -> Currency {
553
- self.rpc + self.storage + self.egress + self.ingress + self.account_funding
554
- }
555
-
556
- pub fn host_collateral(&self) -> Currency {
557
- self.risked_collateral
558
- }
559
-
560
- pub fn write_sector(prices: &HostPrices, data_length: usize) -> Self {
561
- const TEMP_SECTOR_DURATION: u64 = 144 * 3;
562
- let data_length = Currency::from(Self::round_4kib(data_length as u64));
563
- Usage {
564
- storage: prices.storage_price * data_length * Currency::from(TEMP_SECTOR_DURATION),
565
- ingress: prices.ingress_price * data_length,
566
- ..Default::default()
567
- }
568
- }
569
-
570
- pub fn read_sector(prices: &HostPrices, data_length: usize) -> Self {
571
- Usage {
572
- egress: prices.egress_price * Currency::from(Self::round_4kib(data_length as u64)),
573
- ..Default::default()
574
- }
575
- }
576
-
577
- pub fn sector_roots(prices: &HostPrices, num_roots: usize) -> Self {
578
- Usage {
579
- egress: prices.egress_price * Currency::from(Self::round_4kib(32 * (num_roots as u64))),
580
- ..Default::default()
581
- }
582
- }
583
-
584
- pub fn verify_sector(prices: &HostPrices) -> Self {
585
- Usage {
586
- egress: prices.egress_price * Currency::from(SECTOR_SIZE),
587
- ..Default::default()
588
- }
589
- }
590
-
591
- pub fn free_sectors(prices: &HostPrices, num_sectors: usize) -> Self {
592
- Usage {
593
- rpc: prices.free_sector_price * Currency::from(num_sectors),
594
- ..Default::default()
595
- }
596
- }
597
-
598
- pub fn append_sectors(prices: &HostPrices, num_sectors: usize, duration: u64) -> Self {
599
- Usage {
600
- storage: prices.storage_price * Currency::from(num_sectors) * Currency::from(duration),
601
- ingress: prices.ingress_price
602
- * Currency::from(Self::round_4kib(32 * num_sectors as u64)),
603
- risked_collateral: prices.collateral
604
- * Currency::from(num_sectors)
605
- * Currency::from(duration),
606
- ..Default::default()
607
- }
608
- }
609
-
610
- pub fn form_contract(prices: &HostPrices) -> Self {
611
- Usage {
612
- rpc: prices.contract_price,
613
- ..Default::default()
614
- }
615
- }
616
- }
617
-
618
- pub trait RPCRequest: Sized + sealed::Sealed {
619
- fn encode_request<E: AsyncEncoder>(
620
- &self,
621
- w: &mut E,
622
- ) -> impl Future<Output = Result<(), E::Error>>;
623
- }
624
-
625
- pub trait RPCResponse: Sized + sealed::Sealed {
626
- fn decode_response<D: AsyncDecoder>(r: &mut D) -> impl Future<Output = Result<Self, D::Error>>
627
- where
628
- D::Error: From<rhp::Error>;
629
- fn encode_response<E: AsyncEncoder>(
630
- &self,
631
- w: &mut E,
632
- ) -> impl Future<Output = Result<(), E::Error>>;
633
- }
634
-
635
- mod sealed {
636
- pub trait Sealed {}
637
- }
638
-
639
- pub trait Transport {
640
- type Error: From<Error>;
641
-
642
- fn write_request<R: RPCRequest>(
643
- &mut self,
644
- request: &R,
645
- ) -> impl Future<Output = Result<(), Self::Error>>;
646
-
647
- fn write_bytes(&mut self, data: Bytes) -> impl Future<Output = Result<(), Self::Error>>;
648
-
649
- fn write_response<R: RPCResponse>(
650
- &mut self,
651
- response: &R,
652
- ) -> impl Future<Output = Result<(), Self::Error>>;
653
-
654
- fn read_response<R: RPCResponse>(&mut self) -> impl Future<Output = Result<R, Self::Error>>;
655
- }
656
-
657
- /// Marker type for the initial stage of the RPC process.
658
- pub struct RPCInit;
659
-
660
- /// Marker type for the waiting stage for host inputs.
661
- pub struct RPCAwaitingHostInputs;
662
-
663
- /// Marker type for the waiting stage for host signatures.
664
- pub struct RPCAwaitingHostSignatures;
665
-
666
- /// Marker type for the waiting stage for renter signatures.
667
- pub struct RPCAwaitingRenterSignatures;
668
-
669
- /// Marker type for the completion stage of the RPC process.
670
- pub struct RPCComplete;
671
-
672
- /// RPCSettingsRequest is the request type getting the host's current settings.
673
- /// It is encoded as 0 bytes.
674
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
675
- struct RPCSettingsRequest {}
676
-
677
- impl_rpc_request!(RPCSettingsRequest, "Settings");
678
-
679
- /// RPCSettingsResponse is the response type for the RPC settings endpoint.
680
- #[derive(Debug, PartialEq, AsyncSiaDecode, AsyncSiaEncode)]
681
- struct RPCSettingsResponse {
682
- pub settings: HostSettings,
683
- }
684
- impl_rpc_response!(RPCSettingsResponse);
685
-
686
- #[derive(Debug, Serialize, Deserialize)]
687
- #[serde(rename_all = "camelCase")]
688
- pub struct RPCSettingsResult {
689
- pub settings: HostSettings,
690
- pub usage: Usage,
691
- }
692
-
693
- /// RPCSettings returns the host's current settings.
694
- pub struct RPCSettings<T: Transport, State> {
695
- transport: T,
696
- state: PhantomData<State>,
697
- }
698
-
699
- impl<T: Transport> RPCSettings<T, RPCInit> {
700
- pub async fn send_request(mut transport: T) -> Result<RPCSettings<T, RPCComplete>, T::Error> {
701
- transport.write_request(&RPCSettingsRequest {}).await?;
702
-
703
- Ok(RPCSettings {
704
- transport,
705
- state: PhantomData,
706
- })
707
- }
708
- }
709
-
710
- impl<T: Transport> RPCSettings<T, RPCComplete> {
711
- pub async fn complete(mut self) -> Result<RPCSettingsResult, T::Error> {
712
- let response: RPCSettingsResponse = self.transport.read_response().await?;
713
- Ok(RPCSettingsResult {
714
- settings: response.settings,
715
- usage: Usage::default(),
716
- })
717
- }
718
- }
719
-
720
- /// RPCWriteSectorRequest is the request type for writing a sector to the host's
721
- /// temporary storage.
722
- ///
723
- /// The host will store the sector for 432 blocks. If the sector is not
724
- /// appended to a contract within that time, it will be deleted.
725
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
726
- struct RPCWriteSectorRequest {
727
- pub prices: HostPrices,
728
- pub token: AccountToken,
729
- pub data_len: usize,
730
- }
731
- impl_rpc_request!(RPCWriteSectorRequest, "WriteSector");
732
-
733
- /// RPCWriteSectorResponse contains the root hash of the written sector.
734
- ///
735
- /// The renter must verify the root hash against the data written.
736
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
737
- struct RPCWriteSectorResponse {
738
- pub root: Hash256,
739
- }
740
- impl_rpc_response!(RPCWriteSectorResponse);
741
-
742
- /// RPCWriteSectorResult contains the result of a write sector operation,
743
- pub struct RPCWriteSectorResult {
744
- pub root: Hash256,
745
- pub usage: Usage,
746
- }
747
-
748
- /// RPCWriteSector writes a sector to the host's temporary storage.
749
- /// The host will store the sector for 432 blocks.
750
- /// If the sector is not appended to a contract within that time, it will be deleted.
751
- pub struct RPCWriteSector<T: Transport, State> {
752
- root: Hash256,
753
- transport: T,
754
- usage: Usage,
755
- state: PhantomData<State>,
756
- }
757
-
758
- impl<T: Transport> RPCWriteSector<T, RPCInit> {
759
- pub async fn send_request(
760
- mut transport: T,
761
- prices: HostPrices,
762
- token: AccountToken,
763
- data: Bytes,
764
- ) -> Result<RPCWriteSector<T, RPCComplete>, T::Error> {
765
- let usage = Usage::write_sector(&prices, data.len());
766
- let request = RPCWriteSectorRequest {
767
- prices,
768
- token,
769
- data_len: data.len(),
770
- };
771
-
772
- #[cfg(not(target_arch = "wasm32"))]
773
- let root_rx = {
774
- let (tx, rx) = oneshot::channel();
775
- let root_data = data.clone();
776
- rayon::spawn(move || {
777
- let root = merkle::sector_root(root_data.as_ref());
778
- let _ = tx.send(root);
779
- });
780
- Some(rx)
781
- };
782
- #[cfg(target_arch = "wasm32")]
783
- let root_rx: Option<oneshot::Receiver<crate::types::Hash256>> = None;
784
-
785
- transport.write_request(&request).await?;
786
- transport.write_bytes(data.clone()).await?;
787
-
788
- let root = match root_rx {
789
- Some(rx) => rx.await.unwrap(),
790
- None => merkle::sector_root(data.as_ref()),
791
- };
792
-
793
- Ok(RPCWriteSector {
794
- root,
795
- transport,
796
- usage,
797
- state: PhantomData,
798
- })
799
- }
800
- }
801
-
802
- impl<T: Transport> RPCWriteSector<T, RPCComplete> {
803
- pub async fn complete(mut self) -> Result<RPCWriteSectorResult, T::Error> {
804
- let response: RPCWriteSectorResponse = self.transport.read_response().await?;
805
- if response.root != self.root {
806
- return Err(Error::SectorRootMismatch {
807
- expected: self.root,
808
- got: response.root,
809
- }
810
- .into());
811
- }
812
-
813
- Ok(RPCWriteSectorResult {
814
- root: response.root,
815
- usage: self.usage,
816
- })
817
- }
818
- }
819
-
820
- /// RPCReadSectorRequest is the request type for reading a sector from the
821
- /// host.
822
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
823
- struct RPCReadSectorRequest {
824
- pub prices: HostPrices,
825
- pub token: AccountToken,
826
- pub root: Hash256,
827
- pub offset: u64,
828
- pub length: u64,
829
- }
830
- impl_rpc_request!(RPCReadSectorRequest, "ReadSector");
831
-
832
- /// RPCReadSectorResponse contains the proof and data for a sector read request.
833
- /// The renter must validate the proof against the root hash.
834
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
835
- struct RPCReadSectorResponse {
836
- pub data: merkle::RangeProof,
837
- }
838
- impl_rpc_response!(RPCReadSectorResponse);
839
-
840
- pub struct RPCReadSectorResult {
841
- pub data: Bytes,
842
- pub usage: Usage,
843
- }
844
-
845
- /// RPCReadSector reads a sector from the host.
846
- /// The proof must be validated against the expected
847
- /// root hash.
848
- pub struct RPCReadSector<T: Transport, State> {
849
- transport: T,
850
- usage: Usage,
851
- state: PhantomData<State>,
852
- offset: usize,
853
- length: usize,
854
- root: Hash256,
855
- }
856
-
857
- impl<T: Transport> RPCReadSector<T, RPCInit> {
858
- pub async fn send_request(
859
- mut transport: T,
860
- prices: HostPrices,
861
- token: AccountToken,
862
- root: Hash256,
863
- offset: usize,
864
- length: usize,
865
- ) -> Result<RPCReadSector<T, RPCComplete>, T::Error> {
866
- let usage = Usage::read_sector(&prices, length);
867
- let request = RPCReadSectorRequest {
868
- prices,
869
- token,
870
- root,
871
- offset: offset as u64,
872
- length: length as u64,
873
- };
874
- transport.write_request(&request).await?;
875
-
876
- Ok(RPCReadSector {
877
- transport,
878
- usage,
879
- state: PhantomData,
880
- offset,
881
- length,
882
- root,
883
- })
884
- }
885
- }
886
-
887
- impl<T: Transport> RPCReadSector<T, RPCComplete> {
888
- pub async fn complete(mut self) -> Result<RPCReadSectorResult, T::Error> {
889
- let response: RPCReadSectorResponse = self.transport.read_response().await?;
890
-
891
- // verify proof
892
- let start = self.offset / SEGMENT_SIZE;
893
- let end = (self.offset + self.length).div_ceil(SEGMENT_SIZE);
894
- #[cfg(not(target_arch = "wasm32"))]
895
- let data = {
896
- let (tx, rx) = oneshot::channel();
897
- let root = self.root;
898
- rayon::spawn(move || {
899
- let res = response
900
- .data
901
- .verify(&root, start, end)
902
- .map_err(Error::ProofValidation);
903
- tx.send(res).unwrap();
904
- });
905
- rx.await.unwrap()?
906
- };
907
- #[cfg(target_arch = "wasm32")]
908
- let data = response
909
- .data
910
- .verify(&self.root, start, end)
911
- .map_err(Error::ProofValidation)?;
912
- Ok(RPCReadSectorResult {
913
- usage: self.usage,
914
- data,
915
- })
916
- }
917
- }
918
-
919
- /// RPCAccountBalanceRequest is the request type for getting the balance of
920
- /// an account.
921
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
922
- struct RPCAccountBalanceRequest {
923
- pub account: PublicKey,
924
- }
925
- impl_rpc_request!(RPCAccountBalanceRequest, "AccountBalance");
926
-
927
- /// RPCAccountBalanceResponse contains the balance of an account.
928
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
929
- struct RPCAccountBalanceResponse {
930
- pub balance: Currency,
931
- }
932
- impl_rpc_response!(RPCAccountBalanceResponse);
933
-
934
- /// RPCAccountBalanceResult contains the result of an account balance RPC.
935
- pub struct RPCAccountBalanceResult {
936
- pub balance: Currency,
937
- pub usage: Usage,
938
- }
939
-
940
- /// Requests the current balance of an account.
941
- pub struct RPCAccountBalance<T: Transport, State> {
942
- transport: T,
943
- state: PhantomData<State>,
944
- }
945
-
946
- impl<T: Transport> RPCAccountBalance<T, RPCInit> {
947
- pub async fn send_request(
948
- mut transport: T,
949
- account: PublicKey,
950
- ) -> Result<RPCAccountBalance<T, RPCComplete>, T::Error> {
951
- let request = RPCAccountBalanceRequest { account };
952
- transport.write_request(&request).await?;
953
-
954
- Ok(RPCAccountBalance {
955
- transport,
956
- state: PhantomData,
957
- })
958
- }
959
- }
960
-
961
- impl<T: Transport> RPCAccountBalance<T, RPCComplete> {
962
- pub async fn complete(mut self) -> Result<RPCAccountBalanceResult, T::Error> {
963
- let response: RPCAccountBalanceResponse = self.transport.read_response().await?;
964
- Ok(RPCAccountBalanceResult {
965
- balance: response.balance,
966
- usage: Usage::default(),
967
- })
968
- }
969
- }
970
-
971
- /// FormContractParams contains the parameters for forming a new contract.
972
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
973
- struct FormContractParams {
974
- pub renter_public_key: PublicKey,
975
- pub renter_address: Address,
976
- pub allowance: Currency,
977
- pub collateral: Currency,
978
- pub proof_height: u64,
979
- }
980
-
981
- /// RPCFormContractRequest is the request type for RPCFormContract.
982
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
983
- struct RPCFormContractRequest {
984
- pub prices: HostPrices,
985
- pub contract: FormContractParams,
986
- pub miner_fee: Currency,
987
- pub basis: ChainIndex,
988
- pub renter_inputs: Vec<SiacoinElement>,
989
- pub renter_parents: Vec<Transaction>,
990
- }
991
- impl_rpc_request!(RPCFormContractRequest, "FormContract");
992
-
993
- /// RenterFormContractSignaturesResponse contains the renter's contract signature and
994
- /// Siacoin input signatures for the contract formation transaction.
995
- ///
996
- /// At this point, the host has enough information to broadcast the formation.
997
- #[derive(Debug, PartialEq, AsyncSiaEncode, AsyncSiaDecode)]
998
- struct RenterFormContractSignaturesResponse {
999
- pub renter_contract_signature: Signature,
1000
- pub renter_satisfied_policies: Vec<SatisfiedPolicy>,
1001
- }
1002
- impl_rpc_response!(RenterFormContractSignaturesResponse);
1003
-
1004
- pub struct RPCFormContractParams {
1005
- state: ChainState,
1006
- prices: HostPrices,
1007
- contract: FormContractParams,
1008
- host_public_key: PublicKey,
1009
- host_address: Address,
1010
- }
1011
-
1012
- pub struct RPCFormContract<T, S, B, State>
1013
- where
1014
- T: Transport,
1015
- S: RenterContractSigner,
1016
- B: TransactionBuilder,
1017
- {
1018
- transport: T,
1019
- contract_signer: S,
1020
- transaction_builder: B,
1021
- state: PhantomData<State>,
1022
-
1023
- usage: Usage,
1024
- chain_state: ChainState,
1025
- contract: FileContract,
1026
- formation_transaction: Transaction,
1027
- renter_inputs_len: usize,
1028
- }
1029
-
1030
- impl<T: Transport, S: RenterContractSigner, B: TransactionBuilder, State>
1031
- RPCFormContract<T, S, B, State>
1032
- {
1033
- pub fn file_contract(&self) -> &FileContract {
1034
- &self.contract
1035
- }
1036
-
1037
- pub fn formation_transaction(&self) -> &Transaction {
1038
- &self.formation_transaction
1039
- }
1040
-
1041
- pub fn renter_inputs(&self) -> &[SiacoinInput] {
1042
- &self.formation_transaction.siacoin_inputs[..self.renter_inputs_len]
1043
- }
1044
- }
1045
-
1046
- pub struct RPCFormContractResult {
1047
- pub basis: ChainIndex,
1048
- pub transaction_set: Vec<Transaction>,
1049
- pub contract: FileContract,
1050
- pub usage: Usage,
1051
- }
1052
-
1053
- impl<T: Transport, S: RenterContractSigner, B: TransactionBuilder>
1054
- RPCFormContract<T, S, B, RPCInit>
1055
- {
1056
- pub async fn send_request(
1057
- mut transport: T,
1058
- contract_signer: S,
1059
- transaction_builder: B,
1060
- params: RPCFormContractParams,
1061
- ) -> Result<RPCFormContract<T, S, B, RPCAwaitingHostInputs>, T::Error> {
1062
- let usage = Usage::form_contract(&params.prices);
1063
- let mut contract = FileContract {
1064
- revision_number: 0,
1065
- capacity: 0,
1066
- filesize: 0,
1067
- file_merkle_root: Hash256::default(),
1068
- proof_height: params.contract.proof_height,
1069
- expiration_height: params.contract.proof_height + 144,
1070
- renter_output: SiacoinOutput {
1071
- address: params.contract.renter_address.clone(),
1072
- value: params.contract.allowance,
1073
- },
1074
- host_output: SiacoinOutput {
1075
- address: params.host_address,
1076
- value: params.contract.collateral + params.prices.contract_price,
1077
- },
1078
- missed_host_value: params.contract.collateral,
1079
- total_collateral: params.contract.collateral,
1080
- host_public_key: params.host_public_key,
1081
- renter_public_key: params.contract.renter_public_key,
1082
-
1083
- host_signature: Signature::default(),
1084
- renter_signature: Signature::default(),
1085
- };
1086
- contract.renter_signature = contract_signer.sign(contract.sig_hash(&params.state).as_ref());
1087
-
1088
- let miner_fee = transaction_builder.miner_fee() * Currency::new(1000);
1089
- let mut formation_txn = Transaction {
1090
- miner_fee,
1091
- ..Default::default()
1092
- };
1093
-
1094
- let renter_fund_amount = params.contract.allowance
1095
- + params.prices.contract_price
1096
- + miner_fee
1097
- + contract.tax(&params.state);
1098
- let renter_basis = transaction_builder
1099
- .fund_transaction(&mut formation_txn, renter_fund_amount)
1100
- .map_err(Error::from)?;
1101
-
1102
- let request = RPCFormContractRequest {
1103
- prices: params.prices,
1104
- miner_fee,
1105
- contract: params.contract,
1106
- basis: renter_basis,
1107
- renter_inputs: formation_txn
1108
- .siacoin_inputs
1109
- .iter()
1110
- .map(|si| si.parent.clone())
1111
- .collect(),
1112
- renter_parents: Vec::new(),
1113
- };
1114
- transport.write_request(&request).await?;
1115
-
1116
- Ok(RPCFormContract {
1117
- transport,
1118
- contract_signer,
1119
- transaction_builder,
1120
- state: PhantomData,
1121
- usage,
1122
- chain_state: params.state,
1123
- contract,
1124
- renter_inputs_len: formation_txn.siacoin_inputs.len(),
1125
- formation_transaction: formation_txn,
1126
- })
1127
- }
1128
- }
1129
-
1130
- impl<T: Transport, S: RenterContractSigner, B: TransactionBuilder>
1131
- RPCFormContract<T, S, B, RPCAwaitingHostInputs>
1132
- {
1133
- pub async fn receive_host_inputs(
1134
- mut self,
1135
- ) -> Result<RPCFormContract<T, S, B, RPCAwaitingRenterSignatures>, T::Error> {
1136
- let host_inputs_response: HostInputsResponse = self.transport.read_response().await?;
1137
- let mut formation_txn = self.formation_transaction;
1138
-
1139
- let host_funding = self.contract.total_collateral;
1140
- let host_sum: Currency = host_inputs_response
1141
- .host_inputs
1142
- .iter()
1143
- .map(|si| si.parent.siacoin_output.value)
1144
- .sum();
1145
- if host_sum < host_funding {
1146
- return Err(Error::NotEnoughHostFunds(host_sum, host_funding).into());
1147
- } else if host_sum > host_funding {
1148
- formation_txn.siacoin_outputs.push(SiacoinOutput {
1149
- address: self.contract.host_output.address.clone(),
1150
- value: host_sum - host_funding,
1151
- });
1152
- }
1153
- formation_txn.siacoin_inputs = host_inputs_response.host_inputs;
1154
-
1155
- Ok(RPCFormContract {
1156
- transport: self.transport,
1157
- contract_signer: self.contract_signer,
1158
- transaction_builder: self.transaction_builder,
1159
- state: PhantomData,
1160
- usage: self.usage,
1161
- chain_state: self.chain_state,
1162
- contract: self.contract,
1163
- renter_inputs_len: self.renter_inputs_len,
1164
- formation_transaction: formation_txn,
1165
- })
1166
- }
1167
- }
1168
-
1169
- impl<T: Transport, S: RenterContractSigner, B: TransactionBuilder>
1170
- RPCFormContract<T, S, B, RPCAwaitingRenterSignatures>
1171
- {
1172
- pub fn host_inputs(&self) -> &[SiacoinInput] {
1173
- &self.formation_transaction.siacoin_inputs[self.renter_inputs_len..]
1174
- }
1175
-
1176
- pub async fn send_renter_signatures(
1177
- mut self,
1178
- ) -> Result<RPCFormContract<T, S, B, RPCComplete>, T::Error> {
1179
- let mut formation_txn = self.formation_transaction;
1180
- let mut contract = self.contract;
1181
-
1182
- self.contract_signer
1183
- .sign_revision(&self.chain_state, &mut contract);
1184
- self.transaction_builder
1185
- .sign_transaction(&mut formation_txn)
1186
- .map_err(Error::from)?;
1187
-
1188
- let renter_sigs_response = RenterFormContractSignaturesResponse {
1189
- renter_contract_signature: contract.renter_signature.clone(),
1190
- renter_satisfied_policies: formation_txn.siacoin_inputs[..self.renter_inputs_len]
1191
- .iter()
1192
- .map(|si| si.satisfied_policy.clone())
1193
- .collect(),
1194
- };
1195
- self.transport.write_response(&renter_sigs_response).await?;
1196
-
1197
- Ok(RPCFormContract {
1198
- transport: self.transport,
1199
- contract_signer: self.contract_signer,
1200
- transaction_builder: self.transaction_builder,
1201
- state: PhantomData,
1202
- usage: self.usage,
1203
- chain_state: self.chain_state,
1204
- renter_inputs_len: self.renter_inputs_len,
1205
- contract,
1206
- formation_transaction: formation_txn,
1207
- })
1208
- }
1209
- }
1210
-
1211
- impl<T: Transport, S: RenterContractSigner, B: TransactionBuilder>
1212
- RPCFormContract<T, S, B, RPCComplete>
1213
- {
1214
- pub async fn complete(mut self) -> Result<RPCFormContractResult, T::Error> {
1215
- let resp: TransactionSetResponse = self.transport.read_response().await?;
1216
- let formation_txn = resp
1217
- .transaction_set
1218
- .last()
1219
- .ok_or(Error::ExpectedTransactionSet)?;
1220
- if formation_txn.file_contracts.len() != 1 {
1221
- return Err(
1222
- Error::ExpectedContractTransaction(formation_txn.file_contracts.len()).into(),
1223
- );
1224
- }
1225
- let contract = formation_txn.file_contracts.first().unwrap().clone();
1226
-
1227
- Ok(RPCFormContractResult {
1228
- basis: resp.basis,
1229
- transaction_set: resp.transaction_set,
1230
- contract,
1231
- usage: self.usage,
1232
- })
1233
- }
1234
- }
1235
-
1236
- #[cfg(test)]
1237
- mod test {
1238
- use bytes::BytesMut;
1239
- use chrono::DateTime;
1240
- use std::io::Cursor;
1241
- use tokio::io::AsyncWriteExt;
1242
-
1243
- use super::*;
1244
-
1245
- struct BufStream {
1246
- buf: Cursor<Vec<u8>>,
1247
- }
1248
-
1249
- impl BufStream {
1250
- fn new() -> Self {
1251
- Self {
1252
- buf: Cursor::new(Vec::new()),
1253
- }
1254
- }
1255
-
1256
- fn inner(self) -> Vec<u8> {
1257
- self.buf.clone().into_inner()
1258
- }
1259
- }
1260
-
1261
- impl From<Cursor<Vec<u8>>> for BufStream {
1262
- fn from(buf: Cursor<Vec<u8>>) -> Self {
1263
- Self { buf }
1264
- }
1265
- }
1266
-
1267
- impl AsyncDecoder for BufStream {
1268
- type Error = Error;
1269
- async fn decode_buf(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
1270
- self.buf.decode_buf(buf).await?;
1271
- Ok(())
1272
- }
1273
- }
1274
-
1275
- impl AsyncEncoder for BufStream {
1276
- type Error = Error;
1277
- async fn encode_buf(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
1278
- self.buf.encode_buf(buf).await?;
1279
- Ok(())
1280
- }
1281
- }
1282
-
1283
- impl Transport for BufStream {
1284
- type Error = Error;
1285
-
1286
- async fn write_request<R: RPCRequest>(&mut self, request: &R) -> Result<(), Self::Error> {
1287
- request.encode_request(self).await?;
1288
- self.buf.flush().await?;
1289
- self.buf.set_position(0); // ready to read
1290
- Ok(())
1291
- }
1292
-
1293
- async fn write_bytes(&mut self, data: Bytes) -> Result<(), Self::Error> {
1294
- self.buf.write_all(&data).await?;
1295
- Ok(())
1296
- }
1297
-
1298
- async fn write_response<R: RPCResponse>(
1299
- &mut self,
1300
- response: &R,
1301
- ) -> Result<(), Self::Error> {
1302
- response.encode_response(self).await?;
1303
- self.buf.flush().await?;
1304
- self.buf.set_position(0); // ready to read
1305
- Ok(())
1306
- }
1307
-
1308
- async fn read_response<R: RPCResponse>(&mut self) -> Result<R, Self::Error> {
1309
- R::decode_response(self).await
1310
- }
1311
- }
1312
-
1313
- const TEST_PRICES: HostPrices = HostPrices {
1314
- contract_price: Currency::siacoins(1),
1315
- collateral: Currency::siacoins(2),
1316
- storage_price: Currency::siacoins(3),
1317
- ingress_price: Currency::siacoins(4),
1318
- egress_price: Currency::siacoins(5),
1319
- free_sector_price: Currency::siacoins(6),
1320
- tip_height: 7,
1321
- valid_until: DateTime::UNIX_EPOCH,
1322
- signature: Signature::new([0u8; 64]),
1323
- };
1324
-
1325
- const TEST_ACCOUNT_TOKEN: AccountToken = AccountToken {
1326
- host_key: PublicKey::new({
1327
- let mut bytes = [0u8; 32];
1328
- bytes[0] = 10;
1329
- bytes
1330
- }),
1331
- account: PublicKey::new({
1332
- let mut bytes = [0u8; 32];
1333
- bytes[0] = 11;
1334
- bytes
1335
- }),
1336
- valid_until: DateTime::UNIX_EPOCH,
1337
- signature: Signature::new({
1338
- let mut bytes = [0u8; 64];
1339
- bytes[0] = 13;
1340
- bytes
1341
- }),
1342
- };
1343
-
1344
- #[tokio::test]
1345
- async fn test_write_request() {
1346
- const EXPECTED_HEX: &str = "52656164536563746f72000000000000000000a1edccce1bc2d300000000000000000042db999d3784a7010000000000000000e3c8666c53467b02000000000000000084b6333b6f084f03000000000000000025a4000a8bca22040000000000000000c691cdd8a68cf604000000000007000000000000000800000000000000090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f000000000000001000000000000000";
1347
-
1348
- let mut prices = TEST_PRICES;
1349
- prices.valid_until = DateTime::from_timestamp_secs(8).unwrap();
1350
- prices.signature = Signature::new({
1351
- let mut bytes = [0u8; 64];
1352
- bytes[0] = 9;
1353
- bytes
1354
- });
1355
-
1356
- let mut token = TEST_ACCOUNT_TOKEN;
1357
- token.valid_until = DateTime::from_timestamp_secs(12).unwrap();
1358
- token.signature = Signature::new({
1359
- let mut bytes = [0u8; 64];
1360
- bytes[0] = 13;
1361
- bytes
1362
- });
1363
-
1364
- let req = RPCReadSectorRequest {
1365
- prices,
1366
- token,
1367
- root: Hash256::new({
1368
- let mut bytes = [0u8; 32];
1369
- bytes[0] = 14;
1370
- bytes
1371
- }),
1372
- offset: 15,
1373
- length: 16,
1374
- };
1375
-
1376
- let mut transport = BufStream::new();
1377
- transport.write_request(&req).await.unwrap();
1378
- assert_eq!(transport.inner(), hex::decode(EXPECTED_HEX).unwrap());
1379
- }
1380
-
1381
- #[tokio::test]
1382
- async fn test_read_response() {
1383
- const HEX_BYTES: &str = "00030000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000";
1384
-
1385
- let mut transport = BufStream::from(Cursor::new(hex::decode(HEX_BYTES).unwrap()));
1386
- let resp: RPCFreeSectorsResponse = transport.read_response().await.unwrap();
1387
-
1388
- let expected = RPCFreeSectorsResponse {
1389
- old_subtree_hashes: vec![
1390
- Hash256::new({
1391
- let mut bytes = [0u8; 32];
1392
- bytes[0] = 1;
1393
- bytes
1394
- }),
1395
- Hash256::new({
1396
- let mut bytes = [0u8; 32];
1397
- bytes[0] = 2;
1398
- bytes
1399
- }),
1400
- Hash256::new({
1401
- let mut bytes = [0u8; 32];
1402
- bytes[0] = 3;
1403
- bytes
1404
- }),
1405
- ],
1406
- old_leaf_hashes: vec![
1407
- Hash256::new({
1408
- let mut bytes = [0u8; 32];
1409
- bytes[0] = 4;
1410
- bytes
1411
- }),
1412
- Hash256::new({
1413
- let mut bytes = [0u8; 32];
1414
- bytes[0] = 5;
1415
- bytes
1416
- }),
1417
- Hash256::new({
1418
- let mut bytes = [0u8; 32];
1419
- bytes[0] = 6;
1420
- bytes
1421
- }),
1422
- ],
1423
- new_merkle_root: Hash256::new({
1424
- let mut bytes = [0u8; 32];
1425
- bytes[0] = 7;
1426
- bytes
1427
- }),
1428
- };
1429
-
1430
- assert_eq!(resp, expected);
1431
- }
1432
-
1433
- #[tokio::test]
1434
- async fn test_response_error() {
1435
- const HEX_BYTES: &str = "01010b00000000000000666f6f206261722062617a";
1436
-
1437
- let mut transport = BufStream::from(Cursor::new(hex::decode(HEX_BYTES).unwrap()));
1438
- let err = transport
1439
- .read_response::<RPCReadSectorResponse>()
1440
- .await
1441
- .unwrap_err();
1442
-
1443
- let expected_err = RPCError {
1444
- code: 1,
1445
- description: "foo bar baz".to_string(),
1446
- };
1447
-
1448
- match err {
1449
- Error::RPC(rpc_err) => {
1450
- assert_eq!(rpc_err, expected_err);
1451
- }
1452
- _ => panic!("Expected RPCError, got {err:?}"),
1453
- }
1454
- }
1455
-
1456
- #[tokio::test]
1457
- async fn test_rpc_write_sector_send_request() {
1458
- let mut data = BytesMut::zeroed(SECTOR_SIZE);
1459
- rand::fill(&mut data[..]);
1460
- let root = merkle::sector_root(&data);
1461
- let transport = BufStream::new();
1462
- let rpc =
1463
- RPCWriteSector::send_request(transport, TEST_PRICES, TEST_ACCOUNT_TOKEN, data.freeze())
1464
- .await
1465
- .unwrap();
1466
- assert_eq!(rpc.root, root);
1467
- }
1468
-
1469
- #[tokio::test]
1470
- async fn test_rpc_write_sector_complete() {
1471
- let mut data = BytesMut::zeroed(SECTOR_SIZE);
1472
- rand::fill(&mut data[..]);
1473
- let root = merkle::sector_root(&data);
1474
-
1475
- // helper to prepare the transport and fill it with the expected host
1476
- // response
1477
- let transport = async || -> BufStream {
1478
- let mut transport = BufStream::new();
1479
- let response = RPCWriteSectorResponse { root };
1480
- transport.write_response(&response).await.unwrap();
1481
- transport
1482
- };
1483
-
1484
- // perform the RPC with the correct root
1485
- let rpc = RPCWriteSector::<_, RPCComplete> {
1486
- root,
1487
- transport: transport().await,
1488
- usage: Usage::default(),
1489
- state: PhantomData,
1490
- };
1491
- rpc.complete().await.unwrap();
1492
-
1493
- // change the root to force a mismatch
1494
- let rpc = RPCWriteSector::<_, RPCComplete> {
1495
- root: Hash256::default(),
1496
- transport: transport().await,
1497
- usage: Usage::default(),
1498
- state: PhantomData,
1499
- };
1500
- if let Err(Error::SectorRootMismatch { expected, got }) = rpc.complete().await {
1501
- assert_eq!(expected, Hash256::default());
1502
- assert_eq!(got, root);
1503
- } else {
1504
- panic!("Expected SectorRootMismatch");
1505
- }
1506
- }
1507
- }