Browse Source

fund/utxopsbt: flag 'excess_as_change' to add a change output for excess

In the case where you want a PSBT and also want the output to be added
as a change address, use `excess_as_change` = true.

Generates a change address to use. If you want to pay the excess
elsewhere, you will have to add separately.

Changelog-Added: JSON-RPC: Add new parameter `excess_as_change` to fundpsbt+utxopsbt
master
niftynei 4 years ago
committed by neil saitug
parent
commit
ea95ad9c12
  1. 6
      contrib/pyln-client/pyln/client/lightning.py
  2. 15
      doc/lightning-fundpsbt.7
  3. 11
      doc/lightning-fundpsbt.7.md
  4. 15
      doc/lightning-utxopsbt.7
  5. 11
      doc/lightning-utxopsbt.7.md
  6. 35
      tests/test_wallet.py
  7. 57
      wallet/reservation.c

6
contrib/pyln-client/pyln/client/lightning.py

@ -1319,7 +1319,7 @@ class LightningRpc(UnixDomainSocketRpc):
}
return self.call("unreserveinputs", payload)
def fundpsbt(self, satoshi, feerate, startweight, minconf=None, reserve=True, locktime=None, min_witness_weight=None):
def fundpsbt(self, satoshi, feerate, startweight, minconf=None, reserve=True, locktime=None, min_witness_weight=None, excess_as_change=False):
"""
Create a PSBT with inputs sufficient to give an output of satoshi.
"""
@ -1331,10 +1331,11 @@ class LightningRpc(UnixDomainSocketRpc):
"reserve": reserve,
"locktime": locktime,
"min_witness_weight": min_witness_weight,
"excess_as_change": excess_as_change,
}
return self.call("fundpsbt", payload)
def utxopsbt(self, satoshi, feerate, startweight, utxos, reserve=True, reservedok=False, locktime=None, min_witness_weight=None):
def utxopsbt(self, satoshi, feerate, startweight, utxos, reserve=True, reservedok=False, locktime=None, min_witness_weight=None, excess_as_change=False):
"""
Create a PSBT with given inputs, to give an output of satoshi.
"""
@ -1347,6 +1348,7 @@ class LightningRpc(UnixDomainSocketRpc):
"reservedok": reservedok,
"locktime": locktime,
"min_witness_weight": min_witness_weight,
"excess_as_change": excess_as_change,
}
return self.call("utxopsbt", payload)

15
doc/lightning-fundpsbt.7

@ -3,7 +3,7 @@
lightning-fundpsbt - Command to populate PSBT inputs from the wallet
.SH SYNOPSIS
\fBfundpsbt\fR \fIsatoshi\fR \fIfeerate\fR \fIstartweight\fR [\fIminconf\fR] [\fIreserve\fR] [\fIlocktime\fR] [\fImin_witness_weight\fR]
\fBfundpsbt\fR \fIsatoshi\fR \fIfeerate\fR \fIstartweight\fR [\fIminconf\fR] [\fIreserve\fR] [\fIlocktime\fR] [\fImin_witness_weight\fR] [\fIexcess_as_change\fR]
.SH DESCRIPTION
@ -48,6 +48,10 @@ block height\.
witness\. If the actual witness weight is greater than the provided minimum,
the actual witness weight will be used\.
\fIexcess_as_change\fR is an optional boolean to flag to add a change output
for the excess sats\.
.SH EXAMPLE USAGE
Let's assume the caller is trying to produce a 100,000 satoshi output\.
@ -85,6 +89,13 @@ If \fIreserve\fR was true, then a \fIreservations\fR array is returned,
exactly like \fIreserveinputs\fR\.
If \fIexcess_as_change\fR is true and the excess is enough to cover
an additional output above the \fBdust_limit\fR, then an output is
added to the PSBT for the excess amount\. The \fIexcess_msat\fR will
be zero\. A \fIchange_outnum\fR will be returned with the index of
the change output\.
On error the returned object will contain \fBcode\fR and \fBmessage\fR properties,
with \fBcode\fR being one of the following:
@ -109,4 +120,4 @@ Rusty Russell \fI<rusty@rustcorp.com.au\fR> is mainly responsible\.
Main web site: \fIhttps://github.com/ElementsProject/lightning\fR
\" SHA256STAMP:a8b9705274638127c2f5ec4e97ed94e6d7f6b6b10a76c2248e8bc8b36dd804ff
\" SHA256STAMP:b9ecd1408f0e5d8424e530ab44ab21b0e773c537c3512b68b31f197851d9abce

11
doc/lightning-fundpsbt.7.md

@ -4,7 +4,7 @@ lightning-fundpsbt -- Command to populate PSBT inputs from the wallet
SYNOPSIS
--------
**fundpsbt** *satoshi* *feerate* *startweight* \[*minconf*\] \[*reserve*\] \[*locktime*\] \[*min_witness_weight*\]
**fundpsbt** *satoshi* *feerate* *startweight* \[*minconf*\] \[*reserve*\] \[*locktime*\] \[*min_witness_weight*\] \[*excess_as_change*\]
DESCRIPTION
-----------
@ -43,6 +43,9 @@ block height.
witness. If the actual witness weight is greater than the provided minimum,
the actual witness weight will be used.
*excess_as_change* is an optional boolean to flag to add a change output
for the excess sats.
EXAMPLE USAGE
-------------
@ -77,6 +80,12 @@ for the weights of the inputs and startweight.
If *reserve* was true, then a *reservations* array is returned,
exactly like *reserveinputs*.
If *excess_as_change* is true and the excess is enough to cover
an additional output above the `dust_limit`, then an output is
added to the PSBT for the excess amount. The *excess_msat* will
be zero. A *change_outnum* will be returned with the index of
the change output.
On error the returned object will contain `code` and `message` properties,
with `code` being one of the following:

15
doc/lightning-utxopsbt.7

@ -3,7 +3,7 @@
lightning-utxopsbt - Command to populate PSBT inputs from given UTXOs
.SH SYNOPSIS
\fButxopsbt\fR \fIsatoshi\fR \fIfeerate\fR \fIstartweight\fR \fIutxos\fR [\fIreserve\fR] [\fIreservedok\fR] [\fIlocktime\fR] [\fImin_witness_weight\fR]
\fButxopsbt\fR \fIsatoshi\fR \fIfeerate\fR \fIstartweight\fR \fIutxos\fR [\fIreserve\fR] [\fIreservedok\fR] [\fIlocktime\fR] [\fImin_witness_weight\fR] [\fIexcess_as_change\fR]
.SH DESCRIPTION
@ -36,6 +36,10 @@ block height\.
witness\. If the actual witness weight is greater than the provided minimum,
the actual witness weight will be used\.
\fIexcess_as_change\fR is an optional boolean to flag to add a change output
for the excess sats\.
.SH RETURN VALUE
On success, returns the \fIpsbt\fR containing the inputs, \fIfeerate_per_kw\fR
@ -51,6 +55,13 @@ If \fIreserve\fR was true, then a \fIreservations\fR array is returned,
exactly like \fIreserveinputs\fR\.
If \fIexcess_as_change\fR is true and the excess is enough to cover
an additional output above the \fBdust_limit\fR, then an output is
added to the PSBT for the excess amount\. The \fIexcess_msat\fR will
be zero\. A \fIchange_outnum\fR will be returned with the index of
the change output\.
On error the returned object will contain \fBcode\fR and \fBmessage\fR properties,
with \fBcode\fR being one of the following:
@ -75,4 +86,4 @@ Rusty Russell \fI<rusty@rustcorp.com.au\fR> is mainly responsible\.
Main web site: \fIhttps://github.com/ElementsProject/lightning\fR
\" SHA256STAMP:777710bb963f435193e92a55344c740c123d7aa4d54bf573c99a616f59eeee54
\" SHA256STAMP:a56057522ed576f232d7d96794f39cc67a5a1b1bb7b6f7912e42c3769555e007

11
doc/lightning-utxopsbt.7.md

@ -4,7 +4,7 @@ lightning-utxopsbt -- Command to populate PSBT inputs from given UTXOs
SYNOPSIS
--------
**utxopsbt** *satoshi* *feerate* *startweight* *utxos* \[*reserve*\] \[*reservedok*\] \[*locktime*\] \[*min_witness_weight*\]
**utxopsbt** *satoshi* *feerate* *startweight* *utxos* \[*reserve*\] \[*reservedok*\] \[*locktime*\] \[*min_witness_weight*\] \[*excess_as_change*\]
DESCRIPTION
-----------
@ -33,6 +33,9 @@ block height.
witness. If the actual witness weight is greater than the provided minimum,
the actual witness weight will be used.
*excess_as_change* is an optional boolean to flag to add a change output
for the excess sats.
RETURN VALUE
------------
@ -47,6 +50,12 @@ for the weights of the inputs and *startweight*.
If *reserve* was true, then a *reservations* array is returned,
exactly like *reserveinputs*.
If *excess_as_change* is true and the excess is enough to cover
an additional output above the `dust_limit`, then an output is
added to the PSBT for the excess amount. The *excess_msat* will
be zero. A *change_outnum* will be returned with the index of
the change output.
On error the returned object will contain `code` and `message` properties,
with `code` being one of the following:

35
tests/test_wallet.py

@ -515,6 +515,18 @@ def test_fundpsbt(node_factory, bitcoind, chainparams):
with pytest.raises(RpcError, match=r"not afford"):
l1.rpc.fundpsbt(amount // 2, feerate, 0, minconf=2)
funding3 = l1.rpc.fundpsbt(amount // 2, feerate, 0, reserve=False, excess_as_change=True)
assert funding3['excess_msat'] == Millisatoshi(0)
# Should have the excess msat as the output value (minus fee for change)
psbt = bitcoind.rpc.decodepsbt(funding3['psbt'])
change = Millisatoshi("{}btc".format(psbt['tx']['vout'][funding3['change_outnum']]['value']))
# The weight should be greater (now includes change output)
change_weight = funding3['estimated_final_weight'] - funding['estimated_final_weight']
assert change_weight > 0
# Check that the amount is ok (equal to excess minus change fee)
change_fee = Millisatoshi(7500 * change_weight)
assert funding['excess_msat'] == change + change_fee
# Should get two inputs.
psbt = bitcoind.rpc.decodepsbt(l1.rpc.fundpsbt(amount, feerate, 0, reserve=False)['psbt'])
assert len(psbt['tx']['vin']) == 2
@ -602,6 +614,29 @@ def test_utxopsbt(node_factory, bitcoind, chainparams):
['{}:{}'.format(outputs[0][0], outputs[0][1]),
'{}:{}'.format(outputs[1][0], outputs[1][1])])
funding3 = l1.rpc.utxopsbt(amount // 2, feerate, 0,
['{}:{}'.format(outputs[0][0], outputs[0][1])],
reserve=False,
excess_as_change=True)
assert funding3['excess_msat'] == Millisatoshi(0)
# Should have the excess msat as the output value (minus fee for change)
psbt = bitcoind.rpc.decodepsbt(funding3['psbt'])
change = Millisatoshi("{}btc".format(psbt['tx']['vout'][funding3['change_outnum']]['value']))
# The weight should be greater (now includes change output)
change_weight = funding3['estimated_final_weight'] - funding['estimated_final_weight']
assert change_weight > 0
# Check that the amount is ok (equal to excess minus change fee)
change_fee = Millisatoshi(fee_val * change_weight // 1000 * 1000)
assert funding['excess_msat'] == change + change_fee
# Do it again, but without enough for change!
funding4 = l1.rpc.utxopsbt(amount - 3500,
feerate, 0,
['{}:{}'.format(outputs[0][0], outputs[0][1])],
reserve=False,
excess_as_change=True)
assert 'change_outnum' not in funding4
# Should get two inputs (and reserve!)
funding = l1.rpc.utxopsbt(amount, feerate, 0,
['{}:{}'.format(outputs[0][0], outputs[0][1]),

57
wallet/reservation.c

@ -1,6 +1,7 @@
/* Dealing with reserving UTXOs */
#include <bitcoin/psbt.h>
#include <bitcoin/script.h>
#include <bitcoin/tx.h>
#include <ccan/mem/mem.h>
#include <common/json_command.h>
#include <common/json_helpers.h>
@ -319,10 +320,12 @@ static struct command_result *finish_psbt(struct command *cmd,
size_t weight,
struct amount_sat excess,
bool reserve,
u32 *locktime)
u32 *locktime,
bool excess_as_change)
{
struct json_stream *response;
struct wally_psbt *psbt;
size_t change_outnum;
u32 current_height = get_block_height(cmd->ld->topology);
/* Setting the locktime to the next block to be mined has multiple
@ -347,6 +350,44 @@ static struct command_result *finish_psbt(struct command *cmd,
cmd->ld->wallet->bip32_base,
*locktime, BITCOIN_TX_RBF_SEQUENCE);
/* Should we add a change output for the excess? */
if (excess_as_change) {
struct amount_sat change;
struct pubkey pubkey;
s64 keyidx;
u8 *b32script;
/* Checks for dust, returns 0sat if below dust */
change = change_amount(excess, feerate_per_kw);
if (!amount_sat_greater(change, AMOUNT_SAT(0))) {
excess_as_change = false;
goto fee_calc;
}
/* Get a change adddress */
keyidx = wallet_get_newindex(cmd->ld);
if (keyidx < 0)
return command_fail(cmd, LIGHTNINGD,
"Failed to generate change address."
" Keys exhausted.");
if (!bip32_pubkey(cmd->ld->wallet->bip32_base, &pubkey, keyidx))
return command_fail(cmd, LIGHTNINGD,
"Failed to generate change address."
" Keys generation failure");
b32script = scriptpubkey_p2wpkh(tmpctx, &pubkey);
txfilter_add_scriptpubkey(cmd->ld->owned_txfilter, b32script);
change_outnum = psbt->num_outputs;
psbt_append_output(psbt, b32script, change);
/* Set excess to zero */
excess = AMOUNT_SAT(0);
/* Add additional weight of output */
weight += bitcoin_tx_output_weight(
BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN);
}
fee_calc:
/* Add a fee output if this is elements */
if (is_elements(chainparams)) {
struct amount_sat est_fee =
@ -358,6 +399,8 @@ static struct command_result *finish_psbt(struct command *cmd,
json_add_num(response, "feerate_per_kw", feerate_per_kw);
json_add_num(response, "estimated_final_weight", weight);
json_add_amount_sat_only(response, "excess_msat", excess);
if (excess_as_change)
json_add_num(response, "change_outnum", change_outnum);
if (reserve)
reserve_and_report(response, cmd->ld->wallet, current_height,
utxos);
@ -388,7 +431,7 @@ static struct command_result *json_fundpsbt(struct command *cmd,
u32 *feerate_per_kw;
u32 *minconf, *weight, *min_witness_weight;
struct amount_sat *amount, input, diff;
bool all, *reserve;
bool all, *reserve, *excess_as_change;
u32 *locktime, maxheight;
if (!param(cmd, buffer, params,
@ -400,6 +443,8 @@ static struct command_result *json_fundpsbt(struct command *cmd,
p_opt("locktime", param_number, &locktime),
p_opt_def("min_witness_weight", param_number,
&min_witness_weight, 0),
p_opt_def("excess_as_change", param_bool,
&excess_as_change, false),
NULL))
return command_param_failed();
@ -474,7 +519,7 @@ static struct command_result *json_fundpsbt(struct command *cmd,
}
return finish_psbt(cmd, utxos, *feerate_per_kw, *weight, diff, *reserve,
locktime);
locktime, *excess_as_change);
}
static const struct json_command fundpsbt_command = {
@ -558,7 +603,7 @@ static struct command_result *json_utxopsbt(struct command *cmd,
{
struct utxo **utxos;
u32 *feerate_per_kw, *weight, *min_witness_weight;
bool all, *reserve, *reserved_ok;
bool all, *reserve, *reserved_ok, *excess_as_change;
struct amount_sat *amount, input, excess;
u32 current_height, *locktime;
@ -572,6 +617,8 @@ static struct command_result *json_utxopsbt(struct command *cmd,
p_opt("locktime", param_number, &locktime),
p_opt_def("min_witness_weight", param_number,
&min_witness_weight, 0),
p_opt_def("excess_as_change", param_bool,
&excess_as_change, false),
NULL))
return command_param_failed();
@ -615,7 +662,7 @@ static struct command_result *json_utxopsbt(struct command *cmd,
}
return finish_psbt(cmd, utxos, *feerate_per_kw, *weight, excess,
*reserve, locktime);
*reserve, locktime, *excess_as_change);
}
static const struct json_command utxopsbt_command = {
"utxopsbt",

Loading…
Cancel
Save