Skip to main content

ur_parse_lib/
keystone_ur_encoder.rs

1use alloc::string::{String, ToString};
2use core::fmt;
3use ur_registry::error::{URError, URResult};
4
5pub fn cyclic_encode(
6    message: &[u8],
7    max_fragment_length: usize,
8    ur_type: String,
9) -> URResult<UREncodeResult> {
10    let mut encoder = ur::Encoder::new(message, max_fragment_length, ur_type.clone())
11        .map_err(|e| URError::CborEncodeError(e.to_string()))?;
12    if encoder.fragment_count() > 1 {
13        Ok(UREncodeResult {
14            is_multi_part: true,
15            data: encoder
16                .next_part()
17                .map_err(|e| URError::UrEncodeError(e.to_string()))?,
18            encoder: Some(KeystoneUREncoder::new(encoder)),
19        })
20    } else {
21        let ur = ur::encode(message, ur_type);
22        Ok(UREncodeResult {
23            is_multi_part: false,
24            data: ur,
25            encoder: None,
26        })
27    }
28}
29
30pub fn probe_encode(
31    message: &[u8],
32    max_fragment_length: usize,
33    ur_type: String,
34) -> URResult<UREncodeResult> {
35    let mut encoder = ur::Encoder::new(message, max_fragment_length, ur_type.clone())
36        .map_err(|e| URError::CborEncodeError(e.to_string()))?;
37    if encoder.fragment_count() > 1 {
38        Ok(UREncodeResult {
39            is_multi_part: true,
40            data: encoder
41                .next_part()
42                .map_err(|e| URError::UrEncodeError(e.to_string()))?,
43            encoder: Some(KeystoneUREncoder::new(encoder)),
44        })
45    } else {
46        let ur = ur::encode(message, ur_type);
47        Ok(UREncodeResult {
48            is_multi_part: false,
49            data: ur,
50            encoder: None,
51        })
52    }
53}
54
55pub struct UREncodeResult {
56    pub is_multi_part: bool,
57    pub data: String,
58    pub encoder: Option<KeystoneUREncoder>,
59}
60
61pub struct KeystoneUREncoder {
62    encoder: ur::Encoder,
63}
64
65impl fmt::Debug for UREncodeResult {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        f.debug_tuple("")
68            .field(&self.is_multi_part)
69            .field(&self.data)
70            .finish()
71    }
72}
73
74impl KeystoneUREncoder {
75    pub fn new(encoder: ur::Encoder) -> Self {
76        KeystoneUREncoder { encoder }
77    }
78
79    pub fn next_cyclic_part(&mut self) -> URResult<String> {
80        self.encoder
81            .next_cyclic_part()
82            .map_err(|e| URError::CborEncodeError(e.to_string()))
83    }
84
85    pub fn next_part(&mut self) -> URResult<String> {
86        self.encoder
87            .next_part()
88            .map_err(|e| URError::CborEncodeError(e.to_string()))
89    }
90
91    pub fn current_index(&self) -> usize {
92        self.encoder.current_index()
93    }
94
95    pub fn fragment_count(&self) -> usize {
96        self.encoder.fragment_count()
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use crate::keystone_ur_decoder::{probe_decode, MultiURParseResult, URParseResult};
103    use crate::keystone_ur_encoder::{cyclic_encode, probe_encode};
104    use alloc::string::ToString;
105    use alloc::vec;
106    use alloc::vec::Vec;
107    use hex::FromHex;
108    use ur_registry::crypto_psbt::CryptoPSBT;
109    use ur_registry::extend::qr_hardware_call::QRHardwareCall;
110    use ur_registry::traits::{RegistryItem, UR};
111    use ur_registry::zcash::zcash_sign_batch::{
112        ZcashSignBatch, ZcashSignMessage, ZCASH_SIGN_BATCH_NETWORK_MAINNET,
113        ZCASH_SIGN_BATCH_VERSION, ZCASH_SIGN_MESSAGE_KIND_PCZT_V1,
114    };
115    use ur_registry::zcash::zcash_sign_result::{
116        ZcashSignMessageResult, ZcashSignResult, ZCASH_SIGN_RESULT_KIND_PCZT_V1,
117        ZCASH_SIGN_RESULT_VERSION, ZCASH_SIGN_STATUS_SIGNED,
118    };
119
120    #[test]
121    fn test_encode_decode_zcash_sign_batch_ur() {
122        let payload_digest =
123            Vec::from_hex("7a66e6be087afee0665161828bd11dc1e201fdb8c2d72786ee485e29897c8da4")
124                .unwrap();
125        let batch = ZcashSignBatch::new(
126            ZCASH_SIGN_BATCH_VERSION,
127            vec![0xaa, 0xbb],
128            ZCASH_SIGN_BATCH_NETWORK_MAINNET,
129            vec![ZcashSignMessage::new(
130                vec![0x01],
131                ZCASH_SIGN_MESSAGE_KIND_PCZT_V1,
132                b"pczt-request".to_vec(),
133                Some(payload_digest.clone()),
134            )],
135            Some(false),
136        );
137        let cbor: Vec<u8> = batch.try_into().unwrap();
138        let encoded =
139            probe_encode(&cbor, 400, ZcashSignBatch::get_registry_type().get_type()).unwrap();
140        let literal_ur = "ur:zcash-sign-batch/onadadaofwpkrkaxadaalyoxadfpadaoadaxgsjoiaknjydpjpihjskpihjkjyamhdcxkniyvarnayknzevtiygyhslfluttcasevoadzcrosatsdilnwyfdhydtldkelgoxbdwkeccarlis";
141
142        assert!(!encoded.is_multi_part);
143        assert_eq!(encoded.data, literal_ur);
144
145        let decoded: URParseResult<ZcashSignBatch> = probe_decode(literal_ur.to_string()).unwrap();
146        let decoded_batch = decoded.data.unwrap();
147
148        assert_eq!(decoded.ur_type.unwrap().get_type_str(), "zcash-sign-batch");
149        assert_eq!(decoded_batch.get_version(), ZCASH_SIGN_BATCH_VERSION);
150        assert_eq!(decoded_batch.get_request_id(), &vec![0xaa, 0xbb]);
151        assert_eq!(
152            decoded_batch.get_network(),
153            ZCASH_SIGN_BATCH_NETWORK_MAINNET
154        );
155        assert!(!decoded_batch.get_atomic());
156        assert_eq!(decoded_batch.get_messages().len(), 1);
157        assert_eq!(decoded_batch.get_messages()[0].get_id(), &vec![0x01]);
158        assert_eq!(
159            decoded_batch.get_messages()[0].get_kind(),
160            ZCASH_SIGN_MESSAGE_KIND_PCZT_V1
161        );
162        assert_eq!(
163            decoded_batch.get_messages()[0].get_payload(),
164            &b"pczt-request".to_vec()
165        );
166        assert_eq!(
167            decoded_batch.get_messages()[0].get_payload_digest(),
168            Some(&payload_digest)
169        );
170    }
171
172    #[test]
173    fn test_registry_ur_encoder_type_is_compatible() {
174        let crypto = CryptoPSBT::new(vec![0xaa, 0xbb, 0xcc]);
175        let mut encoder = super::KeystoneUREncoder::new(crypto.to_ur_encoder(400));
176
177        assert_eq!(encoder.fragment_count(), 1);
178
179        let part = encoder.next_part().unwrap();
180        assert!(part.starts_with("ur:crypto-psbt/"));
181    }
182
183    #[test]
184    fn test_encode_decode_zcash_sign_result_ur() {
185        let payload_digest =
186            Vec::from_hex("f2dbc955d1edad3014bc907efc15e93adb4412cdee847d261cd942998693e590")
187                .unwrap();
188        let result = ZcashSignResult::new(
189            ZCASH_SIGN_RESULT_VERSION,
190            vec![0xaa, 0xbb],
191            vec![ZcashSignMessageResult::signed(
192                vec![0x01],
193                ZCASH_SIGN_RESULT_KIND_PCZT_V1,
194                b"signed-pczt-result".to_vec(),
195                payload_digest.clone(),
196            )],
197        );
198        let cbor: Vec<u8> = result.try_into().unwrap();
199        let encoded =
200            probe_encode(&cbor, 400, ZcashSignResult::get_registry_type().get_type()).unwrap();
201        let literal_ur = "ur:zcash-sign-result/otadadaofwpkrkaxlyonadfpadaoaeaxadaagmjkiniojtihiedpjoiaknjydpjpihjkkpjzjyamhdcxwzuysogottwepmdybbrfmhkbztbzwlftuyfybgsnwylrkidscetafwnllnmuvwmhuorkvwgr";
202
203        assert!(!encoded.is_multi_part);
204        assert_eq!(encoded.data, literal_ur);
205
206        let decoded: URParseResult<ZcashSignResult> = probe_decode(literal_ur.to_string()).unwrap();
207        let decoded_result = decoded.data.unwrap();
208
209        assert_eq!(decoded.ur_type.unwrap().get_type_str(), "zcash-sign-result");
210        assert_eq!(decoded_result.get_version(), ZCASH_SIGN_RESULT_VERSION);
211        assert_eq!(decoded_result.get_request_id(), &vec![0xaa, 0xbb]);
212        assert_eq!(decoded_result.get_results().len(), 1);
213        assert_eq!(decoded_result.get_results()[0].get_id(), &vec![0x01]);
214        assert_eq!(
215            decoded_result.get_results()[0].get_status(),
216            ZCASH_SIGN_STATUS_SIGNED
217        );
218        assert_eq!(
219            decoded_result.get_results()[0].get_kind(),
220            ZCASH_SIGN_RESULT_KIND_PCZT_V1
221        );
222        assert_eq!(
223            decoded_result.get_results()[0].get_payload(),
224            &b"signed-pczt-result".to_vec()
225        );
226        assert_eq!(
227            decoded_result.get_results()[0].get_payload_digest(),
228            &payload_digest
229        );
230    }
231
232    #[test]
233    fn test_encode_decode_multipart_zcash_sign_batch_ur() {
234        let payload = vec![0x42; 1024];
235        let payload_digest =
236            Vec::from_hex("7a66e6be087afee0665161828bd11dc1e201fdb8c2d72786ee485e29897c8da4")
237                .unwrap();
238        let batch = ZcashSignBatch::new(
239            ZCASH_SIGN_BATCH_VERSION,
240            vec![0xaa, 0xbb],
241            ZCASH_SIGN_BATCH_NETWORK_MAINNET,
242            vec![ZcashSignMessage::new(
243                vec![0x01],
244                ZCASH_SIGN_MESSAGE_KIND_PCZT_V1,
245                payload.clone(),
246                Some(payload_digest.clone()),
247            )],
248            Some(true),
249        );
250        let cbor: Vec<u8> = batch.try_into().unwrap();
251        let encoded =
252            probe_encode(&cbor, 100, ZcashSignBatch::get_registry_type().get_type()).unwrap();
253
254        assert!(encoded.is_multi_part);
255        let first: URParseResult<ZcashSignBatch> = probe_decode(encoded.data).unwrap();
256        assert!(first.is_multi_part);
257        assert!(first.data.is_none());
258
259        let mut decoder = first.decoder.unwrap();
260        let mut encoder = encoded.encoder.unwrap();
261        let fragment_count = encoder.fragment_count();
262        let mut decoded_batch = None;
263        for _ in 1..fragment_count {
264            let part = encoder.next_part().unwrap();
265            let parsed: MultiURParseResult<ZcashSignBatch> = decoder.parse_ur(part).unwrap();
266            if parsed.is_complete {
267                decoded_batch = parsed.data;
268                break;
269            }
270        }
271        let decoded_batch = decoded_batch.unwrap();
272
273        assert_eq!(decoded_batch.get_version(), ZCASH_SIGN_BATCH_VERSION);
274        assert_eq!(decoded_batch.get_request_id(), &vec![0xaa, 0xbb]);
275        assert_eq!(
276            decoded_batch.get_network(),
277            ZCASH_SIGN_BATCH_NETWORK_MAINNET
278        );
279        assert!(decoded_batch.get_atomic());
280        assert_eq!(decoded_batch.get_messages().len(), 1);
281        assert_eq!(
282            decoded_batch.get_messages()[0].get_kind(),
283            ZCASH_SIGN_MESSAGE_KIND_PCZT_V1
284        );
285        assert_eq!(decoded_batch.get_messages()[0].get_payload(), &payload);
286        assert_eq!(
287            decoded_batch.get_messages()[0].get_payload_digest(),
288            Some(&payload_digest)
289        );
290    }
291
292    #[test]
293    fn test_encode_ada_hardware_call() {
294        let data = "a3010002d90515a10182d90516a101d90130a10186182cf500f500f5d90516a201d90130a1018a182cf51901f5f500f500f500f502010400";
295        let data = Vec::from_hex(data).unwrap();
296        // hardware call
297        let res = probe_encode(&data, 400, QRHardwareCall::get_registry_type().get_type()).unwrap();
298        assert_eq!(
299            "ur:qr-hardware-call/otadaeaotaahbzoyadlftaahcmoyadtaaddyoyadlncsdwykaeykaeyktaahcmoeadtaaddyoyadlecsdwykcfadykykaeykaeykaeykaoadaaaeyteyldre",
300            res.data
301        )
302    }
303    //
304    #[test]
305    fn test_encode_sol_hardware_call() {
306        let data = "a4010002d90515a10184d90516a301d90130a10186182cf5183cf500f502000463455448d90516a301d90130a10186182cf51901f5f500f502010463534f4cd90516a301d90130a10186182cf51901f5f501f502000463534f4cd90516a301d90130a1018a182cf51901f5f500f500f400f402010463534f4c036b4c6561702057616c6c65740401";
307        let data = Vec::from_hex(data).unwrap();
308        // hardware call
309        let res = probe_encode(&data, 400, QRHardwareCall::get_registry_type().get_type()).unwrap();
310        assert_eq!(
311                "ur:qr-hardware-call/oxadaeaotaahbzoyadlrtaahcmotadtaaddyoyadlncsdwykcsfnykaeykaoaeaaiafeghfdtaahcmotadtaaddyoyadlncsdwykcfadykykaeykaoadaaiagugwgstaahcmotadtaaddyoyadlncsdwykcfadykykadykaoaeaaiagugwgstaahcmotadtaaddyoyadlecsdwykcfadykykaeykaewkaewkaoadaaiagugwgsaxjegsihhsjocxhghsjzjzihjyaaadfnfxcmfy",
312                res.data
313            )
314    }
315    #[test]
316    fn test_encode_cosmos_hardware_call() {
317        let data = "a3010002d90515a10182d90516a101d90130a10186182cf500f500f5d90516a201d90130a1018a182cf51901f5f500f500f500f502010400";
318        let data = Vec::from_hex(data).unwrap();
319        // hardware call
320        let res = probe_encode(&data, 400, QRHardwareCall::get_registry_type().get_type()).unwrap();
321        assert_eq!(
322            "ur:qr-hardware-call/otadaeaotaahbzoyadlftaahcmoyadtaaddyoyadlncsdwykaeykaeyktaahcmoeadtaaddyoyadlecsdwykcfadykykaeykaeykaeykaoadaaaeyteyldre",
323            res.data
324        )
325    }
326
327    #[test]
328    fn test_encode() {
329        let crypto = CryptoPSBT::new(
330            Vec::from_hex("8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa")
331                .unwrap());
332        let result: Vec<u8> = crypto.try_into().unwrap();
333        let result =
334            probe_encode(&result, 400, CryptoPSBT::get_registry_type().get_type()).unwrap();
335        assert_eq!("ur:crypto-psbt/1-3/lpadaxcfaxiacyvwhdfhndhkadclhkaxhnlkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbnychpmiy",
336                   result.data);
337        if result.is_multi_part {
338            let mut encoder = result.encoder.unwrap();
339            let next = encoder.next_part().unwrap();
340            assert_eq!("ur:crypto-psbt/2-3/lpaoaxcfaxiacyvwhdfhndhkadclaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaylbntahvo",
341                       next);
342            let next = encoder.next_part().unwrap();
343            assert_eq!("ur:crypto-psbt/3-3/lpaxaxcfaxiacyvwhdfhndhkadclpklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypknseoskve",
344                       next
345            );
346            let next = encoder.next_part().unwrap();
347            assert_eq!("ur:crypto-psbt/4-3/lpaaaxcfaxiacyvwhdfhndhkadclaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbayneieyksn",
348                       next);
349        }
350    }
351
352    #[test]
353    fn test_cyclic_encode() {
354        let crypto = CryptoPSBT::new(
355            Vec::from_hex("8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa")
356                .unwrap());
357        let result: Vec<u8> = crypto.try_into().unwrap();
358        let result =
359            cyclic_encode(&result, 400, CryptoPSBT::get_registry_type().get_type()).unwrap();
360        assert_eq!("ur:crypto-psbt/1-3/lpadaxcfaxiacyvwhdfhndhkadclhkaxhnlkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbnychpmiy",
361                    result.data);
362        if result.is_multi_part {
363            let mut encoder = result.encoder.unwrap();
364            let next = encoder.next_cyclic_part().unwrap();
365            assert_eq!("ur:crypto-psbt/2-3/lpaoaxcfaxiacyvwhdfhndhkadclaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaylbntahvo",
366                        next);
367            let next = encoder.next_cyclic_part().unwrap();
368            assert_eq!("ur:crypto-psbt/3-3/lpaxaxcfaxiacyvwhdfhndhkadclpklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypknseoskve",
369                        next
370            );
371            let next = encoder.next_cyclic_part().unwrap();
372            assert_eq!("ur:crypto-psbt/1-3/lpadaxcfaxiacyvwhdfhndhkadclhkaxhnlkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbnychpmiy",
373                        next);
374        }
375    }
376}