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 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 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 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 #[test]
278 fn test_encode_sol_hardware_call() {
279 let data = "a4010002d90515a10184d90516a301d90130a10186182cf5183cf500f502000463455448d90516a301d90130a10186182cf51901f5f500f502010463534f4cd90516a301d90130a10186182cf51901f5f501f502000463534f4cd90516a301d90130a1018a182cf51901f5f500f500f400f402010463534f4c036b4c6561702057616c6c65740401";
280 let data = Vec::from_hex(data).unwrap();
281 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 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}