1use std::io::{Error, Write};
2use std::str::FromStr;
3
4use anyhow::format_err;
5use bitcoin::address::NetworkUnchecked;
6use bitcoin::hashes::Hash as BitcoinHash;
7use hex::{FromHex, ToHex};
8use lightning::util::ser::{BigSize, Readable, Writeable};
9use miniscript::{Descriptor, MiniscriptKey};
10use serde::{Deserialize, Serialize};
11
12use crate::encoding::{Decodable, DecodeError, Encodable};
13use crate::get_network_for_address;
14use crate::module::registry::ModuleDecoderRegistry;
15
16macro_rules! impl_encode_decode_bridge {
17 ($btc_type:ty) => {
18 impl crate::encoding::Encodable for $btc_type {
19 fn consensus_encode<W: std::io::Write>(
20 &self,
21 writer: &mut W,
22 ) -> Result<(), std::io::Error> {
23 bitcoin::consensus::Encodable::consensus_encode(
24 self,
25 &mut std::io::BufWriter::new(writer),
26 )?;
27 Ok(())
28 }
29 }
30
31 impl crate::encoding::Decodable for $btc_type {
32 fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
33 d: &mut D,
34 _modules: &$crate::module::registry::ModuleDecoderRegistry,
35 ) -> Result<Self, crate::encoding::DecodeError> {
36 bitcoin::consensus::Decodable::consensus_decode_from_finite_reader(
37 &mut SimpleBitcoinRead(d),
38 )
39 .map_err(crate::encoding::DecodeError::from_err)
40 }
41 }
42 };
43}
44
45impl_encode_decode_bridge!(bitcoin::block::Header);
46impl_encode_decode_bridge!(bitcoin::BlockHash);
47impl_encode_decode_bridge!(bitcoin::OutPoint);
48impl_encode_decode_bridge!(bitcoin::TxOut);
49impl_encode_decode_bridge!(bitcoin::ScriptBuf);
50impl_encode_decode_bridge!(bitcoin::Transaction);
51impl_encode_decode_bridge!(bitcoin::merkle_tree::PartialMerkleTree);
52
53impl crate::encoding::Encodable for bitcoin::psbt::Psbt {
54 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
55 self.serialize_to_writer(&mut BitoinIoWriteAdapter::from(writer))?;
56 Ok(())
57 }
58}
59
60impl crate::encoding::Decodable for bitcoin::psbt::Psbt {
61 fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
62 d: &mut D,
63 _modules: &ModuleDecoderRegistry,
64 ) -> Result<Self, crate::encoding::DecodeError> {
65 Self::deserialize_from_reader(&mut BufBitcoinReader::new(d))
66 .map_err(crate::encoding::DecodeError::from_err)
67 }
68}
69
70impl crate::encoding::Encodable for bitcoin::Txid {
71 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
72 bitcoin::consensus::Encodable::consensus_encode(
73 self,
74 &mut std::io::BufWriter::new(writer),
75 )?;
76 Ok(())
77 }
78
79 fn consensus_encode_to_hex(&self) -> String {
80 let mut bytes = self.consensus_encode_to_vec();
81
82 bytes.reverse();
84
85 bytes.encode_hex()
87 }
88}
89
90impl crate::encoding::Decodable for bitcoin::Txid {
91 fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
92 d: &mut D,
93 _modules: &::fedimint_core::module::registry::ModuleDecoderRegistry,
94 ) -> Result<Self, crate::encoding::DecodeError> {
95 bitcoin::consensus::Decodable::consensus_decode_from_finite_reader(&mut SimpleBitcoinRead(
96 d,
97 ))
98 .map_err(crate::encoding::DecodeError::from_err)
99 }
100
101 fn consensus_decode_hex(
102 hex: &str,
103 modules: &ModuleDecoderRegistry,
104 ) -> Result<Self, DecodeError> {
105 let mut bytes = Vec::<u8>::from_hex(hex)
106 .map_err(anyhow::Error::from)
107 .map_err(DecodeError::new_custom)?;
108
109 bytes.reverse();
111
112 Decodable::consensus_decode_whole(&bytes, modules)
113 }
114}
115
116impl<K> Encodable for Descriptor<K>
117where
118 K: MiniscriptKey,
119{
120 fn consensus_encode<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
121 let descriptor_str = self.to_string();
122 descriptor_str.consensus_encode(writer)
123 }
124}
125
126impl<K> Decodable for Descriptor<K>
127where
128 Self: FromStr,
129 <Self as FromStr>::Err: ToString + std::error::Error + Send + Sync + 'static,
130 K: MiniscriptKey,
131{
132 fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
133 d: &mut D,
134 modules: &ModuleDecoderRegistry,
135 ) -> Result<Self, DecodeError> {
136 let descriptor_str = String::consensus_decode_partial_from_finite_reader(d, modules)?;
137 Self::from_str(&descriptor_str).map_err(DecodeError::from_err)
138 }
139}
140
141#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
145pub struct NetworkLegacyEncodingWrapper(pub bitcoin::Network);
146
147impl std::fmt::Display for NetworkLegacyEncodingWrapper {
148 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
149 write!(f, "{}", self.0)
150 }
151}
152
153impl Encodable for NetworkLegacyEncodingWrapper {
154 fn consensus_encode<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
155 u32::from_le_bytes(self.0.magic().to_bytes()).consensus_encode(writer)
156 }
157}
158
159impl Decodable for NetworkLegacyEncodingWrapper {
160 fn consensus_decode_partial<D: std::io::Read>(
161 d: &mut D,
162 modules: &ModuleDecoderRegistry,
163 ) -> Result<Self, DecodeError> {
164 let num = u32::consensus_decode_partial(d, modules)?;
165 let magic = bitcoin::p2p::Magic::from_bytes(num.to_le_bytes());
166 let network = bitcoin::Network::from_magic(magic).ok_or_else(|| {
167 DecodeError::new_custom(format_err!("Unknown network magic: {:x}", magic))
168 })?;
169 Ok(Self(network))
170 }
171}
172impl Encodable for bitcoin::Network {
173 fn consensus_encode<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
174 self.magic().to_bytes().consensus_encode(writer)
175 }
176}
177
178impl Decodable for bitcoin::Network {
179 fn consensus_decode_partial<D: std::io::Read>(
180 d: &mut D,
181 modules: &ModuleDecoderRegistry,
182 ) -> Result<Self, DecodeError> {
183 Self::from_magic(bitcoin::p2p::Magic::from_bytes(
184 Decodable::consensus_decode_partial(d, modules)?,
185 ))
186 .ok_or_else(|| DecodeError::new_custom(format_err!("Unknown network magic")))
187 }
188}
189
190impl Encodable for bitcoin::Amount {
191 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
192 self.to_sat().consensus_encode(writer)
193 }
194}
195
196impl Decodable for bitcoin::Amount {
197 fn consensus_decode_partial<D: std::io::Read>(
198 d: &mut D,
199 modules: &ModuleDecoderRegistry,
200 ) -> Result<Self, DecodeError> {
201 Ok(Self::from_sat(u64::consensus_decode_partial(d, modules)?))
202 }
203}
204
205impl Encodable for bitcoin::Address<NetworkUnchecked> {
206 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
207 NetworkLegacyEncodingWrapper(get_network_for_address(self)).consensus_encode(writer)?;
208 self.clone()
209 .assume_checked()
213 .script_pubkey()
214 .consensus_encode(writer)?;
215 Ok(())
216 }
217}
218
219impl Decodable for bitcoin::Address<NetworkUnchecked> {
220 fn consensus_decode_partial<D: std::io::Read>(
221 mut d: &mut D,
222 modules: &ModuleDecoderRegistry,
223 ) -> Result<Self, DecodeError> {
224 let network = NetworkLegacyEncodingWrapper::consensus_decode_partial(&mut d, modules)?.0;
225 let script_pk = bitcoin::ScriptBuf::consensus_decode_partial(&mut d, modules)?;
226
227 let address = bitcoin::Address::from_script(&script_pk, network)
228 .map_err(|e| DecodeError::new_custom(e.into()))?;
229
230 Ok(address.into_unchecked())
231 }
232}
233
234impl Encodable for bitcoin::hashes::sha256::Hash {
235 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
236 self.to_byte_array().consensus_encode(writer)
237 }
238}
239
240impl Decodable for bitcoin::hashes::sha256::Hash {
241 fn consensus_decode_partial<D: std::io::Read>(
242 d: &mut D,
243 modules: &ModuleDecoderRegistry,
244 ) -> Result<Self, DecodeError> {
245 Ok(Self::from_byte_array(Decodable::consensus_decode_partial(
246 d, modules,
247 )?))
248 }
249}
250
251impl Encodable for lightning_invoice::Bolt11Invoice {
252 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
253 self.to_string().consensus_encode(writer)
254 }
255}
256
257impl Decodable for lightning_invoice::Bolt11Invoice {
258 fn consensus_decode_partial<D: std::io::Read>(
259 d: &mut D,
260 modules: &ModuleDecoderRegistry,
261 ) -> Result<Self, DecodeError> {
262 String::consensus_decode_partial(d, modules)?
263 .parse::<Self>()
264 .map_err(DecodeError::from_err)
265 }
266}
267
268impl Encodable for lightning_invoice::RoutingFees {
269 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
270 self.base_msat.consensus_encode(writer)?;
271 self.proportional_millionths.consensus_encode(writer)?;
272 Ok(())
273 }
274}
275
276impl Decodable for lightning_invoice::RoutingFees {
277 fn consensus_decode_partial<D: std::io::Read>(
278 d: &mut D,
279 modules: &ModuleDecoderRegistry,
280 ) -> Result<Self, DecodeError> {
281 let base_msat = Decodable::consensus_decode_partial(d, modules)?;
282 let proportional_millionths = Decodable::consensus_decode_partial(d, modules)?;
283 Ok(Self {
284 base_msat,
285 proportional_millionths,
286 })
287 }
288}
289
290impl Encodable for BigSize {
291 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
292 let mut writer = BitoinIoWriteAdapter::from(writer);
293 self.write(&mut writer)?;
294 Ok(())
295 }
296}
297
298impl Decodable for BigSize {
299 fn consensus_decode_partial<R: std::io::Read>(
300 r: &mut R,
301 _modules: &ModuleDecoderRegistry,
302 ) -> Result<Self, DecodeError> {
303 Self::read(&mut SimpleBitcoinRead(r))
304 .map_err(|e| DecodeError::new_custom(anyhow::anyhow!("BigSize decoding error: {e:?}")))
305 }
306}
307
308struct SimpleBitcoinRead<R: std::io::Read>(R);
312
313impl<R: std::io::Read> bitcoin_io::Read for SimpleBitcoinRead<R> {
314 fn read(&mut self, buf: &mut [u8]) -> bitcoin_io::Result<usize> {
315 self.0.read(buf).map_err(bitcoin_io::Error::from)
316 }
317}
318
319struct BufBitcoinReader<'a, R: std::io::Read> {
330 inner: &'a mut R,
331 buf: [u8; 1],
332 is_consumed: bool,
333}
334
335impl<'a, R: std::io::Read> BufBitcoinReader<'a, R> {
336 fn new(inner: &'a mut R) -> Self {
338 BufBitcoinReader {
339 inner,
340 buf: [0; 1],
341 is_consumed: true,
342 }
343 }
344}
345
346impl<'a, R: std::io::Read> bitcoin_io::Read for BufBitcoinReader<'a, R> {
347 #[inline]
348 fn read(&mut self, output: &mut [u8]) -> bitcoin_io::Result<usize> {
349 if output.is_empty() {
350 return Ok(0);
351 }
352 #[allow(clippy::useless_let_if_seq)]
353 let mut offset = 0;
354 if !self.is_consumed {
355 output[0] = self.buf[0];
356 self.is_consumed = true;
357 offset = 1;
358 }
359 Ok(self
360 .inner
361 .read(&mut output[offset..])
362 .map(|len| len + offset)?)
363 }
364}
365
366impl<'a, R: std::io::Read> bitcoin_io::BufRead for BufBitcoinReader<'a, R> {
367 #[inline]
368 fn fill_buf(&mut self) -> bitcoin_io::Result<&[u8]> {
369 debug_assert!(false, "rust-bitcoin doesn't actually use this");
370 if self.is_consumed {
371 let count = self.inner.read(&mut self.buf[..])?;
372 debug_assert!(count <= 1, "read gave us a garbage length");
373
374 self.is_consumed = count == 0;
376 }
377
378 if self.is_consumed {
379 Ok(&[])
380 } else {
381 Ok(&self.buf[..])
382 }
383 }
384
385 #[inline]
386 fn consume(&mut self, amount: usize) {
387 debug_assert!(false, "rust-bitcoin doesn't actually use this");
388 if amount >= 1 {
389 debug_assert_eq!(amount, 1, "Can only consume one byte");
390 debug_assert!(!self.is_consumed, "Cannot consume more than had been read");
391 self.is_consumed = true;
392 }
393 }
394}
395
396pub struct BitoinIoWriteAdapter<W> {
402 inner: W,
403}
404
405impl<W> From<W> for BitoinIoWriteAdapter<W> {
406 fn from(inner: W) -> Self {
407 Self { inner }
408 }
409}
410
411impl<W: Write> bitcoin_io::Write for BitoinIoWriteAdapter<W> {
412 fn write(&mut self, buf: &[u8]) -> bitcoin_io::Result<usize> {
413 let written = self.inner.write(buf)?;
414 Ok(written)
415 }
416
417 fn flush(&mut self) -> bitcoin_io::Result<()> {
418 self.inner.flush().map_err(bitcoin_io::Error::from)
419 }
420}
421
422#[cfg(test)]
423mod tests {
424 use std::str::FromStr;
425
426 use bitcoin::hashes::Hash as BitcoinHash;
427 use hex::FromHex;
428
429 use crate::ModuleDecoderRegistry;
430 use crate::db::DatabaseValue;
431 use crate::encoding::btc::NetworkLegacyEncodingWrapper;
432 use crate::encoding::tests::{test_roundtrip, test_roundtrip_expected};
433 use crate::encoding::{Decodable, Encodable};
434
435 #[test_log::test]
436 fn block_hash_roundtrip() {
437 let blockhash = bitcoin::BlockHash::from_str(
438 "0000000000000000000065bda8f8a88f2e1e00d9a6887a43d640e52a4c7660f2",
439 )
440 .unwrap();
441 test_roundtrip_expected(
442 &blockhash,
443 &[
444 242, 96, 118, 76, 42, 229, 64, 214, 67, 122, 136, 166, 217, 0, 30, 46, 143, 168,
445 248, 168, 189, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
446 ],
447 );
448 }
449
450 #[test_log::test]
451 fn tx_roundtrip() {
452 let transaction: Vec<u8> = FromHex::from_hex(
453 "02000000000101d35b66c54cf6c09b81a8d94cd5d179719cd7595c258449452a9305ab9b12df250200000000fdffffff020cd50a0000000000160014ae5d450b71c04218e6e81c86fcc225882d7b7caae695b22100000000160014f60834ef165253c571b11ce9fa74e46692fc5ec10248304502210092062c609f4c8dc74cd7d4596ecedc1093140d90b3fd94b4bdd9ad3e102ce3bc02206bb5a6afc68d583d77d5d9bcfb6252a364d11a307f3418be1af9f47f7b1b3d780121026e5628506ecd33242e5ceb5fdafe4d3066b5c0f159b3c05a621ef65f177ea28600000000"
454 ).unwrap();
455 let transaction =
456 bitcoin::Transaction::from_bytes(&transaction, &ModuleDecoderRegistry::default())
457 .unwrap();
458 test_roundtrip_expected(
459 &transaction,
460 &[
461 2, 0, 0, 0, 0, 1, 1, 211, 91, 102, 197, 76, 246, 192, 155, 129, 168, 217, 76, 213,
462 209, 121, 113, 156, 215, 89, 92, 37, 132, 73, 69, 42, 147, 5, 171, 155, 18, 223,
463 37, 2, 0, 0, 0, 0, 253, 255, 255, 255, 2, 12, 213, 10, 0, 0, 0, 0, 0, 22, 0, 20,
464 174, 93, 69, 11, 113, 192, 66, 24, 230, 232, 28, 134, 252, 194, 37, 136, 45, 123,
465 124, 170, 230, 149, 178, 33, 0, 0, 0, 0, 22, 0, 20, 246, 8, 52, 239, 22, 82, 83,
466 197, 113, 177, 28, 233, 250, 116, 228, 102, 146, 252, 94, 193, 2, 72, 48, 69, 2,
467 33, 0, 146, 6, 44, 96, 159, 76, 141, 199, 76, 215, 212, 89, 110, 206, 220, 16, 147,
468 20, 13, 144, 179, 253, 148, 180, 189, 217, 173, 62, 16, 44, 227, 188, 2, 32, 107,
469 181, 166, 175, 198, 141, 88, 61, 119, 213, 217, 188, 251, 98, 82, 163, 100, 209,
470 26, 48, 127, 52, 24, 190, 26, 249, 244, 127, 123, 27, 61, 120, 1, 33, 2, 110, 86,
471 40, 80, 110, 205, 51, 36, 46, 92, 235, 95, 218, 254, 77, 48, 102, 181, 192, 241,
472 89, 179, 192, 90, 98, 30, 246, 95, 23, 126, 162, 134, 0, 0, 0, 0,
473 ],
474 );
475 }
476
477 #[test_log::test]
478 fn txid_roundtrip() {
479 let txid = bitcoin::Txid::from_str(
480 "51f7ed2f23e58cc6e139e715e9ce304a1e858416edc9079dd7b74fa8d2efc09a",
481 )
482 .unwrap();
483 test_roundtrip_expected(
484 &txid,
485 &[
486 154, 192, 239, 210, 168, 79, 183, 215, 157, 7, 201, 237, 22, 132, 133, 30, 74, 48,
487 206, 233, 21, 231, 57, 225, 198, 140, 229, 35, 47, 237, 247, 81,
488 ],
489 );
490 }
491
492 #[test_log::test]
493 fn network_roundtrip() {
494 let networks: [(bitcoin::Network, [u8; 5], [u8; 4]); 5] = [
495 (
496 bitcoin::Network::Bitcoin,
497 [0xFE, 0xD9, 0xB4, 0xBE, 0xF9],
498 [0xF9, 0xBE, 0xB4, 0xD9],
499 ),
500 (
501 bitcoin::Network::Testnet,
502 [0xFE, 0x07, 0x09, 0x11, 0x0B],
503 [0x0B, 0x11, 0x09, 0x07],
504 ),
505 (
506 bitcoin::Network::Testnet4,
507 [0xFE, 0x28, 0x3F, 0x16, 0x1C],
508 [0x1C, 0x16, 0x3F, 0x28],
509 ),
510 (
511 bitcoin::Network::Signet,
512 [0xFE, 0x40, 0xCF, 0x03, 0x0A],
513 [0x0A, 0x03, 0xCF, 0x40],
514 ),
515 (
516 bitcoin::Network::Regtest,
517 [0xFE, 0xDA, 0xB5, 0xBF, 0xFA],
518 [0xFA, 0xBF, 0xB5, 0xDA],
519 ),
520 ];
521
522 for (network, magic_legacy_bytes, magic_bytes) in networks {
523 let network_legacy_encoded =
524 NetworkLegacyEncodingWrapper(network).consensus_encode_to_vec();
525
526 let network_encoded = network.consensus_encode_to_vec();
527
528 let network_legacy_decoded = NetworkLegacyEncodingWrapper::consensus_decode_whole(
529 &network_legacy_encoded,
530 &ModuleDecoderRegistry::default(),
531 )
532 .unwrap()
533 .0;
534
535 let network_decoded = bitcoin::Network::consensus_decode_whole(
536 &network_encoded,
537 &ModuleDecoderRegistry::default(),
538 )
539 .unwrap();
540
541 assert_eq!(magic_legacy_bytes, *network_legacy_encoded);
542 assert_eq!(magic_bytes, *network_encoded);
543 assert_eq!(network, network_legacy_decoded);
544 assert_eq!(network, network_decoded);
545 }
546 }
547
548 #[test_log::test]
549 fn address_roundtrip() {
550 let addresses = [
551 "bc1p2wsldez5mud2yam29q22wgfh9439spgduvct83k3pm50fcxa5dps59h4z5",
552 "mxMYaq5yWinZ9AKjCDcBEbiEwPJD9n2uLU",
553 "1FK8o7mUxyd6QWJAUw7J4vW7eRxuyjj6Ne",
554 "3JSrSU7z7R1Yhh26pt1zzRjQz44qjcrXwb",
555 "tb1qunn0thpt8uk3yk2938ypjccn3urxprt78z9ccq",
556 "2MvUMRv2DRHZi3VshkP7RMEU84mVTfR9xjq",
557 ];
558
559 for address_str in addresses {
560 let address =
561 bitcoin::Address::from_str(address_str).expect("All tested addresses are valid");
562 let encoding = address.consensus_encode_to_vec();
563 let parsed_address = bitcoin::Address::consensus_decode_whole(
564 &encoding,
565 &ModuleDecoderRegistry::default(),
566 )
567 .expect("Decoding address failed");
568
569 assert_eq!(address, parsed_address);
570 }
571 }
572
573 #[test_log::test]
574 fn sha256_roundtrip() {
575 test_roundtrip_expected(
576 &bitcoin::hashes::sha256::Hash::hash(b"Hello world!"),
577 &[
578 192, 83, 94, 75, 226, 183, 159, 253, 147, 41, 19, 5, 67, 107, 248, 137, 49, 78, 74,
579 63, 174, 192, 94, 207, 252, 187, 125, 243, 26, 217, 229, 26,
580 ],
581 );
582 }
583
584 #[test_log::test]
585 fn bolt11_invoice_roundtrip() {
586 let invoice_str = "lnbc100p1psj9jhxdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4q0d3p2sfluzdx45tqcs\
587 h2pu5qc7lgq0xs578ngs6s0s68ua4h7cvspp5q6rmq35js88zp5dvwrv9m459tnk2zunwj5jalqtyxqulh0l\
588 5gflssp5nf55ny5gcrfl30xuhzj3nphgj27rstekmr9fw3ny5989s300gyus9qyysgqcqpcrzjqw2sxwe993\
589 h5pcm4dxzpvttgza8zhkqxpgffcrf5v25nwpr3cmfg7z54kuqq8rgqqqqqqqq2qqqqq9qq9qrzjqd0ylaqcl\
590 j9424x9m8h2vcukcgnm6s56xfgu3j78zyqzhgs4hlpzvznlugqq9vsqqqqqqqlgqqqqqeqq9qrzjqwldmj9d\
591 ha74df76zhx6l9we0vjdquygcdt3kssupehe64g6yyp5yz5rhuqqwccqqyqqqqlgqqqqjcqq9qrzjqf9e58a\
592 guqr0rcun0ajlvmzq3ek63cw2w282gv3z5uupmuwvgjtq2z55qsqqg6qqqyqqqrtnqqqzq3cqygrzjqvphms\
593 ywntrrhqjcraumvc4y6r8v4z5v593trte429v4hredj7ms5z52usqq9ngqqqqqqqlgqqqqqqgq9qrzjq2v0v\
594 p62g49p7569ev48cmulecsxe59lvaw3wlxm7r982zxa9zzj7z5l0cqqxusqqyqqqqlgqqqqqzsqygarl9fh3\
595 8s0gyuxjjgux34w75dnc6xp2l35j7es3jd4ugt3lu0xzre26yg5m7ke54n2d5sym4xcmxtl8238xxvw5h5h5\
596 j5r6drg6k6zcqj0fcwg";
597 let invoice = invoice_str
598 .parse::<lightning_invoice::Bolt11Invoice>()
599 .unwrap();
600 test_roundtrip(&invoice);
601 }
602}