Skip to content

Commit 6259e7a

Browse files
authored
Merge pull request #1339 from TheBlueMatt/2022-02-0.0.105-sec
0.0.105 Security Fixes
2 parents 82b8d85 + d798ac1 commit 6259e7a

File tree

7 files changed

+336
-37
lines changed

7 files changed

+336
-37
lines changed

CHANGELOG.md

+13-2
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,19 @@
7878
0.0.104 or before and then upgrading again will invalidate existing phantom
7979
SCIDs which may be included in invoices (#1199).
8080

81-
In total, this release features 108 files changed, 6914 insertions, 2095
82-
deletions in 102 commits from 15 authors, in alphabetical order:
81+
## Security
82+
0.0.105 fixes two denial-of-service vulnerabilities which may be reachable from
83+
untrusted input in certain application designs.
84+
85+
* Route calculation spuriously panics when a routing decision is made for a
86+
path where the second-to-last hop is a private channel, included due to a
87+
multi-hop route hint in an invoice.
88+
* `ChannelMonitor::get_claimable_balances` spuriously panics in some scenarios
89+
when the LDK application's local commitment transaction is confirmed while
90+
HTLCs are still pending resolution.
91+
92+
In total, this release features 109 files changed, 7270 insertions, 2131
93+
deletions in 108 commits from 15 authors, in alphabetical order:
8394
* Conor Okus
8495
* Devrandom
8596
* Elias Rohrer

lightning/src/chain/channelmonitor.rs

+17-2
Original file line numberDiff line numberDiff line change
@@ -1392,8 +1392,23 @@ impl<Signer: Sign> ChannelMonitor<Signer> {
13921392
($holder_commitment: expr, $htlc_iter: expr) => {
13931393
for htlc in $htlc_iter {
13941394
if let Some(htlc_input_idx) = htlc.transaction_output_index {
1395-
if us.htlcs_resolved_on_chain.iter().any(|v| v.input_idx == htlc_input_idx) {
1396-
assert!(us.funding_spend_confirmed.is_some());
1395+
if let Some(conf_thresh) = us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| {
1396+
if let OnchainEvent::MaturingOutput { descriptor: SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) } = &event.event {
1397+
if descriptor.outpoint.index as u32 == htlc_input_idx { Some(event.confirmation_threshold()) } else { None }
1398+
} else { None }
1399+
}) {
1400+
debug_assert!($holder_commitment);
1401+
res.push(Balance::ClaimableAwaitingConfirmations {
1402+
claimable_amount_satoshis: htlc.amount_msat / 1000,
1403+
confirmation_height: conf_thresh,
1404+
});
1405+
} else if us.htlcs_resolved_on_chain.iter().any(|v| v.input_idx == htlc_input_idx) {
1406+
// Funding transaction spends should be fully confirmed by the time any
1407+
// HTLC transactions are resolved, unless we're talking about a holder
1408+
// commitment tx, whose resolution is delayed until the CSV timeout is
1409+
// reached, even though HTLCs may be resolved after only
1410+
// ANTI_REORG_DELAY confirmations.
1411+
debug_assert!($holder_commitment || us.funding_spend_confirmed.is_some());
13971412
} else if htlc.offered == $holder_commitment {
13981413
// If the payment was outbound, check if there's an HTLCUpdate
13991414
// indicating we have spent this HTLC with a timeout, claiming it back

lightning/src/ln/functional_test_utils.rs

+13
Original file line numberDiff line numberDiff line change
@@ -123,19 +123,29 @@ pub fn connect_block<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, block: &Block)
123123
do_connect_block(node, block, false);
124124
}
125125

126+
fn call_claimable_balances<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>) {
127+
// Ensure `get_claimable_balances`' self-tests never panic
128+
for funding_outpoint in node.chain_monitor.chain_monitor.list_monitors() {
129+
node.chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances();
130+
}
131+
}
132+
126133
fn do_connect_block<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, block: &Block, skip_intermediaries: bool) {
134+
call_claimable_balances(node);
127135
let height = node.best_block_info().1 + 1;
128136
if !skip_intermediaries {
129137
let txdata: Vec<_> = block.txdata.iter().enumerate().collect();
130138
match *node.connect_style.borrow() {
131139
ConnectStyle::BestBlockFirst|ConnectStyle::BestBlockFirstSkippingBlocks => {
132140
node.chain_monitor.chain_monitor.best_block_updated(&block.header, height);
141+
call_claimable_balances(node);
133142
node.chain_monitor.chain_monitor.transactions_confirmed(&block.header, &txdata, height);
134143
node.node.best_block_updated(&block.header, height);
135144
node.node.transactions_confirmed(&block.header, &txdata, height);
136145
},
137146
ConnectStyle::TransactionsFirst|ConnectStyle::TransactionsFirstSkippingBlocks => {
138147
node.chain_monitor.chain_monitor.transactions_confirmed(&block.header, &txdata, height);
148+
call_claimable_balances(node);
139149
node.chain_monitor.chain_monitor.best_block_updated(&block.header, height);
140150
node.node.transactions_confirmed(&block.header, &txdata, height);
141151
node.node.best_block_updated(&block.header, height);
@@ -146,11 +156,13 @@ fn do_connect_block<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, block: &Block, s
146156
}
147157
}
148158
}
159+
call_claimable_balances(node);
149160
node.node.test_process_background_events();
150161
node.blocks.lock().unwrap().push((block.header, height));
151162
}
152163

153164
pub fn disconnect_blocks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, count: u32) {
165+
call_claimable_balances(node);
154166
for i in 0..count {
155167
let orig_header = node.blocks.lock().unwrap().pop().unwrap();
156168
assert!(orig_header.1 > 0); // Cannot disconnect genesis
@@ -172,6 +184,7 @@ pub fn disconnect_blocks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, count: u32)
172184
node.node.best_block_updated(&prev_header.0, prev_header.1);
173185
},
174186
}
187+
call_claimable_balances(node);
175188
}
176189
}
177190

lightning/src/ln/monitor_tests.rs

+190
Original file line numberDiff line numberDiff line change
@@ -536,3 +536,193 @@ fn test_claim_value_force_close() {
536536
do_test_claim_value_force_close(true);
537537
do_test_claim_value_force_close(false);
538538
}
539+
540+
#[test]
541+
fn test_balances_on_local_commitment_htlcs() {
542+
// Previously, when handling the broadcast of a local commitment transactions (with associated
543+
// CSV delays prior to spendability), we incorrectly handled the CSV delays on HTLC
544+
// transactions. This caused us to miss spendable outputs for HTLCs which were awaiting a CSV
545+
// delay prior to spendability.
546+
//
547+
// Further, because of this, we could hit an assertion as `get_claimable_balances` asserted
548+
// that HTLCs were resolved after the funding spend was resolved, which was not true if the
549+
// HTLC did not have a CSV delay attached (due to the above bug or due to it being an HTLC
550+
// claim by our counterparty).
551+
let chanmon_cfgs = create_chanmon_cfgs(2);
552+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
553+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
554+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
555+
556+
// Create a single channel with two pending HTLCs from nodes[0] to nodes[1], one which nodes[1]
557+
// knows the preimage for, one which it does not.
558+
let (_, _, chan_id, funding_tx) = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0, InitFeatures::known(), InitFeatures::known());
559+
let funding_outpoint = OutPoint { txid: funding_tx.txid(), index: 0 };
560+
561+
let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 10_000_000);
562+
let htlc_cltv_timeout = nodes[0].best_block_info().1 + TEST_FINAL_CLTV + 1; // Note ChannelManager adds one to CLTV timeouts for safety
563+
nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
564+
check_added_monitors!(nodes[0], 1);
565+
566+
let updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
567+
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &updates.update_add_htlcs[0]);
568+
commitment_signed_dance!(nodes[1], nodes[0], updates.commitment_signed, false);
569+
570+
expect_pending_htlcs_forwardable!(nodes[1]);
571+
expect_payment_received!(nodes[1], payment_hash, payment_secret, 10_000_000);
572+
573+
let (route_2, payment_hash_2, payment_preimage_2, payment_secret_2) = get_route_and_payment_hash!(nodes[0], nodes[1], 20_000_000);
574+
nodes[0].node.send_payment(&route_2, payment_hash_2, &Some(payment_secret_2)).unwrap();
575+
check_added_monitors!(nodes[0], 1);
576+
577+
let updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
578+
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &updates.update_add_htlcs[0]);
579+
commitment_signed_dance!(nodes[1], nodes[0], updates.commitment_signed, false);
580+
581+
expect_pending_htlcs_forwardable!(nodes[1]);
582+
expect_payment_received!(nodes[1], payment_hash_2, payment_secret_2, 20_000_000);
583+
assert!(nodes[1].node.claim_funds(payment_preimage_2));
584+
get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
585+
check_added_monitors!(nodes[1], 1);
586+
587+
let chan_feerate = get_feerate!(nodes[0], chan_id) as u64;
588+
let opt_anchors = get_opt_anchors!(nodes[0], chan_id);
589+
590+
// Get nodes[0]'s commitment transaction and HTLC-Timeout transactions
591+
let as_txn = get_local_commitment_txn!(nodes[0], chan_id);
592+
assert_eq!(as_txn.len(), 3);
593+
check_spends!(as_txn[1], as_txn[0]);
594+
check_spends!(as_txn[2], as_txn[0]);
595+
check_spends!(as_txn[0], funding_tx);
596+
597+
// First confirm the commitment transaction on nodes[0], which should leave us with three
598+
// claimable balances.
599+
let node_a_commitment_claimable = nodes[0].best_block_info().1 + BREAKDOWN_TIMEOUT as u32;
600+
mine_transaction(&nodes[0], &as_txn[0]);
601+
check_added_monitors!(nodes[0], 1);
602+
check_closed_broadcast!(nodes[0], true);
603+
check_closed_event!(nodes[0], 1, ClosureReason::CommitmentTxConfirmed);
604+
605+
assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
606+
claimable_amount_satoshis: 1_000_000 - 10_000 - 20_000 - chan_feerate *
607+
(channel::commitment_tx_base_weight(opt_anchors) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
608+
confirmation_height: node_a_commitment_claimable,
609+
}, Balance::MaybeClaimableHTLCAwaitingTimeout {
610+
claimable_amount_satoshis: 10_000,
611+
claimable_height: htlc_cltv_timeout,
612+
}, Balance::MaybeClaimableHTLCAwaitingTimeout {
613+
claimable_amount_satoshis: 20_000,
614+
claimable_height: htlc_cltv_timeout,
615+
}]),
616+
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
617+
618+
// Get nodes[1]'s HTLC claim tx for the second HTLC
619+
mine_transaction(&nodes[1], &as_txn[0]);
620+
check_added_monitors!(nodes[1], 1);
621+
check_closed_broadcast!(nodes[1], true);
622+
check_closed_event!(nodes[1], 1, ClosureReason::CommitmentTxConfirmed);
623+
let bs_htlc_claim_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
624+
assert_eq!(bs_htlc_claim_txn.len(), 3);
625+
check_spends!(bs_htlc_claim_txn[0], as_txn[0]);
626+
check_spends!(bs_htlc_claim_txn[1], funding_tx);
627+
check_spends!(bs_htlc_claim_txn[2], bs_htlc_claim_txn[1]);
628+
629+
// Connect blocks until the HTLCs expire, allowing us to (validly) broadcast the HTLC-Timeout
630+
// transaction.
631+
connect_blocks(&nodes[0], TEST_FINAL_CLTV - 1);
632+
assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
633+
claimable_amount_satoshis: 1_000_000 - 10_000 - 20_000 - chan_feerate *
634+
(channel::commitment_tx_base_weight(opt_anchors) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
635+
confirmation_height: node_a_commitment_claimable,
636+
}, Balance::MaybeClaimableHTLCAwaitingTimeout {
637+
claimable_amount_satoshis: 10_000,
638+
claimable_height: htlc_cltv_timeout,
639+
}, Balance::MaybeClaimableHTLCAwaitingTimeout {
640+
claimable_amount_satoshis: 20_000,
641+
claimable_height: htlc_cltv_timeout,
642+
}]),
643+
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
644+
assert_eq!(as_txn[1].lock_time, nodes[0].best_block_info().1 + 1); // as_txn[1] can be included in the next block
645+
646+
// Now confirm nodes[0]'s HTLC-Timeout transaction, which changes the claimable balance to an
647+
// "awaiting confirmations" one.
648+
let node_a_htlc_claimable = nodes[0].best_block_info().1 + BREAKDOWN_TIMEOUT as u32;
649+
mine_transaction(&nodes[0], &as_txn[1]);
650+
// Note that prior to the fix in the commit which introduced this test, this (and the next
651+
// balance) check failed. With this check removed, the code panicked in the `connect_blocks`
652+
// call, as described, two hunks down.
653+
assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
654+
claimable_amount_satoshis: 1_000_000 - 10_000 - 20_000 - chan_feerate *
655+
(channel::commitment_tx_base_weight(opt_anchors) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
656+
confirmation_height: node_a_commitment_claimable,
657+
}, Balance::ClaimableAwaitingConfirmations {
658+
claimable_amount_satoshis: 10_000,
659+
confirmation_height: node_a_htlc_claimable,
660+
}, Balance::MaybeClaimableHTLCAwaitingTimeout {
661+
claimable_amount_satoshis: 20_000,
662+
claimable_height: htlc_cltv_timeout,
663+
}]),
664+
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
665+
666+
// Now confirm nodes[1]'s HTLC claim, giving nodes[0] the preimage. Note that the "maybe
667+
// claimable" balance remains until we see ANTI_REORG_DELAY blocks.
668+
mine_transaction(&nodes[0], &bs_htlc_claim_txn[0]);
669+
expect_payment_sent!(nodes[0], payment_preimage_2);
670+
assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
671+
claimable_amount_satoshis: 1_000_000 - 10_000 - 20_000 - chan_feerate *
672+
(channel::commitment_tx_base_weight(opt_anchors) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
673+
confirmation_height: node_a_commitment_claimable,
674+
}, Balance::ClaimableAwaitingConfirmations {
675+
claimable_amount_satoshis: 10_000,
676+
confirmation_height: node_a_htlc_claimable,
677+
}, Balance::MaybeClaimableHTLCAwaitingTimeout {
678+
claimable_amount_satoshis: 20_000,
679+
claimable_height: htlc_cltv_timeout,
680+
}]),
681+
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
682+
683+
// Finally make the HTLC transactions have ANTI_REORG_DELAY blocks. This call previously
684+
// panicked as described in the test introduction. This will remove the "maybe claimable"
685+
// spendable output as nodes[1] has fully claimed the second HTLC.
686+
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
687+
expect_payment_failed!(nodes[0], payment_hash, true);
688+
689+
assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
690+
claimable_amount_satoshis: 1_000_000 - 10_000 - 20_000 - chan_feerate *
691+
(channel::commitment_tx_base_weight(opt_anchors) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
692+
confirmation_height: node_a_commitment_claimable,
693+
}, Balance::ClaimableAwaitingConfirmations {
694+
claimable_amount_satoshis: 10_000,
695+
confirmation_height: node_a_htlc_claimable,
696+
}]),
697+
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
698+
699+
// Connect blocks until the commitment transaction's CSV expires, providing us the relevant
700+
// `SpendableOutputs` event and removing the claimable balance entry.
701+
connect_blocks(&nodes[0], node_a_commitment_claimable - nodes[0].best_block_info().1);
702+
assert_eq!(vec![Balance::ClaimableAwaitingConfirmations {
703+
claimable_amount_satoshis: 10_000,
704+
confirmation_height: node_a_htlc_claimable,
705+
}],
706+
nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
707+
let mut node_a_spendable = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events();
708+
assert_eq!(node_a_spendable.len(), 1);
709+
if let Event::SpendableOutputs { outputs } = node_a_spendable.pop().unwrap() {
710+
assert_eq!(outputs.len(), 1);
711+
let spend_tx = nodes[0].keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(),
712+
Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &Secp256k1::new()).unwrap();
713+
check_spends!(spend_tx, as_txn[0]);
714+
}
715+
716+
// Connect blocks until the HTLC-Timeout's CSV expires, providing us the relevant
717+
// `SpendableOutputs` event and removing the claimable balance entry.
718+
connect_blocks(&nodes[0], node_a_htlc_claimable - nodes[0].best_block_info().1);
719+
assert!(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances().is_empty());
720+
let mut node_a_spendable = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events();
721+
assert_eq!(node_a_spendable.len(), 1);
722+
if let Event::SpendableOutputs { outputs } = node_a_spendable.pop().unwrap() {
723+
assert_eq!(outputs.len(), 1);
724+
let spend_tx = nodes[0].keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(),
725+
Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &Secp256k1::new()).unwrap();
726+
check_spends!(spend_tx, as_txn[1]);
727+
}
728+
}

lightning/src/ln/payment_tests.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,9 @@ fn do_retry_with_no_persist(confirm_before_reload: bool) {
423423
check_spends!(bs_htlc_claim_txn[0], as_commitment_tx);
424424
expect_payment_forwarded!(nodes[1], None, false);
425425

426-
mine_transaction(&nodes[0], &as_commitment_tx);
426+
if !confirm_before_reload {
427+
mine_transaction(&nodes[0], &as_commitment_tx);
428+
}
427429
mine_transaction(&nodes[0], &bs_htlc_claim_txn[0]);
428430
expect_payment_sent!(nodes[0], payment_preimage_1);
429431
connect_blocks(&nodes[0], TEST_FINAL_CLTV*4 + 20);
@@ -515,6 +517,10 @@ fn do_test_dup_htlc_onchain_fails_on_reload(persist_manager_post_event: bool, co
515517
check_added_monitors!(nodes[1], 1);
516518
check_closed_event!(nodes[1], 1, ClosureReason::CommitmentTxConfirmed);
517519
let claim_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
520+
assert_eq!(claim_txn.len(), 3);
521+
check_spends!(claim_txn[0], node_txn[1]);
522+
check_spends!(claim_txn[1], funding_tx);
523+
check_spends!(claim_txn[2], claim_txn[1]);
518524

519525
header.prev_blockhash = nodes[0].best_block_hash();
520526
connect_block(&nodes[0], &Block { header, txdata: vec![node_txn[1].clone()]});
@@ -524,7 +530,7 @@ fn do_test_dup_htlc_onchain_fails_on_reload(persist_manager_post_event: bool, co
524530
}
525531

526532
header.prev_blockhash = nodes[0].best_block_hash();
527-
let claim_block = Block { header, txdata: if payment_timeout { timeout_txn } else { claim_txn } };
533+
let claim_block = Block { header, txdata: if payment_timeout { timeout_txn } else { vec![claim_txn[0].clone()] } };
528534

529535
if payment_timeout {
530536
assert!(confirm_commitment_tx); // Otherwise we're spending below our CSV!

lightning/src/ln/reorg_tests.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ fn do_test_onchain_htlc_reorg(local_commitment: bool, claim: bool) {
9898
vec![node_1_commitment_txn[0].clone(), node_2_commitment_txn[0].clone()]
9999
} else {
100100
// Broadcast node 2 commitment txn
101-
let node_2_commitment_txn = get_local_commitment_txn!(nodes[2], chan_2.2);
101+
let mut node_2_commitment_txn = get_local_commitment_txn!(nodes[2], chan_2.2);
102102
assert_eq!(node_2_commitment_txn.len(), 2); // 1 local commitment tx, 1 Received HTLC-Claim
103103
assert_eq!(node_2_commitment_txn[0].output.len(), 2); // to-remote and Received HTLC (to-self is dust)
104104
check_spends!(node_2_commitment_txn[0], chan_2.3);
@@ -113,12 +113,10 @@ fn do_test_onchain_htlc_reorg(local_commitment: bool, claim: bool) {
113113
check_spends!(node_1_commitment_txn[0], chan_2.3);
114114
check_spends!(node_1_commitment_txn[1], node_2_commitment_txn[0]);
115115

116-
// Confirm node 2's commitment txn (and node 1's HTLC-Timeout) on node 1
117-
header.prev_blockhash = nodes[1].best_block_hash();
118-
let block = Block { header, txdata: vec![node_2_commitment_txn[0].clone(), node_1_commitment_txn[1].clone()] };
119-
connect_block(&nodes[1], &block);
116+
// Confirm node 1's HTLC-Timeout on node 1
117+
mine_transaction(&nodes[1], &node_1_commitment_txn[1]);
120118
// ...but return node 2's commitment tx (and claim) in case claim is set and we're preparing to reorg
121-
node_2_commitment_txn
119+
vec![node_2_commitment_txn.pop().unwrap()]
122120
};
123121
check_added_monitors!(nodes[1], 1);
124122
check_closed_broadcast!(nodes[1], true); // We should get a BroadcastChannelUpdate (and *only* a BroadcstChannelUpdate)

0 commit comments

Comments
 (0)