Browse Source

paymod: Parse error from waitsendpay and exclude failed chans

paymod-02
Christian Decker 5 years ago
parent
commit
1280bdf432
  1. 160
      plugins/libplugin-pay.c
  2. 17
      plugins/libplugin-pay.h

160
plugins/libplugin-pay.c

@ -55,6 +55,8 @@ struct payment *payment_new(tal_t *ctx, struct command *cmd,
p->next_partid = 1;
p->plugin = cmd->plugin;
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
@ -231,6 +233,7 @@ static struct command_result *payment_getroute_error(struct command *cmd,
struct payment *p)
{
p->step = PAYMENT_STEP_FAILED;
p->route = NULL;
payment_continue(p);
/* 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 *codetok = json_get_member(buffer, toks, "code");
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;
/* 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);
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 (partidtok != NULL)
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);
}
/* 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;
fail:
return tal_free(result);
@ -384,6 +455,11 @@ static struct command_result *
payment_waitsendpay_finished(struct command *cmd, const char *buffer,
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);
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",
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;
else
p->step = PAYMENT_STEP_FAILED;
goto cont;
}
/* TODO examine the failure and eventually stash exclusions that we
* learned in the payment, so sub-payments can avoid them. */
p->step = PAYMENT_STEP_FAILED;
root = payment_root(p);
switch (p->result->failcode) {
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);
return command_still_pending(cmd);
}

17
plugins/libplugin-pay.h

@ -64,6 +64,15 @@ struct payment_result {
enum payment_result_state state;
struct amount_msat amount_sent;
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
@ -183,8 +192,16 @@ struct payment {
/* tal_arr of channel_hints we incrementally learn while performing
* payment attempts. */
struct channel_hint *channel_hints;
struct node_id *excluded_nodes;
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 {

Loading…
Cancel
Save