fuels_accounts/signers/
private_key.rs1use async_trait::async_trait;
2use fuel_crypto::{Message, PublicKey, SecretKey, Signature};
3use fuels_core::{
4 traits::Signer,
5 types::{
6 bech32::{Bech32Address, FUEL_BECH32_HRP},
7 errors::Result,
8 },
9};
10use rand::{CryptoRng, Rng, RngCore};
11use zeroize::{Zeroize, ZeroizeOnDrop};
12
13pub fn generate_mnemonic_phrase<R: Rng>(rng: &mut R, count: usize) -> Result<String> {
16 Ok(fuel_crypto::generate_mnemonic_phrase(rng, count)?)
17}
18
19#[derive(Clone, Zeroize, ZeroizeOnDrop)]
20pub struct PrivateKeySigner {
21 private_key: SecretKey,
22 #[zeroize(skip)]
23 address: Bech32Address,
24}
25
26impl std::fmt::Debug for PrivateKeySigner {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 f.debug_struct("PrivateKeySigner")
29 .field("private_key", &"REDACTED")
30 .field("address", &self.address)
31 .finish()
32 }
33}
34
35impl PrivateKeySigner {
36 pub fn new(private_key: SecretKey) -> Self {
37 let public = PublicKey::from(&private_key);
38 let hashed = public.hash();
39 let address = Bech32Address::new(FUEL_BECH32_HRP, hashed);
40
41 Self {
42 private_key,
43 address,
44 }
45 }
46
47 pub fn random(rng: &mut (impl CryptoRng + RngCore)) -> Self {
48 Self::new(SecretKey::random(rng))
49 }
50
51 pub fn address(&self) -> &Bech32Address {
52 &self.address
53 }
54}
55
56#[async_trait]
57impl Signer for PrivateKeySigner {
58 async fn sign(&self, message: Message) -> Result<Signature> {
59 let sig = Signature::sign(&self.private_key, &message);
60
61 Ok(sig)
62 }
63
64 fn address(&self) -> &Bech32Address {
65 &self.address
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use std::str::FromStr;
72
73 use rand::{rngs::StdRng, SeedableRng};
74
75 use crate::signers::derivation::DEFAULT_DERIVATION_PATH;
76
77 use super::*;
78
79 #[tokio::test]
80 async fn mnemonic_generation() -> Result<()> {
81 let mnemonic = generate_mnemonic_phrase(&mut rand::thread_rng(), 12)?;
82 let _wallet = PrivateKeySigner::new(SecretKey::new_from_mnemonic_phrase_with_path(
83 &mnemonic,
84 DEFAULT_DERIVATION_PATH,
85 )?);
86
87 Ok(())
88 }
89
90 #[tokio::test]
91 async fn sign_and_verify() -> Result<()> {
92 let mut rng = StdRng::seed_from_u64(2322u64);
94 let mut secret_seed = [0u8; 32];
95 rng.fill_bytes(&mut secret_seed);
96
97 let secret = secret_seed.as_slice().try_into()?;
98
99 let signer = PrivateKeySigner::new(secret);
101
102 let message = Message::new("my message".as_bytes());
103 let signature = signer.sign(message).await?;
104
105 assert_eq!(signature, Signature::from_str("0x8eeb238db1adea4152644f1cd827b552dfa9ab3f4939718bb45ca476d167c6512a656f4d4c7356bfb9561b14448c230c6e7e4bd781df5ee9e5999faa6495163d")?);
107
108 let recovered_address = signature.recover(&message)?;
110
111 assert_eq!(signer.address().hash(), recovered_address.hash());
112
113 signature.verify(&recovered_address, &message)?;
115 Ok(())
118 }
119}