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