diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index e2d240ba2..88ce1b199 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -3885,3 +3885,45 @@ static void payee_incoming_limit_step_cb(void *d UNUSED, struct payment *p) REGISTER_PAYMENT_MODIFIER(payee_incoming_limit, void *, NULL, payee_incoming_limit_step_cb); + +static struct route_exclusions_data * +route_exclusions_data_init(struct payment *p) +{ + struct route_exclusions_data *d; + if (p->parent != NULL) { + return payment_mod_route_exclusions_get_data(p->parent); + } else { + d = tal(p, struct route_exclusions_data); + d->exclusions = NULL; + } + return d; +} + +static void route_exclusions_step_cb(struct route_exclusions_data *d, + struct payment *p) +{ + if (p->parent) + return payment_continue(p); + struct route_exclusion **exclusions = d->exclusions; + for (size_t i = 0; i < tal_count(exclusions); i++) { + struct route_exclusion *e = exclusions[i]; + if (e->type == EXCLUDE_CHANNEL) { + channel_hints_update(p, e->u.chan_id.scid, e->u.chan_id.dir, + false, false, NULL, NULL); + } else { + if (node_id_eq(&e->u.node_id, p->destination)) { + payment_abort(p, "Payee is manually excluded"); + return; + } else if (node_id_eq(&e->u.node_id, p->local_id)) { + payment_abort(p, "Payer is manually excluded"); + return; + } + + tal_arr_expand(&p->excluded_nodes, e->u.node_id); + } + } + payment_continue(p); +} + +REGISTER_PAYMENT_MODIFIER(route_exclusions, struct route_exclusions_data *, + route_exclusions_data_init, route_exclusions_step_cb); diff --git a/plugins/libplugin-pay.h b/plugins/libplugin-pay.h index 92566b745..b2b4ce7fe 100644 --- a/plugins/libplugin-pay.h +++ b/plugins/libplugin-pay.h @@ -406,6 +406,10 @@ struct adaptive_split_mod_data { u32 htlc_budget; }; +struct route_exclusions_data { + struct route_exclusion **exclusions; +}; + /* List of globally available payment modifiers. */ REGISTER_PAYMENT_MODIFIER_HEADER(retry, struct retry_mod_data); REGISTER_PAYMENT_MODIFIER_HEADER(routehints, struct routehints_data); @@ -426,6 +430,8 @@ REGISTER_PAYMENT_MODIFIER_HEADER(local_channel_hints, void); * we detect the payee to have, in order to not exhaust the number of HTLCs * each of those channels can bear. */ REGISTER_PAYMENT_MODIFIER_HEADER(payee_incoming_limit, void); +REGISTER_PAYMENT_MODIFIER_HEADER(route_exclusions, struct route_exclusions_data); + struct payment *payment_new(tal_t *ctx, struct command *cmd, struct payment *parent, diff --git a/plugins/pay.c b/plugins/pay.c index bcb9e89fe..cbd2c7c15 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -2255,7 +2255,14 @@ payment_listsendpays_previous(struct command *cmd, const char *buf, } struct payment_modifier *paymod_mods[] = { + /* NOTE: The order in which these four paymods are executed is + * significant! + * local_channel_hints *must* execute first before route_exclusions + * which *must* execute before directpay. + * exemptfee *must* also execute before directpay. + */ &local_channel_hints_pay_mod, + &route_exclusions_pay_mod, &exemptfee_pay_mod, &directpay_pay_mod, &shadowroute_pay_mod, @@ -2305,6 +2312,7 @@ static struct command_result *json_paymod(struct command *cmd, struct sha256 *local_offer_id; const struct tlv_invoice *b12; struct out_req *req; + struct route_exclusion **exclusions; #if DEVELOPER bool *use_shadow; #endif @@ -2326,6 +2334,7 @@ static struct command_result *json_paymod(struct command *cmd, maxdelay_default), p_opt_def("exemptfee", param_msat, &exemptfee, AMOUNT_MSAT(5000)), p_opt("localofferid", param_sha256, &local_offer_id), + p_opt("exclude", param_route_exclusion_array, &exclusions), #if DEVELOPER p_opt_def("use_shadow", param_bool, &use_shadow, true), #endif @@ -2479,6 +2488,7 @@ static struct command_result *json_paymod(struct command *cmd, shadow_route = payment_mod_shadowroute_get_data(p); payment_mod_presplit_get_data(p)->disable = disablempp; payment_mod_adaptive_splitter_get_data(p)->disable = disablempp; + payment_mod_route_exclusions_get_data(p)->exclusions = exclusions; /* This is an MPP enabled pay command, disable amount fuzzing. */ shadow_route->fuzz_amount = false;