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,1737 @@
1
+ use core::fmt;
2
+ use std::ops::Deref;
3
+
4
+ use blake2b_simd::Params;
5
+ use num_bigint::BigInt;
6
+ use num_rational::Ratio;
7
+ use num_traits::{FromPrimitive, ToPrimitive};
8
+ use serde::de::Error;
9
+ use serde::{Deserialize, Serialize};
10
+
11
+ use crate::consensus::ChainState;
12
+ use crate::encoding::{
13
+ self, SiaDecodable, SiaDecode, SiaEncodable, SiaEncode, V1SiaDecodable, V1SiaDecode,
14
+ V1SiaEncodable, V1SiaEncode,
15
+ };
16
+ use crate::encoding_async::{AsyncSiaDecodable, AsyncSiaDecode, AsyncSiaEncodable, AsyncSiaEncode};
17
+ use crate::merkle::{Accumulator, LEAF_HASH_PREFIX};
18
+ use crate::signing::PublicKey;
19
+ use crate::types::{Specifier, specifier};
20
+
21
+ use super::currency::Currency;
22
+ use super::{
23
+ Address, FileContractID, Hash256, HexParseError, Leaf, SiacoinOutput, SiacoinOutputID,
24
+ SiafundOutput, SiafundOutputID, StateElement, TransactionID,
25
+ };
26
+
27
+ pub const ALGORITHM_ED25519: Specifier = specifier!["ed25519"];
28
+
29
+ /// A generic public key that can be used to spend a utxo or revise a file
30
+ /// contract
31
+ ///
32
+ /// Currently only supports ed25519 keys
33
+ #[derive(
34
+ Debug,
35
+ PartialEq,
36
+ Clone,
37
+ AsyncSiaDecode,
38
+ AsyncSiaEncode,
39
+ SiaEncode,
40
+ V1SiaEncode,
41
+ SiaDecode,
42
+ V1SiaDecode,
43
+ )]
44
+ pub struct UnlockKey {
45
+ pub algorithm: Specifier,
46
+ pub key: Vec<u8>,
47
+ }
48
+
49
+ impl Serialize for UnlockKey {
50
+ fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
51
+ String::serialize(&self.to_string(), serializer)
52
+ }
53
+ }
54
+
55
+ impl<'de> Deserialize<'de> for UnlockKey {
56
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
57
+ where
58
+ D: serde::Deserializer<'de>,
59
+ {
60
+ if deserializer.is_human_readable() {
61
+ let s = String::deserialize(deserializer)?;
62
+ s.parse().map_err(|e| Error::custom(format!("{e:?}")))
63
+ } else {
64
+ let (algorithm, key) = <(Specifier, Vec<u8>)>::deserialize(deserializer)?;
65
+ Ok(Self { algorithm, key })
66
+ }
67
+ }
68
+ }
69
+
70
+ impl fmt::Display for UnlockKey {
71
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
72
+ write!(f, "{}:{}", self.algorithm, hex::encode(self.key.as_slice()))
73
+ }
74
+ }
75
+
76
+ impl std::str::FromStr for UnlockKey {
77
+ type Err = crate::types::HexParseError;
78
+
79
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
80
+ let (prefix, key_str) = s.split_once(':').ok_or(HexParseError::MissingPrefix)?;
81
+ Ok(UnlockKey {
82
+ algorithm: Specifier::from(prefix),
83
+ key: hex::decode(key_str).map_err(HexParseError::HexError)?,
84
+ })
85
+ }
86
+ }
87
+
88
+ impl From<PublicKey> for UnlockKey {
89
+ fn from(val: PublicKey) -> Self {
90
+ UnlockKey {
91
+ algorithm: ALGORITHM_ED25519,
92
+ key: val.as_ref().to_vec(),
93
+ }
94
+ }
95
+ }
96
+
97
+ /// A FileContractElement is a record of a FileContract within the state accumulator.
98
+ pub struct FileContractElement {
99
+ pub state_element: StateElement,
100
+ pub id: FileContractID,
101
+ pub file_contract: FileContract,
102
+ }
103
+
104
+ // specifies the conditions for spending an output or revising a file contract.
105
+ #[derive(
106
+ Debug,
107
+ PartialEq,
108
+ Clone,
109
+ Serialize,
110
+ Deserialize,
111
+ AsyncSiaDecode,
112
+ AsyncSiaEncode,
113
+ SiaEncode,
114
+ SiaDecode,
115
+ V1SiaEncode,
116
+ V1SiaDecode,
117
+ )]
118
+ #[serde(rename_all = "camelCase")]
119
+ pub struct UnlockConditions {
120
+ pub timelock: u64,
121
+ pub public_keys: Vec<UnlockKey>,
122
+ pub signatures_required: u64,
123
+ }
124
+
125
+ impl UnlockConditions {
126
+ pub fn new(
127
+ timelock: u64,
128
+ public_keys: Vec<UnlockKey>,
129
+ required_signatures: u64,
130
+ ) -> UnlockConditions {
131
+ UnlockConditions {
132
+ timelock,
133
+ public_keys,
134
+ signatures_required: required_signatures,
135
+ }
136
+ }
137
+
138
+ pub fn standard_unlock_conditions(public_key: PublicKey) -> UnlockConditions {
139
+ UnlockConditions {
140
+ timelock: 0,
141
+ public_keys: vec![public_key.into()],
142
+ signatures_required: 1,
143
+ }
144
+ }
145
+
146
+ pub fn address(&self) -> Address {
147
+ let mut acc = Accumulator::new();
148
+ let mut p = Params::new();
149
+ p.hash_length(32);
150
+
151
+ let h = p
152
+ .to_state()
153
+ .update(LEAF_HASH_PREFIX)
154
+ .update(&self.timelock.to_le_bytes())
155
+ .finalize();
156
+
157
+ acc.add_leaf(h.into());
158
+
159
+ for key in self.public_keys.iter() {
160
+ let mut state = p.to_state();
161
+ state.update(LEAF_HASH_PREFIX);
162
+ key.encode(&mut state).unwrap();
163
+
164
+ let h = state.finalize();
165
+ acc.add_leaf(h.into());
166
+ }
167
+
168
+ let h = p
169
+ .to_state()
170
+ .update(LEAF_HASH_PREFIX)
171
+ .update(&self.signatures_required.to_le_bytes())
172
+ .finalize();
173
+
174
+ acc.add_leaf(h.into());
175
+
176
+ Address::new(acc.root().into())
177
+ }
178
+ }
179
+
180
+ #[derive(Debug, PartialEq, Serialize, Deserialize, V1SiaEncode, V1SiaDecode)]
181
+ #[serde(rename_all = "camelCase")]
182
+ pub struct SiacoinInput {
183
+ #[serde(rename = "parentID")]
184
+ pub parent_id: SiacoinOutputID,
185
+ pub unlock_conditions: UnlockConditions,
186
+ }
187
+
188
+ #[derive(Debug, PartialEq, Serialize, Deserialize, V1SiaEncode, V1SiaDecode)]
189
+ #[serde(rename_all = "camelCase")]
190
+ pub struct SiafundInput {
191
+ #[serde(rename = "parentID")]
192
+ pub parent_id: SiafundOutputID,
193
+ pub unlock_conditions: UnlockConditions,
194
+ pub claim_address: Address,
195
+ }
196
+
197
+ #[derive(Debug, PartialEq, Serialize, Deserialize, V1SiaEncode, V1SiaDecode)]
198
+ #[serde(rename_all = "camelCase")]
199
+ pub struct FileContract {
200
+ #[serde(rename = "filesize")]
201
+ pub file_size: u64,
202
+ pub file_merkle_root: Hash256,
203
+ pub window_start: u64,
204
+ pub window_end: u64,
205
+ pub payout: Currency,
206
+ pub valid_proof_outputs: Vec<SiacoinOutput>,
207
+ pub missed_proof_outputs: Vec<SiacoinOutput>,
208
+ pub unlock_hash: Address,
209
+ pub revision_number: u64,
210
+ }
211
+
212
+ impl FileContract {
213
+ pub fn tax(&self, cs: &ChainState) -> Currency {
214
+ let payout = BigInt::from_u128(*self.payout.deref()).unwrap();
215
+ let tax = if cs.child_height() < cs.network.hardfork_tax.height {
216
+ let tax = Ratio::from(payout) * Ratio::from_float(0.039).unwrap();
217
+ tax.numer() / tax.denom()
218
+ } else {
219
+ payout * 39 / 1000
220
+ };
221
+ Currency::new((&tax - &tax % cs.siafund_count()).to_u128().unwrap())
222
+ }
223
+ }
224
+
225
+ #[derive(Debug, PartialEq, Serialize, Deserialize, V1SiaEncode, V1SiaDecode)]
226
+ #[serde(rename_all = "camelCase")]
227
+ pub struct FileContractRevision {
228
+ #[serde(rename = "parentID")]
229
+ pub parent_id: FileContractID,
230
+ pub unlock_conditions: UnlockConditions,
231
+ pub revision_number: u64,
232
+ #[serde(rename = "filesize")]
233
+ pub file_size: u64,
234
+ pub file_merkle_root: Hash256,
235
+ pub window_start: u64,
236
+ pub window_end: u64,
237
+ pub valid_proof_outputs: Vec<SiacoinOutput>,
238
+ pub missed_proof_outputs: Vec<SiacoinOutput>,
239
+ pub unlock_hash: Address,
240
+ }
241
+
242
+ #[derive(Debug, PartialEq, Serialize, Deserialize, V1SiaEncode, V1SiaDecode)]
243
+ #[serde(rename_all = "camelCase")]
244
+ pub struct StorageProof {
245
+ #[serde(rename = "parentID")]
246
+ pub parent_id: FileContractID,
247
+ pub leaf: Leaf,
248
+ pub proof: Vec<Hash256>,
249
+ }
250
+
251
+ #[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize, V1SiaEncode, V1SiaDecode)]
252
+ #[serde(rename_all = "camelCase")]
253
+ pub struct CoveredFields {
254
+ pub whole_transaction: bool,
255
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
256
+ pub siacoin_inputs: Vec<usize>,
257
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
258
+ pub siacoin_outputs: Vec<usize>,
259
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
260
+ pub file_contracts: Vec<usize>,
261
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
262
+ pub file_contract_revisions: Vec<usize>,
263
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
264
+ pub storage_proofs: Vec<usize>,
265
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
266
+ pub siafund_inputs: Vec<usize>,
267
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
268
+ pub siafund_outputs: Vec<usize>,
269
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
270
+ pub miner_fees: Vec<usize>,
271
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
272
+ pub arbitrary_data: Vec<usize>,
273
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
274
+ pub signatures: Vec<usize>,
275
+ }
276
+
277
+ impl CoveredFields {
278
+ pub fn whole_transaction() -> Self {
279
+ CoveredFields {
280
+ whole_transaction: true,
281
+ ..Default::default()
282
+ }
283
+ }
284
+ }
285
+
286
+ #[derive(Debug, PartialEq, Serialize, Deserialize, V1SiaEncode, V1SiaDecode)]
287
+ #[serde(rename_all = "camelCase")]
288
+ pub struct TransactionSignature {
289
+ #[serde(rename = "parentID")]
290
+ pub parent_id: Hash256,
291
+ pub public_key_index: u64,
292
+ pub timelock: u64,
293
+ pub covered_fields: CoveredFields,
294
+ #[serde(with = "crate::types::utils::base64")]
295
+ pub signature: Vec<u8>,
296
+ }
297
+
298
+ #[derive(Default, Debug, PartialEq, Serialize, Deserialize, V1SiaEncode, V1SiaDecode)]
299
+ #[serde(rename_all = "camelCase")]
300
+ pub struct Transaction {
301
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
302
+ pub siacoin_inputs: Vec<SiacoinInput>,
303
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
304
+ pub siacoin_outputs: Vec<SiacoinOutput>,
305
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
306
+ pub file_contracts: Vec<FileContract>,
307
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
308
+ pub file_contract_revisions: Vec<FileContractRevision>,
309
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
310
+ pub storage_proofs: Vec<StorageProof>,
311
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
312
+ pub siafund_inputs: Vec<SiafundInput>,
313
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
314
+ pub siafund_outputs: Vec<SiafundOutput>,
315
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
316
+ pub miner_fees: Vec<Currency>,
317
+ #[serde(
318
+ default,
319
+ skip_serializing_if = "Vec::is_empty",
320
+ with = "crate::types::utils::vec_base64"
321
+ )]
322
+ pub arbitrary_data: Vec<Vec<u8>>,
323
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
324
+ pub signatures: Vec<TransactionSignature>,
325
+ }
326
+
327
+ impl Transaction {
328
+ pub fn encode_no_sigs<W: std::io::Write>(&self, w: &mut W) -> Result<(), encoding::Error> {
329
+ self.siacoin_inputs.encode_v1(w)?;
330
+ self.siacoin_outputs.encode_v1(w)?;
331
+ self.file_contracts.encode_v1(w)?;
332
+ self.file_contract_revisions.encode_v1(w)?;
333
+ self.storage_proofs.encode_v1(w)?;
334
+ self.siafund_inputs.encode_v1(w)?;
335
+ self.siafund_outputs.encode_v1(w)?;
336
+ self.miner_fees.encode_v1(w)?;
337
+ self.arbitrary_data.encode_v1(w)
338
+ }
339
+
340
+ pub fn whole_sig_hash<T: AsRef<[u8; 32]>>(
341
+ &self,
342
+ cs: &ChainState,
343
+ parent_id: &T,
344
+ public_key_index: u64,
345
+ timelock: u64,
346
+ ) -> Result<Hash256, encoding::Error> {
347
+ let mut state = Params::new().hash_length(32).to_state();
348
+
349
+ state.update(&(self.siacoin_inputs.len() as u64).to_le_bytes());
350
+ for input in self.siacoin_inputs.iter() {
351
+ state.update(cs.replay_prefix());
352
+ input.encode_v1(&mut state)?;
353
+ }
354
+
355
+ self.siacoin_outputs.encode_v1(&mut state)?;
356
+ self.file_contracts.encode_v1(&mut state)?;
357
+ self.file_contract_revisions.encode_v1(&mut state)?;
358
+ self.storage_proofs.encode_v1(&mut state)?;
359
+
360
+ state.update(&(self.siafund_inputs.len() as u64).to_le_bytes());
361
+ for input in self.siafund_inputs.iter() {
362
+ state.update(cs.replay_prefix());
363
+ input.encode_v1(&mut state)?;
364
+ }
365
+
366
+ self.siafund_outputs.encode_v1(&mut state)?;
367
+ self.miner_fees.encode_v1(&mut state)?;
368
+ self.arbitrary_data.encode_v1(&mut state)?;
369
+
370
+ state.update(parent_id.as_ref());
371
+ public_key_index.encode_v1(&mut state)?;
372
+ timelock.encode_v1(&mut state)?;
373
+
374
+ Ok(state.finalize().into())
375
+ }
376
+
377
+ pub fn partial_sig_hash(
378
+ &self,
379
+ cs: &ChainState,
380
+ covered_fields: &CoveredFields,
381
+ ) -> Result<Hash256, encoding::Error> {
382
+ let mut state = Params::new().hash_length(32).to_state();
383
+
384
+ for &i in covered_fields.siacoin_inputs.iter() {
385
+ if i >= self.siacoin_inputs.len() {
386
+ return Err(encoding::Error::Custom(
387
+ "siacoin_inputs index out of bounds".to_string(),
388
+ ));
389
+ }
390
+ state.update(cs.replay_prefix());
391
+ self.siacoin_inputs[i].encode_v1(&mut state)?;
392
+ }
393
+
394
+ for &i in covered_fields.siacoin_outputs.iter() {
395
+ if i >= self.siacoin_outputs.len() {
396
+ return Err(encoding::Error::Custom(
397
+ "siacoin_outputs index out of bounds".to_string(),
398
+ ));
399
+ }
400
+ self.siacoin_outputs[i].encode_v1(&mut state)?;
401
+ }
402
+
403
+ for &i in covered_fields.file_contracts.iter() {
404
+ if i >= self.file_contracts.len() {
405
+ return Err(encoding::Error::Custom(
406
+ "file_contracts index out of bounds".to_string(),
407
+ ));
408
+ }
409
+ self.file_contracts[i].encode_v1(&mut state)?;
410
+ }
411
+
412
+ for &i in covered_fields.file_contract_revisions.iter() {
413
+ if i >= self.file_contract_revisions.len() {
414
+ return Err(encoding::Error::Custom(
415
+ "file_contract_revisions index out of bounds".to_string(),
416
+ ));
417
+ }
418
+ self.file_contract_revisions[i].encode_v1(&mut state)?;
419
+ }
420
+
421
+ for &i in covered_fields.storage_proofs.iter() {
422
+ if i >= self.storage_proofs.len() {
423
+ return Err(encoding::Error::Custom(
424
+ "storage_proofs index out of bounds".to_string(),
425
+ ));
426
+ }
427
+ self.storage_proofs[i].encode_v1(&mut state)?;
428
+ }
429
+
430
+ for &i in covered_fields.siafund_inputs.iter() {
431
+ if i >= self.siafund_inputs.len() {
432
+ return Err(encoding::Error::Custom(
433
+ "siafund_inputs index out of bounds".to_string(),
434
+ ));
435
+ }
436
+ state.update(cs.replay_prefix());
437
+ self.siafund_inputs[i].encode_v1(&mut state)?;
438
+ }
439
+
440
+ for &i in covered_fields.siafund_outputs.iter() {
441
+ if i >= self.siafund_outputs.len() {
442
+ return Err(encoding::Error::Custom(
443
+ "siafund_outputs index out of bounds".to_string(),
444
+ ));
445
+ }
446
+ self.siafund_outputs[i].encode_v1(&mut state)?;
447
+ }
448
+
449
+ for &i in covered_fields.miner_fees.iter() {
450
+ if i >= self.miner_fees.len() {
451
+ return Err(encoding::Error::Custom(
452
+ "miner_fees index out of bounds".to_string(),
453
+ ));
454
+ }
455
+ self.miner_fees[i].encode_v1(&mut state)?;
456
+ }
457
+
458
+ for &i in covered_fields.arbitrary_data.iter() {
459
+ if i >= self.arbitrary_data.len() {
460
+ return Err(encoding::Error::Custom(
461
+ "arbitrary_data index out of bounds".to_string(),
462
+ ));
463
+ }
464
+ self.arbitrary_data[i].encode_v1(&mut state)?;
465
+ }
466
+
467
+ for &i in covered_fields.signatures.iter() {
468
+ if i >= self.signatures.len() {
469
+ return Err(encoding::Error::Custom(
470
+ "signatures index out of bounds".to_string(),
471
+ ));
472
+ }
473
+ self.signatures[i].encode_v1(&mut state)?;
474
+ }
475
+ Ok(state.finalize().into())
476
+ }
477
+
478
+ pub fn id(&self) -> TransactionID {
479
+ let mut state = Params::new().hash_length(32).to_state();
480
+ self.encode_no_sigs(&mut state).unwrap();
481
+
482
+ state.finalize().into()
483
+ }
484
+
485
+ fn derive_child_id<T: From<blake2b_simd::Hash>>(&self, prefix: &Specifier, i: usize) -> T {
486
+ let mut state = Params::new().hash_length(32).to_state();
487
+ state.update(prefix.as_ref());
488
+ self.encode_no_sigs(&mut state).unwrap();
489
+ state.update(&(i as u64).to_le_bytes());
490
+ state.finalize().into()
491
+ }
492
+
493
+ /// siacoin_output_id returns the SiacoinOutputID for the i-th siacoin output of the transaction
494
+ pub fn siacoin_output_id(&self, i: usize) -> SiacoinOutputID {
495
+ const SIACOIN_OUTPUT_ID_PREFIX: Specifier = specifier!("siacoin output");
496
+
497
+ self.derive_child_id(&SIACOIN_OUTPUT_ID_PREFIX, i)
498
+ }
499
+
500
+ /// siafund_output_id returns the SiafundOutputID for the i-th siafund output of the transaction
501
+ pub fn siafund_output_id(&self, i: usize) -> SiafundOutputID {
502
+ const SIAFUND_OUTPUT_ID_PREFIX: Specifier = specifier!("siafund output");
503
+
504
+ self.derive_child_id(&SIAFUND_OUTPUT_ID_PREFIX, i)
505
+ }
506
+
507
+ /// file_contract_id returns the FileContractID for the i-th file contract of the transaction
508
+ pub fn file_contract_id(&self, i: usize) -> FileContractID {
509
+ const FILE_CONTRACT_ID_PREFIX: Specifier = specifier!("file contract");
510
+
511
+ self.derive_child_id(&FILE_CONTRACT_ID_PREFIX, i)
512
+ }
513
+ }
514
+
515
+ #[cfg(test)]
516
+ mod tests {
517
+ use super::*;
518
+ use crate::consensus::{
519
+ ElementAccumulator, HardforkASIC, HardforkDevAddr, HardforkFoundation, HardforkOak,
520
+ HardforkStorageProof, HardforkTax, HardforkV2, Network, State,
521
+ };
522
+ use crate::seed::Seed;
523
+ use crate::signing::{PrivateKey, PublicKey, Signature};
524
+ use crate::types::{BlockID, ChainIndex, Work};
525
+ use crate::{address, contract_id, hash_256, siacoin_id, siafund_id};
526
+ use chrono::{DateTime, Duration};
527
+ use serde::de::DeserializeOwned;
528
+ use std::fmt::Debug;
529
+ use std::vec;
530
+
531
+ /// test_serialize_json is a helper to test serialization and deserialization of a struct to and from JSON.
532
+ fn test_serialize_json<S: Serialize + DeserializeOwned + Debug + PartialEq>(
533
+ obj: &S,
534
+ json_str: &str,
535
+ ) {
536
+ let serialized = serde_json::to_string(&obj).unwrap();
537
+ assert_eq!(serialized, json_str);
538
+ let deserialized: S = serde_json::from_str(&serialized).unwrap();
539
+ assert_eq!(deserialized, *obj);
540
+ }
541
+
542
+ /// test_serialize_v1 is a helper to test serialization and deserialization of a struct to and from Sia's
543
+ /// custom binary encoding.
544
+ fn test_serialize_v1<S: V1SiaEncodable + V1SiaDecodable + Debug + PartialEq>(
545
+ obj: &S,
546
+ hex_binary: &str,
547
+ ) {
548
+ let mut serialized = Vec::new();
549
+ obj.encode_v1(&mut serialized).unwrap();
550
+ assert_eq!(hex::encode(serialized.clone()), hex_binary);
551
+ let deserialized = S::decode_v1(&mut &serialized[..]).unwrap();
552
+ assert_eq!(deserialized, *obj);
553
+ }
554
+
555
+ #[test]
556
+ fn test_serialize_unlock_key() {
557
+ let unlock_key: UnlockKey = PublicKey::new([
558
+ 0x9a, 0xac, 0x1f, 0xfb, 0x1c, 0xfd, 0x10, 0x79, 0xa8, 0xc6, 0xc8, 0x7b, 0x47, 0xda,
559
+ 0x1d, 0x56, 0x7e, 0x35, 0xb9, 0x72, 0x34, 0x99, 0x3c, 0x28, 0x8c, 0x1a, 0xd0, 0xdb,
560
+ 0x1d, 0x1c, 0xe1, 0xb6,
561
+ ])
562
+ .into();
563
+
564
+ // binary
565
+ let mut unlock_key_serialized: Vec<u8> = Vec::new();
566
+ unlock_key.encode(&mut unlock_key_serialized).unwrap();
567
+ assert_eq!(
568
+ unlock_key_serialized,
569
+ [
570
+ 0x65, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
571
+ 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0xac, 0x1f, 0xfb,
572
+ 0x1c, 0xfd, 0x10, 0x79, 0xa8, 0xc6, 0xc8, 0x7b, 0x47, 0xda, 0x1d, 0x56, 0x7e, 0x35,
573
+ 0xb9, 0x72, 0x34, 0x99, 0x3c, 0x28, 0x8c, 0x1a, 0xd0, 0xdb, 0x1d, 0x1c, 0xe1, 0xb6
574
+ ]
575
+ );
576
+ //assert_eq!(unlock_key_deserialized, unlock_key);
577
+
578
+ // json
579
+ let unlock_key_serialized = serde_json::to_string(&unlock_key).unwrap();
580
+ let unlock_key_deserialized: UnlockKey =
581
+ serde_json::from_str(&unlock_key_serialized).unwrap();
582
+ assert_eq!(
583
+ unlock_key_serialized,
584
+ "\"ed25519:9aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b6\""
585
+ );
586
+ assert_eq!(unlock_key_deserialized, unlock_key);
587
+ }
588
+
589
+ #[test]
590
+ fn test_serialize_unlock_conditions() {
591
+ let unlock_conditions = UnlockConditions::new(
592
+ 123,
593
+ vec![
594
+ PublicKey::new([
595
+ 0x9a, 0xac, 0x1f, 0xfb, 0x1c, 0xfd, 0x10, 0x79, 0xa8, 0xc6, 0xc8, 0x7b, 0x47,
596
+ 0xda, 0x1d, 0x56, 0x7e, 0x35, 0xb9, 0x72, 0x34, 0x99, 0x3c, 0x28, 0x8c, 0x1a,
597
+ 0xd0, 0xdb, 0x1d, 0x1c, 0xe1, 0xb6,
598
+ ])
599
+ .into(),
600
+ ],
601
+ 1,
602
+ );
603
+
604
+ // binary
605
+ let mut unlock_conditions_serialized: Vec<u8> = Vec::new();
606
+ unlock_conditions
607
+ .encode(&mut unlock_conditions_serialized)
608
+ .unwrap();
609
+
610
+ assert_eq!(
611
+ unlock_conditions_serialized,
612
+ [
613
+ 123, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 101, 100, 50, 53, 53, 49, 57, 0,
614
+ 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 154, 172, 31, 251, 28, 253, 16,
615
+ 121, 168, 198, 200, 123, 71, 218, 29, 86, 126, 53, 185, 114, 52, 153, 60, 40, 140,
616
+ 26, 208, 219, 29, 28, 225, 182, 1, 0, 0, 0, 0, 0, 0, 0
617
+ ]
618
+ );
619
+ //assert_eq!(unlock_conditions_deserialized, unlock_conditions);
620
+
621
+ // json
622
+ let unlock_conditions_serialized = serde_json::to_string(&unlock_conditions).unwrap();
623
+ let unlock_conditions_deserialized: UnlockConditions =
624
+ serde_json::from_str(&unlock_conditions_serialized).unwrap();
625
+ assert_eq!(
626
+ unlock_conditions_serialized,
627
+ "{\"timelock\":123,\"publicKeys\":[\"ed25519:9aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b6\"],\"signaturesRequired\":1}"
628
+ );
629
+ assert_eq!(unlock_conditions_deserialized, unlock_conditions);
630
+ }
631
+
632
+ #[test]
633
+ fn test_serialize_covered_fields() {
634
+ let mut cf = CoveredFields::default();
635
+ cf.siacoin_inputs.push(1);
636
+ cf.siacoin_outputs.push(2);
637
+ cf.siacoin_outputs.push(3);
638
+
639
+ let binary_str = "000100000000000000010000000000000002000000000000000200000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
640
+ test_serialize_v1(&cf, binary_str);
641
+
642
+ let json_str =
643
+ "{\"wholeTransaction\":false,\"siacoinInputs\":[1],\"siacoinOutputs\":[2,3]}";
644
+ test_serialize_json(&cf, json_str);
645
+ }
646
+
647
+ #[test]
648
+ fn test_serialize_siacoin_input() {
649
+ let siacoin_input = SiacoinInput {
650
+ parent_id: siacoin_id!(
651
+ "b3633a1370a72002ae2a956d21e8d481c3a69e146633470cf625ecd83fdeaa24"
652
+ ),
653
+ unlock_conditions: UnlockConditions::new(
654
+ 123,
655
+ vec![
656
+ PublicKey::new([
657
+ 0x9a, 0xac, 0x1f, 0xfb, 0x1c, 0xfd, 0x10, 0x79, 0xa8, 0xc6, 0xc8, 0x7b,
658
+ 0x47, 0xda, 0x1d, 0x56, 0x7e, 0x35, 0xb9, 0x72, 0x34, 0x99, 0x3c, 0x28,
659
+ 0x8c, 0x1a, 0xd0, 0xdb, 0x1d, 0x1c, 0xe1, 0xb6,
660
+ ])
661
+ .into(),
662
+ ],
663
+ 1,
664
+ ),
665
+ };
666
+
667
+ let binary_str = hex::encode([
668
+ 179, 99, 58, 19, 112, 167, 32, 2, 174, 42, 149, 109, 33, 232, 212, 129, 195, 166, 158,
669
+ 20, 102, 51, 71, 12, 246, 37, 236, 216, 63, 222, 170, 36, 123, 0, 0, 0, 0, 0, 0, 0, 1,
670
+ 0, 0, 0, 0, 0, 0, 0, 101, 100, 50, 53, 53, 49, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0,
671
+ 0, 0, 0, 0, 0, 154, 172, 31, 251, 28, 253, 16, 121, 168, 198, 200, 123, 71, 218, 29,
672
+ 86, 126, 53, 185, 114, 52, 153, 60, 40, 140, 26, 208, 219, 29, 28, 225, 182, 1, 0, 0,
673
+ 0, 0, 0, 0, 0,
674
+ ]);
675
+ test_serialize_v1(&siacoin_input, binary_str.as_str());
676
+
677
+ let json_str = "{\"parentID\":\"b3633a1370a72002ae2a956d21e8d481c3a69e146633470cf625ecd83fdeaa24\",\"unlockConditions\":{\"timelock\":123,\"publicKeys\":[\"ed25519:9aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b6\"],\"signaturesRequired\":1}}";
678
+ test_serialize_json(&siacoin_input, json_str);
679
+ }
680
+
681
+ #[test]
682
+ fn test_serialize_siafund_input() {
683
+ let siafund_input = SiafundInput {
684
+ parent_id: siafund_id!(
685
+ "b3633a1370a72002ae2a956d21e8d481c3a69e146633470cf625ecd83fdeaa24"
686
+ ),
687
+ unlock_conditions: UnlockConditions::new(
688
+ 123,
689
+ vec![
690
+ PublicKey::new([
691
+ 0x9a, 0xac, 0x1f, 0xfb, 0x1c, 0xfd, 0x10, 0x79, 0xa8, 0xc6, 0xc8, 0x7b,
692
+ 0x47, 0xda, 0x1d, 0x56, 0x7e, 0x35, 0xb9, 0x72, 0x34, 0x99, 0x3c, 0x28,
693
+ 0x8c, 0x1a, 0xd0, 0xdb, 0x1d, 0x1c, 0xe1, 0xb6,
694
+ ])
695
+ .into(),
696
+ ],
697
+ 1,
698
+ ),
699
+ claim_address: Address::new(
700
+ hex::decode("8fb49ccf17dfdcc9526dec6ee8a5cca20ff8247302053d3777410b9b0494ba8c")
701
+ .unwrap()
702
+ .try_into()
703
+ .unwrap(),
704
+ ),
705
+ };
706
+
707
+ // binary
708
+ let binary_str = hex::encode([
709
+ 179, 99, 58, 19, 112, 167, 32, 2, 174, 42, 149, 109, 33, 232, 212, 129, 195, 166, 158,
710
+ 20, 102, 51, 71, 12, 246, 37, 236, 216, 63, 222, 170, 36, 123, 0, 0, 0, 0, 0, 0, 0, 1,
711
+ 0, 0, 0, 0, 0, 0, 0, 101, 100, 50, 53, 53, 49, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0,
712
+ 0, 0, 0, 0, 0, 154, 172, 31, 251, 28, 253, 16, 121, 168, 198, 200, 123, 71, 218, 29,
713
+ 86, 126, 53, 185, 114, 52, 153, 60, 40, 140, 26, 208, 219, 29, 28, 225, 182, 1, 0, 0,
714
+ 0, 0, 0, 0, 0, 143, 180, 156, 207, 23, 223, 220, 201, 82, 109, 236, 110, 232, 165, 204,
715
+ 162, 15, 248, 36, 115, 2, 5, 61, 55, 119, 65, 11, 155, 4, 148, 186, 140,
716
+ ]);
717
+ test_serialize_v1(&siafund_input, binary_str.as_str());
718
+
719
+ let json_str = "{\"parentID\":\"b3633a1370a72002ae2a956d21e8d481c3a69e146633470cf625ecd83fdeaa24\",\"unlockConditions\":{\"timelock\":123,\"publicKeys\":[\"ed25519:9aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b6\"],\"signaturesRequired\":1},\"claimAddress\":\"8fb49ccf17dfdcc9526dec6ee8a5cca20ff8247302053d3777410b9b0494ba8cdf32abee86f0\"}";
720
+ test_serialize_json(&siafund_input, json_str);
721
+ }
722
+
723
+ #[test]
724
+ fn test_serialize_transaction_signature() {
725
+ let signature = TransactionSignature {
726
+ parent_id: hash_256!(
727
+ "b3633a1370a72002ae2a956d21e8d481c3a69e146633470cf625ecd83fdeaa24"
728
+ ),
729
+ public_key_index: 1,
730
+ timelock: 2,
731
+ covered_fields: CoveredFields {
732
+ whole_transaction: true,
733
+ ..Default::default()
734
+ },
735
+ signature: Signature::new([3u8; 64]).data().to_vec(),
736
+ };
737
+
738
+ let binary_str = hex::encode([
739
+ 179, 99, 58, 19, 112, 167, 32, 2, 174, 42, 149, 109, 33, 232, 212, 129, 195, 166, 158,
740
+ 20, 102, 51, 71, 12, 246, 37, 236, 216, 63, 222, 170, 36, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0,
741
+ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
742
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
743
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
744
+ 64, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
745
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
746
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
747
+ ]);
748
+ test_serialize_v1(&signature, binary_str.as_str());
749
+
750
+ let json_str = "{\"parentID\":\"b3633a1370a72002ae2a956d21e8d481c3a69e146633470cf625ecd83fdeaa24\",\"publicKeyIndex\":1,\"timelock\":2,\"coveredFields\":{\"wholeTransaction\":true},\"signature\":\"AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw==\"}";
751
+ test_serialize_json(&signature, json_str);
752
+ }
753
+
754
+ #[test]
755
+ fn test_serialize_filecontract() {
756
+ let contract = FileContract {
757
+ file_size: 1,
758
+ file_merkle_root: Hash256::from([
759
+ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
760
+ 0, 0, 0, 0,
761
+ ]),
762
+ window_start: 2,
763
+ window_end: 3,
764
+ payout: Currency::new(456),
765
+ valid_proof_outputs: vec![SiacoinOutput {
766
+ value: Currency::new(789),
767
+ address: Address::new([
768
+ 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
769
+ 0, 0, 0, 0, 0, 0,
770
+ ]),
771
+ }],
772
+ missed_proof_outputs: vec![SiacoinOutput {
773
+ value: Currency::new(101112),
774
+ address: Address::new([
775
+ 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
776
+ 0, 0, 0, 0, 0, 0,
777
+ ]),
778
+ }],
779
+ unlock_hash: Address::from([
780
+ 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
781
+ 0, 0, 0, 0,
782
+ ]),
783
+ revision_number: 4,
784
+ };
785
+
786
+ let binary_str = hex::encode([
787
+ 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
788
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 2, 0,
789
+ 0, 0, 0, 0, 0, 0, 1, 200, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 21, 2, 2,
790
+ 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
791
+ 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 1, 138, 248, 3, 3, 3, 0, 0, 0, 0, 0,
792
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0,
793
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0,
794
+ 0, 0, 0, 0, 0, 0,
795
+ ]);
796
+ test_serialize_v1(&contract, binary_str.as_str());
797
+
798
+ let json_str = "{\"filesize\":1,\"fileMerkleRoot\":\"0101010000000000000000000000000000000000000000000000000000000000\",\"windowStart\":2,\"windowEnd\":3,\"payout\":\"456\",\"validProofOutputs\":[{\"value\":\"789\",\"address\":\"02020200000000000000000000000000000000000000000000000000000000008749787b31db\"}],\"missedProofOutputs\":[{\"value\":\"101112\",\"address\":\"0303030000000000000000000000000000000000000000000000000000000000c596d559a239\"}],\"unlockHash\":\"04040400000000000000000000000000000000000000000000000000000000006c604f10c928\",\"revisionNumber\":4}";
799
+ test_serialize_json(&contract, json_str);
800
+ }
801
+
802
+ #[test]
803
+ fn test_serialize_filecontract_revision() {
804
+ let revision = FileContractRevision {
805
+ parent_id: FileContractID::from([
806
+ 9, 8, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
807
+ 0, 0, 0, 0,
808
+ ]),
809
+ file_size: 1,
810
+ file_merkle_root: Hash256::from([
811
+ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
812
+ 0, 0, 0, 0,
813
+ ]),
814
+ window_start: 2,
815
+ window_end: 3,
816
+ valid_proof_outputs: vec![SiacoinOutput {
817
+ value: Currency::new(789),
818
+ address: Address::new([
819
+ 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
820
+ 0, 0, 0, 0, 0, 0,
821
+ ]),
822
+ }],
823
+ missed_proof_outputs: vec![SiacoinOutput {
824
+ value: Currency::new(789),
825
+ address: Address::new([
826
+ 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
827
+ 0, 0, 0, 0, 0, 0,
828
+ ]),
829
+ }],
830
+ unlock_conditions: UnlockConditions::new(
831
+ 123,
832
+ vec![
833
+ PublicKey::new([
834
+ 0x9a, 0xac, 0x1f, 0xfb, 0x1c, 0xfd, 0x10, 0x79, 0xa8, 0xc6, 0xc8, 0x7b,
835
+ 0x47, 0xda, 0x1d, 0x56, 0x7e, 0x35, 0xb9, 0x72, 0x34, 0x99, 0x3c, 0x28,
836
+ 0x8c, 0x1a, 0xd0, 0xdb, 0x1d, 0x1c, 0xe1, 0xb6,
837
+ ])
838
+ .into(),
839
+ ],
840
+ 1,
841
+ ),
842
+ unlock_hash: Address::from([
843
+ 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
844
+ 0, 0, 0, 0,
845
+ ]),
846
+ revision_number: 4,
847
+ };
848
+
849
+ let binary_str = hex::encode([
850
+ 9, 8, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
851
+ 0, 0, 0, 123, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 101, 100, 50, 53, 53, 49,
852
+ 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 154, 172, 31, 251, 28, 253, 16,
853
+ 121, 168, 198, 200, 123, 71, 218, 29, 86, 126, 53, 185, 114, 52, 153, 60, 40, 140, 26,
854
+ 208, 219, 29, 28, 225, 182, 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
855
+ 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
856
+ 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
857
+ 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 21, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
858
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0,
859
+ 0, 0, 3, 21, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
860
+ 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
861
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
862
+ ]);
863
+ test_serialize_v1(&revision, binary_str.as_str());
864
+
865
+ let json_str = "{\"parentID\":\"0908070000000000000000000000000000000000000000000000000000000000\",\"unlockConditions\":{\"timelock\":123,\"publicKeys\":[\"ed25519:9aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b6\"],\"signaturesRequired\":1},\"revisionNumber\":4,\"filesize\":1,\"fileMerkleRoot\":\"0101010000000000000000000000000000000000000000000000000000000000\",\"windowStart\":2,\"windowEnd\":3,\"validProofOutputs\":[{\"value\":\"789\",\"address\":\"02020200000000000000000000000000000000000000000000000000000000008749787b31db\"}],\"missedProofOutputs\":[{\"value\":\"789\",\"address\":\"0303030000000000000000000000000000000000000000000000000000000000c596d559a239\"}],\"unlockHash\":\"04040400000000000000000000000000000000000000000000000000000000006c604f10c928\"}";
866
+ test_serialize_json(&revision, json_str);
867
+ }
868
+
869
+ #[test]
870
+ fn test_serialize_storage_proof() {
871
+ let storage_proof = StorageProof {
872
+ parent_id: contract_id!(
873
+ "b3633a1370a72002ae2a956d21e8d481c3a69e146633470cf625ecd83fdeaa24"
874
+ ),
875
+ leaf: Leaf::from([
876
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
877
+ 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
878
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
879
+ ]),
880
+ proof: vec![
881
+ hash_256!("0102030000000000000000000000000000000000000000000000000000000000"),
882
+ hash_256!("0405060000000000000000000000000000000000000000000000000000000000"),
883
+ ],
884
+ };
885
+
886
+ let binary_str = hex::encode([
887
+ 179, 99, 58, 19, 112, 167, 32, 2, 174, 42, 149, 109, 33, 232, 212, 129, 195, 166, 158,
888
+ 20, 102, 51, 71, 12, 246, 37, 236, 216, 63, 222, 170, 36, 1, 2, 3, 4, 5, 6, 7, 8, 9,
889
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
890
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
891
+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 2, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0,
892
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 6,
893
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
894
+ ]);
895
+ test_serialize_v1(&storage_proof, binary_str.as_str());
896
+
897
+ let json_str = "{\"parentID\":\"b3633a1370a72002ae2a956d21e8d481c3a69e146633470cf625ecd83fdeaa24\",\"leaf\":\"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40\",\"proof\":[\"0102030000000000000000000000000000000000000000000000000000000000\",\"0405060000000000000000000000000000000000000000000000000000000000\"]}";
898
+ test_serialize_json(&storage_proof, json_str);
899
+ }
900
+
901
+ #[test]
902
+ fn test_serialize_transaction() {
903
+ let transaction = Transaction {
904
+ siacoin_inputs: vec![SiacoinInput {
905
+ parent_id: siacoin_id!(
906
+ "750d22eff727689d1d8d1c83e513a30bb68ee7f9125a4dafc882459e34c2069d"
907
+ ),
908
+ unlock_conditions: UnlockConditions {
909
+ timelock: 0,
910
+ public_keys: vec!["ed25519:800ed6c2760e3e4ba1ff00128585c8cf8fed2e3dc1e3da1eb92d49f405bd6360".parse().unwrap()],
911
+ signatures_required: 6312611591377486220,
912
+ },
913
+ }],
914
+ siacoin_outputs: vec![SiacoinOutput {
915
+ value: Currency::new(890415399000000000000000000000000),
916
+ address: address!("480a064b5fca13002a7fe575845154bbf0b3af4cc4f147cbed387d43cce3568ae2497366eaa7"),
917
+ }],
918
+ file_contracts: vec![
919
+ FileContract{
920
+ file_size: 0,
921
+ file_merkle_root: Hash256::default(),
922
+ window_start: 10536451586783908586,
923
+ window_end: 9324702155635244357,
924
+ payout: Currency::new(0),
925
+ unlock_hash: Address::default(),
926
+ revision_number: 9657412421282982780,
927
+ valid_proof_outputs: vec![
928
+ SiacoinOutput{
929
+ value: Currency::new(1933513214000000000000000000000000),
930
+ address: address!("944524fff2c49c401e748db37cfda7569fa6df35b704fe716394f2ac3f40ce87b4506e9906f0"),
931
+ }
932
+ ],
933
+ missed_proof_outputs: vec![
934
+ SiacoinOutput{
935
+ value: Currency::new(2469287901000000000000000000000000),
936
+ address: address!("1df67838262d7109ffcd9018f183b1eb33f05659a274b89ea6b52ff3617d34a770e9dd071d2e"),
937
+ }
938
+ ]
939
+ }
940
+ ],
941
+ file_contract_revisions: vec![
942
+ FileContractRevision{
943
+ parent_id: contract_id!(
944
+ "e4e26d93771d3bbb3d9dd306105d77cfb3a6254d1cc3495903af6e013442c63c"),
945
+ unlock_conditions: UnlockConditions { timelock: 0, public_keys: vec!["ed25519:e6b9cde4eb058f8ecbb083d99779cb0f6d518d5386f019af6ead09fa52de8567".parse().unwrap()], signatures_required: 206644730660526450 },
946
+ revision_number: 10595710523108536025,
947
+ file_size: 0,
948
+ file_merkle_root: Hash256::default(),
949
+ window_start: 4348934140507359445,
950
+ window_end: 14012366839994454386,
951
+ valid_proof_outputs: vec![
952
+ SiacoinOutput{
953
+ value: Currency::new(2435858510000000000000000000000000),
954
+ address: address!("543bc0eda69f728d0a0fbce08e5bfc5ed7b961300e0af226949e135f7d12e32f0544e5262d6f"),
955
+ }
956
+ ],
957
+ missed_proof_outputs: vec![
958
+ SiacoinOutput{
959
+ value: Currency::new(880343701000000000000000000000000),
960
+ address: address!("7b7f9aee981fe0d93bb3f49c6233cf847ebdd39d7dc5253f7fc330df2167073b35f035703237"),
961
+ },
962
+ ],
963
+ unlock_hash: Address::default(),
964
+ }
965
+ ],
966
+ storage_proofs: vec![
967
+ StorageProof{
968
+ parent_id: contract_id!(
969
+ "c0b9e98c9e03a2740c75d673871c1ee91f36d1bb329ff3ddbf1dfa8c6e1a64eb"),
970
+ leaf: "b78fa521dc62d9ced82bc3b61e0aa5a5c221d6cca5db63d94c9879543fb98c0a971094a89cd4408487ae32902248d321b545f9a051729aa0bb1725b848e3d453".parse().unwrap(),
971
+ proof: vec![
972
+ hash_256!("fe08c0a061475e7e5dec19e717cf98792fa7b555d0b5d3540a05db09f59ab8de"),
973
+ ],
974
+ }
975
+ ],
976
+ siafund_inputs: Vec::new(),
977
+ siafund_outputs: Vec::new(),
978
+ miner_fees: vec![
979
+ Currency::new(241119475000000000000000000000000),
980
+ ],
981
+ arbitrary_data: vec![
982
+ vec![218,200,115,32,113,20,37,140,46,52,124,250,115,248,15,207,230,147,17,102,81,78,144,211,153,233,151,247,38,0,42,86]
983
+ ],
984
+ signatures: vec![
985
+ TransactionSignature{
986
+ parent_id: hash_256!("06d1fca03c5ddd9b09116db1b97c5451f7dc792b05362969f83e3e8dc1007f46"),
987
+ public_key_index: 6088345341283457116,
988
+ timelock: 2014247885072555224,
989
+ covered_fields: CoveredFields::whole_transaction(),
990
+ signature: vec![217,115,68,40,102,107,151,212,97,49,173,137,152,107,47,114,106,144,88,2,23,254,236,109,51,2,231,60,142,149,36,247,23,170,230,250,169,130,46,160,4,225,98,159,77,3,6,157,147,237,70,27,160,151,11,174,160,226,179,163,21,139,141,72],
991
+ },
992
+ ],
993
+ };
994
+ let binary_str = "0100000000000000750d22eff727689d1d8d1c83e513a30bb68ee7f9125a4dafc882459e34c2069d00000000000000000100000000000000656432353531390000000000000000002000000000000000800ed6c2760e3e4ba1ff00128585c8cf8fed2e3dc1e3da1eb92d49f405bd63608c8111f5cbe69a5701000000000000000e000000000000002be69f532be55cd4697c87000000480a064b5fca13002a7fe575845154bbf0b3af4cc4f147cbed387d43cce3568a010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ea125bc7e4fe3892453df5c634ff6781000000000000000001000000000000000e000000000000005f545e24bc638098ba76be000000944524fff2c49c401e748db37cfda7569fa6df35b704fe716394f2ac3f40ce8701000000000000000e0000000000000079becb8684da8f5198dafd0000001df67838262d7109ffcd9018f183b1eb33f05659a274b89ea6b52ff3617d34a700000000000000000000000000000000000000000000000000000000000000007c6b04146e0506860100000000000000e4e26d93771d3bbb3d9dd306105d77cfb3a6254d1cc3495903af6e013442c63c00000000000000000100000000000000656432353531390000000000000000002000000000000000e6b9cde4eb058f8ecbb083d99779cb0f6d518d5386f019af6ead09fa52de85677219baa54926de02d9aec01095860b9300000000000000000000000000000000000000000000000000000000000000000000000000000000d58882248f845a3c7211557866ed75c201000000000000000e000000000000007818db5664f130fd08a70e000000543bc0eda69f728d0a0fbce08e5bfc5ed7b961300e0af226949e135f7d12e32f01000000000000000e000000000000002b677fe9d9b27794cd88b50000007b7f9aee981fe0d93bb3f49c6233cf847ebdd39d7dc5253f7fc330df2167073b00000000000000000000000000000000000000000000000000000000000000000100000000000000c0b9e98c9e03a2740c75d673871c1ee91f36d1bb329ff3ddbf1dfa8c6e1a64ebb78fa521dc62d9ced82bc3b61e0aa5a5c221d6cca5db63d94c9879543fb98c0a971094a89cd4408487ae32902248d321b545f9a051729aa0bb1725b848e3d4530100000000000000fe08c0a061475e7e5dec19e717cf98792fa7b555d0b5d3540a05db09f59ab8de0000000000000000000000000000000001000000000000000e000000000000000be35b0b076a36b5d2bfd300000001000000000000002000000000000000dac873207114258c2e347cfa73f80fcfe6931166514e90d399e997f726002a56010000000000000006d1fca03c5ddd9b09116db1b97c5451f7dc792b05362969f83e3e8dc1007f465cd81a99d4257e54d8dc548fc70bf41b0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000d9734428666b97d46131ad89986b2f726a90580217feec6d3302e73c8e9524f717aae6faa9822ea004e1629f4d03069d93ed461ba0970baea0e2b3a3158b8d48";
995
+ test_serialize_v1(&transaction, binary_str);
996
+
997
+ let json_str = "{\"siacoinInputs\":[{\"parentID\":\"750d22eff727689d1d8d1c83e513a30bb68ee7f9125a4dafc882459e34c2069d\",\"unlockConditions\":{\"timelock\":0,\"publicKeys\":[\"ed25519:800ed6c2760e3e4ba1ff00128585c8cf8fed2e3dc1e3da1eb92d49f405bd6360\"],\"signaturesRequired\":6312611591377486220}}],\"siacoinOutputs\":[{\"value\":\"890415399000000000000000000000000\",\"address\":\"480a064b5fca13002a7fe575845154bbf0b3af4cc4f147cbed387d43cce3568ae2497366eaa7\"}],\"fileContracts\":[{\"filesize\":0,\"fileMerkleRoot\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"windowStart\":10536451586783908586,\"windowEnd\":9324702155635244357,\"payout\":\"0\",\"validProofOutputs\":[{\"value\":\"1933513214000000000000000000000000\",\"address\":\"944524fff2c49c401e748db37cfda7569fa6df35b704fe716394f2ac3f40ce87b4506e9906f0\"}],\"missedProofOutputs\":[{\"value\":\"2469287901000000000000000000000000\",\"address\":\"1df67838262d7109ffcd9018f183b1eb33f05659a274b89ea6b52ff3617d34a770e9dd071d2e\"}],\"unlockHash\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\",\"revisionNumber\":9657412421282982780}],\"fileContractRevisions\":[{\"parentID\":\"e4e26d93771d3bbb3d9dd306105d77cfb3a6254d1cc3495903af6e013442c63c\",\"unlockConditions\":{\"timelock\":0,\"publicKeys\":[\"ed25519:e6b9cde4eb058f8ecbb083d99779cb0f6d518d5386f019af6ead09fa52de8567\"],\"signaturesRequired\":206644730660526450},\"revisionNumber\":10595710523108536025,\"filesize\":0,\"fileMerkleRoot\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"windowStart\":4348934140507359445,\"windowEnd\":14012366839994454386,\"validProofOutputs\":[{\"value\":\"2435858510000000000000000000000000\",\"address\":\"543bc0eda69f728d0a0fbce08e5bfc5ed7b961300e0af226949e135f7d12e32f0544e5262d6f\"}],\"missedProofOutputs\":[{\"value\":\"880343701000000000000000000000000\",\"address\":\"7b7f9aee981fe0d93bb3f49c6233cf847ebdd39d7dc5253f7fc330df2167073b35f035703237\"}],\"unlockHash\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"}],\"storageProofs\":[{\"parentID\":\"c0b9e98c9e03a2740c75d673871c1ee91f36d1bb329ff3ddbf1dfa8c6e1a64eb\",\"leaf\":\"b78fa521dc62d9ced82bc3b61e0aa5a5c221d6cca5db63d94c9879543fb98c0a971094a89cd4408487ae32902248d321b545f9a051729aa0bb1725b848e3d453\",\"proof\":[\"fe08c0a061475e7e5dec19e717cf98792fa7b555d0b5d3540a05db09f59ab8de\"]}],\"minerFees\":[\"241119475000000000000000000000000\"],\"arbitraryData\":[\"2shzIHEUJYwuNHz6c/gPz+aTEWZRTpDTmemX9yYAKlY=\"],\"signatures\":[{\"parentID\":\"06d1fca03c5ddd9b09116db1b97c5451f7dc792b05362969f83e3e8dc1007f46\",\"publicKeyIndex\":6088345341283457116,\"timelock\":2014247885072555224,\"coveredFields\":{\"wholeTransaction\":true},\"signature\":\"2XNEKGZrl9RhMa2JmGsvcmqQWAIX/uxtMwLnPI6VJPcXqub6qYIuoAThYp9NAwadk+1GG6CXC66g4rOjFYuNSA==\"}]}";
998
+ test_serialize_json(&transaction, json_str);
999
+ }
1000
+
1001
+ #[test]
1002
+ fn test_transaction_id() {
1003
+ let txn = Transaction::default();
1004
+ let id = txn.id();
1005
+ assert_eq!(
1006
+ hex::encode(id),
1007
+ "b3633a1370a72002ae2a956d21e8d481c3a69e146633470cf625ecd83fdeaa24"
1008
+ );
1009
+ }
1010
+
1011
+ #[test]
1012
+ fn test_transaction_whole_sig_hash() {
1013
+ let cs = ChainState {
1014
+ state: State {
1015
+ index: ChainIndex {
1016
+ height: 1,
1017
+ id: BlockID::default(),
1018
+ },
1019
+ prev_timestamps: [DateTime::UNIX_EPOCH; 11],
1020
+ depth: BlockID::default(),
1021
+ child_target: BlockID::default(),
1022
+ siafund_pool: Currency::new(0),
1023
+ oak_time: Duration::zero(),
1024
+ oak_target: BlockID::default(),
1025
+ foundation_primary_address: Address::new([0u8; 32]),
1026
+ foundation_failsafe_address: Address::new([0u8; 32]),
1027
+ total_work: Work::zero(),
1028
+ difficulty: Work::zero(),
1029
+ oak_work: Work::zero(),
1030
+ attestations: 0,
1031
+ elements: ElementAccumulator::default(),
1032
+ },
1033
+ network: Network {
1034
+ name: "test".to_string(),
1035
+ initial_coinbase: Currency::new(0),
1036
+ minimum_coinbase: Currency::new(0),
1037
+ initial_target: BlockID::default(),
1038
+ block_interval: Duration::seconds(1),
1039
+ maturity_delay: 0,
1040
+ hardfork_dev_addr: HardforkDevAddr {
1041
+ height: 0,
1042
+ old_address: Address::new([0u8; 32]),
1043
+ new_address: Address::new([0u8; 32]),
1044
+ },
1045
+ hardfork_tax: HardforkTax { height: 10 },
1046
+ hardfork_storage_proof: HardforkStorageProof { height: 0 },
1047
+ hardfork_asic: HardforkASIC {
1048
+ height: 0,
1049
+ oak_time: Duration::zero(),
1050
+ oak_target: BlockID::default(),
1051
+ },
1052
+ hardfork_oak: HardforkOak {
1053
+ height: 0,
1054
+ fix_height: 0,
1055
+ genesis_timestamp: DateTime::UNIX_EPOCH,
1056
+ },
1057
+ hardfork_foundation: HardforkFoundation {
1058
+ height: 0,
1059
+ primary_address: Address::new([0u8; 32]),
1060
+ failsafe_address: Address::new([0u8; 32]),
1061
+ },
1062
+ hardfork_v2: HardforkV2 {
1063
+ // test was made for pre-v2
1064
+ allow_height: 100,
1065
+ require_height: 100,
1066
+ },
1067
+ },
1068
+ };
1069
+ let pk = PrivateKey::from_seed(&[
1070
+ 136, 215, 58, 248, 45, 30, 78, 97, 128, 111, 82, 204, 43, 233, 223, 111, 110, 29, 73,
1071
+ 157, 52, 25, 242, 96, 131, 16, 187, 22, 232, 107, 17, 205,
1072
+ ]);
1073
+ struct TestCase {
1074
+ transaction: Transaction,
1075
+ parent_id: Hash256,
1076
+ timelock: u64,
1077
+ key_index: u64,
1078
+ expected: &'static str,
1079
+ }
1080
+ let test_cases = vec![
1081
+ TestCase{
1082
+ transaction: Transaction {
1083
+ siacoin_inputs: vec![
1084
+ SiacoinInput{
1085
+ parent_id: siacoin_id!("200bd724a6ae87005cd7b3124ae5349addc2d5d8db2fe1cdfb54f80245fc2575"),
1086
+ unlock_conditions: UnlockConditions::standard_unlock_conditions(pk.public_key()),
1087
+ }
1088
+ ],
1089
+ siacoin_outputs: vec![
1090
+ SiacoinOutput{
1091
+ value: Currency::new(67856467336433871),
1092
+ address: address!("000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69"),
1093
+ }
1094
+ ],
1095
+ file_contracts: Vec::new(),
1096
+ file_contract_revisions: Vec::new(),
1097
+ storage_proofs: Vec::new(),
1098
+ siafund_inputs: Vec::new(),
1099
+ siafund_outputs: Vec::new(),
1100
+ miner_fees: Vec::new(),
1101
+ arbitrary_data: Vec::new(),
1102
+ signatures: Vec::new(),
1103
+ },
1104
+ parent_id: hash_256!("200bd724a6ae87005cd7b3124ae5349addc2d5d8db2fe1cdfb54f80245fc2575"),
1105
+ timelock: 0,
1106
+ key_index: 0,
1107
+ expected: "a4b1855c546db7ec902237f730717faae96187db8ce9fe139504323a639f731e"
1108
+ },
1109
+ TestCase{
1110
+ transaction: serde_json::from_str("{\"siacoinInputs\":[{\"parentID\":\"750d22eff727689d1d8d1c83e513a30bb68ee7f9125a4dafc882459e34c2069d\",\"unlockConditions\":{\"timelock\":0,\"publicKeys\":[\"ed25519:800ed6c2760e3e4ba1ff00128585c8cf8fed2e3dc1e3da1eb92d49f405bd6360\"],\"signaturesRequired\":6312611591377486220}}],\"siacoinOutputs\":[{\"value\":\"890415399000000000000000000000000\",\"address\":\"480a064b5fca13002a7fe575845154bbf0b3af4cc4f147cbed387d43cce3568ae2497366eaa7\"}],\"fileContracts\":[{\"filesize\":0,\"fileMerkleRoot\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"windowStart\":10536451586783908586,\"windowEnd\":9324702155635244357,\"payout\":\"0\",\"validProofOutputs\":[{\"value\":\"1933513214000000000000000000000000\",\"address\":\"944524fff2c49c401e748db37cfda7569fa6df35b704fe716394f2ac3f40ce87b4506e9906f0\"}],\"missedProofOutputs\":[{\"value\":\"2469287901000000000000000000000000\",\"address\":\"1df67838262d7109ffcd9018f183b1eb33f05659a274b89ea6b52ff3617d34a770e9dd071d2e\"}],\"unlockHash\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\",\"revisionNumber\":9657412421282982780}],\"fileContractRevisions\":[{\"parentID\":\"e4e26d93771d3bbb3d9dd306105d77cfb3a6254d1cc3495903af6e013442c63c\",\"unlockConditions\":{\"timelock\":0,\"publicKeys\":[\"ed25519:e6b9cde4eb058f8ecbb083d99779cb0f6d518d5386f019af6ead09fa52de8567\"],\"signaturesRequired\":206644730660526450},\"revisionNumber\":10595710523108536025,\"filesize\":0,\"fileMerkleRoot\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"windowStart\":4348934140507359445,\"windowEnd\":14012366839994454386,\"validProofOutputs\":[{\"value\":\"2435858510000000000000000000000000\",\"address\":\"543bc0eda69f728d0a0fbce08e5bfc5ed7b961300e0af226949e135f7d12e32f0544e5262d6f\"}],\"missedProofOutputs\":[{\"value\":\"880343701000000000000000000000000\",\"address\":\"7b7f9aee981fe0d93bb3f49c6233cf847ebdd39d7dc5253f7fc330df2167073b35f035703237\"}],\"unlockHash\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"}],\"storageProofs\":[{\"parentID\":\"c0b9e98c9e03a2740c75d673871c1ee91f36d1bb329ff3ddbf1dfa8c6e1a64eb\",\"leaf\":\"b78fa521dc62d9ced82bc3b61e0aa5a5c221d6cca5db63d94c9879543fb98c0a971094a89cd4408487ae32902248d321b545f9a051729aa0bb1725b848e3d453\",\"proof\":[\"fe08c0a061475e7e5dec19e717cf98792fa7b555d0b5d3540a05db09f59ab8de\"]}],\"minerFees\":[\"241119475000000000000000000000000\"],\"arbitraryData\":[\"2shzIHEUJYwuNHz6c/gPz+aTEWZRTpDTmemX9yYAKlY=\"],\"signatures\":[{\"parentID\":\"06d1fca03c5ddd9b09116db1b97c5451f7dc792b05362969f83e3e8dc1007f46\",\"publicKeyIndex\":6088345341283457116,\"timelock\":2014247885072555224,\"coveredFields\":{\"wholeTransaction\":true},\"signature\":\"2XNEKGZrl9RhMa2JmGsvcmqQWAIX/uxtMwLnPI6VJPcXqub6qYIuoAThYp9NAwadk+1GG6CXC66g4rOjFYuNSA==\"}]}").expect("valid transaction"),
1111
+ parent_id: hash_256!("59c36e89224f681c1a843c3f30e12c77823d17581d4061724ec6d61555c49095"),
1112
+ timelock: 0,
1113
+ key_index: 0,
1114
+ expected: "f93b095e09f8c860ba8638737ba5f4c81a311af1a840c6b18635009e4d90574e",
1115
+ },
1116
+ TestCase{
1117
+ transaction: serde_json::from_str("{\"siacoinInputs\":[{\"parentID\":\"750d22eff727689d1d8d1c83e513a30bb68ee7f9125a4dafc882459e34c2069d\",\"unlockConditions\":{\"timelock\":0,\"publicKeys\":[\"ed25519:800ed6c2760e3e4ba1ff00128585c8cf8fed2e3dc1e3da1eb92d49f405bd6360\"],\"signaturesRequired\":6312611591377486220}}],\"siacoinOutputs\":[{\"value\":\"890415399000000000000000000000000\",\"address\":\"480a064b5fca13002a7fe575845154bbf0b3af4cc4f147cbed387d43cce3568ae2497366eaa7\"}],\"fileContracts\":[{\"filesize\":0,\"fileMerkleRoot\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"windowStart\":10536451586783908586,\"windowEnd\":9324702155635244357,\"payout\":\"0\",\"validProofOutputs\":[{\"value\":\"1933513214000000000000000000000000\",\"address\":\"944524fff2c49c401e748db37cfda7569fa6df35b704fe716394f2ac3f40ce87b4506e9906f0\"}],\"missedProofOutputs\":[{\"value\":\"2469287901000000000000000000000000\",\"address\":\"1df67838262d7109ffcd9018f183b1eb33f05659a274b89ea6b52ff3617d34a770e9dd071d2e\"}],\"unlockHash\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\",\"revisionNumber\":9657412421282982780}],\"fileContractRevisions\":[{\"parentID\":\"e4e26d93771d3bbb3d9dd306105d77cfb3a6254d1cc3495903af6e013442c63c\",\"unlockConditions\":{\"timelock\":0,\"publicKeys\":[\"ed25519:e6b9cde4eb058f8ecbb083d99779cb0f6d518d5386f019af6ead09fa52de8567\"],\"signaturesRequired\":206644730660526450},\"revisionNumber\":10595710523108536025,\"filesize\":0,\"fileMerkleRoot\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"windowStart\":4348934140507359445,\"windowEnd\":14012366839994454386,\"validProofOutputs\":[{\"value\":\"2435858510000000000000000000000000\",\"address\":\"543bc0eda69f728d0a0fbce08e5bfc5ed7b961300e0af226949e135f7d12e32f0544e5262d6f\"}],\"missedProofOutputs\":[{\"value\":\"880343701000000000000000000000000\",\"address\":\"7b7f9aee981fe0d93bb3f49c6233cf847ebdd39d7dc5253f7fc330df2167073b35f035703237\"}],\"unlockHash\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"}],\"storageProofs\":[{\"parentID\":\"c0b9e98c9e03a2740c75d673871c1ee91f36d1bb329ff3ddbf1dfa8c6e1a64eb\",\"leaf\":\"b78fa521dc62d9ced82bc3b61e0aa5a5c221d6cca5db63d94c9879543fb98c0a971094a89cd4408487ae32902248d321b545f9a051729aa0bb1725b848e3d453\",\"proof\":[\"fe08c0a061475e7e5dec19e717cf98792fa7b555d0b5d3540a05db09f59ab8de\"]}],\"minerFees\":[\"241119475000000000000000000000000\"],\"arbitraryData\":[\"2shzIHEUJYwuNHz6c/gPz+aTEWZRTpDTmemX9yYAKlY=\"],\"signatures\":[{\"parentID\":\"06d1fca03c5ddd9b09116db1b97c5451f7dc792b05362969f83e3e8dc1007f46\",\"publicKeyIndex\":6088345341283457116,\"timelock\":2014247885072555224,\"coveredFields\":{\"wholeTransaction\":true},\"signature\":\"2XNEKGZrl9RhMa2JmGsvcmqQWAIX/uxtMwLnPI6VJPcXqub6qYIuoAThYp9NAwadk+1GG6CXC66g4rOjFYuNSA==\"}]}").expect("valid transaction"),
1118
+ parent_id: hash_256!("59c36e89224f681c1a843c3f30e12c77823d17581d4061724ec6d61555c49095"),
1119
+ timelock: 467251739279473577,
1120
+ key_index: 1493934879227647507,
1121
+ expected: "7aea4ec52e1f7de90261c4448a928081d4be6015252e482ec9c1cfa01663d768",
1122
+ },
1123
+ ];
1124
+
1125
+ for (i, case) in test_cases.iter().enumerate() {
1126
+ let sig_hash = case
1127
+ .transaction
1128
+ .whole_sig_hash(&cs, &case.parent_id, case.key_index, case.timelock)
1129
+ .expect("encoded sig hash");
1130
+ assert_eq!(sig_hash.to_string(), case.expected, "case {i}")
1131
+ }
1132
+ }
1133
+
1134
+ #[test]
1135
+ fn test_transaction_partial_sig_hash() {
1136
+ let cs = ChainState {
1137
+ state: State {
1138
+ index: ChainIndex {
1139
+ height: 1,
1140
+ id: BlockID::default(),
1141
+ },
1142
+ prev_timestamps: [DateTime::UNIX_EPOCH; 11],
1143
+ depth: BlockID::default(),
1144
+ child_target: BlockID::default(),
1145
+ siafund_pool: Currency::new(0),
1146
+ oak_time: Duration::zero(),
1147
+ oak_target: BlockID::default(),
1148
+ foundation_primary_address: Address::new([0u8; 32]),
1149
+ foundation_failsafe_address: Address::new([0u8; 32]),
1150
+ total_work: Work::zero(),
1151
+ difficulty: Work::zero(),
1152
+ oak_work: Work::zero(),
1153
+ attestations: 0,
1154
+ elements: ElementAccumulator::default(),
1155
+ },
1156
+ network: Network {
1157
+ name: "test".to_string(),
1158
+ initial_coinbase: Currency::new(0),
1159
+ minimum_coinbase: Currency::new(0),
1160
+ initial_target: BlockID::default(),
1161
+ block_interval: Duration::seconds(1),
1162
+ maturity_delay: 0,
1163
+ hardfork_dev_addr: HardforkDevAddr {
1164
+ height: 0,
1165
+ old_address: Address::new([0u8; 32]),
1166
+ new_address: Address::new([0u8; 32]),
1167
+ },
1168
+ hardfork_tax: HardforkTax { height: 10 },
1169
+ hardfork_storage_proof: HardforkStorageProof { height: 0 },
1170
+ hardfork_asic: HardforkASIC {
1171
+ height: 0,
1172
+ oak_time: Duration::zero(),
1173
+ oak_target: BlockID::default(),
1174
+ },
1175
+ hardfork_oak: HardforkOak {
1176
+ height: 0,
1177
+ fix_height: 0,
1178
+ genesis_timestamp: DateTime::UNIX_EPOCH,
1179
+ },
1180
+ hardfork_foundation: HardforkFoundation {
1181
+ height: 0,
1182
+ primary_address: Address::new([0u8; 32]),
1183
+ failsafe_address: Address::new([0u8; 32]),
1184
+ },
1185
+ hardfork_v2: HardforkV2 {
1186
+ // test was made for pre-v2
1187
+ allow_height: 100,
1188
+ require_height: 100,
1189
+ },
1190
+ },
1191
+ };
1192
+ let txn: Transaction = serde_json::from_str("{\"siacoinInputs\":[{\"parentID\":\"750d22eff727689d1d8d1c83e513a30bb68ee7f9125a4dafc882459e34c2069d\",\"unlockConditions\":{\"timelock\":0,\"publicKeys\":[\"ed25519:800ed6c2760e3e4ba1ff00128585c8cf8fed2e3dc1e3da1eb92d49f405bd6360\"],\"signaturesRequired\":6312611591377486220}}],\"siacoinOutputs\":[{\"value\":\"890415399000000000000000000000000\",\"address\":\"480a064b5fca13002a7fe575845154bbf0b3af4cc4f147cbed387d43cce3568ae2497366eaa7\"}],\"fileContracts\":[{\"filesize\":0,\"fileMerkleRoot\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"windowStart\":10536451586783908586,\"windowEnd\":9324702155635244357,\"payout\":\"0\",\"validProofOutputs\":[{\"value\":\"1933513214000000000000000000000000\",\"address\":\"944524fff2c49c401e748db37cfda7569fa6df35b704fe716394f2ac3f40ce87b4506e9906f0\"}],\"missedProofOutputs\":[{\"value\":\"2469287901000000000000000000000000\",\"address\":\"1df67838262d7109ffcd9018f183b1eb33f05659a274b89ea6b52ff3617d34a770e9dd071d2e\"}],\"unlockHash\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\",\"revisionNumber\":9657412421282982780}],\"fileContractRevisions\":[{\"parentID\":\"e4e26d93771d3bbb3d9dd306105d77cfb3a6254d1cc3495903af6e013442c63c\",\"unlockConditions\":{\"timelock\":0,\"publicKeys\":[\"ed25519:e6b9cde4eb058f8ecbb083d99779cb0f6d518d5386f019af6ead09fa52de8567\"],\"signaturesRequired\":206644730660526450},\"revisionNumber\":10595710523108536025,\"filesize\":0,\"fileMerkleRoot\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"windowStart\":4348934140507359445,\"windowEnd\":14012366839994454386,\"validProofOutputs\":[{\"value\":\"2435858510000000000000000000000000\",\"address\":\"543bc0eda69f728d0a0fbce08e5bfc5ed7b961300e0af226949e135f7d12e32f0544e5262d6f\"}],\"missedProofOutputs\":[{\"value\":\"880343701000000000000000000000000\",\"address\":\"7b7f9aee981fe0d93bb3f49c6233cf847ebdd39d7dc5253f7fc330df2167073b35f035703237\"}],\"unlockHash\":\"000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69\"}],\"storageProofs\":[{\"parentID\":\"c0b9e98c9e03a2740c75d673871c1ee91f36d1bb329ff3ddbf1dfa8c6e1a64eb\",\"leaf\":\"b78fa521dc62d9ced82bc3b61e0aa5a5c221d6cca5db63d94c9879543fb98c0a971094a89cd4408487ae32902248d321b545f9a051729aa0bb1725b848e3d453\",\"proof\":[\"fe08c0a061475e7e5dec19e717cf98792fa7b555d0b5d3540a05db09f59ab8de\"]}],\"minerFees\":[\"241119475000000000000000000000000\"],\"arbitraryData\":[\"2shzIHEUJYwuNHz6c/gPz+aTEWZRTpDTmemX9yYAKlY=\"],\"signatures\":[{\"parentID\":\"06d1fca03c5ddd9b09116db1b97c5451f7dc792b05362969f83e3e8dc1007f46\",\"publicKeyIndex\":6088345341283457116,\"timelock\":2014247885072555224,\"coveredFields\":{\"wholeTransaction\":true},\"signature\":\"2XNEKGZrl9RhMa2JmGsvcmqQWAIX/uxtMwLnPI6VJPcXqub6qYIuoAThYp9NAwadk+1GG6CXC66g4rOjFYuNSA==\"}]}").expect("valid transaction");
1193
+
1194
+ let test_cases = vec![
1195
+ (
1196
+ CoveredFields {
1197
+ whole_transaction: false,
1198
+ siacoin_inputs: vec![0],
1199
+ siacoin_outputs: vec![0],
1200
+ file_contracts: vec![0],
1201
+ file_contract_revisions: vec![0],
1202
+ storage_proofs: vec![0],
1203
+ siafund_inputs: vec![],
1204
+ siafund_outputs: vec![],
1205
+ miner_fees: vec![0],
1206
+ arbitrary_data: vec![0],
1207
+ signatures: vec![0],
1208
+ },
1209
+ "5a9a69acb9211dc952b7458754e9fdf7f86fef419c03e8054f992d95c14ebf63",
1210
+ ),
1211
+ (
1212
+ CoveredFields {
1213
+ whole_transaction: false,
1214
+ siacoin_inputs: vec![],
1215
+ siacoin_outputs: vec![0],
1216
+ file_contracts: vec![0],
1217
+ file_contract_revisions: vec![0],
1218
+ storage_proofs: vec![0],
1219
+ siafund_inputs: vec![],
1220
+ siafund_outputs: vec![],
1221
+ miner_fees: vec![0],
1222
+ arbitrary_data: vec![0],
1223
+ signatures: vec![0],
1224
+ },
1225
+ "81c3e106089f1c86f3402c3bbc0d572d49eafe559961b18344102de4ee1abd94",
1226
+ ),
1227
+ (
1228
+ CoveredFields {
1229
+ whole_transaction: false,
1230
+ siacoin_inputs: vec![0],
1231
+ siacoin_outputs: vec![],
1232
+ file_contracts: vec![0],
1233
+ file_contract_revisions: vec![0],
1234
+ storage_proofs: vec![0],
1235
+ siafund_inputs: vec![],
1236
+ siafund_outputs: vec![],
1237
+ miner_fees: vec![0],
1238
+ arbitrary_data: vec![0],
1239
+ signatures: vec![0],
1240
+ },
1241
+ "78d8dca99a5a775e5223867af702bf6304326784c4e7f3fd431043504a7f7a98",
1242
+ ),
1243
+ (
1244
+ CoveredFields {
1245
+ whole_transaction: false,
1246
+ siacoin_inputs: vec![0],
1247
+ siacoin_outputs: vec![0],
1248
+ file_contracts: vec![0],
1249
+ file_contract_revisions: vec![0],
1250
+ storage_proofs: vec![0],
1251
+ siafund_inputs: vec![],
1252
+ siafund_outputs: vec![],
1253
+ miner_fees: vec![0],
1254
+ arbitrary_data: vec![],
1255
+ signatures: vec![0],
1256
+ },
1257
+ "79efc21fbdbf9536fe90b49b4858d5e2945a6bcb28f82b49b189d793f22f150b",
1258
+ ),
1259
+ (
1260
+ CoveredFields {
1261
+ whole_transaction: false,
1262
+ siacoin_inputs: vec![],
1263
+ siacoin_outputs: vec![],
1264
+ file_contracts: vec![],
1265
+ file_contract_revisions: vec![0],
1266
+ storage_proofs: vec![],
1267
+ siafund_inputs: vec![],
1268
+ siafund_outputs: vec![],
1269
+ miner_fees: vec![],
1270
+ arbitrary_data: vec![],
1271
+ signatures: vec![],
1272
+ },
1273
+ "22d26fe3e660d58a26a7da864357c1930c3c24393dd5712d851e0c1c0a0706c2",
1274
+ ),
1275
+ ];
1276
+
1277
+ for (i, (cf, expected)) in test_cases.iter().enumerate() {
1278
+ let sig_hash = txn.partial_sig_hash(&cs, cf).expect("valid sig hash");
1279
+ assert_eq!(sig_hash.to_string(), *expected, "case {i}")
1280
+ }
1281
+ }
1282
+
1283
+ #[test]
1284
+ fn test_standard_unlockhash() {
1285
+ let test_cases = vec![
1286
+ (
1287
+ "80f637df83a93a6916d1b5c8bdbb061f967fb9fe8fe51ef4d97eeec73c6bfc394771e4a04f42",
1288
+ hex::decode("ad08d551ab7116b8c2285de81ffa528ef3679f9e242c3f551b560a60ab9763db")
1289
+ .unwrap(),
1290
+ ),
1291
+ (
1292
+ "99a27a168bdde2e9c59bc967f6c662e3db0b2cf13da26ddae26004fa19c61d3db017dca7d0d3",
1293
+ hex::decode("18ac9c05b0c5e7c62859812b943572429cda178aa3df92697569b8984c603b4c")
1294
+ .unwrap(),
1295
+ ),
1296
+ (
1297
+ "128151658b256d0185f3f91504758349a96e73c1a68a39c7ff7bf9d0e416997c964d773858ce",
1298
+ hex::decode("2b36cc860796f2e8a1990b437f46a4b905840e6ba41ba5f68fe2b8ebe23626af")
1299
+ .unwrap(),
1300
+ ),
1301
+ (
1302
+ "1f47d453cfd7369bce4034d3ab461feb2a4d073bf59c959225993d00e38d71a8fea7c57cd3f1",
1303
+ hex::decode("a3e3c2f3493a079d3dfe69681bf878c59337e3d1c79d17a34e3da81f062bbe21")
1304
+ .unwrap(),
1305
+ ),
1306
+ (
1307
+ "e03c56f8d95894cea875711e2f909c68c07dd37142a8253813ad09abceb2b6e5dd89992c9638",
1308
+ hex::decode("a03d3b27db7e143cb8b39a1eb9234bffad59d6f50adf4f0ee916afd510a939a0")
1309
+ .unwrap(),
1310
+ ),
1311
+ (
1312
+ "68b6dd2e50f12e2deef2efd6b7baa660d87950ea16c5a8402a6db5873e062bcdd5246940b44e",
1313
+ hex::decode("52e4438ca9b6eb2d33953f97255e410130d55749432094fe9963f4fc65167ce5")
1314
+ .unwrap(),
1315
+ ),
1316
+ (
1317
+ "c766e0a5ef49b7bab6c2e9c6a0b699e87eb3580e08f3fe77648dd93b66795a8606787cc5e29e",
1318
+ hex::decode("4110f8b0ade1cca7aa40008a9b9911655393288eaacc3948fecd13edd3f092ec")
1319
+ .unwrap(),
1320
+ ),
1321
+ (
1322
+ "b455cf3c22de0d84ab8599499b0c2056d4916ab4c642b6b716148487f83ca5a85ad199b7a454",
1323
+ hex::decode("861d50c4ee90b0a6a5544a3820978dad1fd9391c4813ede9e4963f0d6bec010a")
1324
+ .unwrap(),
1325
+ ),
1326
+ (
1327
+ "5274e9f3db1acfe8bb2d67dbbb5b6f2cc20769a0b42c8a9187ae827bf637f06e62ecada30f9f",
1328
+ hex::decode("a5329c135951f3505d9a26d2833cb6c1aebb875fbada80f38b09bd3314f26802")
1329
+ .unwrap(),
1330
+ ),
1331
+ (
1332
+ "1540f42840b0479b238ec0143984a784c58240a8ca5e21da4b66be89a2f54353c99739938947",
1333
+ hex::decode("e11589e1857b7a0d2fc3526fbdfdc4d4708dfbf251184be1118138df4ef2d47e")
1334
+ .unwrap(),
1335
+ ),
1336
+ (
1337
+ "21592f041e6f6861f199d54a26fe6bdfc5d629bb5dda12058d3ce28549c4aeffdbbdb67c2b95",
1338
+ hex::decode("d57887af5b838ea2d20a582f659b3e36ca47d33b253364e3b774a1f4feb8415b")
1339
+ .unwrap(),
1340
+ ),
1341
+ (
1342
+ "f34b1e0b74a695f8bc82a97bab3b9d1ebe420956cbb3f7611c349c9659ba13fa362a417b1fd2",
1343
+ hex::decode("5da4058d2f95e3c547aab8b5c70817ed3795856aa7988676f266cb429d75ce06")
1344
+ .unwrap(),
1345
+ ),
1346
+ (
1347
+ "3549a1680fcc093347e2674f4e89c84200965e1b779e2b3c05e4b21645a0b2fd5ac86923ef7a",
1348
+ hex::decode("98ced26430f3be35b29ca76d3f65ea616f89e2510a3c1307856522e23057d958")
1349
+ .unwrap(),
1350
+ ),
1351
+ (
1352
+ "86fc291f7f53def33f2f7566f5ff08763bf5ada158f97c87fc240d1dcb04aa2a7b289018e33e",
1353
+ hex::decode("e715d5dc3bd8edecb453c59f85998591d7c14fd08057a0605cb416f6751eaad9")
1354
+ .unwrap(),
1355
+ ),
1356
+ (
1357
+ "46e60abc3acbff858e382783f0739a8b2f2ba4c51b26941d979e60cb5292f11df1112b7016c0",
1358
+ hex::decode("359eee8d1ef18ed647bbd63cb4b2be85061f8e3fd67318e13924ddbc1beb815f")
1359
+ .unwrap(),
1360
+ ),
1361
+ (
1362
+ "015b4b0759b0adee6c01de051bdacefe1f30eb571c83fa6c37607008696a9fa7f85273061e72",
1363
+ hex::decode("cf5cd07f31ca3aa3b7d2947da7e92c42ec5f981eff80ff1b438e59fd456465fb")
1364
+ .unwrap(),
1365
+ ),
1366
+ (
1367
+ "7435604655772ca5ff011127c83692e40945187954da3bc7c01102d59701c7351aadbdc9ac8b",
1368
+ hex::decode("7f6a73aeb6de28f1d3935941caa8cab286d13d8c74f2352b5b717c3d743db9c1")
1369
+ .unwrap(),
1370
+ ),
1371
+ (
1372
+ "c554d56a2eaffd8426006fb6d987cc615fb4ec05b1b15e793ab9d9127d79cf323787817467e6",
1373
+ hex::decode("14b98855c4f22295fcf3e2ec5d5fdfbb877979639c963bf6e226a0fb71902baf")
1374
+ .unwrap(),
1375
+ ),
1376
+ (
1377
+ "c4850dbcddb9dfac6f44007ec58fe824bc58e3de2432de478f3e53f7965c2afd7ea651b6c2bf",
1378
+ hex::decode("6f5c23f8797f93d3d3c689fe1a3f5d9a1fbf326a7a6ea51fecbeaa9aba46f180")
1379
+ .unwrap(),
1380
+ ),
1381
+ (
1382
+ "6a8f4f1d5a7405aa24cb1fb2a3c1dcaae74175c712002627289b5cd9dd887088afe605460abd",
1383
+ hex::decode("45f12760f6005a93cece248f5ec78adf15f9d29dafe397c8c28fefc72781d6fb")
1384
+ .unwrap(),
1385
+ ),
1386
+ (
1387
+ "e464b9b1c9282d8edeed5832b95405761db6dacf6a156fc9119a396bdc8f8892815c7dce20fd",
1388
+ hex::decode("1c12d17a2a8b2c25950872f312d5d0758f07d8357c98897fc472565a44b3d1f1")
1389
+ .unwrap(),
1390
+ ),
1391
+ (
1392
+ "9ae839af434aa13de6e8baa280541716811dcbaa33165fea5e9bad0c33998c10f16fcac4f214",
1393
+ hex::decode("686d28bf7e4b4cadf759994caed1e52092e12c11cef257a265b50402dbd70c3b")
1394
+ .unwrap(),
1395
+ ),
1396
+ (
1397
+ "e92722d80103af9574f19a6cf72aab424335927eb7da022455f53314e3587dc8ece40d254981",
1398
+ hex::decode("b2e9ddef40897219a997ae7af277a5550cc10c54e793b6d2146de94df3bd552b")
1399
+ .unwrap(),
1400
+ ),
1401
+ (
1402
+ "e2a02510f242f35e46b8840d8da42c087ea906b09d8e454c734663650236977da0362dd2ab43",
1403
+ hex::decode("4f756e475a706cdcec8eb1c02b21a591e0c0450cc0408ae8aec82ae97f634ecf")
1404
+ .unwrap(),
1405
+ ),
1406
+ (
1407
+ "8fb49ccf17dfdcc9526dec6ee8a5cca20ff8247302053d3777410b9b0494ba8cdf32abee86f0",
1408
+ hex::decode("cd46b523d2ee92f205a00726d8544094bb4fe58142ecffd20ea32b37b6e6bfc3")
1409
+ .unwrap(),
1410
+ ),
1411
+ ];
1412
+
1413
+ for (expected_str, public_key) in test_cases {
1414
+ let expected = expected_str.parse().unwrap();
1415
+
1416
+ let public_key = PublicKey::new(public_key.as_slice().try_into().unwrap());
1417
+ let addr = UnlockConditions::standard_unlock_conditions(public_key).address();
1418
+
1419
+ assert_eq!(addr, expected);
1420
+ // test string round-trip
1421
+ if !expected_str.starts_with("") {
1422
+ assert_eq!(addr.to_string(), "".to_string() + expected_str)
1423
+ } else {
1424
+ assert_eq!(addr.to_string(), expected_str)
1425
+ }
1426
+ }
1427
+ }
1428
+
1429
+ #[test]
1430
+ fn test_seed_standard_unlock_hash() {
1431
+ const PHRASE: &str =
1432
+ "song renew capable taxi follow sword more hybrid laptop dance unfair poem";
1433
+ let test_addresses = vec![
1434
+ (
1435
+ 0,
1436
+ address!(
1437
+ "16e09f8dc8a100a03ba1f9503e4035661738d1bea0b6cdc9bb012d3cd25edaacfd780909e550"
1438
+ ),
1439
+ ),
1440
+ (
1441
+ 1,
1442
+ address!(
1443
+ "cb016a7018485325fa299bc247113e3792dbea27ee08d2bb57a16cb0804fa449d3a91ee647a1"
1444
+ ),
1445
+ ),
1446
+ (
1447
+ 2,
1448
+ address!(
1449
+ "5eb70f141387df1e2ecd434b22be50bff57a6e08484f3890fe4415a6d323b5e9e758b4f79b34"
1450
+ ),
1451
+ ),
1452
+ (
1453
+ 3,
1454
+ address!(
1455
+ "c3bc7bc1431460ed2556874cb63714760120125da758ebbd78198534cb3d25774352fdbb3e8b"
1456
+ ),
1457
+ ),
1458
+ (
1459
+ 4,
1460
+ address!(
1461
+ "ebc7eae02ecf76e3ba7312bab6b6f71e9d255801a3a3b83f7cc26bd520b2c27a511cd8604e4b"
1462
+ ),
1463
+ ),
1464
+ (
1465
+ 5,
1466
+ address!(
1467
+ "fce241a44b944b10f414782dd35f5d96b92aec3d6da92a45ae44b7dc8cfb4b4ba97a34ce7032"
1468
+ ),
1469
+ ),
1470
+ (
1471
+ 6,
1472
+ address!(
1473
+ "36d253e7c3af2213eccaf0a61c6d24be8668f72af6e773463f3c41efc8bb70f2b353b90de9dd"
1474
+ ),
1475
+ ),
1476
+ (
1477
+ 7,
1478
+ address!(
1479
+ "c8f85375fb264428c86594863440f856db1da4614d75f4a30e3d9db3dfc88af6995128c6a845"
1480
+ ),
1481
+ ),
1482
+ (
1483
+ 8,
1484
+ address!(
1485
+ "85ef2ba14ee464060570b16bddaac91353961e7545067ccdf868a0ece305f00d2c08ec6844c6"
1486
+ ),
1487
+ ),
1488
+ (
1489
+ 9,
1490
+ address!(
1491
+ "9dcf644245eba91e7ea70c47ccadf479e6834c1c1221335e7246e0a6bc40e18362c4faa760b8"
1492
+ ),
1493
+ ),
1494
+ (
1495
+ 4294967295,
1496
+ address!(
1497
+ "a906891f0c524fd272a905aa5dd7018c69e5d68222385cbd9d5292f38f021ce4bf00953a0659"
1498
+ ),
1499
+ ),
1500
+ (
1501
+ 4294967296,
1502
+ address!(
1503
+ "b6ab338e624a304add7afe205361ac71821b87559a3b9c5b3735eaafa914eed533613a0af7fa"
1504
+ ),
1505
+ ),
1506
+ (
1507
+ 18446744073709551615,
1508
+ address!(
1509
+ "832d0e8b5f967677d812d75559c373d930ad16eb90c31c29982a190bb7db9edf9438fd827938"
1510
+ ),
1511
+ ),
1512
+ ];
1513
+
1514
+ let seed = Seed::new(PHRASE).unwrap();
1515
+ for (i, expected) in test_addresses {
1516
+ let pk = seed.private_key(i).public_key();
1517
+ let addr: Address = UnlockConditions::standard_unlock_conditions(pk).address();
1518
+
1519
+ assert_eq!(addr, expected, "index {i}");
1520
+ }
1521
+ }
1522
+
1523
+ #[test]
1524
+ fn test_file_contract_tax() {
1525
+ struct TestCase {
1526
+ payout: Currency,
1527
+ prefork: Currency,
1528
+ postfork: Currency,
1529
+ }
1530
+ let test_cases = vec![
1531
+ TestCase {
1532
+ payout: Currency::new(0),
1533
+ prefork: Currency::new(0),
1534
+ postfork: Currency::new(0),
1535
+ },
1536
+ TestCase {
1537
+ payout: Currency::new(1),
1538
+ prefork: Currency::new(0),
1539
+ postfork: Currency::new(0),
1540
+ },
1541
+ TestCase {
1542
+ payout: Currency::new(2),
1543
+ prefork: Currency::new(0),
1544
+ postfork: Currency::new(0),
1545
+ },
1546
+ TestCase {
1547
+ payout: Currency::new(340282366920938463463374607431768211455),
1548
+ prefork: Currency::new(13271012309916600075071609689838960000),
1549
+ postfork: Currency::new(13271012309916600075071609689838960000),
1550
+ },
1551
+ TestCase {
1552
+ payout: Currency::new(595540520000000000000000000000000),
1553
+ prefork: Currency::new(23226080280000000000000000000000),
1554
+ postfork: Currency::new(23226080280000000000000000000000),
1555
+ },
1556
+ TestCase {
1557
+ payout: Currency::new(103983762872653413852682180916164570605),
1558
+ prefork: Currency::new(4055366752033483140254605055730410000),
1559
+ postfork: Currency::new(4055366752033483140254605055730410000),
1560
+ },
1561
+ TestCase {
1562
+ payout: Currency::new(3827764528000000000000000000000000),
1563
+ prefork: Currency::new(149282816592000000000000000000000),
1564
+ postfork: Currency::new(149282816592000000000000000000000),
1565
+ },
1566
+ TestCase {
1567
+ payout: Currency::new(216196544861605368913933682930139323360),
1568
+ prefork: Currency::new(8431665249602609387643413634275430000),
1569
+ postfork: Currency::new(8431665249602609387643413634275430000),
1570
+ },
1571
+ TestCase {
1572
+ payout: Currency::new(2630651610000000000000000000000000),
1573
+ prefork: Currency::new(102595412790000000000000000000000),
1574
+ postfork: Currency::new(102595412790000000000000000000000),
1575
+ },
1576
+ TestCase {
1577
+ payout: Currency::new(242035606817835258338180817515298863866),
1578
+ prefork: Currency::new(9439388665895575075189051883096650000),
1579
+ postfork: Currency::new(9439388665895575075189051883096650000),
1580
+ },
1581
+ TestCase {
1582
+ payout: Currency::new(3830155529000000000000000000000000),
1583
+ prefork: Currency::new(149376065631000000000000000000000),
1584
+ postfork: Currency::new(149376065631000000000000000000000),
1585
+ },
1586
+ TestCase {
1587
+ payout: Currency::new(126852058180828682198189558142776852406),
1588
+ prefork: Currency::new(4947230269052318605729392767568290000),
1589
+ postfork: Currency::new(4947230269052318605729392767568290000),
1590
+ },
1591
+ TestCase {
1592
+ payout: Currency::new(3497644816000000000000000000000000),
1593
+ prefork: Currency::new(136408147824000000000000000000000),
1594
+ postfork: Currency::new(136408147824000000000000000000000),
1595
+ },
1596
+ TestCase {
1597
+ payout: Currency::new(202288428857506739014982837533616200189),
1598
+ prefork: Currency::new(7889248725442762821584330663811030000),
1599
+ postfork: Currency::new(7889248725442762821584330663811030000),
1600
+ },
1601
+ TestCase {
1602
+ payout: Currency::new(3717108920000000000000000000000000),
1603
+ prefork: Currency::new(144967247880000000000000000000000),
1604
+ postfork: Currency::new(144967247880000000000000000000000),
1605
+ },
1606
+ TestCase {
1607
+ payout: Currency::new(319915558118289110319073160920214552278),
1608
+ prefork: Currency::new(12476706766613275302443853275888360000),
1609
+ postfork: Currency::new(12476706766613275302443853275888360000),
1610
+ },
1611
+ TestCase {
1612
+ payout: Currency::new(2205536583000000000000000000000000),
1613
+ prefork: Currency::new(86015926737000000000000000000000),
1614
+ postfork: Currency::new(86015926737000000000000000000000),
1615
+ },
1616
+ TestCase {
1617
+ payout: Currency::new(138040906509763116024244911687441931808),
1618
+ prefork: Currency::new(5383595353880761524945551555810230000),
1619
+ postfork: Currency::new(5383595353880761524945551555810230000),
1620
+ },
1621
+ TestCase {
1622
+ payout: Currency::new(1110927021000000000000000000000000),
1623
+ prefork: Currency::new(43326153819000000000000000000000),
1624
+ postfork: Currency::new(43326153819000000000000000000000),
1625
+ },
1626
+ TestCase {
1627
+ payout: Currency::new(215175656237682186102553629801543673418),
1628
+ prefork: Currency::new(8391850593269605257999591562260200000),
1629
+ postfork: Currency::new(8391850593269605257999591562260200000),
1630
+ },
1631
+ TestCase {
1632
+ payout: Currency::new(646634091000000000000000000000000),
1633
+ prefork: Currency::new(25218729549000000000000000000000),
1634
+ postfork: Currency::new(25218729549000000000000000000000),
1635
+ },
1636
+ TestCase {
1637
+ payout: Currency::new(128469923315667767051668090462322677166),
1638
+ prefork: Currency::new(5010327009311042915015055528030580000),
1639
+ postfork: Currency::new(5010327009311042915015055528030580000),
1640
+ },
1641
+ TestCase {
1642
+ payout: Currency::new(2889549915000000000000000000000000),
1643
+ prefork: Currency::new(112692446685000000000000000000000),
1644
+ postfork: Currency::new(112692446685000000000000000000000),
1645
+ },
1646
+ TestCase {
1647
+ payout: Currency::new(269364173205837265395254441592734706401),
1648
+ prefork: Currency::new(10505202755027653350414923222116650000),
1649
+ postfork: Currency::new(10505202755027653350414923222116650000),
1650
+ },
1651
+ ];
1652
+
1653
+ let mut cs = ChainState {
1654
+ state: State {
1655
+ index: ChainIndex {
1656
+ height: 1,
1657
+ id: BlockID::default(),
1658
+ },
1659
+ prev_timestamps: [DateTime::UNIX_EPOCH; 11],
1660
+ depth: BlockID::default(),
1661
+ child_target: BlockID::default(),
1662
+ siafund_pool: Currency::new(0),
1663
+ oak_time: Duration::zero(),
1664
+ oak_target: BlockID::default(),
1665
+ foundation_primary_address: Address::new([0u8; 32]),
1666
+ foundation_failsafe_address: Address::new([0u8; 32]),
1667
+ total_work: Work::zero(),
1668
+ difficulty: Work::zero(),
1669
+ oak_work: Work::zero(),
1670
+ attestations: 0,
1671
+ elements: ElementAccumulator::default(),
1672
+ },
1673
+ network: Network {
1674
+ name: "test".to_string(),
1675
+ initial_coinbase: Currency::new(0),
1676
+ minimum_coinbase: Currency::new(0),
1677
+ initial_target: BlockID::default(),
1678
+ block_interval: Duration::seconds(1),
1679
+ maturity_delay: 0,
1680
+ hardfork_dev_addr: HardforkDevAddr {
1681
+ height: 0,
1682
+ old_address: Address::new([0u8; 32]),
1683
+ new_address: Address::new([0u8; 32]),
1684
+ },
1685
+ hardfork_tax: HardforkTax { height: 10 },
1686
+ hardfork_storage_proof: HardforkStorageProof { height: 0 },
1687
+ hardfork_asic: HardforkASIC {
1688
+ height: 0,
1689
+ oak_time: Duration::zero(),
1690
+ oak_target: BlockID::default(),
1691
+ },
1692
+ hardfork_oak: HardforkOak {
1693
+ height: 0,
1694
+ fix_height: 0,
1695
+ genesis_timestamp: DateTime::UNIX_EPOCH,
1696
+ },
1697
+ hardfork_foundation: HardforkFoundation {
1698
+ height: 0,
1699
+ primary_address: Address::new([0u8; 32]),
1700
+ failsafe_address: Address::new([0u8; 32]),
1701
+ },
1702
+ hardfork_v2: HardforkV2 {
1703
+ allow_height: 0,
1704
+ require_height: 0,
1705
+ },
1706
+ },
1707
+ };
1708
+
1709
+ for tc in test_cases.iter() {
1710
+ let fc = FileContract {
1711
+ payout: tc.payout,
1712
+ file_size: 0,
1713
+ file_merkle_root: Hash256::default(),
1714
+ window_start: 0,
1715
+ window_end: 0,
1716
+ unlock_hash: Address::new([0u8; 32]),
1717
+ revision_number: 0,
1718
+ valid_proof_outputs: vec![],
1719
+ missed_proof_outputs: vec![],
1720
+ };
1721
+
1722
+ let tax = fc.tax(&cs);
1723
+ assert_eq!(
1724
+ tax, tc.prefork,
1725
+ "prefork tax incorrect for payout {:?}",
1726
+ tc.payout
1727
+ );
1728
+ cs.state.index.height = 11;
1729
+ let tax = fc.tax(&cs);
1730
+ assert_eq!(
1731
+ tax, tc.postfork,
1732
+ "postfork tax incorrect for payout {:?}",
1733
+ tc.payout
1734
+ );
1735
+ }
1736
+ }
1737
+ }