diff --git a/common/jsonrpc_errors.h b/common/jsonrpc_errors.h index c7de013dc..08478e000 100644 --- a/common/jsonrpc_errors.h +++ b/common/jsonrpc_errors.h @@ -52,5 +52,6 @@ #define INVOICE_LABEL_ALREADY_EXISTS 900 #define INVOICE_PREIMAGE_ALREADY_EXISTS 901 #define INVOICE_HINTS_GAVE_NO_ROUTES 902 +#define INVOICE_WAIT_TIMED_OUT 904 #endif /* LIGHTNING_COMMON_JSONRPC_ERRORS_H */ diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index d019ed9c2..0264dfbcd 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -988,14 +988,18 @@ class LightningRpc(UnixDomainSocketRpc): """ return self.call("stop") - def waitanyinvoice(self, lastpay_index=None): + def waitanyinvoice(self, lastpay_index=None, timeout=None, **kwargs): """ Wait for the next invoice to be paid, after {lastpay_index} (if supplied) + Fail after {timeout} seconds has passed without an invoice + being paid. """ payload = { - "lastpay_index": lastpay_index + "lastpay_index": lastpay_index, + "timeout": timeout } + payload.update({k: v for k, v in kwargs.items()}) return self.call("waitanyinvoice", payload) def waitblockheight(self, blockheight, timeout=None): diff --git a/doc/lightning-waitanyinvoice.7 b/doc/lightning-waitanyinvoice.7 index bd3335d8c..f5fc24ed5 100644 --- a/doc/lightning-waitanyinvoice.7 +++ b/doc/lightning-waitanyinvoice.7 @@ -3,7 +3,7 @@ lightning-waitanyinvoice - Command for waiting for payments .SH SYNOPSIS -\fBwaitanyinvoice\fR [\fIlastpay_index\fR] +\fBwaitanyinvoice\fR [\fIlastpay_index\fR] [\fItimeout\fR] .SH DESCRIPTION @@ -22,11 +22,29 @@ 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\. + +If \fItimeout\fR is specified, wait at most that number of seconds, which +must be an integer\. +If the specified \fItimeout\fR is reached, this command will return with an +error\. +You can specify this to 0 so that \fBwaitanyinvoice\fR will return +immediately with an error if no pending invoice is available yet\. +If unspecified, this command will wait indefinitely\. + .SH RETURN VALUE On success, an invoice description will be returned as per \fBlightning-listinvoice\fR(7): \fIcomplete\fR will always be \fItrue\fR\. + +Possible errors are: + +.RS +.IP \[bu] +904\. +The \fItimeout\fR was reached without an invoice being paid\. + +.RE .SH AUTHOR Rusty Russell \fI is mainly responsible\. diff --git a/doc/lightning-waitanyinvoice.7.md b/doc/lightning-waitanyinvoice.7.md index 0c0192a32..be17fb525 100644 --- a/doc/lightning-waitanyinvoice.7.md +++ b/doc/lightning-waitanyinvoice.7.md @@ -4,7 +4,7 @@ lightning-waitanyinvoice -- Command for waiting for payments SYNOPSIS -------- -**waitanyinvoice** \[*lastpay\_index*\] +**waitanyinvoice** \[*lastpay\_index*\] \[*timeout*\] DESCRIPTION ----------- @@ -22,12 +22,25 @@ 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. +If *timeout* is specified, wait at most that number of seconds, which +must be an integer. +If the specified *timeout* is reached, this command will return with an +error. +You can specify this to 0 so that **waitanyinvoice** will return +immediately with an error if no pending invoice is available yet. +If unspecified, this command will wait indefinitely. + RETURN VALUE ------------ On success, an invoice description will be returned as per lightning-listinvoice(7): *complete* will always be *true*. +Possible errors are: + +* 904. + The *timeout* was reached without an invoice being paid. + AUTHOR ------ diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 4d94b6fc6..3cc5073be 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -102,6 +103,12 @@ static void wait_on_invoice(const struct invoice *invoice, void *cmd) else tell_waiter_deleted((struct command *) cmd); } +static void wait_timed_out(struct command *cmd) +{ + was_pending(command_fail(cmd, INVOICE_WAIT_TIMED_OUT, + "Timed out while waiting " + "for invoice to be paid")); +} /* We derive invoice secret using 1-way function from payment_preimage * (just a different one from the payment_hash!) */ @@ -1129,13 +1136,25 @@ static struct command_result *json_waitanyinvoice(struct command *cmd, const jsmntok_t *params) { u64 *pay_index; + u64 *timeout; struct wallet *wallet = cmd->ld->wallet; if (!param(cmd, buffer, params, p_opt_def("lastpay_index", param_u64, &pay_index, 0), + p_opt("timeout", ¶m_u64, &timeout), NULL)) return command_param_failed(); + /*~ We allocate the timeout and the wallet-waitanyinvoice + * in the cmd context, so whichever one manages to complete + * the command first (and destroy the cmd context) + * auto-cancels the other, is not tal amazing? + */ + if (timeout) + (void) new_reltimer(cmd->ld->timers, cmd, + time_from_sec(*timeout), + &wait_timed_out, cmd); + /* Set command as pending. We do not know if * wallet_invoice_waitany will return immediately * or not, so indicating pending is safest. */ @@ -1155,7 +1174,8 @@ static const struct json_command waitanyinvoice_command = { "waitanyinvoice", "payment", json_waitanyinvoice, - "Wait for the next invoice to be paid, after {lastpay_index} (if supplied)" + "Wait for the next invoice to be paid, after {lastpay_index} (if supplied). " + "If {timeout} seconds is reached while waiting, fail with an error." }; AUTODATA(json_command, &waitanyinvoice_command);