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