@ -1,4 +1,5 @@
from bitcoin . rpc import RawProxy
from decimal import Decimal
from fixtures import * # noqa: F401,F403
from flaky import flaky # noqa: F401
from lightning import RpcError
@ -453,6 +454,187 @@ def test_bech32_funding(node_factory, chainparams):
assert only_one ( fundingtx [ ' vin ' ] ) [ ' txid ' ] == res [ ' wallettxid ' ]
def test_withdraw ( node_factory , bitcoind , chainparams ) :
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 . 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 ' ) )
# 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
if chainparams [ ' name ' ] != ' regtest ' :
return
# 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 ' )
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_io_logging ( node_factory , executor ) :
l1 = node_factory . get_node ( options = { ' log-level ' : ' io ' } )
l2 = node_factory . get_node ( )