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