Browse Source

paymod: Update the route constraints as we apply routes and mods

These are primarily the fee and cltv constraints that we need to keep up to
date in order to give modifiers a correct view of what is and what isn't
allowed.
keysend
Christian Decker 4 years ago
parent
commit
215a0ada8b
  1. 80
      plugins/libplugin-pay.c
  2. 24
      plugins/libplugin-pay.h
  3. 6
      plugins/pay.c

80
plugins/libplugin-pay.c

@ -32,8 +32,9 @@ struct payment *payment_new(tal_t *ctx, struct command *cmd,
p->payment_hash = parent->payment_hash; p->payment_hash = parent->payment_hash;
p->partid = payment_root(p->parent)->next_partid++; p->partid = payment_root(p->parent)->next_partid++;
p->plugin = parent->plugin; p->plugin = parent->plugin;
p->fee_budget = parent->fee_budget;
p->cltv_budget = parent->cltv_budget; /* Re-establish the unmodified constraints for our sub-payment. */
p->constraints = *parent->start_constraints;
} else { } else {
assert(cmd != NULL); assert(cmd != NULL);
p->partid = 0; p->partid = 0;
@ -166,6 +167,8 @@ void payment_start(struct payment *p)
p->getroute->cltv = DEFAULT_FINAL_CLTV_DELTA; p->getroute->cltv = DEFAULT_FINAL_CLTV_DELTA;
p->getroute->amount = p->amount; p->getroute->amount = p->amount;
p->start_constraints = tal_dup(p, struct payment_constraints, &p->constraints);
/* TODO If this is not the root, we can actually skip the getinfo call /* TODO If this is not the root, we can actually skip the getinfo call
* and just reuse the parent's value. */ * and just reuse the parent's value. */
send_outreq(p->plugin, send_outreq(p->plugin,
@ -258,6 +261,41 @@ static void payment_exclude_longest_delay(struct payment *p)
tal_arr_expand(&root->channel_hints, hint); tal_arr_expand(&root->channel_hints, hint);
} }
static struct amount_msat payment_route_fee(struct payment *p)
{
struct amount_msat fee;
if (!amount_msat_sub(&fee, p->route[0].amount, p->amount)) {
plugin_log(
p->plugin,
LOG_BROKEN,
"gossipd returned a route with a negative fee: sending %s "
"to deliver %s",
type_to_string(tmpctx, struct amount_msat,
&p->route[0].amount),
type_to_string(tmpctx, struct amount_msat, &p->amount));
abort();
}
return fee;
}
/* Update the constraints by subtracting the delta_fee and delta_cltv if the
* result is positive. Returns whether or not the update has been applied. */
static WARN_UNUSED_RESULT bool
payment_constraints_update(struct payment_constraints *cons,
const struct amount_msat delta_fee,
const u32 delta_cltv)
{
if (delta_cltv > cons->cltv_budget)
return false;
/* amount_msat_sub performs a check before actually subtracting. */
if (!amount_msat_sub(&cons->fee_budget, cons->fee_budget, delta_fee))
return false;
cons->cltv_budget -= delta_cltv;
return true;
}
static struct command_result *payment_getroute_result(struct command *cmd, static struct command_result *payment_getroute_result(struct command *cmd,
const char *buffer, const char *buffer,
const jsmntok_t *toks, const jsmntok_t *toks,
@ -269,39 +307,36 @@ static struct command_result *payment_getroute_result(struct command *cmd,
p->route = tal_route_from_json(p, buffer, rtok); p->route = tal_route_from_json(p, buffer, rtok);
p->step = PAYMENT_STEP_GOT_ROUTE; p->step = PAYMENT_STEP_GOT_ROUTE;
/* Ensure that our fee and CLTV budgets are respected. */ fee = payment_route_fee(p);
if (!amount_msat_sub(&fee, p->route[0].amount, p->amount)) { /* Ensure that our fee and CLTV budgets are respected. */
plugin_err( if (amount_msat_greater(fee, p->constraints.fee_budget)) {
p->plugin,
"gossipd returned a route with a negative fee: sending %s "
"to deliver %s",
type_to_string(tmpctx, struct amount_msat,
&p->route[0].amount),
type_to_string(tmpctx, struct amount_msat, &p->amount));
payment_fail(p);
return command_still_pending(cmd);
}
if (amount_msat_greater(fee, p->fee_budget)) {
plugin_log(p->plugin, LOG_INFORM, plugin_log(p->plugin, LOG_INFORM,
"Fee exceeds our fee budget: %s > %s, discarding route", "Fee exceeds our fee budget: %s > %s, discarding route",
type_to_string(tmpctx, struct amount_msat, &fee), type_to_string(tmpctx, struct amount_msat, &fee),
type_to_string(tmpctx, struct amount_msat, &p->fee_budget)); type_to_string(tmpctx, struct amount_msat, &p->constraints.fee_budget));
payment_exclude_most_expensive(p); payment_exclude_most_expensive(p);
payment_fail(p); payment_fail(p);
return command_still_pending(cmd); return command_still_pending(cmd);
} }
if (p->route[0].delay > p->cltv_budget) { if (p->route[0].delay > p->constraints.cltv_budget) {
plugin_log(p->plugin, LOG_INFORM, plugin_log(p->plugin, LOG_INFORM,
"CLTV delay exceeds our CLTV budget: %d > %d", "CLTV delay exceeds our CLTV budget: %d > %d",
p->route[0].delay, p->cltv_budget); p->route[0].delay, p->constraints.cltv_budget);
payment_exclude_longest_delay(p); payment_exclude_longest_delay(p);
payment_fail(p); payment_fail(p);
return command_still_pending(cmd); return command_still_pending(cmd);
} }
/* Now update the constraints in fee_budget and cltv_budget so
* modifiers know what constraints they need to adhere to. */
if (!payment_constraints_update(&p->constraints, fee, p->route[0].delay)) {
plugin_log(p->plugin, LOG_BROKEN,
"Could not update constraints.");
abort();
}
/* Allow modifiers to modify the route, before /* Allow modifiers to modify the route, before
* payment_compute_onion_payloads uses the route to generate the * payment_compute_onion_payloads uses the route to generate the
* onion_payloads */ * onion_payloads */
@ -1557,13 +1592,14 @@ static void exemptfee_cb(struct exemptfee_data *d, struct payment *p)
if (p->step != PAYMENT_STEP_INITIALIZED) if (p->step != PAYMENT_STEP_INITIALIZED)
return payment_continue(p); return payment_continue(p);
if (amount_msat_greater_eq(d->amount, p->amount)) { if (amount_msat_greater_eq(d->amount, p->constraints.fee_budget)) {
p->fee_budget = d->amount; p->constraints.fee_budget = d->amount;
p->start_constraints->fee_budget = d->amount;
plugin_log( plugin_log(
p->plugin, LOG_INFORM, p->plugin, LOG_INFORM,
"Payment amount is below exemption threshold, " "Payment amount is below exemption threshold, "
"allowing a maximum fee of %s", "allowing a maximum fee of %s",
type_to_string(tmpctx, struct amount_msat, &p->fee_budget)); type_to_string(tmpctx, struct amount_msat, &p->constraints.fee_budget));
} }
return payment_continue(p); return payment_continue(p);
} }

24
plugins/libplugin-pay.h

@ -151,6 +151,17 @@ struct getroute_request {
u32 max_hops; u32 max_hops;
}; };
struct payment_constraints {
/* Maximum remaining fees we're willing to pay to complete this
* (sub-)payment. This is modified by a route being applied of by
* modifiers that use some of the budget. */
struct amount_msat fee_budget;
/* Maximum end-to-end CLTV delta we're willing to wait for this
* (sub-)payment to complete. */
u32 cltv_budget;
};
struct payment { struct payment {
/* The command that triggered this payment. Only set for the root /* The command that triggered this payment. Only set for the root
* payment. */ * payment. */
@ -201,13 +212,14 @@ struct payment {
struct timeabs start_time, end_time; struct timeabs start_time, end_time;
struct timeabs deadline; struct timeabs deadline;
/* Maximum remaining fees we're willing to pay to complete this /* Constraints the state machine and modifiers needs to maintain. */
* (sub-)payment. */ struct payment_constraints constraints;
struct amount_msat fee_budget;
/* Maximum end-to-end CLTV delta we're willing to wait for this /* Copy of the above constraints inherited to sub-payments
* (sub-)payment to complete. */ * automatically. This is mainly so we don't have to unapply changes
u32 cltv_budget; * to the constraints when retrying or splitting. The copy is made in
* `payment_start` so they can be adjusted until then. */
struct payment_constraints *start_constraints;
struct short_channel_id *exclusions; struct short_channel_id *exclusions;

6
plugins/pay.c

@ -1908,14 +1908,16 @@ static struct command_result *json_paymod(struct command *cmd,
p->invoice = tal_steal(p, b11); p->invoice = tal_steal(p, b11);
p->bolt11 = tal_steal(p, b11str); p->bolt11 = tal_steal(p, b11str);
p->why = "Initial attempt"; p->why = "Initial attempt";
p->cltv_budget = *maxdelay; p->constraints.cltv_budget = *maxdelay;
if (!amount_msat_fee(&p->fee_budget, p->amount, 0, *maxfee_pct_millionths)) { if (!amount_msat_fee(&p->constraints.fee_budget, p->amount, 0,
*maxfee_pct_millionths / 100)) {
tal_free(p); tal_free(p);
return command_fail( return command_fail(
cmd, JSONRPC2_INVALID_PARAMS, cmd, JSONRPC2_INVALID_PARAMS,
"Overflow when computing fee budget, fee rate too high."); "Overflow when computing fee budget, fee rate too high.");
} }
p->constraints.cltv_budget = *maxdelay;
payment_mod_exemptfee_get_data(p)->amount = *exemptfee; payment_mod_exemptfee_get_data(p)->amount = *exemptfee;

Loading…
Cancel
Save