mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 05:12:45 +01:00
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
This commit is contained in:
parent
f3159ec4ac
commit
ea95ad9c12
@ -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
generated
15
doc/lightning-fundpsbt.7
generated
@ -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
|
||||
|
@ -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
generated
15
doc/lightning-utxopsbt.7
generated
@ -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
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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]),
|
||||
|
@ -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…
Reference in New Issue
Block a user