short_channel_id_dir: new primitive for one direction of short_channel_id

Currently only used by gossipd for channel elimination.

Also print them in canonical form (/[01]), so tests need to be
changed.

Suggested-by: @cdecker
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2019-01-15 14:41:27 +10:30 committed by Christian Decker
parent 80753bfbd5
commit 9f1f79587e
16 changed files with 109 additions and 69 deletions

View File

@ -61,3 +61,29 @@ char *short_channel_id_to_str(const tal_t *ctx, const struct short_channel_id *s
short_channel_id_txnum(scid),
short_channel_id_outnum(scid));
}
bool short_channel_id_dir_from_str(const char *str, size_t strlen,
struct short_channel_id_dir *scidd)
{
const char *slash = memchr(str, '/', strlen);
if (!slash || slash + 2 != str + strlen)
return false;
if (!short_channel_id_from_str(str, slash - str, &scidd->scid))
return false;
if (slash[1] == '0')
scidd->dir = 0;
else if (slash[1] == '1')
scidd->dir = 1;
else
return false;
return true;
}
char *short_channel_id_dir_to_str(const tal_t *ctx,
const struct short_channel_id_dir *scidd)
{
char *str, *scidstr = short_channel_id_to_str(NULL, &scidd->scid);
str = tal_fmt(ctx, "%s/%u", scidstr, scidd->dir);
tal_free(scidstr);
return str;
}

View File

@ -15,6 +15,23 @@ struct short_channel_id {
/* Define short_channel_id_eq (no padding) */
STRUCTEQ_DEF(short_channel_id, 0, u64);
/* BOLT #7:
*
* - MUST set `node_id_1` and `node_id_2` to the public keys of the two nodes
* operating the channel, such that `node_id_1` is the numerically-lesser of the
* two DER-encoded keys sorted in ascending numerical order.
*...
* - if the origin node is `node_id_1` in the message:
* - MUST set the `direction` bit of `channel_flags` to 0.
* - otherwise:
* - MUST set the `direction` bit of `channel_flags` to 1.
*/
struct short_channel_id_dir {
struct short_channel_id scid;
/* 0 == from lesser id node, 1 == to lesser id node */
int dir;
};
static inline u32 short_channel_id_blocknum(const struct short_channel_id *scid)
{
return scid->u64 >> 40;
@ -38,4 +55,10 @@ bool short_channel_id_from_str(const char *str, size_t strlen,
char *short_channel_id_to_str(const tal_t *ctx, const struct short_channel_id *scid);
bool short_channel_id_dir_from_str(const char *str, size_t strlen,
struct short_channel_id_dir *scidd);
char *short_channel_id_dir_to_str(const tal_t *ctx,
const struct short_channel_id_dir *scidd);
#endif /* LIGHTNING_BITCOIN_SHORT_CHANNEL_ID_H */

View File

@ -24,6 +24,7 @@ union printable_types {
const secp256k1_pubkey *secp256k1_pubkey;
const struct channel_id *channel_id;
const struct short_channel_id *short_channel_id;
const struct short_channel_id_dir *short_channel_id_dir;
const struct secret *secret;
const struct privkey *privkey;
const secp256k1_ecdsa_signature *secp256k1_ecdsa_signature;

View File

@ -33,8 +33,7 @@ gossip_getroute_request,,riskfactor,u16
gossip_getroute_request,,final_cltv,u32
gossip_getroute_request,,fuzz,double
gossip_getroute_request,,num_excluded,u16
gossip_getroute_request,,excluded,num_excluded*struct short_channel_id
gossip_getroute_request,,excluded_dir,num_excluded*bool
gossip_getroute_request,,excluded,num_excluded*struct short_channel_id_dir
gossip_getroute_request,,max_hops,u32
gossip_getroute_reply,3106

1 #include <common/cryptomsg.h>
33 gossip_getroute_request,,excluded_dir,num_excluded*bool gossip_getroute_request,,max_hops,u32
34 gossip_getroute_request,,max_hops,u32 gossip_getroute_reply,3106
35 gossip_getroute_reply,3106 gossip_getroute_reply,,num_hops,u16
36 gossip_getroute_reply,,num_hops,u16 gossip_getroute_reply,,hops,num_hops*struct route_hop
gossip_getroute_reply,,hops,num_hops*struct route_hop
37 gossip_getchannels_request,3007
38 gossip_getchannels_request,,short_channel_id,?struct short_channel_id
39 gossip_getchannels_request,,source,?struct pubkey

View File

@ -1871,8 +1871,7 @@ static struct io_plan *getroute_req(struct io_conn *conn, struct daemon *daemon,
u8 *out;
struct route_hop *hops;
double fuzz;
struct short_channel_id *excluded;
bool *excluded_dir;
struct short_channel_id_dir *excluded;
/* To choose between variations, we need to know how much we're
* sending (eliminates too-small channels, and also effects the fees
@ -1884,7 +1883,7 @@ static struct io_plan *getroute_req(struct io_conn *conn, struct daemon *daemon,
&source, &destination,
&msatoshi, &riskfactor,
&final_cltv, &fuzz,
&excluded, &excluded_dir,
&excluded,
&max_hops))
master_badmsg(WIRE_GOSSIP_GETROUTE_REQUEST, msg);
@ -1895,7 +1894,7 @@ static struct io_plan *getroute_req(struct io_conn *conn, struct daemon *daemon,
/* routing.c does all the hard work; can return NULL. */
hops = get_route(tmpctx, daemon->rstate, &source, &destination,
msatoshi, riskfactor, final_cltv,
fuzz, siphash_seed(), excluded, excluded_dir, max_hops);
fuzz, siphash_seed(), excluded, max_hops);
out = towire_gossip_getroute_reply(NULL, hops);
daemon_conn_send(daemon->master, take(out));

View File

@ -1003,7 +1003,7 @@ static void update_pending(struct pending_cannouncement *pending,
u32 timestamp, const u8 *update,
const u8 direction)
{
SUPERVERBOSE("Deferring update for pending channel %s(%d)",
SUPERVERBOSE("Deferring update for pending channel %s/%d",
type_to_string(tmpctx, struct short_channel_id,
&pending->short_channel_id), direction);
@ -1043,12 +1043,12 @@ static void set_connection_values(struct chan *chan,
/* If it was temporarily unroutable, re-enable */
c->unroutable_until = 0;
SUPERVERBOSE("Channel %s(%d) was updated.",
SUPERVERBOSE("Channel %s/%d was updated.",
type_to_string(tmpctx, struct short_channel_id, &chan->scid),
idx);
if (c->proportional_fee >= MAX_PROPORTIONAL_FEE) {
status_trace("Channel %s(%d) massive proportional fee %u:"
status_trace("Channel %s/%d massive proportional fee %u:"
" disabling.",
type_to_string(tmpctx, struct short_channel_id,
&chan->scid),
@ -1272,7 +1272,7 @@ u8 *handle_channel_update(struct routing_state *rstate, const u8 *update TAKES,
return err;
}
status_trace("Received channel_update for channel %s(%d) now %s was %s (from %s)",
status_trace("Received channel_update for channel %s/%d now %s was %s (from %s)",
type_to_string(tmpctx, struct short_channel_id,
&short_channel_id),
channel_flags & 0x01,
@ -1506,8 +1506,7 @@ struct route_hop *get_route(const tal_t *ctx, struct routing_state *rstate,
const u64 msatoshi, double riskfactor,
u32 final_cltv,
double fuzz, const struct siphash_seed *base_seed,
const struct short_channel_id *excluded,
const bool *excluded_dir,
const struct short_channel_id_dir *excluded,
size_t max_hops)
{
struct chan **route;
@ -1518,17 +1517,16 @@ struct route_hop *get_route(const tal_t *ctx, struct routing_state *rstate,
struct node *n;
u64 *saved_capacity;
assert(tal_count(excluded) == tal_count(excluded_dir));
saved_capacity = tal_arr(tmpctx, u64, tal_count(excluded));
/* Temporarily set excluded channels' capacity to zero. */
for (size_t i = 0; i < tal_count(excluded); i++) {
struct chan *chan = get_channel(rstate, &excluded[i]);
struct chan *chan = get_channel(rstate, &excluded[i].scid);
if (!chan)
continue;
saved_capacity[i]
= chan->half[excluded_dir[i]].htlc_maximum_msat;
chan->half[excluded_dir[i]].htlc_maximum_msat = 0;
= chan->half[excluded[i].dir].htlc_maximum_msat;
chan->half[excluded[i].dir].htlc_maximum_msat = 0;
}
route = find_route(ctx, rstate, source, destination, msatoshi,
@ -1537,10 +1535,10 @@ struct route_hop *get_route(const tal_t *ctx, struct routing_state *rstate,
/* Now restore the capacity. */
for (size_t i = 0; i < tal_count(excluded); i++) {
struct chan *chan = get_channel(rstate, &excluded[i]);
struct chan *chan = get_channel(rstate, &excluded[i].scid);
if (!chan)
continue;
chan->half[excluded_dir[i]].htlc_maximum_msat
chan->half[excluded[i].dir].htlc_maximum_msat
= saved_capacity[i];
}

View File

@ -268,8 +268,7 @@ struct route_hop *get_route(const tal_t *ctx, struct routing_state *rstate,
u32 final_cltv,
double fuzz,
const struct siphash_seed *base_seed,
const struct short_channel_id *excluded,
const bool *excluded_dir,
const struct short_channel_id_dir *excluded,
size_t max_hops);
/* Disable channel(s) based on the given routing failure. */
void routing_failure(struct routing_state *rstate,

View File

@ -292,27 +292,6 @@ 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,
@ -325,8 +304,7 @@ static struct command_result *json_getroute(struct command *cmd,
u64 *msatoshi;
unsigned *cltv;
double *riskfactor;
struct short_channel_id *excluded;
bool *excluded_dir;
struct short_channel_id_dir *excluded;
u32 *max_hops;
/* Higher fuzz means that some high-fee paths can be discounted
@ -356,16 +334,15 @@ static struct command_result *json_getroute(struct command *cmd,
const jsmntok_t *t, *end = json_next(excludetok);
size_t i;
excluded = tal_arr(cmd, struct short_channel_id,
excluded = tal_arr(cmd, struct short_channel_id_dir,
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])) {
if (!short_channel_id_dir_from_str(buffer + t->start,
t->end - t->start,
&excluded[i])) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"%.*s is not a valid"
" short_channel_id/direction",
@ -375,13 +352,12 @@ static struct command_result *json_getroute(struct command *cmd,
}
} else {
excluded = NULL;
excluded_dir = NULL;
}
u8 *req = towire_gossip_getroute_request(cmd, source, destination,
*msatoshi, *riskfactor * 1000,
*cltv, fuzz,
excluded, excluded_dir,
excluded,
*max_hops);
subd_req(ld->gossip, ld->gossip, req, -1, 0, json_getroute_reply, cmd);
return command_still_pending(cmd);

View File

@ -557,7 +557,7 @@ static struct command_result *json_pay_try(struct pay *pay)
pay->msatoshi + overpayment,
pay->riskfactor,
pay->min_final_cltv_expiry,
&pay->fuzz, NULL, NULL,
&pay->fuzz, NULL,
ROUTING_MAX_HOPS);
subd_req(pay->try_parent, cmd->ld->gossip, req, -1, 0, json_pay_getroute_reply, pay);

View File

@ -1446,23 +1446,23 @@ def test_restart_many_payments(node_factory):
l1_logs = []
for i in range(len(innodes)):
scid = inchans[i]
l1_logs += [r'update for channel {}\(0\) now ACTIVE'.format(scid),
r'update for channel {}\(1\) now ACTIVE'.format(scid),
l1_logs += [r'update for channel {}/0 now ACTIVE'.format(scid),
r'update for channel {}/1 now ACTIVE'.format(scid),
'to CHANNELD_NORMAL']
innodes[i].daemon.wait_for_logs([r'update for channel {}\(0\) now ACTIVE'
innodes[i].daemon.wait_for_logs([r'update for channel {}/0 now ACTIVE'
.format(scid),
r'update for channel {}\(1\) now ACTIVE'
r'update for channel {}/1 now ACTIVE'
.format(scid),
'to CHANNELD_NORMAL'])
for i in range(len(outnodes)):
scid = outchans[i]
l1_logs += [r'update for channel {}\(0\) now ACTIVE'.format(scid),
r'update for channel {}\(1\) now ACTIVE'.format(scid),
l1_logs += [r'update for channel {}/0 now ACTIVE'.format(scid),
r'update for channel {}/1 now ACTIVE'.format(scid),
'to CHANNELD_NORMAL']
outnodes[i].daemon.wait_for_logs([r'update for channel {}\(0\) now ACTIVE'
outnodes[i].daemon.wait_for_logs([r'update for channel {}/0 now ACTIVE'
.format(scid),
r'update for channel {}\(1\) now ACTIVE'
r'update for channel {}/1 now ACTIVE'
.format(scid),
'to CHANNELD_NORMAL'])

View File

@ -978,9 +978,9 @@ def test_getroute_exclude(node_factory, bitcoind):
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'
l1.daemon.wait_for_logs([r'update for channel {}/0 now ACTIVE'
.format(scid),
r'update for channel {}\(1\) now ACTIVE'
r'update for channel {}/1 now ACTIVE'
.format(scid)])
# l3 id is > l2 id, so 1 means l3->l2

View File

@ -151,7 +151,7 @@ def test_pay_get_error_with_update(node_factory):
l1.daemon.wait_for_log(r'Extracted channel_update 0102.*from onionreply 10070088[0-9a-fA-F]{88}')
# And now monitor for l1 to apply the channel_update we just extracted
l1.daemon.wait_for_log(r'Received channel_update for channel {}\(.\) now DISABLED was ACTIVE \(from error\)'.format(chanid2))
l1.daemon.wait_for_log(r'Received channel_update for channel {}/. now DISABLED was ACTIVE \(from error\)'.format(chanid2))
def test_pay_optional_args(node_factory):

View File

@ -536,14 +536,14 @@ class LightningNode(object):
if wait_for_active:
# We wait until gossipd sees both local updates, as well as status NORMAL,
# so it can definitely route through.
self.daemon.wait_for_logs([r'update for channel {}\(0\) now ACTIVE'
self.daemon.wait_for_logs([r'update for channel {}/0 now ACTIVE'
.format(scid),
r'update for channel {}\(1\) now ACTIVE'
r'update for channel {}/1 now ACTIVE'
.format(scid),
'to CHANNELD_NORMAL'])
l2.daemon.wait_for_logs([r'update for channel {}\(0\) now ACTIVE'
l2.daemon.wait_for_logs([r'update for channel {}/0 now ACTIVE'
.format(scid),
r'update for channel {}\(1\) now ACTIVE'
r'update for channel {}/1 now ACTIVE'
.format(scid),
'to CHANNELD_NORMAL'])
return scid
@ -596,9 +596,9 @@ class LightningNode(object):
# (or for local channels, at least a local announcement)
def wait_for_routes(self, channel_ids):
# Could happen in any order...
self.daemon.wait_for_logs(['Received channel_update for channel {}\\(0\\)'.format(c)
self.daemon.wait_for_logs(['Received channel_update for channel {}/0'.format(c)
for c in channel_ids]
+ ['Received channel_update for channel {}\\(1\\)'.format(c)
+ ['Received channel_update for channel {}/1'.format(c)
for c in channel_ids])
def pay(self, dst, amt, label=None):
@ -829,7 +829,7 @@ class NodeFactory(object):
for src, dst in connections:
wait_for(lambda: src.channel_state(dst) == 'CHANNELD_NORMAL')
scid = src.get_channel_scid(dst)
src.daemon.wait_for_log(r'Received channel_update for channel {scid}\(.\) now ACTIVE'.format(scid=scid))
src.daemon.wait_for_log(r'Received channel_update for channel {scid}/. now ACTIVE'.format(scid=scid))
scids.append(scid)
if not wait_for_announce:

View File

@ -161,6 +161,13 @@ void fromwire_short_channel_id(const u8 **cursor, size_t *max,
short_channel_id->u64 = fromwire_u64(cursor, max);
}
void fromwire_short_channel_id_dir(const u8 **cursor, size_t *max,
struct short_channel_id_dir *scidd)
{
fromwire_short_channel_id(cursor, max, &scidd->scid);
scidd->dir = fromwire_bool(cursor, max);
}
void fromwire_sha256(const u8 **cursor, size_t *max, struct sha256 *sha256)
{
fromwire(cursor, max, sha256, sizeof(*sha256));
@ -235,6 +242,7 @@ char *fromwire_wirestring(const tal_t *ctx, const u8 **cursor, size_t *max)
}
REGISTER_TYPE_TO_STRING(short_channel_id, short_channel_id_to_str);
REGISTER_TYPE_TO_STRING(short_channel_id_dir, short_channel_id_dir_to_str);
REGISTER_TYPE_TO_HEXSTR(channel_id);
/* BOLT #2:

View File

@ -109,6 +109,13 @@ void towire_short_channel_id(u8 **pptr,
towire_u64(pptr, short_channel_id->u64);
}
void towire_short_channel_id_dir(u8 **pptr,
const struct short_channel_id_dir *scidd)
{
towire_short_channel_id(pptr, &scidd->scid);
towire_bool(pptr, scidd->dir);
}
void towire_sha256(u8 **pptr, const struct sha256 *sha256)
{
towire(pptr, sha256, sizeof(*sha256));

View File

@ -47,6 +47,8 @@ void towire_secp256k1_ecdsa_recoverable_signature(u8 **pptr,
void towire_channel_id(u8 **pptr, const struct channel_id *channel_id);
void towire_short_channel_id(u8 **pptr,
const struct short_channel_id *short_channel_id);
void towire_short_channel_id_dir(u8 **pptr,
const struct short_channel_id_dir *scidd);
void towire_sha256(u8 **pptr, const struct sha256 *sha256);
void towire_sha256_double(u8 **pptr, const struct sha256_double *sha256d);
void towire_bitcoin_txid(u8 **pptr, const struct bitcoin_txid *txid);
@ -87,6 +89,8 @@ void fromwire_channel_id(const u8 **cursor, size_t *max,
struct channel_id *channel_id);
void fromwire_short_channel_id(const u8 **cursor, size_t *max,
struct short_channel_id *short_channel_id);
void fromwire_short_channel_id_dir(const u8 **cursor, size_t *max,
struct short_channel_id_dir *scidd);
void fromwire_sha256(const u8 **cursor, size_t *max, struct sha256 *sha256);
void fromwire_sha256_double(const u8 **cursor, size_t *max,
struct sha256_double *sha256d);