JSONRPC: listpayments can list just a specific bolt11 or payment_hash.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
06c4f6ddca
commit
6e703ad977
|
@ -441,8 +441,49 @@ static void json_listpayments(struct command *cmd, const char *buffer,
|
|||
{
|
||||
const struct wallet_payment **payments;
|
||||
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);
|
||||
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 = {
|
||||
"listpayments",
|
||||
json_listpayments,
|
||||
"Get a list of incoming and outgoing payments",
|
||||
"Returns a list of payments with {payment_hash}, {destination}, {msatoshi}, {timestamp} and {status}"
|
||||
"Get a list of outgoing payments",
|
||||
"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);
|
||||
|
|
|
@ -678,7 +678,7 @@ class LightningDTests(BaseLightningDTests):
|
|||
assert p2['msatoshi_total'] == 10**6 * 1000
|
||||
|
||||
# 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]['pay_index'] == 1
|
||||
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)
|
||||
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')
|
||||
assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == True
|
||||
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']
|
||||
assert l2.rpc.listinvoice('testpayment3')[0]['complete'] == False
|
||||
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]['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):
|
||||
l1,l2 = self.connect()
|
||||
|
@ -744,7 +752,7 @@ class LightningDTests(BaseLightningDTests):
|
|||
|
||||
inv = l2.rpc.invoice(123000, 'test_pay', 'description')['bolt11']
|
||||
before = int(time.time())
|
||||
l1.rpc.pay(inv)
|
||||
preimage = l1.rpc.pay(inv)
|
||||
after = int(time.time())
|
||||
invoice = l2.rpc.listinvoice('test_pay')[0]
|
||||
assert invoice['complete'] == True
|
||||
|
@ -764,11 +772,18 @@ class LightningDTests(BaseLightningDTests):
|
|||
# Check payment of any-amount invoice.
|
||||
for i in range(5):
|
||||
label = "any{}".format(i)
|
||||
inv = l2.rpc.invoice("any", label, 'description')['bolt11']
|
||||
inv2 = l2.rpc.invoice("any", label, 'description')['bolt11']
|
||||
# Must provide an amount!
|
||||
self.assertRaises(ValueError, l1.rpc.pay, inv)
|
||||
self.assertRaises(ValueError, l1.rpc.pay, inv, None)
|
||||
l1.rpc.pay(inv, random.randint(1000, 999999))
|
||||
self.assertRaises(ValueError, l1.rpc.pay, inv2)
|
||||
self.assertRaises(ValueError, l1.rpc.pay, inv2, None)
|
||||
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):
|
||||
# l1 asks for a too-long locktime
|
||||
|
@ -1785,7 +1800,7 @@ class LightningDTests(BaseLightningDTests):
|
|||
assert c[1]['short_channel_id'] == scid
|
||||
assert c[0]['source'] == c[1]['destination']
|
||||
assert c[1]['source'] == c[0]['destination']
|
||||
|
||||
|
||||
@unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1 for --dev-broadcast-interval")
|
||||
def test_forward_pad_fees_and_cltv(self):
|
||||
"""Test that we are allowed extra locktime delta, and fees"""
|
||||
|
@ -2602,7 +2617,7 @@ class LightningDTests(BaseLightningDTests):
|
|||
|
||||
# FIXME: We should re-add pre-announced routes on startup!
|
||||
self.wait_for_routes(l1, [chanid])
|
||||
|
||||
|
||||
# A duplicate should succeed immediately (nop) and return correct preimage.
|
||||
preimage = l1.rpc.pay(inv1['bolt11'])['preimage']
|
||||
assert l1.rpc.dev_rhash(preimage)['rhash'] == inv1['payment_hash']
|
||||
|
@ -2646,7 +2661,7 @@ class LightningDTests(BaseLightningDTests):
|
|||
|
||||
# Another attempt should also fail.
|
||||
self.assertRaises(ValueError, l1.rpc.pay, inv1['bolt11'])
|
||||
|
||||
|
||||
@unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1")
|
||||
def test_payment_duplicate_uncommitted(self):
|
||||
# We want to test two payments at the same time, before we send commit
|
||||
|
|
|
@ -1431,8 +1431,10 @@ void wallet_payment_set_status(struct wallet *wallet,
|
|||
}
|
||||
}
|
||||
|
||||
const struct wallet_payment **wallet_payment_list(const tal_t *ctx,
|
||||
struct wallet *wallet)
|
||||
const struct wallet_payment **
|
||||
wallet_payment_list(const tal_t *ctx,
|
||||
struct wallet *wallet,
|
||||
const struct sha256 *payment_hash)
|
||||
{
|
||||
const struct wallet_payment **payments;
|
||||
sqlite3_stmt *stmt;
|
||||
|
@ -1440,12 +1442,23 @@ const struct wallet_payment **wallet_payment_list(const tal_t *ctx,
|
|||
size_t i;
|
||||
|
||||
payments = tal_arr(ctx, const struct wallet_payment *, 0);
|
||||
stmt = db_prepare(
|
||||
wallet->db,
|
||||
"SELECT id, status, destination, "
|
||||
"msatoshi, payment_hash, timestamp, payment_preimage, "
|
||||
"path_secrets "
|
||||
"FROM payments;");
|
||||
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(
|
||||
wallet->db,
|
||||
"SELECT id, status, destination, "
|
||||
"msatoshi, payment_hash, timestamp, payment_preimage, "
|
||||
"path_secrets "
|
||||
"FROM payments;");
|
||||
}
|
||||
|
||||
for (i = 0; sqlite3_step(stmt) == SQLITE_ROW; i++) {
|
||||
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. */
|
||||
list_for_each(&wallet->unstored_payments, p, list) {
|
||||
if (payment_hash && !structeq(&p->payment_hash, payment_hash))
|
||||
continue;
|
||||
tal_resize(&payments, i+1);
|
||||
payments[i++] = p;
|
||||
}
|
||||
|
|
|
@ -592,8 +592,11 @@ struct secret *wallet_payment_get_secrets(const tal_t *ctx,
|
|||
|
||||
/**
|
||||
* 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,
|
||||
struct wallet *wallet);
|
||||
struct wallet *wallet,
|
||||
const struct sha256 *payment_hash);
|
||||
|
||||
#endif /* WALLET_WALLET_H */
|
||||
|
|
Loading…
Reference in New Issue