fedimint_core/encoding/
btc.rs

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        // Just Bitcoin things: transaction hashes are encoded reverse
83        bytes.reverse();
84
85        // TODO: remove double-allocation
86        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        // Just Bitcoin things: transaction hashes are encoded reverse
110        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/// Wrapper around `bitcoin::Network` that encodes and decodes the network as a
142/// little-endian u32. This is here for backwards compatibility and is used by
143/// the LNv1 and WalletV1 modules.
144#[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            // We need an `Address<NetworkChecked>` in order to get the script pubkey.
210            // Calling `assume_checked` is generally a bad idea, but it's safe here where we're
211            // encoding the address because addresses are always decoded as unchecked.
212            .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
308// Simple decoder implementing `bitcoin_io::Read` for `std::io::Read`.
309// This is needed because `bitcoin::consensus::Decodable` requires a
310// `bitcoin_io::Read`.
311struct 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
319/// Wrap buffering support for implementations of Read.
320/// A reader which keeps an internal buffer to avoid hitting the underlying
321/// stream directly for every read.
322///
323/// In order to avoid reading bytes past the first object, and those bytes then
324/// ending up getting dropped, this BufBitcoinReader operates in
325/// one-byte-increments.
326///
327/// This code is vendored from the `lightning` crate:
328/// <https://github.com/lightningdevkit/rust-lightning/blob/5718baaed947fcaa9c60d80cdf309040c0c68489/lightning/src/util/ser.rs#L72-L138>
329struct 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    /// Creates a [`BufBitcoinReader`] which will read from the given `inner`.
337    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            // upon hitting EOF, assume the byte is already consumed
375            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
396/// A writer counting number of bytes written to it
397///
398/// Copy&pasted from <https://github.com/SOF3/count-write> which
399/// uses Apache license (and it's a trivial amount of code, repeating
400/// on stack overflow).
401pub 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}