Skip to content

Commit e8c95bb

Browse files
committed
Honor ordering when resuming monitor
When we resume the monitor, ensure that we provide the commitment update and revoke-and-ack messages in the order that our counterparty expects them. With an asynchronous signer, it's possible that the message may become available in an order other than what's expected.
1 parent d08cca0 commit e8c95bb

File tree

2 files changed

+78
-6
lines changed

2 files changed

+78
-6
lines changed

lightning/src/ln/async_signer_tests.rs

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -786,12 +786,62 @@ fn channel_update_fee_test() {
786786

787787
alice.node.timer_tick_occurred();
788788

789+
assert!(alice.node.get_and_clear_pending_msg_events().is_empty());
790+
791+
alice.set_channel_signer_ops_available(
792+
&bob.node.get_our_node_id(), &chan_id, ops::GET_PER_COMMITMENT_POINT, true);
793+
alice.node.signer_unblocked(None);
794+
789795
let events = alice.node.get_and_clear_pending_msg_events();
790-
assert_eq!(events.len(), 1);
791-
let (_update_msg, _commitment_signed) = match events[0] { // (1)
792-
MessageSendEvent::UpdateHTLCs { updates: msgs::CommitmentUpdate { ref update_fee, ref commitment_signed, .. }, .. } => {
793-
(update_fee.as_ref(), commitment_signed)
794-
},
795-
_ => panic!("Unexpected event"),
796+
assert_eq!(events.len(), 2);
797+
match (&events[0], &events[1]) {
798+
(MessageSendEvent::SendRevokeAndACK { .. }, MessageSendEvent::UpdateHTLCs { .. }) => {
799+
// TODO(waterson) we'd kind of expect to see an update_fee here, but we actually don't because
800+
// signer_maybe_unblocked doesn't create that. It probably should.
801+
}
802+
(a, b) => {
803+
panic!("Expected SendRevokeAndACK and UpdateHTLCs, got {:?} and {:?}", a, b);
804+
}
805+
}
806+
}
807+
808+
#[test]
809+
fn monitor_honors_commitment_raa_order() {
810+
let chanmon_cfgs = create_chanmon_cfgs(2);
811+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
812+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
813+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
814+
let (_, _, chan_id, _) = create_announced_chan_between_nodes(&nodes, 0, 1);
815+
816+
let alice = &nodes[0];
817+
let bob = &nodes[1];
818+
819+
let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(alice, bob, 8_000_000);
820+
821+
alice.node.send_payment_with_route(&route, payment_hash,
822+
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap();
823+
check_added_monitors!(alice, 1);
824+
825+
let payment_event = {
826+
let mut events = alice.node.get_and_clear_pending_msg_events();
827+
assert_eq!(events.len(), 1);
828+
SendEvent::from_event(events.remove(0))
796829
};
830+
831+
// Make the commitment secret be unavailable. The expectation here is that Bob should send the
832+
// revoke-and-ack first. So even though he can generate a commitment update, he should hold onto
833+
// that until he's ready to revoke.
834+
bob.set_channel_signer_ops_available(&alice.node.get_our_node_id(), &chan_id, ops::RELEASE_COMMITMENT_SECRET, false);
835+
836+
bob.node.handle_update_add_htlc(&alice.node.get_our_node_id(), &payment_event.msgs[0]);
837+
bob.node.handle_commitment_signed(&alice.node.get_our_node_id(), &payment_event.commitment_msg);
838+
check_added_monitors(bob, 1);
839+
840+
assert!(bob.node.get_and_clear_pending_msg_events().is_empty());
841+
842+
// Now make the commitment secret available and restart the channel.
843+
bob.set_channel_signer_ops_available(&alice.node.get_our_node_id(), &chan_id, ops::RELEASE_COMMITMENT_SECRET, true);
844+
bob.node.signer_unblocked(None);
845+
846+
get_revoke_commit_msgs(bob, &alice.node.get_our_node_id());
797847
}

lightning/src/ln/channel.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4107,7 +4107,29 @@ impl<SP: Deref> Channel<SP> where
41074107
self.context.monitor_pending_revoke_and_ack = false;
41084108
self.context.monitor_pending_commitment_signed = false;
41094109

4110+
// Enforce the ordering between commitment update and revoke-and-ack: with an asynchronous
4111+
// signer, they may have become available in an order that violates the ordering that our
4112+
// counterparty expects. If that happens, suppress the out-of-order message and mark it as
4113+
// pending. When the signer becomes unblocked we can provide the messages in the proper order.
41104114
let order = self.context.resend_order.clone();
4115+
let (commitment_update, raa) = match order {
4116+
RAACommitmentOrder::CommitmentFirst => {
4117+
if self.context.signer_pending_commitment_update && raa.is_some() {
4118+
log_trace!(logger, "RAA is available, but we can't deliver it because ordering requires CommitmentFirst; setting signer_pending_revoke_and_ack = true");
4119+
self.context.signer_pending_revoke_and_ack = true;
4120+
}
4121+
(commitment_update, if !self.context.signer_pending_commitment_update { raa } else { None })
4122+
}
4123+
4124+
RAACommitmentOrder::RevokeAndACKFirst => {
4125+
if self.context.signer_pending_revoke_and_ack && commitment_update.is_some() {
4126+
log_trace!(logger, "commitment_update is available, but we can't deliver it because ordering requires RevokeAndACKFirst; setting signer_pending_commitment_update = true");
4127+
self.context.signer_pending_commitment_update = true;
4128+
}
4129+
(if !self.context.signer_pending_revoke_and_ack { commitment_update } else { None }, raa)
4130+
}
4131+
};
4132+
41114133
log_debug!(logger, "Restored monitor updating in channel {} resulting in {}{} commitment update and {} RAA{}",
41124134
&self.context.channel_id(), if funding_broadcastable.is_some() { "a funding broadcastable, " } else { "" },
41134135
if commitment_update.is_some() { "a" } else { "no" }, if raa.is_some() { "an" } else { "no" },

0 commit comments

Comments
 (0)