Skip to main content

forest/key_management/
wallet.rs

1// Copyright 2019-2026 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4use std::{convert::TryFrom, str::FromStr};
5
6use super::{KeyInfo, KeyStore, errors::Error, wallet_helpers};
7use crate::shim::{address::Address, crypto::SignatureType};
8use serde::{Deserialize, Serialize};
9
10#[cfg(test)]
11use {
12    crate::shim::crypto::Signature,
13    ahash::{HashMap, HashMapExt as _},
14};
15
16/// A key, this contains a `KeyInfo`, an address, and a public key.
17#[derive(Clone, PartialEq, Debug, Eq, Serialize, Deserialize)]
18pub struct Key {
19    pub key_info: KeyInfo,
20    // Vec<u8> is used because The public keys for BLS and SECP256K1 are not of the same type
21    pub public_key: Vec<u8>,
22    pub address: Address,
23}
24
25impl TryFrom<KeyInfo> for Key {
26    type Error = crate::key_management::errors::Error;
27
28    fn try_from(key_info: KeyInfo) -> Result<Self, Self::Error> {
29        let public_key = wallet_helpers::to_public(*key_info.key_type(), key_info.private_key())?;
30        let address = wallet_helpers::new_address(*key_info.key_type(), &public_key)?;
31        Ok(Key {
32            key_info,
33            public_key,
34            address,
35        })
36    }
37}
38
39// This is a Wallet, it contains 2 HashMaps:
40// - keys which is a HashMap of Keys resolved by their Address
41// - keystore which is a HashMap of KeyInfos resolved by their Address
42/// A wallet is a collection of private keys with optional persistence and
43/// optional encryption.
44#[cfg(test)]
45#[derive(Clone, PartialEq, Debug, Eq)]
46pub struct Wallet {
47    keys: HashMap<Address, Key>,
48    keystore: KeyStore,
49}
50
51#[cfg(test)]
52impl Wallet {
53    /// Return a new wallet with a given `KeyStore`
54    pub fn new(keystore: KeyStore) -> Self {
55        Wallet {
56            keys: HashMap::new(),
57            keystore,
58        }
59    }
60
61    /// Return a wallet from a given amount of keys.
62    pub fn new_from_keys(keystore: KeyStore, key_vec: impl IntoIterator<Item = Key>) -> Self {
63        let mut keys: HashMap<Address, Key> = HashMap::new();
64        for item in key_vec.into_iter() {
65            keys.insert(item.address, item);
66        }
67        Wallet { keys, keystore }
68    }
69
70    // If this key does not exist in the keys hashmap, check if this key is in
71    // the keystore, if it is, then add it to keys, otherwise return Error
72    /// Return the key that is resolved by a given address,
73    pub fn find_key(&mut self, addr: &Address) -> Result<Key, Error> {
74        if let Some(k) = self.keys.get(addr) {
75            return Ok(k.clone());
76        }
77        let key_string = format!("wallet-{addr}");
78        let key_info = match self.keystore.get(&key_string) {
79            Ok(k) => k,
80            Err(_) => {
81                // replace with testnet prefix
82                self.keystore
83                    .get(&format!("wallet-t{}", &addr.to_string()[1..]))?
84            }
85        };
86        let new_key = Key::try_from(key_info)?;
87        self.keys.insert(*addr, new_key.clone());
88        Ok(new_key)
89    }
90
91    /// Return the resultant `Signature` after signing a given message
92    pub fn sign(&mut self, addr: &Address, msg: &[u8]) -> Result<Signature, Error> {
93        // this will return an error if the key cannot be found in either the keys
94        // hashmap or it is not found in the keystore
95        let key = self.find_key(addr).map_err(|_| Error::KeyNotExists)?;
96        wallet_helpers::sign(*key.key_info.key_type(), key.key_info.private_key(), msg)
97    }
98
99    /// Return the `KeyInfo` for a given address
100    pub fn export(&mut self, addr: &Address) -> Result<KeyInfo, Error> {
101        let k = self.find_key(addr)?;
102        Ok(k.key_info)
103    }
104
105    /// Add `KeyInfo` to the wallet, return the address that resolves to this
106    /// newly added `KeyInfo`
107    pub fn import(&mut self, key_info: KeyInfo) -> Result<Address, Error> {
108        let k = Key::try_from(key_info)?;
109        let addr = format!("wallet-{}", k.address);
110        self.keystore.put(&addr, k.key_info)?;
111        Ok(k.address)
112    }
113
114    /// Return a vector that contains all of the addresses in the wallet's
115    /// `KeyStore`
116    pub fn list_addrs(&self) -> Result<Vec<Address>, Error> {
117        list_addrs(&self.keystore)
118    }
119
120    /// Return the address of the default `KeyInfo` in the wallet
121    pub fn get_default(&self) -> Result<Address, Error> {
122        let key_info = self.keystore.get("default")?;
123        let k = Key::try_from(key_info)?;
124        Ok(k.address)
125    }
126
127    /// Set a default `KeyInfo` to the wallet
128    pub fn set_default(&mut self, addr: Address) -> anyhow::Result<()> {
129        let addr_string = format!("wallet-{addr}");
130        let key_info = self.keystore.get(&addr_string)?;
131        if self.keystore.get("default").is_ok() {
132            self.keystore.remove("default")?; // This line should
133            // unregister current
134            // default key then
135            // continue
136        }
137        self.keystore.put("default", key_info)?;
138        Ok(())
139    }
140
141    /// Generate a new address that fits the requirement of the given
142    /// `SignatureType`
143    pub fn generate_addr(&mut self, typ: SignatureType) -> anyhow::Result<Address> {
144        let key = generate_key(typ)?;
145        let addr = format!("wallet-{}", key.address);
146        self.keystore.put(&addr, key.key_info.clone())?;
147        self.keys.insert(key.address, key.clone());
148        let value = self.keystore.get("default");
149        if value.is_err() {
150            self.keystore
151                .put("default", key.key_info.clone())
152                .map_err(|err| Error::Other(err.to_string()))?;
153        }
154
155        Ok(key.address)
156    }
157
158    /// Return whether or not the Wallet contains a key that is resolved by the
159    /// supplied address
160    pub fn has_key(&mut self, addr: &Address) -> bool {
161        self.find_key(addr).is_ok()
162    }
163}
164
165/// Return the default address for `KeyStore`
166pub fn get_default(keystore: &KeyStore) -> Result<Option<Address>, Error> {
167    if let Ok(key_info) = keystore.get("default") {
168        let k = Key::try_from(key_info)?;
169        Ok(Some(k.address))
170    } else {
171        Ok(None)
172    }
173}
174
175/// Return vector of addresses sorted by their string representation in
176/// `KeyStore`
177pub fn list_addrs(keystore: &KeyStore) -> Result<Vec<Address>, Error> {
178    let mut all = keystore.list();
179    all.sort();
180    let mut out = Vec::new();
181    for i in all {
182        if let Some(addr_str) = i.strip_prefix("wallet-")
183            && let Ok(addr) = crate::shim::address::StrictAddress::from_str(addr_str)
184        {
185            out.push(addr.into());
186        }
187    }
188    Ok(out)
189}
190
191/// Returns a key corresponding to given address
192pub fn find_key(addr: &Address, keystore: &KeyStore) -> Result<Key, Error> {
193    let key_string = format!("wallet-{addr}");
194    let key_info = keystore.get(&key_string)?;
195    let new_key = Key::try_from(key_info)?;
196    Ok(new_key)
197}
198
199/// Removes a key corresponding to given address
200pub fn remove_key(addr: &Address, keystore: &mut KeyStore) -> Result<(), Error> {
201    let key_string = format!("wallet-{addr}");
202    let deleted_keyinfo = keystore
203        .remove(&key_string)
204        .map_err(|_| Error::KeyNotExists)?;
205    if let Ok(default_keyinfo) = keystore.get("default")
206        && default_keyinfo == deleted_keyinfo
207    {
208        keystore
209            .remove("default")
210            .map_err(|_| Error::KeyNotExists)?;
211    }
212    println!("wallet {addr} deleted");
213    Ok(())
214}
215
216pub fn try_find(addr: &Address, keystore: &mut KeyStore) -> Result<KeyInfo, Error> {
217    let key_string = format!("wallet-{addr}");
218    match keystore.get(&key_string) {
219        Ok(k) => Ok(k),
220        Err(_) => {
221            let mut new_addr = addr.to_string();
222            if new_addr.len() < 2 {
223                return Err(Error::Other(format!("Invalid addr {new_addr}")));
224            }
225            // Try to replace prefix with testnet, for backwards compatibility
226            // * We might be able to remove this, look into variants
227            new_addr.replace_range(0..1, "t");
228            let key_string = format!("wallet-{new_addr}");
229            let key_info = match keystore.get(&key_string) {
230                Ok(k) => k,
231                #[allow(clippy::indexing_slicing)]
232                Err(_) => keystore.get(&format!("wallet-f{}", &new_addr[1..]))?,
233            };
234            Ok(key_info)
235        }
236    }
237}
238
239/// Return `KeyInfo` for given address in `KeyStore`
240pub fn export_key_info(addr: &Address, keystore: &KeyStore) -> Result<KeyInfo, Error> {
241    let key = find_key(addr, keystore)?;
242    Ok(key.key_info)
243}
244
245/// Generate new key of given `SignatureType`
246pub fn generate_key(typ: SignatureType) -> Result<Key, Error> {
247    let private_key = wallet_helpers::generate(typ)?;
248    let key_info = KeyInfo::new(typ, private_key);
249    Key::try_from(key_info)
250}
251
252#[cfg(test)]
253mod tests {
254    use crate::utils::encoding::{blake2b_256, keccak_256};
255    use bls_signatures::{PrivateKey as BlsPrivate, Serialize};
256    use libsecp256k1::{Message as SecpMessage, SecretKey as SecpPrivate};
257
258    use super::*;
259    use crate::key_management::{KeyStoreConfig, generate};
260
261    fn construct_priv_keys() -> Vec<Key> {
262        let mut secp_keys = Vec::new();
263        let mut bls_keys = Vec::new();
264        let mut delegated_keys = Vec::new();
265        for _ in 1..5 {
266            let secp_priv_key = generate(SignatureType::Secp256k1).unwrap();
267            let secp_key_info = KeyInfo::new(SignatureType::Secp256k1, secp_priv_key);
268            let secp_key = Key::try_from(secp_key_info).unwrap();
269            secp_keys.push(secp_key);
270
271            let bls_priv_key = generate(SignatureType::Bls).unwrap();
272            let bls_key_info = KeyInfo::new(SignatureType::Bls, bls_priv_key);
273            let bls_key = Key::try_from(bls_key_info).unwrap();
274            bls_keys.push(bls_key);
275
276            let delegated_priv_key = generate(SignatureType::Delegated).unwrap();
277            let delegated_key_info = KeyInfo::new(SignatureType::Delegated, delegated_priv_key);
278            let delegated_key = Key::try_from(delegated_key_info).unwrap();
279            delegated_keys.push(delegated_key);
280        }
281
282        secp_keys.append(bls_keys.as_mut());
283        secp_keys.append(delegated_keys.as_mut());
284        secp_keys
285    }
286
287    fn generate_wallet() -> Wallet {
288        let key_vec = construct_priv_keys();
289        Wallet::new_from_keys(KeyStore::new(KeyStoreConfig::Memory).unwrap(), key_vec)
290    }
291
292    #[test]
293    fn contains_key() {
294        let key_vec = construct_priv_keys();
295        let found_key = key_vec[0].clone();
296        let addr = key_vec[0].address;
297
298        let mut wallet =
299            Wallet::new_from_keys(KeyStore::new(KeyStoreConfig::Memory).unwrap(), key_vec);
300
301        // make sure that this address resolves to the right key
302        assert_eq!(wallet.find_key(&addr).unwrap(), found_key);
303        // make sure that has_key returns true as well
304        assert!(wallet.has_key(&addr));
305
306        let new_priv_key = generate(SignatureType::Bls).unwrap();
307        let pub_key =
308            wallet_helpers::to_public(SignatureType::Bls, new_priv_key.as_slice()).unwrap();
309        let address = Address::new_bls(pub_key.as_slice()).unwrap();
310
311        // test to see if the new key has been created and added to the wallet
312        assert!(!wallet.has_key(&address));
313        // test to make sure that the newly made key cannot be added to the wallet
314        // because it is not found in the keystore
315        assert!(matches!(
316            wallet.find_key(&address).unwrap_err(),
317            Error::KeyInfo
318        ));
319        // sanity check to make sure that the key has not been added to the wallet
320        assert!(!wallet.has_key(&address));
321    }
322
323    #[test]
324    fn secp_sign() {
325        let key_vec = construct_priv_keys();
326        let priv_key_bytes = key_vec[2].key_info.private_key().clone();
327        let addr = key_vec[2].address;
328
329        let keystore = KeyStore::new(KeyStoreConfig::Memory).unwrap();
330        let mut wallet = Wallet::new_from_keys(keystore, key_vec);
331        let msg = [0u8; 64];
332
333        let msg_sig = wallet.sign(&addr, &msg).unwrap();
334
335        let msg_complete = blake2b_256(&msg);
336        let message = SecpMessage::parse(&msg_complete);
337        let priv_key = SecpPrivate::parse_slice(&priv_key_bytes).unwrap();
338        let (sig, recovery_id) = libsecp256k1::sign(&message, &priv_key);
339        let mut new_bytes = [0; 65];
340        new_bytes[..64].copy_from_slice(&sig.serialize());
341        new_bytes[64] = recovery_id.serialize();
342        let actual = Signature::new_secp256k1(new_bytes.to_vec());
343        assert_eq!(msg_sig, actual)
344    }
345
346    #[test]
347    fn bls_sign() {
348        let key_vec = construct_priv_keys();
349        let priv_key_bytes = key_vec[4].key_info.private_key().clone();
350        let addr = key_vec[4].address;
351        let mut wallet =
352            Wallet::new_from_keys(KeyStore::new(KeyStoreConfig::Memory).unwrap(), key_vec);
353
354        let msg = [0u8; 64];
355        let msg_sign = wallet.sign(&addr, &msg).unwrap();
356
357        let priv_key = BlsPrivate::from_bytes(&priv_key_bytes).unwrap();
358        let sig = priv_key.sign(msg);
359        let actual = Signature::new_bls(sig.as_bytes());
360        assert_eq!(msg_sign, actual);
361    }
362
363    #[test]
364    fn delegated_sign() {
365        let key_vec = construct_priv_keys();
366        let priv_key_bytes = key_vec[9].key_info.private_key().clone();
367        let addr = key_vec[9].address;
368
369        let keystore = KeyStore::new(KeyStoreConfig::Memory).unwrap();
370        let mut wallet = Wallet::new_from_keys(keystore, key_vec);
371        let msg = [0u8; 64];
372
373        let msg_sig = wallet.sign(&addr, &msg).unwrap();
374
375        let msg_complete = keccak_256(&msg);
376        let message = SecpMessage::parse(&msg_complete);
377        let priv_key = SecpPrivate::parse_slice(&priv_key_bytes).unwrap();
378        let (sig, recovery_id) = libsecp256k1::sign(&message, &priv_key);
379        let mut new_bytes = [0; 65];
380        new_bytes[..64].copy_from_slice(&sig.serialize());
381        new_bytes[64] = recovery_id.serialize();
382        let actual = Signature::new_delegated(new_bytes.to_vec());
383        assert_eq!(msg_sig, actual)
384    }
385
386    #[test]
387    fn import_export() {
388        let key_vec = construct_priv_keys();
389        let key = key_vec[0].clone();
390        let keystore = KeyStore::new(KeyStoreConfig::Memory).unwrap();
391        let mut wallet = Wallet::new_from_keys(keystore, key_vec);
392
393        let key_info = wallet.export(&key.address).unwrap();
394        // test to see if export returns the correct key_info
395        assert_eq!(key_info, key.key_info);
396
397        let new_priv_key = generate(SignatureType::Secp256k1).unwrap();
398        let pub_key =
399            wallet_helpers::to_public(SignatureType::Secp256k1, new_priv_key.as_slice()).unwrap();
400        let test_addr = Address::new_secp256k1(pub_key.as_slice()).unwrap();
401        let key_info_err = wallet.export(&test_addr).unwrap_err();
402        // test to make sure that an error is raised when an incorrect address is added
403        assert!(matches!(key_info_err, Error::KeyInfo));
404
405        let test_key_info = KeyInfo::new(SignatureType::Secp256k1, new_priv_key);
406        // make sure that key_info has been imported to wallet
407        assert!(wallet.import(test_key_info.clone()).is_ok());
408
409        let duplicate_error = wallet.import(test_key_info).unwrap_err();
410        // make sure that error is thrown when attempted to re-import a duplicate
411        // key_info
412        assert!(matches!(duplicate_error, Error::KeyExists));
413    }
414
415    #[test]
416    fn list_addr() {
417        let key_vec = construct_priv_keys();
418        let mut addr_string_vec = Vec::new();
419
420        let mut key_store = KeyStore::new(KeyStoreConfig::Memory).unwrap();
421
422        for i in &key_vec {
423            addr_string_vec.push(i.address.to_string());
424
425            let addr_string = format!("wallet-{}", i.address);
426            key_store.put(&addr_string, i.key_info.clone()).unwrap();
427        }
428
429        addr_string_vec.sort();
430
431        let mut addr_vec = Vec::new();
432
433        for addr in addr_string_vec {
434            addr_vec.push(Address::from_str(addr.as_str()).unwrap())
435        }
436
437        let wallet = Wallet::new(key_store);
438
439        let test_addr_vec = wallet.list_addrs().unwrap();
440
441        // check to see if the addrs in wallet are the same as the key_vec before it was
442        // added to the wallet
443        assert_eq!(test_addr_vec, addr_vec);
444    }
445
446    #[test]
447    fn generate_new_key() {
448        let mut wallet = generate_wallet();
449        let addr = wallet.generate_addr(SignatureType::Bls).unwrap();
450        let key = wallet.keystore.get("default").unwrap();
451        // make sure that the newly generated key is the default key - checking by key
452        // type
453        assert_eq!(&SignatureType::Bls, key.key_type());
454
455        let address = format!("wallet-{addr}");
456
457        let key_info = wallet.keystore.get(&address).unwrap();
458        let key = wallet.keys.get(&addr).unwrap();
459
460        // these assertions will make sure that the key has actually been added to the
461        // wallet
462        assert_eq!(key_info.key_type(), &SignatureType::Bls);
463        assert_eq!(key.address, addr);
464    }
465
466    #[test]
467    fn get_set_default() {
468        let key_store = KeyStore::new(KeyStoreConfig::Memory).unwrap();
469        let mut wallet = Wallet::new(key_store);
470        // check to make sure that there is no default
471        assert!(matches!(wallet.get_default().unwrap_err(), Error::KeyInfo));
472
473        let new_priv_key = generate(SignatureType::Secp256k1).unwrap();
474        let pub_key =
475            wallet_helpers::to_public(SignatureType::Secp256k1, new_priv_key.as_slice()).unwrap();
476        let test_addr = Address::new_secp256k1(pub_key.as_slice()).unwrap();
477
478        let key_info = KeyInfo::new(SignatureType::Secp256k1, new_priv_key);
479        let test_addr_string = format!("wallet-{test_addr}");
480
481        wallet.keystore.put(&test_addr_string, key_info).unwrap();
482
483        // check to make sure that the set_default function completed without error
484        assert!(wallet.set_default(test_addr).is_ok());
485
486        // check to make sure that the test_addr is actually the default addr for the
487        // wallet
488        assert_eq!(wallet.get_default().unwrap(), test_addr);
489    }
490
491    #[test]
492    fn secp_verify() {
493        let secp_priv_key = generate(SignatureType::Secp256k1).unwrap();
494        let secp_key_info = KeyInfo::new(SignatureType::Secp256k1, secp_priv_key);
495        let secp_key = Key::try_from(secp_key_info).unwrap();
496        let addr = secp_key.address;
497        let key_store = KeyStore::new(KeyStoreConfig::Memory).unwrap();
498        let mut wallet = Wallet::new_from_keys(key_store, vec![secp_key]);
499
500        let msg = [0u8; 64];
501
502        let sig = wallet.sign(&addr, &msg).unwrap();
503        sig.verify(&msg, &addr).unwrap();
504
505        // invalid verify check
506        let invalid_addr = wallet.generate_addr(SignatureType::Secp256k1).unwrap();
507        assert!(sig.verify(&msg, &invalid_addr).is_err())
508    }
509
510    #[test]
511    fn bls_verify_test() {
512        let bls_priv_key = generate(SignatureType::Bls).unwrap();
513        let bls_key_info = KeyInfo::new(SignatureType::Bls, bls_priv_key);
514        let bls_key = Key::try_from(bls_key_info).unwrap();
515        let addr = bls_key.address;
516        let key_store = KeyStore::new(KeyStoreConfig::Memory).unwrap();
517        let mut wallet = Wallet::new_from_keys(key_store, vec![bls_key]);
518
519        let msg = [0u8; 64];
520
521        let sig = wallet.sign(&addr, &msg).unwrap();
522        sig.verify(&msg, &addr).unwrap();
523
524        // invalid verify check
525        let invalid_addr = wallet.generate_addr(SignatureType::Bls).unwrap();
526        assert!(sig.verify(&msg, &invalid_addr).is_err())
527    }
528
529    #[test]
530    fn delegated_verify() {
531        let delegated_priv_key = generate(SignatureType::Delegated).unwrap();
532        let delegated_key_info = KeyInfo::new(SignatureType::Delegated, delegated_priv_key);
533        let delegated_key = Key::try_from(delegated_key_info).unwrap();
534        let addr = delegated_key.address;
535
536        let key_store = KeyStore::new(KeyStoreConfig::Memory).unwrap();
537        let mut wallet = Wallet::new_from_keys(key_store, vec![delegated_key]);
538
539        let msg = [0u8; 64];
540
541        let sig = wallet.sign(&addr, &msg).unwrap();
542        sig.verify(&msg, &addr).unwrap();
543
544        // invalid verify check
545        let invalid_addr = wallet.generate_addr(SignatureType::Delegated).unwrap();
546        assert!(sig.verify(&msg, &invalid_addr).is_err())
547    }
548}