Browse Source

paymod: Rewrite the shadow-route constraint enforcement

We now check against both constraints on the modifier and the payment before
applying either. This "fixes" the assert that was causing the crash in #3851,
but we are still looking for the source of the inconsistency where the
modifier constraints, initialized to 1/4th of the payment, suddenly get more
permissive than the payment itself.
fixup-0.9.0
Christian Decker 5 years ago
parent
commit
68e23f4232
  1. 80
      plugins/libplugin-pay.c

80
plugins/libplugin-pay.c

@ -1782,7 +1782,7 @@ static struct command_result *shadow_route_listchannels(struct command *cmd,
u64 sample = 0; u64 sample = 0;
struct amount_msat best_fee; struct amount_msat best_fee;
const jsmntok_t *sattok, *delaytok, *basefeetok, *propfeetok, *desttok, const jsmntok_t *sattok, *delaytok, *basefeetok, *propfeetok, *desttok,
*channelstok, *chan; *channelstok, *chan, *scidtok;
/* Check the invariants on the constraints between payment and modifier. */ /* Check the invariants on the constraints between payment and modifier. */
assert(d->constraints.cltv_budget <= p->constraints.cltv_budget / 4); assert(d->constraints.cltv_budget <= p->constraints.cltv_budget / 4);
@ -1800,18 +1800,21 @@ static struct command_result *shadow_route_listchannels(struct command *cmd,
delaytok = json_get_member(buf, chan, "delay"); delaytok = json_get_member(buf, chan, "delay");
basefeetok = json_get_member(buf, chan, "base_fee_millisatoshi"); basefeetok = json_get_member(buf, chan, "base_fee_millisatoshi");
propfeetok = json_get_member(buf, chan, "fee_per_millionth"); propfeetok = json_get_member(buf, chan, "fee_per_millionth");
scidtok = json_get_member(buf, chan, "short_channel_id");
desttok = json_get_member(buf, chan, "destination"); desttok = json_get_member(buf, chan, "destination");
if (sattok == NULL || delaytok == NULL || if (sattok == NULL || delaytok == NULL ||
delaytok->type != JSMN_PRIMITIVE || basefeetok == NULL || delaytok->type != JSMN_PRIMITIVE || basefeetok == NULL ||
basefeetok->type != JSMN_PRIMITIVE || propfeetok == NULL || basefeetok->type != JSMN_PRIMITIVE || propfeetok == NULL ||
propfeetok->type != JSMN_PRIMITIVE || desttok == NULL) propfeetok->type != JSMN_PRIMITIVE || desttok == NULL ||
scidtok == NULL)
continue; continue;
json_to_u16(buf, delaytok, &curr.cltv_expiry_delta); json_to_u16(buf, delaytok, &curr.cltv_expiry_delta);
json_to_number(buf, basefeetok, &curr.fee_base_msat); json_to_number(buf, basefeetok, &curr.fee_base_msat);
json_to_number(buf, propfeetok, json_to_number(buf, propfeetok,
&curr.fee_proportional_millionths); &curr.fee_proportional_millionths);
json_to_short_channel_id(buf, scidtok, &curr.short_channel_id);
json_to_sat(buf, sattok, &capacity); json_to_sat(buf, sattok, &capacity);
json_to_node_id(buf, desttok, &curr.pubkey); json_to_node_id(buf, desttok, &curr.pubkey);
@ -1841,33 +1844,64 @@ static struct command_result *shadow_route_listchannels(struct command *cmd,
} }
if (best != NULL) { if (best != NULL) {
bool ok; /* Check that we could apply the shadow route extension. Check
/* Ok, we found an extension, let's add it. */ * against both the shadow route budget as well as the
d->destination = best->pubkey; * original payment's budget. */
if (best->cltv_expiry_delta > d->constraints.cltv_budget ||
/* Apply deltas to the constraints in the shadow route so we best->cltv_expiry_delta > p->constraints.cltv_budget) {
* don't overshoot our 1/4th target. */
if (!payment_constraints_update(&d->constraints, best_fee,
best->cltv_expiry_delta)) {
best = NULL; best = NULL;
goto next; goto next;
} }
/* Now do the same to the payment constraints so other /* Check the fee budget only if we didn't opt out, since
* modifiers don't do it either. */ * testing against a virtual budget is not useful if we do not
ok = payment_constraints_update(&p->constraints, best_fee, * actually use it (it could give false positives and fail
best->cltv_expiry_delta); * attempts that might have gone through, */
if (d->fuzz_amount &&
/* And now the thing that caused all of this: adjust the call (amount_msat_greater(best_fee, d->constraints.fee_budget) ||
* to getroute. */ (amount_msat_greater(best_fee,
if (d->fuzz_amount) { p->constraints.fee_budget)))) {
/* Only fuzz the amount to route to the destination if best = NULL;
* we didn't opt-out earlier. */ goto next;
ok &= amount_msat_add(&p->getroute->amount,
p->getroute->amount, best_fee);
} }
/* Now we can be sure that adding the shadow route will succeed */
plugin_log(
p->plugin, LOG_DBG,
"Adding shadow_route hop over channel %s: adding %s "
"in fees and %d CLTV delta",
type_to_string(tmpctx, struct short_channel_id,
&best->short_channel_id),
type_to_string(tmpctx, struct amount_msat, &best_fee),
best->cltv_expiry_delta);
d->destination = best->pubkey;
d->constraints.cltv_budget -= best->cltv_expiry_delta;
p->getroute->cltv += best->cltv_expiry_delta; p->getroute->cltv += best->cltv_expiry_delta;
assert(ok);
if (!d->fuzz_amount)
goto next;
/* Only try to apply the fee budget changes if we want to fuzz
* the amount. Virtual fees that we then don't deliver to the
* destination could otherwise cause the route to be too
* expensive, while really being ok. If any of these fail then
* the above checks are insufficient. */
if (!amount_msat_sub(&d->constraints.fee_budget,
d->constraints.fee_budget, best_fee) ||
!amount_msat_sub(&p->constraints.fee_budget,
p->constraints.fee_budget, best_fee))
plugin_err(p->plugin,
"Could not update fee constraints "
"for shadow route extension. "
"payment fee budget %s, modifier "
"fee budget %s, shadow fee to add %s",
type_to_string(tmpctx, struct amount_msat,
&p->constraints.fee_budget),
type_to_string(tmpctx, struct amount_msat,
&d->constraints.fee_budget),
type_to_string(tmpctx, struct amount_msat,
&best_fee));
} }
next: next:

Loading…
Cancel
Save