closingd: configurable closing fee negotiation step

When negotiating the transaction fee for closing a channel [1], we used
to always pick the middle of the range between our proposal and the
peer's proposal.

Introduce a new option `fee_negotiation_step` to the close command, so
the peer who initiates the close can choose his back off step.

Partially resolves https://github.com/ElementsProject/lightning/issues/3270

[1] https://github.com/lightningnetwork/lightning-rfc/blob/master/02-peer-protocol.md#closing-negotiation-closing_signed

Changelog-Added: New optional parameter to the `close` command to control the closing transaction fee negotiation back off step
This commit is contained in:
Vasil Dimov 2020-01-03 14:08:29 +01:00 committed by Rusty Russell
parent 3ce0552dd4
commit 158d2212c2
13 changed files with 288 additions and 66 deletions

View File

@ -21,6 +21,8 @@ msgdata,closing_init,local_scriptpubkey_len,u16,
msgdata,closing_init,local_scriptpubkey,u8,local_scriptpubkey_len
msgdata,closing_init,remote_scriptpubkey_len,u16,
msgdata,closing_init,remote_scriptpubkey,u8,remote_scriptpubkey_len
msgdata,closing_init,fee_negotiation_step,u64,
msgdata,closing_init,fee_negotiation_step_unit,u8,
msgdata,closing_init,reconnected,bool,
msgdata,closing_init,next_index_local,u64,
msgdata,closing_init,next_index_remote,u64,

1 #include <common/cryptomsg.h>
21 msgdata,closing_init,local_scriptpubkey,u8,local_scriptpubkey_len
22 msgdata,closing_init,remote_scriptpubkey_len,u16,
23 msgdata,closing_init,remote_scriptpubkey,u8,remote_scriptpubkey_len
24 msgdata,closing_init,fee_negotiation_step,u64,
25 msgdata,closing_init,fee_negotiation_step_unit,u8,
26 msgdata,closing_init,reconnected,bool,
27 msgdata,closing_init,next_index_local,u64,
28 msgdata,closing_init,next_index_remote,u64,

View File

@ -2,6 +2,7 @@
#include <ccan/fdpass/fdpass.h>
#include <closingd/gen_closing_wire.h>
#include <common/close_tx.h>
#include <common/closing_fee.h>
#include <common/crypto_sync.h>
#include <common/derive_basepoints.h>
#include <common/htlc.h>
@ -489,13 +490,14 @@ static void adjust_feerange(struct feerange *feerange,
}
/* Figure out what we should offer now. */
static struct amount_sat adjust_offer(struct per_peer_state *pps,
const struct channel_id *channel_id,
const struct feerange *feerange,
struct amount_sat remote_offer,
struct amount_sat min_fee_to_accept)
static struct amount_sat
adjust_offer(struct per_peer_state *pps, const struct channel_id *channel_id,
const struct feerange *feerange, struct amount_sat remote_offer,
struct amount_sat min_fee_to_accept, u64 fee_negotiation_step,
u8 fee_negotiation_step_unit)
{
struct amount_sat min_plus_one, avg;
struct amount_sat min_plus_one, range_len, step_sat, result;
struct amount_msat step_msat;
/* Within 1 satoshi? Agree. */
if (!amount_sat_add(&min_plus_one, feerange->min, AMOUNT_SAT(1)))
@ -507,8 +509,15 @@ static struct amount_sat adjust_offer(struct per_peer_state *pps,
if (amount_sat_greater_eq(min_plus_one, feerange->max))
return remote_offer;
/* feerange has already been adjusted so that our new offer is ok to be
* any number in [feerange->min, feerange->max] and after the following
* min_fee_to_accept is in that range. Thus, pick a fee in
* [min_fee_to_accept, feerange->max]. */
if (amount_sat_greater(feerange->min, min_fee_to_accept))
min_fee_to_accept = feerange->min;
/* Max is below our minimum acceptable? */
if (amount_sat_less(feerange->max, min_fee_to_accept))
if (!amount_sat_sub(&range_len, feerange->max, min_fee_to_accept))
peer_failed(pps, channel_id,
"Feerange %s-%s"
" below minimum acceptable %s",
@ -519,18 +528,44 @@ static struct amount_sat adjust_offer(struct per_peer_state *pps,
type_to_string(tmpctx, struct amount_sat,
&min_fee_to_accept));
/* Bisect between our minimum and max. */
if (amount_sat_greater(feerange->min, min_fee_to_accept))
min_fee_to_accept = feerange->min;
if (fee_negotiation_step_unit ==
CLOSING_FEE_NEGOTIATION_STEP_UNIT_SATOSHI) {
/* -1 because the range boundary has already been adjusted with
* one from our previous proposal. So, if the user requested a
* step of 1 satoshi at a time we should just return our end of
* the range from this function. */
amount_msat_from_u64(&step_msat,
(fee_negotiation_step - 1) * MSAT_PER_SAT);
} else {
/* fee_negotiation_step is e.g. 20 to designate 20% from
* range_len (which is in satoshi), so:
* range_len * fee_negotiation_step / 100 [sat]
* is equivalent to:
* range_len * fee_negotiation_step * 10 [msat] */
amount_msat_from_u64(&step_msat,
range_len.satoshis /* Raw: % calc */ *
fee_negotiation_step * 10);
}
if (!amount_sat_add(&avg, feerange->max, min_fee_to_accept))
peer_failed(pps, channel_id,
"Fee offer %s max too large",
type_to_string(tmpctx, struct amount_sat,
&feerange->max));
step_sat = amount_msat_to_sat_round_down(step_msat);
avg.satoshis /= 2; /* Raw: average calculation */
return avg;
if (feerange->higher_side == LOCAL) {
if (!amount_sat_sub(&result, feerange->max, step_sat))
/* step_sat > feerange->max, unlikely */
return min_fee_to_accept;
if (amount_sat_less_eq(result, min_fee_to_accept))
return min_fee_to_accept;
} else {
if (!amount_sat_add(&result, min_fee_to_accept, step_sat))
/* overflow, unlikely */
return feerange->max;
if (amount_sat_greater_eq(result, feerange->max))
return feerange->max;
}
return result;
}
#if DEVELOPER
@ -570,6 +605,9 @@ int main(int argc, char *argv[])
struct feerange feerange;
enum side funder;
u8 *scriptpubkey[NUM_SIDES], *funding_wscript;
u64 fee_negotiation_step;
u8 fee_negotiation_step_unit;
char fee_negotiation_step_str[32]; /* fee_negotiation_step + "sat" */
struct channel_id channel_id;
bool reconnected;
u64 next_index[NUM_SIDES], revocations_received;
@ -597,6 +635,8 @@ int main(int argc, char *argv[])
&offer[LOCAL],
&scriptpubkey[LOCAL],
&scriptpubkey[REMOTE],
&fee_negotiation_step,
&fee_negotiation_step_unit,
&reconnected,
&next_index[LOCAL],
&next_index[REMOTE],
@ -609,6 +649,13 @@ int main(int argc, char *argv[])
/* stdin == requests, 3 == peer, 4 = gossip, 5 = gossip_store, 6 = hsmd */
per_peer_state_set_fds(pps, 3, 4, 5);
snprintf(fee_negotiation_step_str, sizeof(fee_negotiation_step_str),
"%" PRIu64 "%s", fee_negotiation_step,
fee_negotiation_step_unit ==
CLOSING_FEE_NEGOTIATION_STEP_UNIT_PERCENTAGE
? "%"
: "sat");
status_debug("out = %s/%s",
type_to_string(tmpctx, struct amount_sat, &out[LOCAL]),
type_to_string(tmpctx, struct amount_sat, &out[REMOTE]));
@ -616,6 +663,7 @@ int main(int argc, char *argv[])
type_to_string(tmpctx, struct amount_sat, &our_dust_limit));
status_debug("fee = %s",
type_to_string(tmpctx, struct amount_sat, &offer[LOCAL]));
status_debug("fee negotiation step = %s", fee_negotiation_step_str);
derive_channel_id(&channel_id, &funding_txid, funding_txout);
funding_wscript = bitcoin_redeem_2of2(ctx,
@ -628,13 +676,14 @@ int main(int argc, char *argv[])
channel_reestablish, scriptpubkey[LOCAL],
&last_remote_per_commit_secret);
peer_billboard(true, "Negotiating closing fee between %s"
" and %s satoshi (ideal %s)",
type_to_string(tmpctx, struct amount_sat,
&min_fee_to_accept),
type_to_string(tmpctx, struct amount_sat,
&commitment_fee),
type_to_string(tmpctx, struct amount_sat, &offer[LOCAL]));
peer_billboard(
true,
"Negotiating closing fee between %s and %s satoshi (ideal %s) "
"using step %s",
type_to_string(tmpctx, struct amount_sat, &min_fee_to_accept),
type_to_string(tmpctx, struct amount_sat, &commitment_fee),
type_to_string(tmpctx, struct amount_sat, &offer[LOCAL]),
fee_negotiation_step_str);
/* BOLT #2:
*
@ -692,7 +741,9 @@ int main(int argc, char *argv[])
offer[LOCAL] = adjust_offer(pps,
&channel_id,
&feerange, offer[REMOTE],
min_fee_to_accept);
min_fee_to_accept,
fee_negotiation_step,
fee_negotiation_step_unit);
send_offer(pps, chainparams, &channel_id,
funding_pubkey,
scriptpubkey, &funding_txid, funding_txout,

View File

@ -73,6 +73,7 @@ COMMON_SRC_NOGEN := \
COMMON_SRC_GEN := common/gen_status_wire.c common/gen_peer_status_wire.c
COMMON_HEADERS_NOGEN := $(COMMON_SRC_NOGEN:.c=.h) \
common/closing_fee.h \
common/ecdh.h \
common/errcode.h \
common/gossip_constants.h \

16
common/closing_fee.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef LIGHTNING_COMMON_CLOSING_FEE_H
#define LIGHTNING_COMMON_CLOSING_FEE_H
#include "config.h"
#include <ccan/short_types/short_types.h>
/** During closing fee negotiation give up N% of the range between our
* proposal and the peer's proposal on each step. */
static const u8 CLOSING_FEE_NEGOTIATION_STEP_UNIT_PERCENTAGE = 0;
/** During closing fee negotiation give up N satoshi of the range between our
* proposal and the peer's proposal on each step. */
static const u8 CLOSING_FEE_NEGOTIATION_STEP_UNIT_SATOSHI = 1;
#endif /* LIGHTNING_COMMON_CLOSING_FEE_H */

View File

@ -412,11 +412,12 @@ class LightningRpc(UnixDomainSocketRpc):
if args[0] is None and isinstance(args[1], int):
return self._deprecated_close(peer_id, *args, **kwargs)
def _close(peer_id, unilateraltimeout=None, destination=None):
def _close(peer_id, unilateraltimeout=None, destination=None, fee_negotiation_step=None):
payload = {
"id": peer_id,
"unilateraltimeout": unilateraltimeout,
"destination": destination
"destination": destination,
"fee_negotiation_step": fee_negotiation_step
}
return self.call("close", payload)

26
doc/lightning-close.7 generated
View File

@ -3,7 +3,7 @@
lightning-close - Command for closing channels with direct peers
.SH SYNOPSIS
\fBclose\fR \fIid\fR [\fIunilateraltimeout\fR] [\fIdestination\fR]
\fBclose\fR \fIid\fR [\fIunilateraltimeout\fR] [\fIdestination\fR] [\fIfee_negotiation_step\fR]
.SH DESCRIPTION
@ -30,6 +30,30 @@ The \fIdestination\fR can be of any Bitcoin accepted type, including bech32\.
If it isn't specified, the default is a c-lightning wallet address\.
The \fIfee_negotiation_step\fR parameter controls how closing fee
negotiation is performed assuming the peer proposes a fee that is
different than our estimate\. On every negotiation step we must give up
some amount from our proposal towards the peer's proposal\. This parameter
can be an integer in which case it is interpreted as number of satoshis
to step at a time\. Or it can be an integer followed by "%s" to designate
a percentage of the interval to give up\. A few examples, assuming the peer
proposes a closing fee of 3000 satoshi and our estimate shows it must be 4000:
.RS
.IP \[bu]
"10": our next proposal will be 4000-10=3990\.
.IP \[bu]
"10%": our next proposal will be 4000-(10% of (4000-3000))=3900\.
.IP \[bu]
"1": our next proposal will be 3999\. This is the most extreme case when we
insist on our fee as much as possible\.
.IP \[bu]
"100%": our next proposal will be 3000\. This is the most relaxed case when
we quickly accept the peer's proposal\.
The default is "50%"\.
.RE
The peer needs to be live and connected in order to negotiate a mutual
close\. The default of unilaterally closing after 48 hours is usually a
reasonable indication that you can no longer contact the peer\.

View File

@ -4,7 +4,7 @@ lightning-close -- Command for closing channels with direct peers
SYNOPSIS
--------
**close** *id* \[*unilateraltimeout*\] \[*destination*\]
**close** *id* \[*unilateraltimeout*\] \[*destination*\] \[*fee_negotiation_step*\]
DESCRIPTION
-----------
@ -28,6 +28,22 @@ The default is 2 days (172800 seconds).
The *destination* can be of any Bitcoin accepted type, including bech32.
If it isn't specified, the default is a c-lightning wallet address.
The *fee_negotiation_step* parameter controls how closing fee
negotiation is performed assuming the peer proposes a fee that is
different than our estimate. On every negotiation step we must give up
some amount from our proposal towards the peer's proposal. This parameter
can be an integer in which case it is interpreted as number of satoshis
to step at a time. Or it can be an integer followed by "%s" to designate
a percentage of the interval to give up. A few examples, assuming the peer
proposes a closing fee of 3000 satoshi and our estimate shows it must be 4000:
* "10": our next proposal will be 4000-10=3990.
* "10%": our next proposal will be 4000-(10% of (4000-3000))=3900.
* "1": our next proposal will be 3999. This is the most extreme case when we
insist on our fee as much as possible.
* "100%": our next proposal will be 3000. This is the most relaxed case when
we quickly accept the peer's proposal.
The default is "50%".
The peer needs to be live and connected in order to negotiate a mutual
close. The default of unilaterally closing after 48 hours is usually a
reasonable indication that you can no longer contact the peer.

View File

@ -1,6 +1,7 @@
#include <bitcoin/script.h>
#include <ccan/crypto/hkdf_sha256/hkdf_sha256.h>
#include <ccan/tal/str/str.h>
#include <common/closing_fee.h>
#include <common/fee_states.h>
#include <common/json_command.h>
#include <common/jsonrpc_errors.h>
@ -242,6 +243,9 @@ struct channel *new_channel(struct peer *peer, u64 dbid,
channel->shutdown_scriptpubkey[REMOTE]
= tal_steal(channel, remote_shutdown_scriptpubkey);
channel->final_key_idx = final_key_idx;
channel->closing_fee_negotiation_step = 50;
channel->closing_fee_negotiation_step_unit
= CLOSING_FEE_NEGOTIATION_STEP_UNIT_PERCENTAGE;
if (local_shutdown_scriptpubkey)
channel->shutdown_scriptpubkey[LOCAL]
= tal_steal(channel, local_shutdown_scriptpubkey);

View File

@ -94,6 +94,12 @@ struct channel {
/* Address for any final outputs */
u64 final_key_idx;
/* Amount to give up on each step of the closing fee negotiation. */
u64 closing_fee_negotiation_step;
/* Whether closing_fee_negotiation_step is in satoshi or %. */
u8 closing_fee_negotiation_step_unit;
/* Reestablishment stuff: last sent commit and revocation details. */
bool last_was_revoke;
struct changed_htlc *last_sent_commit;

View File

@ -290,6 +290,8 @@ void peer_start_closingd(struct channel *channel,
minfee, feelimit, startfee,
channel->shutdown_scriptpubkey[LOCAL],
channel->shutdown_scriptpubkey[REMOTE],
channel->closing_fee_negotiation_step,
channel->closing_fee_negotiation_step_unit,
reconnected,
channel->next_index[LOCAL],
channel->next_index[REMOTE],

View File

@ -15,6 +15,7 @@
#include <ccan/tal/str/str.h>
#include <channeld/gen_channel_wire.h>
#include <common/addr.h>
#include <common/closing_fee.h>
#include <common/dev_disconnect.h>
#include <common/features.h>
#include <common/htlc_trim.h>
@ -52,6 +53,8 @@
#include <lightningd/options.h>
#include <lightningd/peer_htlcs.h>
#include <lightningd/plugin_hook.h>
#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <wally_bip32.h>
#include <wire/gen_common_wire.h>
@ -1285,12 +1288,16 @@ static struct command_result *json_close(struct command *cmd,
bool do_timeout;
const u8 *close_to_script = NULL;
bool close_script_set;
const char *fee_negotiation_step_str;
char* end;
if (!param(cmd, buffer, params,
p_req("id", param_tok, &idtok),
p_opt_def("unilateraltimeout", param_number, &timeout,
48 * 3600),
p_opt("destination", param_bitcoin_address, &close_to_script),
p_opt("fee_negotiation_step", param_string,
&fee_negotiation_step_str),
NULL))
return command_param_failed();
@ -1356,6 +1363,35 @@ static struct command_result *json_close(struct command *cmd,
} else
close_script_set = false;
if (fee_negotiation_step_str == NULL) {
channel->closing_fee_negotiation_step = 50;
channel->closing_fee_negotiation_step_unit =
CLOSING_FEE_NEGOTIATION_STEP_UNIT_PERCENTAGE;
} else {
channel->closing_fee_negotiation_step =
strtoull(fee_negotiation_step_str, &end, 10);
if (*end == '%') {
if (channel->closing_fee_negotiation_step < 1 ||
channel->closing_fee_negotiation_step > 100)
return command_fail(
cmd, JSONRPC2_INVALID_PARAMS,
"Wrong value given for "
"fee_negotiation_step: \"%s\", the "
"percentage should be between 1 and 100",
fee_negotiation_step_str);
channel->closing_fee_negotiation_step_unit =
CLOSING_FEE_NEGOTIATION_STEP_UNIT_PERCENTAGE;
} else if (*end == '\0')
channel->closing_fee_negotiation_step_unit =
CLOSING_FEE_NEGOTIATION_STEP_UNIT_SATOSHI;
else
return command_fail(
cmd, JSONRPC2_INVALID_PARAMS,
"Wrong value given for fee_negotiation_step: "
"\"%s\", should be an integer or an integer "
"followed by %%",
fee_negotiation_step_str);
}
/* Normal case.
* We allow states shutting down and sigexchange; a previous

View File

@ -360,11 +360,11 @@ def test_closing_specified_destination(node_factory, bitcoind, chainparams):
assert 1 == bitcoind.rpc.gettxout(closetx, output_num1)['confirmations']
def closing_fee(node_factory, bitcoind, chainparams, opts):
rate = opts['funder_feerate_per_kw']
def closing_negotiation_step(node_factory, bitcoind, chainparams, opts):
rate = 29006 # closing fee negotiation starts at 21000
funder = node_factory.get_node(feerates=(rate, rate, rate, rate))
rate = opts['fundee_feerate_per_kw']
rate = 27625 # closing fee negotiation starts at 20000
fundee = node_factory.get_node(feerates=(rate, rate, rate, rate))
funder_id = funder.info['id']
@ -378,26 +378,10 @@ def closing_fee(node_factory, bitcoind, chainparams, opts):
assert bitcoind.rpc.getmempoolinfo()['size'] == 0
if opts['close_initiated_by'] == 'funder':
funder.rpc.close(peer_id=fundee_id)
funder.rpc.close(peer_id=fundee_id, fee_negotiation_step=opts['fee_negotiation_step'])
else:
assert opts['close_initiated_by'] == 'fundee'
fundee.rpc.close(peer_id=funder_id)
# Get the closing transaction from the bitcoind mempool and get its fee
mempool = None
mempool_tx_ids = None
def get_mempool_when_size_1():
nonlocal mempool, mempool_tx_ids
mempool = bitcoind.rpc.getrawmempool(True)
mempool_tx_ids = list(mempool.keys())
return len(mempool_tx_ids) == 1
wait_for(get_mempool_when_size_1)
close_tx_id = mempool_tx_ids[0]
fee_mempool = round(mempool[close_tx_id]['fee'] * 10**8)
fundee.rpc.close(peer_id=funder_id, fee_negotiation_step=opts['fee_negotiation_step'])
# Get the proclaimed closing fee from the two nodes' statuses
@ -418,27 +402,101 @@ def closing_fee(node_factory, bitcoind, chainparams, opts):
wait_for(lambda: get_fee_from_status(funder, fundee_id, 0))
wait_for(lambda: get_fee_from_status(fundee, funder_id, 1))
assert fee_mempool == fees_from_status[0]
assert fee_mempool == fees_from_status[1]
assert fee_mempool == opts['expected_close_fee']
assert opts['expected_close_fee'] == fees_from_status[0]
assert opts['expected_close_fee'] == fees_from_status[1]
# Get the closing transaction from the bitcoind mempool and get its fee
mempool = None
mempool_tx_ids = None
def get_mempool_when_size_1():
nonlocal mempool, mempool_tx_ids
mempool = bitcoind.rpc.getrawmempool(True)
mempool_tx_ids = list(mempool.keys())
return len(mempool_tx_ids) == 1
wait_for(get_mempool_when_size_1)
close_tx_id = mempool_tx_ids[0]
fee_mempool = round(mempool[close_tx_id]['fee'] * 10**8)
assert opts['expected_close_fee'] == fee_mempool
def test_closing_fee(node_factory, bitcoind, chainparams):
"""Test that the closing negotiation strategy works"""
# feerate 27625 -> closing fee negotiation starts at 20000
# feerate 29006 -> closing fee negotiation starts at 21000
def test_closing_negotiation_step_30pct(node_factory, bitcoind, chainparams):
"""Test that the closing fee negotiation step works, 30%"""
opts = {}
opts['fee_negotiation_step'] = '30%'
opts = {
'funder_feerate_per_kw': 29006,
'fundee_feerate_per_kw': 27625,
'close_initiated_by': 'funder',
'expected_close_fee': 33533 if chainparams['elements'] else 20333
}
closing_fee(node_factory, bitcoind, chainparams, opts)
opts['close_initiated_by'] = 'funder'
opts['expected_close_fee'] = 20537 if not chainparams['elements'] else 33870
closing_negotiation_step(node_factory, bitcoind, chainparams, opts)
opts['close_initiated_by'] = 'fundee'
closing_fee(node_factory, bitcoind, chainparams, opts)
opts['expected_close_fee'] = 20233 if not chainparams['elements'] else 33366
closing_negotiation_step(node_factory, bitcoind, chainparams, opts)
def test_closing_negotiation_step_50pct(node_factory, bitcoind, chainparams):
"""Test that the closing fee negotiation step works, 50%, the default"""
opts = {}
opts['fee_negotiation_step'] = '50%'
opts['close_initiated_by'] = 'funder'
opts['expected_close_fee'] = 20334 if not chainparams['elements'] else 33533
closing_negotiation_step(node_factory, bitcoind, chainparams, opts)
opts['close_initiated_by'] = 'fundee'
opts['expected_close_fee'] = 20334 if not chainparams['elements'] else 33533
closing_negotiation_step(node_factory, bitcoind, chainparams, opts)
def test_closing_negotiation_step_100pct(node_factory, bitcoind, chainparams):
"""Test that the closing fee negotiation step works, 100%"""
opts = {}
opts['fee_negotiation_step'] = '100%'
opts['close_initiated_by'] = 'funder'
opts['expected_close_fee'] = 20001 if not chainparams['elements'] else 32985
closing_negotiation_step(node_factory, bitcoind, chainparams, opts)
# The close fee of 20499 looks strange in this case - one would expect
# to have a number close to 21000. This is because
# * the range is initially set to [20000 (fundee), 21000 (funder)]
# * the funder is always first to propose, he uses 50% step, so he proposes 20500
# * the range is narrowed to [20001, 20499] and the fundee proposes 20499
opts['close_initiated_by'] = 'fundee'
opts['expected_close_fee'] = 20499 if not chainparams['elements'] else 33808
closing_negotiation_step(node_factory, bitcoind, chainparams, opts)
def test_closing_negotiation_step_1sat(node_factory, bitcoind, chainparams):
"""Test that the closing fee negotiation step works, 1sat"""
opts = {}
opts['fee_negotiation_step'] = '1'
opts['close_initiated_by'] = 'funder'
opts['expected_close_fee'] = 20989 if not chainparams['elements'] else 34621
closing_negotiation_step(node_factory, bitcoind, chainparams, opts)
opts['close_initiated_by'] = 'fundee'
opts['expected_close_fee'] = 20010 if not chainparams['elements'] else 32995
closing_negotiation_step(node_factory, bitcoind, chainparams, opts)
def test_closing_negotiation_step_700sat(node_factory, bitcoind, chainparams):
"""Test that the closing fee negotiation step works, 700sat"""
opts = {}
opts['fee_negotiation_step'] = '700'
opts['close_initiated_by'] = 'funder'
opts['expected_close_fee'] = 20151 if not chainparams['elements'] else 33459
closing_negotiation_step(node_factory, bitcoind, chainparams, opts)
opts['close_initiated_by'] = 'fundee'
opts['expected_close_fee'] = 20499 if not chainparams['elements'] else 33746
closing_negotiation_step(node_factory, bitcoind, chainparams, opts)
@unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1")

View File

@ -492,6 +492,11 @@ struct command_result *param_short_channel_id(struct command *cmd UNNEEDED,
const jsmntok_t *tok UNNEEDED,
struct short_channel_id **scid UNNEEDED)
{ fprintf(stderr, "param_short_channel_id called!\n"); abort(); }
/* Generated stub for param_string */
struct command_result *param_string(struct command *cmd UNNEEDED, const char *name UNNEEDED,
const char * buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
const char **str UNNEEDED)
{ fprintf(stderr, "param_string called!\n"); abort(); }
/* Generated stub for param_tok */
struct command_result *param_tok(struct command *cmd UNNEEDED, const char *name UNNEEDED,
const char *buffer UNNEEDED, const jsmntok_t * tok UNNEEDED,