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
33fn consensus_encode_with_buffer<T: bitcoin::consensus::Encodable, W: std::io::Write>(
34 value: &T,
35 writer: &mut W,
36) -> Result<(), std::io::Error> {
37 let mut buffered_writer = std::io::BufWriter::new(writer);
38 bitcoin::consensus::Encodable::consensus_encode(value, &mut buffered_writer)?;
39 buffered_writer
40 .into_inner()
41 .map_err(std::io::IntoInnerError::into_error)?;
42 Ok(())
43}
44
45macro_rules! impl_encode_decode_bridge {
46 ($btc_type:ty) => {
47 impl crate::encoding::Encodable for $btc_type {
48 fn consensus_encode<W: std::io::Write>(
49 &self,
50 writer: &mut W,
51 ) -> Result<(), std::io::Error> {
52 consensus_encode_with_buffer(self, writer)
53 }
54 }
55
56 impl crate::encoding::Decodable for $btc_type {
57 fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
58 d: &mut D,
59 _modules: &$crate::module::registry::ModuleDecoderRegistry,
60 ) -> Result<Self, crate::encoding::DecodeError> {
61 bitcoin::consensus::Decodable::consensus_decode_from_finite_reader(
62 &mut SimpleBitcoinRead(d),
63 )
64 .map_err(from_bitcoin_encode_error)
65 }
66 }
67 };
68}
69
70impl_encode_decode_bridge!(bitcoin::block::Header);
71impl_encode_decode_bridge!(bitcoin::BlockHash);
72impl_encode_decode_bridge!(bitcoin::OutPoint);
73impl_encode_decode_bridge!(bitcoin::TxOut);
74impl_encode_decode_bridge!(bitcoin::ScriptBuf);
75impl_encode_decode_bridge!(bitcoin::Transaction);
76impl_encode_decode_bridge!(bitcoin::merkle_tree::PartialMerkleTree);
77
78impl crate::encoding::Encodable for bitcoin::psbt::Psbt {
79 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
80 self.serialize_to_writer(&mut BitoinIoWriteAdapter::from(writer))?;
81 Ok(())
82 }
83}
84
85impl crate::encoding::Decodable for bitcoin::psbt::Psbt {
86 fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
87 d: &mut D,
88 _modules: &ModuleDecoderRegistry,
89 ) -> Result<Self, crate::encoding::DecodeError> {
90 Self::deserialize_from_reader(&mut BufBitcoinReader::new(d)).map_err(from_psbt_error)
91 }
92}
93
94impl crate::encoding::Encodable for bitcoin::Txid {
95 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
96 consensus_encode_with_buffer(self, writer)
97 }
98
99 fn consensus_encode_to_hex(&self) -> String {
100 let mut bytes = self.consensus_encode_to_vec();
101
102 bytes.reverse();
104
105 bytes.encode_hex()
107 }
108}
109
110impl crate::encoding::Decodable for bitcoin::Txid {
111 fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
112 d: &mut D,
113 _modules: &::fedimint_core::module::registry::ModuleDecoderRegistry,
114 ) -> Result<Self, crate::encoding::DecodeError> {
115 bitcoin::consensus::Decodable::consensus_decode_from_finite_reader(&mut SimpleBitcoinRead(
116 d,
117 ))
118 .map_err(from_bitcoin_encode_error)
119 }
120
121 fn consensus_decode_hex(
122 hex: &str,
123 modules: &ModuleDecoderRegistry,
124 ) -> Result<Self, DecodeError> {
125 let mut bytes = Vec::<u8>::from_hex(hex).map_err(DecodeError::from_err)?;
126
127 bytes.reverse();
129
130 Decodable::consensus_decode_whole(&bytes, modules)
131 }
132}
133
134impl<K> Encodable for Descriptor<K>
135where
136 K: MiniscriptKey,
137{
138 fn consensus_encode<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
139 let descriptor_str = self.to_string();
140 descriptor_str.consensus_encode(writer)
141 }
142}
143
144impl<K> Decodable for Descriptor<K>
145where
146 Self: FromStr,
147 <Self as FromStr>::Err: ToString + std::error::Error + Send + Sync + 'static,
148 K: MiniscriptKey,
149{
150 fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
151 d: &mut D,
152 modules: &ModuleDecoderRegistry,
153 ) -> Result<Self, DecodeError> {
154 let descriptor_str = String::consensus_decode_partial_from_finite_reader(d, modules)?;
155 Self::from_str(&descriptor_str).map_err(DecodeError::from_err)
156 }
157}
158
159#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
163pub struct NetworkLegacyEncodingWrapper(pub bitcoin::Network);
164
165impl std::fmt::Display for NetworkLegacyEncodingWrapper {
166 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
167 write!(f, "{}", self.0)
168 }
169}
170
171impl Encodable for NetworkLegacyEncodingWrapper {
172 fn consensus_encode<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
173 u32::from_le_bytes(self.0.magic().to_bytes()).consensus_encode(writer)
174 }
175}
176
177impl Decodable for NetworkLegacyEncodingWrapper {
178 fn consensus_decode_partial<D: std::io::Read>(
179 d: &mut D,
180 modules: &ModuleDecoderRegistry,
181 ) -> Result<Self, DecodeError> {
182 let num = u32::consensus_decode_partial(d, modules)?;
183 let magic = bitcoin::p2p::Magic::from_bytes(num.to_le_bytes());
184 let network = bitcoin::Network::from_magic(magic)
185 .ok_or_else(|| DecodeError::custom(format!("Unknown network magic: {magic:x}")))?;
186 Ok(Self(network))
187 }
188}
189impl Encodable for bitcoin::Network {
190 fn consensus_encode<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
191 self.magic().to_bytes().consensus_encode(writer)
192 }
193}
194
195impl Decodable for bitcoin::Network {
196 fn consensus_decode_partial<D: std::io::Read>(
197 d: &mut D,
198 modules: &ModuleDecoderRegistry,
199 ) -> Result<Self, DecodeError> {
200 Self::from_magic(bitcoin::p2p::Magic::from_bytes(
201 Decodable::consensus_decode_partial(d, modules)?,
202 ))
203 .ok_or_else(|| DecodeError::from_str("Unknown network magic"))
204 }
205}
206
207impl Encodable for bitcoin::Amount {
208 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
209 self.to_sat().consensus_encode(writer)
210 }
211}
212
213impl Decodable for bitcoin::Amount {
214 fn consensus_decode_partial<D: std::io::Read>(
215 d: &mut D,
216 modules: &ModuleDecoderRegistry,
217 ) -> Result<Self, DecodeError> {
218 Ok(Self::from_sat(u64::consensus_decode_partial(d, modules)?))
219 }
220}
221
222impl Encodable for bitcoin::Address<NetworkUnchecked> {
223 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
224 NetworkLegacyEncodingWrapper(get_network_for_address(self)).consensus_encode(writer)?;
225 self.clone()
226 .assume_checked()
230 .script_pubkey()
231 .consensus_encode(writer)?;
232 Ok(())
233 }
234}
235
236impl Decodable for bitcoin::Address<NetworkUnchecked> {
237 fn consensus_decode_partial<D: std::io::Read>(
238 mut d: &mut D,
239 modules: &ModuleDecoderRegistry,
240 ) -> Result<Self, DecodeError> {
241 let network = NetworkLegacyEncodingWrapper::consensus_decode_partial(&mut d, modules)?.0;
242 let script_pk = bitcoin::ScriptBuf::consensus_decode_partial(&mut d, modules)?;
243
244 let address =
245 bitcoin::Address::from_script(&script_pk, network).map_err(DecodeError::from_err)?;
246
247 Ok(address.into_unchecked())
248 }
249}
250
251impl Encodable for bitcoin::hashes::sha256::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::sha256::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 bitcoin::hashes::hash160::Hash {
269 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
270 self.to_byte_array().consensus_encode(writer)
271 }
272}
273
274impl Decodable for bitcoin::hashes::hash160::Hash {
275 fn consensus_decode_partial<D: std::io::Read>(
276 d: &mut D,
277 modules: &ModuleDecoderRegistry,
278 ) -> Result<Self, DecodeError> {
279 Ok(Self::from_byte_array(Decodable::consensus_decode_partial(
280 d, modules,
281 )?))
282 }
283}
284
285impl Encodable for lightning_invoice::Bolt11Invoice {
286 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
287 self.to_string().consensus_encode(writer)
288 }
289}
290
291impl Decodable for lightning_invoice::Bolt11Invoice {
292 fn consensus_decode_partial<D: std::io::Read>(
293 d: &mut D,
294 modules: &ModuleDecoderRegistry,
295 ) -> Result<Self, DecodeError> {
296 String::consensus_decode_partial(d, modules)?
297 .parse::<Self>()
298 .map_err(DecodeError::from_err)
299 }
300}
301
302impl Encodable for lightning_invoice::RoutingFees {
303 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
304 self.base_msat.consensus_encode(writer)?;
305 self.proportional_millionths.consensus_encode(writer)?;
306 Ok(())
307 }
308}
309
310impl Decodable for lightning_invoice::RoutingFees {
311 fn consensus_decode_partial<D: std::io::Read>(
312 d: &mut D,
313 modules: &ModuleDecoderRegistry,
314 ) -> Result<Self, DecodeError> {
315 let base_msat = Decodable::consensus_decode_partial(d, modules)?;
316 let proportional_millionths = Decodable::consensus_decode_partial(d, modules)?;
317 Ok(Self {
318 base_msat,
319 proportional_millionths,
320 })
321 }
322}
323
324impl Encodable for BigSize {
325 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
326 let mut writer = BitoinIoWriteAdapter::from(writer);
327 self.write(&mut writer)?;
328 Ok(())
329 }
330}
331
332impl Decodable for BigSize {
333 fn consensus_decode_partial<R: std::io::Read>(
334 r: &mut R,
335 _modules: &ModuleDecoderRegistry,
336 ) -> Result<Self, DecodeError> {
337 Self::read(&mut SimpleBitcoinRead(r)).map_err(|error| match error {
338 msgs::DecodeError::ShortRead => {
339 DecodeError::Io(std::io::ErrorKind::UnexpectedEof.into())
340 }
341 msgs::DecodeError::Io(kind) => DecodeError::Io(bitcoin_io::Error::from(kind).into()),
342 other => DecodeError::custom(format!("BigSize decoding error: {other:?}")),
343 })
344 }
345}
346
347struct SimpleBitcoinRead<R: std::io::Read>(R);
351
352impl<R: std::io::Read> bitcoin_io::Read for SimpleBitcoinRead<R> {
353 fn read(&mut self, buf: &mut [u8]) -> bitcoin_io::Result<usize> {
354 self.0.read(buf).map_err(bitcoin_io::Error::from)
355 }
356}
357
358struct BufBitcoinReader<'a, R: std::io::Read> {
369 inner: &'a mut R,
370 buf: [u8; 1],
371 is_consumed: bool,
372}
373
374impl<'a, R: std::io::Read> BufBitcoinReader<'a, R> {
375 fn new(inner: &'a mut R) -> Self {
377 BufBitcoinReader {
378 inner,
379 buf: [0; 1],
380 is_consumed: true,
381 }
382 }
383}
384
385impl<R: std::io::Read> bitcoin_io::Read for BufBitcoinReader<'_, R> {
386 #[inline]
387 fn read(&mut self, output: &mut [u8]) -> bitcoin_io::Result<usize> {
388 if output.is_empty() {
389 return Ok(0);
390 }
391 #[allow(clippy::useless_let_if_seq)]
392 let mut offset = 0;
393 if !self.is_consumed {
394 output[0] = self.buf[0];
395 self.is_consumed = true;
396 offset = 1;
397 }
398 Ok(self
399 .inner
400 .read(&mut output[offset..])
401 .map(|len| len + offset)?)
402 }
403}
404
405impl<R: std::io::Read> bitcoin_io::BufRead for BufBitcoinReader<'_, R> {
406 #[inline]
407 fn fill_buf(&mut self) -> bitcoin_io::Result<&[u8]> {
408 debug_assert!(false, "rust-bitcoin doesn't actually use this");
409 if self.is_consumed {
410 let count = self.inner.read(&mut self.buf[..])?;
411 debug_assert!(count <= 1, "read gave us a garbage length");
412
413 self.is_consumed = count == 0;
415 }
416
417 if self.is_consumed {
418 Ok(&[])
419 } else {
420 Ok(&self.buf[..])
421 }
422 }
423
424 #[inline]
425 fn consume(&mut self, amount: usize) {
426 debug_assert!(false, "rust-bitcoin doesn't actually use this");
427 if amount >= 1 {
428 debug_assert_eq!(amount, 1, "Can only consume one byte");
429 debug_assert!(!self.is_consumed, "Cannot consume more than had been read");
430 self.is_consumed = true;
431 }
432 }
433}
434
435pub struct BitoinIoWriteAdapter<W> {
441 inner: W,
442}
443
444impl<W> From<W> for BitoinIoWriteAdapter<W> {
445 fn from(inner: W) -> Self {
446 Self { inner }
447 }
448}
449
450impl<W: Write> bitcoin_io::Write for BitoinIoWriteAdapter<W> {
451 fn write(&mut self, buf: &[u8]) -> bitcoin_io::Result<usize> {
452 let written = self.inner.write(buf)?;
453 Ok(written)
454 }
455
456 fn flush(&mut self) -> bitcoin_io::Result<()> {
457 self.inner.flush().map_err(bitcoin_io::Error::from)
458 }
459}
460
461#[cfg(test)]
462mod tests;