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

openingd: Allow users to set the enforced reserve for their counterparty (zeroreserve) #5315

Merged
merged 16 commits into from
Sep 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .msggen.json
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@
"FundChannel.mindepth": 12,
"FundChannel.push_msat": 5,
"FundChannel.request_amt": 7,
"FundChannel.reserve": 13,
"FundChannel.utxos[]": 11
},
"FundchannelResponse": {
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ endif

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

# 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
# we set, including CWARNFLAGS which by default contains -Wall -Werror. This
Expand Down
1 change: 1 addition & 0 deletions cln-grpc/proto/node.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1158,6 +1158,7 @@ message FundchannelRequest {
optional string compact_lease = 8;
repeated Outpoint utxos = 11;
optional uint32 mindepth = 12;
optional Amount reserve = 13;
}

message FundchannelResponse {
Expand Down
1 change: 1 addition & 0 deletions cln-grpc/src/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1464,6 +1464,7 @@ impl From<&pb::FundchannelRequest> for requests::FundchannelRequest {
compact_lease: c.compact_lease.clone(), // Rule #1 for type string?
utxos: Some(c.utxos.iter().map(|s| s.into()).collect()), // Rule #4
mindepth: c.mindepth.clone(), // Rule #1 for type u32?
reserve: c.reserve.as_ref().map(|a| a.into()), // Rule #1 for type msat?
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions cln-rpc/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,8 @@ pub mod requests {
pub utxos: Option<Vec<Outpoint>>,
#[serde(alias = "mindepth", skip_serializing_if = "Option::is_none")]
pub mindepth: Option<u32>,
#[serde(alias = "reserve", skip_serializing_if = "Option::is_none")]
pub reserve: Option<Amount>,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
Expand Down
16 changes: 16 additions & 0 deletions common/amount.c
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,22 @@ struct amount_sat amount_sat_div(struct amount_sat sat, u64 div)
return sat;
}

bool amount_sat_mul(struct amount_sat *res, struct amount_sat sat, u64 mul)
{
if ( mul_overflows_u64(sat.satoshis, mul))
return false;
res->satoshis = sat.satoshis * mul;
return true;
}

bool amount_msat_mul(struct amount_msat *res, struct amount_msat msat, u64 mul)
{
if ( mul_overflows_u64(msat.millisatoshis, mul))
return false;
res->millisatoshis = msat.millisatoshis * mul;
return true;
}

bool amount_msat_fee(struct amount_msat *fee,
struct amount_msat amt,
u32 fee_base_msat,
Expand Down
3 changes: 3 additions & 0 deletions common/amount.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ WARN_UNUSED_RESULT bool amount_sat_scale(struct amount_sat *val,
struct amount_msat amount_msat_div(struct amount_msat msat, u64 div);
struct amount_sat amount_sat_div(struct amount_sat sat, u64 div);

bool amount_sat_mul(struct amount_sat *res, struct amount_sat sat, u64 mul);
bool amount_msat_mul(struct amount_msat *res, struct amount_msat msat, u64 mul);

/* Is a == b? */
bool amount_sat_eq(struct amount_sat a, struct amount_sat b);
bool amount_msat_eq(struct amount_msat a, struct amount_msat b);
Expand Down
4 changes: 3 additions & 1 deletion contrib/pyln-client/pyln/client/lightning.py
Original file line number Diff line number Diff line change
Expand Up @@ -730,7 +730,8 @@ def feerates(self, style, urgent=None, normal=None, slow=None):
def fundchannel(self, node_id, amount, feerate=None, announce=True,
minconf=None, utxos=None, push_msat=None, close_to=None,
request_amt=None, compact_lease=None,
mindepth: Optional[int] = None):
mindepth: Optional[int] = None,
reserve: Optional[str] = None):
rustyrussell marked this conversation as resolved.
Show resolved Hide resolved
"""
Fund channel with {id} using {amount} satoshis with feerate
of {feerate} (uses default feerate if unset).
Expand All @@ -756,6 +757,7 @@ def fundchannel(self, node_id, amount, feerate=None, announce=True,
"request_amt": request_amt,
"compact_lease": compact_lease,
"mindepth": mindepth,
"reserve": reserve,
}
return self.call("fundchannel", payload)

Expand Down
96 changes: 48 additions & 48 deletions contrib/pyln-testing/pyln/testing/node_pb2.py

Large diffs are not rendered by default.

18 changes: 17 additions & 1 deletion doc/PLUGINS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1198,13 +1198,29 @@ e.g.
{
"result": "continue",
"close_to": "bc1qlq8srqnz64wgklmqvurv7qnr4rvtq2u96hhfg2"
"mindepth": 0,
"reserve": "1234sat"
}
```

Note that `close_to` must be a valid address for the current chain,
an invalid address will cause the node to exit with an error.

Note that `openchannel` is a chained hook. Therefore `close_to` will only be
- `mindepth` is the number of confirmations to require before making
the channel usable. Notice that setting this to 0 (`zeroconf`) or
some other low value might expose you to double-spending issues, so
only lower this value from the default if you trust the peer not to
double-spend, or you reject incoming payments, including forwards,
until the funding is confirmed.

- `reserve` is an absolute value for the amount in the channel that
the peer must keep on their side. This ensures that they always
have something to lose, so only lower this below the 1% of funding
amount if you trust the peer. The protocol requires this to be
larger than the dust limit, hence it will be adjusted to be the
dust limit if the specified value is below.

Note that `openchannel` is a chained hook. Therefore `close_to`, `reserve` will only be
evaluated for the first plugin that sets it. If more than one plugin tries to
set a `close_to` address an error will be logged.

Expand Down
4 changes: 3 additions & 1 deletion doc/lightning-listconfigs.7.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
- **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
- **dev-allowdustreserve** (boolean, optional): Whether we allow setting dust reserves

[comment]: # (GENERATE-FROM-SCHEMA-END)

Expand Down Expand Up @@ -216,4 +217,5 @@ RESOURCES
---------

Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:dcab86f29b946fed925de5e05cb79faa03cc4421cefeab3561a596ed5e64962d)

[comment]: # ( SHA256STAMP:310cc00ef62e7075d5d2588b0492c2dd96f507cc739f67d56ccc6c4f3135bca5)
4 changes: 4 additions & 0 deletions doc/schemas/fundchannel.request.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
"mindepth": {
"description": "Number of confirmations required before we consider the channel active",
"type": "u32"
},
"reserve": {
"type": "msat",
"description": "The amount we want the peer to maintain on its side"
}
}
}
4 changes: 4 additions & 0 deletions doc/schemas/listconfigs.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,10 @@
"tor-service-password": {
"type": "string",
"description": "`tor-service-password` field from config or cmdline, if any"
},
"dev-allowdustreserve": {
"type": "boolean",
"description": "Whether we allow setting dust reserves"
}
}
}
6 changes: 6 additions & 0 deletions lightningd/lightningd.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ struct config {

/* EXPERIMENTAL: offers support */
bool exp_offers;

/* Allow dust reserves (including 0) when being called via
* `fundchannel` or in the `openchannel` hook. This is a
* slight spec incompatibility, but implementations do this
* already. */
bool allowdustreserve;
cdecker marked this conversation as resolved.
Show resolved Hide resolved
};

typedef STRMAP(const char *) alt_subdaemon_map;
Expand Down
7 changes: 7 additions & 0 deletions lightningd/opening_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ new_uncommitted_channel(struct peer *peer)
/* We override this in openchannel hook if we want zeroconf */
uc->minimum_depth = ld->config.anchor_confirms;

/* Use default 1% reserve if not otherwise specified. If this
* is not-NULL it will be used by openingd as absolute value
* (clamped to dust limit). */
uc->reserve = NULL;

memset(&uc->cid, 0xFF, sizeof(uc->cid));

/* Declare the new channel to the HSM. */
new_channel_msg = towire_hsmd_new_channel(NULL, &uc->peer->id, uc->dbid);
if (!wire_sync_write(ld->hsm_fd, take(new_channel_msg)))
Expand Down
7 changes: 7 additions & 0 deletions lightningd/opening_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ struct uncommitted_channel {

/* Our channel config. */
struct channel_config our_config;

/* Reserve we will impose on the other side. If this is NULL
* we will use our default of 1% of the funding
* amount. Otherwise it will be used by openingd as absolute
* value (clamped to dust limit). */
struct amount_sat *reserve;
};

struct funding_channel {
Expand All @@ -66,6 +72,7 @@ struct funding_channel {
struct wallet_tx *wtx;
struct amount_msat push;
struct amount_sat funding_sats;

u8 channel_flags;
const u8 *our_upfront_shutdown_script;

Expand Down
63 changes: 40 additions & 23 deletions lightningd/opening_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,8 @@ openchannel_hook_final(struct openchannel_hook_payload *payload STEALS)
subd_send_msg(openingd,
take(towire_openingd_got_offer_reply(NULL, errmsg,
our_upfront_shutdown_script,
upfront_shutdown_script_wallet_index)));
upfront_shutdown_script_wallet_index,
payload->uc->reserve)));
}

static bool
Expand All @@ -732,6 +733,7 @@ openchannel_hook_deserialize(struct openchannel_hook_payload *payload,
const jsmntok_t *t_errmsg = json_get_member(buffer, toks, "error_message");
const jsmntok_t *t_closeto = json_get_member(buffer, toks, "close_to");
const jsmntok_t *t_mindepth = json_get_member(buffer, toks, "mindepth");
const jsmntok_t *t_reserve = json_get_member(buffer, toks, "reserve");

if (!t_result)
fatal("Plugin returned an invalid response to the"
Expand Down Expand Up @@ -793,6 +795,16 @@ openchannel_hook_deserialize(struct openchannel_hook_payload *payload,
payload->uc->minimum_depth);
}

if (t_reserve != NULL) {
payload->uc->reserve = tal(payload->uc, struct amount_sat);
json_to_sat(buffer, t_reserve, payload->uc->reserve);
log_debug(openingd->ld->log,
"Setting reserve=%s for this channel as requested by "
"the openchannel hook",
type_to_string(tmpctx, struct amount_sat,
payload->uc->reserve));
}

return true;
}

Expand Down Expand Up @@ -934,18 +946,19 @@ bool peer_start_openingd(struct peer *peer, struct peer_fd *peer_fd)


msg = towire_openingd_init(NULL,
chainparams,
peer->ld->our_features,
peer->their_features,
&uc->our_config,
max_to_self_delay,
min_effective_htlc_capacity,
&uc->local_basepoints,
&uc->local_funding_pubkey,
uc->minimum_depth,
feerate_min(peer->ld, NULL),
feerate_max(peer->ld, NULL),
IFDEV(peer->ld->dev_force_tmp_channel_id, NULL));
chainparams,
peer->ld->our_features,
peer->their_features,
&uc->our_config,
max_to_self_delay,
min_effective_htlc_capacity,
&uc->local_basepoints,
&uc->local_funding_pubkey,
uc->minimum_depth,
feerate_min(peer->ld, NULL),
feerate_max(peer->ld, NULL),
IFDEV(peer->ld->dev_force_tmp_channel_id, NULL),
peer->ld->config.allowdustreserve);
subd_send_msg(uc->open_daemon, take(msg));
return true;
}
Expand Down Expand Up @@ -1095,7 +1108,7 @@ static struct command_result *json_fundchannel_start(struct command *cmd,
bool *announce_channel;
u32 *feerate_per_kw, *mindepth;
int fds[2];
struct amount_sat *amount;
struct amount_sat *amount, *reserve;
struct amount_msat *push_msat;
u32 *upfront_shutdown_script_wallet_index;
struct channel_id tmp_channel_id;
Expand All @@ -1114,6 +1127,7 @@ static struct command_result *json_fundchannel_start(struct command *cmd,
p_opt("close_to", param_bitcoin_address, &fc->our_upfront_shutdown_script),
p_opt("push_msat", param_msat, &push_msat),
p_opt_def("mindepth", param_u32, &mindepth, cmd->ld->config.anchor_confirms),
p_opt("reserve", param_sat, &reserve),
NULL))
return command_param_failed();

Expand Down Expand Up @@ -1223,6 +1237,8 @@ static struct command_result *json_fundchannel_start(struct command *cmd,
assert(mindepth != NULL);
fc->uc->minimum_depth = *mindepth;

fc->uc->reserve = reserve;

/* Needs to be stolen away from cmd */
if (fc->our_upfront_shutdown_script)
fc->our_upfront_shutdown_script
Expand All @@ -1241,15 +1257,16 @@ static struct command_result *json_fundchannel_start(struct command *cmd,
} else
upfront_shutdown_script_wallet_index = NULL;

fc->open_msg
= towire_openingd_funder_start(fc,
*amount,
fc->push,
fc->our_upfront_shutdown_script,
upfront_shutdown_script_wallet_index,
*feerate_per_kw,
&tmp_channel_id,
fc->channel_flags);
fc->open_msg = towire_openingd_funder_start(
fc,
*amount,
fc->push,
fc->our_upfront_shutdown_script,
upfront_shutdown_script_wallet_index,
*feerate_per_kw,
&tmp_channel_id,
fc->channel_flags,
fc->uc->reserve);

if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) {
return command_fail(cmd, FUND_MAX_EXCEEDED,
Expand Down
8 changes: 8 additions & 0 deletions lightningd/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,8 @@ static const struct config testnet_config = {
.connection_timeout_secs = 60,

.exp_offers = IFEXPERIMENTAL(true, false),

.allowdustreserve = false,
};

/* aka. "Dude, where's my coins?" */
Expand Down Expand Up @@ -875,6 +877,8 @@ static const struct config mainnet_config = {
.connection_timeout_secs = 60,

.exp_offers = IFEXPERIMENTAL(true, false),

.allowdustreserve = false,
};

static void check_config(struct lightningd *ld)
Expand Down Expand Up @@ -1168,6 +1172,10 @@ static void register_opts(struct lightningd *ld)
&ld->autolisten,
"If true, listen on default port and announce if it seems to be a public interface");

opt_register_arg("--dev-allowdustreserve", opt_set_bool_arg, opt_show_bool,
&ld->config.allowdustreserve,
"If true, we allow the `fundchannel` RPC command and the `openchannel` plugin hook to set a reserve that is below the dust limit.");

opt_register_arg("--proxy", opt_add_proxy_addr, NULL,
ld,"Set a socks v5 proxy IP address and port");
opt_register_arg("--tor-service-password", opt_set_talstr, NULL,
Expand Down
18 changes: 0 additions & 18 deletions openingd/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -180,24 +180,6 @@ bool check_config_bounds(const tal_t *ctx,
return false;
}

/* BOLT #2:
*
* The receiving node MUST fail the channel if:
*...
* - `dust_limit_satoshis` is greater than `channel_reserve_satoshis`.
*/
if (amount_sat_greater(remoteconf->dust_limit,
remoteconf->channel_reserve)) {
*err_reason = tal_fmt(ctx,
"dust_limit_satoshis %s"
" too large for channel_reserve_satoshis %s",
type_to_string(ctx, struct amount_sat,
&remoteconf->dust_limit),
type_to_string(ctx, struct amount_sat,
&remoteconf->channel_reserve));
return false;
}

return true;
}

Expand Down
Loading