Browse Source

rpc: add 'utxos' parameter to txprepare + withdraw

Allow a user to select the utxo set that will be added to a
transaction, via the `utxos` parameter. Optional.

Format for utxos should be of the form ["txid:vout","..."]
travis-debug
lisa neigut 5 years ago
committed by Rusty Russell
parent
commit
5c70890efa
  1. 3
      CHANGELOG.md
  2. 2
      common/wallet_tx.c
  3. 7
      contrib/pylightning/lightning/lightning.py
  4. 20
      doc/lightning-txprepare.7
  5. 5
      doc/lightning-txprepare.7.md
  6. 14
      doc/lightning-withdraw.7
  7. 5
      doc/lightning-withdraw.7.md
  8. 44
      tests/test_wallet.py
  9. 14
      wallet/walletrpc.c

3
CHANGELOG.md

@ -8,7 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- JSON API: `listfunds` now lists a blockheight for confirmed transactions
- JSON API: `fundchannel_start` result now includes field `scriptpubkey`
- JSON API: `fundchannel_start` now includes field `scriptpubkey`
- JSON API: `txprepare` and `withdraw` now accept an optional parameter `utxos`, a list of utxos to include in the prepared transaction
- bolt11: support for parsing feature bits (field `9`).

2
common/wallet_tx.c

@ -201,7 +201,7 @@ struct command_result *wtx_from_utxos(struct wallet_tx *tx,
if (!amount_sat_add(&total_amount, total_amount, utxos[i]->amount))
fatal("Overflow when computing input amount");
}
tx->utxos = utxos;
tx->utxos = tal_steal(tx, utxos);
if (!tx->all_funds && amount_sat_less(tx->amount, total_amount)
&& !amount_sat_sub(&tx->change, total_amount, tx->amount))

7
contrib/pylightning/lightning/lightning.py

@ -864,7 +864,7 @@ class LightningRpc(UnixDomainSocketRpc):
}
return self.call("waitsendpay", payload)
def withdraw(self, destination, satoshi, feerate=None, minconf=None):
def withdraw(self, destination, satoshi, feerate=None, minconf=None, utxos=None):
"""
Send to {destination} address {satoshi} (or "all")
amount via Bitcoin transaction. Only select outputs
@ -875,10 +875,12 @@ class LightningRpc(UnixDomainSocketRpc):
"satoshi": satoshi,
"feerate": feerate,
"minconf": minconf,
"utxos": utxos,
}
return self.call("withdraw", payload)
def txprepare(self, outputs, feerate=None, minconf=None):
def txprepare(self, outputs, feerate=None, minconf=None, utxos=None):
"""
Prepare a bitcoin transaction which sends to [outputs].
The format of output is like [{address1: amount1},
@ -892,6 +894,7 @@ class LightningRpc(UnixDomainSocketRpc):
"outputs": outputs,
"feerate": feerate,
"minconf": minconf,
"utxos": utxos,
}
return self.call("txprepare", payload)

20
doc/lightning-txprepare.7

@ -1,9 +1,9 @@
.TH "LIGHTNING-TXPREPARE" "7" "" "" "lightning-txprepare"
.SH NAME
lightning-txprepare - Command to prepare to withdraw funds from the internal wallet
.SH SYNOPSIS
\fBtxprepare\fR \fIoutputs\fR [\fIfeerate\fR] [\fIminconf\fR]
\fBtxprepare\fR \fIoutputs\fR [\fIfeerate\fR] [\fIminconf\fR] [\fIutxos\fR]
.SH DESCRIPTION
@ -11,6 +11,7 @@ The \fBtxprepare\fR RPC command creates an unsigned transaction which
spends funds from c-lightning’s internal wallet to the outputs specified
in \fIoutputs\fR\.
The \fIoutputs\fR is the array of output that include \fIdestination\fR
and \fIamount\fR({\fIdestination\fR: \fIamount\fR})\. Its format is like:
[{address1: amount1}, {address2: amount2}]
@ -18,19 +19,26 @@ or
[{address: \fIall\fR}]\.
It supports the any number of outputs\.
The \fIdestination\fR of output is the address which can be of any Bitcoin accepted
type, including bech32\.
The \fIamount\fR of output is the amount to be sent from the internal wallet
(expressed, as name suggests, in amount)\. The string \fIall\fR can be used to specify
all available funds\. Otherwise, it is in amount precision; it can be a whole
number, a whole number ending in \fIsat\fR, a whole number ending in \fI000msat\fR,
or a number with 1 to 8 decimal places ending in \fI000msat\fR\.
or a number with 1 to 8 decimal places ending in \fIbtc\fR\.
\fBtxprepare\fR is similar to the first part of a \fBwithdraw\fR command, but
supports multiple outputs and uses \fIoutputs\fR as parameter\. The second part
is provided by \fBtxsend\fR\.
\fIutxos\fR specifies the utxos to be used to fund the transaction, as an array
of "txid:vout"\. These must be drawn from the node's available UTXO set\.
.SH RETURN VALUE
On success, an object with attributes \fIunsigned_tx\fR and \fItxid\fR will be
@ -48,6 +56,7 @@ On failure, an error is reported and the transaction is not created\.
The following error codes may occur:
.RS
.IP \[bu]
-1: Catchall nonspecific error\.
.IP \[bu]
@ -56,9 +65,10 @@ fees) to create the transaction\.
.IP \[bu]
302: The dust limit is not met\.
.RE
.SH AUTHOR
Rusty Russell \fI<rusty@rustcorp.com.au\fR> is mainly responsible\.
Rusty Russell \fBNone\fR (\fI<rusty@rustcorp.com.au\fR)> is mainly responsible\.
.SH SEE ALSO
@ -66,5 +76,5 @@ Rusty Russell \fI<rusty@rustcorp.com.au\fR> is mainly responsible\.
.SH RESOURCES
Main web site: \fIhttps://github.com/ElementsProject/lightning\fR
Main web site: \fBNone\fR (\fIhttps://github.com/ElementsProject/lightning\fR)

5
doc/lightning-txprepare.7.md

@ -4,7 +4,7 @@ lightning-txprepare -- Command to prepare to withdraw funds from the internal wa
SYNOPSIS
--------
**txprepare** *outputs* \[*feerate*\] \[*minconf*\]
**txprepare** *outputs* \[*feerate*\] \[*minconf*\] \[*utxos*\]
DESCRIPTION
-----------
@ -33,6 +33,9 @@ or a number with 1 to 8 decimal places ending in *btc*.
supports multiple outputs and uses *outputs* as parameter. The second part
is provided by **txsend**.
*utxos* specifies the utxos to be used to fund the transaction, as an array
of "txid:vout". These must be drawn from the node's available UTXO set.
RETURN VALUE
------------

14
doc/lightning-withdraw.7

@ -1,9 +1,9 @@
.TH "LIGHTNING-WITHDRAW" "7" "" "" "lightning-withdraw"
.SH NAME
lightning-withdraw - Command for withdrawing funds from the internal wallet
.SH SYNOPSIS
\fBwithdraw\fR \fIdestination\fR \fIsatoshi\fR [\fIfeerate\fR] [\fIminconf\fR]
\fBwithdraw\fR \fIdestination\fR \fIsatoshi\fR [\fIfeerate\fR] [\fIminconf\fR] [\fIutxos\fR]
.SH DESCRIPTION
@ -37,6 +37,10 @@ the suffix is equivalent to \fIperkb\fR\.
\fIminconf\fR specifies the minimum number of confirmations that used
outputs should have\. Default is 1\.
\fIutxos\fR specifies the utxos to be used to be withdrawn from, as an array
of "txid:vout"\. These must be drawn from the node's available UTXO set\.
.SH RETURN VALUE
On success, an object with attributes \fItx\fR and \fItxid\fR will be returned\.
@ -52,6 +56,7 @@ created\.
The following error codes may occur:
.RS
.IP \[bu]
-1: Catchall nonspecific error\.
.IP \[bu]
@ -60,9 +65,10 @@ fees) to create the transaction\.
.IP \[bu]
302: The dust limit is not met\.
.RE
.SH AUTHOR
Felix \fI<fixone@gmail.com\fR> is mainly responsible\.
Felix \fBNone\fR (\fI<fixone@gmail.com\fR)> is mainly responsible\.
.SH SEE ALSO
@ -71,5 +77,5 @@ Felix \fI<fixone@gmail.com\fR> is mainly responsible\.
.SH RESOURCES
Main web site: \fIhttps://github.com/ElementsProject/lightning\fR
Main web site: \fBNone\fR (\fIhttps://github.com/ElementsProject/lightning\fR)

5
doc/lightning-withdraw.7.md

@ -4,7 +4,7 @@ lightning-withdraw -- Command for withdrawing funds from the internal wallet
SYNOPSIS
--------
**withdraw** *destination* *satoshi* \[*feerate*\] \[*minconf*\]
**withdraw** *destination* *satoshi* \[*feerate*\] \[*minconf*\] \[*utxos*\]
DESCRIPTION
-----------
@ -34,6 +34,9 @@ the suffix is equivalent to *perkb*.
*minconf* specifies the minimum number of confirmations that used
outputs should have. Default is 1.
*utxos* specifies the utxos to be used to be withdrawn from, as an array
of "txid:vout". These must be drawn from the node's available UTXO set.
RETURN VALUE
------------

44
tests/test_wallet.py

@ -137,6 +137,26 @@ def test_withdraw(node_factory, bitcoind):
with pytest.raises(RpcError, match=r'Cannot afford transaction'):
l1.rpc.withdraw(waddr, 'all')
# Add some funds to withdraw
for i in range(10):
l1.bitcoin.rpc.sendtoaddress(addr, amount / 10**8 + 0.01)
bitcoind.generate_block(1)
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 10)
# Try passing in a utxo set
utxos = [utxo["txid"] + ":" + str(utxo["output"]) for utxo in l1.rpc.listfunds()["outputs"]][:4]
withdrawal = l1.rpc.withdraw(waddr, 2 * amount, utxos=utxos)
decode = bitcoind.rpc.decoderawtransaction(withdrawal['tx'])
assert decode['txid'] == withdrawal['txid']
# Check that correct utxos are included
assert len(decode['vin']) == 4
vins = ["{}:{}".format(v['txid'], v['vout']) for v in decode['vin']]
for utxo in utxos:
assert utxo in vins
def test_minconf_withdraw(node_factory, bitcoind):
"""Issue 2518: ensure that ridiculous confirmation levels don't overflow
@ -280,9 +300,31 @@ def test_txprepare(node_factory, bitcoind):
assert decode['vout'][0]['value'] > Decimal(amount * 10) / 10**8 - Decimal(0.0003)
assert decode['vout'][0]['scriptPubKey']['type'] == 'witness_v0_keyhash'
assert decode['vout'][0]['scriptPubKey']['addresses'] == ['bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg']
l1.rpc.txdiscard(prep4['txid'])
# Try passing in a utxo set
utxos = [utxo["txid"] + ":" + str(utxo["output"]) for utxo in l1.rpc.listfunds()["outputs"]][:4]
prep5 = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg':
Millisatoshi(amount * 3.5 * 1000)}], utxos=utxos)
decode = bitcoind.rpc.decoderawtransaction(prep5['unsigned_tx'])
assert decode['txid'] == prep5['txid']
# Check that correct utxos are included
assert len(decode['vin']) == 4
vins = ["{}:{}".format(v['txid'], v['vout']) for v in decode['vin']]
for utxo in utxos:
assert utxo in vins
# We should have a change output, so this is exact
assert len(decode['vout']) == 2
assert decode['vout'][1]['value'] == Decimal(amount * 3.5) / 10**8
assert decode['vout'][1]['scriptPubKey']['type'] == 'witness_v0_keyhash'
assert decode['vout'][1]['scriptPubKey']['addresses'] == ['bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg']
# Discard prep4 and get all funds again
l1.rpc.txdiscard(prep4['txid'])
l1.rpc.txdiscard(prep5['txid'])
with pytest.raises(RpcError, match=r'this destination wants all satoshi. The count of outputs can\'t be more than 1'):
prep5 = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': Millisatoshi(amount * 3 * 1000)},
{'bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080': 'all'}])

14
wallet/walletrpc.c

@ -165,6 +165,7 @@ static struct command_result *json_prepare_tx(struct command *cmd,
const jsmntok_t *outputstok, *t;
const u8 *destination = NULL;
size_t out_len, i;
const struct utxo **chosen_utxos;
*utx = tal(cmd, struct unreleased_tx);
(*utx)->wtx = tal(*utx, struct wallet_tx);
@ -177,6 +178,7 @@ static struct command_result *json_prepare_tx(struct command *cmd,
p_req("outputs", param_array, &outputstok),
p_opt("feerate", param_feerate, &feerate_per_kw),
p_opt_def("minconf", param_number, &minconf, 1),
p_opt("utxos", param_utxos, &chosen_utxos),
NULL)) {
/* For generating help, give new-style. */
@ -204,6 +206,7 @@ static struct command_result *json_prepare_tx(struct command *cmd,
p_req("satoshi", param_wtx, (*utx)->wtx),
p_opt("feerate", param_feerate, &feerate_per_kw),
p_opt_def("minconf", param_number, &minconf, 1),
p_opt("utxos", param_utxos, &chosen_utxos),
NULL))
return command_param_failed();
}
@ -297,8 +300,15 @@ static struct command_result *json_prepare_tx(struct command *cmd,
create_tx:
(*utx)->outputs = tal_steal(*utx, outputs);
res = wtx_select_utxos((*utx)->wtx, *feerate_per_kw,
out_len, maxheight);
if (chosen_utxos)
res = wtx_from_utxos((*utx)->wtx, *feerate_per_kw,
out_len, maxheight,
chosen_utxos);
else
res = wtx_select_utxos((*utx)->wtx, *feerate_per_kw,
out_len, maxheight);
if (res)
return res;

Loading…
Cancel
Save