From e45eac79fd5a55aa2af0b2e49619f5c17862aef5 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 30 Nov 2020 15:04:24 +0100 Subject: [PATCH 1/2] keysend: Check that the destination supports keysend upon init We were blindly initiating the keysend payment, which could lead to confusing outcomes. This adds a very specific error message to the error returned. Changelog-Fixed: keysend: Keysend now checks whether the destination supports keysend before attempting a payment. If not a more informative error is returned. --- plugins/keysend.c | 19 +++++++++++++++++++ tests/test_pay.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/plugins/keysend.c b/plugins/keysend.c index 1dac7575219b..ed8f2a9ae567 100644 --- a/plugins/keysend.c +++ b/plugins/keysend.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,24 @@ static struct keysend_data *keysend_init(struct payment *p) static void keysend_cb(struct keysend_data *d, struct payment *p) { struct createonion_hop *last_payload; size_t hopcount; + struct gossmap_node *node; + bool enabled; + const struct gossmap *gossmap = get_gossmap(p->plugin); + + /* On the root payment we perform the featurebit check. */ + if (p->parent == NULL && p->step == PAYMENT_STEP_INITIALIZED) { + node = gossmap_find_node(gossmap, p->destination); + + enabled = gossmap_node_get_feature(gossmap, node, + KEYSEND_FEATUREBIT) != -1; + if (!enabled) + return payment_fail( + p, + "Recipient %s does not support keysend payments " + "(feature bit %d missing in node announcement)", + node_id_to_hexstr(tmpctx, p->destination), + KEYSEND_FEATUREBIT); + } if (p->step != PAYMENT_STEP_ONION_PAYLOAD) return payment_continue(p); diff --git a/tests/test_pay.py b/tests/test_pay.py index e5b20f2b1a0b..84d1142c7fe1 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -3373,7 +3373,7 @@ def test_listpay_result_with_paymod(node_factory, bitcoind): amount_sat = 10 ** 6 - l1, l2, l3 = node_factory.line_graph(3) + l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True) invl2 = l2.rpc.invoice(amount_sat * 2, "inv_l2", "inv_l2") l1.rpc.pay(invl2['bolt11']) From ac9f7dd24a03579803e6503aaf078432b76d718e Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 30 Nov 2020 15:05:39 +0100 Subject: [PATCH 2/2] pytest: Add test for keysend feature support --- tests/test_pay.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/test_pay.py b/tests/test_pay.py index 84d1142c7fe1..944266f0398c 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -2990,13 +2990,22 @@ def test_excluded_adjacent_routehint(node_factory, bitcoind, compat): def test_keysend(node_factory): amt = 10000 - l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True) + l1, l2, l3, l4 = node_factory.line_graph( + 4, + wait_for_announce=True, + opts=[{}, {}, {}, {'disable-plugin': 'keysend'}] + ) # The keysend featurebit must be set in the announcement, i.e., l1 should # learn that l3 supports keysends. features = l1.rpc.listnodes(l3.info['id'])['nodes'][0]['features'] assert(int(features, 16) >> 55 & 0x01 == 1) + # If we disable keysend, then the featurebit must not be set, + # i.e., l4 doesn't support it. + features = l1.rpc.listnodes(l4.info['id'])['nodes'][0]['features'] + assert(int(features, 16) >> 55 & 0x01 == 0) + # Send an indirect one from l1 to l3 l1.rpc.keysend(l3.info['id'], amt) invs = l3.rpc.listinvoices()['invoices'] @@ -3014,6 +3023,11 @@ def test_keysend(node_factory): inv = invs[0] assert(inv['msatoshi_received'] >= amt) + # And finally try to send a keysend payment to l4, which doesn't + # support it. It MUST fail. + with pytest.raises(RpcError, match=r"Recipient [0-9a-f]{66} does not support keysend payments"): + l3.rpc.keysend(l4.info['id'], amt) + def test_invalid_onion_channel_update(node_factory): '''