1use super::*;
2
3#[cfg_attr(target_arch = "wasm32", typetag::serialize(tag = "pluginName"))]
4#[cfg_attr(not(target_arch = "wasm32"), typetag::serde(tag = "pluginName"))]
5pub trait VaultPlugin: Send + Sync {
6 fn name(&self) -> &'static str;
7 fn to_any(&self) -> Box<dyn Any>;
8 fn eq(&self, other: &dyn VaultPlugin) -> bool;
9}
10
11impl fmt::Debug for dyn VaultPlugin {
12 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
13 formatter.write_str(self.name())
14 }
15}
16
17#[cfg_attr(not(target_arch = "wasm32"), derive(Deserialize))]
18#[derive(Debug, Serialize)]
19#[serde(rename_all = "camelCase")]
20struct VaultImpl {
21 encrypted_seed: String,
22 plugins: Vec<Box<dyn VaultPlugin>>,
23 #[serde(skip)]
24 is_dirty: bool, }
26
27impl VaultImpl {
28 fn new(encrypted_seed: String, plugins: Vec<Box<dyn VaultPlugin>>, is_dirty: bool) -> Self {
29 Self { encrypted_seed, plugins, is_dirty }
30 }
31}
32
33#[cfg_attr(not(target_arch = "wasm32"), derive(Deserialize))]
34#[derive(Clone, Debug, Serialize)]
35#[serde(transparent)]
36pub struct Vault {
37 inner: Arc<RwLock<VaultImpl>>,
38}
39
40impl Vault {
41 pub fn new(encrypted_seed: String, plugins: Vec<Box<dyn VaultPlugin>>, dirty: bool) -> Self {
42 let imp = VaultImpl::new(encrypted_seed, plugins, dirty);
43 let inner = Arc::new(RwLock::new(imp));
44 Self { inner }
45 }
46
47 pub fn create(
48 lang_code: Option<&str>, phrase: impl AsRef<str>, bip39_password: impl AsRef<str>,
49 unlock_password: impl AsRef<str>,
50 ) -> Result<Vault> {
51 let bip39 = match lang_code {
52 None => Bip39::new(),
53 Some(code) => Bip39::language_code(code)?,
54 };
55 let seed = bip39.phrase(phrase)?.password(bip39_password);
56 let encrypted_seed = Self::encrypt_seed(&seed, unlock_password.as_ref())?;
57 let vault = Self::new(encrypted_seed, Vec::new(), true);
58 Ok(vault)
59 }
60
61 pub fn unlock(&self, unlock_password: &str) -> Result<Seed> {
62 let imp = self.inner.try_read().ok_or_else(|| format_err!("Read lock on Vault failed"))?;
63 Self::decrypt_seed(&imp.encrypted_seed, unlock_password)
64 }
65
66 pub fn plugins_by_type<T: VaultPlugin + 'static>(&self) -> Result<Vec<Box<T>>> {
67 let imp = self.inner.try_read().ok_or_else(|| format_err!("Read lock on Vault failed"))?;
68 let plugins =
69 imp.plugins.iter().by_ref().filter_map(|p| p.to_any().downcast().ok()).collect();
70 Ok(plugins)
71 }
72
73 pub fn add(&mut self, plugin: Box<dyn VaultPlugin>) -> Result<()> {
74 let mut imp =
75 self.inner.try_write().ok_or_else(|| format_err!("Write lock on Vault failed"))?;
76 ensure!(
77 imp.plugins.iter().all(|p| !p.eq(plugin.as_ref())),
78 "Same plugin was already added to vault"
79 );
80 imp.plugins.push(plugin);
81 imp.is_dirty = true;
82 Ok(())
83 }
84
85 pub fn to_modifiable(&self) -> Box<dyn State<bool>> {
86 <dyn State<_>>::map(&self.inner, |v| &v.is_dirty, |v| &mut v.is_dirty)
87 }
88
89 fn encrypt_seed(seed: &Seed, unlock_password: &str) -> Result<String> {
90 let nonce = nonce()?;
91 let encrypted_seed_bytes = encrypt(seed.as_bytes(), unlock_password, nonce)?;
92 Ok(multibase::encode(multibase::Base::Base64Url, &encrypted_seed_bytes))
93 }
94
95 fn decrypt_seed(seed: &str, unlock_password: &str) -> Result<Seed> {
96 let (_, encrypted_seed_bytes) = multibase::decode(seed)?;
97 let decrypted_bytes = decrypt(&encrypted_seed_bytes, unlock_password)?;
98 Seed::from_bytes(&decrypted_bytes)
99 }
100}
101
102pub trait PluginPublic<T: VaultPlugin>: Sized {
103 fn create(plugin: &T, vault_dirty: Box<dyn State<bool>>) -> Result<Self>;
104}
105
106pub trait PluginPrivate<T: VaultPlugin>: Sized {
107 fn create(plugin: &T, seed: Seed, vault_dirty: Box<dyn State<bool>>) -> Result<Self>;
108}
109
110pub struct BoundPlugin<T: VaultPlugin, TPublic: PluginPublic<T>, TPriv: PluginPrivate<T>> {
111 vault: Vault,
112 plugin: T,
113 _pub: PhantomData<TPublic>,
114 _priv: PhantomData<TPriv>,
115}
116
117impl<T: VaultPlugin, TPublic: PluginPublic<T>, TPriv: PluginPrivate<T>>
118 BoundPlugin<T, TPublic, TPriv>
119{
120 pub fn new(vault: Vault, plugin: T) -> Self {
121 Self { vault, plugin, _pub: Default::default(), _priv: Default::default() }
122 }
123
124 pub fn private(&self, unlock_password: impl AsRef<str>) -> Result<TPriv> {
125 let seed = self.vault.unlock(unlock_password.as_ref())?;
126 TPriv::create(&self.plugin, seed, self.vault.to_modifiable())
127 }
128
129 pub fn public(&self) -> Result<TPublic> {
130 TPublic::create(&self.plugin, self.vault.to_modifiable())
131 }
132}