Skip to content

Commit 01bf037

Browse files
committed
Replace funding input tuple with struct
The funding inputs used for splicing and v2 channel establishment are passed as a tuple of txin, prevtx, and witness weight. Add a struct so that the items included can be better documented.
1 parent 2bcba88 commit 01bf037

File tree

5 files changed

+186
-99
lines changed

5 files changed

+186
-99
lines changed

lightning/src/ln/channel.rs

Lines changed: 46 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use bitcoin::consensus::encode;
1313
use bitcoin::constants::ChainHash;
1414
use bitcoin::script::{Builder, Script, ScriptBuf, WScriptHash};
1515
use bitcoin::sighash::EcdsaSighashType;
16-
use bitcoin::transaction::{Transaction, TxIn, TxOut};
16+
use bitcoin::transaction::{Transaction, TxOut};
1717
use bitcoin::{Weight, Witness};
1818

1919
use bitcoin::hash_types::{BlockHash, Txid};
@@ -26,7 +26,7 @@ use bitcoin::secp256k1::{ecdsa::Signature, Secp256k1};
2626
use bitcoin::secp256k1::{PublicKey, SecretKey};
2727
#[cfg(splicing)]
2828
use bitcoin::Sequence;
29-
use bitcoin::{secp256k1, sighash};
29+
use bitcoin::{secp256k1, sighash, TxIn};
3030

3131
use crate::chain::chaininterface::{
3232
fee_for_weight, ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator,
@@ -53,7 +53,7 @@ use crate::ln::channel_state::{
5353
OutboundHTLCDetails, OutboundHTLCStateDetails,
5454
};
5555
use crate::ln::channelmanager::{
56-
self, FundingConfirmedMessage, HTLCFailureMsg, HTLCSource, OpenChannelMessage,
56+
self, FundingConfirmedMessage, FundingTxInput, HTLCFailureMsg, HTLCSource, OpenChannelMessage,
5757
PaymentClaimDetails, PendingHTLCInfo, PendingHTLCStatus, RAACommitmentOrder, SentHTLCId,
5858
BREAKDOWN_TIMEOUT, MAX_LOCAL_BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA,
5959
};
@@ -5915,10 +5915,12 @@ fn estimate_v2_funding_transaction_fee(
59155915
#[cfg(splicing)]
59165916
#[rustfmt::skip]
59175917
fn check_v2_funding_inputs_sufficient(
5918-
contribution_amount: i64, funding_inputs: &[(TxIn, Transaction, Weight)], is_initiator: bool,
5918+
contribution_amount: i64, funding_inputs: &[FundingTxInput], is_initiator: bool,
59195919
is_splice: bool, funding_feerate_sat_per_1000_weight: u32,
59205920
) -> Result<u64, ChannelError> {
5921-
let mut total_input_witness_weight = Weight::from_wu(funding_inputs.iter().map(|(_, _, w)| w.to_wu()).sum());
5921+
let mut total_input_witness_weight = Weight::from_wu(
5922+
funding_inputs.iter().map(|input| input.utxo.satisfaction_weight).sum(),
5923+
);
59225924
let mut funding_inputs_len = funding_inputs.len();
59235925
if is_initiator && is_splice {
59245926
// consider the weight of the input and witness needed for spending the old funding transaction
@@ -5928,15 +5930,8 @@ fn check_v2_funding_inputs_sufficient(
59285930
let estimated_fee = estimate_v2_funding_transaction_fee(is_initiator, funding_inputs_len, total_input_witness_weight, funding_feerate_sat_per_1000_weight);
59295931

59305932
let mut total_input_sats = 0u64;
5931-
for (idx, input) in funding_inputs.iter().enumerate() {
5932-
if let Some(output) = input.1.output.get(input.0.previous_output.vout as usize) {
5933-
total_input_sats = total_input_sats.saturating_add(output.value.to_sat());
5934-
} else {
5935-
return Err(ChannelError::Warn(format!(
5936-
"Transaction with txid {} does not have an output with vout of {} corresponding to TxIn at funding_inputs[{}]",
5937-
input.1.compute_txid(), input.0.previous_output.vout, idx
5938-
)));
5939-
}
5933+
for FundingTxInput { utxo, .. } in funding_inputs.iter() {
5934+
total_input_sats = total_input_sats.saturating_add(utxo.output.value.to_sat());
59405935
}
59415936

59425937
// If the inputs are enough to cover intended contribution amount, with fees even when
@@ -5978,7 +5973,7 @@ pub(super) struct FundingNegotiationContext {
59785973
pub shared_funding_input: Option<SharedOwnedInput>,
59795974
/// The funding inputs we will be contributing to the channel.
59805975
#[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
5981-
pub our_funding_inputs: Vec<(TxIn, Transaction, Weight)>,
5976+
pub our_funding_inputs: Vec<FundingTxInput>,
59825977
/// The change output script. This will be used if needed or -- if not set -- generated using
59835978
/// `SignerProvider::get_destination_script`.
59845979
#[allow(dead_code)] // TODO(splicing): Remove once splicing is enabled.
@@ -6050,8 +6045,13 @@ impl FundingNegotiationContext {
60506045
}
60516046
}
60526047

6053-
let funding_inputs =
6054-
self.our_funding_inputs.into_iter().map(|(txin, tx, _)| (txin, tx)).collect();
6048+
let funding_inputs = self
6049+
.our_funding_inputs
6050+
.into_iter()
6051+
.map(|FundingTxInput { utxo, sequence, prevtx }| {
6052+
(TxIn { previous_output: utxo.outpoint, sequence, ..Default::default() }, prevtx)
6053+
})
6054+
.collect();
60556055

60566056
let constructor_args = InteractiveTxConstructorArgs {
60576057
entropy_source,
@@ -10604,9 +10604,8 @@ where
1060410604
/// generated by `SignerProvider::get_destination_script`.
1060510605
#[cfg(splicing)]
1060610606
pub fn splice_channel(
10607-
&mut self, our_funding_contribution_satoshis: i64,
10608-
our_funding_inputs: Vec<(TxIn, Transaction, Weight)>, change_script: Option<ScriptBuf>,
10609-
funding_feerate_per_kw: u32, locktime: u32,
10607+
&mut self, our_funding_contribution_satoshis: i64, our_funding_inputs: Vec<FundingTxInput>,
10608+
change_script: Option<ScriptBuf>, funding_feerate_per_kw: u32, locktime: u32,
1061010609
) -> Result<msgs::SpliceInit, APIError> {
1061110610
// Check if a splice has been initiated already.
1061210611
// Note: only a single outstanding splice is supported (per spec)
@@ -10672,7 +10671,7 @@ where
1067210671
),
1067310672
})?;
1067410673

10675-
for (txin, tx, _) in our_funding_inputs.iter() {
10674+
for FundingTxInput { utxo, prevtx, .. } in our_funding_inputs.iter() {
1067610675
const MESSAGE_TEMPLATE: msgs::TxAddInput = msgs::TxAddInput {
1067710676
channel_id: ChannelId([0; 32]),
1067810677
serial_id: 0,
@@ -10681,12 +10680,12 @@ where
1068110680
sequence: 0,
1068210681
shared_input_txid: None,
1068310682
};
10684-
let message_len = MESSAGE_TEMPLATE.serialized_length() + tx.serialized_length();
10683+
let message_len = MESSAGE_TEMPLATE.serialized_length() + prevtx.serialized_length();
1068510684
if message_len > LN_MAX_MSG_LEN {
1068610685
return Err(APIError::APIMisuseError {
1068710686
err: format!(
1068810687
"Funding input references a prevtx that is too large for tx_add_input: {}",
10689-
txin.previous_output,
10688+
utxo.outpoint,
1069010689
),
1069110690
});
1069210691
}
@@ -12468,7 +12467,7 @@ where
1246812467
pub fn new_outbound<ES: Deref, F: Deref, L: Deref>(
1246912468
fee_estimator: &LowerBoundedFeeEstimator<F>, entropy_source: &ES, signer_provider: &SP,
1247012469
counterparty_node_id: PublicKey, their_features: &InitFeatures, funding_satoshis: u64,
12471-
funding_inputs: Vec<(TxIn, Transaction, Weight)>, user_id: u128, config: &UserConfig,
12470+
funding_inputs: Vec<FundingTxInput>, user_id: u128, config: &UserConfig,
1247212471
current_chain_height: u32, outbound_scid_alias: u64, funding_confirmation_target: ConfirmationTarget,
1247312472
logger: L,
1247412473
) -> Result<Self, APIError>
@@ -12682,8 +12681,12 @@ where
1268212681
value: Amount::from_sat(funding.get_value_satoshis()),
1268312682
script_pubkey: funding.get_funding_redeemscript().to_p2wsh(),
1268412683
};
12685-
let inputs_to_contribute =
12686-
our_funding_inputs.into_iter().map(|(txin, tx, _)| (txin, tx)).collect();
12684+
let inputs_to_contribute = our_funding_inputs
12685+
.into_iter()
12686+
.map(|FundingTxInput { utxo, sequence, prevtx }| {
12687+
(TxIn { previous_output: utxo.outpoint, sequence, ..Default::default() }, prevtx)
12688+
})
12689+
.collect();
1268712690

1268812691
let interactive_tx_constructor = Some(InteractiveTxConstructor::new(
1268912692
InteractiveTxConstructorArgs {
@@ -14119,6 +14122,8 @@ mod tests {
1411914122
TOTAL_BITCOIN_SUPPLY_SATOSHIS,
1412014123
};
1412114124
use crate::ln::channel_keys::{RevocationBasepoint, RevocationKey};
14125+
#[cfg(splicing)]
14126+
use crate::ln::channelmanager::FundingTxInput;
1412214127
use crate::ln::channelmanager::{self, HTLCSource, PaymentId};
1412314128
use crate::ln::msgs;
1412414129
use crate::ln::msgs::{ChannelUpdate, UnsignedChannelUpdate, MAX_VALUE_MSAT};
@@ -14151,11 +14156,9 @@ mod tests {
1415114156
use bitcoin::secp256k1::ffi::Signature as FFISignature;
1415214157
use bitcoin::secp256k1::{ecdsa::Signature, Secp256k1};
1415314158
use bitcoin::secp256k1::{PublicKey, SecretKey};
14154-
#[cfg(splicing)]
14155-
use bitcoin::transaction::TxIn;
1415614159
use bitcoin::transaction::{Transaction, TxOut, Version};
1415714160
#[cfg(splicing)]
14158-
use bitcoin::Weight;
14161+
use bitcoin::{ScriptBuf, Sequence, WPubkeyHash};
1415914162
use bitcoin::{WitnessProgram, WitnessVersion};
1416014163
use std::cmp;
1416114164

@@ -15897,19 +15900,17 @@ mod tests {
1589715900

1589815901
#[cfg(splicing)]
1589915902
#[rustfmt::skip]
15900-
fn funding_input_sats(input_value_sats: u64) -> (TxIn, Transaction, Weight) {
15901-
use crate::sign::P2WPKH_WITNESS_WEIGHT;
15902-
15903-
let input_1_prev_out = TxOut { value: Amount::from_sat(input_value_sats), script_pubkey: bitcoin::ScriptBuf::default() };
15904-
let input_1_prev_tx = Transaction {
15905-
input: vec![], output: vec![input_1_prev_out],
15906-
version: Version::TWO, lock_time: bitcoin::absolute::LockTime::ZERO,
15903+
fn funding_input_sats(input_value_sats: u64) -> FundingTxInput {
15904+
let prevout = TxOut {
15905+
value: Amount::from_sat(input_value_sats),
15906+
script_pubkey: ScriptBuf::new_p2wpkh(&WPubkeyHash::all_zeros()),
1590715907
};
15908-
let input_1_txin = TxIn {
15909-
previous_output: bitcoin::OutPoint { txid: input_1_prev_tx.compute_txid(), vout: 0 },
15910-
..Default::default()
15908+
let prevtx = Transaction {
15909+
input: vec![], output: vec![prevout],
15910+
version: Version::TWO, lock_time: bitcoin::absolute::LockTime::ZERO,
1591115911
};
15912-
(input_1_txin, input_1_prev_tx, Weight::from_wu(P2WPKH_WITNESS_WEIGHT))
15912+
15913+
FundingTxInput::new_p2wpkh(prevtx, 0, Sequence::ZERO).unwrap()
1591315914
}
1591415915

1591515916
#[cfg(splicing)]
@@ -15930,7 +15931,7 @@ mod tests {
1593015931
true,
1593115932
2000,
1593215933
).unwrap(),
15933-
2268,
15934+
2284,
1593415935
);
1593515936

1593615937
// negative case, inputs clearly insufficient
@@ -15946,13 +15947,13 @@ mod tests {
1594615947
);
1594715948
assert_eq!(
1594815949
format!("{:?}", res.err().unwrap()),
15949-
"Warn: Total input amount 100000 is lower than needed for contribution 220000, considering fees of 1730. Need more inputs.",
15950+
"Warn: Total input amount 100000 is lower than needed for contribution 220000, considering fees of 1738. Need more inputs.",
1595015951
);
1595115952
}
1595215953

1595315954
// barely covers
1595415955
{
15955-
let expected_fee: u64 = 2268;
15956+
let expected_fee: u64 = 2284;
1595615957
assert_eq!(
1595715958
check_v2_funding_inputs_sufficient(
1595815959
(300_000 - expected_fee - 20) as i64,
@@ -15982,13 +15983,13 @@ mod tests {
1598215983
);
1598315984
assert_eq!(
1598415985
format!("{:?}", res.err().unwrap()),
15985-
"Warn: Total input amount 300000 is lower than needed for contribution 298032, considering fees of 2495. Need more inputs.",
15986+
"Warn: Total input amount 300000 is lower than needed for contribution 298032, considering fees of 2513. Need more inputs.",
1598615987
);
1598715988
}
1598815989

1598915990
// barely covers, less fees (no extra weight, no init)
1599015991
{
15991-
let expected_fee: u64 = 1076;
15992+
let expected_fee: u64 = 1092;
1599215993
assert_eq!(
1599315994
check_v2_funding_inputs_sufficient(
1599415995
(300_000 - expected_fee - 20) as i64,

lightning/src/ln/channelmanager.rs

Lines changed: 105 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ use bitcoin::hashes::{Hash, HashEngine, HmacEngine};
3030

3131
use bitcoin::secp256k1::Secp256k1;
3232
use bitcoin::secp256k1::{PublicKey, SecretKey};
33-
use bitcoin::{secp256k1, Sequence, SignedAmount};
3433
#[cfg(splicing)]
35-
use bitcoin::{ScriptBuf, TxIn, Weight};
34+
use bitcoin::ScriptBuf;
35+
use bitcoin::{secp256k1, Sequence, SignedAmount, Weight};
3636

3737
use crate::blinded_path::message::MessageForwardNode;
3838
use crate::blinded_path::message::{AsyncPaymentsContext, OffersContext};
@@ -51,6 +51,7 @@ use crate::chain::channelmonitor::{
5151
};
5252
use crate::chain::transaction::{OutPoint, TransactionData};
5353
use crate::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Watch};
54+
use crate::events::bump_transaction::{Utxo, EMPTY_SCRIPT_SIG_WEIGHT};
5455
use crate::events::{
5556
self, ClosureReason, Event, EventHandler, EventsProvider, HTLCHandlingFailureType,
5657
InboundChannelFunds, PaymentFailureReason, ReplayEvent,
@@ -116,7 +117,10 @@ use crate::routing::router::{
116117
RouteParameters, RouteParametersConfig, Router,
117118
};
118119
use crate::sign::ecdsa::EcdsaChannelSigner;
119-
use crate::sign::{EntropySource, NodeSigner, Recipient, SignerProvider};
120+
use crate::sign::{
121+
EntropySource, NodeSigner, Recipient, SignerProvider, P2TR_KEY_PATH_WITNESS_WEIGHT,
122+
P2WPKH_WITNESS_WEIGHT,
123+
};
120124
#[cfg(any(feature = "_test_utils", test))]
121125
use crate::types::features::Bolt11InvoiceFeatures;
122126
use crate::types::features::{
@@ -200,6 +204,101 @@ pub use crate::ln::outbound_payment::{
200204
};
201205
use crate::ln::script::ShutdownScript;
202206

207+
/// An input to contribute to a channel's funding transaction either when using the v2 channel
208+
/// establishment protocol or when splicing.
209+
#[derive(Clone)]
210+
pub struct FundingTxInput {
211+
/// The unspent [`TxOut`] that the input spends.
212+
///
213+
/// [`TxOut`]: bitcoin::TxOut
214+
pub(super) utxo: Utxo,
215+
216+
/// The sequence number to use in the [`TxIn`].
217+
///
218+
/// [`TxIn`]: bitcoin::TxIn
219+
pub(super) sequence: Sequence,
220+
221+
/// The transaction containing the unspent [`TxOut`] referenced by [`utxo`].
222+
///
223+
/// [`TxOut`]: bitcoin::TxOut
224+
/// [`utxo`]: Self::utxo
225+
pub(super) prevtx: Transaction,
226+
}
227+
228+
impl FundingTxInput {
229+
fn new<F: FnOnce(&bitcoin::Script) -> bool>(
230+
prevtx: Transaction, vout: u32, sequence: Sequence, witness_weight: Weight,
231+
script_filter: F,
232+
) -> Result<Self, ()> {
233+
Ok(FundingTxInput {
234+
utxo: Utxo {
235+
outpoint: bitcoin::OutPoint { txid: prevtx.compute_txid(), vout },
236+
output: prevtx
237+
.output
238+
.get(vout as usize)
239+
.filter(|output| script_filter(&output.script_pubkey))
240+
.ok_or(())?
241+
.clone(),
242+
satisfaction_weight: EMPTY_SCRIPT_SIG_WEIGHT + witness_weight.to_wu(),
243+
},
244+
sequence,
245+
prevtx,
246+
})
247+
}
248+
249+
/// Creates an input spending a P2WPKH output from the given `prevtx` at index `vout` using the
250+
/// provided `sequence` number.
251+
///
252+
/// Returns `Err` if no such output exists in `prevtx` at index `vout`.
253+
pub fn new_p2wpkh(prevtx: Transaction, vout: u32, sequence: Sequence) -> Result<Self, ()> {
254+
let witness_weight = Weight::from_wu(P2WPKH_WITNESS_WEIGHT);
255+
FundingTxInput::new(prevtx, vout, sequence, witness_weight, bitcoin::Script::is_p2wpkh)
256+
}
257+
258+
/// Creates an input spending a P2WSH output from the given `prevtx` at index `vout` using the
259+
/// provided `sequence` number.
260+
///
261+
/// Requires passing the weight of witness needed to satisfy the output's script.
262+
///
263+
/// Returns `Err` if no such output exists in `prevtx` at index `vout`.
264+
pub fn new_p2wsh(
265+
prevtx: Transaction, vout: u32, sequence: Sequence, witness_weight: Weight,
266+
) -> Result<Self, ()> {
267+
FundingTxInput::new(prevtx, vout, sequence, witness_weight, bitcoin::Script::is_p2wsh)
268+
}
269+
270+
/// Creates an input spending a P2TR output from the given `prevtx` at index `vout` using the
271+
/// provided `sequence` number.
272+
///
273+
/// This is meant for inputs spending a taproot output using the key path. See
274+
/// [`new_p2tr_script_spend`] for when spending using a script path.
275+
///
276+
/// Returns `Err` if no such output exists in `prevtx` at index `vout`.
277+
///
278+
/// [`new_p2tr_script_spend`]: Self::new_p2tr_script_spend
279+
pub fn new_p2tr_key_spend(
280+
prevtx: Transaction, vout: u32, sequence: Sequence,
281+
) -> Result<Self, ()> {
282+
let witness_weight = Weight::from_wu(P2TR_KEY_PATH_WITNESS_WEIGHT);
283+
FundingTxInput::new(prevtx, vout, sequence, witness_weight, bitcoin::Script::is_p2tr)
284+
}
285+
286+
/// Creates an input spending a P2TR output from the given `prevtx` at index `vout` using the
287+
/// provided `sequence` number.
288+
///
289+
/// Requires passing the weight of witness needed to satisfy a script path of the taproot
290+
/// output. See [`new_p2tr_key_spend`] for when spending using the key path.
291+
///
292+
/// Returns `Err` if no such output exists in `prevtx` at index `vout`.
293+
///
294+
/// [`new_p2tr_key_spend`]: Self::new_p2tr_key_spend
295+
pub fn new_p2tr_script_spend(
296+
prevtx: Transaction, vout: u32, sequence: Sequence, witness_weight: Weight,
297+
) -> Result<Self, ()> {
298+
FundingTxInput::new(prevtx, vout, sequence, witness_weight, bitcoin::Script::is_p2tr)
299+
}
300+
}
301+
203302
// We hold various information about HTLC relay in the HTLC objects in Channel itself:
204303
//
205304
// Upon receipt of an HTLC from a peer, we'll give it a PendingHTLCStatus indicating if it should
@@ -4459,7 +4558,7 @@ where
44594558
#[rustfmt::skip]
44604559
pub fn splice_channel(
44614560
&self, channel_id: &ChannelId, counterparty_node_id: &PublicKey, our_funding_contribution_satoshis: i64,
4462-
our_funding_inputs: Vec<(TxIn, Transaction, Weight)>, change_script: Option<ScriptBuf>,
4561+
our_funding_inputs: Vec<FundingTxInput>, change_script: Option<ScriptBuf>,
44634562
funding_feerate_per_kw: u32, locktime: Option<u32>,
44644563
) -> Result<(), APIError> {
44654564
let mut res = Ok(());
@@ -4480,9 +4579,8 @@ where
44804579
#[cfg(splicing)]
44814580
fn internal_splice_channel(
44824581
&self, channel_id: &ChannelId, counterparty_node_id: &PublicKey,
4483-
our_funding_contribution_satoshis: i64,
4484-
our_funding_inputs: Vec<(TxIn, Transaction, Weight)>, change_script: Option<ScriptBuf>,
4485-
funding_feerate_per_kw: u32, locktime: Option<u32>,
4582+
our_funding_contribution_satoshis: i64, our_funding_inputs: Vec<FundingTxInput>,
4583+
change_script: Option<ScriptBuf>, funding_feerate_per_kw: u32, locktime: Option<u32>,
44864584
) -> Result<(), APIError> {
44874585
let per_peer_state = self.per_peer_state.read().unwrap();
44884586

0 commit comments

Comments
 (0)