mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-21 08:04:26 +01:00
pytest: restore test_sign_and_send_psbt.
It uses reservations heavily, and assumed we generated change, etc. It's now a simpler test, in many ways. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
@@ -544,7 +544,6 @@ def test_fundpsbt(node_factory, bitcoind, chainparams):
|
|||||||
l1.rpc.fundpsbt(amount // 2, feerate)
|
l1.rpc.fundpsbt(amount // 2, feerate)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(strict=True)
|
|
||||||
def test_sign_and_send_psbt(node_factory, bitcoind, chainparams):
|
def test_sign_and_send_psbt(node_factory, bitcoind, chainparams):
|
||||||
"""
|
"""
|
||||||
Tests for the sign + send psbt RPCs
|
Tests for the sign + send psbt RPCs
|
||||||
@@ -566,53 +565,58 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams):
|
|||||||
bitcoind.generate_block(1)
|
bitcoind.generate_block(1)
|
||||||
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == total_outs)
|
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == total_outs)
|
||||||
|
|
||||||
# Make a PSBT out of our inputs
|
# Make a PSBT out of our inputs (FIXME: satoshi amount should include fees!)
|
||||||
reserved = l1.rpc.reserveinputs(outputs=[{addr: Millisatoshi(3 * amount * 1000)}])
|
funding = l1.rpc.fundpsbt(satoshi=Millisatoshi(3 * amount * 1000),
|
||||||
|
feerate=7500,
|
||||||
|
reserve=True)
|
||||||
assert len([x for x in l1.rpc.listfunds()['outputs'] if x['reserved']]) == 4
|
assert len([x for x in l1.rpc.listfunds()['outputs'] if x['reserved']]) == 4
|
||||||
psbt = bitcoind.rpc.decodepsbt(reserved['psbt'])
|
psbt = bitcoind.rpc.decodepsbt(funding['psbt'])
|
||||||
saved_input = psbt['tx']['vin'][0]
|
saved_input = psbt['tx']['vin'][0]
|
||||||
|
|
||||||
# Go ahead and unreserve the UTXOs, we'll use a smaller
|
# Go ahead and unreserve the UTXOs, we'll use a smaller
|
||||||
# set of them to create a second PSBT that we'll attempt to sign
|
# set of them to create a second PSBT that we'll attempt to sign
|
||||||
# and broadcast (to disastrous results)
|
# and broadcast (to disastrous results)
|
||||||
l1.rpc.unreserveinputs(reserved['psbt'])
|
l1.rpc.unreserveinputs(funding['psbt'])
|
||||||
|
|
||||||
# Re-reserve one of the utxos we just unreserved
|
# Re-reserve one of the utxos we just unreserved
|
||||||
utxos = []
|
psbt = bitcoind.rpc.createpsbt([{'txid': saved_input['txid'],
|
||||||
utxos.append(saved_input['txid'] + ":" + str(saved_input['vout']))
|
'vout': saved_input['vout']}], [])
|
||||||
second_reservation = l1.rpc.reserveinputs([{addr: Millisatoshi(amount * 0.5 * 1000)}], feerate='253perkw', utxos=utxos)
|
l1.rpc.reserveinputs(psbt)
|
||||||
|
|
||||||
# We require the utxos be reserved before signing them
|
# We require the utxos be reserved before signing them
|
||||||
with pytest.raises(RpcError, match=r"Aborting PSBT signing. UTXO .* is not reserved"):
|
with pytest.raises(RpcError, match=r"Aborting PSBT signing. UTXO .* is not reserved"):
|
||||||
l1.rpc.signpsbt(reserved['psbt'])['signed_psbt']
|
l1.rpc.signpsbt(funding['psbt'])['signed_psbt']
|
||||||
|
|
||||||
# Now we unreserve the singleton, so we can reserve it again
|
# Now we unreserve the singleton, so we can reserve it again
|
||||||
l1.rpc.unreserveinputs(second_reservation['psbt'])
|
l1.rpc.unreserveinputs(psbt)
|
||||||
|
|
||||||
|
# Now add an output.
|
||||||
|
output_pbst = bitcoind.rpc.createpsbt([],
|
||||||
|
[{addr: 3 * amount / 10**8}])
|
||||||
|
fullpsbt = bitcoind.rpc.joinpsbts([funding['psbt'], output_pbst])
|
||||||
|
|
||||||
# We re-reserve the first set...
|
# We re-reserve the first set...
|
||||||
utxos = []
|
l1.rpc.reserveinputs(fullpsbt)
|
||||||
for vin in psbt['tx']['vin']:
|
|
||||||
utxos.append(vin['txid'] + ':' + str(vin['vout']))
|
|
||||||
reserved = l1.rpc.reserveinputs(outputs=[{addr: Millisatoshi(3 * amount * 1000)}], utxos=utxos)
|
|
||||||
# Sign + send the PSBT we've created
|
# Sign + send the PSBT we've created
|
||||||
signed_psbt = l1.rpc.signpsbt(reserved['psbt'])['signed_psbt']
|
signed_psbt = l1.rpc.signpsbt(fullpsbt)['signed_psbt']
|
||||||
broadcast_tx = l1.rpc.sendpsbt(signed_psbt)
|
broadcast_tx = l1.rpc.sendpsbt(signed_psbt)
|
||||||
|
|
||||||
# Check that it was broadcast successfully
|
# Check that it was broadcast successfully
|
||||||
l1.daemon.wait_for_log(r'sendrawtx exit 0 .* sendrawtransaction {}'.format(broadcast_tx['tx']))
|
l1.daemon.wait_for_log(r'sendrawtx exit 0 .* sendrawtransaction {}'.format(broadcast_tx['tx']))
|
||||||
bitcoind.generate_block(1)
|
bitcoind.generate_block(1)
|
||||||
|
|
||||||
# We expect a change output to be added to the wallet
|
# We didn't add a change output.
|
||||||
expected_outs = total_outs - 4 + 1
|
expected_outs = total_outs - 4
|
||||||
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == expected_outs)
|
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == expected_outs)
|
||||||
|
|
||||||
# Let's try *sending* a PSBT that can't be finalized (it's unsigned)
|
# Let's try *sending* a PSBT that can't be finalized (it's unsigned)
|
||||||
with pytest.raises(RpcError, match=r"PSBT not finalizeable"):
|
with pytest.raises(RpcError, match=r"PSBT not finalizeable"):
|
||||||
l1.rpc.sendpsbt(second_reservation['psbt'])
|
l1.rpc.sendpsbt(fullpsbt)
|
||||||
|
|
||||||
# Now we try signing a PSBT with an output that's already been spent
|
# Now we try signing a PSBT with an output that's already been spent
|
||||||
with pytest.raises(RpcError, match=r"Aborting PSBT signing. UTXO {} is not reserved".format(utxos[0])):
|
with pytest.raises(RpcError, match=r"Aborting PSBT signing. UTXO .* is not reserved"):
|
||||||
l1.rpc.signpsbt(second_reservation['psbt'])
|
l1.rpc.signpsbt(fullpsbt)
|
||||||
|
|
||||||
# Queue up another node, to make some PSBTs for us
|
# Queue up another node, to make some PSBTs for us
|
||||||
for i in range(total_outs // 2):
|
for i in range(total_outs // 2):
|
||||||
@@ -623,15 +627,22 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams):
|
|||||||
# Create a PSBT using L2
|
# Create a PSBT using L2
|
||||||
bitcoind.generate_block(1)
|
bitcoind.generate_block(1)
|
||||||
wait_for(lambda: len(l2.rpc.listfunds()['outputs']) == total_outs)
|
wait_for(lambda: len(l2.rpc.listfunds()['outputs']) == total_outs)
|
||||||
l2_reserved = l2.rpc.reserveinputs(outputs=[{addr: Millisatoshi(3 * amount * 1000)}])
|
l2_funding = l2.rpc.fundpsbt(satoshi=Millisatoshi(3 * amount * 1000),
|
||||||
|
feerate=7500,
|
||||||
|
reserve=True)
|
||||||
|
|
||||||
# Try to get L1 to sign it
|
# Try to get L1 to sign it
|
||||||
with pytest.raises(RpcError, match=r"No wallet inputs to sign"):
|
with pytest.raises(RpcError, match=r"No wallet inputs to sign"):
|
||||||
l1.rpc.signpsbt(l2_reserved['psbt'])
|
l1.rpc.signpsbt(l2_funding['psbt'])
|
||||||
|
|
||||||
# Add some of our own PSBT inputs to it
|
# Add some of our own PSBT inputs to it
|
||||||
l1_reserved = l1.rpc.reserveinputs(outputs=[{addr: Millisatoshi(3 * amount * 1000)}])
|
l1_funding = l1.rpc.fundpsbt(satoshi=Millisatoshi(3 * amount * 1000),
|
||||||
joint_psbt = bitcoind.rpc.joinpsbts([l1_reserved['psbt'], l2_reserved['psbt']])
|
feerate=7500,
|
||||||
|
reserve=True)
|
||||||
|
|
||||||
|
# Join and add an output
|
||||||
|
joint_psbt = bitcoind.rpc.joinpsbts([l1_funding['psbt'], l2_funding['psbt'],
|
||||||
|
output_pbst])
|
||||||
|
|
||||||
half_signed_psbt = l1.rpc.signpsbt(joint_psbt)['signed_psbt']
|
half_signed_psbt = l1.rpc.signpsbt(joint_psbt)['signed_psbt']
|
||||||
totally_signed = l2.rpc.signpsbt(half_signed_psbt)['signed_psbt']
|
totally_signed = l2.rpc.signpsbt(half_signed_psbt)['signed_psbt']
|
||||||
@@ -640,8 +651,11 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams):
|
|||||||
l1.daemon.wait_for_log(r'sendrawtx exit 0 .* sendrawtransaction {}'.format(broadcast_tx['tx']))
|
l1.daemon.wait_for_log(r'sendrawtx exit 0 .* sendrawtransaction {}'.format(broadcast_tx['tx']))
|
||||||
|
|
||||||
# Send a PSBT that's not ours
|
# Send a PSBT that's not ours
|
||||||
l2_reserved = l2.rpc.reserveinputs(outputs=[{addr: Millisatoshi(3 * amount * 1000)}])
|
l2_funding = l2.rpc.fundpsbt(satoshi=Millisatoshi(3 * amount * 1000),
|
||||||
l2_signed_psbt = l2.rpc.signpsbt(l2_reserved['psbt'])['signed_psbt']
|
feerate=7500,
|
||||||
|
reserve=True)
|
||||||
|
psbt = bitcoind.rpc.joinpsbts([l2_funding['psbt'], output_pbst])
|
||||||
|
l2_signed_psbt = l2.rpc.signpsbt(psbt)['signed_psbt']
|
||||||
l1.rpc.sendpsbt(l2_signed_psbt)
|
l1.rpc.sendpsbt(l2_signed_psbt)
|
||||||
|
|
||||||
# Re-try sending the same tx?
|
# Re-try sending the same tx?
|
||||||
@@ -658,7 +672,7 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams):
|
|||||||
l1.rpc.sendpsbt('')
|
l1.rpc.sendpsbt('')
|
||||||
|
|
||||||
# Try a modified (invalid) PSBT string
|
# Try a modified (invalid) PSBT string
|
||||||
modded_psbt = l2_reserved['psbt'][:-3] + 'A' + l2_reserved['psbt'][-3:]
|
modded_psbt = psbt[:-3] + 'A' + psbt[-3:]
|
||||||
with pytest.raises(RpcError, match=r"should be a PSBT, not"):
|
with pytest.raises(RpcError, match=r"should be a PSBT, not"):
|
||||||
l1.rpc.signpsbt(modded_psbt)
|
l1.rpc.signpsbt(modded_psbt)
|
||||||
|
|
||||||
@@ -679,27 +693,16 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams):
|
|||||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
|
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
|
||||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
|
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
|
||||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
|
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
|
||||||
# Nicely splits out withdrawals and chain fees, because it's all our tx
|
|
||||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 988255000, 'tag': 'withdrawal'},
|
|
||||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 3000000000, 'tag': 'withdrawal'},
|
{'type': 'chain_mvt', 'credit': 0, 'debit': 3000000000, 'tag': 'withdrawal'},
|
||||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 11745000, 'tag': 'chain_fees'},
|
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'chain_fees'},
|
||||||
{'type': 'chain_mvt', 'credit': 988255000, 'debit': 0, 'tag': 'deposit'},
|
|
||||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
|
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
|
||||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
|
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
|
||||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
|
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
|
||||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
|
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
|
||||||
# Note that this is technically wrong since we paid 11745sat in fees
|
{'type': 'chain_mvt', 'credit': 0, 'debit': 3000000000, 'tag': 'withdrawal'},
|
||||||
# but since it includes inputs / outputs from a second node, we can't
|
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'chain_fees'},
|
||||||
# do proper acccounting for it.
|
|
||||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 4000000000, 'tag': 'withdrawal'},
|
|
||||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'chain_fees'},
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if chainparams['elements']:
|
|
||||||
wallet_coin_mvts.append({'type': 'chain_mvt', 'credit': 984625000, 'debit': 0, 'tag': 'deposit'})
|
|
||||||
else:
|
|
||||||
wallet_coin_mvts.append({'type': 'chain_mvt', 'credit': 988285000, 'debit': 0, 'tag': 'deposit'})
|
|
||||||
|
|
||||||
check_coin_moves(l1, 'wallet', wallet_coin_mvts, chainparams)
|
check_coin_moves(l1, 'wallet', wallet_coin_mvts, chainparams)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user