opening: Add dev-allowdustreserve option to opt into dust reserves

Technically this is a non-conformance with the spec, hence the `dev`
flag to opt-in, however I'm being told that it is also implemented in
other implementations. I'll follow this up with a proposal to the spec
to remove the checks we now bypass.
This commit is contained in:
Christian Decker
2022-06-24 12:58:26 +02:00
parent 1bd3d8d9f9
commit 54b4baabb0
5 changed files with 43 additions and 35 deletions

View File

@@ -258,8 +258,6 @@ endif
CPPFLAGS += -DBINTOPKGLIBEXECDIR="\"$(shell sh tools/rel.sh $(bindir) $(pkglibexecdir))\"" CPPFLAGS += -DBINTOPKGLIBEXECDIR="\"$(shell sh tools/rel.sh $(bindir) $(pkglibexecdir))\""
CFLAGS = $(CPPFLAGS) $(CWARNFLAGS) $(CDEBUGFLAGS) $(COPTFLAGS) -I $(CCANDIR) $(EXTERNAL_INCLUDE_FLAGS) -I . -I$(CPATH) $(SQLITE3_CFLAGS) $(POSTGRES_INCLUDE) $(FEATURES) $(COVFLAGS) $(DEV_CFLAGS) -DSHACHAIN_BITS=48 -DJSMN_PARENT_LINKS $(PIE_CFLAGS) $(COMPAT_CFLAGS) -DBUILD_ELEMENTS=1 CFLAGS = $(CPPFLAGS) $(CWARNFLAGS) $(CDEBUGFLAGS) $(COPTFLAGS) -I $(CCANDIR) $(EXTERNAL_INCLUDE_FLAGS) -I . -I$(CPATH) $(SQLITE3_CFLAGS) $(POSTGRES_INCLUDE) $(FEATURES) $(COVFLAGS) $(DEV_CFLAGS) -DSHACHAIN_BITS=48 -DJSMN_PARENT_LINKS $(PIE_CFLAGS) $(COMPAT_CFLAGS) -DBUILD_ELEMENTS=1
CFLAGS += -DZERORESERVE=1
# If CFLAGS is already set in the environment of make (to whatever value, it # If CFLAGS is already set in the environment of make (to whatever value, it
# does not matter) then it would export it to subprocesses with the above value # does not matter) then it would export it to subprocesses with the above value
# we set, including CWARNFLAGS which by default contains -Wall -Werror. This # we set, including CWARNFLAGS which by default contains -Wall -Werror. This

View File

@@ -99,6 +99,7 @@ On success, an object is returned, containing:
- **subdaemon** (string, optional): `subdaemon` fields from config or cmdline if any (can be more than one) - **subdaemon** (string, optional): `subdaemon` fields from config or cmdline if any (can be more than one)
- **fetchinvoice-noconnect** (boolean, optional): `featchinvoice-noconnect` fileds from config or cmdline, or default - **fetchinvoice-noconnect** (boolean, optional): `featchinvoice-noconnect` fileds from config or cmdline, or default
- **tor-service-password** (string, optional): `tor-service-password` field from config or cmdline, if any - **tor-service-password** (string, optional): `tor-service-password` field from config or cmdline, if any
- **dev-allowdustreserve** (boolean, optional): Whether we allow setting dust reserves
[comment]: # (GENERATE-FROM-SCHEMA-END) [comment]: # (GENERATE-FROM-SCHEMA-END)
@@ -216,4 +217,5 @@ RESOURCES
--------- ---------
Main web site: <https://github.com/ElementsProject/lightning> Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:dcab86f29b946fed925de5e05cb79faa03cc4421cefeab3561a596ed5e64962d)
[comment]: # ( SHA256STAMP:310cc00ef62e7075d5d2588b0492c2dd96f507cc739f67d56ccc6c4f3135bca5)

View File

@@ -286,6 +286,10 @@
"tor-service-password": { "tor-service-password": {
"type": "string", "type": "string",
"description": "`tor-service-password` field from config or cmdline, if any" "description": "`tor-service-password` field from config or cmdline, if any"
},
"dev-allowdustreserve": {
"type": "boolean",
"description": "Whether we allow setting dust reserves"
} }
} }
} }

View File

@@ -146,27 +146,28 @@ static void set_reserve_absolute(struct state * state, const struct amount_sat d
{ {
status_debug("Setting their reserve to %s", status_debug("Setting their reserve to %s",
type_to_string(tmpctx, struct amount_sat, &reserve_sat)); type_to_string(tmpctx, struct amount_sat, &reserve_sat));
#ifdef ZERORESERVE if (state->allowdustreserve) {
state->localconf.channel_reserve = reserve_sat; state->localconf.channel_reserve = reserve_sat;
#else } else {
/* BOLT #2: /* BOLT #2:
* *
* The sending node: * The sending node:
*... *...
* - MUST set `channel_reserve_satoshis` greater than or equal to * - MUST set `channel_reserve_satoshis` greater than or equal
* `dust_limit_satoshis` from the `open_channel` message. *to `dust_limit_satoshis` from the `open_channel` message.
*/ */
if (amount_sat_greater(dust_limit, reserve_sat)) { if (amount_sat_greater(dust_limit, reserve_sat)) {
status_debug( status_debug("Their reserve is too small, bumping to "
"Their reserve is too small, bumping to dust_limit: %s < %s", "dust_limit: %s < %s",
type_to_string(tmpctx, struct amount_sat, &reserve_sat), type_to_string(tmpctx, struct amount_sat,
type_to_string(tmpctx, struct amount_sat, &dust_limit)); &reserve_sat),
state->localconf.channel_reserve type_to_string(tmpctx, struct amount_sat,
= dust_limit; &dust_limit));
state->localconf.channel_reserve = dust_limit;
} else { } else {
state->localconf.channel_reserve = reserve_sat; state->localconf.channel_reserve = reserve_sat;
} }
#endif }
} }
/* We always set channel_reserve_satoshis to 1%, rounded down. */ /* We always set channel_reserve_satoshis to 1%, rounded down. */
@@ -464,8 +465,8 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags)
type_to_string(msg, struct channel_id, type_to_string(msg, struct channel_id,
&state->channel_id)); &state->channel_id));
#ifndef ZERORESERVE if (!state->allowdustreserve &&
if (amount_sat_greater(state->remoteconf.dust_limit, amount_sat_greater(state->remoteconf.dust_limit,
state->localconf.channel_reserve)) { state->localconf.channel_reserve)) {
negotiation_failed(state, negotiation_failed(state,
"dust limit %s" "dust limit %s"
@@ -476,7 +477,6 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags)
&state->localconf.channel_reserve)); &state->localconf.channel_reserve));
return NULL; return NULL;
} }
#endif
if (!check_config_bounds(tmpctx, state->funding_sats, if (!check_config_bounds(tmpctx, state->funding_sats,
state->feerate_per_kw, state->feerate_per_kw,
@@ -972,7 +972,8 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg)
* - MUST set `dust_limit_satoshis` less than or equal to * - MUST set `dust_limit_satoshis` less than or equal to
* `channel_reserve_satoshis` from the `open_channel` message. * `channel_reserve_satoshis` from the `open_channel` message.
*/ */
if (amount_sat_greater(state->remoteconf.dust_limit, if (!state->allowdustreserve &&
amount_sat_greater(state->remoteconf.dust_limit,
state->localconf.channel_reserve)) { state->localconf.channel_reserve)) {
negotiation_failed(state, negotiation_failed(state,
"Our channel reserve %s" "Our channel reserve %s"

View File

@@ -1740,17 +1740,20 @@ def test_zeroreserve(node_factory, bitcoind):
- l2 enforces default reserve - l2 enforces default reserve
- l3 enforces sub-dust reserves - l3 enforces sub-dust reserves
""" """
ZEROCONF = True
plugin_path = Path(__file__).parent / "plugins" / "zeroreserve.py" plugin_path = Path(__file__).parent / "plugins" / "zeroreserve.py"
opts = [ opts = [
{ {
'plugin': str(plugin_path), 'plugin': str(plugin_path),
'reserve': '0sat', 'reserve': '0sat',
'dev-allowdustreserve': True,
},
{
'dev-allowdustreserve': True,
}, },
{},
{ {
'plugin': str(plugin_path), 'plugin': str(plugin_path),
'reserve': '123sat' 'reserve': '123sat',
'dev-allowdustreserve': True,
} }
] ]
l1, l2, l3 = node_factory.get_nodes(3, opts=opts) l1, l2, l3 = node_factory.get_nodes(3, opts=opts)
@@ -1780,16 +1783,16 @@ def test_zeroreserve(node_factory, bitcoind):
l1c3 = l1.rpc.listpeers(l3.info['id'])['peers'][0]['channels'][0] l1c3 = l1.rpc.listpeers(l3.info['id'])['peers'][0]['channels'][0]
# l1 imposed a 0sat reserve on l2, while l2 imposed the default 1% reserve on l1 # l1 imposed a 0sat reserve on l2, while l2 imposed the default 1% reserve on l1
assert l1c1['their_channel_reserve_satoshis'] == l2c1['our_channel_reserve_satoshis'] == (0 if ZEROCONF else 546) assert l1c1['their_reserve_msat'] == l2c1['our_reserve_msat'] == Millisatoshi('0sat')
assert l1c1['our_channel_reserve_satoshis'] == l2c1['their_channel_reserve_satoshis'] == 10000 assert l1c1['our_reserve_msat'] == l2c1['their_reserve_msat'] == Millisatoshi('10000sat')
# l2 imposed the default 1% on l3, while l3 imposed a custom 123sat fee on l2 # l2 imposed the default 1% on l3, while l3 imposed a custom 123sat fee on l2
assert l2c2['their_channel_reserve_satoshis'] == l3c2['our_channel_reserve_satoshis'] == 10000 assert l2c2['their_reserve_msat'] == l3c2['our_reserve_msat'] == Millisatoshi('10000sat')
assert l2c2['our_channel_reserve_satoshis'] == l3c2['their_channel_reserve_satoshis'] == (123 if ZEROCONF else 546) assert l2c2['our_reserve_msat'] == l3c2['their_reserve_msat'] == Millisatoshi('123sat')
# l3 imposed a custom 321sat fee on l1, while l1 imposed a custom 0sat fee on l3 # l3 imposed a custom 321sat fee on l1, while l1 imposed a custom 0sat fee on l3
assert l3c3['their_channel_reserve_satoshis'] == l1c3['our_channel_reserve_satoshis'] == (321 if ZEROCONF else 546) assert l3c3['their_reserve_msat'] == l1c3['our_reserve_msat'] == Millisatoshi('321sat')
assert l3c3['our_channel_reserve_satoshis'] == l1c3['their_channel_reserve_satoshis'] == (0 if ZEROCONF else 546) assert l3c3['our_reserve_msat'] == l1c3['their_reserve_msat'] == Millisatoshi('0sat')
# Now do some drain tests on c1, as that should be drainable # Now do some drain tests on c1, as that should be drainable
# completely by l2 being the fundee # completely by l2 being the fundee