Skip to content

Commit 12b59b2

Browse files
committed
Add ability to set shutdown script when closing channel
1 parent 101c09f commit 12b59b2

File tree

3 files changed

+80
-11
lines changed

3 files changed

+80
-11
lines changed

lightning/src/ln/channel.rs

+14-4
Original file line numberDiff line numberDiff line change
@@ -6050,7 +6050,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
60506050
/// May jump to the channel being fully shutdown (see [`Self::is_shutdown`]) in which case no
60516051
/// [`ChannelMonitorUpdate`] will be returned).
60526052
pub fn get_shutdown<SP: Deref>(&mut self, signer_provider: &SP, their_features: &InitFeatures,
6053-
target_feerate_sats_per_kw: Option<u32>)
6053+
target_feerate_sats_per_kw: Option<u32>, override_shutdown_script: Option<ShutdownScript>)
60546054
-> Result<(msgs::Shutdown, Option<&ChannelMonitorUpdate>, Vec<(HTLCSource, PaymentHash)>), APIError>
60556055
where SP::Target: SignerProvider {
60566056
for htlc in self.pending_outbound_htlcs.iter() {
@@ -6066,6 +6066,9 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
60666066
return Err(APIError::ChannelUnavailable{err: "Shutdown already in progress by remote".to_owned()});
60676067
}
60686068
}
6069+
if self.shutdown_scriptpubkey.is_some() && override_shutdown_script.is_some() {
6070+
return Err(APIError::APIMisuseError{err: "Cannot override shutdown script for a channel with one already set".to_owned()});
6071+
}
60696072
assert_eq!(self.channel_state & ChannelState::ShutdownComplete as u32, 0);
60706073
if self.channel_state & (ChannelState::PeerDisconnected as u32 | ChannelState::MonitorUpdateInProgress as u32) != 0 {
60716074
return Err(APIError::ChannelUnavailable{err: "Cannot begin shutdown while peer is disconnected or we're waiting on a monitor update, maybe force-close instead?".to_owned()});
@@ -6081,9 +6084,16 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
60816084
let update_shutdown_script = match self.shutdown_scriptpubkey {
60826085
Some(_) => false,
60836086
None if !chan_closed => {
6084-
let shutdown_scriptpubkey = match signer_provider.get_shutdown_scriptpubkey() {
6085-
Ok(scriptpubkey) => scriptpubkey,
6086-
Err(_) => return Err(APIError::ChannelUnavailable { err: "Failed to get shutdown scriptpubkey".to_owned() }),
6087+
// use override shutdown script if provided
6088+
let shutdown_scriptpubkey = match override_shutdown_script {
6089+
Some(script) => script,
6090+
None => {
6091+
// otherwise, use the shutdown scriptpubkey provided by the signer
6092+
match signer_provider.get_shutdown_scriptpubkey() {
6093+
Ok(scriptpubkey) => scriptpubkey,
6094+
Err(_) => return Err(APIError::ChannelUnavailable{err: "Failed to get shutdown scriptpubkey".to_owned()}),
6095+
}
6096+
},
60876097
};
60886098
if !shutdown_scriptpubkey.is_compatible(their_features) {
60896099
return Err(APIError::IncompatibleShutdownScript { script: shutdown_scriptpubkey.clone() });

lightning/src/ln/channelmanager.rs

+11-5
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ use core::ops::Deref;
7878

7979
// Re-export this for use in the public API.
8080
pub use crate::ln::outbound_payment::{PaymentSendFailure, Retry, RetryableSendFailure, RecipientOnionFields};
81+
use crate::ln::script::ShutdownScript;
8182

8283
// We hold various information about HTLC relay in the HTLC objects in Channel itself:
8384
//
@@ -2028,7 +2029,7 @@ where
20282029
});
20292030
}
20302031

2031-
fn close_channel_internal(&self, channel_id: &[u8; 32], counterparty_node_id: &PublicKey, target_feerate_sats_per_1000_weight: Option<u32>) -> Result<(), APIError> {
2032+
fn close_channel_internal(&self, channel_id: &[u8; 32], counterparty_node_id: &PublicKey, target_feerate_sats_per_1000_weight: Option<u32>, override_shutdown_script: Option<ShutdownScript>) -> Result<(), APIError> {
20322033
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
20332034

20342035
let mut failed_htlcs: Vec<(HTLCSource, PaymentHash)>;
@@ -2045,7 +2046,7 @@ where
20452046
let funding_txo_opt = chan_entry.get().get_funding_txo();
20462047
let their_features = &peer_state.latest_features;
20472048
let (shutdown_msg, mut monitor_update_opt, htlcs) = chan_entry.get_mut()
2048-
.get_shutdown(&self.signer_provider, their_features, target_feerate_sats_per_1000_weight)?;
2049+
.get_shutdown(&self.signer_provider, their_features, target_feerate_sats_per_1000_weight, override_shutdown_script)?;
20492050
failed_htlcs = htlcs;
20502051

20512052
// We can send the `shutdown` message before updating the `ChannelMonitor`
@@ -2112,7 +2113,7 @@ where
21122113
/// [`Normal`]: crate::chain::chaininterface::ConfirmationTarget::Normal
21132114
/// [`SendShutdown`]: crate::events::MessageSendEvent::SendShutdown
21142115
pub fn close_channel(&self, channel_id: &[u8; 32], counterparty_node_id: &PublicKey) -> Result<(), APIError> {
2115-
self.close_channel_internal(channel_id, counterparty_node_id, None)
2116+
self.close_channel_internal(channel_id, counterparty_node_id, None, None)
21162117
}
21172118

21182119
/// Begins the process of closing a channel. After this call (plus some timeout), no new HTLCs
@@ -2129,6 +2130,11 @@ where
21292130
/// transaction feerate below `target_feerate_sat_per_1000_weight` (or the feerate which
21302131
/// will appear on a force-closure transaction, whichever is lower).
21312132
///
2133+
/// The `shutdown_script` provided will be used as the `scriptPubKey` for the closing transaction.
2134+
/// Will fail if a shutdown script has already been set for this channel by
2135+
/// ['ChannelHandshakeConfig::commit_upfront_shutdown_pubkey`]. The given shutdown script must
2136+
/// also be compatible with our and the counterparty's features.
2137+
///
21322138
/// May generate a [`SendShutdown`] message event on success, which should be relayed.
21332139
///
21342140
/// Raises [`APIError::ChannelUnavailable`] if the channel cannot be closed due to failing to
@@ -2140,8 +2146,8 @@ where
21402146
/// [`Background`]: crate::chain::chaininterface::ConfirmationTarget::Background
21412147
/// [`Normal`]: crate::chain::chaininterface::ConfirmationTarget::Normal
21422148
/// [`SendShutdown`]: crate::events::MessageSendEvent::SendShutdown
2143-
pub fn close_channel_with_target_feerate(&self, channel_id: &[u8; 32], counterparty_node_id: &PublicKey, target_feerate_sats_per_1000_weight: u32) -> Result<(), APIError> {
2144-
self.close_channel_internal(channel_id, counterparty_node_id, Some(target_feerate_sats_per_1000_weight))
2149+
pub fn close_channel_with_feerate_and_script(&self, channel_id: &[u8; 32], counterparty_node_id: &PublicKey, target_feerate_sats_per_1000_weight: Option<u32>, shutdown_script: Option<ShutdownScript>) -> Result<(), APIError> {
2150+
self.close_channel_internal(channel_id, counterparty_node_id, target_feerate_sats_per_1000_weight, shutdown_script)
21452151
}
21462152

21472153
#[inline]

lightning/src/ln/shutdown_tests.rs

+55-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use bitcoin::util::address::WitnessVersion;
3131
use regex;
3232

3333
use core::default::Default;
34+
use std::convert::TryFrom;
3435

3536
use crate::ln::functional_test_utils::*;
3637
use crate::ln::msgs::OptionalField::Present;
@@ -723,6 +724,58 @@ fn test_invalid_shutdown_script() {
723724
"Got a nonstandard scriptpubkey (00020000) from remote peer");
724725
}
725726

727+
#[test]
728+
fn test_user_shutdown_script() {
729+
let mut config = test_default_channel_config();
730+
config.channel_handshake_config.announced_channel = true;
731+
config.channel_handshake_limits.force_announced_channel_preference = false;
732+
config.channel_handshake_config.commit_upfront_shutdown_pubkey = false;
733+
let user_cfgs = [None, Some(config), None];
734+
let chanmon_cfgs = create_chanmon_cfgs(3);
735+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
736+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &user_cfgs);
737+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
738+
739+
// Segwit v0 script of the form OP_0 <20-byte hash>
740+
let script = Builder::new().push_int(0)
741+
.push_slice(&[0; 20])
742+
.into_script();
743+
744+
let shutdown_script = ShutdownScript::try_from(script.clone()).unwrap();
745+
746+
let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
747+
nodes[1].node.close_channel_with_feerate_and_script(&OutPoint { txid: chan.3.txid(), index: 0 }.to_channel_id(), &nodes[0].node.get_our_node_id(), None, Some(shutdown_script)).unwrap();
748+
check_added_monitors!(nodes[1], 1);
749+
750+
let mut node_0_shutdown = get_event_msg!(nodes[1], MessageSendEvent::SendShutdown, nodes[0].node.get_our_node_id());
751+
752+
assert_eq!(node_0_shutdown.scriptpubkey, script);
753+
}
754+
755+
#[test]
756+
fn test_already_set_user_shutdown_script() {
757+
let mut config = test_default_channel_config();
758+
config.channel_handshake_config.announced_channel = true;
759+
config.channel_handshake_limits.force_announced_channel_preference = false;
760+
let user_cfgs = [None, Some(config), None];
761+
let chanmon_cfgs = create_chanmon_cfgs(3);
762+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
763+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &user_cfgs);
764+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
765+
766+
// Segwit v0 script of the form OP_0 <20-byte hash>
767+
let script = Builder::new().push_int(0)
768+
.push_slice(&[0; 20])
769+
.into_script();
770+
771+
let shutdown_script = ShutdownScript::try_from(script).unwrap();
772+
773+
let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
774+
let result = nodes[1].node.close_channel_with_feerate_and_script(&OutPoint { txid: chan.3.txid(), index: 0 }.to_channel_id(), &nodes[0].node.get_our_node_id(), None, Some(shutdown_script));
775+
776+
assert_eq!(result, Err(APIError::APIMisuseError { err: "Cannot override shutdown script for a channel with one already set".to_string() }));
777+
}
778+
726779
#[derive(PartialEq)]
727780
enum TimeoutStep {
728781
AfterShutdown,
@@ -891,9 +944,9 @@ fn simple_target_feerate_shutdown() {
891944
let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
892945
let chan_id = OutPoint { txid: chan.3.txid(), index: 0 }.to_channel_id();
893946

894-
nodes[0].node.close_channel_with_target_feerate(&chan_id, &nodes[1].node.get_our_node_id(), 253 * 10).unwrap();
947+
nodes[0].node.close_channel_with_feerate_and_script(&chan_id, &nodes[1].node.get_our_node_id(), Some(253 * 10), None).unwrap();
895948
let node_0_shutdown = get_event_msg!(nodes[0], MessageSendEvent::SendShutdown, nodes[1].node.get_our_node_id());
896-
nodes[1].node.close_channel_with_target_feerate(&chan_id, &nodes[0].node.get_our_node_id(), 253 * 5).unwrap();
949+
nodes[1].node.close_channel_with_feerate_and_script(&chan_id, &nodes[0].node.get_our_node_id(), Some(253 * 5), None).unwrap();
897950
let node_1_shutdown = get_event_msg!(nodes[1], MessageSendEvent::SendShutdown, nodes[0].node.get_our_node_id());
898951

899952
nodes[1].node.handle_shutdown(&nodes[0].node.get_our_node_id(), &node_0_shutdown);

0 commit comments

Comments
 (0)