|
|
|
from decimal import Decimal
|
|
|
|
from fixtures import * # noqa: F401,F403
|
|
|
|
from flaky import flaky # noqa: F401
|
|
|
|
from lightning import RpcError, Millisatoshi
|
|
|
|
from utils import only_one, wait_for, sync_blockheight
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
import time
|
|
|
|
|
|
|
|
|
|
|
|
def test_withdraw(node_factory, bitcoind):
|
|
|
|
amount = 1000000
|
|
|
|
# Don't get any funds from previous runs.
|
|
|
|
l1 = node_factory.get_node(random_hsm=True)
|
|
|
|
l2 = node_factory.get_node(random_hsm=True)
|
|
|
|
addr = l1.rpc.newaddr()['bech32']
|
|
|
|
|
|
|
|
# Add some funds to withdraw later
|
|
|
|
for i in range(10):
|
|
|
|
l1.bitcoin.rpc.sendtoaddress(addr, amount / 10**8 + 0.01)
|
|
|
|
|
|
|
|
bitcoind.generate_block(1)
|
|
|
|
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 10)
|
|
|
|
|
|
|
|
# Reach around into the db to check that outputs were added
|
|
|
|
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=0')[0]['c'] == 10
|
|
|
|
|
|
|
|
waddr = l1.bitcoin.rpc.getnewaddress()
|
|
|
|
# Now attempt to withdraw some (making sure we collect multiple inputs)
|
|
|
|
with pytest.raises(RpcError):
|
|
|
|
l1.rpc.withdraw('not an address', amount)
|
|
|
|
with pytest.raises(RpcError):
|
|
|
|
l1.rpc.withdraw(waddr, 'not an amount')
|
|
|
|
with pytest.raises(RpcError):
|
|
|
|
l1.rpc.withdraw(waddr, -amount)
|
|
|
|
with pytest.raises(RpcError, match=r'Cannot afford transaction'):
|
|
|
|
l1.rpc.withdraw(waddr, amount * 100)
|
|
|
|
|
|
|
|
out = l1.rpc.withdraw(waddr, 2 * amount)
|
|
|
|
|
|
|
|
# Make sure bitcoind received the withdrawal
|
|
|
|
unspent = l1.bitcoin.rpc.listunspent(0)
|
|
|
|
withdrawal = [u for u in unspent if u['txid'] == out['txid']]
|
|
|
|
|
|
|
|
assert(withdrawal[0]['amount'] == Decimal('0.02'))
|
|
|
|
|
|
|
|
l1.bitcoin.generate_block(1)
|
|
|
|
sync_blockheight(l1.bitcoin, [l1])
|
|
|
|
|
|
|
|
# Check that there are no unconfirmed outputs (change should be confirmed)
|
|
|
|
for o in l1.rpc.listfunds()['outputs']:
|
|
|
|
assert o['status'] == 'confirmed'
|
|
|
|
|
|
|
|
# Now make sure two of them were marked as spent
|
|
|
|
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=2')[0]['c'] == 2
|
|
|
|
|
|
|
|
# Now send some money to l2.
|
|
|
|
# lightningd uses P2SH-P2WPKH
|
|
|
|
waddr = l2.rpc.newaddr('bech32')['bech32']
|
|
|
|
l1.rpc.withdraw(waddr, 2 * amount)
|
|
|
|
bitcoind.generate_block(1)
|
|
|
|
|
|
|
|
# Make sure l2 received the withdrawal.
|
|
|
|
wait_for(lambda: len(l2.rpc.listfunds()['outputs']) == 1)
|
|
|
|
outputs = l2.db_query('SELECT value FROM outputs WHERE status=0;')
|
|
|
|
assert only_one(outputs)['value'] == 2 * amount
|
|
|
|
|
|
|
|
# Now make sure an additional two of them were marked as spent
|
|
|
|
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=2')[0]['c'] == 4
|
|
|
|
|
|
|
|
# Simple test for withdrawal to P2WPKH
|
|
|
|
# Address from: https://bc-2.jp/tools/bech32demo/index.html
|
|
|
|
waddr = 'bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080'
|
|
|
|
with pytest.raises(RpcError):
|
|
|
|
l1.rpc.withdraw('xx1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx', 2 * amount)
|
|
|
|
with pytest.raises(RpcError):
|
|
|
|
l1.rpc.withdraw('tb1pw508d6qejxtdg4y5r3zarvary0c5xw7kdl9fad', 2 * amount)
|
|
|
|
with pytest.raises(RpcError):
|
|
|
|
l1.rpc.withdraw('tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxxxxxx', 2 * amount)
|
|
|
|
l1.rpc.withdraw(waddr, 2 * amount)
|
|
|
|
bitcoind.generate_block(1)
|
|
|
|
# Now make sure additional two of them were marked as spent
|
|
|
|
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=2')[0]['c'] == 6
|
|
|
|
|
|
|
|
# Simple test for withdrawal to P2WSH
|
|
|
|
# Address from: https://bc-2.jp/tools/bech32demo/index.html
|
|
|
|
waddr = 'bcrt1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qzf4jry'
|
|
|
|
with pytest.raises(RpcError):
|
|
|
|
l1.rpc.withdraw('xx1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7', 2 * amount)
|
|
|
|
with pytest.raises(RpcError):
|
|
|
|
l1.rpc.withdraw('tb1prp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qsm03tq', 2 * amount)
|
|
|
|
with pytest.raises(RpcError):
|
|
|
|
l1.rpc.withdraw('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qxxxxxx', 2 * amount)
|
|
|
|
l1.rpc.withdraw(waddr, 2 * amount)
|
|
|
|
bitcoind.generate_block(1)
|
|
|
|
# Now make sure additional two of them were marked as spent
|
|
|
|
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=2')[0]['c'] == 8
|
|
|
|
|
|
|
|
# failure testing for invalid SegWit addresses, from BIP173
|
|
|
|
# HRP character out of range
|
|
|
|
with pytest.raises(RpcError):
|
|
|
|
l1.rpc.withdraw(' 1nwldj5', 2 * amount)
|
|
|
|
# overall max length exceeded
|
|
|
|
with pytest.raises(RpcError):
|
|
|
|
l1.rpc.withdraw('an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx', 2 * amount)
|
|
|
|
# No separator character
|
|
|
|
with pytest.raises(RpcError):
|
|
|
|
l1.rpc.withdraw('pzry9x0s0muk', 2 * amount)
|
|
|
|
# Empty HRP
|
|
|
|
with pytest.raises(RpcError):
|
|
|
|
l1.rpc.withdraw('1pzry9x0s0muk', 2 * amount)
|
|
|
|
# Invalid witness version
|
|
|
|
with pytest.raises(RpcError):
|
|
|
|
l1.rpc.withdraw('BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2', 2 * amount)
|
|
|
|
# Invalid program length for witness version 0 (per BIP141)
|
|
|
|
with pytest.raises(RpcError):
|
|
|
|
l1.rpc.withdraw('BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P', 2 * amount)
|
|
|
|
# Mixed case
|
|
|
|
with pytest.raises(RpcError):
|
|
|
|
l1.rpc.withdraw('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7', 2 * amount)
|
|
|
|
# Non-zero padding in 8-to-5 conversion
|
|
|
|
with pytest.raises(RpcError):
|
|
|
|
l1.rpc.withdraw('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv', 2 * amount)
|
|
|
|
|
|
|
|
# Should have 6 outputs available.
|
|
|
|
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=0')[0]['c'] == 6
|
|
|
|
|
|
|
|
# Test withdrawal to self.
|
|
|
|
l1.rpc.withdraw(l1.rpc.newaddr('bech32')['bech32'], 'all', minconf=0)
|
|
|
|
bitcoind.generate_block(1)
|
|
|
|
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=0')[0]['c'] == 1
|
|
|
|
|
|
|
|
l1.rpc.withdraw(waddr, 'all', minconf=0)
|
|
|
|
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=0')[0]['c'] == 0
|
|
|
|
|
|
|
|
# This should fail, can't even afford fee.
|
|
|
|
with pytest.raises(RpcError, match=r'Cannot afford transaction'):
|
|
|
|
l1.rpc.withdraw(waddr, 'all')
|
|
|
|
|
|
|
|
# Add some funds to withdraw
|
|
|
|
for i in range(10):
|
|
|
|
l1.bitcoin.rpc.sendtoaddress(addr, amount / 10**8 + 0.01)
|
|
|
|
|
|
|
|
bitcoind.generate_block(1)
|
|
|
|
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 10)
|
|
|
|
|
|
|
|
# Try passing in a utxo set
|
|
|
|
utxos = [utxo["txid"] + ":" + str(utxo["output"]) for utxo in l1.rpc.listfunds()["outputs"]][:4]
|
|
|
|
|
|
|
|
withdrawal = l1.rpc.withdraw(waddr, 2 * amount, utxos=utxos)
|
|
|
|
decode = bitcoind.rpc.decoderawtransaction(withdrawal['tx'])
|
|
|
|
assert decode['txid'] == withdrawal['txid']
|
|
|
|
|
|
|
|
# Check that correct utxos are included
|
|
|
|
assert len(decode['vin']) == 4
|
|
|
|
vins = ["{}:{}".format(v['txid'], v['vout']) for v in decode['vin']]
|
|
|
|
for utxo in utxos:
|
|
|
|
assert utxo in vins
|
|
|
|
|
|
|
|
|
|
|
|
def test_minconf_withdraw(node_factory, bitcoind):
|
|
|
|
"""Issue 2518: ensure that ridiculous confirmation levels don't overflow
|
|
|
|
|
|
|
|
The number of confirmations is used to compute a maximum height that is to
|
|
|
|
be accepted. If the current height is smaller than the number of
|
|
|
|
confirmations we wrap around and just select everything. The fix is to
|
|
|
|
clamp the maxheight parameter to a positive small number.
|
|
|
|
|
|
|
|
"""
|
|
|
|
amount = 1000000
|
|
|
|
# Don't get any funds from previous runs.
|
|
|
|
l1 = node_factory.get_node(random_hsm=True)
|
|
|
|
addr = l1.rpc.newaddr()['bech32']
|
|
|
|
|
|
|
|
# Add some funds to withdraw later
|
|
|
|
for i in range(10):
|
|
|
|
l1.bitcoin.rpc.sendtoaddress(addr, amount / 10**8 + 0.01)
|
|
|
|
|
|
|
|
bitcoind.generate_block(1)
|
|
|
|
|
|
|
|
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 10)
|
|
|
|
with pytest.raises(RpcError):
|
|
|
|
l1.rpc.withdraw(destination=addr, satoshi=10000, feerate='normal', minconf=9999999)
|
|
|
|
|
|
|
|
|
|
|
|
def test_addfunds_from_block(node_factory, bitcoind):
|
|
|
|
"""Send funds to the daemon without telling it explicitly
|
|
|
|
"""
|
|
|
|
# Previous runs with same bitcoind can leave funds!
|
|
|
|
l1 = node_factory.get_node(random_hsm=True)
|
|
|
|
|
|
|
|
addr = l1.rpc.newaddr()['bech32']
|
|
|
|
bitcoind.rpc.sendtoaddress(addr, 0.1)
|
|
|
|
bitcoind.generate_block(1)
|
|
|
|
|
|
|
|
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 1)
|
|
|
|
|
|
|
|
outputs = l1.db_query('SELECT value FROM outputs WHERE status=0;')
|
|
|
|
assert only_one(outputs)['value'] == 10000000
|
|
|
|
|
|
|
|
# The address we detect must match what was paid to.
|
|
|
|
output = only_one(l1.rpc.listfunds()['outputs'])
|
|
|
|
assert output['address'] == addr
|
|
|
|
|
|
|
|
# Send all our money to a P2WPKH address this time.
|
|
|
|
addr = l1.rpc.newaddr("bech32")['bech32']
|
|
|
|
l1.rpc.withdraw(addr, "all")
|
|
|
|
bitcoind.generate_block(1)
|
|
|
|
time.sleep(1)
|
|
|
|
|
|
|
|
# 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']
|
|
|
|
l1.rpc.txdiscard(prep4['txid'])
|
|
|
|
|
|
|
|
# Try passing in a utxo set
|
|
|
|
utxos = [utxo["txid"] + ":" + str(utxo["output"]) for utxo in l1.rpc.listfunds()["outputs"]][:4]
|
|
|
|
|
|
|
|
prep5 = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg':
|
|
|
|
Millisatoshi(amount * 3.5 * 1000)}], utxos=utxos)
|
|
|
|
|
|
|
|
decode = bitcoind.rpc.decoderawtransaction(prep5['unsigned_tx'])
|
|
|
|
assert decode['txid'] == prep5['txid']
|
|
|
|
|
|
|
|
# Check that correct utxos are included
|
|
|
|
assert len(decode['vin']) == 4
|
|
|
|
vins = ["{}:{}".format(v['txid'], v['vout']) for v in decode['vin']]
|
|
|
|
for utxo in utxos:
|
|
|
|
assert utxo in vins
|
|
|
|
|
|
|
|
# We should have a change output, so this is exact
|
|
|
|
assert len(decode['vout']) == 2
|
|
|
|
assert decode['vout'][1]['value'] == Decimal(amount * 3.5) / 10**8
|
|
|
|
assert decode['vout'][1]['scriptPubKey']['type'] == 'witness_v0_keyhash'
|
|
|
|
assert decode['vout'][1]['scriptPubKey']['addresses'] == ['bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg']
|
|
|
|
|
|
|
|
# Discard prep4 and get all funds again
|
|
|
|
l1.rpc.txdiscard(prep5['txid'])
|
|
|
|
with pytest.raises(RpcError, match=r'this destination wants all satoshi. The count of outputs can\'t be more than 1'):
|
|
|
|
prep5 = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': Millisatoshi(amount * 3 * 1000)},
|
|
|
|
{'bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080': 'all'}])
|
|
|
|
prep5 = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': Millisatoshi(amount * 3 * 500 + 100000)},
|
|
|
|
{'bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080': Millisatoshi(amount * 3 * 500 - 100000)}])
|
|
|
|
decode = bitcoind.rpc.decoderawtransaction(prep5['unsigned_tx'])
|
|
|
|
assert decode['txid'] == prep5['txid']
|
|
|
|
# 4 inputs, 3 outputs(include change).
|
|
|
|
assert len(decode['vin']) == 4
|
|
|
|
assert len(decode['vout']) == 3
|
|
|
|
|
|
|
|
# One output will be correct.
|
|
|
|
for i in range(3):
|
|
|
|
if decode['vout'][i - 1]['value'] == Decimal('0.01500100'):
|
|
|
|
outnum1 = i - 1
|
|
|
|
elif decode['vout'][i - 1]['value'] == Decimal('0.01499900'):
|
|
|
|
outnum2 = i - 1
|
|
|
|
else:
|
|
|
|
changenum = i - 1
|
|
|
|
|
|
|
|
assert decode['vout'][outnum1]['scriptPubKey']['type'] == 'witness_v0_keyhash'
|
|
|
|
assert decode['vout'][outnum1]['scriptPubKey']['addresses'] == ['bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg']
|
|
|
|
|
|
|
|
assert decode['vout'][outnum2]['scriptPubKey']['type'] == 'witness_v0_keyhash'
|
|
|
|
assert decode['vout'][outnum2]['scriptPubKey']['addresses'] == ['bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080']
|
|
|
|
|
|
|
|
assert decode['vout'][changenum]['scriptPubKey']['type'] == 'witness_v0_keyhash'
|
|
|
|
|
|
|
|
|
|
|
|
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']]
|
|
|
|
|
|
|
|
|
|
|
|
def test_txprepare_restart(node_factory, bitcoind):
|
|
|
|
amount = 1000000
|
|
|
|
l1 = node_factory.get_node(may_fail=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: [o['status'] for o in l1.rpc.listfunds()['outputs']] == ['confirmed'] * 10)
|
|
|
|
|
|
|
|
prep = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': 'all'}])
|
|
|
|
decode = bitcoind.rpc.decoderawtransaction(prep['unsigned_tx'])
|
|
|
|
assert decode['txid'] == prep['txid']
|
|
|
|
# All 10 inputs
|
|
|
|
assert len(decode['vin']) == 10
|
|
|
|
|
|
|
|
# L1 will forget all about it.
|
|
|
|
l1.restart()
|
|
|
|
|
|
|
|
# It goes backwards in blockchain just in case there was a reorg. Wait.
|
|
|
|
wait_for(lambda: [o['status'] for o in l1.rpc.listfunds()['outputs']] == ['confirmed'] * 10)
|
|
|
|
|
|
|
|
with pytest.raises(RpcError, match=r'not an unreleased txid'):
|
|
|
|
l1.rpc.txdiscard(prep['txid'])
|
|
|
|
|
|
|
|
prep = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': 'all'}])
|
|
|
|
|
|
|
|
decode = bitcoind.rpc.decoderawtransaction(prep['unsigned_tx'])
|
|
|
|
assert decode['txid'] == prep['txid']
|
|
|
|
# All 10 inputs
|
|
|
|
assert len(decode['vin']) == 10
|
|
|
|
|
|
|
|
# This will also work if we simply kill it.
|
|
|
|
l1.restart(clean=False)
|
|
|
|
|
|
|
|
# It goes backwards in blockchain just in case there was a reorg. Wait.
|
|
|
|
wait_for(lambda: [o['status'] for o in l1.rpc.listfunds()['outputs']] == ['confirmed'] * 10)
|
|
|
|
|
|
|
|
# It should have logged this for each output.
|
|
|
|
for i in decode['vin']:
|
|
|
|
assert l1.daemon.is_in_log('wallet: reserved output {}/{} reset to available'.format(i['txid'], i['vout']))
|
|
|
|
|
|
|
|
prep = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': 'all'}])
|
|
|
|
decode = bitcoind.rpc.decoderawtransaction(prep['unsigned_tx'])
|
|
|
|
assert decode['txid'] == prep['txid']
|
|
|
|
# All 10 inputs
|
|
|
|
assert len(decode['vin']) == 10
|