Skip to content

Commit 372ba65

Browse files
authored
Merge pull request #4355 from wpaulino/splice-async-signing
Support async signing of interactive-tx initial commitment signatures
2 parents ee768f4 + ee74209 commit 372ba65

File tree

3 files changed

+237
-51
lines changed

3 files changed

+237
-51
lines changed

lightning/src/ln/async_signer_tests.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,13 @@
1010
//! Tests for asynchronous signing. These tests verify that the channel state machine behaves
1111
//! properly with a signer implementation that asynchronously derives signatures.
1212
13+
use crate::events::bump_transaction::sync::WalletSourceSync;
14+
use crate::ln::funding::SpliceContribution;
15+
use crate::ln::splicing_tests::negotiate_splice_tx;
1316
use crate::prelude::*;
1417
use crate::util::ser::Writeable;
1518
use bitcoin::secp256k1::Secp256k1;
19+
use bitcoin::{Amount, TxOut};
1620

1721
use crate::chain::channelmonitor::LATENCY_GRACE_PERIOD_BLOCKS;
1822
use crate::chain::ChannelMonitorUpdateStatus;
@@ -1550,3 +1554,105 @@ fn test_async_force_close_on_invalid_secret_for_stale_state() {
15501554
check_closed_broadcast(&nodes[1], 1, true);
15511555
check_closed_event(&nodes[1], 1, closure_reason, &[node_id_0], 100_000);
15521556
}
1557+
1558+
#[test]
1559+
fn test_async_splice_initial_commit_sig() {
1560+
let chanmon_cfgs = create_chanmon_cfgs(2);
1561+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
1562+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
1563+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
1564+
1565+
let channel_id = create_announced_chan_between_nodes(&nodes, 0, 1).2;
1566+
send_payment(&nodes[0], &[&nodes[1]], 1_000);
1567+
1568+
let (initiator, acceptor) = (&nodes[0], &nodes[1]);
1569+
let initiator_node_id = initiator.node.get_our_node_id();
1570+
let acceptor_node_id = acceptor.node.get_our_node_id();
1571+
1572+
initiator.disable_channel_signer_op(
1573+
&acceptor_node_id,
1574+
&channel_id,
1575+
SignerOp::SignCounterpartyCommitment,
1576+
);
1577+
acceptor.disable_channel_signer_op(
1578+
&initiator_node_id,
1579+
&channel_id,
1580+
SignerOp::SignCounterpartyCommitment,
1581+
);
1582+
1583+
// Negotiate a splice up until the signature exchange.
1584+
let contribution = SpliceContribution::splice_out(vec![TxOut {
1585+
value: Amount::from_sat(1_000),
1586+
script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(),
1587+
}]);
1588+
negotiate_splice_tx(initiator, acceptor, channel_id, contribution);
1589+
1590+
assert!(initiator.node.get_and_clear_pending_msg_events().is_empty());
1591+
assert!(acceptor.node.get_and_clear_pending_msg_events().is_empty());
1592+
1593+
// Have the initiator sign the funding transaction. We won't see their initial commitment signed
1594+
// go out until their signer returns.
1595+
let event = get_event!(initiator, Event::FundingTransactionReadyForSigning);
1596+
if let Event::FundingTransactionReadyForSigning { unsigned_transaction, .. } = event {
1597+
let partially_signed_tx = initiator.wallet_source.sign_tx(unsigned_transaction).unwrap();
1598+
initiator
1599+
.node
1600+
.funding_transaction_signed(&channel_id, &acceptor_node_id, partially_signed_tx)
1601+
.unwrap();
1602+
}
1603+
1604+
assert!(initiator.node.get_and_clear_pending_msg_events().is_empty());
1605+
assert!(acceptor.node.get_and_clear_pending_msg_events().is_empty());
1606+
1607+
initiator.enable_channel_signer_op(
1608+
&acceptor_node_id,
1609+
&channel_id,
1610+
SignerOp::SignCounterpartyCommitment,
1611+
);
1612+
initiator.node.signer_unblocked(None);
1613+
1614+
// Have the acceptor process the message. They should be able to send their `tx_signatures` as
1615+
// they go first, but it is held back as their initial `commitment_signed` is not ready yet.
1616+
let initiator_commit_sig = get_htlc_update_msgs(initiator, &acceptor_node_id);
1617+
acceptor
1618+
.node
1619+
.handle_commitment_signed(initiator_node_id, &initiator_commit_sig.commitment_signed[0]);
1620+
check_added_monitors(acceptor, 1);
1621+
assert!(acceptor.node.get_and_clear_pending_msg_events().is_empty());
1622+
1623+
// Reestablish the channel to make sure the acceptor doesn't attempt to retransmit any messages
1624+
// that are not ready yet.
1625+
initiator.node.peer_disconnected(acceptor_node_id);
1626+
acceptor.node.peer_disconnected(initiator_node_id);
1627+
reconnect_nodes(ReconnectArgs::new(initiator, acceptor));
1628+
1629+
// Re-enable the acceptor's signer. We should see both their initial `commitment_signed` and
1630+
// `tx_signatures` go out.
1631+
acceptor.enable_channel_signer_op(
1632+
&initiator_node_id,
1633+
&channel_id,
1634+
SignerOp::SignCounterpartyCommitment,
1635+
);
1636+
acceptor.node.signer_unblocked(None);
1637+
1638+
let msg_events = acceptor.node.get_and_clear_pending_msg_events();
1639+
assert_eq!(msg_events.len(), 2, "{msg_events:?}");
1640+
if let MessageSendEvent::UpdateHTLCs { updates, .. } = &msg_events[0] {
1641+
initiator.node.handle_commitment_signed(acceptor_node_id, &updates.commitment_signed[0]);
1642+
check_added_monitors(initiator, 1);
1643+
} else {
1644+
panic!("Unexpected event");
1645+
}
1646+
if let MessageSendEvent::SendTxSignatures { msg, .. } = &msg_events[1] {
1647+
initiator.node.handle_tx_signatures(acceptor_node_id, &msg);
1648+
} else {
1649+
panic!("Unexpected event");
1650+
}
1651+
1652+
let tx_signatures =
1653+
get_event_msg!(initiator, MessageSendEvent::SendTxSignatures, acceptor_node_id);
1654+
acceptor.node.handle_tx_signatures(initiator_node_id, &tx_signatures);
1655+
1656+
let _ = get_event!(initiator, Event::SplicePending);
1657+
let _ = get_event!(acceptor, Event::SplicePending);
1658+
}

lightning/src/ln/channel.rs

Lines changed: 113 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,6 +1158,8 @@ pub(super) struct SignerResumeUpdates {
11581158
pub accept_channel: Option<msgs::AcceptChannel>,
11591159
pub funding_created: Option<msgs::FundingCreated>,
11601160
pub funding_signed: Option<msgs::FundingSigned>,
1161+
pub funding_commit_sig: Option<msgs::CommitmentSigned>,
1162+
pub tx_signatures: Option<msgs::TxSignatures>,
11611163
pub channel_ready: Option<msgs::ChannelReady>,
11621164
pub order: RAACommitmentOrder,
11631165
pub closing_signed: Option<msgs::ClosingSigned>,
@@ -1605,6 +1607,8 @@ where
16051607
accept_channel: None,
16061608
funding_created,
16071609
funding_signed: None,
1610+
funding_commit_sig: None,
1611+
tx_signatures: None,
16081612
channel_ready: None,
16091613
order: chan.context.resend_order.clone(),
16101614
closing_signed: None,
@@ -1621,6 +1625,8 @@ where
16211625
accept_channel,
16221626
funding_created: None,
16231627
funding_signed: None,
1628+
funding_commit_sig: None,
1629+
tx_signatures: None,
16241630
channel_ready: None,
16251631
order: chan.context.resend_order.clone(),
16261632
closing_signed: None,
@@ -3057,8 +3063,9 @@ pub(super) struct ChannelContext<SP: SignerProvider> {
30573063
/// setting it again as a side-effect of [`FundedChannel::channel_reestablish`].
30583064
signer_pending_commitment_update: bool,
30593065
/// Similar to [`Self::signer_pending_commitment_update`] but we're waiting to send either a
3060-
/// [`msgs::FundingCreated`] or [`msgs::FundingSigned`] depending on if this channel is
3061-
/// outbound or inbound.
3066+
/// [`msgs::FundingCreated`] for an outbound V1 channel, [`msgs::FundingSigned`] for an inbound
3067+
/// V1 channel, or [`msgs::CommitmentSigned`] for a V2 channel (dual-funded) or a funded channel
3068+
/// with a pending splice.
30623069
signer_pending_funding: bool,
30633070
/// If we attempted to sign a cooperative close transaction but the signer wasn't ready, then this
30643071
/// will be set to `true`.
@@ -6362,7 +6369,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
63626369
}
63636370

63646371
fn get_initial_commitment_signed_v2<L: Logger>(
6365-
&self, funding: &FundingScope, logger: &L,
6372+
&mut self, funding: &FundingScope, logger: &L,
63666373
) -> Option<msgs::CommitmentSigned> {
63676374
let signatures = self.get_initial_counterparty_commitment_signatures(funding, logger);
63686375
if let Some((signature, htlc_signatures)) = signatures {
@@ -6371,6 +6378,7 @@ impl<SP: SignerProvider> ChannelContext<SP> {
63716378
// We shouldn't expect any HTLCs before `ChannelReady`.
63726379
debug_assert!(htlc_signatures.is_empty());
63736380
}
6381+
self.signer_pending_funding = false;
63746382
Some(msgs::CommitmentSigned {
63756383
channel_id: self.channel_id,
63766384
htlc_signatures,
@@ -6380,7 +6388,11 @@ impl<SP: SignerProvider> ChannelContext<SP> {
63806388
partial_signature_with_nonce: None,
63816389
})
63826390
} else {
6383-
// TODO(splicing): Support async signing
6391+
log_debug!(
6392+
logger,
6393+
"Initial counterparty commitment signature not available, waiting on async signer"
6394+
);
6395+
self.signer_pending_funding = true;
63846396
None
63856397
}
63866398
}
@@ -9372,6 +9384,11 @@ where
93729384
// We want to clear that the monitor update for our `tx_signatures` has completed, but
93739385
// we may still need to hold back the message until it's ready to be sent.
93749386
self.context.monitor_pending_tx_signatures = false;
9387+
9388+
if self.context.signer_pending_funding {
9389+
tx_signatures.take();
9390+
}
9391+
93759392
let signing_session = self.context.interactive_tx_signing_session.as_ref()
93769393
.expect("We have a tx_signatures message so we must have a valid signing session");
93779394
if !signing_session.holder_sends_tx_signatures_first()
@@ -9547,7 +9564,12 @@ where
95479564
log_trace!(logger, "Attempting to update holder per-commitment point...");
95489565
self.holder_commitment_point.try_resolve_pending(&self.context.holder_signer, &self.context.secp_ctx, logger);
95499566
}
9550-
let funding_signed = if self.context.signer_pending_funding && !self.funding.is_outbound() {
9567+
9568+
let funding_signed = if self.context.signer_pending_funding
9569+
&& !self.is_v2_established()
9570+
&& !self.funding.is_outbound()
9571+
&& self.pending_splice.is_none()
9572+
{
95519573
let commitment_data = self.context.build_commitment_transaction(&self.funding,
95529574
// The previous transaction number (i.e., when adding 1) is used because this field
95539575
// is advanced when handling funding_created, but the point is not advanced until
@@ -9557,6 +9579,43 @@ where
95579579
let counterparty_initial_commitment_tx = commitment_data.tx;
95589580
self.context.get_funding_signed_msg(&self.funding.channel_transaction_parameters, logger, counterparty_initial_commitment_tx)
95599581
} else { None };
9582+
9583+
let funding_commit_sig = if self.context.signer_pending_funding
9584+
&& (self.is_v2_established() || self.pending_splice.is_some())
9585+
{
9586+
log_debug!(logger, "Attempting to generate pending initial commitment_signed...");
9587+
let funding = self
9588+
.pending_splice
9589+
.as_ref()
9590+
.and_then(|pending_splice| pending_splice.funding_negotiation.as_ref())
9591+
.and_then(|funding_negotiation| {
9592+
debug_assert!(matches!(
9593+
funding_negotiation,
9594+
FundingNegotiation::AwaitingSignatures { .. }
9595+
));
9596+
funding_negotiation.as_funding()
9597+
})
9598+
.unwrap_or(&self.funding);
9599+
self.context.get_initial_commitment_signed_v2(funding, logger)
9600+
} else {
9601+
None
9602+
};
9603+
9604+
let tx_signatures = if funding_commit_sig.is_some() {
9605+
if let Some(signing_session) = self.context.interactive_tx_signing_session.as_ref() {
9606+
let should_send_tx_signatures = signing_session.holder_sends_tx_signatures_first()
9607+
|| signing_session.has_received_tx_signatures();
9608+
should_send_tx_signatures
9609+
.then(|| ())
9610+
.and_then(|_| signing_session.holder_tx_signatures().clone())
9611+
} else {
9612+
debug_assert!(false);
9613+
None
9614+
}
9615+
} else {
9616+
None
9617+
};
9618+
95609619
// Provide a `channel_ready` message if we need to, but only if we're _not_ still pending
95619620
// funding.
95629621
let channel_ready = if self.context.signer_pending_channel_ready && !self.context.signer_pending_funding {
@@ -9615,12 +9674,14 @@ where
96159674
} else { (None, None, None) }
96169675
} else { (None, None, None) };
96179676

9618-
log_trace!(logger, "Signer unblocked with {} commitment_update, {} revoke_and_ack, with resend order {:?}, {} funding_signed, {} channel_ready,
9619-
{} closing_signed, {} signed_closing_tx, and {} shutdown result",
9677+
log_trace!(logger, "Signer unblocked with {} commitment_update, {} revoke_and_ack, with resend order {:?}, {} funding_signed, \
9678+
{} funding commit_sig, {} tx_signatures, {} channel_ready, {} closing_signed, {} signed_closing_tx, and {} shutdown result",
96209679
if commitment_update.is_some() { "a" } else { "no" },
96219680
if revoke_and_ack.is_some() { "a" } else { "no" },
96229681
self.context.resend_order,
96239682
if funding_signed.is_some() { "a" } else { "no" },
9683+
if funding_commit_sig.is_some() { "a" } else { "no" },
9684+
if tx_signatures.is_some() { "a" } else { "no" },
96249685
if channel_ready.is_some() { "a" } else { "no" },
96259686
if closing_signed.is_some() { "a" } else { "no" },
96269687
if signed_closing_tx.is_some() { "a" } else { "no" },
@@ -9633,6 +9694,8 @@ where
96339694
accept_channel: None,
96349695
funding_created: None,
96359696
funding_signed,
9697+
funding_commit_sig,
9698+
tx_signatures,
96369699
channel_ready,
96379700
order: self.context.resend_order.clone(),
96389701
closing_signed,
@@ -9930,6 +9993,7 @@ where
99309993

99319994
// A receiving node:
99329995
// - if the `next_funding` TLV is set:
9996+
let mut retransmit_funding_commit_sig = None;
99339997
if let Some(next_funding) = &msg.next_funding {
99349998
// - if `next_funding_txid` matches the latest interactive funding transaction
99359999
// or the current channel funding transaction:
@@ -9952,49 +10016,7 @@ where
995210016
&& next_funding.should_retransmit(msgs::NextFundingFlag::CommitmentSigned)
995310017
{
995410018
// - MUST retransmit its `commitment_signed` for that funding transaction.
9955-
let funding = self
9956-
.pending_splice
9957-
.as_ref()
9958-
.and_then(|pending_splice| pending_splice.funding_negotiation.as_ref())
9959-
.and_then(|funding_negotiation| {
9960-
if let FundingNegotiation::AwaitingSignatures { funding, .. } = &funding_negotiation {
9961-
Some(funding)
9962-
} else {
9963-
None
9964-
}
9965-
})
9966-
.or_else(|| Some(&self.funding))
9967-
.filter(|funding| funding.get_funding_txid() == Some(next_funding.txid))
9968-
.ok_or_else(|| {
9969-
let message = "Failed to find funding for new commitment_signed".to_owned();
9970-
ChannelError::Close(
9971-
(
9972-
message.clone(),
9973-
ClosureReason::HolderForceClosed { message, broadcasted_latest_txn: Some(false) },
9974-
)
9975-
)
9976-
})?;
9977-
9978-
let commitment_signed = self.context.get_initial_commitment_signed_v2(&funding, logger)
9979-
// TODO(splicing): Support async signing
9980-
.ok_or_else(|| {
9981-
let message = "Failed to get signatures for new commitment_signed".to_owned();
9982-
ChannelError::Close(
9983-
(
9984-
message.clone(),
9985-
ClosureReason::HolderForceClosed { message, broadcasted_latest_txn: Some(false) },
9986-
)
9987-
)
9988-
})?;
9989-
9990-
commitment_update = Some(msgs::CommitmentUpdate {
9991-
commitment_signed: vec![commitment_signed],
9992-
update_add_htlcs: vec![],
9993-
update_fulfill_htlcs: vec![],
9994-
update_fail_htlcs: vec![],
9995-
update_fail_malformed_htlcs: vec![],
9996-
update_fee: None,
9997-
});
10019+
retransmit_funding_commit_sig = Some(next_funding.txid);
999810020
}
999910021

1000010022
// - if it has already received `commitment_signed` and it should sign first
@@ -10026,6 +10048,47 @@ where
1002610048
"No active signing session. The associated funding transaction may have already been broadcast.".as_bytes().to_vec() });
1002710049
}
1002810050
}
10051+
if let Some(funding_txid) = retransmit_funding_commit_sig {
10052+
let funding = self
10053+
.pending_splice
10054+
.as_ref()
10055+
.and_then(|pending_splice| pending_splice.funding_negotiation.as_ref())
10056+
.and_then(|funding_negotiation| {
10057+
if let FundingNegotiation::AwaitingSignatures { funding, .. } = &funding_negotiation {
10058+
Some(funding)
10059+
} else {
10060+
None
10061+
}
10062+
})
10063+
.or_else(|| Some(&self.funding))
10064+
.filter(|funding| funding.get_funding_txid() == Some(funding_txid))
10065+
.ok_or_else(|| {
10066+
let message = "Failed to find funding for new commitment_signed".to_owned();
10067+
ChannelError::Close(
10068+
(
10069+
message.clone(),
10070+
ClosureReason::HolderForceClosed { message, broadcasted_latest_txn: Some(false) },
10071+
)
10072+
)
10073+
})?;
10074+
10075+
commitment_update = self
10076+
.context
10077+
.get_initial_commitment_signed_v2(&funding, logger)
10078+
.map(|commitment_signed|
10079+
msgs::CommitmentUpdate {
10080+
commitment_signed: vec![commitment_signed],
10081+
update_add_htlcs: vec![],
10082+
update_fulfill_htlcs: vec![],
10083+
update_fail_htlcs: vec![],
10084+
update_fail_malformed_htlcs: vec![],
10085+
update_fee: None,
10086+
}
10087+
);
10088+
if commitment_update.is_none() {
10089+
tx_signatures.take();
10090+
}
10091+
}
1002910092

1003010093
if matches!(self.context.channel_state, ChannelState::AwaitingChannelReady(_)) {
1003110094
// If we're waiting on a monitor update, we shouldn't re-send any channel_ready's.

0 commit comments

Comments
 (0)