|
|
@ -438,130 +438,52 @@ def test_txprepare(node_factory, bitcoind, chainparams): |
|
|
|
|
|
|
|
|
|
|
|
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'] |
|
|
|
|
|
|
|
outputs = [] |
|
|
|
# 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) |
|
|
|
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']) == 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 not any(o['reserved'] for o in l1.rpc.listfunds()['outputs']) |
|
|
|
|
|
|
|
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] |
|
|
|
# Try reserving one at a time. |
|
|
|
for out in outputs: |
|
|
|
psbt = bitcoind.rpc.createpsbt([{'txid': out[0], 'vout': out[1]}], []) |
|
|
|
l1.rpc.reserveinputs(psbt) |
|
|
|
|
|
|
|
# We should have two outputs |
|
|
|
for vout in psbt['tx']['vout']: |
|
|
|
if chainparams['elements'] and vout['scriptPubKey']['type'] == 'fee': |
|
|
|
continue |
|
|
|
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 all(o['reserved'] for o in l1.rpc.listfunds()['outputs']) |
|
|
|
|
|
|
|
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 all([x['unreserved'] for x in unreserved['outputs']]) |
|
|
|
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 any([x['unreserved'] for x in unreserved['outputs']]) |
|
|
|
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'] |
|
|
|
# Unreserve as a batch. |
|
|
|
psbt = bitcoind.rpc.createpsbt([{'txid': out[0], 'vout': out[1]} for out in outputs], []) |
|
|
|
l1.rpc.unreserveinputs(psbt) |
|
|
|
assert not any(o['reserved'] for o in l1.rpc.listfunds()['outputs']) |
|
|
|
|
|
|
|
# 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 |
|
|
|
# Reserve twice fails unless exclusive. |
|
|
|
l1.rpc.reserveinputs(psbt) |
|
|
|
with pytest.raises(RpcError, match=r"already reserved"): |
|
|
|
l1.rpc.reserveinputs(psbt) |
|
|
|
l1.rpc.reserveinputs(psbt, False) |
|
|
|
l1.rpc.unreserveinputs(psbt) |
|
|
|
assert all(o['reserved'] for o in l1.rpc.listfunds()['outputs']) |
|
|
|
|
|
|
|
# FIXME: restart the node, nothing will remain reserved |
|
|
|
# Stays reserved across restarts. |
|
|
|
l1.restart() |
|
|
|
assert len(l1.rpc.listfunds()['outputs']) == 12 |
|
|
|
assert all(o['reserved'] for o in l1.rpc.listfunds()['outputs']) |
|
|
|
|
|
|
|
# Final unreserve works. |
|
|
|
l1.rpc.unreserveinputs(psbt) |
|
|
|
assert not any(o['reserved'] for o in l1.rpc.listfunds()['outputs']) |
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.xfail(strict=True) |
|
|
|