mirror of
https://github.com/aljazceru/lightning.git
synced 2026-01-06 15:44:21 +01:00
reserve/unreserve input: new RPC commands for reserving inputs/outputs
Reserve and unreserve wallet UTXOs using a PSBT which includes those inputs. Note that currently we unreserve inputs everytime the node restarts. This will be addressed in a future commit. Changelog-Added: JSON-RPC: Adds two new rpc methods, `reserveinputs` and `unreserveinputs`, which allow for reserving or unreserving wallet UTXOs
This commit is contained in:
committed by
Christian Decker
parent
b90be4f6c8
commit
103dce63ef
@@ -436,6 +436,131 @@ def test_txprepare(node_factory, bitcoind, chainparams):
|
||||
assert decode['vout'][changenum]['scriptPubKey']['type'] == 'witness_v0_keyhash'
|
||||
|
||||
|
||||
def test_reserveinputs(node_factory, bitcoind, chainparams):
|
||||
"""
|
||||
Reserve inputs is basically the same as txprepare, with the
|
||||
slight exception that 'reserveinputs' doesn't keep the
|
||||
unsent transaction around
|
||||
"""
|
||||
amount = 1000000
|
||||
total_outs = 12
|
||||
l1 = node_factory.get_node(feerates=(7500, 7500, 7500, 7500))
|
||||
addr = chainparams['example_addr']
|
||||
|
||||
# Add a medley of funds to withdraw later, bech32 + p2sh-p2wpkh
|
||||
for i in range(total_outs // 2):
|
||||
bitcoind.rpc.sendtoaddress(l1.rpc.newaddr()['bech32'],
|
||||
amount / 10**8)
|
||||
bitcoind.rpc.sendtoaddress(l1.rpc.newaddr('p2sh-segwit')['p2sh-segwit'],
|
||||
amount / 10**8)
|
||||
|
||||
bitcoind.generate_block(1)
|
||||
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == total_outs)
|
||||
|
||||
utxo_count = 8
|
||||
sent = Decimal('0.01') * (utxo_count - 1)
|
||||
reserved = l1.rpc.reserveinputs(outputs=[{addr: Millisatoshi(amount * (utxo_count - 1) * 1000)}])
|
||||
assert reserved['feerate_per_kw'] == 7500
|
||||
psbt = bitcoind.rpc.decodepsbt(reserved['psbt'])
|
||||
out_found = False
|
||||
|
||||
assert len(psbt['inputs']) == utxo_count
|
||||
outputs = l1.rpc.listfunds()['outputs']
|
||||
assert len([x for x in outputs if not x['reserved']]) == total_outs - utxo_count
|
||||
assert len([x for x in outputs if x['reserved']]) == utxo_count
|
||||
total_outs -= utxo_count
|
||||
saved_input = psbt['tx']['vin'][0]
|
||||
|
||||
# We should have two outputs
|
||||
for vout in psbt['tx']['vout']:
|
||||
if vout['scriptPubKey']['addresses'][0] == addr:
|
||||
assert vout['value'] == sent
|
||||
out_found = True
|
||||
assert out_found
|
||||
|
||||
# Do it again, but for too many inputs
|
||||
utxo_count = 12 - utxo_count + 1
|
||||
sent = Decimal('0.01') * (utxo_count - 1)
|
||||
with pytest.raises(RpcError, match=r"Cannot afford transaction"):
|
||||
reserved = l1.rpc.reserveinputs(outputs=[{addr: Millisatoshi(amount * (utxo_count - 1) * 1000)}])
|
||||
|
||||
utxo_count -= 1
|
||||
sent = Decimal('0.01') * (utxo_count - 1)
|
||||
reserved = l1.rpc.reserveinputs(outputs=[{addr: Millisatoshi(amount * (utxo_count - 1) * 1000)}], feerate='10000perkw')
|
||||
|
||||
assert reserved['feerate_per_kw'] == 10000
|
||||
psbt = bitcoind.rpc.decodepsbt(reserved['psbt'])
|
||||
|
||||
assert len(psbt['inputs']) == utxo_count
|
||||
outputs = l1.rpc.listfunds()['outputs']
|
||||
assert len([x for x in outputs if not x['reserved']]) == total_outs - utxo_count == 0
|
||||
assert len([x for x in outputs if x['reserved']]) == 12
|
||||
|
||||
# No more available
|
||||
with pytest.raises(RpcError, match=r"Cannot afford transaction"):
|
||||
reserved = l1.rpc.reserveinputs(outputs=[{addr: Millisatoshi(amount * 1)}], feerate='253perkw')
|
||||
|
||||
# Unreserve three, from different psbts
|
||||
unreserve_utxos = [
|
||||
{
|
||||
'txid': saved_input['txid'],
|
||||
'vout': saved_input['vout'],
|
||||
'sequence': saved_input['sequence']
|
||||
}, {
|
||||
'txid': psbt['tx']['vin'][0]['txid'],
|
||||
'vout': psbt['tx']['vin'][0]['vout'],
|
||||
'sequence': psbt['tx']['vin'][0]['sequence']
|
||||
}, {
|
||||
'txid': psbt['tx']['vin'][1]['txid'],
|
||||
'vout': psbt['tx']['vin'][1]['vout'],
|
||||
'sequence': psbt['tx']['vin'][1]['sequence']
|
||||
}]
|
||||
unreserve_psbt = bitcoind.rpc.createpsbt(unreserve_utxos, [])
|
||||
|
||||
unreserved = l1.rpc.unreserveinputs(unreserve_psbt)
|
||||
assert unreserved['all_unreserved']
|
||||
outputs = l1.rpc.listfunds()['outputs']
|
||||
assert len([x for x in outputs if not x['reserved']]) == len(unreserved['outputs'])
|
||||
for i in range(len(unreserved['outputs'])):
|
||||
un = unreserved['outputs'][i]
|
||||
u_utxo = unreserve_utxos[i]
|
||||
assert un['txid'] == u_utxo['txid'] and un['vout'] == u_utxo['vout'] and un['unreserved']
|
||||
|
||||
# Try unreserving the same utxos again, plus one that's not included
|
||||
# We expect this to be a no-op.
|
||||
unreserve_utxos.append({'txid': 'b' * 64, 'vout': 0, 'sequence': 0})
|
||||
unreserve_psbt = bitcoind.rpc.createpsbt(unreserve_utxos, [])
|
||||
unreserved = l1.rpc.unreserveinputs(unreserve_psbt)
|
||||
assert not unreserved['all_unreserved']
|
||||
for un in unreserved['outputs']:
|
||||
assert not un['unreserved']
|
||||
assert len([x for x in l1.rpc.listfunds()['outputs'] if not x['reserved']]) == 3
|
||||
|
||||
# passing in an empty string should fail
|
||||
with pytest.raises(RpcError, match=r"should be a PSBT, not "):
|
||||
l1.rpc.unreserveinputs('')
|
||||
|
||||
# reserve one of the utxos that we just unreserved
|
||||
utxos = []
|
||||
utxos.append(saved_input['txid'] + ":" + str(saved_input['vout']))
|
||||
reserved = l1.rpc.reserveinputs([{addr: Millisatoshi(amount * 0.5 * 1000)}], feerate='253perkw', utxos=utxos)
|
||||
assert len([x for x in l1.rpc.listfunds()['outputs'] if not x['reserved']]) == 2
|
||||
psbt = bitcoind.rpc.decodepsbt(reserved['psbt'])
|
||||
assert len(psbt['inputs']) == 1
|
||||
vin = psbt['tx']['vin'][0]
|
||||
assert vin['txid'] == saved_input['txid'] and vin['vout'] == saved_input['vout']
|
||||
|
||||
# reserve them all!
|
||||
reserved = l1.rpc.reserveinputs([{addr: 'all'}])
|
||||
outputs = l1.rpc.listfunds()['outputs']
|
||||
assert len([x for x in outputs if not x['reserved']]) == 0
|
||||
assert len([x for x in outputs if x['reserved']]) == 12
|
||||
|
||||
# FIXME: restart the node, nothing will remain reserved
|
||||
l1.restart()
|
||||
assert len(l1.rpc.listfunds()['outputs']) == 12
|
||||
|
||||
|
||||
def test_txsend(node_factory, bitcoind, chainparams):
|
||||
amount = 1000000
|
||||
l1 = node_factory.get_node(random_hsm=True)
|
||||
|
||||
Reference in New Issue
Block a user