Skip to main content

kaspa_bip32/mnemonic/
language.rs

1//! Wordlist support
2//!
3//! NOTE: This implementation is not constant time and may leak information
4//! via timing side-channels!
5//!
6//! Adapted from the `bip39` crate
7
8use super::bits::{Bits, Bits11};
9use std::{collections::BTreeMap, vec::Vec};
10use wasm_bindgen::prelude::*;
11
12///
13/// Languages supported by BIP39.
14///
15/// Presently only English is specified by the BIP39 standard.
16///
17/// @see {@link Mnemonic}
18///
19/// @category Wallet SDK
20#[derive(Copy, Clone, Debug, Default)]
21#[wasm_bindgen]
22pub enum Language {
23    /// English is presently the only supported language
24    #[default]
25    English,
26}
27
28impl Language {
29    /// Get the word list for this language
30    pub fn wordlist(&self) -> &'static WordList {
31        match *self {
32            Language::English => &lazy::WORDLIST_ENGLISH,
33        }
34    }
35
36    /// Get a wordmap that allows word -> index lookups in the word list
37    pub(crate) fn wordmap(&self) -> &'static WordMap {
38        match *self {
39            Language::English => &lazy::WORDMAP_ENGLISH,
40        }
41    }
42}
43
44pub(crate) struct WordMap {
45    inner: BTreeMap<&'static str, Bits11>,
46}
47
48pub struct WordList {
49    inner: Vec<&'static str>,
50}
51
52impl WordMap {
53    pub fn get_bits(&self, word: &str) -> Option<Bits11> {
54        self.inner.get(word).cloned()
55    }
56}
57
58impl WordList {
59    pub fn get_word(&self, bits: Bits11) -> &'static str {
60        self.inner[bits.bits() as usize]
61    }
62
63    pub fn iter(&self) -> WordListIterator<'_> {
64        WordListIterator { wordlist: self, index: 0 }
65    }
66}
67
68pub struct WordListIterator<'a> {
69    wordlist: &'a WordList,
70    index: usize,
71}
72
73impl Iterator for WordListIterator<'_> {
74    type Item = &'static str;
75
76    fn next(&mut self) -> Option<Self::Item> {
77        if self.index < self.wordlist.inner.len() {
78            let word = self.wordlist.inner[self.index];
79            self.index += 1;
80            Some(word)
81        } else {
82            None
83        }
84    }
85}
86
87// TODO(tarcieri): use `const fn` instead of `Lazy`
88mod lazy {
89    use super::{Bits11, WordList, WordMap};
90    //use alloc::vec::Vec;
91    use once_cell::sync::Lazy;
92
93    /// lazy generation of the word list
94    fn gen_wordlist(lang_words: &'static str) -> WordList {
95        let inner: Vec<_> = lang_words.split_whitespace().collect();
96
97        debug_assert!(inner.len() == 2048, "Invalid wordlist length");
98
99        WordList { inner }
100    }
101
102    /// lazy generation of the word map
103    fn gen_wordmap(wordlist: &WordList) -> WordMap {
104        let inner = wordlist.inner.iter().enumerate().map(|(i, item)| (*item, Bits11::from(i as u16))).collect();
105
106        WordMap { inner }
107    }
108
109    pub(crate) static WORDLIST_ENGLISH: Lazy<WordList> = Lazy::new(|| gen_wordlist(include_str!("words/english.txt")));
110
111    pub(crate) static WORDMAP_ENGLISH: Lazy<WordMap> = Lazy::new(|| gen_wordmap(&WORDLIST_ENGLISH));
112}