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