mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-19 15:14:23 +01:00
paymod: Add user-provided label back into the paystatus result
This commit is contained in:
@@ -2,10 +2,9 @@
|
||||
#include <ccan/array_size/array_size.h>
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <common/json_stream.h>
|
||||
#include <common/pseudorand.h>
|
||||
#include <common/type_to_string.h>
|
||||
#include <plugins/libplugin-pay.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#define DEFAULT_FINAL_CLTV_DELTA 9
|
||||
|
||||
@@ -22,6 +21,7 @@ struct payment *payment_new(tal_t *ctx, struct command *cmd,
|
||||
p->result = NULL;
|
||||
p->why = NULL;
|
||||
p->getroute = tal(p, struct getroute_request);
|
||||
p->label = NULL;
|
||||
|
||||
/* Copy over the relevant pieces of information. */
|
||||
if (parent != NULL) {
|
||||
@@ -1178,8 +1178,6 @@ void payment_continue(struct payment *p)
|
||||
|
||||
void payment_fail(struct payment *p)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
p->end_time = time_now();
|
||||
p->step = PAYMENT_STEP_FAILED;
|
||||
payment_continue(p);
|
||||
@@ -1607,3 +1605,177 @@ static void exemptfee_cb(struct exemptfee_data *d, struct payment *p)
|
||||
REGISTER_PAYMENT_MODIFIER(exemptfee, struct exemptfee_data *,
|
||||
exemptfee_data_init, exemptfee_cb);
|
||||
|
||||
/* BOLT #7:
|
||||
*
|
||||
* If a route is computed by simply routing to the intended recipient and
|
||||
* summing the `cltv_expiry_delta`s, then it's possible for intermediate nodes
|
||||
* to guess their position in the route. Knowing the CLTV of the HTLC, the
|
||||
* surrounding network topology, and the `cltv_expiry_delta`s gives an
|
||||
* attacker a way to guess the intended recipient. Therefore, it's highly
|
||||
* desirable to add a random offset to the CLTV that the intended recipient
|
||||
* will receive, which bumps all CLTVs along the route.
|
||||
*
|
||||
* In order to create a plausible offset, the origin node MAY start a limited
|
||||
* random walk on the graph, starting from the intended recipient and summing
|
||||
* the `cltv_expiry_delta`s, and use the resulting sum as the offset. This
|
||||
* effectively creates a _shadow route extension_ to the actual route and
|
||||
* provides better protection against this attack vector than simply picking a
|
||||
* random offset would.
|
||||
*/
|
||||
|
||||
static struct shadow_route_data *shadow_route_init(struct payment *p)
|
||||
{
|
||||
if (p->parent != NULL)
|
||||
return payment_mod_shadowroute_get_data(p->parent);
|
||||
else
|
||||
return tal(p, struct shadow_route_data);
|
||||
}
|
||||
|
||||
/* Mutual recursion */
|
||||
static struct command_result *shadow_route_listchannels(struct command *cmd,
|
||||
const char *buf,
|
||||
const jsmntok_t *result,
|
||||
struct payment *p);
|
||||
|
||||
static struct command_result *shadow_route_extend(struct shadow_route_data *d,
|
||||
struct payment *p)
|
||||
{
|
||||
struct out_req *req;
|
||||
req = jsonrpc_request_start(p->plugin, NULL, "listchannels",
|
||||
shadow_route_listchannels,
|
||||
payment_rpc_failure, p);
|
||||
json_add_string(req->js, "source",
|
||||
type_to_string(req, struct node_id, &d->destination));
|
||||
return send_outreq(p->plugin, req);
|
||||
}
|
||||
|
||||
static struct command_result *shadow_route_listchannels(struct command *cmd,
|
||||
const char *buf,
|
||||
const jsmntok_t *result,
|
||||
struct payment *p)
|
||||
{
|
||||
/* Use reservoir sampling across the capable channels. */
|
||||
struct shadow_route_data *d = payment_mod_shadowroute_get_data(p);
|
||||
struct payment_constraints *cons = &d->constraints;
|
||||
struct route_info *best = NULL;
|
||||
size_t i;
|
||||
u64 sample = 0;
|
||||
struct amount_msat best_fee;
|
||||
const jsmntok_t *sattok, *delaytok, *basefeetok, *propfeetok, *desttok,
|
||||
*channelstok, *chan;
|
||||
|
||||
channelstok = json_get_member(buf, result, "channels");
|
||||
json_for_each_arr(i, chan, channelstok) {
|
||||
u64 v = pseudorand(UINT64_MAX);
|
||||
struct route_info curr;
|
||||
struct amount_sat capacity;
|
||||
struct amount_msat fee;
|
||||
|
||||
sattok = json_get_member(buf, chan, "satoshis");
|
||||
delaytok = json_get_member(buf, chan, "delay");
|
||||
basefeetok = json_get_member(buf, chan, "base_fee_millisatoshi");
|
||||
propfeetok = json_get_member(buf, chan, "fee_per_millionth");
|
||||
desttok = json_get_member(buf, chan, "destination");
|
||||
|
||||
if (sattok == NULL || delaytok == NULL ||
|
||||
delaytok->type != JSMN_PRIMITIVE || basefeetok == NULL ||
|
||||
basefeetok->type != JSMN_PRIMITIVE || propfeetok == NULL ||
|
||||
propfeetok->type != JSMN_PRIMITIVE || desttok == NULL)
|
||||
continue;
|
||||
|
||||
json_to_u16(buf, delaytok, &curr.cltv_expiry_delta);
|
||||
json_to_number(buf, basefeetok, &curr.fee_base_msat);
|
||||
json_to_number(buf, propfeetok,
|
||||
&curr.fee_proportional_millionths);
|
||||
json_to_sat(buf, sattok, &capacity);
|
||||
json_to_node_id(buf, desttok, &curr.pubkey);
|
||||
|
||||
if (!best || v > sample) {
|
||||
/* If the capacity is insufficient to pass the amount
|
||||
* it's not a plausible extension. */
|
||||
if (amount_msat_greater_sat(p->amount, capacity))
|
||||
continue;
|
||||
|
||||
if (curr.cltv_expiry_delta > cons->cltv_budget)
|
||||
continue;
|
||||
|
||||
if (!amount_msat_fee(
|
||||
&fee, p->amount, curr.fee_base_msat,
|
||||
curr.fee_proportional_millionths)) {
|
||||
/* Fee computation failed... */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (amount_msat_greater_eq(fee, cons->fee_budget))
|
||||
continue;
|
||||
|
||||
best = tal_dup(tmpctx, struct route_info, &curr);
|
||||
best_fee = fee;
|
||||
sample = v;
|
||||
}
|
||||
}
|
||||
|
||||
if (best != NULL) {
|
||||
bool ok;
|
||||
/* Ok, we found an extension, let's add it. */
|
||||
d->destination = best->pubkey;
|
||||
|
||||
/* Apply deltas to the constraints in the shadow route so we
|
||||
* don't overshoot our 1/4th target. */
|
||||
if (!payment_constraints_update(&d->constraints, best_fee,
|
||||
best->cltv_expiry_delta)) {
|
||||
best = NULL;
|
||||
goto next;
|
||||
}
|
||||
|
||||
/* Now do the same to the payment constraints so other
|
||||
* modifiers don't do it either. */
|
||||
ok = payment_constraints_update(&p->constraints, best_fee,
|
||||
best->cltv_expiry_delta);
|
||||
|
||||
/* And now the thing that caused all of this: adjust the call
|
||||
* to getroute. */
|
||||
ok &= amount_msat_add(&p->getroute->amount, p->getroute->amount,
|
||||
best_fee);
|
||||
p->getroute->cltv += best->cltv_expiry_delta;
|
||||
assert(ok);
|
||||
}
|
||||
|
||||
next:
|
||||
|
||||
/* Now it's time to decide whether we want to extend or continue. */
|
||||
if (best == NULL || pseudorand(2) == 0) {
|
||||
payment_continue(p);
|
||||
return command_still_pending(cmd);
|
||||
} else {
|
||||
return shadow_route_extend(d, p);
|
||||
}
|
||||
}
|
||||
|
||||
static void shadow_route_cb(struct shadow_route_data *d,
|
||||
struct payment *p)
|
||||
{
|
||||
#if DEVELOPER
|
||||
if (!d->use_shadow)
|
||||
return payment_continue(p);
|
||||
#endif
|
||||
|
||||
if (p->step != PAYMENT_STEP_INITIALIZED)
|
||||
return payment_continue(p);
|
||||
|
||||
d->destination = *p->destination;
|
||||
|
||||
/* Allow shadowroutes to consume up to 1/4th of our budget. */
|
||||
d->constraints.cltv_budget = p->constraints.cltv_budget / 4;
|
||||
d->constraints.fee_budget = p->constraints.fee_budget;
|
||||
d->constraints.fee_budget.millisatoshis /= 4; /* Raw: msat division. */
|
||||
|
||||
if (pseudorand(2) == 0) {
|
||||
return payment_continue(p);
|
||||
} else {
|
||||
shadow_route_extend(d, p);
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER_PAYMENT_MODIFIER(shadowroute, struct shadow_route_data *,
|
||||
shadow_route_init, shadow_route_cb);
|
||||
|
||||
Reference in New Issue
Block a user