From cdab75ecee15dae25b3ba9d8fa4325013195e97f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 5 Jun 2019 16:30:05 +0930 Subject: [PATCH] pytest: test txprepare/txdiscard/txsend. Signed-off-by: Rusty Russell --- tests/test_wallet.py | 139 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 138 insertions(+), 1 deletion(-) diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 7918f0552..ae62a8e44 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -1,7 +1,7 @@ from decimal import Decimal from fixtures import * # noqa: F401,F403 from flaky import flaky # noqa: F401 -from lightning import RpcError +from lightning import RpcError, Millisatoshi from utils import only_one, wait_for import pytest @@ -184,3 +184,140 @@ def test_addfunds_from_block(node_factory, bitcoind): # The address we detect must match what was paid to. output = only_one(l1.rpc.listfunds()['outputs']) assert output['address'] == addr + + +def test_txprepare(node_factory, bitcoind): + amount = 1000000 + l1 = node_factory.get_node(random_hsm=True) + + # Add some funds to withdraw later: both bech32 and p2sh + for i in range(5): + 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']) == 10) + + prep = l1.rpc.txprepare('bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg', + Millisatoshi(amount * 3 * 1000)) + decode = bitcoind.rpc.decoderawtransaction(prep['unsigned_tx']) + assert decode['txid'] == prep['txid'] + # 4 inputs, 2 outputs. + assert len(decode['vin']) == 4 + assert len(decode['vout']) == 2 + + # One output will be correct. + if decode['vout'][0]['value'] == Decimal(amount * 3) / 10**8: + outnum = 0 + changenum = 1 + elif decode['vout'][1]['value'] == Decimal(amount * 3) / 10**8: + outnum = 1 + changenum = 0 + else: + assert False + + assert decode['vout'][outnum]['scriptPubKey']['type'] == 'witness_v0_keyhash' + assert decode['vout'][outnum]['scriptPubKey']['addresses'] == ['bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg'] + + assert decode['vout'][changenum]['scriptPubKey']['type'] == 'witness_v0_keyhash' + + # Now prepare one with no change. + prep2 = l1.rpc.txprepare('bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg', + 'all') + decode = bitcoind.rpc.decoderawtransaction(prep2['unsigned_tx']) + assert decode['txid'] == prep2['txid'] + # 6 inputs, 1 outputs. + assert len(decode['vin']) == 6 + assert len(decode['vout']) == 1 + + # Some fees will be paid. + assert decode['vout'][0]['value'] < Decimal(amount * 6) / 10**8 + assert decode['vout'][0]['value'] > Decimal(amount * 6) / 10**8 - Decimal(0.0002) + assert decode['vout'][0]['scriptPubKey']['type'] == 'witness_v0_keyhash' + assert decode['vout'][0]['scriptPubKey']['addresses'] == ['bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg'] + + # If I cancel the first one, I can get those first 4 outputs. + discard = l1.rpc.txdiscard(prep['txid']) + assert discard['txid'] == prep['txid'] + assert discard['unsigned_tx'] == prep['unsigned_tx'] + + prep3 = l1.rpc.txprepare('bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg', + 'all') + decode = bitcoind.rpc.decoderawtransaction(prep3['unsigned_tx']) + assert decode['txid'] == prep3['txid'] + # 4 inputs, 1 outputs. + assert len(decode['vin']) == 4 + assert len(decode['vout']) == 1 + + # Some fees will be taken + assert decode['vout'][0]['value'] < Decimal(amount * 4) / 10**8 + assert decode['vout'][0]['value'] > Decimal(amount * 4) / 10**8 - Decimal(0.0002) + assert decode['vout'][0]['scriptPubKey']['type'] == 'witness_v0_keyhash' + assert decode['vout'][0]['scriptPubKey']['addresses'] == ['bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg'] + + # Cannot discard twice. + with pytest.raises(RpcError, match=r'not an unreleased txid'): + l1.rpc.txdiscard(prep['txid']) + + # Discard everything, we should now spend all inputs. + l1.rpc.txdiscard(prep2['txid']) + l1.rpc.txdiscard(prep3['txid']) + prep4 = l1.rpc.txprepare('bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg', + 'all') + decode = bitcoind.rpc.decoderawtransaction(prep4['unsigned_tx']) + assert decode['txid'] == prep4['txid'] + # 10 inputs, 1 outputs. + assert len(decode['vin']) == 10 + assert len(decode['vout']) == 1 + + # Some fees will be taken + assert decode['vout'][0]['value'] < Decimal(amount * 10) / 10**8 + assert decode['vout'][0]['value'] > Decimal(amount * 10) / 10**8 - Decimal(0.0003) + assert decode['vout'][0]['scriptPubKey']['type'] == 'witness_v0_keyhash' + assert decode['vout'][0]['scriptPubKey']['addresses'] == ['bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg'] + + +def test_txsend(node_factory, bitcoind): + amount = 1000000 + l1 = node_factory.get_node(random_hsm=True) + + # Add some funds to withdraw later: both bech32 and p2sh + for i in range(5): + 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']) == 10) + + prep = l1.rpc.txprepare('bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg', + Millisatoshi(amount * 3 * 1000)) + out = l1.rpc.txsend(prep['txid']) + + # Cannot discard after send! + with pytest.raises(RpcError, match=r'not an unreleased txid'): + l1.rpc.txdiscard(prep['txid']) + + wait_for(lambda: prep['txid'] in bitcoind.rpc.getrawmempool()) + + # Signed tx should have same txid + decode = bitcoind.rpc.decoderawtransaction(out['tx']) + assert decode['txid'] == prep['txid'] + + bitcoind.generate_block(1) + + # Change output should appear. + if decode['vout'][0]['value'] == Decimal(amount * 3) / 10**8: + changenum = 1 + elif decode['vout'][1]['value'] == Decimal(amount * 3) / 10**8: + changenum = 0 + else: + assert False + + # Those spent outputs are gone, but change output has arrived. + wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 10 - len(decode['vin']) + 1) + + # Change address should appear in listfunds() + assert decode['vout'][changenum]['scriptPubKey']['addresses'][0] in [f['address'] for f in l1.rpc.listfunds()['outputs']]