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 crate::keystone_ur_encoder::probe_encode;
128 use alloc::string::ToString;
129 use alloc::vec;
130 use alloc::{string::String, vec::Vec};
131 use ur_registry::crypto_psbt::CryptoPSBT;
132 use ur_registry::ethereum::eth_sign_request::EthSignRequest;
133 use ur_registry::sui::sui_sign_request::SuiSignRequest;
134 use ur_registry::traits::RegistryItem;
135 use ur_registry::zcash::zcash_accounts::ZcashAccounts;
136 use ur_registry::zcash::zcash_unified_full_viewing_key::ZcashUnifiedFullViewingKey;
137 use ur_registry::{
138 cardano::cardano_sign_request::CardanoSignRequest, crypto_key_path::CryptoKeyPath,
139 };
140 use ur_registry::{
141 cardano::cardano_sign_tx_hash_request::CardanoSignTxHashRequest,
142 crypto_key_path::PathComponent,
143 };
144 #[test]
145 fn test_decode_psbt() {
146 let ur = "ur:crypto-psbt/hdcxlkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypkvoonhknt";
147 let result: URParseResult<CryptoPSBT> = probe_decode(ur.to_string()).unwrap();
148 if !result.is_multi_part {
149 let crypto = result.data.unwrap();
150 assert_eq!(
151 "8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa",
152 hex::encode(crypto.get_psbt()).to_lowercase()
153 );
154 }
155
156 let ur1 = "ur:crypto-psbt/1-3/lpadaxcfaxiacyvwhdfhndhkadclhkaxhnlkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbnychpmiy";
157 let ur2 = "ur:crypto-psbt/2-3/lpaoaxcfaxiacyvwhdfhndhkadclaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaylbntahvo";
158 let ur3 = "ur:crypto-psbt/3-3/lpaxaxcfaxiacyvwhdfhndhkadclpklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypknseoskve";
159 let result: URParseResult<CryptoPSBT> = probe_decode(ur1.to_string()).unwrap();
160 if result.is_multi_part {
161 let mut decoder = result.decoder.unwrap();
162 let _result: MultiURParseResult<CryptoPSBT> =
163 decoder.parse_ur(ur2.to_string()).unwrap();
164 let result: MultiURParseResult<CryptoPSBT> = decoder.parse_ur(ur3.to_string()).unwrap();
165 let psbt = result.data.unwrap();
166 assert_eq!("8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa",
167 hex::encode(psbt.get_psbt()).to_lowercase())
168 }
169 }
170
171 #[test]
172 fn test_decode_zcash_accounts_registry_ur() {
173 let seed_fingerprint = hex::decode("d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1").unwrap();
174 let mut accounts = ZcashAccounts::new(seed_fingerprint.clone(), vec![]);
175 accounts.set_device_version("1.2.3".to_string());
176 let cbor: Vec<u8> = accounts.try_into().unwrap();
177 let encoded =
178 probe_encode(&cbor, 400, ZcashAccounts::get_registry_type().get_type()).unwrap();
179
180 assert!(!encoded.is_multi_part);
181 let decoded: URParseResult<ZcashAccounts> = probe_decode(encoded.data).unwrap();
182 let decoded_accounts = decoded.data.unwrap();
183
184 assert_eq!(decoded.ur_type.unwrap().get_type_str(), "zcash-accounts");
185 assert_eq!(decoded_accounts.get_seed_fingerprint(), seed_fingerprint);
186 assert!(decoded_accounts.get_accounts().is_empty());
187 assert_eq!(
188 decoded_accounts.get_device_version(),
189 Some("1.2.3".to_string())
190 );
191 }
192
193 #[test]
194 fn test_decode_zcash_unified_full_viewing_key_registry_ur() {
195 let expected_ufvk = "uview1qqqqqqqqqqqqqq8rzd0efkm6ej5n0twzum9czt9kj5y7jxjm9qz3uq9qgpqqqqqqqqqqqqqq9en0hkucteqncqcfqcqcpz4wuwl";
196 let ufvk = ZcashUnifiedFullViewingKey::new(
197 expected_ufvk.to_string(),
198 7,
199 Some("Keystone".to_string()),
200 );
201 let cbor: Vec<u8> = ufvk.try_into().unwrap();
202 let encoded = probe_encode(
203 &cbor,
204 400,
205 ZcashUnifiedFullViewingKey::get_registry_type().get_type(),
206 )
207 .unwrap();
208
209 assert!(!encoded.is_multi_part);
210 let decoded: URParseResult<ZcashUnifiedFullViewingKey> =
211 probe_decode(encoded.data).unwrap();
212 let decoded_ufvk = decoded.data.unwrap();
213
214 assert_eq!(
215 decoded.ur_type.unwrap().get_type_str(),
216 "zcash-unified-full-viewing-key"
217 );
218 assert_eq!(decoded_ufvk.get_ufvk(), expected_ufvk);
219 assert_eq!(decoded_ufvk.get_index(), 7);
220 assert_eq!(decoded_ufvk.get_name(), Some("Keystone".to_string()));
221 }
222
223 #[test]
224 fn test_decode_eth_sign_request() {
225 let ur = "ur:eth-sign-request/onadtpdagdwnbstbpfkidafxlbprqzdiktfldlaxheaohddlaoweaalalrhkisdlaelrhkisdlcwlfgmaymwvttkvsptykhkfwswosbdlrhhtiknftkihsnbfxdalnhtwfbeknfzaelartaxaaaaaaahtaaddyoeadlocsdwykcsfnykaeykaewkaocyjokbwejzvdrtpssp";
226 let result: URParseResult<EthSignRequest> = probe_decode(ur.to_string()).unwrap();
227 if !result.is_multi_part {
228 let crypto = result.data.unwrap();
229 assert_eq!("02ed04808459682f008459682f1b82520894e0cfe8a9f55942c6a70b845cd07a3a7d61a04325865af3107a400080c0",
230 hex::encode(crypto.get_sign_data()).to_lowercase());
231 }
232 }
233
234 #[test]
235 fn test_decode_sui_sign_request() {
236 let ur3 = "UR:SUI-SIGN-REQUEST/1268-2/LPCFAAWKAOCFADTTCYBENTKOSPHDWLATROUTDAGDSAIOSPYKDYRTDLBDSAGWCAENTPNNVWMOAYCEWETPIDYTTADWGHBZWFKBHFGROTHLOEAHRSDLFRADKNGHAAVLGOAHHDEMMWYLBZBKWLLKDKGESBHFAHNBLSWLOXAALUBWFLSRCTWELFVOKKLEURENZTCSTIPRNTRSOTGEZTHNGLDIWTMYDSQDFDMDWYPDIYTEMKMSFMFHETSALFCAKIGUCFCTGDFDLNSOSFZEGLSSCTJSGEPSZCSTEYNSYTFESGJPGTDETSFDWETOCESNUEWSYAQZYANELGZCOEAOAOADAEDPDYAEADAEADADAOAXLYTPAXDYWZGABNTTRTRPOLWKCYAMLBNBLNONMWESETIATOSEAXKNYNTELUDWLSESYTUOAENERYZEWTQDWKQZDMLUAETOGYGTIHGMHTFZBNWTSOPYMWZEPRHTQDKTSBCFGMURIDLTWTWPLPUEWFKN";
237 let ur1 = "UR:SUI-SIGN-REQUEST/418-2/LPCFADOEAOCFADTTCYBENTKOSPHDWLONADTPDAGDSAIOVSSSWFIAFGGLNEDKSKFTPYYAGWVTAOHKADJPAEAEAEAEAEAXADAEMNHHCEBKDIBDRLFRCPKNTDTTJOCWDISFRYWEYKADGMGSLEIDLTHKJSBTKSKOLTJNWDOERHAHAEAEAEAECXHPKELEURENZTETDPRKRKLYEEBZPYFTNDPRCFCAJYSEWNEEGMTNKGLONYGASODRIHAEAYUOAEAEAEAEAEAEAEAECXRYWNEOBZLFECYTKGMSOLGDSRFNHFSTASGYDACYFMSATKRFOYZSBBISCMNTLGZCOEAOAOADAEAEADADADAEADADAOAEAEADAOAEGDFDLNSOWPFXRSYLBKWFLBGOLNGDMWSFFTKKNSREFYKKWZGMTEBNTEJSLBBZWPUOAOASZSFZLUVYPDUYMWAOJEEEYTVDCKESZEOECYOSFLMYSNGWHEPYTOKEADPKBDVOLRWPGMBDRKJS";
238 let ur2 = "UR:SUI-SIGN-REQUEST/1072-2/LPCFAADYAOCFADTTCYBENTKOSPHDWLOERHAHAEAEAEAECXEHSROTINFEHLJETPBNJKIYPKJPBKFEWPPKIDYTTADWGHCMWZKBTPCHRSHGLPBAAYBBCFKGPDLPJYYAJPSOVWTNHSYNFLFGIAWYOTBWRDHPKITBAALRGLOLEYCMFLSRCTWEOERHAHAEAEAEAECXZCASDSFMMSHEHGHTTLMDWLMOGMJPRHOYRFJPCAHPAOUEYLBZHLSALESEKIGUCFCTGDFDLNSOWPFXRSYLBKWFLBGOLNGDMWSFFTKKNSREFYKKWZGMTEBNTEJSLBBZWPUOWYAOAEAEAEAEAEAEAEDPEHADAEAEAEAEAEAXLYTAADDYOEADLECSDWYKCFAXBEYKAEYKAEYKAEYKAOCYGMJYFLAXAALYHDCXGDFDLNSOWPFXRSYLBKWFLBGOLNGDMWSFFTKKNSREFYKKWZGMTEBNTEJSLBBZWPUOAHIHGUKPINIHJYAESTZSVEGA";
239 let result: URParseResult<SuiSignRequest> =
240 probe_decode(ur1.to_string().to_lowercase()).unwrap();
241 if result.is_multi_part {
242 let mut decoder = result.decoder.unwrap();
243 let _result: MultiURParseResult<SuiSignRequest> =
244 decoder.parse_ur(ur2.to_string().to_lowercase()).unwrap();
245 let result: MultiURParseResult<SuiSignRequest> =
246 decoder.parse_ur(ur3.to_string().to_lowercase()).unwrap();
247 let sui_sign_request = result.data.unwrap();
248 assert_eq!("00000000000301008e5c1c0a270bb73b227ad2d1701b27ccbdedf501524c8a628759710d7876876deaa2b90500000000205b7c8adf36fc382dbbbb813415ab3a9bb2191d74c1f13452da7b889a49c92a650008dc000000000000000020bdf133158235f97b97a650c33c56c70951251a3ec2cfbca1fa1468169d8dfda20202010000010101000101020000010200504886c9ec43bff70af37f55865094cc3a799cb54479f252d30cd3717f15ecdc0209fa408be1a8db94026b34f9e71e39fea21aa7478fcd4f5fabce7c01aa0be284eca2b905000000002031c3a369455d6bd80c7366aa720a45ecaa62f9d92c5416f27ed817bf57850e0814197ba88574f872c9e5da61f6474663eea313ba5b7dd604844ea6321647c31feda2b9050000000020fd09263e975f575ad595e9925272b9a1bc721d5b02def7155dc28ac17d53191f504886c9ec43bff70af37f55865094cc3a799cb54479f252d30cd3717f15ecdcee02000000000000002d31010000000000",
249 hex::encode(sui_sign_request.get_intent_message()).to_lowercase());
250 let components = vec![
251 PathComponent::new(Some(44), true).unwrap(),
252 PathComponent::new(Some(784), true).unwrap(),
253 PathComponent::new(Some(0), true).unwrap(),
254 PathComponent::new(Some(0), true).unwrap(),
255 PathComponent::new(Some(0), true).unwrap(),
256 ];
257 let source_fingerprint = hex::decode("52744703").unwrap().try_into().unwrap();
258 let crypto_key_path = vec![CryptoKeyPath::new(
259 components,
260 Some(source_fingerprint),
261 None,
262 )];
263 assert_eq!(crypto_key_path, sui_sign_request.get_derivation_paths());
264 assert_eq!(
266 "c267e8c4f363464e9f24c53aabf84fe0",
267 hex::encode(sui_sign_request.get_request_id().unwrap())
268 );
269 let addresses = sui_sign_request.get_addresses().unwrap();
271 assert_eq!(
272 vec!["504886c9ec43bff70af37f55865094cc3a799cb54479f252d30cd3717f15ecdc"],
273 addresses
274 .iter()
275 .map(|a| hex::encode(a))
276 .collect::<Vec<String>>()
277 );
278 assert_eq!("Suiet", sui_sign_request.get_origin().unwrap());
280 }
281 }
282
283 #[test]
284 fn test_decode_cardano_sign_request() {
285 let ur1 = "UR:CARDANO-SIGN-REQUEST/80-2/LPCSGDAOCFADVECYRKMNIDESHDWZLKVTNDSFASVDPERHHYMWOTTPTBSKGUDRWZSACLOLVTDYHSYNRYMHEHPRRKJTBZWKLBJLZMVLMWONVOFPFGKOHGCAFXAXUOVYRPWLPMREGMPAHDPESGDSDEINLNZTMKIEIHWFUTDWHHJKHLIHWLCLIYJKIYLGFHENBDVEPEEEIMPEDEAYPAGAHDZOAEFTUYNBCHTKKOPETKLPGWCEVACLSKURLDBTCLZTBWHKGAPEHNHSYTRHPFISGYJKJLNDINISENIHHNWPKKSFCKEEDRJOIYSEKGKODTJPCLJPCMEMVARDINPRIHECJEFSROLKLDGEBTRDESURLONEFPINWSFWDARNBACELEJNTKLPCSKBNSWECTHNJEWDCACHWKVTGHBTURTNGYMEEYQDCFWYAXRHLRGSFMTYIMDYENSBSRKGCXLFCFOSSOAXCYLNTOZCNLGDAXPSCXEHKKPSWPLSLACSJNFMDKFMSTKODRKSKSDNEMTB";
286 let ur2 = "UR:CARDANO-SIGN-REQUEST/66-2/LPCSFWAOCFADVECYRKMNIDESHDWZDTVYFXWLHKREOLQDFWRYNYMHMWJZZEMHYTAOAEAXJEEYESEEESENEHEOESENECEHAATAADDYOEADLECFATFNYKCFATCHYKAEYKAEWKAEWKAOCYBGGDRPRFAHKSJZHSIEIEJPHEJYIHJKJYEHJSJSECEEIYKSKNJKEYIEJPJKJOHSEYESIOECKPIAJOISEYETJPJYETJOECKOKTJEDYJKJEIHJKKPKNIMHSKSKOKSJYKTENKTJNJZHSDYKSKTJPISENIHIYEYHSKTISENDYJOIEIHKOKPEOJOETJZJYEOIOESJEDYIHJNKTJKIOKOKNJPKNJZJEJSEYIHESEYKKDYAALYTAAYNSOEADHDCEGLURZSWFETKTTBGMPLRDZSKSHPMTJPCLFHTSBBCMTKTOUTAADYFXCHWPAOTAADDYOEADLECFATFNYKCFATCHYKAEYKAOWKAEWKAOCYBGGDRPRFAHIYIHJYIHJPJTJZHPLTHTSF";
287 let ur3 = "UR:CARDANO-SIGN-REQUEST/95-2/LPCSHEAOCFADVECYRKMNIDESHDWZLKVTNDSFASVDPERHHYMWOTTPTBSKGUDRWZSACLOLVTDYHSYNRYMHEHPRRKJTBZWKLBJLZMVLMWONVOFPFGKOHGCAFXAXUOVYRPWLPMREGMPAHDPESGDSDEINLNZTMKIEIHWFUTDWHHJKHLIHWLCLIYJKIYLGFHENBDVEPEEEIMPEDEAYPAGAHDZOAEFTUYNBCHTKKOPETKLPGWCEVACLSKURLDBTCLZTBWHKGAPEHNHSYTRHPFISGYJKJLNDINISENIHHNWPKKSFCKEEDRJOIYSEKGKODTJPCLJPCMEMVARDINPRIHECJEFSROLKLDGEBTRDESURLONEFPINWSFWDARNBACELEJNTKLPCSKBNSWECTHNJEWDCACHWKVTGHBTURTNGYMEEYQDCFWYAXRHLRGSFMTYIMDYENSBSRKGCXLFCFOSSOAXCYLNTOZCNLGDAXPSCXEHKKPSWPLSLACSJNFMDKFMSTKODRKSIHSNWEMH";
288
289 let result: URParseResult<CardanoSignRequest> =
290 probe_decode(ur1.to_string().to_lowercase()).unwrap();
291 if result.is_multi_part {
292 let mut decoder = result.decoder.unwrap();
293 let _result: MultiURParseResult<CardanoSignRequest> =
294 decoder.parse_ur(ur2.to_string().to_lowercase()).unwrap();
295 let result: MultiURParseResult<CardanoSignRequest> =
296 decoder.parse_ur(ur3.to_string().to_lowercase()).unwrap();
297 let ada_sign_request = result.data.unwrap();
298 assert_eq!("84a60081825820c57bb6fed336a46858414aa204441429e143e959b5a6b342bd9a90946cfe90f90001818258390029549850534700f545453980dd471ace1a31d67c2d987052e99865bb4edffaf33877d652aebafa785b9672213fd71416cfcedd04304317ec1b00000006de18bb76021a0002a40d031a02191e62048183028200581c4edffaf33877d652aebafa785b9672213fd71416cfcedd04304317ec581c3c4fb94e1a2c5649a870aee5a70f21cd64807c7dc38632efcaf3d9210800a0f5f6",
299 hex::encode(ada_sign_request.get_sign_data()).to_lowercase());
300 let components = vec![
301 PathComponent::new(Some(1852), true).unwrap(),
302 PathComponent::new(Some(1815), true).unwrap(),
303 PathComponent::new(Some(0), true).unwrap(),
304 PathComponent::new(Some(0), false).unwrap(),
305 PathComponent::new(Some(0), false).unwrap(),
306 ];
307 let source_fingerprint = hex::decode("1250b6bc").unwrap().try_into().unwrap();
308 let crypto_key_path = vec![CryptoKeyPath::new(
309 components,
310 Some(source_fingerprint),
311 None,
312 )];
313 assert_eq!(
314 crypto_key_path,
315 ada_sign_request
316 .get_utxos()
317 .iter()
318 .map(|u| u.get_key_path())
319 .collect::<Vec<CryptoKeyPath>>()
320 );
321
322 assert_eq!(1, ada_sign_request.get_utxos().len());
323 let components = vec![
324 PathComponent::new(Some(1852), true).unwrap(),
325 PathComponent::new(Some(1815), true).unwrap(),
326 PathComponent::new(Some(0), true).unwrap(),
327 PathComponent::new(Some(2), false).unwrap(),
328 PathComponent::new(Some(0), false).unwrap(),
329 ];
330 let source_fingerprint = hex::decode("1250b6bc").unwrap().try_into().unwrap();
331 let crypto_key_path = vec![CryptoKeyPath::new(
332 components,
333 Some(source_fingerprint),
334 None,
335 )];
336 assert_eq!(
337 crypto_key_path,
338 ada_sign_request
339 .get_cert_keys()
340 .iter()
341 .map(|k| k.get_key_path())
342 .collect::<Vec<CryptoKeyPath>>()
343 );
344 assert_eq!(
346 "52090a1c29394842a9adba0bc021a58b",
347 hex::encode(ada_sign_request.get_request_id().unwrap())
348 );
349 assert_eq!("eternl", ada_sign_request.get_origin().unwrap());
351 }
352 }
353
354 #[test]
355 fn test_decode_cardano_sign_tx_hash_request() {
356 let ur = "UR:CARDANO-SIGN-TX-HASH-REQUEST/OXADTPDAGDGMASBKCEDTESFDFWPTPMRDBDRTCLONLUAOKSFZECEYHSEHIYECECESENIYEOEHEOECETDYEODYIYDYIEESIEEOHSEYIEIDEYIDEHEHESIDETIYEMENENEOETENDYEMEHENETEEIEEYENIEDYIEEOEMEEEOESIAEHEEEEIHAXLFTAADDYOEADLECFATFNYKCFATCHYKAEYKAEWKAEWKAOCYBGGDRPRFTAADDYOEADLECFATFNYKCFATCHYKAEYKAOWKAEWKAOCYBGGDRPRFAAIYIHJYIHJPJTJZAMAXZMAM";
357 let result: URParseResult<CardanoSignTxHashRequest> =
358 probe_decode(ur.to_string().to_lowercase()).unwrap();
359 if !result.is_multi_part {
360 let crypto = result.data.unwrap();
361 assert_eq!("eternl", crypto.get_origin().unwrap());
363 assert_eq!(
365 "52090a1c29394842a9adba0bc021a58b",
366 hex::encode(crypto.get_request_id().unwrap())
367 );
368 let expected_tx_hash =
369 "52a1f5596f31358030f0d9d3a2db2b119b8f766386071684d26d0d37439c144e";
370 assert_eq!(expected_tx_hash, crypto.get_tx_hash());
371 let components1 = vec![
373 PathComponent::new(Some(1852), true).unwrap(),
374 PathComponent::new(Some(1815), true).unwrap(),
375 PathComponent::new(Some(0), true).unwrap(),
376 PathComponent::new(Some(2), false).unwrap(),
377 PathComponent::new(Some(0), false).unwrap(),
378 ];
379 let source_fingerprint1 = hex::decode("1250b6bc").unwrap().try_into().unwrap();
380
381 let components2 = vec![
382 PathComponent::new(Some(1852), true).unwrap(),
383 PathComponent::new(Some(1815), true).unwrap(),
384 PathComponent::new(Some(0), true).unwrap(),
385 PathComponent::new(Some(0), false).unwrap(),
386 PathComponent::new(Some(0), false).unwrap(),
387 ];
388 let source_fingerprint2 = hex::decode("1250b6bc").unwrap().try_into().unwrap();
389
390 let crypto_key_path1 = CryptoKeyPath::new(components1, Some(source_fingerprint1), None);
391 let crypto_key_path2 = CryptoKeyPath::new(components2, Some(source_fingerprint2), None);
392
393 let expected_paths = vec![crypto_key_path2, crypto_key_path1];
394 assert_eq!(expected_paths, crypto.get_paths());
395 }
396 }
397}