From e7b003b4992d44dcb98b03501b620ecbf64b02c7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 26 Aug 2016 15:31:19 +0930 Subject: [PATCH] daemon: handle feechange requests. I originally overloaded struct htlc for this, as they go through the same states, but separating them turned out to be clearer. Signed-off-by: Rusty Russell --- daemon/Makefile | 10 ++- daemon/db.c | 161 ++++++++++++++++++++++++++++++++------- daemon/db.h | 4 + daemon/feechange.c | 142 ++++++++++++++++++++++++++++++++++ daemon/feechange.h | 50 ++++++++++++ daemon/feechange_state.h | 23 ++++++ daemon/packets.c | 10 +++ daemon/packets.h | 1 + daemon/peer.c | 130 ++++++++++++++++++++++++++----- daemon/peer.h | 4 + 10 files changed, 488 insertions(+), 47 deletions(-) create mode 100644 daemon/feechange.c create mode 100644 daemon/feechange.h create mode 100644 daemon/feechange_state.h diff --git a/daemon/Makefile b/daemon/Makefile index 9d487ef06..aff143510 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -22,6 +22,7 @@ DAEMON_SRC := \ daemon/cryptopkt.c \ daemon/db.c \ daemon/dns.c \ + daemon/feechange.c \ daemon/htlc.c \ daemon/jsonrpc.c \ daemon/lightningd.c \ @@ -49,7 +50,9 @@ DAEMON_CLI_OBJS := $(DAEMON_CLI_SRC:.c=.o) DAEMON_JSMN_OBJS := daemon/jsmn.o DAEMON_JSMN_HEADERS := daemon/jsmn/jsmn.h -DAEMON_GEN_HEADERS := daemon/gen_htlc_state_names.h +DAEMON_GEN_HEADERS := \ + daemon/gen_feechange_state_names.h \ + daemon/gen_htlc_state_names.h DAEMON_HEADERS := \ daemon/bitcoind.h \ @@ -61,6 +64,8 @@ DAEMON_HEADERS := \ daemon/cryptopkt.h \ daemon/db.h \ daemon/dns.h \ + daemon/feechange.h \ + daemon/feechange_state.h \ daemon/htlc.h \ daemon/htlc_state.h \ daemon/json.h \ @@ -85,6 +90,9 @@ DAEMON_HEADERS := \ daemon/gen_htlc_state_names.h: daemon/htlc_state.h ccan/ccan/cdump/tools/cdump-enumstr ccan/ccan/cdump/tools/cdump-enumstr daemon/htlc_state.h > $@ +daemon/gen_feechange_state_names.h: daemon/feechange_state.h ccan/ccan/cdump/tools/cdump-enumstr + ccan/ccan/cdump/tools/cdump-enumstr daemon/feechange_state.h > $@ + $(DAEMON_OBJS) $(DAEMON_LIB_OBJS) $(DAEMON_CLI_OBJS): $(DAEMON_HEADERS) $(DAEMON_JSMN_HEADERS) $(BITCOIN_HEADERS) $(CORE_HEADERS) $(GEN_HEADERS) $(DAEMON_GEN_HEADERS) $(CCAN_HEADERS) $(DAEMON_JSMN_OBJS): $(DAEMON_JSMN_HEADERS) diff --git a/daemon/db.c b/daemon/db.c index f5f4fbd3b..27de6c2c8 100644 --- a/daemon/db.c +++ b/daemon/db.c @@ -1,6 +1,7 @@ #include "bitcoin/pullpush.h" #include "commit_tx.h" #include "db.h" +#include "feechange.h" #include "htlc.h" #include "lightningd.h" #include "log.h" @@ -434,21 +435,48 @@ static void load_peer_commit_info(struct peer *peer) fatal("load_peer_commit_info:no remote commit info found"); } -/* This htlc no longer committed; either resolved or failed. */ -static void htlc_resolved(struct channel_state *cstate, const struct htlc *htlc) +static void apply_htlc(struct channel_state *cstate, const struct htlc *htlc, + enum htlc_side side) { - if (htlc->r) - cstate_fulfill_htlc(cstate, htlc); - else - cstate_fail_htlc(cstate, htlc); + const char *sidestr = (side == LOCAL ? "LOCAL" : "REMOTE"); + + if (!htlc_has(htlc, HTLC_FLAG(side,HTLC_F_WAS_COMMITTED))) + return; + + log_debug(htlc->peer->log, " %s committed", sidestr); + if (!cstate_add_htlc(cstate, htlc)) + fatal("load_peer_htlcs:can't add %s HTLC", sidestr); + + if (!htlc_has(htlc, HTLC_FLAG(side, HTLC_F_COMMITTED))) { + log_debug(htlc->peer->log, " %s %s", + sidestr, htlc->r ? "resolved" : "failed"); + if (htlc->r) + cstate_fulfill_htlc(cstate, htlc); + else + cstate_fail_htlc(cstate, htlc); + } } +static void apply_feechange(struct channel_state *cstate, + const struct feechange *f, + enum htlc_side side) +{ + /* We only ever apply feechanges to the owner. */ + if (feechange_side(f->state) != side) + return; + + if (!feechange_has(f, HTLC_FLAG(side,HTLC_F_WAS_COMMITTED))) + return; + + adjust_fee(cstate, f->fee_rate); +} + /* As we load the HTLCs, we apply them to get the final channel_state. * We also get the last used htlc id. * This is slow, but sure. */ static void load_peer_htlcs(struct peer *peer) { - int err; + int err, i; sqlite3_stmt *stmt; sqlite3 *sql = peer->dstate->db->sql; char *ctx = tal(peer, char); @@ -482,6 +510,7 @@ static void load_peer_htlcs(struct peer *peer) struct htlc *htlc; struct sha256 rhash; enum htlc_state hstate; + u64 id; if (err != SQLITE_ROW) fatal("load_peer_htlcs:step gave %s:%s", @@ -521,28 +550,8 @@ static void load_peer_htlcs(struct peer *peer) peer->htlc_id_counter = htlc->id + 1; /* Update cstate with this HTLC. */ - if (htlc_has(htlc, HTLC_LOCAL_F_WAS_COMMITTED)) { - log_debug(peer->log, " Local committed"); - if (!cstate_add_htlc(peer->local.commit->cstate, htlc)) - fatal("load_peer_htlcs:can't add local HTLC"); - - if (!htlc_has(htlc, HTLC_LOCAL_F_COMMITTED)) { - log_debug(peer->log, " Local %s", - htlc->r ? "resolved" : "failed"); - htlc_resolved(peer->local.commit->cstate, htlc); - } - } - - if (htlc_has(htlc, HTLC_REMOTE_F_WAS_COMMITTED)) { - log_debug(peer->log, " Remote committed"); - if (!cstate_add_htlc(peer->remote.commit->cstate, htlc)) - fatal("load_peer_htlcs:can't add remote HTLC"); - if (!htlc_has(htlc, HTLC_REMOTE_F_COMMITTED)) { - log_debug(peer->log, " Remote %s", - htlc->r ? "resolved" : "failed"); - htlc_resolved(peer->remote.commit->cstate, htlc); - } - } + apply_htlc(peer->local.commit->cstate, htlc, LOCAL); + apply_htlc(peer->remote.commit->cstate, htlc, REMOTE); } err = sqlite3_finalize(stmt); @@ -550,6 +559,55 @@ static void load_peer_htlcs(struct peer *peer) fatal("load_peer_htlcs:finalize gave %s:%s", sqlite3_errstr(err), sqlite3_errmsg(sql)); + /* Now set any in-progress fee changes. */ + select = tal_fmt(ctx, + "SELECT * FROM feechanges WHERE peer = x'%s';", + pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id)); + + err = sqlite3_prepare_v2(sql, select, -1, &stmt, NULL); + if (err != SQLITE_OK) + fatal("load_peer_htlcs:prepare gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + + while ((err = sqlite3_step(stmt)) != SQLITE_DONE) { + enum feechange_state feechange_state; + + if (err != SQLITE_ROW) + fatal("load_peer_htlcs:step gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + + if (sqlite3_column_count(stmt) != 3) + fatal("load_peer_htlcs:step gave %i cols, not 3", + sqlite3_column_count(stmt)); + + feechange_state + = feechange_state_from_name(sqlite3_column_str(stmt, 1)); + if (feechange_state == FEECHANGE_STATE_INVALID) + fatal("load_peer_htlcs:invalid feechange state %s", + sqlite3_column_str(stmt, 1)); + if (peer->feechanges[feechange_state]) + fatal("load_peer_htlcs: second feechange in state %s", + sqlite3_column_str(stmt, 1)); + peer->feechanges[feechange_state] + = new_feechange(peer, sqlite3_column_int64(stmt, 2), + feechange_state); + } + err = sqlite3_finalize(stmt); + if (err != SQLITE_OK) + fatal("load_peer_htlcs:finalize gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + + /* Apply feechanges from oldest to newest (newest counts). */ + for (i = FEECHANGE_STATE_INVALID - 1; i >= SENT_FEECHANGE; i--) { + if (!peer->feechanges[i]) + continue; + + apply_feechange(peer->local.commit->cstate, + peer->feechanges[i], LOCAL); + apply_feechange(peer->remote.commit->cstate, + peer->feechanges[i], REMOTE); + } + /* Update commit->tx and commit->map */ peer->local.commit->tx = create_commit_tx(peer->local.commit, peer, @@ -985,6 +1043,7 @@ void db_init(struct lightningd_state *dstate) "CREATE TABLE wallet (privkey "SQL_PRIVKEY");" "CREATE TABLE anchors (peer "SQL_PUBKEY", txid "SQL_TXID", idx INT, amount INT, ok_depth INT, min_depth INT, bool ours, PRIMARY KEY(peer));" "CREATE TABLE htlcs (peer "SQL_PUBKEY", id INT, state TEXT, msatoshis INT, expiry INT, rhash "SQL_RHASH", r "SQL_R", routing "SQL_ROUTING", src_peer "SQL_PUBKEY", src_id INT, PRIMARY KEY(peer, id));" + "CREATE TABLE feechanges (peer "SQL_PUBKEY", state TEXT, fee_rate INT, PRIMARY KEY(peer,state));" "CREATE TABLE commit_info (peer "SQL_PUBKEY", side TEXT, commit_num INT, revocation_hash "SQL_SHA256", xmit_order INT, sig "SQL_SIGNATURE", prev_revocation_hash "SQL_SHA256", PRIMARY KEY(peer, side));" "CREATE TABLE shachain (peer "SQL_PUBKEY", shachain BINARY(%zu), PRIMARY KEY(peer));" "CREATE TABLE their_visible_state (peer "SQL_PUBKEY", offered_anchor BOOLEAN, commitkey "SQL_PUBKEY", finalkey "SQL_PUBKEY", locktime INT, mindepth INT, commit_fee_rate INT, next_revocation_hash "SQL_SHA256", PRIMARY KEY(peer));" @@ -1242,6 +1301,28 @@ bool db_new_htlc(struct peer *peer, const struct htlc *htlc) return !errmsg; } +bool db_new_feechange(struct peer *peer, const struct feechange *feechange) +{ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s)", __func__, peerid); + assert(peer->dstate->db->in_transaction); + + /* "CREATE TABLE feechanges (peer "SQL_PUBKEY", state TEXT, fee_rate INT, PRIMARY KEY(peer,state));" */ + errmsg = db_exec(ctx, peer->dstate, + "INSERT INTO feechanges VALUES" + " (x'%s', '%s', %"PRIu64");", + peerid, + feechange_state_name(feechange->state), + feechange->fee_rate); + + if (errmsg) + log_broken(peer->log, "%s:%s", __func__, errmsg); + tal_free(ctx); + return !errmsg; +} + bool db_update_htlc_state(struct peer *peer, const struct htlc *htlc, enum htlc_state oldstate) { @@ -1263,6 +1344,28 @@ bool db_update_htlc_state(struct peer *peer, const struct htlc *htlc, return !errmsg; } +bool db_update_feechange_state(struct peer *peer, + const struct feechange *f, + enum htlc_state oldstate) +{ + const char *errmsg, *ctx = tal(peer, char); + const char *peerid = pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id); + + log_debug(peer->log, "%s(%s): %s->%s", __func__, peerid, + feechange_state_name(oldstate), + feechange_state_name(f->state)); + assert(peer->dstate->db->in_transaction); + errmsg = db_exec(ctx, peer->dstate, + "UPDATE feechanges SET state='%s' WHERE peer=x'%s' AND state='%s';", + feechange_state_name(f->state), peerid, + feechange_state_name(oldstate)); + + if (errmsg) + log_broken(peer->log, "%s:%s", __func__, errmsg); + tal_free(ctx); + return !errmsg; +} + bool db_update_state(struct peer *peer) { const char *errmsg, *ctx = tal(peer, char); diff --git a/daemon/db.h b/daemon/db.h index 89feab598..26062119b 100644 --- a/daemon/db.h +++ b/daemon/db.h @@ -32,8 +32,12 @@ bool db_update_their_closing(struct peer *peer); /* Must be inside transaction. */ bool db_new_htlc(struct peer *peer, const struct htlc *htlc); +bool db_new_feechange(struct peer *peer, const struct feechange *feechange); bool db_update_htlc_state(struct peer *peer, const struct htlc *htlc, enum htlc_state oldstate); +bool db_update_feechange_state(struct peer *peer, + const struct feechange *f, + enum htlc_state oldstate); bool db_new_commit_info(struct peer *peer, enum channel_side side, const struct sha256 *prev_rhash); bool db_remove_their_prev_revocation_hash(struct peer *peer); diff --git a/daemon/feechange.c b/daemon/feechange.c new file mode 100644 index 000000000..57eeb033a --- /dev/null +++ b/daemon/feechange.c @@ -0,0 +1,142 @@ +#include "db.h" +#include "feechange.h" +#include "log.h" +#include "peer.h" +#include +#include + #include "gen_feechange_state_names.h" + +/* This is the HTLC-like flags for each state. */ +static const int per_state_bits[] = { + [SENT_FEECHANGE] = HTLC_ADDING + HTLC_LOCAL_F_OWNER + + HTLC_REMOTE_F_PENDING, + + [SENT_FEECHANGE_COMMIT] = HTLC_ADDING + HTLC_LOCAL_F_OWNER + + HTLC_REMOTE_F_COMMITTED + + HTLC_REMOTE_F_WAS_COMMITTED, + + [RCVD_FEECHANGE_REVOCATION] = HTLC_ADDING + HTLC_LOCAL_F_OWNER + + HTLC_REMOTE_F_COMMITTED + + HTLC_REMOTE_F_REVOKED + + HTLC_LOCAL_F_PENDING + + HTLC_REMOTE_F_WAS_COMMITTED, + + [RCVD_FEECHANGE_ACK_COMMIT] = HTLC_ADDING + HTLC_LOCAL_F_OWNER + + HTLC_REMOTE_F_COMMITTED + + HTLC_REMOTE_F_REVOKED + + HTLC_LOCAL_F_COMMITTED + + HTLC_LOCAL_F_WAS_COMMITTED + + HTLC_REMOTE_F_WAS_COMMITTED, + + [SENT_FEECHANGE_ACK_REVOCATION] = HTLC_LOCAL_F_OWNER + + HTLC_REMOTE_F_COMMITTED + + HTLC_REMOTE_F_REVOKED + + HTLC_LOCAL_F_COMMITTED + + HTLC_LOCAL_F_REVOKED + + HTLC_LOCAL_F_WAS_COMMITTED + + HTLC_REMOTE_F_WAS_COMMITTED, + + [RCVD_FEECHANGE] = HTLC_ADDING + HTLC_REMOTE_F_OWNER + + HTLC_LOCAL_F_PENDING, + + [RCVD_FEECHANGE_COMMIT] = HTLC_ADDING + HTLC_REMOTE_F_OWNER + + HTLC_LOCAL_F_COMMITTED + + HTLC_LOCAL_F_WAS_COMMITTED, + + [SENT_FEECHANGE_REVOCATION] = HTLC_ADDING + HTLC_REMOTE_F_OWNER + + HTLC_LOCAL_F_COMMITTED + + HTLC_LOCAL_F_REVOKED + + HTLC_REMOTE_F_PENDING + + HTLC_LOCAL_F_WAS_COMMITTED, + + [SENT_FEECHANGE_ACK_COMMIT] = HTLC_ADDING + HTLC_REMOTE_F_OWNER + + HTLC_LOCAL_F_COMMITTED + + HTLC_LOCAL_F_REVOKED + + HTLC_REMOTE_F_COMMITTED + + HTLC_LOCAL_F_WAS_COMMITTED + + HTLC_REMOTE_F_WAS_COMMITTED, + + [RCVD_FEECHANGE_ACK_REVOCATION] = HTLC_REMOTE_F_OWNER + + HTLC_LOCAL_F_COMMITTED + + HTLC_LOCAL_F_REVOKED + + HTLC_REMOTE_F_COMMITTED + + HTLC_REMOTE_F_REVOKED + + HTLC_LOCAL_F_WAS_COMMITTED + + HTLC_REMOTE_F_WAS_COMMITTED, +}; + +int feechange_state_flags(enum feechange_state state) +{ + assert(state < ARRAY_SIZE(per_state_bits)); + assert(per_state_bits[state]); + return per_state_bits[state]; +} + +const char *feechange_state_name(enum feechange_state s) +{ + size_t i; + + for (i = 0; enum_feechange_state_names[i].name; i++) + if (enum_feechange_state_names[i].v == s) + return enum_feechange_state_names[i].name; + return "unknown"; +} + +enum feechange_state feechange_state_from_name(const char *name) +{ + size_t i; + + for (i = 0; enum_feechange_state_names[i].name; i++) + if (streq(enum_feechange_state_names[i].name, name)) + return enum_feechange_state_names[i].v; + return FEECHANGE_STATE_INVALID; +} + +struct feechange *new_feechange(struct peer *peer, + u64 fee_rate, + enum feechange_state state) +{ + struct feechange *f = tal(peer, struct feechange); + f->state = state; + f->fee_rate = fee_rate; + + return f; +} + +bool feechange_changestate(struct peer *peer, + struct feechange *f, + enum feechange_state oldstate, + enum feechange_state newstate, + bool db_commit) +{ + log_debug(peer->log, "feechange: %s->%s", + feechange_state_name(f->state), + feechange_state_name(newstate)); + assert(f->state == oldstate); + assert(peer->feechanges[f->state] == f); + + /* You can only go to consecutive states. */ + assert(newstate == f->state + 1); + + /* You can't change sides. */ + assert(feechange_side(f->state) == feechange_side(newstate)); + + f->state = newstate; + + /* We can have multiple dead feestates, but only one in any other */ + if (!feechange_is_dead(f)) + assert(!peer->feechanges[f->state]); + + peer->feechanges[oldstate] = NULL; + peer->feechanges[newstate] = f; + + if (db_commit) { + if (newstate == RCVD_FEECHANGE_COMMIT + || newstate == SENT_FEECHANGE_COMMIT) + return db_new_feechange(peer, f); + + return db_update_feechange_state(peer, f, oldstate); + } + return true; +} + diff --git a/daemon/feechange.h b/daemon/feechange.h new file mode 100644 index 000000000..0a29f01b5 --- /dev/null +++ b/daemon/feechange.h @@ -0,0 +1,50 @@ +#ifndef LIGHTNING_DAEMON_FEECHANGE_H +#define LIGHTNING_DAEMON_FEECHANGE_H +#include "config.h" +#include "feechange_state.h" +#include "htlc.h" + +struct feechange { + /* What's the status */ + enum feechange_state state; + /* The rate. */ + u64 fee_rate; +}; + +static inline enum htlc_side feechange_side(enum feechange_state state) +{ + if (state <= SENT_FEECHANGE_ACK_REVOCATION) { + return LOCAL; + } else { + assert(state < FEECHANGE_STATE_INVALID); + return REMOTE; + } +} + +bool feechange_changestate(struct peer *peer, + struct feechange *feechange, + enum feechange_state oldstate, + enum feechange_state newstate, + bool db_commit); + +struct feechange *new_feechange(struct peer *peer, + u64 fee_rate, + enum feechange_state state); + +const char *feechange_state_name(enum feechange_state s); +enum feechange_state feechange_state_from_name(const char *name); + +/* HTLC-add-style bitflags for each feechange state */ +int feechange_state_flags(enum feechange_state state); + +static inline bool feechange_has(const struct feechange *f, int flag) +{ + return feechange_state_flags(f->state) & flag; +} + +static inline bool feechange_is_dead(const struct feechange *feechange) +{ + return feechange->state == SENT_FEECHANGE_ACK_REVOCATION + || feechange->state == RCVD_FEECHANGE_ACK_REVOCATION; +} +#endif /* LIGHTNING_DAEMON_FEECHANGE_H */ diff --git a/daemon/feechange_state.h b/daemon/feechange_state.h new file mode 100644 index 000000000..6841d15a9 --- /dev/null +++ b/daemon/feechange_state.h @@ -0,0 +1,23 @@ +#ifndef LIGHTNING_DAEMON_FEECHANGE_STATE_H +#define LIGHTNING_DAEMON_FEECHANGE_STATE_H +#include "config.h" + +/* Like HTLCs, but only adding; we never "remove" a feechange. */ +enum feechange_state { + /* When we add a new feechange, it goes in this order. */ + SENT_FEECHANGE, + SENT_FEECHANGE_COMMIT, + RCVD_FEECHANGE_REVOCATION, + RCVD_FEECHANGE_ACK_COMMIT, + SENT_FEECHANGE_ACK_REVOCATION, + + /* When they add a new feechange, it goes in this order. */ + RCVD_FEECHANGE, + RCVD_FEECHANGE_COMMIT, + SENT_FEECHANGE_REVOCATION, + SENT_FEECHANGE_ACK_COMMIT, + RCVD_FEECHANGE_ACK_REVOCATION, + + FEECHANGE_STATE_INVALID +}; +#endif /* LIGHTNING_DAEMON_FEECHANGE_STATE_H */ diff --git a/daemon/packets.c b/daemon/packets.c index ef8712604..f266937bf 100644 --- a/daemon/packets.c +++ b/daemon/packets.c @@ -178,6 +178,16 @@ void queue_pkt_htlc_fail(struct peer *peer, struct htlc *htlc) queue_pkt(peer, PKT__PKT_UPDATE_FAIL_HTLC, f); } +void queue_pkt_feechange(struct peer *peer, u64 feerate) +{ + UpdateFee *f = tal(peer, UpdateFee); + + update_fee__init(f); + f->fee_rate = feerate; + + queue_pkt(peer, PKT__PKT_UPDATE_FEE, f); +} + /* OK, we're sending a signature for their pending changes. */ void queue_pkt_commit(struct peer *peer, const struct bitcoin_signature *sig) { diff --git a/daemon/packets.h b/daemon/packets.h index bb4dc7198..f30bb0575 100644 --- a/daemon/packets.h +++ b/daemon/packets.h @@ -17,6 +17,7 @@ void queue_pkt_open_complete(struct peer *peer); void queue_pkt_htlc_add(struct peer *peer, struct htlc *htlc); void queue_pkt_htlc_fulfill(struct peer *peer, struct htlc *htlc); void queue_pkt_htlc_fail(struct peer *peer, struct htlc *htlc); +void queue_pkt_feechange(struct peer *peer, u64 feerate); void queue_pkt_commit(struct peer *peer, const struct bitcoin_signature *sig); void queue_pkt_revocation(struct peer *peer, const struct sha256 *preimage, diff --git a/daemon/peer.c b/daemon/peer.c index 0def764ac..8a0e38864 100644 --- a/daemon/peer.c +++ b/daemon/peer.c @@ -242,6 +242,7 @@ static bool peer_uncommitted_changes(const struct peer *peer) { struct htlc_map_iter it; struct htlc *h; + enum feechange_state i; for (h = htlc_map_first(&peer->htlcs, &it); h; @@ -249,6 +250,13 @@ static bool peer_uncommitted_changes(const struct peer *peer) if (htlc_has(h, HTLC_REMOTE_F_PENDING)) return true; } + /* Pending feechange we sent, or pending ack of theirs. */ + for (i = 0; i < ARRAY_SIZE(peer->feechanges); i++) { + if (!peer->feechanges[i]) + continue; + if (feechange_state_flags(i) & HTLC_REMOTE_F_PENDING) + return true; + } return false; } @@ -632,6 +640,34 @@ static void adjust_cstates(struct peer *peer, struct htlc *h, adjust_cstate_side(peer->local.staging_cstate, h, old, new, LOCAL); } +static void adjust_cstate_fee_side(struct channel_state *cstate, + const struct feechange *f, + enum feechange_state old, + enum feechange_state new, + enum htlc_side side) +{ + /* We applied changes to staging_cstate when we first received + * feechange packet, so we could make sure it was valid. Don't + * do that again. */ + if (old == SENT_FEECHANGE || old == RCVD_FEECHANGE) + return; + + /* Feechanges only ever get applied to the side which created them: + * ours gets applied when they ack, theirs gets applied when we ack. */ + if (side == LOCAL && new == RCVD_FEECHANGE_REVOCATION) + adjust_fee(cstate, f->fee_rate); + else if (side == REMOTE && new == SENT_FEECHANGE_REVOCATION) + adjust_fee(cstate, f->fee_rate); +} + +static void adjust_cstates_fee(struct peer *peer, const struct feechange *f, + enum feechange_state old, + enum feechange_state new) +{ + adjust_cstate_fee_side(peer->remote.staging_cstate, f, old, new, REMOTE); + adjust_cstate_fee_side(peer->local.staging_cstate, f, old, new, LOCAL); +} + static void check_both_committed(struct peer *peer, struct htlc *h) { if (!htlc_has(h, HTLC_ADDING) && !htlc_has(h, HTLC_REMOVING)) @@ -657,22 +693,29 @@ static void check_both_committed(struct peer *peer, struct htlc *h) } } -struct state_table { +struct htlcs_table { enum htlc_state from, to; }; -static const char *htlcs_changestate(struct peer *peer, - const struct state_table *table, size_t n, - bool db_commit) +struct feechanges_table { + enum feechange_state from, to; +}; + +static const char *changestates(struct peer *peer, + const struct htlcs_table *table, + size_t n, + const struct feechanges_table *ftable, + size_t n_ftable, + bool db_commit) { struct htlc_map_iter it; struct htlc *h; bool changed = false; + size_t i; for (h = htlc_map_first(&peer->htlcs, &it); h; h = htlc_map_next(&peer->htlcs, &it)) { - size_t i; for (i = 0; i < n; i++) { if (h->state == table[i].from) { adjust_cstates(peer, h, @@ -685,6 +728,19 @@ static const char *htlcs_changestate(struct peer *peer, } } } + + for (i = 0; i < n_ftable; i++) { + struct feechange *f = peer->feechanges[ftable[i].from]; + if (!f) + continue; + adjust_cstates_fee(peer, f, ftable[i].from, ftable[i].to); + if (!feechange_changestate(peer, f, + ftable[i].from, ftable[i].to, + db_commit)) + return "database error"; + changed = true; + } + /* BOLT #2: * * A node MUST NOT send an `update_commit` message which does @@ -827,18 +883,26 @@ static Pkt *handle_pkt_commit(struct peer *peer, const Pkt *pkt) struct commit_info *ci; bool to_them_only; /* FIXME: We can actually merge these two... */ - static const struct state_table commit_changes[] = { + static const struct htlcs_table commit_changes[] = { { RCVD_ADD_REVOCATION, RCVD_ADD_ACK_COMMIT }, { RCVD_REMOVE_HTLC, RCVD_REMOVE_COMMIT }, { RCVD_ADD_HTLC, RCVD_ADD_COMMIT }, { RCVD_REMOVE_REVOCATION, RCVD_REMOVE_ACK_COMMIT } }; - static const struct state_table revocation_changes[] = { + static const struct feechanges_table commit_feechanges[] = { + { RCVD_FEECHANGE_REVOCATION, RCVD_FEECHANGE_ACK_COMMIT }, + { RCVD_FEECHANGE, RCVD_FEECHANGE_COMMIT } + }; + static const struct htlcs_table revocation_changes[] = { { RCVD_ADD_ACK_COMMIT, SENT_ADD_ACK_REVOCATION }, { RCVD_REMOVE_COMMIT, SENT_REMOVE_REVOCATION }, { RCVD_ADD_COMMIT, SENT_ADD_REVOCATION }, { RCVD_REMOVE_ACK_COMMIT, SENT_REMOVE_ACK_REVOCATION } }; + static const struct feechanges_table revocation_feechanges[] = { + { RCVD_FEECHANGE_ACK_COMMIT, SENT_FEECHANGE_ACK_REVOCATION }, + { RCVD_FEECHANGE_COMMIT, SENT_FEECHANGE_REVOCATION } + }; ci = new_commit_info(peer, peer->local.commit->commit_num + 1); @@ -850,9 +914,10 @@ static Pkt *handle_pkt_commit(struct peer *peer, const Pkt *pkt) * A node MUST NOT send an `update_commit` message which does * not include any updates. */ - errmsg = htlcs_changestate(peer, - commit_changes, ARRAY_SIZE(commit_changes), - true); + errmsg = changestates(peer, + commit_changes, ARRAY_SIZE(commit_changes), + commit_feechanges, ARRAY_SIZE(commit_feechanges), + true); if (errmsg) { db_abort_transaction(peer); return pkt_err(peer, "%s", errmsg); @@ -930,8 +995,11 @@ static Pkt *handle_pkt_commit(struct peer *peer, const Pkt *pkt) assert(to_them_only || peer->local.commit->sig); assert(peer->local.commit->commit_num > 0); - errmsg = htlcs_changestate(peer, revocation_changes, - ARRAY_SIZE(revocation_changes), true); + errmsg = changestates(peer, + revocation_changes, ARRAY_SIZE(revocation_changes), + revocation_feechanges, + ARRAY_SIZE(revocation_feechanges), + true); if (errmsg) { log_broken(peer->log, "queue_pkt_revocation: %s", errmsg); /* FIXME: Return error. */ @@ -1028,12 +1096,16 @@ static Pkt *handle_pkt_revocation(struct peer *peer, const Pkt *pkt, { Pkt *err; const char *errmsg; - static const struct state_table changes[] = { + static const struct htlcs_table changes[] = { { SENT_ADD_COMMIT, RCVD_ADD_REVOCATION }, { SENT_REMOVE_ACK_COMMIT, RCVD_REMOVE_ACK_REVOCATION }, { SENT_ADD_ACK_COMMIT, RCVD_ADD_ACK_REVOCATION }, { SENT_REMOVE_COMMIT, RCVD_REMOVE_REVOCATION } }; + static const struct feechanges_table feechanges[] = { + { SENT_FEECHANGE_COMMIT, RCVD_FEECHANGE_REVOCATION }, + { SENT_FEECHANGE_ACK_COMMIT, RCVD_FEECHANGE_ACK_REVOCATION } + }; err = accept_pkt_revocation(peer, pkt); if (err) @@ -1046,7 +1118,8 @@ static Pkt *handle_pkt_revocation(struct peer *peer, const Pkt *pkt, */ if (!db_start_transaction(peer)) return pkt_err(peer, "database error"); - errmsg = htlcs_changestate(peer, changes, ARRAY_SIZE(changes), true); + errmsg = changestates(peer, changes, ARRAY_SIZE(changes), + feechanges, ARRAY_SIZE(feechanges), true); if (errmsg) { log_broken(peer->log, "accept_pkt_revocation: %s", errmsg); db_abort_transaction(peer); @@ -1705,6 +1778,17 @@ static void retransmit_updates(struct peer *peer) break; } } + + /* This feechange may not be appropriate any more, but that's no + * different from when we sent it last time. And this avoids us + * creating different commit txids on retransmission */ + if (peer->feechanges[SENT_FEECHANGE_COMMIT]) { + u64 feerate = peer->feechanges[SENT_FEECHANGE_COMMIT]->fee_rate; + log_debug(peer->log, + "Retransmitting feechange %"PRIu64, feerate); + queue_pkt_feechange(peer, feerate); + } + assert(!peer->feechanges[SENT_FEECHANGE]); } /* FIXME: Maybe it would be neater to remember all pay commands, and simply @@ -1802,7 +1886,7 @@ again: if (!cstate_add_htlc(peer->remote.staging_cstate, h)) fatal("Could not add HTLC?"); break; - } + } /* Fall thru */ case RCVD_ADD_HTLC: log_debug(peer->log, "Forgetting %s %"PRIu64, htlc_state_name(h->state), h->id); @@ -1839,6 +1923,12 @@ again: } if (retry) goto again; + + /* Forget uncommitted feechanges */ + peer->feechanges[SENT_FEECHANGE] + = tal_free(peer->feechanges[SENT_FEECHANGE]); + peer->feechanges[RCVD_FEECHANGE] + = tal_free(peer->feechanges[RCVD_FEECHANGE]); } static void retransmit_pkts(struct peer *peer, s64 ack) @@ -1979,12 +2069,16 @@ static void do_commit(struct peer *peer, struct command *jsoncmd) { struct commit_info *ci; const char *errmsg; - static const struct state_table changes[] = { + static const struct htlcs_table changes[] = { { SENT_ADD_HTLC, SENT_ADD_COMMIT }, { SENT_REMOVE_REVOCATION, SENT_REMOVE_ACK_COMMIT }, { SENT_ADD_REVOCATION, SENT_ADD_ACK_COMMIT}, { SENT_REMOVE_HTLC, SENT_REMOVE_COMMIT} }; + static const struct feechanges_table feechanges[] = { + { SENT_FEECHANGE, SENT_FEECHANGE_COMMIT }, + { SENT_FEECHANGE_REVOCATION, SENT_FEECHANGE_ACK_COMMIT} + }; bool to_us_only; /* We can have changes we suggested, or changes they suggested. */ @@ -2014,7 +2108,8 @@ static void do_commit(struct peer *peer, struct command *jsoncmd) fatal("queue_pkt_commit: db fail"); } - errmsg = htlcs_changestate(peer, changes, ARRAY_SIZE(changes), true); + errmsg = changestates(peer, changes, ARRAY_SIZE(changes), + feechanges, ARRAY_SIZE(feechanges), true); if (errmsg) { log_broken(peer->log, "queue_pkt_commit: %s", errmsg); /* FIXME: Return error. */ @@ -2185,6 +2280,7 @@ struct peer *new_peer(struct lightningd_state *dstate, log_prefix(peer->dstate->base_log), peer); htlc_map_init(&peer->htlcs); + memset(peer->feechanges, 0, sizeof(peer->feechanges)); shachain_init(&peer->their_preimages); list_add(&dstate->peers, &peer->list); diff --git a/daemon/peer.h b/daemon/peer.h index 96d04bfd9..cfe7f87b9 100644 --- a/daemon/peer.h +++ b/daemon/peer.h @@ -7,6 +7,7 @@ #include "bitcoin/script.h" #include "bitcoin/shadouble.h" #include "channel.h" +#include "feechange.h" #include "htlc.h" #include "lightning.pb-c.h" #include "netaddr.h" @@ -190,6 +191,9 @@ struct peer { /* All HTLCs. */ struct htlc_map htlcs; + /* We only track one feechange per state: last one counts. */ + struct feechange *feechanges[FEECHANGE_STATE_INVALID]; + /* Current ongoing packetflow */ struct io_data *io_data;