diff --git a/lightningd/htlc_set.c b/lightningd/htlc_set.c index fc59c64a5..fa5b1d032 100644 --- a/lightningd/htlc_set.c +++ b/lightningd/htlc_set.c @@ -178,8 +178,7 @@ void htlc_set_add(struct lightningd *ld, } if (amount_msat_eq(set->so_far, total_msat)) { - /* FIXME: hand to invoice_try_pay! */ - tal_free(set); + invoice_try_pay(ld, set, details); return; } diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 5c13212e2..114900379 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -123,7 +123,7 @@ static void invoice_secret(const struct preimage *payment_preimage, struct invoice_payment_hook_payload { struct lightningd *ld; /* Set to NULL if it is deleted while waiting for plugin */ - struct htlc_in *hin; + struct htlc_set *set; /* What invoice it's trying to pay. */ const struct json_escape *label; /* Amount it's offering. */ @@ -146,12 +146,13 @@ invoice_payment_serialize(struct invoice_payment_hook_payload *payload, json_object_end(stream); /* .payment */ } -/* Peer dies? Remove hin ptr from payload so we know to ignore plugin return */ -static void invoice_payload_remove_hin(struct htlc_in *hin, +/* Set times out or HTLC deleted? Remove set ptr from payload so we + * know to ignore plugin return */ +static void invoice_payload_remove_set(struct htlc_set *set, struct invoice_payment_hook_payload *payload) { - assert(payload->hin == hin); - payload->hin = NULL; + assert(payload->set == set); + payload->set = NULL; } static bool hook_gives_failcode(const char *buffer, @@ -195,12 +196,12 @@ invoice_payment_hook_cb(struct invoice_payment_hook_payload *payload, * called even if the hook is not registered. */ notify_invoice_payment(ld, payload->msat, payload->preimage, payload->label); - tal_del_destructor2(payload->hin, invoice_payload_remove_hin, payload); + tal_del_destructor2(payload->set, invoice_payload_remove_set, payload); /* We want to free this, whatever happens. */ tal_steal(tmpctx, payload); /* If peer dies or something, this can happen. */ - if (!payload->hin) { + if (!payload->set) { log_debug(ld->log, "invoice '%s' paying htlc_in has gone!", payload->label->s); return; @@ -210,21 +211,22 @@ invoice_payment_hook_cb(struct invoice_payment_hook_payload *payload, * we can also fail */ if (!wallet_invoice_find_by_label(ld->wallet, &invoice, payload->label)) { failcode = WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS; - fail_htlc(payload->hin, failcode); + htlc_set_fail(payload->set, failcode); return; } /* Did we have a hook result? */ if (hook_gives_failcode(buffer, toks, &failcode)) { - fail_htlc(payload->hin, failcode); + htlc_set_fail(payload->set, failcode); return; } - log_info(ld->log, "Resolved invoice '%s' with amount %s", - payload->label->s, - type_to_string(tmpctx, struct amount_msat, &payload->msat)); + log_info(ld->log, "Resolved invoice '%s' with amount %s in %zu htlcs", + payload->label->s, + type_to_string(tmpctx, struct amount_msat, &payload->msat), + tal_count(payload->set->htlcs)); wallet_invoice_resolve(ld->wallet, invoice, payload->msat); - fulfill_htlc(payload->hin, &payload->preimage); + htlc_set_fulfill(payload->set, &payload->preimage); } REGISTER_PLUGIN_HOOK(invoice_payment, @@ -303,29 +305,18 @@ invoice_check_payment(const tal_t *ctx, } void invoice_try_pay(struct lightningd *ld, - struct htlc_in *hin, - const struct sha256 *payment_hash, - const struct amount_msat msat, - const struct secret *payment_secret) + struct htlc_set *set, + const struct invoice_details *details) { - const struct invoice_details *details; struct invoice_payment_hook_payload *payload; - details = invoice_check_payment(tmpctx, ld, payment_hash, msat, - payment_secret); - - if (!details) { - fail_htlc(hin, WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS); - return; - } - payload = tal(ld, struct invoice_payment_hook_payload); payload->ld = ld; payload->label = tal_steal(payload, details->label); - payload->msat = msat; + payload->msat = set->so_far; payload->preimage = details->r; - payload->hin = hin; - tal_add_destructor2(hin, invoice_payload_remove_hin, payload); + payload->set = set; + tal_add_destructor2(set, invoice_payload_remove_set, payload); plugin_hook_call_invoice_payment(ld, payload, payload); } diff --git a/lightningd/invoice.h b/lightningd/invoice.h index 199ab4b90..e50d87ca2 100644 --- a/lightningd/invoice.h +++ b/lightningd/invoice.h @@ -4,7 +4,7 @@ #include struct amount_msat; -struct htlc_in; +struct htlc_set; struct lightningd; struct sha256; @@ -27,19 +27,15 @@ invoice_check_payment(const tal_t *ctx, const struct secret *payment_secret); /** - * invoice_try_pay - process payment for this payment_hash, amount msat. + * invoice_try_pay - process payment for these incoming payments. * @ld: lightningd - * @hin: the input HTLC which is offering to pay. - * @payment_hash: hash of preimage they want. - * @msat: amount they offer to pay. - * @payment_secret: they payment secret they sent, if any. + * @set: the htlc_set used to pay this. + * @details: returned from successful invoice_check_payment. * - * Either calls fulfill_htlc() or fail_htlcs(). + * Either calls fulfill_htlc_set() or fail_htlc_set(). */ void invoice_try_pay(struct lightningd *ld, - struct htlc_in *hin, - const struct sha256 *payment_hash, - const struct amount_msat msat, - const struct secret *payment_secret); + struct htlc_set *set, + const struct invoice_details *details); #endif /* LIGHTNING_LIGHTNINGD_INVOICE_H */ diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 9bd2c4a9d..a43b9807e 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -299,18 +300,6 @@ static void handle_localpay(struct htlc_in *hin, goto fail; } - /* BOLT-9441a66faad63edc8cd89860b22fbf24a86f0dcd #4: - * - if it does not support `basic_mpp`: - * - MUST fail the HTLC if `total_msat` is not exactly equal to - * `amt_to_forward`. - */ - if (!amount_msat_eq(amt_to_forward, total_msat)) { - /* FIXME: Ideally, we use WIRE_INVALID_ONION_PAYLOAD and - * point at the total_msat field */ - failcode = WIRE_FINAL_INCORRECT_HTLC_AMOUNT; - goto fail; - } - /* BOLT #4: * * 1. type: 18 (`final_incorrect_cltv_expiry`) @@ -341,7 +330,7 @@ static void handle_localpay(struct htlc_in *hin, goto fail; } - invoice_try_pay(ld, hin, &hin->payment_hash, amt_to_forward, payment_secret); + htlc_set_add(ld, hin, total_msat, payment_secret); return; fail: diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 33e078df9..eab27f7d3 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -87,9 +87,6 @@ char *encode_scriptpubkey_to_addr(const tal_t *ctx UNNEEDED, const struct chainparams *chainparams UNNEEDED, const u8 *scriptPubkey UNNEEDED) { fprintf(stderr, "encode_scriptpubkey_to_addr 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(); } @@ -120,9 +117,6 @@ 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(); } @@ -136,6 +130,12 @@ bool htlc_is_trimmed(enum side htlc_owner UNNEEDED, struct amount_sat dust_limit UNNEEDED, enum side side UNNEEDED) { fprintf(stderr, "htlc_is_trimmed called!\n"); abort(); } +/* Generated stub for htlc_set_fail */ +void htlc_set_fail(struct htlc_set *set UNNEEDED, enum onion_type failcode UNNEEDED) +{ fprintf(stderr, "htlc_set_fail called!\n"); abort(); } +/* Generated stub for htlc_set_fulfill */ +void htlc_set_fulfill(struct htlc_set *set UNNEEDED, const struct preimage *preimage UNNEEDED) +{ fprintf(stderr, "htlc_set_fulfill 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) diff --git a/tests/test_pay.py b/tests/test_pay.py index f83e43318..3c5119f1a 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -2625,16 +2625,15 @@ def test_partial_payment(node_factory, bitcoind, executor): l2.daemon.wait_for_log(r'dev_disconnect') l3.daemon.wait_for_log(r'dev_disconnect') - # Now continue, payments will fail because receiver doesn't do MPP. + # Now continue, payments will succeed due to MPP. l2.rpc.dev_reenable_commit(l4.info['id']) l3.rpc.dev_reenable_commit(l4.info['id']) - # See FIXME in peer_htlcs: WIRE_INVALID_ONION_PAYLOAD would be better. - with pytest.raises(RpcError, match=r'WIRE_FINAL_INCORRECT_HTLC_AMOUNT'): - l1.rpc.call('waitsendpay', [inv['payment_hash'], None, 1]) - with pytest.raises(RpcError, match=r'WIRE_FINAL_INCORRECT_HTLC_AMOUNT'): - l1.rpc.call('waitsendpay', {'payment_hash': inv['payment_hash'], - 'partid': 2}) + res = l1.rpc.call('waitsendpay', [inv['payment_hash'], None, 1]) + assert res['partid'] == 1 + res = l1.rpc.call('waitsendpay', {'payment_hash': inv['payment_hash'], + 'partid': 2}) + assert res['partid'] == 2 for i in range(2): line = l4.daemon.wait_for_log('print_htlc_onion.py: Got onion') diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index ca2c5b785..98cc0b127 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -130,6 +130,12 @@ bool htlc_is_trimmed(enum side htlc_owner UNNEEDED, struct amount_sat dust_limit UNNEEDED, enum side side UNNEEDED) { fprintf(stderr, "htlc_is_trimmed called!\n"); abort(); } +/* Generated stub for htlc_set_add */ +void htlc_set_add(struct lightningd *ld UNNEEDED, + struct htlc_in *hin UNNEEDED, + struct amount_msat total_msat UNNEEDED, + const struct secret *payment_secret UNNEEDED) +{ fprintf(stderr, "htlc_set_add called!\n"); abort(); } /* Generated stub for invoices_create */ bool invoices_create(struct invoices *invoices UNNEEDED, struct invoice *pinvoice UNNEEDED, @@ -203,13 +209,6 @@ 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, - const struct secret *payment_secret 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)