-
Notifications
You must be signed in to change notification settings - Fork 122
Fix/secure store prompting #2313
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d7b9c18
6e56981
beed676
019ae15
bf61b15
ff5bfb8
3cb4abf
426062f
5815575
f75b923
0af0a3f
a7934f8
af9d430
df86683
c174c24
a36c403
968e5db
fb770cf
4c30730
9a38824
db79631
379d408
1b928c6
569432b
bd4b856
9e13756
97c83a5
f57be37
fec719c
aa40550
dc3f5bb
383c9ea
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -17,7 +17,7 @@ pub enum Error { | |||||
| Parse, | ||||||
| } | ||||||
|
|
||||||
| #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] | ||||||
| #[derive(Debug, Serialize, Deserialize, Clone)] | ||||||
| pub enum Key { | ||||||
| #[serde(rename = "public_key")] | ||||||
| PublicKey(Public), | ||||||
|
|
@@ -85,7 +85,9 @@ impl From<&stellar_strkey::ed25519::PublicKey> for Key { | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| #[derive(Debug, PartialEq, Eq, serde_with::SerializeDisplay, serde_with::DeserializeFromStr)] | ||||||
| #[derive( | ||||||
| Debug, Clone, PartialEq, Eq, serde_with::SerializeDisplay, serde_with::DeserializeFromStr, | ||||||
| )] | ||||||
| pub struct Public(pub stellar_strkey::ed25519::PublicKey); | ||||||
|
|
||||||
| impl FromStr for Public { | ||||||
|
|
@@ -111,7 +113,9 @@ impl From<&Public> for stellar_strkey::ed25519::MuxedAccount { | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| #[derive(Debug, PartialEq, Eq, serde_with::SerializeDisplay, serde_with::DeserializeFromStr)] | ||||||
| #[derive( | ||||||
| Debug, Clone, PartialEq, Eq, serde_with::SerializeDisplay, serde_with::DeserializeFromStr, | ||||||
| )] | ||||||
| pub struct MuxedAccount(pub stellar_strkey::ed25519::MuxedAccount); | ||||||
|
|
||||||
| impl FromStr for MuxedAccount { | ||||||
|
|
@@ -143,13 +147,66 @@ impl TryFrom<Key> for Secret { | |||||
|
|
||||||
| #[cfg(test)] | ||||||
| mod test { | ||||||
| use std::sync::Arc; | ||||||
|
|
||||||
| use super::*; | ||||||
|
|
||||||
| fn round_trip(key: &Key) { | ||||||
| let serialized = toml::to_string(&key).unwrap(); | ||||||
| println!("{serialized}"); | ||||||
| let deserialized: Key = toml::from_str(&serialized).unwrap(); | ||||||
| assert_eq!(key, &deserialized); | ||||||
|
|
||||||
| assert_key_equality(key, &deserialized); | ||||||
| } | ||||||
|
|
||||||
| // using this fn instead of just doing assert_eq!(key, deserialized) because Secret::SecureStore keys contain a StellarEntry which contains a keyring::Entry | ||||||
| // keyring::Entry comes from the keyring crate which does not implement PartialEq | ||||||
| fn assert_key_equality(expected: &Key, actual: &Key) { | ||||||
| match (expected, actual) { | ||||||
| (Key::PublicKey(e), Key::PublicKey(a)) => { | ||||||
| assert_eq!(e, a); | ||||||
| } | ||||||
| (Key::MuxedAccount(e), Key::MuxedAccount(a)) => { | ||||||
| assert_eq!(e, a); | ||||||
| } | ||||||
| (Key::Secret(e), Key::Secret(a)) => match (e, a) { | ||||||
| ( | ||||||
| Secret::SecretKey { | ||||||
| secret_key: e_secret_key, | ||||||
| }, | ||||||
| Secret::SecretKey { | ||||||
| secret_key: a_secret_key, | ||||||
| }, | ||||||
| ) => { | ||||||
| assert_eq!(e_secret_key, a_secret_key); | ||||||
| } | ||||||
| ( | ||||||
| Secret::SeedPhrase { | ||||||
| seed_phrase: e_seed_phrase, | ||||||
| }, | ||||||
| Secret::SeedPhrase { | ||||||
| seed_phrase: a_seed_phrase, | ||||||
| }, | ||||||
| ) => { | ||||||
| assert_eq!(e_seed_phrase, a_seed_phrase); | ||||||
| } | ||||||
| (Secret::Ledger, Secret::Ledger) => todo!(), | ||||||
| ( | ||||||
| Secret::SecureStore { | ||||||
| entry_name: e_entry_name, | ||||||
| cached_entry: e_cached_entry, | ||||||
| }, | ||||||
| Secret::SecureStore { | ||||||
| entry_name: a_entry_name, | ||||||
| cached_entry: a_cached_entry, | ||||||
| }, | ||||||
| ) => { | ||||||
| assert_eq!(e_entry_name, a_entry_name); | ||||||
| assert!(Arc::ptr_eq(e_cached_entry, a_cached_entry)); | ||||||
|
||||||
| assert!(Arc::ptr_eq(e_cached_entry, a_cached_entry)); | |
| // Do not compare cached_entry pointer identity; cache is runtime-only and not serialized. |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,8 @@ | ||||||||||
| use directories::UserDirs; | ||||||||||
| use itertools::Itertools; | ||||||||||
| use serde::de::DeserializeOwned; | ||||||||||
| use std::collections::HashMap; | ||||||||||
| use std::sync::{Arc, Mutex, OnceLock}; | ||||||||||
| use std::{ | ||||||||||
| ffi::OsStr, | ||||||||||
| fmt::Display, | ||||||||||
|
|
@@ -14,7 +16,7 @@ use stellar_strkey::{Contract, DecodeError}; | |||||||||
| use crate::{ | ||||||||||
| commands::{global, HEADING_GLOBAL}, | ||||||||||
| print::Print, | ||||||||||
| signer::secure_store, | ||||||||||
| signer::secure_store_entry::{self, SecureStoreEntry}, | ||||||||||
| utils::find_config_dir, | ||||||||||
| xdr, Pwd, | ||||||||||
| }; | ||||||||||
|
|
@@ -96,7 +98,7 @@ pub enum Error { | |||||||||
| #[error("Key cannot {0} cannot overlap with contract alias")] | ||||||||||
| KeyCannotOverlapWithContractAlias(String), | ||||||||||
| #[error(transparent)] | ||||||||||
| SecureStore(#[from] secure_store::Error), | ||||||||||
| SecureStoreEntry(#[from] secure_store_entry::Error), | ||||||||||
| #[error("Only private keys and seed phrases are supported for getting private keys {0}")] | ||||||||||
| SecretKeyOnly(String), | ||||||||||
| #[error(transparent)] | ||||||||||
|
|
@@ -105,6 +107,8 @@ pub enum Error { | |||||||||
| ProjectDirsError(), | ||||||||||
| } | ||||||||||
|
|
||||||||||
| pub type CachedKeys = HashMap<String, Key>; | ||||||||||
|
|
||||||||||
| #[derive(Debug, clap::Args, Default, Clone)] | ||||||||||
| #[group(skip)] | ||||||||||
| pub struct Args { | ||||||||||
|
|
@@ -116,6 +120,10 @@ pub struct Args { | |||||||||
| /// Contains configuration files, aliases, and other persistent settings. | ||||||||||
| #[arg(long, global = true, help_heading = HEADING_GLOBAL)] | ||||||||||
| pub config_dir: Option<PathBuf>, | ||||||||||
|
|
||||||||||
| #[clap(skip)] | ||||||||||
| // This saves us from reading the same key from the file system more than once for one cmd | ||||||||||
|
||||||||||
| // This saves us from reading the same key from the file system more than once for one cmd | |
| /// Caches loaded keys to avoid reading the same key from the filesystem multiple times during a single command execution. | |
| /// This field is initialized once per command and shared via an `Arc<Mutex<...>>` to ensure thread-safe access. | |
| /// It improves performance by preventing redundant disk I/O when the same key is requested multiple times. |
elizabethengelman marked this conversation as resolved.
Show resolved
Hide resolved
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential panic from unwrap() call on mutex lock. If the mutex is poisoned, this will panic instead of returning a proper error. Consider using map_err to convert the poison error into a proper Error type, similar to how it's handled in keyring.rs line 80-81.
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential panic from unwrap() call on mutex lock. If the mutex is poisoned, this will panic instead of returning a proper error. Consider using map_err to convert the poison error into a proper Error type, similar to how it's handled in keyring.rs line 80-81.
| let mut map = arc.lock().unwrap(); | |
| let mut map = arc | |
| .lock() | |
| .map_err(|e| Error::ConfigIo(format!("Failed to lock cached_keys mutex: {e}")))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Placeholder
todo!()in test code will panic if Ledger equality check is ever executed. Either implement the equality check for Ledger keys or add a comment explaining why this case doesn't need to be handled in current tests.