1use super::writer::BinaryWriter;
7use sha2::{Digest, Sha256};
8
9pub mod signature_types {
11 pub const UNKNOWN: u64 = 0;
12 pub const LEGACY_ED25519: u64 = 1;
13 pub const ED25519: u64 = 2;
14 pub const RCD1: u64 = 3;
15 pub const BTC: u64 = 4;
16 pub const BTC_LEGACY: u64 = 5;
17 pub const ETH: u64 = 6;
18 pub const DELEGATED: u64 = 7;
19 pub const INTERNAL: u64 = 8;
20 pub const RSA_SHA256: u64 = 9;
21 pub const ECDSA_SHA256: u64 = 10;
22 pub const TYPED_DATA: u64 = 11;
23 pub const REMOTE: u64 = 12;
24 pub const RECEIPT: u64 = 13;
25 pub const PARTITION: u64 = 14;
26 pub const SET: u64 = 15;
27 pub const AUTHORITY: u64 = 16;
28}
29
30pub mod tx_types {
32 pub const CREATE_IDENTITY: u64 = 0x01;
33 pub const CREATE_TOKEN_ACCOUNT: u64 = 0x02;
34 pub const SEND_TOKENS: u64 = 0x03;
35 pub const CREATE_DATA_ACCOUNT: u64 = 0x04;
36 pub const WRITE_DATA: u64 = 0x05;
37 pub const WRITE_DATA_TO: u64 = 0x06;
38 pub const ACME_FAUCET: u64 = 0x07;
39 pub const CREATE_TOKEN: u64 = 0x08;
40 pub const ISSUE_TOKENS: u64 = 0x09;
41 pub const BURN_TOKENS: u64 = 0x0A;
42 pub const CREATE_LITE_TOKEN_ACCOUNT: u64 = 0x0B;
43 pub const CREATE_KEY_PAGE: u64 = 0x0C;
44 pub const CREATE_KEY_BOOK: u64 = 0x0D;
45 pub const ADD_CREDITS: u64 = 0x0E;
46 pub const UPDATE_KEY_PAGE: u64 = 0x0F;
47 pub const LOCK_ACCOUNT: u64 = 0x10;
48 pub const BURN_CREDITS: u64 = 0x11;
49 pub const TRANSFER_CREDITS: u64 = 0x12;
50 pub const UPDATE_ACCOUNT_AUTH: u64 = 0x15;
51 pub const UPDATE_KEY: u64 = 0x16;
52}
53
54pub mod key_page_op_types {
57 pub const UNKNOWN: u64 = 0;
58 pub const UPDATE: u64 = 1; pub const REMOVE: u64 = 2; pub const ADD: u64 = 3; pub const SET_THRESHOLD: u64 = 4; pub const UPDATE_ALLOWED: u64 = 5; pub const SET_REJECT_THRESHOLD: u64 = 6; pub const SET_RESPONSE_THRESHOLD: u64 = 7; }
66
67pub fn compute_ed25519_signature_metadata_hash(
84 public_key: &[u8],
85 signer: &str,
86 signer_version: u64,
87 timestamp: u64,
88) -> [u8; 32] {
89 compute_signature_metadata_hash(
90 signature_types::ED25519,
91 public_key,
92 signer,
93 signer_version,
94 timestamp,
95 0, None, None, )
99}
100
101pub fn compute_signature_metadata_hash(
103 signature_type: u64,
104 public_key: &[u8],
105 signer: &str,
106 signer_version: u64,
107 timestamp: u64,
108 vote: u64,
109 memo: Option<&str>,
110 data: Option<&[u8]>,
111) -> [u8; 32] {
112 let mut writer = BinaryWriter::new();
113
114 let _ = writer.write_uvarint(1);
116 let _ = writer.write_uvarint(signature_type);
117
118 if !public_key.is_empty() {
120 let _ = writer.write_uvarint(2);
121 let _ = writer.write_uvarint(public_key.len() as u64);
122 let _ = writer.write_bytes(public_key);
123 }
124
125 if !signer.is_empty() {
129 let _ = writer.write_uvarint(4);
130 let signer_bytes = signer.as_bytes();
131 let _ = writer.write_uvarint(signer_bytes.len() as u64);
132 let _ = writer.write_bytes(signer_bytes);
133 }
134
135 if signer_version != 0 {
137 let _ = writer.write_uvarint(5);
138 let _ = writer.write_uvarint(signer_version);
139 }
140
141 if timestamp != 0 {
143 let _ = writer.write_uvarint(6);
144 let _ = writer.write_uvarint(timestamp);
145 }
146
147 if vote != 0 {
149 let _ = writer.write_uvarint(7);
150 let _ = writer.write_uvarint(vote);
151 }
152
153 if let Some(memo_str) = memo {
157 if !memo_str.is_empty() {
158 let _ = writer.write_uvarint(9);
159 let memo_bytes = memo_str.as_bytes();
160 let _ = writer.write_uvarint(memo_bytes.len() as u64);
161 let _ = writer.write_bytes(memo_bytes);
162 }
163 }
164
165 if let Some(data_bytes) = data {
167 if !data_bytes.is_empty() {
168 let _ = writer.write_uvarint(10);
169 let _ = writer.write_uvarint(data_bytes.len() as u64);
170 let _ = writer.write_bytes(data_bytes);
171 }
172 }
173
174 sha256_bytes(writer.bytes())
176}
177
178#[derive(Debug, Clone, Default)]
180pub struct HeaderBinaryOptions {
181 pub expire_at_time: Option<i64>,
183 pub hold_until_minor_block: Option<u64>,
185 pub authorities: Option<Vec<String>>,
187}
188
189pub fn marshal_transaction_header(
200 principal: &str,
201 initiator: &[u8; 32],
202 memo: Option<&str>,
203 metadata: Option<&[u8]>,
204) -> Vec<u8> {
205 marshal_transaction_header_full(principal, initiator, memo, metadata, None)
206}
207
208pub fn marshal_transaction_header_full(
210 principal: &str,
211 initiator: &[u8; 32],
212 memo: Option<&str>,
213 metadata: Option<&[u8]>,
214 extended: Option<&HeaderBinaryOptions>,
215) -> Vec<u8> {
216 let mut writer = BinaryWriter::new();
217
218 let _ = writer.write_uvarint(1);
220 let principal_bytes = principal.as_bytes();
221 let _ = writer.write_uvarint(principal_bytes.len() as u64);
222 let _ = writer.write_bytes(principal_bytes);
223
224 let is_zero = initiator.iter().all(|&b| b == 0);
227 if !is_zero {
228 let _ = writer.write_uvarint(2);
229 let _ = writer.write_bytes(initiator);
230 }
231
232 if let Some(memo_str) = memo {
234 if !memo_str.is_empty() {
235 let _ = writer.write_uvarint(3);
236 let memo_bytes = memo_str.as_bytes();
237 let _ = writer.write_uvarint(memo_bytes.len() as u64);
238 let _ = writer.write_bytes(memo_bytes);
239 }
240 }
241
242 if let Some(metadata_bytes) = metadata {
244 if !metadata_bytes.is_empty() {
245 let _ = writer.write_uvarint(4);
246 let _ = writer.write_uvarint(metadata_bytes.len() as u64);
247 let _ = writer.write_bytes(metadata_bytes);
248 }
249 }
250
251 if let Some(ext) = extended {
253 if let Some(at_time) = ext.expire_at_time {
259 let mut expire_writer = BinaryWriter::new();
260 let _ = expire_writer.write_uvarint(1); let _ = expire_writer.write_varint(at_time); let expire_bytes = expire_writer.into_bytes();
263
264 let _ = writer.write_uvarint(5);
265 let _ = writer.write_uvarint(expire_bytes.len() as u64);
266 let _ = writer.write_bytes(&expire_bytes);
267 }
268
269 if let Some(minor_block) = ext.hold_until_minor_block {
271 let mut hold_writer = BinaryWriter::new();
272 let _ = hold_writer.write_uvarint(1); let _ = hold_writer.write_uvarint(minor_block);
274 let hold_bytes = hold_writer.into_bytes();
275
276 let _ = writer.write_uvarint(6);
277 let _ = writer.write_uvarint(hold_bytes.len() as u64);
278 let _ = writer.write_bytes(&hold_bytes);
279 }
280
281 if let Some(ref authorities) = ext.authorities {
283 for auth_url in authorities {
284 let _ = writer.write_uvarint(7);
285 let auth_bytes = auth_url.as_bytes();
286 let _ = writer.write_uvarint(auth_bytes.len() as u64);
287 let _ = writer.write_bytes(auth_bytes);
288 }
289 }
290 }
291
292 writer.into_bytes()
293}
294
295pub fn marshal_add_credits_body(
303 recipient: &str,
304 amount: u64,
305 oracle: u64,
306) -> Vec<u8> {
307 let mut writer = BinaryWriter::new();
308
309 let _ = writer.write_uvarint(1);
311 let _ = writer.write_uvarint(tx_types::ADD_CREDITS);
312
313 let _ = writer.write_uvarint(2);
315 let recipient_bytes = recipient.as_bytes();
316 let _ = writer.write_uvarint(recipient_bytes.len() as u64);
317 let _ = writer.write_bytes(recipient_bytes);
318
319 if amount > 0 {
321 let _ = writer.write_uvarint(3);
322 let amount_bytes = amount_to_bigint_bytes(amount);
323 let _ = writer.write_uvarint(amount_bytes.len() as u64);
324 let _ = writer.write_bytes(&amount_bytes);
325 }
326
327 if oracle > 0 {
329 let _ = writer.write_uvarint(4);
330 let _ = writer.write_uvarint(oracle);
331 }
332
333 writer.into_bytes()
334}
335
336pub fn marshal_send_tokens_body(
344 recipients: &[(String, u64)], ) -> Vec<u8> {
346 let mut writer = BinaryWriter::new();
347
348 let _ = writer.write_uvarint(1);
350 let _ = writer.write_uvarint(tx_types::SEND_TOKENS);
351
352 for (url, amount) in recipients {
354 let recipient_bytes = marshal_token_recipient(url, *amount);
355 let _ = writer.write_uvarint(4);
356 let _ = writer.write_uvarint(recipient_bytes.len() as u64);
357 let _ = writer.write_bytes(&recipient_bytes);
358 }
359
360 writer.into_bytes()
361}
362
363pub fn marshal_create_identity_body(
371 url: &str,
372 key_hash: &[u8],
373 key_book_url: &str,
374) -> Vec<u8> {
375 let mut writer = BinaryWriter::new();
376
377 let _ = writer.write_uvarint(1);
379 let _ = writer.write_uvarint(tx_types::CREATE_IDENTITY);
380
381 let _ = writer.write_uvarint(2);
383 let url_bytes = url.as_bytes();
384 let _ = writer.write_uvarint(url_bytes.len() as u64);
385 let _ = writer.write_bytes(url_bytes);
386
387 if !key_hash.is_empty() {
389 let _ = writer.write_uvarint(3);
390 let _ = writer.write_uvarint(key_hash.len() as u64);
391 let _ = writer.write_bytes(key_hash);
392 }
393
394 let _ = writer.write_uvarint(4);
396 let book_bytes = key_book_url.as_bytes();
397 let _ = writer.write_uvarint(book_bytes.len() as u64);
398 let _ = writer.write_bytes(book_bytes);
399
400 writer.into_bytes()
401}
402
403pub fn marshal_create_data_account_body(url: &str) -> Vec<u8> {
410 let mut writer = BinaryWriter::new();
411
412 let _ = writer.write_uvarint(1);
414 let _ = writer.write_uvarint(tx_types::CREATE_DATA_ACCOUNT);
415
416 if !url.is_empty() {
418 let _ = writer.write_uvarint(2);
419 let url_bytes = url.as_bytes();
420 let _ = writer.write_uvarint(url_bytes.len() as u64);
421 let _ = writer.write_bytes(url_bytes);
422 }
423
424 writer.into_bytes()
425}
426
427pub fn marshal_write_data_body(entries_hex: &[String], scratch: bool, write_to_state: bool) -> Vec<u8> {
435 let mut writer = BinaryWriter::new();
436
437 let _ = writer.write_uvarint(1);
439 let _ = writer.write_uvarint(tx_types::WRITE_DATA);
440
441 if !entries_hex.is_empty() {
443 let entry_bytes = marshal_data_entry(entries_hex);
444 let _ = writer.write_uvarint(2);
445 let _ = writer.write_uvarint(entry_bytes.len() as u64);
446 let _ = writer.write_bytes(&entry_bytes);
447 }
448
449 if scratch {
451 let _ = writer.write_uvarint(3);
452 let _ = writer.write_uvarint(1); }
454
455 if write_to_state {
457 let _ = writer.write_uvarint(4);
458 let _ = writer.write_uvarint(1); }
460
461 writer.into_bytes()
462}
463
464fn marshal_data_entry(entries_hex: &[String]) -> Vec<u8> {
470 let mut writer = BinaryWriter::new();
471
472 let _ = writer.write_uvarint(1);
474 let _ = writer.write_uvarint(3); for entry_hex in entries_hex {
478 if let Ok(data) = hex::decode(entry_hex) {
479 let _ = writer.write_uvarint(2);
480 let _ = writer.write_uvarint(data.len() as u64);
481 let _ = writer.write_bytes(&data);
482 }
483 }
484
485 writer.into_bytes()
486}
487
488pub fn marshal_create_token_account_body(
496 url: &str,
497 token_url: &str,
498) -> Vec<u8> {
499 let mut writer = BinaryWriter::new();
500
501 let _ = writer.write_uvarint(1);
503 let _ = writer.write_uvarint(tx_types::CREATE_TOKEN_ACCOUNT);
504
505 if !url.is_empty() {
507 let _ = writer.write_uvarint(2);
508 let url_bytes = url.as_bytes();
509 let _ = writer.write_uvarint(url_bytes.len() as u64);
510 let _ = writer.write_bytes(url_bytes);
511 }
512
513 if !token_url.is_empty() {
515 let _ = writer.write_uvarint(3);
516 let token_bytes = token_url.as_bytes();
517 let _ = writer.write_uvarint(token_bytes.len() as u64);
518 let _ = writer.write_bytes(token_bytes);
519 }
520
521 writer.into_bytes()
522}
523
524fn marshal_token_recipient(url: &str, amount: u64) -> Vec<u8> {
526 let mut writer = BinaryWriter::new();
527
528 let _ = writer.write_uvarint(1);
530 let url_bytes = url.as_bytes();
531 let _ = writer.write_uvarint(url_bytes.len() as u64);
532 let _ = writer.write_bytes(url_bytes);
533
534 if amount > 0 {
536 let _ = writer.write_uvarint(2);
537 let amount_bytes = amount_to_bigint_bytes(amount);
538 let _ = writer.write_uvarint(amount_bytes.len() as u64);
539 let _ = writer.write_bytes(&amount_bytes);
540 }
541
542 writer.into_bytes()
543}
544
545fn amount_to_bigint_bytes(mut value: u64) -> Vec<u8> {
547 if value == 0 {
548 return vec![];
549 }
550
551 let mut bytes = Vec::new();
552 while value > 0 {
553 bytes.push((value & 0xFF) as u8);
554 value >>= 8;
555 }
556 bytes.reverse(); bytes
558}
559
560pub fn marshal_create_token_body(url: &str, symbol: &str, precision: u64, supply_limit: Option<u64>) -> Vec<u8> {
570 let mut writer = BinaryWriter::new();
571
572 let _ = writer.write_uvarint(1);
574 let _ = writer.write_uvarint(tx_types::CREATE_TOKEN);
575
576 if !url.is_empty() {
578 let _ = writer.write_uvarint(2);
579 let url_bytes = url.as_bytes();
580 let _ = writer.write_uvarint(url_bytes.len() as u64);
581 let _ = writer.write_bytes(url_bytes);
582 }
583
584 if !symbol.is_empty() {
586 let _ = writer.write_uvarint(4);
587 let symbol_bytes = symbol.as_bytes();
588 let _ = writer.write_uvarint(symbol_bytes.len() as u64);
589 let _ = writer.write_bytes(symbol_bytes);
590 }
591
592 let _ = writer.write_uvarint(5);
594 let _ = writer.write_uvarint(precision);
595
596 if let Some(limit) = supply_limit {
598 if limit > 0 {
599 let _ = writer.write_uvarint(7);
600 let limit_bytes = amount_to_bigint_bytes(limit);
601 let _ = writer.write_uvarint(limit_bytes.len() as u64);
602 let _ = writer.write_bytes(&limit_bytes);
603 }
604 }
605
606 writer.into_bytes()
607}
608
609pub fn marshal_issue_tokens_body(recipients: &[(&str, u64)]) -> Vec<u8> {
615 let mut writer = BinaryWriter::new();
616
617 let _ = writer.write_uvarint(1);
619 let _ = writer.write_uvarint(tx_types::ISSUE_TOKENS);
620
621 for (url, amount) in recipients {
623 let recipient_bytes = marshal_token_recipient(url, *amount);
624 let _ = writer.write_uvarint(4);
625 let _ = writer.write_uvarint(recipient_bytes.len() as u64);
626 let _ = writer.write_bytes(&recipient_bytes);
627 }
628
629 writer.into_bytes()
630}
631
632pub fn compute_transaction_hash(header_bytes: &[u8], body_bytes: &[u8]) -> [u8; 32] {
637 let header_hash = sha256_bytes(header_bytes);
638 let body_hash = sha256_bytes(body_bytes);
639
640 let mut combined = Vec::with_capacity(64);
641 combined.extend_from_slice(&header_hash);
642 combined.extend_from_slice(&body_hash);
643
644 sha256_bytes(&combined)
645}
646
647pub fn create_signing_preimage(
652 signature_metadata_hash: &[u8; 32],
653 transaction_hash: &[u8; 32],
654) -> [u8; 32] {
655 let mut combined = Vec::with_capacity(64);
656 combined.extend_from_slice(signature_metadata_hash);
657 combined.extend_from_slice(transaction_hash);
658
659 sha256_bytes(&combined)
660}
661
662pub fn sha256_bytes(data: &[u8]) -> [u8; 32] {
664 let mut hasher = Sha256::new();
665 hasher.update(data);
666 let result = hasher.finalize();
667 let mut output = [0u8; 32];
668 output.copy_from_slice(&result);
669 output
670}
671
672pub fn compute_write_data_body_hash(entries_hex: &[String], scratch: bool, write_to_state: bool) -> [u8; 32] {
682 let body_without_entry = marshal_write_data_body_without_entry(scratch, write_to_state);
684 let body_part_hash = sha256_bytes(&body_without_entry);
685
686 let entry_hash = if entries_hex.is_empty() {
688 [0u8; 32]
689 } else {
690 compute_data_entry_hash(entries_hex)
691 };
692
693 merkle_hash(&[body_part_hash, entry_hash])
695}
696
697fn marshal_write_data_body_without_entry(scratch: bool, write_to_state: bool) -> Vec<u8> {
699 let mut writer = BinaryWriter::new();
700
701 let _ = writer.write_uvarint(1);
703 let _ = writer.write_uvarint(tx_types::WRITE_DATA);
704
705 if scratch {
709 let _ = writer.write_uvarint(3);
710 let _ = writer.write_uvarint(1);
711 }
712
713 if write_to_state {
715 let _ = writer.write_uvarint(4);
716 let _ = writer.write_uvarint(1);
717 }
718
719 writer.into_bytes()
720}
721
722pub fn compute_write_data_to_body_hash(recipient: &str, entries_hex: &[String]) -> [u8; 32] {
727 let body_without_entry = marshal_write_data_to_body_without_entry(recipient);
729 let body_part_hash = sha256_bytes(&body_without_entry);
730
731 let entry_hash = if entries_hex.is_empty() {
733 [0u8; 32]
734 } else {
735 compute_data_entry_hash(entries_hex)
736 };
737
738 merkle_hash(&[body_part_hash, entry_hash])
740}
741
742fn marshal_write_data_to_body_without_entry(recipient: &str) -> Vec<u8> {
745 let mut writer = BinaryWriter::new();
746
747 let _ = writer.write_uvarint(1);
749 let _ = writer.write_uvarint(tx_types::WRITE_DATA_TO);
750
751 let url_bytes = recipient.as_bytes();
753 let _ = writer.write_uvarint(2);
754 let _ = writer.write_uvarint(url_bytes.len() as u64);
755 let _ = writer.write_bytes(url_bytes);
756
757 writer.into_bytes()
760}
761
762fn compute_data_entry_hash(entries_hex: &[String]) -> [u8; 32] {
767 if entries_hex.is_empty() {
768 return [0u8; 32];
769 }
770
771 let mut data_hashes: Vec<[u8; 32]> = Vec::new();
773 for entry_hex in entries_hex {
774 if let Ok(data) = hex::decode(entry_hex) {
775 data_hashes.push(sha256_bytes(&data));
776 }
777 }
778
779 if data_hashes.is_empty() {
780 return [0u8; 32];
781 }
782
783 let merkle_root = merkle_hash(&data_hashes);
785
786 sha256_bytes(&merkle_root)
788}
789
790fn merkle_hash(hashes: &[[u8; 32]]) -> [u8; 32] {
795 if hashes.is_empty() {
796 return [0u8; 32];
797 }
798
799 if hashes.len() == 1 {
800 return hashes[0];
801 }
802
803 let mut pending: Vec<Option<[u8; 32]>> = Vec::new();
805
806 for hash in hashes {
807 let mut current = *hash;
808 let mut i = 0;
809 loop {
810 if i >= pending.len() {
812 pending.push(Some(current));
813 break;
814 }
815
816 if pending[i].is_none() {
818 pending[i] = Some(current);
819 break;
820 }
821
822 current = combine_hashes(&pending[i].unwrap(), ¤t);
824 pending[i] = None;
825 i += 1;
826 }
827 }
828
829 let mut anchor: Option<[u8; 32]> = None;
831 for v in &pending {
832 if anchor.is_none() {
833 anchor = *v;
834 } else if let Some(val) = v {
835 anchor = Some(combine_hashes(val, &anchor.unwrap()));
836 }
837 }
838
839 anchor.unwrap_or([0u8; 32])
840}
841
842fn combine_hashes(left: &[u8; 32], right: &[u8; 32]) -> [u8; 32] {
844 let mut combined = Vec::with_capacity(64);
845 combined.extend_from_slice(left);
846 combined.extend_from_slice(right);
847 sha256_bytes(&combined)
848}
849
850pub fn marshal_key_spec_params(key_hash: &[u8], delegate: Option<&str>) -> Vec<u8> {
860 let mut writer = BinaryWriter::new();
861
862 if !key_hash.is_empty() {
864 let _ = writer.write_uvarint(1);
865 let _ = writer.write_uvarint(key_hash.len() as u64);
866 let _ = writer.write_bytes(key_hash);
867 }
868
869 if let Some(delegate_url) = delegate {
871 if !delegate_url.is_empty() {
872 let _ = writer.write_uvarint(2);
873 let delegate_bytes = delegate_url.as_bytes();
874 let _ = writer.write_uvarint(delegate_bytes.len() as u64);
875 let _ = writer.write_bytes(delegate_bytes);
876 }
877 }
878
879 writer.into_bytes()
880}
881
882pub fn marshal_key_page_operation(
893 op_type: &str,
894 key_hash: Option<&[u8]>,
895 delegate: Option<&str>,
896 old_key_hash: Option<&[u8]>,
897 new_key_hash: Option<&[u8]>,
898 threshold: Option<u64>,
899) -> Vec<u8> {
900 let mut writer = BinaryWriter::new();
901
902 let op_type_num = match op_type {
904 "add" => key_page_op_types::ADD,
905 "remove" => key_page_op_types::REMOVE,
906 "update" => key_page_op_types::UPDATE,
907 "setThreshold" => key_page_op_types::SET_THRESHOLD,
908 "setRejectThreshold" => key_page_op_types::SET_REJECT_THRESHOLD,
909 "setResponseThreshold" => key_page_op_types::SET_RESPONSE_THRESHOLD,
910 "updateAllowed" => key_page_op_types::UPDATE_ALLOWED,
911 _ => key_page_op_types::UNKNOWN,
912 };
913
914 let _ = writer.write_uvarint(1);
916 let _ = writer.write_uvarint(op_type_num);
917
918 match op_type {
919 "add" | "remove" => {
920 if let Some(hash) = key_hash {
922 let entry_bytes = marshal_key_spec_params(hash, delegate);
923 let _ = writer.write_uvarint(2);
924 let _ = writer.write_uvarint(entry_bytes.len() as u64);
925 let _ = writer.write_bytes(&entry_bytes);
926 }
927 }
928 "update" => {
929 if let Some(old_hash) = old_key_hash {
931 let old_entry_bytes = marshal_key_spec_params(old_hash, None);
932 let _ = writer.write_uvarint(2);
933 let _ = writer.write_uvarint(old_entry_bytes.len() as u64);
934 let _ = writer.write_bytes(&old_entry_bytes);
935 }
936 if let Some(new_hash) = new_key_hash {
938 let new_entry_bytes = marshal_key_spec_params(new_hash, delegate);
939 let _ = writer.write_uvarint(3);
940 let _ = writer.write_uvarint(new_entry_bytes.len() as u64);
941 let _ = writer.write_bytes(&new_entry_bytes);
942 }
943 }
944 "setThreshold" | "setRejectThreshold" | "setResponseThreshold" => {
945 if let Some(thresh) = threshold {
947 let _ = writer.write_uvarint(2);
948 let _ = writer.write_uvarint(thresh);
949 }
950 }
951 "updateAllowed" => {
952 }
956 _ => {}
957 }
958
959 writer.into_bytes()
960}
961
962pub fn marshal_update_key_page_body(operations: &[Vec<u8>]) -> Vec<u8> {
968 let mut writer = BinaryWriter::new();
969
970 let _ = writer.write_uvarint(1);
972 let _ = writer.write_uvarint(tx_types::UPDATE_KEY_PAGE);
973
974 for op_bytes in operations {
976 let _ = writer.write_uvarint(2);
977 let _ = writer.write_uvarint(op_bytes.len() as u64);
978 let _ = writer.write_bytes(op_bytes);
979 }
980
981 writer.into_bytes()
982}
983
984pub fn marshal_create_key_page_body(key_hashes: &[Vec<u8>]) -> Vec<u8> {
990 let mut writer = BinaryWriter::new();
991
992 let _ = writer.write_uvarint(1);
994 let _ = writer.write_uvarint(tx_types::CREATE_KEY_PAGE);
995
996 for key_hash in key_hashes {
998 let key_spec_bytes = marshal_key_spec_params(key_hash, None);
999 let _ = writer.write_uvarint(2);
1000 let _ = writer.write_uvarint(key_spec_bytes.len() as u64);
1001 let _ = writer.write_bytes(&key_spec_bytes);
1002 }
1003
1004 writer.into_bytes()
1005}
1006
1007pub fn marshal_burn_tokens_body(amount: u64) -> Vec<u8> {
1013 let mut writer = BinaryWriter::new();
1014
1015 let _ = writer.write_uvarint(1);
1017 let _ = writer.write_uvarint(tx_types::BURN_TOKENS);
1018
1019 if amount > 0 {
1021 let _ = writer.write_uvarint(2);
1022 let amount_bytes = amount_to_bigint_bytes(amount);
1023 let _ = writer.write_uvarint(amount_bytes.len() as u64);
1024 let _ = writer.write_bytes(&amount_bytes);
1025 }
1026
1027 writer.into_bytes()
1028}
1029
1030pub fn marshal_create_key_book_body(url: &str, public_key_hash: &[u8]) -> Vec<u8> {
1037 let mut writer = BinaryWriter::new();
1038
1039 let _ = writer.write_uvarint(1);
1041 let _ = writer.write_uvarint(tx_types::CREATE_KEY_BOOK);
1042
1043 let url_bytes = url.as_bytes();
1045 let _ = writer.write_uvarint(2);
1046 let _ = writer.write_uvarint(url_bytes.len() as u64);
1047 let _ = writer.write_bytes(url_bytes);
1048
1049 if !public_key_hash.is_empty() {
1051 let _ = writer.write_uvarint(3);
1052 let _ = writer.write_uvarint(public_key_hash.len() as u64);
1053 let _ = writer.write_bytes(public_key_hash);
1054 }
1055
1056 writer.into_bytes()
1057}
1058
1059pub fn marshal_update_key_body(new_key_hash: &[u8]) -> Vec<u8> {
1065 let mut writer = BinaryWriter::new();
1066
1067 let _ = writer.write_uvarint(1);
1069 let _ = writer.write_uvarint(tx_types::UPDATE_KEY);
1070
1071 if !new_key_hash.is_empty() {
1073 let _ = writer.write_uvarint(2);
1074 let _ = writer.write_uvarint(new_key_hash.len() as u64);
1075 let _ = writer.write_bytes(new_key_hash);
1076 }
1077
1078 writer.into_bytes()
1079}
1080
1081pub fn marshal_burn_credits_body(amount: u64) -> Vec<u8> {
1087 let mut writer = BinaryWriter::new();
1088
1089 let _ = writer.write_uvarint(1);
1091 let _ = writer.write_uvarint(tx_types::BURN_CREDITS);
1092
1093 if amount > 0 {
1095 let _ = writer.write_uvarint(2);
1096 let _ = writer.write_uvarint(amount);
1097 }
1098
1099 writer.into_bytes()
1100}
1101
1102pub fn marshal_transfer_credits_body(recipients: &[(&str, u64)]) -> Vec<u8> {
1108 let mut writer = BinaryWriter::new();
1109
1110 let _ = writer.write_uvarint(1);
1112 let _ = writer.write_uvarint(tx_types::TRANSFER_CREDITS);
1113
1114 for (url, amount) in recipients {
1116 let recipient_bytes = marshal_credit_recipient(url, *amount);
1117 let _ = writer.write_uvarint(2);
1118 let _ = writer.write_uvarint(recipient_bytes.len() as u64);
1119 let _ = writer.write_bytes(&recipient_bytes);
1120 }
1121
1122 writer.into_bytes()
1123}
1124
1125fn marshal_credit_recipient(url: &str, amount: u64) -> Vec<u8> {
1128 let mut writer = BinaryWriter::new();
1129
1130 let url_bytes = url.as_bytes();
1132 let _ = writer.write_uvarint(1);
1133 let _ = writer.write_uvarint(url_bytes.len() as u64);
1134 let _ = writer.write_bytes(url_bytes);
1135
1136 if amount > 0 {
1138 let _ = writer.write_uvarint(2);
1139 let _ = writer.write_uvarint(amount);
1140 }
1141
1142 writer.into_bytes()
1143}
1144
1145pub fn marshal_write_data_to_body(recipient: &str, entries_hex: &[String]) -> Vec<u8> {
1152 let mut writer = BinaryWriter::new();
1153
1154 let _ = writer.write_uvarint(1);
1156 let _ = writer.write_uvarint(tx_types::WRITE_DATA_TO);
1157
1158 let url_bytes = recipient.as_bytes();
1160 let _ = writer.write_uvarint(2);
1161 let _ = writer.write_uvarint(url_bytes.len() as u64);
1162 let _ = writer.write_bytes(url_bytes);
1163
1164 if !entries_hex.is_empty() {
1166 let entry_bytes = marshal_data_entry(entries_hex);
1167 let _ = writer.write_uvarint(3);
1168 let _ = writer.write_uvarint(entry_bytes.len() as u64);
1169 let _ = writer.write_bytes(&entry_bytes);
1170 }
1171
1172 writer.into_bytes()
1173}
1174
1175pub fn marshal_lock_account_body(height: u64) -> Vec<u8> {
1181 let mut writer = BinaryWriter::new();
1182
1183 let _ = writer.write_uvarint(1);
1185 let _ = writer.write_uvarint(tx_types::LOCK_ACCOUNT);
1186
1187 if height > 0 {
1189 let _ = writer.write_uvarint(2);
1190 let _ = writer.write_uvarint(height);
1191 }
1192
1193 writer.into_bytes()
1194}
1195
1196pub mod account_auth_op_types {
1198 pub const ENABLE: u64 = 1;
1199 pub const DISABLE: u64 = 2;
1200 pub const ADD_AUTHORITY: u64 = 3;
1201 pub const REMOVE_AUTHORITY: u64 = 4;
1202}
1203
1204pub fn marshal_update_account_auth_body(operations: &[(&str, &str)]) -> Vec<u8> {
1210 let mut writer = BinaryWriter::new();
1211
1212 let _ = writer.write_uvarint(1);
1214 let _ = writer.write_uvarint(tx_types::UPDATE_ACCOUNT_AUTH);
1215
1216 for (op_type, authority_url) in operations {
1218 let op_bytes = marshal_account_auth_operation(op_type, authority_url);
1219 let _ = writer.write_uvarint(2);
1220 let _ = writer.write_uvarint(op_bytes.len() as u64);
1221 let _ = writer.write_bytes(&op_bytes);
1222 }
1223
1224 writer.into_bytes()
1225}
1226
1227fn marshal_account_auth_operation(op_type: &str, authority_url: &str) -> Vec<u8> {
1230 let mut writer = BinaryWriter::new();
1231
1232 let type_num = match op_type {
1234 "enable" => account_auth_op_types::ENABLE,
1235 "disable" => account_auth_op_types::DISABLE,
1236 "add" | "addAuthority" => account_auth_op_types::ADD_AUTHORITY,
1237 "remove" | "removeAuthority" => account_auth_op_types::REMOVE_AUTHORITY,
1238 _ => 0,
1239 };
1240 let _ = writer.write_uvarint(1);
1241 let _ = writer.write_uvarint(type_num);
1242
1243 if !authority_url.is_empty() {
1245 let url_bytes = authority_url.as_bytes();
1246 let _ = writer.write_uvarint(2);
1247 let _ = writer.write_uvarint(url_bytes.len() as u64);
1248 let _ = writer.write_bytes(url_bytes);
1249 }
1250
1251 writer.into_bytes()
1252}
1253
1254#[cfg(test)]
1255mod tests {
1256 use super::*;
1257
1258 #[test]
1259 fn test_signature_metadata_hash() {
1260 let public_key = [1u8; 32];
1261 let signer = "acc://test.acme/book/1";
1262 let signer_version = 1;
1263 let timestamp = 1234567890000u64;
1264
1265 let hash = compute_ed25519_signature_metadata_hash(
1266 &public_key,
1267 signer,
1268 signer_version,
1269 timestamp,
1270 );
1271
1272 assert_eq!(hash.len(), 32);
1274
1275 let hash2 = compute_ed25519_signature_metadata_hash(
1277 &public_key,
1278 signer,
1279 signer_version,
1280 timestamp,
1281 );
1282 assert_eq!(hash, hash2);
1283 }
1284
1285 #[test]
1286 fn test_transaction_hash() {
1287 let header = b"test header";
1288 let body = b"test body";
1289
1290 let hash = compute_transaction_hash(header, body);
1291 assert_eq!(hash.len(), 32);
1292
1293 let hash2 = compute_transaction_hash(header, body);
1295 assert_eq!(hash, hash2);
1296 }
1297
1298 #[test]
1299 fn test_signing_preimage() {
1300 let sig_hash = [1u8; 32];
1301 let tx_hash = [2u8; 32];
1302
1303 let preimage = create_signing_preimage(&sig_hash, &tx_hash);
1304 assert_eq!(preimage.len(), 32);
1305 }
1306
1307 #[test]
1308 fn test_amount_encoding() {
1309 assert_eq!(amount_to_bigint_bytes(0), vec![] as Vec<u8>);
1311 assert_eq!(amount_to_bigint_bytes(1), vec![1]);
1312 assert_eq!(amount_to_bigint_bytes(255), vec![255]);
1313 assert_eq!(amount_to_bigint_bytes(256), vec![1, 0]);
1314 assert_eq!(amount_to_bigint_bytes(0x123456), vec![0x12, 0x34, 0x56]);
1315 }
1316}