soroban_cli/config/
address.rs1use 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#[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}