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 miniscript::{Descriptor, MiniscriptKey};
9use serde::{Deserialize, Serialize};
10
11use super::{BufBitcoinReader, CountWrite, SimpleBitcoinRead};
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<usize, std::io::Error> {
23 Ok(bitcoin::consensus::Encodable::consensus_encode(
24 self,
25 &mut std::io::BufWriter::new(writer),
26 )?)
27 }
28 }
29
30 impl crate::encoding::Decodable for $btc_type {
31 fn consensus_decode_from_finite_reader<D: std::io::Read>(
32 d: &mut D,
33 _modules: &$crate::module::registry::ModuleDecoderRegistry,
34 ) -> Result<Self, crate::encoding::DecodeError> {
35 bitcoin::consensus::Decodable::consensus_decode_from_finite_reader(
36 &mut SimpleBitcoinRead(d),
37 )
38 .map_err(crate::encoding::DecodeError::from_err)
39 }
40 }
41 };
42}
43
44impl_encode_decode_bridge!(bitcoin::block::Header);
45impl_encode_decode_bridge!(bitcoin::BlockHash);
46impl_encode_decode_bridge!(bitcoin::OutPoint);
47impl_encode_decode_bridge!(bitcoin::ScriptBuf);
48impl_encode_decode_bridge!(bitcoin::Transaction);
49impl_encode_decode_bridge!(bitcoin::merkle_tree::PartialMerkleTree);
50
51impl crate::encoding::Encodable for bitcoin::psbt::Psbt {
52 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, std::io::Error> {
53 Ok(self.serialize_to_writer(&mut CountWrite::from(writer))?)
54 }
55}
56
57impl crate::encoding::Decodable for bitcoin::psbt::Psbt {
58 fn consensus_decode_from_finite_reader<D: std::io::Read>(
59 d: &mut D,
60 _modules: &ModuleDecoderRegistry,
61 ) -> Result<Self, crate::encoding::DecodeError> {
62 Self::deserialize_from_reader(&mut BufBitcoinReader::new(d))
63 .map_err(crate::encoding::DecodeError::from_err)
64 }
65}
66
67impl crate::encoding::Encodable for bitcoin::Txid {
68 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, std::io::Error> {
69 Ok(bitcoin::consensus::Encodable::consensus_encode(
70 self,
71 &mut std::io::BufWriter::new(writer),
72 )?)
73 }
74
75 fn consensus_encode_to_hex(&self) -> String {
76 let mut bytes = vec![];
77 self.consensus_encode(&mut bytes)
78 .expect("encoding to bytes can't fail for io reasons");
79
80 bytes.reverse();
82
83 bytes.encode_hex()
85 }
86}
87
88impl crate::encoding::Decodable for bitcoin::Txid {
89 fn consensus_decode_from_finite_reader<D: std::io::Read>(
90 d: &mut D,
91 _modules: &::fedimint_core::module::registry::ModuleDecoderRegistry,
92 ) -> Result<Self, crate::encoding::DecodeError> {
93 bitcoin::consensus::Decodable::consensus_decode_from_finite_reader(&mut SimpleBitcoinRead(
94 d,
95 ))
96 .map_err(crate::encoding::DecodeError::from_err)
97 }
98
99 fn consensus_decode_hex(
100 hex: &str,
101 modules: &ModuleDecoderRegistry,
102 ) -> Result<Self, DecodeError> {
103 let mut bytes = Vec::<u8>::from_hex(hex)
104 .map_err(anyhow::Error::from)
105 .map_err(DecodeError::new_custom)?;
106
107 bytes.reverse();
109
110 let mut reader = std::io::Cursor::new(bytes);
111 Decodable::consensus_decode(&mut reader, modules)
112 }
113}
114
115impl<K> Encodable for Descriptor<K>
116where
117 K: MiniscriptKey,
118{
119 fn consensus_encode<W: Write>(&self, writer: &mut W) -> Result<usize, Error> {
120 let descriptor_str = self.to_string();
121 descriptor_str.consensus_encode(writer)
122 }
123}
124
125impl<K> Decodable for Descriptor<K>
126where
127 Self: FromStr,
128 <Self as FromStr>::Err: ToString + std::error::Error + Send + Sync + 'static,
129 K: MiniscriptKey,
130{
131 fn consensus_decode_from_finite_reader<D: std::io::Read>(
132 d: &mut D,
133 modules: &ModuleDecoderRegistry,
134 ) -> Result<Self, DecodeError> {
135 let descriptor_str = String::consensus_decode_from_finite_reader(d, modules)?;
136 Self::from_str(&descriptor_str).map_err(DecodeError::from_err)
137 }
138}
139
140impl Encodable for bitcoin::Network {
141 fn consensus_encode<W: Write>(&self, writer: &mut W) -> Result<usize, Error> {
142 u32::from_le_bytes(self.magic().to_bytes()).consensus_encode(writer)
143 }
144}
145
146impl Decodable for bitcoin::Network {
147 fn consensus_decode<D: std::io::Read>(
148 d: &mut D,
149 modules: &ModuleDecoderRegistry,
150 ) -> Result<Self, DecodeError> {
151 let num = u32::consensus_decode(d, modules)?;
152 let magic = bitcoin::p2p::Magic::from_bytes(num.to_le_bytes());
153 Self::from_magic(magic).ok_or_else(|| {
154 DecodeError::new_custom(format_err!("Unknown network magic: {:x}", magic))
155 })
156 }
157}
158
159#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
160pub struct NetworkSaneEncodingWrapper(pub bitcoin::Network);
161
162impl Encodable for NetworkSaneEncodingWrapper {
163 fn consensus_encode<W: Write>(&self, writer: &mut W) -> Result<usize, Error> {
164 self.0.magic().to_bytes().consensus_encode(writer)
165 }
166}
167
168impl Decodable for NetworkSaneEncodingWrapper {
169 fn consensus_decode<D: std::io::Read>(
170 d: &mut D,
171 modules: &ModuleDecoderRegistry,
172 ) -> Result<Self, DecodeError> {
173 Ok(Self(
174 bitcoin::Network::from_magic(bitcoin::p2p::Magic::from_bytes(
175 Decodable::consensus_decode(d, modules)?,
176 ))
177 .ok_or_else(|| DecodeError::new_custom(format_err!("Unknown network magic")))?,
178 ))
179 }
180}
181
182impl Encodable for bitcoin::Amount {
183 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, std::io::Error> {
184 self.to_sat().consensus_encode(writer)
185 }
186}
187
188impl Decodable for bitcoin::Amount {
189 fn consensus_decode<D: std::io::Read>(
190 d: &mut D,
191 modules: &ModuleDecoderRegistry,
192 ) -> Result<Self, DecodeError> {
193 Ok(Self::from_sat(u64::consensus_decode(d, modules)?))
194 }
195}
196
197impl Encodable for bitcoin::Address<NetworkUnchecked> {
198 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, Error> {
199 let mut len = 0;
200 len += get_network_for_address(self.as_unchecked()).consensus_encode(writer)?;
201 len += self
202 .clone()
203 .assume_checked()
204 .script_pubkey()
205 .consensus_encode(writer)?;
206 Ok(len)
207 }
208}
209
210impl Decodable for bitcoin::Address<NetworkUnchecked> {
211 fn consensus_decode<D: std::io::Read>(
212 mut d: &mut D,
213 modules: &ModuleDecoderRegistry,
214 ) -> Result<Self, DecodeError> {
215 let network = bitcoin::Network::consensus_decode(&mut d, modules)?;
216 let script_pk = bitcoin::ScriptBuf::consensus_decode(&mut d, modules)?;
217
218 let address = bitcoin::Address::from_script(&script_pk, network)
219 .map_err(|e| DecodeError::new_custom(e.into()))?;
220
221 Ok(address.as_unchecked().clone())
222 }
223}
224
225impl Encodable for bitcoin::hashes::sha256::Hash {
226 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, Error> {
227 self.to_byte_array().consensus_encode(writer)
228 }
229}
230
231impl Decodable for bitcoin::hashes::sha256::Hash {
232 fn consensus_decode<D: std::io::Read>(
233 d: &mut D,
234 modules: &ModuleDecoderRegistry,
235 ) -> Result<Self, DecodeError> {
236 Ok(Self::from_byte_array(Decodable::consensus_decode(
237 d, modules,
238 )?))
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use std::io::Cursor;
245 use std::str::FromStr;
246
247 use bitcoin::hashes::Hash as BitcoinHash;
248
249 use crate::encoding::btc::NetworkSaneEncodingWrapper;
250 use crate::encoding::tests::test_roundtrip_expected;
251 use crate::encoding::{Decodable, Encodable};
252 use crate::ModuleDecoderRegistry;
253
254 #[test_log::test]
255 fn network_roundtrip() {
256 let networks: [(bitcoin::Network, [u8; 5], [u8; 4]); 5] = [
257 (
258 bitcoin::Network::Bitcoin,
259 [0xFE, 0xD9, 0xB4, 0xBE, 0xF9],
260 [0xF9, 0xBE, 0xB4, 0xD9],
261 ),
262 (
263 bitcoin::Network::Testnet,
264 [0xFE, 0x07, 0x09, 0x11, 0x0B],
265 [0x0B, 0x11, 0x09, 0x07],
266 ),
267 (
268 bitcoin::Network::Testnet4,
269 [0xFE, 0x28, 0x3F, 0x16, 0x1C],
270 [0x1C, 0x16, 0x3F, 0x28],
271 ),
272 (
273 bitcoin::Network::Signet,
274 [0xFE, 0x40, 0xCF, 0x03, 0x0A],
275 [0x0A, 0x03, 0xCF, 0x40],
276 ),
277 (
278 bitcoin::Network::Regtest,
279 [0xFE, 0xDA, 0xB5, 0xBF, 0xFA],
280 [0xFA, 0xBF, 0xB5, 0xDA],
281 ),
282 ];
283
284 for (network, magic_bytes, magic_sane_bytes) in networks {
285 let mut network_encoded = Vec::new();
286 network.consensus_encode(&mut network_encoded).unwrap();
287
288 let mut network_sane_encoded = Vec::new();
289 NetworkSaneEncodingWrapper(network)
290 .consensus_encode(&mut network_sane_encoded)
291 .unwrap();
292
293 let network_decoded = bitcoin::Network::consensus_decode(
294 &mut Cursor::new(network_encoded.clone()),
295 &ModuleDecoderRegistry::default(),
296 )
297 .unwrap();
298
299 let network_sane_decoded = NetworkSaneEncodingWrapper::consensus_decode(
300 &mut Cursor::new(network_sane_encoded.clone()),
301 &ModuleDecoderRegistry::default(),
302 )
303 .unwrap();
304
305 assert_eq!(magic_bytes, *network_encoded);
306 assert_eq!(magic_sane_bytes, *network_sane_encoded);
307 assert_eq!(network, network_decoded);
308 assert_eq!(network, network_sane_decoded.0);
309 }
310 }
311
312 #[test_log::test]
313 fn address_roundtrip() {
314 let addresses = [
315 "bc1p2wsldez5mud2yam29q22wgfh9439spgduvct83k3pm50fcxa5dps59h4z5",
316 "mxMYaq5yWinZ9AKjCDcBEbiEwPJD9n2uLU",
317 "1FK8o7mUxyd6QWJAUw7J4vW7eRxuyjj6Ne",
318 "3JSrSU7z7R1Yhh26pt1zzRjQz44qjcrXwb",
319 "tb1qunn0thpt8uk3yk2938ypjccn3urxprt78z9ccq",
320 "2MvUMRv2DRHZi3VshkP7RMEU84mVTfR9xjq",
321 ];
322
323 for address_str in addresses {
324 let address =
325 bitcoin::Address::from_str(address_str).expect("All tested addresses are valid");
326 let mut encoding = vec![];
327 address
328 .consensus_encode(&mut encoding)
329 .expect("Encoding to vec can't fail");
330 let mut cursor = Cursor::new(encoding);
331 let parsed_address =
332 bitcoin::Address::consensus_decode(&mut cursor, &ModuleDecoderRegistry::default())
333 .expect("Decoding address failed");
334
335 assert_eq!(address, parsed_address);
336 }
337 }
338
339 #[test_log::test]
340 fn sha256_roundtrip() {
341 test_roundtrip_expected(
342 &bitcoin::hashes::sha256::Hash::hash(b"Hello world!"),
343 &[
344 192, 83, 94, 75, 226, 183, 159, 253, 147, 41, 19, 5, 67, 107, 248, 137, 49, 78, 74,
345 63, 174, 192, 94, 207, 252, 187, 125, 243, 26, 217, 229, 26,
346 ],
347 );
348 }
349}