Skip to content

Commit 0f00ce8

Browse files
committed
wip pass funding inputs
1 parent 47d8f17 commit 0f00ce8

File tree

4 files changed

+176
-45
lines changed

4 files changed

+176
-45
lines changed

lightning/src/ln/channel.rs

+47-8
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use crate::ln::types::ChannelId;
3030
use crate::types::payment::{PaymentPreimage, PaymentHash};
3131
use crate::types::features::{ChannelTypeFeatures, InitFeatures};
3232
use crate::ln::interactivetxs::{
33-
calculate_change_output_value, get_output_weight, AbortReason, HandleTxCompleteResult, InteractiveTxConstructor,
33+
calculate_change_output_value, estimate_input_weight, get_output_weight, AbortReason, HandleTxCompleteResult, InteractiveTxConstructor,
3434
InteractiveTxConstructorArgs, InteractiveTxMessageSend, InteractiveTxSigningSession, InteractiveTxMessageSendResult,
3535
OutputOwned, SharedOwnedOutput, TX_COMMON_FIELDS_WEIGHT,
3636
};
@@ -5035,6 +5035,41 @@ fn get_v2_channel_reserve_satoshis(channel_value_satoshis: u64, dust_limit_satos
50355035
cmp::min(channel_value_satoshis, cmp::max(q, dust_limit_satoshis))
50365036
}
50375037

5038+
fn calculate_our_funding_satoshis(
5039+
is_initiator: bool, funding_inputs: &[(TxIn, TransactionU16LenLimited, Weight)], funding_outputs: &[TxOut],
5040+
funding_feerate_sat_per_1000_weight: u32, holder_dust_limit_satoshis: u64,
5041+
) -> Result<u64, APIError> {
5042+
let mut total_input_satoshis = 0u64;
5043+
let mut our_contributed_weight = 0u64;
5044+
5045+
for (idx, input) in funding_inputs.iter().enumerate() {
5046+
if let Some(output) = input.1.as_transaction().output.get(input.0.previous_output.vout as usize) {
5047+
total_input_satoshis = total_input_satoshis.saturating_add(output.value.to_sat());
5048+
our_contributed_weight = our_contributed_weight.saturating_add(estimate_input_weight(output).to_wu());
5049+
} else {
5050+
return Err(APIError::APIMisuseError {
5051+
err: format!("Transaction with txid {} does not have an output with vout of {} corresponding to TxIn at funding_inputs[{}]",
5052+
input.1.as_transaction().compute_txid(), input.0.previous_output.vout, idx) });
5053+
}
5054+
}
5055+
our_contributed_weight = our_contributed_weight.saturating_add(funding_outputs.iter().fold(0u64, |weight, txout| {
5056+
weight.saturating_add(get_output_weight(&txout.script_pubkey).to_wu())
5057+
}));
5058+
5059+
// If we are the initiator, we must pay for weight of all common fields in the funding transaction.
5060+
if is_initiator {
5061+
our_contributed_weight = our_contributed_weight.saturating_add(TX_COMMON_FIELDS_WEIGHT);
5062+
}
5063+
5064+
let funding_satoshis = total_input_satoshis
5065+
.saturating_sub(fee_for_weight(funding_feerate_sat_per_1000_weight, our_contributed_weight));
5066+
if funding_satoshis < holder_dust_limit_satoshis {
5067+
Ok(0)
5068+
} else {
5069+
Ok(funding_satoshis)
5070+
}
5071+
}
5072+
50385073
/// Estimate our part of the fee of the new funding transaction.
50395074
/// input_count: Number of contributed inputs.
50405075
/// witness_weight: The witness weight for contributed inputs.
@@ -5139,7 +5174,7 @@ pub(super) struct DualFundingChannelContext {
51395174
///
51405175
/// Note that this field may be emptied once the interactive negotiation has been started.
51415176
#[allow(dead_code)] // TODO(dual_funding): Remove once contribution to V2 channels is enabled.
5142-
pub our_funding_inputs: Vec<(TxIn, TransactionU16LenLimited)>,
5177+
pub our_funding_inputs: Vec<(TxIn, TransactionU16LenLimited, Weight)>,
51435178
}
51445179

51455180
// Holder designates channel data owned for the benefit of the user client.
@@ -10329,7 +10364,7 @@ impl<SP: Deref> PendingV2Channel<SP> where SP::Target: SignerProvider {
1032910364
pub fn new_outbound<ES: Deref, F: Deref, L: Deref>(
1033010365
fee_estimator: &LowerBoundedFeeEstimator<F>, entropy_source: &ES, signer_provider: &SP,
1033110366
counterparty_node_id: PublicKey, their_features: &InitFeatures, funding_satoshis: u64,
10332-
funding_inputs: Vec<(TxIn, TransactionU16LenLimited)>, user_id: u128, config: &UserConfig,
10367+
funding_inputs: Vec<(TxIn, TransactionU16LenLimited, Weight)>, user_id: u128, config: &UserConfig,
1033310368
current_chain_height: u32, outbound_scid_alias: u64, funding_confirmation_target: ConfirmationTarget,
1033410369
logger: L,
1033510370
) -> Result<Self, APIError>
@@ -10469,16 +10504,20 @@ impl<SP: Deref> PendingV2Channel<SP> where SP::Target: SignerProvider {
1046910504
pub fn new_inbound<ES: Deref, F: Deref, L: Deref>(
1047010505
fee_estimator: &LowerBoundedFeeEstimator<F>, entropy_source: &ES, signer_provider: &SP,
1047110506
holder_node_id: PublicKey, counterparty_node_id: PublicKey, our_supported_features: &ChannelTypeFeatures,
10472-
their_features: &InitFeatures, msg: &msgs::OpenChannelV2,
10473-
user_id: u128, config: &UserConfig, current_chain_height: u32, logger: &L,
10507+
their_features: &InitFeatures, msg: &msgs::OpenChannelV2, user_id: u128, config: &UserConfig,
10508+
current_chain_height: u32, logger: &L, our_funding_inputs: Vec<(TxIn, TransactionU16LenLimited, Weight)>,
1047410509
) -> Result<Self, ChannelError>
1047510510
where ES::Target: EntropySource,
1047610511
F::Target: FeeEstimator,
1047710512
L::Target: Logger,
1047810513
{
10479-
// TODO(dual_funding): Take these as input once supported
10480-
let our_funding_satoshis = 0u64;
10481-
let our_funding_inputs = Vec::new();
10514+
let our_funding_satoshis = calculate_our_funding_satoshis(
10515+
false, &our_funding_inputs, &[], msg.funding_feerate_sat_per_1000_weight,
10516+
msg.common_fields.dust_limit_satoshis,
10517+
).map_err(|_| ChannelError::Close((
10518+
"Failed to accept channel".to_string(),
10519+
ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) }
10520+
)))?;
1048210521

1048310522
let channel_value_satoshis = our_funding_satoshis.saturating_add(msg.common_fields.funding_satoshis);
1048410523
let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(

lightning/src/ln/channelmanager.rs

+55-9
Original file line numberDiff line numberDiff line change
@@ -30,9 +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};
34-
#[cfg(splicing)]
35-
use bitcoin::{TxIn, Weight};
33+
use bitcoin::{secp256k1, Sequence, TxIn, Weight};
3634

3735
use crate::events::{FundingInfo, PaidBolt12Invoice};
3836
use crate::blinded_path::message::{AsyncPaymentsContext, MessageContext, OffersContext};
@@ -84,7 +82,7 @@ use crate::util::config::{ChannelConfig, ChannelConfigUpdate, ChannelConfigOverr
8482
use crate::util::wakers::{Future, Notifier};
8583
use crate::util::scid_utils::fake_scid;
8684
use crate::util::string::UntrustedString;
87-
use crate::util::ser::{BigSize, FixedLengthReader, LengthReadable, Readable, ReadableArgs, MaybeReadable, Writeable, Writer, VecWriter};
85+
use crate::util::ser::{BigSize, FixedLengthReader, LengthReadable, MaybeReadable, Readable, ReadableArgs, TransactionU16LenLimited, VecWriter, Writeable, Writer};
8886
use crate::util::logger::{Level, Logger, WithContext};
8987
use crate::util::errors::APIError;
9088

@@ -7874,7 +7872,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
78747872
/// [`Event::OpenChannelRequest`]: events::Event::OpenChannelRequest
78757873
/// [`Event::ChannelClosed::user_channel_id`]: events::Event::ChannelClosed::user_channel_id
78767874
pub fn accept_inbound_channel(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, user_channel_id: u128, config_overrides: Option<ChannelConfigOverrides>) -> Result<(), APIError> {
7877-
self.do_accept_inbound_channel(temporary_channel_id, counterparty_node_id, false, user_channel_id, config_overrides)
7875+
self.do_accept_inbound_channel(temporary_channel_id, counterparty_node_id, false, user_channel_id, config_overrides, vec![])
78787876
}
78797877

78807878
/// Accepts a request to open a channel after a [`events::Event::OpenChannelRequest`], treating
@@ -7896,13 +7894,61 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
78967894
/// [`Event::OpenChannelRequest`]: events::Event::OpenChannelRequest
78977895
/// [`Event::ChannelClosed::user_channel_id`]: events::Event::ChannelClosed::user_channel_id
78987896
pub fn accept_inbound_channel_from_trusted_peer_0conf(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, user_channel_id: u128, config_overrides: Option<ChannelConfigOverrides>) -> Result<(), APIError> {
7899-
self.do_accept_inbound_channel(temporary_channel_id, counterparty_node_id, true, user_channel_id, config_overrides)
7897+
self.do_accept_inbound_channel(temporary_channel_id, counterparty_node_id, true, user_channel_id, config_overrides, vec![])
7898+
}
7899+
7900+
/// Accepts a request to open a dual-funded channel with a contribution provided by us after an
7901+
/// [`Event::OpenChannelRequest`].
7902+
///
7903+
/// The `temporary_channel_id` parameter indicates which inbound channel should be accepted,
7904+
/// and the `counterparty_node_id` parameter is the id of the peer which has requested to open
7905+
/// the channel.
7906+
///
7907+
/// The `user_channel_id` parameter will be provided back in
7908+
/// [`Event::ChannelClosed::user_channel_id`] to allow tracking of which events correspond
7909+
/// with which `accept_inbound_channel_*` call.
7910+
///
7911+
/// The `funding_inputs` parameter provides the `txin`s along with their previous transactions, and
7912+
/// a corresponding witness weight for each input that will be used to contribute towards our
7913+
/// portion of the channel value. Our contribution will be calculated as the total value of these
7914+
/// inputs minus the fees we need to cover for the interactive funding transaction. The witness
7915+
/// weights must correspond to the witnesses you will provide through [`ChannelManager::funding_transaction_signed`]
7916+
/// after receiving [`Event::FundingTransactionReadyForSigning`].
7917+
///
7918+
/// Note that this method will return an error and reject the channel if it requires support for
7919+
/// zero confirmations.
7920+
// TODO(dual_funding): Discussion on complications with 0conf dual-funded channels where "locking"
7921+
// of UTXOs used for funding would be required and other issues.
7922+
// See https://diyhpl.us/~bryan/irc/bitcoin/bitcoin-dev/linuxfoundation-pipermail/lightning-dev/2023-May/003922.txt
7923+
///
7924+
/// [`Event::OpenChannelRequest`]: events::Event::OpenChannelRequest
7925+
/// [`Event::ChannelClosed::user_channel_id`]: events::Event::ChannelClosed::user_channel_id
7926+
/// [`Event::FundingTransactionReadyForSigning`]: events::Event::FundingTransactionReadyForSigning
7927+
/// [`ChannelManager::funding_transaction_signed`]: ChannelManager::funding_transaction_signed
7928+
pub fn accept_inbound_channel_with_contribution(
7929+
&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, user_channel_id: u128,
7930+
config_overrides: Option<ChannelConfigOverrides>, funding_inputs: Vec<(TxIn, Transaction, Weight)>
7931+
) -> Result<(), APIError> {
7932+
let funding_inputs = Self::length_limit_holder_input_prev_txs(funding_inputs)?;
7933+
self.do_accept_inbound_channel(temporary_channel_id, counterparty_node_id, false, user_channel_id,
7934+
config_overrides, funding_inputs)
7935+
}
7936+
7937+
fn length_limit_holder_input_prev_txs(funding_inputs: Vec<(TxIn, Transaction, Weight)>) -> Result<Vec<(TxIn, TransactionU16LenLimited, Weight)>, APIError> {
7938+
funding_inputs.into_iter().map(|(txin, tx, witness_weight)| {
7939+
match TransactionU16LenLimited::new(tx) {
7940+
Ok(tx) => Ok((txin, tx, witness_weight)),
7941+
Err(err) => Err(err)
7942+
}
7943+
}).collect::<Result<Vec<(TxIn, TransactionU16LenLimited, Weight)>, ()>>()
7944+
.map_err(|_| APIError::APIMisuseError { err: "One or more transactions had a serialized length exceeding 65535 bytes".into() })
79007945
}
79017946

79027947
/// TODO(dual_funding): Allow contributions, pass intended amount and inputs
79037948
fn do_accept_inbound_channel(
79047949
&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, accept_0conf: bool,
7905-
user_channel_id: u128, config_overrides: Option<ChannelConfigOverrides>
7950+
user_channel_id: u128, config_overrides: Option<ChannelConfigOverrides>,
7951+
funding_inputs: Vec<(TxIn, TransactionU16LenLimited, Weight)>
79067952
) -> Result<(), APIError> {
79077953

79087954
let mut config = self.default_configuration.clone();
@@ -7961,7 +8007,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
79618007
&self.channel_type_features(), &peer_state.latest_features,
79628008
&open_channel_msg,
79638009
user_channel_id, &config, best_block_height,
7964-
&self.logger,
8010+
&self.logger, funding_inputs,
79658011
).map_err(|_| MsgHandleErrInternal::from_chan_no_close(
79668012
ChannelError::Close(
79678013
(
@@ -8242,7 +8288,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
82428288
&self.fee_estimator, &self.entropy_source, &self.signer_provider,
82438289
self.get_our_node_id(), *counterparty_node_id, &self.channel_type_features(),
82448290
&peer_state.latest_features, msg, user_channel_id,
8245-
&self.default_configuration, best_block_height, &self.logger,
8291+
&self.default_configuration, best_block_height, &self.logger, vec![],
82468292
).map_err(|e| MsgHandleErrInternal::from_chan_no_close(e, msg.common_fields.temporary_channel_id))?;
82478293
let message_send_event = MessageSendEvent::SendAcceptChannelV2 {
82488294
node_id: *counterparty_node_id,

lightning/src/ln/dual_funding_tests.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ fn do_test_v2_channel_establishment(session: V2ChannelEstablishmentTestSession)
5151
&[session.initiator_input_value_satoshis],
5252
)
5353
.into_iter()
54-
.map(|(txin, tx, _)| (txin, TransactionU16LenLimited::new(tx).unwrap()))
54+
.map(|(txin, tx, weight)| (txin, TransactionU16LenLimited::new(tx).unwrap(), weight))
5555
.collect();
5656

5757
// Alice creates a dual-funded channel as initiator.

0 commit comments

Comments
 (0)