From 5eceaa7be9ed27a77a97adef200d9b112fe5cdc0 Mon Sep 17 00:00:00 2001 From: ZmnSCPxj Date: Tue, 26 Dec 2017 22:58:42 +0000 Subject: [PATCH] invoice: Modify waitanyinvoice interface to use pay_index. --- doc/lightning-waitanyinvoice.7 | 14 ++++---- doc/lightning-waitanyinvoice.7.txt | 14 +++++--- lightningd/invoice.c | 50 ++++++++++++++------------- tests/test_lightningd.py | 11 +++--- wallet/wallet.c | 54 ++++++++++-------------------- wallet/wallet.h | 31 ++++++++--------- 6 files changed, 86 insertions(+), 88 deletions(-) diff --git a/doc/lightning-waitanyinvoice.7 b/doc/lightning-waitanyinvoice.7 index 411a7c9db..bd0c59d36 100644 --- a/doc/lightning-waitanyinvoice.7 +++ b/doc/lightning-waitanyinvoice.7 @@ -2,12 +2,12 @@ .\" Title: lightning-waitanyinvoice .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 -.\" Date: 01/23/2017 +.\" Date: 12/26/2017 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "LIGHTNING\-WAITANYIN" "7" "01/23/2017" "\ \&" "\ \&" +.TH "LIGHTNING\-WAITANYIN" "7" "12/26/2017" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -31,15 +31,17 @@ lightning-waitanyinvoice \- Protocol for waiting for payments\&. .SH "SYNOPSIS" .sp -\fBwaitanyinvoice\fR [\fIlastlabel\fR] +\fBwaitanyinvoice\fR [\fIlastpay_index\fR] .SH "DESCRIPTION" .sp -The \fBwaitanyinvoice\fR RPC command waits until an invoice is paid, then returns a single entry as per \fBlistinvoice\fR\&. It will not return for any invoices paid prior to or including \fIlastlabel\fR\&. +The \fBwaitanyinvoice\fR RPC command waits until an invoice is paid, then returns a single entry as per \fBlistinvoice\fR\&. It will not return for any invoices paid prior to or including the \fIlastpay_index\fR\&. .sp -This is usually called iteratively: once with no arguments, then repeatedly with the returned \fIlabel\fR entry\&. This ensures that no paid invoice is missed\&. +This is usually called iteratively: once with no arguments, then repeatedly with the returned \fIpay_index\fR entry\&. This ensures that no paid invoice is missed\&. +.sp +The \fIpay_index\fR is a monotonically\-increasing number assigned to an invoice when it gets paid\&. The first valid \fIpay_index\fR is 1; specifying \fIlastpay_index\fR of 0 equivalent to not specifying a \fIlastpay_index\fR\&. Negative \fIlastpay_index\fR is invalid\&. .SH "RETURN VALUE" .sp -On success, the \fIrhash\fR, \fIlabel\fR, and \fImsatoshi\fR will be returned\&. +On success, the \fIrhash\fR, \fIlabel\fR, \fIpay_index\fR, and \fImsatoshi\fR will be returned\&. .SH "AUTHOR" .sp Rusty Russell is mainly responsible\&. diff --git a/doc/lightning-waitanyinvoice.7.txt b/doc/lightning-waitanyinvoice.7.txt index cb33443a0..df80792cf 100644 --- a/doc/lightning-waitanyinvoice.7.txt +++ b/doc/lightning-waitanyinvoice.7.txt @@ -8,21 +8,27 @@ lightning-waitanyinvoice - Protocol for waiting for payments. SYNOPSIS -------- -*waitanyinvoice* ['lastlabel'] +*waitanyinvoice* ['lastpay_index'] DESCRIPTION ----------- The *waitanyinvoice* RPC command waits until an invoice is paid, then returns a single entry as per *listinvoice*. It will not return for -any invoices paid prior to or including 'lastlabel'. +any invoices paid prior to or including the 'lastpay_index'. This is usually called iteratively: once with no arguments, then -repeatedly with the returned 'label' entry. This ensures that no paid +repeatedly with the returned 'pay_index' entry. This ensures that no paid invoice is missed. +The 'pay_index' is a monotonically-increasing number assigned to an +invoice when it gets paid. The first valid 'pay_index' is 1; specifying +'lastpay_index' of 0 equivalent to not specifying a 'lastpay_index'. +Negative 'lastpay_index' is invalid. + + RETURN VALUE ------------ -On success, the 'rhash', 'label', and 'msatoshi' will be returned. +On success, the 'rhash', 'label', 'pay_index', and 'msatoshi' will be returned. //FIXME:Enumerate errors diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 1a8991fc9..ba42ae145 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -83,6 +83,8 @@ static void tell_waiter(struct command *cmd, const struct invoice *paid) json_add_hex(response, "rhash", &paid->rhash, sizeof(paid->rhash)); json_add_u64(response, "msatoshi", paid->msatoshi); json_add_bool(response, "complete", paid->state == PAID); + if (paid->state == PAID) + json_add_u64(response, "pay_index", paid->pay_index); json_object_end(response); command_success(cmd, response); } @@ -98,7 +100,11 @@ void resolve_invoice(struct lightningd *ld, struct invoice *invoice) invoice->state = PAID; - /* Tell all the waitanyinvoice waiters about the new paid invoice */ + /* wallet_invoice_save updates pay_index member, + * which tell_waiter needs. */ + wallet_invoice_save(ld->wallet, invoice); + + /* Tell all the waiters about the new paid invoice */ while ((w = list_pop(&invs->invoice_waiters, struct invoice_waiter, list)) != NULL) @@ -109,8 +115,6 @@ void resolve_invoice(struct lightningd *ld, struct invoice *invoice) list)) != NULL) tell_waiter(w->cmd, invoice); - wallet_invoice_save(ld->wallet, invoice); - /* Also mark the payment in the history table as complete */ wallet_payment_set_status(ld->wallet, &invoice->rhash, PAYMENT_COMPLETE); } @@ -389,44 +393,43 @@ AUTODATA(json_command, &delinvoice_command); static void json_waitanyinvoice(struct command *cmd, const char *buffer, const jsmntok_t *params) { - jsmntok_t *labeltok; - const char *label = NULL; + jsmntok_t *pay_indextok; + u64 pay_index; struct invoice_waiter *w; struct invoices *invs = cmd->ld->invoices; - int res; + bool res; struct wallet *wallet = cmd->ld->wallet; char* outlabel; struct sha256 outrhash; u64 outmsatoshi; + u64 outpay_index; struct json_result *response; if (!json_get_params(buffer, params, - "?label", &labeltok, + "?lastpay_index", &pay_indextok, NULL)) { command_fail(cmd, "Invalid arguments"); return; } - if (!labeltok) { - label = NULL; + if (!pay_indextok) { + pay_index = 0; } else { - label = tal_strndup(cmd, buffer + labeltok->start, - labeltok->end - labeltok->start); + if (!json_tok_u64(buffer, pay_indextok, &pay_index)) { + command_fail(cmd, "'%.*s' is not a valid number", + pay_indextok->end - pay_indextok->start, + buffer + pay_indextok->start); + return; + } } /* Find next paid invoice. */ - res = wallet_invoice_nextpaid(cmd, wallet, label, - &outlabel, &outrhash, &outmsatoshi); - label = tal_free(label); - - /* If we failed to find label, error it. */ - if (res == -1) { - command_fail(cmd, "Label not found"); - return; - } + res = wallet_invoice_nextpaid(cmd, wallet, pay_index, + &outlabel, &outrhash, + &outmsatoshi, &outpay_index); /* If we found one, return it. */ - if (res == 1) { + if (res) { response = new_json_result(cmd); json_object_start(response, NULL); @@ -434,6 +437,7 @@ static void json_waitanyinvoice(struct command *cmd, json_add_hex(response, "rhash", &outrhash, sizeof(outrhash)); json_add_u64(response, "msatoshi", outmsatoshi); json_add_bool(response, "complete", true); + json_add_u64(response, "pay_index", outpay_index); json_object_end(response); command_success(cmd, response); @@ -454,8 +458,8 @@ static void json_waitanyinvoice(struct command *cmd, static const struct json_command waitanyinvoice_command = { "waitanyinvoice", json_waitanyinvoice, - "Wait for the next invoice to be paid, after {label} (if supplied)))", - "Returns {label}, {rhash} and {msatoshi} on success. " + "Wait for the next invoice to be paid, after {lastpay_index} (if supplied)))", + "Returns {label}, {rhash}, {msatoshi}, and {pay_index} on success. " }; AUTODATA(json_command, &waitanyinvoice_command); diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index 6b1c1c194..ffe08c694 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -2435,20 +2435,22 @@ class LightningDTests(BaseLightningDTests): l1.rpc.pay(inv2['bolt11']) r = f.result(timeout=5) assert r['label'] == 'inv1' + pay_index = r['pay_index'] # This one should return immediately with inv2 - r = self.executor.submit(l2.rpc.waitanyinvoice, 'inv1').result(timeout=5) + r = self.executor.submit(l2.rpc.waitanyinvoice, pay_index).result(timeout=5) assert r['label'] == 'inv2' + pay_index = r['pay_index'] # Now spawn the next waiter - f = self.executor.submit(l2.rpc.waitanyinvoice, 'inv2') + f = self.executor.submit(l2.rpc.waitanyinvoice, pay_index) time.sleep(1) assert not f.done() l1.rpc.pay(inv3['bolt11']) r = f.result(timeout=5) assert r['label'] == 'inv3' - self.assertRaises(ValueError, l2.rpc.waitanyinvoice, 'doesntexist') + self.assertRaises(ValueError, l2.rpc.waitanyinvoice, 'non-number') def test_waitanyinvoice_reversed(self): @@ -2471,10 +2473,11 @@ class LightningDTests(BaseLightningDTests): # Wait - should not block, should return inv2 r = self.executor.submit(l2.rpc.waitanyinvoice).result(timeout=5) assert r['label'] == 'inv2' + pay_index = r['pay_index'] # Pay inv1 l1.rpc.pay(inv1['bolt11']) # Wait inv2 - should not block, should return inv1 - r = self.executor.submit(l2.rpc.waitanyinvoice, 'inv2').result(timeout=5) + r = self.executor.submit(l2.rpc.waitanyinvoice, pay_index).result(timeout=5) assert r['label'] == 'inv1' def test_channel_reenable(self): diff --git a/wallet/wallet.c b/wallet/wallet.c index 7060c95b4..ab8a44972 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1125,49 +1125,31 @@ bool wallet_htlcs_reconnect(struct wallet *wallet, return true; } -int wallet_invoice_nextpaid(const tal_t *cxt, - const struct wallet *wallet, - const char *labelz, - char **outlabel, - struct sha256 *outrhash, - u64 *outmsatoshi) +bool wallet_invoice_nextpaid(const tal_t *cxt, + const struct wallet *wallet, + u64 pay_index, + char **outlabel, + struct sha256 *outrhash, + u64 *outmsatoshi, + u64 *outpay_index) { sqlite3_stmt *stmt; int res; - u64 pay_index; /* Generate query. */ - if (labelz) { - /* Find label. */ - stmt = db_prepare(wallet->db, - "SELECT pay_index FROM invoices WHERE label=?;"); - sqlite3_bind_text(stmt, 1, labelz, strlen(labelz), SQLITE_TRANSIENT); - res = sqlite3_step(stmt); - if (res != SQLITE_ROW) { - sqlite3_finalize(stmt); - return -1; - } - pay_index = sqlite3_column_int64(stmt, 0); - sqlite3_finalize(stmt); - - stmt = db_prepare(wallet->db, - "SELECT label, payment_hash, msatoshi FROM invoices" - " WHERE pay_index NOT NULL" - " AND pay_index > ?" - " ORDER BY pay_index ASC LIMIT 1;"); - sqlite3_bind_int64(stmt, 1, pay_index); - } else { - stmt = db_prepare(wallet->db, - "SELECT label, payment_hash, msatoshi FROM invoices" - " WHERE pay_index NOT NULL" - " ORDER BY pay_index ASC LIMIT 1;"); - } + stmt = db_prepare(wallet->db, + "SELECT label, payment_hash, msatoshi, pay_index" + " FROM invoices" + " WHERE pay_index NOT NULL" + " AND pay_index > ?" + " ORDER BY pay_index ASC LIMIT 1;"); + sqlite3_bind_int64(stmt, 1, pay_index); res = sqlite3_step(stmt); if (res != SQLITE_ROW) { /* No paid invoice found. */ sqlite3_finalize(stmt); - return 0; + return false; } else { /* Paid invoice found, return data. */ *outlabel = tal_strndup(cxt, sqlite3_column_blob(stmt, 0), sqlite3_column_bytes(stmt, 0)); @@ -1177,11 +1159,11 @@ int wallet_invoice_nextpaid(const tal_t *cxt, *outmsatoshi = sqlite3_column_int64(stmt, 2); + *outpay_index = sqlite3_column_int64(stmt, 3); + sqlite3_finalize(stmt); - return 1; + return true; } - - return -1; } /* Acquire the next pay_index. */ diff --git a/wallet/wallet.h b/wallet/wallet.h index ad911c1fa..1fa121189 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -333,28 +333,29 @@ bool wallet_htlcs_reconnect(struct wallet *wallet, /** * wallet_invoice_nextpaid -- Find a paid invoice. * - * Get the details (label, rhash, msatoshi) of the next paid - * invoice after the invoice with the given label. If label is - * `NULL`, get the details of the first paid invoice. Return -1 - * if label is non-`NULL` and is not found, 0 if no more paid - * invoices after specified invoice (or no paid invoices if label - * is `NULL`), 1 if the next paid invoice was found. + * Get the details (label, rhash, msatoshi, pay_index) of the first paid + * invoice greater than the given pay_index. Return false if no paid + * invoice found, return true if found. The first ever paid invoice will + * have a pay_index of 1 or greater, so giving a pay_index of 0 will get + * the first ever paid invoice if there is one. * * @ctx: Context to create the returned label. * @wallet: Wallet to query - * @labelz: The label to be queried (zero-terminated), or - * `NULL` if first invoice is to be queried. + * @pay_index: The paid invoice returned will have pay_index greater + * than this argument. * @outlabel: Pointer to label of found paid invoice. Caller - * must free if this function returns 1. + * must free if this function returns true. * @outrhash: Pointer to struct rhash to be filled. * @outmsatoshi: Pointer to number of millisatoshis value to pay. + * @outpay_index: Pointer to pay_index of found paid invoice. */ -int wallet_invoice_nextpaid(const tal_t *cxt, - const struct wallet *wallet, - const char *labelz, - char **outlabel, - struct sha256 *outrhash, - u64 *outmsatoshi); +bool wallet_invoice_nextpaid(const tal_t *cxt, + const struct wallet *wallet, + u64 pay_index, + char **outlabel, + struct sha256 *outrhash, + u64 *outmsatoshi, + u64 *outpay_index); /** * wallet_invoice_save -- Save/update an invoice to the wallet