forked from ElementsProject/lightning
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtest_wallet.py
378 lines (302 loc) · 15.2 KB
/
test_wallet.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
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
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'))
# 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')
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']
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