diff --git a/channeld/channel.c b/channeld/channel.c index b01d5be6a..192c605a6 100644 --- a/channeld/channel.c +++ b/channeld/channel.c @@ -624,6 +624,9 @@ static void handle_peer_feechange(struct peer *peer, const u8 *msg) &peer->channel_id, "update_fee from non-funder?"); + status_trace("update_fee %u, range %u-%u", + feerate, peer->feerate_min, peer->feerate_max); + /* BOLT #2: * * A receiving node SHOULD fail the channel if the `update_fee` is too diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 479510a7c..9ab01a01c 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -71,6 +71,9 @@ struct config { /* Channel update interval */ u32 channel_update_interval; + + /* Do we let the funder set any fee rate they want */ + bool ignore_fee_limits; }; struct lightningd { diff --git a/lightningd/options.c b/lightningd/options.c index 3e3581fa9..40b738742 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -204,6 +204,9 @@ static char *opt_set_fee_rates(const char *arg, struct chain_topology *topo) static void config_register_opts(struct lightningd *ld) { + opt_register_arg("--ignore-fee-limits", opt_set_bool_arg, opt_show_bool, + &ld->config.ignore_fee_limits, + "(DANGEROUS) allow peer to set any feerate"); opt_register_arg("--locktime-blocks", opt_set_u32, opt_show_u32, &ld->config.locktime_blocks, "Blocks before peer can unilaterally spend funds"); @@ -346,6 +349,9 @@ static const struct config testnet_config = { /* Send a keepalive update at least every week, prune every twice that */ .channel_update_interval = 1209600/2, + + /* Testnet sucks */ + .ignore_fee_limits = true, }; /* aka. "Dude, where's my coins?" */ @@ -407,6 +413,9 @@ static const struct config mainnet_config = { /* Send a keepalive update at least every week, prune every twice that */ .channel_update_interval = 1209600/2, + + /* Mainnet should have more stable fees */ + .ignore_fee_limits = false, }; static void check_config(struct lightningd *ld) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 5c71c04ed..2c7baddd2 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1938,6 +1938,29 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) return 0; } +u32 feerate_min(struct lightningd *ld) +{ + if (ld->config.ignore_fee_limits) + return 1; + + /* Set this to average of slow and normal.*/ + return (get_feerate(ld->topology, FEERATE_SLOW) + + get_feerate(ld->topology, FEERATE_NORMAL)) / 2; +} + +/* BOLT #2: + * + * Given the variance in fees, and the fact that the transaction may + * be spent in the future, it's a good idea for the fee payer to keep + * a good margin, say 5x the expected fee requirement */ +u32 feerate_max(struct lightningd *ld) +{ + if (ld->config.ignore_fee_limits) + return UINT_MAX; + + return get_feerate(ld->topology, FEERATE_IMMEDIATE) * 5; +} + static bool peer_start_channeld(struct peer *peer, const struct crypto_state *cs, u64 gossip_index, @@ -2019,6 +2042,10 @@ static bool peer_start_channeld(struct peer *peer, num_revocations = revocations_received(&peer->their_shachain.chain); + /* Warn once. */ + if (peer->ld->config.ignore_fee_limits) + log_unusual(peer->log, "Ignoring fee limits!"); + initmsg = towire_channel_init(tmpctx, &get_chainparams(peer->ld) ->genesis_blockhash, @@ -2028,8 +2055,8 @@ static bool peer_start_channeld(struct peer *peer, &peer->our_config, &peer->channel_info->their_config, peer->channel_info->feerate_per_kw, - get_feerate(peer->ld->topology, FEERATE_NORMAL), - get_feerate(peer->ld->topology, FEERATE_IMMEDIATE) * 5, + feerate_min(peer->ld), + feerate_max(peer->ld), peer->last_sig, cs, gossip_index, &peer->channel_info->remote_fundingkey, diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index 31d8640b2..1b8793566 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -220,4 +220,9 @@ void setup_listeners(struct lightningd *ld); void activate_peers(struct lightningd *ld); void free_htlcs(struct lightningd *ld, const struct peer *peer); + +/* Get range of feerates to insist other side abide by for normal channels. */ +u32 feerate_min(struct lightningd *ld); +u32 feerate_max(struct lightningd *ld); + #endif /* LIGHTNING_LIGHTNINGD_PEER_CONTROL_H */ diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index ef16c4b19..361759dd7 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -1501,15 +1501,11 @@ void notify_feerate_change(struct lightningd *ld) if (!peer->owner) continue; - /* FIXME: low bound is probably too low. */ msg = towire_channel_feerates(peer, get_feerate(ld->topology, FEERATE_IMMEDIATE), - get_feerate(ld->topology, - FEERATE_NORMAL) / 2, - get_feerate(ld->topology, - FEERATE_IMMEDIATE) - * 5); + feerate_min(ld), + feerate_max(ld)); subd_send_msg(peer->owner, take(msg)); } } diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index 596c3b7f5..1dc618af7 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -2709,6 +2709,36 @@ class LightningDTests(BaseLightningDTests): l1.daemon.wait_for_log('onchaind complete, forgetting peer') l2.daemon.wait_for_log('onchaind complete, forgetting peer') + def test_fee_limits(self): + # FIXME: Test case where opening denied. + l1, l2 = self.connect() + self.fund_channel(l1, l2, 10**6) + + # L1 asks for stupid low fees + l1.rpc.dev_setfees(15) + + l1.daemon.wait_for_log('STATUS_FAIL_PEER_BAD') + + # Restore to normal. + l1.rpc.dev_setfees(15000) + + # Try with node which sets --ignore-fee-limits + l3 = self.node_factory.get_node(options=['--ignore-fee-limits=true']) + l1.rpc.connect(l3.info['id'], 'localhost', l3.info['port']) + + self.fund_channel(l1, l3, 10**6) + + # Try stupid high fees + l1.rpc.dev_setfees(15000 * 10) + + l3.daemon.wait_for_log('peer_in WIRE_UPDATE_FEE') + l3.daemon.wait_for_log('peer_in WIRE_COMMITMENT_SIGNED') + + # Now shutdown cleanly. + l1.rpc.close(l3.info['id']) + l1.daemon.wait_for_log('-> CLOSINGD_COMPLETE') + l3.daemon.wait_for_log('-> CLOSINGD_COMPLETE') + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_update_fee_reconnect(self): # Disconnect after first commitsig. diff --git a/tests/utils.py b/tests/utils.py index 1fa0c9959..5e34b743e 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -244,7 +244,8 @@ class LightningD(TailableProc): '--lightning-dir={}'.format(lightning_dir), '--port={}'.format(port), '--override-fee-rates=15000/7500/1000', - '--network=regtest' + '--network=regtest', + '--ignore-fee-limits=false' ] if DEVELOPER: self.cmd_line += ['--dev-broadcast-interval=1000']