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