Browse Source

fundchannel / withdraw: allow explicit feerate setting.

These are the two cases where we'll refuse without a fee estimate.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 6 years ago
committed by Christian Decker
parent
commit
14dc1c37ab
  1. 1
      CHANGELOG.md
  2. 12
      contrib/pylightning/lightning/lightning.py
  3. 7
      doc/lightning-fundchannel.7.txt
  4. 6
      doc/lightning-withdraw.7.txt
  5. 30
      lightningd/chaintopology.c
  6. 8
      lightningd/chaintopology.h
  7. 14
      lightningd/opening_control.c
  8. 13
      tests/test_connection.py
  9. 17
      wallet/walletrpc.c

1
CHANGELOG.md

@ -22,6 +22,7 @@ This release named by ZmnSCPxj.
- JSON API: `ping` command to send a ping to a connected peer. - JSON API: `ping` command to send a ping to a connected peer.
- JSON API: `feerates` command to inject fee estimates manually, and retrieve - JSON API: `feerates` command to inject fee estimates manually, and retrieve
current estimates. current estimates.
- JSON API: `withdraw` and `fundchannel` can be given manual feerate.
- Config: `--conf` option to set config file. - Config: `--conf` option to set config file.
- Documentation: Added CHANGELOG.md - Documentation: Added CHANGELOG.md
- pylightning: RpcError now has `method` and `payload` fields. - pylightning: RpcError now has `method` and `payload` fields.

12
contrib/pylightning/lightning/lightning.py

@ -331,13 +331,15 @@ class LightningRpc(UnixDomainSocketRpc):
} }
return self.call("listpeers", payload) return self.call("listpeers", payload)
def fundchannel(self, node_id, satoshi): def fundchannel(self, node_id, satoshi, feerate=None, feeratestyle=None):
""" """
Fund channel with {id} using {satoshi} satoshis" Fund channel with {id} using {satoshi} satoshis"
""" """
payload = { payload = {
"id": node_id, "id": node_id,
"satoshi": satoshi "satoshi": satoshi,
"feerate": feerate,
"feeratestyle": feeratestyle
} }
return self.call("fundchannel", payload) return self.call("fundchannel", payload)
@ -404,14 +406,16 @@ class LightningRpc(UnixDomainSocketRpc):
""" """
return self.call("dev-memleak") return self.call("dev-memleak")
def withdraw(self, destination, satoshi): def withdraw(self, destination, satoshi, feerate=None, feeratestyle=None):
""" """
Send to {destination} address {satoshi} (or "all") Send to {destination} address {satoshi} (or "all")
amount via Bitcoin transaction amount via Bitcoin transaction
""" """
payload = { payload = {
"destination": destination, "destination": destination,
"satoshi": satoshi "satoshi": satoshi,
"feerate": feerate,
"feeratestyle": feeratestyle
} }
return self.call("withdraw", payload) return self.call("withdraw", payload)

7
doc/lightning-fundchannel.7.txt

@ -8,7 +8,7 @@ lightning-fundchannel - Command for establishing a lightning channel.
SYNOPSIS SYNOPSIS
-------- --------
*fundchannel* 'id' 'satoshi' *fundchannel* 'id' 'satoshi' ['feerate' 'feeratestyle']
DESCRIPTION DESCRIPTION
----------- -----------
@ -27,6 +27,11 @@ The string 'all' can be used to specify all available funds (or 16777215 satoshi
The value cannot be less than the dust limit, currently set to 546, nor more The value cannot be less than the dust limit, currently set to 546, nor more
than 16777215 satoshi. than 16777215 satoshi.
'feerate' is an optional feerate to use, overriding lightningd's
internal estimate. If specified, 'feeratestyle' must be either
'"perkw"' for if 'feerate' is in satoshi-per-kilosipa (weight),
or '"perkb"' for if 'feerate' is in bitcoind-style satoshi-per-kilobyte.
RETURN VALUE RETURN VALUE
------------ ------------
On success, the 'tx' and 'txid' of the transaction is returned, as well as the On success, the 'tx' and 'txid' of the transaction is returned, as well as the

6
doc/lightning-withdraw.7.txt

@ -9,7 +9,7 @@ internal wallet.
SYNOPSIS SYNOPSIS
-------- --------
*withdraw* 'destination' 'satoshi' *withdraw* 'destination' 'satoshi' ['feerate' 'feeratestyle']
DESCRIPTION DESCRIPTION
----------- -----------
@ -24,6 +24,10 @@ wallet (expressed, as name suggests, in satoshi).
The string 'all' can be used to specify withdrawal of all The string 'all' can be used to specify withdrawal of all
available funds. available funds.
'feerate' is an optional feerate to use, overriding lightningd's
internal estimate. If specified, 'feeratestyle' must be either
'"perkw"' for if 'feerate' is in satoshi-per-kilosipa (weight),
or '"perkb"' for if 'feerate' is in bitcoind-style satoshi-per-kilobyte.
RETURN VALUE RETURN VALUE
------------ ------------

30
lightningd/chaintopology.c

@ -447,6 +447,36 @@ u32 feerate_to_style(u32 feerate_perkw, enum feerate_style style)
abort(); abort();
} }
/* If we have both feerate and style, use that, otherwise use inbuilt if avail.
* Return false if we failed command, otherwise fills in feerate_perkw. */
bool json_feerate_and_style(struct command *cmd,
const u32 *feerate, enum feerate_style *style,
u32 fallback_feerate_per_kw,
u32 *feerate_per_kw)
{
if (feerate) {
if (!style) {
command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"'feerate' requires 'feeratestyle'");
return false;
}
*feerate_per_kw = feerate_from_style(*feerate, *style);
return true;
} else {
if (style) {
command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"'feeratestyle' requires 'feerate'");
return false;
}
*feerate_per_kw = fallback_feerate_per_kw;
if (!*feerate_per_kw) {
command_fail(cmd, LIGHTNINGD, "Cannot estimate fees");
return false;
}
return true;
}
}
static void json_feerates(struct command *cmd, static void json_feerates(struct command *cmd,
const char *buffer, const jsmntok_t *params) const char *buffer, const jsmntok_t *params)
{ {

8
lightningd/chaintopology.h

@ -150,6 +150,14 @@ u32 unilateral_feerate(struct chain_topology *topo);
u32 feerate_from_style(u32 feerate, enum feerate_style style); u32 feerate_from_style(u32 feerate, enum feerate_style style);
u32 feerate_to_style(u32 feerate_perkw, enum feerate_style style); u32 feerate_to_style(u32 feerate_perkw, enum feerate_style style);
/* If we have both feerate and style, use that, otherwise use fallback
* if nonzero. Return false if we failed command, otherwise fills in
* feerate_per_kw. */
bool json_feerate_and_style(struct command *cmd,
const u32 *feerate, enum feerate_style *style,
u32 fallback_feerate_per_kw,
u32 *feerate_per_kw);
/* Broadcast a single tx, and rebroadcast as reqd (copies tx). /* Broadcast a single tx, and rebroadcast as reqd (copies tx).
* If failed is non-NULL, call that and don't rebroadcast. */ * If failed is non-NULL, call that and don't rebroadcast. */
void broadcast_tx(struct chain_topology *topo, void broadcast_tx(struct chain_topology *topo,

14
lightningd/opening_control.c

@ -764,7 +764,9 @@ static void json_fund_channel(struct command *cmd,
struct pubkey *id; struct pubkey *id;
struct peer *peer; struct peer *peer;
struct channel *channel; struct channel *channel;
u32 feerate_per_kw = opening_feerate(cmd->ld->topology); unsigned int *feerate;
enum feerate_style *style;
u32 feerate_per_kw;
u8 *msg; u8 *msg;
fc->cmd = cmd; fc->cmd = cmd;
@ -773,16 +775,18 @@ static void json_fund_channel(struct command *cmd,
if (!param(fc->cmd, buffer, params, if (!param(fc->cmd, buffer, params,
p_req("id", json_tok_pubkey, &id), p_req("id", json_tok_pubkey, &id),
p_req("satoshi", json_tok_tok, &sattok), p_req("satoshi", json_tok_tok, &sattok),
p_opt("feerate", json_tok_number, &feerate),
p_opt("feeratestyle", json_tok_feerate_style, &style),
NULL)) NULL))
return; return;
if (!json_tok_wtx(&fc->wtx, buffer, sattok, MAX_FUNDING_SATOSHI)) if (!json_tok_wtx(&fc->wtx, buffer, sattok, MAX_FUNDING_SATOSHI))
return; return;
if (!feerate_per_kw) { if (!json_feerate_and_style(cmd, feerate, style,
command_fail(cmd, LIGHTNINGD, "Cannot estimate fees"); opening_feerate(cmd->ld->topology),
&feerate_per_kw))
return; return;
}
peer = peer_by_id(cmd->ld, id); peer = peer_by_id(cmd->ld, id);
if (!peer) { if (!peer) {
@ -839,6 +843,6 @@ static void json_fund_channel(struct command *cmd,
static const struct json_command fund_channel_command = { static const struct json_command fund_channel_command = {
"fundchannel", "fundchannel",
json_fund_channel, json_fund_channel,
"Fund channel with {id} using {satoshi} (or 'all') satoshis" "Fund channel with {id} using {satoshi} (or 'all') satoshis, at optional {feerate}"
}; };
AUTODATA(json_command, &fund_channel_command); AUTODATA(json_command, &fund_channel_command);

13
tests/test_connection.py

@ -1124,7 +1124,20 @@ def test_no_fee_estimate(node_factory, bitcoind, executor):
with pytest.raises(RpcError, match=r'Cannot estimate fees'): with pytest.raises(RpcError, match=r'Cannot estimate fees'):
l1.rpc.withdraw(l2.rpc.newaddr()['address'], 'all') l1.rpc.withdraw(l2.rpc.newaddr()['address'], 'all')
# Can with manual feerate.
l1.rpc.withdraw(l2.rpc.newaddr()['address'], 10000, 1500, 'perkb')
l1.rpc.fundchannel(l2.info['id'], 10**6, 2000, 'perkw')
# Make sure we clean up cahnnel for later attempt.
l1.daemon.wait_for_log('sendrawtx exit 0')
l1.rpc.dev_fail(l2.info['id'])
l1.daemon.wait_for_log('sendrawtx exit 0')
bitcoind.generate_block(6)
wait_for(lambda: only_one(l1.rpc.getpeer(l2.info['id'])['channels'])['state'] == 'ONCHAIN')
wait_for(lambda: only_one(l2.rpc.getpeer(l1.info['id'])['channels'])['state'] == 'ONCHAIN')
# But can accept incoming connections. # But can accept incoming connections.
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
l2.fund_channel(l1, 10**6) l2.fund_channel(l1, 10**6)
# Can do HTLCs. # Can do HTLCs.

17
wallet/walletrpc.c

@ -89,10 +89,10 @@ static void json_withdraw(struct command *cmd,
{ {
const jsmntok_t *desttok, *sattok; const jsmntok_t *desttok, *sattok;
struct withdrawal *withdraw = tal(cmd, struct withdrawal); struct withdrawal *withdraw = tal(cmd, struct withdrawal);
u32 feerate_per_kw;
u32 feerate_per_kw = try_get_feerate(cmd->ld->topology, FEERATE_NORMAL); unsigned int *feerate;
enum feerate_style *style;
struct bitcoin_tx *tx; struct bitcoin_tx *tx;
enum address_parse_result addr_parse; enum address_parse_result addr_parse;
withdraw->cmd = cmd; withdraw->cmd = cmd;
@ -101,16 +101,19 @@ static void json_withdraw(struct command *cmd,
if (!param(cmd, buffer, params, if (!param(cmd, buffer, params,
p_req("destination", json_tok_tok, &desttok), p_req("destination", json_tok_tok, &desttok),
p_req("satoshi", json_tok_tok, &sattok), p_req("satoshi", json_tok_tok, &sattok),
p_opt("feerate", json_tok_number, &feerate),
p_opt("feeratestyle", json_tok_feerate_style, &style),
NULL)) NULL))
return; return;
if (!json_tok_wtx(&withdraw->wtx, buffer, sattok, -1ULL)) if (!json_tok_wtx(&withdraw->wtx, buffer, sattok, -1ULL))
return; return;
if (!feerate_per_kw) { if (!json_feerate_and_style(cmd, feerate, style,
command_fail(cmd, LIGHTNINGD, "Cannot estimate fees"); try_get_feerate(cmd->ld->topology,
FEERATE_NORMAL),
&feerate_per_kw))
return; return;
}
/* Parse address. */ /* Parse address. */
addr_parse = json_tok_address_scriptpubkey(cmd, addr_parse = json_tok_address_scriptpubkey(cmd,
@ -163,7 +166,7 @@ static void json_withdraw(struct command *cmd,
static const struct json_command withdraw_command = { static const struct json_command withdraw_command = {
"withdraw", "withdraw",
json_withdraw, json_withdraw,
"Send to {destination} address {satoshi} (or 'all') amount via Bitcoin transaction", "Send to {destination} address {satoshi} (or 'all') amount via Bitcoin transaction, at optional {feerate}",
false, "Send funds from the internal wallet to the specified address. Either specify a number of satoshis to send or 'all' to sweep all funds in the internal wallet to the address." false, "Send funds from the internal wallet to the specified address. Either specify a number of satoshis to send or 'all' to sweep all funds in the internal wallet to the address."
}; };
AUTODATA(json_command, &withdraw_command); AUTODATA(json_command, &withdraw_command);

Loading…
Cancel
Save