From 5d0390f637544b6227dd372918802eff521289d8 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Sat, 9 Mar 2019 21:29:39 +0100 Subject: [PATCH] json: add cmd setchannelfee and wire to channeld * adds the channeld_specific_feerates wire message 1029 * adds a json command handler * adds u32 access methods for amount_msat --- channeld/channel_wire.csv | 4 + channeld/channeld.c | 19 ++++ common/amount.c | 15 +++ common/amount.h | 9 ++ lightningd/channel_control.c | 1 + lightningd/peer_control.c | 110 ++++++++++++++++++-- lightningd/test/run-invoice-select-inchan.c | 10 +- wallet/test/run-wallet.c | 10 +- 8 files changed, 168 insertions(+), 10 deletions(-) diff --git a/channeld/channel_wire.csv b/channeld/channel_wire.csv index 2cc526575..82c55d187 100644 --- a/channeld/channel_wire.csv +++ b/channeld/channel_wire.csv @@ -178,3 +178,7 @@ channel_dev_memleak_reply,,leak,bool channel_fail_fallen_behind,1028 channel_fail_fallen_behind,,remote_per_commitment_point,struct pubkey +# Handle a channel specific feerate base ppm configuration +channel_specific_feerates,1029 +channel_specific_feerates,,feerate_base,u32 +channel_specific_feerates,,feerate_ppm,u32 diff --git a/channeld/channeld.c b/channeld/channeld.c index 6efc22dee..143cc01ca 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -2528,6 +2528,22 @@ static void handle_feerates(struct peer *peer, const u8 *inmsg) } } +static void handle_specific_feerates(struct peer *peer, const u8 *inmsg) +{ + u32 base_old = peer->fee_base; + u32 per_satoshi_old = peer->fee_per_satoshi; + + if (!fromwire_channel_specific_feerates(inmsg, + &peer->fee_base, + &peer->fee_per_satoshi)) + master_badmsg(WIRE_CHANNEL_SPECIFIC_FEERATES, inmsg); + + /* only send channel updates if values actually changed */ + if (peer->fee_base != base_old || peer->fee_per_satoshi != per_satoshi_old) + send_channel_update(peer, 0); +} + + static void handle_preimage(struct peer *peer, const u8 *inmsg) { struct fulfilled_htlc fulfilled_htlc; @@ -2648,6 +2664,9 @@ static void req_in(struct peer *peer, const u8 *msg) case WIRE_CHANNEL_FAIL_HTLC: handle_fail(peer, msg); return; + case WIRE_CHANNEL_SPECIFIC_FEERATES: + handle_specific_feerates(peer, msg); + return; case WIRE_CHANNEL_SEND_SHUTDOWN: handle_shutdown_cmd(peer, msg); return; diff --git a/common/amount.c b/common/amount.c index 96cee1f85..c090418ed 100644 --- a/common/amount.c +++ b/common/amount.c @@ -362,6 +362,21 @@ bool amount_msat_less_eq_sat(struct amount_msat msat, struct amount_sat sat) return msat.millisatoshis <= msat_from_sat.millisatoshis; } +bool amount_msat_overflow_u32(struct amount_msat msat) +{ + return amount_msat_greater_eq(msat, AMOUNT_MSAT(0x100000000)); +} + +u32 amount_msat_to_u32(struct amount_msat msat) +{ + return (u32)msat.millisatoshis; +} + +void amount_msat_from_u32(struct amount_msat *msat, u32 value) +{ + msat->millisatoshis = (u64)value; +} + bool amount_msat_fee(struct amount_msat *fee, struct amount_msat amt, u32 fee_base_msat, diff --git a/common/amount.h b/common/amount.h index 6df3e7c64..a16654c8f 100644 --- a/common/amount.h +++ b/common/amount.h @@ -98,6 +98,15 @@ bool amount_msat_less_sat(struct amount_msat msat, struct amount_sat sat); /* Is msat <= sat? */ bool amount_msat_less_eq_sat(struct amount_msat msat, struct amount_sat sat); +/* Does value overflow u32 bit */ +bool amount_msat_overflow_u32(struct amount_msat msat); + +/* Return unchecked u32 value */ +u32 amount_msat_to_u32(struct amount_msat msat); + +/* Set by u32 value */ +void amount_msat_from_u32(struct amount_msat *msat, u32 value); + /* Common operation: what is the HTLC fee for given feerate? Can overflow! */ WARN_UNUSED_RESULT bool amount_msat_fee(struct amount_msat *fee, struct amount_msat amt, diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 01e52a18b..1c1d7949f 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -232,6 +232,7 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) case WIRE_CHANNEL_SEND_SHUTDOWN: case WIRE_CHANNEL_DEV_REENABLE_COMMIT: case WIRE_CHANNEL_FEERATES: + case WIRE_CHANNEL_SPECIFIC_FEERATES: case WIRE_CHANNEL_DEV_MEMLEAK: /* Replies go to requests. */ case WIRE_CHANNEL_OFFER_HTLC_REPLY: diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index ff5d7246b..bea793f89 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1034,7 +1034,7 @@ AUTODATA(json_command, &listpeers_command); static struct command_result * command_find_channel(struct command *cmd, const char *buffer, const jsmntok_t *tok, - struct channel **channel, struct peer **peer_out) + struct channel **channel) { struct lightningd *ld = cmd->ld; struct channel_id cid; @@ -1050,10 +1050,8 @@ command_find_channel(struct command *cmd, derive_channel_id(&channel_cid, &(*channel)->funding_txid, (*channel)->funding_outnum); - if (channel_id_eq(&channel_cid, &cid)) { - if (peer_out) *peer_out = peer; + if (channel_id_eq(&channel_cid, &cid)) return NULL; - } } return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Channel ID not found: '%.*s'", @@ -1066,10 +1064,8 @@ command_find_channel(struct command *cmd, if (!*channel) continue; if ((*channel)->scid - && (*channel)->scid->u64 == scid.u64) { - if (peer_out) *peer_out = peer; + && (*channel)->scid->u64 == scid.u64) return NULL; - } } return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Short channel ID not found: '%.*s'", @@ -1108,7 +1104,7 @@ static struct command_result *json_close(struct command *cmd, channel = peer_active_channel(peer); else { struct command_result *res; - res = command_find_channel(cmd, buffer, idtok, &channel, NULL); + res = command_find_channel(cmd, buffer, idtok, &channel); if (res) return res; } @@ -1343,6 +1339,104 @@ static const struct json_command getinfo_command = { }; AUTODATA(json_command, &getinfo_command); + +static struct command_result *json_setchannelfee(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct json_stream *response; + const jsmntok_t *idtok; + struct peer *peer; + struct channel *channel; + struct channel_id cid; + struct command_result *res; + struct amount_msat *base, base_default; + u32 *ppm, ppm_default; + + /* Use daemon config values base/ppm as default */ + amount_msat_from_u32(&base_default, cmd->ld->config.fee_base); + ppm_default = cmd->ld->config.fee_per_satoshi; + + /* Parse the JSON command */ + if (!param(cmd, buffer, params, + p_req("id", param_tok, &idtok), + p_opt_def("base", param_msat, &base, base_default), + p_opt_def("ppm", param_number, &ppm, ppm_default), + NULL)) + return command_param_failed(); + + /* Check base for u32 overflow */ + if (amount_msat_overflow_u32(*base)) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Value 'base' exceeds u32 max"); + + /* Find the channel */ + peer = peer_from_json(cmd->ld, buffer, idtok); + if (peer) { + channel = peer_active_channel(peer); + if (!channel) + return command_fail(cmd, LIGHTNINGD, + "Could not find active channel of peer with that id"); + } else { + res = command_find_channel(cmd, buffer, idtok, &channel); + if (res) + return res; + /* check channel is found and in valid state */ + if (!channel) + return command_fail(cmd, LIGHTNINGD, + "Could not find channel with that id"); + peer = channel->peer; + } + + if (channel->state != CHANNELD_NORMAL && + channel->state != CHANNELD_AWAITING_LOCKIN) + return command_fail(cmd, LIGHTNINGD, + "Channel is in state %s", channel_state_name(channel)); + + /* set, notify and save new values */ + channel->feerate_base = amount_msat_to_u32(*base); + channel->feerate_ppm = *ppm; + + /* tell channeld to make a send_channel_update */ + if (channel->owner && streq(channel->owner->name, "lightning_channeld")) + subd_send_msg(channel->owner, + take(towire_channel_specific_feerates(channel, + amount_msat_to_u32(*base), *ppm))); + + /* save values to database */ + wallet_channel_save(cmd->ld->wallet, channel); + + /* get channel id */ + derive_channel_id(&cid, &channel->funding_txid, channel->funding_outnum); + + /* write response */ + response = json_stream_success(cmd); + json_object_start(response, NULL); + json_add_num(response, "base", amount_msat_to_u32(*base)); + json_add_num(response, "ppm", *ppm); + json_add_pubkey(response, "peer_id", &peer->id); + json_add_string(response, "channel_id", + type_to_string(tmpctx, struct channel_id, &cid)); + if (channel->scid) + json_add_short_channel_id(response, "short_channel_id", channel->scid); + json_object_end(response); + return command_success(cmd, response); +} + +static const struct json_command setchannelfee_command = { + "setchannelfee", + json_setchannelfee, + "Sets specific routing fees for channel with {id} " + "(either peer ID, channel ID or short channel ID). " + "The routing fees are defined by a fixed {base} (msat) " + "and a {ppm} value (proportional per millionth)." + "To disable channel specific fees and restore global values, " + "just leave out the optional {base} and {ppm} values. " + "{base} can also be supplied in other units, i.e. '1sat'." +}; +AUTODATA(json_command, &setchannelfee_command); + #if DEVELOPER static struct command_result *json_sign_last_tx(struct command *cmd, const char *buffer, diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 519c946a7..36564de83 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -131,7 +131,7 @@ void json_add_amount_msat(struct json_stream *result UNNEEDED, void json_add_amount_sat(struct json_stream *result UNNEEDED, struct amount_sat sat UNNEEDED, const char *rawfieldname UNNEEDED, - const char *satfieldname) + const char *msatfieldname) { fprintf(stderr, "json_add_amount_sat called!\n"); abort(); } /* Generated stub for json_add_bool */ @@ -311,6 +311,11 @@ struct command_result *param_loglevel(struct command *cmd UNNEEDED, const jsmntok_t *tok UNNEEDED, enum log_level **level UNNEEDED) { fprintf(stderr, "param_loglevel called!\n"); abort(); } +/* Generated stub for param_msat */ +struct command_result *param_msat(struct command *cmd UNNEEDED, const char *name UNNEEDED, + const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct amount_msat **msat UNNEEDED) +{ fprintf(stderr, "param_msat called!\n"); abort(); } /* Generated stub for param_number */ struct command_result *param_number(struct command *cmd UNNEEDED, const char *name UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, @@ -404,6 +409,9 @@ u8 *towire_channel_dev_reenable_commit(const tal_t *ctx UNNEEDED) /* Generated stub for towire_channel_send_shutdown */ u8 *towire_channel_send_shutdown(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_channel_send_shutdown called!\n"); abort(); } +/* Generated stub for towire_channel_specific_feerates */ +u8 *towire_channel_specific_feerates(const tal_t *ctx UNNEEDED, u32 feerate_base UNNEEDED, u32 feerate_ppm UNNEEDED) +{ fprintf(stderr, "towire_channel_specific_feerates called!\n"); abort(); } /* Generated stub for towire_connectctl_connect_to_peer */ u8 *towire_connectctl_connect_to_peer(const tal_t *ctx UNNEEDED, const struct pubkey *id UNNEEDED, u32 seconds_waited UNNEEDED, const struct wireaddr_internal *addrhint UNNEEDED) { fprintf(stderr, "towire_connectctl_connect_to_peer called!\n"); abort(); } diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index aaa484524..250a6b071 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -210,7 +210,7 @@ void json_add_amount_msat(struct json_stream *result UNNEEDED, void json_add_amount_sat(struct json_stream *result UNNEEDED, struct amount_sat sat UNNEEDED, const char *rawfieldname UNNEEDED, - const char *satfieldname) + const char *msatfieldname) { fprintf(stderr, "json_add_amount_sat called!\n"); abort(); } /* Generated stub for json_add_bool */ @@ -371,6 +371,11 @@ struct command_result *param_loglevel(struct command *cmd UNNEEDED, const jsmntok_t *tok UNNEEDED, enum log_level **level UNNEEDED) { fprintf(stderr, "param_loglevel called!\n"); abort(); } +/* Generated stub for param_msat */ +struct command_result *param_msat(struct command *cmd UNNEEDED, const char *name UNNEEDED, + const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct amount_msat **msat UNNEEDED) +{ fprintf(stderr, "param_msat called!\n"); abort(); } /* Generated stub for param_number */ struct command_result *param_number(struct command *cmd UNNEEDED, const char *name UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, @@ -492,6 +497,9 @@ u8 *towire_channel_sending_commitsig_reply(const tal_t *ctx UNNEEDED) /* Generated stub for towire_channel_send_shutdown */ u8 *towire_channel_send_shutdown(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_channel_send_shutdown called!\n"); abort(); } +/* Generated stub for towire_channel_specific_feerates */ +u8 *towire_channel_specific_feerates(const tal_t *ctx UNNEEDED, u32 feerate_base UNNEEDED, u32 feerate_ppm UNNEEDED) +{ fprintf(stderr, "towire_channel_specific_feerates called!\n"); abort(); } /* Generated stub for towire_connectctl_connect_to_peer */ u8 *towire_connectctl_connect_to_peer(const tal_t *ctx UNNEEDED, const struct pubkey *id UNNEEDED, u32 seconds_waited UNNEEDED, const struct wireaddr_internal *addrhint UNNEEDED) { fprintf(stderr, "towire_connectctl_connect_to_peer called!\n"); abort(); }