From 6630b99cf7895cdb111e811b7b6f307b637d3d85 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 11 Apr 2019 09:38:55 +0930 Subject: [PATCH] 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 --- lightningd/invoice.c | 53 ++++++++++++++++ lightningd/invoice.h | 24 ++++++-- lightningd/peer_htlcs.c | 68 ++++++--------------- lightningd/peer_htlcs.h | 4 ++ lightningd/test/run-invoice-select-inchan.c | 16 +++++ tests/test_pay.py | 3 +- wallet/test/run-wallet.c | 6 ++ 7 files changed, 118 insertions(+), 56 deletions(-) diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 345ebcf66..4609f483e 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -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, diff --git a/lightningd/invoice.h b/lightningd/invoice.h index a7b615780..8f1615aee 100644 --- a/lightningd/invoice.h +++ b/lightningd/invoice.h @@ -1,9 +1,25 @@ #ifndef LIGHTNING_LIGHTNINGD_INVOICE_H #define LIGHTNING_LIGHTNINGD_INVOICE_H #include "config.h" -#include -#include -#include -#include +#include + +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 */ diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index d3cff6483..08a925c28 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -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); } /* diff --git a/lightningd/peer_htlcs.h b/lightningd/peer_htlcs.h index 27f601b86..dd57c99f3 100644 --- a/lightningd/peer_htlcs.h +++ b/lightningd/peer_htlcs.h @@ -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 */ diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index ebb07b391..034fec7e3 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -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, diff --git a/tests/test_pay.py b/tests/test_pay.py index d46a61cf7..8ae79b3fb 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -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 diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 9b1fa8272..2bc1c855d 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -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)