Browse Source

JSONRPC: listpayments can list just a specific bolt11 or payment_hash.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 7 years ago
parent
commit
6e703ad977
  1. 47
      lightningd/pay.c
  2. 31
      tests/test_lightningd.py
  3. 19
      wallet/wallet.c
  4. 5
      wallet/wallet.h

47
lightningd/pay.c

@ -441,8 +441,49 @@ static void json_listpayments(struct command *cmd, const char *buffer,
{ {
const struct wallet_payment **payments; const struct wallet_payment **payments;
struct json_result *response = new_json_result(cmd); struct json_result *response = new_json_result(cmd);
jsmntok_t *bolt11tok, *rhashtok;
struct sha256 *rhash = NULL;
payments = wallet_payment_list(cmd, cmd->ld->wallet); if (!json_get_params(buffer, params,
"?bolt11", &bolt11tok,
"?payment_hash", &rhashtok,
NULL)) {
command_fail(cmd, "Invalid parameters");
return;
}
if (bolt11tok) {
struct bolt11 *b11;
char *b11str, *fail;
if (rhashtok) {
command_fail(cmd, "Can only specify one of"
" {bolt11} or {payment_hash}");
return;
}
b11str = tal_strndup(cmd, buffer + bolt11tok->start,
bolt11tok->end - bolt11tok->start);
b11 = bolt11_decode(cmd, b11str, NULL, &fail);
if (!b11) {
command_fail(cmd, "Invalid bolt11: %s", fail);
return;
}
rhash = &b11->payment_hash;
} else if (rhashtok) {
rhash = tal(cmd, struct sha256);
if (!hex_decode(buffer + rhashtok->start,
rhashtok->end - rhashtok->start,
rhash, sizeof(*rhash))) {
command_fail(cmd, "'%.*s' is not a valid sha256 hash",
(int)(rhashtok->end - rhashtok->start),
buffer + rhashtok->start);
return;
}
}
payments = wallet_payment_list(cmd, cmd->ld->wallet, rhash);
json_array_start(response, NULL); json_array_start(response, NULL);
for (int i=0; i<tal_count(payments); i++) { for (int i=0; i<tal_count(payments); i++) {
@ -479,7 +520,7 @@ static void json_listpayments(struct command *cmd, const char *buffer,
static const struct json_command listpayments_command = { static const struct json_command listpayments_command = {
"listpayments", "listpayments",
json_listpayments, json_listpayments,
"Get a list of incoming and outgoing payments", "Get a list of outgoing payments",
"Returns a list of payments with {payment_hash}, {destination}, {msatoshi}, {timestamp} and {status}" "Returns a list of payments with {payment_hash}, {destination}, {msatoshi}, {timestamp} and {status} (and {payment_preimage} if {status} is 'complete')"
}; };
AUTODATA(json_command, &listpayments_command); AUTODATA(json_command, &listpayments_command);

31
tests/test_lightningd.py

@ -678,7 +678,7 @@ class LightningDTests(BaseLightningDTests):
assert p2['msatoshi_total'] == 10**6 * 1000 assert p2['msatoshi_total'] == 10**6 * 1000
# This works. # This works.
l1.rpc.sendpay(to_json([routestep]), rhash) preimage2 = l1.rpc.sendpay(to_json([routestep]), rhash)
assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == True assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == True
assert l2.rpc.listinvoice('testpayment2')[0]['pay_index'] == 1 assert l2.rpc.listinvoice('testpayment2')[0]['pay_index'] == 1
assert l2.rpc.listinvoice('testpayment2')[0]['msatoshi_received'] == rs['msatoshi'] assert l2.rpc.listinvoice('testpayment2')[0]['msatoshi_received'] == rs['msatoshi']
@ -694,7 +694,8 @@ class LightningDTests(BaseLightningDTests):
# Repeat will "succeed", but won't actually send anything (duplicate) # Repeat will "succeed", but won't actually send anything (duplicate)
assert not l1.daemon.is_in_log('... succeeded') assert not l1.daemon.is_in_log('... succeeded')
l1.rpc.sendpay(to_json([routestep]), rhash) preimage = l1.rpc.sendpay(to_json([routestep]), rhash)
assert preimage == preimage2
l1.daemon.wait_for_log('... succeeded') l1.daemon.wait_for_log('... succeeded')
assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == True assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == True
assert l2.rpc.listinvoice('testpayment2')[0]['msatoshi_received'] == rs['msatoshi'] assert l2.rpc.listinvoice('testpayment2')[0]['msatoshi_received'] == rs['msatoshi']
@ -703,9 +704,16 @@ class LightningDTests(BaseLightningDTests):
rhash = l2.rpc.invoice(amt, 'testpayment3', 'desc')['payment_hash'] rhash = l2.rpc.invoice(amt, 'testpayment3', 'desc')['payment_hash']
assert l2.rpc.listinvoice('testpayment3')[0]['complete'] == False assert l2.rpc.listinvoice('testpayment3')[0]['complete'] == False
routestep = { 'msatoshi' : amt * 2, 'id' : l2.info['id'], 'delay' : 5, 'channel': '1:1:1'} routestep = { 'msatoshi' : amt * 2, 'id' : l2.info['id'], 'delay' : 5, 'channel': '1:1:1'}
l1.rpc.sendpay(to_json([routestep]), rhash) preimage3 = l1.rpc.sendpay(to_json([routestep]), rhash)
assert l2.rpc.listinvoice('testpayment3')[0]['complete'] == True assert l2.rpc.listinvoice('testpayment3')[0]['complete'] == True
assert l2.rpc.listinvoice('testpayment3')[0]['msatoshi_received'] == amt*2 assert l2.rpc.listinvoice('testpayment3')[0]['msatoshi_received'] == amt*2
# Test listpayments
assert len(l1.rpc.listpayments()) == 2
assert len(l1.rpc.listpayments(None, l2.rpc.listinvoice('testpayment2')[0]['payment_hash'])) == 1
assert l1.rpc.listpayments(None, l2.rpc.listinvoice('testpayment2')[0]['payment_hash'])[0]['status'] == 'complete'
assert l1.rpc.listpayments(None, l2.rpc.listinvoice('testpayment2')[0]['payment_hash'])[0]['payment_preimage'] == preimage2['preimage']
assert l1.rpc.listpayments(None, l2.rpc.listinvoice('testpayment3')[0]['payment_hash'])[0]['status'] == 'complete'
assert l1.rpc.listpayments(None, l2.rpc.listinvoice('testpayment3')[0]['payment_hash'])[0]['payment_preimage'] == preimage3['preimage']
def test_sendpay_cant_afford(self): def test_sendpay_cant_afford(self):
l1,l2 = self.connect() l1,l2 = self.connect()
@ -744,7 +752,7 @@ class LightningDTests(BaseLightningDTests):
inv = l2.rpc.invoice(123000, 'test_pay', 'description')['bolt11'] inv = l2.rpc.invoice(123000, 'test_pay', 'description')['bolt11']
before = int(time.time()) before = int(time.time())
l1.rpc.pay(inv) preimage = l1.rpc.pay(inv)
after = int(time.time()) after = int(time.time())
invoice = l2.rpc.listinvoice('test_pay')[0] invoice = l2.rpc.listinvoice('test_pay')[0]
assert invoice['complete'] == True assert invoice['complete'] == True
@ -764,11 +772,18 @@ class LightningDTests(BaseLightningDTests):
# Check payment of any-amount invoice. # Check payment of any-amount invoice.
for i in range(5): for i in range(5):
label = "any{}".format(i) label = "any{}".format(i)
inv = l2.rpc.invoice("any", label, 'description')['bolt11'] inv2 = l2.rpc.invoice("any", label, 'description')['bolt11']
# Must provide an amount! # Must provide an amount!
self.assertRaises(ValueError, l1.rpc.pay, inv) self.assertRaises(ValueError, l1.rpc.pay, inv2)
self.assertRaises(ValueError, l1.rpc.pay, inv, None) self.assertRaises(ValueError, l1.rpc.pay, inv2, None)
l1.rpc.pay(inv, random.randint(1000, 999999)) l1.rpc.pay(inv2, random.randint(1000, 999999))
# Should see 6 completed payments
assert len(l1.rpc.listpayments()) == 6
# Test listpayments indexed by bolt11.
assert len(l1.rpc.listpayments(inv)) == 1
assert l1.rpc.listpayments(inv)[0]['payment_preimage'] == preimage['preimage']
def test_bad_opening(self): def test_bad_opening(self):
# l1 asks for a too-long locktime # l1 asks for a too-long locktime

19
wallet/wallet.c

@ -1431,8 +1431,10 @@ void wallet_payment_set_status(struct wallet *wallet,
} }
} }
const struct wallet_payment **wallet_payment_list(const tal_t *ctx, const struct wallet_payment **
struct wallet *wallet) wallet_payment_list(const tal_t *ctx,
struct wallet *wallet,
const struct sha256 *payment_hash)
{ {
const struct wallet_payment **payments; const struct wallet_payment **payments;
sqlite3_stmt *stmt; sqlite3_stmt *stmt;
@ -1440,12 +1442,23 @@ const struct wallet_payment **wallet_payment_list(const tal_t *ctx,
size_t i; size_t i;
payments = tal_arr(ctx, const struct wallet_payment *, 0); payments = tal_arr(ctx, const struct wallet_payment *, 0);
if (payment_hash) {
stmt = db_prepare(
wallet->db,
"SELECT id, status, destination, "
"msatoshi, payment_hash, timestamp, payment_preimage, "
"path_secrets "
"FROM payments "
"WHERE payment_hash = ?;");
sqlite3_bind_sha256(stmt, 1, payment_hash);
} else {
stmt = db_prepare( stmt = db_prepare(
wallet->db, wallet->db,
"SELECT id, status, destination, " "SELECT id, status, destination, "
"msatoshi, payment_hash, timestamp, payment_preimage, " "msatoshi, payment_hash, timestamp, payment_preimage, "
"path_secrets " "path_secrets "
"FROM payments;"); "FROM payments;");
}
for (i = 0; sqlite3_step(stmt) == SQLITE_ROW; i++) { for (i = 0; sqlite3_step(stmt) == SQLITE_ROW; i++) {
tal_resize(&payments, i+1); tal_resize(&payments, i+1);
@ -1456,6 +1469,8 @@ const struct wallet_payment **wallet_payment_list(const tal_t *ctx,
/* Now attach payments not yet in db. */ /* Now attach payments not yet in db. */
list_for_each(&wallet->unstored_payments, p, list) { list_for_each(&wallet->unstored_payments, p, list) {
if (payment_hash && !structeq(&p->payment_hash, payment_hash))
continue;
tal_resize(&payments, i+1); tal_resize(&payments, i+1);
payments[i++] = p; payments[i++] = p;
} }

5
wallet/wallet.h

@ -592,8 +592,11 @@ struct secret *wallet_payment_get_secrets(const tal_t *ctx,
/** /**
* wallet_payment_list - Retrieve a list of payments * wallet_payment_list - Retrieve a list of payments
*
* payment_hash: optional filter for only this payment hash.
*/ */
const struct wallet_payment **wallet_payment_list(const tal_t *ctx, const struct wallet_payment **wallet_payment_list(const tal_t *ctx,
struct wallet *wallet); struct wallet *wallet,
const struct sha256 *payment_hash);
#endif /* WALLET_WALLET_H */ #endif /* WALLET_WALLET_H */

Loading…
Cancel
Save