1
1
#!/usr/bin/env python3
2
- # Copyright (c) 2018 The Bitcoin Core developers
2
+ # Copyright (c) 2018-2019 The Bitcoin Core developers
3
3
# Distributed under the MIT software license, see the accompanying
4
4
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
5
"""Test bitcoin-wallet."""
6
+
7
+ import hashlib
8
+ import os
9
+ import stat
6
10
import subprocess
7
11
import textwrap
8
12
9
13
from test_framework .test_framework import BitcoinTestFramework
10
14
from test_framework .util import assert_equal
11
15
16
+ BUFFER_SIZE = 16 * 1024
17
+
12
18
class ToolWalletTest (BitcoinTestFramework ):
13
19
def set_test_params (self ):
14
20
self .num_nodes = 1
@@ -32,23 +38,54 @@ def assert_raises_tool_error(self, error, *args):
32
38
def assert_tool_output (self , output , * args ):
33
39
p = self .bitcoin_wallet_process (* args )
34
40
stdout , stderr = p .communicate ()
35
- assert_equal (p .poll (), 0 )
36
41
assert_equal (stderr , '' )
37
42
assert_equal (stdout , output )
43
+ assert_equal (p .poll (), 0 )
38
44
39
- def run_test (self ):
45
+ def wallet_shasum (self ):
46
+ h = hashlib .sha1 ()
47
+ mv = memoryview (bytearray (BUFFER_SIZE ))
48
+ with open (self .wallet_path , 'rb' , buffering = 0 ) as f :
49
+ for n in iter (lambda : f .readinto (mv ), 0 ):
50
+ h .update (mv [:n ])
51
+ return h .hexdigest ()
40
52
53
+ def wallet_timestamp (self ):
54
+ return os .path .getmtime (self .wallet_path )
55
+
56
+ def wallet_permissions (self ):
57
+ return oct (os .lstat (self .wallet_path ).st_mode )[- 3 :]
58
+
59
+ def log_wallet_timestamp_comparison (self , old , new ):
60
+ result = 'unchanged' if new == old else 'increased!'
61
+ self .log .debug ('Wallet file timestamp {}' .format (result ))
62
+
63
+ def test_invalid_tool_commands_and_args (self ):
64
+ self .log .info ('Testing that various invalid commands raise with specific error messages' )
41
65
self .assert_raises_tool_error ('Invalid command: foo' , 'foo' )
42
- # `bitcoin-wallet help` is an error. Use `bitcoin-wallet -help`
66
+ # `bitcoin-wallet help` raises an error. Use `bitcoin-wallet -help`.
43
67
self .assert_raises_tool_error ('Invalid command: help' , 'help' )
44
68
self .assert_raises_tool_error ('Error: two methods provided (info and create). Only one method should be provided.' , 'info' , 'create' )
45
69
self .assert_raises_tool_error ('Error parsing command line arguments: Invalid parameter -foo' , '-foo' )
46
70
self .assert_raises_tool_error ('Error loading wallet.dat. Is wallet being used by other process?' , '-wallet=wallet.dat' , 'info' )
47
71
self .assert_raises_tool_error ('Error: no wallet file at nonexistent.dat' , '-wallet=nonexistent.dat' , 'info' )
48
72
49
- # stop the node to close the wallet to call info command
73
+ def test_tool_wallet_info (self ):
74
+ # Stop the node to close the wallet to call the info command.
50
75
self .stop_node (0 )
51
-
76
+ self .log .info ('Calling wallet tool info, testing output' )
77
+ #
78
+ # TODO: Wallet tool info should work with wallet file permissions set to
79
+ # read-only without raising:
80
+ # "Error loading wallet.dat. Is wallet being used by another process?"
81
+ # The following lines should be uncommented and the tests still succeed:
82
+ #
83
+ # self.log.debug('Setting wallet file permissions to 400 (read-only)')
84
+ # os.chmod(self.wallet_path, stat.S_IRUSR)
85
+ # assert(self.wallet_permissions() in ['400', '666']) # Sanity check. 666 because Appveyor.
86
+ # shasum_before = self.wallet_shasum()
87
+ timestamp_before = self .wallet_timestamp ()
88
+ self .log .debug ('Wallet file timestamp before calling info: {}' .format (timestamp_before ))
52
89
out = textwrap .dedent ('''\
53
90
Wallet info
54
91
===========
@@ -59,12 +96,35 @@ def run_test(self):
59
96
Address Book: 3
60
97
''' )
61
98
self .assert_tool_output (out , '-wallet=wallet.dat' , 'info' )
62
-
63
- # mutate the wallet to check the info command output changes accordingly
99
+ timestamp_after = self .wallet_timestamp ()
100
+ self .log .debug ('Wallet file timestamp after calling info: {}' .format (timestamp_after ))
101
+ self .log_wallet_timestamp_comparison (timestamp_before , timestamp_after )
102
+ self .log .debug ('Setting wallet file permissions back to 600 (read/write)' )
103
+ os .chmod (self .wallet_path , stat .S_IRUSR | stat .S_IWUSR )
104
+ assert (self .wallet_permissions () in ['600' , '666' ]) # Sanity check. 666 because Appveyor.
105
+ #
106
+ # TODO: Wallet tool info should not write to the wallet file.
107
+ # The following lines should be uncommented and the tests still succeed:
108
+ #
109
+ # assert_equal(timestamp_before, timestamp_after)
110
+ # shasum_after = self.wallet_shasum()
111
+ # assert_equal(shasum_before, shasum_after)
112
+ # self.log.debug('Wallet file shasum unchanged\n')
113
+
114
+ def test_tool_wallet_info_after_transaction (self ):
115
+ """
116
+ Mutate the wallet with a transaction to verify that the info command
117
+ output changes accordingly.
118
+ """
64
119
self .start_node (0 )
120
+ self .log .info ('Generating transaction to mutate wallet' )
65
121
self .nodes [0 ].generate (1 )
66
122
self .stop_node (0 )
67
123
124
+ self .log .info ('Calling wallet tool info after generating a transaction, testing output' )
125
+ shasum_before = self .wallet_shasum ()
126
+ timestamp_before = self .wallet_timestamp ()
127
+ self .log .debug ('Wallet file timestamp before calling info: {}' .format (timestamp_before ))
68
128
out = textwrap .dedent ('''\
69
129
Wallet info
70
130
===========
@@ -75,7 +135,22 @@ def run_test(self):
75
135
Address Book: 3
76
136
''' )
77
137
self .assert_tool_output (out , '-wallet=wallet.dat' , 'info' )
78
-
138
+ shasum_after = self .wallet_shasum ()
139
+ timestamp_after = self .wallet_timestamp ()
140
+ self .log .debug ('Wallet file timestamp after calling info: {}' .format (timestamp_after ))
141
+ self .log_wallet_timestamp_comparison (timestamp_before , timestamp_after )
142
+ #
143
+ # TODO: Wallet tool info should not write to the wallet file.
144
+ # This assertion should be uncommented and succeed:
145
+ # assert_equal(timestamp_before, timestamp_after)
146
+ assert_equal (shasum_before , shasum_after )
147
+ self .log .debug ('Wallet file shasum unchanged\n ' )
148
+
149
+ def test_tool_wallet_create_on_existing_wallet (self ):
150
+ self .log .info ('Calling wallet tool create on an existing wallet, testing output' )
151
+ shasum_before = self .wallet_shasum ()
152
+ timestamp_before = self .wallet_timestamp ()
153
+ self .log .debug ('Wallet file timestamp before calling create: {}' .format (timestamp_before ))
79
154
out = textwrap .dedent ('''\
80
155
Topping up keypool...
81
156
Wallet info
@@ -87,15 +162,48 @@ def run_test(self):
87
162
Address Book: 0
88
163
''' )
89
164
self .assert_tool_output (out , '-wallet=foo' , 'create' )
90
-
165
+ shasum_after = self .wallet_shasum ()
166
+ timestamp_after = self .wallet_timestamp ()
167
+ self .log .debug ('Wallet file timestamp after calling create: {}' .format (timestamp_after ))
168
+ self .log_wallet_timestamp_comparison (timestamp_before , timestamp_after )
169
+ assert_equal (timestamp_before , timestamp_after )
170
+ assert_equal (shasum_before , shasum_after )
171
+ self .log .debug ('Wallet file shasum unchanged\n ' )
172
+
173
+ def test_getwalletinfo_on_different_wallet (self ):
174
+ self .log .info ('Starting node with arg -wallet=foo' )
91
175
self .start_node (0 , ['-wallet=foo' ])
176
+
177
+ self .log .info ('Calling getwalletinfo on a different wallet ("foo"), testing output' )
178
+ shasum_before = self .wallet_shasum ()
179
+ timestamp_before = self .wallet_timestamp ()
180
+ self .log .debug ('Wallet file timestamp before calling getwalletinfo: {}' .format (timestamp_before ))
92
181
out = self .nodes [0 ].getwalletinfo ()
93
182
self .stop_node (0 )
94
183
184
+ shasum_after = self .wallet_shasum ()
185
+ timestamp_after = self .wallet_timestamp ()
186
+ self .log .debug ('Wallet file timestamp after calling getwalletinfo: {}' .format (timestamp_after ))
187
+
95
188
assert_equal (0 , out ['txcount' ])
96
189
assert_equal (1000 , out ['keypoolsize' ])
97
190
assert_equal (1000 , out ['keypoolsize_hd_internal' ])
98
191
assert_equal (True , 'hdseedid' in out )
99
192
193
+ self .log_wallet_timestamp_comparison (timestamp_before , timestamp_after )
194
+ assert_equal (timestamp_before , timestamp_after )
195
+ assert_equal (shasum_after , shasum_before )
196
+ self .log .debug ('Wallet file shasum unchanged\n ' )
197
+
198
+ def run_test (self ):
199
+ self .wallet_path = os .path .join (self .nodes [0 ].datadir , 'regtest' , 'wallets' , 'wallet.dat' )
200
+ self .test_invalid_tool_commands_and_args ()
201
+ # Warning: The following tests are order-dependent.
202
+ self .test_tool_wallet_info ()
203
+ self .test_tool_wallet_info_after_transaction ()
204
+ self .test_tool_wallet_create_on_existing_wallet ()
205
+ self .test_getwalletinfo_on_different_wallet ()
206
+
207
+
100
208
if __name__ == '__main__' :
101
209
ToolWalletTest ().main ()
0 commit comments