diff --git a/README b/README index 100a62e..429a38a 100644 --- a/README +++ b/README @@ -11,10 +11,15 @@ Options: --label=LABEL label shown in the adress book (defaults to '') --testnet use testnet subdirectory and address type --namecoin use namecoin address type + --otherversion=OTHERVERSION + use other network address type, whose version is + OTHERVERSION --info display pubkey, privkey (both depending on the network) and hexkey --reserve import as a reserve key, i.e. it won't show in the adress book --balance=KEY_BALANCE prints balance of KEY_BALANCE + --web run pywallet web interface + --port=PORT port of web interface (defaults to 8989) diff --git a/pywallet.py b/pywallet.py index 4ff67ac..96c010a 100755 --- a/pywallet.py +++ b/pywallet.py @@ -19,6 +19,13 @@ import hashlib import random import urllib +from twisted.internet import reactor +from twisted.web import server, resource +from twisted.web.static import File +from twisted.python import log +from datetime import datetime + + max_version = 32500 addrtype = 0 json_db = {} @@ -30,7 +37,7 @@ for i in range(256): aversions[i] = "version %d" % i; aversions[0] = 'Bitcoin'; aversions[52] = 'Namecoin'; -aversions[111] = 'Bitcoin testnet'; +aversions[111] = 'Testnet'; def determine_db_dir(): import os @@ -853,9 +860,256 @@ def keyinfo(sec, keyishex): return True -def main(): +class WIRoot(resource.Resource): - global max_version, addrtype + def render_GET(self, request): + header = '

Pywallet Web Interface



' + + DWForm = '

Dump your wallet:

\ + Wallet Directory:
\ + Wallet Filename:
\ + \ + \ + \ +

' + + InfoForm = '

Get some info about one key:

\ + Key:
\ + Version:
\ + Format:
\ + Regular, base 58
\ + Hexadecimal, 64 characters long
\ + \ + \ + \ +

' + + ImportForm = '

Import a key in your wallet:

\ + Wallet Directory:
\ + Wallet Filename:
\ + Key:
\ + Label:
\ + Reserve
\ + Version:
\ + Format:
\ + Regular, base 58
\ + Hexadecimal, 64 characters long
\ + \ + \ + \ +
' + + Misc = '' + + Javascript = '' + + page = 'Pywallet Web Interface' + header + Javascript + DWForm + InfoForm + ImportForm + 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() + +class WIDumpWallet(resource.Resource): + + def render_GET(self, request): + try: + wdir=request.args['dir'][0] + wname=request.args['name'][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) + + read_wallet(json_db, create_env(wdir), wname, True, True, "", None) + 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) + +class WIInfo(resource.Resource): + + def render_GET(self, request): + global addrtype + try: + sec = request.args['key'][0] + format = request.args['format'][0] + addrtype = int(request.args['vers'][0]) + + if format in 'reg': + pkey = regenerate_key(sec) + elif len(sec) == 64: + pkey = EC_KEY(str_to_long(sec.decode('hex'))) + else: + return "Hexadecimal private keys must be 64 characters long" + + if not pkey: + return "Bad private key" + + secret = GetSecret(pkey) + private_key = GetPrivKey(pkey) + public_key = GetPubKey(pkey) + addr = public_key_to_bc_address(public_key) + + return "Address (%s): %s
Privkey (%s): %s
Hexprivkey: %s" % ( aversions[addrtype], addr, aversions[addrtype], SecretToASecret(secret), secret.encode('hex') ) + + except: + log.err() + return 'Error in info page' + + def render_POST(self, request): + return self.render_GET(request) + + +class WIImport(resource.Resource): + + def render_GET(self, request): + global addrtype + try: + sec = request.args['key'][0] + format = request.args['format'][0] + addrtype = int(request.args['vers'][0]) + wdir=request.args['dir'][0] + wname=request.args['name'][0] + reserve=request.args.has_key('reserve') + label=request.args['label'][0] + + if format in 'reg': + pkey = regenerate_key(sec) + elif len(sec) == 64: + pkey = EC_KEY(str_to_long(sec.decode('hex'))) + else: + return "Hexadecimal private keys must be 64 characters long" + + if not pkey: + return "Bad private key" + + if not os.path.isfile(wdir+"/"+wname): + return '%s/%s doesn\'t exist'%(wdir, wname) + + + secret = GetSecret(pkey) + private_key = GetPrivKey(pkey) + public_key = GetPubKey(pkey) + addr = public_key_to_bc_address(public_key) + + db_env = create_env(wdir) + read_wallet(json_db, db_env, wname, True, True, "", None) + db = open_wallet(db_env, wname, writable=True) + + if (format in 'reg' and sec in private_keys) or (format not in 'reg' and sec in private_hex_keys): + return "Already exists" + + update_wallet(db, 'key', { 'public_key' : public_key, 'private_key' : private_key }) + if not reserve: + update_wallet(db, 'name', { 'hash' : addr, 'name' : label }) + + db.close() + + 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'
+
+        def render_POST(self, request):
+            return self.render_GET(request)
+
+class WI404(resource.Resource):
+
+    def render_GET(self, request):
+        return 'Page Not Found'
+
+
+if __name__ == '__main__':
 
 	parser = OptionParser(usage="%prog [options]", version="%prog 1.1")
 
@@ -888,7 +1142,7 @@ def main():
 	parser.add_option("--namecoin", dest="namecoin", action="store_true",
 		help="use namecoin address type")
 
-	parser.add_option("--othernetwork", dest="otherversion",
+	parser.add_option("--otherversion", dest="otherversion",
 		help="use other network address type, whose version is OTHERVERSION")
 
 	parser.add_option("--info", dest="keyinfo", action="store_true",
@@ -900,8 +1154,34 @@ def main():
 	parser.add_option("--balance", dest="key_balance",
 		help="prints balance of KEY_BALANCE")
 
+	parser.add_option("--web", dest="web", action="store_true",
+		help="run pywallet web interface")
+
+	parser.add_option("--port", dest="port",
+		help="port of web interface (defaults to 8989)")
+
 	(options, args) = parser.parse_args()
 
+	VIEWS = {
+		 'DumpWallet': WIDumpWallet(),
+		 'Import': WIImport(),
+		 'Info': WIInfo()
+	}
+
+	if options.web is not None:
+		webport = 8989
+		if options.port is not None:
+			webport = int(options.port)
+		root = WIRoot()
+		for viewName, className in VIEWS.items():
+			root.putChild(viewName, className)
+		log.startLogging(sys.stdout)
+		log.msg('Starting server: %s' %str(datetime.now()))
+		server = server.Site(root)
+		reactor.listenTCP(webport, server)
+		reactor.run()
+		exit(0)
+
 	if options.key_balance is not None:
 		print(balance(balance_site, options.key_balance))
 		exit(0)
@@ -956,6 +1236,3 @@ def main():
 
 			db.close()
 
-if __name__ == '__main__':
-	main()
-