mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-17 19:03:42 +01:00
utxopsbt: let caller specify locktime, add tests and python binding.
Changelog-Added: JSON-RPC: `utxopsbt` takes a new `locktime` parameter Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
14baaaa8ba
commit
aab3808668
@ -1140,6 +1140,21 @@ class LightningRpc(UnixDomainSocketRpc):
|
||||
}
|
||||
return self.call("fundpsbt", payload)
|
||||
|
||||
def utxopsbt(self, satoshi, feerate, startweight, utxos, reserve=True, reservedok=False, locktime=None):
|
||||
"""
|
||||
Create a PSBT with given inputs, to give an output of satoshi.
|
||||
"""
|
||||
payload = {
|
||||
"satoshi": satoshi,
|
||||
"feerate": feerate,
|
||||
"startweight": startweight,
|
||||
"utxos": utxos,
|
||||
"reserve": reserve,
|
||||
"reservedok": reservedok,
|
||||
"locktime": locktime,
|
||||
}
|
||||
return self.call("utxopsbt", payload)
|
||||
|
||||
def signpsbt(self, psbt):
|
||||
"""
|
||||
Add internal wallet's signatures to PSBT
|
||||
|
6
doc/lightning-utxopsbt.7
generated
6
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]
|
||||
\fButxopsbt\fR \fIsatoshi\fR \fIfeerate\fR \fIstartweight\fR \fIutxos\fR [\fIreserve\fR] [\fIreservedok\fR] [\fIlocktime\fR]
|
||||
|
||||
.SH DESCRIPTION
|
||||
|
||||
@ -27,6 +27,10 @@ is equivalent to setting it to zero)\.
|
||||
Unless \fIreservedok\fR is set to true (default is false) it will also fail
|
||||
if any of the \fIutxos\fR are already reserved\.
|
||||
|
||||
|
||||
\fIlocktime\fR is an optional locktime: if not set, it is set to a recent
|
||||
block height\.
|
||||
|
||||
.SH RETURN VALUE
|
||||
|
||||
On success, returns the \fIpsbt\fR containing the inputs, \fIfeerate_per_kw\fR
|
||||
|
@ -4,7 +4,7 @@ lightning-utxopsbt -- Command to populate PSBT inputs from given UTXOs
|
||||
SYNOPSIS
|
||||
--------
|
||||
|
||||
**utxopsbt** *satoshi* *feerate* *startweight* *utxos* \[*reserve*\] \[*reservedok*\]
|
||||
**utxopsbt** *satoshi* *feerate* *startweight* *utxos* \[*reserve*\] \[*reservedok*\] \[*locktime*\]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@ -26,6 +26,9 @@ is equivalent to setting it to zero).
|
||||
Unless *reservedok* is set to true (default is false) it will also fail
|
||||
if any of the *utxos* are already reserved.
|
||||
|
||||
*locktime* is an optional locktime: if not set, it is set to a recent
|
||||
block height.
|
||||
|
||||
RETURN VALUE
|
||||
------------
|
||||
|
||||
|
@ -559,6 +559,92 @@ def test_fundpsbt(node_factory, bitcoind, chainparams):
|
||||
l1.rpc.fundpsbt(amount // 2, feerate, 0)
|
||||
|
||||
|
||||
def test_utxopsbt(node_factory, bitcoind):
|
||||
amount = 1000000
|
||||
l1 = node_factory.get_node()
|
||||
|
||||
outputs = []
|
||||
# Add a medley of funds to withdraw later, bech32 + p2sh-p2wpkh
|
||||
txid = bitcoind.rpc.sendtoaddress(l1.rpc.newaddr()['bech32'],
|
||||
amount / 10**8)
|
||||
outputs.append((txid, bitcoind.rpc.gettransaction(txid)['details'][0]['vout']))
|
||||
txid = bitcoind.rpc.sendtoaddress(l1.rpc.newaddr('p2sh-segwit')['p2sh-segwit'],
|
||||
amount / 10**8)
|
||||
outputs.append((txid, bitcoind.rpc.gettransaction(txid)['details'][0]['vout']))
|
||||
|
||||
bitcoind.generate_block(1)
|
||||
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == len(outputs))
|
||||
|
||||
feerate = '7500perkw'
|
||||
|
||||
# Explicitly spend the first output above.
|
||||
funding = l1.rpc.utxopsbt(amount // 2, feerate, 0,
|
||||
['{}:{}'.format(outputs[0][0], outputs[0][1])],
|
||||
reserve=False)
|
||||
psbt = bitcoind.rpc.decodepsbt(funding['psbt'])
|
||||
# We can fuzz this up to 99 blocks back.
|
||||
assert psbt['tx']['locktime'] > bitcoind.rpc.getblockcount() - 100
|
||||
assert psbt['tx']['locktime'] <= bitcoind.rpc.getblockcount()
|
||||
assert len(psbt['tx']['vin']) == 1
|
||||
assert funding['excess_msat'] > Millisatoshi(0)
|
||||
assert funding['excess_msat'] < Millisatoshi(amount // 2 * 1000)
|
||||
assert funding['feerate_per_kw'] == 7500
|
||||
assert 'estimated_final_weight' in funding
|
||||
assert 'reservations' not in funding
|
||||
|
||||
# This should add 99 to the weight, but otherwise be identical except for locktime.
|
||||
funding2 = l1.rpc.utxopsbt(amount // 2, feerate, 99,
|
||||
['{}:{}'.format(outputs[0][0], outputs[0][1])],
|
||||
reserve=False, locktime=bitcoind.rpc.getblockcount() + 1)
|
||||
psbt2 = bitcoind.rpc.decodepsbt(funding2['psbt'])
|
||||
assert psbt2['tx']['locktime'] == bitcoind.rpc.getblockcount() + 1
|
||||
assert psbt2['tx']['vin'] == psbt['tx']['vin']
|
||||
assert psbt2['tx']['vout'] == psbt['tx']['vout']
|
||||
assert funding2['excess_msat'] < funding['excess_msat']
|
||||
assert funding2['feerate_per_kw'] == 7500
|
||||
assert funding2['estimated_final_weight'] == funding['estimated_final_weight'] + 99
|
||||
assert 'reservations' not in funding2
|
||||
|
||||
# Cannot afford this one (too much)
|
||||
with pytest.raises(RpcError, match=r"not afford"):
|
||||
l1.rpc.utxopsbt(amount, feerate, 0,
|
||||
['{}:{}'.format(outputs[0][0], outputs[0][1])])
|
||||
|
||||
# Nor this (even with both)
|
||||
with pytest.raises(RpcError, match=r"not afford"):
|
||||
l1.rpc.utxopsbt(amount * 2, feerate, 0,
|
||||
['{}:{}'.format(outputs[0][0], outputs[0][1]),
|
||||
'{}:{}'.format(outputs[1][0], outputs[1][1])])
|
||||
|
||||
# Should get two inputs (and reserve!)
|
||||
funding = l1.rpc.utxopsbt(amount, feerate, 0,
|
||||
['{}:{}'.format(outputs[0][0], outputs[0][1]),
|
||||
'{}:{}'.format(outputs[1][0], outputs[1][1])])
|
||||
psbt = bitcoind.rpc.decodepsbt(funding['psbt'])
|
||||
assert len(psbt['tx']['vin']) == 2
|
||||
assert len(funding['reservations']) == 2
|
||||
assert funding['reservations'][0]['txid'] == outputs[0][0]
|
||||
assert funding['reservations'][0]['vout'] == outputs[0][1]
|
||||
assert funding['reservations'][0]['was_reserved'] is False
|
||||
assert funding['reservations'][0]['reserved'] is True
|
||||
assert funding['reservations'][1]['txid'] == outputs[1][0]
|
||||
assert funding['reservations'][1]['vout'] == outputs[1][1]
|
||||
assert funding['reservations'][1]['was_reserved'] is False
|
||||
assert funding['reservations'][1]['reserved'] is True
|
||||
|
||||
# Should refuse to use reserved outputs.
|
||||
with pytest.raises(RpcError, match=r"already reserved"):
|
||||
l1.rpc.utxopsbt(amount, feerate, 0,
|
||||
['{}:{}'.format(outputs[0][0], outputs[0][1]),
|
||||
'{}:{}'.format(outputs[1][0], outputs[1][1])])
|
||||
|
||||
# Unless we tell it that's ok.
|
||||
l1.rpc.utxopsbt(amount, feerate, 0,
|
||||
['{}:{}'.format(outputs[0][0], outputs[0][1]),
|
||||
'{}:{}'.format(outputs[1][0], outputs[1][1])],
|
||||
reservedok=True)
|
||||
|
||||
|
||||
def test_sign_and_send_psbt(node_factory, bitcoind, chainparams):
|
||||
"""
|
||||
Tests for the sign + send psbt RPCs
|
||||
|
@ -427,7 +427,7 @@ static struct command_result *json_utxopsbt(struct command *cmd,
|
||||
u32 *feerate_per_kw, *weight;
|
||||
bool all, *reserve, *reserved_ok;
|
||||
struct amount_sat *amount, input, excess;
|
||||
u32 current_height;
|
||||
u32 current_height, *locktime;
|
||||
|
||||
if (!param(cmd, buffer, params,
|
||||
p_req("satoshi", param_sat_or_all, &amount),
|
||||
@ -436,6 +436,7 @@ static struct command_result *json_utxopsbt(struct command *cmd,
|
||||
p_req("utxos", param_txout, &utxos),
|
||||
p_opt_def("reserve", param_bool, &reserve, true),
|
||||
p_opt_def("reservedok", param_bool, &reserved_ok, false),
|
||||
p_opt("locktime", param_number, &locktime),
|
||||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
@ -479,7 +480,7 @@ static struct command_result *json_utxopsbt(struct command *cmd,
|
||||
}
|
||||
|
||||
return finish_psbt(cmd, utxos, *feerate_per_kw, *weight, excess,
|
||||
*reserve, NULL);
|
||||
*reserve, locktime);
|
||||
}
|
||||
static const struct json_command utxopsbt_command = {
|
||||
"utxopsbt",
|
||||
|
Loading…
Reference in New Issue
Block a user