Browse Source

plugins/offer: allow `offer` to create `send_invoice`, `refund` and `single_use` offers.

refund offers are implied send_invoice offers.  And send_invoice offers
are implied single-use offers, though it can also make sense to have
a non-send_invoice offer be single-use.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa
Rusty Russell 4 years ago
parent
commit
43f5304539
  1. 58
      plugins/offers_offer.c
  2. 15
      tests/test_pay.py

58
plugins/offers_offer.c

@ -199,6 +199,32 @@ static struct command_result *param_recurrence_paywindow(struct command *cmd,
return NULL;
}
static struct command_result *param_invoice_payment_hash(struct command *cmd,
const char *name,
const char *buffer,
const jsmntok_t *tok,
struct sha256 **hash)
{
struct tlv_invoice *inv;
char *fail;
inv = invoice_decode(tmpctx, buffer + tok->start, tok->end - tok->start,
plugin_feature_set(cmd->plugin), chainparams,
&fail);
if (!inv)
return command_fail_badparam(cmd, name, buffer, tok,
tal_fmt(cmd,
"Unparsable invoice: %s",
fail));
if (!inv->payment_hash)
return command_fail_badparam(cmd, name, buffer, tok,
"invoice missing payment_hash");
*hash = tal_steal(cmd, inv->payment_hash);
return NULL;
}
struct command_result *json_offer(struct command *cmd,
const char *buffer,
const jsmntok_t *params)
@ -206,13 +232,14 @@ struct command_result *json_offer(struct command *cmd,
const char *desc, *vendor, *label;
struct tlv_offer *offer;
struct out_req *req;
bool *single_use;
bool *single_use, *send_invoice;
offer = tlv_offer_new(cmd);
if (!param(cmd, buffer, params,
p_req("amount", param_amount, offer),
p_req("description", param_escaped_string, &desc),
p_opt("send_invoice", param_bool, &send_invoice),
p_opt("label", param_escaped_string, &label),
p_opt("vendor", param_escaped_string, &vendor),
p_opt("quantity_min", param_u64, &offer->quantity_min),
@ -228,7 +255,8 @@ struct command_result *json_offer(struct command *cmd,
p_opt("recurrence_limit",
param_number,
&offer->recurrence_limit),
p_opt_def("single_use", param_bool, &single_use, false),
p_opt("refund_for", param_invoice_payment_hash, &offer->refund_for),
p_opt("single_use", param_bool, &single_use),
/* FIXME: hints support! */
NULL))
return command_param_failed();
@ -245,6 +273,32 @@ struct command_result *json_offer(struct command *cmd,
offer->chains[0] = chainparams->genesis_blockhash;
}
/* If refund_for, send_invoice is true. */
if (offer->refund_for) {
if (!send_invoice) {
send_invoice = tal(cmd, bool);
*send_invoice = true;
}
if (!*send_invoice)
return command_fail_badparam(cmd, "refund_for",
buffer, params,
"needs send_invoice=true");
} else {
if (!send_invoice) {
send_invoice = tal(cmd, bool);
*send_invoice = false;
}
}
if (*send_invoice)
offer->send_invoice = tal(offer, struct tlv_offer_send_invoice);
/* single_use defaults to 'true' for send_invoices, false otherwise */
if (!single_use) {
single_use = tal(cmd, bool);
*single_use = offer->send_invoice ? true : false;
}
if (!offer->recurrence) {
if (offer->recurrence_limit)
return command_fail_badparam(cmd, "recurrence_limit",

15
tests/test_pay.py

@ -3952,3 +3952,18 @@ def test_pay_waitblockheight_timeout(node_factory, bitcoind):
# Should have only one attempt that triggered the wait, which then failed.
assert len(status['pay']) == 1
assert len(status['pay'][0]['attempts']) == 1
@unittest.skipIf(not EXPERIMENTAL_FEATURES, "offers are experimental")
def test_sendinvoice(node_factory, bitcoind):
l1, l2 = node_factory.line_graph(2, wait_for_announce=True)
# Simple offer to send money (balances channel a little)
offer = l1.rpc.call('offer', {'amount': '100000sat',
'description': 'simple test',
'send_invoice': True})['bolt12']
print(offer)
# Fetchinvoice will refuse, since you're supposed to send an invoice.
with pytest.raises(RpcError, match='Offer wants an invoice, not invoice_request'):
l2.rpc.call('fetchinvoice', {'offer': offer})

Loading…
Cancel
Save