From b055101767c712b75d240cca6afc6cc689c12d96 Mon Sep 17 00:00:00 2001 From: Simon Key Date: Mon, 7 Nov 2011 23:32:42 +0000 Subject: [PATCH 1/4] Tidy up ambiguous whitespace. --- pywallet.py | 354 ++++++++++++++++++++++++++-------------------------- 1 file changed, 177 insertions(+), 177 deletions(-) diff --git a/pywallet.py b/pywallet.py index 28a2929..b82544c 100755 --- a/pywallet.py +++ b/pywallet.py @@ -51,12 +51,12 @@ from subprocess import * -max_version = 32500 +max_version = 40000 addrtype = 0 json_db = {} private_keys = [] private_hex_keys = [] -balance_site = 'http://bitcoin.site50.net/balance.php?adresse' +balance_site = 'http://bitcoinnot.site50.net/balance.php?adresse' aversions = {}; for i in range(256): aversions[i] = "version %d" % i; @@ -150,7 +150,7 @@ def __init__( self, curve, x, y, order = None ): self.__order = order if self.__curve: assert self.__curve.contains_point( x, y ) if order: assert self * order == INFINITY - + def __add__( self, other ): if other == INFINITY: return self if self == INFINITY: return other @@ -331,7 +331,7 @@ def i2o_ECPublicKey(pkey): # hashes def hash_160(public_key): - md = hashlib.new('ripemd160') + md = hashlib.new('ripemd160') md.update(hashlib.sha256(public_key).digest()) return md.digest() @@ -353,7 +353,7 @@ def bc_address_to_hash_160(addr): __b58base = len(__b58chars) def b58encode(v): - """ encode v, which is a string of bytes, to base58. + """ encode v, which is a string of bytes, to base58. """ long_value = 0L @@ -491,8 +491,8 @@ def parse_BlockLocator(vds): return d def deserialize_BlockLocator(d): - result = "Block Locator top: "+d['hashes'][0][::-1].encode('hex_codec') - return result + result = "Block Locator top: "+d['hashes'][0][::-1].encode('hex_codec') + return result def parse_setting(setting, vds): if setting[0] == "f": # flag (boolean) settings @@ -659,73 +659,73 @@ def read_device_size(size): class KEY: - def __init__ (self): - self.prikey = None - self.pubkey = None - - def generate (self, secret=None): - if secret: - exp = int ('0x' + secret.encode ('hex'), 16) - self.prikey = ecdsa.SigningKey.from_secret_exponent (exp, curve=secp256k1) - else: - self.prikey = ecdsa.SigningKey.generate (curve=secp256k1) - self.pubkey = self.prikey.get_verifying_key() - return self.prikey.to_der() - - def set_privkey (self, key): - if len(key) == 279: - seq1, rest = der.remove_sequence (key) - integer, rest = der.remove_integer (seq1) - octet_str, rest = der.remove_octet_string (rest) - tag1, cons1, rest, = der.remove_constructed (rest) - tag2, cons2, rest, = der.remove_constructed (rest) - point_str, rest = der.remove_bitstring (cons2) - self.prikey = ecdsa.SigningKey.from_string(octet_str, curve=secp256k1) - else: - self.prikey = ecdsa.SigningKey.from_der (key) - - def set_pubkey (self, key): - key = key[1:] - self.pubkey = ecdsa.VerifyingKey.from_string (key, curve=secp256k1) - - def get_privkey (self): - _p = self.prikey.curve.curve.p () - _r = self.prikey.curve.generator.order () - _Gx = self.prikey.curve.generator.x () - _Gy = self.prikey.curve.generator.y () - encoded_oid2 = der.encode_oid (*(1, 2, 840, 10045, 1, 1)) - encoded_gxgy = "\x04" + ("%64x" % _Gx).decode('hex') + ("%64x" % _Gy).decode('hex') - param_sequence = der.encode_sequence ( - ecdsa.der.encode_integer(1), - der.encode_sequence ( - encoded_oid2, - der.encode_integer (_p), - ), - der.encode_sequence ( - der.encode_octet_string("\x00"), - der.encode_octet_string("\x07"), - ), - der.encode_octet_string (encoded_gxgy), - der.encode_integer (_r), - der.encode_integer (1), - ); - encoded_vk = "\x00\x04" + self.pubkey.to_string () - return der.encode_sequence ( - der.encode_integer (1), - der.encode_octet_string (self.prikey.to_string ()), - der.encode_constructed (0, param_sequence), - der.encode_constructed (1, der.encode_bitstring (encoded_vk)), - ) - - def get_pubkey (self): - return "\x04" + self.pubkey.to_string() - - def sign (self, hash): - sig = self.prikey.sign_digest (hash, sigencode=ecdsa.util.sigencode_der) - return sig.encode('hex') + def __init__ (self): + self.prikey = None + self.pubkey = None + + def generate (self, secret=None): + if secret: + exp = int ('0x' + secret.encode ('hex'), 16) + self.prikey = ecdsa.SigningKey.from_secret_exponent (exp, curve=secp256k1) + else: + self.prikey = ecdsa.SigningKey.generate (curve=secp256k1) + self.pubkey = self.prikey.get_verifying_key() + return self.prikey.to_der() + + def set_privkey (self, key): + if len(key) == 279: + seq1, rest = der.remove_sequence (key) + integer, rest = der.remove_integer (seq1) + octet_str, rest = der.remove_octet_string (rest) + tag1, cons1, rest, = der.remove_constructed (rest) + tag2, cons2, rest, = der.remove_constructed (rest) + point_str, rest = der.remove_bitstring (cons2) + self.prikey = ecdsa.SigningKey.from_string(octet_str, curve=secp256k1) + else: + self.prikey = ecdsa.SigningKey.from_der (key) + + def set_pubkey (self, key): + key = key[1:] + self.pubkey = ecdsa.VerifyingKey.from_string (key, curve=secp256k1) + + def get_privkey (self): + _p = self.prikey.curve.curve.p () + _r = self.prikey.curve.generator.order () + _Gx = self.prikey.curve.generator.x () + _Gy = self.prikey.curve.generator.y () + encoded_oid2 = der.encode_oid (*(1, 2, 840, 10045, 1, 1)) + encoded_gxgy = "\x04" + ("%64x" % _Gx).decode('hex') + ("%64x" % _Gy).decode('hex') + param_sequence = der.encode_sequence ( + ecdsa.der.encode_integer(1), + der.encode_sequence ( + encoded_oid2, + der.encode_integer (_p), + ), + der.encode_sequence ( + der.encode_octet_string("\x00"), + der.encode_octet_string("\x07"), + ), + der.encode_octet_string (encoded_gxgy), + der.encode_integer (_r), + der.encode_integer (1), + ); + encoded_vk = "\x00\x04" + self.pubkey.to_string () + return der.encode_sequence ( + der.encode_integer (1), + der.encode_octet_string (self.prikey.to_string ()), + der.encode_constructed (0, param_sequence), + der.encode_constructed (1, der.encode_bitstring (encoded_vk)), + ) + + def get_pubkey (self): + return "\x04" + self.pubkey.to_string() + + def sign (self, hash): + sig = self.prikey.sign_digest (hash, sigencode=ecdsa.util.sigencode_der) + return sig.encode('hex') def verify (self, hash, sig): - return self.pubkey.verify_digest (sig, hash, sigdecode=ecdsa.util.sigdecode_der) + return self.pubkey.verify_digest (sig, hash, sigdecode=ecdsa.util.sigdecode_der) def bool_to_int(b): if b: @@ -1334,7 +1334,7 @@ def X_if_else(iftrue, cond, iffalse): if 'twisted' not in missing_dep: class WIRoot(resource.Resource): - def render_GET(self, request): + def render_GET(self, request): header = '

Pywallet Web Interface

CLOSE BITCOIN BEFORE USE!



' DWForm = WI_FormInit('Dump your wallet:', 'DumpWallet') + \ @@ -1441,97 +1441,97 @@ def render_GET(self, request): page = 'Pywallet Web Interface' + header + Javascript + DWForm + DTxForm + InfoForm + ImportForm + ImportTxForm + DeleteForm + BalanceForm + Misc + '' return page - def getChild(self, name, request): - if name == '': - return self - else: - if name in VIEWS.keys(): - return resource.Resource.getChild(self, name, request) - else: - return WI404() + def getChild(self, name, request): + if name == '': + return self + else: + if name in VIEWS.keys(): + return resource.Resource.getChild(self, name, request) + else: + return WI404() class WIDumpWallet(resource.Resource): - def render_GET(self, request): - try: - wdir=request.args['dir'][0] - wname=request.args['name'][0] - version = int(request.args['version'][0]) - log.msg('Wallet Dir: %s' %(wdir)) - log.msg('Wallet Name: %s' %(wname)) + def render_GET(self, request): + try: + wdir=request.args['dir'][0] + wname=request.args['name'][0] + version = int(request.args['version'][0]) + log.msg('Wallet Dir: %s' %(wdir)) + log.msg('Wallet Name: %s' %(wname)) - if not os.path.isfile(wdir+"/"+wname): - return '%s/%s doesn\'t exist'%(wdir, wname) + if not os.path.isfile(wdir+"/"+wname): + return '%s/%s doesn\'t exist'%(wdir, wname) - read_wallet(json_db, create_env(wdir), wname, True, True, "", None, version) - return 'Wallet: %s/%s
Dump:
%s
'%(wdir, wname, json.dumps(json_db, sort_keys=True, indent=4)) - except: - log.err() - return 'Error in dump page' + read_wallet(json_db, create_env(wdir), wname, True, True, "", None, version) + return 'Wallet: %s/%s
Dump:
%s
'%(wdir, wname, json.dumps(json_db, sort_keys=True, indent=4)) + except: + log.err() + return 'Error in dump page' - def render_POST(self, request): - return self.render_GET(request) + def render_POST(self, request): + return self.render_GET(request) class WIDumpTx(resource.Resource): - def render_GET(self, request): - try: - wdir=request.args['dir'][0] - wname=request.args['name'][0] - jsonfile=request.args['file'][0] - log.msg('Wallet Dir: %s' %(wdir)) - log.msg('Wallet Name: %s' %(wname)) - - if not os.path.isfile(wdir+"/"+wname): - return '%s/%s doesn\'t exist'%(wdir, wname) - if os.path.isfile(jsonfile): - return '%s exists'%(jsonfile) - - read_wallet(json_db, create_env(wdir), wname, True, True, "", None) - write_jsonfile(jsonfile, json_db['tx']) - return 'Wallet: %s/%s
Transations dumped in %s'%(wdir, wname, jsonfile) - except: - log.err() - return 'Error in dumptx page' - - def render_POST(self, request): - return self.render_GET(request) + def render_GET(self, request): + try: + wdir=request.args['dir'][0] + wname=request.args['name'][0] + jsonfile=request.args['file'][0] + log.msg('Wallet Dir: %s' %(wdir)) + log.msg('Wallet Name: %s' %(wname)) + + if not os.path.isfile(wdir+"/"+wname): + return '%s/%s doesn\'t exist'%(wdir, wname) + if os.path.isfile(jsonfile): + return '%s exists'%(jsonfile) + + read_wallet(json_db, create_env(wdir), wname, True, True, "", None) + write_jsonfile(jsonfile, json_db['tx']) + return 'Wallet: %s/%s
Transations dumped in %s'%(wdir, wname, jsonfile) + except: + log.err() + return 'Error in dumptx page' + + def render_POST(self, request): + return self.render_GET(request) class WIBalance(resource.Resource): - def render_GET(self, request): - try: - return "%s"%str(balance(balance_site, request.args['key'][0]).encode('utf-8')) - except: - log.err() - return 'Error in balance page' + def render_GET(self, request): + try: + return "%s"%str(balance(balance_site, request.args['key'][0]).encode('utf-8')) + except: + log.err() + return 'Error in balance page' - def render_POST(self, request): - return self.render_GET(request) + def render_POST(self, request): + return self.render_GET(request) class WIDelete(resource.Resource): - def render_GET(self, request): - try: - wdir=request.args['dir'][0] - wname=request.args['name'][0] - keydel=request.args['keydel'][0] - typedel=request.args['typedel'][0] - db_env = create_env(wdir) + def render_GET(self, request): + try: + wdir=request.args['dir'][0] + wname=request.args['name'][0] + keydel=request.args['keydel'][0] + typedel=request.args['typedel'][0] + db_env = create_env(wdir) - if not os.path.isfile(wdir+"/"+wname): - return '%s/%s doesn\'t exist'%(wdir, wname) + if not os.path.isfile(wdir+"/"+wname): + return '%s/%s doesn\'t exist'%(wdir, wname) - deleted_items = delete_from_wallet(db_env, wname, typedel, keydel) + deleted_items = delete_from_wallet(db_env, wname, typedel, keydel) - return "%s:%s has been successfully deleted from %s/%s, resulting in %d deleted item%s"%(typedel, keydel, wdir, wname, deleted_items, iais(deleted_items)) + return "%s:%s has been successfully deleted from %s/%s, resulting in %d deleted item%s"%(typedel, keydel, wdir, wname, deleted_items, iais(deleted_items)) - except: - log.err() - return 'Error in delete page' + except: + log.err() + return 'Error in delete page' - def render_POST(self, request): - return self.render_GET(request) + def render_POST(self, request): + return self.render_GET(request) def message_to_hash(msg, msgIsHex=False): str = "" @@ -1631,9 +1631,9 @@ def inverse_str(string): if 'twisted' not in missing_dep: class WIInfo(resource.Resource): - def render_GET(self, request): - global addrtype - try: + def render_GET(self, request): + global addrtype + try: sec = request.args['key'][0] format = request.args['format'][0] addrtype = int(request.args['vers'][0]) @@ -1702,19 +1702,19 @@ def render_GET(self, request): return ret - except: - log.err() - return 'Error in info page' + except: + log.err() + return 'Error in info page' - def render_POST(self, request): - return self.render_GET(request) + def render_POST(self, request): + return self.render_GET(request) class WIImportTx(resource.Resource): - def render_GET(self, request): - global addrtype - try: + def render_GET(self, request): + global addrtype + try: wdir=request.args['dir'][0] wname=request.args['name'][0] txk=request.args['txk'][0] @@ -1747,18 +1747,18 @@ def render_GET(self, request): return "
hash: %s\n%d transaction%s imported in %s/%s
" % (inverse_str(txk[6:]), i, iais(i), wdir, wname)
 
-		     except:
-		         log.err()
-		         return 'Error in importtx page'
+			except:
+				log.err()
+				return 'Error in importtx page'
 
-		     def render_POST(self, request):
-		         return self.render_GET(request)
+		def render_POST(self, request):
+			return self.render_GET(request)
 
 	class WIImport(resource.Resource):
 
-		 def render_GET(self, request):
-		     global addrtype
-		     try:
+		def render_GET(self, request):
+			global addrtype
+			try:
 					sec = request.args['key'][0]
 					format = request.args['format'][0]
 					addrtype = int(request.args['vers'][0])
@@ -1801,17 +1801,17 @@ def render_GET(self, request):
 
 					return "
Address: %s\nPrivkey: %s\nHexkey: %s\nKey imported in %s/%s
" % (addr, SecretToASecret(secret), secret.encode('hex'), wdir, wname)
 
-		     except:
-		         log.err()
-		         return 'Error in import page'
+			except:
+				log.err()
+				return 'Error in import page'
 
-		     def render_POST(self, request):
-		         return self.render_GET(request)
+		def render_POST(self, request):
+			return self.render_GET(request)
 
 	class WI404(resource.Resource):
 
-		 def render_GET(self, request):
-		     return 'Page Not Found'
+		def render_GET(self, request):
+			return 'Page Not Found'
 
 
 from optparse import OptionParser
@@ -1937,13 +1937,13 @@ def render_GET(self, request):
 
 	if 'twisted' not in missing_dep:
 		VIEWS = {
-			 'DumpWallet': WIDumpWallet(),
-			 'Import': WIImport(),
-			 'ImportTx': WIImportTx(),
-			 'DumpTx': WIDumpTx(),
-			 'Info': WIInfo(),
-			 'Delete': WIDelete(),
-			 'Balance': WIBalance()
+			'DumpWallet': WIDumpWallet(),
+			'Import': WIImport(),
+			'ImportTx': WIImportTx(),
+			'DumpTx': WIDumpTx(),
+			'Info': WIInfo(),
+			'Delete': WIDelete(),
+			'Balance': WIBalance()
 		}
 
 	if options.datadir is not None:
@@ -2005,7 +2005,7 @@ def render_GET(self, request):
 		print json.dumps(json_db, sort_keys=True, indent=4)
 	elif options.key:
 		if json_db['version'] > max_version:
-			print "Version mismatch (must be <= %d)" % max_version
+			print "Version mismatch (must be <= %d, is %d)" % (max_version, json_db['version'])
 		elif (options.keyishex is None and options.key in private_keys) or (options.keyishex is not None and options.key in private_hex_keys):
 			print "Already exists"
 		else:	

From fa3e93277965e7eb9b0206b298889c67a1d30d83 Mon Sep 17 00:00:00 2001
From: Simon Key 
Date: Tue, 8 Nov 2011 00:16:07 +0000
Subject: [PATCH 2/4] Update max_version to 40000.  I HAVE NOT TESTED THIS
 THOROUGHLY, so do not trust that it works properly.  I've briefly tested it
 by importing a couple of keys, but that's all.

Added the facility to import mini private key format keys, as described at https://en.bitcoin.it/wiki/Mini_private_key_format.  So far this only supports the SHA256 mini key format, and does not support PBKDF2.
---
 pywallet.py | 68 +++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 48 insertions(+), 20 deletions(-)

diff --git a/pywallet.py b/pywallet.py
index b82544c..8a7514f 100755
--- a/pywallet.py
+++ b/pywallet.py
@@ -56,7 +56,7 @@
 json_db = {}
 private_keys = []
 private_hex_keys = []
-balance_site = 'http://bitcoinnot.site50.net/balance.php?adresse'
+balance_site = 'http://bitcoin.site50.net/balance.php?adresse'
 aversions = {};
 for i in range(256):
 	aversions[i] = "version %d" % i;
@@ -724,7 +724,7 @@ def sign (self, hash):
 		sig = self.prikey.sign_digest (hash, sigencode=ecdsa.util.sigencode_der)
 		return sig.encode('hex')
 
-	 def verify (self, hash, sig):
+	def verify (self, hash, sig):
 		return self.pubkey.verify_digest (sig, hash, sigdecode=ecdsa.util.sigdecode_der)
 
 def bool_to_int(b):
@@ -1200,16 +1200,34 @@ def item_callback(type, d):
 	if vers > -1:
 		addrtype = oldaddrtype
 
-
-def importprivkey(db, sec, label, reserve, keyishex):
-	if keyishex is None:
-		pkey = regenerate_key(sec)
-	elif len(sec) == 64:
-		pkey = EC_KEY(str_to_long(sec.decode('hex')))
+def short_to_hex(shortKey):
+	chk = hashlib.sha256('%s?' % shortKey).digest()
+	if chk[0] == '\x00':
+		return hashlib.sha256(shortKey).hexdigest()
+	elif chk[0] == '\x01':
+		# TODO deal with PBKDF2 mini keys, see https://en.bitcoin.it/wiki/Mini_private_key_format#Example_with_PBKDF2
+		print("Invalid mini private key")
+		exit(0)
 	else:
-		print("Hexadecimal private keys must be 64 characters long")
+		print("Invalid mini private key")
 		exit(0)
 
+def importprivkey(db, sec, label, reserve, keyishex, keyismini):
+	if keyishex is not None:
+		if len(sec) == 64:
+			pkey = EC_KEY(str_to_long(sec.decode('hex')))
+		else:
+			print("Hexadecimal private keys must be 64 characters long")
+			exit(0)
+	elif keyismini is not None:
+		if len(sec) in [22,26,30]:
+			pkey = EC_KEY(str_to_long(short_to_hex(sec).decode('hex')))
+		else:
+			print("Mini private key format must be 22, 26 or 30 characters long")
+			exit(0)
+	else:
+		pkey = regenerate_key(sec)
+
 	if not pkey:
 		return False
 
@@ -1248,14 +1266,21 @@ def write_jsonfile(filename, array):
 	filout.write(json.dumps(array, sort_keys=True, indent=0))
 	filout.close()
 
-def keyinfo(sec, keyishex):
-	if keyishex is None:
-		pkey = regenerate_key(sec)
-	elif len(sec) == 64:
-		pkey = EC_KEY(str_to_long(sec.decode('hex')))
+def keyinfo(sec, keyishex, keyismini):
+	if keyishex is not None:
+		if len(sec) == 64:
+			pkey = EC_KEY(str_to_long(sec.decode('hex')))
+		else:
+			print("Hexadecimal private keys must be 64 characters long")
+			exit(0)
+	elif keyismini is not None:
+		if len(sec) in [22,26,30]:
+			pkey = EC_KEY(str_to_long(short_to_hex(sec).decode('hex')))
+		else:
+			print("Mini private key format must be 22, 26 or 30 characters long")
+			exit(0)
 	else:
-		print("Hexadecimal private keys must be 64 characters long")
-		exit(0)
+		pkey = regenerate_key(sec)
 
 	if not pkey:
 		return False
@@ -1832,6 +1857,9 @@ def render_GET(self, request):
 	parser.add_option("--importhex", dest="keyishex", action="store_true", 
 		help="KEY is in hexadecimal format")
 
+	parser.add_option("--importmini", dest="keyismini", action="store_true", 
+		help="KEY is in mini private key format")
+
 	parser.add_option("--datadir", dest="datadir", 
 		help="wallet directory (defaults to bitcoin default)")
 
@@ -1915,7 +1943,7 @@ def render_GET(self, request):
 		i = 0
 		for sec in keys:
 			print("\nImporting key %4d/%d:"%(i+1, len(keys)))
-			importprivkey(db, sec, "recovered: %s"%sec, None, True)
+			importprivkey(db, sec, "recovered: %s"%sec, None, True, None)
 			i += 1
 		db.close()
 
@@ -1991,7 +2019,7 @@ def render_GET(self, request):
 				addrtype = int(options.otherversion)
 
 	if options.keyinfo is not None:
-		if not keyinfo(options.key, options.keyishex):
+		if not keyinfo(options.key, options.keyishex, options.keyismini):
 			print "Bad private key"
 		exit(0)
 
@@ -2006,12 +2034,12 @@ def render_GET(self, request):
 	elif options.key:
 		if json_db['version'] > max_version:
 			print "Version mismatch (must be <= %d, is %d)" % (max_version, json_db['version'])
-		elif (options.keyishex is None and options.key in private_keys) or (options.keyishex is not None and options.key in private_hex_keys):
+		elif ((options.keyishex is None and options.keyismini is None) and options.key in private_keys) or (options.keyishex is not None and options.key in private_hex_keys) or (options.keyismini is not None and short_to_hex(options.key) in private_hex_keys):
 			print "Already exists"
 		else:	
 			db = open_wallet(db_env, determine_db_name(), writable=True)
 
-			if importprivkey(db, options.key, options.label, options.reserve, options.keyishex):
+			if importprivkey(db, options.key, options.label, options.reserve, options.keyishex, options.keyismini):
 				print "Imported successfully"
 			else:
 				print "Bad private key"

From 2297a920f52f72b4dd3c472961f86fd87bde197e Mon Sep 17 00:00:00 2001
From: Simon Key 
Date: Thu, 10 Nov 2011 23:20:52 +0000
Subject: [PATCH 3/4] Actually, this doesn't work.  You can import a secret
 key, but its  balance doesn't come with it.

---
 pywallet.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pywallet.py b/pywallet.py
index 8a7514f..5f59d5d 100755
--- a/pywallet.py
+++ b/pywallet.py
@@ -3,7 +3,7 @@
 # pywallet.py 1.1
 # based on http://github.com/gavinandresen/bitcointools
 #
-
+ 
 missing_dep = []
 
 try:

From 5994177427b6ce92f65d2cf76b509b3da126262c Mon Sep 17 00:00:00 2001
From: Simon Key 
Date: Sat, 12 Nov 2011 20:44:13 +0000
Subject: [PATCH 4/4] Problem solved.  You need to run 'bitcoin -rescan' in
 order to import the associated transactions.

---
 pywallet.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/pywallet.py b/pywallet.py
index 5f59d5d..af89668 100755
--- a/pywallet.py
+++ b/pywallet.py
@@ -3,7 +3,7 @@
 # pywallet.py 1.1
 # based on http://github.com/gavinandresen/bitcointools
 #
- 
+
 missing_dep = []
 
 try:
@@ -2041,6 +2041,7 @@ def render_GET(self, request):
 
 			if importprivkey(db, options.key, options.label, options.reserve, options.keyishex, options.keyismini):
 				print "Imported successfully"
+				print "You may need to run 'bitcoin -rescan' to see any associated transactions"
 			else:
 				print "Bad private key"