gossmap: save the offset where the channel updates are, function for details.

This takes an extra 8 bytes per channel, but means we can go back and
get more information about them; this is implemented in
gossmap_chan_get_update_details() which is what listchannels will need.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2021-05-22 14:30:22 +09:30
parent d1f8a5f0be
commit 770a328d56
3 changed files with 164 additions and 5 deletions

View File

@ -336,6 +336,7 @@ static struct gossmap_chan *new_channel(struct gossmap *map,
chan->cann_off = cannounce_off; chan->cann_off = cannounce_off;
chan->private = private; chan->private = private;
chan->plus_scid_off = plus_scid_off; chan->plus_scid_off = plus_scid_off;
chan->cupdate_off[0] = chan->cupdate_off[1] = 0;
memset(chan->half, 0, sizeof(chan->half)); memset(chan->half, 0, sizeof(chan->half));
chan->half[0].nodeidx = n1idx; chan->half[0].nodeidx = n1idx;
chan->half[1].nodeidx = n2idx; chan->half[1].nodeidx = n2idx;
@ -491,11 +492,14 @@ static void update_channel(struct gossmap *map, size_t cupdate_off)
= u64_to_fp16(map_be64(map, htlc_maximum_off), true); = u64_to_fp16(map_be64(map, htlc_maximum_off), true);
else else
hc.htlc_max = 0xFFFF; hc.htlc_max = 0xFFFF;
chanflags = map_u8(map, channel_flags_off);
hc.enabled = !(chanflags & 2);
hc.base_fee = map_be32(map, fee_base_off); hc.base_fee = map_be32(map, fee_base_off);
hc.proportional_fee = map_be32(map, fee_prop_off); hc.proportional_fee = map_be32(map, fee_prop_off);
hc.delay = map_be16(map, cltv_expiry_delta_off); hc.delay = map_be16(map, cltv_expiry_delta_off);
/* Check they fit */ /* Check they fit: we turn off if not. */
if (hc.base_fee != map_be32(map, fee_base_off) if (hc.base_fee != map_be32(map, fee_base_off)
|| hc.proportional_fee != map_be32(map, fee_prop_off) || hc.proportional_fee != map_be32(map, fee_prop_off)
|| hc.delay != map_be16(map, cltv_expiry_delta_off)) { || hc.delay != map_be16(map, cltv_expiry_delta_off)) {
@ -504,14 +508,14 @@ static void update_channel(struct gossmap *map, size_t cupdate_off)
map_be32(map, fee_base_off), map_be32(map, fee_base_off),
map_be32(map, fee_prop_off), map_be32(map, fee_prop_off),
map_be16(map, cltv_expiry_delta_off)); map_be16(map, cltv_expiry_delta_off));
return; hc.htlc_max = 0;
hc.enabled = false;
} }
chanflags = map_u8(map, channel_flags_off);
hc.enabled = !(chanflags & 2);
/* Preserve this */ /* Preserve this */
hc.nodeidx = chan->half[chanflags & 1].nodeidx; hc.nodeidx = chan->half[chanflags & 1].nodeidx;
chan->half[chanflags & 1] = hc; chan->half[chanflags & 1] = hc;
chan->cupdate_off[chanflags & 1] = cupdate_off;
} }
static void remove_channel_by_deletemsg(struct gossmap *map, size_t del_off) static void remove_channel_by_deletemsg(struct gossmap *map, size_t del_off)
@ -679,6 +683,9 @@ struct localmod {
struct half_chan hc[2]; struct half_chan hc[2];
/* orig[n] defined if updates_set[n] and local_off == 0xFFFFFFFF */ /* orig[n] defined if updates_set[n] and local_off == 0xFFFFFFFF */
struct half_chan orig[2]; struct half_chan orig[2];
/* Original update offsets */
u32 orig_cupdate_off[2];
}; };
struct gossmap_localmods { struct gossmap_localmods {
@ -853,8 +860,10 @@ void gossmap_apply_localmods(struct gossmap *map,
if (!mod->updates_set[h]) if (!mod->updates_set[h])
continue; continue;
mod->orig[h] = chan->half[h]; mod->orig[h] = chan->half[h];
mod->orig_cupdate_off[h] = chan->cupdate_off[h];
chan->half[h] = mod->hc[h]; chan->half[h] = mod->hc[h];
chan->half[h].nodeidx = mod->orig[h].nodeidx; chan->half[h].nodeidx = mod->orig[h].nodeidx;
chan->cupdate_off[h] = 0xFFFFFFFF;
} }
} }
} }
@ -883,6 +892,7 @@ void gossmap_remove_localmods(struct gossmap *map,
nodeidx = chan->half[h].nodeidx; nodeidx = chan->half[h].nodeidx;
chan->half[h] = mod->orig[h]; chan->half[h] = mod->orig[h];
chan->half[h].nodeidx = nodeidx; chan->half[h].nodeidx = nodeidx;
chan->cupdate_off[h] = mod->orig_cupdate_off[h];
} }
} }
} }
@ -1130,6 +1140,67 @@ int gossmap_chan_get_feature(const struct gossmap *map,
c->cann_off + feature_len_off + 2, feature_len); c->cann_off + feature_len_off + 2, feature_len);
} }
/* BOLT #7:
* 1. type: 258 (`channel_update`)
* 2. data:
* * [`signature`:`signature`]
* * [`chain_hash`:`chain_hash`]
* * [`short_channel_id`:`short_channel_id`]
* * [`u32`:`timestamp`]
* * [`byte`:`message_flags`]
* * [`byte`:`channel_flags`]
* * [`u16`:`cltv_expiry_delta`]
* * [`u64`:`htlc_minimum_msat`]
* * [`u32`:`fee_base_msat`]
* * [`u32`:`fee_proportional_millionths`]
* * [`u64`:`htlc_maximum_msat`] (option_channel_htlc_max)
*/
void gossmap_chan_get_update_details(const struct gossmap *map,
const struct gossmap_chan *chan,
int dir,
u32 *timestamp,
u8 *message_flags,
u8 *channel_flags,
u32 *fee_base_msat,
u32 *fee_proportional_millionths,
struct amount_msat *htlc_minimum_msat,
/* iff message_flags & 1 */
struct amount_msat *htlc_maximum_msat)
{
/* Note that first two bytes are message type */
const size_t scid_off = chan->cupdate_off[dir] + 2 + (64 + 32);
const size_t timestamp_off = scid_off + 8;
const size_t message_flags_off = timestamp_off + 4;
const size_t channel_flags_off = message_flags_off + 1;
const size_t cltv_expiry_delta_off = channel_flags_off + 1;
const size_t htlc_minimum_off = cltv_expiry_delta_off + 2;
const size_t fee_base_off = htlc_minimum_off + 8;
const size_t fee_prop_off = fee_base_off + 4;
const size_t htlc_maximum_off = fee_prop_off + 4;
u8 mflags;
assert(gossmap_chan_set(chan, dir));
if (timestamp)
*timestamp = map_be32(map, timestamp_off);
/* We need this (below), even if they don't want it */
mflags = map_u8(map, message_flags_off);
if (message_flags)
*message_flags = mflags;
if (channel_flags)
*channel_flags = map_u8(map, channel_flags_off);
if (fee_base_msat)
*fee_base_msat = map_be32(map, fee_base_off);
if (fee_proportional_millionths)
*fee_proportional_millionths = map_be32(map, fee_prop_off);
if (htlc_minimum_msat)
*htlc_minimum_msat
= amount_msat(map_be64(map, htlc_minimum_off));
if (htlc_maximum_msat && (mflags & 1))
*htlc_maximum_msat
= amount_msat(map_be64(map, htlc_maximum_off));
}
/* BOLT #7: /* BOLT #7:
* 1. type: 257 (`node_announcement`) * 1. type: 257 (`node_announcement`)
* 2. data: * 2. data:

View File

@ -22,6 +22,9 @@ struct gossmap_chan {
u32 private: 1; u32 private: 1;
/* Technically redundant, but we have a hole anyway: from cann_off */ /* Technically redundant, but we have a hole anyway: from cann_off */
u32 plus_scid_off: 31; u32 plus_scid_off: 31;
/* Offsets of cupdates (0 if missing). Logically inside half_chan,
* but that would add padding. */
u32 cupdate_off[2];
/* two nodes we connect (lesser idx first) */ /* two nodes we connect (lesser idx first) */
struct half_chan { struct half_chan {
/* Top bit indicates it's enabled */ /* Top bit indicates it's enabled */
@ -108,7 +111,7 @@ void gossmap_node_get_id(const struct gossmap *map,
/* Do we have any values for this halfchannel ? */ /* Do we have any values for this halfchannel ? */
static inline bool gossmap_chan_set(const struct gossmap_chan *chan, int dir) static inline bool gossmap_chan_set(const struct gossmap_chan *chan, int dir)
{ {
return chan->half[dir].htlc_max != 0; return chan->cupdate_off[dir] != 0;
} }
/* Return capacity if it's known (fails only on race condition) */ /* Return capacity if it's known (fails only on race condition) */
@ -136,6 +139,20 @@ int gossmap_node_get_feature(const struct gossmap *map,
const struct gossmap_node *n, const struct gossmap_node *n,
int fbit); int fbit);
/* Returns details from channel_update (must be gossmap_chan_set, and
* does not work for local_updatechan! */
void gossmap_chan_get_update_details(const struct gossmap *map,
const struct gossmap_chan *chan,
int dir,
u32 *timestamp,
u8 *message_flags,
u8 *channel_flags,
u32 *fee_base_msat,
u32 *fee_proportional_millionths,
struct amount_msat *htlc_minimum_msat,
/* iff message_flags & 1 */
struct amount_msat *htlc_maximum_msat);
/* Given a struct node, get the nth channel, and tell us if we're half[0/1]. /* Given a struct node, get the nth channel, and tell us if we're half[0/1].
* n must be less than node->num_chans */ * n must be less than node->num_chans */
struct gossmap_chan *gossmap_nth_chan(const struct gossmap *map, struct gossmap_chan *gossmap_nth_chan(const struct gossmap *map,

View File

@ -300,6 +300,9 @@ int main(int argc, char *argv[])
struct gossmap_chan *chan; struct gossmap_chan *chan;
struct gossmap_localmods *mods; struct gossmap_localmods *mods;
struct amount_sat capacity; struct amount_sat capacity;
u32 timestamp, fee_base_msat, fee_proportional_millionths;
u8 message_flags, channel_flags;
struct amount_msat htlc_minimum_msat, htlc_maximum_msat;
common_setup(argv[0]); common_setup(argv[0]);
@ -331,6 +334,74 @@ int main(int argc, char *argv[])
&capacity)); &capacity));
assert(amount_sat_eq(capacity, AMOUNT_SAT(1000000))); assert(amount_sat_eq(capacity, AMOUNT_SAT(1000000)));
gossmap_chan_get_update_details(map, gossmap_find_chan(map, &scid23),
0,
&timestamp,
&message_flags,
&channel_flags,
&fee_base_msat,
&fee_proportional_millionths,
&htlc_minimum_msat,
&htlc_maximum_msat);
assert(timestamp == 1612141433);
assert(message_flags == 1);
assert(channel_flags == 0);
assert(fee_base_msat == 20);
assert(fee_proportional_millionths == 1000);
assert(amount_msat_eq(htlc_minimum_msat, AMOUNT_MSAT(0)));
assert(amount_msat_eq(htlc_maximum_msat, AMOUNT_MSAT(990000000)));
gossmap_chan_get_update_details(map, gossmap_find_chan(map, &scid23),
1,
&timestamp,
&message_flags,
&channel_flags,
&fee_base_msat,
&fee_proportional_millionths,
&htlc_minimum_msat,
&htlc_maximum_msat);
assert(timestamp == 1612141435);
assert(message_flags == 1);
assert(channel_flags == 1);
assert(fee_base_msat == 20);
assert(fee_proportional_millionths == 1000);
assert(amount_msat_eq(htlc_minimum_msat, AMOUNT_MSAT(0)));
assert(amount_msat_eq(htlc_maximum_msat, AMOUNT_MSAT(990000000)));
gossmap_chan_get_update_details(map, gossmap_find_chan(map, &scid12),
0,
&timestamp,
&message_flags,
&channel_flags,
&fee_base_msat,
&fee_proportional_millionths,
&htlc_minimum_msat,
&htlc_maximum_msat);
assert(timestamp == 1612141439);
assert(message_flags == 1);
assert(channel_flags == 0);
assert(fee_base_msat == 20);
assert(fee_proportional_millionths == 1000);
assert(amount_msat_eq(htlc_minimum_msat, AMOUNT_MSAT(0)));
assert(amount_msat_eq(htlc_maximum_msat, AMOUNT_MSAT(990000000)));
gossmap_chan_get_update_details(map, gossmap_find_chan(map, &scid12),
1,
&timestamp,
&message_flags,
&channel_flags,
&fee_base_msat,
&fee_proportional_millionths,
&htlc_minimum_msat,
&htlc_maximum_msat);
assert(timestamp == 1612141439);
assert(message_flags == 1);
assert(channel_flags == 1);
assert(fee_base_msat == 20);
assert(fee_proportional_millionths == 1000);
assert(amount_msat_eq(htlc_minimum_msat, AMOUNT_MSAT(0)));
assert(amount_msat_eq(htlc_maximum_msat, AMOUNT_MSAT(990000000)));
/* Now, let's add a new channel l1 -> l4. */ /* Now, let's add a new channel l1 -> l4. */
mods = gossmap_localmods_new(tmpctx); mods = gossmap_localmods_new(tmpctx);
assert(node_id_from_hexstr("0382ce59ebf18be7d84677c2e35f23294b9992ceca95491fcf8a56c6cb2d9de199", 66, &l4)); assert(node_id_from_hexstr("0382ce59ebf18be7d84677c2e35f23294b9992ceca95491fcf8a56c6cb2d9de199", 66, &l4));