Skip to content

Commit af1f4c8

Browse files
committed
Refactor async sign counterparty commitment tests
1 parent f3b20fa commit af1f4c8

File tree

2 files changed

+92
-77
lines changed

2 files changed

+92
-77
lines changed

lightning/src/ln/async_signer_tests.rs

Lines changed: 91 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,54 @@
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 bitcoin::secp256k1::PublicKey;
1314
use bitcoin::{Transaction, TxOut, TxIn, Amount};
1415
use bitcoin::blockdata::locktime::absolute::LockTime;
1516
use bitcoin::transaction::Version;
1617

1718
use crate::chain::channelmonitor::LATENCY_GRACE_PERIOD_BLOCKS;
1819
use crate::events::bump_transaction::WalletSource;
1920
use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider, ClosureReason};
21+
use crate::ln::ChannelId;
2022
use crate::ln::functional_test_utils::*;
2123
use crate::ln::msgs::ChannelMessageHandler;
2224
use crate::ln::channelmanager::{PaymentId, RecipientOnionFields};
25+
use crate::util::test_channel_signer::ops;
26+
27+
/// Helper to run operations with a simulated asynchronous signer.
28+
///
29+
/// Disables the signer for the specified channel and then runs `do_fn`, then re-enables the signer
30+
/// and calls `signer_unblocked`.
31+
#[cfg(test)]
32+
pub fn with_async_signer<DoFn, T>(node: &Node, peer_id: &PublicKey, channel_id: &ChannelId, masks: Vec<u32>, do_fn: DoFn) -> T
33+
where DoFn: Fn() -> T
34+
{
35+
let mask = masks.iter().fold(0, |acc, m| (acc | m));
36+
eprintln!("disabling {}", ops::string_from(mask));
37+
node.set_channel_signer_ops_available(peer_id, channel_id, mask, false);
38+
let res = do_fn();
39+
40+
// Recompute the channel ID just in case the original ID was temporary.
41+
let new_channel_id = {
42+
let channels = node.node.list_channels();
43+
assert_eq!(channels.len(), 1, "expected one channel, not {}", channels.len());
44+
channels[0].channel_id
45+
};
46+
47+
for mask in masks {
48+
eprintln!("enabling {} and calling signer_unblocked", ops::string_from(mask));
49+
node.set_channel_signer_ops_available(peer_id, &new_channel_id, mask, true);
50+
node.node.signer_unblocked(Some((*peer_id, new_channel_id)));
51+
}
52+
res
53+
}
2354

2455
#[test]
2556
fn test_async_commitment_signature_for_funding_created() {
57+
do_test_async_funding_created(vec![ops::SIGN_COUNTERPARTY_COMMITMENT]);
58+
}
59+
60+
fn do_test_async_funding_created(masks: Vec<u32>) {
2661
// Simulate acquiring the signature for `funding_created` asynchronously.
2762
let chanmon_cfgs = create_chanmon_cfgs(2);
2863
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
@@ -43,23 +78,15 @@ fn test_async_commitment_signature_for_funding_created() {
4378
// But! Let's make node[0]'s signer be unavailable: we should *not* broadcast a funding_created
4479
// message...
4580
let (temporary_channel_id, tx, _) = create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 100000, 42);
46-
nodes[0].set_channel_signer_available(&nodes[1].node.get_our_node_id(), &temporary_channel_id, false);
47-
nodes[0].node.funding_transaction_generated(&temporary_channel_id, &nodes[1].node.get_our_node_id(), tx.clone()).unwrap();
48-
check_added_monitors(&nodes[0], 0);
49-
50-
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
51-
52-
// Now re-enable the signer and simulate a retry. The temporary_channel_id won't work anymore so
53-
// we have to dig out the real channel ID.
54-
let chan_id = {
55-
let channels = nodes[0].node.list_channels();
56-
assert_eq!(channels.len(), 1, "expected one channel, not {}", channels.len());
57-
channels[0].channel_id
58-
};
59-
60-
nodes[0].set_channel_signer_available(&nodes[1].node.get_our_node_id(), &chan_id, true);
61-
nodes[0].node.signer_unblocked(Some((nodes[1].node.get_our_node_id(), chan_id)));
62-
81+
with_async_signer(&nodes[0], &nodes[1].node.get_our_node_id(), &temporary_channel_id, masks, || {
82+
nodes[0].node.funding_transaction_generated(&temporary_channel_id, &nodes[1].node.get_our_node_id(), tx.clone()).unwrap();
83+
check_added_monitors(&nodes[0], 0);
84+
let events = nodes[0].node.get_and_clear_pending_msg_events();
85+
assert!(events.is_empty(), "expected no message, got {:?}", events);
86+
});
87+
88+
//// Now that we've re-enabled the signer, simulate a retry. The temporary_channel_id won't work anymore so
89+
//// we have to dig out the real channel ID.
6390
let mut funding_created_msg = get_event_msg!(nodes[0], MessageSendEvent::SendFundingCreated, nodes[1].node.get_our_node_id());
6491
nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created_msg);
6592
check_added_monitors(&nodes[1], 1);
@@ -74,6 +101,10 @@ fn test_async_commitment_signature_for_funding_created() {
74101

75102
#[test]
76103
fn test_async_commitment_signature_for_funding_signed() {
104+
do_test_async_funding_signed(vec![ops::SIGN_COUNTERPARTY_COMMITMENT]);
105+
}
106+
107+
fn do_test_async_funding_signed(masks: Vec<u32>) {
77108
// Simulate acquiring the signature for `funding_signed` asynchronously.
78109
let chanmon_cfgs = create_chanmon_cfgs(2);
79110
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
@@ -98,22 +129,14 @@ fn test_async_commitment_signature_for_funding_signed() {
98129

99130
// Now let's make node[1]'s signer be unavailable while handling the `funding_created`. It should
100131
// *not* broadcast a `funding_signed`...
101-
nodes[1].set_channel_signer_available(&nodes[0].node.get_our_node_id(), &temporary_channel_id, false);
102-
nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created_msg);
103-
check_added_monitors(&nodes[1], 1);
104-
105-
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
132+
with_async_signer(&nodes[1], &nodes[0].node.get_our_node_id(), &temporary_channel_id, masks, || {
133+
nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created_msg);
134+
check_added_monitors(&nodes[1], 1);
135+
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
136+
});
106137

107-
// Now re-enable the signer and simulate a retry. The temporary_channel_id won't work anymore so
138+
// Now that we've re-enabled the signer, simulate a retry. The temporary_channel_id won't work anymore so
108139
// we have to dig out the real channel ID.
109-
let chan_id = {
110-
let channels = nodes[0].node.list_channels();
111-
assert_eq!(channels.len(), 1, "expected one channel, not {}", channels.len());
112-
channels[0].channel_id
113-
};
114-
nodes[1].set_channel_signer_available(&nodes[0].node.get_our_node_id(), &chan_id, true);
115-
nodes[1].node.signer_unblocked(Some((nodes[0].node.get_our_node_id(), chan_id)));
116-
117140
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());
118141

119142
// nodes[0] <-- funding_signed --- nodes[1]
@@ -125,6 +148,10 @@ fn test_async_commitment_signature_for_funding_signed() {
125148

126149
#[test]
127150
fn test_async_commitment_signature_for_commitment_signed() {
151+
do_test_async_commitment_signed(vec![ops::SIGN_COUNTERPARTY_COMMITMENT]);
152+
}
153+
154+
fn do_test_async_commitment_signed(masks: Vec<u32>) {
128155
let chanmon_cfgs = create_chanmon_cfgs(2);
129156
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
130157
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
@@ -150,18 +177,15 @@ fn test_async_commitment_signature_for_commitment_signed() {
150177

151178
dst.node.handle_update_add_htlc(&src.node.get_our_node_id(), &payment_event.msgs[0]);
152179

153-
// Mark dst's signer as unavailable and handle src's commitment_signed: while dst won't yet have a
154-
// `commitment_signed` of its own to offer, it should publish a `revoke_and_ack`.
155-
dst.set_channel_signer_available(&src.node.get_our_node_id(), &chan_id, false);
156-
dst.node.handle_commitment_signed(&src.node.get_our_node_id(), &payment_event.commitment_msg);
157-
check_added_monitors(dst, 1);
158-
159-
get_event_msg!(dst, MessageSendEvent::SendRevokeAndACK, src.node.get_our_node_id());
160-
161-
// Mark dst's signer as available and retry: we now expect to see dst's `commitment_signed`.
162-
dst.set_channel_signer_available(&src.node.get_our_node_id(), &chan_id, true);
163-
dst.node.signer_unblocked(Some((src.node.get_our_node_id(), chan_id)));
180+
// Mark dst's signer as unavailable and handle src's commitment_signed. If dst's signer is
181+
// offline, it oughtn't yet respond with any updates.
182+
with_async_signer(dst, &src.node.get_our_node_id(), &chan_id, masks, &|| {
183+
dst.node.handle_commitment_signed(&src.node.get_our_node_id(), &payment_event.commitment_msg);
184+
check_added_monitors(dst, 1);
185+
get_event_msg!(dst, MessageSendEvent::SendRevokeAndACK, src.node.get_our_node_id());
186+
});
164187

188+
// Now that we marked dst's signer as available, retry: we now expect to see dst's `commitment_signed`.
165189
let events = dst.node.get_and_clear_pending_msg_events();
166190
assert_eq!(events.len(), 1, "expected one message, got {}", events.len());
167191
if let MessageSendEvent::UpdateHTLCs { ref node_id, .. } = events[0] {
@@ -173,6 +197,10 @@ fn test_async_commitment_signature_for_commitment_signed() {
173197

174198
#[test]
175199
fn test_async_commitment_signature_for_funding_signed_0conf() {
200+
do_test_async_funding_signed_0conf(vec![ops::SIGN_COUNTERPARTY_COMMITMENT]);
201+
}
202+
203+
fn do_test_async_funding_signed_0conf(masks: Vec<u32>) {
176204
// Simulate acquiring the signature for `funding_signed` asynchronously for a zero-conf channel.
177205
let mut manually_accept_config = test_default_channel_config();
178206
manually_accept_config.manually_accept_inbound_channels = true;
@@ -215,24 +243,13 @@ fn test_async_commitment_signature_for_funding_signed_0conf() {
215243

216244
// Now let's make node[1]'s signer be unavailable while handling the `funding_created`. It should
217245
// *not* broadcast a `funding_signed`...
218-
nodes[1].set_channel_signer_available(&nodes[0].node.get_our_node_id(), &temporary_channel_id, false);
219-
nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created_msg);
220-
check_added_monitors(&nodes[1], 1);
221-
222-
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
223-
224-
// Now re-enable the signer and simulate a retry. The temporary_channel_id won't work anymore so
225-
// we have to dig out the real channel ID.
226-
let chan_id = {
227-
let channels = nodes[0].node.list_channels();
228-
assert_eq!(channels.len(), 1, "expected one channel, not {}", channels.len());
229-
channels[0].channel_id
230-
};
246+
with_async_signer(&nodes[1], &nodes[0].node.get_our_node_id(), &temporary_channel_id, masks, || {
247+
nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created_msg);
248+
check_added_monitors(&nodes[1], 1);
249+
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
250+
});
231251

232252
// At this point, we basically expect the channel to open like a normal zero-conf channel.
233-
nodes[1].set_channel_signer_available(&nodes[0].node.get_our_node_id(), &chan_id, true);
234-
nodes[1].node.signer_unblocked(Some((nodes[0].node.get_our_node_id(), chan_id)));
235-
236253
let (funding_signed, channel_ready_1) = {
237254
let events = nodes[1].node.get_and_clear_pending_msg_events();
238255
assert_eq!(events.len(), 2);
@@ -299,24 +316,22 @@ fn test_async_commitment_signature_for_peer_disconnect() {
299316

300317
// Mark dst's signer as unavailable and handle src's commitment_signed: while dst won't yet have a
301318
// `commitment_signed` of its own to offer, it should publish a `revoke_and_ack`.
302-
dst.set_channel_signer_available(&src.node.get_our_node_id(), &chan_id, false);
303-
dst.node.handle_commitment_signed(&src.node.get_our_node_id(), &payment_event.commitment_msg);
304-
check_added_monitors(dst, 1);
305-
306-
get_event_msg!(dst, MessageSendEvent::SendRevokeAndACK, src.node.get_our_node_id());
307-
308-
// Now disconnect and reconnect the peers.
309-
src.node.peer_disconnected(&dst.node.get_our_node_id());
310-
dst.node.peer_disconnected(&src.node.get_our_node_id());
311-
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
312-
reconnect_args.send_channel_ready = (false, false);
313-
reconnect_args.pending_raa = (true, false);
314-
reconnect_nodes(reconnect_args);
315-
316-
// Mark dst's signer as available and retry: we now expect to see dst's `commitment_signed`.
317-
dst.set_channel_signer_available(&src.node.get_our_node_id(), &chan_id, true);
318-
dst.node.signer_unblocked(Some((src.node.get_our_node_id(), chan_id)));
319-
319+
with_async_signer(dst, &src.node.get_our_node_id(), &chan_id, vec![ops::SIGN_COUNTERPARTY_COMMITMENT], &|| {
320+
dst.node.handle_commitment_signed(&src.node.get_our_node_id(), &payment_event.commitment_msg);
321+
check_added_monitors(dst, 1);
322+
323+
get_event_msg!(dst, MessageSendEvent::SendRevokeAndACK, src.node.get_our_node_id());
324+
325+
// Now disconnect and reconnect the peers.
326+
src.node.peer_disconnected(&dst.node.get_our_node_id());
327+
dst.node.peer_disconnected(&src.node.get_our_node_id());
328+
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
329+
reconnect_args.send_channel_ready = (false, false);
330+
reconnect_args.pending_raa = (true, false);
331+
reconnect_nodes(reconnect_args);
332+
});
333+
334+
// Now that dst's signer is available, retry: we now expect to see dst's `commitment_signed`.
320335
{
321336
let events = dst.node.get_and_clear_pending_msg_events();
322337
assert_eq!(events.len(), 1, "expected one message, got {}", events.len());

lightning/src/util/test_channel_signer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ impl EcdsaChannelSigner for TestChannelSigner {
233233
self.verify_counterparty_commitment_tx(commitment_tx, secp_ctx);
234234

235235
{
236-
if !*self.available.lock().unwrap() {
236+
if !self.is_signer_available(ops::SIGN_COUNTERPARTY_COMMITMENT) {
237237
return Err(());
238238
}
239239
let mut state = self.state.lock().unwrap();

0 commit comments

Comments
 (0)