1
2use std::borrow::Borrow;
3use std::collections::BTreeMap;
4
5use bitcoin::{
6 taproot, Amount, Denomination, FeeRate, OutPoint, ScriptBuf, Transaction, TxOut, Weight,
7};
8use bitcoin::taproot::ControlBlock;
9use bitcoin::secp256k1::{self, Keypair, Secp256k1};
10
11use crate::{fee, P2PKH_DUST, P2SH_DUST, P2TR_DUST, P2WPKH_DUST, P2WSH_DUST};
12
13pub trait KeypairExt: Borrow<Keypair> {
15 fn for_keyspend(&self, secp: &Secp256k1<impl secp256k1::Verification>) -> Keypair {
17 let tweak = taproot::TapTweakHash::from_key_and_tweak(
18 self.borrow().x_only_public_key().0,
19 None, );
21 self.borrow().add_xonly_tweak(secp, &tweak.to_scalar()).expect("hashed values")
22 }
23}
24impl KeypairExt for Keypair {}
25
26
27pub trait TxOutExt: Borrow<TxOut> {
29 fn is_p2a_fee_anchor(&self) -> bool {
31 self.borrow().script_pubkey == *fee::P2A_SCRIPT
32 }
33
34 fn is_standard(&self) -> bool {
36 let out = self.borrow();
37
38 let dust_limit = if out.script_pubkey.is_p2pkh() {
39 P2PKH_DUST
40 } else if out.script_pubkey.is_p2sh() {
41 P2SH_DUST
42 } else if out.script_pubkey.is_p2wpkh() {
43 P2WPKH_DUST
44 } else if out.script_pubkey.is_p2wsh() {
45 P2WSH_DUST
46 } else if out.script_pubkey.is_p2tr() {
47 P2TR_DUST
48 } else if out.script_pubkey.is_op_return() {
49 return out.script_pubkey.len() <= 83;
50 } else {
51 return false;
52 };
53
54 out.value >= dust_limit
55 }
56}
57impl TxOutExt for TxOut {}
58
59
60pub trait TransactionExt: Borrow<Transaction> {
62 fn fee_anchor(&self) -> Option<(OutPoint, &TxOut)> {
66 for (i, out) in self.borrow().output.iter().enumerate() {
67 if out.is_p2a_fee_anchor() {
68 let point = OutPoint::new(self.borrow().compute_txid(), i as u32);
69 return Some((point, out));
70 }
71 }
72 None
73 }
74
75 fn output_value(&self) -> Amount {
77 self.borrow().output.iter().map(|o| o.value).sum()
78 }
79
80 #[cfg(feature = "all-related-utxos")]
82 fn all_related_utxos(&self) -> impl Iterator<Item = OutPoint> {
83 let tx = self.borrow();
84 let inputs = tx.input.iter().map(|i| i.previous_output);
85 let txid = tx.compute_txid();
86 let outputs = (0..tx.output.len()).map(move |idx| OutPoint::new(txid, idx as u32));
87 inputs.chain(outputs)
88 }
89}
90impl TransactionExt for Transaction {}
91
92
93pub trait TaprootSpendInfoExt: Borrow<taproot::TaprootSpendInfo> {
95 fn script_pubkey(&self) -> ScriptBuf {
97 ScriptBuf::new_p2tr_tweaked(self.borrow().output_key())
98 }
99
100 fn psbt_tap_scripts(&self) -> BTreeMap<ControlBlock, (ScriptBuf, taproot::LeafVersion)> {
102 let s = self.borrow();
103 s.script_map().keys().map(|pair| {
104 let cb = s.control_block(pair).unwrap();
105 let (script, leaf_version) = pair;
106 (cb, (script.clone(), *leaf_version))
107 }).collect()
108 }
109}
110impl TaprootSpendInfoExt for taproot::TaprootSpendInfo {}
111
112pub trait AmountExt: Borrow<Amount> {
114 fn to_msat(&self) -> u64 {
115 self.borrow().to_sat() * 1_000
116 }
117
118 fn from_msat_ceil(value: u64) -> Amount {
120 Amount::from_sat((value + 999) / 1_000)
121 }
122
123 fn from_msat_floor(value: u64) -> Amount {
125 Amount::from_sat(value / 1_000)
126 }
127}
128impl AmountExt for Amount {}
129
130
131pub trait FeeRateExt: Borrow<FeeRate> {
133 fn from_amount_per_kvb_ceil(amount_vkb: Amount) -> FeeRate {
134 FeeRate::from_sat_per_kvb_ceil(amount_vkb.to_sat())
135 }
136
137 fn from_amount_and_weight_ceil(fee: Amount, weight: Weight) -> Option<FeeRate> {
138 if weight == Weight::ZERO {
139 return None;
140 }
141
142 let amount_time_thousand = u64::checked_mul(fee.to_sat(), 1_000)?;
144 let sat_kwu = u64::div_ceil(amount_time_thousand, weight.to_wu());
145 Some(FeeRate::from_sat_per_kwu(sat_kwu))
146 }
147
148 fn from_sat_per_kvb_ceil(sat_kvb: u64) -> FeeRate {
149 FeeRate::from_sat_per_kwu((sat_kvb + 3) / 4)
151 }
152
153 fn from_sat_per_vb_decimal_checked_ceil(sat_vb: f64) -> Option<FeeRate> {
154 let fee = (sat_vb * 250.0).ceil();
155 if fee.is_finite() && fee >= 0.0 && fee <= u64::MAX as f64 {
156 Some(FeeRate::from_sat_per_kwu(fee as u64))
157 } else {
158 None
159 }
160 }
161
162 fn to_btc_per_kvb(&self) -> String {
163 Amount::from_sat(self.to_sat_per_kvb()).to_string_in(Denomination::Bitcoin)
164 }
165
166 fn to_sat_per_kvb(&self) -> u64 {
167 self.borrow().to_sat_per_kwu() * 4
168 }
169}
170impl FeeRateExt for FeeRate {}
171
172
173#[cfg(test)]
174mod test {
175 use super::*;
176
177 #[test]
178 fn amount_from_msat() {
179 assert_eq!(Amount::from_msat_ceil(3000), Amount::from_sat(3));
180 assert_eq!(Amount::from_msat_ceil(3001), Amount::from_sat(4));
181 assert_eq!(Amount::from_msat_ceil(3999), Amount::from_sat(4));
182
183 assert_eq!(Amount::from_msat_floor(3000), Amount::from_sat(3));
184 assert_eq!(Amount::from_msat_floor(3001), Amount::from_sat(3));
185 assert_eq!(Amount::from_msat_floor(3999), Amount::from_sat(3));
186 }
187
188 #[test]
189 fn fee_rate_from_amount_per_kvb() {
190 assert_eq!(FeeRate::from_amount_per_kvb_ceil(Amount::from_sat(1_000)),
191 FeeRate::from_sat_per_kwu(250)
192 );
193 assert_eq!(FeeRate::from_amount_per_kvb_ceil(Amount::from_sat(7_372)),
194 FeeRate::from_sat_per_kwu(1_843)
195 );
196 assert_eq!(FeeRate::from_amount_per_kvb_ceil(Amount::from_sat(238)),
197 FeeRate::from_sat_per_kwu(60) );
199 assert_eq!(FeeRate::from_amount_per_kvb_ceil(Amount::from_sat(15_775)),
200 FeeRate::from_sat_per_kwu(3_944) );
202 assert_eq!(FeeRate::from_amount_per_kvb_ceil(Amount::from_sat(10_125)),
203 FeeRate::from_sat_per_kwu(2_532) );
205 }
206
207 #[test]
208 fn fee_rate_from_amount_and_weight() {
209 assert_eq!(FeeRate::from_amount_and_weight_ceil(
210 Amount::from_sat(1_000), Weight::from_wu(0),
211 ),
212 None );
214 assert_eq!(FeeRate::from_amount_and_weight_ceil(
215 Amount::from_sat(u64::MAX / 2), Weight::from_wu(1),
216 ),
217 None );
219 assert_eq!(FeeRate::from_amount_and_weight_ceil(
220 Amount::from_sat(0), Weight::from_wu(1_000),
221 ),
222 Some(FeeRate::ZERO)
223 );
224 assert_eq!(FeeRate::from_amount_and_weight_ceil(
225 Amount::from_sat(500), Weight::from_wu(250)
226 ),
227 Some(FeeRate::from_sat_per_kwu(2_000))
228 );
229 assert_eq!(FeeRate::from_amount_and_weight_ceil(
230 Amount::from_sat(100), Weight::from_wu(1000)
231 ),
232 Some(FeeRate::from_sat_per_kwu(100))
233 );
234 assert_eq!(FeeRate::from_amount_and_weight_ceil(
235 Amount::from_sat(10_000), Weight::from_wu(327)
236 ),
237 Some(FeeRate::from_sat_per_kwu(30_582)) );
239 assert_eq!(FeeRate::from_amount_and_weight_ceil(
240 Amount::from_sat(10_000), Weight::from_wu(256)
241 ),
242 Some(FeeRate::from_sat_per_kwu(39_063)) );
244 assert_eq!(FeeRate::from_amount_and_weight_ceil(
245 Amount::from_sat(10_000), Weight::from_wu(2_588)
246 ),
247 Some(FeeRate::from_sat_per_kwu(3_864)) );
249 }
250
251 #[test]
252 fn fee_rate_from_sat_per_kvb() {
253 assert_eq!(FeeRate::from_sat_per_kvb_ceil(1_000),
254 FeeRate::from_sat_per_kwu(250)
255 );
256 assert_eq!(FeeRate::from_sat_per_kvb_ceil(7_372),
257 FeeRate::from_sat_per_kwu(1_843)
258 );
259 assert_eq!(FeeRate::from_sat_per_kvb_ceil(238),
260 FeeRate::from_sat_per_kwu(60) );
262 assert_eq!(FeeRate::from_sat_per_kvb_ceil(15_775),
263 FeeRate::from_sat_per_kwu(3_944) );
265 assert_eq!(FeeRate::from_sat_per_kvb_ceil(10_125),
266 FeeRate::from_sat_per_kwu(2_532) );
268 }
269
270 #[test]
271 fn fee_rate_from_sat_per_vb_decimal_checked() {
272 assert_eq!(FeeRate::from_sat_per_vb_decimal_checked_ceil(-1.0), None);
273 assert_eq!(FeeRate::from_sat_per_vb_decimal_checked_ceil(-15_4921.0), None);
274
275 assert_eq!(FeeRate::from_sat_per_vb_decimal_checked_ceil(1.0),
276 Some(FeeRate::from_sat_per_kwu(250))
277 );
278 assert_eq!(FeeRate::from_sat_per_vb_decimal_checked_ceil(7.372),
279 Some(FeeRate::from_sat_per_kwu(1_843))
280 );
281 assert_eq!(FeeRate::from_sat_per_vb_decimal_checked_ceil(0.238),
282 Some(FeeRate::from_sat_per_kwu(60)) );
284 assert_eq!(FeeRate::from_sat_per_vb_decimal_checked_ceil(15.775),
285 Some(FeeRate::from_sat_per_kwu(3_944)) );
287 assert_eq!(FeeRate::from_sat_per_vb_decimal_checked_ceil(10.12452),
288 Some(FeeRate::from_sat_per_kwu(2_532)) );
290 }
291
292 #[test]
293 fn fee_rate_to_btc_per_kvb() {
294 assert_eq!(FeeRate::from_sat_per_kwu(250).to_btc_per_kvb(), "0.00001");
295 assert_eq!(FeeRate::from_sat_per_kwu(1_843).to_btc_per_kvb(), "0.00007372");
296 assert_eq!(FeeRate::from_sat_per_kwu(60).to_btc_per_kvb(), "0.0000024");
297 assert_eq!(FeeRate::from_sat_per_kwu(3_944).to_btc_per_kvb(), "0.00015776");
298 assert_eq!(FeeRate::from_sat_per_kwu(2_532).to_btc_per_kvb(), "0.00010128");
299 }
300
301 #[test]
302 fn fee_rate_to_sat_per_kvb() {
303 assert_eq!(FeeRate::from_sat_per_kwu(250).to_sat_per_kvb(), 1_000);
304 assert_eq!(FeeRate::from_sat_per_kwu(1_843).to_sat_per_kvb(), 7_372);
305 assert_eq!(FeeRate::from_sat_per_kwu(60).to_sat_per_kvb(), 240);
306 assert_eq!(FeeRate::from_sat_per_kwu(3_944).to_sat_per_kvb(), 15_776);
307 assert_eq!(FeeRate::from_sat_per_kwu(2_532).to_sat_per_kvb(), 10_128);
308 }
309}