mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-02-24 15:02:20 +01:00
Merge pull request #2219 from benthecarman/custom-closing-address
Add ability to set shutdown script when closing channel
This commit is contained in:
commit
ca1d569356
3 changed files with 80 additions and 11 deletions
|
@ -6040,7 +6040,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
|
|||
/// May jump to the channel being fully shutdown (see [`Self::is_shutdown`]) in which case no
|
||||
/// [`ChannelMonitorUpdate`] will be returned).
|
||||
pub fn get_shutdown<SP: Deref>(&mut self, signer_provider: &SP, their_features: &InitFeatures,
|
||||
target_feerate_sats_per_kw: Option<u32>)
|
||||
target_feerate_sats_per_kw: Option<u32>, override_shutdown_script: Option<ShutdownScript>)
|
||||
-> Result<(msgs::Shutdown, Option<&ChannelMonitorUpdate>, Vec<(HTLCSource, PaymentHash)>), APIError>
|
||||
where SP::Target: SignerProvider {
|
||||
for htlc in self.pending_outbound_htlcs.iter() {
|
||||
|
@ -6056,6 +6056,9 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
|
|||
return Err(APIError::ChannelUnavailable{err: "Shutdown already in progress by remote".to_owned()});
|
||||
}
|
||||
}
|
||||
if self.shutdown_scriptpubkey.is_some() && override_shutdown_script.is_some() {
|
||||
return Err(APIError::APIMisuseError{err: "Cannot override shutdown script for a channel with one already set".to_owned()});
|
||||
}
|
||||
assert_eq!(self.channel_state & ChannelState::ShutdownComplete as u32, 0);
|
||||
if self.channel_state & (ChannelState::PeerDisconnected as u32 | ChannelState::MonitorUpdateInProgress as u32) != 0 {
|
||||
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> {
|
|||
let update_shutdown_script = match self.shutdown_scriptpubkey {
|
||||
Some(_) => false,
|
||||
None if !chan_closed => {
|
||||
let shutdown_scriptpubkey = match signer_provider.get_shutdown_scriptpubkey() {
|
||||
Ok(scriptpubkey) => scriptpubkey,
|
||||
Err(_) => return Err(APIError::ChannelUnavailable { err: "Failed to get shutdown scriptpubkey".to_owned() }),
|
||||
// use override shutdown script if provided
|
||||
let shutdown_scriptpubkey = match override_shutdown_script {
|
||||
Some(script) => script,
|
||||
None => {
|
||||
// otherwise, use the shutdown scriptpubkey provided by the signer
|
||||
match signer_provider.get_shutdown_scriptpubkey() {
|
||||
Ok(scriptpubkey) => scriptpubkey,
|
||||
Err(_) => return Err(APIError::ChannelUnavailable{err: "Failed to get shutdown scriptpubkey".to_owned()}),
|
||||
}
|
||||
},
|
||||
};
|
||||
if !shutdown_scriptpubkey.is_compatible(their_features) {
|
||||
return Err(APIError::IncompatibleShutdownScript { script: shutdown_scriptpubkey.clone() });
|
||||
|
|
|
@ -78,6 +78,7 @@ use core::ops::Deref;
|
|||
|
||||
// Re-export this for use in the public API.
|
||||
pub use crate::ln::outbound_payment::{PaymentSendFailure, Retry, RetryableSendFailure, RecipientOnionFields};
|
||||
use crate::ln::script::ShutdownScript;
|
||||
|
||||
// We hold various information about HTLC relay in the HTLC objects in Channel itself:
|
||||
//
|
||||
|
@ -2028,7 +2029,7 @@ where
|
|||
});
|
||||
}
|
||||
|
||||
fn close_channel_internal(&self, channel_id: &[u8; 32], counterparty_node_id: &PublicKey, target_feerate_sats_per_1000_weight: Option<u32>) -> Result<(), APIError> {
|
||||
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> {
|
||||
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
|
||||
|
||||
let mut failed_htlcs: Vec<(HTLCSource, PaymentHash)>;
|
||||
|
@ -2045,7 +2046,7 @@ where
|
|||
let funding_txo_opt = chan_entry.get().get_funding_txo();
|
||||
let their_features = &peer_state.latest_features;
|
||||
let (shutdown_msg, mut monitor_update_opt, htlcs) = chan_entry.get_mut()
|
||||
.get_shutdown(&self.signer_provider, their_features, target_feerate_sats_per_1000_weight)?;
|
||||
.get_shutdown(&self.signer_provider, their_features, target_feerate_sats_per_1000_weight, override_shutdown_script)?;
|
||||
failed_htlcs = htlcs;
|
||||
|
||||
// We can send the `shutdown` message before updating the `ChannelMonitor`
|
||||
|
@ -2112,7 +2113,7 @@ where
|
|||
/// [`Normal`]: crate::chain::chaininterface::ConfirmationTarget::Normal
|
||||
/// [`SendShutdown`]: crate::events::MessageSendEvent::SendShutdown
|
||||
pub fn close_channel(&self, channel_id: &[u8; 32], counterparty_node_id: &PublicKey) -> Result<(), APIError> {
|
||||
self.close_channel_internal(channel_id, counterparty_node_id, None)
|
||||
self.close_channel_internal(channel_id, counterparty_node_id, None, None)
|
||||
}
|
||||
|
||||
/// Begins the process of closing a channel. After this call (plus some timeout), no new HTLCs
|
||||
|
@ -2129,6 +2130,11 @@ where
|
|||
/// transaction feerate below `target_feerate_sat_per_1000_weight` (or the feerate which
|
||||
/// will appear on a force-closure transaction, whichever is lower).
|
||||
///
|
||||
/// The `shutdown_script` provided will be used as the `scriptPubKey` for the closing transaction.
|
||||
/// Will fail if a shutdown script has already been set for this channel by
|
||||
/// ['ChannelHandshakeConfig::commit_upfront_shutdown_pubkey`]. The given shutdown script must
|
||||
/// also be compatible with our and the counterparty's features.
|
||||
///
|
||||
/// May generate a [`SendShutdown`] message event on success, which should be relayed.
|
||||
///
|
||||
/// Raises [`APIError::ChannelUnavailable`] if the channel cannot be closed due to failing to
|
||||
|
@ -2140,8 +2146,8 @@ where
|
|||
/// [`Background`]: crate::chain::chaininterface::ConfirmationTarget::Background
|
||||
/// [`Normal`]: crate::chain::chaininterface::ConfirmationTarget::Normal
|
||||
/// [`SendShutdown`]: crate::events::MessageSendEvent::SendShutdown
|
||||
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> {
|
||||
self.close_channel_internal(channel_id, counterparty_node_id, Some(target_feerate_sats_per_1000_weight))
|
||||
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> {
|
||||
self.close_channel_internal(channel_id, counterparty_node_id, target_feerate_sats_per_1000_weight, shutdown_script)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -31,6 +31,7 @@ use bitcoin::util::address::WitnessVersion;
|
|||
use regex;
|
||||
|
||||
use core::default::Default;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use crate::ln::functional_test_utils::*;
|
||||
|
||||
|
@ -722,6 +723,58 @@ fn test_invalid_shutdown_script() {
|
|||
"Got a nonstandard scriptpubkey (00020000) from remote peer");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_user_shutdown_script() {
|
||||
let mut config = test_default_channel_config();
|
||||
config.channel_handshake_config.announced_channel = true;
|
||||
config.channel_handshake_limits.force_announced_channel_preference = false;
|
||||
config.channel_handshake_config.commit_upfront_shutdown_pubkey = false;
|
||||
let user_cfgs = [None, Some(config), None];
|
||||
let chanmon_cfgs = create_chanmon_cfgs(3);
|
||||
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
|
||||
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &user_cfgs);
|
||||
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
|
||||
|
||||
// Segwit v0 script of the form OP_0 <20-byte hash>
|
||||
let script = Builder::new().push_int(0)
|
||||
.push_slice(&[0; 20])
|
||||
.into_script();
|
||||
|
||||
let shutdown_script = ShutdownScript::try_from(script.clone()).unwrap();
|
||||
|
||||
let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
|
||||
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();
|
||||
check_added_monitors!(nodes[1], 1);
|
||||
|
||||
let mut node_0_shutdown = get_event_msg!(nodes[1], MessageSendEvent::SendShutdown, nodes[0].node.get_our_node_id());
|
||||
|
||||
assert_eq!(node_0_shutdown.scriptpubkey, script);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_already_set_user_shutdown_script() {
|
||||
let mut config = test_default_channel_config();
|
||||
config.channel_handshake_config.announced_channel = true;
|
||||
config.channel_handshake_limits.force_announced_channel_preference = false;
|
||||
let user_cfgs = [None, Some(config), None];
|
||||
let chanmon_cfgs = create_chanmon_cfgs(3);
|
||||
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
|
||||
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &user_cfgs);
|
||||
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
|
||||
|
||||
// Segwit v0 script of the form OP_0 <20-byte hash>
|
||||
let script = Builder::new().push_int(0)
|
||||
.push_slice(&[0; 20])
|
||||
.into_script();
|
||||
|
||||
let shutdown_script = ShutdownScript::try_from(script).unwrap();
|
||||
|
||||
let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
|
||||
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));
|
||||
|
||||
assert_eq!(result, Err(APIError::APIMisuseError { err: "Cannot override shutdown script for a channel with one already set".to_string() }));
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum TimeoutStep {
|
||||
AfterShutdown,
|
||||
|
@ -890,9 +943,9 @@ fn simple_target_feerate_shutdown() {
|
|||
let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
|
||||
let chan_id = OutPoint { txid: chan.3.txid(), index: 0 }.to_channel_id();
|
||||
|
||||
nodes[0].node.close_channel_with_target_feerate(&chan_id, &nodes[1].node.get_our_node_id(), 253 * 10).unwrap();
|
||||
nodes[0].node.close_channel_with_feerate_and_script(&chan_id, &nodes[1].node.get_our_node_id(), Some(253 * 10), None).unwrap();
|
||||
let node_0_shutdown = get_event_msg!(nodes[0], MessageSendEvent::SendShutdown, nodes[1].node.get_our_node_id());
|
||||
nodes[1].node.close_channel_with_target_feerate(&chan_id, &nodes[0].node.get_our_node_id(), 253 * 5).unwrap();
|
||||
nodes[1].node.close_channel_with_feerate_and_script(&chan_id, &nodes[0].node.get_our_node_id(), Some(253 * 5), None).unwrap();
|
||||
let node_1_shutdown = get_event_msg!(nodes[1], MessageSendEvent::SendShutdown, nodes[0].node.get_our_node_id());
|
||||
|
||||
nodes[1].node.handle_shutdown(&nodes[0].node.get_our_node_id(), &node_0_shutdown);
|
||||
|
|
Loading…
Add table
Reference in a new issue