Skip to main content

ur_parse_lib/
keystone_ur_decoder.rs

1use crate::ur::UR;
2use alloc::string::{String, ToString};
3use alloc::vec::Vec;
4use core::fmt;
5use ur::ur::Kind;
6use ur_registry::error::{URError, URResult};
7use ur_registry::registry_types::URType;
8
9pub fn probe_decode<T: TryFrom<Vec<u8>, Error = URError>>(
10    part: String,
11) -> URResult<URParseResult<T>> {
12    let mut ur_parse_result = URParseResult {
13        is_multi_part: false,
14        progress: 0,
15        ur_type: None,
16        data: None,
17        decoder: None,
18    };
19    let decoded = ur::decode(&part).map_err(|e| URError::UrDecodeError(e.to_string()))?;
20    match decoded.0 {
21        Kind::SinglePart => {
22            ur_parse_result.is_multi_part = false;
23            ur_parse_result.progress = 100;
24            let ur_type = get_type(&part)?;
25            ur_parse_result.ur_type = Some(ur_type.clone());
26            let ur = UR::new(ur_type, decoded.1);
27            ur_parse_result.data = Some(ur.parse()?.1);
28        }
29        Kind::MultiPart => {
30            ur_parse_result.is_multi_part = true;
31            let mut decoder = ur::Decoder::default();
32            decoder
33                .receive(&part)
34                .map_err(|e| URError::UrDecodeError(e.to_string()))?;
35            ur_parse_result.progress = decoder.progress();
36            ur_parse_result.decoder = Some(KeystoneURDecoder { decoder })
37        }
38    }
39    Ok(ur_parse_result)
40}
41
42pub fn get_type(part: &String) -> URResult<URType> {
43    let part = part.to_lowercase();
44    let strip_scheme = part.strip_prefix("ur:").ok_or(URError::NotAUr)?;
45    let (type_, _) = strip_scheme
46        .split_once('/')
47        .ok_or(URError::TypeUnspecified)?;
48    URType::from(type_)
49}
50
51pub struct KeystoneURDecoder {
52    decoder: ur::Decoder,
53}
54
55impl KeystoneURDecoder {
56    pub fn parse_ur<T: TryFrom<Vec<u8>, Error = URError>>(
57        &mut self,
58        part: String,
59    ) -> URResult<MultiURParseResult<T>> {
60        let mut ur_parse_result = MultiURParseResult {
61            is_complete: false,
62            progress: 0,
63            ur_type: None,
64            data: None,
65        };
66        self.decoder
67            .receive(&part)
68            .map_err(|e| URError::UrDecodeError(e.to_string()))?;
69        if self.decoder.complete() {
70            let cbor = self
71                .decoder
72                .message()
73                .map_err(|e| URError::UrDecodeError(e.to_string()))?;
74            match cbor {
75                Some(cbor) => {
76                    ur_parse_result.is_complete = true;
77                    ur_parse_result.progress = 100;
78                    let ur_type = get_type(&part)?;
79                    ur_parse_result.ur_type = Some(ur_type.clone());
80                    let ur = UR::new(ur_type, cbor);
81                    ur_parse_result.data = Some(ur.parse()?.1);
82                }
83                None => {
84                    return Err(URError::UrDecodeError("cbor is none".to_string()));
85                }
86            }
87        } else {
88            ur_parse_result.data = None;
89            ur_parse_result.is_complete = false;
90            ur_parse_result.progress = self.decoder.progress();
91        }
92
93        Ok(ur_parse_result)
94    }
95}
96
97pub struct URParseResult<T> {
98    pub is_multi_part: bool,
99    pub progress: u8,
100    pub ur_type: Option<URType>,
101    pub data: Option<T>,
102    pub decoder: Option<KeystoneURDecoder>,
103}
104
105#[derive(Debug)]
106pub struct MultiURParseResult<T> {
107    pub is_complete: bool,
108    pub progress: u8,
109    pub ur_type: Option<URType>,
110    pub data: Option<T>,
111}
112
113impl<T: fmt::Debug> fmt::Debug for URParseResult<T> {
114    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        f.debug_tuple("")
116            .field(&self.is_multi_part)
117            .field(&self.progress)
118            .field(&self.ur_type)
119            .field(&self.data)
120            .finish()
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use crate::keystone_ur_decoder::{probe_decode, MultiURParseResult, URParseResult};
127    use alloc::string::ToString;
128    use alloc::vec;
129    use alloc::{string::String, vec::Vec};
130    use ur_registry::crypto_psbt::CryptoPSBT;
131    use ur_registry::ethereum::eth_sign_request::EthSignRequest;
132    use ur_registry::sui::sui_sign_request::SuiSignRequest;
133    use ur_registry::{
134        cardano::cardano_sign_request::CardanoSignRequest, crypto_key_path::CryptoKeyPath,
135    };
136    use ur_registry::{
137        cardano::cardano_sign_tx_hash_request::CardanoSignTxHashRequest,
138        crypto_key_path::PathComponent,
139    };
140    #[test]
141    fn test_decode_psbt() {
142        let ur = "ur:crypto-psbt/hdcxlkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypkvoonhknt";
143        let result: URParseResult<CryptoPSBT> = probe_decode(ur.to_string()).unwrap();
144        if !result.is_multi_part {
145            let crypto = result.data.unwrap();
146            assert_eq!(
147                "8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa",
148                hex::encode(crypto.get_psbt()).to_lowercase()
149            );
150        }
151
152        let ur1 = "ur:crypto-psbt/1-3/lpadaxcfaxiacyvwhdfhndhkadclhkaxhnlkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbnychpmiy";
153        let ur2 = "ur:crypto-psbt/2-3/lpaoaxcfaxiacyvwhdfhndhkadclaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaylbntahvo";
154        let ur3 = "ur:crypto-psbt/3-3/lpaxaxcfaxiacyvwhdfhndhkadclpklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypknseoskve";
155        let result: URParseResult<CryptoPSBT> = probe_decode(ur1.to_string()).unwrap();
156        if result.is_multi_part {
157            let mut decoder = result.decoder.unwrap();
158            let _result: MultiURParseResult<CryptoPSBT> =
159                decoder.parse_ur(ur2.to_string()).unwrap();
160            let result: MultiURParseResult<CryptoPSBT> = decoder.parse_ur(ur3.to_string()).unwrap();
161            let psbt = result.data.unwrap();
162            assert_eq!("8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa",
163                       hex::encode(psbt.get_psbt()).to_lowercase())
164        }
165    }
166
167    #[test]
168    fn test_decode_eth_sign_request() {
169        let ur = "ur:eth-sign-request/onadtpdagdwnbstbpfkidafxlbprqzdiktfldlaxheaohddlaoweaalalrhkisdlaelrhkisdlcwlfgmaymwvttkvsptykhkfwswosbdlrhhtiknftkihsnbfxdalnhtwfbeknfzaelartaxaaaaaaahtaaddyoeadlocsdwykcsfnykaeykaewkaocyjokbwejzvdrtpssp";
170        let result: URParseResult<EthSignRequest> = probe_decode(ur.to_string()).unwrap();
171        if !result.is_multi_part {
172            let crypto = result.data.unwrap();
173            assert_eq!("02ed04808459682f008459682f1b82520894e0cfe8a9f55942c6a70b845cd07a3a7d61a04325865af3107a400080c0",
174                       hex::encode(crypto.get_sign_data()).to_lowercase());
175        }
176    }
177
178    #[test]
179    fn test_decode_sui_sign_request() {
180        let ur3 = "UR:SUI-SIGN-REQUEST/1268-2/LPCFAAWKAOCFADTTCYBENTKOSPHDWLATROUTDAGDSAIOSPYKDYRTDLBDSAGWCAENTPNNVWMOAYCEWETPIDYTTADWGHBZWFKBHFGROTHLOEAHRSDLFRADKNGHAAVLGOAHHDEMMWYLBZBKWLLKDKGESBHFAHNBLSWLOXAALUBWFLSRCTWELFVOKKLEURENZTCSTIPRNTRSOTGEZTHNGLDIWTMYDSQDFDMDWYPDIYTEMKMSFMFHETSALFCAKIGUCFCTGDFDLNSOSFZEGLSSCTJSGEPSZCSTEYNSYTFESGJPGTDETSFDWETOCESNUEWSYAQZYANELGZCOEAOAOADAEDPDYAEADAEADADAOAXLYTPAXDYWZGABNTTRTRPOLWKCYAMLBNBLNONMWESETIATOSEAXKNYNTELUDWLSESYTUOAENERYZEWTQDWKQZDMLUAETOGYGTIHGMHTFZBNWTSOPYMWZEPRHTQDKTSBCFGMURIDLTWTWPLPUEWFKN";
181        let ur1 = "UR:SUI-SIGN-REQUEST/418-2/LPCFADOEAOCFADTTCYBENTKOSPHDWLONADTPDAGDSAIOVSSSWFIAFGGLNEDKSKFTPYYAGWVTAOHKADJPAEAEAEAEAEAXADAEMNHHCEBKDIBDRLFRCPKNTDTTJOCWDISFRYWEYKADGMGSLEIDLTHKJSBTKSKOLTJNWDOERHAHAEAEAEAECXHPKELEURENZTETDPRKRKLYEEBZPYFTNDPRCFCAJYSEWNEEGMTNKGLONYGASODRIHAEAYUOAEAEAEAEAEAEAEAECXRYWNEOBZLFECYTKGMSOLGDSRFNHFSTASGYDACYFMSATKRFOYZSBBISCMNTLGZCOEAOAOADAEAEADADADAEADADAOAEAEADAOAEGDFDLNSOWPFXRSYLBKWFLBGOLNGDMWSFFTKKNSREFYKKWZGMTEBNTEJSLBBZWPUOAOASZSFZLUVYPDUYMWAOJEEEYTVDCKESZEOECYOSFLMYSNGWHEPYTOKEADPKBDVOLRWPGMBDRKJS";
182        let ur2 = "UR:SUI-SIGN-REQUEST/1072-2/LPCFAADYAOCFADTTCYBENTKOSPHDWLOERHAHAEAEAEAECXEHSROTINFEHLJETPBNJKIYPKJPBKFEWPPKIDYTTADWGHCMWZKBTPCHRSHGLPBAAYBBCFKGPDLPJYYAJPSOVWTNHSYNFLFGIAWYOTBWRDHPKITBAALRGLOLEYCMFLSRCTWEOERHAHAEAEAEAECXZCASDSFMMSHEHGHTTLMDWLMOGMJPRHOYRFJPCAHPAOUEYLBZHLSALESEKIGUCFCTGDFDLNSOWPFXRSYLBKWFLBGOLNGDMWSFFTKKNSREFYKKWZGMTEBNTEJSLBBZWPUOWYAOAEAEAEAEAEAEAEDPEHADAEAEAEAEAEAXLYTAADDYOEADLECSDWYKCFAXBEYKAEYKAEYKAEYKAOCYGMJYFLAXAALYHDCXGDFDLNSOWPFXRSYLBKWFLBGOLNGDMWSFFTKKNSREFYKKWZGMTEBNTEJSLBBZWPUOAHIHGUKPINIHJYAESTZSVEGA";
183        let result: URParseResult<SuiSignRequest> =
184            probe_decode(ur1.to_string().to_lowercase()).unwrap();
185        if result.is_multi_part {
186            let mut decoder = result.decoder.unwrap();
187            let _result: MultiURParseResult<SuiSignRequest> =
188                decoder.parse_ur(ur2.to_string().to_lowercase()).unwrap();
189            let result: MultiURParseResult<SuiSignRequest> =
190                decoder.parse_ur(ur3.to_string().to_lowercase()).unwrap();
191            let sui_sign_request = result.data.unwrap();
192            assert_eq!("00000000000301008e5c1c0a270bb73b227ad2d1701b27ccbdedf501524c8a628759710d7876876deaa2b90500000000205b7c8adf36fc382dbbbb813415ab3a9bb2191d74c1f13452da7b889a49c92a650008dc000000000000000020bdf133158235f97b97a650c33c56c70951251a3ec2cfbca1fa1468169d8dfda20202010000010101000101020000010200504886c9ec43bff70af37f55865094cc3a799cb54479f252d30cd3717f15ecdc0209fa408be1a8db94026b34f9e71e39fea21aa7478fcd4f5fabce7c01aa0be284eca2b905000000002031c3a369455d6bd80c7366aa720a45ecaa62f9d92c5416f27ed817bf57850e0814197ba88574f872c9e5da61f6474663eea313ba5b7dd604844ea6321647c31feda2b9050000000020fd09263e975f575ad595e9925272b9a1bc721d5b02def7155dc28ac17d53191f504886c9ec43bff70af37f55865094cc3a799cb54479f252d30cd3717f15ecdcee02000000000000002d31010000000000",
193                       hex::encode(sui_sign_request.get_intent_message()).to_lowercase());
194            let components = vec![
195                PathComponent::new(Some(44), true).unwrap(),
196                PathComponent::new(Some(784), true).unwrap(),
197                PathComponent::new(Some(0), true).unwrap(),
198                PathComponent::new(Some(0), true).unwrap(),
199                PathComponent::new(Some(0), true).unwrap(),
200            ];
201            let source_fingerprint = hex::decode("52744703").unwrap().try_into().unwrap();
202            let crypto_key_path = vec![CryptoKeyPath::new(
203                components,
204                Some(source_fingerprint),
205                None,
206            )];
207            assert_eq!(crypto_key_path, sui_sign_request.get_derivation_paths());
208            // request id
209            assert_eq!(
210                "c267e8c4f363464e9f24c53aabf84fe0",
211                hex::encode(sui_sign_request.get_request_id().unwrap())
212            );
213            // addresses
214            let addresses = sui_sign_request.get_addresses().unwrap();
215            assert_eq!(
216                vec!["504886c9ec43bff70af37f55865094cc3a799cb54479f252d30cd3717f15ecdc"],
217                addresses
218                    .iter()
219                    .map(|a| hex::encode(a))
220                    .collect::<Vec<String>>()
221            );
222            // origin origin
223            assert_eq!("Suiet", sui_sign_request.get_origin().unwrap());
224        }
225    }
226
227    #[test]
228    fn test_decode_cardano_sign_request() {
229        let ur1 = "UR:CARDANO-SIGN-REQUEST/80-2/LPCSGDAOCFADVECYRKMNIDESHDWZLKVTNDSFASVDPERHHYMWOTTPTBSKGUDRWZSACLOLVTDYHSYNRYMHEHPRRKJTBZWKLBJLZMVLMWONVOFPFGKOHGCAFXAXUOVYRPWLPMREGMPAHDPESGDSDEINLNZTMKIEIHWFUTDWHHJKHLIHWLCLIYJKIYLGFHENBDVEPEEEIMPEDEAYPAGAHDZOAEFTUYNBCHTKKOPETKLPGWCEVACLSKURLDBTCLZTBWHKGAPEHNHSYTRHPFISGYJKJLNDINISENIHHNWPKKSFCKEEDRJOIYSEKGKODTJPCLJPCMEMVARDINPRIHECJEFSROLKLDGEBTRDESURLONEFPINWSFWDARNBACELEJNTKLPCSKBNSWECTHNJEWDCACHWKVTGHBTURTNGYMEEYQDCFWYAXRHLRGSFMTYIMDYENSBSRKGCXLFCFOSSOAXCYLNTOZCNLGDAXPSCXEHKKPSWPLSLACSJNFMDKFMSTKODRKSKSDNEMTB";
230        let ur2 = "UR:CARDANO-SIGN-REQUEST/66-2/LPCSFWAOCFADVECYRKMNIDESHDWZDTVYFXWLHKREOLQDFWRYNYMHMWJZZEMHYTAOAEAXJEEYESEEESENEHEOESENECEHAATAADDYOEADLECFATFNYKCFATCHYKAEYKAEWKAEWKAOCYBGGDRPRFAHKSJZHSIEIEJPHEJYIHJKJYEHJSJSECEEIYKSKNJKEYIEJPJKJOHSEYESIOECKPIAJOISEYETJPJYETJOECKOKTJEDYJKJEIHJKKPKNIMHSKSKOKSJYKTENKTJNJZHSDYKSKTJPISENIHIYEYHSKTISENDYJOIEIHKOKPEOJOETJZJYEOIOESJEDYIHJNKTJKIOKOKNJPKNJZJEJSEYIHESEYKKDYAALYTAAYNSOEADHDCEGLURZSWFETKTTBGMPLRDZSKSHPMTJPCLFHTSBBCMTKTOUTAADYFXCHWPAOTAADDYOEADLECFATFNYKCFATCHYKAEYKAOWKAEWKAOCYBGGDRPRFAHIYIHJYIHJPJTJZHPLTHTSF";
231        let ur3 = "UR:CARDANO-SIGN-REQUEST/95-2/LPCSHEAOCFADVECYRKMNIDESHDWZLKVTNDSFASVDPERHHYMWOTTPTBSKGUDRWZSACLOLVTDYHSYNRYMHEHPRRKJTBZWKLBJLZMVLMWONVOFPFGKOHGCAFXAXUOVYRPWLPMREGMPAHDPESGDSDEINLNZTMKIEIHWFUTDWHHJKHLIHWLCLIYJKIYLGFHENBDVEPEEEIMPEDEAYPAGAHDZOAEFTUYNBCHTKKOPETKLPGWCEVACLSKURLDBTCLZTBWHKGAPEHNHSYTRHPFISGYJKJLNDINISENIHHNWPKKSFCKEEDRJOIYSEKGKODTJPCLJPCMEMVARDINPRIHECJEFSROLKLDGEBTRDESURLONEFPINWSFWDARNBACELEJNTKLPCSKBNSWECTHNJEWDCACHWKVTGHBTURTNGYMEEYQDCFWYAXRHLRGSFMTYIMDYENSBSRKGCXLFCFOSSOAXCYLNTOZCNLGDAXPSCXEHKKPSWPLSLACSJNFMDKFMSTKODRKSIHSNWEMH";
232
233        let result: URParseResult<CardanoSignRequest> =
234            probe_decode(ur1.to_string().to_lowercase()).unwrap();
235        if result.is_multi_part {
236            let mut decoder = result.decoder.unwrap();
237            let _result: MultiURParseResult<CardanoSignRequest> =
238                decoder.parse_ur(ur2.to_string().to_lowercase()).unwrap();
239            let result: MultiURParseResult<CardanoSignRequest> =
240                decoder.parse_ur(ur3.to_string().to_lowercase()).unwrap();
241            let ada_sign_request = result.data.unwrap();
242            assert_eq!("84a60081825820c57bb6fed336a46858414aa204441429e143e959b5a6b342bd9a90946cfe90f90001818258390029549850534700f545453980dd471ace1a31d67c2d987052e99865bb4edffaf33877d652aebafa785b9672213fd71416cfcedd04304317ec1b00000006de18bb76021a0002a40d031a02191e62048183028200581c4edffaf33877d652aebafa785b9672213fd71416cfcedd04304317ec581c3c4fb94e1a2c5649a870aee5a70f21cd64807c7dc38632efcaf3d9210800a0f5f6",
243                       hex::encode(ada_sign_request.get_sign_data()).to_lowercase());
244            let components = vec![
245                PathComponent::new(Some(1852), true).unwrap(),
246                PathComponent::new(Some(1815), true).unwrap(),
247                PathComponent::new(Some(0), true).unwrap(),
248                PathComponent::new(Some(0), false).unwrap(),
249                PathComponent::new(Some(0), false).unwrap(),
250            ];
251            let source_fingerprint = hex::decode("1250b6bc").unwrap().try_into().unwrap();
252            let crypto_key_path = vec![CryptoKeyPath::new(
253                components,
254                Some(source_fingerprint),
255                None,
256            )];
257            assert_eq!(
258                crypto_key_path,
259                ada_sign_request
260                    .get_utxos()
261                    .iter()
262                    .map(|u| u.get_key_path())
263                    .collect::<Vec<CryptoKeyPath>>()
264            );
265
266            assert_eq!(1, ada_sign_request.get_utxos().len());
267            let components = vec![
268                PathComponent::new(Some(1852), true).unwrap(),
269                PathComponent::new(Some(1815), true).unwrap(),
270                PathComponent::new(Some(0), true).unwrap(),
271                PathComponent::new(Some(2), false).unwrap(),
272                PathComponent::new(Some(0), false).unwrap(),
273            ];
274            let source_fingerprint = hex::decode("1250b6bc").unwrap().try_into().unwrap();
275            let crypto_key_path = vec![CryptoKeyPath::new(
276                components,
277                Some(source_fingerprint),
278                None,
279            )];
280            assert_eq!(
281                crypto_key_path,
282                ada_sign_request
283                    .get_cert_keys()
284                    .iter()
285                    .map(|k| k.get_key_path())
286                    .collect::<Vec<CryptoKeyPath>>()
287            );
288            // request id
289            assert_eq!(
290                "52090a1c29394842a9adba0bc021a58b",
291                hex::encode(ada_sign_request.get_request_id().unwrap())
292            );
293            // origin origin
294            assert_eq!("eternl", ada_sign_request.get_origin().unwrap());
295        }
296    }
297
298    #[test]
299    fn test_decode_cardano_sign_tx_hash_request() {
300        let ur = "UR:CARDANO-SIGN-TX-HASH-REQUEST/OXADTPDAGDGMASBKCEDTESFDFWPTPMRDBDRTCLONLUAOKSFZECEYHSEHIYECECESENIYEOEHEOECETDYEODYIYDYIEESIEEOHSEYIEIDEYIDEHEHESIDETIYEMENENEOETENDYEMEHENETEEIEEYENIEDYIEEOEMEEEOESIAEHEEEEIHAXLFTAADDYOEADLECFATFNYKCFATCHYKAEYKAEWKAEWKAOCYBGGDRPRFTAADDYOEADLECFATFNYKCFATCHYKAEYKAOWKAEWKAOCYBGGDRPRFAAIYIHJYIHJPJTJZAMAXZMAM";
301        let result: URParseResult<CardanoSignTxHashRequest> =
302            probe_decode(ur.to_string().to_lowercase()).unwrap();
303        if !result.is_multi_part {
304            let crypto = result.data.unwrap();
305            // origin origin
306            assert_eq!("eternl", crypto.get_origin().unwrap());
307            // request id
308            assert_eq!(
309                "52090a1c29394842a9adba0bc021a58b",
310                hex::encode(crypto.get_request_id().unwrap())
311            );
312            let expected_tx_hash =
313                "52a1f5596f31358030f0d9d3a2db2b119b8f766386071684d26d0d37439c144e";
314            assert_eq!(expected_tx_hash, crypto.get_tx_hash());
315            // path
316            let components1 = vec![
317                PathComponent::new(Some(1852), true).unwrap(),
318                PathComponent::new(Some(1815), true).unwrap(),
319                PathComponent::new(Some(0), true).unwrap(),
320                PathComponent::new(Some(2), false).unwrap(),
321                PathComponent::new(Some(0), false).unwrap(),
322            ];
323            let source_fingerprint1 = hex::decode("1250b6bc").unwrap().try_into().unwrap();
324
325            let components2 = vec![
326                PathComponent::new(Some(1852), true).unwrap(),
327                PathComponent::new(Some(1815), true).unwrap(),
328                PathComponent::new(Some(0), true).unwrap(),
329                PathComponent::new(Some(0), false).unwrap(),
330                PathComponent::new(Some(0), false).unwrap(),
331            ];
332            let source_fingerprint2 = hex::decode("1250b6bc").unwrap().try_into().unwrap();
333
334            let crypto_key_path1 = CryptoKeyPath::new(components1, Some(source_fingerprint1), None);
335            let crypto_key_path2 = CryptoKeyPath::new(components2, Some(source_fingerprint2), None);
336
337            let expected_paths = vec![crypto_key_path2, crypto_key_path1];
338            assert_eq!(expected_paths, crypto.get_paths());
339        }
340    }
341}