Skip to content

Commit b67f15c

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 fc1c75c commit b67f15c

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

0 commit comments

Comments
 (0)