1use std::{
6 collections::{BTreeMap, HashSet},
7 fmt::{Display, Formatter, Write},
8 fs,
9 fs::File,
10 io::BufReader,
11 path::{Path, PathBuf},
12};
13
14use anyhow::{Context, anyhow, bail, ensure};
15use bip32::DerivationPath;
16use bip39::{Language, Mnemonic, Seed};
17use iota_sdk_types::{
18 Address,
19 crypto::{Intent, IntentMessage},
20};
21use iota_types::crypto::{
22 EncodeDecodeBase64, IotaKeyPair, PublicKey, Signature, SignatureScheme, enum_dispatch,
23 get_key_pair_from_rng,
24};
25use rand::{SeedableRng, rngs::StdRng};
26use regex::Regex;
27use serde::{Deserialize, Deserializer, Serialize, Serializer};
28use serde_with::{DisplayFromStr, serde_as};
29use tracing::{debug, info};
30
31use crate::{
32 key_derive::{derive_key_pair_from_path, generate_new_key},
33 random_names::{random_name, random_names},
34 serde_iota_keypair, serde_public_key,
35};
36
37#[derive(Serialize, Deserialize)]
38#[enum_dispatch(AccountKeystore)]
39pub enum Keystore {
40 File(FileBasedKeystore),
41 InMem(InMemKeystore),
42}
43
44#[enum_dispatch]
45pub trait AccountKeystore: Send + Sync {
46 fn add_key(
47 &mut self,
48 alias: Option<String>,
49 key: impl Into<StoredKey>,
50 ) -> Result<(), anyhow::Error>;
51 fn remove_key(&mut self, address: &Address) -> Result<(), anyhow::Error>;
52 fn keys(&self) -> Vec<&StoredKey>;
53 fn get_key(&self, address: &Address) -> Result<&StoredKey, anyhow::Error>;
54
55 fn sign_hashed(&self, address: &Address, msg: &[u8]) -> Result<Signature, signature::Error>;
56
57 fn sign_secure<T>(
58 &self,
59 address: &Address,
60 msg: &T,
61 intent: Intent,
62 ) -> Result<Signature, signature::Error>
63 where
64 T: Serialize;
65 fn addresses(&self) -> Vec<Address> {
66 self.keys().into_iter().map(|k| k.address()).collect()
67 }
68 fn addresses_with_alias(&self) -> Vec<(&Address, &Alias)>;
69 fn aliases(&self) -> Vec<&Alias>;
70 fn aliases_mut(&mut self) -> Vec<&mut Alias>;
71 fn alias_names(&self) -> Vec<&str> {
72 self.aliases()
73 .into_iter()
74 .map(|a| a.alias.as_str())
75 .collect()
76 }
77 fn get_alias_by_address(&self, address: &Address) -> Result<String, anyhow::Error>;
79 fn get_address_by_alias(&self, alias: String) -> Result<&Address, anyhow::Error>;
80 fn alias_exists(&self, alias: &str) -> bool {
82 self.alias_names().contains(&alias)
83 }
84
85 fn create_alias(&self, alias: Option<String>) -> Result<String, anyhow::Error>;
86
87 fn update_alias(
88 &mut self,
89 old_alias: &str,
90 new_alias: Option<&str>,
91 ) -> Result<String, anyhow::Error>;
92
93 fn update_alias_value(
95 &mut self,
96 old_alias: &str,
97 new_alias: Option<&str>,
98 ) -> Result<String, anyhow::Error> {
99 if !self.alias_exists(old_alias) {
100 bail!("The provided alias {old_alias} does not exist");
101 }
102
103 let new_alias_name = self.create_alias(new_alias.map(str::to_string))?;
104
105 for a in self.aliases_mut() {
106 if a.alias == old_alias {
107 *a = Alias {
108 alias: new_alias_name.clone(),
109 };
110 }
111 }
112 Ok(new_alias_name)
113 }
114
115 fn generate_and_add_new_key(
116 &mut self,
117 key_scheme: SignatureScheme,
118 alias: Option<String>,
119 derivation_path: Option<DerivationPath>,
120 word_length: Option<String>,
121 ) -> Result<(Address, String, SignatureScheme), anyhow::Error> {
122 let (address, kp, scheme, phrase) =
123 generate_new_key(key_scheme, derivation_path, word_length)?;
124 self.add_key(alias, kp)?;
125 Ok((address, phrase, scheme))
126 }
127
128 fn import_from_mnemonic(
129 &mut self,
130 phrase: &str,
131 key_scheme: SignatureScheme,
132 derivation_path: Option<DerivationPath>,
133 alias: Option<String>,
134 ) -> Result<Address, anyhow::Error> {
135 let mnemonic = Mnemonic::from_phrase(phrase, Language::English)
136 .map_err(|e| anyhow::anyhow!("Invalid mnemonic phrase: {e:?}"))?;
137 let seed = Seed::new(&mnemonic, "");
138 self.import_from_seed(seed.as_bytes(), key_scheme, derivation_path, alias)
139 }
140
141 fn import_from_seed(
142 &mut self,
143 seed: &[u8],
144 key_scheme: SignatureScheme,
145 derivation_path: Option<DerivationPath>,
146 alias: Option<String>,
147 ) -> Result<Address, anyhow::Error> {
148 match derive_key_pair_from_path(seed, derivation_path, &key_scheme) {
149 Ok((address, kp)) => {
150 self.add_key(alias, kp)?;
151 Ok(address)
152 }
153 Err(e) => Err(anyhow!("error getting keypair {e:?}")),
154 }
155 }
156
157 fn import_from_external(
158 &mut self,
159 source: &str,
160 public_key: PublicKey,
161 derivation_path: Option<DerivationPath>,
162 alias: Option<String>,
163 ) -> Result<(), anyhow::Error> {
164 self.add_key(
165 alias,
166 StoredKey::External {
167 derivation_path,
168 public_key,
169 source: source.to_string(),
170 },
171 )
172 }
173}
174
175impl Display for Keystore {
176 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
177 let mut writer = String::new();
178 match self {
179 Keystore::File(file) => {
180 writeln!(writer, "Keystore Type: File")?;
181 write!(writer, "Keystore Path : {:?}", file.path)?;
182 write!(f, "{writer}")
183 }
184 Keystore::InMem(_) => {
185 writeln!(writer, "Keystore Type: InMem")?;
186 write!(f, "{writer}")
187 }
188 }
189 }
190}
191
192#[derive(Serialize, Deserialize, Clone, Debug)]
194pub struct LegacyAlias {
195 pub alias: String,
196 pub public_key_base64: String,
197}
198
199#[derive(Serialize, Deserialize, Clone, Debug)]
200pub struct Alias {
201 pub alias: String,
202}
203
204#[expect(clippy::large_enum_variant)]
205#[serde_as]
206#[derive(Serialize, Deserialize, Debug, Clone)]
207#[serde(
208 tag = "type", content = "value", rename_all = "snake_case"
211)]
212pub enum StoredKey {
213 #[serde(with = "serde_iota_keypair")]
214 KeyPair(IotaKeyPair),
215 Account(Address),
216 External {
217 source: String,
218 #[serde_as(as = "Option<DisplayFromStr>")]
219 #[serde(skip_serializing_if = "Option::is_none")]
220 derivation_path: Option<DerivationPath>,
221 #[serde(rename = "public_key_base64_with_flag", with = "serde_public_key")]
222 public_key: PublicKey,
223 },
224}
225
226impl From<IotaKeyPair> for StoredKey {
227 fn from(keypair: IotaKeyPair) -> Self {
228 StoredKey::KeyPair(keypair)
229 }
230}
231
232impl StoredKey {
233 pub fn address(&self) -> Address {
234 match self {
235 StoredKey::KeyPair(key) => (&key.public()).into(),
236 StoredKey::Account(address) => *address,
237 StoredKey::External { public_key, .. } => public_key.into(),
238 }
239 }
240
241 pub fn public(&self) -> PublicKey {
242 match self {
243 StoredKey::KeyPair(keypair) => keypair.public(),
244 StoredKey::Account(_) => panic!("Account addresses are not backed by key pairs."),
245 StoredKey::External { public_key, .. } => public_key.clone(),
246 }
247 }
248
249 pub fn derivation_path(&self) -> Option<DerivationPath> {
250 match self {
251 StoredKey::KeyPair(_) => None,
252 StoredKey::Account(_) => None,
253 StoredKey::External {
254 derivation_path, ..
255 } => derivation_path.clone(),
256 }
257 }
258
259 pub fn external_source(&self) -> Option<String> {
260 match self {
261 StoredKey::KeyPair(_) => None,
262 StoredKey::Account(_) => None,
263 StoredKey::External { source, .. } => Some(source.clone()),
264 }
265 }
266
267 pub fn as_keypair(&self) -> Result<&IotaKeyPair, anyhow::Error> {
268 match self {
269 StoredKey::KeyPair(keypair) => Ok(keypair),
270 StoredKey::Account(_) => bail!("Account addresses are not backed by key pairs."),
271 StoredKey::External { .. } => bail!("Cannot get key pair for External keys."),
272 }
273 }
274
275 pub fn source(&self) -> &str {
276 match self {
277 StoredKey::KeyPair(_) => "keypair",
278 StoredKey::Account(_) => "account",
279 StoredKey::External { source, .. } => source,
280 }
281 }
282}
283
284#[derive(Default)]
285pub struct FileBasedKeystore {
286 keys: BTreeMap<Address, StoredKey>,
287 aliases: BTreeMap<Address, Alias>,
288 path: PathBuf,
289}
290
291impl Serialize for FileBasedKeystore {
292 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
293 where
294 S: Serializer,
295 {
296 serializer.serialize_str(self.path.to_str().unwrap_or(""))
297 }
298}
299
300impl<'de> Deserialize<'de> for FileBasedKeystore {
301 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
302 where
303 D: Deserializer<'de>,
304 {
305 use serde::de::Error;
306 FileBasedKeystore::new(&PathBuf::from(String::deserialize(deserializer)?))
307 .map_err(D::Error::custom)
308 }
309}
310#[derive(Serialize, Deserialize, Debug)]
311pub struct FileBasedKeystoreFile {
312 pub version: u8,
313 pub keys: Vec<AliasedKey>,
314}
315
316#[derive(Serialize, Deserialize, Debug)]
317pub struct AliasedKey {
318 pub alias: String,
319 pub address: Address,
320 pub key: StoredKey,
321}
322
323impl AccountKeystore for FileBasedKeystore {
324 fn sign_hashed(&self, address: &Address, msg: &[u8]) -> Result<Signature, signature::Error> {
325 let stored_key = self.keys.get(address).ok_or_else(|| {
326 signature::Error::from_source(format!("Cannot find key for address: [{address}]"))
327 })?;
328
329 match stored_key {
330 StoredKey::KeyPair(keypair) => Ok(Signature::new_hashed(msg, keypair)),
331 StoredKey::Account(_) => Err(signature::Error::from_source(
332 "sign_hashed is not supported for account type",
333 )),
334 StoredKey::External { source, .. } => Err(signature::Error::from_source(format!(
335 "sign_hashed is not supported for external type: {source} [{address}]"
336 ))),
337 }
338 }
339 fn sign_secure<T>(
340 &self,
341 address: &Address,
342 msg: &T,
343 intent: Intent,
344 ) -> Result<Signature, signature::Error>
345 where
346 T: Serialize,
347 {
348 let stored_key = self.keys.get(address).ok_or_else(|| {
349 signature::Error::from_source(format!("Cannot find key for address: [{address}]"))
350 })?;
351
352 let intent_msg = &IntentMessage::new(intent, msg);
353 match stored_key {
354 StoredKey::KeyPair(keypair) => Ok(Signature::new_secure(intent_msg, keypair)),
355 StoredKey::Account(_) => Err(signature::Error::from_source(
356 "sign_secure is not supported for account type",
357 )),
358 StoredKey::External { source, .. } => Err(signature::Error::from_source(format!(
359 "sign_secure is not supported for external type: {source} [{address}]",
360 ))),
361 }
362 }
363
364 fn add_key(
365 &mut self,
366 alias: Option<String>,
367 key: impl Into<StoredKey>,
368 ) -> Result<(), anyhow::Error> {
369 let key = key.into();
370 let address = key.address();
371
372 let alias = self.create_alias(alias)?;
373 self.aliases.insert(address, Alias { alias });
374 self.keys.insert(address, key);
375 self.save()?;
376 Ok(())
377 }
378
379 fn remove_key(&mut self, address: &Address) -> Result<(), anyhow::Error> {
380 self.aliases.remove(address);
381 self.keys.remove(address);
382 self.save()?;
383 Ok(())
384 }
385
386 fn aliases(&self) -> Vec<&Alias> {
389 self.aliases.values().collect()
390 }
391
392 fn addresses_with_alias(&self) -> Vec<(&Address, &Alias)> {
393 self.aliases.iter().collect::<Vec<_>>()
394 }
395
396 fn aliases_mut(&mut self) -> Vec<&mut Alias> {
399 self.aliases.values_mut().collect()
400 }
401
402 fn keys(&self) -> Vec<&StoredKey> {
403 self.keys.values().collect()
404 }
405
406 fn create_alias(&self, alias: Option<String>) -> Result<String, anyhow::Error> {
410 match alias {
411 Some(a) if self.alias_exists(&a) => {
412 bail!("Alias {a} already exists. Please choose another alias.")
413 }
414 Some(a) => validate_alias(&a),
415 None => Ok(random_name(
416 &self
417 .alias_names()
418 .into_iter()
419 .map(|x| x.to_string())
420 .collect::<HashSet<_>>(),
421 )),
422 }
423 }
424
425 fn get_address_by_alias(&self, alias: String) -> Result<&Address, anyhow::Error> {
427 self.addresses_with_alias()
428 .iter()
429 .find(|x| x.1.alias == alias)
430 .ok_or_else(|| anyhow!("Cannot resolve alias {alias} to an address"))
431 .map(|x| x.0)
432 }
433
434 fn get_alias_by_address(&self, address: &Address) -> Result<String, anyhow::Error> {
436 match self.aliases.get(address) {
437 Some(alias) => Ok(alias.alias.clone()),
438 None => bail!("Cannot find alias for address {address}"),
439 }
440 }
441
442 fn get_key(&self, address: &Address) -> Result<&StoredKey, anyhow::Error> {
443 match self.keys.get(address) {
444 Some(key) => Ok(key),
445 None => Err(anyhow!("Cannot find key for address: [{address}]")),
446 }
447 }
448
449 fn update_alias(
452 &mut self,
453 old_alias: &str,
454 new_alias: Option<&str>,
455 ) -> Result<String, anyhow::Error> {
456 let new_alias_name = self.update_alias_value(old_alias, new_alias)?;
457 self.save()?;
458 Ok(new_alias_name)
459 }
460}
461
462impl FileBasedKeystore {
463 pub fn new_from_v1(path: &PathBuf) -> Result<Self, anyhow::Error> {
464 let keys = if path.exists() {
465 let reader =
466 BufReader::new(File::open(path).with_context(|| {
467 format!("Cannot open the keystore file: {}", path.display())
468 })?);
469 let kp_strings: Vec<String> = serde_json::from_reader(reader).with_context(|| {
470 format!("Cannot deserialize the keystore file: {}", path.display(),)
471 })?;
472 kp_strings
473 .iter()
474 .map(|kpstr| {
475 let key = IotaKeyPair::decode(kpstr);
476 key.map(|k| (Address::from(&k.public()), StoredKey::KeyPair(k)))
477 })
478 .collect::<Result<BTreeMap<_, _>, _>>()
479 .map_err(|e| anyhow!("Invalid keystore file: {}. {}", path.display(), e))?
480 } else {
481 BTreeMap::new()
482 };
483
484 let mut aliases_path = path.clone();
486 aliases_path.set_extension("aliases");
487
488 let aliases = if aliases_path.exists() {
489 let reader = BufReader::new(File::open(&aliases_path).with_context(|| {
490 format!(
491 "Cannot open aliases file in keystore: {}",
492 aliases_path.display()
493 )
494 })?);
495
496 let legacy_aliases: Vec<LegacyAlias> =
497 serde_json::from_reader(reader).with_context(|| {
498 format!(
499 "Cannot deserialize aliases file in keystore: {}",
500 aliases_path.display(),
501 )
502 })?;
503
504 legacy_aliases
505 .into_iter()
506 .map(|legacy_alias| {
507 let key = PublicKey::decode_base64(&legacy_alias.public_key_base64);
508 key.map(|k| {
509 (
510 Into::<Address>::into(&k),
511 Alias {
512 alias: legacy_alias.alias,
513 },
514 )
515 })
516 })
517 .collect::<Result<BTreeMap<_, _>, _>>()
518 .map_err(|e| {
519 anyhow!(
520 "Invalid aliases file in keystore: {}. {}",
521 aliases_path.display(),
522 e
523 )
524 })?
525 } else if keys.is_empty() {
526 BTreeMap::new()
527 } else {
528 let names: Vec<String> = random_names(HashSet::new(), keys.len());
529 let aliases = keys
530 .iter()
531 .zip(names)
532 .map(|((iota_address, _ikp), alias)| (*iota_address, Alias { alias }))
533 .collect::<BTreeMap<_, _>>();
534 let aliases_store = serde_json::to_string_pretty(&aliases.values().collect::<Vec<_>>())
535 .with_context(|| {
536 format!(
537 "Cannot serialize aliases to file in keystore: {}",
538 aliases_path.display()
539 )
540 })?;
541 fs::write(aliases_path, aliases_store)?;
542 aliases
543 };
544
545 Ok(Self {
546 keys,
547 aliases,
548 path: path.to_path_buf(),
549 })
550 }
551
552 fn needs_migration(path: &PathBuf) -> Result<bool, anyhow::Error> {
553 let mut aliases_path = path.clone();
554 aliases_path.set_extension("aliases");
555 if aliases_path.exists() {
556 debug!(
558 "An alias file exists at {}, assuming keystore is in v1 format",
559 aliases_path.display()
560 );
561 return Ok(true);
562 }
563 if path.exists() {
566 let reader =
567 BufReader::new(File::open(path).with_context(|| {
568 format!("Cannot open the keystore file: {}", path.display())
569 })?);
570 let is_v1_format = serde_json::from_reader::<_, Vec<String>>(reader).is_ok();
573 debug!(
574 "Keystore file at {} is in v1 format: {}",
575 path.display(),
576 is_v1_format,
577 );
578 Ok(is_v1_format)
579 } else {
580 Ok(false)
582 }
583 }
584
585 fn migrate_v1_to_v2(path: &PathBuf) -> Result<Self, anyhow::Error> {
586 let migrated = Self::new_from_v1(path)?;
587 let mut backup_path = path.clone();
590 backup_path.set_extension(
591 path.extension()
594 .and_then(|ext| Some(ext.to_str()?.to_owned() + ".migrated"))
595 .unwrap_or(String::from("migrated")),
596 );
597 fs::rename(path, &backup_path).with_context(|| {
598 format!(
599 "Failed to rename the old keystore file to {}",
600 backup_path.display()
601 )
602 })?;
603 let mut aliases_path = path.clone();
604 aliases_path.set_extension("aliases");
605 let mut backup_aliases_path = aliases_path.clone();
606 backup_aliases_path.set_extension("aliases.migrated");
607 fs::rename(&aliases_path, &backup_aliases_path).with_context(|| {
608 format!(
609 "Failed to rename the old aliases file to {}",
610 backup_aliases_path.display()
611 )
612 })?;
613
614 info!(
615 "Migrated {} keys in keystore from v1 to v2 format. Old files have been renamed to {} and {}",
616 migrated.keys.len(),
617 backup_path.display(),
618 backup_aliases_path.display()
619 );
620
621 migrated.save()?;
623
624 Ok(migrated)
625 }
626
627 pub fn new(path: &PathBuf) -> Result<Self, anyhow::Error> {
628 if Self::needs_migration(path)? {
629 return Self::migrate_v1_to_v2(path);
630 }
631
632 let (keys, aliases) = if path.exists() {
633 let reader =
634 BufReader::new(File::open(path).with_context(|| {
635 format!("Cannot open the keystore file: {}", path.display())
636 })?);
637
638 let file: FileBasedKeystoreFile = serde_json::from_reader(reader).map_err(|e| {
639 anyhow!(
640 "Cannot deserialize the keystore file: {}. {e}",
641 path.display()
642 )
643 })?;
644
645 let aliases = file
646 .keys
647 .iter()
648 .map(|aliased| {
649 (
650 aliased.key.address(),
651 Alias {
652 alias: aliased.alias.clone(),
653 },
654 )
655 })
656 .collect::<BTreeMap<_, _>>();
657
658 let keys = file
659 .keys
660 .into_iter()
661 .map(|aliased| (aliased.key.address(), aliased.key))
662 .collect::<BTreeMap<_, _>>();
663
664 (keys, aliases)
665 } else {
666 (BTreeMap::new(), BTreeMap::new())
667 };
668
669 Ok(Self {
670 keys,
671 aliases,
672 path: path.to_path_buf(),
673 })
674 }
675
676 pub fn set_path(&mut self, path: &Path) {
677 self.path = path.to_path_buf();
678 }
679
680 pub fn save(&self) -> Result<(), anyhow::Error> {
681 let file = FileBasedKeystoreFile {
682 version: 2,
683 keys: self
684 .keys
685 .iter()
686 .map(|(address, key)| AliasedKey {
687 alias: self
688 .aliases
689 .get(address)
690 .map_or_else(|| self.create_alias(None).unwrap(), |a| a.alias.clone()),
691 address: *address,
692 key: key.clone(),
693 })
694 .collect(),
695 };
696
697 let store = serde_json::to_string_pretty(&file).with_context(|| {
698 format!("Cannot serialize keystore to file: {}", self.path.display())
699 })?;
700 fs::write(&self.path, store)
701 .map_err(|e| anyhow!("Couldn't save keystore to {}: {e}", self.path.display()))?;
702 Ok(())
703 }
704}
705
706#[derive(Default, Serialize, Deserialize)]
707pub struct InMemKeystore {
708 aliases: BTreeMap<Address, Alias>,
709 keys: BTreeMap<Address, StoredKey>,
710}
711
712impl AccountKeystore for InMemKeystore {
713 fn sign_hashed(&self, address: &Address, msg: &[u8]) -> Result<Signature, signature::Error> {
714 let stored_key = self.keys.get(address).ok_or_else(|| {
715 signature::Error::from_source(format!("Cannot find key for address: [{address}]"))
716 })?;
717
718 match stored_key {
719 StoredKey::KeyPair(keypair) => Ok(Signature::new_hashed(msg, keypair)),
720 StoredKey::Account(_) => Err(signature::Error::from_source(
721 "sign_hashed is not supported for account type",
722 )),
723 StoredKey::External { source, .. } => Err(signature::Error::from_source(format!(
724 "sign_hashed is not supported for external type: {source} [{address}]"
725 ))),
726 }
727 }
728 fn sign_secure<T>(
729 &self,
730 address: &Address,
731 msg: &T,
732 intent: Intent,
733 ) -> Result<Signature, signature::Error>
734 where
735 T: Serialize,
736 {
737 let stored_key = self.keys.get(address).ok_or_else(|| {
738 signature::Error::from_source(format!("Cannot find key for address: [{address}]"))
739 })?;
740
741 let intent_msg = &IntentMessage::new(intent, msg);
742 match stored_key {
743 StoredKey::KeyPair(keypair) => Ok(Signature::new_secure(intent_msg, keypair)),
744 StoredKey::Account(_) => Err(signature::Error::from_source(
745 "sign_secure is not supported for account type",
746 )),
747 StoredKey::External { source, .. } => Err(signature::Error::from_source(format!(
748 "sign_secure is not supported for external type: {source} [{address}]",
749 ))),
750 }
751 }
752
753 fn add_key(
754 &mut self,
755 alias: Option<String>,
756 key: impl Into<StoredKey>,
757 ) -> Result<(), anyhow::Error> {
758 let key = key.into();
759 let address: Address = (&key.public()).into();
760 let alias = alias.unwrap_or_else(|| {
761 random_name(
762 &self
763 .aliases()
764 .iter()
765 .map(|x| x.alias.clone())
766 .collect::<HashSet<_>>(),
767 )
768 });
769
770 let alias = Alias { alias };
771 self.aliases.insert(address, alias);
772 self.keys.insert(address, key);
773 Ok(())
774 }
775
776 fn remove_key(&mut self, address: &Address) -> Result<(), anyhow::Error> {
777 self.aliases.remove(address);
778 self.keys.remove(address);
779 Ok(())
780 }
781
782 fn aliases(&self) -> Vec<&Alias> {
784 self.aliases.values().collect()
785 }
786
787 fn addresses_with_alias(&self) -> Vec<(&Address, &Alias)> {
788 self.aliases.iter().collect::<Vec<_>>()
789 }
790
791 fn keys(&self) -> Vec<&StoredKey> {
792 self.keys.values().collect()
793 }
794
795 fn get_key(&self, address: &Address) -> Result<&StoredKey, anyhow::Error> {
796 match self.keys.get(address) {
797 Some(key) => Ok(key),
798 None => Err(anyhow!("Cannot find key for address: [{address}]")),
799 }
800 }
801
802 fn get_alias_by_address(&self, address: &Address) -> Result<String, anyhow::Error> {
804 match self.aliases.get(address) {
805 Some(alias) => Ok(alias.alias.clone()),
806 None => bail!("Cannot find alias for address {address}"),
807 }
808 }
809
810 fn get_address_by_alias(&self, alias: String) -> Result<&Address, anyhow::Error> {
812 self.addresses_with_alias()
813 .iter()
814 .find(|x| x.1.alias == alias)
815 .ok_or_else(|| anyhow!("Cannot resolve alias {alias} to an address"))
816 .map(|x| x.0)
817 }
818
819 fn create_alias(&self, alias: Option<String>) -> Result<String, anyhow::Error> {
823 match alias {
824 Some(a) if self.alias_exists(&a) => {
825 bail!("Alias {a} already exists. Please choose another alias.")
826 }
827 Some(a) => validate_alias(&a),
828 None => Ok(random_name(
829 &self
830 .alias_names()
831 .into_iter()
832 .map(|x| x.to_string())
833 .collect::<HashSet<_>>(),
834 )),
835 }
836 }
837
838 fn aliases_mut(&mut self) -> Vec<&mut Alias> {
839 self.aliases.values_mut().collect()
840 }
841
842 fn update_alias(
845 &mut self,
846 old_alias: &str,
847 new_alias: Option<&str>,
848 ) -> Result<String, anyhow::Error> {
849 self.update_alias_value(old_alias, new_alias)
850 }
851}
852
853impl InMemKeystore {
854 pub fn new_insecure_for_tests(initial_key_number: usize) -> Self {
855 let mut rng = StdRng::from_seed([0; 32]);
856 let keys = (0..initial_key_number)
857 .map(|_| get_key_pair_from_rng(&mut rng))
858 .map(|(ad, k)| (ad, IotaKeyPair::Ed25519(k).into()))
859 .collect::<BTreeMap<Address, StoredKey>>();
860
861 let aliases = keys
862 .iter()
863 .zip(random_names(HashSet::new(), keys.len()))
864 .map(|((iota_address, _ikp), alias)| (*iota_address, Alias { alias }))
865 .collect::<BTreeMap<_, _>>();
866
867 Self { aliases, keys }
868 }
869}
870
871fn validate_alias(alias: &str) -> Result<String, anyhow::Error> {
872 let re = Regex::new(r"^[A-Za-z][A-Za-z0-9-_\.]*$")
873 .map_err(|_| anyhow!("Cannot build the regex needed to validate the alias naming"))?;
874 let alias = alias.trim();
875 ensure!(
876 re.is_match(alias),
877 "Invalid alias. A valid alias must start with a letter and can contain only letters, digits, hyphens (-), dots (.), or underscores (_)."
878 );
879 Ok(alias.to_string())
880}
881
882#[cfg(test)]
883mod tests {
884 use crate::keystore::validate_alias;
885
886 #[test]
887 fn validate_alias_test() {
888 assert!(validate_alias("A.B_dash").is_ok());
890 assert!(validate_alias("A.B-C1_dash").is_ok());
891 assert!(validate_alias("abc_123.iota").is_ok());
892 assert!(validate_alias("A.B-C_dash!").is_err());
894 assert!(validate_alias(".B-C_dash!").is_err());
895 assert!(validate_alias("_test").is_err());
896 assert!(validate_alias("123").is_err());
897 assert!(validate_alias("@@123").is_err());
898 assert!(validate_alias("@_Ab").is_err());
899 assert!(validate_alias("_Ab").is_err());
900 assert!(validate_alias("^A").is_err());
901 assert!(validate_alias("-A").is_err());
902 }
903}