core-lightning/lightningd/json.c

354 lines
10 KiB
C
Raw Normal View History

#include <arpa/inet.h>
#include <ccan/mem/mem.h>
#include <ccan/str/hex/hex.h>
#include <ccan/tal/str/str.h>
#include <common/json.h>
#include <common/json_command.h>
#include <common/json_escaped.h>
#include <common/json_helpers.h>
#include <common/jsonrpc_errors.h>
#include <common/memleak.h>
#include <common/param.h>
#include <common/type_to_string.h>
#include <common/wallet_tx.h>
#include <common/wireaddr.h>
#include <gossipd/routing.h>
#include <lightningd/json.h>
#include <lightningd/json_stream.h>
#include <lightningd/jsonrpc.h>
#include <lightningd/options.h>
#include <sys/socket.h>
#include <wallet/wallet.h>
#include <wire/wire.h>
/* Output a route hop */
static void
json_add_route_hop(struct json_stream *r, char const *n,
const struct route_hop *h)
{
/* Imitate what getroute/sendpay use */
json_object_start(r, n);
json_add_pubkey(r, "id", &h->nodeid);
json_add_short_channel_id(r, "channel",
&h->channel_id);
json_add_num(r, "direction", h->direction);
json_add_amount_msat(r, h->amount, "msatoshi", "amount_msat");
json_add_num(r, "delay", h->delay);
json_object_end(r);
}
/* Output a route */
void
json_add_route(struct json_stream *r, char const *n,
const struct route_hop *hops, size_t hops_len)
{
size_t i;
json_array_start(r, n);
for (i = 0; i < hops_len; ++i) {
json_add_route_hop(r, NULL, &hops[i]);
}
json_array_end(r);
}
void json_add_pubkey(struct json_stream *response,
const char *fieldname,
const struct pubkey *key)
{
u8 der[PUBKEY_DER_LEN];
pubkey_to_der(der, key);
json_add_hex(response, fieldname, der, sizeof(der));
}
void json_add_txid(struct json_stream *result, const char *fieldname,
const struct bitcoin_txid *txid)
{
char hex[hex_str_size(sizeof(*txid))];
bitcoin_txid_to_hex(txid, hex, sizeof(hex));
json_add_string(result, fieldname, hex);
}
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(cmd, JSONRPC2_INVALID_PARAMS,
"'%s' should be a pubkey, not '%.*s'",
name, json_tok_full_len(tok),
json_tok_full(buffer, tok));
}
void json_add_short_channel_id(struct json_stream *response,
const char *fieldname,
const struct short_channel_id *id)
{
json_add_string(response, fieldname,
type_to_string(response, struct short_channel_id, 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,
deprecated_apis))
return NULL;
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"'%s' should be a short channel id, not '%.*s'",
name, json_tok_full_len(tok),
json_tok_full(buffer, tok));
}
const char *json_feerate_style_name(enum feerate_style style)
{
switch (style) {
case FEERATE_PER_KBYTE:
return "perkb";
case FEERATE_PER_KSIPA:
return "perkw";
}
abort();
}
struct command_result *param_feerate_style(struct command *cmd,
const char *name,
const char *buffer,
const jsmntok_t *tok,
enum feerate_style **style)
{
*style = tal(cmd, enum feerate_style);
if (json_tok_streq(buffer, tok,
json_feerate_style_name(FEERATE_PER_KSIPA))) {
**style = FEERATE_PER_KSIPA;
return NULL;
} else if (json_tok_streq(buffer, tok,
json_feerate_style_name(FEERATE_PER_KBYTE))) {
**style = FEERATE_PER_KBYTE;
return NULL;
}
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"'%s' should be '%s' or '%s', not '%.*s'",
name,
json_feerate_style_name(FEERATE_PER_KSIPA),
json_feerate_style_name(FEERATE_PER_KBYTE),
json_tok_full_len(tok), json_tok_full(buffer, tok));
}
struct command_result *param_feerate(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
u32 **feerate)
{
jsmntok_t base = *tok, suffix = *tok;
enum feerate_style style;
unsigned int num;
for (size_t i = 0; i < NUM_FEERATES; i++) {
if (json_tok_streq(buffer, tok, feerate_name(i)))
return param_feerate_estimate(cmd, feerate, i);
}
/* We have to split the number and suffix. */
suffix.start = suffix.end;
while (suffix.start > base.start && !isdigit(buffer[suffix.start-1])) {
suffix.start--;
base.end--;
}
if (!json_to_number(buffer, &base, &num)) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"'%s' prefix should be an integer, not '%.*s'",
name, base.end - base.start,
buffer + base.start);
}
if (json_tok_streq(buffer, &suffix, "")
|| json_tok_streq(buffer, &suffix,
json_feerate_style_name(FEERATE_PER_KBYTE))) {
style = FEERATE_PER_KBYTE;
} else if (json_tok_streq(buffer, &suffix,
json_feerate_style_name(FEERATE_PER_KSIPA))) {
style = FEERATE_PER_KSIPA;
} else {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"'%s' suffix should be '%s' or '%s', not '%.*s'",
name,
json_feerate_style_name(FEERATE_PER_KSIPA),
json_feerate_style_name(FEERATE_PER_KBYTE),
suffix.end - suffix.start,
buffer + suffix.start);
}
*feerate = tal(cmd, u32);
**feerate = feerate_from_style(num, style);
return NULL;
}
bool
json_tok_channel_id(const char *buffer, const jsmntok_t *tok,
struct channel_id *cid)
{
return hex_decode(buffer + tok->start, tok->end - tok->start,
cid, sizeof(*cid));
}
void json_add_address(struct json_stream *response, const char *fieldname,
const struct wireaddr *addr)
{
json_object_start(response, fieldname);
char *addrstr = tal_arr(response, char, INET6_ADDRSTRLEN);
if (addr->type == ADDR_TYPE_IPV4) {
inet_ntop(AF_INET, addr->addr, addrstr, INET_ADDRSTRLEN);
json_add_string(response, "type", "ipv4");
json_add_string(response, "address", addrstr);
json_add_num(response, "port", addr->port);
} else if (addr->type == ADDR_TYPE_IPV6) {
inet_ntop(AF_INET6, addr->addr, addrstr, INET6_ADDRSTRLEN);
json_add_string(response, "type", "ipv6");
json_add_string(response, "address", addrstr);
json_add_num(response, "port", addr->port);
} else if (addr->type == ADDR_TYPE_TOR_V2) {
json_add_string(response, "type", "torv2");
json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr));
json_add_num(response, "port", addr->port);
} else if (addr->type == ADDR_TYPE_TOR_V3) {
json_add_string(response, "type", "torv3");
json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr));
json_add_num(response, "port", addr->port);
}
json_object_end(response);
}
void json_add_address_internal(struct json_stream *response,
const char *fieldname,
const struct wireaddr_internal *addr)
{
switch (addr->itype) {
case ADDR_INTERNAL_SOCKNAME:
json_object_start(response, fieldname);
json_add_string(response, "type", "local socket");
json_add_string(response, "socket", addr->u.sockname);
json_object_end(response);
return;
case ADDR_INTERNAL_ALLPROTO:
json_object_start(response, fieldname);
json_add_string(response, "type", "any protocol");
json_add_num(response, "port", addr->u.port);
json_object_end(response);
return;
case ADDR_INTERNAL_AUTOTOR:
json_object_start(response, fieldname);
json_add_string(response, "type", "Tor generated address");
json_add_address(response, "service", &addr->u.torservice);
json_object_end(response);
return;
case ADDR_INTERNAL_FORPROXY:
json_object_start(response, fieldname);
json_add_string(response, "type", "unresolved");
json_add_string(response, "name", addr->u.unresolved.name);
json_add_num(response, "port", addr->u.unresolved.port);
json_object_end(response);
return;
case ADDR_INTERNAL_WIREADDR:
json_add_address(response, fieldname, &addr->u.wireaddr);
return;
}
abort();
}
void json_add_num(struct json_stream *result, const char *fieldname, unsigned int value)
{
json_add_member(result, fieldname, "%u", value);
}
void json_add_double(struct json_stream *result, const char *fieldname, double value)
{
json_add_member(result, fieldname, "%f", value);
}
void json_add_u64(struct json_stream *result, const char *fieldname,
uint64_t value)
{
json_add_member(result, fieldname, "%"PRIu64, value);
}
void json_add_literal(struct json_stream *result, const char *fieldname,
const char *literal, int len)
{
json_add_member(result, fieldname, "%.*s", len, literal);
}
void json_add_string(struct json_stream *result, const char *fieldname, const char *value TAKES)
{
struct json_escaped *esc = json_partial_escape(NULL, value);
json_add_member(result, fieldname, "\"%s\"", esc->s);
tal_free(esc);
}
void json_add_bool(struct json_stream *result, const char *fieldname, bool value)
{
json_add_member(result, fieldname, value ? "true" : "false");
}
void json_add_null(struct json_stream *stream, const char *fieldname)
{
json_add_member(stream, fieldname, "null");
}
void json_add_hex(struct json_stream *result, const char *fieldname,
const void *data, size_t len)
{
char *hex = tal_arr(NULL, char, hex_str_size(len));
hex_encode(data, len, hex, hex_str_size(len));
json_add_string(result, fieldname, hex);
tal_free(hex);
}
void json_add_hex_talarr(struct json_stream *result,
const char *fieldname,
const tal_t *data)
{
json_add_hex(result, fieldname, data, tal_bytelen(data));
}
void json_add_escaped_string(struct json_stream *result, const char *fieldname,
const struct json_escaped *esc TAKES)
{
json_add_member(result, fieldname, "\"%s\"", esc->s);
if (taken(esc))
tal_free(esc);
}
void json_add_amount_msat(struct json_stream *result,
struct amount_msat msat,
const char *rawfieldname,
const char *msatfieldname)
{
json_add_u64(result, rawfieldname, msat.millisatoshis); /* Raw: low-level helper */
json_add_member(result, msatfieldname, "\"%s\"",
type_to_string(tmpctx, struct amount_msat, &msat));
}
void json_add_amount_sat(struct json_stream *result,
struct amount_sat sat,
const char *rawfieldname,
const char *msatfieldname)
{
struct amount_msat msat;
json_add_u64(result, rawfieldname, sat.satoshis); /* Raw: low-level helper */
if (amount_sat_to_msat(&msat, sat))
json_add_member(result, msatfieldname, "\"%s\"",
type_to_string(tmpctx, struct amount_msat, &msat));
}