lightningd: move local invoice resolution into invoice.c function.

We're going to make it async, so start by moving the core code into
invoice.c and having that directly call fail/success functions for the
htlc.

We add an extra check in fulfill_htlc() that the HTLC state is correct:
that can't happen now, but may once we're async.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2019-04-11 09:38:55 +09:30
parent aa00e26765
commit 6630b99cf7
7 changed files with 118 additions and 56 deletions

View file

@ -28,6 +28,7 @@
#include <lightningd/log.h>
#include <lightningd/options.h>
#include <lightningd/peer_control.h>
#include <lightningd/peer_htlcs.h>
#include <lightningd/subd.h>
#include <sodium/randombytes.h>
#include <wire/wire_sync.h>
@ -100,6 +101,58 @@ static void wait_on_invoice(const struct invoice *invoice, void *cmd)
tell_waiter_deleted((struct command *) cmd);
}
void invoice_try_pay(struct lightningd *ld,
struct htlc_in *hin,
const struct sha256 *payment_hash,
const struct amount_msat msat)
{
struct invoice invoice;
const struct invoice_details *details;
if (!wallet_invoice_find_unpaid(ld->wallet, &invoice, payment_hash)) {
fail_htlc(hin, WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS);
return;
}
details = wallet_invoice_details(tmpctx, ld->wallet, invoice);
/* BOLT #4:
*
* An _intermediate hop_ MUST NOT, but the _final node_:
*...
* - if the amount paid is less than the amount expected:
* - MUST fail the HTLC.
*/
if (details->msat != NULL) {
struct amount_msat twice;
if (amount_msat_less(msat, *details->msat)) {
fail_htlc(hin,
WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS);
return;
}
if (amount_msat_add(&twice, *details->msat, *details->msat)
&& amount_msat_greater(msat, twice)) {
/* FIXME: bolt update fixes this quote! */
/* BOLT #4:
*
* - if the amount paid is more than twice the amount expected:
* - SHOULD fail the HTLC.
* - SHOULD return an `incorrect_or_unknown_payment_details` error.
*/
fail_htlc(hin,
WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS);
return;
}
}
log_info(ld->log, "Resolved invoice '%s' with amount %s",
details->label->s,
type_to_string(tmpctx, struct amount_msat, &msat));
wallet_invoice_resolve(ld->wallet, invoice, msat);
fulfill_htlc(hin, &details->r);
}
static bool hsm_sign_b11(const u5 *u5bytes,
const u8 *hrpu8,
secp256k1_ecdsa_recoverable_signature *rsig,

View file

@ -1,9 +1,25 @@
#ifndef LIGHTNING_LIGHTNINGD_INVOICE_H
#define LIGHTNING_LIGHTNINGD_INVOICE_H
#include "config.h"
#include <bitcoin/preimage.h>
#include <ccan/crypto/sha256/sha256.h>
#include <ccan/list/list.h>
#include <ccan/tal/tal.h>
#include <wire/gen_onion_wire.h>
struct amount_msat;
struct htlc_in;
struct lightningd;
struct sha256;
/**
* invoice_try_pay - process payment for this payment_hash, amount msat.
* @ld: lightningd
* @hin: the input HTLC which is offering to pay.
* @payment_hash: hash of preimage they want.
* @msat: amount they offer to pay.
*
* Either calls fulfill_htlc() or fail_htlcs().
*/
void invoice_try_pay(struct lightningd *ld,
struct htlc_in *hin,
const struct sha256 *payment_hash,
const struct amount_msat msat);
#endif /* LIGHTNING_LIGHTNINGD_INVOICE_H */

View file

@ -135,6 +135,14 @@ static void local_fail_htlc(struct htlc_in *hin, enum onion_type failcode,
fail_in_htlc(hin, failcode, NULL, out_channel);
}
void fail_htlc(struct htlc_in *hin, enum onion_type failcode)
{
assert(failcode);
/* Final hop never sends an UPDATE. */
assert(!(failcode & UPDATE));
local_fail_htlc(hin, failcode, NULL);
}
/* localfail are for handing to the local payer if it's local. */
static void fail_out_htlc(struct htlc_out *hout, const char *localfail)
{
@ -216,12 +224,19 @@ static bool check_cltv(struct htlc_in *hin,
return false;
}
static void fulfill_htlc(struct htlc_in *hin, const struct preimage *preimage)
void fulfill_htlc(struct htlc_in *hin, const struct preimage *preimage)
{
u8 *msg;
struct channel *channel = hin->key.channel;
struct wallet *wallet = channel->peer->ld->wallet;
if (hin->hstate != RCVD_ADD_ACK_REVOCATION) {
log_debug(channel->log,
"HTLC fulfilled, but not ready any more (%s).",
htlc_state_name(hin->hstate));
return;
}
hin->preimage = tal_dup(hin, struct preimage, preimage);
/* We update state now to signal it's in progress, for persistence. */
@ -259,8 +274,6 @@ static void handle_localpay(struct htlc_in *hin,
u32 outgoing_cltv_value)
{
enum onion_type failcode;
struct invoice invoice;
const struct invoice_details *details;
struct lightningd *ld = hin->key.channel->peer->ld;
/* BOLT #4:
@ -289,41 +302,6 @@ static void handle_localpay(struct htlc_in *hin,
goto fail;
}
if (!wallet_invoice_find_unpaid(ld->wallet, &invoice, payment_hash)) {
failcode = WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS;
goto fail;
}
details = wallet_invoice_details(tmpctx, ld->wallet, invoice);
/* BOLT #4:
*
* An _intermediate hop_ MUST NOT, but the _final node_:
*...
* - if the amount paid is less than the amount expected:
* - MUST fail the HTLC.
*/
if (details->msat != NULL) {
struct amount_msat twice;
if (amount_msat_less(hin->msat, *details->msat)) {
failcode = WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS;
goto fail;
}
if (amount_msat_add(&twice, *details->msat, *details->msat)
&& amount_msat_greater(hin->msat, twice)) {
/* FIXME: bolt update fixes this quote! */
/* BOLT #4:
*
* - if the amount paid is more than twice the amount expected:
* - SHOULD fail the HTLC.
* - SHOULD return an `incorrect_or_unknown_payment_details` error.
*/
failcode = WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS;
goto fail;
}
}
/* BOLT #4:
*
* - if the `cltv_expiry` value is unreasonably near the present:
@ -341,21 +319,11 @@ static void handle_localpay(struct htlc_in *hin,
goto fail;
}
log_info(ld->log, "Resolving invoice '%s' with HTLC %"PRIu64,
details->label->s, hin->key.id);
log_debug(ld->log, "%s: Actual amount %s, HTLC expiry %u",
details->label->s,
type_to_string(tmpctx, struct amount_msat, &hin->msat),
cltv_expiry);
fulfill_htlc(hin, &details->r);
wallet_invoice_resolve(ld->wallet, invoice, hin->msat);
invoice_try_pay(ld, hin, payment_hash, amt_to_forward);
return;
fail:
/* Final hop never sends an UPDATE. */
assert(!(failcode & UPDATE));
local_fail_htlc(hin, failcode, NULL);
fail_htlc(hin, failcode);
}
/*

View file

@ -63,4 +63,8 @@ void htlcs_reconnect(struct lightningd *ld,
struct htlc_in_map *htlcs_in,
struct htlc_out_map *htlcs_out);
/* For HTLCs which terminate here, invoice payment calls one of these. */
void fulfill_htlc(struct htlc_in *hin, const struct preimage *preimage);
void fail_htlc(struct htlc_in *hin, enum onion_type failcode);
#endif /* LIGHTNING_LIGHTNINGD_PEER_HTLCS_H */

View file

@ -73,6 +73,9 @@ void connect_succeeded(struct lightningd *ld UNNEEDED, const struct node_id *id
void delay_then_reconnect(struct channel *channel UNNEEDED, u32 seconds_delay UNNEEDED,
const struct wireaddr_internal *addrhint TAKES UNNEEDED)
{ fprintf(stderr, "delay_then_reconnect called!\n"); abort(); }
/* Generated stub for fail_htlc */
void fail_htlc(struct htlc_in *hin UNNEEDED, enum onion_type failcode UNNEEDED)
{ fprintf(stderr, "fail_htlc called!\n"); abort(); }
/* Generated stub for fatal */
void fatal(const char *fmt UNNEEDED, ...)
{ fprintf(stderr, "fatal called!\n"); abort(); }
@ -97,6 +100,9 @@ bool fromwire_hsm_sign_invoice_reply(const void *p UNNEEDED, secp256k1_ecdsa_rec
/* Generated stub for fromwire_onchain_dev_memleak_reply */
bool fromwire_onchain_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED)
{ fprintf(stderr, "fromwire_onchain_dev_memleak_reply called!\n"); abort(); }
/* Generated stub for fulfill_htlc */
void fulfill_htlc(struct htlc_in *hin UNNEEDED, const struct preimage *preimage UNNEEDED)
{ fprintf(stderr, "fulfill_htlc called!\n"); abort(); }
/* Generated stub for get_block_height */
u32 get_block_height(const struct chain_topology *topo UNNEEDED)
{ fprintf(stderr, "get_block_height called!\n"); abort(); }
@ -514,6 +520,11 @@ bool wallet_invoice_find_by_rhash(struct wallet *wallet UNNEEDED,
struct invoice *pinvoice UNNEEDED,
const struct sha256 *rhash UNNEEDED)
{ fprintf(stderr, "wallet_invoice_find_by_rhash called!\n"); abort(); }
/* Generated stub for wallet_invoice_find_unpaid */
bool wallet_invoice_find_unpaid(struct wallet *wallet UNNEEDED,
struct invoice *pinvoice UNNEEDED,
const struct sha256 *rhash UNNEEDED)
{ fprintf(stderr, "wallet_invoice_find_unpaid called!\n"); abort(); }
/* Generated stub for wallet_invoice_iterate */
bool wallet_invoice_iterate(struct wallet *wallet UNNEEDED,
struct invoice_iterator *it UNNEEDED)
@ -523,6 +534,11 @@ const struct invoice_details *wallet_invoice_iterator_deref(const tal_t *ctx UNN
struct wallet *wallet UNNEEDED,
const struct invoice_iterator *it UNNEEDED)
{ fprintf(stderr, "wallet_invoice_iterator_deref called!\n"); abort(); }
/* Generated stub for wallet_invoice_resolve */
void wallet_invoice_resolve(struct wallet *wallet UNNEEDED,
struct invoice invoice UNNEEDED,
struct amount_msat received UNNEEDED)
{ fprintf(stderr, "wallet_invoice_resolve called!\n"); abort(); }
/* Generated stub for wallet_invoice_waitany */
void wallet_invoice_waitany(const tal_t *ctx UNNEEDED,
struct wallet *wallet UNNEEDED,

View file

@ -979,8 +979,7 @@ def test_forward_different_fees_and_cltv(node_factory, bitcoind):
.format(bitcoind.rpc.getblockcount() + 20 + 9 + shadow_route))
l2.daemon.wait_for_log("Adding HTLC 0 amount=4999999msat cltv={} gave CHANNEL_ERR_ADD_OK"
.format(bitcoind.rpc.getblockcount() + 9 + shadow_route))
l3.daemon.wait_for_log("test_forward_different_fees_and_cltv: Actual amount 4999999msat, HTLC expiry {}"
.format(bitcoind.rpc.getblockcount() + 9 + shadow_route))
l3.daemon.wait_for_log("Resolved invoice 'test_forward_different_fees_and_cltv' with amount 4999999msat")
assert only_one(l3.rpc.listinvoices('test_forward_different_fees_and_cltv')['invoices'])['status'] == 'paid'
# Check that we see all the channels

View file

@ -190,6 +190,12 @@ void invoices_waitone(const tal_t *ctx UNNEEDED,
void (*cb)(const struct invoice * UNNEEDED, void*) UNNEEDED,
void *cbarg UNNEEDED)
{ fprintf(stderr, "invoices_waitone called!\n"); abort(); }
/* Generated stub for invoice_try_pay */
void invoice_try_pay(struct lightningd *ld UNNEEDED,
struct htlc_in *hin UNNEEDED,
const struct sha256 *payment_hash UNNEEDED,
const struct amount_msat msat UNNEEDED)
{ fprintf(stderr, "invoice_try_pay called!\n"); abort(); }
/* Generated stub for json_add_address */
void json_add_address(struct json_stream *response UNNEEDED, const char *fieldname UNNEEDED,
const struct wireaddr *addr UNNEEDED)