Skip to content

Commit 4f4bf67

Browse files
committed
Close channels when find_funding_output fails to find an output
In `funding_transaction_generated_intern`, if `find_funding_output` fails (e.g. due to the require output not being present in the provided funding transaction) we'd previously not generated a `ChannelClosed` event which leaves users possibly in a confused state. Here we fix this, also fixing the relevant tests to check for the new event. Fixes #2843.
1 parent 8701b1b commit 4f4bf67

File tree

2 files changed

+53
-29
lines changed

2 files changed

+53
-29
lines changed

lightning/src/ln/channelmanager.rs

+32-23
Original file line numberDiff line numberDiff line change
@@ -4485,7 +4485,7 @@ where
44854485

44864486
/// Handles the generation of a funding transaction, optionally (for tests) with a function
44874487
/// which checks the correctness of the funding transaction given the associated channel.
4488-
fn funding_transaction_generated_intern<FundingOutput: FnMut(&OutboundV1Channel<SP>, &Transaction) -> Result<OutPoint, APIError>>(
4488+
fn funding_transaction_generated_intern<FundingOutput: FnMut(&OutboundV1Channel<SP>, &Transaction) -> Result<OutPoint, &'static str>>(
44894489
&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, funding_transaction: Transaction, is_batch_funding: bool,
44904490
mut find_funding_output: FundingOutput,
44914491
) -> Result<(), APIError> {
@@ -4498,26 +4498,38 @@ where
44984498
let funding_txo;
44994499
let (mut chan, msg_opt) = match peer_state.channel_by_id.remove(temporary_channel_id) {
45004500
Some(ChannelPhase::UnfundedOutboundV1(mut chan)) => {
4501-
funding_txo = find_funding_output(&chan, &funding_transaction)?;
4501+
macro_rules! close_chan { ($err: expr, $api_err: expr, $chan: expr) => { {
4502+
let counterparty;
4503+
let err = if let ChannelError::Close(msg) = $err {
4504+
let channel_id = $chan.context.channel_id();
4505+
counterparty = chan.context.get_counterparty_node_id();
4506+
let reason = ClosureReason::ProcessingError { err: msg.clone() };
4507+
let shutdown_res = $chan.context.force_shutdown(false, reason);
4508+
MsgHandleErrInternal::from_finish_shutdown(msg, channel_id, shutdown_res, None)
4509+
} else { unreachable!(); };
4510+
4511+
mem::drop(peer_state_lock);
4512+
mem::drop(per_peer_state);
4513+
let _: Result<(), _> = handle_error!(self, Err(err), counterparty);
4514+
Err($api_err)
4515+
} } }
4516+
match find_funding_output(&chan, &funding_transaction) {
4517+
Ok(found_funding_txo) => funding_txo = found_funding_txo,
4518+
Err(err) => {
4519+
let chan_err = ChannelError::Close(err.to_owned());
4520+
let api_err = APIError::APIMisuseError { err: err.to_owned() };
4521+
return close_chan!(chan_err, api_err, chan);
4522+
},
4523+
}
45024524

45034525
let logger = WithChannelContext::from(&self.logger, &chan.context);
4504-
let funding_res = chan.get_funding_created(funding_transaction, funding_txo, is_batch_funding, &&logger)
4505-
.map_err(|(mut chan, e)| if let ChannelError::Close(msg) = e {
4506-
let channel_id = chan.context.channel_id();
4507-
let reason = ClosureReason::ProcessingError { err: msg.clone() };
4508-
let shutdown_res = chan.context.force_shutdown(false, reason);
4509-
(chan, MsgHandleErrInternal::from_finish_shutdown(msg, channel_id, shutdown_res, None))
4510-
} else { unreachable!(); });
4526+
let funding_res = chan.get_funding_created(funding_transaction, funding_txo, is_batch_funding, &&logger);
45114527
match funding_res {
45124528
Ok(funding_msg) => (chan, funding_msg),
4513-
Err((chan, err)) => {
4514-
mem::drop(peer_state_lock);
4515-
mem::drop(per_peer_state);
4516-
let _: Result<(), _> = handle_error!(self, Err(err), chan.context.get_counterparty_node_id());
4517-
return Err(APIError::ChannelUnavailable {
4518-
err: "Signer refused to sign the initial commitment transaction".to_owned()
4519-
});
4520-
},
4529+
Err((mut chan, chan_err)) => {
4530+
let api_err = APIError::ChannelUnavailable { err: "Signer refused to sign the initial commitment transaction".to_owned() };
4531+
return close_chan!(chan_err, api_err, chan);
4532+
}
45214533
}
45224534
},
45234535
Some(phase) => {
@@ -4682,17 +4694,14 @@ where
46824694
for (idx, outp) in tx.output.iter().enumerate() {
46834695
if outp.script_pubkey == expected_spk && outp.value == chan.context.get_value_satoshis() {
46844696
if output_index.is_some() {
4685-
return Err(APIError::APIMisuseError {
4686-
err: "Multiple outputs matched the expected script and value".to_owned()
4687-
});
4697+
return Err("Multiple outputs matched the expected script and value");
46884698
}
46894699
output_index = Some(idx as u16);
46904700
}
46914701
}
46924702
if output_index.is_none() {
4693-
return Err(APIError::APIMisuseError {
4694-
err: "No output matched the script_pubkey and value in the FundingGenerationReady event".to_owned()
4695-
});
4703+
eprintln!("FAILING HERE, setting result");
4704+
return Err("No output matched the script_pubkey and value in the FundingGenerationReady event");
46964705
}
46974706
let outpoint = OutPoint { txid: tx.txid(), index: output_index.unwrap() };
46984707
if let Some(funding_batch_state) = funding_batch_state.as_mut() {

lightning/src/ln/shutdown_tests.rs

+21-6
Original file line numberDiff line numberDiff line change
@@ -1401,8 +1401,8 @@ fn batch_funding_failure() {
14011401
let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]);
14021402
let nodes = create_network(4, &node_cfgs, &node_chanmgrs);
14031403

1404-
exchange_open_accept_chan(&nodes[0], &nodes[1], 1_000_000, 0);
1405-
exchange_open_accept_chan(&nodes[0], &nodes[2], 1_000_000, 0);
1404+
let temp_chan_id_a = exchange_open_accept_chan(&nodes[0], &nodes[1], 1_000_000, 0);
1405+
let temp_chan_id_b = exchange_open_accept_chan(&nodes[0], &nodes[2], 1_000_000, 0);
14061406

14071407
let events = nodes[0].node.get_and_clear_pending_events();
14081408
assert_eq!(events.len(), 2);
@@ -1419,14 +1419,29 @@ fn batch_funding_failure() {
14191419
} else { panic!(); }
14201420
}
14211421

1422-
// We should probably end up with an error for both channels, but currently we don't generate
1423-
// an error for the failing channel itself.
14241422
let err = "Error in transaction funding: Misuse error: No output matched the script_pubkey and value in the FundingGenerationReady event".to_string();
1425-
let close = [ExpectedCloseEvent::from_id_reason(ChannelId::v1_from_funding_txid(tx.txid().as_ref(), 0), true, ClosureReason::ProcessingError { err })];
1423+
let temp_err = "No output matched the script_pubkey and value in the FundingGenerationReady event".to_string();
1424+
let post_funding_chan_id_a = ChannelId::v1_from_funding_txid(tx.txid().as_ref(), 0);
1425+
let close = [
1426+
ExpectedCloseEvent::from_id_reason(post_funding_chan_id_a, true, ClosureReason::ProcessingError { err: err.clone() }),
1427+
ExpectedCloseEvent::from_id_reason(temp_chan_id_b, false, ClosureReason::ProcessingError { err: temp_err }),
1428+
];
14261429

14271430
nodes[0].node.batch_funding_transaction_generated(&chans, tx).unwrap_err();
14281431

1429-
get_event_msg!(nodes[0], MessageSendEvent::SendFundingCreated, nodes[1].node.get_our_node_id());
1432+
let msgs = nodes[0].node.get_and_clear_pending_msg_events();
1433+
assert_eq!(msgs.len(), 3);
1434+
// We currently spuriously send `FundingCreated` for the first channel and then immediately
1435+
// fail both channels, which isn't ideal but should be fine.
1436+
assert!(matches!(msgs[0],
1437+
MessageSendEvent::SendFundingCreated { msg: msgs::FundingCreated { temporary_channel_id: temp_chan_id_a, .. }, .. }
1438+
));
1439+
assert!(matches!(msgs[1],
1440+
MessageSendEvent::HandleError { action: msgs::ErrorAction::SendErrorMessage {
1441+
msg: msgs::ErrorMessage { channel_id: post_funding_chan_id_a, .. }, ..
1442+
}, .. }
1443+
));
1444+
14301445
check_closed_events(&nodes[0], &close);
14311446
assert_eq!(nodes[0].node.list_channels().len(), 0);
14321447
}

0 commit comments

Comments
 (0)