1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
use crate::errors::{to_mnemonic_error, MnemonicError, SimpleError};
use crate::random::generate_random_bytes;

use bip39::{Language, Mnemonic};
use cipher::consts::U32;
use std::str::FromStr;

/// An object that holds necessary secrets. Should be dealt with carefully and never be logged.
///
/// The consumer of the library *must* persist `mnemonic` and `passphrase`
/// *securely* on the device,
/// The consumer of the library *must* never use or share it except to display it to
/// the end user for backup or for recovering a wallet.
/// The consumer of the library may want to *securely* persist `seed` or derive it
/// every time `seed` is needed, but it will have performance implications.
#[derive(PartialEq, Eq, Debug)]
pub struct Secret {
    /// The 24 words used to derive the node's private key.
    pub mnemonic: Vec<String>,
    /// Optional passphrase. If not provided, it is an empty string.
    pub passphrase: String,
    /// The seed derived from the mnemonic and the passphrase.
    pub seed: Vec<u8>,
}

impl Secret {
    fn from_mnemonic(mnemonic: Mnemonic, passphrase: String) -> Secret {
        let seed = mnemonic.to_seed(&passphrase);
        let mnemonic_string: Vec<_> = mnemonic.words().map(String::from).collect();

        Secret {
            mnemonic: mnemonic_string,
            passphrase,
            seed: seed.to_vec(),
        }
    }
}

/// Generate a new mnemonic with an optional passphrase. Provide an empty string to use no passphrase.
pub fn generate_secret(passphrase: String) -> std::result::Result<Secret, SimpleError> {
    let entropy = generate_random_bytes::<U32>().map_err(|e| SimpleError::Simple {
        msg: format!("Failed to generate random bytes: {e}"),
    })?;
    let mnemonic = Mnemonic::from_entropy(&entropy).map_err(|e| SimpleError::Simple {
        msg: format!("Failed to generate mnemonic: {e}"),
    })?;

    Ok(Secret::from_mnemonic(mnemonic, passphrase))
}

/// Generate a Secret object (containing the seed). Provide an empty string to use no passphrase.
pub fn mnemonic_to_secret(
    mnemonic_string: Vec<String>,
    passphrase: String,
) -> std::result::Result<Secret, MnemonicError> {
    let mnemonic = Mnemonic::from_str(&mnemonic_string.join(" ")).map_err(to_mnemonic_error)?;
    Ok(Secret::from_mnemonic(mnemonic, passphrase))
}

/// Return a list of valid BIP-39 English words starting with the prefix.
/// Calling this function with empty prefix will return the full list of BIP-39 words.
pub fn words_by_prefix(prefix: String) -> Vec<String> {
    Language::English
        .words_by_prefix(&prefix)
        .iter()
        .map(|w| w.to_string())
        .collect()
}