Skip to content

Commit 8c31c7f

Browse files
committed
Fix channel_ready
Make sure that we send a `channel_ready` once we complete funding. Add logging around each of places where we change the `signer_pending_*` status. Even more better tests.
1 parent 2674aaa commit 8c31c7f

File tree

3 files changed

+282
-129
lines changed

3 files changed

+282
-129
lines changed

lightning/src/ln/async_signer_tests.rs

+174-83
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,36 @@ use crate::ln::msgs::ChannelMessageHandler;
1919
use crate::ln::channelmanager::{PaymentId, RAACommitmentOrder, RecipientOnionFields};
2020
use crate::util::test_channel_signer::ops;
2121

22-
const OPS: u32 = ops::GET_PER_COMMITMENT_POINT | ops::RELEASE_COMMITMENT_SECRET | ops::SIGN_COUNTERPARTY_COMMITMENT;
22+
/// Helper to run operations with a simulated asynchronous signer.
23+
///
24+
/// Disables the signer for the specified channel and then runs `do_fn`, then re-enables the signer
25+
/// and calls `signer_unblocked`.
26+
#[cfg(test)]
27+
pub fn with_async_signer<'a, DoFn, T>(node: &Node, peer_id: &PublicKey, channel_id: &ChannelId, masks: &Vec<u32>, do_fn: &'a DoFn) -> T
28+
where DoFn: Fn() -> T
29+
{
30+
let mask = masks.iter().fold(0, |acc, m| (acc | m));
31+
eprintln!("disabling {}", ops::string_from(mask));
32+
node.set_channel_signer_ops_available(peer_id, channel_id, mask, false);
33+
let res = do_fn();
2334

24-
#[test]
25-
fn test_funding_created() {
35+
// Recompute the channel ID just in case the original ID was temporary.
36+
let new_channel_id = {
37+
let channels = node.node.list_channels();
38+
assert_eq!(channels.len(), 1, "expected one channel, not {}", channels.len());
39+
channels[0].channel_id
40+
};
41+
42+
for mask in masks {
43+
eprintln!("enabling {} and calling signer_unblocked", ops::string_from(*mask));
44+
node.set_channel_signer_ops_available(peer_id, &new_channel_id, *mask, true);
45+
node.node.signer_unblocked(Some((*peer_id, new_channel_id)));
46+
}
47+
res
48+
}
49+
50+
#[cfg(test)]
51+
fn do_test_funding_created(masks: &Vec<u32>) {
2652
// Simulate acquiring the signature for `funding_created` asynchronously.
2753
let chanmon_cfgs = create_chanmon_cfgs(2);
2854
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
@@ -43,22 +69,11 @@ fn test_funding_created() {
4369
// But! Let's make node[0]'s signer be unavailable: we should *not* broadcast a funding_created
4470
// message...
4571
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_ops_available(&nodes[1].node.get_our_node_id(), &temporary_channel_id, OPS, 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_ops_available(&nodes[1].node.get_our_node_id(), &chan_id, OPS, true);
61-
nodes[0].node.signer_unblocked(Some((nodes[1].node.get_our_node_id(), chan_id)));
72+
with_async_signer(&nodes[0], &nodes[1].node.get_our_node_id(), &temporary_channel_id, masks, &|| {
73+
nodes[0].node.funding_transaction_generated(&temporary_channel_id, &nodes[1].node.get_our_node_id(), tx.clone()).unwrap();
74+
check_added_monitors(&nodes[0], 0);
75+
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
76+
});
6277

6378
let mut funding_created_msg = get_event_msg!(nodes[0], MessageSendEvent::SendFundingCreated, nodes[1].node.get_our_node_id());
6479
nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created_msg);
@@ -73,7 +88,38 @@ fn test_funding_created() {
7388
}
7489

7590
#[test]
76-
fn test_for_funding_signed() {
91+
fn test_funding_created_grs() {
92+
do_test_funding_created(&vec![ops::GET_PER_COMMITMENT_POINT, ops::RELEASE_COMMITMENT_SECRET, ops::SIGN_COUNTERPARTY_COMMITMENT]);
93+
}
94+
95+
#[test]
96+
fn test_funding_created_gsr() {
97+
do_test_funding_created(&vec![ops::GET_PER_COMMITMENT_POINT, ops::SIGN_COUNTERPARTY_COMMITMENT, ops::RELEASE_COMMITMENT_SECRET]);
98+
}
99+
100+
#[test]
101+
fn test_funding_created_rsg() {
102+
do_test_funding_created(&vec![ops::RELEASE_COMMITMENT_SECRET, ops::SIGN_COUNTERPARTY_COMMITMENT, ops::GET_PER_COMMITMENT_POINT]);
103+
}
104+
105+
#[test]
106+
fn test_funding_created_rgs() {
107+
do_test_funding_created(&vec![ops::RELEASE_COMMITMENT_SECRET, ops::GET_PER_COMMITMENT_POINT, ops::SIGN_COUNTERPARTY_COMMITMENT]);
108+
}
109+
110+
#[test]
111+
fn test_funding_created_srg() {
112+
do_test_funding_created(&vec![ops::SIGN_COUNTERPARTY_COMMITMENT, ops::RELEASE_COMMITMENT_SECRET, ops::GET_PER_COMMITMENT_POINT]);
113+
}
114+
115+
#[test]
116+
fn test_funding_created_sgr() {
117+
do_test_funding_created(&vec![ops::SIGN_COUNTERPARTY_COMMITMENT, ops::GET_PER_COMMITMENT_POINT, ops::RELEASE_COMMITMENT_SECRET]);
118+
}
119+
120+
121+
#[cfg(test)]
122+
fn do_test_funding_signed(masks: &Vec<u32>) {
77123
// Simulate acquiring the signature for `funding_signed` asynchronously.
78124
let chanmon_cfgs = create_chanmon_cfgs(2);
79125
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
@@ -98,21 +144,11 @@ fn test_for_funding_signed() {
98144

99145
// Now let's make node[1]'s signer be unavailable while handling the `funding_created`. It should
100146
// *not* broadcast a `funding_signed`...
101-
nodes[1].set_channel_signer_ops_available(&nodes[0].node.get_our_node_id(), &temporary_channel_id, OPS, 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());
106-
107-
// Now re-enable the signer and simulate a retry. The temporary_channel_id won't work anymore so
108-
// 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_ops_available(&nodes[0].node.get_our_node_id(), &chan_id, OPS, true);
115-
nodes[1].node.signer_unblocked(Some((nodes[0].node.get_our_node_id(), chan_id)));
147+
with_async_signer(&nodes[1], &nodes[0].node.get_our_node_id(), &temporary_channel_id, masks, &|| {
148+
nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created_msg);
149+
check_added_monitors(&nodes[1], 1);
150+
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
151+
});
116152

117153
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());
118154

@@ -124,7 +160,38 @@ fn test_for_funding_signed() {
124160
}
125161

126162
#[test]
127-
fn test_commitment_signed() {
163+
fn test_funding_signed_grs() {
164+
do_test_funding_signed(&vec![ops::GET_PER_COMMITMENT_POINT, ops::RELEASE_COMMITMENT_SECRET, ops::SIGN_COUNTERPARTY_COMMITMENT]);
165+
}
166+
167+
#[test]
168+
fn test_funding_signed_gsr() {
169+
do_test_funding_signed(&vec![ops::GET_PER_COMMITMENT_POINT, ops::SIGN_COUNTERPARTY_COMMITMENT, ops::RELEASE_COMMITMENT_SECRET]);
170+
}
171+
172+
#[test]
173+
fn test_funding_signed_rsg() {
174+
do_test_funding_signed(&vec![ops::RELEASE_COMMITMENT_SECRET, ops::SIGN_COUNTERPARTY_COMMITMENT, ops::GET_PER_COMMITMENT_POINT]);
175+
}
176+
177+
#[test]
178+
fn test_funding_signed_rgs() {
179+
do_test_funding_signed(&vec![ops::RELEASE_COMMITMENT_SECRET, ops::GET_PER_COMMITMENT_POINT, ops::SIGN_COUNTERPARTY_COMMITMENT]);
180+
}
181+
182+
#[test]
183+
fn test_funding_signed_srg() {
184+
do_test_funding_signed(&vec![ops::SIGN_COUNTERPARTY_COMMITMENT, ops::RELEASE_COMMITMENT_SECRET, ops::GET_PER_COMMITMENT_POINT]);
185+
}
186+
187+
#[test]
188+
fn test_funding_signed_sgr() {
189+
do_test_funding_signed(&vec![ops::SIGN_COUNTERPARTY_COMMITMENT, ops::GET_PER_COMMITMENT_POINT, ops::RELEASE_COMMITMENT_SECRET]);
190+
}
191+
192+
193+
#[cfg(test)]
194+
fn do_test_commitment_signed(masks: &Vec<u32>) {
128195
let chanmon_cfgs = create_chanmon_cfgs(2);
129196
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
130197
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
@@ -152,24 +219,48 @@ fn test_commitment_signed() {
152219

153220
// Mark dst's signer as unavailable and handle src's commitment_signed. If dst's signer is
154221
// offline, it oughtn't yet respond with any updates.
155-
dst.set_channel_signer_ops_available(&src.node.get_our_node_id(), &chan_id, OPS, false);
156-
dst.node.handle_commitment_signed(&src.node.get_our_node_id(), &payment_event.commitment_msg);
157-
check_added_monitors(dst, 1);
222+
with_async_signer(dst, &src.node.get_our_node_id(), &chan_id, masks, &|| {
223+
dst.node.handle_commitment_signed(&src.node.get_our_node_id(), &payment_event.commitment_msg);
224+
check_added_monitors(dst, 1);
225+
assert!(dst.node.get_and_clear_pending_msg_events().is_empty());
226+
});
158227

159-
{
160-
let events = dst.node.get_and_clear_pending_msg_events();
161-
assert_eq!(events.len(), 0, "expected 0 events to be generated, got {}", events.len());
162-
}
228+
get_revoke_commit_msgs(&dst, &src.node.get_our_node_id());
229+
}
163230

164-
// Mark dst's signer as available and retry: we now expect to see dst's `commitment_signed`.
165-
dst.set_channel_signer_ops_available(&src.node.get_our_node_id(), &chan_id, OPS, true);
166-
dst.node.signer_unblocked(Some((src.node.get_our_node_id(), chan_id)));
231+
#[test]
232+
fn test_commitment_signed_grs() {
233+
do_test_commitment_signed(&vec![ops::GET_PER_COMMITMENT_POINT, ops::RELEASE_COMMITMENT_SECRET, ops::SIGN_COUNTERPARTY_COMMITMENT]);
234+
}
167235

168-
get_revoke_commit_msgs(&dst, &src.node.get_our_node_id());
236+
#[test]
237+
fn test_commitment_signed_gsr() {
238+
do_test_commitment_signed(&vec![ops::GET_PER_COMMITMENT_POINT, ops::SIGN_COUNTERPARTY_COMMITMENT, ops::RELEASE_COMMITMENT_SECRET]);
239+
}
240+
241+
#[test]
242+
fn test_commitment_signed_rsg() {
243+
do_test_commitment_signed(&vec![ops::RELEASE_COMMITMENT_SECRET, ops::SIGN_COUNTERPARTY_COMMITMENT, ops::GET_PER_COMMITMENT_POINT]);
169244
}
170245

171246
#[test]
172-
fn test_funding_signed_0conf() {
247+
fn test_commitment_signed_rgs() {
248+
do_test_commitment_signed(&vec![ops::RELEASE_COMMITMENT_SECRET, ops::GET_PER_COMMITMENT_POINT, ops::SIGN_COUNTERPARTY_COMMITMENT]);
249+
}
250+
251+
#[test]
252+
fn test_commitment_signed_srg() {
253+
do_test_commitment_signed(&vec![ops::SIGN_COUNTERPARTY_COMMITMENT, ops::RELEASE_COMMITMENT_SECRET, ops::GET_PER_COMMITMENT_POINT]);
254+
}
255+
256+
#[test]
257+
fn test_commitment_signed_sgr() {
258+
do_test_commitment_signed(&vec![ops::SIGN_COUNTERPARTY_COMMITMENT, ops::GET_PER_COMMITMENT_POINT, ops::RELEASE_COMMITMENT_SECRET]);
259+
}
260+
261+
262+
#[cfg(test)]
263+
fn do_test_funding_signed_0conf(masks: &Vec<u32>) {
173264
// Simulate acquiring the signature for `funding_signed` asynchronously for a zero-conf channel.
174265
let mut manually_accept_config = test_default_channel_config();
175266
manually_accept_config.manually_accept_inbound_channels = true;
@@ -187,7 +278,6 @@ fn test_funding_signed_0conf() {
187278

188279
{
189280
let events = nodes[1].node.get_and_clear_pending_events();
190-
assert_eq!(events.len(), 1, "Expected one event, got {}", events.len());
191281
match &events[0] {
192282
Event::OpenChannelRequest { temporary_channel_id, .. } => {
193283
nodes[1].node.accept_inbound_channel_from_trusted_peer_0conf(
@@ -196,6 +286,7 @@ fn test_funding_signed_0conf() {
196286
},
197287
ev => panic!("Expected OpenChannelRequest, not {:?}", ev)
198288
}
289+
assert_eq!(events.len(), 1, "Expected one event, got {}", events.len());
199290
}
200291

201292
// nodes[0] <-- accept_channel --- nodes[1]
@@ -212,24 +303,13 @@ fn test_funding_signed_0conf() {
212303

213304
// Now let's make node[1]'s signer be unavailable while handling the `funding_created`. It should
214305
// *not* broadcast a `funding_signed`...
215-
nodes[1].set_channel_signer_ops_available(&nodes[0].node.get_our_node_id(), &temporary_channel_id, OPS, false);
216-
nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created_msg);
217-
check_added_monitors(&nodes[1], 1);
218-
219-
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
220-
221-
// Now re-enable the signer and simulate a retry. The temporary_channel_id won't work anymore so
222-
// we have to dig out the real channel ID.
223-
let chan_id = {
224-
let channels = nodes[0].node.list_channels();
225-
assert_eq!(channels.len(), 1, "expected one channel, not {}", channels.len());
226-
channels[0].channel_id
227-
};
306+
with_async_signer(&nodes[1], &nodes[0].node.get_our_node_id(), &temporary_channel_id, masks, &|| {
307+
nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created_msg);
308+
check_added_monitors(&nodes[1], 1);
309+
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
310+
});
228311

229312
// At this point, we basically expect the channel to open like a normal zero-conf channel.
230-
nodes[1].set_channel_signer_ops_available(&nodes[0].node.get_our_node_id(), &chan_id, OPS, true);
231-
nodes[1].node.signer_unblocked(Some((nodes[0].node.get_our_node_id(), chan_id)));
232-
233313
let (funding_signed, channel_ready_1) = {
234314
let events = nodes[1].node.get_and_clear_pending_msg_events();
235315
let funding_signed = match &events[0] {
@@ -266,26 +346,37 @@ fn test_funding_signed_0conf() {
266346
assert_eq!(nodes[1].node.list_usable_channels().len(), 1);
267347
}
268348

269-
/// Helper to run operations with a simulated asynchronous signer.
270-
///
271-
/// Disables the signer for the specified channel and then runs `do_fn`, then re-enables the signer
272-
/// and calls `signer_unblocked`.
273-
#[cfg(test)]
274-
pub fn with_async_signer<'a, DoFn, T>(node: &Node, peer_id: &PublicKey, channel_id: &ChannelId, masks: &Vec<u32>, do_fn: &'a DoFn) -> T
275-
where DoFn: Fn() -> T
276-
{
277-
let mask = masks.iter().fold(0, |acc, m| (acc | m));
278-
eprintln!("disabling {}", ops::string_from(mask));
279-
node.set_channel_signer_ops_available(peer_id, channel_id, mask, false);
280-
let res = do_fn();
281-
for mask in masks {
282-
eprintln!("enabling {} and calling signer_unblocked", ops::string_from(*mask));
283-
node.set_channel_signer_ops_available(peer_id, channel_id, *mask, true);
284-
node.node.signer_unblocked(Some((*peer_id, *channel_id)));
285-
}
286-
res
349+
#[test]
350+
fn test_funding_signed_0conf_grs() {
351+
do_test_funding_signed_0conf(&vec![ops::GET_PER_COMMITMENT_POINT, ops::RELEASE_COMMITMENT_SECRET, ops::SIGN_COUNTERPARTY_COMMITMENT]);
352+
}
353+
354+
#[test]
355+
fn test_funding_signed_0conf_gsr() {
356+
do_test_funding_signed_0conf(&vec![ops::GET_PER_COMMITMENT_POINT, ops::SIGN_COUNTERPARTY_COMMITMENT, ops::RELEASE_COMMITMENT_SECRET]);
357+
}
358+
359+
#[test]
360+
fn test_funding_signed_0conf_rsg() {
361+
do_test_funding_signed_0conf(&vec![ops::RELEASE_COMMITMENT_SECRET, ops::SIGN_COUNTERPARTY_COMMITMENT, ops::GET_PER_COMMITMENT_POINT]);
362+
}
363+
364+
#[test]
365+
fn test_funding_signed_0conf_rgs() {
366+
do_test_funding_signed_0conf(&vec![ops::RELEASE_COMMITMENT_SECRET, ops::GET_PER_COMMITMENT_POINT, ops::SIGN_COUNTERPARTY_COMMITMENT]);
367+
}
368+
369+
#[test]
370+
fn test_funding_signed_0conf_srg() {
371+
do_test_funding_signed_0conf(&vec![ops::SIGN_COUNTERPARTY_COMMITMENT, ops::RELEASE_COMMITMENT_SECRET, ops::GET_PER_COMMITMENT_POINT]);
287372
}
288373

374+
#[test]
375+
fn test_funding_signed_0conf_sgr() {
376+
do_test_funding_signed_0conf(&vec![ops::SIGN_COUNTERPARTY_COMMITMENT, ops::GET_PER_COMMITMENT_POINT, ops::RELEASE_COMMITMENT_SECRET]);
377+
}
378+
379+
289380
#[cfg(test)]
290381
fn do_test_payment(masks: &Vec<u32>) {
291382
// This runs through a one-hop payment from start to finish, simulating an asynchronous signer at

0 commit comments

Comments
 (0)