soroban_cli/config/
address.rs

1use std::{
2    fmt::{self, Display, Formatter},
3    str::FromStr,
4};
5
6use crate::{
7    signer::{self, ledger},
8    xdr,
9};
10
11use super::{key, locator, secret};
12
13/// Address can be either a public key or eventually an alias of a address.
14#[derive(Clone, Debug)]
15pub enum UnresolvedMuxedAccount {
16    Resolved(xdr::MuxedAccount),
17    AliasOrSecret(String),
18    Ledger(u32),
19}
20
21impl Default for UnresolvedMuxedAccount {
22    fn default() -> Self {
23        UnresolvedMuxedAccount::AliasOrSecret(String::default())
24    }
25}
26
27#[derive(thiserror::Error, Debug)]
28pub enum Error {
29    #[error(transparent)]
30    Locator(#[from] locator::Error),
31    #[error(transparent)]
32    Secret(#[from] secret::Error),
33    #[error(transparent)]
34    Signer(#[from] signer::Error),
35    #[error(transparent)]
36    Key(#[from] key::Error),
37    #[error("Address cannot be used to sign {0}")]
38    CannotSign(xdr::MuxedAccount),
39    #[error("Ledger cannot reveal private keys")]
40    LedgerPrivateKeyRevealNotSupported,
41    #[error("Invalid key name: {0}\n `ledger` is not allowed")]
42    LedgerIsInvalidKeyName(String),
43    #[error("Invalid key name: {0}\n only alphanumeric characters, underscores (_), and hyphens (-) are allowed.")]
44    InvalidKeyNameCharacters(String),
45    #[error("Invalid key name: {0}\n keys cannot exceed 250 characters")]
46    InvalidKeyNameLength(String),
47    #[error("Invalid key name: {0}\n keys cannot be the word \"ledger\"")]
48    InvalidKeyName(String),
49    #[error("Ledger not supported in this context")]
50    LedgerNotSupported,
51    #[error(transparent)]
52    Ledger(#[from] signer::ledger::Error),
53}
54
55impl FromStr for UnresolvedMuxedAccount {
56    type Err = Error;
57
58    fn from_str(value: &str) -> Result<Self, Self::Err> {
59        if value.starts_with("ledger") {
60            if let Some(ledger) = parse_ledger(value) {
61                return Ok(UnresolvedMuxedAccount::Ledger(ledger));
62            }
63        }
64        Ok(xdr::MuxedAccount::from_str(value).map_or_else(
65            |_| UnresolvedMuxedAccount::AliasOrSecret(value.to_string()),
66            UnresolvedMuxedAccount::Resolved,
67        ))
68    }
69}
70
71fn parse_ledger(value: &str) -> Option<u32> {
72    let vals: Vec<_> = value.split(':').collect();
73    if vals.len() > 2 {
74        return None;
75    }
76    if vals.len() == 1 {
77        return Some(0);
78    }
79    vals[1].parse().ok()
80}
81
82impl UnresolvedMuxedAccount {
83    pub async fn resolve_muxed_account(
84        &self,
85        locator: &locator::Args,
86        hd_path: Option<usize>,
87    ) -> Result<xdr::MuxedAccount, Error> {
88        match self {
89            UnresolvedMuxedAccount::Ledger(hd_path) => Ok(xdr::MuxedAccount::Ed25519(
90                ledger::new(*hd_path).await?.public_key().await?.0.into(),
91            )),
92            UnresolvedMuxedAccount::Resolved(_) | UnresolvedMuxedAccount::AliasOrSecret(_) => {
93                self.resolve_muxed_account_sync(locator, hd_path)
94            }
95        }
96    }
97
98    pub fn resolve_muxed_account_sync(
99        &self,
100        locator: &locator::Args,
101        hd_path: Option<usize>,
102    ) -> Result<xdr::MuxedAccount, Error> {
103        match self {
104            UnresolvedMuxedAccount::Resolved(muxed_account) => Ok(muxed_account.clone()),
105            UnresolvedMuxedAccount::AliasOrSecret(alias_or_secret) => {
106                Ok(locator.read_key(alias_or_secret)?.muxed_account(hd_path)?)
107            }
108            UnresolvedMuxedAccount::Ledger(_) => Err(Error::LedgerNotSupported),
109        }
110    }
111
112    pub fn resolve_secret(&self, locator: &locator::Args) -> Result<secret::Secret, Error> {
113        match &self {
114            UnresolvedMuxedAccount::Resolved(muxed_account) => {
115                Err(Error::CannotSign(muxed_account.clone()))
116            }
117            UnresolvedMuxedAccount::AliasOrSecret(alias_or_secret) => {
118                Ok(locator.read_key(alias_or_secret)?.try_into()?)
119            }
120            UnresolvedMuxedAccount::Ledger(_) => Err(Error::LedgerPrivateKeyRevealNotSupported),
121        }
122    }
123}
124
125#[derive(Clone, Debug)]
126pub struct KeyName(pub String);
127
128impl std::ops::Deref for KeyName {
129    type Target = str;
130    fn deref(&self) -> &Self::Target {
131        &self.0
132    }
133}
134
135impl std::str::FromStr for KeyName {
136    type Err = Error;
137    fn from_str(s: &str) -> Result<Self, Self::Err> {
138        if !s.chars().all(allowed_char) {
139            return Err(Error::InvalidKeyNameCharacters(s.to_string()));
140        }
141        if s == "ledger" {
142            return Err(Error::InvalidKeyName(s.to_string()));
143        }
144        if s.len() > 250 {
145            return Err(Error::InvalidKeyNameLength(s.to_string()));
146        }
147        Ok(KeyName(s.to_string()))
148    }
149}
150
151impl Display for KeyName {
152    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
153        write!(f, "{}", self.0)
154    }
155}
156
157fn allowed_char(c: char) -> bool {
158    c.is_ascii_alphanumeric() || c == '_' || c == '-'
159}