a41d3a62e52688682a6f49634d32292b4464863b
[novacoin.git] / bitcointools / wallet.py
1 #
2 # Code for parsing the wallet.dat file
3 #
4
5 from bsddb.db import *
6 import logging
7 import re
8 import sys
9 import time
10
11 from BCDataStream import *
12 from base58 import public_key_to_bc_address, bc_address_to_hash_160, hash_160
13 from util import short_hex, long_hex
14 from deserialize import *
15
16 def open_wallet(db_env, writable=False):
17   db = DB(db_env)
18   flags = DB_THREAD | (DB_CREATE if writable else DB_RDONLY)
19   try:
20     r = db.open("wallet.dat", "main", DB_BTREE, flags)
21   except DBError:
22     r = True
23
24   if r is not None:
25     logging.error("Couldn't open wallet.dat/main. Try quitting Bitcoin and running this again.")
26     sys.exit(1)
27   
28   return db
29
30 def parse_wallet(db, item_callback):
31   kds = BCDataStream()
32   vds = BCDataStream()
33
34   for (key, value) in db.items():
35     d = { }
36
37     kds.clear(); kds.write(key)
38     vds.clear(); vds.write(value)
39
40     type = kds.read_string()
41
42     d["__key__"] = key
43     d["__value__"] = value
44     d["__type__"] = type
45
46     try:
47       if type == "tx":
48         d["tx_id"] = kds.read_bytes(32)
49         d.update(parse_WalletTx(vds))
50       elif type == "name":
51         d['hash'] = kds.read_string()
52         d['name'] = vds.read_string()
53       elif type == "version":
54         d['version'] = vds.read_uint32()
55       elif type == "setting":
56         d['setting'] = kds.read_string()
57         d['value'] = parse_setting(d['setting'], vds)
58       elif type == "key":
59         d['public_key'] = kds.read_bytes(kds.read_compact_size())
60         d['private_key'] = vds.read_bytes(vds.read_compact_size())
61       elif type == "wkey":
62         d['public_key'] = kds.read_bytes(kds.read_compact_size())
63         d['private_key'] = vds.read_bytes(vds.read_compact_size())
64         d['created'] = vds.read_int64()
65         d['expires'] = vds.read_int64()
66         d['comment'] = vds.read_string()
67       elif type == "ckey":
68         d['public_key'] = kds.read_bytes(kds.read_compact_size())
69         d['crypted_key'] = vds.read_bytes(vds.read_compact_size())
70       elif type == "mkey":
71         d['nID'] = kds.read_int32()
72         d['crypted_key'] = vds.read_bytes(vds.read_compact_size())
73         d['salt'] = vds.read_bytes(vds.read_compact_size())
74         d['nDerivationMethod'] = vds.read_int32()
75         d['nDeriveIterations'] = vds.read_int32()
76         d['vchOtherDerivationParameters'] = vds.read_bytes(vds.read_compact_size())
77       elif type == "defaultkey":
78         d['key'] = vds.read_bytes(vds.read_compact_size())
79       elif type == "pool":
80         d['n'] = kds.read_int64()
81         d['nVersion'] = vds.read_int32()
82         d['nTime'] = vds.read_int64()
83         d['public_key'] = vds.read_bytes(vds.read_compact_size())
84       elif type == "acc":
85         d['account'] = kds.read_string()
86         d['nVersion'] = vds.read_int32()
87         d['public_key'] = vds.read_bytes(vds.read_compact_size())
88       elif type == "acentry":
89         d['account'] = kds.read_string()
90         d['n'] = kds.read_uint64()
91         d['nVersion'] = vds.read_int32()
92         d['nCreditDebit'] = vds.read_int64()
93         d['nTime'] = vds.read_int64()
94         d['otherAccount'] = vds.read_string()
95         d['comment'] = vds.read_string()
96       elif type == "bestblock":
97         d['nVersion'] = vds.read_int32()
98         d.update(parse_BlockLocator(vds))
99       else:
100         print "Unknown key type: "+type
101       
102       item_callback(type, d)
103
104     except Exception, e:
105       print("ERROR parsing wallet.dat, type %s"%type)
106       print("key data in hex: %s"%key.encode('hex_codec'))
107       print("value data in hex: %s"%value.encode('hex_codec'))
108   
109 def update_wallet(db, type, data):
110   """Write a single item to the wallet.
111   db must be open with writable=True.
112   type and data are the type code and data dictionary as parse_wallet would
113   give to item_callback.
114   data's __key__, __value__ and __type__ are ignored; only the primary data
115   fields are used.
116   """
117   d = data
118   kds = BCDataStream()
119   vds = BCDataStream()
120
121   # Write the type code to the key
122   kds.write_string(type)
123   vds.write("")             # Ensure there is something
124
125   try:
126     if type == "tx":
127       raise NotImplementedError("Writing items of type 'tx'")
128       kds.write(d['tx_id'])
129       #d.update(parse_WalletTx(vds))
130     elif type == "name":
131       kds.write(d['hash'])
132       vds.write(d['name'])
133     elif type == "version":
134       vds.write_uint32(d['version'])
135     elif type == "setting":
136       raise NotImplementedError("Writing items of type 'setting'")
137       kds.write_string(d['setting'])
138       #d['value'] = parse_setting(d['setting'], vds)
139     elif type == "key":
140       kds.write_string(d['public_key'])
141       vds.write_string(d['private_key'])
142     elif type == "wkey":
143       kds.write_string(d['public_key'])
144       vds.write_string(d['private_key'])
145       vds.write_int64(d['created'])
146       vds.write_int64(d['expires'])
147       vds.write_string(d['comment'])
148     elif type == "ckey":
149       kds.write_string(d['public_key'])
150       kds.write_string(d['crypted_key'])
151     elif type == "mkey":
152       kds.write_int32(d['nID'])
153       vds.write_string(d['crypted_key'])
154       vds.write_string(d['salt'])
155       vds.write_int32(d['nDeriveIterations'])
156       vds.write_int32(d['nDerivationMethod'])
157       vds.write_string(d['vchOtherDerivationParameters'])
158     elif type == "defaultkey":
159       vds.write_string(d['key'])
160     elif type == "pool":
161       kds.write_int64(d['n'])
162       vds.write_int32(d['nVersion'])
163       vds.write_int64(d['nTime'])
164       vds.write_string(d['public_key'])
165     elif type == "acc":
166       kds.write_string(d['account'])
167       vds.write_int32(d['nVersion'])
168       vds.write_string(d['public_key'])
169     elif type == "acentry":
170       kds.write_string(d['account'])
171       kds.write_uint64(d['n'])
172       vds.write_int32(d['nVersion'])
173       vds.write_int64(d['nCreditDebit'])
174       vds.write_int64(d['nTime'])
175       vds.write_string(d['otherAccount'])
176       vds.write_string(d['comment'])
177     elif type == "bestblock":
178       vds.write_int32(d['nVersion'])
179       vds.write_compact_size(len(d['hashes']))
180       for h in d['hashes']:
181         vds.write(h)
182     else:
183       print "Unknown key type: "+type
184
185     # Write the key/value pair to the database
186     db.put(kds.input, vds.input)
187
188   except Exception, e:
189     print("ERROR writing to wallet.dat, type %s"%type)
190     print("data dictionary: %r"%data)
191
192 def dump_wallet(db_env, print_wallet, print_wallet_transactions, transaction_filter):
193   db = open_wallet(db_env)
194
195   wallet_transactions = []
196   transaction_index = { }
197   owner_keys = { }
198
199   def item_callback(type, d):
200     if type == "tx":
201       wallet_transactions.append( d )
202       transaction_index[d['tx_id']] = d
203     elif type == "key":
204       owner_keys[public_key_to_bc_address(d['public_key'])] = d['private_key']
205     elif type == "ckey":
206       owner_keys[public_key_to_bc_address(d['public_key'])] = d['crypted_key']
207
208     if not print_wallet:
209       return
210     if type == "tx":
211       return
212     elif type == "name":
213       print("ADDRESS "+d['hash']+" : "+d['name'])
214     elif type == "version":
215       print("Version: %d"%(d['version'],))
216     elif type == "setting":
217       print(d['setting']+": "+str(d['value']))
218     elif type == "key":
219       print("PubKey "+ short_hex(d['public_key']) + " " + public_key_to_bc_address(d['public_key']) +
220             ": PriKey "+ short_hex(d['private_key']))
221     elif type == "wkey":
222       print("WPubKey 0x"+ short_hex(d['public_key']) + " " + public_key_to_bc_address(d['public_key']) +
223             ": WPriKey 0x"+ short_hex(d['crypted_key']))
224       print(" Created: "+time.ctime(d['created'])+" Expires: "+time.ctime(d['expires'])+" Comment: "+d['comment'])
225     elif type == "ckey":
226       print("PubKey "+ short_hex(d['public_key']) + " " + public_key_to_bc_address(d['public_key']) +
227             ": Encrypted PriKey "+ short_hex(d['crypted_key']))
228     elif type == "mkey":
229       print("Master Key %d"%(d['nID']) + ": 0x"+ short_hex(d['crypted_key']) +
230             ", Salt: 0x"+ short_hex(d['salt']) +
231             ". Passphrase hashed %d times with method %d with other parameters 0x"%(d['nDeriveIterations'], d['nDerivationMethod']) +
232             long_hex(d['vchOtherDerivationParameters']))
233     elif type == "defaultkey":
234       print("Default Key: 0x"+ short_hex(d['key']) + " " + public_key_to_bc_address(d['key']))
235     elif type == "pool":
236       print("Change Pool key %d: %s (Time: %s)"% (d['n'], public_key_to_bc_address(d['public_key']), time.ctime(d['nTime'])))
237     elif type == "acc":
238       print("Account %s (current key: %s)"%(d['account'], public_key_to_bc_address(d['public_key'])))
239     elif type == "acentry":
240       print("Move '%s' %d (other: '%s', time: %s, entry %d) %s"%
241             (d['account'], d['nCreditDebit'], d['otherAccount'], time.ctime(d['nTime']), d['n'], d['comment']))
242     elif type == "bestblock":
243       print deserialize_BlockLocator(d)
244     else:
245       print "Unknown key type: "+type
246
247   parse_wallet(db, item_callback)
248
249   if print_wallet_transactions:
250     keyfunc = lambda i: i['timeReceived']
251     for d in sorted(wallet_transactions, key=keyfunc):
252       tx_value = deserialize_WalletTx(d, transaction_index, owner_keys)
253       if len(transaction_filter) > 0 and re.search(transaction_filter, tx_value) is None: continue
254
255       print("==WalletTransaction== "+long_hex(d['tx_id'][::-1]))
256       print(tx_value)
257
258   db.close()
259
260 def dump_accounts(db_env):
261   db = open_wallet(db_env)
262
263   kds = BCDataStream()
264   vds = BCDataStream()
265
266   accounts = set()
267
268   for (key, value) in db.items():
269     kds.clear(); kds.write(key)
270     vds.clear(); vds.write(value)
271
272     type = kds.read_string()
273
274     if type == "acc":
275       accounts.add(kds.read_string())
276     elif type == "name":
277       accounts.add(vds.read_string())
278     elif type == "acentry":
279       accounts.add(kds.read_string())
280       # Note: don't need to add otheraccount, because moves are
281       # always double-entry
282
283   for name in sorted(accounts):
284     print(name)
285
286   db.close()
287
288 def rewrite_wallet(db_env, destFileName, pre_put_callback=None):
289   db = open_wallet(db_env)
290
291   db_out = DB(db_env)
292   try:
293     r = db_out.open(destFileName, "main", DB_BTREE, DB_CREATE)
294   except DBError:
295     r = True
296
297   if r is not None:
298     logging.error("Couldn't open %s."%destFileName)
299     sys.exit(1)
300
301   def item_callback(type, d):
302     if (pre_put_callback is None or pre_put_callback(type, d)):
303       db_out.put(d["__key__"], d["__value__"])
304
305   parse_wallet(db, item_callback)
306
307   db_out.close()
308   db.close()
309
310 def trim_wallet(db_env, destFileName, pre_put_callback=None):
311   """Write out ONLY address book public/private keys
312      THIS WILL NOT WRITE OUT 'change' KEYS-- you should
313      send all of your bitcoins to one of your public addresses
314      before calling this.
315   """
316   db = open_wallet(db_env)
317   
318   pubkeys = []
319   def gather_pubkeys(type, d):
320     if type == "name":
321       pubkeys.append(bc_address_to_hash_160(d['hash']))
322   
323   parse_wallet(db, gather_pubkeys)
324
325   db_out = DB(db_env)
326   try:
327     r = db_out.open(destFileName, "main", DB_BTREE, DB_CREATE)
328   except DBError:
329     r = True
330
331   if r is not None:
332     logging.error("Couldn't open %s."%destFileName)
333     sys.exit(1)
334
335   def item_callback(type, d):
336     should_write = False
337     if type in [ 'version', 'name', 'acc' ]:
338       should_write = True
339     if type in [ 'key', 'wkey', 'ckey' ] and hash_160(d['public_key']) in pubkeys:
340       should_write = True
341     if pre_put_callback is not None:
342       should_write = pre_put_callback(type, d, pubkeys)
343     if should_write:
344       db_out.put(d["__key__"], d["__value__"])
345
346   parse_wallet(db, item_callback)
347
348   db_out.close()
349   db.close()