drt_sdk/
wallet.rs

1extern crate rand;
2
3use anyhow::Result;
4use bip39::{Language, Mnemonic};
5use hmac::{Hmac, Mac};
6use pbkdf2::pbkdf2;
7use serde_json::json;
8use sha2::{Digest, Sha512};
9use sha3::Keccak256;
10use zeroize::Zeroize;
11
12use crate::{
13    crypto::{
14        private_key::{PrivateKey, PRIVATE_KEY_LENGTH},
15        public_key::PublicKey,
16    },
17    data::{address::Address, transaction::Transaction},
18};
19
20const REWA_COIN_TYPE: u32 = 508;
21const HARDENED: u32 = 0x80000000;
22
23type HmacSha521 = Hmac<Sha512>;
24
25#[derive(Copy, Clone, Debug)]
26pub struct Wallet {
27    priv_key: PrivateKey,
28}
29
30impl Wallet {
31    // GenerateMnemonic will generate a new mnemonic value using the bip39 implementation
32    pub fn generate_mnemonic() -> Mnemonic {
33        Mnemonic::generate_in(Language::English, 24).unwrap()
34    }
35
36    fn seed_from_mnemonic(mnemonic: Mnemonic, password: &str) -> [u8; 64] {
37        let mut salt = String::with_capacity(8 + password.len());
38        salt.push_str("mnemonic");
39        salt.push_str(password);
40
41        let mut seed = [0u8; 64];
42
43        let _ = pbkdf2::<Hmac<Sha512>>(
44            mnemonic.to_string().as_bytes(),
45            salt.as_bytes(),
46            2048,
47            &mut seed,
48        );
49
50        salt.zeroize();
51
52        seed
53    }
54
55    pub fn get_private_key_from_mnemonic(
56        mnemonic: Mnemonic,
57        account: u32,
58        address_index: u32,
59    ) -> PrivateKey {
60        let seed = Self::seed_from_mnemonic(mnemonic, "");
61
62        let serialized_key_len = 32;
63        let hardened_child_padding: u8 = 0;
64
65        let mut digest =
66            HmacSha521::new_from_slice(b"ed25519 seed").expect("HMAC can take key of any size");
67        digest.update(&seed);
68        let intermediary: Vec<u8> = digest.finalize().into_bytes().into_iter().collect();
69        let mut key = intermediary[..serialized_key_len].to_vec();
70        let mut chain_code = intermediary[serialized_key_len..].to_vec();
71
72        for child_idx in [
73            44 | HARDENED,
74            REWA_COIN_TYPE | HARDENED,
75            account | HARDENED, // account
76            HARDENED,
77            address_index | HARDENED, // addressIndex
78        ] {
79            let mut buff = [vec![hardened_child_padding], key.clone()].concat();
80            buff.push((child_idx >> 24) as u8);
81            buff.push((child_idx >> 16) as u8);
82            buff.push((child_idx >> 8) as u8);
83            buff.push(child_idx as u8);
84
85            digest =
86                HmacSha521::new_from_slice(&chain_code).expect("HMAC can take key of any size");
87            digest.update(&buff);
88            let intermediary: Vec<u8> = digest.finalize().into_bytes().into_iter().collect();
89            key = intermediary[..serialized_key_len].to_vec();
90            chain_code = intermediary[serialized_key_len..].to_vec();
91        }
92
93        PrivateKey::from_bytes(key.as_slice()).unwrap()
94    }
95
96    pub fn from_private_key(priv_key: &str) -> Result<Self> {
97        let pri_key = PrivateKey::from_hex_str(priv_key)?;
98        Ok(Self { priv_key: pri_key })
99    }
100
101    pub fn from_pem_file(file_path: &str) -> Result<Self> {
102        let contents = std::fs::read_to_string(file_path).unwrap();
103        Self::from_pem_file_contents(contents)
104    }
105
106    pub fn from_pem_file_contents(contents: String) -> Result<Self> {
107        let x = pem::parse(contents)?;
108        let x = x.contents()[..PRIVATE_KEY_LENGTH].to_vec();
109        let priv_key_str = std::str::from_utf8(x.as_slice())?;
110        let pri_key = PrivateKey::from_hex_str(priv_key_str)?;
111        Ok(Self { priv_key: pri_key })
112    }
113
114    pub fn address(&self) -> Address {
115        let public_key = PublicKey::from(&self.priv_key);
116        Address::from(&public_key)
117    }
118
119    pub fn sign_tx(&self, unsign_tx: &Transaction) -> [u8; 64] {
120        let mut unsign_tx = unsign_tx.clone();
121        unsign_tx.signature = None;
122
123        let mut tx_bytes = json!(unsign_tx).to_string().as_bytes().to_vec();
124
125        let should_sign_on_tx_hash = unsign_tx.version >= 2 && unsign_tx.options & 1 > 0;
126        if should_sign_on_tx_hash {
127            let mut h = Keccak256::new();
128            h.update(tx_bytes);
129            tx_bytes = h.finalize().as_slice().to_vec();
130        }
131
132        self.priv_key.sign(tx_bytes)
133    }
134}