1use crate::account::account::{Account, AuthenticationKey};
7use crate::crypto::{
8 AnyPublicKey, AnyPublicKeyVariant, AnySignature, MULTI_KEY_SCHEME, MultiKeyPublicKey,
9 MultiKeySignature,
10};
11use crate::error::{AptosError, AptosResult};
12use crate::types::AccountAddress;
13use std::fmt;
14
15pub enum AnyPrivateKey {
17 #[cfg(feature = "ed25519")]
19 Ed25519(crate::crypto::Ed25519PrivateKey),
20 #[cfg(feature = "secp256k1")]
22 Secp256k1(crate::crypto::Secp256k1PrivateKey),
23 #[cfg(feature = "secp256r1")]
25 Secp256r1(crate::crypto::Secp256r1PrivateKey),
26}
27
28impl AnyPrivateKey {
29 #[allow(unreachable_code)]
31 pub fn variant(&self) -> AnyPublicKeyVariant {
32 match self {
33 #[cfg(feature = "ed25519")]
34 Self::Ed25519(_) => AnyPublicKeyVariant::Ed25519,
35 #[cfg(feature = "secp256k1")]
36 Self::Secp256k1(_) => AnyPublicKeyVariant::Secp256k1,
37 #[cfg(feature = "secp256r1")]
38 Self::Secp256r1(_) => AnyPublicKeyVariant::Secp256r1,
39 #[allow(unreachable_patterns)]
40 _ => unreachable!("AnyPrivateKey requires at least one crypto feature to be enabled"),
41 }
42 }
43
44 #[allow(unreachable_code)]
46 pub fn public_key(&self) -> AnyPublicKey {
47 match self {
48 #[cfg(feature = "ed25519")]
49 Self::Ed25519(key) => AnyPublicKey::ed25519(&key.public_key()),
50 #[cfg(feature = "secp256k1")]
51 Self::Secp256k1(key) => AnyPublicKey::secp256k1(&key.public_key()),
52 #[cfg(feature = "secp256r1")]
53 Self::Secp256r1(key) => AnyPublicKey::secp256r1(&key.public_key()),
54 #[allow(unreachable_patterns)]
55 _ => unreachable!("AnyPrivateKey requires at least one crypto feature to be enabled"),
56 }
57 }
58
59 #[allow(unreachable_code, unused_variables)]
61 pub fn sign(&self, message: &[u8]) -> AnySignature {
62 match self {
63 #[cfg(feature = "ed25519")]
64 Self::Ed25519(key) => AnySignature::ed25519(&key.sign(message)),
65 #[cfg(feature = "secp256k1")]
66 Self::Secp256k1(key) => AnySignature::secp256k1(&key.sign(message)),
67 #[cfg(feature = "secp256r1")]
68 Self::Secp256r1(key) => AnySignature::secp256r1(&key.sign(message)),
69 #[allow(unreachable_patterns)]
70 _ => unreachable!("AnyPrivateKey requires at least one crypto feature to be enabled"),
71 }
72 }
73
74 #[cfg(feature = "ed25519")]
76 pub fn ed25519(key: crate::crypto::Ed25519PrivateKey) -> Self {
77 Self::Ed25519(key)
78 }
79
80 #[cfg(feature = "secp256k1")]
82 pub fn secp256k1(key: crate::crypto::Secp256k1PrivateKey) -> Self {
83 Self::Secp256k1(key)
84 }
85
86 #[cfg(feature = "secp256r1")]
88 pub fn secp256r1(key: crate::crypto::Secp256r1PrivateKey) -> Self {
89 Self::Secp256r1(key)
90 }
91}
92
93impl Clone for AnyPrivateKey {
94 #[allow(unreachable_code)]
95 fn clone(&self) -> Self {
96 match self {
97 #[cfg(feature = "ed25519")]
98 Self::Ed25519(key) => Self::Ed25519(key.clone()),
99 #[cfg(feature = "secp256k1")]
100 Self::Secp256k1(key) => Self::Secp256k1(key.clone()),
101 #[cfg(feature = "secp256r1")]
102 Self::Secp256r1(key) => Self::Secp256r1(key.clone()),
103 #[allow(unreachable_patterns)]
104 _ => unreachable!("AnyPrivateKey requires at least one crypto feature to be enabled"),
105 }
106 }
107}
108
109impl fmt::Debug for AnyPrivateKey {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 write!(f, "AnyPrivateKey({:?})", self.variant())
112 }
113}
114
115pub struct MultiKeyAccount {
138 private_keys: Vec<(u8, AnyPrivateKey)>,
140 public_key: MultiKeyPublicKey,
142 address: AccountAddress,
144}
145
146impl MultiKeyAccount {
147 pub fn new(private_keys: Vec<AnyPrivateKey>, threshold: u8) -> AptosResult<Self> {
164 if private_keys.is_empty() {
165 return Err(AptosError::InvalidPrivateKey(
166 "at least one private key is required".into(),
167 ));
168 }
169 if (threshold as usize) > private_keys.len() {
170 return Err(AptosError::InvalidPrivateKey(format!(
171 "threshold {} exceeds number of keys {}",
172 threshold,
173 private_keys.len()
174 )));
175 }
176
177 let public_keys: Vec<_> = private_keys.iter().map(AnyPrivateKey::public_key).collect();
178 let multi_public_key = MultiKeyPublicKey::new(public_keys, threshold)?;
179 let address = multi_public_key.to_address();
180
181 #[allow(clippy::cast_possible_truncation)]
183 let indexed_keys: Vec<_> = private_keys
184 .into_iter()
185 .enumerate()
186 .map(|(i, k)| (i as u8, k))
187 .collect();
188
189 Ok(Self {
190 private_keys: indexed_keys,
191 public_key: multi_public_key,
192 address,
193 })
194 }
195
196 pub fn from_keys(
213 public_keys: Vec<AnyPublicKey>,
214 private_keys: Vec<(u8, AnyPrivateKey)>,
215 threshold: u8,
216 ) -> AptosResult<Self> {
217 let multi_public_key = MultiKeyPublicKey::new(public_keys, threshold)?;
218
219 for (index, key) in &private_keys {
221 if *index as usize >= multi_public_key.num_keys() {
222 return Err(AptosError::InvalidPrivateKey(format!(
223 "private key index {index} out of bounds"
224 )));
225 }
226
227 let Some(expected_pk) = multi_public_key.get(*index as usize) else {
229 return Err(AptosError::InvalidPrivateKey(format!(
230 "private key index {index} out of bounds"
231 )));
232 };
233
234 let actual_pk = key.public_key();
235 if expected_pk.variant != actual_pk.variant || expected_pk.bytes != actual_pk.bytes {
236 return Err(AptosError::InvalidPrivateKey(format!(
237 "private key at index {index} doesn't match public key"
238 )));
239 }
240 }
241
242 let address = multi_public_key.to_address();
243
244 Ok(Self {
245 private_keys,
246 public_key: multi_public_key,
247 address,
248 })
249 }
250
251 pub fn view_only(public_keys: Vec<AnyPublicKey>, threshold: u8) -> AptosResult<Self> {
257 let multi_public_key = MultiKeyPublicKey::new(public_keys, threshold)?;
258 let address = multi_public_key.to_address();
259
260 Ok(Self {
261 private_keys: vec![],
262 public_key: multi_public_key,
263 address,
264 })
265 }
266
267 pub fn address(&self) -> AccountAddress {
269 self.address
270 }
271
272 pub fn public_key(&self) -> &MultiKeyPublicKey {
274 &self.public_key
275 }
276
277 pub fn num_keys(&self) -> usize {
279 self.public_key.num_keys()
280 }
281
282 pub fn threshold(&self) -> u8 {
284 self.public_key.threshold()
285 }
286
287 pub fn num_owned_keys(&self) -> usize {
289 self.private_keys.len()
290 }
291
292 pub fn can_sign(&self) -> bool {
294 self.private_keys.len() >= self.threshold() as usize
295 }
296
297 pub fn owned_key_indices(&self) -> Vec<u8> {
299 self.private_keys.iter().map(|(i, _)| *i).collect()
300 }
301
302 pub fn key_types(&self) -> Vec<AnyPublicKeyVariant> {
304 self.public_key
305 .public_keys()
306 .iter()
307 .map(|pk| pk.variant)
308 .collect()
309 }
310
311 pub fn sign_message(&self, message: &[u8]) -> AptosResult<MultiKeySignature> {
319 let threshold = self.threshold() as usize;
320 if self.private_keys.len() < threshold {
321 return Err(AptosError::InsufficientSignatures {
322 required: threshold,
323 provided: self.private_keys.len(),
324 });
325 }
326
327 let signatures: Vec<_> = self.private_keys[..threshold]
329 .iter()
330 .map(|(index, key)| (*index, key.sign(message)))
331 .collect();
332
333 MultiKeySignature::new(signatures)
334 }
335
336 pub fn sign_with_indices(
344 &self,
345 message: &[u8],
346 indices: &[u8],
347 ) -> AptosResult<MultiKeySignature> {
348 if indices.len() < self.threshold() as usize {
349 return Err(AptosError::InsufficientSignatures {
350 required: self.threshold() as usize,
351 provided: indices.len(),
352 });
353 }
354
355 let mut signatures = Vec::with_capacity(indices.len());
356
357 for &index in indices {
358 let key = self
359 .private_keys
360 .iter()
361 .find(|(i, _)| *i == index)
362 .ok_or_else(|| {
363 AptosError::InvalidPrivateKey(format!(
364 "don't have private key at index {index}"
365 ))
366 })?;
367
368 signatures.push((index, key.1.sign(message)));
369 }
370
371 MultiKeySignature::new(signatures)
372 }
373
374 pub fn verify(&self, message: &[u8], signature: &MultiKeySignature) -> AptosResult<()> {
380 self.public_key.verify(message, signature)
381 }
382
383 pub fn auth_key(&self) -> AuthenticationKey {
385 AuthenticationKey::new(self.public_key.to_authentication_key())
386 }
387
388 pub fn aggregate_signatures(
397 signatures: Vec<(u8, AnySignature)>,
398 ) -> AptosResult<MultiKeySignature> {
399 MultiKeySignature::new(signatures)
400 }
401
402 pub fn create_signature_contribution(
408 &self,
409 message: &[u8],
410 key_index: u8,
411 ) -> AptosResult<(u8, AnySignature)> {
412 let key = self
413 .private_keys
414 .iter()
415 .find(|(i, _)| *i == key_index)
416 .ok_or_else(|| {
417 AptosError::InvalidPrivateKey(format!(
418 "don't have private key at index {key_index}"
419 ))
420 })?;
421
422 Ok((key_index, key.1.sign(message)))
423 }
424}
425
426impl Account for MultiKeyAccount {
427 fn address(&self) -> AccountAddress {
428 self.address
429 }
430
431 fn public_key_bytes(&self) -> Vec<u8> {
432 self.public_key.to_bytes()
433 }
434
435 fn sign(&self, message: &[u8]) -> AptosResult<Vec<u8>> {
436 let sig = self.sign_message(message)?;
437 Ok(sig.to_bytes())
438 }
439
440 fn authentication_key(&self) -> AuthenticationKey {
441 self.auth_key()
442 }
443
444 fn signature_scheme(&self) -> u8 {
445 MULTI_KEY_SCHEME
446 }
447}
448
449impl fmt::Debug for MultiKeyAccount {
450 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
451 f.debug_struct("MultiKeyAccount")
454 .field("address", &self.address)
455 .field(
456 "keys",
457 &format!(
458 "{}-of-{} (own {})",
459 self.threshold(),
460 self.num_keys(),
461 self.num_owned_keys()
462 ),
463 )
464 .field("types", &self.key_types())
465 .field("public_key", &self.public_key)
466 .field("private_keys", &"[REDACTED]")
467 .finish()
468 }
469}
470
471impl fmt::Display for MultiKeyAccount {
472 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
473 write!(
474 f,
475 "MultiKeyAccount({}, {}-of-{})",
476 self.address.to_short_string(),
477 self.threshold(),
478 self.num_keys()
479 )
480 }
481}
482
483#[cfg(test)]
484mod tests {
485 use super::*;
486
487 #[test]
488 #[cfg(feature = "ed25519")]
489 fn test_create_ed25519_only() {
490 use crate::crypto::Ed25519PrivateKey;
491
492 let keys: Vec<_> = (0..3)
493 .map(|_| AnyPrivateKey::ed25519(Ed25519PrivateKey::generate()))
494 .collect();
495 let account = MultiKeyAccount::new(keys, 2).unwrap();
496
497 assert_eq!(account.num_keys(), 3);
498 assert_eq!(account.threshold(), 2);
499 assert_eq!(account.num_owned_keys(), 3);
500 assert!(account.can_sign());
501
502 for variant in account.key_types() {
504 assert_eq!(variant, AnyPublicKeyVariant::Ed25519);
505 }
506 }
507
508 #[test]
509 #[cfg(all(feature = "ed25519", feature = "secp256k1"))]
510 fn test_create_mixed_types() {
511 use crate::crypto::{Ed25519PrivateKey, Secp256k1PrivateKey};
512
513 let keys = vec![
514 AnyPrivateKey::ed25519(Ed25519PrivateKey::generate()),
515 AnyPrivateKey::secp256k1(Secp256k1PrivateKey::generate()),
516 AnyPrivateKey::ed25519(Ed25519PrivateKey::generate()),
517 ];
518 let account = MultiKeyAccount::new(keys, 2).unwrap();
519
520 assert_eq!(account.num_keys(), 3);
521 let types = account.key_types();
522 assert_eq!(types[0], AnyPublicKeyVariant::Ed25519);
523 assert_eq!(types[1], AnyPublicKeyVariant::Secp256k1);
524 assert_eq!(types[2], AnyPublicKeyVariant::Ed25519);
525 }
526
527 #[test]
528 #[cfg(feature = "ed25519")]
529 fn test_sign_and_verify() {
530 use crate::crypto::Ed25519PrivateKey;
531
532 let keys: Vec<_> = (0..3)
533 .map(|_| AnyPrivateKey::ed25519(Ed25519PrivateKey::generate()))
534 .collect();
535 let account = MultiKeyAccount::new(keys, 2).unwrap();
536
537 let message = b"test message";
538 let signature = account.sign_message(message).unwrap();
539
540 assert!(account.verify(message, &signature).is_ok());
541 assert!(account.verify(b"wrong message", &signature).is_err());
542 }
543
544 #[test]
545 #[cfg(all(feature = "ed25519", feature = "secp256k1"))]
546 fn test_sign_mixed_types() {
547 use crate::crypto::{Ed25519PrivateKey, Secp256k1PrivateKey};
548
549 let keys = vec![
550 AnyPrivateKey::ed25519(Ed25519PrivateKey::generate()),
551 AnyPrivateKey::secp256k1(Secp256k1PrivateKey::generate()),
552 AnyPrivateKey::ed25519(Ed25519PrivateKey::generate()),
553 ];
554 let account = MultiKeyAccount::new(keys, 2).unwrap();
555
556 let message = b"test message";
557 let signature = account.sign_message(message).unwrap();
558
559 assert!(account.verify(message, &signature).is_ok());
560 }
561
562 #[test]
563 #[cfg(feature = "ed25519")]
564 fn test_partial_keys() {
565 use crate::crypto::Ed25519PrivateKey;
566
567 let all_keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
568 let public_keys: Vec<_> = all_keys
569 .iter()
570 .map(|k| AnyPublicKey::ed25519(&k.public_key()))
571 .collect();
572
573 let my_keys = vec![
575 (0u8, AnyPrivateKey::ed25519(all_keys[0].clone())),
576 (2u8, AnyPrivateKey::ed25519(all_keys[2].clone())),
577 ];
578
579 let account = MultiKeyAccount::from_keys(public_keys, my_keys, 2).unwrap();
580
581 assert_eq!(account.num_keys(), 3);
582 assert_eq!(account.num_owned_keys(), 2);
583 assert!(account.can_sign());
584
585 let message = b"test";
587 let signature = account.sign_message(message).unwrap();
588 assert!(account.verify(message, &signature).is_ok());
589 }
590
591 #[test]
592 #[cfg(feature = "ed25519")]
593 fn test_insufficient_keys() {
594 use crate::crypto::Ed25519PrivateKey;
595
596 let all_keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
597 let public_keys: Vec<_> = all_keys
598 .iter()
599 .map(|k| AnyPublicKey::ed25519(&k.public_key()))
600 .collect();
601
602 let my_keys = vec![(0u8, AnyPrivateKey::ed25519(all_keys[0].clone()))];
604
605 let account = MultiKeyAccount::from_keys(public_keys, my_keys, 2).unwrap();
606
607 assert!(!account.can_sign());
608 assert!(account.sign_message(b"test").is_err());
609 }
610
611 #[test]
612 #[cfg(feature = "ed25519")]
613 fn test_view_only() {
614 use crate::crypto::Ed25519PrivateKey;
615
616 let keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
617 let public_keys: Vec<_> = keys
618 .iter()
619 .map(|k| AnyPublicKey::ed25519(&k.public_key()))
620 .collect();
621
622 let view_only = MultiKeyAccount::view_only(public_keys, 2).unwrap();
623
624 assert_eq!(view_only.num_keys(), 3);
625 assert_eq!(view_only.num_owned_keys(), 0);
626 assert!(!view_only.can_sign());
627 assert!(view_only.sign_message(b"test").is_err());
628 }
629
630 #[test]
631 #[cfg(feature = "ed25519")]
632 fn test_deterministic_address() {
633 use crate::crypto::Ed25519PrivateKey;
634
635 let keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
636 let public_keys: Vec<_> = keys
637 .iter()
638 .map(|k| AnyPublicKey::ed25519(&k.public_key()))
639 .collect();
640
641 let account1 = MultiKeyAccount::new(
642 keys.iter()
643 .map(|k| AnyPrivateKey::ed25519(k.clone()))
644 .collect(),
645 2,
646 )
647 .unwrap();
648 let account2 = MultiKeyAccount::view_only(public_keys, 2).unwrap();
649
650 assert_eq!(account1.address(), account2.address());
652 }
653
654 #[test]
655 #[cfg(feature = "ed25519")]
656 fn test_any_private_key_variant() {
657 use crate::crypto::Ed25519PrivateKey;
658
659 let key = AnyPrivateKey::ed25519(Ed25519PrivateKey::generate());
660 assert_eq!(key.variant(), AnyPublicKeyVariant::Ed25519);
661 }
662
663 #[test]
664 #[cfg(feature = "secp256k1")]
665 fn test_any_private_key_secp256k1() {
666 use crate::crypto::Secp256k1PrivateKey;
667
668 let key = AnyPrivateKey::secp256k1(Secp256k1PrivateKey::generate());
669 assert_eq!(key.variant(), AnyPublicKeyVariant::Secp256k1);
670
671 let pk = key.public_key();
673 assert_eq!(pk.variant, AnyPublicKeyVariant::Secp256k1);
674
675 let _sig = key.sign(b"test message");
677 }
678
679 #[test]
680 #[cfg(feature = "secp256r1")]
681 fn test_any_private_key_secp256r1() {
682 use crate::crypto::Secp256r1PrivateKey;
683
684 let key = AnyPrivateKey::secp256r1(Secp256r1PrivateKey::generate());
685 assert_eq!(key.variant(), AnyPublicKeyVariant::Secp256r1);
686
687 let pk = key.public_key();
689 assert_eq!(pk.variant, AnyPublicKeyVariant::Secp256r1);
690
691 let _sig = key.sign(b"test message");
693 }
694
695 #[test]
696 #[cfg(feature = "ed25519")]
697 fn test_any_private_key_clone() {
698 use crate::crypto::Ed25519PrivateKey;
699
700 let key = AnyPrivateKey::ed25519(Ed25519PrivateKey::generate());
701 let cloned = key.clone();
702 assert_eq!(key.variant(), cloned.variant());
703 assert_eq!(
705 key.public_key().to_bcs_bytes(),
706 cloned.public_key().to_bcs_bytes()
707 );
708 }
709
710 #[test]
711 #[cfg(feature = "ed25519")]
712 fn test_any_private_key_debug() {
713 use crate::crypto::Ed25519PrivateKey;
714
715 let key = AnyPrivateKey::ed25519(Ed25519PrivateKey::generate());
716 let debug = format!("{key:?}");
717 assert!(debug.contains("AnyPrivateKey"));
718 assert!(debug.contains("Ed25519"));
719 }
720
721 #[test]
722 #[cfg(feature = "ed25519")]
723 fn test_multi_key_account_empty_keys() {
724 let result = MultiKeyAccount::new(vec![], 1);
725 assert!(result.is_err());
726 }
727
728 #[test]
729 #[cfg(feature = "ed25519")]
730 fn test_multi_key_account_threshold_zero() {
731 use crate::crypto::Ed25519PrivateKey;
732
733 let keys: Vec<_> = (0..2)
734 .map(|_| AnyPrivateKey::ed25519(Ed25519PrivateKey::generate()))
735 .collect();
736 let result = MultiKeyAccount::new(keys, 0);
737 assert!(result.is_err());
738 }
739
740 #[test]
741 #[cfg(feature = "ed25519")]
742 fn test_multi_key_account_threshold_exceeds_keys() {
743 use crate::crypto::Ed25519PrivateKey;
744
745 let keys: Vec<_> = (0..2)
746 .map(|_| AnyPrivateKey::ed25519(Ed25519PrivateKey::generate()))
747 .collect();
748 let result = MultiKeyAccount::new(keys, 5);
749 assert!(result.is_err());
750 }
751
752 #[test]
753 #[cfg(feature = "ed25519")]
754 fn test_from_keys_invalid_index() {
755 use crate::crypto::Ed25519PrivateKey;
756
757 let keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
758 let public_keys: Vec<_> = keys
759 .iter()
760 .map(|k| AnyPublicKey::ed25519(&k.public_key()))
761 .collect();
762
763 let my_keys = vec![(10u8, AnyPrivateKey::ed25519(keys[0].clone()))];
765
766 let result = MultiKeyAccount::from_keys(public_keys, my_keys, 1);
767 assert!(result.is_err());
768 }
769
770 #[test]
771 #[cfg(feature = "ed25519")]
772 fn test_from_keys_mismatched_public_key() {
773 use crate::crypto::Ed25519PrivateKey;
774
775 let keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
776 let public_keys: Vec<_> = keys
777 .iter()
778 .map(|k| AnyPublicKey::ed25519(&k.public_key()))
779 .collect();
780
781 let different_key = Ed25519PrivateKey::generate();
783 let my_keys = vec![(0u8, AnyPrivateKey::ed25519(different_key))];
784
785 let result = MultiKeyAccount::from_keys(public_keys, my_keys, 1);
786 assert!(result.is_err());
787 }
788
789 #[test]
790 #[cfg(feature = "ed25519")]
791 fn test_multi_key_account_public_key() {
792 use crate::crypto::Ed25519PrivateKey;
793
794 let keys: Vec<_> = (0..2)
795 .map(|_| AnyPrivateKey::ed25519(Ed25519PrivateKey::generate()))
796 .collect();
797 let account = MultiKeyAccount::new(keys, 2).unwrap();
798 let pk = account.public_key();
799 assert_eq!(pk.num_keys(), 2);
800 }
801
802 #[test]
803 #[cfg(feature = "ed25519")]
804 fn test_multi_key_account_address_not_zero() {
805 use crate::crypto::Ed25519PrivateKey;
806
807 let keys: Vec<_> = (0..2)
808 .map(|_| AnyPrivateKey::ed25519(Ed25519PrivateKey::generate()))
809 .collect();
810 let account = MultiKeyAccount::new(keys, 2).unwrap();
811 assert!(!account.address().is_zero());
812 }
813
814 #[test]
815 #[cfg(feature = "ed25519")]
816 fn test_multi_key_account_display() {
817 use crate::crypto::Ed25519PrivateKey;
818
819 let keys: Vec<_> = (0..2)
820 .map(|_| AnyPrivateKey::ed25519(Ed25519PrivateKey::generate()))
821 .collect();
822 let account = MultiKeyAccount::new(keys, 2).unwrap();
823 let display = format!("{account}");
824 assert!(display.contains("0x") || display.contains("MultiKeyAccount"));
826 }
827
828 #[test]
829 #[cfg(feature = "ed25519")]
830 fn test_multi_key_account_debug() {
831 use crate::crypto::Ed25519PrivateKey;
832
833 let keys: Vec<_> = (0..2)
834 .map(|_| AnyPrivateKey::ed25519(Ed25519PrivateKey::generate()))
835 .collect();
836 let account = MultiKeyAccount::new(keys, 2).unwrap();
837 let debug = format!("{account:?}");
838 assert!(debug.contains("MultiKeyAccount"));
839 }
840}