Skip to main content

aptos_sdk/account/
account.rs

1//! Account trait and common types.
2
3use crate::error::AptosResult;
4use crate::types::AccountAddress;
5use serde::{Deserialize, Serialize};
6use std::fmt;
7
8/// An authentication key used to verify account ownership.
9///
10/// The authentication key is derived from the public key and can be
11/// rotated to support key rotation.
12#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13pub struct AuthenticationKey([u8; 32]);
14
15impl AuthenticationKey {
16    /// Creates an authentication key from bytes.
17    pub fn new(bytes: [u8; 32]) -> Self {
18        Self(bytes)
19    }
20
21    /// Creates an authentication key from a byte slice.
22    ///
23    /// # Errors
24    ///
25    /// Returns an error if the byte slice length is not exactly 32 bytes.
26    pub fn from_bytes(bytes: &[u8]) -> AptosResult<Self> {
27        if bytes.len() != 32 {
28            return Err(crate::error::AptosError::InvalidAddress(format!(
29                "authentication key must be 32 bytes, got {}",
30                bytes.len()
31            )));
32        }
33        let mut key = [0u8; 32];
34        key.copy_from_slice(bytes);
35        Ok(Self(key))
36    }
37
38    /// Creates an authentication key from a hex string.
39    ///
40    /// # Errors
41    ///
42    /// This function will return an error if:
43    /// - The hex string is invalid or cannot be decoded
44    /// - The decoded bytes are not exactly 32 bytes long
45    pub fn from_hex(hex_str: &str) -> AptosResult<Self> {
46        let hex_str = hex_str.strip_prefix("0x").unwrap_or(hex_str);
47        let bytes = hex::decode(hex_str)?;
48        Self::from_bytes(&bytes)
49    }
50
51    /// Returns the authentication key as bytes.
52    pub fn as_bytes(&self) -> &[u8; 32] {
53        &self.0
54    }
55
56    /// Returns the authentication key as a byte array.
57    pub fn to_bytes(&self) -> [u8; 32] {
58        self.0
59    }
60
61    /// Returns the authentication key as a hex string.
62    pub fn to_hex(&self) -> String {
63        format!("0x{}", hex::encode(self.0))
64    }
65
66    /// Derives the account address from this authentication key.
67    ///
68    /// For most accounts, the address equals the authentication key.
69    pub fn to_address(&self) -> AccountAddress {
70        AccountAddress::new(self.0)
71    }
72}
73
74impl fmt::Debug for AuthenticationKey {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        write!(f, "AuthenticationKey({})", self.to_hex())
77    }
78}
79
80impl fmt::Display for AuthenticationKey {
81    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82        write!(f, "{}", self.to_hex())
83    }
84}
85
86impl From<[u8; 32]> for AuthenticationKey {
87    fn from(bytes: [u8; 32]) -> Self {
88        Self(bytes)
89    }
90}
91
92impl From<AuthenticationKey> for [u8; 32] {
93    fn from(key: AuthenticationKey) -> Self {
94        key.0
95    }
96}
97
98impl From<AuthenticationKey> for AccountAddress {
99    fn from(key: AuthenticationKey) -> Self {
100        key.to_address()
101    }
102}
103
104/// Trait for account types that can sign transactions.
105///
106/// This trait provides a common interface for different account types
107/// (Ed25519, Secp256k1, multi-sig, keyless, etc.).
108pub trait Account: Send + Sync {
109    /// Returns the account address.
110    fn address(&self) -> AccountAddress;
111
112    /// Returns the authentication key.
113    fn authentication_key(&self) -> AuthenticationKey;
114
115    /// Signs a message and returns the signature bytes.
116    ///
117    /// # Errors
118    ///
119    /// May return an error if signing fails (e.g., insufficient signatures
120    /// for multi-sig accounts).
121    fn sign(&self, message: &[u8]) -> AptosResult<Vec<u8>>;
122
123    /// Returns the public key bytes.
124    fn public_key_bytes(&self) -> Vec<u8>;
125
126    /// Returns the scheme identifier for this account type.
127    fn signature_scheme(&self) -> u8;
128}
129
130/// An enum that can hold any account type.
131///
132/// This is useful when you need to store different account types
133/// in the same collection or pass them around generically.
134#[derive(Debug)]
135#[allow(clippy::large_enum_variant)] // Keyless account is intentionally large; boxing would complicate API
136pub enum AnyAccount {
137    /// An Ed25519 account.
138    #[cfg(feature = "ed25519")]
139    Ed25519(super::Ed25519Account),
140    /// A multi-Ed25519 account.
141    #[cfg(feature = "ed25519")]
142    MultiEd25519(super::MultiEd25519Account),
143    /// A multi-key account (mixed signature types).
144    MultiKey(super::MultiKeyAccount),
145    /// A Keyless account.
146    #[cfg(feature = "keyless")]
147    Keyless(super::KeylessAccount),
148    /// A Secp256k1 account.
149    #[cfg(feature = "secp256k1")]
150    Secp256k1(super::Secp256k1Account),
151}
152
153impl Account for AnyAccount {
154    fn address(&self) -> AccountAddress {
155        match self {
156            #[cfg(feature = "ed25519")]
157            AnyAccount::Ed25519(account) => account.address(),
158            #[cfg(feature = "ed25519")]
159            AnyAccount::MultiEd25519(account) => account.address(),
160            AnyAccount::MultiKey(account) => account.address(),
161            #[cfg(feature = "keyless")]
162            AnyAccount::Keyless(account) => account.address(),
163            #[cfg(feature = "secp256k1")]
164            AnyAccount::Secp256k1(account) => account.address(),
165        }
166    }
167
168    fn authentication_key(&self) -> AuthenticationKey {
169        match self {
170            #[cfg(feature = "ed25519")]
171            AnyAccount::Ed25519(account) => account.authentication_key(),
172            #[cfg(feature = "ed25519")]
173            AnyAccount::MultiEd25519(account) => account.authentication_key(),
174            AnyAccount::MultiKey(account) => account.authentication_key(),
175            #[cfg(feature = "keyless")]
176            AnyAccount::Keyless(account) => account.authentication_key(),
177            #[cfg(feature = "secp256k1")]
178            AnyAccount::Secp256k1(account) => account.authentication_key(),
179        }
180    }
181
182    fn sign(&self, message: &[u8]) -> AptosResult<Vec<u8>> {
183        match self {
184            #[cfg(feature = "ed25519")]
185            AnyAccount::Ed25519(account) => Account::sign(account, message),
186            #[cfg(feature = "ed25519")]
187            AnyAccount::MultiEd25519(account) => Account::sign(account, message),
188            AnyAccount::MultiKey(account) => Account::sign(account, message),
189            #[cfg(feature = "keyless")]
190            AnyAccount::Keyless(account) => Account::sign(account, message),
191            #[cfg(feature = "secp256k1")]
192            AnyAccount::Secp256k1(account) => Account::sign(account, message),
193        }
194    }
195
196    fn public_key_bytes(&self) -> Vec<u8> {
197        match self {
198            #[cfg(feature = "ed25519")]
199            AnyAccount::Ed25519(account) => account.public_key_bytes(),
200            #[cfg(feature = "ed25519")]
201            AnyAccount::MultiEd25519(account) => account.public_key_bytes(),
202            AnyAccount::MultiKey(account) => account.public_key_bytes(),
203            #[cfg(feature = "keyless")]
204            AnyAccount::Keyless(account) => account.public_key_bytes(),
205            #[cfg(feature = "secp256k1")]
206            AnyAccount::Secp256k1(account) => account.public_key_bytes(),
207        }
208    }
209
210    fn signature_scheme(&self) -> u8 {
211        match self {
212            #[cfg(feature = "ed25519")]
213            AnyAccount::Ed25519(account) => account.signature_scheme(),
214            #[cfg(feature = "ed25519")]
215            AnyAccount::MultiEd25519(account) => account.signature_scheme(),
216            AnyAccount::MultiKey(account) => account.signature_scheme(),
217            #[cfg(feature = "keyless")]
218            AnyAccount::Keyless(account) => account.signature_scheme(),
219            #[cfg(feature = "secp256k1")]
220            AnyAccount::Secp256k1(account) => account.signature_scheme(),
221        }
222    }
223}
224
225#[cfg(feature = "ed25519")]
226impl From<super::Ed25519Account> for AnyAccount {
227    fn from(account: super::Ed25519Account) -> Self {
228        AnyAccount::Ed25519(account)
229    }
230}
231
232#[cfg(feature = "ed25519")]
233impl From<super::MultiEd25519Account> for AnyAccount {
234    fn from(account: super::MultiEd25519Account) -> Self {
235        AnyAccount::MultiEd25519(account)
236    }
237}
238
239#[cfg(feature = "keyless")]
240impl From<super::KeylessAccount> for AnyAccount {
241    fn from(account: super::KeylessAccount) -> Self {
242        AnyAccount::Keyless(account)
243    }
244}
245
246#[cfg(feature = "secp256k1")]
247impl From<super::Secp256k1Account> for AnyAccount {
248    fn from(account: super::Secp256k1Account) -> Self {
249        AnyAccount::Secp256k1(account)
250    }
251}
252
253impl From<super::MultiKeyAccount> for AnyAccount {
254    fn from(account: super::MultiKeyAccount) -> Self {
255        AnyAccount::MultiKey(account)
256    }
257}
258
259#[cfg(test)]
260mod tests {
261    use super::*;
262
263    #[test]
264    fn test_authentication_key() {
265        let key = AuthenticationKey::new([1u8; 32]);
266        assert_eq!(key.as_bytes(), &[1u8; 32]);
267
268        let hex = key.to_hex();
269        let restored = AuthenticationKey::from_hex(&hex).unwrap();
270        assert_eq!(key, restored);
271    }
272
273    #[test]
274    fn test_auth_key_to_address() {
275        let key = AuthenticationKey::new([42u8; 32]);
276        let address = key.to_address();
277        assert_eq!(address.as_bytes(), &[42u8; 32]);
278    }
279
280    #[test]
281    fn test_auth_key_from_bytes() {
282        let bytes = [5u8; 32];
283        let key = AuthenticationKey::from_bytes(&bytes).unwrap();
284        assert_eq!(key.to_bytes(), bytes);
285    }
286
287    #[test]
288    fn test_auth_key_from_bytes_invalid_length() {
289        let bytes = [5u8; 16];
290        let result = AuthenticationKey::from_bytes(&bytes);
291        assert!(result.is_err());
292    }
293
294    #[test]
295    fn test_auth_key_from_hex_with_prefix() {
296        let key = AuthenticationKey::new([0xab; 32]);
297        let hex = key.to_hex();
298        assert!(hex.starts_with("0x"));
299        let restored = AuthenticationKey::from_hex(&hex).unwrap();
300        assert_eq!(key, restored);
301    }
302
303    #[test]
304    fn test_auth_key_from_hex_without_prefix() {
305        let key = AuthenticationKey::new([0xcd; 32]);
306        let hex = key.to_hex();
307        let hex_without_prefix = hex.trim_start_matches("0x");
308        let restored = AuthenticationKey::from_hex(hex_without_prefix).unwrap();
309        assert_eq!(key, restored);
310    }
311
312    #[test]
313    fn test_auth_key_display() {
314        let key = AuthenticationKey::new([0xff; 32]);
315        let display = format!("{key}");
316        assert!(display.starts_with("0x"));
317        assert_eq!(display.len(), 66); // 0x + 64 hex chars
318    }
319
320    #[test]
321    fn test_auth_key_debug() {
322        let key = AuthenticationKey::new([0xaa; 32]);
323        let debug = format!("{key:?}");
324        assert!(debug.contains("AuthenticationKey"));
325    }
326
327    #[test]
328    fn test_auth_key_from_array() {
329        let bytes = [7u8; 32];
330        let key: AuthenticationKey = bytes.into();
331        assert_eq!(key.to_bytes(), bytes);
332    }
333
334    #[test]
335    fn test_auth_key_to_array() {
336        let key = AuthenticationKey::new([8u8; 32]);
337        let bytes: [u8; 32] = key.into();
338        assert_eq!(bytes, [8u8; 32]);
339    }
340
341    #[test]
342    fn test_auth_key_to_account_address() {
343        let key = AuthenticationKey::new([9u8; 32]);
344        let address: AccountAddress = key.into();
345        assert_eq!(address.as_bytes(), &[9u8; 32]);
346    }
347
348    #[cfg(feature = "ed25519")]
349    #[test]
350    fn test_any_account_from_ed25519() {
351        let ed25519 = super::super::Ed25519Account::generate();
352        let any_account: AnyAccount = ed25519.into();
353        if let AnyAccount::Ed25519(account) = any_account {
354            assert!(!account.address().is_zero());
355        } else {
356            panic!("Expected Ed25519 account");
357        }
358    }
359
360    #[cfg(feature = "ed25519")]
361    #[test]
362    fn test_any_account_ed25519_trait_methods() {
363        let ed25519 = super::super::Ed25519Account::generate();
364        let address = ed25519.address();
365        let auth_key = ed25519.authentication_key();
366        let any_account: AnyAccount = ed25519.into();
367
368        assert_eq!(any_account.address(), address);
369        assert_eq!(any_account.authentication_key(), auth_key);
370        assert!(!any_account.public_key_bytes().is_empty());
371
372        let sig = any_account.sign(b"test message").unwrap();
373        assert!(!sig.is_empty());
374    }
375
376    #[cfg(feature = "secp256k1")]
377    #[test]
378    fn test_any_account_from_secp256k1() {
379        let secp = super::super::Secp256k1Account::generate();
380        let any_account: AnyAccount = secp.into();
381        if let AnyAccount::Secp256k1(account) = any_account {
382            assert!(!account.address().is_zero());
383        } else {
384            panic!("Expected Secp256k1 account");
385        }
386    }
387
388    #[cfg(feature = "secp256k1")]
389    #[test]
390    fn test_any_account_secp256k1_trait_methods() {
391        let secp = super::super::Secp256k1Account::generate();
392        let address = secp.address();
393        let auth_key = secp.authentication_key();
394        let any_account: AnyAccount = secp.into();
395
396        assert_eq!(any_account.address(), address);
397        assert_eq!(any_account.authentication_key(), auth_key);
398        assert!(!any_account.public_key_bytes().is_empty());
399
400        let sig = any_account.sign(b"test message").unwrap();
401        assert!(!sig.is_empty());
402    }
403
404    #[cfg(feature = "ed25519")]
405    #[test]
406    fn test_any_account_from_multi_ed25519() {
407        use crate::crypto::Ed25519PrivateKey;
408
409        let keys: Vec<_> = (0..2).map(|_| Ed25519PrivateKey::generate()).collect();
410        let account = super::super::MultiEd25519Account::new(keys, 2).unwrap();
411        let any_account: AnyAccount = account.into();
412
413        if let AnyAccount::MultiEd25519(_) = any_account {
414            // Success
415        } else {
416            panic!("Expected MultiEd25519 account");
417        }
418    }
419
420    #[cfg(feature = "ed25519")]
421    #[test]
422    fn test_any_account_multi_ed25519_trait_methods() {
423        use crate::crypto::Ed25519PrivateKey;
424
425        let keys: Vec<_> = (0..2).map(|_| Ed25519PrivateKey::generate()).collect();
426        let account = super::super::MultiEd25519Account::new(keys, 2).unwrap();
427        let address = account.address();
428        let auth_key = account.authentication_key();
429        let any_account: AnyAccount = account.into();
430
431        assert_eq!(any_account.address(), address);
432        assert_eq!(any_account.authentication_key(), auth_key);
433        assert!(!any_account.public_key_bytes().is_empty());
434        assert!(any_account.signature_scheme() > 0);
435
436        let sig = any_account.sign(b"test").unwrap();
437        assert!(!sig.is_empty());
438    }
439
440    #[cfg(feature = "ed25519")]
441    #[test]
442    fn test_any_account_from_multi_key() {
443        use crate::account::AnyPrivateKey;
444        use crate::crypto::Ed25519PrivateKey;
445
446        let keys: Vec<_> = (0..2)
447            .map(|_| AnyPrivateKey::ed25519(Ed25519PrivateKey::generate()))
448            .collect();
449        let account = super::super::MultiKeyAccount::new(keys, 2).unwrap();
450        let any_account: AnyAccount = account.into();
451
452        if let AnyAccount::MultiKey(_) = any_account {
453            // Success
454        } else {
455            panic!("Expected MultiKey account");
456        }
457    }
458
459    #[cfg(feature = "ed25519")]
460    #[test]
461    fn test_any_account_multi_key_trait_methods() {
462        use crate::account::AnyPrivateKey;
463        use crate::crypto::Ed25519PrivateKey;
464
465        let keys: Vec<_> = (0..2)
466            .map(|_| AnyPrivateKey::ed25519(Ed25519PrivateKey::generate()))
467            .collect();
468        let account = super::super::MultiKeyAccount::new(keys, 2).unwrap();
469        let address = account.address();
470        let auth_key = account.authentication_key();
471        let any_account: AnyAccount = account.into();
472
473        assert_eq!(any_account.address(), address);
474        assert_eq!(any_account.authentication_key(), auth_key);
475        assert!(!any_account.public_key_bytes().is_empty());
476
477        let sig = any_account.sign(b"test").unwrap();
478        assert!(!sig.is_empty());
479    }
480
481    #[test]
482    fn test_auth_key_json_serialization() {
483        let key = AuthenticationKey::new([0xab; 32]);
484        let json = serde_json::to_string(&key).unwrap();
485        let restored: AuthenticationKey = serde_json::from_str(&json).unwrap();
486        assert_eq!(key, restored);
487    }
488
489    #[test]
490    fn test_auth_key_hash() {
491        use std::collections::HashSet;
492        let key1 = AuthenticationKey::new([1u8; 32]);
493        let key2 = AuthenticationKey::new([2u8; 32]);
494
495        let mut set = HashSet::new();
496        set.insert(key1);
497        set.insert(key2);
498        assert_eq!(set.len(), 2);
499        assert!(set.contains(&key1));
500    }
501
502    #[test]
503    fn test_auth_key_clone() {
504        let key = AuthenticationKey::new([42u8; 32]);
505        let cloned = key;
506        assert_eq!(key, cloned);
507    }
508
509    #[test]
510    fn test_any_account_debug() {
511        #[cfg(feature = "ed25519")]
512        {
513            let ed25519 = super::super::Ed25519Account::generate();
514            let any_account: AnyAccount = ed25519.into();
515            let debug = format!("{any_account:?}");
516            assert!(debug.contains("Ed25519"));
517        }
518    }
519}