Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

keysend text fields #5619

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion doc/lightning-keysend.7.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ lightning-keysend -- Send funds to a node without an invoice
SYNOPSIS
--------

**keysend** *destination* *msatoshi* [*label*] [*maxfeepercent*] [*retry\_for*] [*maxdelay*] [*exemptfee*]
**keysend** *destination* *msatoshi* [*label*] [*maxfeepercent*] [*retry\_for*] [*maxdelay*] [*exemptfee*] [*extratlvs*]

DESCRIPTION
-----------
Expand Down Expand Up @@ -36,6 +36,8 @@ Unlike lightning-pay(7), issuing the same `keysend` commands multiple times will
Until *retry_for* seconds passes (default: 60), the command will keep finding routes and retrying the payment.
However, a payment may be delayed for up to `maxdelay` blocks by another node; clients should be prepared for this worst case.

*extratlvs* is an optional dictionary of additional fields to insert into the final tlv. The format is 'fieldnumber': 'hexstring'.

When using *lightning-cli*, you may skip optional parameters by using
*null*. Alternatively, use **-k** option to provide parameters by name.

Expand Down
6 changes: 3 additions & 3 deletions doc/lightning-listconfigs.7.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ On success, an object is returned, containing:
- **log-timestamps** (boolean, optional): `log-timestamps` field from config or cmdline, or default
- **force-feerates** (string, optional): force-feerate configuration setting, if any
- **subdaemon** (string, optional): `subdaemon` fields from config or cmdline if any (can be more than one)
- **fetchinvoice-noconnect** (boolean, optional): `featchinvoice-noconnect` fileds from config or cmdline, or default
- **fetchinvoice-noconnect** (boolean, optional): `fetchinvoice-noconnect` fields from config or cmdline, or default
- **accept-htlc-tlv-types** (string, optional): `accept-extra-tlvs-type` fields from config or cmdline, or not present
- **tor-service-password** (string, optional): `tor-service-password` field from config or cmdline, if any
- **dev-allowdustreserve** (boolean, optional): Whether we allow setting dust reserves

Expand Down Expand Up @@ -217,5 +218,4 @@ RESOURCES
---------

Main web site: <https://github.com/ElementsProject/lightning>

[comment]: # ( SHA256STAMP:310cc00ef62e7075d5d2588b0492c2dd96f507cc739f67d56ccc6c4f3135bca5)
[comment]: # ( SHA256STAMP:5871ac751654339ed65ab905d61f0bc3afbb8576a33a5c4e9a73d2084f438582)
6 changes: 6 additions & 0 deletions doc/lightningd-config.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,12 @@ the outgoing is redeemed.
might need to redeem this on-chain, so this is the number of blocks we
have to do that.

* **accept-htlc-tlv-types**=*types*

Normally HTLC onions which contain unknown even fields are rejected.
This option specifies that these (comma-separated) types are to be
accepted, and ignored.

### Invoice control options:

* **autocleaninvoice-cycle**=*SECONDS* [plugin `autoclean`]
Expand Down
6 changes: 5 additions & 1 deletion doc/schemas/listconfigs.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,11 @@
},
"fetchinvoice-noconnect": {
"type": "boolean",
"description": "`featchinvoice-noconnect` fileds from config or cmdline, or default"
"description": "`fetchinvoice-noconnect` fields from config or cmdline, or default"
},
"accept-htlc-tlv-types": {
"type": "string",
"description": "`accept-extra-tlvs-type` fields from config or cmdline, or not present"
},
"tor-service-password": {
"type": "string",
Expand Down
3 changes: 1 addition & 2 deletions doc/undoc-flags.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"flags": [
"experimental-accept-extra-tlv-types",
"channel-fee-max-base-msat",
"channel-fee-max-proportional-thousandths",
"funder-fund-probability",
Expand All @@ -18,4 +17,4 @@
"lease-fee-basis",
"lease-funding-weight"
]
}
}
29 changes: 16 additions & 13 deletions lightningd/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,10 @@ static char *fmt_force_feerates(const tal_t *ctx, const u32 *force_feerates)
return ret;
}

#if EXPERIMENTAL_FEATURES
static char *opt_set_accept_extra_tlv_types(const char *arg,
struct lightningd *ld)
struct lightningd *ld)
{
char *endp, **elements = tal_strsplit(NULL, arg, ",", STR_NO_EMPTY);;
char *endp, **elements = tal_strsplit(NULL, arg, ",", STR_NO_EMPTY);
unsigned long long l;
u64 u;
for (int i = 0; elements[i] != NULL; i++) {
Expand All @@ -193,7 +192,6 @@ static char *opt_set_accept_extra_tlv_types(const char *arg,
tal_free(elements);
return NULL;
}
#endif

/* Returns the number of wireaddr types already announced */
static size_t num_announced_types(enum wire_addr_type type, struct lightningd *ld)
Expand Down Expand Up @@ -1182,11 +1180,9 @@ static void register_opts(struct lightningd *ld)
&ld->tor_service_password,
"Set a Tor hidden service password");

#if EXPERIMENTAL_FEATURES
opt_register_arg("--experimental-accept-extra-tlv-types",
opt_register_arg("--accept-htlc-tlv-types",
opt_set_accept_extra_tlv_types, NULL, ld,
"Comma separated list of extra TLV types to accept.");
#endif
"Comma separated list of extra HTLC TLV types to accept.");

opt_register_early_noarg("--disable-dns", opt_set_invbool, &ld->config.use_dns,
"Disable DNS lookups of peers");
Expand Down Expand Up @@ -1509,7 +1505,7 @@ static void add_config(struct lightningd *ld,
const char *name, size_t len)
{
char *name0 = tal_strndup(tmpctx, name, len);
const char *answer = NULL;
char *answer = NULL;
char buf[OPT_SHOW_LEN + sizeof("...")];

#if DEVELOPER
Expand Down Expand Up @@ -1605,7 +1601,7 @@ static void add_config(struct lightningd *ld,
if (ld->rgb)
answer = tal_hexstr(name0, ld->rgb, 3);
} else if (opt->cb_arg == (void *)opt_set_alias) {
answer = (const char *)ld->alias;
answer = (char *)ld->alias;
} else if (opt->cb_arg == (void *)arg_log_to_file) {
if (ld->logfiles)
json_add_opt_log_to_files(response, name0, ld->logfiles);
Expand Down Expand Up @@ -1668,10 +1664,17 @@ static void add_config(struct lightningd *ld,
fmt_amount_msat(tmpctx,
*(struct amount_msat *)
opt->u.carg));
#if EXPERIMENTAL_FEATURES
} else if (opt->cb_arg == (void *)opt_set_accept_extra_tlv_types) {
/* TODO Actually print the option */
#endif
for (size_t i = 0;
i < tal_count(ld->accept_extra_tlv_types);
i++) {
if (i == 0)
answer = tal_fmt(name0, "%"PRIu64,
ld->accept_extra_tlv_types[i]);
else
tal_append_fmt(&answer, ",%"PRIu64,
ld->accept_extra_tlv_types[i]);
}
#if DEVELOPER
} else if (strstarts(name, "dev-")) {
/* Ignore dev settings */
Expand Down
47 changes: 23 additions & 24 deletions plugins/keysend.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@ static struct keysend_data *keysend_init(struct payment *p)
randombytes_buf(&d->preimage, sizeof(d->preimage));
ccan_sha256(&payment_hash, &d->preimage, sizeof(d->preimage));
p->payment_hash = tal_dup(p, struct sha256, &payment_hash);
#if EXPERIMENTAL_FEATURES
d->extra_tlvs = NULL;
#endif
return d;
} else {
/* If we are a child payment (retry or split) we copy the
Expand Down Expand Up @@ -92,15 +90,13 @@ static void keysend_cb(struct keysend_data *d, struct payment *p) {
tlvstream_set_raw(&last_payload->tlv_payload->fields, PREIMAGE_TLV_TYPE,
&d->preimage, sizeof(struct preimage));

#if EXPERIMENTAL_FEATURES
if (d->extra_tlvs != NULL) {
for (size_t i = 0; i < tal_count(d->extra_tlvs); i++) {
struct tlv_field *f = &d->extra_tlvs[i];
tlvstream_set_raw(&last_payload->tlv_payload->fields,
f->numtype, f->value, f->length);
}
}
#endif

return payment_continue(p);
}
Expand Down Expand Up @@ -148,9 +144,7 @@ static struct command_result *json_keysend(struct command *cmd, const char *buf,
u32 *maxdelay;
unsigned int *retryfor;
struct route_info **hints;
#if EXPERIMENTAL_FEATURES
struct tlv_field *extra_fields;
#endif

#if DEVELOPER
bool *use_shadow;
Expand All @@ -165,12 +159,10 @@ static struct command_result *json_keysend(struct command *cmd, const char *buf,
p_opt_def("maxdelay", param_number, &maxdelay,
maxdelay_default),
p_opt_def("exemptfee", param_msat, &exemptfee, AMOUNT_MSAT(5000)),
p_opt("extratlvs", param_extra_tlvs, &extra_fields),
p_opt("routehints", param_routehint_array, &hints),
#if DEVELOPER
p_opt_def("use_shadow", param_bool, &use_shadow, true),
#endif
p_opt("routehints", param_routehint_array, &hints),
#if EXPERIMENTAL_FEATURES
p_opt("extratlvs", param_extra_tlvs, &extra_fields),
#endif
NULL))
return command_param_failed();
Expand Down Expand Up @@ -210,10 +202,8 @@ static struct command_result *json_keysend(struct command *cmd, const char *buf,

p->constraints.cltv_budget = *maxdelay;

#if EXPERIMENTAL_FEATURES
payment_mod_keysend_get_data(p)->extra_tlvs =
tal_steal(p, extra_fields);
#endif

payment_mod_exemptfee_get_data(p)->amount = *exemptfee;
#if DEVELOPER
Expand Down Expand Up @@ -341,9 +331,8 @@ static struct command_result *htlc_accepted_call(struct command *cmd,
struct sha256 payment_hash;
size_t max;
struct tlv_tlv_payload *payload;
struct tlv_field *preimage_field = NULL, *unknown_field = NULL;
struct tlv_field *preimage_field = NULL, *unknown_field = NULL, *desc_field = NULL;
bigsize_t s;
struct tlv_field *field;
struct keysend_in *ki;
struct out_req *req;
struct timeabs now = time_now();
Expand All @@ -366,14 +355,8 @@ static struct command_result *htlc_accepted_call(struct command *cmd,
return htlc_accepted_continue(cmd, NULL);
}

#if EXPERIMENTAL_FEATURES
/* Note: This is a magic pointer value, not an actual array */
allowed = cast_const(u64 *, FROMWIRE_TLV_ANY_TYPE);
#else
/* We explicitly allow our type. */
allowed = tal_arr(cmd, u64, 1);
allowed[0] = PREIMAGE_TLV_TYPE;
#endif

payload = tlv_tlv_payload_new(cmd);
if (!fromwire_tlv(&rawpayload, &max, tlvs_tlv_tlv_payload, TLVS_ARRAY_SIZE_tlv_tlv_payload,
Expand All @@ -387,15 +370,21 @@ static struct command_result *htlc_accepted_call(struct command *cmd,
}

/* Try looking for the field that contains the preimage */
for (int i=0; i<tal_count(payload->fields); i++) {
field = &payload->fields[i];
for (size_t i = 0; i < tal_count(payload->fields); i++) {
struct tlv_field *field = &payload->fields[i];
if (field->numtype == PREIMAGE_TLV_TYPE) {
preimage_field = field;
break;
continue;
} else if (field->numtype % 2 == 0 && field->meta == NULL) {
/* This can only happen with FROMWIRE_TLV_ANY_TYPE! */
unknown_field = field;
}

/* Longest (unknown) text field wins. */
if (!field->meta
&& utf8_check(field->value, field->length)
&& (!desc_field || field->length > desc_field->length))
desc_field = field;
}

/* If we don't have a preimage field then this is not a keysend, let
Expand Down Expand Up @@ -464,7 +453,17 @@ static struct command_result *htlc_accepted_call(struct command *cmd,
plugin_log(cmd->plugin, LOG_INFORM, "Inserting a new invoice for keysend with payment_hash %s", type_to_string(tmpctx, struct sha256, &payment_hash));
json_add_string(req->js, "amount_msat", "any");
json_add_string(req->js, "label", ki->label);
json_add_string(req->js, "description", "Spontaneous incoming payment through keysend");
if (desc_field) {
const char *desc = tal_fmt(tmpctx, "keysend: %.*s",
(int)desc_field->length,
(const char *)desc_field->value);
json_add_string(req->js, "description", desc);
/* Don't exceed max possible desc length! */
if (strlen(desc) > 1023)
json_add_bool(req->js, "deschashonly", true);
} else {
json_add_string(req->js, "description", "keysend");
}
json_add_preimage(req->js, "preimage", &ki->payment_preimage);

return send_outreq(cmd->plugin, req);
Expand Down
38 changes: 30 additions & 8 deletions tests/test_pay.py
Original file line number Diff line number Diff line change
Expand Up @@ -3568,7 +3568,6 @@ def test_keysend(node_factory):
l3.rpc.keysend(l4.info['id'], amt)


@unittest.skipIf(not EXPERIMENTAL_FEATURES, "Requires extratlvs option")
def test_keysend_extra_tlvs(node_factory):
"""Use the extratlvs option to deliver a message with sphinx' TLV type.
"""
Expand All @@ -3577,25 +3576,48 @@ def test_keysend_extra_tlvs(node_factory):
2,
wait_for_announce=True,
opts=[
{},
{
'experimental-accept-extra-tlv-types': '133773310',
# Not needed, just for listconfigs test.
'accept-htlc-tlv-types': '133773310,99990',
},
{
'accept-htlc-tlv-types': '133773310',
"plugin": os.path.join(os.path.dirname(__file__), "plugins/sphinx-receiver.py"),
},
]
)

# Send an indirect one from l1 to l3
# Make sure listconfigs works here
assert l1.rpc.listconfigs()['accept-htlc-tlv-types'] == '133773310,99990'
assert l2.rpc.listconfigs()['accept-htlc-tlv-types'] == '133773310'

l1.rpc.keysend(l2.info['id'], amt, extratlvs={133773310: 'FEEDC0DE'})
invs = l2.rpc.listinvoices()['invoices']
assert(len(invs) == 1)
inv = only_one(l2.rpc.listinvoices()['invoices'])
assert(l2.daemon.is_in_log(r'plugin-sphinx-receiver.py.*extratlvs.*133773310.*feedc0de'))

inv = invs[0]
assert(inv['amount_received_msat'] >= Millisatoshi(amt))
assert inv['description'] == 'keysend'
l2.rpc.delinvoice(inv['label'], 'paid')

# Now try again with the TLV type in extra_tlvs as string:
l1.rpc.keysend(l2.info['id'], amt, extratlvs={133773310: 'FEEDC0DE'})
l1.rpc.keysend(l2.info['id'], amt, extratlvs={133773310: b'hello there'.hex()})
inv = only_one(l2.rpc.listinvoices()['invoices'])
assert inv['description'] == 'keysend: hello there'
l2.rpc.delinvoice(inv['label'], 'paid')

# We can (just!) fit a giant description in.
l1.rpc.keysend(l2.info['id'], amt, extratlvs={133773310: (b'a' * 1100).hex()})
inv = only_one(l2.rpc.listinvoices()['invoices'])
assert inv['description'] == 'keysend: ' + 'a' * 1100
l2.rpc.delinvoice(inv['label'], 'paid')

# Now try with some special characters
ksinfo = """💕 ₿"'
More info
"""
l1.rpc.keysend(l2.info['id'], amt, extratlvs={133773310: bytes(ksinfo, encoding='utf8').hex()})
inv = only_one(l2.rpc.listinvoices()['invoices'])
assert inv['description'] == 'keysend: ' + ksinfo


def test_keysend_routehint(node_factory):
Expand Down