From 0bb9bcc0f135f2eadf997efe84e0daefe92f4eaf Mon Sep 17 00:00:00 2001 From: ZmnSCPxj Date: Sat, 24 Mar 2018 08:26:08 +0000 Subject: [PATCH] wallet: Track some channel usage statistics. Fixes: #1049 --- lightningd/peer_control.c | 23 +++++++++++++++ lightningd/peer_htlcs.c | 27 +++++++++++++---- wallet/db.c | 16 ++++++++++ wallet/wallet.c | 62 +++++++++++++++++++++++++++++++++++++++ wallet/wallet.h | 29 ++++++++++++++++++ 5 files changed, 152 insertions(+), 5 deletions(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 65216242a..0e6921913 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -625,6 +625,7 @@ static void gossipd_getpeers_complete(struct subd *gossip, const u8 *msg, list_for_each(&gpa->cmd->ld->peers, p, list) { bool connected; struct channel *channel; + struct channel_stats channel_stats; if (gpa->specific_id && !pubkey_eq(gpa->specific_id, &p->id)) continue; @@ -712,6 +713,28 @@ static void gossipd_getpeers_complete(struct subd *gossip, const u8 *msg, json_add_string(response, NULL, channel->billboard.transient); json_array_end(response); + + /* Provide channel statistics */ + wallet_channel_stats_load(gpa->cmd->ld->wallet, + channel->dbid, + &channel_stats); + json_add_u64(response, "in_payments_offered", + channel_stats.in_payments_offered); + json_add_u64(response, "in_msatoshi_offered", + channel_stats.in_msatoshi_offered); + json_add_u64(response, "in_payments_fulfilled", + channel_stats.in_payments_fulfilled); + json_add_u64(response, "in_msatoshi_fulfilled", + channel_stats.in_msatoshi_fulfilled); + json_add_u64(response, "out_payments_offered", + channel_stats.out_payments_offered); + json_add_u64(response, "out_msatoshi_offered", + channel_stats.out_msatoshi_offered); + json_add_u64(response, "out_payments_fulfilled", + channel_stats.out_payments_fulfilled); + json_add_u64(response, "out_msatoshi_fulfilled", + channel_stats.out_msatoshi_fulfilled); + json_object_end(response); } json_array_end(response); diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 6b5bebc7f..ba226aacb 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -199,26 +199,32 @@ static bool check_cltv(struct htlc_in *hin, static void fulfill_htlc(struct htlc_in *hin, const struct preimage *preimage) { u8 *msg; + struct channel *channel = hin->key.channel; + struct wallet *wallet = channel->peer->ld->wallet; hin->preimage = tal_dup(hin, struct preimage, preimage); htlc_in_check(hin, __func__); /* We update state now to signal it's in progress, for persistence. */ - htlc_in_update_state(hin->key.channel, hin, SENT_REMOVE_HTLC); + htlc_in_update_state(channel, hin, SENT_REMOVE_HTLC); + /* Update channel stats */ + wallet_channel_stats_incr_in_fulfilled(wallet, + channel->dbid, + hin->msatoshi); /* No owner? We'll either send to channeld in peer_htlcs, or * onchaind in onchaind_tell_fulfill. */ - if (!hin->key.channel->owner) { - log_debug(hin->key.channel->log, "HTLC fulfilled, but no owner."); + if (!channel->owner) { + log_debug(channel->log, "HTLC fulfilled, but no owner."); return; } - if (channel_on_chain(hin->key.channel)) { + if (channel_on_chain(channel)) { msg = towire_onchain_known_preimage(hin, preimage); } else { msg = towire_channel_fulfill_htlc(hin, hin->key.id, preimage); } - subd_send_msg(hin->key.channel->owner, take(msg)); + subd_send_msg(channel->owner, take(msg)); } static void handle_localpay(struct htlc_in *hin, @@ -665,6 +671,10 @@ static void fulfill_our_htlc_out(struct channel *channel, struct htlc_out *hout, htlc_out_check(hout, __func__); wallet_htlc_update(ld->wallet, hout->dbid, hout->hstate, preimage); + /* Update channel stats */ + wallet_channel_stats_incr_out_fulfilled(ld->wallet, + channel->dbid, + hout->msatoshi); if (hout->in) fulfill_htlc(hout->in, preimage); @@ -889,6 +899,10 @@ static bool update_out_htlc(struct channel *channel, if (!hout->dbid) { wallet_htlc_save_out(ld->wallet, channel, hout); + /* Update channel stats */ + wallet_channel_stats_incr_out_offered(ld->wallet, + channel->dbid, + hout->msatoshi); /* For our own HTLCs, we commit payment to db lazily */ if (hout->origin_htlc_id == 0) @@ -1053,6 +1067,9 @@ static bool channel_added_their_htlc(struct channel *channel, /* Save an incoming htlc to the wallet */ wallet_htlc_save_in(ld->wallet, channel, hin); + /* Update channel stats */ + wallet_channel_stats_incr_in_offered(ld->wallet, channel->dbid, + added->amount_msat); log_debug(channel->log, "Adding their HTLC %"PRIu64, added->id); connect_htlc_in(&channel->peer->ld->htlcs_in, hin); diff --git a/wallet/db.c b/wallet/db.c index c45e68edf..3e16ee75c 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -251,6 +251,22 @@ char *dbmigrations[] = { " , route_nodes = NULL" " , route_channels = NULL" " WHERE status <> 0;", /* PAYMENT_PENDING */ + /* -- Routing statistics -- */ + "ALTER TABLE channels ADD in_payments_offered INTEGER;", + "ALTER TABLE channels ADD in_payments_fulfilled INTEGER;", + "ALTER TABLE channels ADD in_msatoshi_offered INTEGER;", + "ALTER TABLE channels ADD in_msatoshi_fulfilled INTEGER;", + "ALTER TABLE channels ADD out_payments_offered INTEGER;", + "ALTER TABLE channels ADD out_payments_fulfilled INTEGER;", + "ALTER TABLE channels ADD out_msatoshi_offered INTEGER;", + "ALTER TABLE channels ADD out_msatoshi_fulfilled INTEGER;", + "UPDATE channels" + " SET in_payments_offered = 0, in_payments_fulfilled = 0" + " , in_msatoshi_offered = 0, in_msatoshi_fulfilled = 0" + " , out_payments_offered = 0, out_payments_fulfilled = 0" + " , out_msatoshi_offered = 0, out_msatoshi_fulfilled = 0" + " ;", + /* -- Routing statistics ends --*/ NULL, }; diff --git a/wallet/wallet.c b/wallet/wallet.c index d3d516cc5..549afbbc4 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -719,6 +719,68 @@ bool wallet_channels_load_active(const tal_t *ctx, struct wallet *w) return ok; } +static +void wallet_channel_stats_incr_x(struct wallet *w, + char const *dir, + char const *typ, + u64 cdbid, + u64 msatoshi) +{ + char const *payments_stat = tal_fmt(tmpctx, "%s_payments_%s", + dir, typ); + char const *msatoshi_stat = tal_fmt(tmpctx, "%s_msatoshi_%s", + dir, typ); + char const *qry = tal_fmt(tmpctx, + "UPDATE channels" + " SET %s = COALESCE(%s, 0) + 1" + " , %s = COALESCE(%s, 0) + %"PRIu64"" + " WHERE id = %"PRIu64";", + payments_stat, payments_stat, + msatoshi_stat, msatoshi_stat, msatoshi, + cdbid); + sqlite3_stmt *stmt = db_prepare(w->db, qry); + db_exec_prepared(w->db, stmt); +} +void wallet_channel_stats_incr_in_offered(struct wallet *w, u64 id, u64 m) +{ + wallet_channel_stats_incr_x(w, "in", "offered", id, m); +} +void wallet_channel_stats_incr_in_fulfilled(struct wallet *w, u64 id, u64 m) +{ + wallet_channel_stats_incr_x(w, "in", "fulfilled", id, m); +} +void wallet_channel_stats_incr_out_offered(struct wallet *w, u64 id, u64 m) +{ + wallet_channel_stats_incr_x(w, "out", "offered", id, m); +} +void wallet_channel_stats_incr_out_fulfilled(struct wallet *w, u64 id, u64 m) +{ + wallet_channel_stats_incr_x(w, "out", "fulfilled", id, m); +} + +void wallet_channel_stats_load(struct wallet *w, + u64 id, + struct channel_stats *stats) +{ + sqlite3_stmt *stmt; + stmt = db_prepare(w->db, + "SELECT in_payments_offered, in_payments_fulfilled" + " , in_msatoshi_offered, in_msatoshi_fulfilled" + " , out_payments_offered, out_payments_fulfilled" + " , out_msatoshi_offered, out_msatoshi_fulfilled" + " FROM channels" + " WHERE id = ?"); + sqlite3_bind_int64(stmt, 1, id); + stats->in_payments_offered = sqlite3_column_int64(stmt, 0); + stats->in_payments_fulfilled = sqlite3_column_int64(stmt, 1); + stats->in_msatoshi_offered = sqlite3_column_int64(stmt, 2); + stats->in_msatoshi_fulfilled = sqlite3_column_int64(stmt, 3); + stats->out_payments_offered = sqlite3_column_int64(stmt, 4); + stats->out_payments_fulfilled = sqlite3_column_int64(stmt, 5); + stats->out_msatoshi_offered = sqlite3_column_int64(stmt, 6); + stats->out_msatoshi_fulfilled = sqlite3_column_int64(stmt, 7); +} + #ifdef COMPAT_V052 /* Upgrade of db (or initial create): do we have anything to scan for? */ static bool wallet_ever_used(struct wallet *w) diff --git a/wallet/wallet.h b/wallet/wallet.h index 3ae4cdc18..a12711dfe 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -119,6 +119,14 @@ struct outpoint { u32 spendheight; }; +/* Statistics for a channel */ +struct channel_stats { + u64 in_payments_offered, in_payments_fulfilled; + u64 in_msatoshi_offered, in_msatoshi_fulfilled; + u64 out_payments_offered, out_payments_fulfilled; + u64 out_msatoshi_offered, out_msatoshi_fulfilled; +}; + /** * wallet_new - Constructor for a new sqlite3 based wallet * @@ -286,6 +294,27 @@ bool wallet_peer_by_nodeid(struct wallet *w, const struct pubkey *nodeid, */ bool wallet_channels_load_active(const tal_t *ctx, struct wallet *w); +/** + * wallet_channel_stats_incr_* - Increase channel statistics. + * + * @w: wallet containing the channel + * @cdbid: channel database id + * @msatoshi: amount in msatoshi being transferred + */ +void wallet_channel_stats_incr_in_offered(struct wallet *w, u64 cdbid, u64 msatoshi); +void wallet_channel_stats_incr_in_fulfilled(struct wallet *w, u64 cdbid, u64 msatoshi); +void wallet_channel_stats_incr_out_offered(struct wallet *w, u64 cdbid, u64 msatoshi); +void wallet_channel_stats_incr_out_fulfilled(struct wallet *w, u64 cdbid, u64 msatoshi); + +/** + * wallet_channel_stats_load - Load channel statistics + * + * @w: wallet containing the channel + * @cdbid: channel database id + * @stats: location to load statistics to + */ +void wallet_channel_stats_load(struct wallet *w, u64 cdbid, struct channel_stats *stats); + /** * wallet_first_blocknum - get first block we're interested in. *