lightning/ln/funding.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//! Types pertaining to funding channels.
11
12use alloc::vec::Vec;
13
14use bitcoin::{Amount, ScriptBuf, SignedAmount, TxOut};
15use bitcoin::{Script, Sequence, Transaction, Weight};
16
17use crate::events::bump_transaction::Utxo;
18use crate::ln::chan_utils::EMPTY_SCRIPT_SIG_WEIGHT;
19use crate::sign::{P2TR_KEY_PATH_WITNESS_WEIGHT, P2WPKH_WITNESS_WEIGHT};
20
21/// The components of a splice's funding transaction that are contributed by one party.
22#[derive(Debug, Clone)]
23pub enum SpliceContribution {
24 /// When funds are added to a channel.
25 SpliceIn {
26 /// The amount to contribute to the splice.
27 value: Amount,
28
29 /// The inputs included in the splice's funding transaction to meet the contributed amount
30 /// plus fees. Any excess amount will be sent to a change output.
31 inputs: Vec<FundingTxInput>,
32
33 /// An optional change output script. This will be used if needed or, when not set,
34 /// generated using [`SignerProvider::get_destination_script`].
35 ///
36 /// [`SignerProvider::get_destination_script`]: crate::sign::SignerProvider::get_destination_script
37 change_script: Option<ScriptBuf>,
38 },
39 /// When funds are removed from a channel.
40 SpliceOut {
41 /// The outputs to include in the splice's funding transaction. The total value of all
42 /// outputs plus fees will be the amount that is removed.
43 outputs: Vec<TxOut>,
44 },
45}
46
47impl SpliceContribution {
48 pub(super) fn value(&self) -> SignedAmount {
49 match self {
50 SpliceContribution::SpliceIn { value, .. } => {
51 value.to_signed().unwrap_or(SignedAmount::MAX)
52 },
53 SpliceContribution::SpliceOut { outputs } => {
54 let value_removed = outputs
55 .iter()
56 .map(|txout| txout.value)
57 .sum::<Amount>()
58 .to_signed()
59 .unwrap_or(SignedAmount::MAX);
60 -value_removed
61 },
62 }
63 }
64
65 pub(super) fn inputs(&self) -> &[FundingTxInput] {
66 match self {
67 SpliceContribution::SpliceIn { inputs, .. } => &inputs[..],
68 SpliceContribution::SpliceOut { .. } => &[],
69 }
70 }
71
72 pub(super) fn outputs(&self) -> &[TxOut] {
73 match self {
74 SpliceContribution::SpliceIn { .. } => &[],
75 SpliceContribution::SpliceOut { outputs } => &outputs[..],
76 }
77 }
78
79 pub(super) fn into_tx_parts(self) -> (Vec<FundingTxInput>, Vec<TxOut>, Option<ScriptBuf>) {
80 match self {
81 SpliceContribution::SpliceIn { inputs, change_script, .. } => {
82 (inputs, vec![], change_script)
83 },
84 SpliceContribution::SpliceOut { outputs } => (vec![], outputs, None),
85 }
86 }
87}
88
89/// An input to contribute to a channel's funding transaction either when using the v2 channel
90/// establishment protocol or when splicing.
91#[derive(Debug, Clone)]
92pub struct FundingTxInput {
93 /// The unspent [`TxOut`] that the input spends.
94 ///
95 /// [`TxOut`]: bitcoin::TxOut
96 pub(super) utxo: Utxo,
97
98 /// The sequence number to use in the [`TxIn`].
99 ///
100 /// [`TxIn`]: bitcoin::TxIn
101 pub(super) sequence: Sequence,
102
103 /// The transaction containing the unspent [`TxOut`] referenced by [`utxo`].
104 ///
105 /// [`TxOut`]: bitcoin::TxOut
106 /// [`utxo`]: Self::utxo
107 pub(super) prevtx: Transaction,
108}
109
110impl_writeable_tlv_based!(FundingTxInput, {
111 (1, utxo, required),
112 (3, sequence, required),
113 (5, prevtx, required),
114});
115
116impl FundingTxInput {
117 fn new<F: FnOnce(&bitcoin::Script) -> bool>(
118 prevtx: Transaction, vout: u32, witness_weight: Weight, script_filter: F,
119 ) -> Result<Self, ()> {
120 Ok(FundingTxInput {
121 utxo: Utxo {
122 outpoint: bitcoin::OutPoint { txid: prevtx.compute_txid(), vout },
123 output: prevtx
124 .output
125 .get(vout as usize)
126 .filter(|output| script_filter(&output.script_pubkey))
127 .ok_or(())?
128 .clone(),
129 satisfaction_weight: EMPTY_SCRIPT_SIG_WEIGHT + witness_weight.to_wu(),
130 },
131 sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
132 prevtx,
133 })
134 }
135
136 /// Creates an input spending a P2WPKH output from the given `prevtx` at index `vout`.
137 ///
138 /// Uses [`Sequence::ENABLE_RBF_NO_LOCKTIME`] as the [`TxIn::sequence`], which can be overridden
139 /// by [`set_sequence`].
140 ///
141 /// Returns `Err` if no such output exists in `prevtx` at index `vout`.
142 ///
143 /// [`TxIn::sequence`]: bitcoin::TxIn::sequence
144 /// [`set_sequence`]: Self::set_sequence
145 pub fn new_p2wpkh(prevtx: Transaction, vout: u32) -> Result<Self, ()> {
146 let witness_weight = Weight::from_wu(P2WPKH_WITNESS_WEIGHT)
147 - if cfg!(feature = "grind_signatures") {
148 // Guarantees a low R signature
149 Weight::from_wu(1)
150 } else {
151 Weight::ZERO
152 };
153 FundingTxInput::new(prevtx, vout, witness_weight, Script::is_p2wpkh)
154 }
155
156 /// Creates an input spending a P2WSH output from the given `prevtx` at index `vout`.
157 ///
158 /// Requires passing the weight of witness needed to satisfy the output's script.
159 ///
160 /// Uses [`Sequence::ENABLE_RBF_NO_LOCKTIME`] as the [`TxIn::sequence`], which can be overridden
161 /// by [`set_sequence`].
162 ///
163 /// Returns `Err` if no such output exists in `prevtx` at index `vout`.
164 ///
165 /// [`TxIn::sequence`]: bitcoin::TxIn::sequence
166 /// [`set_sequence`]: Self::set_sequence
167 pub fn new_p2wsh(prevtx: Transaction, vout: u32, witness_weight: Weight) -> Result<Self, ()> {
168 FundingTxInput::new(prevtx, vout, witness_weight, Script::is_p2wsh)
169 }
170
171 /// Creates an input spending a P2TR output from the given `prevtx` at index `vout`.
172 ///
173 /// This is meant for inputs spending a taproot output using the key path. See
174 /// [`new_p2tr_script_spend`] for when spending using a script path.
175 ///
176 /// Uses [`Sequence::ENABLE_RBF_NO_LOCKTIME`] as the [`TxIn::sequence`], which can be overridden
177 /// by [`set_sequence`].
178 ///
179 /// Returns `Err` if no such output exists in `prevtx` at index `vout`.
180 ///
181 /// [`new_p2tr_script_spend`]: Self::new_p2tr_script_spend
182 ///
183 /// [`TxIn::sequence`]: bitcoin::TxIn::sequence
184 /// [`set_sequence`]: Self::set_sequence
185 pub fn new_p2tr_key_spend(prevtx: Transaction, vout: u32) -> Result<Self, ()> {
186 let witness_weight = Weight::from_wu(P2TR_KEY_PATH_WITNESS_WEIGHT);
187 FundingTxInput::new(prevtx, vout, witness_weight, Script::is_p2tr)
188 }
189
190 /// Creates an input spending a P2TR output from the given `prevtx` at index `vout`.
191 ///
192 /// Requires passing the weight of witness needed to satisfy a script path of the taproot
193 /// output. See [`new_p2tr_key_spend`] for when spending using the key path.
194 ///
195 /// Uses [`Sequence::ENABLE_RBF_NO_LOCKTIME`] as the [`TxIn::sequence`], which can be overridden
196 /// by [`set_sequence`].
197 ///
198 /// Returns `Err` if no such output exists in `prevtx` at index `vout`.
199 ///
200 /// [`new_p2tr_key_spend`]: Self::new_p2tr_key_spend
201 ///
202 /// [`TxIn::sequence`]: bitcoin::TxIn::sequence
203 /// [`set_sequence`]: Self::set_sequence
204 pub fn new_p2tr_script_spend(
205 prevtx: Transaction, vout: u32, witness_weight: Weight,
206 ) -> Result<Self, ()> {
207 FundingTxInput::new(prevtx, vout, witness_weight, Script::is_p2tr)
208 }
209
210 #[cfg(test)]
211 pub(crate) fn new_p2pkh(prevtx: Transaction, vout: u32) -> Result<Self, ()> {
212 FundingTxInput::new(prevtx, vout, Weight::ZERO, Script::is_p2pkh)
213 }
214
215 /// The outpoint of the UTXO being spent.
216 pub fn outpoint(&self) -> bitcoin::OutPoint {
217 self.utxo.outpoint
218 }
219
220 /// The sequence number to use in the [`TxIn`].
221 ///
222 /// [`TxIn`]: bitcoin::TxIn
223 pub fn sequence(&self) -> Sequence {
224 self.sequence
225 }
226
227 /// Sets the sequence number to use in the [`TxIn`].
228 ///
229 /// [`TxIn`]: bitcoin::TxIn
230 pub fn set_sequence(&mut self, sequence: Sequence) {
231 self.sequence = sequence;
232 }
233
234 /// Converts the [`FundingTxInput`] into a [`Utxo`] for coin selection.
235 pub fn into_utxo(self) -> Utxo {
236 self.utxo
237 }
238}