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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use crate::crypto::bip39::{Language, Mnemonic};
use anyhow::Result;
#[cfg(not(target_family = "wasm"))]
use std::fs::OpenOptions;
#[cfg(not(target_family = "wasm"))]
use std::io::Write;
use std::path::Path;
#[cfg(not(target_family = "wasm"))]
use std::path::PathBuf;
#[cfg(target_family = "wasm")]
use wasm_bindgen::{JsCast, UnwrapThrowExt};
#[cfg(target_family = "wasm")]
use web_sys::Storage;
pub fn generate_mnemonic() -> Result<Mnemonic> {
let mut entropy = [0; 32];
getrandom::getrandom(&mut entropy)?;
let mnemonic = Mnemonic::from_entropy_in(Language::English, &entropy)?;
Ok(mnemonic)
}
pub struct MnemonicStore {
#[cfg(not(target_family = "wasm"))]
path: PathBuf,
#[cfg(target_family = "wasm")]
local_storage: Storage,
}
impl MnemonicStore {
pub fn generate(&self) -> Result<Mnemonic> {
let mnemonic = generate_mnemonic()?;
self.set(&mnemonic)?;
Ok(mnemonic)
}
pub fn get_or_generate_mnemonic(&self) -> Result<Mnemonic> {
if self.exists() {
self.get()
} else {
self.generate()
}
}
}
#[cfg(not(target_family = "wasm"))]
impl MnemonicStore {
pub fn new(path: Option<&Path>) -> Result<Self> {
let path = if let Some(path) = path {
path.into()
} else {
dirs_next::config_dir()
.ok_or_else(|| anyhow::anyhow!("no config dir found"))?
.join("rosetta-wallet")
.join("mnemonic")
};
Ok(Self { path })
}
pub fn set(&self, mnemonic: &Mnemonic) -> Result<()> {
std::fs::create_dir_all(self.path.parent().unwrap())?;
#[cfg(unix)]
use std::os::unix::fs::OpenOptionsExt;
let mut opts = OpenOptions::new();
opts.create(true).write(true).truncate(true);
#[cfg(unix)]
opts.mode(0o600);
let mut f = opts.open(&self.path)?;
f.write_all(mnemonic.to_string().as_bytes())?;
Ok(())
}
pub fn get(&self) -> Result<Mnemonic> {
let mnemonic = std::fs::read_to_string(&self.path)?;
let mnemonic = Mnemonic::parse_in(Language::English, mnemonic)?;
Ok(mnemonic)
}
pub fn exists(&self) -> bool {
self.path.exists()
}
}
#[cfg(target_family = "wasm")]
impl MnemonicStore {
pub fn new(_path: Option<&Path>) -> Result<Self> {
let local_storage = web_sys::window()
.expect_throw("no window")
.local_storage()
.expect_throw("failed to get local_storage")
.expect_throw("no local storage");
Ok(Self { local_storage })
}
pub fn set(&self, mnemonic: &Mnemonic) -> Result<()> {
self.local_storage
.set_item("mnemonic", &mnemonic.to_string())
.map_err(|value| {
anyhow::anyhow!(String::from(
value.dyn_into::<js_sys::Error>().unwrap().to_string()
))
})?;
Ok(())
}
pub fn get(&self) -> Result<Mnemonic> {
let mnemonic = self
.local_storage
.get_item("mnemonic")
.expect_throw("unreachable: get_item does not throw an exception")
.expect_throw("no mnemonic in store");
Ok(Mnemonic::parse_in(Language::English, &mnemonic)?)
}
pub fn exists(&self) -> bool {
self.local_storage
.get_item("mnemonic")
.expect_throw("unreachable: get_item does not throw an exception")
.is_some()
}
}