diff --git a/doc/lightning-listinvoice.7 b/doc/lightning-listinvoice.7 index 6b058c0fb..135849752 100644 --- a/doc/lightning-listinvoice.7 +++ b/doc/lightning-listinvoice.7 @@ -37,7 +37,7 @@ lightning-listinvoice \- Protocol for querying invoice status The \fBlistinvoice\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 containing \fIlabel\fR, \fIpayment_hash, \*(Aqmsatoshi\fR (if not "any"), \fIcomplete\fR, \fIpay_index\fR (if paid) and \fIexpiry_time\fR} will be returned\&. \fIcomplete\fR is a boolean, and \fIexpiry_time\fR is the number of seconds since UNIX epoch\&. +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)\&. .SH "AUTHOR" .sp Rusty Russell is mainly responsible\&. diff --git a/doc/lightning-listinvoice.7.txt b/doc/lightning-listinvoice.7.txt index 5956d72f6..9f5c766e3 100644 --- a/doc/lightning-listinvoice.7.txt +++ b/doc/lightning-listinvoice.7.txt @@ -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. +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). //FIXME:Enumerate errors diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 8497e037b..dbf7b5d7c 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -83,8 +83,11 @@ static void json_add_invoice(struct json_result *response, if (inv->msatoshi) json_add_u64(response, "msatoshi", *inv->msatoshi); json_add_bool(response, "complete", inv->state == PAID); - if (inv->state == PAID) + if (inv->state == PAID) { json_add_u64(response, "pay_index", inv->pay_index); + json_add_u64(response, "msatoshi_received", + inv->msatoshi_received); + } json_add_u64(response, "expiry_time", inv->expiry_time); json_object_end(response); } @@ -101,12 +104,14 @@ static void tell_waiter_deleted(struct command *cmd, const struct invoice *paid) command_fail(cmd, "invoice deleted during wait"); } -void resolve_invoice(struct lightningd *ld, struct invoice *invoice) +void resolve_invoice(struct lightningd *ld, struct invoice *invoice, + u64 msatoshi_received) { struct invoice_waiter *w; struct invoices *invs = ld->invoices; invoice->state = PAID; + invoice->msatoshi_received = msatoshi_received; /* wallet_invoice_save updates pay_index member, * which tell_waiter needs. */ diff --git a/lightningd/invoice.h b/lightningd/invoice.h index 2695f3608..8206fe634 100644 --- a/lightningd/invoice.h +++ b/lightningd/invoice.h @@ -17,15 +17,22 @@ enum invoice_status { }; struct invoice { + /* List off ld->invoices->invlist */ struct list_node list; + /* Database ID */ u64 id; enum invoice_status state; const char *label; + /* NULL if they specified "any" */ u64 *msatoshi; + /* Set if state == PAID */ + u64 msatoshi_received; struct preimage r; u64 expiry_time; struct sha256 rhash; + /* Non-zero if state == PAID */ u64 pay_index; + /* Any JSON waitinvoice calls waiting for this to be paid. */ struct list_head waitone_waiters; }; @@ -35,7 +42,8 @@ struct invoice { void invoice_add(struct invoices *invs, struct invoice *inv); -void resolve_invoice(struct lightningd *ld, struct invoice *invoice); +void resolve_invoice(struct lightningd *ld, struct invoice *invoice, + u64 msatoshi_received); struct invoice *find_unpaid(struct invoices *i, const struct sha256 *rhash); diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index a68709e75..998436bcd 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -297,7 +297,7 @@ static void handle_localpay(struct htlc_in *hin, log_debug(ld->log, "%s: Actual amount %"PRIu64"msat, HTLC expiry %u", invoice->label, hin->msatoshi, cltv_expiry); fulfill_htlc(hin, &invoice->r); - resolve_invoice(ld, invoice); + resolve_invoice(ld, invoice, hin->msatoshi); return; fail: diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index b4309aa91..dc748eb40 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -678,6 +678,8 @@ class LightningDTests(BaseLightningDTests): # This works. l1.rpc.sendpay(to_json([routestep]), rhash) assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == True + assert l2.rpc.listinvoice('testpayment2')[0]['pay_index'] == 1 + assert l2.rpc.listinvoice('testpayment2')[0]['msatoshi_received'] == rs['msatoshi'] # Balances should reflect it. time.sleep(1) @@ -693,6 +695,7 @@ class LightningDTests(BaseLightningDTests): l1.rpc.sendpay(to_json([routestep]), rhash) l1.daemon.wait_for_log('... succeeded') assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == True + assert l2.rpc.listinvoice('testpayment2')[0]['msatoshi_received'] == rs['msatoshi'] # Overpaying by "only" a factor of 2 succeeds. rhash = l2.rpc.invoice(amt, 'testpayment3', 'desc')['payment_hash'] @@ -700,6 +703,7 @@ class LightningDTests(BaseLightningDTests): routestep = { 'msatoshi' : amt * 2, 'id' : l2.info['id'], 'delay' : 5, 'channel': '1:1:1'} l1.rpc.sendpay(to_json([routestep]), rhash) assert l2.rpc.listinvoice('testpayment3')[0]['complete'] == True + assert l2.rpc.listinvoice('testpayment3')[0]['msatoshi_received'] == amt*2 def test_sendpay_cant_afford(self): l1,l2 = self.connect() diff --git a/wallet/db.c b/wallet/db.c index 887dc4954..d92d29a8b 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -153,6 +153,9 @@ char *dbmigrations[] = { "ALTER TABLE outputs ADD COLUMN channel_id INTEGER;", "ALTER TABLE outputs ADD COLUMN peer_id BLOB;", "ALTER TABLE outputs ADD COLUMN commitment_point BLOB;", + "ALTER TABLE invoices ADD COLUMN msatoshi_received INTEGER;", + /* Normally impossible, so at least we'll know if databases are ancient. */ + "UPDATE invoices SET msatoshi_received=0 WHERE state=1;", NULL, }; diff --git a/wallet/wallet.c b/wallet/wallet.c index 0e34ef5ce..b6d298970 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1171,6 +1171,8 @@ static void wallet_stmt2invoice(sqlite3_stmt *stmt, struct invoice *inv) /* Correctly 0 if pay_index is NULL. */ inv->pay_index = sqlite3_column_int64(stmt, 7); + if (inv->state == PAID) + inv->msatoshi_received = sqlite3_column_int64(stmt, 8); list_head_init(&inv->waitone_waiters); } @@ -1185,7 +1187,8 @@ struct invoice *wallet_invoice_nextpaid(const tal_t *ctx, /* Generate query. */ stmt = db_prepare(wallet->db, "SELECT id, state, payment_key, payment_hash," - " label, msatoshi, expiry_time, pay_index " + " label, msatoshi, expiry_time, pay_index," + " msatoshi_received " " FROM invoices" " WHERE pay_index NOT NULL" " AND pay_index > ?" @@ -1302,7 +1305,8 @@ bool wallet_invoices_load(struct wallet *wallet, struct invoices *invs) int count = 0; sqlite3_stmt *stmt = db_query(__func__, wallet->db, "SELECT id, state, payment_key, payment_hash, " - "label, msatoshi, expiry_time, pay_index " + "label, msatoshi, expiry_time, pay_index, " + "msatoshi_received " "FROM invoices;"); if (!stmt) { log_broken(wallet->log, "Could not load invoices");