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 bitcoin::hashes::hash160::Hash {
252    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
253        self.to_byte_array().consensus_encode(writer)
254    }
255}
256
257impl Decodable for bitcoin::hashes::hash160::Hash {
258    fn consensus_decode_partial<D: std::io::Read>(
259        d: &mut D,
260        modules: &ModuleDecoderRegistry,
261    ) -> Result<Self, DecodeError> {
262        Ok(Self::from_byte_array(Decodable::consensus_decode_partial(
263            d, modules,
264        )?))
265    }
266}
267
268impl Encodable for lightning_invoice::Bolt11Invoice {
269    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
270        self.to_string().consensus_encode(writer)
271    }
272}
273
274impl Decodable for lightning_invoice::Bolt11Invoice {
275    fn consensus_decode_partial<D: std::io::Read>(
276        d: &mut D,
277        modules: &ModuleDecoderRegistry,
278    ) -> Result<Self, DecodeError> {
279        String::consensus_decode_partial(d, modules)?
280            .parse::<Self>()
281            .map_err(DecodeError::from_err)
282    }
283}
284
285impl Encodable for lightning_invoice::RoutingFees {
286    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
287        self.base_msat.consensus_encode(writer)?;
288        self.proportional_millionths.consensus_encode(writer)?;
289        Ok(())
290    }
291}
292
293impl Decodable for lightning_invoice::RoutingFees {
294    fn consensus_decode_partial<D: std::io::Read>(
295        d: &mut D,
296        modules: &ModuleDecoderRegistry,
297    ) -> Result<Self, DecodeError> {
298        let base_msat = Decodable::consensus_decode_partial(d, modules)?;
299        let proportional_millionths = Decodable::consensus_decode_partial(d, modules)?;
300        Ok(Self {
301            base_msat,
302            proportional_millionths,
303        })
304    }
305}
306
307impl Encodable for BigSize {
308    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
309        let mut writer = BitoinIoWriteAdapter::from(writer);
310        self.write(&mut writer)?;
311        Ok(())
312    }
313}
314
315impl Decodable for BigSize {
316    fn consensus_decode_partial<R: std::io::Read>(
317        r: &mut R,
318        _modules: &ModuleDecoderRegistry,
319    ) -> Result<Self, DecodeError> {
320        Self::read(&mut SimpleBitcoinRead(r))
321            .map_err(|e| DecodeError::new_custom(anyhow::anyhow!("BigSize decoding error: {e:?}")))
322    }
323}
324
325// Simple decoder implementing `bitcoin_io::Read` for `std::io::Read`.
326// This is needed because `bitcoin::consensus::Decodable` requires a
327// `bitcoin_io::Read`.
328struct SimpleBitcoinRead<R: std::io::Read>(R);
329
330impl<R: std::io::Read> bitcoin_io::Read for SimpleBitcoinRead<R> {
331    fn read(&mut self, buf: &mut [u8]) -> bitcoin_io::Result<usize> {
332        self.0.read(buf).map_err(bitcoin_io::Error::from)
333    }
334}
335
336/// Wrap buffering support for implementations of Read.
337/// A reader which keeps an internal buffer to avoid hitting the underlying
338/// stream directly for every read.
339///
340/// In order to avoid reading bytes past the first object, and those bytes then
341/// ending up getting dropped, this BufBitcoinReader operates in
342/// one-byte-increments.
343///
344/// This code is vendored from the `lightning` crate:
345/// <https://github.com/lightningdevkit/rust-lightning/blob/5718baaed947fcaa9c60d80cdf309040c0c68489/lightning/src/util/ser.rs#L72-L138>
346struct BufBitcoinReader<'a, R: std::io::Read> {
347    inner: &'a mut R,
348    buf: [u8; 1],
349    is_consumed: bool,
350}
351
352impl<'a, R: std::io::Read> BufBitcoinReader<'a, R> {
353    /// Creates a [`BufBitcoinReader`] which will read from the given `inner`.
354    fn new(inner: &'a mut R) -> Self {
355        BufBitcoinReader {
356            inner,
357            buf: [0; 1],
358            is_consumed: true,
359        }
360    }
361}
362
363impl<R: std::io::Read> bitcoin_io::Read for BufBitcoinReader<'_, R> {
364    #[inline]
365    fn read(&mut self, output: &mut [u8]) -> bitcoin_io::Result<usize> {
366        if output.is_empty() {
367            return Ok(0);
368        }
369        #[allow(clippy::useless_let_if_seq)]
370        let mut offset = 0;
371        if !self.is_consumed {
372            output[0] = self.buf[0];
373            self.is_consumed = true;
374            offset = 1;
375        }
376        Ok(self
377            .inner
378            .read(&mut output[offset..])
379            .map(|len| len + offset)?)
380    }
381}
382
383impl<R: std::io::Read> bitcoin_io::BufRead for BufBitcoinReader<'_, R> {
384    #[inline]
385    fn fill_buf(&mut self) -> bitcoin_io::Result<&[u8]> {
386        debug_assert!(false, "rust-bitcoin doesn't actually use this");
387        if self.is_consumed {
388            let count = self.inner.read(&mut self.buf[..])?;
389            debug_assert!(count <= 1, "read gave us a garbage length");
390
391            // upon hitting EOF, assume the byte is already consumed
392            self.is_consumed = count == 0;
393        }
394
395        if self.is_consumed {
396            Ok(&[])
397        } else {
398            Ok(&self.buf[..])
399        }
400    }
401
402    #[inline]
403    fn consume(&mut self, amount: usize) {
404        debug_assert!(false, "rust-bitcoin doesn't actually use this");
405        if amount >= 1 {
406            debug_assert_eq!(amount, 1, "Can only consume one byte");
407            debug_assert!(!self.is_consumed, "Cannot consume more than had been read");
408            self.is_consumed = true;
409        }
410    }
411}
412
413/// A writer counting number of bytes written to it
414///
415/// Copy&pasted from <https://github.com/SOF3/count-write> which
416/// uses Apache license (and it's a trivial amount of code, repeating
417/// on stack overflow).
418pub struct BitoinIoWriteAdapter<W> {
419    inner: W,
420}
421
422impl<W> From<W> for BitoinIoWriteAdapter<W> {
423    fn from(inner: W) -> Self {
424        Self { inner }
425    }
426}
427
428impl<W: Write> bitcoin_io::Write for BitoinIoWriteAdapter<W> {
429    fn write(&mut self, buf: &[u8]) -> bitcoin_io::Result<usize> {
430        let written = self.inner.write(buf)?;
431        Ok(written)
432    }
433
434    fn flush(&mut self) -> bitcoin_io::Result<()> {
435        self.inner.flush().map_err(bitcoin_io::Error::from)
436    }
437}
438
439#[cfg(test)]
440mod tests {
441    use std::str::FromStr;
442
443    use bitcoin::hashes::Hash as BitcoinHash;
444    use hex::FromHex;
445
446    use crate::ModuleDecoderRegistry;
447    use crate::db::DatabaseValue;
448    use crate::encoding::btc::NetworkLegacyEncodingWrapper;
449    use crate::encoding::tests::{test_roundtrip, test_roundtrip_expected};
450    use crate::encoding::{Decodable, Encodable};
451
452    #[test_log::test]
453    fn block_hash_roundtrip() {
454        let blockhash = bitcoin::BlockHash::from_str(
455            "0000000000000000000065bda8f8a88f2e1e00d9a6887a43d640e52a4c7660f2",
456        )
457        .unwrap();
458        test_roundtrip_expected(
459            &blockhash,
460            &[
461                242, 96, 118, 76, 42, 229, 64, 214, 67, 122, 136, 166, 217, 0, 30, 46, 143, 168,
462                248, 168, 189, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
463            ],
464        );
465    }
466
467    #[test_log::test]
468    fn tx_roundtrip() {
469        let transaction: Vec<u8> = FromHex::from_hex(
470            "02000000000101d35b66c54cf6c09b81a8d94cd5d179719cd7595c258449452a9305ab9b12df250200000000fdffffff020cd50a0000000000160014ae5d450b71c04218e6e81c86fcc225882d7b7caae695b22100000000160014f60834ef165253c571b11ce9fa74e46692fc5ec10248304502210092062c609f4c8dc74cd7d4596ecedc1093140d90b3fd94b4bdd9ad3e102ce3bc02206bb5a6afc68d583d77d5d9bcfb6252a364d11a307f3418be1af9f47f7b1b3d780121026e5628506ecd33242e5ceb5fdafe4d3066b5c0f159b3c05a621ef65f177ea28600000000"
471        ).unwrap();
472        let transaction =
473            bitcoin::Transaction::from_bytes(&transaction, &ModuleDecoderRegistry::default())
474                .unwrap();
475        test_roundtrip_expected(
476            &transaction,
477            &[
478                2, 0, 0, 0, 0, 1, 1, 211, 91, 102, 197, 76, 246, 192, 155, 129, 168, 217, 76, 213,
479                209, 121, 113, 156, 215, 89, 92, 37, 132, 73, 69, 42, 147, 5, 171, 155, 18, 223,
480                37, 2, 0, 0, 0, 0, 253, 255, 255, 255, 2, 12, 213, 10, 0, 0, 0, 0, 0, 22, 0, 20,
481                174, 93, 69, 11, 113, 192, 66, 24, 230, 232, 28, 134, 252, 194, 37, 136, 45, 123,
482                124, 170, 230, 149, 178, 33, 0, 0, 0, 0, 22, 0, 20, 246, 8, 52, 239, 22, 82, 83,
483                197, 113, 177, 28, 233, 250, 116, 228, 102, 146, 252, 94, 193, 2, 72, 48, 69, 2,
484                33, 0, 146, 6, 44, 96, 159, 76, 141, 199, 76, 215, 212, 89, 110, 206, 220, 16, 147,
485                20, 13, 144, 179, 253, 148, 180, 189, 217, 173, 62, 16, 44, 227, 188, 2, 32, 107,
486                181, 166, 175, 198, 141, 88, 61, 119, 213, 217, 188, 251, 98, 82, 163, 100, 209,
487                26, 48, 127, 52, 24, 190, 26, 249, 244, 127, 123, 27, 61, 120, 1, 33, 2, 110, 86,
488                40, 80, 110, 205, 51, 36, 46, 92, 235, 95, 218, 254, 77, 48, 102, 181, 192, 241,
489                89, 179, 192, 90, 98, 30, 246, 95, 23, 126, 162, 134, 0, 0, 0, 0,
490            ],
491        );
492    }
493
494    #[test_log::test]
495    fn txid_roundtrip() {
496        let txid = bitcoin::Txid::from_str(
497            "51f7ed2f23e58cc6e139e715e9ce304a1e858416edc9079dd7b74fa8d2efc09a",
498        )
499        .unwrap();
500        test_roundtrip_expected(
501            &txid,
502            &[
503                154, 192, 239, 210, 168, 79, 183, 215, 157, 7, 201, 237, 22, 132, 133, 30, 74, 48,
504                206, 233, 21, 231, 57, 225, 198, 140, 229, 35, 47, 237, 247, 81,
505            ],
506        );
507    }
508
509    #[test_log::test]
510    fn network_roundtrip() {
511        let networks: [(bitcoin::Network, [u8; 5], [u8; 4]); 5] = [
512            (
513                bitcoin::Network::Bitcoin,
514                [0xFE, 0xD9, 0xB4, 0xBE, 0xF9],
515                [0xF9, 0xBE, 0xB4, 0xD9],
516            ),
517            (
518                bitcoin::Network::Testnet,
519                [0xFE, 0x07, 0x09, 0x11, 0x0B],
520                [0x0B, 0x11, 0x09, 0x07],
521            ),
522            (
523                bitcoin::Network::Testnet4,
524                [0xFE, 0x28, 0x3F, 0x16, 0x1C],
525                [0x1C, 0x16, 0x3F, 0x28],
526            ),
527            (
528                bitcoin::Network::Signet,
529                [0xFE, 0x40, 0xCF, 0x03, 0x0A],
530                [0x0A, 0x03, 0xCF, 0x40],
531            ),
532            (
533                bitcoin::Network::Regtest,
534                [0xFE, 0xDA, 0xB5, 0xBF, 0xFA],
535                [0xFA, 0xBF, 0xB5, 0xDA],
536            ),
537        ];
538
539        for (network, magic_legacy_bytes, magic_bytes) in networks {
540            let network_legacy_encoded =
541                NetworkLegacyEncodingWrapper(network).consensus_encode_to_vec();
542
543            let network_encoded = network.consensus_encode_to_vec();
544
545            let network_legacy_decoded = NetworkLegacyEncodingWrapper::consensus_decode_whole(
546                &network_legacy_encoded,
547                &ModuleDecoderRegistry::default(),
548            )
549            .unwrap()
550            .0;
551
552            let network_decoded = bitcoin::Network::consensus_decode_whole(
553                &network_encoded,
554                &ModuleDecoderRegistry::default(),
555            )
556            .unwrap();
557
558            assert_eq!(magic_legacy_bytes, *network_legacy_encoded);
559            assert_eq!(magic_bytes, *network_encoded);
560            assert_eq!(network, network_legacy_decoded);
561            assert_eq!(network, network_decoded);
562        }
563    }
564
565    #[test_log::test]
566    fn address_roundtrip() {
567        let addresses = [
568            "bc1p2wsldez5mud2yam29q22wgfh9439spgduvct83k3pm50fcxa5dps59h4z5",
569            "mxMYaq5yWinZ9AKjCDcBEbiEwPJD9n2uLU",
570            "1FK8o7mUxyd6QWJAUw7J4vW7eRxuyjj6Ne",
571            "3JSrSU7z7R1Yhh26pt1zzRjQz44qjcrXwb",
572            "tb1qunn0thpt8uk3yk2938ypjccn3urxprt78z9ccq",
573            "2MvUMRv2DRHZi3VshkP7RMEU84mVTfR9xjq",
574        ];
575
576        for address_str in addresses {
577            let address =
578                bitcoin::Address::from_str(address_str).expect("All tested addresses are valid");
579            let encoding = address.consensus_encode_to_vec();
580            let parsed_address = bitcoin::Address::consensus_decode_whole(
581                &encoding,
582                &ModuleDecoderRegistry::default(),
583            )
584            .expect("Decoding address failed");
585
586            assert_eq!(address, parsed_address);
587        }
588    }
589
590    #[test_log::test]
591    fn sha256_roundtrip() {
592        test_roundtrip_expected(
593            &bitcoin::hashes::sha256::Hash::hash(b"Hello world!"),
594            &[
595                192, 83, 94, 75, 226, 183, 159, 253, 147, 41, 19, 5, 67, 107, 248, 137, 49, 78, 74,
596                63, 174, 192, 94, 207, 252, 187, 125, 243, 26, 217, 229, 26,
597            ],
598        );
599    }
600
601    #[test_log::test]
602    fn bolt11_invoice_roundtrip() {
603        let invoice_str = "lnbc100p1psj9jhxdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4q0d3p2sfluzdx45tqcs\
604			h2pu5qc7lgq0xs578ngs6s0s68ua4h7cvspp5q6rmq35js88zp5dvwrv9m459tnk2zunwj5jalqtyxqulh0l\
605			5gflssp5nf55ny5gcrfl30xuhzj3nphgj27rstekmr9fw3ny5989s300gyus9qyysgqcqpcrzjqw2sxwe993\
606			h5pcm4dxzpvttgza8zhkqxpgffcrf5v25nwpr3cmfg7z54kuqq8rgqqqqqqqq2qqqqq9qq9qrzjqd0ylaqcl\
607			j9424x9m8h2vcukcgnm6s56xfgu3j78zyqzhgs4hlpzvznlugqq9vsqqqqqqqlgqqqqqeqq9qrzjqwldmj9d\
608			ha74df76zhx6l9we0vjdquygcdt3kssupehe64g6yyp5yz5rhuqqwccqqyqqqqlgqqqqjcqq9qrzjqf9e58a\
609			guqr0rcun0ajlvmzq3ek63cw2w282gv3z5uupmuwvgjtq2z55qsqqg6qqqyqqqrtnqqqzq3cqygrzjqvphms\
610			ywntrrhqjcraumvc4y6r8v4z5v593trte429v4hredj7ms5z52usqq9ngqqqqqqqlgqqqqqqgq9qrzjq2v0v\
611			p62g49p7569ev48cmulecsxe59lvaw3wlxm7r982zxa9zzj7z5l0cqqxusqqyqqqqlgqqqqqzsqygarl9fh3\
612			8s0gyuxjjgux34w75dnc6xp2l35j7es3jd4ugt3lu0xzre26yg5m7ke54n2d5sym4xcmxtl8238xxvw5h5h5\
613			j5r6drg6k6zcqj0fcwg";
614        let invoice = invoice_str
615            .parse::<lightning_invoice::Bolt11Invoice>()
616            .unwrap();
617        test_roundtrip(&invoice);
618    }
619}