Skip to content

Commit 1cb6e76

Browse files
Reconstruct pending_claiming_payments from monitor data
We are working on reducing reliance on regular persistence of the ChannelManager, and instead reconstructing (at least parts of) it using ChannelMonitor data on startup. As part of this, here we stop persisting the manager's pending_claiming_payments map, and reconstruct it from ChannelMonitor data. We already had most of the logic to do this that was in place for cases where the manager has fallen behind the monitors. The only missing piece was checking in-flight monitor updates in case they have preimages that tell us we're mid-claim.
1 parent 4d316b2 commit 1cb6e76

2 files changed

Lines changed: 248 additions & 2 deletions

File tree

lightning/src/ln/channelmanager.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18499,6 +18499,33 @@ impl<
1849918499
is_connected: false,
1850018500
};
1850118501

18502+
// Extract preimage data from in_flight_monitor_updates before it's consumed by the loop below.
18503+
// We need this for reconstructing pending_claiming_payments claims on restart.
18504+
let in_flight_preimages: Vec<_> = in_flight_monitor_updates
18505+
.iter()
18506+
.flat_map(|((counterparty_id, channel_id), updates)| {
18507+
updates.iter().flat_map(move |update| {
18508+
update.updates.iter().filter_map(move |step| {
18509+
if let ChannelMonitorUpdateStep::PaymentPreimage {
18510+
payment_preimage,
18511+
payment_info: Some(details),
18512+
} = step
18513+
{
18514+
Some((
18515+
*channel_id,
18516+
*counterparty_id,
18517+
(*payment_preimage).into(),
18518+
*payment_preimage,
18519+
vec![details.clone()],
18520+
))
18521+
} else {
18522+
None
18523+
}
18524+
})
18525+
})
18526+
})
18527+
.collect();
18528+
1850218529
const MAX_ALLOC_SIZE: usize = 1024 * 64;
1850318530
let mut failed_htlcs = Vec::new();
1850418531
let channel_count = channels.len();
@@ -19778,7 +19805,7 @@ impl<
1977819805
decode_update_add_htlcs: Mutex::new(decode_update_add_htlcs),
1977919806
claimable_payments: Mutex::new(ClaimablePayments {
1978019807
claimable_payments,
19781-
pending_claiming_payments: pending_claiming_payments_legacy,
19808+
pending_claiming_payments: new_hash_map(),
1978219809
}),
1978319810
outbound_scid_aliases: Mutex::new(outbound_scid_aliases),
1978419811
short_to_chan_info: FairRwLock::new(short_to_chan_info),
@@ -19850,8 +19877,12 @@ impl<
1985019877
}
1985119878
}
1985219879
}
19880+
19881+
// Because we are rebuilding `ClaimablePayments::pending_claiming_payments` here, we need to
19882+
// iterate over all the preimages in all the monitors as well as the preimages in in-flight
19883+
// monitor updates to get a complete picture of which channels/payments are mid-claim.
1985319884
for (channel_id, counterparty_node_id, payment_hash, payment_preimage, payment_claims) in
19854-
monitor_preimages
19885+
monitor_preimages.chain(in_flight_preimages.into_iter())
1985519886
{
1985619887
// If we have unresolved inbound committed HTLCs that were already forwarded to the
1985719888
// outbound edge and removed via claim, we need to make sure to claim them backwards via

lightning/src/ln/reload_tests.rs

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use crate::routing::router::{PaymentParameters, RouteParameters};
1818
use crate::sign::EntropySource;
1919
use crate::chain::transaction::OutPoint;
2020
use crate::events::{ClosureReason, Event, HTLCHandlingFailureType};
21+
use crate::ln::chan_utils::HTLCClaim;
2122
use crate::ln::channelmanager::{ChannelManager, ChannelManagerReadArgs, PaymentId, RAACommitmentOrder};
2223
use crate::ln::outbound_payment::RecipientOnionFields;
2324
use crate::ln::msgs;
@@ -2247,3 +2248,217 @@ fn test_reload_with_mpp_claims_on_same_channel() {
22472248
// nodes[0] should now have received both fulfills and generate PaymentSent.
22482249
expect_payment_sent(&nodes[0], payment_preimage, None, true, true);
22492250
}
2251+
2252+
#[test]
2253+
fn test_reload_with_in_flight_preimage_claim() {
2254+
do_test_reload_with_in_flight_preimage_claim(false);
2255+
do_test_reload_with_in_flight_preimage_claim(true);
2256+
}
2257+
2258+
fn do_test_reload_with_in_flight_preimage_claim(close_channel: bool) {
2259+
// Test that if a node receives a payment and calls `claim_funds`, but the
2260+
// `ChannelMonitorUpdate` containing the preimage is still in-flight (not yet persisted),
2261+
// then after a restart the payment claim completes correctly using the preimage from the
2262+
// in-flight monitor update.
2263+
//
2264+
// If close_channel is set, the channel is force-closed before reload to test that in-flight
2265+
// monitor updates are preserved across channel closure.
2266+
let chanmon_cfgs = create_chanmon_cfgs(2);
2267+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
2268+
let persister;
2269+
let new_chain_monitor;
2270+
let persister_2;
2271+
let new_chain_monitor_2;
2272+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
2273+
let nodes_1_deserialized;
2274+
let nodes_1_deserialized_2;
2275+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
2276+
2277+
let node_0_id = nodes[0].node.get_our_node_id();
2278+
let node_1_id = nodes[1].node.get_our_node_id();
2279+
2280+
let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1).2;
2281+
2282+
// Send a payment from nodes[0] to nodes[1].
2283+
let amt_msat = 1_000_000;
2284+
let (route, payment_hash, payment_preimage, payment_secret) =
2285+
get_route_and_payment_hash!(nodes[0], nodes[1], amt_msat);
2286+
send_along_route_with_secret(
2287+
&nodes[0], route, &[&[&nodes[1]]], amt_msat, payment_hash, payment_secret,
2288+
);
2289+
2290+
// Serialize the monitor before claiming so it doesn't have the preimage update.
2291+
let mon_serialized_pre_claim = get_monitor!(nodes[1], chan_id).encode();
2292+
2293+
// Set the persister to return InProgress so the preimage monitor update will be stored as
2294+
// in-flight.
2295+
chanmon_cfgs[1].persister.set_update_ret(ChannelMonitorUpdateStatus::InProgress);
2296+
2297+
nodes[1].node.claim_funds(payment_preimage);
2298+
check_added_monitors(&nodes[1], 1);
2299+
2300+
// The PaymentClaimed event is held back until the monitor update completes.
2301+
assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
2302+
2303+
// Disconnect peers before reload/close.
2304+
nodes[0].node.peer_disconnected(node_1_id);
2305+
nodes[1].node.peer_disconnected(node_0_id);
2306+
2307+
let (commitment_tx, coinbase_tx) = if close_channel {
2308+
// Provide anchor reserves for fee bumping (anchors are enabled by default).
2309+
let coinbase_tx = provide_anchor_reserves(&nodes);
2310+
2311+
// Force close the channel - the in-flight preimage update should be preserved
2312+
nodes[1].node.force_close_broadcasting_latest_txn(&chan_id, &node_0_id, "test".to_string()).unwrap();
2313+
check_closed_broadcast(&nodes[1], 1, false);
2314+
check_added_monitors(&nodes[1], 1);
2315+
let reason = ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(true), message: "test".to_string() };
2316+
check_closed_event(&nodes[1], 1, reason, &[node_0_id], 100_000);
2317+
// Handle the bump event to broadcast the commitment tx (anchors are enabled by default).
2318+
handle_bump_close_event(&nodes[1]);
2319+
let txn = nodes[1].tx_broadcaster.txn_broadcast();
2320+
assert_eq!(txn.len(), 1);
2321+
(Some(txn.into_iter().next().unwrap()), Some(coinbase_tx))
2322+
} else {
2323+
(None, None)
2324+
};
2325+
2326+
// Serialize the ChannelManager containing the in-flight preimage monitor update.
2327+
let node_1_serialized = nodes[1].node.encode();
2328+
2329+
reload_node!(
2330+
nodes[1],
2331+
node_1_serialized,
2332+
&[&mon_serialized_pre_claim],
2333+
persister,
2334+
new_chain_monitor,
2335+
nodes_1_deserialized
2336+
);
2337+
2338+
// The PaymentClaimed event should be regenerated from the in-flight update.
2339+
expect_payment_claimed!(nodes[1], payment_hash, amt_msat);
2340+
2341+
if close_channel {
2342+
check_added_monitors(&nodes[1], 4);
2343+
{
2344+
let monitor_updates = nodes[1].chain_monitor.monitor_updates.lock().unwrap();
2345+
let updates = monitor_updates.get(&chan_id).unwrap();
2346+
for (i, update) in updates.iter().rev().take(4).enumerate() {
2347+
match i {
2348+
0 => {
2349+
// The latest update should be because we processed PaymentClaimed on a closed channel.
2350+
assert_eq!(update.updates.len(), 1);
2351+
assert!(matches!(update.updates[0], ChannelMonitorUpdateStep::InboundPaymentClaimed { .. }));
2352+
},
2353+
1 => {
2354+
// Because pre-reload our preimage update was in-flight, we will still generate a
2355+
// redundant one on startup
2356+
assert_eq!(update.updates.len(), 1);
2357+
assert!(matches!(update.updates[0], ChannelMonitorUpdateStep::PaymentPreimage { payment_info: None, .. }))
2358+
},
2359+
2 => {
2360+
// The force close update
2361+
assert_eq!(update.updates.len(), 1);
2362+
assert!(matches!(update.updates[0], ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast: true }));
2363+
},
2364+
3 => {
2365+
// The original in-flight claim with full payment info and counterparty commitment
2366+
assert_eq!(update.updates.len(), 2);
2367+
assert!(matches!(update.updates[0], ChannelMonitorUpdateStep::PaymentPreimage { payment_info: Some(_), .. }));
2368+
assert!(matches!(update.updates[1], ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { .. }));
2369+
},
2370+
_ => panic!("Unexpected update index"),
2371+
}
2372+
}
2373+
}
2374+
} else {
2375+
check_added_monitors(&nodes[1], 1);
2376+
{
2377+
let monitor_updates = nodes[1].chain_monitor.monitor_updates.lock().unwrap();
2378+
let updates = monitor_updates.get(&chan_id).unwrap();
2379+
let update = updates.last().unwrap();
2380+
assert_eq!(update.updates.len(), 2);
2381+
assert!(matches!(update.updates[0], ChannelMonitorUpdateStep::PaymentPreimage { payment_info: Some(_), .. }));
2382+
assert!(matches!(update.updates[1], ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { .. }));
2383+
}
2384+
}
2385+
2386+
// Verify the monitor now has the preimage (the in-flight update was applied during reload).
2387+
assert!(
2388+
get_monitor!(nodes[1], chan_id).test_get_all_stored_preimages().contains_key(&payment_hash),
2389+
"Monitor should have preimage after in-flight update replay"
2390+
);
2391+
2392+
// Second reload to test for redundant PaymentClaimed events.
2393+
let node_1_serialized_2 = nodes[1].node.encode();
2394+
let mon_serialized_2 = get_monitor!(nodes[1], chan_id).encode();
2395+
2396+
reload_node!(
2397+
nodes[1],
2398+
node_1_serialized_2,
2399+
&[&mon_serialized_2],
2400+
persister_2,
2401+
new_chain_monitor_2,
2402+
nodes_1_deserialized_2
2403+
);
2404+
2405+
// The second reload should not replay any monitor updates (they were already applied).
2406+
check_added_monitors(&nodes[1], 0);
2407+
2408+
if !close_channel {
2409+
// If the channel is still open, there will be a redundant PaymentClaimed event generated each
2410+
// restart until the HTLC is removed.
2411+
expect_payment_claimed!(nodes[1], payment_hash, amt_msat);
2412+
2413+
// Complete the payment. Use pending_htlc_claims instead of pending_cell_htlc_claims
2414+
// because the latter expects a monitor update, but the claim is already in the monitor.
2415+
let mut reconnect_args = ReconnectArgs::new(&nodes[1], &nodes[0]);
2416+
reconnect_args.pending_htlc_claims = (0, 1);
2417+
reconnect_nodes(reconnect_args);
2418+
2419+
expect_payment_sent(&nodes[0], payment_preimage, None, true, true);
2420+
} else {
2421+
// No redundant PaymentClaimed event.
2422+
assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
2423+
2424+
// Mine the commitment tx on both nodes so nodes[0] sees the channel is closed.
2425+
let commitment_tx = commitment_tx.unwrap();
2426+
let coinbase_tx = coinbase_tx.unwrap();
2427+
mine_transaction(&nodes[0], &commitment_tx);
2428+
mine_transaction(&nodes[1], &commitment_tx);
2429+
2430+
// Peers are disconnected, so no error message is sent.
2431+
check_closed_broadcast(&nodes[0], 1, false);
2432+
check_added_monitors(&nodes[0], 1);
2433+
check_closed_event(&nodes[0], 1, ClosureReason::CommitmentTxConfirmed, &[node_1_id], 100_000);
2434+
2435+
// nodes[1] broadcasts HTLC claim tx with the preimage.
2436+
// We get 2 BumpTransaction events: ChannelClose (for anchor) and HTLCResolution.
2437+
let events = nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events();
2438+
assert!(events.len() <= 2);
2439+
for event in events {
2440+
if let Event::BumpTransaction(bump) = event {
2441+
nodes[1].bump_tx_handler.handle_event(&bump);
2442+
} else {
2443+
panic!("Unexpected event: {:?}", event);
2444+
}
2445+
}
2446+
// Filter for HTLC claim tx by checking for preimage in the witness.
2447+
let htlc_claim_txn: Vec<_> = nodes[1]
2448+
.tx_broadcaster
2449+
.txn_broadcast()
2450+
.into_iter()
2451+
.filter(|tx| {
2452+
tx.input.iter().any(|inp| {
2453+
matches!(HTLCClaim::from_witness(&inp.witness), Some(HTLCClaim::AcceptedPreimage))
2454+
})
2455+
})
2456+
.collect();
2457+
assert_eq!(htlc_claim_txn.len(), 1);
2458+
check_spends!(htlc_claim_txn[0], commitment_tx, coinbase_tx);
2459+
2460+
// Mine the HTLC claim on nodes[0] - it learns the preimage and generates PaymentSent.
2461+
mine_transaction(&nodes[0], &htlc_claim_txn[0]);
2462+
expect_payment_sent(&nodes[0], payment_preimage, None, true, true);
2463+
}
2464+
}

0 commit comments

Comments
 (0)