mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-20 15:44:21 +01:00
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.
This commit is contained in:
committed by
Rusty Russell
parent
83f57ac300
commit
544e110c96
@@ -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,57 +468,83 @@ 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,
|
|
||||||
&curhop->channel_id) &&
|
|
||||||
curhint->scid.dir == curhop->direction) {
|
|
||||||
|
|
||||||
/* Update the number of htlcs for any local
|
/* If we don't have a hint we can't fail updating it. */
|
||||||
* channel in the route */
|
if (!curhint)
|
||||||
if (curhint->local && remove)
|
continue;
|
||||||
curhint->htlc_budget++;
|
|
||||||
else if (curhint->local)
|
|
||||||
curhint->htlc_budget--;
|
|
||||||
|
|
||||||
if (remove && !amount_msat_add(
|
/* A failure can happen if we add an HTLC, and either
|
||||||
&curhint->estimated_capacity,
|
* the local htlc_budget is exhausted, or the capacity
|
||||||
curhint->estimated_capacity,
|
* is exceeded. */
|
||||||
curhop->amount)) {
|
if ((curhint->local && curhint->htlc_budget <= 0) ||
|
||||||
/* This should never happen, it'd mean
|
amount_msat_greater(curhop->amount,
|
||||||
* that we unapply a route that would
|
curhint->estimated_capacity)) {
|
||||||
* result in a msatoshi
|
/* This can happen in case of multiple
|
||||||
* wrap-around. */
|
* concurrent getroute calls using the
|
||||||
abort();
|
* same channel_hints, no biggy, it's
|
||||||
} else if (!amount_msat_sub(
|
* an estimation anyway. */
|
||||||
&curhint->estimated_capacity,
|
paymod_log(p, LOG_DBG,
|
||||||
curhint->estimated_capacity,
|
"Could not update the channel hint "
|
||||||
curhop->amount)) {
|
"for %s. Could be a concurrent "
|
||||||
/* This can happen in case of multipl
|
"`getroute` call.",
|
||||||
* concurrent getroute calls using the
|
type_to_string(tmpctx,
|
||||||
* same channel_hints, no biggy, it's
|
struct short_channel_id_dir,
|
||||||
* an estimation anyway. */
|
&curhint->scid));
|
||||||
paymod_log(
|
return false;
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
* channel in the route */
|
||||||
|
if (curhint->local && remove)
|
||||||
|
curhint->htlc_budget++;
|
||||||
|
else if (curhint->local)
|
||||||
|
curhint->htlc_budget--;
|
||||||
|
|
||||||
|
if (remove && !amount_msat_add(
|
||||||
|
&curhint->estimated_capacity,
|
||||||
|
curhint->estimated_capacity,
|
||||||
|
curhop->amount)) {
|
||||||
|
/* This should never happen, it'd mean
|
||||||
|
* that we unapply a route that would
|
||||||
|
* result in a msatoshi
|
||||||
|
* wrap-around. */
|
||||||
|
abort();
|
||||||
|
} else if (!amount_msat_sub(
|
||||||
|
&curhint->estimated_capacity,
|
||||||
|
curhint->estimated_capacity,
|
||||||
|
curhop->amount)) {
|
||||||
|
/* Given our preemptive test
|
||||||
|
* above, this should never
|
||||||
|
* happen either. */
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct short_channel_id_dir *
|
static const struct short_channel_id_dir *
|
||||||
|
|||||||
Reference in New Issue
Block a user