1use crate::error::AptosResult;
4use crate::transaction::authenticator::TransactionAuthenticator;
5use crate::transaction::payload::TransactionPayload;
6use crate::types::{AccountAddress, ChainId, HashValue};
7use serde::{Deserialize, Serialize};
8
9#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
15pub struct RawTransaction {
16 pub sender: AccountAddress,
18 pub sequence_number: u64,
20 pub payload: TransactionPayload,
22 pub max_gas_amount: u64,
24 pub gas_unit_price: u64,
26 pub expiration_timestamp_secs: u64,
28 pub chain_id: ChainId,
30}
31
32impl RawTransaction {
33 pub fn new(
35 sender: AccountAddress,
36 sequence_number: u64,
37 payload: TransactionPayload,
38 max_gas_amount: u64,
39 gas_unit_price: u64,
40 expiration_timestamp_secs: u64,
41 chain_id: ChainId,
42 ) -> Self {
43 Self {
44 sender,
45 sequence_number,
46 payload,
47 max_gas_amount,
48 gas_unit_price,
49 expiration_timestamp_secs,
50 chain_id,
51 }
52 }
53
54 pub fn signing_message(&self) -> AptosResult<Vec<u8>> {
63 let prefix = crate::crypto::sha3_256(b"APTOS::RawTransaction");
64 let bcs_bytes = aptos_bcs::to_bytes(self).map_err(crate::error::AptosError::bcs)?;
65
66 let mut message = Vec::with_capacity(prefix.len() + bcs_bytes.len());
67 message.extend_from_slice(&prefix);
68 message.extend_from_slice(&bcs_bytes);
69 Ok(message)
70 }
71
72 pub fn to_bcs(&self) -> AptosResult<Vec<u8>> {
78 aptos_bcs::to_bytes(self).map_err(crate::error::AptosError::bcs)
79 }
80}
81
82#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
94pub struct RawTransactionOrderless {
95 pub sender: AccountAddress,
97 pub nonce: Vec<u8>,
99 pub payload: TransactionPayload,
101 pub max_gas_amount: u64,
103 pub gas_unit_price: u64,
105 pub expiration_timestamp_secs: u64,
108 pub chain_id: ChainId,
110}
111
112impl RawTransactionOrderless {
113 pub fn new(
115 sender: AccountAddress,
116 payload: TransactionPayload,
117 max_gas_amount: u64,
118 gas_unit_price: u64,
119 expiration_timestamp_secs: u64,
120 chain_id: ChainId,
121 ) -> Self {
122 let mut nonce = vec![0u8; 32];
124 rand::RngCore::fill_bytes(&mut rand::rngs::OsRng, &mut nonce);
125 Self {
126 sender,
127 nonce,
128 payload,
129 max_gas_amount,
130 gas_unit_price,
131 expiration_timestamp_secs,
132 chain_id,
133 }
134 }
135
136 pub fn with_nonce(
138 sender: AccountAddress,
139 nonce: Vec<u8>,
140 payload: TransactionPayload,
141 max_gas_amount: u64,
142 gas_unit_price: u64,
143 expiration_timestamp_secs: u64,
144 chain_id: ChainId,
145 ) -> Self {
146 Self {
147 sender,
148 nonce,
149 payload,
150 max_gas_amount,
151 gas_unit_price,
152 expiration_timestamp_secs,
153 chain_id,
154 }
155 }
156
157 pub fn signing_message(&self) -> AptosResult<Vec<u8>> {
163 let prefix = crate::crypto::sha3_256(b"APTOS::RawTransactionOrderless");
164 let bcs_bytes = aptos_bcs::to_bytes(self).map_err(crate::error::AptosError::bcs)?;
165
166 let mut message = Vec::with_capacity(prefix.len() + bcs_bytes.len());
167 message.extend_from_slice(&prefix);
168 message.extend_from_slice(&bcs_bytes);
169 Ok(message)
170 }
171
172 pub fn to_bcs(&self) -> AptosResult<Vec<u8>> {
178 aptos_bcs::to_bytes(self).map_err(crate::error::AptosError::bcs)
179 }
180}
181
182#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
184pub struct SignedTransactionOrderless {
185 pub raw_txn: RawTransactionOrderless,
187 pub authenticator: TransactionAuthenticator,
189}
190
191impl SignedTransactionOrderless {
192 pub fn new(raw_txn: RawTransactionOrderless, authenticator: TransactionAuthenticator) -> Self {
194 Self {
195 raw_txn,
196 authenticator,
197 }
198 }
199
200 pub fn to_bcs(&self) -> AptosResult<Vec<u8>> {
206 aptos_bcs::to_bytes(self).map_err(crate::error::AptosError::bcs)
207 }
208
209 pub fn sender(&self) -> AccountAddress {
211 self.raw_txn.sender
212 }
213
214 pub fn nonce(&self) -> &[u8] {
216 &self.raw_txn.nonce
217 }
218
219 pub fn hash(&self) -> AptosResult<HashValue> {
225 let bcs_bytes = self.to_bcs()?;
226 let prefix = crate::crypto::sha3_256(b"APTOS::Transaction");
227
228 let mut data = Vec::with_capacity(prefix.len() + 1 + bcs_bytes.len());
229 data.extend_from_slice(&prefix);
230 data.push(2);
232 data.extend_from_slice(&bcs_bytes);
233
234 Ok(HashValue::new(crate::crypto::sha3_256(&data)))
235 }
236}
237
238#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
240pub struct SignedTransaction {
241 pub raw_txn: RawTransaction,
243 pub authenticator: TransactionAuthenticator,
245}
246
247impl SignedTransaction {
248 pub fn new(raw_txn: RawTransaction, authenticator: TransactionAuthenticator) -> Self {
250 Self {
251 raw_txn,
252 authenticator,
253 }
254 }
255
256 pub fn to_bcs(&self) -> AptosResult<Vec<u8>> {
262 aptos_bcs::to_bytes(self).map_err(crate::error::AptosError::bcs)
263 }
264
265 pub fn sender(&self) -> AccountAddress {
267 self.raw_txn.sender
268 }
269
270 pub fn sequence_number(&self) -> u64 {
272 self.raw_txn.sequence_number
273 }
274
275 pub fn hash(&self) -> AptosResult<HashValue> {
281 let bcs_bytes = self.to_bcs()?;
282 let prefix = crate::crypto::sha3_256(b"APTOS::Transaction");
283
284 let mut data = Vec::with_capacity(prefix.len() + 1 + bcs_bytes.len());
285 data.extend_from_slice(&prefix);
286 data.push(0); data.extend_from_slice(&bcs_bytes);
288
289 Ok(HashValue::new(crate::crypto::sha3_256(&data)))
290 }
291}
292
293#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
295pub struct TransactionInfo {
296 pub hash: HashValue,
298 #[serde(default)]
300 pub version: Option<u64>,
301 #[serde(default)]
303 pub success: Option<bool>,
304 #[serde(default)]
306 pub vm_status: Option<String>,
307 #[serde(default)]
309 pub gas_used: Option<u64>,
310}
311
312impl TransactionInfo {
313 pub fn is_success(&self) -> bool {
315 self.success.unwrap_or(false)
316 }
317}
318
319#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
321pub struct MultiAgentRawTransaction {
322 pub raw_txn: RawTransaction,
324 pub secondary_signer_addresses: Vec<AccountAddress>,
326}
327
328impl MultiAgentRawTransaction {
329 pub fn new(raw_txn: RawTransaction, secondary_signer_addresses: Vec<AccountAddress>) -> Self {
331 Self {
332 raw_txn,
333 secondary_signer_addresses,
334 }
335 }
336
337 pub fn signing_message(&self) -> AptosResult<Vec<u8>> {
343 #[derive(Serialize)]
345 enum RawTransactionWithData<'a> {
346 MultiAgent {
347 raw_txn: &'a RawTransaction,
348 secondary_signer_addresses: &'a Vec<AccountAddress>,
349 },
350 }
351
352 let prefix = crate::crypto::sha3_256(b"APTOS::RawTransactionWithData");
353
354 let data = RawTransactionWithData::MultiAgent {
355 raw_txn: &self.raw_txn,
356 secondary_signer_addresses: &self.secondary_signer_addresses,
357 };
358
359 let bcs_bytes = aptos_bcs::to_bytes(&data).map_err(crate::error::AptosError::bcs)?;
360
361 let mut message = Vec::with_capacity(prefix.len() + bcs_bytes.len());
362 message.extend_from_slice(&prefix);
363 message.extend_from_slice(&bcs_bytes);
364 Ok(message)
365 }
366}
367
368#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
370pub struct FeePayerRawTransaction {
371 pub raw_txn: RawTransaction,
373 pub secondary_signer_addresses: Vec<AccountAddress>,
375 pub fee_payer_address: AccountAddress,
377}
378
379impl FeePayerRawTransaction {
380 pub fn new(
382 raw_txn: RawTransaction,
383 secondary_signer_addresses: Vec<AccountAddress>,
384 fee_payer_address: AccountAddress,
385 ) -> Self {
386 Self {
387 raw_txn,
388 secondary_signer_addresses,
389 fee_payer_address,
390 }
391 }
392
393 pub fn new_simple(raw_txn: RawTransaction, fee_payer_address: AccountAddress) -> Self {
395 Self {
396 raw_txn,
397 secondary_signer_addresses: vec![],
398 fee_payer_address,
399 }
400 }
401
402 pub fn signing_message(&self) -> AptosResult<Vec<u8>> {
408 #[derive(Serialize)]
409 enum RawTransactionWithData<'a> {
410 #[allow(dead_code)]
411 MultiAgent {
412 raw_txn: &'a RawTransaction,
413 secondary_signer_addresses: &'a Vec<AccountAddress>,
414 },
415 MultiAgentWithFeePayer {
416 raw_txn: &'a RawTransaction,
417 secondary_signer_addresses: &'a Vec<AccountAddress>,
418 fee_payer_address: &'a AccountAddress,
419 },
420 }
421 let prefix = crate::crypto::sha3_256(b"APTOS::RawTransactionWithData");
422
423 let data = RawTransactionWithData::MultiAgentWithFeePayer {
424 raw_txn: &self.raw_txn,
425 secondary_signer_addresses: &self.secondary_signer_addresses,
426 fee_payer_address: &self.fee_payer_address,
427 };
428
429 let bcs_bytes = aptos_bcs::to_bytes(&data).map_err(crate::error::AptosError::bcs)?;
430
431 let mut message = Vec::with_capacity(prefix.len() + bcs_bytes.len());
432 message.extend_from_slice(&prefix);
433 message.extend_from_slice(&bcs_bytes);
434 Ok(message)
435 }
436}
437
438#[cfg(test)]
439mod tests {
440 use super::*;
441 use crate::transaction::payload::EntryFunction;
442 use crate::types::MoveModuleId;
443
444 fn create_test_raw_transaction() -> RawTransaction {
445 RawTransaction::new(
446 AccountAddress::ONE,
447 0,
448 TransactionPayload::EntryFunction(EntryFunction {
449 module: MoveModuleId::from_str_strict("0x1::coin").unwrap(),
450 function: "transfer".to_string(),
451 type_args: vec![],
452 args: vec![],
453 }),
454 100_000,
455 100,
456 1_000_000_000,
457 ChainId::testnet(),
458 )
459 }
460
461 #[test]
462 fn test_raw_transaction_signing_message() {
463 let txn = create_test_raw_transaction();
464 let message = txn.signing_message().unwrap();
465 assert!(!message.is_empty());
466 assert_eq!(message.len(), 32 + txn.to_bcs().unwrap().len());
468 }
469
470 #[test]
471 fn test_raw_transaction_fields() {
472 let txn = create_test_raw_transaction();
473 assert_eq!(txn.sender, AccountAddress::ONE);
474 assert_eq!(txn.sequence_number, 0);
475 assert_eq!(txn.max_gas_amount, 100_000);
476 assert_eq!(txn.gas_unit_price, 100);
477 assert_eq!(txn.expiration_timestamp_secs, 1_000_000_000);
478 assert_eq!(txn.chain_id, ChainId::testnet());
479 }
480
481 #[test]
482 fn test_raw_transaction_bcs_serialization() {
483 let txn = create_test_raw_transaction();
484 let bcs = txn.to_bcs().unwrap();
485 assert!(!bcs.is_empty());
486 }
487
488 #[test]
489 fn test_signed_transaction() {
490 use crate::transaction::authenticator::{Ed25519PublicKey, Ed25519Signature};
491 let txn = create_test_raw_transaction();
492 let auth = crate::transaction::TransactionAuthenticator::Ed25519 {
494 public_key: Ed25519PublicKey([0u8; 32]),
495 signature: Ed25519Signature([0u8; 64]),
496 };
497 let signed = SignedTransaction::new(txn, auth);
498 assert_eq!(signed.sender(), AccountAddress::ONE);
499 }
500
501 #[test]
502 fn test_signed_transaction_bcs() {
503 use crate::transaction::authenticator::{Ed25519PublicKey, Ed25519Signature};
504 let txn = create_test_raw_transaction();
505 let auth = crate::transaction::TransactionAuthenticator::Ed25519 {
506 public_key: Ed25519PublicKey([0u8; 32]),
507 signature: Ed25519Signature([0u8; 64]),
508 };
509 let signed = SignedTransaction::new(txn, auth);
510 let bcs = signed.to_bcs().unwrap();
511 assert!(!bcs.is_empty());
512 }
513
514 #[test]
515 fn test_authenticator_bcs_format() {
516 use crate::transaction::authenticator::{Ed25519PublicKey, Ed25519Signature};
518 let auth = crate::transaction::TransactionAuthenticator::Ed25519 {
519 public_key: Ed25519PublicKey([0xab; 32]),
520 signature: Ed25519Signature([0xcd; 64]),
521 };
522 let bcs = aptos_bcs::to_bytes(&auth).unwrap();
523
524 println!("Authenticator BCS bytes: {}", hex::encode(&bcs));
526 println!("First byte (variant index): {}", bcs[0]);
527 println!("Second byte (length prefix): {}", bcs[1]);
528 println!("Third byte (first pubkey byte): {}", bcs[2]);
529
530 assert_eq!(bcs[0], 0, "Ed25519 variant index should be 0");
532 assert_eq!(bcs[1], 32, "Pubkey length prefix should be 32");
534 assert_eq!(bcs[2], 0xab, "First pubkey byte should be 0xab");
536 assert_eq!(bcs[34], 64, "Signature length prefix should be 64");
538 assert_eq!(bcs[35], 0xcd, "First signature byte should be 0xcd");
540 assert_eq!(bcs.len(), 99, "BCS length should be 99");
542 }
543
544 #[test]
545 fn test_transaction_info_deserialization() {
546 let json = r#"{
547 "version": 12345,
548 "hash": "0x0000000000000000000000000000000000000000000000000000000000000001",
549 "gas_used": 100,
550 "success": true,
551 "vm_status": "Executed successfully"
552 }"#;
553 let info: TransactionInfo = serde_json::from_str(json).unwrap();
554 assert_eq!(info.version, Some(12345));
555 assert_eq!(info.gas_used, Some(100));
556 assert_eq!(info.success, Some(true));
557 assert_eq!(info.vm_status, Some("Executed successfully".to_string()));
558 }
559
560 #[test]
561 fn test_fee_payer_raw_transaction_new() {
562 let raw_txn = create_test_raw_transaction();
563 let secondary_addr = AccountAddress::from_hex("0x2").unwrap();
564 let fee_payer_addr = AccountAddress::THREE;
565 let fee_payer = FeePayerRawTransaction::new(raw_txn, vec![secondary_addr], fee_payer_addr);
566 assert_eq!(fee_payer.fee_payer_address, AccountAddress::THREE);
567 assert_eq!(fee_payer.secondary_signer_addresses.len(), 1);
568 }
569
570 #[test]
571 fn test_fee_payer_raw_transaction_new_simple() {
572 let raw_txn = create_test_raw_transaction();
573 let fee_payer = FeePayerRawTransaction::new_simple(raw_txn, AccountAddress::THREE);
574 assert_eq!(fee_payer.fee_payer_address, AccountAddress::THREE);
575 assert!(fee_payer.secondary_signer_addresses.is_empty());
576 }
577
578 #[test]
579 fn test_fee_payer_signing_message() {
580 let raw_txn = create_test_raw_transaction();
581 let fee_payer = FeePayerRawTransaction::new_simple(raw_txn, AccountAddress::THREE);
582 let message = fee_payer.signing_message().unwrap();
583 assert!(!message.is_empty());
584 }
585
586 #[test]
587 fn test_signed_transaction_hash() {
588 use crate::transaction::authenticator::{Ed25519PublicKey, Ed25519Signature};
589 let txn = create_test_raw_transaction();
590 let auth = crate::transaction::TransactionAuthenticator::Ed25519 {
591 public_key: Ed25519PublicKey([0u8; 32]),
592 signature: Ed25519Signature([0u8; 64]),
593 };
594 let signed = SignedTransaction::new(txn, auth);
595 let hash = signed.hash().unwrap();
596 assert_eq!(hash.as_bytes().len(), 32);
598 let hash2 = signed.hash().unwrap();
600 assert_eq!(hash, hash2);
601 }
602
603 #[test]
604 fn test_signed_transaction_sequence_number() {
605 use crate::transaction::authenticator::{Ed25519PublicKey, Ed25519Signature};
606 let txn = create_test_raw_transaction();
607 let auth = crate::transaction::TransactionAuthenticator::Ed25519 {
608 public_key: Ed25519PublicKey([0u8; 32]),
609 signature: Ed25519Signature([0u8; 64]),
610 };
611 let signed = SignedTransaction::new(txn, auth);
612 assert_eq!(signed.sequence_number(), 0);
613 }
614
615 #[test]
616 fn test_transaction_info_is_success() {
617 let info_success = TransactionInfo {
618 hash: HashValue::new([0; 32]),
619 version: Some(1),
620 success: Some(true),
621 vm_status: None,
622 gas_used: Some(100),
623 };
624 assert!(info_success.is_success());
625
626 let info_failed = TransactionInfo {
627 hash: HashValue::new([0; 32]),
628 version: Some(1),
629 success: Some(false),
630 vm_status: Some("Failed".to_string()),
631 gas_used: Some(100),
632 };
633 assert!(!info_failed.is_success());
634
635 let info_unknown = TransactionInfo {
636 hash: HashValue::new([0; 32]),
637 version: None,
638 success: None,
639 vm_status: None,
640 gas_used: None,
641 };
642 assert!(!info_unknown.is_success());
643 }
644
645 fn create_test_orderless_transaction() -> RawTransactionOrderless {
646 RawTransactionOrderless::with_nonce(
647 AccountAddress::ONE,
648 vec![
649 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
650 24, 25, 26, 27, 28, 29, 30, 31, 32,
651 ],
652 TransactionPayload::EntryFunction(EntryFunction {
653 module: MoveModuleId::from_str_strict("0x1::coin").unwrap(),
654 function: "transfer".to_string(),
655 type_args: vec![],
656 args: vec![],
657 }),
658 100_000,
659 100,
660 1_000_000_000,
661 ChainId::testnet(),
662 )
663 }
664
665 #[test]
666 fn test_orderless_transaction_with_nonce() {
667 let nonce = vec![0xab; 32];
668 let txn = RawTransactionOrderless::with_nonce(
669 AccountAddress::ONE,
670 nonce.clone(),
671 TransactionPayload::EntryFunction(EntryFunction {
672 module: MoveModuleId::from_str_strict("0x1::coin").unwrap(),
673 function: "transfer".to_string(),
674 type_args: vec![],
675 args: vec![],
676 }),
677 100_000,
678 100,
679 1_000_000_000,
680 ChainId::testnet(),
681 );
682 assert_eq!(txn.sender, AccountAddress::ONE);
683 assert_eq!(txn.nonce, nonce);
684 assert_eq!(txn.max_gas_amount, 100_000);
685 assert_eq!(txn.gas_unit_price, 100);
686 }
687
688 #[test]
689 fn test_orderless_transaction_new_generates_random_nonce() {
690 let txn1 = RawTransactionOrderless::new(
691 AccountAddress::ONE,
692 TransactionPayload::EntryFunction(EntryFunction {
693 module: MoveModuleId::from_str_strict("0x1::coin").unwrap(),
694 function: "transfer".to_string(),
695 type_args: vec![],
696 args: vec![],
697 }),
698 100_000,
699 100,
700 1_000_000_000,
701 ChainId::testnet(),
702 );
703 let txn2 = RawTransactionOrderless::new(
704 AccountAddress::ONE,
705 TransactionPayload::EntryFunction(EntryFunction {
706 module: MoveModuleId::from_str_strict("0x1::coin").unwrap(),
707 function: "transfer".to_string(),
708 type_args: vec![],
709 args: vec![],
710 }),
711 100_000,
712 100,
713 1_000_000_000,
714 ChainId::testnet(),
715 );
716 assert_ne!(txn1.nonce, txn2.nonce);
718 assert_eq!(txn1.nonce.len(), 32);
720 assert_eq!(txn2.nonce.len(), 32);
721 }
722
723 #[test]
724 fn test_orderless_transaction_signing_message() {
725 let txn = create_test_orderless_transaction();
726 let message = txn.signing_message().unwrap();
727 assert!(!message.is_empty());
728 assert_eq!(message.len(), 32 + txn.to_bcs().unwrap().len());
730 }
731
732 #[test]
733 fn test_orderless_transaction_bcs() {
734 let txn = create_test_orderless_transaction();
735 let bcs = txn.to_bcs().unwrap();
736 assert!(!bcs.is_empty());
737 }
738
739 #[test]
740 fn test_signed_orderless_transaction() {
741 use crate::transaction::authenticator::{Ed25519PublicKey, Ed25519Signature};
742 let txn = create_test_orderless_transaction();
743 let auth = crate::transaction::TransactionAuthenticator::Ed25519 {
744 public_key: Ed25519PublicKey([0u8; 32]),
745 signature: Ed25519Signature([0u8; 64]),
746 };
747 let signed = SignedTransactionOrderless::new(txn, auth);
748 assert_eq!(signed.sender(), AccountAddress::ONE);
749 assert_eq!(signed.nonce().len(), 32);
750 }
751
752 #[test]
753 fn test_signed_orderless_transaction_bcs() {
754 use crate::transaction::authenticator::{Ed25519PublicKey, Ed25519Signature};
755 let txn = create_test_orderless_transaction();
756 let auth = crate::transaction::TransactionAuthenticator::Ed25519 {
757 public_key: Ed25519PublicKey([0u8; 32]),
758 signature: Ed25519Signature([0u8; 64]),
759 };
760 let signed = SignedTransactionOrderless::new(txn, auth);
761 let bcs = signed.to_bcs().unwrap();
762 assert!(!bcs.is_empty());
763 }
764
765 #[test]
766 fn test_signed_orderless_transaction_hash() {
767 use crate::transaction::authenticator::{Ed25519PublicKey, Ed25519Signature};
768 let txn = create_test_orderless_transaction();
769 let auth = crate::transaction::TransactionAuthenticator::Ed25519 {
770 public_key: Ed25519PublicKey([0u8; 32]),
771 signature: Ed25519Signature([0u8; 64]),
772 };
773 let signed = SignedTransactionOrderless::new(txn, auth);
774 let hash = signed.hash().unwrap();
775 assert_eq!(hash.as_bytes().len(), 32);
777 let hash2 = signed.hash().unwrap();
779 assert_eq!(hash, hash2);
780 }
781
782 #[test]
783 fn test_multi_agent_raw_transaction() {
784 let raw_txn = create_test_raw_transaction();
785 let secondary = vec![AccountAddress::from_hex("0x2").unwrap()];
786 let multi_agent = MultiAgentRawTransaction::new(raw_txn, secondary.clone());
787 assert_eq!(multi_agent.secondary_signer_addresses, secondary);
788 }
789
790 #[test]
791 fn test_multi_agent_signing_message() {
792 let raw_txn = create_test_raw_transaction();
793 let secondary = vec![AccountAddress::from_hex("0x2").unwrap()];
794 let multi_agent = MultiAgentRawTransaction::new(raw_txn, secondary);
795 let message = multi_agent.signing_message().unwrap();
796 assert!(!message.is_empty());
797 }
798}