Skip to content

Commit 3f7d75b

Browse files
committed
WIP Implement closing_sig handler for option_simple_close
Implement the closer-side handling of the `closing_sig` message, which is the response to our `closing_complete`. The new `closing_sig()` method on `FundedChannel` validates that echoed fields match what we sent, ensures exactly one signature is present in a TLV field we offered, verifies the counterparty's signature, builds the signed closing transaction, and transitions the channel to `ShutdownComplete`. The `internal_closing_sig()` method in `ChannelManager` replaces the previous `unimplemented!()` stub, following the same pattern as `internal_closing_complete()` but without queuing a response message since the closer simply broadcasts. Co-Authored-By: HAL 9000 Signed-off-by: Elias Rohrer <dev@tnull.de>
1 parent 29a7f82 commit 3f7d75b

2 files changed

Lines changed: 237 additions & 16 deletions

File tree

lightning/src/ln/channel.rs

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11722,6 +11722,182 @@ where
1172211722
Ok((Some(closing_sig), Some((signed_tx, shutdown_result))))
1172311723
}
1172411724

11725+
pub fn closing_sig<L: Logger>(
11726+
&mut self, msg: &msgs::ClosingSig, logger: &L,
11727+
) -> Result<Option<(Transaction, ShutdownResult)>, ChannelError> {
11728+
if !self.context.channel_state.is_both_sides_shutdown() {
11729+
return Err(ChannelError::close(
11730+
"Remote end sent us a closing_sig before both sides provided a shutdown".to_owned(),
11731+
));
11732+
}
11733+
if self.context.channel_state.is_peer_disconnected() {
11734+
return Err(ChannelError::close(
11735+
"Peer sent closing_sig when we needed a channel_reestablish".to_owned(),
11736+
));
11737+
}
11738+
if !self.context.pending_inbound_htlcs.is_empty()
11739+
|| !self.context.pending_outbound_htlcs.is_empty()
11740+
{
11741+
return Err(ChannelError::close(
11742+
"Remote end sent us a closing_sig while there were still pending HTLCs".to_owned(),
11743+
));
11744+
}
11745+
11746+
// Retrieve the closing_complete we sent that this closing_sig is responding to.
11747+
let v2_closing_negotiation =
11748+
self.context.v2_closing_negotiation.as_ref().ok_or_else(|| {
11749+
ChannelError::close(
11750+
"Received closing_sig without an active v2 closing negotiation".to_owned(),
11751+
)
11752+
})?;
11753+
let sent_closing_complete =
11754+
v2_closing_negotiation.last_sent_closing_complete.as_ref().ok_or_else(|| {
11755+
ChannelError::close(
11756+
"Received closing_sig without having sent closing_complete".to_owned(),
11757+
)
11758+
})?;
11759+
11760+
// Validate that the echoed fields match what we sent in closing_complete.
11761+
if msg.closer_scriptpubkey != sent_closing_complete.closer_scriptpubkey
11762+
|| msg.closee_scriptpubkey != sent_closing_complete.closee_scriptpubkey
11763+
|| msg.fee_satoshis != sent_closing_complete.fee_satoshis
11764+
|| msg.locktime != sent_closing_complete.locktime
11765+
{
11766+
return Err(ChannelError::WarnAndDisconnect(
11767+
"closing_sig fields do not match what was sent in closing_complete".to_owned(),
11768+
));
11769+
}
11770+
11771+
// Validate exactly one signature is present.
11772+
let sig_count = msg.closer_output_only.is_some() as u8
11773+
+ msg.closee_output_only.is_some() as u8
11774+
+ msg.closer_and_closee_outputs.is_some() as u8;
11775+
if sig_count != 1 {
11776+
return Err(ChannelError::WarnAndDisconnect(
11777+
"closing_sig must contain exactly one signature".to_owned(),
11778+
));
11779+
}
11780+
11781+
// Validate that the signature field corresponds to one we sent in closing_complete.
11782+
enum SelectedField {
11783+
CloserOutputOnly,
11784+
CloseeOutputOnly,
11785+
CloserAndCloseeOutputs,
11786+
}
11787+
11788+
let (selected_field, counterparty_sig) = if let Some(sig) = msg.closer_output_only {
11789+
if sent_closing_complete.closer_output_only.is_none() {
11790+
return Err(ChannelError::WarnAndDisconnect(
11791+
"closing_sig contains closer_output_only but we did not send it in \
11792+
closing_complete"
11793+
.to_owned(),
11794+
));
11795+
}
11796+
(SelectedField::CloserOutputOnly, sig)
11797+
} else if let Some(sig) = msg.closee_output_only {
11798+
if sent_closing_complete.closee_output_only.is_none() {
11799+
return Err(ChannelError::WarnAndDisconnect(
11800+
"closing_sig contains closee_output_only but we did not send it in \
11801+
closing_complete"
11802+
.to_owned(),
11803+
));
11804+
}
11805+
(SelectedField::CloseeOutputOnly, sig)
11806+
} else if let Some(sig) = msg.closer_and_closee_outputs {
11807+
if sent_closing_complete.closer_and_closee_outputs.is_none() {
11808+
return Err(ChannelError::WarnAndDisconnect(
11809+
"closing_sig contains closer_and_closee_outputs but we did not send it \
11810+
in closing_complete"
11811+
.to_owned(),
11812+
));
11813+
}
11814+
(SelectedField::CloserAndCloseeOutputs, sig)
11815+
} else {
11816+
// Already checked sig_count == 1, so this is unreachable.
11817+
unreachable!()
11818+
};
11819+
11820+
// Extract our stored signature from the sent closing_complete.
11821+
let our_sig = match selected_field {
11822+
SelectedField::CloserOutputOnly => sent_closing_complete.closer_output_only.unwrap(),
11823+
SelectedField::CloseeOutputOnly => sent_closing_complete.closee_output_only.unwrap(),
11824+
SelectedField::CloserAndCloseeOutputs => {
11825+
sent_closing_complete.closer_and_closee_outputs.unwrap()
11826+
},
11827+
};
11828+
11829+
// Build the closing transaction for the selected variant.
11830+
// We are the closer, so fee comes from our balance.
11831+
let closer_balance_msat = self.funding.value_to_self_msat;
11832+
let closee_balance_msat =
11833+
(self.funding.get_value_satoshis() * 1000).saturating_sub(closer_balance_msat);
11834+
11835+
let closer_is_op_return = msg.closer_scriptpubkey.is_op_return();
11836+
let value_to_closer = if closer_is_op_return {
11837+
Amount::ZERO
11838+
} else {
11839+
Amount::from_sat(closer_balance_msat / 1000 - msg.fee_satoshis)
11840+
};
11841+
let value_to_closee = Amount::from_sat(closee_balance_msat / 1000);
11842+
let lock_time = LockTime::from_consensus(msg.locktime);
11843+
11844+
let funding_outpoint = self.funding_outpoint().into_bitcoin_outpoint();
11845+
let outputs = match selected_field {
11846+
SelectedField::CloserOutputOnly => ClosingTransactionV2Outputs::CloserOutputOnly {
11847+
value_to_closer,
11848+
to_closer_script: msg.closer_scriptpubkey.clone(),
11849+
},
11850+
SelectedField::CloseeOutputOnly => ClosingTransactionV2Outputs::CloseeOutputOnly {
11851+
value_to_closee,
11852+
to_closee_script: msg.closee_scriptpubkey.clone(),
11853+
},
11854+
SelectedField::CloserAndCloseeOutputs => {
11855+
ClosingTransactionV2Outputs::CloserAndCloseeOutputs {
11856+
value_to_closer,
11857+
to_closer_script: msg.closer_scriptpubkey.clone(),
11858+
value_to_closee,
11859+
to_closee_script: msg.closee_scriptpubkey.clone(),
11860+
}
11861+
},
11862+
};
11863+
let closing_tx = ClosingTransaction::new_v2(outputs, funding_outpoint, lock_time);
11864+
11865+
// Verify the counterparty's signature.
11866+
let funding_redeemscript = self.funding.get_funding_redeemscript();
11867+
let sighash = closing_tx
11868+
.trust()
11869+
.get_sighash_all(&funding_redeemscript, self.funding.get_value_satoshis());
11870+
secp_check!(
11871+
self.context.secp_ctx.verify_ecdsa(
11872+
&sighash,
11873+
&counterparty_sig,
11874+
self.funding.counterparty_funding_pubkey(),
11875+
),
11876+
"Invalid closing_sig signature from peer".to_owned()
11877+
);
11878+
11879+
// Build the signed closing transaction.
11880+
let signed_tx =
11881+
self.build_signed_closing_transaction(&closing_tx, &counterparty_sig, &our_sig);
11882+
11883+
// State transitions.
11884+
self.context.channel_state = ChannelState::ShutdownComplete;
11885+
self.context.update_time_counter += 1;
11886+
11887+
let v2_closing_negotiation = self.context.v2_closing_negotiation.as_mut().unwrap();
11888+
v2_closing_negotiation.last_sent_closing_complete = None;
11889+
11890+
let shutdown_result = self.shutdown_result_coop_close();
11891+
11892+
log_info!(
11893+
logger,
11894+
"Received closing_sig with fee {} sat, broadcasting closing tx",
11895+
msg.fee_satoshis,
11896+
);
11897+
11898+
Ok(Some((signed_tx, shutdown_result)))
11899+
}
11900+
1172511901
#[rustfmt::skip]
1172611902
fn internal_htlc_satisfies_config(
1172711903
&self, htlc: &msgs::UpdateAddHTLC, amt_to_forward: u64, outgoing_cltv_value: u32, config: &ChannelConfig,

lightning/src/ln/channelmanager.rs

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12566,23 +12566,68 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1256612566
}
1256712567

1256812568
fn internal_closing_sig(
12569-
&self, _counterparty_node_id: PublicKey, _msg: msgs::ClosingSig,
12569+
&self, counterparty_node_id: PublicKey, msg: msgs::ClosingSig,
1257012570
) -> Result<(), MsgHandleErrInternal> {
12571-
// The receiver of `closing_sig`:
12572-
// - If `closer_scriptpubkey`, `closee_scriptpubkey`, `fee_satoshis` or `locktime` don't match what was sent in `closing_complete`:
12573-
// - MUST either send a `warning` and close the connection, or send an `error` and fail the channel.
12574-
// - If `tlvs` does not contain exactly one signature:
12575-
// - MUST either send a `warning` and close the connection, or send an `error` and fail the channel.
12576-
// - If `tlvs` does not contain one of the TLV fields sent in `closing_complete`:
12577-
// - MUST either send a `warning` and close the connection, or send an `error` and fail the channel.
12578-
// - If the signature field is not valid for the corresponding closing transaction specified in [BOLT #3](03-transactions.md#closing-transaction):
12579-
// - MUST either send a `warning` and close the connection, or send an `error` and fail the channel.
12580-
// - If the signature field is non-compliant with LOW-S-standard rule<sup>[LOWS](https://github.com/bitcoin/bitcoin/pull/6769)</sup>:
12581-
// - MUST either send a `warning` and close the connection, or send an `error` and fail the channel.
12582-
// - otherwise:
12583-
// - MUST broadcast the corresponding closing transaction.
12584-
// - MAY send another `closing_complete` (e.g. with a different `fee_satoshis` or `closer_scriptpubkey`).
12585-
unimplemented!("Handling ClosingSig is not implemented");
12571+
let per_peer_state = self.per_peer_state.read().unwrap();
12572+
let peer_state_mutex = per_peer_state.get(&counterparty_node_id).ok_or_else(|| {
12573+
debug_assert!(false);
12574+
MsgHandleErrInternal::unreachable_no_such_peer(&counterparty_node_id, msg.channel_id)
12575+
})?;
12576+
let logger;
12577+
let tx_err: Option<(_, Result<Infallible, _>)> = {
12578+
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
12579+
let peer_state = &mut *peer_state_lock;
12580+
match peer_state.channel_by_id.entry(msg.channel_id.clone()) {
12581+
hash_map::Entry::Occupied(mut chan_entry) => {
12582+
if let Some(chan) = chan_entry.get_mut().as_funded_mut() {
12583+
logger = WithChannelContext::from(&self.logger, &chan.context, None);
12584+
let res = chan.closing_sig(&msg, &&logger);
12585+
let tx_shutdown_result =
12586+
try_channel_entry!(self, peer_state, res, chan_entry);
12587+
if let Some((tx, close_res)) = tx_shutdown_result {
12588+
let err = self.locked_handle_funded_coop_close(
12589+
&mut peer_state.closed_channel_monitor_update_ids,
12590+
&mut peer_state.in_flight_monitor_updates,
12591+
close_res,
12592+
chan,
12593+
);
12594+
chan_entry.remove();
12595+
Some((tx, Err(err)))
12596+
} else {
12597+
None
12598+
}
12599+
} else {
12600+
return try_channel_entry!(
12601+
self,
12602+
peer_state,
12603+
Err(ChannelError::close(
12604+
"Got a closing_sig message for an unfunded channel!".into()
12605+
)),
12606+
chan_entry
12607+
);
12608+
}
12609+
},
12610+
hash_map::Entry::Vacant(_) => {
12611+
return Err(MsgHandleErrInternal::no_such_channel_for_peer(
12612+
&counterparty_node_id,
12613+
msg.channel_id,
12614+
))
12615+
},
12616+
}
12617+
};
12618+
mem::drop(per_peer_state);
12619+
if let Some((broadcast_tx, err)) = tx_err {
12620+
log_info!(logger, "Broadcasting {}", log_tx!(broadcast_tx));
12621+
self.tx_broadcaster.broadcast_transactions(&[(
12622+
&broadcast_tx,
12623+
TransactionType::CooperativeClose {
12624+
counterparty_node_id,
12625+
channel_id: msg.channel_id,
12626+
},
12627+
)]);
12628+
let _ = self.handle_error(err, counterparty_node_id);
12629+
}
12630+
Ok(())
1258612631
}
1258712632

1258812633
#[rustfmt::skip]

0 commit comments

Comments
 (0)