From b9d6e8bbaaa8b44a5c6b23773010f5be108d6caa Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 13 Dec 2019 00:34:53 +1030 Subject: [PATCH] common: add fee_states state machine for update_fee. This uses the same state machine as HTLCs, but they're only ever added, not removed. Since we can only have one in each state, we use a simple array; mostly NULL. We could make this more space-efficient by folding everything into the first 5 states, but that would be more complex than just using the identical state machine. One subtlety: we don't send uncommitted fee_states over the wire. Signed-off-by: Rusty Russell --- channeld/Makefile | 1 + common/Makefile | 1 + common/fee_states.c | 158 ++++++++++++++++++++++++++++++++++++++++ common/fee_states.h | 88 ++++++++++++++++++++++ common/type_to_string.h | 1 + openingd/Makefile | 2 + 6 files changed, 251 insertions(+) create mode 100644 common/fee_states.c create mode 100644 common/fee_states.h diff --git a/channeld/Makefile b/channeld/Makefile index 128e39fb1..920879cb2 100644 --- a/channeld/Makefile +++ b/channeld/Makefile @@ -46,6 +46,7 @@ CHANNELD_COMMON_OBJS := \ common/derive_basepoints.o \ common/dev_disconnect.o \ common/features.o \ + common/fee_states.o \ common/gen_status_wire.o \ common/gen_peer_status_wire.o \ common/gossip_rcvd_filter.o \ diff --git a/common/Makefile b/common/Makefile index 0a3b37a56..46a7cc6c1 100644 --- a/common/Makefile +++ b/common/Makefile @@ -20,6 +20,7 @@ COMMON_SRC_NOGEN := \ common/derive_basepoints.c \ common/dev_disconnect.c \ common/features.c \ + common/fee_states.c \ common/funding_tx.c \ common/gossip_rcvd_filter.c \ common/gossip_store.c \ diff --git a/common/fee_states.c b/common/fee_states.c new file mode 100644 index 000000000..b8cb60be1 --- /dev/null +++ b/common/fee_states.c @@ -0,0 +1,158 @@ +#include +#include +#include +#include +#include + +/* If we're the finder, it's like an HTLC we added, if they are, it's like + * a HTLC they added. */ +enum htlc_state first_fee_state(enum side funder) +{ + if (funder == LOCAL) + return SENT_ADD_HTLC; + else + return RCVD_ADD_HTLC; +} + +enum htlc_state last_fee_state(enum side funder) +{ + if (funder == LOCAL) + return SENT_ADD_ACK_REVOCATION; + else + return RCVD_ADD_ACK_REVOCATION; +} + +struct fee_states *new_fee_states(const tal_t *ctx, + enum side funder, + const u32 *feerate_per_kw) +{ + struct fee_states *fee_states = tal(ctx, struct fee_states); + + /* Set to NULL except terminal value */ + for (size_t i = 0; i < ARRAY_SIZE(fee_states->feerate); i++) + fee_states->feerate[i] = NULL; + if (feerate_per_kw) + fee_states->feerate[last_fee_state(funder)] + = tal_dup(fee_states, u32, feerate_per_kw); + return fee_states; +} + +struct fee_states *dup_fee_states(const tal_t *ctx, + const struct fee_states *fee_states TAKES) +{ + struct fee_states *n; + + if (taken(fee_states)) + return cast_const(struct fee_states *, + tal_steal(ctx, fee_states)); + n = tal_dup(ctx, struct fee_states, fee_states); + for (size_t i = 0; i < ARRAY_SIZE(n->feerate); i++) { + if (n->feerate[i]) + n->feerate[i] = tal_dup(n, u32, n->feerate[i]); + } + return n; +} + +u32 get_feerate(const struct fee_states *fee_states, + enum side funder, + enum side side) +{ + /* The first non-NULL feerate committed to this side is current */ + for (enum htlc_state i = first_fee_state(funder); + i <= last_fee_state(funder); + i++) { + if (!fee_states->feerate[i]) + continue; + if (!(htlc_state_flags(i) & HTLC_FLAG(side, HTLC_F_COMMITTED))) + continue; + return *fee_states->feerate[i]; + } + + /* Some feerate should always be set! */ + abort(); +} + +void start_fee_update(struct fee_states *fee_states, + enum side funder, + u32 feerate_per_kw) +{ + enum htlc_state start = first_fee_state(funder); + + /* BOLT #2: + * Unlike an HTLC, `update_fee` is never closed but simply replaced. + */ + if (fee_states->feerate[start] == NULL) + fee_states->feerate[start] = tal(fee_states, u32); + *fee_states->feerate[start] = feerate_per_kw; +} + +bool inc_fee_state(struct fee_states *fee_states, enum htlc_state hstate) +{ + /* These only advance through ADDING states. */ + assert(htlc_state_flags(hstate) & HTLC_ADDING); + + if (!fee_states->feerate[hstate]) + return false; + + /* FIXME: We can never clash, except at final state unless someone + * has violated protocol (eg, send two revoke_and_ack back-to-back) */ + tal_free(fee_states->feerate[hstate+1]); + fee_states->feerate[hstate+1] = fee_states->feerate[hstate]; + fee_states->feerate[hstate] = NULL; + return true; +} + +struct fee_states *fromwire_fee_states(const tal_t *ctx, + const u8 **cursor, size_t *max) +{ + struct fee_states *fee_states = tal(ctx, struct fee_states); + + for (enum htlc_state i = 0; i < ARRAY_SIZE(fee_states->feerate); i++) { + if (fromwire_bool(cursor, max)) { + fee_states->feerate[i] = tal(fee_states, u32); + *fee_states->feerate[i] = fromwire_u32(cursor, max); + } else { + fee_states->feerate[i] = NULL; + } + } + if (!*cursor) + return tal_free(fee_states); + return fee_states; +} + +void towire_fee_states(u8 **pptr, const struct fee_states *fee_states) +{ + for (enum htlc_state i = 0; i < ARRAY_SIZE(fee_states->feerate); i++) { + /* We don't send uncommitted feestates */ + if (!(htlc_state_flags(i) & (HTLC_REMOTE_F_COMMITTED + | HTLC_LOCAL_F_COMMITTED)) + || fee_states->feerate[i] == NULL) { + towire_bool(pptr, false); + continue; + } + towire_bool(pptr, true); + towire_u32(pptr, *fee_states->feerate[i]); + } +} + +/* FIXME: we don't know funder inside fromwire_fee_states, so can't do + * this there :( */ +bool fee_states_valid(const struct fee_states *fee_states, enum side funder) +{ + return fee_states->feerate[last_fee_state(funder)] != NULL; +} + +static const char *fmt_fee_states(const tal_t *ctx, + const struct fee_states *fee_states) +{ + char *ret = tal_strdup(ctx, "{"); + for (enum htlc_state i = 0; i < ARRAY_SIZE(fee_states->feerate); i++) { + if (fee_states->feerate[i] != NULL) + tal_append_fmt(&ret, " %s:%u", + htlc_state_name(i), + *fee_states->feerate[i]); + } + tal_append_fmt(&ret, " }"); + return ret; +} +REGISTER_TYPE_TO_STRING(fee_states, fmt_fee_states); diff --git a/common/fee_states.h b/common/fee_states.h new file mode 100644 index 000000000..606a057bd --- /dev/null +++ b/common/fee_states.h @@ -0,0 +1,88 @@ +#ifndef LIGHTNING_COMMON_FEE_STATES_H +#define LIGHTNING_COMMON_FEE_STATES_H +#include "config.h" +#include +#include + +struct fee_states { + /* Current feerate in satoshis per 1000 weight: goes through same + * state machine as htlc addition, but can only have one rate at a + * time in any state and are never removed. + * + * We need to know if there's an actual change pending though (even if + * it's a "change" to an idential feerate!) so we use pointers. + */ + u32 *feerate[HTLC_STATE_INVALID]; +}; + +/** + * new_fee_states: Initialize a fee_states structure as at open-of-channel. + * @ctx: the tal ctx to allocate off + * @funder: which side funded the channel (and thus, proposes fee updates). + * @feerate_per_kw: the initial feerate (if any). + */ +struct fee_states *new_fee_states(const tal_t *ctx, + enum side funder, + const u32 *feerate_per_kw); + +/** + * dup_fee_states: copy a fee_states structure. + * @ctx: the tal ctx to allocate off + * @fee_states: the fee_states to copy. + */ +struct fee_states *dup_fee_states(const tal_t *ctx, + const struct fee_states *fee_states TAKES); + +/** + * get_feerate: Get the current feerate + * @fee_states: the fee state machine + * @funder: which side funded the channel (and thus, proposes fee updates). + * @side: which side to get the feerate for + */ +u32 get_feerate(const struct fee_states *fee_states, + enum side funder, + enum side side); + +/** + * first_fee_state: get the initial fee state. + * @funder: which side funded the channel (and thus, proposes fee updates). + */ +enum htlc_state first_fee_state(enum side funder); + +/** + * last_fee_state: get the final fee state. + * @funder: which side funded the channel (and thus, proposes fee updates). + */ +enum htlc_state last_fee_state(enum side funder); + +/** + * start_fee_update: feed a new fee update into state machine. + * @fee_states: the fee state machine + * @funder: which side funded the channel (and thus, proposes fee updates). + * @feerate_per_kw: the new feerate. + */ +void start_fee_update(struct fee_states *fee_states, + enum side funder, + u32 feerate_per_kw); + +/** + * inc_fee_state: move this feerate to the next state. + * @fee_states: the fee state machine + * @hstate: state + * + * Moves fee_states[hstate] to fee_states[hstate+1], if not NULL. + * Returns true if it wasn't NULL. + */ +bool inc_fee_state(struct fee_states *fee_states, enum htlc_state hstate); + +/* Marshal and unmarshal */ +void towire_fee_states(u8 **pptr, const struct fee_states *fee_states); +/* FIXME: You must check that fee_states_valid! */ +struct fee_states *fromwire_fee_states(const tal_t *ctx, + const u8 **cursor, size_t *max); + +/** + * Is this fee_state struct valid for this funding side? + */ +bool fee_states_valid(const struct fee_states *fee_states, enum side funder); +#endif /* LIGHTNING_COMMON_FEE_STATES_H */ diff --git a/common/type_to_string.h b/common/type_to_string.h index e35d87b38..209c26e6a 100644 --- a/common/type_to_string.h +++ b/common/type_to_string.h @@ -33,6 +33,7 @@ union printable_types { const struct channel *channel; const struct amount_msat *amount_msat; const struct amount_sat *amount_sat; + const struct fee_states *fee_states; const char *charp_; }; diff --git a/openingd/Makefile b/openingd/Makefile index 31308c705..ea146d7e1 100644 --- a/openingd/Makefile +++ b/openingd/Makefile @@ -48,11 +48,13 @@ OPENINGD_COMMON_OBJS := \ common/derive_basepoints.o \ common/dev_disconnect.o \ common/features.o \ + common/fee_states.o \ common/funding_tx.o \ common/gen_status_wire.o \ common/gen_peer_status_wire.o \ common/gossip_rcvd_filter.o \ common/gossip_store.o \ + common/htlc_state.o \ common/htlc_wire.o \ common/initial_channel.o \ common/initial_commit_tx.o \