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