Browse Source

pay: Add a pre-apply check to channel_hint updates

This allows us to atomically update all channel_hints and determine if
we had a collision and therefore should retry.
ppa-prep
Christian Decker 4 years ago
committed by Rusty Russell
parent
commit
544e110c96
  1. 83
      plugins/libplugin-pay.c

83
plugins/libplugin-pay.c

@ -444,6 +444,21 @@ payment_constraints_update(struct payment_constraints *cons,
return true; return true;
} }
static struct channel_hint *payment_chanhints_get(struct payment *p,
struct route_hop *h)
{
struct payment *root = payment_root(p);
struct channel_hint *curhint;
for (size_t j = 0; j < tal_count(root->channel_hints); j++) {
curhint = &root->channel_hints[j];
if (short_channel_id_eq(&curhint->scid.scid, &h->channel_id) &&
curhint->scid.dir == h->direction) {
return curhint;
}
}
return NULL;
}
/* Given a route and a couple of channel hints, apply the route to the channel /* Given a route and a couple of channel hints, apply the route to the channel
* hints, so we have a better estimation of channel's capacity. We apply a * hints, so we have a better estimation of channel's capacity. We apply a
* route to a channel hint before calling `sendonion` so subsequent `route` * route to a channel hint before calling `sendonion` so subsequent `route`
@ -453,19 +468,55 @@ payment_constraints_update(struct payment_constraints *cons,
* through, since the balances really changed in that case. The `remove` * through, since the balances really changed in that case. The `remove`
* argument indicates whether we want to apply (`remove=false`), or clear a * argument indicates whether we want to apply (`remove=false`), or clear a
* prior application (`remove=true`). */ * prior application (`remove=true`). */
static void payment_chanhints_apply_route(struct payment *p, bool remove) static bool payment_chanhints_apply_route(struct payment *p, bool remove)
{ {
struct route_hop *curhop; struct route_hop *curhop;
struct channel_hint *curhint; struct channel_hint *curhint;
struct payment *root = payment_root(p); struct payment *root = payment_root(p);
assert(p->route != NULL); assert(p->route != NULL);
/* No need to check for applicability if we increase
* capacity and budgets. */
if (remove)
goto apply_changes;
/* First round: make sure we can cleanly apply the update. */
for (size_t i = 0; i < tal_count(p->route); i++) { for (size_t i = 0; i < tal_count(p->route); i++) {
curhop = &p->route[i]; curhop = &p->route[i];
for (size_t j = 0; j < tal_count(root->channel_hints); j++) { curhint = payment_chanhints_get(root, curhop);
curhint = &root->channel_hints[j];
if (short_channel_id_eq(&curhint->scid.scid, /* If we don't have a hint we can't fail updating it. */
&curhop->channel_id) && if (!curhint)
curhint->scid.dir == curhop->direction) { continue;
/* A failure can happen if we add an HTLC, and either
* the local htlc_budget is exhausted, or the capacity
* is exceeded. */
if ((curhint->local && curhint->htlc_budget <= 0) ||
amount_msat_greater(curhop->amount,
curhint->estimated_capacity)) {
/* This can happen in case of multiple
* concurrent getroute calls using the
* same channel_hints, no biggy, it's
* an estimation anyway. */
paymod_log(p, LOG_DBG,
"Could not update the channel hint "
"for %s. Could be a concurrent "
"`getroute` call.",
type_to_string(tmpctx,
struct short_channel_id_dir,
&curhint->scid));
return false;
}
}
apply_changes:
/* Second round: apply the changes, now that we know they'll succeed. */
for (size_t i = 0; i < tal_count(p->route); i++) {
curhop = &p->route[i];
curhint = payment_chanhints_get(root, curhop);
if (!curhint)
continue;
/* Update the number of htlcs for any local /* Update the number of htlcs for any local
* channel in the route */ * channel in the route */
@ -487,23 +538,13 @@ static void payment_chanhints_apply_route(struct payment *p, bool remove)
&curhint->estimated_capacity, &curhint->estimated_capacity,
curhint->estimated_capacity, curhint->estimated_capacity,
curhop->amount)) { curhop->amount)) {
/* This can happen in case of multipl /* Given our preemptive test
* concurrent getroute calls using the * above, this should never
* same channel_hints, no biggy, it's * happen either. */
* an estimation anyway. */ abort();
paymod_log(
p, LOG_UNUSUAL,
"Could not update the channel hint "
"for %s. Could be a concurrent "
"`getroute` call.",
type_to_string(
tmpctx,
struct short_channel_id_dir,
&curhint->scid));
}
}
} }
} }
return true;
} }
static const struct short_channel_id_dir * static const struct short_channel_id_dir *

Loading…
Cancel
Save