mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-21 08:04:26 +01:00
paymod: Parse error from waitsendpay and exclude failed chans
This commit is contained in:
@@ -55,6 +55,8 @@ struct payment *payment_new(tal_t *ctx, struct command *cmd,
|
|||||||
p->next_partid = 1;
|
p->next_partid = 1;
|
||||||
p->plugin = cmd->plugin;
|
p->plugin = cmd->plugin;
|
||||||
p->channel_hints = tal_arr(p, struct channel_hint, 0);
|
p->channel_hints = tal_arr(p, struct channel_hint, 0);
|
||||||
|
p->excluded_nodes = tal_arr(p, struct node_id, 0);
|
||||||
|
p->abort = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize all modifier data so we can point to the fields when
|
/* Initialize all modifier data so we can point to the fields when
|
||||||
@@ -231,6 +233,7 @@ static struct command_result *payment_getroute_error(struct command *cmd,
|
|||||||
struct payment *p)
|
struct payment *p)
|
||||||
{
|
{
|
||||||
p->step = PAYMENT_STEP_FAILED;
|
p->step = PAYMENT_STEP_FAILED;
|
||||||
|
p->route = NULL;
|
||||||
payment_continue(p);
|
payment_continue(p);
|
||||||
|
|
||||||
/* Let payment_finished_ handle this, so we mark it as pending */
|
/* Let payment_finished_ handle this, so we mark it as pending */
|
||||||
@@ -330,6 +333,8 @@ static struct payment_result *tal_sendpay_result_from_json(const tal_t *ctx,
|
|||||||
const jsmntok_t *preimagetok = json_get_member(buffer, toks, "payment_preimage");
|
const jsmntok_t *preimagetok = json_get_member(buffer, toks, "payment_preimage");
|
||||||
const jsmntok_t *codetok = json_get_member(buffer, toks, "code");
|
const jsmntok_t *codetok = json_get_member(buffer, toks, "code");
|
||||||
const jsmntok_t *datatok = json_get_member(buffer, toks, "data");
|
const jsmntok_t *datatok = json_get_member(buffer, toks, "data");
|
||||||
|
const jsmntok_t *erridxtok, *msgtok, *failcodetok, *rawmsgtok,
|
||||||
|
*failcodenametok, *errchantok, *errnodetok, *errdirtok;
|
||||||
struct payment_result *result;
|
struct payment_result *result;
|
||||||
|
|
||||||
/* Check if we have an error and need to descend into data to get
|
/* Check if we have an error and need to descend into data to get
|
||||||
@@ -352,6 +357,11 @@ static struct payment_result *tal_sendpay_result_from_json(const tal_t *ctx,
|
|||||||
|
|
||||||
result = tal(ctx, struct payment_result);
|
result = tal(ctx, struct payment_result);
|
||||||
|
|
||||||
|
if (codetok != NULL)
|
||||||
|
json_to_u32(buffer, codetok, &result->code);
|
||||||
|
else
|
||||||
|
result->code = 0;
|
||||||
|
|
||||||
/* If the partid is 0 it'd be omitted in waitsendpay, fix this here. */
|
/* If the partid is 0 it'd be omitted in waitsendpay, fix this here. */
|
||||||
if (partidtok != NULL)
|
if (partidtok != NULL)
|
||||||
json_to_u32(buffer, partidtok, &result->partid);
|
json_to_u32(buffer, partidtok, &result->partid);
|
||||||
@@ -375,6 +385,67 @@ static struct payment_result *tal_sendpay_result_from_json(const tal_t *ctx,
|
|||||||
json_to_preimage(buffer, preimagetok, result->payment_preimage);
|
json_to_preimage(buffer, preimagetok, result->payment_preimage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Now extract the error details if the error code is not 0 */
|
||||||
|
if (result->code != 0) {
|
||||||
|
erridxtok = json_get_member(buffer, datatok, "erring_index");
|
||||||
|
errnodetok = json_get_member(buffer, datatok, "erring_node");
|
||||||
|
errchantok = json_get_member(buffer, datatok, "erring_channel");
|
||||||
|
errdirtok = json_get_member(buffer, datatok, "erring_direction");
|
||||||
|
failcodetok = json_get_member(buffer, datatok, "failcode");
|
||||||
|
failcodenametok =json_get_member(buffer, datatok, "failcodename");
|
||||||
|
msgtok = json_get_member(buffer, toks, "message");
|
||||||
|
rawmsgtok = json_get_member(buffer, datatok, "raw_message");
|
||||||
|
if (failcodetok == NULL || failcodetok->type != JSMN_PRIMITIVE ||
|
||||||
|
failcodenametok == NULL || failcodenametok->type != JSMN_STRING ||
|
||||||
|
(erridxtok != NULL && erridxtok->type != JSMN_PRIMITIVE) ||
|
||||||
|
(errnodetok != NULL && errnodetok->type != JSMN_STRING) ||
|
||||||
|
(errchantok != NULL && errchantok->type != JSMN_STRING) ||
|
||||||
|
(errdirtok != NULL && errdirtok->type != JSMN_PRIMITIVE) ||
|
||||||
|
msgtok == NULL || msgtok->type != JSMN_STRING ||
|
||||||
|
(rawmsgtok != NULL && rawmsgtok->type != JSMN_STRING))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (rawmsgtok != NULL)
|
||||||
|
result->raw_message = json_tok_bin_from_hex(result, buffer, rawmsgtok);
|
||||||
|
else
|
||||||
|
result->raw_message = NULL;
|
||||||
|
|
||||||
|
result->failcodename = json_strdup(result, buffer, failcodenametok);
|
||||||
|
json_to_u32(buffer, failcodetok, &result->failcode);
|
||||||
|
result->message = json_strdup(result, buffer, msgtok);
|
||||||
|
|
||||||
|
if (erridxtok != NULL) {
|
||||||
|
result->erring_index = tal(result, u32);
|
||||||
|
json_to_u32(buffer, erridxtok, result->erring_index);
|
||||||
|
} else {
|
||||||
|
result->erring_index = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errdirtok != NULL) {
|
||||||
|
result->erring_direction = tal(result, int);
|
||||||
|
json_to_int(buffer, errdirtok, result->erring_direction);
|
||||||
|
} else {
|
||||||
|
result->erring_direction = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errnodetok != NULL) {
|
||||||
|
result->erring_node = tal(result, struct node_id);
|
||||||
|
json_to_node_id(buffer, errnodetok,
|
||||||
|
result->erring_node);
|
||||||
|
} else {
|
||||||
|
result->erring_node = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errchantok != NULL) {
|
||||||
|
result->erring_channel =
|
||||||
|
tal(result, struct short_channel_id);
|
||||||
|
json_to_short_channel_id(buffer, errchantok,
|
||||||
|
result->erring_channel);
|
||||||
|
} else {
|
||||||
|
result->erring_channel = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
fail:
|
fail:
|
||||||
return tal_free(result);
|
return tal_free(result);
|
||||||
@@ -384,6 +455,11 @@ static struct command_result *
|
|||||||
payment_waitsendpay_finished(struct command *cmd, const char *buffer,
|
payment_waitsendpay_finished(struct command *cmd, const char *buffer,
|
||||||
const jsmntok_t *toks, struct payment *p)
|
const jsmntok_t *toks, struct payment *p)
|
||||||
{
|
{
|
||||||
|
struct payment *root;
|
||||||
|
struct channel_hint hint;
|
||||||
|
struct route_hop *hop;
|
||||||
|
assert(p->route != NULL);
|
||||||
|
|
||||||
p->result = tal_sendpay_result_from_json(p, buffer, toks);
|
p->result = tal_sendpay_result_from_json(p, buffer, toks);
|
||||||
|
|
||||||
if (p->result == NULL)
|
if (p->result == NULL)
|
||||||
@@ -391,14 +467,88 @@ payment_waitsendpay_finished(struct command *cmd, const char *buffer,
|
|||||||
p->plugin, "Unable to parse `waitsendpay` result: %.*s",
|
p->plugin, "Unable to parse `waitsendpay` result: %.*s",
|
||||||
json_tok_full_len(toks), json_tok_full(buffer, toks));
|
json_tok_full_len(toks), json_tok_full(buffer, toks));
|
||||||
|
|
||||||
if (p->result->state == PAYMENT_COMPLETE)
|
if (p->result->state == PAYMENT_COMPLETE) {
|
||||||
p->step = PAYMENT_STEP_SUCCESS;
|
p->step = PAYMENT_STEP_SUCCESS;
|
||||||
else
|
goto cont;
|
||||||
|
}
|
||||||
|
|
||||||
p->step = PAYMENT_STEP_FAILED;
|
p->step = PAYMENT_STEP_FAILED;
|
||||||
|
root = payment_root(p);
|
||||||
|
|
||||||
/* TODO examine the failure and eventually stash exclusions that we
|
switch (p->result->failcode) {
|
||||||
* learned in the payment, so sub-payments can avoid them. */
|
case WIRE_PERMANENT_CHANNEL_FAILURE:
|
||||||
|
case WIRE_CHANNEL_DISABLED:
|
||||||
|
case WIRE_UNKNOWN_NEXT_PEER:
|
||||||
|
case WIRE_REQUIRED_CHANNEL_FEATURE_MISSING:
|
||||||
|
/* All of these result in the channel being marked as disabled. */
|
||||||
|
assert(*p->result->erring_index < tal_count(p->route));
|
||||||
|
hop = &p->route[*p->result->erring_index];
|
||||||
|
hint.enabled = false;
|
||||||
|
hint.scid.scid = hop->channel_id;
|
||||||
|
hint.scid.dir = hop->direction;
|
||||||
|
hint.estimated_capacity = AMOUNT_MSAT(0);
|
||||||
|
tal_arr_expand(&root->channel_hints, hint);
|
||||||
|
break;
|
||||||
|
case WIRE_TEMPORARY_CHANNEL_FAILURE:
|
||||||
|
/* These are an indication that the capacity was insufficient,
|
||||||
|
* remember the amount we tried as an estimate. */
|
||||||
|
assert(*p->result->erring_index < tal_count(p->route));
|
||||||
|
hop = &p->route[*p->result->erring_index];
|
||||||
|
hint.enabled = true;
|
||||||
|
hint.scid.scid = hop->channel_id;
|
||||||
|
hint.scid.dir = hop->direction;
|
||||||
|
hint.estimated_capacity.millisatoshis = hop->amount.millisatoshis * 0.75; /* Raw: Multiplication */
|
||||||
|
tal_arr_expand(&root->channel_hints, hint);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WIRE_INVALID_ONION_PAYLOAD:
|
||||||
|
case WIRE_INVALID_REALM:
|
||||||
|
case WIRE_PERMANENT_NODE_FAILURE:
|
||||||
|
case WIRE_TEMPORARY_NODE_FAILURE:
|
||||||
|
case WIRE_REQUIRED_NODE_FEATURE_MISSING:
|
||||||
|
case WIRE_INVALID_ONION_VERSION:
|
||||||
|
case WIRE_INVALID_ONION_HMAC:
|
||||||
|
case WIRE_INVALID_ONION_KEY:
|
||||||
|
#if EXPERIMENTAL_FEATURES
|
||||||
|
case WIRE_INVALID_ONION_BLINDING:
|
||||||
|
#endif
|
||||||
|
/* These are reported by the last hop, i.e., the destination of hop i-1. */
|
||||||
|
assert(*p->result->erring_index - 1 < tal_count(p->route));
|
||||||
|
hop = &p->route[*p->result->erring_index - 1];
|
||||||
|
tal_arr_expand(&root->excluded_nodes, hop->nodeid);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS:
|
||||||
|
case WIRE_MPP_TIMEOUT:
|
||||||
|
/* These are permanent failures that should abort all of our
|
||||||
|
* attempts right away. We'll still track pending partial
|
||||||
|
* payments correctly, just not start new ones. */
|
||||||
|
root->abort = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WIRE_AMOUNT_BELOW_MINIMUM:
|
||||||
|
case WIRE_EXPIRY_TOO_FAR:
|
||||||
|
case WIRE_EXPIRY_TOO_SOON:
|
||||||
|
case WIRE_FEE_INSUFFICIENT:
|
||||||
|
case WIRE_INCORRECT_CLTV_EXPIRY:
|
||||||
|
case WIRE_FINAL_INCORRECT_CLTV_EXPIRY:
|
||||||
|
/* These are issues that are due to gossipd being out of date,
|
||||||
|
* we ignore them here, and wait for gossipd to adjust
|
||||||
|
* instead. */
|
||||||
|
break;
|
||||||
|
case WIRE_FINAL_INCORRECT_HTLC_AMOUNT:
|
||||||
|
/* These are symptoms of intermediate hops tampering with the
|
||||||
|
* payment. */
|
||||||
|
hop = &p->route[*p->result->erring_index];
|
||||||
|
plugin_log(
|
||||||
|
p->plugin, LOG_UNUSUAL,
|
||||||
|
"Node %s reported an incorrect HTLC amount, this could be "
|
||||||
|
"a prior hop messing with the amounts.",
|
||||||
|
type_to_string(tmpctx, struct node_id, &hop->nodeid));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cont:
|
||||||
payment_continue(p);
|
payment_continue(p);
|
||||||
return command_still_pending(cmd);
|
return command_still_pending(cmd);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,15 @@ struct payment_result {
|
|||||||
enum payment_result_state state;
|
enum payment_result_state state;
|
||||||
struct amount_msat amount_sent;
|
struct amount_msat amount_sent;
|
||||||
struct preimage *payment_preimage;
|
struct preimage *payment_preimage;
|
||||||
|
u32 code;
|
||||||
|
const char* failcodename;
|
||||||
|
enum onion_type failcode;
|
||||||
|
const u8 *raw_message;
|
||||||
|
const char *message;
|
||||||
|
u32 *erring_index;
|
||||||
|
struct node_id *erring_node;
|
||||||
|
struct short_channel_id *erring_channel;
|
||||||
|
int *erring_direction;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Information about channels we inferred from a) looking at our channels, and
|
/* Information about channels we inferred from a) looking at our channels, and
|
||||||
@@ -183,8 +192,16 @@ struct payment {
|
|||||||
/* tal_arr of channel_hints we incrementally learn while performing
|
/* tal_arr of channel_hints we incrementally learn while performing
|
||||||
* payment attempts. */
|
* payment attempts. */
|
||||||
struct channel_hint *channel_hints;
|
struct channel_hint *channel_hints;
|
||||||
|
struct node_id *excluded_nodes;
|
||||||
|
|
||||||
struct payment_result *result;
|
struct payment_result *result;
|
||||||
|
|
||||||
|
/* Did something happen that will cause all future attempts to fail?
|
||||||
|
* This usually means that the final node reported that it can't be
|
||||||
|
* reached, or in MPP payments there are no more paths we can
|
||||||
|
* attempt. Modifiers need to leave failures alone once this is set to
|
||||||
|
* true. Set only on the root payment. */
|
||||||
|
bool abort;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct payment_modifier {
|
struct payment_modifier {
|
||||||
|
|||||||
Reference in New Issue
Block a user