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.
Files changed (112) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +179 -0
  3. package/package.json +29 -0
  4. package/template/CLAUDE.md +160 -0
  5. package/template/README.md +102 -0
  6. package/template/_gitignore +5 -0
  7. package/template/biome.json +40 -0
  8. package/template/index.html +13 -0
  9. package/template/package.json +30 -0
  10. package/template/rust/README.md +16 -0
  11. package/template/rust/sia-sdk-rs/.changeset/added_cancel_function_to_cancel_inflight_packed_uploads.md +6 -0
  12. package/template/rust/sia-sdk-rs/.changeset/check_if_we_have_enough_hosts_prior_to_encoding_in_upload_slabs.md +16 -0
  13. package/template/rust/sia-sdk-rs/.changeset/fix_slab_length_in_packed_object.md +5 -0
  14. package/template/rust/sia-sdk-rs/.changeset/fix_upload_racing_race_conditon.md +13 -0
  15. package/template/rust/sia-sdk-rs/.changeset/improved_parallelism_of_packed_uploads.md +5 -0
  16. package/template/rust/sia-sdk-rs/.changeset/progress_callback_will_now_be_called_as_expected_for_packed_uploads.md +5 -0
  17. package/template/rust/sia-sdk-rs/.github/dependabot.yml +10 -0
  18. package/template/rust/sia-sdk-rs/.github/workflows/main.yml +36 -0
  19. package/template/rust/sia-sdk-rs/.github/workflows/prepare-release.yml +34 -0
  20. package/template/rust/sia-sdk-rs/.github/workflows/release.yml +30 -0
  21. package/template/rust/sia-sdk-rs/.rustfmt.toml +4 -0
  22. package/template/rust/sia-sdk-rs/Cargo.lock +4127 -0
  23. package/template/rust/sia-sdk-rs/Cargo.toml +3 -0
  24. package/template/rust/sia-sdk-rs/LICENSE +21 -0
  25. package/template/rust/sia-sdk-rs/README.md +30 -0
  26. package/template/rust/sia-sdk-rs/indexd/CHANGELOG.md +79 -0
  27. package/template/rust/sia-sdk-rs/indexd/Cargo.toml +79 -0
  28. package/template/rust/sia-sdk-rs/indexd/benches/upload.rs +258 -0
  29. package/template/rust/sia-sdk-rs/indexd/src/app_client.rs +1710 -0
  30. package/template/rust/sia-sdk-rs/indexd/src/builder.rs +354 -0
  31. package/template/rust/sia-sdk-rs/indexd/src/download.rs +379 -0
  32. package/template/rust/sia-sdk-rs/indexd/src/hosts.rs +659 -0
  33. package/template/rust/sia-sdk-rs/indexd/src/lib.rs +827 -0
  34. package/template/rust/sia-sdk-rs/indexd/src/mock.rs +162 -0
  35. package/template/rust/sia-sdk-rs/indexd/src/object_encryption.rs +125 -0
  36. package/template/rust/sia-sdk-rs/indexd/src/quic.rs +575 -0
  37. package/template/rust/sia-sdk-rs/indexd/src/rhp4.rs +52 -0
  38. package/template/rust/sia-sdk-rs/indexd/src/slabs.rs +497 -0
  39. package/template/rust/sia-sdk-rs/indexd/src/upload.rs +629 -0
  40. package/template/rust/sia-sdk-rs/indexd/src/wasm_time.rs +41 -0
  41. package/template/rust/sia-sdk-rs/indexd/src/web_transport.rs +398 -0
  42. package/template/rust/sia-sdk-rs/indexd_ffi/CHANGELOG.md +76 -0
  43. package/template/rust/sia-sdk-rs/indexd_ffi/Cargo.toml +47 -0
  44. package/template/rust/sia-sdk-rs/indexd_ffi/examples/python/README.md +10 -0
  45. package/template/rust/sia-sdk-rs/indexd_ffi/examples/python/example.py +130 -0
  46. package/template/rust/sia-sdk-rs/indexd_ffi/src/bin/uniffi-bindgen.rs +3 -0
  47. package/template/rust/sia-sdk-rs/indexd_ffi/src/builder.rs +377 -0
  48. package/template/rust/sia-sdk-rs/indexd_ffi/src/io.rs +155 -0
  49. package/template/rust/sia-sdk-rs/indexd_ffi/src/lib.rs +1039 -0
  50. package/template/rust/sia-sdk-rs/indexd_ffi/src/logging.rs +58 -0
  51. package/template/rust/sia-sdk-rs/indexd_ffi/src/tls.rs +23 -0
  52. package/template/rust/sia-sdk-rs/indexd_wasm/Cargo.toml +33 -0
  53. package/template/rust/sia-sdk-rs/indexd_wasm/src/lib.rs +818 -0
  54. package/template/rust/sia-sdk-rs/knope.toml +54 -0
  55. package/template/rust/sia-sdk-rs/sia_derive/CHANGELOG.md +38 -0
  56. package/template/rust/sia-sdk-rs/sia_derive/Cargo.toml +19 -0
  57. package/template/rust/sia-sdk-rs/sia_derive/src/lib.rs +278 -0
  58. package/template/rust/sia-sdk-rs/sia_sdk/CHANGELOG.md +91 -0
  59. package/template/rust/sia-sdk-rs/sia_sdk/Cargo.toml +59 -0
  60. package/template/rust/sia-sdk-rs/sia_sdk/benches/merkle_root.rs +12 -0
  61. package/template/rust/sia-sdk-rs/sia_sdk/src/blake2.rs +22 -0
  62. package/template/rust/sia-sdk-rs/sia_sdk/src/consensus.rs +767 -0
  63. package/template/rust/sia-sdk-rs/sia_sdk/src/encoding/v1.rs +257 -0
  64. package/template/rust/sia-sdk-rs/sia_sdk/src/encoding/v2.rs +291 -0
  65. package/template/rust/sia-sdk-rs/sia_sdk/src/encoding.rs +26 -0
  66. package/template/rust/sia-sdk-rs/sia_sdk/src/encoding_async/v2.rs +367 -0
  67. package/template/rust/sia-sdk-rs/sia_sdk/src/encoding_async.rs +6 -0
  68. package/template/rust/sia-sdk-rs/sia_sdk/src/encryption.rs +303 -0
  69. package/template/rust/sia-sdk-rs/sia_sdk/src/erasure_coding.rs +347 -0
  70. package/template/rust/sia-sdk-rs/sia_sdk/src/lib.rs +15 -0
  71. package/template/rust/sia-sdk-rs/sia_sdk/src/macros.rs +435 -0
  72. package/template/rust/sia-sdk-rs/sia_sdk/src/merkle.rs +112 -0
  73. package/template/rust/sia-sdk-rs/sia_sdk/src/rhp/merkle.rs +357 -0
  74. package/template/rust/sia-sdk-rs/sia_sdk/src/rhp/rpc.rs +1507 -0
  75. package/template/rust/sia-sdk-rs/sia_sdk/src/rhp/types.rs +146 -0
  76. package/template/rust/sia-sdk-rs/sia_sdk/src/rhp.rs +7 -0
  77. package/template/rust/sia-sdk-rs/sia_sdk/src/seed.rs +278 -0
  78. package/template/rust/sia-sdk-rs/sia_sdk/src/signing.rs +236 -0
  79. package/template/rust/sia-sdk-rs/sia_sdk/src/types/common.rs +677 -0
  80. package/template/rust/sia-sdk-rs/sia_sdk/src/types/currency.rs +450 -0
  81. package/template/rust/sia-sdk-rs/sia_sdk/src/types/specifier.rs +110 -0
  82. package/template/rust/sia-sdk-rs/sia_sdk/src/types/spendpolicy.rs +778 -0
  83. package/template/rust/sia-sdk-rs/sia_sdk/src/types/utils.rs +117 -0
  84. package/template/rust/sia-sdk-rs/sia_sdk/src/types/v1.rs +1737 -0
  85. package/template/rust/sia-sdk-rs/sia_sdk/src/types/v2.rs +1726 -0
  86. package/template/rust/sia-sdk-rs/sia_sdk/src/types/work.rs +59 -0
  87. package/template/rust/sia-sdk-rs/sia_sdk/src/types.rs +16 -0
  88. package/template/scripts/setup-rust.js +29 -0
  89. package/template/src/App.tsx +13 -0
  90. package/template/src/components/DevNote.tsx +21 -0
  91. package/template/src/components/auth/ApproveScreen.tsx +84 -0
  92. package/template/src/components/auth/AuthFlow.tsx +77 -0
  93. package/template/src/components/auth/ConnectScreen.tsx +214 -0
  94. package/template/src/components/auth/LoadingScreen.tsx +8 -0
  95. package/template/src/components/auth/RecoveryScreen.tsx +182 -0
  96. package/template/src/components/upload/UploadZone.tsx +314 -0
  97. package/template/src/index.css +9 -0
  98. package/template/src/lib/constants.ts +8 -0
  99. package/template/src/lib/format.ts +35 -0
  100. package/template/src/lib/hex.ts +13 -0
  101. package/template/src/lib/sdk.ts +25 -0
  102. package/template/src/lib/wasm-env.ts +5 -0
  103. package/template/src/main.tsx +12 -0
  104. package/template/src/stores/auth.ts +86 -0
  105. package/template/tsconfig.app.json +31 -0
  106. package/template/tsconfig.json +7 -0
  107. package/template/tsconfig.node.json +26 -0
  108. package/template/vite.config.ts +18 -0
  109. package/template/wasm/indexd_wasm/indexd_wasm.d.ts +309 -0
  110. package/template/wasm/indexd_wasm/indexd_wasm.js +1507 -0
  111. package/template/wasm/indexd_wasm/indexd_wasm_bg.wasm +0 -0
  112. package/template/wasm/indexd_wasm/package.json +31 -0
@@ -0,0 +1,1726 @@
1
+ use std::io;
2
+
3
+ use crate::consensus::ChainState;
4
+ use crate::encoding::{self, SiaDecodable, SiaDecode, SiaEncodable, SiaEncode};
5
+ use crate::encoding_async::{
6
+ AsyncDecoder, AsyncEncoder, AsyncSiaDecodable, AsyncSiaDecode, AsyncSiaEncodable,
7
+ AsyncSiaEncode,
8
+ };
9
+ use blake2b_simd::Params;
10
+ use serde::de::{Error, MapAccess, Visitor};
11
+ use serde::ser::SerializeStruct;
12
+ use serde::{Deserialize, Serialize};
13
+ use serde_json::json;
14
+ use thiserror::Error;
15
+
16
+ use crate::signing::{PublicKey, Signature};
17
+ use crate::types::common::BlockID;
18
+
19
+ use super::{
20
+ Address, AttestationID, ChainIndex, Currency, FileContractID, Hash256, Leaf, SiacoinOutput,
21
+ SiacoinOutputID, SiafundOutput, SiafundOutputID, StateElement, TransactionID,
22
+ };
23
+
24
+ // expose spend policies
25
+ pub use super::spendpolicy::*;
26
+
27
+ #[derive(Debug, PartialEq, Eq, Clone, Copy)]
28
+ pub enum Protocol {
29
+ SiaMux,
30
+ QUIC,
31
+ }
32
+
33
+ #[derive(Debug, Error)]
34
+ pub enum ProtocolParseError {
35
+ #[error("invalid protocol {0}")]
36
+ InvalidProtocol(String),
37
+ }
38
+
39
+ impl Protocol {
40
+ pub const fn as_str(&self) -> &'static str {
41
+ match self {
42
+ Protocol::SiaMux => "siamux",
43
+ Protocol::QUIC => "quic",
44
+ }
45
+ }
46
+ }
47
+
48
+ impl TryFrom<String> for Protocol {
49
+ type Error = ProtocolParseError;
50
+
51
+ fn try_from(value: String) -> Result<Self, Self::Error> {
52
+ match value.as_str() {
53
+ "siamux" => Ok(Protocol::SiaMux),
54
+ "quic" => Ok(Protocol::QUIC),
55
+ _ => Err(ProtocolParseError::InvalidProtocol(value)),
56
+ }
57
+ }
58
+ }
59
+
60
+ impl Serialize for Protocol {
61
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
62
+ where
63
+ S: serde::Serializer,
64
+ {
65
+ serializer.serialize_str(self.as_str())
66
+ }
67
+ }
68
+
69
+ impl<'de> Deserialize<'de> for Protocol {
70
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
71
+ where
72
+ D: serde::Deserializer<'de>,
73
+ {
74
+ let s = String::deserialize(deserializer)?;
75
+ Protocol::try_from(s).map_err(|e| D::Error::custom(format!("{e:?}")))
76
+ }
77
+ }
78
+
79
+ impl SiaEncodable for Protocol {
80
+ fn encode<W: io::Write>(&self, w: &mut W) -> encoding::Result<()> {
81
+ self.as_str().as_bytes().encode(w)
82
+ }
83
+ }
84
+
85
+ impl SiaDecodable for Protocol {
86
+ fn decode<R: io::Read>(r: &mut R) -> encoding::Result<Self> {
87
+ let s = String::decode(r)?;
88
+ let v = Protocol::try_from(s).map_err(|e| encoding::Error::InvalidValue(e.to_string()))?;
89
+ Ok(v)
90
+ }
91
+ }
92
+
93
+ impl AsyncSiaEncodable for Protocol {
94
+ async fn encode_async<E: AsyncEncoder>(&self, e: &mut E) -> Result<(), E::Error> {
95
+ self.as_str().as_bytes().encode_async(e).await
96
+ }
97
+ }
98
+
99
+ impl AsyncSiaDecodable for Protocol {
100
+ async fn decode_async<D: AsyncDecoder>(d: &mut D) -> Result<Self, D::Error> {
101
+ let s = String::decode_async(d).await?;
102
+ let v = Protocol::try_from(s).map_err(|e| encoding::Error::InvalidValue(e.to_string()))?;
103
+ Ok(v)
104
+ }
105
+ }
106
+
107
+ #[derive(
108
+ Debug,
109
+ PartialEq,
110
+ Clone,
111
+ SiaEncode,
112
+ SiaDecode,
113
+ AsyncSiaEncode,
114
+ AsyncSiaDecode,
115
+ Serialize,
116
+ Deserialize,
117
+ )]
118
+ #[serde(rename_all = "camelCase")]
119
+ pub struct NetAddress {
120
+ pub protocol: Protocol,
121
+ pub address: String,
122
+ }
123
+
124
+ /// An Attestation associates a key-value pair with an identity. For example,
125
+ /// hosts attest to their network address by setting Key to "HostAnnouncement"
126
+ /// and Value to their address, thereby allowing renters to discover them.
127
+ /// Generally, an attestation for a particular key is considered to overwrite any
128
+ /// previous attestations with the same key. (This allows hosts to announce a new
129
+ /// network address, for example.)
130
+ #[derive(
131
+ Debug, PartialEq, Serialize, Deserialize, SiaEncode, SiaDecode, AsyncSiaEncode, AsyncSiaDecode,
132
+ )]
133
+ #[serde(rename_all = "camelCase")]
134
+ pub struct Attestation {
135
+ pub public_key: PublicKey,
136
+ pub key: String,
137
+ #[serde(with = "crate::types::utils::base64")]
138
+ pub value: Vec<u8>,
139
+
140
+ pub signature: Signature,
141
+ }
142
+
143
+ impl Attestation {
144
+ fn encode_semantics<W: std::io::Write>(&self, w: &mut W) -> encoding::Result<()> {
145
+ self.public_key.encode(w)?;
146
+ self.key.encode(w)?;
147
+ self.value.encode(w)?;
148
+ [0u8; 64].encode(w) // empty sig
149
+ }
150
+
151
+ pub fn sig_hash(&self, cs: &ChainState) -> Hash256 {
152
+ let mut state = Params::new().hash_length(32).to_state();
153
+ state.update("sia/sig/attestation|".as_bytes());
154
+ state.update(cs.replay_prefix());
155
+ self.encode_semantics(&mut state).unwrap();
156
+ state.finalize().into()
157
+ }
158
+ }
159
+
160
+ /// A FileContract is a storage agreement between a renter and a host. It
161
+ /// consists of a bidirectional payment channel that resolves as either "valid"
162
+ /// or "missed" depending on whether a valid StorageProof is submitted for the
163
+ /// contract.
164
+ #[derive(
165
+ Debug,
166
+ PartialEq,
167
+ Serialize,
168
+ Deserialize,
169
+ SiaEncode,
170
+ SiaDecode,
171
+ AsyncSiaEncode,
172
+ AsyncSiaDecode,
173
+ Clone,
174
+ )]
175
+ #[serde(rename_all = "camelCase")]
176
+ pub struct FileContract {
177
+ pub capacity: u64,
178
+ pub filesize: u64,
179
+ pub file_merkle_root: Hash256,
180
+ pub proof_height: u64,
181
+ pub expiration_height: u64,
182
+ pub renter_output: SiacoinOutput,
183
+ pub host_output: SiacoinOutput,
184
+ pub missed_host_value: Currency,
185
+ pub total_collateral: Currency,
186
+ pub renter_public_key: PublicKey,
187
+ pub host_public_key: PublicKey,
188
+ pub revision_number: u64,
189
+
190
+ pub renter_signature: Signature,
191
+ pub host_signature: Signature,
192
+ }
193
+
194
+ impl FileContract {
195
+ pub fn tax(&self, cs: &ChainState) -> Currency {
196
+ let tax = (self.renter_output.value + self.host_output.value) / Currency::new(25); // 4%
197
+ tax - (tax % Currency::new(cs.siafund_count() as u128))
198
+ }
199
+
200
+ fn encode_semantics<W: std::io::Write>(&self, w: &mut W) -> encoding::Result<()> {
201
+ self.capacity.encode(w)?;
202
+ self.filesize.encode(w)?;
203
+ self.file_merkle_root.encode(w)?;
204
+ self.proof_height.encode(w)?;
205
+ self.expiration_height.encode(w)?;
206
+ self.renter_output.encode(w)?;
207
+ self.host_output.encode(w)?;
208
+ self.missed_host_value.encode(w)?;
209
+ self.total_collateral.encode(w)?;
210
+ self.renter_public_key.encode(w)?;
211
+ self.host_public_key.encode(w)?;
212
+ self.revision_number.encode(w)?;
213
+ [0u8; 64].encode(w)?; // nil renter signature
214
+ [0u8; 64].encode(w)?; // nil host signature
215
+ Ok(())
216
+ }
217
+
218
+ pub fn sig_hash(&self, cs: &ChainState) -> Hash256 {
219
+ let mut state = Params::new().hash_length(32).to_state();
220
+ state.update("sia/sig/filecontract|".as_bytes());
221
+ state.update(cs.replay_prefix());
222
+ self.encode_semantics(&mut state).unwrap();
223
+ state.finalize().into()
224
+ }
225
+ }
226
+
227
+ /// A SiacoinElement is a record of a Siacoin UTXO within the state accumulator.
228
+ #[derive(
229
+ Debug,
230
+ PartialEq,
231
+ Serialize,
232
+ Deserialize,
233
+ SiaEncode,
234
+ SiaDecode,
235
+ AsyncSiaDecode,
236
+ AsyncSiaEncode,
237
+ Clone,
238
+ )]
239
+ #[serde(rename_all = "camelCase")]
240
+ pub struct SiacoinElement {
241
+ pub state_element: StateElement,
242
+ pub id: SiacoinOutputID,
243
+ pub siacoin_output: SiacoinOutput,
244
+ pub maturity_height: u64,
245
+ }
246
+
247
+ /// A SiafundElement is a record of a Siafund UTXO within the state accumulator.
248
+ #[derive(
249
+ Debug, PartialEq, Serialize, Deserialize, SiaEncode, SiaDecode, AsyncSiaDecode, AsyncSiaEncode,
250
+ )]
251
+ #[serde(rename_all = "camelCase")]
252
+ pub struct SiafundElement {
253
+ pub state_element: StateElement,
254
+ pub id: SiafundOutputID,
255
+ pub siafund_output: SiafundOutput,
256
+ pub claim_start: Currency,
257
+ }
258
+
259
+ /// A V2FileContractElement is a record of a FileContract within the state
260
+ /// accumulator.
261
+ #[derive(
262
+ Debug, PartialEq, Serialize, Deserialize, SiaEncode, SiaDecode, AsyncSiaDecode, AsyncSiaEncode,
263
+ )]
264
+ #[serde(rename_all = "camelCase")]
265
+ pub struct FileContractElement {
266
+ pub state_element: StateElement,
267
+ pub id: FileContractID,
268
+ pub v2_file_contract: FileContract,
269
+ }
270
+
271
+ /// A ChainIndexElement is a record of a ChainIndex within the state accumulator.
272
+ #[derive(
273
+ Debug, PartialEq, Serialize, Deserialize, SiaEncode, SiaDecode, AsyncSiaDecode, AsyncSiaEncode,
274
+ )]
275
+ #[serde(rename_all = "camelCase")]
276
+ pub struct ChainIndexElement {
277
+ pub state_element: StateElement,
278
+ pub id: BlockID,
279
+ pub chain_index: ChainIndex,
280
+ }
281
+
282
+ impl ChainIndexElement {
283
+ pub fn encode_semantics<W: std::io::Write>(&self, w: &mut W) -> encoding::Result<()> {
284
+ self.state_element.leaf_index.encode(w)?;
285
+ Vec::<Hash256>::new().encode(w)?; // empty merkle proof
286
+ self.id.encode(w)?;
287
+ self.chain_index.encode(w)?;
288
+ Ok(())
289
+ }
290
+ }
291
+
292
+ /// An AttestationElement is a record of an Attestation within the state
293
+ /// accumulator.
294
+ pub struct AttestationElement {
295
+ pub state_element: StateElement,
296
+ pub id: AttestationID,
297
+ pub attestation: Attestation,
298
+ }
299
+
300
+ /// A V2SiacoinInput represents a Siacoin UTXO that is being spent in a v2
301
+ /// transaction.
302
+ #[derive(
303
+ Debug, PartialEq, Serialize, Deserialize, SiaEncode, SiaDecode, AsyncSiaEncode, AsyncSiaDecode,
304
+ )]
305
+ #[serde(rename_all = "camelCase")]
306
+ pub struct SiacoinInput {
307
+ pub parent: SiacoinElement,
308
+ pub satisfied_policy: SatisfiedPolicy,
309
+ }
310
+
311
+ /// A V2SiafundInput represents a Siafund UTXO that is being spent in a v2
312
+ /// transaction.
313
+ #[derive(
314
+ Debug, PartialEq, Serialize, Deserialize, AsyncSiaDecode, AsyncSiaEncode, SiaEncode, SiaDecode,
315
+ )]
316
+ #[serde(rename_all = "camelCase")]
317
+ pub struct SiafundInput {
318
+ pub parent: SiafundElement,
319
+ pub claim_address: Address,
320
+ pub satisfied_policy: SatisfiedPolicy,
321
+ }
322
+
323
+ /// A FileContractRevision updates the state of an existing file contract.
324
+ #[derive(
325
+ Debug, PartialEq, Serialize, Deserialize, AsyncSiaDecode, AsyncSiaEncode, SiaEncode, SiaDecode,
326
+ )]
327
+ #[serde(rename_all = "camelCase")]
328
+ pub struct FileContractRevision {
329
+ pub parent: FileContractElement,
330
+ pub revision: FileContract,
331
+ }
332
+
333
+ impl FileContractRevision {
334
+ fn encode_semantics<W: std::io::Write>(&self, w: &mut W) -> encoding::Result<()> {
335
+ self.parent.id.encode(w)?;
336
+ self.revision.encode_semantics(w)?;
337
+ Ok(())
338
+ }
339
+ }
340
+
341
+ /// A FileContractRenewal renews a file contract with optional rollover
342
+ /// of any unspent funds.
343
+ #[derive(
344
+ Debug, PartialEq, Serialize, Deserialize, AsyncSiaDecode, AsyncSiaEncode, SiaEncode, SiaDecode,
345
+ )]
346
+ #[serde(rename_all = "camelCase")]
347
+ pub struct FileContractRenewal {
348
+ pub final_revision: FileContract,
349
+ pub new_contract: FileContract,
350
+ pub renter_rollover: Currency,
351
+ pub host_rollover: Currency,
352
+
353
+ // signatures cover above fields
354
+ pub renter_signature: Signature,
355
+ pub host_signature: Signature,
356
+ }
357
+
358
+ impl FileContractRenewal {
359
+ fn encode_semantics<W: std::io::Write>(&self, w: &mut W) -> encoding::Result<()> {
360
+ self.final_revision.encode_semantics(w)?;
361
+ self.new_contract.encode_semantics(w)?;
362
+ self.renter_rollover.encode(w)?;
363
+ self.host_rollover.encode(w)?;
364
+ [0u8; 64].encode(w)?; // empty renter sig
365
+ [0u8; 64].encode(w)?; // empty host sig
366
+ Ok(())
367
+ }
368
+
369
+ pub fn sig_hash(&self, cs: &ChainState) -> Hash256 {
370
+ let mut state = Params::new().hash_length(32).to_state();
371
+ state.update("sia/sig/filecontractrenewal|".as_bytes());
372
+ state.update(cs.replay_prefix());
373
+ self.encode_semantics(&mut state).unwrap();
374
+ state.finalize().into()
375
+ }
376
+ }
377
+
378
+ /// A StorageProof asserts the presence of a randomly-selected leaf within the
379
+ /// Merkle tree of a V2FileContract's data.
380
+ #[derive(
381
+ Debug, PartialEq, Serialize, Deserialize, AsyncSiaDecode, AsyncSiaEncode, SiaEncode, SiaDecode,
382
+ )]
383
+ #[serde(rename_all = "camelCase")]
384
+ pub struct StorageProof {
385
+ // Selecting the leaf requires a source of unpredictable entropy; we use the
386
+ // ID of the block at the contract's ProofHeight. The storage proof thus
387
+ // includes a proof that this ID is the correct ancestor.
388
+ //
389
+ // During validation, it is imperative to check that ProofIndex.Height
390
+ // matches the ProofHeight field of the contract's final revision;
391
+ // otherwise, the prover could use any ProofIndex, giving them control over
392
+ // the leaf index.
393
+ pub proof_index: ChainIndexElement,
394
+ // The leaf is always 64 bytes, extended with zeros if necessary.
395
+ pub leaf: Leaf,
396
+ pub proof: Vec<Hash256>,
397
+ }
398
+
399
+ impl StorageProof {
400
+ fn encode_semantics<W: std::io::Write>(&self, w: &mut W) -> encoding::Result<()> {
401
+ self.proof_index.encode_semantics(w)?;
402
+ self.leaf.encode(w)?;
403
+ self.proof.encode(w)?;
404
+ Ok(())
405
+ }
406
+ }
407
+
408
+ // A ContractResolution closes a v2 file contract's payment channel. There
409
+ /// are four ways a contract can be resolved:
410
+ ///
411
+ /// 1. The renter can finalize the contract's current state, preventing further
412
+ /// revisions and immediately creating its outputs.
413
+ ///
414
+ /// 2. The renter and host can jointly renew the contract. The old contract is
415
+ /// finalized, and a portion of its funds are "rolled over" into a new contract.
416
+ ///
417
+ /// 3. The host can submit a storage proof, asserting that it has faithfully
418
+ /// stored the contract data for the agreed-upon duration. A storage proof can only be submitted after the
419
+ /// contract's ProofHeight; this allows the renter (or host) to broadcast the
420
+ /// latest contract revision prior to the proof.
421
+ ///
422
+ /// 4. Lastly, anyone can submit a contract expiration. Typically, an expiration
423
+ /// is only required if the host is unable or unwilling to sign a finalization or
424
+ /// renewal. An expiration can only be submitted after the contract's
425
+ /// ExpirationHeight; this gives the host a reasonable window of time after the
426
+ /// ProofHeight in which to submit a storage proof.
427
+ ///
428
+ /// Once a contract has been resolved, it cannot be altered or resolved again.
429
+ /// When a contract is resolved, its RenterOutput and HostOutput are created
430
+ /// immediately (though they will not be spendable until their timelock expires).
431
+ /// However, if the contract is resolved via an expiration, the HostOutput will
432
+ /// have value equal to MissedHostValue; in other words, the host forfeits its
433
+ /// collateral. This is considered a "missed" resolution; all other resolution
434
+ /// types are "valid." As a special case, the expiration of an empty contract is
435
+ /// considered valid, reflecting the fact that the host has not failed to perform
436
+ /// any duty.
437
+ #[derive(Debug, PartialEq, Serialize, Deserialize)]
438
+ #[serde(rename_all = "camelCase")]
439
+ #[allow(clippy::large_enum_variant)]
440
+ pub enum ContractResolution {
441
+ Renewal(FileContractRenewal),
442
+ StorageProof(StorageProof),
443
+ Expiration(),
444
+ }
445
+
446
+ /// A FileContractResolution closes a v2 file contract's payment channel.
447
+ #[derive(Debug, PartialEq)]
448
+ pub struct FileContractResolution {
449
+ pub parent: FileContractElement,
450
+ pub resolution: ContractResolution,
451
+ }
452
+
453
+ impl FileContractResolution {
454
+ fn encode_semantics<W: std::io::Write>(&self, w: &mut W) -> encoding::Result<()> {
455
+ self.parent.id.encode(w)?;
456
+ match &self.resolution {
457
+ // type is not encoded in the resolution semantics
458
+ ContractResolution::Renewal(renewal) => renewal.encode_semantics(w),
459
+ ContractResolution::StorageProof(proof) => proof.encode_semantics(w),
460
+ ContractResolution::Expiration() => Ok(()),
461
+ }
462
+ }
463
+ }
464
+
465
+ impl SiaEncodable for FileContractResolution {
466
+ fn encode<W: std::io::Write>(&self, w: &mut W) -> encoding::Result<()> {
467
+ self.parent.encode(w)?;
468
+ match &self.resolution {
469
+ ContractResolution::Renewal(renewal) => {
470
+ 0u8.encode(w)?;
471
+ renewal.encode(w)
472
+ }
473
+ ContractResolution::StorageProof(proof) => {
474
+ 1u8.encode(w)?;
475
+ proof.encode(w)
476
+ }
477
+ ContractResolution::Expiration() => 2u8.encode(w),
478
+ }
479
+ }
480
+ }
481
+
482
+ impl SiaDecodable for FileContractResolution {
483
+ fn decode<R: std::io::Read>(r: &mut R) -> encoding::Result<Self> {
484
+ let parent = FileContractElement::decode(r)?;
485
+ let resolution = match u8::decode(r)? {
486
+ 0 => ContractResolution::Renewal(FileContractRenewal::decode(r)?),
487
+ 1 => ContractResolution::StorageProof(StorageProof::decode(r)?),
488
+ 2 => ContractResolution::Expiration(),
489
+ _ => {
490
+ return Err(encoding::Error::Custom(
491
+ "invalid contract resolution type".to_string(),
492
+ ));
493
+ }
494
+ };
495
+ Ok(FileContractResolution { parent, resolution })
496
+ }
497
+ }
498
+
499
+ impl AsyncSiaEncodable for FileContractResolution {
500
+ async fn encode_async<E: AsyncEncoder>(&self, e: &mut E) -> Result<(), E::Error> {
501
+ self.parent.encode_async(e).await?;
502
+ match &self.resolution {
503
+ ContractResolution::Renewal(renewal) => {
504
+ 0u8.encode_async(e).await?;
505
+ renewal.encode_async(e).await
506
+ }
507
+ ContractResolution::StorageProof(proof) => {
508
+ 1u8.encode_async(e).await?;
509
+ proof.encode_async(e).await
510
+ }
511
+ ContractResolution::Expiration() => 2u8.encode_async(e).await,
512
+ }
513
+ }
514
+ }
515
+
516
+ impl AsyncSiaDecodable for FileContractResolution {
517
+ async fn decode_async<D: AsyncDecoder>(d: &mut D) -> Result<Self, D::Error> {
518
+ let parent = FileContractElement::decode_async(d).await?;
519
+ let resolution_type = u8::decode_async(d).await?;
520
+ let resolution = match resolution_type {
521
+ 0 => ContractResolution::Renewal(FileContractRenewal::decode_async(d).await?),
522
+ 1 => ContractResolution::StorageProof(StorageProof::decode_async(d).await?),
523
+ 2 => ContractResolution::Expiration(),
524
+ _ => {
525
+ return Err(encoding::Error::InvalidValue(format!(
526
+ "invalid contract resolution type: {resolution_type}"
527
+ ))
528
+ .into());
529
+ }
530
+ };
531
+ Ok(FileContractResolution { parent, resolution })
532
+ }
533
+ }
534
+
535
+ impl Serialize for FileContractResolution {
536
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
537
+ where
538
+ S: serde::Serializer,
539
+ {
540
+ let mut state = serializer.serialize_struct("FileContractResolution", 3)?;
541
+ state.serialize_field("parent", &self.parent)?;
542
+ state.serialize_field(
543
+ "type",
544
+ &match &self.resolution {
545
+ ContractResolution::Renewal(_) => "renewal",
546
+ ContractResolution::StorageProof(_) => "storageProof",
547
+ ContractResolution::Expiration() => "expiration",
548
+ },
549
+ )?;
550
+ let resolution = match &self.resolution {
551
+ ContractResolution::Renewal(renewal) => {
552
+ serde_json::to_value(renewal).map_err(serde::ser::Error::custom)?
553
+ }
554
+ ContractResolution::StorageProof(proof) => {
555
+ serde_json::to_value(proof).map_err(serde::ser::Error::custom)?
556
+ }
557
+ ContractResolution::Expiration() => json!({}),
558
+ };
559
+ state.serialize_field("resolution", &resolution)?;
560
+ state.end()
561
+ }
562
+ }
563
+
564
+ impl<'de> Deserialize<'de> for FileContractResolution {
565
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
566
+ where
567
+ D: serde::Deserializer<'de>,
568
+ {
569
+ struct FileContractResolutionVisitor;
570
+
571
+ impl<'de> Visitor<'de> for FileContractResolutionVisitor {
572
+ type Value = FileContractResolution;
573
+
574
+ fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
575
+ formatter.write_str("struct FileContractResolution")
576
+ }
577
+
578
+ fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
579
+ where
580
+ V: MapAccess<'de>,
581
+ {
582
+ let mut parent: Option<FileContractElement> = None;
583
+ let mut resolution_type: Option<String> = None;
584
+ let mut resolution_value: Option<serde_json::Value> = None;
585
+
586
+ while let Some(key) = map.next_key::<String>()? {
587
+ match key.as_str() {
588
+ "parent" => parent = Some(map.next_value()?),
589
+ "type" => resolution_type = Some(map.next_value()?),
590
+ "resolution" => resolution_value = Some(map.next_value()?),
591
+ _ => {
592
+ return Err(serde::de::Error::unknown_field(
593
+ key.as_str(),
594
+ &["parent", "type", "resolution"],
595
+ ));
596
+ }
597
+ }
598
+ }
599
+
600
+ let parent = parent.ok_or_else(|| serde::de::Error::missing_field("parent"))?;
601
+ let resolution_type =
602
+ resolution_type.ok_or_else(|| serde::de::Error::missing_field("type"))?;
603
+ let resolution_value = resolution_value
604
+ .ok_or_else(|| serde::de::Error::missing_field("resolution"))?;
605
+
606
+ let resolution = match resolution_type.as_str() {
607
+ "renewal" => ContractResolution::Renewal(
608
+ serde_json::from_value(resolution_value).map_err(Error::custom)?,
609
+ ),
610
+ "storageProof" => ContractResolution::StorageProof(
611
+ serde_json::from_value(resolution_value).map_err(Error::custom)?,
612
+ ),
613
+ "expiration" => ContractResolution::Expiration(),
614
+ _ => return Err(serde::de::Error::custom("invalid contract resolution type")),
615
+ };
616
+
617
+ Ok(FileContractResolution { parent, resolution })
618
+ }
619
+ }
620
+ deserializer.deserialize_struct(
621
+ "FileContractResolution",
622
+ &["parent", "type", "resolution"],
623
+ FileContractResolutionVisitor,
624
+ )
625
+ }
626
+ }
627
+
628
+ /// A Transaction effects a change of blockchain state.
629
+ #[derive(Debug, PartialEq, Serialize, Deserialize)]
630
+ #[serde(rename_all = "camelCase")]
631
+ pub struct Transaction {
632
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
633
+ pub siacoin_inputs: Vec<SiacoinInput>,
634
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
635
+ pub siacoin_outputs: Vec<SiacoinOutput>,
636
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
637
+ pub siafund_inputs: Vec<SiafundInput>,
638
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
639
+ pub siafund_outputs: Vec<SiafundOutput>,
640
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
641
+ pub file_contracts: Vec<FileContract>,
642
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
643
+ pub file_contract_revisions: Vec<FileContractRevision>,
644
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
645
+ pub file_contract_resolutions: Vec<FileContractResolution>,
646
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
647
+ pub attestations: Vec<Attestation>,
648
+ #[serde(
649
+ default,
650
+ with = "crate::types::utils::base64",
651
+ skip_serializing_if = "Vec::is_empty"
652
+ )]
653
+ pub arbitrary_data: Vec<u8>,
654
+ #[serde(default, skip_serializing_if = "Option::is_none")]
655
+ pub new_foundation_address: Option<Address>,
656
+ pub miner_fee: Currency,
657
+ }
658
+
659
+ impl Transaction {
660
+ fn encode_semantics<W: io::Write>(&self, w: &mut W) -> encoding::Result<()> {
661
+ self.siacoin_inputs.len().encode(w)?;
662
+ for input in &self.siacoin_inputs {
663
+ input.parent.id.encode(w)?;
664
+ }
665
+ self.siacoin_outputs.encode(w)?;
666
+ self.siafund_inputs.len().encode(w)?;
667
+ for input in &self.siafund_inputs {
668
+ input.parent.id.encode(w)?;
669
+ }
670
+ self.siafund_outputs.encode(w)?;
671
+ self.file_contracts.len().encode(w)?;
672
+ for fc in &self.file_contracts {
673
+ fc.encode_semantics(w)?;
674
+ }
675
+ self.file_contract_revisions.len().encode(w)?;
676
+ for fcr in &self.file_contract_revisions {
677
+ fcr.encode_semantics(w)?;
678
+ }
679
+ self.file_contract_resolutions.len().encode(w)?;
680
+ for fcr in &self.file_contract_resolutions {
681
+ fcr.encode_semantics(w)?;
682
+ }
683
+ self.attestations.encode(w)?;
684
+ self.arbitrary_data.encode(w)?;
685
+ self.new_foundation_address.encode(w)?;
686
+ self.miner_fee.encode(w)?;
687
+ Ok(())
688
+ }
689
+
690
+ pub fn id(&self) -> TransactionID {
691
+ let mut state = Params::new().hash_length(32).to_state();
692
+ self.encode_semantics(&mut state).unwrap();
693
+ state.finalize().into()
694
+ }
695
+
696
+ pub fn input_sig_hash(&self, cs: &ChainState) -> Hash256 {
697
+ let mut state = Params::new().hash_length(32).to_state();
698
+ state.update("sia/sig/input|".as_bytes());
699
+ state.update(cs.replay_prefix());
700
+ self.encode_semantics(&mut state).unwrap();
701
+ state.finalize().into()
702
+ }
703
+ }
704
+
705
+ impl Default for Transaction {
706
+ fn default() -> Self {
707
+ Transaction {
708
+ siacoin_inputs: Vec::new(),
709
+ siacoin_outputs: Vec::new(),
710
+ siafund_inputs: Vec::new(),
711
+ siafund_outputs: Vec::new(),
712
+ file_contracts: Vec::new(),
713
+ file_contract_revisions: Vec::new(),
714
+ file_contract_resolutions: Vec::new(),
715
+ attestations: Vec::new(),
716
+ arbitrary_data: Vec::new(),
717
+ new_foundation_address: None,
718
+ miner_fee: Currency::zero(),
719
+ }
720
+ }
721
+ }
722
+
723
+ const TXN_VERSION: u8 = 2;
724
+
725
+ impl SiaEncodable for Transaction {
726
+ fn encode<W: io::Write>(&self, w: &mut W) -> encoding::Result<()> {
727
+ TXN_VERSION.encode(w)?;
728
+
729
+ let fields = [
730
+ !self.siacoin_inputs.is_empty(),
731
+ !self.siacoin_outputs.is_empty(),
732
+ !self.siafund_inputs.is_empty(),
733
+ !self.siafund_outputs.is_empty(),
734
+ !self.file_contracts.is_empty(),
735
+ !self.file_contract_revisions.is_empty(),
736
+ !self.file_contract_resolutions.is_empty(),
737
+ !self.attestations.is_empty(),
738
+ !self.arbitrary_data.is_empty(),
739
+ self.new_foundation_address.is_some(),
740
+ self.miner_fee != Currency::zero(),
741
+ ]
742
+ .iter()
743
+ .enumerate()
744
+ .filter(|&(_, condition)| *condition)
745
+ .map(|(i, _)| 1 << i)
746
+ .sum::<u64>();
747
+ fields.encode(w)?;
748
+
749
+ if !self.siacoin_inputs.is_empty() {
750
+ self.siacoin_inputs.encode(w)?;
751
+ }
752
+ if !self.siacoin_outputs.is_empty() {
753
+ self.siacoin_outputs.encode(w)?;
754
+ }
755
+ if !self.siafund_inputs.is_empty() {
756
+ self.siafund_inputs.encode(w)?;
757
+ }
758
+ if !self.siafund_outputs.is_empty() {
759
+ self.siafund_outputs.encode(w)?;
760
+ }
761
+ if !self.file_contracts.is_empty() {
762
+ self.file_contracts.encode(w)?;
763
+ }
764
+ if !self.file_contract_revisions.is_empty() {
765
+ self.file_contract_revisions.encode(w)?;
766
+ }
767
+ if !self.file_contract_resolutions.is_empty() {
768
+ self.file_contract_resolutions.encode(w)?;
769
+ }
770
+ if !self.attestations.is_empty() {
771
+ self.attestations.encode(w)?;
772
+ }
773
+ if !self.arbitrary_data.is_empty() {
774
+ self.arbitrary_data.encode(w)?;
775
+ }
776
+ if let Some(addr) = &self.new_foundation_address {
777
+ addr.encode(w)?;
778
+ }
779
+ if self.miner_fee != Currency::zero() {
780
+ self.miner_fee.encode(w)?;
781
+ }
782
+ Ok(())
783
+ }
784
+ }
785
+
786
+ impl SiaDecodable for Transaction {
787
+ fn decode<R: io::Read>(r: &mut R) -> encoding::Result<Self> {
788
+ let version = u8::decode(r)?;
789
+ if version != TXN_VERSION {
790
+ return Err(encoding::Error::Custom(
791
+ "unsupported transaction version".to_string(),
792
+ ));
793
+ }
794
+
795
+ let fields = u64::decode(r)?;
796
+ let mut txn = Transaction {
797
+ siacoin_inputs: Vec::new(),
798
+ siacoin_outputs: Vec::new(),
799
+ siafund_inputs: Vec::new(),
800
+ siafund_outputs: Vec::new(),
801
+ file_contracts: Vec::new(),
802
+ file_contract_revisions: Vec::new(),
803
+ file_contract_resolutions: Vec::new(),
804
+ attestations: Vec::new(),
805
+ arbitrary_data: Vec::new(),
806
+ new_foundation_address: None,
807
+ miner_fee: Currency::zero(),
808
+ };
809
+ if fields & 1 != 0 {
810
+ txn.siacoin_inputs = Vec::decode(r)?;
811
+ }
812
+ if fields & 2 != 0 {
813
+ txn.siacoin_outputs = Vec::decode(r)?;
814
+ }
815
+ if fields & 4 != 0 {
816
+ txn.siafund_inputs = Vec::decode(r)?;
817
+ }
818
+ if fields & 8 != 0 {
819
+ txn.siafund_outputs = Vec::decode(r)?;
820
+ }
821
+ if fields & 16 != 0 {
822
+ txn.file_contracts = Vec::decode(r)?;
823
+ }
824
+ if fields & 32 != 0 {
825
+ txn.file_contract_revisions = Vec::decode(r)?;
826
+ }
827
+ if fields & 64 != 0 {
828
+ txn.file_contract_resolutions = Vec::decode(r)?;
829
+ }
830
+ if fields & 128 != 0 {
831
+ txn.attestations = Vec::decode(r)?;
832
+ }
833
+ if fields & 256 != 0 {
834
+ txn.arbitrary_data = Vec::decode(r)?;
835
+ }
836
+ if fields & 512 != 0 {
837
+ txn.new_foundation_address = Some(Address::decode(r)?);
838
+ }
839
+ if fields & 1024 != 0 {
840
+ txn.miner_fee = Currency::decode(r)?;
841
+ }
842
+
843
+ Ok(txn)
844
+ }
845
+ }
846
+
847
+ impl AsyncSiaEncodable for Transaction {
848
+ async fn encode_async<E: AsyncEncoder>(&self, e: &mut E) -> Result<(), E::Error> {
849
+ TXN_VERSION.encode_async(e).await?;
850
+
851
+ let fields = [
852
+ !self.siacoin_inputs.is_empty(),
853
+ !self.siacoin_outputs.is_empty(),
854
+ !self.siafund_inputs.is_empty(),
855
+ !self.siafund_outputs.is_empty(),
856
+ !self.file_contracts.is_empty(),
857
+ !self.file_contract_revisions.is_empty(),
858
+ !self.file_contract_resolutions.is_empty(),
859
+ !self.attestations.is_empty(),
860
+ !self.arbitrary_data.is_empty(),
861
+ self.new_foundation_address.is_some(),
862
+ self.miner_fee != Currency::zero(),
863
+ ]
864
+ .iter()
865
+ .enumerate()
866
+ .filter(|&(_, condition)| *condition)
867
+ .map(|(i, _)| 1 << i)
868
+ .sum::<u64>();
869
+ fields.encode_async(e).await?;
870
+
871
+ if !self.siacoin_inputs.is_empty() {
872
+ self.siacoin_inputs.encode_async(e).await?;
873
+ }
874
+ if !self.siacoin_outputs.is_empty() {
875
+ self.siacoin_outputs.encode_async(e).await?;
876
+ }
877
+ if !self.siafund_inputs.is_empty() {
878
+ self.siafund_inputs.encode_async(e).await?;
879
+ }
880
+ if !self.siafund_outputs.is_empty() {
881
+ self.siafund_outputs.encode_async(e).await?;
882
+ }
883
+ if !self.file_contracts.is_empty() {
884
+ self.file_contracts.encode_async(e).await?;
885
+ }
886
+ if !self.file_contract_revisions.is_empty() {
887
+ self.file_contract_revisions.encode_async(e).await?;
888
+ }
889
+ if !self.file_contract_resolutions.is_empty() {
890
+ self.file_contract_resolutions.encode_async(e).await?;
891
+ }
892
+ if !self.attestations.is_empty() {
893
+ self.attestations.encode_async(e).await?;
894
+ }
895
+ if !self.arbitrary_data.is_empty() {
896
+ self.arbitrary_data.encode_async(e).await?;
897
+ }
898
+ if let Some(addr) = &self.new_foundation_address {
899
+ addr.encode_async(e).await?;
900
+ }
901
+ if self.miner_fee != Currency::zero() {
902
+ self.miner_fee.encode_async(e).await?;
903
+ }
904
+ Ok(())
905
+ }
906
+ }
907
+
908
+ impl AsyncSiaDecodable for Transaction {
909
+ async fn decode_async<D: AsyncDecoder>(d: &mut D) -> Result<Self, D::Error> {
910
+ let version = u8::decode_async(d).await?;
911
+ if version != TXN_VERSION {
912
+ return Err(encoding::Error::InvalidValue(format!(
913
+ "invalid transaction version: {version}"
914
+ ))
915
+ .into());
916
+ }
917
+
918
+ let fields = u64::decode_async(d).await?;
919
+ let mut txn = Transaction {
920
+ siacoin_inputs: Vec::new(),
921
+ siacoin_outputs: Vec::new(),
922
+ siafund_inputs: Vec::new(),
923
+ siafund_outputs: Vec::new(),
924
+ file_contracts: Vec::new(),
925
+ file_contract_revisions: Vec::new(),
926
+ file_contract_resolutions: Vec::new(),
927
+ attestations: Vec::new(),
928
+ arbitrary_data: Vec::new(),
929
+ new_foundation_address: None,
930
+ miner_fee: Currency::zero(),
931
+ };
932
+ if fields & 1 != 0 {
933
+ txn.siacoin_inputs = Vec::decode_async(d).await?;
934
+ }
935
+ if fields & 2 != 0 {
936
+ txn.siacoin_outputs = Vec::decode_async(d).await?;
937
+ }
938
+ if fields & 4 != 0 {
939
+ txn.siafund_inputs = Vec::decode_async(d).await?;
940
+ }
941
+ if fields & 8 != 0 {
942
+ txn.siafund_outputs = Vec::decode_async(d).await?;
943
+ }
944
+ if fields & 16 != 0 {
945
+ txn.file_contracts = Vec::decode_async(d).await?;
946
+ }
947
+ if fields & 32 != 0 {
948
+ txn.file_contract_revisions = Vec::decode_async(d).await?;
949
+ }
950
+ if fields & 64 != 0 {
951
+ txn.file_contract_resolutions = Vec::decode_async(d).await?;
952
+ }
953
+ if fields & 128 != 0 {
954
+ txn.attestations = Vec::decode_async(d).await?;
955
+ }
956
+ if fields & 256 != 0 {
957
+ txn.arbitrary_data = Vec::decode_async(d).await?;
958
+ }
959
+ if fields & 512 != 0 {
960
+ txn.new_foundation_address = Some(Address::decode_async(d).await?);
961
+ }
962
+ if fields & 1024 != 0 {
963
+ txn.miner_fee = Currency::decode_async(d).await?;
964
+ }
965
+
966
+ Ok(txn)
967
+ }
968
+ }
969
+
970
+ #[cfg(test)]
971
+ mod tests {
972
+ use super::*;
973
+ use crate::consensus::{
974
+ ElementAccumulator, HardforkASIC, HardforkDevAddr, HardforkFoundation, HardforkOak,
975
+ HardforkStorageProof, HardforkTax, HardforkV2, Network, State,
976
+ };
977
+ use crate::types::Work;
978
+ use crate::{address, hash_256, public_key, siacoin_id};
979
+ use chrono::{DateTime, Duration};
980
+ use core::fmt::Debug;
981
+ use serde::Serialize;
982
+ use serde::de::DeserializeOwned;
983
+
984
+ /// test_serialize_json is a helper to test serialization and deserialization of a struct to and from JSON.
985
+ fn test_serialize_json<S: Serialize + DeserializeOwned + Debug + PartialEq>(
986
+ obj: &S,
987
+ json_str: &str,
988
+ ) {
989
+ let serialized = serde_json::to_string(&obj).unwrap();
990
+ assert_eq!(serialized, json_str);
991
+ let deserialized: S = serde_json::from_str(&serialized).unwrap();
992
+ assert_eq!(deserialized, *obj);
993
+ }
994
+
995
+ /// test_serialize is a helper to test serialization and deserialization of a struct to and from Sia's
996
+ /// custom binary encoding.
997
+ fn test_serialize<S: SiaEncodable + SiaDecodable + Debug + PartialEq>(
998
+ obj: &S,
999
+ hex_binary: &str,
1000
+ ) {
1001
+ let mut serialized = Vec::new();
1002
+ obj.encode(&mut serialized).unwrap();
1003
+ assert_eq!(hex::encode(serialized.clone()), hex_binary);
1004
+ let deserialized = S::decode(&mut &serialized[..]).unwrap();
1005
+ assert_eq!(deserialized, *obj);
1006
+ }
1007
+
1008
+ #[test]
1009
+ fn test_serialize_siacoin_element() {
1010
+ let se = SiacoinElement {
1011
+ id: SiacoinOutputID::default(),
1012
+ siacoin_output: SiacoinOutput {
1013
+ value: Currency::new(2389084800000000000000000000000000),
1014
+ address: Address::new([0; 32]),
1015
+ },
1016
+ state_element: StateElement {
1017
+ leaf_index: 0,
1018
+ merkle_proof: vec![Hash256::default()],
1019
+ },
1020
+ maturity_height: 0,
1021
+ };
1022
+
1023
+ let binary_str = "0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000809ab5dad71c4e547dca75000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
1024
+ test_serialize(&se, binary_str);
1025
+
1026
+ let json_str = "{\"stateElement\":{\"leafIndex\":0,\"merkleProof\":[\"0000000000000000000000000000000000000000000000000000000000000000\"]},\"id\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"siacoinOutput\":{\"value\":\"2389084800000000000000000000000000\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"maturityHeight\":0}";
1027
+ test_serialize_json(&se, json_str);
1028
+ }
1029
+
1030
+ #[test]
1031
+ fn test_serialize_siafund_element() {
1032
+ let se = SiafundElement {
1033
+ id: SiafundOutputID::default(),
1034
+ state_element: StateElement {
1035
+ leaf_index: 0,
1036
+ merkle_proof: vec![Hash256::default()],
1037
+ },
1038
+ siafund_output: SiafundOutput {
1039
+ value: 1086708929188041408,
1040
+ address: Address::new([0; 32]),
1041
+ },
1042
+ claim_start: Currency::new(0),
1043
+ };
1044
+
1045
+ let binary_str = "0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0927f7203c4140f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
1046
+ test_serialize(&se, binary_str);
1047
+
1048
+ let json_str = "{\"stateElement\":{\"leafIndex\":0,\"merkleProof\":[\"0000000000000000000000000000000000000000000000000000000000000000\"]},\"id\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"siafundOutput\":{\"value\":1086708929188041408,\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"claimStart\":\"0\"}";
1049
+ test_serialize_json(&se, json_str);
1050
+ }
1051
+
1052
+ #[test]
1053
+ fn test_serialize_file_contract_element() {
1054
+ let fce = FileContractElement {
1055
+ id: FileContractID::default(),
1056
+ state_element: StateElement {
1057
+ leaf_index: 0,
1058
+ merkle_proof: vec![Hash256::default()],
1059
+ },
1060
+ v2_file_contract: FileContract {
1061
+ capacity: 7938725446189123975,
1062
+ filesize: 4815560028289493432,
1063
+ file_merkle_root: hash_256!(
1064
+ "dc033023420634ed4c7685c82aa884eebe8415e16c57b6a55c673a5a98fa7b0d"
1065
+ ),
1066
+ proof_height: 6265010746208955018,
1067
+ expiration_height: 5159880069065321628,
1068
+ renter_output: SiacoinOutput {
1069
+ value: Currency::new(3837391090000000000000000000000000),
1070
+ address: Address::new([0; 32]),
1071
+ },
1072
+ host_output: SiacoinOutput {
1073
+ value: Currency::new(3827725744000000000000000000000000),
1074
+ address: Address::new([0; 32]),
1075
+ },
1076
+ missed_host_value: Currency::new(965900695000000000000000000000000),
1077
+ total_collateral: Currency::new(0),
1078
+ host_public_key: PublicKey::new([0; 32]),
1079
+ renter_public_key: PublicKey::new([0; 32]),
1080
+ revision_number: 0,
1081
+
1082
+ host_signature: Signature::new([0; 64]),
1083
+ renter_signature: Signature::new([0; 64]),
1084
+ },
1085
+ };
1086
+
1087
+ let binary_str = "00000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008709756dbb042c6eb8e9c3f0544ed442dc033023420634ed4c7685c82aa884eebe8415e16c57b6a55c673a5a98fa7b0d8a2aca8914caf1569cac41a895939b470000003292d8b9e4856e58af32bd00000000000000000000000000000000000000000000000000000000000000000000000000b0f3637fb05e9bedb0b8bc00000000000000000000000000000000000000000000000000000000000000000000000000f7420336ee348e78619f2f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
1088
+ test_serialize(&fce, binary_str);
1089
+
1090
+ let json_str = "{\"stateElement\":{\"leafIndex\":0,\"merkleProof\":[\"0000000000000000000000000000000000000000000000000000000000000000\"]},\"id\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"v2FileContract\":{\"capacity\":7938725446189123975,\"filesize\":4815560028289493432,\"fileMerkleRoot\":\"dc033023420634ed4c7685c82aa884eebe8415e16c57b6a55c673a5a98fa7b0d\",\"proofHeight\":6265010746208955018,\"expirationHeight\":5159880069065321628,\"renterOutput\":{\"value\":\"3837391090000000000000000000000000\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"hostOutput\":{\"value\":\"3827725744000000000000000000000000\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"missedHostValue\":\"965900695000000000000000000000000\",\"totalCollateral\":\"0\",\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":0,\"renterSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"hostSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"}}";
1091
+ test_serialize_json(&fce, json_str);
1092
+ }
1093
+
1094
+ #[test]
1095
+ fn test_serialize_file_contract() {
1096
+ let fc = FileContract {
1097
+ capacity: 0,
1098
+ filesize: 0,
1099
+ file_merkle_root: Hash256::default(),
1100
+ proof_height: 0,
1101
+ expiration_height: 0,
1102
+ renter_output: SiacoinOutput {
1103
+ value: Currency::new(0),
1104
+ address: Address::new([0; 32]),
1105
+ },
1106
+ host_output: SiacoinOutput {
1107
+ value: Currency::new(0),
1108
+ address: Address::new([0; 32]),
1109
+ },
1110
+ missed_host_value: Currency::new(0),
1111
+ total_collateral: Currency::new(0),
1112
+ host_public_key: PublicKey::new([0; 32]),
1113
+ renter_public_key: PublicKey::new([0; 32]),
1114
+ revision_number: 0,
1115
+
1116
+ host_signature: Signature::new([0; 64]),
1117
+ renter_signature: Signature::new([0; 64]),
1118
+ };
1119
+
1120
+ let binary_str = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
1121
+ test_serialize(&fc, binary_str);
1122
+
1123
+ let json_str = "{\"capacity\":0,\"filesize\":0,\"fileMerkleRoot\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"proofHeight\":0,\"expirationHeight\":0,\"renterOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"hostOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"missedHostValue\":\"0\",\"totalCollateral\":\"0\",\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":0,\"renterSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"hostSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"}";
1124
+ test_serialize_json(&fc, json_str);
1125
+ }
1126
+
1127
+ #[test]
1128
+ fn test_serialize_file_contract_revision() {
1129
+ let fcr = FileContractRevision {
1130
+ parent: FileContractElement {
1131
+ id: FileContractID::default(),
1132
+ state_element: StateElement {
1133
+ leaf_index: 0,
1134
+ merkle_proof: vec![Hash256::default()],
1135
+ },
1136
+ v2_file_contract: FileContract {
1137
+ capacity: 0,
1138
+ filesize: 0,
1139
+ file_merkle_root: Hash256::default(),
1140
+ proof_height: 0,
1141
+ expiration_height: 0,
1142
+ renter_output: SiacoinOutput {
1143
+ value: Currency::new(0),
1144
+ address: Address::new([0; 32]),
1145
+ },
1146
+ host_output: SiacoinOutput {
1147
+ value: Currency::new(0),
1148
+ address: Address::new([0; 32]),
1149
+ },
1150
+ missed_host_value: Currency::new(0),
1151
+ total_collateral: Currency::new(0),
1152
+ host_public_key: PublicKey::new([0; 32]),
1153
+ renter_public_key: PublicKey::new([0; 32]),
1154
+ revision_number: 0,
1155
+
1156
+ host_signature: Signature::new([0; 64]),
1157
+ renter_signature: Signature::new([0; 64]),
1158
+ },
1159
+ },
1160
+ revision: FileContract {
1161
+ capacity: 0,
1162
+ filesize: 0,
1163
+ file_merkle_root: Hash256::default(),
1164
+ proof_height: 0,
1165
+ expiration_height: 0,
1166
+ renter_output: SiacoinOutput {
1167
+ value: Currency::new(0),
1168
+ address: Address::new([0; 32]),
1169
+ },
1170
+ host_output: SiacoinOutput {
1171
+ value: Currency::new(0),
1172
+ address: Address::new([0; 32]),
1173
+ },
1174
+ missed_host_value: Currency::new(0),
1175
+ total_collateral: Currency::new(0),
1176
+ host_public_key: PublicKey::new([0; 32]),
1177
+ renter_public_key: PublicKey::new([0; 32]),
1178
+ revision_number: 0,
1179
+
1180
+ host_signature: Signature::new([0; 64]),
1181
+ renter_signature: Signature::new([0; 64]),
1182
+ },
1183
+ };
1184
+
1185
+ let binary_str = "000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
1186
+ test_serialize(&fcr, binary_str);
1187
+
1188
+ let json_str = "{\"parent\":{\"stateElement\":{\"leafIndex\":0,\"merkleProof\":[\"0000000000000000000000000000000000000000000000000000000000000000\"]},\"id\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"v2FileContract\":{\"capacity\":0,\"filesize\":0,\"fileMerkleRoot\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"proofHeight\":0,\"expirationHeight\":0,\"renterOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"hostOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"missedHostValue\":\"0\",\"totalCollateral\":\"0\",\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":0,\"renterSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"hostSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"}},\"revision\":{\"capacity\":0,\"filesize\":0,\"fileMerkleRoot\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"proofHeight\":0,\"expirationHeight\":0,\"renterOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"hostOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"missedHostValue\":\"0\",\"totalCollateral\":\"0\",\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":0,\"renterSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"hostSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"}}";
1189
+ test_serialize_json(&fcr, json_str);
1190
+ }
1191
+
1192
+ #[test]
1193
+ fn test_serialize_file_contract_renewal() {
1194
+ let fcr = FileContractRenewal {
1195
+ new_contract: FileContract {
1196
+ capacity: 0,
1197
+ filesize: 0,
1198
+ file_merkle_root: Hash256::default(),
1199
+ proof_height: 0,
1200
+ expiration_height: 0,
1201
+ renter_output: SiacoinOutput {
1202
+ value: Currency::new(0),
1203
+ address: Address::new([0; 32]),
1204
+ },
1205
+ host_output: SiacoinOutput {
1206
+ value: Currency::new(0),
1207
+ address: Address::new([0; 32]),
1208
+ },
1209
+ missed_host_value: Currency::new(0),
1210
+ total_collateral: Currency::new(0),
1211
+ host_public_key: PublicKey::new([0; 32]),
1212
+ renter_public_key: PublicKey::new([0; 32]),
1213
+ revision_number: 0,
1214
+
1215
+ host_signature: Signature::new([0; 64]),
1216
+ renter_signature: Signature::new([0; 64]),
1217
+ },
1218
+ final_revision: FileContract {
1219
+ capacity: 0,
1220
+ filesize: 0,
1221
+ file_merkle_root: Hash256::default(),
1222
+ proof_height: 0,
1223
+ expiration_height: 0,
1224
+ renter_output: SiacoinOutput {
1225
+ value: Currency::new(0),
1226
+ address: Address::new([0; 32]),
1227
+ },
1228
+ host_output: SiacoinOutput {
1229
+ value: Currency::new(0),
1230
+ address: Address::new([0; 32]),
1231
+ },
1232
+ missed_host_value: Currency::new(0),
1233
+ total_collateral: Currency::new(0),
1234
+ host_public_key: PublicKey::new([0; 32]),
1235
+ renter_public_key: PublicKey::new([0; 32]),
1236
+ revision_number: 0,
1237
+
1238
+ host_signature: Signature::new([0; 64]),
1239
+ renter_signature: Signature::new([0; 64]),
1240
+ },
1241
+ renter_rollover: Currency::new(0),
1242
+ host_rollover: Currency::new(0),
1243
+ renter_signature: Signature::new([0u8; 64]),
1244
+ host_signature: Signature::new([0u8; 64]),
1245
+ };
1246
+
1247
+ let binary_str = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
1248
+ test_serialize(&fcr, binary_str);
1249
+
1250
+ let json_str = "{\"finalRevision\":{\"capacity\":0,\"filesize\":0,\"fileMerkleRoot\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"proofHeight\":0,\"expirationHeight\":0,\"renterOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"hostOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"missedHostValue\":\"0\",\"totalCollateral\":\"0\",\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":0,\"renterSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"hostSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"},\"newContract\":{\"capacity\":0,\"filesize\":0,\"fileMerkleRoot\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"proofHeight\":0,\"expirationHeight\":0,\"renterOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"hostOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"missedHostValue\":\"0\",\"totalCollateral\":\"0\",\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":0,\"renterSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"hostSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"},\"renterRollover\":\"0\",\"hostRollover\":\"0\",\"renterSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"hostSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"}";
1251
+ test_serialize_json(&fcr, json_str);
1252
+ }
1253
+
1254
+ #[test]
1255
+ fn test_serialize_storage_proof() {
1256
+ let sp = StorageProof {
1257
+ proof_index: ChainIndexElement {
1258
+ id: BlockID::default(),
1259
+ chain_index: ChainIndex {
1260
+ id: BlockID::default(),
1261
+ height: 0,
1262
+ },
1263
+ state_element: StateElement {
1264
+ leaf_index: 0,
1265
+ merkle_proof: vec![Hash256::default()],
1266
+ },
1267
+ },
1268
+ leaf: [3u8; 64].into(),
1269
+ proof: vec![Hash256::default()],
1270
+ };
1271
+
1272
+ let binary_str = "0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030301000000000000000000000000000000000000000000000000000000000000000000000000000000";
1273
+ test_serialize(&sp, binary_str);
1274
+
1275
+ let json_str = "{\"proofIndex\":{\"stateElement\":{\"leafIndex\":0,\"merkleProof\":[\"0000000000000000000000000000000000000000000000000000000000000000\"]},\"id\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"chainIndex\":{\"height\":0,\"id\":\"0000000000000000000000000000000000000000000000000000000000000000\"}},\"leaf\":\"03030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303\",\"proof\":[\"0000000000000000000000000000000000000000000000000000000000000000\"]}";
1276
+ test_serialize_json(&sp, json_str);
1277
+ }
1278
+
1279
+ fn test_chain_state() -> ChainState {
1280
+ ChainState {
1281
+ state: State {
1282
+ index: ChainIndex {
1283
+ height: 1,
1284
+ id: BlockID::default(),
1285
+ },
1286
+ prev_timestamps: [DateTime::UNIX_EPOCH; 11],
1287
+ depth: BlockID::default(),
1288
+ child_target: BlockID::default(),
1289
+ siafund_pool: Currency::new(0),
1290
+ oak_time: Duration::zero(),
1291
+ oak_target: BlockID::default(),
1292
+ foundation_primary_address: Address::new([0u8; 32]),
1293
+ foundation_failsafe_address: Address::new([0u8; 32]),
1294
+ total_work: Work::zero(),
1295
+ difficulty: Work::zero(),
1296
+ oak_work: Work::zero(),
1297
+ elements: ElementAccumulator::default(),
1298
+ attestations: 0,
1299
+ },
1300
+ network: Network {
1301
+ name: "test".to_string(),
1302
+ initial_coinbase: Currency::new(0),
1303
+ minimum_coinbase: Currency::new(0),
1304
+ initial_target: BlockID::default(),
1305
+ block_interval: Duration::seconds(1),
1306
+ maturity_delay: 0,
1307
+ hardfork_dev_addr: HardforkDevAddr {
1308
+ height: 0,
1309
+ old_address: Address::new([0u8; 32]),
1310
+ new_address: Address::new([0u8; 32]),
1311
+ },
1312
+ hardfork_tax: HardforkTax { height: 10 },
1313
+ hardfork_storage_proof: HardforkStorageProof { height: 0 },
1314
+ hardfork_asic: HardforkASIC {
1315
+ height: 0,
1316
+ oak_time: Duration::zero(),
1317
+ oak_target: BlockID::default(),
1318
+ },
1319
+ hardfork_oak: HardforkOak {
1320
+ height: 0,
1321
+ fix_height: 0,
1322
+ genesis_timestamp: DateTime::UNIX_EPOCH,
1323
+ },
1324
+ hardfork_foundation: HardforkFoundation {
1325
+ height: 0,
1326
+ primary_address: Address::new([0u8; 32]),
1327
+ failsafe_address: Address::new([0u8; 32]),
1328
+ },
1329
+ hardfork_v2: HardforkV2 {
1330
+ allow_height: 0,
1331
+ require_height: 0,
1332
+ },
1333
+ },
1334
+ }
1335
+ }
1336
+
1337
+ #[test]
1338
+ fn test_serialize_v2_file_contract_resolution() {
1339
+ struct TestCase {
1340
+ resolution: ContractResolution,
1341
+ binary_str: String,
1342
+ json_str: String,
1343
+ }
1344
+ let test_cases = vec![
1345
+ TestCase{
1346
+ resolution: ContractResolution::Renewal(FileContractRenewal {
1347
+ new_contract: FileContract {
1348
+ capacity: 0,
1349
+ filesize: 0,
1350
+ file_merkle_root: Hash256::default(),
1351
+ proof_height: 0,
1352
+ expiration_height: 0,
1353
+ renter_output: SiacoinOutput {
1354
+ value: Currency::new(0),
1355
+ address: Address::new([0; 32]),
1356
+ },
1357
+ host_output: SiacoinOutput {
1358
+ value: Currency::new(0),
1359
+ address: Address::new([0; 32]),
1360
+ },
1361
+ missed_host_value: Currency::new(0),
1362
+ total_collateral: Currency::new(0),
1363
+ host_public_key: PublicKey::new([0; 32]),
1364
+ renter_public_key: PublicKey::new([0; 32]),
1365
+ revision_number: 0,
1366
+
1367
+ host_signature: Signature::new([0; 64]),
1368
+ renter_signature: Signature::new([0; 64]),
1369
+ },
1370
+ final_revision: FileContract {
1371
+ capacity: 0,
1372
+ filesize: 0,
1373
+ file_merkle_root: Hash256::default(),
1374
+ proof_height: 0,
1375
+ expiration_height: 0,
1376
+ renter_output: SiacoinOutput {
1377
+ value: Currency::new(0),
1378
+ address: Address::new([0; 32]),
1379
+ },
1380
+ host_output: SiacoinOutput {
1381
+ value: Currency::new(0),
1382
+ address: Address::new([0; 32]),
1383
+ },
1384
+ missed_host_value: Currency::new(0),
1385
+ total_collateral: Currency::new(0),
1386
+ host_public_key: PublicKey::new([0; 32]),
1387
+ renter_public_key: PublicKey::new([0; 32]),
1388
+ revision_number: 0,
1389
+
1390
+ host_signature: Signature::new([0; 64]),
1391
+ renter_signature: Signature::new([0; 64]),
1392
+ },
1393
+ renter_rollover: Currency::new(0),
1394
+ host_rollover: Currency::new(0),
1395
+ renter_signature: Signature::new([0u8; 64]),
1396
+ host_signature: Signature::new([0u8; 64]),
1397
+ }),
1398
+ binary_str: "00000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".to_string(),
1399
+ json_str: "{\"parent\":{\"stateElement\":{\"leafIndex\":0,\"merkleProof\":[\"0000000000000000000000000000000000000000000000000000000000000000\"]},\"id\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"v2FileContract\":{\"capacity\":0,\"filesize\":0,\"fileMerkleRoot\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"proofHeight\":0,\"expirationHeight\":0,\"renterOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"hostOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"missedHostValue\":\"0\",\"totalCollateral\":\"0\",\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":0,\"renterSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"hostSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"}},\"type\":\"renewal\",\"resolution\":{\"finalRevision\":{\"capacity\":0,\"expirationHeight\":0,\"fileMerkleRoot\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"filesize\":0,\"hostOutput\":{\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\",\"value\":\"0\"},\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"missedHostValue\":\"0\",\"proofHeight\":0,\"renterOutput\":{\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\",\"value\":\"0\"},\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"renterSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":0,\"totalCollateral\":\"0\"},\"hostRollover\":\"0\",\"hostSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"newContract\":{\"capacity\":0,\"expirationHeight\":0,\"fileMerkleRoot\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"filesize\":0,\"hostOutput\":{\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\",\"value\":\"0\"},\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"missedHostValue\":\"0\",\"proofHeight\":0,\"renterOutput\":{\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\",\"value\":\"0\"},\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"renterSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":0,\"totalCollateral\":\"0\"},\"renterRollover\":\"0\",\"renterSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"}}".to_string(),
1400
+ },
1401
+ TestCase{
1402
+ resolution: ContractResolution::StorageProof(StorageProof{
1403
+ proof_index: ChainIndexElement {
1404
+ id: BlockID::default(),
1405
+ chain_index: ChainIndex {
1406
+ id: BlockID::default(),
1407
+ height: 0,
1408
+ },
1409
+ state_element: StateElement {
1410
+ leaf_index: 0,
1411
+ merkle_proof: vec![Hash256::default()],
1412
+ },
1413
+ },
1414
+ leaf: [0u8; 64].into(),
1415
+ proof: vec![Hash256::default()],
1416
+ }),
1417
+ binary_str: "00000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000".to_string(),
1418
+ json_str: "{\"parent\":{\"stateElement\":{\"leafIndex\":0,\"merkleProof\":[\"0000000000000000000000000000000000000000000000000000000000000000\"]},\"id\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"v2FileContract\":{\"capacity\":0,\"filesize\":0,\"fileMerkleRoot\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"proofHeight\":0,\"expirationHeight\":0,\"renterOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"hostOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"missedHostValue\":\"0\",\"totalCollateral\":\"0\",\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":0,\"renterSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"hostSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"}},\"type\":\"storageProof\",\"resolution\":{\"leaf\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"proof\":[\"0000000000000000000000000000000000000000000000000000000000000000\"],\"proofIndex\":{\"chainIndex\":{\"height\":0,\"id\":\"0000000000000000000000000000000000000000000000000000000000000000\"},\"id\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"stateElement\":{\"leafIndex\":0,\"merkleProof\":[\"0000000000000000000000000000000000000000000000000000000000000000\"]}}}}".to_string(),
1419
+ },
1420
+ TestCase{
1421
+ resolution: ContractResolution::Expiration(),
1422
+ binary_str: "0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002".to_string(),
1423
+ json_str: "{\"parent\":{\"stateElement\":{\"leafIndex\":0,\"merkleProof\":[\"0000000000000000000000000000000000000000000000000000000000000000\"]},\"id\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"v2FileContract\":{\"capacity\":0,\"filesize\":0,\"fileMerkleRoot\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"proofHeight\":0,\"expirationHeight\":0,\"renterOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"hostOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"missedHostValue\":\"0\",\"totalCollateral\":\"0\",\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":0,\"renterSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"hostSignature\":\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"}},\"type\":\"expiration\",\"resolution\":{}}".to_string(),
1424
+ }
1425
+ ];
1426
+ for tc in test_cases {
1427
+ let fcr = FileContractResolution {
1428
+ parent: FileContractElement {
1429
+ id: FileContractID::default(),
1430
+ state_element: StateElement {
1431
+ leaf_index: 0,
1432
+ merkle_proof: vec![Hash256::default()],
1433
+ },
1434
+ v2_file_contract: FileContract {
1435
+ capacity: 0,
1436
+ filesize: 0,
1437
+ file_merkle_root: Hash256::default(),
1438
+ proof_height: 0,
1439
+ expiration_height: 0,
1440
+ renter_output: SiacoinOutput {
1441
+ value: Currency::new(0),
1442
+ address: Address::new([0; 32]),
1443
+ },
1444
+ host_output: SiacoinOutput {
1445
+ value: Currency::new(0),
1446
+ address: Address::new([0; 32]),
1447
+ },
1448
+ missed_host_value: Currency::new(0),
1449
+ total_collateral: Currency::new(0),
1450
+ host_public_key: PublicKey::new([0; 32]),
1451
+ renter_public_key: PublicKey::new([0; 32]),
1452
+ revision_number: 0,
1453
+
1454
+ host_signature: Signature::new([0; 64]),
1455
+ renter_signature: Signature::new([0; 64]),
1456
+ },
1457
+ },
1458
+ resolution: tc.resolution,
1459
+ };
1460
+
1461
+ test_serialize(&fcr, tc.binary_str.as_str());
1462
+ test_serialize_json(&fcr, tc.json_str.as_str());
1463
+ }
1464
+ }
1465
+
1466
+ #[test]
1467
+ fn test_file_contract_tax() {
1468
+ struct TestCase {
1469
+ output_value: Currency,
1470
+ tax: Currency,
1471
+ }
1472
+ let test_cases = vec![
1473
+ TestCase {
1474
+ output_value: Currency::new(0),
1475
+ tax: Currency::new(0),
1476
+ },
1477
+ TestCase {
1478
+ output_value: Currency::new(0),
1479
+ tax: Currency::new(0),
1480
+ },
1481
+ TestCase {
1482
+ output_value: Currency::new(1),
1483
+ tax: Currency::new(0),
1484
+ },
1485
+ TestCase {
1486
+ output_value: Currency::new(170141183460469231731687303715884105727),
1487
+ tax: Currency::new(13611294676837538538534984297270720000),
1488
+ },
1489
+ TestCase {
1490
+ output_value: Currency::new(805949712500000000000000000000000),
1491
+ tax: Currency::new(64475977000000000000000000000000),
1492
+ },
1493
+ TestCase {
1494
+ output_value: Currency::new(151318823166141058930638084278357033939),
1495
+ tax: Currency::new(12105505853291284714451046742268560000),
1496
+ },
1497
+ TestCase {
1498
+ output_value: Currency::new(2087287149000000000000000000000000),
1499
+ tax: Currency::new(166982971920000000000000000000000),
1500
+ },
1501
+ TestCase {
1502
+ output_value: Currency::new(19054329256128693174495496952954959442),
1503
+ tax: Currency::new(1524346340490295453959639756236390000),
1504
+ },
1505
+ TestCase {
1506
+ output_value: Currency::new(1463835551500000000000000000000000),
1507
+ tax: Currency::new(117106844120000000000000000000000),
1508
+ },
1509
+ TestCase {
1510
+ output_value: Currency::new(34808017277671855105213753517013880976),
1511
+ tax: Currency::new(2784641382213748408417100281361110000),
1512
+ },
1513
+ TestCase {
1514
+ output_value: Currency::new(1456475819000000000000000000000000),
1515
+ tax: Currency::new(116518065520000000000000000000000),
1516
+ },
1517
+ TestCase {
1518
+ output_value: Currency::new(4050009160038948139568737574567615426),
1519
+ tax: Currency::new(324000732803115851165499005965400000),
1520
+ },
1521
+ TestCase {
1522
+ output_value: Currency::new(611362349000000000000000000000000),
1523
+ tax: Currency::new(48908987920000000000000000000000),
1524
+ },
1525
+ TestCase {
1526
+ output_value: Currency::new(59744399278834191323014460064531501644),
1527
+ tax: Currency::new(4779551942306735305841156805162520000),
1528
+ },
1529
+ TestCase {
1530
+ output_value: Currency::new(1971395366500000000000000000000000),
1531
+ tax: Currency::new(157711629320000000000000000000000),
1532
+ },
1533
+ TestCase {
1534
+ output_value: Currency::new(129395477943018813820173885271365401215),
1535
+ tax: Currency::new(10351638235441505105613910821709230000),
1536
+ },
1537
+ TestCase {
1538
+ output_value: Currency::new(1562430843000000000000000000000000),
1539
+ tax: Currency::new(124994467440000000000000000000000),
1540
+ },
1541
+ TestCase {
1542
+ output_value: Currency::new(33394010960011557818205782768368594560),
1543
+ tax: Currency::new(2671520876800924625456462621469480000),
1544
+ },
1545
+ TestCase {
1546
+ output_value: Currency::new(1464305596000000000000000000000000),
1547
+ tax: Currency::new(117144447680000000000000000000000),
1548
+ },
1549
+ TestCase {
1550
+ output_value: Currency::new(33699424149038914903292787212706936885),
1551
+ tax: Currency::new(2695953931923113192263422977016550000),
1552
+ },
1553
+ TestCase {
1554
+ output_value: Currency::new(455795805000000000000000000000000),
1555
+ tax: Currency::new(36463664400000000000000000000000),
1556
+ },
1557
+ TestCase {
1558
+ output_value: Currency::new(88567642754201788868008131876936390234),
1559
+ tax: Currency::new(7085411420336143109440650550154910000),
1560
+ },
1561
+ TestCase {
1562
+ output_value: Currency::new(359253930000000000000000000000000),
1563
+ tax: Currency::new(28740314400000000000000000000000),
1564
+ },
1565
+ TestCase {
1566
+ output_value: Currency::new(56501907684312465044405566127405468273),
1567
+ tax: Currency::new(4520152614744997203552445290192430000),
1568
+ },
1569
+ ];
1570
+
1571
+ let cs = test_chain_state();
1572
+ for tc in test_cases.iter() {
1573
+ let fc = FileContract {
1574
+ capacity: 0,
1575
+ filesize: 0,
1576
+ file_merkle_root: Hash256::default(),
1577
+ revision_number: 0,
1578
+ proof_height: 0,
1579
+ expiration_height: 0,
1580
+ missed_host_value: Currency::new(0),
1581
+ total_collateral: Currency::new(0),
1582
+ host_public_key: PublicKey::new([0u8; 32]),
1583
+ renter_public_key: PublicKey::new([0u8; 32]),
1584
+ host_signature: Signature::new([0u8; 64]),
1585
+ renter_signature: Signature::new([0u8; 64]),
1586
+ renter_output: SiacoinOutput {
1587
+ value: tc.output_value,
1588
+ address: Address::default(),
1589
+ },
1590
+ host_output: SiacoinOutput {
1591
+ value: tc.output_value,
1592
+ address: Address::default(),
1593
+ },
1594
+ };
1595
+
1596
+ let tax = fc.tax(&cs);
1597
+ assert_eq!(
1598
+ tax, tc.tax,
1599
+ "prefork tax incorrect for payout {:?}",
1600
+ tc.output_value
1601
+ );
1602
+ }
1603
+ }
1604
+
1605
+ #[test]
1606
+ fn test_attestation_sig_hash() {
1607
+ let cs = test_chain_state();
1608
+ let a = Attestation {
1609
+ public_key: PublicKey::new([
1610
+ 119, 70, 48, 66, 126, 125, 116, 9, 234, 170, 136, 51, 123, 122, 142, 138, 198, 136,
1611
+ 19, 32, 194, 144, 129, 104, 130, 246, 58, 195, 16, 72, 139, 112,
1612
+ ]),
1613
+ key: "2d341885482102f2".to_string(),
1614
+ value: vec![113, 1, 70, 231, 190, 215, 117, 38],
1615
+ signature: Signature::default(),
1616
+ };
1617
+ let sig_hash = a.sig_hash(&cs);
1618
+ assert_eq!(
1619
+ hex::encode(sig_hash),
1620
+ "5c4201fd4c261a1a3deb25130bd8f06d7d87a46281fd022252152844336a7c17"
1621
+ )
1622
+ }
1623
+
1624
+ #[test]
1625
+ fn test_file_contract_sig_hash() {
1626
+ let cs = test_chain_state();
1627
+ let s = "{\"capacity\":15437388468662742744,\"filesize\":16009828729725979578,\"fileMerkleRoot\":\"2d3b2c7b78f04eb1b66ac467ae7831081f7b495a2964b943c2c750e70180785a\",\"proofHeight\":0,\"expirationHeight\":0,\"renterOutput\":{\"value\":\"2825068531000000000000000000000000\",\"address\":\"cf620149bcbde171fcd9611a32ba29e2e97687f3b1562c1fe14c504f642690c9d8f5ec3cbeaf\"},\"hostOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"missedHostValue\":\"0\",\"totalCollateral\":\"0\",\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":0,\"renterSignature\":\"bee650e82a5534269bef42be0dd9a0b0f6c465b31437567075275d2188c685b2b65ef0fc7f369c780a758786f480da4d55459a1d85215f64aa47db1e79b1b8de\",\"hostSignature\":\"c32cf921ee00344d76e96e6f2dd306bd9c21226c83bb2ac55ce69b8d991a2f2212f645341c720f2fd8a7c57edef9b32f26a2c29c55958a45fd5c0d56a2addae3\"}";
1628
+
1629
+ let fc: FileContract = serde_json::from_str(s).unwrap();
1630
+ let sig_hash = fc.sig_hash(&cs);
1631
+ assert_eq!(
1632
+ hex::encode(sig_hash),
1633
+ "0b9b74e471b8936e0045e752a1a22a77b7c17807ec98a1a3f272f6d917790325"
1634
+ );
1635
+ }
1636
+
1637
+ #[test]
1638
+ fn test_contract_renewal_sig_hash() {
1639
+ let cs = test_chain_state();
1640
+ let s = "{\"finalRevision\":{\"capacity\":11959377077631068749,\"filesize\":10613077956865333006,\"fileMerkleRoot\":\"9482be576a5d68b6ad311b359b12070fae3df71fc0d2ec480a5ba8c3f4c9ad40\",\"proofHeight\":0,\"expirationHeight\":0,\"renterOutput\":{\"value\":\"107325855000000000000000000000000\",\"address\":\"cc590b0901f908ee76ea2c8497b9c29d7f2250db4427b7b1dfd8d5c0a368845c4a70d66c8998\"},\"hostOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"missedHostValue\":\"0\",\"totalCollateral\":\"0\",\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":18446744073709551615,\"renterSignature\":\"a71062c50866e8e834143340117efc49adbe7c8e50775c2ef6c2d8fd84470231928115e377288d588d93342946adbcbc83c5e3be01d4d22accf59c86f44ad523\",\"hostSignature\":\"a277c4b62d6ab7eead9d02c9e6243810baa0af9a8fef8b70b2f33df83fc8e67b94b2247b53c5e6d3a93a6197d41dd07652b75896757cf1dfeb059edca458de40\"},\"newContract\":{\"capacity\":2615073250361876210,\"filesize\":14611382285114970285,\"fileMerkleRoot\":\"5942d80cb6816da220a9576d62f3979b5e4f96b769cf785bcddf31698afb1432\",\"proofHeight\":0,\"expirationHeight\":0,\"renterOutput\":{\"value\":\"4046664893000000000000000000000000\",\"address\":\"41b74682d50aed617224d17162150c3854cc290b522c20d62260466ef13a95e212b7d9c6778b\"},\"hostOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"missedHostValue\":\"0\",\"totalCollateral\":\"0\",\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":0,\"renterSignature\":\"2614ae9d0b8300c98eadc1ffcab414298e30c678c0072359b1794623a04a68ef2474fbb1492071313b6f768946a52978b520c219a23649bfef9b9d0baabc7331\",\"hostSignature\":\"9c14a4f1428ad5bd72075c626fdf7dcf24143eda3a9cbecbc16ca751f0c88ee09e04a7f4f79718fb87df6debdd1d94d97bd00bcfa4fa77b3de582ad26eb45139\"},\"renterRollover\":\"3597839704000000000000000000000000\",\"hostRollover\":\"3099140907000000000000000000000000\",\"renterSignature\":\"4906067918dc6e7951c4e42b5b5bc1c3a3e3f02bddd10bf981275ccd0e8a5a067d35d4b5c2115256454490fed011e5c11ea35cb378eb6ada168b65d614515a44\",\"hostSignature\":\"5414a4c1e32f17b275148f3935a53b30cc30d1a0ff43e88f71e8308209282528a2942779638a6e2329314101e5ea79627d98b0f1b19830bf0ea89f670f7798bf\"}";
1641
+
1642
+ let fcr: FileContractRenewal = serde_json::from_str(s).unwrap();
1643
+ let sig_hash = fcr.sig_hash(&cs);
1644
+ assert_eq!(
1645
+ hex::encode(sig_hash),
1646
+ "f20822926c53eac2bf91d54e5b39f7de76739226b131448e5508f077ebbccf3b"
1647
+ );
1648
+ }
1649
+
1650
+ #[test]
1651
+ fn test_input_sig_hash() {
1652
+ let cs = test_chain_state();
1653
+ let s = "{\"siacoinInputs\":[{\"parent\":{\"id\":\"af88fb86ace93d500549ad5d3ccae9d75184c932d8b9f6b39b22dc1beb19cb2b\",\"stateElement\":{\"leafIndex\":17247661272366213787,\"merkleProof\":[\"419c0046c59819e4541a3810134dac5c975eb61cb12382c0fbad713257a9efd3\"]},\"siacoinOutput\":{\"value\":\"3088711336000000000000000000000000\",\"address\":\"c6b4adae9284845d1075bc33a68d281bf02ed406206f58a50d13191af7b6f617933bf733cd42\"},\"maturityHeight\":0},\"satisfiedPolicy\":{\"policy\":{\"type\":\"pk\",\"policy\":\"ed25519:e655ec65952c4953c904c9ee16961dfb02e8689f1f82fcfd9e387c8ea1104a2b\"},\"signatures\":[\"7ba834e5e33ca4ac7ad7dbd2b3bd42c7ae62db25263feb2a51777f5c8d5569b1eb28e349544689d3f96242a66d33dcdebeab4e5fa30c906fd2caaf83c16f3bfe\"]}}],\"siacoinOutputs\":[{\"value\":\"1597553234000000000000000000000000\",\"address\":\"bc99db1f50a653604797de23d535c9fbf73b493ab70089c7716c1e5c5fc2d0a578575d367eb0\"}],\"fileContracts\":[{\"capacity\":10874342648285931495,\"filesize\":13810078500867640614,\"fileMerkleRoot\":\"424b9304a9fccf94945ef14c6377060be59fb7d58ac8c952d67be05c67d863a2\",\"proofHeight\":0,\"expirationHeight\":0,\"renterOutput\":{\"value\":\"1556050983000000000000000000000000\",\"address\":\"4505c61036d22e5075c676ea5906a645eb1cfa9a6a53d933ecfa198654b7d3f0fe74bdfbdaef\"},\"hostOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"missedHostValue\":\"0\",\"totalCollateral\":\"0\",\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":18446744073709551615,\"renterSignature\":\"84d8091517ff09f11df171b21a1414200ea7074664c7e27d9de13c44bbb6cbc0de6de5783c6955065a77035bb2d92974fb5f11aa0dae9ac50dbddbb17d63d7fc\",\"hostSignature\":\"2fad832ddb5d6b11893851df4ecbd9a4b512a7a425dda67ac0da1604a0e8781d04e29cef928f31be706e6fc81089048ea87366449450e3da941a0b04487582f2\"}],\"fileContractRevisions\":[{\"parent\":{\"id\":\"22e9e60d81478b36fc44ee67b8d8ba874d196fc6e9c56bfacd5fcecdde39b736\",\"stateElement\":{\"leafIndex\":12023426945600912207,\"merkleProof\":[\"a0efcbdbe1f8e1b4fb6615eccac5948303d5a2c5afedda0baf658e5bbe41896e\"]},\"v2FileContract\":{\"capacity\":10776473623907646048,\"filesize\":7973631990249562263,\"fileMerkleRoot\":\"acd56f68224de7828076cb53f3476413819c8c78dde2461422c5a96644fba623\",\"proofHeight\":0,\"expirationHeight\":0,\"renterOutput\":{\"value\":\"842083234000000000000000000000000\",\"address\":\"150084c56cd2bc38e80220eb69a485b5c298ee3c9f7fcc046c6b975ea8fb70f9098afe1ad85b\"},\"hostOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"missedHostValue\":\"0\",\"totalCollateral\":\"0\",\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":18446744073709551615,\"renterSignature\":\"94dd2555507b81cfd927b36891a9b37465c00cb46a61d96ede5f760f1c5a5cadabb8c7719adb5cb088eec92d9a9c52497973fc2b5d0c54a2dfa04e35490e25b5\",\"hostSignature\":\"d66133988a412e5542535e9a6c3815396c46020a4ad693074d6681049b39c438af9dd395478566e193263e8acfa915848ec23ff47a95eca51bac4ede2a92f634\"}},\"revision\":{\"capacity\":6034084714889303577,\"filesize\":12020548219123782123,\"fileMerkleRoot\":\"2ce9c19107be51ab776221d61300c6c1aa32c2195246473eb15d7db12ec0943f\",\"proofHeight\":0,\"expirationHeight\":0,\"renterOutput\":{\"value\":\"2247488091000000000000000000000000\",\"address\":\"6f7add99e7eba38429b101ff31190aa3a148c0087a3ad775790a8e01a90f71a94ca9ba2ffc71\"},\"hostOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"missedHostValue\":\"0\",\"totalCollateral\":\"0\",\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":18446744073709551615,\"renterSignature\":\"7fb556fef4bfea24f7b9472b1b9874be600795da4535107d3910085a3e34c5818a60bd77d0bcbd92e6dbec604f1b67d827ba02072e83abfac217a2294157c53f\",\"hostSignature\":\"77664782e2c1dd42a3669f11f0519630561a4c0b48552c89bb74f79367f21fd73b6bc5ea112502f81bbc9e3df3b916158486c2e17145b95e5307ca56fb37fc6c\"}}],\"fileContractResolutions\":[{\"parent\":{\"id\":\"6b8527472e1143e07099e3f516f56ac2b46860f13652582b2abb6fffeac1f5d9\",\"stateElement\":{\"leafIndex\":14811616376775388856,\"merkleProof\":[\"bf4325ff8d463ba3bf62e9f697911a4bf8038971298440f4a2c5e010730ac594\"]},\"v2FileContract\":{\"capacity\":6642268570724229223,\"filesize\":15896577861535581162,\"fileMerkleRoot\":\"6e19255c03515dbbee1280f8e13e7378810243042a01a439849c0ddbc0fd7eac\",\"proofHeight\":0,\"expirationHeight\":0,\"renterOutput\":{\"value\":\"2825432965000000000000000000000000\",\"address\":\"2ca7f5d7cbd79fbf4b5f2f5a4ead652342dd0413e39e9406b2935c53162d9347359d0477b320\"},\"hostOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"missedHostValue\":\"0\",\"totalCollateral\":\"0\",\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":18446744073709551615,\"renterSignature\":\"169330702f83d499de6bff18cc7c2474785e02b8101baf8427407e9f6cc3ee6e7768b7e6ef929a6510c1b0ac1c24eab8090af84c27c772c80201fe93da535e17\",\"hostSignature\":\"29bf20483727c327e36c3085a87d91ad3072ca82f5d62da1d9895980320ab7a65d133bb147d55bd97c9f5a36a85afbfb8d9209ac82afaca18df64054394f0f08\"}},\"type\":\"expiration\",\"resolution\":{}},{\"parent\":{\"id\":\"2dd1bedefe81f2914ddf9f61c9d714d134d0af7a75d1c1322474d5e8eea342f2\",\"stateElement\":{\"leafIndex\":11863728860462350395,\"merkleProof\":[\"91a391d6104f76e439580a90f56fec3c07d01ad5535315416243ae6d8e17289d\"]},\"v2FileContract\":{\"capacity\":3158332065060007910,\"filesize\":11561455863259225844,\"fileMerkleRoot\":\"b4db7aa14dc851a348e0981c3ed1161dc286bca85424da42609b3c12f5f31ee6\",\"proofHeight\":0,\"expirationHeight\":0,\"renterOutput\":{\"value\":\"2522086295000000000000000000000000\",\"address\":\"36e4ed130d7a9f0cbcdd0ed6fa78738dcd9eb6e779fc7501dd06f7cf968ce215e33778f0c79e\"},\"hostOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"missedHostValue\":\"0\",\"totalCollateral\":\"0\",\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":18446744073709551615,\"renterSignature\":\"ff79f46439dd108ebe2c42434a11b916f7dee6f018798435b3c34a92ca121d3e21c0282d4efba4982a36e2cfe146e3c8ee31ba545911857a93d947faef543e2f\",\"hostSignature\":\"7f45f2f977e574ea980a0842f48d216b6680d129a98056cafa7f108487457bde8e0cd1cc5d74ba3187170f214115827bc961f0163203c479c3d4ab7e19f3d31f\"}},\"type\":\"storageProof\",\"resolution\":{\"proofIndex\":{\"id\":\"90da2d2ed68b716be7617b8c35c713ad66584a1dbf564cc44c09b6f3815e1d79\",\"stateElement\":{\"leafIndex\":1814794313179331469,\"merkleProof\":[\"8a3990c651140b9486c09559c54623a4e8ca5c2705a7de2b0b54de6d97e6d01d\"]},\"chainIndex\":{\"height\":17891680254001945312,\"id\":\"e5b6bf4a036b93d8a178ffe86ee209502a0ff3d45d2b3216052267b7a5561d21\"}},\"leaf\":\"c5e078423107b4d2c0ab10509404c525343e96eff45ea6353039194470aec04e3994d52cd2d9a6cbbd3709387207f59f063bec4e7266f6fa0c6dfcf7d7634a1c\",\"proof\":[\"f900e345a663fb6ec9eca5721a81e25fae111e50f38aeb76fd6258f2d4cecd86\"]}},{\"parent\":{\"id\":\"49d9f6a8cfae42cd9cc4d792b1799ab2752be164cd86ce5e474fe3731c31b492\",\"stateElement\":{\"leafIndex\":376572300431080610,\"merkleProof\":[\"b2945edc55afbc2344d201baaed373fc0a737c00216186c5e724c3e7d681a705\"]},\"v2FileContract\":{\"capacity\":14165001982102687892,\"filesize\":5514344128076756235,\"fileMerkleRoot\":\"3bcf704f911a925eea72629d71352e1f402fc7d3b632f0df6e203089a6fd9092\",\"proofHeight\":0,\"expirationHeight\":0,\"renterOutput\":{\"value\":\"1548819653000000000000000000000000\",\"address\":\"c1f4c5d401041e4eb68455283ce6705f2dcdc5ac4d700f7dc233b13f91bbfa986aec255c62bb\"},\"hostOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"missedHostValue\":\"0\",\"totalCollateral\":\"0\",\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":18446744073709551615,\"renterSignature\":\"ac99ce31b11341a8835e2666eed317b0042d45ce6ccb52a11a3d2d6c7ec050f432ea8e1fef95b3481da64c5b2b3a772459db094a68d4a3cf52f2e928f3dd7b55\",\"hostSignature\":\"c8a097593157a24390c5fc77bed87c54355e03572ff2b49137801c51ac55ff092141dfb46505808fa97ee22e839d682f9de7f595896338ebf1c4e1885067747c\"}},\"type\":\"renewal\",\"resolution\":{\"finalRevision\":{\"capacity\":6539007356562521256,\"filesize\":17290867016333557028,\"fileMerkleRoot\":\"ebaa3f96d6572cc3caf3036468ddf52ef6fbfcc3eacef4cffd6eabcd43d1b91a\",\"proofHeight\":0,\"expirationHeight\":0,\"renterOutput\":{\"value\":\"2415984205000000000000000000000000\",\"address\":\"1300755d91ac9d739e168f5e54eb2aba9aa0c29cc29142ab4babf0e92be83f17cde6a5ff4ed5\"},\"hostOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"missedHostValue\":\"0\",\"totalCollateral\":\"0\",\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":18446744073709551615,\"renterSignature\":\"a1489731bddba377854020ae706127938ce95c617d22db29b9f9aa331ee27dbfb625b67b3418647b40d4ab4f0075a69a10d66840a3cec4b3c86112ce7e648761\",\"hostSignature\":\"4181fc7c46f09d0124c365a1aeba76f906d99afb3f49d6637f54fc4e2746776a8f3aa343bc823c705c11a581a87ddb918d9bccec0cb2deb46aa4627cf5cf3e80\"},\"newContract\":{\"capacity\":2724141139581307456,\"filesize\":2682841277844293416,\"fileMerkleRoot\":\"62ea0682d909fb36df6557ea2d8174b20db44c0ad1ed692b7b11b40893147e36\",\"proofHeight\":0,\"expirationHeight\":0,\"renterOutput\":{\"value\":\"4004958805000000000000000000000000\",\"address\":\"cc6a403530f6da43fb262723a9536a96666bd74075d64dd7d0eb65df5c494c24b115cdee00c5\"},\"hostOutput\":{\"value\":\"0\",\"address\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"},\"missedHostValue\":\"0\",\"totalCollateral\":\"0\",\"renterPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"hostPublicKey\":\"ed25519:0000000000000000000000000000000000000000000000000000000000000000\",\"revisionNumber\":0,\"renterSignature\":\"ed98b9027f795d3eeafac4928963a55189066fa8a447fd70041d498d6d1072ad5247d36cd657e1f5163889f14591e89159daca5c8c3e645af9b26ac782990cfc\",\"hostSignature\":\"841fff506bfbdc5c9f6dcda6de472dd6bc7e73e5f0974e0bc979fdcc24059246590ef2f6af2b75a786c27f78f43a27e5da91b320736a5cbb7bb15be8375e372e\"},\"renterRollover\":\"3037399761000000000000000000000000\",\"hostRollover\":\"2211277679000000000000000000000000\",\"renterSignature\":\"745f21dae4f1fba04c2d9b09ae69a9e5f096cf26707447fb3b1c17e486b71589b7dc5fda40ef08eac2122b51606b2fbe84211c5f6507631456ac0dd0ee164ef3\",\"hostSignature\":\"0510b572606c3144a4472538b722c2420165efda406b92b74457459d5c5b90a622490d7a70bd7e5e40b031fadd9d3edd90c13226869d8d358512aab5b0145a11\"}}],\"attestations\":[{\"publicKey\":\"ed25519:269ba257f490941f5fcfde5313e326fa205a1a8d91715d91f892581d7282bf21\",\"key\":\"629109f07df18f46\",\"value\":\"Adg+GFWhwVo=\",\"signature\":\"d44eb9a001803f52814adae65da4dc195d760e7afb00b9593716e54a116db7b98ad8e774e101098d31abca3b389a01a08438d7a33508f1f665d2d48f4524509d\"}],\"minerFee\":\"2208027072000000000000000000000000\"}";
1654
+ let txn: Transaction = serde_json::from_str(s).unwrap();
1655
+ let sig_hash = txn.input_sig_hash(&cs);
1656
+ assert_eq!(
1657
+ hex::encode(sig_hash),
1658
+ "aefb1120a3f69fc8293caeb0bd36b4637d6fdf12f2f60494a2875358552f1422"
1659
+ );
1660
+ }
1661
+
1662
+ #[test]
1663
+ fn test_transaction_serialization() {
1664
+ let txn = Transaction {
1665
+ siacoin_inputs: vec![],
1666
+ siacoin_outputs: vec![],
1667
+ siafund_inputs: vec![],
1668
+ siafund_outputs: vec![],
1669
+ file_contracts: vec![],
1670
+ file_contract_revisions: vec![],
1671
+ file_contract_resolutions: vec![],
1672
+ attestations: vec![],
1673
+ new_foundation_address: None,
1674
+ arbitrary_data: vec![],
1675
+ miner_fee: Currency::new(0),
1676
+ };
1677
+ test_serialize_json(&txn, "{\"minerFee\":\"0\"}");
1678
+ test_serialize(&txn, "020000000000000000");
1679
+
1680
+ let txn = Transaction {
1681
+ siacoin_inputs: vec![SiacoinInput {
1682
+ parent: SiacoinElement{
1683
+ id: siacoin_id!("aefb1120a3f69fc8293caeb0bd36b4637d6fdf12f2f60494a2875358552f1422"),
1684
+ state_element: StateElement{
1685
+ leaf_index: 12312413,
1686
+ merkle_proof: vec![hash_256!("aefb1120a3f69fc8293caeb0bd36b4637d6fdf12f2f60494a2875358552f1422")]
1687
+ },
1688
+ siacoin_output: SiacoinOutput{
1689
+ value: Currency::new(100),
1690
+ address: address!("8fb49ccf17dfdcc9526dec6ee8a5cca20ff8247302053d3777410b9b0494ba8cdf32abee86f0")
1691
+ },
1692
+ maturity_height: 0,
1693
+ },
1694
+ satisfied_policy: SatisfiedPolicy {
1695
+ policy: SpendPolicy::public_key(public_key!("ed25519:aefb1120a3f69fc8293caeb0bd36b4637d6fdf12f2f60494a2875358552f1422")),
1696
+ signatures: vec![
1697
+ "aefb1120a3f69fc8293caeb0bd36b4637d6fdf12f2f60494a2875358552f1422aefb1120a3f69fc8293caeb0bd36b4637d6fdf12f2f60494a2875358552f1422".parse().unwrap()
1698
+ ],
1699
+ preimages: vec![],
1700
+ },
1701
+ },
1702
+ ],
1703
+ siacoin_outputs: vec![SiacoinOutput {
1704
+ value: Currency::new(100),
1705
+ address: address!("8fb49ccf17dfdcc9526dec6ee8a5cca20ff8247302053d3777410b9b0494ba8cdf32abee86f0")
1706
+ }],
1707
+ siafund_inputs: vec![],
1708
+ siafund_outputs: vec![],
1709
+ file_contracts: vec![],
1710
+ file_contract_revisions: vec![],
1711
+ file_contract_resolutions: vec![],
1712
+ attestations: vec![],
1713
+ new_foundation_address: None,
1714
+ arbitrary_data: vec![],
1715
+ miner_fee: Currency::new(123581),
1716
+ };
1717
+ test_serialize(
1718
+ &txn,
1719
+ "02030400000000000001000000000000005ddfbb00000000000100000000000000aefb1120a3f69fc8293caeb0bd36b4637d6fdf12f2f60494a2875358552f1422aefb1120a3f69fc8293caeb0bd36b4637d6fdf12f2f60494a2875358552f1422640000000000000000000000000000008fb49ccf17dfdcc9526dec6ee8a5cca20ff8247302053d3777410b9b0494ba8c00000000000000000103aefb1120a3f69fc8293caeb0bd36b4637d6fdf12f2f60494a2875358552f14220100000000000000aefb1120a3f69fc8293caeb0bd36b4637d6fdf12f2f60494a2875358552f1422aefb1120a3f69fc8293caeb0bd36b4637d6fdf12f2f60494a2875358552f142200000000000000000100000000000000640000000000000000000000000000008fb49ccf17dfdcc9526dec6ee8a5cca20ff8247302053d3777410b9b0494ba8cbde20100000000000000000000000000",
1720
+ );
1721
+ test_serialize_json(
1722
+ &txn,
1723
+ "{\"siacoinInputs\":[{\"parent\":{\"stateElement\":{\"leafIndex\":12312413,\"merkleProof\":[\"aefb1120a3f69fc8293caeb0bd36b4637d6fdf12f2f60494a2875358552f1422\"]},\"id\":\"aefb1120a3f69fc8293caeb0bd36b4637d6fdf12f2f60494a2875358552f1422\",\"siacoinOutput\":{\"value\":\"100\",\"address\":\"8fb49ccf17dfdcc9526dec6ee8a5cca20ff8247302053d3777410b9b0494ba8cdf32abee86f0\"},\"maturityHeight\":0},\"satisfiedPolicy\":{\"policy\":{\"type\":\"pk\",\"policy\":\"ed25519:aefb1120a3f69fc8293caeb0bd36b4637d6fdf12f2f60494a2875358552f1422\"},\"signatures\":[\"aefb1120a3f69fc8293caeb0bd36b4637d6fdf12f2f60494a2875358552f1422aefb1120a3f69fc8293caeb0bd36b4637d6fdf12f2f60494a2875358552f1422\"]}}],\"siacoinOutputs\":[{\"value\":\"100\",\"address\":\"8fb49ccf17dfdcc9526dec6ee8a5cca20ff8247302053d3777410b9b0494ba8cdf32abee86f0\"}],\"minerFee\":\"123581\"}",
1724
+ );
1725
+ }
1726
+ }