Skip to content

Commit bdb8d5d

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 75b7e80 commit bdb8d5d

File tree

7 files changed

+271
-138
lines changed

7 files changed

+271
-138
lines changed

lightning/src/ln/channel.rs

Lines changed: 91 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ 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};
17-
use bitcoin::{Weight, Witness};
16+
use bitcoin::transaction::{Transaction, TxOut};
17+
use bitcoin::Witness;
1818

1919
use bitcoin::hash_types::{BlockHash, Txid};
2020
use bitcoin::hashes::sha256::Hash as Sha256;
@@ -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,
@@ -37,18 +37,15 @@ use crate::chain::channelmonitor::{
3737
};
3838
use crate::chain::transaction::{OutPoint, TransactionData};
3939
use crate::chain::BestBlock;
40-
use crate::events::bump_transaction::BASE_INPUT_WEIGHT;
41-
#[cfg(splicing)]
42-
use crate::events::bump_transaction::EMPTY_SCRIPT_SIG_WEIGHT;
40+
use crate::events::bump_transaction::{BASE_INPUT_WEIGHT, EMPTY_SCRIPT_SIG_WEIGHT};
4341
use crate::events::ClosureReason;
4442
use crate::ln::chan_utils;
45-
#[cfg(splicing)]
46-
use crate::ln::chan_utils::FUNDING_TRANSACTION_WITNESS_WEIGHT;
4743
use crate::ln::chan_utils::{
4844
get_commitment_transaction_number_obscure_factor, max_htlcs, second_stage_tx_fees_sat,
4945
selected_commitment_sat_per_1000_weight, ChannelPublicKeys, ChannelTransactionParameters,
5046
ClosingTransaction, CommitmentTransaction, CounterpartyChannelTransactionParameters,
5147
CounterpartyCommitmentSecrets, HTLCOutputInCommitment, HolderCommitmentTransaction,
48+
FUNDING_TRANSACTION_WITNESS_WEIGHT,
5249
};
5350
use crate::ln::channel_state::{
5451
ChannelShutdownState, CounterpartyForwardingInfo, InboundHTLCDetails, InboundHTLCStateDetails,
@@ -59,6 +56,7 @@ use crate::ln::channelmanager::{
5956
PaymentClaimDetails, PendingHTLCInfo, PendingHTLCStatus, RAACommitmentOrder, SentHTLCId,
6057
BREAKDOWN_TIMEOUT, MAX_LOCAL_BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA,
6158
};
59+
use crate::ln::funding::FundingTxInput;
6260
#[cfg(splicing)]
6361
use crate::ln::interactivetxs::{
6462
calculate_change_output_value, AbortReason, InteractiveTxMessageSend,
@@ -5880,21 +5878,18 @@ fn get_v2_channel_reserve_satoshis(channel_value_satoshis: u64, dust_limit_satos
58805878
}
58815879

58825880
/// Estimate our part of the fee of the new funding transaction.
5883-
/// input_count: Number of contributed inputs.
5884-
/// input_satisfaction_weight: The satisfaction weight for contributed inputs.
58855881
#[allow(dead_code)] // TODO(dual_funding): TODO(splicing): Remove allow once used.
58865882
#[rustfmt::skip]
58875883
fn estimate_v2_funding_transaction_fee(
5888-
is_initiator: bool, input_count: usize, input_satisfaction_weight: Weight,
5884+
funding_inputs: &[FundingTxInput], is_initiator: bool, is_splice: bool,
58895885
funding_feerate_sat_per_1000_weight: u32,
58905886
) -> u64 {
5891-
// Inputs
5892-
let mut weight = (input_count as u64) * BASE_INPUT_WEIGHT;
5887+
let mut weight: u64 = funding_inputs
5888+
.iter()
5889+
.map(|input| BASE_INPUT_WEIGHT.saturating_add(input.utxo.satisfaction_weight))
5890+
.fold(0, |total_weight, input_weight| total_weight.saturating_add(input_weight));
58935891

5894-
// Witnesses
5895-
weight = weight.saturating_add(input_satisfaction_weight.to_wu());
5896-
5897-
// If we are the initiator, we must pay for weight of all common fields in the funding transaction.
5892+
// The initiator pays for all common fields and the shared output in the funding transaction.
58985893
if is_initiator {
58995894
weight = weight
59005895
.saturating_add(TX_COMMON_FIELDS_WEIGHT)
@@ -5903,7 +5898,15 @@ fn estimate_v2_funding_transaction_fee(
59035898
// to calculate the contributed weight, so we use an all-zero hash.
59045899
.saturating_add(get_output_weight(&ScriptBuf::new_p2wsh(
59055900
&WScriptHash::from_raw_hash(Hash::all_zeros())
5906-
)).to_wu())
5901+
)).to_wu());
5902+
5903+
// The splice initiator pays for the input spending the previous funding output.
5904+
if is_splice {
5905+
weight = weight
5906+
.saturating_add(BASE_INPUT_WEIGHT)
5907+
.saturating_add(EMPTY_SCRIPT_SIG_WEIGHT)
5908+
.saturating_add(FUNDING_TRANSACTION_WITNESS_WEIGHT);
5909+
}
59075910
}
59085911

59095912
fee_for_weight(funding_feerate_sat_per_1000_weight, weight)
@@ -5918,29 +5921,16 @@ fn estimate_v2_funding_transaction_fee(
59185921
#[cfg(splicing)]
59195922
#[rustfmt::skip]
59205923
fn check_v2_funding_inputs_sufficient(
5921-
contribution_amount: i64, funding_inputs: &[(TxIn, Transaction, Weight)], is_initiator: bool,
5924+
contribution_amount: i64, funding_inputs: &[FundingTxInput], is_initiator: bool,
59225925
is_splice: bool, funding_feerate_sat_per_1000_weight: u32,
59235926
) -> Result<u64, ChannelError> {
5924-
let mut total_input_satisfaction_weight = Weight::from_wu(funding_inputs.iter().map(|(_, _, w)| w.to_wu()).sum());
5925-
let mut funding_inputs_len = funding_inputs.len();
5926-
if is_initiator && is_splice {
5927-
// consider the weight of the input and witness needed for spending the old funding transaction
5928-
funding_inputs_len += 1;
5929-
total_input_satisfaction_weight +=
5930-
Weight::from_wu(EMPTY_SCRIPT_SIG_WEIGHT + FUNDING_TRANSACTION_WITNESS_WEIGHT);
5931-
}
5932-
let estimated_fee = estimate_v2_funding_transaction_fee(is_initiator, funding_inputs_len, total_input_satisfaction_weight, funding_feerate_sat_per_1000_weight);
5927+
let estimated_fee = estimate_v2_funding_transaction_fee(
5928+
funding_inputs, is_initiator, is_splice, funding_feerate_sat_per_1000_weight,
5929+
);
59335930

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

59465936
// If the inputs are enough to cover intended contribution amount, with fees even when
@@ -5982,7 +5972,7 @@ pub(super) struct FundingNegotiationContext {
59825972
pub shared_funding_input: Option<SharedOwnedInput>,
59835973
/// The funding inputs we will be contributing to the channel.
59845974
#[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
5985-
pub our_funding_inputs: Vec<(TxIn, Transaction, Weight)>,
5975+
pub our_funding_inputs: Vec<FundingTxInput>,
59865976
/// The change output script. This will be used if needed or -- if not set -- generated using
59875977
/// `SignerProvider::get_destination_script`.
59885978
#[allow(dead_code)] // TODO(splicing): Remove once splicing is enabled.
@@ -6054,8 +6044,13 @@ impl FundingNegotiationContext {
60546044
}
60556045
}
60566046

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

60606055
let constructor_args = InteractiveTxConstructorArgs {
60616056
entropy_source,
@@ -10608,9 +10603,8 @@ where
1060810603
/// generated by `SignerProvider::get_destination_script`.
1060910604
#[cfg(splicing)]
1061010605
pub fn splice_channel(
10611-
&mut self, our_funding_contribution_satoshis: i64,
10612-
our_funding_inputs: Vec<(TxIn, Transaction, Weight)>, change_script: Option<ScriptBuf>,
10613-
funding_feerate_per_kw: u32, locktime: u32,
10606+
&mut self, our_funding_contribution_satoshis: i64, our_funding_inputs: Vec<FundingTxInput>,
10607+
change_script: Option<ScriptBuf>, funding_feerate_per_kw: u32, locktime: u32,
1061410608
) -> Result<msgs::SpliceInit, APIError> {
1061510609
// Check if a splice has been initiated already.
1061610610
// Note: only a single outstanding splice is supported (per spec)
@@ -10676,21 +10670,22 @@ where
1067610670
),
1067710671
})?;
1067810672

10679-
for (txin, tx, _) in our_funding_inputs.iter() {
10673+
for FundingTxInput { utxo, prevtx, .. } in our_funding_inputs.iter() {
1068010674
const MESSAGE_TEMPLATE: msgs::TxAddInput = msgs::TxAddInput {
1068110675
channel_id: ChannelId([0; 32]),
1068210676
serial_id: 0,
1068310677
prevtx: None,
1068410678
prevtx_out: 0,
1068510679
sequence: 0,
10680+
// Mutually exclusive with prevtx, which is accounted for below.
1068610681
shared_input_txid: None,
1068710682
};
10688-
let message_len = MESSAGE_TEMPLATE.serialized_length() + tx.serialized_length();
10683+
let message_len = MESSAGE_TEMPLATE.serialized_length() + prevtx.serialized_length();
1068910684
if message_len > LN_MAX_MSG_LEN {
1069010685
return Err(APIError::APIMisuseError {
1069110686
err: format!(
1069210687
"Funding input references a prevtx that is too large for tx_add_input: {}",
10693-
txin.previous_output,
10688+
utxo.outpoint,
1069410689
),
1069510690
});
1069610691
}
@@ -12472,7 +12467,7 @@ where
1247212467
pub fn new_outbound<ES: Deref, F: Deref, L: Deref>(
1247312468
fee_estimator: &LowerBoundedFeeEstimator<F>, entropy_source: &ES, signer_provider: &SP,
1247412469
counterparty_node_id: PublicKey, their_features: &InitFeatures, funding_satoshis: u64,
12475-
funding_inputs: Vec<(TxIn, Transaction, Weight)>, user_id: u128, config: &UserConfig,
12470+
funding_inputs: Vec<FundingTxInput>, user_id: u128, config: &UserConfig,
1247612471
current_chain_height: u32, outbound_scid_alias: u64, funding_confirmation_target: ConfirmationTarget,
1247712472
logger: L,
1247812473
) -> Result<Self, APIError>
@@ -12686,8 +12681,12 @@ where
1268612681
value: Amount::from_sat(funding.get_value_satoshis()),
1268712682
script_pubkey: funding.get_funding_redeemscript().to_p2wsh(),
1268812683
};
12689-
let inputs_to_contribute =
12690-
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();
1269112690

1269212691
let interactive_tx_constructor = Some(InteractiveTxConstructor::new(
1269312692
InteractiveTxConstructorArgs {
@@ -14124,6 +14123,7 @@ mod tests {
1412414123
};
1412514124
use crate::ln::channel_keys::{RevocationBasepoint, RevocationKey};
1412614125
use crate::ln::channelmanager::{self, HTLCSource, PaymentId};
14126+
use crate::ln::funding::FundingTxInput;
1412714127
use crate::ln::msgs;
1412814128
use crate::ln::msgs::{ChannelUpdate, UnsignedChannelUpdate, MAX_VALUE_MSAT};
1412914129
use crate::ln::onion_utils::{AttributionData, LocalHTLCFailureReason};
@@ -14155,12 +14155,8 @@ mod tests {
1415514155
use bitcoin::secp256k1::ffi::Signature as FFISignature;
1415614156
use bitcoin::secp256k1::{ecdsa::Signature, Secp256k1};
1415714157
use bitcoin::secp256k1::{PublicKey, SecretKey};
14158-
#[cfg(splicing)]
14159-
use bitcoin::transaction::TxIn;
1416014158
use bitcoin::transaction::{Transaction, TxOut, Version};
14161-
#[cfg(splicing)]
14162-
use bitcoin::Weight;
14163-
use bitcoin::{WitnessProgram, WitnessVersion};
14159+
use bitcoin::{ScriptBuf, WPubkeyHash, WitnessProgram, WitnessVersion};
1416414160
use std::cmp;
1416514161

1416614162
#[test]
@@ -15866,54 +15862,65 @@ mod tests {
1586615862
#[rustfmt::skip]
1586715863
fn test_estimate_v2_funding_transaction_fee() {
1586815864
use crate::ln::channel::estimate_v2_funding_transaction_fee;
15869-
use bitcoin::Weight;
1587015865

15871-
// 2 inputs with weight 300, initiator, 2000 sat/kw feerate
15866+
let one_input = [funding_input_sats(1_000)];
15867+
let two_inputs = [funding_input_sats(1_000), funding_input_sats(1_000)];
15868+
15869+
// 2 inputs, initiator, 2000 sat/kw feerate
1587215870
assert_eq!(
15873-
estimate_v2_funding_transaction_fee(true, 2, Weight::from_wu(300), 2000),
15874-
1668
15871+
estimate_v2_funding_transaction_fee(&two_inputs, true, false, 2000),
15872+
1520,
1587515873
);
1587615874

1587715875
// higher feerate
1587815876
assert_eq!(
15879-
estimate_v2_funding_transaction_fee(true, 2, Weight::from_wu(300), 3000),
15880-
2502
15877+
estimate_v2_funding_transaction_fee(&two_inputs, true, false, 3000),
15878+
2280,
1588115879
);
1588215880

1588315881
// only 1 input
1588415882
assert_eq!(
15885-
estimate_v2_funding_transaction_fee(true, 1, Weight::from_wu(300), 2000),
15886-
1348
15883+
estimate_v2_funding_transaction_fee(&one_input, true, false, 2000),
15884+
974,
1588715885
);
1588815886

15889-
// 0 input weight
15887+
// 0 inputs
1589015888
assert_eq!(
15891-
estimate_v2_funding_transaction_fee(true, 1, Weight::from_wu(0), 2000),
15892-
748
15889+
estimate_v2_funding_transaction_fee(&[], true, false, 2000),
15890+
428,
1589315891
);
1589415892

1589515893
// not initiator
1589615894
assert_eq!(
15897-
estimate_v2_funding_transaction_fee(false, 1, Weight::from_wu(0), 2000),
15898-
320
15895+
estimate_v2_funding_transaction_fee(&[], false, false, 2000),
15896+
0,
15897+
);
15898+
15899+
// splice initiator
15900+
assert_eq!(
15901+
estimate_v2_funding_transaction_fee(&one_input, true, true, 2000),
15902+
1746,
15903+
);
15904+
15905+
// splice acceptor
15906+
assert_eq!(
15907+
estimate_v2_funding_transaction_fee(&one_input, false, true, 2000),
15908+
546,
1589915909
);
1590015910
}
1590115911

15902-
#[cfg(splicing)]
1590315912
#[rustfmt::skip]
15904-
fn funding_input_sats(input_value_sats: u64) -> (TxIn, Transaction, Weight) {
15905-
use crate::sign::P2WPKH_WITNESS_WEIGHT;
15906-
15907-
let input_1_prev_out = TxOut { value: Amount::from_sat(input_value_sats), script_pubkey: bitcoin::ScriptBuf::default() };
15908-
let input_1_prev_tx = Transaction {
15909-
input: vec![], output: vec![input_1_prev_out],
15910-
version: Version::TWO, lock_time: bitcoin::absolute::LockTime::ZERO,
15913+
fn funding_input_sats(input_value_sats: u64) -> FundingTxInput {
15914+
let prevout = TxOut {
15915+
value: Amount::from_sat(input_value_sats),
15916+
script_pubkey: ScriptBuf::new_p2wpkh(&WPubkeyHash::all_zeros()),
1591115917
};
15912-
let input_1_txin = TxIn {
15913-
previous_output: bitcoin::OutPoint { txid: input_1_prev_tx.compute_txid(), vout: 0 },
15914-
..Default::default()
15918+
let prevtx = Transaction {
15919+
input: vec![], output: vec![prevout],
15920+
version: Version::TWO, lock_time: bitcoin::absolute::LockTime::ZERO,
1591515921
};
15916-
(input_1_txin, input_1_prev_tx, Weight::from_wu(P2WPKH_WITNESS_WEIGHT))
15922+
15923+
FundingTxInput::new_p2wpkh(prevtx, 0).unwrap()
1591715924
}
1591815925

1591915926
#[cfg(splicing)]
@@ -15934,7 +15941,7 @@ mod tests {
1593415941
true,
1593515942
2000,
1593615943
).unwrap(),
15937-
2276,
15944+
2292,
1593815945
);
1593915946

1594015947
// negative case, inputs clearly insufficient
@@ -15950,13 +15957,13 @@ mod tests {
1595015957
);
1595115958
assert_eq!(
1595215959
format!("{:?}", res.err().unwrap()),
15953-
"Warn: Total input amount 100000 is lower than needed for contribution 220000, considering fees of 1738. Need more inputs.",
15960+
"Warn: Total input amount 100000 is lower than needed for contribution 220000, considering fees of 1746. Need more inputs.",
1595415961
);
1595515962
}
1595615963

1595715964
// barely covers
1595815965
{
15959-
let expected_fee: u64 = 2276;
15966+
let expected_fee: u64 = 2292;
1596015967
assert_eq!(
1596115968
check_v2_funding_inputs_sufficient(
1596215969
(300_000 - expected_fee - 20) as i64,
@@ -15986,13 +15993,13 @@ mod tests {
1598615993
);
1598715994
assert_eq!(
1598815995
format!("{:?}", res.err().unwrap()),
15989-
"Warn: Total input amount 300000 is lower than needed for contribution 298032, considering fees of 2504. Need more inputs.",
15996+
"Warn: Total input amount 300000 is lower than needed for contribution 298032, considering fees of 2522. Need more inputs.",
1599015997
);
1599115998
}
1599215999

1599316000
// barely covers, less fees (no extra weight, no init)
1599416001
{
15995-
let expected_fee: u64 = 1076;
16002+
let expected_fee: u64 = 1092;
1599616003
assert_eq!(
1599716004
check_v2_funding_inputs_sufficient(
1599816005
(300_000 - expected_fee - 20) as i64,

0 commit comments

Comments
 (0)