|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <bitcoin/address.h>
|
|
|
|
#include <bitcoin/base58.h>
|
|
|
|
#include <bitcoin/script.h>
|
|
|
|
#include <ccan/json_escape/json_escape.h>
|
|
|
|
#include <ccan/mem/mem.h>
|
|
|
|
#include <ccan/str/hex/hex.h>
|
|
|
|
#include <ccan/tal/str/str.h>
|
|
|
|
#include <common/bech32.h>
|
|
|
|
#include <common/json.h>
|
|
|
|
#include <common/json_command.h>
|
|
|
|
#include <common/json_helpers.h>
|
|
|
|
#include <common/jsonrpc_errors.h>
|
|
|
|
#include <common/memleak.h>
|
common/node_id: new type.
Node ids are pubkeys, but we only use them as pubkeys for routing and checking
gossip messages. So we're packing and unpacking them constantly, and wasting
some space and time.
This introduces a new type, explicitly the SEC1 compressed encoding
(33 bytes). We ensure its validity when we load from the db, or get it
from JSON. We still use 'struct pubkey' for peer messages, which checks
validity.
Results from 5 runs, min-max(mean +/- stddev):
store_load_msec,vsz_kb,store_rewrite_sec,listnodes_sec,listchannels_sec,routing_sec,peer_write_all_sec
39475-39572(39518+/-36),2880732,41.150000-41.390000(41.298+/-0.085),2.260000-2.550000(2.336+/-0.11),44.390000-65.150000(58.648+/-7.5),32.740000-33.020000(32.89+/-0.093),44.130000-45.090000(44.566+/-0.32)
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
6 years ago
|
|
|
#include <common/node_id.h>
|
|
|
|
#include <common/param.h>
|
|
|
|
#include <common/type_to_string.h>
|
|
|
|
#include <common/utils.h>
|
|
|
|
#include <common/wallet_tx.h>
|
|
|
|
#include <common/wireaddr.h>
|
|
|
|
#include <gossipd/routing.h>
|
|
|
|
#include <lightningd/chaintopology.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>
|
|
|
|
|
common/node_id: new type.
Node ids are pubkeys, but we only use them as pubkeys for routing and checking
gossip messages. So we're packing and unpacking them constantly, and wasting
some space and time.
This introduces a new type, explicitly the SEC1 compressed encoding
(33 bytes). We ensure its validity when we load from the db, or get it
from JSON. We still use 'struct pubkey' for peer messages, which checks
validity.
Results from 5 runs, min-max(mean +/- stddev):
store_load_msec,vsz_kb,store_rewrite_sec,listnodes_sec,listchannels_sec,routing_sec,peer_write_all_sec
39475-39572(39518+/-36),2880732,41.150000-41.390000(41.298+/-0.085),2.260000-2.550000(2.336+/-0.11),44.390000-65.150000(58.648+/-7.5),32.740000-33.020000(32.89+/-0.093),44.130000-45.090000(44.566+/-0.32)
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
6 years ago
|
|
|
void json_add_node_id(struct json_stream *response,
|
|
|
|
const char *fieldname,
|
|
|
|
const struct node_id *id)
|
|
|
|
{
|
|
|
|
json_add_hex(response, fieldname, id->k, sizeof(id->k));
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_add_pubkey(struct json_stream *response,
|
|
|
|
const char *fieldname,
|
|
|
|
const struct pubkey *key)
|
|
|
|
{
|
pubkey: rename PUBKEY_DER_LEN to PUBKEY_CMPR_LEN.
Pubkeys are not not actually DER encoding, but Pieter Wuille corrected
me: it's SEC 1 documented encoding.
Results from 5 runs, min-max(mean +/- stddev):
store_load_msec,vsz_kb,store_rewrite_sec,listnodes_sec,listchannels_sec,routing_sec,peer_write_all_sec
38922-39297(39180.6+/-1.3e+02),2880728,41.040000-41.160000(41.106+/-0.05),2.270000-2.530000(2.338+/-0.097),44.570000-53.980000(49.696+/-3),32.840000-33.080000(32.95+/-0.095),43.060000-44.950000(43.696+/-0.72)
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
6 years ago
|
|
|
u8 der[PUBKEY_CMPR_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));
|
|
|
|
}
|
|
|
|
|
|
|
|
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(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"'%s' should be txid, 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 *scid)
|
|
|
|
{
|
|
|
|
json_add_member(response, fieldname, true, "%dx%dx%d",
|
|
|
|
short_channel_id_blocknum(scid),
|
|
|
|
short_channel_id_txnum(scid),
|
|
|
|
short_channel_id_outnum(scid));
|
|
|
|
}
|
|
|
|
|
|
|
|
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(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.address);
|
|
|
|
json_object_end(response);
|
|
|
|
return;
|
|
|
|
case ADDR_INTERNAL_STATICTOR:
|
|
|
|
json_object_start(response, fieldname);
|
|
|
|
json_add_string(response, "type", "Tor from blob generated static address");
|
|
|
|
json_add_address(response, "service", &addr->u.torservice.address);
|
|
|
|
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, false, "%u", value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_add_double(struct json_stream *result, const char *fieldname, double value)
|
|
|
|
{
|
|
|
|
json_add_member(result, fieldname, false, "%f", value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_add_u64(struct json_stream *result, const char *fieldname,
|
|
|
|
uint64_t value)
|
|
|
|
{
|
|
|
|
json_add_member(result, fieldname, false, "%"PRIu64, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_add_s64(struct json_stream *result, const char *fieldname,
|
|
|
|
int64_t value)
|
|
|
|
{
|
|
|
|
json_add_member(result, fieldname, false, "%"PRIi64, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_add_u32(struct json_stream *result, const char *fieldname,
|
|
|
|
uint32_t value)
|
|
|
|
{
|
|
|
|
json_add_member(result, fieldname, false, "%u", value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_add_s32(struct json_stream *result, const char *fieldname,
|
|
|
|
int32_t value)
|
|
|
|
{
|
|
|
|
json_add_member(result, fieldname, false, "%d", value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_add_literal(struct json_stream *result, const char *fieldname,
|
|
|
|
const char *literal, int len)
|
|
|
|
{
|
|
|
|
/* Literal may contain quotes, so bypass normal checks */
|
|
|
|
char *dest = json_member_direct(result, fieldname, strlen(literal));
|
|
|
|
if (dest)
|
|
|
|
memcpy(dest, literal, strlen(literal));
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_add_string(struct json_stream *result, const char *fieldname, const char *value TAKES)
|
|
|
|
{
|
|
|
|
json_add_member(result, fieldname, true, "%s", value);
|
|
|
|
if (taken(value))
|
|
|
|
tal_free(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_add_bool(struct json_stream *result, const char *fieldname, bool value)
|
|
|
|
{
|
|
|
|
json_add_member(result, fieldname, false, value ? "true" : "false");
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_add_null(struct json_stream *stream, const char *fieldname)
|
|
|
|
{
|
|
|
|
json_add_member(stream, fieldname, false, "null");
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_add_hex(struct json_stream *js, const char *fieldname,
|
|
|
|
const void *data, size_t len)
|
|
|
|
{
|
|
|
|
/* Size without NUL term */
|
|
|
|
size_t hexlen = hex_str_size(len) - 1;
|
|
|
|
char *dest;
|
|
|
|
|
|
|
|
dest = json_member_direct(js, fieldname, 1 + hexlen + 1);
|
|
|
|
if (dest) {
|
|
|
|
dest[0] = '"';
|
|
|
|
if (!hex_encode(data, len, dest + 1, hexlen + 1))
|
|
|
|
abort();
|
|
|
|
dest[1+hexlen] = '"';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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_tx(struct json_stream *result,
|
|
|
|
const char *fieldname,
|
|
|
|
const struct bitcoin_tx *tx)
|
|
|
|
{
|
|
|
|
json_add_hex_talarr(result, fieldname, linearize_tx(tmpctx, tx));
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_add_escaped_string(struct json_stream *result, const char *fieldname,
|
|
|
|
const struct json_escape *esc TAKES)
|
|
|
|
{
|
|
|
|
/* Already escaped, don't re-escape! */
|
|
|
|
char *dest = json_member_direct(result, fieldname,
|
|
|
|
1 + strlen(esc->s) + 1);
|
|
|
|
|
|
|
|
if (dest) {
|
|
|
|
dest[0] = '"';
|
|
|
|
memcpy(dest + 1, esc->s, strlen(esc->s));
|
|
|
|
dest[1+strlen(esc->s)] = '"';
|
|
|
|
}
|
|
|
|
if (taken(esc))
|
|
|
|
tal_free(esc);
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_add_amount_msat_compat(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_amount_msat_only(result, msatfieldname, msat);
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_add_amount_msat_only(struct json_stream *result,
|
|
|
|
const char *msatfieldname,
|
|
|
|
struct amount_msat msat)
|
|
|
|
{
|
|
|
|
json_add_string(result, msatfieldname,
|
|
|
|
type_to_string(tmpctx, struct amount_msat, &msat));
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_add_amount_sat_compat(struct json_stream *result,
|
|
|
|
struct amount_sat sat,
|
|
|
|
const char *rawfieldname,
|
|
|
|
const char *msatfieldname)
|
|
|
|
{
|
|
|
|
json_add_u64(result, rawfieldname, sat.satoshis); /* Raw: low-level helper */
|
|
|
|
json_add_amount_sat_only(result, msatfieldname, sat);
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_add_amount_sat_only(struct json_stream *result,
|
|
|
|
const char *msatfieldname,
|
|
|
|
struct amount_sat sat)
|
|
|
|
{
|
|
|
|
struct amount_msat msat;
|
|
|
|
if (amount_sat_to_msat(&msat, sat))
|
|
|
|
json_add_string(result, msatfieldname,
|
|
|
|
type_to_string(tmpctx, struct amount_msat, &msat));
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_add_timeabs(struct json_stream *result, const char *fieldname,
|
|
|
|
struct timeabs t)
|
|
|
|
{
|
|
|
|
json_add_member(result, fieldname, false, "%" PRIu64 ".%03" PRIu64,
|
|
|
|
(u64)t.ts.tv_sec, (u64)t.ts.tv_nsec / 1000000);
|
|
|
|
}
|
plugin: Add new notification type: warning
This notification bases on `LOG_BROKEN` and `LOG_UNUSUAL` level log.
--Introduction
A notification for topic `warning` is sent every time a new `BROKEN`/
`UNUSUAL` level(in plugins, we use `error`/`warn`) log generated, which
means an unusual/borken thing happens, such as channel failed,
message resolving failed...
```json
{
"warning": {
"level": "warn",
"time": "1559743608.565342521",
"source": "lightningd(17652): 0821f80652fb840239df8dc99205792bba2e559a05469915804c08420230e23c7c chan #7854:",
"log": "Peer permanent failure in CHANNELD_NORMAL: lightning_channeld: sent ERROR bad reestablish dataloss msg"
}
}
```
1. `level` is `warn` or `error`:
`warn` means something seems bad happened and it's under control, but
we'd better check it;
`error` means something extremely bad is out of control, and it may lead
to crash;
2. `time` is the second since epoch;
3. `source`, in fact, is the `prefix` of the log_entry. It means where
the event happened, it may have the following forms:
`<node_id> chan #<db_id_of_channel>:`, `lightningd(<lightningd_pid>):`,
`plugin-<plugin_name>:`, `<daemon_name>(<daemon_pid>):`, `jsonrpc:`,
`jcon fd <error_fd_to_jsonrpc>:`, `plugin-manager`;
4. `log` is the context of the original log entry.
--Note:
1. The main code uses `UNUSUAL`/`BROKEN`, and plugin module uses `warn`
/`error`, considering the consistency with plugin, warning choose `warn`
/`error`. But users who use c-lightning with plugins may want to
`getlog` with specified level when receive warning. It's the duty for
plugin dev to turn `warn`/`error` into `UNUSUAL`/`BROKEN` and present it
to the users, or pass it directly to `getlog`;
2. About time, `json_log()` in `log` module uses the Relative Time, from
the time when `log_book` inited to the time when this event happend.
But I consider the `UNUSUAL`/`BROKEN` event is rare, and it is very
likely to happen after running for a long time, so for users, they will
pay more attention to Absolute Time.
-- Related Change
1. Remove the definitions of `log`, `log_book`, `log_entry` from `log.c`
to `log.h`, then they can be used in warning declaration and definition.
2. Remove `void json_add_time(struct json_stream *result, const char
*fieldname, struct timespec ts)` from `log.c` to `json.c`, and add
related declaration in `json.h`. Now the notification function in
`notification.c` can call it.
2. Add a pointer to `struct lightningd` in `struct log_book`. This may
affect the independence of the `log` module, but storing a pointer to
`ld` is more direct;
6 years ago
|
|
|
|
|
|
|
void json_add_time(struct json_stream *result, const char *fieldname,
|
|
|
|
struct timespec ts)
|
|
|
|
{
|
|
|
|
char timebuf[100];
|
|
|
|
|
|
|
|
snprintf(timebuf, sizeof(timebuf), "%lu.%09u",
|
|
|
|
(unsigned long)ts.tv_sec,
|
|
|
|
(unsigned)ts.tv_nsec);
|
|
|
|
json_add_string(result, fieldname, timebuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_add_secret(struct json_stream *response, const char *fieldname,
|
|
|
|
const struct secret *secret)
|
|
|
|
{
|
|
|
|
json_add_hex(response, fieldname, secret, sizeof(struct secret));
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_add_sha256(struct json_stream *result, const char *fieldname,
|
|
|
|
const struct sha256 *hash)
|
|
|
|
{
|
|
|
|
json_add_hex(result, fieldname, hash, sizeof(*hash));
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_add_preimage(struct json_stream *result, const char *fieldname,
|
|
|
|
const struct preimage *preimage)
|
|
|
|
{
|
|
|
|
json_add_hex(result, fieldname, preimage, sizeof(*preimage));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* segwit_addr_net_decode - Try to decode a Bech32 address and detect
|
|
|
|
* testnet/mainnet/regtest/signet
|
|
|
|
*
|
|
|
|
* 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"), regtest ("bcrt"), or signet ("sb")
|
|
|
|
* 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->bip173_name,
|
|
|
|
addrz))
|
|
|
|
return chainparams->bip173_name;
|
|
|
|
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 *bip173;
|
|
|
|
|
|
|
|
bool parsed;
|
|
|
|
bool right_network;
|
|
|
|
u8 addr_version;
|
|
|
|
|
|
|
|
parsed =
|
|
|
|
ripemd160_from_base58(&addr_version, &destination.addr,
|
|
|
|
buffer + tok->start, tok->end - tok->start);
|
|
|
|
|
|
|
|
if (parsed) {
|
|
|
|
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. */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Generate null-terminated address. */
|
|
|
|
addrz = tal_dup_arr(ctx, 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, chainparams);
|
|
|
|
|
|
|
|
if (bip173) {
|
|
|
|
bool witness_ok = false;
|
|
|
|
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(ctx, witness_version,
|
|
|
|
witness_program, witness_program_len);
|
|
|
|
parsed = true;
|
|
|
|
right_network = streq(bip173, chainparams->bip173_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Insert other parsers that accept null-terminated string here. */
|
|
|
|
|
|
|
|
tal_free(addrz);
|
|
|
|
|
|
|
|
if (parsed) {
|
|
|
|
if (right_network)
|
|
|
|
return ADDRESS_PARSE_SUCCESS;
|
|
|
|
else
|
|
|
|
return ADDRESS_PARSE_WRONG_NETWORK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ADDRESS_PARSE_UNRECOGNIZED;
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_add_tok(struct json_stream *result, const char *fieldname,
|
|
|
|
const jsmntok_t *tok, const char *buffer)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
const jsmntok_t *t;
|
|
|
|
|
|
|
|
switch (tok->type) {
|
|
|
|
case JSMN_PRIMITIVE:
|
|
|
|
if (json_tok_is_num(buffer, tok)) {
|
|
|
|
json_to_int(buffer, tok, &i);
|
|
|
|
json_add_num(result, fieldname, i);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case JSMN_STRING:
|
|
|
|
if (json_tok_streq(buffer, tok, "true"))
|
|
|
|
json_add_bool(result, fieldname, true);
|
|
|
|
else if (json_tok_streq(buffer, tok, "false"))
|
|
|
|
json_add_bool(result, fieldname, false);
|
|
|
|
else
|
|
|
|
json_add_string(result, fieldname, json_strdup(tmpctx, buffer, tok));
|
|
|
|
return;
|
|
|
|
|
|
|
|
case JSMN_ARRAY:
|
|
|
|
json_array_start(result, fieldname);
|
|
|
|
json_for_each_arr(i, t, tok)
|
|
|
|
json_add_tok(result, NULL, t, buffer);
|
|
|
|
json_array_end(result);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case JSMN_OBJECT:
|
|
|
|
json_object_start(result, fieldname);
|
|
|
|
json_for_each_obj(i, t, tok)
|
|
|
|
json_add_tok(result, json_strdup(tmpctx, buffer, t), t+1, buffer);
|
|
|
|
json_object_end(result);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case JSMN_UNDEFINED:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
abort();
|
|
|
|
}
|