Skip to content

Commit 2519fd6

Browse files
committed
Add test for async open and accept channel
1 parent 12dc350 commit 2519fd6

File tree

3 files changed

+130
-23
lines changed

3 files changed

+130
-23
lines changed

lightning/src/ln/async_signer_tests.rs

+119-23
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
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 std::collections::HashSet;
14-
use bitcoin::key::Secp256k1;
13+
use crate::prelude::*;
14+
use bitcoin::secp256k1::Secp256k1;
1515
use bitcoin::{Transaction, TxOut, TxIn, Amount};
1616
use bitcoin::locktime::absolute::LockTime;
1717
use bitcoin::transaction::Version;
@@ -31,7 +31,78 @@ use crate::util::test_channel_signer::SignerOp;
3131
use crate::util::logger::Logger;
3232

3333
#[test]
34-
fn test_async_commitment_signature_for_funding_created() {
34+
fn test_open_channel() {
35+
do_test_open_channel(false);
36+
do_test_open_channel(true);
37+
}
38+
39+
fn do_test_open_channel(zero_conf: bool) {
40+
// Simulate acquiring the commitment point for `open_channel` and `accept_channel` asynchronously.
41+
let mut manually_accept_config = test_default_channel_config();
42+
manually_accept_config.manually_accept_inbound_channels = zero_conf;
43+
44+
let chanmon_cfgs = create_chanmon_cfgs(2);
45+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
46+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(manually_accept_config)]);
47+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
48+
49+
// Open an outbound channel simulating an async signer.
50+
let channel_value_satoshis = 100000;
51+
let user_channel_id = 42;
52+
nodes[0].disable_next_channel_signer_op(SignerOp::GetPerCommitmentPoint);
53+
let channel_id_0 = nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), channel_value_satoshis, 10001, user_channel_id, None, None).unwrap();
54+
55+
{
56+
let msgs = nodes[0].node.get_and_clear_pending_msg_events();
57+
assert!(msgs.is_empty(), "Expected no message events; got {:?}", msgs);
58+
}
59+
60+
nodes[0].enable_channel_signer_op(&nodes[1].node.get_our_node_id(), &channel_id_0, SignerOp::GetPerCommitmentPoint);
61+
nodes[0].node.signer_unblocked(None);
62+
63+
// nodes[0] --- open_channel --> nodes[1]
64+
let mut open_chan_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
65+
66+
// Handle an inbound channel simulating an async signer.
67+
nodes[1].disable_next_channel_signer_op(SignerOp::GetPerCommitmentPoint);
68+
nodes[1].node.handle_open_channel(nodes[0].node.get_our_node_id(), &open_chan_msg);
69+
70+
if zero_conf {
71+
let events = nodes[1].node.get_and_clear_pending_events();
72+
assert_eq!(events.len(), 1, "Expected one event, got {}", events.len());
73+
match &events[0] {
74+
Event::OpenChannelRequest { temporary_channel_id, .. } => {
75+
nodes[1].node.accept_inbound_channel_from_trusted_peer_0conf(
76+
temporary_channel_id, &nodes[0].node.get_our_node_id(), 0)
77+
.expect("Unable to accept inbound zero-conf channel");
78+
},
79+
ev => panic!("Expected OpenChannelRequest, not {:?}", ev)
80+
}
81+
} else {
82+
let msgs = nodes[1].node.get_and_clear_pending_msg_events();
83+
assert!(msgs.is_empty(), "Expected no message events; got {:?}", msgs);
84+
}
85+
86+
let channel_id_1 = {
87+
let channels = nodes[1].node.list_channels();
88+
assert_eq!(channels.len(), 1, "expected one channel, not {}", channels.len());
89+
channels[0].channel_id
90+
};
91+
92+
nodes[1].enable_channel_signer_op(&nodes[0].node.get_our_node_id(), &channel_id_1, SignerOp::GetPerCommitmentPoint);
93+
nodes[1].node.signer_unblocked(None);
94+
95+
// nodes[0] <-- accept_channel --- nodes[1]
96+
get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id());
97+
}
98+
99+
#[test]
100+
fn test_funding_created() {
101+
do_test_funding_created(vec![SignerOp::SignCounterpartyCommitment, SignerOp::GetPerCommitmentPoint]);
102+
do_test_funding_created(vec![SignerOp::GetPerCommitmentPoint, SignerOp::SignCounterpartyCommitment]);
103+
}
104+
105+
fn do_test_funding_created(signer_ops: Vec<SignerOp>) {
35106
// Simulate acquiring the signature for `funding_created` asynchronously.
36107
let chanmon_cfgs = create_chanmon_cfgs(2);
37108
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
@@ -52,7 +123,9 @@ fn test_async_commitment_signature_for_funding_created() {
52123
// But! Let's make node[0]'s signer be unavailable: we should *not* broadcast a funding_created
53124
// message...
54125
let (temporary_channel_id, tx, _) = create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 100000, 42);
55-
nodes[0].disable_channel_signer_op(&nodes[1].node.get_our_node_id(), &temporary_channel_id, SignerOp::SignCounterpartyCommitment);
126+
for op in signer_ops.iter() {
127+
nodes[0].disable_channel_signer_op(&nodes[1].node.get_our_node_id(), &temporary_channel_id, *op);
128+
}
56129
nodes[0].node.funding_transaction_generated(temporary_channel_id, nodes[1].node.get_our_node_id(), tx.clone()).unwrap();
57130
check_added_monitors(&nodes[0], 0);
58131

@@ -66,8 +139,10 @@ fn test_async_commitment_signature_for_funding_created() {
66139
channels[0].channel_id
67140
};
68141

69-
nodes[0].enable_channel_signer_op(&nodes[1].node.get_our_node_id(), &chan_id, SignerOp::SignCounterpartyCommitment);
70-
nodes[0].node.signer_unblocked(Some((nodes[1].node.get_our_node_id(), chan_id)));
142+
for op in signer_ops.iter() {
143+
nodes[0].enable_channel_signer_op(&nodes[1].node.get_our_node_id(), &chan_id, *op);
144+
nodes[0].node.signer_unblocked(Some((nodes[1].node.get_our_node_id(), chan_id)));
145+
}
71146

72147
let mut funding_created_msg = get_event_msg!(nodes[0], MessageSendEvent::SendFundingCreated, nodes[1].node.get_our_node_id());
73148
nodes[1].node.handle_funding_created(nodes[0].node.get_our_node_id(), &funding_created_msg);
@@ -82,7 +157,12 @@ fn test_async_commitment_signature_for_funding_created() {
82157
}
83158

84159
#[test]
85-
fn test_async_commitment_signature_for_funding_signed() {
160+
fn test_funding_signed() {
161+
do_test_funding_signed(vec![SignerOp::SignCounterpartyCommitment, SignerOp::GetPerCommitmentPoint]);
162+
do_test_funding_signed(vec![SignerOp::GetPerCommitmentPoint, SignerOp::SignCounterpartyCommitment]);
163+
}
164+
165+
fn do_test_funding_signed(signer_ops: Vec<SignerOp>) {
86166
// Simulate acquiring the signature for `funding_signed` asynchronously.
87167
let chanmon_cfgs = create_chanmon_cfgs(2);
88168
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
@@ -107,7 +187,9 @@ fn test_async_commitment_signature_for_funding_signed() {
107187

108188
// Now let's make node[1]'s signer be unavailable while handling the `funding_created`. It should
109189
// *not* broadcast a `funding_signed`...
110-
nodes[1].disable_channel_signer_op(&nodes[0].node.get_our_node_id(), &temporary_channel_id, SignerOp::SignCounterpartyCommitment);
190+
for op in signer_ops.iter() {
191+
nodes[1].disable_channel_signer_op(&nodes[0].node.get_our_node_id(), &temporary_channel_id, *op);
192+
}
111193
nodes[1].node.handle_funding_created(nodes[0].node.get_our_node_id(), &funding_created_msg);
112194
check_added_monitors(&nodes[1], 1);
113195

@@ -120,16 +202,21 @@ fn test_async_commitment_signature_for_funding_signed() {
120202
assert_eq!(channels.len(), 1, "expected one channel, not {}", channels.len());
121203
channels[0].channel_id
122204
};
123-
nodes[1].enable_channel_signer_op(&nodes[0].node.get_our_node_id(), &chan_id, SignerOp::SignCounterpartyCommitment);
124-
nodes[1].node.signer_unblocked(Some((nodes[0].node.get_our_node_id(), chan_id)));
125-
126-
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());
127-
128-
// nodes[0] <-- funding_signed --- nodes[1]
129-
let funding_signed_msg = get_event_msg!(nodes[1], MessageSendEvent::SendFundingSigned, nodes[0].node.get_our_node_id());
130-
nodes[0].node.handle_funding_signed(nodes[1].node.get_our_node_id(), &funding_signed_msg);
131-
check_added_monitors(&nodes[0], 1);
132-
expect_channel_pending_event(&nodes[0], &nodes[1].node.get_our_node_id());
205+
for op in signer_ops.iter() {
206+
nodes[1].enable_channel_signer_op(&nodes[0].node.get_our_node_id(), &chan_id, *op);
207+
nodes[1].node.signer_unblocked(Some((nodes[0].node.get_our_node_id(), chan_id)));
208+
if *op == SignerOp::SignCounterpartyCommitment {
209+
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());
210+
211+
// nodes[0] <-- funding_signed --- nodes[1]
212+
let funding_signed_msg = get_event_msg!(nodes[1], MessageSendEvent::SendFundingSigned, nodes[0].node.get_our_node_id());
213+
nodes[0].node.handle_funding_signed(nodes[1].node.get_our_node_id(), &funding_signed_msg);
214+
check_added_monitors(&nodes[0], 1);
215+
expect_channel_pending_event(&nodes[0], &nodes[1].node.get_our_node_id());
216+
} else {
217+
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
218+
}
219+
}
133220
}
134221

135222
#[test]
@@ -178,7 +265,7 @@ fn do_test_async_commitment_signature_for_commitment_signed_revoke_and_ack(enabl
178265
dst.node.handle_commitment_signed(src.node.get_our_node_id(), &payment_event.commitment_msg);
179266
check_added_monitors(dst, 1);
180267

181-
let mut enabled_signer_ops = HashSet::new();
268+
let mut enabled_signer_ops = new_hash_set();
182269
log_trace!(dst.logger, "enable_signer_op_order={:?}", enable_signer_op_order);
183270
for op in enable_signer_op_order {
184271
enabled_signer_ops.insert(op);
@@ -204,7 +291,12 @@ fn do_test_async_commitment_signature_for_commitment_signed_revoke_and_ack(enabl
204291
}
205292

206293
#[test]
207-
fn test_async_commitment_signature_for_funding_signed_0conf() {
294+
fn test_funding_signed_0conf() {
295+
do_test_funding_signed_0conf(vec![SignerOp::GetPerCommitmentPoint, SignerOp::SignCounterpartyCommitment]);
296+
do_test_funding_signed_0conf(vec![SignerOp::SignCounterpartyCommitment, SignerOp::GetPerCommitmentPoint]);
297+
}
298+
299+
fn do_test_funding_signed_0conf(signer_ops: Vec<SignerOp>) {
208300
// Simulate acquiring the signature for `funding_signed` asynchronously for a zero-conf channel.
209301
let mut manually_accept_config = test_default_channel_config();
210302
manually_accept_config.manually_accept_inbound_channels = true;
@@ -247,7 +339,9 @@ fn test_async_commitment_signature_for_funding_signed_0conf() {
247339

248340
// Now let's make node[1]'s signer be unavailable while handling the `funding_created`. It should
249341
// *not* broadcast a `funding_signed`...
250-
nodes[1].disable_channel_signer_op(&nodes[0].node.get_our_node_id(), &temporary_channel_id, SignerOp::SignCounterpartyCommitment);
342+
for op in signer_ops.iter() {
343+
nodes[1].disable_channel_signer_op(&nodes[0].node.get_our_node_id(), &temporary_channel_id, *op);
344+
}
251345
nodes[1].node.handle_funding_created(nodes[0].node.get_our_node_id(), &funding_created_msg);
252346
check_added_monitors(&nodes[1], 1);
253347

@@ -262,8 +356,10 @@ fn test_async_commitment_signature_for_funding_signed_0conf() {
262356
};
263357

264358
// At this point, we basically expect the channel to open like a normal zero-conf channel.
265-
nodes[1].enable_channel_signer_op(&nodes[0].node.get_our_node_id(), &chan_id, SignerOp::SignCounterpartyCommitment);
266-
nodes[1].node.signer_unblocked(Some((nodes[0].node.get_our_node_id(), chan_id)));
359+
for op in signer_ops.iter() {
360+
nodes[1].enable_channel_signer_op(&nodes[0].node.get_our_node_id(), &chan_id, *op);
361+
nodes[1].node.signer_unblocked(Some((nodes[0].node.get_our_node_id(), chan_id)));
362+
}
267363

268364
let (funding_signed, channel_ready_1) = {
269365
let events = nodes[1].node.get_and_clear_pending_msg_events();

lightning/src/ln/functional_test_utils.rs

+5
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,11 @@ impl<'a, 'b, 'c> Node<'a, 'b, 'c> {
580580
entry.insert(signer_op);
581581
};
582582
}
583+
584+
#[cfg(test)]
585+
pub fn disable_next_channel_signer_op(&self, signer_op: SignerOp) {
586+
self.keys_manager.next_signer_disabled_ops.lock().unwrap().insert(signer_op);
587+
}
583588
}
584589

585590
/// If we need an unsafe pointer to a `Node` (ie to reference it in a thread

lightning/src/util/test_utils.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1233,6 +1233,7 @@ pub struct TestKeysInterface {
12331233
enforcement_states: Mutex<HashMap<[u8;32], Arc<Mutex<EnforcementState>>>>,
12341234
expectations: Mutex<Option<VecDeque<OnGetShutdownScriptpubkey>>>,
12351235
pub unavailable_signers_ops: Mutex<HashMap<[u8; 32], HashSet<SignerOp>>>,
1236+
pub next_signer_disabled_ops: Mutex<HashSet<SignerOp>>,
12361237
}
12371238

12381239
impl EntropySource for TestKeysInterface {
@@ -1292,6 +1293,10 @@ impl SignerProvider for TestKeysInterface {
12921293
signer.disable_op(op);
12931294
}
12941295
}
1296+
#[cfg(test)]
1297+
for op in self.next_signer_disabled_ops.lock().unwrap().drain() {
1298+
signer.disable_op(op);
1299+
}
12951300
signer
12961301
}
12971302

@@ -1331,6 +1336,7 @@ impl TestKeysInterface {
13311336
enforcement_states: Mutex::new(new_hash_map()),
13321337
expectations: Mutex::new(None),
13331338
unavailable_signers_ops: Mutex::new(new_hash_map()),
1339+
next_signer_disabled_ops: Mutex::new(new_hash_set()),
13341340
}
13351341
}
13361342

0 commit comments

Comments
 (0)