Browse Source

invoice: fix potential race where invoice is paid/expired while we're calling hook.

There's actually a (very unlikely) race here: we would previously have
crashed with an assertion in invoices_resolve.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
fix-mocks
Rusty Russell 4 years ago
parent
commit
1a3c0a0c0d
  1. 8
      lightningd/invoice.c
  2. 2
      lightningd/test/run-invoice-select-inchan.c
  3. 2
      wallet/db_postgres_sqlgen.c
  4. 2
      wallet/db_sqlite3_sqlgen.c
  5. 6
      wallet/invoices.c
  6. 5
      wallet/invoices.h
  7. 8
      wallet/statements_gettextgen.po
  8. 2
      wallet/test/run-wallet.c
  9. 4
      wallet/wallet.c
  10. 5
      wallet/wallet.h

8
lightningd/invoice.c

@ -250,11 +250,17 @@ invoice_payment_hooks_done(struct invoice_payment_hook_payload *payload STEALS)
return; return;
} }
/* Paid or expired in the meantime. */
if (!wallet_invoice_resolve(ld->wallet, invoice, payload->msat)) {
htlc_set_fail(payload->set, take(failmsg_incorrect_or_unknown(
NULL, ld, payload->set->htlcs[0])));
return;
}
log_info(ld->log, "Resolved invoice '%s' with amount %s in %zu htlcs", log_info(ld->log, "Resolved invoice '%s' with amount %s in %zu htlcs",
payload->label->s, payload->label->s,
type_to_string(tmpctx, struct amount_msat, &payload->msat), type_to_string(tmpctx, struct amount_msat, &payload->msat),
tal_count(payload->set->htlcs)); tal_count(payload->set->htlcs));
wallet_invoice_resolve(ld->wallet, invoice, payload->msat);
htlc_set_fulfill(payload->set, &payload->preimage); htlc_set_fulfill(payload->set, &payload->preimage);
} }

2
lightningd/test/run-invoice-select-inchan.c

@ -643,7 +643,7 @@ const struct invoice_details *wallet_invoice_iterator_deref(const tal_t *ctx UNN
const struct invoice_iterator *it UNNEEDED) const struct invoice_iterator *it UNNEEDED)
{ fprintf(stderr, "wallet_invoice_iterator_deref called!\n"); abort(); } { fprintf(stderr, "wallet_invoice_iterator_deref called!\n"); abort(); }
/* Generated stub for wallet_invoice_resolve */ /* Generated stub for wallet_invoice_resolve */
void wallet_invoice_resolve(struct wallet *wallet UNNEEDED, bool wallet_invoice_resolve(struct wallet *wallet UNNEEDED,
struct invoice invoice UNNEEDED, struct invoice invoice UNNEEDED,
struct amount_msat received UNNEEDED) struct amount_msat received UNNEEDED)
{ fprintf(stderr, "wallet_invoice_resolve called!\n"); abort(); } { fprintf(stderr, "wallet_invoice_resolve called!\n"); abort(); }

2
wallet/db_postgres_sqlgen.c

@ -1690,4 +1690,4 @@ struct db_query db_postgres_queries[] = {
#endif /* LIGHTNINGD_WALLET_GEN_DB_POSTGRES */ #endif /* LIGHTNINGD_WALLET_GEN_DB_POSTGRES */
// SHA256STAMP:a61b8b6ea86287e22c696530a9b5cd35ccef271280b8ac34d2665dc5aff42fce // SHA256STAMP:fac2fa6846e0deadf6f24fc9deb6efb0bac04b5947c254ae39300663366178af

2
wallet/db_sqlite3_sqlgen.c

@ -1690,4 +1690,4 @@ struct db_query db_sqlite3_queries[] = {
#endif /* LIGHTNINGD_WALLET_GEN_DB_SQLITE3 */ #endif /* LIGHTNINGD_WALLET_GEN_DB_SQLITE3 */
// SHA256STAMP:a61b8b6ea86287e22c696530a9b5cd35ccef271280b8ac34d2665dc5aff42fce // SHA256STAMP:fac2fa6846e0deadf6f24fc9deb6efb0bac04b5947c254ae39300663366178af

6
wallet/invoices.c

@ -496,7 +496,7 @@ static enum invoice_status invoice_get_status(struct invoices *invoices, struct
return state; return state;
} }
void invoices_resolve(struct invoices *invoices, bool invoices_resolve(struct invoices *invoices,
struct invoice invoice, struct invoice invoice,
struct amount_msat received) struct amount_msat received)
{ {
@ -505,7 +505,8 @@ void invoices_resolve(struct invoices *invoices,
u64 paid_timestamp; u64 paid_timestamp;
enum invoice_status state = invoice_get_status(invoices, invoice); enum invoice_status state = invoice_get_status(invoices, invoice);
assert(state == UNPAID); if (state != UNPAID)
return false;
/* Assign a pay-index. */ /* Assign a pay-index. */
pay_index = get_next_pay_index(invoices->db); pay_index = get_next_pay_index(invoices->db);
@ -527,6 +528,7 @@ void invoices_resolve(struct invoices *invoices,
/* Tell all the waiters about the paid invoice. */ /* Tell all the waiters about the paid invoice. */
trigger_invoice_waiter_resolve(invoices, invoice.id, &invoice); trigger_invoice_waiter_resolve(invoices, invoice.id, &invoice);
return true;
} }
/* Called when an invoice waiter is destructed. */ /* Called when an invoice waiter is destructed. */

5
wallet/invoices.h

@ -174,10 +174,9 @@ const struct invoice_details *invoices_iterator_deref(
* @invoice - the invoice to mark as paid. * @invoice - the invoice to mark as paid.
* @received - the actual amount received. * @received - the actual amount received.
* *
* Precondition: the invoice must not yet be expired (invoices * If the invoice is not UNPAID, returns false.
* does not check).
*/ */
void invoices_resolve(struct invoices *invoices, bool invoices_resolve(struct invoices *invoices,
struct invoice invoice, struct invoice invoice,
struct amount_msat received); struct amount_msat received);

8
wallet/statements_gettextgen.po

@ -666,15 +666,15 @@ msgstr ""
msgid "SELECT state FROM invoices WHERE id = ?;" msgid "SELECT state FROM invoices WHERE id = ?;"
msgstr "" msgstr ""
#: wallet/invoices.c:515 #: wallet/invoices.c:516
msgid "UPDATE invoices SET state=? , pay_index=? , msatoshi_received=? , paid_timestamp=? WHERE id=?;" msgid "UPDATE invoices SET state=? , pay_index=? , msatoshi_received=? , paid_timestamp=? WHERE id=?;"
msgstr "" msgstr ""
#: wallet/invoices.c:571 #: wallet/invoices.c:573
msgid "SELECT id FROM invoices WHERE pay_index IS NOT NULL AND pay_index > ? ORDER BY pay_index ASC LIMIT 1;" msgid "SELECT id FROM invoices WHERE pay_index IS NOT NULL AND pay_index > ? ORDER BY pay_index ASC LIMIT 1;"
msgstr "" msgstr ""
#: wallet/invoices.c:620 #: wallet/invoices.c:622
msgid "SELECT state, payment_key, payment_hash, label, msatoshi, expiry_time, pay_index, msatoshi_received, paid_timestamp, bolt11, description, features FROM invoices WHERE id = ?;" msgid "SELECT state, payment_key, payment_hash, label, msatoshi, expiry_time, pay_index, msatoshi_received, paid_timestamp, bolt11, description, features FROM invoices WHERE id = ?;"
msgstr "" msgstr ""
@ -1113,4 +1113,4 @@ msgstr ""
#: wallet/test/run-wallet.c:1376 #: wallet/test/run-wallet.c:1376
msgid "INSERT INTO channels (id) VALUES (1);" msgid "INSERT INTO channels (id) VALUES (1);"
msgstr "" msgstr ""
# SHA256STAMP:a63bd31a3977b161d8245ffddf5de3a03c972a60436eb8cbc1bb23f0ffb03405 # SHA256STAMP:8f226711a58166b481aaa7b8c0593c7159711bee623737e3b4750f6c154d4f65

2
wallet/test/run-wallet.c

@ -220,7 +220,7 @@ struct invoices *invoices_new(const tal_t *ctx UNNEEDED,
struct timers *timers UNNEEDED) struct timers *timers UNNEEDED)
{ fprintf(stderr, "invoices_new called!\n"); abort(); } { fprintf(stderr, "invoices_new called!\n"); abort(); }
/* Generated stub for invoices_resolve */ /* Generated stub for invoices_resolve */
void invoices_resolve(struct invoices *invoices UNNEEDED, bool invoices_resolve(struct invoices *invoices UNNEEDED,
struct invoice invoice UNNEEDED, struct invoice invoice UNNEEDED,
struct amount_msat received UNNEEDED) struct amount_msat received UNNEEDED)
{ fprintf(stderr, "invoices_resolve called!\n"); abort(); } { fprintf(stderr, "invoices_resolve called!\n"); abort(); }

4
wallet/wallet.c

@ -2337,11 +2337,11 @@ wallet_invoice_iterator_deref(const tal_t *ctx, struct wallet *wallet,
{ {
return invoices_iterator_deref(ctx, wallet->invoices, it); return invoices_iterator_deref(ctx, wallet->invoices, it);
} }
void wallet_invoice_resolve(struct wallet *wallet, bool wallet_invoice_resolve(struct wallet *wallet,
struct invoice invoice, struct invoice invoice,
struct amount_msat msatoshi_received) struct amount_msat msatoshi_received)
{ {
invoices_resolve(wallet->invoices, invoice, msatoshi_received); return invoices_resolve(wallet->invoices, invoice, msatoshi_received);
} }
void wallet_invoice_waitany(const tal_t *ctx, void wallet_invoice_waitany(const tal_t *ctx,
struct wallet *wallet, struct wallet *wallet,

5
wallet/wallet.h

@ -883,10 +883,9 @@ const struct invoice_details *wallet_invoice_iterator_deref(const tal_t *ctx,
* @invoice - the invoice to mark as paid. * @invoice - the invoice to mark as paid.
* @received - the actual amount received. * @received - the actual amount received.
* *
* Precondition: the invoice must not yet be expired (wallet * If the invoice is not UNPAID, returns false.
* does not check!).
*/ */
void wallet_invoice_resolve(struct wallet *wallet, bool wallet_invoice_resolve(struct wallet *wallet,
struct invoice invoice, struct invoice invoice,
struct amount_msat received); struct amount_msat received);

Loading…
Cancel
Save