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