Browse Source

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>
release-0.9.0
Rusty Russell 4 years ago
parent
commit
e85c895c5b
  1. 85
      tests/test_wallet.py

85
tests/test_wallet.py

@ -544,7 +544,6 @@ def test_fundpsbt(node_factory, bitcoind, chainparams):
l1.rpc.fundpsbt(amount // 2, feerate)
@pytest.mark.xfail(strict=True)
def test_sign_and_send_psbt(node_factory, bitcoind, chainparams):
"""
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)
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == total_outs)
# Make a PSBT out of our inputs
reserved = l1.rpc.reserveinputs(outputs=[{addr: Millisatoshi(3 * amount * 1000)}])
# Make a PSBT out of our inputs (FIXME: satoshi amount should include fees!)
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
psbt = bitcoind.rpc.decodepsbt(reserved['psbt'])
psbt = bitcoind.rpc.decodepsbt(funding['psbt'])
saved_input = psbt['tx']['vin'][0]
# 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
# and broadcast (to disastrous results)
l1.rpc.unreserveinputs(reserved['psbt'])
l1.rpc.unreserveinputs(funding['psbt'])
# Re-reserve one of the utxos we just unreserved
utxos = []
utxos.append(saved_input['txid'] + ":" + str(saved_input['vout']))
second_reservation = l1.rpc.reserveinputs([{addr: Millisatoshi(amount * 0.5 * 1000)}], feerate='253perkw', utxos=utxos)
psbt = bitcoind.rpc.createpsbt([{'txid': saved_input['txid'],
'vout': saved_input['vout']}], [])
l1.rpc.reserveinputs(psbt)
# We require the utxos be reserved before signing them
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
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...
utxos = []
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)
l1.rpc.reserveinputs(fullpsbt)
# 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)
# Check that it was broadcast successfully
l1.daemon.wait_for_log(r'sendrawtx exit 0 .* sendrawtransaction {}'.format(broadcast_tx['tx']))
bitcoind.generate_block(1)
# We expect a change output to be added to the wallet
expected_outs = total_outs - 4 + 1
# We didn't add a change output.
expected_outs = total_outs - 4
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == expected_outs)
# Let's try *sending* a PSBT that can't be finalized (it's unsigned)
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
with pytest.raises(RpcError, match=r"Aborting PSBT signing. UTXO {} is not reserved".format(utxos[0])):
l1.rpc.signpsbt(second_reservation['psbt'])
with pytest.raises(RpcError, match=r"Aborting PSBT signing. UTXO .* is not reserved"):
l1.rpc.signpsbt(fullpsbt)
# Queue up another node, to make some PSBTs for us
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
bitcoind.generate_block(1)
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
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
l1_reserved = l1.rpc.reserveinputs(outputs=[{addr: Millisatoshi(3 * amount * 1000)}])
joint_psbt = bitcoind.rpc.joinpsbts([l1_reserved['psbt'], l2_reserved['psbt']])
l1_funding = l1.rpc.fundpsbt(satoshi=Millisatoshi(3 * amount * 1000),
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']
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']))
# Send a PSBT that's not ours
l2_reserved = l2.rpc.reserveinputs(outputs=[{addr: Millisatoshi(3 * amount * 1000)}])
l2_signed_psbt = l2.rpc.signpsbt(l2_reserved['psbt'])['signed_psbt']
l2_funding = l2.rpc.fundpsbt(satoshi=Millisatoshi(3 * amount * 1000),
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)
# Re-try sending the same tx?
@ -658,7 +672,7 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams):
l1.rpc.sendpsbt('')
# 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"):
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'},
# 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': 11745000, 'tag': 'chain_fees'},
{'type': 'chain_mvt', 'credit': 988255000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'chain_fees'},
{'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
# but since it includes inputs / outputs from a second node, we can't
# do proper acccounting for it.
{'type': 'chain_mvt', 'credit': 0, 'debit': 4000000000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'chain_fees'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 3000000000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, '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)

Loading…
Cancel
Save