1use crate::account::account::{Account, AuthenticationKey};
7use crate::crypto::{
8 Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature, MULTI_ED25519_SCHEME,
9 MultiEd25519PublicKey, MultiEd25519Signature,
10};
11use crate::error::{AptosError, AptosResult};
12use crate::types::AccountAddress;
13use std::fmt;
14
15pub struct MultiEd25519Account {
38 private_keys: Vec<(u8, Ed25519PrivateKey)>,
40 public_key: MultiEd25519PublicKey,
42 address: AccountAddress,
44}
45
46impl MultiEd25519Account {
47 pub fn new(private_keys: Vec<Ed25519PrivateKey>, threshold: u8) -> AptosResult<Self> {
72 if private_keys.is_empty() {
73 return Err(AptosError::InvalidPrivateKey(
74 "at least one private key is required".into(),
75 ));
76 }
77 if (threshold as usize) > private_keys.len() {
78 return Err(AptosError::InvalidPrivateKey(format!(
79 "threshold {} exceeds number of keys {}",
80 threshold,
81 private_keys.len()
82 )));
83 }
84
85 let public_keys: Vec<_> = private_keys
86 .iter()
87 .map(Ed25519PrivateKey::public_key)
88 .collect();
89 let multi_public_key = MultiEd25519PublicKey::new(public_keys, threshold)?;
90 let address = multi_public_key.to_address();
91
92 #[allow(clippy::cast_possible_truncation)]
94 let indexed_keys: Vec<_> = private_keys
95 .into_iter()
96 .enumerate()
97 .map(|(i, k)| (i as u8, k))
98 .collect();
99
100 Ok(Self {
101 private_keys: indexed_keys,
102 public_key: multi_public_key,
103 address,
104 })
105 }
106
107 pub fn from_keys(
134 public_keys: Vec<Ed25519PublicKey>,
135 private_keys: Vec<(u8, Ed25519PrivateKey)>,
136 threshold: u8,
137 ) -> AptosResult<Self> {
138 let multi_public_key = MultiEd25519PublicKey::new(public_keys, threshold)?;
139
140 for (index, key) in &private_keys {
142 if *index as usize >= multi_public_key.num_keys() {
143 return Err(AptosError::InvalidPrivateKey(format!(
144 "private key index {index} out of bounds"
145 )));
146 }
147 let expected_pk = &multi_public_key.public_keys()[*index as usize];
149 if key.public_key() != *expected_pk {
150 return Err(AptosError::InvalidPrivateKey(format!(
151 "private key at index {index} doesn't match public key"
152 )));
153 }
154 }
155
156 let address = multi_public_key.to_address();
157
158 Ok(Self {
159 private_keys,
160 public_key: multi_public_key,
161 address,
162 })
163 }
164
165 pub fn view_only(public_keys: Vec<Ed25519PublicKey>, threshold: u8) -> AptosResult<Self> {
174 let multi_public_key = MultiEd25519PublicKey::new(public_keys, threshold)?;
175 let address = multi_public_key.to_address();
176
177 Ok(Self {
178 private_keys: vec![],
179 public_key: multi_public_key,
180 address,
181 })
182 }
183
184 pub fn address(&self) -> AccountAddress {
186 self.address
187 }
188
189 pub fn public_key(&self) -> &MultiEd25519PublicKey {
191 &self.public_key
192 }
193
194 pub fn num_keys(&self) -> usize {
196 self.public_key.num_keys()
197 }
198
199 pub fn threshold(&self) -> u8 {
201 self.public_key.threshold()
202 }
203
204 pub fn num_owned_keys(&self) -> usize {
206 self.private_keys.len()
207 }
208
209 pub fn can_sign(&self) -> bool {
211 self.private_keys.len() >= self.threshold() as usize
212 }
213
214 pub fn owned_key_indices(&self) -> Vec<u8> {
216 self.private_keys.iter().map(|(i, _)| *i).collect()
217 }
218
219 fn sign_internal(&self, message: &[u8]) -> AptosResult<MultiEd25519Signature> {
227 let threshold = self.threshold() as usize;
228 if self.private_keys.len() < threshold {
229 return Err(AptosError::InsufficientSignatures {
230 required: threshold,
231 provided: self.private_keys.len(),
232 });
233 }
234
235 let signatures: Vec<_> = self.private_keys[..threshold]
237 .iter()
238 .map(|(index, key)| (*index, key.sign(message)))
239 .collect();
240
241 MultiEd25519Signature::new(signatures)
242 }
243
244 pub fn sign_with_indices(
257 &self,
258 message: &[u8],
259 indices: &[u8],
260 ) -> AptosResult<MultiEd25519Signature> {
261 if indices.len() < self.threshold() as usize {
262 return Err(AptosError::InsufficientSignatures {
263 required: self.threshold() as usize,
264 provided: indices.len(),
265 });
266 }
267
268 let mut signatures = Vec::with_capacity(indices.len());
269
270 for &index in indices {
271 let key = self
272 .private_keys
273 .iter()
274 .find(|(i, _)| *i == index)
275 .ok_or_else(|| {
276 AptosError::InvalidPrivateKey(format!(
277 "don't have private key at index {index}"
278 ))
279 })?;
280
281 signatures.push((index, key.1.sign(message)));
282 }
283
284 MultiEd25519Signature::new(signatures)
285 }
286
287 pub fn verify(&self, message: &[u8], signature: &MultiEd25519Signature) -> AptosResult<()> {
293 self.public_key.verify(message, signature)
294 }
295
296 pub fn sign(&self, message: &[u8]) -> AptosResult<MultiEd25519Signature> {
304 self.sign_internal(message)
305 }
306
307 pub fn auth_key(&self) -> AuthenticationKey {
309 AuthenticationKey::new(self.public_key.to_authentication_key())
310 }
311
312 pub fn aggregate_signatures(
323 signatures: Vec<(u8, Ed25519Signature)>,
324 ) -> AptosResult<MultiEd25519Signature> {
325 MultiEd25519Signature::new(signatures)
326 }
327
328 pub fn create_signature_contribution(
337 &self,
338 message: &[u8],
339 key_index: u8,
340 ) -> AptosResult<(u8, Ed25519Signature)> {
341 let key = self
342 .private_keys
343 .iter()
344 .find(|(i, _)| *i == key_index)
345 .ok_or_else(|| {
346 AptosError::InvalidPrivateKey(format!(
347 "don't have private key at index {key_index}"
348 ))
349 })?;
350
351 Ok((key_index, key.1.sign(message)))
352 }
353}
354
355impl Account for MultiEd25519Account {
356 fn address(&self) -> AccountAddress {
357 self.address
358 }
359
360 fn public_key_bytes(&self) -> Vec<u8> {
361 self.public_key.to_bytes()
362 }
363
364 fn sign(&self, message: &[u8]) -> AptosResult<Vec<u8>> {
365 let sig = self.sign_internal(message)?;
366 Ok(sig.to_bytes())
367 }
368
369 fn authentication_key(&self) -> AuthenticationKey {
370 AuthenticationKey::new(self.public_key.to_authentication_key())
371 }
372
373 fn signature_scheme(&self) -> u8 {
374 MULTI_ED25519_SCHEME
375 }
376}
377
378impl fmt::Debug for MultiEd25519Account {
379 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
380 f.debug_struct("MultiEd25519Account")
383 .field("address", &self.address)
384 .field(
385 "keys",
386 &format!(
387 "{}-of-{} (own {})",
388 self.threshold(),
389 self.num_keys(),
390 self.num_owned_keys()
391 ),
392 )
393 .field("public_key", &self.public_key)
394 .field("private_keys", &"[REDACTED]")
395 .finish()
396 }
397}
398
399impl fmt::Display for MultiEd25519Account {
400 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
401 write!(
402 f,
403 "MultiEd25519Account({}, {}-of-{})",
404 self.address.to_short_string(),
405 self.threshold(),
406 self.num_keys()
407 )
408 }
409}
410
411#[cfg(test)]
412mod tests {
413 use super::*;
414
415 #[test]
416 fn test_create_2_of_3() {
417 let keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
418 let account = MultiEd25519Account::new(keys, 2).unwrap();
419
420 assert_eq!(account.num_keys(), 3);
421 assert_eq!(account.threshold(), 2);
422 assert_eq!(account.num_owned_keys(), 3);
423 assert!(account.can_sign());
424 }
425
426 #[test]
427 fn test_sign_and_verify() {
428 let keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
429 let account = MultiEd25519Account::new(keys, 2).unwrap();
430
431 let message = b"test message";
432 let signature = account.sign(message).unwrap();
433
434 assert!(account.verify(message, &signature).is_ok());
435 assert!(account.verify(b"wrong message", &signature).is_err());
436 }
437
438 #[test]
439 fn test_partial_keys() {
440 let all_keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
441 let public_keys: Vec<_> = all_keys.iter().map(Ed25519PrivateKey::public_key).collect();
442
443 let my_keys = vec![(0u8, all_keys[0].clone()), (2u8, all_keys[2].clone())];
445
446 let account = MultiEd25519Account::from_keys(public_keys.clone(), my_keys, 2).unwrap();
447
448 assert_eq!(account.num_keys(), 3);
449 assert_eq!(account.num_owned_keys(), 2);
450 assert!(account.can_sign());
451
452 let message = b"test";
454 let signature = account.sign(message).unwrap();
455 assert!(account.verify(message, &signature).is_ok());
456 }
457
458 #[test]
459 fn test_insufficient_keys() {
460 let all_keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
461 let public_keys: Vec<_> = all_keys.iter().map(Ed25519PrivateKey::public_key).collect();
462
463 let my_keys = vec![(0u8, all_keys[0].clone())];
465
466 let account = MultiEd25519Account::from_keys(public_keys.clone(), my_keys, 2).unwrap();
467
468 assert!(!account.can_sign());
469 assert!(account.sign(b"test").is_err());
470 }
471
472 #[test]
473 fn test_sign_with_specific_indices() {
474 let keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
475 let account = MultiEd25519Account::new(keys, 2).unwrap();
476
477 let message = b"test";
478
479 let signature = account.sign_with_indices(message, &[1, 2]).unwrap();
481 assert!(account.verify(message, &signature).is_ok());
482
483 assert!(signature.has_signature(1));
485 assert!(signature.has_signature(2));
486 assert!(!signature.has_signature(0));
487 }
488
489 #[test]
490 fn test_view_only_account() {
491 let keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
492 let public_keys: Vec<_> = keys.iter().map(Ed25519PrivateKey::public_key).collect();
493
494 let view_only = MultiEd25519Account::view_only(public_keys, 2).unwrap();
495
496 assert_eq!(view_only.num_keys(), 3);
497 assert_eq!(view_only.num_owned_keys(), 0);
498 assert!(!view_only.can_sign());
499 assert!(view_only.sign(b"test").is_err());
500 }
501
502 #[test]
503 fn test_signature_aggregation() {
504 let keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
505 let public_keys: Vec<_> = keys.iter().map(Ed25519PrivateKey::public_key).collect();
506
507 let party0 =
509 MultiEd25519Account::from_keys(public_keys.clone(), vec![(0, keys[0].clone())], 2)
510 .unwrap();
511 let party2 =
512 MultiEd25519Account::from_keys(public_keys.clone(), vec![(2, keys[2].clone())], 2)
513 .unwrap();
514
515 let message = b"transaction data";
516
517 let contrib0 = party0.create_signature_contribution(message, 0).unwrap();
519 let contrib2 = party2.create_signature_contribution(message, 2).unwrap();
520
521 let aggregated =
523 MultiEd25519Account::aggregate_signatures(vec![contrib0, contrib2]).unwrap();
524
525 assert!(party0.verify(message, &aggregated).is_ok());
527 assert!(party2.verify(message, &aggregated).is_ok());
528 }
529
530 #[test]
531 fn test_deterministic_address() {
532 let keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
533 let public_keys: Vec<_> = keys.iter().map(Ed25519PrivateKey::public_key).collect();
534
535 let account1 = MultiEd25519Account::new(keys.clone(), 2).unwrap();
536 let account2 = MultiEd25519Account::view_only(public_keys, 2).unwrap();
537
538 assert_eq!(account1.address(), account2.address());
540 }
541
542 #[test]
543 fn test_empty_keys_error() {
544 let result = MultiEd25519Account::new(vec![], 1);
545 assert!(result.is_err());
546 }
547
548 #[test]
549 fn test_threshold_exceeds_keys_error() {
550 let keys: Vec<_> = (0..2).map(|_| Ed25519PrivateKey::generate()).collect();
551 let result = MultiEd25519Account::new(keys, 5);
552 assert!(result.is_err());
553 }
554
555 #[test]
556 fn test_from_keys_index_out_of_bounds() {
557 let keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
558 let public_keys: Vec<_> = keys.iter().map(Ed25519PrivateKey::public_key).collect();
559
560 let my_keys = vec![(10u8, keys[0].clone())];
562 let result = MultiEd25519Account::from_keys(public_keys, my_keys, 1);
563 assert!(result.is_err());
564 }
565
566 #[test]
567 fn test_from_keys_mismatched_key() {
568 let keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
569 let public_keys: Vec<_> = keys.iter().map(Ed25519PrivateKey::public_key).collect();
570
571 let different_key = Ed25519PrivateKey::generate();
573 let my_keys = vec![(0u8, different_key)];
574 let result = MultiEd25519Account::from_keys(public_keys, my_keys, 1);
575 assert!(result.is_err());
576 }
577
578 #[test]
579 fn test_owned_key_indices() {
580 let all_keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
581 let public_keys: Vec<_> = all_keys.iter().map(Ed25519PrivateKey::public_key).collect();
582
583 let my_keys = vec![(0u8, all_keys[0].clone()), (2u8, all_keys[2].clone())];
584
585 let account = MultiEd25519Account::from_keys(public_keys, my_keys, 2).unwrap();
586 let indices = account.owned_key_indices();
587 assert_eq!(indices, vec![0, 2]);
588 }
589
590 #[test]
591 fn test_sign_with_indices_insufficient() {
592 let keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
593 let account = MultiEd25519Account::new(keys, 2).unwrap();
594
595 let result = account.sign_with_indices(b"test", &[0]);
597 assert!(result.is_err());
598 }
599
600 #[test]
601 fn test_sign_with_indices_missing_key() {
602 let all_keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
603 let public_keys: Vec<_> = all_keys.iter().map(Ed25519PrivateKey::public_key).collect();
604
605 let my_keys = vec![(0u8, all_keys[0].clone())];
607 let account = MultiEd25519Account::from_keys(public_keys, my_keys, 1).unwrap();
608
609 let result = account.sign_with_indices(b"test", &[1]);
611 assert!(result.is_err());
612 }
613
614 #[test]
615 fn test_create_signature_contribution_missing_key() {
616 let all_keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
617 let public_keys: Vec<_> = all_keys.iter().map(Ed25519PrivateKey::public_key).collect();
618
619 let my_keys = vec![(0u8, all_keys[0].clone())];
621 let account = MultiEd25519Account::from_keys(public_keys, my_keys, 1).unwrap();
622
623 let result = account.create_signature_contribution(b"test", 1);
625 assert!(result.is_err());
626 }
627
628 #[test]
629 fn test_account_trait_implementation() {
630 use crate::account::Account;
631
632 let keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
633 let account = MultiEd25519Account::new(keys, 2).unwrap();
634
635 assert!(!account.address().is_zero());
637 assert!(!account.public_key_bytes().is_empty());
638 assert_eq!(
639 account.signature_scheme(),
640 crate::crypto::MULTI_ED25519_SCHEME
641 );
642
643 let sig_bytes: Vec<u8> = Account::sign(&account, b"test").unwrap();
645 assert!(!sig_bytes.is_empty());
646 }
647
648 #[test]
649 fn test_auth_key() {
650 let keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
651 let account = MultiEd25519Account::new(keys, 2).unwrap();
652 let auth_key = account.auth_key();
653 assert_eq!(auth_key.as_bytes().len(), 32);
654 }
655
656 #[test]
657 fn test_display_format() {
658 let keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
659 let account = MultiEd25519Account::new(keys, 2).unwrap();
660 let display = format!("{account}");
661 assert!(display.contains("MultiEd25519Account"));
662 assert!(display.contains("2-of-3"));
663 }
664
665 #[test]
666 fn test_debug_format() {
667 let keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
668 let account = MultiEd25519Account::new(keys, 2).unwrap();
669 let debug = format!("{account:?}");
670 assert!(debug.contains("MultiEd25519Account"));
671 assert!(debug.contains("address"));
672 }
673
674 #[test]
675 fn test_public_key_accessor() {
676 let keys: Vec<_> = (0..3).map(|_| Ed25519PrivateKey::generate()).collect();
677 let account = MultiEd25519Account::new(keys, 2).unwrap();
678 let pk = account.public_key();
679 assert_eq!(pk.num_keys(), 3);
680 assert_eq!(pk.threshold(), 2);
681 }
682}