From ce0f5440733901984672757c9e4b1f93854c4e97 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 21 Sep 2022 13:27:37 +0930 Subject: [PATCH] keysend: try to find description in TLV. "Who needs specs?" FFS... Signed-off-by: Rusty Russell Changelog-Added: Protocol: `keysend` will now attach the longest valid text field in the onion to the invoice (so you can have Sphinx.chat users spam you!) --- plugins/keysend.c | 27 +++++++++++++++++++++------ tests/test_pay.py | 26 +++++++++++++++++++++----- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/plugins/keysend.c b/plugins/keysend.c index 47fdc8fd5..1a88b7ca5 100644 --- a/plugins/keysend.c +++ b/plugins/keysend.c @@ -341,9 +341,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(); @@ -387,15 +386,21 @@ static struct command_result *htlc_accepted_call(struct command *cmd, } /* Try looking for the field that contains the preimage */ - for (int i=0; ifields); 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 @@ -464,7 +469,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); diff --git a/tests/test_pay.py b/tests/test_pay.py index 72745d5f4..706a9cd86 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -3585,17 +3585,33 @@ def test_keysend_extra_tlvs(node_factory): ] ) - # Send an indirect one from l1 to l3 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):