Browse Source

invoice: provide bolt11 invoice.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 7 years ago
parent
commit
d577769350
  1. 34
      lightningd/invoice.c
  2. 41
      tests/test_lightningd.py

34
lightningd/invoice.c

@ -6,6 +6,7 @@
#include <ccan/tal/str/str.h>
#include <common/utils.h>
#include <inttypes.h>
#include <lightningd/bolt11.h>
#include <lightningd/log.h>
#include <sodium/randombytes.h>
@ -109,16 +110,19 @@ static void json_invoice(struct command *cmd,
const char *buffer, const jsmntok_t *params)
{
struct invoice *invoice;
jsmntok_t *msatoshi, *r, *label;
jsmntok_t *msatoshi, *r, *label, *desc;
struct json_result *response = new_json_result(cmd);
struct invoices *invs = cmd->ld->invoices;
struct bolt11 *b11;
char *b11enc;
if (!json_get_params(buffer, params,
"amount", &msatoshi,
"label", &label,
"description", &desc,
"?r", &r,
NULL)) {
command_fail(cmd, "Need {amount} and {label}");
command_fail(cmd, "Need {amount}, {label} and {description}");
return;
}
@ -171,6 +175,25 @@ static void json_invoice(struct command *cmd,
return;
}
/* Construct bolt11 string. */
b11 = new_bolt11(cmd, &invoice->msatoshi);
b11->chain = get_chainparams(cmd->ld);
b11->timestamp = time_now().ts.tv_sec;
b11->payment_hash = invoice->rhash;
b11->receiver_id = cmd->ld->id;
if (desc->end - desc->start >= BOLT11_FIELD_BYTE_LIMIT) {
b11->description_hash = tal(b11, struct sha256);
sha256(b11->description_hash, buffer + desc->start,
desc->end - desc->start);
} else
b11->description = tal_strndup(b11, buffer + desc->start,
desc->end - desc->start);
/* FIXME: add option to set this */
b11->expiry = 3600;
/* FIXME: add private routes if necessary! */
b11enc = bolt11_encode(cmd, cmd->ld, b11, false);
/* OK, connect it to main state, respond with hash */
tal_steal(invs, invoice);
list_add(&invs->invlist, &invoice->list);
@ -178,6 +201,9 @@ static void json_invoice(struct command *cmd,
json_object_start(response, NULL);
json_add_hex(response, "rhash",
&invoice->rhash, sizeof(invoice->rhash));
json_add_string(response, "bolt11", b11enc);
if (b11->description_hash)
json_add_string(response, "description", b11->description);
json_object_end(response);
command_success(cmd, response);
@ -186,8 +212,8 @@ static void json_invoice(struct command *cmd,
static const struct json_command invoice_command = {
"invoice",
json_invoice,
"Create invoice for {msatoshi} with {label} (with a set {r}, otherwise generate one)",
"Returns the {rhash} on success. "
"Create invoice for {msatoshi} with {label} and {description} (with a set {r}, otherwise generate one)",
"Returns the {rhash} and {bolt11} on success, and {description} if too alrge for {bolt11}. "
};
AUTODATA(json_command, &invoice_command);

41
tests/test_lightningd.py

@ -253,7 +253,7 @@ class LightningDTests(BaseLightningDTests):
if not label:
label = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(20))
rhash = ldst.rpc.invoice(amt, label)['rhash']
rhash = ldst.rpc.invoice(amt, label, label)['rhash']
assert ldst.rpc.listinvoice(label)[0]['complete'] == False
routestep = {
@ -281,6 +281,21 @@ class LightningDTests(BaseLightningDTests):
l1 = self.node_factory.get_node()
l1.rpc.stop()
def test_invoice(self):
l1 = self.node_factory.get_node()
before = int(time.time())
inv = l1.rpc.invoice(123000, 'label', 'description')
after = int(time.time())
b11 = l1.rpc.decodepay(inv['bolt11'])
assert b11['currency'] == 'tb'
assert b11['timestamp'] >= before
assert b11['timestamp'] <= after
assert b11['payment_hash'] == inv['rhash']
assert b11['description'] == 'description'
assert b11['expiry'] == 3600
assert b11['payee'] == l1.info['id']
def test_connect(self):
l1,l2 = self.connect()
@ -534,7 +549,7 @@ class LightningDTests(BaseLightningDTests):
self.fund_channel(l1, l2, 10**6)
amt = 200000000
rhash = l2.rpc.invoice(amt, 'testpayment2')['rhash']
rhash = l2.rpc.invoice(amt, 'testpayment2', 'desc')['rhash']
assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == False
routestep = {
@ -596,7 +611,7 @@ class LightningDTests(BaseLightningDTests):
assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == True
# Overpaying by "only" a factor of 2 succeeds.
rhash = l2.rpc.invoice(amt, 'testpayment3')['rhash']
rhash = l2.rpc.invoice(amt, 'testpayment3', 'desc')['rhash']
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)
@ -770,7 +785,7 @@ class LightningDTests(BaseLightningDTests):
self.fund_channel(l1, l2, 10**6)
# Must be dust!
rhash = l2.rpc.invoice(1, 'onchain_dust_out')['rhash']
rhash = l2.rpc.invoice(1, 'onchain_dust_out', 'desc')['rhash']
routestep = {
'msatoshi' : 1,
'id' : l2.info['id'],
@ -822,7 +837,7 @@ class LightningDTests(BaseLightningDTests):
l1.rpc.connect(l2.info['id'], 'localhost:{}'.format(l2.info['port']))
self.fund_channel(l1, l2, 10**6)
rhash = l2.rpc.invoice(10**8, 'onchain_timeout')['rhash']
rhash = l2.rpc.invoice(10**8, 'onchain_timeout', 'desc')['rhash']
# We underpay, so it fails.
routestep = {
'msatoshi' : 10**8 - 1,
@ -885,7 +900,7 @@ class LightningDTests(BaseLightningDTests):
self.pay(l2, l1, 2 * 10**8)
# Must be bigger than dust!
rhash = l3.rpc.invoice(10**8, 'middleman')['rhash']
rhash = l3.rpc.invoice(10**8, 'middleman', 'desc')['rhash']
# Wait for route propagation.
l1.bitcoin.rpc.generate(5)
l1.daemon.wait_for_log('Received node_announcement for node {}'
@ -1348,7 +1363,7 @@ class LightningDTests(BaseLightningDTests):
assert l2.rpc.getpeer(l1.info['id'])['channel'] == chanid1
assert l3.rpc.getpeer(l2.info['id'])['channel'] == chanid2
rhash = l3.rpc.invoice(100000000, 'testpayment1')['rhash']
rhash = l3.rpc.invoice(100000000, 'testpayment1', 'desc')['rhash']
assert l3.rpc.listinvoice('testpayment1')[0]['complete'] == False
# Fee for node2 is 10 millionths, plus 1.
@ -1496,7 +1511,7 @@ class LightningDTests(BaseLightningDTests):
assert route[1]['msatoshi'] == 4999999
assert route[1]['delay'] == 9 + shadow_route
rhash = l3.rpc.invoice(4999999, 'test_forward_different_fees_and_cltv')['rhash']
rhash = l3.rpc.invoice(4999999, 'test_forward_different_fees_and_cltv', 'desc')['rhash']
assert l3.rpc.listinvoice('test_forward_different_fees_and_cltv')[0]['complete'] == False
# This should work.
@ -1560,7 +1575,7 @@ class LightningDTests(BaseLightningDTests):
route[1]['delay'] += 10
# This should work.
rhash = l3.rpc.invoice(4999999, 'test_forward_pad_fees_and_cltv')['rhash']
rhash = l3.rpc.invoice(4999999, 'test_forward_pad_fees_and_cltv', 'desc')['rhash']
l1.rpc.sendpay(to_json(route), rhash)
assert l3.rpc.listinvoice('test_forward_pad_fees_and_cltv')[0]['complete'] == True
@ -1726,7 +1741,7 @@ class LightningDTests(BaseLightningDTests):
self.fund_channel(l1, l2, 10**6)
amt = 200000000
rhash = l2.rpc.invoice(amt, 'test_reconnect_sender_add1')['rhash']
rhash = l2.rpc.invoice(amt, 'test_reconnect_sender_add1', 'desc')['rhash']
assert l2.rpc.listinvoice('test_reconnect_sender_add1')[0]['complete'] == False
route = [ { 'msatoshi' : amt, 'id' : l2.info['id'], 'delay' : 5, 'channel': '1:1:1'} ]
@ -1754,7 +1769,7 @@ class LightningDTests(BaseLightningDTests):
self.fund_channel(l1, l2, 10**6)
amt = 200000000
rhash = l2.rpc.invoice(amt, 'testpayment')['rhash']
rhash = l2.rpc.invoice(amt, 'testpayment', 'desc')['rhash']
assert l2.rpc.listinvoice('testpayment')[0]['complete'] == False
route = [ { 'msatoshi' : amt, 'id' : l2.info['id'], 'delay' : 5, 'channel': '1:1:1'} ]
@ -1780,7 +1795,7 @@ class LightningDTests(BaseLightningDTests):
self.fund_channel(l1, l2, 10**6)
amt = 200000000
rhash = l2.rpc.invoice(amt, 'testpayment2')['rhash']
rhash = l2.rpc.invoice(amt, 'testpayment2', 'desc')['rhash']
assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == False
route = [ { 'msatoshi' : amt, 'id' : l2.info['id'], 'delay' : 5, 'channel': '1:1:1'} ]
@ -1810,7 +1825,7 @@ class LightningDTests(BaseLightningDTests):
self.fund_channel(l1, l2, 10**6)
amt = 200000000
rhash = l2.rpc.invoice(amt, 'testpayment2')['rhash']
rhash = l2.rpc.invoice(amt, 'testpayment2', 'desc')['rhash']
assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == False
route = [ { 'msatoshi' : amt, 'id' : l2.info['id'], 'delay' : 5, 'channel': '1:1:1'} ]

Loading…
Cancel
Save