1#![allow(missing_docs)]
6
7use serde::{Serialize, Deserialize};
8use crate::errors::{Error, ValidationError};
9
10fn validate_accumulate_url(url: &str, field_name: &str) -> Result<(), Error> {
17 if url.is_empty() {
18 return Err(ValidationError::RequiredFieldMissing(field_name.to_string()).into());
19 }
20
21 if !url.starts_with("acc://") {
22 return Err(ValidationError::InvalidUrl(
23 format!("{}: must start with 'acc://', got '{}'", field_name, url)
24 ).into());
25 }
26
27 if !url.is_ascii() {
28 return Err(ValidationError::InvalidUrl(
29 format!("{}: must contain only ASCII characters", field_name)
30 ).into());
31 }
32
33 if url.chars().any(|c| c.is_whitespace() || c.is_control()) {
35 return Err(ValidationError::InvalidUrl(
36 format!("{}: must not contain whitespace or control characters", field_name)
37 ).into());
38 }
39
40 if url.len() <= 6 {
42 return Err(ValidationError::InvalidUrl(
43 format!("{}: URL path cannot be empty", field_name)
44 ).into());
45 }
46
47 Ok(())
48}
49
50fn validate_amount_string(amount: &str, field_name: &str) -> Result<(), Error> {
52 if amount.is_empty() {
53 return Err(ValidationError::RequiredFieldMissing(field_name.to_string()).into());
54 }
55
56 if !amount.chars().all(|c| c.is_ascii_digit()) {
58 return Err(ValidationError::InvalidAmount(
59 format!("{}: must be a valid non-negative integer, got '{}'", field_name, amount)
60 ).into());
61 }
62
63 if amount != "0" && amount.chars().all(|c| c == '0') {
65 return Err(ValidationError::InvalidAmount(
66 format!("{}: invalid zero representation", field_name)
67 ).into());
68 }
69
70 Ok(())
71}
72
73fn validate_authorities(authorities: &Option<Vec<String>>) -> Result<(), Error> {
75 if let Some(ref auths) = authorities {
76 for (i, auth) in auths.iter().enumerate() {
77 validate_accumulate_url(auth, &format!("authorities[{}]", i))?;
78 }
79 }
80 Ok(())
81}
82
83#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
84#[serde(rename_all = "camelCase")]
85pub struct AcmeFaucetBody {
86 #[serde(rename = "Url")]
87 pub url: String,
88}
89
90impl AcmeFaucetBody {
91 pub fn validate(&self) -> Result<(), Error> {
92 validate_accumulate_url(&self.url, "url")?;
94 Ok(())
95 }
96}
97
98#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
99#[serde(rename_all = "camelCase")]
100pub struct ActivateProtocolVersionBody {
101 #[serde(rename = "Version")]
102 #[serde(skip_serializing_if = "Option::is_none")]
103 pub version: Option<String>,
104}
105
106impl ActivateProtocolVersionBody {
107 pub fn validate(&self) -> Result<(), Error> {
108 if let Some(ref version) = self.version {
110 if version.is_empty() {
111 return Err(ValidationError::InvalidFieldValue {
112 field: "version".to_string(),
113 reason: "version string cannot be empty if provided".to_string(),
114 }.into());
115 }
116 }
117 Ok(())
118 }
119}
120
121#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
122#[serde(rename_all = "camelCase")]
123pub struct AddCreditsBody {
124 #[serde(rename = "Recipient")]
125 pub recipient: String,
126 #[serde(rename = "Amount")]
127 pub amount: String,
128 #[serde(rename = "Oracle")]
129 pub oracle: u64,
130}
131
132impl AddCreditsBody {
133 pub fn validate(&self) -> Result<(), Error> {
134 validate_accumulate_url(&self.recipient, "recipient")?;
136
137 validate_amount_string(&self.amount, "amount")?;
139
140 if self.oracle == 0 {
142 return Err(ValidationError::InvalidFieldValue {
143 field: "oracle".to_string(),
144 reason: "oracle price must be positive".to_string(),
145 }.into());
146 }
147
148 Ok(())
149 }
150}
151
152#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
153#[serde(rename_all = "camelCase")]
154pub struct BlockValidatorAnchorBody {
155 #[serde(rename = "AcmeBurnt")]
156 pub acme_burnt: String,
157}
158
159impl BlockValidatorAnchorBody {
160 pub fn validate(&self) -> Result<(), Error> {
161 validate_amount_string(&self.acme_burnt, "acmeBurnt")?;
163 Ok(())
164 }
165}
166
167#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
168#[serde(rename_all = "camelCase")]
169pub struct BurnCreditsBody {
170 #[serde(rename = "Amount")]
171 pub amount: u64,
172}
173
174impl BurnCreditsBody {
175 pub fn validate(&self) -> Result<(), Error> {
176 if self.amount == 0 {
178 return Err(ValidationError::InvalidAmount(
179 "amount: must be greater than zero to burn credits".to_string()
180 ).into());
181 }
182 Ok(())
183 }
184}
185
186#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
187#[serde(rename_all = "camelCase")]
188pub struct BurnTokensBody {
189 #[serde(rename = "Amount")]
190 pub amount: String,
191}
192
193impl BurnTokensBody {
194 pub fn validate(&self) -> Result<(), Error> {
195 validate_amount_string(&self.amount, "amount")?;
197
198 if self.amount == "0" {
200 return Err(ValidationError::InvalidAmount(
201 "amount: must be greater than zero to burn tokens".to_string()
202 ).into());
203 }
204 Ok(())
205 }
206}
207
208#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
209#[serde(rename_all = "camelCase")]
210pub struct CreateDataAccountBody {
211 #[serde(rename = "Url")]
212 pub url: String,
213 #[serde(rename = "Authorities")]
214 #[serde(skip_serializing_if = "Option::is_none")]
215 pub authorities: Option<Vec<String>>,
216}
217
218impl CreateDataAccountBody {
219 pub fn validate(&self) -> Result<(), Error> {
220 validate_accumulate_url(&self.url, "url")?;
222
223 validate_authorities(&self.authorities)?;
225
226 Ok(())
227 }
228}
229
230#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
231#[serde(rename_all = "camelCase")]
232pub struct CreateIdentityBody {
233 #[serde(rename = "Url")]
234 pub url: String,
235 #[serde(rename = "KeyHash")]
236 #[serde(skip_serializing_if = "Option::is_none")]
237 pub key_hash: Option<Vec<u8>>,
238 #[serde(rename = "KeyBookUrl")]
239 #[serde(skip_serializing_if = "Option::is_none")]
240 pub key_book_url: Option<String>,
241 #[serde(rename = "Authorities")]
242 #[serde(skip_serializing_if = "Option::is_none")]
243 pub authorities: Option<Vec<String>>,
244}
245
246impl CreateIdentityBody {
247 pub fn validate(&self) -> Result<(), Error> {
248 validate_accumulate_url(&self.url, "url")?;
250
251 if let Some(ref key_book_url) = self.key_book_url {
253 validate_accumulate_url(key_book_url, "keyBookUrl")?;
254 }
255
256 if let Some(ref key_hash) = self.key_hash {
258 if key_hash.len() != 32 {
259 return Err(ValidationError::InvalidHash {
260 expected: 32,
261 actual: key_hash.len(),
262 }.into());
263 }
264 }
265
266 validate_authorities(&self.authorities)?;
268
269 Ok(())
270 }
271}
272
273#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
274#[serde(rename_all = "camelCase")]
275pub struct CreateKeyBookBody {
276 #[serde(rename = "Url")]
277 pub url: String,
278 #[serde(rename = "PublicKeyHash")]
279 #[serde(with = "hex::serde")]
280 pub public_key_hash: Vec<u8>,
281 #[serde(rename = "Authorities")]
282 #[serde(skip_serializing_if = "Option::is_none")]
283 pub authorities: Option<Vec<String>>,
284}
285
286impl CreateKeyBookBody {
287 pub fn validate(&self) -> Result<(), Error> {
288 validate_accumulate_url(&self.url, "url")?;
290
291 if self.public_key_hash.len() != 32 {
293 return Err(ValidationError::InvalidHash {
294 expected: 32,
295 actual: self.public_key_hash.len(),
296 }.into());
297 }
298
299 validate_authorities(&self.authorities)?;
301
302 Ok(())
303 }
304}
305
306#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
307#[serde(rename_all = "camelCase")]
308pub struct CreateKeyPageBody {
309 #[serde(rename = "Keys")]
310 pub keys: Vec<serde_json::Value>,
311}
312
313impl CreateKeyPageBody {
314 pub fn validate(&self) -> Result<(), Error> {
315 if self.keys.is_empty() {
317 return Err(ValidationError::EmptyCollection(
318 "keys: at least one key is required".to_string()
319 ).into());
320 }
321
322 for (i, key) in self.keys.iter().enumerate() {
324 if !key.is_object() {
325 return Err(ValidationError::InvalidFieldValue {
326 field: format!("keys[{}]", i),
327 reason: "each key must be a valid key specification object".to_string(),
328 }.into());
329 }
330 }
331
332 Ok(())
333 }
334}
335
336#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
337#[serde(rename_all = "camelCase")]
338pub struct CreateLiteTokenAccountBody {
339 }
341
342impl CreateLiteTokenAccountBody {
343 pub fn validate(&self) -> Result<(), Error> {
344 Ok(())
346 }
347}
348
349#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
350#[serde(rename_all = "camelCase")]
351pub struct CreateTokenBody {
352 #[serde(rename = "Url")]
353 pub url: String,
354 #[serde(rename = "Symbol")]
355 pub symbol: String,
356 #[serde(rename = "Precision")]
357 pub precision: u64,
358 #[serde(rename = "Properties")]
359 #[serde(skip_serializing_if = "Option::is_none")]
360 pub properties: Option<String>,
361 #[serde(rename = "SupplyLimit")]
362 #[serde(skip_serializing_if = "Option::is_none")]
363 pub supply_limit: Option<String>,
364 #[serde(rename = "Authorities")]
365 #[serde(skip_serializing_if = "Option::is_none")]
366 pub authorities: Option<Vec<String>>,
367}
368
369impl CreateTokenBody {
370 pub fn validate(&self) -> Result<(), Error> {
371 validate_accumulate_url(&self.url, "url")?;
373
374 if self.symbol.is_empty() {
376 return Err(ValidationError::InvalidTokenSymbol(
377 "symbol cannot be empty".to_string()
378 ).into());
379 }
380
381 if !self.symbol.chars().all(|c| c.is_ascii_alphanumeric()) {
382 return Err(ValidationError::InvalidTokenSymbol(
383 format!("symbol must be alphanumeric, got '{}'", self.symbol)
384 ).into());
385 }
386
387 if self.symbol.len() > 10 {
388 return Err(ValidationError::InvalidTokenSymbol(
389 format!("symbol too long (max 10 chars), got {} chars", self.symbol.len())
390 ).into());
391 }
392
393 if self.precision > 18 {
395 return Err(ValidationError::InvalidPrecision(self.precision).into());
396 }
397
398 if let Some(ref supply_limit) = self.supply_limit {
400 validate_amount_string(supply_limit, "supplyLimit")?;
401 }
402
403 validate_authorities(&self.authorities)?;
405
406 Ok(())
407 }
408}
409
410#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
411#[serde(rename_all = "camelCase")]
412pub struct CreateTokenAccountBody {
413 #[serde(rename = "Url")]
414 pub url: String,
415 #[serde(rename = "TokenUrl")]
416 pub token_url: String,
417 #[serde(rename = "Authorities")]
418 #[serde(skip_serializing_if = "Option::is_none")]
419 pub authorities: Option<Vec<String>>,
420 #[serde(rename = "Proof")]
421 #[serde(skip_serializing_if = "Option::is_none")]
422 pub proof: Option<serde_json::Value>,
423}
424
425impl CreateTokenAccountBody {
426 pub fn validate(&self) -> Result<(), Error> {
427 validate_accumulate_url(&self.url, "url")?;
429
430 validate_accumulate_url(&self.token_url, "tokenUrl")?;
432
433 validate_authorities(&self.authorities)?;
435
436 Ok(())
437 }
438}
439
440#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
441#[serde(rename_all = "camelCase")]
442pub struct DirectoryAnchorBody {
443 #[serde(rename = "Updates")]
444 pub updates: Vec<serde_json::Value>,
445 #[serde(rename = "Receipts")]
446 pub receipts: Vec<serde_json::Value>,
447 #[serde(rename = "MakeMajorBlock")]
448 pub make_major_block: u64,
449 #[serde(rename = "MakeMajorBlockTime")]
450 pub make_major_block_time: u64,
451}
452
453impl DirectoryAnchorBody {
454 pub fn validate(&self) -> Result<(), Error> {
455 Ok(())
459 }
460}
461
462#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
463#[serde(rename_all = "camelCase")]
464pub struct IssueTokensBody {
465 #[serde(rename = "Recipient")]
466 pub recipient: String,
467 #[serde(rename = "Amount")]
468 pub amount: String,
469 #[serde(rename = "To")]
470 pub to: Vec<serde_json::Value>,
471}
472
473impl IssueTokensBody {
474 pub fn validate(&self) -> Result<(), Error> {
475 validate_accumulate_url(&self.recipient, "recipient")?;
477
478 validate_amount_string(&self.amount, "amount")?;
480
481 if self.amount == "0" {
483 return Err(ValidationError::InvalidAmount(
484 "amount: must be greater than zero to issue tokens".to_string()
485 ).into());
486 }
487
488 Ok(())
489 }
490}
491
492#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
493#[serde(rename_all = "camelCase")]
494pub struct LockAccountBody {
495 #[serde(rename = "Height")]
496 pub height: u64,
497}
498
499impl LockAccountBody {
500 pub fn validate(&self) -> Result<(), Error> {
501 if self.height == 0 {
503 return Err(ValidationError::InvalidFieldValue {
504 field: "height".to_string(),
505 reason: "lock height must be greater than zero".to_string(),
506 }.into());
507 }
508 Ok(())
509 }
510}
511
512#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
513#[serde(rename_all = "camelCase")]
514pub struct NetworkMaintenanceBody {
515 #[serde(rename = "Operations")]
516 pub operations: Vec<serde_json::Value>,
517}
518
519impl NetworkMaintenanceBody {
520 pub fn validate(&self) -> Result<(), Error> {
521 Ok(())
524 }
525}
526
527#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
528#[serde(rename_all = "camelCase")]
529pub struct RemoteTransactionBody {
530 #[serde(rename = "Hash")]
531 #[serde(skip_serializing_if = "Option::is_none")]
532 pub hash: Option<Vec<u8>>,
533}
534
535impl RemoteTransactionBody {
536 pub fn validate(&self) -> Result<(), Error> {
537 if let Some(ref hash) = self.hash {
539 if hash.len() != 32 {
540 return Err(ValidationError::InvalidHash {
541 expected: 32,
542 actual: hash.len(),
543 }.into());
544 }
545 }
546 Ok(())
547 }
548}
549
550#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
551#[serde(rename_all = "camelCase")]
552pub struct SendTokensBody {
553 #[serde(rename = "Hash")]
554 #[serde(skip_serializing_if = "Option::is_none")]
555 pub hash: Option<Vec<u8>>,
556 #[serde(rename = "Meta")]
557 #[serde(skip_serializing_if = "Option::is_none")]
558 pub meta: Option<serde_json::Value>,
559 #[serde(rename = "To")]
560 pub to: Vec<serde_json::Value>,
561}
562
563impl SendTokensBody {
564 pub fn validate(&self) -> Result<(), Error> {
565 if self.to.is_empty() {
567 return Err(ValidationError::EmptyCollection(
568 "to: at least one recipient is required".to_string()
569 ).into());
570 }
571
572 for (i, recipient) in self.to.iter().enumerate() {
574 if !recipient.is_object() {
575 return Err(ValidationError::InvalidFieldValue {
576 field: format!("to[{}]", i),
577 reason: "each recipient must be a valid object with url and amount".to_string(),
578 }.into());
579 }
580
581 if let Some(url) = recipient.get("url").and_then(|v| v.as_str()) {
583 validate_accumulate_url(url, &format!("to[{}].url", i))?;
584 }
585
586 if let Some(amount) = recipient.get("amount").and_then(|v| v.as_str()) {
588 validate_amount_string(amount, &format!("to[{}].amount", i))?;
589 }
590 }
591
592 if let Some(ref hash) = self.hash {
594 if hash.len() != 32 {
595 return Err(ValidationError::InvalidHash {
596 expected: 32,
597 actual: hash.len(),
598 }.into());
599 }
600 }
601
602 Ok(())
603 }
604}
605
606#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
607#[serde(rename_all = "camelCase")]
608pub struct SystemGenesisBody {
609 }
611
612impl SystemGenesisBody {
613 pub fn validate(&self) -> Result<(), Error> {
614 Ok(())
616 }
617}
618
619#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
620#[serde(rename_all = "camelCase")]
621pub struct SystemWriteDataBody {
622 #[serde(rename = "Entry")]
623 pub entry: serde_json::Value,
624 #[serde(rename = "WriteToState")]
625 #[serde(skip_serializing_if = "Option::is_none")]
626 pub write_to_state: Option<bool>,
627}
628
629impl SystemWriteDataBody {
630 pub fn validate(&self) -> Result<(), Error> {
631 if !self.entry.is_object() {
633 return Err(ValidationError::InvalidFieldValue {
634 field: "entry".to_string(),
635 reason: "entry must be a valid data entry object".to_string(),
636 }.into());
637 }
638 Ok(())
639 }
640}
641
642#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
643#[serde(rename_all = "camelCase")]
644pub struct TransferCreditsBody {
645 #[serde(rename = "To")]
646 pub to: Vec<serde_json::Value>,
647}
648
649impl TransferCreditsBody {
650 pub fn validate(&self) -> Result<(), Error> {
651 if self.to.is_empty() {
653 return Err(ValidationError::EmptyCollection(
654 "to: at least one credit recipient is required".to_string()
655 ).into());
656 }
657
658 for (i, recipient) in self.to.iter().enumerate() {
660 if !recipient.is_object() {
661 return Err(ValidationError::InvalidFieldValue {
662 field: format!("to[{}]", i),
663 reason: "each recipient must be a valid object with url and amount".to_string(),
664 }.into());
665 }
666
667 if let Some(url) = recipient.get("url").and_then(|v| v.as_str()) {
669 validate_accumulate_url(url, &format!("to[{}].url", i))?;
670 }
671 }
672
673 Ok(())
674 }
675}
676
677#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
678#[serde(rename_all = "camelCase")]
679pub struct UpdateAccountAuthBody {
680 #[serde(rename = "Operations")]
681 pub operations: Vec<serde_json::Value>,
682}
683
684impl UpdateAccountAuthBody {
685 pub fn validate(&self) -> Result<(), Error> {
686 if self.operations.is_empty() {
688 return Err(ValidationError::EmptyCollection(
689 "operations: at least one auth operation is required".to_string()
690 ).into());
691 }
692
693 for (i, op) in self.operations.iter().enumerate() {
695 if !op.is_object() {
696 return Err(ValidationError::InvalidFieldValue {
697 field: format!("operations[{}]", i),
698 reason: "each operation must be a valid auth operation object".to_string(),
699 }.into());
700 }
701 }
702
703 Ok(())
704 }
705}
706
707#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
708#[serde(rename_all = "camelCase")]
709pub struct UpdateKeyBody {
710 #[serde(rename = "NewKeyHash")]
711 #[serde(with = "hex::serde")]
712 pub new_key_hash: Vec<u8>,
713}
714
715impl UpdateKeyBody {
716 pub fn validate(&self) -> Result<(), Error> {
717 if self.new_key_hash.len() != 32 {
719 return Err(ValidationError::InvalidHash {
720 expected: 32,
721 actual: self.new_key_hash.len(),
722 }.into());
723 }
724 Ok(())
725 }
726}
727
728#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
729#[serde(rename_all = "camelCase")]
730pub struct UpdateKeyPageBody {
731 #[serde(rename = "Operation")]
732 pub operation: Vec<serde_json::Value>,
733}
734
735impl UpdateKeyPageBody {
736 pub fn validate(&self) -> Result<(), Error> {
737 if self.operation.is_empty() {
739 return Err(ValidationError::EmptyCollection(
740 "operation: at least one key page operation is required".to_string()
741 ).into());
742 }
743
744 for (i, op) in self.operation.iter().enumerate() {
746 if !op.is_object() {
747 return Err(ValidationError::InvalidFieldValue {
748 field: format!("operation[{}]", i),
749 reason: "each operation must be a valid key page operation object".to_string(),
750 }.into());
751 }
752 }
753
754 Ok(())
755 }
756}
757
758#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
759#[serde(rename_all = "camelCase")]
760pub struct WriteDataBody {
761 #[serde(rename = "Entry")]
762 pub entry: serde_json::Value,
763 #[serde(rename = "Scratch")]
764 #[serde(skip_serializing_if = "Option::is_none")]
765 pub scratch: Option<bool>,
766 #[serde(rename = "WriteToState")]
767 #[serde(skip_serializing_if = "Option::is_none")]
768 pub write_to_state: Option<bool>,
769}
770
771impl WriteDataBody {
772 pub fn validate(&self) -> Result<(), Error> {
773 if !self.entry.is_object() {
775 return Err(ValidationError::InvalidFieldValue {
776 field: "entry".to_string(),
777 reason: "entry must be a valid data entry object".to_string(),
778 }.into());
779 }
780 Ok(())
781 }
782}
783
784#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
785#[serde(rename_all = "camelCase")]
786pub struct WriteDataToBody {
787 #[serde(rename = "Recipient")]
788 pub recipient: String,
789 #[serde(rename = "Entry")]
790 pub entry: serde_json::Value,
791}
792
793impl WriteDataToBody {
794 pub fn validate(&self) -> Result<(), Error> {
795 validate_accumulate_url(&self.recipient, "recipient")?;
797
798 if !self.entry.is_object() {
800 return Err(ValidationError::InvalidFieldValue {
801 field: "entry".to_string(),
802 reason: "entry must be a valid data entry object".to_string(),
803 }.into());
804 }
805
806 Ok(())
807 }
808}
809
810#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
811#[serde(tag = "type")]
812pub enum TransactionBody {
813 #[serde(rename = "acmeFaucet")]
814 AcmeFaucet(AcmeFaucetBody),
815 #[serde(rename = "activateProtocolVersion")]
816 ActivateProtocolVersion(ActivateProtocolVersionBody),
817 #[serde(rename = "addCredits")]
818 AddCredits(AddCreditsBody),
819 #[serde(rename = "blockValidatorAnchor")]
820 BlockValidatorAnchor(BlockValidatorAnchorBody),
821 #[serde(rename = "burnCredits")]
822 BurnCredits(BurnCreditsBody),
823 #[serde(rename = "burnTokens")]
824 BurnTokens(BurnTokensBody),
825 #[serde(rename = "createDataAccount")]
826 CreateDataAccount(CreateDataAccountBody),
827 #[serde(rename = "createIdentity")]
828 CreateIdentity(CreateIdentityBody),
829 #[serde(rename = "createKeyBook")]
830 CreateKeyBook(CreateKeyBookBody),
831 #[serde(rename = "createKeyPage")]
832 CreateKeyPage(CreateKeyPageBody),
833 #[serde(rename = "createLiteTokenAccount")]
834 CreateLiteTokenAccount(CreateLiteTokenAccountBody),
835 #[serde(rename = "createToken")]
836 CreateToken(CreateTokenBody),
837 #[serde(rename = "createTokenAccount")]
838 CreateTokenAccount(CreateTokenAccountBody),
839 #[serde(rename = "directoryAnchor")]
840 DirectoryAnchor(DirectoryAnchorBody),
841 #[serde(rename = "issueTokens")]
842 IssueTokens(IssueTokensBody),
843 #[serde(rename = "lockAccount")]
844 LockAccount(LockAccountBody),
845 #[serde(rename = "networkMaintenance")]
846 NetworkMaintenance(NetworkMaintenanceBody),
847 #[serde(rename = "remoteTransaction")]
848 RemoteTransaction(RemoteTransactionBody),
849 #[serde(rename = "sendTokens")]
850 SendTokens(SendTokensBody),
851 #[serde(rename = "systemGenesis")]
852 SystemGenesis(SystemGenesisBody),
853 #[serde(rename = "systemWriteData")]
854 SystemWriteData(SystemWriteDataBody),
855 #[serde(rename = "transferCredits")]
856 TransferCredits(TransferCreditsBody),
857 #[serde(rename = "updateAccountAuth")]
858 UpdateAccountAuth(UpdateAccountAuthBody),
859 #[serde(rename = "updateKey")]
860 UpdateKey(UpdateKeyBody),
861 #[serde(rename = "updateKeyPage")]
862 UpdateKeyPage(UpdateKeyPageBody),
863 #[serde(rename = "writeData")]
864 WriteData(WriteDataBody),
865 #[serde(rename = "writeDataTo")]
866 WriteDataTo(WriteDataToBody),
867}
868
869impl TransactionBody {
870 pub fn validate(&self) -> Result<(), Error> {
871 match self {
872 TransactionBody::AcmeFaucet(b) => b.validate(),
873 TransactionBody::ActivateProtocolVersion(b) => b.validate(),
874 TransactionBody::AddCredits(b) => b.validate(),
875 TransactionBody::BlockValidatorAnchor(b) => b.validate(),
876 TransactionBody::BurnCredits(b) => b.validate(),
877 TransactionBody::BurnTokens(b) => b.validate(),
878 TransactionBody::CreateDataAccount(b) => b.validate(),
879 TransactionBody::CreateIdentity(b) => b.validate(),
880 TransactionBody::CreateKeyBook(b) => b.validate(),
881 TransactionBody::CreateKeyPage(b) => b.validate(),
882 TransactionBody::CreateLiteTokenAccount(b) => b.validate(),
883 TransactionBody::CreateToken(b) => b.validate(),
884 TransactionBody::CreateTokenAccount(b) => b.validate(),
885 TransactionBody::DirectoryAnchor(b) => b.validate(),
886 TransactionBody::IssueTokens(b) => b.validate(),
887 TransactionBody::LockAccount(b) => b.validate(),
888 TransactionBody::NetworkMaintenance(b) => b.validate(),
889 TransactionBody::RemoteTransaction(b) => b.validate(),
890 TransactionBody::SendTokens(b) => b.validate(),
891 TransactionBody::SystemGenesis(b) => b.validate(),
892 TransactionBody::SystemWriteData(b) => b.validate(),
893 TransactionBody::TransferCredits(b) => b.validate(),
894 TransactionBody::UpdateAccountAuth(b) => b.validate(),
895 TransactionBody::UpdateKey(b) => b.validate(),
896 TransactionBody::UpdateKeyPage(b) => b.validate(),
897 TransactionBody::WriteData(b) => b.validate(),
898 TransactionBody::WriteDataTo(b) => b.validate(),
899 }
900 }
901}
902
903#[cfg(test)]
904pub fn __minimal_tx_body_json(wire_tag: &str) -> serde_json::Value {
905 match wire_tag {
906 "acmeFaucet" => serde_json::json!({
907 "type": "acmeFaucet",
908 "Url": ""
909 }),
910 "activateProtocolVersion" => serde_json::json!({
911 "type": "activateProtocolVersion"
912 }),
913 "addCredits" => serde_json::json!({
914 "type": "addCredits",
915 "Recipient": "",
916 "Amount": "0",
917 "Oracle": 0
918 }),
919 "blockValidatorAnchor" => serde_json::json!({
920 "type": "blockValidatorAnchor",
921 "AcmeBurnt": "0"
922 }),
923 "burnCredits" => serde_json::json!({
924 "type": "burnCredits",
925 "Amount": 0
926 }),
927 "burnTokens" => serde_json::json!({
928 "type": "burnTokens",
929 "Amount": "0"
930 }),
931 "createDataAccount" => serde_json::json!({
932 "type": "createDataAccount",
933 "Url": ""
934 }),
935 "createIdentity" => serde_json::json!({
936 "type": "createIdentity",
937 "Url": ""
938 }),
939 "createKeyBook" => serde_json::json!({
940 "type": "createKeyBook",
941 "Url": "",
942 "PublicKeyHash": "00"
943 }),
944 "createKeyPage" => serde_json::json!({
945 "type": "createKeyPage",
946 "Keys": []
947 }),
948 "createLiteTokenAccount" => serde_json::json!({
949 "type": "createLiteTokenAccount"
950 }),
951 "createToken" => serde_json::json!({
952 "type": "createToken",
953 "Url": "",
954 "Symbol": "",
955 "Precision": 0
956 }),
957 "createTokenAccount" => serde_json::json!({
958 "type": "createTokenAccount",
959 "Url": "",
960 "TokenUrl": ""
961 }),
962 "directoryAnchor" => serde_json::json!({
963 "type": "directoryAnchor",
964 "Updates": [],
965 "Receipts": [],
966 "MakeMajorBlock": 0,
967 "MakeMajorBlockTime": {}
968 }),
969 "issueTokens" => serde_json::json!({
970 "type": "issueTokens",
971 "Recipient": "",
972 "Amount": "0",
973 "To": []
974 }),
975 "lockAccount" => serde_json::json!({
976 "type": "lockAccount",
977 "Height": 0
978 }),
979 "networkMaintenance" => serde_json::json!({
980 "type": "networkMaintenance",
981 "Operations": []
982 }),
983 "remoteTransaction" => serde_json::json!({
984 "type": "remoteTransaction"
985 }),
986 "sendTokens" => serde_json::json!({
987 "type": "sendTokens",
988 "To": []
989 }),
990 "systemGenesis" => serde_json::json!({
991 "type": "systemGenesis"
992 }),
993 "systemWriteData" => serde_json::json!({
994 "type": "systemWriteData",
995 "Entry": {}
996 }),
997 "transferCredits" => serde_json::json!({
998 "type": "transferCredits",
999 "To": []
1000 }),
1001 "updateAccountAuth" => serde_json::json!({
1002 "type": "updateAccountAuth",
1003 "Operations": []
1004 }),
1005 "updateKey" => serde_json::json!({
1006 "type": "updateKey",
1007 "NewKeyHash": "00"
1008 }),
1009 "updateKeyPage" => serde_json::json!({
1010 "type": "updateKeyPage",
1011 "Operation": []
1012 }),
1013 "writeData" => serde_json::json!({
1014 "type": "writeData",
1015 "Entry": {}
1016 }),
1017 "writeDataTo" => serde_json::json!({
1018 "type": "writeDataTo",
1019 "Recipient": "",
1020 "Entry": {}
1021 }),
1022 _ => serde_json::json!({"type": wire_tag}),
1023 }
1024}
1025
1026#[cfg(test)]
1027pub fn __tx_roundtrip_one(wire_tag: &str) -> Result<(), Box<dyn std::error::Error>> {
1028 let original = __minimal_tx_body_json(wire_tag);
1029 let body: TransactionBody = serde_json::from_value(original.clone())?;
1030 let serialized = serde_json::to_value(&body)?;
1031
1032 if original != serialized {
1033 return Err(format!("Roundtrip mismatch for {}: original != serialized", wire_tag).into());
1034 }
1035
1036 body.validate()?;
1037 Ok(())
1038}
1039
1040#[cfg(test)]
1041pub fn __test_all_tx_roundtrips() -> Result<(), Box<dyn std::error::Error>> {
1042 __tx_roundtrip_one("acmeFaucet");
1043 __tx_roundtrip_one("activateProtocolVersion");
1044 __tx_roundtrip_one("addCredits");
1045 __tx_roundtrip_one("blockValidatorAnchor");
1046 __tx_roundtrip_one("burnCredits");
1047 __tx_roundtrip_one("burnTokens");
1048 __tx_roundtrip_one("createDataAccount");
1049 __tx_roundtrip_one("createIdentity");
1050 __tx_roundtrip_one("createKeyBook");
1051 __tx_roundtrip_one("createKeyPage");
1052 __tx_roundtrip_one("createLiteTokenAccount");
1053 __tx_roundtrip_one("createToken");
1054 __tx_roundtrip_one("createTokenAccount");
1055 __tx_roundtrip_one("directoryAnchor");
1056 __tx_roundtrip_one("issueTokens");
1057 __tx_roundtrip_one("lockAccount");
1058 __tx_roundtrip_one("networkMaintenance");
1059 __tx_roundtrip_one("remoteTransaction");
1060 __tx_roundtrip_one("sendTokens");
1061 __tx_roundtrip_one("systemGenesis");
1062 __tx_roundtrip_one("systemWriteData");
1063 __tx_roundtrip_one("transferCredits");
1064 __tx_roundtrip_one("updateAccountAuth");
1065 __tx_roundtrip_one("updateKey");
1066 __tx_roundtrip_one("updateKeyPage");
1067 __tx_roundtrip_one("writeData");
1068 __tx_roundtrip_one("writeDataTo");
1069 Ok(())
1070}