Browse Source

invoice: Modify waitanyinvoice interface to use pay_index.

ppa-0.6.1
ZmnSCPxj 7 years ago
committed by Rusty Russell
parent
commit
5eceaa7be9
  1. 14
      doc/lightning-waitanyinvoice.7
  2. 14
      doc/lightning-waitanyinvoice.7.txt
  3. 50
      lightningd/invoice.c
  4. 11
      tests/test_lightningd.py
  5. 54
      wallet/wallet.c
  6. 31
      wallet/wallet.h

14
doc/lightning-waitanyinvoice.7

@ -2,12 +2,12 @@
.\" Title: lightning-waitanyinvoice
.\" Author: [see the "AUTHOR" section]
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
.\" 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 <rusty@rustcorp\&.com\&.au> is mainly responsible\&.

14
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

50
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);

11
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):

54
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. */

31
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

Loading…
Cancel
Save