1use crate::{error::SorobanHelperError, guard::Guard, Env, Signer, TransactionBuilder};
33use std::fmt;
34use stellar_strkey::ed25519::PublicKey;
35use stellar_xdr::curr::{
36 AccountEntry, AccountId, DecoratedSignature, Hash, Operation, OperationBody, SetOptionsOp,
37 Signer as XdrSigner, SignerKey, Transaction, TransactionEnvelope, TransactionV1Envelope, VecM,
38};
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub struct AccountSequence(i64);
43
44impl AccountSequence {
45 pub fn new(val: i64) -> Self {
51 Self(val)
52 }
53
54 pub fn next(&self) -> Self {
56 Self(self.0 + 1)
57 }
58
59 pub fn increment(self) -> Self {
64 self.next()
65 }
66
67 pub fn value(self) -> i64 {
69 self.0
70 }
71}
72
73impl fmt::Display for AccountSequence {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 write!(f, "{}", self.0)
76 }
77}
78
79impl From<i64> for AccountSequence {
80 fn from(val: i64) -> Self {
81 Self(val)
82 }
83}
84
85impl From<AccountSequence> for i64 {
86 fn from(seq: AccountSequence) -> Self {
87 seq.0
88 }
89}
90
91#[derive(Default, Debug, Clone)]
109pub struct AccountConfig {
110 master_weight: Option<u32>,
112 low_threshold: Option<u32>,
114 med_threshold: Option<u32>,
116 high_threshold: Option<u32>,
118 signers: Vec<(PublicKey, u32)>,
120}
121
122impl AccountConfig {
123 pub fn new() -> Self {
125 Self::default()
126 }
127
128 pub fn with_master_weight(mut self, weight: u32) -> Self {
135 self.master_weight = Some(weight);
136 self
137 }
138
139 pub fn with_thresholds(mut self, low: u32, med: u32, high: u32) -> Self {
147 self.low_threshold = Some(low);
148 self.med_threshold = Some(med);
149 self.high_threshold = Some(high);
150 self
151 }
152
153 pub fn add_signer(mut self, key: PublicKey, weight: u32) -> Self {
160 self.signers.push((key, weight));
161 self
162 }
163
164 fn create_signer_operation(&self, public_key: &PublicKey, weight: u32) -> Operation {
166 let signer_key = SignerKey::Ed25519(public_key.0.into());
167 Operation {
168 source_account: None,
169 body: OperationBody::SetOptions(SetOptionsOp {
170 inflation_dest: None,
171 clear_flags: None,
172 set_flags: None,
173 master_weight: None,
174 low_threshold: None,
175 med_threshold: None,
176 high_threshold: None,
177 home_domain: None,
178 signer: Some(XdrSigner {
179 key: signer_key,
180 weight,
181 }),
182 }),
183 }
184 }
185
186 fn create_thresholds_operation(&self) -> Option<Operation> {
188 let has_thresholds = self.master_weight.is_some()
189 || self.low_threshold.is_some()
190 || self.med_threshold.is_some()
191 || self.high_threshold.is_some();
192
193 if !has_thresholds {
194 return None;
195 }
196
197 Some(Operation {
198 source_account: None,
199 body: OperationBody::SetOptions(SetOptionsOp {
200 inflation_dest: None,
201 clear_flags: None,
202 set_flags: None,
203 master_weight: self.master_weight,
204 low_threshold: self.low_threshold,
205 med_threshold: self.med_threshold,
206 high_threshold: self.high_threshold,
207 home_domain: None,
208 signer: None,
209 }),
210 })
211 }
212}
213
214#[derive(Clone)]
216pub struct SingleAccount {
217 account_id: AccountId,
219 signer: Box<Signer>,
221 pub guards: Vec<Guard>,
223}
224
225impl SingleAccount {
226 pub fn new(signer: Signer) -> Self {
233 Self {
234 account_id: signer.account_id(),
235 signer: Box::new(signer),
236 guards: Vec::new(),
237 }
238 }
239}
240
241impl fmt::Display for SingleAccount {
242 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243 write!(f, "SingleAccount({})", self.account_id.0)
244 }
245}
246
247#[derive(Clone)]
249pub struct MultisigAccount {
250 account_id: AccountId,
252 pub signers: Vec<Signer>,
254 pub guards: Vec<Guard>,
256}
257
258impl MultisigAccount {
259 pub fn new(account_id: AccountId, signers: Vec<Signer>) -> Self {
267 Self {
268 account_id,
269 signers,
270 guards: Vec::new(),
271 }
272 }
273}
274
275impl fmt::Display for MultisigAccount {
276 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
277 write!(
278 f,
279 "MultisigAccount({}, {} signers)",
280 self.account_id.0,
281 self.signers.len()
282 )
283 }
284}
285
286#[derive(Clone)]
292pub enum Account {
293 KeyPair(SingleAccount),
295 Multisig(MultisigAccount),
297}
298
299impl fmt::Display for Account {
300 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301 match self {
302 Self::KeyPair(account) => write!(f, "{}", account),
303 Self::Multisig(account) => write!(f, "{}", account),
304 }
305 }
306}
307
308impl From<SingleAccount> for Account {
309 fn from(account: SingleAccount) -> Self {
310 Self::KeyPair(account)
311 }
312}
313
314impl From<MultisigAccount> for Account {
315 fn from(account: MultisigAccount) -> Self {
316 Self::Multisig(account)
317 }
318}
319
320impl From<Signer> for Account {
321 fn from(signer: Signer) -> Self {
322 Self::single(signer)
323 }
324}
325
326impl Account {
328 pub fn single(signer: Signer) -> Self {
340 Self::KeyPair(SingleAccount {
341 signer: Box::new(signer.clone()),
342 account_id: signer.account_id(),
343 guards: Vec::new(),
344 })
345 }
346
347 pub fn multisig(account_id: AccountId, signers: Vec<Signer>) -> Self {
358 Self::Multisig(MultisigAccount {
359 account_id,
360 signers,
361 guards: Vec::new(),
362 })
363 }
364
365 pub fn account_id(&self) -> AccountId {
367 match self {
368 Self::KeyPair(account) => account.account_id.clone(),
369 Self::Multisig(account) => account.account_id.clone(),
370 }
371 }
372
373 pub fn signers(&self) -> &[Signer] {
375 match self {
376 Self::KeyPair(account) => std::slice::from_ref(&*account.signer),
377 Self::Multisig(account) => &account.signers,
378 }
379 }
380
381 pub async fn load(&self, env: &Env) -> Result<AccountEntry, SorobanHelperError> {
389 env.get_account(&self.account_id().to_string()).await
390 }
391
392 pub async fn get_sequence(&self, env: &Env) -> Result<AccountSequence, SorobanHelperError> {
402 let entry = self.load(env).await?;
403 Ok(AccountSequence::from(entry.seq_num.0))
404 }
405
406 pub async fn next_sequence(&self, env: &Env) -> Result<AccountSequence, SorobanHelperError> {
418 Ok(self.get_sequence(env).await?.next())
419 }
420
421 pub fn add_guard(&mut self, guard: Guard) {
430 match self {
431 Self::KeyPair(account) => account.guards.push(guard),
432 Self::Multisig(account) => account.guards.push(guard),
433 }
434 }
435
436 pub fn check_guards(&self, tx: &Transaction) -> Result<bool, SorobanHelperError> {
445 match self {
446 Self::KeyPair(account) => {
447 for guard in &account.guards {
448 if !guard.check(tx)? {
449 return Ok(false);
450 }
451 }
452 Ok(true)
453 }
454 Self::Multisig(account) => {
455 for guard in &account.guards {
456 if !guard.check(tx)? {
457 return Ok(false);
458 }
459 }
460 Ok(true)
461 }
462 }
463 }
464
465 pub fn update_guards(&mut self, tx: &Transaction) -> Result<(), SorobanHelperError> {
470 match self {
471 Self::KeyPair(account) => {
472 for guard in &mut account.guards {
473 guard.update(tx)?;
474 }
475 Ok(())
476 }
477 Self::Multisig(account) => {
478 for guard in &mut account.guards {
479 guard.update(tx)?;
480 }
481 Ok(())
482 }
483 }
484 }
485
486 fn sign_with_tx(
498 tx: &Transaction,
499 network_id: &Hash,
500 signers: &[Signer],
501 ) -> Result<VecM<DecoratedSignature, 20>, SorobanHelperError> {
502 let signatures: Vec<DecoratedSignature> = signers
503 .iter()
504 .map(|signer| signer.sign_transaction(tx, network_id))
505 .collect::<Result<_, _>>()
506 .map_err(|e| SorobanHelperError::XdrEncodingFailed(e.to_string()))?;
507 signatures.try_into().map_err(|_| {
508 SorobanHelperError::XdrEncodingFailed("Failed to convert signatures to XDR".to_string())
509 })
510 }
511
512 pub fn sign_transaction(
525 &mut self,
526 tx: &Transaction,
527 network_id: &Hash,
528 ) -> Result<TransactionEnvelope, SorobanHelperError> {
529 if !self.check_guards(tx)? {
530 return Err(SorobanHelperError::Unauthorized(
531 "The transaction didn't pass one or more guards".to_string(),
532 ));
533 }
534
535 let signatures = Self::sign_with_tx(tx, network_id, self.signers())?;
536 self.update_guards(tx)?;
537
538 Ok(TransactionEnvelope::Tx(TransactionV1Envelope {
539 tx: tx.clone(),
540 signatures,
541 }))
542 }
543
544 pub fn sign_transaction_envelope(
555 &mut self,
556 tx_envelope: &TransactionEnvelope,
557 network_id: &Hash,
558 ) -> Result<TransactionEnvelope, SorobanHelperError> {
559 let tx_v1 = match tx_envelope {
560 TransactionEnvelope::Tx(tx_v1) => tx_v1,
561 _ => {
562 return Err(SorobanHelperError::XdrEncodingFailed(
563 "Invalid transaction envelope".to_string(),
564 ));
565 }
566 };
567
568 if !self.check_guards(&tx_v1.tx)? {
569 return Err(SorobanHelperError::Unauthorized(
570 "The transaction didn't pass one or more guards".to_string(),
571 ));
572 }
573
574 let prev_signatures = tx_v1.signatures.clone();
575 let new_signatures = Self::sign_with_signers(&tx_v1.tx, network_id, self.signers())?;
576
577 let mut all_signatures = prev_signatures.to_vec();
578 all_signatures.extend(new_signatures.to_vec());
579 let signatures: VecM<DecoratedSignature, 20> = all_signatures.try_into().map_err(|_| {
580 SorobanHelperError::XdrEncodingFailed(
581 "Too many signatures for XDR vector (max 20)".to_string(),
582 )
583 })?;
584
585 self.update_guards(&tx_v1.tx)?;
586
587 Ok(TransactionEnvelope::Tx(TransactionV1Envelope {
588 tx: tx_v1.tx.clone(),
589 signatures,
590 }))
591 }
592
593 pub async fn configure(
606 mut self,
607 env: &Env,
608 config: AccountConfig,
609 ) -> Result<TransactionEnvelope, SorobanHelperError> {
610 let mut tx = TransactionBuilder::new(&self, env);
611
612 for (public_key, weight) in &config.signers {
614 tx = tx.add_operation(config.create_signer_operation(public_key, *weight));
615 }
616
617 if let Some(op) = config.create_thresholds_operation() {
619 tx = tx.add_operation(op);
620 }
621
622 let tx = tx.simulate_and_build(env, &self).await?;
623 self.sign_transaction(&tx, &env.network_id())
624 }
625
626 pub fn sign_transaction_unsafe(
639 &self,
640 tx: &Transaction,
641 network_id: &Hash,
642 ) -> Result<TransactionEnvelope, SorobanHelperError> {
643 let signatures = Self::sign_with_signers(tx, network_id, self.signers())?;
644
645 Ok(TransactionEnvelope::Tx(TransactionV1Envelope {
646 tx: tx.clone(),
647 signatures,
648 }))
649 }
650
651 pub fn sign_with_signers(
663 tx: &Transaction,
664 network_id: &Hash,
665 signers: &[Signer],
666 ) -> Result<VecM<DecoratedSignature, 20>, SorobanHelperError> {
667 if signers.is_empty() {
668 return Err(SorobanHelperError::SigningFailed(
669 "No signers provided".to_string(),
670 ));
671 }
672
673 let signatures: Vec<DecoratedSignature> = signers
674 .iter()
675 .map(|signer| signer.sign_transaction(tx, network_id))
676 .collect::<Result<_, _>>()?;
677
678 signatures.try_into().map_err(|_| {
679 SorobanHelperError::XdrEncodingFailed(
680 "Too many signatures for XDR vector (max 20)".to_string(),
681 )
682 })
683 }
684}
685
686#[cfg(test)]
687mod test {
688 use stellar_xdr::curr::{OperationBody, Signer as XdrSigner, SignerKey, TransactionEnvelope};
689
690 use crate::account::AccountSequence;
691 use crate::guard::Guard;
692 use crate::mock::{all_signers, mock_env, mock_signer1, mock_signer3};
693 use crate::{
694 Account, AccountConfig, MultisigAccount, SingleAccount, SorobanHelperError,
695 TransactionBuilder,
696 };
697
698 #[tokio::test]
699 async fn load_account() {
700 let env = mock_env(None, None, None);
701
702 let account = Account::single(mock_signer1());
704
705 let entry = account.load(&env).await;
707
708 let expected_account_id = mock_signer1().account_id().0.to_string();
709 let res_account_id = entry.unwrap().account_id.0.to_string();
710
711 assert_eq!(expected_account_id, res_account_id);
712 }
713
714 #[tokio::test]
715 async fn multisig() {
716 let env = mock_env(None, None, None);
717
718 let account = Account::multisig(mock_signer3().account_id(), all_signers());
720
721 let entry = account.load(&env).await;
723
724 let expected_account_id = mock_signer3().account_id().0.to_string();
725 let res_account_id = entry.unwrap().account_id.0.to_string();
726
727 let signers = account.signers();
728
729 for (i, sig) in signers.iter().enumerate() {
730 assert_eq!(sig.account_id(), all_signers()[i].account_id())
731 }
732
733 assert_eq!(expected_account_id, res_account_id);
734 }
735
736 #[tokio::test]
737 async fn sign_transaction() {
738 let env = mock_env(None, None, None);
739
740 let mut account = Account::single(mock_signer1());
742
743 let tx = TransactionBuilder::new(&account, &env)
745 .build()
746 .await
747 .unwrap();
748
749 let authorized_calls = Guard::NumberOfAllowedCalls(1);
750 account.add_guard(authorized_calls.clone());
751 let signed_tx = account.sign_transaction(&tx, &env.network_id());
752
753 assert!(signed_tx.is_ok());
754
755 let signed_tx_2 = account.sign_transaction(&tx, &env.network_id());
757 assert!(signed_tx_2.is_err());
758 assert_eq!(
760 signed_tx_2.err().unwrap(),
761 SorobanHelperError::Unauthorized(
762 "The transaction didn't pass one or more guards".to_string()
763 )
764 );
765 }
766
767 #[tokio::test]
768 async fn sign_transaction_unsafe() {
769 let env = mock_env(None, None, None);
770
771 let mut account = Account::single(mock_signer1());
773
774 let tx = TransactionBuilder::new(&account, &env)
776 .build()
777 .await
778 .unwrap();
779
780 let authorized_calls = Guard::NumberOfAllowedCalls(0);
782 account.add_guard(authorized_calls);
783
784 let signed_tx = account.sign_transaction_unsafe(&tx, &env.network_id());
786
787 assert!(signed_tx.is_ok());
788 }
789
790 #[test]
791 fn test_display_account_sequence() {
792 let seq = AccountSequence::new(42);
793 assert_eq!(seq.to_string(), "42");
794 }
795
796 #[test]
797 fn test_account_sequence_conversions() {
798 let seq_from_i64 = crate::account::AccountSequence::from(123_i64);
800 assert_eq!(seq_from_i64.value(), 123);
801
802 let seq = crate::account::AccountSequence::new(456);
804 let i64_from_seq: i64 = seq.into();
805 assert_eq!(i64_from_seq, 456);
806 }
807
808 #[test]
809 fn test_account_config_default() {
810 let config = AccountConfig::default();
811
812 assert_eq!(config.master_weight, None);
813 assert_eq!(config.low_threshold, None);
814 assert_eq!(config.med_threshold, None);
815 assert_eq!(config.high_threshold, None);
816 assert!(config.signers.is_empty());
817
818 let config_new = AccountConfig::new();
819 assert_eq!(config.master_weight, config_new.master_weight);
820 assert_eq!(config.low_threshold, config_new.low_threshold);
821 assert_eq!(config.med_threshold, config_new.med_threshold);
822 assert_eq!(config.high_threshold, config_new.high_threshold);
823 assert_eq!(config.signers.len(), config_new.signers.len());
824 }
825
826 #[test]
827 fn test_account_display_implementations() {
828 let signer = mock_signer1();
829 let single_account = SingleAccount::new(signer.clone());
830 let expected_single_display = format!("SingleAccount({})", signer.account_id().0);
831 assert_eq!(single_account.to_string(), expected_single_display);
832
833 let signers = all_signers();
834 let account_id = mock_signer3().account_id();
835 let multisig_account = MultisigAccount::new(account_id.clone(), signers.clone());
836 let expected_multisig_display = format!(
837 "MultisigAccount({}, {} signers)",
838 account_id.0,
839 signers.len()
840 );
841 assert_eq!(multisig_account.to_string(), expected_multisig_display);
842
843 let account_single = Account::single(mock_signer1());
844 let account_multisig = Account::multisig(mock_signer3().account_id(), all_signers());
845
846 assert!(account_single.to_string().starts_with("SingleAccount"));
847 assert!(account_multisig.to_string().starts_with("MultisigAccount"));
848 }
849
850 #[test]
851 fn test_account_from_implementations() {
852 let signer1 = mock_signer1();
853 let single_account = SingleAccount::new(signer1);
854 let expected_id = single_account.account_id.0.to_string();
855 let account_from_single: Account = single_account.into();
856
857 assert!(matches!(account_from_single, Account::KeyPair(_)));
858
859 assert!(
860 matches!(account_from_single, Account::KeyPair(inner) if inner.account_id.0.to_string() == expected_id)
861 );
862
863 let signers = all_signers();
864 let account_id = mock_signer3().account_id();
865
866 let expected_multi_id = account_id.0.to_string();
867 let expected_signers_len = signers.len();
868
869 let multisig_account = MultisigAccount::new(account_id, signers);
870 let account_from_multisig: Account = multisig_account.into();
871
872 assert!(matches!(account_from_multisig, Account::Multisig(_)));
873
874 assert!(matches!(account_from_multisig, Account::Multisig(inner) if
875 inner.account_id.0.to_string() == expected_multi_id &&
876 inner.signers.len() == expected_signers_len
877 ));
878
879 let signer2 = mock_signer1();
880 let expected_signer_id = signer2.account_id().0.to_string();
881 let account_from_signer: Account = signer2.into();
882
883 assert!(matches!(account_from_signer, Account::KeyPair(_)));
884
885 assert!(
886 matches!(account_from_signer, Account::KeyPair(inner) if inner.account_id.0.to_string() == expected_signer_id)
887 );
888 }
889
890 #[tokio::test]
891 async fn test_next_sequence() {
892 let env = mock_env(None, None, None);
893 let account = Account::single(mock_signer1());
894
895 let current_seq = account.get_sequence(&env).await.unwrap();
896
897 let next_seq = account.next_sequence(&env).await.unwrap();
898 assert_eq!(next_seq.value(), current_seq.value() + 1);
899
900 let second_current = account.get_sequence(&env).await.unwrap();
901 let second_next = account.next_sequence(&env).await.unwrap();
902 assert_eq!(second_current.value(), current_seq.value());
903
904 assert_eq!(second_next.value(), second_current.value() + 1);
905 }
906
907 #[test]
908 fn test_create_thresholds_operation() {
909 let empty_config = AccountConfig::new();
910 assert!(empty_config.create_thresholds_operation().is_none());
911
912 let master_weight_config = AccountConfig::new().with_master_weight(5);
913 assert!(master_weight_config.create_thresholds_operation().is_some());
914
915 let thresholds_config = AccountConfig::new().with_thresholds(1, 2, 3);
916 assert!(thresholds_config.create_thresholds_operation().is_some());
917
918 let low_threshold_config = AccountConfig::new().with_thresholds(1, 0, 0);
919 let low_op = low_threshold_config.create_thresholds_operation().unwrap();
920 if let OperationBody::SetOptions(op_body) = &low_op.body {
921 assert_eq!(op_body.low_threshold, Some(1));
922 assert_eq!(op_body.med_threshold, Some(0));
923 assert_eq!(op_body.high_threshold, Some(0));
924 }
925
926 let med_threshold_config = AccountConfig::new().with_thresholds(0, 2, 0);
927 let med_op = med_threshold_config.create_thresholds_operation().unwrap();
928 if let OperationBody::SetOptions(op_body) = &med_op.body {
929 assert_eq!(op_body.low_threshold, Some(0));
930 assert_eq!(op_body.med_threshold, Some(2));
931 assert_eq!(op_body.high_threshold, Some(0));
932 }
933
934 let high_threshold_config = AccountConfig::new().with_thresholds(0, 0, 3);
935 let high_op = high_threshold_config.create_thresholds_operation().unwrap();
936 if let OperationBody::SetOptions(op_body) = &high_op.body {
937 assert_eq!(op_body.low_threshold, Some(0));
938 assert_eq!(op_body.med_threshold, Some(0));
939 assert_eq!(op_body.high_threshold, Some(3));
940 }
941 }
942
943 #[tokio::test]
944 async fn test_configure() {
945 let env = mock_env(None, None, None);
946
947 let account = Account::single(mock_signer1());
948 let config = AccountConfig::new()
949 .with_master_weight(10)
950 .with_thresholds(1, 2, 3)
951 .add_signer(mock_signer3().public_key(), 5);
952
953 let tx = account.configure(&env, config).await.unwrap();
954
955 assert!(matches!(tx, TransactionEnvelope::Tx(_)));
956
957 let tx_env = match tx {
958 TransactionEnvelope::Tx(env) => env,
959 _ => return, };
961
962 assert_eq!(tx_env.tx.operations.len(), 2);
963
964 assert!(matches!(
965 tx_env.tx.operations[0].body,
966 OperationBody::SetOptions(_)
967 ));
968
969 if let OperationBody::SetOptions(op0_body) = &tx_env.tx.operations[0].body {
970 assert_eq!(
971 op0_body.signer,
972 Some(XdrSigner {
973 key: SignerKey::Ed25519(mock_signer3().public_key().0.into()),
974 weight: 5
975 })
976 );
977 }
978
979 assert!(matches!(
980 tx_env.tx.operations[1].body,
981 OperationBody::SetOptions(_)
982 ));
983
984 if let OperationBody::SetOptions(op1_body) = &tx_env.tx.operations[1].body {
985 assert_eq!(op1_body.master_weight, Some(10));
986 assert_eq!(op1_body.low_threshold, Some(1));
987 assert_eq!(op1_body.med_threshold, Some(2));
988 assert_eq!(op1_body.high_threshold, Some(3));
989 }
990 }
991
992 #[tokio::test]
993 async fn test_sign_transaction_envelope() {
994 let env = mock_env(None, None, None);
995
996 let mut first_account = Account::single(mock_signer1());
997 let mut second_account = Account::single(mock_signer3());
998
999 let authorized_calls = Guard::NumberOfAllowedCalls(1);
1000 first_account.add_guard(authorized_calls.clone());
1001 second_account.add_guard(authorized_calls);
1002
1003 let tx = TransactionBuilder::new(&first_account, &env)
1004 .build()
1005 .await
1006 .unwrap();
1007
1008 let first_signed_envelope = first_account
1009 .sign_transaction(&tx, &env.network_id())
1010 .unwrap();
1011
1012 assert!(matches!(first_signed_envelope, TransactionEnvelope::Tx(_)));
1013
1014 let first_envelope_signatures = match &first_signed_envelope {
1015 TransactionEnvelope::Tx(tx_v1) => &tx_v1.signatures,
1016 _ => &[][..], };
1018 assert_eq!(first_envelope_signatures.len(), 1); let final_envelope = second_account
1021 .sign_transaction_envelope(&first_signed_envelope, &env.network_id())
1022 .unwrap();
1023
1024 assert!(matches!(final_envelope, TransactionEnvelope::Tx(_)));
1025
1026 let final_signatures = match &final_envelope {
1027 TransactionEnvelope::Tx(tx_v1) => &tx_v1.signatures,
1028 _ => &[][..], };
1030 assert_eq!(final_signatures.len(), 2); let first_public_key = mock_signer1().public_key();
1033 let second_public_key = mock_signer3().public_key();
1034 assert_eq!(final_signatures[0].hint.0, &first_public_key.0[28..32]); assert_eq!(final_signatures[1].hint.0, &second_public_key.0[28..32]); }
1037}