mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 05:12:45 +01:00
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","..."]
This commit is contained in:
parent
b1f05795a8
commit
5c70890efa
@ -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`).
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
------------
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
------------
|
||||
|
||||
|
@ -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'}])
|
||||
|
@ -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…
Reference in New Issue
Block a user