2016-01-21 21:11:48 +01:00
|
|
|
/* Code for JSON_RPC API */
|
|
|
|
/* eg: { "method" : "dev-echo", "params" : [ "hello", "Arabella!" ], "id" : "1" } */
|
2017-08-28 18:04:01 +02:00
|
|
|
#include <arpa/inet.h>
|
2018-02-15 00:45:04 +01:00
|
|
|
#include <bitcoin/address.h>
|
|
|
|
#include <bitcoin/base58.h>
|
|
|
|
#include <bitcoin/script.h>
|
2016-01-21 21:11:48 +01:00
|
|
|
#include <ccan/array_size/array_size.h>
|
|
|
|
#include <ccan/err/err.h>
|
|
|
|
#include <ccan/io/io.h>
|
2016-01-21 21:15:27 +01:00
|
|
|
#include <ccan/str/hex/hex.h>
|
2016-01-21 21:11:48 +01:00
|
|
|
#include <ccan/tal/str/str.h>
|
2018-02-15 00:45:04 +01:00
|
|
|
#include <common/bech32.h>
|
2017-12-15 11:22:57 +01:00
|
|
|
#include <common/memleak.h>
|
2017-08-28 18:02:01 +02:00
|
|
|
#include <common/version.h>
|
2018-04-05 21:19:47 +02:00
|
|
|
#include <common/wallet_tx.h>
|
2017-10-23 06:17:38 +02:00
|
|
|
#include <common/wireaddr.h>
|
2016-01-21 21:11:48 +01:00
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
2017-08-28 18:04:01 +02:00
|
|
|
#include <lightningd/chaintopology.h>
|
2018-03-16 04:45:08 +01:00
|
|
|
#include <lightningd/json.h>
|
2018-10-19 03:17:48 +02:00
|
|
|
#include <lightningd/json_escaped.h>
|
2017-08-28 18:04:01 +02:00
|
|
|
#include <lightningd/jsonrpc.h>
|
2018-02-05 02:19:16 +01:00
|
|
|
#include <lightningd/jsonrpc_errors.h>
|
2017-08-28 18:04:01 +02:00
|
|
|
#include <lightningd/lightningd.h>
|
|
|
|
#include <lightningd/log.h>
|
2018-01-16 20:43:14 +01:00
|
|
|
#include <lightningd/options.h>
|
2018-07-20 03:14:02 +02:00
|
|
|
#include <lightningd/param.h>
|
2016-01-21 21:11:48 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
|
2017-09-28 05:31:47 +02:00
|
|
|
/* jcon and cmd have separate lifetimes: we detach them on either destruction */
|
2017-09-28 05:31:36 +02:00
|
|
|
static void destroy_jcon(struct json_connection *jcon)
|
2016-01-21 21:11:48 +01:00
|
|
|
{
|
2018-10-19 03:17:48 +02:00
|
|
|
if (jcon->command) {
|
2018-02-27 17:41:29 +01:00
|
|
|
log_debug(jcon->log, "Abandoning command");
|
2018-10-19 03:17:48 +02:00
|
|
|
jcon->command->jcon = NULL;
|
2016-01-21 21:11:48 +01:00
|
|
|
}
|
2018-02-16 03:53:00 +01:00
|
|
|
|
2017-09-28 05:31:36 +02:00
|
|
|
/* Make sure this happens last! */
|
|
|
|
tal_free(jcon->log);
|
2016-01-21 21:11:48 +01:00
|
|
|
}
|
|
|
|
|
2016-01-21 21:11:48 +01:00
|
|
|
static void json_help(struct command *cmd,
|
|
|
|
const char *buffer, const jsmntok_t *params);
|
2016-01-21 21:11:48 +01:00
|
|
|
|
|
|
|
static const struct json_command help_command = {
|
|
|
|
"help",
|
|
|
|
json_help,
|
2018-09-13 17:57:24 +02:00
|
|
|
"List available commands, or give verbose help on one {command}.",
|
2018-01-29 01:30:15 +01:00
|
|
|
|
|
|
|
.verbose = "help [command]\n"
|
|
|
|
"Without [command]:\n"
|
|
|
|
" Outputs an array of objects with 'command' and 'description'\n"
|
|
|
|
"With [command]:\n"
|
|
|
|
" Give a single object containing 'verbose', which completely describes\n"
|
|
|
|
" the command inputs and outputs."
|
2016-01-21 21:11:48 +01:00
|
|
|
};
|
2017-01-04 04:38:15 +01:00
|
|
|
AUTODATA(json_command, &help_command);
|
2016-01-21 21:11:48 +01:00
|
|
|
|
2016-01-21 21:11:48 +01:00
|
|
|
static void json_stop(struct command *cmd,
|
2018-02-21 16:06:07 +01:00
|
|
|
const char *buffer UNUSED, const jsmntok_t *params UNUSED)
|
2016-01-21 21:11:48 +01:00
|
|
|
{
|
2018-10-19 03:17:49 +02:00
|
|
|
struct json_stream *response;
|
2016-01-21 21:11:48 +01:00
|
|
|
|
2018-09-14 16:51:04 +02:00
|
|
|
if (!param(cmd, buffer, params, NULL))
|
|
|
|
return;
|
|
|
|
|
2016-01-21 21:11:48 +01:00
|
|
|
/* This can't have closed yet! */
|
|
|
|
cmd->jcon->stop = true;
|
2018-10-19 03:17:48 +02:00
|
|
|
response = json_stream_success(cmd);
|
2016-01-21 21:11:48 +01:00
|
|
|
json_add_string(response, NULL, "Shutting down");
|
2016-01-21 21:11:48 +01:00
|
|
|
command_success(cmd, response);
|
2016-01-21 21:11:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct json_command stop_command = {
|
|
|
|
"stop",
|
|
|
|
json_stop,
|
2018-01-22 09:55:07 +01:00
|
|
|
"Shut down the lightningd process"
|
2016-01-21 21:11:48 +01:00
|
|
|
};
|
2017-01-04 04:38:15 +01:00
|
|
|
AUTODATA(json_command, &stop_command);
|
2016-01-21 21:11:48 +01:00
|
|
|
|
2017-10-24 04:06:14 +02:00
|
|
|
#if DEVELOPER
|
2016-01-21 21:15:27 +01:00
|
|
|
static void json_rhash(struct command *cmd,
|
|
|
|
const char *buffer, const jsmntok_t *params)
|
|
|
|
{
|
2018-10-19 03:17:49 +02:00
|
|
|
struct json_stream *response;
|
2018-08-14 22:16:49 +02:00
|
|
|
struct sha256 *secret;
|
2016-01-21 21:15:27 +01:00
|
|
|
|
2018-07-20 03:14:02 +02:00
|
|
|
if (!param(cmd, buffer, params,
|
2018-08-15 17:16:02 +02:00
|
|
|
p_req("secret", json_tok_sha256, &secret),
|
2018-07-20 03:14:02 +02:00
|
|
|
NULL))
|
2016-01-21 21:15:27 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* Hash in place. */
|
2018-08-14 22:16:49 +02:00
|
|
|
sha256(secret, secret, sizeof(*secret));
|
2018-10-19 03:17:48 +02:00
|
|
|
response = json_stream_success(cmd);
|
2016-01-21 21:15:27 +01:00
|
|
|
json_object_start(response, NULL);
|
2018-08-14 22:16:49 +02:00
|
|
|
json_add_hex(response, "rhash", secret, sizeof(*secret));
|
2016-01-21 21:15:27 +01:00
|
|
|
json_object_end(response);
|
|
|
|
command_success(cmd, response);
|
|
|
|
}
|
|
|
|
|
2016-09-06 09:17:49 +02:00
|
|
|
static const struct json_command dev_rhash_command = {
|
2016-01-21 21:15:27 +01:00
|
|
|
"dev-rhash",
|
|
|
|
json_rhash,
|
2018-01-22 09:55:07 +01:00
|
|
|
"Show SHA256 of {secret}"
|
2016-01-21 21:15:27 +01:00
|
|
|
};
|
2017-01-04 04:38:15 +01:00
|
|
|
AUTODATA(json_command, &dev_rhash_command);
|
2016-01-21 21:15:27 +01:00
|
|
|
|
2018-02-21 16:06:07 +01:00
|
|
|
static void json_crash(struct command *cmd UNUSED,
|
|
|
|
const char *buffer UNUSED, const jsmntok_t *params UNUSED)
|
2016-01-21 21:15:28 +01:00
|
|
|
{
|
2018-09-14 16:51:04 +02:00
|
|
|
if (!param(cmd, buffer, params, NULL))
|
|
|
|
return;
|
|
|
|
|
2016-01-21 21:15:28 +01:00
|
|
|
fatal("Crash at user request");
|
|
|
|
}
|
|
|
|
|
2016-09-06 09:17:49 +02:00
|
|
|
static const struct json_command dev_crash_command = {
|
2016-01-21 21:15:28 +01:00
|
|
|
"dev-crash",
|
|
|
|
json_crash,
|
2018-01-22 09:55:07 +01:00
|
|
|
"Crash lightningd by calling fatal()"
|
2016-01-21 21:15:28 +01:00
|
|
|
};
|
2017-01-04 04:38:15 +01:00
|
|
|
AUTODATA(json_command, &dev_crash_command);
|
2017-10-24 04:06:14 +02:00
|
|
|
#endif /* DEVELOPER */
|
2016-01-21 21:15:28 +01:00
|
|
|
|
2016-09-12 20:07:07 +02:00
|
|
|
static void json_getinfo(struct command *cmd,
|
2018-02-21 16:06:07 +01:00
|
|
|
const char *buffer UNUSED, const jsmntok_t *params UNUSED)
|
2016-09-12 20:07:07 +02:00
|
|
|
{
|
2018-10-19 03:17:49 +02:00
|
|
|
struct json_stream *response;
|
2016-09-12 20:07:07 +02:00
|
|
|
|
2018-09-14 16:51:04 +02:00
|
|
|
if (!param(cmd, buffer, params, NULL))
|
|
|
|
return;
|
|
|
|
|
2018-10-19 03:17:48 +02:00
|
|
|
response = json_stream_success(cmd);
|
2016-09-12 20:07:07 +02:00
|
|
|
json_object_start(response, NULL);
|
2017-08-28 18:09:01 +02:00
|
|
|
json_add_pubkey(response, "id", &cmd->ld->id);
|
2018-06-27 09:22:35 +02:00
|
|
|
json_add_string(response, "alias", (const char *)cmd->ld->alias);
|
2018-07-28 07:53:33 +02:00
|
|
|
json_add_hex_talarr(response, "color", cmd->ld->rgb);
|
2018-05-07 06:28:12 +02:00
|
|
|
if (cmd->ld->listen) {
|
|
|
|
/* These are the addresses we're announcing */
|
2017-10-23 06:15:38 +02:00
|
|
|
json_array_start(response, "address");
|
2018-05-07 06:29:22 +02:00
|
|
|
for (size_t i = 0; i < tal_count(cmd->ld->announcable); i++)
|
|
|
|
json_add_address(response, NULL, cmd->ld->announcable+i);
|
2017-10-23 06:15:38 +02:00
|
|
|
json_array_end(response);
|
2018-05-07 06:28:12 +02:00
|
|
|
|
2018-05-07 06:29:22 +02:00
|
|
|
/* This is what we're actually bound to. */
|
|
|
|
json_array_start(response, "binding");
|
|
|
|
for (size_t i = 0; i < tal_count(cmd->ld->binding); i++)
|
|
|
|
json_add_address_internal(response, NULL,
|
|
|
|
cmd->ld->binding+i);
|
|
|
|
json_array_end(response);
|
2017-10-23 06:15:38 +02:00
|
|
|
}
|
2016-09-13 21:52:41 +02:00
|
|
|
json_add_string(response, "version", version());
|
2017-08-28 18:09:01 +02:00
|
|
|
json_add_num(response, "blockheight", get_block_height(cmd->ld->topology));
|
2018-01-18 21:21:44 +01:00
|
|
|
json_add_string(response, "network", get_chainparams(cmd->ld)->network_name);
|
2018-10-19 06:19:52 +02:00
|
|
|
json_add_u64(response, "msatoshi_fees_collected",
|
|
|
|
wallet_total_forward_fees(cmd->ld->wallet));
|
2016-09-12 20:07:07 +02:00
|
|
|
json_object_end(response);
|
|
|
|
command_success(cmd, response);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct json_command getinfo_command = {
|
|
|
|
"getinfo",
|
|
|
|
json_getinfo,
|
2018-01-22 09:55:07 +01:00
|
|
|
"Show information about this node"
|
2016-09-12 20:07:07 +02:00
|
|
|
};
|
2017-01-04 04:38:15 +01:00
|
|
|
AUTODATA(json_command, &getinfo_command);
|
2016-09-12 20:07:07 +02:00
|
|
|
|
2017-01-04 04:38:15 +01:00
|
|
|
static size_t num_cmdlist;
|
|
|
|
|
|
|
|
static struct json_command **get_cmdlist(void)
|
|
|
|
{
|
|
|
|
static struct json_command **cmdlist;
|
|
|
|
if (!cmdlist)
|
|
|
|
cmdlist = autodata_get(json_command, &num_cmdlist);
|
|
|
|
|
|
|
|
return cmdlist;
|
|
|
|
}
|
2016-01-21 21:11:48 +01:00
|
|
|
|
2018-10-09 03:09:54 +02:00
|
|
|
static void json_add_help_command(struct command *cmd,
|
2018-10-19 03:17:49 +02:00
|
|
|
struct json_stream *response,
|
2018-10-09 03:09:54 +02:00
|
|
|
struct json_command *json_command)
|
|
|
|
{
|
|
|
|
char *usage;
|
|
|
|
cmd->mode = CMD_USAGE;
|
|
|
|
json_command->dispatch(cmd, NULL, NULL);
|
|
|
|
usage = tal_fmt(cmd, "%s %s", json_command->name, cmd->usage);
|
|
|
|
|
|
|
|
json_object_start(response, NULL);
|
|
|
|
|
|
|
|
json_add_string(response, "command", usage);
|
|
|
|
json_add_string(response, "description", json_command->description);
|
|
|
|
|
|
|
|
if (!json_command->verbose) {
|
|
|
|
json_add_string(response, "verbose",
|
|
|
|
"HELP! Please contribute"
|
|
|
|
" a description for this"
|
|
|
|
" json_command!");
|
|
|
|
} else {
|
|
|
|
struct json_escaped *esc;
|
|
|
|
|
|
|
|
esc = json_escape(NULL, json_command->verbose);
|
|
|
|
json_add_escaped_string(response, "verbose", take(esc));
|
|
|
|
}
|
|
|
|
|
|
|
|
json_object_end(response);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-01-21 21:11:48 +01:00
|
|
|
static void json_help(struct command *cmd,
|
|
|
|
const char *buffer, const jsmntok_t *params)
|
2016-01-21 21:11:48 +01:00
|
|
|
{
|
|
|
|
unsigned int i;
|
2018-10-19 03:17:49 +02:00
|
|
|
struct json_stream *response;
|
2017-01-04 04:38:15 +01:00
|
|
|
struct json_command **cmdlist = get_cmdlist();
|
2018-07-20 03:14:02 +02:00
|
|
|
const jsmntok_t *cmdtok;
|
2018-01-29 01:30:15 +01:00
|
|
|
|
2018-07-20 03:14:02 +02:00
|
|
|
if (!param(cmd, buffer, params,
|
2018-08-15 17:16:02 +02:00
|
|
|
p_opt("command", json_tok_tok, &cmdtok),
|
2018-07-20 03:14:02 +02:00
|
|
|
NULL))
|
2018-01-29 01:30:15 +01:00
|
|
|
return;
|
2016-01-21 21:11:48 +01:00
|
|
|
|
2018-01-29 01:30:15 +01:00
|
|
|
if (cmdtok) {
|
|
|
|
for (i = 0; i < num_cmdlist; i++) {
|
|
|
|
if (json_tok_streq(buffer, cmdtok, cmdlist[i]->name)) {
|
2018-10-19 03:17:48 +02:00
|
|
|
response = json_stream_success(cmd);
|
2018-10-09 03:09:54 +02:00
|
|
|
json_add_help_command(cmd, response, cmdlist[i]);
|
2018-01-29 01:30:15 +01:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
2018-05-24 23:40:18 +02:00
|
|
|
command_fail(cmd, JSONRPC2_METHOD_NOT_FOUND,
|
|
|
|
"Unknown command '%.*s'",
|
2018-01-29 01:30:15 +01:00
|
|
|
cmdtok->end - cmdtok->start,
|
|
|
|
buffer + cmdtok->start);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-10-19 03:17:48 +02:00
|
|
|
response = json_stream_success(cmd);
|
2018-10-09 03:09:54 +02:00
|
|
|
json_object_start(response, NULL);
|
2018-01-16 21:29:36 +01:00
|
|
|
json_array_start(response, "help");
|
2017-01-04 04:38:15 +01:00
|
|
|
for (i = 0; i < num_cmdlist; i++) {
|
2018-10-09 03:09:54 +02:00
|
|
|
json_add_help_command(cmd, response, cmdlist[i]);
|
2016-01-21 21:11:48 +01:00
|
|
|
}
|
|
|
|
json_array_end(response);
|
2018-10-09 03:09:54 +02:00
|
|
|
json_object_end(response);
|
2018-01-29 01:30:15 +01:00
|
|
|
|
|
|
|
done:
|
2016-01-21 21:11:48 +01:00
|
|
|
command_success(cmd, response);
|
2016-01-21 21:11:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct json_command *find_cmd(const char *buffer,
|
|
|
|
const jsmntok_t *tok)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
2017-01-04 04:38:15 +01:00
|
|
|
struct json_command **cmdlist = get_cmdlist();
|
2016-01-21 21:11:48 +01:00
|
|
|
|
|
|
|
/* cmdlist[i]->name can be NULL in test code. */
|
2017-01-04 04:38:15 +01:00
|
|
|
for (i = 0; i < num_cmdlist; i++)
|
2016-01-21 21:11:48 +01:00
|
|
|
if (cmdlist[i]->name
|
|
|
|
&& json_tok_streq(buffer, tok, cmdlist[i]->name))
|
|
|
|
return cmdlist[i];
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-10-19 03:17:49 +02:00
|
|
|
struct json_stream *null_response(struct command *cmd)
|
2016-06-28 23:19:21 +02:00
|
|
|
{
|
2018-10-19 03:17:49 +02:00
|
|
|
struct json_stream *response;
|
2016-11-11 00:02:04 +01:00
|
|
|
|
2018-10-19 03:17:48 +02:00
|
|
|
response = json_stream_success(cmd);
|
2016-06-28 23:19:21 +02:00
|
|
|
json_object_start(response, NULL);
|
|
|
|
json_object_end(response);
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
2018-10-19 03:17:48 +02:00
|
|
|
/* This can be called directly on shutdown, even with unfinished cmd */
|
|
|
|
static void destroy_command(struct command *cmd)
|
2016-01-21 21:11:48 +01:00
|
|
|
{
|
2018-10-19 03:17:48 +02:00
|
|
|
if (!cmd->jcon) {
|
2018-02-27 17:41:29 +01:00
|
|
|
log_debug(cmd->ld->log,
|
2016-01-21 21:11:48 +01:00
|
|
|
"Command returned result after jcon close");
|
|
|
|
return;
|
|
|
|
}
|
2018-10-19 03:17:48 +02:00
|
|
|
|
|
|
|
assert(cmd->jcon->command == cmd);
|
|
|
|
cmd->jcon->command = NULL;
|
2016-01-21 21:11:48 +01:00
|
|
|
}
|
|
|
|
|
2018-10-19 03:17:49 +02:00
|
|
|
void command_success(struct command *cmd, struct json_stream *result)
|
2016-01-21 21:11:48 +01:00
|
|
|
{
|
2018-10-19 03:17:48 +02:00
|
|
|
assert(cmd);
|
|
|
|
assert(cmd->have_json_stream);
|
2018-11-20 02:46:32 +01:00
|
|
|
json_stream_append(result, " }\n\n");
|
|
|
|
json_stream_close(result, cmd);
|
2018-10-19 03:17:48 +02:00
|
|
|
if (cmd->ok)
|
|
|
|
*(cmd->ok) = true;
|
2018-11-20 02:46:32 +01:00
|
|
|
|
|
|
|
/* If we have a jcon, it will free result for us. */
|
|
|
|
if (cmd->jcon)
|
|
|
|
tal_steal(cmd->jcon, result);
|
|
|
|
|
2018-10-19 03:17:48 +02:00
|
|
|
tal_free(cmd);
|
2016-01-21 21:11:48 +01:00
|
|
|
}
|
2018-05-24 23:40:18 +02:00
|
|
|
|
2018-10-19 03:17:49 +02:00
|
|
|
void command_failed(struct command *cmd, struct json_stream *result)
|
2018-01-27 03:05:49 +01:00
|
|
|
{
|
2018-10-19 03:17:48 +02:00
|
|
|
assert(cmd->have_json_stream);
|
|
|
|
/* Have to close error */
|
2018-11-20 02:46:32 +01:00
|
|
|
json_stream_append(result, " } }\n\n");
|
|
|
|
json_stream_close(result, cmd);
|
2018-10-19 03:17:48 +02:00
|
|
|
if (cmd->ok)
|
|
|
|
*(cmd->ok) = false;
|
2018-11-20 02:46:32 +01:00
|
|
|
/* If we have a jcon, it will free result for us. */
|
|
|
|
if (cmd->jcon)
|
|
|
|
tal_steal(cmd->jcon, result);
|
|
|
|
|
2018-10-19 03:17:48 +02:00
|
|
|
tal_free(cmd);
|
2018-01-27 03:05:49 +01:00
|
|
|
}
|
2018-05-24 23:40:18 +02:00
|
|
|
|
2018-10-19 03:17:48 +02:00
|
|
|
void PRINTF_FMT(3, 4) command_fail(struct command *cmd, int code,
|
|
|
|
const char *fmt, ...)
|
2018-01-27 03:05:49 +01:00
|
|
|
{
|
2018-10-19 03:17:48 +02:00
|
|
|
const char *errmsg;
|
2018-10-19 03:17:49 +02:00
|
|
|
struct json_stream *r;
|
2018-01-27 03:05:49 +01:00
|
|
|
va_list ap;
|
2018-10-19 03:17:48 +02:00
|
|
|
|
2018-01-27 03:05:49 +01:00
|
|
|
va_start(ap, fmt);
|
2018-10-19 03:17:48 +02:00
|
|
|
errmsg = tal_vfmt(cmd, fmt, ap);
|
2018-01-27 03:05:49 +01:00
|
|
|
va_end(ap);
|
2018-10-19 03:17:48 +02:00
|
|
|
r = json_stream_fail_nodata(cmd, code, errmsg);
|
|
|
|
|
|
|
|
command_failed(cmd, r);
|
2018-01-27 03:05:49 +01:00
|
|
|
}
|
2016-01-21 21:11:48 +01:00
|
|
|
|
2017-12-15 11:15:54 +01:00
|
|
|
void command_still_pending(struct command *cmd)
|
|
|
|
{
|
2017-12-15 11:29:21 +01:00
|
|
|
notleak_with_children(cmd);
|
|
|
|
notleak(cmd->jcon);
|
2017-12-15 11:15:54 +01:00
|
|
|
cmd->pending = true;
|
|
|
|
}
|
|
|
|
|
2016-01-21 21:11:48 +01:00
|
|
|
static void json_command_malformed(struct json_connection *jcon,
|
|
|
|
const char *id,
|
|
|
|
const char *error)
|
|
|
|
{
|
2018-11-20 02:46:32 +01:00
|
|
|
/* FIXME: We only allow one command at a time */
|
|
|
|
assert(!jcon->js);
|
|
|
|
jcon->js = new_json_stream(jcon, NULL);
|
|
|
|
io_wake(jcon);
|
|
|
|
|
|
|
|
json_stream_append_fmt(jcon->js,
|
|
|
|
"{ \"jsonrpc\": \"2.0\", \"id\" : %s,"
|
|
|
|
" \"error\" : "
|
|
|
|
"{ \"code\" : %d,"
|
|
|
|
" \"message\" : \"%s\" } }\n\n",
|
|
|
|
id, JSONRPC2_INVALID_REQUEST, error);
|
|
|
|
|
|
|
|
json_stream_close(jcon->js, NULL);
|
2016-01-21 21:11:48 +01:00
|
|
|
}
|
|
|
|
|
2018-11-20 02:46:32 +01:00
|
|
|
static struct json_stream *attach_json_stream(struct command *cmd)
|
|
|
|
{
|
|
|
|
struct json_stream *js = new_json_stream(cmd, cmd);
|
|
|
|
|
|
|
|
/* If they still care about the result, wake them */
|
|
|
|
if (cmd->jcon) {
|
|
|
|
/* FIXME: We only allow one command at a time */
|
|
|
|
assert(!cmd->jcon->js);
|
|
|
|
cmd->jcon->js = js;
|
|
|
|
io_wake(cmd->jcon);
|
|
|
|
}
|
|
|
|
assert(!cmd->have_json_stream);
|
|
|
|
cmd->have_json_stream = true;
|
|
|
|
return js;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct json_stream *json_start(struct command *cmd)
|
|
|
|
{
|
|
|
|
struct json_stream *js = attach_json_stream(cmd);
|
|
|
|
|
|
|
|
json_stream_append_fmt(js, "{ \"jsonrpc\": \"2.0\", \"id\" : %s, ",
|
|
|
|
cmd->id);
|
|
|
|
return js;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct json_stream *json_stream_success(struct command *cmd)
|
|
|
|
{
|
|
|
|
struct json_stream *r = json_start(cmd);
|
|
|
|
json_stream_append(r, "\"result\" : ");
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct json_stream *json_stream_fail_nodata(struct command *cmd,
|
|
|
|
int code,
|
|
|
|
const char *errmsg)
|
|
|
|
{
|
|
|
|
struct json_stream *r = json_start(cmd);
|
|
|
|
|
|
|
|
assert(code);
|
|
|
|
assert(errmsg);
|
|
|
|
|
|
|
|
json_stream_append_fmt(r, " \"error\" : "
|
|
|
|
"{ \"code\" : %d,"
|
|
|
|
" \"message\" : \"%s\"", code, errmsg);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct json_stream *json_stream_fail(struct command *cmd,
|
|
|
|
int code,
|
|
|
|
const char *errmsg)
|
|
|
|
{
|
|
|
|
struct json_stream *r = json_stream_fail_nodata(cmd, code, errmsg);
|
|
|
|
|
|
|
|
json_stream_append(r, ", \"data\" : ");
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2018-10-19 03:17:48 +02:00
|
|
|
/* Returns true if command already completed. */
|
|
|
|
static bool parse_request(struct json_connection *jcon, const jsmntok_t tok[])
|
2016-01-21 21:11:48 +01:00
|
|
|
{
|
|
|
|
const jsmntok_t *method, *id, *params;
|
2018-02-16 03:53:00 +01:00
|
|
|
struct command *c;
|
2016-01-21 21:11:48 +01:00
|
|
|
|
|
|
|
if (tok[0].type != JSMN_OBJECT) {
|
2016-01-21 21:11:48 +01:00
|
|
|
json_command_malformed(jcon, "null",
|
|
|
|
"Expected {} for json command");
|
2018-10-19 03:17:48 +02:00
|
|
|
return true;
|
2016-01-21 21:11:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
method = json_get_member(jcon->buffer, tok, "method");
|
|
|
|
params = json_get_member(jcon->buffer, tok, "params");
|
|
|
|
id = json_get_member(jcon->buffer, tok, "id");
|
|
|
|
|
2016-01-21 21:11:48 +01:00
|
|
|
if (!id) {
|
|
|
|
json_command_malformed(jcon, "null", "No id");
|
2018-10-19 03:17:48 +02:00
|
|
|
return true;
|
2016-01-21 21:11:48 +01:00
|
|
|
}
|
|
|
|
if (id->type != JSMN_STRING && id->type != JSMN_PRIMITIVE) {
|
2016-01-21 21:11:48 +01:00
|
|
|
json_command_malformed(jcon, "null",
|
|
|
|
"Expected string/primitive for id");
|
2018-10-19 03:17:48 +02:00
|
|
|
return true;
|
2016-01-21 21:11:48 +01:00
|
|
|
}
|
|
|
|
|
2017-12-07 23:59:39 +01:00
|
|
|
/* This is a convenient tal parent for duration of command
|
2016-01-21 21:11:48 +01:00
|
|
|
* (which may outlive the conn!). */
|
2018-07-24 22:32:58 +02:00
|
|
|
c = tal(jcon->ld->rpc_listener, struct command);
|
2018-02-16 03:53:00 +01:00
|
|
|
c->jcon = jcon;
|
|
|
|
c->ld = jcon->ld;
|
|
|
|
c->pending = false;
|
2018-10-19 03:17:48 +02:00
|
|
|
c->have_json_stream = false;
|
2018-02-16 03:53:00 +01:00
|
|
|
c->id = tal_strndup(c,
|
|
|
|
json_tok_contents(jcon->buffer, id),
|
|
|
|
json_tok_len(id));
|
2018-09-12 17:56:39 +02:00
|
|
|
c->mode = CMD_NORMAL;
|
2018-09-15 19:12:15 +02:00
|
|
|
c->ok = NULL;
|
2018-10-19 03:17:48 +02:00
|
|
|
jcon->command = c;
|
2018-10-19 03:17:48 +02:00
|
|
|
tal_add_destructor(c, destroy_command);
|
|
|
|
|
2016-01-21 21:11:48 +01:00
|
|
|
if (!method || !params) {
|
2018-05-24 23:40:18 +02:00
|
|
|
command_fail(c, JSONRPC2_INVALID_REQUEST,
|
|
|
|
method ? "No params" : "No method");
|
2018-10-19 03:17:48 +02:00
|
|
|
return true;
|
2016-01-21 21:11:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (method->type != JSMN_STRING) {
|
2018-05-24 23:40:18 +02:00
|
|
|
command_fail(c, JSONRPC2_INVALID_REQUEST,
|
|
|
|
"Expected string for method");
|
2018-10-19 03:17:48 +02:00
|
|
|
return true;
|
2016-01-21 21:11:48 +01:00
|
|
|
}
|
|
|
|
|
2018-07-26 23:19:37 +02:00
|
|
|
c->json_cmd = find_cmd(jcon->buffer, method);
|
|
|
|
if (!c->json_cmd) {
|
2018-05-24 23:40:18 +02:00
|
|
|
command_fail(c, JSONRPC2_METHOD_NOT_FOUND,
|
|
|
|
"Unknown command '%.*s'",
|
|
|
|
method->end - method->start,
|
|
|
|
jcon->buffer + method->start);
|
2018-10-19 03:17:48 +02:00
|
|
|
return true;
|
2016-01-21 21:11:48 +01:00
|
|
|
}
|
2018-07-26 23:19:37 +02:00
|
|
|
if (c->json_cmd->deprecated && !deprecated_apis) {
|
2018-05-24 23:40:18 +02:00
|
|
|
command_fail(c, JSONRPC2_METHOD_NOT_FOUND,
|
|
|
|
"Command '%.*s' is deprecated",
|
|
|
|
method->end - method->start,
|
|
|
|
jcon->buffer + method->start);
|
2018-10-19 03:17:48 +02:00
|
|
|
return true;
|
2018-01-16 20:43:14 +01:00
|
|
|
}
|
2016-01-21 21:11:48 +01:00
|
|
|
|
2017-11-01 02:10:48 +01:00
|
|
|
db_begin_transaction(jcon->ld->wallet->db);
|
2018-07-26 23:19:37 +02:00
|
|
|
c->json_cmd->dispatch(c, jcon->buffer, params);
|
2017-11-01 02:10:48 +01:00
|
|
|
db_commit_transaction(jcon->ld->wallet->db);
|
2017-12-15 11:15:54 +01:00
|
|
|
|
|
|
|
/* If they didn't complete it, they must call command_still_pending */
|
2018-10-19 03:17:48 +02:00
|
|
|
if (jcon->command == c)
|
2018-02-16 03:53:00 +01:00
|
|
|
assert(c->pending);
|
2018-10-19 03:17:48 +02:00
|
|
|
|
|
|
|
return jcon->command == NULL;
|
2016-01-21 21:11:48 +01:00
|
|
|
}
|
|
|
|
|
2018-10-19 03:17:48 +02:00
|
|
|
/* Mutual recursion */
|
2018-11-20 02:46:32 +01:00
|
|
|
static struct io_plan *stream_out_complete(struct io_conn *conn,
|
|
|
|
struct json_connection *jcon);
|
2018-09-26 20:57:43 +02:00
|
|
|
|
2018-11-20 02:46:32 +01:00
|
|
|
static struct io_plan *start_json_stream(struct io_conn *conn,
|
|
|
|
struct json_connection *jcon)
|
2016-01-21 21:11:48 +01:00
|
|
|
{
|
2018-11-20 02:46:32 +01:00
|
|
|
/* If something has created an output buffer, start streaming. */
|
|
|
|
if (jcon->js)
|
|
|
|
return json_stream_output(jcon->js, conn,
|
|
|
|
stream_out_complete, jcon);
|
2016-01-21 21:11:48 +01:00
|
|
|
|
2018-11-20 02:46:32 +01:00
|
|
|
/* Wait for attach_json_stream */
|
|
|
|
return io_out_wait(conn, jcon, start_json_stream, jcon);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Command has completed writing, and we've written it all out to conn. */
|
|
|
|
static struct io_plan *stream_out_complete(struct io_conn *conn,
|
|
|
|
struct json_connection *jcon)
|
|
|
|
{
|
|
|
|
/* Free up the json_stream for next command. */
|
|
|
|
assert(jcon->js);
|
|
|
|
jcon->js = tal_free(jcon->js);
|
2018-10-19 03:17:48 +02:00
|
|
|
|
2018-10-19 03:17:48 +02:00
|
|
|
if (jcon->stop) {
|
|
|
|
log_unusual(jcon->log, "JSON-RPC shutdown");
|
|
|
|
/* Return us to toplevel lightningd.c */
|
|
|
|
io_break(jcon->ld);
|
|
|
|
return io_close(conn);
|
2016-01-21 21:11:48 +01:00
|
|
|
}
|
|
|
|
|
2018-11-20 02:46:32 +01:00
|
|
|
/* Tell reader to run next command. */
|
|
|
|
io_wake(conn);
|
2016-01-21 21:11:48 +01:00
|
|
|
|
2018-10-19 03:17:48 +02:00
|
|
|
/* Wait for more output. */
|
2018-11-20 02:46:32 +01:00
|
|
|
return start_json_stream(conn, jcon);
|
2018-09-26 20:57:43 +02:00
|
|
|
}
|
|
|
|
|
2016-01-21 21:11:48 +01:00
|
|
|
static struct io_plan *read_json(struct io_conn *conn,
|
|
|
|
struct json_connection *jcon)
|
|
|
|
{
|
|
|
|
jsmntok_t *toks;
|
2018-10-19 03:17:48 +02:00
|
|
|
bool valid, completed;
|
2016-01-21 21:11:48 +01:00
|
|
|
|
2018-10-19 03:17:48 +02:00
|
|
|
if (jcon->len_read)
|
|
|
|
log_io(jcon->log, LOG_IO_IN, "",
|
|
|
|
jcon->buffer + jcon->used, jcon->len_read);
|
2016-01-21 21:11:48 +01:00
|
|
|
|
|
|
|
/* Resize larger if we're full. */
|
|
|
|
jcon->used += jcon->len_read;
|
|
|
|
if (jcon->used == tal_count(jcon->buffer))
|
|
|
|
tal_resize(&jcon->buffer, jcon->used * 2);
|
|
|
|
|
2018-11-20 02:46:32 +01:00
|
|
|
/* We wait for pending output to be consumed, to avoid DoS */
|
|
|
|
if (jcon->js) {
|
|
|
|
jcon->len_read = 0;
|
|
|
|
return io_wait(conn, conn, read_json, jcon);
|
|
|
|
}
|
|
|
|
|
2016-01-21 21:11:48 +01:00
|
|
|
toks = json_parse_input(jcon->buffer, jcon->used, &valid);
|
|
|
|
if (!toks) {
|
|
|
|
if (!valid) {
|
2018-10-19 03:17:48 +02:00
|
|
|
log_unusual(jcon->log,
|
2016-01-21 21:11:48 +01:00
|
|
|
"Invalid token in json input: '%.*s'",
|
|
|
|
(int)jcon->used, jcon->buffer);
|
2018-02-08 21:46:34 +01:00
|
|
|
json_command_malformed(
|
2018-02-28 11:01:09 +01:00
|
|
|
jcon, "null",
|
2018-02-08 21:46:34 +01:00
|
|
|
"Invalid token in json input");
|
|
|
|
return io_halfclose(conn);
|
2016-01-21 21:11:48 +01:00
|
|
|
}
|
|
|
|
/* We need more. */
|
|
|
|
goto read_more;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Empty buffer? (eg. just whitespace). */
|
|
|
|
if (tal_count(toks) == 1) {
|
|
|
|
jcon->used = 0;
|
|
|
|
goto read_more;
|
|
|
|
}
|
|
|
|
|
2018-10-19 03:17:48 +02:00
|
|
|
completed = parse_request(jcon, toks);
|
2016-01-21 21:11:48 +01:00
|
|
|
|
|
|
|
/* Remove first {}. */
|
|
|
|
memmove(jcon->buffer, jcon->buffer + toks[0].end,
|
|
|
|
tal_count(jcon->buffer) - toks[0].end);
|
|
|
|
jcon->used -= toks[0].end;
|
|
|
|
|
2018-10-19 03:17:48 +02:00
|
|
|
/* If we haven't completed, wait for cmd completion. */
|
2018-10-19 03:17:48 +02:00
|
|
|
jcon->len_read = 0;
|
2018-10-19 03:17:48 +02:00
|
|
|
if (!completed) {
|
|
|
|
tal_free(toks);
|
|
|
|
return io_wait(conn, conn, read_json, jcon);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we have more to process, try again. FIXME: this still gets
|
|
|
|
* first priority in io_loop, so can starve others. Hack would be
|
|
|
|
* a (non-zero) timer, but better would be to have io_loop avoid
|
|
|
|
* such livelock */
|
|
|
|
if (jcon->used) {
|
|
|
|
tal_free(toks);
|
|
|
|
return io_always(conn, read_json, jcon);
|
|
|
|
}
|
2016-01-21 21:11:48 +01:00
|
|
|
|
|
|
|
read_more:
|
|
|
|
tal_free(toks);
|
|
|
|
return io_read_partial(conn, jcon->buffer + jcon->used,
|
|
|
|
tal_count(jcon->buffer) - jcon->used,
|
|
|
|
&jcon->len_read, read_json, jcon);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct io_plan *jcon_connected(struct io_conn *conn,
|
2017-08-28 18:09:01 +02:00
|
|
|
struct lightningd *ld)
|
2016-01-21 21:11:48 +01:00
|
|
|
{
|
|
|
|
struct json_connection *jcon;
|
|
|
|
|
2017-09-28 05:31:36 +02:00
|
|
|
jcon = tal(conn, struct json_connection);
|
2018-10-19 03:17:48 +02:00
|
|
|
jcon->conn = conn;
|
2017-08-28 18:09:01 +02:00
|
|
|
jcon->ld = ld;
|
2016-01-21 21:11:48 +01:00
|
|
|
jcon->used = 0;
|
2016-01-21 21:11:48 +01:00
|
|
|
jcon->buffer = tal_arr(jcon, char, 64);
|
2016-01-21 21:11:48 +01:00
|
|
|
jcon->stop = false;
|
2018-11-20 02:46:32 +01:00
|
|
|
jcon->js = NULL;
|
2018-10-19 03:17:48 +02:00
|
|
|
jcon->len_read = 0;
|
|
|
|
jcon->command = NULL;
|
2018-02-16 03:53:00 +01:00
|
|
|
|
2017-09-28 05:31:36 +02:00
|
|
|
/* We want to log on destruction, so we free this in destructor. */
|
|
|
|
jcon->log = new_log(ld->log_book, ld->log_book, "%sjcon fd %i:",
|
2017-08-28 18:09:01 +02:00
|
|
|
log_prefix(ld->log), io_conn_fd(conn));
|
2016-01-21 21:11:48 +01:00
|
|
|
|
2017-09-28 05:31:36 +02:00
|
|
|
tal_add_destructor(jcon, destroy_jcon);
|
2016-01-21 21:11:48 +01:00
|
|
|
|
2018-10-19 03:17:48 +02:00
|
|
|
/* Note that write_json and read_json alternate manually, by waking
|
|
|
|
* each other. It would be simpler to not use a duplex io, and have
|
|
|
|
* read_json parse one command, then io_wait() for command completion
|
|
|
|
* and go to write_json.
|
|
|
|
*
|
|
|
|
* However, if we ever have notifications, this neat cmd-response
|
|
|
|
* pattern would break down, so we use this trick. */
|
2016-01-21 21:11:48 +01:00
|
|
|
return io_duplex(conn,
|
2018-10-19 03:17:48 +02:00
|
|
|
read_json(conn, jcon),
|
2018-11-20 02:46:32 +01:00
|
|
|
start_json_stream(conn, jcon));
|
2016-01-21 21:11:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct io_plan *incoming_jcon_connected(struct io_conn *conn,
|
2017-08-28 18:09:01 +02:00
|
|
|
struct lightningd *ld)
|
2016-01-21 21:11:48 +01:00
|
|
|
{
|
2017-12-15 11:22:57 +01:00
|
|
|
/* Lifetime of JSON conn is limited to fd connect time. */
|
|
|
|
return jcon_connected(notleak(conn), ld);
|
2016-01-21 21:11:48 +01:00
|
|
|
}
|
|
|
|
|
2017-08-28 18:09:01 +02:00
|
|
|
void setup_jsonrpc(struct lightningd *ld, const char *rpc_filename)
|
2016-01-21 21:11:48 +01:00
|
|
|
{
|
|
|
|
struct sockaddr_un addr;
|
|
|
|
int fd, old_umask;
|
|
|
|
|
|
|
|
if (streq(rpc_filename, "/dev/tty")) {
|
|
|
|
fd = open(rpc_filename, O_RDWR);
|
|
|
|
if (fd == -1)
|
|
|
|
err(1, "Opening %s", rpc_filename);
|
2017-12-15 11:22:57 +01:00
|
|
|
/* Technically this is a leak, but there's only one */
|
|
|
|
notleak(io_new_conn(ld, fd, jcon_connected, ld));
|
2016-01-21 21:11:48 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
2018-01-08 21:09:01 +01:00
|
|
|
if (fd < 0) {
|
|
|
|
errx(1, "domain socket creation failed");
|
|
|
|
}
|
2016-01-21 21:11:48 +01:00
|
|
|
if (strlen(rpc_filename) + 1 > sizeof(addr.sun_path))
|
|
|
|
errx(1, "rpc filename '%s' too long", rpc_filename);
|
|
|
|
strcpy(addr.sun_path, rpc_filename);
|
|
|
|
addr.sun_family = AF_UNIX;
|
|
|
|
|
|
|
|
/* Of course, this is racy! */
|
|
|
|
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == 0)
|
|
|
|
errx(1, "rpc filename '%s' in use", rpc_filename);
|
|
|
|
unlink(rpc_filename);
|
|
|
|
|
|
|
|
/* This file is only rw by us! */
|
|
|
|
old_umask = umask(0177);
|
|
|
|
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)))
|
|
|
|
err(1, "Binding rpc socket to '%s'", rpc_filename);
|
|
|
|
umask(old_umask);
|
|
|
|
|
|
|
|
if (listen(fd, 1) != 0)
|
|
|
|
err(1, "Listening on '%s'", rpc_filename);
|
|
|
|
|
gossipd: rewrite to do the handshake internally.
Now the flow is much simpler from a lightningd POV:
1. If we want to connect to a peer, just send gossipd `gossipctl_reach_peer`.
2. Every new peer, gossipd hands up to lightningd, with global/local features
and the peer fd and a gossip fd using `gossip_peer_connected`
3. If lightningd doesn't want it, it just hands the peerfd and global/local
features back to gossipd using `gossipctl_handle_peer`
4. If a peer sends a non-gossip msg (eg `open_channel`) the gossipd sends
it up using `gossip_peer_nongossip`.
5. If lightningd wants to fund a channel, it simply calls `release_channel`.
Notes:
* There's no more "unique_id": we use the peer id.
* For the moment, we don't ask gossipd when we're told to list peers, so
connected peers without a channel don't appear in the JSON getpeers API.
* We add a `gossipctl_peer_addrhint` for the moment, so you can connect to
a specific ip/port, but using other sources is a TODO.
* We now (correctly) only give up on reaching a peer after we exchange init
messages, which changes the test_disconnect case.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2017-10-11 12:09:49 +02:00
|
|
|
log_debug(ld->log, "Listening on '%s'", rpc_filename);
|
2018-07-24 22:32:58 +02:00
|
|
|
ld->rpc_listener = io_new_listener(ld->rpc_filename, fd, incoming_jcon_connected, ld);
|
2016-01-21 21:11:48 +01:00
|
|
|
}
|
2018-02-15 00:45:04 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* segwit_addr_net_decode - Try to decode a Bech32 address and detect
|
|
|
|
* testnet/mainnet/regtest
|
|
|
|
*
|
|
|
|
* This processes the address and returns a string if it is a Bech32
|
|
|
|
* address specified by BIP173. The string is set whether it is
|
|
|
|
* testnet ("tb"), mainnet ("bc"), or 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 char *network[] = { "bc", "tb", "bcrt" };
|
|
|
|
for (int i = 0; i < sizeof(network) / sizeof(*network); ++i) {
|
|
|
|
if (segwit_addr_decode(witness_version,
|
|
|
|
witness_program, witness_program_len,
|
|
|
|
network[i], addrz))
|
|
|
|
return network[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-02-24 15:59:39 +01:00
|
|
|
enum address_parse_result
|
|
|
|
json_tok_address_scriptpubkey(const tal_t *cxt,
|
|
|
|
const struct chainparams *chainparams,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *tok, const u8 **scriptpubkey)
|
2018-02-15 00:45:04 +01:00
|
|
|
{
|
|
|
|
struct bitcoin_address p2pkh_destination;
|
|
|
|
struct ripemd160 p2sh_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 *bip173;
|
2018-02-24 15:59:39 +01:00
|
|
|
|
|
|
|
bool parsed;
|
|
|
|
bool right_network;
|
2018-02-15 00:45:04 +01:00
|
|
|
bool testnet;
|
|
|
|
|
2018-02-24 15:59:39 +01:00
|
|
|
parsed = false;
|
2018-02-15 00:45:04 +01:00
|
|
|
if (bitcoin_from_base58(&testnet, &p2pkh_destination,
|
|
|
|
buffer + tok->start, tok->end - tok->start)) {
|
|
|
|
*scriptpubkey = scriptpubkey_p2pkh(cxt, &p2pkh_destination);
|
2018-02-24 15:59:39 +01:00
|
|
|
parsed = true;
|
|
|
|
right_network = (testnet == chainparams->testnet);
|
2018-02-15 00:45:04 +01:00
|
|
|
} else if (p2sh_from_base58(&testnet, &p2sh_destination,
|
|
|
|
buffer + tok->start, tok->end - tok->start)) {
|
|
|
|
*scriptpubkey = scriptpubkey_p2sh_hash(cxt, &p2sh_destination);
|
2018-02-24 15:59:39 +01:00
|
|
|
parsed = true;
|
|
|
|
right_network = (testnet == chainparams->testnet);
|
2018-02-15 00:45:04 +01:00
|
|
|
}
|
|
|
|
/* Insert other parsers that accept pointer+len here. */
|
|
|
|
|
2018-02-24 15:59:39 +01:00
|
|
|
if (parsed) {
|
|
|
|
if (right_network)
|
|
|
|
return ADDRESS_PARSE_SUCCESS;
|
|
|
|
else
|
|
|
|
return ADDRESS_PARSE_WRONG_NETWORK;
|
|
|
|
}
|
2018-02-15 00:45:04 +01:00
|
|
|
|
|
|
|
/* Generate null-terminated address. */
|
|
|
|
addrz = tal_dup_arr(cxt, char, buffer + tok->start, tok->end - tok->start, 1);
|
|
|
|
addrz[tok->end - tok->start] = '\0';
|
|
|
|
|
|
|
|
bip173 = segwit_addr_net_decode(&witness_version, witness_program,
|
|
|
|
&witness_program_len, addrz);
|
|
|
|
|
|
|
|
if (bip173) {
|
2018-03-25 21:51:11 +02:00
|
|
|
bool witness_ok = false;
|
2018-02-15 00:45:04 +01:00
|
|
|
if (witness_version == 0 && (witness_program_len == 20 ||
|
|
|
|
witness_program_len == 32)) {
|
|
|
|
witness_ok = true;
|
|
|
|
}
|
|
|
|
/* Insert other witness versions here. */
|
|
|
|
|
|
|
|
if (witness_ok) {
|
|
|
|
*scriptpubkey = scriptpubkey_witness_raw(cxt, witness_version,
|
|
|
|
witness_program, witness_program_len);
|
2018-02-24 15:59:39 +01:00
|
|
|
parsed = true;
|
|
|
|
right_network = streq(bip173, chainparams->bip173_name);
|
2018-02-15 00:45:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Insert other parsers that accept null-terminated string here. */
|
|
|
|
|
|
|
|
tal_free(addrz);
|
2018-02-24 15:59:39 +01:00
|
|
|
|
|
|
|
if (parsed) {
|
|
|
|
if (right_network)
|
|
|
|
return ADDRESS_PARSE_SUCCESS;
|
|
|
|
else
|
|
|
|
return ADDRESS_PARSE_WRONG_NETWORK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ADDRESS_PARSE_UNRECOGNIZED;
|
2018-02-15 00:45:04 +01:00
|
|
|
}
|
2018-04-05 21:19:47 +02:00
|
|
|
|
|
|
|
bool json_tok_wtx(struct wallet_tx * tx, const char * buffer,
|
2018-07-29 07:51:38 +02:00
|
|
|
const jsmntok_t *sattok, u64 max)
|
2018-04-05 21:19:47 +02:00
|
|
|
{
|
2018-05-24 23:40:18 +02:00
|
|
|
if (json_tok_streq(buffer, sattok, "all")) {
|
|
|
|
tx->all_funds = true;
|
2018-07-29 07:51:38 +02:00
|
|
|
tx->amount = max;
|
2018-08-13 18:16:41 +02:00
|
|
|
} else if (!json_to_u64(buffer, sattok, &tx->amount)) {
|
2018-05-24 23:40:18 +02:00
|
|
|
command_fail(tx->cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Invalid satoshis");
|
|
|
|
return false;
|
2018-07-29 07:51:38 +02:00
|
|
|
} else if (tx->amount > max) {
|
|
|
|
command_fail(tx->cmd, FUND_MAX_EXCEEDED,
|
|
|
|
"Amount exceeded %"PRIu64, max);
|
|
|
|
return false;
|
|
|
|
}
|
2018-05-24 23:40:18 +02:00
|
|
|
return true;
|
2018-04-05 21:19:47 +02:00
|
|
|
}
|