listinvoices: deprecate 'complete' in favor of 'status' trinary.

[ Manpage regen fixup by Christian Decker ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2018-01-17 13:34:29 +10:30
parent 67b7362986
commit bd010d4b96
4 changed files with 56 additions and 40 deletions

View File

@ -2,12 +2,12 @@
.\" Title: lightning-listinvoices
.\" Author: [see the "AUTHOR" section]
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
.\" Date: 01/16/2018
.\" Date: 01/18/2018
.\" Manual: \ \&
.\" Source: \ \&
.\" Language: English
.\"
.TH "LIGHTNING\-LISTINVOI" "7" "01/16/2018" "\ \&" "\ \&"
.TH "LIGHTNING\-LISTINVOI" "7" "01/18/2018" "\ \&" "\ \&"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
@ -37,7 +37,7 @@ lightning-listinvoices \- Protocol for querying invoice status
The \fBlistinvoices\fR RPC command gets the status of a specific invoice, if it exists, or the status of all invoices if given no argument\&.
.SH "RETURN VALUE"
.sp
On success, an array \fIinvoices\fR of objects is returned\&. Each object contains \fIlabel\fR, \fIpayment_hash, \*(Aqcomplete\fR (a boolean), and \fIexpiry_time\fR (a UNIX timestamp)\&. If the \fImsatoshi\fR argument to lightning\-invoice(7) was not "any", there will be an \fImsatoshi\fR field\&. If the invoice has been paid, there will be a \fIpay_index\fR field and an \fImsatoshi_received\fR field (which may be slightly greater than \fImsatoshi\fR as some overpaying is permitted to allow clients to obscure payment paths)\&.
On success, an array \fIinvoices\fR of objects is returned\&. Each object contains \fIlabel\fR, \fIpayment_hash\fR, \fIstatus\fR (one of \fIunpaid\fR, \fIpaid\fR or \fIexpired\fR), and \fIexpiry_time\fR (a UNIX timestamp)\&. If the \fImsatoshi\fR argument to lightning\-invoice(7) was not "any", there will be an \fImsatoshi\fR field\&. If the invoice \fIstatus\fR is \fIpaid\fR, there will be a \fIpay_index\fR field and an \fImsatoshi_received\fR field (which may be slightly greater than \fImsatoshi\fR as some overpaying is permitted to allow clients to obscure payment paths)\&.
.SH "AUTHOR"
.sp
Rusty Russell <rusty@rustcorp\&.com\&.au> is mainly responsible\&.

View File

@ -17,7 +17,7 @@ it exists, or the status of all invoices if given no argument.
RETURN VALUE
------------
On success, an array 'invoices' of objects is returned. Each object contains 'label', 'payment_hash, 'complete' (a boolean), and 'expiry_time' (a UNIX timestamp). If the 'msatoshi' argument to lightning-invoice(7) was not "any", there will be an 'msatoshi' field. If the invoice has been paid, there will be a 'pay_index' field and an 'msatoshi_received' field (which may be slightly greater than 'msatoshi' as some overpaying is permitted to allow clients to obscure payment paths).
On success, an array 'invoices' of objects is returned. Each object contains 'label', 'payment_hash', 'status' (one of 'unpaid', 'paid' or 'expired'), and 'expiry_time' (a UNIX timestamp). If the 'msatoshi' argument to lightning-invoice(7) was not "any", there will be an 'msatoshi' field. If the invoice 'status' is 'paid', there will be a 'pay_index' field and an 'msatoshi_received' field (which may be slightly greater than 'msatoshi' as some overpaying is permitted to allow clients to obscure payment paths).
//FIXME:Enumerate errors

View File

@ -15,18 +15,32 @@
#include <inttypes.h>
#include <lightningd/hsm_control.h>
#include <lightningd/log.h>
#include <lightningd/options.h>
#include <sodium/randombytes.h>
#include <wire/wire_sync.h>
static const char *invoice_status_str(const struct invoice *inv)
{
if (inv->state == PAID)
return "paid";
if (time_now().ts.tv_sec > inv->expiry_time)
return "expired";
return "unpaid";
}
static void json_add_invoice(struct json_result *response,
const struct invoice *inv)
const struct invoice *inv,
bool modern)
{
json_object_start(response, NULL);
json_add_string(response, "label", inv->label);
json_add_hex(response, "payment_hash", &inv->rhash, sizeof(inv->rhash));
if (inv->msatoshi)
json_add_u64(response, "msatoshi", *inv->msatoshi);
json_add_bool(response, "complete", inv->state == PAID);
if (modern)
json_add_string(response, "status", invoice_status_str(inv));
else if (deprecated_apis && !modern)
json_add_bool(response, "complete", inv->state == PAID);
if (inv->state == PAID) {
json_add_u64(response, "pay_index", inv->pay_index);
json_add_u64(response, "msatoshi_received",
@ -42,7 +56,7 @@ static void tell_waiter(struct command *cmd, const struct invoice *paid)
{
struct json_result *response = new_json_result(cmd);
json_add_invoice(response, paid);
json_add_invoice(response, paid, true);
command_success(cmd, response);
}
static void tell_waiter_deleted(struct command *cmd)
@ -183,7 +197,8 @@ AUTODATA(json_command, &invoice_command);
static void json_add_invoices(struct json_result *response,
struct wallet *wallet,
const char *buffer, const jsmntok_t *label)
const char *buffer, const jsmntok_t *label,
bool modern)
{
const struct invoice *i;
char *lbl = NULL;
@ -194,7 +209,7 @@ static void json_add_invoices(struct json_result *response,
while ((i = wallet_invoice_iterate(wallet, i)) != NULL) {
if (lbl && !streq(i->label, lbl))
continue;
json_add_invoice(response, i);
json_add_invoice(response, i, modern);
}
}
@ -219,7 +234,7 @@ static void json_listinvoice_internal(struct command *cmd,
json_array_start(response, "invoices");
} else
json_array_start(response, NULL);
json_add_invoices(response, wallet, buffer, label);
json_add_invoices(response, wallet, buffer, label, modern);
json_array_end(response);
if (modern)
json_object_end(response);
@ -283,7 +298,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);
json_add_invoice(response, i, true);
error = wallet_invoice_delete(wallet, i);

View File

@ -280,7 +280,7 @@ class LightningDTests(BaseLightningDTests):
label = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(20))
rhash = ldst.rpc.invoice(amt, label, label)['payment_hash']
assert ldst.rpc.listinvoices(label)['invoices'][0]['complete'] == False
assert ldst.rpc.listinvoices(label)['invoices'][0]['status'] == 'unpaid'
routestep = {
'msatoshi' : amt,
@ -292,7 +292,7 @@ class LightningDTests(BaseLightningDTests):
def wait_pay():
# Up to 10 seconds for payment to succeed.
start_time = time.time()
while not ldst.rpc.listinvoices(label)['invoices'][0]['complete']:
while ldst.rpc.listinvoices(label)['invoices'][0]['status'] != 'paid':
if time.time() > start_time + 10:
raise TimeoutError('Payment timed out')
time.sleep(0.1)
@ -366,7 +366,7 @@ class LightningDTests(BaseLightningDTests):
inv = l2.rpc.invoice(123000, 'test_pay', 'description', 1)['bolt11']
time.sleep(2)
self.assertRaises(ValueError, l1.rpc.pay, inv)
assert l2.rpc.listinvoices('test_pay')['invoices'][0]['complete'] == False
assert l2.rpc.listinvoices('test_pay')['invoices'][0]['status'] == 'expired'
assert l2.rpc.listinvoices('test_pay')['invoices'][0]['expiry_time'] < time.time()
def test_connect(self):
@ -639,7 +639,7 @@ class LightningDTests(BaseLightningDTests):
amt = 200000000
rhash = l2.rpc.invoice(amt, 'testpayment2', 'desc')['payment_hash']
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['complete'] == False
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['status'] == 'unpaid'
routestep = {
'msatoshi' : amt,
@ -652,25 +652,25 @@ class LightningDTests(BaseLightningDTests):
rs = copy.deepcopy(routestep)
rs['msatoshi'] = rs['msatoshi'] - 1
self.assertRaises(ValueError, l1.rpc.sendpay, to_json([rs]), rhash)
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['complete'] == False
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['status'] == 'unpaid'
# Gross overpayment (more than factor of 2)
rs = copy.deepcopy(routestep)
rs['msatoshi'] = rs['msatoshi'] * 2 + 1
self.assertRaises(ValueError, l1.rpc.sendpay, to_json([rs]), rhash)
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['complete'] == False
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['status'] == 'unpaid'
# Insufficient delay.
rs = copy.deepcopy(routestep)
rs['delay'] = rs['delay'] - 2
self.assertRaises(ValueError, l1.rpc.sendpay, to_json([rs]), rhash)
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['complete'] == False
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['status'] == 'unpaid'
# Bad ID.
rs = copy.deepcopy(routestep)
rs['id'] = '00000000000000000000000000000000'
self.assertRaises(ValueError, l1.rpc.sendpay, to_json([rs]), rhash)
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['complete'] == False
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['status'] == 'unpaid'
# FIXME: test paying via another node, should fail to pay twice.
p1 = l1.rpc.getpeer(l2.info['id'], 'info')
@ -682,7 +682,7 @@ class LightningDTests(BaseLightningDTests):
# This works.
preimage2 = l1.rpc.sendpay(to_json([routestep]), rhash)
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['complete'] == True
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['status'] == 'paid'
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['pay_index'] == 1
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['msatoshi_received'] == rs['msatoshi']
@ -700,15 +700,15 @@ class LightningDTests(BaseLightningDTests):
preimage = l1.rpc.sendpay(to_json([routestep]), rhash)
assert preimage == preimage2
l1.daemon.wait_for_log('... succeeded')
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['complete'] == True
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['status'] == 'paid'
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['msatoshi_received'] == rs['msatoshi']
# Overpaying by "only" a factor of 2 succeeds.
rhash = l2.rpc.invoice(amt, 'testpayment3', 'desc')['payment_hash']
assert l2.rpc.listinvoices('testpayment3')['invoices'][0]['complete'] == False
assert l2.rpc.listinvoices('testpayment3')['invoices'][0]['status'] == 'unpaid'
routestep = { 'msatoshi' : amt * 2, 'id' : l2.info['id'], 'delay' : 5, 'channel': '1:1:1'}
preimage3 = l1.rpc.sendpay(to_json([routestep]), rhash)
assert l2.rpc.listinvoices('testpayment3')['invoices'][0]['complete'] == True
assert l2.rpc.listinvoices('testpayment3')['invoices'][0]['status'] == 'paid'
assert l2.rpc.listinvoices('testpayment3')['invoices'][0]['msatoshi_received'] == amt*2
# Test listpayments
@ -769,7 +769,8 @@ class LightningDTests(BaseLightningDTests):
preimage = l1.rpc.pay(inv)
after = int(time.time())
invoice = l2.rpc.listinvoices('test_pay')['invoices'][0]
assert invoice['complete'] == True
print(invoice)
assert invoice['status'] == 'paid'
assert invoice['paid_timestamp'] >= before
assert invoice['paid_timestamp'] <= after
@ -1001,7 +1002,7 @@ class LightningDTests(BaseLightningDTests):
l1.daemon.wait_for_log('onchaind complete, forgetting peer')
# Payment failed, BTW
assert l2.rpc.listinvoices('onchain_dust_out')['invoices'][0]['complete'] == False
assert l2.rpc.listinvoices('onchain_dust_out')['invoices'][0]['status'] == 'unpaid'
@unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1")
def test_onchain_timeout(self):
@ -1064,7 +1065,7 @@ class LightningDTests(BaseLightningDTests):
l1.daemon.wait_for_log('onchaind complete, forgetting peer')
# Payment failed, BTW
assert l2.rpc.listinvoices('onchain_timeout')['invoices'][0]['complete'] == False
assert l2.rpc.listinvoices('onchain_timeout')['invoices'][0]['status'] == 'unpaid'
@unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1")
def test_onchain_middleman(self):
@ -1642,7 +1643,7 @@ class LightningDTests(BaseLightningDTests):
assert l3.rpc.getpeer(l2.info['id'])['channel'] == chanid2
rhash = l3.rpc.invoice(100000000, 'testpayment1', 'desc')['payment_hash']
assert l3.rpc.listinvoices('testpayment1')['invoices'][0]['complete'] == False
assert l3.rpc.listinvoices('testpayment1')['invoices'][0]['status'] == 'unpaid'
# Fee for node2 is 10 millionths, plus 1.
amt = 100000000
@ -1783,7 +1784,7 @@ class LightningDTests(BaseLightningDTests):
assert route[1]['delay'] == 9 + shadow_route
rhash = l3.rpc.invoice(4999999, 'test_forward_different_fees_and_cltv', 'desc')['payment_hash']
assert l3.rpc.listinvoices('test_forward_different_fees_and_cltv')['invoices'][0]['complete'] == False
assert l3.rpc.listinvoices('test_forward_different_fees_and_cltv')['invoices'][0]['status'] == 'unpaid'
# This should work.
l1.rpc.sendpay(to_json(route), rhash)
@ -1796,7 +1797,7 @@ class LightningDTests(BaseLightningDTests):
.format(bitcoind.rpc.getblockcount() + 9 + shadow_route))
l3.daemon.wait_for_log("test_forward_different_fees_and_cltv: Actual amount 4999999msat, HTLC expiry {}"
.format(bitcoind.rpc.getblockcount() + 9 + shadow_route))
assert l3.rpc.listinvoices('test_forward_different_fees_and_cltv')['invoices'][0]['complete'] == True
assert l3.rpc.listinvoices('test_forward_different_fees_and_cltv')['invoices'][0]['status'] == 'paid'
# Check that we see all the channels
shortids = set(c['short_channel_id'] for c in l2.rpc.listchannels()['channels'])
@ -1852,7 +1853,7 @@ class LightningDTests(BaseLightningDTests):
# This should work.
rhash = l3.rpc.invoice(4999999, 'test_forward_pad_fees_and_cltv', 'desc')['payment_hash']
l1.rpc.sendpay(to_json(route), rhash)
assert l3.rpc.listinvoices('test_forward_pad_fees_and_cltv')['invoices'][0]['complete'] == True
assert l3.rpc.listinvoices('test_forward_pad_fees_and_cltv')['invoices'][0]['status'] == 'paid'
@unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1")
def test_htlc_out_timeout(self):
@ -1872,7 +1873,7 @@ class LightningDTests(BaseLightningDTests):
amt = 200000000
inv = l2.rpc.invoice(amt, 'test_htlc_out_timeout', 'desc')['bolt11']
assert l2.rpc.listinvoices('test_htlc_out_timeout')['invoices'][0]['complete'] == False
assert l2.rpc.listinvoices('test_htlc_out_timeout')['invoices'][0]['status'] == 'unpaid'
payfuture = self.executor.submit(l1.rpc.pay, inv)
@ -1927,7 +1928,7 @@ class LightningDTests(BaseLightningDTests):
amt = 200000000
inv = l2.rpc.invoice(amt, 'test_htlc_in_timeout', 'desc')['bolt11']
assert l2.rpc.listinvoices('test_htlc_in_timeout')['invoices'][0]['complete'] == False
assert l2.rpc.listinvoices('test_htlc_in_timeout')['invoices'][0]['status'] == 'unpaid'
payfuture = self.executor.submit(l1.rpc.pay, inv)
@ -2126,7 +2127,7 @@ class LightningDTests(BaseLightningDTests):
amt = 200000000
rhash = l2.rpc.invoice(amt, 'test_reconnect_sender_add1', 'desc')['payment_hash']
assert l2.rpc.listinvoices('test_reconnect_sender_add1')['invoices'][0]['complete'] == False
assert l2.rpc.listinvoices('test_reconnect_sender_add1')['invoices'][0]['status'] == 'unpaid'
route = [ { 'msatoshi' : amt, 'id' : l2.info['id'], 'delay' : 5, 'channel': '1:1:1'} ]
@ -2154,7 +2155,7 @@ class LightningDTests(BaseLightningDTests):
amt = 200000000
rhash = l2.rpc.invoice(amt, 'testpayment', 'desc')['payment_hash']
assert l2.rpc.listinvoices('testpayment')['invoices'][0]['complete'] == False
assert l2.rpc.listinvoices('testpayment')['invoices'][0]['status'] == 'unpaid'
route = [ { 'msatoshi' : amt, 'id' : l2.info['id'], 'delay' : 5, 'channel': '1:1:1'} ]
@ -2180,13 +2181,13 @@ class LightningDTests(BaseLightningDTests):
amt = 200000000
rhash = l2.rpc.invoice(amt, 'testpayment2', 'desc')['payment_hash']
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['complete'] == False
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['status'] == 'unpaid'
route = [ { 'msatoshi' : amt, 'id' : l2.info['id'], 'delay' : 5, 'channel': '1:1:1'} ]
l1.rpc.sendpay(to_json(route), rhash)
for i in range(len(disconnects)):
l1.daemon.wait_for_log('Already have funding locked in')
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['complete'] == True
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['status'] == 'paid'
@unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1")
def test_reconnect_receiver_fulfill(self):
@ -2210,13 +2211,13 @@ class LightningDTests(BaseLightningDTests):
amt = 200000000
rhash = l2.rpc.invoice(amt, 'testpayment2', 'desc')['payment_hash']
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['complete'] == False
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['status'] == 'unpaid'
route = [ { 'msatoshi' : amt, 'id' : l2.info['id'], 'delay' : 5, 'channel': '1:1:1'} ]
l1.rpc.sendpay(to_json(route), rhash)
for i in range(len(disconnects)):
l1.daemon.wait_for_log('Already have funding locked in')
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['complete'] == True
assert l2.rpc.listinvoices('testpayment2')['invoices'][0]['status'] == 'paid'
@unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1")
def test_shutdown_reconnect(self):
@ -2586,7 +2587,7 @@ class LightningDTests(BaseLightningDTests):
wait_for(lambda: l1.rpc.listpayments()['payments'][0]['status'] != 'pending')
assert l1.rpc.listpayments()['payments'][0]['status'] == 'complete'
assert l2.rpc.listinvoices('inv1')['invoices'][0]['complete'] == True
assert l2.rpc.listinvoices('inv1')['invoices'][0]['status'] == 'paid'
# FIXME: We should re-add pre-announced routes on startup!
self.wait_for_routes(l1, [chanid])
@ -2629,7 +2630,7 @@ class LightningDTests(BaseLightningDTests):
wait_for(lambda: l1.rpc.listpayments()['payments'][0]['status'] != 'pending')
assert l2.rpc.listinvoices('inv1')['invoices'][0]['complete'] == False
assert l2.rpc.listinvoices('inv1')['invoices'][0]['status'] == 'expired'
assert l1.rpc.listpayments()['payments'][0]['status'] == 'failed'
# Another attempt should also fail.