Skip to content

Commit 60bb39a

Browse files
committed
Add test coverage for holder commitment rebroadcast after reorg
1 parent 90f24a6 commit 60bb39a

File tree

1 file changed

+119
-0
lines changed

1 file changed

+119
-0
lines changed

lightning/src/ln/reorg_tests.rs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,3 +759,122 @@ fn test_htlc_preimage_claim_prev_counterparty_commitment_after_current_counterpa
759759
// commitment (still unrevoked) is the currently confirmed closing transaction.
760760
assert_eq!(htlc_preimage_tx.input[0].witness.second_to_last().unwrap(), &payment_preimage.0[..]);
761761
}
762+
763+
fn do_test_retries_own_commitment_broadcast_after_reorg(anchors: bool, revoked_counterparty_commitment: bool) {
764+
// Tests that a node will retry broadcasting its own commitment after seeing a confirmed
765+
// counterparty commitment be reorged out.
766+
let mut chanmon_cfgs = create_chanmon_cfgs(2);
767+
if revoked_counterparty_commitment {
768+
chanmon_cfgs[1].keys_manager.disable_revocation_policy_check = true;
769+
}
770+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
771+
let mut config = test_default_channel_config();
772+
if anchors {
773+
config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
774+
config.manually_accept_inbound_channels = true;
775+
}
776+
let persister;
777+
let new_chain_monitor;
778+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config), Some(config)]);
779+
let nodes_1_deserialized;
780+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
781+
782+
let (_, _, chan_id, funding_tx) = create_announced_chan_between_nodes(&nodes, 0, 1);
783+
784+
// Route a payment so we have an HTLC to claim as well.
785+
let _ = route_payment(&nodes[0], &[&nodes[1]], 1_000_000);
786+
787+
if revoked_counterparty_commitment {
788+
// Trigger a fee update such that we advance the state. We will have B broadcast its state
789+
// without the fee update.
790+
let serialized_node = nodes[1].node.encode();
791+
let serialized_monitor = get_monitor!(nodes[1], chan_id).encode();
792+
793+
*chanmon_cfgs[0].fee_estimator.sat_per_kw.lock().unwrap() += 1;
794+
nodes[0].node.timer_tick_occurred();
795+
check_added_monitors!(nodes[0], 1);
796+
797+
let fee_update = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
798+
nodes[1].node.handle_update_fee(&nodes[0].node.get_our_node_id(), &fee_update.update_fee.unwrap());
799+
commitment_signed_dance!(nodes[1], nodes[0], fee_update.commitment_signed, false);
800+
801+
reload_node!(
802+
nodes[1], config, &serialized_node, &[&serialized_monitor], persister, new_chain_monitor, nodes_1_deserialized
803+
);
804+
}
805+
806+
// Connect blocks until the HTLC expiry is met, prompting a commitment broadcast by A.
807+
connect_blocks(&nodes[0], TEST_FINAL_CLTV + LATENCY_GRACE_PERIOD_BLOCKS + 1);
808+
check_closed_broadcast(&nodes[0], 1, true);
809+
check_added_monitors(&nodes[0], 1);
810+
check_closed_event(&nodes[0], 1, ClosureReason::HolderForceClosed, false, &[nodes[1].node.get_our_node_id()], 100_000);
811+
812+
{
813+
let mut txn = nodes[0].tx_broadcaster.txn_broadcast();
814+
if anchors {
815+
assert_eq!(txn.len(), 1);
816+
let commitment_tx_a = txn.pop().unwrap();
817+
check_spends!(commitment_tx_a, funding_tx);
818+
} else {
819+
assert_eq!(txn.len(), 2);
820+
let htlc_tx_a = txn.pop().unwrap();
821+
let commitment_tx_a = txn.pop().unwrap();
822+
check_spends!(commitment_tx_a, funding_tx);
823+
check_spends!(htlc_tx_a, commitment_tx_a);
824+
}
825+
};
826+
827+
// B will also broadcast its own commitment.
828+
nodes[1].node.force_close_broadcasting_latest_txn(&chan_id, &nodes[0].node.get_our_node_id()).unwrap();
829+
check_closed_broadcast(&nodes[1], 1, true);
830+
check_added_monitors(&nodes[1], 1);
831+
check_closed_event(&nodes[1], 1, ClosureReason::HolderForceClosed, false, &[nodes[0].node.get_our_node_id()], 100_000);
832+
833+
let commitment_b = {
834+
let mut txn = nodes[1].tx_broadcaster.txn_broadcast();
835+
assert_eq!(txn.len(), 1);
836+
let tx = txn.pop().unwrap();
837+
check_spends!(tx, funding_tx);
838+
tx
839+
};
840+
841+
// Confirm B's commitment, A should now broadcast an HTLC timeout for commitment B.
842+
mine_transaction(&nodes[0], &commitment_b);
843+
{
844+
let mut txn = nodes[0].tx_broadcaster.txn_broadcast();
845+
if nodes[0].connect_style.borrow().updates_best_block_first() {
846+
// `commitment_a` and `htlc_timeout_a` are rebroadcast because the best block was
847+
// updated prior to seeing `commitment_b`.
848+
assert_eq!(txn.len(), if anchors { 2 } else { 3 });
849+
check_spends!(txn.last().unwrap(), commitment_b);
850+
} else {
851+
assert_eq!(txn.len(), 1);
852+
check_spends!(txn[0], commitment_b);
853+
}
854+
}
855+
856+
// Disconnect the block, allowing A to retry its own commitment. Note that we connect two
857+
// blocks, one to get us back to the original height, and another to retry our pending claims.
858+
disconnect_blocks(&nodes[0], 1);
859+
connect_blocks(&nodes[0], 2);
860+
{
861+
let mut txn = nodes[0].tx_broadcaster.unique_txn_broadcast();
862+
if anchors {
863+
assert_eq!(txn.len(), 1);
864+
check_spends!(txn[0], funding_tx);
865+
} else {
866+
assert_eq!(txn.len(), 2);
867+
check_spends!(txn[0], txn[1]); // HTLC timeout A
868+
check_spends!(txn[1], funding_tx); // Commitment A
869+
assert_ne!(txn[1].txid(), commitment_b.txid());
870+
}
871+
}
872+
}
873+
874+
#[test]
875+
fn test_retries_own_commitment_broadcast_after_reorg() {
876+
do_test_retries_own_commitment_broadcast_after_reorg(false, false);
877+
do_test_retries_own_commitment_broadcast_after_reorg(false, true);
878+
do_test_retries_own_commitment_broadcast_after_reorg(true, false);
879+
do_test_retries_own_commitment_broadcast_after_reorg(true, true);
880+
}

0 commit comments

Comments
 (0)