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    /// Returns the BCS-serialized `AnySignature::Ed25519` bytes for `SingleKey` authenticator.
316    ///
317    /// Format: `0x00 || ULEB128(64) || signature_bytes`
318    fn bcs_signature_bytes(sig: &crate::crypto::Ed25519Signature) -> Vec<u8> {
319        let sig_bytes = sig.to_bytes();
320        let mut out = Vec::with_capacity(1 + 1 + sig_bytes.len());
321        out.push(0x00); // Ed25519 variant
322        out.push(64); // ULEB128(64)
323        out.extend_from_slice(&sig_bytes);
324        out
325    }
326}
327
328impl Account for Ed25519SingleKeyAccount {
329    fn address(&self) -> AccountAddress {
330        self.address
331    }
332
333    fn authentication_key(&self) -> AuthenticationKey {
334        let bcs_bytes = self.bcs_public_key_bytes();
335        let key = derive_authentication_key(&bcs_bytes, SINGLE_KEY_SCHEME);
336        AuthenticationKey::new(key)
337    }
338
339    fn sign(&self, message: &[u8]) -> AptosResult<Vec<u8>> {
340        // Return BCS-serialized `AnySignature::Ed25519` so the wire format consumed
341        // by `AccountAuthenticator::SingleKey` matches what the on-chain
342        // `SingleKeyAuthenticator { signature: AnySignature }` deserializer expects.
343        let sig = self.private_key.sign(message);
344        Ok(Self::bcs_signature_bytes(&sig))
345    }
346
347    fn public_key_bytes(&self) -> Vec<u8> {
348        // Return BCS-serialized AnyPublicKey::Ed25519 format
349        self.bcs_public_key_bytes()
350    }
351
352    fn signature_scheme(&self) -> u8 {
353        SINGLE_KEY_SCHEME
354    }
355}
356
357impl fmt::Debug for Ed25519SingleKeyAccount {
358    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
359        f.debug_struct("Ed25519SingleKeyAccount")
360            .field("address", &self.address)
361            .field("public_key", &self.public_key)
362            .finish_non_exhaustive()
363    }
364}
365
366#[cfg(test)]
367mod tests {
368    use super::*;
369
370    #[test]
371    fn test_generate() {
372        let account = Ed25519Account::generate();
373        assert!(!account.address().is_zero());
374    }
375
376    #[test]
377    #[cfg(feature = "mnemonic")]
378    fn test_from_mnemonic() {
379        // Standard test mnemonic
380        let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
381        let account = Ed25519Account::from_mnemonic(mnemonic, 0).unwrap();
382
383        // Same mnemonic should produce same account
384        let account2 = Ed25519Account::from_mnemonic(mnemonic, 0).unwrap();
385        assert_eq!(account.address(), account2.address());
386
387        // Different index should produce different account
388        let account3 = Ed25519Account::from_mnemonic(mnemonic, 1).unwrap();
389        assert_ne!(account.address(), account3.address());
390    }
391
392    #[test]
393    fn test_sign_and_verify() {
394        let account = Ed25519Account::generate();
395        let message = b"hello world";
396
397        let signature = account.sign_message(message);
398        assert!(account.public_key().verify(message, &signature).is_ok());
399    }
400
401    #[test]
402    #[cfg(feature = "mnemonic")]
403    fn test_generate_with_mnemonic() {
404        let (account, mnemonic) = Ed25519Account::generate_with_mnemonic().unwrap();
405
406        // Should be able to restore from the mnemonic
407        let restored = Ed25519Account::from_mnemonic(&mnemonic, 0).unwrap();
408        assert_eq!(account.address(), restored.address());
409    }
410
411    #[test]
412    fn test_from_private_key() {
413        let original = Ed25519Account::generate();
414        let private_key = original.private_key().clone();
415        let restored = Ed25519Account::from_private_key(private_key);
416        assert_eq!(original.address(), restored.address());
417    }
418
419    #[test]
420    fn test_from_private_key_bytes() {
421        let original = Ed25519Account::generate();
422        let bytes = original.private_key().to_bytes();
423        let restored = Ed25519Account::from_private_key_bytes(&bytes).unwrap();
424        assert_eq!(original.address(), restored.address());
425    }
426
427    #[test]
428    fn test_from_private_key_hex() {
429        let original = Ed25519Account::generate();
430        let hex = original.private_key().to_hex();
431        let restored = Ed25519Account::from_private_key_hex(&hex).unwrap();
432        assert_eq!(original.address(), restored.address());
433    }
434
435    #[test]
436    fn test_authentication_key() {
437        let account = Ed25519Account::generate();
438        let auth_key = account.authentication_key();
439        assert_eq!(auth_key.as_bytes().len(), 32);
440    }
441
442    #[test]
443    fn test_public_key_bytes() {
444        let account = Ed25519Account::generate();
445        let bytes = account.public_key_bytes();
446        assert_eq!(bytes.len(), 32);
447    }
448
449    #[test]
450    fn test_signature_scheme() {
451        let account = Ed25519Account::generate();
452        assert_eq!(account.signature_scheme(), ED25519_SCHEME);
453    }
454
455    #[test]
456    fn test_sign_trait() {
457        let account = Ed25519Account::generate();
458        let message = b"test message";
459        let sig_bytes = account.sign(message).unwrap();
460        assert_eq!(sig_bytes.len(), 64);
461    }
462
463    #[test]
464    fn test_debug_output() {
465        let account = Ed25519Account::generate();
466        let debug = format!("{account:?}");
467        assert!(debug.contains("Ed25519Account"));
468        assert!(debug.contains("address"));
469    }
470
471    #[test]
472    fn test_invalid_private_key_bytes() {
473        let result = Ed25519Account::from_private_key_bytes(&[0u8; 16]);
474        assert!(result.is_err());
475    }
476
477    #[test]
478    fn test_invalid_private_key_hex() {
479        let result = Ed25519Account::from_private_key_hex("invalid");
480        assert!(result.is_err());
481    }
482
483    #[test]
484    #[cfg(feature = "mnemonic")]
485    fn test_invalid_mnemonic() {
486        let result = Ed25519Account::from_mnemonic("invalid mnemonic phrase", 0);
487        assert!(result.is_err());
488    }
489
490    // Ed25519SingleKeyAccount tests
491
492    #[test]
493    fn test_single_key_generate() {
494        let account = Ed25519SingleKeyAccount::generate();
495        assert!(!account.address().is_zero());
496    }
497
498    #[test]
499    fn test_single_key_different_address() {
500        // Same private key should produce different addresses for Ed25519Account vs Ed25519SingleKeyAccount
501        let legacy_account = Ed25519Account::generate();
502        let private_key = legacy_account.private_key().clone();
503
504        let single_key_account = Ed25519SingleKeyAccount::from_private_key(private_key);
505
506        // Addresses should be DIFFERENT because they use different scheme IDs
507        assert_ne!(legacy_account.address(), single_key_account.address());
508    }
509
510    #[test]
511    fn test_single_key_sign_and_verify() {
512        let account = Ed25519SingleKeyAccount::generate();
513        let message = b"hello world";
514
515        let signature = account.sign_message(message);
516        assert!(account.public_key().verify(message, &signature).is_ok());
517    }
518
519    #[test]
520    fn test_single_key_from_private_key() {
521        let original = Ed25519SingleKeyAccount::generate();
522        let private_key = original.private_key().clone();
523        let restored = Ed25519SingleKeyAccount::from_private_key(private_key);
524        assert_eq!(original.address(), restored.address());
525    }
526
527    #[test]
528    fn test_single_key_from_private_key_bytes() {
529        let original = Ed25519SingleKeyAccount::generate();
530        let bytes = original.private_key().to_bytes();
531        let restored = Ed25519SingleKeyAccount::from_private_key_bytes(&bytes).unwrap();
532        assert_eq!(original.address(), restored.address());
533    }
534
535    #[test]
536    fn test_single_key_from_private_key_hex() {
537        let original = Ed25519SingleKeyAccount::generate();
538        let hex = original.private_key().to_hex();
539        let restored = Ed25519SingleKeyAccount::from_private_key_hex(&hex).unwrap();
540        assert_eq!(original.address(), restored.address());
541    }
542
543    #[test]
544    fn test_single_key_authentication_key() {
545        let account = Ed25519SingleKeyAccount::generate();
546        let auth_key = account.authentication_key();
547        assert_eq!(auth_key.as_bytes().len(), 32);
548    }
549
550    #[test]
551    fn test_single_key_public_key_bytes() {
552        let account = Ed25519SingleKeyAccount::generate();
553        let bytes = account.public_key_bytes();
554        // BCS(AnyPublicKey::Ed25519): 1-byte variant (0x00) + ULEB128(32) + 32-byte key = 34 bytes
555        assert_eq!(bytes.len(), 34);
556        assert_eq!(bytes[0], 0x00); // Ed25519 variant
557        assert_eq!(bytes[1], 32); // ULEB128(32)
558    }
559
560    #[test]
561    fn test_single_key_signature_scheme() {
562        let account = Ed25519SingleKeyAccount::generate();
563        assert_eq!(account.signature_scheme(), SINGLE_KEY_SCHEME);
564    }
565
566    #[test]
567    fn test_single_key_sign_trait() {
568        let account = Ed25519SingleKeyAccount::generate();
569        let message = b"test message";
570        let sig_bytes = account.sign(message).unwrap();
571        // sign() returns BCS(AnySignature::Ed25519) = 1 + 1 + 64 = 66 bytes.
572        assert_eq!(sig_bytes.len(), 66);
573        assert_eq!(sig_bytes[0], 0x00, "AnySignature::Ed25519 variant tag");
574        assert_eq!(sig_bytes[1], 64, "ULEB128(64)");
575    }
576
577    #[test]
578    fn test_single_key_debug_output() {
579        let account = Ed25519SingleKeyAccount::generate();
580        let debug = format!("{account:?}");
581        assert!(debug.contains("Ed25519SingleKeyAccount"));
582        assert!(debug.contains("address"));
583    }
584
585    #[test]
586    #[cfg(feature = "mnemonic")]
587    fn test_single_key_from_mnemonic() {
588        let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
589        let account = Ed25519SingleKeyAccount::from_mnemonic(mnemonic, 0).unwrap();
590
591        // Same mnemonic should produce same account
592        let account2 = Ed25519SingleKeyAccount::from_mnemonic(mnemonic, 0).unwrap();
593        assert_eq!(account.address(), account2.address());
594
595        // Different index should produce different account
596        let account3 = Ed25519SingleKeyAccount::from_mnemonic(mnemonic, 1).unwrap();
597        assert_ne!(account.address(), account3.address());
598    }
599
600    #[test]
601    #[cfg(feature = "mnemonic")]
602    fn test_single_key_vs_legacy_mnemonic() {
603        let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
604
605        let legacy = Ed25519Account::from_mnemonic(mnemonic, 0).unwrap();
606        let single_key = Ed25519SingleKeyAccount::from_mnemonic(mnemonic, 0).unwrap();
607
608        // Same mnemonic, same private key, but DIFFERENT addresses
609        assert_eq!(
610            legacy.private_key().to_bytes(),
611            single_key.private_key().to_bytes()
612        );
613        assert_ne!(legacy.address(), single_key.address());
614    }
615}