lightning/events/bump_transaction/sync.rs
1// This file is Copyright its original authors, visible in version control
2// history.
3//
4// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7// You may not use this file except in accordance with one or both of these
8// licenses.
9
10//! This module provides synchronous wrappers around [`BumpTransactionEventHandler`] and related types.
11
12use core::future::Future;
13use core::ops::Deref;
14use core::task;
15
16use crate::chain::chaininterface::BroadcasterInterface;
17use crate::chain::ClaimId;
18use crate::prelude::*;
19use crate::sign::SignerProvider;
20use crate::util::async_poll::{dummy_waker, AsyncResult, MaybeSend, MaybeSync};
21use crate::util::logger::Logger;
22
23use bitcoin::{Psbt, ScriptBuf, Transaction, TxOut};
24
25use super::BumpTransactionEvent;
26use super::{
27 BumpTransactionEventHandler, CoinSelection, CoinSelectionSource, Input, Utxo, Wallet,
28 WalletSource,
29};
30
31/// An alternative to [`CoinSelectionSourceSync`] that can be implemented and used along
32/// [`WalletSync`] to provide a default implementation to [`CoinSelectionSourceSync`].
33///
34/// For an asynchronous version of this trait, see [`WalletSource`].
35// Note that updates to documentation on this trait should be copied to the asynchronous version.
36pub trait WalletSourceSync {
37 /// Returns all UTXOs, with at least 1 confirmation each, that are available to spend.
38 fn list_confirmed_utxos(&self) -> Result<Vec<Utxo>, ()>;
39 /// Returns a script to use for change above dust resulting from a successful coin selection
40 /// attempt.
41 fn get_change_script(&self) -> Result<ScriptBuf, ()>;
42 /// Signs and provides the full [`TxIn::script_sig`] and [`TxIn::witness`] for all inputs within
43 /// the transaction known to the wallet (i.e., any provided via
44 /// [`WalletSource::list_confirmed_utxos`]).
45 ///
46 /// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
47 /// unsigned transaction and then sign it with your wallet.
48 ///
49 /// [`TxIn::script_sig`]: bitcoin::TxIn::script_sig
50 /// [`TxIn::witness`]: bitcoin::TxIn::witness
51 fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()>;
52}
53
54pub(crate) struct WalletSourceSyncWrapper<T: Deref>(T)
55where
56 T::Target: WalletSourceSync;
57
58// Implement `Deref` directly on WalletSourceSyncWrapper so that it can be used directly
59// below, rather than via a wrapper.
60impl<T: Deref> Deref for WalletSourceSyncWrapper<T>
61where
62 T::Target: WalletSourceSync,
63{
64 type Target = Self;
65 fn deref(&self) -> &Self {
66 self
67 }
68}
69
70impl<T: Deref> WalletSource for WalletSourceSyncWrapper<T>
71where
72 T::Target: WalletSourceSync,
73{
74 fn list_confirmed_utxos<'a>(&'a self) -> AsyncResult<'a, Vec<Utxo>, ()> {
75 let utxos = self.0.list_confirmed_utxos();
76 Box::pin(async move { utxos })
77 }
78
79 fn get_change_script<'a>(&'a self) -> AsyncResult<'a, ScriptBuf, ()> {
80 let script = self.0.get_change_script();
81 Box::pin(async move { script })
82 }
83
84 fn sign_psbt<'a>(&'a self, psbt: Psbt) -> AsyncResult<'a, Transaction, ()> {
85 let signed_psbt = self.0.sign_psbt(psbt);
86 Box::pin(async move { signed_psbt })
87 }
88}
89
90/// A wrapper over [`WalletSourceSync`] that implements [`CoinSelectionSourceSync`] by preferring
91/// UTXOs that would avoid conflicting double spends. If not enough UTXOs are available to do so,
92/// conflicting double spends may happen.
93///
94/// For an asynchronous version of this wrapper, see [`Wallet`].
95// Note that updates to documentation on this struct should be copied to the asynchronous version.
96pub struct WalletSync<W: Deref + MaybeSync + MaybeSend, L: Deref + MaybeSync + MaybeSend>
97where
98 W::Target: WalletSourceSync + MaybeSend,
99 L::Target: Logger + MaybeSend,
100{
101 wallet: Wallet<WalletSourceSyncWrapper<W>, L>,
102}
103
104impl<W: Deref + MaybeSync + MaybeSend, L: Deref + MaybeSync + MaybeSend> WalletSync<W, L>
105where
106 W::Target: WalletSourceSync + MaybeSend,
107 L::Target: Logger + MaybeSend,
108{
109 /// Constructs a new [`WalletSync`] instance.
110 pub fn new(source: W, logger: L) -> Self {
111 Self { wallet: Wallet::new(WalletSourceSyncWrapper(source), logger) }
112 }
113}
114
115impl<W: Deref + MaybeSync + MaybeSend, L: Deref + MaybeSync + MaybeSend> CoinSelectionSourceSync
116 for WalletSync<W, L>
117where
118 W::Target: WalletSourceSync + MaybeSend + MaybeSync,
119 L::Target: Logger + MaybeSend + MaybeSync,
120{
121 fn select_confirmed_utxos(
122 &self, claim_id: ClaimId, must_spend: Vec<Input>, must_pay_to: &[TxOut],
123 target_feerate_sat_per_1000_weight: u32, max_tx_weight: u64,
124 ) -> Result<CoinSelection, ()> {
125 let mut fut = self.wallet.select_confirmed_utxos(
126 claim_id,
127 must_spend,
128 must_pay_to,
129 target_feerate_sat_per_1000_weight,
130 max_tx_weight,
131 );
132 let mut waker = dummy_waker();
133 let mut ctx = task::Context::from_waker(&mut waker);
134 match fut.as_mut().poll(&mut ctx) {
135 task::Poll::Ready(result) => result,
136 task::Poll::Pending => {
137 unreachable!(
138 "Wallet::select_confirmed_utxos should not be pending in a sync context"
139 );
140 },
141 }
142 }
143
144 fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()> {
145 let mut fut = self.wallet.sign_psbt(psbt);
146 let mut waker = dummy_waker();
147 let mut ctx = task::Context::from_waker(&mut waker);
148 match fut.as_mut().poll(&mut ctx) {
149 task::Poll::Ready(result) => result,
150 task::Poll::Pending => {
151 unreachable!("Wallet::sign_psbt should not be pending in a sync context");
152 },
153 }
154 }
155}
156
157/// An abstraction over a bitcoin wallet that can perform coin selection over a set of UTXOs and can
158/// sign for them. The coin selection method aims to mimic Bitcoin Core's `fundrawtransaction` RPC,
159/// which most wallets should be able to satisfy. Otherwise, consider implementing
160/// [`WalletSourceSync`], which can provide a default implementation of this trait when used with
161/// [`WalletSync`].
162///
163/// For an asynchronous version of this trait, see [`CoinSelectionSource`].
164// Note that updates to documentation on this trait should be copied to the asynchronous version.
165pub trait CoinSelectionSourceSync {
166 /// Performs coin selection of a set of UTXOs, with at least 1 confirmation each, that are
167 /// available to spend. Implementations are free to pick their coin selection algorithm of
168 /// choice, as long as the following requirements are met:
169 ///
170 /// 1. `must_spend` contains a set of [`Input`]s that must be included in the transaction
171 /// throughout coin selection, but must not be returned as part of the result.
172 /// 2. `must_pay_to` contains a set of [`TxOut`]s that must be included in the transaction
173 /// throughout coin selection. In some cases, like when funding an anchor transaction, this
174 /// set is empty. Implementations should ensure they handle this correctly on their end,
175 /// e.g., Bitcoin Core's `fundrawtransaction` RPC requires at least one output to be
176 /// provided, in which case a zero-value empty OP_RETURN output can be used instead.
177 /// 3. Enough inputs must be selected/contributed for the resulting transaction (including the
178 /// inputs and outputs noted above) to meet `target_feerate_sat_per_1000_weight`.
179 /// 4. The final transaction must have a weight smaller than `max_tx_weight`; if this
180 /// constraint can't be met, return an `Err`. In the case of counterparty-signed HTLC
181 /// transactions, we will remove a chunk of HTLCs and try your algorithm again. As for
182 /// anchor transactions, we will try your coin selection again with the same input-output
183 /// set when you call [`ChannelMonitor::rebroadcast_pending_claims`], as anchor transactions
184 /// cannot be downsized.
185 ///
186 /// Implementations must take note that [`Input::satisfaction_weight`] only tracks the weight of
187 /// the input's `script_sig` and `witness`. Some wallets, like Bitcoin Core's, may require
188 /// providing the full input weight. Failing to do so may lead to underestimating fee bumps and
189 /// delaying block inclusion.
190 ///
191 /// The `claim_id` must map to the set of external UTXOs assigned to the claim, such that they
192 /// can be re-used within new fee-bumped iterations of the original claiming transaction,
193 /// ensuring that claims don't double spend each other. If a specific `claim_id` has never had a
194 /// transaction associated with it, and all of the available UTXOs have already been assigned to
195 /// other claims, implementations must be willing to double spend their UTXOs. The choice of
196 /// which UTXOs to double spend is left to the implementation, but it must strive to keep the
197 /// set of other claims being double spent to a minimum.
198 ///
199 /// [`ChannelMonitor::rebroadcast_pending_claims`]: crate::chain::channelmonitor::ChannelMonitor::rebroadcast_pending_claims
200 fn select_confirmed_utxos(
201 &self, claim_id: ClaimId, must_spend: Vec<Input>, must_pay_to: &[TxOut],
202 target_feerate_sat_per_1000_weight: u32, max_tx_weight: u64,
203 ) -> Result<CoinSelection, ()>;
204
205 /// Signs and provides the full witness for all inputs within the transaction known to the
206 /// trait (i.e., any provided via [`CoinSelectionSourceSync::select_confirmed_utxos`]).
207 ///
208 /// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
209 /// unsigned transaction and then sign it with your wallet.
210 fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()>;
211}
212
213struct CoinSelectionSourceSyncWrapper<T: Deref>(T)
214where
215 T::Target: CoinSelectionSourceSync;
216
217// Implement `Deref` directly on CoinSelectionSourceSyncWrapper so that it can be used directly
218// below, rather than via a wrapper.
219impl<T: Deref> Deref for CoinSelectionSourceSyncWrapper<T>
220where
221 T::Target: CoinSelectionSourceSync,
222{
223 type Target = Self;
224 fn deref(&self) -> &Self {
225 self
226 }
227}
228
229impl<T: Deref> CoinSelectionSource for CoinSelectionSourceSyncWrapper<T>
230where
231 T::Target: CoinSelectionSourceSync,
232{
233 fn select_confirmed_utxos<'a>(
234 &'a self, claim_id: ClaimId, must_spend: Vec<Input>, must_pay_to: &'a [TxOut],
235 target_feerate_sat_per_1000_weight: u32, max_tx_weight: u64,
236 ) -> AsyncResult<'a, CoinSelection, ()> {
237 let coins = self.0.select_confirmed_utxos(
238 claim_id,
239 must_spend,
240 must_pay_to,
241 target_feerate_sat_per_1000_weight,
242 max_tx_weight,
243 );
244 Box::pin(async move { coins })
245 }
246
247 fn sign_psbt<'a>(&'a self, psbt: Psbt) -> AsyncResult<'a, Transaction, ()> {
248 let psbt = self.0.sign_psbt(psbt);
249 Box::pin(async move { psbt })
250 }
251}
252
253/// A handler for [`Event::BumpTransaction`] events that sources confirmed UTXOs from a
254/// [`CoinSelectionSourceSync`] to fee bump transactions via Child-Pays-For-Parent (CPFP) or
255/// Replace-By-Fee (RBF).
256///
257/// For an asynchronous version of this handler, see [`BumpTransactionEventHandler`].
258///
259/// [`Event::BumpTransaction`]: crate::events::Event::BumpTransaction
260// Note that updates to documentation on this struct should be copied to the synchronous version.
261pub struct BumpTransactionEventHandlerSync<B: Deref, C: Deref, SP: Deref, L: Deref>
262where
263 B::Target: BroadcasterInterface,
264 C::Target: CoinSelectionSourceSync,
265 SP::Target: SignerProvider,
266 L::Target: Logger,
267{
268 bump_transaction_event_handler:
269 BumpTransactionEventHandler<B, CoinSelectionSourceSyncWrapper<C>, SP, L>,
270}
271
272impl<B: Deref, C: Deref, SP: Deref, L: Deref> BumpTransactionEventHandlerSync<B, C, SP, L>
273where
274 B::Target: BroadcasterInterface,
275 C::Target: CoinSelectionSourceSync,
276 SP::Target: SignerProvider,
277 L::Target: Logger,
278{
279 /// Constructs a new instance of [`BumpTransactionEventHandlerSync`].
280 pub fn new(broadcaster: B, utxo_source: C, signer_provider: SP, logger: L) -> Self {
281 let bump_transaction_event_handler = BumpTransactionEventHandler::new(
282 broadcaster,
283 CoinSelectionSourceSyncWrapper(utxo_source),
284 signer_provider,
285 logger,
286 );
287 Self { bump_transaction_event_handler }
288 }
289
290 /// Handles all variants of [`BumpTransactionEvent`].
291 pub fn handle_event(&self, event: &BumpTransactionEvent) {
292 let mut fut = Box::pin(self.bump_transaction_event_handler.handle_event(event));
293 let mut waker = dummy_waker();
294 let mut ctx = task::Context::from_waker(&mut waker);
295 match fut.as_mut().poll(&mut ctx) {
296 task::Poll::Ready(result) => result,
297 task::Poll::Pending => {
298 // In a sync context, we can't wait for the future to complete.
299 unreachable!("BumpTransactionEventHandlerSync::handle_event should not be pending in a sync context");
300 },
301 }
302 }
303}