Browse Source

offer: allow offers in other currencies if we can convert.

This avoids a footgun where they create an offer then we can't create
the invoice because they don't have a converter plugin.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa
Rusty Russell 4 years ago
committed by Christian Decker
parent
commit
9681d491df
  1. 72
      plugins/offers_offer.c
  2. 18
      tests/plugins/currencyUSDAUD5000.py
  3. 8
      tests/test_pay.py

72
plugins/offers_offer.c

@ -249,22 +249,57 @@ static struct command_result *param_invoice_payment_hash(struct command *cmd,
return NULL;
}
struct offer_info {
const struct tlv_offer *offer;
const char *label;
bool *single_use;
};
static struct command_result *create_offer(struct command *cmd,
struct offer_info *offinfo)
{
struct out_req *req;
/* We simply pass this through. */
req = jsonrpc_request_start(cmd->plugin, cmd, "createoffer",
forward_result, forward_error,
offinfo);
json_add_string(req->js, "bolt12",
offer_encode(tmpctx, offinfo->offer));
if (offinfo->label)
json_add_string(req->js, "label", offinfo->label);
json_add_bool(req->js, "single_use", *offinfo->single_use);
return send_outreq(cmd->plugin, req);
}
static struct command_result *currency_done(struct command *cmd,
const char *buf,
const jsmntok_t *result,
struct offer_info *offinfo)
{
/* Fail in this case, by forwarding warnings. */
if (!json_get_member(buf, result, "msat"))
return forward_error(cmd, buf, result, offinfo);
return create_offer(cmd, offinfo);
}
struct command_result *json_offer(struct command *cmd,
const char *buffer,
const jsmntok_t *params)
{
const char *desc, *vendor, *label;
const char *desc, *vendor;
struct tlv_offer *offer;
struct out_req *req;
bool *single_use;
struct offer_info *offinfo = tal(cmd, struct offer_info);
offer = tlv_offer_new(cmd);
offinfo->offer = offer = tlv_offer_new(offinfo);
if (!param(cmd, buffer, params,
p_req("amount", param_amount, offer),
p_req("description", param_escaped_string, &desc),
p_opt("vendor", param_escaped_string, &vendor),
p_opt("label", param_escaped_string, &label),
p_opt("label", param_escaped_string, &offinfo->label),
p_opt("quantity_min", param_u64, &offer->quantity_min),
p_opt("quantity_max", param_u64, &offer->quantity_max),
p_opt("absolute_expiry", param_u64, &offer->absolute_expiry),
@ -278,7 +313,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_def("single_use", param_bool,
&offinfo->single_use, false),
/* FIXME: hints support! */
NULL))
return command_param_failed();
@ -318,16 +354,22 @@ struct command_result *json_offer(struct command *cmd,
offer->node_id = tal_dup(offer, struct pubkey32, &id);
/* We simply pass this through. */
req = jsonrpc_request_start(cmd->plugin, cmd, "createoffer",
forward_result, forward_error,
offer);
json_add_string(req->js, "bolt12", offer_encode(tmpctx, offer));
if (label)
json_add_string(req->js, "label", label);
json_add_bool(req->js, "single_use", *single_use);
/* If they specify a different currency, warn if we can't
* convert it! */
if (offer->currency) {
struct out_req *req;
req = jsonrpc_request_start(cmd->plugin, cmd, "currencyconvert",
currency_done, forward_error,
offinfo);
json_add_u32(req->js, "amount", 1);
json_add_stringn(req->js, "currency",
(const char *)offer->currency,
tal_bytelen(offer->currency));
return send_outreq(cmd->plugin, req);
}
return send_outreq(cmd->plugin, req);
return create_offer(cmd, offinfo);
}
struct command_result *json_offerout(struct command *cmd,

18
tests/plugins/currencyUSDAUD5000.py

@ -0,0 +1,18 @@
#!/usr/bin/env python3
"""
This plugin is used to test the currency command
"""
from pyln.client import Plugin, Millisatoshi
plugin = Plugin()
@plugin.method("currencyconvert")
def currencyconvert(plugin, amount, currency):
"""Converts currency using given APIs."""
if currency in ('USD', 'AUD'):
return {"msat": Millisatoshi(round(amount * 5000))}
raise Exception("No values available for currency {}".format(currency.upper()))
plugin.run()

8
tests/test_pay.py

@ -3691,7 +3691,8 @@ def test_mpp_overload_payee(node_factory, bitcoind):
@unittest.skipIf(not EXPERIMENTAL_FEATURES, "offers are experimental")
def test_offer(node_factory, bitcoind):
l1 = node_factory.get_node()
plugin = os.path.join(os.path.dirname(__file__), 'plugins/currencyUSDAUD5000.py')
l1 = node_factory.get_node(options={'plugin': plugin})
bolt12tool = os.path.join(os.path.dirname(__file__), "..", "devtools", "bolt12-cli")
# Try different amount strings
@ -3719,6 +3720,11 @@ def test_offer(node_factory, bitcoind):
l1.rpc.call('offer', {'amount': '1.1AUD',
'description': 'test for invalid amount'})
# Make sure it fails on unknown currencies.
with pytest.raises(RpcError, match='No values available for currency EUR'):
l1.rpc.call('offer', {'amount': '1.00EUR',
'description': 'test for unknown currency'})
# Test label and description
weird_label = 'label \\ " \t \n'
weird_desc = 'description \\ " \t \n ナンセンス 1杯'

Loading…
Cancel
Save