Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e88e051
gossmap: don't crash on localmods on non-existant channels.
rustyrussell Oct 3, 2024
8146177
askrene: add support for disabled channels in layers.
rustyrussell Oct 3, 2024
a657d15
common: round out the short_channel_id_dir JSON routines.
rustyrussell Oct 3, 2024
1a76fb9
add askrene-disable-channel
Lagrang3 Oct 3, 2024
c6350f9
askrene: use short_channel_id_dir in API.
rustyrussell Oct 3, 2024
1cc30c3
askrene: askrene-create-layer and askrene-remove-layer.
rustyrussell Oct 3, 2024
c099b2d
askrene: make `route_query` contain pointer to the command.
rustyrussell Oct 3, 2024
eda4bdc
askrene: clean up reserve array handling.
rustyrussell Oct 3, 2024
05517f2
askrene: remember individual reservations, for better debugging.
rustyrussell Oct 3, 2024
6671c6d
askrene: implement listreservations
rustyrussell Oct 3, 2024
f63727a
askrene: change inform interface, take into account reserve.
rustyrussell Oct 3, 2024
96ac580
askrene: rework constraints to exist in pairs.
rustyrussell Oct 3, 2024
2b08b25
askrene: remove unused parameter in layer_add_localmods.
rustyrussell Oct 3, 2024
51294e6
gossmods_from_listpeerchannels: use correct type for cltv_delta.
rustyrussell Oct 3, 2024
c24491a
pytest: enhance test_getroutes_auto_sourcefree with same case *withou…
rustyrussell Oct 3, 2024
792525b
askrene: ignore disabled channels for min-cost-flow.
rustyrussell Oct 3, 2024
0239ad9
gossmap: implement partial updates.
rustyrussell Oct 3, 2024
2e6771b
common/gossmods_listpeerchannels: include channel capacity in callback.
rustyrussell Oct 3, 2024
b60de76
gossmap: keep capacity for locally-generated channels as well.
rustyrussell Oct 3, 2024
8ea4971
askrene: split askrene-create-channel into create-channel and update-…
rustyrussell Oct 3, 2024
d2f64d7
askrene: don't replace constraints, simply accumulate.
rustyrussell Oct 3, 2024
c55e2d1
askrene: give better feedback when we can't find a suitable route.
rustyrussell Oct 3, 2024
4d473f1
askrene: trivial changes to avoid -O3 compiler warnings.
rustyrussell Oct 3, 2024
a74686f
askrene: more code tweaks on feedback from Lagrang3.
rustyrussell Oct 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
247 changes: 175 additions & 72 deletions common/gossmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -474,8 +474,8 @@ static struct gossmap_chan *add_channel(struct gossmap *map,
return NULL;
}

/* gossipd writes WIRE_GOSSIP_STORE_CHANNEL_AMOUNT after this (not for
* local channels), so ignore channel_announcement until that appears */
/* gossipd writes WIRE_GOSSIP_STORE_CHANNEL_AMOUNT after this,
* so ignore channel_announcement until that appears */
if (msglen
&& (map->map_size < cannounce_off + msglen + sizeof(struct gossip_hdr) + sizeof(u16) + sizeof(u64)))
return NULL;
Expand Down Expand Up @@ -760,24 +760,34 @@ static void destroy_map(struct gossmap *map)
}

/* Local modifications. We only expect a few, so we use a simple
* array. */
* array. If this changes, use a hashtable and a storage area for all
* those pointers to avoid dynamic allocation overhead! */
struct localmod {
struct short_channel_id scid;
/* If this is an entirely-local channel, here's its offset.
* Otherwise, 0xFFFFFFFF. */
u32 local_off;

/* Are updates in either direction set? */
bool updates_set[2];
/* hc[n] defined if updates_set[n]. */
struct half_chan hc[2];
/* orig[n] defined if updates_set[n] and local_off == 0xFFFFFFFF */
/* Non-NULL values mean change existing ones */
struct localmod_changes {
const bool *enabled;
const fp16_t *htlc_min, *htlc_max;
const u32 *base_fee, *proportional_fee;
const u16 *delay;
} changes[2];

/* orig[n] defined if local_off == 0xFFFFFFFF */
struct half_chan orig[2];

/* Original update offsets */
u32 orig_cupdate_off[2];
};

static bool localmod_is_local_chan(const struct localmod *mod)
{
return mod->local_off != 0xFFFFFFFF;
}

struct gossmap_localmods {
struct localmod *mods;
/* This is the local array to be used by the gossmap */
Expand Down Expand Up @@ -818,6 +828,7 @@ bool gossmap_local_addchan(struct gossmap_localmods *localmods,
const struct node_id *n1,
const struct node_id *n2,
struct short_channel_id scid,
struct amount_msat capacity,
const u8 *features)
{
be16 be16;
Expand All @@ -837,15 +848,23 @@ bool gossmap_local_addchan(struct gossmap_localmods *localmods,
* compressed keys sorted in ascending lexicographic order.
*/
if (node_id_cmp(n1, n2) > 0)
return gossmap_local_addchan(localmods, n2, n1, scid, features);
return gossmap_local_addchan(localmods, n2, n1, scid, capacity,
features);

mod.scid = scid;
mod.updates_set[0] = mod.updates_set[1] = false;
memset(&mod.changes, 0, sizeof(mod.changes));

/* We create fake local channel_announcement. */
/* We create amount, then fake local channel_announcement */
off = insert_local_space(localmods,
2 + 64 * 4 + 2 + tal_bytelen(features)
8 + 2 + 64 * 4 + 2 + tal_bytelen(features)
+ 32 + 8 + 33 + 33);

/* Write amount */
be64 = be64_to_cpu(capacity.millisatoshis / 1000 /* Raw: gossmap */);
memcpy(localmods->local + off, &be64, sizeof(be64));
off += sizeof(be64);

/* From here is a channel-announcment, with only the fields we use */
mod.local_off = off;

/* Set type to be kosher. */
Expand Down Expand Up @@ -885,53 +904,100 @@ bool gossmap_local_addchan(struct gossmap_localmods *localmods,
return true;
};

/* Insert a local-only channel_update. */
/* Insert a local-only channel_update: false if can't represent. */
bool gossmap_local_updatechan(struct gossmap_localmods *localmods,
struct short_channel_id scid,
struct amount_msat htlc_min,
struct amount_msat htlc_max,
u32 base_fee,
u32 proportional_fee,
u16 delay,
bool enabled,
int dir)
const struct short_channel_id_dir *scidd,
const bool *enabled,
const struct amount_msat *htlc_min,
const struct amount_msat *htlc_max,
const struct amount_msat *base_fee,
const u32 *proportional_fee,
const u16 *delay)
{
struct localmod *mod;
struct localmod_changes *lc;
struct half_chan test;

/* Check fit before making any changes. */
if (base_fee) {
test.base_fee = base_fee->millisatoshis /* Raw: localmod */;
if (!amount_msat_eq(amount_msat(test.base_fee), *base_fee))
return false;
}
if (proportional_fee) {
test.proportional_fee = *proportional_fee;
if (test.proportional_fee != *proportional_fee)
return false;
}
if (delay) {
test.delay = *delay;
if (test.delay != *delay)
return false;
}

mod = find_localmod(localmods, scid);
mod = find_localmod(localmods, scidd->scid);
if (!mod) {
/* Create new reference to (presumably) existing channel. */
size_t nmods = tal_count(localmods->mods);

tal_resize(&localmods->mods, nmods + 1);
mod = &localmods->mods[nmods];
mod->scid = scid;
mod->updates_set[0] = mod->updates_set[1] = false;
mod->scid = scidd->scid;
memset(&mod->changes, 0, sizeof(mod->changes));
mod->local_off = 0xFFFFFFFF;
}

assert(dir == 0 || dir == 1);
mod->updates_set[dir] = true;
mod->hc[dir].enabled = enabled;
/* node_idx needs to be set once we're in the gossmap. */
mod->hc[dir].htlc_min
= u64_to_fp16(htlc_min.millisatoshis, /* Raw: to fp16 */
false);
mod->hc[dir].htlc_max
= u64_to_fp16(htlc_max.millisatoshis, /* Raw: to fp16 */
true);
mod->hc[dir].base_fee = base_fee;
mod->hc[dir].proportional_fee = proportional_fee;
mod->hc[dir].delay = delay;

/* Check they fit */
if (mod->hc[dir].base_fee != base_fee
|| mod->hc[dir].proportional_fee != proportional_fee
|| mod->hc[dir].delay != delay)
return false;
lc = &mod->changes[scidd->dir];
if (enabled) {
tal_free(lc->enabled);
lc->enabled = tal_dup(localmods, bool, enabled);
}
if (htlc_min) {
fp16_t min = u64_to_fp16(htlc_min->millisatoshis, /* Raw: to fp16 */
false);
tal_free(lc->htlc_min);
lc->htlc_min = tal_dup(localmods, fp16_t, &min);
}
if (htlc_max) {
fp16_t max = u64_to_fp16(htlc_max->millisatoshis, /* Raw: to fp16 */
true);
tal_free(lc->htlc_max);
lc->htlc_max = tal_dup(localmods, fp16_t, &max);
}
if (base_fee) {
u32 base_as_u32 = base_fee->millisatoshis; /* Raw: localmod */
tal_free(lc->base_fee);
lc->base_fee = tal_dup(localmods, u32, &base_as_u32);
}
if (proportional_fee) {
tal_free(lc->proportional_fee);
lc->proportional_fee = tal_dup(localmods, u32, proportional_fee);
}
if (delay) {
tal_free(lc->delay);
lc->delay = tal_dup(localmods, u16, delay);
}
return true;
}

bool gossmap_local_setchan(struct gossmap_localmods *localmods,
struct short_channel_id scid,
struct amount_msat htlc_min,
struct amount_msat htlc_max,
struct amount_msat base_fee,
u32 proportional_fee,
u16 delay,
bool enabled,
int dir)
{
struct short_channel_id_dir scidd = {scid, dir};
return gossmap_local_updatechan(localmods, &scidd,
&enabled,
&htlc_min, &htlc_max,
&base_fee, &proportional_fee,
&delay);
}

/* Apply localmods to this map */
void gossmap_apply_localmods(struct gossmap *map,
struct gossmap_localmods *localmods)
Expand All @@ -949,22 +1015,56 @@ void gossmap_apply_localmods(struct gossmap *map,
chan = gossmap_find_chan(map, &mod->scid);
/* If it doesn't exist, are we supposed to create a local one? */
if (!chan) {
if (mod->local_off == 0xFFFFFFFF)
if (!localmod_is_local_chan(mod))
continue;

/* Create new channel, pointing into local. */
chan = add_channel(map, map->map_size + mod->local_off, 0);
}

/* Save old, overwrite (keep nodeidx) */
/* Save old, update any fields they wanted to change */
for (size_t h = 0; h < 2; h++) {
if (!mod->updates_set[h])
continue;
bool was_set, all_changed;
const struct localmod_changes *c = &mod->changes[h];
/* Save existing versions */
mod->orig[h] = chan->half[h];
mod->orig_cupdate_off[h] = chan->cupdate_off[h];
chan->half[h] = mod->hc[h];
chan->half[h].nodeidx = mod->orig[h].nodeidx;
chan->cupdate_off[h] = 0xFFFFFFFF;

was_set = gossmap_chan_set(chan, h);

/* Override specified fields. */
all_changed = true;
if (c->enabled)
chan->half[h].enabled = *c->enabled;
else
all_changed = false;
if (c->htlc_min)
chan->half[h].htlc_min = *c->htlc_min;
else
all_changed = false;
if (c->htlc_max)
chan->half[h].htlc_max = *c->htlc_max;
else
all_changed = false;
if (c->base_fee)
chan->half[h].base_fee = *c->base_fee;
else
all_changed = false;
if (c->proportional_fee)
chan->half[h].proportional_fee = *c->proportional_fee;
else
all_changed = false;
if (c->delay)
chan->half[h].delay = *c->delay;
else
all_changed = false;

/* Is it all defined?
* This controls gossmap_chan_set(chan, h); */
if (was_set || all_changed)
chan->cupdate_off[h] = 0xFFFFFFFF;
else
chan->cupdate_off[h] = 0;
}
}
}
Expand All @@ -980,19 +1080,17 @@ void gossmap_remove_localmods(struct gossmap *map,
const struct localmod *mod = &localmods->mods[i];
struct gossmap_chan *chan = gossmap_find_chan(map, &mod->scid);

/* If there was no channel, ignore */
if (!chan)
continue;

/* If that's a local channel, remove it now. */
if (chan->cann_off >= map->map_size) {
gossmap_remove_chan(map, chan);
} else {
/* Restore (keep nodeidx). */
/* Restore. */
for (size_t h = 0; h < 2; h++) {
u32 nodeidx;
if (!mod->updates_set[h])
continue;

nodeidx = chan->half[h].nodeidx;
chan->half[h] = mod->orig[h];
chan->half[h].nodeidx = nodeidx;
chan->cupdate_off[h] = mod->orig_cupdate_off[h];
}
}
Expand Down Expand Up @@ -1135,34 +1233,39 @@ bool gossmap_chan_is_dying(const struct gossmap *map,
return ghdr.flags & CPU_TO_BE16(GOSSIP_STORE_DYING_BIT);
}

bool gossmap_chan_get_capacity(const struct gossmap *map,
const struct gossmap_chan *c,
struct amount_sat *amount)
struct amount_msat gossmap_chan_get_capacity(const struct gossmap *map,
const struct gossmap_chan *c)
{
struct gossip_hdr ghdr;
size_t off;
u16 type;
struct amount_sat sat;
struct amount_msat msat;

/* Fail for local channels */
if (gossmap_chan_is_localmod(map, c))
return false;
if (gossmap_chan_is_localmod(map, c)) {
/* Amount is *before* c->cann_off */
off = c->cann_off - sizeof(u64);
goto get_amount;
}

/* Skip over this record to next; expect a gossip_store_channel_amount */
off = c->cann_off - sizeof(ghdr);
map_copy(map, off, &ghdr, sizeof(ghdr));
off += sizeof(ghdr) + be16_to_cpu(ghdr.len);

/* Partial write, this can happen. */
if (off + sizeof(ghdr) + sizeof(u16) + sizeof(u64) > map->map_size)
return false;

/* Get type of next field. */
/* We don't allow loading a channel unless it has capacity field! */
type = map_be16(map, off + sizeof(ghdr));
if (type != WIRE_GOSSIP_STORE_CHANNEL_AMOUNT)
return false;
assert(type == WIRE_GOSSIP_STORE_CHANNEL_AMOUNT);

*amount = amount_sat(map_be64(map, off + sizeof(ghdr) + sizeof(be16)));
return true;
off += sizeof(ghdr) + sizeof(be16);

get_amount:
/* Shouldn't overflow */
sat = amount_sat(map_be64(map, off));
if (!amount_sat_to_msat(&msat, sat))
errx(1, "invalid capacity %s", fmt_amount_sat(tmpctx, sat));

return msat;
}

struct gossmap_chan *gossmap_nth_chan(const struct gossmap *map,
Expand Down
Loading
Loading