|
|
|
@ -44,18 +44,26 @@
|
|
|
|
|
#include <wire/peer_wire.h>
|
|
|
|
|
#include <wire/wire_sync.h>
|
|
|
|
|
|
|
|
|
|
/* FIXME: Reorder */
|
|
|
|
|
static void destroy_peer(struct peer *peer)
|
|
|
|
|
{
|
|
|
|
|
list_del_from(&peer->ld->peers, &peer->list);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We copy per-peer entries above --log-level into the main log. */
|
|
|
|
|
static void copy_to_parent_log(const char *prefix,
|
|
|
|
|
enum log_level level,
|
|
|
|
|
bool continued,
|
|
|
|
|
const struct timeabs *time,
|
|
|
|
|
const char *str,
|
|
|
|
|
const u8 *io,
|
|
|
|
|
struct log *parent_log);
|
|
|
|
|
|
|
|
|
|
static void destroy_peer(struct peer *peer)
|
|
|
|
|
struct log *parent_log)
|
|
|
|
|
{
|
|
|
|
|
list_del_from(&peer->ld->peers, &peer->list);
|
|
|
|
|
if (level == LOG_IO_IN || level == LOG_IO_OUT)
|
|
|
|
|
log_io(parent_log, level, prefix, io, tal_len(io));
|
|
|
|
|
else if (continued)
|
|
|
|
|
log_add(parent_log, "%s ... %s", prefix, str);
|
|
|
|
|
else
|
|
|
|
|
log_(parent_log, level, "%s %s", prefix, str);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct peer *new_peer(struct lightningd *ld, u64 dbid,
|
|
|
|
@ -105,6 +113,61 @@ struct peer *find_peer_by_dbid(struct lightningd *ld, u64 dbid)
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct peer *peer_by_id(struct lightningd *ld, const struct pubkey *id)
|
|
|
|
|
{
|
|
|
|
|
struct peer *p;
|
|
|
|
|
|
|
|
|
|
list_for_each(&ld->peers, p, list)
|
|
|
|
|
if (pubkey_eq(&p->id, id))
|
|
|
|
|
return p;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct peer *peer_from_json(struct lightningd *ld,
|
|
|
|
|
const char *buffer,
|
|
|
|
|
jsmntok_t *peeridtok)
|
|
|
|
|
{
|
|
|
|
|
struct pubkey peerid;
|
|
|
|
|
|
|
|
|
|
if (!json_tok_pubkey(buffer, peeridtok, &peerid))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
return peer_by_id(ld, &peerid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u8 *p2wpkh_for_keyidx(const tal_t *ctx, struct lightningd *ld, u64 keyidx)
|
|
|
|
|
{
|
|
|
|
|
struct pubkey shutdownkey;
|
|
|
|
|
|
|
|
|
|
if (!bip32_pubkey(ld->wallet->bip32_base, &shutdownkey, keyidx))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
return scriptpubkey_p2wpkh(ctx, &shutdownkey);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u32 feerate_min(struct lightningd *ld)
|
|
|
|
|
{
|
|
|
|
|
if (ld->config.ignore_fee_limits)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
/* Set this to average of slow and normal.*/
|
|
|
|
|
return (get_feerate(ld->topology, FEERATE_SLOW)
|
|
|
|
|
+ get_feerate(ld->topology, FEERATE_NORMAL)) / 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* BOLT #2:
|
|
|
|
|
*
|
|
|
|
|
* Given the variance in fees, and the fact that the transaction may
|
|
|
|
|
* be spent in the future, it's a good idea for the fee payer to keep
|
|
|
|
|
* a good margin, say 5x the expected fee requirement */
|
|
|
|
|
u32 feerate_max(struct lightningd *ld)
|
|
|
|
|
{
|
|
|
|
|
if (ld->config.ignore_fee_limits)
|
|
|
|
|
return UINT_MAX;
|
|
|
|
|
|
|
|
|
|
return get_feerate(ld->topology, FEERATE_IMMEDIATE) * 5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void sign_last_tx(struct channel *channel)
|
|
|
|
|
{
|
|
|
|
|
const tal_t *tmpctx = tal_tmpctx(channel);
|
|
|
|
@ -340,19 +403,6 @@ send_error:
|
|
|
|
|
subd_send_fd(ld->gossip, gossip_fd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Gossipd tells us peer was already connected. */
|
|
|
|
|
void peer_already_connected(struct lightningd *ld, const u8 *msg)
|
|
|
|
|
{
|
|
|
|
|
struct pubkey id;
|
|
|
|
|
|
|
|
|
|
if (!fromwire_gossip_peer_already_connected(msg, NULL, &id))
|
|
|
|
|
fatal("Gossip gave bad GOSSIP_PEER_ALREADY_CONNECTED message %s",
|
|
|
|
|
tal_hex(msg, msg));
|
|
|
|
|
|
|
|
|
|
/* If we were waiting for connection, we succeeded. */
|
|
|
|
|
connect_succeeded(ld, &id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct channel *channel_by_channel_id(struct peer *peer,
|
|
|
|
|
const struct channel_id *channel_id)
|
|
|
|
|
{
|
|
|
|
@ -429,213 +479,6 @@ send_error:
|
|
|
|
|
tal_free(error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We copy per-peer entries above --log-level into the main log. */
|
|
|
|
|
static void copy_to_parent_log(const char *prefix,
|
|
|
|
|
enum log_level level,
|
|
|
|
|
bool continued,
|
|
|
|
|
const struct timeabs *time,
|
|
|
|
|
const char *str,
|
|
|
|
|
const u8 *io,
|
|
|
|
|
struct log *parent_log)
|
|
|
|
|
{
|
|
|
|
|
if (level == LOG_IO_IN || level == LOG_IO_OUT)
|
|
|
|
|
log_io(parent_log, level, prefix, io, tal_len(io));
|
|
|
|
|
else if (continued)
|
|
|
|
|
log_add(parent_log, "%s ... %s", prefix, str);
|
|
|
|
|
else
|
|
|
|
|
log_(parent_log, level, "%s %s", prefix, str);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct peer *peer_by_id(struct lightningd *ld, const struct pubkey *id)
|
|
|
|
|
{
|
|
|
|
|
struct peer *p;
|
|
|
|
|
|
|
|
|
|
list_for_each(&ld->peers, p, list)
|
|
|
|
|
if (pubkey_eq(&p->id, id))
|
|
|
|
|
return p;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct getpeers_args {
|
|
|
|
|
struct command *cmd;
|
|
|
|
|
/* If non-NULL, they want logs too */
|
|
|
|
|
enum log_level *ll;
|
|
|
|
|
/* If set, only report on a specific id. */
|
|
|
|
|
struct pubkey *specific_id;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void gossipd_getpeers_complete(struct subd *gossip, const u8 *msg,
|
|
|
|
|
const int *fds,
|
|
|
|
|
struct getpeers_args *gpa)
|
|
|
|
|
{
|
|
|
|
|
/* This is a little sneaky... */
|
|
|
|
|
struct pubkey *ids;
|
|
|
|
|
struct wireaddr *addrs;
|
|
|
|
|
struct json_result *response = new_json_result(gpa->cmd);
|
|
|
|
|
struct peer *p;
|
|
|
|
|
|
|
|
|
|
if (!fromwire_gossip_getpeers_reply(msg, msg, NULL, &ids, &addrs)) {
|
|
|
|
|
command_fail(gpa->cmd, "Bad response from gossipd");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* First the peers not just gossiping. */
|
|
|
|
|
json_object_start(response, NULL);
|
|
|
|
|
json_array_start(response, "peers");
|
|
|
|
|
list_for_each(&gpa->cmd->ld->peers, p, list) {
|
|
|
|
|
bool connected;
|
|
|
|
|
struct channel *channel;
|
|
|
|
|
|
|
|
|
|
if (gpa->specific_id && !pubkey_eq(gpa->specific_id, &p->id))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
json_object_start(response, NULL);
|
|
|
|
|
json_add_pubkey(response, "id", &p->id);
|
|
|
|
|
channel = peer_active_channel(p);
|
|
|
|
|
connected = (channel && channel->owner != NULL);
|
|
|
|
|
json_add_bool(response, "connected", connected);
|
|
|
|
|
|
|
|
|
|
if (connected) {
|
|
|
|
|
json_array_start(response, "netaddr");
|
|
|
|
|
if (p->addr.type != ADDR_TYPE_PADDING)
|
|
|
|
|
json_add_string(response, NULL,
|
|
|
|
|
type_to_string(response,
|
|
|
|
|
struct wireaddr,
|
|
|
|
|
&p->addr));
|
|
|
|
|
json_array_end(response);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_array_start(response, "channels");
|
|
|
|
|
json_add_uncommitted_channel(response, p->uncommitted_channel);
|
|
|
|
|
|
|
|
|
|
list_for_each(&p->channels, channel, list) {
|
|
|
|
|
json_object_start(response, NULL);
|
|
|
|
|
json_add_string(response, "state",
|
|
|
|
|
channel_state_name(channel));
|
|
|
|
|
if (channel->owner)
|
|
|
|
|
json_add_string(response, "owner",
|
|
|
|
|
channel->owner->name);
|
|
|
|
|
if (channel->scid)
|
|
|
|
|
json_add_short_channel_id(response,
|
|
|
|
|
"short_channel_id",
|
|
|
|
|
channel->scid);
|
|
|
|
|
json_add_txid(response,
|
|
|
|
|
"funding_txid",
|
|
|
|
|
&channel->funding_txid);
|
|
|
|
|
json_add_u64(response, "msatoshi_to_us",
|
|
|
|
|
channel->our_msatoshi);
|
|
|
|
|
json_add_u64(response, "msatoshi_total",
|
|
|
|
|
channel->funding_satoshi * 1000);
|
|
|
|
|
|
|
|
|
|
/* channel config */
|
|
|
|
|
json_add_u64(response, "dust_limit_satoshis",
|
|
|
|
|
channel->our_config.dust_limit_satoshis);
|
|
|
|
|
json_add_u64(response, "max_htlc_value_in_flight_msat",
|
|
|
|
|
channel->our_config.max_htlc_value_in_flight_msat);
|
|
|
|
|
json_add_u64(response, "channel_reserve_satoshis",
|
|
|
|
|
channel->our_config.channel_reserve_satoshis);
|
|
|
|
|
json_add_u64(response, "htlc_minimum_msat",
|
|
|
|
|
channel->our_config.htlc_minimum_msat);
|
|
|
|
|
json_add_num(response, "to_self_delay",
|
|
|
|
|
channel->our_config.to_self_delay);
|
|
|
|
|
json_add_num(response, "max_accepted_htlcs",
|
|
|
|
|
channel->our_config.max_accepted_htlcs);
|
|
|
|
|
|
|
|
|
|
json_object_end(response);
|
|
|
|
|
}
|
|
|
|
|
json_array_end(response);
|
|
|
|
|
|
|
|
|
|
if (gpa->ll)
|
|
|
|
|
json_add_log(response, "log", p->log_book, *gpa->ll);
|
|
|
|
|
json_object_end(response);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < tal_count(ids); i++) {
|
|
|
|
|
/* Don't report peers in both, which can happen if they're
|
|
|
|
|
* reconnecting */
|
|
|
|
|
if (peer_by_id(gpa->cmd->ld, ids + i))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
json_object_start(response, NULL);
|
|
|
|
|
/* Fake state. */
|
|
|
|
|
json_add_string(response, "state", "GOSSIPING");
|
|
|
|
|
json_add_pubkey(response, "id", ids+i);
|
|
|
|
|
json_array_start(response, "netaddr");
|
|
|
|
|
if (addrs[i].type != ADDR_TYPE_PADDING)
|
|
|
|
|
json_add_string(response, NULL,
|
|
|
|
|
type_to_string(response, struct wireaddr,
|
|
|
|
|
addrs + i));
|
|
|
|
|
json_array_end(response);
|
|
|
|
|
json_add_bool(response, "connected", true);
|
|
|
|
|
json_add_string(response, "owner", gossip->name);
|
|
|
|
|
json_object_end(response);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_array_end(response);
|
|
|
|
|
json_object_end(response);
|
|
|
|
|
command_success(gpa->cmd, response);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void json_listpeers(struct command *cmd,
|
|
|
|
|
const char *buffer, const jsmntok_t *params)
|
|
|
|
|
{
|
|
|
|
|
jsmntok_t *leveltok;
|
|
|
|
|
struct getpeers_args *gpa = tal(cmd, struct getpeers_args);
|
|
|
|
|
jsmntok_t *idtok;
|
|
|
|
|
|
|
|
|
|
gpa->cmd = cmd;
|
|
|
|
|
gpa->specific_id = NULL;
|
|
|
|
|
if (!json_get_params(cmd, buffer, params,
|
|
|
|
|
"?id", &idtok,
|
|
|
|
|
"?level", &leveltok,
|
|
|
|
|
NULL)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (idtok) {
|
|
|
|
|
gpa->specific_id = tal_arr(cmd, struct pubkey, 1);
|
|
|
|
|
if (!json_tok_pubkey(buffer, idtok, gpa->specific_id)) {
|
|
|
|
|
command_fail(cmd, "id %.*s not valid",
|
|
|
|
|
idtok->end - idtok->start,
|
|
|
|
|
buffer + idtok->start);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (leveltok) {
|
|
|
|
|
gpa->ll = tal(gpa, enum log_level);
|
|
|
|
|
if (!json_tok_loglevel(buffer, leveltok, gpa->ll)) {
|
|
|
|
|
command_fail(cmd, "Invalid level param");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else
|
|
|
|
|
gpa->ll = NULL;
|
|
|
|
|
|
|
|
|
|
/* Get peers from gossipd. */
|
|
|
|
|
subd_req(cmd, cmd->ld->gossip,
|
|
|
|
|
take(towire_gossip_getpeers_request(cmd, gpa->specific_id)),
|
|
|
|
|
-1, 0, gossipd_getpeers_complete, gpa);
|
|
|
|
|
command_still_pending(cmd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct json_command listpeers_command = {
|
|
|
|
|
"listpeers",
|
|
|
|
|
json_listpeers,
|
|
|
|
|
"Show current peers, if {level} is set, include logs for {id}"
|
|
|
|
|
};
|
|
|
|
|
AUTODATA(json_command, &listpeers_command);
|
|
|
|
|
|
|
|
|
|
struct peer *peer_from_json(struct lightningd *ld,
|
|
|
|
|
const char *buffer,
|
|
|
|
|
jsmntok_t *peeridtok)
|
|
|
|
|
{
|
|
|
|
|
struct pubkey peerid;
|
|
|
|
|
|
|
|
|
|
if (!json_tok_pubkey(buffer, peeridtok, &peerid))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
return peer_by_id(ld, &peerid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum watch_result funding_announce_cb(struct channel *channel,
|
|
|
|
|
const struct bitcoin_tx *tx,
|
|
|
|
|
unsigned int depth,
|
|
|
|
@ -659,50 +502,6 @@ static enum watch_result funding_announce_cb(struct channel *channel,
|
|
|
|
|
return DELETE_WATCH;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If channel is NULL, free them all (for shutdown) */
|
|
|
|
|
void free_htlcs(struct lightningd *ld, const struct channel *channel)
|
|
|
|
|
{
|
|
|
|
|
struct htlc_out_map_iter outi;
|
|
|
|
|
struct htlc_out *hout;
|
|
|
|
|
struct htlc_in_map_iter ini;
|
|
|
|
|
struct htlc_in *hin;
|
|
|
|
|
bool deleted;
|
|
|
|
|
|
|
|
|
|
/* FIXME: Implement check_htlcs to ensure no dangling hout->in ptrs! */
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
deleted = false;
|
|
|
|
|
for (hout = htlc_out_map_first(&ld->htlcs_out, &outi);
|
|
|
|
|
hout;
|
|
|
|
|
hout = htlc_out_map_next(&ld->htlcs_out, &outi)) {
|
|
|
|
|
if (channel && hout->key.channel != channel)
|
|
|
|
|
continue;
|
|
|
|
|
tal_free(hout);
|
|
|
|
|
deleted = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (hin = htlc_in_map_first(&ld->htlcs_in, &ini);
|
|
|
|
|
hin;
|
|
|
|
|
hin = htlc_in_map_next(&ld->htlcs_in, &ini)) {
|
|
|
|
|
if (channel && hin->key.channel != channel)
|
|
|
|
|
continue;
|
|
|
|
|
tal_free(hin);
|
|
|
|
|
deleted = true;
|
|
|
|
|
}
|
|
|
|
|
/* Can skip over elements due to iterating while deleting. */
|
|
|
|
|
} while (deleted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u8 *p2wpkh_for_keyidx(const tal_t *ctx, struct lightningd *ld, u64 keyidx)
|
|
|
|
|
{
|
|
|
|
|
struct pubkey shutdownkey;
|
|
|
|
|
|
|
|
|
|
if (!bip32_pubkey(ld->wallet->bip32_base, &shutdownkey, keyidx))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
return scriptpubkey_p2wpkh(ctx, &shutdownkey);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum watch_result funding_lockin_cb(struct channel *channel,
|
|
|
|
|
const struct bitcoin_tx *tx,
|
|
|
|
|
unsigned int depth,
|
|
|
|
@ -964,29 +763,6 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u32 feerate_min(struct lightningd *ld)
|
|
|
|
|
{
|
|
|
|
|
if (ld->config.ignore_fee_limits)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
/* Set this to average of slow and normal.*/
|
|
|
|
|
return (get_feerate(ld->topology, FEERATE_SLOW)
|
|
|
|
|
+ get_feerate(ld->topology, FEERATE_NORMAL)) / 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* BOLT #2:
|
|
|
|
|
*
|
|
|
|
|
* Given the variance in fees, and the fact that the transaction may
|
|
|
|
|
* be spent in the future, it's a good idea for the fee payer to keep
|
|
|
|
|
* a good margin, say 5x the expected fee requirement */
|
|
|
|
|
u32 feerate_max(struct lightningd *ld)
|
|
|
|
|
{
|
|
|
|
|
if (ld->config.ignore_fee_limits)
|
|
|
|
|
return UINT_MAX;
|
|
|
|
|
|
|
|
|
|
return get_feerate(ld->topology, FEERATE_IMMEDIATE) * 5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool peer_start_channeld(struct channel *channel,
|
|
|
|
|
const struct crypto_state *cs,
|
|
|
|
|
u64 gossip_index,
|
|
|
|
@ -1116,6 +892,174 @@ bool peer_start_channeld(struct channel *channel,
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct getpeers_args {
|
|
|
|
|
struct command *cmd;
|
|
|
|
|
/* If non-NULL, they want logs too */
|
|
|
|
|
enum log_level *ll;
|
|
|
|
|
/* If set, only report on a specific id. */
|
|
|
|
|
struct pubkey *specific_id;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void gossipd_getpeers_complete(struct subd *gossip, const u8 *msg,
|
|
|
|
|
const int *fds,
|
|
|
|
|
struct getpeers_args *gpa)
|
|
|
|
|
{
|
|
|
|
|
/* This is a little sneaky... */
|
|
|
|
|
struct pubkey *ids;
|
|
|
|
|
struct wireaddr *addrs;
|
|
|
|
|
struct json_result *response = new_json_result(gpa->cmd);
|
|
|
|
|
struct peer *p;
|
|
|
|
|
|
|
|
|
|
if (!fromwire_gossip_getpeers_reply(msg, msg, NULL, &ids, &addrs)) {
|
|
|
|
|
command_fail(gpa->cmd, "Bad response from gossipd");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* First the peers not just gossiping. */
|
|
|
|
|
json_object_start(response, NULL);
|
|
|
|
|
json_array_start(response, "peers");
|
|
|
|
|
list_for_each(&gpa->cmd->ld->peers, p, list) {
|
|
|
|
|
bool connected;
|
|
|
|
|
struct channel *channel;
|
|
|
|
|
|
|
|
|
|
if (gpa->specific_id && !pubkey_eq(gpa->specific_id, &p->id))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
json_object_start(response, NULL);
|
|
|
|
|
json_add_pubkey(response, "id", &p->id);
|
|
|
|
|
channel = peer_active_channel(p);
|
|
|
|
|
connected = (channel && channel->owner != NULL);
|
|
|
|
|
json_add_bool(response, "connected", connected);
|
|
|
|
|
|
|
|
|
|
if (connected) {
|
|
|
|
|
json_array_start(response, "netaddr");
|
|
|
|
|
if (p->addr.type != ADDR_TYPE_PADDING)
|
|
|
|
|
json_add_string(response, NULL,
|
|
|
|
|
type_to_string(response,
|
|
|
|
|
struct wireaddr,
|
|
|
|
|
&p->addr));
|
|
|
|
|
json_array_end(response);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_array_start(response, "channels");
|
|
|
|
|
json_add_uncommitted_channel(response, p->uncommitted_channel);
|
|
|
|
|
|
|
|
|
|
list_for_each(&p->channels, channel, list) {
|
|
|
|
|
json_object_start(response, NULL);
|
|
|
|
|
json_add_string(response, "state",
|
|
|
|
|
channel_state_name(channel));
|
|
|
|
|
if (channel->owner)
|
|
|
|
|
json_add_string(response, "owner",
|
|
|
|
|
channel->owner->name);
|
|
|
|
|
if (channel->scid)
|
|
|
|
|
json_add_short_channel_id(response,
|
|
|
|
|
"short_channel_id",
|
|
|
|
|
channel->scid);
|
|
|
|
|
json_add_txid(response,
|
|
|
|
|
"funding_txid",
|
|
|
|
|
&channel->funding_txid);
|
|
|
|
|
json_add_u64(response, "msatoshi_to_us",
|
|
|
|
|
channel->our_msatoshi);
|
|
|
|
|
json_add_u64(response, "msatoshi_total",
|
|
|
|
|
channel->funding_satoshi * 1000);
|
|
|
|
|
|
|
|
|
|
/* channel config */
|
|
|
|
|
json_add_u64(response, "dust_limit_satoshis",
|
|
|
|
|
channel->our_config.dust_limit_satoshis);
|
|
|
|
|
json_add_u64(response, "max_htlc_value_in_flight_msat",
|
|
|
|
|
channel->our_config.max_htlc_value_in_flight_msat);
|
|
|
|
|
json_add_u64(response, "channel_reserve_satoshis",
|
|
|
|
|
channel->our_config.channel_reserve_satoshis);
|
|
|
|
|
json_add_u64(response, "htlc_minimum_msat",
|
|
|
|
|
channel->our_config.htlc_minimum_msat);
|
|
|
|
|
json_add_num(response, "to_self_delay",
|
|
|
|
|
channel->our_config.to_self_delay);
|
|
|
|
|
json_add_num(response, "max_accepted_htlcs",
|
|
|
|
|
channel->our_config.max_accepted_htlcs);
|
|
|
|
|
|
|
|
|
|
json_object_end(response);
|
|
|
|
|
}
|
|
|
|
|
json_array_end(response);
|
|
|
|
|
|
|
|
|
|
if (gpa->ll)
|
|
|
|
|
json_add_log(response, "log", p->log_book, *gpa->ll);
|
|
|
|
|
json_object_end(response);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < tal_count(ids); i++) {
|
|
|
|
|
/* Don't report peers in both, which can happen if they're
|
|
|
|
|
* reconnecting */
|
|
|
|
|
if (peer_by_id(gpa->cmd->ld, ids + i))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
json_object_start(response, NULL);
|
|
|
|
|
/* Fake state. */
|
|
|
|
|
json_add_string(response, "state", "GOSSIPING");
|
|
|
|
|
json_add_pubkey(response, "id", ids+i);
|
|
|
|
|
json_array_start(response, "netaddr");
|
|
|
|
|
if (addrs[i].type != ADDR_TYPE_PADDING)
|
|
|
|
|
json_add_string(response, NULL,
|
|
|
|
|
type_to_string(response, struct wireaddr,
|
|
|
|
|
addrs + i));
|
|
|
|
|
json_array_end(response);
|
|
|
|
|
json_add_bool(response, "connected", true);
|
|
|
|
|
json_add_string(response, "owner", gossip->name);
|
|
|
|
|
json_object_end(response);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_array_end(response);
|
|
|
|
|
json_object_end(response);
|
|
|
|
|
command_success(gpa->cmd, response);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void json_listpeers(struct command *cmd,
|
|
|
|
|
const char *buffer, const jsmntok_t *params)
|
|
|
|
|
{
|
|
|
|
|
jsmntok_t *leveltok;
|
|
|
|
|
struct getpeers_args *gpa = tal(cmd, struct getpeers_args);
|
|
|
|
|
jsmntok_t *idtok;
|
|
|
|
|
|
|
|
|
|
gpa->cmd = cmd;
|
|
|
|
|
gpa->specific_id = NULL;
|
|
|
|
|
if (!json_get_params(cmd, buffer, params,
|
|
|
|
|
"?id", &idtok,
|
|
|
|
|
"?level", &leveltok,
|
|
|
|
|
NULL)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (idtok) {
|
|
|
|
|
gpa->specific_id = tal_arr(cmd, struct pubkey, 1);
|
|
|
|
|
if (!json_tok_pubkey(buffer, idtok, gpa->specific_id)) {
|
|
|
|
|
command_fail(cmd, "id %.*s not valid",
|
|
|
|
|
idtok->end - idtok->start,
|
|
|
|
|
buffer + idtok->start);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (leveltok) {
|
|
|
|
|
gpa->ll = tal(gpa, enum log_level);
|
|
|
|
|
if (!json_tok_loglevel(buffer, leveltok, gpa->ll)) {
|
|
|
|
|
command_fail(cmd, "Invalid level param");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else
|
|
|
|
|
gpa->ll = NULL;
|
|
|
|
|
|
|
|
|
|
/* Get peers from gossipd. */
|
|
|
|
|
subd_req(cmd, cmd->ld->gossip,
|
|
|
|
|
take(towire_gossip_getpeers_request(cmd, gpa->specific_id)),
|
|
|
|
|
-1, 0, gossipd_getpeers_complete, gpa);
|
|
|
|
|
command_still_pending(cmd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct json_command listpeers_command = {
|
|
|
|
|
"listpeers",
|
|
|
|
|
json_listpeers,
|
|
|
|
|
"Show current peers, if {level} is set, include logs for {id}"
|
|
|
|
|
};
|
|
|
|
|
AUTODATA(json_command, &listpeers_command);
|
|
|
|
|
|
|
|
|
|
static void json_close(struct command *cmd,
|
|
|
|
|
const char *buffer, const jsmntok_t *params)
|
|
|
|
|
{
|
|
|
|
|