cfxstore/
cfxstore.rs

1// Copyright 2015-2019 Parity Technologies (UK) Ltd.
2// This file is part of Parity Ethereum.
3
4// Parity Ethereum is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity Ethereum is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity Ethereum.  If not, see <http://www.gnu.org/licenses/>.
16
17use parking_lot::{Mutex, RwLock};
18use std::{
19    collections::{BTreeMap, HashMap},
20    path::PathBuf,
21    time::{Duration, Instant},
22};
23
24use crate::{
25    account::SafeAccount,
26    accounts_dir::{KeyDirectory, SetKeyError, VaultKey, VaultKeyDirectory},
27    import,
28    json::{self, OpaqueKeyFile, Uuid},
29    random::Random,
30    Derivation, Error, OpaqueSecret, SecretStore, SecretVaultRef,
31    SimpleSecretStore, StoreAccountRef,
32};
33use cfx_crypto::crypto::KEY_ITERATIONS;
34use cfxkey::{
35    self, Address, ExtendedKeyPair, KeyPair, Message, Password, Public, Secret,
36    Signature,
37};
38
39/// Accounts store.
40pub struct CfxStore {
41    store: CfxMultiStore,
42}
43
44impl CfxStore {
45    /// Open a new accounts store with given key directory backend.
46    pub fn open(directory: Box<dyn KeyDirectory>) -> Result<Self, Error> {
47        Self::open_with_iterations(directory, KEY_ITERATIONS as u32)
48    }
49
50    /// Open a new account store with given key directory backend and custom
51    /// number of iterations.
52    pub fn open_with_iterations(
53        directory: Box<dyn KeyDirectory>, iterations: u32,
54    ) -> Result<Self, Error> {
55        Ok(CfxStore {
56            store: CfxMultiStore::open_with_iterations(directory, iterations)?,
57        })
58    }
59
60    /// Modify account refresh timeout - how often they are re-read from
61    /// `KeyDirectory`.
62    ///
63    /// Setting this to low values (or 0) will cause new accounts to be picked
64    /// up quickly, although it may induce heavy disk reads and is not
65    /// recommended if you manage many keys (say over 10k).
66    ///
67    /// By default refreshing is disabled, so only accounts created using this
68    /// instance of `CfxStore` are taken into account.
69    pub fn set_refresh_time(&self, time: Duration) {
70        self.store.set_refresh_time(time)
71    }
72
73    fn get(&self, account: &StoreAccountRef) -> Result<SafeAccount, Error> {
74        let mut accounts = self.store.get_accounts(account)?.into_iter();
75        accounts.next().ok_or(Error::InvalidAccount)
76    }
77}
78
79impl SimpleSecretStore for CfxStore {
80    fn insert_account(
81        &self, vault: SecretVaultRef, secret: Secret, password: &Password,
82    ) -> Result<StoreAccountRef, Error> {
83        self.store.insert_account(vault, secret, password)
84    }
85
86    fn insert_derived(
87        &self, vault: SecretVaultRef, account_ref: &StoreAccountRef,
88        password: &Password, derivation: Derivation,
89    ) -> Result<StoreAccountRef, Error> {
90        self.store
91            .insert_derived(vault, account_ref, password, derivation)
92    }
93
94    fn generate_derived(
95        &self, account_ref: &StoreAccountRef, password: &Password,
96        derivation: Derivation,
97    ) -> Result<Address, Error> {
98        self.store
99            .generate_derived(account_ref, password, derivation)
100    }
101
102    fn account_ref(&self, address: &Address) -> Result<StoreAccountRef, Error> {
103        self.store.account_ref(address)
104    }
105
106    fn accounts(&self) -> Result<Vec<StoreAccountRef>, Error> {
107        self.store.accounts()
108    }
109
110    fn change_password(
111        &self, account: &StoreAccountRef, old_password: &Password,
112        new_password: &Password,
113    ) -> Result<(), Error> {
114        self.store
115            .change_password(account, old_password, new_password)
116    }
117
118    fn export_account(
119        &self, account: &StoreAccountRef, password: &Password,
120    ) -> Result<OpaqueKeyFile, Error> {
121        self.store.export_account(account, password)
122    }
123
124    fn remove_account(
125        &self, account: &StoreAccountRef, password: &Password,
126    ) -> Result<(), Error> {
127        self.store.remove_account(account, password)
128    }
129
130    fn sign(
131        &self, account: &StoreAccountRef, password: &Password,
132        message: &Message,
133    ) -> Result<Signature, Error> {
134        self.get(account)?.sign(password, message)
135    }
136
137    fn sign_derived(
138        &self, account_ref: &StoreAccountRef, password: &Password,
139        derivation: Derivation, message: &Message,
140    ) -> Result<Signature, Error> {
141        self.store
142            .sign_derived(account_ref, password, derivation, message)
143    }
144
145    fn agree(
146        &self, account: &StoreAccountRef, password: &Password, other: &Public,
147    ) -> Result<Secret, Error> {
148        self.store.agree(account, password, other)
149    }
150
151    fn decrypt(
152        &self, account: &StoreAccountRef, password: &Password,
153        shared_mac: &[u8], message: &[u8],
154    ) -> Result<Vec<u8>, Error> {
155        let account = self.get(account)?;
156        account.decrypt(password, shared_mac, message)
157    }
158
159    fn create_vault(
160        &self, name: &str, password: &Password,
161    ) -> Result<(), Error> {
162        self.store.create_vault(name, password)
163    }
164
165    fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error> {
166        self.store.open_vault(name, password)
167    }
168
169    fn close_vault(&self, name: &str) -> Result<(), Error> {
170        self.store.close_vault(name)
171    }
172
173    fn list_vaults(&self) -> Result<Vec<String>, Error> {
174        self.store.list_vaults()
175    }
176
177    fn list_opened_vaults(&self) -> Result<Vec<String>, Error> {
178        self.store.list_opened_vaults()
179    }
180
181    fn change_vault_password(
182        &self, name: &str, new_password: &Password,
183    ) -> Result<(), Error> {
184        self.store.change_vault_password(name, new_password)
185    }
186
187    fn change_account_vault(
188        &self, vault: SecretVaultRef, account: StoreAccountRef,
189    ) -> Result<StoreAccountRef, Error> {
190        self.store.change_account_vault(vault, account)
191    }
192
193    fn get_vault_meta(&self, name: &str) -> Result<String, Error> {
194        self.store.get_vault_meta(name)
195    }
196
197    fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error> {
198        self.store.set_vault_meta(name, meta)
199    }
200}
201
202impl SecretStore for CfxStore {
203    fn raw_secret(
204        &self, account: &StoreAccountRef, password: &Password,
205    ) -> Result<OpaqueSecret, Error> {
206        Ok(OpaqueSecret(self.get(account)?.crypto.secret(password)?))
207    }
208
209    fn import_wallet(
210        &self, vault: SecretVaultRef, json: &[u8], password: &Password,
211        gen_id: bool,
212    ) -> Result<StoreAccountRef, Error> {
213        let json_keyfile = json::KeyFile::load(json).map_err(|_| {
214            Error::InvalidKeyFile("Invalid JSON format".to_owned())
215        })?;
216        let mut safe_account =
217            SafeAccount::from_file(json_keyfile, None, &None)?;
218
219        if gen_id {
220            safe_account.id = Random::random();
221        }
222
223        let secret = safe_account
224            .crypto
225            .secret(password)
226            .map_err(|_| Error::InvalidPassword)?;
227        safe_account.address = KeyPair::from_secret(secret)?.address();
228        self.store.import(vault, safe_account)
229    }
230
231    fn test_password(
232        &self, account: &StoreAccountRef, password: &Password,
233    ) -> Result<bool, Error> {
234        let account = self.get(account)?;
235        Ok(account.check_password(password))
236    }
237
238    fn copy_account(
239        &self, new_store: &dyn SimpleSecretStore, new_vault: SecretVaultRef,
240        account: &StoreAccountRef, password: &Password,
241        new_password: &Password,
242    ) -> Result<(), Error> {
243        let account = self.get(account)?;
244        let secret = account.crypto.secret(password)?;
245        new_store.insert_account(new_vault, secret, new_password)?;
246        Ok(())
247    }
248
249    fn public(
250        &self, account: &StoreAccountRef, password: &Password,
251    ) -> Result<Public, Error> {
252        let account = self.get(account)?;
253        account.public(password)
254    }
255
256    fn uuid(&self, account: &StoreAccountRef) -> Result<Uuid, Error> {
257        let account = self.get(account)?;
258        Ok(account.id.into())
259    }
260
261    fn name(&self, account: &StoreAccountRef) -> Result<String, Error> {
262        let account = self.get(account)?;
263        Ok(account.name)
264    }
265
266    fn meta(&self, account: &StoreAccountRef) -> Result<String, Error> {
267        let account = self.get(account)?;
268        Ok(account.meta)
269    }
270
271    fn set_name(
272        &self, account_ref: &StoreAccountRef, name: String,
273    ) -> Result<(), Error> {
274        let old = self.get(account_ref)?;
275        let mut safe_account = old.clone();
276        safe_account.name = name;
277
278        // save to file
279        self.store.update(account_ref, old, safe_account)
280    }
281
282    fn set_meta(
283        &self, account_ref: &StoreAccountRef, meta: String,
284    ) -> Result<(), Error> {
285        let old = self.get(account_ref)?;
286        let mut safe_account = old.clone();
287        safe_account.meta = meta;
288
289        // save to file
290        self.store.update(account_ref, old, safe_account)
291    }
292
293    fn local_path(&self) -> PathBuf {
294        self.store.dir.path().cloned().unwrap_or_else(PathBuf::new)
295    }
296
297    fn list_geth_accounts(&self, testnet: bool) -> Vec<Address> {
298        import::read_geth_accounts(testnet)
299    }
300
301    fn import_geth_accounts(
302        &self, vault: SecretVaultRef, desired: Vec<Address>, testnet: bool,
303    ) -> Result<Vec<StoreAccountRef>, Error> {
304        let imported_addresses = match vault {
305            SecretVaultRef::Root => import::import_geth_accounts(
306                &*self.store.dir,
307                desired.into_iter().collect(),
308                testnet,
309            ),
310            SecretVaultRef::Vault(vault_name) => {
311                if let Some(vault) = self.store.vaults.lock().get(&vault_name) {
312                    import::import_geth_accounts(
313                        vault.as_key_directory(),
314                        desired.into_iter().collect(),
315                        testnet,
316                    )
317                } else {
318                    Err(Error::VaultNotFound)
319                }
320            }
321        };
322
323        imported_addresses
324            .map(|a| a.into_iter().map(StoreAccountRef::root).collect())
325    }
326}
327
328/// Similar to `CfxStore` but may store many accounts (with different passwords)
329/// for the same `Address`
330pub struct CfxMultiStore {
331    dir: Box<dyn KeyDirectory>,
332    iterations: u32,
333    // order lock: cache, then vaults
334    cache: RwLock<BTreeMap<StoreAccountRef, Vec<SafeAccount>>>,
335    vaults: Mutex<HashMap<String, Box<dyn VaultKeyDirectory>>>,
336    timestamp: Mutex<Timestamp>,
337}
338
339struct Timestamp {
340    dir_hash: Option<u64>,
341    last_checked: Instant,
342    refresh_time: Duration,
343}
344
345impl CfxMultiStore {
346    /// Open new multi-accounts store with given key directory backend.
347    pub fn open(directory: Box<dyn KeyDirectory>) -> Result<Self, Error> {
348        Self::open_with_iterations(directory, KEY_ITERATIONS as u32)
349    }
350
351    /// Open new multi-accounts store with given key directory backend and
352    /// custom number of iterations for new keys.
353    pub fn open_with_iterations(
354        directory: Box<dyn KeyDirectory>, iterations: u32,
355    ) -> Result<Self, Error> {
356        let store = CfxMultiStore {
357            dir: directory,
358            vaults: Mutex::new(HashMap::new()),
359            iterations,
360            cache: Default::default(),
361            timestamp: Mutex::new(Timestamp {
362                dir_hash: None,
363                last_checked: Instant::now(),
364                // by default we never refresh accounts
365                refresh_time: Duration::from_secs(u64::max_value()),
366            }),
367        };
368        store.reload_accounts()?;
369        Ok(store)
370    }
371
372    /// Modify account refresh timeout - how often they are re-read from
373    /// `KeyDirectory`.
374    ///
375    /// Setting this to low values (or 0) will cause new accounts to be picked
376    /// up quickly, although it may induce heavy disk reads and is not
377    /// recommended if you manage many keys (say over 10k).
378    ///
379    /// By default refreshing is disabled, so only accounts created using this
380    /// instance of `CfxStore` are taken into account.
381    pub fn set_refresh_time(&self, time: Duration) {
382        self.timestamp.lock().refresh_time = time;
383    }
384
385    fn reload_if_changed(&self) -> Result<(), Error> {
386        let mut last_timestamp = self.timestamp.lock();
387        let now = Instant::now();
388
389        if now - last_timestamp.last_checked > last_timestamp.refresh_time {
390            let dir_hash = Some(self.dir.unique_repr()?);
391            last_timestamp.last_checked = now;
392
393            if last_timestamp.dir_hash == dir_hash {
394                return Ok(());
395            }
396
397            self.reload_accounts()?;
398            last_timestamp.dir_hash = dir_hash;
399        }
400
401        Ok(())
402    }
403
404    fn reload_accounts(&self) -> Result<(), Error> {
405        let mut cache = self.cache.write();
406
407        let mut new_accounts = BTreeMap::new();
408        for account in self.dir.load()? {
409            let account_ref = StoreAccountRef::root(account.address);
410            new_accounts
411                .entry(account_ref)
412                .or_insert_with(Vec::new)
413                .push(account);
414        }
415        for (vault_name, vault) in &*self.vaults.lock() {
416            for account in vault.load()? {
417                let account_ref =
418                    StoreAccountRef::vault(vault_name, account.address);
419                new_accounts
420                    .entry(account_ref)
421                    .or_insert_with(Vec::new)
422                    .push(account);
423            }
424        }
425
426        *cache = new_accounts;
427        Ok(())
428    }
429
430    fn get_accounts(
431        &self, account: &StoreAccountRef,
432    ) -> Result<Vec<SafeAccount>, Error> {
433        let from_cache = |account| {
434            let cache = self.cache.read();
435            if let Some(accounts) = cache.get(account) {
436                if !accounts.is_empty() {
437                    return Some(accounts.clone());
438                }
439            }
440
441            None
442        };
443
444        match from_cache(account) {
445            Some(accounts) => Ok(accounts),
446            None => {
447                self.reload_if_changed()?;
448                from_cache(account).ok_or(Error::InvalidAccount)
449            }
450        }
451    }
452
453    fn get_matching(
454        &self, account: &StoreAccountRef, password: &Password,
455    ) -> Result<Vec<SafeAccount>, Error> {
456        let accounts = self.get_accounts(account)?;
457
458        Ok(accounts
459            .into_iter()
460            .filter(|acc| acc.check_password(password))
461            .collect())
462    }
463
464    fn import(
465        &self, vault: SecretVaultRef, account: SafeAccount,
466    ) -> Result<StoreAccountRef, Error> {
467        // save to file
468        let account = match vault {
469            SecretVaultRef::Root => self.dir.insert(account)?,
470            SecretVaultRef::Vault(ref vault_name) => self
471                .vaults
472                .lock()
473                .get_mut(vault_name)
474                .ok_or(Error::VaultNotFound)?
475                .insert(account)?,
476        };
477
478        // update cache
479        let account_ref = StoreAccountRef::new(vault, account.address.clone());
480        let mut cache = self.cache.write();
481        cache
482            .entry(account_ref.clone())
483            .or_insert_with(Vec::new)
484            .push(account);
485
486        Ok(account_ref)
487    }
488
489    fn update(
490        &self, account_ref: &StoreAccountRef, old: SafeAccount,
491        new: SafeAccount,
492    ) -> Result<(), Error> {
493        // save to file
494        let account = match account_ref.vault {
495            SecretVaultRef::Root => self.dir.update(new)?,
496            SecretVaultRef::Vault(ref vault_name) => self
497                .vaults
498                .lock()
499                .get_mut(vault_name)
500                .ok_or(Error::VaultNotFound)?
501                .update(new)?,
502        };
503
504        // update cache
505        let mut cache = self.cache.write();
506        let accounts =
507            cache.entry(account_ref.clone()).or_insert_with(Vec::new);
508        // Remove old account
509        accounts.retain(|acc| acc != &old);
510        // And push updated to the end
511        accounts.push(account);
512        Ok(())
513    }
514
515    fn remove_safe_account(
516        &self, account_ref: &StoreAccountRef, account: &SafeAccount,
517    ) -> Result<(), Error> {
518        // Remove from dir
519        match account_ref.vault {
520            SecretVaultRef::Root => self.dir.remove(&account)?,
521            SecretVaultRef::Vault(ref vault_name) => self
522                .vaults
523                .lock()
524                .get(vault_name)
525                .ok_or(Error::VaultNotFound)?
526                .remove(&account)?,
527        };
528
529        // Remove from cache
530        let mut cache = self.cache.write();
531        let is_empty = {
532            if let Some(accounts) = cache.get_mut(account_ref) {
533                if let Some(position) =
534                    accounts.iter().position(|acc| acc == account)
535                {
536                    accounts.remove(position);
537                }
538                accounts.is_empty()
539            } else {
540                false
541            }
542        };
543
544        if is_empty {
545            cache.remove(account_ref);
546        }
547
548        Ok(())
549    }
550
551    fn generate(
552        &self, secret: Secret, derivation: Derivation,
553    ) -> Result<ExtendedKeyPair, Error> {
554        let mut extended = ExtendedKeyPair::new(secret);
555        match derivation {
556            Derivation::Hierarchical(path) => {
557                for path_item in path {
558                    extended = extended.derive(
559                        if path_item.soft {
560                            cfxkey::Derivation::Soft(path_item.index)
561                        } else {
562                            cfxkey::Derivation::Hard(path_item.index)
563                        },
564                    )?;
565                }
566            }
567            Derivation::SoftHash(h256) => {
568                extended = extended.derive(cfxkey::Derivation::Soft(h256))?;
569            }
570            Derivation::HardHash(h256) => {
571                extended = extended.derive(cfxkey::Derivation::Hard(h256))?;
572            }
573        }
574        Ok(extended)
575    }
576}
577
578impl SimpleSecretStore for CfxMultiStore {
579    fn insert_account(
580        &self, vault: SecretVaultRef, secret: Secret, password: &Password,
581    ) -> Result<StoreAccountRef, Error> {
582        let keypair =
583            KeyPair::from_secret(secret).map_err(|_| Error::CreationFailed)?;
584        let id: [u8; 16] = Random::random();
585        let account = SafeAccount::create(
586            &keypair,
587            id,
588            password,
589            self.iterations,
590            "".to_owned(),
591            "{}".to_owned(),
592        )?;
593        self.import(vault, account)
594    }
595
596    fn insert_derived(
597        &self, vault: SecretVaultRef, account_ref: &StoreAccountRef,
598        password: &Password, derivation: Derivation,
599    ) -> Result<StoreAccountRef, Error> {
600        let accounts = self.get_matching(account_ref, password)?;
601        if let Some(account) = accounts.first() {
602            let extended =
603                self.generate(account.crypto.secret(password)?, derivation)?;
604            self.insert_account(
605                vault,
606                extended.secret().as_raw().clone(),
607                password,
608            )
609        } else {
610            Err(Error::InvalidPassword)
611        }
612    }
613
614    fn generate_derived(
615        &self, account_ref: &StoreAccountRef, password: &Password,
616        derivation: Derivation,
617    ) -> Result<Address, Error> {
618        let accounts = self.get_matching(&account_ref, password)?;
619        if let Some(account) = accounts.first() {
620            let extended =
621                self.generate(account.crypto.secret(password)?, derivation)?;
622            Ok(cfxkey::public_to_address(extended.public().public(), true))
623        } else {
624            Err(Error::InvalidPassword)
625        }
626    }
627
628    fn sign_derived(
629        &self, account_ref: &StoreAccountRef, password: &Password,
630        derivation: Derivation, message: &Message,
631    ) -> Result<Signature, Error> {
632        let accounts = self.get_matching(&account_ref, password)?;
633        if let Some(account) = accounts.first() {
634            let extended =
635                self.generate(account.crypto.secret(password)?, derivation)?;
636            let secret = extended.secret().as_raw();
637            Ok(cfxkey::sign(&secret, message)?)
638        } else {
639            Err(Error::InvalidPassword)
640        }
641    }
642
643    fn account_ref(&self, address: &Address) -> Result<StoreAccountRef, Error> {
644        let read_from_cache = |address: &Address| {
645            use std::collections::Bound;
646            let cache = self.cache.read();
647            let mut r = cache
648                .range((Bound::Included(*address), Bound::Included(*address)));
649            r.next().map(|(k, _)| k.clone())
650        };
651
652        match read_from_cache(address) {
653            Some(account) => Ok(account),
654            None => {
655                self.reload_if_changed()?;
656                read_from_cache(address).ok_or(Error::InvalidAccount)
657            }
658        }
659    }
660
661    fn accounts(&self) -> Result<Vec<StoreAccountRef>, Error> {
662        self.reload_if_changed()?;
663        Ok(self.cache.read().keys().cloned().collect())
664    }
665
666    fn remove_account(
667        &self, account_ref: &StoreAccountRef, password: &Password,
668    ) -> Result<(), Error> {
669        let accounts = self.get_matching(account_ref, password)?;
670
671        if let Some(account) = accounts.first() {
672            self.remove_safe_account(account_ref, &account)
673        } else {
674            Err(Error::InvalidPassword)
675        }
676    }
677
678    fn change_password(
679        &self, account_ref: &StoreAccountRef, old_password: &Password,
680        new_password: &Password,
681    ) -> Result<(), Error> {
682        let accounts = self.get_matching(account_ref, old_password)?;
683
684        if accounts.is_empty() {
685            return Err(Error::InvalidPassword);
686        }
687
688        for account in accounts {
689            // Change password
690            let new_account = account.change_password(
691                old_password,
692                new_password,
693                self.iterations,
694            )?;
695            self.update(account_ref, account, new_account)?;
696        }
697
698        Ok(())
699    }
700
701    fn export_account(
702        &self, account_ref: &StoreAccountRef, password: &Password,
703    ) -> Result<OpaqueKeyFile, Error> {
704        self.get_matching(account_ref, password)?
705            .into_iter()
706            .nth(0)
707            .map(Into::into)
708            .ok_or(Error::InvalidPassword)
709    }
710
711    fn sign(
712        &self, account: &StoreAccountRef, password: &Password,
713        message: &Message,
714    ) -> Result<Signature, Error> {
715        let accounts = self.get_matching(account, password)?;
716        match accounts.first() {
717            Some(ref account) => account.sign(password, message),
718            None => Err(Error::InvalidPassword),
719        }
720    }
721
722    fn decrypt(
723        &self, account: &StoreAccountRef, password: &Password,
724        shared_mac: &[u8], message: &[u8],
725    ) -> Result<Vec<u8>, Error> {
726        let accounts = self.get_matching(account, password)?;
727        match accounts.first() {
728            Some(ref account) => account.decrypt(password, shared_mac, message),
729            None => Err(Error::InvalidPassword),
730        }
731    }
732
733    fn agree(
734        &self, account: &StoreAccountRef, password: &Password, other: &Public,
735    ) -> Result<Secret, Error> {
736        let accounts = self.get_matching(account, password)?;
737        match accounts.first() {
738            Some(ref account) => account.agree(password, other),
739            None => Err(Error::InvalidPassword),
740        }
741    }
742
743    fn create_vault(
744        &self, name: &str, password: &Password,
745    ) -> Result<(), Error> {
746        let is_vault_created = {
747            // lock border
748            let mut vaults = self.vaults.lock();
749            if !vaults.contains_key(&name.to_owned()) {
750                let vault_provider = self
751                    .dir
752                    .as_vault_provider()
753                    .ok_or(Error::VaultsAreNotSupported)?;
754                let vault = vault_provider
755                    .create(name, VaultKey::new(password, self.iterations))?;
756                vaults.insert(name.to_owned(), vault);
757                true
758            } else {
759                false
760            }
761        };
762
763        if is_vault_created {
764            self.reload_accounts()?;
765        }
766
767        Ok(())
768    }
769
770    fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error> {
771        let is_vault_opened = {
772            // lock border
773            let mut vaults = self.vaults.lock();
774            if !vaults.contains_key(&name.to_owned()) {
775                let vault_provider = self
776                    .dir
777                    .as_vault_provider()
778                    .ok_or(Error::VaultsAreNotSupported)?;
779                let vault = vault_provider
780                    .open(name, VaultKey::new(password, self.iterations))?;
781                vaults.insert(name.to_owned(), vault);
782                true
783            } else {
784                false
785            }
786        };
787
788        if is_vault_opened {
789            self.reload_accounts()?;
790        }
791
792        Ok(())
793    }
794
795    fn close_vault(&self, name: &str) -> Result<(), Error> {
796        let is_vault_removed =
797            self.vaults.lock().remove(&name.to_owned()).is_some();
798        if is_vault_removed {
799            self.reload_accounts()?;
800        }
801        Ok(())
802    }
803
804    fn list_vaults(&self) -> Result<Vec<String>, Error> {
805        let vault_provider = self
806            .dir
807            .as_vault_provider()
808            .ok_or(Error::VaultsAreNotSupported)?;
809        vault_provider.list_vaults()
810    }
811
812    fn list_opened_vaults(&self) -> Result<Vec<String>, Error> {
813        Ok(self.vaults.lock().keys().cloned().collect())
814    }
815
816    fn change_vault_password(
817        &self, name: &str, new_password: &Password,
818    ) -> Result<(), Error> {
819        let old_key = self
820            .vaults
821            .lock()
822            .get(name)
823            .map(|v| v.key())
824            .ok_or(Error::VaultNotFound)?;
825        let vault_provider = self
826            .dir
827            .as_vault_provider()
828            .ok_or(Error::VaultsAreNotSupported)?;
829        let vault = vault_provider.open(name, old_key)?;
830        match vault.set_key(VaultKey::new(new_password, self.iterations)) {
831            Ok(_) => self
832                .close_vault(name)
833                .and_then(|_| self.open_vault(name, new_password)),
834            Err(SetKeyError::Fatal(err)) => {
835                let _ = self.close_vault(name);
836                Err(err)
837            }
838            Err(SetKeyError::NonFatalNew(err)) => {
839                let _ = self
840                    .close_vault(name)
841                    .and_then(|_| self.open_vault(name, new_password));
842                Err(err)
843            }
844            Err(SetKeyError::NonFatalOld(err)) => Err(err),
845        }
846    }
847
848    fn change_account_vault(
849        &self, vault: SecretVaultRef, account_ref: StoreAccountRef,
850    ) -> Result<StoreAccountRef, Error> {
851        if account_ref.vault == vault {
852            return Ok(account_ref);
853        }
854
855        let account = self
856            .get_accounts(&account_ref)?
857            .into_iter()
858            .nth(0)
859            .ok_or(Error::InvalidAccount)?;
860        let new_account_ref = self.import(vault, account.clone())?;
861        self.remove_safe_account(&account_ref, &account)?;
862        self.reload_accounts()?;
863        Ok(new_account_ref)
864    }
865
866    fn get_vault_meta(&self, name: &str) -> Result<String, Error> {
867        // vault meta contains password hint
868        // => allow reading meta even if vault is not yet opened
869        self.vaults
870            .lock()
871            .get(name)
872            .map(|v| v.meta())
873            .ok_or(Error::VaultNotFound)
874            .or_else(|_| {
875                let vault_provider = self
876                    .dir
877                    .as_vault_provider()
878                    .ok_or(Error::VaultsAreNotSupported)?;
879                vault_provider.vault_meta(name)
880            })
881    }
882
883    fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error> {
884        self.vaults
885            .lock()
886            .get(name)
887            .ok_or(Error::VaultNotFound)
888            .and_then(|v| v.set_meta(meta))
889    }
890}
891
892#[cfg(test)]
893mod tests {
894    use super::{CfxMultiStore, CfxStore};
895    use crate::{
896        accounts_dir::{KeyDirectory, MemoryDirectory, RootDiskDirectory},
897        secret_store::{
898            Derivation, SecretStore, SecretVaultRef, SimpleSecretStore,
899            StoreAccountRef,
900        },
901    };
902    use cfx_types::H256;
903    use cfxkey::{Generator, KeyPair, Random};
904    use tempfile::{tempdir, TempDir};
905    fn keypair() -> KeyPair { Random.generate().unwrap() }
906
907    fn store() -> CfxStore {
908        CfxStore::open(Box::new(MemoryDirectory::default()))
909            .expect("MemoryDirectory always load successfuly; qed")
910    }
911
912    fn multi_store() -> CfxMultiStore {
913        CfxMultiStore::open(Box::new(MemoryDirectory::default()))
914            .expect("MemoryDirectory always load successfuly; qed")
915    }
916
917    struct RootDiskDirectoryGuard {
918        pub key_dir: Option<Box<dyn KeyDirectory>>,
919        _path: TempDir,
920    }
921
922    impl RootDiskDirectoryGuard {
923        pub fn new() -> Self {
924            let temp_path = tempdir().unwrap();
925            let disk_dir =
926                Box::new(RootDiskDirectory::create(temp_path.path()).unwrap());
927
928            RootDiskDirectoryGuard {
929                key_dir: Some(disk_dir),
930                _path: temp_path,
931            }
932        }
933    }
934
935    #[test]
936    fn should_insert_account_successfully() {
937        // given
938        let store = store();
939        let keypair = keypair();
940
941        // when
942        let passwd = "test".into();
943        let address = store
944            .insert_account(
945                SecretVaultRef::Root,
946                keypair.secret().clone(),
947                &passwd,
948            )
949            .unwrap();
950
951        // then
952        assert_eq!(address, StoreAccountRef::root(keypair.address()));
953        assert!(store.get(&address).is_ok(), "Should contain account.");
954        assert_eq!(
955            store.accounts().unwrap().len(),
956            1,
957            "Should have one account."
958        );
959    }
960
961    #[test]
962    fn should_update_meta_and_name() {
963        // given
964        let store = store();
965        let keypair = keypair();
966        let passwd = "test".into();
967        let address = store
968            .insert_account(
969                SecretVaultRef::Root,
970                keypair.secret().clone(),
971                &passwd,
972            )
973            .unwrap();
974        assert_eq!(&store.meta(&address).unwrap(), "{}");
975        assert_eq!(&store.name(&address).unwrap(), "");
976
977        // when
978        store.set_meta(&address, "meta".into()).unwrap();
979        store.set_name(&address, "name".into()).unwrap();
980
981        // then
982        assert_eq!(&store.meta(&address).unwrap(), "meta");
983        assert_eq!(&store.name(&address).unwrap(), "name");
984        assert_eq!(store.accounts().unwrap().len(), 1);
985    }
986
987    #[test]
988    fn should_remove_account() {
989        // given
990        let store = store();
991        let passwd = "test".into();
992        let keypair = keypair();
993        let address = store
994            .insert_account(
995                SecretVaultRef::Root,
996                keypair.secret().clone(),
997                &passwd,
998            )
999            .unwrap();
1000
1001        // when
1002        store.remove_account(&address, &passwd).unwrap();
1003
1004        // then
1005        assert_eq!(
1006            store.accounts().unwrap().len(),
1007            0,
1008            "Should remove account."
1009        );
1010    }
1011
1012    #[test]
1013    fn should_return_true_if_password_is_correct() {
1014        // given
1015        let store = store();
1016        let passwd = "test".into();
1017        let keypair = keypair();
1018        let address = store
1019            .insert_account(
1020                SecretVaultRef::Root,
1021                keypair.secret().clone(),
1022                &passwd,
1023            )
1024            .unwrap();
1025
1026        // when
1027        let res1 = store.test_password(&address, &"x".into()).unwrap();
1028        let res2 = store.test_password(&address, &passwd).unwrap();
1029
1030        assert!(!res1, "First password should be invalid.");
1031        assert!(res2, "Second password should be correct.");
1032    }
1033
1034    #[test]
1035    fn multistore_should_be_able_to_have_the_same_account_twice() {
1036        // given
1037        let store = multi_store();
1038        let passwd1 = "test".into();
1039        let passwd2 = "xyz".into();
1040        let keypair = keypair();
1041        let address = store
1042            .insert_account(
1043                SecretVaultRef::Root,
1044                keypair.secret().clone(),
1045                &passwd1,
1046            )
1047            .unwrap();
1048        let address2 = store
1049            .insert_account(
1050                SecretVaultRef::Root,
1051                keypair.secret().clone(),
1052                &passwd2,
1053            )
1054            .unwrap();
1055        assert_eq!(address, address2);
1056
1057        // when
1058        assert!(
1059            store.remove_account(&address, &passwd1).is_ok(),
1060            "First password should work."
1061        );
1062        assert_eq!(store.accounts().unwrap().len(), 1);
1063
1064        assert!(
1065            store.remove_account(&address, &passwd2).is_ok(),
1066            "Second password should work too."
1067        );
1068        assert_eq!(store.accounts().unwrap().len(), 0);
1069    }
1070
1071    #[test]
1072    fn should_copy_account() {
1073        // given
1074        let store = store();
1075        let passwd1 = "test".into();
1076        let passwd2 = "xzy".into();
1077        let multi_store = multi_store();
1078        let keypair = keypair();
1079        let address = store
1080            .insert_account(
1081                SecretVaultRef::Root,
1082                keypair.secret().clone(),
1083                &passwd1,
1084            )
1085            .unwrap();
1086        assert_eq!(multi_store.accounts().unwrap().len(), 0);
1087
1088        // when
1089        store
1090            .copy_account(
1091                &multi_store,
1092                SecretVaultRef::Root,
1093                &address,
1094                &passwd1,
1095                &passwd2,
1096            )
1097            .unwrap();
1098
1099        // then
1100        assert!(
1101            store.test_password(&address, &passwd1).unwrap(),
1102            "First password should work for store."
1103        );
1104        assert!(
1105            multi_store
1106                .sign(&address, &passwd2, &Default::default())
1107                .is_ok(),
1108            "Second password should work for second store."
1109        );
1110        assert_eq!(multi_store.accounts().unwrap().len(), 1);
1111    }
1112
1113    #[test]
1114    fn should_create_and_open_vaults() {
1115        // given
1116        let mut dir = RootDiskDirectoryGuard::new();
1117        let store = CfxStore::open(dir.key_dir.take().unwrap()).unwrap();
1118        let name1 = "vault1";
1119        let password1 = "password1".into();
1120        let name2 = "vault2";
1121        let password2 = "password2".into();
1122        let keypair1 = keypair();
1123        let keypair2 = keypair();
1124        let keypair3 = keypair();
1125        let password3 = "password3".into();
1126
1127        // when
1128        store.create_vault(name1, &password1).unwrap();
1129        store.create_vault(name2, &password2).unwrap();
1130
1131        // then [can create vaults] ^^^
1132
1133        // and when
1134        store
1135            .insert_account(
1136                SecretVaultRef::Vault(name1.to_owned()),
1137                keypair1.secret().clone(),
1138                &password1,
1139            )
1140            .unwrap();
1141        store
1142            .insert_account(
1143                SecretVaultRef::Vault(name2.to_owned()),
1144                keypair2.secret().clone(),
1145                &password2,
1146            )
1147            .unwrap();
1148        store
1149            .insert_account(
1150                SecretVaultRef::Root,
1151                keypair3.secret().clone(),
1152                &password3,
1153            )
1154            .unwrap();
1155        store
1156            .insert_account(
1157                SecretVaultRef::Vault("vault3".to_owned()),
1158                keypair1.secret().clone(),
1159                &password3,
1160            )
1161            .unwrap_err();
1162        let accounts = store.accounts().unwrap();
1163
1164        // then [can create accounts in vaults]
1165        assert_eq!(accounts.len(), 3);
1166        assert!(accounts.iter().any(|a| a.vault == SecretVaultRef::Root));
1167        assert!(accounts
1168            .iter()
1169            .any(|a| a.vault == SecretVaultRef::Vault(name1.to_owned())));
1170        assert!(accounts
1171            .iter()
1172            .any(|a| a.vault == SecretVaultRef::Vault(name2.to_owned())));
1173
1174        // and when
1175        store.close_vault(name1).unwrap();
1176        store.close_vault(name2).unwrap();
1177        store.close_vault("vault3").unwrap();
1178        let accounts = store.accounts().unwrap();
1179
1180        // then [can close vaults + accounts from vaults disappear]
1181        assert_eq!(accounts.len(), 1);
1182        assert!(accounts.iter().any(|a| a.vault == SecretVaultRef::Root));
1183
1184        // and when
1185        store.open_vault(name1, &password2).unwrap_err();
1186        store.open_vault(name2, &password1).unwrap_err();
1187        store.open_vault(name1, &password1).unwrap();
1188        store.open_vault(name2, &password2).unwrap();
1189        let accounts = store.accounts().unwrap();
1190
1191        // then [can check vaults on open + can reopen vaults + accounts from
1192        // vaults appear]
1193        assert_eq!(accounts.len(), 3);
1194        assert!(accounts.iter().any(|a| a.vault == SecretVaultRef::Root));
1195        assert!(accounts
1196            .iter()
1197            .any(|a| a.vault == SecretVaultRef::Vault(name1.to_owned())));
1198        assert!(accounts
1199            .iter()
1200            .any(|a| a.vault == SecretVaultRef::Vault(name2.to_owned())));
1201    }
1202
1203    #[test]
1204    fn should_move_vault_acounts() {
1205        // given
1206        let mut dir = RootDiskDirectoryGuard::new();
1207        let store = CfxStore::open(dir.key_dir.take().unwrap()).unwrap();
1208        let name1 = "vault1";
1209        let password1 = "password1".into();
1210        let name2 = "vault2";
1211        let password2 = "password2".into();
1212        let password3 = "password3".into();
1213        let keypair1 = keypair();
1214        let keypair2 = keypair();
1215        let keypair3 = keypair();
1216
1217        // when
1218        store.create_vault(name1, &password1).unwrap();
1219        store.create_vault(name2, &password2).unwrap();
1220        let account1 = store
1221            .insert_account(
1222                SecretVaultRef::Vault(name1.to_owned()),
1223                keypair1.secret().clone(),
1224                &password1,
1225            )
1226            .unwrap();
1227        let account2 = store
1228            .insert_account(
1229                SecretVaultRef::Vault(name1.to_owned()),
1230                keypair2.secret().clone(),
1231                &password1,
1232            )
1233            .unwrap();
1234        let account3 = store
1235            .insert_account(
1236                SecretVaultRef::Root,
1237                keypair3.secret().clone(),
1238                &password3,
1239            )
1240            .unwrap();
1241
1242        // then
1243        let account1 = store
1244            .change_account_vault(SecretVaultRef::Root, account1)
1245            .unwrap();
1246        let account2 = store
1247            .change_account_vault(
1248                SecretVaultRef::Vault(name2.to_owned()),
1249                account2,
1250            )
1251            .unwrap();
1252        let account3 = store
1253            .change_account_vault(
1254                SecretVaultRef::Vault(name2.to_owned()),
1255                account3,
1256            )
1257            .unwrap();
1258        let accounts = store.accounts().unwrap();
1259        assert_eq!(accounts.len(), 3);
1260        assert!(accounts
1261            .iter()
1262            .any(|a| a == &StoreAccountRef::root(account1.address.clone())));
1263        assert!(accounts
1264            .iter()
1265            .any(|a| a
1266                == &StoreAccountRef::vault(name2, account2.address.clone())));
1267        assert!(accounts
1268            .iter()
1269            .any(|a| a
1270                == &StoreAccountRef::vault(name2, account3.address.clone())));
1271
1272        // and then
1273        assert_eq!(
1274            store
1275                .meta(&StoreAccountRef::root(account1.address))
1276                .unwrap(),
1277            r#"{}"#
1278        );
1279        assert_eq!(
1280            store
1281                .meta(&StoreAccountRef::vault("vault2", account2.address))
1282                .unwrap(),
1283            r#"{"vault":"vault2"}"#
1284        );
1285        assert_eq!(
1286            store
1287                .meta(&StoreAccountRef::vault("vault2", account3.address))
1288                .unwrap(),
1289            r#"{"vault":"vault2"}"#
1290        );
1291    }
1292
1293    #[test]
1294    fn should_not_remove_account_when_moving_to_self() {
1295        // given
1296        let mut dir = RootDiskDirectoryGuard::new();
1297        let store = CfxStore::open(dir.key_dir.take().unwrap()).unwrap();
1298        let password1 = "password1".into();
1299        let keypair1 = keypair();
1300
1301        // when
1302        let account1 = store
1303            .insert_account(
1304                SecretVaultRef::Root,
1305                keypair1.secret().clone(),
1306                &password1,
1307            )
1308            .unwrap();
1309        store
1310            .change_account_vault(SecretVaultRef::Root, account1)
1311            .unwrap();
1312
1313        // then
1314        let accounts = store.accounts().unwrap();
1315        assert_eq!(accounts.len(), 1);
1316    }
1317
1318    #[test]
1319    fn should_remove_account_from_vault() {
1320        // given
1321        let mut dir = RootDiskDirectoryGuard::new();
1322        let store = CfxStore::open(dir.key_dir.take().unwrap()).unwrap();
1323        let name1 = "vault1";
1324        let password1 = "password1".into();
1325        let keypair1 = keypair();
1326
1327        // when
1328        store.create_vault(name1, &password1).unwrap();
1329        let account1 = store
1330            .insert_account(
1331                SecretVaultRef::Vault(name1.to_owned()),
1332                keypair1.secret().clone(),
1333                &password1,
1334            )
1335            .unwrap();
1336        assert_eq!(store.accounts().unwrap().len(), 1);
1337
1338        // then
1339        store.remove_account(&account1, &password1).unwrap();
1340        assert_eq!(store.accounts().unwrap().len(), 0);
1341    }
1342
1343    #[test]
1344    fn should_not_remove_account_from_vault_when_password_is_incorrect() {
1345        // given
1346        let mut dir = RootDiskDirectoryGuard::new();
1347        let store = CfxStore::open(dir.key_dir.take().unwrap()).unwrap();
1348        let name1 = "vault1";
1349        let password1 = "password1".into();
1350        let password2 = "password2".into();
1351        let keypair1 = keypair();
1352
1353        // when
1354        store.create_vault(name1, &password1).unwrap();
1355        let account1 = store
1356            .insert_account(
1357                SecretVaultRef::Vault(name1.to_owned()),
1358                keypair1.secret().clone(),
1359                &password1,
1360            )
1361            .unwrap();
1362        assert_eq!(store.accounts().unwrap().len(), 1);
1363
1364        // then
1365        store.remove_account(&account1, &password2).unwrap_err();
1366        assert_eq!(store.accounts().unwrap().len(), 1);
1367    }
1368
1369    #[test]
1370    fn should_change_vault_password() {
1371        // given
1372        let mut dir = RootDiskDirectoryGuard::new();
1373        let store = CfxStore::open(dir.key_dir.take().unwrap()).unwrap();
1374        let name = "vault";
1375        let password = "password".into();
1376        let keypair = keypair();
1377
1378        // when
1379        store.create_vault(name, &password).unwrap();
1380        store
1381            .insert_account(
1382                SecretVaultRef::Vault(name.to_owned()),
1383                keypair.secret().clone(),
1384                &password,
1385            )
1386            .unwrap();
1387
1388        // then
1389        assert_eq!(store.accounts().unwrap().len(), 1);
1390        let new_password = "new_password".into();
1391        store.change_vault_password(name, &new_password).unwrap();
1392        assert_eq!(store.accounts().unwrap().len(), 1);
1393
1394        // and when
1395        store.close_vault(name).unwrap();
1396
1397        // then
1398        store.open_vault(name, &new_password).unwrap();
1399        assert_eq!(store.accounts().unwrap().len(), 1);
1400    }
1401
1402    #[test]
1403    fn should_have_different_passwords_for_vault_secret_and_meta() {
1404        // given
1405        let mut dir = RootDiskDirectoryGuard::new();
1406        let store = CfxStore::open(dir.key_dir.take().unwrap()).unwrap();
1407        let name = "vault";
1408        let password = "password".into();
1409        let secret_password = "sec_password".into();
1410        let keypair = keypair();
1411
1412        // when
1413        store.create_vault(name, &password).unwrap();
1414        let account_ref = store
1415            .insert_account(
1416                SecretVaultRef::Vault(name.to_owned()),
1417                keypair.secret().clone(),
1418                &secret_password,
1419            )
1420            .unwrap();
1421
1422        // then
1423        assert_eq!(store.accounts().unwrap().len(), 1);
1424        let new_secret_password = "new_sec_password".into();
1425        store
1426            .change_password(
1427                &account_ref,
1428                &secret_password,
1429                &new_secret_password,
1430            )
1431            .unwrap();
1432        assert_eq!(store.accounts().unwrap().len(), 1);
1433    }
1434
1435    #[test]
1436    fn should_list_opened_vaults() {
1437        // given
1438        let mut dir = RootDiskDirectoryGuard::new();
1439        let store = CfxStore::open(dir.key_dir.take().unwrap()).unwrap();
1440        let name1 = "vault1";
1441        let password1 = "password1".into();
1442        let name2 = "vault2";
1443        let password2 = "password2".into();
1444        let name3 = "vault3";
1445        let password3 = "password3".into();
1446
1447        // when
1448        store.create_vault(name1, &password1).unwrap();
1449        store.create_vault(name2, &password2).unwrap();
1450        store.create_vault(name3, &password3).unwrap();
1451        store.close_vault(name2).unwrap();
1452
1453        // then
1454        let opened_vaults = store.list_opened_vaults().unwrap();
1455        assert_eq!(opened_vaults.len(), 2);
1456        assert!(opened_vaults.iter().any(|v| *v == name1));
1457        assert!(opened_vaults.iter().any(|v| *v == name3));
1458    }
1459
1460    #[test]
1461    fn should_manage_vaults_meta() {
1462        // given
1463        let mut dir = RootDiskDirectoryGuard::new();
1464        let store = CfxStore::open(dir.key_dir.take().unwrap()).unwrap();
1465        let name1 = "vault1";
1466        let password1 = "password1".into();
1467
1468        // when
1469        store.create_vault(name1, &password1).unwrap();
1470
1471        // then
1472        assert_eq!(store.get_vault_meta(name1).unwrap(), "{}".to_owned());
1473        assert!(store.set_vault_meta(name1, "Hello, world!!!").is_ok());
1474        assert_eq!(
1475            store.get_vault_meta(name1).unwrap(),
1476            "Hello, world!!!".to_owned()
1477        );
1478
1479        // and when
1480        store.close_vault(name1).unwrap();
1481        store.open_vault(name1, &password1).unwrap();
1482
1483        // then
1484        assert_eq!(
1485            store.get_vault_meta(name1).unwrap(),
1486            "Hello, world!!!".to_owned()
1487        );
1488
1489        // and when
1490        store.close_vault(name1).unwrap();
1491
1492        // then
1493        assert_eq!(
1494            store.get_vault_meta(name1).unwrap(),
1495            "Hello, world!!!".to_owned()
1496        );
1497        assert!(store.get_vault_meta("vault2").is_err());
1498    }
1499
1500    #[test]
1501    fn should_store_derived_keys() {
1502        // given we have one account in the store
1503        let store = store();
1504        let keypair = keypair();
1505        let address = store
1506            .insert_account(
1507                SecretVaultRef::Root,
1508                keypair.secret().clone(),
1509                &"test".into(),
1510            )
1511            .unwrap();
1512
1513        // when we deriving from that account
1514        let derived = store
1515            .insert_derived(
1516                SecretVaultRef::Root,
1517                &address,
1518                &"test".into(),
1519                Derivation::HardHash(H256::zero()),
1520            )
1521            .unwrap();
1522
1523        // there should be 2 accounts in the store
1524        let accounts = store.accounts().unwrap();
1525        assert_eq!(accounts.len(), 2);
1526
1527        // and we can sign with the derived contract
1528        assert!(
1529            store
1530                .sign(&derived, &"test".into(), &Default::default())
1531                .is_ok(),
1532            "Second password should work for second store."
1533        );
1534    }
1535
1536    #[test]
1537    fn should_save_meta_when_setting_before_password() {
1538        // given
1539        let mut dir = RootDiskDirectoryGuard::new();
1540        let store = CfxStore::open(dir.key_dir.take().unwrap()).unwrap();
1541        let name = "vault";
1542        let password = "password1".into();
1543        let new_password = "password2".into();
1544
1545        // when
1546        store.create_vault(name, &password).unwrap();
1547        store.set_vault_meta(name, "OldMeta").unwrap();
1548        store.change_vault_password(name, &new_password).unwrap();
1549
1550        // then
1551        assert_eq!(store.get_vault_meta(name).unwrap(), "OldMeta".to_owned());
1552    }
1553
1554    #[test]
1555    fn should_export_account() {
1556        // given
1557        let store = store();
1558        let keypair = keypair();
1559        let address = store
1560            .insert_account(
1561                SecretVaultRef::Root,
1562                keypair.secret().clone(),
1563                &"test".into(),
1564            )
1565            .unwrap();
1566
1567        // when
1568        let exported = store.export_account(&address, &"test".into());
1569
1570        // then
1571        assert!(
1572            exported.is_ok(),
1573            "Should export single account: {:?}",
1574            exported
1575        );
1576    }
1577}