#include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Overridden by run-param.c */ #ifndef paramcheck_assert #define paramcheck_assert assert #endif /* Overridden by run-param.c */ #ifndef paramcheck_assert #define paramcheck_assert assert #endif struct param { const char *name; const char *depr_start, *depr_end; bool is_set; enum param_style style; param_cbx cbx; void *arg; }; static void param_add(struct param **params, const char *name, enum param_style style, const char *depr_start, const char *depr_end, param_cbx cbx, void *arg) { struct param last; paramcheck_assert(name); paramcheck_assert(cbx); paramcheck_assert(arg); last.is_set = false; last.name = name; last.depr_start = depr_start; last.depr_end = depr_end; last.style = style; last.cbx = cbx; last.arg = arg; tal_arr_expand(params, last); } static bool is_required(enum param_style style) { return style == PARAM_REQUIRED; } static struct command_result *make_callback(struct command *cmd, struct param *def, const char *buffer, const jsmntok_t *tok) { /* If it had a default, free that now to avoid leak */ if ((def->style == PARAM_OPTIONAL_WITH_DEFAULT || def->style == PARAM_OPTIONAL_DEV_WITH_DEFAULT) && !def->is_set) tal_free(*(void **)def->arg); def->is_set = true; return def->cbx(cmd, def->name, buffer, tok, def->arg); } static struct command_result *post_check(struct command *cmd, struct param *params) { struct param *first = params; struct param *last = first + tal_count(params); /* Make sure required params were provided. */ while (first != last && is_required(first->style)) { if (!first->is_set) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "missing required parameter: %s", first->name); } first++; } return NULL; } static struct command_result *parse_by_position(struct command *cmd, struct param *params, const char *buffer, const jsmntok_t tokens[], bool allow_extra) { struct command_result *res; const jsmntok_t *tok; size_t i; json_for_each_arr(i, tok, tokens) { /* check for unexpected trailing params */ if (i == tal_count(params)) { if (!allow_extra) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "too many parameters:" " got %u, expected %zu", tokens->size, tal_count(params)); } break; } if (!json_tok_is_null(buffer, tok)) { if (params[i].style == PARAM_OPTIONAL_DEV_WITH_DEFAULT && !command_dev_apis(cmd)) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Parameter %zu is developer-only", i); } res = make_callback(cmd, params+i, buffer, tok); if (res) return res; } } return post_check(cmd, params); } static struct param *find_param(struct command *cmd, struct param *params, const char *start, size_t n) { struct param *first = params; struct param *last = first + tal_count(params); while (first != last) { if (memeqstr(start, n, first->name)) { if (!command_deprecated_in_ok(cmd, first->name, first->depr_start, first->depr_end)) { return NULL; } return first; } first++; } return NULL; } static struct command_result *parse_by_name(struct command *cmd, struct param *params, const char *buffer, const jsmntok_t tokens[], bool allow_extra) { size_t i; const jsmntok_t *t; json_for_each_obj(i, t, tokens) { struct param *p = find_param(cmd, params, buffer + t->start, t->end - t->start); if (!p) { if (!allow_extra) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "unknown parameter: %.*s, this may be caused by a failure to autodetect key=value-style parameters. Please try using the -k flag and explicit key=value pairs of parameters.", t->end - t->start, buffer + t->start); } } else { struct command_result *res; if (p->is_set) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "duplicate json names: %s", p->name); } if (p->style == PARAM_OPTIONAL_DEV_WITH_DEFAULT && !command_dev_apis(cmd)) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Parameter '%s' is developer-only", p->name); } res = make_callback(cmd, p, buffer, t + 1); if (res) return res; } } return post_check(cmd, params); } static int comp_by_name(const struct param *a, const struct param *b, void *unused) { return strcmp(a->name, b->name); } /* This comparator is a bit different, but works well. * Return 0 if @a is optional and @b is required. Otherwise return 1. */ static int comp_req_order(const struct param *a, const struct param *b, void *unused) { if (!is_required(a->style) && is_required(b->style)) return 0; return 1; } /* * Make sure 2 sequential items in @params are not equal (based on * provided comparator). */ static void check_distinct(const struct param *params, int (*compar)(const struct param *a, const struct param *b, void *unused)) { const struct param *first = params; const struct param *last = first + tal_count(params); first++; while (first != last) { paramcheck_assert(compar(first - 1, first, NULL) != 0); first++; } } static void check_unique(struct param *copy, int (*compar) (const struct param *a, const struct param *b, void *unused)) { asort(copy, tal_count(copy), compar, NULL); check_distinct(copy, compar); } /* * Verify consistent internal state. */ static void check_params(const struct param *params) { if (tal_count(params) < 2) return; /* make sure there are no required params following optional */ check_distinct(params, comp_req_order); /* duplicate so we can sort */ struct param *copy = tal_dup_talarr(params, struct param, params); /* check for repeated names and args */ check_unique(copy, comp_by_name); tal_free(copy); } static char *param_usage(const tal_t *ctx, const struct param *params) { char *usage = tal_strdup(ctx, ""); for (size_t i = 0; i < tal_count(params); i++) { /* Don't print |deprecated part! */ int len = strcspn(params[i].name, "|"); if (i != 0) tal_append_fmt(&usage, " "); if (is_required(params[i].style)) tal_append_fmt(&usage, "%.*s", len, params[i].name); else tal_append_fmt(&usage, "[%.*s]", len, params[i].name); } return usage; } static struct command_result *param_arr(struct command *cmd, const char *buffer, const jsmntok_t tokens[], struct param *params, bool allow_extra) { if (tokens->type == JSMN_ARRAY) return parse_by_position(cmd, params, buffer, tokens, allow_extra); else if (tokens->type == JSMN_OBJECT) return parse_by_name(cmd, params, buffer, tokens, allow_extra); return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Expected array or object for params"); } const char *param_subcommand(struct command *cmd, const char *buffer, const jsmntok_t tokens[], const char *name, ...) { va_list ap; struct param *params = tal_arr(cmd, struct param, 0); const char *arg, **names = tal_arr(tmpctx, const char *, 1); const char *subcmd; param_add(¶ms, "subcommand", PARAM_REQUIRED, NULL, NULL, (void *)param_string, &subcmd); names[0] = name; va_start(ap, name); while ((arg = va_arg(ap, const char *)) != NULL) tal_arr_expand(&names, arg); va_end(ap); if (command_usage_only(cmd)) { char *usage = tal_strdup(cmd, "subcommand"); for (size_t i = 0; i < tal_count(names); i++) tal_append_fmt(&usage, "%c%s", i == 0 ? '=' : '|', names[i]); check_params(params); command_set_usage(cmd, usage); return NULL; } /* Check it's valid */ if (param_arr(cmd, buffer, tokens, params, true) != NULL) { return NULL; } /* Check it's one of the known ones. */ for (size_t i = 0; i < tal_count(names); i++) if (streq(subcmd, names[i])) return subcmd; /* We really do ignore this. */ struct command_result *ignore; ignore = command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Unknown subcommand '%s'", subcmd); assert(ignore); return NULL; } static bool param_core(struct command *cmd, const char *buffer, const jsmntok_t tokens[], va_list ap) { struct param *params = tal_arr(tmpctx, struct param, 0); const char *name; bool allow_extra = false; while ((name = va_arg(ap, const char *)) != NULL) { enum param_style style = va_arg(ap, enum param_style); const char *depr_start = va_arg(ap, const char *); const char *depr_end = va_arg(ap, const char *); param_cbx cbx = va_arg(ap, param_cbx); void *arg = va_arg(ap, void *); if (streq(name, "")) { allow_extra = true; continue; } param_add(¶ms, name, style, depr_start, depr_end, cbx, arg); } if (command_usage_only(cmd)) { check_params(params); command_set_usage(cmd, param_usage(cmd, params)); return false; } return param_arr(cmd, buffer, tokens, params, allow_extra) == NULL; } bool param(struct command *cmd, const char *buffer, const jsmntok_t tokens[], ...) { bool ret; va_list ap; va_start(ap, tokens); ret = param_core(cmd, buffer, tokens, ap); va_end(ap); /* Always "fail" if we're just checking! */ if (ret && command_check_only(cmd)) { /* We really do ignore result here! */ if (command_check_done(cmd)) ; ret = false; } return ret; } bool param_check(struct command *cmd, const char *buffer, const jsmntok_t tokens[], ...) { bool ret; va_list ap; va_start(ap, tokens); ret = param_core(cmd, buffer, tokens, ap); va_end(ap); return ret; } struct command_result *param_array(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, const jsmntok_t **arr) { if (tok->type == JSMN_ARRAY) { *arr = tok; return NULL; } return command_fail_badparam(cmd, name, buffer, tok, "should be an array"); } struct command_result *param_bool(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, bool **b) { *b = tal(cmd, bool); if (json_to_bool(buffer, tok, *b)) return NULL; return command_fail_badparam(cmd, name, buffer, tok, "should be 'true' or 'false'"); } struct command_result *param_millionths(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, uint64_t **num) { *num = tal(cmd, uint64_t); if (json_to_millionths(buffer, tok, *num)) return NULL; return command_fail_badparam(cmd, name, buffer, tok, "should be a non-negative floating-point number"); } struct command_result *param_escaped_string(struct command *cmd, const char *name, const char * buffer, const jsmntok_t *tok, const char **str) { if (tok->type == JSMN_STRING) { struct json_escape *esc; /* jsmn always gives us ~ well-formed strings. */ esc = json_escape_string_(cmd, buffer + tok->start, tok->end - tok->start); *str = json_escape_unescape(cmd, esc); if (*str) return NULL; } return command_fail_badparam(cmd, name, buffer, tok, "should be a string (without \\u)"); } struct command_result *param_string(struct command *cmd, const char *name, const char * buffer, const jsmntok_t *tok, const char **str) { *str = tal_strndup(cmd, buffer + tok->start, tok->end - tok->start); return NULL; } struct command_result *param_invstring(struct command *cmd, const char *name, const char * buffer, const jsmntok_t *tok, const char **str) { const char *strtmp = json_strdup(cmd, buffer, tok); *str = to_canonical_invstr(cmd, strtmp); return NULL; } struct command_result *param_ignore(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, const void *unused) { return NULL; } struct command_result *param_label(struct command *cmd, const char *name, const char * buffer, const jsmntok_t *tok, struct json_escape **label) { /* We accept both strings and number literals here. */ *label = json_escape_string_(cmd, buffer + tok->start, tok->end - tok->start); if (*label && (tok->type == JSMN_STRING || json_tok_is_num(buffer, tok))) return NULL; return command_fail_badparam(cmd, name, buffer, tok, "should be a string or number"); } struct command_result *param_number(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, unsigned int **num) { *num = tal(cmd, unsigned int); if (json_to_number(buffer, tok, *num)) return NULL; return command_fail_badparam(cmd, name, buffer, tok, "should be an integer"); } struct command_result *param_sha256(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct sha256 **hash) { *hash = tal(cmd, struct sha256); if (hex_decode(buffer + tok->start, tok->end - tok->start, *hash, sizeof(**hash))) return NULL; return command_fail_badparam(cmd, name, buffer, tok, "should be a 32 byte hex value"); } struct command_result *param_u32(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, uint32_t **num) { *num = tal(cmd, uint32_t); if (json_to_u32(buffer, tok, *num)) return NULL; return command_fail_badparam(cmd, name, buffer, tok, "should be an unsigned 32 bit integer"); } struct command_result *param_u64(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, uint64_t **num) { *num = tal(cmd, uint64_t); if (json_to_u64(buffer, tok, *num)) return NULL; return command_fail_badparam(cmd, name, buffer, tok, "should be an unsigned 64 bit integer"); } struct command_result *param_s64(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, int64_t **num) { *num = tal(cmd, int64_t); if (json_to_s64(buffer, tok, *num)) return NULL; return command_fail_badparam(cmd, name, buffer, tok, "should be an sign 64 bit integer"); } struct command_result *param_msat(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct amount_msat **msat) { *msat = tal(cmd, struct amount_msat); if (parse_amount_msat(*msat, buffer + tok->start, tok->end - tok->start)) return NULL; return command_fail_badparam(cmd, name, buffer, tok, "should be a millisatoshi amount"); } struct command_result *param_sat(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct amount_sat **sat) { *sat = tal(cmd, struct amount_sat); if (parse_amount_sat(*sat, buffer + tok->start, tok->end - tok->start)) return NULL; return command_fail_badparam(cmd, name, buffer, tok, "should be a satoshi amount"); } struct command_result *param_sat_or_all(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct amount_sat **sat) { if (json_tok_streq(buffer, tok, "all")) { *sat = tal(cmd, struct amount_sat); **sat = AMOUNT_SAT(-1ULL); return NULL; } return param_sat(cmd, name, buffer, tok, sat); } struct command_result *param_node_id(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct node_id **id) { *id = tal(cmd, struct node_id); if (json_to_node_id(buffer, tok, *id)) return NULL; return command_fail_badparam(cmd, name, buffer, tok, "should be a node id"); } struct command_result *param_channel_id(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct channel_id **cid) { *cid = tal(cmd, struct channel_id); if (json_to_channel_id(buffer, tok, *cid)) return NULL; return command_fail_badparam(cmd, name, buffer, tok, "should be a channel id"); } struct command_result *param_short_channel_id(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct short_channel_id **scid) { *scid = tal(cmd, struct short_channel_id); if (json_to_short_channel_id(buffer, tok, *scid)) return NULL; return command_fail_badparam(cmd, name, buffer, tok, "should be a short_channel_id of form NxNxN"); } struct command_result *param_secret(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct secret **secret) { *secret = tal(cmd, struct secret); if (hex_decode(buffer + tok->start, tok->end - tok->start, *secret, sizeof(**secret))) return NULL; return command_fail_badparam(cmd, name, buffer, tok, "should be a 32 byte hex value"); } struct command_result *param_bin_from_hex(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, u8 **bin) { *bin = json_tok_bin_from_hex(cmd, buffer, tok); if (bin != NULL) return NULL; return command_fail_badparam(cmd, name, buffer, tok, "should be a hex value"); } 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, *pubkeytok; struct sphinx_hop h; size_t i; if (tok->type != JSMN_ARRAY) { return command_fail_badparam(cmd, name, buffer, tok, "should be an array of hops"); } *hops = tal_arr(cmd, struct sphinx_hop, 0); json_for_each_arr(i, hop, tok) { payloadtok = json_get_member(buffer, hop, "payload"); 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.raw_payload = json_tok_bin_from_hex(*hops, buffer, payloadtok); if (!json_to_pubkey(buffer, pubkeytok, &h.pubkey)) return command_fail_badparam(cmd, name, buffer, pubkeytok, "should be a pubkey"); if (!h.raw_payload) return command_fail_badparam(cmd, name, buffer, payloadtok, "should be hex"); 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; } struct command_result *param_secrets_array(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct secret **secrets) { size_t i; const jsmntok_t *s; struct secret secret; if (tok->type != JSMN_ARRAY) { return command_fail_badparam(cmd, name, buffer, tok, "should be an array of secrets"); } *secrets = tal_arr(cmd, struct secret, 0); json_for_each_arr(i, s, tok) { if (!hex_decode(buffer + s->start, s->end - s->start, &secret, sizeof(secret))) return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "'%s[%zu]' should be a 32 byte hex " "value, not '%.*s'", name, i, s->end - s->start, buffer + s->start); tal_arr_expand(secrets, secret); } return NULL; } /** * segwit_addr_net_decode - Try to decode a Bech32(m) address and detect * testnet/mainnet/regtest/signet * * This processes the address and returns a string if it is a Bech32 * address specified by BIP173 or Bech32m as by BIP350. The string is * set whether it is testnet or signet (both "tb"), mainnet ("bc"), * regtest ("bcrt"). It does not check witness version and program size * restrictions. * * Out: witness_version: Pointer to an int that will be updated to contain * the witness program version (between 0 and 16 inclusive). * witness_program: Pointer to a buffer of size 40 that will be updated * to contain the witness program bytes. * witness_program_len: Pointer to a size_t that will be updated to * contain the length of bytes in witness_program. * In: addrz: Pointer to the null-terminated address. * Returns string containing the human readable segment of bech32 address */ static const char *segwit_addr_net_decode(int *witness_version, uint8_t *witness_program, size_t *witness_program_len, const char *addrz, const struct chainparams *chainparams) { if (segwit_addr_decode(witness_version, witness_program, witness_program_len, chainparams->onchain_hrp, addrz)) return chainparams->onchain_hrp; else return NULL; } enum address_parse_result json_to_address_scriptpubkey(const tal_t *ctx, const struct chainparams *chainparams, const char *buffer, const jsmntok_t *tok, const u8 **scriptpubkey) { struct bitcoin_address destination; int witness_version; /* segwit_addr_net_decode requires a buffer of size 40, and will * not write to the buffer if the address is too long, so a buffer * of fixed size 40 will not overflow. */ uint8_t witness_program[40]; size_t witness_program_len; char *addrz; const char *bech32; u8 addr_version; if (ripemd160_from_base58(&addr_version, &destination.addr, buffer + tok->start, tok->end - tok->start)) { if (addr_version == chainparams->p2pkh_version) { *scriptpubkey = scriptpubkey_p2pkh(ctx, &destination); return ADDRESS_PARSE_SUCCESS; } else if (addr_version == chainparams->p2sh_version) { *scriptpubkey = scriptpubkey_p2sh_hash(ctx, &destination.addr); return ADDRESS_PARSE_SUCCESS; } else { return ADDRESS_PARSE_WRONG_NETWORK; } /* Insert other parsers that accept pointer+len here. */ return ADDRESS_PARSE_UNRECOGNIZED; } /* Generate null-terminated address. */ addrz = tal_dup_arr(tmpctx, char, buffer + tok->start, tok->end - tok->start, 1); addrz[tok->end - tok->start] = '\0'; bech32 = segwit_addr_net_decode(&witness_version, witness_program, &witness_program_len, addrz, chainparams); if (bech32) { bool witness_ok; /* Only V0 has restricted lengths of witness programs */ if (witness_version == 0) { witness_ok = (witness_program_len == 20 || witness_program_len == 32); } else witness_ok = true; if (!witness_ok) return ADDRESS_PARSE_UNRECOGNIZED; if (!streq(bech32, chainparams->onchain_hrp)) return ADDRESS_PARSE_WRONG_NETWORK; *scriptpubkey = scriptpubkey_witness_raw(ctx, witness_version, witness_program, witness_program_len); return ADDRESS_PARSE_SUCCESS; } /* Insert other parsers that accept null-terminated string here. */ return ADDRESS_PARSE_UNRECOGNIZED; } struct command_result *param_txid(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct bitcoin_txid **txid) { *txid = tal(cmd, struct bitcoin_txid); if (json_to_txid(buffer, tok, *txid)) return NULL; return command_fail_badparam(cmd, name, buffer, tok, "should be a txid"); } struct command_result *param_bitcoin_address(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, const u8 **scriptpubkey) { /* Parse address. */ switch (json_to_address_scriptpubkey(cmd, chainparams, buffer, tok, scriptpubkey)) { case ADDRESS_PARSE_UNRECOGNIZED: return command_fail(cmd, LIGHTNINGD, "Could not parse destination address, " "%s should be a valid address", name ? name : "address field"); case ADDRESS_PARSE_WRONG_NETWORK: return command_fail(cmd, LIGHTNINGD, "Destination address is not on network %s", chainparams->network_name); case ADDRESS_PARSE_SUCCESS: return NULL; } abort(); } struct command_result *param_psbt(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct wally_psbt **psbt) { *psbt = psbt_from_b64(cmd, buffer + tok->start, tok->end - tok->start); if (*psbt) return NULL; return command_fail_badparam(cmd, name, buffer, tok, "Expected a PSBT"); } struct command_result *param_outpoint_arr(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct bitcoin_outpoint **outpoints) { size_t i; const jsmntok_t *curr; if (tok->type != JSMN_ARRAY) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Could not decode the outpoint array for %s: " "\"%s\" is not a valid outpoint array.", name, json_strdup(tmpctx, buffer, tok)); } *outpoints = tal_arr(cmd, struct bitcoin_outpoint, tok->size); json_for_each_arr(i, curr, tok) { if (!json_to_outpoint(buffer, curr, &(*outpoints)[i])) return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Could not decode outpoint \"%.*s\", " "expected format: txid:output", json_tok_full_len(curr), json_tok_full(buffer, curr)); } return NULL; } struct command_result *param_extra_tlvs(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct tlv_field **fields) { size_t i; const jsmntok_t *curr; struct tlv_field *f, *temp; if (tok->type != JSMN_OBJECT) { return command_fail( cmd, JSONRPC2_INVALID_PARAMS, "Could not decode the TLV object from %s: " "\"%s\" is not a valid JSON object.", name, json_strdup(tmpctx, buffer, tok)); } temp = tal_arr(cmd, struct tlv_field, tok->size); json_for_each_obj(i, curr, tok) { f = &temp[i]; /* Accept either bare ints as keys (not spec * compliant, but simpler), or ints in strings, which * are JSON spec compliant. */ if (!(json_str_to_u64(buffer, curr, &f->numtype) || json_to_u64(buffer, curr, &f->numtype))) { return command_fail( cmd, JSONRPC2_INVALID_PARAMS, "\"%s\" is not a valid numeric TLV type.", json_strdup(tmpctx, buffer, curr)); } f->value = json_tok_bin_from_hex(temp, buffer, curr + 1); if (f->value == NULL) { return command_fail( cmd, JSONRPC2_INVALID_PARAMS, "\"%s\" is not a valid hex encoded TLV value.", json_strdup(tmpctx, buffer, curr)); } f->length = tal_bytelen(f->value); f->meta = NULL; } *fields = temp; return NULL; } static struct command_result *param_routehint(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct route_info **ri) { size_t i; const jsmntok_t *curr; const char *err; if (tok->type != JSMN_ARRAY) { return command_fail( cmd, JSONRPC2_INVALID_PARAMS, "Routehint %s (\"%s\") is not an array of hop objects", name, json_strdup(tmpctx, buffer, tok)); } *ri = tal_arr(cmd, struct route_info, tok->size); json_for_each_arr(i, curr, tok) { struct route_info *e = &(*ri)[i]; struct amount_msat temp; err = json_scan(tmpctx, buffer, curr, "{id:%,scid:%,feebase:%,feeprop:%,expirydelta:%}", JSON_SCAN(json_to_node_id, &e->pubkey), JSON_SCAN(json_to_short_channel_id, &e->short_channel_id), JSON_SCAN(json_to_msat, &temp), JSON_SCAN(json_to_u32, &e->fee_proportional_millionths), JSON_SCAN(json_to_u16, &e->cltv_expiry_delta) ); e->fee_base_msat = temp.millisatoshis; /* Raw: internal conversion. */ if (err != NULL) { return command_fail( cmd, JSONRPC2_INVALID_PARAMS, "Error parsing routehint %s[%zu]: %s", name, i, err); } } return NULL; } struct command_result * param_routehint_array(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct route_info ***ris) { size_t i; const jsmntok_t *curr; char *element_name; struct command_result *err; if (tok->type != JSMN_ARRAY) { return command_fail( cmd, JSONRPC2_INVALID_PARAMS, "Routehint array %s (\"%s\") is not an array", name, json_strdup(tmpctx, buffer, tok)); } *ris = tal_arr(cmd, struct route_info *, 0); json_for_each_arr(i, curr, tok) { struct route_info *element; element_name = tal_fmt(cmd, "%s[%zu]", name, i); err = param_routehint(cmd, element_name, buffer, curr, &element); if (err != NULL) { return err; } tal_arr_expand(ris, element); tal_free(element_name); } return NULL; } struct command_result *param_route_exclusion(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct route_exclusion **re) { *re = tal(cmd, struct route_exclusion); struct short_channel_id_dir *chan_id = tal(tmpctx, struct short_channel_id_dir); if (!short_channel_id_dir_from_str(buffer + tok->start, tok->end - tok->start, chan_id)) { struct node_id *node_id = tal(tmpctx, struct node_id); if (!json_to_node_id(buffer, tok, node_id)) return command_fail_badparam(cmd, "exclude", buffer, tok, "should be short_channel_id_dir or node_id"); (*re)->type = EXCLUDE_NODE; (*re)->u.node_id = *node_id; } else { (*re)->type = EXCLUDE_CHANNEL; (*re)->u.chan_id = *chan_id; } return NULL; } struct command_result * param_route_exclusion_array(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct route_exclusion ***res) { size_t i; const jsmntok_t *curr; char *element_name; struct command_result *err; if (tok->type != JSMN_ARRAY) { return command_fail( cmd, JSONRPC2_INVALID_PARAMS, "Exclude array %s (\"%s\") is not an array", name, json_strdup(tmpctx, buffer, tok)); } *res = tal_arr(cmd, struct route_exclusion *, 0); json_for_each_arr(i, curr, tok) { struct route_exclusion *element; element_name = tal_fmt(cmd, "%s[%zu]", name, i); err = param_route_exclusion(cmd, element_name, buffer, curr, &element); if (err != NULL) { return err; } tal_arr_expand(res, element); tal_free(element_name); } return NULL; } struct command_result *param_lease_hex(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct lease_rates **rates) { *rates = lease_rates_fromhex(cmd, buffer + tok->start, tok->end - tok->start); if (!*rates) return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Could not decode '%s' %.*s", name, json_tok_full_len(tok), json_tok_full(buffer, tok)); return NULL; } struct command_result *param_pubkey(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct pubkey **pubkey) { *pubkey = tal(cmd, struct pubkey); if (json_to_pubkey(buffer, tok, *pubkey)) return NULL; return command_fail_badparam(cmd, name, buffer, tok, "should be a compressed pubkey"); }