mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 05:12:45 +01:00
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>
This commit is contained in:
parent
2de467274e
commit
9681d491df
@ -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;
|
||||
|
||||
return send_outreq(cmd->plugin, 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 create_offer(cmd, offinfo);
|
||||
}
|
||||
|
||||
struct command_result *json_offerout(struct command *cmd,
|
||||
|
18
tests/plugins/currencyUSDAUD5000.py
Executable file
18
tests/plugins/currencyUSDAUD5000.py
Executable file
@ -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()
|
@ -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…
Reference in New Issue
Block a user