Skip to content
Open
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
2 changes: 2 additions & 0 deletions common/gossmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,8 @@ bool gossmap_local_addchan(struct gossmap_localmods *localmods,
u64 off;
struct localmod mod;

assert(!node_id_eq(n1, n2));

/* Don't create duplicate channels. */
if (find_localmod(localmods, scid))
return false;
Expand Down
1 change: 1 addition & 0 deletions common/gossmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ struct gossmap_localmods *gossmap_localmods_new(const tal_t *ctx);
/* Create a local-only channel; if this conflicts with a real channel when added,
* that will be used instead.
* Returns false (and does nothing) if scid was already in localmods.
* n1 must be different from n2.
*/
bool gossmap_local_addchan(struct gossmap_localmods *localmods,
const struct node_id *n1,
Expand Down
2 changes: 1 addition & 1 deletion contrib/msggen/msggen/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@
"destination": {
"type": "pubkey",
"description": [
"The destination node id for the channel."
"The destination node id for the channel (must be different from source)."
]
},
"short_channel_id": {
Expand Down
2 changes: 1 addition & 1 deletion doc/schemas/askrene-create-channel.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"destination": {
"type": "pubkey",
"description": [
"The destination node id for the channel."
"The destination node id for the channel (must be different from source)."
]
},
"short_channel_id": {
Expand Down
4 changes: 4 additions & 0 deletions plugins/askrene/askrene.c
Original file line number Diff line number Diff line change
Expand Up @@ -1066,6 +1066,10 @@ static struct command_result *json_askrene_create_channel(struct command *cmd,
plugin_log(cmd->plugin, LOG_TRACE, "%s called: %.*s", __func__,
json_tok_full_len(params), json_tok_full(buffer, params));

if (node_id_cmp(src, dst) == 0)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"source and destination must be different");

if (layer_find_local_channel(layer, *scid)) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"channel already exists");
Expand Down
1 change: 1 addition & 0 deletions plugins/askrene/layer.c
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ static struct local_channel *add_local_channel(struct layer *layer,
lc->n1 = *n1;
lc->n2 = *n2;
} else {
assert(!node_id_eq(n1, n2));
lc->n1 = *n2;
lc->n2 = *n1;
}
Expand Down
2 changes: 1 addition & 1 deletion plugins/askrene/layer.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ bool layer_check_local_channel(const struct local_channel *lc,
const struct node_id *n2,
struct amount_msat capacity);

/* Add a local channel to a layer! */
/* Add a local channel to a layer: src must not be equal to dst!*/
void layer_add_local_channel(struct layer *layer,
const struct node_id *src,
const struct node_id *dst,
Expand Down
16 changes: 14 additions & 2 deletions plugins/xpay/xpay.c
Original file line number Diff line number Diff line change
Expand Up @@ -1922,6 +1922,16 @@ static void add_fake_channel(struct command *aux_cmd,
struct out_req *req;
struct short_channel_id_dir scidd;

/* We're not allowed to send these to askrene-create-channel,
* so catch them now */
if (node_id_eq(src, dst)) {
payment_log(payment, LOG_UNUSUAL,
"Invoice gave bad self-node route %s->%s",
fmt_node_id(tmpctx, src),
fmt_node_id(tmpctx, dst));
return;
}

scidd.scid = scid;
scidd.dir = node_id_idx(src, dst);
payment_log(payment, LOG_DBG,
Expand Down Expand Up @@ -2153,7 +2163,8 @@ static struct command_result *check_offer_payable(struct command *cmd,
/* We will only one-shot if we know amount! (FIXME: Convert!) */
if (b12offer->offer_currency)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Cannot pay offer in different currency %s",
"Cannot pay offer in different currency %.*s",
(int)tal_bytelen(b12offer->offer_currency),
b12offer->offer_currency);
if (b12offer->offer_amount) {
if (msat && !amount_msat_eq(amount_msat(*b12offer->offer_amount), *msat)) {
Expand Down Expand Up @@ -2188,7 +2199,8 @@ check_offer_sendamount_payable(struct command *cmd, const char *offerstr)
/* FIXME: add currency support */
if (b12offer->offer_currency)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Cannot pay offer in different currency %s",
"Cannot pay offer in different currency %.*s",
(int)tal_bytelen(b12offer->offer_currency),
b12offer->offer_currency);
/* Can only be applied to *any amount* offers. */
if (b12offer->offer_amount)
Expand Down
41 changes: 41 additions & 0 deletions tests/test_xpay.py
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,47 @@ def test_xpay_offer(node_factory):
l1.rpc.xpay(offer2, 5000)


def test_xpay_circular_routehint(node_factory):
"""Test that xpay gracefully skips a circular bolt11 routehint (src == dst)."""
l1, l2 = node_factory.line_graph(2)

# A routehint containing a same-node channel (example is l3 in this case)
circular_hint = [{'id': '03cecbfdc68544cc596223b68ce0710c9e5d2c9cb317ee07822d95079acc703d31',
'short_channel_id': '1x2x3',
'fee_base_msat': 0,
'fee_proportional_millionths': 0,
'cltv_expiry_delta': 6},
{'id': '03cecbfdc68544cc596223b68ce0710c9e5d2c9cb317ee07822d95079acc703d31',
'short_channel_id': '1x2x4',
'fee_base_msat': 0,
'fee_proportional_millionths': 0,
'cltv_expiry_delta': 6}]
inv = l2.dev_invoice(amount_msat=10000,
label='circular_hint',
description='circular hint test',
dev_routes=[circular_hint])

# Payment should still succeed via the direct channel.
ret = l1.rpc.xpay(inv['bolt11'])
assert ret['successful_parts'] == 1
l1.daemon.wait_for_log('Invoice gave bad self-node route')


def test_xpay_currency_offer(node_factory):
"""Test that xpay and sendamount correctly report the currency name when rejecting non-msat offers."""
plugin = Path(__file__).parent / "plugins" / "currencyUSDAUD5000.py"
l1, l2 = node_factory.line_graph(2, wait_for_announce=True,
opts=[{}, {'plugin': str(plugin)}])

offerusd = l2.rpc.offer('10USD', 'USD test')['bolt12']

with pytest.raises(RpcError, match=r"Cannot pay offer in different currency USD"):
l1.rpc.xpay(offerusd)

with pytest.raises(RpcError, match=r"Cannot pay offer in different currency USD"):
l1.rpc.sendamount(offerusd, '100sat')


def test_xpay_bip353(node_factory):
fakebip353_plugin = Path(__file__).parent / "plugins" / "fakebip353.py"

Expand Down
Loading