Skip to main content

aptos_sdk/account/
ed25519.rs

1//! Ed25519 account implementations.
2//!
3//! This module provides two Ed25519 account types:
4//!
5//! - [`Ed25519Account`]: Uses the legacy Ed25519 authenticator (scheme 0).
6//!   This is the most common account type and is backwards compatible.
7//!
8//! - [`Ed25519SingleKeyAccount`]: Uses the modern `SingleKey` authenticator (scheme 2).
9//!   This format is more flexible and recommended for new implementations.
10//!
11//! **Note**: The two account types produce DIFFERENT addresses for the same private key
12//! because they use different authentication key derivation schemes.
13
14#[cfg(feature = "mnemonic")]
15use crate::account::Mnemonic;
16use crate::account::account::{Account, AuthenticationKey};
17use crate::crypto::{
18    ED25519_SCHEME, Ed25519PrivateKey, Ed25519PublicKey, SINGLE_KEY_SCHEME,
19    derive_authentication_key,
20};
21use crate::error::AptosResult;
22use crate::types::AccountAddress;
23use std::fmt;
24
25/// An Ed25519 account for signing transactions.
26///
27/// This is the most common account type on Aptos.
28///
29/// # Example
30///
31/// ```rust
32/// use aptos_sdk::account::Ed25519Account;
33///
34/// // Generate a new random account
35/// let account = Ed25519Account::generate();
36/// println!("Address: {}", account.address());
37/// ```
38#[derive(Clone)]
39pub struct Ed25519Account {
40    private_key: Ed25519PrivateKey,
41    public_key: Ed25519PublicKey,
42    address: AccountAddress,
43}
44
45impl Ed25519Account {
46    /// Generates a new random Ed25519 account.
47    pub fn generate() -> Self {
48        let private_key = Ed25519PrivateKey::generate();
49        Self::from_private_key(private_key)
50    }
51
52    /// Creates an account from a private key.
53    pub fn from_private_key(private_key: Ed25519PrivateKey) -> Self {
54        let public_key = private_key.public_key();
55        let address = public_key.to_address();
56        Self {
57            private_key,
58            public_key,
59            address,
60        }
61    }
62
63    /// Creates an account from private key bytes.
64    ///
65    /// # Errors
66    ///
67    /// Returns an error if the bytes are not a valid Ed25519 private key (must be exactly 32 bytes).
68    pub fn from_private_key_bytes(bytes: &[u8]) -> AptosResult<Self> {
69        let private_key = Ed25519PrivateKey::from_bytes(bytes)?;
70        Ok(Self::from_private_key(private_key))
71    }
72
73    /// Creates an account from a private key hex string.
74    ///
75    /// # Errors
76    ///
77    /// This function will return an error if:
78    /// - The hex string is invalid or cannot be decoded
79    /// - The decoded bytes are not a valid Ed25519 private key
80    pub fn from_private_key_hex(hex_str: &str) -> AptosResult<Self> {
81        let private_key = Ed25519PrivateKey::from_hex(hex_str)?;
82        Ok(Self::from_private_key(private_key))
83    }
84
85    /// Creates an account from a BIP-39 mnemonic phrase.
86    ///
87    /// Uses the standard Aptos derivation path: `m/44'/637'/0'/0'/index'`
88    ///
89    /// # Arguments
90    ///
91    /// * `mnemonic` - A BIP-39 mnemonic phrase (12, 15, 18, 21, or 24 words)
92    /// * `index` - The account index in the derivation path
93    ///
94    /// # Example
95    ///
96    /// ```rust,ignore
97    /// use aptos_sdk::account::Ed25519Account;
98    ///
99    /// let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
100    /// let account = Ed25519Account::from_mnemonic(mnemonic, 0).unwrap();
101    /// ```
102    ///
103    /// # Errors
104    ///
105    /// Returns an error if the mnemonic phrase is invalid or if key derivation fails.
106    #[cfg(feature = "mnemonic")]
107    pub fn from_mnemonic(mnemonic: &str, index: u32) -> AptosResult<Self> {
108        let mnemonic = Mnemonic::from_phrase(mnemonic)?;
109        let private_key = mnemonic.derive_ed25519_key(index)?;
110        Ok(Self::from_private_key(private_key))
111    }
112
113    /// Generates a new account with a random mnemonic.
114    ///
115    /// Returns both the account and the mnemonic phrase (for backup).
116    ///
117    /// # Errors
118    ///
119    /// Returns an error if mnemonic generation or key derivation fails.
120    #[cfg(feature = "mnemonic")]
121    pub fn generate_with_mnemonic() -> AptosResult<(Self, String)> {
122        let mnemonic = Mnemonic::generate(24)?;
123        let phrase = mnemonic.phrase().to_string();
124        let private_key = mnemonic.derive_ed25519_key(0)?;
125        let account = Self::from_private_key(private_key);
126        Ok((account, phrase))
127    }
128
129    /// Returns the account address.
130    pub fn address(&self) -> AccountAddress {
131        self.address
132    }
133
134    /// Returns the public key.
135    pub fn public_key(&self) -> &Ed25519PublicKey {
136        &self.public_key
137    }
138
139    /// Returns a reference to the private key.
140    ///
141    /// **Warning**: Handle with care to avoid leaking sensitive key material.
142    pub fn private_key(&self) -> &Ed25519PrivateKey {
143        &self.private_key
144    }
145
146    /// Signs a message and returns the Ed25519 signature.
147    pub fn sign_message(&self, message: &[u8]) -> crate::crypto::Ed25519Signature {
148        self.private_key.sign(message)
149    }
150}
151
152impl Account for Ed25519Account {
153    fn address(&self) -> AccountAddress {
154        self.address
155    }
156
157    fn authentication_key(&self) -> AuthenticationKey {
158        AuthenticationKey::new(self.public_key.to_authentication_key())
159    }
160
161    fn sign(&self, message: &[u8]) -> AptosResult<Vec<u8>> {
162        Ok(self.private_key.sign(message).to_bytes().to_vec())
163    }
164
165    fn public_key_bytes(&self) -> Vec<u8> {
166        self.public_key.to_bytes().to_vec()
167    }
168
169    fn signature_scheme(&self) -> u8 {
170        ED25519_SCHEME
171    }
172}
173
174impl fmt::Debug for Ed25519Account {
175    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176        f.debug_struct("Ed25519Account")
177            .field("address", &self.address)
178            .field("public_key", &self.public_key)
179            .finish_non_exhaustive()
180    }
181}
182
183/// An Ed25519 account using the modern `SingleKey` authenticator format.
184///
185/// This account type uses the `SingleSender` > `SingleKey` > `AnyPublicKey::Ed25519`
186/// authenticator path, which is the modern unified format recommended for new
187/// implementations.
188///
189/// **Note**: This produces a DIFFERENT address than [`Ed25519Account`] for the
190/// same private key because it uses scheme ID 2 instead of 0.
191///
192/// # Authentication Key Derivation
193///
194/// The authentication key is derived as:
195/// ```text
196/// auth_key = SHA3-256(BCS(AnyPublicKey::Ed25519) || 0x02)
197/// ```
198///
199/// Where `BCS(AnyPublicKey::Ed25519) = 0x00 || ULEB128(32) || public_key_bytes`
200///
201/// # Example
202///
203/// ```rust
204/// use aptos_sdk::account::Ed25519SingleKeyAccount;
205///
206/// // Generate a new random account
207/// let account = Ed25519SingleKeyAccount::generate();
208/// println!("Address: {}", account.address());
209/// ```
210#[derive(Clone)]
211pub struct Ed25519SingleKeyAccount {
212    private_key: Ed25519PrivateKey,
213    public_key: Ed25519PublicKey,
214    address: AccountAddress,
215}
216
217impl Ed25519SingleKeyAccount {
218    /// Generates a new random Ed25519 `SingleKey` account.
219    pub fn generate() -> Self {
220        let private_key = Ed25519PrivateKey::generate();
221        Self::from_private_key(private_key)
222    }
223
224    /// Creates an account from a private key.
225    pub fn from_private_key(private_key: Ed25519PrivateKey) -> Self {
226        let public_key = private_key.public_key();
227        let address = Self::derive_address(&public_key);
228        Self {
229            private_key,
230            public_key,
231            address,
232        }
233    }
234
235    /// Creates an account from private key bytes.
236    ///
237    /// # Errors
238    ///
239    /// Returns an error if the bytes are not a valid Ed25519 private key (must be exactly 32 bytes).
240    pub fn from_private_key_bytes(bytes: &[u8]) -> AptosResult<Self> {
241        let private_key = Ed25519PrivateKey::from_bytes(bytes)?;
242        Ok(Self::from_private_key(private_key))
243    }
244
245    /// Creates an account from a private key hex string.
246    ///
247    /// # Errors
248    ///
249    /// This function will return an error if:
250    /// - The hex string is invalid or cannot be decoded
251    /// - The decoded bytes are not a valid Ed25519 private key
252    pub fn from_private_key_hex(hex_str: &str) -> AptosResult<Self> {
253        let private_key = Ed25519PrivateKey::from_hex(hex_str)?;
254        Ok(Self::from_private_key(private_key))
255    }
256
257    /// Creates an account from a BIP-39 mnemonic phrase.
258    ///
259    /// Uses the standard Aptos derivation path: `m/44'/637'/0'/0'/index'`
260    ///
261    /// # Errors
262    ///
263    /// Returns an error if the mnemonic phrase is invalid or if key derivation fails.
264    #[cfg(feature = "mnemonic")]
265    pub fn from_mnemonic(mnemonic: &str, index: u32) -> AptosResult<Self> {
266        let mnemonic = Mnemonic::from_phrase(mnemonic)?;
267        let private_key = mnemonic.derive_ed25519_key(index)?;
268        Ok(Self::from_private_key(private_key))
269    }
270
271    /// Returns the account address.
272    pub fn address(&self) -> AccountAddress {
273        self.address
274    }
275
276    /// Returns the public key.
277    pub fn public_key(&self) -> &Ed25519PublicKey {
278        &self.public_key
279    }
280
281    /// Returns a reference to the private key.
282    pub fn private_key(&self) -> &Ed25519PrivateKey {
283        &self.private_key
284    }
285
286    /// Signs a message and returns the Ed25519 signature.
287    pub fn sign_message(&self, message: &[u8]) -> crate::crypto::Ed25519Signature {
288        self.private_key.sign(message)
289    }
290
291    /// Derives the address for an Ed25519 public key using `SingleKey` scheme.
292    fn derive_address(public_key: &Ed25519PublicKey) -> AccountAddress {
293        // BCS format: variant_byte || ULEB128(length) || public_key_bytes
294        let pk_bytes = public_key.to_bytes();
295        let mut bcs_bytes = Vec::with_capacity(1 + 1 + pk_bytes.len());
296        bcs_bytes.push(0x00); // Ed25519 variant
297        bcs_bytes.push(32); // ULEB128(32) = 32 (since 32 < 128)
298        bcs_bytes.extend_from_slice(&pk_bytes);
299        let auth_key = derive_authentication_key(&bcs_bytes, SINGLE_KEY_SCHEME);
300        AccountAddress::new(auth_key)
301    }
302
303    /// Returns the BCS-serialized public key bytes for `SingleKey` authenticator.
304    ///
305    /// Format: `0x00 || ULEB128(32) || public_key_bytes`
306    fn bcs_public_key_bytes(&self) -> Vec<u8> {
307        let pk_bytes = self.public_key.to_bytes();
308        let mut bcs_bytes = Vec::with_capacity(1 + 1 + pk_bytes.len());
309        bcs_bytes.push(0x00); // Ed25519 variant
310        bcs_bytes.push(32); // ULEB128(32) = 32
311        bcs_bytes.extend_from_slice(&pk_bytes);
312        bcs_bytes
313    }
314}
315
316impl Account for Ed25519SingleKeyAccount {
317    fn address(&self) -> AccountAddress {
318        self.address
319    }
320
321    fn authentication_key(&self) -> AuthenticationKey {
322        let bcs_bytes = self.bcs_public_key_bytes();
323        let key = derive_authentication_key(&bcs_bytes, SINGLE_KEY_SCHEME);
324        AuthenticationKey::new(key)
325    }
326
327    fn sign(&self, message: &[u8]) -> AptosResult<Vec<u8>> {
328        Ok(self.private_key.sign(message).to_bytes().to_vec())
329    }
330
331    fn public_key_bytes(&self) -> Vec<u8> {
332        // Return BCS-serialized AnyPublicKey::Ed25519 format
333        self.bcs_public_key_bytes()
334    }
335
336    fn signature_scheme(&self) -> u8 {
337        SINGLE_KEY_SCHEME
338    }
339}
340
341impl fmt::Debug for Ed25519SingleKeyAccount {
342    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343        f.debug_struct("Ed25519SingleKeyAccount")
344            .field("address", &self.address)
345            .field("public_key", &self.public_key)
346            .finish_non_exhaustive()
347    }
348}
349
350#[cfg(test)]
351mod tests {
352    use super::*;
353
354    #[test]
355    fn test_generate() {
356        let account = Ed25519Account::generate();
357        assert!(!account.address().is_zero());
358    }
359
360    #[test]
361    #[cfg(feature = "mnemonic")]
362    fn test_from_mnemonic() {
363        // Standard test mnemonic
364        let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
365        let account = Ed25519Account::from_mnemonic(mnemonic, 0).unwrap();
366
367        // Same mnemonic should produce same account
368        let account2 = Ed25519Account::from_mnemonic(mnemonic, 0).unwrap();
369        assert_eq!(account.address(), account2.address());
370
371        // Different index should produce different account
372        let account3 = Ed25519Account::from_mnemonic(mnemonic, 1).unwrap();
373        assert_ne!(account.address(), account3.address());
374    }
375
376    #[test]
377    fn test_sign_and_verify() {
378        let account = Ed25519Account::generate();
379        let message = b"hello world";
380
381        let signature = account.sign_message(message);
382        assert!(account.public_key().verify(message, &signature).is_ok());
383    }
384
385    #[test]
386    #[cfg(feature = "mnemonic")]
387    fn test_generate_with_mnemonic() {
388        let (account, mnemonic) = Ed25519Account::generate_with_mnemonic().unwrap();
389
390        // Should be able to restore from the mnemonic
391        let restored = Ed25519Account::from_mnemonic(&mnemonic, 0).unwrap();
392        assert_eq!(account.address(), restored.address());
393    }
394
395    #[test]
396    fn test_from_private_key() {
397        let original = Ed25519Account::generate();
398        let private_key = original.private_key().clone();
399        let restored = Ed25519Account::from_private_key(private_key);
400        assert_eq!(original.address(), restored.address());
401    }
402
403    #[test]
404    fn test_from_private_key_bytes() {
405        let original = Ed25519Account::generate();
406        let bytes = original.private_key().to_bytes();
407        let restored = Ed25519Account::from_private_key_bytes(&bytes).unwrap();
408        assert_eq!(original.address(), restored.address());
409    }
410
411    #[test]
412    fn test_from_private_key_hex() {
413        let original = Ed25519Account::generate();
414        let hex = original.private_key().to_hex();
415        let restored = Ed25519Account::from_private_key_hex(&hex).unwrap();
416        assert_eq!(original.address(), restored.address());
417    }
418
419    #[test]
420    fn test_authentication_key() {
421        let account = Ed25519Account::generate();
422        let auth_key = account.authentication_key();
423        assert_eq!(auth_key.as_bytes().len(), 32);
424    }
425
426    #[test]
427    fn test_public_key_bytes() {
428        let account = Ed25519Account::generate();
429        let bytes = account.public_key_bytes();
430        assert_eq!(bytes.len(), 32);
431    }
432
433    #[test]
434    fn test_signature_scheme() {
435        let account = Ed25519Account::generate();
436        assert_eq!(account.signature_scheme(), ED25519_SCHEME);
437    }
438
439    #[test]
440    fn test_sign_trait() {
441        let account = Ed25519Account::generate();
442        let message = b"test message";
443        let sig_bytes = account.sign(message).unwrap();
444        assert_eq!(sig_bytes.len(), 64);
445    }
446
447    #[test]
448    fn test_debug_output() {
449        let account = Ed25519Account::generate();
450        let debug = format!("{account:?}");
451        assert!(debug.contains("Ed25519Account"));
452        assert!(debug.contains("address"));
453    }
454
455    #[test]
456    fn test_invalid_private_key_bytes() {
457        let result = Ed25519Account::from_private_key_bytes(&[0u8; 16]);
458        assert!(result.is_err());
459    }
460
461    #[test]
462    fn test_invalid_private_key_hex() {
463        let result = Ed25519Account::from_private_key_hex("invalid");
464        assert!(result.is_err());
465    }
466
467    #[test]
468    #[cfg(feature = "mnemonic")]
469    fn test_invalid_mnemonic() {
470        let result = Ed25519Account::from_mnemonic("invalid mnemonic phrase", 0);
471        assert!(result.is_err());
472    }
473
474    // Ed25519SingleKeyAccount tests
475
476    #[test]
477    fn test_single_key_generate() {
478        let account = Ed25519SingleKeyAccount::generate();
479        assert!(!account.address().is_zero());
480    }
481
482    #[test]
483    fn test_single_key_different_address() {
484        // Same private key should produce different addresses for Ed25519Account vs Ed25519SingleKeyAccount
485        let legacy_account = Ed25519Account::generate();
486        let private_key = legacy_account.private_key().clone();
487
488        let single_key_account = Ed25519SingleKeyAccount::from_private_key(private_key);
489
490        // Addresses should be DIFFERENT because they use different scheme IDs
491        assert_ne!(legacy_account.address(), single_key_account.address());
492    }
493
494    #[test]
495    fn test_single_key_sign_and_verify() {
496        let account = Ed25519SingleKeyAccount::generate();
497        let message = b"hello world";
498
499        let signature = account.sign_message(message);
500        assert!(account.public_key().verify(message, &signature).is_ok());
501    }
502
503    #[test]
504    fn test_single_key_from_private_key() {
505        let original = Ed25519SingleKeyAccount::generate();
506        let private_key = original.private_key().clone();
507        let restored = Ed25519SingleKeyAccount::from_private_key(private_key);
508        assert_eq!(original.address(), restored.address());
509    }
510
511    #[test]
512    fn test_single_key_from_private_key_bytes() {
513        let original = Ed25519SingleKeyAccount::generate();
514        let bytes = original.private_key().to_bytes();
515        let restored = Ed25519SingleKeyAccount::from_private_key_bytes(&bytes).unwrap();
516        assert_eq!(original.address(), restored.address());
517    }
518
519    #[test]
520    fn test_single_key_from_private_key_hex() {
521        let original = Ed25519SingleKeyAccount::generate();
522        let hex = original.private_key().to_hex();
523        let restored = Ed25519SingleKeyAccount::from_private_key_hex(&hex).unwrap();
524        assert_eq!(original.address(), restored.address());
525    }
526
527    #[test]
528    fn test_single_key_authentication_key() {
529        let account = Ed25519SingleKeyAccount::generate();
530        let auth_key = account.authentication_key();
531        assert_eq!(auth_key.as_bytes().len(), 32);
532    }
533
534    #[test]
535    fn test_single_key_public_key_bytes() {
536        let account = Ed25519SingleKeyAccount::generate();
537        let bytes = account.public_key_bytes();
538        // BCS format: variant (1) + length (1) + pubkey (32) = 34 bytes
539        assert_eq!(bytes.len(), 34);
540        assert_eq!(bytes[0], 0x00); // Ed25519 variant
541        assert_eq!(bytes[1], 32); // ULEB128(32)
542    }
543
544    #[test]
545    fn test_single_key_signature_scheme() {
546        let account = Ed25519SingleKeyAccount::generate();
547        assert_eq!(account.signature_scheme(), SINGLE_KEY_SCHEME);
548    }
549
550    #[test]
551    fn test_single_key_sign_trait() {
552        let account = Ed25519SingleKeyAccount::generate();
553        let message = b"test message";
554        let sig_bytes = account.sign(message).unwrap();
555        assert_eq!(sig_bytes.len(), 64);
556    }
557
558    #[test]
559    fn test_single_key_debug_output() {
560        let account = Ed25519SingleKeyAccount::generate();
561        let debug = format!("{account:?}");
562        assert!(debug.contains("Ed25519SingleKeyAccount"));
563        assert!(debug.contains("address"));
564    }
565
566    #[test]
567    #[cfg(feature = "mnemonic")]
568    fn test_single_key_from_mnemonic() {
569        let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
570        let account = Ed25519SingleKeyAccount::from_mnemonic(mnemonic, 0).unwrap();
571
572        // Same mnemonic should produce same account
573        let account2 = Ed25519SingleKeyAccount::from_mnemonic(mnemonic, 0).unwrap();
574        assert_eq!(account.address(), account2.address());
575
576        // Different index should produce different account
577        let account3 = Ed25519SingleKeyAccount::from_mnemonic(mnemonic, 1).unwrap();
578        assert_ne!(account.address(), account3.address());
579    }
580
581    #[test]
582    #[cfg(feature = "mnemonic")]
583    fn test_single_key_vs_legacy_mnemonic() {
584        let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
585
586        let legacy = Ed25519Account::from_mnemonic(mnemonic, 0).unwrap();
587        let single_key = Ed25519SingleKeyAccount::from_mnemonic(mnemonic, 0).unwrap();
588
589        // Same mnemonic, same private key, but DIFFERENT addresses
590        assert_eq!(
591            legacy.private_key().to_bytes(),
592            single_key.private_key().to_bytes()
593        );
594        assert_ne!(legacy.address(), single_key.address());
595    }
596}