Skip to content

Commit ca1d569

Browse files
authored
Merge pull request #2219 from benthecarman/custom-closing-address
Add ability to set shutdown script when closing channel
2 parents ec3de62 + 12b59b2 commit ca1d569

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
@@ -6040,7 +6040,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
60406040
/// May jump to the channel being fully shutdown (see [`Self::is_shutdown`]) in which case no
60416041
/// [`ChannelMonitorUpdate`] will be returned).
60426042
pub fn get_shutdown<SP: Deref>(&mut self, signer_provider: &SP, their_features: &InitFeatures,
6043-
target_feerate_sats_per_kw: Option<u32>)
6043+
target_feerate_sats_per_kw: Option<u32>, override_shutdown_script: Option<ShutdownScript>)
60446044
-> Result<(msgs::Shutdown, Option<&ChannelMonitorUpdate>, Vec<(HTLCSource, PaymentHash)>), APIError>
60456045
where SP::Target: SignerProvider {
60466046
for htlc in self.pending_outbound_htlcs.iter() {
@@ -6056,6 +6056,9 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
60566056
return Err(APIError::ChannelUnavailable{err: "Shutdown already in progress by remote".to_owned()});
60576057
}
60586058
}
6059+
if self.shutdown_scriptpubkey.is_some() && override_shutdown_script.is_some() {
6060+
return Err(APIError::APIMisuseError{err: "Cannot override shutdown script for a channel with one already set".to_owned()});
6061+
}
60596062
assert_eq!(self.channel_state & ChannelState::ShutdownComplete as u32, 0);
60606063
if self.channel_state & (ChannelState::PeerDisconnected as u32 | ChannelState::MonitorUpdateInProgress as u32) != 0 {
60616064
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()});
@@ -6071,9 +6074,16 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
60716074
let update_shutdown_script = match self.shutdown_scriptpubkey {
60726075
Some(_) => false,
60736076
None if !chan_closed => {
6074-
let shutdown_scriptpubkey = match signer_provider.get_shutdown_scriptpubkey() {
6075-
Ok(scriptpubkey) => scriptpubkey,
6076-
Err(_) => return Err(APIError::ChannelUnavailable { err: "Failed to get shutdown scriptpubkey".to_owned() }),
6077+
// use override shutdown script if provided
6078+
let shutdown_scriptpubkey = match override_shutdown_script {
6079+
Some(script) => script,
6080+
None => {
6081+
// otherwise, use the shutdown scriptpubkey provided by the signer
6082+
match signer_provider.get_shutdown_scriptpubkey() {
6083+
Ok(scriptpubkey) => scriptpubkey,
6084+
Err(_) => return Err(APIError::ChannelUnavailable{err: "Failed to get shutdown scriptpubkey".to_owned()}),
6085+
}
6086+
},
60776087
};
60786088
if !shutdown_scriptpubkey.is_compatible(their_features) {
60796089
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

@@ -722,6 +723,58 @@ fn test_invalid_shutdown_script() {
722723
"Got a nonstandard scriptpubkey (00020000) from remote peer");
723724
}
724725

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

893-
nodes[0].node.close_channel_with_target_feerate(&chan_id, &nodes[1].node.get_our_node_id(), 253 * 10).unwrap();
946+
nodes[0].node.close_channel_with_feerate_and_script(&chan_id, &nodes[1].node.get_our_node_id(), Some(253 * 10), None).unwrap();
894947
let node_0_shutdown = get_event_msg!(nodes[0], MessageSendEvent::SendShutdown, nodes[1].node.get_our_node_id());
895-
nodes[1].node.close_channel_with_target_feerate(&chan_id, &nodes[0].node.get_our_node_id(), 253 * 5).unwrap();
948+
nodes[1].node.close_channel_with_feerate_and_script(&chan_id, &nodes[0].node.get_our_node_id(), Some(253 * 5), None).unwrap();
896949
let node_1_shutdown = get_event_msg!(nodes[1], MessageSendEvent::SendShutdown, nodes[0].node.get_our_node_id());
897950

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

0 commit comments

Comments
 (0)