stellar_baselib/
signer_key.rs

1use core::panic;
2use std::{collections::HashMap, str::FromStr};
3
4use crate::xdr;
5use crate::xdr::{SignerKey as XDRSignerKey, SignerKeyEd25519SignedPayload};
6use stellar_strkey::{
7    ed25519::{PublicKey, SignedPayload},
8    HashX, PreAuthTx,
9};
10
11pub struct SignerKey;
12
13// Define a trait for SignerKey behavior
14pub trait SignerKeyBehavior {
15    fn decode_address(address: &str) -> XDRSignerKey;
16    fn encode_signer_key(signer_key: &XDRSignerKey) -> String;
17}
18
19impl SignerKeyBehavior for SignerKey {
20    fn decode_address(address: &str) -> XDRSignerKey {
21        let val = stellar_strkey::Strkey::from_string(address);
22        if val.is_err() {
23            panic!("Invalid Type")
24        }
25
26        match val.unwrap() {
27            stellar_strkey::Strkey::SignedPayloadEd25519(x) => {
28                XDRSignerKey::Ed25519SignedPayload(SignerKeyEd25519SignedPayload {
29                    ed25519: xdr::Uint256(x.ed25519),
30                    payload: x.payload.try_into().unwrap(),
31                })
32            }
33            stellar_strkey::Strkey::PublicKeyEd25519(x) => XDRSignerKey::Ed25519(xdr::Uint256(x.0)),
34            stellar_strkey::Strkey::PreAuthTx(x) => XDRSignerKey::PreAuthTx(xdr::Uint256(x.0)),
35            stellar_strkey::Strkey::HashX(x) => XDRSignerKey::HashX(xdr::Uint256(x.0)),
36            _ => panic!("Invalid Type"),
37        }
38    }
39
40    fn encode_signer_key(signer_key: &XDRSignerKey) -> String {
41        match signer_key {
42            XDRSignerKey::Ed25519(x) => {
43                stellar_strkey::Strkey::PublicKeyEd25519(PublicKey::from_payload(&x.0).unwrap())
44                    .to_string()
45            }
46            XDRSignerKey::PreAuthTx(x) => {
47                stellar_strkey::Strkey::PreAuthTx(PreAuthTx(x.0)).to_string()
48            }
49            XDRSignerKey::HashX(x) => stellar_strkey::Strkey::HashX(HashX(x.0)).to_string(),
50            XDRSignerKey::Ed25519SignedPayload(x) => {
51                stellar_strkey::Strkey::SignedPayloadEd25519(SignedPayload {
52                    ed25519: x.ed25519.0,
53                    payload: x.payload.clone().into_vec(),
54                })
55                .to_string()
56            }
57        }
58    }
59}
60
61fn assert_panic<F: FnOnce(), S: AsRef<str>>(f: F, expected_msg: S) {
62    let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f));
63    match result {
64        Ok(_) => panic!("Function did not panic as expected"),
65        Err(err) => {
66            if let Some(s) = err.downcast_ref::<&str>() {
67                assert!(
68                    s.contains(expected_msg.as_ref()),
69                    "Unexpected panic message. Got: {}",
70                    s
71                );
72            } else {
73                panic!("Unexpected panic type");
74            }
75        }
76    }
77}
78
79mod tests {
80    use xdr::{ReadXdr, WriteXdr};
81
82    use super::*;
83    #[derive(Debug)]
84    struct TestCase {
85        strkey: &'static str,
86        r#type: xdr::SignerKeyType,
87    }
88
89    static TEST_CASES: [TestCase; 4] = [
90        TestCase {
91            strkey: "GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ",
92            r#type: xdr::SignerKeyType::Ed25519,
93        },
94        TestCase {
95            strkey: "TBU2RRGLXH3E5CQHTD3ODLDF2BWDCYUSSBLLZ5GNW7JXHDIYKXZWHXL7",
96            r#type: xdr::SignerKeyType::PreAuthTx,
97        },
98        TestCase {
99            strkey: "XBU2RRGLXH3E5CQHTD3ODLDF2BWDCYUSSBLLZ5GNW7JXHDIYKXZWGTOG",
100            r#type: xdr::SignerKeyType::HashX,
101        },
102        TestCase {
103            strkey: "PA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAQACAQDAQCQMBYIBEFAWDANBYHRAEISCMKBKFQXDAMRUGY4DUPB6IBZGM",
104            r#type: xdr::SignerKeyType::Ed25519SignedPayload,
105        },
106    ];
107
108    #[test]
109    fn test_encode_decode_roundtrip() {
110        for test_case in &TEST_CASES {
111            let skey = SignerKey::decode_address(test_case.strkey);
112
113            assert_eq!(skey.discriminant(), test_case.r#type);
114
115            let raw_xdr = skey.to_xdr(xdr::Limits::none()).unwrap();
116            let raw_sk = xdr::SignerKey::from_xdr(raw_xdr, xdr::Limits::none()).unwrap();
117            assert_eq!(raw_sk, skey);
118
119            let address = SignerKey::encode_signer_key(&skey);
120            assert_eq!(address, test_case.strkey);
121        }
122    }
123
124    #[test]
125    fn error_cases_for_invalid_signers() {
126        let invalid_signers = &[
127            "SAB5556L5AN5KSR5WF7UOEFDCIODEWEO7H2UR4S5R62DFTQOGLKOVZDY",
128            "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK",
129            "NONSENSE",
130        ];
131
132        for strkey in invalid_signers.iter() {
133            let scenario_1 = || {
134                SignerKey::decode_address(strkey);
135            };
136            assert_panic(scenario_1, "Invalid Type")
137        }
138    }
139
140    #[test]
141    fn error_cases_for_invalid_strkey() {
142        let strkey = "G47QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVP2I";
143        let scenario_1 = || {
144            SignerKey::decode_address(strkey);
145        };
146        assert_panic(scenario_1, "Invalid Type")
147    }
148}