diff --git a/common/json_tok.c b/common/json_tok.c index 4621d4ccc..7695639cd 100644 --- a/common/json_tok.c +++ b/common/json_tok.c @@ -256,3 +256,71 @@ struct command_result *param_bin_from_hex(struct command *cmd, const char *name, "'%s' should be a hex value, not '%.*s'", name, tok->end - tok->start, buffer + tok->start); } + +struct command_result *param_hops_array(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + struct sphinx_hop **hops) +{ + const jsmntok_t *hop, *payloadtok, *typetok, *pubkeytok; + struct sphinx_hop h; + size_t i; + if (tok->type != JSMN_ARRAY) { + return command_fail( + cmd, JSONRPC2_INVALID_PARAMS, + "'%s' should be an array of hops, got '%.*s'", name, + tok->end - tok->start, buffer + tok->start); + } + + *hops = tal_arr(cmd, struct sphinx_hop, 0); + + json_for_each_arr(i, hop, tok) { + + payloadtok = json_get_member(buffer, hop, "payload"); + typetok = json_get_member(buffer, hop, "type"); + pubkeytok = json_get_member(buffer, hop, "pubkey"); + + if (!pubkeytok) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Hop %zu does not have a pubkey", i); + + if (!payloadtok) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Hop %zu does not have a payload", i); + + h.payload = json_tok_bin_from_hex(*hops, buffer, payloadtok); + if (!json_to_pubkey(buffer, pubkeytok, &h.pubkey)) + return command_fail( + cmd, JSONRPC2_INVALID_PARAMS, + "'pubkey' should be a pubkey, not '%.*s'", + pubkeytok->end - pubkeytok->start, + buffer + pubkeytok->start); + + if (!h.payload) + return command_fail( + cmd, JSONRPC2_INVALID_PARAMS, + "'payload' should be a hex encoded binary, not '%.*s'", + pubkeytok->end - pubkeytok->start, + buffer + pubkeytok->start); + + if (!typetok || json_tok_streq(buffer, typetok, "tlv")) { + h.type = SPHINX_TLV_PAYLOAD; + } else if (json_tok_streq(buffer, typetok, "legacy")) { + h.type = SPHINX_V0_PAYLOAD; + } else { + return command_fail( + cmd, JSONRPC2_INVALID_PARAMS, + "Unknown payload type for hop %zu: '%.*s'", i, + pubkeytok->end - pubkeytok->start, + buffer + pubkeytok->start); + } + + tal_arr_expand(hops, h); + } + + if (tal_count(*hops) == 0) { + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "At least one hop must be specified."); + } + + return NULL; +} diff --git a/common/json_tok.h b/common/json_tok.h index 3a8cf9417..beba94303 100644 --- a/common/json_tok.h +++ b/common/json_tok.h @@ -5,6 +5,7 @@ #include #include #include +#include #include struct amount_msat; @@ -121,4 +122,8 @@ struct command_result *param_bin_from_hex(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, u8 **bin); +struct command_result *param_hops_array(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + struct sphinx_hop **hops); + #endif /* LIGHTNING_COMMON_JSON_TOK_H */ diff --git a/common/test/run-param.c b/common/test/run-param.c index 6fe4c65bc..37c91a03d 100644 --- a/common/test/run-param.c +++ b/common/test/run-param.c @@ -50,6 +50,10 @@ bool json_to_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEED bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "json_to_node_id called!\n"); abort(); } +/* Generated stub for json_to_pubkey */ +bool json_to_pubkey(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct pubkey *pubkey UNNEEDED) +{ fprintf(stderr, "json_to_pubkey called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ /* We do this lightningd-style: */ diff --git a/lightningd/pay.c b/lightningd/pay.c index 5a1547db1..faa9d7049 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1370,18 +1370,15 @@ static struct command_result *json_createonion(struct command *cmd, const jsmntok_t *obj UNNEEDED, const jsmntok_t *params) { - const jsmntok_t *hopstok, *hop, *payloadtok, *typetok, *pubkeytok; - enum sphinx_payload_type type; - size_t i, hop_count = 0; struct json_stream *response; - struct pubkey pubkey; struct secret *session_key, *shared_secrets; struct sphinx_path *sp; - u8 *assocdata, *payload, *serialized; + u8 *assocdata, *serialized; struct onionpacket *packet; + struct sphinx_hop *hops; if (!param(cmd, buffer, params, - p_req("hops", param_array, &hopstok), + p_req("hops", param_hops_array, &hops), p_req("assocdata", param_bin_from_hex, &assocdata), p_opt("session_key", param_secret, &session_key), NULL)) { @@ -1393,48 +1390,9 @@ static struct command_result *json_createonion(struct command *cmd, else sp = sphinx_path_new_with_key(cmd, assocdata, session_key); - json_for_each_arr(i, hop, hopstok) { - payloadtok = json_get_member(buffer, hop, "payload"); - typetok = json_get_member(buffer, hop, "type"); - pubkeytok = json_get_member(buffer, hop, "pubkey"); - - if (!pubkeytok) - return command_fail(cmd, JSONRPC2_INVALID_REQUEST, - "Hop %zu does not have a pubkey", i); - - if (!payloadtok) - return command_fail(cmd, JSONRPC2_INVALID_REQUEST, - "Hop %zu does not have a payload", i); - - payload = json_tok_bin_from_hex(cmd, buffer, payloadtok); - if (!json_to_pubkey(buffer, pubkeytok, &pubkey)) - return command_fail( - cmd, JSONRPC2_INVALID_PARAMS, - "'pubkey' should be a pubkey, not '%.*s'", - pubkeytok->end - pubkeytok->start, - buffer + pubkeytok->start); - - if (!payload) - return command_fail( - cmd, JSONRPC2_INVALID_PARAMS, - "'payload' should be a hex encoded binary, not '%.*s'", - pubkeytok->end - pubkeytok->start, - buffer + pubkeytok->start); - - if (!typetok || !json_tok_streq(buffer, typetok, "legacy")) { - type = SPHINX_RAW_PAYLOAD; - } else { - type = SPHINX_V0_PAYLOAD; - } - - sphinx_add_raw_hop(sp, &pubkey, type, payload); - hop_count++; - } - - if (hop_count == 0) - return command_fail(cmd, JSONRPC2_INVALID_REQUEST, - "Cannot create an onion without hops."); - + for (size_t i=0; i