diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 4536b3243..e6063f5da 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -243,7 +243,7 @@ static bool check_amount(struct htlc_in *hin, * * `outgoing_cltv_value` - The CLTV value that the _outgoing_ HTLC carrying * the packet should have. * - * cltv_expiry - cltv_expiry_delta = outgoing_cltv_value + * cltv_expiry - cltv_expiry_delta >= outgoing_cltv_value * * Inclusion of this field allows a node to both authenticate the information * specified by the original sender and the parameters of the HTLC forwarded, @@ -259,7 +259,7 @@ static bool check_amount(struct htlc_in *hin, static bool check_cltv(struct htlc_in *hin, u32 cltv_expiry, u32 outgoing_cltv_value, u32 delta) { - if (cltv_expiry - delta == outgoing_cltv_value) + if (cltv_expiry - delta >= outgoing_cltv_value) return true; log_debug(hin->key.peer->ld->log, "HTLC %"PRIu64" incorrect CLTV:" " %u in, %u out, delta reqd %u", diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index 9c116852b..28b9a6c5a 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -1290,6 +1290,58 @@ class LightningDTests(BaseLightningDTests): .format(bitcoind.rpc.getblockcount() + 9 + shadow_route)) assert l3.rpc.listinvoice('test_forward_different_fees_and_cltv')[0]['complete'] == True + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1 for --dev-broadcast-interval") + def test_forward_pad_fees_and_cltv(self): + """Test that we are allowed extra locktime delta, and fees""" + + l1 = self.node_factory.get_node(options=['--cltv-delta=10', '--fee-base=100', '--fee-per-satoshi=1000']) + l2 = self.node_factory.get_node(options=['--cltv-delta=20', '--fee-base=200', '--fee-per-satoshi=2000']) + l3 = self.node_factory.get_node(options=['--cltv-delta=30', '--cltv-final=9', '--fee-base=300', '--fee-per-satoshi=3000']) + + ret = l1.rpc.connect(l2.info['id'], 'localhost:{}'.format(l2.info['port'])) + assert ret['id'] == l2.info['id'] + + l1.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER') + l2.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER') + + ret = l2.rpc.connect(l3.info['id'], 'localhost:{}'.format(l3.info['port'])) + assert ret['id'] == l3.info['id'] + + l2.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER') + l3.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER') + + c1 = self.fund_channel(l1, l2, 10**6) + c2 = self.fund_channel(l2, l3, 10**6) + + # Allow announce messages. + l1.bitcoin.rpc.generate(5) + + # Make sure l1 has seen announce for all channels. + l1.daemon.wait_for_logs([ + 'Received channel_update for channel {}\\(0\\)'.format(c1), + 'Received channel_update for channel {}\\(1\\)'.format(c1), + 'Received channel_update for channel {}\\(0\\)'.format(c2), + 'Received channel_update for channel {}\\(1\\)'.format(c2)]) + + route = l1.rpc.getroute(l3.info['id'], 4999999, 1)["route"] + assert len(route) == 2 + + assert route[0]['msatoshi'] == 5010198 + assert route[0]['delay'] == 20 + 9 + assert route[1]['msatoshi'] == 4999999 + assert route[1]['delay'] == 9 + + # Modify so we overpay, overdo the cltv. + route[0]['msatoshi'] += 2000 + route[0]['delay'] += 20 + route[1]['msatoshi'] += 1000 + route[1]['delay'] += 10 + + # This should work. + rhash = l3.rpc.invoice(4999999, 'test_forward_pad_fees_and_cltv')['rhash'] + l1.rpc.sendpay(to_json(route), rhash) + assert l3.rpc.listinvoice('test_forward_pad_fees_and_cltv')[0]['complete'] == True + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_disconnect(self): # These should all make us fail, and retry.