Skip to content

Commit f47acdb

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 48a3f42 commit f47acdb

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
@@ -578,3 +578,78 @@ fn test_dup_htlc_onchain_fails_on_reload() {
578578
do_test_dup_htlc_onchain_fails_on_reload(false, true, false);
579579
do_test_dup_htlc_onchain_fails_on_reload(false, false, false);
580580
}
581+
582+
#[test]
583+
fn test_fulfill_restart_failure() {
584+
// When we receive an update_fulfill_htlc message, we immediately consider the HTLC fully
585+
// fulfilled. At this point, the peer can reconnect and decide to either fulfill the HTLC
586+
// again, or fail it, giving us free money.
587+
//
588+
// Of course probably they won't fail it and give us free money, but because we have code to
589+
// handle it, we should test the logic for it anyway. We do that here.
590+
let chanmon_cfgs = create_chanmon_cfgs(2);
591+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
592+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
593+
let persister: test_utils::TestPersister;
594+
let new_chain_monitor: test_utils::TestChainMonitor;
595+
let nodes_1_deserialized: ChannelManager<EnforcingSigner, &test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestLogger>;
596+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
597+
598+
let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known()).2;
599+
let (payment_preimage, payment_hash, _) = route_payment(&nodes[0], &[&nodes[1]], 100_000);
600+
601+
// The simplest way to get a failure after a fulfill is to reload nodes[1] from a state
602+
// pre-fulfill, which we do by serializing it here.
603+
let mut chan_manager_serialized = test_utils::TestVecWriter(Vec::new());
604+
nodes[1].node.write(&mut chan_manager_serialized).unwrap();
605+
let mut chan_0_monitor_serialized = test_utils::TestVecWriter(Vec::new());
606+
get_monitor!(nodes[1], chan_id).write(&mut chan_0_monitor_serialized).unwrap();
607+
608+
nodes[1].node.claim_funds(payment_preimage);
609+
check_added_monitors!(nodes[1], 1);
610+
let htlc_fulfill_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
611+
nodes[0].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &htlc_fulfill_updates.update_fulfill_htlcs[0]);
612+
expect_payment_sent!(nodes[0], payment_preimage);
613+
614+
// Now reload nodes[1]...
615+
persister = test_utils::TestPersister::new();
616+
let keys_manager = &chanmon_cfgs[1].keys_manager;
617+
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);
618+
nodes[1].chain_monitor = &new_chain_monitor;
619+
let mut chan_0_monitor_read = &chan_0_monitor_serialized.0[..];
620+
let (_, mut chan_0_monitor) = <(BlockHash, ChannelMonitor<EnforcingSigner>)>::read(
621+
&mut chan_0_monitor_read, keys_manager).unwrap();
622+
assert!(chan_0_monitor_read.is_empty());
623+
624+
let (_, nodes_1_deserialized_tmp) = {
625+
let mut channel_monitors = HashMap::new();
626+
channel_monitors.insert(chan_0_monitor.get_funding_txo().0, &mut chan_0_monitor);
627+
<(BlockHash, ChannelManager<EnforcingSigner, &test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestLogger>)>
628+
::read(&mut io::Cursor::new(&chan_manager_serialized.0[..]), ChannelManagerReadArgs {
629+
default_config: Default::default(),
630+
keys_manager,
631+
fee_estimator: node_cfgs[1].fee_estimator,
632+
chain_monitor: nodes[1].chain_monitor,
633+
tx_broadcaster: nodes[1].tx_broadcaster.clone(),
634+
logger: nodes[1].logger,
635+
channel_monitors,
636+
}).unwrap()
637+
};
638+
nodes_1_deserialized = nodes_1_deserialized_tmp;
639+
640+
assert!(nodes[1].chain_monitor.watch_channel(chan_0_monitor.get_funding_txo().0, chan_0_monitor).is_ok());
641+
check_added_monitors!(nodes[1], 1);
642+
nodes[1].node = &nodes_1_deserialized;
643+
644+
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
645+
reconnect_nodes(&nodes[0], &nodes[1], (false, false), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (false, false));
646+
647+
nodes[1].node.fail_htlc_backwards(&payment_hash);
648+
expect_pending_htlcs_forwardable!(nodes[1]);
649+
check_added_monitors!(nodes[1], 1);
650+
let htlc_fail_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
651+
nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &htlc_fail_updates.update_fail_htlcs[0]);
652+
commitment_signed_dance!(nodes[0], nodes[1], htlc_fail_updates.commitment_signed, false);
653+
// nodes[0] shouldn't generate any events here, while it just got a payment failure completion
654+
// it had already considered the payment fulfilled, and now they just got free money.
655+
}

0 commit comments

Comments
 (0)