Skip to content

Commit 7cf95e1

Browse files
authored
Merge pull request #3558 from optout21/funding-fee-estimation
Include base input fee in fee, in calculate_our_funding_satoshis()
2 parents 11d12d1 + c29ec0f commit 7cf95e1

File tree

3 files changed

+83
-70
lines changed

3 files changed

+83
-70
lines changed

lightning/src/ln/channel.rs

Lines changed: 62 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ use crate::chain::transaction::{OutPoint, TransactionData};
5757
use crate::sign::ecdsa::EcdsaChannelSigner;
5858
use crate::sign::{EntropySource, ChannelSigner, SignerProvider, NodeSigner, Recipient};
5959
use crate::events::{ClosureReason, Event};
60+
use crate::events::bump_transaction::BASE_INPUT_WEIGHT;
6061
use crate::routing::gossip::NodeId;
6162
use crate::util::ser::{Readable, ReadableArgs, TransactionU16LenLimited, Writeable, Writer};
6263
use crate::util::logger::{Logger, Record, WithContext};
@@ -4470,46 +4471,33 @@ fn get_v2_channel_reserve_satoshis(channel_value_satoshis: u64, dust_limit_satos
44704471
cmp::min(channel_value_satoshis, cmp::max(q, dust_limit_satoshis))
44714472
}
44724473

4473-
#[allow(dead_code)] // TODO(dual_funding): Remove once V2 channels is enabled.
4474-
pub(super) fn calculate_our_funding_satoshis(
4475-
is_initiator: bool, funding_inputs: &[(TxIn, TransactionU16LenLimited)],
4476-
total_witness_weight: Weight, funding_feerate_sat_per_1000_weight: u32,
4477-
holder_dust_limit_satoshis: u64,
4478-
) -> Result<u64, APIError> {
4479-
let mut total_input_satoshis = 0u64;
4480-
let mut our_contributed_weight = 0u64;
4474+
/// Estimate our part of the fee of the new funding transaction.
4475+
/// input_count: Number of contributed inputs.
4476+
/// witness_weight: The witness weight for contributed inputs.
4477+
#[allow(dead_code)] // TODO(dual_funding): TODO(splicing): Remove allow once used.
4478+
fn estimate_v2_funding_transaction_fee(
4479+
is_initiator: bool, input_count: usize, witness_weight: Weight,
4480+
funding_feerate_sat_per_1000_weight: u32,
4481+
) -> u64 {
4482+
// Inputs
4483+
let mut weight = (input_count as u64) * BASE_INPUT_WEIGHT;
44814484

4482-
for (idx, input) in funding_inputs.iter().enumerate() {
4483-
if let Some(output) = input.1.as_transaction().output.get(input.0.previous_output.vout as usize) {
4484-
total_input_satoshis = total_input_satoshis.saturating_add(output.value.to_sat());
4485-
} else {
4486-
return Err(APIError::APIMisuseError {
4487-
err: format!("Transaction with txid {} does not have an output with vout of {} corresponding to TxIn at funding_inputs[{}]",
4488-
input.1.as_transaction().compute_txid(), input.0.previous_output.vout, idx) });
4489-
}
4490-
}
4491-
our_contributed_weight = our_contributed_weight.saturating_add(total_witness_weight.to_wu());
4485+
// Witnesses
4486+
weight = weight.saturating_add(witness_weight.to_wu());
44924487

44934488
// If we are the initiator, we must pay for weight of all common fields in the funding transaction.
44944489
if is_initiator {
4495-
our_contributed_weight = our_contributed_weight
4490+
weight = weight
44964491
.saturating_add(TX_COMMON_FIELDS_WEIGHT)
4497-
// The weight of a P2WSH output to be added later.
4498-
//
4492+
// The weight of the funding output, a P2WSH output
44994493
// NOTE: The witness script hash given here is irrelevant as it's a fixed size and we just want
45004494
// to calculate the contributed weight, so we use an all-zero hash.
45014495
.saturating_add(get_output_weight(&ScriptBuf::new_p2wsh(
45024496
&WScriptHash::from_raw_hash(Hash::all_zeros())
45034497
)).to_wu())
45044498
}
45054499

4506-
let funding_satoshis = total_input_satoshis
4507-
.saturating_sub(fee_for_weight(funding_feerate_sat_per_1000_weight, our_contributed_weight));
4508-
if funding_satoshis < holder_dust_limit_satoshis {
4509-
Ok(0)
4510-
} else {
4511-
Ok(funding_satoshis)
4512-
}
4500+
fee_for_weight(funding_feerate_sat_per_1000_weight, weight)
45134501
}
45144502

45154503
/// Context for dual-funded channels.
@@ -9249,27 +9237,23 @@ impl<SP: Deref> PendingV2Channel<SP> where SP::Target: SignerProvider {
92499237

92509238
/// Creates a new dual-funded channel from a remote side's request for one.
92519239
/// Assumes chain_hash has already been checked and corresponds with what we expect!
9240+
/// TODO(dual_funding): Allow contributions, pass intended amount and inputs
92529241
#[allow(dead_code)] // TODO(dual_funding): Remove once V2 channels is enabled.
92539242
pub fn new_inbound<ES: Deref, F: Deref, L: Deref>(
92549243
fee_estimator: &LowerBoundedFeeEstimator<F>, entropy_source: &ES, signer_provider: &SP,
92559244
holder_node_id: PublicKey, counterparty_node_id: PublicKey, our_supported_features: &ChannelTypeFeatures,
92569245
their_features: &InitFeatures, msg: &msgs::OpenChannelV2,
9257-
funding_inputs: Vec<(TxIn, TransactionU16LenLimited)>, total_witness_weight: Weight,
92589246
user_id: u128, config: &UserConfig, current_chain_height: u32, logger: &L,
92599247
) -> Result<Self, ChannelError>
92609248
where ES::Target: EntropySource,
92619249
F::Target: FeeEstimator,
92629250
L::Target: Logger,
92639251
{
9264-
let funding_satoshis = calculate_our_funding_satoshis(
9265-
false, &funding_inputs, total_witness_weight, msg.funding_feerate_sat_per_1000_weight,
9266-
msg.common_fields.dust_limit_satoshis
9267-
).map_err(|_| ChannelError::Close(
9268-
(
9269-
"Failed to accept channel".to_string(),
9270-
ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) },
9271-
)))?;
9272-
let channel_value_satoshis = funding_satoshis.saturating_add(msg.common_fields.funding_satoshis);
9252+
// TODO(dual_funding): Take these as input once supported
9253+
let our_funding_satoshis = 0u64;
9254+
let our_funding_inputs = Vec::new();
9255+
9256+
let channel_value_satoshis = our_funding_satoshis.saturating_add(msg.common_fields.funding_satoshis);
92739257
let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
92749258
channel_value_satoshis, msg.common_fields.dust_limit_satoshis);
92759259
let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
@@ -9303,7 +9287,7 @@ impl<SP: Deref> PendingV2Channel<SP> where SP::Target: SignerProvider {
93039287
logger,
93049288
false,
93059289

9306-
funding_satoshis,
9290+
our_funding_satoshis,
93079291

93089292
counterparty_pubkeys,
93099293
channel_type,
@@ -9318,10 +9302,10 @@ impl<SP: Deref> PendingV2Channel<SP> where SP::Target: SignerProvider {
93189302
context.channel_id = channel_id;
93199303

93209304
let dual_funding_context = DualFundingChannelContext {
9321-
our_funding_satoshis: funding_satoshis,
9305+
our_funding_satoshis: our_funding_satoshis,
93229306
funding_tx_locktime: LockTime::from_consensus(msg.locktime),
93239307
funding_feerate_sat_per_1000_weight: msg.funding_feerate_sat_per_1000_weight,
9324-
our_funding_inputs: funding_inputs.clone(),
9308+
our_funding_inputs: our_funding_inputs.clone(),
93259309
};
93269310

93279311
let interactive_tx_constructor = Some(InteractiveTxConstructor::new(
@@ -9333,7 +9317,7 @@ impl<SP: Deref> PendingV2Channel<SP> where SP::Target: SignerProvider {
93339317
feerate_sat_per_kw: dual_funding_context.funding_feerate_sat_per_1000_weight,
93349318
funding_tx_locktime: dual_funding_context.funding_tx_locktime,
93359319
is_initiator: false,
9336-
inputs_to_contribute: funding_inputs,
9320+
inputs_to_contribute: our_funding_inputs,
93379321
outputs_to_contribute: Vec::new(),
93389322
expected_remote_shared_funding_output: Some((context.get_funding_redeemscript().to_p2wsh(), context.channel_value_satoshis)),
93399323
}
@@ -12238,4 +12222,40 @@ mod tests {
1223812222
assert_eq!(node_a_chan.context.channel_state, ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::THEIR_CHANNEL_READY));
1223912223
assert!(node_a_chan.check_get_channel_ready(0, &&logger).is_some());
1224012224
}
12225+
12226+
#[test]
12227+
fn test_estimate_v2_funding_transaction_fee() {
12228+
use crate::ln::channel::estimate_v2_funding_transaction_fee;
12229+
use bitcoin::Weight;
12230+
12231+
// 2 inputs with weight 300, initiator, 2000 sat/kw feerate
12232+
assert_eq!(
12233+
estimate_v2_funding_transaction_fee(true, 2, Weight::from_wu(300), 2000),
12234+
1668
12235+
);
12236+
12237+
// higher feerate
12238+
assert_eq!(
12239+
estimate_v2_funding_transaction_fee(true, 2, Weight::from_wu(300), 3000),
12240+
2502
12241+
);
12242+
12243+
// only 1 input
12244+
assert_eq!(
12245+
estimate_v2_funding_transaction_fee(true, 1, Weight::from_wu(300), 2000),
12246+
1348
12247+
);
12248+
12249+
// 0 input weight
12250+
assert_eq!(
12251+
estimate_v2_funding_transaction_fee(true, 1, Weight::from_wu(0), 2000),
12252+
748
12253+
);
12254+
12255+
// not initiator
12256+
assert_eq!(
12257+
estimate_v2_funding_transaction_fee(false, 1, Weight::from_wu(0), 2000),
12258+
320
12259+
);
12260+
}
1224112261
}

lightning/src/ln/channelmanager.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
//! imply it needs to fail HTLCs/payments/channels it manages).
1919

2020
use bitcoin::block::Header;
21-
use bitcoin::transaction::{Transaction, TxIn};
21+
use bitcoin::transaction::Transaction;
2222
use bitcoin::constants::ChainHash;
2323
use bitcoin::key::constants::SECRET_KEY_SIZE;
2424
use bitcoin::network::Network;
@@ -30,7 +30,7 @@ use bitcoin::hash_types::{BlockHash, Txid};
3030

3131
use bitcoin::secp256k1::{SecretKey,PublicKey};
3232
use bitcoin::secp256k1::Secp256k1;
33-
use bitcoin::{secp256k1, Sequence, Weight};
33+
use bitcoin::{secp256k1, Sequence};
3434

3535
use crate::events::FundingInfo;
3636
use crate::blinded_path::message::{AsyncPaymentsContext, MessageContext, OffersContext};
@@ -83,7 +83,6 @@ use crate::util::wakers::{Future, Notifier};
8383
use crate::util::scid_utils::fake_scid;
8484
use crate::util::string::UntrustedString;
8585
use crate::util::ser::{BigSize, FixedLengthReader, Readable, ReadableArgs, MaybeReadable, Writeable, Writer, VecWriter};
86-
use crate::util::ser::TransactionU16LenLimited;
8786
use crate::util::logger::{Level, Logger, WithContext};
8887
use crate::util::errors::APIError;
8988
#[cfg(async_payments)] use {
@@ -7700,7 +7699,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
77007699
/// [`Event::OpenChannelRequest`]: events::Event::OpenChannelRequest
77017700
/// [`Event::ChannelClosed::user_channel_id`]: events::Event::ChannelClosed::user_channel_id
77027701
pub fn accept_inbound_channel(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, user_channel_id: u128) -> Result<(), APIError> {
7703-
self.do_accept_inbound_channel(temporary_channel_id, counterparty_node_id, false, user_channel_id, vec![], Weight::from_wu(0))
7702+
self.do_accept_inbound_channel(temporary_channel_id, counterparty_node_id, false, user_channel_id)
77047703
}
77057704

77067705
/// Accepts a request to open a channel after a [`events::Event::OpenChannelRequest`], treating
@@ -7722,13 +7721,13 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
77227721
/// [`Event::OpenChannelRequest`]: events::Event::OpenChannelRequest
77237722
/// [`Event::ChannelClosed::user_channel_id`]: events::Event::ChannelClosed::user_channel_id
77247723
pub fn accept_inbound_channel_from_trusted_peer_0conf(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, user_channel_id: u128) -> Result<(), APIError> {
7725-
self.do_accept_inbound_channel(temporary_channel_id, counterparty_node_id, true, user_channel_id, vec![], Weight::from_wu(0))
7724+
self.do_accept_inbound_channel(temporary_channel_id, counterparty_node_id, true, user_channel_id)
77267725
}
77277726

7727+
/// TODO(dual_funding): Allow contributions, pass intended amount and inputs
77287728
fn do_accept_inbound_channel(
77297729
&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, accept_0conf: bool,
7730-
user_channel_id: u128, _funding_inputs: Vec<(TxIn, TransactionU16LenLimited)>,
7731-
_total_witness_weight: Weight,
7730+
user_channel_id: u128,
77327731
) -> Result<(), APIError> {
77337732
let logger = WithContext::from(&self.logger, Some(*counterparty_node_id), Some(*temporary_channel_id), None);
77347733
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
@@ -7778,7 +7777,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
77787777
&self.fee_estimator, &self.entropy_source, &self.signer_provider,
77797778
self.get_our_node_id(), *counterparty_node_id,
77807779
&self.channel_type_features(), &peer_state.latest_features,
7781-
&open_channel_msg, _funding_inputs, _total_witness_weight,
7780+
&open_channel_msg,
77827781
user_channel_id, &self.default_configuration, best_block_height,
77837782
&self.logger,
77847783
).map_err(|_| MsgHandleErrInternal::from_chan_no_close(
@@ -8064,7 +8063,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
80648063
let channel = PendingV2Channel::new_inbound(
80658064
&self.fee_estimator, &self.entropy_source, &self.signer_provider,
80668065
self.get_our_node_id(), *counterparty_node_id, &self.channel_type_features(),
8067-
&peer_state.latest_features, msg, vec![], Weight::from_wu(0), user_channel_id,
8066+
&peer_state.latest_features, msg, user_channel_id,
80688067
&self.default_configuration, best_block_height, &self.logger,
80698068
).map_err(|e| MsgHandleErrInternal::from_chan_no_close(e, msg.common_fields.temporary_channel_id))?;
80708069
let message_send_event = events::MessageSendEvent::SendAcceptChannelV2 {

lightning/src/ln/dual_funding_tests.rs

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,28 @@
1111
1212
#[cfg(dual_funding)]
1313
use {
14-
crate::chain::chaininterface::{ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator},
14+
crate::chain::chaininterface::{ConfirmationTarget, LowerBoundedFeeEstimator},
1515
crate::events::{Event, MessageSendEvent, MessageSendEventsProvider},
1616
crate::ln::chan_utils::{
1717
make_funding_redeemscript, ChannelPublicKeys, ChannelTransactionParameters,
1818
CounterpartyChannelTransactionParameters,
1919
},
20-
crate::ln::channel::{
21-
calculate_our_funding_satoshis, PendingV2Channel, MIN_CHAN_DUST_LIMIT_SATOSHIS,
22-
},
20+
crate::ln::channel::PendingV2Channel,
2321
crate::ln::channel_keys::{DelayedPaymentBasepoint, HtlcBasepoint, RevocationBasepoint},
2422
crate::ln::functional_test_utils::*,
2523
crate::ln::msgs::ChannelMessageHandler,
2624
crate::ln::msgs::{CommitmentSigned, TxAddInput, TxAddOutput, TxComplete},
2725
crate::ln::types::ChannelId,
2826
crate::prelude::*,
29-
crate::sign::{ChannelSigner as _, P2WPKH_WITNESS_WEIGHT},
27+
crate::sign::ChannelSigner as _,
3028
crate::util::ser::TransactionU16LenLimited,
3129
crate::util::test_utils,
32-
bitcoin::Weight,
3330
};
3431

3532
#[cfg(dual_funding)]
3633
// Dual-funding: V2 Channel Establishment Tests
3734
struct V2ChannelEstablishmentTestSession {
35+
funding_input_sats: u64,
3836
initiator_input_value_satoshis: u64,
3937
}
4038

@@ -60,17 +58,7 @@ fn do_test_v2_channel_establishment(
6058
.collect();
6159

6260
// Alice creates a dual-funded channel as initiator.
63-
let funding_feerate = node_cfgs[0]
64-
.fee_estimator
65-
.get_est_sat_per_1000_weight(ConfirmationTarget::NonAnchorChannelFee);
66-
let funding_satoshis = calculate_our_funding_satoshis(
67-
true,
68-
&initiator_funding_inputs[..],
69-
Weight::from_wu(P2WPKH_WITNESS_WEIGHT),
70-
funding_feerate,
71-
MIN_CHAN_DUST_LIMIT_SATOSHIS,
72-
)
73-
.unwrap();
61+
let funding_satoshis = session.funding_input_sats;
7462
let mut channel = PendingV2Channel::new_outbound(
7563
&LowerBoundedFeeEstimator(node_cfgs[0].fee_estimator),
7664
&nodes[0].node.entropy_source,
@@ -263,12 +251,18 @@ fn do_test_v2_channel_establishment(
263251
fn test_v2_channel_establishment() {
264252
// Only initiator contributes, no persist pending
265253
do_test_v2_channel_establishment(
266-
V2ChannelEstablishmentTestSession { initiator_input_value_satoshis: 100_000 },
254+
V2ChannelEstablishmentTestSession {
255+
funding_input_sats: 100_000,
256+
initiator_input_value_satoshis: 150_000,
257+
},
267258
false,
268259
);
269260
// Only initiator contributes, persist pending
270261
do_test_v2_channel_establishment(
271-
V2ChannelEstablishmentTestSession { initiator_input_value_satoshis: 100_000 },
262+
V2ChannelEstablishmentTestSession {
263+
funding_input_sats: 100_000,
264+
initiator_input_value_satoshis: 150_000,
265+
},
272266
true,
273267
);
274268
}

0 commit comments

Comments
 (0)