Skip to content

Commit 527c78a

Browse files
committed
Test commitment broadcast during different stages of a splice
This ensures a valid commitment transaction is broadcast according to the different stages of a splice: 1. Negotiated but unconfirmed 2. Confirmed but not locked 3. Locked
1 parent a16db51 commit 527c78a

File tree

2 files changed

+174
-2
lines changed

2 files changed

+174
-2
lines changed

lightning/src/ln/functional_test_utils.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4279,6 +4279,13 @@ pub fn test_default_channel_config() -> UserConfig {
42794279
default_config
42804280
}
42814281

4282+
pub fn test_default_anchors_channel_config() -> UserConfig {
4283+
let mut config = test_default_channel_config();
4284+
config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
4285+
config.manually_accept_inbound_channels = true;
4286+
config
4287+
}
4288+
42824289
pub fn create_node_chanmgrs<'a, 'b>(
42834290
node_count: usize, cfgs: &'a Vec<NodeCfg<'b>>, node_config: &[Option<UserConfig>],
42844291
) -> Vec<

lightning/src/ln/splicing_tests.rs

Lines changed: 167 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
// licenses.
99

1010
use crate::chain::chaininterface::FEERATE_FLOOR_SATS_PER_KW;
11-
use crate::chain::channelmonitor::ANTI_REORG_DELAY;
11+
use crate::chain::channelmonitor::{ANTI_REORG_DELAY, LATENCY_GRACE_PERIOD_BLOCKS};
1212
use crate::events::bump_transaction::sync::WalletSourceSync;
13-
use crate::events::Event;
13+
use crate::events::{ClosureReason, Event, HTLCHandlingFailureType};
1414
use crate::ln::funding::{FundingTxInput, SpliceContribution};
1515
use crate::ln::msgs::{self, BaseMessageHandler, ChannelMessageHandler, MessageSendEvent};
1616
use crate::ln::types::ChannelId;
@@ -444,3 +444,168 @@ fn test_splice_out() {
444444
assert!(htlc_limit_msat < initial_channel_value_sat / 2 * 1000);
445445
let _ = send_payment(&nodes[0], &[&nodes[1]], htlc_limit_msat);
446446
}
447+
448+
#[derive(PartialEq)]
449+
enum SpliceStatus {
450+
Unconfirmed,
451+
Confirmed,
452+
Locked,
453+
}
454+
455+
#[test]
456+
fn test_splice_commitment_broadcast() {
457+
do_test_splice_commitment_broadcast(SpliceStatus::Unconfirmed, false);
458+
do_test_splice_commitment_broadcast(SpliceStatus::Unconfirmed, true);
459+
do_test_splice_commitment_broadcast(SpliceStatus::Confirmed, false);
460+
do_test_splice_commitment_broadcast(SpliceStatus::Confirmed, true);
461+
do_test_splice_commitment_broadcast(SpliceStatus::Locked, false);
462+
do_test_splice_commitment_broadcast(SpliceStatus::Locked, true);
463+
}
464+
465+
fn do_test_splice_commitment_broadcast(splice_status: SpliceStatus, claim_htlcs: bool) {
466+
// Tests that we're able to enforce HTLCs onchain during the different stages of a splice.
467+
let chanmon_cfgs = create_chanmon_cfgs(2);
468+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
469+
let config = test_default_anchors_channel_config();
470+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config.clone()), Some(config)]);
471+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
472+
473+
let node_id_0 = nodes[0].node.get_our_node_id();
474+
let node_id_1 = nodes[1].node.get_our_node_id();
475+
476+
let initial_channel_capacity = 100_000;
477+
let (_, _, channel_id, initial_funding_tx) =
478+
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, initial_channel_capacity, 0);
479+
480+
let coinbase_tx = provide_anchor_reserves(&nodes);
481+
482+
// We want to have two HTLCs pending to make sure we can claim those sent before and after a
483+
// splice negotiation.
484+
let payment_amount = 1_000_000;
485+
let (preimage1, payment_hash1, ..) = route_payment(&nodes[0], &[&nodes[1]], payment_amount);
486+
let splice_in_amount = initial_channel_capacity / 2;
487+
let initiator_contribution = SpliceContribution::SpliceIn {
488+
value: Amount::from_sat(splice_in_amount),
489+
inputs: vec![FundingTxInput::new_p2wpkh(coinbase_tx.clone(), 0).unwrap()],
490+
change_script: Some(nodes[0].wallet_source.get_change_script().unwrap()),
491+
};
492+
let splice_tx = splice_channel(&nodes[0], &nodes[1], channel_id, initiator_contribution);
493+
let (preimage2, payment_hash2, ..) = route_payment(&nodes[0], &[&nodes[1]], payment_amount);
494+
let htlc_expiry = nodes[0].best_block_info().1 + TEST_FINAL_CLTV + LATENCY_GRACE_PERIOD_BLOCKS;
495+
496+
if splice_status == SpliceStatus::Confirmed || splice_status == SpliceStatus::Locked {
497+
mine_transaction(&nodes[0], &splice_tx);
498+
mine_transaction(&nodes[1], &splice_tx);
499+
}
500+
if splice_status == SpliceStatus::Locked {
501+
lock_splice_after_blocks(&nodes[0], &nodes[1], channel_id, ANTI_REORG_DELAY - 1);
502+
}
503+
504+
if claim_htlcs {
505+
// Claim both HTLCs, but don't do anything with the update message sent since we want to
506+
// resolve the HTLCs onchain instead with a single transaction (thanks to anchors).
507+
nodes[1].node.claim_funds(preimage1);
508+
expect_payment_claimed!(&nodes[1], payment_hash1, payment_amount);
509+
nodes[1].node.claim_funds(preimage2);
510+
expect_payment_claimed!(&nodes[1], payment_hash2, payment_amount);
511+
check_added_monitors(&nodes[1], 2);
512+
let _ = get_htlc_update_msgs(&nodes[1], &node_id_0);
513+
}
514+
515+
// Force close the channel. This should broadcast the appropriate commitment transaction based
516+
// on the currently confirmed funding.
517+
nodes[0]
518+
.node
519+
.force_close_broadcasting_latest_txn(&channel_id, &node_id_1, "test".to_owned())
520+
.unwrap();
521+
handle_bump_events(&nodes[0], true, 0);
522+
let commitment_tx = {
523+
let mut txn = nodes[0].tx_broadcaster.txn_broadcast();
524+
assert_eq!(txn.len(), 1);
525+
let commitment_tx = txn.remove(0);
526+
match splice_status {
527+
SpliceStatus::Unconfirmed => check_spends!(&commitment_tx, &initial_funding_tx),
528+
SpliceStatus::Confirmed | SpliceStatus::Locked => {
529+
check_spends!(&commitment_tx, &splice_tx)
530+
},
531+
}
532+
commitment_tx
533+
};
534+
535+
mine_transaction(&nodes[0], &commitment_tx);
536+
mine_transaction(&nodes[1], &commitment_tx);
537+
538+
let closure_reason = ClosureReason::HolderForceClosed {
539+
broadcasted_latest_txn: Some(true),
540+
message: "test".to_owned(),
541+
};
542+
let closed_channel_capacity = if splice_status == SpliceStatus::Locked {
543+
initial_channel_capacity + splice_in_amount
544+
} else {
545+
initial_channel_capacity
546+
};
547+
check_closed_event(&nodes[0], 1, closure_reason, false, &[node_id_1], closed_channel_capacity);
548+
check_closed_broadcast(&nodes[0], 1, true);
549+
check_added_monitors(&nodes[0], 1);
550+
551+
let closure_reason = ClosureReason::CommitmentTxConfirmed;
552+
check_closed_event(&nodes[1], 1, closure_reason, false, &[node_id_0], closed_channel_capacity);
553+
check_closed_broadcast(&nodes[1], 1, true);
554+
check_added_monitors(&nodes[1], 1);
555+
556+
if !claim_htlcs {
557+
// If we're supposed to time out the HTLCs, mine enough blocks until the expiration.
558+
connect_blocks(&nodes[0], htlc_expiry - nodes[0].best_block_info().1);
559+
connect_blocks(&nodes[1], htlc_expiry - nodes[1].best_block_info().1);
560+
expect_htlc_handling_failed_destinations!(
561+
nodes[1].node.get_and_clear_pending_events(),
562+
&[
563+
HTLCHandlingFailureType::Receive { payment_hash: payment_hash1 },
564+
HTLCHandlingFailureType::Receive { payment_hash: payment_hash2 }
565+
]
566+
);
567+
}
568+
569+
// We should see either an aggregated HTLC timeout or success transaction spending the valid
570+
// commitment transaction we mined earlier.
571+
let htlc_claim_tx = if claim_htlcs {
572+
let mut txn = nodes[1].tx_broadcaster.txn_broadcast();
573+
assert_eq!(txn.len(), 1);
574+
let htlc_success_tx = txn.remove(0);
575+
assert_eq!(htlc_success_tx.input.len(), 2);
576+
check_spends!(&htlc_success_tx, &commitment_tx);
577+
htlc_success_tx
578+
} else {
579+
handle_bump_htlc_event(&nodes[0], 1);
580+
let mut txn = nodes[0].tx_broadcaster.txn_broadcast();
581+
assert_eq!(txn.len(), 1);
582+
let htlc_timeout_tx = txn.remove(0);
583+
// The inputs spent correspond to the fee bump input and the two HTLCs from the commitment
584+
// transaction.
585+
assert_eq!(htlc_timeout_tx.input.len(), 3);
586+
let tx_with_fee_bump_utxo =
587+
if splice_status == SpliceStatus::Unconfirmed { &coinbase_tx } else { &splice_tx };
588+
check_spends!(&htlc_timeout_tx, &commitment_tx, tx_with_fee_bump_utxo);
589+
htlc_timeout_tx
590+
};
591+
592+
mine_transaction(&nodes[0], &htlc_claim_tx);
593+
mine_transaction(&nodes[1], &htlc_claim_tx);
594+
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
595+
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
596+
597+
let events = nodes[0].node.get_and_clear_pending_events();
598+
if claim_htlcs {
599+
assert_eq!(events.iter().filter(|e| matches!(e, Event::PaymentSent { .. })).count(), 2);
600+
assert_eq!(
601+
events.iter().filter(|e| matches!(e, Event::PaymentPathSuccessful { .. })).count(),
602+
2
603+
);
604+
} else {
605+
assert_eq!(events.iter().filter(|e| matches!(e, Event::PaymentFailed { .. })).count(), 2,);
606+
assert_eq!(
607+
events.iter().filter(|e| matches!(e, Event::PaymentPathFailed { .. })).count(),
608+
2
609+
);
610+
}
611+
}

0 commit comments

Comments
 (0)