Skip to main content

bitcoin_ext/
lib.rs

1
2#[macro_use] extern crate lazy_static;
3#[macro_use] extern crate serde as serde_crate;
4
5pub extern crate bitcoin;
6
7pub mod cpfp;
8pub mod fee;
9
10#[cfg(feature = "bdk")]
11pub mod bdk;
12#[cfg(feature = "rpc")]
13pub mod rpc;
14pub mod serde;
15
16pub use mbitcoin::{
17	AmountExt, FeeRateExt, TaprootSpendInfoExt, KeypairExt, TransactionExt, TxOutExt,
18};
19
20#[path = "bitcoin.rs"]
21mod mbitcoin;
22
23use std::{fmt, str::FromStr};
24
25use bitcoin::{Amount, BlockHash};
26
27use serde_crate::ser::SerializeStruct;
28
29/// The number of confirmations after which we don't expect a
30/// re-org to ever happen.
31pub const DEEPLY_CONFIRMED: BlockHeight = 100;
32
33pub const P2TR_DUST_VB: u64 = 110;
34/// 330 satoshis
35pub const P2TR_DUST_SAT: u64 = P2TR_DUST_VB * 3;
36pub const P2TR_DUST: Amount = Amount::from_sat(P2TR_DUST_SAT);
37
38pub const P2WPKH_DUST_VB: u64 = 90;
39/// 294 satoshis
40pub const P2WPKH_DUST_SAT: u64 = P2WPKH_DUST_VB * 3;
41pub const P2WPKH_DUST: Amount = Amount::from_sat(P2WPKH_DUST_SAT);
42
43pub const P2PKH_DUST_VB: u64 = 182;
44/// 546 satoshis
45pub const P2PKH_DUST_SAT: u64 = P2PKH_DUST_VB * 3;
46pub const P2PKH_DUST: Amount = Amount::from_sat(P2PKH_DUST_SAT);
47
48pub const P2SH_DUST_VB: u64 = 180;
49/// 540 satoshis
50pub const P2SH_DUST_SAT: u64 = P2SH_DUST_VB * 3;
51pub const P2SH_DUST: Amount = Amount::from_sat(P2SH_DUST_SAT);
52
53pub const P2WSH_DUST_VB: u64 = 110;
54/// 330 satoshis
55pub const P2WSH_DUST_SAT: u64 = P2WSH_DUST_VB * 3;
56pub const P2WSH_DUST: Amount = Amount::from_sat(P2WSH_DUST_SAT);
57
58/// Witness weight of a taproot keyspend.
59pub const TAPROOT_KEYSPEND_WEIGHT: usize = 66;
60
61/// Type representing a block height in the bitcoin blockchain.
62pub type BlockHeight = u32;
63/// Type representing a block height delta
64pub type BlockDelta = u16;
65/// Reference to a block in the chain
66///
67/// String representation is "<height>:<hash>".
68#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
69pub struct BlockRef {
70	pub height: BlockHeight,
71	pub hash: BlockHash,
72}
73
74impl fmt::Display for BlockRef {
75	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76		write!(f, "{}:{}", self.height, self.hash)
77	}
78}
79
80impl fmt::Debug for BlockRef {
81	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82		fmt::Display::fmt(self, f)
83	}
84}
85
86impl FromStr for BlockRef {
87	type Err = &'static str;
88
89	fn from_str(s: &str) -> Result<Self, Self::Err> {
90		let mut parts = s.splitn(2, ':');
91		Ok(BlockRef {
92			height: parts.next().expect("always one part")
93				.parse().map_err(|_| "invalid height")?,
94			hash: parts.next().ok_or("should be <height>:<hash> string")?
95				.parse().map_err(|_| "invalid hash")?,
96		})
97	}
98}
99
100impl serde_crate::Serialize for BlockRef {
101	fn serialize<S: serde_crate::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
102		let mut state = s.serialize_struct("BlockRef", 2)?;
103		state.serialize_field("height", &self.height)?;
104		state.serialize_field("hash", &self.hash)?;
105		state.end()
106	}
107}
108
109impl<'de> serde_crate::Deserialize<'de> for BlockRef {
110	fn deserialize<D: serde_crate::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
111		struct Visitor;
112		impl<'de> serde_crate::de::Visitor<'de> for Visitor {
113			type Value = BlockRef;
114			fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115				write!(f, "a BlockRef (struct/string)")
116			}
117			fn visit_str<E: serde_crate::de::Error>(self, v: &str) -> Result<Self::Value, E> {
118				BlockRef::from_str(v).map_err(serde_crate::de::Error::custom)
119			}
120			fn visit_map<A: serde_crate::de::MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
121				let mut height = None;
122				let mut hash = None;
123				while let Some(key) = map.next_key::<&str>()? {
124					match key {
125						"height" => height = Some(map.next_value()?),
126						"hash" => hash = Some(map.next_value()?),
127						_ => {
128							let _ = map.next_value::<serde_crate::de::IgnoredAny>()?;
129						}
130					}
131				}
132				Ok(BlockRef {
133					height: height.ok_or_else(|| serde_crate::de::Error::missing_field("height"))?,
134					hash: hash.ok_or_else(|| serde_crate::de::Error::missing_field("hash"))?,
135				})
136			}
137		}
138		d.deserialize_any(Visitor)
139	}
140}
141
142#[cfg(feature = "bdk")]
143impl From<bdk_wallet::chain::BlockId> for BlockRef {
144	fn from(id: bdk_wallet::chain::BlockId) -> Self {
145		Self {
146			height: id.height,
147			hash: id.hash,
148		}
149	}
150}
151
152#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
153pub enum TxStatus {
154	Confirmed(BlockRef),
155	Mempool,
156	NotFound,
157}
158
159impl TxStatus {
160	pub fn is_confirmed(&self) -> bool {
161		match self {
162			TxStatus::Confirmed(_) => true,
163			_ => false,
164		}
165	}
166
167	pub fn confirmed_height(&self) -> Option<BlockHeight> {
168		match self {
169			TxStatus::Confirmed(block_ref) => Some(block_ref.height),
170			_ => None,
171		}
172	}
173
174	pub fn confirmed_in(&self) -> Option<BlockRef> {
175		match self {
176			TxStatus::Confirmed(block_ref) => Some(*block_ref),
177			_ => None,
178		}
179	}
180}
181