From 9b4c6699f9a67493bb8f19d85a81f3592a902fa5 Mon Sep 17 00:00:00 2001 From: ZmnSCPxj Date: Tue, 20 Feb 2018 01:07:30 +0000 Subject: [PATCH] invoices: Semantically separate invoice details from invoice. In preparation for removing in-memory invoice structures. Invoice details are requested rarely anyway. --- lightningd/invoice.c | 42 ++++++++++------ lightningd/peer_htlcs.c | 17 +++++-- wallet/invoices.c | 100 ++++++++++++++++++++++++--------------- wallet/invoices.h | 14 ++++++ wallet/test/run-wallet.c | 6 +++ wallet/wallet.c | 8 +++- wallet/wallet.h | 51 ++++++++++++++------ 7 files changed, 166 insertions(+), 72 deletions(-) diff --git a/lightningd/invoice.c b/lightningd/invoice.c index c84161d4c..2cf380277 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -19,7 +19,7 @@ #include #include -static const char *invoice_status_str(const struct invoice *inv) +static const char *invoice_status_str(const struct invoice_details *inv) { if (inv->state == PAID) return "paid"; @@ -29,7 +29,7 @@ static const char *invoice_status_str(const struct invoice *inv) } static void json_add_invoice(struct json_result *response, - const struct invoice *inv, + const struct invoice_details *inv, bool modern) { json_object_start(response, NULL); @@ -60,9 +60,11 @@ static void json_add_invoice(struct json_result *response, static void tell_waiter(struct command *cmd, const struct invoice *inv) { struct json_result *response = new_json_result(cmd); + struct invoice_details details; - json_add_invoice(response, inv, true); - if (inv->state == PAID) + wallet_invoice_details(cmd, cmd->ld->wallet, inv, &details); + json_add_invoice(response, &details, true); + if (details.state == PAID) command_success(cmd, response); else command_fail_detailed(cmd, -2, response, @@ -103,6 +105,7 @@ static void json_invoice(struct command *cmd, const char *buffer, const jsmntok_t *params) { const struct invoice *invoice; + struct invoice_details details; jsmntok_t *msatoshi, *label, *desc, *exp, *fallback; u64 *msatoshi_val; const char *label_val; @@ -199,11 +202,14 @@ static void json_invoice(struct command *cmd, return; } + /* Get details */ + wallet_invoice_details(cmd, cmd->ld->wallet, invoice, &details); + /* Construct bolt11 string. */ - b11 = new_bolt11(cmd, invoice->msatoshi); + b11 = new_bolt11(cmd, details.msatoshi); b11->chain = get_chainparams(cmd->ld); b11->timestamp = time_now().ts.tv_sec; - b11->payment_hash = invoice->rhash; + b11->payment_hash = details.rhash; b11->receiver_id = cmd->ld->id; b11->min_final_cltv_expiry = cmd->ld->config.cltv_final; b11->expiry = expiry; @@ -217,10 +223,10 @@ static void json_invoice(struct command *cmd, json_object_start(response, NULL); json_add_hex(response, "payment_hash", - &invoice->rhash, sizeof(invoice->rhash)); + &details.rhash, sizeof(details.rhash)); if (deprecated_apis) - json_add_u64(response, "expiry_time", invoice->expiry_time); - json_add_u64(response, "expires_at", invoice->expiry_time); + json_add_u64(response, "expiry_time", details.expiry_time); + json_add_u64(response, "expires_at", details.expiry_time); json_add_string(response, "bolt11", b11enc); json_object_end(response); @@ -240,15 +246,17 @@ static void json_add_invoices(struct json_result *response, bool modern) { const struct invoice *i; + struct invoice_details details; char *lbl = NULL; if (label) lbl = tal_strndup(response, &buffer[label->start], label->end - label->start); i = NULL; while ((i = wallet_invoice_iterate(wallet, i)) != NULL) { - if (lbl && !streq(i->label, lbl)) + wallet_invoice_details(response, wallet, i, &details); + if (lbl && !streq(details.label, lbl)) continue; - json_add_invoice(response, i, modern); + json_add_invoice(response, &details, modern); } } @@ -311,6 +319,7 @@ static void json_delinvoice(struct command *cmd, const char *buffer, const jsmntok_t *params) { const struct invoice *i; + struct invoice_details details; jsmntok_t *labeltok, *statustok; struct json_result *response = new_json_result(cmd); const char *label, *status, *actual_status; @@ -330,12 +339,13 @@ static void json_delinvoice(struct command *cmd, command_fail(cmd, "Unknown invoice"); return; } + wallet_invoice_details(cmd, cmd->ld->wallet, i, &details); status = tal_strndup(cmd, buffer + statustok->start, statustok->end - statustok->start); /* This is time-sensitive, so only call once; otherwise error msg * might not make sense if it changed! */ - actual_status = invoice_status_str(i); + actual_status = invoice_status_str(&details); if (!streq(actual_status, status)) { command_fail(cmd, "Invoice status is %s not %s", actual_status, status); @@ -344,7 +354,7 @@ static void json_delinvoice(struct command *cmd, /* Get invoice details before attempting to delete, as * otherwise the invoice will be freed. */ - json_add_invoice(response, i, true); + json_add_invoice(response, &details, true); if (!wallet_invoice_delete(wallet, i)) { log_broken(cmd->ld->log, @@ -415,6 +425,7 @@ static void json_waitinvoice(struct command *cmd, const char *buffer, const jsmntok_t *params) { const struct invoice *i; + struct invoice_details details; struct wallet *wallet = cmd->ld->wallet; jsmntok_t *labeltok; const char *label = NULL; @@ -430,7 +441,10 @@ static void json_waitinvoice(struct command *cmd, if (!i) { command_fail(cmd, "Label not found"); return; - } else if (i->state == PAID || i->state == EXPIRED) { + } + wallet_invoice_details(cmd, cmd->ld->wallet, i, &details); + + if (details.state == PAID || details.state == EXPIRED) { tell_waiter(cmd, i); return; } else { diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index ef5b64dbb..a617421d4 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -230,7 +230,9 @@ static void handle_localpay(struct htlc_in *hin, { enum onion_type failcode; const struct invoice *invoice; + struct invoice_details details; struct lightningd *ld = hin->key.channel->peer->ld; + const tal_t *tmpctx = tal_tmpctx(ld); /* BOLT #4: * @@ -265,6 +267,7 @@ static void handle_localpay(struct htlc_in *hin, failcode = WIRE_UNKNOWN_PAYMENT_HASH; goto fail; } + wallet_invoice_details(tmpctx, ld->wallet, invoice, &details); /* BOLT #4: * @@ -276,10 +279,10 @@ static void handle_localpay(struct htlc_in *hin, * * 1. type: PERM|16 (`incorrect_payment_amount`) */ - if (invoice->msatoshi != NULL && hin->msatoshi < *invoice->msatoshi) { + if (details.msatoshi != NULL && hin->msatoshi < *details.msatoshi) { failcode = WIRE_INCORRECT_PAYMENT_AMOUNT; goto fail; - } else if (invoice->msatoshi != NULL && hin->msatoshi > *invoice->msatoshi * 2) { + } else if (details.msatoshi != NULL && hin->msatoshi > *details.msatoshi * 2) { failcode = WIRE_INCORRECT_PAYMENT_AMOUNT; goto fail; } @@ -300,17 +303,21 @@ static void handle_localpay(struct htlc_in *hin, } log_info(ld->log, "Resolving invoice '%s' with HTLC %"PRIu64, - invoice->label, hin->key.id); + details.label, hin->key.id); log_debug(ld->log, "%s: Actual amount %"PRIu64"msat, HTLC expiry %u", - invoice->label, hin->msatoshi, cltv_expiry); - fulfill_htlc(hin, &invoice->r); + details.label, hin->msatoshi, cltv_expiry); + fulfill_htlc(hin, &details.r); wallet_invoice_resolve(ld->wallet, invoice, hin->msatoshi); + + tal_free(tmpctx); return; fail: /* Final hop never sends an UPDATE. */ assert(!(failcode & UPDATE)); local_fail_htlc(hin, failcode, NULL); + + tal_free(tmpctx); } /* diff --git a/wallet/invoices.c b/wallet/invoices.c index 2e6da6bf8..92e9c3ced 100644 --- a/wallet/invoices.c +++ b/wallet/invoices.c @@ -42,36 +42,38 @@ static void trigger_invoice_waiter(struct invoice_waiter *w, w->cb(invoice, w->cbarg); } -static bool wallet_stmt2invoice(sqlite3_stmt *stmt, struct invoice *inv) +static bool wallet_stmt2invoice_details(sqlite3_stmt *stmt, + struct invoice *invoice, + struct invoice_details *dtl) { - inv->id = sqlite3_column_int64(stmt, 0); - inv->state = sqlite3_column_int(stmt, 1); + invoice->id = sqlite3_column_int64(stmt, 0); + dtl->state = sqlite3_column_int(stmt, 1); assert(sqlite3_column_bytes(stmt, 2) == sizeof(struct preimage)); - memcpy(&inv->r, sqlite3_column_blob(stmt, 2), sqlite3_column_bytes(stmt, 2)); + memcpy(&dtl->r, sqlite3_column_blob(stmt, 2), sqlite3_column_bytes(stmt, 2)); assert(sqlite3_column_bytes(stmt, 3) == sizeof(struct sha256)); - memcpy(&inv->rhash, sqlite3_column_blob(stmt, 3), sqlite3_column_bytes(stmt, 3)); + memcpy(&dtl->rhash, sqlite3_column_blob(stmt, 3), sqlite3_column_bytes(stmt, 3)); - inv->label = tal_strndup(inv, sqlite3_column_blob(stmt, 4), sqlite3_column_bytes(stmt, 4)); + dtl->label = tal_strndup(dtl, sqlite3_column_blob(stmt, 4), sqlite3_column_bytes(stmt, 4)); if (sqlite3_column_type(stmt, 5) != SQLITE_NULL) { - inv->msatoshi = tal(inv, u64); - *inv->msatoshi = sqlite3_column_int64(stmt, 5); + dtl->msatoshi = tal(dtl, u64); + *dtl->msatoshi = sqlite3_column_int64(stmt, 5); } else { - inv->msatoshi = NULL; + dtl->msatoshi = NULL; } - inv->expiry_time = sqlite3_column_int64(stmt, 6); + dtl->expiry_time = sqlite3_column_int64(stmt, 6); - if (inv->state == PAID) { - inv->pay_index = sqlite3_column_int64(stmt, 7); - inv->msatoshi_received = sqlite3_column_int64(stmt, 8); - inv->paid_timestamp = sqlite3_column_int64(stmt, 9); + if (dtl->state == PAID) { + dtl->pay_index = sqlite3_column_int64(stmt, 7); + dtl->msatoshi_received = sqlite3_column_int64(stmt, 8); + dtl->paid_timestamp = sqlite3_column_int64(stmt, 9); } - list_head_init(&inv->waitone_waiters); - inv->expiration_timer = NULL; + list_head_init(&invoice->waitone_waiters); + invoice->expiration_timer = NULL; return true; } @@ -102,7 +104,7 @@ static void trigger_expiration(struct invoice *i) sqlite3_stmt *stmt; struct invoice_waiter *w; - assert(i->state == UNPAID); + assert(i->details->state == UNPAID); /* Timer already triggered, destroy the timer object. */ i->expiration_timer = tal_free(i->expiration_timer); @@ -111,11 +113,11 @@ static void trigger_expiration(struct invoice *i) * (used by the timer system) and time_now (used * by the expiry time measurements). So check that * time_now is reached. */ - if (i->expiry_time <= now) { + if (i->details->expiry_time <= now) { const tal_t *tmpctx = tal_tmpctx(i); /* Update in-memory and db. */ - i->state = EXPIRED; + i->details->state = EXPIRED; stmt = db_prepare(invoices->db, "UPDATE invoices" " SET state = ?" @@ -143,11 +145,11 @@ static void install_expiration_timer(struct invoices *invoices, struct timeabs expiry; struct timeabs now = time_now(); - assert(i->state == UNPAID); + assert(i->details->state == UNPAID); assert(!i->expiration_timer); memset(&expiry, 0, sizeof(expiry)); - expiry.ts.tv_sec = i->expiry_time; + expiry.ts.tv_sec = i->details->expiry_time; /* now > expiry */ if (time_after(now, expiry)) @@ -197,12 +199,13 @@ bool invoices_load(struct invoices *invoices) while (sqlite3_step(stmt) == SQLITE_ROW) { i = tal(invoices, struct invoice); i->owner = invoices; - if (!wallet_stmt2invoice(stmt, i)) { + i->details = tal(i, struct invoice_details); + if (!wallet_stmt2invoice_details(stmt, i, i->details)) { log_broken(invoices->log, "Error deserializing invoice"); sqlite3_finalize(stmt); return false; } - if (i->state == UNPAID) + if (i->details->state == UNPAID) install_expiration_timer(invoices, i); list_add_tail(&invoices->invlist, &i->list); count++; @@ -272,12 +275,13 @@ const struct invoice *invoices_create(struct invoices *invoices, invoice->owner = invoices; invoice->id = sqlite3_last_insert_rowid(invoices->db->sql); - invoice->state = UNPAID; - invoice->label = tal_strdup(invoice, label); - invoice->msatoshi = tal_dup(invoice, u64, msatoshi); /* Works even if msatoshi == NULL. */ - memcpy(&invoice->r, &r, sizeof(invoice->r)); - memcpy(&invoice->rhash, &rhash, sizeof(invoice->rhash)); - invoice->expiry_time = expiry_time; + invoice->details = tal(invoice, struct invoice_details); + invoice->details->state = UNPAID; + invoice->details->label = tal_strdup(invoice->details, label); + invoice->details->msatoshi = tal_dup(invoice->details, u64, msatoshi); /* Works even if msatoshi == NULL. */ + memcpy(&invoice->details->r, &r, sizeof(invoice->details->r)); + memcpy(&invoice->details->rhash, &rhash, sizeof(invoice->details->rhash)); + invoice->details->expiry_time = expiry_time; list_head_init(&invoice->waitone_waiters); invoice->expiration_timer = NULL; @@ -298,7 +302,7 @@ const struct invoice *invoices_find_by_label(struct invoices *invoices, /* FIXME: Use something better than a linear scan. */ list_for_each(&invoices->invlist, i, list) { - if (streq(i->label, label)) + if (streq(i->details->label, label)) return i; } return NULL; @@ -310,8 +314,9 @@ const struct invoice *invoices_find_unpaid(struct invoices *invoices, struct invoice *i; list_for_each(&invoices->invlist, i, list) { - if (structeq(rhash, &i->rhash) && i->state == UNPAID) { - if (time_now().ts.tv_sec > i->expiry_time) + if (structeq(rhash, &i->details->rhash) && + i->details->state == UNPAID) { + if (time_now().ts.tv_sec > i->details->expiry_time) break; return i; } @@ -407,10 +412,10 @@ void invoices_resolve(struct invoices *invoices, db_exec_prepared(invoices->db, stmt); /* Update in-memory structure. */ - invoice->state = PAID; - invoice->pay_index = pay_index; - invoice->msatoshi_received = msatoshi_received; - invoice->paid_timestamp = paid_timestamp; + invoice->details->state = PAID; + invoice->details->pay_index = pay_index; + invoice->details->msatoshi_received = msatoshi_received; + invoice->details->paid_timestamp = paid_timestamp; invoice->expiration_timer = tal_free(invoice->expiration_timer); /* Tell all the waitany waiters about the new paid invoice. */ @@ -506,7 +511,7 @@ void invoices_waitone(const tal_t *ctx, void *cbarg) { struct invoice *invoice = (struct invoice*) cinvoice; - if (invoice->state == PAID || invoice->state == EXPIRED) { + if (invoice->details->state == PAID || invoice->details->state == EXPIRED) { cb(invoice, cbarg); return; } @@ -514,3 +519,24 @@ void invoices_waitone(const tal_t *ctx, /* Not yet paid. */ add_invoice_waiter(ctx, &invoice->waitone_waiters, cb, cbarg); } + +void invoices_get_details(const tal_t *ctx, + struct invoices *invoices, + const struct invoice *invoice, + struct invoice_details *dtl) +{ + dtl->state = invoice->details->state; + dtl->r = invoice->details->r; + dtl->rhash = invoice->details->rhash; + dtl->label = tal_strdup(ctx, invoice->details->label); + dtl->msatoshi = + invoice->details->msatoshi ? + tal_dup(ctx, u64, invoice->details->msatoshi) : + /*otherwise*/ NULL ; + dtl->expiry_time = invoice->details->expiry_time; + if (dtl->state == PAID) { + dtl->pay_index = invoice->details->pay_index; + dtl->msatoshi_received = invoice->details->msatoshi_received; + dtl->paid_timestamp = invoice->details->paid_timestamp; + } +} diff --git a/wallet/invoices.h b/wallet/invoices.h index b9ca50e8d..de46ed6b4 100644 --- a/wallet/invoices.h +++ b/wallet/invoices.h @@ -7,6 +7,7 @@ struct db; struct invoice; +struct invoice_details; struct invoices; struct log; struct sha256; @@ -159,4 +160,17 @@ void invoices_waitone(const tal_t *ctx, void (*cb)(const struct invoice *, void*), void *cbarg); +/** + * invoices_get_details - Get the invoice_details of an invoice. + * + * @ctx - the owner of the label and msatoshi fields returned. + * @invoices - the invoice handler, + * @invoice - the invoice to get details on. + * @details - pointer to details object to load. + */ +void invoices_get_details(const tal_t *ctx, + struct invoices *invoices, + const struct invoice *invoice, + struct invoice_details *details); + #endif /* LIGHTNING_WALLET_INVOICES_H */ diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 16099d9bd..097ff3cdc 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -112,6 +112,12 @@ const struct invoice *invoices_find_by_label(struct invoices *invoices UNNEEDED, const struct invoice *invoices_find_unpaid(struct invoices *invoices UNNEEDED, const struct sha256 *rhash UNNEEDED) { fprintf(stderr, "invoices_find_unpaid called!\n"); abort(); } +/* Generated stub for invoices_get_details */ +void invoices_get_details(const tal_t *ctx UNNEEDED, + struct invoices *invoices UNNEEDED, + const struct invoice *invoice UNNEEDED, + struct invoice_details *details UNNEEDED) +{ fprintf(stderr, "invoices_get_details called!\n"); abort(); } /* Generated stub for invoices_iterate */ const struct invoice *invoices_iterate(struct invoices *invoices UNNEEDED, const struct invoice *invoice UNNEEDED) diff --git a/wallet/wallet.c b/wallet/wallet.c index 3b6684187..2c02242b0 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1314,7 +1314,13 @@ void wallet_invoice_waitone(const tal_t *ctx, { invoices_waitone(ctx, wallet->invoices, invoice, cb, cbarg); } - +void wallet_invoice_details(const tal_t *ctx, + struct wallet *wallet, + const struct invoice *invoice, + struct invoice_details *details) +{ + invoices_get_details(ctx, wallet->invoices, invoice, details); +} struct htlc_stub *wallet_htlc_stubs(const tal_t *ctx, struct wallet *wallet, diff --git a/wallet/wallet.h b/wallet/wallet.h index 21fb2c909..d01838ef0 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -368,6 +368,28 @@ enum invoice_status { EXPIRED, }; +/* The information about an invoice */ +struct invoice_details { + /* Current invoice state */ + enum invoice_status state; + /* Preimage for this invoice */ + struct preimage r; + /* Hash of preimage r */ + struct sha256 rhash; + /* Label assigned by user */ + const char *label; + /* NULL if they specified "any" */ + u64 *msatoshi; + /* Absolute UNIX epoch time this will expire */ + u64 expiry_time; + /* Set if state == PAID; order to be returned by waitanyinvoice */ + u64 pay_index; + /* Set if state == PAID; amount received */ + u64 msatoshi_received; + /* Set if state == PAID; time paid */ + u64 paid_timestamp; +}; + struct invoice { /* Internal, rest of lightningd should not use */ /* List off ld->wallet->invoices. Must be first or else @@ -381,21 +403,8 @@ struct invoice { struct oneshot *expiration_timer; /* The owning invoices object. */ struct invoices *owner; - - /* Publicly-usable fields. */ - enum invoice_status state; - const char *label; - /* NULL if they specified "any" */ - u64 *msatoshi; - /* Set if state == PAID */ - u64 msatoshi_received; - /* Set if state == PAID */ - u64 paid_timestamp; - struct preimage r; - u64 expiry_time; - struct sha256 rhash; - /* Set if state == PAID */ - u64 pay_index; + /* Loaded details. */ + struct invoice_details *details; }; #define INVOICE_MAX_LABEL_LEN 128 @@ -540,6 +549,18 @@ void wallet_invoice_waitone(const tal_t *ctx, void (*cb)(const struct invoice *, void*), void *cbarg); +/** + * wallet_invoice_details - Get the invoice_details of an invoice. + * + * @ctx - the owner of the label and msatoshi fields returned. + * @wallet - the wallet to query. + * @invoice - the invoice to get details on. + * @details - pointer to details object to load. + */ +void wallet_invoice_details(const tal_t *ctx, + struct wallet *wallet, + const struct invoice *invoice, + struct invoice_details *details); /** * wallet_htlc_stubs - Retrieve HTLC stubs for the given channel