mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 13:25:43 +01:00
common: provide readable explanation when onion payload is invalid.
I had to use fprintf, which is terrible. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
9ed7260328
commit
e38f5d8c27
@ -3,10 +3,12 @@
|
||||
#include <ccan/array_size/array_size.h>
|
||||
#include <ccan/cast/cast.h>
|
||||
#include <ccan/mem/mem.h>
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <common/blindedpath.h>
|
||||
#include <common/ecdh.h>
|
||||
#include <common/onion_decode.h>
|
||||
#include <common/sphinx.h>
|
||||
#include <inttypes.h>
|
||||
#include <sodium/crypto_aead_chacha20poly1305.h>
|
||||
|
||||
/* BOLT #4:
|
||||
@ -62,17 +64,22 @@ static u64 ceil_div(u64 a, u64 b)
|
||||
return (a + b - 1) / b;
|
||||
}
|
||||
|
||||
static bool handle_blinded_forward(struct onion_payload *p,
|
||||
static bool handle_blinded_forward(const tal_t *ctx,
|
||||
struct onion_payload *p,
|
||||
struct amount_msat amount_in,
|
||||
u32 cltv_expiry,
|
||||
const struct tlv_payload *tlv,
|
||||
const struct tlv_encrypted_data_tlv *enc,
|
||||
u64 *failtlvtype)
|
||||
u64 *failtlvtype,
|
||||
const char **explanation)
|
||||
{
|
||||
u64 amt = amount_in.millisatoshis; /* Raw: allowed to wrap */
|
||||
|
||||
if (!check_nonfinal_tlv(tlv, failtlvtype))
|
||||
if (!check_nonfinal_tlv(tlv, failtlvtype)) {
|
||||
if (explanation)
|
||||
*explanation = tal_fmt(ctx, "unexpected tlv type %"PRIu64, *failtlvtype);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* BOLT #4:
|
||||
* - If it is not the final node:
|
||||
@ -81,6 +88,8 @@ static bool handle_blinded_forward(struct onion_payload *p,
|
||||
* contain either `short_channel_id` or `next_node_id`.
|
||||
*/
|
||||
if (!enc->short_channel_id && !enc->next_node_id) {
|
||||
if (explanation)
|
||||
*explanation = tal_fmt(ctx, "neither short_channel_id nor next_node_id present");
|
||||
*failtlvtype = TLV_PAYLOAD_ENCRYPTED_RECIPIENT_DATA;
|
||||
return false;
|
||||
}
|
||||
@ -104,6 +113,8 @@ static bool handle_blinded_forward(struct onion_payload *p,
|
||||
* contain `payment_relay`.
|
||||
*/
|
||||
if (!enc->payment_relay) {
|
||||
if (explanation)
|
||||
*explanation = tal_fmt(ctx, "missing payment_relay");
|
||||
*failtlvtype = TLV_PAYLOAD_ENCRYPTED_RECIPIENT_DATA;
|
||||
return false;
|
||||
}
|
||||
@ -117,13 +128,18 @@ static bool handle_blinded_forward(struct onion_payload *p,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool handle_blinded_terminal(struct onion_payload *p,
|
||||
static bool handle_blinded_terminal(const tal_t *ctx,
|
||||
struct onion_payload *p,
|
||||
const struct tlv_payload *tlv,
|
||||
const struct tlv_encrypted_data_tlv *enc,
|
||||
u64 *failtlvtype)
|
||||
u64 *failtlvtype,
|
||||
const char **explanation)
|
||||
{
|
||||
if (!check_final_tlv(tlv, failtlvtype))
|
||||
if (!check_final_tlv(tlv, failtlvtype)) {
|
||||
if (explanation)
|
||||
*explanation = tal_fmt(ctx, "unexpected tlv type %"PRIu64, *failtlvtype);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* BOLT #4:
|
||||
* - MUST return an error if `amt_to_forward`, `outgoing_cltv_value`
|
||||
@ -132,16 +148,22 @@ static bool handle_blinded_terminal(struct onion_payload *p,
|
||||
* for the payment.
|
||||
*/
|
||||
if (!tlv->amt_to_forward) {
|
||||
if (explanation)
|
||||
*explanation = tal_fmt(ctx, "missing amt_to_forward");
|
||||
*failtlvtype = TLV_PAYLOAD_AMT_TO_FORWARD;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tlv->outgoing_cltv_value) {
|
||||
if (explanation)
|
||||
*explanation = tal_fmt(ctx, "missing outgoing_cltv_value");
|
||||
*failtlvtype = TLV_PAYLOAD_OUTGOING_CLTV_VALUE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tlv->total_amount_msat) {
|
||||
if (explanation)
|
||||
*explanation = tal_fmt(ctx, "missing total_amount_msat");
|
||||
*failtlvtype = TLV_PAYLOAD_TOTAL_AMOUNT_MSAT;
|
||||
return false;
|
||||
}
|
||||
@ -173,7 +195,8 @@ struct onion_payload *onion_decode(const tal_t *ctx,
|
||||
struct amount_msat amount_in,
|
||||
u32 cltv_expiry,
|
||||
u64 *failtlvtype,
|
||||
size_t *failtlvpos)
|
||||
size_t *failtlvpos,
|
||||
const char **explanation)
|
||||
{
|
||||
struct onion_payload *p = tal(ctx, struct onion_payload);
|
||||
const u8 *cursor = rs->raw_payload;
|
||||
@ -191,6 +214,8 @@ struct onion_payload *onion_decode(const tal_t *ctx,
|
||||
if (!cursor || len > max) {
|
||||
*failtlvtype = 0;
|
||||
*failtlvpos = tal_bytelen(rs->raw_payload);
|
||||
if (explanation)
|
||||
*explanation = tal_fmt(ctx, "Too short for initial length");
|
||||
return tal_free(p);
|
||||
}
|
||||
|
||||
@ -201,6 +226,8 @@ struct onion_payload *onion_decode(const tal_t *ctx,
|
||||
TLVS_ARRAY_SIZE_tlv_payload,
|
||||
p->tlv, &p->tlv->fields, accepted_extra_tlvs,
|
||||
failtlvpos, failtlvtype)) {
|
||||
if (explanation)
|
||||
*explanation = tal_fmt(ctx, "Unparseable TLV");
|
||||
return tal_free(p);
|
||||
}
|
||||
|
||||
@ -225,12 +252,16 @@ struct onion_payload *onion_decode(const tal_t *ctx,
|
||||
if (path_key) {
|
||||
if (p->tlv->current_path_key) {
|
||||
*failtlvtype = TLV_PAYLOAD_CURRENT_PATH_KEY;
|
||||
if (explanation)
|
||||
*explanation = tal_fmt(ctx, "current_path_key was present");
|
||||
goto field_bad;
|
||||
}
|
||||
p->path_key = tal_dup(p, struct pubkey, path_key);
|
||||
} else {
|
||||
if (!p->tlv->current_path_key) {
|
||||
*failtlvtype = TLV_PAYLOAD_CURRENT_PATH_KEY;
|
||||
if (explanation)
|
||||
*explanation = tal_fmt(ctx, "current_path_key was not present");
|
||||
goto field_bad;
|
||||
}
|
||||
p->path_key = tal_dup(p, struct pubkey,
|
||||
@ -248,6 +279,8 @@ struct onion_payload *onion_decode(const tal_t *ctx,
|
||||
enc = decrypt_encrypted_data(tmpctx, &p->blinding_ss,
|
||||
p->tlv->encrypted_recipient_data);
|
||||
if (!enc) {
|
||||
if (explanation)
|
||||
*explanation = tal_fmt(ctx, "encrypted_recipient_data decryption failed");
|
||||
*failtlvtype = TLV_PAYLOAD_ENCRYPTED_RECIPIENT_DATA;
|
||||
goto field_bad;
|
||||
}
|
||||
@ -259,6 +292,10 @@ struct onion_payload *onion_decode(const tal_t *ctx,
|
||||
* `encrypted_recipient_data.payment_constraints.max_cltv_expiry`.
|
||||
*/
|
||||
if (cltv_expiry > enc->payment_constraints->max_cltv_expiry) {
|
||||
if (explanation)
|
||||
*explanation = tal_fmt(ctx, "cltv_expiry %u > payment_constraint %u",
|
||||
cltv_expiry,
|
||||
enc->payment_constraints->max_cltv_expiry);
|
||||
*failtlvtype = TLV_PAYLOAD_ENCRYPTED_RECIPIENT_DATA;
|
||||
goto field_bad;
|
||||
}
|
||||
@ -271,6 +308,10 @@ struct onion_payload *onion_decode(const tal_t *ctx,
|
||||
*/
|
||||
if (amount_msat_less(amount_in,
|
||||
amount_msat(enc->payment_constraints->htlc_minimum_msat))) {
|
||||
if (explanation)
|
||||
*explanation = tal_fmt(ctx, "amount_in %s < payment_constraint min %"PRIu64,
|
||||
fmt_amount_msat(tmpctx, amount_in),
|
||||
enc->payment_constraints->htlc_minimum_msat);
|
||||
*failtlvtype = TLV_PAYLOAD_ENCRYPTED_RECIPIENT_DATA;
|
||||
goto field_bad;
|
||||
}
|
||||
@ -299,21 +340,26 @@ struct onion_payload *onion_decode(const tal_t *ctx,
|
||||
/* No features, this is easy */
|
||||
if (!memeqzero(enc->allowed_features,
|
||||
tal_bytelen(enc->allowed_features))) {
|
||||
if (explanation)
|
||||
*explanation = tal_fmt(ctx, "non-zero allowed_features (%s)",
|
||||
tal_hex(tmpctx, enc->allowed_features));
|
||||
*failtlvtype = TLV_PAYLOAD_ENCRYPTED_RECIPIENT_DATA;
|
||||
goto field_bad;
|
||||
}
|
||||
|
||||
if (enc->short_channel_id && enc->next_node_id) {
|
||||
if (explanation)
|
||||
*explanation = tal_fmt(ctx, "both scid and next_node_id present");
|
||||
*failtlvtype = TLV_PAYLOAD_ENCRYPTED_RECIPIENT_DATA;
|
||||
goto field_bad;
|
||||
}
|
||||
|
||||
if (!p->final) {
|
||||
if (!handle_blinded_forward(p, amount_in, cltv_expiry,
|
||||
p->tlv, enc, failtlvtype))
|
||||
if (!handle_blinded_forward(ctx, p, amount_in, cltv_expiry,
|
||||
p->tlv, enc, failtlvtype, explanation))
|
||||
goto field_bad;
|
||||
} else {
|
||||
if (!handle_blinded_terminal(p, p->tlv, enc, failtlvtype))
|
||||
if (!handle_blinded_terminal(ctx, p, p->tlv, enc, failtlvtype, explanation))
|
||||
goto field_bad;
|
||||
}
|
||||
|
||||
@ -336,6 +382,9 @@ struct onion_payload *onion_decode(const tal_t *ctx,
|
||||
* is present.
|
||||
*/
|
||||
if (path_key || p->tlv->current_path_key) {
|
||||
if (explanation)
|
||||
*explanation = tal_fmt(ctx, "%s set outside blinded route",
|
||||
path_key ? "update_add_htlc->path_key" : "current_path_key");
|
||||
*failtlvtype = TLV_PAYLOAD_ENCRYPTED_RECIPIENT_DATA;
|
||||
goto field_bad;
|
||||
}
|
||||
@ -348,10 +397,14 @@ struct onion_payload *onion_decode(const tal_t *ctx,
|
||||
* `outgoing_cltv_value` are not present.
|
||||
*/
|
||||
if (!p->tlv->amt_to_forward) {
|
||||
if (explanation)
|
||||
*explanation = tal_fmt(ctx, "missing amt_to_forward");
|
||||
*failtlvtype = TLV_PAYLOAD_AMT_TO_FORWARD;
|
||||
goto field_bad;
|
||||
}
|
||||
if (!p->tlv->outgoing_cltv_value) {
|
||||
if (explanation)
|
||||
*explanation = tal_fmt(ctx, "missing outgoing_cltv_value");
|
||||
*failtlvtype = TLV_PAYLOAD_OUTGOING_CLTV_VALUE;
|
||||
goto field_bad;
|
||||
}
|
||||
@ -367,6 +420,8 @@ struct onion_payload *onion_decode(const tal_t *ctx,
|
||||
*/
|
||||
if (!p->final) {
|
||||
if (!p->tlv->short_channel_id) {
|
||||
if (explanation)
|
||||
*explanation = tal_fmt(ctx, "missing short_channel_id");
|
||||
*failtlvtype = TLV_PAYLOAD_SHORT_CHANNEL_ID;
|
||||
goto field_bad;
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
* @cltv_expiry: Incoming HTLC cltv_expiry
|
||||
* @failtlvtype: (out) the tlv type which failed to parse.
|
||||
* @failtlvpos: (out) the offset in the tlv which failed to parse.
|
||||
* @explanation: (out) if non-NULL, a string describing the problem on failure.
|
||||
*
|
||||
* If the payload is not valid, returns NULL.
|
||||
*/
|
||||
@ -26,5 +27,6 @@ struct onion_payload *onion_decode(const tal_t *ctx,
|
||||
struct amount_msat amount_in,
|
||||
u32 cltv_expiry,
|
||||
u64 *failtlvtype,
|
||||
size_t *failtlvpos);
|
||||
size_t *failtlvpos,
|
||||
const char **explanation);
|
||||
#endif /* LIGHTNING_COMMON_ONION_DECODE_H */
|
||||
|
@ -55,6 +55,9 @@ struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u
|
||||
/* Generated stub for amount_tx_fee */
|
||||
struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED)
|
||||
{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); }
|
||||
/* Generated stub for fmt_amount_msat */
|
||||
char *fmt_amount_msat(const tal_t *ctx UNNEEDED, struct amount_msat msat UNNEEDED)
|
||||
{ fprintf(stderr, "fmt_amount_msat called!\n"); abort(); }
|
||||
/* Generated stub for fromwire_amount_msat */
|
||||
struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
|
||||
{ fprintf(stderr, "fromwire_amount_msat called!\n"); abort(); }
|
||||
|
@ -66,6 +66,9 @@ struct tlv_encrypted_data_tlv *decrypt_encrypted_data(const tal_t *ctx UNNEEDED,
|
||||
/* Generated stub for ecdh */
|
||||
void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED)
|
||||
{ fprintf(stderr, "ecdh called!\n"); abort(); }
|
||||
/* Generated stub for fmt_amount_msat */
|
||||
char *fmt_amount_msat(const tal_t *ctx UNNEEDED, struct amount_msat msat UNNEEDED)
|
||||
{ fprintf(stderr, "fmt_amount_msat called!\n"); abort(); }
|
||||
/* Generated stub for fromwire_amount_msat */
|
||||
struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
|
||||
{ fprintf(stderr, "fromwire_amount_msat called!\n"); abort(); }
|
||||
|
@ -67,6 +67,9 @@ struct tlv_encrypted_data_tlv *decrypt_encrypted_data(const tal_t *ctx UNNEEDED,
|
||||
/* Generated stub for ecdh */
|
||||
void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED)
|
||||
{ fprintf(stderr, "ecdh called!\n"); abort(); }
|
||||
/* Generated stub for fmt_amount_msat */
|
||||
char *fmt_amount_msat(const tal_t *ctx UNNEEDED, struct amount_msat msat UNNEEDED)
|
||||
{ fprintf(stderr, "fmt_amount_msat called!\n"); abort(); }
|
||||
/* Generated stub for fromwire_amount_msat */
|
||||
struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
|
||||
{ fprintf(stderr, "fromwire_amount_msat called!\n"); abort(); }
|
||||
|
@ -1844,6 +1844,7 @@ static struct command_result *json_injectpaymentonion(struct command *cmd,
|
||||
const u8 *failmsg;
|
||||
struct htlc_out *hout;
|
||||
const struct wallet_payment *prev_payment;
|
||||
const char *explanation;
|
||||
|
||||
if (!param_check(cmd, buffer, params,
|
||||
p_req("onion", param_bin_from_hex, &onion),
|
||||
@ -1915,12 +1916,13 @@ static struct command_result *json_injectpaymentonion(struct command *cmd,
|
||||
cmd->ld->accept_extra_tlv_types,
|
||||
*msat, *cltv,
|
||||
&failtlvtype,
|
||||
&failtlvpos);
|
||||
&failtlvpos,
|
||||
&explanation);
|
||||
if (!payload) {
|
||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||
"Onion decode for %s failed at type %"PRIu64" offset %zu",
|
||||
"Onion decode for %s failed at type %"PRIu64" offset %zu (%s)",
|
||||
tal_hex(tmpctx, rs->raw_payload),
|
||||
failtlvtype, failtlvpos);
|
||||
failtlvtype, failtlvpos, explanation);
|
||||
}
|
||||
|
||||
if (payload->final) {
|
||||
|
@ -927,6 +927,7 @@ struct htlc_accepted_hook_payload {
|
||||
u8 *next_onion;
|
||||
u64 failtlvtype;
|
||||
size_t failtlvpos;
|
||||
const char *failexplanation;
|
||||
};
|
||||
|
||||
static void
|
||||
@ -1017,7 +1018,8 @@ static bool htlc_accepted_hook_deserialize(struct htlc_accepted_hook_payload *re
|
||||
hin->msat,
|
||||
hin->cltv_expiry,
|
||||
&request->failtlvtype,
|
||||
&request->failtlvpos);
|
||||
&request->failtlvpos,
|
||||
&request->failexplanation);
|
||||
}
|
||||
|
||||
fwdtok = json_get_member(buffer, toks, "forward_to");
|
||||
@ -1176,8 +1178,9 @@ htlc_accepted_hook_final(struct htlc_accepted_hook_payload *request STEALS)
|
||||
/* *Now* we barf if it failed to decode */
|
||||
if (!request->payload) {
|
||||
log_debug(channel->log,
|
||||
"Failing HTLC because of an invalid payload (TLV %"PRIu64" pos %zu)",
|
||||
request->failtlvtype, request->failtlvpos);
|
||||
"Failing HTLC because of an invalid payload (TLV %"PRIu64" pos %zu): %s",
|
||||
request->failtlvtype, request->failtlvpos,
|
||||
request->failexplanation);
|
||||
local_fail_in_htlc(hin,
|
||||
take(towire_invalid_onion_payload(
|
||||
NULL, request->failtlvtype,
|
||||
@ -1438,7 +1441,8 @@ static bool peer_accepted_htlc(const tal_t *ctx,
|
||||
hin->msat,
|
||||
hin->cltv_expiry,
|
||||
&hook_payload->failtlvtype,
|
||||
&hook_payload->failtlvpos);
|
||||
&hook_payload->failtlvpos,
|
||||
&hook_payload->failexplanation);
|
||||
hook_payload->ld = ld;
|
||||
hook_payload->hin = hin;
|
||||
hook_payload->channel = channel;
|
||||
|
@ -292,6 +292,7 @@ static struct onion_payload *decode_onion(const tal_t *ctx,
|
||||
struct privkey pk;
|
||||
struct pubkey current_pubkey;
|
||||
struct node_id current_node_id;
|
||||
const char *explanation;
|
||||
|
||||
op = parse_onionpacket(tmpctx, onion_routing_packet,
|
||||
TOTAL_PACKET_SIZE(ROUTING_INFO_SIZE),
|
||||
@ -335,11 +336,11 @@ static struct onion_payload *decode_onion(const tal_t *ctx,
|
||||
rs, path_key,
|
||||
NULL,
|
||||
amount,
|
||||
cltv, &failtlvtype, &failtlvpos);
|
||||
cltv, &failtlvtype, &failtlvpos, &explanation);
|
||||
if (!payload) {
|
||||
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||
"Failed tlvtype %"PRIu64" at %zu",
|
||||
failtlvtype, failtlvpos);
|
||||
"Failed tlvtype %"PRIu64" at %zu: %s",
|
||||
failtlvtype, failtlvpos, explanation);
|
||||
}
|
||||
|
||||
/* Find ourselves in the gossmap, so we know our channels */
|
||||
|
@ -742,7 +742,8 @@ struct onion_payload *onion_decode(const tal_t *ctx UNNEEDED,
|
||||
struct amount_msat amount_in UNNEEDED,
|
||||
u32 cltv_expiry UNNEEDED,
|
||||
u64 *failtlvtype UNNEEDED,
|
||||
size_t *failtlvpos UNNEEDED)
|
||||
size_t *failtlvpos UNNEEDED,
|
||||
const char **explanation UNNEEDED)
|
||||
{ fprintf(stderr, "onion_decode called!\n"); abort(); }
|
||||
/* Generated stub for onion_final_hop */
|
||||
u8 *onion_final_hop(const tal_t *ctx UNNEEDED,
|
||||
|
Loading…
Reference in New Issue
Block a user