Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do complete fee tracking. #3329

Merged
merged 8 commits into from
Dec 12, 2019
1 change: 1 addition & 0 deletions channeld/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
1 change: 1 addition & 0 deletions common/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
158 changes: 158 additions & 0 deletions common/fee_states.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#include <ccan/array_size/array_size.h>
#include <ccan/cast/cast.h>
#include <common/fee_states.h>
#include <common/type_to_string.h>
#include <wire/wire.h>

/* 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]);
}
Comment on lines +49 to +52
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a no-op to me, it's assigning a copy of the array member to itself, or am I missing something?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should probably be n->feerate[i] = tal_dup(n, u32, fee_states->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);
88 changes: 88 additions & 0 deletions common/fee_states.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#ifndef LIGHTNING_COMMON_FEE_STATES_H
#define LIGHTNING_COMMON_FEE_STATES_H
#include "config.h"
#include <ccan/tal/tal.h>
#include <common/htlc.h>

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 */
1 change: 1 addition & 0 deletions common/type_to_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_;
};

Expand Down
2 changes: 2 additions & 0 deletions openingd/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down