Skip to content

Commit f72b7ea

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 3327382 commit f72b7ea

File tree

7 files changed

+242
-130
lines changed

7 files changed

+242
-130
lines changed

lightning/src/ln/channel.rs

Lines changed: 92 additions & 77 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,16 +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;
40+
use crate::events::bump_transaction::{BASE_INPUT_WEIGHT, EMPTY_SCRIPT_SIG_WEIGHT};
4141
use crate::events::ClosureReason;
4242
use crate::ln::chan_utils;
43-
#[cfg(splicing)]
44-
use crate::ln::chan_utils::FUNDING_TRANSACTION_WITNESS_WEIGHT;
4543
use crate::ln::chan_utils::{
4644
get_commitment_transaction_number_obscure_factor, max_htlcs, second_stage_tx_fees_sat,
4745
selected_commitment_sat_per_1000_weight, ChannelPublicKeys, ChannelTransactionParameters,
4846
ClosingTransaction, CommitmentTransaction, CounterpartyChannelTransactionParameters,
4947
CounterpartyCommitmentSecrets, HTLCOutputInCommitment, HolderCommitmentTransaction,
48+
FUNDING_TRANSACTION_WITNESS_WEIGHT,
5049
};
5150
use crate::ln::channel_state::{
5251
ChannelShutdownState, CounterpartyForwardingInfo, InboundHTLCDetails, InboundHTLCStateDetails,
@@ -57,6 +56,7 @@ use crate::ln::channelmanager::{
5756
PaymentClaimDetails, PendingHTLCInfo, PendingHTLCStatus, RAACommitmentOrder, SentHTLCId,
5857
BREAKDOWN_TIMEOUT, MAX_LOCAL_BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA,
5958
};
59+
use crate::ln::funding::FundingTxInput;
6060
#[cfg(splicing)]
6161
use crate::ln::interactivetxs::{
6262
calculate_change_output_value, AbortReason, InteractiveTxMessageSend,
@@ -5883,16 +5883,15 @@ fn get_v2_channel_reserve_satoshis(channel_value_satoshis: u64, dust_limit_satos
58835883
#[allow(dead_code)] // TODO(dual_funding): TODO(splicing): Remove allow once used.
58845884
#[rustfmt::skip]
58855885
fn estimate_v2_funding_transaction_fee(
5886-
is_initiator: bool, input_count: usize, witness_weight: Weight,
5886+
funding_inputs: &[FundingTxInput], is_initiator: bool, is_splice: bool,
58875887
funding_feerate_sat_per_1000_weight: u32,
58885888
) -> u64 {
5889-
// Inputs
5890-
let mut weight = (input_count as u64) * BASE_INPUT_WEIGHT;
5891-
5892-
// Witnesses
5893-
weight = weight.saturating_add(witness_weight.to_wu());
5889+
let mut weight: u64 = funding_inputs
5890+
.iter()
5891+
.map(|input| BASE_INPUT_WEIGHT.saturating_add(input.utxo.satisfaction_weight))
5892+
.fold(0, |total_weight, input_weight| total_weight.saturating_add(input_weight));
58945893

5895-
// If we are the initiator, we must pay for weight of all common fields in the funding transaction.
5894+
// The initiator pays for all common fields and the shared output in the funding transaction.
58965895
if is_initiator {
58975896
weight = weight
58985897
.saturating_add(TX_COMMON_FIELDS_WEIGHT)
@@ -5901,7 +5900,15 @@ fn estimate_v2_funding_transaction_fee(
59015900
// to calculate the contributed weight, so we use an all-zero hash.
59025901
.saturating_add(get_output_weight(&ScriptBuf::new_p2wsh(
59035902
&WScriptHash::from_raw_hash(Hash::all_zeros())
5904-
)).to_wu())
5903+
)).to_wu());
5904+
5905+
// The splice initiator pays for the input spending the previous funding output.
5906+
if is_splice {
5907+
weight = weight
5908+
.saturating_add(BASE_INPUT_WEIGHT)
5909+
.saturating_add(EMPTY_SCRIPT_SIG_WEIGHT)
5910+
.saturating_add(FUNDING_TRANSACTION_WITNESS_WEIGHT);
5911+
}
59055912
}
59065913

59075914
fee_for_weight(funding_feerate_sat_per_1000_weight, weight)
@@ -5916,28 +5923,16 @@ fn estimate_v2_funding_transaction_fee(
59165923
#[cfg(splicing)]
59175924
#[rustfmt::skip]
59185925
fn check_v2_funding_inputs_sufficient(
5919-
contribution_amount: i64, funding_inputs: &[(TxIn, Transaction, Weight)], is_initiator: bool,
5926+
contribution_amount: i64, funding_inputs: &[FundingTxInput], is_initiator: bool,
59205927
is_splice: bool, funding_feerate_sat_per_1000_weight: u32,
59215928
) -> Result<u64, ChannelError> {
5922-
let mut total_input_witness_weight = Weight::from_wu(funding_inputs.iter().map(|(_, _, w)| w.to_wu()).sum());
5923-
let mut funding_inputs_len = funding_inputs.len();
5924-
if is_initiator && is_splice {
5925-
// consider the weight of the input and witness needed for spending the old funding transaction
5926-
funding_inputs_len += 1;
5927-
total_input_witness_weight += Weight::from_wu(FUNDING_TRANSACTION_WITNESS_WEIGHT);
5928-
}
5929-
let estimated_fee = estimate_v2_funding_transaction_fee(is_initiator, funding_inputs_len, total_input_witness_weight, funding_feerate_sat_per_1000_weight);
5929+
let estimated_fee = estimate_v2_funding_transaction_fee(
5930+
funding_inputs, is_initiator, is_splice, funding_feerate_sat_per_1000_weight,
5931+
);
59305932

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

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

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

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

10676-
for (txin, tx, _) in our_funding_inputs.iter() {
10675+
for FundingTxInput { utxo, prevtx, .. } in our_funding_inputs.iter() {
1067710676
const MESSAGE_TEMPLATE: msgs::TxAddInput = msgs::TxAddInput {
1067810677
channel_id: ChannelId([0; 32]),
1067910678
serial_id: 0,
1068010679
prevtx: None,
1068110680
prevtx_out: 0,
1068210681
sequence: 0,
10682+
// Mutually exclusive with prevtx, which is accounted for below.
1068310683
shared_input_txid: None,
1068410684
};
10685-
let message_len = MESSAGE_TEMPLATE.serialized_length() + tx.serialized_length();
10685+
let message_len = MESSAGE_TEMPLATE.serialized_length() + prevtx.serialized_length();
1068610686
if message_len > LN_MAX_MSG_LEN {
1068710687
return Err(APIError::APIMisuseError {
1068810688
err: format!(
1068910689
"Funding input references a prevtx that is too large for tx_add_input: {}",
10690-
txin.previous_output,
10690+
utxo.outpoint,
1069110691
),
1069210692
});
1069310693
}
@@ -12469,7 +12469,7 @@ where
1246912469
pub fn new_outbound<ES: Deref, F: Deref, L: Deref>(
1247012470
fee_estimator: &LowerBoundedFeeEstimator<F>, entropy_source: &ES, signer_provider: &SP,
1247112471
counterparty_node_id: PublicKey, their_features: &InitFeatures, funding_satoshis: u64,
12472-
funding_inputs: Vec<(TxIn, Transaction, Weight)>, user_id: u128, config: &UserConfig,
12472+
funding_inputs: Vec<FundingTxInput>, user_id: u128, config: &UserConfig,
1247312473
current_chain_height: u32, outbound_scid_alias: u64, funding_confirmation_target: ConfirmationTarget,
1247412474
logger: L,
1247512475
) -> Result<Self, APIError>
@@ -12683,8 +12683,12 @@ where
1268312683
value: Amount::from_sat(funding.get_value_satoshis()),
1268412684
script_pubkey: funding.get_funding_redeemscript().to_p2wsh(),
1268512685
};
12686-
let inputs_to_contribute =
12687-
our_funding_inputs.into_iter().map(|(txin, tx, _)| (txin, tx)).collect();
12686+
let inputs_to_contribute = our_funding_inputs
12687+
.into_iter()
12688+
.map(|FundingTxInput { utxo, sequence, prevtx }| {
12689+
(TxIn { previous_output: utxo.outpoint, sequence, ..Default::default() }, prevtx)
12690+
})
12691+
.collect();
1268812692

1268912693
let interactive_tx_constructor = Some(InteractiveTxConstructor::new(
1269012694
InteractiveTxConstructorArgs {
@@ -14121,6 +14125,8 @@ mod tests {
1412114125
};
1412214126
use crate::ln::channel_keys::{RevocationBasepoint, RevocationKey};
1412314127
use crate::ln::channelmanager::{self, HTLCSource, PaymentId};
14128+
#[cfg(splicing)]
14129+
use crate::ln::funding::FundingTxInput;
1412414130
use crate::ln::msgs;
1412514131
use crate::ln::msgs::{ChannelUpdate, UnsignedChannelUpdate, MAX_VALUE_MSAT};
1412614132
use crate::ln::onion_utils::{AttributionData, LocalHTLCFailureReason};
@@ -14152,11 +14158,9 @@ mod tests {
1415214158
use bitcoin::secp256k1::ffi::Signature as FFISignature;
1415314159
use bitcoin::secp256k1::{ecdsa::Signature, Secp256k1};
1415414160
use bitcoin::secp256k1::{PublicKey, SecretKey};
14155-
#[cfg(splicing)]
14156-
use bitcoin::transaction::TxIn;
1415714161
use bitcoin::transaction::{Transaction, TxOut, Version};
1415814162
#[cfg(splicing)]
14159-
use bitcoin::Weight;
14163+
use bitcoin::{ScriptBuf, Sequence, WPubkeyHash};
1416014164
use bitcoin::{WitnessProgram, WitnessVersion};
1416114165
use std::cmp;
1416214166

@@ -15863,54 +15867,65 @@ mod tests {
1586315867
#[rustfmt::skip]
1586415868
fn test_estimate_v2_funding_transaction_fee() {
1586515869
use crate::ln::channel::estimate_v2_funding_transaction_fee;
15866-
use bitcoin::Weight;
1586715870

15868-
// 2 inputs with weight 300, initiator, 2000 sat/kw feerate
15871+
let one_input = [funding_input_sats(1_000)];
15872+
let two_inputs = [funding_input_sats(1_000), funding_input_sats(1_000)];
15873+
15874+
// 2 inputs, initiator, 2000 sat/kw feerate
1586915875
assert_eq!(
15870-
estimate_v2_funding_transaction_fee(true, 2, Weight::from_wu(300), 2000),
15871-
1668
15876+
estimate_v2_funding_transaction_fee(&two_inputs, true, false, 2000),
15877+
1520,
1587215878
);
1587315879

1587415880
// higher feerate
1587515881
assert_eq!(
15876-
estimate_v2_funding_transaction_fee(true, 2, Weight::from_wu(300), 3000),
15877-
2502
15882+
estimate_v2_funding_transaction_fee(&two_inputs, true, false, 3000),
15883+
2280,
1587815884
);
1587915885

1588015886
// only 1 input
1588115887
assert_eq!(
15882-
estimate_v2_funding_transaction_fee(true, 1, Weight::from_wu(300), 2000),
15883-
1348
15888+
estimate_v2_funding_transaction_fee(&one_input, true, false, 2000),
15889+
974,
1588415890
);
1588515891

15886-
// 0 input weight
15892+
// 0 inputs
1588715893
assert_eq!(
15888-
estimate_v2_funding_transaction_fee(true, 1, Weight::from_wu(0), 2000),
15889-
748
15894+
estimate_v2_funding_transaction_fee(&[], true, false, 2000),
15895+
428,
1589015896
);
1589115897

1589215898
// not initiator
1589315899
assert_eq!(
15894-
estimate_v2_funding_transaction_fee(false, 1, Weight::from_wu(0), 2000),
15895-
320
15900+
estimate_v2_funding_transaction_fee(&[], false, false, 2000),
15901+
0,
15902+
);
15903+
15904+
// splice initiator
15905+
assert_eq!(
15906+
estimate_v2_funding_transaction_fee(&one_input, true, true, 2000),
15907+
1746,
15908+
);
15909+
15910+
// splice acceptor
15911+
assert_eq!(
15912+
estimate_v2_funding_transaction_fee(&one_input, false, true, 2000),
15913+
546,
1589615914
);
1589715915
}
1589815916

15899-
#[cfg(splicing)]
1590015917
#[rustfmt::skip]
15901-
fn funding_input_sats(input_value_sats: u64) -> (TxIn, Transaction, Weight) {
15902-
use crate::sign::P2WPKH_WITNESS_WEIGHT;
15903-
15904-
let input_1_prev_out = TxOut { value: Amount::from_sat(input_value_sats), script_pubkey: bitcoin::ScriptBuf::default() };
15905-
let input_1_prev_tx = Transaction {
15906-
input: vec![], output: vec![input_1_prev_out],
15907-
version: Version::TWO, lock_time: bitcoin::absolute::LockTime::ZERO,
15918+
fn funding_input_sats(input_value_sats: u64) -> FundingTxInput {
15919+
let prevout = TxOut {
15920+
value: Amount::from_sat(input_value_sats),
15921+
script_pubkey: ScriptBuf::new_p2wpkh(&WPubkeyHash::all_zeros()),
1590815922
};
15909-
let input_1_txin = TxIn {
15910-
previous_output: bitcoin::OutPoint { txid: input_1_prev_tx.compute_txid(), vout: 0 },
15911-
..Default::default()
15923+
let prevtx = Transaction {
15924+
input: vec![], output: vec![prevout],
15925+
version: Version::TWO, lock_time: bitcoin::absolute::LockTime::ZERO,
1591215926
};
15913-
(input_1_txin, input_1_prev_tx, Weight::from_wu(P2WPKH_WITNESS_WEIGHT))
15927+
15928+
FundingTxInput::new_p2wpkh(prevtx, 0, Sequence::ZERO).unwrap()
1591415929
}
1591515930

1591615931
#[cfg(splicing)]
@@ -15931,7 +15946,7 @@ mod tests {
1593115946
true,
1593215947
2000,
1593315948
).unwrap(),
15934-
2268,
15949+
2284,
1593515950
);
1593615951

1593715952
// negative case, inputs clearly insufficient
@@ -15947,13 +15962,13 @@ mod tests {
1594715962
);
1594815963
assert_eq!(
1594915964
format!("{:?}", res.err().unwrap()),
15950-
"Warn: Total input amount 100000 is lower than needed for contribution 220000, considering fees of 1730. Need more inputs.",
15965+
"Warn: Total input amount 100000 is lower than needed for contribution 220000, considering fees of 1738. Need more inputs.",
1595115966
);
1595215967
}
1595315968

1595415969
// barely covers
1595515970
{
15956-
let expected_fee: u64 = 2268;
15971+
let expected_fee: u64 = 2284;
1595715972
assert_eq!(
1595815973
check_v2_funding_inputs_sufficient(
1595915974
(300_000 - expected_fee - 20) as i64,
@@ -15983,13 +15998,13 @@ mod tests {
1598315998
);
1598415999
assert_eq!(
1598516000
format!("{:?}", res.err().unwrap()),
15986-
"Warn: Total input amount 300000 is lower than needed for contribution 298032, considering fees of 2495. Need more inputs.",
16001+
"Warn: Total input amount 300000 is lower than needed for contribution 298032, considering fees of 2513. Need more inputs.",
1598716002
);
1598816003
}
1598916004

1599016005
// barely covers, less fees (no extra weight, no init)
1599116006
{
15992-
let expected_fee: u64 = 1076;
16007+
let expected_fee: u64 = 1092;
1599316008
assert_eq!(
1599416009
check_v2_funding_inputs_sufficient(
1599516010
(300_000 - expected_fee - 20) as i64,

0 commit comments

Comments
 (0)