Skip to content

Commit 4348833

Browse files
committed
Add a test of an HTLC being fulfilled and then later failed
Peers probably shouldn't do this, but if they want to give us free money, we should take it and not generate any spurious events.
1 parent 8ae3617 commit 4348833

File tree

1 file changed

+75
-0
lines changed

1 file changed

+75
-0
lines changed

lightning/src/ln/payment_tests.rs

+75
Original file line numberDiff line numberDiff line change
@@ -623,3 +623,78 @@ fn test_dup_htlc_onchain_fails_on_reload() {
623623
do_test_dup_htlc_onchain_fails_on_reload(false, true, false);
624624
do_test_dup_htlc_onchain_fails_on_reload(false, false, false);
625625
}
626+
627+
#[test]
628+
fn test_fulfill_restart_failure() {
629+
// When we receive an update_fulfill_htlc message, we immediately consider the HTLC fully
630+
// fulfilled. At this point, the peer can reconnect and decide to either fulfill the HTLC
631+
// again, or fail it, giving us free money.
632+
//
633+
// Of course probably they won't fail it and give us free money, but because we have code to
634+
// handle it, we should test the logic for it anyway. We do that here.
635+
let chanmon_cfgs = create_chanmon_cfgs(2);
636+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
637+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
638+
let persister: test_utils::TestPersister;
639+
let new_chain_monitor: test_utils::TestChainMonitor;
640+
let nodes_1_deserialized: ChannelManager<EnforcingSigner, &test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestLogger>;
641+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
642+
643+
let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known()).2;
644+
let (payment_preimage, payment_hash, _) = route_payment(&nodes[0], &[&nodes[1]], 100_000);
645+
646+
// The simplest way to get a failure after a fulfill is to reload nodes[1] from a state
647+
// pre-fulfill, which we do by serializing it here.
648+
let mut chan_manager_serialized = test_utils::TestVecWriter(Vec::new());
649+
nodes[1].node.write(&mut chan_manager_serialized).unwrap();
650+
let mut chan_0_monitor_serialized = test_utils::TestVecWriter(Vec::new());
651+
get_monitor!(nodes[1], chan_id).write(&mut chan_0_monitor_serialized).unwrap();
652+
653+
nodes[1].node.claim_funds(payment_preimage);
654+
check_added_monitors!(nodes[1], 1);
655+
let htlc_fulfill_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
656+
nodes[0].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &htlc_fulfill_updates.update_fulfill_htlcs[0]);
657+
expect_payment_sent!(nodes[0], payment_preimage);
658+
659+
// Now reload nodes[1]...
660+
persister = test_utils::TestPersister::new();
661+
let keys_manager = &chanmon_cfgs[1].keys_manager;
662+
new_chain_monitor = test_utils::TestChainMonitor::new(Some(nodes[1].chain_source), nodes[1].tx_broadcaster.clone(), nodes[1].logger, node_cfgs[1].fee_estimator, &persister, keys_manager);
663+
nodes[1].chain_monitor = &new_chain_monitor;
664+
let mut chan_0_monitor_read = &chan_0_monitor_serialized.0[..];
665+
let (_, mut chan_0_monitor) = <(BlockHash, ChannelMonitor<EnforcingSigner>)>::read(
666+
&mut chan_0_monitor_read, keys_manager).unwrap();
667+
assert!(chan_0_monitor_read.is_empty());
668+
669+
let (_, nodes_1_deserialized_tmp) = {
670+
let mut channel_monitors = HashMap::new();
671+
channel_monitors.insert(chan_0_monitor.get_funding_txo().0, &mut chan_0_monitor);
672+
<(BlockHash, ChannelManager<EnforcingSigner, &test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestLogger>)>
673+
::read(&mut io::Cursor::new(&chan_manager_serialized.0[..]), ChannelManagerReadArgs {
674+
default_config: Default::default(),
675+
keys_manager,
676+
fee_estimator: node_cfgs[1].fee_estimator,
677+
chain_monitor: nodes[1].chain_monitor,
678+
tx_broadcaster: nodes[1].tx_broadcaster.clone(),
679+
logger: nodes[1].logger,
680+
channel_monitors,
681+
}).unwrap()
682+
};
683+
nodes_1_deserialized = nodes_1_deserialized_tmp;
684+
685+
assert!(nodes[1].chain_monitor.watch_channel(chan_0_monitor.get_funding_txo().0, chan_0_monitor).is_ok());
686+
check_added_monitors!(nodes[1], 1);
687+
nodes[1].node = &nodes_1_deserialized;
688+
689+
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
690+
reconnect_nodes(&nodes[0], &nodes[1], (false, false), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (false, false));
691+
692+
nodes[1].node.fail_htlc_backwards(&payment_hash);
693+
expect_pending_htlcs_forwardable!(nodes[1]);
694+
check_added_monitors!(nodes[1], 1);
695+
let htlc_fail_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
696+
nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &htlc_fail_updates.update_fail_htlcs[0]);
697+
commitment_signed_dance!(nodes[0], nodes[1], htlc_fail_updates.commitment_signed, false);
698+
// nodes[0] shouldn't generate any events here, while it just got a payment failure completion
699+
// it had already considered the payment fulfilled, and now they just got free money.
700+
}

0 commit comments

Comments
 (0)