cfxcore_accounts/
lib.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
17#![warn(missing_docs)]
18
19//! Account management.
20
21mod account_data;
22mod error;
23mod stores;
24
25use self::{
26    account_data::{AccountData, Unlock},
27    stores::AddressBook,
28};
29
30use std::{
31    collections::HashMap,
32    time::{Duration, Instant},
33};
34
35use cfxkey::{Address, Generator, Message, Password, Public, Random, Secret};
36use cfxstore::{
37    accounts_dir::MemoryDirectory, random_string, CfxMultiStore, CfxStore,
38    OpaqueSecret, SecretStore, SecretVaultRef, SimpleSecretStore,
39    StoreAccountRef,
40};
41use log::warn;
42use parking_lot::RwLock;
43
44pub use cfxkey::Signature;
45pub use cfxstore::{Derivation, Error, IndexDerivation, KeyFile};
46
47pub use self::{account_data::AccountMeta, error::SignError};
48
49type AccountToken = Password;
50
51/// Account management settings.
52#[derive(Debug, Default)]
53pub struct AccountProviderSettings {
54    /// Store raw account secret when unlocking the account permanently.
55    pub unlock_keep_secret: bool,
56    /// Disallowed accounts.
57    pub blacklisted_accounts: Vec<Address>,
58}
59
60/// Account management.
61/// Responsible for unlocking accounts.
62pub struct AccountProvider {
63    /// For performance reasons some methods can re-use unlocked secrets.
64    unlocked_secrets: RwLock<HashMap<StoreAccountRef, OpaqueSecret>>,
65    /// Unlocked account data.
66    unlocked: RwLock<HashMap<StoreAccountRef, AccountData>>,
67    /// Address book.
68    address_book: RwLock<AddressBook>,
69    /// Accounts on disk
70    sstore: Box<dyn SecretStore>,
71    /// Accounts unlocked with rolling tokens
72    transient_sstore: CfxMultiStore,
73    /// When unlocking account permanently we additionally keep a raw secret in
74    /// memory to increase the performance of transaction signing.
75    unlock_keep_secret: bool,
76    /// Disallowed accounts.
77    blacklisted_accounts: Vec<Address>,
78}
79
80fn transient_sstore() -> CfxMultiStore {
81    CfxMultiStore::open(Box::new(MemoryDirectory::default()))
82        .expect("MemoryDirectory load always succeeds; qed")
83}
84
85impl AccountProvider {
86    /// Creates new account provider.
87    pub fn new(
88        sstore: Box<dyn SecretStore>, settings: AccountProviderSettings,
89    ) -> Self {
90        if let Ok(accounts) = sstore.accounts() {
91            for account in accounts
92                .into_iter()
93                .filter(|a| settings.blacklisted_accounts.contains(&a.address))
94            {
95                warn!("Local Account {} has a blacklisted (known to be weak) address and will be ignored",
96					account.address);
97            }
98        }
99
100        // Remove blacklisted accounts from address book.
101        let mut address_book = AddressBook::new(&sstore.local_path());
102        for addr in &settings.blacklisted_accounts {
103            address_book.remove(*addr);
104        }
105
106        AccountProvider {
107            unlocked_secrets: RwLock::new(HashMap::new()),
108            unlocked: RwLock::new(HashMap::new()),
109            address_book: RwLock::new(address_book),
110            sstore,
111            transient_sstore: transient_sstore(),
112            unlock_keep_secret: settings.unlock_keep_secret,
113            blacklisted_accounts: settings.blacklisted_accounts,
114        }
115    }
116
117    /// Creates not disk backed provider.
118    pub fn transient_provider() -> Self {
119        AccountProvider {
120            unlocked_secrets: RwLock::new(HashMap::new()),
121            unlocked: RwLock::new(HashMap::new()),
122            address_book: RwLock::new(AddressBook::transient()),
123            sstore: Box::new(
124                CfxStore::open(Box::new(MemoryDirectory::default()))
125                    .expect("MemoryDirectory load always succeeds; qed"),
126            ),
127            transient_sstore: transient_sstore(),
128            unlock_keep_secret: false,
129            blacklisted_accounts: vec![],
130        }
131    }
132
133    /// Creates new random account.
134    pub fn new_account(&self, password: &Password) -> Result<Address, Error> {
135        self.new_account_and_public(password).map(|d| d.0)
136    }
137
138    /// Creates new random account and returns address and public key
139    pub fn new_account_and_public(
140        &self, password: &Password,
141    ) -> Result<(Address, Public), Error> {
142        let acc = Random
143            .generate()
144            .expect("secp context has generation capabilities; qed");
145        let public = *acc.public();
146        let secret = acc.secret().clone();
147        let account = self.sstore.insert_account(
148            SecretVaultRef::Root,
149            secret,
150            password,
151        )?;
152        Ok((account.address, public))
153    }
154
155    /// Inserts new account into underlying store.
156    /// Does not unlock account!
157    pub fn insert_account(
158        &self, secret: Secret, password: &Password,
159    ) -> Result<Address, Error> {
160        let account = self.sstore.insert_account(
161            SecretVaultRef::Root,
162            secret,
163            password,
164        )?;
165        if self.blacklisted_accounts.contains(&account.address) {
166            self.sstore.remove_account(&account, password)?;
167            return Err(Error::InvalidAccount);
168        }
169        Ok(account.address)
170    }
171
172    /// Generates new derived account based on the existing one
173    /// If password is not provided, account must be unlocked
174    /// New account will be created with the same password (if save: true)
175    pub fn derive_account(
176        &self, address: &Address, password: Option<Password>,
177        derivation: Derivation, save: bool,
178    ) -> Result<Address, SignError> {
179        let account = self.sstore.account_ref(address)?;
180        let password = password
181            .map(Ok)
182            .unwrap_or_else(|| self.password(&account))?;
183        Ok(if save {
184            self.sstore
185                .insert_derived(
186                    SecretVaultRef::Root,
187                    &account,
188                    &password,
189                    derivation,
190                )?
191                .address
192        } else {
193            self.sstore
194                .generate_derived(&account, &password, derivation)?
195        })
196    }
197
198    /// Import a new wallet.
199    pub fn import_wallet(
200        &self, json: &[u8], password: &Password, gen_id: bool,
201    ) -> Result<Address, Error> {
202        let account = self.sstore.import_wallet(
203            SecretVaultRef::Root,
204            json,
205            password,
206            gen_id,
207        )?;
208        if self.blacklisted_accounts.contains(&account.address) {
209            self.sstore.remove_account(&account, password)?;
210            return Err(Error::InvalidAccount);
211        }
212        Ok(account.address)
213    }
214
215    /// Checks whether an account with a given address is present.
216    pub fn has_account(&self, address: Address) -> bool {
217        self.sstore.account_ref(&address).is_ok()
218            && !self.blacklisted_accounts.contains(&address)
219    }
220
221    /// Returns addresses of all accounts.
222    pub fn accounts(&self) -> Result<Vec<Address>, Error> {
223        let accounts = self.sstore.accounts()?;
224        Ok(accounts
225            .into_iter()
226            .map(|a| a.address)
227            .filter(|address| !self.blacklisted_accounts.contains(address))
228            .collect())
229    }
230
231    /// Returns the address of default account.
232    pub fn default_account(&self) -> Result<Address, Error> {
233        Ok(self.accounts()?.first().cloned().unwrap_or_default())
234    }
235
236    /// Returns each address along with metadata.
237    pub fn addresses_info(&self) -> HashMap<Address, AccountMeta> {
238        self.address_book.read().get()
239    }
240
241    /// Returns each address along with metadata.
242    pub fn set_address_name(&self, account: Address, name: String) {
243        self.address_book.write().set_name(account, name)
244    }
245
246    /// Returns each address along with metadata.
247    pub fn set_address_meta(&self, account: Address, meta: String) {
248        self.address_book.write().set_meta(account, meta)
249    }
250
251    /// Removes and address from the address book
252    pub fn remove_address(&self, addr: Address) {
253        self.address_book.write().remove(addr)
254    }
255
256    /// Returns each account along with name and meta.
257    pub fn accounts_info(
258        &self,
259    ) -> Result<HashMap<Address, AccountMeta>, Error> {
260        let r = self
261            .sstore
262            .accounts()?
263            .into_iter()
264            .filter(|a| !self.blacklisted_accounts.contains(&a.address))
265            .map(|a| {
266                (
267                    a.address,
268                    self.account_meta(a.address).ok().unwrap_or_default(),
269                )
270            })
271            .collect();
272        Ok(r)
273    }
274
275    /// Returns each account along with name and meta.
276    pub fn account_meta(&self, address: Address) -> Result<AccountMeta, Error> {
277        let account = self.sstore.account_ref(&address)?;
278        Ok(AccountMeta {
279            name: self.sstore.name(&account)?,
280            meta: self.sstore.meta(&account)?,
281            uuid: self.sstore.uuid(&account).ok().map(Into::into), /* allowed to not have a Uuid */
282        })
283    }
284
285    /// Returns account public key.
286    pub fn account_public(
287        &self, address: Address, password: &Password,
288    ) -> Result<Public, Error> {
289        self.sstore
290            .public(&self.sstore.account_ref(&address)?, password)
291    }
292
293    /// Returns each account along with name and meta.
294    pub fn set_account_name(
295        &self, address: Address, name: String,
296    ) -> Result<(), Error> {
297        self.sstore
298            .set_name(&self.sstore.account_ref(&address)?, name)?;
299        Ok(())
300    }
301
302    /// Returns each account along with name and meta.
303    pub fn set_account_meta(
304        &self, address: Address, meta: String,
305    ) -> Result<(), Error> {
306        self.sstore
307            .set_meta(&self.sstore.account_ref(&address)?, meta)?;
308        Ok(())
309    }
310
311    /// Returns `true` if the password for `account` is `password`. `false` if
312    /// not.
313    pub fn test_password(
314        &self, address: &Address, password: &Password,
315    ) -> Result<bool, Error> {
316        self.sstore
317            .test_password(&self.sstore.account_ref(address)?, password)
318    }
319
320    /// Permanently removes an account.
321    pub fn kill_account(
322        &self, address: &Address, password: &Password,
323    ) -> Result<(), Error> {
324        self.sstore
325            .remove_account(&self.sstore.account_ref(address)?, password)?;
326        Ok(())
327    }
328
329    /// Changes the password of `account` from `password` to `new_password`.
330    /// Fails if incorrect `password` given.
331    pub fn change_password(
332        &self, address: &Address, password: Password, new_password: Password,
333    ) -> Result<(), Error> {
334        self.sstore.change_password(
335            &self.sstore.account_ref(address)?,
336            &password,
337            &new_password,
338        )
339    }
340
341    /// Exports an account for given address.
342    pub fn export_account(
343        &self, address: &Address, password: Password,
344    ) -> Result<KeyFile, Error> {
345        self.sstore
346            .export_account(&self.sstore.account_ref(address)?, &password)
347    }
348
349    /// Helper method used for unlocking accounts.
350    fn unlock_account(
351        &self, address: Address, password: Password, unlock: Unlock,
352    ) -> Result<(), Error> {
353        let account = self.sstore.account_ref(&address)?;
354
355        // check if account is already unlocked permanently, if it is, do
356        // nothing
357        let mut unlocked = self.unlocked.write();
358        if let Some(data) = unlocked.get(&account) {
359            if let Unlock::Perm = data.unlock {
360                return Ok(());
361            }
362        }
363
364        if self.unlock_keep_secret && unlock == Unlock::Perm {
365            // verify password and get the secret
366            let secret = self.sstore.raw_secret(&account, &password)?;
367            self.unlocked_secrets
368                .write()
369                .insert(account.clone(), secret);
370        } else {
371            // verify password by signing dump message
372            // result may be discarded
373            let _ =
374                self.sstore.sign(&account, &password, &Default::default())?;
375        }
376
377        let data = AccountData { unlock, password };
378
379        unlocked.insert(account, data);
380        Ok(())
381    }
382
383    /// Lock an account
384    pub fn lock_account(&self, address: Address) -> Result<(), Error> {
385        let account = self.sstore.account_ref(&address)?;
386
387        let mut unlocked = self.unlocked.write();
388        if let Some(data) = unlocked.get(&account) {
389            if let Unlock::Perm = data.unlock {
390                self.unlocked_secrets.write().remove(&account);
391            }
392            unlocked.remove(&account);
393        }
394        Ok(())
395    }
396
397    fn password(
398        &self, account: &StoreAccountRef,
399    ) -> Result<Password, SignError> {
400        let mut unlocked = self.unlocked.write();
401        Self::password_with_unlocked(&mut unlocked, account)
402    }
403
404    fn password_with_unlocked(
405        unlocked: &mut HashMap<StoreAccountRef, AccountData>,
406        account: &StoreAccountRef,
407    ) -> Result<Password, SignError> {
408        let data = unlocked.get(account).ok_or(SignError::NotUnlocked)?.clone();
409        if let Unlock::OneTime = data.unlock {
410            unlocked
411                .remove(account)
412                .expect("data exists: so key must exist: qed");
413        }
414        if let Unlock::Timed(ref end) = data.unlock {
415            if Instant::now() > *end {
416                unlocked
417                    .remove(account)
418                    .expect("data exists: so key must exist: qed");
419                return Err(SignError::NotUnlocked);
420            }
421        }
422        Ok(data.password)
423    }
424
425    /// Unlocks account permanently.
426    pub fn unlock_account_permanently(
427        &self, account: Address, password: Password,
428    ) -> Result<(), Error> {
429        self.unlock_account(account, password, Unlock::Perm)
430    }
431
432    /// Unlocks account temporarily (for one signing).
433    pub fn unlock_account_temporarily(
434        &self, account: Address, password: Password,
435    ) -> Result<(), Error> {
436        self.unlock_account(account, password, Unlock::OneTime)
437    }
438
439    /// Unlocks account temporarily with a timeout.
440    pub fn unlock_account_timed(
441        &self, account: Address, password: Password, duration: Duration,
442    ) -> Result<(), Error> {
443        self.unlock_account(
444            account,
445            password,
446            Unlock::Timed(Instant::now() + duration),
447        )
448    }
449
450    /// Checks if given account is unlocked
451    pub fn is_unlocked(&self, address: &Address) -> bool {
452        let unlocked = self.unlocked.read();
453        let unlocked_secrets = self.unlocked_secrets.read();
454        self.sstore
455            .account_ref(address)
456            .map(|r| {
457                unlocked.get(&r).is_some() || unlocked_secrets.get(&r).is_some()
458            })
459            .unwrap_or(false)
460    }
461
462    /// Checks if given account is unlocked permanently
463    pub fn is_unlocked_permanently(&self, address: &Address) -> bool {
464        let unlocked = self.unlocked.read();
465        self.sstore
466            .account_ref(address)
467            .map(|r| {
468                unlocked
469                    .get(&r)
470                    .is_some_and(|account| account.unlock == Unlock::Perm)
471            })
472            .unwrap_or(false)
473    }
474
475    /// Signs the message. If password is not provided the account must be
476    /// unlocked.
477    pub fn sign(
478        &self, address: Address, password: Option<Password>, message: Message,
479    ) -> Result<Signature, SignError> {
480        let account = self.sstore.account_ref(&address)?;
481        // unlocked must be acquired before unlocked_secrets
482        let mut unlocked = self.unlocked.write();
483        match self.unlocked_secrets.read().get(&account) {
484            Some(secret) => {
485                Ok(self.sstore.sign_with_secret(secret, &message)?)
486            }
487            None => {
488                let password = password.map(Ok).unwrap_or_else(|| {
489                    Self::password_with_unlocked(&mut unlocked, &account)
490                })?;
491                Ok(self.sstore.sign(&account, &password, &message)?)
492            }
493        }
494    }
495
496    /// Signs message using the derived secret. If password is not provided the
497    /// account must be unlocked.
498    pub fn sign_derived(
499        &self, address: &Address, password: Option<Password>,
500        derivation: Derivation, message: Message,
501    ) -> Result<Signature, SignError> {
502        let account = self.sstore.account_ref(address)?;
503        let password = password
504            .map(Ok)
505            .unwrap_or_else(|| self.password(&account))?;
506        Ok(self
507            .sstore
508            .sign_derived(&account, &password, derivation, &message)?)
509    }
510
511    /// Signs given message with supplied token. Returns a token to use in next
512    /// signing within this session.
513    pub fn sign_with_token(
514        &self, address: Address, token: AccountToken, message: Message,
515    ) -> Result<(Signature, AccountToken), SignError> {
516        let account = self.sstore.account_ref(&address)?;
517        let is_std_password = self.sstore.test_password(&account, &token)?;
518
519        let new_token = Password::from(random_string(16));
520        let signature = if is_std_password {
521            // Insert to transient store
522            self.sstore.copy_account(
523                &self.transient_sstore,
524                SecretVaultRef::Root,
525                &account,
526                &token,
527                &new_token,
528            )?;
529            // sign
530            self.sstore.sign(&account, &token, &message)?
531        } else {
532            // check transient store
533            self.transient_sstore
534                .change_password(&account, &token, &new_token)?;
535            // and sign
536            self.transient_sstore.sign(&account, &new_token, &message)?
537        };
538
539        Ok((signature, new_token))
540    }
541
542    /// Decrypts a message with given token. Returns a token to use in next
543    /// operation for this account.
544    pub fn decrypt_with_token(
545        &self, address: Address, token: AccountToken, shared_mac: &[u8],
546        message: &[u8],
547    ) -> Result<(Vec<u8>, AccountToken), SignError> {
548        let account = self.sstore.account_ref(&address)?;
549        let is_std_password = self.sstore.test_password(&account, &token)?;
550
551        let new_token = Password::from(random_string(16));
552        let message = if is_std_password {
553            // Insert to transient store
554            self.sstore.copy_account(
555                &self.transient_sstore,
556                SecretVaultRef::Root,
557                &account,
558                &token,
559                &new_token,
560            )?;
561            // decrypt
562            self.sstore.decrypt(&account, &token, shared_mac, message)?
563        } else {
564            // check transient store
565            self.transient_sstore
566                .change_password(&account, &token, &new_token)?;
567            // and decrypt
568            self.transient_sstore
569                .decrypt(&account, &token, shared_mac, message)?
570        };
571
572        Ok((message, new_token))
573    }
574
575    /// Decrypts a message. If password is not provided the account must be
576    /// unlocked.
577    pub fn decrypt(
578        &self, address: Address, password: Option<Password>, shared_mac: &[u8],
579        message: &[u8],
580    ) -> Result<Vec<u8>, SignError> {
581        let account = self.sstore.account_ref(&address)?;
582        let password = password
583            .map(Ok)
584            .unwrap_or_else(|| self.password(&account))?;
585        Ok(self
586            .sstore
587            .decrypt(&account, &password, shared_mac, message)?)
588    }
589
590    /// Agree on shared key.
591    pub fn agree(
592        &self, address: Address, password: Option<Password>,
593        other_public: &Public,
594    ) -> Result<Secret, SignError> {
595        let account = self.sstore.account_ref(&address)?;
596        let password = password
597            .map(Ok)
598            .unwrap_or_else(|| self.password(&account))?;
599        Ok(self.sstore.agree(&account, &password, other_public)?)
600    }
601
602    /// Returns the underlying `SecretStore` reference if one exists.
603    pub fn list_geth_accounts(&self, testnet: bool) -> Vec<Address> {
604        self.sstore
605            .list_geth_accounts(testnet)
606            .into_iter()
607            .collect()
608    }
609
610    /// Returns the underlying `SecretStore` reference if one exists.
611    pub fn import_geth_accounts(
612        &self, desired: Vec<Address>, testnet: bool,
613    ) -> Result<Vec<Address>, Error> {
614        self.sstore
615            .import_geth_accounts(SecretVaultRef::Root, desired, testnet)
616            .map(|a| a.into_iter().map(|a| a.address).collect())
617    }
618
619    /// Create new vault.
620    pub fn create_vault(
621        &self, name: &str, password: &Password,
622    ) -> Result<(), Error> {
623        self.sstore.create_vault(name, password)
624    }
625
626    /// Open existing vault.
627    pub fn open_vault(
628        &self, name: &str, password: &Password,
629    ) -> Result<(), Error> {
630        self.sstore.open_vault(name, password)
631    }
632
633    /// Close previously opened vault.
634    pub fn close_vault(&self, name: &str) -> Result<(), Error> {
635        self.sstore.close_vault(name)
636    }
637
638    /// List all vaults
639    pub fn list_vaults(&self) -> Result<Vec<String>, Error> {
640        self.sstore.list_vaults()
641    }
642
643    /// List all currently opened vaults
644    pub fn list_opened_vaults(&self) -> Result<Vec<String>, Error> {
645        self.sstore.list_opened_vaults()
646    }
647
648    /// Change vault password.
649    pub fn change_vault_password(
650        &self, name: &str, new_password: &Password,
651    ) -> Result<(), Error> {
652        self.sstore.change_vault_password(name, new_password)
653    }
654
655    /// Change vault of the given address.
656    pub fn change_vault(
657        &self, address: Address, new_vault: &str,
658    ) -> Result<(), Error> {
659        let new_vault_ref = if new_vault.is_empty() {
660            SecretVaultRef::Root
661        } else {
662            SecretVaultRef::Vault(new_vault.to_owned())
663        };
664        let old_account_ref = self.sstore.account_ref(&address)?;
665        self.sstore
666            .change_account_vault(new_vault_ref, old_account_ref)
667            .map(|_| ())
668    }
669
670    /// Get vault metadata string.
671    pub fn get_vault_meta(&self, name: &str) -> Result<String, Error> {
672        self.sstore.get_vault_meta(name)
673    }
674
675    /// Set vault metadata string.
676    pub fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error> {
677        self.sstore.set_vault_meta(name, meta)
678    }
679}
680
681#[cfg(test)]
682mod tests {
683    use super::{AccountProvider, Unlock};
684    use cfx_types::H256;
685    use cfxkey::{Address, Generator, Random};
686    use cfxstore::{Derivation, StoreAccountRef};
687    use std::time::{Duration, Instant};
688
689    #[test]
690    fn unlock_account_temp() {
691        let kp = Random.generate().unwrap();
692        let ap = AccountProvider::transient_provider();
693        assert!(ap
694            .insert_account(kp.secret().clone(), &"test".into())
695            .is_ok());
696        assert!(ap
697            .unlock_account_temporarily(kp.address(), "test1".into())
698            .is_err());
699        assert!(ap
700            .unlock_account_temporarily(kp.address(), "test".into())
701            .is_ok());
702        assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
703        assert!(ap.sign(kp.address(), None, Default::default()).is_err());
704    }
705
706    #[test]
707    fn derived_account_nosave() {
708        let kp = Random.generate().unwrap();
709        let ap = AccountProvider::transient_provider();
710        assert!(ap
711            .insert_account(kp.secret().clone(), &"base".into())
712            .is_ok());
713        assert!(ap
714            .unlock_account_permanently(kp.address(), "base".into())
715            .is_ok());
716
717        let derived_addr = ap
718            .derive_account(
719                &kp.address(),
720                None,
721                Derivation::SoftHash(H256::from_low_u64_be(999)),
722                false,
723            )
724            .expect("Derivation should not fail");
725
726        assert!(ap.unlock_account_permanently(derived_addr, "base".into()).is_err(),
727			"There should be an error because account is not supposed to be saved");
728    }
729
730    #[test]
731    fn derived_account_save() {
732        let kp = Random.generate().unwrap();
733        let ap = AccountProvider::transient_provider();
734        assert!(ap
735            .insert_account(kp.secret().clone(), &"base".into())
736            .is_ok());
737        assert!(ap
738            .unlock_account_permanently(kp.address(), "base".into())
739            .is_ok());
740
741        let derived_addr = ap
742            .derive_account(
743                &kp.address(),
744                None,
745                Derivation::SoftHash(H256::from_low_u64_be(999)),
746                true,
747            )
748            .expect("Derivation should not fail");
749
750        assert!(
751            ap.unlock_account_permanently(derived_addr, "base_wrong".into())
752                .is_err(),
753            "There should be an error because password is invalid"
754        );
755
756        assert!(
757            ap.unlock_account_permanently(derived_addr, "base".into())
758                .is_ok(),
759            "Should be ok because account is saved and password is valid"
760        );
761    }
762
763    #[test]
764    fn derived_account_sign() {
765        let kp = Random.generate().unwrap();
766        let ap = AccountProvider::transient_provider();
767        assert!(ap
768            .insert_account(kp.secret().clone(), &"base".into())
769            .is_ok());
770        assert!(ap
771            .unlock_account_permanently(kp.address(), "base".into())
772            .is_ok());
773
774        let derived_addr = ap
775            .derive_account(
776                &kp.address(),
777                None,
778                Derivation::SoftHash(H256::from_low_u64_be(1999)),
779                true,
780            )
781            .expect("Derivation should not fail");
782        ap.unlock_account_permanently(derived_addr, "base".into())
783            .expect(
784                "Should be ok because account is saved and password is valid",
785            );
786
787        let msg = Default::default();
788        let signed_msg1 = ap
789            .sign(derived_addr, None, msg)
790            .expect("Signing with existing unlocked account should not fail");
791        let signed_msg2 = ap.sign_derived(
792			&kp.address(),
793			None,
794			Derivation::SoftHash(H256::from_low_u64_be(1999)),
795			msg,
796		).expect("Derived signing with existing unlocked account should not fail");
797
798        assert_eq!(signed_msg1, signed_msg2, "Signed messages should match");
799    }
800
801    #[test]
802    fn unlock_account_perm() {
803        let kp = Random.generate().unwrap();
804        let ap = AccountProvider::transient_provider();
805        assert!(ap
806            .insert_account(kp.secret().clone(), &"test".into())
807            .is_ok());
808        assert!(ap
809            .unlock_account_permanently(kp.address(), "test1".into())
810            .is_err());
811        assert!(ap
812            .unlock_account_permanently(kp.address(), "test".into())
813            .is_ok());
814        assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
815        assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
816        assert!(ap
817            .unlock_account_temporarily(kp.address(), "test".into())
818            .is_ok());
819        assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
820        assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
821    }
822
823    #[test]
824    fn unlock_account_timer() {
825        let kp = Random.generate().unwrap();
826        let ap = AccountProvider::transient_provider();
827        assert!(ap
828            .insert_account(kp.secret().clone(), &"test".into())
829            .is_ok());
830        assert!(ap
831            .unlock_account_timed(
832                kp.address(),
833                "test1".into(),
834                Duration::from_secs(60)
835            )
836            .is_err());
837        assert!(ap
838            .unlock_account_timed(
839                kp.address(),
840                "test".into(),
841                Duration::from_secs(60)
842            )
843            .is_ok());
844        assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
845        ap.unlocked
846            .write()
847            .get_mut(&StoreAccountRef::root(kp.address()))
848            .unwrap()
849            .unlock = Unlock::Timed(Instant::now());
850        assert!(ap.sign(kp.address(), None, Default::default()).is_err());
851    }
852
853    #[test]
854    fn should_sign_and_return_token() {
855        // given
856        let kp = Random.generate().unwrap();
857        let ap = AccountProvider::transient_provider();
858        assert!(ap
859            .insert_account(kp.secret().clone(), &"test".into())
860            .is_ok());
861
862        // when
863        let (_signature, token) = ap
864            .sign_with_token(kp.address(), "test".into(), Default::default())
865            .unwrap();
866
867        // then
868        ap.sign_with_token(kp.address(), token.clone(), Default::default())
869            .expect("First usage of token should be correct.");
870        assert!(
871            ap.sign_with_token(kp.address(), token, Default::default())
872                .is_err(),
873            "Second usage of the same token should fail."
874        );
875    }
876
877    #[test]
878    fn should_not_return_blacklisted_account() {
879        // given
880        let mut ap = AccountProvider::transient_provider();
881        let acc = ap.new_account(&"test".into()).unwrap();
882        ap.blacklisted_accounts = vec![acc];
883
884        // then
885        assert_eq!(
886            ap.accounts_info()
887                .unwrap()
888                .keys()
889                .cloned()
890                .collect::<Vec<Address>>(),
891            vec![]
892        );
893        assert_eq!(ap.accounts().unwrap(), vec![]);
894    }
895}