Skip to content

Commit 5bd6e42

Browse files
committed
wip: add channelmanager dual-funding support
1 parent 7c5321f commit 5bd6e42

File tree

2 files changed

+268
-2
lines changed

2 files changed

+268
-2
lines changed

Diff for: lightning/src/ln/channelmanager.rs

+252-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use bitcoin::hash_types::{BlockHash, Txid};
2828

2929
use bitcoin::secp256k1::{SecretKey,PublicKey};
3030
use bitcoin::secp256k1::Secp256k1;
31-
use bitcoin::{LockTime, secp256k1, Sequence};
31+
use bitcoin::{LockTime, secp256k1, Sequence, Script};
3232

3333
use crate::chain;
3434
use crate::chain::{Confirm, ChannelMonitorUpdateStatus, Watch, BestBlock};
@@ -40,7 +40,7 @@ use crate::events::{Event, EventHandler, EventsProvider, MessageSendEvent, Messa
4040
// Since this struct is returned in `list_channels` methods, expose it here in case users want to
4141
// construct one themselves.
4242
use crate::ln::{inbound_payment, PaymentHash, PaymentPreimage, PaymentSecret};
43-
use crate::ln::channel::{Channel, OutboundV1Channel, InboundV1Channel, ChannelInterface, ChannelError, ChannelUpdateStatus, UpdateFulfillCommitFetch};
43+
use crate::ln::channel::{Channel, OutboundV1Channel, InboundV1Channel, OutboundV2Channel, InboundV2Channel, ChannelInterface, ChannelError, ChannelUpdateStatus, UpdateFulfillCommitFetch};
4444
use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
4545
#[cfg(any(feature = "_test_utils", test))]
4646
use crate::ln::features::InvoiceFeatures;
@@ -80,6 +80,8 @@ use core::ops::Deref;
8080
pub use crate::ln::outbound_payment::{PaymentSendFailure, Retry, RetryableSendFailure, RecipientOnionFields};
8181
use crate::ln::script::ShutdownScript;
8282

83+
use super::channel::DualFundingUtxo;
84+
8385
// We hold various information about HTLC relay in the HTLC objects in Channel itself:
8486
//
8587
// Upon receipt of an HTLC from a peer, we'll give it a PendingHTLCStatus indicating if it should
@@ -556,6 +558,27 @@ pub(super) struct PeerState<Signer: ChannelSigner> {
556558
/// been assigned a `channel_id`, the entry in this map is removed and one is created in
557559
/// `channel_by_id`.
558560
pub(super) inbound_v1_channel_by_id: HashMap<[u8; 32], InboundV1Channel<Signer>>,
561+
/// `(temporary_)channel_id` -> `OutboundV2Channel`.
562+
///
563+
/// Holds all outbound V2 channels where the peer is the counterparty. V2 channels are assigned
564+
/// a `channel_id` before a funding transaction is created interactively as it's derived from
565+
/// both parties' revocation basepoints once these are known. Hence, this map's keys are either
566+
/// temporary channel IDs or channel IDs.
567+
///
568+
/// The entries in this map are only moved to `channel_by_id` once interactive transaction
569+
/// construction completes successfully.
570+
pub(super) outbound_v2_channel_by_id: HashMap<[u8; 32], OutboundV2Channel<Signer>>,
571+
/// `channel_id` -> `InboundV2Channel`.
572+
///
573+
/// Holds all inbound V2 channels where the peer is the counterparty. V2 channels are assigned
574+
/// a `channel_id` before a funding transaction is created interactively as it's derived from
575+
/// both parties' revocation basepoints once these are known. At the stage of receiving an
576+
/// `open_channel2` request, we have enough information to derive the `channel_id`. Hence, this
577+
/// map's keys are always `channel_id`s.
578+
///
579+
/// The entries in this map are only moved to `channel_by_id` once interactive transaction
580+
/// construction completes successfully.
581+
pub(super) inbound_v2_channel_by_id: HashMap<[u8; 32], InboundV2Channel<Signer>>,
559582
/// The latest `InitFeatures` we heard from the peer.
560583
latest_features: InitFeatures,
561584
/// Messages to send to the peer - pushed to in the same lock that they are generated in (except
@@ -1913,6 +1936,102 @@ where
19131936
Ok(temporary_channel_id)
19141937
}
19151938

1939+
1940+
/// Creates a new outbound dual-funded channel to the given remote node and with the given value
1941+
/// contributed by us.
1942+
///
1943+
/// `user_channel_id` will be provided back as in
1944+
/// [`Event::FundingGenerationReady::user_channel_id`] to allow tracking of which events
1945+
/// correspond with which `create_channel` call. Note that the `user_channel_id` defaults to a
1946+
/// randomized value for inbound channels. `user_channel_id` has no meaning inside of LDK, it
1947+
/// is simply copied to events and otherwise ignored.
1948+
///
1949+
/// `funnding_satoshis` is the amount we are contributing to the channel.
1950+
/// Raises [`APIError::APIMisuseError`] when `funding_satoshis` > 2**24.
1951+
///
1952+
/// The `funding_inputs` parameter accepts UTXOs in the form of [`DualFundingUtxo`] which will
1953+
/// be used to contribute `funding_satoshis` towards the channel (minus any mining fees due).
1954+
/// Raises [`APIError::APIMisuseError`] if the total value of the provided `funding_inputs` is
1955+
/// less than `funding_satoshis`.
1956+
// TODO(dual_funding): Describe error relating to inputs not being able to cover fees payable by us.
1957+
///
1958+
/// The `change_script_pubkey` parameter provides a destination for the change output if any value
1959+
/// is remaining (greater than dust) after `funding_satoshis` and fees payable are satisfied by
1960+
/// `funding_inputs`
1961+
// TODO(dual_funding): We could allow a list of such outputs to be provided so that the user may
1962+
/// be able to do some more interesting things at the same time as funding a channel, like making
1963+
/// some low priority on-chain payment.
1964+
///
1965+
/// Raises [`APIError::ChannelUnavailable`] if the channel cannot be opened due to failing to
1966+
/// generate a shutdown scriptpubkey or destination script set by
1967+
/// [`SignerProvider::get_shutdown_scriptpubkey`] or [`SignerProvider::get_destination_script`].
1968+
///
1969+
/// Note that we do not check if you are currently connected to the given peer. If no
1970+
/// connection is available, the outbound `open_channel` message may fail to send, resulting in
1971+
/// the channel eventually being silently forgotten (dropped on reload).
1972+
///
1973+
/// Returns the new Channel's temporary `channel_id`. This ID will appear as
1974+
/// [`Event::FundingGenerationReady::temporary_channel_id`] and in
1975+
/// [`ChannelDetails::channel_id`] until after
1976+
/// [`ChannelManager::funding_transaction_generated`] is called, swapping the Channel's ID for
1977+
/// one derived from the funding transaction's TXID. If the counterparty rejects the channel
1978+
/// immediately, this temporary ID will appear in [`Event::ChannelClosed::channel_id`].
1979+
///
1980+
/// [`Event::FundingGenerationReady::user_channel_id`]: events::Event::FundingGenerationReady::user_channel_id
1981+
/// [`Event::FundingGenerationReady::temporary_channel_id`]: events::Event::FundingGenerationReady::temporary_channel_id
1982+
/// [`Event::ChannelClosed::channel_id`]: events::Event::ChannelClosed::channel_id
1983+
pub fn create_dual_funded_channel(&self, their_network_key: PublicKey, funding_satoshis: u64,
1984+
funding_inputs: Vec<DualFundingUtxo>, change_script_pubkey: Script,
1985+
user_channel_id: u128, override_config: Option<UserConfig>) -> Result<[u8; 32], APIError>
1986+
{
1987+
Self::dual_funding_amount_checks(funding_satoshis, &funding_inputs);
1988+
1989+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
1990+
// We want to make sure the lock is actually acquired by PersistenceNotifierGuard.
1991+
debug_assert!(&self.total_consistency_lock.try_write().is_err());
1992+
1993+
let per_peer_state = self.per_peer_state.read().unwrap();
1994+
1995+
let peer_state_mutex = per_peer_state.get(&their_network_key)
1996+
.ok_or_else(|| APIError::APIMisuseError{ err: format!("Not connected to node: {}", their_network_key) })?;
1997+
1998+
let mut peer_state = peer_state_mutex.lock().unwrap();
1999+
let channel = {
2000+
let outbound_scid_alias = self.create_and_insert_outbound_scid_alias();
2001+
let their_features = &peer_state.latest_features;
2002+
let config = if override_config.is_some() { override_config.as_ref().unwrap() } else { &self.default_configuration };
2003+
match OutboundV2Channel::new(&self.fee_estimator, &self.entropy_source, &self.signer_provider, their_network_key,
2004+
their_features, funding_satoshis, funding_inputs, change_script_pubkey, user_channel_id, config,
2005+
self.best_block.read().unwrap().height(), outbound_scid_alias, true)
2006+
{
2007+
Ok(res) => res,
2008+
Err(e) => {
2009+
self.outbound_scid_aliases.lock().unwrap().remove(&outbound_scid_alias);
2010+
return Err(e);
2011+
},
2012+
}
2013+
};
2014+
let res = channel.get_open_channel_v2(self.genesis_hash.clone());
2015+
2016+
let temporary_channel_id = channel.channel_id();
2017+
match peer_state.outbound_v2_channel_by_id.entry(temporary_channel_id) {
2018+
hash_map::Entry::Occupied(_) => {
2019+
if cfg!(fuzzing) {
2020+
return Err(APIError::APIMisuseError { err: "Fuzzy bad RNG".to_owned() });
2021+
} else {
2022+
panic!("RNG is bad???");
2023+
}
2024+
},
2025+
hash_map::Entry::Vacant(entry) => { entry.insert(channel); }
2026+
}
2027+
2028+
peer_state.pending_msg_events.push(events::MessageSendEvent::SendOpenChannelV2 {
2029+
node_id: their_network_key,
2030+
msg: res,
2031+
});
2032+
Ok(temporary_channel_id)
2033+
}
2034+
19162035
fn list_funded_channels_with_filter<Fn: FnMut(&(&[u8; 32], &Channel<<SP::Target as SignerProvider>::Signer>)) -> bool + Copy>(&self, f: Fn) -> Vec<ChannelDetails> {
19172036
// Allocate our best estimate of the number of channels we have in the `res`
19182037
// Vec. Sadly the `short_to_chan_info` map doesn't cover channels without
@@ -4687,6 +4806,128 @@ where
46874806
Ok(())
46884807
}
46894808

4809+
/// Accepts a request to open a dual-funded channel after an [`Event::OpenChannelV2Request`].
4810+
///
4811+
/// The `temporary_channel_id` parameter indicates which inbound channel should be accepted,
4812+
/// and the `counterparty_node_id` parameter is the id of the peer which has requested to open
4813+
/// the channel.
4814+
///
4815+
/// The `user_channel_id` parameter will be provided back in
4816+
/// [`Event::ChannelClosed::user_channel_id`] to allow tracking of which events correspond
4817+
/// with which `accept_inbound_dual_funded_channel`/`accept_inbound_dual_funded_channel_from_trusted_peer_0conf` call.
4818+
///
4819+
/// `funnding_satoshis` is the amount we are contributing to the channel.
4820+
/// Raises [`APIError::APIMisuseError`] when `funding_satoshis` > 2**24.
4821+
///
4822+
/// The `funding_inputs` parameter accepts UTXOs in the form of [`DualFundingUtxo`] which will
4823+
/// be used to contribute `funding_satoshis` towards the channel (minus any mining fees due).
4824+
/// Raises [`APIError::APIMisuseError`] if the total value of the provided `funding_inputs` is
4825+
/// less than `funding_satoshis`.
4826+
// TODO(dual_funding): Describe error relating to inputs not being able to cover fees payable by us.
4827+
///
4828+
/// The `change_script_pubkey` parameter provides a destination for the change output if any value
4829+
/// is remaining (greater than dust) after `funding_satoshis` and fees payable are satisfied by
4830+
/// `funding_inputs`
4831+
// TODO(dual_funding): We could allow a list of such outputs to be provided so that the user may
4832+
/// be able to do some more interesting things at the same time as funding.
4833+
///
4834+
/// Note that this method will return an error and reject the channel, if it requires support
4835+
/// for zero confirmations.
4836+
// TODO(dual_funding): Discussion on complications with 0conf dual-funded channels where "locking"
4837+
// of UTXOs used for funding would be required and other issues.
4838+
// See: https://lists.linuxfoundation.org/pipermail/lightning-dev/2023-May/003920.html
4839+
///
4840+
///
4841+
/// [`Event::OpenChannelV2Request`]: events::Event::OpenChannelV2Request
4842+
/// [`Event::ChannelClosed::user_channel_id`]: events::Event::ChannelClosed::user_channel_id
4843+
pub fn accept_inbound_dual_funded_channel(&self, temporary_channel_id: &[u8; 32],
4844+
counterparty_node_id: &PublicKey, user_channel_id: u128, funding_satoshis: u64,
4845+
funding_inputs: Vec<DualFundingUtxo>, change_script_pubkey: Script) -> Result<(), APIError> {
4846+
self.do_accept_inbound_dual_funded_channel(temporary_channel_id, counterparty_node_id, false,
4847+
user_channel_id, funding_satoshis, funding_inputs, change_script_pubkey)
4848+
}
4849+
4850+
fn do_accept_inbound_dual_funded_channel(&self, temporary_channel_id: &[u8; 32],
4851+
counterparty_node_id: &PublicKey, accept_0conf: bool, user_channel_id: u128,
4852+
funding_satoshis: u64, funding_inputs: Vec<DualFundingUtxo>, change_script_pubkey: Script,
4853+
) -> Result<(), APIError> {
4854+
Self::dual_funding_amount_checks(funding_satoshis, &funding_inputs)?;
4855+
4856+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
4857+
4858+
let peers_without_funded_channels =
4859+
self.peers_without_funded_channels(|peer| { peer.total_channel_count() > 0 });
4860+
let per_peer_state = self.per_peer_state.read().unwrap();
4861+
let peer_state_mutex = per_peer_state.get(counterparty_node_id)
4862+
.ok_or_else(|| APIError::ChannelUnavailable { err: format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id) })?;
4863+
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
4864+
let peer_state = &mut *peer_state_lock;
4865+
let is_only_peer_channel = peer_state.total_channel_count() == 1;
4866+
match peer_state.inbound_v2_channel_by_id.entry(temporary_channel_id.clone()) {
4867+
hash_map::Entry::Occupied(mut channel) => {
4868+
if !channel.get().inbound_is_awaiting_accept() {
4869+
return Err(APIError::APIMisuseError { err: "The channel isn't currently awaiting to be accepted.".to_owned() });
4870+
}
4871+
if accept_0conf {
4872+
channel.get_mut().set_0conf();
4873+
} else if channel.get().get_channel_type().requires_zero_conf() {
4874+
let send_msg_err_event = events::MessageSendEvent::HandleError {
4875+
node_id: channel.get().get_counterparty_node_id(),
4876+
action: msgs::ErrorAction::SendErrorMessage{
4877+
msg: msgs::ErrorMessage { channel_id: temporary_channel_id.clone(), data: "No zero confirmation channels accepted".to_owned(), }
4878+
}
4879+
};
4880+
peer_state.pending_msg_events.push(send_msg_err_event);
4881+
let _ = remove_channel!(self, channel);
4882+
return Err(APIError::APIMisuseError { err: "Please use accept_inbound_channel_from_trusted_peer_0conf to accept channels with zero confirmations.".to_owned() });
4883+
} else {
4884+
// If this peer already has some channels, a new channel won't increase our number of peers
4885+
// with unfunded channels, so as long as we aren't over the maximum number of unfunded
4886+
// channels per-peer we can accept channels from a peer with existing ones.
4887+
if is_only_peer_channel && peers_without_funded_channels >= MAX_UNFUNDED_CHANNEL_PEERS {
4888+
let send_msg_err_event = events::MessageSendEvent::HandleError {
4889+
node_id: channel.get().get_counterparty_node_id(),
4890+
action: msgs::ErrorAction::SendErrorMessage{
4891+
msg: msgs::ErrorMessage { channel_id: temporary_channel_id.clone(), data: "Have too many peers with unfunded channels, not accepting new ones".to_owned(), }
4892+
}
4893+
};
4894+
peer_state.pending_msg_events.push(send_msg_err_event);
4895+
let _ = remove_channel!(self, channel);
4896+
return Err(APIError::APIMisuseError { err: "Too many peers with unfunded channels, refusing to accept new ones".to_owned() });
4897+
}
4898+
}
4899+
4900+
peer_state.pending_msg_events.push(events::MessageSendEvent::SendAcceptChannel {
4901+
node_id: channel.get().get_counterparty_node_id(),
4902+
msg: channel.get_mut().accept_inbound_channel(user_channel_id),
4903+
});
4904+
}
4905+
hash_map::Entry::Vacant(_) => {
4906+
return Err(APIError::ChannelUnavailable { err: format!("Channel with id {} not found for the passed counterparty node_id {}", log_bytes!(*temporary_channel_id), counterparty_node_id) });
4907+
}
4908+
}
4909+
Ok(())
4910+
}
4911+
4912+
/// Checks related to inputs and their amounts related to establishing dual-funded channels.
4913+
fn dual_funding_amount_checks(funding_satoshis: u64, funding_inputs: &Vec<DualFundingUtxo>)
4914+
-> Result<(), APIError> {
4915+
if funding_satoshis < 1000 {
4916+
return Err(APIError::APIMisuseError {
4917+
err: format!("Funding amount must be at least 1000 satoshis. It was {} sats", funding_satoshis),
4918+
});
4919+
}
4920+
4921+
let total_input_satoshis: u64 = funding_inputs.iter().map(|input| input.output.value).sum();
4922+
if total_input_satoshis < funding_satoshis {
4923+
Err(APIError::APIMisuseError {
4924+
err: format!("Total value of funding inputs must be at least funding amount. It was {} sats",
4925+
total_input_satoshis) })
4926+
} else {
4927+
Ok(())
4928+
}
4929+
}
4930+
46904931
/// Gets the number of peers which match the given filter and do not have any funded, outbound,
46914932
/// or 0-conf channels.
46924933
///
@@ -4726,6 +4967,11 @@ where
47264967
num_unfunded_channels += 1;
47274968
}
47284969
}
4970+
for (_, chan) in peer.inbound_v2_channel_by_id.iter() {
4971+
if chan.minimum_depth().unwrap_or(1) != 0 {
4972+
num_unfunded_channels += 1;
4973+
}
4974+
}
47294975
num_unfunded_channels
47304976
}
47314977

@@ -6820,6 +7066,8 @@ where
68207066
channel_by_id: HashMap::new(),
68217067
outbound_v1_channel_by_id: HashMap::new(),
68227068
inbound_v1_channel_by_id: HashMap::new(),
7069+
outbound_v2_channel_by_id: HashMap::new(),
7070+
inbound_v2_channel_by_id: HashMap::new(),
68237071
latest_features: init_msg.features.clone(),
68247072
pending_msg_events: Vec::new(),
68257073
monitor_update_blocked_actions: BTreeMap::new(),
@@ -8013,6 +8261,8 @@ where
80138261
channel_by_id: peer_channels.remove(&peer_pubkey).unwrap_or(HashMap::new()),
80148262
outbound_v1_channel_by_id: HashMap::new(),
80158263
inbound_v1_channel_by_id: HashMap::new(),
8264+
outbound_v2_channel_by_id: HashMap::new(),
8265+
inbound_v2_channel_by_id: HashMap::new(),
80168266
latest_features: Readable::read(reader)?,
80178267
pending_msg_events: Vec::new(),
80188268
monitor_update_blocked_actions: BTreeMap::new(),

Diff for: lightning/src/util/config.rs

+16
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,21 @@ pub struct UserConfig {
552552
/// [`ChannelManager::get_intercept_scid`]: crate::ln::channelmanager::ChannelManager::get_intercept_scid
553553
/// [`Event::HTLCIntercepted`]: crate::events::Event::HTLCIntercepted
554554
pub accept_intercept_htlcs: bool,
555+
/// If this is set to true, regardless of `accept_inbound_channels`, the user needs to manually
556+
/// accept inbound requests to open a new dual-funded channel and provide any UTXOs they'd like
557+
/// to contribute to the funding transaction.
558+
///
559+
/// When set to true, [`Event::OpenChannelV2Request`] will be triggered once a request to open a
560+
/// new inbound dual-funded channel is received through a [`msgs::OpenChannelV2`] message. In that
561+
/// case, a [`msgs::AcceptChannelV2`] message will not be sent back to the counterparty node unless
562+
/// the user explicitly chooses to accept the request.
563+
///
564+
/// Default value: false.
565+
///
566+
/// [`Event::OpenChannelV2Request`]: crate::events::Event::OpenChannelV2Request
567+
/// [`msgs::OpenChannelV2`]: crate::ln::msgs::OpenChannelV2
568+
/// [`msgs::AcceptChannelV2`]: crate::ln::msgs::AcceptChannelV2
569+
pub contribute_to_dual_funded_channels: bool,
555570
}
556571

557572
impl Default for UserConfig {
@@ -564,6 +579,7 @@ impl Default for UserConfig {
564579
accept_inbound_channels: true,
565580
manually_accept_inbound_channels: false,
566581
accept_intercept_htlcs: false,
582+
contribute_to_dual_funded_channels: false,
567583
}
568584
}
569585
}

0 commit comments

Comments
 (0)