1use array_bytes::{Dehexify, Hexify};
23use parking_lot::RwLock;
24use sc_keystore::{Error, Result};
25use sp_application_crypto::{AppCrypto, AppPair, IsWrappedBy};
26use sp_core::{
27 crypto::{ByteArray, ExposeSecret, KeyTypeId, Pair as CorePair, SecretString, VrfSecret},
28 ecdsa, ed25519, sr25519,
29};
30use std::{
31 collections::HashMap,
32 fs::{self, File},
33 io::Write,
34 path::PathBuf,
35 sync::Arc,
36};
37
38mod keystore_ext;
39pub use keystore_ext::KeystoreExt;
40pub use sp_keystore::{Error as TraitError, Keystore, KeystorePtr};
41
42sp_keystore::bandersnatch_experimental_enabled! {
43use sp_core::bandersnatch;
44}
45
46sp_keystore::bls_experimental_enabled! {
47use sp_core::{bls381, ecdsa_bls381, KeccakHasher};
48}
49
50pub struct LocalKeystore(RwLock<KeystoreInner>);
52
53impl LocalKeystore {
54 pub fn open<T: Into<PathBuf>>(path: T, password: Option<SecretString>) -> Result<Self> {
63 let inner = KeystoreInner::open(path, password)?;
64 Ok(Self(RwLock::new(inner)))
65 }
66
67 pub fn in_memory() -> Self {
69 let inner = KeystoreInner::new_in_memory();
70 Self(RwLock::new(inner))
71 }
72
73 pub fn key_pair<Pair: AppPair>(
78 &self,
79 public: &<Pair as AppCrypto>::Public,
80 ) -> Result<Option<Pair>> {
81 self.0.read().key_pair::<Pair>(public)
82 }
83
84 fn public_keys<T: CorePair>(&self, key_type: KeyTypeId) -> Vec<T::Public> {
85 self.0
86 .read()
87 .raw_public_keys(key_type)
88 .map(|v| {
89 v.into_iter().filter_map(|k| T::Public::from_slice(k.as_slice()).ok()).collect()
90 })
91 .unwrap_or_default()
92 }
93
94 fn generate_new<T: CorePair>(
95 &self,
96 key_type: KeyTypeId,
97 seed: Option<&str>,
98 ) -> std::result::Result<T::Public, TraitError> {
99 let pair = match seed {
100 Some(seed) => self.0.write().insert_ephemeral_from_seed_by_type::<T>(seed, key_type),
101 None => self.0.write().generate_by_type::<T>(key_type),
102 }
103 .map_err(|e| -> TraitError { e.into() })?;
104 Ok(pair.public())
105 }
106
107 fn sign<T: CorePair>(
108 &self,
109 key_type: KeyTypeId,
110 public: &T::Public,
111 msg: &[u8],
112 ) -> std::result::Result<Option<T::Signature>, TraitError> {
113 let signature = self
114 .0
115 .read()
116 .key_pair_by_type::<T>(public, key_type)?
117 .map(|pair| pair.sign(msg));
118 Ok(signature)
119 }
120
121 fn vrf_sign<T: CorePair + VrfSecret>(
122 &self,
123 key_type: KeyTypeId,
124 public: &T::Public,
125 data: &T::VrfSignData,
126 ) -> std::result::Result<Option<T::VrfSignature>, TraitError> {
127 let sig = self
128 .0
129 .read()
130 .key_pair_by_type::<T>(public, key_type)?
131 .map(|pair| pair.vrf_sign(data));
132 Ok(sig)
133 }
134
135 fn vrf_pre_output<T: CorePair + VrfSecret>(
136 &self,
137 key_type: KeyTypeId,
138 public: &T::Public,
139 input: &T::VrfInput,
140 ) -> std::result::Result<Option<T::VrfPreOutput>, TraitError> {
141 let pre_output = self
142 .0
143 .read()
144 .key_pair_by_type::<T>(public, key_type)?
145 .map(|pair| pair.vrf_pre_output(input));
146 Ok(pre_output)
147 }
148}
149
150impl Keystore for LocalKeystore {
151 fn insert(
159 &self,
160 key_type: KeyTypeId,
161 suri: &str,
162 public: &[u8],
163 ) -> std::result::Result<(), ()> {
164 self.0.write().insert(key_type, suri, public).map_err(|_| ())
165 }
166
167 fn keys(&self, key_type: KeyTypeId) -> std::result::Result<Vec<Vec<u8>>, TraitError> {
168 self.0.read().raw_public_keys(key_type).map_err(|e| e.into())
169 }
170
171 fn has_keys(&self, public_keys: &[(Vec<u8>, KeyTypeId)]) -> bool {
172 public_keys
173 .iter()
174 .all(|(p, t)| self.0.read().key_phrase_by_type(p, *t).ok().flatten().is_some())
175 }
176
177 fn sr25519_public_keys(&self, key_type: KeyTypeId) -> Vec<sr25519::Public> {
178 self.public_keys::<sr25519::Pair>(key_type)
179 }
180
181 fn sr25519_generate_new(
185 &self,
186 key_type: KeyTypeId,
187 seed: Option<&str>,
188 ) -> std::result::Result<sr25519::Public, TraitError> {
189 self.generate_new::<sr25519::Pair>(key_type, seed)
190 }
191
192 fn sr25519_sign(
193 &self,
194 key_type: KeyTypeId,
195 public: &sr25519::Public,
196 msg: &[u8],
197 ) -> std::result::Result<Option<sr25519::Signature>, TraitError> {
198 self.sign::<sr25519::Pair>(key_type, public, msg)
199 }
200
201 fn sr25519_vrf_sign(
202 &self,
203 key_type: KeyTypeId,
204 public: &sr25519::Public,
205 data: &sr25519::vrf::VrfSignData,
206 ) -> std::result::Result<Option<sr25519::vrf::VrfSignature>, TraitError> {
207 self.vrf_sign::<sr25519::Pair>(key_type, public, data)
208 }
209
210 fn sr25519_vrf_pre_output(
211 &self,
212 key_type: KeyTypeId,
213 public: &sr25519::Public,
214 input: &sr25519::vrf::VrfInput,
215 ) -> std::result::Result<Option<sr25519::vrf::VrfPreOutput>, TraitError> {
216 self.vrf_pre_output::<sr25519::Pair>(key_type, public, input)
217 }
218
219 fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec<ed25519::Public> {
220 self.public_keys::<ed25519::Pair>(key_type)
221 }
222
223 fn ed25519_generate_new(
227 &self,
228 key_type: KeyTypeId,
229 seed: Option<&str>,
230 ) -> std::result::Result<ed25519::Public, TraitError> {
231 self.generate_new::<ed25519::Pair>(key_type, seed)
232 }
233
234 fn ed25519_sign(
235 &self,
236 key_type: KeyTypeId,
237 public: &ed25519::Public,
238 msg: &[u8],
239 ) -> std::result::Result<Option<ed25519::Signature>, TraitError> {
240 self.sign::<ed25519::Pair>(key_type, public, msg)
241 }
242
243 fn ecdsa_public_keys(&self, key_type: KeyTypeId) -> Vec<ecdsa::Public> {
244 self.public_keys::<ecdsa::Pair>(key_type)
245 }
246
247 fn ecdsa_generate_new(
251 &self,
252 key_type: KeyTypeId,
253 seed: Option<&str>,
254 ) -> std::result::Result<ecdsa::Public, TraitError> {
255 self.generate_new::<ecdsa::Pair>(key_type, seed)
256 }
257
258 fn ecdsa_sign(
259 &self,
260 key_type: KeyTypeId,
261 public: &ecdsa::Public,
262 msg: &[u8],
263 ) -> std::result::Result<Option<ecdsa::Signature>, TraitError> {
264 self.sign::<ecdsa::Pair>(key_type, public, msg)
265 }
266
267 fn ecdsa_sign_prehashed(
268 &self,
269 key_type: KeyTypeId,
270 public: &ecdsa::Public,
271 msg: &[u8; 32],
272 ) -> std::result::Result<Option<ecdsa::Signature>, TraitError> {
273 let sig = self
274 .0
275 .read()
276 .key_pair_by_type::<ecdsa::Pair>(public, key_type)?
277 .map(|pair| pair.sign_prehashed(msg));
278 Ok(sig)
279 }
280
281 sp_keystore::bandersnatch_experimental_enabled! {
282 fn bandersnatch_public_keys(&self, key_type: KeyTypeId) -> Vec<bandersnatch::Public> {
283 self.public_keys::<bandersnatch::Pair>(key_type)
284 }
285
286 fn bandersnatch_generate_new(
290 &self,
291 key_type: KeyTypeId,
292 seed: Option<&str>,
293 ) -> std::result::Result<bandersnatch::Public, TraitError> {
294 self.generate_new::<bandersnatch::Pair>(key_type, seed)
295 }
296
297 fn bandersnatch_sign(
298 &self,
299 key_type: KeyTypeId,
300 public: &bandersnatch::Public,
301 msg: &[u8],
302 ) -> std::result::Result<Option<bandersnatch::Signature>, TraitError> {
303 self.sign::<bandersnatch::Pair>(key_type, public, msg)
304 }
305
306 fn bandersnatch_vrf_sign(
307 &self,
308 key_type: KeyTypeId,
309 public: &bandersnatch::Public,
310 data: &bandersnatch::vrf::VrfSignData,
311 ) -> std::result::Result<Option<bandersnatch::vrf::VrfSignature>, TraitError> {
312 self.vrf_sign::<bandersnatch::Pair>(key_type, public, data)
313 }
314
315 fn bandersnatch_vrf_pre_output(
316 &self,
317 key_type: KeyTypeId,
318 public: &bandersnatch::Public,
319 input: &bandersnatch::vrf::VrfInput,
320 ) -> std::result::Result<Option<bandersnatch::vrf::VrfPreOutput>, TraitError> {
321 self.vrf_pre_output::<bandersnatch::Pair>(key_type, public, input)
322 }
323
324 fn bandersnatch_ring_vrf_sign(
325 &self,
326 key_type: KeyTypeId,
327 public: &bandersnatch::Public,
328 data: &bandersnatch::vrf::VrfSignData,
329 prover: &bandersnatch::ring_vrf::RingProver,
330 ) -> std::result::Result<Option<bandersnatch::ring_vrf::RingVrfSignature>, TraitError> {
331 let sig = self
332 .0
333 .read()
334 .key_pair_by_type::<bandersnatch::Pair>(public, key_type)?
335 .map(|pair| pair.ring_vrf_sign(data, prover));
336 Ok(sig)
337 }
338 }
339
340 sp_keystore::bls_experimental_enabled! {
341 fn bls381_public_keys(&self, key_type: KeyTypeId) -> Vec<bls381::Public> {
342 self.public_keys::<bls381::Pair>(key_type)
343 }
344
345 fn bls381_generate_new(
349 &self,
350 key_type: KeyTypeId,
351 seed: Option<&str>,
352 ) -> std::result::Result<bls381::Public, TraitError> {
353 self.generate_new::<bls381::Pair>(key_type, seed)
354 }
355
356 fn bls381_sign(
357 &self,
358 key_type: KeyTypeId,
359 public: &bls381::Public,
360 msg: &[u8],
361 ) -> std::result::Result<Option<bls381::Signature>, TraitError> {
362 self.sign::<bls381::Pair>(key_type, public, msg)
363 }
364
365 fn ecdsa_bls381_public_keys(&self, key_type: KeyTypeId) -> Vec<ecdsa_bls381::Public> {
366 self.public_keys::<ecdsa_bls381::Pair>(key_type)
367 }
368
369 fn ecdsa_bls381_generate_new(
373 &self,
374 key_type: KeyTypeId,
375 seed: Option<&str>,
376 ) -> std::result::Result<ecdsa_bls381::Public, TraitError> {
377 self.generate_new::<ecdsa_bls381::Pair>(key_type, seed)
378 }
379
380 fn ecdsa_bls381_sign(
381 &self,
382 key_type: KeyTypeId,
383 public: &ecdsa_bls381::Public,
384 msg: &[u8],
385 ) -> std::result::Result<Option<ecdsa_bls381::Signature>, TraitError> {
386 self.sign::<ecdsa_bls381::Pair>(key_type, public, msg)
387 }
388
389 fn ecdsa_bls381_sign_with_keccak256(
390 &self,
391 key_type: KeyTypeId,
392 public: &ecdsa_bls381::Public,
393 msg: &[u8],
394 ) -> std::result::Result<Option<ecdsa_bls381::Signature>, TraitError> {
395 let sig = self.0
396 .read()
397 .key_pair_by_type::<ecdsa_bls381::Pair>(public, key_type)?
398 .map(|pair| pair.sign_with_hasher::<KeccakHasher>(msg));
399 Ok(sig)
400 }
401 }
402}
403
404impl From<LocalKeystore> for KeystorePtr {
405 fn from(val: LocalKeystore) -> Self {
406 Arc::new(val)
407 }
408}
409
410struct KeystoreInner {
416 path: Option<PathBuf>,
417 additional: HashMap<(KeyTypeId, Vec<u8>), String>,
419 password: Option<SecretString>,
420}
421
422impl KeystoreInner {
423 fn open<T: Into<PathBuf>>(path: T, password: Option<SecretString>) -> Result<Self> {
427 let path = path.into();
428 fs::create_dir_all(&path)?;
429
430 Ok(Self { path: Some(path), additional: HashMap::new(), password })
431 }
432
433 fn password(&self) -> Option<&str> {
435 self.password.as_ref().map(|p| p.expose_secret()).map(|p| p.as_str())
436 }
437
438 fn new_in_memory() -> Self {
440 Self { path: None, additional: HashMap::new(), password: None }
441 }
442
443 fn get_additional_pair(&self, public: &[u8], key_type: KeyTypeId) -> Option<&String> {
445 let key = (key_type, public.to_vec());
446 self.additional.get(&key)
447 }
448
449 fn insert_ephemeral_pair<Pair: CorePair>(
453 &mut self,
454 pair: &Pair,
455 seed: &str,
456 key_type: KeyTypeId,
457 ) {
458 let key = (key_type, pair.public().to_raw_vec());
459 self.additional.insert(key, seed.into());
460 }
461
462 fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<()> {
466 if let Some(path) = self.key_file_path(public, key_type) {
467 Self::write_to_file(path, suri)?;
468 }
469
470 Ok(())
471 }
472
473 fn generate_by_type<Pair: CorePair>(&mut self, key_type: KeyTypeId) -> Result<Pair> {
478 let (pair, phrase, _) = Pair::generate_with_phrase(self.password());
479 if let Some(path) = self.key_file_path(pair.public().as_slice(), key_type) {
480 Self::write_to_file(path, &phrase)?;
481 } else {
482 self.insert_ephemeral_pair(&pair, &phrase, key_type);
483 }
484
485 Ok(pair)
486 }
487
488 fn write_to_file(file: PathBuf, data: &str) -> Result<()> {
490 let mut file = File::create(file)?;
491
492 #[cfg(target_family = "unix")]
493 {
494 use std::os::unix::fs::PermissionsExt;
495 file.set_permissions(fs::Permissions::from_mode(0o600))?;
496 }
497
498 serde_json::to_writer(&file, data)?;
499 file.flush()?;
500 Ok(())
501 }
502
503 fn insert_ephemeral_from_seed_by_type<Pair: CorePair>(
507 &mut self,
508 seed: &str,
509 key_type: KeyTypeId,
510 ) -> Result<Pair> {
511 let pair = Pair::from_string(seed, None).map_err(|_| Error::InvalidSeed)?;
512 self.insert_ephemeral_pair(&pair, seed, key_type);
513 Ok(pair)
514 }
515
516 fn key_phrase_by_type(&self, public: &[u8], key_type: KeyTypeId) -> Result<Option<String>> {
518 if let Some(phrase) = self.get_additional_pair(public, key_type) {
519 return Ok(Some(phrase.clone()))
520 }
521
522 let path = if let Some(path) = self.key_file_path(public, key_type) {
523 path
524 } else {
525 return Ok(None)
526 };
527
528 if path.exists() {
529 let file = File::open(path)?;
530
531 serde_json::from_reader(&file).map_err(Into::into).map(Some)
532 } else {
533 Ok(None)
534 }
535 }
536
537 fn key_pair_by_type<Pair: CorePair>(
539 &self,
540 public: &Pair::Public,
541 key_type: KeyTypeId,
542 ) -> Result<Option<Pair>> {
543 let phrase = if let Some(p) = self.key_phrase_by_type(public.as_slice(), key_type)? {
544 p
545 } else {
546 return Ok(None)
547 };
548
549 let pair = Pair::from_string(&phrase, self.password()).map_err(|_| Error::InvalidPhrase)?;
550
551 if &pair.public() == public {
552 Ok(Some(pair))
553 } else {
554 Err(Error::PublicKeyMismatch)
555 }
556 }
557
558 fn key_file_path(&self, public: &[u8], key_type: KeyTypeId) -> Option<PathBuf> {
562 let mut buf = self.path.as_ref()?.clone();
563 let key_type = key_type.0.hexify();
564 let key = public.hexify();
565 buf.push(key_type + key.as_str());
566 Some(buf)
567 }
568
569 fn raw_public_keys(&self, key_type: KeyTypeId) -> Result<Vec<Vec<u8>>> {
571 let mut public_keys: Vec<Vec<u8>> = self
572 .additional
573 .keys()
574 .filter_map(|k| if k.0 == key_type { Some(k.1.clone()) } else { None })
575 .collect();
576
577 if let Some(path) = &self.path {
578 for entry in fs::read_dir(path)? {
579 let entry = entry?;
580 let path = entry.path();
581
582 if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
584 match <Vec<u8>>::dehexify(name) {
585 Ok(ref hex) if hex.len() > 4 => {
586 if hex[0..4] != key_type.0 {
587 continue
588 }
589 let public = hex[4..].to_vec();
590 public_keys.push(public);
591 },
592 _ => continue,
593 }
594 }
595 }
596 }
597
598 Ok(public_keys)
599 }
600
601 pub fn key_pair<Pair: AppPair>(
606 &self,
607 public: &<Pair as AppCrypto>::Public,
608 ) -> Result<Option<Pair>> {
609 self.key_pair_by_type::<Pair::Generic>(IsWrappedBy::from_ref(public), Pair::ID)
610 .map(|v| v.map(Into::into))
611 }
612}
613
614#[cfg(test)]
615mod tests {
616 use super::*;
617 use sp_application_crypto::{ed25519, sr25519, AppPublic};
618 use sp_core::{crypto::Ss58Codec, testing::SR25519, Pair};
619 use std::{fs, str::FromStr};
620 use tempfile::TempDir;
621
622 const TEST_KEY_TYPE: KeyTypeId = KeyTypeId(*b"test");
623
624 impl KeystoreInner {
625 fn insert_ephemeral_from_seed<Pair: AppPair>(&mut self, seed: &str) -> Result<Pair> {
626 self.insert_ephemeral_from_seed_by_type::<Pair::Generic>(seed, Pair::ID)
627 .map(Into::into)
628 }
629
630 fn public_keys<Public: AppPublic>(&self) -> Result<Vec<Public>> {
631 self.raw_public_keys(Public::ID).map(|v| {
632 v.into_iter().filter_map(|k| Public::from_slice(k.as_slice()).ok()).collect()
633 })
634 }
635
636 fn generate<Pair: AppPair>(&mut self) -> Result<Pair> {
637 self.generate_by_type::<Pair::Generic>(Pair::ID).map(Into::into)
638 }
639 }
640
641 #[test]
642 fn basic_store() {
643 let temp_dir = TempDir::new().unwrap();
644 let mut store = KeystoreInner::open(temp_dir.path(), None).unwrap();
645
646 assert!(store.public_keys::<ed25519::AppPublic>().unwrap().is_empty());
647
648 let key: ed25519::AppPair = store.generate().unwrap();
649 let key2: ed25519::AppPair = store.key_pair(&key.public()).unwrap().unwrap();
650
651 assert_eq!(key.public(), key2.public());
652
653 assert_eq!(store.public_keys::<ed25519::AppPublic>().unwrap()[0], key.public());
654 }
655
656 #[test]
657 fn has_keys_works() {
658 let temp_dir = TempDir::new().unwrap();
659 let store = LocalKeystore::open(temp_dir.path(), None).unwrap();
660
661 let key: ed25519::AppPair = store.0.write().generate().unwrap();
662 let key2 = ed25519::Pair::generate().0;
663
664 assert!(!store.has_keys(&[(key2.public().to_vec(), ed25519::AppPublic::ID)]));
665
666 assert!(!store.has_keys(&[
667 (key2.public().to_vec(), ed25519::AppPublic::ID),
668 (key.public().to_raw_vec(), ed25519::AppPublic::ID),
669 ],));
670
671 assert!(store.has_keys(&[(key.public().to_raw_vec(), ed25519::AppPublic::ID)]));
672 }
673
674 #[test]
675 fn test_insert_ephemeral_from_seed() {
676 let temp_dir = TempDir::new().unwrap();
677 let mut store = KeystoreInner::open(temp_dir.path(), None).unwrap();
678
679 let pair: ed25519::AppPair = store
680 .insert_ephemeral_from_seed(
681 "0x3d97c819d68f9bafa7d6e79cb991eebcd77d966c5334c0b94d9e1fa7ad0869dc",
682 )
683 .unwrap();
684 assert_eq!(
685 "5DKUrgFqCPV8iAXx9sjy1nyBygQCeiUYRFWurZGhnrn3HJCA",
686 pair.public().to_ss58check()
687 );
688
689 drop(store);
690 let store = KeystoreInner::open(temp_dir.path(), None).unwrap();
691 assert!(store.key_pair::<ed25519::AppPair>(&pair.public()).unwrap().is_none());
693 }
694
695 #[test]
696 fn password_being_used() {
697 let password = String::from("password");
698 let temp_dir = TempDir::new().unwrap();
699 let mut store = KeystoreInner::open(
700 temp_dir.path(),
701 Some(FromStr::from_str(password.as_str()).unwrap()),
702 )
703 .unwrap();
704
705 let pair: ed25519::AppPair = store.generate().unwrap();
706 assert_eq!(
707 pair.public(),
708 store.key_pair::<ed25519::AppPair>(&pair.public()).unwrap().unwrap().public(),
709 );
710
711 let store = KeystoreInner::open(temp_dir.path(), None).unwrap();
713 assert!(store.key_pair::<ed25519::AppPair>(&pair.public()).is_err());
714
715 let store = KeystoreInner::open(
716 temp_dir.path(),
717 Some(FromStr::from_str(password.as_str()).unwrap()),
718 )
719 .unwrap();
720 assert_eq!(
721 pair.public(),
722 store.key_pair::<ed25519::AppPair>(&pair.public()).unwrap().unwrap().public(),
723 );
724 }
725
726 #[test]
727 fn public_keys_are_returned() {
728 let temp_dir = TempDir::new().unwrap();
729 let mut store = KeystoreInner::open(temp_dir.path(), None).unwrap();
730
731 let mut keys = Vec::new();
732 for i in 0..10 {
733 keys.push(store.generate::<ed25519::AppPair>().unwrap().public());
734 keys.push(
735 store
736 .insert_ephemeral_from_seed::<ed25519::AppPair>(&format!(
737 "0x3d97c819d68f9bafa7d6e79cb991eebcd7{}d966c5334c0b94d9e1fa7ad0869dc",
738 i
739 ))
740 .unwrap()
741 .public(),
742 );
743 }
744
745 store.generate::<sr25519::AppPair>().unwrap();
747
748 keys.sort();
749 let mut store_pubs = store.public_keys::<ed25519::AppPublic>().unwrap();
750 store_pubs.sort();
751
752 assert_eq!(keys, store_pubs);
753 }
754
755 #[test]
756 fn store_unknown_and_extract_it() {
757 let temp_dir = TempDir::new().unwrap();
758 let store = KeystoreInner::open(temp_dir.path(), None).unwrap();
759
760 let secret_uri = "//Alice";
761 let key_pair = sr25519::AppPair::from_string(secret_uri, None).expect("Generates key pair");
762
763 store
764 .insert(SR25519, secret_uri, key_pair.public().as_ref())
765 .expect("Inserts unknown key");
766
767 let store_key_pair = store
768 .key_pair_by_type::<sr25519::AppPair>(&key_pair.public(), SR25519)
769 .expect("Gets key pair from keystore")
770 .unwrap();
771
772 assert_eq!(key_pair.public(), store_key_pair.public());
773 }
774
775 #[test]
776 fn store_ignores_files_with_invalid_name() {
777 let temp_dir = TempDir::new().unwrap();
778 let store = LocalKeystore::open(temp_dir.path(), None).unwrap();
779
780 let file_name = temp_dir.path().join(&SR25519.0[..2].hexify());
781 fs::write(file_name, "test").expect("Invalid file is written");
782
783 assert!(store.sr25519_public_keys(SR25519).is_empty());
784 }
785
786 #[test]
787 fn generate_with_seed_is_not_stored() {
788 let temp_dir = TempDir::new().unwrap();
789 let store = LocalKeystore::open(temp_dir.path(), None).unwrap();
790 let _alice_tmp_key = store.sr25519_generate_new(TEST_KEY_TYPE, Some("//Alice")).unwrap();
791
792 assert_eq!(store.sr25519_public_keys(TEST_KEY_TYPE).len(), 1);
793
794 drop(store);
795 let store = LocalKeystore::open(temp_dir.path(), None).unwrap();
796 assert_eq!(store.sr25519_public_keys(TEST_KEY_TYPE).len(), 0);
797 }
798
799 #[test]
800 fn generate_can_be_fetched_in_memory() {
801 let store = LocalKeystore::in_memory();
802 store.sr25519_generate_new(TEST_KEY_TYPE, Some("//Alice")).unwrap();
803
804 assert_eq!(store.sr25519_public_keys(TEST_KEY_TYPE).len(), 1);
805 store.sr25519_generate_new(TEST_KEY_TYPE, None).unwrap();
806 assert_eq!(store.sr25519_public_keys(TEST_KEY_TYPE).len(), 2);
807 }
808
809 #[test]
810 #[cfg(target_family = "unix")]
811 fn uses_correct_file_permissions_on_unix() {
812 use std::os::unix::fs::PermissionsExt;
813
814 let temp_dir = TempDir::new().unwrap();
815 let store = LocalKeystore::open(temp_dir.path(), None).unwrap();
816
817 let public = store.sr25519_generate_new(TEST_KEY_TYPE, None).unwrap();
818
819 let path = store.0.read().key_file_path(public.as_ref(), TEST_KEY_TYPE).unwrap();
820 let permissions = File::open(path).unwrap().metadata().unwrap().permissions();
821
822 assert_eq!(0o100600, permissions.mode());
823 }
824}