Skip to content

Commit 292ab5e

Browse files
authored
Merge pull request #282 from TheBlueMatt/2019-01-multi-htlc-claims
Fix multi-remote-HTLC-claim preimage learning
2 parents a5bcd56 + c9df4bd commit 292ab5e

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)