askrene: use short_channel_id_dir in API.

It's generally much more convenient, and it's already present in
other APIs.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2024-10-04 08:52:53 +09:30
parent 33404b03a0
commit 29cc227a53
9 changed files with 109 additions and 228 deletions

View file

@ -474,8 +474,7 @@
"request": {
"required": [
"layer",
"short_channel_id",
"direction"
"short_channel_id_dir"
],
"properties": {
"layer": {
@ -484,16 +483,10 @@
"The name of the layer to apply this change to."
]
},
"short_channel_id": {
"type": "short_channel_id",
"short_channel_id_dir": {
"type": "short_channel_id_dir",
"description": [
"The short channel id to apply this change to."
]
},
"direction": {
"type": "u32",
"description": [
"The direction to apply this change to."
"The short channel id and direction to apply this change to."
]
},
"minimum_msat": {
@ -518,21 +511,14 @@
"constraint": {
"type": "object",
"required": [
"short_channel_id",
"direction",
"short_channel_id_dir",
"timestamp"
],
"properties": {
"short_channel_id": {
"type": "short_channel_id",
"short_channel_id_dir": {
"type": "short_channel_id_dir",
"description": [
"The *short_channel_id* specified."
]
},
"direction": {
"type": "u32",
"description": [
"The *direction* specified."
"The *short_channel_id* and *direction* specified."
]
},
"timestamp": {
@ -713,20 +699,13 @@
"items": {
"type": "object",
"required": [
"short_channel_id",
"direction"
"short_channel_id_dir"
],
"properties": {
"short_channel_id": {
"type": "short_channel_id",
"short_channel_id_dir": {
"type": "short_channel_id_dir",
"description": [
"The short channel id."
]
},
"direction": {
"type": "u32",
"description": [
"The direction."
"The short channel id and direction"
]
},
"maximum_msat": {
@ -787,21 +766,14 @@
"type": "object",
"additionalProperties": true,
"required": [
"short_channel_id",
"direction",
"short_channel_id_dir",
"amount_msat"
],
"properties": {
"short_channel_id": {
"type": "short_channel_id",
"short_channel_id_dir": {
"type": "short_channel_id_dir",
"description": [
"The channel joining these nodes."
]
},
"direction": {
"type": "u32",
"description": [
"0 if this channel is traversed from lesser to greater **id**, otherwise 1."
"The channel and direction joining these nodes."
]
},
"amount_msat": {
@ -859,21 +831,14 @@
"type": "object",
"additionalProperties": true,
"required": [
"short_channel_id",
"direction",
"short_channel_id_dir",
"amount_msat"
],
"properties": {
"short_channel_id": {
"type": "short_channel_id",
"short_channel_id_dir": {
"type": "short_channel_id_dir",
"description": [
"The channel joining these nodes."
]
},
"direction": {
"type": "u32",
"description": [
"0 if this channel is traversed from lesser to greater **id**, otherwise 1."
"The channel and direction joining these nodes."
]
},
"amount_msat": {
@ -14435,23 +14400,16 @@
"type": "object",
"additionalProperties": false,
"required": [
"short_channel_id",
"direction",
"short_channel_id_dir",
"next_node_id",
"amount_msat",
"delay"
],
"properties": {
"short_channel_id": {
"type": "short_channel_id",
"short_channel_id_dir": {
"type": "short_channel_id_dir",
"description": [
"The channel joining these nodes."
]
},
"direction": {
"type": "u32",
"description": [
"0 if this channel is traversed from lesser to greater **id**, otherwise 1."
"The channel and direction joining these nodes."
]
},
"amount_msat": {

View file

@ -12,8 +12,7 @@
"request": {
"required": [
"layer",
"short_channel_id",
"direction"
"short_channel_id_dir"
],
"properties": {
"layer": {
@ -22,16 +21,10 @@
"The name of the layer to apply this change to."
]
},
"short_channel_id": {
"type": "short_channel_id",
"short_channel_id_dir": {
"type": "short_channel_id_dir",
"description": [
"The short channel id to apply this change to."
]
},
"direction": {
"type": "u32",
"description": [
"The direction to apply this change to."
"The short channel id and direction to apply this change to."
]
},
"minimum_msat": {
@ -56,21 +49,14 @@
"constraint": {
"type": "object",
"required": [
"short_channel_id",
"direction",
"short_channel_id_dir",
"timestamp"
],
"properties": {
"short_channel_id": {
"type": "short_channel_id",
"short_channel_id_dir": {
"type": "short_channel_id_dir",
"description": [
"The *short_channel_id* specified."
]
},
"direction": {
"type": "u32",
"description": [
"The *direction* specified."
"The *short_channel_id* and *direction* specified."
]
},
"timestamp": {

View file

@ -140,20 +140,13 @@
"items": {
"type": "object",
"required": [
"short_channel_id",
"direction"
"short_channel_id_dir"
],
"properties": {
"short_channel_id": {
"type": "short_channel_id",
"short_channel_id_dir": {
"type": "short_channel_id_dir",
"description": [
"The short channel id."
]
},
"direction": {
"type": "u32",
"description": [
"The direction."
"The short channel id and direction"
]
},
"maximum_msat": {

View file

@ -22,21 +22,14 @@
"type": "object",
"additionalProperties": true,
"required": [
"short_channel_id",
"direction",
"short_channel_id_dir",
"amount_msat"
],
"properties": {
"short_channel_id": {
"type": "short_channel_id",
"short_channel_id_dir": {
"type": "short_channel_id_dir",
"description": [
"The channel joining these nodes."
]
},
"direction": {
"type": "u32",
"description": [
"0 if this channel is traversed from lesser to greater **id**, otherwise 1."
"The channel and direction joining these nodes."
]
},
"amount_msat": {

View file

@ -22,21 +22,14 @@
"type": "object",
"additionalProperties": true,
"required": [
"short_channel_id",
"direction",
"short_channel_id_dir",
"amount_msat"
],
"properties": {
"short_channel_id": {
"type": "short_channel_id",
"short_channel_id_dir": {
"type": "short_channel_id_dir",
"description": [
"The channel joining these nodes."
]
},
"direction": {
"type": "u32",
"description": [
"0 if this channel is traversed from lesser to greater **id**, otherwise 1."
"The channel and direction joining these nodes."
]
},
"amount_msat": {

View file

@ -121,23 +121,16 @@
"type": "object",
"additionalProperties": false,
"required": [
"short_channel_id",
"direction",
"short_channel_id_dir",
"next_node_id",
"amount_msat",
"delay"
],
"properties": {
"short_channel_id": {
"type": "short_channel_id",
"short_channel_id_dir": {
"type": "short_channel_id_dir",
"description": [
"The channel joining these nodes."
]
},
"direction": {
"type": "u32",
"description": [
"0 if this channel is traversed from lesser to greater **id**, otherwise 1."
"The channel and direction joining these nodes."
]
},
"amount_msat": {

View file

@ -102,20 +102,6 @@ static struct command_result *param_known_layer(struct command *cmd,
return NULL;
}
static struct command_result *param_zero_or_one(struct command *cmd,
const char *name,
const char *buffer,
const jsmntok_t *tok,
int **num)
{
*num = tal(cmd, int);
if (json_to_zero_or_one(buffer, tok, *num))
return NULL;
return command_fail_badparam(cmd, name, buffer, tok,
"should be 0 or 1");
}
struct reserve_path {
struct short_channel_id_dir *scidds;
struct amount_msat *amounts;
@ -130,9 +116,8 @@ static struct command_result *parse_reserve_path(struct command *cmd,
{
const char *err;
err = json_scan(tmpctx, buffer, tok, "{short_channel_id:%,direction:%,amount_msat:%}",
JSON_SCAN(json_to_short_channel_id, &scidd->scid),
JSON_SCAN(json_to_zero_or_one, &scidd->dir),
err = json_scan(tmpctx, buffer, tok, "{short_channel_id_dir:%,amount_msat:%}",
JSON_SCAN(json_to_short_channel_id_dir, scidd),
JSON_SCAN(json_to_msat, amount));
if (err)
return command_fail_badparam(cmd, name, buffer, tok, err);
@ -553,10 +538,12 @@ static struct command_result *do_getroutes(struct command *cmd,
json_add_u32(response, "final_cltv", *info->finalcltv);
json_array_start(response, "path");
for (size_t j = 0; j < tal_count(routes[i]->hops); j++) {
struct short_channel_id_dir scidd;
const struct route_hop *r = &routes[i]->hops[j];
json_object_start(response, NULL);
json_add_short_channel_id(response, "short_channel_id", r->scid);
json_add_u32(response, "direction", r->direction);
scidd.scid = r->scid;
scidd.dir = r->direction;
json_add_short_channel_id_dir(response, "short_channel_id_dir", scidd);
json_add_node_id(response, "next_node_id", &r->node_id);
json_add_amount_msat(response, "amount_msat", r->amount);
json_add_u32(response, "delay", r->delay);
@ -825,18 +812,15 @@ static struct command_result *json_askrene_inform_channel(struct command *cmd,
{
struct layer *layer;
const char *layername;
struct short_channel_id *scid;
int *direction;
struct short_channel_id_dir *scidd;
struct json_stream *response;
struct amount_msat *max, *min;
const struct constraint *c;
struct short_channel_id_dir scidd;
struct askrene *askrene = get_askrene(cmd->plugin);
if (!param_check(cmd, buffer, params,
p_req("layer", param_layername, &layername),
p_req("short_channel_id", param_short_channel_id, &scid),
p_req("direction", param_zero_or_one, &direction),
p_req("short_channel_id_dir", param_short_channel_id_dir, &scidd),
p_opt("minimum_msat", param_msat, &min),
p_opt("maximum_msat", param_msat, &max),
NULL))
@ -854,15 +838,11 @@ static struct command_result *json_askrene_inform_channel(struct command *cmd,
if (!layer)
layer = new_layer(askrene, layername);
/* Calls expect a convenient short_channel_id_dir struct */
scidd.scid = *scid;
scidd.dir = *direction;
if (min) {
c = layer_update_constraint(layer, &scidd, CONSTRAINT_MIN,
c = layer_update_constraint(layer, scidd, CONSTRAINT_MIN,
time_now().ts.tv_sec, *min);
} else {
c = layer_update_constraint(layer, &scidd, CONSTRAINT_MAX,
c = layer_update_constraint(layer, scidd, CONSTRAINT_MAX,
time_now().ts.tv_sec, *max);
}
response = jsonrpc_stream_success(cmd);

View file

@ -419,8 +419,7 @@ void json_add_constraint(struct json_stream *js,
json_object_start(js, fieldname);
if (layer)
json_add_string(js, "layer", layer->name);
json_add_short_channel_id(js, "short_channel_id", c->key.scidd.scid);
json_add_u32(js, "direction", c->key.scidd.dir);
json_add_short_channel_id_dir(js, "short_channel_id_dir", c->key.scidd);
json_add_u64(js, "timestamp", c->timestamp);
switch (c->key.type) {
case CONSTRAINT_MIN:

View file

@ -8,6 +8,13 @@ import pytest
import time
def direction(src, dst):
"""BOLT 7 direction: 0 means from lesser encoded id"""
if src < dst:
return 0
return 1
def test_layers(node_factory):
"""Test manipulating information in layers"""
l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True)
@ -52,12 +59,10 @@ def test_layers(node_factory):
# We can tell it about made up channels...
first_timestamp = int(time.time())
l2.rpc.askrene_inform_channel('test_layers',
'0x0x1',
1,
'0x0x1/1',
100000)
last_timestamp = int(time.time()) + 1
expect['constraints'].append({'short_channel_id': '0x0x1',
'direction': 1,
expect['constraints'].append({'short_channel_id_dir': '0x0x1/1',
'minimum_msat': 100000})
# Check timestamp first.
listlayers = l2.rpc.askrene_listlayers('test_layers')
@ -72,25 +77,23 @@ def test_layers(node_factory):
# We can tell it about existing channels...
scid12 = first_scid(l1, l2)
first_timestamp = int(time.time())
scid12dir = f"{scid12}/{direction(l2.info['id'], l1.info['id'])}"
l2.rpc.askrene_inform_channel(layer='test_layers',
short_channel_id=scid12,
# This is l2 -> l1
direction=0,
short_channel_id_dir=scid12dir,
maximum_msat=12341234)
last_timestamp = int(time.time()) + 1
expect['constraints'].append({'short_channel_id': scid12,
'direction': 0,
expect['constraints'].append({'short_channel_id_dir': scid12dir,
'timestamp': first_timestamp,
'maximum_msat': 12341234})
# Check timestamp first.
listlayers = l2.rpc.askrene_listlayers('test_layers')
ts2 = only_one([c['timestamp'] for c in only_one(listlayers['layers'])['constraints'] if c['short_channel_id'] == scid12])
ts2 = only_one([c['timestamp'] for c in only_one(listlayers['layers'])['constraints'] if c['short_channel_id_dir'] == scid12dir])
assert first_timestamp <= ts2 <= last_timestamp
expect['constraints'][1]['timestamp'] = ts2
# Could be either order!
actual = expect.copy()
if only_one(listlayers['layers'])['constraints'][0]['short_channel_id'] == scid12:
if only_one(listlayers['layers'])['constraints'][0]['short_channel_id_dir'] == scid12dir:
actual['constraints'] = [expect['constraints'][1], expect['constraints'][0]]
assert listlayers == {'layers': [actual]}
@ -169,13 +172,8 @@ def test_getroutes(node_factory):
# Set up l1 with this as the gossip_store
l1 = node_factory.get_node(gossip_store_file=gsfile.name)
def direction(nodemap, src, dst):
if nodemap[src] < nodemap[dst]:
return 0
return 1
# Disabling channels makes getroutes fail
l1.rpc.askrene_disable_channel("chans_disabled", f"0x1x0/{direction(nodemap, 0, 1)}")
l1.rpc.askrene_disable_channel("chans_disabled", '0x1x0/1')
with pytest.raises(RpcError, match="Could not find route"):
l1.rpc.getroutes(source=nodemap[0],
destination=nodemap[1],
@ -193,8 +191,7 @@ def test_getroutes(node_factory):
'routes': [{'probability_ppm': 999999,
'final_cltv': 99,
'amount_msat': 1000,
'path': [{'short_channel_id': '0x1x0',
'direction': 1,
'path': [{'short_channel_id_dir': '0x1x0/1',
'next_node_id': nodemap[1],
'amount_msat': 1010,
'delay': 99 + 6}]}]}
@ -208,13 +205,11 @@ def test_getroutes(node_factory):
'routes': [{'probability_ppm': 999798,
'final_cltv': 99,
'amount_msat': 100000,
'path': [{'short_channel_id': '0x1x0',
'direction': 1,
'path': [{'short_channel_id_dir': '0x1x0/1',
'next_node_id': nodemap[1],
'amount_msat': 103020,
'delay': 99 + 6 + 6},
{'short_channel_id': '1x3x2',
'direction': 1,
{'short_channel_id_dir': '1x3x2/1',
'next_node_id': nodemap[3],
'amount_msat': 102000,
'delay': 99 + 6}
@ -254,8 +249,7 @@ def test_getroutes(node_factory):
'routes': [{'probability_ppm': 900000,
'final_cltv': 99,
'amount_msat': 1000000,
'path': [{'short_channel_id': '0x2x3',
'direction': 1,
'path': [{'short_channel_id_dir': '0x2x3/1',
'next_node_id': nodemap[2],
'amount_msat': 1000001,
'delay': 99 + 6}]}]}
@ -265,11 +259,11 @@ def test_getroutes(node_factory):
nodemap[0],
nodemap[2],
10000000,
[[{'short_channel_id': '0x2x1',
[[{'short_channel_id_dir': '0x2x1/1',
'next_node_id': nodemap[2],
'amount_msat': 500000,
'delay': 99 + 6}],
[{'short_channel_id': '0x2x3',
[{'short_channel_id_dir': '0x2x3/1',
'next_node_id': nodemap[2],
'amount_msat': 9500009,
'delay': 99 + 6}]])
@ -299,8 +293,8 @@ def test_getroutes_fee_fallback(node_factory):
nodemap[3],
10000,
maxfee_msat=201,
paths=[[{'short_channel_id': '0x1x0'},
{'short_channel_id': '1x3x2'}]])
paths=[[{'short_channel_id_dir': '0x1x0/1'},
{'short_channel_id_dir': '1x3x2/1'}]])
# maxfee exceeded? lower prob path.
check_getroute_paths(l1,
@ -308,8 +302,8 @@ def test_getroutes_fee_fallback(node_factory):
nodemap[3],
10000,
maxfee_msat=200,
paths=[[{'short_channel_id': '0x2x1'},
{'short_channel_id': '2x3x3'}]])
paths=[[{'short_channel_id_dir': '0x2x1/1'},
{'short_channel_id_dir': '2x3x3/0'}]])
def test_getroutes_auto_sourcefree(node_factory):
@ -333,8 +327,7 @@ def test_getroutes_auto_sourcefree(node_factory):
'routes': [{'probability_ppm': 999999,
'final_cltv': 99,
'amount_msat': 1000,
'path': [{'short_channel_id': '0x1x0',
'direction': 1,
'path': [{'short_channel_id_dir': '0x1x0/1',
'next_node_id': nodemap[1],
'amount_msat': 1000,
'delay': 99}]}]}
@ -348,13 +341,11 @@ def test_getroutes_auto_sourcefree(node_factory):
'routes': [{'probability_ppm': 999798,
'final_cltv': 99,
'amount_msat': 100000,
'path': [{'short_channel_id': '0x1x0',
'direction': 1,
'path': [{'short_channel_id_dir': '0x1x0/1',
'next_node_id': nodemap[1],
'amount_msat': 102000,
'delay': 99 + 6},
{'short_channel_id': '1x3x2',
'direction': 1,
{'short_channel_id_dir': '1x3x2/1',
'next_node_id': nodemap[3],
'amount_msat': 102000,
'delay': 99 + 6}
@ -408,15 +399,16 @@ def test_getroutes_auto_localchans(node_factory):
final_cltv=99)
# This should work
scid21dir = f"{scid12}/{direction(l2.info['id'], l1.info['id'])}"
check_getroute_paths(l2,
l2.info['id'],
nodemap[2],
100000,
maxfee_msat=100000,
layers=['auto.localchans'],
paths=[[{'short_channel_id': scid12, 'amount_msat': 102012, 'delay': 99 + 6 + 6 + 6},
{'short_channel_id': '0x1x0', 'amount_msat': 102010, 'delay': 99 + 6 + 6},
{'short_channel_id': '1x2x1', 'amount_msat': 101000, 'delay': 99 + 6}]])
paths=[[{'short_channel_id_dir': scid21dir, 'amount_msat': 102012, 'delay': 99 + 6 + 6 + 6},
{'short_channel_id_dir': '0x1x0/0', 'amount_msat': 102010, 'delay': 99 + 6 + 6},
{'short_channel_id_dir': '1x2x1/1', 'amount_msat': 101000, 'delay': 99 + 6}]])
# This should get self-discount correct
check_getroute_paths(l2,
@ -425,9 +417,9 @@ def test_getroutes_auto_localchans(node_factory):
100000,
maxfee_msat=100000,
layers=['auto.localchans', 'auto.sourcefree'],
paths=[[{'short_channel_id': scid12, 'amount_msat': 102010, 'delay': 99 + 6 + 6},
{'short_channel_id': '0x1x0', 'amount_msat': 102010, 'delay': 99 + 6 + 6},
{'short_channel_id': '1x2x1', 'amount_msat': 101000, 'delay': 99 + 6}]])
paths=[[{'short_channel_id_dir': scid21dir, 'amount_msat': 102010, 'delay': 99 + 6 + 6},
{'short_channel_id_dir': '0x1x0/0', 'amount_msat': 102010, 'delay': 99 + 6 + 6},
{'short_channel_id_dir': '1x2x1/1', 'amount_msat': 101000, 'delay': 99 + 6}]])
def test_fees_dont_exceed_constraints(node_factory):
@ -444,8 +436,7 @@ def test_fees_dont_exceed_constraints(node_factory):
chan = only_one([c for c in l1.rpc.listchannels(source=nodemap[0])['channels'] if c['destination'] == nodemap[1]])
l1.rpc.askrene_inform_channel(layer='test_layers',
short_channel_id=chan['short_channel_id'],
direction=chan['direction'],
short_channel_id_dir=f"{chan['short_channel_id']}/{chan['direction']}",
maximum_msat=max_msat)
routes = l1.rpc.getroutes(source=nodemap[0],
@ -456,7 +447,7 @@ def test_fees_dont_exceed_constraints(node_factory):
final_cltv=99)['routes']
assert len(routes) == 2
for hop in routes[0]['path'] + routes[1]['path']:
if hop['short_channel_id'] == chan['short_channel_id']:
if hop['short_channel_id_dir'] == f"{chan['short_channel_id']}/{chan['direction']}":
amount = hop['amount_msat']
assert amount <= max_msat
@ -483,7 +474,7 @@ def test_sourcefree_on_mods(node_factory, bitcoind):
maxfee_msat=100000,
final_cltv=99)['routes']
# Expect no fee.
check_route_as_expected(routes, [[{'short_channel_id': '0x3x3',
check_route_as_expected(routes, [[{'short_channel_id_dir': '0x3x3/1',
'amount_msat': 1000000, 'delay': 99}]])
# Same if we specify layers in the other order!
@ -494,7 +485,7 @@ def test_sourcefree_on_mods(node_factory, bitcoind):
maxfee_msat=100000,
final_cltv=99)['routes']
# Expect no fee.
check_route_as_expected(routes, [[{'short_channel_id': '0x3x3',
check_route_as_expected(routes, [[{'short_channel_id_dir': '0x3x3/1',
'amount_msat': 1000000, 'delay': 99}]])
@ -536,9 +527,7 @@ def test_live_spendable(node_factory, bitcoind):
path_total = {}
num_htlcs = {}
for r in routes["routes"]:
key = "{}/{}".format(
r["path"][0]["short_channel_id"], r["path"][0]["direction"]
)
key = r["path"][0]["short_channel_id_dir"]
path_total[key] = path_total.get(key, 0) + r["path"][0]["amount_msat"]
num_htlcs[key] = num_htlcs.get(key, 0) + 1
@ -557,9 +546,9 @@ def test_live_spendable(node_factory, bitcoind):
# No duplicate paths!
for i in range(0, len(routes["routes"])):
path_i = [(p['short_channel_id'], p['direction']) for p in routes["routes"][i]['path']]
path_i = [p['short_channel_id_dir'] for p in routes["routes"][i]['path']]
for j in range(i + 1, len(routes["routes"])):
path_j = [(p['short_channel_id'], p['direction']) for p in routes["routes"][j]['path']]
path_j = [p['short_channel_id_dir'] for p in routes["routes"][j]['path']]
assert path_i != path_j
# Must deliver exact amount.
@ -592,12 +581,11 @@ def test_limits_fake_gossmap(node_factory, bitcoind):
assert scidd in [f"{c['short_channel_id']}/{c['direction']}" for c in l1.rpc.listchannels(source=nodemap[0])['channels']]
for scidd, amount in spendable.items():
chan, direction = scidd.split('/')
l1.rpc.askrene_inform_channel(layer='localchans',
short_channel_id=chan, direction=int(direction),
short_channel_id_dir=scidd,
minimum_msat=amount)
l1.rpc.askrene_inform_channel(layer='localchans',
short_channel_id=chan, direction=int(direction),
short_channel_id_dir=scidd,
maximum_msat=amount)
routes = l1.rpc.getroutes(
@ -611,9 +599,7 @@ def test_limits_fake_gossmap(node_factory, bitcoind):
path_total = {}
for r in routes["routes"]:
key = "{}/{}".format(
r["path"][0]["short_channel_id"], r["path"][0]["direction"]
)
key = r["path"][0]["short_channel_id_dir"]
path_total[key] = path_total.get(key, 0) + r["path"][0]["amount_msat"]
exceeded = {}
@ -626,9 +612,9 @@ def test_limits_fake_gossmap(node_factory, bitcoind):
# No duplicate paths!
for i in range(0, len(routes["routes"])):
path_i = [(p['short_channel_id'], p['direction']) for p in routes["routes"][i]['path']]
path_i = [p['short_channel_id_dir'] for p in routes["routes"][i]['path']]
for j in range(i + 1, len(routes["routes"])):
path_j = [(p['short_channel_id'], p['direction']) for p in routes["routes"][j]['path']]
path_j = [p['short_channel_id_dir'] for p in routes["routes"][j]['path']]
assert path_i != path_j
# Must deliver exact amount.
@ -650,12 +636,12 @@ def test_max_htlc(node_factory, bitcoind):
final_cltv=10)
check_route_as_expected(routes['routes'],
[[{'short_channel_id': '0x1x0', 'amount_msat': 1_000_001, 'delay': 10 + 6}],
[{'short_channel_id': '0x1x1', 'amount_msat': 19_000_019, 'delay': 10 + 6}]])
[[{'short_channel_id_dir': '0x1x0/1', 'amount_msat': 1_000_001, 'delay': 10 + 6}],
[{'short_channel_id_dir': '0x1x1/1', 'amount_msat': 19_000_019, 'delay': 10 + 6}]])
# If we can't use channel 2, we fail.
l1.rpc.askrene_inform_channel(layer='removechan2',
short_channel_id='0x1x1', direction=1,
short_channel_id_dir='0x1x1/1',
maximum_msat=0)
# FIXME: Better diag!