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::{MultiURParseResult, URParseResult, probe_decode};
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_batch_sig_result::ZcashBatchSigResult;
112    use ur_registry::zcash::zcash_sign_batch::ZcashSignBatch;
113    use ur_registry::zcash::zcash_sign_result::{
114        ZCASH_SIGN_RESULT_KIND_PCZT_V1, ZCASH_SIGN_RESULT_VERSION, ZCASH_SIGN_STATUS_SIGNED,
115        ZcashSignMessageResult, ZcashSignResult,
116    };
117
118    #[test]
119    fn test_encode_decode_zcash_sign_batch_ur() {
120        // Serialization of an empty PCZT BatchSignRequest:
121        // "PCZB" || batch_version_le || pczt_version_le || Postcard body.
122        let request = vec![b'P', b'C', b'Z', b'B', 1, 0, 0, 0, 2, 0, 0, 0, 0];
123        let request_id = vec![0xaa, 0xbb];
124        let batch = ZcashSignBatch::new(request_id.clone(), request.clone());
125        let cbor: Vec<u8> = batch.try_into().unwrap();
126        let encoded =
127            probe_encode(&cbor, 400, ZcashSignBatch::get_registry_type().get_type()).unwrap();
128
129        assert!(!encoded.is_multi_part);
130
131        let decoded: URParseResult<ZcashSignBatch> = probe_decode(encoded.data).unwrap();
132        let decoded_batch = decoded.data.unwrap();
133
134        assert_eq!(decoded.ur_type.unwrap().get_type_str(), "zcash-sign-batch");
135        assert_eq!(decoded_batch.get_data(), request);
136        assert_eq!(decoded_batch.get_request_id(), request_id);
137    }
138
139    #[test]
140    fn test_registry_ur_encoder_type_is_compatible() {
141        let crypto = CryptoPSBT::new(vec![0xaa, 0xbb, 0xcc]);
142        let mut encoder = super::KeystoneUREncoder::new(crypto.to_ur_encoder(400));
143
144        assert_eq!(encoder.fragment_count(), 1);
145
146        let part = encoder.next_part().unwrap();
147        assert!(part.starts_with("ur:crypto-psbt/"));
148    }
149
150    #[test]
151    fn test_encode_decode_zcash_sign_result_ur() {
152        let payload_digest =
153            Vec::from_hex("f2dbc955d1edad3014bc907efc15e93adb4412cdee847d261cd942998693e590")
154                .unwrap();
155        let result = ZcashSignResult::new(
156            ZCASH_SIGN_RESULT_VERSION,
157            vec![0xaa, 0xbb],
158            vec![ZcashSignMessageResult::signed(
159                vec![0x01],
160                ZCASH_SIGN_RESULT_KIND_PCZT_V1,
161                b"signed-pczt-result".to_vec(),
162                payload_digest.clone(),
163            )],
164        );
165        let cbor: Vec<u8> = result.try_into().unwrap();
166        let encoded =
167            probe_encode(&cbor, 400, ZcashSignResult::get_registry_type().get_type()).unwrap();
168        let literal_ur = "ur:zcash-sign-result/otadadaofwpkrkaxlyonadfpadaoaeaxadaagmjkiniojtihiedpjoiaknjydpjpihjkkpjzjyamhdcxwzuysogottwepmdybbrfmhkbztbzwlftuyfybgsnwylrkidscetafwnllnmuvwmhuorkvwgr";
169
170        assert!(!encoded.is_multi_part);
171        assert_eq!(encoded.data, literal_ur);
172
173        let decoded: URParseResult<ZcashSignResult> = probe_decode(literal_ur.to_string()).unwrap();
174        let decoded_result = decoded.data.unwrap();
175
176        assert_eq!(decoded.ur_type.unwrap().get_type_str(), "zcash-sign-result");
177        assert_eq!(decoded_result.get_version(), ZCASH_SIGN_RESULT_VERSION);
178        assert_eq!(decoded_result.get_request_id(), &vec![0xaa, 0xbb]);
179        assert_eq!(decoded_result.get_results().len(), 1);
180        assert_eq!(decoded_result.get_results()[0].get_id(), &vec![0x01]);
181        assert_eq!(
182            decoded_result.get_results()[0].get_status(),
183            ZCASH_SIGN_STATUS_SIGNED
184        );
185        assert_eq!(
186            decoded_result.get_results()[0].get_kind(),
187            ZCASH_SIGN_RESULT_KIND_PCZT_V1
188        );
189        assert_eq!(
190            decoded_result.get_results()[0].get_payload(),
191            &b"signed-pczt-result".to_vec()
192        );
193        assert_eq!(
194            decoded_result.get_results()[0].get_payload_digest(),
195            &payload_digest
196        );
197    }
198
199    #[test]
200    fn test_encode_decode_zcash_batch_sig_result_ur() {
201        // Serialization of an empty PCZT BatchSignResponse:
202        // "PCZS" || batch_version_le || Postcard body.
203        let response = vec![b'P', b'C', b'Z', b'S', 1, 0, 0, 0, 0];
204        let request_id = vec![0xaa, 0xbb];
205        let firmware_version = [1, 2, 3];
206        let result = ZcashBatchSigResult::new(
207            request_id.clone(),
208            response.clone(),
209            firmware_version,
210        );
211        let cbor: Vec<u8> = result.try_into().unwrap();
212        let encoded = probe_encode(
213            &cbor,
214            400,
215            ZcashBatchSigResult::get_registry_type().get_type(),
216        )
217        .unwrap();
218
219        assert!(!encoded.is_multi_part);
220
221        let decoded: URParseResult<ZcashBatchSigResult> = probe_decode(encoded.data).unwrap();
222        let decoded_result = decoded.data.unwrap();
223
224        assert_eq!(
225            decoded.ur_type.unwrap().get_type_str(),
226            "zcash-batch-sig-result"
227        );
228        assert_eq!(decoded_result.get_data(), response);
229        assert_eq!(decoded_result.get_request_id(), request_id);
230        assert_eq!(decoded_result.get_firmware_version(), &firmware_version);
231    }
232
233    #[test]
234    fn test_encode_decode_multipart_zcash_sign_batch_ur() {
235        let payload = vec![0x42; 1024];
236        let request_id = vec![0xaa, 0xbb];
237        let batch = ZcashSignBatch::new(request_id.clone(), payload.clone());
238        let cbor: Vec<u8> = batch.try_into().unwrap();
239        let encoded =
240            probe_encode(&cbor, 100, ZcashSignBatch::get_registry_type().get_type()).unwrap();
241
242        assert!(encoded.is_multi_part);
243        let first: URParseResult<ZcashSignBatch> = probe_decode(encoded.data).unwrap();
244        assert!(first.is_multi_part);
245        assert!(first.data.is_none());
246
247        let mut decoder = first.decoder.unwrap();
248        let mut encoder = encoded.encoder.unwrap();
249        let fragment_count = encoder.fragment_count();
250        let mut decoded_batch = None;
251        for _ in 1..fragment_count {
252            let part = encoder.next_part().unwrap();
253            let parsed: MultiURParseResult<ZcashSignBatch> = decoder.parse_ur(part).unwrap();
254            if parsed.is_complete {
255                decoded_batch = parsed.data;
256                break;
257            }
258        }
259        let decoded_batch = decoded_batch.unwrap();
260
261        assert_eq!(decoded_batch.get_data(), payload);
262        assert_eq!(decoded_batch.get_request_id(), request_id);
263    }
264
265    #[test]
266    fn test_encode_ada_hardware_call() {
267        let data = "a3010002d90515a10182d90516a101d90130a10186182cf500f500f5d90516a201d90130a1018a182cf51901f5f500f500f500f502010400";
268        let data = Vec::from_hex(data).unwrap();
269        // hardware call
270        let res = probe_encode(&data, 400, QRHardwareCall::get_registry_type().get_type()).unwrap();
271        assert_eq!(
272            "ur:qr-hardware-call/otadaeaotaahbzoyadlftaahcmoyadtaaddyoyadlncsdwykaeykaeyktaahcmoeadtaaddyoyadlecsdwykcfadykykaeykaeykaeykaoadaaaeyteyldre",
273            res.data
274        )
275    }
276    //
277    #[test]
278    fn test_encode_sol_hardware_call() {
279        let data = "a4010002d90515a10184d90516a301d90130a10186182cf5183cf500f502000463455448d90516a301d90130a10186182cf51901f5f500f502010463534f4cd90516a301d90130a10186182cf51901f5f501f502000463534f4cd90516a301d90130a1018a182cf51901f5f500f500f400f402010463534f4c036b4c6561702057616c6c65740401";
280        let data = Vec::from_hex(data).unwrap();
281        // hardware call
282        let res = probe_encode(&data, 400, QRHardwareCall::get_registry_type().get_type()).unwrap();
283        assert_eq!(
284                "ur:qr-hardware-call/oxadaeaotaahbzoyadlrtaahcmotadtaaddyoyadlncsdwykcsfnykaeykaoaeaaiafeghfdtaahcmotadtaaddyoyadlncsdwykcfadykykaeykaoadaaiagugwgstaahcmotadtaaddyoyadlncsdwykcfadykykadykaoaeaaiagugwgstaahcmotadtaaddyoyadlecsdwykcfadykykaeykaewkaewkaoadaaiagugwgsaxjegsihhsjocxhghsjzjzihjyaaadfnfxcmfy",
285                res.data
286            )
287    }
288    #[test]
289    fn test_encode_cosmos_hardware_call() {
290        let data = "a3010002d90515a10182d90516a101d90130a10186182cf500f500f5d90516a201d90130a1018a182cf51901f5f500f500f500f502010400";
291        let data = Vec::from_hex(data).unwrap();
292        // hardware call
293        let res = probe_encode(&data, 400, QRHardwareCall::get_registry_type().get_type()).unwrap();
294        assert_eq!(
295            "ur:qr-hardware-call/otadaeaotaahbzoyadlftaahcmoyadtaaddyoyadlncsdwykaeykaeyktaahcmoeadtaaddyoyadlecsdwykcfadykykaeykaeykaeykaoadaaaeyteyldre",
296            res.data
297        )
298    }
299
300    #[test]
301    fn test_encode() {
302        let crypto = CryptoPSBT::new(
303            Vec::from_hex("8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa")
304                .unwrap());
305        let result: Vec<u8> = crypto.try_into().unwrap();
306        let result =
307            probe_encode(&result, 400, CryptoPSBT::get_registry_type().get_type()).unwrap();
308        assert_eq!("ur:crypto-psbt/1-3/lpadaxcfaxiacyvwhdfhndhkadclhkaxhnlkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbnychpmiy",
309                   result.data);
310        if result.is_multi_part {
311            let mut encoder = result.encoder.unwrap();
312            let next = encoder.next_part().unwrap();
313            assert_eq!("ur:crypto-psbt/2-3/lpaoaxcfaxiacyvwhdfhndhkadclaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaylbntahvo",
314                       next);
315            let next = encoder.next_part().unwrap();
316            assert_eq!("ur:crypto-psbt/3-3/lpaxaxcfaxiacyvwhdfhndhkadclpklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypknseoskve",
317                       next
318            );
319            let next = encoder.next_part().unwrap();
320            assert_eq!("ur:crypto-psbt/4-3/lpaaaxcfaxiacyvwhdfhndhkadclaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbayneieyksn",
321                       next);
322        }
323    }
324
325    #[test]
326    fn test_cyclic_encode() {
327        let crypto = CryptoPSBT::new(
328            Vec::from_hex("8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa8c05c4b4f3e88840a4f4b5f155cfd69473ea169f3d0431b7a6787a23777f08aa")
329                .unwrap());
330        let result: Vec<u8> = crypto.try_into().unwrap();
331        let result =
332            cyclic_encode(&result, 400, CryptoPSBT::get_registry_type().get_type()).unwrap();
333        assert_eq!("ur:crypto-psbt/1-3/lpadaxcfaxiacyvwhdfhndhkadclhkaxhnlkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbnychpmiy",
334                    result.data);
335        if result.is_multi_part {
336            let mut encoder = result.encoder.unwrap();
337            let next = encoder.next_cyclic_part().unwrap();
338            assert_eq!("ur:crypto-psbt/2-3/lpaoaxcfaxiacyvwhdfhndhkadclaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaylbntahvo",
339                        next);
340            let next = encoder.next_cyclic_part().unwrap();
341            assert_eq!("ur:crypto-psbt/3-3/lpaxaxcfaxiacyvwhdfhndhkadclpklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypknseoskve",
342                        next
343            );
344            let next = encoder.next_cyclic_part().unwrap();
345            assert_eq!("ur:crypto-psbt/1-3/lpadaxcfaxiacyvwhdfhndhkadclhkaxhnlkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbaypklkahssqzwfvslofzoxwkrewngotktbmwjkwdcmnefsaaehrlolkskncnktlbnychpmiy",
346                        next);
347        }
348    }
349}