Skip to content

Support opening anchor channels and test end-to-end unilateral close #1860

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
291 changes: 239 additions & 52 deletions lightning/src/ln/channel.rs

Large diffs are not rendered by default.

50 changes: 47 additions & 3 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4193,7 +4193,7 @@ where
let mut peer_state_lock = peer_state_mutex_opt.unwrap().lock().unwrap();
let peer_state = &mut *peer_state_lock;
let mut channel = match Channel::new_from_req(&self.fee_estimator, &self.entropy_source, &self.signer_provider,
counterparty_node_id.clone(), &peer_state.latest_features, msg, user_channel_id, &self.default_configuration,
counterparty_node_id.clone(), &self.channel_type_features(), &peer_state.latest_features, msg, user_channel_id, &self.default_configuration,
self.best_block.read().unwrap().height(), &self.logger, outbound_scid_alias)
{
Err(e) => {
Expand Down Expand Up @@ -6264,7 +6264,7 @@ pub(crate) fn provided_channel_features(config: &UserConfig) -> ChannelFeatures
/// Fetches the set of [`ChannelTypeFeatures`] flags which are provided by or required by
/// [`ChannelManager`].
pub(crate) fn provided_channel_type_features(config: &UserConfig) -> ChannelTypeFeatures {
ChannelTypeFeatures::from_counterparty_init(&provided_init_features(config))
ChannelTypeFeatures::from_init(&provided_init_features(config))
}

/// Fetches the set of [`InitFeatures`] flags which are provided by or required by
Expand All @@ -6285,6 +6285,12 @@ pub fn provided_init_features(_config: &UserConfig) -> InitFeatures {
features.set_channel_type_optional();
features.set_scid_privacy_optional();
features.set_zero_conf_optional();
#[cfg(anchors)]
{ // Attributes are not allowed on if expressions on our current MSRV of 1.41.
if _config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx {
features.set_anchors_zero_fee_htlc_tx_optional();
}
}
features
}

Expand Down Expand Up @@ -7023,7 +7029,9 @@ where
let mut short_to_chan_info = HashMap::with_capacity(cmp::min(channel_count as usize, 128));
let mut channel_closures = Vec::new();
for _ in 0..channel_count {
let mut channel: Channel<<SP::Target as SignerProvider>::Signer> = Channel::read(reader, (&args.entropy_source, &args.signer_provider, best_block_height))?;
let mut channel: Channel<<SP::Target as SignerProvider>::Signer> = Channel::read(reader, (
&args.entropy_source, &args.signer_provider, best_block_height, &provided_channel_type_features(&args.default_config)
))?;
let funding_txo = channel.get_funding_txo().ok_or(DecodeError::InvalidValue)?;
funding_txo_set.insert(funding_txo.clone());
if let Some(ref mut monitor) = args.channel_monitors.get_mut(&funding_txo) {
Expand Down Expand Up @@ -8307,6 +8315,42 @@ mod tests {

nodes[1].node.handle_update_fee(&unkown_public_key, &update_fee_msg);
}

#[cfg(anchors)]
#[test]
fn test_anchors_zero_fee_htlc_tx_fallback() {
// Tests that if both nodes support anchors, but the remote node does not want to accept
// anchor channels at the moment, an error it sent to the local node such that it can retry
// the channel without the anchors feature.
let chanmon_cfgs = create_chanmon_cfgs(2);
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let mut anchors_config = test_default_channel_config();
anchors_config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
anchors_config.manually_accept_inbound_channels = true;
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(anchors_config.clone()), Some(anchors_config.clone())]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);

nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100_000, 0, 0, None).unwrap();
let open_channel_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
assert!(open_channel_msg.channel_type.as_ref().unwrap().supports_anchors_zero_fee_htlc_tx());

nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), &open_channel_msg);
let events = nodes[1].node.get_and_clear_pending_events();
match events[0] {
Event::OpenChannelRequest { temporary_channel_id, .. } => {
nodes[1].node.force_close_broadcasting_latest_txn(&temporary_channel_id, &nodes[0].node.get_our_node_id()).unwrap();
}
_ => panic!("Unexpected event"),
}

let error_msg = get_err_msg!(nodes[1], nodes[0].node.get_our_node_id());
nodes[0].node.handle_error(&nodes[1].node.get_our_node_id(), &error_msg);

let open_channel_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
assert!(!open_channel_msg.channel_type.unwrap().supports_anchors_zero_fee_htlc_tx());

check_closed_event!(nodes[1], 1, ClosureReason::HolderForceClosed);
}
}

#[cfg(all(any(test, feature = "_test_utils"), feature = "_bench_unstable"))]
Expand Down
56 changes: 47 additions & 9 deletions lightning/src/ln/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@
//! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md) for more information).
//! - `Keysend` - send funds to a node without an invoice
//! (see the [`Keysend` feature assignment proposal](https://github.com/lightning/bolts/issues/605#issuecomment-606679798) for more information).
//! - `AnchorsZeroFeeHtlcTx` - requires/supports that commitment transactions include anchor outputs
//! and HTLC transactions are pre-signed with zero fee (see
//! [BOLT-3](https://github.com/lightning/bolts/blob/master/03-transactions.md) for more
//! information).
//!
//! [BOLT #9]: https://github.com/lightning/bolts/blob/master/09-features.md
//! [messages]: crate::ln::msgs
Expand Down Expand Up @@ -122,7 +126,7 @@ mod sealed {
// Byte 1
VariableLengthOnion | StaticRemoteKey | PaymentSecret,
// Byte 2
BasicMPP | Wumbo,
BasicMPP | Wumbo | AnchorsZeroFeeHtlcTx,
// Byte 3
ShutdownAnySegwit,
// Byte 4
Expand All @@ -138,7 +142,7 @@ mod sealed {
// Byte 1
VariableLengthOnion | StaticRemoteKey | PaymentSecret,
// Byte 2
BasicMPP | Wumbo,
BasicMPP | Wumbo | AnchorsZeroFeeHtlcTx,
// Byte 3
ShutdownAnySegwit,
// Byte 4
Expand Down Expand Up @@ -176,7 +180,7 @@ mod sealed {
// Byte 1
StaticRemoteKey,
// Byte 2
,
AnchorsZeroFeeHtlcTx,
// Byte 3
,
// Byte 4
Expand Down Expand Up @@ -357,6 +361,9 @@ mod sealed {
define_feature!(19, Wumbo, [InitContext, NodeContext],
"Feature flags for `option_support_large_channel` (aka wumbo channels).", set_wumbo_optional, set_wumbo_required,
supports_wumbo, requires_wumbo);
define_feature!(23, AnchorsZeroFeeHtlcTx, [InitContext, NodeContext, ChannelTypeContext],
"Feature flags for `option_anchors_zero_fee_htlc_tx`.", set_anchors_zero_fee_htlc_tx_optional,
set_anchors_zero_fee_htlc_tx_required, supports_anchors_zero_fee_htlc_tx, requires_anchors_zero_fee_htlc_tx);
define_feature!(27, ShutdownAnySegwit, [InitContext, NodeContext],
"Feature flags for `opt_shutdown_anysegwit`.", set_shutdown_any_segwit_optional,
set_shutdown_any_segwit_required, supports_shutdown_anysegwit, requires_shutdown_anysegwit);
Expand Down Expand Up @@ -505,10 +512,10 @@ impl InvoiceFeatures {
}

impl ChannelTypeFeatures {
/// Constructs the implicit channel type based on the common supported types between us and our
/// counterparty
pub(crate) fn from_counterparty_init(counterparty_init: &InitFeatures) -> Self {
let mut ret = counterparty_init.to_context_internal();
// Maps the relevant `InitFeatures` to `ChannelTypeFeatures`. Any unknown features to
// `ChannelTypeFeatures` are not included in the result.
pub(crate) fn from_init(init: &InitFeatures) -> Self {
let mut ret = init.to_context_internal();
// ChannelTypeFeatures must only contain required bits, so we OR the required forms of all
// optional bits and then AND out the optional ones.
for byte in ret.flags.iter_mut() {
Expand Down Expand Up @@ -678,6 +685,24 @@ impl<T: sealed::Context> Features<T> {
(byte & unknown_features) != 0
})
}

// Returns true if the features within `self` are a subset of the features within `other`.
pub(crate) fn is_subset(&self, other: &Self) -> bool {
for (idx, byte) in self.flags.iter().enumerate() {
if let Some(other_byte) = other.flags.get(idx) {
if byte & other_byte != *byte {
// `self` has bits set that `other` doesn't.
return false;
}
} else {
if *byte > 0 {
// `self` has a non-zero byte that `other` doesn't.
return false;
}
}
}
true
}
}

impl<T: sealed::UpfrontShutdownScript> Features<T> {
Expand All @@ -704,6 +729,18 @@ impl<T: sealed::Wumbo> Features<T> {
}
}

impl<T: sealed::SCIDPrivacy> Features<T> {
pub(crate) fn clear_scid_privacy(&mut self) {
<T as sealed::SCIDPrivacy>::clear_bits(&mut self.flags);
}
}

impl<T: sealed::AnchorsZeroFeeHtlcTx> Features<T> {
pub(crate) fn clear_anchors_zero_fee_htlc_tx(&mut self) {
<T as sealed::AnchorsZeroFeeHtlcTx>::clear_bits(&mut self.flags);
}
}

#[cfg(test)]
impl<T: sealed::UnknownFeature> Features<T> {
pub(crate) fn unknown() -> Self {
Expand Down Expand Up @@ -808,6 +845,7 @@ mod tests {
init_features.set_channel_type_optional();
init_features.set_scid_privacy_optional();
init_features.set_zero_conf_optional();
init_features.set_anchors_zero_fee_htlc_tx_optional();

assert!(init_features.initial_routing_sync());
assert!(!init_features.supports_upfront_shutdown_script());
Expand All @@ -826,7 +864,7 @@ mod tests {
assert_eq!(node_features.flags.len(), 7);
assert_eq!(node_features.flags[0], 0b00000010);
assert_eq!(node_features.flags[1], 0b01010001);
assert_eq!(node_features.flags[2], 0b00001010);
assert_eq!(node_features.flags[2], 0b10001010);
assert_eq!(node_features.flags[3], 0b00001000);
assert_eq!(node_features.flags[4], 0b10000000);
assert_eq!(node_features.flags[5], 0b10100000);
Expand Down Expand Up @@ -917,7 +955,7 @@ mod tests {
// required-StaticRemoteKey ChannelTypeFeatures.
let mut init_features = InitFeatures::empty();
init_features.set_static_remote_key_optional();
let converted_features = ChannelTypeFeatures::from_counterparty_init(&init_features);
let converted_features = ChannelTypeFeatures::from_init(&init_features);
assert_eq!(converted_features, ChannelTypeFeatures::only_static_remote_key());
assert!(!converted_features.supports_any_optional_bits());
assert!(converted_features.requires_static_remote_key());
Expand Down
16 changes: 13 additions & 3 deletions lightning/src/ln/functional_test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,22 @@ pub fn confirm_transaction<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Tran
connect_blocks(node, CHAN_CONFIRM_DEPTH - 1);
scid
}
/// Mine a signle block containing the given transaction
/// Mine a single block containing the given transaction
pub fn mine_transaction<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Transaction) {
let height = node.best_block_info().1 + 1;
confirm_transaction_at(node, tx, height);
}
/// Mine a single block containing the given transactions
pub fn mine_transactions<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, txn: &[&Transaction]) {
let height = node.best_block_info().1 + 1;
confirm_transactions_at(node, txn, height);
}
/// Mine the given transaction at the given height, mining blocks as required to build to that
/// height
///
/// Returns the SCID a channel confirmed in the given transaction will have, assuming the funding
/// output is the 1st output in the transaction.
pub fn confirm_transaction_at<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Transaction, conf_height: u32) -> u64 {
pub fn confirm_transactions_at<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, txn: &[&Transaction], conf_height: u32) -> u64 {
let first_connect_height = node.best_block_info().1 + 1;
assert!(first_connect_height <= conf_height);
if conf_height > first_connect_height {
Expand All @@ -84,10 +89,15 @@ pub fn confirm_transaction_at<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &T
for _ in 0..*node.network_chan_count.borrow() { // Make sure we don't end up with channels at the same short id by offsetting by chan_count
block.txdata.push(Transaction { version: 0, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: Vec::new() });
}
block.txdata.push(tx.clone());
for tx in txn {
block.txdata.push((*tx).clone());
}
connect_block(node, &block);
scid_utils::scid_from_parts(conf_height as u64, block.txdata.len() as u64 - 1, 0).unwrap()
}
pub fn confirm_transaction_at<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Transaction, conf_height: u32) -> u64 {
confirm_transactions_at(node, &[tx], conf_height)
}

/// The possible ways we may notify a ChannelManager of a new block
#[derive(Clone, Copy, Debug, PartialEq)]
Expand Down
4 changes: 2 additions & 2 deletions lightning/src/ln/functional_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6861,7 +6861,7 @@ fn test_user_configurable_csv_delay() {
let mut open_channel = get_event_msg!(nodes[1], MessageSendEvent::SendOpenChannel, nodes[0].node.get_our_node_id());
open_channel.to_self_delay = 200;
if let Err(error) = Channel::new_from_req(&LowerBoundedFeeEstimator::new(&test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) }),
&nodes[0].keys_manager, &nodes[0].keys_manager, nodes[1].node.get_our_node_id(), &nodes[1].node.init_features(), &open_channel, 0,
&nodes[0].keys_manager, &nodes[0].keys_manager, nodes[1].node.get_our_node_id(), &nodes[0].node.channel_type_features(), &nodes[1].node.init_features(), &open_channel, 0,
&low_our_to_self_config, 0, &nodes[0].logger, 42)
{
match error {
Expand Down Expand Up @@ -6893,7 +6893,7 @@ fn test_user_configurable_csv_delay() {
let mut open_channel = get_event_msg!(nodes[1], MessageSendEvent::SendOpenChannel, nodes[0].node.get_our_node_id());
open_channel.to_self_delay = 200;
if let Err(error) = Channel::new_from_req(&LowerBoundedFeeEstimator::new(&test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) }),
&nodes[0].keys_manager, &nodes[0].keys_manager, nodes[1].node.get_our_node_id(), &nodes[1].node.init_features(), &open_channel, 0,
&nodes[0].keys_manager, &nodes[0].keys_manager, nodes[1].node.get_our_node_id(), &nodes[0].node.channel_type_features(), &nodes[1].node.init_features(), &open_channel, 0,
&high_their_to_self_config, 0, &nodes[0].logger, 42)
{
match error {
Expand Down
Loading