Skip to main content

aptos_sdk/account/
secp256k1.rs

1//! Secp256k1 account implementation.
2
3use crate::account::account::{Account, AuthenticationKey};
4use crate::crypto::{
5    SINGLE_KEY_SCHEME, Secp256k1PrivateKey, Secp256k1PublicKey, derive_authentication_key,
6};
7use crate::error::AptosResult;
8use crate::types::AccountAddress;
9use std::fmt;
10
11/// A Secp256k1 ECDSA account for signing transactions.
12///
13/// This account type uses the same elliptic curve as Bitcoin and Ethereum.
14///
15/// # Example
16///
17/// ```rust
18/// use aptos_sdk::account::Secp256k1Account;
19///
20/// let account = Secp256k1Account::generate();
21/// println!("Address: {}", account.address());
22/// ```
23#[derive(Clone)]
24pub struct Secp256k1Account {
25    private_key: Secp256k1PrivateKey,
26    public_key: Secp256k1PublicKey,
27    address: AccountAddress,
28}
29
30impl Secp256k1Account {
31    /// Generates a new random Secp256k1 account.
32    pub fn generate() -> Self {
33        let private_key = Secp256k1PrivateKey::generate();
34        Self::from_private_key(private_key)
35    }
36
37    /// Creates an account from a private key.
38    pub fn from_private_key(private_key: Secp256k1PrivateKey) -> Self {
39        let public_key = private_key.public_key();
40        let address = public_key.to_address();
41        Self {
42            private_key,
43            public_key,
44            address,
45        }
46    }
47
48    /// Creates an account from private key bytes.
49    ///
50    /// # Errors
51    ///
52    /// Returns an error if the bytes are not a valid Secp256k1 private key (must be exactly 32 bytes and a valid curve point).
53    pub fn from_private_key_bytes(bytes: &[u8]) -> AptosResult<Self> {
54        let private_key = Secp256k1PrivateKey::from_bytes(bytes)?;
55        Ok(Self::from_private_key(private_key))
56    }
57
58    /// Creates an account from a private key hex string.
59    ///
60    /// # Errors
61    ///
62    /// This function will return an error if:
63    /// - The hex string is invalid or cannot be decoded
64    /// - The decoded bytes are not a valid Secp256k1 private key
65    pub fn from_private_key_hex(hex_str: &str) -> AptosResult<Self> {
66        let private_key = Secp256k1PrivateKey::from_hex(hex_str)?;
67        Ok(Self::from_private_key(private_key))
68    }
69
70    /// Returns the account address.
71    pub fn address(&self) -> AccountAddress {
72        self.address
73    }
74
75    /// Returns the public key.
76    pub fn public_key(&self) -> &Secp256k1PublicKey {
77        &self.public_key
78    }
79
80    /// Returns a reference to the private key.
81    pub fn private_key(&self) -> &Secp256k1PrivateKey {
82        &self.private_key
83    }
84
85    /// Signs a message and returns the Secp256k1 signature.
86    pub fn sign_message(&self, message: &[u8]) -> crate::crypto::Secp256k1Signature {
87        self.private_key.sign(message)
88    }
89}
90
91impl Account for Secp256k1Account {
92    fn address(&self) -> AccountAddress {
93        self.address
94    }
95
96    fn authentication_key(&self) -> AuthenticationKey {
97        // Use correct BCS format: variant_byte || ULEB128(length) || uncompressed_public_key
98        let uncompressed = self.public_key.to_uncompressed_bytes();
99        let mut bcs_bytes = Vec::with_capacity(1 + 1 + uncompressed.len());
100        bcs_bytes.push(0x01); // Secp256k1 variant
101        bcs_bytes.push(65); // ULEB128(65) = 65
102        bcs_bytes.extend_from_slice(&uncompressed);
103        let key = derive_authentication_key(&bcs_bytes, SINGLE_KEY_SCHEME);
104        AuthenticationKey::new(key)
105    }
106
107    fn sign(&self, message: &[u8]) -> crate::error::AptosResult<Vec<u8>> {
108        Ok(self.private_key.sign(message).to_bytes().to_vec())
109    }
110
111    fn public_key_bytes(&self) -> Vec<u8> {
112        // Return uncompressed format (65 bytes) as required by Aptos protocol
113        self.public_key.to_uncompressed_bytes()
114    }
115
116    fn signature_scheme(&self) -> u8 {
117        SINGLE_KEY_SCHEME
118    }
119}
120
121impl fmt::Debug for Secp256k1Account {
122    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123        f.debug_struct("Secp256k1Account")
124            .field("address", &self.address)
125            .field("public_key", &self.public_key)
126            .finish_non_exhaustive()
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133    use crate::account::Account;
134
135    #[test]
136    fn test_generate() {
137        let account = Secp256k1Account::generate();
138        assert!(!account.address().is_zero());
139    }
140
141    #[test]
142    fn test_from_private_key_roundtrip() {
143        let account = Secp256k1Account::generate();
144        let bytes = account.private_key().to_bytes();
145
146        let restored = Secp256k1Account::from_private_key_bytes(&bytes).unwrap();
147        assert_eq!(account.address(), restored.address());
148    }
149
150    #[test]
151    fn test_sign_and_verify() {
152        let account = Secp256k1Account::generate();
153        let message = b"hello world";
154
155        let signature = account.sign_message(message);
156        assert!(account.public_key().verify(message, &signature).is_ok());
157    }
158
159    #[test]
160    fn test_from_private_key() {
161        let original = Secp256k1Account::generate();
162        let private_key = original.private_key().clone();
163        let restored = Secp256k1Account::from_private_key(private_key);
164        assert_eq!(original.address(), restored.address());
165    }
166
167    #[test]
168    fn test_from_private_key_hex() {
169        let original = Secp256k1Account::generate();
170        let hex = original.private_key().to_hex();
171        let restored = Secp256k1Account::from_private_key_hex(&hex).unwrap();
172        assert_eq!(original.address(), restored.address());
173    }
174
175    #[test]
176    fn test_authentication_key() {
177        let account = Secp256k1Account::generate();
178        let auth_key = account.authentication_key();
179        assert_eq!(auth_key.as_bytes().len(), 32);
180    }
181
182    #[test]
183    fn test_public_key_bytes() {
184        let account = Secp256k1Account::generate();
185        let bytes = account.public_key_bytes();
186        assert_eq!(bytes.len(), 65); // Uncompressed public key (required by Aptos protocol)
187    }
188
189    #[test]
190    fn test_signature_scheme() {
191        let account = Secp256k1Account::generate();
192        assert_eq!(account.signature_scheme(), SINGLE_KEY_SCHEME);
193    }
194
195    #[test]
196    fn test_sign_trait() {
197        let account = Secp256k1Account::generate();
198        let message = b"test message";
199        let sig_bytes = account.sign(message).unwrap();
200        assert_eq!(sig_bytes.len(), 64);
201    }
202
203    #[test]
204    fn test_debug_output() {
205        let account = Secp256k1Account::generate();
206        let debug = format!("{account:?}");
207        assert!(debug.contains("Secp256k1Account"));
208        assert!(debug.contains("address"));
209    }
210
211    #[test]
212    fn test_invalid_private_key_bytes() {
213        let result = Secp256k1Account::from_private_key_bytes(&[0u8; 16]);
214        assert!(result.is_err());
215    }
216
217    #[test]
218    fn test_invalid_private_key_hex() {
219        let result = Secp256k1Account::from_private_key_hex("invalid");
220        assert!(result.is_err());
221    }
222}