Skip to content

Commit 6e51873

Browse files
committed
add askrene-disable-channel
Changelog-EXPERIMENTAL: askrene: add askrene-disable-channel RPC Signed-off-by: Lagrang3 <[email protected]>
1 parent 99360a4 commit 6e51873

File tree

9 files changed

+216
-12
lines changed

9 files changed

+216
-12
lines changed

contrib/msggen/msggen/schema.json

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,62 @@
361361
"Main web site: <https://github.com/ElementsProject/lightning>"
362362
]
363363
},
364+
"lightning-askrene-disable-channel.json": {
365+
"$schema": "../rpc-schema-draft.json",
366+
"type": "object",
367+
"additionalProperties": false,
368+
"rpc": "askrene-disable-channel",
369+
"title": "Command to disable a channel in a layer (EXPERIMENTAL)",
370+
"description": [
371+
"WARNING: experimental, so API may change.",
372+
"",
373+
"The **askrene-disable-channel** RPC command tells askrene to disable a channel whenever the given layer is used. This is mainly useful to force the use of alternate paths."
374+
],
375+
"request": {
376+
"required": [
377+
"layer",
378+
"short_channel_id"
379+
],
380+
"properties": {
381+
"layer": {
382+
"type": "string",
383+
"description": [
384+
"The name of the layer to apply this change to."
385+
]
386+
},
387+
"short_channel_id": {
388+
"type": "short_channel_id",
389+
"description": [
390+
"The channel to disable."
391+
]
392+
},
393+
"direction": {
394+
"type": "u32",
395+
"description": [
396+
"The direction of the channel. If the direction is not specified then both directions are disabled."
397+
]
398+
}
399+
}
400+
},
401+
"response": {
402+
"required": [],
403+
"properties": {}
404+
},
405+
"see_also": [
406+
"lightning-getroutes(7)",
407+
"lightning-askrene-create-channel(7)",
408+
"lightning-askrene-inform-channel(7)",
409+
"lightning-askrene-disable-node(7)",
410+
"lightning-askrene-listlayers(7)",
411+
"lightning-askrene-age(7)"
412+
],
413+
"author": [
414+
"Rusty Russell <<[email protected]>> is mainly responsible."
415+
],
416+
"resources": [
417+
"Main web site: <https://github.com/ElementsProject/lightning>"
418+
]
419+
},
364420
"lightning-askrene-disable-node.json": {
365421
"$schema": "../rpc-schema-draft.json",
366422
"type": "object",
@@ -555,7 +611,7 @@
555611
"additionalProperties": false,
556612
"required": [
557613
"layer",
558-
"disabled_nodes",
614+
"disabled",
559615
"created_channels",
560616
"constraints"
561617
],
@@ -566,12 +622,22 @@
566622
"The name of the layer."
567623
]
568624
},
569-
"disabled_nodes": {
625+
"disabled": {
570626
"type": "array",
571627
"items": {
572-
"type": "pubkey",
573-
"description": [
574-
"The id of the disabled node."
628+
"oneOf": [
629+
{
630+
"type": "pubkey",
631+
"description": [
632+
"The id of the disabled node."
633+
]
634+
},
635+
{
636+
"type": "short_channel_id_dir",
637+
"description": [
638+
"The short channel id of the disabled channel."
639+
]
640+
}
575641
]
576642
}
577643
},

doc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ GENERATE_MARKDOWN := doc/lightning-addgossip.7 \
88
doc/lightning-addpsbtoutput.7 \
99
doc/lightning-askrene-create-channel.7 \
1010
doc/lightning-askrene-disable-node.7 \
11+
doc/lightning-askrene-disable-channel.7 \
1112
doc/lightning-askrene-inform-channel.7 \
1213
doc/lightning-askrene-listlayers.7 \
1314
doc/lightning-askrene-reserve.7 \

doc/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Core Lightning Documentation
1515
lightning-addgossip <lightning-addgossip.7.md>
1616
lightning-addpsbtoutput <lightning-addpsbtoutput.7.md>
1717
lightning-askrene-create-channel <lightning-askrene-create-channel.7.md>
18+
lightning-askrene-disable-channel <lightning-askrene-disable-channel.7.md>
1819
lightning-askrene-disable-node <lightning-askrene-disable-node.7.md>
1920
lightning-askrene-inform-channel <lightning-askrene-inform-channel.7.md>
2021
lightning-askrene-listlayers <lightning-askrene-listlayers.7.md>
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
{
2+
"$schema": "../rpc-schema-draft.json",
3+
"type": "object",
4+
"additionalProperties": false,
5+
"rpc": "askrene-disable-channel",
6+
"title": "Command to disable a channel in a layer (EXPERIMENTAL)",
7+
"description": [
8+
"WARNING: experimental, so API may change.",
9+
"",
10+
"The **askrene-disable-channel** RPC command tells askrene to disable a channel whenever the given layer is used. This is mainly useful to force the use of alternate paths."
11+
],
12+
"request": {
13+
"required": [
14+
"layer",
15+
"short_channel_id"
16+
],
17+
"properties": {
18+
"layer": {
19+
"type": "string",
20+
"description": [
21+
"The name of the layer to apply this change to."
22+
]
23+
},
24+
"short_channel_id": {
25+
"type": "short_channel_id",
26+
"description": [
27+
"The channel to disable."
28+
]
29+
},
30+
"direction": {
31+
"type": "u32",
32+
"description": [
33+
"The direction of the channel. If the direction is not specified then both directions are disabled."
34+
]
35+
}
36+
}
37+
},
38+
"response": {
39+
"required": [],
40+
"properties": {}
41+
},
42+
"see_also": [
43+
"lightning-getroutes(7)",
44+
"lightning-askrene-create-channel(7)",
45+
"lightning-askrene-inform-channel(7)",
46+
"lightning-askrene-disable-node(7)",
47+
"lightning-askrene-listlayers(7)",
48+
"lightning-askrene-age(7)"
49+
],
50+
"author": [
51+
"Rusty Russell <<[email protected]>> is mainly responsible."
52+
],
53+
"resources": [
54+
"Main web site: <https://github.com/ElementsProject/lightning>"
55+
]
56+
}

doc/schemas/lightning-askrene-listlayers.json

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"additionalProperties": false,
3333
"required": [
3434
"layer",
35-
"disabled_nodes",
35+
"disabled",
3636
"created_channels",
3737
"constraints"
3838
],
@@ -43,12 +43,22 @@
4343
"The name of the layer."
4444
]
4545
},
46-
"disabled_nodes": {
46+
"disabled": {
4747
"type": "array",
4848
"items": {
49-
"type": "pubkey",
50-
"description": [
51-
"The id of the disabled node."
49+
"oneOf": [
50+
{
51+
"type": "pubkey",
52+
"description": [
53+
"The id of the disabled node."
54+
]
55+
},
56+
{
57+
"type": "short_channel_id_dir",
58+
"description": [
59+
"The short channel id of the disabled channel."
60+
]
61+
}
5262
]
5363
}
5464
},

plugins/askrene/askrene.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,44 @@ static struct command_result *json_askrene_inform_channel(struct command *cmd,
881881
return command_finished(cmd, response);
882882
}
883883

884+
static struct command_result *json_askrene_disable_channel(struct command *cmd,
885+
const char *buffer,
886+
const jsmntok_t *params)
887+
{
888+
struct short_channel_id *scid;
889+
int *direction;
890+
const char *layername;
891+
struct layer *layer;
892+
struct json_stream *response;
893+
struct askrene *askrene = get_askrene(cmd->plugin);
894+
895+
if (!param(cmd, buffer, params,
896+
p_req("layer", param_layername, &layername),
897+
p_req("short_channel_id", param_short_channel_id, &scid),
898+
p_opt("direction", param_zero_or_one, &direction),
899+
NULL))
900+
return command_param_failed();
901+
902+
layer = find_layer(askrene, layername);
903+
if (!layer)
904+
layer = new_layer(askrene, layername);
905+
906+
struct short_channel_id_dir scidd = {.scid = *scid};
907+
if (direction) {
908+
scidd.dir = *direction;
909+
layer_add_disabled_channel(layer, &scidd);
910+
} else {
911+
/* If no direction is provided we disable both. */
912+
scidd.dir = 0;
913+
layer_add_disabled_channel(layer, &scidd);
914+
scidd.dir = 1;
915+
layer_add_disabled_channel(layer, &scidd);
916+
}
917+
918+
response = jsonrpc_stream_success(cmd);
919+
return command_finished(cmd, response);
920+
}
921+
884922
static struct command_result *json_askrene_disable_node(struct command *cmd,
885923
const char *buffer,
886924
const jsmntok_t *params)
@@ -983,6 +1021,10 @@ static const struct plugin_command commands[] = {
9831021
"askrene-age",
9841022
json_askrene_age,
9851023
},
1024+
{
1025+
"askrene-disable-channel",
1026+
json_askrene_disable_channel,
1027+
},
9861028
};
9871029

9881030
static void askrene_markmem(struct plugin *plugin, struct htable *memtable)

plugins/askrene/layer.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,15 @@ void layer_add_disabled_node(struct layer *layer, const struct node_id *node)
306306
tal_arr_expand(&layer->disabled, ex);
307307
}
308308

309+
void layer_add_disabled_channel(struct layer *layer,
310+
const struct short_channel_id_dir *scidd)
311+
{
312+
struct route_exclusion ex;
313+
ex.type = EXCLUDE_CHANNEL;
314+
ex.u.chan_id = *scidd;
315+
tal_arr_expand(&layer->disabled, ex);
316+
}
317+
309318
void layer_add_localmods(const struct layer *layer,
310319
const struct gossmap *gossmap,
311320
bool zero_cost,

plugins/askrene/layer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ size_t layer_trim_constraints(struct layer *layer, u64 cutoff);
102102
/* Add a disabled node to a layer. */
103103
void layer_add_disabled_node(struct layer *layer, const struct node_id *node);
104104

105+
/* Add a disabled channel to a layer. */
106+
void layer_add_disabled_channel(struct layer *layer,
107+
const struct short_channel_id_dir *scidd);
108+
105109
/* Print out a json object per layer, or all if layer is NULL */
106110
void json_add_layers(struct json_stream *js,
107111
struct askrene *askrene,

tests/test_askrene.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,19 @@ def test_layers(node_factory):
1616
assert l2.rpc.askrene_listlayers('test_layers') == {'layers': []}
1717

1818
expect = {'layer': 'test_layers',
19-
'disabled_nodes': [],
19+
'disabled': [],
2020
'created_channels': [],
2121
'constraints': []}
2222
l2.rpc.askrene_disable_node('test_layers', l1.info['id'])
23-
expect['disabled_nodes'].append(l1.info['id'])
23+
expect['disabled'].append(l1.info['id'])
2424
assert l2.rpc.askrene_listlayers('test_layers') == {'layers': [expect]}
2525
assert l2.rpc.askrene_listlayers() == {'layers': [expect]}
2626
assert l2.rpc.askrene_listlayers('test_layers2') == {'layers': []}
2727

28+
l2.rpc.askrene_disable_channel('test_layers', "1x2x3", 0)
29+
expect['disabled'].append("1x2x3/0")
30+
assert l2.rpc.askrene_listlayers('test_layers') == {'layers': [expect]}
31+
2832
# Tell it l3 connects to l1!
2933
l2.rpc.askrene_create_channel('test_layers',
3034
l3.info['id'],
@@ -164,6 +168,17 @@ def test_getroutes(node_factory):
164168
# Set up l1 with this as the gossip_store
165169
l1 = node_factory.get_node(gossip_store_file=gsfile.name)
166170

171+
# Disabling channels makes getroutes fail
172+
l1.rpc.askrene_disable_channel("chans_disabled", "0x1x0")
173+
l1.rpc.askrene_disable_channel("chans_disabled", "0x2x1")
174+
l1.rpc.askrene_disable_channel("chans_disabled", "0x2x3")
175+
with pytest.raises(RpcError, match="Could not find route"):
176+
l1.rpc.getroutes(source=nodemap[0],
177+
destination=nodemap[1],
178+
amount_msat=1000,
179+
layers=["chans_disabled"],
180+
maxfee_msat=1000,
181+
final_cltv=99)
167182
# Start easy
168183
assert l1.rpc.getroutes(source=nodemap[0],
169184
destination=nodemap[1],

0 commit comments

Comments
 (0)