mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 21:35:11 +01:00
getroute: allow array of channels to exclude.
The pay plugin will use this, rather than the current "suppress for 90 second" hacks. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
599ec5efbe
commit
3d016e7249
@ -147,13 +147,13 @@ class LightningRpc(UnixDomainSocketRpc):
|
||||
}
|
||||
return self.call("listnodes", payload)
|
||||
|
||||
def getroute(self, peer_id, msatoshi, riskfactor, cltv=9, fromid=None, fuzzpercent=None, seed=None):
|
||||
def getroute(self, peer_id, msatoshi, riskfactor, cltv=9, fromid=None, fuzzpercent=None, seed=None, exclude=[]):
|
||||
"""
|
||||
Show route to {id} for {msatoshi}, using {riskfactor} and optional
|
||||
{cltv} (default 9). If specified search from {fromid} otherwise use
|
||||
this node as source. Randomize the route with up to {fuzzpercent}
|
||||
(0.0 -> 100.0, default 5.0) using {seed} as an arbitrary-size string
|
||||
seed.
|
||||
seed. {exclude} is an optional array of scids[xDirection] to exclude.
|
||||
"""
|
||||
payload = {
|
||||
"id": peer_id,
|
||||
@ -162,7 +162,8 @@ class LightningRpc(UnixDomainSocketRpc):
|
||||
"cltv": cltv,
|
||||
"fromid": fromid,
|
||||
"fuzzpercent": fuzzpercent,
|
||||
"seed": seed
|
||||
"seed": seed,
|
||||
"exclude": exclude
|
||||
}
|
||||
return self.call("getroute", payload)
|
||||
|
||||
|
@ -292,6 +292,27 @@ static void json_getroute_reply(struct subd *gossip UNUSED, const u8 *reply, con
|
||||
was_pending(command_success(cmd, response));
|
||||
}
|
||||
|
||||
static bool json_to_short_channel_id_with_dir(const char *buffer,
|
||||
const jsmntok_t *tok,
|
||||
struct short_channel_id *scid,
|
||||
bool *dir)
|
||||
{
|
||||
/* Ends in /0 or /1 */
|
||||
if (tok->end - tok->start < 2)
|
||||
return false;
|
||||
if (buffer[tok->end - 2] != '/')
|
||||
return false;
|
||||
if (buffer[tok->end - 1] == '0')
|
||||
*dir = false;
|
||||
else if (buffer[tok->end - 1] == '1')
|
||||
*dir = true;
|
||||
else
|
||||
return false;
|
||||
|
||||
return short_channel_id_from_str(buffer + tok->start,
|
||||
tok->end - tok->start - 2, scid);
|
||||
}
|
||||
|
||||
static struct command_result *json_getroute(struct command *cmd,
|
||||
const char *buffer,
|
||||
const jsmntok_t *obj UNNEEDED,
|
||||
@ -300,9 +321,13 @@ static struct command_result *json_getroute(struct command *cmd,
|
||||
struct lightningd *ld = cmd->ld;
|
||||
struct pubkey *destination;
|
||||
struct pubkey *source;
|
||||
const jsmntok_t *excludetok;
|
||||
u64 *msatoshi;
|
||||
unsigned *cltv;
|
||||
double *riskfactor;
|
||||
struct short_channel_id *excluded;
|
||||
bool *excluded_dir;
|
||||
|
||||
/* Higher fuzz means that some high-fee paths can be discounted
|
||||
* for an even larger value, increasing the scope for route
|
||||
* randomization (the higher-fee paths become more likely to
|
||||
@ -317,16 +342,43 @@ static struct command_result *json_getroute(struct command *cmd,
|
||||
p_opt_def("cltv", param_number, &cltv, 9),
|
||||
p_opt_def("fromid", param_pubkey, &source, ld->id),
|
||||
p_opt_def("fuzzpercent", param_percent, &fuzz, 5.0),
|
||||
p_opt("exclude", param_array, &excludetok),
|
||||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
/* Convert from percentage */
|
||||
*fuzz = *fuzz / 100.0;
|
||||
|
||||
if (excludetok) {
|
||||
const jsmntok_t *t, *end = json_next(excludetok);
|
||||
size_t i;
|
||||
|
||||
excluded = tal_arr(cmd, struct short_channel_id,
|
||||
excludetok->size);
|
||||
excluded_dir = tal_arr(cmd, bool, excludetok->size);
|
||||
|
||||
for (i = 0, t = excludetok + 1;
|
||||
t < end;
|
||||
t = json_next(t), i++) {
|
||||
if (!json_to_short_channel_id_with_dir(buffer, t,
|
||||
&excluded[i],
|
||||
&excluded_dir[i])) {
|
||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||
"%.*s is not a valid"
|
||||
" id+direction",
|
||||
t->end - t->start,
|
||||
buffer + t->start);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
excluded = NULL;
|
||||
excluded_dir = NULL;
|
||||
}
|
||||
|
||||
u8 *req = towire_gossip_getroute_request(cmd, source, destination,
|
||||
*msatoshi, *riskfactor * 1000,
|
||||
*cltv, fuzz,
|
||||
NULL, NULL);
|
||||
excluded, excluded_dir);
|
||||
subd_req(ld->gossip, ld->gossip, req, -1, 0, json_getroute_reply, cmd);
|
||||
return command_still_pending(cmd);
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
from fixtures import * # noqa: F401,F403
|
||||
from lightning import RpcError
|
||||
from utils import wait_for, TIMEOUT, only_one
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import pytest
|
||||
import struct
|
||||
import subprocess
|
||||
import time
|
||||
@ -941,3 +943,51 @@ def test_gossip_notices_close(node_factory, bitcoind):
|
||||
l1.start()
|
||||
assert(l1.rpc.listchannels()['channels'] == [])
|
||||
assert(l1.rpc.listnodes()['nodes'] == [])
|
||||
|
||||
|
||||
def test_getroute_exclude(node_factory, bitcoind):
|
||||
"""Test getroute's exclude argument"""
|
||||
l1, l2, l3, l4 = node_factory.line_graph(4, wait_for_announce=True)
|
||||
|
||||
# This should work
|
||||
route = l1.rpc.getroute(l4.info['id'], 1, 1)['route']
|
||||
|
||||
# l1 id is > l2 id, so 1 means l1->l2
|
||||
chan_l1l2 = route[0]['channel'] + '/1'
|
||||
chan_l2l1 = route[0]['channel'] + '/0'
|
||||
|
||||
# This should not
|
||||
with pytest.raises(RpcError):
|
||||
l1.rpc.getroute(l4.info['id'], 1, 1, exclude=[chan_l1l2])
|
||||
|
||||
# Blocking the wrong way should be fine.
|
||||
l1.rpc.getroute(l4.info['id'], 1, 1, exclude=[chan_l2l1])
|
||||
|
||||
# Now, create an alternate (better) route.
|
||||
l2.rpc.connect(l4.info['id'], 'localhost', l4.port)
|
||||
scid = l2.fund_channel(l4, 1000000, wait_for_active=False)
|
||||
bitcoind.generate_block(5)
|
||||
|
||||
# We don't wait above, because we care about it hitting l1.
|
||||
l1.daemon.wait_for_logs([r'update for channel {}\(0\) now ACTIVE'
|
||||
.format(scid),
|
||||
r'update for channel {}\(1\) now ACTIVE'
|
||||
.format(scid)])
|
||||
|
||||
# l3 id is > l2 id, so 1 means l3->l2
|
||||
# chan_l3l2 = route[1]['channel'] + '/1'
|
||||
chan_l2l3 = route[1]['channel'] + '/0'
|
||||
|
||||
# l4 is > l2
|
||||
# chan_l4l2 = scid + '/1'
|
||||
chan_l2l4 = scid + '/0'
|
||||
|
||||
# This works
|
||||
l1.rpc.getroute(l4.info['id'], 1, 1, exclude=[chan_l2l3])
|
||||
|
||||
# This works
|
||||
l1.rpc.getroute(l4.info['id'], 1, 1, exclude=[chan_l2l4])
|
||||
|
||||
# This doesn't
|
||||
with pytest.raises(RpcError):
|
||||
l1.rpc.getroute(l4.info['id'], 1, 1, exclude=[chan_l2l3, chan_l2l4])
|
||||
|
Loading…
Reference in New Issue
Block a user