Skip to content

Commit

Permalink
Merge pull request #2863 from benthecarman/breakup-coop-close
Browse files Browse the repository at this point in the history
Breakup CooperativeClosure into Local/Remote initiated
  • Loading branch information
TheBlueMatt authored Feb 5, 2024
2 parents ce2907f + efcb5d9 commit f70c113
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 56 deletions.
21 changes: 16 additions & 5 deletions lightning/src/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,15 @@ pub enum ClosureReason {
HolderForceClosed,
/// The channel was closed after negotiating a cooperative close and we've now broadcasted
/// the cooperative close transaction. Note the shutdown may have been initiated by us.
//TODO: split between CounterpartyInitiated/LocallyInitiated
CooperativeClosure,
// Can be removed once we disallow downgrading to 0.0.121
LegacyCooperativeClosure,
/// The channel was closed after negotiating a cooperative close and we've now broadcasted
/// the cooperative close transaction. This indicates that the shutdown was initiated by our
/// counterparty.
CounterpartyInitiatedCooperativeClosure,
/// The channel was closed after negotiating a cooperative close and we've now broadcasted
/// the cooperative close transaction. This indicates that the shutdown was initiated by us.
LocallyInitiatedCooperativeClosure,
/// A commitment transaction was confirmed on chain, closing the channel. Most likely this
/// commitment transaction came from our counterparty, but it may also have come from
/// a copy of our own `ChannelMonitor`.
Expand Down Expand Up @@ -230,7 +237,9 @@ impl core::fmt::Display for ClosureReason {
f.write_fmt(format_args!("counterparty force-closed with message: {}", peer_msg))
},
ClosureReason::HolderForceClosed => f.write_str("user manually force-closed the channel"),
ClosureReason::CooperativeClosure => f.write_str("the channel was cooperatively closed"),
ClosureReason::LegacyCooperativeClosure => f.write_str("the channel was cooperatively closed"),
ClosureReason::CounterpartyInitiatedCooperativeClosure => f.write_str("the channel was cooperatively closed by our peer"),
ClosureReason::LocallyInitiatedCooperativeClosure => f.write_str("the channel was cooperatively closed by us"),
ClosureReason::CommitmentTxConfirmed => f.write_str("commitment or closing transaction was confirmed on chain."),
ClosureReason::FundingTimedOut => write!(f, "funding transaction failed to confirm within {} blocks", FUNDING_CONF_DEADLINE_BLOCKS),
ClosureReason::ProcessingError { err } => {
Expand All @@ -250,12 +259,14 @@ impl_writeable_tlv_based_enum_upgradable!(ClosureReason,
(1, FundingTimedOut) => {},
(2, HolderForceClosed) => {},
(6, CommitmentTxConfirmed) => {},
(4, CooperativeClosure) => {},
(4, LegacyCooperativeClosure) => {},
(8, ProcessingError) => { (1, err, required) },
(10, DisconnectedPeer) => {},
(12, OutdatedChannelManager) => {},
(13, CounterpartyCoopClosedUnfundedChannel) => {},
(15, FundingBatchClosure) => {}
(15, FundingBatchClosure) => {},
(17, CounterpartyInitiatedCooperativeClosure) => {},
(19, LocallyInitiatedCooperativeClosure) => {},
);

/// Intended destination of a failed HTLC as indicated in [`Event::HTLCHandlingFailed`].
Expand Down
8 changes: 4 additions & 4 deletions lightning/src/ln/chanmon_update_fail_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1955,8 +1955,8 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf:

send_payment(&nodes[0], &[&nodes[1]], 8000000);
close_channel(&nodes[0], &nodes[1], &channel_id, funding_tx, true);
check_closed_event!(nodes[0], 1, ClosureReason::CooperativeClosure, [nodes[1].node.get_our_node_id()], 100000);
check_closed_event!(nodes[1], 1, ClosureReason::CooperativeClosure, [nodes[0].node.get_our_node_id()], 100000);
check_closed_event!(nodes[0], 1, ClosureReason::CounterpartyInitiatedCooperativeClosure, [nodes[1].node.get_our_node_id()], 100000);
check_closed_event!(nodes[1], 1, ClosureReason::LocallyInitiatedCooperativeClosure, [nodes[0].node.get_our_node_id()], 100000);
}

#[test]
Expand Down Expand Up @@ -2634,8 +2634,8 @@ fn test_temporary_error_during_shutdown() {
assert_eq!(txn_a, txn_b);
assert_eq!(txn_a.len(), 1);
check_spends!(txn_a[0], funding_tx);
check_closed_event!(nodes[1], 1, ClosureReason::CooperativeClosure, [nodes[0].node.get_our_node_id()], 100000);
check_closed_event!(nodes[0], 1, ClosureReason::CooperativeClosure, [nodes[1].node.get_our_node_id()], 100000);
check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyInitiatedCooperativeClosure, [nodes[0].node.get_our_node_id()], 100000);
check_closed_event!(nodes[0], 1, ClosureReason::LocallyInitiatedCooperativeClosure, [nodes[1].node.get_our_node_id()], 100000);
}

#[test]
Expand Down
28 changes: 26 additions & 2 deletions lightning/src/ln/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1256,6 +1256,9 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
// We track whether we already emitted a `ChannelReady` event.
channel_ready_event_emitted: bool,

/// Some if we initiated to shut down the channel.
local_initiated_shutdown: Option<()>,

/// The unique identifier used to re-derive the private key material for the channel through
/// [`SignerProvider::derive_channel_signer`].
channel_keys_id: [u8; 32],
Expand Down Expand Up @@ -4959,11 +4962,17 @@ impl<SP: Deref> Channel<SP> where
}
}

let closure_reason = if self.initiated_shutdown() {
ClosureReason::LocallyInitiatedCooperativeClosure
} else {
ClosureReason::CounterpartyInitiatedCooperativeClosure
};

assert!(self.context.shutdown_scriptpubkey.is_some());
if let Some((last_fee, sig)) = self.context.last_sent_closing_fee {
if last_fee == msg.fee_satoshis {
let shutdown_result = ShutdownResult {
closure_reason: ClosureReason::CooperativeClosure,
closure_reason,
monitor_update: None,
dropped_outbound_htlcs: Vec::new(),
unbroadcasted_batch_funding_txid: self.context.unbroadcasted_batch_funding_txid(),
Expand Down Expand Up @@ -4998,7 +5007,7 @@ impl<SP: Deref> Channel<SP> where
.map_err(|_| ChannelError::Close("External signer refused to sign closing transaction".to_owned()))?;
let (signed_tx, shutdown_result) = if $new_fee == msg.fee_satoshis {
let shutdown_result = ShutdownResult {
closure_reason: ClosureReason::CooperativeClosure,
closure_reason,
monitor_update: None,
dropped_outbound_htlcs: Vec::new(),
unbroadcasted_batch_funding_txid: self.context.unbroadcasted_batch_funding_txid(),
Expand Down Expand Up @@ -5263,6 +5272,11 @@ impl<SP: Deref> Channel<SP> where
self.context.channel_state.is_local_shutdown_sent()
}

/// Returns true if we initiated to shut down the channel.
pub fn initiated_shutdown(&self) -> bool {
self.context.local_initiated_shutdown.is_some()
}

/// Returns true if this channel is fully shut down. True here implies that no further actions
/// may/will be taken on this channel, and thus this object should be freed. Any future changes
/// will be handled appropriately by the chain monitor.
Expand Down Expand Up @@ -6177,6 +6191,7 @@ impl<SP: Deref> Channel<SP> where
// From here on out, we may not fail!
self.context.target_closing_feerate_sats_per_kw = target_feerate_sats_per_kw;
self.context.channel_state.set_local_shutdown_sent();
self.context.local_initiated_shutdown = Some(());
self.context.update_time_counter += 1;

let monitor_update = if update_shutdown_script {
Expand Down Expand Up @@ -6437,6 +6452,7 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
channel_keys_id,

blocked_monitor_updates: Vec::new(),
local_initiated_shutdown: None,
},
unfunded_context: UnfundedChannelContext { unfunded_channel_age_ticks: 0 }
})
Expand Down Expand Up @@ -7237,6 +7253,8 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
channel_type,
channel_keys_id,

local_initiated_shutdown: None,

blocked_monitor_updates: Vec::new(),
},
unfunded_context: UnfundedChannelContext { unfunded_channel_age_ticks: 0 }
Expand Down Expand Up @@ -7811,6 +7829,7 @@ impl<SP: Deref> Writeable for Channel<SP> where SP::Target: SignerProvider {
(39, pending_outbound_blinding_points, optional_vec),
(41, holding_cell_blinding_points, optional_vec),
(43, malformed_htlcs, optional_vec), // Added in 0.0.119
(45, self.context.local_initiated_shutdown, option), // Added in 0.0.122
});

Ok(())
Expand Down Expand Up @@ -8098,6 +8117,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch

let mut is_batch_funding: Option<()> = None;

let mut local_initiated_shutdown: Option<()> = None;

let mut pending_outbound_blinding_points_opt: Option<Vec<Option<PublicKey>>> = None;
let mut holding_cell_blinding_points_opt: Option<Vec<Option<PublicKey>>> = None;

Expand Down Expand Up @@ -8132,6 +8153,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
(39, pending_outbound_blinding_points_opt, optional_vec),
(41, holding_cell_blinding_points_opt, optional_vec),
(43, malformed_htlcs, optional_vec), // Added in 0.0.119
(45, local_initiated_shutdown, option),
});

let (channel_keys_id, holder_signer) = if let Some(channel_keys_id) = channel_keys_id {
Expand Down Expand Up @@ -8362,6 +8384,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
channel_type: channel_type.unwrap(),
channel_keys_id,

local_initiated_shutdown,

blocked_monitor_updates: blocked_monitor_updates.unwrap(),
}
})
Expand Down
4 changes: 2 additions & 2 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11861,8 +11861,8 @@ mod tests {
}
let (_nodes_1_update, _none) = get_closing_signed_broadcast!(nodes[1].node, nodes[0].node.get_our_node_id());

check_closed_event!(nodes[0], 1, ClosureReason::CooperativeClosure, [nodes[1].node.get_our_node_id()], 1000000);
check_closed_event!(nodes[1], 1, ClosureReason::CooperativeClosure, [nodes[0].node.get_our_node_id()], 1000000);
check_closed_event!(nodes[0], 1, ClosureReason::LocallyInitiatedCooperativeClosure, [nodes[1].node.get_our_node_id()], 1000000);
check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyInitiatedCooperativeClosure, [nodes[0].node.get_our_node_id()], 1000000);
}

fn check_not_connected_to_peer_error<T>(res_err: Result<T, APIError>, expected_public_key: PublicKey) {
Expand Down
28 changes: 14 additions & 14 deletions lightning/src/ln/functional_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -871,8 +871,8 @@ fn test_update_fee_with_fundee_update_add_htlc() {
send_payment(&nodes[1], &vec!(&nodes[0])[..], 800000);
send_payment(&nodes[0], &vec!(&nodes[1])[..], 800000);
close_channel(&nodes[0], &nodes[1], &chan.2, chan.3, true);
check_closed_event!(nodes[0], 1, ClosureReason::CooperativeClosure, [nodes[1].node.get_our_node_id()], 100000);
check_closed_event!(nodes[1], 1, ClosureReason::CooperativeClosure, [nodes[0].node.get_our_node_id()], 100000);
check_closed_event!(nodes[0], 1, ClosureReason::CounterpartyInitiatedCooperativeClosure, [nodes[1].node.get_our_node_id()], 100000);
check_closed_event!(nodes[1], 1, ClosureReason::LocallyInitiatedCooperativeClosure, [nodes[0].node.get_our_node_id()], 100000);
}

#[test]
Expand Down Expand Up @@ -985,8 +985,8 @@ fn test_update_fee() {
assert_eq!(get_feerate!(nodes[0], nodes[1], channel_id), feerate + 30);
assert_eq!(get_feerate!(nodes[1], nodes[0], channel_id), feerate + 30);
close_channel(&nodes[0], &nodes[1], &chan.2, chan.3, true);
check_closed_event!(nodes[0], 1, ClosureReason::CooperativeClosure, [nodes[1].node.get_our_node_id()], 100000);
check_closed_event!(nodes[1], 1, ClosureReason::CooperativeClosure, [nodes[0].node.get_our_node_id()], 100000);
check_closed_event!(nodes[0], 1, ClosureReason::CounterpartyInitiatedCooperativeClosure, [nodes[1].node.get_our_node_id()], 100000);
check_closed_event!(nodes[1], 1, ClosureReason::LocallyInitiatedCooperativeClosure, [nodes[0].node.get_our_node_id()], 100000);
}

#[test]
Expand Down Expand Up @@ -1104,17 +1104,17 @@ fn fake_network_test() {

// Close down the channels...
close_channel(&nodes[0], &nodes[1], &chan_1.2, chan_1.3, true);
check_closed_event!(nodes[0], 1, ClosureReason::CooperativeClosure, [nodes[1].node.get_our_node_id()], 100000);
check_closed_event!(nodes[1], 1, ClosureReason::CooperativeClosure, [nodes[0].node.get_our_node_id()], 100000);
check_closed_event!(nodes[0], 1, ClosureReason::CounterpartyInitiatedCooperativeClosure, [nodes[1].node.get_our_node_id()], 100000);
check_closed_event!(nodes[1], 1, ClosureReason::LocallyInitiatedCooperativeClosure, [nodes[0].node.get_our_node_id()], 100000);
close_channel(&nodes[1], &nodes[2], &chan_2.2, chan_2.3, false);
check_closed_event!(nodes[1], 1, ClosureReason::CooperativeClosure, [nodes[2].node.get_our_node_id()], 100000);
check_closed_event!(nodes[2], 1, ClosureReason::CooperativeClosure, [nodes[1].node.get_our_node_id()], 100000);
check_closed_event!(nodes[1], 1, ClosureReason::LocallyInitiatedCooperativeClosure, [nodes[2].node.get_our_node_id()], 100000);
check_closed_event!(nodes[2], 1, ClosureReason::CounterpartyInitiatedCooperativeClosure, [nodes[1].node.get_our_node_id()], 100000);
close_channel(&nodes[2], &nodes[3], &chan_3.2, chan_3.3, true);
check_closed_event!(nodes[2], 1, ClosureReason::CooperativeClosure, [nodes[3].node.get_our_node_id()], 100000);
check_closed_event!(nodes[3], 1, ClosureReason::CooperativeClosure, [nodes[2].node.get_our_node_id()], 100000);
check_closed_event!(nodes[2], 1, ClosureReason::CounterpartyInitiatedCooperativeClosure, [nodes[3].node.get_our_node_id()], 100000);
check_closed_event!(nodes[3], 1, ClosureReason::LocallyInitiatedCooperativeClosure, [nodes[2].node.get_our_node_id()], 100000);
close_channel(&nodes[1], &nodes[3], &chan_4.2, chan_4.3, false);
check_closed_event!(nodes[1], 1, ClosureReason::CooperativeClosure, [nodes[3].node.get_our_node_id()], 100000);
check_closed_event!(nodes[3], 1, ClosureReason::CooperativeClosure, [nodes[1].node.get_our_node_id()], 100000);
check_closed_event!(nodes[1], 1, ClosureReason::LocallyInitiatedCooperativeClosure, [nodes[3].node.get_our_node_id()], 100000);
check_closed_event!(nodes[3], 1, ClosureReason::CounterpartyInitiatedCooperativeClosure, [nodes[1].node.get_our_node_id()], 100000);
}

#[test]
Expand Down Expand Up @@ -5625,15 +5625,15 @@ fn test_static_output_closing_tx() {
let closing_tx = close_channel(&nodes[0], &nodes[1], &chan.2, chan.3, true).2;

mine_transaction(&nodes[0], &closing_tx);
check_closed_event!(nodes[0], 1, ClosureReason::CooperativeClosure, [nodes[1].node.get_our_node_id()], 100000);
check_closed_event!(nodes[0], 1, ClosureReason::CounterpartyInitiatedCooperativeClosure, [nodes[1].node.get_our_node_id()], 100000);
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);

let spend_txn = check_spendable_outputs!(nodes[0], node_cfgs[0].keys_manager);
assert_eq!(spend_txn.len(), 1);
check_spends!(spend_txn[0], closing_tx);

mine_transaction(&nodes[1], &closing_tx);
check_closed_event!(nodes[1], 1, ClosureReason::CooperativeClosure, [nodes[0].node.get_our_node_id()], 100000);
check_closed_event!(nodes[1], 1, ClosureReason::LocallyInitiatedCooperativeClosure, [nodes[0].node.get_our_node_id()], 100000);
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);

let spend_txn = check_spendable_outputs!(nodes[1], node_cfgs[1].keys_manager);
Expand Down
4 changes: 2 additions & 2 deletions lightning/src/ln/monitor_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,8 @@ fn do_chanmon_claim_value_coop_close(anchors: bool) {
spendable_outputs_b
);

check_closed_event!(nodes[0], 1, ClosureReason::CooperativeClosure, [nodes[1].node.get_our_node_id()], 1000000);
check_closed_event!(nodes[1], 1, ClosureReason::CooperativeClosure, [nodes[0].node.get_our_node_id()], 1000000);
check_closed_event!(nodes[0], 1, ClosureReason::LocallyInitiatedCooperativeClosure, [nodes[1].node.get_our_node_id()], 1000000);
check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyInitiatedCooperativeClosure, [nodes[0].node.get_our_node_id()], 1000000);
}

#[test]
Expand Down
Loading

0 comments on commit f70c113

Please sign in to comment.