Skip to content

Commit 4441827

Browse files
authored
Merge pull request #587 from TheBlueMatt/2020-04-mpp-timeout
Time out HTLCs before they expire
2 parents 86a2607 + d316f30 commit 4441827

8 files changed

+494
-287
lines changed

lightning/src/ln/chanmon_update_fail_tests.rs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//! here. See also the chanmon_fail_consistency fuzz test.
55
66
use chain::transaction::OutPoint;
7-
use ln::channelmanager::{RAACommitmentOrder, PaymentPreimage, PaymentHash, PaymentSendFailure};
7+
use ln::channelmanager::{RAACommitmentOrder, PaymentPreimage, PaymentHash, PaymentSecret, PaymentSendFailure};
88
use ln::channelmonitor::ChannelMonitorUpdateErr;
99
use ln::features::InitFeatures;
1010
use ln::msgs;
@@ -1731,3 +1731,63 @@ fn during_funding_monitor_fail() {
17311731
do_during_funding_monitor_fail(true, false);
17321732
do_during_funding_monitor_fail(false, false);
17331733
}
1734+
1735+
#[test]
1736+
fn test_path_paused_mpp() {
1737+
// Simple test of sending a multi-part payment where one path is currently blocked awaiting
1738+
// monitor update
1739+
let chanmon_cfgs = create_chanmon_cfgs(4);
1740+
let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
1741+
let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]);
1742+
let mut nodes = create_network(4, &node_cfgs, &node_chanmgrs);
1743+
1744+
let chan_1_id = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::supported(), InitFeatures::supported()).0.contents.short_channel_id;
1745+
let (chan_2_ann, _, chan_2_id, _) = create_announced_chan_between_nodes(&nodes, 0, 2, InitFeatures::supported(), InitFeatures::supported());
1746+
let chan_3_id = create_announced_chan_between_nodes(&nodes, 1, 3, InitFeatures::supported(), InitFeatures::supported()).0.contents.short_channel_id;
1747+
let chan_4_id = create_announced_chan_between_nodes(&nodes, 2, 3, InitFeatures::supported(), InitFeatures::supported()).0.contents.short_channel_id;
1748+
1749+
let (payment_preimage, payment_hash) = get_payment_preimage_hash!(&nodes[0]);
1750+
let payment_secret = PaymentSecret([0xdb; 32]);
1751+
let mut route = nodes[0].router.get_route(&nodes[3].node.get_our_node_id(), None, &[], 100000, TEST_FINAL_CLTV).unwrap();
1752+
1753+
// Set us up to take multiple routes, one 0 -> 1 -> 3 and one 0 -> 2 -> 3:
1754+
let path = route.paths[0].clone();
1755+
route.paths.push(path);
1756+
route.paths[0][0].pubkey = nodes[1].node.get_our_node_id();
1757+
route.paths[0][0].short_channel_id = chan_1_id;
1758+
route.paths[0][1].short_channel_id = chan_3_id;
1759+
route.paths[1][0].pubkey = nodes[2].node.get_our_node_id();
1760+
route.paths[1][0].short_channel_id = chan_2_ann.contents.short_channel_id;
1761+
route.paths[1][1].short_channel_id = chan_4_id;
1762+
1763+
// Set it so that the first monitor update (for the path 0 -> 1 -> 3) succeeds, but the second
1764+
// (for the path 0 -> 2 -> 3) fails.
1765+
*nodes[0].chan_monitor.update_ret.lock().unwrap() = Ok(());
1766+
*nodes[0].chan_monitor.next_update_ret.lock().unwrap() = Some(Err(ChannelMonitorUpdateErr::TemporaryFailure));
1767+
1768+
// Now check that we get the right return value, indicating that the first path succeeded but
1769+
// the second got a MonitorUpdateFailed err. This implies PaymentSendFailure::PartialFailure as
1770+
// some paths succeeded, preventing retry.
1771+
if let Err(PaymentSendFailure::PartialFailure(results)) = nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)) {
1772+
assert_eq!(results.len(), 2);
1773+
if let Ok(()) = results[0] {} else { panic!(); }
1774+
if let Err(APIError::MonitorUpdateFailed) = results[1] {} else { panic!(); }
1775+
} else { panic!(); }
1776+
check_added_monitors!(nodes[0], 2);
1777+
*nodes[0].chan_monitor.update_ret.lock().unwrap() = Ok(());
1778+
1779+
// Pass the first HTLC of the payment along to nodes[3].
1780+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
1781+
assert_eq!(events.len(), 1);
1782+
pass_along_path(&nodes[0], &[&nodes[1], &nodes[3]], 0, payment_hash.clone(), Some(payment_secret), events.pop().unwrap(), false);
1783+
1784+
// And check that, after we successfully update the monitor for chan_2 we can pass the second
1785+
// HTLC along to nodes[3] and claim the whole payment back to nodes[0].
1786+
let (outpoint, latest_update) = nodes[0].chan_monitor.latest_monitor_update_id.lock().unwrap().get(&chan_2_id).unwrap().clone();
1787+
nodes[0].node.channel_monitor_updated(&outpoint, latest_update);
1788+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
1789+
assert_eq!(events.len(), 1);
1790+
pass_along_path(&nodes[0], &[&nodes[2], &nodes[3]], 200_000, payment_hash.clone(), Some(payment_secret), events.pop().unwrap(), true);
1791+
1792+
claim_payment_along_route_with_secret(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, payment_preimage, Some(payment_secret), 200_000);
1793+
}

lightning/src/ln/channel.rs

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use secp256k1;
1818
use ln::features::{ChannelFeatures, InitFeatures};
1919
use ln::msgs;
2020
use ln::msgs::{DecodeError, OptionalField, DataLossProtect};
21-
use ln::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep};
21+
use ln::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, HTLC_FAIL_BACK_BUFFER};
2222
use ln::channelmanager::{PendingHTLCStatus, HTLCSource, HTLCFailReason, HTLCFailureMsg, PendingHTLCInfo, RAACommitmentOrder, PaymentPreimage, PaymentHash, BREAKDOWN_TIMEOUT, MAX_LOCAL_BREAKDOWN_TIMEOUT};
2323
use ln::chan_utils::{CounterpartyCommitmentSecrets, LocalCommitmentTransaction, TxCreationKeys, HTLCOutputInCommitment, HTLC_SUCCESS_TX_WEIGHT, HTLC_TIMEOUT_TX_WEIGHT, make_funding_redeemscript, ChannelPublicKeys};
2424
use ln::chan_utils;
@@ -3154,13 +3154,33 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
31543154
self.network_sync == UpdateStatus::DisabledMarked
31553155
}
31563156

3157-
/// Called by channelmanager based on chain blocks being connected.
3158-
/// Note that we only need to use this to detect funding_signed, anything else is handled by
3159-
/// the channel_monitor.
3160-
/// In case of Err, the channel may have been closed, at which point the standard requirements
3161-
/// apply - no calls may be made except those explicitly stated to be allowed post-shutdown.
3157+
/// When we receive a new block, we (a) check whether the block contains the funding
3158+
/// transaction (which would start us counting blocks until we send the funding_signed), and
3159+
/// (b) check the height of the block against outbound holding cell HTLCs in case we need to
3160+
/// give up on them prematurely and time them out. Everything else (e.g. commitment
3161+
/// transaction broadcasts, channel closure detection, HTLC transaction broadcasting, etc) is
3162+
/// handled by the ChannelMonitor.
3163+
///
3164+
/// If we return Err, the channel may have been closed, at which point the standard
3165+
/// requirements apply - no calls may be made except those explicitly stated to be allowed
3166+
/// post-shutdown.
31623167
/// Only returns an ErrorAction of DisconnectPeer, if Err.
3163-
pub fn block_connected(&mut self, header: &BlockHeader, height: u32, txn_matched: &[&Transaction], indexes_of_txn_matched: &[u32]) -> Result<Option<msgs::FundingLocked>, msgs::ErrorMessage> {
3168+
///
3169+
/// May return some HTLCs (and their payment_hash) which have timed out and should be failed
3170+
/// back.
3171+
pub fn block_connected(&mut self, header: &BlockHeader, height: u32, txn_matched: &[&Transaction], indexes_of_txn_matched: &[u32]) -> Result<(Option<msgs::FundingLocked>, Vec<(HTLCSource, PaymentHash)>), msgs::ErrorMessage> {
3172+
let mut timed_out_htlcs = Vec::new();
3173+
self.holding_cell_htlc_updates.retain(|htlc_update| {
3174+
match htlc_update {
3175+
&HTLCUpdateAwaitingACK::AddHTLC { ref payment_hash, ref source, ref cltv_expiry, .. } => {
3176+
if *cltv_expiry <= height + HTLC_FAIL_BACK_BUFFER {
3177+
timed_out_htlcs.push((source.clone(), payment_hash.clone()));
3178+
false
3179+
} else { true }
3180+
},
3181+
_ => true
3182+
}
3183+
});
31643184
let non_shutdown_state = self.channel_state & (!MULTI_STATE_FLAGS);
31653185
if header.bitcoin_hash() != self.last_block_connected {
31663186
if self.funding_tx_confirmations > 0 {
@@ -3243,19 +3263,19 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
32433263
if self.channel_state & (ChannelState::MonitorUpdateFailed as u32) == 0 {
32443264
let next_per_commitment_secret = self.build_local_commitment_secret(self.cur_local_commitment_transaction_number);
32453265
let next_per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &next_per_commitment_secret);
3246-
return Ok(Some(msgs::FundingLocked {
3266+
return Ok((Some(msgs::FundingLocked {
32473267
channel_id: self.channel_id,
32483268
next_per_commitment_point: next_per_commitment_point,
3249-
}));
3269+
}), timed_out_htlcs));
32503270
} else {
32513271
self.monitor_pending_funding_locked = true;
3252-
return Ok(None);
3272+
return Ok((None, timed_out_htlcs));
32533273
}
32543274
}
32553275
}
32563276
}
32573277
}
3258-
Ok(None)
3278+
Ok((None, timed_out_htlcs))
32593279
}
32603280

32613281
/// Called by channelmanager based on chain blocks being disconnected.

0 commit comments

Comments
 (0)