soroban_cli/config/
key.rs

1use std::{fmt::Display, str::FromStr};
2
3use serde::{Deserialize, Serialize};
4
5use super::secret::{self, Secret};
6use crate::xdr;
7
8#[derive(thiserror::Error, Debug)]
9pub enum Error {
10    #[error("failed to extract secret from public key ")]
11    SecretPublicKey,
12    #[error(transparent)]
13    Secret(#[from] secret::Error),
14    #[error(transparent)]
15    StrKey(#[from] stellar_strkey::DecodeError),
16    #[error("failed to parse key")]
17    Parse,
18}
19
20#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
21pub enum Key {
22    #[serde(rename = "public_key")]
23    PublicKey(Public),
24    #[serde(rename = "muxed_account")]
25    MuxedAccount(MuxedAccount),
26    #[serde(untagged)]
27    Secret(Secret),
28}
29
30impl Key {
31    pub fn muxed_account(&self, hd_path: Option<usize>) -> Result<xdr::MuxedAccount, Error> {
32        let bytes = match self {
33            Key::Secret(secret) => secret.public_key(hd_path)?.0,
34            Key::PublicKey(Public(key)) => key.0,
35            Key::MuxedAccount(MuxedAccount(stellar_strkey::ed25519::MuxedAccount {
36                ed25519,
37                id,
38            })) => {
39                return Ok(xdr::MuxedAccount::MuxedEd25519(xdr::MuxedAccountMed25519 {
40                    ed25519: xdr::Uint256(*ed25519),
41                    id: *id,
42                }))
43            }
44        };
45        Ok(xdr::MuxedAccount::Ed25519(xdr::Uint256(bytes)))
46    }
47
48    pub fn private_key(
49        &self,
50        hd_path: Option<usize>,
51    ) -> Result<stellar_strkey::ed25519::PrivateKey, Error> {
52        match self {
53            Key::Secret(secret) => Ok(secret.private_key(hd_path)?),
54            _ => Err(Error::SecretPublicKey),
55        }
56    }
57}
58
59impl FromStr for Key {
60    type Err = Error;
61
62    fn from_str(s: &str) -> Result<Self, Self::Err> {
63        if let Ok(secret) = s.parse() {
64            return Ok(Key::Secret(secret));
65        }
66        if let Ok(public_key) = s.parse() {
67            return Ok(Key::PublicKey(public_key));
68        }
69        if let Ok(muxed_account) = s.parse() {
70            return Ok(Key::MuxedAccount(muxed_account));
71        }
72        Err(Error::Parse)
73    }
74}
75
76impl From<stellar_strkey::ed25519::PublicKey> for Key {
77    fn from(value: stellar_strkey::ed25519::PublicKey) -> Self {
78        Key::PublicKey(Public(value))
79    }
80}
81
82impl From<&stellar_strkey::ed25519::PublicKey> for Key {
83    fn from(stellar_strkey::ed25519::PublicKey(key): &stellar_strkey::ed25519::PublicKey) -> Self {
84        stellar_strkey::ed25519::PublicKey(*key).into()
85    }
86}
87
88#[derive(Debug, PartialEq, Eq, serde_with::SerializeDisplay, serde_with::DeserializeFromStr)]
89pub struct Public(pub stellar_strkey::ed25519::PublicKey);
90
91impl FromStr for Public {
92    type Err = stellar_strkey::DecodeError;
93
94    fn from_str(s: &str) -> Result<Self, Self::Err> {
95        Ok(Public(stellar_strkey::ed25519::PublicKey::from_str(s)?))
96    }
97}
98
99impl Display for Public {
100    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101        write!(f, "{}", self.0)
102    }
103}
104
105impl From<&Public> for stellar_strkey::ed25519::MuxedAccount {
106    fn from(Public(stellar_strkey::ed25519::PublicKey(key)): &Public) -> Self {
107        stellar_strkey::ed25519::MuxedAccount {
108            id: 0,
109            ed25519: *key,
110        }
111    }
112}
113
114#[derive(Debug, PartialEq, Eq, serde_with::SerializeDisplay, serde_with::DeserializeFromStr)]
115pub struct MuxedAccount(pub stellar_strkey::ed25519::MuxedAccount);
116
117impl FromStr for MuxedAccount {
118    type Err = stellar_strkey::DecodeError;
119
120    fn from_str(s: &str) -> Result<Self, Self::Err> {
121        Ok(MuxedAccount(
122            stellar_strkey::ed25519::MuxedAccount::from_str(s)?,
123        ))
124    }
125}
126
127impl Display for MuxedAccount {
128    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129        write!(f, "{}", self.0)
130    }
131}
132
133impl TryFrom<Key> for Secret {
134    type Error = Error;
135
136    fn try_from(key: Key) -> Result<Secret, Self::Error> {
137        match key {
138            Key::Secret(secret) => Ok(secret),
139            _ => Err(Error::SecretPublicKey),
140        }
141    }
142}
143
144#[cfg(test)]
145mod test {
146    use super::*;
147
148    fn round_trip(key: &Key) {
149        let serialized = toml::to_string(&key).unwrap();
150        println!("{serialized}");
151        let deserialized: Key = toml::from_str(&serialized).unwrap();
152        assert_eq!(key, &deserialized);
153    }
154
155    #[test]
156    fn public_key() {
157        let key = Key::PublicKey(Public(stellar_strkey::ed25519::PublicKey([0; 32])));
158        round_trip(&key);
159    }
160    #[test]
161    fn muxed_key() {
162        let key: stellar_strkey::ed25519::MuxedAccount =
163            "MA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAAAAAAAAAPCICBKU"
164                .parse()
165                .unwrap();
166        let key = Key::MuxedAccount(MuxedAccount(key));
167        round_trip(&key);
168    }
169    #[test]
170    fn secret_key() {
171        let secret_key = stellar_strkey::ed25519::PrivateKey([0; 32]).to_string();
172        let secret = Secret::SecretKey { secret_key };
173        let key = Key::Secret(secret);
174        round_trip(&key);
175    }
176    #[test]
177    fn secret_seed_phrase() {
178        let seed_phrase = "singer swing mango apple singer swing mango apple singer swing mango apple singer swing mango apple".to_string();
179        let secret = Secret::SeedPhrase { seed_phrase };
180        let key = Key::Secret(secret);
181        round_trip(&key);
182    }
183}