Skip to content

Commit c9df4bd

Browse files
committed
Fix multi-remote-HTLC-claim preimage learning
When our counterparty claims multiple HTLCs from offered outputs in one transaction we should still be able to learn the preimages. Sadly, due to two bugs we were not previously doing so.
1 parent a5bcd56 commit c9df4bd

File tree

2 files changed

+83
-53
lines changed

2 files changed

+83
-53
lines changed

src/ln/channelmonitor.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1747,10 +1747,13 @@ impl ChannelMonitor {
17471747
for tx in txn.iter() {
17481748
broadcaster.broadcast_transaction(tx);
17491749
}
1750-
let mut updated = self.is_resolving_htlc_output(tx);
1751-
if updated.len() > 0 {
1752-
htlc_updated.append(&mut updated);
1753-
}
1750+
}
1751+
// While all commitment/HTLC-Success/HTLC-Timeout transactions have one input, HTLCs
1752+
// can also be resolved in a few other ways which can have more than one output. Thus,
1753+
// we call is_resolving_htlc_output here outside of the tx.input.len() == 1 check.
1754+
let mut updated = self.is_resolving_htlc_output(tx);
1755+
if updated.len() > 0 {
1756+
htlc_updated.append(&mut updated);
17541757
}
17551758
}
17561759
if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx {
@@ -1882,10 +1885,10 @@ impl ChannelMonitor {
18821885
|| (input.witness.len() == 3 && input.witness[2].len() == ACCEPTED_HTLC_SCRIPT_WEIGHT && input.witness[1].len() == 33) {
18831886
log_error!(self, "Remote used revocation sig to take a {} HTLC output at index {} from commitment_tx {}", if input.witness[2].len() == OFFERED_HTLC_SCRIPT_WEIGHT { "offered" } else { "accepted" }, input.previous_output.vout, input.previous_output.txid);
18841887
} else if input.witness.len() == 5 && input.witness[4].len() == ACCEPTED_HTLC_SCRIPT_WEIGHT {
1885-
payment_preimage.0.copy_from_slice(&tx.input[0].witness[3]);
1888+
payment_preimage.0.copy_from_slice(&input.witness[3]);
18861889
htlc_updated.push((source, Some(payment_preimage), payment_hash));
18871890
} else if input.witness.len() == 3 && input.witness[2].len() == OFFERED_HTLC_SCRIPT_WEIGHT {
1888-
payment_preimage.0.copy_from_slice(&tx.input[0].witness[1]);
1891+
payment_preimage.0.copy_from_slice(&input.witness[1]);
18891892
htlc_updated.push((source, Some(payment_preimage), payment_hash));
18901893
} else {
18911894
htlc_updated.push((source, None, payment_hash));

src/ln/functional_tests.rs

Lines changed: 74 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2648,14 +2648,15 @@ fn test_htlc_on_chain_success() {
26482648
// Test that in case of an unilateral close onchain, we detect the state of output thanks to
26492649
// ChainWatchInterface and pass the preimage backward accordingly. So here we test that ChannelManager is
26502650
// broadcasting the right event to other nodes in payment path.
2651+
// We test with two HTLCs simultaneously as that was not handled correctly in the past.
26512652
// A --------------------> B ----------------------> C (preimage)
2652-
// First, C should claim the HTLC output via HTLC-Success when its own latest local
2653+
// First, C should claim the HTLC outputs via HTLC-Success when its own latest local
26532654
// commitment transaction was broadcast.
26542655
// Then, B should learn the preimage from said transactions, attempting to claim backwards
26552656
// towards B.
26562657
// B should be able to claim via preimage if A then broadcasts its local tx.
26572658
// Finally, when A sees B's latest local commitment transaction it should be able to claim
2658-
// the HTLC output via the preimage it learned (which, once confirmed should generate a
2659+
// the HTLC outputs via the preimage it learned (which, once confirmed should generate a
26592660
// PaymentSent event).
26602661

26612662
let nodes = create_network(3);
@@ -2669,6 +2670,7 @@ fn test_htlc_on_chain_success() {
26692670
send_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 8000000);
26702671

26712672
let (our_payment_preimage, _payment_hash) = route_payment(&nodes[0], &vec!(&nodes[1], &nodes[2]), 3000000);
2673+
let (our_payment_preimage_2, _payment_hash_2) = route_payment(&nodes[0], &vec!(&nodes[1], &nodes[2]), 3000000);
26722674
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42};
26732675

26742676
// Broadcast legit commitment tx from C on B's chain
@@ -2677,7 +2679,8 @@ fn test_htlc_on_chain_success() {
26772679
assert_eq!(commitment_tx.len(), 1);
26782680
check_spends!(commitment_tx[0], chan_2.3.clone());
26792681
nodes[2].node.claim_funds(our_payment_preimage);
2680-
check_added_monitors!(nodes[2], 1);
2682+
nodes[2].node.claim_funds(our_payment_preimage_2);
2683+
check_added_monitors!(nodes[2], 2);
26812684
let updates = get_htlc_update_msgs!(nodes[2], nodes[1].node.get_our_node_id());
26822685
assert!(updates.update_add_htlcs.is_empty());
26832686
assert!(updates.update_fail_htlcs.is_empty());
@@ -2686,22 +2689,28 @@ fn test_htlc_on_chain_success() {
26862689

26872690
nodes[2].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![commitment_tx[0].clone()]}, 1);
26882691
check_closed_broadcast!(nodes[2]);
2689-
let node_txn = nodes[2].tx_broadcaster.txn_broadcasted.lock().unwrap().clone(); // ChannelManager : 1 (commitment tx), ChannelMonitor : 2 (2 * HTLC-Success tx)
2690-
assert_eq!(node_txn.len(), 3);
2691-
assert_eq!(node_txn[1], commitment_tx[0]);
2692-
assert_eq!(node_txn[0], node_txn[2]);
2692+
let node_txn = nodes[2].tx_broadcaster.txn_broadcasted.lock().unwrap().clone(); // ChannelManager : 1 (commitment tx), ChannelMonitor : 4 (2*2 * HTLC-Success tx)
2693+
assert_eq!(node_txn.len(), 5);
2694+
assert_eq!(node_txn[0], node_txn[3]);
2695+
assert_eq!(node_txn[1], node_txn[4]);
2696+
assert_eq!(node_txn[2], commitment_tx[0]);
26932697
check_spends!(node_txn[0], commitment_tx[0].clone());
2698+
check_spends!(node_txn[1], commitment_tx[0].clone());
26942699
assert_eq!(node_txn[0].input[0].witness.clone().last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
2700+
assert_eq!(node_txn[1].input[0].witness.clone().last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
26952701
assert!(node_txn[0].output[0].script_pubkey.is_v0_p2wsh()); // revokeable output
2702+
assert!(node_txn[1].output[0].script_pubkey.is_v0_p2wsh()); // revokeable output
26962703
assert_eq!(node_txn[0].lock_time, 0);
2704+
assert_eq!(node_txn[1].lock_time, 0);
26972705

26982706
// Verify that B's ChannelManager is able to extract preimage from HTLC Success tx and pass it backward
26992707
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: node_txn}, 1);
27002708
let events = nodes[1].node.get_and_clear_pending_msg_events();
27012709
{
27022710
let mut added_monitors = nodes[1].chan_monitor.added_monitors.lock().unwrap();
2703-
assert_eq!(added_monitors.len(), 1);
2711+
assert_eq!(added_monitors.len(), 2);
27042712
assert_eq!(added_monitors[0].0.txid, chan_1.3.txid());
2713+
assert_eq!(added_monitors[1].0.txid, chan_1.3.txid());
27052714
added_monitors.clear();
27062715
}
27072716
assert_eq!(events.len(), 2);
@@ -2719,25 +2728,45 @@ fn test_htlc_on_chain_success() {
27192728
},
27202729
_ => panic!("Unexpected event"),
27212730
};
2722-
{
2723-
// nodes[1] now broadcasts its own local state as a fallback, suggesting an alternate
2724-
// commitment transaction with a corresponding HTLC-Timeout transaction, as well as a
2725-
// timeout-claim of the output that nodes[2] just claimed via success.
2726-
let mut node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap(); // ChannelManager : 2 (commitment tx, HTLC-Timeout tx), ChannelMonitor : 1 (timeout tx) * 2 (block-rescan)
2727-
assert_eq!(node_txn.len(), 4);
2728-
assert_eq!(node_txn[0], node_txn[3]);
2729-
check_spends!(node_txn[0], commitment_tx[0].clone());
2730-
assert_eq!(node_txn[0].input[0].witness.clone().last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
2731-
assert_ne!(node_txn[0].lock_time, 0);
2732-
assert!(node_txn[0].output[0].script_pubkey.is_v0_p2wpkh()); // direct payment
2733-
check_spends!(node_txn[1], chan_2.3.clone());
2734-
check_spends!(node_txn[2], node_txn[1].clone());
2735-
assert_eq!(node_txn[1].input[0].witness.clone().last().unwrap().len(), 71);
2736-
assert_eq!(node_txn[2].input[0].witness.clone().last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
2737-
assert!(node_txn[2].output[0].script_pubkey.is_v0_p2wsh()); // revokeable output
2738-
assert_ne!(node_txn[2].lock_time, 0);
2739-
node_txn.clear();
2740-
}
2731+
macro_rules! check_tx_local_broadcast {
2732+
($node: expr, $htlc_offered: expr, $commitment_tx: expr, $chan_tx: expr) => { {
2733+
// ChannelManager : 3 (commitment tx, 2*HTLC-Timeout tx), ChannelMonitor : 2 (timeout tx) * 2 (block-rescan)
2734+
let mut node_txn = $node.tx_broadcaster.txn_broadcasted.lock().unwrap();
2735+
assert_eq!(node_txn.len(), 7);
2736+
assert_eq!(node_txn[0], node_txn[5]);
2737+
assert_eq!(node_txn[1], node_txn[6]);
2738+
check_spends!(node_txn[0], $commitment_tx.clone());
2739+
check_spends!(node_txn[1], $commitment_tx.clone());
2740+
assert_ne!(node_txn[0].lock_time, 0);
2741+
assert_ne!(node_txn[1].lock_time, 0);
2742+
if $htlc_offered {
2743+
assert_eq!(node_txn[0].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
2744+
assert_eq!(node_txn[1].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
2745+
assert!(node_txn[0].output[0].script_pubkey.is_v0_p2wsh()); // revokeable output
2746+
assert!(node_txn[1].output[0].script_pubkey.is_v0_p2wsh()); // revokeable output
2747+
} else {
2748+
assert_eq!(node_txn[0].input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
2749+
assert_eq!(node_txn[1].input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
2750+
assert!(node_txn[0].output[0].script_pubkey.is_v0_p2wpkh()); // direct payment
2751+
assert!(node_txn[1].output[0].script_pubkey.is_v0_p2wpkh()); // direct payment
2752+
}
2753+
check_spends!(node_txn[2], $chan_tx.clone());
2754+
check_spends!(node_txn[3], node_txn[2].clone());
2755+
check_spends!(node_txn[4], node_txn[2].clone());
2756+
assert_eq!(node_txn[2].input[0].witness.last().unwrap().len(), 71);
2757+
assert_eq!(node_txn[3].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
2758+
assert_eq!(node_txn[4].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
2759+
assert!(node_txn[3].output[0].script_pubkey.is_v0_p2wsh()); // revokeable output
2760+
assert!(node_txn[4].output[0].script_pubkey.is_v0_p2wsh()); // revokeable output
2761+
assert_ne!(node_txn[3].lock_time, 0);
2762+
assert_ne!(node_txn[4].lock_time, 0);
2763+
node_txn.clear();
2764+
} }
2765+
}
2766+
// nodes[1] now broadcasts its own local state as a fallback, suggesting an alternate
2767+
// commitment transaction with a corresponding HTLC-Timeout transactions, as well as a
2768+
// timeout-claim of the output that nodes[2] just claimed via success.
2769+
check_tx_local_broadcast!(nodes[1], false, commitment_tx[0], chan_2.3);
27412770

27422771
// Broadcast legit commitment tx from A on B's chain
27432772
// Broadcast preimage tx by B on offered output from A commitment tx on A's chain
@@ -2749,7 +2778,9 @@ fn test_htlc_on_chain_success() {
27492778
assert_eq!(node_txn.len(), 3);
27502779
assert_eq!(node_txn[0], node_txn[2]);
27512780
check_spends!(node_txn[0], commitment_tx[0].clone());
2752-
assert_eq!(node_txn[0].input[0].witness.clone().last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
2781+
assert_eq!(node_txn[0].input.len(), 2);
2782+
assert_eq!(node_txn[0].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
2783+
assert_eq!(node_txn[0].input[1].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
27532784
assert_eq!(node_txn[0].lock_time, 0);
27542785
assert!(node_txn[0].output[0].script_pubkey.is_v0_p2wpkh()); // direct payment
27552786
check_spends!(node_txn[1], chan_1.3.clone());
@@ -2761,26 +2792,22 @@ fn test_htlc_on_chain_success() {
27612792
nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![commitment_tx[0].clone(), node_txn[0].clone()] }, 1);
27622793
check_closed_broadcast!(nodes[0]);
27632794
let events = nodes[0].node.get_and_clear_pending_events();
2764-
assert_eq!(events.len(), 1);
2765-
match events[0] {
2766-
Event::PaymentSent { payment_preimage } => {
2767-
assert_eq!(payment_preimage, our_payment_preimage);
2768-
},
2769-
_ => panic!("Unexpected event"),
2795+
assert_eq!(events.len(), 2);
2796+
let mut first_claimed = false;
2797+
for event in events {
2798+
match event {
2799+
Event::PaymentSent { payment_preimage } => {
2800+
if payment_preimage == our_payment_preimage {
2801+
assert!(!first_claimed);
2802+
first_claimed = true;
2803+
} else {
2804+
assert_eq!(payment_preimage, our_payment_preimage_2);
2805+
}
2806+
},
2807+
_ => panic!("Unexpected event"),
2808+
}
27702809
}
2771-
let node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().clone(); // ChannelManager : 2 (commitment tx, HTLC-Timeout tx), ChannelMonitor : 1 (HTLC-Timeout tx) * 2 (block-rescan)
2772-
assert_eq!(node_txn.len(), 4);
2773-
assert_eq!(node_txn[0], node_txn[3]);
2774-
check_spends!(node_txn[0], commitment_tx[0].clone());
2775-
assert_eq!(node_txn[0].input[0].witness.clone().last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
2776-
assert_ne!(node_txn[0].lock_time, 0);
2777-
assert!(node_txn[0].output[0].script_pubkey.is_v0_p2wsh()); // revokeable output
2778-
check_spends!(node_txn[1], chan_1.3.clone());
2779-
check_spends!(node_txn[2], node_txn[1].clone());
2780-
assert_eq!(node_txn[1].input[0].witness.clone().last().unwrap().len(), 71);
2781-
assert_eq!(node_txn[2].input[0].witness.clone().last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
2782-
assert!(node_txn[2].output[0].script_pubkey.is_v0_p2wsh()); // revokeable output
2783-
assert_ne!(node_txn[2].lock_time, 0);
2810+
check_tx_local_broadcast!(nodes[0], true, commitment_tx[0], chan_1.3);
27842811
}
27852812

27862813
#[test]

0 commit comments

Comments
 (0)